[Pkg-golang-commits] [golang] 01/01: Imported Upstream version 1.8~beta1

Michael Hudson-Doyle mwhudson-guest at moszumanska.debian.org
Wed Dec 7 21:34:19 UTC 2016


This is an automated email from the git hooks/post-receive script.

mwhudson-guest pushed a commit to branch upstream-1.8
in repository golang.

commit 7c1f35a3d06c29f961bbb7859cdb154b2ab7c730
Author: Michael Hudson-Doyle <michael.hudson at canonical.com>
Date:   Thu Dec 8 10:15:14 2016 +1300

    Imported Upstream version 1.8~beta1
---
 AUTHORS                                            |    76 +
 CONTRIBUTORS                                       |   103 +-
 VERSION                                            |     2 +-
 api/except.txt                                     |   186 +-
 api/go1.8.txt                                      |   259 +
 doc/articles/go_command.html                       |    95 +-
 doc/code.html                                      |    39 +-
 doc/conduct.html                                   |     4 +-
 doc/devel/release.html                             |    25 +-
 doc/devel/weekly.html                              |    12 +-
 doc/docs.html                                      |     8 +-
 doc/effective_go.html                              |    10 +-
 doc/gccgo_contribute.html                          |     2 +-
 doc/go1.7.html                                     |    13 +-
 doc/go1.8.html                                     |  1615 ++
 doc/go1.8.txt                                      |    56 +
 doc/go_faq.html                                    |    85 +-
 doc/go_spec.html                                   |    93 +-
 doc/install-source.html                            |    14 +-
 doc/install.html                                   |    70 +-
 doc/progs/json1.go                                 |     6 +-
 doc/progs/json3.go                                 |     2 +-
 doc/progs/json4.go                                 |     2 +-
 lib/time/update.bash                               |     4 +-
 lib/time/zoneinfo.zip                              |   Bin 364741 -> 364943 bytes
 misc/cgo/errors/issue16591.go                      |    17 +
 misc/cgo/errors/malloc.go                          |    34 +
 misc/cgo/errors/ptr.go                             |    21 +
 misc/cgo/errors/test.bash                          |    11 +
 misc/cgo/test/api.go                               |     6 +
 misc/cgo/test/basic.go                             |     3 +
 misc/cgo/test/callback.go                          |     1 +
 misc/cgo/test/cgo_test.go                          |     6 +
 misc/cgo/test/cgo_thread_lock.go                   |    53 +
 misc/cgo/test/checkconst.go                        |    33 +
 misc/cgo/test/complex.go                           |    24 +
 misc/cgo/test/issue17065.go                        |    29 +
 misc/cgo/test/issue17537.go                        |    42 +
 misc/cgo/test/issue18126.go                        |    26 +
 misc/cgo/test/issue7978.go                         |    11 +
 misc/cgo/test/issue8756.go                         |    17 +
 misc/cgo/test/issue8756/issue8756.go               |    11 +
 misc/cgo/testcarchive/carchive_test.go             |   190 +-
 misc/cgo/testcarchive/main2.c                      |    56 +-
 misc/cgo/testcarchive/main3.c                      |    25 +-
 misc/cgo/testcarchive/main4.c                      |    19 +-
 misc/cgo/testcarchive/main5.c                      |    18 +
 misc/cgo/testcarchive/src/libgo2/libgo2.go         |    30 +
 misc/cgo/testcarchive/src/libgo3/libgo3.go         |    12 +
 misc/cgo/testcshared/main4.c                       |     9 +-
 misc/cgo/testcshared/main5.c                       |    18 +-
 misc/cgo/testcshared/test.bash                     |     2 +-
 misc/cgo/testgodefs/test.bash                      |     2 +-
 misc/cgo/testplugin/altpath/src/common/common.go   |    11 +
 .../testplugin/altpath/src/plugin-mismatch/main.go |    17 +
 misc/cgo/testplugin/src/common/common.go           |    11 +
 misc/cgo/testplugin/src/host/host.go               |   148 +
 misc/cgo/testplugin/src/plugin1/plugin1.go         |    35 +
 misc/cgo/testplugin/src/plugin2/plugin2.go         |    18 +
 misc/cgo/testplugin/src/sub/plugin1/plugin1.go     |    23 +
 misc/cgo/testplugin/test.bash                      |    34 +
 misc/cgo/testplugin/unnamed1.go                    |    12 +
 misc/cgo/testplugin/unnamed2.go                    |    12 +
 misc/cgo/testsanitizers/msan5.go                   |    57 +
 misc/cgo/testsanitizers/test.bash                  |    14 +
 misc/cgo/testsanitizers/tsan7.go                   |    40 +
 misc/cgo/testsanitizers/tsan8.go                   |    60 +
 misc/cgo/testshared/shared_test.go                 |    30 +-
 misc/cgo/testshared/src/depBase/dep.go             |     9 +
 misc/cgo/testshared/src/exe/exe.go                 |     6 +
 misc/cgo/testsigfwd/main.go                        |     1 +
 misc/nacl/testzip.proto                            |     8 +
 src/androidtest.bash                               |    20 +-
 src/archive/tar/common.go                          |    27 +-
 src/archive/tar/reader.go                          |   531 +-
 src/archive/tar/reader_test.go                     |   773 +-
 src/archive/tar/strconv.go                         |   252 +
 src/archive/tar/strconv_test.go                    |   319 +
 src/archive/tar/tar_test.go                        |   236 +-
 src/archive/tar/testdata/gnu-incremental.tar       |   Bin 0 -> 2560 bytes
 src/archive/tar/testdata/pax-bad-hdr-file.tar      |   Bin 0 -> 2560 bytes
 src/archive/tar/testdata/pax-bad-mtime-file.tar    |   Bin 0 -> 2560 bytes
 src/archive/tar/testdata/pax-pos-size-file.tar     |   Bin 0 -> 2560 bytes
 src/archive/tar/testdata/ustar.issue12594.tar      |   Bin 0 -> 3072 bytes
 src/archive/tar/testdata/writer-big-long.tar       |   Bin 4096 -> 4096 bytes
 src/archive/tar/writer.go                          |   106 +-
 src/archive/tar/writer_test.go                     |   483 +-
 src/archive/zip/reader.go                          |    49 +-
 src/archive/zip/reader_test.go                     |    31 +-
 src/archive/zip/struct.go                          |     3 +
 src/archive/zip/testdata/extra-timestamp.zip       |   Bin 0 -> 152 bytes
 src/archive/zip/writer.go                          |    28 +-
 src/archive/zip/writer_test.go                     |    27 +
 src/archive/zip/zip_test.go                        |   363 +-
 src/bufio/bufio.go                                 |    29 +-
 src/bufio/bufio_test.go                            |    21 +
 src/bufio/scan.go                                  |     1 -
 src/builtin/builtin.go                             |     4 +-
 src/bytes/buffer.go                                |    43 +-
 src/bytes/buffer_test.go                           |    13 +
 src/bytes/bytes.go                                 |   146 +-
 src/bytes/bytes_amd64.go                           |   115 +
 src/bytes/bytes_generic.go                         |    41 +
 src/bytes/bytes_s390x.go                           |   118 +
 src/bytes/bytes_test.go                            |   194 +-
 src/bytes/example_test.go                          |   203 +
 src/cmd/addr2line/addr2line_test.go                |     4 +-
 src/cmd/api/run.go                                 |     2 +-
 src/cmd/asm/internal/arch/amd64.go                 |     4 +-
 src/cmd/asm/internal/arch/arch.go                  |    74 +-
 src/cmd/asm/internal/arch/mips.go                  |    67 +
 src/cmd/asm/internal/arch/mips64.go                |    67 -
 src/cmd/asm/internal/arch/ppc64.go                 |    12 +
 src/cmd/asm/internal/arch/s390x.go                 |    12 +-
 src/cmd/asm/internal/asm/asm.go                    |    93 +-
 src/cmd/asm/internal/asm/endtoend_test.go          |     3 +-
 src/cmd/asm/internal/asm/operand_test.go           |   187 +-
 src/cmd/asm/internal/asm/parse.go                  |     8 -
 src/cmd/asm/internal/asm/testdata/amd64enc.s       |   192 +-
 src/cmd/asm/internal/asm/testdata/mips.s           |   430 +
 src/cmd/asm/internal/asm/testdata/ppc64.s          |   349 +
 src/cmd/asm/internal/asm/testdata/s390x.s          |   151 +-
 src/cmd/asm/internal/flags/flags.go                |     9 +-
 src/cmd/asm/main.go                                |    35 +-
 src/cmd/cgo/ast.go                                 |     3 +-
 src/cmd/cgo/doc.go                                 |    12 +
 src/cmd/cgo/gcc.go                                 |   418 +-
 src/cmd/cgo/main.go                                |    15 +-
 src/cmd/cgo/out.go                                 |    86 +-
 src/cmd/compile/fmt_test.go                        |   716 +
 src/cmd/compile/internal/amd64/cgen.go             |   161 -
 src/cmd/compile/internal/amd64/galign.go           |    73 +-
 src/cmd/compile/internal/amd64/ggen.go             |   681 +-
 src/cmd/compile/internal/amd64/gsubr.go            |  1423 --
 src/cmd/compile/internal/amd64/peep.go             |  1025 -
 src/cmd/compile/internal/amd64/prog.go             |   120 +-
 src/cmd/compile/internal/amd64/reg.go              |   152 -
 src/cmd/compile/internal/amd64/ssa.go              |   562 +-
 src/cmd/compile/internal/arm/cgen.go               |   224 -
 src/cmd/compile/internal/arm/cgen64.go             |   859 -
 src/cmd/compile/internal/arm/galign.go             |    49 +-
 src/cmd/compile/internal/arm/ggen.go               |   479 +-
 src/cmd/compile/internal/arm/gsubr.go              |  1225 -
 src/cmd/compile/internal/arm/peep.go               |  1734 --
 src/cmd/compile/internal/arm/prog.go               |    19 +-
 src/cmd/compile/internal/arm/reg.go                |   136 -
 src/cmd/compile/internal/arm/ssa.go                |   915 +-
 src/cmd/compile/internal/arm64/cgen.go             |   151 -
 src/cmd/compile/internal/arm64/galign.go           |    52 +-
 src/cmd/compile/internal/arm64/ggen.go             |   501 +-
 src/cmd/compile/internal/arm64/gsubr.go            |   983 -
 src/cmd/compile/internal/arm64/peep.go             |   797 -
 src/cmd/compile/internal/arm64/prog.go             |   158 +-
 src/cmd/compile/internal/arm64/reg.go              |   169 -
 src/cmd/compile/internal/arm64/ssa.go              |   844 +
 src/cmd/compile/internal/big/accuracy_string.go    |    17 -
 src/cmd/compile/internal/big/arith.go              |   305 -
 src/cmd/compile/internal/big/arith_decl.go         |    53 -
 src/cmd/compile/internal/big/arith_test.go         |   442 -
 src/cmd/compile/internal/big/bits_test.go          |   224 -
 src/cmd/compile/internal/big/calibrate_test.go     |    88 -
 src/cmd/compile/internal/big/decimal.go            |   266 -
 src/cmd/compile/internal/big/decimal_test.go       |   116 -
 src/cmd/compile/internal/big/example_rat_test.go   |    65 -
 src/cmd/compile/internal/big/example_test.go       |   128 -
 src/cmd/compile/internal/big/float.go              |  1683 --
 src/cmd/compile/internal/big/float_test.go         |  1764 --
 src/cmd/compile/internal/big/floatconv.go          |   275 -
 src/cmd/compile/internal/big/floatconv_test.go     |   662 -
 src/cmd/compile/internal/big/floatexample_test.go  |   141 -
 src/cmd/compile/internal/big/floatmarsh.go         |    33 -
 src/cmd/compile/internal/big/floatmarsh_test.go    |    54 -
 src/cmd/compile/internal/big/ftoa.go               |   456 -
 src/cmd/compile/internal/big/gcd_test.go           |    47 -
 src/cmd/compile/internal/big/hilbert_test.go       |   160 -
 src/cmd/compile/internal/big/int.go                |   934 -
 src/cmd/compile/internal/big/int_test.go           |  1482 --
 src/cmd/compile/internal/big/intconv.go            |   248 -
 src/cmd/compile/internal/big/intconv_test.go       |   391 -
 src/cmd/compile/internal/big/intmarsh.go           |    74 -
 src/cmd/compile/internal/big/intmarsh_test.go      |   121 -
 src/cmd/compile/internal/big/nat.go                |  1282 -
 src/cmd/compile/internal/big/nat_test.go           |   654 -
 src/cmd/compile/internal/big/natconv.go            |   492 -
 src/cmd/compile/internal/big/natconv_test.go       |   422 -
 src/cmd/compile/internal/big/rat.go                |   510 -
 src/cmd/compile/internal/big/rat_test.go           |   622 -
 src/cmd/compile/internal/big/ratconv.go            |   264 -
 src/cmd/compile/internal/big/ratconv_test.go       |   453 -
 src/cmd/compile/internal/big/ratmarsh.go           |    73 -
 src/cmd/compile/internal/big/ratmarsh_test.go      |   125 -
 .../compile/internal/big/roundingmode_string.go    |    16 -
 src/cmd/compile/internal/big/vendor.bash           |    31 -
 src/cmd/compile/internal/gc/alg.go                 |   108 +-
 src/cmd/compile/internal/gc/align.go               |    50 +-
 src/cmd/compile/internal/gc/asm_test.go            |   119 +-
 src/cmd/compile/internal/gc/bexport.go             |   325 +-
 src/cmd/compile/internal/gc/bimport.go             |   450 +-
 src/cmd/compile/internal/gc/builtin.go             |   339 +-
 src/cmd/compile/internal/gc/builtin/runtime.go     |    38 +-
 src/cmd/compile/internal/gc/builtin/unsafe.go      |    18 -
 src/cmd/compile/internal/gc/builtin_test.go        |     2 +-
 src/cmd/compile/internal/gc/bv.go                  |    65 +-
 src/cmd/compile/internal/gc/cgen.go                |  3600 ---
 src/cmd/compile/internal/gc/closure.go             |   135 +-
 src/cmd/compile/internal/gc/const.go               |   329 +-
 src/cmd/compile/internal/gc/constFold_test.go      |  4596 ++--
 src/cmd/compile/internal/gc/cplx.go                |   474 -
 src/cmd/compile/internal/gc/dcl.go                 |   412 +-
 src/cmd/compile/internal/gc/esc.go                 |   807 +-
 src/cmd/compile/internal/gc/export.go              |   325 +-
 src/cmd/compile/internal/gc/fixedbugs_test.go      |     2 +-
 src/cmd/compile/internal/gc/float_test.go          |    33 +
 src/cmd/compile/internal/gc/fmt.go                 |  1149 +-
 src/cmd/compile/internal/gc/gen.go                 |  1139 +-
 src/cmd/compile/internal/gc/global_test.go         |     6 +-
 src/cmd/compile/internal/gc/go.go                  |   186 +-
 src/cmd/compile/internal/gc/gsubr.go               |   720 +-
 src/cmd/compile/internal/gc/iface_test.go          |   128 +
 src/cmd/compile/internal/gc/init.go                |    54 +-
 src/cmd/compile/internal/gc/inl.go                 |   215 +-
 src/cmd/compile/internal/gc/lex.go                 |  1176 +-
 src/cmd/compile/internal/gc/magic.go               |     4 +-
 src/cmd/compile/internal/gc/main.go                |   242 +-
 src/cmd/compile/internal/gc/mkbuiltin.go           |   219 +-
 src/cmd/compile/internal/gc/mpfloat.go             |    10 +-
 src/cmd/compile/internal/gc/mpint.go               |    46 +-
 src/cmd/compile/internal/gc/noder.go               |  1083 +
 src/cmd/compile/internal/gc/obj.go                 |   183 +-
 src/cmd/compile/internal/gc/opnames.go             |     6 +-
 src/cmd/compile/internal/gc/order.go               |   182 +-
 src/cmd/compile/internal/gc/parser.go              |  3353 ---
 src/cmd/compile/internal/gc/pgen.go                |   274 +-
 src/cmd/compile/internal/gc/phi.go                 |   521 +
 src/cmd/compile/internal/gc/plive.go               |   316 +-
 src/cmd/compile/internal/gc/popt.go                |  1094 -
 src/cmd/compile/internal/gc/racewalk.go            |   104 +-
 src/cmd/compile/internal/gc/range.go               |   171 +-
 src/cmd/compile/internal/gc/reflect.go             |   203 +-
 src/cmd/compile/internal/gc/reg.go                 |  1532 --
 src/cmd/compile/internal/gc/select.go              |    82 +-
 src/cmd/compile/internal/gc/sinit.go               |   823 +-
 src/cmd/compile/internal/gc/sizeof_test.go         |     6 +-
 .../internal/gc/sparselocatephifunctions.go        |   202 -
 src/cmd/compile/internal/gc/ssa.go                 |  2043 +-
 src/cmd/compile/internal/gc/ssa_test.go            |    58 +-
 src/cmd/compile/internal/gc/subr.go                |   591 +-
 src/cmd/compile/internal/gc/swt.go                 |   677 +-
 src/cmd/compile/internal/gc/swt_test.go            |   152 +-
 src/cmd/compile/internal/gc/syntax.go              |   146 +-
 .../gc/testdata/{addressed_ssa.go => addressed.go} |     0
 .../gc/testdata/{append_ssa.go => append.go}       |     0
 src/cmd/compile/internal/gc/testdata/arith.go      |  1020 +
 .../{arithBoundary_ssa.go => arithBoundary.go}     |     0
 .../testdata/{arithConst_ssa.go => arithConst.go}  |     0
 src/cmd/compile/internal/gc/testdata/arith_ssa.go  |   580 -
 .../gc/testdata/{array_ssa.go => array.go}         |     0
 .../gc/testdata/{assert_ssa.go => assert.go}       |     0
 .../gc/testdata/{break_ssa.go => break.go}         |     0
 .../internal/gc/testdata/{chan_ssa.go => chan.go}  |     0
 .../gc/testdata/{closure_ssa.go => closure.go}     |     0
 .../internal/gc/testdata/{cmp_ssa.go => cmp.go}    |     0
 .../gc/testdata/{compound_ssa.go => compound.go}   |     0
 .../internal/gc/testdata/{copy_ssa.go => copy.go}  |     0
 .../internal/gc/testdata/{ctl_ssa.go => ctl.go}    |     0
 .../{deferNoReturn_ssa.go => deferNoReturn.go}     |     0
 .../gc/testdata/{divbyzero_ssa.go => divbyzero.go} |     0
 .../internal/gc/testdata/{fp_ssa.go => fp.go}      |     0
 .../internal/gc/testdata/gen/arithBoundaryGen.go   |     4 +-
 .../internal/gc/testdata/gen/arithConstGen.go      |     4 +-
 .../internal/gc/testdata/gen/constFoldGen.go       |     8 +-
 .../compile/internal/gc/testdata/gen/copyGen.go    |     4 +-
 .../compile/internal/gc/testdata/gen/zeroGen.go    |     4 +-
 .../gc/testdata/{loadstore_ssa.go => loadstore.go} |     0
 .../internal/gc/testdata/{map_ssa.go => map.go}    |     0
 .../internal/gc/testdata/{phi_ssa.go => phi.go}    |     0
 .../gc/testdata/{regalloc_ssa.go => regalloc.go}   |     0
 .../gc/testdata/{short_ssa.go => short.go}         |     0
 src/cmd/compile/internal/gc/testdata/sqrt_const.go |    59 +
 src/cmd/compile/internal/gc/testdata/string.go     |   224 +
 src/cmd/compile/internal/gc/testdata/string_ssa.go |   160 -
 .../gc/testdata/{unsafe_ssa.go => unsafe.go}       |     0
 .../internal/gc/testdata/{zero_ssa.go => zero.go}  |     0
 src/cmd/compile/internal/gc/timings.go             |   235 +
 src/cmd/compile/internal/gc/trace.go               |    27 +
 src/cmd/compile/internal/gc/type.go                |   122 +-
 src/cmd/compile/internal/gc/typecheck.go           |   890 +-
 src/cmd/compile/internal/gc/universe.go            |   145 +-
 src/cmd/compile/internal/gc/unsafe.go              |   127 +-
 src/cmd/compile/internal/gc/util.go                |    11 +-
 src/cmd/compile/internal/gc/walk.go                |  1403 +-
 src/cmd/compile/internal/mips/galign.go            |    26 +
 src/cmd/compile/internal/mips/ggen.go              |   101 +
 src/cmd/compile/internal/mips/prog.go              |   157 +
 src/cmd/compile/internal/mips/ssa.go               |   907 +
 src/cmd/compile/internal/mips64/cgen.go            |   157 -
 src/cmd/compile/internal/mips64/galign.go          |    51 +-
 src/cmd/compile/internal/mips64/ggen.go            |   419 +-
 src/cmd/compile/internal/mips64/gsubr.go           |  1071 -
 src/cmd/compile/internal/mips64/peep.go            |   772 -
 src/cmd/compile/internal/mips64/prog.go            |    28 +-
 src/cmd/compile/internal/mips64/reg.go             |   162 -
 src/cmd/compile/internal/mips64/ssa.go             |   672 +
 src/cmd/compile/internal/ppc64/cgen.go             |   143 -
 src/cmd/compile/internal/ppc64/galign.go           |    56 +-
 src/cmd/compile/internal/ppc64/ggen.go             |   488 +-
 src/cmd/compile/internal/ppc64/gsubr.go            |  1076 -
 src/cmd/compile/internal/ppc64/peep.go             |  1032 -
 src/cmd/compile/internal/ppc64/prog.go             |    62 +-
 src/cmd/compile/internal/ppc64/reg.go              |   168 -
 src/cmd/compile/internal/ppc64/ssa.go              |   938 +
 src/cmd/compile/internal/s390x/cgen.go             |   178 -
 src/cmd/compile/internal/s390x/galign.go           |    51 +-
 src/cmd/compile/internal/s390x/ggen.go             |   453 +-
 src/cmd/compile/internal/s390x/gsubr.go            |  1110 -
 src/cmd/compile/internal/s390x/peep.go             |  1664 --
 src/cmd/compile/internal/s390x/prog.go             |    89 +-
 src/cmd/compile/internal/s390x/reg.go              |   130 -
 src/cmd/compile/internal/s390x/ssa.go              |   862 +
 src/cmd/compile/internal/ssa/block.go              |    15 +-
 src/cmd/compile/internal/ssa/check.go              |    27 +-
 src/cmd/compile/internal/ssa/compile.go            |   118 +-
 src/cmd/compile/internal/ssa/config.go             |   173 +-
 src/cmd/compile/internal/ssa/cse.go                |   174 +-
 src/cmd/compile/internal/ssa/cse_test.go           |     1 -
 src/cmd/compile/internal/ssa/deadcode.go           |    12 +
 src/cmd/compile/internal/ssa/deadstore.go          |    13 +-
 src/cmd/compile/internal/ssa/decompose.go          |    97 +-
 src/cmd/compile/internal/ssa/dom.go                |     8 +-
 src/cmd/compile/internal/ssa/export_test.go        |    22 +-
 src/cmd/compile/internal/ssa/flagalloc.go          |    47 +-
 src/cmd/compile/internal/ssa/func.go               |    70 +-
 src/cmd/compile/internal/ssa/fuse.go               |     4 +-
 src/cmd/compile/internal/ssa/gen/386.rules         |  1252 +
 src/cmd/compile/internal/ssa/gen/386Ops.go         |   506 +
 src/cmd/compile/internal/ssa/gen/AMD64.rules       |   586 +-
 src/cmd/compile/internal/ssa/gen/AMD64Ops.go       |   434 +-
 src/cmd/compile/internal/ssa/gen/ARM.rules         |  1231 +-
 src/cmd/compile/internal/ssa/gen/ARM64.rules       |  1302 +
 src/cmd/compile/internal/ssa/gen/ARM64Ops.go       |   535 +
 src/cmd/compile/internal/ssa/gen/ARMOps.go         |   527 +-
 src/cmd/compile/internal/ssa/gen/MIPS.rules        |   739 +
 src/cmd/compile/internal/ssa/gen/MIPS64.rules      |   708 +
 src/cmd/compile/internal/ssa/gen/MIPS64Ops.go      |   381 +
 src/cmd/compile/internal/ssa/gen/MIPSOps.go        |   413 +
 src/cmd/compile/internal/ssa/gen/PPC64.rules       |   832 +
 src/cmd/compile/internal/ssa/gen/PPC64Ops.go       |   398 +
 src/cmd/compile/internal/ssa/gen/S390X.rules       |  1649 ++
 src/cmd/compile/internal/ssa/gen/S390XOps.go       |   623 +
 src/cmd/compile/internal/ssa/gen/dec64.rules       |   447 +
 src/cmd/compile/internal/ssa/gen/dec64Ops.go       |    20 +
 src/cmd/compile/internal/ssa/gen/generic.rules     |   199 +-
 src/cmd/compile/internal/ssa/gen/genericOps.go     |   250 +-
 src/cmd/compile/internal/ssa/gen/main.go           |   114 +-
 src/cmd/compile/internal/ssa/gen/rulegen.go        |   412 +-
 src/cmd/compile/internal/ssa/html.go               |    15 +-
 src/cmd/compile/internal/ssa/lca.go                |   123 +
 src/cmd/compile/internal/ssa/lca_test.go           |   103 +
 src/cmd/compile/internal/ssa/likelyadjust.go       |    29 +-
 src/cmd/compile/internal/ssa/location.go           |    22 +-
 src/cmd/compile/internal/ssa/loopbce.go            |    10 +-
 src/cmd/compile/internal/ssa/lower.go              |    11 +-
 src/cmd/compile/internal/ssa/nilcheck.go           |   209 +-
 src/cmd/compile/internal/ssa/nilcheck_test.go      |    10 -
 src/cmd/compile/internal/ssa/op.go                 |    72 +-
 src/cmd/compile/internal/ssa/opGen.go              | 19742 +++++++++++++--
 src/cmd/compile/internal/ssa/opt.go                |     3 +
 src/cmd/compile/internal/ssa/passbm_test.go        |     2 -
 src/cmd/compile/internal/ssa/phiopt.go             |    66 +-
 src/cmd/compile/internal/ssa/prove.go              |   101 +-
 src/cmd/compile/internal/ssa/regalloc.go           |   537 +-
 src/cmd/compile/internal/ssa/rewrite.go            |   184 +-
 src/cmd/compile/internal/ssa/rewrite386.go         | 14787 ++++++++++++
 src/cmd/compile/internal/ssa/rewriteAMD64.go       | 23855 +++++++++++--------
 src/cmd/compile/internal/ssa/rewriteARM.go         | 18629 ++++++++++++++-
 src/cmd/compile/internal/ssa/rewriteARM64.go       | 16703 +++++++++++++
 src/cmd/compile/internal/ssa/rewriteMIPS.go        |  9831 ++++++++
 src/cmd/compile/internal/ssa/rewriteMIPS64.go      | 10432 ++++++++
 src/cmd/compile/internal/ssa/rewritePPC64.go       | 10848 +++++++++
 src/cmd/compile/internal/ssa/rewriteS390X.go       | 18694 +++++++++++++++
 src/cmd/compile/internal/ssa/rewrite_test.go       |     3 +
 src/cmd/compile/internal/ssa/rewritedec.go         |     2 +-
 src/cmd/compile/internal/ssa/rewritedec64.go       |  2720 +++
 src/cmd/compile/internal/ssa/rewritegeneric.go     |  2379 +-
 src/cmd/compile/internal/ssa/schedule.go           |    55 +-
 src/cmd/compile/internal/ssa/sparsemap.go          |     8 +-
 src/cmd/compile/internal/ssa/sparsetreemap.go      |     2 +-
 src/cmd/compile/internal/ssa/stackalloc.go         |     2 +-
 src/cmd/compile/internal/ssa/stackframe.go         |    10 +
 src/cmd/compile/internal/ssa/tighten.go            |   152 +-
 src/cmd/compile/internal/ssa/trim.go               |   127 +-
 src/cmd/compile/internal/ssa/type.go               |    69 +-
 src/cmd/compile/internal/ssa/type_test.go          |     1 +
 src/cmd/compile/internal/ssa/value.go              |    55 +-
 src/cmd/compile/internal/ssa/writebarrier.go       |   278 +
 src/cmd/compile/internal/syntax/dumper.go          |   212 +
 src/cmd/compile/internal/syntax/dumper_test.go     |    22 +
 src/cmd/compile/internal/syntax/nodes.go           |   452 +
 src/cmd/compile/internal/syntax/parser.go          |  2143 ++
 src/cmd/compile/internal/syntax/parser_test.go     |   184 +
 src/cmd/compile/internal/syntax/printer.go         |   942 +
 src/cmd/compile/internal/syntax/printer_test.go    |    24 +
 src/cmd/compile/internal/syntax/scanner.go         |   664 +
 src/cmd/compile/internal/syntax/scanner_test.go    |   355 +
 src/cmd/compile/internal/syntax/source.go          |   181 +
 src/cmd/compile/internal/syntax/syntax.go          |   100 +
 src/cmd/compile/internal/syntax/tokens.go          |   263 +
 src/cmd/compile/internal/x86/387.go                |   357 +
 src/cmd/compile/internal/x86/cgen.go               |   159 -
 src/cmd/compile/internal/x86/cgen64.go             |   598 -
 src/cmd/compile/internal/x86/galign.go             |    58 +-
 src/cmd/compile/internal/x86/ggen.go               |   856 +-
 src/cmd/compile/internal/x86/gsubr.go              |  1844 --
 src/cmd/compile/internal/x86/peep.go               |   807 -
 src/cmd/compile/internal/x86/prog.go               |    89 +-
 src/cmd/compile/internal/x86/reg.go                |   114 -
 src/cmd/compile/internal/x86/ssa.go                |   918 +
 src/cmd/compile/main.go                            |    25 +-
 src/cmd/cover/cover.go                             |    82 +-
 src/cmd/cover/cover_test.go                        |    23 +-
 src/cmd/cover/html.go                              |    29 +-
 src/cmd/cover/profile.go                           |    23 +
 src/cmd/cover/testdata/main.go                     |     6 +-
 src/cmd/cover/testdata/test.go                     |    43 +
 src/cmd/dist/build.go                              |    59 +-
 src/cmd/dist/buildgo.go                            |    10 +-
 src/cmd/dist/buildtool.go                          |   119 +-
 src/cmd/dist/deps.go                               |    12 +-
 src/cmd/dist/test.go                               |   150 +-
 src/cmd/dist/util.go                               |     5 +
 src/cmd/doc/dirs.go                                |     8 +-
 src/cmd/doc/doc_test.go                            |    68 +-
 src/cmd/doc/pkg.go                                 |   327 +-
 src/cmd/doc/testdata/pkg.go                        |    46 +
 src/cmd/fix/context.go                             |    25 +
 src/cmd/fix/context_test.go                        |    42 +
 src/cmd/fix/fix.go                                 |     9 +-
 src/cmd/fix/gotypes.go                             |     8 +-
 src/cmd/fix/main.go                                |     9 +-
 src/cmd/fix/netipv6zone.go                         |     8 +-
 src/cmd/fix/printerconfig.go                       |     8 +-
 src/cmd/go/alldocs.go                              |   191 +-
 src/cmd/go/bootstrap.go                            |     3 +
 src/cmd/go/bug.go                                  |   209 +
 src/cmd/go/build.go                                |   442 +-
 src/cmd/go/build_test.go                           |    44 +
 src/cmd/go/env.go                                  |    43 +-
 src/cmd/go/generate.go                             |    10 +-
 src/cmd/go/get.go                                  |    46 +-
 src/cmd/go/go_test.go                              |   850 +-
 src/cmd/go/go_windows_test.go                      |     3 +-
 src/cmd/go/help.go                                 |    52 +-
 src/cmd/go/http.go                                 |     4 +
 src/cmd/go/list.go                                 |    19 +-
 src/cmd/go/main.go                                 |     9 +-
 src/cmd/go/pkg.go                                  |   262 +-
 src/cmd/go/test.go                                 |   158 +-
 src/cmd/go/testdata/src/canonical/a/a.go           |     3 +
 src/cmd/go/testdata/src/canonical/a/vendor/c/c.go  |     1 +
 src/cmd/go/testdata/src/canonical/b/b.go           |     3 +
 src/cmd/go/testdata/src/canonical/d/d.go           |     3 +
 src/cmd/go/testdata/{ => src}/cgocover/p.go        |     0
 src/cmd/go/testdata/{ => src}/cgocover/p_test.go   |     0
 .../go/testdata/{cgocover => src/cgocover2}/p.go   |     0
 src/cmd/go/testdata/src/cgocover2/x_test.go        |    10 +
 .../go/testdata/{cgocover => src/cgocover3}/p.go   |     0
 src/cmd/go/testdata/src/cgocover3/p_test.go        |     1 +
 src/cmd/go/testdata/src/cgocover3/x_test.go        |    10 +
 src/cmd/go/testdata/src/cgocover4/notcgo.go        |     1 +
 .../go/testdata/{cgocover => src/cgocover4}/p.go   |     0
 src/cmd/go/testdata/src/cgocover4/x_test.go        |    10 +
 src/cmd/go/testdata/src/dupload/dupload.go         |     8 +
 src/cmd/go/testdata/src/dupload/p/p.go             |     1 +
 src/cmd/go/testdata/src/dupload/p2/p2.go           |     2 +
 src/cmd/go/testdata/src/dupload/vendor/p/p.go      |     1 +
 src/cmd/go/testdata/src/gencycle/gencycle.go       |     5 +
 src/cmd/go/testdata/src/importmain/ismain/main.go  |     5 +
 src/cmd/go/testdata/src/importmain/test/test.go    |     1 +
 .../go/testdata/src/importmain/test/test_test.go   |     6 +
 src/cmd/go/testdata/src/my.pkg/main/main.go        |     5 +
 src/cmd/go/testdata/src/my.pkg/pkg.go              |     3 +
 src/cmd/go/testdata/src/testrace/race_test.go      |    29 +
 src/cmd/go/testdata/standalone_benchmark_test.go   |     6 +
 src/cmd/go/testdata/standalone_fail_sub_test.go    |     8 +
 .../go/testdata/standalone_parallel_sub_test.go    |    14 +
 src/cmd/go/testdata/standalone_sub_test.go         |     7 +
 src/cmd/go/testflag.go                             |     4 +-
 src/cmd/go/tool.go                                 |     2 +-
 src/cmd/go/vcs.go                                  |    70 +-
 src/cmd/go/vcs_test.go                             |    69 +-
 src/cmd/gofmt/doc.go                               |     5 +-
 src/cmd/gofmt/gofmt.go                             |    52 +-
 src/cmd/gofmt/gofmt_test.go                        |    13 +
 src/cmd/gofmt/simplify.go                          |    60 +-
 src/cmd/gofmt/testdata/composites.golden           |    14 +
 src/cmd/gofmt/testdata/composites.input            |    14 +
 src/cmd/internal/browser/browser.go                |    46 +
 src/cmd/internal/dwarf/dwarf.go                    |   604 +
 src/cmd/internal/dwarf/dwarf_defs.go               |   483 +
 src/cmd/internal/gcprog/gcprog.go                  |     1 -
 src/cmd/internal/goobj/read.go                     |   108 +-
 src/cmd/internal/obj/addrtype_string.go            |    27 +
 src/cmd/internal/obj/arm/a.out.go                  |     4 +-
 src/cmd/internal/obj/arm/anames.go                 |     2 +
 src/cmd/internal/obj/arm/asm5.go                   |    79 +-
 src/cmd/internal/obj/arm/list5.go                  |     2 +-
 src/cmd/internal/obj/arm/obj5.go                   |    45 +-
 src/cmd/internal/obj/arm64/a.out.go                |    20 +-
 src/cmd/internal/obj/arm64/anames7.go              |     1 +
 src/cmd/internal/obj/arm64/asm7.go                 |   438 +-
 src/cmd/internal/obj/arm64/asm_test.go             |    62 +
 src/cmd/internal/obj/arm64/obj7.go                 |   150 +-
 src/cmd/internal/obj/data.go                       |    20 +-
 src/cmd/internal/obj/ld.go                         |     4 +-
 src/cmd/internal/obj/link.go                       |   345 +-
 src/cmd/internal/obj/mips/a.out.go                 |    50 +-
 src/cmd/internal/obj/mips/anames.go                |    13 +
 src/cmd/internal/obj/mips/asm0.go                  |   754 +-
 src/cmd/internal/obj/mips/list0.go                 |     6 +-
 src/cmd/internal/obj/mips/obj0.go                  |   230 +-
 src/cmd/internal/obj/obj.go                        |     1 -
 src/cmd/internal/obj/objfile.go                    |   119 +-
 src/cmd/internal/obj/pass.go                       |     2 +-
 src/cmd/internal/obj/pcln.go                       |    40 +-
 src/cmd/internal/obj/plist.go                      |    82 +-
 src/cmd/internal/obj/ppc64/a.out.go                |   454 +-
 src/cmd/internal/obj/ppc64/anames.go               |   244 +
 src/cmd/internal/obj/ppc64/anames9.go              |     2 +
 src/cmd/internal/obj/ppc64/asm9.go                 |  1260 +-
 src/cmd/internal/obj/ppc64/list9.go                |     6 +
 src/cmd/internal/obj/ppc64/obj9.go                 |   134 +-
 src/cmd/internal/obj/reloctype_string.go           |    17 +
 src/cmd/internal/obj/s390x/a.out.go                |    46 +-
 src/cmd/internal/obj/s390x/anames.go               |    40 +-
 src/cmd/internal/obj/s390x/asmz.go                 |   644 +-
 src/cmd/internal/obj/s390x/objz.go                 |    96 +-
 src/cmd/internal/obj/sizeof_test.go                |     6 +-
 src/cmd/internal/obj/stack.go                      |     2 +-
 src/cmd/internal/obj/sym.go                        |    58 +-
 src/cmd/internal/obj/symkind_string.go             |    16 +
 src/cmd/internal/obj/util.go                       |    92 +-
 src/cmd/internal/obj/x86/a.out.go                  |    10 +-
 src/cmd/internal/obj/x86/anames.go                 |     8 +
 src/cmd/internal/obj/x86/asm6.go                   |   204 +-
 src/cmd/internal/obj/x86/list6.go                  |     2 +-
 src/cmd/internal/obj/x86/obj6.go                   |   158 +-
 src/cmd/internal/obj/x86/obj6_test.go              |     8 +-
 src/cmd/internal/objfile/disasm.go                 |    56 +-
 src/cmd/internal/objfile/elf.go                    |     4 +
 src/cmd/internal/objfile/goobj.go                  |    71 +-
 src/cmd/internal/objfile/objfile.go                |    31 +-
 src/cmd/internal/pprof/commands/commands.go        |   244 -
 src/cmd/internal/pprof/driver/driver.go            |  1041 -
 src/cmd/internal/pprof/driver/interactive.go       |   492 -
 src/cmd/internal/pprof/fetch/fetch.go              |    82 -
 src/cmd/internal/pprof/plugin/plugin.go            |   213 -
 src/cmd/internal/pprof/profile/legacy_profile.go   |  1236 -
 src/cmd/internal/pprof/profile/profile_test.go     |    24 -
 src/cmd/internal/pprof/report/report.go            |  1684 --
 src/cmd/internal/pprof/report/source.go            |   454 -
 src/cmd/internal/pprof/symbolizer/symbolizer.go    |   195 -
 src/cmd/internal/pprof/symbolz/symbolz.go          |   111 -
 src/cmd/internal/sys/arch.go                       |    36 +
 src/cmd/link/doc.go                                |     2 +
 src/cmd/link/internal/amd64/asm.go                 |   476 +-
 src/cmd/link/internal/amd64/l.go                   |    12 +-
 src/cmd/link/internal/amd64/obj.go                 |   136 +-
 src/cmd/link/internal/arm/asm.go                   |   453 +-
 src/cmd/link/internal/arm/l.go                     |    14 +-
 src/cmd/link/internal/arm/obj.go                   |   115 +-
 src/cmd/link/internal/arm64/asm.go                 |   159 +-
 src/cmd/link/internal/arm64/l.go                   |    12 +-
 src/cmd/link/internal/arm64/obj.go                 |   113 +-
 src/cmd/link/internal/ld/ar.go                     |    17 +-
 src/cmd/link/internal/ld/config.go                 |   250 +
 src/cmd/link/internal/ld/data.go                   |  1330 +-
 src/cmd/link/internal/ld/deadcode.go               |    87 +-
 src/cmd/link/internal/ld/decodesym.go              |   186 +-
 src/cmd/link/internal/ld/dwarf.go                  |  1904 +-
 src/cmd/link/internal/ld/dwarf_defs.go             |   516 -
 src/cmd/link/internal/ld/elf.go                    |   541 +-
 src/cmd/link/internal/ld/go.go                     |    82 +-
 src/cmd/link/internal/ld/ld.go                     |    36 +-
 src/cmd/link/internal/ld/ldelf.go                  |   141 +-
 src/cmd/link/internal/ld/ldmacho.go                |   114 +-
 src/cmd/link/internal/ld/ldpe.go                   |   397 +-
 src/cmd/link/internal/ld/lib.go                    |  1126 +-
 src/cmd/link/internal/ld/link.go                   |   152 +-
 src/cmd/link/internal/ld/macho.go                  |   234 +-
 src/cmd/link/internal/ld/main.go                   |   264 +
 src/cmd/link/internal/ld/objfile.go                |   130 +-
 src/cmd/link/internal/ld/pcln.go                   |   188 +-
 src/cmd/link/internal/ld/pe.go                     |   241 +-
 src/cmd/link/internal/ld/pobj.go                   |   227 -
 src/cmd/link/internal/ld/sym.go                    |   120 +-
 src/cmd/link/internal/ld/symbols.go                |    84 +
 src/cmd/link/internal/ld/symtab.go                 |   482 +-
 src/cmd/link/internal/ld/typelink.go               |    49 +
 src/cmd/link/internal/ld/util.go                   |    64 +-
 src/cmd/link/internal/mips/asm.go                  |   191 +
 src/cmd/link/internal/mips/l.go                    |    74 +
 src/cmd/link/internal/mips/obj.go                  |   110 +
 src/cmd/link/internal/mips64/asm.go                |    99 +-
 src/cmd/link/internal/mips64/l.go                  |    12 +-
 src/cmd/link/internal/mips64/obj.go                |    93 +-
 src/cmd/link/internal/ppc64/asm.go                 |   370 +-
 src/cmd/link/internal/ppc64/l.go                   |    12 +-
 src/cmd/link/internal/ppc64/obj.go                 |   110 +-
 src/cmd/link/internal/s390x/asm.go                 |   295 +-
 src/cmd/link/internal/s390x/l.go                   |    12 +-
 src/cmd/link/internal/s390x/obj.go                 |    55 +-
 src/cmd/link/internal/x86/asm.go                   |   376 +-
 src/cmd/link/internal/x86/l.go                     |    12 +-
 src/cmd/link/internal/x86/obj.go                   |   131 +-
 src/cmd/link/linkbig_test.go                       |   109 +
 src/cmd/link/main.go                               |    37 +-
 src/cmd/nm/nm.go                                   |    24 +-
 src/cmd/nm/nm_test.go                              |     2 +-
 src/cmd/objdump/objdump_test.go                    |    73 +-
 src/cmd/pack/pack_test.go                          |    16 +-
 src/cmd/pprof/internal/commands/commands.go        |   235 +
 src/cmd/pprof/internal/driver/driver.go            |  1042 +
 src/cmd/pprof/internal/driver/interactive.go       |   492 +
 src/cmd/pprof/internal/fetch/fetch.go              |    98 +
 src/cmd/pprof/internal/plugin/plugin.go            |   213 +
 src/cmd/pprof/internal/report/report.go            |  1726 ++
 src/cmd/pprof/internal/report/source.go            |   454 +
 .../pprof => pprof/internal}/report/source_html.go |     0
 .../{internal/pprof => pprof/internal}/svg/svg.go  |     0
 .../pprof => pprof/internal}/svg/svgpan.go         |     0
 src/cmd/pprof/internal/symbolizer/symbolizer.go    |   195 +
 src/cmd/pprof/internal/symbolz/symbolz.go          |   111 +
 .../pprof => pprof/internal}/tempfile/tempfile.go  |     0
 src/cmd/pprof/pprof.go                             |    19 +-
 src/cmd/trace/main.go                              |    72 +-
 src/cmd/trace/pprof.go                             |   117 +-
 src/cmd/trace/trace.go                             |   209 +-
 src/cmd/trace/trace_test.go                        |   101 +
 .../golang.org/x/arch/ppc64/ppc64asm/decode.go     |   179 +
 .../x/arch/ppc64/ppc64asm/decode_test.go           |    64 +
 .../vendor/golang.org/x/arch/ppc64/ppc64asm/doc.go |     6 +
 .../golang.org/x/arch/ppc64/ppc64asm/ext_test.go   |   535 +
 .../golang.org/x/arch/ppc64/ppc64asm/field.go      |    84 +
 .../golang.org/x/arch/ppc64/ppc64asm/field_test.go |    60 +
 .../vendor/golang.org/x/arch/ppc64/ppc64asm/gnu.go |   125 +
 .../golang.org/x/arch/ppc64/ppc64asm/inst.go       |   344 +
 .../x/arch/ppc64/ppc64asm/objdump_test.go          |   133 +
 .../x/arch/ppc64/ppc64asm/objdumpext_test.go       |   255 +
 .../golang.org/x/arch/ppc64/ppc64asm/plan9.go      |   172 +
 .../golang.org/x/arch/ppc64/ppc64asm/tables.go     |  5421 +++++
 .../x/arch/ppc64/ppc64asm/testdata/decode.txt      |    25 +
 src/cmd/vendor/vendor.json                         |     6 +
 src/cmd/vet/all/main.go                            |   332 +
 src/cmd/vet/all/whitelist/386.txt                  |    29 +
 src/cmd/vet/all/whitelist/64bit.txt                |    13 +
 src/cmd/vet/all/whitelist/all.txt                  |    92 +
 src/cmd/vet/all/whitelist/amd64.txt                |    35 +
 src/cmd/vet/all/whitelist/android_386.txt          |     8 +
 src/cmd/vet/all/whitelist/android_amd64.txt        |     3 +
 src/cmd/vet/all/whitelist/android_arm.txt          |     5 +
 src/cmd/vet/all/whitelist/arm.txt                  |    26 +
 src/cmd/vet/all/whitelist/arm64.txt                |    17 +
 src/cmd/vet/all/whitelist/darwin_386.txt           |     8 +
 src/cmd/vet/all/whitelist/darwin_amd64.txt         |     4 +
 src/cmd/vet/all/whitelist/darwin_arm.txt           |    12 +
 src/cmd/vet/all/whitelist/darwin_arm64.txt         |    14 +
 src/cmd/vet/all/whitelist/dragonfly_amd64.txt      |     7 +
 src/cmd/vet/all/whitelist/freebsd_386.txt          |    19 +
 src/cmd/vet/all/whitelist/freebsd_amd64.txt        |     6 +
 src/cmd/vet/all/whitelist/freebsd_arm.txt          |     4 +
 src/cmd/vet/all/whitelist/linux_386.txt            |    13 +
 src/cmd/vet/all/whitelist/linux_amd64.txt          |     8 +
 src/cmd/vet/all/whitelist/linux_arm.txt            |    12 +
 src/cmd/vet/all/whitelist/linux_arm64.txt          |     5 +
 src/cmd/vet/all/whitelist/linux_ppc64x.txt         |     5 +
 src/cmd/vet/all/whitelist/mips64x.txt              |     8 +
 src/cmd/vet/all/whitelist/nacl_386.txt             |    13 +
 src/cmd/vet/all/whitelist/nacl_amd64p32.txt        |    31 +
 src/cmd/vet/all/whitelist/nacl_arm.txt             |     8 +
 src/cmd/vet/all/whitelist/netbsd.txt               |     3 +
 src/cmd/vet/all/whitelist/netbsd_386.txt           |    23 +
 src/cmd/vet/all/whitelist/netbsd_amd64.txt         |     3 +
 src/cmd/vet/all/whitelist/netbsd_arm.txt           |     5 +
 src/cmd/vet/all/whitelist/openbsd_386.txt          |    17 +
 src/cmd/vet/all/whitelist/openbsd_amd64.txt        |     3 +
 src/cmd/vet/all/whitelist/openbsd_arm.txt          |     4 +
 src/cmd/vet/all/whitelist/plan9_386.txt            |     3 +
 src/cmd/vet/all/whitelist/plan9_amd64.txt          |     4 +
 src/cmd/vet/all/whitelist/plan9_arm.txt            |     4 +
 src/cmd/vet/all/whitelist/ppc64x.txt               |    12 +
 src/cmd/vet/all/whitelist/readme.txt               |     4 +
 src/cmd/vet/all/whitelist/s390x.txt                |    19 +
 src/cmd/vet/all/whitelist/solaris_amd64.txt        |     6 +
 src/cmd/vet/all/whitelist/windows.txt              |     5 +
 src/cmd/vet/all/whitelist/windows_386.txt          |     9 +
 src/cmd/vet/all/whitelist/windows_amd64.txt        |     8 +
 src/cmd/vet/asmdecl.go                             |   465 +-
 src/cmd/vet/cgo.go                                 |     5 +
 src/cmd/vet/copylock.go                            |     8 +
 src/cmd/vet/doc.go                                 |    21 +-
 src/cmd/vet/httpresponse.go                        |   153 +
 src/cmd/vet/main.go                                |    27 +-
 src/cmd/vet/print.go                               |    83 +-
 src/cmd/vet/shift.go                               |     6 +
 src/cmd/vet/structtag.go                           |    54 +-
 src/cmd/vet/testdata/asm.go                        |    35 -
 src/cmd/vet/testdata/asm/asm.go                    |    45 +
 src/cmd/vet/testdata/asm/asm1.s                    |   315 +
 src/cmd/vet/testdata/{ => asm}/asm2.s              |     0
 src/cmd/vet/testdata/{ => asm}/asm3.s              |     0
 src/cmd/vet/testdata/{ => asm}/asm4.s              |     0
 src/cmd/vet/testdata/{ => asm}/asm5.s              |     0
 src/cmd/vet/testdata/asm/asm6.s                    |   193 +
 src/cmd/vet/testdata/asm/asm7.s                    |   193 +
 src/cmd/vet/testdata/asm1.s                        |   265 -
 src/cmd/vet/testdata/asm8.s                        |   165 +
 src/cmd/vet/testdata/{ => buildtag}/buildtag.go    |     0
 .../vet/testdata/{ => buildtag}/buildtag_bad.go    |     0
 src/cmd/vet/testdata/cgo.go                        |    54 -
 src/cmd/vet/testdata/cgo/cgo.go                    |    56 +
 src/cmd/vet/testdata/{ => cgo}/cgo2.go             |     0
 src/cmd/vet/testdata/copylock.go                   |    18 +
 src/cmd/vet/testdata/httpresponse.go               |    85 +
 src/cmd/vet/testdata/print.go                      |    34 +-
 src/cmd/vet/testdata/shift.go                      |     2 +
 src/cmd/vet/testdata/structtag.go                  |    30 +
 src/cmd/vet/testdata/testingpkg/tests.go           |     1 +
 .../vet/testdata/{ => testingpkg}/tests_test.go    |     0
 src/cmd/vet/testdata/unsafeptr.go                  |     4 +-
 src/cmd/vet/types.go                               |    14 +-
 src/cmd/vet/unsafeptr.go                           |     2 +-
 src/cmd/vet/vet_test.go                            |   128 +-
 src/cmd/yacc/doc.go                                |    69 -
 src/cmd/yacc/testdata/expr/README                  |    20 -
 src/cmd/yacc/testdata/expr/expr.y                  |   202 -
 src/cmd/yacc/testdata/expr/main.go                 |    15 -
 src/cmd/yacc/yacc.go                               |  3641 ---
 src/cmp.bash                                       |     2 +-
 src/compress/flate/deflate.go                      |    20 +-
 src/compress/flate/deflate_test.go                 |   183 +
 src/compress/flate/deflatefast.go                  |   186 +-
 src/compress/flate/example_test.go                 |   243 +
 src/compress/flate/flate_test.go                   |     1 +
 src/compress/flate/huffman_bit_writer.go           |     2 +-
 src/compress/flate/inflate.go                      |     3 +
 src/compress/flate/inflate_test.go                 |    29 +
 src/compress/flate/writer_test.go                  |     9 +-
 src/compress/gzip/example_test.go                  |   128 +
 src/compress/gzip/gunzip.go                        |     7 +-
 src/compress/gzip/gunzip_test.go                   |    20 +
 src/compress/gzip/gzip.go                          |    23 +-
 src/compress/gzip/gzip_test.go                     |     4 +
 src/compress/gzip/issue14937_test.go               |     9 +-
 src/compress/zlib/reader_test.go                   |    18 +
 src/compress/zlib/writer.go                        |    11 +-
 src/compress/zlib/writer_test.go                   |     5 +
 src/container/heap/heap.go                         |     9 +-
 src/container/list/list_test.go                    |     6 +-
 src/context/benchmark_test.go                      |    44 +
 src/context/context.go                             |    26 +-
 src/context/context_test.go                        |    84 +-
 src/context/example_test.go                        |   116 +
 src/context/net_test.go                            |    21 +
 src/context/withtimeout_test.go                    |    33 -
 src/context/x_test.go                              |    29 +
 src/crypto/aes/aes_gcm.go                          |     7 +
 src/crypto/aes/asm_amd64.s                         |    11 -
 src/crypto/aes/asm_s390x.s                         |   115 +-
 src/crypto/aes/cipher_amd64.go                     |     4 +-
 src/crypto/aes/cipher_s390x.go                     |     8 +-
 src/crypto/aes/gcm_s390x.go                        |   270 +
 src/crypto/cipher/example_test.go                  |     4 +-
 src/crypto/cipher/gcm.go                           |     8 +
 src/crypto/cipher/gcm_test.go                      |   158 +
 src/crypto/ecdsa/ecdsa.go                          |     2 +-
 src/crypto/ecdsa/ecdsa_test.go                     |    12 +
 src/crypto/elliptic/elliptic_test.go               |     2 +-
 src/crypto/elliptic/p256.go                        |    28 +-
 src/crypto/elliptic/p256_asm_s390x.s               |  2201 ++
 src/crypto/elliptic/p256_generic.go                |    16 +
 src/crypto/elliptic/p256_s390x.go                  |   513 +
 src/crypto/hmac/hmac.go                            |     2 +-
 src/crypto/internal/cipherhw/asm_amd64.s           |    17 +
 src/crypto/internal/cipherhw/asm_s390x.s           |    44 +
 src/crypto/internal/cipherhw/cipherhw_amd64.go     |    16 +
 src/crypto/internal/cipherhw/cipherhw_s390x.go     |    18 +
 src/crypto/internal/cipherhw/doc.go                |     7 +
 src/crypto/internal/cipherhw/generic.go            |    11 +
 src/crypto/md5/example_test.go                     |    17 +
 src/crypto/md5/md5block_amd64p32.s                 |     2 +-
 src/crypto/rand/util_test.go                       |     9 +
 src/crypto/rc4/rc4_arm.s                           |     4 +-
 src/crypto/rsa/rsa.go                              |    23 +-
 src/crypto/rsa/rsa_test.go                         |    11 +
 src/crypto/sha1/example_test.go                    |    17 +
 src/crypto/sha1/sha1.go                            |    70 +-
 src/crypto/sha1/sha1_test.go                       |    17 +-
 src/crypto/sha1/sha1block_amd64.go                 |     2 +-
 src/crypto/sha1/sha1block_amd64p32.s               |     2 +-
 src/crypto/sha256/example_test.go                  |    41 +
 src/crypto/sha256/sha256block_386.s                |     2 +-
 src/crypto/sha256/sha256block_amd64.s              |     4 +-
 src/crypto/sha256/sha256block_decl.go              |     2 +-
 src/crypto/sha256/sha256block_generic.go           |     2 +-
 src/crypto/sha256/sha256block_ppc64le.s            |   269 +
 src/crypto/sha512/sha512block_decl.go              |     2 +-
 src/crypto/sha512/sha512block_generic.go           |     2 +-
 src/crypto/sha512/sha512block_ppc64le.s            |   293 +
 src/crypto/tls/alert.go                            |     2 +
 src/crypto/tls/cipher_suites.go                    |   155 +-
 src/crypto/tls/common.go                           |   230 +-
 src/crypto/tls/conn.go                             |   144 +-
 src/crypto/tls/conn_test.go                        |    22 +-
 src/crypto/tls/example_test.go                     |    58 +
 src/crypto/tls/handshake_client.go                 |   198 +-
 src/crypto/tls/handshake_client_test.go            |   538 +-
 src/crypto/tls/handshake_messages.go               |     7 +-
 src/crypto/tls/handshake_messages_test.go          |    63 +
 src/crypto/tls/handshake_server.go                 |   130 +-
 src/crypto/tls/handshake_server_test.go            |   307 +-
 src/crypto/tls/handshake_test.go                   |    71 +-
 src/crypto/tls/key_agreement.go                    |   156 +-
 .../testdata/Client-TLSv10-ClientCert-ECDSA-ECDSA  |   112 +-
 .../testdata/Client-TLSv10-ClientCert-ECDSA-RSA    |   175 +-
 .../testdata/Client-TLSv10-ClientCert-RSA-ECDSA    |   176 +-
 .../tls/testdata/Client-TLSv10-ClientCert-RSA-RSA  |   239 +-
 .../tls/testdata/Client-TLSv10-ECDHE-ECDSA-AES     |    93 +-
 .../tls/testdata/Client-TLSv10-ECDHE-RSA-AES       |   168 +-
 src/crypto/tls/testdata/Client-TLSv10-RSA-RC4      |   145 +-
 .../tls/testdata/Client-TLSv11-ECDHE-ECDSA-AES     |    95 +-
 .../tls/testdata/Client-TLSv11-ECDHE-RSA-AES       |   170 +-
 src/crypto/tls/testdata/Client-TLSv11-RSA-RC4      |   145 +-
 .../tls/testdata/Client-TLSv12-AES128-GCM-SHA256   |   149 +-
 .../tls/testdata/Client-TLSv12-AES128-SHA256       |    89 +
 .../tls/testdata/Client-TLSv12-AES256-GCM-SHA384   |   149 +-
 src/crypto/tls/testdata/Client-TLSv12-ALPN         |   165 +-
 src/crypto/tls/testdata/Client-TLSv12-ALPN-NoMatch |   158 +-
 .../testdata/Client-TLSv12-ClientCert-ECDSA-ECDSA  |   119 +-
 .../testdata/Client-TLSv12-ClientCert-ECDSA-RSA    |   177 +-
 .../Client-TLSv12-ClientCert-RSA-AES256-GCM-SHA384 |   250 +-
 .../testdata/Client-TLSv12-ClientCert-RSA-ECDSA    |   183 +-
 .../tls/testdata/Client-TLSv12-ClientCert-RSA-RSA  |   240 +-
 .../tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES     |    95 +-
 .../tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES-GCM |    87 +-
 .../Client-TLSv12-ECDHE-ECDSA-AES128-SHA256        |    91 +
 .../Client-TLSv12-ECDHE-ECDSA-AES256-GCM-SHA384    |    87 +-
 .../Client-TLSv12-ECDHE-ECDSA-CHACHA20-POLY1305    |    77 +
 .../tls/testdata/Client-TLSv12-ECDHE-RSA-AES       |   170 +-
 .../testdata/Client-TLSv12-ECDHE-RSA-AES128-SHA256 |    95 +
 .../Client-TLSv12-ECDHE-RSA-CHACHA20-POLY1305      |    81 +
 src/crypto/tls/testdata/Client-TLSv12-RSA-RC4      |   145 +-
 .../tls/testdata/Client-TLSv12-RenegotiateOnce     |   456 +-
 .../tls/testdata/Client-TLSv12-RenegotiateTwice    |   751 +-
 .../Client-TLSv12-RenegotiateTwiceRejected         |   463 +-
 .../testdata/Client-TLSv12-RenegotiationRejected   |   168 +-
 src/crypto/tls/testdata/Client-TLSv12-SCT          |   162 +-
 .../Client-TLSv12-X25519-ECDHE-RSA-AES-GCM         |    85 +
 src/crypto/tls/testdata/Server-SSLv3-RSA-3DES      |   140 +-
 src/crypto/tls/testdata/Server-SSLv3-RSA-AES       |   142 +-
 src/crypto/tls/testdata/Server-SSLv3-RSA-RC4       |   132 +-
 .../tls/testdata/Server-TLSv10-ECDHE-ECDSA-AES     |    81 +-
 src/crypto/tls/testdata/Server-TLSv10-RSA-3DES     |   132 +-
 src/crypto/tls/testdata/Server-TLSv10-RSA-AES      |   138 +-
 src/crypto/tls/testdata/Server-TLSv10-RSA-RC4      |   126 +-
 src/crypto/tls/testdata/Server-TLSv11-FallbackSCSV |    21 +-
 src/crypto/tls/testdata/Server-TLSv11-RSA-RC4      |   126 +-
 src/crypto/tls/testdata/Server-TLSv12-ALPN         |   181 +-
 src/crypto/tls/testdata/Server-TLSv12-ALPN-NoMatch |   182 +-
 .../Server-TLSv12-CipherSuiteCertPreferenceECDSA   |    97 +-
 .../Server-TLSv12-CipherSuiteCertPreferenceRSA     |   173 +-
 .../Server-TLSv12-ClientAuthRequestedAndECDSAGiven |   163 +-
 .../Server-TLSv12-ClientAuthRequestedAndGiven      |   227 +-
 .../Server-TLSv12-ClientAuthRequestedNotGiven      |   145 +-
 .../tls/testdata/Server-TLSv12-ECDHE-ECDSA-AES     |    83 +-
 src/crypto/tls/testdata/Server-TLSv12-IssueTicket  |   154 +-
 .../testdata/Server-TLSv12-IssueTicketPreDisable   |   154 +-
 src/crypto/tls/testdata/Server-TLSv12-RSA-3DES     |   137 +-
 src/crypto/tls/testdata/Server-TLSv12-RSA-AES      |   141 +-
 src/crypto/tls/testdata/Server-TLSv12-RSA-AES-GCM  |   149 +-
 .../testdata/Server-TLSv12-RSA-AES256-GCM-SHA384   |   149 +-
 src/crypto/tls/testdata/Server-TLSv12-RSA-RC4      |   131 +-
 src/crypto/tls/testdata/Server-TLSv12-Resume       |    64 +-
 .../tls/testdata/Server-TLSv12-ResumeDisabled      |   160 +-
 src/crypto/tls/testdata/Server-TLSv12-SNI          |   131 +-
 .../tls/testdata/Server-TLSv12-SNI-GetCertificate  |   131 +-
 .../Server-TLSv12-SNI-GetCertificateNotFound       |   131 +-
 .../Server-TLSv12-X25519-ECDHE-RSA-AES-GCM         |    79 +
 src/crypto/tls/tls.go                              |     6 +-
 src/crypto/tls/tls_test.go                         |   150 +-
 src/crypto/x509/cert_pool.go                       |    15 +-
 src/crypto/x509/pkix/pkix.go                       |    56 +-
 src/crypto/x509/root_cgo_darwin.go                 |    83 +-
 src/crypto/x509/root_darwin.go                     |   114 +-
 src/crypto/x509/root_darwin_test.go                |     1 -
 src/crypto/x509/root_linux.go                      |     9 +-
 src/crypto/x509/root_windows.go                    |    35 +-
 src/crypto/x509/verify.go                          |    76 +-
 src/crypto/x509/verify_test.go                     |   309 +-
 src/crypto/x509/x509.go                            |   218 +-
 src/crypto/x509/x509_test.go                       |   224 +-
 src/database/sql/convert.go                        |    72 +-
 src/database/sql/convert_test.go                   |    83 +
 src/database/sql/ctxutil.go                        |   156 +
 src/database/sql/driver/driver.go                  |   197 +-
 src/database/sql/driver/types.go                   |    42 +-
 src/database/sql/driver/types_test.go              |    16 +
 src/database/sql/example_test.go                   |    62 +
 src/database/sql/fakedb_test.go                    |   375 +-
 src/database/sql/internal/types.go                 |    11 +
 src/database/sql/sql.go                            |   909 +-
 src/database/sql/sql_test.go                       |   476 +-
 src/debug/elf/file.go                              |    89 +-
 src/debug/elf/file_test.go                         |    57 +
 .../testdata/go-relocation-test-gcc492-mipsle.obj  |   Bin 0 -> 2864 bytes
 .../testdata/go-relocation-test-gcc540-mips.obj    |   Bin 0 -> 3064 bytes
 .../testdata/go-relocation-test-gcc620-sparc64.obj |   Bin 0 -> 5952 bytes
 src/debug/gosym/pclntab.go                         |    10 +-
 src/debug/gosym/pclntab_test.go                    |     4 +-
 src/debug/macho/macho.go                           |     2 +-
 src/debug/pe/file.go                               |    21 +-
 src/debug/pe/file_test.go                          |     2 +-
 src/debug/pe/section.go                            |    14 +-
 src/debug/pe/string.go                             |    14 +-
 src/debug/pe/symbol.go                             |    13 +-
 src/encoding/asn1/asn1.go                          |     9 +-
 src/encoding/asn1/asn1_test.go                     |    41 +-
 src/encoding/asn1/marshal.go                       |   621 +-
 src/encoding/asn1/marshal_test.go                  |    34 +
 src/encoding/base64/base64.go                      |    24 +-
 src/encoding/base64/base64_test.go                 |    21 +
 src/encoding/binary/binary.go                      |    59 +-
 src/encoding/binary/binary_test.go                 |    39 +-
 src/encoding/csv/reader.go                         |    68 +-
 src/encoding/csv/reader_test.go                    |    72 +-
 src/encoding/gob/encoder.go                        |     3 +
 src/encoding/gob/encoder_test.go                   |    14 +
 src/encoding/hex/example_test.go                   |    98 +
 src/encoding/hex/hex.go                            |    15 +-
 src/encoding/hex/hex_test.go                       |    16 +
 src/encoding/json/decode.go                        |   129 +-
 src/encoding/json/decode_test.go                   |   297 +-
 src/encoding/json/encode.go                        |   132 +-
 src/encoding/json/encode_test.go                   |   273 +-
 src/encoding/json/example_marshaling_test.go       |    73 +
 src/encoding/json/example_test.go                  |    26 +-
 src/encoding/json/scanner_test.go                  |     1 +
 src/encoding/json/stream.go                        |     9 +-
 src/encoding/json/tables.go                        |   218 +
 src/encoding/json/tagkey_test.go                   |     5 +
 src/encoding/pem/example_test.go                   |    44 +
 src/encoding/pem/pem.go                            |    19 +-
 src/encoding/pem/pem_test.go                       |    42 +
 src/encoding/xml/marshal.go                        |   180 +-
 src/encoding/xml/marshal_test.go                   |    91 +-
 src/encoding/xml/read.go                           |    92 +-
 src/encoding/xml/read_test.go                      |    21 +-
 src/encoding/xml/typeinfo.go                       |     8 +-
 src/expvar/expvar.go                               |    27 +
 src/expvar/expvar_test.go                          |    28 +-
 src/flag/export_test.go                            |     1 +
 src/flag/flag.go                                   |    37 +-
 src/fmt/doc.go                                     |    70 +-
 src/fmt/export_test.go                             |     1 +
 src/fmt/fmt_test.go                                |    28 +-
 src/fmt/format.go                                  |     2 +-
 src/fmt/print.go                                   |     8 +
 src/fmt/scan.go                                    |    96 +-
 src/fmt/scan_test.go                               |   143 +-
 src/go/ast/ast.go                                  |     4 +-
 src/go/build/build.go                              |    67 +-
 src/go/build/build_test.go                         |    12 +
 src/go/build/deps_test.go                          |   140 +-
 src/go/build/doc.go                                |     1 +
 src/go/build/syslist.go                            |     2 +-
 src/go/build/testdata/ignored/ignored.go           |     3 +
 src/go/constant/value.go                           |    40 +-
 src/go/doc/comment.go                              |     2 +-
 src/go/doc/comment_test.go                         |     1 +
 src/go/doc/reader.go                               |    24 +-
 src/go/doc/testdata/issue17788.0.golden            |     8 +
 src/go/doc/testdata/issue17788.1.golden            |     8 +
 src/go/doc/testdata/issue17788.2.golden            |     8 +
 src/go/doc/testdata/issue17788.go                  |     8 +
 src/go/doc/testdata/predeclared.0.golden           |     8 +
 src/go/doc/testdata/predeclared.1.golden           |    22 +
 src/go/doc/testdata/predeclared.2.golden           |     8 +
 src/go/doc/testdata/predeclared.go                 |    22 +
 src/go/format/format_test.go                       |    27 +
 src/go/internal/gccgoimporter/importer.go          |     5 +-
 src/go/internal/gccgoimporter/importer_test.go     |     1 +
 src/go/internal/gccgoimporter/parser.go            |    40 +-
 .../internal/gccgoimporter/testdata/conversions.go |     5 +
 .../gccgoimporter/testdata/conversions.gox         |     6 +
 src/go/internal/gcimporter/bimport.go              |   288 +-
 src/go/internal/gcimporter/exportdata.go           |    10 +-
 src/go/internal/gcimporter/gcimporter.go           |   881 +-
 src/go/internal/gcimporter/gcimporter_test.go      |    85 +-
 src/go/internal/gcimporter/testdata/exports.go     |     7 +-
 .../internal/gcimporter/testdata/versions/test.go  |    25 +
 .../gcimporter/testdata/versions/test_go1.7_0.a    |   Bin 0 -> 1862 bytes
 .../gcimporter/testdata/versions/test_go1.7_1.a    |   Bin 0 -> 2316 bytes
 src/go/printer/printer.go                          |    38 +-
 src/go/printer/printer_test.go                     |    55 +-
 src/go/printer/testdata/comments.golden            |    26 +
 src/go/printer/testdata/comments.input             |    18 +
 src/go/scanner/scanner.go                          |     6 +-
 src/go/scanner/scanner_test.go                     |     1 +
 src/go/token/position.go                           |     2 +
 src/go/token/position_test.go                      |    31 +-
 src/go/types/api.go                                |    13 +-
 src/go/types/api_test.go                           |   392 +-
 src/go/types/assignments.go                        |     4 +-
 src/go/types/builtins.go                           |     6 +-
 src/go/types/call.go                               |    27 +-
 src/go/types/check.go                              |     1 -
 src/go/types/check_test.go                         |     2 +
 src/go/types/conversions.go                        |    11 +-
 src/go/types/decl.go                               |   104 +
 src/go/types/expr.go                               |    41 +-
 src/go/types/initorder.go                          |   230 +-
 src/go/types/object.go                             |    53 +-
 src/go/types/ordering.go                           |    20 +-
 src/go/types/predicates.go                         |    35 +-
 src/go/types/resolver.go                           |    22 +-
 src/go/types/sizes.go                              |    51 +-
 src/go/types/sizes_test.go                         |   112 +
 src/go/types/stdlib_test.go                        |     1 +
 src/go/types/stmt.go                               |    26 +-
 src/go/types/testdata/conversions2.src             |   313 +
 src/go/types/testdata/expr3.src                    |    24 +
 src/go/types/testdata/stmt0.src                    |    12 +-
 src/go/types/type.go                               |    11 +-
 src/go/types/typexpr.go                            |    25 +-
 src/hash/crc32/crc32.go                            |   160 +-
 src/hash/crc32/crc32_amd64.go                      |   214 +-
 src/hash/crc32/crc32_amd64.s                       |   116 +-
 src/hash/crc32/crc32_amd64p32.go                   |    37 +-
 src/hash/crc32/crc32_generic.go                    |    92 +-
 src/hash/crc32/crc32_otherarch.go                  |    15 +
 src/hash/crc32/crc32_s390x.go                      |    81 +-
 src/hash/crc32/crc32_test.go                       |   215 +-
 src/html/template/clone_test.go                    |    68 +
 src/html/template/content_test.go                  |    41 +
 src/html/template/context.go                       |    14 +-
 src/html/template/doc.go                           |     2 +-
 src/html/template/error.go                         |     2 +-
 src/html/template/escape.go                        |    14 +-
 src/html/template/escape_test.go                   |    14 +-
 src/html/template/js.go                            |    42 +-
 src/html/template/js_test.go                       |    18 +
 src/html/template/template.go                      |    81 +-
 src/html/template/template_test.go                 |   130 +-
 src/html/template/transition.go                    |    30 +-
 src/html/template/url.go                           |     2 +-
 src/image/color/color.go                           |    23 +-
 src/image/color/ycbcr.go                           |    94 +-
 src/image/color/ycbcr_test.go                      |    63 +-
 src/image/draw/bench_test.go                       |     2 +-
 src/image/draw/draw.go                             |    16 +-
 src/image/draw/example_test.go                     |    48 +
 src/image/gif/reader.go                            |    83 +-
 src/image/gif/reader_test.go                       |    17 +
 src/image/png/example_test.go                      |    77 +
 src/image/png/reader.go                            |   266 +-
 src/image/png/reader_test.go                       |   175 +-
 src/image/png/testdata/pngsuite/README             |    21 +-
 src/image/png/testdata/pngsuite/ftbbn0g01.png      |   Bin 0 -> 176 bytes
 src/image/png/testdata/pngsuite/ftbbn0g01.sng      |    44 +
 src/image/png/testdata/pngsuite/ftbbn0g02.png      |   Bin 0 -> 197 bytes
 src/image/png/testdata/pngsuite/ftbbn0g02.sng      |    45 +
 src/image/png/testdata/pngsuite/ftbbn0g04.png      |   Bin 0 -> 429 bytes
 src/image/png/testdata/pngsuite/ftbbn0g04.sng      |    45 +
 src/image/png/testdata/pngsuite/ftbbn2c16.png      |   Bin 0 -> 2041 bytes
 src/image/png/testdata/pngsuite/ftbbn2c16.sng      |    45 +
 src/image/png/testdata/pngsuite/ftbbn3p08.png      |   Bin 0 -> 1499 bytes
 src/image/png/testdata/pngsuite/ftbbn3p08.sng      |   292 +
 src/image/png/testdata/pngsuite/ftbgn2c16.png      |   Bin 0 -> 2041 bytes
 src/image/png/testdata/pngsuite/ftbgn2c16.sng      |    45 +
 src/image/png/testdata/pngsuite/ftbgn3p08.png      |   Bin 0 -> 1499 bytes
 src/image/png/testdata/pngsuite/ftbgn3p08.sng      |   292 +
 src/image/png/testdata/pngsuite/ftbrn2c08.png      |   Bin 0 -> 1633 bytes
 src/image/png/testdata/pngsuite/ftbrn2c08.sng      |    45 +
 src/image/png/testdata/pngsuite/ftbwn0g16.png      |   Bin 0 -> 1313 bytes
 src/image/png/testdata/pngsuite/ftbwn0g16.sng      |    45 +
 src/image/png/testdata/pngsuite/ftbwn3p08.png      |   Bin 0 -> 1496 bytes
 src/image/png/testdata/pngsuite/ftbwn3p08.sng      |   291 +
 src/image/png/testdata/pngsuite/ftbyn3p08.png      |   Bin 0 -> 1499 bytes
 src/image/png/testdata/pngsuite/ftbyn3p08.sng      |   292 +
 src/image/png/testdata/pngsuite/ftp0n0g08.png      |   Bin 0 -> 719 bytes
 src/image/png/testdata/pngsuite/ftp0n0g08.sng      |    41 +
 src/image/png/testdata/pngsuite/ftp0n2c08.png      |   Bin 0 -> 1594 bytes
 src/image/png/testdata/pngsuite/ftp0n2c08.sng      |    41 +
 src/image/png/testdata/pngsuite/ftp0n3p08.png      |   Bin 0 -> 1476 bytes
 src/image/png/testdata/pngsuite/ftp0n3p08.sng      |   288 +
 src/image/png/testdata/pngsuite/ftp1n3p08.png      |   Bin 0 -> 1483 bytes
 src/image/png/testdata/pngsuite/ftp1n3p08.sng      |   290 +
 src/image/png/writer.go                            |     5 +-
 src/index/suffixarray/example_test.go              |    22 +
 src/{cmd => }/internal/pprof/profile/encode.go     |     0
 src/{cmd => }/internal/pprof/profile/filter.go     |     0
 src/internal/pprof/profile/legacy_profile.go       |  1266 +
 src/{cmd => }/internal/pprof/profile/profile.go    |     0
 src/internal/pprof/profile/profile_test.go         |    79 +
 src/{cmd => }/internal/pprof/profile/proto.go      |     0
 src/{cmd => }/internal/pprof/profile/proto_test.go |     0
 src/{cmd => }/internal/pprof/profile/prune.go      |     0
 src/internal/race/norace.go                        |     2 +
 src/internal/race/race.go                          |     4 +
 src/internal/syscall/unix/getrandom_linux_mipsx.go |    11 +
 src/internal/syscall/windows/mksyscall.go          |     7 +
 src/internal/syscall/windows/registry/mksyscall.go |     7 +
 src/internal/syscall/windows/registry/syscall.go   |     2 -
 .../syscall/windows/registry/zsyscall_windows.go   |    27 +-
 src/internal/syscall/windows/reparse_windows.go    |    64 +
 src/internal/syscall/windows/security_windows.go   |    57 +
 src/internal/syscall/windows/syscall_windows.go    |    26 +-
 src/internal/syscall/windows/zsyscall_windows.go   |   174 +-
 src/internal/testenv/testenv.go                    |    52 +-
 src/internal/testenv/testenv_notwin.go             |    20 +
 src/internal/testenv/testenv_windows.go            |    49 +
 src/internal/trace/goroutines.go                   |     6 +-
 src/internal/trace/mkcanned.bash                   |    19 +
 src/internal/trace/order.go                        |     5 +-
 src/internal/trace/parser.go                       |    33 +-
 src/internal/trace/parser_test.go                  |    47 +-
 src/internal/trace/testdata/http_1_7_good          |   Bin 0 -> 1971 bytes
 src/internal/trace/testdata/stress_1_7_good        |   Bin 0 -> 396526 bytes
 .../trace/testdata/stress_start_stop_1_7_good      |   Bin 0 -> 2055 bytes
 src/internal/trace/writer.go                       |    45 +
 src/io/io.go                                       |     7 +-
 src/io/ioutil/ioutil.go                            |     9 +-
 src/io/ioutil/tempfile.go                          |     5 +
 src/io/ioutil/tempfile_test.go                     |    16 +
 src/io/multi.go                                    |     1 +
 src/io/multi_test.go                               |    32 +-
 src/io/pipe.go                                     |    18 +-
 src/log/log.go                                     |     2 +
 src/log/syslog/doc.go                              |     2 +-
 src/log/syslog/example_test.go                     |    23 +
 src/log/syslog/syslog.go                           |     2 +
 src/log/syslog/syslog_test.go                      |     5 +-
 src/make.bash                                      |     2 +
 src/make.rc                                        |     2 +-
 src/math/all_test.go                               |   145 +-
 src/math/arith_s390x.go                            |    29 +
 src/math/arith_s390x_test.go                       |   144 +
 src/math/big/arith_amd64.s                         |    35 +
 src/math/big/arith_decl_s390x.go                   |    23 +
 src/math/big/arith_mipsx.s                         |    46 +
 src/math/big/arith_ppc64.s                         |    14 +
 src/math/big/arith_ppc64le.s                       |    50 +
 src/math/big/arith_ppc64x.s                        |   178 +-
 src/math/big/arith_s390x.s                         |  1230 +-
 src/math/big/arith_s390x_test.go                   |    44 +
 src/math/big/arith_test.go                         |    13 +
 src/math/big/decimal.go                            |     7 +-
 src/math/big/decimal_test.go                       |    22 +-
 src/math/big/doc.go                                |     2 +-
 src/math/big/example_test.go                       |    13 +
 src/math/big/float.go                              |    44 +-
 src/math/big/float_test.go                         |    47 +-
 src/math/big/floatconv.go                          |    24 +-
 src/math/big/floatconv_test.go                     |    52 +
 src/math/big/floatexample_test.go                  |     6 +-
 src/math/big/floatmarsh.go                         |     2 +-
 src/math/big/ftoa.go                               |     2 +
 src/math/big/gcd_test.go                           |     3 +
 src/math/big/int.go                                |    39 +-
 src/math/big/int_test.go                           |   173 +-
 src/math/big/intconv.go                            |     4 +
 src/math/big/intmarsh.go                           |     6 +-
 src/math/big/nat.go                                |   176 +-
 src/math/big/natconv_test.go                       |     6 +
 src/math/big/prime.go                              |   320 +
 src/math/big/prime_test.go                         |   214 +
 src/math/big/rat_test.go                           |    12 +-
 src/math/big/ratconv.go                            |    16 +-
 src/math/big/ratconv_test.go                       |    11 +-
 src/math/cmplx/cmath_test.go                       |     8 +
 src/math/cmplx/example_test.go                     |    28 +
 src/math/cmplx/tan.go                              |    12 +-
 src/math/cosh_s390x.s                              |   227 +
 src/math/dim_arm64.s                               |    78 +
 src/math/exp_386.s                                 |    36 +-
 src/math/expm1.go                                  |     2 +-
 src/math/export_s390x_test.go                      |    14 +
 src/math/floor_arm64.s                             |    26 +
 src/math/floor_ppc64x.s                            |    25 +
 src/math/floor_s390x.s                             |    26 +
 src/math/gamma.go                                  |    43 +-
 src/math/j0.go                                     |    38 +-
 src/math/j1.go                                     |    38 +-
 src/math/jn.go                                     |     2 +-
 src/math/log10_s390x.s                             |   170 +
 src/math/log1p.go                                  |     7 +-
 src/math/modf_386.s                                |    12 +-
 src/math/modf_arm64.s                              |    18 +
 src/math/rand/gen_cooked.go                        |    89 +
 src/math/rand/race_test.go                         |     1 +
 src/math/rand/rand.go                              |    42 +-
 src/math/rand/rand_test.go                         |    15 +-
 src/math/rand/regress_test.go                      |    20 +
 src/math/rand/rng.go                               |   295 +-
 src/math/sin.go                                    |     8 +-
 src/math/sin_s390x.s                               |   356 +
 src/math/sincos.go                                 |     4 +-
 src/math/sinh.go                                   |     8 +-
 src/math/sinh_s390x.s                              |   261 +
 src/math/sinh_stub.s                               |    17 +
 src/math/sqrt_amd64.s                              |     5 +-
 src/math/sqrt_mipsx.s                              |    14 +
 src/math/stubs_arm64.s                             |    30 +-
 src/math/stubs_mips64x.s                           |     9 +
 src/math/stubs_mipsx.s                             |    98 +
 src/math/stubs_ppc64x.s                            |    18 +-
 src/math/stubs_s390x.s                             |   167 +-
 src/math/tan.go                                    |     4 +-
 src/math/tanh.go                                   |     4 +-
 src/math/tanh_s390x.s                              |   173 +
 src/mime/mediatype.go                              |    37 +-
 src/mime/mediatype_test.go                         |    14 +-
 src/mime/multipart/multipart.go                    |   219 +-
 src/mime/multipart/multipart_test.go               |    69 +
 src/mime/quotedprintable/example_test.go           |    37 +
 src/mime/quotedprintable/reader.go                 |    13 +-
 src/mime/quotedprintable/reader_test.go            |    16 +-
 src/net/cgo_unix.go                                |     5 +
 src/net/conf.go                                    |    26 +-
 src/net/conf_test.go                               |   114 +-
 src/net/dial.go                                    |    31 +-
 src/net/dial_test.go                               |    17 +
 src/net/dnsclient.go                               |    16 +-
 src/net/dnsclient_unix.go                          |    43 +-
 src/net/dnsclient_unix_test.go                     |    84 +-
 src/net/dnsconfig_unix.go                          |    25 +-
 src/net/dnsconfig_unix_test.go                     |    83 +
 src/net/dnsmsg.go                                  |     2 +-
 src/net/dnsmsg_test.go                             |     6 +-
 src/net/dnsname_test.go                            |    27 +-
 src/net/error_test.go                              |    21 +-
 src/net/example_test.go                            |    13 +
 src/net/fd_io_plan9.go                             |    93 +
 src/net/fd_plan9.go                                |   149 +-
 src/net/fd_poll_nacl.go                            |     2 +
 src/net/fd_poll_runtime.go                         |     4 +-
 src/net/fd_unix.go                                 |    15 +-
 src/net/fd_windows.go                              |    66 +-
 src/net/file.go                                    |     3 +
 src/net/file_plan9.go                              |     2 +-
 src/net/http/client.go                             |   349 +-
 src/net/http/client_test.go                        |   623 +-
 src/net/http/clientserver_test.go                  |   169 +-
 src/net/http/cookie.go                             |    66 +-
 src/net/http/cookie_test.go                        |    98 +
 src/net/http/cookiejar/dummy_publicsuffix_test.go  |    21 +
 src/net/http/cookiejar/example_test.go             |    65 +
 src/net/http/cookiejar/jar.go                      |    33 +-
 src/net/http/doc.go                                |    30 +-
 src/net/http/export_test.go                        |    39 +
 src/net/http/fcgi/fcgi.go                          |     6 +-
 src/net/http/fs.go                                 |   339 +-
 src/net/http/fs_test.go                            |   162 +-
 src/net/http/h2_bundle.go                          |  2040 +-
 src/net/http/header.go                             |     6 +-
 src/net/http/http.go                               |    98 +
 src/net/http/http_test.go                          |    20 +-
 src/net/http/httptest/example_test.go              |    16 +-
 src/net/http/httptest/httptest.go                  |     3 +
 src/net/http/httptest/recorder.go                  |    58 +-
 src/net/http/httptest/recorder_test.go             |    22 +-
 src/net/http/httptest/server.go                    |    15 +-
 src/net/http/httptrace/example_test.go             |    29 +
 src/net/http/httptrace/trace.go                    |    28 +-
 src/net/http/httptrace/trace_test.go               |    29 +-
 src/net/http/httputil/persist.go                   |     9 +-
 src/net/http/httputil/reverseproxy.go              |   149 +-
 src/net/http/httputil/reverseproxy_test.go         |   179 +-
 src/net/http/internal/chunked.go                   |    30 +-
 src/net/http/internal/chunked_test.go              |    27 +
 src/net/http/main_test.go                          |    21 +
 src/net/http/npn_test.go                           |     1 +
 src/net/http/range_test.go                         |     2 +-
 src/net/http/readrequest_test.go                   |    26 +-
 src/net/http/request.go                            |   165 +-
 src/net/http/request_test.go                       |   198 +-
 src/net/http/requestwrite_test.go                  |    82 +-
 src/net/http/response.go                           |     4 +-
 src/net/http/response_test.go                      |    35 +-
 src/net/http/responsewrite_test.go                 |    21 +-
 src/net/http/serve_test.go                         |   521 +-
 src/net/http/server.go                             |   715 +-
 src/net/http/sniff_test.go                         |     2 +
 src/net/http/transfer.go                           |    73 +-
 src/net/http/transport.go                          |   266 +-
 src/net/http/transport_internal_test.go            |    67 +
 src/net/http/transport_test.go                     |   381 +-
 src/net/interface.go                               |    24 +-
 src/net/interface_plan9.go                         |   198 +
 src/net/interface_solaris.go                       |   107 +
 src/net/interface_stub.go                          |     2 +-
 src/net/interface_test.go                          |    11 +-
 src/net/ip.go                                      |    92 +-
 src/net/ip_test.go                                 |    16 +-
 src/net/iprawsock.go                               |    11 +-
 src/net/iprawsock_posix.go                         |     4 +
 src/net/ipsock.go                                  |    54 +-
 src/net/ipsock_plan9.go                            |    60 +-
 src/net/ipsock_posix.go                            |     3 +
 src/net/ipsock_test.go                             |    14 +-
 src/net/lookup.go                                  |   275 +-
 src/net/lookup_nacl.go                             |    52 +
 src/net/lookup_plan9.go                            |    37 +-
 src/net/lookup_stub.go                             |    52 -
 src/net/lookup_test.go                             |    78 +-
 src/net/lookup_unix.go                             |    37 +-
 src/net/lookup_windows.go                          |    59 +-
 src/net/lookup_windows_test.go                     |     4 +-
 src/net/mail/message.go                            |    15 +-
 src/net/mail/message_test.go                       |    23 +-
 src/net/main_test.go                               |     2 +
 src/net/net.go                                     |   122 +-
 src/net/net_test.go                                |   104 +-
 src/net/parse.go                                   |    58 +-
 src/net/parse_test.go                              |     7 +-
 src/net/port_unix.go                               |    27 +-
 src/net/rpc/client.go                              |     2 +
 src/net/rpc/client_test.go                         |     4 -
 src/net/rpc/server.go                              |     6 +-
 src/net/rpc/server_test.go                         |     3 +-
 src/net/smtp/smtp.go                               |     5 +-
 src/net/smtp/smtp_test.go                          |    71 +-
 src/net/sock_linux.go                              |     2 +-
 src/net/sock_posix.go                              |     3 +
 src/net/tcpsock.go                                 |    10 +-
 src/net/tcpsock_posix.go                           |     4 +
 src/net/tcpsock_test.go                            |   120 +-
 src/net/tcpsock_unix_test.go                       |     4 +-
 src/net/testdata/invalid-ndots-resolv.conf         |     1 +
 src/net/testdata/large-ndots-resolv.conf           |     1 +
 src/net/testdata/negative-ndots-resolv.conf        |     1 +
 src/net/textproto/header.go                        |     4 +-
 src/net/timeout_test.go                            |    47 +-
 src/net/udpsock.go                                 |    14 +-
 src/net/udpsock_plan9.go                           |    38 +-
 src/net/udpsock_plan9_test.go                      |    69 +
 src/net/udpsock_posix.go                           |     4 +
 src/net/unixsock.go                                |    14 +-
 src/net/unixsock_posix.go                          |    25 +-
 src/net/unixsock_test.go                           |   124 +-
 src/net/url/example_test.go                        |    19 +
 src/net/url/url.go                                 |   185 +-
 src/net/url/url_test.go                            |   327 +-
 src/net/writev_test.go                             |   225 +
 src/net/writev_unix.go                             |    95 +
 src/os/dir.go                                      |    46 +
 src/os/dir_unix.go                                 |    27 +
 src/os/dir_windows.go                              |    64 +
 src/os/doc.go                                      |   139 -
 src/os/env.go                                      |     3 +-
 src/os/env_test.go                                 |    28 +
 src/os/env_unix_test.go                            |    26 +
 src/os/error.go                                    |    14 +
 src/os/error_plan9.go                              |    48 +-
 src/os/error_test.go                               |     2 +
 src/os/error_unix.go                               |    33 +-
 src/os/error_windows.go                            |    34 +-
 src/os/error_windows_test.go                       |     4 +
 src/os/example_test.go                             |   106 +
 src/os/exec.go                                     |    87 +
 src/os/exec/example_test.go                        |    62 +
 src/os/exec/exec.go                                |    63 +-
 src/os/exec/exec_test.go                           |    50 +-
 src/os/exec_windows.go                             |     4 +-
 src/os/executable.go                               |    23 +
 src/os/executable_darwin.go                        |    24 +
 src/os/executable_freebsd.go                       |    33 +
 src/os/executable_plan9.go                         |    19 +
 src/os/executable_procfs.go                        |    36 +
 src/os/executable_solaris.go                       |    27 +
 src/os/executable_test.go                          |    87 +
 src/os/executable_windows.go                       |    32 +
 src/os/export_windows_test.go                      |    13 +
 src/os/file.go                                     |    45 +-
 src/os/file_plan9.go                               |    14 +-
 src/os/file_posix.go                               |    22 +-
 src/os/file_unix.go                                |    97 +-
 src/os/file_windows.go                             |   247 +-
 src/os/os_test.go                                  |   145 +-
 src/os/os_unix_test.go                             |    39 +
 src/os/os_windows_test.go                          |   555 +-
 src/os/path_test.go                                |    10 +-
 src/os/path_unix.go                                |    18 +
 src/os/path_windows.go                             |   190 +
 src/os/path_windows_test.go                        |    46 +
 src/os/signal/doc.go                               |     9 +-
 src/os/signal/signal_windows_test.go               |     3 +-
 src/os/stat_plan9.go                               |    10 +-
 src/os/stat_unix.go                                |    52 +
 src/os/stat_windows.go                             |    98 +-
 src/os/sys.go                                      |    10 +
 src/os/types.go                                    |     5 +
 src/os/types_plan9.go                              |    13 +-
 src/os/types_unix.go                               |     6 +
 src/os/wait_wait6.go                               |     1 +
 src/os/wait_waitid.go                              |     2 +-
 src/path/filepath/match.go                         |    18 +-
 src/path/filepath/match_test.go                    |    11 +-
 src/path/filepath/path.go                          |    11 +-
 src/path/filepath/path_test.go                     |   103 +-
 src/path/filepath/path_unix.go                     |     2 +
 src/path/filepath/path_windows.go                  |     8 +-
 src/path/filepath/path_windows_test.go             |    72 +-
 src/path/filepath/symlink.go                       |     5 +-
 src/path/path.go                                   |     2 +
 src/plugin/plugin.go                               |    73 +
 src/plugin/plugin_dlopen.go                        |   138 +
 src/plugin/plugin_stubs.go                         |    17 +
 src/reflect/all_test.go                            |   246 +-
 src/reflect/asm_mipsx.s                            |    34 +
 src/reflect/deepequal.go                           |    11 +-
 src/reflect/export_test.go                         |     4 +
 src/reflect/makefunc.go                            |     2 +
 src/reflect/swapper.go                             |    74 +
 src/reflect/type.go                                |   147 +-
 src/reflect/value.go                               |    18 +-
 src/regexp/all_test.go                             |   124 +-
 src/regexp/exec.go                                 |    31 +-
 src/regexp/exec_test.go                            |     6 +
 src/regexp/onepass.go                              |     5 -
 src/regexp/regexp.go                               |    56 +-
 src/runtime/HACKING.md                             |   135 +
 src/runtime/alg.go                                 |    10 +-
 src/runtime/append_test.go                         |    16 +
 src/runtime/asm.s                                  |     3 -
 src/runtime/asm_386.s                              |   103 +-
 src/runtime/asm_amd64.s                            |   174 +-
 src/runtime/asm_amd64p32.s                         |    94 +-
 src/runtime/asm_arm.s                              |    95 +-
 src/runtime/asm_arm64.s                            |   104 +-
 src/runtime/asm_mips64x.s                          |   105 +-
 src/runtime/asm_mipsx.s                            |   794 +
 src/runtime/asm_ppc64x.s                           |   366 +-
 src/runtime/asm_s390x.s                            |   462 +-
 src/runtime/atomic_mipsx.s                         |    11 +
 src/runtime/atomic_pointer.go                      |    23 +-
 src/runtime/cgo/asm_arm64.s                        |     1 -
 src/runtime/cgo/asm_mips64x.s                      |    12 +-
 src/runtime/cgo/asm_s390x.s                        |    46 +-
 src/runtime/cgo/cgo.go                             |     3 -
 src/runtime/cgo/gcc_context.c                      |     2 +-
 src/runtime/cgo/gcc_dragonfly_amd64.c              |    12 -
 src/runtime/cgo/gcc_libinit_windows.c              |     4 +-
 src/runtime/cgo/gcc_linux_mips64x.c                |     2 +-
 src/runtime/cgo/gcc_mips64x.S                      |     5 +-
 src/runtime/cgo/gcc_s390x.S                        |    60 +-
 src/runtime/cgo/gcc_setenv.c                       |     4 +
 src/runtime/cgo/gcc_sigaction.c                    |    76 +
 src/runtime/cgo/sigaction.go                       |    22 +
 src/runtime/cgo_mips64x.go                         |    12 -
 src/runtime/cgo_mmap.go                            |     1 -
 src/runtime/cgo_sigaction.go                       |    89 +
 src/runtime/cgocall.go                             |    71 +-
 src/runtime/cgocheck.go                            |     4 +-
 src/runtime/chan.go                                |    49 +-
 src/runtime/chan_test.go                           |    11 +-
 src/runtime/cpuflags_amd64.go                      |    75 +
 src/runtime/cpuidlow_amd64.s                       |    22 +
 src/runtime/cpuprof.go                             |     3 +-
 src/runtime/cputicks.go                            |     2 +
 src/runtime/crash_cgo_test.go                      |   115 +-
 src/runtime/crash_test.go                          |    55 +-
 src/runtime/crash_unix_test.go                     |     4 +-
 src/runtime/debug/garbage.go                       |     8 +-
 src/runtime/debug/garbage_test.go                  |    19 +-
 src/runtime/defs1_linux.go                         |     2 +-
 src/runtime/defs1_netbsd_386.go                    |     6 -
 src/runtime/defs1_netbsd_amd64.go                  |     7 -
 src/runtime/defs1_netbsd_arm.go                    |    17 +-
 src/runtime/defs1_solaris_amd64.go                 |    10 +-
 src/runtime/defs2_linux.go                         |     2 +-
 src/runtime/defs3_linux.go                         |     2 +-
 src/runtime/defs_arm_linux.go                      |     2 +-
 src/runtime/defs_dragonfly.go                      |     1 -
 src/runtime/defs_dragonfly_amd64.go                |     7 -
 src/runtime/defs_freebsd.go                        |     1 -
 src/runtime/defs_freebsd_386.go                    |     6 -
 src/runtime/defs_freebsd_amd64.go                  |     7 -
 src/runtime/defs_freebsd_arm.go                    |     6 -
 src/runtime/defs_linux_386.go                      |     4 +-
 src/runtime/defs_linux_amd64.go                    |     4 +-
 src/runtime/defs_linux_arm.go                      |     4 +-
 src/runtime/defs_linux_arm64.go                    |     4 +-
 src/runtime/defs_linux_mips64x.go                  |     4 +-
 src/runtime/defs_linux_mipsx.go                    |   188 +
 src/runtime/defs_linux_ppc64.go                    |     4 +-
 src/runtime/defs_linux_ppc64le.go                  |     4 +-
 src/runtime/defs_linux_s390x.go                    |     4 +-
 src/runtime/defs_netbsd.go                         |     1 -
 src/runtime/defs_openbsd.go                        |     1 -
 src/runtime/defs_openbsd_386.go                    |     6 -
 src/runtime/defs_openbsd_amd64.go                  |     7 -
 src/runtime/defs_openbsd_arm.go                    |     6 -
 src/runtime/defs_plan9_386.go                      |     3 +
 src/runtime/defs_plan9_amd64.go                    |     3 +
 src/runtime/defs_plan9_arm.go                      |     3 +
 src/runtime/defs_solaris.go                        |     1 -
 src/runtime/duff_arm64.s                           |   387 +-
 src/runtime/export_mmap_test.go                    |     6 +
 src/runtime/export_test.go                         |    17 +-
 src/runtime/extern.go                              |     5 +
 src/runtime/gc_test.go                             |    42 -
 src/runtime/gcinfo_test.go                         |     2 +-
 src/runtime/hash32.go                              |     2 +-
 src/runtime/hash_test.go                           |     3 +
 src/runtime/hashmap.go                             |   305 +-
 src/runtime/hashmap_fast.go                        |    36 +-
 src/runtime/heapdump.go                            |    18 +-
 src/runtime/iface.go                               |   237 +-
 src/runtime/internal/atomic/asm.s                  |     8 -
 src/runtime/internal/atomic/asm_386.s              |     5 +-
 src/runtime/internal/atomic/asm_amd64.s            |     5 +-
 src/runtime/internal/atomic/asm_amd64p32.s         |     2 +-
 src/runtime/internal/atomic/asm_arm.s              |     2 +-
 src/runtime/internal/atomic/asm_arm64.s            |     2 +-
 src/runtime/internal/atomic/asm_mipsx.s            |   149 +
 src/runtime/internal/atomic/asm_s390x.s            |     8 +-
 src/runtime/internal/atomic/atomic_arm.go          |    18 +
 src/runtime/internal/atomic/atomic_arm64.go        |    36 +-
 src/runtime/internal/atomic/atomic_arm64.s         |    19 +
 src/runtime/internal/atomic/atomic_mipsx.go        |   132 +
 src/runtime/internal/atomic/atomic_mipsx.s         |    28 +
 src/runtime/internal/atomic/atomic_ppc64x.s        |    12 +-
 src/runtime/internal/atomic/atomic_test.go         |    44 +-
 src/runtime/internal/atomic/bench_test.go          |    28 +
 src/runtime/internal/atomic/sys_nacl_arm.s         |     3 -
 src/runtime/internal/sys/arch.go                   |     1 +
 src/runtime/internal/sys/arch_386.go               |    16 +-
 src/runtime/internal/sys/arch_amd64.go             |    16 +-
 src/runtime/internal/sys/arch_amd64p32.go          |    16 +-
 src/runtime/internal/sys/arch_arm.go               |    16 +-
 src/runtime/internal/sys/arch_arm64.go             |    16 +-
 src/runtime/internal/sys/arch_mips.go              |    18 +
 src/runtime/internal/sys/arch_mips64.go            |    16 +-
 src/runtime/internal/sys/arch_mips64le.go          |    16 +-
 src/runtime/internal/sys/arch_mipsle.go            |    18 +
 src/runtime/internal/sys/arch_ppc64.go             |    16 +-
 src/runtime/internal/sys/arch_ppc64le.go           |    16 +-
 src/runtime/internal/sys/arch_s390x.go             |    16 +-
 src/runtime/internal/sys/intrinsics.go             |    33 -
 src/runtime/internal/sys/intrinsics_386.s          |    16 -
 src/runtime/internal/sys/intrinsics_stubs.go       |     2 -
 src/runtime/internal/sys/intrinsics_test.go        |    16 -
 src/runtime/internal/sys/zgoarch_mips.go           |    26 +
 src/runtime/internal/sys/zgoarch_mipsle.go         |    26 +
 src/runtime/lfstack_32bit.go                       |     2 +-
 src/runtime/malloc.go                              |   173 +-
 src/runtime/map_test.go                            |     1 +
 src/runtime/mbarrier.go                            |   215 +-
 src/runtime/mbitmap.go                             |   298 +-
 src/runtime/mcache.go                              |     3 +-
 src/runtime/mcentral.go                            |     2 +
 src/runtime/mem_linux.go                           |     9 +-
 src/runtime/mem_plan9.go                           |    10 +-
 src/runtime/memclr_386.s                           |     4 +-
 src/runtime/memclr_amd64.s                         |     4 +-
 src/runtime/memclr_arm.s                           |     4 +-
 src/runtime/memclr_arm64.s                         |     4 +-
 src/runtime/memclr_mips64x.s                       |     4 +-
 src/runtime/memclr_mipsx.s                         |    71 +
 src/runtime/memclr_plan9_386.s                     |     4 +-
 src/runtime/memclr_plan9_amd64.s                   |     4 +-
 src/runtime/memclr_ppc64x.s                        |    75 +-
 src/runtime/memclr_s390x.s                         |    74 +-
 src/runtime/memmove_386.s                          |     2 +-
 src/runtime/memmove_amd64.s                        |   245 +-
 src/runtime/memmove_arm.s                          |     2 +-
 src/runtime/memmove_linux_amd64_test.go            |     3 +-
 src/runtime/memmove_mipsx.s                        |   258 +
 src/runtime/memmove_plan9_386.s                    |     2 +-
 src/runtime/memmove_plan9_amd64.s                  |     2 +-
 src/runtime/memmove_test.go                        |   108 +
 src/runtime/mfinal.go                              |    46 +-
 src/runtime/mfixalloc.go                           |    15 +-
 src/runtime/mgc.go                                 |   356 +-
 src/runtime/mgcmark.go                             |   517 +-
 src/runtime/mgcsweep.go                            |    50 +-
 src/runtime/mgcsweepbuf.go                         |   178 +
 src/runtime/mgcwork.go                             |    27 +-
 src/runtime/mheap.go                               |   279 +-
 src/runtime/mkduff.go                              |    16 +-
 src/runtime/mksizeclasses.go                       |   309 +
 src/runtime/mprof.go                               |   105 +-
 src/runtime/msan_amd64.s                           |     6 +-
 src/runtime/msize.go                               |   239 +-
 src/runtime/mstats.go                              |   395 +-
 src/runtime/mstkbar.go                             |     4 +
 src/runtime/net_plan9.go                           |    29 +
 src/runtime/netpoll.go                             |     4 +
 src/runtime/noasm.go                               |     1 +
 src/runtime/os3_plan9.go                           |     3 +
 src/runtime/os3_solaris.go                         |   118 +-
 src/runtime/os_darwin.go                           |   147 +-
 src/runtime/os_darwin_arm.go                       |     4 +-
 src/runtime/os_darwin_arm64.go                     |     4 +-
 src/runtime/os_dragonfly.go                        |   110 +-
 src/runtime/os_freebsd.go                          |   113 +-
 src/runtime/os_freebsd_arm.go                      |     4 +-
 src/runtime/os_linux.go                            |   170 +-
 src/runtime/os_linux_arm.go                        |     5 +-
 src/runtime/os_linux_arm64.go                      |     4 +-
 src/runtime/os_linux_be64.go                       |    48 +
 src/runtime/os_linux_generic.go                    |     9 +-
 src/runtime/os_linux_mips64x.go                    |    12 +-
 src/runtime/os_linux_mipsx.go                      |    62 +
 src/runtime/os_linux_noauxv.go                     |     2 +-
 src/runtime/os_linux_ppc64x.go                     |    60 +
 src/runtime/os_linux_s390x.go                      |    50 +-
 src/runtime/os_nacl.go                             |     9 +-
 src/runtime/os_nacl_arm.go                         |     4 +-
 src/runtime/os_netbsd.go                           |   102 +-
 src/runtime/os_netbsd_arm.go                       |     4 +-
 src/runtime/os_openbsd.go                          |   135 +-
 src/runtime/os_openbsd_arm.go                      |     4 +-
 src/runtime/os_plan9.go                            |    55 +-
 src/runtime/os_plan9_arm.go                        |     4 +-
 src/runtime/os_windows.go                          |   112 +-
 src/runtime/panic.go                               |   160 +-
 src/runtime/plugin.go                              |    96 +
 .../pprof/internal/protopprof/protomemprofile.go   |    83 +
 .../internal/protopprof/protomemprofile_test.go    |   104 +
 .../pprof/internal/protopprof/protopprof.go        |   105 +
 .../pprof/internal/protopprof/protopprof_test.go   |   171 +
 src/runtime/pprof/mprof_test.go                    |    29 +-
 src/runtime/pprof/pprof.go                         |   211 +-
 src/runtime/pprof/pprof_test.go                    |   171 +-
 src/runtime/print.go                               |    40 +-
 src/runtime/proc.go                                |   299 +-
 src/runtime/race.go                                |    40 +-
 src/runtime/race/README                            |     2 +-
 src/runtime/race/output_test.go                    |    78 +-
 src/runtime/race/race_darwin_amd64.syso            |   Bin 326172 -> 328168 bytes
 src/runtime/race/race_freebsd_amd64.syso           |   Bin 404216 -> 405576 bytes
 src/runtime/race/race_linux_amd64.syso             |   Bin 376048 -> 378032 bytes
 src/runtime/race/race_test.go                      |    17 +-
 src/runtime/race/race_windows_amd64.syso           |   Bin 367717 -> 369467 bytes
 src/runtime/race/testdata/cgo_test.go              |     3 +-
 src/runtime/race/testdata/pool_test.go             |    47 +
 src/runtime/race/testdata/reflect_test.go          |    46 +
 src/runtime/rt0_android_amd64.s                    |    15 +-
 src/runtime/rt0_android_arm.s                      |    15 +-
 src/runtime/rt0_android_arm64.s                    |     7 +-
 src/runtime/rt0_linux_mipsx.s                      |    27 +
 src/runtime/rune.go                                |   219 -
 src/runtime/runtime-gdb_test.go                    |   126 +-
 src/runtime/runtime-lldb_test.go                   |     4 +-
 src/runtime/runtime.go                             |     3 +
 src/runtime/runtime1.go                            |    34 +-
 src/runtime/runtime2.go                            |    33 +-
 src/runtime/runtime_mmap_test.go                   |    28 +-
 src/runtime/select.go                              |     6 +-
 src/runtime/sema.go                                |    40 +-
 src/runtime/sigaction_linux.go                     |    11 +
 src/runtime/signal1_unix.go                        |   350 -
 src/runtime/signal2_unix.go                        |    69 -
 src/runtime/signal_386.go                          |   179 +-
 src/runtime/signal_amd64x.go                       |   198 +-
 src/runtime/signal_arm.go                          |   164 +-
 src/runtime/signal_arm64.go                        |   164 +-
 src/runtime/signal_darwin.go                       |    49 -
 src/runtime/signal_darwin_386.go                   |    27 +-
 src/runtime/signal_darwin_amd64.go                 |    63 +-
 src/runtime/signal_darwin_arm.go                   |    41 +-
 src/runtime/signal_darwin_arm64.go                 |    73 +-
 src/runtime/signal_dragonfly_amd64.go              |    41 +-
 src/runtime/signal_freebsd.go                      |    41 -
 src/runtime/signal_freebsd_386.go                  |    25 +-
 src/runtime/signal_freebsd_amd64.go                |    41 +-
 src/runtime/signal_freebsd_arm.go                  |    39 +-
 src/runtime/signal_linux_386.go                    |    37 +-
 src/runtime/signal_linux_amd64.go                  |    41 +-
 src/runtime/signal_linux_arm.go                    |    49 +-
 src/runtime/signal_linux_arm64.go                  |    77 +-
 src/runtime/signal_linux_mips64x.go                |    81 +-
 src/runtime/signal_linux_mipsx.go                  |    65 +
 src/runtime/signal_linux_ppc64x.go                 |    85 +-
 src/runtime/signal_linux_s390x.go                  |   211 +-
 src/runtime/signal_mips64x.go                      |   164 +-
 src/runtime/signal_mipsx.go                        |    91 +
 src/runtime/signal_nacl_386.go                     |    37 +-
 src/runtime/signal_nacl_amd64p32.go                |    41 +-
 src/runtime/signal_nacl_arm.go                     |    38 +-
 src/runtime/signal_netbsd_386.go                   |    35 +-
 src/runtime/signal_netbsd_amd64.go                 |    41 +-
 src/runtime/signal_netbsd_arm.go                   |    49 +-
 src/runtime/signal_openbsd.go                      |    41 -
 src/runtime/signal_openbsd_386.go                  |    24 +-
 src/runtime/signal_openbsd_amd64.go                |    40 +-
 src/runtime/signal_openbsd_arm.go                  |    38 +-
 src/runtime/signal_ppc64x.go                       |   167 +-
 src/runtime/signal_sighandler.go                   |   133 +
 src/runtime/signal_sigtramp.go                     |    58 -
 src/runtime/signal_solaris.go                      |     2 +-
 src/runtime/signal_solaris_amd64.go                |    41 +-
 src/runtime/signal_unix.go                         |   640 +-
 src/runtime/signal_windows.go                      |     2 +-
 src/runtime/sigpanic_unix.go                       |    53 -
 src/runtime/sigtab_linux_generic.go                |     2 +
 src/runtime/sigtab_linux_mips64x.go                |    81 -
 src/runtime/sigtab_linux_mipsx.go                  |   145 +
 src/runtime/sizeclasses.go                         |    27 +
 src/runtime/slice.go                               |    32 +-
 src/runtime/softfloat_arm.go                       |    35 +
 src/runtime/stack.go                               |    44 +-
 src/runtime/string.go                              |   186 +-
 src/runtime/string_test.go                         |   163 +-
 src/runtime/stubs.go                               |    94 +-
 src/runtime/stubs32.go                             |     2 +-
 src/runtime/stubs_asm.go                           |    11 +
 src/runtime/symtab.go                              |   111 +-
 src/runtime/sys_arm.go                             |    17 -
 src/runtime/sys_arm64.go                           |    18 -
 src/runtime/sys_darwin_386.s                       |    37 +-
 src/runtime/sys_darwin_amd64.s                     |    45 +-
 src/runtime/sys_darwin_arm.s                       |     4 +-
 src/runtime/sys_darwin_arm64.s                     |     2 +-
 src/runtime/sys_dragonfly_amd64.s                  |    39 +-
 src/runtime/sys_freebsd_386.s                      |    20 +-
 src/runtime/sys_freebsd_amd64.s                    |    27 +-
 src/runtime/sys_linux_386.s                        |    32 +-
 src/runtime/sys_linux_amd64.s                      |    42 +-
 src/runtime/sys_linux_arm.s                        |    25 +-
 src/runtime/sys_linux_arm64.s                      |    16 +-
 src/runtime/sys_linux_mips64x.s                    |    22 +-
 src/runtime/sys_linux_mipsx.s                      |   467 +
 src/runtime/sys_linux_ppc64x.s                     |    16 +-
 src/runtime/sys_linux_s390x.s                      |    34 +-
 src/runtime/sys_mips64x.go                         |    23 -
 src/runtime/sys_mipsx.go                           |    20 +
 src/runtime/sys_nacl_386.s                         |     4 +-
 src/runtime/sys_nacl_amd64p32.s                    |     4 +-
 src/runtime/sys_nacl_arm.s                         |     4 +-
 src/runtime/sys_netbsd_386.s                       |    22 +-
 src/runtime/sys_netbsd_amd64.s                     |    27 +-
 src/runtime/sys_netbsd_arm.s                       |     2 +-
 src/runtime/sys_openbsd_386.s                      |    24 +-
 src/runtime/sys_openbsd_amd64.s                    |    29 +-
 src/runtime/sys_openbsd_arm.s                      |    80 +-
 src/runtime/sys_plan9_386.s                        |     4 +-
 src/runtime/sys_plan9_amd64.s                      |     6 +-
 src/runtime/sys_plan9_arm.s                        |     6 +-
 src/runtime/sys_ppc64x.go                          |    17 -
 src/runtime/sys_s390x.go                           |    27 -
 src/runtime/sys_solaris_amd64.s                    |    13 +
 src/runtime/sys_windows_386.s                      |     8 +-
 src/runtime/sys_windows_amd64.s                    |    12 +-
 src/runtime/sys_x86.go                             |    30 -
 src/runtime/syscall_windows_test.go                |    94 +
 src/runtime/testdata/testprog/deadlock.go          |    11 +
 src/runtime/testdata/testprog/gc.go                |    18 +-
 src/runtime/testdata/testprog/map.go               |    77 +
 src/runtime/testdata/testprogcgo/pprof.go          |     2 +-
 src/runtime/testdata/testprogcgo/raceprof.go       |    78 +
 src/runtime/testdata/testprogcgo/racesig.go        |   102 +
 src/runtime/testdata/testprogcgo/threadpprof.go    |    35 +-
 src/runtime/testdata/testprogcgo/threadprof.go     |     9 +-
 src/runtime/time.go                                |     8 +-
 src/runtime/tls_mipsx.s                            |    21 +
 src/runtime/trace.go                               |   134 +-
 src/runtime/trace/trace_stack_test.go              |    27 +-
 src/runtime/trace/trace_test.go                    |    29 +-
 src/runtime/traceback.go                           |    63 +-
 src/runtime/type.go                                |    83 +-
 src/runtime/unaligned2.go                          |     2 +-
 src/runtime/utf8.go                                |   123 +
 src/runtime/vdso_none.go                           |     1 +
 src/runtime/vlop_386.s                             |    20 +-
 src/runtime/vlop_arm.s                             |    34 +-
 src/runtime/vlrt.go                                |    23 +-
 src/runtime/write_err_android.go                   |     4 +-
 src/sort/example_search_test.go                    |    42 +
 src/sort/genzfunc.go                               |   122 +
 src/sort/sort.go                                   |    68 +-
 src/sort/sort_test.go                              |    92 +-
 src/sort/zfuncversion.go                           |   265 +
 src/strconv/atoi.go                                |     4 +
 src/strconv/decimal.go                             |     6 +-
 src/strconv/ftoa_test.go                           |     3 +
 src/strconv/quote.go                               |    10 +
 src/strconv/quote_test.go                          |     3 +-
 src/strconv/strconv_test.go                        |    31 +
 src/strings/strings.go                             |   163 +-
 src/strings/strings_amd64.go                       |    52 +-
 src/strings/strings_generic.go                     |     2 +-
 src/strings/strings_s390x.go                       |    98 +
 src/strings/strings_test.go                        |   178 +-
 src/sync/atomic/asm_amd64.s                        |     3 +
 src/sync/atomic/asm_amd64p32.s                     |    15 +-
 src/sync/atomic/asm_arm.s                          |    42 +-
 src/sync/atomic/asm_mips64x.s                      |     4 +-
 src/sync/atomic/asm_mipsx.s                        |    85 +
 src/sync/atomic/asm_ppc64x.s                       |     4 +-
 src/sync/atomic/asm_s390x.s                        |    22 +-
 src/sync/atomic/atomic_test.go                     |    29 +-
 src/sync/cond_test.go                              |     4 +-
 src/sync/example_pool_test.go                      |    45 +
 src/sync/mutex.go                                  |     8 +-
 src/sync/mutex_test.go                             |   108 +-
 src/sync/pool.go                                   |    81 +-
 src/sync/pool_test.go                              |     3 +-
 src/sync/runtime.go                                |     3 +
 src/sync/rwmutex.go                                |     4 +-
 src/sync/rwmutex_test.go                           |    42 -
 src/syscall/asm9_unix1_amd64.s                     |    45 +
 src/syscall/asm9_unix2_amd64.s                     |    46 +
 src/syscall/asm_darwin_arm.s                       |   121 +-
 src/syscall/asm_darwin_arm64.s                     |    30 +-
 src/syscall/asm_dragonfly_amd64.s                  |   134 -
 src/syscall/asm_freebsd_386.s                      |   143 -
 src/syscall/asm_freebsd_amd64.s                    |   137 -
 src/syscall/asm_linux_mipsx.s                      |   142 +
 src/syscall/asm_netbsd_386.s                       |   143 -
 src/syscall/asm_netbsd_amd64.s                     |   136 -
 src/syscall/asm_openbsd_386.s                      |   143 -
 src/syscall/asm_openbsd_amd64.s                    |   136 -
 src/syscall/asm_openbsd_arm.s                      |    10 +-
 src/syscall/asm_plan9_386.s                        |    61 +-
 src/syscall/asm_plan9_amd64.s                      |    59 +-
 src/syscall/asm_unix_386.s                         |   142 +
 src/syscall/asm_unix_amd64.s                       |   102 +
 src/syscall/const_plan9.go                         |    11 +
 src/syscall/dir_plan9.go                           |     2 +-
 src/syscall/dirent.go                              |   102 +
 src/syscall/dll_windows.go                         |     1 -
 src/syscall/endian_big.go                          |     9 +
 src/syscall/endian_little.go                       |     9 +
 src/syscall/env_windows.go                         |     2 +-
 src/syscall/exec_linux.go                          |     6 +-
 src/syscall/exec_linux_test.go                     |    10 +-
 src/syscall/exec_plan9.go                          |     5 -
 src/syscall/exec_unix.go                           |     2 +-
 src/syscall/exec_windows.go                        |     2 -
 src/syscall/flock_linux_32bit.go                   |     2 +-
 src/syscall/mkall.sh                               |     2 +-
 src/syscall/mksyscall_windows.go                   |    31 +-
 src/syscall/mksysnum_linux.pl                      |    14 +-
 src/syscall/net_nacl.go                            |    44 +-
 src/syscall/netlink_linux.go                       |     5 +-
 src/syscall/setuidgid_32_linux.go                  |    13 +
 src/syscall/setuidgid_linux.go                     |    13 +
 src/syscall/sockcmsg_linux.go                      |     3 +
 src/syscall/sockcmsg_unix.go                       |     7 +-
 src/syscall/syscall.go                             |     6 +
 src/syscall/syscall_darwin.go                      |    36 +-
 src/syscall/syscall_darwin_386.go                  |    19 +-
 src/syscall/syscall_darwin_amd64.go                |    19 +-
 src/syscall/syscall_darwin_arm.go                  |    19 +-
 src/syscall/syscall_darwin_arm64.go                |    19 +-
 src/syscall/syscall_darwin_test.go                 |    23 -
 src/syscall/syscall_dragonfly.go                   |    35 +-
 src/syscall/syscall_dragonfly_amd64.go             |    19 +-
 src/syscall/syscall_freebsd.go                     |    36 +-
 src/syscall/syscall_freebsd_386.go                 |    19 +-
 src/syscall/syscall_freebsd_amd64.go               |    19 +-
 src/syscall/syscall_freebsd_arm.go                 |    19 +-
 src/syscall/syscall_linux.go                       |    42 +-
 src/syscall/syscall_linux_386.go                   |    24 +-
 src/syscall/syscall_linux_amd64.go                 |    24 +-
 src/syscall/syscall_linux_arm.go                   |    22 +-
 src/syscall/syscall_linux_arm64.go                 |    24 +-
 src/syscall/syscall_linux_mips64x.go               |    22 +-
 src/syscall/syscall_linux_mipsx.go                 |   222 +
 src/syscall/syscall_linux_ppc64x.go                |    24 +-
 src/syscall/syscall_linux_s390x.go                 |    24 +-
 src/syscall/syscall_linux_test.go                  |    28 +
 src/syscall/syscall_nacl.go                        |    40 +-
 src/syscall/syscall_nacl_386.go                    |    17 +-
 src/syscall/syscall_nacl_amd64p32.go               |    17 +-
 src/syscall/syscall_nacl_arm.go                    |    17 +-
 src/syscall/syscall_netbsd.go                      |    38 +-
 src/syscall/syscall_netbsd_386.go                  |    19 +-
 src/syscall/syscall_netbsd_amd64.go                |    19 +-
 src/syscall/syscall_netbsd_arm.go                  |    19 +-
 src/syscall/syscall_openbsd.go                     |    38 +-
 src/syscall/syscall_openbsd_386.go                 |    19 +-
 src/syscall/syscall_openbsd_amd64.go               |    19 +-
 src/syscall/syscall_openbsd_arm.go                 |    19 +-
 src/syscall/syscall_plan9.go                       |     2 -
 src/syscall/syscall_solaris.go                     |    84 +-
 src/syscall/syscall_solaris_amd64.go               |    19 +-
 src/syscall/syscall_test.go                        |    14 +
 src/syscall/syscall_unix.go                        |     1 +
 src/syscall/syscall_unix_test.go                   |     9 -
 src/syscall/syscall_windows.go                     |    28 +-
 src/syscall/timestruct.go                          |    40 +
 src/syscall/types_linux.go                         |     2 +-
 src/syscall/zerrors_linux_mips.go                  |  1834 ++
 src/syscall/zerrors_linux_mipsle.go                |  1834 ++
 src/syscall/zsyscall_linux_386.go                  |     4 +-
 src/syscall/zsyscall_linux_amd64.go                |     4 +-
 src/syscall/zsyscall_linux_arm.go                  |     4 +-
 src/syscall/zsyscall_linux_arm64.go                |     4 +-
 src/syscall/zsyscall_linux_mips.go                 |  1759 ++
 src/syscall/zsyscall_linux_mips64.go               |     4 +-
 src/syscall/zsyscall_linux_mips64le.go             |     4 +-
 src/syscall/zsyscall_linux_mipsle.go               |  1759 ++
 src/syscall/zsyscall_linux_ppc64.go                |     4 +-
 src/syscall/zsyscall_linux_ppc64le.go              |     4 +-
 src/syscall/zsyscall_linux_s390x.go                |     4 +-
 src/syscall/zsyscall_solaris_amd64.go              |    86 +-
 src/syscall/zsyscall_windows.go                    |   259 +-
 src/syscall/zsysnum_linux_mips.go                  |   357 +
 src/syscall/zsysnum_linux_mipsle.go                |   357 +
 src/syscall/ztypes_linux_386.go                    |     7 +-
 src/syscall/ztypes_linux_amd64.go                  |     7 +-
 src/syscall/ztypes_linux_arm.go                    |     7 +-
 src/syscall/ztypes_linux_arm64.go                  |     7 +-
 src/syscall/ztypes_linux_mips.go                   |   592 +
 src/syscall/ztypes_linux_mips64.go                 |     7 +-
 src/syscall/ztypes_linux_mips64le.go               |     7 +-
 src/syscall/ztypes_linux_mipsle.go                 |   592 +
 src/syscall/ztypes_linux_ppc64.go                  |     7 +-
 src/syscall/ztypes_linux_ppc64le.go                |     7 +-
 src/syscall/ztypes_windows.go                      |     2 +
 src/testing/benchmark.go                           |    26 +-
 src/testing/example.go                             |    10 +-
 src/testing/internal/testdeps/deps.go              |    51 +
 src/testing/quick/quick.go                         |     2 +
 src/testing/sub_test.go                            |    51 +-
 src/testing/testing.go                             |   249 +-
 src/testing/testing_test.go                        |    38 +-
 src/text/tabwriter/tabwriter.go                    |     1 +
 src/text/template/exec.go                          |    47 +-
 src/text/template/exec_test.go                     |   106 +-
 src/text/template/funcs.go                         |    80 +-
 src/text/template/multi_test.go                    |    36 +
 src/text/template/parse/lex.go                     |    77 +-
 src/text/template/parse/lex_test.go                |   259 +-
 src/text/template/parse/parse.go                   |    25 +-
 src/text/template/parse/parse_test.go              |    34 +
 src/text/template/template.go                      |    15 +-
 src/time/example_test.go                           |    18 +-
 src/time/export_android_test.go                    |    12 +
 src/time/format.go                                 |    57 +-
 src/time/format_test.go                            |     3 +
 src/time/sleep.go                                  |    28 +-
 src/time/time.go                                   |    77 +-
 src/time/time_test.go                              |   146 +-
 src/time/zoneinfo.go                               |     2 +
 src/time/zoneinfo_abbrs_windows.go                 |   183 +-
 src/time/zoneinfo_android.go                       |   119 +
 src/time/zoneinfo_android_test.go                  |    18 +
 src/time/zoneinfo_unix.go                          |     2 +-
 src/time/zoneinfo_windows.go                       |     2 -
 src/unicode/letter.go                              |     7 +
 src/unicode/letter_test.go                         |     4 +
 src/unicode/utf8/utf8.go                           |    15 +-
 src/unicode/utf8/utf8_test.go                      |    91 +
 src/unsafe/unsafe.go                               |    10 +-
 .../x/crypto/chacha20poly1305/chacha20poly1305.go  |    83 +
 .../chacha20poly1305/chacha20poly1305_amd64.go     |    80 +
 .../chacha20poly1305/chacha20poly1305_amd64.s      |  2707 +++
 .../chacha20poly1305/chacha20poly1305_generic.go   |    70 +
 .../chacha20poly1305/chacha20poly1305_noasm.go     |    15 +
 .../chacha20poly1305/chacha20poly1305_test.go      |   182 +
 .../chacha20poly1305_test_vectors.go               |   332 +
 .../internal/chacha20/chacha_generic.go            |   199 +
 .../internal/chacha20/chacha_test.go               |    29 +
 .../golang_org/x/crypto/curve25519/const_amd64.s   |    20 +
 .../golang_org/x/crypto/curve25519/cswap_amd64.s   |    88 +
 .../golang_org/x/crypto/curve25519/curve25519.go   |   841 +
 .../x/crypto/curve25519/curve25519_test.go         |    29 +
 src/vendor/golang_org/x/crypto/curve25519/doc.go   |    23 +
 .../golang_org/x/crypto/curve25519/freeze_amd64.s  |    71 +
 .../x/crypto/curve25519/ladderstep_amd64.s         |  1375 ++
 .../x/crypto/curve25519/mont25519_amd64.go         |   240 +
 .../golang_org/x/crypto/curve25519/mul_amd64.s     |   167 +
 .../golang_org/x/crypto/curve25519/square_amd64.s  |   130 +
 .../golang_org/x/crypto/poly1305/poly1305.go       |    32 +
 .../golang_org/x/crypto/poly1305/poly1305_test.go  |    92 +
 .../golang_org/x/crypto/poly1305/sum_amd64.go      |    22 +
 .../golang_org/x/crypto/poly1305/sum_amd64.s       |   125 +
 src/vendor/golang_org/x/crypto/poly1305/sum_arm.go |    22 +
 src/vendor/golang_org/x/crypto/poly1305/sum_arm.s  |   427 +
 src/vendor/golang_org/x/crypto/poly1305/sum_ref.go |  1531 ++
 src/vendor/golang_org/x/net/idna/idna.go           |    68 +
 src/vendor/golang_org/x/net/idna/idna_test.go      |    43 +
 src/vendor/golang_org/x/net/idna/punycode.go       |   200 +
 src/vendor/golang_org/x/net/idna/punycode_test.go  |   198 +
 src/vendor/golang_org/x/net/lex/httplex/httplex.go |    39 +
 .../golang_org/x/net/lex/httplex/httplex_test.go   |    18 +
 src/vendor/golang_org/x/net/lif/address.go         |   105 +
 src/vendor/golang_org/x/net/lif/address_test.go    |   121 +
 src/vendor/golang_org/x/net/lif/binary.go          |    68 +
 src/vendor/golang_org/x/net/lif/defs_solaris.go    |    90 +
 src/vendor/golang_org/x/net/lif/lif.go             |    43 +
 src/vendor/golang_org/x/net/lif/link.go            |   122 +
 src/vendor/golang_org/x/net/lif/link_test.go       |    61 +
 .../golang_org/x/net/lif/sys_solaris_amd64.s       |    11 +
 src/vendor/golang_org/x/net/lif/syscall.go         |    33 +
 .../golang_org/x/net/lif/zsys_solaris_amd64.go     |   103 +
 src/vendor/golang_org/x/net/route/address.go       |    18 +-
 .../golang_org/x/net/route/interface_freebsd.go    |    12 +-
 .../golang_org/x/net/route/interface_openbsd.go    |     9 +-
 src/vendor/golang_org/x/net/route/message.go       |     6 +
 src/vendor/golang_org/x/net/route/message_test.go  |    23 +
 src/vendor/golang_org/x/net/route/route_openbsd.go |     6 +-
 src/vendor/golang_org/x/net/route/route_test.go    |    35 +-
 .../golang_org/x/text/transform/transform.go       |   705 +
 .../golang_org/x/text/unicode/norm/composition.go  |   514 +
 .../golang_org/x/text/unicode/norm/forminfo.go     |   256 +
 src/vendor/golang_org/x/text/unicode/norm/input.go |   105 +
 src/vendor/golang_org/x/text/unicode/norm/iter.go  |   450 +
 .../golang_org/x/text/unicode/norm/normalize.go    |   608 +
 .../golang_org/x/text/unicode/norm/readwriter.go   |   125 +
 .../golang_org/x/text/unicode/norm/tables.go       |  7627 ++++++
 .../golang_org/x/text/unicode/norm/transform.go    |    88 +
 src/vendor/golang_org/x/text/unicode/norm/trie.go  |    54 +
 .../golang_org/x/text/unicode/norm/triegen.go      |   117 +
 src/vendor/golang_org/x/text/width/kind_string.go  |    16 +
 src/vendor/golang_org/x/text/width/tables.go       |  1284 +
 src/vendor/golang_org/x/text/width/transform.go    |   239 +
 src/vendor/golang_org/x/text/width/trieval.go      |    30 +
 src/vendor/golang_org/x/text/width/width.go        |   206 +
 test/bugs/bug395.go                                |    24 -
 test/bugs/placeholder                              |     2 -
 test/checkbce.go                                   |     4 +-
 test/const.go                                      |    35 +
 test/convert2.go                                   |   315 +
 test/ddd1.go                                       |     5 +-
 test/escape_because.go                             |    48 +-
 test/escape_iface.go                               |    18 +-
 test/fixedbugs/bug255.go                           |     2 +-
 test/fixedbugs/bug332.go                           |     4 +-
 test/fixedbugs/bug376.go                           |     3 +-
 test/fixedbugs/bug498.go                           |    23 +
 test/fixedbugs/bug499.go                           |    15 +
 test/fixedbugs/issue10607.go                       |     2 +-
 test/fixedbugs/issue11370.go                       |    13 +
 test/fixedbugs/issue11610.go                       |     2 +-
 test/fixedbugs/issue13162.go                       |    82 +
 test/fixedbugs/issue13171.go                       |     2 +-
 test/fixedbugs/issue13262.go                       |    21 +
 test/fixedbugs/issue13485.go                       |    18 +
 test/fixedbugs/issue14136.go                       |     2 +-
 test/fixedbugs/issue15141.go                       |    33 +
 test/fixedbugs/issue15277.go                       |     2 +
 test/fixedbugs/issue15303.go                       |    24 +
 test/fixedbugs/issue15514.dir/a.go                 |     7 +
 test/fixedbugs/issue15514.dir/b.go                 |     7 +
 test/fixedbugs/issue15514.dir/c.go                 |    10 +
 test/fixedbugs/issue15514.go                       |     7 +
 test/fixedbugs/issue15528.go                       |   131 +
 test/fixedbugs/issue15609.dir/call.go              |     7 +
 test/fixedbugs/issue15609.dir/call_386.s           |     8 +
 test/fixedbugs/issue15609.dir/call_amd64.s         |     8 +
 test/fixedbugs/issue15609.dir/call_decl.go         |     5 +
 test/fixedbugs/issue15609.dir/main.go              |    14 +
 test/fixedbugs/issue15722.go                       |    21 +
 test/fixedbugs/issue15747.go                       |    11 +-
 test/fixedbugs/issue15895.go                       |    27 +
 test/fixedbugs/issue16306.go                       |    15 +
 test/fixedbugs/issue16317.dir/a.go                 |    11 +
 test/fixedbugs/issue16317.dir/b.go                 |    11 +
 test/fixedbugs/issue16317.go                       |    10 +
 test/fixedbugs/issue16331.go                       |    48 +
 test/fixedbugs/issue16369.go                       |    13 +
 test/fixedbugs/issue16428.go                       |    12 +
 test/fixedbugs/issue16439.go                       |    18 +
 test/fixedbugs/issue16616.dir/a.go                 |     7 +
 test/fixedbugs/issue16616.dir/b.go                 |    14 +
 test/fixedbugs/issue16616.dir/issue16616.go        |    26 +
 test/fixedbugs/issue16616.go                       |     9 +
 test/fixedbugs/issue16733.go                       |    16 +
 test/fixedbugs/issue16741.go                       |    17 +
 test/fixedbugs/issue16760.go                       |    42 +
 test/fixedbugs/issue16804.go                       |    16 +
 test/fixedbugs/issue16870.go                       |   140 +
 test/fixedbugs/issue16948.go                       |    34 +
 test/fixedbugs/issue16949.go                       |    30 +
 test/fixedbugs/issue16985.go                       |    37 +
 test/fixedbugs/issue17005.go                       |    46 +
 test/fixedbugs/issue17038.go                       |     9 +
 test/fixedbugs/issue17039.go                       |    17 +
 test/fixedbugs/issue17111.go                       |    16 +
 test/fixedbugs/issue17194.go                       |    17 +
 test/fixedbugs/issue17270.go                       |    11 +
 test/fixedbugs/issue17381.go                       |    54 +
 test/fixedbugs/issue17449.go                       |    34 +
 test/fixedbugs/issue17551.go                       |    21 +
 test/fixedbugs/issue17588.go                       |    20 +
 test/fixedbugs/issue17596.go                       |    19 +
 test/fixedbugs/issue17631.go                       |    22 +
 test/fixedbugs/issue17640.go                       |    28 +
 test/fixedbugs/issue17645.go                       |    17 +
 test/fixedbugs/issue17710.go                       |    13 +
 test/fixedbugs/issue17752.go                       |    20 +
 test/fixedbugs/issue17918.go                       |    41 +
 test/fixedbugs/issue18092.go                       |    15 +
 test/fixedbugs/issue4085b.go                       |    24 +-
 test/fixedbugs/issue4215.go                        |    53 +
 test/fixedbugs/issue6750.go                        |    22 +
 test/fixedbugs/issue8613.go                        |     1 -
 test/fixedbugs/issue9608.dir/issue9608.go          |     9 +
 test/float_lit2.go                                 |     4 +-
 test/inline_variadic.go                            |    19 +
 test/interface/assertinline.go                     |    41 +-
 test/intrinsic.dir/main.go                         |    15 -
 test/intrinsic.go                                  |     2 +-
 test/intrinsic_atomic.go                           |    20 +
 test/live.go                                       |   234 +-
 test/live2.go                                      |    11 +-
 test/live_ssa.go                                   |   648 -
 test/live_syscall.go                               |     4 +-
 test/method2.go                                    |     4 +-
 test/nilptr3.go                                    |   104 +-
 test/nilptr3_ssa.go                                |   230 -
 test/nosplit.go                                    |     4 +-
 test/notinheap.go                                  |    55 +
 test/notinheap2.go                                 |    43 +
 test/nowritebarrier.go                             |    78 +
 test/nul1.go                                       |     7 +-
 test/phiopt.go                                     |     2 +-
 test/prove.go                                      |     4 +-
 test/range.go                                      |    25 +
 test/run.go                                        |    68 +-
 test/sliceopt.go                                   |    77 +-
 test/switch2.go                                    |     4 +-
 test/switch5.go                                    |    22 +-
 test/switch6.go                                    |     8 +-
 test/syntax/chan1.go                               |     4 +-
 test/syntax/semi4.go                               |     9 +-
 test/uintptrescapes2.go                            |    10 +-
 test/writebarrier.go                               |    17 +-
 2133 files changed, 312937 insertions(+), 121883 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 9494ba0..cb487d5 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -27,6 +27,7 @@ Ainar Garipov <gugl.zadolbal at gmail.com>
 Akihiro Suda <suda.kyoto at gmail.com>
 Akshat Kumar <seed at mail.nanosouffle.net>
 Alan Shreve <alan at inconshreveable.com>
+Albert Nigmatzianov <albertnigma at gmail.com>
 Albert Strasheim <fullung at gmail.com>
 Alberto Bertogli <albertito at blitiri.com.ar>
 Alberto Donizetti <alb.donizetti at gmail.com>
@@ -35,11 +36,14 @@ Aleksandar Dezelin <dezelin at gmail.com>
 Alessandro Arzilli <alessandro.arzilli at gmail.com>
 Alex A Skinner <alex at lx.lc>
 Alex Brainman <alex.brainman at gmail.com>
+Alex Browne <stephenalexbrowne at gmail.com>
+Alex Carol <alex.carol.c at gmail.com>
 Alex Jin <toalexjin at gmail.com>
 Alex Plugaru <alex at plugaru.org> <alexandru.plugaru at gmail.com>
 Alex Schroeder <alex at gnu.org>
 Alex Sergeyev <abc at alexsergeyev.com>
 Alexander Demakin <alexander.demakin at gmail.com>
+Alexander Döring <email at alexd.ch>
 Alexander Larsson <alexander.larsson at gmail.com>
 Alexander Morozov <lk4d4math at gmail.com>
 Alexander Neumann <alexander at bumpern.de>
@@ -55,10 +59,14 @@ Alexey Borzenkov <snaury at gmail.com>
 Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
 Aliaksandr Valialkin <valyala at gmail.com>
 Alif Rachmawadi <subosito at gmail.com>
+Allan Simon <allan.simon at supinfo.com>
+Alok Menghrajani <alok.menghrajani at gmail.com>
 Amazon.com, Inc
 Amir Mohammad Saied <amir at gluegadget.com>
 Amrut Joshi <amrut.joshi at gmail.com>
 Andre Nathan <andrenth at gmail.com>
+Andreas Auernhammer <aead at mail.de>
+Andreas Litt <andreas.litt at gmail.com>
 Andrei Korzhevskii <a.korzhevskiy at gmail.com>
 Andrei Vieru <euvieru at gmail.com>
 Andrew Balholm <andybalholm at gmail.com>
@@ -68,6 +76,7 @@ Andrew Ekstedt <andrew.ekstedt at gmail.com>
 Andrew Etter <andrew.etter at gmail.com>
 Andrew Harding <andrew at spacemonkey.com>
 Andrew Lutomirski <andy at luto.us>
+Andrew Pogrebnoy <absourd.noise at gmail.com>
 Andrew Pritchard <awpritchard at gmail.com>
 Andrew Radev <andrey.radev at gmail.com>
 Andrew Skiba <skibaa at gmail.com>
@@ -100,6 +109,7 @@ Arnout Engelen <arnout at bzzt.net>
 Aron Nopanen <aron.nopanen at gmail.com>
 Artyom Pervukhin <artyom.pervukhin at gmail.com>
 Arvindh Rajesh Tamilmani <art at a-30.net>
+Atin Malaviya <amalaviy at akamai.com>
 Ato Araki <ato.araki at gmail.com>
 Audrey Lim <audreylh at gmail.com>
 Augusto Roman <aroman at gmail.com>
@@ -118,7 +128,9 @@ Bjorn Tillenius <bjorn at tillenius.me>
 Bjorn Tipling <bjorn.tipling at gmail.com>
 Blake Gentry <blakesgentry at gmail.com>
 Blake Mizerany <blake.mizerany at gmail.com>
+Blixt <me at blixt.nyc>
 Bobby Powers <bobbypowers at gmail.com>
+Bolt
 Brady Catherman <brady at gmail.com>
 Brady Sullivan <brady at bsull.com>
 Brendan Daniel Tracey <tracey.brendan at gmail.com>
@@ -126,12 +138,15 @@ Brett Cannon <bcannon at gmail.com>
 Brian Dellisanti <briandellisanti at gmail.com>
 Brian G. Merrell <bgmerrell at gmail.com>
 Brian Gitonga Marete <marete at toshnix.com> <bgmarete at gmail.com>
+Brian Kennedy <btkennedy at gmail.com>
 Brian Ketelsen <bketelsen at gmail.com>
 Brian Smith <ohohvi at gmail.com>
+Bryan Alexander <Kozical at msn.com>
 Bryan Ford <brynosaurus at gmail.com>
 Caine Tighe <arctanofyourface at gmail.com>
 Caleb Spare <cespare at gmail.com>
 Carl Chatfield <carlchatfield at gmail.com>
+Carl Johnson <me at carlmjohnson.net>
 Carlos Castillo <cookieo9 at gmail.com>
 Carlos Cirello <uldericofilho at gmail.com>
 Case Nelson <case.nelson at gmail.com>
@@ -170,6 +185,7 @@ CoreOS, Inc.
 Corey Thomasson <cthom.lists at gmail.com>
 Cristian Staretu <unclejacksons at gmail.com>
 Currant
+Cyrill Schumacher <cyrill at schumacher.fm>
 Damian Gryski <dgryski at gmail.com>
 Dan Caddigan <goldcaddy77 at gmail.com>
 Dan Callahan <dan.callahan at gmail.com>
@@ -180,6 +196,7 @@ Daniel Johansson <dajo2002 at gmail.com>
 Daniel Kerwin <d.kerwin at gini.net>
 Daniel Krech <eikeon at eikeon.com>
 Daniel Lidén <daniel.liden.87 at gmail.com>
+Daniel Martí <mvdan at mvdan.cc>
 Daniel Morsing <daniel.morsing at gmail.com>
 Daniel Ortiz Pereira da Silva <daniel.particular at gmail.com>
 Daniel Skinner <daniel at dasa.cc>
@@ -199,10 +216,12 @@ David Jakob Fritz <david.jakob.fritz at gmail.com>
 David Leon Gil <coruus at gmail.com>
 David R. Jenni <david.r.jenni at gmail.com>
 David Sansome <me at davidsansome.com>
+David Stainton <dstainton415 at gmail.com>
 David Thomas <davidthomas426 at gmail.com>
 David Titarenco <david.titarenco at gmail.com>
 Davies Liu <davies.liu at gmail.com>
 Dean Prichard <dean.prichard at gmail.com>
+Deepak Jois <deepak.jois at gmail.com>
 Denis Bernard <db047h at gmail.com>
 Denis Brandolini <denis.brandolini at gmail.com>
 Denys Honsiorovskyi <honsiorovskyi at gmail.com>
@@ -211,11 +230,13 @@ Derek Parker <parkerderek86 at gmail.com>
 Derek Shockey <derek.shockey at gmail.com>
 Develer SRL
 Devon H. O'Dell <devon.odell at gmail.com>
+Dhaivat Pandit <dhaivatpandit at gmail.com>
 Dhiru Kholia <dhiru.kholia at gmail.com>
 Didier Spezia <didier.06 at gmail.com>
 Dimitri Tcaciuc <dtcaciuc at gmail.com>
 Dirk Gadsden <dirk at esherido.com>
 Diwaker Gupta <diwakergupta at gmail.com>
+Dmitri Popov <operator at cv.dp-net.com>
 Dmitri Shuralyov <shurcooL at gmail.com>
 Dmitriy Dudkin <dudkin.dmitriy at gmail.com>
 Dmitriy Shelenin <deemok at googlemail.com> <deemok at gmail.com>
@@ -257,10 +278,12 @@ Evan Shaw <chickencha at gmail.com>
 Ewan Chou <coocood at gmail.com>
 Fabian Wickborn <fabian at wickborn.net>
 Fabrizio Milo <mistobaan at gmail.com>
+Faiyaz Ahmed <ahmedf at vmware.com>
 Fan Hongjian <fan.howard at gmail.com>
 Fastly, Inc.
 Fatih Arslan <fatih at arslan.io>
 Fazlul Shahriar <fshahriar at gmail.com>
+Fedor Indutny <fedor at indutny.com>
 Felix Geisendörfer <haimuiba at gmail.com>
 Filippo Valsorda <hi at filippo.io>
 Firmansyah Adiputra <frm.adiputra at gmail.com>
@@ -275,16 +298,20 @@ Fredrik Enestad <fredrik.enestad at soundtrackyourbrand.com>
 Frithjof Schulze <schulze at math.uni-hannover.de> <sfrithjof at gmail.com>
 Frits van Bommel <fvbommel at gmail.com>
 Gabriel Aszalos <gabriel.aszalos at gmail.com>
+Gabriel Russell <gabriel.russell at gmail.com>
+Gareth Paul Jones <gpj at foursquare.com>
 Gary Burd <gary at beagledreams.com>
 Gaurish Sharma <contact at gaurishsharma.com>
 Gautham Thambidorai <gautham.dorai at gmail.com>
 Geert-Johan Riemer <gjr19912 at gmail.com>
+Geoffroy Lorieux <lorieux.g at gmail.com>
 Georg Reinke <guelfey at gmail.com>
 George Shammas <george at shamm.as> <georgyo at gmail.com>
 Gerasimos Dimitriadis <gedimitr at gmail.com>
 Gideon Jan-Wessel Redelinghuys <gjredelinghuys at gmail.com>
 Giles Lean <giles.lean at pobox.com>
 Giulio Iotti <dullgiulio at gmail.com>
+Gleb Stepanov <glebstepanov1992 at gmail.com>
 Google Inc.
 Gordon Klaus <gordon.klaus at gmail.com>
 Graham King <graham4king at gmail.com>
@@ -307,6 +334,7 @@ Hector Chu <hectorchu at gmail.com>
 Hector Martin Cantero <hector at marcansoft.com>
 Henning Schmiedehausen <henning at schmiedehausen.org>
 Henrik Edwards <henrik.edwards at gmail.com>
+Henrik Hodne <henrik at hodne.io>
 Herbert Georg Fischer <herbert.fischer at gmail.com>
 Hironao OTSUBO <motemen at gmail.com>
 Hiroshi Ioka <hirochachacha at gmail.com>
@@ -327,12 +355,14 @@ Ingo Oeser <nightlyone at googlemail.com>
 Intel Corporation
 Irieda Noboru <irieda at gmail.com>
 Isaac Wagner <ibw at isaacwagner.me>
+Ivan Babrou <ivan at cloudflare.com>
 Ivan Ukhov <ivan.ukhov at gmail.com>
 Jacob Hoffman-Andrews <github at hoffman-andrews.com>
 Jae Kwon <jae at tendermint.com>
 Jakob Borg <jakob at nym.se>
 Jakub Ryszard Czarnowicz <j.czarnowicz at gmail.com>
 James Bardin <j.bardin at gmail.com>
+James Clarke <jrtc27 at jrtc27.com>
 James David Chalfant <james.chalfant at gmail.com>
 James Fysh <james.fysh at gmail.com>
 James Gray <james at james4k.com>
@@ -342,6 +372,7 @@ James Schofield <james at shoeboxapp.com>
 James Sweet <james.sweet88 at googlemail.com>
 James Toy <nil at opensesame.st>
 James Whitehead <jnwhiteh at gmail.com>
+Jamie Beverly <jamie.r.beverly at gmail.com>
 Jamil Djadala <djadala at gmail.com>
 Jan H. Hosang <jan.hosang at gmail.com>
 Jan Mercl <0xjnml at gmail.com>
@@ -352,14 +383,17 @@ Jani Monoses <jani.monoses at ubuntu.com>
 Jaroslavas Počepko <jp at webmaster.ms>
 Jason Barnett <jason.w.barnett at gmail.com>
 Jason Del Ponte <delpontej at gmail.com>
+Jason Smale <jsmale at zendesk.com>
 Jason Travis <infomaniac7 at gmail.com>
 Jay Weisskopf <jay at jayschwa.net>
+Jean-Nicolas Moal <jn.moal at gmail.com>
 Jeff Hodges <jeff at somethingsimilar.com>
 Jeff R. Allen <jra at nella.org>
 Jeff Sickel <jas at corpus-callosum.com>
 Jeff Wendling <jeff at spacemonkey.com>
 Jens Frederich <jfrederich at gmail.com>
 Jeremy Jackins <jeremyjackins at gmail.com>
+Jeroen Bobbeldijk <jerbob92 at gmail.com>
 Jess Frazelle <me at jessfraz.com>
 Jihyun Yu <yjh0502 at gmail.com>
 Jim McGrath <jimmc2 at gmail.com>
@@ -367,6 +401,7 @@ Jimmy Zelinskie <jimmyzelinskie at gmail.com>
 Jingcheng Zhang <diogin at gmail.com>
 Jingguo Yao <yaojingguo at gmail.com>
 Jiong Du <londevil at gmail.com>
+Jirka Daněk <dnk at mail.muni.cz>
 Joakim Sernbrant <serbaut at gmail.com>
 Joe Farrell <joe2farrell at gmail.com>
 Joe Harrison <joehazzers at gmail.com>
@@ -393,9 +428,11 @@ Jonathan Mark <jhmark at xenops.com>
 Jonathan Rudenberg <jonathan at titanous.com>
 Jonathan Wills <runningwild at gmail.com>
 Jongmin Kim <atomaths at gmail.com>
+Joonas Kuorilehto <joneskoo at derbian.fi>
 Jose Luis Vázquez González <josvazg at gmail.com>
 Joseph Holsten <joseph at josephholsten.com>
 Josh Bleecher Snyder <josharian at gmail.com>
+Josh Chorlton <jchorlton at gmail.com>
 Josh Goebel <dreamer3 at gmail.com>
 Josh Holland <jrh at joshh.co.uk>
 Joshua Chase <jcjoshuachase at gmail.com>
@@ -406,7 +443,9 @@ Julian Kornberger <jk+github at digineo.de>
 Julian Phillips <julian at quantumfyre.co.uk>
 Julien Schmidt <google at julienschmidt.com>
 Justin Nuß <nuss.justin at gmail.com>
+Justyn Temme <justyntemme at gmail.com>
 Kai Backman <kaib at golang.org>
+Kale Blankenship <kale at lemnisys.com>
 Kamil Kisiel <kamil at kamilkisiel.net> <kamil.kisiel at gmail.com>
 Kang Hu <hukangustc at gmail.com>
 Kato Kazuyoshi <kato.kazuyoshi at gmail.com>
@@ -437,6 +476,7 @@ Kyle Lemons <kyle at kylelemons.net>
 L Campbell <unpantsu at gmail.com>
 Lai Jiangshan <eag0628 at gmail.com>
 Larz Conwell <larzconwell at gmail.com>
+LE Manh Cuong <cuong.manhle.vn at gmail.com>
 Lee Hinman <hinman at gmail.com>
 Lee Packham <lpackham at gmail.com>
 Lewin Bormann <lewin.bormann at gmail.com>
@@ -448,10 +488,12 @@ Luan Santos <cfcluan at gmail.com>
 Luca Greco <luca.greco at alcacoop.it>
 Lucien Stuker <lucien.stuker at gmail.com>
 Lucio De Re <lucio.dere at gmail.com>
+Luigi Riefolo <luigi.riefolo at gmail.com>
 Luit van Drongelen <luitvd at gmail.com>
 Luka Zakrajšek <tr00.g33k at gmail.com>
 Luke Curley <qpingu at gmail.com>
 Mal Curtis <mal at mal.co.nz>
+Manfred Touron <m at 42.am>
 Manu S Ajith <neo at codingarena.in>
 Manuel Mendez <mmendez534 at gmail.com>
 Marc Weistroff <marc at weistroff.net>
@@ -465,7 +507,9 @@ Markover Inc. DBA Poptip
 Markus Duft <markus.duft at salomon.at>
 Markus Sonderegger <marraison at gmail.com>
 Markus Zimmermann <zimmski at gmail.com>
+Martin Bertschler <mbertschler at gmail.com>
 Martin Garton <garton at gmail.com>
+Martin Hamrle <martin.hamrle at gmail.com>
 Martin Möhrmann <martisch at uos.de>
 Martin Neubauer <m.ne at gmx.net>
 Martin Olsson <martin at minimum.se>
@@ -487,10 +531,13 @@ Matt T. Proud <matt.proud at gmail.com>
 Matt Williams <gh at mattyw.net>
 Matthew Brennan <matty.brennan at gmail.com>
 Matthew Cottingham <mattcottingham at gmail.com>
+Matthew Denton <mdenton at skyportsystems.com>
 Matthew Holt <Matthew.Holt+git at gmail.com>
 Matthew Horsnell <matthew.horsnell at gmail.com>
+Matthieu Hauglustaine <matt.hauglustaine at gmail.com>
 Maxim Khitrov <max at mxcrypt.com>
 Maxwell Krohn <themax at gmail.com>
+MediaMath, Inc
 Meir Fischer <meirfischer at gmail.com>
 Meng Zhuo <mengzhuo1203 at gmail.com>
 Meteor Development Group
@@ -517,6 +564,8 @@ Miguel Mendez <stxmendez at gmail.com>
 Mihai Borobocea <MihaiBorobocea at gmail.com>
 Mikael Tillenius <mikti42 at gmail.com>
 Mike Andrews <mra at xoba.com>
+Mike Appleby <mike at app.leby.org>
+Mike Houston <mike at kothar.net>
 Mike Rosset <mike.rosset at gmail.com>
 Mikhail Gusarov <dottedmag at dottedmag.net>
 Mikhail Panchenko <m at mihasya.com>
@@ -524,7 +573,9 @@ Miki Tebeka <miki.tebeka at gmail.com>
 Mikio Hara <mikioh.mikioh at gmail.com>
 Mikkel Krautz <mikkel at krautz.dk>
 Miquel Sabaté Solà <mikisabate at gmail.com>
+Miroslav Genov <mgenov at gmail.com>
 Mohit Agarwal <mohit at sdf.org>
+Momchil Velikov <momchil.velikov at gmail.com>
 Monty Taylor <mordred at inaugust.com>
 Moov Corporation
 Moriyoshi Koizumi <mozo at mozo.jp>
@@ -559,6 +610,7 @@ Niko Dziemba <niko at dziemba.com>
 Nikolay Turpitko <nikolay at turpitko.com>
 Noah Campbell <noahcampbell at gmail.com>
 Norberto Lopes <nlopes.ml at gmail.com>
+Oleg Vakheta <helginet at gmail.com>
 Oleku Konko <oleku.konko at gmail.com>
 Oling Cat <olingcat at gmail.com>
 Oliver Hookins <ohookins at gmail.com>
@@ -573,6 +625,7 @@ Padraig Kitterick <padraigkitterick at gmail.com>
 Palm Stone Games
 Paolo Giarrusso <p.giarrusso at gmail.com>
 Paolo Martini <mrtnpaolo at gmail.com>
+Parker Moore <parkrmoore at gmail.com>
 Pascal S. de Kloe <pascal at quies.net>
 Patrick Crosby <patrick at stathat.com>
 Patrick Gavlin <pgavlin at gmail.com>
@@ -619,6 +672,7 @@ Quan Yong Zhai <qyzhai at gmail.com>
 Quentin Perez <qperez at ocs.online.net>
 Quoc-Viet Nguyen <afelion at gmail.com>
 RackTop Systems Inc.
+Radu Berinde <radu at cockroachlabs.com>
 Raif S. Naffah <go at naffah-raif.name>
 Rajat Goel <rajat.goel2010 at gmail.com>
 Ralph Corderoy <ralph at inputplus.co.uk>
@@ -629,6 +683,7 @@ Ricardo Padilha <ricardospadilha at gmail.com>
 Richard Barnes <rlb at ipv.sx>
 Richard Crowley <r at rcrowley.org>
 Richard Eric Gavaletz <gavaletz at gmail.com>
+Richard Gibson <richard.gibson at gmail.com>
 Richard Miller <miller.research at gmail.com>
 Richard Musiol <mail at richard-musiol.de>
 Rick Arnold <rickarnoldjr at gmail.com>
@@ -659,12 +714,14 @@ S.Çağlar Onur <caglar at 10ur.org>
 Salmān Aljammāz <s at 0x65.net>
 Sam Hug <samuel.b.hug at gmail.com>
 Sam Whited <sam at samwhited.com>
+Samuele Pedroni <pedronis at lucediurna.net>
 Sanjay Menakuru <balasanjay at gmail.com>
 Sasha Sobol <sasha at scaledinference.com>
 Scott Barron <scott.barron at github.com>
 Scott Bell <scott at sctsm.com>
 Scott Ferguson <scottwferg at gmail.com>
 Scott Lawrence <bytbox at gmail.com>
+Sean Rees <sean at erifax.org>
 Sebastien Binet <seb.binet at gmail.com>
 Sébastien Paolacci <sebastien.paolacci at gmail.com>
 Sergei Skorobogatov <skorobo at rambler.ru>
@@ -681,9 +738,12 @@ Shinji Tanaka <shinji.tanaka at gmail.com>
 Shivakumar GN <shivakumar.gn at gmail.com>
 Silvan Jegen <s.jegen at gmail.com>
 Simon Jefford <simon.jefford at gmail.com>
+Simon Rawet <simon at rawet.se>
 Simon Thulbourn <simon+github at thulbourn.com>
 Simon Whitehead <chemnova at gmail.com>
+Sina Siadat <siadat at gmail.com>
 Sokolov Yura <funny.falcon at gmail.com>
+Song Gao <song at gao.io>
 Spencer Nelson <s at spenczar.com>
 Spring Mc <heresy.mc at gmail.com>
 Square, Inc.
@@ -700,7 +760,9 @@ Steve Streeting <steve at stevestreeting.com>
 Steven Elliot Harris <seharris at gmail.com>
 Steven Hartland <steven.hartland at multiplay.co.uk>
 Stripe, Inc.
+Suyash <dextrous93 at gmail.com>
 Sven Almgren <sven at tras.se>
+Syohei YOSHIDA <syohex at gmail.com>
 Szabolcs Nagy <nsz at port70.net>
 Tad Glines <tad.glines at gmail.com>
 Taj Khattra <taj.khattra at gmail.com>
@@ -710,15 +772,18 @@ Tamir Duberstein <tamird at gmail.com>
 Tarmigan Casebolt <tarmigan at gmail.com>
 Taru Karttunen <taruti at taruti.net>
 Tatsuhiro Tsujikawa <tatsuhiro.t at gmail.com>
+Terrel Shumway <gopher at shumway.us>
 Tetsuo Kiso <tetsuokiso9 at gmail.com>
 Thiago Fransosi Farina <thiago.farina at gmail.com>
 Thomas Alan Copeland <talan.copeland at gmail.com>
+Thomas de Zeeuw <thomasdezeeuw at gmail.com>
 Thomas Desrosiers <thomasdesr at gmail.com>
 Thomas Kappler <tkappler at gmail.com>
 Thorben Krueger <thorben.krueger at gmail.com>
 Tilman Dilo <tilman.dilo at gmail.com>
 Tim Cooijmans <timcooijmans at gmail.com>
 Tim Ebringer <tim.ebringer at gmail.com>
+Tim Henderson <tim.tadh at gmail.com>
 Timo Savola <timo.savola at gmail.com>
 Timo Truyts <alkaloid.btx at gmail.com>
 Timothy Studd <tim at timstudd.com>
@@ -731,8 +796,11 @@ Tor Andersson <tor.andersson at gmail.com>
 Tormod Erevik Lea <tormodlea at gmail.com>
 Totoro W <tw19881113 at gmail.com>
 Travis Cline <travis.cline at gmail.com>
+Trey Lawrence <lawrence.trey at gmail.com>
 Trey Tacon <ttacon at gmail.com>
+Tristan Ooohry <ooohry at gmail.com>
 Tudor Golubenco <tudor.g at gmail.com>
+Tuo Shan <sturbo89 at gmail.com>
 Tyler Bunnell <tylerbunnell at gmail.com>
 Tyler Treat <ttreat31 at gmail.com>
 Ugorji Nwoke <ugorji at gmail.com>
@@ -742,13 +810,18 @@ Upthere, Inc.
 Uriel Mangado <uriel at berlinblue.org>
 Vadim Grek <vadimprog at gmail.com>
 Vadim Vygonets <unixdj at gmail.com>
+Vendasta
 Vincent Ambo <tazjin at googlemail.com>
 Vincent Batts <vbatts at hashbangbash.com> <vbatts at gmail.com>
 Vincent Vanackere <vincent.vanackere at gmail.com>
 Vinu Rajashekhar <vinutheraj at gmail.com>
 Vishvananda Ishaya <vishvananda at gmail.com>
+Vitor De Mario <vitordemario at gmail.com>
+Vladimir Mihailenco <vladimir.webdev at gmail.com>
 Vladimir Nikishenko <vova616 at gmail.com>
+Vladimir Stefanovic <vladimir.stefanovic at imgtec.com>
 Volker Dobler <dr.volker.dobler at gmail.com>
+Weaveworks
 Wei Guangjing <vcc.163 at gmail.com>
 Willem van der Schyff <willemvds at gmail.com>
 William Josephson <wjosephson at gmail.com>
@@ -757,6 +830,7 @@ Wisdom Omuya <deafgoat at gmail.com>
 Xia Bin <snyh at snyh.org>
 Xing Xing <mikespook at gmail.com>
 Xudong Zhang <felixmelon at gmail.com>
+Xuyang Kang <xuyangkang at gmail.com>
 Yahoo Inc.
 Yann Kerhervé <yann.kerherve at gmail.com>
 Yao Zhang <lunaria21 at gmail.com>
@@ -766,11 +840,13 @@ Yesudeep Mangalapilly <yesudeep at google.com>
 Yissakhar Z. Beck <yissakhar.beck at gmail.com>
 Yo-An Lin <yoanlin93 at gmail.com>
 Yongjian Xu <i3dmaster at gmail.com>
+Yorman Arias <cixtords at gmail.com>
 Yoshiyuki Kanno <nekotaroh at gmail.com> <yoshiyuki.kanno at stoic.co.jp>
 Yusuke Kagiwada <block.rxckin.beats at gmail.com>
 Yuusei Kuwana <kuwana at kumama.org>
 Yuval Pavel Zholkover <paulzhol at gmail.com>
 Zemanta d.o.o.
+Zev Goldstein <zev.goldstein at gmail.com>
 Ziad Hatahet <hatahet at gmail.com>
 Zorion Arrizabalaga <zorionk at gmail.com>
 申习之 <bronze1man at gmail.com>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 6763f52..43d1d9a 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -53,6 +53,7 @@ Akihiro Suda <suda.kyoto at gmail.com>
 Akshat Kumar <seed at mail.nanosouffle.net>
 Alan Donovan <adonovan at google.com>
 Alan Shreve <alan at inconshreveable.com>
+Albert Nigmatzianov <albertnigma at gmail.com>
 Albert Strasheim <fullung at gmail.com>
 Alberto Bertogli <albertito at blitiri.com.ar>
 Alberto Donizetti <alb.donizetti at gmail.com>
@@ -62,12 +63,15 @@ Alessandro Arzilli <alessandro.arzilli at gmail.com>
 Alex A Skinner <alex at lx.lc>
 Alex Brainman <alex.brainman at gmail.com>
 Alex Bramley <abramley at google.com>
+Alex Browne <stephenalexbrowne at gmail.com>
+Alex Carol <alex.carol.c at gmail.com>
 Alex Jin <toalexjin at gmail.com>
 Alex Plugaru <alex at plugaru.org> <alexandru.plugaru at gmail.com>
 Alex Schroeder <alex at gnu.org>
 Alex Sergeyev <abc at alexsergeyev.com>
 Alex Vaghin <crhyme at google.com>
 Alexander Demakin <alexander.demakin at gmail.com>
+Alexander Döring <email at alexd.ch>
 Alexander Larsson <alexander.larsson at gmail.com>
 Alexander Morozov <lk4d4math at gmail.com>
 Alexander Neumann <alexander at bumpern.de>
@@ -85,11 +89,15 @@ Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
 Alexis Imperial-Legrand <ail at google.com>
 Aliaksandr Valialkin <valyala at gmail.com>
 Alif Rachmawadi <subosito at gmail.com>
+Allan Simon <allan.simon at supinfo.com>
+Alok Menghrajani <alok.menghrajani at gmail.com>
 Amir Mohammad Saied <amir at gluegadget.com>
 Amrut Joshi <amrut.joshi at gmail.com>
 Andre Nathan <andrenth at gmail.com>
 Andrea Spadaccini <spadaccio at google.com>
+Andreas Auernhammer <aead at mail.de>
 Andreas Jellinghaus <andreas at ionisiert.de> <anj at google.com>
+Andreas Litt <andreas.litt at gmail.com>
 Andrei Korzhevskii <a.korzhevskiy at gmail.com>
 Andrei Vieru <euvieru at gmail.com>
 Andres Erbsen <andreser at google.com>
@@ -102,6 +110,7 @@ Andrew Gerrand <adg at golang.org>
 Andrew Harding <andrew at spacemonkey.com>
 Andrew Lutomirski <andy at luto.us>
 Andrew Pilloud <andrewpilloud at igneoussystems.com>
+Andrew Pogrebnoy <absourd.noise at gmail.com>
 Andrew Pritchard <awpritchard at gmail.com>
 Andrew Radev <andrey.radev at gmail.com>
 Andrew Skiba <skibaa at gmail.com>
@@ -124,6 +133,7 @@ Anthony Canino <anthony.canino1 at gmail.com>
 Anthony Eufemio <anthony.eufemio at gmail.com>
 Anthony Martin <ality at pbrane.org>
 Anthony Starks <ajstarks at gmail.com>
+Antonio Murdaca <runcom at redhat.com>
 Apisak Darakananda <pongad at gmail.com>
 Aram Hăvărneanu <aram at mgk.ro>
 Areski Belaid <areski at gmail.com>
@@ -136,6 +146,7 @@ Aron Nopanen <aron.nopanen at gmail.com>
 Artyom Pervukhin <artyom.pervukhin at gmail.com>
 Arvindh Rajesh Tamilmani <art at a-30.net>
 Asim Shankar <asimshankar at gmail.com>
+Atin Malaviya <amalaviy at akamai.com>
 Ato Araki <ato.araki at gmail.com>
 Audrey Lim <audreylh at gmail.com>
 Augusto Roman <aroman at gmail.com>
@@ -160,13 +171,17 @@ Bill Neubauer <wcn at golang.org> <wcn at google.com> <bill.neubauer at gmail.com>
 Bill O'Farrell <billo at ca.ibm.com>
 Bill Thiede <couchmoney at gmail.com>
 Billie Harold Cleek <bhcleek at gmail.com>
+Billy Lynch <wlynch at google.com>
 Bjorn Tillenius <bjorn at tillenius.me>
 Bjorn Tipling <bjorn.tipling at gmail.com>
 Blake Gentry <blakesgentry at gmail.com>
 Blake Mizerany <blake.mizerany at gmail.com>
+Blixt <me at blixt.nyc>
 Bobby Powers <bobbypowers at gmail.com>
+Boris Nagaev <nagaev at google.com>
 Brad Fitzpatrick <bradfitz at golang.org> <bradfitz at gmail.com>
 Brad Garcia <bgarcia at golang.org>
+Braden Bassingthwaite <bbassingthwaite at vendasta.com>
 Brady Catherman <brady at gmail.com>
 Brady Sullivan <brady at bsull.com>
 Brandon Gilmore <varz at google.com>
@@ -176,9 +191,11 @@ Brett Cannon <bcannon at gmail.com>
 Brian Dellisanti <briandellisanti at gmail.com>
 Brian G. Merrell <bgmerrell at gmail.com>
 Brian Gitonga Marete <marete at toshnix.com> <bgmarete at gmail.com> <bgm at google.com>
+Brian Kennedy <btkennedy at gmail.com>
 Brian Ketelsen <bketelsen at gmail.com>
 Brian Slesinsky <skybrian at google.com>
 Brian Smith <ohohvi at gmail.com>
+Bryan Alexander <Kozical at msn.com>
 Bryan C. Mills <bcmills at google.com>
 Bryan Chan <bryan.chan at ca.ibm.com>
 Bryan Ford <brynosaurus at gmail.com>
@@ -187,10 +204,12 @@ Caio Marcelo de Oliveira Filho <caio.oliveira at intel.com>
 Caleb Spare <cespare at gmail.com>
 Carl Chatfield <carlchatfield at gmail.com>
 Carl Jackson <carl at stripe.com>
+Carl Johnson <me at carlmjohnson.net>
 Carl Mastrangelo <notcarl at google.com>
 Carl Shapiro <cshapiro at google.com> <cshapiro at golang.org>
 Carlos Castillo <cookieo9 at gmail.com>
 Carlos Cirello <uldericofilho at gmail.com>
+Carlos Eduardo Seo <cseo at linux.vnet.ibm.com>
 Cary Hull <chull at google.com>
 Case Nelson <case.nelson at gmail.com>
 Casey Marshall <casey.marshall at gmail.com>
@@ -240,10 +259,12 @@ Corey Thomasson <cthom.lists at gmail.com>
 Cosmos Nicolaou <cnicolaou at google.com>
 Cristian Staretu <unclejacksons at gmail.com>
 Cuihtlauac ALVARADO <cuihtlauac.alvarado at orange.com>
+Cyrill Schumacher <cyrill at schumacher.fm>
 Damian Gryski <dgryski at gmail.com>
 Damien Neil <dneil at google.com>
 Dan Caddigan <goldcaddy77 at gmail.com>
 Dan Callahan <dan.callahan at gmail.com>
+Dan Harrington <harringtond at google.com>
 Dan Jacques <dnj at google.com>
 Dan Peterson <dpiddy at gmail.com>
 Dan Pupius <dan at medium.com>
@@ -253,12 +274,14 @@ Daniel Johansson <dajo2002 at gmail.com>
 Daniel Kerwin <d.kerwin at gini.net>
 Daniel Krech <eikeon at eikeon.com>
 Daniel Lidén <daniel.liden.87 at gmail.com>
+Daniel Martí <mvdan at mvdan.cc>
 Daniel Morsing <daniel.morsing at gmail.com>
 Daniel Nadasi <dnadasi at google.com>
 Daniel Ortiz Pereira da Silva <daniel.particular at gmail.com>
 Daniel Skinner <daniel at dasa.cc>
 Daniel Speichert <daniel at speichert.pl>
 Daniel Theophanes <kardianos at gmail.com>
+Daria Kolistratova <daria.kolistratova at intel.com>
 Darren Elwood <darren at textnode.com>
 Datong Sun <dndx at idndx.com>
 Dave Borowitz <dborowitz at google.com>
@@ -280,30 +303,37 @@ David Forsythe <dforsythe at gmail.com>
 David G. Andersen <dave.andersen at gmail.com>
 David Glasser <glasser at meteor.com>
 David Howden <dhowden at gmail.com>
+David Hubbard <dsp at google.com>
 David Jakob Fritz <david.jakob.fritz at gmail.com>
 David Leon Gil <coruus at gmail.com>
 David McLeish <davemc at google.com>
 David Presotto <presotto at gmail.com>
 David R. Jenni <david.r.jenni at gmail.com>
 David Sansome <me at davidsansome.com>
+David Stainton <dstainton415 at gmail.com>
 David Symonds <dsymonds at golang.org>
 David Thomas <davidthomas426 at gmail.com>
 David Titarenco <david.titarenco at gmail.com>
 Davies Liu <davies.liu at gmail.com>
 Dean Prichard <dean.prichard at gmail.com>
+Deepak Jois <deepak.jois at gmail.com>
 Denis Bernard <db047h at gmail.com>
 Denis Brandolini <denis.brandolini at gmail.com>
+Denis Nagorny <denis.nagorny at intel.com>
 Denys Honsiorovskyi <honsiorovskyi at gmail.com>
 Derek Buitenhuis <derek.buitenhuis at gmail.com>
 Derek Che <drc at yahoo-inc.com>
 Derek Parker <parkerderek86 at gmail.com>
 Derek Shockey <derek.shockey at gmail.com>
 Devon H. O'Dell <devon.odell at gmail.com>
+Dhaivat Pandit <dhaivatpandit at gmail.com>
+Dhananjay Nakrani <dhananjayn at google.com>
 Dhiru Kholia <dhiru.kholia at gmail.com>
 Didier Spezia <didier.06 at gmail.com>
 Dimitri Tcaciuc <dtcaciuc at gmail.com>
 Dirk Gadsden <dirk at esherido.com>
 Diwaker Gupta <diwakergupta at gmail.com>
+Dmitri Popov <operator at cv.dp-net.com>
 Dmitri Shuralyov <shurcooL at gmail.com>
 Dmitriy Dudkin <dudkin.dmitriy at gmail.com>
 Dmitriy Shelenin <deemok at googlemail.com> <deemok at gmail.com>
@@ -343,10 +373,12 @@ Eric Roshan-Eisner <eric.d.eisner at gmail.com>
 Erik Aigner <aigner.erik at gmail.com>
 Erik Dubbelboer <erik at dubbelboer.com>
 Erik St. Martin <alakriti at gmail.com>
+Erik Staab <estaab at google.com>
 Erik Westrup <erik.westrup at gmail.com>
 Ernest Chiang <ernest_chiang at htc.com>
 Esko Luontola <esko.luontola at gmail.com>
 Ethan Burns <eaburns at google.com>
+Ethan Miller <eamiller at us.ibm.com>
 Evan Broder <evan at stripe.com>
 Evan Brown <evanbrown at google.com>
 Evan Kroske <evankroske at google.com>
@@ -356,10 +388,12 @@ Evan Shaw <chickencha at gmail.com>
 Ewan Chou <coocood at gmail.com>
 Fabian Wickborn <fabian at wickborn.net>
 Fabrizio Milo <mistobaan at gmail.com>
+Faiyaz Ahmed <ahmedf at vmware.com>
 Fan Hongjian <fan.howard at gmail.com>
 Fatih Arslan <fatih at arslan.io>
 Fazlul Shahriar <fshahriar at gmail.com>
 Federico Simoncelli <fsimonce at redhat.com>
+Fedor Indutny <fedor at indutny.com>
 Felix Geisendörfer <haimuiba at gmail.com>
 Filippo Valsorda <hi at filippo.io>
 Firmansyah Adiputra <frm.adiputra at gmail.com>
@@ -378,12 +412,15 @@ Frits van Bommel <fvbommel at gmail.com>
 Fumitoshi Ukai <ukai at google.com>
 Gaal Yahas <gaal at google.com>
 Gabriel Aszalos <gabriel.aszalos at gmail.com>
+Gabriel Russell <gabriel.russell at gmail.com>
+Gareth Paul Jones <gpj at foursquare.com>
 Garrick Evans <garrick at google.com>
 Gary Burd <gary at beagledreams.com> <gary.burd at gmail.com>
 Gary Elliott <garyelliott at google.com>
 Gaurish Sharma <contact at gaurishsharma.com>
 Gautham Thambidorai <gautham.dorai at gmail.com>
 Geert-Johan Riemer <gjr19912 at gmail.com>
+Geoffroy Lorieux <lorieux.g at gmail.com>
 Georg Reinke <guelfey at gmail.com>
 George Shammas <george at shamm.as> <georgyo at gmail.com>
 Gerasimos Dimitriadis <gedimitr at gmail.com>
@@ -391,6 +428,7 @@ Gideon Jan-Wessel Redelinghuys <gjredelinghuys at gmail.com>
 Giles Lean <giles.lean at pobox.com>
 Giovanni Bajo <rasky at develer.com>
 Giulio Iotti <dullgiulio at gmail.com>
+Gleb Stepanov <glebstepanov1992 at gmail.com>
 Glenn Brown <glennb at google.com>
 Glenn Lewis <gmlewis at google.com>
 Gordon Klaus <gordon.klaus at gmail.com>
@@ -417,6 +455,7 @@ Hector Chu <hectorchu at gmail.com>
 Hector Martin Cantero <hector at marcansoft.com>
 Henning Schmiedehausen <henning at schmiedehausen.org>
 Henrik Edwards <henrik.edwards at gmail.com>
+Henrik Hodne <henrik at hodne.io>
 Herbert Georg Fischer <herbert.fischer at gmail.com>
 Hironao OTSUBO <motemen at gmail.com>
 Hiroshi Ioka <hirochachacha at gmail.com>
@@ -438,9 +477,11 @@ Ingo Krabbe <ikrabbe.ask at gmail.com>
 Ingo Oeser <nightlyone at googlemail.com> <nightlyone at gmail.com>
 Irieda Noboru <irieda at gmail.com>
 Isaac Wagner <ibw at isaacwagner.me>
+Ivan Babrou <ivan at cloudflare.com>
 Ivan Krasin <krasin at golang.org>
 Ivan Ukhov <ivan.ukhov at gmail.com>
 Jaana Burcu Dogan <jbd at google.com> <jbd at golang.org> <burcujdogan at gmail.com>
+Jack Lindamood <jlindamo at justin.tv>
 Jacob Baskin <jbaskin at google.com>
 Jacob H. Haven <jacob at cloudflare.com>
 Jacob Hoffman-Andrews <github at hoffman-andrews.com>
@@ -451,6 +492,7 @@ Jakub Ryszard Czarnowicz <j.czarnowicz at gmail.com>
 James Aguilar <jaguilar at google.com>
 James Bardin <j.bardin at gmail.com>
 James Chacon <jchacon at google.com>
+James Clarke <jrtc27 at jrtc27.com>
 James David Chalfant <james.chalfant at gmail.com>
 James Fysh <james.fysh at gmail.com>
 James Gray <james at james4k.com>
@@ -462,6 +504,7 @@ James Sweet <james.sweet88 at googlemail.com>
 James Toy <nil at opensesame.st>
 James Tucker <raggi at google.com>
 James Whitehead <jnwhiteh at gmail.com>
+Jamie Beverly <jamie.r.beverly at gmail.com>
 Jamie Gennis <jgennis at google.com> <jgennis at gmail.com>
 Jamie Turner <jamwt at dropbox.com>
 Jamie Wilkinson <jaq at spacepants.org>
@@ -477,9 +520,11 @@ Jaroslavas Počepko <jp at webmaster.ms>
 Jason Barnett <jason.w.barnett at gmail.com>
 Jason Del Ponte <delpontej at gmail.com>
 Jason Hall <jasonhall at google.com>
+Jason Smale <jsmale at zendesk.com>
 Jason Travis <infomaniac7 at gmail.com>
 Jay Weisskopf <jay at jayschwa.net>
 Jean-Marc Eurin <jmeurin at google.com>
+Jean-Nicolas Moal <jn.moal at gmail.com>
 Jed Denlea <jed at fastly.com>
 Jeff Craig <jeffcraig at google.com>
 Jeff Hodges <jeff at somethingsimilar.com>
@@ -490,14 +535,17 @@ Jens Frederich <jfrederich at gmail.com>
 Jeremiah Harmsen <jeremiah at google.com>
 Jeremy Jackins <jeremyjackins at gmail.com>
 Jeremy Schlatter <jeremy.schlatter at gmail.com>
+Jeroen Bobbeldijk <jerbob92 at gmail.com>
 Jess Frazelle <me at jessfraz.com>
 Jihyun Yu <yjh0502 at gmail.com>
 Jim Cote <jfcote87 at gmail.com>
+Jim Kingdon <jim at bolt.me>
 Jim McGrath <jimmc2 at gmail.com>
 Jimmy Zelinskie <jimmyzelinskie at gmail.com>
 Jingcheng Zhang <diogin at gmail.com>
 Jingguo Yao <yaojingguo at gmail.com>
 Jiong Du <londevil at gmail.com>
+Jirka Daněk <dnk at mail.muni.cz>
 Joakim Sernbrant <serbaut at gmail.com>
 Joe Farrell <joe2farrell at gmail.com>
 Joe Harrison <joehazzers at gmail.com>
@@ -524,6 +572,7 @@ John Potocny <johnp at vividcortex.com>
 John Schnake <schnake.john at gmail.com>
 John Shahid <jvshahid at gmail.com>
 John Tuley <john at tuley.org>
+Jon Chen <jchen at justin.tv>
 Jonathan Allie <jonallie at google.com>
 Jonathan Amsterdam <jba at google.com>
 Jonathan Boulle <jonathanboulle at gmail.com>
@@ -536,14 +585,17 @@ Jonathan Pittman <jmpittman at google.com> <jonathan.mark.pittman at gmail.com>
 Jonathan Rudenberg <jonathan at titanous.com>
 Jonathan Wills <runningwild at gmail.com>
 Jongmin Kim <atomaths at gmail.com>
+Joonas Kuorilehto <joneskoo at derbian.fi>
 Jos Visser <josv at google.com>
 Jose Luis Vázquez González <josvazg at gmail.com>
 Joseph Bonneau <jcb at google.com>
 Joseph Holsten <joseph at josephholsten.com>
 Josh Bleecher Snyder <josharian at gmail.com>
+Josh Chorlton <jchorlton at gmail.com>
 Josh Goebel <dreamer3 at gmail.com>
 Josh Hoak <jhoak at google.com>
 Josh Holland <jrh at joshh.co.uk>
+Joshua Boelter <joshua.boelter at intel.com>
 Joshua Chase <jcjoshuachase at gmail.com>
 Jostein Stuhaug <js at solidsystem.no>
 JP Sugarbroad <jpsugar at google.com>
@@ -556,7 +608,9 @@ Julien Schmidt <google at julienschmidt.com>
 Jungho Ahn <jhahn at google.com>
 Jure Ham <jure.ham at zemanta.com>
 Justin Nuß <nuss.justin at gmail.com>
+Justyn Temme <justyntemme at gmail.com>
 Kai Backman <kaib at golang.org>
+Kale Blankenship <kale at lemnisys.com>
 Kamal Aboul-Hosn <aboulhosn at google.com>
 Kamil Kisiel <kamil at kamilkisiel.net> <kamil.kisiel at gmail.com>
 Kang Hu <hukangustc at gmail.com>
@@ -598,6 +652,7 @@ L Campbell <unpantsu at gmail.com>
 Lai Jiangshan <eag0628 at gmail.com>
 Larry Hosken <lahosken at golang.org>
 Larz Conwell <larzconwell at gmail.com>
+LE Manh Cuong <cuong.manhle.vn at gmail.com>
 Lee Hinman <hinman at gmail.com>
 Lee Packham <lpackham at gmail.com>
 Lewin Bormann <lewin.bormann at gmail.com>
@@ -608,6 +663,7 @@ Luan Santos <cfcluan at gmail.com>
 Luca Greco <luca.greco at alcacoop.it>
 Lucien Stuker <lucien.stuker at gmail.com>
 Lucio De Re <lucio.dere at gmail.com>
+Luigi Riefolo <luigi.riefolo at gmail.com>
 Luit van Drongelen <luitvd at gmail.com>
 Luka Zakrajšek <tr00.g33k at gmail.com>
 Luke Curley <qpingu at gmail.com>
@@ -615,6 +671,7 @@ Luna Duclos <luna.duclos at palmstonegames.com>
 Luuk van Dijk <lvd at golang.org> <lvd at google.com>
 Lynn Boger <laboger at linux.vnet.ibm.com>
 Mal Curtis <mal at mal.co.nz>
+Manfred Touron <m at 42.am>
 Manoj Dayaram <platform-dev at moovweb.com> <manoj.dayaram at moovweb.com>
 Manu Garg <manugarg at google.com>
 Manu S Ajith <neo at codingarena.in>
@@ -635,8 +692,10 @@ Marko Tiikkaja <marko at joh.to>
 Markus Duft <markus.duft at salomon.at>
 Markus Sonderegger <marraison at gmail.com>
 Markus Zimmermann <zimmski at gmail.com>
+Martin Bertschler <mbertschler at gmail.com>
 Martin Garton <garton at gmail.com>
-Martin Möhrmann <martisch at uos.de>
+Martin Hamrle <martin.hamrle at gmail.com>
+Martin Möhrmann <moehrmann at google.com> <martisch at uos.de>
 Martin Neubauer <m.ne at gmx.net>
 Martin Olsson <martin at minimum.se>
 Marvin Stenger <marvin.stenger94 at gmail.com>
@@ -660,8 +719,10 @@ Matt Williams <gh at mattyw.net> <mattyjwilliams at gmail.com>
 Matthew Brennan <matty.brennan at gmail.com>
 Matthew Cottingham <mattcottingham at gmail.com>
 Matthew Dempsky <mdempsky at google.com>
+Matthew Denton <mdenton at skyportsystems.com>
 Matthew Holt <Matthew.Holt+git at gmail.com>
 Matthew Horsnell <matthew.horsnell at gmail.com>
+Matthieu Hauglustaine <matt.hauglustaine at gmail.com>
 Maxim Khitrov <max at mxcrypt.com>
 Maxim Pimenov <mpimenov at google.com>
 Maxim Ushakov <ushakov at google.com>
@@ -671,6 +732,7 @@ Meng Zhuo <mengzhuo1203 at gmail.com>
 Mhd Sulhan <m.shulhan at gmail.com>
 Micah Stetson <micah.stetson at gmail.com>
 Michael Chaten <mchaten at gmail.com>
+Michael Darakananda <pongad at google.com>
 Michael Elkins <michael.elkins at gmail.com>
 Michael Fraenkel <michael.fraenkel at gmail.com>
 Michael Gehring <mg at ebfe.org> <gnirheg.leahcim at gmail.com>
@@ -704,17 +766,22 @@ Miguel Mendez <stxmendez at gmail.com>
 Mihai Borobocea <MihaiBorobocea at gmail.com>
 Mikael Tillenius <mikti42 at gmail.com>
 Mike Andrews <mra at xoba.com>
+Mike Appleby <mike at app.leby.org>
 Mike Danese <mikedanese at google.com>
+Mike Houston <mike at kothar.net>
 Mike Rosset <mike.rosset at gmail.com>
 Mike Samuel <mikesamuel at gmail.com>
 Mike Solomon <msolo at gmail.com>
+Mike Strosaker <strosake at us.ibm.com>
 Mikhail Gusarov <dottedmag at dottedmag.net>
 Mikhail Panchenko <m at mihasya.com>
 Miki Tebeka <miki.tebeka at gmail.com>
 Mikio Hara <mikioh.mikioh at gmail.com>
 Mikkel Krautz <mikkel at krautz.dk> <krautz at gmail.com>
 Miquel Sabaté Solà <mikisabate at gmail.com>
+Miroslav Genov <mgenov at gmail.com>
 Mohit Agarwal <mohit at sdf.org>
+Momchil Velikov <momchil.velikov at gmail.com>
 Monty Taylor <mordred at inaugust.com>
 Moriyoshi Koizumi <mozo at mozo.jp>
 Morten Siebuhr <sbhr at sbhr.dk>
@@ -738,6 +805,7 @@ Nicholas Sullivan <nicholas.sullivan at gmail.com>
 Nicholas Waples <nwaples at gmail.com>
 Nick Cooper <nmvc at google.com>
 Nick Craig-Wood <nick at craig-wood.com> <nickcw at gmail.com>
+Nick Harper <nharper at google.com>
 Nick Patavalis <nick.patavalis at gmail.com>
 Nick Petroni <npetroni at cs.umd.edu>
 Nicolas Kaiser <nikai at nikai.net>
@@ -751,6 +819,7 @@ Nikolay Turpitko <nikolay at turpitko.com>
 Noah Campbell <noahcampbell at gmail.com>
 Nodir Turakulov <nodir at google.com>
 Norberto Lopes <nlopes.ml at gmail.com>
+Oleg Vakheta <helginet at gmail.com>
 Oleku Konko <oleku.konko at gmail.com>
 Oling Cat <olingcat at gmail.com>
 Oliver Hookins <ohookins at gmail.com>
@@ -763,6 +832,7 @@ Omar Jarjur <ojarjur at google.com>
 Padraig Kitterick <padraigkitterick at gmail.com>
 Paolo Giarrusso <p.giarrusso at gmail.com>
 Paolo Martini <mrtnpaolo at gmail.com>
+Parker Moore <parkrmoore at gmail.com>
 Pascal S. de Kloe <pascal at quies.net>
 Patrick Crosby <patrick at stathat.com>
 Patrick Gavlin <pgavlin at gmail.com>
@@ -817,6 +887,7 @@ Pierre Durand <pierredurand at gmail.com>
 Pierre Roullon <pierre.roullon at gmail.com>
 Pieter Droogendijk <pieter at binky.org.uk>
 Pietro Gagliardi <pietro10 at mac.com>
+Prasanna Swaminathan <prasanna at mediamath.com>
 Prashant Varanasi <prashant at prashantv.com>
 Preetam Jinka <pj at preet.am>
 Quan Tran <qeed.quan at gmail.com>
@@ -824,10 +895,12 @@ Quan Yong Zhai <qyzhai at gmail.com>
 Quentin Perez <qperez at ocs.online.net>
 Quentin Smith <quentin at golang.org>
 Quoc-Viet Nguyen <afelion at gmail.com>
+Radu Berinde <radu at cockroachlabs.com>
 Rahul Chaudhry <rahulchaudhry at chromium.org>
 Raif S. Naffah <go at naffah-raif.name>
 Rajat Goel <rajat.goel2010 at gmail.com>
 Ralph Corderoy <ralph at inputplus.co.uk>
+Ramesh Dharan <dharan at google.com>
 Raph Levien <raph at google.com>
 Raul Silvera <rsilvera at google.com>
 Reinaldo de Souza Jr <juniorz at gmail.com>
@@ -837,6 +910,7 @@ Ricardo Padilha <ricardospadilha at gmail.com>
 Richard Barnes <rlb at ipv.sx>
 Richard Crowley <r at rcrowley.org>
 Richard Eric Gavaletz <gavaletz at gmail.com>
+Richard Gibson <richard.gibson at gmail.com>
 Richard Miller <miller.research at gmail.com>
 Richard Musiol <mail at richard-musiol.de> <neelance at gmail.com>
 Rick Arnold <rickarnoldjr at gmail.com>
@@ -884,7 +958,10 @@ Sam Thorogood <thorogood at google.com> <sam.thorogood at gmail.com>
 Sam Whited <sam at samwhited.com>
 Sameer Ajmani <sameer at golang.org> <ajmani at gmail.com>
 Sami Commerot <samic at google.com>
+Samuel Tan <samueltan at google.com>
+Samuele Pedroni <pedronis at lucediurna.net>
 Sanjay Menakuru <balasanjay at gmail.com>
+Sarah Adams <shadams at google.com>
 Sasha Lionheart <lionhearts at google.com>
 Sasha Sobol <sasha at scaledinference.com>
 Scott Barron <scott.barron at github.com>
@@ -897,6 +974,7 @@ Scott Van Woudenberg <scottvw at google.com>
 Sean Burford <sburford at google.com>
 Sean Dolphin <Sean.Dolphin at kpcompass.com>
 Sean Harger <sharger at google.com>
+Sean Rees <sean at erifax.org>
 Sebastien Binet <seb.binet at gmail.com>
 Sébastien Paolacci <sebastien.paolacci at gmail.com>
 Sergei Skorobogatov <skorobo at rambler.ru>
@@ -917,9 +995,12 @@ Shivakumar GN <shivakumar.gn at gmail.com>
 Shun Fan <sfan at google.com>
 Silvan Jegen <s.jegen at gmail.com>
 Simon Jefford <simon.jefford at gmail.com>
+Simon Rawet <simon at rawet.se>
 Simon Thulbourn <simon+github at thulbourn.com>
 Simon Whitehead <chemnova at gmail.com>
+Sina Siadat <siadat at gmail.com>
 Sokolov Yura <funny.falcon at gmail.com>
+Song Gao <song at gao.io>
 Spencer Nelson <s at spenczar.com>
 Spring Mc <heresy.mc at gmail.com>
 Srdjan Petrovic <spetrovic at google.com>
@@ -939,7 +1020,10 @@ Steven Elliot Harris <seharris at gmail.com>
 Steven Hartland <steven.hartland at multiplay.co.uk>
 Sugu Sougoumarane <ssougou at gmail.com>
 Suharsh Sivakumar <suharshs at google.com>
+Suyash <dextrous93 at gmail.com>
 Sven Almgren <sven at tras.se>
+Sven Blumenstein <svbl at google.com>
+Syohei YOSHIDA <syohex at gmail.com>
 Szabolcs Nagy <nsz at port70.net>
 Tad Glines <tad.glines at gmail.com>
 Taj Khattra <taj.khattra at gmail.com>
@@ -950,9 +1034,12 @@ Tamir Duberstein <tamird at gmail.com>
 Tarmigan Casebolt <tarmigan at gmail.com>
 Taru Karttunen <taruti at taruti.net>
 Tatsuhiro Tsujikawa <tatsuhiro.t at gmail.com>
+Terrel Shumway <gopher at shumway.us>
 Tetsuo Kiso <tetsuokiso9 at gmail.com>
+Than McIntosh <thanm at google.com>
 Thiago Fransosi Farina <thiago.farina at gmail.com> <tfarina at chromium.org>
 Thomas Alan Copeland <talan.copeland at gmail.com>
+Thomas de Zeeuw <thomasdezeeuw at gmail.com>
 Thomas Desrosiers <thomasdesr at gmail.com>
 Thomas Habets <habets at google.com>
 Thomas Kappler <tkappler at gmail.com>
@@ -960,6 +1047,7 @@ Thorben Krueger <thorben.krueger at gmail.com>
 Tilman Dilo <tilman.dilo at gmail.com>
 Tim Cooijmans <timcooijmans at gmail.com>
 Tim Ebringer <tim.ebringer at gmail.com>
+Tim Henderson <tim.tadh at gmail.com>
 Tim Hockin <thockin at google.com>
 Tim Swast <swast at google.com>
 Timo Savola <timo.savola at gmail.com>
@@ -974,15 +1062,19 @@ Tom Bergan <tombergan at google.com>
 Tom Heng <zhm20070928 at gmail.com>
 Tom Linford <tomlinford at gmail.com>
 Tom Szymanski <tgs at google.com>
+Tom Wilkie <tom at weave.works>
 Tommy Schaefer <tommy.schaefer at teecom.com>
 Tor Andersson <tor.andersson at gmail.com>
 Tormod Erevik Lea <tormodlea at gmail.com>
 Totoro W <tw19881113 at gmail.com>
 Travis Cline <travis.cline at gmail.com>
 Trevor Strohman <trevor.strohman at gmail.com>
+Trey Lawrence <lawrence.trey at gmail.com>
 Trey Tacon <ttacon at gmail.com>
 Tristan Amini <tamini01 at ca.ibm.com>
+Tristan Ooohry <ooohry at gmail.com>
 Tudor Golubenco <tudor.g at gmail.com>
+Tuo Shan <sturbo89 at gmail.com> <shantuo at google.com>
 Tyler Bunnell <tylerbunnell at gmail.com>
 Tyler Treat <ttreat31 at gmail.com>
 Tzu-Jung Lee <roylee17 at currant.com>
@@ -994,15 +1086,21 @@ Uttam C Pawar <uttam.c.pawar at intel.com>
 Vadim Grek <vadimprog at gmail.com>
 Vadim Vygonets <unixdj at gmail.com>
 Vega Garcia Luis Alfonso <vegacom at gmail.com>
+Victor Chudnovsky <vchudnov at google.com>
 Vincent Ambo <tazjin at googlemail.com>
 Vincent Batts <vbatts at hashbangbash.com> <vbatts at gmail.com>
 Vincent Vanackere <vincent.vanackere at gmail.com>
 Vinu Rajashekhar <vinutheraj at gmail.com>
 Vish Subramanian <vish at google.com>
 Vishvananda Ishaya <vishvananda at gmail.com>
+Vitor De Mario <vitordemario at gmail.com>
 Vlad Krasnov <vlad at cloudflare.com>
+Vladimir Mihailenco <vladimir.webdev at gmail.com>
 Vladimir Nikishenko <vova616 at gmail.com>
+Vladimir Stefanovic <vladimir.stefanovic at imgtec.com>
 Volker Dobler <dr.volker.dobler at gmail.com>
+Volodymyr Paprotski <vpaprots at ca.ibm.com>
+Walter Poupore <wpoupore at google.com>
 Wedson Almeida Filho <wedsonaf at google.com>
 Wei Guangjing <vcc.163 at gmail.com>
 Will Chan <willchan at google.com>
@@ -1015,6 +1113,7 @@ Wisdom Omuya <deafgoat at gmail.com>
 Xia Bin <snyh at snyh.org>
 Xing Xing <mikespook at gmail.com>
 Xudong Zhang <felixmelon at gmail.com>
+Xuyang Kang <xuyangkang at gmail.com>
 Yan Zou <yzou at google.com>
 Yann Kerhervé <yann.kerherve at gmail.com>
 Yao Zhang <lunaria21 at gmail.com>
@@ -1024,6 +1123,7 @@ Yesudeep Mangalapilly <yesudeep at google.com>
 Yissakhar Z. Beck <yissakhar.beck at gmail.com>
 Yo-An Lin <yoanlin93 at gmail.com>
 Yongjian Xu <i3dmaster at gmail.com>
+Yorman Arias <cixtords at gmail.com>
 Yoshiyuki Kanno <nekotaroh at gmail.com> <yoshiyuki.kanno at stoic.co.jp>
 Yu Heng Zhang <annita.zhang at cn.ibm.com>
 Yu Xuan Zhang <zyxsh at cn.ibm.com>
@@ -1032,6 +1132,7 @@ Yusuke Kagiwada <block.rxckin.beats at gmail.com>
 Yuusei Kuwana <kuwana at kumama.org>
 Yuval Pavel Zholkover <paulzhol at gmail.com>
 Yves Junqueira <yvesj at google.com> <yves.junqueira at gmail.com>
+Zev Goldstein <zev.goldstein at gmail.com>
 Zhongwei Yao <zhongwei.yao at arm.com>
 Ziad Hatahet <hatahet at gmail.com>
 Zorion Arrizabalaga <zorionk at gmail.com>
diff --git a/VERSION b/VERSION
index 83caa74..6ec9dfa 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-go1.7.4
\ No newline at end of file
+go1.8beta1
\ No newline at end of file
diff --git a/api/except.txt b/api/except.txt
index 4040d14..2062cbf 100644
--- a/api/except.txt
+++ b/api/except.txt
@@ -1,23 +1,107 @@
+pkg encoding/json, method (*RawMessage) MarshalJSON() ([]uint8, error)
 pkg net, func ListenUnixgram(string, *UnixAddr) (*UDPConn, error)
+pkg os (linux-arm), const O_SYNC = 4096
+pkg os (linux-arm-cgo), const O_SYNC = 4096
+pkg syscall (darwin-386), const ImplementsGetwd = false
 pkg syscall (darwin-386), func Fchflags(string, int) error
+pkg syscall (darwin-386-cgo), const ImplementsGetwd = false
 pkg syscall (darwin-386-cgo), func Fchflags(string, int) error
+pkg syscall (darwin-amd64), const ImplementsGetwd = false
 pkg syscall (darwin-amd64), func Fchflags(string, int) error
+pkg syscall (darwin-amd64-cgo), const ImplementsGetwd = false
 pkg syscall (darwin-amd64-cgo), func Fchflags(string, int) error
+pkg syscall (freebsd-386), const AF_MAX = 38
+pkg syscall (freebsd-386), const DLT_MATCHING_MAX = 242
+pkg syscall (freebsd-386), const ELAST = 94
+pkg syscall (freebsd-386), const O_CLOEXEC = 0
 pkg syscall (freebsd-386), func Fchflags(string, int) error
+pkg syscall (freebsd-386-cgo), const AF_MAX = 38
+pkg syscall (freebsd-386-cgo), const DLT_MATCHING_MAX = 242
+pkg syscall (freebsd-386-cgo), const ELAST = 94
+pkg syscall (freebsd-386-cgo), const O_CLOEXEC = 0
+pkg syscall (freebsd-amd64), const AF_MAX = 38
+pkg syscall (freebsd-amd64), const DLT_MATCHING_MAX = 242
+pkg syscall (freebsd-amd64), const ELAST = 94
+pkg syscall (freebsd-amd64), const O_CLOEXEC = 0
 pkg syscall (freebsd-amd64), func Fchflags(string, int) error
+pkg syscall (freebsd-amd64-cgo), const AF_MAX = 38
+pkg syscall (freebsd-amd64-cgo), const DLT_MATCHING_MAX = 242
+pkg syscall (freebsd-amd64-cgo), const ELAST = 94
+pkg syscall (freebsd-amd64-cgo), const O_CLOEXEC = 0
+pkg syscall (freebsd-arm), const AF_MAX = 38
+pkg syscall (freebsd-arm), const BIOCGRTIMEOUT = 1074545262
+pkg syscall (freebsd-arm), const BIOCSRTIMEOUT = 2148287085
+pkg syscall (freebsd-arm), const ELAST = 94
+pkg syscall (freebsd-arm), const O_CLOEXEC = 0
+pkg syscall (freebsd-arm), const SIOCAIFADDR = 2151967019
+pkg syscall (freebsd-arm), const SIOCGIFSTATUS = 3274991931
+pkg syscall (freebsd-arm), const SIOCSIFPHYADDR = 2151967046
+pkg syscall (freebsd-arm), const SYS_CAP_FCNTLS_GET = 537
+pkg syscall (freebsd-arm), const SYS_CAP_FCNTLS_GET ideal-int
+pkg syscall (freebsd-arm), const SYS_CAP_FCNTLS_LIMIT = 536
+pkg syscall (freebsd-arm), const SYS_CAP_FCNTLS_LIMIT ideal-int
+pkg syscall (freebsd-arm), const SYS_CAP_IOCTLS_GET = 535
+pkg syscall (freebsd-arm), const SYS_CAP_IOCTLS_GET ideal-int
+pkg syscall (freebsd-arm), const SYS_CAP_IOCTLS_LIMIT = 534
+pkg syscall (freebsd-arm), const SYS_CAP_IOCTLS_LIMIT ideal-int
+pkg syscall (freebsd-arm), const SYS_CAP_RIGHTS_GET = 515
+pkg syscall (freebsd-arm), const SYS_CAP_RIGHTS_GET ideal-int
+pkg syscall (freebsd-arm), const SYS_CAP_RIGHTS_LIMIT = 533
+pkg syscall (freebsd-arm), const SYS_CAP_RIGHTS_LIMIT ideal-int
+pkg syscall (freebsd-arm), const SizeofBpfHdr = 24
+pkg syscall (freebsd-arm), const SizeofIfData = 88
+pkg syscall (freebsd-arm), const SizeofIfMsghdr = 104
+pkg syscall (freebsd-arm), const SizeofSockaddrDatalink = 56
+pkg syscall (freebsd-arm), const SizeofSockaddrUnix = 108
+pkg syscall (freebsd-arm), const TIOCTIMESTAMP = 1074558041
 pkg syscall (freebsd-arm), func Fchflags(string, int) error
+pkg syscall (freebsd-arm), type BpfHdr struct, Pad_cgo_0 [2]uint8
+pkg syscall (freebsd-arm), type RawSockaddrDatalink struct, Pad_cgo_0 [2]uint8
+pkg syscall (freebsd-arm), type RawSockaddrUnix struct, Pad_cgo_0 [2]uint8
+pkg syscall (freebsd-arm), type Stat_t struct, Pad_cgo_0 [4]uint8
+pkg syscall (freebsd-arm-cgo), const AF_MAX = 38
+pkg syscall (freebsd-arm-cgo), const BIOCGRTIMEOUT = 1074545262
+pkg syscall (freebsd-arm-cgo), const BIOCSRTIMEOUT = 2148287085
+pkg syscall (freebsd-arm-cgo), const ELAST = 94
+pkg syscall (freebsd-arm-cgo), const O_CLOEXEC = 0
+pkg syscall (freebsd-arm-cgo), const SIOCAIFADDR = 2151967019
+pkg syscall (freebsd-arm-cgo), const SIOCGIFSTATUS = 3274991931
+pkg syscall (freebsd-arm-cgo), const SIOCSIFPHYADDR = 2151967046
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_FCNTLS_GET = 537
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_FCNTLS_GET ideal-int
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_FCNTLS_LIMIT = 536
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_FCNTLS_LIMIT ideal-int
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_IOCTLS_GET = 535
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_IOCTLS_GET ideal-int
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_IOCTLS_LIMIT = 534
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_IOCTLS_LIMIT ideal-int
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_RIGHTS_GET = 515
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_RIGHTS_GET ideal-int
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_RIGHTS_LIMIT = 533
+pkg syscall (freebsd-arm-cgo), const SYS_CAP_RIGHTS_LIMIT ideal-int
+pkg syscall (freebsd-arm-cgo), const SizeofBpfHdr = 24
+pkg syscall (freebsd-arm-cgo), const SizeofIfData = 88
+pkg syscall (freebsd-arm-cgo), const SizeofIfMsghdr = 104
+pkg syscall (freebsd-arm-cgo), const SizeofSockaddrDatalink = 56
+pkg syscall (freebsd-arm-cgo), const SizeofSockaddrUnix = 108
+pkg syscall (freebsd-arm-cgo), const TIOCTIMESTAMP = 1074558041
 pkg syscall (freebsd-arm-cgo), func Fchflags(string, int) error
+pkg syscall (freebsd-arm-cgo), type BpfHdr struct, Pad_cgo_0 [2]uint8
+pkg syscall (freebsd-arm-cgo), type RawSockaddrDatalink struct, Pad_cgo_0 [2]uint8
+pkg syscall (freebsd-arm-cgo), type RawSockaddrUnix struct, Pad_cgo_0 [2]uint8
+pkg syscall (freebsd-arm-cgo), type Stat_t struct, Pad_cgo_0 [4]uint8
+pkg syscall (linux-386), type Cmsghdr struct, X__cmsg_data [0]uint8
+pkg syscall (linux-386-cgo), type Cmsghdr struct, X__cmsg_data [0]uint8
+pkg syscall (linux-amd64), type Cmsghdr struct, X__cmsg_data [0]uint8
+pkg syscall (linux-amd64-cgo), type Cmsghdr struct, X__cmsg_data [0]uint8
+pkg syscall (linux-arm), type Cmsghdr struct, X__cmsg_data [0]uint8
+pkg syscall (linux-arm-cgo), type Cmsghdr struct, X__cmsg_data [0]uint8
+pkg syscall (netbsd-arm), const SizeofIfData = 132
 pkg syscall (netbsd-arm), func Fchflags(string, int) error
+pkg syscall (netbsd-arm), type IfMsghdr struct, Pad_cgo_1 [4]uint8
+pkg syscall (netbsd-arm-cgo), const SizeofIfData = 132
 pkg syscall (netbsd-arm-cgo), func Fchflags(string, int) error
-pkg testing, func RegisterCover(Cover)
-pkg text/template/parse, type DotNode bool
-pkg text/template/parse, type Node interface { Copy, String, Type }
-pkg os (linux-arm), const O_SYNC = 4096
-pkg os (linux-arm-cgo), const O_SYNC = 4096
-pkg syscall (darwin-386), const ImplementsGetwd = false
-pkg syscall (darwin-386-cgo), const ImplementsGetwd = false
-pkg syscall (darwin-amd64), const ImplementsGetwd = false
-pkg syscall (darwin-amd64-cgo), const ImplementsGetwd = false
+pkg syscall (netbsd-arm-cgo), type IfMsghdr struct, Pad_cgo_1 [4]uint8
 pkg syscall (openbsd-386), const BIOCGRTIMEOUT = 1074283118
 pkg syscall (openbsd-386), const BIOCSRTIMEOUT = 2148024941
 pkg syscall (openbsd-386), const RTF_FMASK = 63496
@@ -246,87 +330,11 @@ pkg syscall (openbsd-amd64-cgo), type Statfs_t struct, F_spare [3]uint32
 pkg syscall (openbsd-amd64-cgo), type Statfs_t struct, Pad_cgo_1 [4]uint8
 pkg syscall (openbsd-amd64-cgo), type Timespec struct, Pad_cgo_0 [4]uint8
 pkg syscall (openbsd-amd64-cgo), type Timespec struct, Sec int32
+pkg testing, func RegisterCover(Cover)
+pkg testing, func MainStart(func(string, string) (bool, error), []InternalTest, []InternalBenchmark, []InternalExample) *M
+pkg text/template/parse, type DotNode bool
+pkg text/template/parse, type Node interface { Copy, String, Type }
 pkg unicode, const Version = "6.2.0"
-pkg syscall (freebsd-386), const AF_MAX = 38
-pkg syscall (freebsd-386), const DLT_MATCHING_MAX = 242
-pkg syscall (freebsd-386), const ELAST = 94
-pkg syscall (freebsd-386), const O_CLOEXEC = 0
-pkg syscall (freebsd-386-cgo), const AF_MAX = 38
-pkg syscall (freebsd-386-cgo), const DLT_MATCHING_MAX = 242
-pkg syscall (freebsd-386-cgo), const ELAST = 94
-pkg syscall (freebsd-386-cgo), const O_CLOEXEC = 0
-pkg syscall (freebsd-amd64), const AF_MAX = 38
-pkg syscall (freebsd-amd64), const DLT_MATCHING_MAX = 242
-pkg syscall (freebsd-amd64), const ELAST = 94
-pkg syscall (freebsd-amd64), const O_CLOEXEC = 0
-pkg syscall (freebsd-amd64-cgo), const AF_MAX = 38
-pkg syscall (freebsd-amd64-cgo), const DLT_MATCHING_MAX = 242
-pkg syscall (freebsd-amd64-cgo), const ELAST = 94
-pkg syscall (freebsd-amd64-cgo), const O_CLOEXEC = 0
-pkg syscall (freebsd-arm), const AF_MAX = 38
-pkg syscall (freebsd-arm), const BIOCGRTIMEOUT = 1074545262
-pkg syscall (freebsd-arm), const BIOCSRTIMEOUT = 2148287085
-pkg syscall (freebsd-arm), const ELAST = 94
-pkg syscall (freebsd-arm), const O_CLOEXEC = 0
-pkg syscall (freebsd-arm), const SIOCAIFADDR = 2151967019
-pkg syscall (freebsd-arm), const SIOCGIFSTATUS = 3274991931
-pkg syscall (freebsd-arm), const SIOCSIFPHYADDR = 2151967046
-pkg syscall (freebsd-arm), const SYS_CAP_FCNTLS_GET = 537
-pkg syscall (freebsd-arm), const SYS_CAP_FCNTLS_GET ideal-int
-pkg syscall (freebsd-arm), const SYS_CAP_FCNTLS_LIMIT = 536
-pkg syscall (freebsd-arm), const SYS_CAP_FCNTLS_LIMIT ideal-int
-pkg syscall (freebsd-arm), const SYS_CAP_IOCTLS_GET = 535
-pkg syscall (freebsd-arm), const SYS_CAP_IOCTLS_GET ideal-int
-pkg syscall (freebsd-arm), const SYS_CAP_IOCTLS_LIMIT = 534
-pkg syscall (freebsd-arm), const SYS_CAP_IOCTLS_LIMIT ideal-int
-pkg syscall (freebsd-arm), const SYS_CAP_RIGHTS_GET = 515
-pkg syscall (freebsd-arm), const SYS_CAP_RIGHTS_GET ideal-int
-pkg syscall (freebsd-arm), const SYS_CAP_RIGHTS_LIMIT = 533
-pkg syscall (freebsd-arm), const SYS_CAP_RIGHTS_LIMIT ideal-int
-pkg syscall (freebsd-arm), const SizeofBpfHdr = 24
-pkg syscall (freebsd-arm), const SizeofIfData = 88
-pkg syscall (freebsd-arm), const SizeofIfMsghdr = 104
-pkg syscall (freebsd-arm), const SizeofSockaddrDatalink = 56
-pkg syscall (freebsd-arm), const SizeofSockaddrUnix = 108
-pkg syscall (freebsd-arm), const TIOCTIMESTAMP = 1074558041
-pkg syscall (freebsd-arm), type BpfHdr struct, Pad_cgo_0 [2]uint8
-pkg syscall (freebsd-arm), type RawSockaddrDatalink struct, Pad_cgo_0 [2]uint8
-pkg syscall (freebsd-arm), type RawSockaddrUnix struct, Pad_cgo_0 [2]uint8
-pkg syscall (freebsd-arm), type Stat_t struct, Pad_cgo_0 [4]uint8
-pkg syscall (freebsd-arm-cgo), const AF_MAX = 38
-pkg syscall (freebsd-arm-cgo), const BIOCGRTIMEOUT = 1074545262
-pkg syscall (freebsd-arm-cgo), const BIOCSRTIMEOUT = 2148287085
-pkg syscall (freebsd-arm-cgo), const ELAST = 94
-pkg syscall (freebsd-arm-cgo), const O_CLOEXEC = 0
-pkg syscall (freebsd-arm-cgo), const SIOCAIFADDR = 2151967019
-pkg syscall (freebsd-arm-cgo), const SIOCGIFSTATUS = 3274991931
-pkg syscall (freebsd-arm-cgo), const SIOCSIFPHYADDR = 2151967046
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_FCNTLS_GET = 537
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_FCNTLS_GET ideal-int
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_FCNTLS_LIMIT = 536
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_FCNTLS_LIMIT ideal-int
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_IOCTLS_GET = 535
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_IOCTLS_GET ideal-int
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_IOCTLS_LIMIT = 534
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_IOCTLS_LIMIT ideal-int
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_RIGHTS_GET = 515
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_RIGHTS_GET ideal-int
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_RIGHTS_LIMIT = 533
-pkg syscall (freebsd-arm-cgo), const SYS_CAP_RIGHTS_LIMIT ideal-int
-pkg syscall (freebsd-arm-cgo), const SizeofBpfHdr = 24
-pkg syscall (freebsd-arm-cgo), const SizeofIfData = 88
-pkg syscall (freebsd-arm-cgo), const SizeofIfMsghdr = 104
-pkg syscall (freebsd-arm-cgo), const SizeofSockaddrDatalink = 56
-pkg syscall (freebsd-arm-cgo), const SizeofSockaddrUnix = 108
-pkg syscall (freebsd-arm-cgo), const TIOCTIMESTAMP = 1074558041
-pkg syscall (freebsd-arm-cgo), type BpfHdr struct, Pad_cgo_0 [2]uint8
-pkg syscall (freebsd-arm-cgo), type RawSockaddrDatalink struct, Pad_cgo_0 [2]uint8
-pkg syscall (freebsd-arm-cgo), type RawSockaddrUnix struct, Pad_cgo_0 [2]uint8
-pkg syscall (freebsd-arm-cgo), type Stat_t struct, Pad_cgo_0 [4]uint8
-pkg syscall (netbsd-arm), const SizeofIfData = 132
-pkg syscall (netbsd-arm), type IfMsghdr struct, Pad_cgo_1 [4]uint8
-pkg syscall (netbsd-arm-cgo), const SizeofIfData = 132
-pkg syscall (netbsd-arm-cgo), type IfMsghdr struct, Pad_cgo_1 [4]uint8
 pkg unicode, const Version = "6.3.0"
 pkg unicode, const Version = "7.0.0"
 pkg unicode, const Version = "8.0.0"
diff --git a/api/go1.8.txt b/api/go1.8.txt
new file mode 100644
index 0000000..e9ddc28
--- /dev/null
+++ b/api/go1.8.txt
@@ -0,0 +1,259 @@
+pkg compress/gzip, const HuffmanOnly = -2
+pkg compress/gzip, const HuffmanOnly ideal-int
+pkg compress/zlib, const HuffmanOnly = -2
+pkg compress/zlib, const HuffmanOnly ideal-int
+pkg crypto/tls, const ECDSAWithP256AndSHA256 = 1027
+pkg crypto/tls, const ECDSAWithP256AndSHA256 SignatureScheme
+pkg crypto/tls, const ECDSAWithP384AndSHA384 = 1283
+pkg crypto/tls, const ECDSAWithP384AndSHA384 SignatureScheme
+pkg crypto/tls, const ECDSAWithP521AndSHA512 = 1539
+pkg crypto/tls, const ECDSAWithP521AndSHA512 SignatureScheme
+pkg crypto/tls, const PKCS1WithSHA1 = 513
+pkg crypto/tls, const PKCS1WithSHA1 SignatureScheme
+pkg crypto/tls, const PKCS1WithSHA256 = 1025
+pkg crypto/tls, const PKCS1WithSHA256 SignatureScheme
+pkg crypto/tls, const PKCS1WithSHA384 = 1281
+pkg crypto/tls, const PKCS1WithSHA384 SignatureScheme
+pkg crypto/tls, const PKCS1WithSHA512 = 1537
+pkg crypto/tls, const PKCS1WithSHA512 SignatureScheme
+pkg crypto/tls, const PSSWithSHA256 = 2052
+pkg crypto/tls, const PSSWithSHA256 SignatureScheme
+pkg crypto/tls, const PSSWithSHA384 = 2053
+pkg crypto/tls, const PSSWithSHA384 SignatureScheme
+pkg crypto/tls, const PSSWithSHA512 = 2054
+pkg crypto/tls, const PSSWithSHA512 SignatureScheme
+pkg crypto/tls, const TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 49187
+pkg crypto/tls, const TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16
+pkg crypto/tls, const TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 52393
+pkg crypto/tls, const TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 uint16
+pkg crypto/tls, const TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 49191
+pkg crypto/tls, const TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16
+pkg crypto/tls, const TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 52392
+pkg crypto/tls, const TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 uint16
+pkg crypto/tls, const TLS_RSA_WITH_AES_128_CBC_SHA256 = 60
+pkg crypto/tls, const TLS_RSA_WITH_AES_128_CBC_SHA256 uint16
+pkg crypto/tls, const X25519 = 29
+pkg crypto/tls, const X25519 CurveID
+pkg crypto/tls, method (*Config) Clone() *Config
+pkg crypto/tls, method (*Conn) CloseWrite() error
+pkg crypto/tls, type CertificateRequestInfo struct
+pkg crypto/tls, type CertificateRequestInfo struct, AcceptableCAs [][]uint8
+pkg crypto/tls, type CertificateRequestInfo struct, SignatureSchemes []SignatureScheme
+pkg crypto/tls, type ClientHelloInfo struct, Conn net.Conn
+pkg crypto/tls, type ClientHelloInfo struct, SignatureSchemes []SignatureScheme
+pkg crypto/tls, type ClientHelloInfo struct, SupportedProtos []string
+pkg crypto/tls, type ClientHelloInfo struct, SupportedVersions []uint16
+pkg crypto/tls, type Config struct, GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error)
+pkg crypto/tls, type Config struct, GetConfigForClient func(*ClientHelloInfo) (*Config, error)
+pkg crypto/tls, type Config struct, KeyLogWriter io.Writer
+pkg crypto/tls, type Config struct, VerifyPeerCertificate func([][]uint8, [][]*x509.Certificate) error
+pkg crypto/tls, type SignatureScheme uint16
+pkg crypto/x509, const NameMismatch = 5
+pkg crypto/x509, const NameMismatch InvalidReason
+pkg crypto/x509, const SHA256WithRSAPSS = 13
+pkg crypto/x509, const SHA256WithRSAPSS SignatureAlgorithm
+pkg crypto/x509, const SHA384WithRSAPSS = 14
+pkg crypto/x509, const SHA384WithRSAPSS SignatureAlgorithm
+pkg crypto/x509, const SHA512WithRSAPSS = 15
+pkg crypto/x509, const SHA512WithRSAPSS SignatureAlgorithm
+pkg crypto/x509, type UnknownAuthorityError struct, Cert *Certificate
+pkg database/sql, const LevelDefault = 0
+pkg database/sql, const LevelDefault IsolationLevel
+pkg database/sql, const LevelLinearizable = 7
+pkg database/sql, const LevelLinearizable IsolationLevel
+pkg database/sql, const LevelReadCommitted = 2
+pkg database/sql, const LevelReadCommitted IsolationLevel
+pkg database/sql, const LevelReadUncommitted = 1
+pkg database/sql, const LevelReadUncommitted IsolationLevel
+pkg database/sql, const LevelRepeatableRead = 4
+pkg database/sql, const LevelRepeatableRead IsolationLevel
+pkg database/sql, const LevelSerializable = 6
+pkg database/sql, const LevelSerializable IsolationLevel
+pkg database/sql, const LevelSnapshot = 5
+pkg database/sql, const LevelSnapshot IsolationLevel
+pkg database/sql, const LevelWriteCommitted = 3
+pkg database/sql, const LevelWriteCommitted IsolationLevel
+pkg database/sql/driver, func IsolationFromContext(context.Context) (IsolationLevel, bool)
+pkg database/sql/driver, func ReadOnlyFromContext(context.Context) bool
+pkg database/sql/driver, type ConnBeginContext interface { BeginContext }
+pkg database/sql/driver, type ConnBeginContext interface, BeginContext(context.Context) (Tx, error)
+pkg database/sql/driver, type ConnPrepareContext interface { PrepareContext }
+pkg database/sql/driver, type ConnPrepareContext interface, PrepareContext(context.Context, string) (Stmt, error)
+pkg database/sql/driver, type ExecerContext interface { ExecContext }
+pkg database/sql/driver, type ExecerContext interface, ExecContext(context.Context, string, []NamedValue) (Result, error)
+pkg database/sql/driver, type IsolationLevel int
+pkg database/sql/driver, type NamedValue struct
+pkg database/sql/driver, type NamedValue struct, Name string
+pkg database/sql/driver, type NamedValue struct, Ordinal int
+pkg database/sql/driver, type NamedValue struct, Value Value
+pkg database/sql/driver, type Pinger interface { Ping }
+pkg database/sql/driver, type Pinger interface, Ping(context.Context) error
+pkg database/sql/driver, type QueryerContext interface { QueryContext }
+pkg database/sql/driver, type QueryerContext interface, QueryContext(context.Context, string, []NamedValue) (Rows, error)
+pkg database/sql/driver, type RowsColumnTypeDatabaseTypeName interface { Close, ColumnTypeDatabaseTypeName, Columns, Next }
+pkg database/sql/driver, type RowsColumnTypeDatabaseTypeName interface, Close() error
+pkg database/sql/driver, type RowsColumnTypeDatabaseTypeName interface, Columns() []string
+pkg database/sql/driver, type RowsColumnTypeDatabaseTypeName interface, ColumnTypeDatabaseTypeName(int) string
+pkg database/sql/driver, type RowsColumnTypeDatabaseTypeName interface, Next([]Value) error
+pkg database/sql/driver, type RowsColumnTypeLength interface { Close, ColumnTypeLength, Columns, Next }
+pkg database/sql/driver, type RowsColumnTypeLength interface, Close() error
+pkg database/sql/driver, type RowsColumnTypeLength interface, Columns() []string
+pkg database/sql/driver, type RowsColumnTypeLength interface, ColumnTypeLength(int) (int64, bool)
+pkg database/sql/driver, type RowsColumnTypeLength interface, Next([]Value) error
+pkg database/sql/driver, type RowsColumnTypeNullable interface { Close, ColumnTypeNullable, Columns, Next }
+pkg database/sql/driver, type RowsColumnTypeNullable interface, Close() error
+pkg database/sql/driver, type RowsColumnTypeNullable interface, Columns() []string
+pkg database/sql/driver, type RowsColumnTypeNullable interface, ColumnTypeNullable(int) (bool, bool)
+pkg database/sql/driver, type RowsColumnTypeNullable interface, Next([]Value) error
+pkg database/sql/driver, type RowsColumnTypePrecisionScale interface { Close, ColumnTypePrecisionScale, Columns, Next }
+pkg database/sql/driver, type RowsColumnTypePrecisionScale interface, Close() error
+pkg database/sql/driver, type RowsColumnTypePrecisionScale interface, Columns() []string
+pkg database/sql/driver, type RowsColumnTypePrecisionScale interface, ColumnTypePrecisionScale(int) (int64, int64, bool)
+pkg database/sql/driver, type RowsColumnTypePrecisionScale interface, Next([]Value) error
+pkg database/sql/driver, type RowsColumnTypeScanType interface { Close, ColumnTypeScanType, Columns, Next }
+pkg database/sql/driver, type RowsColumnTypeScanType interface, Close() error
+pkg database/sql/driver, type RowsColumnTypeScanType interface, Columns() []string
+pkg database/sql/driver, type RowsColumnTypeScanType interface, ColumnTypeScanType(int) reflect.Type
+pkg database/sql/driver, type RowsColumnTypeScanType interface, Next([]Value) error
+pkg database/sql/driver, type RowsNextResultSet interface { Close, Columns, HasNextResultSet, Next, NextResultSet }
+pkg database/sql/driver, type RowsNextResultSet interface, Close() error
+pkg database/sql/driver, type RowsNextResultSet interface, Columns() []string
+pkg database/sql/driver, type RowsNextResultSet interface, HasNextResultSet() bool
+pkg database/sql/driver, type RowsNextResultSet interface, NextResultSet() error
+pkg database/sql/driver, type RowsNextResultSet interface, Next([]Value) error
+pkg database/sql/driver, type StmtExecContext interface { ExecContext }
+pkg database/sql/driver, type StmtExecContext interface, ExecContext(context.Context, []NamedValue) (Result, error)
+pkg database/sql/driver, type StmtQueryContext interface { QueryContext }
+pkg database/sql/driver, type StmtQueryContext interface, QueryContext(context.Context, []NamedValue) (Rows, error)
+pkg database/sql, func IsolationContext(context.Context, IsolationLevel) context.Context
+pkg database/sql, func Named(string, interface{}) NamedArg
+pkg database/sql, func ReadOnlyContext(context.Context) context.Context
+pkg database/sql, method (*ColumnType) DatabaseTypeName() string
+pkg database/sql, method (*ColumnType) DecimalSize() (int64, int64, bool)
+pkg database/sql, method (*ColumnType) Length() (int64, bool)
+pkg database/sql, method (*ColumnType) Name() string
+pkg database/sql, method (*ColumnType) Nullable() (bool, bool)
+pkg database/sql, method (*ColumnType) ScanType() reflect.Type
+pkg database/sql, method (*DB) BeginContext(context.Context) (*Tx, error)
+pkg database/sql, method (*DB) ExecContext(context.Context, string, ...interface{}) (Result, error)
+pkg database/sql, method (*DB) PingContext(context.Context) error
+pkg database/sql, method (*DB) PrepareContext(context.Context, string) (*Stmt, error)
+pkg database/sql, method (*DB) QueryContext(context.Context, string, ...interface{}) (*Rows, error)
+pkg database/sql, method (*DB) QueryRowContext(context.Context, string, ...interface{}) *Row
+pkg database/sql, method (*Rows) ColumnTypes() ([]*ColumnType, error)
+pkg database/sql, method (*Rows) NextResultSet() bool
+pkg database/sql, method (*Stmt) ExecContext(context.Context, ...interface{}) (Result, error)
+pkg database/sql, method (*Stmt) QueryContext(context.Context, ...interface{}) (*Rows, error)
+pkg database/sql, method (*Stmt) QueryRowContext(context.Context, ...interface{}) *Row
+pkg database/sql, method (*Tx) ExecContext(context.Context, string, ...interface{}) (Result, error)
+pkg database/sql, method (*Tx) PrepareContext(context.Context, string) (*Stmt, error)
+pkg database/sql, method (*Tx) QueryContext(context.Context, string, ...interface{}) (*Rows, error)
+pkg database/sql, method (*Tx) QueryRowContext(context.Context, string, ...interface{}) *Row
+pkg database/sql, method (*Tx) StmtContext(context.Context, *Stmt) *Stmt
+pkg database/sql, type ColumnType struct
+pkg database/sql, type IsolationLevel int
+pkg database/sql, type NamedArg struct
+pkg database/sql, type NamedArg struct, Name string
+pkg database/sql, type NamedArg struct, Value interface{}
+pkg debug/gosym, func PCValue([]uint8, uint64, int) int
+pkg debug/pe, method (*COFFSymbol) FullName(StringTable) (string, error)
+pkg debug/pe, method (StringTable) String(uint32) (string, error)
+pkg debug/pe, type File struct, COFFSymbols []COFFSymbol
+pkg debug/pe, type File struct, StringTable StringTable
+pkg debug/pe, type Reloc struct
+pkg debug/pe, type Reloc struct, SymbolTableIndex uint32
+pkg debug/pe, type Reloc struct, Type uint16
+pkg debug/pe, type Reloc struct, VirtualAddress uint32
+pkg debug/pe, type Section struct, Relocs []Reloc
+pkg debug/pe, type StringTable []uint8
+pkg encoding/base64, method (Encoding) Strict() *Encoding
+pkg encoding/json, method (RawMessage) MarshalJSON() ([]uint8, error)
+pkg encoding/json, type UnmarshalTypeError struct, Field string
+pkg encoding/json, type UnmarshalTypeError struct, Struct string
+pkg expvar, func Handler() http.Handler
+pkg expvar, method (*Float) Value() float64
+pkg expvar, method (Func) Value() interface{}
+pkg expvar, method (*Int) Value() int64
+pkg expvar, method (*String) Value() string
+pkg go/build, type NoGoError struct, Ignored bool
+pkg go/doc, func IsPredeclared(string) bool
+pkg go/types, func Default(Type) Type
+pkg go/types, func IdenticalIgnoreTags(Type, Type) bool
+pkg math/big, method (*Float) Scan(fmt.ScanState, int32) error
+pkg math/big, method (*Int) Sqrt(*Int) *Int
+pkg math/rand, func Uint64() uint64
+pkg math/rand, method (*Rand) Uint64() uint64
+pkg math/rand, type Source64 interface, Int63() int64
+pkg math/rand, type Source64 interface { Int63, Seed, Uint64 }
+pkg math/rand, type Source64 interface, Seed(int64)
+pkg math/rand, type Source64 interface, Uint64() uint64
+pkg net/http, const TrailerPrefix ideal-string
+pkg net/http, const TrailerPrefix = "Trailer:"
+pkg net/http/httptrace, type ClientTrace struct, TLSHandshakeDone func(tls.ConnectionState, error)
+pkg net/http/httptrace, type ClientTrace struct, TLSHandshakeStart func()
+pkg net/http/httputil, type ReverseProxy struct, ModifyResponse func(*http.Response) error
+pkg net/http, method (*Server) Close() error
+pkg net/http, method (*Server) Shutdown(context.Context) error
+pkg net/http, type Pusher interface { Push }
+pkg net/http, type Pusher interface, Push(string, *PushOptions) error
+pkg net/http, type PushOptions struct
+pkg net/http, type PushOptions struct, Header Header
+pkg net/http, type PushOptions struct, Method string
+pkg net/http, type Request struct, GetBody func() (io.ReadCloser, error)
+pkg net/http, type Server struct, IdleTimeout time.Duration
+pkg net/http, type Server struct, ReadHeaderTimeout time.Duration
+pkg net/http, type Transport struct, ProxyConnectHeader Header
+pkg net/http, var ErrAbortHandler error
+pkg net/http, var ErrServerClosed error
+pkg net/http, var NoBody noBody
+pkg net/mail, func ParseDate(string) (time.Time, error)
+pkg net, method (*Buffers) Read([]uint8) (int, error)
+pkg net, method (*Buffers) WriteTo(io.Writer) (int64, error)
+pkg net, method (*Resolver) LookupAddr(context.Context, string) ([]string, error)
+pkg net, method (*Resolver) LookupCNAME(context.Context, string) (string, error)
+pkg net, method (*Resolver) LookupHost(context.Context, string) ([]string, error)
+pkg net, method (*Resolver) LookupIPAddr(context.Context, string) ([]IPAddr, error)
+pkg net, method (*Resolver) LookupMX(context.Context, string) ([]*MX, error)
+pkg net, method (*Resolver) LookupNS(context.Context, string) ([]*NS, error)
+pkg net, method (*Resolver) LookupPort(context.Context, string, string) (int, error)
+pkg net, method (*Resolver) LookupSRV(context.Context, string, string, string) (string, []*SRV, error)
+pkg net, method (*Resolver) LookupTXT(context.Context, string) ([]string, error)
+pkg net, method (*UnixListener) SetUnlinkOnClose(bool)
+pkg net, type Buffers [][]uint8
+pkg net, type Dialer struct, Resolver *Resolver
+pkg net, type Resolver struct
+pkg net, type Resolver struct, PreferGo bool
+pkg net/url, func PathEscape(string) string
+pkg net/url, func PathUnescape(string) (string, error)
+pkg net/url, method (*URL) Hostname() string
+pkg net/url, method (*URL) MarshalBinary() ([]uint8, error)
+pkg net/url, method (*URL) Port() string
+pkg net/url, method (*URL) UnmarshalBinary([]uint8) error
+pkg net, var DefaultResolver *Resolver
+pkg os, func Executable() (string, error)
+pkg os, var ErrClosed error
+pkg plugin, func Open(string) (*Plugin, error)
+pkg plugin, method (*Plugin) Lookup(string) (Symbol, error)
+pkg plugin, type Plugin struct
+pkg plugin, type Symbol interface {}
+pkg reflect, func Swapper(interface{}) func(int, int)
+pkg runtime, func MutexProfile([]BlockProfileRecord) (int, bool)
+pkg runtime, func SetMutexProfileFraction(int) int
+pkg sort, func Slice(interface{}, func(int, int) bool)
+pkg sort, func SliceIsSorted(interface{}, func(int, int) bool) bool
+pkg sort, func SliceStable(interface{}, func(int, int) bool)
+pkg syscall (linux-arm-cgo), func TimevalToNsec(Timeval) int64
+pkg syscall (linux-arm), func TimevalToNsec(Timeval) int64
+pkg syscall (windows-386), const ERROR_DIR_NOT_EMPTY = 145
+pkg syscall (windows-386), const ERROR_DIR_NOT_EMPTY Errno
+pkg syscall (windows-amd64), const ERROR_DIR_NOT_EMPTY = 145
+pkg syscall (windows-amd64), const ERROR_DIR_NOT_EMPTY Errno
+pkg testing, func CoverMode() string
+pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalExample) *M
+pkg testing, method (*B) Context() context.Context
+pkg testing, method (*B) Name() string
+pkg testing, method (*T) Context() context.Context
+pkg testing, method (*T) Name() string
+pkg testing, type TB interface, Context() context.Context
+pkg testing, type TB interface, Name() string
+pkg time, func Until(Time) Duration
diff --git a/doc/articles/go_command.html b/doc/articles/go_command.html
index cc1d86a..0fd83cb 100644
--- a/doc/articles/go_command.html
+++ b/doc/articles/go_command.html
@@ -97,13 +97,14 @@ a tool like the go command to look at an unfamiliar import path and
 deduce where to obtain the source code.</p>
 
 <p>Second, the place to store sources in the local file system is derived
-in a known way from the import path.  Specifically, the first choice
-is <code>$GOPATH/src/<import-path></code>.  If <code>$GOPATH</code> is
-unset, the go command will fall back to storing source code alongside the
-standard Go packages, in <code>$GOROOT/src/<import-path></code>.
+in a known way from the import path, specifically
+<code>$GOPATH/src/<import-path></code>.
+If unset, <code>$GOPATH</code> defaults to a subdirectory
+named <code>go</code> in the user's home directory.
 If <code>$GOPATH</code> is set to a list of paths, the go command tries
 <code><dir>/src/<import-path></code> for each of the directories in
-that list.</p>
+that list.
+</p>
 
 <p>Each of those trees contains, by convention, a top-level directory named
 "<code>bin</code>", for holding compiled executables, and a top-level directory
@@ -137,41 +138,26 @@ to the use of a specific tool chain.</p>
 
 <h2>Getting started with the go command</h2>
 
-<p>Finally, a quick tour of how to use the go command, to supplement
-the information in <a href="/doc/code.html">How to Write Go Code</a>,
-which you might want to read first.  Assuming you want
-to keep your source code separate from the Go distribution source
-tree, the first step is to set <code>$GOPATH</code>, the one piece of global
-configuration that the go command needs.  The <code>$GOPATH</code> can be a
-list of directories, but by far the most common usage should be to set it to a
-single directory.  In particular, you do not need a separate entry in
-<code>$GOPATH</code> for each of your projects.  One <code>$GOPATH</code> can
-support many projects.</p>
-
-<p>Here’s an example.  Let’s say we decide to keep our Go code in the directory
-<code>$HOME/mygo</code>.  We need to create that directory and set
-<code>$GOPATH</code> accordingly.</p>
+<p>Finally, a quick tour of how to use the go command.
+As mentioned above, the default <code>$GOPATH</code> on Unix is <code>$HOME/go</code>.
+We'll store our programs there.
+To use a different location, you can set <code>$GOPATH</code>;
+see <a href="/doc/code.html">How to Write Go Code</a> for details.
 
-<pre>
-$ mkdir $HOME/mygo
-$ export GOPATH=$HOME/mygo
-$
-</pre>
-
-<p>Into this directory, we now add some source code.  Suppose we want to use
+<p>We first add some source code.  Suppose we want to use
 the indexing library from the codesearch project along with a left-leaning
 red-black tree.  We can install both with the "<code>go get</code>"
 subcommand:</p>
 
 <pre>
-$ go get code.google.com/p/codesearch/index
+$ go get github.com/google/codesearch/index
 $ go get github.com/petar/GoLLRB/llrb
 $
 </pre>
 
-<p>Both of these projects are now downloaded and installed into our
-<code>$GOPATH</code> directory. The one tree now contains the two directories
-<code>src/code.google.com/p/codesearch/index/</code> and
+<p>Both of these projects are now downloaded and installed into <code>$HOME/go</code>,
+which contains the two directories
+<code>src/github.com/google/codesearch/index/</code> and
 <code>src/github.com/petar/GoLLRB/llrb/</code>, along with the compiled
 packages (in <code>pkg/</code>) for those libraries and their dependencies.</p>
 
@@ -184,13 +170,14 @@ the pattern "<code>./...</code>" means start in the current directory
 ("<code>...</code>"):</p>
 
 <pre>
+$ cd $HOME/go/src
 $ go list ./...
-code.google.com/p/codesearch/cmd/cgrep
-code.google.com/p/codesearch/cmd/cindex
-code.google.com/p/codesearch/cmd/csearch
-code.google.com/p/codesearch/index
-code.google.com/p/codesearch/regexp
-code.google.com/p/codesearch/sparse
+github.com/google/codesearch/cmd/cgrep
+github.com/google/codesearch/cmd/cindex
+github.com/google/codesearch/cmd/csearch
+github.com/google/codesearch/index
+github.com/google/codesearch/regexp
+github.com/google/codesearch/sparse
 github.com/petar/GoLLRB/example
 github.com/petar/GoLLRB/llrb
 $
@@ -200,12 +187,12 @@ $
 
 <pre>
 $ go test ./...
-?       code.google.com/p/codesearch/cmd/cgrep   [no test files]
-?       code.google.com/p/codesearch/cmd/cindex  [no test files]
-?       code.google.com/p/codesearch/cmd/csearch [no test files]
-ok      code.google.com/p/codesearch/index       0.239s
-ok      code.google.com/p/codesearch/regexp      0.021s
-?       code.google.com/p/codesearch/sparse      [no test files]
+?   	github.com/google/codesearch/cmd/cgrep	[no test files]
+?   	github.com/google/codesearch/cmd/cindex	[no test files]
+?   	github.com/google/codesearch/cmd/csearch	[no test files]
+ok  	github.com/google/codesearch/index	0.203s
+ok  	github.com/google/codesearch/regexp	0.017s
+?   	github.com/google/codesearch/sparse	[no test files]
 ?       github.com/petar/GoLLRB/example          [no test files]
 ok      github.com/petar/GoLLRB/llrb             0.231s
 $
@@ -215,18 +202,18 @@ $
 current directory:</p>
 
 <pre>
-$ cd $GOPATH/src/code.google.com/p/codesearch/regexp
+$ cd github.com/google/codesearch/regexp
 $ go list
-code.google.com/p/codesearch/regexp
+github.com/google/codesearch/regexp
 $ go test -v
-=== RUN TestNstateEnc
---- PASS: TestNstateEnc (0.00 seconds)
-=== RUN TestMatch
---- PASS: TestMatch (0.01 seconds)
-=== RUN TestGrep
---- PASS: TestGrep (0.00 seconds)
+=== RUN   TestNstateEnc
+--- PASS: TestNstateEnc (0.00s)
+=== RUN   TestMatch
+--- PASS: TestMatch (0.00s)
+=== RUN   TestGrep
+--- PASS: TestGrep (0.00s)
 PASS
-ok      code.google.com/p/codesearch/regexp     0.021s
+ok  	github.com/google/codesearch/regexp	0.018s
 $ go install
 $
 </pre>
@@ -244,9 +231,6 @@ pick such a long name, but that ability would require additional configuration
 and complexity in the tool. Typing an extra directory name or two is a small
 price to pay for the increased simplicity and power.</p>
 
-<p>As the example shows, it’s fine to work with packages from many different
-projects at once within a single <code>$GOPATH</code> root directory.</p>
-
 <h2>Limitations</h2>
 
 <p>As mentioned above, the go command is not a general-purpose build
@@ -255,8 +239,7 @@ In particular, it does not have any facility for generating Go
 source files <em>during</em> a build, although it does provide
 <a href="/cmd/go/#hdr-Generate_Go_files_by_processing_source"><code>go</code>
 <code>generate</code></a>,
-which can automate the creation of Go files <em>before</em>
-the build, such as by running <code>yacc</code>.
+which can automate the creation of Go files <em>before</em> the build.
 For more advanced build setups, you may need to write a
 makefile (or a configuration file for the build tool of your choice)
 to run whatever tool creates the Go files and then check those generated source files
diff --git a/doc/code.html b/doc/code.html
index fdca404..9978b52 100644
--- a/doc/code.html
+++ b/doc/code.html
@@ -120,36 +120,49 @@ We will discuss the distinction <a href="#PackageNames">later</a>.
 
 <p>
 The <code>GOPATH</code> environment variable specifies the location of your
-workspace. It is likely the only environment variable you'll need to set
-when developing Go code.
+workspace. It defaults to a directory named <code>go</code> inside your home directory,
+so <code>$HOME/go</code> on Unix,
+<code>$home/go</code> on Plan 9,
+and <code>%USERPROFILE%\go</code> (usually <code>C:\Users\YourName\go</code>) on Windows.
+If you would like to work in a different location, you will need to set
+<code>GOPATH</code> to the path to that directory.
+(Another common setup is to set <code>GOPATH=$HOME</code>.)
+Note that <code>GOPATH</code> must <b>not</b> be the
+same path as your Go installation.
 </p>
 
 <p>
-To get started, create a workspace directory and set <code>GOPATH</code>
-accordingly. Your workspace can be located wherever you like, but we'll use
-<code>$HOME/work</code> in this document. Note that this must <b>not</b> be the
-same path as your Go installation.
-(Another common setup is to set <code>GOPATH=$HOME</code>.)
+The command <code>go</code> <code>env</code> <code>GOPATH</code>
+prints the effective current <code>GOPATH</code>;
+it prints the default location if the environment variable is unset.
+</p>
+
+<p>
+For convenience, add the workspace's <code>bin</code> subdirectory
+to your <code>PATH</code>:
 </p>
 
 <pre>
-$ <b>mkdir $HOME/work</b>
-$ <b>export GOPATH=$HOME/work</b>
+$ <b>export PATH=$PATH:$(go env GOPATH)/bin</b>
 </pre>
 
 <p>
-For convenience, add the workspace's <code>bin</code> subdirectory
-to your <code>PATH</code>:
+The scripts in the rest of this document use <code>$GOPATH</code>
+instead of <code>$(go env GOPATH)</code> for brevity.
+To make the scripts run as written
+if you have not set GOPATH,
+you can substitute $HOME/go in those commands
+or else run:
 </p>
 
 <pre>
-$ <b>export PATH=$PATH:$GOPATH/bin</b>
+$ <b>export GOPATH=$(go env GOPATH)</b>
 </pre>
 
 <p>
 To learn more about setting up the <code>GOPATH</code> environment variable,
 please see
-<a href="/cmd/go/#hdr-GOPATH_environment_variable"><code>go help gopath</code></a>
+<a href="/cmd/go/#hdr-GOPATH_environment_variable"><code>'go help gopath'</code></a>
 </p>
 
 <h3 id="ImportPaths">Import paths</h3>
diff --git a/doc/conduct.html b/doc/conduct.html
index ce824a1..c749266 100644
--- a/doc/conduct.html
+++ b/doc/conduct.html
@@ -20,8 +20,8 @@ ul ul {
 <p>
 Online communities include people from many different backgrounds.
 The Go contributors are committed to providing a friendly, safe and welcoming
-environment for all, regardless of age, disability, gender, nationality, race,
-religion, sexuality, or similar personal characteristic.
+environment for all, regardless of age, disability, gender, nationality,
+ethnicity, religion, sexuality, or similar personal characteristic.
 </p>
 
 <p>
diff --git a/doc/devel/release.html b/doc/devel/release.html
index 51957df..773f889 100644
--- a/doc/devel/release.html
+++ b/doc/devel/release.html
@@ -50,23 +50,11 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.1">Go
 </p>
 
 <p>
-go1.7.2 should not be used. It was tagged but not fully released.
-The release was deferred due to a last minute bug report.
-Use go1.7.3 instead, and refer to the summary of changes below.
-</p>
-
-<p>
-go1.7.3 (released 2016/10/19) includes fixes to the compiler, runtime,
+go1.7.2 (released 2016/10/17) includes fixes to the compiler, runtime,
 and the <code>crypto/cipher</code>, <code>crypto/tls</code>,
 <code>net/http</code>, and <code>strings</code> packages.
-See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.3">Go
-1.7.3 milestone</a> on our issue tracker for details.
-</p>
-
-<p>
-go1.7.4 (released 2016/12/01) includes two security fixes.
-See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.4">Go
-1.7.4 milestone</a> on our issue tracker for details.
+See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.2">Go
+1.7.2 milestone</a> on our issue tracker for details.
 </p>
 
 <h2 id="go1.6">go1.6 (released 2016/02/17)</h2>
@@ -100,13 +88,6 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.6.3">Go
 1.6.3 milestone</a> on our issue tracker for details.
 </p>
 
-<p>
-go1.6.4 (released 2016/12/01) includes two security fixes.
-It contains the same fixes as Go 1.7.4 and was released at the same time.
-See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.4">Go
-1.7.4 milestone</a> on our issue tracker for details.
-</p>
-
 <h2 id="go1.5">go1.5 (released 2015/08/19)</h2>
 
 <p>
diff --git a/doc/devel/weekly.html b/doc/devel/weekly.html
index 143727f..7166a76 100644
--- a/doc/devel/weekly.html
+++ b/doc/devel/weekly.html
@@ -2450,7 +2450,7 @@ The http package's URL parsing and query escaping code (such as ParseURL and
 URLEscape) has been moved to the new url package, with several simplifications
 to the names. Client code can be updated automatically with gofix.
 
-* asn1: support unmarshalling structs with int32 members (thanks Dave Cheney).
+* asn1: support unmarshaling structs with int32 members (thanks Dave Cheney).
 * build: allow builds without cgo or hg,
 	support versioning without hg (thanks Gustavo Niemeyer).
 * builtin: add documentation for builtins.
@@ -3030,7 +3030,7 @@ Other changes:
 * 5g: alignment fixes.
 * 6l, 8l: fix Mach-O binaries with many dynamic libraries.
 * 8l: emit resources (.rsrc) in Windows PE.  (thanks Wei Guangjing).
-* asn1: fix marshalling of empty optional RawValues (thanks Mikkel Krautz).
+* asn1: fix marshaling of empty optional RawValues (thanks Mikkel Krautz).
 * big: make Int and Rat implement fmt.Scanner (thanks Evan Shaw),
 	~8x faster number scanning,
 	remove some unnecessary conversions.
@@ -4238,7 +4238,7 @@ example: http://golang.org/pkg/xml/
 <pre>
 The json, gob, and template packages have changed, and code that uses them
 may need to be updated after this release. They will no longer read or write
-unexported struct fields. When marshalling a struct with json or gob the
+unexported struct fields. When marshaling a struct with json or gob the
 unexported fields will be silently ignored. Attempting to unmarshal json or
 gob data into an unexported field will generate an error. Accessing an
 unexported field from a template will cause the Execute function to return
@@ -5682,7 +5682,7 @@ Other changes:
 	pidigits ~10% performance win by using adds instead of shifts.
 * time: remove incorrect time.ISO8601 and add time.RFC3339 (thanks Micah Stetson).
 * utf16: add DecodeRune, EncodeRune.
-* xml: add support for XML marshalling embedded structs (thanks Raif S. Naffah),
+* xml: add support for XML marshaling embedded structs (thanks Raif S. Naffah),
 	new "innerxml" tag to collect inner XML.
 </pre>
 
@@ -5925,10 +5925,10 @@ Other changes and fixes:
 * 8a/8l: Added CMOVcc instructions (thanks Evan Shaw)
 * 8l: pe executable building code changed to include import table for kernel32.dll functions (thanks Alex Brainman)
 * 5g/6g/8g: bug fixes
-* asn1: bug fixes and additions (incl marshalling)
+* asn1: bug fixes and additions (incl marshaling)
 * build: fix build for Native Client, Linux/ARM
 * dashboard: show benchmarks, add garbage collector benchmarks
-* encoding/pem: add marshalling support
+* encoding/pem: add marshaling support
 * exp/draw: fast paths for a nil mask
 * godoc: support for directories outside $GOROOT
 * http: sort header keys when writing Response or Request to wire (thanks Petar Maymounkov)
diff --git a/doc/docs.html b/doc/docs.html
index 7eb3a3a..1ccd1f3 100644
--- a/doc/docs.html
+++ b/doc/docs.html
@@ -40,7 +40,13 @@ The first section covers basic syntax and data structures; the second discusses
 methods and interfaces; and the third introduces Go's concurrency primitives.
 Each section concludes with a few exercises so you can practice what you've
 learned. You can <a href="//tour.golang.org/">take the tour online</a> or
-<a href="//code.google.com/p/go-tour/">install it locally</a>.
+install it locally with:
+</p>
+<p>
+<pre>
+$ go get golang.org/x/tour/gotour
+</pre>
+This will place the <code>gotour</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>
diff --git a/doc/effective_go.html b/doc/effective_go.html
index f6fe48c..e3f3124 100644
--- a/doc/effective_go.html
+++ b/doc/effective_go.html
@@ -245,15 +245,15 @@ func Compile(str string) (*Regexp, error) {
 </pre>
 
 <p>
-If the name always begins the comment, the output of <code>godoc</code>
-can usefully be run through <code>grep</code>.
+If every doc comment begins with the name of the item it describes,
+the output of <code>godoc</code> can usefully be run through <code>grep</code>.
 Imagine you couldn't remember the name "Compile" but were looking for
 the parsing function for regular expressions, so you ran
 the command,
 </p>
 
 <pre>
-$ godoc regexp | grep parse
+$ godoc regexp | grep -i parse
 </pre>
 
 <p>
@@ -2409,7 +2409,7 @@ The <code>http</code> package contains this code:
 // Handler object that calls f.
 type HandlerFunc func(ResponseWriter, *Request)
 
-// ServeHTTP calls f(c, req).
+// ServeHTTP calls f(w, req).
 func (f HandlerFunc) ServeHTTP(w ResponseWriter, req *Request) {
     f(w, req)
 }
@@ -2447,7 +2447,7 @@ the handler installed at that page has value <code>ArgServer</code>
 and type <code>HandlerFunc</code>.
 The HTTP server will invoke the method <code>ServeHTTP</code>
 of that type, with <code>ArgServer</code> as the receiver, which will in turn call
-<code>ArgServer</code> (via the invocation <code>f(c, req)</code>
+<code>ArgServer</code> (via the invocation <code>f(w, req)</code>
 inside <code>HandlerFunc.ServeHTTP</code>).
 The arguments will then be displayed.
 </p>
diff --git a/doc/gccgo_contribute.html b/doc/gccgo_contribute.html
index dd1327a..1286fcc 100644
--- a/doc/gccgo_contribute.html
+++ b/doc/gccgo_contribute.html
@@ -12,7 +12,7 @@ information on building gccgo for yourself,
 see <a href="/doc/gccgo_install.html">Setting up and using gccgo</a>.
 For more of the gritty details on the process of doing development
 with the gccgo frontend,
-see <a href="https://code.google.com/p/gofrontend/source/browse/HACKING">the
+see <a href="https://go.googlesource.com/gofrontend/+/master/HACKING">the
 file HACKING</a> in the gofrontend repository.
 </p>
 
diff --git a/doc/go1.7.html b/doc/go1.7.html
index 6839c5e..2b0f01d 100644
--- a/doc/go1.7.html
+++ b/doc/go1.7.html
@@ -43,7 +43,7 @@ includes the <a href="#context">context package</a>, promoted from the
 and now used in the standard library;
 and <a href="#testing">adds support in the testing package</a> for
 creating hierarchies of tests and benchmarks.
-The release also <a href="#cmd/go">finalizes the vendoring support</a>
+The release also <a href="#cmd_go">finalizes the vendoring support</a>
 started in Go 1.5, making it a standard feature.
 </p>
 
@@ -357,7 +357,7 @@ the code generation changes alone typically reduce program CPU time by 5-35%.
 </p>
 
 <p>
-<!-- git log &#45&#45grep '-[0-9][0-9]\.[0-9][0-9]%' go1.6.. -->
+<!-- git log -''-grep '-[0-9][0-9]\.[0-9][0-9]%' go1.6.. -->
 There have been significant optimizations bringing more than 10% improvements
 to implementations in the
 <a href="/pkg/crypto/sha1/"><code>crypto/sha1</code></a>,
@@ -394,9 +394,9 @@ This allows the use of contexts for cancelation, timeouts, and passing
 request-scoped data in other standard library packages,
 including
 <a href="#net">net</a>,
-<a href="#net/http">net/http</a>,
+<a href="#net_http">net/http</a>,
 and
-<a href="#os/exec">os/exec</a>,
+<a href="#os_exec">os/exec</a>,
 as noted below.
 </p>
 
@@ -552,10 +552,9 @@ The
 
 <dd>
 <p>
-As noted above,
-there are significant performance optimizations throughout the package.
+There are many performance optimizations throughout the package.
 Decompression speed is improved by about 10%,
-while compression speed for <code>DefaultCompression</code> is roughly doubled.
+while compression for <code>DefaultCompression</code> is twice as fast.
 </p>
 
 <p>
diff --git a/doc/go1.8.html b/doc/go1.8.html
new file mode 100644
index 0000000..22176a2
--- /dev/null
+++ b/doc/go1.8.html
@@ -0,0 +1,1615 @@
+<!--{
+	"Title": "Go 1.8 Release Notes",
+	"Path":  "/doc/go1.8",
+	"Template": true
+}-->
+
+<!--
+NOTE: In this document and others in this directory, the convention is to
+set fixed-width phrases with non-fixed-width spaces, as in
+<code>hello</code> <code>world</code>.
+Do not send CLs removing the interior tags from such phrases.
+-->
+
+<style>
+ul li { margin: 0.5em 0; }
+</style>
+
+<h2 id="introduction">DRAFT RELEASE NOTES - Introduction to Go 1.8</h2>
+
+<p><strong>
+Go 1.8 is not yet released. These are work-in-progress
+release notes. Go 1.8 is expected to be released in February 2017.
+</strong></p>
+
+<p>
+The latest Go release, version 1.8, arrives six months after <a href="go1.7">Go 1.7</a>.
+Most of its changes are in the implementation of the toolchain, runtime, and libraries.
+There is one minor change to the language specification.
+As always, the release maintains the Go 1 <a href="/doc/go1compat.html">promise of compatibility</a>.
+We expect almost all Go programs to continue to compile and run as before.
+</p>
+
+<p>
+The release <a href="#ports">adds support for 32-bit MIPS</a>,
+<a href="#compiler">updates the compiler back end</a> to generate more efficient code,
+<a href="#gc">reduces GC pauses</a> by eliminating stop-the-world stack rescanning,
+<a href="#h2push">adds HTTP/2 Push support</a>,
+<a href="#http_shutdown">adds HTTP graceful shutdown</a>,
+<a href="#more_context">adds more context support</a>,
+<a href="#mutex_prof">enables profiling mutexes</a>,
+and <a href="#sort_slice">simplifies sorting slices</a>.
+</p>
+
+<h2 id="language">Changes to the language</h2>
+
+<p>
+  When explicitly converting a value from one struct type to another, as of Go 1. 8 the tags are ignored.
+  Thus two structs that differ only in their tags may be converted from one to the other:
+</p>
+
+<pre>
+func example() {
+	type T1 struct {
+		X int `json:"foo"`
+	}
+	type T2 struct {
+		X int `json:"bar"`
+	}
+	var v1 T1
+	var v2 T2
+	v1 = T1(v2) // now legal
+}
+</pre>
+
+
+<p> <!-- CL 17711 -->
+  The language specification now only requires that implementations
+  support up to 16-bit exponents in floating-point constants.  This does not affect
+  either the “<a href="/cmd/compile/"><code>gc</code></a>” or
+  <code>gccgo</code> compilers, both of
+  which still support 32-bit exponents.
+</p>
+
+<h2 id="ports">Ports</h2>
+
+<p>
+Go now supports 32-bit MIPS on Linux for both big-endian
+(<code>linux/mips</code>) and little-endian machines
+(<code>linux/mipsle</code>).
+</p>
+
+<p>
+On DragonFly BSD, Go now requires DragonFly 4.4.4 or later. <!-- CL 29491, CL 29971 -->
+</p>
+
+<p>
+The Plan 9 port's networking support is now much more complete
+and matches the behavior of Unix and Windows with respect to deadlines
+and cancelation.
+</p>
+
+<p>
+  Go 1.8 now only supports OS X 10.8 or later. This is likely the last
+  Go release to support 10.8. Compiling Go or running
+  binaries on older OS X versions is untested.
+</p>
+
+
+<h3 id="known_issues">Known Issues</h3>
+
+<p>
+There are some instabilities on FreeBSD and NetBSD that are known but not understood.
+These can lead to program crashes in rare cases.
+See
+<a href="https://golang.org/issue/15658">issue 15658</a>,
+<a href="https://golang.org/issue/16396">issue 16396</a>, and
+<a href="https://golang.org/issue/16511">issue 16511</a>.
+Any help in solving these issues would be appreciated.
+</p>
+
+<h2 id="tools">Tools</h2>
+
+<h3 id="cmd_asm">Assembler</h3>
+
+<p>
+For 64-bit x86 systems, the following instructions have been added:
+<code>VBROADCASTSD</code>,
+<code>BROADCASTSS</code>,
+<code>MOVDDUP</code>,
+<code>MOVSHDUP</code>,
+<code>MOVSLDUP</code>,
+<code>VMOVDDUP</code>,
+<code>VMOVSHDUP</code>,
+and <code>VMOVSLDUP</code>.</p>
+
+<p>
+For 64-bit PPC systems, the common vector scalar instructions have been
+added:
+<code>LXS</code>,
+<code>LXSDX</code>,
+<code>LXSI</code>,
+<code>LXSIWAX</code>,
+<code>LXSIWZX</code>,
+<code>LXV</code>,
+<code>LXVD2X</code>,
+<code>LXVDSX</code>,
+<code>LXVW4X</code>,
+<code>MFVSR</code>,
+<code>MFVSRD</code>,
+<code>MFVSRWZ</code>,
+<code>MTVSR</code>,
+<code>MTVSRD</code>,
+<code>MTVSRWA</code>,
+<code>MTVSRWZ</code>,
+<code>STXS</code>,
+<code>STXSDX</code>,
+<code>STXSI</code>,
+<code>STXSIWX</code>,
+<code>STXV</code>,
+<code>STXVD2X</code>,
+<code>STXVW4X</code>,
+<code>XSCV</code>,
+<code>XSCVDPSP</code>,
+<code>XSCVDPSPN</code>,
+<code>XSCVDPSXDS</code>,
+<code>XSCVDPSXWS</code>,
+<code>XSCVDPUXDS</code>,
+<code>XSCVDPUXWS</code>,
+<code>XSCVSPDP</code>,
+<code>XSCVSPDPN</code>,
+<code>XSCVSXDDP</code>,
+<code>XSCVSXDSP</code>,
+<code>XSCVUXDDP</code>,
+<code>XSCVUXDSP</code>,
+<code>XSCVX</code>,
+<code>XSCVXP</code>,
+<code>XVCV</code>,
+<code>XVCVDPSP</code>,
+<code>XVCVDPSXDS</code>,
+<code>XVCVDPSXWS</code>,
+<code>XVCVDPUXDS</code>,
+<code>XVCVDPUXWS</code>,
+<code>XVCVSPDP</code>,
+<code>XVCVSPSXDS</code>,
+<code>XVCVSPSXWS</code>,
+<code>XVCVSPUXDS</code>,
+<code>XVCVSPUXWS</code>,
+<code>XVCVSXDDP</code>,
+<code>XVCVSXDSP</code>,
+<code>XVCVSXWDP</code>,
+<code>XVCVSXWSP</code>,
+<code>XVCVUXDDP</code>,
+<code>XVCVUXDSP</code>,
+<code>XVCVUXWDP</code>,
+<code>XVCVUXWSP</code>,
+<code>XVCVX</code>,
+<code>XVCVXP</code>,
+<code>XXLAND</code>,
+<code>XXLANDC</code>,
+<code>XXLANDQ</code>,
+<code>XXLEQV</code>,
+<code>XXLNAND</code>,
+<code>XXLNOR</code>,
+<code>XXLOR</code>,
+<code>XXLORC</code>,
+<code>XXLORQ</code>,
+<code>XXLXOR</code>,
+<code>XXMRG</code>,
+<code>XXMRGHW</code>,
+<code>XXMRGLW</code>,
+<code>XXPERM</code>,
+<code>XXPERMDI</code>,
+<code>XXSEL</code>,
+<code>XXSI</code>,
+<code>XXSLDWI</code>,
+<code>XXSPLT</code>, and 
+<code>XXSPLTW</code>.
+</p>
+
+<h3 id="tool_yacc">Yacc</h3>
+
+<p> <!-- CL 27324, CL 27325 -->
+The <code>yacc</code> tool (previously available by running
+“<code>go</code> <code>tool</code> <code>yacc</code>”)
+has been removed. As of Go 1.7 it was no longer used by the Go compiler.
+It has moved to the “tools” repository and is now available at
+<code><a href="https://godoc.org/golang.org/x/tools/cmd/goyacc">golang.org/x/tools/cmd/goyacc</a></code>.
+</p>
+
+<h3 id="tool_fix">Fix</h3>
+
+<p> <!-- CL 28872 -->
+  The <code>fix</code> tool has a new “<code>context</code>”
+  fix to change imports from “<code>golang.org/x/net/context</code>”
+  to “<a href="/pkg/context/"><code>context</code></a>”.
+</p>
+
+<h3 id="tool_pprof">Pprof</h3>
+
+<p> <!-- CL 33157 -->
+  The <code>pprof</code> tool can now profile TLS servers
+  and skip certificate validation by using the "<code>https+insecure</code>"
+  URL scheme.
+</p>
+
+<p> <!-- CL 23781 -->
+  The callgrind output now has instruction-level granularity.
+</p>
+
+<p>
+  TODO: more. proto? standalone profiles with symbols?
+<pre>
+runtime/pprof: output CPU profiles in pprof protobuf format (CL 33071)
+runtime/pprof: write profiles in protobuf format. (CL 32257)
+</pre>
+</p>
+
+<h3 id="tool_trace">Trace</h3>
+
+<p>TODO:</p>
+<pre>
+cmd/trace: add option to output pprof files (CL 23324)
+cmd/trace: fix a runnable goroutine count bug (CL 25552)
+cmd/trace: move process-wide GC events to their own row (CL 30017)
+internal/trace: fix analysis of EvGoWaiting/EvGoInSyscall events (CL 25572)
+cmd/trace: annotate different mark worker types (CL 30702)
+</pre>
+
+<h3 id="tool_vet">Vet</h3>
+
+<p>Vet is stricter in some ways and looser where it
+  previously caused false positives.</p>
+
+<p>Vet now checks copying of array of locks,
+  duplicate JSON and XML struct field tags,
+  non-space-separated struct tags,
+  deferred calls to HTTP <code>Response.Body.Close</code>
+  before checking errors,
+  indexed arguments in <code>Printf</code>,
+  and improves existing checks.</p>
+</p>
+
+<h3 id="compiler">Compiler Toolchain</h3>
+
+<p>
+Go 1.7 introduced a new compiler back end for 64-bit x86 systems.
+In Go 1.8, that back end has been developed further and is now used for
+all architectures.
+</p>
+
+<p>
+The new back end, based on
+<a href="https://en.wikipedia.org/wiki/Static_single_assignment_form">static single assignment form</a> (SSA),
+generates more compact, more efficient code
+and provides a better platform for optimizations
+such as bounds check elimination.
+The new back end reduces the CPU time required by
+<a href="https://golang.org/test/bench/go1/">our benchmark programs</a> by 20-30%
+on 32-bit ARM systems. For 64-bit x86 systems, which already used the SSA backend in
+Go 1.7, the gains are a more modest 0-10%. Other architectures will likely
+see improvements closer to the 32-bit ARM numbers.
+</p>
+
+<p>
+  The temporary <code>-ssa=0</code> compiler flag introduced in Go 1.7
+  to disable the new backend has been removed in Go 1.8.
+</p>
+
+<p>
+  In addition to enabling the new compiler back end for all systems,
+  Go 1.8 also introduces a new compiler front end. The new compiler
+  front end should not be noticeable to users but is the foundation for
+  future performance work.
+</p>
+
+<p>
+  The compiler and linker have been optimized and run faster in this
+  release than in Go 1.7, although they are still slower than we would
+  like and will continue to be optimized in future releases.
+  Compared to the previous release, Go 1.8 is
+  <a href="https://dave.cheney.net/2016/11/19/go-1-8-toolchain-improvements">about 15% faster</a>.
+</p>
+
+<h3 id="cmd_cgo">Cgo</h3>
+
+<p> <!-- CL 29991 -->
+The environment variable <code>PKG_CONFIG</code> may now be used to
+set the program to run to handle <code>#cgo pkg-config</code>
+directives.  The default is <code>pkg-config</code>, the program
+always used by earlier releases.  This is intended to make it easier
+to cross-compile
+<a href="/cmd/cgo/">cgo</a> code.
+</p>
+
+<p> <!-- CL 32354 -->
+The <a href="/cmd/cgo/">cgo</a> tool now supports a <code>-srcdir</code>
+option, which is used by the <a href="/cmd/go/">go</a> command.
+</p>
+
+<p> <!-- CL 31768, 31811 -->
+If <a href="/cmd/cgo/">cgo</a> code calls <code>C.malloc</code>, and
+<code>malloc</code> returns <code>NULL</code>, the program will now
+crash with an out of memory error.
+<code>C.malloc</code> will never return <code>nil</code>.
+Unlike most C functions, <code>C.malloc</code> may not be used in a
+two-result form returning an errno value.
+</p>
+
+<p> <!-- CL 33237 -->
+If <a href="/cmd/cgo/">cgo</a> is used to call a C function passing a
+pointer to a C union, and if the C union can contain any pointer
+values, and if <a href="/cmd/cgo/#hdr-Passing_pointers">cgo pointer
+checking</a> is enabled (as it is by default), the union value is now
+checked for Go pointers.
+</p>
+
+<h3 id="gccgo">Gccgo</h3>
+
+<p>
+Due to the alignment of Go's semiannual release schedule with GCC's
+annual release schedule,
+GCC release 6 contains the Go 1.6.1 version of gccgo.
+We expect that the next release, GCC 7, will contain the Go 1.8
+version of gccgo.
+</p>
+
+<h3 id="gopath">Default GOPATH</h3>
+
+<p>
+  The
+  <a href="/cmd/go/#hdr-GOPATH_environment_variable"><code>GOPATH</code>
+  environment variable</a> now has a default value if it
+  is unset. It defaults to
+  <code>$HOME/go</code> on Unix and
+  <code>%USERPROFILE%/go</code> on Windows.
+</p>
+
+<h3 id="go_bug">Go bug</h3>
+
+<p>
+  The new
+  “<a href="/cmd/go/#hdr-Print_information_for_bug_reports"><code>go</code>
+   <code>bug</code></a>” command starts a bug report on GitHub, prefilled
+  with information about the current system.
+</p>
+
+<h3 id="cmd_doc">Go doc</h3>
+
+<p> <!-- CL 25419 -->
+  The
+  “<a href="/cmd/go/#hdr-Show_documentation_for_package_or_symbol"><code>go</code>
+   <code>doc</code></a>” command
+  now groups constants and variables with their type,
+  following the behavior of
+  <a href="/cmd/godoc/"><code>godoc</code></a>.
+</p>
+
+<p> <!-- CL 25420 -->
+  In order to improve the readability of <code>doc</code>'s
+  output, each summary of the first-level items is guaranteed to
+  occupy a single line.
+</p>
+
+<p> <!-- CL 31852 -->
+  Documentation for a specific method in an interface definition can
+  now be requested, as in
+  “<code>go</code> <code>doc</code> <code>net.Conn.SetDeadline</code>”.
+</p>
+
+<h3 id="plugin">Plugins</h3>
+
+<p>
+  Go now supports a “<code>plugin</code>” build mode for generating
+  plugins written in Go, and a
+  new <a href="/pkg/plugin/"><code>plugin</code></a> package for
+  loading such plugins at run time. Plugin support is only currently
+  available on Linux and macOS.
+</p>
+
+<h2 id="runtime">Runtime</h2>
+
+<h3 id="liveness">Argument Liveness</h3>
+
+<p>
+  <!-- Issue 15843 --> The garbage collector no longer considers
+  arguments live throughout the entirety of a function. For more
+  information, and for how to force a variable to remain live, see
+  the <a href="/pkg/runtime/#KeepAlive"><code>runtime.KeepAlive</code></a>
+  function added in Go 1.7.
+</p>
+
+<p>
+  <i>Updating:</i>
+  Code that sets a finalizer on an allocated object may need to add
+  calls to <code>runtime.KeepAlive</code> in functions or methods
+  using that object.
+  Read the
+  <a href="/pkg/runtime/#KeepAlive"><code>KeepAlive</code>
+  documentation</a> and its example for more details.
+</p>
+
+<h3 id="memstats">MemStats Documentation</h3>
+
+<p> <!-- CL 28972 -->
+  The runtime's <a href="/pkg/runtime/#MemStats"><code>MemStats</code></a>
+  type has been more thoroughly documented.
+</p>
+
+<h2 id="performance">Performance</h2>
+
+<p>
+As always, the changes are so general and varied that precise statements
+about performance are difficult to make.
+Most programs should run a bit faster,
+due to speedups in the garbage collector and
+optimizations in the standard library.
+</p>
+
+<p>
+There have been optimizations to implementations in the
+<a href="/pkg/bytes/"><code>bytes</code></a>,
+<a href="/pkg/crypto/aes/"><code>crypto/aes</code></a>,
+<a href="/pkg/crypto/cipher/"><code>crypto/cipher</code></a>,
+<a href="/pkg/crypto/elliptic/"><code>crypto/elliptic</code></a>,
+<a href="/pkg/crypto/sha256/"><code>crypto/sha256</code></a>,
+<a href="/pkg/crypto/sha512/"><code>crypto/sha512</code></a>,
+<a href="/pkg/encoding/asn1/"><code>encoding/asn1</code></a>,
+<a href="/pkg/encoding/csv/"><code>encoding/csv</code></a>,
+<a href="/pkg/encoding/hex/"><code>encoding/hex</code></a>,
+<a href="/pkg/encoding/json/"><code>encoding/json</code></a>,
+<a href="/pkg/hash/crc32/"><code>hash/crc32</code></a>,
+<a href="/pkg/image/color/"><code>image/color</code></a>,
+<a href="/pkg/image/draw/"><code>image/draw</code></a>,
+<a href="/pkg/math/"><code>math</code></a>,
+<a href="/pkg/math/big/"><code>math/big</code></a>,
+<a href="/pkg/reflect/"><code>reflect</code></a>,
+<a href="/pkg/regexp/"><code>regexp</code></a>,
+<a href="/pkg/runtime/"><code>runtime</code></a>,
+<a href="/pkg/strconv/"><code>strconv</code></a>,
+<a href="/pkg/strings/"><code>strings</code></a>,
+<a href="/pkg/syscall/"><code>syscall</code></a>,
+<a href="/pkg/text/template/"><code>text/template</code></a>, and
+<a href="/pkg/unicode/utf8/"><code>unicode/utf8</code></a>,
+packages.
+</p>
+
+<h3 id="gc">Garbage Collector</h3>
+
+<p>
+  Garbage collection pauses should be significantly shorter than they
+  were in Go 1.7, usually under 100 microseconds and often as low as
+  10 microseconds.
+  See the
+  <a href="https://github.com/golang/proposal/blob/master/design/17503-eliminate-rescan.md"
+     >document on eliminating stop-the-world stack re-scanning</a>
+  for details.  More work remains for Go 1.9.
+</p>
+
+<h3 id="defer">Defer</h3>
+
+<!-- CL 29656, CL 29656 -->
+<p>
+  The overhead of <a href="/ref/spec/#Defer_statements">deferred
+  function calls</a> has been reduced by about half.
+</p>
+
+<h3 id="cgoperf">Cgo</h3>
+
+<p>The overhead of calls from Go into C has been reduced by about half.</p>
+
+<h2 id="library">Standard library</h2>
+
+<h3 id="examples">Examples</h3>
+
+<p>
+Examples have been added to the documentation across many packages.
+</p>
+
+<h3 id="sort_slice">Sort</h3>
+
+<p>
+The <a href="/pkg/sort/">sort</a> package
+now includes a convenience function
+<a href="/pkg/sort/#Slice"><code>Slice</code></a> to sort a
+slice given a <em>less</em> function.
+
+In many cases this means that writing a new sorter type is not
+necessary.
+</p>
+
+<p>
+Also new are
+<a href="/pkg/sort/#SliceStable"><code>SliceStable</code></a> and
+<a href="/pkg/sort/#SliceIsSorted"><code>SliceIsSorted</code></a>.
+</p>
+
+<h3 id="h2push">HTTP/2 Push</h3>
+
+<p>
+The <a href="/pkg/net/http/">net/http</a> package now includes a
+mechanism to
+send HTTP/2 server pushes from a
+<a href="/pkg/net/http/#Handler"><code>Handler</code></a>.
+Similar to the existing <code>Flusher</code> and <code>Hijacker</code>
+interfaces, an HTTP/2
+<a href="/pkg/net/http/#ResponseWriter"><code>ResponseWriter</code></a>
+now implements the new
+<a href="/pkg/net/http/#Pusher"><code>Pusher</code></a> interface.
+</p>
+
+<h3 id="http_shutdown">HTTP Server Graceful Shutdown</h3>
+
+<p> <!-- CL 32329 -->
+  The HTTP Server now has support for graceful shutdown using the new
+  <a href="/pkg/net/http/#Server.Shutdown"><code>Server.Shutdown</code></a>
+  method and abrupt shutdown using the new
+  <a href="/pkg/net/http/#Server.Close"><code>Server.Close</code></a>
+  method.
+</p>
+
+<h3 id="more_context">More Context Support</h3>
+
+<p>
+  Continuing <a href="/doc/go1.7#context">Go 1.7's adoption</a>
+  of <a href="/pkg/context/#Context"><code>context.Context</code></a>
+  into the standard library, Go 1.8 adds more context support
+  to existing packages:
+</p>
+
+<ul>
+  <li>The new <a href="/pkg/net/http/#Server.Shutdown"><code>Server.Shutdown</code></a>
+    takes a context argument.</li>
+  <li>There have been <a href="#database_sql">significant additions</a> to the
+    <a href="/pkg/database/sql/">database/sql</a> package with context support.</li>
+  <li>The new <a href="/pkg/testing/#T.Context"><code>T.Context</code></a>
+    method in the <a href="/pkg/testing/">testing</a> package now returns a context for
+    the active test or benchmark.</li>
+  <li>All nine of the new <code>Lookup</code> methods on the new
+    <a href="/pkg/net/#Resolver"><code>net.Resolver</code></a> now
+    take a context.</li>
+  </ul>
+
+<h3 id="mutex_prof">Mutex Contention Profiling</h3>
+
+<p>
+  The runtime and tools now support profiling contended mutexes.
+</p>
+
+<p>
+  Most users will want to use the new <code>-mutexprofile</code>
+  flag with <a href="/cmd/go/#hdr-Description_of_testing_flags"><code>go</code> <code>test</code></a>,
+  and then use <a href="/cmd/pprof/">pprof</a> on the resultant file.
+</p>
+
+<p>
+  Lower-level support is also available via the new
+  <a href="/pkg/runtime/#MutexProfile"><code>MutexProfile</code></a>
+  and
+  <a href="/pkg/runtime/#SetMutexProfileFraction"><code>SetMutexProfileFraction</code></a>.
+</p>
+
+<h3 id="minor_library_changes">Minor changes to the library</h3>
+
+<p>
+As always, there are various minor changes and updates to the library,
+made with the Go 1 <a href="/doc/go1compat">promise of compatibility</a>
+in mind. The follow sections list the user visible changes and additions.
+Optimizations and bug fixes are not listed.
+</p>
+
+<dl id="archive_tar"><dt><a href="/pkg/archive/tar/">archive/tar</a></dt>
+  <dd>
+
+    <p> <!-- CL 28471, CL 31440, CL 31441, CL 31444, CL 28418, CL 31439 -->
+      The tar implementation corrects many bugs in corner cases of the file format.
+      The <a href="/pkg/archive/tar/#Reader"><code>Reader</code></a>
+      is now able to process tar files in the PAX format with entries larger than 8GB.
+      The <a href="/pkg/archive/tar/#Writer"><code>Writer</code></a>
+      no longer produces invalid tar files in some situations involving long pathnames.
+    </p>
+
+  </dd>
+</dl>
+
+<dl id="archive_zip"><dt><a href="/pkg/archive/zip/">archive/zip</a></dt>
+  <dd>
+
+    <p> <!-- CL 18274 -->
+      The zip <code>Reader</code> now supports modification times in
+      the NTFS, UNIX, and Extended Time Stamp metadata fields.
+      <!-- CL 30811 -->
+      When writing zip files, the Extended Time Stamp field is written
+      for files with non-zero modification times.
+    </p>
+
+  </dd>
+</dl>
+
+<dl id="compress_flate"><dt><a href="/pkg/compress/flate/">compress/flate</a></dt>
+  <dd>
+
+    <p> <!-- CL 31640, CL 31174, CL 32149 -->
+      There have been some minor fixes to the encoder to improve the
+      compression ratio in certain situations. As a result, the exact
+      encoded output of <code>DEFLATE</code> may be different from Go 1.7. Since
+      DEFLATE is the underlying compression of gzip, png, zlib, and zip,
+      those formats may have changed outputs.
+    </p>
+
+    <p>
+      The encoder, when operating in
+      <a href="/pkg/compress/flate/#NoCompression"><code>NoCompression</code></a>
+      mode, now produces a consistent output that is not dependent on
+      the size of the slices passed to the
+      <a href="/pkg/compress/flate/#Writer.Write"><code>Write</code></a>
+      method.
+    </p>
+
+    <p> <!-- CL 28216 -->
+      The decoder, upon encountering an error, now returns any
+      buffered data it had uncompressed along with the error.
+    </p>
+
+  </dd>
+</dl>
+
+
+<dl id="compress_gzip"><dt><a href="/pkg/compress/gzip/">compress/gzip</a></dt>
+  <dd>
+
+    <p>
+      The <a href="/pkg/compress/gzip/#Writer"><code>Writer</code></a>
+      now encodes a zero <code>MTIME</code> field when
+      the <a href="/pkg/compress/gzip/#Header"><code>Header.ModTime</code></a>
+      field is the zero value.
+
+      In previous releases of Go, the <code>Writer</code> would encode
+      a nonsensical value.
+
+      Similarly,
+      the <a href="/pkg/compress/gzip/#Reader"><code>Reader</code></a>
+      now reports a zero encoded <code>MTIME</code> field as a zero
+      <code>Header.ModTime</code>.
+    </p>
+
+  </dd>
+</dl>
+
+<dl id="context"><dt><a href="/pkg/context/">context</a></dt>
+  <dd>
+    <p> <!-- CL 30370 -->
+      The <a href="/pkg/context#DeadlineExceeded"><code>DeadlineExceeded</code></a>
+      error now implements
+      <a href="/pkg/net/#Error"><code>net.Error</code></a>
+      and reports true for both the <code>Timeout</code> and
+      <code>Temporary</code> methods.
+    </p>
+  </dd>
+</dl>
+
+<dl id="crypto_tls"><dt><a href="/pkg/crypto/tls/">crypto/tls</a></dt>
+  <dd>
+    <p> <!-- CL 25159, CL 31318 -->
+      The new method
+      <a href="/pkg/crypto/tls/#Conn.CloseWrite"><code>Conn.CloseWrite</code></a>
+      allows TLS connections to be half closed.
+    </p>
+
+    <p> <!-- CL 28075 -->
+      The new method
+      <a href="/pkg/crypto/tls/#Config.Clone"><code>Config.Clone</code></a>
+      clones a TLS configuration.
+    </p>
+
+    <p>
+      <!-- CL 30790 -->
+      The new <a href="/pkg/crypto/tls/#Config.GetConfigForClient"><code>Config.GetConfigForClient</code></a>
+      callback allows selecting a configuration for a client dynamically, based
+      on the client's
+      <a href="/pkg/crypto/tls/#ClientHelloInfo"><code>ClientHelloInfo</code></a>.
+
+      <!-- CL 31391, CL 32119 -->
+      The <a href="/pkg/crypto/tls/#ClientHelloInfo"><code>ClientHelloInfo</code></a>
+      struct now has new
+      fields <code>Conn</code>, <code>SignatureSchemes</code> (using
+      the new
+      type <a href="/kg/crypto/tls/#SignatureScheme"><code>SignatureScheme</code></a>),
+      <code>SupportedProtos</code>, and <code>SupportedVersions</code>.
+    </p>
+
+    <p> <!-- CL 32115 -->
+      The new <a href="/pkg/crypto/tls/#Config.GetClientCertificate"><code>Config.GetClientCertificate</code></a>
+      callback allows selecting a client certificate based on the server's
+      TLS <code>CertificateRequest</code> message, represented by the new
+      <a href="/pkg/crypto/tls/#CertificateRequestInfo"><code>CertificateRequestInfo</code></a>.
+    </p>
+
+    <p> <!-- CL 27434 -->
+      The new
+      <a href="/pkg/crypto/tls/#Config.KeyLogWriter"><code>Config.KeyLogWriter</code></a>
+      allows debugging TLS connections
+      in <a href="https://www.wireshark.org/">WireShark</a> and
+      similar tools.
+    </p>
+
+    <p> <!-- CL 32115 -->
+      The new
+      <a href="/pkg/crypto/tls/#Config.VerifyPeerCertificate"><code>Config.VerifyPeerCertificate</code></a>
+      callback allows additional validation of a peer's presented certificate.
+    </p>
+
+    <p> <!-- CL 18130 -->
+      The <code>crypto/tls</code> package now implements basic
+      countermeasures against CBC padding oracles. There should be
+      no explicit secret-dependent timings, but it does not attempt to
+      normalize memory accesses to prevent cache timing leaks.
+    </p>
+
+    <p>
+      The <code>crypto/tls</code> package now supports
+      X25519 and <!-- CL 30824, CL 30825 -->
+      ChaCha20-Poly1305.  <!-- CL 30957, CL 30958 -->
+      ChaCha20-Poly1305 is now prioritized unless <!-- CL 32871 -->
+      AES-GCM when hardware support is present.
+    </p>
+
+    <p> <!-- CL 27315 -->
+      AES-128-CBC cipher suites with SHA-256 are also
+      now supported.
+    </p>
+    
+  </dd>
+</dl>
+
+<dl id="crypto_x509"><dt><a href="/pkg/crypto/x509/">crypto/x509</a></dt>
+  <dd>
+    <p> <!-- CL 30578 -->
+      <a href="/pkg/crypto/x509/#SystemCertPool"><code>SystemCertPool</code></a>
+      is now implemented on Windows.
+    </p>
+
+    <p> <!-- CL 24743 -->
+      PSS signatures are now supported.
+    </p>
+
+    <p> <!-- CL 32644 -->
+      <a href="/pkg/crypto/x509/#UnknownAuthorityError"><code>UnknownAuthorityError</code></a>
+      now has a <code>Cert</code> field, reporting the untrusted
+      certificate.
+    </p>
+
+    <p>
+      Certificate validation is more permissive in a few cases and
+      stricter in a few other cases.
+    <!--
+crypto/x509: allow a leaf certificate to be specified directly as root (CL 27393)
+crypto/x509: check that the issuer name matches the issuer's subject name (CL 23571)
+crypto/x509: don't accept a root that already appears in a chain. (CL 32121)
+crypto/x509: fix name constraints handling (CL 30155)
+crypto/x509: parse all names in an RDN (CL 30810)
+crypto/x509: recognise ISO OID for RSA+SHA1 (CL 27394)
+crypto/x509: require a NULL parameters for RSA public keys (CL 16166, CL 27312)
+crypto/x509: return error for missing SerialNumber (CL 27238)
+-->
+    </p>
+
+    <p><!-- CL 30375 -->
+      Root certificates will now also be looked for
+      at <code>/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem</code>
+      on Linux, to support RHEL and CentOS.
+    </p>
+    
+  </dd>
+</dl>
+    
+<dl id="database_sql"><dt><a href="/pkg/database/sql/">database/sql</a></dt>
+  <dd>
+    <p>
+	  The package now supports <code>context.Context</code>. There are new methods
+	  ending in <code>Context</code> such as
+	  <a href="/pkg/database/sql/#DB.QueryContext"><code>DB.QueryContext</code></a> and
+	  <a href="/pkg/database/sql/#DB.PrepareContext"><code>DB.PrepareContext</code></a>
+	  that take context arguments. Using the new <code>Context</code> methods ensures that
+	  connections are closed and returned to the connection pool when the
+	  request is done; enables canceling in-progress queries
+	  should the driver support that; and allows the database
+	  pool to cancel waiting for the next available connection.
+    </p>
+    <p>
+      The <a href="/pkg/database/sql#IsolationLevel"><code>IsolationLevel</code></a>
+	  can now be set when starting a transaction by setting the isolation level
+	  on the <code>Context</code> then passing that <code>Context</code> to
+	  <a href="/pkg/database/sql#DB.BeginContext"><code>DB.BeginContext</code></a>.
+	  An error will be returned if an isolation level is selected that the driver
+	  does not support. A read-only attribute may also be set on the transaction
+	  with <a href="/pkg/database/sql/#ReadOnlyContext"><code>ReadOnlyContext</code></a>.
+	</p>
+	<p>
+      Queries now expose the SQL column type information for drivers that support it.
+	  Rows can return <a href="/pkg/database/sql#Rows.ColumnTypes"><code>ColumnTypes</code></a>
+	  which can include SQL type information, column type lengths, and the Go type.
+    </p>
+	<p>
+          A <a href="/pkg/database/sql/#Rows"><code>Rows</code></a>
+          can now represent multiple result sets. After
+	  <a href="/pkg/database/sql/#Rows.Next"><code>Rows.Next</code></a> returns false,
+	  <a href="/pkg/database/sql/#Rows.NextResultSet"><code>Rows.NextResultSet</code></a>
+	  may be called to advance to the next result set. The existing <code>Rows</code>
+	  should continue to be used after it advances to the next result set.
+    </p>
+	<p>
+	  <a href="/pkg/database/sql/#NamedParam"><code>NamedParam</code></a> may be used
+	  as query arguments. The new function <a href="/pkg/database/sql/#Param"><code>Param</code></a>
+	  helps create a <a href="/pkg/database/sql/#NamedParam"><code>NamedParam</code></a>
+	  more succinctly.
+	<p>
+          If a driver supports the new
+          <a href="/pkg/database/sql/driver/#Pinger"><code>Pinger</code></a>
+          interface, the <code>DB</code>'s
+	  <a href="/pkg/database/sql/#DB.Ping"><code>DB.Ping</code></a>
+	  and
+          <a href="/pkg/database/sql/#DB.PingContext"><code>DB.PingContext</code></a>
+          methods will use that interface to check whether a
+          database connection is still valid.
+	</p>
+    <p>
+	  The new <code>Context</code> query methods work for all drivers, but
+	  <code>Context</code> cancelation is not responsive unless the driver has been
+	  updated to use them. The other features require driver support in
+	  <a href="/pkg/database/sql/driver"><code>database/sql/driver</code></a>.
+	  Driver authors should review the new interfaces. Users of existing
+	  driver should review the driver documentation to see what
+	  it supports and any system specific documentation on each feature.
+	</p>
+  </dd>
+</dl>
+
+<dl id="debug_pe"><dt><a href="/pkg/debug/pe/">debug/pe</a></dt>
+  <dd>
+    <p> <!-- CL 22720, CL 27212, CL 22181, CL 22332, CL 22336, Issue 15345 -->
+      The package has been fleshed out and is now used by <a href="/cmd/link/">the Go linker</a>.
+      New are
+      <a href="/pkg/debug/pe/#Reloc"><code>Reloc</code></a>,
+      <a href="/pkg/debug/pe/#Section"><code>Section</code></a>,
+      <a href="/pkg/debug/pe/#StringTable"><code>StringTable</code></a>,
+      the method
+      <a href="/pkg/debug/pe/#COFFSymbol.FullName"><code>COFFSymbol.FullName</code></a>,
+      and
+      <a href="/pkg/debug/pe/#File"><code>File</code></a>
+      fields
+      <a href="/pkg/debug/pe/#File.COFFSymbols"><code>COFFSymbols</code></a> and
+      <a href="/pkg/debug/pe/#File.StringTable"><code>StringTable</code></a>.
+      </p>
+  </dd>
+</dl>
+
+<dl id="encoding_base64"><dt><a href="/pkg/encoding/base64/">encoding/base64</a></dt>
+  <dd>
+    <p> <!-- CL 24964 -->
+      The new
+      <a href="/pkg/encoding/base64/#Encoding.Strict"><code>Encoding.Strict</code></a>
+      method returns an <code>Encoding</code> that causes the decoder
+      to return an error when the trailing padding bits are not zero.
+    </p>
+  </dd>
+</dl>
+
+<dl id="encoding_binary"><dt><a href="/pkg/encoding/binary/">encoding/binary</a></dt>
+  <dd>
+    <p> <!-- CL 28514 -->
+      <a href="/pkg/encoding/binary/#Read"><code>Read</code></a>
+      and
+      <a href="/pkg/encoding/binary/#Write"><code>Write</code></a>
+      now support booleans.
+    </p>
+  </dd>
+</dl>
+
+<dl id="encoding_json"><dt><a href="/pkg/encoding/json/">encoding/json</a></dt>
+  <dd>
+
+    <p> <!-- CL 18692  -->
+      <a href="/pkg/encoding/json/#UnmarshalTypeError"><code>UnmarshalTypeError</code></a>
+      now includes the struct and field name.
+    </p>
+
+    <p> <!-- CL 31932 -->
+      A nil <a href="/pkg/encoding/json/#Marshaler"><code>Marshaler</code></a>
+      now marshals as a JSON <code>null</code> value.
+    </p>
+
+    <p> <!-- CL 21811 -->
+      A <a href="/pkg/encoding/json/#RawMessage"><code>RawMessage</code></a> value now
+      marshals the same as its pointer type.
+    </p>
+
+    <p> <!-- CL 30371 -->
+      <a href="/pkg/encoding/json/#Marshal"><code>Marshal</code></a>
+      encodes floating-point numbers using the same format as in ES6,
+      preferring decimal (not exponential) notation for a wider range of values.
+      In particular, all floating-point integers up to 2<sup>64</sup> format the
+      same as the equivalent <code>int64</code> representation.
+    </p>
+
+    <p> <!-- CL 30944 -->
+
+      In previous versions of Go, unmarshaling a JSON <code>null</code> into an
+      of <a href="/pkg/encoding/json/#Unmarshaler"><code>Unmarshaler</code></a>
+      was considered a no-op; now the <code>Unmarshaler</code>'s
+      <code>UnmarshalJSON</code> method is called with the JSON literal
+      <code>null</code> and can define the semantics of that case.
+    </p>
+
+  </dd>
+</dl>
+
+<dl id="encoding_pem"><dt><a href="/pkg/encoding/pem/">encoding/pem</a></dt>
+  <dd>
+    <p> <!-- CL 27391 -->
+      <a href="/pkg/encoding/pem/#Decode"><code>Decode</code></a>
+      is now strict about the format of the ending line.
+    </p>
+  </dd>
+</dl>
+
+<dl id="encoding_xml"><dt><a href="/pkg/encoding/xml/">encoding/xml</a></dt>
+  <dd>
+    <p> <!-- CL 30946 -->
+      <a href="/pkg/encoding/xml/#Unmarshal"><code>Unmarshal</code></a>
+      now has wildcard support for collecting all attributes using
+      the new <code>",any,attr"</code> struct tag.
+    </p>
+  </dd>
+</dl>
+
+<dl id="expvar"><dt><a href="/pkg/expvar/">expvar</a></dt>
+  <dd>
+    <p> <!-- CL 30917 -->
+      The new methods
+      <a href="/pkg/expvar/#Int.Value"><code>Int.Value</code></a>,
+      <a href="/pkg/expvar/#String.Value"><code>String.Value</code></a>,
+      <a href="/pkg/expvar/#Float.Value"><code>Float.Value</code></a>, and
+      <a href="/pkg/expvar/#Func.Value"><code>Func.Value</code></a>
+      report the current value of an exported variable.
+    </p>
+
+    <p> <!-- CL 24722 -->
+      The new
+      function <a href="/pkg/expvar/#Handler"><code>Handler</code></a>
+      returns the package's HTTP handler, to enable installing it in
+      non-standard locations.
+      </p>
+  </dd>
+</dl>
+
+<dl id="fmt"><dt><a href="/pkg/fmt/">fmt</a></dt>
+  <dd>
+    <p><!-- CL 30611 -->
+      <a href="/pkg/fmt/#Scanf"><code>Scanf</code></a>,
+      <a href="/pkg/fmt/#Fscanf"><code>Fscanf</code></a>, and
+      <a href="/pkg/fmt/#Sscanf"><code>Sscanf</code></a> now
+      handle spaces differently and more consistently than
+      previous releases. See the
+      <a href="/pkg/fmt/#hdr-Scanning">scanning documentation</a>
+      for details.
+    </p>
+  </dd>
+</dl>
+
+<dl id="go_doc"><dt><a href="/pkg/go/doc/">go/doc</a></dt>
+  <dd>
+    <p><!-- CL 29870 -->
+      The new <a href="/pkg/go/doc/#IsPredeclared"><code>IsPredeclared</code></a>
+      function reports whether a string is a predeclared identifier.
+    </p>
+  </dd>
+</dl>
+
+<dl id="go_types"><dt><a href="/pkg/go/types/">go/types</a></dt>
+  <dd>
+    <p><!-- CL 30715 -->
+      The new function
+      <a href="/pkg/go/types/#Default"><code>Default</code></a>
+      returns the default "typed" type for an "untyped" type.
+    </p>
+
+    <p><!-- CL 31939 -->
+      The alignment of <code>complex64</code> now matches
+      the <a href="/cmd/compile/">Go compiler</a>.
+    </p>
+  </dd>
+</dl>
+
+<dl id="html_template"><dt><a href="/pkg/html/template/">html/template</a></dt>
+  <dd>
+    <p><!-- CL 14336 -->
+      The package now validates
+      the <code>"type"</code> attribute on
+      a <code><script></code> tag.
+    </p>
+  </dd>
+</dl>
+
+<dl id="image_png"><dt><a href="/pkg/image/png/">image/png</a></dt>
+  <dd>
+    <p> <!-- CL 32143, CL 32140 -->
+      <a href="/pkg/image/png/#Decode"><code>Decode</code></a>
+      (and <code>DecodeConfig</code>)
+      now supports True Color and grayscale transparency.
+    </p>
+    <p> <!-- CL 29872 -->
+      <a href="/pkg/image/png/#Encoder"><code>Encoder</code></a>
+      is now faster and creates smaller output
+      when encoding paletted images.
+      </p>
+  </dd>
+</dl>
+
+<dl id="math_big"><dt><a href="/pkg/math/big/">math/big</a></dt>
+  <dd>
+    <p><!-- CL 30706 -->
+      The new method
+      <a href="/pkg/math/big/#Int.Sqrt"><code>Int.Sqrt</code></a>
+      calculates ⌊√x⌋.
+    </p>
+
+    <p>
+      The new method
+      <a href="/pkg/math/big/#Float.Scan"><code>Float.Scan</code></a>
+      is a support routine for
+      <a href="/pkg/fmt/#Scanner"><code>fmt.Scanner</code></a>.
+    </p>
+
+    <p>
+      <a href="/pkg/math/big/#Int.ModInverse"><code>Int.ModInverse</code></a>
+      now supports negative numbers.
+    </p>
+    
+  </dd>
+</dl>
+
+<dl id="math_rand"><dt><a href="/pkg/math/rand/">math/rand</a></dt>
+  <dd>
+
+    <p><!-- CL 27253, CL 33456 -->
+      The new <a href="/pkg/math/rand/#Rand.Uint64"><code>Rand.Uint64</code></a>
+      method returns <code>uint64</code> values. The
+      new <a href="/pkg/math/rand/#Source64"><code>Source64</code></a>
+      interface describes sources capable of generating such values
+      directly; otherwise the <code>Rand.Uint64</code> method
+      constructs a <code>uint64</code> from two calls
+      to <a href="/pkg/math/rand/#Source"><code>Source</code></a>'s
+      <code>Int63</code> method.
+    </p>
+
+  </dd>
+</dl>
+
+<dl id="mime"><dt><a href="/pkg/mime/">mime</a></dt>
+  <dd>
+    <p> <!-- CL 32175 -->
+    <a href="/pkg/mime/#ParseMediaType"><code>ParseMediaType</code></a>
+    now preserves unnecessary backslash escapes as literals,
+    in order to support MSIE.
+    When MSIE sends a full file path (in "intranet mode"), it does not
+    escape backslashes: <code>"C:\dev\go\foo.txt"</code>, not
+    <code>"C:\\dev\\go\\foo.txt"</code>.
+    If we see an unnecessary backslash escape, we now assume it is from MSIE
+    and intended as a literal backslash.
+    No known MIME generators emit unnecessary backslash escapes
+    for simple token characters like numbers and letters.
+    </p>
+  </dd>
+</dl>
+
+<dl id="mime_quotedprintable"><dt><a href="/pkg/mime/quotedprintable/">mime/quotedprintable</a></dt>
+  <dd>
+
+    <p>
+      The
+      <a href="/pkg/mime/quotedprintable/#Reader"><code>Reader</code></a>'s
+      parsing has been relaxed in two ways to accept
+      more input seen in the wild.
+
+      <!-- CL 32174 -->
+      First, it accepts an equals sign (<code>=</code>) not followed
+      by two hex digits as a literal equal sign.
+
+      <!-- CL 27530 -->
+      Second, it silently ignores a trailing equals sign at the end of
+      an encoded input.
+    </p>
+
+  </dd>
+</dl>
+
+<dl id="net"><dt><a href="/pkg/net/">net</a></dt>
+  <dd>
+    
+    <p><!-- CL 30164, CL 33473 -->
+      The <a href="/pkg/net/#Conn"><code>Conn</code></a> documentation
+      has been updated to clarify expectations of an interface
+      implementation. Updates in the <code>net/http</code> packages
+      depend on implementations obeying the documentation.
+    </p>
+    <p><i>Updating:</i> implementations of the <code>Conn</code> interface should verify
+      they implement the documented semantics. The
+      <a href="https://godoc.org/golang.org/x/net/nettest">golang.org/x/net/nettest</a>
+      package will exercise a <code>Conn</code> and validate it behaves properly.
+    </p>
+
+    <p><!-- CL 32099 -->
+      The new method
+      <a href="/pkg/net/#UnixListener.SetUnlinkOnClose"><code>UnixListener.SetUnlinkOnClose</code></a>
+      sets whether the underlying socket file should be removed from the file system when
+      the listener is closed.
+    </p>
+
+    <p><!-- CL 29951 -->
+      The new <a href="/pkg/net/#Buffers"><code>Buffers</code></a> types permits
+      more efficiently writing to the network from multiple discontiguous buffers
+      in memory. On certain machines, for certain types of connections,
+      this is optimized into an OS-specific batch write operation (such as <code>writev</code>).
+    </p>
+
+    <p><!-- CL 29440 -->
+      The new <a href="/pkg/net/#Resolver"><code>Resolver</code></a> looks up names and numbers
+      and supports <a href="/pkg/context/#Context"><code>context.Context</code></a>.
+      The <a href="/pkg/net/#Dialer"><code>Dialer</code></a> now has an optional
+      <a href="/pkg/net/#Dialer.Resolver"><code>Resolver</code> field</a>.
+    </p>
+
+    <p><!-- CL 29892 -->
+      <a href="/pkg/net/#Interfaces"><code>Interfaces</code></a> is now supported on Solaris.
+    </p>
+
+    <p><!-- CL 29233, CL 24901 -->
+      The Go DNS resolver now supports <code>resolv.conf</code>'s "<code>rotate</code>"
+      and "<code>option ndots:0</code>" options. The "<code>ndots</code>" option is
+      now respected in the same way as <code>libresolve</code>.
+    </p>
+
+  </dd>
+</dl>
+
+<dl id="net_http"><dt><a href="/pkg/net/http/">net/http</a></dt>
+  <dd>
+
+    <p>Server changes:</p>
+    <ul>
+      <li>The server now supports graceful shutdown support, <a href="#http_shutdown">mentioned above</a>.</li>
+
+      <li> <!-- CL 32024 -->
+        The <a href="/pkg/net/http/#Server"><code>Server</code></a>
+        adds configuration options
+        <code>ReadHeaderTimeout</code> and <code>IdleTimeout</code>
+        and documents <code>WriteTimeout</code>.
+      </li>
+
+      <li> <!-- CL 32014 -->
+        <a href="/pkg/net/http/#FileServer"><code>FileServer</code></a>
+        and
+        <a href="/pkg/net/http/#ServeContent"><code>ServeContent</code></a>
+        now support HTTP <code>If-Match</code> conditional requests,
+        in addition to the previous <code>If-None-Match</code>
+        support.
+      </li>
+    </ul>
+
+    <p>
+      There are several additions to what a server's <code>Handler</code> can do:
+    </p>
+
+    <ul>
+      <li><!-- CL 31173 -->
+        The <a href="/pkg/context/#Context"><code>Context</code></a>
+        returned
+        by <a href="/pkg/net/http/#Request.Context"><code>Request.Context</code></a>
+        is canceled if the underlying <code>net.Conn</code>
+        closes. For instance, if the user closes their browser in the
+        middle of a slow request, the <code>Handler</code> can now
+        detect that the user is gone. This complements the
+        existing <a href="/pkg/net/http/#CloseNotifier"><code>CloseNotifier</code></a>
+        support. This functionality requires that the underlying
+        <a href="/pkg/net/#Conn"><code>net.Conn</code></a> implements
+        <a href="#net">recently-clarified interface documentation</a>.
+      </li>
+
+      <li><!-- CL 32479 -->
+        To serve trailers known after the header has been written,
+        see the new
+        <a href="/pkg/net/http/#TrailerPrefix"><code>TrailerPrefix</code></a>
+        mechanism.
+      </li>
+
+      <li><!-- CL 33099 -->
+        A <code>Handler</code> can now abort a response by panicking
+        with the error
+        <a href="/pkg/net/http/#ErrAbortHandler"><code>ErrAbortHandler</code></a>.
+      </li>
+
+      <li><!-- CL 30812 -->
+        A <code>Write</code> of zero bytes to a
+        <a href="/pkg/net/http/#ResponseWriter"><code>ResponseWriter</code></a>
+        is now defined as a
+        way to test whether a <code>ResponseWriter</code> has been hijacked:
+        if so, the <code>Write</code> returns
+        <a href="/pkg/net/http/#ErrHijacked"><code>ErrHijacked</code></a>
+        without printing an error
+        to the server's error log.
+      </li>
+
+    </ul>
+
+    <p>Client & Transport changes:</p>
+    <ul>
+      <li><!-- CL 28930 -->
+        The <a href="/pkg/net/http/#Client"><code>Client</code></a>
+        now copies most request headers on redirect. See
+        <a href="/pkg/net/http/#Client">the documentation</a>
+        on the <code>Client</code> type for details.
+      </li>
+
+      <li><!-- CL 29072 -->
+        The <a href="/pkg/net/http/#Transport"><code>Transport</code></a>
+        now supports international domain names. Consequently, so do
+        <a href="/pkg/net/http/#Get">Get</a> and other helpers.
+      </li>
+
+      <li><!-- CL 31733, CL 29852 -->
+        The <code>Client</code> now supports 307 and 308 redirects.
+        If the redirect requires resending the request body,
+        the request must have the new
+        <a href="/pkg/net/http/#Request"><code>Request.GetBody</code></a>
+        field defined.
+        <a href="pkg/net/http/#NewRequest"><code>NewRequest</code></a>
+        sets <code>Request.GetBody</code> automatically for common
+        body types.
+      </li>
+
+      <li><!-- CL 32482 -->
+        The <code>Transport</code> now rejects requests for URLs with
+        ports containing non-digit characters.
+      </li>
+
+      <li><!-- CL 27117 -->
+        The <code>Transport</code> will now retry non-idempotent
+        requests if no bytes were written before a network failure.
+      </li>
+
+      <li><!-- CL 32481 -->
+        The
+        new <a href="/pkg/net/http/#Transport"><code>Transport.ProxyConnectHeader</code></a>
+        allows configuration of header values to send to a proxy
+        during a <code>CONNECT</code> request.
+      </li>
+
+      <li> <!-- CL 28077 -->
+        The <a href="/pkg/net/http/#DefaultTransport"><code>DefaultTransport.Dialer</code></a>
+        now enables <code>DualStack</code> ("<a href="https://tools.ietf.org/html/rfc6555">Happy Eyeballs</a>") support,
+        to use IPv4 as a backup if it looks like IPv6 might be
+        failing.
+      </li>
+    </ul>
+
+  </dd>
+</dl>
+
+<dl id="net_http_httptrace"><dt><a href="/pkg/net/http/httptrace/">net/http/httptrace</a></dt>
+  <dd>
+    <p> <!-- CL 30359 -->
+    There is now support for tracing a client request's TLS handshakes with
+    the new
+    <a href="/pkg/net/http/httptrace/#ClientTrace.TLSHandshakeStart"><code>ClientTrace.TLSHandshakeStart</code></a>
+    and
+    <a href="/pkg/net/http/httptrace/#ClientTrace.TLSHandshakeDone"><code>ClientTrace.TLSHandshakeDone</code></a>.
+    </p>
+  </dd>
+</dl>
+
+<dl id="net_http_httputil"><dt><a href="/pkg/net/http/httputil/">net/http/httputil</a></dt>
+  <dd>
+    <p> <!-- CL 32356 -->
+    The <a href="/pkg/net/http/httputil/#ReverseProxy"><code>ReverseProxy</code></a>
+    has a new optional hook,
+    <a href="/pkg/net/http/httputil/#ReverseProxy.ModifyResponse"><code>ModifyResponse</code></a>,
+    for modifying the response from the backend before proxying it to the client.
+    </p>
+    
+  </dd>
+</dl>
+
+<dl id="net_mail"><dt><a href="/pkg/net/mail/">net/mail</a></dt>
+  <dd>
+
+    <p> <!-- CL 32176 -->
+      Empty quoted strings are once again allowed in the name part of
+      an address. That is, Go 1.4 and earlier accepted
+      <code>"" <gopher at example.com></code>,
+      but Go 1.5 introduced a bug that rejected this address.
+      The address is recognized again.
+    </p>
+
+    <p> <!-- CL 31581 -->
+      The
+      <a href="/pkg/net/mail/#Header.Date"><code>Header.Date</code></a>
+      method has always provided a way to parse
+      the <code>Date:</code> header.
+      A new function
+      <a href="/pkg/net/mail/#ParseDate"><code>ParseDate</code></a>
+      allows parsing dates found in other
+      header lines, such as the <code>Resent-Date:</code> header.
+    </p>
+    
+  </dd>
+</dl>
+
+<dl id="net_smtp"><dt><a href="/pkg/net/smtp/">net/smtp</a></dt>
+  <dd>
+
+    <p> <!-- CL 33143 -->
+      If an implementation of
+      the <a href="/pkg/net/smtp/#Auth"><code>Auth</code></a>
+      interface's <code>Start</code> method returns an
+      empty <code>toServer</code> value, the package no longer sends
+      trailing whitespace in the SMTP <code>AUTH</code> command,
+      which some servers rejected.
+    </p>
+
+  </dd>
+</dl>
+
+<dl id="net_url"><dt><a href="/pkg/net/url/">net/url</a></dt>
+  <dd>
+
+    <p> <!-- CL 31322 --> The new functions
+      <a href="/pkg/net/url/#PathEscape"><code>PathEscape</code></a>
+      and
+      <a href="/pkg/net/url/#PathUnescape"><code>PathUnescape</code></a>
+      are similar to the query escaping and unescaping functions but
+      for path elements.</p>
+
+    <p> <!-- CL 28933 --> The new methods
+      <a href="/pkg/net/url/#URL.Hostname"><code>URL.Hostname</code></a>
+      and
+      <a href="/pkg/net/url/#URL.Port"><code>URL.Port</code></a>
+      return the hostname and port fields of a URL,
+      correctly handling the case where the port may not be present.
+    </p>
+
+    <p> <!-- CL 28343 --> The existing method
+      <a href="/pkg/net/url/#URL.ResolveReference"><code>URL.ResolveReference</code></a>
+      now properly handles paths with escaped bytes without losing
+      the escaping.
+    </p>
+
+    <p> <!-- CL 31467 -->
+      The <code>URL</code> type now implements
+      <a href="/pkg/encoding/#BinaryMarshaler"><code>encoding.BinaryMarshaler</code></a> and
+      <a href="/pkg/encoding/#BinaryUnmarshaler"><code>encoding.BinaryUnmarshaler</code></a>,
+      making it possible to process URLs in <a href="/pkg/encoding/gob/">gob data</a>.
+    </p>
+
+    <p> <!-- CL 29610, CL 31582 -->
+      Following RFC 3986,
+      <a href="/pkg/net/url/#Parse"><code>Parse</code></a>
+      now rejects URLs like <code>this_that:other/thing</code> instead of
+      interpreting them as relative paths (<code>this_that</code> is not a valid scheme).
+      To force interpretation as a relative path,
+      such URLs should be prefixed with <code>"./"</code>.
+      The <code>URL.String</code> method now inserts this prefix as needed.
+    </p>
+
+  </dd>
+</dl>
+
+<dl id="os"><dt><a href="/pkg/os/">os</a></dt>
+  <dd>
+    <p>
+      The new function
+      <a href="/pkg/os/#Executable"><code>Executable</code></a> returns
+      the path name of the running executable.
+    </p>
+
+    <p> <!-- CL 30614 -->
+      An attempt to call a method on
+      an <a href="/pkg/os/#File"><code>os.File</code></a> that has
+      already been closed will now return the new error
+      value <a href="/pkg/os/#ErrClosed"><code>os.ErrClosed</code></a>.
+      Previously it returned a system-specific error such
+      as <code>syscall.EBADF</code>.
+    </p>
+
+    <p> <!-- CL 31358 -->
+      On Unix systems, <a href="/pkg/os/#Rename"><code>os.Rename</code></a>
+      will now return an error when used to rename a directory to an
+      existing empty directory.
+      Previously it would fail when renaming to a non-empty directory
+      but succeed when renaming to an empty directory.
+      This makes the behavior on Unix correspond to that on other systems.
+    </p>
+
+    <p> <!-- CL 32451 -->
+      On Windows, long absolute paths are now transparently converted to
+      extended-length paths (paths that start with <code>\\?\</code>).
+      This permits the package to work with files whose path names are
+      longer than 260 characters.
+    </p>
+
+    <p> <!-- CL 29753 -->
+      On Windows, <a href="/pkg/os/#IsExist"><code>os.IsExist</code></a>
+      will now return <code>true</code> for the system
+      error <code>ERROR_DIR_NOT_EMPTY</code>.
+      This roughly corresponds to the existing handling of the Unix
+      error <code>ENOTEMPTY</code>.
+    </p>
+
+    <p> <!-- CL 32152 -->
+      On Plan 9, files that are not served by <code>#M</code> will now
+      have <a href="/pkg/os/#ModeDevice"><code>ModeDevice</code></a> set in
+      the value returned
+      by <a href="/pkg/os/#FileInfo"><code>FileInfo.Mode</code></a>.
+    </p>
+  </dd>
+</dl>
+
+<dl id="os_signal"><dt><a href="/pkg/os/signal/">os/signal</a></dt>
+  <dd>
+    <p> <!-- CL 32796 -->
+      In a Go library built with <code>-buildmode=c-archive</code>
+      or <code>c-shared</code>, when C code calls a Go function,
+      the <code>SIGPIPE</code> signal will be treated as usual for Go code.
+      In particular, when <code>SIGPIPE</code> is triggered by a write
+      to a closed Go network connection, it will not cause the program
+      to exit.
+    </p>
+  </dd>
+</dl>
+
+<dl id="path_filepath"><dt><a href="/pkg/path/filepath/">path/filepath</a></dt>
+  <dd>
+    <p>
+    <p>A number of bugs and corner cases on Windows were fixed:
+      <a href="/pkg/path/filepath/#Abs"><code>Abs</code></a> now calls <code>Clean</code> paths as documented,
+      <a href="/pkg/path/filepath/#Glob"><code>Glob</code></a> now matches
+      "<code>\\?\c:\*</code>",
+      <a href="/pkg/path/filepath/#EvalSymlinks"><code>EvalSymlinks</code></a> now
+      correctly handles "<code>C:.</code>", and
+      <a href="/pkg/path/filepath/#Clean"><code>Clean</code></a> now properly
+      handles a leading "<code>..</code>" in the path.
+    <p>
+
+  </dd>
+</dl>
+
+<dl id="reflect"><dt><a href="/pkg/reflect/">reflect</a></dt>
+  <dd>
+    <p> <!-- CL 30088 -->
+      The new function
+      <a href="/pkg/reflect/#Swapper"><code>Swapper</code></a> was
+      added to support <a href="#sortslice"><code>sort.Slice</code></a>.
+    </p>
+  </dd>
+</dl>
+
+<dl id="strconv"><dt><a href="/pkg/strconv/">strconv</a></dt>
+  <dd>
+    <p> <!-- CL 31210 -->
+      The <a href="/pkg/strconv/#Unquote"><code>Unquote</code></a>
+      function now strips carriage returns (<code>\r</code>) in
+      backquoted raw strings, following the
+      <a href="/ref/spec#String_literals">Go language semantics</a>.
+    </p>
+  </dd>
+</dl>
+
+<dl id="syscall"><dt><a href="/pkg/syscall/">syscall</a></dt>
+  <dd>
+    <p> <!-- CL 25050, CL 25022 -->
+      The <a href="/pkg/syscall/#Getpagesize"><code>Getpagesize</code></a>
+      now returns the system's size, rather than a constant value.
+      Previously it always returned 4KB.
+    </p>
+
+    <p> <!-- CL 31446 -->
+      The signature
+      of <a href="/pkg/syscall/#Utimes"><code>Utimes</code></a> has
+      changed on Solaris to match all the other Unix systems'
+      signature. Portable code should continue to use
+      <a href="/pkg/os/#Chtimes"><code>os.Chtimes</code></a> instead.
+    </p>
+
+    <p> <!-- CL 32319 -->
+      The <code>X__cmsg_data</code> field has been removed from
+      <a href="/pkg/syscall/#Cmsghdr"><code>Cmsghdr</code></a>.
+      </p>
+  </dd>
+</dl>
+
+<dl id="text_template"><dt><a href="/pkg/text/template/">text/template</a></dt>
+  <dd>
+    <p> <!-- CL 31462 -->
+      <a href="/pkg/text/template/#Template.Execute"><code>Template.Execute</code></a>
+      can now take a
+      <a href="/pkg/reflect/#Value"><code>reflect.Value</code></a> as its data
+      argument, and
+      <a href="/pkg/text/template/#FuncMap"><code>FuncMap</code></a>
+      functions can also accept and return <code>reflect.Value</code>.
+    </p>
+
+  </dd>
+</dl>
+
+<dl id="time"><dt><a href="/pkg/time/">time</a></dt>
+  <dd>
+
+    <p> <!-- CL 20118 --> The new function
+      <a href="/pkg/time/#Until"><code>Until</code></a> complements
+      the analogous <code>Since</code> function.
+    </p>
+
+    <p> <!-- CL 29338 -->
+      <a href="/pkg/time/#ParseDuration"><code>ParseDuration</code></a>
+      now accepts long fractional parts.
+    </p>
+
+    <p> <!-- CL 33429 -->
+      <a href="/pkg/time/#Parse"><code>Parse</code></a>
+      now rejects dates before the start of a month, such as June 0;
+      it already rejected dates beyond the end of the month, such as
+      June 31 and July 32.
+    </p>
+
+    <p> <!-- CL 33029 -->
+      The <code>tzdata</code> database has been updated to version
+      2016i for systems that don't already have a local time zone
+      database.
+    </p>
+
+    <p>
+  </dd>
+</dl>
+
+<dl id="testing"><dt><a href="/pkg/testing/">testing</a></dt>
+  <dd>
+    <p><!-- CL 29970 -->
+      The new method
+      <a href="/pkg/testing/#T.Name"><code>T.Name</code></a>
+      (and <code>B.Name</code>) returns the name of the current
+      test or benchmark.
+    </p>
+
+    <p><!-- CL 31724 -->
+      The new method
+      <a href="/pkg/testing/#T.Context"><code>T.Context</code></a>
+      (and <code>B.Context</code>) returns
+      a <a href="/pkg/context/#Context"><code>Context</code></a> for
+      the current running test or benchmark.
+    </p>
+
+    <p><!-- CL 32483 -->
+      The new function
+      <a href="/pkg/testing/#CoverMode"><code>CoverMode</code></a>
+      reports the test coverage mode.
+    </p>
+
+    <p><!-- CL 32615 -->
+      Tests and benchmarks are now marked as failed if the race
+      detector is enabled and a data race occurs during execution.
+      Previously, individual test cases would appear to pass,
+      and only the overall execution of the test binary would fail.
+    </p>
+   
+  </dd>
+</dl>
+
+<dl id="unicode"><dt><a href="/pkg/unicode/">unicode</a></dt>
+  <dd>
+    <p><!-- CL 30935 -->
+      <code>SimpleFold</code> now returns its argument unchanged
+      if the provided input was an invalid rune.
+      Previously, the implementation failed with an index bounds check panic.
+    </p>
+  </dd>
+</dl>
diff --git a/doc/go1.8.txt b/doc/go1.8.txt
new file mode 100644
index 0000000..e66ad38
--- /dev/null
+++ b/doc/go1.8.txt
@@ -0,0 +1,56 @@
+This file lists things yet to be moved into go1.8.html or deemed too
+minor to mention. Either way, delete from here when done.
+
+Tools:
+
+go: -buildmode=c-archive now builds PIC on ELF (CL 24180)
+go: mobile pkg dir change, recommend using go list in scripts (CL 24930, CL 27929)
+go, dist: can set default pkg-config tool using PKG_CONFIG env var (CL 29991)
+go: can set secure/insecure GIT schemes using GIT_ALLOW_PROTOCOL env var (CL 30135)
+
+API additions and behavior changes:
+
+cmd/compile, runtime, etc: get rid of constant FP registers (CL 28095)
+cmd/compile, runtime: add go:yeswritebarrierrec pragma (CL 30938)
+cmd/compile/internal/gc: enable new parser by default (CL 27203)
+cmd/compile/internal/syntax: fast Go syntax trees, initial commit (CL 27195)
+cmd/compile: add compiler phase timing (CL 24462)
+cmd/compile: add inline explainer (CL 22782)
+cmd/compile: enable flag-specified dump of specific phase+function (CL 23044)
+
+cmd/internal/obj, cmd/link: darwin dynlink support (CL 29393)
+cmd/internal/objfile: add ppc64/ppc64le disassembler support (CL 9682)
+cmd/link, cmd/go: delay linking of mingwex and mingw32 until very end (CL 26670)
+cmd/link: R_ADDR dynamic relocs for internal PIE (CL 29118)
+cmd/link: add trampolines for too far calls in ppc64x (CL 30850)
+cmd/link: allow internal PIE linking (CL 28543)
+cmd/link: fix -X importpath.name=value when import path needs escaping (CL 31970)
+cmd/link: fix -buildmode=pie / -linkshared combination (CL 28996)
+cmd/link: for -buildmode=exe pass -no-pie to external linker (CL 33106)
+cmd/link: insert trampolines for too-far jumps on ARM (CL 29397)
+cmd/link: non-executable stack support for Solaris (CL 24142)
+cmd/link: plugin support on darwin/amd64 (CL 29394)
+cmd/link: put text at address 0x1000000 on darwin/amd64 (CL 32185)
+cmd/link: remove the -shared flag (CL 28852)
+cmd/link: split large elf text sections on ppc64x (CL 27790)
+cmd/link: trampoline support for external linking on ARM (CL 31143)
+cmd/objdump: implement objdump of .o files (CL 24818)
+
+go/build: allow % in ${SRCDIR} expansion for Jenkins (CL 31611)
+go/build: do not record go:binary-only-package if build tags not satisfied (CL 31577)
+go/build: implement default GOPATH (CL 32019)
+
+runtime/race: update race runtime (CL 32160)
+runtime: assume 64kB physical pages on ARM (CL 25021)
+runtime: disable stack rescanning by default (CL 31766)
+runtime: don't call cgocallback from signal handler (CL 30218)
+runtime: fix check for vacuous page boundary rounding (CL 27230)
+runtime: fix map iterator concurrent map check (CL 24749)
+runtime: fix newextram PC passed to race detector (CL 29712)
+runtime: implement unconditional hybrid barrier (CL 31765)
+runtime: include pre-panic/throw logs in core dumps (CL 32013)
+runtime: limit the number of map overflow buckets (CL 25049)
+runtime: pass windows float syscall args via XMM (CL 32173)
+runtime: print sigcode on signal crash (CL 32183)
+runtime: record current PC for SIGPROF on non-Go thread (CL 30252)
+runtime: sleep on CLOCK_MONOTONIC in futexsleep1 on freebsd (CL 30154)
diff --git a/doc/go_faq.html b/doc/go_faq.html
index 5954d17..3006b3d 100644
--- a/doc/go_faq.html
+++ b/doc/go_faq.html
@@ -271,6 +271,27 @@ you will need to abide by the guidelines at
 
 <h2 id="Design">Design</h2>
 
+<h3 id="runtime">
+Does Go have a runtime?</h3>
+
+<p>
+Go does have an extensive library, called the <em>runtime</em>,
+that is part of every Go program.
+The runtime library implements garbage collection, concurrency,
+stack management, and other critical features of the Go language.
+Although it is more central to the language, Go's runtime is analogous
+to <code>libc</code>, the C library.
+</p>
+
+<p>
+It is important to understand, however, that Go's runtime does not
+include a virtual machine, such as is provided by the Java runtime.
+Go programs are compiled ahead of time to native machine code.
+Thus, although the term is often used to describe the virtual
+environment in which a program runs, in Go the word “runtime”
+is just the name given to the library providing critical language services.
+</p>
+
 <h3 id="unicode_identifiers">
 What's up with Unicode identifiers?</h3>
 
@@ -748,6 +769,29 @@ for i, v := range t {
 }
 </pre>
 
+<h3 id="convert_slice_with_same_underlying_type">
+Can I convert []T1 to []T2 if T1 and T2 have the same underlying type?</h3>
+
+This last line of this code sample does not compile.
+
+<pre>
+type T1 int
+type T2 int
+var t1 T1
+var x = T2(t1) // OK
+var st1 []T1
+var sx = ([]T2)(st1) // NOT OK
+</pre>
+
+<p>
+In Go, types are closely tied to methods, in that every named type has
+a (possibly empty) method set.
+The general rule is that you can change the name of the type being
+converted (and thus possibly change its method set) but you can't
+change the name (and method set) of elements of a composite type.
+Go requires you to be explicit about type conversions.
+</p>
+
 <h3 id="nil_error">
 Why is my nil error value not equal to nil?
 </h3>
@@ -868,6 +912,7 @@ Why does Go not have covariant result types?</h3>
 
 <p>
 Covariant result types would mean that an interface like
+</p>
 
 <pre>
 type Copyable interface {
@@ -875,13 +920,15 @@ type Copyable interface {
 }
 </pre>
 
+<p>
 would be satisfied by the method
+</p>
 
 <pre>
 func (v Value) Copy() Value
 </pre>
 
-because <code>Value</code> implements the empty interface.
+<p>because <code>Value</code> implements the empty interface.
 In Go method types must match exactly, so <code>Value</code> does not
 implement <code>Copyable</code>.
 Go separates the notion of what a
@@ -1047,7 +1094,7 @@ it's easy to work around this. For GitHub, try one of these solutions:
 <ul>
 <li>Manually clone the repository in the expected package directory:
 <pre>
-$ cd $GOPATH/src/github.com/username
+$ cd src/github.com/username
 $ git clone git at github.com:username/package.git
 </pre>
 </li>
@@ -1127,6 +1174,12 @@ struct.  If the interface value holds a pointer, copying the interface value
 makes a copy of the pointer, but again not the data it points to.
 </p>
 
+<p>
+Note that this discussion is about the semantics of the operations.
+Actual implementations may apply optimizations to avoid copying
+as long as the optimizations do not change the semantics.
+</p>
+
 <h3 id="pointer_to_interface">
 When should I use a pointer to an interface?</h3>
 
@@ -1262,11 +1315,26 @@ size of value should use an explicitly sized type, like <code>int64</code>.
 Prior to Go 1.1, the 64-bit Go compilers (both gc and gccgo) used
 a 32-bit representation for <code>int</code>. As of Go 1.1 they use
 a 64-bit representation.
+</p>
+
+<p>
 On the other hand, floating-point scalars and complex
-numbers are always sized: <code>float32</code>, <code>complex64</code>,
-etc., because programmers should be aware of precision when using
-floating-point numbers.
-The default size of a floating-point constant is <code>float64</code>.
+types are always sized (there are no <code>float</code> or <code>complex</code> basic types),
+because programmers should be aware of precision when using floating-point numbers.
+The default type used for an (untyped) floating-point constant is <code>float64</code>.
+Thus <code>foo</code> <code>:=</code> <code>3.0</code> declares a variable <code>foo</code>
+of type <code>float64</code>.
+For a <code>float32</code> variable initialized by an (untyped) constant, the variable type
+must be specified explicitly in the variable declaration:
+</p>
+
+<pre>
+var foo float32 = 3.0
+</pre>
+
+<p>
+Alternatively, the constant must be given a type with a conversion as in
+<code>foo := float32(3.0)</code>.
 </p>
 
 <h3 id="stack_or_heap">
@@ -1670,8 +1738,7 @@ What compiler technology is used to build the compilers?</h3>
 
 <p>
 <code>Gccgo</code> has a front end written in C++, with a recursive descent parser coupled to the
-standard GCC back end. <code>Gc</code> is written in Go using
-<code>yacc</code>/<code>bison</code> for the parser
+standard GCC back end. <code>Gc</code> is written in Go with a recursive descent parser
 and uses a custom loader, also written in Go but
 based on the Plan 9 loader, to generate ELF/Mach-O/PE binaries.
 </p>
@@ -1732,7 +1799,7 @@ A simple C "hello, world" program compiled and linked statically using gcc
 on Linux is around 750 kB,
 including an implementation of <code>printf</code>.
 An equivalent Go program using <code>fmt.Printf</code>
-is around 2.3 MB, but
+is around 1.5 MB, but
 that includes more powerful run-time support and type information.
 </p>
 
diff --git a/doc/go_spec.html b/doc/go_spec.html
index 731186e..5872eef 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
 <!--{
 	"Title": "The Go Programming Language Specification",
-	"Subtitle": "Version of May 31, 2016",
+	"Subtitle": "Version of November 18, 2016",
 	"Path": "/ref/spec"
 }-->
 
@@ -605,7 +605,7 @@ implementation must:
 
 	<li>Represent floating-point constants, including the parts of
 	    a complex constant, with a mantissa of at least 256 bits
-	    and a signed exponent of at least 32 bits.</li>
+	    and a signed binary exponent of at least 16 bits.</li>
 
 	<li>Give an error if unable to represent an integer constant
 	    precisely.</li>
@@ -2006,7 +2006,7 @@ _, y, _ := coord(p)  // coord() returns three values; only interested in y coord
 <p>
 Unlike regular variable declarations, a short variable declaration may <i>redeclare</i>
 variables provided they were originally declared earlier in the same block
-(or the parameter lists if the block is the function body) with the same type, 
+(or the parameter lists if the block is the function body) with the same type,
 and at least one of the non-<a href="#Blank_identifier">blank</a> variables is new.
 As a consequence, redeclaration can only appear in a multi-variable short declaration.
 Redeclaration does not introduce a new variable; it just assigns a new value to the original.
@@ -2286,8 +2286,10 @@ For array and slice literals the following rules apply:
 	<li>Each element has an associated integer index marking
 	    its position in the array.
 	</li>
-	<li>An element with a key uses the key as its index; the
-	    key must be a constant integer expression.
+	<li>An element with a key uses the key as its index. The
+	    key must be a non-negative constant representable by
+	    a value of type <code>int</code>; and if it is typed
+	    it must be of integer type.
 	</li>
 	<li>An element without a key uses the previous element's index plus one.
 	    If the first element has no key, its index is zero.
@@ -2320,7 +2322,7 @@ days := [...]string{"Sat", "Sun"}  // len(days) == 2
 
 <p>
 A slice literal describes the entire underlying array literal.
-Thus, the length and capacity of a slice literal are the maximum
+Thus the length and capacity of a slice literal are the maximum
 element index plus one. A slice literal has the form
 </p>
 
@@ -2350,10 +2352,11 @@ the <code>&T</code> when the element or key type is <code>*T</code>.
 [][]int{{1, 2, 3}, {4, 5}}          // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
 [][]Point{{{0, 1}, {1, 2}}}         // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
 map[string]Point{"orig": {0, 0}}    // same as map[string]Point{"orig": Point{0, 0}}
-
-[...]*Point{{1.5, -3.5}, {0, 0}}    // same as [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}}
-
 map[Point]string{{0, 0}: "orig"}    // same as map[Point]string{Point{0, 0}: "orig"}
+
+type PPoint *Point
+[2]*Point{{1.5, -3.5}, {}}          // same as [2]*Point{&Point{1.5, -3.5}, &Point{}}
+[2]PPoint{{1.5, -3.5}, {}}          // same as [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}
 </pre>
 
 <p>
@@ -2933,6 +2936,7 @@ used in an <a href="#Assignments">assignment</a> or initialization of the specia
 v, ok = a[x]
 v, ok := a[x]
 var v, ok = a[x]
+var v, ok T = a[x]
 </pre>
 
 <p>
@@ -3113,13 +3117,16 @@ known to be <code>T</code> in a correct program.
 </p>
 
 <pre>
-var x interface{} = 7  // x has dynamic type int and value 7
-i := x.(int)           // i has type int and value 7
+var x interface{} = 7          // x has dynamic type int and value 7
+i := x.(int)                   // i has type int and value 7
 
 type I interface { m() }
-var y I
-s := y.(string)        // illegal: string does not implement I (missing method m)
-r := y.(io.Reader)     // r has type io.Reader and y must implement both I and io.Reader
+
+func f(y I) {
+	s := y.(string)        // illegal: string does not implement I (missing method m)
+	r := y.(io.Reader)     // r has type io.Reader and the dynamic type of y must implement both I and io.Reader
+	…
+}
 </pre>
 
 <p>
@@ -3130,6 +3137,7 @@ A type assertion used in an <a href="#Assignments">assignment</a> or initializat
 v, ok = x.(T)
 v, ok := x.(T)
 var v, ok = x.(T)
+var v, ok T1 = x.(T)
 </pre>
 
 <p>
@@ -3737,6 +3745,7 @@ A receive expression used in an <a href="#Assignments">assignment</a> or initial
 x, ok = <-ch
 x, ok := <-ch
 var x, ok = <-ch
+var x, ok T = <-ch
 </pre>
 
 <p>
@@ -3834,10 +3843,12 @@ in any of these cases:
 	to <code>T</code>.
 	</li>
 	<li>
-	<code>x</code>'s type and <code>T</code> have identical
+	ignoring struct tags (see below),
+	<code>x</code>'s type and <code>T</code> have <a href="#Type_identity">identical</a>
 	<a href="#Types">underlying types</a>.
 	</li>
 	<li>
+	ignoring struct tags (see below),
 	<code>x</code>'s type and <code>T</code> are unnamed pointer types
 	and their pointer base types have identical underlying types.
 	</li>
@@ -3858,6 +3869,31 @@ in any of these cases:
 </ul>
 
 <p>
+<a href="#Struct_types">Struct tags</a> are ignored when comparing struct types
+for identity for the purpose of conversion:
+</p>
+
+<pre>
+type Person struct {
+	Name    string
+	Address *struct {
+		Street string
+		City   string
+	}
+}
+
+var data *struct {
+	Name    string `json:"name"`
+	Address *struct {
+		Street string `json:"street"`
+		City   string `json:"city"`
+	} `json:"address"`
+}
+
+var person = (*Person)(data)  // ignoring tags, the underlying types are identical
+</pre>
+
+<p>
 Specific rules apply to (non-constant) conversions between numeric types or
 to and from a string type.
 These conversions may change the representation of <code>x</code>
@@ -4687,8 +4723,8 @@ TypeList        = Type { "," Type } .
 <p>
 The TypeSwitchGuard may include a
 <a href="#Short_variable_declarations">short variable declaration</a>.
-When that form is used, the variable is declared at the beginning of
-the <a href="#Blocks">implicit block</a> in each clause.
+When that form is used, the variable is declared at the end of the
+TypeSwitchCase in the <a href="#Blocks">implicit block</a> of each clause.
 In clauses with a case listing exactly one type, the variable
 has that type; otherwise, the variable has the type of the expression
 in the TypeSwitchGuard.
@@ -4763,8 +4799,8 @@ The "fallthrough" statement is not permitted in a type switch.
 <h3 id="For_statements">For statements</h3>
 
 <p>
-A "for" statement specifies repeated execution of a block. The iteration is
-controlled by a condition, a "for" clause, or a "range" clause.
+A "for" statement specifies repeated execution of a block. There are three forms:
+The iteration may be controlled by a single condition, a "for" clause, or a "range" clause.
 </p>
 
 <pre class="ebnf">
@@ -4772,6 +4808,8 @@ ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
 Condition = Expression .
 </pre>
 
+<h4 id="For_condition">For statements with single condition</h4>
+
 <p>
 In its simplest form, a "for" statement specifies the repeated execution of
 a block as long as a boolean condition evaluates to true.
@@ -4786,6 +4824,8 @@ for a < b {
 }
 </pre>
 
+<h4 id="For_clause">For statements with <code>for</code> clause</h4>
+
 <p>
 A "for" statement with a ForClause is also controlled by its condition, but
 additionally it may specify an <i>init</i>
@@ -4824,6 +4864,8 @@ for cond { S() }    is the same as    for ; cond ; { S() }
 for      { S() }    is the same as    for true     { S() }
 </pre>
 
+<h4 id="For_range">For statements with <code>range</code> clause</h4>
+
 <p>
 A "for" statement with a "range" clause
 iterates through all entries of an array, slice, string or map,
@@ -5723,12 +5765,12 @@ var a = complex(2, -2)             // complex128
 const b = complex(1.0, -1.4)       // untyped complex constant 1 - 1.4i
 x := float32(math.Cos(math.Pi/2))  // float32
 var c64 = complex(5, -x)           // complex64
-const s uint = complex(1, 0)       // untyped complex constant 1 + 0i can be converted to uint
-_ = complex(1, 2<<s)               // illegal: 2 has floating-point type, cannot shift
+var s uint = complex(1, 0)         // untyped complex constant 1 + 0i can be converted to uint
+_ = complex(1, 2<<s)               // illegal: 2 assumes floating-point type, cannot shift
 var rl = real(c64)                 // float32
 var im = imag(a)                   // float64
 const c = imag(b)                  // untyped constant -1.4
-_ = imag(3 << s)                   // illegal: 3 has complex type, cannot shift
+_ = imag(3 << s)                   // illegal: 3 assumes complex type, cannot shift
 </pre>
 
 <h3 id="Handling_panics">Handling panics</h3>
@@ -6159,9 +6201,10 @@ func init() { … }
 </pre>
 
 <p>
-Multiple such functions may be defined, even within a single
-source file. The <code>init</code> identifier is not
-<a href="#Declarations_and_scope">declared</a> and thus
+Multiple such functions may be defined per package, even within a single
+source file. In the package block, the <code>init</code> identifier can
+be used only to declare <code>init</code> functions, yet the identifier
+itself is not <a href="#Declarations_and_scope">declared</a>. Thus
 <code>init</code> functions cannot be referred to from anywhere
 in a program.
 </p>
diff --git a/doc/install-source.html b/doc/install-source.html
index 77616c1..4a25e37 100644
--- a/doc/install-source.html
+++ b/doc/install-source.html
@@ -43,17 +43,13 @@ architectures.
 	<code>amd64</code> (also known as <code>x86-64</code>)
 </dt>
 <dd>
-	A mature implementation. New in 1.7 is its SSA-based back end
-	that generates compact, efficient code.
+	A mature implementation.
 </dd>
 <dt>
 	<code>386</code> (<code>x86</code> or <code>x86-32</code>)
 </dt>
 <dd>
-	Comparable to the <code>amd64</code> port, but does
-	not yet use the SSA-based back end. It has an effective
-	optimizer (registerizer) and generates good code (although
-	<code>gccgo</code> can do noticeably better sometimes).
+	Comparable to the <code>amd64</code> port.
 </dd>
 <dt>
 	<code>arm</code> (<code>ARM</code>)
@@ -434,7 +430,7 @@ to override the defaults.
 <ul>
 <li><code>$GOROOT</code>
 <p>
-The root of the Go tree, often <code>$HOME/go</code>.
+The root of the Go tree, often <code>$HOME/go1.X</code>.
 Its value is built into the tree when it is compiled, and
 defaults to the parent of the directory where <code>all.bash</code> was run.
 There is no need to set this unless you want to switch between multiple
@@ -459,7 +455,7 @@ These default to the values of <code>$GOHOSTOS</code> and
 
 <p>
 Choices for <code>$GOOS</code> are
-<code>darwin</code> (Mac OS X 10.7 and above and iOS), <code>dragonfly</code>, <code>freebsd</code>,
+<code>darwin</code> (Mac OS X 10.8 and above and iOS), <code>dragonfly</code>, <code>freebsd</code>,
 <code>linux</code>, <code>netbsd</code>, <code>openbsd</code>,
 <code>plan9</code>, <code>solaris</code> and <code>windows</code>.
 Choices for <code>$GOARCH</code> are
@@ -636,7 +632,7 @@ something like this:
 </p>
 
 <pre>
-export GOROOT=$HOME/go
+export GOROOT=$HOME/go1.X
 export GOARCH=amd64
 export GOOS=linux
 </pre>
diff --git a/doc/install.html b/doc/install.html
index cfe3e67..ebe66c0 100644
--- a/doc/install.html
+++ b/doc/install.html
@@ -49,12 +49,12 @@ If your OS or architecture is not on the list, you may be able to
 <tr><td colspan="3"><hr></td></tr>
 <tr><td>FreeBSD 8-STABLE or later</td> <td>amd64</td> <td>Debian GNU/kFreeBSD not supported</td></tr>
 <tr><td>Linux 2.6.23 or later with glibc</td> <td>amd64, 386, arm</td> <td>CentOS/RHEL 5.x not supported</td></tr>
-<tr><td>Mac OS X 10.7 or later</td> <td>amd64</td> <td>use the clang or gcc<sup>†</sup> that comes with Xcode<sup>‡</sup> for <code>cgo</code> support</td></tr>
+<tr><td>Mac OS X 10.8 or later</td> <td>amd64</td> <td>use the clang or gcc<sup>†</sup> that comes with Xcode<sup>‡</sup> for <code>cgo</code> support</td></tr>
 <tr><td>Windows XP or later</td> <td>amd64, 386</td> <td>use MinGW gcc<sup>†</sup>. No need for cygwin or msys.</td></tr>
 </table>
 
 <p>
-<sup>†</sup><code>gcc</code> is required only if you plan to use
+<sup>†</sup>A C compiler is required only if you plan to use
 <a href="/cmd/cgo">cgo</a>.<br/>
 <sup>‡</sup>You only need to install the command line tools for
 <a href="http://developer.apple.com/Xcode/">Xcode</a>. If you have already
@@ -117,12 +117,12 @@ to point to the directory in which it was installed.
 </p>
 
 <p>
-For example, if you installed Go to your home directory you should add the
-following commands to <code>$HOME/.profile</code>:
+For example, if you installed Go to your home directory you should add
+commands like the following to <code>$HOME/.profile</code>:
 </p>
 
 <pre>
-export GOROOT=$HOME/go
+export GOROOT=$HOME/go1.X
 export PATH=$PATH:$GOROOT/bin
 </pre>
 
@@ -219,37 +219,16 @@ and building a simple program, as follows.
 </p>
 
 <p>
-Create a directory to contain your <a href="code.html#Workspaces">workspace</a>,
-<code class="testUnix">$HOME/work</code>
-<code class="testWindows" style="display: none">C:\work</code>
-for example, and set the <code>GOPATH</code> environment
-variable to point to that location.
+Create your <a href="code.html#Workspaces">workspace</a> directory,
+<code class="testUnix">$HOME/go</code><code class="testWindows">%USERPROFILE%\go</code>.
+(If you'd like to use a different directory,
+you will need to set the <code>GOPATH</code> environment variable;
+see <a href="code.html#Workspaces">How to Write Go Code</a> for details.)
 </p>
 
-<pre class="testUnix">
-$ <b>export GOPATH=$HOME/work</b>
-</pre>
-
-<pre class="testWindows" style="display: none">
-C:\> <b>set GOPATH=C:\work</b>
-</pre>
-
 <p>
-<span class="testUnix">
-You should put the above command in your shell startup script
-(<code>$HOME/.profile</code> for example).
-</span>
-<span class="testWindows">
-On Windows, follow the <a href="#windows_env">instructions above</a> to set the
-<code>GOPATH</code> environment variable on your system.
-</span>
-</p>
-
-<p>
-Next, make the directories <code>src/github.com/user/hello</code> inside your
-workspace (if you use GitHub, substitute your user name for <code>user</code>),
-and inside the <code>hello</code> directory create a file named <code>hello.go</code>
-with the following contents:
+Next, make the directory <code>src/hello</code> inside your workspace,
+and in that directory create a file named <code>hello.go</code> that looks like:
 </p>
 
 <pre>
@@ -263,30 +242,33 @@ func main() {
 </pre>
 
 <p>
-Then compile it with the <code>go</code> tool:
+Then build it with the <code>go</code> tool:
 </p>
 
 <pre class="testUnix">
-$ <b>go install github.com/user/hello</b>
+$ <b>cd $HOME/go/src/hello
+$ <b>go build</b>
 </pre>
 
 <pre class="testWindows" style="display: none">
-C:\> <b>go install github.com/user/hello</b>
+C:\> <b>cd %USERPROFILE%\go\src\hello<b>
+C:\Users\Gopher\go\src\hello> <b>go build</b>
 </pre>
 
 <p>
-The command above will put an executable command named <code>hello</code> 
-(or <code>hello.exe</code>) inside the <code>bin</code> directory of your workspace.
-Execute the command to see the greeting:
+The command above will build an executable named
+<code class="testUnix">hello</code><code class="testWindows">hello.exe</code>
+in the directory alongside your source code.
+Execute it to see the greeting:
 </p>
 
 <pre class="testUnix">
-$ <b>$GOPATH/bin/hello</b>
+$ <b>./hello</b>
 hello, world
 </pre>
 
 <pre class="testWindows" style="display: none">
-C:\> <b>%GOPATH%\bin\hello</b>
+C:\Users\Gopher\go\src\hello> <b>hello</b>
 hello, world
 </pre>
 
@@ -295,6 +277,12 @@ If you see the "hello, world" message then your Go installation is working.
 </p>
 
 <p>
+You can run <code>go</code> <code>install</code> to install the binary into
+your workspace's <code>bin</code> directory
+or <code>go</code> <code>clean</code> to remove it.
+</p>
+
+<p>
 Before rushing off to write Go code please read the
 <a href="/doc/code.html">How to Write Go Code</a> document,
 which describes some essential concepts about using the Go tools.
diff --git a/doc/progs/json1.go b/doc/progs/json1.go
index 9e10f47..9804efb 100644
--- a/doc/progs/json1.go
+++ b/doc/progs/json1.go
@@ -28,7 +28,7 @@ func Encode() {
 
 	expected := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
 	if !reflect.DeepEqual(b, expected) {
-		log.Panicf("Error marshalling %q, expected %q, got %q.", m, expected, b)
+		log.Panicf("Error marshaling %q, expected %q, got %q.", m, expected, b)
 	}
 
 }
@@ -49,7 +49,7 @@ func Decode() {
 	}
 
 	if !reflect.DeepEqual(m, expected) {
-		log.Panicf("Error unmarshalling %q, expected %q, got %q.", b, expected, m)
+		log.Panicf("Error unmarshaling %q, expected %q, got %q.", b, expected, m)
 	}
 
 	m = Message{
@@ -77,7 +77,7 @@ func PartialDecode() {
 	}
 
 	if !reflect.DeepEqual(expected, m) {
-		log.Panicf("Error unmarshalling %q, expected %q, got %q.", b, expected, m)
+		log.Panicf("Error unmarshaling %q, expected %q, got %q.", b, expected, m)
 	}
 }
 
diff --git a/doc/progs/json3.go b/doc/progs/json3.go
index a04fdfa..442c155 100644
--- a/doc/progs/json3.go
+++ b/doc/progs/json3.go
@@ -33,7 +33,7 @@ func Decode() {
 	}
 
 	if !reflect.DeepEqual(f, expected) {
-		log.Panicf("Error unmarshalling %q, expected %q, got %q", b, expected, f)
+		log.Panicf("Error unmarshaling %q, expected %q, got %q", b, expected, f)
 	}
 
 	f = map[string]interface{}{
diff --git a/doc/progs/json4.go b/doc/progs/json4.go
index 4926302..1c7e5b4 100644
--- a/doc/progs/json4.go
+++ b/doc/progs/json4.go
@@ -36,7 +36,7 @@ func Decode() {
 	}
 
 	if !reflect.DeepEqual(expected, m) {
-		log.Panicf("Error unmarshalling %q, expected %q, got %q", b, expected, m)
+		log.Panicf("Error unmarshaling %q, expected %q, got %q", b, expected, m)
 	}
 }
 
diff --git a/lib/time/update.bash b/lib/time/update.bash
index e4987bb..b70788e 100755
--- a/lib/time/update.bash
+++ b/lib/time/update.bash
@@ -8,8 +8,8 @@
 # Consult http://www.iana.org/time-zones for the latest versions.
 
 # Versions to use.
-CODE=2016f
-DATA=2016f
+CODE=2016i
+DATA=2016i
 
 set -e
 rm -rf work
diff --git a/lib/time/zoneinfo.zip b/lib/time/zoneinfo.zip
index bbb8e86..e12d6dc 100644
Binary files a/lib/time/zoneinfo.zip and b/lib/time/zoneinfo.zip differ
diff --git a/misc/cgo/errors/issue16591.go b/misc/cgo/errors/issue16591.go
new file mode 100644
index 0000000..10eb840
--- /dev/null
+++ b/misc/cgo/errors/issue16591.go
@@ -0,0 +1,17 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 16591: Test that we detect an invalid call that was being
+// hidden by a type conversion inserted by cgo checking.
+
+package p
+
+// void f(int** p) { }
+import "C"
+
+type x *C.int
+
+func F(p *x) {
+	C.f(p) // ERROR HERE
+}
diff --git a/misc/cgo/errors/malloc.go b/misc/cgo/errors/malloc.go
new file mode 100644
index 0000000..65da020
--- /dev/null
+++ b/misc/cgo/errors/malloc.go
@@ -0,0 +1,34 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that C.malloc does not return nil.
+
+package main
+
+// #include <stdlib.h>
+import "C"
+
+import (
+	"fmt"
+	"runtime"
+)
+
+func main() {
+	var size C.size_t
+	size--
+
+	// The Dragonfly libc succeeds when asked to allocate
+	// 0xffffffffffffffff bytes, so pass a different value that
+	// causes it to fail.
+	if runtime.GOOS == "dragonfly" {
+		size = C.size_t(0x7fffffff << (32 * (^uintptr(0) >> 63)))
+	}
+
+	p := C.malloc(size)
+	if p == nil {
+		fmt.Println("malloc: C.malloc returned nil")
+		// Just exit normally--the test script expects this
+		// program to crash, so exiting normally indicates failure.
+	}
+}
diff --git a/misc/cgo/errors/ptr.go b/misc/cgo/errors/ptr.go
index e39f041..4dafbdf 100644
--- a/misc/cgo/errors/ptr.go
+++ b/misc/cgo/errors/ptr.go
@@ -322,6 +322,27 @@ var ptrTests = []ptrTest{
 		body: `p := &C.s{}; defer C.f(p); p.p = new(C.int)`,
 		fail: true,
 	},
+	{
+		// Check a pointer to a union if the union has any
+		// pointer fields.
+		name:    "union1",
+		c:       `typedef union { char **p; unsigned long i; } u; void f(u *pu) {}`,
+		imports: []string{"unsafe"},
+		body:    `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`,
+		fail:    true,
+	},
+	{
+		// Don't check a pointer to a union if the union does
+		// not have any pointer fields.
+		// Like ptrdata1 above, the uintptr represents an
+		// integer that happens to have the same
+		// representation as a pointer.
+		name:    "union2",
+		c:       `typedef union { unsigned long i; } u; void f(u *pu) {}`,
+		imports: []string{"unsafe"},
+		body:    `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`,
+		fail:    false,
+	},
 }
 
 func main() {
diff --git a/misc/cgo/errors/test.bash b/misc/cgo/errors/test.bash
index 84d44d8..05261e9 100755
--- a/misc/cgo/errors/test.bash
+++ b/misc/cgo/errors/test.bash
@@ -46,6 +46,7 @@ check issue13423.go
 expect issue13635.go C.uchar C.schar C.ushort C.uint C.ulong C.longlong C.ulonglong C.complexfloat C.complexdouble
 check issue13830.go
 check issue16116.go
+check issue16591.go
 
 if ! go build issue14669.go; then
 	exit 1
@@ -58,5 +59,15 @@ if ! go run ptr.go; then
 	exit 1
 fi
 
+# The malloc.go test should crash.
+rm -f malloc.out
+if go run malloc.go >malloc.out 2>&1; then
+	echo '`go run malloc.go` succeeded unexpectedly'
+	cat malloc.out
+	rm -f malloc.out
+	exit 1
+fi
+rm -f malloc.out
+
 rm -rf errs _obj
 exit 0
diff --git a/misc/cgo/test/api.go b/misc/cgo/test/api.go
index b4ae3dd..d2b09cb 100644
--- a/misc/cgo/test/api.go
+++ b/misc/cgo/test/api.go
@@ -7,6 +7,11 @@
 package cgotest
 
 // #include <stdlib.h>
+//
+// // Test for issue 17723.
+// typedef char *cstring_pointer;
+// static void cstring_pointer_fun(cstring_pointer dummy) { }
+//
 // const char *api_hello = "hello!";
 import "C"
 import "unsafe"
@@ -21,4 +26,5 @@ func testAPI() {
 	var b []byte
 	b = C.GoBytes(unsafe.Pointer(C.api_hello), C.int(6))
 	_, _ = s, b
+	C.cstring_pointer_fun(nil)
 }
diff --git a/misc/cgo/test/basic.go b/misc/cgo/test/basic.go
index 2189af6..3ceb4ce 100644
--- a/misc/cgo/test/basic.go
+++ b/misc/cgo/test/basic.go
@@ -162,3 +162,6 @@ func testUnsignedInt(t *testing.T) {
 func sliceOperands(array [2000]int) {
 	_ = array[C.KILO:C.KILO:C.KILO] // no type error
 }
+
+// set in cgo_thread_lock.go init
+var testThreadLockFunc = func(*testing.T) {}
diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go
index 21d1df5..b88bf13 100644
--- a/misc/cgo/test/callback.go
+++ b/misc/cgo/test/callback.go
@@ -186,6 +186,7 @@ func testCallbackCallers(t *testing.T) {
 		"runtime.asmcgocall",
 		"runtime.cgocall",
 		"test._Cfunc_callback",
+		"test.nestedCall.func1",
 		"test.nestedCall",
 		"test.testCallbackCallers",
 		"test.TestCallbackCallers",
diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go
index 8a95b02..a6de999 100644
--- a/misc/cgo/test/cgo_test.go
+++ b/misc/cgo/test/cgo_test.go
@@ -70,5 +70,11 @@ func Test12030(t *testing.T)                 { test12030(t) }
 func TestGCC68255(t *testing.T)              { testGCC68255(t) }
 func TestCallGoWithString(t *testing.T)      { testCallGoWithString(t) }
 func Test14838(t *testing.T)                 { test14838(t) }
+func Test8756(t *testing.T)                  { test8756(t) }
+func Test17065(t *testing.T)                 { test17065(t) }
+func TestThreadLock(t *testing.T)            { testThreadLockFunc(t) }
+func TestCheckConst(t *testing.T)            { testCheckConst(t) }
+func Test17537(t *testing.T)                 { test17537(t) }
+func Test18126(t *testing.T)                 { test18126(t) }
 
 func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
diff --git a/misc/cgo/test/cgo_thread_lock.go b/misc/cgo/test/cgo_thread_lock.go
new file mode 100644
index 0000000..b105068
--- /dev/null
+++ b/misc/cgo/test/cgo_thread_lock.go
@@ -0,0 +1,53 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,freebsd,openbsd
+
+package cgotest
+
+/*
+#include <unistd.h>
+#include <sys/syscall.h>
+void Gosched(void);
+static int Ctid(void) { Gosched(); return syscall(SYS_gettid); }
+*/
+import "C"
+
+import (
+	"runtime"
+	"syscall"
+	"testing"
+	"time"
+)
+
+//export Gosched
+func Gosched() {
+	runtime.Gosched()
+}
+
+func init() {
+	testThreadLockFunc = testThreadLock
+}
+
+func testThreadLock(t *testing.T) {
+	stop := make(chan int)
+	go func() {
+		// We need the G continue running,
+		// so the M has a chance to run this G.
+		for {
+			select {
+			case <-stop:
+				return
+			case <-time.After(time.Millisecond * 100):
+			}
+		}
+	}()
+	defer close(stop)
+
+	for i := 0; i < 1000; i++ {
+		if C.int(syscall.Gettid()) != C.Ctid() {
+			t.Fatalf("cgo has not locked OS thread")
+		}
+	}
+}
diff --git a/misc/cgo/test/checkconst.go b/misc/cgo/test/checkconst.go
new file mode 100644
index 0000000..0160c1e
--- /dev/null
+++ b/misc/cgo/test/checkconst.go
@@ -0,0 +1,33 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test a constant in conjunction with pointer checking.
+
+package cgotest
+
+/*
+#include <stdlib.h>
+
+#define CheckConstVal 0
+
+typedef struct {
+	int *p;
+} CheckConstStruct;
+
+static void CheckConstFunc(CheckConstStruct *p, int e) {
+}
+*/
+import "C"
+
+import (
+	"testing"
+	"unsafe"
+)
+
+func testCheckConst(t *testing.T) {
+	// The test is that this compiles successfully.
+	p := C.malloc(C.size_t(unsafe.Sizeof(C.int(0))))
+	defer C.free(p)
+	C.CheckConstFunc(&C.CheckConstStruct{(*C.int)(p)}, C.CheckConstVal)
+}
diff --git a/misc/cgo/test/complex.go b/misc/cgo/test/complex.go
new file mode 100644
index 0000000..ca0a97d
--- /dev/null
+++ b/misc/cgo/test/complex.go
@@ -0,0 +1,24 @@
+// Copyright 2016 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+/*
+struct {
+	float x;
+	_Complex float y;
+} cplxAlign = { 3.14, 2.17 };
+*/
+import "C"
+
+import "testing"
+
+func TestComplexAlign(t *testing.T) {
+	if C.cplxAlign.x != 3.14 {
+		t.Errorf("got %v, expected 3.14", C.cplxAlign.x)
+	}
+	if C.cplxAlign.y != 2.17 {
+		t.Errorf("got %v, expected 2.17", C.cplxAlign.y)
+	}
+}
diff --git a/misc/cgo/test/issue17065.go b/misc/cgo/test/issue17065.go
new file mode 100644
index 0000000..ede30bc
--- /dev/null
+++ b/misc/cgo/test/issue17065.go
@@ -0,0 +1,29 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+/*
+// Test that C symbols larger than a page play nicely with the race detector.
+// See issue 17065.
+
+int ii[65537];
+*/
+import "C"
+
+import (
+	"runtime"
+	"testing"
+)
+
+var sink C.int
+
+func test17065(t *testing.T) {
+	if runtime.GOOS == "darwin" {
+		t.Skip("broken on darwin; issue 17065")
+	}
+	for i := range C.ii {
+		sink = C.ii[i]
+	}
+}
diff --git a/misc/cgo/test/issue17537.go b/misc/cgo/test/issue17537.go
new file mode 100644
index 0000000..debdbfe
--- /dev/null
+++ b/misc/cgo/test/issue17537.go
@@ -0,0 +1,42 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 17537.  The void* cast introduced by cgo to avoid problems
+// with const/volatile qualifiers breaks C preprocessor macros that
+// emulate functions.
+
+package cgotest
+
+/*
+#include <stdlib.h>
+
+typedef struct {
+	int i;
+} S17537;
+
+int I17537(S17537 *p);
+
+#define I17537(p) ((p)->i)
+
+// Calling this function used to fail without the cast.
+const int F17537(const char **p) {
+	return **p;
+}
+*/
+import "C"
+
+import "testing"
+
+func test17537(t *testing.T) {
+	v := C.S17537{i: 17537}
+	if got, want := C.I17537(&v), C.int(17537); got != want {
+		t.Errorf("got %d, want %d", got, want)
+	}
+
+	p := (*C.char)(C.malloc(1))
+	*p = 17
+	if got, want := C.F17537(&p), C.int(17); got != want {
+		t.Errorf("got %d, want %d", got, want)
+	}
+}
diff --git a/misc/cgo/test/issue18126.go b/misc/cgo/test/issue18126.go
new file mode 100644
index 0000000..ac94a66
--- /dev/null
+++ b/misc/cgo/test/issue18126.go
@@ -0,0 +1,26 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 18126: cgo check of void function returning errno.
+
+package cgotest
+
+/*
+#include <stdlib.h>
+
+void Issue18126C(void **p) {
+}
+*/
+import "C"
+
+import (
+	"testing"
+)
+
+func test18126(t *testing.T) {
+	p := C.malloc(1)
+	_, err := C.Issue18126C(&p)
+	C.free(p)
+	_ = err
+}
diff --git a/misc/cgo/test/issue7978.go b/misc/cgo/test/issue7978.go
index e4cbf1d..7fb62e8 100644
--- a/misc/cgo/test/issue7978.go
+++ b/misc/cgo/test/issue7978.go
@@ -88,9 +88,20 @@ func issue7978wait(store uint32, wait uint32) {
 
 //export issue7978cb
 func issue7978cb() {
+	// Force a stack growth from the callback to put extra
+	// pressure on the runtime. See issue #17785.
+	growStack(64)
 	issue7978wait(3, 4)
 }
 
+func growStack(n int) int {
+	var buf [128]int
+	if n == 0 {
+		return 0
+	}
+	return buf[growStack(n-1)]
+}
+
 func issue7978go() {
 	C.issue7978c((*C.uint32_t)(&issue7978sync))
 	issue7978wait(7, 8)
diff --git a/misc/cgo/test/issue8756.go b/misc/cgo/test/issue8756.go
new file mode 100644
index 0000000..d8ee3b8
--- /dev/null
+++ b/misc/cgo/test/issue8756.go
@@ -0,0 +1,17 @@
+package cgotest
+
+/*
+#cgo LDFLAGS: -lm
+#include <math.h>
+*/
+import "C"
+import (
+	"testing"
+
+	"./issue8756"
+)
+
+func test8756(t *testing.T) {
+	issue8756.Pow()
+	C.pow(1, 2)
+}
diff --git a/misc/cgo/test/issue8756/issue8756.go b/misc/cgo/test/issue8756/issue8756.go
new file mode 100644
index 0000000..5f6b777
--- /dev/null
+++ b/misc/cgo/test/issue8756/issue8756.go
@@ -0,0 +1,11 @@
+package issue8756
+
+/*
+#cgo LDFLAGS: -lm
+#include <math.h>
+*/
+import "C"
+
+func Pow() {
+	C.pow(1, 2)
+}
diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go
index ab14c00..3c768a0 100644
--- a/misc/cgo/testcarchive/carchive_test.go
+++ b/misc/cgo/testcarchive/carchive_test.go
@@ -6,6 +6,7 @@ package carchive_test
 
 import (
 	"bufio"
+	"debug/elf"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -34,13 +35,9 @@ var GOOS, GOARCH string
 var libgodir string
 
 func init() {
-	bin = []string{"./testp"}
 	GOOS = goEnv("GOOS")
 	GOARCH = goEnv("GOARCH")
-	execScript := "go_" + GOOS + "_" + GOARCH + "_exec"
-	if executor, err := exec.LookPath(execScript); err == nil {
-		bin = []string{executor, "./testp"}
-	}
+	bin = cmdToRun("./testp")
 
 	ccOut := goEnv("CC")
 	cc = []string{string(ccOut)}
@@ -84,8 +81,13 @@ func init() {
 		cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
 	}
 	libgodir = GOOS + "_" + GOARCH
-	if GOOS == "darwin" && (GOARCH == "arm" || GOARCH == "arm64") {
-		libgodir = GOOS + "_" + GOARCH + "_shared"
+	switch GOOS {
+	case "darwin":
+		if GOARCH == "arm" || GOARCH == "arm64" {
+			libgodir += "_shared"
+		}
+	case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
+		libgodir += "_shared"
 	}
 	cc = append(cc, "-I", filepath.Join("pkg", libgodir))
 
@@ -120,81 +122,62 @@ func goEnv(key string) string {
 	return strings.TrimSpace(string(out))
 }
 
-func compilemain(t *testing.T, libgo string) {
-	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main.c")
-	if GOOS == "windows" {
-		ccArgs = append(ccArgs, "main_windows.c", libgo, "-lntdll", "-lws2_32", "-lwinmm")
-	} else {
-		ccArgs = append(ccArgs, "main_unix.c", libgo)
-	}
-	t.Log(ccArgs)
-
-	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
-		t.Logf("%s", out)
-		t.Fatal(err)
+func cmdToRun(name string) []string {
+	execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec"
+	executor, err := exec.LookPath(execScript)
+	if err != nil {
+		return []string{name}
 	}
+	return []string{executor, name}
 }
 
-func TestInstall(t *testing.T) {
-	defer func() {
-		os.Remove("libgo.a")
-		os.Remove("libgo.h")
-		os.Remove("testp")
-		os.RemoveAll("pkg")
-	}()
-
-	cmd := exec.Command("go", "install", "-buildmode=c-archive", "libgo")
+func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
+	cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
 	cmd.Env = gopathEnv
 	if out, err := cmd.CombinedOutput(); err != nil {
 		t.Logf("%s", out)
 		t.Fatal(err)
 	}
+	defer func() {
+		os.Remove(libgoa)
+		os.Remove(libgoh)
+	}()
 
-	compilemain(t, filepath.Join("pkg", libgodir, "libgo.a"))
-
-	binArgs := append(bin, "arg1", "arg2")
-	if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil {
-		t.Logf("%s", out)
-		t.Fatal(err)
+	ccArgs := append(cc, "-o", exe, "main.c")
+	if GOOS == "windows" {
+		ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm")
+	} else {
+		ccArgs = append(ccArgs, "main_unix.c", libgoa)
 	}
-
-	os.Remove("libgo.a")
-	os.Remove("libgo.h")
-	os.Remove("testp")
-
-	// Test building libgo other than installing it.
-	// Header files are now present.
-	cmd = exec.Command("go", "build", "-buildmode=c-archive", filepath.Join("src", "libgo", "libgo.go"))
-	cmd.Env = gopathEnv
-	if out, err := cmd.CombinedOutput(); err != nil {
+	t.Log(ccArgs)
+	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
 		t.Logf("%s", out)
 		t.Fatal(err)
 	}
+	defer os.Remove(exe)
 
-	compilemain(t, "libgo.a")
-
+	binArgs := append(cmdToRun(exe), "arg1", "arg2")
 	if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil {
 		t.Logf("%s", out)
 		t.Fatal(err)
 	}
+}
 
-	os.Remove("libgo.a")
-	os.Remove("libgo.h")
-	os.Remove("testp")
+func TestInstall(t *testing.T) {
+	defer os.RemoveAll("pkg")
 
-	cmd = exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo.a", "libgo")
-	cmd.Env = gopathEnv
-	if out, err := cmd.CombinedOutput(); err != nil {
-		t.Logf("%s", out)
-		t.Fatal(err)
-	}
+	testInstall(t, "./testp1"+exeSuffix,
+		filepath.Join("pkg", libgodir, "libgo.a"),
+		filepath.Join("pkg", libgodir, "libgo.h"),
+		"go", "install", "-buildmode=c-archive", "libgo")
 
-	compilemain(t, "libgo.a")
+	// Test building libgo other than installing it.
+	// Header files are now present.
+	testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h",
+		"go", "build", "-buildmode=c-archive", filepath.Join("src", "libgo", "libgo.go"))
 
-	if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil {
-		t.Logf("%s", out)
-		t.Fatal(err)
-	}
+	testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h",
+		"go", "build", "-buildmode=c-archive", "-o", "libgo.a", "libgo")
 }
 
 func TestEarlySignalHandler(t *testing.T) {
@@ -282,6 +265,25 @@ func TestSignalForwarding(t *testing.T) {
 		t.Logf("%s", out)
 		t.Errorf("got %v; expected SIGSEGV", ee)
 	}
+
+	// Test SIGPIPE forwarding
+	cmd = exec.Command(bin[0], append(bin[1:], "3")...)
+
+	out, err = cmd.CombinedOutput()
+
+	if err == nil {
+		t.Logf("%s", out)
+		t.Error("test program succeeded unexpectedly")
+	} else if ee, ok := err.(*exec.ExitError); !ok {
+		t.Logf("%s", out)
+		t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
+	} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
+		t.Logf("%s", out)
+		t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
+	} else if !ws.Signaled() || ws.Signal() != syscall.SIGPIPE {
+		t.Logf("%s", out)
+		t.Errorf("got %v; expected SIGPIPE", ee)
+	}
 }
 
 func TestSignalForwardingExternal(t *testing.T) {
@@ -487,3 +489,71 @@ func TestExtar(t *testing.T) {
 		}
 	}
 }
+
+func TestPIE(t *testing.T) {
+	switch GOOS {
+	case "windows", "darwin", "plan9":
+		t.Skipf("skipping PIE test on %s", GOOS)
+	}
+
+	defer func() {
+		os.Remove("testp" + exeSuffix)
+		os.RemoveAll("pkg")
+	}()
+
+	cmd := exec.Command("go", "install", "-buildmode=c-archive", "libgo")
+	cmd.Env = gopathEnv
+	if out, err := cmd.CombinedOutput(); err != nil {
+		t.Logf("%s", out)
+		t.Fatal(err)
+	}
+
+	ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", filepath.Join("pkg", libgodir, "libgo.a"))
+	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
+		t.Logf("%s", out)
+		t.Fatal(err)
+	}
+
+	binArgs := append(bin, "arg1", "arg2")
+	if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil {
+		t.Logf("%s", out)
+		t.Fatal(err)
+	}
+
+	f, err := elf.Open("testp" + exeSuffix)
+	if err != nil {
+		t.Fatal("elf.Open failed: ", err)
+	}
+	defer f.Close()
+	if hasDynTag(t, f, elf.DT_TEXTREL) {
+		t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix)
+	}
+}
+
+func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool {
+	ds := f.SectionByType(elf.SHT_DYNAMIC)
+	if ds == nil {
+		t.Error("no SHT_DYNAMIC section")
+		return false
+	}
+	d, err := ds.Data()
+	if err != nil {
+		t.Errorf("can't read SHT_DYNAMIC contents: %v", err)
+		return false
+	}
+	for len(d) > 0 {
+		var t elf.DynTag
+		switch f.Class {
+		case elf.ELFCLASS32:
+			t = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
+			d = d[8:]
+		case elf.ELFCLASS64:
+			t = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
+			d = d[16:]
+		}
+		if t == tag {
+			return true
+		}
+	}
+	return false
+}
diff --git a/misc/cgo/testcarchive/main2.c b/misc/cgo/testcarchive/main2.c
index 3726977..55625c5 100644
--- a/misc/cgo/testcarchive/main2.c
+++ b/misc/cgo/testcarchive/main2.c
@@ -7,14 +7,17 @@
 
 #include <setjmp.h>
 #include <signal.h>
+#include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <sched.h>
 #include <time.h>
+#include <errno.h>
 
 #include "libgo2.h"
 
@@ -24,6 +27,7 @@ static void die(const char* msg) {
 }
 
 static volatile sig_atomic_t sigioSeen;
+static volatile sig_atomic_t sigpipeSeen;
 
 // Use up some stack space.
 static void recur(int i, char *p) {
@@ -36,6 +40,11 @@ static void recur(int i, char *p) {
 }
 
 // Signal handler that uses up more stack space than a goroutine will have.
+static void pipeHandler(int signo, siginfo_t* info, void* ctxt) {
+	sigpipeSeen = 1;
+}
+
+// Signal handler that uses up more stack space than a goroutine will have.
 static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
 	char a[1024];
 
@@ -46,11 +55,22 @@ static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
 static jmp_buf jmp;
 static char* nullPointer;
 
+// An arbitrary function which requires proper stack alignment; see
+// http://golang.org/issue/17641.
+static void callWithVarargs(void* dummy, ...) {
+	va_list args;
+	va_start(args, dummy);
+	va_end(args);
+}
+
 // Signal handler for SIGSEGV on a C thread.
 static void segvHandler(int signo, siginfo_t* info, void* ctxt) {
 	sigset_t mask;
 	int i;
 
+	// Call an arbitrary function that requires the stack to be properly aligned.
+	callWithVarargs("dummy arg", 3.1415);
+
 	if (sigemptyset(&mask) < 0) {
 		die("sigemptyset");
 	}
@@ -93,12 +113,17 @@ static void init() {
 		die("sigaction");
 	}
 
+	sa.sa_sigaction = pipeHandler;
+	if (sigaction(SIGPIPE, &sa, NULL) < 0) {
+		die("sigaction");
+	}
 }
 
 int main(int argc, char** argv) {
 	int verbose;
 	sigset_t mask;
 	int i;
+	struct timespec ts;
 
 	verbose = argc > 1;
 	setvbuf(stdout, NULL, _IONBF, 0);
@@ -148,12 +173,35 @@ int main(int argc, char** argv) {
 	// Wait until the signal has been delivered.
 	i = 0;
 	while (!sigioSeen) {
-		if (sched_yield() < 0) {
-			perror("sched_yield");
+		ts.tv_sec = 0;
+		ts.tv_nsec = 1000000;
+		nanosleep(&ts, NULL);
+		i++;
+		if (i > 5000) {
+			fprintf(stderr, "looping too long waiting for SIGIO\n");
+			exit(EXIT_FAILURE);
 		}
+	}
+
+	if (verbose) {
+		printf("provoking SIGPIPE\n");
+	}
+
+	GoRaiseSIGPIPE();
+
+	if (verbose) {
+		printf("waiting for sigpipeSeen\n");
+	}
+
+	// Wait until the signal has been delivered.
+	i = 0;
+	while (!sigpipeSeen) {
+		ts.tv_sec = 0;
+		ts.tv_nsec = 1000000;
+		nanosleep(&ts, NULL);
 		i++;
-		if (i > 100000) {
-			fprintf(stderr, "looping too long waiting for signal\n");
+		if (i > 1000) {
+			fprintf(stderr, "looping too long waiting for SIGPIPE\n");
 			exit(EXIT_FAILURE);
 		}
 	}
diff --git a/misc/cgo/testcarchive/main3.c b/misc/cgo/testcarchive/main3.c
index 11046d0..07d5d1e 100644
--- a/misc/cgo/testcarchive/main3.c
+++ b/misc/cgo/testcarchive/main3.c
@@ -9,6 +9,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <sched.h>
 
 #include "libgo3.h"
@@ -28,11 +29,19 @@ int main(int argc, char** argv) {
 	int verbose;
 	struct sigaction sa;
 	int i;
+	struct timespec ts;
 
 	verbose = argc > 2;
 	setvbuf(stdout, NULL, _IONBF, 0);
 
 	if (verbose) {
+		printf("raising SIGPIPE\n");
+	}
+
+	// Test that the Go runtime handles SIGPIPE.
+	ProvokeSIGPIPE();
+
+	if (verbose) {
 		printf("calling sigaction\n");
 	}
 
@@ -64,11 +73,11 @@ int main(int argc, char** argv) {
 	// Wait until the signal has been delivered.
 	i = 0;
 	while (!sigioSeen) {
-		if (sched_yield() < 0) {
-			perror("sched_yield");
-		}
+		ts.tv_sec = 0;
+		ts.tv_nsec = 1000000;
+		nanosleep(&ts, NULL);
 		i++;
-		if (i > 100000) {
+		if (i > 5000) {
 			fprintf(stderr, "looping too long waiting for signal\n");
 			exit(EXIT_FAILURE);
 		}
@@ -138,11 +147,11 @@ int main(int argc, char** argv) {
 	// Wait until the signal has been delivered.
 	i = 0;
 	while (!sigioSeen) {
-		if (sched_yield() < 0) {
-			perror("sched_yield");
-		}
+		ts.tv_sec = 0;
+		ts.tv_nsec = 1000000;
+		nanosleep(&ts, NULL);
 		i++;
-		if (i > 100000) {
+		if (i > 5000) {
 			fprintf(stderr, "looping too long waiting for signal\n");
 			exit(EXIT_FAILURE);
 		}
diff --git a/misc/cgo/testcarchive/main4.c b/misc/cgo/testcarchive/main4.c
index 353f980..4fd55e7 100644
--- a/misc/cgo/testcarchive/main4.c
+++ b/misc/cgo/testcarchive/main4.c
@@ -8,6 +8,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <sched.h>
 #include <pthread.h>
 
@@ -48,6 +49,7 @@ static void* thread1(void* arg __attribute__ ((unused))) {
 	stack_t ss;
 	int i;
 	stack_t nss;
+	struct timespec ts;
 
 	// Set up an alternate signal stack for this thread.
 	memset(&ss, 0, sizeof ss);
@@ -73,11 +75,11 @@ static void* thread1(void* arg __attribute__ ((unused))) {
 	// Wait until the signal has been delivered.
 	i = 0;
 	while (SIGIOCount() == 0) {
-		if (sched_yield() < 0) {
-			perror("sched_yield");
-		}
+		ts.tv_sec = 0;
+		ts.tv_nsec = 1000000;
+		nanosleep(&ts, NULL);
 		i++;
-		if (i > 100000) {
+		if (i > 5000) {
 			fprintf(stderr, "looping too long waiting for signal\n");
 			exit(EXIT_FAILURE);
 		}
@@ -105,6 +107,7 @@ static void* thread2(void* arg __attribute__ ((unused))) {
 	int i;
 	int oldcount;
 	pthread_t tid;
+	struct timespec ts;
 	stack_t nss;
 
 	// Set up an alternate signal stack for this thread.
@@ -129,11 +132,11 @@ static void* thread2(void* arg __attribute__ ((unused))) {
 	// Wait until the signal has been delivered.
 	i = 0;
 	while (SIGIOCount() == oldcount) {
-		if (sched_yield() < 0) {
-			perror("sched_yield");
-		}
+		ts.tv_sec = 0;
+		ts.tv_nsec = 1000000;
+		nanosleep(&ts, NULL);
 		i++;
-		if (i > 100000) {
+		if (i > 5000) {
 			fprintf(stderr, "looping too long waiting for signal\n");
 			exit(EXIT_FAILURE);
 		}
diff --git a/misc/cgo/testcarchive/main5.c b/misc/cgo/testcarchive/main5.c
index 9fadf08..2437bf0 100644
--- a/misc/cgo/testcarchive/main5.c
+++ b/misc/cgo/testcarchive/main5.c
@@ -68,6 +68,24 @@ int main(int argc, char** argv) {
 
 			break;
 		}
+		case 3: {
+			if (verbose) {
+				printf("attempting SIGPIPE\n");
+			}
+
+			int fd[2];
+			if (pipe(fd) != 0) {
+				printf("pipe(2) failed\n");
+				return 0;
+			}
+			// Close the reading end.
+			close(fd[0]);
+			// Expect that write(2) fails (EPIPE)
+			if (write(fd[1], "some data", 9) != -1) {
+				printf("write(2) unexpectedly succeeded\n");
+				return 0;
+			}
+		}
 		default:
 			printf("Unknown test: %d\n", test);
 			return 0;
diff --git a/misc/cgo/testcarchive/src/libgo2/libgo2.go b/misc/cgo/testcarchive/src/libgo2/libgo2.go
index fbed493..19c8e1a 100644
--- a/misc/cgo/testcarchive/src/libgo2/libgo2.go
+++ b/misc/cgo/testcarchive/src/libgo2/libgo2.go
@@ -4,6 +4,30 @@
 
 package main
 
+/*
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+// Raise SIGPIPE.
+static void CRaiseSIGPIPE() {
+	int fds[2];
+
+	if (pipe(fds) == -1) {
+		perror("pipe");
+		exit(EXIT_FAILURE);
+	}
+	// Close the reader end
+	close(fds[0]);
+	// Write to the writer end to provoke a SIGPIPE
+	if (write(fds[1], "some data", 9) != -1) {
+		fprintf(stderr, "write to a closed pipe succeeded\n");
+		exit(EXIT_FAILURE);
+	}
+	close(fds[1]);
+}
+*/
 import "C"
 
 import (
@@ -46,5 +70,11 @@ func TestSEGV() {
 func Noop() {
 }
 
+// Raise SIGPIPE.
+//export GoRaiseSIGPIPE
+func GoRaiseSIGPIPE() {
+	C.CRaiseSIGPIPE()
+}
+
 func main() {
 }
diff --git a/misc/cgo/testcarchive/src/libgo3/libgo3.go b/misc/cgo/testcarchive/src/libgo3/libgo3.go
index 94e5d21..19fcc7f 100644
--- a/misc/cgo/testcarchive/src/libgo3/libgo3.go
+++ b/misc/cgo/testcarchive/src/libgo3/libgo3.go
@@ -40,5 +40,17 @@ func SawSIGIO() C.int {
 	}
 }
 
+// ProvokeSIGPIPE provokes a kernel-initiated SIGPIPE
+//export ProvokeSIGPIPE
+func ProvokeSIGPIPE() {
+	r, w, err := os.Pipe()
+	if err != nil {
+		panic(err)
+	}
+	r.Close()
+	defer w.Close()
+	w.Write([]byte("some data"))
+}
+
 func main() {
 }
diff --git a/misc/cgo/testcshared/main4.c b/misc/cgo/testcshared/main4.c
index fd7b5b3..355cdef 100644
--- a/misc/cgo/testcshared/main4.c
+++ b/misc/cgo/testcshared/main4.c
@@ -77,6 +77,7 @@ int main(int argc, char** argv) {
 	void (*fn)(void);
 	sigset_t mask;
 	int i;
+	struct timespec ts;
 
 	verbose = argc > 2;
 	setvbuf(stdout, NULL, _IONBF, 0);
@@ -166,11 +167,11 @@ int main(int argc, char** argv) {
 	// Wait until the signal has been delivered.
 	i = 0;
 	while (!sigioSeen) {
-		if (sched_yield() < 0) {
-			perror("sched_yield");
-		}
+		ts.tv_sec = 0;
+		ts.tv_nsec = 1000000;
+		nanosleep(&ts, NULL);
 		i++;
-		if (i > 100000) {
+		if (i > 5000) {
 			fprintf(stderr, "looping too long waiting for signal\n");
 			exit(EXIT_FAILURE);
 		}
diff --git a/misc/cgo/testcshared/main5.c b/misc/cgo/testcshared/main5.c
index 97a258f..1bc9910 100644
--- a/misc/cgo/testcshared/main5.c
+++ b/misc/cgo/testcshared/main5.c
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <sched.h>
 #include <dlfcn.h>
 
@@ -31,6 +32,7 @@ int main(int argc, char** argv) {
 	void (*fn1)(void);
 	int (*sawSIGIO)(void);
 	int i;
+	struct timespec ts;
 
 	verbose = argc > 2;
 	setvbuf(stdout, NULL, _IONBF, 0);
@@ -77,11 +79,11 @@ int main(int argc, char** argv) {
 	// Wait until the signal has been delivered.
 	i = 0;
 	while (!sigioSeen) {
-		if (sched_yield() < 0) {
-			perror("sched_yield");
-		}
+		ts.tv_sec = 0;
+		ts.tv_nsec = 1000000;
+		nanosleep(&ts, NULL);
 		i++;
-		if (i > 100000) {
+		if (i > 5000) {
 			fprintf(stderr, "looping too long waiting for signal\n");
 			exit(EXIT_FAILURE);
 		}
@@ -182,11 +184,11 @@ int main(int argc, char** argv) {
 	// Wait until the signal has been delivered.
 	i = 0;
 	while (!sigioSeen) {
-		if (sched_yield() < 0) {
-			perror("sched_yield");
-		}
+		ts.tv_sec = 0;
+		ts.tv_nsec = 1000000;
+		nanosleep(&ts, NULL);
 		i++;
-		if (i > 100000) {
+		if (i > 5000) {
 			fprintf(stderr, "looping too long waiting for signal\n");
 			exit(EXIT_FAILURE);
 		}
diff --git a/misc/cgo/testcshared/test.bash b/misc/cgo/testcshared/test.bash
index e4bb7d3..052ee0e 100755
--- a/misc/cgo/testcshared/test.bash
+++ b/misc/cgo/testcshared/test.bash
@@ -105,7 +105,7 @@ status=0
 
 # test0: exported symbols in shared lib are accessible.
 # TODO(iant): using _shared here shouldn't really be necessary.
-$(go env CC) ${GOGCCFLAGS} -I ${installdir} -o testp main0.c libgo.$libext
+$(go env CC) ${GOGCCFLAGS} -I ${installdir} -o testp main0.c ./libgo.$libext
 binpush testp
 
 output=$(run LD_LIBRARY_PATH=. ./testp)
diff --git a/misc/cgo/testgodefs/test.bash b/misc/cgo/testgodefs/test.bash
index 14235c0..a82ff93 100755
--- a/misc/cgo/testgodefs/test.bash
+++ b/misc/cgo/testgodefs/test.bash
@@ -12,7 +12,7 @@ FILE_PREFIXES="anonunion issue8478"
 RM=
 for FP in $FILE_PREFIXES
 do
-  go tool cgo -godefs ${FP}.go > ${FP}_defs.go
+  go tool cgo -godefs -srcdir . ${FP}.go > ${FP}_defs.go
   RM="${RM} ${FP}_defs.go"
 done
 
diff --git a/misc/cgo/testplugin/altpath/src/common/common.go b/misc/cgo/testplugin/altpath/src/common/common.go
new file mode 100644
index 0000000..505ba02
--- /dev/null
+++ b/misc/cgo/testplugin/altpath/src/common/common.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package common
+
+var X int
+
+func init() {
+	X = 4
+}
diff --git a/misc/cgo/testplugin/altpath/src/plugin-mismatch/main.go b/misc/cgo/testplugin/altpath/src/plugin-mismatch/main.go
new file mode 100644
index 0000000..8aacafc
--- /dev/null
+++ b/misc/cgo/testplugin/altpath/src/plugin-mismatch/main.go
@@ -0,0 +1,17 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// // No C code required.
+import "C"
+
+// The common package imported here does not match the common package
+// imported by plugin1. A program that attempts to load plugin1 and
+// plugin-mismatch should produce an error.
+import "common"
+
+func ReadCommonX() int {
+	return common.X
+}
diff --git a/misc/cgo/testplugin/src/common/common.go b/misc/cgo/testplugin/src/common/common.go
new file mode 100644
index 0000000..b064e6b
--- /dev/null
+++ b/misc/cgo/testplugin/src/common/common.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package common
+
+var X int
+
+func init() {
+	X = 3
+}
diff --git a/misc/cgo/testplugin/src/host/host.go b/misc/cgo/testplugin/src/host/host.go
new file mode 100644
index 0000000..898f44e
--- /dev/null
+++ b/misc/cgo/testplugin/src/host/host.go
@@ -0,0 +1,148 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"fmt"
+	"log"
+	"path/filepath"
+	"plugin"
+	"strings"
+
+	"common"
+)
+
+func init() {
+	common.X *= 5
+}
+
+// testUnnamed tests that two plugins built with .go files passed on
+// the command line do not have overlapping symbols. That is,
+// unnamed1.so/FuncInt and unnamed2.so/FuncInt should be distinct functions.
+func testUnnamed() {
+	p, err := plugin.Open("unnamed1.so")
+	if err != nil {
+		log.Fatalf(`plugin.Open("unnamed1.so"): %v`, err)
+	}
+	fn, err := p.Lookup("FuncInt")
+	if err != nil {
+		log.Fatalf(`unnamed1.so: Lookup("FuncInt") failed: %v`, err)
+	}
+	if got, want := fn.(func() int)(), 1; got != want {
+		log.Fatalf("unnamed1.so: FuncInt()=%d, want %d", got, want)
+	}
+
+	p, err = plugin.Open("unnamed2.so")
+	if err != nil {
+		log.Fatalf(`plugin.Open("unnamed2.so"): %v`, err)
+	}
+	fn, err = p.Lookup("FuncInt")
+	if err != nil {
+		log.Fatalf(`unnamed2.so: Lookup("FuncInt") failed: %v`, err)
+	}
+	if got, want := fn.(func() int)(), 2; got != want {
+		log.Fatalf("unnamed2.so: FuncInt()=%d, want %d", got, want)
+	}
+}
+
+func main() {
+	if got, want := common.X, 3*5; got != want {
+		log.Fatalf("before plugin load common.X=%d, want %d", got, want)
+	}
+
+	p, err := plugin.Open("plugin1.so")
+	if err != nil {
+		log.Fatalf("plugin.Open failed: %v", err)
+	}
+
+	const wantX = 3 * 5 * 7
+	if got := common.X; got != wantX {
+		log.Fatalf("after plugin load common.X=%d, want %d", got, wantX)
+	}
+
+	seven, err := p.Lookup("Seven")
+	if err != nil {
+		log.Fatalf(`Lookup("Seven") failed: %v`, err)
+	}
+	if got, want := *seven.(*int), 7; got != want {
+		log.Fatalf("plugin1.Seven=%d, want %d", got, want)
+	}
+
+	readFunc, err := p.Lookup("ReadCommonX")
+	if err != nil {
+		log.Fatalf(`plugin1.Lookup("ReadCommonX") failed: %v`, err)
+	}
+	if got := readFunc.(func() int)(); got != wantX {
+		log.Fatalf("plugin1.ReadCommonX()=%d, want %d", got, wantX)
+	}
+
+	// sub/plugin1.so is a different plugin with the same name as
+	// the already loaded plugin. It also depends on common. Test
+	// that we can load the different plugin, it is actually
+	// different, and that it sees the same common package.
+	subpPath, err := filepath.Abs("sub/plugin1.so")
+	if err != nil {
+		log.Fatalf("filepath.Abs(%q) failed: %v", subpPath, err)
+	}
+	subp, err := plugin.Open(subpPath)
+	if err != nil {
+		log.Fatalf("plugin.Open(%q) failed: %v", subpPath, err)
+	}
+
+	funcVar, err := subp.Lookup("FuncVar")
+	if err != nil {
+		log.Fatalf(`sub/plugin1.Lookup("FuncVar") failed: %v`, err)
+	}
+	called := false
+	*funcVar.(*func()) = func() {
+		called = true
+	}
+
+	readFunc, err = subp.Lookup("ReadCommonX")
+	if err != nil {
+		log.Fatalf(`sub/plugin1.Lookup("ReadCommonX") failed: %v`, err)
+	}
+	if got := readFunc.(func() int)(); got != wantX {
+		log.Fatalf("sub/plugin1.ReadCommonX()=%d, want %d", got, wantX)
+	}
+	if !called {
+		log.Fatal("calling ReadCommonX did not call FuncVar")
+	}
+
+	subf, err := subp.Lookup("F")
+	if err != nil {
+		log.Fatalf(`sub/plugin1.Lookup("F") failed: %v`, err)
+	}
+	if gotf := subf.(func() int)(); gotf != 17 {
+		log.Fatalf(`sub/plugin1.F()=%d, want 17`, gotf)
+	}
+	f, err := p.Lookup("F")
+	if err != nil {
+		log.Fatalf(`plugin1.Lookup("F") failed: %v`, err)
+	}
+	if gotf := f.(func() int)(); gotf != 3 {
+		log.Fatalf(`plugin1.F()=%d, want 17`, gotf)
+	}
+
+	// plugin2 has no exported symbols, only an init function.
+	if _, err := plugin.Open("plugin2.so"); err != nil {
+		log.Fatalf("plugin.Open failed: %v", err)
+	}
+	if got, want := common.X, 2; got != want {
+		log.Fatalf("after loading plugin2, common.X=%d, want %d", got, want)
+	}
+
+	_, err = plugin.Open("plugin-mismatch.so")
+	if err == nil {
+		log.Fatal(`plugin.Open("plugin-mismatch.so"): should have failed`)
+	}
+	if s := err.Error(); !strings.Contains(s, "different version") {
+		log.Fatalf(`plugin.Open("plugin-mismatch.so"): error does not mention "different version": %v`, s)
+	}
+
+	testUnnamed()
+
+	fmt.Println("PASS")
+}
diff --git a/misc/cgo/testplugin/src/plugin1/plugin1.go b/misc/cgo/testplugin/src/plugin1/plugin1.go
new file mode 100644
index 0000000..7a62242
--- /dev/null
+++ b/misc/cgo/testplugin/src/plugin1/plugin1.go
@@ -0,0 +1,35 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// // No C code required.
+import "C"
+
+import "common"
+
+func F() int { return 3 }
+
+func ReadCommonX() int {
+	return common.X
+}
+
+var Seven int
+
+func call(fn func()) {
+	fn()
+}
+
+func g() {
+	common.X *= Seven
+}
+
+func init() {
+	Seven = 7
+	call(g)
+}
+
+func main() {
+	panic("plugin1.main called")
+}
diff --git a/misc/cgo/testplugin/src/plugin2/plugin2.go b/misc/cgo/testplugin/src/plugin2/plugin2.go
new file mode 100644
index 0000000..6c23a5e
--- /dev/null
+++ b/misc/cgo/testplugin/src/plugin2/plugin2.go
@@ -0,0 +1,18 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// // No C code required.
+import "C"
+
+import "common"
+
+func init() {
+	common.X = 2
+}
+
+func main() {
+	panic("plugin1.main called")
+}
diff --git a/misc/cgo/testplugin/src/sub/plugin1/plugin1.go b/misc/cgo/testplugin/src/sub/plugin1/plugin1.go
new file mode 100644
index 0000000..cf9000c
--- /dev/null
+++ b/misc/cgo/testplugin/src/sub/plugin1/plugin1.go
@@ -0,0 +1,23 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// // No C code required.
+import "C"
+
+import "common"
+
+func F() int { return 17 }
+
+var FuncVar = func() {}
+
+func ReadCommonX() int {
+	FuncVar()
+	return common.X
+}
+
+func main() {
+	panic("plugin1.main called")
+}
diff --git a/misc/cgo/testplugin/test.bash b/misc/cgo/testplugin/test.bash
new file mode 100755
index 0000000..fee99a7
--- /dev/null
+++ b/misc/cgo/testplugin/test.bash
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+# Copyright 2016 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+set -e
+
+if [ ! -f src/host/host.go ]; then
+	cwd=$(pwd)
+	echo "misc/cgo/testplugin/test.bash is running in $cwd" 1>&2
+	exit 1
+fi
+
+goos=$(go env GOOS)
+goarch=$(go env GOARCH)
+
+function cleanup() {
+	rm -f plugin*.so unnamed*.so
+	rm -rf host pkg sub
+}
+trap cleanup EXIT
+
+rm -rf pkg sub
+mkdir sub
+
+GOPATH=$(pwd) go build -buildmode=plugin plugin1
+GOPATH=$(pwd) go build -buildmode=plugin plugin2
+GOPATH=$(pwd)/altpath go build -buildmode=plugin plugin-mismatch
+GOPATH=$(pwd) go build -buildmode=plugin -o=sub/plugin1.so sub/plugin1
+GOPATH=$(pwd) go build -buildmode=plugin unnamed1.go
+GOPATH=$(pwd) go build -buildmode=plugin unnamed2.go
+GOPATH=$(pwd) go build host
+
+LD_LIBRARY_PATH=$(pwd) ./host
diff --git a/misc/cgo/testplugin/unnamed1.go b/misc/cgo/testplugin/unnamed1.go
new file mode 100644
index 0000000..102edaf
--- /dev/null
+++ b/misc/cgo/testplugin/unnamed1.go
@@ -0,0 +1,12 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// // No C code required.
+import "C"
+
+func FuncInt() int { return 1 }
+
+func main() {}
diff --git a/misc/cgo/testplugin/unnamed2.go b/misc/cgo/testplugin/unnamed2.go
new file mode 100644
index 0000000..55070d5
--- /dev/null
+++ b/misc/cgo/testplugin/unnamed2.go
@@ -0,0 +1,12 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// // No C code required.
+import "C"
+
+func FuncInt() int { return 2 }
+
+func main() {}
diff --git a/misc/cgo/testsanitizers/msan5.go b/misc/cgo/testsanitizers/msan5.go
new file mode 100644
index 0000000..f1479eb
--- /dev/null
+++ b/misc/cgo/testsanitizers/msan5.go
@@ -0,0 +1,57 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Using reflect to set a value was not seen by msan.
+
+/*
+#include <stdlib.h>
+
+extern void Go1(int*);
+extern void Go2(char*);
+
+// Use weak as a hack to permit defining a function even though we use export.
+void C1() __attribute__ ((weak));
+void C2() __attribute__ ((weak));
+
+void C1() {
+	int i;
+	Go1(&i);
+	if (i != 42) {
+		abort();
+	}
+}
+
+void C2() {
+	char a[2];
+	a[1] = 42;
+	Go2(a);
+	if (a[0] != 42) {
+		abort();
+	}
+}
+*/
+import "C"
+
+import (
+	"reflect"
+	"unsafe"
+)
+
+//export Go1
+func Go1(p *C.int) {
+	reflect.ValueOf(p).Elem().Set(reflect.ValueOf(C.int(42)))
+}
+
+//export Go2
+func Go2(p *C.char) {
+	a := (*[2]byte)(unsafe.Pointer(p))
+	reflect.Copy(reflect.ValueOf(a[:1]), reflect.ValueOf(a[1:]))
+}
+
+func main() {
+	C.C1()
+	C.C2()
+}
diff --git a/misc/cgo/testsanitizers/test.bash b/misc/cgo/testsanitizers/test.bash
index 78747d1..01cce95 100755
--- a/misc/cgo/testsanitizers/test.bash
+++ b/misc/cgo/testsanitizers/test.bash
@@ -15,6 +15,11 @@ if test -x "$(type -p clang)"; then
 fi
 export CC
 
+if [ "$(sysctl -n vm.overcommit_memory)" = 2 ]; then
+  echo "skipping msan/tsan tests: vm.overcommit_memory=2" >&2
+  exit 0
+fi
+
 msan=yes
 
 TMPDIR=${TMPDIR:-/tmp}
@@ -88,6 +93,11 @@ if test "$msan" = "yes"; then
 	status=1
     fi
 
+    if ! go run -msan msan5.go; then
+	echo "FAIL: msan5"
+	status=1
+    fi
+
     if go run -msan msan_fail.go 2>/dev/null; then
 	echo "FAIL: msan_fail"
 	status=1
@@ -134,6 +144,7 @@ if test "$tsan" = "yes"; then
     testtsan tsan2.go
     testtsan tsan3.go
     testtsan tsan4.go
+    testtsan tsan8.go
 
     # These tests are only reliable using clang or GCC version 7 or later.
     # Otherwise runtime/cgo/libcgo.h can't tell whether TSAN is in use.
@@ -156,6 +167,9 @@ if test "$tsan" = "yes"; then
 
 	# This test requires rebuilding runtime/cgo with -fsanitize=thread.
 	testtsan tsan6.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan"
+
+	# This test requires rebuilding runtime/cgo with -fsanitize=thread.
+	testtsan tsan7.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan"
     fi
 fi
 
diff --git a/misc/cgo/testsanitizers/tsan7.go b/misc/cgo/testsanitizers/tsan7.go
new file mode 100644
index 0000000..2fb9e45
--- /dev/null
+++ b/misc/cgo/testsanitizers/tsan7.go
@@ -0,0 +1,40 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Setting an environment variable in a cgo program changes the C
+// environment. Test that this does not confuse the race detector.
+
+/*
+#cgo CFLAGS: -fsanitize=thread
+#cgo LDFLAGS: -fsanitize=thread
+*/
+import "C"
+
+import (
+	"fmt"
+	"os"
+	"sync"
+	"time"
+)
+
+func main() {
+	var wg sync.WaitGroup
+	var mu sync.Mutex
+	f := func() {
+		defer wg.Done()
+		for i := 0; i < 100; i++ {
+			time.Sleep(time.Microsecond)
+			mu.Lock()
+			s := fmt.Sprint(i)
+			os.Setenv("TSAN_TEST"+s, s)
+			mu.Unlock()
+		}
+	}
+	wg.Add(2)
+	go f()
+	go f()
+	wg.Wait()
+}
diff --git a/misc/cgo/testsanitizers/tsan8.go b/misc/cgo/testsanitizers/tsan8.go
new file mode 100644
index 0000000..88d82a6
--- /dev/null
+++ b/misc/cgo/testsanitizers/tsan8.go
@@ -0,0 +1,60 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program failed when run under the C/C++ ThreadSanitizer.  The TSAN
+// sigaction function interceptor returned SIG_DFL instead of the Go runtime's
+// handler in registerSegvForwarder.
+
+/*
+#cgo CFLAGS: -fsanitize=thread
+#cgo LDFLAGS: -fsanitize=thread
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct sigaction prev_sa;
+
+void forwardSignal(int signo, siginfo_t *info, void *context) {
+	// One of sa_sigaction and/or sa_handler
+	if ((prev_sa.sa_flags&SA_SIGINFO) != 0) {
+		prev_sa.sa_sigaction(signo, info, context);
+		return;
+	}
+	if (prev_sa.sa_handler != SIG_IGN && prev_sa.sa_handler != SIG_DFL) {
+		prev_sa.sa_handler(signo);
+		return;
+	}
+
+	fprintf(stderr, "No Go handler to forward to!\n");
+	abort();
+}
+
+void registerSegvFowarder() {
+	struct sigaction sa;
+	memset(&sa, 0, sizeof(sa));
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
+	sa.sa_sigaction = forwardSignal;
+
+	if (sigaction(SIGSEGV, &sa, &prev_sa) != 0) {
+		perror("failed to register SEGV forwarder");
+		exit(EXIT_FAILURE);
+	}
+}
+*/
+import "C"
+
+func main() {
+	C.registerSegvFowarder()
+
+	defer func() {
+		recover()
+	}()
+	var nilp *int
+	*nilp = 42
+}
diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go
index 34d97de..af4f915 100644
--- a/misc/cgo/testshared/shared_test.go
+++ b/misc/cgo/testshared/shared_test.go
@@ -43,7 +43,7 @@ func run(t *testing.T, msg string, args ...string) {
 }
 
 // goCmd invokes the go tool with the installsuffix set up by TestMain. It calls
-// t.Errorf if the command fails.
+// t.Fatalf if the command fails.
 func goCmd(t *testing.T, args ...string) {
 	newargs := []string{args[0], "-installsuffix=" + suffix}
 	if testing.Verbose() {
@@ -63,7 +63,7 @@ func goCmd(t *testing.T, args ...string) {
 	}
 	if err != nil {
 		if t != nil {
-			t.Errorf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, output)
+			t.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, output)
 		} else {
 			log.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, output)
 		}
@@ -97,6 +97,9 @@ func testMain(m *testing.M) (int, error) {
 	if gorootInstallDir == "" {
 		return 0, errors.New("could not create temporary directory after 10000 tries")
 	}
+	if testing.Verbose() {
+		fmt.Printf("+ mkdir -p %s\n", gorootInstallDir)
+	}
 	defer os.RemoveAll(gorootInstallDir)
 
 	// Some tests need to edit the source in GOPATH, so copy this directory to a
@@ -105,6 +108,9 @@ func testMain(m *testing.M) (int, error) {
 	if err != nil {
 		return 0, fmt.Errorf("TempDir failed: %v", err)
 	}
+	if testing.Verbose() {
+		fmt.Printf("+ mkdir -p %s\n", scratchDir)
+	}
 	defer os.RemoveAll(scratchDir)
 	err = filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
 		scratchPath := filepath.Join(scratchDir, path)
@@ -112,12 +118,18 @@ func testMain(m *testing.M) (int, error) {
 			if path == "." {
 				return nil
 			}
+			if testing.Verbose() {
+				fmt.Printf("+ mkdir -p %s\n", scratchPath)
+			}
 			return os.Mkdir(scratchPath, info.Mode())
 		} else {
 			fromBytes, err := ioutil.ReadFile(path)
 			if err != nil {
 				return err
 			}
+			if testing.Verbose() {
+				fmt.Printf("+ cp %s %s\n", path, scratchPath)
+			}
 			return ioutil.WriteFile(scratchPath, fromBytes, info.Mode())
 		}
 	})
@@ -125,7 +137,13 @@ func testMain(m *testing.M) (int, error) {
 		return 0, fmt.Errorf("walk failed: %v", err)
 	}
 	os.Setenv("GOPATH", scratchDir)
+	if testing.Verbose() {
+		fmt.Printf("+ export GOPATH=%s\n", scratchDir)
+	}
 	myContext.GOPATH = scratchDir
+	if testing.Verbose() {
+		fmt.Printf("+ cd %s\n", scratchDir)
+	}
 	os.Chdir(scratchDir)
 
 	// All tests depend on runtime being built into a shared library. Because
@@ -376,6 +394,14 @@ func TestTrivialExecutable(t *testing.T) {
 	AssertHasRPath(t, "./bin/trivial", gorootInstallDir)
 }
 
+// Build a trivial program in PIE mode that links against the shared runtime and check it runs.
+func TestTrivialExecutablePIE(t *testing.T) {
+	goCmd(t, "build", "-buildmode=pie", "-o", "trivial.pie", "-linkshared", "trivial")
+	run(t, "trivial executable", "./trivial.pie")
+	AssertIsLinkedTo(t, "./trivial.pie", soname)
+	AssertHasRPath(t, "./trivial.pie", gorootInstallDir)
+}
+
 // Build an executable that uses cgo linked against the shared runtime and check it
 // runs.
 func TestCgoExecutable(t *testing.T) {
diff --git a/misc/cgo/testshared/src/depBase/dep.go b/misc/cgo/testshared/src/depBase/dep.go
index c3ae96f..a518b4e 100644
--- a/misc/cgo/testshared/src/depBase/dep.go
+++ b/misc/cgo/testshared/src/depBase/dep.go
@@ -1,5 +1,10 @@
 package depBase
 
+import (
+	"os"
+	"reflect"
+)
+
 var V int = 1
 
 var HasMask []string = []string{"hi"}
@@ -13,6 +18,10 @@ type Dep struct {
 }
 
 func (d *Dep) Method() int {
+	// This code below causes various go.itab.* symbols to be generated in
+	// the shared library. Similar code in ../exe/exe.go results in
+	// exercising https://github.com/golang/go/issues/17594
+	reflect.TypeOf(os.Stdout).Elem()
 	return 10
 }
 
diff --git a/misc/cgo/testshared/src/exe/exe.go b/misc/cgo/testshared/src/exe/exe.go
index 136803f..31fbedd 100644
--- a/misc/cgo/testshared/src/exe/exe.go
+++ b/misc/cgo/testshared/src/exe/exe.go
@@ -2,11 +2,17 @@ package main
 
 import (
 	"depBase"
+	"os"
+	"reflect"
 	"runtime"
 )
 
 func main() {
 	defer depBase.ImplementedInAsm()
+	// This code below causes various go.itab.* symbols to be generated in
+	// the executable. Similar code in ../depBase/dep.go results in
+	// exercising https://github.com/golang/go/issues/17594
+	reflect.TypeOf(os.Stdout).Elem()
 	runtime.GC()
 	depBase.V = depBase.F() + 1
 }
diff --git a/misc/cgo/testsigfwd/main.go b/misc/cgo/testsigfwd/main.go
index d5fbf50..61bd0da 100644
--- a/misc/cgo/testsigfwd/main.go
+++ b/misc/cgo/testsigfwd/main.go
@@ -50,6 +50,7 @@ static void iohandler(int signum) {
 
 static void* sigioThread(void* arg __attribute__ ((unused))) {
 	raise(SIGIO);
+	return NULL;
 }
 
 static void sigioOnThread() {
diff --git a/misc/nacl/testzip.proto b/misc/nacl/testzip.proto
index 8a8784c..561df80 100644
--- a/misc/nacl/testzip.proto
+++ b/misc/nacl/testzip.proto
@@ -18,6 +18,10 @@ go	src=..
 					asm
 						testdata
 							+
+			compile
+				internal
+					syntax
+						parser.go
 			doc
 				main.go
 				pkg.go
@@ -44,6 +48,10 @@ go	src=..
 								x86asm
 									testdata
 										+
+							ppc64
+								ppc64asm
+									testdata
+										+
 		archive
 			tar
 				testdata
diff --git a/src/androidtest.bash b/src/androidtest.bash
index 823b83b..3ac56d1 100755
--- a/src/androidtest.bash
+++ b/src/androidtest.bash
@@ -30,6 +30,10 @@ if [ "$GOARM" != "7" ]; then
 	echo "android only supports GOARM=7, got GOARM=$GOARM" 1>&2
 	exit 1
 fi
+if [ "$GOARCH" = "" ]; then
+	echo "GOARCH must be set" 1>&2
+	exit 1
+fi
 
 export CGO_ENABLED=1
 unset GOBIN
@@ -43,6 +47,12 @@ GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH go build \
 	-o ../bin/go_android_${GOARCH}_exec \
 	../misc/android/go_android_exec.go
 
+export pkgdir=$(dirname $(go list -f '{{.Target}}' runtime))
+if [ "$pkgdir" = "" ]; then
+	echo "could not find android pkg dir" 1>&2
+	exit 1
+fi
+
 export ANDROID_TEST_DIR=/tmp/androidtest-$$
 
 function cleanup() {
@@ -64,15 +74,7 @@ mkdir -p $FAKE_GOROOT/pkg
 cp -a "${GOROOT}/src" "${FAKE_GOROOT}/"
 cp -a "${GOROOT}/test" "${FAKE_GOROOT}/"
 cp -a "${GOROOT}/lib" "${FAKE_GOROOT}/"
-
-# For android, the go tool will install the compiled package in
-# pkg/android_${GOARCH}_shared directory by default, not in
-# the usual pkg/${GOOS}_${GOARCH}. Some tests in src/go/* assume
-# the compiled packages were installed in the usual places.
-# Instead of reflecting this exception into the go/* packages,
-# we copy the compiled packages into the usual places.
-cp -a "${GOROOT}/pkg/android_${GOARCH}_shared" "${FAKE_GOROOT}/pkg/"
-mv "${FAKE_GOROOT}/pkg/android_${GOARCH}_shared" "${FAKE_GOROOT}/pkg/android_${GOARCH}"
+cp -a "${pkgdir}" "${FAKE_GOROOT}/pkg/"
 
 echo '# Syncing test files to android device'
 adb shell mkdir -p /data/local/tmp/goroot
diff --git a/src/archive/tar/common.go b/src/archive/tar/common.go
index 2a1e432..d2ae66d 100644
--- a/src/archive/tar/common.go
+++ b/src/archive/tar/common.go
@@ -13,7 +13,6 @@
 package tar
 
 import (
-	"bytes"
 	"errors"
 	"fmt"
 	"os"
@@ -21,6 +20,10 @@ import (
 	"time"
 )
 
+// BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit
+// architectures. If a large value is encountered when decoding, the result
+// stored in Header will be the truncated version.
+
 // Header type flags.
 const (
 	TypeReg           = '0'    // regular file
@@ -271,28 +274,6 @@ func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
 	return h, nil
 }
 
-func isASCII(s string) bool {
-	for _, c := range s {
-		if c >= 0x80 {
-			return false
-		}
-	}
-	return true
-}
-
-func toASCII(s string) string {
-	if isASCII(s) {
-		return s
-	}
-	var buf bytes.Buffer
-	for _, c := range s {
-		if c < 0x80 {
-			buf.WriteByte(byte(c))
-		}
-	}
-	return buf.String()
-}
-
 // isHeaderOnlyType checks if the given type flag is of the type that has no
 // data section even if a size is specified.
 func isHeaderOnlyType(flag byte) bool {
diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go
index 096ef08..9abe888 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -22,22 +22,20 @@ var (
 	ErrHeader = errors.New("archive/tar: invalid tar header")
 )
 
-const maxNanoSecondIntSize = 9
-
 // A Reader provides sequential access to the contents of a tar archive.
 // A tar archive consists of a sequence of files.
 // The Next method advances to the next file in the archive (including the first),
 // and then it can be treated as an io.Reader to access the file's data.
 type Reader struct {
 	r    io.Reader
-	err  error
 	pad  int64          // amount of padding (ignored) after current file entry
 	curr numBytesReader // reader for current file entry
 	blk  block          // buffer to use as temporary local storage
-}
 
-type parser struct {
-	err error // Last error seen
+	// err is a persistent error.
+	// It is only the responsibility of every exported method of Reader to
+	// ensure that this error is sticky.
+	err error
 }
 
 // A numBytesReader is an io.Reader with a numBytes method, returning the number
@@ -108,8 +106,12 @@ func (tr *Reader) Next() (*Header, error) {
 	if tr.err != nil {
 		return nil, tr.err
 	}
+	hdr, err := tr.next()
+	tr.err = err
+	return hdr, err
+}
 
-	var hdr *Header
+func (tr *Reader) next() (*Header, error) {
 	var extHdrs map[string]string
 
 	// Externally, Next iterates through the tar archive as if it is a series of
@@ -119,29 +121,29 @@ func (tr *Reader) Next() (*Header, error) {
 	// one or more "header files" until it finds a "normal file".
 loop:
 	for {
-		tr.err = tr.skipUnread()
-		if tr.err != nil {
-			return nil, tr.err
+		if err := tr.skipUnread(); err != nil {
+			return nil, err
 		}
-
-		hdr = tr.readHeader()
-		if tr.err != nil {
-			return nil, tr.err
+		hdr, rawHdr, err := tr.readHeader()
+		if err != nil {
+			return nil, err
+		}
+		if err := tr.handleRegularFile(hdr); err != nil {
+			return nil, err
 		}
 
 		// Check for PAX/GNU special headers and files.
 		switch hdr.Typeflag {
 		case TypeXHeader:
-			extHdrs, tr.err = parsePAX(tr)
-			if tr.err != nil {
-				return nil, tr.err
+			extHdrs, err = parsePAX(tr)
+			if err != nil {
+				return nil, err
 			}
 			continue loop // This is a meta header affecting the next header
 		case TypeGNULongName, TypeGNULongLink:
-			var realname []byte
-			realname, tr.err = ioutil.ReadAll(tr)
-			if tr.err != nil {
-				return nil, tr.err
+			realname, err := ioutil.ReadAll(tr)
+			if err != nil {
+				return nil, err
 			}
 
 			// Convert GNU extensions to use PAX headers.
@@ -156,31 +158,73 @@ loop:
 				extHdrs[paxLinkpath] = p.parseString(realname)
 			}
 			if p.err != nil {
-				tr.err = p.err
-				return nil, tr.err
+				return nil, p.err
 			}
 			continue loop // This is a meta header affecting the next header
 		default:
-			mergePAX(hdr, extHdrs)
+			// The old GNU sparse format is handled here since it is technically
+			// just a regular file with additional attributes.
 
-			// Check for a PAX format sparse file
-			sp, err := tr.checkForGNUSparsePAXHeaders(hdr, extHdrs)
-			if err != nil {
-				tr.err = err
+			if err := mergePAX(hdr, extHdrs); err != nil {
 				return nil, err
 			}
-			if sp != nil {
-				// Current file is a PAX format GNU sparse file.
-				// Set the current file reader to a sparse file reader.
-				tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size)
-				if tr.err != nil {
-					return nil, tr.err
-				}
+
+			// The extended headers may have updated the size.
+			// Thus, setup the regFileReader again after merging PAX headers.
+			if err := tr.handleRegularFile(hdr); err != nil {
+				return nil, err
+			}
+
+			// Sparse formats rely on being able to read from the logical data
+			// section; there must be a preceding call to handleRegularFile.
+			if err := tr.handleSparseFile(hdr, rawHdr, extHdrs); err != nil {
+				return nil, err
 			}
-			break loop // This is a file, so stop
+			return hdr, nil // This is a file, so stop
+		}
+	}
+}
+
+// handleRegularFile sets up the current file reader and padding such that it
+// can only read the following logical data section. It will properly handle
+// special headers that contain no data section.
+func (tr *Reader) handleRegularFile(hdr *Header) error {
+	nb := hdr.Size
+	if isHeaderOnlyType(hdr.Typeflag) {
+		nb = 0
+	}
+	if nb < 0 {
+		return ErrHeader
+	}
+
+	tr.pad = -nb & (blockSize - 1) // blockSize is a power of two
+	tr.curr = &regFileReader{r: tr.r, nb: nb}
+	return nil
+}
+
+// handleSparseFile checks if the current file is a sparse format of any type
+// and sets the curr reader appropriately.
+func (tr *Reader) handleSparseFile(hdr *Header, rawHdr *block, extHdrs map[string]string) error {
+	var sp []sparseEntry
+	var err error
+	if hdr.Typeflag == TypeGNUSparse {
+		sp, err = tr.readOldGNUSparseMap(hdr, rawHdr)
+		if err != nil {
+			return err
+		}
+	} else {
+		sp, err = tr.checkForGNUSparsePAXHeaders(hdr, extHdrs)
+		if err != nil {
+			return err
 		}
 	}
-	return hdr, nil
+
+	// If sp is non-nil, then this is a sparse file.
+	// Note that it is possible for len(sp) to be zero.
+	if sp != nil {
+		tr.curr, err = newSparseFileReader(tr.curr, sp, hdr.Size)
+	}
+	return err
 }
 
 // checkForGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. If they are found, then
@@ -219,13 +263,13 @@ func (tr *Reader) checkForGNUSparsePAXHeaders(hdr *Header, headers map[string]st
 		hdr.Name = sparseName
 	}
 	if sparseSizeOk {
-		realSize, err := strconv.ParseInt(sparseSize, 10, 0)
+		realSize, err := strconv.ParseInt(sparseSize, 10, 64)
 		if err != nil {
 			return nil, ErrHeader
 		}
 		hdr.Size = realSize
 	} else if sparseRealSizeOk {
-		realSize, err := strconv.ParseInt(sparseRealSize, 10, 0)
+		realSize, err := strconv.ParseInt(sparseRealSize, 10, 64)
 		if err != nil {
 			return nil, ErrHeader
 		}
@@ -249,53 +293,32 @@ func (tr *Reader) checkForGNUSparsePAXHeaders(hdr *Header, headers map[string]st
 // in the header struct overwrite those found in the header
 // struct with higher precision or longer values. Esp. useful
 // for name and linkname fields.
-func mergePAX(hdr *Header, headers map[string]string) error {
+func mergePAX(hdr *Header, headers map[string]string) (err error) {
+	var id64 int64
 	for k, v := range headers {
 		switch k {
 		case paxPath:
 			hdr.Name = v
 		case paxLinkpath:
 			hdr.Linkname = v
-		case paxGname:
-			hdr.Gname = v
 		case paxUname:
 			hdr.Uname = v
+		case paxGname:
+			hdr.Gname = v
 		case paxUid:
-			uid, err := strconv.ParseInt(v, 10, 0)
-			if err != nil {
-				return err
-			}
-			hdr.Uid = int(uid)
+			id64, err = strconv.ParseInt(v, 10, 64)
+			hdr.Uid = int(id64) // Integer overflow possible
 		case paxGid:
-			gid, err := strconv.ParseInt(v, 10, 0)
-			if err != nil {
-				return err
-			}
-			hdr.Gid = int(gid)
+			id64, err = strconv.ParseInt(v, 10, 64)
+			hdr.Gid = int(id64) // Integer overflow possible
 		case paxAtime:
-			t, err := parsePAXTime(v)
-			if err != nil {
-				return err
-			}
-			hdr.AccessTime = t
+			hdr.AccessTime, err = parsePAXTime(v)
 		case paxMtime:
-			t, err := parsePAXTime(v)
-			if err != nil {
-				return err
-			}
-			hdr.ModTime = t
+			hdr.ModTime, err = parsePAXTime(v)
 		case paxCtime:
-			t, err := parsePAXTime(v)
-			if err != nil {
-				return err
-			}
-			hdr.ChangeTime = t
+			hdr.ChangeTime, err = parsePAXTime(v)
 		case paxSize:
-			size, err := strconv.ParseInt(v, 10, 0)
-			if err != nil {
-				return err
-			}
-			hdr.Size = size
+			hdr.Size, err = strconv.ParseInt(v, 10, 64)
 		default:
 			if strings.HasPrefix(k, paxXattr) {
 				if hdr.Xattrs == nil {
@@ -304,44 +327,11 @@ func mergePAX(hdr *Header, headers map[string]string) error {
 				hdr.Xattrs[k[len(paxXattr):]] = v
 			}
 		}
-	}
-	return nil
-}
-
-// parsePAXTime takes a string of the form %d.%d as described in
-// the PAX specification.
-func parsePAXTime(t string) (time.Time, error) {
-	buf := []byte(t)
-	pos := bytes.IndexByte(buf, '.')
-	var seconds, nanoseconds int64
-	var err error
-	if pos == -1 {
-		seconds, err = strconv.ParseInt(t, 10, 0)
-		if err != nil {
-			return time.Time{}, err
-		}
-	} else {
-		seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0)
-		if err != nil {
-			return time.Time{}, err
-		}
-		nanoBuf := string(buf[pos+1:])
-		// Pad as needed before converting to a decimal.
-		// For example .030 -> .030000000 -> 30000000 nanoseconds
-		if len(nanoBuf) < maxNanoSecondIntSize {
-			// Right pad
-			nanoBuf += strings.Repeat("0", maxNanoSecondIntSize-len(nanoBuf))
-		} else if len(nanoBuf) > maxNanoSecondIntSize {
-			// Right truncate
-			nanoBuf = nanoBuf[:maxNanoSecondIntSize]
-		}
-		nanoseconds, err = strconv.ParseInt(nanoBuf, 10, 0)
 		if err != nil {
-			return time.Time{}, err
+			return ErrHeader
 		}
 	}
-	ts := time.Unix(seconds, nanoseconds)
-	return ts, nil
+	return nil
 }
 
 // parsePAX parses PAX headers.
@@ -354,12 +344,11 @@ func parsePAX(r io.Reader) (map[string]string, error) {
 	sbuf := string(buf)
 
 	// For GNU PAX sparse format 0.0 support.
-	// This function transforms the sparse format 0.0 headers into sparse format 0.1 headers.
-	var sparseMap bytes.Buffer
+	// This function transforms the sparse format 0.0 headers into format 0.1
+	// headers since 0.0 headers were not PAX compliant.
+	var sparseMap []string
 
-	headers := make(map[string]string)
-	// Each record is constructed as
-	//     "%d %s=%s\n", length, keyword, value
+	extHdrs := make(map[string]string)
 	for len(sbuf) > 0 {
 		key, value, residual, err := parsePAXRecord(sbuf)
 		if err != nil {
@@ -367,127 +356,29 @@ func parsePAX(r io.Reader) (map[string]string, error) {
 		}
 		sbuf = residual
 
-		keyStr := key
-		if keyStr == paxGNUSparseOffset || keyStr == paxGNUSparseNumBytes {
-			// GNU sparse format 0.0 special key. Write to sparseMap instead of using the headers map.
-			sparseMap.WriteString(value)
-			sparseMap.Write([]byte{','})
-		} else {
-			// Normal key. Set the value in the headers map.
-			headers[keyStr] = value
-		}
-	}
-	if sparseMap.Len() != 0 {
-		// Add sparse info to headers, chopping off the extra comma
-		sparseMap.Truncate(sparseMap.Len() - 1)
-		headers[paxGNUSparseMap] = sparseMap.String()
-	}
-	return headers, nil
-}
-
-// parsePAXRecord parses the input PAX record string into a key-value pair.
-// If parsing is successful, it will slice off the currently read record and
-// return the remainder as r.
-//
-// A PAX record is of the following form:
-//	"%d %s=%s\n" % (size, key, value)
-func parsePAXRecord(s string) (k, v, r string, err error) {
-	// The size field ends at the first space.
-	sp := strings.IndexByte(s, ' ')
-	if sp == -1 {
-		return "", "", s, ErrHeader
-	}
-
-	// Parse the first token as a decimal integer.
-	n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int
-	if perr != nil || n < 5 || int64(len(s)) < n {
-		return "", "", s, ErrHeader
-	}
-
-	// Extract everything between the space and the final newline.
-	rec, nl, rem := s[sp+1:n-1], s[n-1:n], s[n:]
-	if nl != "\n" {
-		return "", "", s, ErrHeader
-	}
-
-	// The first equals separates the key from the value.
-	eq := strings.IndexByte(rec, '=')
-	if eq == -1 {
-		return "", "", s, ErrHeader
-	}
-	return rec[:eq], rec[eq+1:], rem, nil
-}
-
-// parseString parses bytes as a NUL-terminated C-style string.
-// If a NUL byte is not found then the whole slice is returned as a string.
-func (*parser) parseString(b []byte) string {
-	n := 0
-	for n < len(b) && b[n] != 0 {
-		n++
-	}
-	return string(b[0:n])
-}
-
-// parseNumeric parses the input as being encoded in either base-256 or octal.
-// This function may return negative numbers.
-// If parsing fails or an integer overflow occurs, err will be set.
-func (p *parser) parseNumeric(b []byte) int64 {
-	// Check for base-256 (binary) format first.
-	// If the first bit is set, then all following bits constitute a two's
-	// complement encoded number in big-endian byte order.
-	if len(b) > 0 && b[0]&0x80 != 0 {
-		// Handling negative numbers relies on the following identity:
-		//	-a-1 == ^a
-		//
-		// If the number is negative, we use an inversion mask to invert the
-		// data bytes and treat the value as an unsigned number.
-		var inv byte // 0x00 if positive or zero, 0xff if negative
-		if b[0]&0x40 != 0 {
-			inv = 0xff
-		}
-
-		var x uint64
-		for i, c := range b {
-			c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing
-			if i == 0 {
-				c &= 0x7f // Ignore signal bit in first byte
+		switch key {
+		case paxGNUSparseOffset, paxGNUSparseNumBytes:
+			// Validate sparse header order and value.
+			if (len(sparseMap)%2 == 0 && key != paxGNUSparseOffset) ||
+				(len(sparseMap)%2 == 1 && key != paxGNUSparseNumBytes) ||
+				strings.Contains(value, ",") {
+				return nil, ErrHeader
 			}
-			if (x >> 56) > 0 {
-				p.err = ErrHeader // Integer overflow
-				return 0
+			sparseMap = append(sparseMap, value)
+		default:
+			// According to PAX specification, a value is stored only if it is
+			// non-empty. Otherwise, the key is deleted.
+			if len(value) > 0 {
+				extHdrs[key] = value
+			} else {
+				delete(extHdrs, key)
 			}
-			x = x<<8 | uint64(c)
-		}
-		if (x >> 63) > 0 {
-			p.err = ErrHeader // Integer overflow
-			return 0
 		}
-		if inv == 0xff {
-			return ^int64(x)
-		}
-		return int64(x)
-	}
-
-	// Normal case is base-8 (octal) format.
-	return p.parseOctal(b)
-}
-
-func (p *parser) parseOctal(b []byte) int64 {
-	// Because unused fields are filled with NULs, we need
-	// to skip leading NULs. Fields may also be padded with
-	// spaces or NULs.
-	// So we remove leading and trailing NULs and spaces to
-	// be sure.
-	b = bytes.Trim(b, " \x00")
-
-	if len(b) == 0 {
-		return 0
 	}
-	x, perr := strconv.ParseUint(p.parseString(b), 8, 64)
-	if perr != nil {
-		p.err = ErrHeader
+	if len(sparseMap) > 0 {
+		extHdrs[paxGNUSparseMap] = strings.Join(sparseMap, ",")
 	}
-	return int64(x)
+	return extHdrs, nil
 }
 
 // skipUnread skips any unread bytes in the existing file entry, as well as any
@@ -516,51 +407,46 @@ func (tr *Reader) skipUnread() error {
 			// Seek seems supported, so perform the real Seek.
 			pos2, err := sr.Seek(dataSkip-1, io.SeekCurrent)
 			if err != nil {
-				tr.err = err
-				return tr.err
+				return err
 			}
 			seekSkipped = pos2 - pos1
 		}
 	}
 
-	var copySkipped int64 // Number of bytes skipped via CopyN
-	copySkipped, tr.err = io.CopyN(ioutil.Discard, tr.r, totalSkip-seekSkipped)
-	if tr.err == io.EOF && seekSkipped+copySkipped < dataSkip {
-		tr.err = io.ErrUnexpectedEOF
+	copySkipped, err := io.CopyN(ioutil.Discard, tr.r, totalSkip-seekSkipped)
+	if err == io.EOF && seekSkipped+copySkipped < dataSkip {
+		err = io.ErrUnexpectedEOF
 	}
-	return tr.err
+	return err
 }
 
 // readHeader reads the next block header and assumes that the underlying reader
-// is already aligned to a block boundary.
+// is already aligned to a block boundary. It returns the raw block of the
+// header in case further processing is required.
 //
 // The err will be set to io.EOF only when one of the following occurs:
 //	* Exactly 0 bytes are read and EOF is hit.
 //	* Exactly 1 block of zeros is read and EOF is hit.
 //	* At least 2 blocks of zeros are read.
-func (tr *Reader) readHeader() *Header {
-	if _, tr.err = io.ReadFull(tr.r, tr.blk[:]); tr.err != nil {
-		return nil // io.EOF is okay here
-	}
-
+func (tr *Reader) readHeader() (*Header, *block, error) {
 	// Two blocks of zero bytes marks the end of the archive.
+	if _, err := io.ReadFull(tr.r, tr.blk[:]); err != nil {
+		return nil, nil, err // EOF is okay here; exactly 0 bytes read
+	}
 	if bytes.Equal(tr.blk[:], zeroBlock[:]) {
-		if _, tr.err = io.ReadFull(tr.r, tr.blk[:]); tr.err != nil {
-			return nil // io.EOF is okay here
+		if _, err := io.ReadFull(tr.r, tr.blk[:]); err != nil {
+			return nil, nil, err // EOF is okay here; exactly 1 block of zeros read
 		}
 		if bytes.Equal(tr.blk[:], zeroBlock[:]) {
-			tr.err = io.EOF
-		} else {
-			tr.err = ErrHeader // zero block and then non-zero block
+			return nil, nil, io.EOF // normal EOF; exactly 2 block of zeros read
 		}
-		return nil
+		return nil, nil, ErrHeader // Zero block and then non-zero block
 	}
 
 	// Verify the header matches a known format.
 	format := tr.blk.GetFormat()
 	if format == formatUnknown {
-		tr.err = ErrHeader
-		return nil
+		return nil, nil, ErrHeader
 	}
 
 	var p parser
@@ -577,6 +463,26 @@ func (tr *Reader) readHeader() *Header {
 	hdr.Typeflag = v7.TypeFlag()[0]
 	hdr.Linkname = p.parseString(v7.LinkName())
 
+	// The atime and ctime fields are often left unused. Some versions of Go
+	// had a bug in the tar.Writer where it would output an invalid tar file
+	// in certain rare situations because the logic incorrectly believed that
+	// the old GNU format had a prefix field. This is wrong and leads to
+	// an outputted file that actually mangles the atime and ctime fields.
+	//
+	// In order to continue reading tar files created by a buggy writer, we
+	// try to parse the atime and ctime fields, but just return the zero value
+	// of time.Time when we cannot parse them.
+	//
+	// See https://golang.org/issues/12594
+	tryParseTime := func(b []byte) time.Time {
+		var p parser
+		n := p.parseNumeric(b)
+		if b[0] != 0x00 && p.err == nil {
+			return time.Unix(n, 0)
+		}
+		return time.Time{}
+	}
+
 	// Unpack format specific fields.
 	if format > formatV7 {
 		ustar := tr.blk.USTAR()
@@ -589,9 +495,7 @@ func (tr *Reader) readHeader() *Header {
 
 		var prefix string
 		switch format {
-		case formatUSTAR, formatGNU:
-			// TODO(dsnet): Do not use the prefix field for the GNU format!
-			// See golang.org/issues/12594
+		case formatUSTAR:
 			ustar := tr.blk.USTAR()
 			prefix = p.parseString(ustar.Prefix())
 		case formatSTAR:
@@ -599,97 +503,68 @@ func (tr *Reader) readHeader() *Header {
 			prefix = p.parseString(star.Prefix())
 			hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0)
 			hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0)
+		case formatGNU:
+			gnu := tr.blk.GNU()
+			hdr.AccessTime = tryParseTime(gnu.AccessTime())
+			hdr.ChangeTime = tryParseTime(gnu.ChangeTime())
 		}
 		if len(prefix) > 0 {
 			hdr.Name = prefix + "/" + hdr.Name
 		}
 	}
+	return hdr, &tr.blk, p.err
+}
 
-	nb := hdr.Size
-	if isHeaderOnlyType(hdr.Typeflag) {
-		nb = 0
-	}
-	if nb < 0 {
-		tr.err = ErrHeader
-		return nil
-	}
-
-	// Set the current file reader.
-	tr.pad = -nb & (blockSize - 1) // blockSize is a power of two
-	tr.curr = &regFileReader{r: tr.r, nb: nb}
-
-	// Check for old GNU sparse format entry.
-	if hdr.Typeflag == TypeGNUSparse {
-		// Get the real size of the file.
-		hdr.Size = p.parseNumeric(tr.blk.GNU().RealSize())
-		if p.err != nil {
-			tr.err = p.err
-			return nil
-		}
-
-		// Read the sparse map.
-		sp := tr.readOldGNUSparseMap(&tr.blk)
-		if tr.err != nil {
-			return nil
-		}
-
-		// Current file is a GNU sparse file. Update the current file reader.
-		tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size)
-		if tr.err != nil {
-			return nil
-		}
+// readOldGNUSparseMap reads the sparse map from the old GNU sparse format.
+// The sparse map is stored in the tar header if it's small enough.
+// If it's larger than four entries, then one or more extension headers are used
+// to store the rest of the sparse map.
+//
+// The Header.Size does not reflect the size of any extended headers used.
+// Thus, this function will read from the raw io.Reader to fetch extra headers.
+// This method mutates blk in the process.
+func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) ([]sparseEntry, error) {
+	// Make sure that the input format is GNU.
+	// Unfortunately, the STAR format also has a sparse header format that uses
+	// the same type flag but has a completely different layout.
+	if blk.GetFormat() != formatGNU {
+		return nil, ErrHeader
 	}
 
+	var p parser
+	hdr.Size = p.parseNumeric(blk.GNU().RealSize())
 	if p.err != nil {
-		tr.err = p.err
-		return nil
+		return nil, p.err
 	}
-
-	return hdr
-}
-
-// readOldGNUSparseMap reads the sparse map as stored in the old GNU sparse format.
-// The sparse map is stored in the tar header if it's small enough. If it's larger than four entries,
-// then one or more extension headers are used to store the rest of the sparse map.
-func (tr *Reader) readOldGNUSparseMap(blk *block) []sparseEntry {
-	var p parser
 	var s sparseArray = blk.GNU().Sparse()
 	var sp = make([]sparseEntry, 0, s.MaxEntries())
-	for i := 0; i < s.MaxEntries(); i++ {
-		offset := p.parseOctal(s.Entry(i).Offset())
-		numBytes := p.parseOctal(s.Entry(i).NumBytes())
-		if p.err != nil {
-			tr.err = p.err
-			return nil
-		}
-		if offset == 0 && numBytes == 0 {
-			break
-		}
-		sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
-	}
-
-	for s.IsExtended()[0] > 0 {
-		// There are more entries. Read an extension header and parse its entries.
-		var blk block
-		if _, tr.err = io.ReadFull(tr.r, blk[:]); tr.err != nil {
-			return nil
-		}
-		s = blk.Sparse()
-
+	for {
 		for i := 0; i < s.MaxEntries(); i++ {
-			offset := p.parseOctal(s.Entry(i).Offset())
-			numBytes := p.parseOctal(s.Entry(i).NumBytes())
-			if p.err != nil {
-				tr.err = p.err
-				return nil
+			// This termination condition is identical to GNU and BSD tar.
+			if s.Entry(i).Offset()[0] == 0x00 {
+				break // Don't return, need to process extended headers (even if empty)
 			}
-			if offset == 0 && numBytes == 0 {
-				break
+			offset := p.parseNumeric(s.Entry(i).Offset())
+			numBytes := p.parseNumeric(s.Entry(i).NumBytes())
+			if p.err != nil {
+				return nil, p.err
 			}
 			sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
 		}
+
+		if s.IsExtended()[0] > 0 {
+			// There are more entries. Read an extension header and parse its entries.
+			if _, err := io.ReadFull(tr.r, blk[:]); err != nil {
+				if err == io.EOF {
+					err = io.ErrUnexpectedEOF
+				}
+				return nil, err
+			}
+			s = blk.Sparse()
+			continue
+		}
+		return sp, nil // Done
 	}
-	return sp
 }
 
 // readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format
@@ -817,7 +692,7 @@ func (tr *Reader) numBytes() int64 {
 // Calling Read on special types like TypeLink, TypeSymLink, TypeChar,
 // TypeBlock, TypeDir, and TypeFifo returns 0, io.EOF regardless of what
 // the Header.Size claims.
-func (tr *Reader) Read(b []byte) (n int, err error) {
+func (tr *Reader) Read(b []byte) (int, error) {
 	if tr.err != nil {
 		return 0, tr.err
 	}
@@ -825,11 +700,11 @@ func (tr *Reader) Read(b []byte) (n int, err error) {
 		return 0, io.EOF
 	}
 
-	n, err = tr.curr.Read(b)
+	n, err := tr.curr.Read(b)
 	if err != nil && err != io.EOF {
 		tr.err = err
 	}
-	return
+	return n, err
 }
 
 func (rfr *regFileReader) Read(b []byte) (n int, err error) {
diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go
index 7b148b5..3386868 100644
--- a/src/archive/tar/reader_test.go
+++ b/src/archive/tar/reader_test.go
@@ -18,17 +18,15 @@ import (
 	"time"
 )
 
-type untarTest struct {
-	file    string    // Test input file
-	headers []*Header // Expected output headers
-	chksums []string  // MD5 checksum of files, leave as nil if not checked
-	err     error     // Expected error to occur
-}
-
-var gnuTarTest = &untarTest{
-	file: "testdata/gnu.tar",
-	headers: []*Header{
-		{
+func TestReader(t *testing.T) {
+	vectors := []struct {
+		file    string    // Test input file
+		headers []*Header // Expected output headers
+		chksums []string  // MD5 checksum of files, leave as nil if not checked
+		err     error     // Expected error to occur
+	}{{
+		file: "testdata/gnu.tar",
+		headers: []*Header{{
 			Name:     "small.txt",
 			Mode:     0640,
 			Uid:      73025,
@@ -38,8 +36,7 @@ var gnuTarTest = &untarTest{
 			Typeflag: '0',
 			Uname:    "dsymonds",
 			Gname:    "eng",
-		},
-		{
+		}, {
 			Name:     "small2.txt",
 			Mode:     0640,
 			Uid:      73025,
@@ -49,18 +46,14 @@ var gnuTarTest = &untarTest{
 			Typeflag: '0',
 			Uname:    "dsymonds",
 			Gname:    "eng",
+		}},
+		chksums: []string{
+			"e38b27eaccb4391bdec553a7f3ae6b2f",
+			"c65bd2e50a56a2138bf1716f2fd56fe9",
 		},
-	},
-	chksums: []string{
-		"e38b27eaccb4391bdec553a7f3ae6b2f",
-		"c65bd2e50a56a2138bf1716f2fd56fe9",
-	},
-}
-
-var sparseTarTest = &untarTest{
-	file: "testdata/sparse-formats.tar",
-	headers: []*Header{
-		{
+	}, {
+		file: "testdata/sparse-formats.tar",
+		headers: []*Header{{
 			Name:     "sparse-gnu",
 			Mode:     420,
 			Uid:      1000,
@@ -73,8 +66,7 @@ var sparseTarTest = &untarTest{
 			Gname:    "david",
 			Devmajor: 0,
 			Devminor: 0,
-		},
-		{
+		}, {
 			Name:     "sparse-posix-0.0",
 			Mode:     420,
 			Uid:      1000,
@@ -87,8 +79,7 @@ var sparseTarTest = &untarTest{
 			Gname:    "david",
 			Devmajor: 0,
 			Devminor: 0,
-		},
-		{
+		}, {
 			Name:     "sparse-posix-0.1",
 			Mode:     420,
 			Uid:      1000,
@@ -101,8 +92,7 @@ var sparseTarTest = &untarTest{
 			Gname:    "david",
 			Devmajor: 0,
 			Devminor: 0,
-		},
-		{
+		}, {
 			Name:     "sparse-posix-1.0",
 			Mode:     420,
 			Uid:      1000,
@@ -115,8 +105,7 @@ var sparseTarTest = &untarTest{
 			Gname:    "david",
 			Devmajor: 0,
 			Devminor: 0,
-		},
-		{
+		}, {
 			Name:     "end",
 			Mode:     420,
 			Uid:      1000,
@@ -129,209 +118,237 @@ var sparseTarTest = &untarTest{
 			Gname:    "david",
 			Devmajor: 0,
 			Devminor: 0,
+		}},
+		chksums: []string{
+			"6f53234398c2449fe67c1812d993012f",
+			"6f53234398c2449fe67c1812d993012f",
+			"6f53234398c2449fe67c1812d993012f",
+			"6f53234398c2449fe67c1812d993012f",
+			"b0061974914468de549a2af8ced10316",
 		},
-	},
-	chksums: []string{
-		"6f53234398c2449fe67c1812d993012f",
-		"6f53234398c2449fe67c1812d993012f",
-		"6f53234398c2449fe67c1812d993012f",
-		"6f53234398c2449fe67c1812d993012f",
-		"b0061974914468de549a2af8ced10316",
-	},
-}
-
-var untarTests = []*untarTest{
-	gnuTarTest,
-	sparseTarTest,
-	{
+	}, {
 		file: "testdata/star.tar",
-		headers: []*Header{
-			{
-				Name:       "small.txt",
-				Mode:       0640,
-				Uid:        73025,
-				Gid:        5000,
-				Size:       5,
-				ModTime:    time.Unix(1244592783, 0),
-				Typeflag:   '0',
-				Uname:      "dsymonds",
-				Gname:      "eng",
-				AccessTime: time.Unix(1244592783, 0),
-				ChangeTime: time.Unix(1244592783, 0),
-			},
-			{
-				Name:       "small2.txt",
-				Mode:       0640,
-				Uid:        73025,
-				Gid:        5000,
-				Size:       11,
-				ModTime:    time.Unix(1244592783, 0),
-				Typeflag:   '0',
-				Uname:      "dsymonds",
-				Gname:      "eng",
-				AccessTime: time.Unix(1244592783, 0),
-				ChangeTime: time.Unix(1244592783, 0),
-			},
-		},
-	},
-	{
+		headers: []*Header{{
+			Name:       "small.txt",
+			Mode:       0640,
+			Uid:        73025,
+			Gid:        5000,
+			Size:       5,
+			ModTime:    time.Unix(1244592783, 0),
+			Typeflag:   '0',
+			Uname:      "dsymonds",
+			Gname:      "eng",
+			AccessTime: time.Unix(1244592783, 0),
+			ChangeTime: time.Unix(1244592783, 0),
+		}, {
+			Name:       "small2.txt",
+			Mode:       0640,
+			Uid:        73025,
+			Gid:        5000,
+			Size:       11,
+			ModTime:    time.Unix(1244592783, 0),
+			Typeflag:   '0',
+			Uname:      "dsymonds",
+			Gname:      "eng",
+			AccessTime: time.Unix(1244592783, 0),
+			ChangeTime: time.Unix(1244592783, 0),
+		}},
+	}, {
 		file: "testdata/v7.tar",
-		headers: []*Header{
-			{
-				Name:     "small.txt",
-				Mode:     0444,
-				Uid:      73025,
-				Gid:      5000,
-				Size:     5,
-				ModTime:  time.Unix(1244593104, 0),
-				Typeflag: '\x00',
-			},
-			{
-				Name:     "small2.txt",
-				Mode:     0444,
-				Uid:      73025,
-				Gid:      5000,
-				Size:     11,
-				ModTime:  time.Unix(1244593104, 0),
-				Typeflag: '\x00',
-			},
-		},
-	},
-	{
+		headers: []*Header{{
+			Name:     "small.txt",
+			Mode:     0444,
+			Uid:      73025,
+			Gid:      5000,
+			Size:     5,
+			ModTime:  time.Unix(1244593104, 0),
+			Typeflag: '\x00',
+		}, {
+			Name:     "small2.txt",
+			Mode:     0444,
+			Uid:      73025,
+			Gid:      5000,
+			Size:     11,
+			ModTime:  time.Unix(1244593104, 0),
+			Typeflag: '\x00',
+		}},
+	}, {
 		file: "testdata/pax.tar",
-		headers: []*Header{
-			{
-				Name:       "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
-				Mode:       0664,
-				Uid:        1000,
-				Gid:        1000,
-				Uname:      "shane",
-				Gname:      "shane",
-				Size:       7,
-				ModTime:    time.Unix(1350244992, 23960108),
-				ChangeTime: time.Unix(1350244992, 23960108),
-				AccessTime: time.Unix(1350244992, 23960108),
-				Typeflag:   TypeReg,
-			},
-			{
-				Name:       "a/b",
-				Mode:       0777,
-				Uid:        1000,
-				Gid:        1000,
-				Uname:      "shane",
-				Gname:      "shane",
-				Size:       0,
-				ModTime:    time.Unix(1350266320, 910238425),
-				ChangeTime: time.Unix(1350266320, 910238425),
-				AccessTime: time.Unix(1350266320, 910238425),
-				Typeflag:   TypeSymlink,
-				Linkname:   "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
-			},
+		headers: []*Header{{
+			Name:       "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
+			Mode:       0664,
+			Uid:        1000,
+			Gid:        1000,
+			Uname:      "shane",
+			Gname:      "shane",
+			Size:       7,
+			ModTime:    time.Unix(1350244992, 23960108),
+			ChangeTime: time.Unix(1350244992, 23960108),
+			AccessTime: time.Unix(1350244992, 23960108),
+			Typeflag:   TypeReg,
+		}, {
+			Name:       "a/b",
+			Mode:       0777,
+			Uid:        1000,
+			Gid:        1000,
+			Uname:      "shane",
+			Gname:      "shane",
+			Size:       0,
+			ModTime:    time.Unix(1350266320, 910238425),
+			ChangeTime: time.Unix(1350266320, 910238425),
+			AccessTime: time.Unix(1350266320, 910238425),
+			Typeflag:   TypeSymlink,
+			Linkname:   "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100",
+		}},
+	}, {
+		file: "testdata/pax-bad-hdr-file.tar",
+		err:  ErrHeader,
+	}, {
+		file: "testdata/pax-bad-mtime-file.tar",
+		err:  ErrHeader,
+	}, {
+		file: "testdata/pax-pos-size-file.tar",
+		headers: []*Header{{
+			Name:     "foo",
+			Mode:     0640,
+			Uid:      319973,
+			Gid:      5000,
+			Size:     999,
+			ModTime:  time.Unix(1442282516, 0),
+			Typeflag: '0',
+			Uname:    "joetsai",
+			Gname:    "eng",
+		}},
+		chksums: []string{
+			"0afb597b283fe61b5d4879669a350556",
 		},
-	},
-	{
+	}, {
 		file: "testdata/nil-uid.tar", // golang.org/issue/5290
-		headers: []*Header{
-			{
-				Name:     "P1050238.JPG.log",
-				Mode:     0664,
-				Uid:      0,
-				Gid:      0,
-				Size:     14,
-				ModTime:  time.Unix(1365454838, 0),
-				Typeflag: TypeReg,
-				Linkname: "",
-				Uname:    "eyefi",
-				Gname:    "eyefi",
-				Devmajor: 0,
-				Devminor: 0,
-			},
-		},
-	},
-	{
+		headers: []*Header{{
+			Name:     "P1050238.JPG.log",
+			Mode:     0664,
+			Uid:      0,
+			Gid:      0,
+			Size:     14,
+			ModTime:  time.Unix(1365454838, 0),
+			Typeflag: TypeReg,
+			Linkname: "",
+			Uname:    "eyefi",
+			Gname:    "eyefi",
+			Devmajor: 0,
+			Devminor: 0,
+		}},
+	}, {
 		file: "testdata/xattrs.tar",
-		headers: []*Header{
-			{
-				Name:       "small.txt",
-				Mode:       0644,
-				Uid:        1000,
-				Gid:        10,
-				Size:       5,
-				ModTime:    time.Unix(1386065770, 448252320),
-				Typeflag:   '0',
-				Uname:      "alex",
-				Gname:      "wheel",
-				AccessTime: time.Unix(1389782991, 419875220),
-				ChangeTime: time.Unix(1389782956, 794414986),
-				Xattrs: map[string]string{
-					"user.key":  "value",
-					"user.key2": "value2",
-					// Interestingly, selinux encodes the terminating null inside the xattr
-					"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
-				},
+		headers: []*Header{{
+			Name:       "small.txt",
+			Mode:       0644,
+			Uid:        1000,
+			Gid:        10,
+			Size:       5,
+			ModTime:    time.Unix(1386065770, 448252320),
+			Typeflag:   '0',
+			Uname:      "alex",
+			Gname:      "wheel",
+			AccessTime: time.Unix(1389782991, 419875220),
+			ChangeTime: time.Unix(1389782956, 794414986),
+			Xattrs: map[string]string{
+				"user.key":  "value",
+				"user.key2": "value2",
+				// Interestingly, selinux encodes the terminating null inside the xattr
+				"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
 			},
-			{
-				Name:       "small2.txt",
-				Mode:       0644,
-				Uid:        1000,
-				Gid:        10,
-				Size:       11,
-				ModTime:    time.Unix(1386065770, 449252304),
-				Typeflag:   '0',
-				Uname:      "alex",
-				Gname:      "wheel",
-				AccessTime: time.Unix(1389782991, 419875220),
-				ChangeTime: time.Unix(1386065770, 449252304),
-				Xattrs: map[string]string{
-					"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
-				},
+		}, {
+			Name:       "small2.txt",
+			Mode:       0644,
+			Uid:        1000,
+			Gid:        10,
+			Size:       11,
+			ModTime:    time.Unix(1386065770, 449252304),
+			Typeflag:   '0',
+			Uname:      "alex",
+			Gname:      "wheel",
+			AccessTime: time.Unix(1389782991, 419875220),
+			ChangeTime: time.Unix(1386065770, 449252304),
+			Xattrs: map[string]string{
+				"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
 			},
-		},
-	},
-	{
+		}},
+	}, {
 		// Matches the behavior of GNU, BSD, and STAR tar utilities.
 		file: "testdata/gnu-multi-hdrs.tar",
-		headers: []*Header{
-			{
-				Name:     "GNU2/GNU2/long-path-name",
-				Linkname: "GNU4/GNU4/long-linkpath-name",
-				ModTime:  time.Unix(0, 0),
-				Typeflag: '2',
-			},
-		},
-	},
-	{
+		headers: []*Header{{
+			Name:     "GNU2/GNU2/long-path-name",
+			Linkname: "GNU4/GNU4/long-linkpath-name",
+			ModTime:  time.Unix(0, 0),
+			Typeflag: '2',
+		}},
+	}, {
+		// GNU tar file with atime and ctime fields set.
+		// Created with the GNU tar v1.27.1.
+		//	tar --incremental -S -cvf gnu-incremental.tar test2
+		file: "testdata/gnu-incremental.tar",
+		headers: []*Header{{
+			Name:       "test2/",
+			Mode:       16877,
+			Uid:        1000,
+			Gid:        1000,
+			Size:       14,
+			ModTime:    time.Unix(1441973427, 0),
+			Typeflag:   'D',
+			Uname:      "rawr",
+			Gname:      "dsnet",
+			AccessTime: time.Unix(1441974501, 0),
+			ChangeTime: time.Unix(1441973436, 0),
+		}, {
+			Name:       "test2/foo",
+			Mode:       33188,
+			Uid:        1000,
+			Gid:        1000,
+			Size:       64,
+			ModTime:    time.Unix(1441973363, 0),
+			Typeflag:   '0',
+			Uname:      "rawr",
+			Gname:      "dsnet",
+			AccessTime: time.Unix(1441974501, 0),
+			ChangeTime: time.Unix(1441973436, 0),
+		}, {
+			Name:       "test2/sparse",
+			Mode:       33188,
+			Uid:        1000,
+			Gid:        1000,
+			Size:       536870912,
+			ModTime:    time.Unix(1441973427, 0),
+			Typeflag:   'S',
+			Uname:      "rawr",
+			Gname:      "dsnet",
+			AccessTime: time.Unix(1441991948, 0),
+			ChangeTime: time.Unix(1441973436, 0),
+		}},
+	}, {
 		// Matches the behavior of GNU and BSD tar utilities.
 		file: "testdata/pax-multi-hdrs.tar",
-		headers: []*Header{
-			{
-				Name:     "bar",
-				Linkname: "PAX4/PAX4/long-linkpath-name",
-				ModTime:  time.Unix(0, 0),
-				Typeflag: '2',
-			},
-		},
-	},
-	{
+		headers: []*Header{{
+			Name:     "bar",
+			Linkname: "PAX4/PAX4/long-linkpath-name",
+			ModTime:  time.Unix(0, 0),
+			Typeflag: '2',
+		}},
+	}, {
 		file: "testdata/neg-size.tar",
 		err:  ErrHeader,
-	},
-	{
+	}, {
 		file: "testdata/issue10968.tar",
 		err:  ErrHeader,
-	},
-	{
+	}, {
 		file: "testdata/issue11169.tar",
 		err:  ErrHeader,
-	},
-	{
+	}, {
 		file: "testdata/issue12435.tar",
 		err:  ErrHeader,
-	},
-}
+	}}
 
-func TestReader(t *testing.T) {
-	for i, v := range untarTests {
+	for i, v := range vectors {
 		f, err := os.Open(v.file)
 		if err != nil {
 			t.Errorf("file %s, test %d: unexpected error: %v", v.file, i, err)
@@ -440,83 +457,8 @@ func TestPartialRead(t *testing.T) {
 	}
 }
 
-func TestParsePAXHeader(t *testing.T) {
-	paxTests := [][3]string{
-		{"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths
-		{"a", "a=name", "9 a=name\n"},  // Test case involving multiple acceptable length
-		{"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}}
-	for _, test := range paxTests {
-		key, expected, raw := test[0], test[1], test[2]
-		reader := bytes.NewReader([]byte(raw))
-		headers, err := parsePAX(reader)
-		if err != nil {
-			t.Errorf("Couldn't parse correctly formatted headers: %v", err)
-			continue
-		}
-		if strings.EqualFold(headers[key], expected) {
-			t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected)
-			continue
-		}
-		trailer := make([]byte, 100)
-		n, err := reader.Read(trailer)
-		if err != io.EOF || n != 0 {
-			t.Error("Buffer wasn't consumed")
-		}
-	}
-	badHeaderTests := [][]byte{
-		[]byte("3 somelongkey=\n"),
-		[]byte("50 tooshort=\n"),
-	}
-	for _, test := range badHeaderTests {
-		if _, err := parsePAX(bytes.NewReader(test)); err != ErrHeader {
-			t.Fatal("Unexpected success when parsing bad header")
-		}
-	}
-}
-
-func TestParsePAXTime(t *testing.T) {
-	// Some valid PAX time values
-	timestamps := map[string]time.Time{
-		"1350244992.023960108":  time.Unix(1350244992, 23960108), // The common case
-		"1350244992.02396010":   time.Unix(1350244992, 23960100), // Lower precision value
-		"1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value
-		"1350244992":            time.Unix(1350244992, 0),        // Low precision value
-	}
-	for input, expected := range timestamps {
-		ts, err := parsePAXTime(input)
-		if err != nil {
-			t.Fatal(err)
-		}
-		if !ts.Equal(expected) {
-			t.Fatalf("Time parsing failure %s %s", ts, expected)
-		}
-	}
-}
-
-func TestMergePAX(t *testing.T) {
-	hdr := new(Header)
-	// Test a string, integer, and time based value.
-	headers := map[string]string{
-		"path":  "a/b/c",
-		"uid":   "1000",
-		"mtime": "1350244992.023960108",
-	}
-	err := mergePAX(hdr, headers)
-	if err != nil {
-		t.Fatal(err)
-	}
-	want := &Header{
-		Name:    "a/b/c",
-		Uid:     1000,
-		ModTime: time.Unix(1350244992, 23960108),
-	}
-	if !reflect.DeepEqual(hdr, want) {
-		t.Errorf("incorrect merge: got %+v, want %+v", hdr, want)
-	}
-}
-
 func TestSparseFileReader(t *testing.T) {
-	var vectors = []struct {
+	vectors := []struct {
 		realSize   int64         // Real size of the output file
 		sparseMap  []sparseEntry // Input sparse map
 		sparseData string        // Input compact data
@@ -639,9 +581,11 @@ func TestSparseFileReader(t *testing.T) {
 		r := bytes.NewReader([]byte(v.sparseData))
 		rfr := &regFileReader{r: r, nb: int64(len(v.sparseData))}
 
-		var sfr *sparseFileReader
-		var err error
-		var buf []byte
+		var (
+			sfr *sparseFileReader
+			err error
+			buf []byte
+		)
 
 		sfr, err = newSparseFileReader(rfr, v.sparseMap, v.realSize)
 		if err != nil {
@@ -668,6 +612,64 @@ func TestSparseFileReader(t *testing.T) {
 	}
 }
 
+func TestReadOldGNUSparseMap(t *testing.T) {
+	const (
+		t00 = "00000000000\x0000000000000\x00"
+		t11 = "00000000001\x0000000000001\x00"
+		t12 = "00000000001\x0000000000002\x00"
+		t21 = "00000000002\x0000000000001\x00"
+	)
+
+	mkBlk := func(size, sp0, sp1, sp2, sp3, ext string, format int) *block {
+		var blk block
+		copy(blk.GNU().RealSize(), size)
+		copy(blk.GNU().Sparse().Entry(0), sp0)
+		copy(blk.GNU().Sparse().Entry(1), sp1)
+		copy(blk.GNU().Sparse().Entry(2), sp2)
+		copy(blk.GNU().Sparse().Entry(3), sp3)
+		copy(blk.GNU().Sparse().IsExtended(), ext)
+		if format != formatUnknown {
+			blk.SetFormat(format)
+		}
+		return &blk
+	}
+
+	vectors := []struct {
+		data   string        // Input data
+		rawHdr *block        // Input raw header
+		want   []sparseEntry // Expected sparse entries to be outputted
+		err    error         // Expected error to be returned
+	}{
+		{"", mkBlk("", "", "", "", "", "", formatUnknown), nil, ErrHeader},
+		{"", mkBlk("1234", "fewa", "", "", "", "", formatGNU), nil, ErrHeader},
+		{"", mkBlk("0031", "", "", "", "", "", formatGNU), nil, nil},
+		{"", mkBlk("1234", t00, t11, "", "", "", formatGNU),
+			[]sparseEntry{{0, 0}, {1, 1}}, nil},
+		{"", mkBlk("1234", t11, t12, t21, t11, "", formatGNU),
+			[]sparseEntry{{1, 1}, {1, 2}, {2, 1}, {1, 1}}, nil},
+		{"", mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU),
+			[]sparseEntry{}, io.ErrUnexpectedEOF},
+		{t11 + t11,
+			mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU),
+			[]sparseEntry{}, io.ErrUnexpectedEOF},
+		{t11 + t21 + strings.Repeat("\x00", 512),
+			mkBlk("1234", t11, t12, t21, t11, "\x80", formatGNU),
+			[]sparseEntry{{1, 1}, {1, 2}, {2, 1}, {1, 1}, {1, 1}, {2, 1}}, nil},
+	}
+
+	for i, v := range vectors {
+		tr := Reader{r: strings.NewReader(v.data)}
+		hdr := new(Header)
+		got, err := tr.readOldGNUSparseMap(hdr, v.rawHdr)
+		if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) {
+			t.Errorf("test %d, readOldGNUSparseMap(...): got %v, want %v", i, got, v.want)
+		}
+		if err != v.err {
+			t.Errorf("test %d, unexpected error: got %v, want %v", i, err, v.err)
+		}
+	}
+}
+
 func TestReadGNUSparseMap0x1(t *testing.T) {
 	const (
 		maxUint = ^uint(0)
@@ -679,7 +681,7 @@ func TestReadGNUSparseMap0x1(t *testing.T) {
 		big3 = fmt.Sprintf("%d", (int64(maxInt) / 3))
 	)
 
-	var vectors = []struct {
+	vectors := []struct {
 		extHdrs   map[string]string // Input data
 		sparseMap []sparseEntry     // Expected sparse entries to be outputted
 		err       error             // Expected errors that may be raised
@@ -745,12 +747,12 @@ func TestReadGNUSparseMap0x1(t *testing.T) {
 }
 
 func TestReadGNUSparseMap1x0(t *testing.T) {
-	var sp = []sparseEntry{{1, 2}, {3, 4}}
+	sp := []sparseEntry{{1, 2}, {3, 4}}
 	for i := 0; i < 98; i++ {
 		sp = append(sp, sparseEntry{54321, 12345})
 	}
 
-	var vectors = []struct {
+	vectors := []struct {
 		input     string        // Input data
 		sparseMap []sparseEntry // Expected sparse entries to be outputted
 		cnt       int           // Expected number of bytes read
@@ -825,8 +827,7 @@ func TestReadGNUSparseMap1x0(t *testing.T) {
 }
 
 func TestUninitializedRead(t *testing.T) {
-	test := gnuTarTest
-	f, err := os.Open(test.file)
+	f, err := os.Open("testdata/gnu.tar")
 	if err != nil {
 		t.Fatalf("Unexpected error: %v", err)
 	}
@@ -868,7 +869,7 @@ func TestReadTruncation(t *testing.T) {
 	data2 += strings.Repeat("\x00", 10*512)
 	trash := strings.Repeat("garbage ", 64) // Exactly 512 bytes
 
-	var vectors = []struct {
+	vectors := []struct {
 		input string // Input stream
 		cnt   int    // Expected number of headers read
 		err   error  // Expected error outcome
@@ -904,8 +905,7 @@ func TestReadTruncation(t *testing.T) {
 		{pax + trash[:1], 0, io.ErrUnexpectedEOF},
 		{pax + trash[:511], 0, io.ErrUnexpectedEOF},
 		{sparse[:511], 0, io.ErrUnexpectedEOF},
-		// TODO(dsnet): This should pass, but currently fails.
-		// {sparse[:512], 0, io.ErrUnexpectedEOF},
+		{sparse[:512], 0, io.ErrUnexpectedEOF},
 		{sparse[:3584], 1, io.EOF},
 		{sparse[:9200], 1, io.EOF}, // Terminate in padding of sparse header
 		{sparse[:9216], 1, io.EOF},
@@ -1002,7 +1002,7 @@ func TestReadHeaderOnly(t *testing.T) {
 		t.Fatalf("len(hdrs): got %d, want %d", len(hdrs), 16)
 	}
 	for i := 0; i < 8; i++ {
-		var hdr1, hdr2 = hdrs[i+0], hdrs[i+8]
+		hdr1, hdr2 := hdrs[i+0], hdrs[i+8]
 		hdr1.Size, hdr2.Size = 0, 0
 		if !reflect.DeepEqual(*hdr1, *hdr2) {
 			t.Errorf("incorrect header:\ngot  %+v\nwant %+v", *hdr1, *hdr2)
@@ -1010,116 +1010,87 @@ func TestReadHeaderOnly(t *testing.T) {
 	}
 }
 
-func TestParsePAXRecord(t *testing.T) {
-	var medName = strings.Repeat("CD", 50)
-	var longName = strings.Repeat("AB", 100)
-
-	var vectors = []struct {
-		input     string
-		residual  string
-		outputKey string
-		outputVal string
-		ok        bool
-	}{
-		{"6 k=v\n\n", "\n", "k", "v", true},
-		{"19 path=/etc/hosts\n", "", "path", "/etc/hosts", true},
-		{"210 path=" + longName + "\nabc", "abc", "path", longName, true},
-		{"110 path=" + medName + "\n", "", "path", medName, true},
-		{"9 foo=ba\n", "", "foo", "ba", true},
-		{"11 foo=bar\n\x00", "\x00", "foo", "bar", true},
-		{"18 foo=b=\nar=\n==\x00\n", "", "foo", "b=\nar=\n==\x00", true},
-		{"27 foo=hello9 foo=ba\nworld\n", "", "foo", "hello9 foo=ba\nworld", true},
-		{"27 ☺☻☹=日a本b語ç\nmeow mix", "meow mix", "☺☻☹", "日a本b語ç", true},
-		{"17 \x00hello=\x00world\n", "", "\x00hello", "\x00world", true},
-		{"1 k=1\n", "1 k=1\n", "", "", false},
-		{"6 k~1\n", "6 k~1\n", "", "", false},
-		{"6_k=1\n", "6_k=1\n", "", "", false},
-		{"6 k=1 ", "6 k=1 ", "", "", false},
-		{"632 k=1\n", "632 k=1\n", "", "", false},
-		{"16 longkeyname=hahaha\n", "16 longkeyname=hahaha\n", "", "", false},
-		{"3 somelongkey=\n", "3 somelongkey=\n", "", "", false},
-		{"50 tooshort=\n", "50 tooshort=\n", "", "", false},
-	}
+func TestMergePAX(t *testing.T) {
+	vectors := []struct {
+		in   map[string]string
+		want *Header
+		ok   bool
+	}{{
+		in: map[string]string{
+			"path":  "a/b/c",
+			"uid":   "1000",
+			"mtime": "1350244992.023960108",
+		},
+		want: &Header{
+			Name:    "a/b/c",
+			Uid:     1000,
+			ModTime: time.Unix(1350244992, 23960108),
+		},
+		ok: true,
+	}, {
+		in: map[string]string{
+			"gid": "gtgergergersagersgers",
+		},
+	}, {
+		in: map[string]string{
+			"missing":          "missing",
+			"SCHILY.xattr.key": "value",
+		},
+		want: &Header{
+			Xattrs: map[string]string{"key": "value"},
+		},
+		ok: true,
+	}}
 
-	for _, v := range vectors {
-		key, val, res, err := parsePAXRecord(v.input)
-		ok := (err == nil)
-		if v.ok != ok {
-			if v.ok {
-				t.Errorf("parsePAXRecord(%q): got parsing failure, want success", v.input)
-			} else {
-				t.Errorf("parsePAXRecord(%q): got parsing success, want failure", v.input)
-			}
-		}
-		if ok && (key != v.outputKey || val != v.outputVal) {
-			t.Errorf("parsePAXRecord(%q): got (%q: %q), want (%q: %q)",
-				v.input, key, val, v.outputKey, v.outputVal)
+	for i, v := range vectors {
+		got := new(Header)
+		err := mergePAX(got, v.in)
+		if v.ok && !reflect.DeepEqual(*got, *v.want) {
+			t.Errorf("test %d, mergePAX(...):\ngot  %+v\nwant %+v", i, *got, *v.want)
 		}
-		if res != v.residual {
-			t.Errorf("parsePAXRecord(%q): got residual %q, want residual %q",
-				v.input, res, v.residual)
+		if ok := err == nil; ok != v.ok {
+			t.Errorf("test %d, mergePAX(...): got %v, want %v", i, ok, v.ok)
 		}
 	}
 }
 
-func TestParseNumeric(t *testing.T) {
-	var vectors = []struct {
-		input  string
-		output int64
-		ok     bool
+func TestParsePAX(t *testing.T) {
+	vectors := []struct {
+		in   string
+		want map[string]string
+		ok   bool
 	}{
-		// Test base-256 (binary) encoded values.
-		{"", 0, true},
-		{"\x80", 0, true},
-		{"\x80\x00", 0, true},
-		{"\x80\x00\x00", 0, true},
-		{"\xbf", (1 << 6) - 1, true},
-		{"\xbf\xff", (1 << 14) - 1, true},
-		{"\xbf\xff\xff", (1 << 22) - 1, true},
-		{"\xff", -1, true},
-		{"\xff\xff", -1, true},
-		{"\xff\xff\xff", -1, true},
-		{"\xc0", -1 * (1 << 6), true},
-		{"\xc0\x00", -1 * (1 << 14), true},
-		{"\xc0\x00\x00", -1 * (1 << 22), true},
-		{"\x87\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true},
-		{"\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true},
-		{"\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true},
-		{"\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true},
-		{"\x80\x7f\xff\xff\xff\xff\xff\xff\xff", math.MaxInt64, true},
-		{"\x80\x80\x00\x00\x00\x00\x00\x00\x00", 0, false},
-		{"\xff\x80\x00\x00\x00\x00\x00\x00\x00", math.MinInt64, true},
-		{"\xff\x7f\xff\xff\xff\xff\xff\xff\xff", 0, false},
-		{"\xf5\xec\xd1\xc7\x7e\x5f\x26\x48\x81\x9f\x8f\x9b", 0, false},
-
-		// Test base-8 (octal) encoded values.
-		{"0000000\x00", 0, true},
-		{" \x0000000\x00", 0, true},
-		{" \x0000003\x00", 3, true},
-		{"00000000227\x00", 0227, true},
-		{"032033\x00 ", 032033, true},
-		{"320330\x00 ", 0320330, true},
-		{"0000660\x00 ", 0660, true},
-		{"\x00 0000660\x00 ", 0660, true},
-		{"0123456789abcdef", 0, false},
-		{"0123456789\x00abcdef", 0, false},
-		{"01234567\x0089abcdef", 342391, true},
-		{"0123\x7e\x5f\x264123", 0, false},
+		{"", nil, true},
+		{"6 k=1\n", map[string]string{"k": "1"}, true},
+		{"10 a=name\n", map[string]string{"a": "name"}, true},
+		{"9 a=name\n", map[string]string{"a": "name"}, true},
+		{"30 mtime=1350244992.023960108\n", map[string]string{"mtime": "1350244992.023960108"}, true},
+		{"3 somelongkey=\n", nil, false},
+		{"50 tooshort=\n", nil, false},
+		{"13 key1=haha\n13 key2=nana\n13 key3=kaka\n",
+			map[string]string{"key1": "haha", "key2": "nana", "key3": "kaka"}, true},
+		{"13 key1=val1\n13 key2=val2\n8 key1=\n",
+			map[string]string{"key2": "val2"}, true},
+		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=2\n" +
+			"23 GNU.sparse.offset=1\n25 GNU.sparse.numbytes=2\n" +
+			"23 GNU.sparse.offset=3\n25 GNU.sparse.numbytes=4\n",
+			map[string]string{paxGNUSparseSize: "10", paxGNUSparseNumBlocks: "2", paxGNUSparseMap: "1,2,3,4"}, true},
+		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" +
+			"25 GNU.sparse.numbytes=2\n23 GNU.sparse.offset=1\n",
+			nil, false},
+		{"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" +
+			"25 GNU.sparse.offset=1,2\n25 GNU.sparse.numbytes=2\n",
+			nil, false},
 	}
 
-	for _, v := range vectors {
-		var p parser
-		num := p.parseNumeric([]byte(v.input))
-		ok := (p.err == nil)
-		if v.ok != ok {
-			if v.ok {
-				t.Errorf("parseNumeric(%q): got parsing failure, want success", v.input)
-			} else {
-				t.Errorf("parseNumeric(%q): got parsing success, want failure", v.input)
-			}
+	for i, v := range vectors {
+		r := strings.NewReader(v.in)
+		got, err := parsePAX(r)
+		if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) {
+			t.Errorf("test %d, parsePAX(...):\ngot  %v\nwant %v", i, got, v.want)
 		}
-		if ok && num != v.output {
-			t.Errorf("parseNumeric(%q): got %d, want %d", v.input, num, v.output)
+		if ok := err == nil; ok != v.ok {
+			t.Errorf("test %d, parsePAX(...): got %v, want %v", i, ok, v.ok)
 		}
 	}
 }
diff --git a/src/archive/tar/strconv.go b/src/archive/tar/strconv.go
new file mode 100644
index 0000000..bb5b51c
--- /dev/null
+++ b/src/archive/tar/strconv.go
@@ -0,0 +1,252 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tar
+
+import (
+	"bytes"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func isASCII(s string) bool {
+	for _, c := range s {
+		if c >= 0x80 {
+			return false
+		}
+	}
+	return true
+}
+
+func toASCII(s string) string {
+	if isASCII(s) {
+		return s
+	}
+	var buf bytes.Buffer
+	for _, c := range s {
+		if c < 0x80 {
+			buf.WriteByte(byte(c))
+		}
+	}
+	return buf.String()
+}
+
+type parser struct {
+	err error // Last error seen
+}
+
+type formatter struct {
+	err error // Last error seen
+}
+
+// parseString parses bytes as a NUL-terminated C-style string.
+// If a NUL byte is not found then the whole slice is returned as a string.
+func (*parser) parseString(b []byte) string {
+	n := 0
+	for n < len(b) && b[n] != 0 {
+		n++
+	}
+	return string(b[0:n])
+}
+
+// Write s into b, terminating it with a NUL if there is room.
+func (f *formatter) formatString(b []byte, s string) {
+	if len(s) > len(b) {
+		f.err = ErrFieldTooLong
+		return
+	}
+	ascii := toASCII(s)
+	copy(b, ascii)
+	if len(ascii) < len(b) {
+		b[len(ascii)] = 0
+	}
+}
+
+// fitsInBase256 reports whether x can be encoded into n bytes using base-256
+// encoding. Unlike octal encoding, base-256 encoding does not require that the
+// string ends with a NUL character. Thus, all n bytes are available for output.
+//
+// If operating in binary mode, this assumes strict GNU binary mode; which means
+// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is
+// equivalent to the sign bit in two's complement form.
+func fitsInBase256(n int, x int64) bool {
+	var binBits = uint(n-1) * 8
+	return n >= 9 || (x >= -1<<binBits && x < 1<<binBits)
+}
+
+// parseNumeric parses the input as being encoded in either base-256 or octal.
+// This function may return negative numbers.
+// If parsing fails or an integer overflow occurs, err will be set.
+func (p *parser) parseNumeric(b []byte) int64 {
+	// Check for base-256 (binary) format first.
+	// If the first bit is set, then all following bits constitute a two's
+	// complement encoded number in big-endian byte order.
+	if len(b) > 0 && b[0]&0x80 != 0 {
+		// Handling negative numbers relies on the following identity:
+		//	-a-1 == ^a
+		//
+		// If the number is negative, we use an inversion mask to invert the
+		// data bytes and treat the value as an unsigned number.
+		var inv byte // 0x00 if positive or zero, 0xff if negative
+		if b[0]&0x40 != 0 {
+			inv = 0xff
+		}
+
+		var x uint64
+		for i, c := range b {
+			c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing
+			if i == 0 {
+				c &= 0x7f // Ignore signal bit in first byte
+			}
+			if (x >> 56) > 0 {
+				p.err = ErrHeader // Integer overflow
+				return 0
+			}
+			x = x<<8 | uint64(c)
+		}
+		if (x >> 63) > 0 {
+			p.err = ErrHeader // Integer overflow
+			return 0
+		}
+		if inv == 0xff {
+			return ^int64(x)
+		}
+		return int64(x)
+	}
+
+	// Normal case is base-8 (octal) format.
+	return p.parseOctal(b)
+}
+
+// Write x into b, as binary (GNUtar/star extension).
+func (f *formatter) formatNumeric(b []byte, x int64) {
+	if fitsInBase256(len(b), x) {
+		for i := len(b) - 1; i >= 0; i-- {
+			b[i] = byte(x)
+			x >>= 8
+		}
+		b[0] |= 0x80 // Highest bit indicates binary format
+		return
+	}
+
+	f.formatOctal(b, 0) // Last resort, just write zero
+	f.err = ErrFieldTooLong
+}
+
+func (p *parser) parseOctal(b []byte) int64 {
+	// Because unused fields are filled with NULs, we need
+	// to skip leading NULs. Fields may also be padded with
+	// spaces or NULs.
+	// So we remove leading and trailing NULs and spaces to
+	// be sure.
+	b = bytes.Trim(b, " \x00")
+
+	if len(b) == 0 {
+		return 0
+	}
+	x, perr := strconv.ParseUint(p.parseString(b), 8, 64)
+	if perr != nil {
+		p.err = ErrHeader
+	}
+	return int64(x)
+}
+
+func (f *formatter) formatOctal(b []byte, x int64) {
+	s := strconv.FormatInt(x, 8)
+	// Add leading zeros, but leave room for a NUL.
+	if n := len(b) - len(s) - 1; n > 0 {
+		s = strings.Repeat("0", n) + s
+	}
+	f.formatString(b, s)
+}
+
+// parsePAXTime takes a string of the form %d.%d as described in the PAX
+// specification. Note that this implementation allows for negative timestamps,
+// which is allowed for by the PAX specification, but not always portable.
+func parsePAXTime(s string) (time.Time, error) {
+	const maxNanoSecondDigits = 9
+
+	// Split string into seconds and sub-seconds parts.
+	ss, sn := s, ""
+	if pos := strings.IndexByte(s, '.'); pos >= 0 {
+		ss, sn = s[:pos], s[pos+1:]
+	}
+
+	// Parse the seconds.
+	secs, err := strconv.ParseInt(ss, 10, 64)
+	if err != nil {
+		return time.Time{}, ErrHeader
+	}
+	if len(sn) == 0 {
+		return time.Unix(secs, 0), nil // No sub-second values
+	}
+
+	// Parse the nanoseconds.
+	if strings.Trim(sn, "0123456789") != "" {
+		return time.Time{}, ErrHeader
+	}
+	if len(sn) < maxNanoSecondDigits {
+		sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad
+	} else {
+		sn = sn[:maxNanoSecondDigits] // Right truncate
+	}
+	nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed
+	if len(ss) > 0 && ss[0] == '-' {
+		return time.Unix(secs, -1*int64(nsecs)), nil // Negative correction
+	}
+	return time.Unix(secs, int64(nsecs)), nil
+}
+
+// TODO(dsnet): Implement formatPAXTime.
+
+// parsePAXRecord parses the input PAX record string into a key-value pair.
+// If parsing is successful, it will slice off the currently read record and
+// return the remainder as r.
+//
+// A PAX record is of the following form:
+//	"%d %s=%s\n" % (size, key, value)
+func parsePAXRecord(s string) (k, v, r string, err error) {
+	// The size field ends at the first space.
+	sp := strings.IndexByte(s, ' ')
+	if sp == -1 {
+		return "", "", s, ErrHeader
+	}
+
+	// Parse the first token as a decimal integer.
+	n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int
+	if perr != nil || n < 5 || int64(len(s)) < n {
+		return "", "", s, ErrHeader
+	}
+
+	// Extract everything between the space and the final newline.
+	rec, nl, rem := s[sp+1:n-1], s[n-1:n], s[n:]
+	if nl != "\n" {
+		return "", "", s, ErrHeader
+	}
+
+	// The first equals separates the key from the value.
+	eq := strings.IndexByte(rec, '=')
+	if eq == -1 {
+		return "", "", s, ErrHeader
+	}
+	return rec[:eq], rec[eq+1:], rem, nil
+}
+
+// formatPAXRecord formats a single PAX record, prefixing it with the
+// appropriate length.
+func formatPAXRecord(k, v string) string {
+	const padding = 3 // Extra padding for ' ', '=', and '\n'
+	size := len(k) + len(v) + padding
+	size += len(strconv.Itoa(size))
+	record := fmt.Sprintf("%d %s=%s\n", size, k, v)
+
+	// Final adjustment if adding size field increased the record size.
+	if len(record) != size {
+		size = len(record)
+		record = fmt.Sprintf("%d %s=%s\n", size, k, v)
+	}
+	return record
+}
diff --git a/src/archive/tar/strconv_test.go b/src/archive/tar/strconv_test.go
new file mode 100644
index 0000000..beb7093
--- /dev/null
+++ b/src/archive/tar/strconv_test.go
@@ -0,0 +1,319 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tar
+
+import (
+	"math"
+	"strings"
+	"testing"
+	"time"
+)
+
+func TestFitsInBase256(t *testing.T) {
+	vectors := []struct {
+		in    int64
+		width int
+		ok    bool
+	}{
+		{+1, 8, true},
+		{0, 8, true},
+		{-1, 8, true},
+		{1 << 56, 8, false},
+		{(1 << 56) - 1, 8, true},
+		{-1 << 56, 8, true},
+		{(-1 << 56) - 1, 8, false},
+		{121654, 8, true},
+		{-9849849, 8, true},
+		{math.MaxInt64, 9, true},
+		{0, 9, true},
+		{math.MinInt64, 9, true},
+		{math.MaxInt64, 12, true},
+		{0, 12, true},
+		{math.MinInt64, 12, true},
+	}
+
+	for _, v := range vectors {
+		ok := fitsInBase256(v.width, v.in)
+		if ok != v.ok {
+			t.Errorf("fitsInBase256(%d, %d): got %v, want %v", v.in, v.width, ok, v.ok)
+		}
+	}
+}
+
+func TestParseNumeric(t *testing.T) {
+	vectors := []struct {
+		in   string
+		want int64
+		ok   bool
+	}{
+		// Test base-256 (binary) encoded values.
+		{"", 0, true},
+		{"\x80", 0, true},
+		{"\x80\x00", 0, true},
+		{"\x80\x00\x00", 0, true},
+		{"\xbf", (1 << 6) - 1, true},
+		{"\xbf\xff", (1 << 14) - 1, true},
+		{"\xbf\xff\xff", (1 << 22) - 1, true},
+		{"\xff", -1, true},
+		{"\xff\xff", -1, true},
+		{"\xff\xff\xff", -1, true},
+		{"\xc0", -1 * (1 << 6), true},
+		{"\xc0\x00", -1 * (1 << 14), true},
+		{"\xc0\x00\x00", -1 * (1 << 22), true},
+		{"\x87\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true},
+		{"\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true},
+		{"\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true},
+		{"\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true},
+		{"\x80\x7f\xff\xff\xff\xff\xff\xff\xff", math.MaxInt64, true},
+		{"\x80\x80\x00\x00\x00\x00\x00\x00\x00", 0, false},
+		{"\xff\x80\x00\x00\x00\x00\x00\x00\x00", math.MinInt64, true},
+		{"\xff\x7f\xff\xff\xff\xff\xff\xff\xff", 0, false},
+		{"\xf5\xec\xd1\xc7\x7e\x5f\x26\x48\x81\x9f\x8f\x9b", 0, false},
+
+		// Test base-8 (octal) encoded values.
+		{"0000000\x00", 0, true},
+		{" \x0000000\x00", 0, true},
+		{" \x0000003\x00", 3, true},
+		{"00000000227\x00", 0227, true},
+		{"032033\x00 ", 032033, true},
+		{"320330\x00 ", 0320330, true},
+		{"0000660\x00 ", 0660, true},
+		{"\x00 0000660\x00 ", 0660, true},
+		{"0123456789abcdef", 0, false},
+		{"0123456789\x00abcdef", 0, false},
+		{"01234567\x0089abcdef", 342391, true},
+		{"0123\x7e\x5f\x264123", 0, false},
+	}
+
+	for _, v := range vectors {
+		var p parser
+		got := p.parseNumeric([]byte(v.in))
+		ok := (p.err == nil)
+		if ok != v.ok {
+			if v.ok {
+				t.Errorf("parseNumeric(%q): got parsing failure, want success", v.in)
+			} else {
+				t.Errorf("parseNumeric(%q): got parsing success, want failure", v.in)
+			}
+		}
+		if ok && got != v.want {
+			t.Errorf("parseNumeric(%q): got %d, want %d", v.in, got, v.want)
+		}
+	}
+}
+
+func TestFormatNumeric(t *testing.T) {
+	vectors := []struct {
+		in   int64
+		want string
+		ok   bool
+	}{
+		// Test base-256 (binary) encoded values.
+		{-1, "\xff", true},
+		{-1, "\xff\xff", true},
+		{-1, "\xff\xff\xff", true},
+		{(1 << 0), "0", false},
+		{(1 << 8) - 1, "\x80\xff", true},
+		{(1 << 8), "0\x00", false},
+		{(1 << 16) - 1, "\x80\xff\xff", true},
+		{(1 << 16), "00\x00", false},
+		{-1 * (1 << 0), "\xff", true},
+		{-1*(1<<0) - 1, "0", false},
+		{-1 * (1 << 8), "\xff\x00", true},
+		{-1*(1<<8) - 1, "0\x00", false},
+		{-1 * (1 << 16), "\xff\x00\x00", true},
+		{-1*(1<<16) - 1, "00\x00", false},
+		{537795476381659745, "0000000\x00", false},
+		{537795476381659745, "\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", true},
+		{-615126028225187231, "0000000\x00", false},
+		{-615126028225187231, "\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", true},
+		{math.MaxInt64, "0000000\x00", false},
+		{math.MaxInt64, "\x80\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff", true},
+		{math.MinInt64, "0000000\x00", false},
+		{math.MinInt64, "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00", true},
+		{math.MaxInt64, "\x80\x7f\xff\xff\xff\xff\xff\xff\xff", true},
+		{math.MinInt64, "\xff\x80\x00\x00\x00\x00\x00\x00\x00", true},
+	}
+
+	for _, v := range vectors {
+		var f formatter
+		got := make([]byte, len(v.want))
+		f.formatNumeric(got, v.in)
+		ok := (f.err == nil)
+		if ok != v.ok {
+			if v.ok {
+				t.Errorf("formatNumeric(%d): got formatting failure, want success", v.in)
+			} else {
+				t.Errorf("formatNumeric(%d): got formatting success, want failure", v.in)
+			}
+		}
+		if string(got) != v.want {
+			t.Errorf("formatNumeric(%d): got %q, want %q", v.in, got, v.want)
+		}
+	}
+}
+
+func TestParsePAXTime(t *testing.T) {
+	vectors := []struct {
+		in   string
+		want time.Time
+		ok   bool
+	}{
+		{"1350244992.023960108", time.Unix(1350244992, 23960108), true},
+		{"1350244992.02396010", time.Unix(1350244992, 23960100), true},
+		{"1350244992.0239601089", time.Unix(1350244992, 23960108), true},
+		{"1350244992.3", time.Unix(1350244992, 300000000), true},
+		{"1350244992", time.Unix(1350244992, 0), true},
+		{"-1.000000001", time.Unix(-1, -1e0+0e0), true},
+		{"-1.000001", time.Unix(-1, -1e3+0e0), true},
+		{"-1.001000", time.Unix(-1, -1e6+0e0), true},
+		{"-1", time.Unix(-1, -0e0+0e0), true},
+		{"-1.999000", time.Unix(-1, -1e9+1e6), true},
+		{"-1.999999", time.Unix(-1, -1e9+1e3), true},
+		{"-1.999999999", time.Unix(-1, -1e9+1e0), true},
+		{"0.000000001", time.Unix(0, 1e0+0e0), true},
+		{"0.000001", time.Unix(0, 1e3+0e0), true},
+		{"0.001000", time.Unix(0, 1e6+0e0), true},
+		{"0", time.Unix(0, 0e0), true},
+		{"0.999000", time.Unix(0, 1e9-1e6), true},
+		{"0.999999", time.Unix(0, 1e9-1e3), true},
+		{"0.999999999", time.Unix(0, 1e9-1e0), true},
+		{"1.000000001", time.Unix(+1, +1e0-0e0), true},
+		{"1.000001", time.Unix(+1, +1e3-0e0), true},
+		{"1.001000", time.Unix(+1, +1e6-0e0), true},
+		{"1", time.Unix(+1, +0e0-0e0), true},
+		{"1.999000", time.Unix(+1, +1e9-1e6), true},
+		{"1.999999", time.Unix(+1, +1e9-1e3), true},
+		{"1.999999999", time.Unix(+1, +1e9-1e0), true},
+		{"-1350244992.023960108", time.Unix(-1350244992, -23960108), true},
+		{"-1350244992.02396010", time.Unix(-1350244992, -23960100), true},
+		{"-1350244992.0239601089", time.Unix(-1350244992, -23960108), true},
+		{"-1350244992.3", time.Unix(-1350244992, -300000000), true},
+		{"-1350244992", time.Unix(-1350244992, 0), true},
+		{"", time.Time{}, false},
+		{"0", time.Unix(0, 0), true},
+		{"1.", time.Unix(1, 0), true},
+		{"0.0", time.Unix(0, 0), true},
+		{".5", time.Time{}, false},
+		{"-1.3", time.Unix(-1, -3e8), true},
+		{"-1.0", time.Unix(-1, -0e0), true},
+		{"-0.0", time.Unix(-0, -0e0), true},
+		{"-0.1", time.Unix(-0, -1e8), true},
+		{"-0.01", time.Unix(-0, -1e7), true},
+		{"-0.99", time.Unix(-0, -99e7), true},
+		{"-0.98", time.Unix(-0, -98e7), true},
+		{"-1.1", time.Unix(-1, -1e8), true},
+		{"-1.01", time.Unix(-1, -1e7), true},
+		{"-2.99", time.Unix(-2, -99e7), true},
+		{"-5.98", time.Unix(-5, -98e7), true},
+		{"-", time.Time{}, false},
+		{"+", time.Time{}, false},
+		{"-1.-1", time.Time{}, false},
+		{"99999999999999999999999999999999999999999999999", time.Time{}, false},
+		{"0.123456789abcdef", time.Time{}, false},
+		{"foo", time.Time{}, false},
+		{"\x00", time.Time{}, false},
+		{"𝟵𝟴𝟳𝟲𝟱.𝟰𝟯𝟮𝟭𝟬", time.Time{}, false}, // Unicode numbers (U+1D7EC to U+1D7F5)
+		{"98765﹒43210", time.Time{}, false}, // Unicode period (U+FE52)
+	}
+
+	for _, v := range vectors {
+		ts, err := parsePAXTime(v.in)
+		ok := (err == nil)
+		if v.ok != ok {
+			if v.ok {
+				t.Errorf("parsePAXTime(%q): got parsing failure, want success", v.in)
+			} else {
+				t.Errorf("parsePAXTime(%q): got parsing success, want failure", v.in)
+			}
+		}
+		if ok && !ts.Equal(v.want) {
+			t.Errorf("parsePAXTime(%q): got (%ds %dns), want (%ds %dns)",
+				v.in, ts.Unix(), ts.Nanosecond(), v.want.Unix(), v.want.Nanosecond())
+		}
+	}
+}
+
+func TestParsePAXRecord(t *testing.T) {
+	medName := strings.Repeat("CD", 50)
+	longName := strings.Repeat("AB", 100)
+
+	vectors := []struct {
+		in      string
+		wantRes string
+		wantKey string
+		wantVal string
+		ok      bool
+	}{
+		{"6 k=v\n\n", "\n", "k", "v", true},
+		{"19 path=/etc/hosts\n", "", "path", "/etc/hosts", true},
+		{"210 path=" + longName + "\nabc", "abc", "path", longName, true},
+		{"110 path=" + medName + "\n", "", "path", medName, true},
+		{"9 foo=ba\n", "", "foo", "ba", true},
+		{"11 foo=bar\n\x00", "\x00", "foo", "bar", true},
+		{"18 foo=b=\nar=\n==\x00\n", "", "foo", "b=\nar=\n==\x00", true},
+		{"27 foo=hello9 foo=ba\nworld\n", "", "foo", "hello9 foo=ba\nworld", true},
+		{"27 ☺☻☹=日a本b語ç\nmeow mix", "meow mix", "☺☻☹", "日a本b語ç", true},
+		{"17 \x00hello=\x00world\n", "", "\x00hello", "\x00world", true},
+		{"1 k=1\n", "1 k=1\n", "", "", false},
+		{"6 k~1\n", "6 k~1\n", "", "", false},
+		{"6_k=1\n", "6_k=1\n", "", "", false},
+		{"6 k=1 ", "6 k=1 ", "", "", false},
+		{"632 k=1\n", "632 k=1\n", "", "", false},
+		{"16 longkeyname=hahaha\n", "16 longkeyname=hahaha\n", "", "", false},
+		{"3 somelongkey=\n", "3 somelongkey=\n", "", "", false},
+		{"50 tooshort=\n", "50 tooshort=\n", "", "", false},
+	}
+
+	for _, v := range vectors {
+		key, val, res, err := parsePAXRecord(v.in)
+		ok := (err == nil)
+		if ok != v.ok {
+			if v.ok {
+				t.Errorf("parsePAXRecord(%q): got parsing failure, want success", v.in)
+			} else {
+				t.Errorf("parsePAXRecord(%q): got parsing success, want failure", v.in)
+			}
+		}
+		if v.ok && (key != v.wantKey || val != v.wantVal) {
+			t.Errorf("parsePAXRecord(%q): got (%q: %q), want (%q: %q)",
+				v.in, key, val, v.wantKey, v.wantVal)
+		}
+		if res != v.wantRes {
+			t.Errorf("parsePAXRecord(%q): got residual %q, want residual %q",
+				v.in, res, v.wantRes)
+		}
+	}
+}
+
+func TestFormatPAXRecord(t *testing.T) {
+	medName := strings.Repeat("CD", 50)
+	longName := strings.Repeat("AB", 100)
+
+	vectors := []struct {
+		inKey string
+		inVal string
+		want  string
+	}{
+		{"k", "v", "6 k=v\n"},
+		{"path", "/etc/hosts", "19 path=/etc/hosts\n"},
+		{"path", longName, "210 path=" + longName + "\n"},
+		{"path", medName, "110 path=" + medName + "\n"},
+		{"foo", "ba", "9 foo=ba\n"},
+		{"foo", "bar", "11 foo=bar\n"},
+		{"foo", "b=\nar=\n==\x00", "18 foo=b=\nar=\n==\x00\n"},
+		{"foo", "hello9 foo=ba\nworld", "27 foo=hello9 foo=ba\nworld\n"},
+		{"☺☻☹", "日a本b語ç", "27 ☺☻☹=日a本b語ç\n"},
+		{"\x00hello", "\x00world", "17 \x00hello=\x00world\n"},
+	}
+
+	for _, v := range vectors {
+		got := formatPAXRecord(v.inKey, v.inVal)
+		if got != v.want {
+			t.Errorf("formatPAXRecord(%q, %q): got %q, want %q",
+				v.inKey, v.inVal, got, v.want)
+		}
+	}
+}
diff --git a/src/archive/tar/tar_test.go b/src/archive/tar/tar_test.go
index d63c072..cf8337c 100644
--- a/src/archive/tar/tar_test.go
+++ b/src/archive/tar/tar_test.go
@@ -135,190 +135,178 @@ type headerRoundTripTest struct {
 }
 
 func TestHeaderRoundTrip(t *testing.T) {
-	golden := []headerRoundTripTest{
+	vectors := []headerRoundTripTest{{
 		// regular file.
-		{
-			h: &Header{
-				Name:     "test.txt",
-				Mode:     0644 | c_ISREG,
-				Size:     12,
-				ModTime:  time.Unix(1360600916, 0),
-				Typeflag: TypeReg,
-			},
-			fm: 0644,
+		h: &Header{
+			Name:     "test.txt",
+			Mode:     0644 | c_ISREG,
+			Size:     12,
+			ModTime:  time.Unix(1360600916, 0),
+			Typeflag: TypeReg,
 		},
+		fm: 0644,
+	}, {
 		// symbolic link.
-		{
-			h: &Header{
-				Name:     "link.txt",
-				Mode:     0777 | c_ISLNK,
-				Size:     0,
-				ModTime:  time.Unix(1360600852, 0),
-				Typeflag: TypeSymlink,
-			},
-			fm: 0777 | os.ModeSymlink,
+		h: &Header{
+			Name:     "link.txt",
+			Mode:     0777 | c_ISLNK,
+			Size:     0,
+			ModTime:  time.Unix(1360600852, 0),
+			Typeflag: TypeSymlink,
 		},
+		fm: 0777 | os.ModeSymlink,
+	}, {
 		// character device node.
-		{
-			h: &Header{
-				Name:     "dev/null",
-				Mode:     0666 | c_ISCHR,
-				Size:     0,
-				ModTime:  time.Unix(1360578951, 0),
-				Typeflag: TypeChar,
-			},
-			fm: 0666 | os.ModeDevice | os.ModeCharDevice,
+		h: &Header{
+			Name:     "dev/null",
+			Mode:     0666 | c_ISCHR,
+			Size:     0,
+			ModTime:  time.Unix(1360578951, 0),
+			Typeflag: TypeChar,
 		},
+		fm: 0666 | os.ModeDevice | os.ModeCharDevice,
+	}, {
 		// block device node.
-		{
-			h: &Header{
-				Name:     "dev/sda",
-				Mode:     0660 | c_ISBLK,
-				Size:     0,
-				ModTime:  time.Unix(1360578954, 0),
-				Typeflag: TypeBlock,
-			},
-			fm: 0660 | os.ModeDevice,
+		h: &Header{
+			Name:     "dev/sda",
+			Mode:     0660 | c_ISBLK,
+			Size:     0,
+			ModTime:  time.Unix(1360578954, 0),
+			Typeflag: TypeBlock,
 		},
+		fm: 0660 | os.ModeDevice,
+	}, {
 		// directory.
-		{
-			h: &Header{
-				Name:     "dir/",
-				Mode:     0755 | c_ISDIR,
-				Size:     0,
-				ModTime:  time.Unix(1360601116, 0),
-				Typeflag: TypeDir,
-			},
-			fm: 0755 | os.ModeDir,
+		h: &Header{
+			Name:     "dir/",
+			Mode:     0755 | c_ISDIR,
+			Size:     0,
+			ModTime:  time.Unix(1360601116, 0),
+			Typeflag: TypeDir,
 		},
+		fm: 0755 | os.ModeDir,
+	}, {
 		// fifo node.
-		{
-			h: &Header{
-				Name:     "dev/initctl",
-				Mode:     0600 | c_ISFIFO,
-				Size:     0,
-				ModTime:  time.Unix(1360578949, 0),
-				Typeflag: TypeFifo,
-			},
-			fm: 0600 | os.ModeNamedPipe,
+		h: &Header{
+			Name:     "dev/initctl",
+			Mode:     0600 | c_ISFIFO,
+			Size:     0,
+			ModTime:  time.Unix(1360578949, 0),
+			Typeflag: TypeFifo,
 		},
+		fm: 0600 | os.ModeNamedPipe,
+	}, {
 		// setuid.
-		{
-			h: &Header{
-				Name:     "bin/su",
-				Mode:     0755 | c_ISREG | c_ISUID,
-				Size:     23232,
-				ModTime:  time.Unix(1355405093, 0),
-				Typeflag: TypeReg,
-			},
-			fm: 0755 | os.ModeSetuid,
+		h: &Header{
+			Name:     "bin/su",
+			Mode:     0755 | c_ISREG | c_ISUID,
+			Size:     23232,
+			ModTime:  time.Unix(1355405093, 0),
+			Typeflag: TypeReg,
 		},
+		fm: 0755 | os.ModeSetuid,
+	}, {
 		// setguid.
-		{
-			h: &Header{
-				Name:     "group.txt",
-				Mode:     0750 | c_ISREG | c_ISGID,
-				Size:     0,
-				ModTime:  time.Unix(1360602346, 0),
-				Typeflag: TypeReg,
-			},
-			fm: 0750 | os.ModeSetgid,
+		h: &Header{
+			Name:     "group.txt",
+			Mode:     0750 | c_ISREG | c_ISGID,
+			Size:     0,
+			ModTime:  time.Unix(1360602346, 0),
+			Typeflag: TypeReg,
 		},
+		fm: 0750 | os.ModeSetgid,
+	}, {
 		// sticky.
-		{
-			h: &Header{
-				Name:     "sticky.txt",
-				Mode:     0600 | c_ISREG | c_ISVTX,
-				Size:     7,
-				ModTime:  time.Unix(1360602540, 0),
-				Typeflag: TypeReg,
-			},
-			fm: 0600 | os.ModeSticky,
+		h: &Header{
+			Name:     "sticky.txt",
+			Mode:     0600 | c_ISREG | c_ISVTX,
+			Size:     7,
+			ModTime:  time.Unix(1360602540, 0),
+			Typeflag: TypeReg,
 		},
+		fm: 0600 | os.ModeSticky,
+	}, {
 		// hard link.
-		{
-			h: &Header{
-				Name:     "hard.txt",
-				Mode:     0644 | c_ISREG,
-				Size:     0,
-				Linkname: "file.txt",
-				ModTime:  time.Unix(1360600916, 0),
-				Typeflag: TypeLink,
-			},
-			fm: 0644,
+		h: &Header{
+			Name:     "hard.txt",
+			Mode:     0644 | c_ISREG,
+			Size:     0,
+			Linkname: "file.txt",
+			ModTime:  time.Unix(1360600916, 0),
+			Typeflag: TypeLink,
 		},
+		fm: 0644,
+	}, {
 		// More information.
-		{
-			h: &Header{
-				Name:     "info.txt",
-				Mode:     0600 | c_ISREG,
-				Size:     0,
-				Uid:      1000,
-				Gid:      1000,
-				ModTime:  time.Unix(1360602540, 0),
-				Uname:    "slartibartfast",
-				Gname:    "users",
-				Typeflag: TypeReg,
-			},
-			fm: 0600,
+		h: &Header{
+			Name:     "info.txt",
+			Mode:     0600 | c_ISREG,
+			Size:     0,
+			Uid:      1000,
+			Gid:      1000,
+			ModTime:  time.Unix(1360602540, 0),
+			Uname:    "slartibartfast",
+			Gname:    "users",
+			Typeflag: TypeReg,
 		},
-	}
+		fm: 0600,
+	}}
 
-	for i, g := range golden {
-		fi := g.h.FileInfo()
+	for i, v := range vectors {
+		fi := v.h.FileInfo()
 		h2, err := FileInfoHeader(fi, "")
 		if err != nil {
 			t.Error(err)
 			continue
 		}
 		if strings.Contains(fi.Name(), "/") {
-			t.Errorf("FileInfo of %q contains slash: %q", g.h.Name, fi.Name())
+			t.Errorf("FileInfo of %q contains slash: %q", v.h.Name, fi.Name())
 		}
-		name := path.Base(g.h.Name)
+		name := path.Base(v.h.Name)
 		if fi.IsDir() {
 			name += "/"
 		}
 		if got, want := h2.Name, name; got != want {
 			t.Errorf("i=%d: Name: got %v, want %v", i, got, want)
 		}
-		if got, want := h2.Size, g.h.Size; got != want {
+		if got, want := h2.Size, v.h.Size; got != want {
 			t.Errorf("i=%d: Size: got %v, want %v", i, got, want)
 		}
-		if got, want := h2.Uid, g.h.Uid; got != want {
+		if got, want := h2.Uid, v.h.Uid; got != want {
 			t.Errorf("i=%d: Uid: got %d, want %d", i, got, want)
 		}
-		if got, want := h2.Gid, g.h.Gid; got != want {
+		if got, want := h2.Gid, v.h.Gid; got != want {
 			t.Errorf("i=%d: Gid: got %d, want %d", i, got, want)
 		}
-		if got, want := h2.Uname, g.h.Uname; got != want {
+		if got, want := h2.Uname, v.h.Uname; got != want {
 			t.Errorf("i=%d: Uname: got %q, want %q", i, got, want)
 		}
-		if got, want := h2.Gname, g.h.Gname; got != want {
+		if got, want := h2.Gname, v.h.Gname; got != want {
 			t.Errorf("i=%d: Gname: got %q, want %q", i, got, want)
 		}
-		if got, want := h2.Linkname, g.h.Linkname; got != want {
+		if got, want := h2.Linkname, v.h.Linkname; got != want {
 			t.Errorf("i=%d: Linkname: got %v, want %v", i, got, want)
 		}
-		if got, want := h2.Typeflag, g.h.Typeflag; got != want {
-			t.Logf("%#v %#v", g.h, fi.Sys())
+		if got, want := h2.Typeflag, v.h.Typeflag; got != want {
+			t.Logf("%#v %#v", v.h, fi.Sys())
 			t.Errorf("i=%d: Typeflag: got %q, want %q", i, got, want)
 		}
-		if got, want := h2.Mode, g.h.Mode; got != want {
+		if got, want := h2.Mode, v.h.Mode; got != want {
 			t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
 		}
-		if got, want := fi.Mode(), g.fm; got != want {
+		if got, want := fi.Mode(), v.fm; got != want {
 			t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want)
 		}
-		if got, want := h2.AccessTime, g.h.AccessTime; got != want {
+		if got, want := h2.AccessTime, v.h.AccessTime; got != want {
 			t.Errorf("i=%d: AccessTime: got %v, want %v", i, got, want)
 		}
-		if got, want := h2.ChangeTime, g.h.ChangeTime; got != want {
+		if got, want := h2.ChangeTime, v.h.ChangeTime; got != want {
 			t.Errorf("i=%d: ChangeTime: got %v, want %v", i, got, want)
 		}
-		if got, want := h2.ModTime, g.h.ModTime; got != want {
+		if got, want := h2.ModTime, v.h.ModTime; got != want {
 			t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
 		}
-		if sysh, ok := fi.Sys().(*Header); !ok || sysh != g.h {
+		if sysh, ok := fi.Sys().(*Header); !ok || sysh != v.h {
 			t.Errorf("i=%d: Sys didn't return original *Header", i)
 		}
 	}
diff --git a/src/archive/tar/testdata/gnu-incremental.tar b/src/archive/tar/testdata/gnu-incremental.tar
new file mode 100644
index 0000000..4c442e5
Binary files /dev/null and b/src/archive/tar/testdata/gnu-incremental.tar differ
diff --git a/src/archive/tar/testdata/pax-bad-hdr-file.tar b/src/archive/tar/testdata/pax-bad-hdr-file.tar
new file mode 100644
index 0000000..b97cc98
Binary files /dev/null and b/src/archive/tar/testdata/pax-bad-hdr-file.tar differ
diff --git a/src/archive/tar/testdata/pax-bad-mtime-file.tar b/src/archive/tar/testdata/pax-bad-mtime-file.tar
new file mode 100644
index 0000000..9b22f7e
Binary files /dev/null and b/src/archive/tar/testdata/pax-bad-mtime-file.tar differ
diff --git a/src/archive/tar/testdata/pax-pos-size-file.tar b/src/archive/tar/testdata/pax-pos-size-file.tar
new file mode 100644
index 0000000..aed9a8a
Binary files /dev/null and b/src/archive/tar/testdata/pax-pos-size-file.tar differ
diff --git a/src/archive/tar/testdata/ustar.issue12594.tar b/src/archive/tar/testdata/ustar.issue12594.tar
new file mode 100644
index 0000000..c7910ae
Binary files /dev/null and b/src/archive/tar/testdata/ustar.issue12594.tar differ
diff --git a/src/archive/tar/testdata/writer-big-long.tar b/src/archive/tar/testdata/writer-big-long.tar
index 5960ee8..52bd748 100644
Binary files a/src/archive/tar/testdata/writer-big-long.tar and b/src/archive/tar/testdata/writer-big-long.tar differ
diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go
index 426e443..596fb8b 100644
--- a/src/archive/tar/writer.go
+++ b/src/archive/tar/writer.go
@@ -42,10 +42,6 @@ type Writer struct {
 	paxHdrBuff block // buffer to use in writeHeader when writing a PAX header
 }
 
-type formatter struct {
-	err error // Last error seen
-}
-
 // NewWriter creates a new Writer writing to w.
 func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
 
@@ -71,56 +67,6 @@ func (tw *Writer) Flush() error {
 	return tw.err
 }
 
-// Write s into b, terminating it with a NUL if there is room.
-func (f *formatter) formatString(b []byte, s string) {
-	if len(s) > len(b) {
-		f.err = ErrFieldTooLong
-		return
-	}
-	ascii := toASCII(s)
-	copy(b, ascii)
-	if len(ascii) < len(b) {
-		b[len(ascii)] = 0
-	}
-}
-
-// Encode x as an octal ASCII string and write it into b with leading zeros.
-func (f *formatter) formatOctal(b []byte, x int64) {
-	s := strconv.FormatInt(x, 8)
-	// leading zeros, but leave room for a NUL.
-	for len(s)+1 < len(b) {
-		s = "0" + s
-	}
-	f.formatString(b, s)
-}
-
-// fitsInBase256 reports whether x can be encoded into n bytes using base-256
-// encoding. Unlike octal encoding, base-256 encoding does not require that the
-// string ends with a NUL character. Thus, all n bytes are available for output.
-//
-// If operating in binary mode, this assumes strict GNU binary mode; which means
-// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is
-// equivalent to the sign bit in two's complement form.
-func fitsInBase256(n int, x int64) bool {
-	var binBits = uint(n-1) * 8
-	return n >= 9 || (x >= -1<<binBits && x < 1<<binBits)
-}
-
-// Write x into b, as binary (GNUtar/star extension).
-func (f *formatter) formatNumeric(b []byte, x int64) {
-	if fitsInBase256(len(b), x) {
-		for i := len(b) - 1; i >= 0; i-- {
-			b[i] = byte(x)
-			x >>= 8
-		}
-		b[0] |= 0x80 // Highest bit indicates binary format
-		return
-	}
-
-	f.formatOctal(b, 0) // Last resort, just write zero
-	f.err = ErrFieldTooLong
-}
-
 var (
 	minTime = time.Unix(0, 0)
 	// There is room for 11 octal digits (33 bits) of mtime.
@@ -224,9 +170,41 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
 	formatNumeric(ustar.DevMajor(), hdr.Devmajor, paxNone)
 	formatNumeric(ustar.DevMinor(), hdr.Devminor, paxNone)
 
+	// TODO(dsnet): The logic surrounding the prefix field is broken when trying
+	// to encode the header as GNU format. The challenge with the current logic
+	// is that we are unsure what format we are using at any given moment until
+	// we have processed *all* of the fields. The problem is that by the time
+	// all fields have been processed, some work has already been done to handle
+	// each field under the assumption that it is for one given format or
+	// another. In some situations, this causes the Writer to be confused and
+	// encode a prefix field when the format being used is GNU. Thus, producing
+	// an invalid tar file.
+	//
+	// As a short-term fix, we disable the logic to use the prefix field, which
+	// will force the badly generated GNU files to become encoded as being
+	// the PAX format.
+	//
+	// As an alternative fix, we could hard-code preferPax to be true. However,
+	// this is problematic for the following reasons:
+	//	* The preferPax functionality is not tested at all.
+	//	* This can result in headers that try to use both the GNU and PAX
+	//	features at the same time, which is also wrong.
+	//
+	// The proper fix for this is to use a two-pass method:
+	//	* The first pass simply determines what set of formats can possibly
+	//	encode the given header.
+	//	* The second pass actually encodes the header as that given format
+	//	without worrying about violating the format.
+	//
+	// See the following:
+	//	https://golang.org/issue/12594
+	//	https://golang.org/issue/17630
+	//	https://golang.org/issue/9683
+	const usePrefix = false
+
 	// try to use a ustar header when only the name is too long
 	_, paxPathUsed := paxHeaders[paxPath]
-	if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
+	if usePrefix && !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
 		prefix, suffix, ok := splitUSTARPath(hdr.Name)
 		if ok {
 			// Since we can encode in USTAR format, disable PAX header.
@@ -317,7 +295,7 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) erro
 	var buf bytes.Buffer
 
 	// Keys are sorted before writing to body to allow deterministic output.
-	var keys []string
+	keys := make([]string, 0, len(paxHeaders))
 	for k := range paxHeaders {
 		keys = append(keys, k)
 	}
@@ -340,22 +318,6 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) erro
 	return nil
 }
 
-// formatPAXRecord formats a single PAX record, prefixing it with the
-// appropriate length.
-func formatPAXRecord(k, v string) string {
-	const padding = 3 // Extra padding for ' ', '=', and '\n'
-	size := len(k) + len(v) + padding
-	size += len(strconv.Itoa(size))
-	record := fmt.Sprintf("%d %s=%s\n", size, k, v)
-
-	// Final adjustment if adding size field increased the record size.
-	if len(record) != size {
-		size = len(record)
-		record = fmt.Sprintf("%d %s=%s\n", size, k, v)
-	}
-	return record
-}
-
 // Write writes to the current entry in the tar archive.
 // Write returns the error ErrWriteTooLong if more than
 // hdr.Size bytes are written after WriteHeader.
diff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go
index 27aa8e5..d88b8f4 100644
--- a/src/archive/tar/writer_test.go
+++ b/src/archive/tar/writer_test.go
@@ -9,7 +9,6 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
-	"math"
 	"os"
 	"reflect"
 	"sort"
@@ -19,176 +18,6 @@ import (
 	"time"
 )
 
-type writerTestEntry struct {
-	header   *Header
-	contents string
-}
-
-type writerTest struct {
-	file    string // filename of expected output
-	entries []*writerTestEntry
-}
-
-var writerTests = []*writerTest{
-	// The writer test file was produced with this command:
-	// tar (GNU tar) 1.26
-	//   ln -s small.txt link.txt
-	//   tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt
-	{
-		file: "testdata/writer.tar",
-		entries: []*writerTestEntry{
-			{
-				header: &Header{
-					Name:     "small.txt",
-					Mode:     0640,
-					Uid:      73025,
-					Gid:      5000,
-					Size:     5,
-					ModTime:  time.Unix(1246508266, 0),
-					Typeflag: '0',
-					Uname:    "dsymonds",
-					Gname:    "eng",
-				},
-				contents: "Kilts",
-			},
-			{
-				header: &Header{
-					Name:     "small2.txt",
-					Mode:     0640,
-					Uid:      73025,
-					Gid:      5000,
-					Size:     11,
-					ModTime:  time.Unix(1245217492, 0),
-					Typeflag: '0',
-					Uname:    "dsymonds",
-					Gname:    "eng",
-				},
-				contents: "Google.com\n",
-			},
-			{
-				header: &Header{
-					Name:     "link.txt",
-					Mode:     0777,
-					Uid:      1000,
-					Gid:      1000,
-					Size:     0,
-					ModTime:  time.Unix(1314603082, 0),
-					Typeflag: '2',
-					Linkname: "small.txt",
-					Uname:    "strings",
-					Gname:    "strings",
-				},
-				// no contents
-			},
-		},
-	},
-	// The truncated test file was produced using these commands:
-	//   dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt
-	//   tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar
-	{
-		file: "testdata/writer-big.tar",
-		entries: []*writerTestEntry{
-			{
-				header: &Header{
-					Name:     "tmp/16gig.txt",
-					Mode:     0640,
-					Uid:      73025,
-					Gid:      5000,
-					Size:     16 << 30,
-					ModTime:  time.Unix(1254699560, 0),
-					Typeflag: '0',
-					Uname:    "dsymonds",
-					Gname:    "eng",
-				},
-				// fake contents
-				contents: strings.Repeat("\x00", 4<<10),
-			},
-		},
-	},
-	// The truncated test file was produced using these commands:
-	//   dd if=/dev/zero bs=1048576 count=16384 > (longname/)*15 /16gig.txt
-	//   tar -b 1 -c -f- (longname/)*15 /16gig.txt | dd bs=512 count=8 > writer-big-long.tar
-	{
-		file: "testdata/writer-big-long.tar",
-		entries: []*writerTestEntry{
-			{
-				header: &Header{
-					Name:     strings.Repeat("longname/", 15) + "16gig.txt",
-					Mode:     0644,
-					Uid:      1000,
-					Gid:      1000,
-					Size:     16 << 30,
-					ModTime:  time.Unix(1399583047, 0),
-					Typeflag: '0',
-					Uname:    "guillaume",
-					Gname:    "guillaume",
-				},
-				// fake contents
-				contents: strings.Repeat("\x00", 4<<10),
-			},
-		},
-	},
-	// This file was produced using gnu tar 1.17
-	// gnutar  -b 4 --format=ustar (longname/)*15 + file.txt
-	{
-		file: "testdata/ustar.tar",
-		entries: []*writerTestEntry{
-			{
-				header: &Header{
-					Name:     strings.Repeat("longname/", 15) + "file.txt",
-					Mode:     0644,
-					Uid:      0765,
-					Gid:      024,
-					Size:     06,
-					ModTime:  time.Unix(1360135598, 0),
-					Typeflag: '0',
-					Uname:    "shane",
-					Gname:    "staff",
-				},
-				contents: "hello\n",
-			},
-		},
-	},
-	// This file was produced using gnu tar 1.26
-	// echo "Slartibartfast" > file.txt
-	// ln file.txt hard.txt
-	// tar -b 1 --format=ustar -c -f hardlink.tar file.txt hard.txt
-	{
-		file: "testdata/hardlink.tar",
-		entries: []*writerTestEntry{
-			{
-				header: &Header{
-					Name:     "file.txt",
-					Mode:     0644,
-					Uid:      1000,
-					Gid:      100,
-					Size:     15,
-					ModTime:  time.Unix(1425484303, 0),
-					Typeflag: '0',
-					Uname:    "vbatts",
-					Gname:    "users",
-				},
-				contents: "Slartibartfast\n",
-			},
-			{
-				header: &Header{
-					Name:     "hard.txt",
-					Mode:     0644,
-					Uid:      1000,
-					Gid:      100,
-					Size:     0,
-					ModTime:  time.Unix(1425484303, 0),
-					Typeflag: '1',
-					Linkname: "file.txt",
-					Uname:    "vbatts",
-					Gname:    "users",
-				},
-				// no contents
-			},
-		},
-	},
-}
-
 // Render byte array in a two-character hexadecimal string, spaced for easy visual inspection.
 func bytestr(offset int, b []byte) string {
 	const rowLen = 32
@@ -228,9 +57,168 @@ func bytediff(a []byte, b []byte) string {
 }
 
 func TestWriter(t *testing.T) {
+	type entry struct {
+		header   *Header
+		contents string
+	}
+
+	vectors := []struct {
+		file    string // filename of expected output
+		entries []*entry
+	}{{
+		// The writer test file was produced with this command:
+		// tar (GNU tar) 1.26
+		//   ln -s small.txt link.txt
+		//   tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt
+		file: "testdata/writer.tar",
+		entries: []*entry{{
+			header: &Header{
+				Name:     "small.txt",
+				Mode:     0640,
+				Uid:      73025,
+				Gid:      5000,
+				Size:     5,
+				ModTime:  time.Unix(1246508266, 0),
+				Typeflag: '0',
+				Uname:    "dsymonds",
+				Gname:    "eng",
+			},
+			contents: "Kilts",
+		}, {
+			header: &Header{
+				Name:     "small2.txt",
+				Mode:     0640,
+				Uid:      73025,
+				Gid:      5000,
+				Size:     11,
+				ModTime:  time.Unix(1245217492, 0),
+				Typeflag: '0',
+				Uname:    "dsymonds",
+				Gname:    "eng",
+			},
+			contents: "Google.com\n",
+		}, {
+			header: &Header{
+				Name:     "link.txt",
+				Mode:     0777,
+				Uid:      1000,
+				Gid:      1000,
+				Size:     0,
+				ModTime:  time.Unix(1314603082, 0),
+				Typeflag: '2',
+				Linkname: "small.txt",
+				Uname:    "strings",
+				Gname:    "strings",
+			},
+			// no contents
+		}},
+	}, {
+		// The truncated test file was produced using these commands:
+		//   dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt
+		//   tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar
+		file: "testdata/writer-big.tar",
+		entries: []*entry{{
+			header: &Header{
+				Name:     "tmp/16gig.txt",
+				Mode:     0640,
+				Uid:      73025,
+				Gid:      5000,
+				Size:     16 << 30,
+				ModTime:  time.Unix(1254699560, 0),
+				Typeflag: '0',
+				Uname:    "dsymonds",
+				Gname:    "eng",
+			},
+			// fake contents
+			contents: strings.Repeat("\x00", 4<<10),
+		}},
+	}, {
+		// This truncated file was produced using this library.
+		// It was verified to work with GNU tar 1.27.1 and BSD tar 3.1.2.
+		//  dd if=/dev/zero bs=1G count=16 >> writer-big-long.tar
+		//  gnutar -xvf writer-big-long.tar
+		//  bsdtar -xvf writer-big-long.tar
+		//
+		// This file is in PAX format.
+		file: "testdata/writer-big-long.tar",
+		entries: []*entry{{
+			header: &Header{
+				Name:     strings.Repeat("longname/", 15) + "16gig.txt",
+				Mode:     0644,
+				Uid:      1000,
+				Gid:      1000,
+				Size:     16 << 30,
+				ModTime:  time.Unix(1399583047, 0),
+				Typeflag: '0',
+				Uname:    "guillaume",
+				Gname:    "guillaume",
+			},
+			// fake contents
+			contents: strings.Repeat("\x00", 4<<10),
+		}},
+	}, {
+		// TODO(dsnet): The Writer output should match the following file.
+		// To fix an issue (see https://golang.org/issue/12594), we disabled
+		// prefix support, which alters the generated output.
+		/*
+			// This file was produced using gnu tar 1.17
+			// gnutar  -b 4 --format=ustar (longname/)*15 + file.txt
+			file: "testdata/ustar.tar"
+		*/
+		file: "testdata/ustar.issue12594.tar", // This is a valid tar file, but not expected
+		entries: []*entry{{
+			header: &Header{
+				Name:     strings.Repeat("longname/", 15) + "file.txt",
+				Mode:     0644,
+				Uid:      0765,
+				Gid:      024,
+				Size:     06,
+				ModTime:  time.Unix(1360135598, 0),
+				Typeflag: '0',
+				Uname:    "shane",
+				Gname:    "staff",
+			},
+			contents: "hello\n",
+		}},
+	}, {
+		// This file was produced using gnu tar 1.26
+		// echo "Slartibartfast" > file.txt
+		// ln file.txt hard.txt
+		// tar -b 1 --format=ustar -c -f hardlink.tar file.txt hard.txt
+		file: "testdata/hardlink.tar",
+		entries: []*entry{{
+			header: &Header{
+				Name:     "file.txt",
+				Mode:     0644,
+				Uid:      1000,
+				Gid:      100,
+				Size:     15,
+				ModTime:  time.Unix(1425484303, 0),
+				Typeflag: '0',
+				Uname:    "vbatts",
+				Gname:    "users",
+			},
+			contents: "Slartibartfast\n",
+		}, {
+			header: &Header{
+				Name:     "hard.txt",
+				Mode:     0644,
+				Uid:      1000,
+				Gid:      100,
+				Size:     0,
+				ModTime:  time.Unix(1425484303, 0),
+				Typeflag: '1',
+				Linkname: "file.txt",
+				Uname:    "vbatts",
+				Gname:    "users",
+			},
+			// no contents
+		}},
+	}}
+
 testLoop:
-	for i, test := range writerTests {
-		expected, err := ioutil.ReadFile(test.file)
+	for i, v := range vectors {
+		expected, err := ioutil.ReadFile(v.file)
 		if err != nil {
 			t.Errorf("test %d: Unexpected error: %v", i, err)
 			continue
@@ -239,7 +227,7 @@ testLoop:
 		buf := new(bytes.Buffer)
 		tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB
 		big := false
-		for j, entry := range test.entries {
+		for j, entry := range v.entries {
 			big = big || entry.header.Size > 1<<10
 			if err := tw.WriteHeader(entry.header); err != nil {
 				t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err)
@@ -576,9 +564,9 @@ func TestWriteAfterClose(t *testing.T) {
 }
 
 func TestSplitUSTARPath(t *testing.T) {
-	var sr = strings.Repeat
+	sr := strings.Repeat
 
-	var vectors = []struct {
+	vectors := []struct {
 		input  string // Input path
 		prefix string // Expected output prefix
 		suffix string // Expected output suffix
@@ -609,114 +597,51 @@ func TestSplitUSTARPath(t *testing.T) {
 	}
 }
 
-func TestFormatPAXRecord(t *testing.T) {
-	var medName = strings.Repeat("CD", 50)
-	var longName = strings.Repeat("AB", 100)
-
-	var vectors = []struct {
-		inputKey string
-		inputVal string
-		output   string
-	}{
-		{"k", "v", "6 k=v\n"},
-		{"path", "/etc/hosts", "19 path=/etc/hosts\n"},
-		{"path", longName, "210 path=" + longName + "\n"},
-		{"path", medName, "110 path=" + medName + "\n"},
-		{"foo", "ba", "9 foo=ba\n"},
-		{"foo", "bar", "11 foo=bar\n"},
-		{"foo", "b=\nar=\n==\x00", "18 foo=b=\nar=\n==\x00\n"},
-		{"foo", "hello9 foo=ba\nworld", "27 foo=hello9 foo=ba\nworld\n"},
-		{"☺☻☹", "日a本b語ç", "27 ☺☻☹=日a本b語ç\n"},
-		{"\x00hello", "\x00world", "17 \x00hello=\x00world\n"},
-	}
-
-	for _, v := range vectors {
-		output := formatPAXRecord(v.inputKey, v.inputVal)
-		if output != v.output {
-			t.Errorf("formatPAXRecord(%q, %q): got %q, want %q",
-				v.inputKey, v.inputVal, output, v.output)
+// TestIssue12594 tests that the Writer does not attempt to populate the prefix
+// field when encoding a header in the GNU format. The prefix field is valid
+// in USTAR and PAX, but not GNU.
+func TestIssue12594(t *testing.T) {
+	names := []string{
+		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/file.txt",
+		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/file.txt",
+		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/333/file.txt",
+		"0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/file.txt",
+		"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/file.txt",
+		"/home/support/.openoffice.org/3/user/uno_packages/cache/registry/com.sun.star.comp.deployment.executable.PackageRegistryBackend",
+	}
+
+	for i, name := range names {
+		var b bytes.Buffer
+
+		tw := NewWriter(&b)
+		if err := tw.WriteHeader(&Header{
+			Name: name,
+			Uid:  1 << 25, // Prevent USTAR format
+		}); err != nil {
+			t.Errorf("test %d, unexpected WriteHeader error: %v", i, err)
 		}
-	}
-}
-
-func TestFitsInBase256(t *testing.T) {
-	var vectors = []struct {
-		input int64
-		width int
-		ok    bool
-	}{
-		{+1, 8, true},
-		{0, 8, true},
-		{-1, 8, true},
-		{1 << 56, 8, false},
-		{(1 << 56) - 1, 8, true},
-		{-1 << 56, 8, true},
-		{(-1 << 56) - 1, 8, false},
-		{121654, 8, true},
-		{-9849849, 8, true},
-		{math.MaxInt64, 9, true},
-		{0, 9, true},
-		{math.MinInt64, 9, true},
-		{math.MaxInt64, 12, true},
-		{0, 12, true},
-		{math.MinInt64, 12, true},
-	}
-
-	for _, v := range vectors {
-		ok := fitsInBase256(v.width, v.input)
-		if ok != v.ok {
-			t.Errorf("checkNumeric(%d, %d): got %v, want %v", v.input, v.width, ok, v.ok)
+		if err := tw.Close(); err != nil {
+			t.Errorf("test %d, unexpected Close error: %v", i, err)
 		}
-	}
-}
 
-func TestFormatNumeric(t *testing.T) {
-	var vectors = []struct {
-		input  int64
-		output string
-		ok     bool
-	}{
-		// Test base-256 (binary) encoded values.
-		{-1, "\xff", true},
-		{-1, "\xff\xff", true},
-		{-1, "\xff\xff\xff", true},
-		{(1 << 0), "0", false},
-		{(1 << 8) - 1, "\x80\xff", true},
-		{(1 << 8), "0\x00", false},
-		{(1 << 16) - 1, "\x80\xff\xff", true},
-		{(1 << 16), "00\x00", false},
-		{-1 * (1 << 0), "\xff", true},
-		{-1*(1<<0) - 1, "0", false},
-		{-1 * (1 << 8), "\xff\x00", true},
-		{-1*(1<<8) - 1, "0\x00", false},
-		{-1 * (1 << 16), "\xff\x00\x00", true},
-		{-1*(1<<16) - 1, "00\x00", false},
-		{537795476381659745, "0000000\x00", false},
-		{537795476381659745, "\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", true},
-		{-615126028225187231, "0000000\x00", false},
-		{-615126028225187231, "\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", true},
-		{math.MaxInt64, "0000000\x00", false},
-		{math.MaxInt64, "\x80\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff", true},
-		{math.MinInt64, "0000000\x00", false},
-		{math.MinInt64, "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00", true},
-		{math.MaxInt64, "\x80\x7f\xff\xff\xff\xff\xff\xff\xff", true},
-		{math.MinInt64, "\xff\x80\x00\x00\x00\x00\x00\x00\x00", true},
-	}
+		// The prefix field should never appear in the GNU format.
+		var blk block
+		copy(blk[:], b.Bytes())
+		prefix := string(blk.USTAR().Prefix())
+		if i := strings.IndexByte(prefix, 0); i >= 0 {
+			prefix = prefix[:i] // Truncate at the NUL terminator
+		}
+		if blk.GetFormat() == formatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) {
+			t.Errorf("test %d, found prefix in GNU format: %s", i, prefix)
+		}
 
-	for _, v := range vectors {
-		var f formatter
-		output := make([]byte, len(v.output))
-		f.formatNumeric(output, v.input)
-		ok := (f.err == nil)
-		if ok != v.ok {
-			if v.ok {
-				t.Errorf("formatNumeric(%d): got formatting failure, want success", v.input)
-			} else {
-				t.Errorf("formatNumeric(%d): got formatting success, want failure", v.input)
-			}
+		tr := NewReader(&b)
+		hdr, err := tr.Next()
+		if err != nil {
+			t.Errorf("test %d, unexpected Next error: %v", i, err)
 		}
-		if string(output) != v.output {
-			t.Errorf("formatNumeric(%d): got %q, want %q", v.input, output, v.output)
+		if hdr.Name != name {
+			t.Errorf("test %d, hdr.Name = %s, want %s", i, hdr.Name, name)
 		}
 	}
 }
diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go
index f6c3ead..9bbc9a9 100644
--- a/src/archive/zip/reader.go
+++ b/src/archive/zip/reader.go
@@ -13,6 +13,7 @@ import (
 	"hash/crc32"
 	"io"
 	"os"
+	"time"
 )
 
 var (
@@ -289,13 +290,16 @@ func readDirectoryHeader(f *File, r io.Reader) error {
 		// Other zip authors might not even follow the basic format,
 		// and we'll just ignore the Extra content in that case.
 		b := readBuf(f.Extra)
+
+	Extras:
 		for len(b) >= 4 { // need at least tag and size
 			tag := b.uint16()
 			size := b.uint16()
 			if int(size) > len(b) {
 				break
 			}
-			if tag == zip64ExtraId {
+			switch tag {
+			case zip64ExtraId:
 				// update directory values from the zip64 extra block.
 				// They should only be consulted if the sizes read earlier
 				// are maxed out.
@@ -323,7 +327,42 @@ func readDirectoryHeader(f *File, r io.Reader) error {
 					}
 					f.headerOffset = int64(eb.uint64())
 				}
-				break
+				break Extras
+
+			case ntfsExtraId:
+				if size == 32 {
+					eb := readBuf(b[:size])
+					eb.uint32() // reserved
+					eb.uint16() // tag1
+					size1 := eb.uint16()
+					if size1 == 24 {
+						sub := readBuf(eb[:size1])
+						lo := sub.uint32()
+						hi := sub.uint32()
+						tick := (uint64(uint64(lo)|uint64(hi)<<32) - 116444736000000000) / 10000000
+						f.SetModTime(time.Unix(int64(tick), 0))
+					}
+				}
+				break Extras
+
+			case unixExtraId:
+				if size >= 12 {
+					eb := readBuf(b[:size])
+					eb.uint32()          // AcTime
+					epoch := eb.uint32() // ModTime
+					f.SetModTime(time.Unix(int64(epoch), 0))
+					break Extras
+				}
+			case exttsExtraId:
+				if size >= 3 {
+					eb := readBuf(b[:size])
+					flags := eb.uint8()  // Flags
+					epoch := eb.uint32() // AcTime/ModTime/CrTime
+					if flags&1 != 0 {
+						f.SetModTime(time.Unix(int64(epoch), 0))
+					}
+					break Extras
+				}
 			}
 			b = b[size:]
 		}
@@ -508,6 +547,12 @@ func findSignatureInBlock(b []byte) int {
 
 type readBuf []byte
 
+func (b *readBuf) uint8() uint8 {
+	v := uint8((*b)[0])
+	*b = (*b)[1:]
+	return v
+}
+
 func (b *readBuf) uint16() uint16 {
 	v := binary.LittleEndian.Uint16(*b)
 	*b = (*b)[2:]
diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go
index dfaae78..576a169 100644
--- a/src/archive/zip/reader_test.go
+++ b/src/archive/zip/reader_test.go
@@ -65,13 +65,13 @@ var tests = []ZipTest{
 			{
 				Name:    "test.txt",
 				Content: []byte("This is a test text file.\n"),
-				Mtime:   "09-05-10 12:12:02",
+				Mtime:   "09-05-10 02:12:00",
 				Mode:    0644,
 			},
 			{
 				Name:  "gophercolor16x16.png",
 				File:  "gophercolor16x16.png",
-				Mtime: "09-05-10 15:52:58",
+				Mtime: "09-05-10 05:52:58",
 				Mode:  0644,
 			},
 		},
@@ -83,13 +83,13 @@ var tests = []ZipTest{
 			{
 				Name:    "test.txt",
 				Content: []byte("This is a test text file.\n"),
-				Mtime:   "09-05-10 12:12:02",
+				Mtime:   "09-05-10 02:12:00",
 				Mode:    0644,
 			},
 			{
 				Name:  "gophercolor16x16.png",
 				File:  "gophercolor16x16.png",
-				Mtime: "09-05-10 15:52:58",
+				Mtime: "09-05-10 05:52:58",
 				Mode:  0644,
 			},
 		},
@@ -145,6 +145,17 @@ var tests = []ZipTest{
 		File: crossPlatform,
 	},
 	{
+		Name: "extra-timestamp.zip",
+		File: []ZipTestFile{
+			{
+				Name:    "hello.txt",
+				Content: []byte(""),
+				Mtime:   "01-06-16 12:25:56",
+				Mode:    0666,
+			},
+		},
+	},
+	{
 		// created by Go, before we wrote the "optional" data
 		// descriptor signatures (which are required by OS X)
 		Name: "go-no-datadesc-sig.zip",
@@ -152,13 +163,13 @@ var tests = []ZipTest{
 			{
 				Name:    "foo.txt",
 				Content: []byte("foo\n"),
-				Mtime:   "03-08-12 16:59:10",
+				Mtime:   "03-09-12 00:59:10",
 				Mode:    0644,
 			},
 			{
 				Name:    "bar.txt",
 				Content: []byte("bar\n"),
-				Mtime:   "03-08-12 16:59:12",
+				Mtime:   "03-09-12 00:59:12",
 				Mode:    0644,
 			},
 		},
@@ -205,13 +216,13 @@ var tests = []ZipTest{
 			{
 				Name:    "foo.txt",
 				Content: []byte("foo\n"),
-				Mtime:   "03-08-12 16:59:10",
+				Mtime:   "03-09-12 00:59:10",
 				Mode:    0644,
 			},
 			{
 				Name:    "bar.txt",
 				Content: []byte("bar\n"),
-				Mtime:   "03-08-12 16:59:12",
+				Mtime:   "03-09-12 00:59:12",
 				Mode:    0644,
 			},
 		},
@@ -225,14 +236,14 @@ var tests = []ZipTest{
 			{
 				Name:       "foo.txt",
 				Content:    []byte("foo\n"),
-				Mtime:      "03-08-12 16:59:10",
+				Mtime:      "03-09-12 00:59:10",
 				Mode:       0644,
 				ContentErr: ErrChecksum,
 			},
 			{
 				Name:    "bar.txt",
 				Content: []byte("bar\n"),
-				Mtime:   "03-08-12 16:59:12",
+				Mtime:   "03-09-12 00:59:12",
 				Mode:    0644,
 			},
 		},
diff --git a/src/archive/zip/struct.go b/src/archive/zip/struct.go
index e92d02f..287571e 100644
--- a/src/archive/zip/struct.go
+++ b/src/archive/zip/struct.go
@@ -63,6 +63,9 @@ const (
 
 	// extra header id's
 	zip64ExtraId = 0x0001 // zip64 Extended Information Extra Field
+	ntfsExtraId  = 0x000a // NTFS Extra Field
+	unixExtraId  = 0x000d // UNIX Extra Field
+	exttsExtraId = 0x5455 // Extended Timestamp Extra Field
 )
 
 // FileHeader describes a file within a zip file.
diff --git a/src/archive/zip/testdata/extra-timestamp.zip b/src/archive/zip/testdata/extra-timestamp.zip
new file mode 100644
index 0000000..819e22c
Binary files /dev/null and b/src/archive/zip/testdata/extra-timestamp.zip differ
diff --git a/src/archive/zip/writer.go b/src/archive/zip/writer.go
index 3a9292e..ea4559e 100644
--- a/src/archive/zip/writer.go
+++ b/src/archive/zip/writer.go
@@ -22,6 +22,10 @@ type Writer struct {
 	last        *fileWriter
 	closed      bool
 	compressors map[uint16]Compressor
+
+	// testHookCloseSizeOffset if non-nil is called with the size
+	// of offset of the central directory at Close.
+	testHookCloseSizeOffset func(size, offset uint64)
 }
 
 type header struct {
@@ -98,6 +102,19 @@ func (w *Writer) Close() error {
 			b.uint32(h.CompressedSize)
 			b.uint32(h.UncompressedSize)
 		}
+
+		// use Extended Timestamp Extra Field.
+		if h.ModifiedTime != 0 || h.ModifiedDate != 0 {
+			mt := uint32(h.ModTime().Unix())
+			var mbuf [9]byte // 2x uint16 + uint8 + uint32
+			eb := writeBuf(mbuf[:])
+			eb.uint16(exttsExtraId)
+			eb.uint16(5)  // size = uint8 + uint32
+			eb.uint8(1)   // flags = modtime
+			eb.uint32(mt) // ModTime
+			h.Extra = append(h.Extra, mbuf[:]...)
+		}
+
 		b.uint16(uint16(len(h.Name)))
 		b.uint16(uint16(len(h.Extra)))
 		b.uint16(uint16(len(h.Comment)))
@@ -127,7 +144,11 @@ func (w *Writer) Close() error {
 	size := uint64(end - start)
 	offset := uint64(start)
 
-	if records > uint16max || size > uint32max || offset > uint32max {
+	if f := w.testHookCloseSizeOffset; f != nil {
+		f(size, offset)
+	}
+
+	if records >= uint16max || size >= uint32max || offset >= uint32max {
 		var buf [directory64EndLen + directory64LocLen]byte
 		b := writeBuf(buf[:])
 
@@ -376,6 +397,11 @@ func (w nopCloser) Close() error {
 
 type writeBuf []byte
 
+func (b *writeBuf) uint8(v uint8) {
+	(*b)[0] = v
+	*b = (*b)[1:]
+}
+
 func (b *writeBuf) uint16(v uint16) {
 	binary.LittleEndian.PutUint16(*b, v)
 	*b = (*b)[2:]
diff --git a/src/archive/zip/writer_test.go b/src/archive/zip/writer_test.go
index 86841c7..f20daa0 100644
--- a/src/archive/zip/writer_test.go
+++ b/src/archive/zip/writer_test.go
@@ -11,6 +11,7 @@ import (
 	"math/rand"
 	"os"
 	"testing"
+	"time"
 )
 
 // TODO(adg): a more sophisticated test suite
@@ -20,6 +21,7 @@ type WriteTest struct {
 	Data   []byte
 	Method uint16
 	Mode   os.FileMode
+	Mtime  string
 }
 
 var writeTests = []WriteTest{
@@ -28,30 +30,35 @@ var writeTests = []WriteTest{
 		Data:   []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."),
 		Method: Store,
 		Mode:   0666,
+		Mtime:  "02-01-08 00:01:02",
 	},
 	{
 		Name:   "bar",
 		Data:   nil, // large data set in the test
 		Method: Deflate,
 		Mode:   0644,
+		Mtime:  "03-02-08 01:02:03",
 	},
 	{
 		Name:   "setuid",
 		Data:   []byte("setuid file"),
 		Method: Deflate,
 		Mode:   0755 | os.ModeSetuid,
+		Mtime:  "04-03-08 02:03:04",
 	},
 	{
 		Name:   "setgid",
 		Data:   []byte("setgid file"),
 		Method: Deflate,
 		Mode:   0755 | os.ModeSetgid,
+		Mtime:  "05-04-08 03:04:04",
 	},
 	{
 		Name:   "symlink",
 		Data:   []byte("../link/target"),
 		Method: Deflate,
 		Mode:   0755 | os.ModeSymlink,
+		Mtime:  "03-02-08 11:22:33",
 	},
 }
 
@@ -148,6 +155,11 @@ func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
 	if wt.Mode != 0 {
 		header.SetMode(wt.Mode)
 	}
+	mtime, err := time.Parse("01-02-06 15:04:05", wt.Mtime)
+	if err != nil {
+		t.Fatal("time.Parse:", err)
+	}
+	header.SetModTime(mtime)
 	f, err := w.CreateHeader(header)
 	if err != nil {
 		t.Fatal(err)
@@ -178,6 +190,21 @@ func testReadFile(t *testing.T, f *File, wt *WriteTest) {
 	if !bytes.Equal(b, wt.Data) {
 		t.Errorf("File contents %q, want %q", b, wt.Data)
 	}
+
+	mtime, err := time.Parse("01-02-06 15:04:05", wt.Mtime)
+	if err != nil {
+		t.Fatal("time.Parse:", err)
+	}
+
+	diff := mtime.Sub(f.ModTime())
+	if diff < 0 {
+		diff = -diff
+	}
+
+	// allow several time span
+	if diff > 5*time.Second {
+		t.Errorf("File modtime %v, want %v", mtime, f.ModTime())
+	}
 }
 
 func BenchmarkCompressedZipGarbage(b *testing.B) {
diff --git a/src/archive/zip/zip_test.go b/src/archive/zip/zip_test.go
index 3a3c915..8801e90 100644
--- a/src/archive/zip/zip_test.go
+++ b/src/archive/zip/zip_test.go
@@ -8,11 +8,14 @@ package zip
 
 import (
 	"bytes"
+	"errors"
 	"fmt"
 	"hash"
+	"internal/race"
 	"internal/testenv"
 	"io"
 	"io/ioutil"
+	"reflect"
 	"sort"
 	"strings"
 	"testing"
@@ -111,6 +114,44 @@ func TestFileHeaderRoundTrip64(t *testing.T) {
 	testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t)
 }
 
+func TestZeroFileRoundTrip(t *testing.T) {
+	var b bytes.Buffer
+	w := NewWriter(&b)
+	if _, err := w.Create(""); err != nil {
+		t.Fatal(err)
+	}
+	if err := w.Close(); err != nil {
+		t.Fatal(err)
+	}
+	r, err := NewReader(bytes.NewReader(b.Bytes()), int64(b.Len()))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Verify that fields that should reasonably be the zero value stays
+	// as the zero value.
+	var want FileHeader
+	if len(r.File) != 1 {
+		t.Fatalf("len(r.File) = %d, want 1", len(r.File))
+	}
+	fh := r.File[0].FileHeader
+	got := FileHeader{
+		Name:               fh.Name,
+		ModifiedTime:       fh.ModifiedTime,
+		ModifiedDate:       fh.ModifiedDate,
+		UncompressedSize:   fh.UncompressedSize,
+		UncompressedSize64: fh.UncompressedSize64,
+		ExternalAttrs:      fh.ExternalAttrs,
+		Comment:            fh.Comment,
+	}
+	if len(fh.Extra) > 0 {
+		got.Extra = fh.Extra
+	}
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("FileHeader mismatch:\ngot  %#v\nwant %#v", got, want)
+	}
+}
+
 type repeatedByte struct {
 	off int64
 	b   byte
@@ -232,6 +273,7 @@ func TestZip64(t *testing.T) {
 	if testing.Short() {
 		t.Skip("slow test; skipping")
 	}
+	t.Parallel()
 	const size = 1 << 32 // before the "END\n" part
 	buf := testZip64(t, size)
 	testZip64DirectoryRecordLength(buf, t)
@@ -241,6 +283,7 @@ func TestZip64EdgeCase(t *testing.T) {
 	if testing.Short() {
 		t.Skip("slow test; skipping")
 	}
+	t.Parallel()
 	// Test a zip file with uncompressed size 0xFFFFFFFF.
 	// That's the magic marker for a 64-bit file, so even though
 	// it fits in a 32-bit field we must use the 64-bit field.
@@ -251,6 +294,256 @@ func TestZip64EdgeCase(t *testing.T) {
 	testZip64DirectoryRecordLength(buf, t)
 }
 
+// Tests that we generate a zip64 file if the the directory at offset
+// 0xFFFFFFFF, but not before.
+func TestZip64DirectoryOffset(t *testing.T) {
+	if testing.Short() && race.Enabled {
+		t.Skip("skipping in short mode")
+	}
+	t.Parallel()
+	const filename = "huge.txt"
+	gen := func(wantOff uint64) func(*Writer) {
+		return func(w *Writer) {
+			w.testHookCloseSizeOffset = func(size, off uint64) {
+				if off != wantOff {
+					t.Errorf("central directory offset = %d (%x); want %d", off, off, wantOff)
+				}
+			}
+			f, err := w.CreateHeader(&FileHeader{
+				Name:   filename,
+				Method: Store,
+			})
+			if err != nil {
+				t.Fatal(err)
+			}
+			f.(*fileWriter).crc32 = fakeHash32{}
+			size := wantOff - fileHeaderLen - uint64(len(filename)) - dataDescriptorLen
+			if _, err := io.CopyN(f, zeros{}, int64(size)); err != nil {
+				t.Fatal(err)
+			}
+			if err := w.Close(); err != nil {
+				t.Fatal(err)
+			}
+		}
+	}
+	t.Run("uint32max-2_NoZip64", func(t *testing.T) {
+		t.Parallel()
+		if generatesZip64(t, gen(0xfffffffe)) {
+			t.Error("unexpected zip64")
+		}
+	})
+	t.Run("uint32max-1_Zip64", func(t *testing.T) {
+		t.Parallel()
+		if !generatesZip64(t, gen(0xffffffff)) {
+			t.Error("expected zip64")
+		}
+	})
+}
+
+// At 16k records, we need to generate a zip64 file.
+func TestZip64ManyRecords(t *testing.T) {
+	if testing.Short() && race.Enabled {
+		t.Skip("skipping in short mode")
+	}
+	t.Parallel()
+	gen := func(numRec int) func(*Writer) {
+		return func(w *Writer) {
+			for i := 0; i < numRec; i++ {
+				_, err := w.CreateHeader(&FileHeader{
+					Name:   "a.txt",
+					Method: Store,
+				})
+				if err != nil {
+					t.Fatal(err)
+				}
+			}
+			if err := w.Close(); err != nil {
+				t.Fatal(err)
+			}
+		}
+	}
+	// 16k-1 records shouldn't make a zip64:
+	t.Run("uint16max-1_NoZip64", func(t *testing.T) {
+		t.Parallel()
+		if generatesZip64(t, gen(0xfffe)) {
+			t.Error("unexpected zip64")
+		}
+	})
+	// 16k records should make a zip64:
+	t.Run("uint16max_Zip64", func(t *testing.T) {
+		t.Parallel()
+		if !generatesZip64(t, gen(0xffff)) {
+			t.Error("expected zip64")
+		}
+	})
+}
+
+// suffixSaver is an io.Writer & io.ReaderAt that remembers the last 0
+// to 'keep' bytes of data written to it. Call Suffix to get the
+// suffix bytes.
+type suffixSaver struct {
+	keep  int
+	buf   []byte
+	start int
+	size  int64
+}
+
+func (ss *suffixSaver) Size() int64 { return ss.size }
+
+var errDiscardedBytes = errors.New("ReadAt of discarded bytes")
+
+func (ss *suffixSaver) ReadAt(p []byte, off int64) (n int, err error) {
+	back := ss.size - off
+	if back > int64(ss.keep) {
+		return 0, errDiscardedBytes
+	}
+	suf := ss.Suffix()
+	n = copy(p, suf[len(suf)-int(back):])
+	if n != len(p) {
+		err = io.EOF
+	}
+	return
+}
+
+func (ss *suffixSaver) Suffix() []byte {
+	if len(ss.buf) < ss.keep {
+		return ss.buf
+	}
+	buf := make([]byte, ss.keep)
+	n := copy(buf, ss.buf[ss.start:])
+	copy(buf[n:], ss.buf[:])
+	return buf
+}
+
+func (ss *suffixSaver) Write(p []byte) (n int, err error) {
+	n = len(p)
+	ss.size += int64(len(p))
+	if len(ss.buf) < ss.keep {
+		space := ss.keep - len(ss.buf)
+		add := len(p)
+		if add > space {
+			add = space
+		}
+		ss.buf = append(ss.buf, p[:add]...)
+		p = p[add:]
+	}
+	for len(p) > 0 {
+		n := copy(ss.buf[ss.start:], p)
+		p = p[n:]
+		ss.start += n
+		if ss.start == ss.keep {
+			ss.start = 0
+		}
+	}
+	return
+}
+
+// generatesZip64 reports whether f wrote a zip64 file.
+// f is also responsible for closing w.
+func generatesZip64(t *testing.T, f func(w *Writer)) bool {
+	ss := &suffixSaver{keep: 10 << 20}
+	w := NewWriter(ss)
+	f(w)
+	return suffixIsZip64(t, ss)
+}
+
+type sizedReaderAt interface {
+	io.ReaderAt
+	Size() int64
+}
+
+func suffixIsZip64(t *testing.T, zip sizedReaderAt) bool {
+	d := make([]byte, 1024)
+	if _, err := zip.ReadAt(d, zip.Size()-int64(len(d))); err != nil {
+		t.Fatalf("ReadAt: %v", err)
+	}
+
+	sigOff := findSignatureInBlock(d)
+	if sigOff == -1 {
+		t.Errorf("failed to find signature in block")
+		return false
+	}
+
+	dirOff, err := findDirectory64End(zip, zip.Size()-int64(len(d))+int64(sigOff))
+	if err != nil {
+		t.Fatalf("findDirectory64End: %v", err)
+	}
+	if dirOff == -1 {
+		return false
+	}
+
+	d = make([]byte, directory64EndLen)
+	if _, err := zip.ReadAt(d, dirOff); err != nil {
+		t.Fatalf("ReadAt(off=%d): %v", dirOff, err)
+	}
+
+	b := readBuf(d)
+	if sig := b.uint32(); sig != directory64EndSignature {
+		return false
+	}
+
+	size := b.uint64()
+	if size != directory64EndLen-12 {
+		t.Errorf("expected length of %d, got %d", directory64EndLen-12, size)
+	}
+	return true
+}
+
+// Zip64 is required if the total size of the records is uint32max.
+func TestZip64LargeDirectory(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping in short mode")
+	}
+	t.Parallel()
+	// gen returns a func that writes a zip with a wantLen bytes
+	// of central directory.
+	gen := func(wantLen int64) func(*Writer) {
+		return func(w *Writer) {
+			w.testHookCloseSizeOffset = func(size, off uint64) {
+				if size != uint64(wantLen) {
+					t.Errorf("Close central directory size = %d; want %d", size, wantLen)
+				}
+			}
+
+			uint16string := strings.Repeat(".", uint16max)
+			remain := wantLen
+			for remain > 0 {
+				commentLen := int(uint16max) - directoryHeaderLen - 1
+				thisRecLen := directoryHeaderLen + int(uint16max) + commentLen
+				if int64(thisRecLen) > remain {
+					remove := thisRecLen - int(remain)
+					commentLen -= remove
+					thisRecLen -= remove
+				}
+				remain -= int64(thisRecLen)
+				f, err := w.CreateHeader(&FileHeader{
+					Name:    uint16string,
+					Comment: uint16string[:commentLen],
+				})
+				if err != nil {
+					t.Fatalf("CreateHeader: %v", err)
+				}
+				f.(*fileWriter).crc32 = fakeHash32{}
+			}
+			if err := w.Close(); err != nil {
+				t.Fatalf("Close: %v", err)
+			}
+		}
+	}
+	t.Run("uint32max-1_NoZip64", func(t *testing.T) {
+		t.Parallel()
+		if generatesZip64(t, gen(uint32max-1)) {
+			t.Error("unexpected zip64")
+		}
+	})
+	t.Run("uint32max_HasZip64", func(t *testing.T) {
+		t.Parallel()
+		if !generatesZip64(t, gen(uint32max)) {
+			t.Error("expected zip64")
+		}
+	})
+}
+
 func testZip64(t testing.TB, size int64) *rleBuffer {
 	const chunkSize = 1024
 	chunks := int(size / chunkSize)
@@ -339,30 +632,8 @@ func testZip64(t testing.TB, size int64) *rleBuffer {
 
 // Issue 9857
 func testZip64DirectoryRecordLength(buf *rleBuffer, t *testing.T) {
-	d := make([]byte, 1024)
-	if _, err := buf.ReadAt(d, buf.Size()-int64(len(d))); err != nil {
-		t.Fatal("read:", err)
-	}
-
-	sigOff := findSignatureInBlock(d)
-	dirOff, err := findDirectory64End(buf, buf.Size()-int64(len(d))+int64(sigOff))
-	if err != nil {
-		t.Fatal("findDirectory64End:", err)
-	}
-
-	d = make([]byte, directory64EndLen)
-	if _, err := buf.ReadAt(d, dirOff); err != nil {
-		t.Fatal("read:", err)
-	}
-
-	b := readBuf(d)
-	if sig := b.uint32(); sig != directory64EndSignature {
-		t.Fatalf("Expected directory64EndSignature (%d), got %d", directory64EndSignature, sig)
-	}
-
-	size := b.uint64()
-	if size != directory64EndLen-12 {
-		t.Fatalf("Expected length of %d, got %d", directory64EndLen-12, size)
+	if !suffixIsZip64(t, buf) {
+		t.Fatal("not a zip64")
 	}
 }
 
@@ -448,3 +719,47 @@ func BenchmarkZip64Test(b *testing.B) {
 		testZip64(b, 1<<26)
 	}
 }
+
+func TestSuffixSaver(t *testing.T) {
+	const keep = 10
+	ss := &suffixSaver{keep: keep}
+	ss.Write([]byte("abc"))
+	if got := string(ss.Suffix()); got != "abc" {
+		t.Errorf("got = %q; want abc", got)
+	}
+	ss.Write([]byte("defghijklmno"))
+	if got := string(ss.Suffix()); got != "fghijklmno" {
+		t.Errorf("got = %q; want fghijklmno", got)
+	}
+	if got, want := ss.Size(), int64(len("abc")+len("defghijklmno")); got != want {
+		t.Errorf("Size = %d; want %d", got, want)
+	}
+	buf := make([]byte, ss.Size())
+	for off := int64(0); off < ss.Size(); off++ {
+		for size := 1; size <= int(ss.Size()-off); size++ {
+			readBuf := buf[:size]
+			n, err := ss.ReadAt(readBuf, off)
+			if off < ss.Size()-keep {
+				if err != errDiscardedBytes {
+					t.Errorf("off %d, size %d = %v, %v (%q); want errDiscardedBytes", off, size, n, err, readBuf[:n])
+				}
+				continue
+			}
+			want := "abcdefghijklmno"[off : off+int64(size)]
+			got := string(readBuf[:n])
+			if err != nil || got != want {
+				t.Errorf("off %d, size %d = %v, %v (%q); want %q", off, size, n, err, got, want)
+			}
+		}
+	}
+
+}
+
+type zeros struct{}
+
+func (zeros) Read(p []byte) (int, error) {
+	for i := range p {
+		p[i] = 0
+	}
+	return len(p), nil
+}
diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go
index 3b30b8b..e1e8fb2 100644
--- a/src/bufio/bufio.go
+++ b/src/bufio/bufio.go
@@ -206,10 +206,18 @@ func (b *Reader) Read(p []byte) (n int, err error) {
 			}
 			return n, b.readErr()
 		}
-		b.fill() // buffer is empty
-		if b.r == b.w {
+		// One read.
+		// Do not use b.fill, which will loop.
+		b.r = 0
+		b.w = 0
+		n, b.err = b.rd.Read(b.buf)
+		if n < 0 {
+			panic(errNegativeRead)
+		}
+		if n == 0 {
 			return 0, b.readErr()
 		}
+		b.w += n
 	}
 
 	// copy as much as we can
@@ -549,11 +557,6 @@ func (b *Writer) Reset(w io.Writer) {
 
 // Flush writes any buffered data to the underlying io.Writer.
 func (b *Writer) Flush() error {
-	err := b.flush()
-	return err
-}
-
-func (b *Writer) flush() error {
 	if b.err != nil {
 		return b.err
 	}
@@ -596,7 +599,7 @@ func (b *Writer) Write(p []byte) (nn int, err error) {
 		} else {
 			n = copy(b.buf[b.n:], p)
 			b.n += n
-			b.flush()
+			b.Flush()
 		}
 		nn += n
 		p = p[n:]
@@ -615,7 +618,7 @@ func (b *Writer) WriteByte(c byte) error {
 	if b.err != nil {
 		return b.err
 	}
-	if b.Available() <= 0 && b.flush() != nil {
+	if b.Available() <= 0 && b.Flush() != nil {
 		return b.err
 	}
 	b.buf[b.n] = c
@@ -638,7 +641,7 @@ func (b *Writer) WriteRune(r rune) (size int, err error) {
 	}
 	n := b.Available()
 	if n < utf8.UTFMax {
-		if b.flush(); b.err != nil {
+		if b.Flush(); b.err != nil {
 			return 0, b.err
 		}
 		n = b.Available()
@@ -663,7 +666,7 @@ func (b *Writer) WriteString(s string) (int, error) {
 		b.n += n
 		nn += n
 		s = s[n:]
-		b.flush()
+		b.Flush()
 	}
 	if b.err != nil {
 		return nn, b.err
@@ -684,7 +687,7 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
 	var m int
 	for {
 		if b.Available() == 0 {
-			if err1 := b.flush(); err1 != nil {
+			if err1 := b.Flush(); err1 != nil {
 				return n, err1
 			}
 		}
@@ -708,7 +711,7 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
 	if err == io.EOF {
 		// If we filled the buffer exactly, flush preemptively.
 		if b.Available() == 0 {
-			err = b.flush()
+			err = b.Flush()
 		} else {
 			err = nil
 		}
diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go
index 8580486..ef0f6c8 100644
--- a/src/bufio/bufio_test.go
+++ b/src/bufio/bufio_test.go
@@ -1236,6 +1236,27 @@ func TestWriterReadFromErrNoProgress(t *testing.T) {
 	}
 }
 
+func TestReadZero(t *testing.T) {
+	for _, size := range []int{100, 2} {
+		t.Run(fmt.Sprintf("bufsize=%d", size), func(t *testing.T) {
+			r := io.MultiReader(strings.NewReader("abc"), &emptyThenNonEmptyReader{r: strings.NewReader("def"), n: 1})
+			br := NewReaderSize(r, size)
+			want := func(s string, wantErr error) {
+				p := make([]byte, 50)
+				n, err := br.Read(p)
+				if err != wantErr || n != len(s) || string(p[:n]) != s {
+					t.Fatalf("read(%d) = %q, %v, want %q, %v", len(p), string(p[:n]), err, s, wantErr)
+				}
+				t.Logf("read(%d) = %q, %v", len(p), string(p[:n]), err)
+			}
+			want("abc", nil)
+			want("", nil)
+			want("def", nil)
+			want("", io.EOF)
+		})
+	}
+}
+
 func TestReaderReset(t *testing.T) {
 	r := NewReader(strings.NewReader("foo foo"))
 	buf := make([]byte, 3)
diff --git a/src/bufio/scan.go b/src/bufio/scan.go
index 27a0f00..9f741c9 100644
--- a/src/bufio/scan.go
+++ b/src/bufio/scan.go
@@ -199,7 +199,6 @@ func (s *Scanner) Scan() bool {
 			s.buf = newBuf
 			s.end -= s.start
 			s.start = 0
-			continue
 		}
 		// Finally we can read some input. Make sure we don't get stuck with
 		// a misbehaving Reader. Officially we don't need to do this, but let's
diff --git a/src/builtin/builtin.go b/src/builtin/builtin.go
index d63ad22..281de0b 100644
--- a/src/builtin/builtin.go
+++ b/src/builtin/builtin.go
@@ -173,8 +173,8 @@ func cap(v Type) int
 //	specify a different capacity; it must be no smaller than the
 //	length, so make([]int, 0, 10) allocates a slice of length 0 and
 //	capacity 10.
-//	Map: An initial allocation is made according to the size but the
-//	resulting map has length 0. The size may be omitted, in which case
+//	Map: An empty map is allocated with enough space to hold the
+//	specified number of elements. The size may be omitted, in which case
 //	a small starting size is allocated.
 //	Channel: The channel's buffer is initialized with the specified
 //	buffer capacity. If zero, or the size is omitted, the channel is
diff --git a/src/bytes/buffer.go b/src/bytes/buffer.go
index 9154a1b..2ee3d73 100644
--- a/src/bytes/buffer.go
+++ b/src/bytes/buffer.go
@@ -15,22 +15,25 @@ import (
 // A Buffer is a variable-sized buffer of bytes with Read and Write methods.
 // The zero value for Buffer is an empty buffer ready to use.
 type Buffer struct {
-	buf       []byte            // contents are the bytes buf[off : len(buf)]
-	off       int               // read at &buf[off], write at &buf[len(buf)]
-	runeBytes [utf8.UTFMax]byte // avoid allocation of slice on each call to WriteRune
-	bootstrap [64]byte          // memory to hold first slice; helps small buffers avoid allocation.
-	lastRead  readOp            // last read operation, so that Unread* can work correctly.
+	buf       []byte   // contents are the bytes buf[off : len(buf)]
+	off       int      // read at &buf[off], write at &buf[len(buf)]
+	bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation.
+	lastRead  readOp   // last read operation, so that Unread* can work correctly.
 }
 
 // The readOp constants describe the last action performed on
-// the buffer, so that UnreadRune and UnreadByte can
-// check for invalid usage.
+// the buffer, so that UnreadRune and UnreadByte can check for
+// invalid usage. opReadRuneX constants are choosen such that
+// converted to int they correspond to the rune size that was read.
 type readOp int
 
 const (
-	opInvalid  readOp = iota // Non-read operation.
-	opReadRune               // Read rune.
-	opRead                   // Any other read operation.
+	opRead      readOp = -1 // Any other read operation.
+	opInvalid          = 0  // Non-read operation.
+	opReadRune1        = 1  // Read rune of size 1.
+	opReadRune2        = 2  // Read rune of size 2.
+	opReadRune3        = 3  // Read rune of size 3.
+	opReadRune4        = 4  // Read rune of size 4.
 )
 
 // ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer.
@@ -246,8 +249,10 @@ func (b *Buffer) WriteRune(r rune) (n int, err error) {
 		b.WriteByte(byte(r))
 		return 1, nil
 	}
-	n = utf8.EncodeRune(b.runeBytes[0:], r)
-	b.Write(b.runeBytes[0:n])
+	b.lastRead = opInvalid
+	m := b.grow(utf8.UTFMax)
+	n = utf8.EncodeRune(b.buf[m:m+utf8.UTFMax], r)
+	b.buf = b.buf[:m+n]
 	return n, nil
 }
 
@@ -318,14 +323,15 @@ func (b *Buffer) ReadRune() (r rune, size int, err error) {
 		b.Truncate(0)
 		return 0, 0, io.EOF
 	}
-	b.lastRead = opReadRune
 	c := b.buf[b.off]
 	if c < utf8.RuneSelf {
 		b.off++
+		b.lastRead = opReadRune1
 		return rune(c), 1, nil
 	}
 	r, n := utf8.DecodeRune(b.buf[b.off:])
 	b.off += n
+	b.lastRead = readOp(n)
 	return r, n, nil
 }
 
@@ -335,14 +341,13 @@ func (b *Buffer) ReadRune() (r rune, size int, err error) {
 // it is stricter than UnreadByte, which will unread the last byte
 // from any read operation.)
 func (b *Buffer) UnreadRune() error {
-	if b.lastRead != opReadRune {
+	if b.lastRead <= opInvalid {
 		return errors.New("bytes.Buffer: UnreadRune: previous operation was not ReadRune")
 	}
-	b.lastRead = opInvalid
-	if b.off > 0 {
-		_, n := utf8.DecodeLastRune(b.buf[0:b.off])
-		b.off -= n
+	if b.off >= int(b.lastRead) {
+		b.off -= int(b.lastRead)
 	}
+	b.lastRead = opInvalid
 	return nil
 }
 
@@ -350,7 +355,7 @@ func (b *Buffer) UnreadRune() error {
 // read operation. If write has happened since the last read, UnreadByte
 // returns an error.
 func (b *Buffer) UnreadByte() error {
-	if b.lastRead != opReadRune && b.lastRead != opRead {
+	if b.lastRead == opInvalid {
 		return errors.New("bytes.Buffer: UnreadByte: previous operation was not a read")
 	}
 	b.lastRead = opInvalid
diff --git a/src/bytes/buffer_test.go b/src/bytes/buffer_test.go
index 7de17ae..b1b85f9 100644
--- a/src/bytes/buffer_test.go
+++ b/src/bytes/buffer_test.go
@@ -514,6 +514,19 @@ func TestBufferGrowth(t *testing.T) {
 	}
 }
 
+func BenchmarkWriteRune(b *testing.B) {
+	const n = 4 << 10
+	const r = '☺'
+	b.SetBytes(int64(n * utf8.RuneLen(r)))
+	buf := NewBuffer(make([]byte, n*utf8.UTFMax))
+	for i := 0; i < b.N; i++ {
+		buf.Reset()
+		for i := 0; i < n; i++ {
+			buf.WriteRune(r)
+		}
+	}
+}
+
 // From Issue 5154.
 func BenchmarkBufferNotEmptyWriteRead(b *testing.B) {
 	buf := make([]byte, 1024)
diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go
index 305c85d..406a382 100644
--- a/src/bytes/bytes.go
+++ b/src/bytes/bytes.go
@@ -93,37 +93,6 @@ func ContainsRune(b []byte, r rune) bool {
 	return IndexRune(b, r) >= 0
 }
 
-// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
-func Index(s, sep []byte) int {
-	n := len(sep)
-	if n == 0 {
-		return 0
-	}
-	if n > len(s) {
-		return -1
-	}
-	c := sep[0]
-	if n == 1 {
-		return IndexByte(s, c)
-	}
-	i := 0
-	t := s[:len(s)-n+1]
-	for i < len(t) {
-		if t[i] != c {
-			o := IndexByte(t[i:], c)
-			if o < 0 {
-				break
-			}
-			i += o
-		}
-		if Equal(s[i:i+n], sep) {
-			return i
-		}
-		i++
-	}
-	return -1
-}
-
 func indexBytePortable(s []byte, c byte) int {
 	for i, b := range s {
 		if b == c {
@@ -161,15 +130,28 @@ func LastIndexByte(s []byte, c byte) int {
 // IndexRune interprets s as a sequence of UTF-8-encoded Unicode code points.
 // It returns the byte index of the first occurrence in s of the given rune.
 // It returns -1 if rune is not present in s.
+// If r is utf8.RuneError, it returns the first instance of any
+// invalid UTF-8 byte sequence.
 func IndexRune(s []byte, r rune) int {
-	for i := 0; i < len(s); {
-		r1, size := utf8.DecodeRune(s[i:])
-		if r == r1 {
-			return i
+	switch {
+	case 0 <= r && r < utf8.RuneSelf:
+		return IndexByte(s, byte(r))
+	case r == utf8.RuneError:
+		for i := 0; i < len(s); {
+			r1, n := utf8.DecodeRune(s[i:])
+			if r1 == utf8.RuneError {
+				return i
+			}
+			i += n
 		}
-		i += size
+		return -1
+	case !utf8.ValidRune(r):
+		return -1
+	default:
+		var b [utf8.UTFMax]byte
+		n := utf8.EncodeRune(b[:], r)
+		return Index(s, b[:n])
 	}
-	return -1
 }
 
 // IndexAny interprets s as a sequence of UTF-8-encoded Unicode code points.
@@ -178,10 +160,19 @@ func IndexRune(s []byte, r rune) int {
 // point in common.
 func IndexAny(s []byte, chars string) int {
 	if len(chars) > 0 {
-		var r rune
+		if len(s) > 8 {
+			if as, isASCII := makeASCIISet(chars); isASCII {
+				for i, c := range s {
+					if as.contains(c) {
+						return i
+					}
+				}
+				return -1
+			}
+		}
 		var width int
 		for i := 0; i < len(s); i += width {
-			r = rune(s[i])
+			r := rune(s[i])
 			if r < utf8.RuneSelf {
 				width = 1
 			} else {
@@ -203,11 +194,21 @@ func IndexAny(s []byte, chars string) int {
 // there is no code point in common.
 func LastIndexAny(s []byte, chars string) int {
 	if len(chars) > 0 {
+		if len(s) > 8 {
+			if as, isASCII := makeASCIISet(chars); isASCII {
+				for i := len(s) - 1; i >= 0; i-- {
+					if as.contains(s[i]) {
+						return i
+					}
+				}
+				return -1
+			}
+		}
 		for i := len(s); i > 0; {
-			r, size := utf8.DecodeLastRune(s[0:i])
+			r, size := utf8.DecodeLastRune(s[:i])
 			i -= size
-			for _, ch := range chars {
-				if r == ch {
+			for _, c := range chars {
+				if r == c {
 					return i
 				}
 			}
@@ -398,7 +399,20 @@ func Map(mapping func(r rune) rune, s []byte) []byte {
 }
 
 // Repeat returns a new byte slice consisting of count copies of b.
+//
+// It panics if count is negative or if
+// the result of (len(b) * count) overflows.
 func Repeat(b []byte, count int) []byte {
+	// Since we cannot return an error on overflow,
+	// we should panic if the repeat will generate
+	// an overflow.
+	// See Issue golang.org/issue/16237.
+	if count < 0 {
+		panic("bytes: negative Repeat count")
+	} else if count > 0 && len(b)*count/count != len(b) {
+		panic("bytes: Repeat count causes overflow")
+	}
+
 	nb := make([]byte, len(b)*count)
 	bp := copy(nb, b)
 	for bp < len(nb) {
@@ -419,20 +433,20 @@ func ToTitle(s []byte) []byte { return Map(unicode.ToTitle, s) }
 
 // ToUpperSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
 // upper case, giving priority to the special casing rules.
-func ToUpperSpecial(_case unicode.SpecialCase, s []byte) []byte {
-	return Map(func(r rune) rune { return _case.ToUpper(r) }, s)
+func ToUpperSpecial(c unicode.SpecialCase, s []byte) []byte {
+	return Map(func(r rune) rune { return c.ToUpper(r) }, s)
 }
 
 // ToLowerSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
 // lower case, giving priority to the special casing rules.
-func ToLowerSpecial(_case unicode.SpecialCase, s []byte) []byte {
-	return Map(func(r rune) rune { return _case.ToLower(r) }, s)
+func ToLowerSpecial(c unicode.SpecialCase, s []byte) []byte {
+	return Map(func(r rune) rune { return c.ToLower(r) }, s)
 }
 
 // ToTitleSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
 // title case, giving priority to the special casing rules.
-func ToTitleSpecial(_case unicode.SpecialCase, s []byte) []byte {
-	return Map(func(r rune) rune { return _case.ToTitle(r) }, s)
+func ToTitleSpecial(c unicode.SpecialCase, s []byte) []byte {
+	return Map(func(r rune) rune { return c.ToTitle(r) }, s)
 }
 
 // isSeparator reports whether the rune could mark a word boundary.
@@ -578,7 +592,43 @@ func lastIndexFunc(s []byte, f func(r rune) bool, truth bool) int {
 	return -1
 }
 
+// asciiSet is a 32-byte value, where each bit represents the presence of a
+// given ASCII character in the set. The 128-bits of the lower 16 bytes,
+// starting with the least-significant bit of the lowest word to the
+// most-significant bit of the highest word, map to the full range of all
+// 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed,
+// ensuring that any non-ASCII character will be reported as not in the set.
+type asciiSet [8]uint32
+
+// makeASCIISet creates a set of ASCII characters and reports whether all
+// characters in chars are ASCII.
+func makeASCIISet(chars string) (as asciiSet, ok bool) {
+	for i := 0; i < len(chars); i++ {
+		c := chars[i]
+		if c >= utf8.RuneSelf {
+			return as, false
+		}
+		as[c>>5] |= 1 << uint(c&31)
+	}
+	return as, true
+}
+
+// contains reports whether c is inside the set.
+func (as *asciiSet) contains(c byte) bool {
+	return (as[c>>5] & (1 << uint(c&31))) != 0
+}
+
 func makeCutsetFunc(cutset string) func(r rune) bool {
+	if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
+		return func(r rune) bool {
+			return r == rune(cutset[0])
+		}
+	}
+	if as, isASCII := makeASCIISet(cutset); isASCII {
+		return func(r rune) bool {
+			return r < utf8.RuneSelf && as.contains(byte(r))
+		}
+	}
 	return func(r rune) bool {
 		for _, c := range cutset {
 			if c == r {
diff --git a/src/bytes/bytes_amd64.go b/src/bytes/bytes_amd64.go
new file mode 100644
index 0000000..6affff6
--- /dev/null
+++ b/src/bytes/bytes_amd64.go
@@ -0,0 +1,115 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bytes
+
+//go:noescape
+
+// indexShortStr returns the index of the first instance of c in s, or -1 if c is not present in s.
+// indexShortStr requires 2 <= len(c) <= shortStringLen
+func indexShortStr(s, c []byte) int // ../runtime/asm_$GOARCH.s
+func supportAVX2() bool             // ../runtime/asm_$GOARCH.s
+
+var shortStringLen int
+
+func init() {
+	if supportAVX2() {
+		shortStringLen = 63
+	} else {
+		shortStringLen = 31
+	}
+}
+
+// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
+func Index(s, sep []byte) int {
+	n := len(sep)
+	switch {
+	case n == 0:
+		return 0
+	case n == 1:
+		return IndexByte(s, sep[0])
+	case n == len(s):
+		if Equal(sep, s) {
+			return 0
+		}
+		return -1
+	case n > len(s):
+		return -1
+	case n <= shortStringLen:
+		// Use brute force when s and sep both are small
+		if len(s) <= 64 {
+			return indexShortStr(s, sep)
+		}
+		c := sep[0]
+		i := 0
+		t := s[:len(s)-n+1]
+		fails := 0
+		for i < len(t) {
+			if t[i] != c {
+				// IndexByte skips 16/32 bytes per iteration,
+				// so it's faster than indexShortStr.
+				o := IndexByte(t[i:], c)
+				if o < 0 {
+					return -1
+				}
+				i += o
+			}
+			if Equal(s[i:i+n], sep) {
+				return i
+			}
+			fails++
+			i++
+			// Switch to indexShortStr when IndexByte produces too many false positives.
+			// Too many means more that 1 error per 8 characters.
+			// Allow some errors in the beginning.
+			if fails > (i+16)/8 {
+				r := indexShortStr(s[i:], sep)
+				if r >= 0 {
+					return r + i
+				}
+				return -1
+			}
+		}
+		return -1
+	}
+	// Rabin-Karp search
+	hashsep, pow := hashStr(sep)
+	var h uint32
+	for i := 0; i < n; i++ {
+		h = h*primeRK + uint32(s[i])
+	}
+	if h == hashsep && Equal(s[:n], sep) {
+		return 0
+	}
+	for i := n; i < len(s); {
+		h *= primeRK
+		h += uint32(s[i])
+		h -= pow * uint32(s[i-n])
+		i++
+		if h == hashsep && Equal(s[i-n:i], sep) {
+			return i - n
+		}
+	}
+	return -1
+}
+
+// primeRK is the prime base used in Rabin-Karp algorithm.
+const primeRK = 16777619
+
+// hashStr returns the hash and the appropriate multiplicative
+// factor for use in Rabin-Karp algorithm.
+func hashStr(sep []byte) (uint32, uint32) {
+	hash := uint32(0)
+	for i := 0; i < len(sep); i++ {
+		hash = hash*primeRK + uint32(sep[i])
+	}
+	var pow, sq uint32 = 1, primeRK
+	for i := len(sep); i > 0; i >>= 1 {
+		if i&1 != 0 {
+			pow *= sq
+		}
+		sq *= sq
+	}
+	return hash, pow
+}
diff --git a/src/bytes/bytes_generic.go b/src/bytes/bytes_generic.go
new file mode 100644
index 0000000..06c9e1f
--- /dev/null
+++ b/src/bytes/bytes_generic.go
@@ -0,0 +1,41 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64,!s390x
+
+package bytes
+
+// TODO: implements short string optimization on non amd64 platforms
+// and get rid of bytes_amd64.go
+
+// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
+func Index(s, sep []byte) int {
+	n := len(sep)
+	if n == 0 {
+		return 0
+	}
+	if n > len(s) {
+		return -1
+	}
+	c := sep[0]
+	if n == 1 {
+		return IndexByte(s, c)
+	}
+	i := 0
+	t := s[:len(s)-n+1]
+	for i < len(t) {
+		if t[i] != c {
+			o := IndexByte(t[i:], c)
+			if o < 0 {
+				break
+			}
+			i += o
+		}
+		if Equal(s[i:i+n], sep) {
+			return i
+		}
+		i++
+	}
+	return -1
+}
diff --git a/src/bytes/bytes_s390x.go b/src/bytes/bytes_s390x.go
new file mode 100644
index 0000000..988c603
--- /dev/null
+++ b/src/bytes/bytes_s390x.go
@@ -0,0 +1,118 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package bytes
+
+//go:noescape
+
+// indexShortStr returns the index of the first instance of sep in s,
+// or -1 if sep is not present in s.
+// indexShortStr requires 2 <= len(sep) <= shortStringLen
+func indexShortStr(s, c []byte) int // ../runtime/asm_s390x.s
+
+// supportsVX reports whether the vector facility is available.
+// indexShortStr must not be called if the vector facility is not
+// available.
+func supportsVX() bool // ../runtime/asm_s390x.s
+
+var shortStringLen = -1
+
+func init() {
+	if supportsVX() {
+		shortStringLen = 64
+	}
+}
+
+// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
+func Index(s, sep []byte) int {
+	n := len(sep)
+	switch {
+	case n == 0:
+		return 0
+	case n == 1:
+		return IndexByte(s, sep[0])
+	case n == len(s):
+		if Equal(sep, s) {
+			return 0
+		}
+		return -1
+	case n > len(s):
+		return -1
+	case n <= shortStringLen:
+		// Use brute force when s and sep both are small
+		if len(s) <= 64 {
+			return indexShortStr(s, sep)
+		}
+		c := sep[0]
+		i := 0
+		t := s[:len(s)-n+1]
+		fails := 0
+		for i < len(t) {
+			if t[i] != c {
+				// IndexByte skips 16/32 bytes per iteration,
+				// so it's faster than indexShortStr.
+				o := IndexByte(t[i:], c)
+				if o < 0 {
+					return -1
+				}
+				i += o
+			}
+			if Equal(s[i:i+n], sep) {
+				return i
+			}
+			fails++
+			i++
+			// Switch to indexShortStr when IndexByte produces too many false positives.
+			// Too many means more that 1 error per 8 characters.
+			// Allow some errors in the beginning.
+			if fails > (i+16)/8 {
+				r := indexShortStr(s[i:], sep)
+				if r >= 0 {
+					return r + i
+				}
+				return -1
+			}
+		}
+		return -1
+	}
+	// Rabin-Karp search
+	hashsep, pow := hashStr(sep)
+	var h uint32
+	for i := 0; i < n; i++ {
+		h = h*primeRK + uint32(s[i])
+	}
+	if h == hashsep && Equal(s[:n], sep) {
+		return 0
+	}
+	for i := n; i < len(s); {
+		h *= primeRK
+		h += uint32(s[i])
+		h -= pow * uint32(s[i-n])
+		i++
+		if h == hashsep && Equal(s[i-n:i], sep) {
+			return i - n
+		}
+	}
+	return -1
+}
+
+// primeRK is the prime base used in Rabin-Karp algorithm.
+const primeRK = 16777619
+
+// hashStr returns the hash and the appropriate multiplicative
+// factor for use in Rabin-Karp algorithm.
+func hashStr(sep []byte) (uint32, uint32) {
+	hash := uint32(0)
+	for i := 0; i < len(sep); i++ {
+		hash = hash*primeRK + uint32(sep[i])
+	}
+	var pow, sq uint32 = 1, primeRK
+	for i := len(sep); i > 0; i >>= 1 {
+		if i&1 != 0 {
+			pow *= sq
+		}
+		sq *= sq
+	}
+	return hash, pow
+}
diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go
index c48f662..26eac5e 100644
--- a/src/bytes/bytes_test.go
+++ b/src/bytes/bytes_test.go
@@ -7,8 +7,10 @@ package bytes_test
 import (
 	. "bytes"
 	"fmt"
+	"internal/testenv"
 	"math/rand"
 	"reflect"
+	"strings"
 	"testing"
 	"unicode"
 	"unicode/utf8"
@@ -165,8 +167,12 @@ var indexAnyTests = []BinOpTest{
 	{"abc", "xyz", -1},
 	{"abc", "xcz", 2},
 	{"ab☺c", "x☺yz", 2},
+	{"a☺b☻c☹d", "cx", len("a☺b☻")},
+	{"a☺b☻c☹d", "uvw☻xyz", len("a☺b")},
 	{"aRegExp*", ".(|)*+?^$[]", 7},
 	{dots + dots + dots, " ", -1},
+	{"012abcba210", "\xffb", 4},
+	{"012\x80bcb\x80210", "\xffb", 3},
 }
 
 var lastIndexAnyTests = []BinOpTest{
@@ -178,18 +184,13 @@ var lastIndexAnyTests = []BinOpTest{
 	{"aaa", "a", 2},
 	{"abc", "xyz", -1},
 	{"abc", "ab", 1},
-	{"a☺b☻c☹d", "uvw☻xyz", 2 + len("☺")},
+	{"ab☺c", "x☺yz", 2},
+	{"a☺b☻c☹d", "cx", len("a☺b☻")},
+	{"a☺b☻c☹d", "uvw☻xyz", len("a☺b")},
 	{"a.RegExp*", ".(|)*+?^$[]", 8},
 	{dots + dots + dots, " ", -1},
-}
-
-var indexRuneTests = []BinOpTest{
-	{"", "a", -1},
-	{"", "☺", -1},
-	{"foo", "☹", -1},
-	{"foo", "o", 1},
-	{"foo☺bar", "☺", 3},
-	{"foo☺☻☹bar", "☹", 9},
+	{"012abcba210", "\xffb", 6},
+	{"012\x80bcb\x80210", "\xffb", 7},
 }
 
 // Execute f on each test case.  funcName should be the name of f; it's used
@@ -346,13 +347,52 @@ func TestIndexByteSmall(t *testing.T) {
 }
 
 func TestIndexRune(t *testing.T) {
-	for _, tt := range indexRuneTests {
-		a := []byte(tt.a)
-		r, _ := utf8.DecodeRuneInString(tt.b)
-		pos := IndexRune(a, r)
-		if pos != tt.i {
-			t.Errorf(`IndexRune(%q, '%c') = %v`, tt.a, r, pos)
+	tests := []struct {
+		in   string
+		rune rune
+		want int
+	}{
+		{"", 'a', -1},
+		{"", '☺', -1},
+		{"foo", '☹', -1},
+		{"foo", 'o', 1},
+		{"foo☺bar", '☺', 3},
+		{"foo☺☻☹bar", '☹', 9},
+		{"a A x", 'A', 2},
+		{"some_text=some_value", '=', 9},
+		{"☺a", 'a', 3},
+		{"a☻☺b", '☺', 4},
+
+		// RuneError should match any invalid UTF-8 byte sequence.
+		{"�", '�', 0},
+		{"\xff", '�', 0},
+		{"☻x�", '�', len("☻x")},
+		{"☻x\xe2\x98", '�', len("☻x")},
+		{"☻x\xe2\x98�", '�', len("☻x")},
+		{"☻x\xe2\x98x", '�', len("☻x")},
+
+		// Invalid rune values should never match.
+		{"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", -1, -1},
+		{"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", 0xD800, -1}, // Surrogate pair
+		{"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", utf8.MaxRune + 1, -1},
+	}
+	for _, tt := range tests {
+		if got := IndexRune([]byte(tt.in), tt.rune); got != tt.want {
+			t.Errorf("IndexRune(%q, %d) = %v; want %v", tt.in, tt.rune, got, tt.want)
+		}
+	}
+
+	haystack := []byte("test世界")
+	allocs := testing.AllocsPerRun(1000, func() {
+		if i := IndexRune(haystack, 's'); i != 2 {
+			t.Fatalf("'s' at %d; want 2", i)
+		}
+		if i := IndexRune(haystack, '世'); i != 4 {
+			t.Fatalf("'世' at %d; want 4", i)
 		}
+	})
+	if allocs != 0 {
+		t.Errorf("expected no allocations, got %f", allocs)
 	}
 }
 
@@ -370,6 +410,9 @@ func valName(x int) string {
 
 func benchBytes(b *testing.B, sizes []int, f func(b *testing.B, n int)) {
 	for _, n := range sizes {
+		if isRaceBuilder && n > 4<<10 {
+			continue
+		}
 		b.Run(valName(n), func(b *testing.B) {
 			if len(bmbuf) < n {
 				bmbuf = make([]byte, n)
@@ -382,6 +425,8 @@ func benchBytes(b *testing.B, sizes []int, f func(b *testing.B, n int)) {
 
 var indexSizes = []int{10, 32, 4 << 10, 4 << 20, 64 << 20}
 
+var isRaceBuilder = strings.HasSuffix(testenv.Builder(), "-race")
+
 func BenchmarkIndexByte(b *testing.B) {
 	benchBytes(b, indexSizes, bmIndexByte(IndexByte))
 }
@@ -404,6 +449,44 @@ func bmIndexByte(index func([]byte, byte) int) func(b *testing.B, n int) {
 	}
 }
 
+func BenchmarkIndexRune(b *testing.B) {
+	benchBytes(b, indexSizes, bmIndexRune(IndexRune))
+}
+
+func BenchmarkIndexRuneASCII(b *testing.B) {
+	benchBytes(b, indexSizes, bmIndexRuneASCII(IndexRune))
+}
+
+func bmIndexRuneASCII(index func([]byte, rune) int) func(b *testing.B, n int) {
+	return func(b *testing.B, n int) {
+		buf := bmbuf[0:n]
+		buf[n-1] = 'x'
+		for i := 0; i < b.N; i++ {
+			j := index(buf, 'x')
+			if j != n-1 {
+				b.Fatal("bad index", j)
+			}
+		}
+		buf[n-1] = '\x00'
+	}
+}
+
+func bmIndexRune(index func([]byte, rune) int) func(b *testing.B, n int) {
+	return func(b *testing.B, n int) {
+		buf := bmbuf[0:n]
+		utf8.EncodeRune(buf[n-3:], '世')
+		for i := 0; i < b.N; i++ {
+			j := index(buf, '世')
+			if j != n-3 {
+				b.Fatal("bad index", j)
+			}
+		}
+		buf[n-3] = '\x00'
+		buf[n-2] = '\x00'
+		buf[n-1] = '\x00'
+	}
+}
+
 func BenchmarkEqual(b *testing.B) {
 	b.Run("0", func(b *testing.B) {
 		var buf [4]byte
@@ -844,6 +927,54 @@ func TestRepeat(t *testing.T) {
 	}
 }
 
+func repeat(b []byte, count int) (err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			switch v := r.(type) {
+			case error:
+				err = v
+			default:
+				err = fmt.Errorf("%s", v)
+			}
+		}
+	}()
+
+	Repeat(b, count)
+
+	return
+}
+
+// See Issue golang.org/issue/16237
+func TestRepeatCatchesOverflow(t *testing.T) {
+	tests := [...]struct {
+		s      string
+		count  int
+		errStr string
+	}{
+		0: {"--", -2147483647, "negative"},
+		1: {"", int(^uint(0) >> 1), ""},
+		2: {"-", 10, ""},
+		3: {"gopher", 0, ""},
+		4: {"-", -1, "negative"},
+		5: {"--", -102, "negative"},
+		6: {string(make([]byte, 255)), int((^uint(0))/255 + 1), "overflow"},
+	}
+
+	for i, tt := range tests {
+		err := repeat([]byte(tt.s), tt.count)
+		if tt.errStr == "" {
+			if err != nil {
+				t.Errorf("#%d panicked %v", i, err)
+			}
+			continue
+		}
+
+		if err == nil || !strings.Contains(err.Error(), tt.errStr) {
+			t.Errorf("#%d expected %q got %q", i, tt.errStr, err)
+		}
+	}
+}
+
 func runesEqual(a, b []rune) bool {
 	if len(a) != len(b) {
 		return false
@@ -906,6 +1037,9 @@ var trimTests = []TrimTest{
 	{"Trim", "* listitem", " *", "listitem"},
 	{"Trim", `"quote"`, `"`, "quote"},
 	{"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"},
+	{"Trim", "\x80test\xff", "\xff", "test"},
+	{"Trim", " Ġ ", " ", "Ġ"},
+	{"Trim", " Ġİ0", "0 ", "Ġİ"},
 	//empty string tests
 	{"Trim", "abba", "", "abba"},
 	{"Trim", "", "123", ""},
@@ -1325,3 +1459,31 @@ func BenchmarkBytesCompare(b *testing.B) {
 		})
 	}
 }
+
+func BenchmarkIndexAnyASCII(b *testing.B) {
+	x := Repeat([]byte{'#'}, 4096) // Never matches set
+	cs := "0123456789abcdef"
+	for k := 1; k <= 4096; k <<= 4 {
+		for j := 1; j <= 16; j <<= 1 {
+			b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
+				for i := 0; i < b.N; i++ {
+					IndexAny(x[:k], cs[:j])
+				}
+			})
+		}
+	}
+}
+
+func BenchmarkTrimASCII(b *testing.B) {
+	cs := "0123456789abcdef"
+	for k := 1; k <= 4096; k <<= 4 {
+		for j := 1; j <= 16; j <<= 1 {
+			b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
+				x := Repeat([]byte(cs[:j]), k) // Always matches set
+				for i := 0; i < b.N; i++ {
+					Trim(x[:k], cs[:j])
+				}
+			})
+		}
+	}
+}
diff --git a/src/bytes/example_test.go b/src/bytes/example_test.go
index ad2dbc6..0d35a0d 100644
--- a/src/bytes/example_test.go
+++ b/src/bytes/example_test.go
@@ -11,6 +11,7 @@ import (
 	"io"
 	"os"
 	"sort"
+	"unicode"
 )
 
 func ExampleBuffer() {
@@ -83,3 +84,205 @@ func ExampleTrimPrefix() {
 	fmt.Printf("Hello%s", b)
 	// Output: Hello, world!
 }
+
+func ExampleFields() {
+	fmt.Printf("Fields are: %q", bytes.Fields([]byte("  foo bar  baz   ")))
+	// Output: Fields are: ["foo" "bar" "baz"]
+}
+
+func ExampleFieldsFunc() {
+	f := func(c rune) bool {
+		return !unicode.IsLetter(c) && !unicode.IsNumber(c)
+	}
+	fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte("  foo1;bar2,baz3..."), f))
+	// Output: Fields are: ["foo1" "bar2" "baz3"]
+}
+
+func ExampleContains() {
+	fmt.Println(bytes.Contains([]byte("seafood"), []byte("foo")))
+	fmt.Println(bytes.Contains([]byte("seafood"), []byte("bar")))
+	fmt.Println(bytes.Contains([]byte("seafood"), []byte("")))
+	fmt.Println(bytes.Contains([]byte(""), []byte("")))
+	// Output:
+	// true
+	// false
+	// true
+	// true
+}
+
+func ExampleCount() {
+	fmt.Println(bytes.Count([]byte("cheese"), []byte("e")))
+	fmt.Println(bytes.Count([]byte("five"), []byte(""))) // before & after each rune
+	// Output:
+	// 3
+	// 5
+}
+
+func ExampleEqualFold() {
+	fmt.Println(bytes.EqualFold([]byte("Go"), []byte("go")))
+	// Output: true
+}
+
+func ExampleHasPrefix() {
+	fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("Go")))
+	fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("C")))
+	fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("")))
+	// Output:
+	// true
+	// false
+	// true
+}
+
+func ExampleHasSuffix() {
+	fmt.Println(bytes.HasSuffix([]byte("Amigo"), []byte("go")))
+	fmt.Println(bytes.HasSuffix([]byte("Amigo"), []byte("O")))
+	fmt.Println(bytes.HasSuffix([]byte("Amigo"), []byte("Ami")))
+	fmt.Println(bytes.HasSuffix([]byte("Amigo"), []byte("")))
+	// Output:
+	// true
+	// false
+	// false
+	// true
+}
+
+func ExampleIndex() {
+	fmt.Println(bytes.Index([]byte("chicken"), []byte("ken")))
+	fmt.Println(bytes.Index([]byte("chicken"), []byte("dmr")))
+	// Output:
+	// 4
+	// -1
+}
+
+func ExampleIndexFunc() {
+	f := func(c rune) bool {
+		return unicode.Is(unicode.Han, c)
+	}
+	fmt.Println(bytes.IndexFunc([]byte("Hello, 世界"), f))
+	fmt.Println(bytes.IndexFunc([]byte("Hello, world"), f))
+	// Output:
+	// 7
+	// -1
+}
+
+func ExampleIndexAny() {
+	fmt.Println(bytes.IndexAny([]byte("chicken"), "aeiouy"))
+	fmt.Println(bytes.IndexAny([]byte("crwth"), "aeiouy"))
+	// Output:
+	// 2
+	// -1
+}
+
+func ExampleIndexRune() {
+	fmt.Println(bytes.IndexRune([]byte("chicken"), 'k'))
+	fmt.Println(bytes.IndexRune([]byte("chicken"), 'd'))
+	// Output:
+	// 4
+	// -1
+}
+
+func ExampleLastIndex() {
+	fmt.Println(bytes.Index([]byte("go gopher"), []byte("go")))
+	fmt.Println(bytes.LastIndex([]byte("go gopher"), []byte("go")))
+	fmt.Println(bytes.LastIndex([]byte("go gopher"), []byte("rodent")))
+	// Output:
+	// 0
+	// 3
+	// -1
+}
+
+func ExampleJoin() {
+	s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")}
+	fmt.Printf("%s", bytes.Join(s, []byte(", ")))
+	// Output: foo, bar, baz
+}
+
+func ExampleRepeat() {
+	fmt.Printf("ba%s", bytes.Repeat([]byte("na"), 2))
+	// Output: banana
+}
+
+func ExampleReplace() {
+	fmt.Printf("%s\n", bytes.Replace([]byte("oink oink oink"), []byte("k"), []byte("ky"), 2))
+	fmt.Printf("%s\n", bytes.Replace([]byte("oink oink oink"), []byte("oink"), []byte("moo"), -1))
+	// Output:
+	// oinky oinky oink
+	// moo moo moo
+}
+
+func ExampleSplit() {
+	fmt.Printf("%q\n", bytes.Split([]byte("a,b,c"), []byte(",")))
+	fmt.Printf("%q\n", bytes.Split([]byte("a man a plan a canal panama"), []byte("a ")))
+	fmt.Printf("%q\n", bytes.Split([]byte(" xyz "), []byte("")))
+	fmt.Printf("%q\n", bytes.Split([]byte(""), []byte("Bernardo O'Higgins")))
+	// Output:
+	// ["a" "b" "c"]
+	// ["" "man " "plan " "canal panama"]
+	// [" " "x" "y" "z" " "]
+	// [""]
+}
+
+func ExampleSplitN() {
+	fmt.Printf("%q\n", bytes.SplitN([]byte("a,b,c"), []byte(","), 2))
+	z := bytes.SplitN([]byte("a,b,c"), []byte(","), 0)
+	fmt.Printf("%q (nil = %v)\n", z, z == nil)
+	// Output:
+	// ["a" "b,c"]
+	// [] (nil = true)
+}
+
+func ExampleSplitAfter() {
+	fmt.Printf("%q\n", bytes.SplitAfter([]byte("a,b,c"), []byte(",")))
+	// Output: ["a," "b," "c"]
+}
+
+func ExampleSplitAfterN() {
+	fmt.Printf("%q\n", bytes.SplitAfterN([]byte("a,b,c"), []byte(","), 2))
+	// Output: ["a," "b,c"]
+}
+
+func ExampleTitle() {
+	fmt.Printf("%s", bytes.Title([]byte("her royal highness")))
+	// Output: Her Royal Highness
+}
+
+func ExampleToTitle() {
+	fmt.Printf("%s\n", bytes.ToTitle([]byte("loud noises")))
+	fmt.Printf("%s\n", bytes.ToTitle([]byte("хлеб")))
+	// Output:
+	// LOUD NOISES
+	// ХЛЕБ
+}
+
+func ExampleTrim() {
+	fmt.Printf("[%q]", bytes.Trim([]byte(" !!! Achtung! Achtung! !!! "), "! "))
+	// Output: ["Achtung! Achtung"]
+}
+
+func ExampleMap() {
+	rot13 := func(r rune) rune {
+		switch {
+		case r >= 'A' && r <= 'Z':
+			return 'A' + (r-'A'+13)%26
+		case r >= 'a' && r <= 'z':
+			return 'a' + (r-'a'+13)%26
+		}
+		return r
+	}
+	fmt.Printf("%s", bytes.Map(rot13, []byte("'Twas brillig and the slithy gopher...")))
+	// Output: 'Gjnf oevyyvt naq gur fyvgul tbcure...
+}
+
+func ExampleTrimSpace() {
+	fmt.Printf("%s", bytes.TrimSpace([]byte(" \t\n a lone gopher \n\t\r\n")))
+	// Output: a lone gopher
+}
+
+func ExampleToUpper() {
+	fmt.Printf("%s", bytes.ToUpper([]byte("Gopher")))
+	// Output: GOPHER
+}
+
+func ExampleToLower() {
+	fmt.Printf("%s", bytes.ToLower([]byte("Gopher")))
+	// Output: gopher
+}
diff --git a/src/cmd/addr2line/addr2line_test.go b/src/cmd/addr2line/addr2line_test.go
index 620b416..2bd2e35 100644
--- a/src/cmd/addr2line/addr2line_test.go
+++ b/src/cmd/addr2line/addr2line_test.go
@@ -18,7 +18,7 @@ import (
 )
 
 func loadSyms(t *testing.T) map[string]string {
-	cmd := exec.Command("go", "tool", "nm", os.Args[0])
+	cmd := exec.Command(testenv.GoToolPath(t), "tool", "nm", os.Args[0])
 	out, err := cmd.CombinedOutput()
 	if err != nil {
 		t.Fatalf("go tool nm %v: %v\n%s", os.Args[0], err, string(out))
@@ -98,7 +98,7 @@ func TestAddr2Line(t *testing.T) {
 	defer os.RemoveAll(tmpDir)
 
 	exepath := filepath.Join(tmpDir, "testaddr2line.exe")
-	out, err := exec.Command("go", "build", "-o", exepath, "cmd/addr2line").CombinedOutput()
+	out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", exepath, "cmd/addr2line").CombinedOutput()
 	if err != nil {
 		t.Fatalf("go build -o %v cmd/addr2line: %v\n%s", exepath, err, string(out))
 	}
diff --git a/src/cmd/api/run.go b/src/cmd/api/run.go
index c8433c5..1753644 100644
--- a/src/cmd/api/run.go
+++ b/src/cmd/api/run.go
@@ -26,7 +26,7 @@ func main() {
 	}
 
 	out, err := exec.Command("go", "tool", "api",
-		"-c", file("go1", "go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7"),
+		"-c", file("go1", "go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7", "go1.8"),
 		"-next", file("next"),
 		"-except", file("except")).CombinedOutput()
 	if err != nil {
diff --git a/src/cmd/asm/internal/arch/amd64.go b/src/cmd/asm/internal/arch/amd64.go
index 625e136..ff20d32 100644
--- a/src/cmd/asm/internal/arch/amd64.go
+++ b/src/cmd/asm/internal/arch/amd64.go
@@ -13,8 +13,8 @@ import (
 	"cmd/internal/obj/x86"
 )
 
-// IsAMD4OP reports whether the op (as defined by an ppc64.A* constant) is
-// The FMADD-like instructions behave similarly.
+// IsAMD4OP reports whether the op (as defined by an amd64.A* constant) is
+// a 4-operand instruction.
 func IsAMD4OP(op obj.As) bool {
 	switch op {
 	case x86.AVPERM2F128,
diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go
index 4b5b46a..9110ca7 100644
--- a/src/cmd/asm/internal/arch/arch.go
+++ b/src/cmd/asm/internal/arch/arch.go
@@ -59,6 +59,14 @@ func Set(GOARCH string) *Arch {
 		return archArm()
 	case "arm64":
 		return archArm64()
+	case "mips":
+		a := archMips()
+		a.LinkArch = &mips.Linkmips
+		return a
+	case "mipsle":
+		a := archMips()
+		a.LinkArch = &mips.Linkmipsle
+		return a
 	case "mips64":
 		a := archMips64()
 		a.LinkArch = &mips.Linkmips64
@@ -319,6 +327,12 @@ func archPPC64() *Arch {
 	for i := ppc64.REG_F0; i <= ppc64.REG_F31; i++ {
 		register[obj.Rconv(i)] = int16(i)
 	}
+	for i := ppc64.REG_V0; i <= ppc64.REG_V31; i++ {
+		register[obj.Rconv(i)] = int16(i)
+	}
+	for i := ppc64.REG_VS0; i <= ppc64.REG_VS63; i++ {
+		register[obj.Rconv(i)] = int16(i)
+	}
 	for i := ppc64.REG_CR0; i <= ppc64.REG_CR7; i++ {
 		register[obj.Rconv(i)] = int16(i)
 	}
@@ -368,6 +382,62 @@ func archPPC64() *Arch {
 	}
 }
 
+func archMips() *Arch {
+	register := make(map[string]int16)
+	// Create maps for easy lookup of instruction names etc.
+	// Note that there is no list of names as there is for x86.
+	for i := mips.REG_R0; i <= mips.REG_R31; i++ {
+		register[obj.Rconv(i)] = int16(i)
+	}
+
+	for i := mips.REG_F0; i <= mips.REG_F31; i++ {
+		register[obj.Rconv(i)] = int16(i)
+	}
+	for i := mips.REG_M0; i <= mips.REG_M31; i++ {
+		register[obj.Rconv(i)] = int16(i)
+	}
+	for i := mips.REG_FCR0; i <= mips.REG_FCR31; i++ {
+		register[obj.Rconv(i)] = int16(i)
+	}
+	register["HI"] = mips.REG_HI
+	register["LO"] = mips.REG_LO
+	// Pseudo-registers.
+	register["SB"] = RSB
+	register["FP"] = RFP
+	register["PC"] = RPC
+	// Avoid unintentionally clobbering g using R30.
+	delete(register, "R30")
+	register["g"] = mips.REG_R30
+
+	registerPrefix := map[string]bool{
+		"F":   true,
+		"FCR": true,
+		"M":   true,
+		"R":   true,
+	}
+
+	instructions := make(map[string]obj.As)
+	for i, s := range obj.Anames {
+		instructions[s] = obj.As(i)
+	}
+	for i, s := range mips.Anames {
+		if obj.As(i) >= obj.A_ARCHSPECIFIC {
+			instructions[s] = obj.As(i) + obj.ABaseMIPS
+		}
+	}
+	// Annoying alias.
+	instructions["JAL"] = mips.AJAL
+
+	return &Arch{
+		LinkArch:       &mips.Linkmipsle,
+		Instructions:   instructions,
+		Register:       register,
+		RegisterPrefix: registerPrefix,
+		RegisterNumber: mipsRegisterNumber,
+		IsJump:         jumpMIPS,
+	}
+}
+
 func archMips64() *Arch {
 	register := make(map[string]int16)
 	// Create maps for easy lookup of instruction names etc.
@@ -409,7 +479,7 @@ func archMips64() *Arch {
 	}
 	for i, s := range mips.Anames {
 		if obj.As(i) >= obj.A_ARCHSPECIFIC {
-			instructions[s] = obj.As(i) + obj.ABaseMIPS64
+			instructions[s] = obj.As(i) + obj.ABaseMIPS
 		}
 	}
 	// Annoying alias.
@@ -421,7 +491,7 @@ func archMips64() *Arch {
 		Register:       register,
 		RegisterPrefix: registerPrefix,
 		RegisterNumber: mipsRegisterNumber,
-		IsJump:         jumpMIPS64,
+		IsJump:         jumpMIPS,
 	}
 }
 
diff --git a/src/cmd/asm/internal/arch/mips.go b/src/cmd/asm/internal/arch/mips.go
new file mode 100644
index 0000000..14b2933
--- /dev/null
+++ b/src/cmd/asm/internal/arch/mips.go
@@ -0,0 +1,67 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file encapsulates some of the odd characteristics of the
+// MIPS (MIPS64) instruction set, to minimize its interaction
+// with the core of the assembler.
+
+package arch
+
+import (
+	"cmd/internal/obj"
+	"cmd/internal/obj/mips"
+)
+
+func jumpMIPS(word string) bool {
+	switch word {
+	case "BEQ", "BFPF", "BFPT", "BGEZ", "BGEZAL", "BGTZ", "BLEZ", "BLTZ", "BLTZAL", "BNE", "JMP", "JAL", "CALL":
+		return true
+	}
+	return false
+}
+
+// IsMIPSCMP reports whether the op (as defined by an mips.A* constant) is
+// one of the CMP instructions that require special handling.
+func IsMIPSCMP(op obj.As) bool {
+	switch op {
+	case mips.ACMPEQF, mips.ACMPEQD, mips.ACMPGEF, mips.ACMPGED,
+		mips.ACMPGTF, mips.ACMPGTD:
+		return true
+	}
+	return false
+}
+
+// IsMIPSMUL reports whether the op (as defined by an mips.A* constant) is
+// one of the MUL/DIV/REM instructions that require special handling.
+func IsMIPSMUL(op obj.As) bool {
+	switch op {
+	case mips.AMUL, mips.AMULU, mips.AMULV, mips.AMULVU,
+		mips.ADIV, mips.ADIVU, mips.ADIVV, mips.ADIVVU,
+		mips.AREM, mips.AREMU, mips.AREMV, mips.AREMVU:
+		return true
+	}
+	return false
+}
+
+func mipsRegisterNumber(name string, n int16) (int16, bool) {
+	switch name {
+	case "F":
+		if 0 <= n && n <= 31 {
+			return mips.REG_F0 + n, true
+		}
+	case "FCR":
+		if 0 <= n && n <= 31 {
+			return mips.REG_FCR0 + n, true
+		}
+	case "M":
+		if 0 <= n && n <= 31 {
+			return mips.REG_M0 + n, true
+		}
+	case "R":
+		if 0 <= n && n <= 31 {
+			return mips.REG_R0 + n, true
+		}
+	}
+	return 0, false
+}
diff --git a/src/cmd/asm/internal/arch/mips64.go b/src/cmd/asm/internal/arch/mips64.go
deleted file mode 100644
index dd93cfb..0000000
--- a/src/cmd/asm/internal/arch/mips64.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file encapsulates some of the odd characteristics of the
-// 64-bit MIPS (MIPS64) instruction set, to minimize its interaction
-// with the core of the assembler.
-
-package arch
-
-import (
-	"cmd/internal/obj"
-	"cmd/internal/obj/mips"
-)
-
-func jumpMIPS64(word string) bool {
-	switch word {
-	case "BEQ", "BFPF", "BFPT", "BGEZ", "BGEZAL", "BGTZ", "BLEZ", "BLTZ", "BLTZAL", "BNE", "JMP", "JAL", "CALL":
-		return true
-	}
-	return false
-}
-
-// IsMIPS64CMP reports whether the op (as defined by an mips.A* constant) is
-// one of the CMP instructions that require special handling.
-func IsMIPS64CMP(op obj.As) bool {
-	switch op {
-	case mips.ACMPEQF, mips.ACMPEQD, mips.ACMPGEF, mips.ACMPGED,
-		mips.ACMPGTF, mips.ACMPGTD:
-		return true
-	}
-	return false
-}
-
-// IsMIPS64MUL reports whether the op (as defined by an mips.A* constant) is
-// one of the MUL/DIV/REM instructions that require special handling.
-func IsMIPS64MUL(op obj.As) bool {
-	switch op {
-	case mips.AMUL, mips.AMULU, mips.AMULV, mips.AMULVU,
-		mips.ADIV, mips.ADIVU, mips.ADIVV, mips.ADIVVU,
-		mips.AREM, mips.AREMU, mips.AREMV, mips.AREMVU:
-		return true
-	}
-	return false
-}
-
-func mipsRegisterNumber(name string, n int16) (int16, bool) {
-	switch name {
-	case "F":
-		if 0 <= n && n <= 31 {
-			return mips.REG_F0 + n, true
-		}
-	case "FCR":
-		if 0 <= n && n <= 31 {
-			return mips.REG_FCR0 + n, true
-		}
-	case "M":
-		if 0 <= n && n <= 31 {
-			return mips.REG_M0 + n, true
-		}
-	case "R":
-		if 0 <= n && n <= 31 {
-			return mips.REG_R0 + n, true
-		}
-	}
-	return 0, false
-}
diff --git a/src/cmd/asm/internal/arch/ppc64.go b/src/cmd/asm/internal/arch/ppc64.go
index fef2565..7e3d55b 100644
--- a/src/cmd/asm/internal/arch/ppc64.go
+++ b/src/cmd/asm/internal/arch/ppc64.go
@@ -39,6 +39,10 @@ func IsPPC64RLD(op obj.As) bool {
 	return false
 }
 
+func IsPPC64ISEL(op obj.As) bool {
+	return op == ppc64.AISEL
+}
+
 // IsPPC64CMP reports whether the op (as defined by an ppc64.A* constant) is
 // one of the CMP instructions that require special handling.
 func IsPPC64CMP(op obj.As) bool {
@@ -73,6 +77,14 @@ func ppc64RegisterNumber(name string, n int16) (int16, bool) {
 		if 0 <= n && n <= 7 {
 			return ppc64.REG_CR0 + n, true
 		}
+	case "VS":
+		if 0 <= n && n <= 63 {
+			return ppc64.REG_VS0 + n, true
+		}
+	case "V":
+		if 0 <= n && n <= 31 {
+			return ppc64.REG_V0 + n, true
+		}
 	case "F":
 		if 0 <= n && n <= 31 {
 			return ppc64.REG_F0 + n, true
diff --git a/src/cmd/asm/internal/arch/s390x.go b/src/cmd/asm/internal/arch/s390x.go
index 6fa0292..1836f87 100644
--- a/src/cmd/asm/internal/arch/s390x.go
+++ b/src/cmd/asm/internal/arch/s390x.go
@@ -22,7 +22,9 @@ func jumpS390x(word string) bool {
 		"BGT",
 		"BL",
 		"BLE",
+		"BLEU",
 		"BLT",
+		"BLTU",
 		"BNE",
 		"BR",
 		"BVC",
@@ -78,11 +80,7 @@ func IsS390xCMP(op obj.As) bool {
 // one of the NEG-like instructions that require special handling.
 func IsS390xNEG(op obj.As) bool {
 	switch op {
-	case s390x.AADDME,
-		s390x.AADDZE,
-		s390x.ANEG,
-		s390x.ASUBME,
-		s390x.ASUBZE:
+	case s390x.ANEG, s390x.ANEGW:
 		return true
 	}
 	return false
@@ -110,6 +108,10 @@ func IsS390xWithIndex(op obj.As) bool {
 		return true
 	case s390x.AVLEIG, s390x.AVLEIF, s390x.AVLEIH, s390x.AVLEIB:
 		return true
+	case s390x.AVLEG, s390x.AVLEF, s390x.AVLEH, s390x.AVLEB:
+		return true
+	case s390x.AVSTEG, s390x.AVSTEF, s390x.AVSTEH, s390x.AVSTEB:
+		return true
 	case s390x.AVPDI:
 		return true
 	}
diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go
index c9c6420..d7c5687 100644
--- a/src/cmd/asm/internal/asm/asm.go
+++ b/src/cmd/asm/internal/asm/asm.go
@@ -269,17 +269,7 @@ func (p *Parser) asmGlobl(word string, operands [][]lex.Token) {
 	}
 
 	// log.Printf("GLOBL %s %d, $%d", name, flag, size)
-	prog := &obj.Prog{
-		Ctxt:   p.ctxt,
-		As:     obj.AGLOBL,
-		Lineno: p.histLineNum,
-		From:   nameAddr,
-		From3: &obj.Addr{
-			Offset: flag,
-		},
-		To: addr,
-	}
-	p.append(prog, "", false)
+	p.ctxt.Globl(nameAddr.Sym, addr.Offset, int(flag))
 }
 
 // asmPCData assembles a PCDATA pseudo-op.
@@ -379,7 +369,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
 			prog.Reg = reg
 			break
 		}
-		if p.arch.Family == sys.MIPS64 {
+		if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 {
 			// 3-operand jumps.
 			// First two must be registers
 			target = &a[2]
@@ -403,7 +393,7 @@ func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {
 
 		fallthrough
 	default:
-		p.errorf("wrong number of arguments to %s instruction", obj.Aconv(op))
+		p.errorf("wrong number of arguments to %s instruction", op)
 		return
 	}
 	switch {
@@ -476,7 +466,7 @@ func (p *Parser) branch(jmp, target *obj.Prog) {
 // asmInstruction assembles an instruction.
 // MOVW R9, (R10)
 func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
-	// fmt.Printf("%s %+v\n", obj.Aconv(op), a)
+	// fmt.Printf("%s %+v\n", op, a)
 	prog := &obj.Prog{
 		Ctxt:   p.ctxt,
 		Lineno: p.histLineNum,
@@ -525,7 +515,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 					prog.To = a[1]
 					break
 				}
-				p.errorf("unrecognized addressing for %s", obj.Aconv(op))
+				p.errorf("unrecognized addressing for %s", op)
 				return
 			}
 			if arch.IsARMFloatCmp(op) {
@@ -537,8 +527,8 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 			prog.From = a[0]
 			prog.Reg = p.getRegister(prog, op, &a[1])
 			break
-		} else if p.arch.Family == sys.MIPS64 {
-			if arch.IsMIPS64CMP(op) || arch.IsMIPS64MUL(op) {
+		} else if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 {
+			if arch.IsMIPSCMP(op) || arch.IsMIPSMUL(op) {
 				prog.From = a[0]
 				prog.Reg = p.getRegister(prog, op, &a[1])
 				break
@@ -548,7 +538,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 		prog.To = a[1]
 	case 3:
 		switch p.arch.Family {
-		case sys.MIPS64:
+		case sys.MIPS, sys.MIPS64:
 			prog.From = a[0]
 			prog.Reg = p.getRegister(prog, op, &a[1])
 			prog.To = a[2]
@@ -572,7 +562,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 			// Catch missing operand here, because we store immediate as part of From3, and can't distinguish
 			// missing operand from legal value 0 in obj/x86/asm6.
 			if arch.IsAMD4OP(op) {
-				p.errorf("4 operands required, but only 3 are provided for %s instruction", obj.Aconv(op))
+				p.errorf("4 operands required, but only 3 are provided for %s instruction", op)
 			}
 			prog.From = a[0]
 			prog.From3 = newAddr(a[1])
@@ -583,7 +573,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 				prog.From = a[0]
 				prog.To = a[1]
 				if a[2].Type != obj.TYPE_REG {
-					p.errorf("invalid addressing modes for third operand to %s instruction, must be register", obj.Aconv(op))
+					p.errorf("invalid addressing modes for third operand to %s instruction, must be register", op)
 					return
 				}
 				prog.RegTo2 = a[2].Reg
@@ -619,7 +609,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 				prog.From3 = newAddr(a[1])
 				prog.To = a[2]
 			default:
-				p.errorf("invalid addressing modes for %s instruction", obj.Aconv(op))
+				p.errorf("invalid addressing modes for %s instruction", op)
 				return
 			}
 		case sys.S390X:
@@ -656,10 +646,10 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 			prog.From = a[1]
 			prog.From3 = newAddr(a[2])
 			if a[0].Type != obj.TYPE_CONST {
-				p.errorf("first operand must be an immediate in %s instruction", obj.Aconv(op))
+				p.errorf("first operand must be an immediate in %s instruction", op)
 			}
 			if prog.From3.Type != obj.TYPE_REG {
-				p.errorf("third operand must be a register in %s instruction", obj.Aconv(op))
+				p.errorf("third operand must be a register in %s instruction", op)
 			}
 			prog.From3.Offset = int64(p.getImmediate(prog, op, &a[0]))
 			prog.To = a[3]
@@ -673,15 +663,42 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 			prog.To = a[3]
 			break
 		}
-		if p.arch.Family == sys.PPC64 && arch.IsPPC64RLD(op) {
-			// 2nd operand must always be a register.
-			// TODO: Do we need to guard this with the instruction type?
-			// That is, are there 4-operand instructions without this property?
-			prog.From = a[0]
-			prog.Reg = p.getRegister(prog, op, &a[1])
-			prog.From3 = newAddr(a[2])
-			prog.To = a[3]
-			break
+		if p.arch.Family == sys.PPC64 {
+			if arch.IsPPC64RLD(op) {
+				prog.From = a[0]
+				prog.Reg = p.getRegister(prog, op, &a[1])
+				prog.From3 = newAddr(a[2])
+				prog.To = a[3]
+				break
+			} else if arch.IsPPC64ISEL(op) {
+				// ISEL BC,RB,RA,RT becomes isel rt,ra,rb,bc
+				prog.From3 = newAddr(a[2])                // ra
+				prog.From = a[0]                          // bc
+				prog.Reg = p.getRegister(prog, op, &a[1]) // rb
+				prog.To = a[3]                            // rt
+				break
+			}
+			// Else, it is a VA-form instruction
+			// reg reg reg reg
+			// imm reg reg reg
+			// Or a VX-form instruction
+			// imm imm reg reg
+			if a[1].Type == obj.TYPE_REG {
+				prog.From = a[0]
+				prog.Reg = p.getRegister(prog, op, &a[1])
+				prog.From3 = newAddr(a[2])
+				prog.To = a[3]
+				break
+			} else if a[1].Type == obj.TYPE_CONST {
+				prog.From = a[0]
+				prog.Reg = p.getRegister(prog, op, &a[2])
+				prog.From3 = newAddr(a[1])
+				prog.To = a[3]
+				break
+			} else {
+				p.errorf("invalid addressing modes for %s instruction", op)
+				return
+			}
 		}
 		if p.arch.Family == sys.S390X {
 			prog.From = a[1]
@@ -690,7 +707,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 			prog.To = a[3]
 			break
 		}
-		p.errorf("can't handle %s instruction with 4 operands", obj.Aconv(op))
+		p.errorf("can't handle %s instruction with 4 operands", op)
 		return
 	case 5:
 		if p.arch.Family == sys.PPC64 && arch.IsPPC64RLD(op) {
@@ -712,7 +729,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 			prog.To = a[4]
 			break
 		}
-		p.errorf("can't handle %s instruction with 5 operands", obj.Aconv(op))
+		p.errorf("can't handle %s instruction with 5 operands", op)
 		return
 	case 6:
 		if p.arch.Family == sys.ARM && arch.IsARMMRC(op) {
@@ -736,7 +753,7 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
 		}
 		fallthrough
 	default:
-		p.errorf("can't handle %s instruction with %d operands", obj.Aconv(op), len(a))
+		p.errorf("can't handle %s instruction with %d operands", op, len(a))
 		return
 	}
 
@@ -771,7 +788,7 @@ func (p *Parser) getConstantPseudo(pseudo string, addr *obj.Addr) int64 {
 // getConstant checks that addr represents a plain constant and returns its value.
 func (p *Parser) getConstant(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {
 	if addr.Type != obj.TYPE_MEM || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {
-		p.errorf("%s: expected integer constant; found %s", obj.Aconv(op), obj.Dconv(prog, addr))
+		p.errorf("%s: expected integer constant; found %s", op, obj.Dconv(prog, addr))
 	}
 	return addr.Offset
 }
@@ -779,7 +796,7 @@ func (p *Parser) getConstant(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {
 // getImmediate checks that addr represents an immediate constant and returns its value.
 func (p *Parser) getImmediate(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {
 	if addr.Type != obj.TYPE_CONST || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {
-		p.errorf("%s: expected immediate constant; found %s", obj.Aconv(op), obj.Dconv(prog, addr))
+		p.errorf("%s: expected immediate constant; found %s", op, obj.Dconv(prog, addr))
 	}
 	return addr.Offset
 }
@@ -787,7 +804,7 @@ func (p *Parser) getImmediate(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {
 // getRegister checks that addr represents a register and returns its value.
 func (p *Parser) getRegister(prog *obj.Prog, op obj.As, addr *obj.Addr) int16 {
 	if addr.Type != obj.TYPE_REG || addr.Offset != 0 || addr.Name != 0 || addr.Index != 0 {
-		p.errorf("%s: expected register; found %s", obj.Aconv(op), obj.Dconv(prog, addr))
+		p.errorf("%s: expected register; found %s", op, obj.Dconv(prog, addr))
 	}
 	return addr.Reg
 }
diff --git a/src/cmd/asm/internal/asm/endtoend_test.go b/src/cmd/asm/internal/asm/endtoend_test.go
index bc992a7..a2f31f8 100644
--- a/src/cmd/asm/internal/asm/endtoend_test.go
+++ b/src/cmd/asm/internal/asm/endtoend_test.go
@@ -383,7 +383,8 @@ func TestAMD64Errors(t *testing.T) {
 	testErrors(t, "amd64", "amd64error")
 }
 
-func TestMIPS64EndToEnd(t *testing.T) {
+func TestMIPSEndToEnd(t *testing.T) {
+	testEndToEnd(t, "mips", "mips")
 	testEndToEnd(t, "mips64", "mips64")
 }
 
diff --git a/src/cmd/asm/internal/asm/operand_test.go b/src/cmd/asm/internal/asm/operand_test.go
index eafc8a3..f1531a8 100644
--- a/src/cmd/asm/internal/asm/operand_test.go
+++ b/src/cmd/asm/internal/asm/operand_test.go
@@ -5,7 +5,6 @@
 package asm
 
 import (
-	"os"
 	"testing"
 
 	"cmd/asm/internal/arch"
@@ -16,7 +15,8 @@ import (
 // A simple in-out test: Do we print what we parse?
 
 func setArch(goarch string) (*arch.Arch, *obj.Link) {
-	os.Setenv("GOOS", "linux") // obj can handle this OS for all architectures.
+	obj.GOOS = "linux" // obj can handle this OS for all architectures.
+	obj.GOARCH = goarch
 	architecture := arch.Set(goarch)
 	if architecture == nil {
 		panic("asm: unrecognized architecture " + goarch)
@@ -65,6 +65,11 @@ func TestPPC64OperandParser(t *testing.T) {
 	testOperandParser(t, parser, ppc64OperandTests)
 }
 
+func TestMIPSOperandParser(t *testing.T) {
+	parser := newParser("mips")
+	testOperandParser(t, parser, mipsOperandTests)
+}
+
 func TestMIPS64OperandParser(t *testing.T) {
 	parser := newParser("mips64")
 	testOperandParser(t, parser, mips64OperandTests)
@@ -340,6 +345,102 @@ var ppc64OperandTests = []operandTest{
 	{"6(PC)", "6(PC)"},
 	{"CR7", "CR7"},
 	{"CTR", "CTR"},
+	{"VS0", "VS0"},
+	{"VS1", "VS1"},
+	{"VS2", "VS2"},
+	{"VS3", "VS3"},
+	{"VS4", "VS4"},
+	{"VS5", "VS5"},
+	{"VS6", "VS6"},
+	{"VS7", "VS7"},
+	{"VS8", "VS8"},
+	{"VS9", "VS9"},
+	{"VS10", "VS10"},
+	{"VS11", "VS11"},
+	{"VS12", "VS12"},
+	{"VS13", "VS13"},
+	{"VS14", "VS14"},
+	{"VS15", "VS15"},
+	{"VS16", "VS16"},
+	{"VS17", "VS17"},
+	{"VS18", "VS18"},
+	{"VS19", "VS19"},
+	{"VS20", "VS20"},
+	{"VS21", "VS21"},
+	{"VS22", "VS22"},
+	{"VS23", "VS23"},
+	{"VS24", "VS24"},
+	{"VS25", "VS25"},
+	{"VS26", "VS26"},
+	{"VS27", "VS27"},
+	{"VS28", "VS28"},
+	{"VS29", "VS29"},
+	{"VS30", "VS30"},
+	{"VS31", "VS31"},
+	{"VS32", "VS32"},
+	{"VS33", "VS33"},
+	{"VS34", "VS34"},
+	{"VS35", "VS35"},
+	{"VS36", "VS36"},
+	{"VS37", "VS37"},
+	{"VS38", "VS38"},
+	{"VS39", "VS39"},
+	{"VS40", "VS40"},
+	{"VS41", "VS41"},
+	{"VS42", "VS42"},
+	{"VS43", "VS43"},
+	{"VS44", "VS44"},
+	{"VS45", "VS45"},
+	{"VS46", "VS46"},
+	{"VS47", "VS47"},
+	{"VS48", "VS48"},
+	{"VS49", "VS49"},
+	{"VS50", "VS50"},
+	{"VS51", "VS51"},
+	{"VS52", "VS52"},
+	{"VS53", "VS53"},
+	{"VS54", "VS54"},
+	{"VS55", "VS55"},
+	{"VS56", "VS56"},
+	{"VS57", "VS57"},
+	{"VS58", "VS58"},
+	{"VS59", "VS59"},
+	{"VS60", "VS60"},
+	{"VS61", "VS61"},
+	{"VS62", "VS62"},
+	{"VS63", "VS63"},
+	{"V0", "V0"},
+	{"V1", "V1"},
+	{"V2", "V2"},
+	{"V3", "V3"},
+	{"V4", "V4"},
+	{"V5", "V5"},
+	{"V6", "V6"},
+	{"V7", "V7"},
+	{"V8", "V8"},
+	{"V9", "V9"},
+	{"V10", "V10"},
+	{"V11", "V11"},
+	{"V12", "V12"},
+	{"V13", "V13"},
+	{"V14", "V14"},
+	{"V15", "V15"},
+	{"V16", "V16"},
+	{"V17", "V17"},
+	{"V18", "V18"},
+	{"V19", "V19"},
+	{"V20", "V20"},
+	{"V21", "V21"},
+	{"V22", "V22"},
+	{"V23", "V23"},
+	{"V24", "V24"},
+	{"V25", "V25"},
+	{"V26", "V26"},
+	{"V27", "V27"},
+	{"V28", "V28"},
+	{"V29", "V29"},
+	{"V30", "V30"},
+	{"V31", "V31"},
 	{"F14", "F14"},
 	{"F15", "F15"},
 	{"F16", "F16"},
@@ -532,6 +633,88 @@ var mips64OperandTests = []operandTest{
 	{"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
 }
 
+var mipsOperandTests = []operandTest{
+	{"$((1<<63)-1)", "$9223372036854775807"},
+	{"$(-64*1024)", "$-65536"},
+	{"$(1024 * 8)", "$8192"},
+	{"$-1", "$-1"},
+	{"$-24(R4)", "$-24(R4)"},
+	{"$0", "$0"},
+	{"$0(R1)", "$(R1)"},
+	{"$0.5", "$(0.5)"},
+	{"$0x7000", "$28672"},
+	{"$0x88888eef", "$2290650863"},
+	{"$1", "$1"},
+	{"$_main<>(SB)", "$_main<>(SB)"},
+	{"$argframe(FP)", "$argframe(FP)"},
+	{"$~3", "$-4"},
+	{"(-288-3*8)(R1)", "-312(R1)"},
+	{"(16)(R7)", "16(R7)"},
+	{"(8)(g)", "8(g)"},
+	{"(R0)", "(R0)"},
+	{"(R3)", "(R3)"},
+	{"(R4)", "(R4)"},
+	{"(R5)", "(R5)"},
+	{"-1(R4)", "-1(R4)"},
+	{"-1(R5)", "-1(R5)"},
+	{"6(PC)", "6(PC)"},
+	{"F14", "F14"},
+	{"F15", "F15"},
+	{"F16", "F16"},
+	{"F17", "F17"},
+	{"F18", "F18"},
+	{"F19", "F19"},
+	{"F20", "F20"},
+	{"F21", "F21"},
+	{"F22", "F22"},
+	{"F23", "F23"},
+	{"F24", "F24"},
+	{"F25", "F25"},
+	{"F26", "F26"},
+	{"F27", "F27"},
+	{"F28", "F28"},
+	{"F29", "F29"},
+	{"F30", "F30"},
+	{"F31", "F31"},
+	{"R0", "R0"},
+	{"R1", "R1"},
+	{"R11", "R11"},
+	{"R12", "R12"},
+	{"R13", "R13"},
+	{"R14", "R14"},
+	{"R15", "R15"},
+	{"R16", "R16"},
+	{"R17", "R17"},
+	{"R18", "R18"},
+	{"R19", "R19"},
+	{"R2", "R2"},
+	{"R20", "R20"},
+	{"R21", "R21"},
+	{"R22", "R22"},
+	{"R23", "R23"},
+	{"R24", "R24"},
+	{"R25", "R25"},
+	{"R26", "R26"},
+	{"R27", "R27"},
+	{"R29", "R29"},
+	{"R3", "R3"},
+	{"R31", "R31"},
+	{"R4", "R4"},
+	{"R5", "R5"},
+	{"R6", "R6"},
+	{"R7", "R7"},
+	{"R8", "R8"},
+	{"R9", "R9"},
+	{"LO", "LO"},
+	{"a(FP)", "a(FP)"},
+	{"g", "g"},
+	{"ret+8(FP)", "ret+8(FP)"},
+	{"runtime·abort(SB)", "runtime.abort(SB)"},
+	{"·AddUint32(SB)", "\"\".AddUint32(SB)"},
+	{"·trunc(SB)", "\"\".trunc(SB)"},
+	{"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
+}
+
 var s390xOperandTests = []operandTest{
 	{"$((1<<63)-1)", "$9223372036854775807"},
 	{"$(-64*1024)", "$-65536"},
diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go
index 40206e6..406c65e 100644
--- a/src/cmd/asm/internal/asm/parse.go
+++ b/src/cmd/asm/internal/asm/parse.go
@@ -941,14 +941,6 @@ func (p *Parser) atof(str string) float64 {
 	return value
 }
 
-func (p *Parser) atos(str string) string {
-	value, err := strconv.Unquote(str)
-	if err != nil {
-		p.errorf("%s", err)
-	}
-	return value
-}
-
 // EOF represents the end of input.
 var EOF = lex.Make(scanner.EOF, "EOF")
 
diff --git a/src/cmd/asm/internal/asm/testdata/amd64enc.s b/src/cmd/asm/internal/asm/testdata/amd64enc.s
index 22dfe12..b27faa5 100644
--- a/src/cmd/asm/internal/asm/testdata/amd64enc.s
+++ b/src/cmd/asm/internal/asm/testdata/amd64enc.s
@@ -2551,14 +2551,14 @@ TEXT asmtest(SB),7,$0
 	MOVQ (R11), X11                         // 66450f6e1b or 664d0f6e1b or f3450f7e1b
 	MOVQ DX, X11                            // 66440f6eda or 664c0f6eda
 	MOVQ R11, X11                           // 66450f6edb or 664d0f6edb
-	//TODO: MOVDDUP (BX), X2                // f20f1213
-	//TODO: MOVDDUP (R11), X2               // f2410f1213
-	//TODO: MOVDDUP X2, X2                  // f20f12d2
-	//TODO: MOVDDUP X11, X2                 // f2410f12d3
-	//TODO: MOVDDUP (BX), X11               // f2440f121b
-	//TODO: MOVDDUP (R11), X11              // f2450f121b
-	//TODO: MOVDDUP X2, X11                 // f2440f12da
-	//TODO: MOVDDUP X11, X11                // f2450f12db
+	MOVDDUP (BX), X2                        // f20f1213
+	MOVDDUP (R11), X2                       // f2410f1213
+	MOVDDUP X2, X2                          // f20f12d2
+	MOVDDUP X11, X2                         // f2410f12d3
+	MOVDDUP (BX), X11                       // f2440f121b
+	MOVDDUP (R11), X11                      // f2450f121b
+	MOVDDUP X2, X11                         // f2440f12da
+	MOVDDUP X11, X11                        // f2450f12db
 	MOVQ X2, M2                             // f20fd6d2
 	MOVQ X11, M2                            // f2410fd6d3
 	MOVQ X2, M3                             // f20fd6da
@@ -2697,22 +2697,22 @@ TEXT asmtest(SB),7,$0
 	//TODO: MOVSD X11, (BX)                 // f2440f111b
 	//TODO: MOVSD X2, (R11)                 // f2410f1113
 	//TODO: MOVSD X11, (R11)                // f2450f111b
-	//TODO: MOVSHDUP (BX), X2               // f30f1613
-	//TODO: MOVSHDUP (R11), X2              // f3410f1613
-	//TODO: MOVSHDUP X2, X2                 // f30f16d2
-	//TODO: MOVSHDUP X11, X2                // f3410f16d3
-	//TODO: MOVSHDUP (BX), X11              // f3440f161b
-	//TODO: MOVSHDUP (R11), X11             // f3450f161b
-	//TODO: MOVSHDUP X2, X11                // f3440f16da
-	//TODO: MOVSHDUP X11, X11               // f3450f16db
-	//TODO: MOVSLDUP (BX), X2               // f30f1213
-	//TODO: MOVSLDUP (R11), X2              // f3410f1213
-	//TODO: MOVSLDUP X2, X2                 // f30f12d2
-	//TODO: MOVSLDUP X11, X2                // f3410f12d3
-	//TODO: MOVSLDUP (BX), X11              // f3440f121b
-	//TODO: MOVSLDUP (R11), X11             // f3450f121b
-	//TODO: MOVSLDUP X2, X11                // f3440f12da
-	//TODO: MOVSLDUP X11, X11               // f3450f12db
+	MOVSHDUP (BX), X2                       // f30f1613
+	MOVSHDUP (R11), X2                      // f3410f1613
+	MOVSHDUP X2, X2                         // f30f16d2
+	MOVSHDUP X11, X2                        // f3410f16d3
+	MOVSHDUP (BX), X11                      // f3440f161b
+	MOVSHDUP (R11), X11                     // f3450f161b
+	MOVSHDUP X2, X11                        // f3440f16da
+	MOVSHDUP X11, X11                       // f3450f16db
+	MOVSLDUP (BX), X2                       // f30f1213
+	MOVSLDUP (R11), X2                      // f3410f1213
+	MOVSLDUP X2, X2                         // f30f12d2
+	MOVSLDUP X11, X2                        // f3410f12d3
+	MOVSLDUP (BX), X11                      // f3440f121b
+	MOVSLDUP (R11), X11                     // f3450f121b
+	MOVSLDUP X2, X11                        // f3440f12da
+	MOVSLDUP X11, X11                       // f3450f12db
 	MOVSQ                                   // 48a5
 	MOVSS (BX), X2                          // f30f1013
 	MOVSS (R11), X2                         // f3410f1013
@@ -6116,30 +6116,30 @@ TEXT asmtest(SB),7,$0
 	//TODO: VBROADCASTI128 (R11), Y2        // c4c27d5a13
 	//TODO: VBROADCASTI128 (BX), Y11        // c4627d5a1b
 	//TODO: VBROADCASTI128 (R11), Y11       // c4427d5a1b
-	//TODO: VBROADCASTSD (BX), Y2           // c4e27d1913
-	//TODO: VBROADCASTSD (R11), Y2          // c4c27d1913
-	//TODO: VBROADCASTSD (BX), Y11          // c4627d191b
-	//TODO: VBROADCASTSD (R11), Y11         // c4427d191b
-	//TODO: VBROADCASTSD X2, Y2             // c4e27d19d2
-	//TODO: VBROADCASTSD X11, Y2            // c4c27d19d3
-	//TODO: VBROADCASTSD X2, Y11            // c4627d19da
-	//TODO: VBROADCASTSD X11, Y11           // c4427d19db
-	//TODO: VBROADCASTSS (BX), X2           // c4e2791813
-	//TODO: VBROADCASTSS (R11), X2          // c4c2791813
-	//TODO: VBROADCASTSS (BX), X11          // c46279181b
-	//TODO: VBROADCASTSS (R11), X11         // c44279181b
-	//TODO: VBROADCASTSS X2, X2             // c4e27918d2
-	//TODO: VBROADCASTSS X11, X2            // c4c27918d3
-	//TODO: VBROADCASTSS X2, X11            // c4627918da
-	//TODO: VBROADCASTSS X11, X11           // c4427918db
-	//TODO: VBROADCASTSS (BX), Y2           // c4e27d1813
-	//TODO: VBROADCASTSS (R11), Y2          // c4c27d1813
-	//TODO: VBROADCASTSS (BX), Y11          // c4627d181b
-	//TODO: VBROADCASTSS (R11), Y11         // c4427d181b
-	//TODO: VBROADCASTSS X2, Y2             // c4e27d18d2
-	//TODO: VBROADCASTSS X11, Y2            // c4c27d18d3
-	//TODO: VBROADCASTSS X2, Y11            // c4627d18da
-	//TODO: VBROADCASTSS X11, Y11           // c4427d18db
+	VBROADCASTSD (BX), Y2                   // c4e27d1913
+	VBROADCASTSD (R11), Y2                  // c4c27d1913
+	VBROADCASTSD (BX), Y11                  // c4627d191b
+	VBROADCASTSD (R11), Y11                 // c4427d191b
+	VBROADCASTSD X2, Y2                     // c4e27d19d2
+	VBROADCASTSD X11, Y2                    // c4c27d19d3
+	VBROADCASTSD X2, Y11                    // c4627d19da
+	VBROADCASTSD X11, Y11                   // c4427d19db
+	VBROADCASTSS (BX), X2                   // c4e2791813
+	VBROADCASTSS (R11), X2                  // c4c2791813
+	VBROADCASTSS (BX), X11                  // c46279181b
+	VBROADCASTSS (R11), X11                 // c44279181b
+	VBROADCASTSS X2, X2                     // c4e27918d2
+	VBROADCASTSS X11, X2                    // c4c27918d3
+	VBROADCASTSS X2, X11                    // c4627918da
+	VBROADCASTSS X11, X11                   // c4427918db
+	VBROADCASTSS (BX), Y2                   // c4e27d1813
+	VBROADCASTSS (R11), Y2                  // c4c27d1813
+	VBROADCASTSS (BX), Y11                  // c4627d181b
+	VBROADCASTSS (R11), Y11                 // c4427d181b
+	VBROADCASTSS X2, Y2                     // c4e27d18d2
+	VBROADCASTSS X11, Y2                    // c4c27d18d3
+	VBROADCASTSS X2, Y11                    // c4627d18da
+	VBROADCASTSS X11, Y11                   // c4427d18db
 	//TODO: VCMPPD $7, (BX), X9, X2         // c4e131c21307 or c5b1c21307
 	//TODO: VCMPPD $7, (R11), X9, X2        // c4c131c21307
 	//TODO: VCMPPD $7, X2, X9, X2           // c4e131c2d207 or c5b1c2d207
@@ -7642,22 +7642,22 @@ TEXT asmtest(SB),7,$0
 	//TODO: VMOVD (R11), X11                // c441796e1b
 	//TODO: VMOVD DX, X11                   // c461796eda or c5796eda
 	//TODO: VMOVD R11, X11                  // c441796edb
-	//TODO: VMOVDDUP (BX), X2               // c4e17b1213 or c5fb1213
-	//TODO: VMOVDDUP (R11), X2              // c4c17b1213
-	//TODO: VMOVDDUP X2, X2                 // c4e17b12d2 or c5fb12d2
-	//TODO: VMOVDDUP X11, X2                // c4c17b12d3
-	//TODO: VMOVDDUP (BX), X11              // c4617b121b or c57b121b
-	//TODO: VMOVDDUP (R11), X11             // c4417b121b
-	//TODO: VMOVDDUP X2, X11                // c4617b12da or c57b12da
-	//TODO: VMOVDDUP X11, X11               // c4417b12db
-	//TODO: VMOVDDUP (BX), Y2               // c4e17f1213 or c5ff1213
-	//TODO: VMOVDDUP (R11), Y2              // c4c17f1213
-	//TODO: VMOVDDUP Y2, Y2                 // c4e17f12d2 or c5ff12d2
-	//TODO: VMOVDDUP Y11, Y2                // c4c17f12d3
-	//TODO: VMOVDDUP (BX), Y11              // c4617f121b or c57f121b
-	//TODO: VMOVDDUP (R11), Y11             // c4417f121b
-	//TODO: VMOVDDUP Y2, Y11                // c4617f12da or c57f12da
-	//TODO: VMOVDDUP Y11, Y11               // c4417f12db
+	VMOVDDUP (BX), X2                       // c4e17b1213 or c5fb1213
+	VMOVDDUP (R11), X2                      // c4c17b1213
+	VMOVDDUP X2, X2                         // c4e17b12d2 or c5fb12d2
+	VMOVDDUP X11, X2                        // c4c17b12d3
+	VMOVDDUP (BX), X11                      // c4617b121b or c57b121b
+	VMOVDDUP (R11), X11                     // c4417b121b
+	VMOVDDUP X2, X11                        // c4617b12da or c57b12da
+	VMOVDDUP X11, X11                       // c4417b12db
+	VMOVDDUP (BX), Y2                       // c4e17f1213 or c5ff1213
+	VMOVDDUP (R11), Y2                      // c4c17f1213
+	VMOVDDUP Y2, Y2                         // c4e17f12d2 or c5ff12d2
+	VMOVDDUP Y11, Y2                        // c4c17f12d3
+	VMOVDDUP (BX), Y11                      // c4617f121b or c57f121b
+	VMOVDDUP (R11), Y11                     // c4417f121b
+	VMOVDDUP Y2, Y11                        // c4617f12da or c57f12da
+	VMOVDDUP Y11, Y11                       // c4417f12db
 	VMOVDQA (BX), X2                        // c4e1796f13 or c5f96f13
 	VMOVDQA (R11), X2                       // c4c1796f13
 	VMOVDQA X2, X2                          // c4e1796fd2 or c5f96fd2 or c4e1797fd2 or c5f97fd2
@@ -7826,38 +7826,38 @@ TEXT asmtest(SB),7,$0
 	//TODO: VMOVSD X11, X9, X2              // c4c13310d3 or c4613311da or c53311da
 	//TODO: VMOVSD X2, X9, X11              // c4613310da or c53310da or c4c13311d3
 	//TODO: VMOVSD X11, X9, X11             // c4413310db or c4413311db
-	//TODO: VMOVSHDUP (BX), X2              // c4e17a1613 or c5fa1613
-	//TODO: VMOVSHDUP (R11), X2             // c4c17a1613
-	//TODO: VMOVSHDUP X2, X2                // c4e17a16d2 or c5fa16d2
-	//TODO: VMOVSHDUP X11, X2               // c4c17a16d3
-	//TODO: VMOVSHDUP (BX), X11             // c4617a161b or c57a161b
-	//TODO: VMOVSHDUP (R11), X11            // c4417a161b
-	//TODO: VMOVSHDUP X2, X11               // c4617a16da or c57a16da
-	//TODO: VMOVSHDUP X11, X11              // c4417a16db
-	//TODO: VMOVSHDUP (BX), Y2              // c4e17e1613 or c5fe1613
-	//TODO: VMOVSHDUP (R11), Y2             // c4c17e1613
-	//TODO: VMOVSHDUP Y2, Y2                // c4e17e16d2 or c5fe16d2
-	//TODO: VMOVSHDUP Y11, Y2               // c4c17e16d3
-	//TODO: VMOVSHDUP (BX), Y11             // c4617e161b or c57e161b
-	//TODO: VMOVSHDUP (R11), Y11            // c4417e161b
-	//TODO: VMOVSHDUP Y2, Y11               // c4617e16da or c57e16da
-	//TODO: VMOVSHDUP Y11, Y11              // c4417e16db
-	//TODO: VMOVSLDUP (BX), X2              // c4e17a1213 or c5fa1213
-	//TODO: VMOVSLDUP (R11), X2             // c4c17a1213
-	//TODO: VMOVSLDUP X2, X2                // c4e17a12d2 or c5fa12d2
-	//TODO: VMOVSLDUP X11, X2               // c4c17a12d3
-	//TODO: VMOVSLDUP (BX), X11             // c4617a121b or c57a121b
-	//TODO: VMOVSLDUP (R11), X11            // c4417a121b
-	//TODO: VMOVSLDUP X2, X11               // c4617a12da or c57a12da
-	//TODO: VMOVSLDUP X11, X11              // c4417a12db
-	//TODO: VMOVSLDUP (BX), Y2              // c4e17e1213 or c5fe1213
-	//TODO: VMOVSLDUP (R11), Y2             // c4c17e1213
-	//TODO: VMOVSLDUP Y2, Y2                // c4e17e12d2 or c5fe12d2
-	//TODO: VMOVSLDUP Y11, Y2               // c4c17e12d3
-	//TODO: VMOVSLDUP (BX), Y11             // c4617e121b or c57e121b
-	//TODO: VMOVSLDUP (R11), Y11            // c4417e121b
-	//TODO: VMOVSLDUP Y2, Y11               // c4617e12da or c57e12da
-	//TODO: VMOVSLDUP Y11, Y11              // c4417e12db
+	VMOVSHDUP (BX), X2                      // c4e17a1613 or c5fa1613
+	VMOVSHDUP (R11), X2                     // c4c17a1613
+	VMOVSHDUP X2, X2                        // c4e17a16d2 or c5fa16d2
+	VMOVSHDUP X11, X2                       // c4c17a16d3
+	VMOVSHDUP (BX), X11                     // c4617a161b or c57a161b
+	VMOVSHDUP (R11), X11                    // c4417a161b
+	VMOVSHDUP X2, X11                       // c4617a16da or c57a16da
+	VMOVSHDUP X11, X11                      // c4417a16db
+	VMOVSHDUP (BX), Y2                      // c4e17e1613 or c5fe1613
+	VMOVSHDUP (R11), Y2                     // c4c17e1613
+	VMOVSHDUP Y2, Y2                        // c4e17e16d2 or c5fe16d2
+	VMOVSHDUP Y11, Y2                       // c4c17e16d3
+	VMOVSHDUP (BX), Y11                     // c4617e161b or c57e161b
+	VMOVSHDUP (R11), Y11                    // c4417e161b
+	VMOVSHDUP Y2, Y11                       // c4617e16da or c57e16da
+	VMOVSHDUP Y11, Y11                      // c4417e16db
+	VMOVSLDUP (BX), X2                      // c4e17a1213 or c5fa1213
+	VMOVSLDUP (R11), X2                     // c4c17a1213
+	VMOVSLDUP X2, X2                        // c4e17a12d2 or c5fa12d2
+	VMOVSLDUP X11, X2                       // c4c17a12d3
+	VMOVSLDUP (BX), X11                     // c4617a121b or c57a121b
+	VMOVSLDUP (R11), X11                    // c4417a121b
+	VMOVSLDUP X2, X11                       // c4617a12da or c57a12da
+	VMOVSLDUP X11, X11                      // c4417a12db
+	VMOVSLDUP (BX), Y2                      // c4e17e1213 or c5fe1213
+	VMOVSLDUP (R11), Y2                     // c4c17e1213
+	VMOVSLDUP Y2, Y2                        // c4e17e12d2 or c5fe12d2
+	VMOVSLDUP Y11, Y2                       // c4c17e12d3
+	VMOVSLDUP (BX), Y11                     // c4617e121b or c57e121b
+	VMOVSLDUP (R11), Y11                    // c4417e121b
+	VMOVSLDUP Y2, Y11                       // c4617e12da or c57e12da
+	VMOVSLDUP Y11, Y11                      // c4417e12db
 	//TODO: VMOVSS X2, (BX)                 // c4e17a1113 or c5fa1113
 	//TODO: VMOVSS X11, (BX)                // c4617a111b or c57a111b
 	//TODO: VMOVSS X2, (R11)                // c4c17a1113
diff --git a/src/cmd/asm/internal/asm/testdata/mips.s b/src/cmd/asm/internal/asm/testdata/mips.s
new file mode 100644
index 0000000..f48d918
--- /dev/null
+++ b/src/cmd/asm/internal/asm/testdata/mips.s
@@ -0,0 +1,430 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This input was created by taking the mips64 testcase and modified
+// by hand.
+
+TEXT foo(SB),7,$0
+
+	//inst:
+	//
+	// load ints and bytes
+	//
+	//	LMOVW rreg ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVW	R1, R2
+	MOVW	LO, R1
+	MOVW	HI, R1
+	MOVW	R1, LO
+	MOVW	R1, HI
+	MOVW	R1, R2
+	MOVW	LO, R1
+	MOVW	HI, R1
+	MOVW	R1, LO
+	MOVW	R1, HI
+
+	//	LMOVW addr ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVW	foo<>+3(SB), R2
+	MOVW	16(R1), R2
+	MOVW	(R1), R2
+	MOVW	foo<>+3(SB), R2
+	MOVW	16(R1), R2
+	MOVW	(R1), R2
+	LL	(R1), R2
+
+	//	LMOVB rreg ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVB	R1, R2
+
+	//	LMOVB addr ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVB	foo<>+3(SB), R2
+	MOVB	16(R1), R2
+	MOVB	(R1), R2
+
+	//
+	// load floats
+	//
+	//	LFMOV addr ',' freg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVF	foo<>+3(SB), F2
+	MOVF	16(R1), F2
+	MOVF	(R1), F2
+
+	//	LFMOV fimm ',' freg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVF	$0.1, F2	// MOVF $(0.10000000000000001), F2
+
+	//	LFMOV freg ',' freg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVF	F1, F2
+
+	//	LFMOV freg ',' addr
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVF	F2, foo<>+3(SB)
+	MOVF	F2, 16(R1)
+	MOVF	F2, (R1)
+
+	//
+	// store ints and bytes
+	//
+	//	LMOVW rreg ',' addr
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVW	R1, foo<>+3(SB)
+	MOVW	R1, 16(R2)
+	MOVW	R1, (R2)
+	MOVW	R1, foo<>+3(SB)
+	MOVW	R1, 16(R2)
+	MOVW	R1, (R2)
+	SC	R1, (R2)
+
+	//	LMOVB rreg ',' addr
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVB	R1, foo<>+3(SB)
+	MOVB	R1, 16(R2)
+	MOVB	R1, (R2)
+
+	//
+	// store floats
+	//
+	//	LMOVW freg ',' addr
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVD	F1, foo<>+3(SB)
+	MOVD	F1, 16(R2)
+	MOVD	F1, (R2)
+
+	//
+	// floating point status
+	//
+	//	LMOVW fpscr ',' freg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVW	FCR0, R1
+
+	//	LMOVW freg ','  fpscr
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVW	R1, FCR0
+
+	//	LMOVW rreg ',' mreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVW	R1, M1
+	MOVW	R1, M1
+
+	//	LMOVW mreg ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVW	M1, R1
+	MOVW	M1, R1
+
+
+	//
+	// integer operations
+	// logical instructions
+	// shift instructions
+	// unary instructions
+	//
+	//	LADDW rreg ',' sreg ',' rreg
+	//	{
+	//		outcode(int($1), &$2, int($4), &$6);
+	//	}
+	ADD	R1, R2, R3
+
+	//	LADDW imm ',' sreg ',' rreg
+	//	{
+	//		outcode(int($1), &$2, int($4), &$6);
+	//	}
+	ADD	$1, R2, R3
+
+	//	LADDW rreg ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	ADD	R1, R2
+
+	//	LADDW imm ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	ADD	$4, R1
+
+	//	LMUL rreg ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MUL	R1, R2
+
+	//	LSHW rreg ',' sreg ',' rreg
+	//	{
+	//		outcode(int($1), &$2, int($4), &$6);
+	//	}
+	SLL	R1, R2, R3
+
+	//	LSHW rreg ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	SLL	R1, R2
+
+	//	LSHW imm ',' sreg ',' rreg
+	//	{
+	//		outcode(int($1), &$2, int($4), &$6);
+	//	}
+	SLL	$4, R1, R2
+
+	//	LSHW imm ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	SLL	$4, R1
+
+	//
+	// move immediate: macro for lui+or, addi, addis, and other combinations
+	//
+	//	LMOVW imm ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVW	$1, R1
+	MOVW	$1, R1
+
+	//	LMOVW ximm ',' rreg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	MOVW	$1, R1
+	MOVW	$foo(SB), R1
+	MOVW	$1, R1
+	MOVW	$foo(SB), R1
+
+
+	//
+	// branch
+	//
+	//	LBRA rel
+	//	{
+	//		outcode(int($1), &nullgen, 0, &$2);
+	//	}
+	BEQ	R1, 2(PC)
+label0:
+	JMP	1(PC)
+	BEQ	R1, 2(PC)
+	JMP	label0+0	// JMP 66
+	BEQ	R1, 2(PC)
+	JAL	1(PC)	// CALL 1(PC)
+	BEQ	R1, 2(PC)
+	JAL	label0+0	// CALL 66
+
+	//	LBRA addr
+	//	{
+	//		outcode(int($1), &nullgen, 0, &$2);
+	//	}
+	BEQ	R1, 2(PC)
+	JMP	0(R1)	// JMP (R1)
+	BEQ	R1, 2(PC)
+	JMP	foo+0(SB)	// JMP foo(SB)
+	BEQ	R1, 2(PC)
+	JAL	0(R1)	// CALL (R1)
+	BEQ	R1, 2(PC)
+	JAL	foo+0(SB)	// CALL foo(SB)
+
+//
+// BEQ/BNE
+//
+//	LBRA rreg ',' rel
+//	{
+//		outcode(int($1), &$2, 0, &$4);
+//	}
+label1:
+	BEQ	R1, 1(PC)
+	BEQ	R1, label1	// BEQ R1, 81
+
+//	LBRA rreg ',' sreg ',' rel
+//	{
+//		outcode(int($1), &$2, 0, &$4);
+//	}
+label2:
+	BEQ	R1, R2, 1(PC)
+	BEQ	R1, R2, label2	// BEQ R1, R2, 83
+
+//
+// other integer conditional branch
+//
+//	LBRA rreg ',' rel
+//	{
+//		outcode(int($1), &$2, 0, &$4);
+//	}
+label3:
+	BLTZ	R1, 1(PC)
+	BLTZ	R1, label3	// BLTZ R1, 85
+
+//
+// floating point conditional branch
+//
+//	LBRA rel
+label4:
+	BFPT	1(PC)
+	BFPT	label4	// BFPT 87
+
+
+	//
+	// floating point operate
+	//
+	//	LFCONV freg ',' freg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	ABSD	F1, F2
+
+	//	LFADD freg ',' freg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	ADDD	F1, F2
+
+	//	LFADD freg ',' freg ',' freg
+	//	{
+	//		outcode(int($1), &$2, int($4.Reg), &$6);
+	//	}
+	ADDD	F1, F2, F3
+
+	//	LFCMP freg ',' freg
+	//	{
+	//		outcode(int($1), &$2, 0, &$4);
+	//	}
+	CMPEQD	F1, F2
+
+
+	//
+	// WORD
+	//
+	WORD	$1
+
+	//
+	// NOP
+	//
+	//	LNOP comma // asm doesn't support the trailing comma.
+	//	{
+	//		outcode(int($1), &nullgen, 0, &nullgen);
+	//	}
+	NOP
+
+	//	LNOP rreg comma // asm doesn't support the trailing comma.
+	//	{
+	//		outcode(int($1), &$2, 0, &nullgen);
+	//	}
+	NOP	R2
+
+	//	LNOP freg comma // asm doesn't support the trailing comma.
+	//	{
+	//		outcode(int($1), &$2, 0, &nullgen);
+	//	}
+	NOP	F2
+
+	//	LNOP ',' rreg // asm doesn't support the leading comma.
+	//	{
+	//		outcode(int($1), &nullgen, 0, &$3);
+	//	}
+	NOP	R2
+
+	//	LNOP ',' freg // asm doesn't support the leading comma.
+	//	{
+	//		outcode(int($1), &nullgen, 0, &$3);
+	//	}
+	NOP	F2
+
+	//	LNOP imm
+	//	{
+	//		outcode(int($1), &$2, 0, &nullgen);
+	//	}
+	NOP	$4
+
+	//
+	// special
+	//
+	SYSCALL
+	BREAK
+	SYNC
+
+	//
+	// conditional move on zero/nonzero gp value
+	//
+	CMOVN	R1, R2, R3
+	CMOVZ	R1, R2, R3
+
+	//
+	// conditional move on fp false/true
+	//
+	CMOVF	R1, R2
+	CMOVT	R1, R2
+
+	//
+	// conditional traps
+	//
+	TEQ	$1, R1, R2
+	TEQ	$1, R1
+
+
+	//
+	// other
+	//
+	CLO	R1, R2
+	SQRTD	F0, F1
+	MUL	R1, R2, R3
+
+
+	//
+	// RET
+	//
+	//	LRETRN	comma // asm doesn't support the trailing comma.
+	//	{
+	//		outcode(int($1), &nullgen, 0, &nullgen);
+	//	}
+	SYSCALL
+	BEQ	R1, 2(PC)
+	RET
+
+
+	// More JMP/JAL cases, and canonical names JMP, CALL.
+
+	JAL	foo(SB)	// CALL foo(SB)
+	BEQ	R1, 2(PC)
+	JMP	foo(SB)
+	CALL	foo(SB)
+
+	// END
+	//
+	//	LEND	comma // asm doesn't support the trailing comma.
+	//	{
+	//		outcode(int($1), &nullgen, 0, &nullgen);
+	//	}
+	END
diff --git a/src/cmd/asm/internal/asm/testdata/ppc64.s b/src/cmd/asm/internal/asm/testdata/ppc64.s
index 2e3bf3b..d1ebaa2 100644
--- a/src/cmd/asm/internal/asm/testdata/ppc64.s
+++ b/src/cmd/asm/internal/asm/testdata/ppc64.s
@@ -594,6 +594,15 @@ label1:
 //	}
 	RLWMI	R1, R2, 4, 5, R3 // RLWMI	R1, R2, $201326592, R3
 
+
+// opcodes added with constant shift counts, not masks
+
+	RLDICR	$3, R2, $24, R4
+
+	RLDICL	$1, R2, $61, R6
+
+	RLDIMI  $7, R2, $52, R7
+
 //
 // load/store multiple
 //
@@ -664,6 +673,346 @@ label1:
 	DCBF	(R1)
 	DCBF	(R1+R2) // DCBF	(R1)(R2*1)
 
+//	VMX instructions
+
+//	Described as:
+//	<instruction type>, <instruction format>
+//	<go asm operand order> produces
+//	<Power ISA operand order>
+
+//	Vector load, VX-form
+//	<MNEMONIC> (RB)(RA*1),VRT produces
+//	<mnemonic> VRT,RA,RB
+	LVEBX	(R1)(R2*1), V0
+	LVEHX	(R3)(R4*1), V1
+	LVEWX	(R5)(R6*1), V2
+	LVX	(R7)(R8*1), V3
+	LVXL	(R9)(R10*1), V4
+	LVSL	(R11)(R12*1), V5
+	LVSR	(R14)(R15*1), V6
+
+//	Vector store, VX-form
+//	<MNEMONIC> VRT,(RB)(RA*1) produces
+//	<mnemonic> VRT,RA,RB
+	STVEBX	V31, (R1)(R2*1)
+	STVEHX	V30, (R2)(R3*1)
+	STVEWX	V29, (R4)(R5*1)
+	STVX	V28, (R6)(R7*1)
+	STVXL	V27, (R9)(R9*1)
+
+//	Vector AND, VX-form
+//	<MNEMONIC> VRA,VRB,VRT produces
+//	<mnemonic> VRT,VRA,VRB
+	VANDL	V10, V9, V8
+	VANDC	V15, V14, V13
+	VNAND	V19, V18, V17
+
+//	Vector OR, VX-form
+//	<MNEMONIC> VRA,VRB,VRT produces
+//	<mnemonic> VRT,VRA,VRB
+	VORL	V26, V25, V24
+	VORC	V23, V22, V21
+	VNOR	V20, V19, V18
+	VXOR	V17, V16, V15
+	VEQV	V14, V13, V12
+
+//	Vector ADD, VX-form
+//	<MNEMONIC> VRA,VRB,VRT produces
+//	<mnemonic> VRT,VRA,VRB
+	VADDUBM	V3, V2, V1
+	VADDUHM	V3, V2, V1
+	VADDUWM	V3, V2, V1
+	VADDUDM	V3, V2, V1
+	VADDUQM	V3, V2, V1
+	VADDCUQ	V3, V2, V1
+	VADDCUW	V3, V2, V1
+	VADDUBS	V3, V2, V1
+	VADDUHS	V3, V2, V1
+	VADDUWS	V3, V2, V1
+	VADDSBS	V3, V2, V1
+	VADDSHS	V3, V2, V1
+	VADDSWS	V3, V2, V1
+
+//	Vector ADD extended, VA-form
+//	<MNEMONIC> VRA,VRB,VRC,VRT produces
+//	<mnemonic> VRT,VRA,VRB,VRC
+	VADDEUQM V4, V3, V2, V1
+	VADDECUQ V4, V3, V2, V1
+
+//	Vector SUB, VX-form
+//	<MNEMONIC> VRA,VRB,VRT produces
+//	<mnemonic> VRT,VRA,VRB
+	VSUBUBM	V3, V2, V1
+	VSUBUHM	V3, V2, V1
+	VSUBUWM	V3, V2, V1
+	VSUBUDM	V3, V2, V1
+	VSUBUQM	V3, V2, V1
+	VSUBCUQ	V3, V2, V1
+	VSUBCUW	V3, V2, V1
+	VSUBUBS	V3, V2, V1
+	VSUBUHS	V3, V2, V1
+	VSUBUWS	V3, V2, V1
+	VSUBSBS	V3, V2, V1
+	VSUBSHS	V3, V2, V1
+	VSUBSWS	V3, V2, V1
+
+//	Vector SUB extended, VA-form
+//	<MNEMONIC> VRA,VRB,VRC,VRT produces
+//	<mnemonic> VRT,VRA,VRB,VRC
+	VSUBEUQM V4, V3, V2, V1
+	VSUBECUQ V4, V3, V2, V1
+
+//	Vector rotate, VX-form
+//	<MNEMONIC> VRA,VRB,VRT produces
+//	<mnemonic> VRT,VRA,VRB
+	VRLB	V2, V1, V0
+	VRLH	V2, V1, V0
+	VRLW	V2, V1, V0
+	VRLD	V2, V1, V0
+
+//	Vector shift, VX-form
+//	<MNEMONIC> VRA,VRB,VRT
+//	<mnemonic> VRT,VRA,VRB
+	VSLB	V2, V1, V0
+	VSLH	V2, V1, V0
+	VSLW	V2, V1, V0
+	VSL	V2, V1, V0
+	VSLO	V2, V1, V0
+	VSRB	V2, V1, V0
+	VSRH	V2, V1, V0
+	VSRW	V2, V1, V0
+	VSR	V2, V1, V0
+	VSRO	V2, V1, V0
+	VSLD	V2, V1, V0
+	VSRD	V2, V1, V0
+	VSRAB	V2, V1, V0
+	VSRAH	V2, V1, V0
+	VSRAW	V2, V1, V0
+	VSRAD	V2, V1, V0
+
+//	Vector shift by octect immediate, VA-form with SHB 4-bit field
+//	<MNEMONIC> SHB,VRA,VRB,VRT produces
+//	<mnemonic> VRT,VRA,VRB,SHB
+	VSLDOI	$4, V2, V1, V0
+
+//	Vector count, VX-form
+//	<MNEMONIC> VRB,VRT produces
+//	<mnemonic> VRT,VRB
+	VCLZB	V4, V5
+	VCLZH	V4, V5
+	VCLZW	V4, V5
+	VCLZD	V4, V5
+	VPOPCNTB V4, V5
+	VPOPCNTH V4, V5
+	VPOPCNTW V4, V5
+	VPOPCNTD V4, V5
+
+//	Vector compare, VC-form
+//	<MNEMONIC> VRA,VRB,VRT produces
+//	<mnemonic> VRT,VRA,VRB
+//	* Note: 'CC' suffix denotes Rc=1
+//	  i.e. vcmpequb. v3,v1,v2 equals VCMPEQUBCC V1,V2,V3
+	VCMPEQUB    V3, V2, V1
+	VCMPEQUBCC  V3, V2, V1
+	VCMPEQUH    V3, V2, V1
+	VCMPEQUHCC  V3, V2, V1
+	VCMPEQUW    V3, V2, V1
+	VCMPEQUWCC  V3, V2, V1
+	VCMPEQUD    V3, V2, V1
+	VCMPEQUDCC  V3, V2, V1
+	VCMPGTUB    V3, V2, V1
+	VCMPGTUBCC  V3, V2, V1
+	VCMPGTUH    V3, V2, V1
+	VCMPGTUHCC  V3, V2, V1
+	VCMPGTUW    V3, V2, V1
+	VCMPGTUWCC  V3, V2, V1
+	VCMPGTUD    V3, V2, V1
+	VCMPGTUDCC  V3, V2, V1
+	VCMPGTSB    V3, V2, V1
+	VCMPGTSBCC  V3, V2, V1
+	VCMPGTSH    V3, V2, V1
+	VCMPGTSHCC  V3, V2, V1
+	VCMPGTSW    V3, V2, V1
+	VCMPGTSWCC  V3, V2, V1
+	VCMPGTSD    V3, V2, V1
+	VCMPGTSDCC  V3, V2, V1
+
+//	Vector permute, VA-form
+//	<MNEMONIC> VRA,VRB,VRC,VRT produces
+//	<mnemonic> VRT,VRA,VRB,VRC
+	VPERM V3, V2, V1, V0
+
+//	Vector select, VA-form
+//	<MNEMONIC> VRA,VRB,VRC,VRT produces
+//	<mnemonic> VRT,VRA,VRB,VRC
+	VSEL  V3, V2, V1, V0
+
+//	Vector splat, VX-form with 4-bit UIM field
+//	<MNEMONIC> UIM,VRB,VRT produces
+//	<mnemonic> VRT,VRB,UIM
+	VSPLTB	  $15, V1, V0
+	VSPLTH	  $7, V1, V0
+	VSPLTW	  $3, V1, V0
+
+//	Vector splat immediate signed, VX-form with 5-bit SIM field
+//	<MNEMONIC> SIM,VRT produces
+//	<mnemonic> VRT,SIM
+	VSPLTISB  $31, V4
+	VSPLTISH  $31, V4
+	VSPLTISW  $31, V4
+
+//	Vector AES cipher, VX-form
+//	<MNEMONIC> VRA,VRB,VRT produces
+//	<mnemonic> VRT,VRA,VRB
+	VCIPHER	      V3, V2, V1
+	VCIPHERLAST   V3, V2, V1
+	VNCIPHER      V3, V2, V1
+	VNCIPHERLAST  V3, V2, V1
+
+//	Vector AES subbytes, VX-form
+//	<MNEMONIC> VRA,VRT produces
+//	<mnemonic> VRT,VRA
+	VSBOX	      V2, V1
+
+//	Vector SHA, VX-form with ST bit field and 4-bit SIX field
+//	<MNEMONIC> SIX,VRA,ST,VRT produces
+//	<mnemonic> VRT,VRA,ST,SIX
+	VSHASIGMAW    $15, V1, $1, V0
+	VSHASIGMAD    $15, V1, $1, V0
+
+//	VSX instructions
+//	Described as:
+//	<instruction type>, <instruction format>
+//	<go asm operand order> produces
+//	<Power ISA operand order>
+
+//	VSX load, XX1-form
+//	<MNEMONIC> (RB)(RA*1),XT produces
+//	<mnemonic> XT,RA,RB
+	LXVD2X	    (R1)(R2*1), VS0
+	LXVDSX	    (R1)(R2*1), VS0
+	LXVW4X	    (R1)(R2*1), VS0
+	LXSDX	    (R1)(R2*1), VS0
+	LXSIWAX	    (R1)(R2*1), VS0
+	LXSIWZX	    (R1)(R2*1), VS0
+
+//	VSX store, XX1-form
+//	<MNEMONIC> XS,(RB)(RA*1) produces
+//	<mnemonic> XS,RA,RB
+	STXVD2X	    VS63, (R1)(R2*1)
+	STXVW4X	    VS63, (R1)(R2*1)
+	STXSDX	    VS63, (R1)(R2*1)
+	STXSIWX	    VS63, (R1)(R2*1)
+
+//	VSX move from VSR, XX1-form
+//	<MNEMONIC> XS,RA produces
+//	<mnemonic> RA,XS
+	MFVSRD	    VS0, R1
+	MFVSRWZ	    VS33, R1
+
+//	VSX move to VSR, XX1-form
+//	<MNEMONIC> RA,XT produces
+//	<mnemonic> XT,RA
+	MTVSRD	    R1, VS0
+	MTVSRWA	    R1, VS31
+	MTVSRWZ	    R1, VS63
+
+//	VSX AND, XX3-form
+//	<MNEMONIC> XA,XB,XT produces
+//	<mnemonic> XT,XA,XB
+	XXLANDQ	    VS0,VS1,VS32
+	XXLANDC	    VS0,VS1,VS32
+	XXLEQV	    VS0,VS1,VS32
+	XXLNAND	    VS0,VS1,VS32
+
+//	VSX OR, XX3-form
+//	<MNEMONIC> XA,XB,XT produces
+//	<mnemonic> XT,XA,XB
+	XXLORC	    VS0,VS1,VS32
+	XXLNOR	    VS0,VS1,VS32
+	XXLORQ	    VS0,VS1,VS32
+	XXLXOR	    VS0,VS1,VS32
+
+//	VSX select, XX4-form
+//	<MNEMONIC> XA,XB,XC,XT produces
+//	<mnemonic> XT,XA,XB,XC
+	XXSEL	    VS0,VS1,VS3,VS32
+
+//	VSX merge, XX3-form
+//	<MNEMONIC> XA,XB,XT produces
+//	<mnemonic> XT,XA,XB
+	XXMRGHW	    VS0,VS1,VS32
+	XXMRGLW	    VS0,VS1,VS32
+
+//	VSX splat, XX2-form
+//	<MNEMONIC> XB,UIM,XT produces
+//	<mnemonic> XT,XB,UIM
+	XXSPLTW	    VS0,$3,VS32
+
+//	VSX permute, XX3-form
+//	<MNEMONIC> XA,XB,DM,XT produces
+//	<mnemonic> XT,XA,XB,DM
+	XXPERMDI    VS0,VS1,$3,VS32
+
+//	VSX shift, XX3-form
+//	<MNEMONIC> XA,XB,SHW,XT produces
+//	<mnemonic> XT,XA,XB,SHW
+	XXSLDWI	    VS0,VS1,$3,VS32
+
+//	VSX scalar FP-FP conversion, XX2-form
+//	<MNEMONIC> XB,XT produces
+//	<mnemonic> XT,XB
+	XSCVDPSP    VS0,VS32
+	XSCVSPDP    VS0,VS32
+	XSCVDPSPN   VS0,VS32
+	XSCVSPDPN   VS0,VS32
+
+//	VSX vector FP-FP conversion, XX2-form
+//	<MNEMONIC> XB,XT produces
+//	<mnemonic> XT,XB
+	XVCVDPSP    VS0,VS32
+	XVCVSPDP    VS0,VS32
+
+//	VSX scalar FP-integer conversion, XX2-form
+//	<MNEMONIC> XB,XT produces
+//	<mnemonic> XT,XB
+	XSCVDPSXDS  VS0,VS32
+	XSCVDPSXWS  VS0,VS32
+	XSCVDPUXDS  VS0,VS32
+	XSCVDPUXWS  VS0,VS32
+
+//	VSX scalar integer-FP conversion, XX2-form
+//	<MNEMONIC> XB,XT produces
+//	<mnemonic> XT,XB
+	XSCVSXDDP   VS0,VS32
+	XSCVUXDDP   VS0,VS32
+	XSCVSXDSP   VS0,VS32
+	XSCVUXDSP   VS0,VS32
+
+//	VSX vector FP-integer conversion, XX2-form
+//	<MNEMONIC> XB,XT produces
+//	<mnemonic> XT,XB
+	XVCVDPSXDS  VS0,VS32
+	XVCVDPSXWS  VS0,VS32
+	XVCVDPUXDS  VS0,VS32
+	XVCVDPUXWS  VS0,VS32
+	XVCVSPSXDS  VS0,VS32
+	XVCVSPSXWS  VS0,VS32
+	XVCVSPUXDS  VS0,VS32
+	XVCVSPUXWS  VS0,VS32
+
+//	VSX scalar integer-FP conversion, XX2-form
+//	<MNEMONIC> XB,XT produces
+//	<mnemonic> XT,XB
+	XVCVSXDDP   VS0,VS32
+	XVCVSXWDP   VS0,VS32
+	XVCVUXDDP   VS0,VS32
+	XVCVUXWDP   VS0,VS32
+	XVCVSXDSP   VS0,VS32
+	XVCVSXWSP   VS0,VS32
+	XVCVUXDSP   VS0,VS32
+	XVCVUXWSP   VS0,VS32
+
 //
 // NOP
 //
diff --git a/src/cmd/asm/internal/asm/testdata/s390x.s b/src/cmd/asm/internal/asm/testdata/s390x.s
index 7729384..badedc1 100644
--- a/src/cmd/asm/internal/asm/testdata/s390x.s
+++ b/src/cmd/asm/internal/asm/testdata/s390x.s
@@ -13,6 +13,13 @@ TEXT main·foo(SB),7,$16-0 // TEXT main.foo(SB), 7, $16-0
 	MOVDBR	R1, R2                // b90f0021
 	MOVWBR	R3, R4                // b91f0043
 
+	MOVDEQ	R0, R1                // b9e28010
+	MOVDGE	R2, R3                // b9e2a032
+	MOVDGT	R4, R5                // b9e22054
+	MOVDLE	R6, R7                // b9e2c076
+	MOVDLT	R8, R9                // b9e24098
+	MOVDNE	R10, R11              // b9e270ba
+
 	MOVD	(R15), R1             // e310f0000004
 	MOVW	(R15), R2             // e320f0000014
 	MOVH	(R15), R3             // e330f0000015
@@ -45,24 +52,34 @@ TEXT main·foo(SB),7,$16-0 // TEXT main.foo(SB), 7, $16-0
 
 	ADD	R1, R2                // b9e81022
 	ADD	R1, R2, R3            // b9e81032
-	ADD	$8192, R1             // c21800002000
+	ADD	$8192, R1             // a71b2000
 	ADD	$8192, R1, R2         // ec21200000d9
+	ADD	$32768, R1            // c21800008000
+	ADD	$32768, R1, R2        // b9040021c22800008000
 	ADDC	R1, R2                // b9ea1022
-	ADDC	$1, R1, R2            // b9040021c22a00000001
+	ADDC	$1, R1, R2            // ec21000100db
 	ADDC	R1, R2, R3            // b9ea1032
+	ADDW	R1, R2                // 1a21
+	ADDW	R1, R2, R3            // b9f81032
+	ADDW	$8192, R1             // a71a2000
+	ADDW	$8192, R1, R2         // ec21200000d8
 	SUB	R3, R4                // b9090043
 	SUB	R3, R4, R5            // b9e93054
-	SUB	$8192, R3             // c238ffffe000
+	SUB	$8192, R3             // a73be000
 	SUB	$8192, R3, R4         // ec43e00000d9
 	SUBC	R1, R2                // b90b0021
-	SUBC	$1, R1, R2            // b9040021c22affffffff
+	SUBC	$1, R1, R2            // ec21ffff00db
 	SUBC	R2, R3, R4            // b9eb2043
+	SUBW	R3, R4                // 1b43
+	SUBW	R3, R4, R5            // b9f93054
+	SUBW	$8192, R1             // c21500002000
+	SUBW	$8192, R1, R2         // 1821c22500002000
 	MULLW	R6, R7                // b91c0076
 	MULLW	R6, R7, R8            // b9040087b91c0086
-	MULLW	$8192, R6             // a76d2000
-	MULLW	$8192, R6, R7         // b9040076a77d2000
-	MULLW	$-65537, R8           // c280fffeffff
-	MULLW   $-65537, R8, R9       // b9040098c290fffeffff
+	MULLW	$8192, R6             // a76c2000
+	MULLW	$8192, R6, R7         // 1876a77c2000
+	MULLW	$-32769, R8           // c281ffff7fff
+	MULLW   $-32769, R8, R9       // 1898c291ffff7fff
 	MULLD	$-2147483648, R1      // c21080000000
 	MULLD   $-2147483648, R1, R2  // b9040021c22080000000
 	MULHD	R9, R8                // b90400b8b98600a9ebb9003f000ab98000b8b90900abebb8003f000ab98000b9b9e9b08a
@@ -73,10 +90,99 @@ TEXT main·foo(SB),7,$16-0 // TEXT main.foo(SB), 7, $16-0
 	DIVD	R1, R2, R3            // b90400b2b90d00a1b904003b
 	DIVW	R4, R5                // b90400b5b91d00a4b904005b
 	DIVW	R4, R5, R6            // b90400b5b91d00a4b904006b
-	DIVDU	R7, R8                // b90400a0b90400b8b98700a7b904008b
-	DIVDU	R7, R8, R9            // b90400a0b90400b8b98700a7b904009b
-	DIVWU	R1, R2                // b90400a0b90400b2b99700a1b904002b
-	DIVWU	R1, R2, R3            // b90400a0b90400b2b99700a1b904003b
+	DIVDU	R7, R8                // a7a90000b90400b8b98700a7b904008b
+	DIVDU	R7, R8, R9            // a7a90000b90400b8b98700a7b904009b
+	DIVWU	R1, R2                // a7a90000b90400b2b99700a1b904002b
+	DIVWU	R1, R2, R3            // a7a90000b90400b2b99700a1b904003b
+	MODD	R1, R2                // b90400b2b90d00a1b904002a
+	MODD	R1, R2, R3            // b90400b2b90d00a1b904003a
+	MODW	R4, R5                // b90400b5b91d00a4b904005a
+	MODW	R4, R5, R6            // b90400b5b91d00a4b904006a
+	MODDU	R7, R8                // a7a90000b90400b8b98700a7b904008a
+	MODDU	R7, R8, R9            // a7a90000b90400b8b98700a7b904009a
+	MODWU	R1, R2                // a7a90000b90400b2b99700a1b904002a
+	MODWU	R1, R2, R3            // a7a90000b90400b2b99700a1b904003a
+	NEG	R1                    // b9030011
+	NEG	R1, R2                // b9030021
+	NEGW	R1                    // b9130011
+	NEGW	R1, R2                // b9130021
+	FLOGR	R2, R2                // b9830022
+
+	AND	R1, R2                // b9800021
+	AND	R1, R2, R3            // b9e42031
+	AND	$-2, R1               // a517fffe
+	AND	$-65536, R1           // c01bffff0000
+	AND	$1, R1                // c0a100000001b980001a
+	ANDW	R1, R2                // 1421
+	ANDW	R1, R2, R3            // b9f42031
+	ANDW	$1, R1                // c01b00000001
+	ANDW	$131071, R1           // a5160001
+	ANDW	$65536, R1            // c01b00010000
+	ANDW	$-2, R1               // a517fffe
+	OR	R1, R2                // b9810021
+	OR	R1, R2, R3            // b9e62031
+	OR	$1, R1                // a51b0001
+	OR	$131071, R1           // c01d0001ffff
+	OR	$65536, R1            // c01d00010000
+	OR	$-2, R1               // c0a1fffffffeb981001a
+	ORW	R1, R2                // 1621
+	ORW	R1, R2, R3            // b9f62031
+	ORW	$1, R1                // a51b0001
+	ORW	$131071, R1           // c01d0001ffff
+	ORW	$65536, R1            // a51a0001
+	ORW	$-2, R1               // c01dfffffffe
+	XOR	R1, R2                // b9820021
+	XOR	R1, R2, R3            // b9e72031
+	XOR	$1, R1                // c01700000001
+	XOR	$131071, R1           // c0170001ffff
+	XOR	$65536, R1            // c01700010000
+	XOR	$-2, R1               // c0a1fffffffeb982001a
+	XORW	R1, R2                // 1721
+	XORW	R1, R2, R3            // b9f72031
+	XORW	$1, R1                // c01700000001
+	XORW	$131071, R1           // c0170001ffff
+	XORW	$65536, R1            // c01700010000
+	XORW	$-2, R1               // c017fffffffe
+
+	ADD	-524288(R1), R2       // e32010008008
+	ADD	524287(R3), R4        // e3403fff7f08
+	ADD	-524289(R1), R2       // c0a1fff7ffffe32a10000008
+	ADD	524288(R3), R4        // c0a100080000e34a30000008
+	ADD	-524289(R1)(R2*1), R3 // c0a1fff7ffff41aa2000e33a10000008
+	ADD	524288(R3)(R4*1), R5  // c0a10008000041aa4000e35a30000008
+	ADDC	(R1), R2              // e3201000000a
+	ADDW	(R5), R6              // 5a605000
+	ADDW	4095(R7), R8          // 5a807fff
+	ADDW	-1(R1), R2            // e3201fffff5a
+	ADDW	4096(R3), R4          // e3403000015a
+	MULLD	(R1)(R2*1), R3        // e3321000000c
+	MULLW	(R3)(R4*1), R5        // 71543000
+	MULLW	4096(R3), R4          // e34030000151
+	SUB	(R1), R2              // e32010000009
+	SUBC	(R1), R2              // e3201000000b
+	SUBE	(R1), R2              // e32010000089
+	SUBW	(R1), R2              // 5b201000
+	SUBW	-1(R1), R2            // e3201fffff5b
+	AND	(R1), R2              // e32010000080
+	ANDW	(R1), R2              // 54201000
+	ANDW	-1(R1), R2            // e3201fffff54
+	OR	(R1), R2              // e32010000081
+	ORW	(R1), R2              // 56201000
+	ORW	-1(R1), R2            // e3201fffff56
+	XOR	(R1), R2              // e32010000082
+	XORW	(R1), R2              // 57201000
+	XORW	-1(R1), R2            // e3201fffff57
+
+	LAA	R1, R2, 524287(R3)    // eb213fff7ff8
+	LAAG	R4, R5, -524288(R6)   // eb54600080e8
+	LAAL	R7, R8, 8192(R9)      // eb87900002fa
+	LAALG	R10, R11, -8192(R12)  // ebbac000feea
+	LAN	R1, R2, (R3)          // eb21300000f4
+	LANG	R4, R5, (R6)          // eb54600000e4
+	LAX	R7, R8, (R9)          // eb87900000f7
+	LAXG	R10, R11, (R12)       // ebbac00000e7
+	LAO	R1, R2, (R3)          // eb21300000f6
+	LAOG	R4, R5, (R6)          // eb54600000e6
 
 	XC	$8, (R15), n-8(SP)       // XC  (R15), $8, n-8(SP)       // d707f010f000
 	NC	$8, (R15), n-8(SP)       // NC  (R15), $8, n-8(SP)       // d407f010f000
@@ -87,10 +193,14 @@ TEXT main·foo(SB),7,$16-0 // TEXT main.foo(SB), 7, $16-0
 	MVC	$256, 8192(R1), 8192(R2) // MVC 8192(R1), $256, 8192(R2) // b90400a2c2a800002000b90400b1c2b800002000d2ffa000b000
 
 	CMP	R1, R2                 // b9200012
+	CMP	R3, $32767             // a73f7fff
+	CMP	R3, $32768             // c23c00008000
 	CMP	R3, $-2147483648       // c23c80000000
 	CMPU	R4, R5                 // b9210045
 	CMPU	R6, $4294967295        // c26effffffff
 	CMPW	R7, R8                 // 1978
+	CMPW	R9, $-32768            // a79e8000
+	CMPW	R9, $-32769            // c29dffff7fff
 	CMPW	R9, $-2147483648       // c29d80000000
 	CMPWU	R1, R2                 // 1512
 	CMPWU	R3, $4294967295        // c23fffffffff
@@ -101,6 +211,8 @@ TEXT main·foo(SB),7,$16-0 // TEXT main.foo(SB), 7, $16-0
 	BLE	0(PC)                  // a7c40000
 	BGT	0(PC)                  // a7240000
 	BGE	0(PC)                  // a7a40000
+	BLTU	0(PC)                  // a7540000
+	BLEU	0(PC)                  // a7d40000
 
 	CMPBNE	R1, R2, 0(PC)          // ec1200007064
 	CMPBEQ	R3, R4, 0(PC)          // ec3400008064
@@ -167,6 +279,8 @@ TEXT main·foo(SB),7,$16-0 // TEXT main.foo(SB), 7, $16-0
 	FABS	F1, F2                 // b3100021
 	FSQRTS	F3, F4                 // b3140043
 	FSQRT	F5, F15                // b31500f5
+	FIEBR	$0, F0, F1             // b3570010
+	FIDBR	$7, F2, F3             // b35f7032
 
 	VL	(R15), V1              // e710f0000006
 	VST	V1, (R15)              // e710f000000e
@@ -209,9 +323,20 @@ TEXT main·foo(SB),7,$16-0 // TEXT main.foo(SB), 7, $16-0
 	VERIMB	$2, V31, V1, V2         // VERIMB  V31, V1, $2, V2         // e72f10020472
 	VSEL	V1, V2, V3, V4          // VSEL    V2, V3, V1, V4          // e7412000308d
 	VGFMAH	V21, V31, V24, V0       // VGFMAH  V31, V24, V21, V0       // e705f10087bc
-	WFMSDB	V2, V25, V24, V31       // WFMSDB  V25, V24, V2, V31       // e7f298038b8e
+	VFMADB	V16, V8, V9, V10        // VFMADB  V8, V9, V16, V10        // e7a08300948f
+	WFMADB	V17, V18, V19, V20      // WFMADB  V18, V19, V17, V20      // e74123083f8f
+	VFMSDB	V2, V25, V24, V31       // VFMSDB  V25, V24, V2, V31       // e7f293008b8e
+	WFMSDB	V31, V2, V3, V4         // WFMSDB  V2, V3, V31, V4         // e74f2308348e
 	VPERM	V31, V0, V2, V3         // VPERM   V0, V2, V31, V3         // e73f0000248c
 	VPDI	$1, V2, V31, V1         // VPDI    V2, V31, $1, V1         // e712f0001284
+	VLEG	$1, (R3), V1            // VLEG    (R3), $1, V1            // e71030001002
+	VLEF	$2, (R0), V31           // VLEF    (R0), $2, V31           // e7f000002803
+	VLEH	$3, (R12), V16          // VLEH    (R12), $3, V16          // e700c0003801
+	VLEB	$15, 4095(R9), V15      // VLEB    4095(R9), $15, V15      // e7f09ffff000
+	VSTEG	$1, V30, (R1)(R2*1)     // VSTEG   V30, $1, (R1)(R2*1)     // e7e21000180a
+	VSTEF	$3, V2, (R9)            // VSTEF   V2, $3, (R9)            // e7209000300b
+	VSTEH	$7, V31, (R2)           // VSTEH   V31, $7, (R2)           // e7f020007809
+	VSTEB	$15, V29, 4094(R12)     // VSTEB   V29, $15, 4094(R12)     // e7d0cffef808
 
 	RET
 
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go
index 4557c2a..bd90b82 100644
--- a/src/cmd/asm/internal/flags/flags.go
+++ b/src/cmd/asm/internal/flags/flags.go
@@ -15,7 +15,7 @@ import (
 
 var (
 	Debug      = flag.Bool("debug", false, "dump instructions as they are parsed")
-	OutputFile = flag.String("o", "", "output file; default foo.6 for /a/b/c/foo.s on amd64")
+	OutputFile = flag.String("o", "", "output file; default foo.o for /a/b/c/foo.s as first argument")
 	PrintOut   = flag.Bool("S", false, "print assembly and machine code")
 	TrimPath   = flag.String("trimpath", "", "remove prefix from recorded source file paths")
 	Shared     = flag.Bool("shared", false, "generate code that can be linked into a shared library")
@@ -49,7 +49,7 @@ func (m *MultiFlag) Set(val string) error {
 }
 
 func Usage() {
-	fmt.Fprintf(os.Stderr, "usage: asm [options] file.s\n")
+	fmt.Fprintf(os.Stderr, "usage: asm [options] file.s ...\n")
 	fmt.Fprintf(os.Stderr, "Flags:\n")
 	flag.PrintDefaults()
 	os.Exit(2)
@@ -58,12 +58,15 @@ func Usage() {
 func Parse() {
 	flag.Usage = Usage
 	flag.Parse()
-	if flag.NArg() != 1 {
+	if flag.NArg() == 0 {
 		flag.Usage()
 	}
 
 	// Flag refinement.
 	if *OutputFile == "" {
+		if flag.NArg() != 1 {
+			flag.Usage()
+		}
 		input := filepath.Base(flag.Arg(0))
 		if strings.HasSuffix(input, ".s") {
 			input = input[:len(input)-2]
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index c612583..13e5302 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -24,7 +24,7 @@ func main() {
 	log.SetFlags(0)
 	log.SetPrefix("asm: ")
 
-	GOARCH := obj.Getgoarch()
+	GOARCH := obj.GOARCH
 
 	architecture := arch.Set(GOARCH)
 	if architecture == nil {
@@ -51,25 +51,36 @@ func main() {
 	defer bio.MustClose(out)
 	buf := bufio.NewWriter(bio.MustWriter(out))
 
-	fmt.Fprintf(buf, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion())
+	fmt.Fprintf(buf, "go object %s %s %s\n", obj.GOOS, obj.GOARCH, obj.Version)
 	fmt.Fprintf(buf, "!\n")
 
-	lexer := lex.NewLexer(flag.Arg(0), ctxt)
-	parser := asm.NewParser(ctxt, architecture, lexer)
-	diag := false
-	ctxt.DiagFunc = func(format string, args ...interface{}) {
-		diag = true
-		log.Printf(format, args...)
+	var ok, diag bool
+	var failedFile string
+	for _, f := range flag.Args() {
+		lexer := lex.NewLexer(f, ctxt)
+		parser := asm.NewParser(ctxt, architecture, lexer)
+		ctxt.DiagFunc = func(format string, args ...interface{}) {
+			diag = true
+			log.Printf(format, args...)
+		}
+		pList := obj.Linknewplist(ctxt)
+		pList.Firstpc, ok = parser.Parse()
+		if !ok {
+			failedFile = f
+			break
+		}
 	}
-	pList := obj.Linknewplist(ctxt)
-	var ok bool
-	pList.Firstpc, ok = parser.Parse()
 	if ok {
 		// reports errors to parser.Errorf
 		obj.Writeobjdirect(ctxt, buf)
 	}
 	if !ok || diag {
-		log.Printf("assembly of %s failed", flag.Arg(0))
+		if failedFile != "" {
+			log.Printf("assembly of %s failed", failedFile)
+		} else {
+			log.Print("assembly failed")
+		}
+		out.Close()
 		os.Remove(*flags.OutputFile)
 		os.Exit(1)
 	}
diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go
index 000ecd4..8ce8241 100644
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -87,6 +87,7 @@ func (f *File) ReadGo(name string) {
 			if cg != nil {
 				f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name)
 				f.Preamble += commentText(cg) + "\n"
+				f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n"
 			}
 		}
 	}
@@ -296,7 +297,7 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
 
 	// everything else just recurs
 	default:
-		error_(token.NoPos, "unexpected type %T in walk", x, visit)
+		error_(token.NoPos, "unexpected type %T in walk", x)
 		panic("unexpected type")
 
 	case nil:
diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
index d3a7b6d..85441e6 100644
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -53,6 +53,8 @@ For example:
 	// #include <png.h>
 	import "C"
 
+The default pkg-config tool may be changed by setting the PKG_CONFIG environment variable.
+
 When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and
 CGO_LDFLAGS environment variables are added to the flags derived from
 these directives.  Package-specific flags should be set using the
@@ -214,6 +216,13 @@ by making copies of the data.  In pseudo-Go definitions:
 	// C data with explicit length to Go []byte
 	func C.GoBytes(unsafe.Pointer, C.int) []byte
 
+As a special case, C.malloc does not call the C library malloc directly
+but instead calls a Go helper function that wraps the C library malloc
+but guarantees never to return nil. If C's malloc indicates out of memory,
+the helper function crashes the program, like when Go itself runs out
+of memory. Because C.malloc cannot fail, it has no two-result form
+that returns errno.
+
 C references to Go
 
 Go functions can be exported for use by C code in the following way:
@@ -317,6 +326,9 @@ The following options are available when running cgo directly:
 		Write out input file in Go syntax replacing C package
 		names with real values. Used to generate files in the
 		syscall package when bootstrapping a new target.
+	-srcdir directory
+		Find the Go input files, listed on the command line,
+		in directory.
 	-objdir directory
 		Put all generated files in directory.
 	-importpath string
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index fc1d011..670a73f 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -167,7 +167,23 @@ func (p *Package) Translate(f *File) {
 	if len(needType) > 0 {
 		p.loadDWARF(f, needType)
 	}
-	p.rewriteCalls(f)
+	if p.rewriteCalls(f) {
+		// Add `import _cgo_unsafe "unsafe"` as the first decl
+		// after the package statement.
+		imp := &ast.GenDecl{
+			Tok: token.IMPORT,
+			Specs: []ast.Spec{
+				&ast.ImportSpec{
+					Name: ast.NewIdent("_cgo_unsafe"),
+					Path: &ast.BasicLit{
+						Kind:  token.STRING,
+						Value: `"unsafe"`,
+					},
+				},
+			},
+		}
+		f.AST.Decls = append([]ast.Decl{imp}, f.AST.Decls...)
+	}
 	p.rewriteRef(f)
 }
 
@@ -413,6 +429,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
 	var b bytes.Buffer
 	b.WriteString(f.Preamble)
 	b.WriteString(builtinProlog)
+	b.WriteString("#line 1 \"cgo-dwarf-inference\"\n")
 	for i, n := range names {
 		fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i)
 		if n.Kind == "const" {
@@ -578,7 +595,9 @@ func (p *Package) mangleName(n *Name) {
 
 // rewriteCalls rewrites all calls that pass pointers to check that
 // they follow the rules for passing pointers between Go and C.
-func (p *Package) rewriteCalls(f *File) {
+// This returns whether the package needs to import unsafe as _cgo_unsafe.
+func (p *Package) rewriteCalls(f *File) bool {
+	needsUnsafe := false
 	for _, call := range f.Calls {
 		// This is a call to C.xxx; set goname to "xxx".
 		goname := call.Call.Fun.(*ast.SelectorExpr).Sel.Name
@@ -590,18 +609,24 @@ func (p *Package) rewriteCalls(f *File) {
 			// Probably a type conversion.
 			continue
 		}
-		p.rewriteCall(f, call, name)
+		if p.rewriteCall(f, call, name) {
+			needsUnsafe = true
+		}
 	}
+	return needsUnsafe
 }
 
-// rewriteCall rewrites one call to add pointer checks. We replace
-// each pointer argument x with _cgoCheckPointer(x).(T).
-func (p *Package) rewriteCall(f *File, call *Call, name *Name) {
+// rewriteCall rewrites one call to add pointer checks.
+// If any pointer checks are required, we rewrite the call into a
+// function literal that calls _cgoCheckPointer for each pointer
+// 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 {
 	// 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) {
-		return
+		return false
 	}
 
 	any := false
@@ -612,38 +637,60 @@ func (p *Package) rewriteCall(f *File, call *Call, name *Name) {
 		}
 	}
 	if !any {
-		return
+		return false
 	}
 
 	// We need to rewrite this call.
 	//
-	// We are going to rewrite C.f(p) to C.f(_cgoCheckPointer(p)).
-	// If the call to C.f is deferred, that will check p at the
-	// point of the defer statement, not when the function is called, so
-	// rewrite to func(_cgo0 ptype) { C.f(_cgoCheckPointer(_cgo0)) }(p)
-
-	var dargs []ast.Expr
-	if call.Deferred {
-		dargs = make([]ast.Expr, len(name.FuncType.Params))
-	}
+	// We are going to rewrite C.f(p) to
+	//    func (_cgo0 ptype) {
+	//            _cgoCheckPointer(_cgo0)
+	//            C.f(_cgo0)
+	//    }(p)
+	// Using a function literal like this lets us do correct
+	// argument type checking, and works correctly if the call is
+	// deferred.
+	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]
-		darg := origArg
+		nparam := ast.NewIdent(fmt.Sprintf("_cgo%d", i))
+		nargs[i] = nparam
 
-		if call.Deferred {
-			dargs[i] = darg
-			darg = ast.NewIdent(fmt.Sprintf("_cgo%d", i))
-			call.Call.Args[i] = darg
+		// 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
+		}
+
+		params[i] = &ast.Field{
+			Names: []*ast.Ident{nparam},
+			Type:  ptype,
 		}
 
 		if !p.needsPointerCheck(f, param.Go, origArg) {
 			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{
-				darg,
+				nparam,
 			},
 		}
 
@@ -651,95 +698,83 @@ func (p *Package) rewriteCall(f *File, call *Call, name *Name) {
 		// expression.
 		c.Args = p.checkAddrArgs(f, c.Args, origArg)
 
-		// _cgoCheckPointer returns interface{}.
-		// We need to type assert that to the type we want.
-		// If the Go version of this C type uses
-		// unsafe.Pointer, we can't use a type assertion,
-		// because the Go file might not import unsafe.
-		// Instead we use a local variant of _cgoCheckPointer.
-
-		var arg ast.Expr
-		if n := p.unsafeCheckPointerName(param.Go, call.Deferred); n != "" {
-			c.Fun = ast.NewIdent(n)
-			arg = c
-		} else {
-			// In order for the type assertion to succeed,
-			// we need it to match the actual type of the
-			// argument. The only type we have is the
-			// type of the function parameter. We know
-			// that the argument type must be assignable
-			// to the function parameter type, or the code
-			// would not compile, but there is nothing
-			// requiring that the types be exactly the
-			// same. Add a type conversion to the
-			// argument so that the type assertion will
-			// succeed.
-			c.Args[0] = &ast.CallExpr{
-				Fun: param.Go,
-				Args: []ast.Expr{
-					c.Args[0],
-				},
-			}
-
-			arg = &ast.TypeAssertExpr{
-				X:    c,
-				Type: param.Go,
-			}
+		stmt := &ast.ExprStmt{
+			X: c,
 		}
-
-		call.Call.Args[i] = arg
+		stmts = append(stmts, stmt)
 	}
 
-	if call.Deferred {
-		params := make([]*ast.Field, len(name.FuncType.Params))
-		for i, param := range name.FuncType.Params {
-			ptype := param.Go
-			if p.hasUnsafePointer(ptype) {
-				// Avoid generating unsafe.Pointer by using
-				// interface{}. This works because we are
-				// going to call a _cgoCheckPointer function
-				// anyhow.
-				ptype = &ast.InterfaceType{
-					Methods: &ast.FieldList{},
-				}
-			}
-			params[i] = &ast.Field{
-				Names: []*ast.Ident{
-					ast.NewIdent(fmt.Sprintf("_cgo%d", i)),
-				},
-				Type: ptype,
-			}
-		}
-
-		dbody := &ast.CallExpr{
-			Fun:  call.Call.Fun,
-			Args: call.Call.Args,
+	fcall := &ast.CallExpr{
+		Fun:  call.Call.Fun,
+		Args: nargs,
+	}
+	ftype := &ast.FuncType{
+		Params: &ast.FieldList{
+			List: params,
+		},
+	}
+	if name.FuncType.Result != nil {
+		rtype := p.rewriteUnsafe(name.FuncType.Result.Go)
+		if rtype != name.FuncType.Result.Go {
+			needsUnsafe = true
 		}
-		call.Call.Fun = &ast.FuncLit{
-			Type: &ast.FuncType{
-				Params: &ast.FieldList{
-					List: params,
-				},
-			},
-			Body: &ast.BlockStmt{
-				List: []ast.Stmt{
-					&ast.ExprStmt{
-						X: dbody,
-					},
+		ftype.Results = &ast.FieldList{
+			List: []*ast.Field{
+				&ast.Field{
+					Type: rtype,
 				},
 			},
 		}
-		call.Call.Args = dargs
-		call.Call.Lparen = token.NoPos
-		call.Call.Rparen = token.NoPos
+	}
 
-		// There is a Ref pointing to the old call.Call.Fun.
-		for _, ref := range f.Ref {
-			if ref.Expr == &call.Call.Fun {
-				ref.Expr = &dbody.Fun
+	// There is a Ref pointing to the old call.Call.Fun.
+	for _, ref := range f.Ref {
+		if ref.Expr == &call.Call.Fun {
+			ref.Expr = &fcall.Fun
+
+			// If this call expects two results, we have to
+			// adjust the results of the function we generated.
+			if ref.Context == "call2" {
+				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"),
+					})
 			}
 		}
 	}
+
+	var fbody ast.Stmt
+	if ftype.Results == nil {
+		fbody = &ast.ExprStmt{
+			X: fcall,
+		}
+	} else {
+		fbody = &ast.ReturnStmt{
+			Results: []ast.Expr{fcall},
+		}
+	}
+	call.Call.Fun = &ast.FuncLit{
+		Type: ftype,
+		Body: &ast.BlockStmt{
+			List: append(stmts, fbody),
+		},
+	}
+	call.Call.Lparen = token.NoPos
+	call.Call.Rparen = token.NoPos
+
+	return needsUnsafe
 }
 
 // needsPointerCheck returns whether the type t needs a pointer check.
@@ -782,6 +817,11 @@ func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool {
 		if !top {
 			return true
 		}
+		// Check whether this is a pointer to a C union (or class)
+		// type that contains a pointer.
+		if unionWithPointer[t.X] {
+			return true
+		}
 		return p.hasPointer(f, t.X, false)
 	case *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
 		return true
@@ -935,69 +975,52 @@ func (p *Package) isType(t ast.Expr) bool {
 	return false
 }
 
-// unsafeCheckPointerName is given the Go version of a C type. If the
-// type uses unsafe.Pointer, we arrange to build a version of
-// _cgoCheckPointer that returns that type. This avoids using a type
-// assertion to unsafe.Pointer in our copy of user code. We return
-// the name of the _cgoCheckPointer function we are going to build, or
-// the empty string if the type does not use unsafe.Pointer.
-//
-// The deferred parameter is true if this check is for the argument of
-// a deferred function. In that case we need to use an empty interface
-// as the argument type, because the deferred function we introduce in
-// rewriteCall will use an empty interface type, and we can't add a
-// type assertion. This is handled by keeping a separate list, and
-// writing out the lists separately in writeDefs.
-func (p *Package) unsafeCheckPointerName(t ast.Expr, deferred bool) string {
-	if !p.hasUnsafePointer(t) {
-		return ""
-	}
-	var buf bytes.Buffer
-	conf.Fprint(&buf, fset, t)
-	s := buf.String()
-	checks := &p.CgoChecks
-	if deferred {
-		checks = &p.DeferredCgoChecks
-	}
-	for i, t := range *checks {
-		if s == t {
-			return p.unsafeCheckPointerNameIndex(i, deferred)
-		}
-	}
-	*checks = append(*checks, s)
-	return p.unsafeCheckPointerNameIndex(len(*checks)-1, deferred)
-}
-
-// hasUnsafePointer returns whether the Go type t uses unsafe.Pointer.
-// t is the Go version of a C type, so we don't need to handle every case.
-// We only care about direct references, not references via typedefs.
-func (p *Package) hasUnsafePointer(t ast.Expr) bool {
+// rewriteUnsafe returns a version of t with references to unsafe.Pointer
+// rewritten to use _cgo_unsafe.Pointer instead.
+func (p *Package) rewriteUnsafe(t ast.Expr) ast.Expr {
 	switch t := t.(type) {
 	case *ast.Ident:
 		// We don't see a SelectorExpr for unsafe.Pointer;
 		// this is created by code in this file.
-		return t.Name == "unsafe.Pointer"
+		if t.Name == "unsafe.Pointer" {
+			return ast.NewIdent("_cgo_unsafe.Pointer")
+		}
 	case *ast.ArrayType:
-		return p.hasUnsafePointer(t.Elt)
+		t1 := p.rewriteUnsafe(t.Elt)
+		if t1 != t.Elt {
+			r := *t
+			r.Elt = t1
+			return &r
+		}
 	case *ast.StructType:
+		changed := false
+		fields := *t.Fields
+		fields.List = nil
 		for _, f := range t.Fields.List {
-			if p.hasUnsafePointer(f.Type) {
-				return true
+			ft := p.rewriteUnsafe(f.Type)
+			if ft == f.Type {
+				fields.List = append(fields.List, f)
+			} else {
+				fn := *f
+				fn.Type = ft
+				fields.List = append(fields.List, &fn)
+				changed = true
 			}
 		}
+		if changed {
+			r := *t
+			r.Fields = &fields
+			return &r
+		}
 	case *ast.StarExpr: // Pointer type.
-		return p.hasUnsafePointer(t.X)
-	}
-	return false
-}
-
-// unsafeCheckPointerNameIndex returns the name to use for a
-// _cgoCheckPointer variant based on the index in the CgoChecks slice.
-func (p *Package) unsafeCheckPointerNameIndex(i int, deferred bool) string {
-	if deferred {
-		return fmt.Sprintf("_cgoCheckPointerInDefer%d", i)
+		x1 := p.rewriteUnsafe(t.X)
+		if x1 != t.X {
+			r := *t
+			r.X = x1
+			return &r
+		}
 	}
-	return fmt.Sprintf("_cgoCheckPointer%d", i)
+	return t
 }
 
 // rewriteRef rewrites all the C.xxx references in f.AST to refer to the
@@ -1415,6 +1438,10 @@ var tagGen int
 var typedef = make(map[string]*Type)
 var goIdent = make(map[string]*ast.Ident)
 
+// unionWithPointer is true for a Go type that represents a C union (or class)
+// that may contain a pointer. This is used for cgo pointer checking.
+var unionWithPointer = make(map[ast.Expr]bool)
+
 func (c *typeConv) Init(ptrSize, intSize int64) {
 	c.ptrSize = ptrSize
 	c.intSize = intSize
@@ -1464,6 +1491,19 @@ func base(dt dwarf.Type) dwarf.Type {
 	return dt
 }
 
+// unqual strips away qualifiers from a DWARF type.
+// In general we don't care about top-level qualifiers.
+func unqual(dt dwarf.Type) dwarf.Type {
+	for {
+		if d, ok := dt.(*dwarf.QualType); ok {
+			dt = d.Type
+		} else {
+			break
+		}
+	}
+	return dt
+}
+
 // Map from dwarf text names to aliases we use in package "C".
 var dwarfToName = map[string]string{
 	"long int":               "long",
@@ -1641,7 +1681,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
 		case 16:
 			t.Go = c.complex128
 		}
-		if t.Align = t.Size; t.Align >= c.ptrSize {
+		if t.Align = t.Size / 2; t.Align >= c.ptrSize {
 			t.Align = c.ptrSize
 		}
 
@@ -1699,9 +1739,16 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
 		c.ptrs[dt.Type] = append(c.ptrs[dt.Type], t)
 
 	case *dwarf.QualType:
-		// Ignore qualifier.
-		t = c.Type(dt.Type, pos)
-		c.m[dtype] = t
+		t1 := c.Type(dt.Type, pos)
+		t.Size = t1.Size
+		t.Align = t1.Align
+		t.Go = t1.Go
+		if unionWithPointer[t1.Go] {
+			unionWithPointer[t.Go] = true
+		}
+		t.EnumValues = nil
+		t.Typedef = ""
+		t.C.Set("%s "+dt.Qual, t1.C)
 		return t
 
 	case *dwarf.StructType:
@@ -1733,6 +1780,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
 		switch dt.Kind {
 		case "class", "union":
 			t.Go = c.Opaque(t.Size)
+			if c.dwarfHasPointer(dt, pos) {
+				unionWithPointer[t.Go] = true
+			}
 			if t.C.Empty() {
 				t.C.Set("__typeof__(unsigned char[%d])", t.Size)
 			}
@@ -1775,6 +1825,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
 		goIdent[name.Name] = name
 		sub := c.Type(dt.Type, pos)
 		t.Go = name
+		if unionWithPointer[sub.Go] {
+			unionWithPointer[t.Go] = true
+		}
 		t.Size = sub.Size
 		t.Align = sub.Align
 		oldType := typedef[name.Name]
@@ -1905,7 +1958,7 @@ func isStructUnionClass(x ast.Expr) bool {
 // FuncArg returns a Go type with the same memory layout as
 // dtype when used as the type of a C function argument.
 func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
-	t := c.Type(dtype, pos)
+	t := c.Type(unqual(dtype), pos)
 	switch dt := dtype.(type) {
 	case *dwarf.ArrayType:
 		// Arrays are passed implicitly as pointers in C.
@@ -1935,9 +1988,12 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
 				return nil
 			}
 
-			// Remember the C spelling, in case the struct
-			// has __attribute__((unavailable)) on it. See issue 2888.
-			t.Typedef = dt.Name
+			// For a struct/union/class, remember the C spelling,
+			// in case it has __attribute__((unavailable)).
+			// See issue 2888.
+			if isStructUnionClass(t.Go) {
+				t.Typedef = dt.Name
+			}
 		}
 	}
 	return t
@@ -1966,7 +2022,7 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType {
 	if _, ok := dtype.ReturnType.(*dwarf.VoidType); ok {
 		gr = []*ast.Field{{Type: c.goVoid}}
 	} else if dtype.ReturnType != nil {
-		r = c.Type(dtype.ReturnType, pos)
+		r = c.Type(unqual(dtype.ReturnType), pos)
 		gr = []*ast.Field{{Type: r.Go}}
 	}
 	return &FuncType{
@@ -2153,6 +2209,44 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
 	return
 }
 
+// dwarfHasPointer returns whether the DWARF type dt contains a pointer.
+func (c *typeConv) dwarfHasPointer(dt dwarf.Type, pos token.Pos) bool {
+	switch dt := dt.(type) {
+	default:
+		fatalf("%s: unexpected type: %s", lineno(pos), dt)
+		return false
+
+	case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.EnumType,
+		*dwarf.FloatType, *dwarf.ComplexType, *dwarf.FuncType,
+		*dwarf.IntType, *dwarf.UcharType, *dwarf.UintType, *dwarf.VoidType:
+
+		return false
+
+	case *dwarf.ArrayType:
+		return c.dwarfHasPointer(dt.Type, pos)
+
+	case *dwarf.PtrType:
+		return true
+
+	case *dwarf.QualType:
+		return c.dwarfHasPointer(dt.Type, pos)
+
+	case *dwarf.StructType:
+		for _, f := range dt.Field {
+			if c.dwarfHasPointer(f.Type, pos) {
+				return true
+			}
+		}
+		return false
+
+	case *dwarf.TypedefType:
+		if dt.Name == "_GoString_" || dt.Name == "_GoBytes_" {
+			return true
+		}
+		return c.dwarfHasPointer(dt.Type, pos)
+	}
+}
+
 func upper(s string) string {
 	if s == "" {
 		return ""
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index 72ac19a..df27983 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -42,10 +42,6 @@ type Package struct {
 	GoFiles     []string // list of Go files
 	GccFiles    []string // list of gcc output files
 	Preamble    string   // collected preamble for _cgo_export.h
-
-	// See unsafeCheckPointerName.
-	CgoChecks         []string
-	DeferredCgoChecks []string
 }
 
 // A File collects information about a single Go input file.
@@ -147,6 +143,8 @@ var ptrSizeMap = map[string]int64{
 	"amd64":    8,
 	"arm":      4,
 	"arm64":    8,
+	"mips":     4,
+	"mipsle":   4,
 	"mips64":   8,
 	"mips64le": 8,
 	"ppc64":    8,
@@ -160,6 +158,8 @@ var intSizeMap = map[string]int64{
 	"amd64":    8,
 	"arm":      4,
 	"arm64":    8,
+	"mips":     4,
+	"mipsle":   4,
 	"mips64":   8,
 	"mips64le": 8,
 	"ppc64":    8,
@@ -182,6 +182,7 @@ var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information
 // constant values used in the host's C libraries and system calls.
 var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
 
+var srcDir = flag.String("srcdir", "", "source directory")
 var objDir = flag.String("objdir", "", "object directory")
 var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
 var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")
@@ -260,6 +261,9 @@ func main() {
 	// Use the beginning of the md5 of the input to disambiguate.
 	h := md5.New()
 	for _, input := range goFiles {
+		if *srcDir != "" {
+			input = filepath.Join(*srcDir, input)
+		}
 		f, err := os.Open(input)
 		if err != nil {
 			fatalf("%s", err)
@@ -271,6 +275,9 @@ func main() {
 
 	fs := make([]*File, len(goFiles))
 	for i, input := range goFiles {
+		if *srcDir != "" {
+			input = filepath.Join(*srcDir, input)
+		}
 		f := new(File)
 		f.ReadGo(input)
 		f.DiscardCgoDirectives()
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 842b1c5..e82ec37 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -19,7 +19,10 @@ import (
 	"strings"
 )
 
-var conf = printer.Config{Mode: printer.SourcePos, Tabwidth: 8}
+var (
+	conf         = printer.Config{Mode: printer.SourcePos, Tabwidth: 8}
+	noSourceConf = printer.Config{Tabwidth: 8}
+)
 
 // writeDefs creates output files to be compiled by gc and gcc.
 func (p *Package) writeDefs() {
@@ -95,7 +98,19 @@ func (p *Package) writeDefs() {
 	for _, name := range typedefNames {
 		def := typedef[name]
 		fmt.Fprintf(fgo2, "type %s ", name)
-		conf.Fprint(fgo2, fset, def.Go)
+		// We don't have source info for these types, so write them out without source info.
+		// Otherwise types would look like:
+		//
+		// type _Ctype_struct_cb struct {
+		// //line :1
+		//        on_test *[0]byte
+		// //line :1
+		// }
+		//
+		// Which is not useful. Moreover we never override source info,
+		// so subsequent source code uses the same source info.
+		// Moreover, empty file name makes compile emit no source debug info at all.
+		noSourceConf.Fprint(fgo2, fset, def.Go)
 		fmt.Fprintf(fgo2, "\n\n")
 	}
 	if *gccgo {
@@ -111,17 +126,11 @@ func (p *Package) writeDefs() {
 		fmt.Fprint(fgo2, goProlog)
 	}
 
-	for i, t := range p.CgoChecks {
-		n := p.unsafeCheckPointerNameIndex(i, false)
-		fmt.Fprintf(fgo2, "\nfunc %s(p %s, args ...interface{}) %s {\n", n, t, t)
-		fmt.Fprintf(fgo2, "\treturn _cgoCheckPointer(p, args...).(%s)\n", t)
-		fmt.Fprintf(fgo2, "}\n")
+	if fc != nil {
+		fmt.Fprintf(fc, "#line 1 \"cgo-generated-wrappers\"\n")
 	}
-	for i, t := range p.DeferredCgoChecks {
-		n := p.unsafeCheckPointerNameIndex(i, true)
-		fmt.Fprintf(fgo2, "\nfunc %s(p interface{}, args ...interface{}) %s {\n", n, t)
-		fmt.Fprintf(fgo2, "\treturn _cgoCheckPointer(p, args...).(%s)\n", t)
-		fmt.Fprintf(fgo2, "}\n")
+	if fm != nil {
+		fmt.Fprintf(fm, "#line 1 \"cgo-generated-wrappers\"\n")
 	}
 
 	gccgoSymbolPrefix := p.gccgoSymbolPrefix()
@@ -346,11 +355,7 @@ func (p *Package) structType(n *Name) (string, int64) {
 			fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
 			off += pad
 		}
-		qual := ""
-		if c := t.C.String(); c[len(c)-1] == '*' {
-			qual = "const "
-		}
-		fmt.Fprintf(&buf, "\t\t%s%s r;\n", qual, t.C)
+		fmt.Fprintf(&buf, "\t\t%s r;\n", t.C)
 		off += t.Size
 	}
 	if off%p.PtrSize != 0 {
@@ -611,20 +616,10 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
 		}
 	}
 	fmt.Fprintf(fgcc, "%s(", n.C)
-	for i, t := range n.FuncType.Params {
+	for i := range n.FuncType.Params {
 		if i > 0 {
 			fmt.Fprintf(fgcc, ", ")
 		}
-		// We know the type params are correct, because
-		// the Go equivalents had good type params.
-		// However, our version of the type omits the magic
-		// words const and volatile, which can provoke
-		// C compiler warnings. Silence them by casting
-		// all pointers to void*.  (Eventually that will produce
-		// other warnings.)
-		if c := t.C.String(); c[len(c)-1] == '*' {
-			fmt.Fprintf(fgcc, "(void*)")
-		}
 		fmt.Fprintf(fgcc, "a->p%d", i)
 	}
 	fmt.Fprintf(fgcc, ");\n")
@@ -684,14 +679,10 @@ func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
 		}
 	}
 	fmt.Fprintf(fgcc, "%s(", n.C)
-	for i, t := range n.FuncType.Params {
+	for i := range n.FuncType.Params {
 		if i > 0 {
 			fmt.Fprintf(fgcc, ", ")
 		}
-		// Cast to void* to avoid warnings due to omitted qualifiers.
-		if c := t.C.String(); c[len(c)-1] == '*' {
-			fmt.Fprintf(fgcc, "(void*)")
-		}
 		fmt.Fprintf(fgcc, "p%d", i)
 	}
 	fmt.Fprintf(fgcc, ");\n")
@@ -1217,8 +1208,8 @@ var goTypes = map[string]*Type{
 	"uint64":     {Size: 8, Align: 8, C: c("GoUint64")},
 	"float32":    {Size: 4, Align: 4, C: c("GoFloat32")},
 	"float64":    {Size: 8, Align: 8, C: c("GoFloat64")},
-	"complex64":  {Size: 8, Align: 8, C: c("GoComplex64")},
-	"complex128": {Size: 16, Align: 16, C: c("GoComplex128")},
+	"complex64":  {Size: 8, Align: 4, C: c("GoComplex64")},
+	"complex128": {Size: 16, Align: 8, C: c("GoComplex128")},
 }
 
 // Map an ast type to a Type.
@@ -1299,6 +1290,7 @@ func (p *Package) cgoType(e ast.Expr) *Type {
 }
 
 const gccProlog = `
+#line 1 "cgo-gcc-prolog"
 /*
   If x and y are not equal, the type will be invalid
   (have a negative array count) and an inscrutable error will come
@@ -1332,6 +1324,7 @@ const noTsanProlog = `
 
 // This must match the TSAN code in runtime/cgo/libcgo.h.
 const yesTsanProlog = `
+#line 1 "cgo-tsan-prolog"
 #define CGO_NO_SANITIZE_THREAD __attribute__ ((no_sanitize_thread))
 
 long long _cgo_sync __attribute__ ((common));
@@ -1354,6 +1347,7 @@ static void _cgo_tsan_release() {
 var tsanProlog = noTsanProlog
 
 const builtinProlog = `
+#line 1 "cgo-builtin-prolog"
 #include <stddef.h> /* for ptrdiff_t and size_t below */
 
 /* Define intgo when compiling with GCC.  */
@@ -1377,14 +1371,14 @@ func _cgo_runtime_cgocall(unsafe.Pointer, uintptr) int32
 func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr, uintptr)
 
 //go:linkname _cgoCheckPointer runtime.cgoCheckPointer
-func _cgoCheckPointer(interface{}, ...interface{}) interface{}
+func _cgoCheckPointer(interface{}, ...interface{})
 
 //go:linkname _cgoCheckResult runtime.cgoCheckResult
 func _cgoCheckResult(interface{})
 `
 
 const gccgoGoProlog = `
-func _cgoCheckPointer(interface{}, ...interface{}) interface{}
+func _cgoCheckPointer(interface{}, ...interface{})
 
 func _cgoCheckResult(interface{})
 `
@@ -1461,9 +1455,15 @@ const cMallocDefGo = `
 var __cgofn__cgoPREFIX_Cfunc__Cmalloc byte
 var _cgoPREFIX_Cfunc__Cmalloc = unsafe.Pointer(&__cgofn__cgoPREFIX_Cfunc__Cmalloc)
 
+//go:linkname runtime_throw runtime.throw
+func runtime_throw(string)
+
 //go:cgo_unsafe_args
 func _cgo_cmalloc(p0 uint64) (r1 unsafe.Pointer) {
 	_cgo_runtime_cgocall(_cgoPREFIX_Cfunc__Cmalloc, uintptr(unsafe.Pointer(&p0)))
+	if r1 == nil {
+		runtime_throw("runtime: C malloc failed")
+	}
 	return
 }
 `
@@ -1500,6 +1500,7 @@ func (p *Package) cPrologGccgo() string {
 }
 
 const cPrologGccgo = `
+#line 1 "cgo-c-prolog-gccgo"
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -1564,18 +1565,17 @@ typedef struct __go_empty_interface {
 	void *__object;
 } Eface;
 
-extern Eface runtimeCgoCheckPointer(Eface, Slice)
+extern void runtimeCgoCheckPointer(Eface, Slice)
 	__asm__("runtime.cgoCheckPointer")
 	__attribute__((weak));
 
-extern Eface localCgoCheckPointer(Eface, Slice)
+extern void localCgoCheckPointer(Eface, Slice)
 	__asm__("GCCGOSYMBOLPREF._cgoCheckPointer");
 
-Eface localCgoCheckPointer(Eface ptr, Slice args) {
+void localCgoCheckPointer(Eface ptr, Slice args) {
 	if(runtimeCgoCheckPointer) {
-		return runtimeCgoCheckPointer(ptr, args);
+		runtimeCgoCheckPointer(ptr, args);
 	}
-	return ptr;
 }
 
 extern void runtimeCgoCheckResult(Eface)
@@ -1598,6 +1598,7 @@ func (p *Package) gccExportHeaderProlog() string {
 
 const gccExportHeaderProlog = `
 /* Start of boilerplate cgo prologue.  */
+#line 1 "cgo-gcc-export-header-prolog"
 
 #ifndef GO_CGO_PROLOGUE_H
 #define GO_CGO_PROLOGUE_H
@@ -1651,6 +1652,7 @@ const gccExportHeaderEpilog = `
 // We use weak declarations, and test the addresses, so that this code
 // works with older versions of gccgo.
 const gccgoExportFileProlog = `
+#line 1 "cgo-gccgo-export-file-prolog"
 extern _Bool runtime_iscgo __attribute__ ((weak));
 
 static void GoInit(void) __attribute__ ((constructor));
diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go
new file mode 100644
index 0000000..1a64808
--- /dev/null
+++ b/src/cmd/compile/fmt_test.go
@@ -0,0 +1,716 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements TestFormats; a test that verifies
+// format strings in the compiler (this directory and all
+// subdirectories, recursively).
+//
+// TestFormats finds potential (Printf, etc.) format strings.
+// If they are used in a call, the format verbs are verified
+// based on the matching argument type against a precomputed
+// table of valid formats. The knownFormats table can be used
+// to automatically rewrite format strings with the -u flag.
+//
+// A new knownFormats table based on the found formats is printed
+// when the test is run in verbose mode (-v flag). The table
+// needs to be updated whenever a new (type, format) combination
+// is found and the format verb is not 'v' or 'T' (as in "%v" or
+// "%T").
+//
+// Run as: go test -run Formats [-u][-v]
+//
+// Known bugs:
+// - indexed format strings ("%[2]s", etc.) are not supported
+//   (the test will fail)
+// - format strings that are not simple string literals cannot
+//   be updated automatically
+//   (the test will fail with respective warnings)
+// - format strings in _test packages outside the current
+//   package are not processed
+//   (the test will report those files)
+//
+package main_test
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"go/ast"
+	"go/build"
+	"go/constant"
+	"go/format"
+	"go/importer"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"internal/testenv"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"sort"
+	"strconv"
+	"strings"
+	"testing"
+	"unicode/utf8"
+)
+
+var update = flag.Bool("u", false, "update format strings")
+
+// The following variables collect information across all processed files.
+var (
+	fset          = token.NewFileSet()
+	formatStrings = make(map[*ast.BasicLit]bool)      // set of all potential format strings found
+	foundFormats  = make(map[string]bool)             // set of all formats found
+	callSites     = make(map[*ast.CallExpr]*callSite) // map of all calls
+)
+
+// A File is a corresponding (filename, ast) pair.
+type File struct {
+	name string
+	ast  *ast.File
+}
+
+func TestFormats(t *testing.T) {
+	testenv.MustHaveGoBuild(t) // more restrictive than necessary, but that's ok
+
+	// process all directories
+	filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
+		if info.IsDir() {
+			if info.Name() == "testdata" {
+				return filepath.SkipDir
+			}
+
+			importPath := filepath.Join("cmd/compile", path)
+			if blacklistedPackages[filepath.ToSlash(importPath)] {
+				return filepath.SkipDir
+			}
+
+			pkg, err := build.Import(importPath, path, 0)
+			if err != nil {
+				if _, ok := err.(*build.NoGoError); ok {
+					return nil // nothing to do here
+				}
+				t.Fatal(err)
+			}
+			collectPkgFormats(t, pkg)
+		}
+		return nil
+	})
+
+	// test and rewrite formats
+	updatedFiles := make(map[string]File) // files that were rewritten
+	for _, p := range callSites {
+		// test current format literal and determine updated one
+		out := formatReplace(p.str, func(index int, in string) string {
+			if in == "*" {
+				return in // cannot rewrite '*' (as in "%*d")
+			}
+			// in != '*'
+			typ := p.types[index]
+			format := typ + " " + in // e.g., "*Node %n"
+
+			// check if format is known
+			out, known := knownFormats[format]
+
+			// record format if not yet found
+			_, found := foundFormats[format]
+			if !found {
+				foundFormats[format] = true
+			}
+
+			// report an error if the format is unknown and this is the first
+			// time we see it; ignore "%v" and "%T" which are always valid
+			if !known && !found && in != "%v" && in != "%T" {
+				t.Errorf("%s: unknown format %q for %s argument", posString(p.arg), in, typ)
+			}
+
+			if out == "" {
+				out = in
+			}
+			return out
+		})
+
+		// replace existing format literal if it changed
+		if out != p.str {
+			// we cannot replace the argument if it's not a string literal for now
+			// (e.g., it may be "foo" + "bar")
+			lit, ok := p.arg.(*ast.BasicLit)
+			if !ok {
+				delete(callSites, p.call) // treat as if we hadn't found this site
+				continue
+			}
+
+			if testing.Verbose() {
+				fmt.Printf("%s:\n\t- %q\n\t+ %q\n", posString(p.arg), p.str, out)
+			}
+
+			// find argument index of format argument
+			index := -1
+			for i, arg := range p.call.Args {
+				if p.arg == arg {
+					index = i
+					break
+				}
+			}
+			if index < 0 {
+				// we may have processed the same call site twice,
+				// but that shouldn't happen
+				panic("internal error: matching argument not found")
+			}
+
+			// replace literal
+			new := *lit                    // make a copy
+			new.Value = strconv.Quote(out) // this may introduce "-quotes where there were `-quotes
+			p.call.Args[index] = &new
+			updatedFiles[p.file.name] = p.file
+		}
+	}
+
+	// write dirty files back
+	var filesUpdated bool
+	if len(updatedFiles) > 0 && *update {
+		for _, file := range updatedFiles {
+			var buf bytes.Buffer
+			if err := format.Node(&buf, fset, file.ast); err != nil {
+				t.Errorf("WARNING: formatting %s failed: %v", file.name, err)
+				continue
+			}
+			if err := ioutil.WriteFile(file.name, buf.Bytes(), 0x666); err != nil {
+				t.Errorf("WARNING: writing %s failed: %v", file.name, err)
+				continue
+			}
+			fmt.Printf("updated %s\n", file.name)
+			filesUpdated = true
+		}
+	}
+
+	// report all function names containing a format string
+	if len(callSites) > 0 && testing.Verbose() {
+		set := make(map[string]bool)
+		for _, p := range callSites {
+			set[nodeString(p.call.Fun)] = true
+		}
+		var list []string
+		for s := range set {
+			list = append(list, s)
+		}
+		fmt.Println("\nFunctions")
+		printList(list)
+	}
+
+	// report all formats found
+	if len(foundFormats) > 0 && testing.Verbose() {
+		var list []string
+		for s := range foundFormats {
+			list = append(list, fmt.Sprintf("%q: \"\",", s))
+		}
+		fmt.Println("\nvar knownFormats = map[string]string{")
+		printList(list)
+		fmt.Println("}")
+	}
+
+	// check that knownFormats is up to date
+	if !testing.Verbose() && !*update {
+		var mismatch bool
+		for s := range foundFormats {
+			if _, ok := knownFormats[s]; !ok {
+				mismatch = true
+				break
+			}
+		}
+		if !mismatch {
+			for s := range knownFormats {
+				if _, ok := foundFormats[s]; !ok {
+					mismatch = true
+					break
+				}
+			}
+		}
+		if mismatch {
+			t.Errorf("knownFormats is out of date; please run with -v to regenerate")
+		}
+	}
+
+	// all format strings of calls must be in the formatStrings set (self-verification)
+	for _, p := range callSites {
+		if lit, ok := p.arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
+			if formatStrings[lit] {
+				// ok
+				delete(formatStrings, lit)
+			} else {
+				// this should never happen
+				panic(fmt.Sprintf("internal error: format string not found (%s)", posString(lit)))
+			}
+		}
+	}
+
+	// if we have any strings left, we may need to update them manually
+	if len(formatStrings) > 0 && filesUpdated {
+		var list []string
+		for lit := range formatStrings {
+			list = append(list, fmt.Sprintf("%s: %s", posString(lit), nodeString(lit)))
+		}
+		fmt.Println("\nWARNING: Potentially missed format strings")
+		printList(list)
+		t.Fail()
+	}
+
+	fmt.Println()
+}
+
+// A callSite describes a function call that appears to contain
+// a format string.
+type callSite struct {
+	file  File
+	call  *ast.CallExpr // call containing the format string
+	arg   ast.Expr      // format argument (string literal or constant)
+	str   string        // unquoted format string
+	types []string      // argument types
+}
+
+func collectPkgFormats(t *testing.T, pkg *build.Package) {
+	// collect all files
+	var filenames []string
+	filenames = append(filenames, pkg.GoFiles...)
+	filenames = append(filenames, pkg.CgoFiles...)
+	filenames = append(filenames, pkg.TestGoFiles...)
+
+	// TODO(gri) verify _test files outside package
+	for _, name := range pkg.XTestGoFiles {
+		// don't process this test itself
+		if name != "fmt_test.go" && testing.Verbose() {
+			fmt.Printf("WARNING: %s not processed\n", filepath.Join(pkg.Dir, name))
+		}
+	}
+
+	// make filenames relative to .
+	for i, name := range filenames {
+		filenames[i] = filepath.Join(pkg.Dir, name)
+	}
+
+	// parse all files
+	files := make([]*ast.File, len(filenames))
+	for i, filename := range filenames {
+		f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
+		if err != nil {
+			t.Fatal(err)
+		}
+		files[i] = f
+	}
+
+	// typecheck package
+	conf := types.Config{Importer: importer.Default()}
+	etypes := make(map[ast.Expr]types.TypeAndValue)
+	if _, err := conf.Check(pkg.ImportPath, fset, files, &types.Info{Types: etypes}); err != nil {
+		t.Fatal(err)
+	}
+
+	// collect all potential format strings (for extra verification later)
+	for _, file := range files {
+		ast.Inspect(file, func(n ast.Node) bool {
+			if s, ok := stringLit(n); ok && isFormat(s) {
+				formatStrings[n.(*ast.BasicLit)] = true
+			}
+			return true
+		})
+	}
+
+	// collect all formats/arguments of calls with format strings
+	for index, file := range files {
+		ast.Inspect(file, func(n ast.Node) bool {
+			if call, ok := n.(*ast.CallExpr); ok {
+				// ignore blacklisted functions
+				if blacklistedFunctions[nodeString(call.Fun)] {
+					return true
+				}
+				// look for an arguments that might be a format string
+				for i, arg := range call.Args {
+					if s, ok := stringVal(etypes[arg]); ok && isFormat(s) {
+						// make sure we have enough arguments
+						n := numFormatArgs(s)
+						if i+1+n > len(call.Args) {
+							t.Errorf("%s: not enough format args (blacklist %s?)", posString(call), nodeString(call.Fun))
+							break // ignore this call
+						}
+						// assume last n arguments are to be formatted;
+						// determine their types
+						argTypes := make([]string, n)
+						for i, arg := range call.Args[len(call.Args)-n:] {
+							if tv, ok := etypes[arg]; ok {
+								argTypes[i] = typeString(tv.Type)
+							}
+						}
+						// collect call site
+						if callSites[call] != nil {
+							panic("internal error: file processed twice?")
+						}
+						callSites[call] = &callSite{
+							file:  File{filenames[index], file},
+							call:  call,
+							arg:   arg,
+							str:   s,
+							types: argTypes,
+						}
+						break // at most one format per argument list
+					}
+				}
+			}
+			return true
+		})
+	}
+}
+
+// printList prints list in sorted order.
+func printList(list []string) {
+	sort.Strings(list)
+	for _, s := range list {
+		fmt.Println("\t", s)
+	}
+}
+
+// posString returns a string representation of n's position
+// in the form filename:line:col: .
+func posString(n ast.Node) string {
+	if n == nil {
+		return ""
+	}
+	return fset.Position(n.Pos()).String()
+}
+
+// nodeString returns a string representation of n.
+func nodeString(n ast.Node) string {
+	var buf bytes.Buffer
+	if err := format.Node(&buf, fset, n); err != nil {
+		log.Fatal(err) // should always succeed
+	}
+	return buf.String()
+}
+
+// typeString returns a string representation of n.
+func typeString(typ types.Type) string {
+	return filepath.ToSlash(typ.String())
+}
+
+// stringLit returns the unquoted string value and true if
+// n represents a string literal; otherwise it returns ""
+// and false.
+func stringLit(n ast.Node) (string, bool) {
+	if lit, ok := n.(*ast.BasicLit); ok && lit.Kind == token.STRING {
+		s, err := strconv.Unquote(lit.Value)
+		if err != nil {
+			log.Fatal(err) // should not happen with correct ASTs
+		}
+		return s, true
+	}
+	return "", false
+}
+
+// stringVal returns the (unquoted) string value and true if
+// tv is a string constant; otherwise it returns "" and false.
+func stringVal(tv types.TypeAndValue) (string, bool) {
+	if tv.IsValue() && tv.Value != nil && tv.Value.Kind() == constant.String {
+		return constant.StringVal(tv.Value), true
+	}
+	return "", false
+}
+
+// formatIter iterates through the string s in increasing
+// index order and calls f for each format specifier '%..v'.
+// The arguments for f describe the specifier's index range.
+// If a format specifier contains a  "*", f is called with
+// the index range for "*" alone, before being called for
+// the entire specifier. The result of f is the index of
+// the rune at which iteration continues.
+func formatIter(s string, f func(i, j int) int) {
+	i := 0     // index after current rune
+	var r rune // current rune
+
+	next := func() {
+		r1, w := utf8.DecodeRuneInString(s[i:])
+		if w == 0 {
+			r1 = -1 // signal end-of-string
+		}
+		r = r1
+		i += w
+	}
+
+	flags := func() {
+		for r == ' ' || r == '#' || r == '+' || r == '-' || r == '0' {
+			next()
+		}
+	}
+
+	index := func() {
+		if r == '[' {
+			log.Fatalf("cannot handle indexed arguments: %s", s)
+		}
+	}
+
+	digits := func() {
+		index()
+		if r == '*' {
+			i = f(i-1, i)
+			next()
+			return
+		}
+		for '0' <= r && r <= '9' {
+			next()
+		}
+	}
+
+	for next(); r >= 0; next() {
+		if r == '%' {
+			i0 := i
+			next()
+			flags()
+			digits()
+			if r == '.' {
+				next()
+				digits()
+			}
+			index()
+			// accept any letter (a-z, A-Z) as format verb;
+			// ignore anything else
+			if 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' {
+				i = f(i0-1, i)
+			}
+		}
+	}
+}
+
+// isFormat reports whether s contains format specifiers.
+func isFormat(s string) (yes bool) {
+	formatIter(s, func(i, j int) int {
+		yes = true
+		return len(s) // stop iteration
+	})
+	return
+}
+
+// oneFormat reports whether s is exactly one format specifier.
+func oneFormat(s string) (yes bool) {
+	formatIter(s, func(i, j int) int {
+		yes = i == 0 && j == len(s)
+		return j
+	})
+	return
+}
+
+// numFormatArgs returns the number of format specifiers in s.
+func numFormatArgs(s string) int {
+	count := 0
+	formatIter(s, func(i, j int) int {
+		count++
+		return j
+	})
+	return count
+}
+
+// formatReplace replaces the i'th format specifier s in the incoming
+// string in with the result of f(i, s) and returns the new string.
+func formatReplace(in string, f func(i int, s string) string) string {
+	var buf []byte
+	i0 := 0
+	index := 0
+	formatIter(in, func(i, j int) int {
+		if sub := in[i:j]; sub != "*" { // ignore calls for "*" width/length specifiers
+			buf = append(buf, in[i0:i]...)
+			buf = append(buf, f(index, sub)...)
+			i0 = j
+		}
+		index++
+		return j
+	})
+	return string(append(buf, in[i0:]...))
+}
+
+// blacklistedPackages is the set of packages which can
+// be ignored.
+var blacklistedPackages = map[string]bool{}
+
+// blacklistedFunctions is the set of functions which may have
+// format-like arguments but which don't do any formatting and
+// thus may be ignored.
+var blacklistedFunctions = map[string]bool{}
+
+func init() {
+	// verify that knownFormats entries are correctly formatted
+	for key, val := range knownFormats {
+		// key must be "typename format", and format starts with a '%'
+		// (formats containing '*' alone are not collected in this table)
+		i := strings.Index(key, "%")
+		if i < 0 || !oneFormat(key[i:]) {
+			log.Fatalf("incorrect knownFormats key: %q", key)
+		}
+		// val must be "format" or ""
+		if val != "" && !oneFormat(val) {
+			log.Fatalf("incorrect knownFormats value: %q (key = %q)", val, key)
+		}
+	}
+}
+
+// knownFormats entries are of the form "typename format" -> "newformat".
+// An absent entry means that the format is not recognized as valid.
+// An empty new format means that the format should remain unchanged.
+// To print out a new table, run: go test -run Formats -v.
+var knownFormats = map[string]string{
+	"*bytes.Buffer %s":                                "",
+	"*cmd/compile/internal/gc.Field %p":               "",
+	"*cmd/compile/internal/gc.Field %v":               "",
+	"*cmd/compile/internal/gc.Mpflt %v":               "",
+	"*cmd/compile/internal/gc.Mpint %v":               "",
+	"*cmd/compile/internal/gc.Node %#v":               "",
+	"*cmd/compile/internal/gc.Node %+S":               "",
+	"*cmd/compile/internal/gc.Node %+v":               "",
+	"*cmd/compile/internal/gc.Node %0j":               "",
+	"*cmd/compile/internal/gc.Node %L":                "",
+	"*cmd/compile/internal/gc.Node %S":                "",
+	"*cmd/compile/internal/gc.Node %j":                "",
+	"*cmd/compile/internal/gc.Node %p":                "",
+	"*cmd/compile/internal/gc.Node %v":                "",
+	"*cmd/compile/internal/gc.Sym %+v":                "",
+	"*cmd/compile/internal/gc.Sym %-v":                "",
+	"*cmd/compile/internal/gc.Sym %0S":                "",
+	"*cmd/compile/internal/gc.Sym %S":                 "",
+	"*cmd/compile/internal/gc.Sym %p":                 "",
+	"*cmd/compile/internal/gc.Sym %v":                 "",
+	"*cmd/compile/internal/gc.Type %#v":               "",
+	"*cmd/compile/internal/gc.Type %+v":               "",
+	"*cmd/compile/internal/gc.Type %-S":               "",
+	"*cmd/compile/internal/gc.Type %0S":               "",
+	"*cmd/compile/internal/gc.Type %L":                "",
+	"*cmd/compile/internal/gc.Type %S":                "",
+	"*cmd/compile/internal/gc.Type %p":                "",
+	"*cmd/compile/internal/gc.Type %v":                "",
+	"*cmd/compile/internal/ssa.Block %s":              "",
+	"*cmd/compile/internal/ssa.Block %v":              "",
+	"*cmd/compile/internal/ssa.Func %s":               "",
+	"*cmd/compile/internal/ssa.SparseTreeNode %v":     "",
+	"*cmd/compile/internal/ssa.Value %s":              "",
+	"*cmd/compile/internal/ssa.Value %v":              "",
+	"*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "",
+	"*cmd/internal/obj.Addr %v":                       "",
+	"*cmd/internal/obj.Prog %p":                       "",
+	"*cmd/internal/obj.Prog %s":                       "",
+	"*cmd/internal/obj.Prog %v":                       "",
+	"*math/big.Int %#x":                               "",
+	"[16]byte %x":                                     "",
+	"[]*cmd/compile/internal/gc.Node %v":              "",
+	"[]*cmd/compile/internal/gc.Sig %#v":              "",
+	"[]*cmd/compile/internal/ssa.Value %v":            "",
+	"[]byte %s":                                       "",
+	"[]byte %x":                                       "",
+	"[]cmd/compile/internal/ssa.Edge %v":              "",
+	"[]cmd/compile/internal/ssa.ID %v":                "",
+	"[]string %v":                                     "",
+	"bool %v":                                         "",
+	"byte %02x":                                       "",
+	"byte %08b":                                       "",
+	"byte %c":                                         "",
+	"cmd/compile/internal/arm.shift %d":               "",
+	"cmd/compile/internal/gc.Class %d":                "",
+	"cmd/compile/internal/gc.Ctype %d":                "",
+	"cmd/compile/internal/gc.Ctype %v":                "",
+	"cmd/compile/internal/gc.EType %d":                "",
+	"cmd/compile/internal/gc.EType %s":                "",
+	"cmd/compile/internal/gc.EType %v":                "",
+	"cmd/compile/internal/gc.Level %d":                "",
+	"cmd/compile/internal/gc.Level %v":                "",
+	"cmd/compile/internal/gc.Node %#v":                "",
+	"cmd/compile/internal/gc.Nodes %#v":               "",
+	"cmd/compile/internal/gc.Nodes %+v":               "",
+	"cmd/compile/internal/gc.Nodes %.v":               "",
+	"cmd/compile/internal/gc.Nodes %v":                "",
+	"cmd/compile/internal/gc.Op %#v":                  "",
+	"cmd/compile/internal/gc.Op %v":                   "",
+	"cmd/compile/internal/gc.Val %#v":                 "",
+	"cmd/compile/internal/gc.Val %T":                  "",
+	"cmd/compile/internal/gc.Val %v":                  "",
+	"cmd/compile/internal/gc.initKind %d":             "",
+	"cmd/compile/internal/ssa.BranchPrediction %d":    "",
+	"cmd/compile/internal/ssa.Edge %v":                "",
+	"cmd/compile/internal/ssa.GCNode %v":              "",
+	"cmd/compile/internal/ssa.ID %d":                  "",
+	"cmd/compile/internal/ssa.LocalSlot %v":           "",
+	"cmd/compile/internal/ssa.Location %v":            "",
+	"cmd/compile/internal/ssa.Op %s":                  "",
+	"cmd/compile/internal/ssa.Op %v":                  "",
+	"cmd/compile/internal/ssa.SizeAndAlign %s":        "",
+	"cmd/compile/internal/ssa.Type %s":                "",
+	"cmd/compile/internal/ssa.Type %v":                "",
+	"cmd/compile/internal/ssa.ValAndOff %s":           "",
+	"cmd/compile/internal/ssa.markKind %d":            "",
+	"cmd/compile/internal/ssa.rbrank %d":              "",
+	"cmd/compile/internal/ssa.regMask %d":             "",
+	"cmd/compile/internal/ssa.register %d":            "",
+	"cmd/compile/internal/syntax.Expr %#v":            "",
+	"cmd/compile/internal/syntax.Expr %s":             "",
+	"cmd/compile/internal/syntax.Node %T":             "",
+	"cmd/compile/internal/syntax.Operator %d":         "",
+	"cmd/compile/internal/syntax.Operator %s":         "",
+	"cmd/compile/internal/syntax.token %d":            "",
+	"cmd/compile/internal/syntax.token %q":            "",
+	"cmd/compile/internal/syntax.token %s":            "",
+	"cmd/internal/obj.As %v":                          "",
+	"error %v":                                        "",
+	"float64 %.2f":                                    "",
+	"float64 %.3f":                                    "",
+	"float64 %.6g":                                    "",
+	"float64 %g":                                      "",
+	"fmt.Stringer %T":                                 "",
+	"int %-12d":                                       "",
+	"int %-6d":                                        "",
+	"int %-8o":                                        "",
+	"int %5d":                                         "",
+	"int %6d":                                         "",
+	"int %c":                                          "",
+	"int %d":                                          "",
+	"int %v":                                          "",
+	"int %x":                                          "",
+	"int16 %d":                                        "",
+	"int16 %x":                                        "",
+	"int32 %d":                                        "",
+	"int32 %v":                                        "",
+	"int32 %x":                                        "",
+	"int64 %+d":                                       "",
+	"int64 %-10d":                                     "",
+	"int64 %X":                                        "",
+	"int64 %d":                                        "",
+	"int64 %v":                                        "",
+	"int64 %x":                                        "",
+	"int8 %d":                                         "",
+	"int8 %x":                                         "",
+	"interface{} %#v":                                 "",
+	"interface{} %T":                                  "",
+	"interface{} %q":                                  "",
+	"interface{} %s":                                  "",
+	"interface{} %v":                                  "",
+	"map[*cmd/compile/internal/gc.Node]*cmd/compile/internal/ssa.Value %v": "",
+	"reflect.Type %s":  "",
+	"rune %#U":         "",
+	"rune %c":          "",
+	"string %-16s":     "",
+	"string %.*s":      "",
+	"string %q":        "",
+	"string %s":        "",
+	"string %v":        "",
+	"time.Duration %d": "",
+	"time.Duration %v": "",
+	"uint %04x":        "",
+	"uint %d":          "",
+	"uint16 %d":        "",
+	"uint16 %v":        "",
+	"uint16 %x":        "",
+	"uint32 %08x":      "",
+	"uint32 %d":        "",
+	"uint32 %x":        "",
+	"uint64 %016x":     "",
+	"uint64 %08x":      "",
+	"uint64 %d":        "",
+	"uint64 %x":        "",
+	"uint8 %d":         "",
+	"uint8 %x":         "",
+	"uintptr %d":       "",
+}
diff --git a/src/cmd/compile/internal/amd64/cgen.go b/src/cmd/compile/internal/amd64/cgen.go
deleted file mode 100644
index 4b00003..0000000
--- a/src/cmd/compile/internal/amd64/cgen.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package amd64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/x86"
-)
-
-func blockcopy(n, ns *gc.Node, osrc, odst, w int64) {
-	var noddi gc.Node
-	gc.Nodreg(&noddi, gc.Types[gc.Tptr], x86.REG_DI)
-	var nodsi gc.Node
-	gc.Nodreg(&nodsi, gc.Types[gc.Tptr], x86.REG_SI)
-
-	var nodl gc.Node
-	var nodr gc.Node
-	if n.Ullman >= ns.Ullman {
-		gc.Agenr(n, &nodr, &nodsi)
-		if ns.Op == gc.ONAME {
-			gc.Gvardef(ns)
-		}
-		gc.Agenr(ns, &nodl, &noddi)
-	} else {
-		if ns.Op == gc.ONAME {
-			gc.Gvardef(ns)
-		}
-		gc.Agenr(ns, &nodl, &noddi)
-		gc.Agenr(n, &nodr, &nodsi)
-	}
-
-	if nodl.Reg != x86.REG_DI {
-		gmove(&nodl, &noddi)
-	}
-	if nodr.Reg != x86.REG_SI {
-		gmove(&nodr, &nodsi)
-	}
-	gc.Regfree(&nodl)
-	gc.Regfree(&nodr)
-
-	c := w % 8 // bytes
-	q := w / 8 // quads
-
-	var oldcx gc.Node
-	var cx gc.Node
-	savex(x86.REG_CX, &cx, &oldcx, nil, gc.Types[gc.TINT64])
-
-	// if we are copying forward on the stack and
-	// the src and dst overlap, then reverse direction
-	if osrc < odst && odst < osrc+w {
-		// reverse direction
-		gins(x86.ASTD, nil, nil) // set direction flag
-		if c > 0 {
-			gconreg(addptr, w-1, x86.REG_SI)
-			gconreg(addptr, w-1, x86.REG_DI)
-
-			gconreg(movptr, c, x86.REG_CX)
-			gins(x86.AREP, nil, nil)   // repeat
-			gins(x86.AMOVSB, nil, nil) // MOVB *(SI)-,*(DI)-
-		}
-
-		if q > 0 {
-			if c > 0 {
-				gconreg(addptr, -7, x86.REG_SI)
-				gconreg(addptr, -7, x86.REG_DI)
-			} else {
-				gconreg(addptr, w-8, x86.REG_SI)
-				gconreg(addptr, w-8, x86.REG_DI)
-			}
-
-			gconreg(movptr, q, x86.REG_CX)
-			gins(x86.AREP, nil, nil)   // repeat
-			gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)-,*(DI)-
-		}
-
-		// we leave with the flag clear
-		gins(x86.ACLD, nil, nil)
-	} else {
-		// normal direction
-		if q > 128 || (gc.Nacl && q >= 4) || (obj.Getgoos() == "plan9" && q >= 4) {
-			gconreg(movptr, q, x86.REG_CX)
-			gins(x86.AREP, nil, nil)   // repeat
-			gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+
-		} else if q >= 4 {
-			var oldx0 gc.Node
-			var x0 gc.Node
-			savex(x86.REG_X0, &x0, &oldx0, nil, gc.Types[gc.TFLOAT64])
-
-			p := gins(obj.ADUFFCOPY, nil, nil)
-			p.To.Type = obj.TYPE_ADDR
-			p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg))
-
-			// 64 blocks taking 14 bytes each
-			// see ../../../../runtime/mkduff.go
-			p.To.Offset = 14 * (64 - q/2)
-			restx(&x0, &oldx0)
-
-			if q%2 != 0 {
-				gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+
-			}
-		} else if !gc.Nacl && c == 0 {
-			// We don't need the MOVSQ side-effect of updating SI and DI,
-			// and issuing a sequence of MOVQs directly is faster.
-			nodsi.Op = gc.OINDREG
-
-			noddi.Op = gc.OINDREG
-			for q > 0 {
-				gmove(&nodsi, &cx) // MOVQ x+(SI),CX
-				gmove(&cx, &noddi) // MOVQ CX,x+(DI)
-				nodsi.Xoffset += 8
-				noddi.Xoffset += 8
-				q--
-			}
-		} else {
-			for q > 0 {
-				gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+
-				q--
-			}
-		}
-
-		// copy the remaining c bytes
-		if w < 4 || c <= 1 || (odst < osrc && osrc < odst+w) {
-			for c > 0 {
-				gins(x86.AMOVSB, nil, nil) // MOVB *(SI)+,*(DI)+
-				c--
-			}
-		} else if w < 8 || c <= 4 {
-			nodsi.Op = gc.OINDREG
-			noddi.Op = gc.OINDREG
-			cx.Type = gc.Types[gc.TINT32]
-			nodsi.Type = gc.Types[gc.TINT32]
-			noddi.Type = gc.Types[gc.TINT32]
-			if c > 4 {
-				nodsi.Xoffset = 0
-				noddi.Xoffset = 0
-				gmove(&nodsi, &cx)
-				gmove(&cx, &noddi)
-			}
-
-			nodsi.Xoffset = c - 4
-			noddi.Xoffset = c - 4
-			gmove(&nodsi, &cx)
-			gmove(&cx, &noddi)
-		} else {
-			nodsi.Op = gc.OINDREG
-			noddi.Op = gc.OINDREG
-			cx.Type = gc.Types[gc.TINT64]
-			nodsi.Type = gc.Types[gc.TINT64]
-			noddi.Type = gc.Types[gc.TINT64]
-			nodsi.Xoffset = c - 8
-			noddi.Xoffset = c - 8
-			gmove(&nodsi, &cx)
-			gmove(&cx, &noddi)
-		}
-	}
-
-	restx(&cx, &oldcx)
-}
diff --git a/src/cmd/compile/internal/amd64/galign.go b/src/cmd/compile/internal/amd64/galign.go
index 4291534..bb3830b 100644
--- a/src/cmd/compile/internal/amd64/galign.go
+++ b/src/cmd/compile/internal/amd64/galign.go
@@ -10,86 +10,21 @@ import (
 	"cmd/internal/obj/x86"
 )
 
-var (
-	addptr = x86.AADDQ
-	movptr = x86.AMOVQ
-	leaptr = x86.ALEAQ
-	cmpptr = x86.ACMPQ
-)
-
-func betypeinit() {
-	if obj.Getgoarch() == "amd64p32" {
-		addptr = x86.AADDL
-		movptr = x86.AMOVL
-		leaptr = x86.ALEAL
-		cmpptr = x86.ACMPL
-	}
-
-	if gc.Ctxt.Flag_dynlink || obj.Getgoos() == "nacl" {
-		resvd = append(resvd, x86.REG_R15)
-	}
-	if gc.Ctxt.Framepointer_enabled || obj.Getgoos() == "nacl" {
-		resvd = append(resvd, x86.REG_BP)
-	}
-	gc.Thearch.ReservedRegs = resvd
-}
+var leaptr = x86.ALEAQ
 
-func Main() {
+func Init() {
 	gc.Thearch.LinkArch = &x86.Linkamd64
-	if obj.Getgoarch() == "amd64p32" {
+	if obj.GOARCH == "amd64p32" {
 		gc.Thearch.LinkArch = &x86.Linkamd64p32
+		leaptr = x86.ALEAL
 	}
 	gc.Thearch.REGSP = x86.REGSP
-	gc.Thearch.REGCTXT = x86.REGCTXT
-	gc.Thearch.REGCALLX = x86.REG_BX
-	gc.Thearch.REGCALLX2 = x86.REG_AX
-	gc.Thearch.REGRETURN = x86.REG_AX
-	gc.Thearch.REGMIN = x86.REG_AX
-	gc.Thearch.REGMAX = x86.REG_R15
-	gc.Thearch.FREGMIN = x86.REG_X0
-	gc.Thearch.FREGMAX = x86.REG_X15
 	gc.Thearch.MAXWIDTH = 1 << 50
 
-	gc.Thearch.AddIndex = addindex
-	gc.Thearch.Betypeinit = betypeinit
-	gc.Thearch.Cgen_bmul = cgen_bmul
-	gc.Thearch.Cgen_hmul = cgen_hmul
-	gc.Thearch.Cgen_shift = cgen_shift
-	gc.Thearch.Clearfat = clearfat
 	gc.Thearch.Defframe = defframe
-	gc.Thearch.Dodiv = dodiv
-	gc.Thearch.Excise = excise
-	gc.Thearch.Expandchecks = expandchecks
-	gc.Thearch.Getg = getg
-	gc.Thearch.Gins = gins
-	gc.Thearch.Ginsboolval = ginsboolval
-	gc.Thearch.Ginscmp = ginscmp
-	gc.Thearch.Ginscon = ginscon
-	gc.Thearch.Ginsnop = ginsnop
-	gc.Thearch.Gmove = gmove
-	gc.Thearch.Peep = peep
 	gc.Thearch.Proginfo = proginfo
-	gc.Thearch.Regtyp = regtyp
-	gc.Thearch.Sameaddr = sameaddr
-	gc.Thearch.Smallindir = smallindir
-	gc.Thearch.Stackaddr = stackaddr
-	gc.Thearch.Blockcopy = blockcopy
-	gc.Thearch.Sudoaddable = sudoaddable
-	gc.Thearch.Sudoclean = sudoclean
-	gc.Thearch.Excludedregs = excludedregs
-	gc.Thearch.RtoB = RtoB
-	gc.Thearch.FtoB = FtoB
-	gc.Thearch.BtoR = BtoR
-	gc.Thearch.BtoF = BtoF
-	gc.Thearch.Optoas = optoas
-	gc.Thearch.Doregbits = doregbits
-	gc.Thearch.Regnames = regnames
 
-	gc.Thearch.SSARegToReg = ssaRegToReg
 	gc.Thearch.SSAMarkMoves = ssaMarkMoves
 	gc.Thearch.SSAGenValue = ssaGenValue
 	gc.Thearch.SSAGenBlock = ssaGenBlock
-
-	gc.Main()
-	gc.Exit(0)
 }
diff --git a/src/cmd/compile/internal/amd64/ggen.go b/src/cmd/compile/internal/amd64/ggen.go
index 909f7b0..c137b52 100644
--- a/src/cmd/compile/internal/amd64/ggen.go
+++ b/src/cmd/compile/internal/amd64/ggen.go
@@ -11,7 +11,7 @@ import (
 )
 
 // no floating point in note handlers on Plan 9
-var isPlan9 = obj.Getgoos() == "plan9"
+var isPlan9 = obj.GOOS == "plan9"
 
 func defframe(ptxt *obj.Prog) {
 	// fill in argument size, stack size
@@ -40,7 +40,7 @@ func defframe(ptxt *obj.Prog) {
 			gc.Fatalf("needzero class %d", n.Class)
 		}
 		if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
-			gc.Fatalf("var %v has size %d offset %d", gc.Nconv(n, gc.FmtLong), int(n.Type.Width), int(n.Xoffset))
+			gc.Fatalf("var %L has size %d offset %d", n, int(n.Type.Width), int(n.Xoffset))
 		}
 
 		if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) {
@@ -112,696 +112,67 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32, x0 *uin
 			gc.Fatalf("zerorange count not a multiple of widthptr %d", cnt)
 		}
 		if *ax == 0 {
-			p = appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
+			p = gc.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
 			*ax = 1
 		}
-		p = appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo)
+		p = gc.Appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo)
 		lo += int64(gc.Widthptr)
 		cnt -= int64(gc.Widthptr)
 	}
 
 	if cnt == 8 {
 		if *ax == 0 {
-			p = appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
+			p = gc.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
 			*ax = 1
 		}
-		p = appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo)
+		p = gc.Appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo)
 	} else if !isPlan9 && cnt <= int64(8*gc.Widthreg) {
 		if *x0 == 0 {
-			p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
+			p = gc.Appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
 			*x0 = 1
 		}
 
 		for i := int64(0); i < cnt/16; i++ {
-			p = appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+i*16)
+			p = gc.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+i*16)
 		}
 
 		if cnt%16 != 0 {
-			p = appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+cnt-int64(16))
+			p = gc.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+cnt-int64(16))
 		}
 	} else if !gc.Nacl && !isPlan9 && (cnt <= int64(128*gc.Widthreg)) {
 		if *x0 == 0 {
-			p = appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
+			p = gc.Appendpp(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_REG, x86.REG_X0, 0)
 			*x0 = 1
 		}
-		p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0)
-		p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt))
+		p = gc.Appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo+dzDI(cnt), obj.TYPE_REG, x86.REG_DI, 0)
+		p = gc.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(cnt))
 		p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
 
 		if cnt%16 != 0 {
-			p = appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_DI, -int64(8))
+			p = gc.Appendpp(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X0, 0, obj.TYPE_MEM, x86.REG_DI, -int64(8))
 		}
 	} else {
 		if *ax == 0 {
-			p = appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
+			p = gc.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
 			*ax = 1
 		}
 
-		p = appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0)
-		p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0)
-		p = appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
-		p = appendpp(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
+		p = gc.Appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0)
+		p = gc.Appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0)
+		p = gc.Appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
+		p = gc.Appendpp(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
 	}
 
 	return p
 }
 
-func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int64, ttype obj.AddrType, treg int, toffset int64) *obj.Prog {
-	q := gc.Ctxt.NewProg()
-	gc.Clearp(q)
-	q.As = as
-	q.Lineno = p.Lineno
-	q.From.Type = ftype
-	q.From.Reg = int16(freg)
-	q.From.Offset = foffset
-	q.To.Type = ttype
-	q.To.Reg = int16(treg)
-	q.To.Offset = toffset
-	q.Link = p.Link
-	p.Link = q
-	return q
-}
-
-var panicdiv *gc.Node
-
-/*
- * generate division.
- * generates one of:
- *	res = nl / nr
- *	res = nl % nr
- * according to op.
- */
-func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	// Have to be careful about handling
-	// most negative int divided by -1 correctly.
-	// The hardware will trap.
-	// Also the byte divide instruction needs AH,
-	// which we otherwise don't have to deal with.
-	// Easiest way to avoid for int8, int16: use int32.
-	// For int32 and int64, use explicit test.
-	// Could use int64 hw for int32.
-	t := nl.Type
-
-	t0 := t
-	check := false
-	if t.IsSigned() {
-		check = true
-		if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<<uint64(t.Width*8-1)) {
-			check = false
-		} else if gc.Isconst(nr, gc.CTINT) && nr.Int64() != -1 {
-			check = false
-		}
-	}
-
-	if t.Width < 4 {
-		if t.IsSigned() {
-			t = gc.Types[gc.TINT32]
-		} else {
-			t = gc.Types[gc.TUINT32]
-		}
-		check = false
-	}
-
-	a := optoas(op, t)
-
-	var n3 gc.Node
-	gc.Regalloc(&n3, t0, nil)
-	var ax gc.Node
-	var oldax gc.Node
-	if nl.Ullman >= nr.Ullman {
-		savex(x86.REG_AX, &ax, &oldax, res, t0)
-		gc.Cgen(nl, &ax)
-		gc.Regalloc(&ax, t0, &ax) // mark ax live during cgen
-		gc.Cgen(nr, &n3)
-		gc.Regfree(&ax)
-	} else {
-		gc.Cgen(nr, &n3)
-		savex(x86.REG_AX, &ax, &oldax, res, t0)
-		gc.Cgen(nl, &ax)
-	}
-
-	if t != t0 {
-		// Convert
-		ax1 := ax
-
-		n31 := n3
-		ax.Type = t
-		n3.Type = t
-		gmove(&ax1, &ax)
-		gmove(&n31, &n3)
-	}
-
-	var n4 gc.Node
-	if gc.Nacl {
-		// Native Client does not relay the divide-by-zero trap
-		// to the executing program, so we must insert a check
-		// for ourselves.
-		gc.Nodconst(&n4, t, 0)
-
-		gins(optoas(gc.OCMP, t), &n3, &n4)
-		p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1)
-		if panicdiv == nil {
-			panicdiv = gc.Sysfunc("panicdivide")
-		}
-		gc.Ginscall(panicdiv, -1)
-		gc.Patch(p1, gc.Pc)
-	}
-
-	var p2 *obj.Prog
-	if check {
-		gc.Nodconst(&n4, t, -1)
-		gins(optoas(gc.OCMP, t), &n3, &n4)
-		p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1)
-		if op == gc.ODIV {
-			// a / (-1) is -a.
-			gins(optoas(gc.OMINUS, t), nil, &ax)
-
-			gmove(&ax, res)
-		} else {
-			// a % (-1) is 0.
-			gc.Nodconst(&n4, t, 0)
-
-			gmove(&n4, res)
-		}
-
-		p2 = gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-	}
-
-	var olddx gc.Node
-	var dx gc.Node
-	savex(x86.REG_DX, &dx, &olddx, res, t)
-	if !t.IsSigned() {
-		gc.Nodconst(&n4, t, 0)
-		gmove(&n4, &dx)
-	} else {
-		gins(optoas(gc.OEXTEND, t), nil, nil)
-	}
-	gins(a, &n3, nil)
-	gc.Regfree(&n3)
-	if op == gc.ODIV {
-		gmove(&ax, res)
-	} else {
-		gmove(&dx, res)
-	}
-	restx(&dx, &olddx)
-	if check {
-		gc.Patch(p2, gc.Pc)
-	}
-	restx(&ax, &oldax)
-}
-
-/*
- * register dr is one of the special ones (AX, CX, DI, SI, etc.).
- * we need to use it.  if it is already allocated as a temporary
- * (r > 1; can only happen if a routine like sgen passed a
- * special as cgen's res and then cgen used regalloc to reuse
- * it as its own temporary), then move it for now to another
- * register.  caller must call restx to move it back.
- * the move is not necessary if dr == res, because res is
- * known to be dead.
- */
-func savex(dr int, x *gc.Node, oldx *gc.Node, res *gc.Node, t *gc.Type) {
-	r := uint8(gc.GetReg(dr))
-
-	// save current ax and dx if they are live
-	// and not the destination
-	*oldx = gc.Node{}
-
-	gc.Nodreg(x, t, dr)
-	if r > 1 && !gc.Samereg(x, res) {
-		gc.Regalloc(oldx, gc.Types[gc.TINT64], nil)
-		x.Type = gc.Types[gc.TINT64]
-		gmove(x, oldx)
-		x.Type = t
-		// TODO(marvin): Fix Node.EType type union.
-		oldx.Etype = gc.EType(r) // squirrel away old r value
-		gc.SetReg(dr, 1)
-	}
-}
-
-func restx(x *gc.Node, oldx *gc.Node) {
-	if oldx.Op != 0 {
-		x.Type = gc.Types[gc.TINT64]
-		gc.SetReg(int(x.Reg), int(oldx.Etype))
-		gmove(oldx, x)
-		gc.Regfree(oldx)
-	}
-}
-
-/*
- * generate high multiply:
- *   res = (nl*nr) >> width
- */
-func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	t := nl.Type
-	a := optoas(gc.OHMUL, t)
-	if nl.Ullman < nr.Ullman {
-		nl, nr = nr, nl
-	}
-
-	var n1 gc.Node
-	gc.Cgenr(nl, &n1, res)
-	var n2 gc.Node
-	gc.Cgenr(nr, &n2, nil)
-	var ax, oldax, dx, olddx gc.Node
-	savex(x86.REG_AX, &ax, &oldax, res, gc.Types[gc.TUINT64])
-	savex(x86.REG_DX, &dx, &olddx, res, gc.Types[gc.TUINT64])
-	gmove(&n1, &ax)
-	gins(a, &n2, nil)
-	gc.Regfree(&n2)
-	gc.Regfree(&n1)
-
-	if t.Width == 1 {
-		// byte multiply behaves differently.
-		var byteAH, byteDX gc.Node
-		gc.Nodreg(&byteAH, t, x86.REG_AH)
-		gc.Nodreg(&byteDX, t, x86.REG_DX)
-		gmove(&byteAH, &byteDX)
-	}
-	gmove(&dx, res)
-
-	restx(&ax, &oldax)
-	restx(&dx, &olddx)
-}
-
-/*
- * generate shift according to op, one of:
- *	res = nl << nr
- *	res = nl >> nr
- */
-func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	a := optoas(op, nl.Type)
-
-	if nr.Op == gc.OLITERAL {
-		var n1 gc.Node
-		gc.Regalloc(&n1, nl.Type, res)
-		gc.Cgen(nl, &n1)
-		sc := uint64(nr.Int64())
-		if sc >= uint64(nl.Type.Width*8) {
-			// large shift gets 2 shifts by width-1
-			var n3 gc.Node
-			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
-
-			gins(a, &n3, &n1)
-			gins(a, &n3, &n1)
-		} else {
-			gins(a, nr, &n1)
-		}
-		gmove(&n1, res)
-		gc.Regfree(&n1)
-		return
-	}
-
-	if nl.Ullman >= gc.UINF {
-		var n4 gc.Node
-		gc.Tempname(&n4, nl.Type)
-		gc.Cgen(nl, &n4)
-		nl = &n4
-	}
-
-	if nr.Ullman >= gc.UINF {
-		var n5 gc.Node
-		gc.Tempname(&n5, nr.Type)
-		gc.Cgen(nr, &n5)
-		nr = &n5
-	}
-
-	rcx := gc.GetReg(x86.REG_CX)
-	var n1 gc.Node
-	gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX)
-
-	// Allow either uint32 or uint64 as shift type,
-	// to avoid unnecessary conversion from uint32 to uint64
-	// just to do the comparison.
-	tcount := gc.Types[gc.Simtype[nr.Type.Etype]]
-
-	if tcount.Etype < gc.TUINT32 {
-		tcount = gc.Types[gc.TUINT32]
-	}
-
-	gc.Regalloc(&n1, nr.Type, &n1) // to hold the shift type in CX
-	var n3 gc.Node
-	gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX
-
-	var cx gc.Node
-	gc.Nodreg(&cx, gc.Types[gc.TUINT64], x86.REG_CX)
-
-	var oldcx gc.Node
-	if rcx > 0 && !gc.Samereg(&cx, res) {
-		gc.Regalloc(&oldcx, gc.Types[gc.TUINT64], nil)
-		gmove(&cx, &oldcx)
-	}
-
-	cx.Type = tcount
-
-	var n2 gc.Node
-	if gc.Samereg(&cx, res) {
-		gc.Regalloc(&n2, nl.Type, nil)
-	} else {
-		gc.Regalloc(&n2, nl.Type, res)
-	}
-	if nl.Ullman >= nr.Ullman {
-		gc.Cgen(nl, &n2)
-		gc.Cgen(nr, &n1)
-		gmove(&n1, &n3)
-	} else {
-		gc.Cgen(nr, &n1)
-		gmove(&n1, &n3)
-		gc.Cgen(nl, &n2)
-	}
-
-	gc.Regfree(&n3)
-
-	// test and fix up large shifts
-	if !bounded {
-		gc.Nodconst(&n3, tcount, nl.Type.Width*8)
-		gins(optoas(gc.OCMP, tcount), &n1, &n3)
-		p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, +1)
-		if op == gc.ORSH && nl.Type.IsSigned() {
-			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
-			gins(a, &n3, &n2)
-		} else {
-			gc.Nodconst(&n3, nl.Type, 0)
-			gmove(&n3, &n2)
-		}
-
-		gc.Patch(p1, gc.Pc)
-	}
-
-	gins(a, &n1, &n2)
-
-	if oldcx.Op != 0 {
-		cx.Type = gc.Types[gc.TUINT64]
-		gmove(&oldcx, &cx)
-		gc.Regfree(&oldcx)
-	}
-
-	gmove(&n2, res)
-
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-/*
- * generate byte multiply:
- *	res = nl * nr
- * there is no 2-operand byte multiply instruction so
- * we do a full-width multiplication and truncate afterwards.
- */
-func cgen_bmul(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) bool {
-	if optoas(op, nl.Type) != x86.AIMULB {
-		return false
-	}
-
-	// largest ullman on left.
-	if nl.Ullman < nr.Ullman {
-		nl, nr = nr, nl
-	}
-
-	// generate operands in "8-bit" registers.
-	var n1b gc.Node
-	gc.Regalloc(&n1b, nl.Type, res)
-
-	gc.Cgen(nl, &n1b)
-	var n2b gc.Node
-	gc.Regalloc(&n2b, nr.Type, nil)
-	gc.Cgen(nr, &n2b)
-
-	// perform full-width multiplication.
-	t := gc.Types[gc.TUINT64]
-
-	if nl.Type.IsSigned() {
-		t = gc.Types[gc.TINT64]
-	}
-	var n1 gc.Node
-	gc.Nodreg(&n1, t, int(n1b.Reg))
-	var n2 gc.Node
-	gc.Nodreg(&n2, t, int(n2b.Reg))
-	a := optoas(op, t)
-	gins(a, &n2, &n1)
-
-	// truncate.
-	gmove(&n1, res)
-
-	gc.Regfree(&n1b)
-	gc.Regfree(&n2b)
-	return true
-}
-
-func clearfat(nl *gc.Node) {
-	/* clear a fat object */
-	if gc.Debug['g'] != 0 {
-		gc.Dump("\nclearfat", nl)
-	}
-
-	// Avoid taking the address for simple enough types.
-	if gc.Componentgen(nil, nl) {
-		return
-	}
-
-	w := nl.Type.Width
-
-	if w > 1024 || (w >= 64 && (gc.Nacl || isPlan9)) {
-		var oldn1 gc.Node
-		var n1 gc.Node
-		savex(x86.REG_DI, &n1, &oldn1, nil, gc.Types[gc.Tptr])
-		gc.Agen(nl, &n1)
-
-		var ax gc.Node
-		var oldax gc.Node
-		savex(x86.REG_AX, &ax, &oldax, nil, gc.Types[gc.Tptr])
-		gconreg(x86.AMOVL, 0, x86.REG_AX)
-		gconreg(movptr, w/8, x86.REG_CX)
-
-		gins(x86.AREP, nil, nil)   // repeat
-		gins(x86.ASTOSQ, nil, nil) // STOQ AL,*(DI)+
-
-		if w%8 != 0 {
-			n1.Op = gc.OINDREG
-			clearfat_tail(&n1, w%8)
-		}
-
-		restx(&n1, &oldn1)
-		restx(&ax, &oldax)
-		return
-	}
-
-	if w >= 64 {
-		var oldn1 gc.Node
-		var n1 gc.Node
-		savex(x86.REG_DI, &n1, &oldn1, nil, gc.Types[gc.Tptr])
-		gc.Agen(nl, &n1)
-
-		var vec_zero gc.Node
-		var old_x0 gc.Node
-		savex(x86.REG_X0, &vec_zero, &old_x0, nil, gc.Types[gc.TFLOAT64])
-		gins(x86.AXORPS, &vec_zero, &vec_zero)
-
-		if di := dzDI(w); di != 0 {
-			gconreg(addptr, di, x86.REG_DI)
-		}
-		p := gins(obj.ADUFFZERO, nil, nil)
-		p.To.Type = obj.TYPE_ADDR
-		p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
-		p.To.Offset = dzOff(w)
-
-		if w%16 != 0 {
-			n1.Op = gc.OINDREG
-			n1.Xoffset -= 16 - w%16
-			gins(x86.AMOVUPS, &vec_zero, &n1)
-		}
-
-		restx(&vec_zero, &old_x0)
-		restx(&n1, &oldn1)
-		return
-	}
-
-	// NOTE: Must use agen, not igen, so that optimizer sees address
-	// being taken. We are not writing on field boundaries.
-	var n1 gc.Node
-	gc.Agenr(nl, &n1, nil)
-	n1.Op = gc.OINDREG
-
-	clearfat_tail(&n1, w)
-
-	gc.Regfree(&n1)
-}
-
-func clearfat_tail(n1 *gc.Node, b int64) {
-	if b >= 16 && isPlan9 {
-		var z gc.Node
-		gc.Nodconst(&z, gc.Types[gc.TUINT64], 0)
-		q := b / 8
-		for ; q > 0; q-- {
-			n1.Type = z.Type
-			gins(x86.AMOVQ, &z, n1)
-			n1.Xoffset += 8
-			b -= 8
-		}
-		if b != 0 {
-			n1.Xoffset -= 8 - b
-			gins(x86.AMOVQ, &z, n1)
-		}
-		return
-	}
-	if b >= 16 {
-		var vec_zero gc.Node
-		gc.Regalloc(&vec_zero, gc.Types[gc.TFLOAT64], nil)
-		gins(x86.AXORPS, &vec_zero, &vec_zero)
-
-		for b >= 16 {
-			gins(x86.AMOVUPS, &vec_zero, n1)
-			n1.Xoffset += 16
-			b -= 16
-		}
-
-		// MOVUPS X0, off(base) is a few bytes shorter than MOV 0, off(base)
-		if b != 0 {
-			n1.Xoffset -= 16 - b
-			gins(x86.AMOVUPS, &vec_zero, n1)
-		}
-
-		gc.Regfree(&vec_zero)
-		return
-	}
-
-	// Write sequence of MOV 0, off(base) instead of using STOSQ.
-	// The hope is that although the code will be slightly longer,
-	// the MOVs will have no dependencies and pipeline better
-	// than the unrolled STOSQ loop.
-	var z gc.Node
-	gc.Nodconst(&z, gc.Types[gc.TUINT64], 0)
-	if b >= 8 {
-		n1.Type = z.Type
-		gins(x86.AMOVQ, &z, n1)
-		n1.Xoffset += 8
-		b -= 8
-
-		if b != 0 {
-			n1.Xoffset -= 8 - b
-			gins(x86.AMOVQ, &z, n1)
-		}
-		return
-	}
-
-	if b >= 4 {
-		gc.Nodconst(&z, gc.Types[gc.TUINT32], 0)
-		n1.Type = z.Type
-		gins(x86.AMOVL, &z, n1)
-		n1.Xoffset += 4
-		b -= 4
-
-		if b != 0 {
-			n1.Xoffset -= 4 - b
-			gins(x86.AMOVL, &z, n1)
-		}
-		return
-	}
-
-	if b >= 2 {
-		gc.Nodconst(&z, gc.Types[gc.TUINT16], 0)
-		n1.Type = z.Type
-		gins(x86.AMOVW, &z, n1)
-		n1.Xoffset += 2
-		b -= 2
-	}
-
-	gc.Nodconst(&z, gc.Types[gc.TUINT8], 0)
-	for b > 0 {
-		n1.Type = z.Type
-		gins(x86.AMOVB, &z, n1)
-		n1.Xoffset++
-		b--
-	}
-
-}
-
-// Called after regopt and peep have run.
-// Expand CHECKNIL pseudo-op into actual nil pointer check.
-func expandchecks(firstp *obj.Prog) {
-	var p1 *obj.Prog
-	var p2 *obj.Prog
-
-	for p := firstp; p != nil; p = p.Link {
-		if p.As != obj.ACHECKNIL {
-			continue
-		}
-		if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers
-			gc.Warnl(p.Lineno, "generated nil check")
-		}
-
-		// check is
-		//	CMP arg, $0
-		//	JNE 2(PC) (likely)
-		//	MOV AX, 0
-		p1 = gc.Ctxt.NewProg()
-
-		p2 = gc.Ctxt.NewProg()
-		gc.Clearp(p1)
-		gc.Clearp(p2)
-		p1.Link = p2
-		p2.Link = p.Link
-		p.Link = p1
-		p1.Lineno = p.Lineno
-		p2.Lineno = p.Lineno
-		p1.Pc = 9999
-		p2.Pc = 9999
-		p.As = cmpptr
-		p.To.Type = obj.TYPE_CONST
-		p.To.Offset = 0
-		p1.As = x86.AJNE
-		p1.From.Type = obj.TYPE_CONST
-		p1.From.Offset = 1 // likely
-		p1.To.Type = obj.TYPE_BRANCH
-		p1.To.Val = p2.Link
-
-		// crash by write to memory address 0.
-		// if possible, since we know arg is 0, use 0(arg),
-		// which will be shorter to encode than plain 0.
-		p2.As = x86.AMOVL
-
-		p2.From.Type = obj.TYPE_REG
-		p2.From.Reg = x86.REG_AX
-		if regtyp(&p.From) {
-			p2.To.Type = obj.TYPE_MEM
-			p2.To.Reg = p.From.Reg
-		} else {
-			p2.To.Type = obj.TYPE_MEM
-			p2.To.Reg = x86.REG_NONE
-		}
-
-		p2.To.Offset = 0
-	}
-}
-
-// addr += index*width if possible.
-func addindex(index *gc.Node, width int64, addr *gc.Node) bool {
-	switch width {
-	case 1, 2, 4, 8:
-		p1 := gins(x86.ALEAQ, index, addr)
-		p1.From.Type = obj.TYPE_MEM
-		p1.From.Scale = int16(width)
-		p1.From.Index = p1.From.Reg
-		p1.From.Reg = p1.To.Reg
-		return true
-	}
-	return false
-}
-
-// res = runtime.getg()
-func getg(res *gc.Node) {
-	var n1 gc.Node
-	gc.Regalloc(&n1, res.Type, res)
-	mov := optoas(gc.OAS, gc.Types[gc.Tptr])
-	p := gins(mov, nil, &n1)
+func ginsnop() {
+	// This is actually not the x86 NOP anymore,
+	// but at the point where it gets used, AX is dead
+	// so it's okay if we lose the high bits.
+	p := gc.Prog(x86.AXCHGL)
 	p.From.Type = obj.TYPE_REG
-	p.From.Reg = x86.REG_TLS
-	p = gins(mov, nil, &n1)
-	p.From = p.To
-	p.From.Type = obj.TYPE_MEM
-	p.From.Index = x86.REG_TLS
-	p.From.Scale = 1
-	gmove(&n1, res)
-	gc.Regfree(&n1)
+	p.From.Reg = x86.REG_AX
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = x86.REG_AX
 }
diff --git a/src/cmd/compile/internal/amd64/gsubr.go b/src/cmd/compile/internal/amd64/gsubr.go
deleted file mode 100644
index 5d9070c..0000000
--- a/src/cmd/compile/internal/amd64/gsubr.go
+++ /dev/null
@@ -1,1423 +0,0 @@
-// Derived from Inferno utils/6c/txt.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package amd64
-
-import (
-	"cmd/compile/internal/big"
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/x86"
-	"fmt"
-)
-
-var resvd = []int{
-	x86.REG_DI, // for movstring
-	x86.REG_SI, // for movstring
-
-	x86.REG_AX, // for divide
-	x86.REG_CX, // for shift
-	x86.REG_DX, // for divide
-	x86.REG_SP, // for stack
-}
-
-/*
- * generate
- *	as $c, reg
- */
-func gconreg(as obj.As, c int64, reg int) {
-	var nr gc.Node
-
-	switch as {
-	case x86.AADDL,
-		x86.AMOVL,
-		x86.ALEAL:
-		gc.Nodreg(&nr, gc.Types[gc.TINT32], reg)
-
-	default:
-		gc.Nodreg(&nr, gc.Types[gc.TINT64], reg)
-	}
-
-	ginscon(as, c, &nr)
-}
-
-/*
- * generate
- *	as $c, n
- */
-func ginscon(as obj.As, c int64, n2 *gc.Node) {
-	var n1 gc.Node
-
-	switch as {
-	case x86.AADDL,
-		x86.AMOVL,
-		x86.ALEAL:
-		gc.Nodconst(&n1, gc.Types[gc.TINT32], c)
-
-	default:
-		gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
-	}
-
-	if as != x86.AMOVQ && (c < -(1<<31) || c >= 1<<31) {
-		// cannot have 64-bit immediate in ADD, etc.
-		// instead, MOV into register first.
-		var ntmp gc.Node
-		gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil)
-
-		gins(x86.AMOVQ, &n1, &ntmp)
-		gins(as, &ntmp, n2)
-		gc.Regfree(&ntmp)
-		return
-	}
-
-	gins(as, &n1, n2)
-}
-
-func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
-	if t.IsInteger() && n1.Op == gc.OLITERAL && gc.Smallintconst(n1) && n2.Op != gc.OLITERAL {
-		// Reverse comparison to place constant last.
-		op = gc.Brrev(op)
-		n1, n2 = n2, n1
-	}
-	// General case.
-	var r1, r2, g1, g2 gc.Node
-
-	// A special case to make write barriers more efficient.
-	// Comparing the first field of a named struct can be done directly.
-	base := n1
-	if n1.Op == gc.ODOT && n1.Left.Type.IsStruct() && n1.Left.Type.Field(0).Sym == n1.Sym {
-		base = n1.Left
-	}
-
-	if base.Op == gc.ONAME && base.Class != gc.PAUTOHEAP || n1.Op == gc.OINDREG {
-		r1 = *n1
-	} else {
-		gc.Regalloc(&r1, t, n1)
-		gc.Regalloc(&g1, n1.Type, &r1)
-		gc.Cgen(n1, &g1)
-		gmove(&g1, &r1)
-	}
-	if n2.Op == gc.OLITERAL && t.IsInteger() && gc.Smallintconst(n2) {
-		r2 = *n2
-	} else {
-		gc.Regalloc(&r2, t, n2)
-		gc.Regalloc(&g2, n1.Type, &r2)
-		gc.Cgen(n2, &g2)
-		gmove(&g2, &r2)
-	}
-	gins(optoas(gc.OCMP, t), &r1, &r2)
-	if r1.Op == gc.OREGISTER {
-		gc.Regfree(&g1)
-		gc.Regfree(&r1)
-	}
-	if r2.Op == gc.OREGISTER {
-		gc.Regfree(&g2)
-		gc.Regfree(&r2)
-	}
-	return gc.Gbranch(optoas(op, t), nil, likely)
-}
-
-func ginsboolval(a obj.As, n *gc.Node) {
-	gins(jmptoset(a), nil, n)
-}
-
-// set up nodes representing 2^63
-var (
-	bigi         gc.Node
-	bigf         gc.Node
-	bignodes_did bool
-)
-
-func bignodes() {
-	if bignodes_did {
-		return
-	}
-	bignodes_did = true
-
-	var i big.Int
-	i.SetInt64(1)
-	i.Lsh(&i, 63)
-
-	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
-	bigi.SetBigInt(&i)
-
-	bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64])
-}
-
-/*
- * generate move:
- *	t = f
- * hard part is conversions.
- */
-func gmove(f *gc.Node, t *gc.Node) {
-	if gc.Debug['M'] != 0 {
-		fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, gc.FmtLong), gc.Nconv(t, gc.FmtLong))
-	}
-
-	ft := gc.Simsimtype(f.Type)
-	tt := gc.Simsimtype(t.Type)
-	cvt := t.Type
-
-	if gc.Iscomplex[ft] || gc.Iscomplex[tt] {
-		gc.Complexmove(f, t)
-		return
-	}
-
-	// cannot have two memory operands
-	var a obj.As
-	if gc.Ismem(f) && gc.Ismem(t) {
-		goto hard
-	}
-
-	// convert constant to desired type
-	if f.Op == gc.OLITERAL {
-		var con gc.Node
-		f.Convconst(&con, t.Type)
-		f = &con
-		ft = tt // so big switch will choose a simple mov
-
-		// some constants can't move directly to memory.
-		if gc.Ismem(t) {
-			// float constants come from memory.
-			if gc.Isfloat[tt] {
-				goto hard
-			}
-
-			// 64-bit immediates are really 32-bit sign-extended
-			// unless moving into a register.
-			if gc.Isint[tt] {
-				if i := con.Int64(); int64(int32(i)) != i {
-					goto hard
-				}
-			}
-		}
-	}
-
-	// value -> value copy, only one memory operand.
-	// figure out the instruction to use.
-	// break out of switch for one-instruction gins.
-	// goto rdst for "destination must be register".
-	// goto hard for "convert to cvt type first".
-	// otherwise handle and return.
-
-	switch uint32(ft)<<16 | uint32(tt) {
-	default:
-		gc.Dump("f", f)
-		gc.Dump("t", t)
-		gc.Fatalf("gmove %v -> %v", gc.Tconv(f.Type, gc.FmtLong), gc.Tconv(t.Type, gc.FmtLong))
-
-		/*
-		 * integer copy and truncate
-		 */
-	case gc.TINT8<<16 | gc.TINT8, // same size
-		gc.TINT8<<16 | gc.TUINT8,
-		gc.TUINT8<<16 | gc.TINT8,
-		gc.TUINT8<<16 | gc.TUINT8,
-		gc.TINT16<<16 | gc.TINT8,
-		// truncate
-		gc.TUINT16<<16 | gc.TINT8,
-		gc.TINT32<<16 | gc.TINT8,
-		gc.TUINT32<<16 | gc.TINT8,
-		gc.TINT64<<16 | gc.TINT8,
-		gc.TUINT64<<16 | gc.TINT8,
-		gc.TINT16<<16 | gc.TUINT8,
-		gc.TUINT16<<16 | gc.TUINT8,
-		gc.TINT32<<16 | gc.TUINT8,
-		gc.TUINT32<<16 | gc.TUINT8,
-		gc.TINT64<<16 | gc.TUINT8,
-		gc.TUINT64<<16 | gc.TUINT8:
-		a = x86.AMOVB
-
-	case gc.TINT16<<16 | gc.TINT16, // same size
-		gc.TINT16<<16 | gc.TUINT16,
-		gc.TUINT16<<16 | gc.TINT16,
-		gc.TUINT16<<16 | gc.TUINT16,
-		gc.TINT32<<16 | gc.TINT16,
-		// truncate
-		gc.TUINT32<<16 | gc.TINT16,
-		gc.TINT64<<16 | gc.TINT16,
-		gc.TUINT64<<16 | gc.TINT16,
-		gc.TINT32<<16 | gc.TUINT16,
-		gc.TUINT32<<16 | gc.TUINT16,
-		gc.TINT64<<16 | gc.TUINT16,
-		gc.TUINT64<<16 | gc.TUINT16:
-		a = x86.AMOVW
-
-	case gc.TINT32<<16 | gc.TINT32, // same size
-		gc.TINT32<<16 | gc.TUINT32,
-		gc.TUINT32<<16 | gc.TINT32,
-		gc.TUINT32<<16 | gc.TUINT32:
-		a = x86.AMOVL
-
-	case gc.TINT64<<16 | gc.TINT32, // truncate
-		gc.TUINT64<<16 | gc.TINT32,
-		gc.TINT64<<16 | gc.TUINT32,
-		gc.TUINT64<<16 | gc.TUINT32:
-		a = x86.AMOVQL
-
-	case gc.TINT64<<16 | gc.TINT64, // same size
-		gc.TINT64<<16 | gc.TUINT64,
-		gc.TUINT64<<16 | gc.TINT64,
-		gc.TUINT64<<16 | gc.TUINT64:
-		a = x86.AMOVQ
-
-		/*
-		 * integer up-conversions
-		 */
-	case gc.TINT8<<16 | gc.TINT16, // sign extend int8
-		gc.TINT8<<16 | gc.TUINT16:
-		a = x86.AMOVBWSX
-
-		goto rdst
-
-	case gc.TINT8<<16 | gc.TINT32,
-		gc.TINT8<<16 | gc.TUINT32:
-		a = x86.AMOVBLSX
-		goto rdst
-
-	case gc.TINT8<<16 | gc.TINT64,
-		gc.TINT8<<16 | gc.TUINT64:
-		a = x86.AMOVBQSX
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8
-		gc.TUINT8<<16 | gc.TUINT16:
-		a = x86.AMOVBWZX
-
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TINT32,
-		gc.TUINT8<<16 | gc.TUINT32:
-		a = x86.AMOVBLZX
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TINT64,
-		gc.TUINT8<<16 | gc.TUINT64:
-		a = x86.AMOVBQZX
-		goto rdst
-
-	case gc.TINT16<<16 | gc.TINT32, // sign extend int16
-		gc.TINT16<<16 | gc.TUINT32:
-		a = x86.AMOVWLSX
-
-		goto rdst
-
-	case gc.TINT16<<16 | gc.TINT64,
-		gc.TINT16<<16 | gc.TUINT64:
-		a = x86.AMOVWQSX
-		goto rdst
-
-	case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16
-		gc.TUINT16<<16 | gc.TUINT32:
-		a = x86.AMOVWLZX
-
-		goto rdst
-
-	case gc.TUINT16<<16 | gc.TINT64,
-		gc.TUINT16<<16 | gc.TUINT64:
-		a = x86.AMOVWQZX
-		goto rdst
-
-	case gc.TINT32<<16 | gc.TINT64, // sign extend int32
-		gc.TINT32<<16 | gc.TUINT64:
-		a = x86.AMOVLQSX
-
-		goto rdst
-
-		// AMOVL into a register zeros the top of the register,
-	// so this is not always necessary, but if we rely on AMOVL
-	// the optimizer is almost certain to screw with us.
-	case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32
-		gc.TUINT32<<16 | gc.TUINT64:
-		a = x86.AMOVLQZX
-
-		goto rdst
-
-		/*
-		* float to integer
-		 */
-	case gc.TFLOAT32<<16 | gc.TINT32:
-		a = x86.ACVTTSS2SL
-
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TINT32:
-		a = x86.ACVTTSD2SL
-		goto rdst
-
-	case gc.TFLOAT32<<16 | gc.TINT64:
-		a = x86.ACVTTSS2SQ
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TINT64:
-		a = x86.ACVTTSD2SQ
-		goto rdst
-
-		// convert via int32.
-	case gc.TFLOAT32<<16 | gc.TINT16,
-		gc.TFLOAT32<<16 | gc.TINT8,
-		gc.TFLOAT32<<16 | gc.TUINT16,
-		gc.TFLOAT32<<16 | gc.TUINT8,
-		gc.TFLOAT64<<16 | gc.TINT16,
-		gc.TFLOAT64<<16 | gc.TINT8,
-		gc.TFLOAT64<<16 | gc.TUINT16,
-		gc.TFLOAT64<<16 | gc.TUINT8:
-		cvt = gc.Types[gc.TINT32]
-
-		goto hard
-
-		// convert via int64.
-	case gc.TFLOAT32<<16 | gc.TUINT32,
-		gc.TFLOAT64<<16 | gc.TUINT32:
-		cvt = gc.Types[gc.TINT64]
-
-		goto hard
-
-		// algorithm is:
-	//	if small enough, use native float64 -> int64 conversion.
-	//	otherwise, subtract 2^63, convert, and add it back.
-	case gc.TFLOAT32<<16 | gc.TUINT64,
-		gc.TFLOAT64<<16 | gc.TUINT64:
-		a := x86.ACVTTSS2SQ
-
-		if ft == gc.TFLOAT64 {
-			a = x86.ACVTTSD2SQ
-		}
-		bignodes()
-		var r1 gc.Node
-		gc.Regalloc(&r1, gc.Types[ft], nil)
-		var r2 gc.Node
-		gc.Regalloc(&r2, gc.Types[tt], t)
-		var r3 gc.Node
-		gc.Regalloc(&r3, gc.Types[ft], nil)
-		var r4 gc.Node
-		gc.Regalloc(&r4, gc.Types[tt], nil)
-		gins(optoas(gc.OAS, f.Type), f, &r1)
-		gins(optoas(gc.OCMP, f.Type), &bigf, &r1)
-		p1 := gc.Gbranch(optoas(gc.OLE, f.Type), nil, +1)
-		gins(a, &r1, &r2)
-		p2 := gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-		gins(optoas(gc.OAS, f.Type), &bigf, &r3)
-		gins(optoas(gc.OSUB, f.Type), &r3, &r1)
-		gins(a, &r1, &r2)
-		gins(x86.AMOVQ, &bigi, &r4)
-		gins(x86.AXORQ, &r4, &r2)
-		gc.Patch(p2, gc.Pc)
-		gmove(&r2, t)
-		gc.Regfree(&r4)
-		gc.Regfree(&r3)
-		gc.Regfree(&r2)
-		gc.Regfree(&r1)
-		return
-
-		/*
-		 * integer to float
-		 */
-	case gc.TINT32<<16 | gc.TFLOAT32:
-		a = x86.ACVTSL2SS
-
-		goto rdst
-
-	case gc.TINT32<<16 | gc.TFLOAT64:
-		a = x86.ACVTSL2SD
-		goto rdst
-
-	case gc.TINT64<<16 | gc.TFLOAT32:
-		a = x86.ACVTSQ2SS
-		goto rdst
-
-	case gc.TINT64<<16 | gc.TFLOAT64:
-		a = x86.ACVTSQ2SD
-		goto rdst
-
-		// convert via int32
-	case gc.TINT16<<16 | gc.TFLOAT32,
-		gc.TINT16<<16 | gc.TFLOAT64,
-		gc.TINT8<<16 | gc.TFLOAT32,
-		gc.TINT8<<16 | gc.TFLOAT64,
-		gc.TUINT16<<16 | gc.TFLOAT32,
-		gc.TUINT16<<16 | gc.TFLOAT64,
-		gc.TUINT8<<16 | gc.TFLOAT32,
-		gc.TUINT8<<16 | gc.TFLOAT64:
-		cvt = gc.Types[gc.TINT32]
-
-		goto hard
-
-		// convert via int64.
-	case gc.TUINT32<<16 | gc.TFLOAT32,
-		gc.TUINT32<<16 | gc.TFLOAT64:
-		cvt = gc.Types[gc.TINT64]
-
-		goto hard
-
-		// algorithm is:
-	//	if small enough, use native int64 -> uint64 conversion.
-	//	otherwise, halve (rounding to odd?), convert, and double.
-	case gc.TUINT64<<16 | gc.TFLOAT32,
-		gc.TUINT64<<16 | gc.TFLOAT64:
-		a := x86.ACVTSQ2SS
-
-		if tt == gc.TFLOAT64 {
-			a = x86.ACVTSQ2SD
-		}
-		var zero gc.Node
-		gc.Nodconst(&zero, gc.Types[gc.TUINT64], 0)
-		var one gc.Node
-		gc.Nodconst(&one, gc.Types[gc.TUINT64], 1)
-		var r1 gc.Node
-		gc.Regalloc(&r1, f.Type, f)
-		var r2 gc.Node
-		gc.Regalloc(&r2, t.Type, t)
-		var r3 gc.Node
-		gc.Regalloc(&r3, f.Type, nil)
-		var r4 gc.Node
-		gc.Regalloc(&r4, f.Type, nil)
-		gmove(f, &r1)
-		gins(x86.ACMPQ, &r1, &zero)
-		p1 := gc.Gbranch(x86.AJLT, nil, +1)
-		gins(a, &r1, &r2)
-		p2 := gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-		gmove(&r1, &r3)
-		gins(x86.ASHRQ, &one, &r3)
-		gmove(&r1, &r4)
-		gins(x86.AANDL, &one, &r4)
-		gins(x86.AORQ, &r4, &r3)
-		gins(a, &r3, &r2)
-		gins(optoas(gc.OADD, t.Type), &r2, &r2)
-		gc.Patch(p2, gc.Pc)
-		gmove(&r2, t)
-		gc.Regfree(&r4)
-		gc.Regfree(&r3)
-		gc.Regfree(&r2)
-		gc.Regfree(&r1)
-		return
-
-		/*
-		 * float to float
-		 */
-	case gc.TFLOAT32<<16 | gc.TFLOAT32:
-		a = x86.AMOVSS
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT64:
-		a = x86.AMOVSD
-
-	case gc.TFLOAT32<<16 | gc.TFLOAT64:
-		a = x86.ACVTSS2SD
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT32:
-		a = x86.ACVTSD2SS
-		goto rdst
-	}
-
-	gins(a, f, t)
-	return
-
-	// requires register destination
-rdst:
-	{
-		var r1 gc.Node
-		gc.Regalloc(&r1, t.Type, t)
-
-		gins(a, f, &r1)
-		gmove(&r1, t)
-		gc.Regfree(&r1)
-		return
-	}
-
-	// requires register intermediate
-hard:
-	var r1 gc.Node
-	gc.Regalloc(&r1, cvt, t)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-}
-
-func samaddr(f *gc.Node, t *gc.Node) bool {
-	if f.Op != t.Op {
-		return false
-	}
-
-	switch f.Op {
-	case gc.OREGISTER:
-		if f.Reg != t.Reg {
-			break
-		}
-		return true
-	}
-
-	return false
-}
-
-/*
- * generate one instruction:
- *	as f, t
- */
-func gins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
-	//	Node nod;
-
-	//	if(f != N && f->op == OINDEX) {
-	//		gc.Regalloc(&nod, &regnode, Z);
-	//		v = constnode.vconst;
-	//		gc.Cgen(f->right, &nod);
-	//		constnode.vconst = v;
-	//		idx.reg = nod.reg;
-	//		gc.Regfree(&nod);
-	//	}
-	//	if(t != N && t->op == OINDEX) {
-	//		gc.Regalloc(&nod, &regnode, Z);
-	//		v = constnode.vconst;
-	//		gc.Cgen(t->right, &nod);
-	//		constnode.vconst = v;
-	//		idx.reg = nod.reg;
-	//		gc.Regfree(&nod);
-	//	}
-
-	if f != nil && f.Op == gc.OADDR && (as == x86.AMOVL || as == x86.AMOVQ) {
-		// Turn MOVL $xxx into LEAL xxx.
-		// These should be equivalent but most of the backend
-		// only expects to see LEAL, because that's what we had
-		// historically generated. Various hidden assumptions are baked in by now.
-		if as == x86.AMOVL {
-			as = x86.ALEAL
-		} else {
-			as = x86.ALEAQ
-		}
-		f = f.Left
-	}
-
-	switch as {
-	case x86.AMOVB,
-		x86.AMOVW,
-		x86.AMOVL,
-		x86.AMOVQ,
-		x86.AMOVSS,
-		x86.AMOVSD:
-		if f != nil && t != nil && samaddr(f, t) {
-			return nil
-		}
-
-	case x86.ALEAQ:
-		if f != nil && gc.Isconst(f, gc.CTNIL) {
-			gc.Fatalf("gins LEAQ nil %v", f.Type)
-		}
-	}
-
-	p := gc.Prog(as)
-	gc.Naddr(&p.From, f)
-	gc.Naddr(&p.To, t)
-
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("%v\n", p)
-	}
-
-	w := int32(0)
-	switch as {
-	case x86.AMOVB:
-		w = 1
-
-	case x86.AMOVW:
-		w = 2
-
-	case x86.AMOVL:
-		w = 4
-
-	case x86.AMOVQ:
-		w = 8
-	}
-
-	if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Width > int64(w))) {
-		gc.Dump("f", f)
-		gc.Dump("t", t)
-		gc.Fatalf("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width)
-	}
-
-	if p.To.Type == obj.TYPE_ADDR && w > 0 {
-		gc.Fatalf("bad use of addr: %v", p)
-	}
-
-	return p
-}
-
-func ginsnop() {
-	// This is actually not the x86 NOP anymore,
-	// but at the point where it gets used, AX is dead
-	// so it's okay if we lose the high bits.
-	var reg gc.Node
-	gc.Nodreg(&reg, gc.Types[gc.TINT], x86.REG_AX)
-	gins(x86.AXCHGL, &reg, &reg)
-}
-
-/*
- * return Axxx for Oxxx on type t.
- */
-func optoas(op gc.Op, t *gc.Type) obj.As {
-	if t == nil {
-		gc.Fatalf("optoas: t is nil")
-	}
-
-	// avoid constant conversions in switches below
-	const (
-		OMINUS_  = uint32(gc.OMINUS) << 16
-		OLSH_    = uint32(gc.OLSH) << 16
-		ORSH_    = uint32(gc.ORSH) << 16
-		OADD_    = uint32(gc.OADD) << 16
-		OSUB_    = uint32(gc.OSUB) << 16
-		OMUL_    = uint32(gc.OMUL) << 16
-		ODIV_    = uint32(gc.ODIV) << 16
-		OMOD_    = uint32(gc.OMOD) << 16
-		OOR_     = uint32(gc.OOR) << 16
-		OAND_    = uint32(gc.OAND) << 16
-		OXOR_    = uint32(gc.OXOR) << 16
-		OEQ_     = uint32(gc.OEQ) << 16
-		ONE_     = uint32(gc.ONE) << 16
-		OLT_     = uint32(gc.OLT) << 16
-		OLE_     = uint32(gc.OLE) << 16
-		OGE_     = uint32(gc.OGE) << 16
-		OGT_     = uint32(gc.OGT) << 16
-		OCMP_    = uint32(gc.OCMP) << 16
-		OPS_     = uint32(gc.OPS) << 16
-		OPC_     = uint32(gc.OPC) << 16
-		OAS_     = uint32(gc.OAS) << 16
-		OHMUL_   = uint32(gc.OHMUL) << 16
-		OSQRT_   = uint32(gc.OSQRT) << 16
-		OADDR_   = uint32(gc.OADDR) << 16
-		OINC_    = uint32(gc.OINC) << 16
-		ODEC_    = uint32(gc.ODEC) << 16
-		OLROT_   = uint32(gc.OLROT) << 16
-		ORROTC_  = uint32(gc.ORROTC) << 16
-		OEXTEND_ = uint32(gc.OEXTEND) << 16
-	)
-
-	a := obj.AXXX
-	switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
-	default:
-		gc.Fatalf("optoas: no entry %v-%v", op, t)
-
-	case OADDR_ | gc.TPTR32:
-		a = x86.ALEAL
-
-	case OADDR_ | gc.TPTR64:
-		a = x86.ALEAQ
-
-	case OEQ_ | gc.TBOOL,
-		OEQ_ | gc.TINT8,
-		OEQ_ | gc.TUINT8,
-		OEQ_ | gc.TINT16,
-		OEQ_ | gc.TUINT16,
-		OEQ_ | gc.TINT32,
-		OEQ_ | gc.TUINT32,
-		OEQ_ | gc.TINT64,
-		OEQ_ | gc.TUINT64,
-		OEQ_ | gc.TPTR32,
-		OEQ_ | gc.TPTR64,
-		OEQ_ | gc.TFLOAT32,
-		OEQ_ | gc.TFLOAT64:
-		a = x86.AJEQ
-
-	case ONE_ | gc.TBOOL,
-		ONE_ | gc.TINT8,
-		ONE_ | gc.TUINT8,
-		ONE_ | gc.TINT16,
-		ONE_ | gc.TUINT16,
-		ONE_ | gc.TINT32,
-		ONE_ | gc.TUINT32,
-		ONE_ | gc.TINT64,
-		ONE_ | gc.TUINT64,
-		ONE_ | gc.TPTR32,
-		ONE_ | gc.TPTR64,
-		ONE_ | gc.TFLOAT32,
-		ONE_ | gc.TFLOAT64:
-		a = x86.AJNE
-
-	case OPS_ | gc.TBOOL,
-		OPS_ | gc.TINT8,
-		OPS_ | gc.TUINT8,
-		OPS_ | gc.TINT16,
-		OPS_ | gc.TUINT16,
-		OPS_ | gc.TINT32,
-		OPS_ | gc.TUINT32,
-		OPS_ | gc.TINT64,
-		OPS_ | gc.TUINT64,
-		OPS_ | gc.TPTR32,
-		OPS_ | gc.TPTR64,
-		OPS_ | gc.TFLOAT32,
-		OPS_ | gc.TFLOAT64:
-		a = x86.AJPS
-
-	case OPC_ | gc.TBOOL,
-		OPC_ | gc.TINT8,
-		OPC_ | gc.TUINT8,
-		OPC_ | gc.TINT16,
-		OPC_ | gc.TUINT16,
-		OPC_ | gc.TINT32,
-		OPC_ | gc.TUINT32,
-		OPC_ | gc.TINT64,
-		OPC_ | gc.TUINT64,
-		OPC_ | gc.TPTR32,
-		OPC_ | gc.TPTR64,
-		OPC_ | gc.TFLOAT32,
-		OPC_ | gc.TFLOAT64:
-		a = x86.AJPC
-
-	case OLT_ | gc.TINT8,
-		OLT_ | gc.TINT16,
-		OLT_ | gc.TINT32,
-		OLT_ | gc.TINT64:
-		a = x86.AJLT
-
-	case OLT_ | gc.TUINT8,
-		OLT_ | gc.TUINT16,
-		OLT_ | gc.TUINT32,
-		OLT_ | gc.TUINT64:
-		a = x86.AJCS
-
-	case OLE_ | gc.TINT8,
-		OLE_ | gc.TINT16,
-		OLE_ | gc.TINT32,
-		OLE_ | gc.TINT64:
-		a = x86.AJLE
-
-	case OLE_ | gc.TUINT8,
-		OLE_ | gc.TUINT16,
-		OLE_ | gc.TUINT32,
-		OLE_ | gc.TUINT64:
-		a = x86.AJLS
-
-	case OGT_ | gc.TINT8,
-		OGT_ | gc.TINT16,
-		OGT_ | gc.TINT32,
-		OGT_ | gc.TINT64:
-		a = x86.AJGT
-
-	case OGT_ | gc.TUINT8,
-		OGT_ | gc.TUINT16,
-		OGT_ | gc.TUINT32,
-		OGT_ | gc.TUINT64,
-		OLT_ | gc.TFLOAT32,
-		OLT_ | gc.TFLOAT64:
-		a = x86.AJHI
-
-	case OGE_ | gc.TINT8,
-		OGE_ | gc.TINT16,
-		OGE_ | gc.TINT32,
-		OGE_ | gc.TINT64:
-		a = x86.AJGE
-
-	case OGE_ | gc.TUINT8,
-		OGE_ | gc.TUINT16,
-		OGE_ | gc.TUINT32,
-		OGE_ | gc.TUINT64,
-		OLE_ | gc.TFLOAT32,
-		OLE_ | gc.TFLOAT64:
-		a = x86.AJCC
-
-	case OCMP_ | gc.TBOOL,
-		OCMP_ | gc.TINT8,
-		OCMP_ | gc.TUINT8:
-		a = x86.ACMPB
-
-	case OCMP_ | gc.TINT16,
-		OCMP_ | gc.TUINT16:
-		a = x86.ACMPW
-
-	case OCMP_ | gc.TINT32,
-		OCMP_ | gc.TUINT32,
-		OCMP_ | gc.TPTR32:
-		a = x86.ACMPL
-
-	case OCMP_ | gc.TINT64,
-		OCMP_ | gc.TUINT64,
-		OCMP_ | gc.TPTR64:
-		a = x86.ACMPQ
-
-	case OCMP_ | gc.TFLOAT32:
-		a = x86.AUCOMISS
-
-	case OCMP_ | gc.TFLOAT64:
-		a = x86.AUCOMISD
-
-	case OAS_ | gc.TBOOL,
-		OAS_ | gc.TINT8,
-		OAS_ | gc.TUINT8:
-		a = x86.AMOVB
-
-	case OAS_ | gc.TINT16,
-		OAS_ | gc.TUINT16:
-		a = x86.AMOVW
-
-	case OAS_ | gc.TINT32,
-		OAS_ | gc.TUINT32,
-		OAS_ | gc.TPTR32:
-		a = x86.AMOVL
-
-	case OAS_ | gc.TINT64,
-		OAS_ | gc.TUINT64,
-		OAS_ | gc.TPTR64:
-		a = x86.AMOVQ
-
-	case OAS_ | gc.TFLOAT32:
-		a = x86.AMOVSS
-
-	case OAS_ | gc.TFLOAT64:
-		a = x86.AMOVSD
-
-	case OADD_ | gc.TINT8,
-		OADD_ | gc.TUINT8:
-		a = x86.AADDB
-
-	case OADD_ | gc.TINT16,
-		OADD_ | gc.TUINT16:
-		a = x86.AADDW
-
-	case OADD_ | gc.TINT32,
-		OADD_ | gc.TUINT32,
-		OADD_ | gc.TPTR32:
-		a = x86.AADDL
-
-	case OADD_ | gc.TINT64,
-		OADD_ | gc.TUINT64,
-		OADD_ | gc.TPTR64:
-		a = x86.AADDQ
-
-	case OADD_ | gc.TFLOAT32:
-		a = x86.AADDSS
-
-	case OADD_ | gc.TFLOAT64:
-		a = x86.AADDSD
-
-	case OSUB_ | gc.TINT8,
-		OSUB_ | gc.TUINT8:
-		a = x86.ASUBB
-
-	case OSUB_ | gc.TINT16,
-		OSUB_ | gc.TUINT16:
-		a = x86.ASUBW
-
-	case OSUB_ | gc.TINT32,
-		OSUB_ | gc.TUINT32,
-		OSUB_ | gc.TPTR32:
-		a = x86.ASUBL
-
-	case OSUB_ | gc.TINT64,
-		OSUB_ | gc.TUINT64,
-		OSUB_ | gc.TPTR64:
-		a = x86.ASUBQ
-
-	case OSUB_ | gc.TFLOAT32:
-		a = x86.ASUBSS
-
-	case OSUB_ | gc.TFLOAT64:
-		a = x86.ASUBSD
-
-	case OINC_ | gc.TINT8,
-		OINC_ | gc.TUINT8:
-		a = x86.AINCB
-
-	case OINC_ | gc.TINT16,
-		OINC_ | gc.TUINT16:
-		a = x86.AINCW
-
-	case OINC_ | gc.TINT32,
-		OINC_ | gc.TUINT32,
-		OINC_ | gc.TPTR32:
-		a = x86.AINCL
-
-	case OINC_ | gc.TINT64,
-		OINC_ | gc.TUINT64,
-		OINC_ | gc.TPTR64:
-		a = x86.AINCQ
-
-	case ODEC_ | gc.TINT8,
-		ODEC_ | gc.TUINT8:
-		a = x86.ADECB
-
-	case ODEC_ | gc.TINT16,
-		ODEC_ | gc.TUINT16:
-		a = x86.ADECW
-
-	case ODEC_ | gc.TINT32,
-		ODEC_ | gc.TUINT32,
-		ODEC_ | gc.TPTR32:
-		a = x86.ADECL
-
-	case ODEC_ | gc.TINT64,
-		ODEC_ | gc.TUINT64,
-		ODEC_ | gc.TPTR64:
-		a = x86.ADECQ
-
-	case OMINUS_ | gc.TINT8,
-		OMINUS_ | gc.TUINT8:
-		a = x86.ANEGB
-
-	case OMINUS_ | gc.TINT16,
-		OMINUS_ | gc.TUINT16:
-		a = x86.ANEGW
-
-	case OMINUS_ | gc.TINT32,
-		OMINUS_ | gc.TUINT32,
-		OMINUS_ | gc.TPTR32:
-		a = x86.ANEGL
-
-	case OMINUS_ | gc.TINT64,
-		OMINUS_ | gc.TUINT64,
-		OMINUS_ | gc.TPTR64:
-		a = x86.ANEGQ
-
-	case OAND_ | gc.TBOOL,
-		OAND_ | gc.TINT8,
-		OAND_ | gc.TUINT8:
-		a = x86.AANDB
-
-	case OAND_ | gc.TINT16,
-		OAND_ | gc.TUINT16:
-		a = x86.AANDW
-
-	case OAND_ | gc.TINT32,
-		OAND_ | gc.TUINT32,
-		OAND_ | gc.TPTR32:
-		a = x86.AANDL
-
-	case OAND_ | gc.TINT64,
-		OAND_ | gc.TUINT64,
-		OAND_ | gc.TPTR64:
-		a = x86.AANDQ
-
-	case OOR_ | gc.TBOOL,
-		OOR_ | gc.TINT8,
-		OOR_ | gc.TUINT8:
-		a = x86.AORB
-
-	case OOR_ | gc.TINT16,
-		OOR_ | gc.TUINT16:
-		a = x86.AORW
-
-	case OOR_ | gc.TINT32,
-		OOR_ | gc.TUINT32,
-		OOR_ | gc.TPTR32:
-		a = x86.AORL
-
-	case OOR_ | gc.TINT64,
-		OOR_ | gc.TUINT64,
-		OOR_ | gc.TPTR64:
-		a = x86.AORQ
-
-	case OXOR_ | gc.TINT8,
-		OXOR_ | gc.TUINT8:
-		a = x86.AXORB
-
-	case OXOR_ | gc.TINT16,
-		OXOR_ | gc.TUINT16:
-		a = x86.AXORW
-
-	case OXOR_ | gc.TINT32,
-		OXOR_ | gc.TUINT32,
-		OXOR_ | gc.TPTR32:
-		a = x86.AXORL
-
-	case OXOR_ | gc.TINT64,
-		OXOR_ | gc.TUINT64,
-		OXOR_ | gc.TPTR64:
-		a = x86.AXORQ
-
-	case OLROT_ | gc.TINT8,
-		OLROT_ | gc.TUINT8:
-		a = x86.AROLB
-
-	case OLROT_ | gc.TINT16,
-		OLROT_ | gc.TUINT16:
-		a = x86.AROLW
-
-	case OLROT_ | gc.TINT32,
-		OLROT_ | gc.TUINT32,
-		OLROT_ | gc.TPTR32:
-		a = x86.AROLL
-
-	case OLROT_ | gc.TINT64,
-		OLROT_ | gc.TUINT64,
-		OLROT_ | gc.TPTR64:
-		a = x86.AROLQ
-
-	case OLSH_ | gc.TINT8,
-		OLSH_ | gc.TUINT8:
-		a = x86.ASHLB
-
-	case OLSH_ | gc.TINT16,
-		OLSH_ | gc.TUINT16:
-		a = x86.ASHLW
-
-	case OLSH_ | gc.TINT32,
-		OLSH_ | gc.TUINT32,
-		OLSH_ | gc.TPTR32:
-		a = x86.ASHLL
-
-	case OLSH_ | gc.TINT64,
-		OLSH_ | gc.TUINT64,
-		OLSH_ | gc.TPTR64:
-		a = x86.ASHLQ
-
-	case ORSH_ | gc.TUINT8:
-		a = x86.ASHRB
-
-	case ORSH_ | gc.TUINT16:
-		a = x86.ASHRW
-
-	case ORSH_ | gc.TUINT32,
-		ORSH_ | gc.TPTR32:
-		a = x86.ASHRL
-
-	case ORSH_ | gc.TUINT64,
-		ORSH_ | gc.TPTR64:
-		a = x86.ASHRQ
-
-	case ORSH_ | gc.TINT8:
-		a = x86.ASARB
-
-	case ORSH_ | gc.TINT16:
-		a = x86.ASARW
-
-	case ORSH_ | gc.TINT32:
-		a = x86.ASARL
-
-	case ORSH_ | gc.TINT64:
-		a = x86.ASARQ
-
-	case ORROTC_ | gc.TINT8,
-		ORROTC_ | gc.TUINT8:
-		a = x86.ARCRB
-
-	case ORROTC_ | gc.TINT16,
-		ORROTC_ | gc.TUINT16:
-		a = x86.ARCRW
-
-	case ORROTC_ | gc.TINT32,
-		ORROTC_ | gc.TUINT32:
-		a = x86.ARCRL
-
-	case ORROTC_ | gc.TINT64,
-		ORROTC_ | gc.TUINT64:
-		a = x86.ARCRQ
-
-	case OHMUL_ | gc.TINT8,
-		OMUL_ | gc.TINT8,
-		OMUL_ | gc.TUINT8:
-		a = x86.AIMULB
-
-	case OHMUL_ | gc.TINT16,
-		OMUL_ | gc.TINT16,
-		OMUL_ | gc.TUINT16:
-		a = x86.AIMULW
-
-	case OHMUL_ | gc.TINT32,
-		OMUL_ | gc.TINT32,
-		OMUL_ | gc.TUINT32,
-		OMUL_ | gc.TPTR32:
-		a = x86.AIMULL
-
-	case OHMUL_ | gc.TINT64,
-		OMUL_ | gc.TINT64,
-		OMUL_ | gc.TUINT64,
-		OMUL_ | gc.TPTR64:
-		a = x86.AIMULQ
-
-	case OHMUL_ | gc.TUINT8:
-		a = x86.AMULB
-
-	case OHMUL_ | gc.TUINT16:
-		a = x86.AMULW
-
-	case OHMUL_ | gc.TUINT32,
-		OHMUL_ | gc.TPTR32:
-		a = x86.AMULL
-
-	case OHMUL_ | gc.TUINT64,
-		OHMUL_ | gc.TPTR64:
-		a = x86.AMULQ
-
-	case OMUL_ | gc.TFLOAT32:
-		a = x86.AMULSS
-
-	case OMUL_ | gc.TFLOAT64:
-		a = x86.AMULSD
-
-	case ODIV_ | gc.TINT8,
-		OMOD_ | gc.TINT8:
-		a = x86.AIDIVB
-
-	case ODIV_ | gc.TUINT8,
-		OMOD_ | gc.TUINT8:
-		a = x86.ADIVB
-
-	case ODIV_ | gc.TINT16,
-		OMOD_ | gc.TINT16:
-		a = x86.AIDIVW
-
-	case ODIV_ | gc.TUINT16,
-		OMOD_ | gc.TUINT16:
-		a = x86.ADIVW
-
-	case ODIV_ | gc.TINT32,
-		OMOD_ | gc.TINT32:
-		a = x86.AIDIVL
-
-	case ODIV_ | gc.TUINT32,
-		ODIV_ | gc.TPTR32,
-		OMOD_ | gc.TUINT32,
-		OMOD_ | gc.TPTR32:
-		a = x86.ADIVL
-
-	case ODIV_ | gc.TINT64,
-		OMOD_ | gc.TINT64:
-		a = x86.AIDIVQ
-
-	case ODIV_ | gc.TUINT64,
-		ODIV_ | gc.TPTR64,
-		OMOD_ | gc.TUINT64,
-		OMOD_ | gc.TPTR64:
-		a = x86.ADIVQ
-
-	case OEXTEND_ | gc.TINT16:
-		a = x86.ACWD
-
-	case OEXTEND_ | gc.TINT32:
-		a = x86.ACDQ
-
-	case OEXTEND_ | gc.TINT64:
-		a = x86.ACQO
-
-	case ODIV_ | gc.TFLOAT32:
-		a = x86.ADIVSS
-
-	case ODIV_ | gc.TFLOAT64:
-		a = x86.ADIVSD
-
-	case OSQRT_ | gc.TFLOAT64:
-		a = x86.ASQRTSD
-	}
-
-	return a
-}
-
-// jmptoset returns ASETxx for AJxx.
-func jmptoset(jmp obj.As) obj.As {
-	switch jmp {
-	case x86.AJEQ:
-		return x86.ASETEQ
-	case x86.AJNE:
-		return x86.ASETNE
-	case x86.AJLT:
-		return x86.ASETLT
-	case x86.AJCS:
-		return x86.ASETCS
-	case x86.AJLE:
-		return x86.ASETLE
-	case x86.AJLS:
-		return x86.ASETLS
-	case x86.AJGT:
-		return x86.ASETGT
-	case x86.AJHI:
-		return x86.ASETHI
-	case x86.AJGE:
-		return x86.ASETGE
-	case x86.AJCC:
-		return x86.ASETCC
-	case x86.AJMI:
-		return x86.ASETMI
-	case x86.AJOC:
-		return x86.ASETOC
-	case x86.AJOS:
-		return x86.ASETOS
-	case x86.AJPC:
-		return x86.ASETPC
-	case x86.AJPL:
-		return x86.ASETPL
-	case x86.AJPS:
-		return x86.ASETPS
-	}
-	gc.Fatalf("jmptoset: no entry for %v", jmp)
-	panic("unreachable")
-}
-
-const (
-	ODynam   = 1 << 0
-	OAddable = 1 << 1
-)
-
-var clean [20]gc.Node
-
-var cleani int = 0
-
-func sudoclean() {
-	if clean[cleani-1].Op != gc.OEMPTY {
-		gc.Regfree(&clean[cleani-1])
-	}
-	if clean[cleani-2].Op != gc.OEMPTY {
-		gc.Regfree(&clean[cleani-2])
-	}
-	cleani -= 2
-}
-
-/*
- * generate code to compute address of n,
- * a reference to a (perhaps nested) field inside
- * an array or struct.
- * return 0 on failure, 1 on success.
- * on success, leaves usable address in a.
- *
- * caller is responsible for calling sudoclean
- * after successful sudoaddable,
- * to release the register used for a.
- */
-func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool {
-	if n.Type == nil {
-		return false
-	}
-
-	*a = obj.Addr{}
-
-	switch n.Op {
-	case gc.OLITERAL:
-		if !gc.Isconst(n, gc.CTINT) {
-			break
-		}
-		v := n.Int64()
-		if v >= 32000 || v <= -32000 {
-			break
-		}
-		switch as {
-		default:
-			return false
-
-		case x86.AADDB,
-			x86.AADDW,
-			x86.AADDL,
-			x86.AADDQ,
-			x86.ASUBB,
-			x86.ASUBW,
-			x86.ASUBL,
-			x86.ASUBQ,
-			x86.AANDB,
-			x86.AANDW,
-			x86.AANDL,
-			x86.AANDQ,
-			x86.AORB,
-			x86.AORW,
-			x86.AORL,
-			x86.AORQ,
-			x86.AXORB,
-			x86.AXORW,
-			x86.AXORL,
-			x86.AXORQ,
-			x86.AINCB,
-			x86.AINCW,
-			x86.AINCL,
-			x86.AINCQ,
-			x86.ADECB,
-			x86.ADECW,
-			x86.ADECL,
-			x86.ADECQ,
-			x86.AMOVB,
-			x86.AMOVW,
-			x86.AMOVL,
-			x86.AMOVQ:
-			break
-		}
-
-		cleani += 2
-		reg := &clean[cleani-1]
-		reg1 := &clean[cleani-2]
-		reg.Op = gc.OEMPTY
-		reg1.Op = gc.OEMPTY
-		gc.Naddr(a, n)
-		return true
-
-	case gc.ODOT,
-		gc.ODOTPTR:
-		cleani += 2
-		reg := &clean[cleani-1]
-		reg1 := &clean[cleani-2]
-		reg.Op = gc.OEMPTY
-		reg1.Op = gc.OEMPTY
-		var nn *gc.Node
-		var oary [10]int64
-		o := gc.Dotoffset(n, oary[:], &nn)
-		if nn == nil {
-			sudoclean()
-			return false
-		}
-
-		if nn.Addable && o == 1 && oary[0] >= 0 {
-			// directly addressable set of DOTs
-			n1 := *nn
-
-			n1.Type = n.Type
-			n1.Xoffset += oary[0]
-			gc.Naddr(a, &n1)
-			return true
-		}
-
-		gc.Regalloc(reg, gc.Types[gc.Tptr], nil)
-		n1 := *reg
-		n1.Op = gc.OINDREG
-		if oary[0] >= 0 {
-			gc.Agen(nn, reg)
-			n1.Xoffset = oary[0]
-		} else {
-			gc.Cgen(nn, reg)
-			gc.Cgen_checknil(reg)
-			n1.Xoffset = -(oary[0] + 1)
-		}
-
-		for i := 1; i < o; i++ {
-			if oary[i] >= 0 {
-				gc.Fatalf("can't happen")
-			}
-			gins(movptr, &n1, reg)
-			gc.Cgen_checknil(reg)
-			n1.Xoffset = -(oary[i] + 1)
-		}
-
-		a.Type = obj.TYPE_NONE
-		a.Index = x86.REG_NONE
-		gc.Fixlargeoffset(&n1)
-		gc.Naddr(a, &n1)
-		return true
-
-	case gc.OINDEX:
-		return false
-	}
-
-	return false
-}
diff --git a/src/cmd/compile/internal/amd64/peep.go b/src/cmd/compile/internal/amd64/peep.go
deleted file mode 100644
index 4ae5fce..0000000
--- a/src/cmd/compile/internal/amd64/peep.go
+++ /dev/null
@@ -1,1025 +0,0 @@
-// Derived from Inferno utils/6c/peep.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package amd64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/x86"
-	"fmt"
-)
-
-var gactive uint32
-
-const (
-	exregoffset = x86.REG_R15
-)
-
-// do we need the carry bit
-func needc(p *obj.Prog) bool {
-	for p != nil {
-		flags := progcarryflags(p)
-		if flags&gc.UseCarry != 0 {
-			return true
-		}
-		if flags&(gc.SetCarry|gc.KillCarry) != 0 {
-			return false
-		}
-		p = p.Link
-	}
-
-	return false
-}
-
-func rnops(r *gc.Flow) *gc.Flow {
-	if r != nil {
-		var p *obj.Prog
-		var r1 *gc.Flow
-		for {
-			p = r.Prog
-			if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE {
-				break
-			}
-			r1 = gc.Uniqs(r)
-			if r1 == nil {
-				break
-			}
-			r = r1
-		}
-	}
-
-	return r
-}
-
-func peep(firstp *obj.Prog) {
-	g := gc.Flowstart(firstp, nil)
-	if g == nil {
-		return
-	}
-	gactive = 0
-
-	// byte, word arithmetic elimination.
-	elimshortmov(g)
-
-	// constant propagation
-	// find MOV $con,R followed by
-	// another MOV $con,R without
-	// setting R in the interim
-	var p *obj.Prog
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		case x86.ALEAL,
-			x86.ALEAQ:
-			if regtyp(&p.To) {
-				if p.From.Sym != nil {
-					if p.From.Index == x86.REG_NONE {
-						conprop(r)
-					}
-				}
-			}
-
-		case x86.AMOVB,
-			x86.AMOVW,
-			x86.AMOVL,
-			x86.AMOVQ,
-			x86.AMOVSS,
-			x86.AMOVSD:
-			if regtyp(&p.To) {
-				if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST {
-					conprop(r)
-				}
-			}
-		}
-	}
-
-	var r *gc.Flow
-	var r1 *gc.Flow
-	var p1 *obj.Prog
-	var t int
-loop1:
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		gc.Dumpit("loop1", g.Start, 0)
-	}
-
-	t = 0
-	for r = g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		case x86.AMOVL,
-			x86.AMOVQ,
-			x86.AMOVSS,
-			x86.AMOVSD:
-			if regtyp(&p.To) {
-				if regtyp(&p.From) {
-					if copyprop(g, r) {
-						excise(r)
-						t++
-					} else if subprop(r) && copyprop(g, r) {
-						excise(r)
-						t++
-					}
-				}
-			}
-
-		case x86.AMOVBLZX,
-			x86.AMOVWLZX,
-			x86.AMOVBLSX,
-			x86.AMOVWLSX:
-			if regtyp(&p.To) {
-				r1 = rnops(gc.Uniqs(r))
-				if r1 != nil {
-					p1 = r1.Prog
-					if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg {
-						p1.As = x86.AMOVL
-						t++
-					}
-				}
-			}
-
-		case x86.AMOVBQSX,
-			x86.AMOVBQZX,
-			x86.AMOVWQSX,
-			x86.AMOVWQZX,
-			x86.AMOVLQSX,
-			x86.AMOVLQZX,
-			x86.AMOVQL:
-			if regtyp(&p.To) {
-				r1 = rnops(gc.Uniqs(r))
-				if r1 != nil {
-					p1 = r1.Prog
-					if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg {
-						p1.As = x86.AMOVQ
-						t++
-					}
-				}
-			}
-
-		case x86.AADDL,
-			x86.AADDQ,
-			x86.AADDW:
-			if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
-				break
-			}
-			if p.From.Offset == -1 {
-				if p.As == x86.AADDQ {
-					p.As = x86.ADECQ
-				} else if p.As == x86.AADDL {
-					p.As = x86.ADECL
-				} else {
-					p.As = x86.ADECW
-				}
-				p.From = obj.Addr{}
-				break
-			}
-
-			if p.From.Offset == 1 {
-				if p.As == x86.AADDQ {
-					p.As = x86.AINCQ
-				} else if p.As == x86.AADDL {
-					p.As = x86.AINCL
-				} else {
-					p.As = x86.AINCW
-				}
-				p.From = obj.Addr{}
-				break
-			}
-
-		case x86.ASUBL,
-			x86.ASUBQ,
-			x86.ASUBW:
-			if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
-				break
-			}
-			if p.From.Offset == -1 {
-				if p.As == x86.ASUBQ {
-					p.As = x86.AINCQ
-				} else if p.As == x86.ASUBL {
-					p.As = x86.AINCL
-				} else {
-					p.As = x86.AINCW
-				}
-				p.From = obj.Addr{}
-				break
-			}
-
-			if p.From.Offset == 1 {
-				if p.As == x86.ASUBQ {
-					p.As = x86.ADECQ
-				} else if p.As == x86.ASUBL {
-					p.As = x86.ADECL
-				} else {
-					p.As = x86.ADECW
-				}
-				p.From = obj.Addr{}
-				break
-			}
-		}
-	}
-
-	if t != 0 {
-		goto loop1
-	}
-
-	// MOVLQZX removal.
-	// The MOVLQZX exists to avoid being confused for a
-	// MOVL that is just copying 32-bit data around during
-	// copyprop. Now that copyprop is done, remov MOVLQZX R1, R2
-	// if it is dominated by an earlier ADDL/MOVL/etc into R1 that
-	// will have already cleared the high bits.
-	//
-	// MOVSD removal.
-	// We never use packed registers, so a MOVSD between registers
-	// can be replaced by MOVAPD, which moves the pair of float64s
-	// instead of just the lower one. We only use the lower one, but
-	// the processor can do better if we do moves using both.
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		if p.As == x86.AMOVLQZX {
-			if regtyp(&p.From) {
-				if p.From.Type == p.To.Type && p.From.Reg == p.To.Reg {
-					if prevl(r, p.From.Reg) {
-						excise(r)
-					}
-				}
-			}
-		}
-
-		if p.As == x86.AMOVSD {
-			if regtyp(&p.From) {
-				if regtyp(&p.To) {
-					p.As = x86.AMOVAPD
-				}
-			}
-		}
-	}
-
-	// load pipelining
-	// push any load from memory as early as possible
-	// to give it time to complete before use.
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		case x86.AMOVB,
-			x86.AMOVW,
-			x86.AMOVL,
-			x86.AMOVQ,
-			x86.AMOVLQZX:
-			if regtyp(&p.To) && !regconsttyp(&p.From) {
-				pushback(r)
-			}
-		}
-	}
-
-	gc.Flowend(g)
-}
-
-func pushback(r0 *gc.Flow) {
-	var r *gc.Flow
-	var p *obj.Prog
-
-	var b *gc.Flow
-	p0 := r0.Prog
-	for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) {
-		p = r.Prog
-		if p.As != obj.ANOP {
-			if !regconsttyp(&p.From) || !regtyp(&p.To) {
-				break
-			}
-			if copyu(p, &p0.To, nil) != 0 || copyu(p0, &p.To, nil) != 0 {
-				break
-			}
-		}
-
-		if p.As == obj.ACALL {
-			break
-		}
-		b = r
-	}
-
-	if b == nil {
-		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-			fmt.Printf("no pushback: %v\n", r0.Prog)
-			if r != nil {
-				fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil)
-			}
-		}
-
-		return
-	}
-
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("pushback\n")
-		for r := b; ; r = r.Link {
-			fmt.Printf("\t%v\n", r.Prog)
-			if r == r0 {
-				break
-			}
-		}
-	}
-
-	t := *r0.Prog
-	for r = gc.Uniqp(r0); ; r = gc.Uniqp(r) {
-		p0 = r.Link.Prog
-		p = r.Prog
-		p0.As = p.As
-		p0.Lineno = p.Lineno
-		p0.From = p.From
-		p0.To = p.To
-
-		if r == b {
-			break
-		}
-	}
-
-	p0 = r.Prog
-	p0.As = t.As
-	p0.Lineno = t.Lineno
-	p0.From = t.From
-	p0.To = t.To
-
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("\tafter\n")
-		for r := b; ; r = r.Link {
-			fmt.Printf("\t%v\n", r.Prog)
-			if r == r0 {
-				break
-			}
-		}
-	}
-}
-
-func excise(r *gc.Flow) {
-	p := r.Prog
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("%v ===delete===\n", p)
-	}
-
-	obj.Nopout(p)
-
-	gc.Ostats.Ndelmov++
-}
-
-func regtyp(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_R15 || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X15)
-}
-
-// movb elimination.
-// movb is simulated by the linker
-// when a register other than ax, bx, cx, dx
-// is used, so rewrite to other instructions
-// when possible.  a movb into a register
-// can smash the entire 32-bit register without
-// causing any trouble.
-//
-// TODO: Using the Q forms here instead of the L forms
-// seems unnecessary, and it makes the instructions longer.
-func elimshortmov(g *gc.Graph) {
-	var p *obj.Prog
-
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		if regtyp(&p.To) {
-			switch p.As {
-			case x86.AINCB,
-				x86.AINCW:
-				p.As = x86.AINCQ
-
-			case x86.ADECB,
-				x86.ADECW:
-				p.As = x86.ADECQ
-
-			case x86.ANEGB,
-				x86.ANEGW:
-				p.As = x86.ANEGQ
-
-			case x86.ANOTB,
-				x86.ANOTW:
-				p.As = x86.ANOTQ
-			}
-
-			if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST {
-				// move or arithmetic into partial register.
-				// from another register or constant can be movl.
-				// we don't switch to 64-bit arithmetic if it can
-				// change how the carry bit is set (and the carry bit is needed).
-				switch p.As {
-				case x86.AMOVB,
-					x86.AMOVW:
-					p.As = x86.AMOVQ
-
-				case x86.AADDB,
-					x86.AADDW:
-					if !needc(p.Link) {
-						p.As = x86.AADDQ
-					}
-
-				case x86.ASUBB,
-					x86.ASUBW:
-					if !needc(p.Link) {
-						p.As = x86.ASUBQ
-					}
-
-				case x86.AMULB,
-					x86.AMULW:
-					p.As = x86.AMULQ
-
-				case x86.AIMULB,
-					x86.AIMULW:
-					p.As = x86.AIMULQ
-
-				case x86.AANDB,
-					x86.AANDW:
-					p.As = x86.AANDQ
-
-				case x86.AORB,
-					x86.AORW:
-					p.As = x86.AORQ
-
-				case x86.AXORB,
-					x86.AXORW:
-					p.As = x86.AXORQ
-
-				case x86.ASHLB,
-					x86.ASHLW:
-					p.As = x86.ASHLQ
-				}
-			} else if p.From.Type != obj.TYPE_REG {
-				// explicit zero extension, but don't
-				// do that if source is a byte register
-				// (only AH can occur and it's forbidden).
-				switch p.As {
-				case x86.AMOVB:
-					p.As = x86.AMOVBQZX
-
-				case x86.AMOVW:
-					p.As = x86.AMOVWQZX
-				}
-			}
-		}
-	}
-}
-
-// is 'a' a register or constant?
-func regconsttyp(a *obj.Addr) bool {
-	if regtyp(a) {
-		return true
-	}
-	switch a.Type {
-	case obj.TYPE_CONST,
-		obj.TYPE_FCONST,
-		obj.TYPE_SCONST,
-		obj.TYPE_ADDR: // TODO(rsc): Not all TYPE_ADDRs are constants.
-		return true
-	}
-
-	return false
-}
-
-// is reg guaranteed to be truncated by a previous L instruction?
-func prevl(r0 *gc.Flow, reg int16) bool {
-	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
-		p := r.Prog
-		if p.To.Type == obj.TYPE_REG && p.To.Reg == reg {
-			flags := progflags(p)
-			if flags&gc.RightWrite != 0 {
-				if flags&gc.SizeL != 0 {
-					return true
-				}
-				return false
-			}
-		}
-	}
-	return false
-}
-
-/*
- * the idea is to substitute
- * one register for another
- * from one MOV to another
- *	MOV	a, R0
- *	ADD	b, R0	/ no use of R1
- *	MOV	R0, R1
- * would be converted to
- *	MOV	a, R1
- *	ADD	b, R1
- *	MOV	R1, R0
- * hopefully, then the former or latter MOV
- * will be eliminated by copy propagation.
- */
-func subprop(r0 *gc.Flow) bool {
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("subprop %v\n", r0.Prog)
-	}
-	p := r0.Prog
-	v1 := &p.From
-	if !regtyp(v1) {
-		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-			fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v1))
-		}
-		return false
-	}
-
-	v2 := &p.To
-	if !regtyp(v2) {
-		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-			fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v2))
-		}
-		return false
-	}
-
-	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
-		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-			fmt.Printf("\t? %v\n", r.Prog)
-		}
-		if gc.Uniqs(r) == nil {
-			if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-				fmt.Printf("\tno unique successor\n")
-			}
-			break
-		}
-
-		p = r.Prog
-		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
-			continue
-		}
-		if p.Info.Flags&gc.Call != 0 {
-			if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-				fmt.Printf("\tfound %v; return 0\n", p)
-			}
-			return false
-		}
-
-		if p.Info.Reguse|p.Info.Regset != 0 {
-			if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-				fmt.Printf("\tfound %v; return 0\n", p)
-			}
-			return false
-		}
-
-		if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg {
-			copysub(&p.To, v1, v2, true)
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
-				if p.From.Type == v2.Type && p.From.Reg == v2.Reg {
-					fmt.Printf(" excise")
-				}
-				fmt.Printf("\n")
-			}
-
-			for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
-				p = r.Prog
-				copysub(&p.From, v1, v2, true)
-				copysub(&p.To, v1, v2, true)
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("%v\n", r.Prog)
-				}
-			}
-
-			v1.Reg, v2.Reg = v2.Reg, v1.Reg
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("%v last\n", r.Prog)
-			}
-			return true
-		}
-
-		if copyau(&p.From, v2) || copyau(&p.To, v2) {
-			if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-				fmt.Printf("\tcopyau %v failed\n", gc.Ctxt.Dconv(v2))
-			}
-			break
-		}
-
-		if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) {
-			if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-				fmt.Printf("\tcopysub failed\n")
-			}
-			break
-		}
-	}
-
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("\tran off end; return 0\n")
-	}
-	return false
-}
-
-/*
- * The idea is to remove redundant copies.
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	use v2	return fail
- *	-----------------
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	set v2	return success
- */
-func copyprop(g *gc.Graph, r0 *gc.Flow) bool {
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("copyprop %v\n", r0.Prog)
-	}
-	p := r0.Prog
-	v1 := &p.From
-	v2 := &p.To
-	if copyas(v1, v2) {
-		return true
-	}
-	gactive++
-	return copy1(v1, v2, r0.S1, false)
-}
-
-func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool {
-	if uint32(r.Active) == gactive {
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("act set; return 1\n")
-		}
-		return true
-	}
-
-	r.Active = int32(gactive)
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f)
-	}
-	for ; r != nil; r = r.S1 {
-		p := r.Prog
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("%v", p)
-		}
-		if !f && gc.Uniqp(r) == nil {
-			f = true
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; merge; f=%v", f)
-			}
-		}
-
-		switch t := copyu(p, v2, nil); t {
-		case 2: /* rar, can't split */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
-			}
-			return false
-
-		case 3: /* set */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
-			}
-			return true
-
-		case 1, /* used, substitute */
-			4: /* use and set */
-			if f {
-				if gc.Debug['P'] == 0 {
-					return false
-				}
-				if t == 4 {
-					fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				} else {
-					fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				}
-				return false
-			}
-
-			if copyu(p, v2, v1) != 0 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; sub fail; return 0\n")
-				}
-				return false
-			}
-
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1))
-			}
-			if t == 4 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
-				}
-				return true
-			}
-		}
-
-		if !f {
-			t := copyu(p, v1, nil)
-			if t == 2 || t == 3 || t == 4 {
-				f = true
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f)
-				}
-			}
-		}
-
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("\n")
-		}
-		if r.S2 != nil {
-			if !copy1(v1, v2, r.S2, f) {
-				return false
-			}
-		}
-	}
-	return true
-}
-
-/*
- * return
- * 1 if v only used (and substitute),
- * 2 if read-alter-rewrite
- * 3 if set
- * 4 if set and used
- * 0 otherwise (not touched)
- */
-func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
-	switch p.As {
-	case obj.AJMP:
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case obj.ARET:
-		if s != nil {
-			return 1
-		}
-		return 3
-
-	case obj.ACALL:
-		if x86.REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= x86.REGEXT && v.Reg > exregoffset {
-			return 2
-		}
-		if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
-			return 2
-		}
-		if v.Type == p.From.Type && v.Reg == p.From.Reg {
-			return 2
-		}
-
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.To, v) {
-			return 4
-		}
-		return 3
-
-	case obj.ATEXT:
-		if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
-			return 3
-		}
-		return 0
-	}
-
-	if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
-		return 0
-	}
-
-	if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 {
-		return 2
-	}
-
-	if (p.Info.Reguse|p.Info.Regset)&FtoB(int(v.Reg)) != 0 {
-		return 2
-	}
-
-	if p.Info.Flags&gc.LeftAddr != 0 {
-		if copyas(&p.From, v) {
-			return 2
-		}
-	}
-
-	if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite {
-		if copyas(&p.To, v) {
-			return 2
-		}
-	}
-
-	if p.Info.Flags&gc.RightWrite != 0 {
-		if copyas(&p.To, v) {
-			if s != nil {
-				if copysub(&p.From, v, s, true) {
-					return 1
-				}
-				return 0
-			}
-			if copyau(&p.From, v) {
-				return 4
-			}
-			return 3
-		}
-	}
-
-	if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 {
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau(&p.To, v) {
-			return 1
-		}
-	}
-	return 0
-}
-
-/*
- * direct reference,
- * could be set/use depending on
- * semantics
- */
-func copyas(a *obj.Addr, v *obj.Addr) bool {
-	if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_R15B {
-		gc.Fatalf("use of byte register")
-	}
-	if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_R15B {
-		gc.Fatalf("use of byte register")
-	}
-
-	if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
-		return false
-	}
-	if regtyp(v) {
-		return true
-	}
-	if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
-		if v.Offset == a.Offset {
-			return true
-		}
-	}
-	return false
-}
-
-func sameaddr(a *obj.Addr, v *obj.Addr) bool {
-	if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
-		return false
-	}
-	if regtyp(v) {
-		return true
-	}
-	if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
-		if v.Offset == a.Offset {
-			return true
-		}
-	}
-	return false
-}
-
-/*
- * either direct or indirect
- */
-func copyau(a *obj.Addr, v *obj.Addr) bool {
-	if copyas(a, v) {
-		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-			fmt.Printf("\tcopyau: copyas returned 1\n")
-		}
-		return true
-	}
-
-	if regtyp(v) {
-		if a.Type == obj.TYPE_MEM && a.Reg == v.Reg {
-			if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-				fmt.Printf("\tcopyau: found indir use - return 1\n")
-			}
-			return true
-		}
-
-		if a.Index == v.Reg {
-			if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-				fmt.Printf("\tcopyau: found index use - return 1\n")
-			}
-			return true
-		}
-	}
-	return false
-}
-
-// copysub substitute s for v in a.
-// copysub returns true on failure to substitute. TODO(dfc) reverse this logic, copysub should return false on failure
-func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool {
-	if copyas(a, v) {
-		if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_R15 || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X0+15 {
-			if f {
-				a.Reg = s.Reg
-			}
-		}
-		return false
-	}
-
-	if regtyp(v) {
-		if a.Type == obj.TYPE_MEM && a.Reg == v.Reg {
-			if (s.Reg == x86.REG_BP || s.Reg == x86.REG_R13) && a.Index != x86.REG_NONE {
-				return true /* can't use BP-base with index */
-			}
-			if f {
-				a.Reg = s.Reg
-			}
-		}
-		if a.Index == v.Reg {
-			if f {
-				a.Index = s.Reg
-			}
-		}
-	}
-	return false
-}
-
-func conprop(r0 *gc.Flow) {
-	p0 := r0.Prog
-	v0 := &p0.To
-	r := r0
-
-loop:
-	r = gc.Uniqs(r)
-	if r == nil || r == r0 {
-		return
-	}
-	if gc.Uniqp(r) == nil {
-		return
-	}
-
-	p := r.Prog
-	t := copyu(p, v0, nil)
-	switch t {
-	case 0, // miss
-		1: // use
-		goto loop
-
-	case 2, // rar
-		4: // use and set
-		break
-
-	case 3: // set
-		if p.As == p0.As {
-			if p.From.Type == p0.From.Type {
-				if p.From.Reg == p0.From.Reg {
-					if p.From.Node == p0.From.Node {
-						if p.From.Offset == p0.From.Offset {
-							if p.From.Scale == p0.From.Scale {
-								if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) {
-									if p.From.Index == p0.From.Index {
-										excise(r)
-										goto loop
-									}
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-}
-
-func smallindir(a *obj.Addr, reg *obj.Addr) bool {
-	return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096
-}
-
-func stackaddr(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP
-}
diff --git a/src/cmd/compile/internal/amd64/prog.go b/src/cmd/compile/internal/amd64/prog.go
index 91b479b..bd95f46 100644
--- a/src/cmd/compile/internal/amd64/prog.go
+++ b/src/cmd/compile/internal/amd64/prog.go
@@ -22,14 +22,13 @@ const (
 // Instructions not generated need not be listed.
 // As an exception to that rule, we typically write down all the
 // size variants of an operation even if we just use a subset.
-var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
+var progtable = [x86.ALAST & obj.AMask]gc.ProgInfo{
 	obj.ATYPE:     {Flags: gc.Pseudo | gc.Skip},
 	obj.ATEXT:     {Flags: gc.Pseudo},
 	obj.AFUNCDATA: {Flags: gc.Pseudo},
 	obj.APCDATA:   {Flags: gc.Pseudo},
 	obj.AUNDEF:    {Flags: gc.Break},
 	obj.AUSEFIELD: {Flags: gc.OK},
-	obj.ACHECKNIL: {Flags: gc.LeftRead},
 	obj.AVARDEF:   {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARKILL:  {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARLIVE:  {Flags: gc.Pseudo | gc.LeftRead},
@@ -61,9 +60,9 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ABSWAPQ & obj.AMask: {Flags: gc.SizeQ | RightRdwr},
 
 	obj.ACALL & obj.AMask: {Flags: gc.RightAddr | gc.Call | gc.KillCarry},
-	x86.ACDQ & obj.AMask:  {Flags: gc.OK, Reguse: AX, Regset: AX | DX},
-	x86.ACQO & obj.AMask:  {Flags: gc.OK, Reguse: AX, Regset: AX | DX},
-	x86.ACWD & obj.AMask:  {Flags: gc.OK, Reguse: AX, Regset: AX | DX},
+	x86.ACDQ & obj.AMask:  {Flags: gc.OK},
+	x86.ACQO & obj.AMask:  {Flags: gc.OK},
+	x86.ACWD & obj.AMask:  {Flags: gc.OK},
 	x86.ACLD & obj.AMask:  {Flags: gc.OK},
 	x86.ASTD & obj.AMask:  {Flags: gc.OK},
 
@@ -78,6 +77,8 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ACMPL & obj.AMask:      {Flags: gc.SizeL | gc.LeftRead | gc.RightRead | gc.SetCarry},
 	x86.ACMPQ & obj.AMask:      {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead | gc.SetCarry},
 	x86.ACMPW & obj.AMask:      {Flags: gc.SizeW | gc.LeftRead | gc.RightRead | gc.SetCarry},
+	x86.ACMPXCHGL & obj.AMask:  {Flags: gc.SizeL | LeftRdwr | RightRdwr | gc.SetCarry},
+	x86.ACMPXCHGQ & obj.AMask:  {Flags: gc.SizeQ | LeftRdwr | RightRdwr | gc.SetCarry},
 	x86.ACOMISD & obj.AMask:    {Flags: gc.SizeD | gc.LeftRead | gc.RightRead | gc.SetCarry},
 	x86.ACOMISS & obj.AMask:    {Flags: gc.SizeF | gc.LeftRead | gc.RightRead | gc.SetCarry},
 	x86.ACVTSD2SL & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
@@ -98,17 +99,17 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ADECL & obj.AMask:      {Flags: gc.SizeL | RightRdwr},
 	x86.ADECQ & obj.AMask:      {Flags: gc.SizeQ | RightRdwr},
 	x86.ADECW & obj.AMask:      {Flags: gc.SizeW | RightRdwr},
-	x86.ADIVB & obj.AMask:      {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX},
-	x86.ADIVL & obj.AMask:      {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry, Reguse: AX | DX, Regset: AX | DX},
-	x86.ADIVQ & obj.AMask:      {Flags: gc.SizeQ | gc.LeftRead | gc.SetCarry, Reguse: AX | DX, Regset: AX | DX},
-	x86.ADIVW & obj.AMask:      {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry, Reguse: AX | DX, Regset: AX | DX},
+	x86.ADIVB & obj.AMask:      {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry},
+	x86.ADIVL & obj.AMask:      {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry},
+	x86.ADIVQ & obj.AMask:      {Flags: gc.SizeQ | gc.LeftRead | gc.SetCarry},
+	x86.ADIVW & obj.AMask:      {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry},
 	x86.ADIVSD & obj.AMask:     {Flags: gc.SizeD | gc.LeftRead | RightRdwr},
 	x86.ADIVSS & obj.AMask:     {Flags: gc.SizeF | gc.LeftRead | RightRdwr},
-	x86.AIDIVB & obj.AMask:     {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX},
-	x86.AIDIVL & obj.AMask:     {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry, Reguse: AX | DX, Regset: AX | DX},
-	x86.AIDIVQ & obj.AMask:     {Flags: gc.SizeQ | gc.LeftRead | gc.SetCarry, Reguse: AX | DX, Regset: AX | DX},
-	x86.AIDIVW & obj.AMask:     {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry, Reguse: AX | DX, Regset: AX | DX},
-	x86.AIMULB & obj.AMask:     {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX},
+	x86.AIDIVB & obj.AMask:     {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry},
+	x86.AIDIVL & obj.AMask:     {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry},
+	x86.AIDIVQ & obj.AMask:     {Flags: gc.SizeQ | gc.LeftRead | gc.SetCarry},
+	x86.AIDIVW & obj.AMask:     {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry},
+	x86.AIMULB & obj.AMask:     {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry},
 	x86.AIMULL & obj.AMask:     {Flags: gc.SizeL | gc.LeftRead | gc.ImulAXDX | gc.SetCarry},
 	x86.AIMULQ & obj.AMask:     {Flags: gc.SizeQ | gc.LeftRead | gc.ImulAXDX | gc.SetCarry},
 	x86.AIMULW & obj.AMask:     {Flags: gc.SizeW | gc.LeftRead | gc.ImulAXDX | gc.SetCarry},
@@ -136,6 +137,7 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ALEAW & obj.AMask:      {Flags: gc.LeftAddr | gc.RightWrite},
 	x86.ALEAL & obj.AMask:      {Flags: gc.LeftAddr | gc.RightWrite},
 	x86.ALEAQ & obj.AMask:      {Flags: gc.LeftAddr | gc.RightWrite},
+	x86.ALOCK & obj.AMask:      {Flags: gc.OK},
 	x86.AMOVBLSX & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
 	x86.AMOVBLZX & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
 	x86.AMOVBQSX & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv},
@@ -154,20 +156,20 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.AMOVQ & obj.AMask:      {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
 	x86.AMOVW & obj.AMask:      {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move},
 	x86.AMOVUPS & obj.AMask:    {Flags: gc.LeftRead | gc.RightWrite | gc.Move},
-	x86.AMOVSB & obj.AMask:     {Flags: gc.OK, Reguse: DI | SI, Regset: DI | SI},
-	x86.AMOVSL & obj.AMask:     {Flags: gc.OK, Reguse: DI | SI, Regset: DI | SI},
-	x86.AMOVSQ & obj.AMask:     {Flags: gc.OK, Reguse: DI | SI, Regset: DI | SI},
-	x86.AMOVSW & obj.AMask:     {Flags: gc.OK, Reguse: DI | SI, Regset: DI | SI},
-	obj.ADUFFCOPY & obj.AMask:  {Flags: gc.OK, Reguse: DI | SI, Regset: DI | SI | X0},
+	x86.AMOVSB & obj.AMask:     {Flags: gc.OK},
+	x86.AMOVSL & obj.AMask:     {Flags: gc.OK},
+	x86.AMOVSQ & obj.AMask:     {Flags: gc.OK},
+	x86.AMOVSW & obj.AMask:     {Flags: gc.OK},
+	obj.ADUFFCOPY & obj.AMask:  {Flags: gc.OK},
 	x86.AMOVSD & obj.AMask:     {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
 	x86.AMOVSS & obj.AMask:     {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move},
 
 	// We use&obj.AMask MOVAPD as a faster synonym for MOVSD.
 	x86.AMOVAPD & obj.AMask:   {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
-	x86.AMULB & obj.AMask:     {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX},
-	x86.AMULL & obj.AMask:     {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX | DX},
-	x86.AMULQ & obj.AMask:     {Flags: gc.SizeQ | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX | DX},
-	x86.AMULW & obj.AMask:     {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX | DX},
+	x86.AMULB & obj.AMask:     {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry},
+	x86.AMULL & obj.AMask:     {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry},
+	x86.AMULQ & obj.AMask:     {Flags: gc.SizeQ | gc.LeftRead | gc.SetCarry},
+	x86.AMULW & obj.AMask:     {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry},
 	x86.AMULSD & obj.AMask:    {Flags: gc.SizeD | gc.LeftRead | RightRdwr},
 	x86.AMULSS & obj.AMask:    {Flags: gc.SizeF | gc.LeftRead | RightRdwr},
 	x86.ANEGB & obj.AMask:     {Flags: gc.SizeB | RightRdwr | gc.SetCarry},
@@ -193,8 +195,8 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ARCRL & obj.AMask:     {Flags: gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry},
 	x86.ARCRQ & obj.AMask:     {Flags: gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry},
 	x86.ARCRW & obj.AMask:     {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry},
-	x86.AREP & obj.AMask:      {Flags: gc.OK, Reguse: CX, Regset: CX},
-	x86.AREPN & obj.AMask:     {Flags: gc.OK, Reguse: CX, Regset: CX},
+	x86.AREP & obj.AMask:      {Flags: gc.OK},
+	x86.AREPN & obj.AMask:     {Flags: gc.OK},
 	obj.ARET & obj.AMask:      {Flags: gc.Break | gc.KillCarry},
 	x86.AROLB & obj.AMask:     {Flags: gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
 	x86.AROLL & obj.AMask:     {Flags: gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
@@ -241,11 +243,11 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ASHRQ & obj.AMask:     {Flags: gc.SizeQ | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
 	x86.ASHRW & obj.AMask:     {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
 	x86.ASQRTSD & obj.AMask:   {Flags: gc.SizeD | gc.LeftRead | RightRdwr},
-	x86.ASTOSB & obj.AMask:    {Flags: gc.OK, Reguse: AX | DI, Regset: DI},
-	x86.ASTOSL & obj.AMask:    {Flags: gc.OK, Reguse: AX | DI, Regset: DI},
-	x86.ASTOSQ & obj.AMask:    {Flags: gc.OK, Reguse: AX | DI, Regset: DI},
-	x86.ASTOSW & obj.AMask:    {Flags: gc.OK, Reguse: AX | DI, Regset: DI},
-	obj.ADUFFZERO & obj.AMask: {Flags: gc.OK, Reguse: X0 | DI, Regset: DI},
+	x86.ASTOSB & obj.AMask:    {Flags: gc.OK},
+	x86.ASTOSL & obj.AMask:    {Flags: gc.OK},
+	x86.ASTOSQ & obj.AMask:    {Flags: gc.OK},
+	x86.ASTOSW & obj.AMask:    {Flags: gc.OK},
+	obj.ADUFFZERO & obj.AMask: {Flags: gc.OK},
 	x86.ASUBB & obj.AMask:     {Flags: gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry},
 	x86.ASUBL & obj.AMask:     {Flags: gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry},
 	x86.ASUBQ & obj.AMask:     {Flags: gc.SizeQ | gc.LeftRead | RightRdwr | gc.SetCarry},
@@ -258,6 +260,8 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ATESTW & obj.AMask:    {Flags: gc.SizeW | gc.LeftRead | gc.RightRead | gc.SetCarry},
 	x86.AUCOMISD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RightRead},
 	x86.AUCOMISS & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | gc.RightRead},
+	x86.AXADDL & obj.AMask:    {Flags: gc.SizeL | LeftRdwr | RightRdwr | gc.KillCarry},
+	x86.AXADDQ & obj.AMask:    {Flags: gc.SizeQ | LeftRdwr | RightRdwr | gc.KillCarry},
 	x86.AXCHGB & obj.AMask:    {Flags: gc.SizeB | LeftRdwr | RightRdwr},
 	x86.AXCHGL & obj.AMask:    {Flags: gc.SizeL | LeftRdwr | RightRdwr},
 	x86.AXCHGQ & obj.AMask:    {Flags: gc.SizeQ | LeftRdwr | RightRdwr},
@@ -269,61 +273,15 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.AXORPS & obj.AMask:    {Flags: gc.LeftRead | RightRdwr},
 }
 
-func progflags(p *obj.Prog) uint32 {
-	flags := progtable[p.As&obj.AMask].Flags
-	if flags&gc.ImulAXDX != 0 && p.To.Type != obj.TYPE_NONE {
-		flags |= RightRdwr
-	}
-	return flags
-}
-
-func progcarryflags(p *obj.Prog) uint32 {
-	return progtable[p.As&obj.AMask].Flags
-}
-
-func proginfo(p *obj.Prog) {
-	info := &p.Info
-	*info = progtable[p.As&obj.AMask]
+func proginfo(p *obj.Prog) gc.ProgInfo {
+	info := progtable[p.As&obj.AMask]
 	if info.Flags == 0 {
 		gc.Fatalf("unknown instruction %v", p)
 	}
 
-	if (info.Flags&gc.ShiftCX != 0) && p.From.Type != obj.TYPE_CONST {
-		info.Reguse |= CX
-	}
-
-	if info.Flags&gc.ImulAXDX != 0 {
-		if p.To.Type == obj.TYPE_NONE {
-			info.Reguse |= AX
-			info.Regset |= AX | DX
-		} else {
-			info.Flags |= RightRdwr
-		}
+	if info.Flags&gc.ImulAXDX != 0 && p.To.Type != obj.TYPE_NONE {
+		info.Flags |= RightRdwr
 	}
 
-	// Addressing makes some registers used.
-	if p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_NONE {
-		info.Regindex |= RtoB(int(p.From.Reg))
-	}
-	if p.From.Index != x86.REG_NONE {
-		info.Regindex |= RtoB(int(p.From.Index))
-	}
-	if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE {
-		info.Regindex |= RtoB(int(p.To.Reg))
-	}
-	if p.To.Index != x86.REG_NONE {
-		info.Regindex |= RtoB(int(p.To.Index))
-	}
-	if gc.Ctxt.Flag_dynlink {
-		// When -dynlink is passed, many operations on external names (and
-		// also calling duffzero/duffcopy) use R15 as a scratch register.
-		if p.As == x86.ALEAQ || info.Flags == gc.Pseudo || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
-			return
-		}
-		if p.As == obj.ADUFFZERO || p.As == obj.ADUFFCOPY || (p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local) || (p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local) {
-			info.Reguse |= R15
-			info.Regset |= R15
-			return
-		}
-	}
+	return info
 }
diff --git a/src/cmd/compile/internal/amd64/reg.go b/src/cmd/compile/internal/amd64/reg.go
deleted file mode 100644
index 77720c8..0000000
--- a/src/cmd/compile/internal/amd64/reg.go
+++ /dev/null
@@ -1,152 +0,0 @@
-// Derived from Inferno utils/6c/reg.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package amd64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj/x86"
-)
-
-const (
-	NREGVAR = 32
-)
-
-var regname = []string{
-	".AX",
-	".CX",
-	".DX",
-	".BX",
-	".SP",
-	".BP",
-	".SI",
-	".DI",
-	".R8",
-	".R9",
-	".R10",
-	".R11",
-	".R12",
-	".R13",
-	".R14",
-	".R15",
-	".X0",
-	".X1",
-	".X2",
-	".X3",
-	".X4",
-	".X5",
-	".X6",
-	".X7",
-	".X8",
-	".X9",
-	".X10",
-	".X11",
-	".X12",
-	".X13",
-	".X14",
-	".X15",
-}
-
-func regnames(n *int) []string {
-	*n = NREGVAR
-	return regname
-}
-
-func excludedregs() uint64 {
-	return RtoB(x86.REG_SP)
-}
-
-func doregbits(r int) uint64 {
-	b := uint64(0)
-	if r >= x86.REG_AX && r <= x86.REG_R15 {
-		b |= RtoB(r)
-	} else if r >= x86.REG_AL && r <= x86.REG_R15B {
-		b |= RtoB(r - x86.REG_AL + x86.REG_AX)
-	} else if r >= x86.REG_AH && r <= x86.REG_BH {
-		b |= RtoB(r - x86.REG_AH + x86.REG_AX)
-	} else if r >= x86.REG_X0 && r <= x86.REG_X0+15 {
-		b |= FtoB(r)
-	}
-	return b
-}
-
-// For ProgInfo.
-const (
-	AX  = 1 << (x86.REG_AX - x86.REG_AX)
-	BX  = 1 << (x86.REG_BX - x86.REG_AX)
-	CX  = 1 << (x86.REG_CX - x86.REG_AX)
-	DX  = 1 << (x86.REG_DX - x86.REG_AX)
-	DI  = 1 << (x86.REG_DI - x86.REG_AX)
-	SI  = 1 << (x86.REG_SI - x86.REG_AX)
-	R15 = 1 << (x86.REG_R15 - x86.REG_AX)
-	X0  = 1 << 16
-)
-
-func RtoB(r int) uint64 {
-	if r < x86.REG_AX || r > x86.REG_R15 {
-		return 0
-	}
-	return 1 << uint(r-x86.REG_AX)
-}
-
-func BtoR(b uint64) int {
-	b &= 0xffff
-	if gc.Nacl {
-		b &^= (1<<(x86.REG_BP-x86.REG_AX) | 1<<(x86.REG_R15-x86.REG_AX))
-	} else if gc.Ctxt.Framepointer_enabled {
-		// BP is part of the calling convention if framepointer_enabled.
-		b &^= (1 << (x86.REG_BP - x86.REG_AX))
-	}
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + x86.REG_AX
-}
-
-/*
- *	bit	reg
- *	16	X0
- *	...
- *	31	X15
- */
-func FtoB(f int) uint64 {
-	if f < x86.REG_X0 || f > x86.REG_X15 {
-		return 0
-	}
-	return 1 << uint(f-x86.REG_X0+16)
-}
-
-func BtoF(b uint64) int {
-	b &= 0xFFFF0000
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) - 16 + x86.REG_X0
-}
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index 0350c29..3c997f2 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -14,46 +14,6 @@ import (
 	"cmd/internal/obj/x86"
 )
 
-// Smallest possible faulting page at address zero.
-const minZeroPage = 4096
-
-// ssaRegToReg maps ssa register numbers to obj register numbers.
-var ssaRegToReg = []int16{
-	x86.REG_AX,
-	x86.REG_CX,
-	x86.REG_DX,
-	x86.REG_BX,
-	x86.REG_SP,
-	x86.REG_BP,
-	x86.REG_SI,
-	x86.REG_DI,
-	x86.REG_R8,
-	x86.REG_R9,
-	x86.REG_R10,
-	x86.REG_R11,
-	x86.REG_R12,
-	x86.REG_R13,
-	x86.REG_R14,
-	x86.REG_R15,
-	x86.REG_X0,
-	x86.REG_X1,
-	x86.REG_X2,
-	x86.REG_X3,
-	x86.REG_X4,
-	x86.REG_X5,
-	x86.REG_X6,
-	x86.REG_X7,
-	x86.REG_X8,
-	x86.REG_X9,
-	x86.REG_X10,
-	x86.REG_X11,
-	x86.REG_X12,
-	x86.REG_X13,
-	x86.REG_X14,
-	x86.REG_X15,
-	0, // SB isn't a real register.  We fill an Addr.Reg field with 0 in this case.
-}
-
 // markMoves marks any MOVXconst ops that need to avoid clobbering flags.
 func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
 	flive := b.FlagsLiveAtEnd
@@ -190,9 +150,9 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 	s.SetLineno(v.Line)
 	switch v.Op {
 	case ssa.OpAMD64ADDQ, ssa.OpAMD64ADDL:
-		r := gc.SSARegNum(v)
-		r1 := gc.SSARegNum(v.Args[0])
-		r2 := gc.SSARegNum(v.Args[1])
+		r := v.Reg()
+		r1 := v.Args[0].Reg()
+		r2 := v.Args[1].Reg()
 		switch {
 		case r == r1:
 			p := gc.Prog(v.Op.Asm())
@@ -233,95 +193,93 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		ssa.OpAMD64ADDSS, ssa.OpAMD64ADDSD, ssa.OpAMD64SUBSS, ssa.OpAMD64SUBSD,
 		ssa.OpAMD64MULSS, ssa.OpAMD64MULSD, ssa.OpAMD64DIVSS, ssa.OpAMD64DIVSD,
 		ssa.OpAMD64PXOR:
-		r := gc.SSARegNum(v)
-		if r != gc.SSARegNum(v.Args[0]) {
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
 			v.Fatalf("input[0] and output not in same register %s", v.LongString())
 		}
-		opregreg(v.Op.Asm(), r, gc.SSARegNum(v.Args[1]))
-
-	case ssa.OpAMD64DIVQ, ssa.OpAMD64DIVL, ssa.OpAMD64DIVW,
-		ssa.OpAMD64DIVQU, ssa.OpAMD64DIVLU, ssa.OpAMD64DIVWU,
-		ssa.OpAMD64MODQ, ssa.OpAMD64MODL, ssa.OpAMD64MODW,
-		ssa.OpAMD64MODQU, ssa.OpAMD64MODLU, ssa.OpAMD64MODWU:
-
-		// Arg[0] is already in AX as it's the only register we allow
-		// and AX is the only output
-		x := gc.SSARegNum(v.Args[1])
+		opregreg(v.Op.Asm(), r, v.Args[1].Reg())
 
-		// CPU faults upon signed overflow, which occurs when most
-		// negative int is divided by -1.
-		var j *obj.Prog
-		if v.Op == ssa.OpAMD64DIVQ || v.Op == ssa.OpAMD64DIVL ||
-			v.Op == ssa.OpAMD64DIVW || v.Op == ssa.OpAMD64MODQ ||
-			v.Op == ssa.OpAMD64MODL || v.Op == ssa.OpAMD64MODW {
+	case ssa.OpAMD64DIVQU, ssa.OpAMD64DIVLU, ssa.OpAMD64DIVWU:
+		// Arg[0] (the dividend) is in AX.
+		// Arg[1] (the divisor) can be in any other register.
+		// Result[0] (the quotient) is in AX.
+		// Result[1] (the remainder) is in DX.
+		r := v.Args[1].Reg()
 
-			var c *obj.Prog
-			switch v.Op {
-			case ssa.OpAMD64DIVQ, ssa.OpAMD64MODQ:
-				c = gc.Prog(x86.ACMPQ)
-				j = gc.Prog(x86.AJEQ)
-				// go ahead and sign extend to save doing it later
-				gc.Prog(x86.ACQO)
+		// Zero extend dividend.
+		c := gc.Prog(x86.AXORL)
+		c.From.Type = obj.TYPE_REG
+		c.From.Reg = x86.REG_DX
+		c.To.Type = obj.TYPE_REG
+		c.To.Reg = x86.REG_DX
 
-			case ssa.OpAMD64DIVL, ssa.OpAMD64MODL:
-				c = gc.Prog(x86.ACMPL)
-				j = gc.Prog(x86.AJEQ)
-				gc.Prog(x86.ACDQ)
-
-			case ssa.OpAMD64DIVW, ssa.OpAMD64MODW:
-				c = gc.Prog(x86.ACMPW)
-				j = gc.Prog(x86.AJEQ)
-				gc.Prog(x86.ACWD)
-			}
-			c.From.Type = obj.TYPE_REG
-			c.From.Reg = x
-			c.To.Type = obj.TYPE_CONST
-			c.To.Offset = -1
+		// Issue divide.
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r
 
-			j.To.Type = obj.TYPE_BRANCH
+	case ssa.OpAMD64DIVQ, ssa.OpAMD64DIVL, ssa.OpAMD64DIVW:
+		// Arg[0] (the dividend) is in AX.
+		// Arg[1] (the divisor) can be in any other register.
+		// Result[0] (the quotient) is in AX.
+		// Result[1] (the remainder) is in DX.
+		r := v.Args[1].Reg()
 
+		// 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 = gc.Prog(x86.ACMPQ)
+		case ssa.OpAMD64DIVL:
+			c = gc.Prog(x86.ACMPL)
+		case ssa.OpAMD64DIVW:
+			c = gc.Prog(x86.ACMPW)
 		}
+		c.From.Type = obj.TYPE_REG
+		c.From.Reg = r
+		c.To.Type = obj.TYPE_CONST
+		c.To.Offset = -1
+		j1 := gc.Prog(x86.AJEQ)
+		j1.To.Type = obj.TYPE_BRANCH
 
-		// for unsigned ints, we sign extend by setting DX = 0
-		// signed ints were sign extended above
-		if v.Op == ssa.OpAMD64DIVQU || v.Op == ssa.OpAMD64MODQU ||
-			v.Op == ssa.OpAMD64DIVLU || v.Op == ssa.OpAMD64MODLU ||
-			v.Op == ssa.OpAMD64DIVWU || v.Op == ssa.OpAMD64MODWU {
-			c := gc.Prog(x86.AXORQ)
-			c.From.Type = obj.TYPE_REG
-			c.From.Reg = x86.REG_DX
-			c.To.Type = obj.TYPE_REG
-			c.To.Reg = x86.REG_DX
+		// Sign extend dividend.
+		switch v.Op {
+		case ssa.OpAMD64DIVQ:
+			gc.Prog(x86.ACQO)
+		case ssa.OpAMD64DIVL:
+			gc.Prog(x86.ACDQ)
+		case ssa.OpAMD64DIVW:
+			gc.Prog(x86.ACWD)
 		}
 
+		// Issue divide.
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = x
+		p.From.Reg = r
 
-		// signed division, rest of the check for -1 case
-		if j != nil {
-			j2 := gc.Prog(obj.AJMP)
-			j2.To.Type = obj.TYPE_BRANCH
+		// Skip over -1 fixup code.
+		j2 := gc.Prog(obj.AJMP)
+		j2.To.Type = obj.TYPE_BRANCH
 
-			var n *obj.Prog
-			if v.Op == ssa.OpAMD64DIVQ || v.Op == ssa.OpAMD64DIVL ||
-				v.Op == ssa.OpAMD64DIVW {
-				// n * -1 = -n
-				n = gc.Prog(x86.ANEGQ)
-				n.To.Type = obj.TYPE_REG
-				n.To.Reg = x86.REG_AX
-			} else {
-				// n % -1 == 0
-				n = gc.Prog(x86.AXORQ)
-				n.From.Type = obj.TYPE_REG
-				n.From.Reg = x86.REG_DX
-				n.To.Type = obj.TYPE_REG
-				n.To.Reg = x86.REG_DX
-			}
+		// Issue -1 fixup code.
+		// n / -1 = -n
+		n1 := gc.Prog(x86.ANEGQ)
+		n1.To.Type = obj.TYPE_REG
+		n1.To.Reg = x86.REG_AX
 
-			j.To.Val = n
-			j2.To.Val = s.Pc()
-		}
+		// n % -1 == 0
+		n2 := gc.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.OpAMD64HMULW, ssa.OpAMD64HMULB,
 		ssa.OpAMD64HMULQU, ssa.OpAMD64HMULLU, ssa.OpAMD64HMULWU, ssa.OpAMD64HMULBU:
@@ -333,7 +291,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		// and DX is the only output we care about (the high bits)
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[1])
+		p.From.Reg = v.Args[1].Reg()
 
 		// IMULB puts the high portion in AH instead of DL,
 		// so move it to DL for consistency
@@ -345,19 +303,33 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 			m.To.Reg = x86.REG_DX
 		}
 
+	case ssa.OpAMD64MULQU2:
+		// Arg[0] is already in AX as it's the only register we allow
+		// results hi in DX, lo in AX
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+
+	case ssa.OpAMD64DIVQU2:
+		// Arg[0], Arg[1] are already in Dx, AX, as they're the only registers we allow
+		// results q in AX, r in DX
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[2].Reg()
+
 	case ssa.OpAMD64AVGQU:
 		// compute (x+y)/2 unsigned.
 		// Do a 64-bit add, the overflow goes into the carry.
 		// Shift right once and pull the carry back into the 63rd bit.
-		r := gc.SSARegNum(v)
-		if r != gc.SSARegNum(v.Args[0]) {
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
 			v.Fatalf("input[0] and output not in same register %s", v.LongString())
 		}
 		p := gc.Prog(x86.AADDQ)
 		p.From.Type = obj.TYPE_REG
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = r
-		p.From.Reg = gc.SSARegNum(v.Args[1])
+		p.From.Reg = v.Args[1].Reg()
 		p = gc.Prog(x86.ARCRQ)
 		p.From.Type = obj.TYPE_CONST
 		p.From.Offset = 1
@@ -365,8 +337,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		p.To.Reg = r
 
 	case ssa.OpAMD64ADDQconst, ssa.OpAMD64ADDLconst:
-		r := gc.SSARegNum(v)
-		a := gc.SSARegNum(v.Args[0])
+		r := v.Reg()
+		a := v.Args[0].Reg()
 		if r == a {
 			if v.AuxInt == 1 {
 				var asm obj.As
@@ -417,29 +389,20 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = r
 
-	case ssa.OpAMD64CMOVQEQconst, ssa.OpAMD64CMOVLEQconst, ssa.OpAMD64CMOVWEQconst,
-		ssa.OpAMD64CMOVQNEconst, ssa.OpAMD64CMOVLNEconst, ssa.OpAMD64CMOVWNEconst:
-		r := gc.SSARegNum(v)
-		if r != gc.SSARegNum(v.Args[0]) {
+	case ssa.OpAMD64CMOVQEQ, ssa.OpAMD64CMOVLEQ:
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
 			v.Fatalf("input[0] and output not in same register %s", v.LongString())
 		}
-
-		// Constant into AX
-		p := gc.Prog(moveByType(v.Type))
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = v.AuxInt
-		p.To.Type = obj.TYPE_REG
-		p.To.Reg = x86.REG_AX
-
-		p = gc.Prog(v.Op.Asm())
+		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = x86.REG_AX
+		p.From.Reg = v.Args[1].Reg()
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = r
 
 	case ssa.OpAMD64MULQconst, ssa.OpAMD64MULLconst:
-		r := gc.SSARegNum(v)
-		if r != gc.SSARegNum(v.Args[0]) {
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
 			v.Fatalf("input[0] and output not in same register %s", v.LongString())
 		}
 		p := gc.Prog(v.Op.Asm())
@@ -451,7 +414,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		// then we don't need to use resultInArg0 for these ops.
 		//p.From3 = new(obj.Addr)
 		//p.From3.Type = obj.TYPE_REG
-		//p.From3.Reg = gc.SSARegNum(v.Args[0])
+		//p.From3.Reg = v.Args[0].Reg()
 
 	case ssa.OpAMD64SUBQconst, ssa.OpAMD64SUBLconst,
 		ssa.OpAMD64ANDQconst, ssa.OpAMD64ANDLconst,
@@ -461,8 +424,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		ssa.OpAMD64SHRQconst, ssa.OpAMD64SHRLconst, ssa.OpAMD64SHRWconst, ssa.OpAMD64SHRBconst,
 		ssa.OpAMD64SARQconst, ssa.OpAMD64SARLconst, ssa.OpAMD64SARWconst, ssa.OpAMD64SARBconst,
 		ssa.OpAMD64ROLQconst, ssa.OpAMD64ROLLconst, ssa.OpAMD64ROLWconst, ssa.OpAMD64ROLBconst:
-		r := gc.SSARegNum(v)
-		if r != gc.SSARegNum(v.Args[0]) {
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
 			v.Fatalf("input[0] and output not in same register %s", v.LongString())
 		}
 		p := gc.Prog(v.Op.Asm())
@@ -471,15 +434,15 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = r
 	case ssa.OpAMD64SBBQcarrymask, ssa.OpAMD64SBBLcarrymask:
-		r := gc.SSARegNum(v)
+		r := v.Reg()
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
 		p.From.Reg = r
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = r
 	case ssa.OpAMD64LEAQ1, ssa.OpAMD64LEAQ2, ssa.OpAMD64LEAQ4, ssa.OpAMD64LEAQ8:
-		r := gc.SSARegNum(v.Args[0])
-		i := gc.SSARegNum(v.Args[1])
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
 		p := gc.Prog(x86.ALEAQ)
 		switch v.Op {
 		case ssa.OpAMD64LEAQ1:
@@ -499,25 +462,25 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		p.From.Index = i
 		gc.AddAux(&p.From, v)
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
-	case ssa.OpAMD64LEAQ:
-		p := gc.Prog(x86.ALEAQ)
+		p.To.Reg = v.Reg()
+	case ssa.OpAMD64LEAQ, ssa.OpAMD64LEAL:
+		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_MEM
-		p.From.Reg = gc.SSARegNum(v.Args[0])
+		p.From.Reg = v.Args[0].Reg()
 		gc.AddAux(&p.From, v)
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 	case ssa.OpAMD64CMPQ, ssa.OpAMD64CMPL, ssa.OpAMD64CMPW, ssa.OpAMD64CMPB,
 		ssa.OpAMD64TESTQ, ssa.OpAMD64TESTL, ssa.OpAMD64TESTW, ssa.OpAMD64TESTB:
-		opregreg(v.Op.Asm(), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[0]))
+		opregreg(v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg())
 	case ssa.OpAMD64UCOMISS, ssa.OpAMD64UCOMISD:
 		// Go assembler has swapped operands for UCOMISx relative to CMP,
 		// must account for that right here.
-		opregreg(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1]))
+		opregreg(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg())
 	case ssa.OpAMD64CMPQconst, ssa.OpAMD64CMPLconst, ssa.OpAMD64CMPWconst, ssa.OpAMD64CMPBconst:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[0])
+		p.From.Reg = v.Args[0].Reg()
 		p.To.Type = obj.TYPE_CONST
 		p.To.Offset = v.AuxInt
 	case ssa.OpAMD64TESTQconst, ssa.OpAMD64TESTLconst, ssa.OpAMD64TESTWconst, ssa.OpAMD64TESTBconst:
@@ -525,9 +488,9 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		p.From.Type = obj.TYPE_CONST
 		p.From.Offset = v.AuxInt
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v.Args[0])
+		p.To.Reg = v.Args[0].Reg()
 	case ssa.OpAMD64MOVLconst, ssa.OpAMD64MOVQconst:
-		x := gc.SSARegNum(v)
+		x := v.Reg()
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_CONST
 		p.From.Offset = v.AuxInt
@@ -539,7 +502,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 			p.Mark |= x86.PRESERVEFLAGS
 		}
 	case ssa.OpAMD64MOVSSconst, ssa.OpAMD64MOVSDconst:
-		x := gc.SSARegNum(v)
+		x := v.Reg()
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_FCONST
 		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
@@ -548,40 +511,40 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 	case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVSSload, ssa.OpAMD64MOVSDload, ssa.OpAMD64MOVLload, ssa.OpAMD64MOVWload, ssa.OpAMD64MOVBload, ssa.OpAMD64MOVBQSXload, ssa.OpAMD64MOVWQSXload, ssa.OpAMD64MOVLQSXload, ssa.OpAMD64MOVOload:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_MEM
-		p.From.Reg = gc.SSARegNum(v.Args[0])
+		p.From.Reg = v.Args[0].Reg()
 		gc.AddAux(&p.From, v)
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 	case ssa.OpAMD64MOVQloadidx8, ssa.OpAMD64MOVSDloadidx8:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_MEM
-		p.From.Reg = gc.SSARegNum(v.Args[0])
+		p.From.Reg = v.Args[0].Reg()
 		gc.AddAux(&p.From, v)
 		p.From.Scale = 8
-		p.From.Index = gc.SSARegNum(v.Args[1])
+		p.From.Index = v.Args[1].Reg()
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 	case ssa.OpAMD64MOVLloadidx4, ssa.OpAMD64MOVSSloadidx4:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_MEM
-		p.From.Reg = gc.SSARegNum(v.Args[0])
+		p.From.Reg = v.Args[0].Reg()
 		gc.AddAux(&p.From, v)
 		p.From.Scale = 4
-		p.From.Index = gc.SSARegNum(v.Args[1])
+		p.From.Index = v.Args[1].Reg()
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 	case ssa.OpAMD64MOVWloadidx2:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_MEM
-		p.From.Reg = gc.SSARegNum(v.Args[0])
+		p.From.Reg = v.Args[0].Reg()
 		gc.AddAux(&p.From, v)
 		p.From.Scale = 2
-		p.From.Index = gc.SSARegNum(v.Args[1])
+		p.From.Index = v.Args[1].Reg()
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 	case ssa.OpAMD64MOVBloadidx1, ssa.OpAMD64MOVWloadidx1, ssa.OpAMD64MOVLloadidx1, ssa.OpAMD64MOVQloadidx1, ssa.OpAMD64MOVSSloadidx1, ssa.OpAMD64MOVSDloadidx1:
-		r := gc.SSARegNum(v.Args[0])
-		i := gc.SSARegNum(v.Args[1])
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
 		if i == x86.REG_SP {
 			r, i = i, r
 		}
@@ -592,50 +555,50 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		p.From.Index = i
 		gc.AddAux(&p.From, v)
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 	case ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVSSstore, ssa.OpAMD64MOVSDstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore, ssa.OpAMD64MOVOstore:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[1])
+		p.From.Reg = v.Args[1].Reg()
 		p.To.Type = obj.TYPE_MEM
-		p.To.Reg = gc.SSARegNum(v.Args[0])
+		p.To.Reg = v.Args[0].Reg()
 		gc.AddAux(&p.To, v)
 	case ssa.OpAMD64MOVQstoreidx8, ssa.OpAMD64MOVSDstoreidx8:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[2])
+		p.From.Reg = v.Args[2].Reg()
 		p.To.Type = obj.TYPE_MEM
-		p.To.Reg = gc.SSARegNum(v.Args[0])
+		p.To.Reg = v.Args[0].Reg()
 		p.To.Scale = 8
-		p.To.Index = gc.SSARegNum(v.Args[1])
+		p.To.Index = v.Args[1].Reg()
 		gc.AddAux(&p.To, v)
 	case ssa.OpAMD64MOVSSstoreidx4, ssa.OpAMD64MOVLstoreidx4:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[2])
+		p.From.Reg = v.Args[2].Reg()
 		p.To.Type = obj.TYPE_MEM
-		p.To.Reg = gc.SSARegNum(v.Args[0])
+		p.To.Reg = v.Args[0].Reg()
 		p.To.Scale = 4
-		p.To.Index = gc.SSARegNum(v.Args[1])
+		p.To.Index = v.Args[1].Reg()
 		gc.AddAux(&p.To, v)
 	case ssa.OpAMD64MOVWstoreidx2:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[2])
+		p.From.Reg = v.Args[2].Reg()
 		p.To.Type = obj.TYPE_MEM
-		p.To.Reg = gc.SSARegNum(v.Args[0])
+		p.To.Reg = v.Args[0].Reg()
 		p.To.Scale = 2
-		p.To.Index = gc.SSARegNum(v.Args[1])
+		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:
-		r := gc.SSARegNum(v.Args[0])
-		i := gc.SSARegNum(v.Args[1])
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
 		if i == x86.REG_SP {
 			r, i = i, r
 		}
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[2])
+		p.From.Reg = v.Args[2].Reg()
 		p.To.Type = obj.TYPE_MEM
 		p.To.Reg = r
 		p.To.Scale = 1
@@ -647,15 +610,15 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		sc := v.AuxValAndOff()
 		p.From.Offset = sc.Val()
 		p.To.Type = obj.TYPE_MEM
-		p.To.Reg = gc.SSARegNum(v.Args[0])
+		p.To.Reg = v.Args[0].Reg()
 		gc.AddAux2(&p.To, v, sc.Off())
 	case ssa.OpAMD64MOVQstoreconstidx1, ssa.OpAMD64MOVQstoreconstidx8, ssa.OpAMD64MOVLstoreconstidx1, ssa.OpAMD64MOVLstoreconstidx4, ssa.OpAMD64MOVWstoreconstidx1, ssa.OpAMD64MOVWstoreconstidx2, ssa.OpAMD64MOVBstoreconstidx1:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_CONST
 		sc := v.AuxValAndOff()
 		p.From.Offset = sc.Val()
-		r := gc.SSARegNum(v.Args[0])
-		i := gc.SSARegNum(v.Args[1])
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
 		switch v.Op {
 		case ssa.OpAMD64MOVBstoreconstidx1, ssa.OpAMD64MOVWstoreconstidx1, ssa.OpAMD64MOVLstoreconstidx1, ssa.OpAMD64MOVQstoreconstidx1:
 			p.To.Scale = 1
@@ -674,10 +637,14 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		p.To.Index = i
 		gc.AddAux2(&p.To, v, sc.Off())
 	case ssa.OpAMD64MOVLQSX, ssa.OpAMD64MOVWQSX, ssa.OpAMD64MOVBQSX, ssa.OpAMD64MOVLQZX, ssa.OpAMD64MOVWQZX, ssa.OpAMD64MOVBQZX,
-		ssa.OpAMD64CVTSL2SS, ssa.OpAMD64CVTSL2SD, ssa.OpAMD64CVTSQ2SS, ssa.OpAMD64CVTSQ2SD,
 		ssa.OpAMD64CVTTSS2SL, ssa.OpAMD64CVTTSD2SL, ssa.OpAMD64CVTTSS2SQ, ssa.OpAMD64CVTTSD2SQ,
 		ssa.OpAMD64CVTSS2SD, ssa.OpAMD64CVTSD2SS:
-		opregreg(v.Op.Asm(), gc.SSARegNum(v), gc.SSARegNum(v.Args[0]))
+		opregreg(v.Op.Asm(), v.Reg(), v.Args[0].Reg())
+	case ssa.OpAMD64CVTSL2SD, ssa.OpAMD64CVTSQ2SD, ssa.OpAMD64CVTSQ2SS, ssa.OpAMD64CVTSL2SS:
+		r := v.Reg()
+		// Break false dependency on destination register.
+		opregreg(x86.AXORPS, r, r)
+		opregreg(v.Op.Asm(), r, v.Args[0].Reg())
 	case ssa.OpAMD64DUFFZERO:
 		off := duffStart(v.AuxInt)
 		adj := duffAdj(v.AuxInt)
@@ -695,9 +662,9 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		p.To.Offset = off
 	case ssa.OpAMD64MOVOconst:
 		if v.AuxInt != 0 {
-			v.Unimplementedf("MOVOconst can only do constant=0")
+			v.Fatalf("MOVOconst can only do constant=0")
 		}
-		r := gc.SSARegNum(v)
+		r := v.Reg()
 		opregreg(x86.AXORPS, r, r)
 	case ssa.OpAMD64DUFFCOPY:
 		p := gc.Prog(obj.ADUFFCOPY)
@@ -705,78 +672,45 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg))
 		p.To.Offset = v.AuxInt
 
-	case ssa.OpCopy, ssa.OpAMD64MOVQconvert: // TODO: use MOVQreg for reg->reg copies instead of OpCopy?
+	case ssa.OpCopy, ssa.OpAMD64MOVQconvert, ssa.OpAMD64MOVLconvert: // TODO: use MOVQreg for reg->reg copies instead of OpCopy?
 		if v.Type.IsMemory() {
 			return
 		}
-		x := gc.SSARegNum(v.Args[0])
-		y := gc.SSARegNum(v)
+		x := v.Args[0].Reg()
+		y := v.Reg()
 		if x != y {
 			opregreg(moveByType(v.Type), y, x)
 		}
 	case ssa.OpLoadReg:
 		if v.Type.IsFlags() {
-			v.Unimplementedf("load flags not implemented: %v", v.LongString())
+			v.Fatalf("load flags not implemented: %v", v.LongString())
 			return
 		}
 		p := gc.Prog(loadByType(v.Type))
-		n, off := gc.AutoVar(v.Args[0])
-		p.From.Type = obj.TYPE_MEM
-		p.From.Node = n
-		p.From.Sym = gc.Linksym(n.Sym)
-		p.From.Offset = off
-		if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT {
-			p.From.Name = obj.NAME_PARAM
-			p.From.Offset += n.Xoffset
-		} else {
-			p.From.Name = obj.NAME_AUTO
-		}
+		gc.AddrAuto(&p.From, v.Args[0])
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 
 	case ssa.OpStoreReg:
 		if v.Type.IsFlags() {
-			v.Unimplementedf("store flags not implemented: %v", v.LongString())
+			v.Fatalf("store flags not implemented: %v", v.LongString())
 			return
 		}
 		p := gc.Prog(storeByType(v.Type))
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[0])
-		n, off := gc.AutoVar(v)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Node = n
-		p.To.Sym = gc.Linksym(n.Sym)
-		p.To.Offset = off
-		if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT {
-			p.To.Name = obj.NAME_PARAM
-			p.To.Offset += n.Xoffset
-		} else {
-			p.To.Name = obj.NAME_AUTO
-		}
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddrAuto(&p.To, v)
 	case ssa.OpPhi:
-		// just check to make sure regalloc and stackalloc did it right
-		if v.Type.IsMemory() {
-			return
-		}
-		f := v.Block.Func
-		loc := f.RegAlloc[v.ID]
-		for _, a := range v.Args {
-			if aloc := f.RegAlloc[a.ID]; aloc != loc { // TODO: .Equal() instead?
-				v.Fatalf("phi arg at different location than phi: %v @ %v, but arg %v @ %v\n%s\n", v, loc, a, aloc, v.Block.Func)
-			}
-		}
+		gc.CheckLoweredPhi(v)
 	case ssa.OpInitMem:
 		// memory arg needs no code
 	case ssa.OpArg:
 		// input args need no code
 	case ssa.OpAMD64LoweredGetClosurePtr:
-		// Output is hardwired to DX only,
-		// and DX contains the closure pointer on
-		// closure entry, and this "instruction"
-		// is scheduled to the very beginning
-		// of the entry block.
+		// Closure pointer is DX.
+		gc.CheckLoweredGetClosurePtr(v)
 	case ssa.OpAMD64LoweredGetG:
-		r := gc.SSARegNum(v)
+		r := v.Reg()
 		// See the comments in cmd/internal/obj/x86/obj6.go
 		// near CanUse1InsnTLS for a detailed explanation of these instructions.
 		if x86.CanUse1InsnTLS(gc.Ctxt) {
@@ -824,7 +758,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 	case ssa.OpAMD64CALLclosure:
 		p := gc.Prog(obj.ACALL)
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v.Args[0])
+		p.To.Reg = v.Args[0].Reg()
 		if gc.Maxarg < v.AuxInt {
 			gc.Maxarg = v.AuxInt
 		}
@@ -847,30 +781,36 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 	case ssa.OpAMD64CALLinter:
 		p := gc.Prog(obj.ACALL)
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v.Args[0])
+		p.To.Reg = v.Args[0].Reg()
 		if gc.Maxarg < v.AuxInt {
 			gc.Maxarg = v.AuxInt
 		}
 	case ssa.OpAMD64NEGQ, ssa.OpAMD64NEGL,
 		ssa.OpAMD64BSWAPQ, ssa.OpAMD64BSWAPL,
 		ssa.OpAMD64NOTQ, ssa.OpAMD64NOTL:
-		r := gc.SSARegNum(v)
-		if r != gc.SSARegNum(v.Args[0]) {
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
 			v.Fatalf("input[0] and output not in same register %s", v.LongString())
 		}
 		p := gc.Prog(v.Op.Asm())
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = r
-	case ssa.OpAMD64BSFQ, ssa.OpAMD64BSFL, ssa.OpAMD64BSFW,
-		ssa.OpAMD64BSRQ, ssa.OpAMD64BSRL, ssa.OpAMD64BSRW,
-		ssa.OpAMD64SQRTSD:
+	case ssa.OpAMD64BSFQ, ssa.OpAMD64BSFL:
+		p := gc.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.OpAMD64SQRTSD:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[0])
+		p.From.Reg = v.Args[0].Reg()
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 	case ssa.OpSP, ssa.OpSB:
 		// nothing to do
+	case ssa.OpSelect0, ssa.OpSelect1:
+		// nothing to do
 	case ssa.OpAMD64SETEQ, ssa.OpAMD64SETNE,
 		ssa.OpAMD64SETL, ssa.OpAMD64SETLE,
 		ssa.OpAMD64SETG, ssa.OpAMD64SETGE,
@@ -880,32 +820,34 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		ssa.OpAMD64SETA, ssa.OpAMD64SETAE:
 		p := gc.Prog(v.Op.Asm())
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 
 	case ssa.OpAMD64SETNEF:
 		p := gc.Prog(v.Op.Asm())
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 		q := gc.Prog(x86.ASETPS)
 		q.To.Type = obj.TYPE_REG
 		q.To.Reg = x86.REG_AX
 		// ORL avoids partial register write and is smaller than ORQ, used by old compiler
-		opregreg(x86.AORL, gc.SSARegNum(v), x86.REG_AX)
+		opregreg(x86.AORL, v.Reg(), x86.REG_AX)
 
 	case ssa.OpAMD64SETEQF:
 		p := gc.Prog(v.Op.Asm())
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg()
 		q := gc.Prog(x86.ASETPC)
 		q.To.Type = obj.TYPE_REG
 		q.To.Reg = x86.REG_AX
 		// ANDL avoids partial register write and is smaller than ANDQ, used by old compiler
-		opregreg(x86.AANDL, gc.SSARegNum(v), x86.REG_AX)
+		opregreg(x86.AANDL, v.Reg(), x86.REG_AX)
 
 	case ssa.OpAMD64InvertFlags:
 		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
 	case ssa.OpAMD64FlagEQ, ssa.OpAMD64FlagLT_ULT, ssa.OpAMD64FlagLT_UGT, ssa.OpAMD64FlagGT_ULT, ssa.OpAMD64FlagGT_UGT:
 		v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
+	case ssa.OpAMD64AddTupleFirst32, ssa.OpAMD64AddTupleFirst64:
+		v.Fatalf("AddTupleFirst* should never make it to codegen %v", v.LongString())
 	case ssa.OpAMD64REPSTOSQ:
 		gc.Prog(x86.AREP)
 		gc.Prog(x86.ASTOSQ)
@@ -919,66 +861,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 	case ssa.OpVarLive:
 		gc.Gvarlive(v.Aux.(*gc.Node))
 	case ssa.OpKeepAlive:
-		if !v.Args[0].Type.IsPtrShaped() {
-			v.Fatalf("keeping non-pointer alive %v", v.Args[0])
-		}
-		n, off := gc.AutoVar(v.Args[0])
-		if n == nil {
-			v.Fatalf("KeepLive with non-spilled value %s %s", v, v.Args[0])
-		}
-		if off != 0 {
-			v.Fatalf("KeepLive with non-zero offset spill location %s:%d", n, off)
-		}
-		gc.Gvarlive(n)
+		gc.KeepAlive(v)
 	case ssa.OpAMD64LoweredNilCheck:
-		// Optimization - if the subsequent block has a load or store
-		// at the same address, we don't need to issue this instruction.
-		mem := v.Args[1]
-		for _, w := range v.Block.Succs[0].Block().Values {
-			if w.Op == ssa.OpPhi {
-				if w.Type.IsMemory() {
-					mem = w
-				}
-				continue
-			}
-			if len(w.Args) == 0 || !w.Args[len(w.Args)-1].Type.IsMemory() {
-				// w doesn't use a store - can't be a memory op.
-				continue
-			}
-			if w.Args[len(w.Args)-1] != mem {
-				v.Fatalf("wrong store after nilcheck v=%s w=%s", v, w)
-			}
-			switch w.Op {
-			case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVLload, ssa.OpAMD64MOVWload, ssa.OpAMD64MOVBload,
-				ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore,
-				ssa.OpAMD64MOVBQSXload, ssa.OpAMD64MOVWQSXload, ssa.OpAMD64MOVLQSXload,
-				ssa.OpAMD64MOVSSload, ssa.OpAMD64MOVSDload, ssa.OpAMD64MOVOload,
-				ssa.OpAMD64MOVSSstore, ssa.OpAMD64MOVSDstore, ssa.OpAMD64MOVOstore:
-				if w.Args[0] == v.Args[0] && w.Aux == nil && w.AuxInt >= 0 && w.AuxInt < minZeroPage {
-					if gc.Debug_checknil != 0 && int(v.Line) > 1 {
-						gc.Warnl(v.Line, "removed nil check")
-					}
-					return
-				}
-			case ssa.OpAMD64MOVQstoreconst, ssa.OpAMD64MOVLstoreconst, ssa.OpAMD64MOVWstoreconst, ssa.OpAMD64MOVBstoreconst:
-				off := ssa.ValAndOff(v.AuxInt).Off()
-				if w.Args[0] == v.Args[0] && w.Aux == nil && off >= 0 && off < minZeroPage {
-					if gc.Debug_checknil != 0 && int(v.Line) > 1 {
-						gc.Warnl(v.Line, "removed nil check")
-					}
-					return
-				}
-			}
-			if w.Type.IsMemory() {
-				if w.Op == ssa.OpVarDef || w.Op == ssa.OpVarKill || w.Op == ssa.OpVarLive {
-					// these ops are OK
-					mem = w
-					continue
-				}
-				// We can't delay the nil check past the next store.
-				break
-			}
-		}
 		// Issue a load which will fault if the input is nil.
 		// TODO: We currently use the 2-byte instruction TESTB AX, (reg).
 		// Should we use the 3-byte TESTB $0, (reg) instead?  It is larger
@@ -989,13 +873,65 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		p.From.Type = obj.TYPE_REG
 		p.From.Reg = x86.REG_AX
 		p.To.Type = obj.TYPE_MEM
-		p.To.Reg = gc.SSARegNum(v.Args[0])
+		p.To.Reg = v.Args[0].Reg()
 		gc.AddAux(&p.To, v)
 		if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
 			gc.Warnl(v.Line, "generated nil check")
 		}
+	case ssa.OpAMD64MOVLatomicload, ssa.OpAMD64MOVQatomicload:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg0()
+	case ssa.OpAMD64XCHGL, ssa.OpAMD64XCHGQ:
+		r := v.Reg0()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output[0] not in same register %s", v.LongString())
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[1].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpAMD64XADDLlock, ssa.OpAMD64XADDQlock:
+		r := v.Reg0()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output[0] not in same register %s", v.LongString())
+		}
+		gc.Prog(x86.ALOCK)
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[1].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpAMD64CMPXCHGLlock, ssa.OpAMD64CMPXCHGQlock:
+		if v.Args[1].Reg() != x86.REG_AX {
+			v.Fatalf("input[1] not in AX %s", v.LongString())
+		}
+		gc.Prog(x86.ALOCK)
+		p := gc.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()
+		gc.AddAux(&p.To, v)
+		p = gc.Prog(x86.ASETEQ)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg0()
+	case ssa.OpAMD64ANDBlock, ssa.OpAMD64ORBlock:
+		gc.Prog(x86.ALOCK)
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
 	default:
-		v.Unimplementedf("genValue not implemented: %s", v.LongString())
+		v.Fatalf("genValue not implemented: %s", v.LongString())
 	}
 }
 
@@ -1029,7 +965,7 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
 	s.SetLineno(b.Line)
 
 	switch b.Kind {
-	case ssa.BlockPlain, ssa.BlockCall, ssa.BlockCheck:
+	case ssa.BlockPlain:
 		if b.Succs[0].Block() != next {
 			p := gc.Prog(obj.AJMP)
 			p.To.Type = obj.TYPE_BRANCH
@@ -1109,6 +1045,6 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
 		}
 
 	default:
-		b.Unimplementedf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
+		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
 	}
 }
diff --git a/src/cmd/compile/internal/arm/cgen.go b/src/cmd/compile/internal/arm/cgen.go
deleted file mode 100644
index c60df08..0000000
--- a/src/cmd/compile/internal/arm/cgen.go
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package arm
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/arm"
-)
-
-/*
- * generate array index into res.
- * n might be any size; res is 32-bit.
- * returns Prog* to patch to panic call.
- */
-func cgenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog {
-	if !gc.Is64(n.Type) {
-		gc.Cgen(n, res)
-		return nil
-	}
-
-	var tmp gc.Node
-	gc.Tempname(&tmp, gc.Types[gc.TINT64])
-	gc.Cgen(n, &tmp)
-	var lo gc.Node
-	var hi gc.Node
-	split64(&tmp, &lo, &hi)
-	gmove(&lo, res)
-	if bounded {
-		splitclean()
-		return nil
-	}
-
-	var n1 gc.Node
-	gc.Regalloc(&n1, gc.Types[gc.TINT32], nil)
-	var n2 gc.Node
-	gc.Regalloc(&n2, gc.Types[gc.TINT32], nil)
-	var zero gc.Node
-	gc.Nodconst(&zero, gc.Types[gc.TINT32], 0)
-	gmove(&hi, &n1)
-	gmove(&zero, &n2)
-	gins(arm.ACMP, &n1, &n2)
-	gc.Regfree(&n2)
-	gc.Regfree(&n1)
-	splitclean()
-	return gc.Gbranch(arm.ABNE, nil, -1)
-}
-
-func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog {
-	gc.Tempname(res, n.Type)
-	return cgenindex(n, res, bounded)
-}
-
-func blockcopy(n, res *gc.Node, osrc, odst, w int64) {
-	// determine alignment.
-	// want to avoid unaligned access, so have to use
-	// smaller operations for less aligned types.
-	// for example moving [4]byte must use 4 MOVB not 1 MOVW.
-	align := int(n.Type.Align)
-
-	var op obj.As
-	switch align {
-	default:
-		gc.Fatalf("sgen: invalid alignment %d for %v", align, n.Type)
-
-	case 1:
-		op = arm.AMOVB
-
-	case 2:
-		op = arm.AMOVH
-
-	case 4:
-		op = arm.AMOVW
-	}
-
-	if w%int64(align) != 0 {
-		gc.Fatalf("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type)
-	}
-	c := int32(w / int64(align))
-
-	if osrc%int64(align) != 0 || odst%int64(align) != 0 {
-		gc.Fatalf("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align)
-	}
-
-	// if we are copying forward on the stack and
-	// the src and dst overlap, then reverse direction
-	dir := align
-	if osrc < odst && odst < osrc+w {
-		dir = -dir
-	}
-
-	if op == arm.AMOVW && !gc.Nacl && dir > 0 && c >= 4 && c <= 128 {
-		var r0 gc.Node
-		r0.Op = gc.OREGISTER
-		r0.Reg = arm.REG_R0
-		var r1 gc.Node
-		r1.Op = gc.OREGISTER
-		r1.Reg = arm.REG_R0 + 1
-		var r2 gc.Node
-		r2.Op = gc.OREGISTER
-		r2.Reg = arm.REG_R0 + 2
-
-		var src gc.Node
-		gc.Regalloc(&src, gc.Types[gc.Tptr], &r1)
-		var dst gc.Node
-		gc.Regalloc(&dst, gc.Types[gc.Tptr], &r2)
-		if n.Ullman >= res.Ullman {
-			// eval n first
-			gc.Agen(n, &src)
-
-			if res.Op == gc.ONAME {
-				gc.Gvardef(res)
-			}
-			gc.Agen(res, &dst)
-		} else {
-			// eval res first
-			if res.Op == gc.ONAME {
-				gc.Gvardef(res)
-			}
-			gc.Agen(res, &dst)
-			gc.Agen(n, &src)
-		}
-
-		var tmp gc.Node
-		gc.Regalloc(&tmp, gc.Types[gc.Tptr], &r0)
-		f := gc.Sysfunc("duffcopy")
-		p := gins(obj.ADUFFCOPY, nil, f)
-		gc.Afunclit(&p.To, f)
-
-		// 8 and 128 = magic constants: see ../../runtime/asm_arm.s
-		p.To.Offset = 8 * (128 - int64(c))
-
-		gc.Regfree(&tmp)
-		gc.Regfree(&src)
-		gc.Regfree(&dst)
-		return
-	}
-
-	var dst gc.Node
-	var src gc.Node
-	if n.Ullman >= res.Ullman {
-		gc.Agenr(n, &dst, res) // temporarily use dst
-		gc.Regalloc(&src, gc.Types[gc.Tptr], nil)
-		gins(arm.AMOVW, &dst, &src)
-		if res.Op == gc.ONAME {
-			gc.Gvardef(res)
-		}
-		gc.Agen(res, &dst)
-	} else {
-		if res.Op == gc.ONAME {
-			gc.Gvardef(res)
-		}
-		gc.Agenr(res, &dst, res)
-		gc.Agenr(n, &src, nil)
-	}
-
-	var tmp gc.Node
-	gc.Regalloc(&tmp, gc.Types[gc.TUINT32], nil)
-
-	// set up end marker
-	var nend gc.Node
-
-	if c >= 4 {
-		gc.Regalloc(&nend, gc.Types[gc.TUINT32], nil)
-
-		p := gins(arm.AMOVW, &src, &nend)
-		p.From.Type = obj.TYPE_ADDR
-		if dir < 0 {
-			p.From.Offset = int64(dir)
-		} else {
-			p.From.Offset = w
-		}
-	}
-
-	// move src and dest to the end of block if necessary
-	if dir < 0 {
-		p := gins(arm.AMOVW, &src, &src)
-		p.From.Type = obj.TYPE_ADDR
-		p.From.Offset = w + int64(dir)
-
-		p = gins(arm.AMOVW, &dst, &dst)
-		p.From.Type = obj.TYPE_ADDR
-		p.From.Offset = w + int64(dir)
-	}
-
-	// move
-	if c >= 4 {
-		p := gins(op, &src, &tmp)
-		p.From.Type = obj.TYPE_MEM
-		p.From.Offset = int64(dir)
-		p.Scond |= arm.C_PBIT
-		ploop := p
-
-		p = gins(op, &tmp, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = int64(dir)
-		p.Scond |= arm.C_PBIT
-
-		p = gins(arm.ACMP, &src, nil)
-		raddr(&nend, p)
-
-		gc.Patch(gc.Gbranch(arm.ABNE, nil, 0), ploop)
-		gc.Regfree(&nend)
-	} else {
-		var p *obj.Prog
-		for ; c > 0; c-- {
-			p = gins(op, &src, &tmp)
-			p.From.Type = obj.TYPE_MEM
-			p.From.Offset = int64(dir)
-			p.Scond |= arm.C_PBIT
-
-			p = gins(op, &tmp, &dst)
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = int64(dir)
-			p.Scond |= arm.C_PBIT
-		}
-	}
-
-	gc.Regfree(&dst)
-	gc.Regfree(&src)
-	gc.Regfree(&tmp)
-}
diff --git a/src/cmd/compile/internal/arm/cgen64.go b/src/cmd/compile/internal/arm/cgen64.go
deleted file mode 100644
index 33e8406..0000000
--- a/src/cmd/compile/internal/arm/cgen64.go
+++ /dev/null
@@ -1,859 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package arm
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/arm"
-)
-
-/*
- * attempt to generate 64-bit
- *	res = n
- * return 1 on success, 0 if op not handled.
- */
-func cgen64(n *gc.Node, res *gc.Node) {
-	if res.Op != gc.OINDREG && res.Op != gc.ONAME {
-		gc.Dump("n", n)
-		gc.Dump("res", res)
-		gc.Fatalf("cgen64 %v of %v", n.Op, res.Op)
-	}
-
-	l := n.Left
-	var t1 gc.Node
-	if !l.Addable {
-		gc.Tempname(&t1, l.Type)
-		gc.Cgen(l, &t1)
-		l = &t1
-	}
-
-	var hi1 gc.Node
-	var lo1 gc.Node
-	split64(l, &lo1, &hi1)
-	switch n.Op {
-	default:
-		gc.Fatalf("cgen64 %v", n.Op)
-
-	case gc.OMINUS:
-		var lo2 gc.Node
-		var hi2 gc.Node
-		split64(res, &lo2, &hi2)
-
-		gc.Regalloc(&t1, lo1.Type, nil)
-		var al gc.Node
-		gc.Regalloc(&al, lo1.Type, nil)
-		var ah gc.Node
-		gc.Regalloc(&ah, hi1.Type, nil)
-
-		gins(arm.AMOVW, &lo1, &al)
-		gins(arm.AMOVW, &hi1, &ah)
-
-		gmove(ncon(0), &t1)
-		p1 := gins(arm.ASUB, &al, &t1)
-		p1.Scond |= arm.C_SBIT
-		gins(arm.AMOVW, &t1, &lo2)
-
-		gmove(ncon(0), &t1)
-		gins(arm.ASBC, &ah, &t1)
-		gins(arm.AMOVW, &t1, &hi2)
-
-		gc.Regfree(&t1)
-		gc.Regfree(&al)
-		gc.Regfree(&ah)
-		splitclean()
-		splitclean()
-		return
-
-	case gc.OCOM:
-		gc.Regalloc(&t1, lo1.Type, nil)
-		gmove(ncon(^uint32(0)), &t1)
-
-		var lo2 gc.Node
-		var hi2 gc.Node
-		split64(res, &lo2, &hi2)
-		var n1 gc.Node
-		gc.Regalloc(&n1, lo1.Type, nil)
-
-		gins(arm.AMOVW, &lo1, &n1)
-		gins(arm.AEOR, &t1, &n1)
-		gins(arm.AMOVW, &n1, &lo2)
-
-		gins(arm.AMOVW, &hi1, &n1)
-		gins(arm.AEOR, &t1, &n1)
-		gins(arm.AMOVW, &n1, &hi2)
-
-		gc.Regfree(&t1)
-		gc.Regfree(&n1)
-		splitclean()
-		splitclean()
-		return
-
-		// binary operators.
-	// common setup below.
-	case gc.OADD,
-		gc.OSUB,
-		gc.OMUL,
-		gc.OLSH,
-		gc.ORSH,
-		gc.OAND,
-		gc.OOR,
-		gc.OXOR,
-		gc.OLROT:
-		break
-	}
-
-	// setup for binary operators
-	r := n.Right
-
-	if r != nil && !r.Addable {
-		var t2 gc.Node
-		gc.Tempname(&t2, r.Type)
-		gc.Cgen(r, &t2)
-		r = &t2
-	}
-
-	var hi2 gc.Node
-	var lo2 gc.Node
-	if gc.Is64(r.Type) {
-		split64(r, &lo2, &hi2)
-	}
-
-	var al gc.Node
-	gc.Regalloc(&al, lo1.Type, nil)
-	var ah gc.Node
-	gc.Regalloc(&ah, hi1.Type, nil)
-
-	// Do op. Leave result in ah:al.
-	switch n.Op {
-	default:
-		gc.Fatalf("cgen64: not implemented: %v\n", n)
-
-		// TODO: Constants
-	case gc.OADD:
-		var bl gc.Node
-		gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil)
-
-		var bh gc.Node
-		gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil)
-		gins(arm.AMOVW, &hi1, &ah)
-		gins(arm.AMOVW, &lo1, &al)
-		gins(arm.AMOVW, &hi2, &bh)
-		gins(arm.AMOVW, &lo2, &bl)
-		p1 := gins(arm.AADD, &bl, &al)
-		p1.Scond |= arm.C_SBIT
-		gins(arm.AADC, &bh, &ah)
-		gc.Regfree(&bl)
-		gc.Regfree(&bh)
-
-		// TODO: Constants.
-	case gc.OSUB:
-		var bl gc.Node
-		gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil)
-
-		var bh gc.Node
-		gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil)
-		gins(arm.AMOVW, &lo1, &al)
-		gins(arm.AMOVW, &hi1, &ah)
-		gins(arm.AMOVW, &lo2, &bl)
-		gins(arm.AMOVW, &hi2, &bh)
-		p1 := gins(arm.ASUB, &bl, &al)
-		p1.Scond |= arm.C_SBIT
-		gins(arm.ASBC, &bh, &ah)
-		gc.Regfree(&bl)
-		gc.Regfree(&bh)
-
-		// TODO(kaib): this can be done with 4 regs and does not need 6
-	case gc.OMUL:
-		var bl gc.Node
-		gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil)
-
-		var bh gc.Node
-		gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil)
-		var cl gc.Node
-		gc.Regalloc(&cl, gc.Types[gc.TPTR32], nil)
-		var ch gc.Node
-		gc.Regalloc(&ch, gc.Types[gc.TPTR32], nil)
-
-		// load args into bh:bl and bh:bl.
-		gins(arm.AMOVW, &hi1, &bh)
-
-		gins(arm.AMOVW, &lo1, &bl)
-		gins(arm.AMOVW, &hi2, &ch)
-		gins(arm.AMOVW, &lo2, &cl)
-
-		// bl * cl -> ah al
-		p1 := gins(arm.AMULLU, nil, nil)
-
-		p1.From.Type = obj.TYPE_REG
-		p1.From.Reg = bl.Reg
-		p1.Reg = cl.Reg
-		p1.To.Type = obj.TYPE_REGREG
-		p1.To.Reg = ah.Reg
-		p1.To.Offset = int64(al.Reg)
-
-		//print("%v\n", p1);
-
-		// bl * ch + ah -> ah
-		p1 = gins(arm.AMULA, nil, nil)
-
-		p1.From.Type = obj.TYPE_REG
-		p1.From.Reg = bl.Reg
-		p1.Reg = ch.Reg
-		p1.To.Type = obj.TYPE_REGREG2
-		p1.To.Reg = ah.Reg
-		p1.To.Offset = int64(ah.Reg)
-
-		//print("%v\n", p1);
-
-		// bh * cl + ah -> ah
-		p1 = gins(arm.AMULA, nil, nil)
-
-		p1.From.Type = obj.TYPE_REG
-		p1.From.Reg = bh.Reg
-		p1.Reg = cl.Reg
-		p1.To.Type = obj.TYPE_REGREG2
-		p1.To.Reg = ah.Reg
-		p1.To.Offset = int64(ah.Reg)
-
-		//print("%v\n", p1);
-
-		gc.Regfree(&bh)
-
-		gc.Regfree(&bl)
-		gc.Regfree(&ch)
-		gc.Regfree(&cl)
-
-		// We only rotate by a constant c in [0,64).
-	// if c >= 32:
-	//	lo, hi = hi, lo
-	//	c -= 32
-	// if c == 0:
-	//	no-op
-	// else:
-	//	t = hi
-	//	shld hi:lo, c
-	//	shld lo:t, c
-	case gc.OLROT:
-		v := uint64(r.Int64())
-
-		var bl gc.Node
-		gc.Regalloc(&bl, lo1.Type, nil)
-		var bh gc.Node
-		gc.Regalloc(&bh, hi1.Type, nil)
-		if v >= 32 {
-			// reverse during load to do the first 32 bits of rotate
-			v -= 32
-
-			gins(arm.AMOVW, &hi1, &bl)
-			gins(arm.AMOVW, &lo1, &bh)
-		} else {
-			gins(arm.AMOVW, &hi1, &bh)
-			gins(arm.AMOVW, &lo1, &bl)
-		}
-
-		if v == 0 {
-			gins(arm.AMOVW, &bh, &ah)
-			gins(arm.AMOVW, &bl, &al)
-		} else {
-			// rotate by 1 <= v <= 31
-			//	MOVW	bl<<v, al
-			//	MOVW	bh<<v, ah
-			//	OR		bl>>(32-v), ah
-			//	OR		bh>>(32-v), al
-			gshift(arm.AMOVW, &bl, arm.SHIFT_LL, int32(v), &al)
-
-			gshift(arm.AMOVW, &bh, arm.SHIFT_LL, int32(v), &ah)
-			gshift(arm.AORR, &bl, arm.SHIFT_LR, int32(32-v), &ah)
-			gshift(arm.AORR, &bh, arm.SHIFT_LR, int32(32-v), &al)
-		}
-
-		gc.Regfree(&bl)
-		gc.Regfree(&bh)
-
-	case gc.OLSH:
-		var bl gc.Node
-		gc.Regalloc(&bl, lo1.Type, nil)
-		var bh gc.Node
-		gc.Regalloc(&bh, hi1.Type, nil)
-		gins(arm.AMOVW, &hi1, &bh)
-		gins(arm.AMOVW, &lo1, &bl)
-
-		var p6 *obj.Prog
-		var s gc.Node
-		var n1 gc.Node
-		var creg gc.Node
-		var p1 *obj.Prog
-		var p2 *obj.Prog
-		var p3 *obj.Prog
-		var p4 *obj.Prog
-		var p5 *obj.Prog
-		if r.Op == gc.OLITERAL {
-			v := uint64(r.Int64())
-			if v >= 64 {
-				// TODO(kaib): replace with gins(AMOVW, nodintconst(0), &al)
-				// here and below (verify it optimizes to EOR)
-				gins(arm.AEOR, &al, &al)
-
-				gins(arm.AEOR, &ah, &ah)
-			} else if v > 32 {
-				gins(arm.AEOR, &al, &al)
-
-				//	MOVW	bl<<(v-32), ah
-				gshift(arm.AMOVW, &bl, arm.SHIFT_LL, int32(v-32), &ah)
-			} else if v == 32 {
-				gins(arm.AEOR, &al, &al)
-				gins(arm.AMOVW, &bl, &ah)
-			} else if v > 0 {
-				//	MOVW	bl<<v, al
-				gshift(arm.AMOVW, &bl, arm.SHIFT_LL, int32(v), &al)
-
-				//	MOVW	bh<<v, ah
-				gshift(arm.AMOVW, &bh, arm.SHIFT_LL, int32(v), &ah)
-
-				//	OR		bl>>(32-v), ah
-				gshift(arm.AORR, &bl, arm.SHIFT_LR, int32(32-v), &ah)
-			} else {
-				gins(arm.AMOVW, &bl, &al)
-				gins(arm.AMOVW, &bh, &ah)
-			}
-
-			goto olsh_break
-		}
-
-		gc.Regalloc(&s, gc.Types[gc.TUINT32], nil)
-		gc.Regalloc(&creg, gc.Types[gc.TUINT32], nil)
-		if gc.Is64(r.Type) {
-			// shift is >= 1<<32
-			var cl gc.Node
-			var ch gc.Node
-			split64(r, &cl, &ch)
-
-			gmove(&ch, &s)
-			gins(arm.ATST, &s, nil)
-			p6 = gc.Gbranch(arm.ABNE, nil, 0)
-			gmove(&cl, &s)
-			splitclean()
-		} else {
-			gmove(r, &s)
-			p6 = nil
-		}
-
-		gins(arm.ATST, &s, nil)
-
-		// shift == 0
-		p1 = gins(arm.AMOVW, &bl, &al)
-
-		p1.Scond = arm.C_SCOND_EQ
-		p1 = gins(arm.AMOVW, &bh, &ah)
-		p1.Scond = arm.C_SCOND_EQ
-		p2 = gc.Gbranch(arm.ABEQ, nil, 0)
-
-		// shift is < 32
-		gc.Nodconst(&n1, gc.Types[gc.TUINT32], 32)
-
-		gmove(&n1, &creg)
-		gins(arm.ACMP, &s, &creg)
-
-		//	MOVW.LO		bl<<s, al
-		p1 = gregshift(arm.AMOVW, &bl, arm.SHIFT_LL, &s, &al)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	MOVW.LO		bh<<s, ah
-		p1 = gregshift(arm.AMOVW, &bh, arm.SHIFT_LL, &s, &ah)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	SUB.LO		s, creg
-		p1 = gins(arm.ASUB, &s, &creg)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	OR.LO		bl>>creg, ah
-		p1 = gregshift(arm.AORR, &bl, arm.SHIFT_LR, &creg, &ah)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	BLO	end
-		p3 = gc.Gbranch(arm.ABLO, nil, 0)
-
-		// shift == 32
-		p1 = gins(arm.AEOR, &al, &al)
-
-		p1.Scond = arm.C_SCOND_EQ
-		p1 = gins(arm.AMOVW, &bl, &ah)
-		p1.Scond = arm.C_SCOND_EQ
-		p4 = gc.Gbranch(arm.ABEQ, nil, 0)
-
-		// shift is < 64
-		gc.Nodconst(&n1, gc.Types[gc.TUINT32], 64)
-
-		gmove(&n1, &creg)
-		gins(arm.ACMP, &s, &creg)
-
-		//	EOR.LO	al, al
-		p1 = gins(arm.AEOR, &al, &al)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	MOVW.LO		creg>>1, creg
-		p1 = gshift(arm.AMOVW, &creg, arm.SHIFT_LR, 1, &creg)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	SUB.LO		creg, s
-		p1 = gins(arm.ASUB, &creg, &s)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	MOVW	bl<<s, ah
-		p1 = gregshift(arm.AMOVW, &bl, arm.SHIFT_LL, &s, &ah)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		p5 = gc.Gbranch(arm.ABLO, nil, 0)
-
-		// shift >= 64
-		if p6 != nil {
-			gc.Patch(p6, gc.Pc)
-		}
-		gins(arm.AEOR, &al, &al)
-		gins(arm.AEOR, &ah, &ah)
-
-		gc.Patch(p2, gc.Pc)
-		gc.Patch(p3, gc.Pc)
-		gc.Patch(p4, gc.Pc)
-		gc.Patch(p5, gc.Pc)
-		gc.Regfree(&s)
-		gc.Regfree(&creg)
-
-	olsh_break:
-		gc.Regfree(&bl)
-		gc.Regfree(&bh)
-
-	case gc.ORSH:
-		var bl gc.Node
-		gc.Regalloc(&bl, lo1.Type, nil)
-		var bh gc.Node
-		gc.Regalloc(&bh, hi1.Type, nil)
-		gins(arm.AMOVW, &hi1, &bh)
-		gins(arm.AMOVW, &lo1, &bl)
-
-		var p4 *obj.Prog
-		var p5 *obj.Prog
-		var n1 gc.Node
-		var p6 *obj.Prog
-		var s gc.Node
-		var p1 *obj.Prog
-		var p2 *obj.Prog
-		var creg gc.Node
-		var p3 *obj.Prog
-		if r.Op == gc.OLITERAL {
-			v := uint64(r.Int64())
-			if v >= 64 {
-				if bh.Type.Etype == gc.TINT32 {
-					//	MOVW	bh->31, al
-					gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &al)
-
-					//	MOVW	bh->31, ah
-					gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah)
-				} else {
-					gins(arm.AEOR, &al, &al)
-					gins(arm.AEOR, &ah, &ah)
-				}
-			} else if v > 32 {
-				if bh.Type.Etype == gc.TINT32 {
-					//	MOVW	bh->(v-32), al
-					gshift(arm.AMOVW, &bh, arm.SHIFT_AR, int32(v-32), &al)
-
-					//	MOVW	bh->31, ah
-					gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah)
-				} else {
-					//	MOVW	bh>>(v-32), al
-					gshift(arm.AMOVW, &bh, arm.SHIFT_LR, int32(v-32), &al)
-
-					gins(arm.AEOR, &ah, &ah)
-				}
-			} else if v == 32 {
-				gins(arm.AMOVW, &bh, &al)
-				if bh.Type.Etype == gc.TINT32 {
-					//	MOVW	bh->31, ah
-					gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah)
-				} else {
-					gins(arm.AEOR, &ah, &ah)
-				}
-			} else if v > 0 {
-				//	MOVW	bl>>v, al
-				gshift(arm.AMOVW, &bl, arm.SHIFT_LR, int32(v), &al)
-
-				//	OR		bh<<(32-v), al
-				gshift(arm.AORR, &bh, arm.SHIFT_LL, int32(32-v), &al)
-
-				if bh.Type.Etype == gc.TINT32 {
-					//	MOVW	bh->v, ah
-					gshift(arm.AMOVW, &bh, arm.SHIFT_AR, int32(v), &ah)
-				} else {
-					//	MOVW	bh>>v, ah
-					gshift(arm.AMOVW, &bh, arm.SHIFT_LR, int32(v), &ah)
-				}
-			} else {
-				gins(arm.AMOVW, &bl, &al)
-				gins(arm.AMOVW, &bh, &ah)
-			}
-
-			goto orsh_break
-		}
-
-		gc.Regalloc(&s, gc.Types[gc.TUINT32], nil)
-		gc.Regalloc(&creg, gc.Types[gc.TUINT32], nil)
-		if gc.Is64(r.Type) {
-			// shift is >= 1<<32
-			var ch gc.Node
-			var cl gc.Node
-			split64(r, &cl, &ch)
-
-			gmove(&ch, &s)
-			gins(arm.ATST, &s, nil)
-			var p1 *obj.Prog
-			if bh.Type.Etype == gc.TINT32 {
-				p1 = gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah)
-			} else {
-				p1 = gins(arm.AEOR, &ah, &ah)
-			}
-			p1.Scond = arm.C_SCOND_NE
-			p6 = gc.Gbranch(arm.ABNE, nil, 0)
-			gmove(&cl, &s)
-			splitclean()
-		} else {
-			gmove(r, &s)
-			p6 = nil
-		}
-
-		gins(arm.ATST, &s, nil)
-
-		// shift == 0
-		p1 = gins(arm.AMOVW, &bl, &al)
-
-		p1.Scond = arm.C_SCOND_EQ
-		p1 = gins(arm.AMOVW, &bh, &ah)
-		p1.Scond = arm.C_SCOND_EQ
-		p2 = gc.Gbranch(arm.ABEQ, nil, 0)
-
-		// check if shift is < 32
-		gc.Nodconst(&n1, gc.Types[gc.TUINT32], 32)
-
-		gmove(&n1, &creg)
-		gins(arm.ACMP, &s, &creg)
-
-		//	MOVW.LO		bl>>s, al
-		p1 = gregshift(arm.AMOVW, &bl, arm.SHIFT_LR, &s, &al)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	SUB.LO		s,creg
-		p1 = gins(arm.ASUB, &s, &creg)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	OR.LO		bh<<(32-s), al
-		p1 = gregshift(arm.AORR, &bh, arm.SHIFT_LL, &creg, &al)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		if bh.Type.Etype == gc.TINT32 {
-			//	MOVW	bh->s, ah
-			p1 = gregshift(arm.AMOVW, &bh, arm.SHIFT_AR, &s, &ah)
-		} else {
-			//	MOVW	bh>>s, ah
-			p1 = gregshift(arm.AMOVW, &bh, arm.SHIFT_LR, &s, &ah)
-		}
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	BLO	end
-		p3 = gc.Gbranch(arm.ABLO, nil, 0)
-
-		// shift == 32
-		p1 = gins(arm.AMOVW, &bh, &al)
-
-		p1.Scond = arm.C_SCOND_EQ
-		if bh.Type.Etype == gc.TINT32 {
-			gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah)
-		} else {
-			gins(arm.AEOR, &ah, &ah)
-		}
-		p4 = gc.Gbranch(arm.ABEQ, nil, 0)
-
-		// check if shift is < 64
-		gc.Nodconst(&n1, gc.Types[gc.TUINT32], 64)
-
-		gmove(&n1, &creg)
-		gins(arm.ACMP, &s, &creg)
-
-		//	MOVW.LO		creg>>1, creg
-		p1 = gshift(arm.AMOVW, &creg, arm.SHIFT_LR, 1, &creg)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		//	SUB.LO		creg, s
-		p1 = gins(arm.ASUB, &creg, &s)
-
-		p1.Scond = arm.C_SCOND_LO
-
-		if bh.Type.Etype == gc.TINT32 {
-			//	MOVW	bh->(s-32), al
-			p1 := gregshift(arm.AMOVW, &bh, arm.SHIFT_AR, &s, &al)
-
-			p1.Scond = arm.C_SCOND_LO
-		} else {
-			//	MOVW	bh>>(v-32), al
-			p1 := gregshift(arm.AMOVW, &bh, arm.SHIFT_LR, &s, &al)
-
-			p1.Scond = arm.C_SCOND_LO
-		}
-
-		//	BLO	end
-		p5 = gc.Gbranch(arm.ABLO, nil, 0)
-
-		// s >= 64
-		if p6 != nil {
-			gc.Patch(p6, gc.Pc)
-		}
-		if bh.Type.Etype == gc.TINT32 {
-			//	MOVW	bh->31, al
-			gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &al)
-		} else {
-			gins(arm.AEOR, &al, &al)
-		}
-
-		gc.Patch(p2, gc.Pc)
-		gc.Patch(p3, gc.Pc)
-		gc.Patch(p4, gc.Pc)
-		gc.Patch(p5, gc.Pc)
-		gc.Regfree(&s)
-		gc.Regfree(&creg)
-
-	orsh_break:
-		gc.Regfree(&bl)
-		gc.Regfree(&bh)
-
-		// TODO(kaib): literal optimizations
-	// make constant the right side (it usually is anyway).
-	//		if(lo1.op == OLITERAL) {
-	//			nswap(&lo1, &lo2);
-	//			nswap(&hi1, &hi2);
-	//		}
-	//		if(lo2.op == OLITERAL) {
-	//			// special cases for constants.
-	//			lv = mpgetfix(lo2.val.u.xval);
-	//			hv = mpgetfix(hi2.val.u.xval);
-	//			splitclean();	// right side
-	//			split64(res, &lo2, &hi2);
-	//			switch(n->op) {
-	//			case OXOR:
-	//				gmove(&lo1, &lo2);
-	//				gmove(&hi1, &hi2);
-	//				switch(lv) {
-	//				case 0:
-	//					break;
-	//				case 0xffffffffu:
-	//					gins(ANOTL, N, &lo2);
-	//					break;
-	//				default:
-	//					gins(AXORL, ncon(lv), &lo2);
-	//					break;
-	//				}
-	//				switch(hv) {
-	//				case 0:
-	//					break;
-	//				case 0xffffffffu:
-	//					gins(ANOTL, N, &hi2);
-	//					break;
-	//				default:
-	//					gins(AXORL, ncon(hv), &hi2);
-	//					break;
-	//				}
-	//				break;
-
-	//			case OAND:
-	//				switch(lv) {
-	//				case 0:
-	//					gins(AMOVL, ncon(0), &lo2);
-	//					break;
-	//				default:
-	//					gmove(&lo1, &lo2);
-	//					if(lv != 0xffffffffu)
-	//						gins(AANDL, ncon(lv), &lo2);
-	//					break;
-	//				}
-	//				switch(hv) {
-	//				case 0:
-	//					gins(AMOVL, ncon(0), &hi2);
-	//					break;
-	//				default:
-	//					gmove(&hi1, &hi2);
-	//					if(hv != 0xffffffffu)
-	//						gins(AANDL, ncon(hv), &hi2);
-	//					break;
-	//				}
-	//				break;
-
-	//			case OOR:
-	//				switch(lv) {
-	//				case 0:
-	//					gmove(&lo1, &lo2);
-	//					break;
-	//				case 0xffffffffu:
-	//					gins(AMOVL, ncon(0xffffffffu), &lo2);
-	//					break;
-	//				default:
-	//					gmove(&lo1, &lo2);
-	//					gins(AORL, ncon(lv), &lo2);
-	//					break;
-	//				}
-	//				switch(hv) {
-	//				case 0:
-	//					gmove(&hi1, &hi2);
-	//					break;
-	//				case 0xffffffffu:
-	//					gins(AMOVL, ncon(0xffffffffu), &hi2);
-	//					break;
-	//				default:
-	//					gmove(&hi1, &hi2);
-	//					gins(AORL, ncon(hv), &hi2);
-	//					break;
-	//				}
-	//				break;
-	//			}
-	//			splitclean();
-	//			splitclean();
-	//			goto out;
-	//		}
-	case gc.OXOR,
-		gc.OAND,
-		gc.OOR:
-		var n1 gc.Node
-		gc.Regalloc(&n1, lo1.Type, nil)
-
-		gins(arm.AMOVW, &lo1, &al)
-		gins(arm.AMOVW, &hi1, &ah)
-		gins(arm.AMOVW, &lo2, &n1)
-		gins(optoas(n.Op, lo1.Type), &n1, &al)
-		gins(arm.AMOVW, &hi2, &n1)
-		gins(optoas(n.Op, lo1.Type), &n1, &ah)
-		gc.Regfree(&n1)
-	}
-
-	if gc.Is64(r.Type) {
-		splitclean()
-	}
-	splitclean()
-
-	split64(res, &lo1, &hi1)
-	gins(arm.AMOVW, &al, &lo1)
-	gins(arm.AMOVW, &ah, &hi1)
-	splitclean()
-
-	//out:
-	gc.Regfree(&al)
-
-	gc.Regfree(&ah)
-}
-
-/*
- * generate comparison of nl, nr, both 64-bit.
- * nl is memory; nr is constant or memory.
- */
-func cmp64(nl *gc.Node, nr *gc.Node, op gc.Op, likely int, to *obj.Prog) {
-	var lo1 gc.Node
-	var hi1 gc.Node
-	var lo2 gc.Node
-	var hi2 gc.Node
-	var r1 gc.Node
-	var r2 gc.Node
-
-	split64(nl, &lo1, &hi1)
-	split64(nr, &lo2, &hi2)
-
-	// compare most significant word;
-	// if they differ, we're done.
-	t := hi1.Type
-
-	gc.Regalloc(&r1, gc.Types[gc.TINT32], nil)
-	gc.Regalloc(&r2, gc.Types[gc.TINT32], nil)
-	gins(arm.AMOVW, &hi1, &r1)
-	gins(arm.AMOVW, &hi2, &r2)
-	gins(arm.ACMP, &r1, &r2)
-	gc.Regfree(&r1)
-	gc.Regfree(&r2)
-
-	var br *obj.Prog
-	switch op {
-	default:
-		gc.Fatalf("cmp64 %v %v", op, t)
-
-		// cmp hi
-	// bne L
-	// cmp lo
-	// beq to
-	// L:
-	case gc.OEQ:
-		br = gc.Gbranch(arm.ABNE, nil, -likely)
-
-		// cmp hi
-	// bne to
-	// cmp lo
-	// bne to
-	case gc.ONE:
-		gc.Patch(gc.Gbranch(arm.ABNE, nil, likely), to)
-
-		// cmp hi
-	// bgt to
-	// blt L
-	// cmp lo
-	// bge to (or bgt to)
-	// L:
-	case gc.OGE,
-		gc.OGT:
-		gc.Patch(gc.Gbranch(optoas(gc.OGT, t), nil, likely), to)
-
-		br = gc.Gbranch(optoas(gc.OLT, t), nil, -likely)
-
-		// cmp hi
-	// blt to
-	// bgt L
-	// cmp lo
-	// ble to (or jlt to)
-	// L:
-	case gc.OLE,
-		gc.OLT:
-		gc.Patch(gc.Gbranch(optoas(gc.OLT, t), nil, likely), to)
-
-		br = gc.Gbranch(optoas(gc.OGT, t), nil, -likely)
-	}
-
-	// compare least significant word
-	t = lo1.Type
-
-	gc.Regalloc(&r1, gc.Types[gc.TINT32], nil)
-	gc.Regalloc(&r2, gc.Types[gc.TINT32], nil)
-	gins(arm.AMOVW, &lo1, &r1)
-	gins(arm.AMOVW, &lo2, &r2)
-	gins(arm.ACMP, &r1, &r2)
-	gc.Regfree(&r1)
-	gc.Regfree(&r2)
-
-	// jump again
-	gc.Patch(gc.Gbranch(optoas(op, t), nil, likely), to)
-
-	// point first branch down here if appropriate
-	if br != nil {
-		gc.Patch(br, gc.Pc)
-	}
-
-	splitclean()
-	splitclean()
-}
diff --git a/src/cmd/compile/internal/arm/galign.go b/src/cmd/compile/internal/arm/galign.go
index afd86e4..308b016 100644
--- a/src/cmd/compile/internal/arm/galign.go
+++ b/src/cmd/compile/internal/arm/galign.go
@@ -10,62 +10,15 @@ import (
 	"cmd/internal/obj/arm"
 )
 
-func betypeinit() {
-}
-
-func Main() {
+func Init() {
 	gc.Thearch.LinkArch = &arm.Linkarm
 	gc.Thearch.REGSP = arm.REGSP
-	gc.Thearch.REGCTXT = arm.REGCTXT
-	gc.Thearch.REGCALLX = arm.REG_R1
-	gc.Thearch.REGCALLX2 = arm.REG_R2
-	gc.Thearch.REGRETURN = arm.REG_R0
-	gc.Thearch.REGMIN = arm.REG_R0
-	gc.Thearch.REGMAX = arm.REGEXT
-	gc.Thearch.FREGMIN = arm.REG_F0
-	gc.Thearch.FREGMAX = arm.FREGEXT
 	gc.Thearch.MAXWIDTH = (1 << 32) - 1
-	gc.Thearch.ReservedRegs = resvd
 
-	gc.Thearch.Betypeinit = betypeinit
-	gc.Thearch.Cgen64 = cgen64
-	gc.Thearch.Cgen_hmul = cgen_hmul
-	gc.Thearch.Cgen_shift = cgen_shift
-	gc.Thearch.Clearfat = clearfat
-	gc.Thearch.Cmp64 = cmp64
 	gc.Thearch.Defframe = defframe
-	gc.Thearch.Excise = excise
-	gc.Thearch.Expandchecks = expandchecks
-	gc.Thearch.Getg = getg
-	gc.Thearch.Gins = gins
-	gc.Thearch.Ginscmp = ginscmp
-	gc.Thearch.Ginscon = ginscon
-	gc.Thearch.Ginsnop = ginsnop
-	gc.Thearch.Gmove = gmove
-	gc.Thearch.Cgenindex = cgenindex
-	gc.Thearch.Peep = peep
 	gc.Thearch.Proginfo = proginfo
-	gc.Thearch.Regtyp = regtyp
-	gc.Thearch.Sameaddr = sameaddr
-	gc.Thearch.Smallindir = smallindir
-	gc.Thearch.Stackaddr = stackaddr
-	gc.Thearch.Blockcopy = blockcopy
-	gc.Thearch.Sudoaddable = sudoaddable
-	gc.Thearch.Sudoclean = sudoclean
-	gc.Thearch.Excludedregs = excludedregs
-	gc.Thearch.RtoB = RtoB
-	gc.Thearch.FtoB = RtoB
-	gc.Thearch.BtoR = BtoR
-	gc.Thearch.BtoF = BtoF
-	gc.Thearch.Optoas = optoas
-	gc.Thearch.Doregbits = doregbits
-	gc.Thearch.Regnames = regnames
 
-	gc.Thearch.SSARegToReg = ssaRegToReg
 	gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
 	gc.Thearch.SSAGenValue = ssaGenValue
 	gc.Thearch.SSAGenBlock = ssaGenBlock
-
-	gc.Main()
-	gc.Exit(0)
 }
diff --git a/src/cmd/compile/internal/arm/ggen.go b/src/cmd/compile/internal/arm/ggen.go
index 4a45e58..6dce0a4 100644
--- a/src/cmd/compile/internal/arm/ggen.go
+++ b/src/cmd/compile/internal/arm/ggen.go
@@ -34,7 +34,7 @@ func defframe(ptxt *obj.Prog) {
 			gc.Fatalf("needzero class %d", n.Class)
 		}
 		if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
-			gc.Fatalf("var %v has size %d offset %d", gc.Nconv(n, gc.FmtLong), int(n.Type.Width), int(n.Xoffset))
+			gc.Fatalf("var %L has size %d offset %d", n, int(n.Type.Width), int(n.Xoffset))
 		}
 		if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthptr) {
 			// merge with range we already have
@@ -62,489 +62,42 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, r0 *uint32) *obj.Pr
 		return p
 	}
 	if *r0 == 0 {
-		p = appendpp(p, arm.AMOVW, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, arm.REG_R0, 0)
+		p = gc.Appendpp(p, arm.AMOVW, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, arm.REG_R0, 0)
 		*r0 = 1
 	}
 
 	if cnt < int64(4*gc.Widthptr) {
 		for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
-			p = appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REGSP, int32(4+frame+lo+i))
+			p = gc.Appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REGSP, 4+frame+lo+i)
 		}
 	} else if !gc.Nacl && (cnt <= int64(128*gc.Widthptr)) {
-		p = appendpp(p, arm.AADD, obj.TYPE_CONST, 0, int32(4+frame+lo), obj.TYPE_REG, arm.REG_R1, 0)
+		p = gc.Appendpp(p, arm.AADD, obj.TYPE_CONST, 0, 4+frame+lo, obj.TYPE_REG, arm.REG_R1, 0)
 		p.Reg = arm.REGSP
-		p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
-		f := gc.Sysfunc("duffzero")
-		gc.Naddr(&p.To, f)
-		gc.Afunclit(&p.To, f)
+		p = gc.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
+		gc.Naddr(&p.To, gc.Sysfunc("duffzero"))
 		p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr))
 	} else {
-		p = appendpp(p, arm.AADD, obj.TYPE_CONST, 0, int32(4+frame+lo), obj.TYPE_REG, arm.REG_R1, 0)
+		p = gc.Appendpp(p, arm.AADD, obj.TYPE_CONST, 0, 4+frame+lo, obj.TYPE_REG, arm.REG_R1, 0)
 		p.Reg = arm.REGSP
-		p = appendpp(p, arm.AADD, obj.TYPE_CONST, 0, int32(cnt), obj.TYPE_REG, arm.REG_R2, 0)
+		p = gc.Appendpp(p, arm.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm.REG_R2, 0)
 		p.Reg = arm.REG_R1
-		p = appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REG_R1, 4)
+		p = gc.Appendpp(p, arm.AMOVW, obj.TYPE_REG, arm.REG_R0, 0, obj.TYPE_MEM, arm.REG_R1, 4)
 		p1 := p
 		p.Scond |= arm.C_PBIT
-		p = appendpp(p, arm.ACMP, obj.TYPE_REG, arm.REG_R1, 0, obj.TYPE_NONE, 0, 0)
+		p = gc.Appendpp(p, arm.ACMP, obj.TYPE_REG, arm.REG_R1, 0, obj.TYPE_NONE, 0, 0)
 		p.Reg = arm.REG_R2
-		p = appendpp(p, arm.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
+		p = gc.Appendpp(p, arm.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
 		gc.Patch(p, p1)
 	}
 
 	return p
 }
 
-func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int32, ttype obj.AddrType, treg int, toffset int32) *obj.Prog {
-	q := gc.Ctxt.NewProg()
-	gc.Clearp(q)
-	q.As = as
-	q.Lineno = p.Lineno
-	q.From.Type = ftype
-	q.From.Reg = int16(freg)
-	q.From.Offset = int64(foffset)
-	q.To.Type = ttype
-	q.To.Reg = int16(treg)
-	q.To.Offset = int64(toffset)
-	q.Link = p.Link
-	p.Link = q
-	return q
-}
-
-/*
- * generate high multiply
- *  res = (nl * nr) >> wordsize
- */
-func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	if nl.Ullman < nr.Ullman {
-		nl, nr = nr, nl
-	}
-
-	t := nl.Type
-	w := t.Width * 8
-	var n1 gc.Node
-	gc.Regalloc(&n1, t, res)
-	gc.Cgen(nl, &n1)
-	var n2 gc.Node
-	gc.Regalloc(&n2, t, nil)
-	gc.Cgen(nr, &n2)
-	switch gc.Simtype[t.Etype] {
-	case gc.TINT8,
-		gc.TINT16:
-		gins(optoas(gc.OMUL, t), &n2, &n1)
-		gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(w), &n1)
-
-	case gc.TUINT8,
-		gc.TUINT16:
-		gins(optoas(gc.OMUL, t), &n2, &n1)
-		gshift(arm.AMOVW, &n1, arm.SHIFT_LR, int32(w), &n1)
-
-		// perform a long multiplication.
-	case gc.TINT32,
-		gc.TUINT32:
-		var p *obj.Prog
-		if t.IsSigned() {
-			p = gins(arm.AMULL, &n2, nil)
-		} else {
-			p = gins(arm.AMULLU, &n2, nil)
-		}
-
-		// n2 * n1 -> (n1 n2)
-		p.Reg = n1.Reg
-
-		p.To.Type = obj.TYPE_REGREG
-		p.To.Reg = n1.Reg
-		p.To.Offset = int64(n2.Reg)
-
-	default:
-		gc.Fatalf("cgen_hmul %v", t)
-	}
-
-	gc.Cgen(&n1, res)
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-/*
- * generate shift according to op, one of:
- *	res = nl << nr
- *	res = nl >> nr
- */
-func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	if nl.Type.Width > 4 {
-		gc.Fatalf("cgen_shift %v", nl.Type)
-	}
-
-	w := int(nl.Type.Width * 8)
-
-	if op == gc.OLROT {
-		v := nr.Int64()
-		var n1 gc.Node
-		gc.Regalloc(&n1, nl.Type, res)
-		if w == 32 {
-			gc.Cgen(nl, &n1)
-			gshift(arm.AMOVW, &n1, arm.SHIFT_RR, int32(w)-int32(v), &n1)
-		} else {
-			var n2 gc.Node
-			gc.Regalloc(&n2, nl.Type, nil)
-			gc.Cgen(nl, &n2)
-			gshift(arm.AMOVW, &n2, arm.SHIFT_LL, int32(v), &n1)
-			gshift(arm.AORR, &n2, arm.SHIFT_LR, int32(w)-int32(v), &n1)
-			gc.Regfree(&n2)
-
-			// Ensure sign/zero-extended result.
-			gins(optoas(gc.OAS, nl.Type), &n1, &n1)
-		}
-
-		gmove(&n1, res)
-		gc.Regfree(&n1)
-		return
-	}
-
-	if nr.Op == gc.OLITERAL {
-		var n1 gc.Node
-		gc.Regalloc(&n1, nl.Type, res)
-		gc.Cgen(nl, &n1)
-		sc := uint64(nr.Int64())
-		if sc == 0 {
-		} else // nothing to do
-		if sc >= uint64(nl.Type.Width*8) {
-			if op == gc.ORSH && nl.Type.IsSigned() {
-				gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(w), &n1)
-			} else {
-				gins(arm.AEOR, &n1, &n1)
-			}
-		} else {
-			if op == gc.ORSH && nl.Type.IsSigned() {
-				gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(sc), &n1)
-			} else if op == gc.ORSH {
-				gshift(arm.AMOVW, &n1, arm.SHIFT_LR, int32(sc), &n1) // OLSH
-			} else {
-				gshift(arm.AMOVW, &n1, arm.SHIFT_LL, int32(sc), &n1)
-			}
-		}
-
-		if w < 32 && op == gc.OLSH {
-			gins(optoas(gc.OAS, nl.Type), &n1, &n1)
-		}
-		gmove(&n1, res)
-		gc.Regfree(&n1)
-		return
-	}
-
-	tr := nr.Type
-	var t gc.Node
-	var n1 gc.Node
-	var n2 gc.Node
-	var n3 gc.Node
-	if tr.Width > 4 {
-		var nt gc.Node
-		gc.Tempname(&nt, nr.Type)
-		if nl.Ullman >= nr.Ullman {
-			gc.Regalloc(&n2, nl.Type, res)
-			gc.Cgen(nl, &n2)
-			gc.Cgen(nr, &nt)
-			n1 = nt
-		} else {
-			gc.Cgen(nr, &nt)
-			gc.Regalloc(&n2, nl.Type, res)
-			gc.Cgen(nl, &n2)
-		}
-
-		var hi gc.Node
-		var lo gc.Node
-		split64(&nt, &lo, &hi)
-		gc.Regalloc(&n1, gc.Types[gc.TUINT32], nil)
-		gc.Regalloc(&n3, gc.Types[gc.TUINT32], nil)
-		gmove(&lo, &n1)
-		gmove(&hi, &n3)
-		splitclean()
-		gins(arm.ATST, &n3, nil)
-		gc.Nodconst(&t, gc.Types[gc.TUINT32], int64(w))
-		p1 := gins(arm.AMOVW, &t, &n1)
-		p1.Scond = arm.C_SCOND_NE
-		tr = gc.Types[gc.TUINT32]
-		gc.Regfree(&n3)
-	} else {
-		if nl.Ullman >= nr.Ullman {
-			gc.Regalloc(&n2, nl.Type, res)
-			gc.Cgen(nl, &n2)
-			gc.Regalloc(&n1, nr.Type, nil)
-			gc.Cgen(nr, &n1)
-		} else {
-			gc.Regalloc(&n1, nr.Type, nil)
-			gc.Cgen(nr, &n1)
-			gc.Regalloc(&n2, nl.Type, res)
-			gc.Cgen(nl, &n2)
-		}
-	}
-
-	// test for shift being 0
-	gins(arm.ATST, &n1, nil)
-
-	p3 := gc.Gbranch(arm.ABEQ, nil, -1)
-
-	// test and fix up large shifts
-	// TODO: if(!bounded), don't emit some of this.
-	gc.Regalloc(&n3, tr, nil)
-
-	gc.Nodconst(&t, gc.Types[gc.TUINT32], int64(w))
-	gmove(&t, &n3)
-	gins(arm.ACMP, &n1, &n3)
-	if op == gc.ORSH {
-		var p1 *obj.Prog
-		var p2 *obj.Prog
-		if nl.Type.IsSigned() {
-			p1 = gshift(arm.AMOVW, &n2, arm.SHIFT_AR, int32(w)-1, &n2)
-			p2 = gregshift(arm.AMOVW, &n2, arm.SHIFT_AR, &n1, &n2)
-		} else {
-			p1 = gins(arm.AEOR, &n2, &n2)
-			p2 = gregshift(arm.AMOVW, &n2, arm.SHIFT_LR, &n1, &n2)
-		}
-
-		p1.Scond = arm.C_SCOND_HS
-		p2.Scond = arm.C_SCOND_LO
-	} else {
-		p1 := gins(arm.AEOR, &n2, &n2)
-		p2 := gregshift(arm.AMOVW, &n2, arm.SHIFT_LL, &n1, &n2)
-		p1.Scond = arm.C_SCOND_HS
-		p2.Scond = arm.C_SCOND_LO
-	}
-
-	gc.Regfree(&n3)
-
-	gc.Patch(p3, gc.Pc)
-
-	// Left-shift of smaller word must be sign/zero-extended.
-	if w < 32 && op == gc.OLSH {
-		gins(optoas(gc.OAS, nl.Type), &n2, &n2)
-	}
-	gmove(&n2, res)
-
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-func clearfat(nl *gc.Node) {
-	/* clear a fat object */
-	if gc.Debug['g'] != 0 {
-		gc.Dump("\nclearfat", nl)
-	}
-
-	w := uint32(nl.Type.Width)
-
-	// Avoid taking the address for simple enough types.
-	if gc.Componentgen(nil, nl) {
-		return
-	}
-
-	c := w % 4 // bytes
-	q := w / 4 // quads
-
-	if nl.Type.Align < 4 {
-		q = 0
-		c = w
-	}
-
-	var r0 gc.Node
-	r0.Op = gc.OREGISTER
-
-	r0.Reg = arm.REG_R0
-	var r1 gc.Node
-	r1.Op = gc.OREGISTER
-	r1.Reg = arm.REG_R1
-	var dst gc.Node
-	gc.Regalloc(&dst, gc.Types[gc.Tptr], &r1)
-	gc.Agen(nl, &dst)
-	var nc gc.Node
-	gc.Nodconst(&nc, gc.Types[gc.TUINT32], 0)
-	var nz gc.Node
-	gc.Regalloc(&nz, gc.Types[gc.TUINT32], &r0)
-	gc.Cgen(&nc, &nz)
-
-	if q > 128 {
-		var end gc.Node
-		gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
-		p := gins(arm.AMOVW, &dst, &end)
-		p.From.Type = obj.TYPE_ADDR
-		p.From.Offset = int64(q) * 4
-
-		p = gins(arm.AMOVW, &nz, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = 4
-		p.Scond |= arm.C_PBIT
-		pl := p
-
-		p = gins(arm.ACMP, &dst, nil)
-		raddr(&end, p)
-		gc.Patch(gc.Gbranch(arm.ABNE, nil, 0), pl)
-
-		gc.Regfree(&end)
-	} else if q >= 4 && !gc.Nacl {
-		f := gc.Sysfunc("duffzero")
-		p := gins(obj.ADUFFZERO, nil, f)
-		gc.Afunclit(&p.To, f)
-
-		// 4 and 128 = magic constants: see ../../runtime/asm_arm.s
-		p.To.Offset = 4 * (128 - int64(q))
-	} else {
-		var p *obj.Prog
-		for q > 0 {
-			p = gins(arm.AMOVW, &nz, &dst)
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = 4
-			p.Scond |= arm.C_PBIT
-
-			//print("1. %v\n", p);
-			q--
-		}
-	}
-
-	if c > 4 {
-		// Loop to zero unaligned memory.
-		var end gc.Node
-		gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
-		p := gins(arm.AMOVW, &dst, &end)
-		p.From.Type = obj.TYPE_ADDR
-		p.From.Offset = int64(c)
-
-		p = gins(arm.AMOVB, &nz, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = 1
-		p.Scond |= arm.C_PBIT
-		pl := p
-
-		p = gins(arm.ACMP, &dst, nil)
-		raddr(&end, p)
-		gc.Patch(gc.Gbranch(arm.ABNE, nil, 0), pl)
-
-		gc.Regfree(&end)
-		c = 0
-	}
-	var p *obj.Prog
-	for c > 0 {
-		p = gins(arm.AMOVB, &nz, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = 1
-		p.Scond |= arm.C_PBIT
-
-		//print("2. %v\n", p);
-		c--
-	}
-
-	gc.Regfree(&dst)
-	gc.Regfree(&nz)
-}
-
-// Called after regopt and peep have run.
-// Expand CHECKNIL pseudo-op into actual nil pointer check.
-func expandchecks(firstp *obj.Prog) {
-	var reg int
-	var p1 *obj.Prog
-
-	for p := firstp; p != nil; p = p.Link {
-		if p.As != obj.ACHECKNIL {
-			continue
-		}
-		if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers
-			gc.Warnl(p.Lineno, "generated nil check")
-		}
-		if p.From.Type != obj.TYPE_REG {
-			gc.Fatalf("invalid nil check %v", p)
-		}
-		reg = int(p.From.Reg)
-
-		// check is
-		//	CMP arg, $0
-		//	MOV.EQ arg, 0(arg)
-		p1 = gc.Ctxt.NewProg()
-
-		gc.Clearp(p1)
-		p1.Link = p.Link
-		p.Link = p1
-		p1.Lineno = p.Lineno
-		p1.Pc = 9999
-		p1.As = arm.AMOVW
-		p1.From.Type = obj.TYPE_REG
-		p1.From.Reg = int16(reg)
-		p1.To.Type = obj.TYPE_MEM
-		p1.To.Reg = int16(reg)
-		p1.To.Offset = 0
-		p1.Scond = arm.C_SCOND_EQ
-		p.As = arm.ACMP
-		p.From.Type = obj.TYPE_CONST
-		p.From.Reg = 0
-		p.From.Offset = 0
-		p.Reg = int16(reg)
-	}
-}
-
 func ginsnop() {
-	var r gc.Node
-	gc.Nodreg(&r, gc.Types[gc.TINT], arm.REG_R0)
-	p := gins(arm.AAND, &r, &r)
+	p := gc.Prog(arm.AAND)
+	p.From.Type = obj.TYPE_REG
+	p.From.Reg = arm.REG_R0
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = arm.REG_R0
 	p.Scond = arm.C_SCOND_EQ
 }
-
-/*
- * generate
- *	as $c, n
- */
-func ginscon(as obj.As, c int64, n *gc.Node) {
-	var n1 gc.Node
-	gc.Nodconst(&n1, gc.Types[gc.TINT32], c)
-	var n2 gc.Node
-	gc.Regalloc(&n2, gc.Types[gc.TINT32], nil)
-	gmove(&n1, &n2)
-	gins(as, &n2, n)
-	gc.Regfree(&n2)
-}
-
-func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
-	if t.IsInteger() && n1.Op == gc.OLITERAL && n1.Int64() == 0 && n2.Op != gc.OLITERAL {
-		op = gc.Brrev(op)
-		n1, n2 = n2, n1
-	}
-	var r1, r2, g1, g2 gc.Node
-	gc.Regalloc(&r1, t, n1)
-	gc.Regalloc(&g1, n1.Type, &r1)
-	gc.Cgen(n1, &g1)
-	gmove(&g1, &r1)
-	if t.IsInteger() && n2.Op == gc.OLITERAL && n2.Int64() == 0 {
-		gins(arm.ACMP, &r1, n2)
-	} else {
-		gc.Regalloc(&r2, t, n2)
-		gc.Regalloc(&g2, n1.Type, &r2)
-		gc.Cgen(n2, &g2)
-		gmove(&g2, &r2)
-		gins(optoas(gc.OCMP, t), &r1, &r2)
-		gc.Regfree(&g2)
-		gc.Regfree(&r2)
-	}
-	gc.Regfree(&g1)
-	gc.Regfree(&r1)
-	return gc.Gbranch(optoas(op, t), nil, likely)
-}
-
-// addr += index*width if possible.
-func addindex(index *gc.Node, width int64, addr *gc.Node) bool {
-	switch width {
-	case 2:
-		gshift(arm.AADD, index, arm.SHIFT_LL, 1, addr)
-		return true
-	case 4:
-		gshift(arm.AADD, index, arm.SHIFT_LL, 2, addr)
-		return true
-	case 8:
-		gshift(arm.AADD, index, arm.SHIFT_LL, 3, addr)
-		return true
-	}
-	return false
-}
-
-// res = runtime.getg()
-func getg(res *gc.Node) {
-	var n1 gc.Node
-	gc.Nodreg(&n1, res.Type, arm.REGG)
-	gmove(&n1, res)
-}
diff --git a/src/cmd/compile/internal/arm/gsubr.go b/src/cmd/compile/internal/arm/gsubr.go
deleted file mode 100644
index b5d7bc0..0000000
--- a/src/cmd/compile/internal/arm/gsubr.go
+++ /dev/null
@@ -1,1225 +0,0 @@
-// Derived from Inferno utils/5c/txt.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5c/txt.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package arm
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/arm"
-	"fmt"
-)
-
-var resvd = []int{
-	arm.REG_R9,  // formerly reserved for m; might be okay to reuse now; not sure about NaCl
-	arm.REG_R10, // reserved for g
-}
-
-/*
- * return constant i node.
- * overwritten by next call, but useful in calls to gins.
- */
-
-var ncon_n gc.Node
-
-func ncon(i uint32) *gc.Node {
-	if ncon_n.Type == nil {
-		gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0)
-	}
-	ncon_n.SetInt(int64(i))
-	return &ncon_n
-}
-
-var sclean [10]gc.Node
-
-var nsclean int
-
-/*
- * n is a 64-bit value.  fill in lo and hi to refer to its 32-bit halves.
- */
-func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
-	if !gc.Is64(n.Type) {
-		gc.Fatalf("split64 %v", n.Type)
-	}
-
-	if nsclean >= len(sclean) {
-		gc.Fatalf("split64 clean")
-	}
-	sclean[nsclean].Op = gc.OEMPTY
-	nsclean++
-	switch n.Op {
-	default:
-		switch n.Op {
-		default:
-			var n1 gc.Node
-			if !dotaddable(n, &n1) {
-				gc.Igen(n, &n1, nil)
-				sclean[nsclean-1] = n1
-			}
-
-			n = &n1
-
-		case gc.ONAME, gc.OINDREG:
-			// nothing
-		}
-
-		*lo = *n
-		*hi = *n
-		lo.Type = gc.Types[gc.TUINT32]
-		if n.Type.Etype == gc.TINT64 {
-			hi.Type = gc.Types[gc.TINT32]
-		} else {
-			hi.Type = gc.Types[gc.TUINT32]
-		}
-		hi.Xoffset += 4
-
-	case gc.OLITERAL:
-		var n1 gc.Node
-		n.Convconst(&n1, n.Type)
-		i := n1.Int64()
-		gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i)))
-		i >>= 32
-		if n.Type.Etype == gc.TINT64 {
-			gc.Nodconst(hi, gc.Types[gc.TINT32], int64(int32(i)))
-		} else {
-			gc.Nodconst(hi, gc.Types[gc.TUINT32], int64(uint32(i)))
-		}
-	}
-}
-
-func splitclean() {
-	if nsclean <= 0 {
-		gc.Fatalf("splitclean")
-	}
-	nsclean--
-	if sclean[nsclean].Op != gc.OEMPTY {
-		gc.Regfree(&sclean[nsclean])
-	}
-}
-
-func gmove(f *gc.Node, t *gc.Node) {
-	if gc.Debug['M'] != 0 {
-		fmt.Printf("gmove %v -> %v\n", f, t)
-	}
-
-	ft := gc.Simsimtype(f.Type)
-	tt := gc.Simsimtype(t.Type)
-	cvt := t.Type
-
-	if gc.Iscomplex[ft] || gc.Iscomplex[tt] {
-		gc.Complexmove(f, t)
-		return
-	}
-
-	// cannot have two memory operands;
-	// except 64-bit, which always copies via registers anyway.
-	var a obj.As
-	var r1 gc.Node
-	if !gc.Is64(f.Type) && !gc.Is64(t.Type) && gc.Ismem(f) && gc.Ismem(t) {
-		goto hard
-	}
-
-	// convert constant to desired type
-	if f.Op == gc.OLITERAL {
-		var con gc.Node
-		switch tt {
-		default:
-			f.Convconst(&con, t.Type)
-
-		case gc.TINT16,
-			gc.TINT8:
-			var con gc.Node
-			f.Convconst(&con, gc.Types[gc.TINT32])
-			var r1 gc.Node
-			gc.Regalloc(&r1, con.Type, t)
-			gins(arm.AMOVW, &con, &r1)
-			gmove(&r1, t)
-			gc.Regfree(&r1)
-			return
-
-		case gc.TUINT16,
-			gc.TUINT8:
-			var con gc.Node
-			f.Convconst(&con, gc.Types[gc.TUINT32])
-			var r1 gc.Node
-			gc.Regalloc(&r1, con.Type, t)
-			gins(arm.AMOVW, &con, &r1)
-			gmove(&r1, t)
-			gc.Regfree(&r1)
-			return
-		}
-
-		f = &con
-		ft = gc.Simsimtype(con.Type)
-
-		// constants can't move directly to memory
-		if gc.Ismem(t) && !gc.Is64(t.Type) {
-			goto hard
-		}
-	}
-
-	// value -> value copy, only one memory operand.
-	// figure out the instruction to use.
-	// break out of switch for one-instruction gins.
-	// goto rdst for "destination must be register".
-	// goto hard for "convert to cvt type first".
-	// otherwise handle and return.
-
-	switch uint32(ft)<<16 | uint32(tt) {
-	default:
-		// should not happen
-		gc.Fatalf("gmove %v -> %v", f, t)
-		return
-
-		/*
-		 * integer copy and truncate
-		 */
-	case gc.TINT8<<16 | gc.TINT8: // same size
-		if !gc.Ismem(f) {
-			a = arm.AMOVB
-			break
-		}
-		fallthrough
-
-	case gc.TUINT8<<16 | gc.TINT8,
-		gc.TINT16<<16 | gc.TINT8, // truncate
-		gc.TUINT16<<16 | gc.TINT8,
-		gc.TINT32<<16 | gc.TINT8,
-		gc.TUINT32<<16 | gc.TINT8:
-		a = arm.AMOVBS
-
-	case gc.TUINT8<<16 | gc.TUINT8:
-		if !gc.Ismem(f) {
-			a = arm.AMOVB
-			break
-		}
-		fallthrough
-
-	case gc.TINT8<<16 | gc.TUINT8,
-		gc.TINT16<<16 | gc.TUINT8,
-		gc.TUINT16<<16 | gc.TUINT8,
-		gc.TINT32<<16 | gc.TUINT8,
-		gc.TUINT32<<16 | gc.TUINT8:
-		a = arm.AMOVBU
-
-	case gc.TINT64<<16 | gc.TINT8, // truncate low word
-		gc.TUINT64<<16 | gc.TINT8:
-		a = arm.AMOVBS
-
-		goto trunc64
-
-	case gc.TINT64<<16 | gc.TUINT8,
-		gc.TUINT64<<16 | gc.TUINT8:
-		a = arm.AMOVBU
-		goto trunc64
-
-	case gc.TINT16<<16 | gc.TINT16: // same size
-		if !gc.Ismem(f) {
-			a = arm.AMOVH
-			break
-		}
-		fallthrough
-
-	case gc.TUINT16<<16 | gc.TINT16,
-		gc.TINT32<<16 | gc.TINT16, // truncate
-		gc.TUINT32<<16 | gc.TINT16:
-		a = arm.AMOVHS
-
-	case gc.TUINT16<<16 | gc.TUINT16:
-		if !gc.Ismem(f) {
-			a = arm.AMOVH
-			break
-		}
-		fallthrough
-
-	case gc.TINT16<<16 | gc.TUINT16,
-		gc.TINT32<<16 | gc.TUINT16,
-		gc.TUINT32<<16 | gc.TUINT16:
-		a = arm.AMOVHU
-
-	case gc.TINT64<<16 | gc.TINT16, // truncate low word
-		gc.TUINT64<<16 | gc.TINT16:
-		a = arm.AMOVHS
-
-		goto trunc64
-
-	case gc.TINT64<<16 | gc.TUINT16,
-		gc.TUINT64<<16 | gc.TUINT16:
-		a = arm.AMOVHU
-		goto trunc64
-
-	case gc.TINT32<<16 | gc.TINT32, // same size
-		gc.TINT32<<16 | gc.TUINT32,
-		gc.TUINT32<<16 | gc.TINT32,
-		gc.TUINT32<<16 | gc.TUINT32:
-		a = arm.AMOVW
-
-	case gc.TINT64<<16 | gc.TINT32, // truncate
-		gc.TUINT64<<16 | gc.TINT32,
-		gc.TINT64<<16 | gc.TUINT32,
-		gc.TUINT64<<16 | gc.TUINT32:
-		var flo gc.Node
-		var fhi gc.Node
-		split64(f, &flo, &fhi)
-
-		var r1 gc.Node
-		gc.Regalloc(&r1, t.Type, nil)
-		gins(arm.AMOVW, &flo, &r1)
-		gins(arm.AMOVW, &r1, t)
-		gc.Regfree(&r1)
-		splitclean()
-		return
-
-	case gc.TINT64<<16 | gc.TINT64, // same size
-		gc.TINT64<<16 | gc.TUINT64,
-		gc.TUINT64<<16 | gc.TINT64,
-		gc.TUINT64<<16 | gc.TUINT64:
-		var fhi gc.Node
-		var flo gc.Node
-		split64(f, &flo, &fhi)
-
-		var tlo gc.Node
-		var thi gc.Node
-		split64(t, &tlo, &thi)
-		var r1 gc.Node
-		gc.Regalloc(&r1, flo.Type, nil)
-		var r2 gc.Node
-		gc.Regalloc(&r2, fhi.Type, nil)
-		gins(arm.AMOVW, &flo, &r1)
-		gins(arm.AMOVW, &fhi, &r2)
-		gins(arm.AMOVW, &r1, &tlo)
-		gins(arm.AMOVW, &r2, &thi)
-		gc.Regfree(&r1)
-		gc.Regfree(&r2)
-		splitclean()
-		splitclean()
-		return
-
-		/*
-		 * integer up-conversions
-		 */
-	case gc.TINT8<<16 | gc.TINT16, // sign extend int8
-		gc.TINT8<<16 | gc.TUINT16,
-		gc.TINT8<<16 | gc.TINT32,
-		gc.TINT8<<16 | gc.TUINT32:
-		a = arm.AMOVBS
-
-		goto rdst
-
-	case gc.TINT8<<16 | gc.TINT64, // convert via int32
-		gc.TINT8<<16 | gc.TUINT64:
-		cvt = gc.Types[gc.TINT32]
-
-		goto hard
-
-	case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8
-		gc.TUINT8<<16 | gc.TUINT16,
-		gc.TUINT8<<16 | gc.TINT32,
-		gc.TUINT8<<16 | gc.TUINT32:
-		a = arm.AMOVBU
-
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TINT64, // convert via uint32
-		gc.TUINT8<<16 | gc.TUINT64:
-		cvt = gc.Types[gc.TUINT32]
-
-		goto hard
-
-	case gc.TINT16<<16 | gc.TINT32, // sign extend int16
-		gc.TINT16<<16 | gc.TUINT32:
-		a = arm.AMOVHS
-
-		goto rdst
-
-	case gc.TINT16<<16 | gc.TINT64, // convert via int32
-		gc.TINT16<<16 | gc.TUINT64:
-		cvt = gc.Types[gc.TINT32]
-
-		goto hard
-
-	case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16
-		gc.TUINT16<<16 | gc.TUINT32:
-		a = arm.AMOVHU
-
-		goto rdst
-
-	case gc.TUINT16<<16 | gc.TINT64, // convert via uint32
-		gc.TUINT16<<16 | gc.TUINT64:
-		cvt = gc.Types[gc.TUINT32]
-
-		goto hard
-
-	case gc.TINT32<<16 | gc.TINT64, // sign extend int32
-		gc.TINT32<<16 | gc.TUINT64:
-		var tlo gc.Node
-		var thi gc.Node
-		split64(t, &tlo, &thi)
-
-		var r1 gc.Node
-		gc.Regalloc(&r1, tlo.Type, nil)
-		var r2 gc.Node
-		gc.Regalloc(&r2, thi.Type, nil)
-		gmove(f, &r1)
-		p1 := gins(arm.AMOVW, &r1, &r2)
-		p1.From.Type = obj.TYPE_SHIFT
-		p1.From.Offset = 2<<5 | 31<<7 | int64(r1.Reg)&15 // r1->31
-		p1.From.Reg = 0
-
-		//print("gmove: %v\n", p1);
-		gins(arm.AMOVW, &r1, &tlo)
-
-		gins(arm.AMOVW, &r2, &thi)
-		gc.Regfree(&r1)
-		gc.Regfree(&r2)
-		splitclean()
-		return
-
-	case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32
-		gc.TUINT32<<16 | gc.TUINT64:
-		var thi gc.Node
-		var tlo gc.Node
-		split64(t, &tlo, &thi)
-
-		gmove(f, &tlo)
-		var r1 gc.Node
-		gc.Regalloc(&r1, thi.Type, nil)
-		gins(arm.AMOVW, ncon(0), &r1)
-		gins(arm.AMOVW, &r1, &thi)
-		gc.Regfree(&r1)
-		splitclean()
-		return
-
-		//	case CASE(TFLOAT64, TUINT64):
-	/*
-	* float to integer
-	 */
-	case gc.TFLOAT32<<16 | gc.TINT8,
-		gc.TFLOAT32<<16 | gc.TUINT8,
-		gc.TFLOAT32<<16 | gc.TINT16,
-		gc.TFLOAT32<<16 | gc.TUINT16,
-		gc.TFLOAT32<<16 | gc.TINT32,
-		gc.TFLOAT32<<16 | gc.TUINT32,
-
-		//	case CASE(TFLOAT32, TUINT64):
-
-		gc.TFLOAT64<<16 | gc.TINT8,
-		gc.TFLOAT64<<16 | gc.TUINT8,
-		gc.TFLOAT64<<16 | gc.TINT16,
-		gc.TFLOAT64<<16 | gc.TUINT16,
-		gc.TFLOAT64<<16 | gc.TINT32,
-		gc.TFLOAT64<<16 | gc.TUINT32:
-		fa := arm.AMOVF
-
-		a := arm.AMOVFW
-		if ft == gc.TFLOAT64 {
-			fa = arm.AMOVD
-			a = arm.AMOVDW
-		}
-
-		ta := arm.AMOVW
-		switch tt {
-		case gc.TINT8:
-			ta = arm.AMOVBS
-
-		case gc.TUINT8:
-			ta = arm.AMOVBU
-
-		case gc.TINT16:
-			ta = arm.AMOVHS
-
-		case gc.TUINT16:
-			ta = arm.AMOVHU
-		}
-
-		var r1 gc.Node
-		gc.Regalloc(&r1, gc.Types[ft], f)
-		var r2 gc.Node
-		gc.Regalloc(&r2, gc.Types[tt], t)
-		gins(fa, f, &r1)        // load to fpu
-		p1 := gins(a, &r1, &r1) // convert to w
-		switch tt {
-		case gc.TUINT8,
-			gc.TUINT16,
-			gc.TUINT32:
-			p1.Scond |= arm.C_UBIT
-		}
-
-		gins(arm.AMOVW, &r1, &r2) // copy to cpu
-		gins(ta, &r2, t)          // store
-		gc.Regfree(&r1)
-		gc.Regfree(&r2)
-		return
-
-		/*
-		 * integer to float
-		 */
-	case gc.TINT8<<16 | gc.TFLOAT32,
-		gc.TUINT8<<16 | gc.TFLOAT32,
-		gc.TINT16<<16 | gc.TFLOAT32,
-		gc.TUINT16<<16 | gc.TFLOAT32,
-		gc.TINT32<<16 | gc.TFLOAT32,
-		gc.TUINT32<<16 | gc.TFLOAT32,
-		gc.TINT8<<16 | gc.TFLOAT64,
-		gc.TUINT8<<16 | gc.TFLOAT64,
-		gc.TINT16<<16 | gc.TFLOAT64,
-		gc.TUINT16<<16 | gc.TFLOAT64,
-		gc.TINT32<<16 | gc.TFLOAT64,
-		gc.TUINT32<<16 | gc.TFLOAT64:
-		fa := arm.AMOVW
-
-		switch ft {
-		case gc.TINT8:
-			fa = arm.AMOVBS
-
-		case gc.TUINT8:
-			fa = arm.AMOVBU
-
-		case gc.TINT16:
-			fa = arm.AMOVHS
-
-		case gc.TUINT16:
-			fa = arm.AMOVHU
-		}
-
-		a := arm.AMOVWF
-		ta := arm.AMOVF
-		if tt == gc.TFLOAT64 {
-			a = arm.AMOVWD
-			ta = arm.AMOVD
-		}
-
-		var r1 gc.Node
-		gc.Regalloc(&r1, gc.Types[ft], f)
-		var r2 gc.Node
-		gc.Regalloc(&r2, gc.Types[tt], t)
-		gins(fa, f, &r1)          // load to cpu
-		gins(arm.AMOVW, &r1, &r2) // copy to fpu
-		p1 := gins(a, &r2, &r2)   // convert
-		switch ft {
-		case gc.TUINT8,
-			gc.TUINT16,
-			gc.TUINT32:
-			p1.Scond |= arm.C_UBIT
-		}
-
-		gins(ta, &r2, t) // store
-		gc.Regfree(&r1)
-		gc.Regfree(&r2)
-		return
-
-	case gc.TUINT64<<16 | gc.TFLOAT32,
-		gc.TUINT64<<16 | gc.TFLOAT64:
-		gc.Fatalf("gmove UINT64, TFLOAT not implemented")
-		return
-
-		/*
-		 * float to float
-		 */
-	case gc.TFLOAT32<<16 | gc.TFLOAT32:
-		a = arm.AMOVF
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT64:
-		a = arm.AMOVD
-
-	case gc.TFLOAT32<<16 | gc.TFLOAT64:
-		var r1 gc.Node
-		gc.Regalloc(&r1, gc.Types[gc.TFLOAT64], t)
-		gins(arm.AMOVF, f, &r1)
-		gins(arm.AMOVFD, &r1, &r1)
-		gins(arm.AMOVD, &r1, t)
-		gc.Regfree(&r1)
-		return
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT32:
-		var r1 gc.Node
-		gc.Regalloc(&r1, gc.Types[gc.TFLOAT64], t)
-		gins(arm.AMOVD, f, &r1)
-		gins(arm.AMOVDF, &r1, &r1)
-		gins(arm.AMOVF, &r1, t)
-		gc.Regfree(&r1)
-		return
-	}
-
-	gins(a, f, t)
-	return
-
-	// TODO(kaib): we almost always require a register dest anyway, this can probably be
-	// removed.
-	// requires register destination
-rdst:
-	{
-		gc.Regalloc(&r1, t.Type, t)
-
-		gins(a, f, &r1)
-		gmove(&r1, t)
-		gc.Regfree(&r1)
-		return
-	}
-
-	// requires register intermediate
-hard:
-	gc.Regalloc(&r1, cvt, t)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-
-	// truncate 64 bit integer
-trunc64:
-	var fhi gc.Node
-	var flo gc.Node
-	split64(f, &flo, &fhi)
-
-	gc.Regalloc(&r1, t.Type, nil)
-	gins(a, &flo, &r1)
-	gins(a, &r1, t)
-	gc.Regfree(&r1)
-	splitclean()
-	return
-}
-
-func samaddr(f *gc.Node, t *gc.Node) bool {
-	if f.Op != t.Op {
-		return false
-	}
-
-	switch f.Op {
-	case gc.OREGISTER:
-		if f.Reg != t.Reg {
-			break
-		}
-		return true
-	}
-
-	return false
-}
-
-/*
- * generate one instruction:
- *	as f, t
- */
-func gins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
-	//	Node nod;
-	//	int32 v;
-
-	if f != nil && f.Op == gc.OINDEX {
-		gc.Fatalf("gins OINDEX not implemented")
-	}
-
-	//		gc.Regalloc(&nod, &regnode, Z);
-	//		v = constnode.vconst;
-	//		gc.Cgen(f->right, &nod);
-	//		constnode.vconst = v;
-	//		idx.reg = nod.reg;
-	//		gc.Regfree(&nod);
-	if t != nil && t.Op == gc.OINDEX {
-		gc.Fatalf("gins OINDEX not implemented")
-	}
-
-	//		gc.Regalloc(&nod, &regnode, Z);
-	//		v = constnode.vconst;
-	//		gc.Cgen(t->right, &nod);
-	//		constnode.vconst = v;
-	//		idx.reg = nod.reg;
-	//		gc.Regfree(&nod);
-
-	p := gc.Prog(as)
-	gc.Naddr(&p.From, f)
-	gc.Naddr(&p.To, t)
-
-	switch as {
-	case arm.ABL:
-		if p.To.Type == obj.TYPE_REG {
-			p.To.Type = obj.TYPE_MEM
-		}
-
-	case arm.ACMP, arm.ACMPF, arm.ACMPD:
-		if t != nil {
-			if f.Op != gc.OREGISTER {
-				/* generate a comparison
-				TODO(kaib): one of the args can actually be a small constant. relax the constraint and fix call sites.
-				*/
-				gc.Fatalf("bad operands to gcmp")
-			}
-			p.From = p.To
-			p.To = obj.Addr{}
-			raddr(f, p)
-		}
-
-	case arm.AMULU:
-		if f != nil && f.Op != gc.OREGISTER {
-			gc.Fatalf("bad operands to mul")
-		}
-
-	case arm.AMOVW:
-		if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR || p.From.Type == obj.TYPE_CONST) && (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) {
-			gc.Fatalf("gins double memory")
-		}
-
-	case arm.AADD:
-		if p.To.Type == obj.TYPE_MEM {
-			gc.Fatalf("gins arith to mem")
-		}
-
-	case arm.ARSB:
-		if p.From.Type == obj.TYPE_NONE {
-			gc.Fatalf("rsb with no from")
-		}
-	}
-
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("%v\n", p)
-	}
-	return p
-}
-
-/*
- * insert n into reg slot of p
- */
-func raddr(n *gc.Node, p *obj.Prog) {
-	var a obj.Addr
-	gc.Naddr(&a, n)
-	if a.Type != obj.TYPE_REG {
-		if n != nil {
-			gc.Fatalf("bad in raddr: %v", n.Op)
-		} else {
-			gc.Fatalf("bad in raddr: <null>")
-		}
-		p.Reg = 0
-	} else {
-		p.Reg = a.Reg
-	}
-}
-
-/* generate a constant shift
- * arm encodes a shift by 32 as 0, thus asking for 0 shift is illegal.
- */
-func gshift(as obj.As, lhs *gc.Node, stype int32, sval int32, rhs *gc.Node) *obj.Prog {
-	if sval <= 0 || sval > 32 {
-		gc.Fatalf("bad shift value: %d", sval)
-	}
-
-	sval = sval & 0x1f
-
-	p := gins(as, nil, rhs)
-	p.From.Type = obj.TYPE_SHIFT
-	p.From.Offset = int64(stype) | int64(sval)<<7 | int64(lhs.Reg)&15
-	return p
-}
-
-/* generate a register shift
- */
-func gregshift(as obj.As, lhs *gc.Node, stype int32, reg *gc.Node, rhs *gc.Node) *obj.Prog {
-	p := gins(as, nil, rhs)
-	p.From.Type = obj.TYPE_SHIFT
-	p.From.Offset = int64(stype) | (int64(reg.Reg)&15)<<8 | 1<<4 | int64(lhs.Reg)&15
-	return p
-}
-
-/*
- * return Axxx for Oxxx on type t.
- */
-func optoas(op gc.Op, t *gc.Type) obj.As {
-	if t == nil {
-		gc.Fatalf("optoas: t is nil")
-	}
-
-	// avoid constant conversions in switches below
-	const (
-		OMINUS_ = uint32(gc.OMINUS) << 16
-		OLSH_   = uint32(gc.OLSH) << 16
-		ORSH_   = uint32(gc.ORSH) << 16
-		OADD_   = uint32(gc.OADD) << 16
-		OSUB_   = uint32(gc.OSUB) << 16
-		OMUL_   = uint32(gc.OMUL) << 16
-		ODIV_   = uint32(gc.ODIV) << 16
-		OMOD_   = uint32(gc.OMOD) << 16
-		OOR_    = uint32(gc.OOR) << 16
-		OAND_   = uint32(gc.OAND) << 16
-		OXOR_   = uint32(gc.OXOR) << 16
-		OEQ_    = uint32(gc.OEQ) << 16
-		ONE_    = uint32(gc.ONE) << 16
-		OLT_    = uint32(gc.OLT) << 16
-		OLE_    = uint32(gc.OLE) << 16
-		OGE_    = uint32(gc.OGE) << 16
-		OGT_    = uint32(gc.OGT) << 16
-		OCMP_   = uint32(gc.OCMP) << 16
-		OPS_    = uint32(gc.OPS) << 16
-		OAS_    = uint32(gc.OAS) << 16
-		OSQRT_  = uint32(gc.OSQRT) << 16
-	)
-
-	a := obj.AXXX
-	switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
-	default:
-		gc.Fatalf("optoas: no entry %v-%v etype %v simtype %v", op, t, gc.Types[t.Etype], gc.Types[gc.Simtype[t.Etype]])
-
-		/*	case CASE(OADDR, TPTR32):
-				a = ALEAL;
-				break;
-
-			case CASE(OADDR, TPTR64):
-				a = ALEAQ;
-				break;
-		*/
-	// TODO(kaib): make sure the conditional branches work on all edge cases
-	case OEQ_ | gc.TBOOL,
-		OEQ_ | gc.TINT8,
-		OEQ_ | gc.TUINT8,
-		OEQ_ | gc.TINT16,
-		OEQ_ | gc.TUINT16,
-		OEQ_ | gc.TINT32,
-		OEQ_ | gc.TUINT32,
-		OEQ_ | gc.TINT64,
-		OEQ_ | gc.TUINT64,
-		OEQ_ | gc.TPTR32,
-		OEQ_ | gc.TPTR64,
-		OEQ_ | gc.TFLOAT32,
-		OEQ_ | gc.TFLOAT64:
-		a = arm.ABEQ
-
-	case ONE_ | gc.TBOOL,
-		ONE_ | gc.TINT8,
-		ONE_ | gc.TUINT8,
-		ONE_ | gc.TINT16,
-		ONE_ | gc.TUINT16,
-		ONE_ | gc.TINT32,
-		ONE_ | gc.TUINT32,
-		ONE_ | gc.TINT64,
-		ONE_ | gc.TUINT64,
-		ONE_ | gc.TPTR32,
-		ONE_ | gc.TPTR64,
-		ONE_ | gc.TFLOAT32,
-		ONE_ | gc.TFLOAT64:
-		a = arm.ABNE
-
-	case OLT_ | gc.TINT8,
-		OLT_ | gc.TINT16,
-		OLT_ | gc.TINT32,
-		OLT_ | gc.TINT64,
-		OLT_ | gc.TFLOAT32,
-		OLT_ | gc.TFLOAT64:
-		a = arm.ABLT
-
-	case OLT_ | gc.TUINT8,
-		OLT_ | gc.TUINT16,
-		OLT_ | gc.TUINT32,
-		OLT_ | gc.TUINT64:
-		a = arm.ABLO
-
-	case OLE_ | gc.TINT8,
-		OLE_ | gc.TINT16,
-		OLE_ | gc.TINT32,
-		OLE_ | gc.TINT64,
-		OLE_ | gc.TFLOAT32,
-		OLE_ | gc.TFLOAT64:
-		a = arm.ABLE
-
-	case OLE_ | gc.TUINT8,
-		OLE_ | gc.TUINT16,
-		OLE_ | gc.TUINT32,
-		OLE_ | gc.TUINT64:
-		a = arm.ABLS
-
-	case OGT_ | gc.TINT8,
-		OGT_ | gc.TINT16,
-		OGT_ | gc.TINT32,
-		OGT_ | gc.TINT64,
-		OGT_ | gc.TFLOAT32,
-		OGT_ | gc.TFLOAT64:
-		a = arm.ABGT
-
-	case OGT_ | gc.TUINT8,
-		OGT_ | gc.TUINT16,
-		OGT_ | gc.TUINT32,
-		OGT_ | gc.TUINT64:
-		a = arm.ABHI
-
-	case OGE_ | gc.TINT8,
-		OGE_ | gc.TINT16,
-		OGE_ | gc.TINT32,
-		OGE_ | gc.TINT64,
-		OGE_ | gc.TFLOAT32,
-		OGE_ | gc.TFLOAT64:
-		a = arm.ABGE
-
-	case OGE_ | gc.TUINT8,
-		OGE_ | gc.TUINT16,
-		OGE_ | gc.TUINT32,
-		OGE_ | gc.TUINT64:
-		a = arm.ABHS
-
-	case OCMP_ | gc.TBOOL,
-		OCMP_ | gc.TINT8,
-		OCMP_ | gc.TUINT8,
-		OCMP_ | gc.TINT16,
-		OCMP_ | gc.TUINT16,
-		OCMP_ | gc.TINT32,
-		OCMP_ | gc.TUINT32,
-		OCMP_ | gc.TPTR32:
-		a = arm.ACMP
-
-	case OCMP_ | gc.TFLOAT32:
-		a = arm.ACMPF
-
-	case OCMP_ | gc.TFLOAT64:
-		a = arm.ACMPD
-
-	case OPS_ | gc.TFLOAT32,
-		OPS_ | gc.TFLOAT64:
-		a = arm.ABVS
-
-	case OAS_ | gc.TBOOL:
-		a = arm.AMOVB
-
-	case OAS_ | gc.TINT8:
-		a = arm.AMOVBS
-
-	case OAS_ | gc.TUINT8:
-		a = arm.AMOVBU
-
-	case OAS_ | gc.TINT16:
-		a = arm.AMOVHS
-
-	case OAS_ | gc.TUINT16:
-		a = arm.AMOVHU
-
-	case OAS_ | gc.TINT32,
-		OAS_ | gc.TUINT32,
-		OAS_ | gc.TPTR32:
-		a = arm.AMOVW
-
-	case OAS_ | gc.TFLOAT32:
-		a = arm.AMOVF
-
-	case OAS_ | gc.TFLOAT64:
-		a = arm.AMOVD
-
-	case OADD_ | gc.TINT8,
-		OADD_ | gc.TUINT8,
-		OADD_ | gc.TINT16,
-		OADD_ | gc.TUINT16,
-		OADD_ | gc.TINT32,
-		OADD_ | gc.TUINT32,
-		OADD_ | gc.TPTR32:
-		a = arm.AADD
-
-	case OADD_ | gc.TFLOAT32:
-		a = arm.AADDF
-
-	case OADD_ | gc.TFLOAT64:
-		a = arm.AADDD
-
-	case OSUB_ | gc.TINT8,
-		OSUB_ | gc.TUINT8,
-		OSUB_ | gc.TINT16,
-		OSUB_ | gc.TUINT16,
-		OSUB_ | gc.TINT32,
-		OSUB_ | gc.TUINT32,
-		OSUB_ | gc.TPTR32:
-		a = arm.ASUB
-
-	case OSUB_ | gc.TFLOAT32:
-		a = arm.ASUBF
-
-	case OSUB_ | gc.TFLOAT64:
-		a = arm.ASUBD
-
-	case OMINUS_ | gc.TINT8,
-		OMINUS_ | gc.TUINT8,
-		OMINUS_ | gc.TINT16,
-		OMINUS_ | gc.TUINT16,
-		OMINUS_ | gc.TINT32,
-		OMINUS_ | gc.TUINT32,
-		OMINUS_ | gc.TPTR32:
-		a = arm.ARSB
-
-	case OAND_ | gc.TINT8,
-		OAND_ | gc.TUINT8,
-		OAND_ | gc.TINT16,
-		OAND_ | gc.TUINT16,
-		OAND_ | gc.TINT32,
-		OAND_ | gc.TUINT32,
-		OAND_ | gc.TPTR32:
-		a = arm.AAND
-
-	case OOR_ | gc.TINT8,
-		OOR_ | gc.TUINT8,
-		OOR_ | gc.TINT16,
-		OOR_ | gc.TUINT16,
-		OOR_ | gc.TINT32,
-		OOR_ | gc.TUINT32,
-		OOR_ | gc.TPTR32:
-		a = arm.AORR
-
-	case OXOR_ | gc.TINT8,
-		OXOR_ | gc.TUINT8,
-		OXOR_ | gc.TINT16,
-		OXOR_ | gc.TUINT16,
-		OXOR_ | gc.TINT32,
-		OXOR_ | gc.TUINT32,
-		OXOR_ | gc.TPTR32:
-		a = arm.AEOR
-
-	case OLSH_ | gc.TINT8,
-		OLSH_ | gc.TUINT8,
-		OLSH_ | gc.TINT16,
-		OLSH_ | gc.TUINT16,
-		OLSH_ | gc.TINT32,
-		OLSH_ | gc.TUINT32,
-		OLSH_ | gc.TPTR32:
-		a = arm.ASLL
-
-	case ORSH_ | gc.TUINT8,
-		ORSH_ | gc.TUINT16,
-		ORSH_ | gc.TUINT32,
-		ORSH_ | gc.TPTR32:
-		a = arm.ASRL
-
-	case ORSH_ | gc.TINT8,
-		ORSH_ | gc.TINT16,
-		ORSH_ | gc.TINT32:
-		a = arm.ASRA
-
-	case OMUL_ | gc.TUINT8,
-		OMUL_ | gc.TUINT16,
-		OMUL_ | gc.TUINT32,
-		OMUL_ | gc.TPTR32:
-		a = arm.AMULU
-
-	case OMUL_ | gc.TINT8,
-		OMUL_ | gc.TINT16,
-		OMUL_ | gc.TINT32:
-		a = arm.AMUL
-
-	case OMUL_ | gc.TFLOAT32:
-		a = arm.AMULF
-
-	case OMUL_ | gc.TFLOAT64:
-		a = arm.AMULD
-
-	case ODIV_ | gc.TUINT8,
-		ODIV_ | gc.TUINT16,
-		ODIV_ | gc.TUINT32,
-		ODIV_ | gc.TPTR32:
-		a = arm.ADIVU
-
-	case ODIV_ | gc.TINT8,
-		ODIV_ | gc.TINT16,
-		ODIV_ | gc.TINT32:
-		a = arm.ADIV
-
-	case OMOD_ | gc.TUINT8,
-		OMOD_ | gc.TUINT16,
-		OMOD_ | gc.TUINT32,
-		OMOD_ | gc.TPTR32:
-		a = arm.AMODU
-
-	case OMOD_ | gc.TINT8,
-		OMOD_ | gc.TINT16,
-		OMOD_ | gc.TINT32:
-		a = arm.AMOD
-
-		//	case CASE(OEXTEND, TINT16):
-	//		a = ACWD;
-	//		break;
-
-	//	case CASE(OEXTEND, TINT32):
-	//		a = ACDQ;
-	//		break;
-
-	//	case CASE(OEXTEND, TINT64):
-	//		a = ACQO;
-	//		break;
-
-	case ODIV_ | gc.TFLOAT32:
-		a = arm.ADIVF
-
-	case ODIV_ | gc.TFLOAT64:
-		a = arm.ADIVD
-
-	case OSQRT_ | gc.TFLOAT64:
-		a = arm.ASQRTD
-	}
-
-	return a
-}
-
-const (
-	ODynam = 1 << 0
-	OPtrto = 1 << 1
-)
-
-var clean [20]gc.Node
-
-var cleani int = 0
-
-func sudoclean() {
-	if clean[cleani-1].Op != gc.OEMPTY {
-		gc.Regfree(&clean[cleani-1])
-	}
-	if clean[cleani-2].Op != gc.OEMPTY {
-		gc.Regfree(&clean[cleani-2])
-	}
-	cleani -= 2
-}
-
-func dotaddable(n *gc.Node, n1 *gc.Node) bool {
-	if n.Op != gc.ODOT {
-		return false
-	}
-
-	var oary [10]int64
-	var nn *gc.Node
-	o := gc.Dotoffset(n, oary[:], &nn)
-	if nn != nil && nn.Addable && o == 1 && oary[0] >= 0 {
-		*n1 = *nn
-		n1.Type = n.Type
-		n1.Xoffset += oary[0]
-		return true
-	}
-
-	return false
-}
-
-/*
- * generate code to compute address of n,
- * a reference to a (perhaps nested) field inside
- * an array or struct.
- * return 0 on failure, 1 on success.
- * on success, leaves usable address in a.
- *
- * caller is responsible for calling sudoclean
- * after successful sudoaddable,
- * to release the register used for a.
- */
-func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool {
-	if n.Type == nil {
-		return false
-	}
-
-	*a = obj.Addr{}
-
-	switch n.Op {
-	case gc.OLITERAL:
-		if !gc.Isconst(n, gc.CTINT) {
-			break
-		}
-		v := n.Int64()
-		if v >= 32000 || v <= -32000 {
-			break
-		}
-		switch as {
-		default:
-			return false
-
-		case arm.AADD,
-			arm.ASUB,
-			arm.AAND,
-			arm.AORR,
-			arm.AEOR,
-			arm.AMOVB,
-			arm.AMOVBS,
-			arm.AMOVBU,
-			arm.AMOVH,
-			arm.AMOVHS,
-			arm.AMOVHU,
-			arm.AMOVW:
-			break
-		}
-
-		cleani += 2
-		reg := &clean[cleani-1]
-		reg1 := &clean[cleani-2]
-		reg.Op = gc.OEMPTY
-		reg1.Op = gc.OEMPTY
-		gc.Naddr(a, n)
-		return true
-
-	case gc.ODOT,
-		gc.ODOTPTR:
-		cleani += 2
-		reg := &clean[cleani-1]
-		reg1 := &clean[cleani-2]
-		reg.Op = gc.OEMPTY
-		reg1.Op = gc.OEMPTY
-		var nn *gc.Node
-		var oary [10]int64
-		o := gc.Dotoffset(n, oary[:], &nn)
-		if nn == nil {
-			sudoclean()
-			return false
-		}
-
-		if nn.Addable && o == 1 && oary[0] >= 0 {
-			// directly addressable set of DOTs
-			n1 := *nn
-
-			n1.Type = n.Type
-			n1.Xoffset += oary[0]
-			gc.Naddr(a, &n1)
-			return true
-		}
-
-		gc.Regalloc(reg, gc.Types[gc.Tptr], nil)
-		n1 := *reg
-		n1.Op = gc.OINDREG
-		if oary[0] >= 0 {
-			gc.Agen(nn, reg)
-			n1.Xoffset = oary[0]
-		} else {
-			gc.Cgen(nn, reg)
-			gc.Cgen_checknil(reg)
-			n1.Xoffset = -(oary[0] + 1)
-		}
-
-		for i := 1; i < o; i++ {
-			if oary[i] >= 0 {
-				gc.Fatalf("can't happen")
-			}
-			gins(arm.AMOVW, &n1, reg)
-			gc.Cgen_checknil(reg)
-			n1.Xoffset = -(oary[i] + 1)
-		}
-
-		a.Type = obj.TYPE_NONE
-		a.Name = obj.NAME_NONE
-		n1.Type = n.Type
-		gc.Naddr(a, &n1)
-		return true
-
-	case gc.OINDEX:
-		return false
-	}
-
-	return false
-}
diff --git a/src/cmd/compile/internal/arm/peep.go b/src/cmd/compile/internal/arm/peep.go
deleted file mode 100644
index e1c8e4d..0000000
--- a/src/cmd/compile/internal/arm/peep.go
+++ /dev/null
@@ -1,1734 +0,0 @@
-// Inferno utils/5c/peep.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5c/peep.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package arm
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/arm"
-	"fmt"
-)
-
-var gactive uint32
-
-// UNUSED
-func peep(firstp *obj.Prog) {
-	g := gc.Flowstart(firstp, nil)
-	if g == nil {
-		return
-	}
-	gactive = 0
-
-	var p *obj.Prog
-	var t int
-loop1:
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		gc.Dumpit("loop1", g.Start, 0)
-	}
-
-	t = 0
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		/*
-		 * elide shift into TYPE_SHIFT operand of subsequent instruction
-		 */
-		//			if(shiftprop(r)) {
-		//				excise(r);
-		//				t++;
-		//				break;
-		//			}
-		case arm.ASLL,
-			arm.ASRL,
-			arm.ASRA:
-			break
-
-		case arm.AMOVB,
-			arm.AMOVH,
-			arm.AMOVW,
-			arm.AMOVF,
-			arm.AMOVD:
-			if regtyp(&p.From) {
-				if p.From.Type == p.To.Type && isfloatreg(&p.From) == isfloatreg(&p.To) {
-					if p.Scond == arm.C_SCOND_NONE {
-						if copyprop(g, r) {
-							excise(r)
-							t++
-							break
-						}
-
-						if subprop(r) && copyprop(g, r) {
-							excise(r)
-							t++
-							break
-						}
-					}
-				}
-			}
-
-		case arm.AMOVHS,
-			arm.AMOVHU,
-			arm.AMOVBS,
-			arm.AMOVBU:
-			if p.From.Type == obj.TYPE_REG {
-				if shortprop(r) {
-					t++
-				}
-			}
-		}
-	}
-
-	/*
-		if(p->scond == C_SCOND_NONE)
-		if(regtyp(&p->to))
-		if(isdconst(&p->from)) {
-			constprop(&p->from, &p->to, r->s1);
-		}
-		break;
-	*/
-	if t != 0 {
-		goto loop1
-	}
-
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		/*
-		 * EOR -1,x,y => MVN x,y
-		 */
-		case arm.AEOR:
-			if isdconst(&p.From) && p.From.Offset == -1 {
-				p.As = arm.AMVN
-				p.From.Type = obj.TYPE_REG
-				if p.Reg != 0 {
-					p.From.Reg = p.Reg
-				} else {
-					p.From.Reg = p.To.Reg
-				}
-				p.Reg = 0
-			}
-		}
-	}
-
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		case arm.AMOVW,
-			arm.AMOVB,
-			arm.AMOVBS,
-			arm.AMOVBU:
-			if p.From.Type == obj.TYPE_MEM && p.From.Offset == 0 {
-				xtramodes(g, r, &p.From)
-			} else if p.To.Type == obj.TYPE_MEM && p.To.Offset == 0 {
-				xtramodes(g, r, &p.To)
-			} else {
-				continue
-			}
-		}
-	}
-
-	//		case ACMP:
-	//			/*
-	//			 * elide CMP $0,x if calculation of x can set condition codes
-	//			 */
-	//			if(isdconst(&p->from) || p->from.offset != 0)
-	//				continue;
-	//			r2 = r->s1;
-	//			if(r2 == nil)
-	//				continue;
-	//			t = r2->prog->as;
-	//			switch(t) {
-	//			default:
-	//				continue;
-	//			case ABEQ:
-	//			case ABNE:
-	//			case ABMI:
-	//			case ABPL:
-	//				break;
-	//			case ABGE:
-	//				t = ABPL;
-	//				break;
-	//			case ABLT:
-	//				t = ABMI;
-	//				break;
-	//			case ABHI:
-	//				t = ABNE;
-	//				break;
-	//			case ABLS:
-	//				t = ABEQ;
-	//				break;
-	//			}
-	//			r1 = r;
-	//			do
-	//				r1 = uniqp(r1);
-	//			while (r1 != nil && r1->prog->as == ANOP);
-	//			if(r1 == nil)
-	//				continue;
-	//			p1 = r1->prog;
-	//			if(p1->to.type != TYPE_REG)
-	//				continue;
-	//			if(p1->to.reg != p->reg)
-	//			if(!(p1->as == AMOVW && p1->from.type == TYPE_REG && p1->from.reg == p->reg))
-	//				continue;
-	//
-	//			switch(p1->as) {
-	//			default:
-	//				continue;
-	//			case AMOVW:
-	//				if(p1->from.type != TYPE_REG)
-	//					continue;
-	//			case AAND:
-	//			case AEOR:
-	//			case AORR:
-	//			case ABIC:
-	//			case AMVN:
-	//			case ASUB:
-	//			case ARSB:
-	//			case AADD:
-	//			case AADC:
-	//			case ASBC:
-	//			case ARSC:
-	//				break;
-	//			}
-	//			p1->scond |= C_SBIT;
-	//			r2->prog->as = t;
-	//			excise(r);
-	//			continue;
-
-	//	predicate(g);
-
-	gc.Flowend(g)
-}
-
-func regtyp(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_REG && (arm.REG_R0 <= a.Reg && a.Reg <= arm.REG_R15 || arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15)
-}
-
-/*
- * the idea is to substitute
- * one register for another
- * from one MOV to another
- *	MOV	a, R0
- *	ADD	b, R0	/ no use of R1
- *	MOV	R0, R1
- * would be converted to
- *	MOV	a, R1
- *	ADD	b, R1
- *	MOV	R1, R0
- * hopefully, then the former or latter MOV
- * will be eliminated by copy propagation.
- */
-func subprop(r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	if !regtyp(v1) {
-		return false
-	}
-	v2 := &p.To
-	if !regtyp(v2) {
-		return false
-	}
-	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
-		if gc.Uniqs(r) == nil {
-			break
-		}
-		p = r.Prog
-		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
-			continue
-		}
-		if p.Info.Flags&gc.Call != 0 {
-			return false
-		}
-
-		// TODO(rsc): Whatever invalidated the info should have done this call.
-		proginfo(p)
-
-		if (p.Info.Flags&gc.CanRegRead != 0) && p.To.Type == obj.TYPE_REG {
-			p.Info.Flags |= gc.RegRead
-			p.Info.Flags &^= (gc.CanRegRead | gc.RightRead)
-			p.Reg = p.To.Reg
-		}
-
-		switch p.As {
-		case arm.AMULLU,
-			arm.AMULA,
-			arm.AMVN:
-			return false
-		}
-
-		if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite {
-			if p.To.Type == v1.Type {
-				if p.To.Reg == v1.Reg {
-					if p.Scond == arm.C_SCOND_NONE {
-						copysub(&p.To, v1, v2, true)
-						if gc.Debug['P'] != 0 {
-							fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
-							if p.From.Type == v2.Type {
-								fmt.Printf(" excise")
-							}
-							fmt.Printf("\n")
-						}
-
-						for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
-							p = r.Prog
-							copysub(&p.From, v1, v2, true)
-							copysub1(p, v1, v2, true)
-							copysub(&p.To, v1, v2, true)
-							if gc.Debug['P'] != 0 {
-								fmt.Printf("%v\n", r.Prog)
-							}
-						}
-
-						v1.Reg, v2.Reg = v2.Reg, v1.Reg
-						if gc.Debug['P'] != 0 {
-							fmt.Printf("%v last\n", r.Prog)
-						}
-						return true
-					}
-				}
-			}
-		}
-
-		if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) {
-			break
-		}
-		if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) {
-			break
-		}
-	}
-
-	return false
-}
-
-/*
- * The idea is to remove redundant copies.
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	use v2	return fail
- *	-----------------
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	set v2	return success
- */
-func copyprop(g *gc.Graph, r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	v2 := &p.To
-	if copyas(v1, v2) {
-		return true
-	}
-	gactive++
-	return copy1(v1, v2, r0.S1, false)
-}
-
-func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool {
-	if uint32(r.Active) == gactive {
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("act set; return 1\n")
-		}
-		return true
-	}
-
-	r.Active = int32(gactive)
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f)
-	}
-	for ; r != nil; r = r.S1 {
-		p := r.Prog
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("%v", p)
-		}
-		if !f && gc.Uniqp(r) == nil {
-			f = true
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; merge; f=%v", f)
-			}
-		}
-
-		switch t := copyu(p, v2, nil); t {
-		case 2: /* rar, can't split */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %vrar; return 0\n", gc.Ctxt.Dconv(v2))
-			}
-			return false
-
-		case 3: /* set */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %vset; return 1\n", gc.Ctxt.Dconv(v2))
-			}
-			return true
-
-		case 1, /* used, substitute */
-			4: /* use and set */
-			if f {
-				if gc.Debug['P'] == 0 {
-					return false
-				}
-				if t == 4 {
-					fmt.Printf("; %vused+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				} else {
-					fmt.Printf("; %vused and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				}
-				return false
-			}
-
-			if copyu(p, v2, v1) != 0 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; sub fail; return 0\n")
-				}
-				return false
-			}
-
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1))
-			}
-			if t == 4 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %vused+set; return 1\n", gc.Ctxt.Dconv(v2))
-				}
-				return true
-			}
-		}
-
-		if !f {
-			t := copyu(p, v1, nil)
-			if t == 2 || t == 3 || t == 4 {
-				f = true
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %vset and !f; f=%v", gc.Ctxt.Dconv(v1), f)
-				}
-			}
-		}
-
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("\n")
-		}
-		if r.S2 != nil {
-			if !copy1(v1, v2, r.S2, f) {
-				return false
-			}
-		}
-	}
-	return true
-}
-
-// UNUSED
-/*
- * The idea is to remove redundant constants.
- *	$c1->v1
- *	($c1->v2 s/$c1/v1)*
- *	set v1  return
- * The v1->v2 should be eliminated by copy propagation.
- */
-func constprop(c1 *obj.Addr, v1 *obj.Addr, r *gc.Flow) {
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("constprop %v->%v\n", gc.Ctxt.Dconv(c1), gc.Ctxt.Dconv(v1))
-	}
-	var p *obj.Prog
-	for ; r != nil; r = r.S1 {
-		p = r.Prog
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("%v", p)
-		}
-		if gc.Uniqp(r) == nil {
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; merge; return\n")
-			}
-			return
-		}
-
-		if p.As == arm.AMOVW && copyas(&p.From, c1) {
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(&p.From), gc.Ctxt.Dconv(v1))
-			}
-			p.From = *v1
-		} else if copyu(p, v1, nil) > 1 {
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %vset; return\n", gc.Ctxt.Dconv(v1))
-			}
-			return
-		}
-
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("\n")
-		}
-		if r.S2 != nil {
-			constprop(c1, v1, r.S2)
-		}
-	}
-}
-
-/*
- * shortprop eliminates redundant zero/sign extensions.
- *
- *   MOVBS x, R
- *   <no use R>
- *   MOVBS R, R'
- *
- * changed to
- *
- *   MOVBS x, R
- *   ...
- *   MOVB  R, R' (compiled to mov)
- *
- * MOVBS above can be a MOVBS, MOVBU, MOVHS or MOVHU.
- */
-func shortprop(r *gc.Flow) bool {
-	p := r.Prog
-	r1 := findpre(r, &p.From)
-	if r1 == nil {
-		return false
-	}
-
-	p1 := r1.Prog
-	if p1.As == p.As {
-		// Two consecutive extensions.
-		goto gotit
-	}
-
-	if p1.As == arm.AMOVW && isdconst(&p1.From) && p1.From.Offset >= 0 && p1.From.Offset < 128 {
-		// Loaded an immediate.
-		goto gotit
-	}
-
-	return false
-
-gotit:
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("shortprop\n%v\n%v", p1, p)
-	}
-	switch p.As {
-	case arm.AMOVBS,
-		arm.AMOVBU:
-		p.As = arm.AMOVB
-
-	case arm.AMOVHS,
-		arm.AMOVHU:
-		p.As = arm.AMOVH
-	}
-
-	if gc.Debug['P'] != 0 {
-		fmt.Printf(" => %v\n", obj.Aconv(p.As))
-	}
-	return true
-}
-
-// UNUSED
-/*
- * ASLL x,y,w
- * .. (not use w, not set x y w)
- * AXXX w,a,b (a != w)
- * .. (not use w)
- * (set w)
- * ----------- changed to
- * ..
- * AXXX (x<<y),a,b
- * ..
- */
-func shiftprop(r *gc.Flow) bool {
-	p := r.Prog
-	if p.To.Type != obj.TYPE_REG {
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("\tBOTCH: result not reg; FAILURE\n")
-		}
-		return false
-	}
-
-	n := p.To.Reg
-	var a obj.Addr
-	if p.Reg != 0 && p.Reg != p.To.Reg {
-		a.Type = obj.TYPE_REG
-		a.Reg = p.Reg
-	}
-
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("shiftprop\n%v", p)
-	}
-	r1 := r
-	var p1 *obj.Prog
-	for {
-		/* find first use of shift result; abort if shift operands or result are changed */
-		r1 = gc.Uniqs(r1)
-
-		if r1 == nil {
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("\tbranch; FAILURE\n")
-			}
-			return false
-		}
-
-		if gc.Uniqp(r1) == nil {
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("\tmerge; FAILURE\n")
-			}
-			return false
-		}
-
-		p1 = r1.Prog
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("\n%v", p1)
-		}
-		switch copyu(p1, &p.To, nil) {
-		case 0: /* not used or set */
-			if (p.From.Type == obj.TYPE_REG && copyu(p1, &p.From, nil) > 1) || (a.Type == obj.TYPE_REG && copyu(p1, &a, nil) > 1) {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("\targs modified; FAILURE\n")
-				}
-				return false
-			}
-
-			continue
-		case 3: /* set, not used */
-			{
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("\tBOTCH: noref; FAILURE\n")
-				}
-				return false
-			}
-		}
-
-		break
-	}
-
-	/* check whether substitution can be done */
-	switch p1.As {
-	default:
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("\tnon-dpi; FAILURE\n")
-		}
-		return false
-
-	case arm.AAND,
-		arm.AEOR,
-		arm.AADD,
-		arm.AADC,
-		arm.AORR,
-		arm.ASUB,
-		arm.ASBC,
-		arm.ARSB,
-		arm.ARSC:
-		if p1.Reg == n || (p1.Reg == 0 && p1.To.Type == obj.TYPE_REG && p1.To.Reg == n) {
-			if p1.From.Type != obj.TYPE_REG {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("\tcan't swap; FAILURE\n")
-				}
-				return false
-			}
-
-			p1.Reg = p1.From.Reg
-			p1.From.Reg = n
-			switch p1.As {
-			case arm.ASUB:
-				p1.As = arm.ARSB
-
-			case arm.ARSB:
-				p1.As = arm.ASUB
-
-			case arm.ASBC:
-				p1.As = arm.ARSC
-
-			case arm.ARSC:
-				p1.As = arm.ASBC
-			}
-
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("\t=>%v", p1)
-			}
-		}
-		fallthrough
-
-	case arm.ABIC,
-		arm.ATST,
-		arm.ACMP,
-		arm.ACMN:
-		if p1.Reg == n {
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("\tcan't swap; FAILURE\n")
-			}
-			return false
-		}
-
-		if p1.Reg == 0 && p1.To.Reg == n {
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("\tshift result used twice; FAILURE\n")
-			}
-			return false
-		}
-
-		//	case AMVN:
-		if p1.From.Type == obj.TYPE_SHIFT {
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("\tshift result used in shift; FAILURE\n")
-			}
-			return false
-		}
-
-		if p1.From.Type != obj.TYPE_REG || p1.From.Reg != n {
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("\tBOTCH: where is it used?; FAILURE\n")
-			}
-			return false
-		}
-	}
-
-	/* check whether shift result is used subsequently */
-	p2 := p1
-
-	if p1.To.Reg != n {
-		var p1 *obj.Prog
-		for {
-			r1 = gc.Uniqs(r1)
-			if r1 == nil {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("\tinconclusive; FAILURE\n")
-				}
-				return false
-			}
-
-			p1 = r1.Prog
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("\n%v", p1)
-			}
-			switch copyu(p1, &p.To, nil) {
-			case 0: /* not used or set */
-				continue
-
-			case 3: /* set, not used */
-				break
-
-			default: /* used */
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("\treused; FAILURE\n")
-				}
-				return false
-			}
-
-			break
-		}
-	}
-
-	/* make the substitution */
-	p2.From.Reg = 0
-	o := p.Reg
-	if o == 0 {
-		o = p.To.Reg
-	}
-	o &= 15
-
-	switch p.From.Type {
-	case obj.TYPE_CONST:
-		o |= int16(p.From.Offset&0x1f) << 7
-
-	case obj.TYPE_REG:
-		o |= 1<<4 | (p.From.Reg&15)<<8
-	}
-
-	switch p.As {
-	case arm.ASLL:
-		o |= 0 << 5
-
-	case arm.ASRL:
-		o |= 1 << 5
-
-	case arm.ASRA:
-		o |= 2 << 5
-	}
-
-	p2.From = obj.Addr{}
-	p2.From.Type = obj.TYPE_SHIFT
-	p2.From.Offset = int64(o)
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("\t=>%v\tSUCCEED\n", p2)
-	}
-	return true
-}
-
-/*
- * findpre returns the last instruction mentioning v
- * before r. It must be a set, and there must be
- * a unique path from that instruction to r.
- */
-func findpre(r *gc.Flow, v *obj.Addr) *gc.Flow {
-	var r1 *gc.Flow
-
-	for r1 = gc.Uniqp(r); r1 != nil; r, r1 = r1, gc.Uniqp(r1) {
-		if gc.Uniqs(r1) != r {
-			return nil
-		}
-		switch copyu(r1.Prog, v, nil) {
-		case 1, /* used */
-			2: /* read-alter-rewrite */
-			return nil
-
-		case 3, /* set */
-			4: /* set and used */
-			return r1
-		}
-	}
-
-	return nil
-}
-
-/*
- * findinc finds ADD instructions with a constant
- * argument which falls within the immed_12 range.
- */
-func findinc(r *gc.Flow, r2 *gc.Flow, v *obj.Addr) *gc.Flow {
-	var r1 *gc.Flow
-	var p *obj.Prog
-
-	for r1 = gc.Uniqs(r); r1 != nil && r1 != r2; r, r1 = r1, gc.Uniqs(r1) {
-		if gc.Uniqp(r1) != r {
-			return nil
-		}
-		switch copyu(r1.Prog, v, nil) {
-		case 0: /* not touched */
-			continue
-
-		case 4: /* set and used */
-			p = r1.Prog
-
-			if p.As == arm.AADD {
-				if isdconst(&p.From) {
-					if p.From.Offset > -4096 && p.From.Offset < 4096 {
-						return r1
-					}
-				}
-			}
-			fallthrough
-
-		default:
-			return nil
-		}
-	}
-
-	return nil
-}
-
-func nochange(r *gc.Flow, r2 *gc.Flow, p *obj.Prog) bool {
-	if r == r2 {
-		return true
-	}
-	n := int(0)
-	var a [3]obj.Addr
-	if p.Reg != 0 && p.Reg != p.To.Reg {
-		a[n].Type = obj.TYPE_REG
-		a[n].Reg = p.Reg
-		n++
-	}
-
-	switch p.From.Type {
-	case obj.TYPE_SHIFT:
-		a[n].Type = obj.TYPE_REG
-		a[n].Reg = int16(arm.REG_R0 + (p.From.Offset & 0xf))
-		n++
-		fallthrough
-
-	case obj.TYPE_REG:
-		a[n].Type = obj.TYPE_REG
-		a[n].Reg = p.From.Reg
-		n++
-	}
-
-	if n == 0 {
-		return true
-	}
-	var i int
-	for ; r != nil && r != r2; r = gc.Uniqs(r) {
-		p = r.Prog
-		for i = 0; i < n; i++ {
-			if copyu(p, &a[i], nil) > 1 {
-				return false
-			}
-		}
-	}
-
-	return true
-}
-
-func findu1(r *gc.Flow, v *obj.Addr) bool {
-	for ; r != nil; r = r.S1 {
-		if r.Active != 0 {
-			return false
-		}
-		r.Active = 1
-		switch copyu(r.Prog, v, nil) {
-		case 1, /* used */
-			2, /* read-alter-rewrite */
-			4: /* set and used */
-			return true
-
-		case 3: /* set */
-			return false
-		}
-
-		if r.S2 != nil {
-			if findu1(r.S2, v) {
-				return true
-			}
-		}
-	}
-
-	return false
-}
-
-func finduse(g *gc.Graph, r *gc.Flow, v *obj.Addr) bool {
-	for r1 := g.Start; r1 != nil; r1 = r1.Link {
-		r1.Active = 0
-	}
-	return findu1(r, v)
-}
-
-/*
- * xtramodes enables the ARM post increment and
- * shift offset addressing modes to transform
- *   MOVW   0(R3),R1
- *   ADD    $4,R3,R3
- * into
- *   MOVW.P 4(R3),R1
- * and
- *   ADD    R0,R1
- *   MOVBU  0(R1),R0
- * into
- *   MOVBU  R0<<0(R1),R0
- */
-func xtramodes(g *gc.Graph, r *gc.Flow, a *obj.Addr) bool {
-	p := r.Prog
-	v := *a
-	v.Type = obj.TYPE_REG
-	r1 := findpre(r, &v)
-	if r1 != nil {
-		p1 := r1.Prog
-		if p1.To.Type == obj.TYPE_REG && p1.To.Reg == v.Reg {
-			switch p1.As {
-			case arm.AADD:
-				if p1.Scond&arm.C_SBIT != 0 {
-					// avoid altering ADD.S/ADC sequences.
-					break
-				}
-
-				if p1.From.Type == obj.TYPE_REG || (p1.From.Type == obj.TYPE_SHIFT && p1.From.Offset&(1<<4) == 0 && ((p.As != arm.AMOVB && p.As != arm.AMOVBS) || (a == &p.From && p1.From.Offset&^0xf == 0))) || ((p1.From.Type == obj.TYPE_ADDR || p1.From.Type == obj.TYPE_CONST) && p1.From.Offset > -4096 && p1.From.Offset < 4096) {
-					if nochange(gc.Uniqs(r1), r, p1) {
-						if a != &p.From || v.Reg != p.To.Reg {
-							if finduse(g, r.S1, &v) {
-								if p1.Reg == 0 || p1.Reg == v.Reg {
-									/* pre-indexing */
-									p.Scond |= arm.C_WBIT
-								} else {
-									return false
-								}
-							}
-						}
-
-						switch p1.From.Type {
-						/* register offset */
-						case obj.TYPE_REG:
-							if gc.Nacl {
-								return false
-							}
-							*a = obj.Addr{}
-							a.Type = obj.TYPE_SHIFT
-							a.Offset = int64(p1.From.Reg) & 15
-
-							/* scaled register offset */
-						case obj.TYPE_SHIFT:
-							if gc.Nacl {
-								return false
-							}
-							*a = obj.Addr{}
-							a.Type = obj.TYPE_SHIFT
-							fallthrough
-
-							/* immediate offset */
-						case obj.TYPE_CONST,
-							obj.TYPE_ADDR:
-							a.Offset = p1.From.Offset
-						}
-
-						if p1.Reg != 0 {
-							a.Reg = p1.Reg
-						}
-						excise(r1)
-						return true
-					}
-				}
-
-			case arm.AMOVW:
-				if p1.From.Type == obj.TYPE_REG {
-					r2 := findinc(r1, r, &p1.From)
-					if r2 != nil {
-						var r3 *gc.Flow
-						for r3 = gc.Uniqs(r2); r3.Prog.As == obj.ANOP; r3 = gc.Uniqs(r3) {
-						}
-						if r3 == r {
-							/* post-indexing */
-							p1 := r2.Prog
-
-							a.Reg = p1.To.Reg
-							a.Offset = p1.From.Offset
-							p.Scond |= arm.C_PBIT
-							if !finduse(g, r, &r1.Prog.To) {
-								excise(r1)
-							}
-							excise(r2)
-							return true
-						}
-					}
-				}
-			}
-		}
-	}
-
-	if a != &p.From || a.Reg != p.To.Reg {
-		r1 := findinc(r, nil, &v)
-		if r1 != nil {
-			/* post-indexing */
-			p1 := r1.Prog
-
-			a.Offset = p1.From.Offset
-			p.Scond |= arm.C_PBIT
-			excise(r1)
-			return true
-		}
-	}
-
-	return false
-}
-
-/*
- * return
- * 1 if v only used (and substitute),
- * 2 if read-alter-rewrite
- * 3 if set
- * 4 if set and used
- * 0 otherwise (not touched)
- */
-func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
-	switch p.As {
-	default:
-		fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As))
-		return 2
-
-	case arm.AMOVM:
-		if v.Type != obj.TYPE_REG {
-			return 0
-		}
-		if p.From.Type == obj.TYPE_CONST { /* read reglist, read/rar */
-			if s != nil {
-				if p.From.Offset&(1<<uint(v.Reg)) != 0 {
-					return 1
-				}
-				if copysub(&p.To, v, s, true) {
-					return 1
-				}
-				return 0
-			}
-
-			if copyau(&p.To, v) {
-				if p.Scond&arm.C_WBIT != 0 {
-					return 2
-				}
-				return 1
-			}
-
-			if p.From.Offset&(1<<uint(v.Reg)) != 0 {
-				return 1 /* read/rar, write reglist */
-			}
-		} else {
-			if s != nil {
-				if p.To.Offset&(1<<uint(v.Reg)) != 0 {
-					return 1
-				}
-				if copysub(&p.From, v, s, true) {
-					return 1
-				}
-				return 0
-			}
-
-			if copyau(&p.From, v) {
-				if p.Scond&arm.C_WBIT != 0 {
-					return 2
-				}
-				if p.To.Offset&(1<<uint(v.Reg)) != 0 {
-					return 4
-				}
-				return 1
-			}
-
-			if p.To.Offset&(1<<uint(v.Reg)) != 0 {
-				return 3
-			}
-		}
-
-		return 0
-
-	case obj.ANOP, /* read,, write */
-		arm.ASQRTD,
-		arm.AMOVW,
-		arm.AMOVF,
-		arm.AMOVD,
-		arm.AMOVH,
-		arm.AMOVHS,
-		arm.AMOVHU,
-		arm.AMOVB,
-		arm.AMOVBS,
-		arm.AMOVBU,
-		arm.AMOVFW,
-		arm.AMOVWF,
-		arm.AMOVDW,
-		arm.AMOVWD,
-		arm.AMOVFD,
-		arm.AMOVDF:
-		if p.Scond&(arm.C_WBIT|arm.C_PBIT) != 0 {
-			if v.Type == obj.TYPE_REG {
-				if p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_SHIFT {
-					if p.From.Reg == v.Reg {
-						return 2
-					}
-				} else {
-					if p.To.Reg == v.Reg {
-						return 2
-					}
-				}
-			}
-		}
-
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if !copyas(&p.To, v) {
-				if copysub(&p.To, v, s, true) {
-					return 1
-				}
-			}
-			return 0
-		}
-
-		if copyas(&p.To, v) {
-			if p.Scond != arm.C_SCOND_NONE {
-				return 2
-			}
-			if copyau(&p.From, v) {
-				return 4
-			}
-			return 3
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case arm.AMULLU, /* read, read, write, write */
-		arm.AMULL,
-		arm.AMULA,
-		arm.AMVN:
-		return 2
-
-	case arm.AADD, /* read, read, write */
-		arm.AADC,
-		arm.ASUB,
-		arm.ASBC,
-		arm.ARSB,
-		arm.ASLL,
-		arm.ASRL,
-		arm.ASRA,
-		arm.AORR,
-		arm.AAND,
-		arm.AEOR,
-		arm.AMUL,
-		arm.AMULU,
-		arm.ADIV,
-		arm.ADIVU,
-		arm.AMOD,
-		arm.AMODU,
-		arm.AADDF,
-		arm.AADDD,
-		arm.ASUBF,
-		arm.ASUBD,
-		arm.AMULF,
-		arm.AMULD,
-		arm.ADIVF,
-		arm.ADIVD,
-		obj.ACHECKNIL,
-		/* read */
-		arm.ACMPF, /* read, read, */
-		arm.ACMPD,
-		arm.ACMP,
-		arm.ACMN,
-		arm.ATST:
-		/* read,, */
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if copysub1(p, v, s, true) {
-				return 1
-			}
-			if !copyas(&p.To, v) {
-				if copysub(&p.To, v, s, true) {
-					return 1
-				}
-			}
-			return 0
-		}
-
-		if copyas(&p.To, v) {
-			if p.Scond != arm.C_SCOND_NONE {
-				return 2
-			}
-			if p.Reg == 0 {
-				p.Reg = p.To.Reg
-			}
-			if copyau(&p.From, v) {
-				return 4
-			}
-			if copyau1(p, v) {
-				return 4
-			}
-			return 3
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau1(p, v) {
-			return 1
-		}
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case arm.ABEQ, /* read, read */
-		arm.ABNE,
-		arm.ABCS,
-		arm.ABHS,
-		arm.ABCC,
-		arm.ABLO,
-		arm.ABMI,
-		arm.ABPL,
-		arm.ABVS,
-		arm.ABVC,
-		arm.ABHI,
-		arm.ABLS,
-		arm.ABGE,
-		arm.ABLT,
-		arm.ABGT,
-		arm.ABLE:
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if copysub1(p, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau1(p, v) {
-			return 1
-		}
-		return 0
-
-	case arm.AB: /* funny */
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case obj.ARET: /* funny */
-		if s != nil {
-			return 1
-		}
-		return 3
-
-	case arm.ABL: /* funny */
-		if v.Type == obj.TYPE_REG {
-			// TODO(rsc): REG_R0 and REG_F0 used to be
-			// (when register numbers started at 0) exregoffset and exfregoffset,
-			// which are unset entirely.
-			// It's strange that this handles R0 and F0 differently from the other
-			// registers. Possible failure to optimize?
-			if arm.REG_R0 < v.Reg && v.Reg <= arm.REGEXT {
-				return 2
-			}
-			if v.Reg == arm.REGARG {
-				return 2
-			}
-			if arm.REG_F0 < v.Reg && v.Reg <= arm.FREGEXT {
-				return 2
-			}
-		}
-
-		if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
-			return 2
-		}
-
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.To, v) {
-			return 4
-		}
-		return 3
-
-	// R0 is zero, used by DUFFZERO, cannot be substituted.
-	// R1 is ptr to memory, used and set, cannot be substituted.
-	case obj.ADUFFZERO:
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == arm.REG_R0 {
-				return 1
-			}
-			if v.Reg == arm.REG_R0+1 {
-				return 2
-			}
-		}
-
-		return 0
-
-	// R0 is scratch, set by DUFFCOPY, cannot be substituted.
-	// R1, R2 areptr to src, dst, used and set, cannot be substituted.
-	case obj.ADUFFCOPY:
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == arm.REG_R0 {
-				return 3
-			}
-			if v.Reg == arm.REG_R0+1 || v.Reg == arm.REG_R0+2 {
-				return 2
-			}
-		}
-
-		return 0
-
-	case obj.ATEXT: /* funny */
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == arm.REGARG {
-				return 3
-			}
-		}
-		return 0
-
-	case obj.APCDATA,
-		obj.AFUNCDATA,
-		obj.AVARDEF,
-		obj.AVARKILL,
-		obj.AVARLIVE,
-		obj.AUSEFIELD:
-		return 0
-	}
-}
-
-/*
- * direct reference,
- * could be set/use depending on
- * semantics
- */
-func copyas(a *obj.Addr, v *obj.Addr) bool {
-	if regtyp(v) {
-		if a.Type == v.Type {
-			if a.Reg == v.Reg {
-				return true
-			}
-		}
-	} else if v.Type == obj.TYPE_CONST { /* for constprop */
-		if a.Type == v.Type {
-			if a.Name == v.Name {
-				if a.Sym == v.Sym {
-					if a.Reg == v.Reg {
-						if a.Offset == v.Offset {
-							return true
-						}
-					}
-				}
-			}
-		}
-	}
-
-	return false
-}
-
-func sameaddr(a *obj.Addr, v *obj.Addr) bool {
-	if a.Type != v.Type {
-		return false
-	}
-	if regtyp(v) && a.Reg == v.Reg {
-		return true
-	}
-
-	// TODO(rsc): Change v->type to v->name and enable.
-	//if(v->type == NAME_AUTO || v->type == NAME_PARAM) {
-	//	if(v->offset == a->offset)
-	//		return 1;
-	//}
-	return false
-}
-
-/*
- * either direct or indirect
- */
-func copyau(a *obj.Addr, v *obj.Addr) bool {
-	if copyas(a, v) {
-		return true
-	}
-	if v.Type == obj.TYPE_REG {
-		if a.Type == obj.TYPE_ADDR && a.Reg != 0 {
-			if a.Reg == v.Reg {
-				return true
-			}
-		} else if a.Type == obj.TYPE_MEM {
-			if a.Reg == v.Reg {
-				return true
-			}
-		} else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 {
-			if a.Reg == v.Reg {
-				return true
-			}
-			if a.Offset == int64(v.Reg) {
-				return true
-			}
-		} else if a.Type == obj.TYPE_SHIFT {
-			if a.Offset&0xf == int64(v.Reg-arm.REG_R0) {
-				return true
-			}
-			if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) {
-				return true
-			}
-		}
-	}
-
-	return false
-}
-
-/*
- * compare v to the center
- * register in p (p->reg)
- */
-func copyau1(p *obj.Prog, v *obj.Addr) bool {
-	if v.Type == obj.TYPE_REG && v.Reg == 0 {
-		return false
-	}
-	return p.Reg == v.Reg
-}
-
-// copysub substitute s for v in a.
-// copysub returns true on failure to substitute.
-// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing.
-func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool {
-	if f && copyau(a, v) {
-		if a.Type == obj.TYPE_SHIFT {
-			if a.Offset&0xf == int64(v.Reg-arm.REG_R0) {
-				a.Offset = a.Offset&^0xf | int64(s.Reg)&0xf
-			}
-			if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) {
-				a.Offset = a.Offset&^(0xf<<8) | (int64(s.Reg)&0xf)<<8
-			}
-		} else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 {
-			if a.Offset == int64(v.Reg) {
-				a.Offset = int64(s.Reg)
-			}
-			if a.Reg == v.Reg {
-				a.Reg = s.Reg
-			}
-		} else {
-			a.Reg = s.Reg
-		}
-	}
-	return false
-}
-
-// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing.
-func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool {
-	if f && copyau1(p1, v) {
-		p1.Reg = s.Reg
-	}
-	return false
-}
-
-var predinfo = []struct {
-	opcode    obj.As
-	notopcode obj.As
-	scond     int
-	notscond  int
-}{
-	{arm.ABEQ, arm.ABNE, 0x0, 0x1},
-	{arm.ABNE, arm.ABEQ, 0x1, 0x0},
-	{arm.ABCS, arm.ABCC, 0x2, 0x3},
-	{arm.ABHS, arm.ABLO, 0x2, 0x3},
-	{arm.ABCC, arm.ABCS, 0x3, 0x2},
-	{arm.ABLO, arm.ABHS, 0x3, 0x2},
-	{arm.ABMI, arm.ABPL, 0x4, 0x5},
-	{arm.ABPL, arm.ABMI, 0x5, 0x4},
-	{arm.ABVS, arm.ABVC, 0x6, 0x7},
-	{arm.ABVC, arm.ABVS, 0x7, 0x6},
-	{arm.ABHI, arm.ABLS, 0x8, 0x9},
-	{arm.ABLS, arm.ABHI, 0x9, 0x8},
-	{arm.ABGE, arm.ABLT, 0xA, 0xB},
-	{arm.ABLT, arm.ABGE, 0xB, 0xA},
-	{arm.ABGT, arm.ABLE, 0xC, 0xD},
-	{arm.ABLE, arm.ABGT, 0xD, 0xC},
-}
-
-type Joininfo struct {
-	start *gc.Flow
-	last  *gc.Flow
-	end   *gc.Flow
-	len   int
-}
-
-const (
-	Join = iota
-	Split
-	End
-	Branch
-	Setcond
-	Toolong
-)
-
-const (
-	Falsecond = iota
-	Truecond
-	Delbranch
-	Keepbranch
-)
-
-func isbranch(p *obj.Prog) bool {
-	return (arm.ABEQ <= p.As) && (p.As <= arm.ABLE)
-}
-
-func predicable(p *obj.Prog) bool {
-	switch p.As {
-	case obj.ANOP,
-		obj.AXXX,
-		obj.AGLOBL,
-		obj.ATEXT,
-		arm.AWORD:
-		return false
-	}
-
-	if isbranch(p) {
-		return false
-	}
-	return true
-}
-
-/*
- * Depends on an analysis of the encodings performed by 5l.
- * These seem to be all of the opcodes that lead to the "S" bit
- * being set in the instruction encodings.
- *
- * C_SBIT may also have been set explicitly in p->scond.
- */
-func modifiescpsr(p *obj.Prog) bool {
-	switch p.As {
-	case arm.AMULLU,
-		arm.AMULA,
-		arm.AMULU,
-		arm.ADIVU,
-		arm.ATEQ,
-		arm.ACMN,
-		arm.ATST,
-		arm.ACMP,
-		arm.AMUL,
-		arm.ADIV,
-		arm.AMOD,
-		arm.AMODU,
-		arm.ABL:
-		return true
-	}
-
-	if p.Scond&arm.C_SBIT != 0 {
-		return true
-	}
-	return false
-}
-
-/*
- * Find the maximal chain of instructions starting with r which could
- * be executed conditionally
- */
-func joinsplit(r *gc.Flow, j *Joininfo) int {
-	j.start = r
-	j.last = r
-	j.len = 0
-	for {
-		if r.P2 != nil && (r.P1 != nil || r.P2.P2link != nil) {
-			j.end = r
-			return Join
-		}
-
-		if r.S1 != nil && r.S2 != nil {
-			j.end = r
-			return Split
-		}
-
-		j.last = r
-		if r.Prog.As != obj.ANOP {
-			j.len++
-		}
-		if r.S1 == nil && r.S2 == nil {
-			j.end = r.Link
-			return End
-		}
-
-		if r.S2 != nil {
-			j.end = r.S2
-			return Branch
-		}
-
-		if modifiescpsr(r.Prog) {
-			j.end = r.S1
-			return Setcond
-		}
-
-		r = r.S1
-		if j.len >= 4 {
-			break
-		}
-	}
-
-	j.end = r
-	return Toolong
-}
-
-func successor(r *gc.Flow) *gc.Flow {
-	if r.S1 != nil {
-		return r.S1
-	}
-	return r.S2
-}
-
-func applypred(rstart *gc.Flow, j *Joininfo, cond int, branch int) {
-	if j.len == 0 {
-		return
-	}
-	var pred int
-	if cond == Truecond {
-		pred = predinfo[rstart.Prog.As-arm.ABEQ].scond
-	} else {
-		pred = predinfo[rstart.Prog.As-arm.ABEQ].notscond
-	}
-
-	for r := j.start; ; r = successor(r) {
-		if r.Prog.As == arm.AB {
-			if r != j.last || branch == Delbranch {
-				excise(r)
-			} else {
-				if cond == Truecond {
-					r.Prog.As = predinfo[rstart.Prog.As-arm.ABEQ].opcode
-				} else {
-					r.Prog.As = predinfo[rstart.Prog.As-arm.ABEQ].notopcode
-				}
-			}
-		} else if predicable(r.Prog) {
-			r.Prog.Scond = uint8(int(r.Prog.Scond&^arm.C_SCOND) | pred)
-		}
-		if r.S1 != r.Link {
-			r.S1 = r.Link
-			r.Link.P1 = r
-		}
-
-		if r == j.last {
-			break
-		}
-	}
-}
-
-func predicate(g *gc.Graph) {
-	var t1 int
-	var t2 int
-	var j1 Joininfo
-	var j2 Joininfo
-
-	for r := g.Start; r != nil; r = r.Link {
-		if isbranch(r.Prog) {
-			t1 = joinsplit(r.S1, &j1)
-			t2 = joinsplit(r.S2, &j2)
-			if j1.last.Link != j2.start {
-				continue
-			}
-			if j1.end == j2.end {
-				if (t1 == Branch && (t2 == Join || t2 == Setcond)) || (t2 == Join && (t1 == Join || t1 == Setcond)) {
-					applypred(r, &j1, Falsecond, Delbranch)
-					applypred(r, &j2, Truecond, Delbranch)
-					excise(r)
-					continue
-				}
-			}
-
-			if t1 == End || t1 == Branch {
-				applypred(r, &j1, Falsecond, Keepbranch)
-				excise(r)
-				continue
-			}
-		}
-	}
-}
-
-func isdconst(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_CONST
-}
-
-func isfloatreg(a *obj.Addr) bool {
-	return arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15
-}
-
-func stackaddr(a *obj.Addr) bool {
-	return regtyp(a) && a.Reg == arm.REGSP
-}
-
-func smallindir(a *obj.Addr, reg *obj.Addr) bool {
-	return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096
-}
-
-func excise(r *gc.Flow) {
-	p := r.Prog
-	obj.Nopout(p)
-}
diff --git a/src/cmd/compile/internal/arm/prog.go b/src/cmd/compile/internal/arm/prog.go
index f69548a..1dd7c98 100644
--- a/src/cmd/compile/internal/arm/prog.go
+++ b/src/cmd/compile/internal/arm/prog.go
@@ -21,14 +21,13 @@ const (
 // Instructions not generated need not be listed.
 // As an exception to that rule, we typically write down all the
 // size variants of an operation even if we just use a subset.
-var progtable = [arm.ALAST & obj.AMask]obj.ProgInfo{
+var progtable = [arm.ALAST & obj.AMask]gc.ProgInfo{
 	obj.ATYPE:     {Flags: gc.Pseudo | gc.Skip},
 	obj.ATEXT:     {Flags: gc.Pseudo},
 	obj.AFUNCDATA: {Flags: gc.Pseudo},
 	obj.APCDATA:   {Flags: gc.Pseudo},
 	obj.AUNDEF:    {Flags: gc.Break},
 	obj.AUSEFIELD: {Flags: gc.OK},
-	obj.ACHECKNIL: {Flags: gc.LeftRead},
 	obj.AVARDEF:   {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARKILL:  {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARLIVE:  {Flags: gc.Pseudo | gc.LeftRead},
@@ -65,6 +64,7 @@ var progtable = [arm.ALAST & obj.AMask]obj.ProgInfo{
 	arm.ASRA & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	arm.ASRL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	arm.ASUB & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm.ACLZ & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite},
 	arm.ATEQ & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
 	arm.ATST & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
 
@@ -79,6 +79,8 @@ var progtable = [arm.ALAST & obj.AMask]obj.ProgInfo{
 	arm.AMULF & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | RightRdwr},
 	arm.ASUBD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | RightRdwr},
 	arm.ASUBF & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | RightRdwr},
+	arm.ANEGD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | RightRdwr},
+	arm.ANEGF & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | RightRdwr},
 	arm.ASQRTD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | RightRdwr},
 
 	// Conversions.
@@ -133,9 +135,8 @@ var progtable = [arm.ALAST & obj.AMask]obj.ProgInfo{
 	obj.ARET:             {Flags: gc.Break},
 }
 
-func proginfo(p *obj.Prog) {
-	info := &p.Info
-	*info = progtable[p.As&obj.AMask]
+func proginfo(p *obj.Prog) gc.ProgInfo {
+	info := progtable[p.As&obj.AMask]
 	if info.Flags == 0 {
 		gc.Fatalf("unknown instruction %v", p)
 	}
@@ -154,11 +155,5 @@ func proginfo(p *obj.Prog) {
 		info.Flags |= gc.RightRead
 	}
 
-	switch p.As {
-	case arm.ADIV,
-		arm.ADIVU,
-		arm.AMOD,
-		arm.AMODU:
-		info.Regset |= RtoB(arm.REG_R12)
-	}
+	return info
 }
diff --git a/src/cmd/compile/internal/arm/reg.go b/src/cmd/compile/internal/arm/reg.go
deleted file mode 100644
index 2313bc4..0000000
--- a/src/cmd/compile/internal/arm/reg.go
+++ /dev/null
@@ -1,136 +0,0 @@
-// Inferno utils/5c/reg.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5c/reg.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package arm
-
-import "cmd/internal/obj/arm"
-import "cmd/compile/internal/gc"
-
-const (
-	NREGVAR = 32
-)
-
-var regname = []string{
-	".R0",
-	".R1",
-	".R2",
-	".R3",
-	".R4",
-	".R5",
-	".R6",
-	".R7",
-	".R8",
-	".R9",
-	".R10",
-	".R11",
-	".R12",
-	".R13",
-	".R14",
-	".R15",
-	".F0",
-	".F1",
-	".F2",
-	".F3",
-	".F4",
-	".F5",
-	".F6",
-	".F7",
-	".F8",
-	".F9",
-	".F10",
-	".F11",
-	".F12",
-	".F13",
-	".F14",
-	".F15",
-}
-
-func regnames(n *int) []string {
-	*n = NREGVAR
-	return regname
-}
-
-func excludedregs() uint64 {
-	return RtoB(arm.REGSP) | RtoB(arm.REGLINK) | RtoB(arm.REGPC)
-}
-
-func doregbits(r int) uint64 {
-	return 0
-}
-
-/*
- *	bit	reg
- *	0	R0
- *	1	R1
- *	...	...
- *	10	R10
- *	12  R12
- *
- *	bit	reg
- *	18	F2
- *	19	F3
- *	...	...
- *	31	F15
- */
-func RtoB(r int) uint64 {
-	if arm.REG_R0 <= r && r <= arm.REG_R15 {
-		if r >= arm.REGTMP-2 && r != arm.REG_R12 { // excluded R9 and R10 for m and g, but not R12
-			return 0
-		}
-		return 1 << uint(r-arm.REG_R0)
-	}
-
-	if arm.REG_F0 <= r && r <= arm.REG_F15 {
-		if r < arm.REG_F2 || r > arm.REG_F0+arm.NFREG-1 {
-			return 0
-		}
-		return 1 << uint((r-arm.REG_F0)+16)
-	}
-
-	return 0
-}
-
-func BtoR(b uint64) int {
-	// TODO Allow R0 and R1, but be careful with a 0 return
-	// TODO Allow R9. Only R10 is reserved now (just g, not m).
-	b &= 0x11fc // excluded R9 and R10 for m and g, but not R12
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + arm.REG_R0
-}
-
-func BtoF(b uint64) int {
-	b &= 0xfffc0000
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) - 16 + arm.REG_F0
-}
diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go
index 8f466e3..5a69ed3 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -5,18 +5,116 @@
 package arm
 
 import (
+	"fmt"
+	"math"
+
 	"cmd/compile/internal/gc"
 	"cmd/compile/internal/ssa"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm"
 )
 
-var ssaRegToReg = []int16{
-	arm.REG_R0,
-	arm.REG_R1,
-	arm.REG_R2,
-	arm.REG_R3,
-	arm.REGSP, // aka R13
+// loadByType returns the load instruction of the given type.
+func loadByType(t ssa.Type) obj.As {
+	if t.IsFloat() {
+		switch t.Size() {
+		case 4:
+			return arm.AMOVF
+		case 8:
+			return arm.AMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			if t.IsSigned() {
+				return arm.AMOVB
+			} else {
+				return arm.AMOVBU
+			}
+		case 2:
+			if t.IsSigned() {
+				return arm.AMOVH
+			} else {
+				return arm.AMOVHU
+			}
+		case 4:
+			return arm.AMOVW
+		}
+	}
+	panic("bad load type")
+}
+
+// storeByType returns the store instruction of the given type.
+func storeByType(t ssa.Type) obj.As {
+	if t.IsFloat() {
+		switch t.Size() {
+		case 4:
+			return arm.AMOVF
+		case 8:
+			return arm.AMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			return arm.AMOVB
+		case 2:
+			return arm.AMOVH
+		case 4:
+			return arm.AMOVW
+		}
+	}
+	panic("bad store type")
+}
+
+// shift type is used as Offset in obj.TYPE_SHIFT operands to encode shifted register operands
+type shift int64
+
+// copied from ../../../internal/obj/util.go:/TYPE_SHIFT
+func (v shift) String() string {
+	op := "<<>>->@>"[((v>>5)&3)<<1:]
+	if v&(1<<4) != 0 {
+		// register shift
+		return fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
+	} else {
+		// constant shift
+		return fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
+	}
+}
+
+// makeshift encodes a register shifted by a constant
+func makeshift(reg int16, typ int64, s int64) shift {
+	return shift(int64(reg&0xf) | typ | (s&31)<<7)
+}
+
+// genshift generates a Prog for r = r0 op (r1 shifted by s)
+func genshift(as obj.As, r0, r1, r int16, typ int64, s int64) *obj.Prog {
+	p := gc.Prog(as)
+	p.From.Type = obj.TYPE_SHIFT
+	p.From.Offset = int64(makeshift(r1, typ, s))
+	p.Reg = r0
+	if r != 0 {
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	}
+	return p
+}
+
+// makeregshift encodes a register shifted by a register
+func makeregshift(r1 int16, typ int64, r2 int16) shift {
+	return shift(int64(r1&0xf) | typ | int64(r2&0xf)<<8 | 1<<4)
+}
+
+// genregshift generates a Prog for r = r0 op (r1 shifted by r2)
+func genregshift(as obj.As, r0, r1, r2, r int16, typ int64) *obj.Prog {
+	p := gc.Prog(as)
+	p.From.Type = obj.TYPE_SHIFT
+	p.From.Offset = int64(makeregshift(r1, typ, r2))
+	p.Reg = r0
+	if r != 0 {
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	}
+	return p
 }
 
 func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
@@ -26,91 +124,528 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		// memory arg needs no code
 	case ssa.OpArg:
 		// input args need no code
-	case ssa.OpSP, ssa.OpSB:
+	case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
+		// nothing to do
+	case ssa.OpCopy, ssa.OpARMMOVWconvert, ssa.OpARMMOVWreg:
+		if v.Type.IsMemory() {
+			return
+		}
+		x := v.Args[0].Reg()
+		y := v.Reg()
+		if x == y {
+			return
+		}
+		as := arm.AMOVW
+		if v.Type.IsFloat() {
+			switch v.Type.Size() {
+			case 4:
+				as = arm.AMOVF
+			case 8:
+				as = arm.AMOVD
+			default:
+				panic("bad float size")
+			}
+		}
+		p := gc.Prog(as)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = y
+	case ssa.OpARMMOVWnop:
+		if v.Reg() != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
 		// nothing to do
-	case ssa.OpCopy:
 	case ssa.OpLoadReg:
-		// TODO: by type
-		p := gc.Prog(arm.AMOVW)
-		n, off := gc.AutoVar(v.Args[0])
-		p.From.Type = obj.TYPE_MEM
-		p.From.Node = n
-		p.From.Sym = gc.Linksym(n.Sym)
-		p.From.Offset = off
-		if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT {
-			p.From.Name = obj.NAME_PARAM
-			p.From.Offset += n.Xoffset
-		} else {
-			p.From.Name = obj.NAME_AUTO
+		if v.Type.IsFlags() {
+			v.Fatalf("load flags not implemented: %v", v.LongString())
+			return
 		}
+		p := gc.Prog(loadByType(v.Type))
+		gc.AddrAuto(&p.From, v.Args[0])
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
-
+		p.To.Reg = v.Reg()
+	case ssa.OpPhi:
+		gc.CheckLoweredPhi(v)
 	case ssa.OpStoreReg:
-		// TODO: by type
-		p := gc.Prog(arm.AMOVW)
+		if v.Type.IsFlags() {
+			v.Fatalf("store flags not implemented: %v", v.LongString())
+			return
+		}
+		p := gc.Prog(storeByType(v.Type))
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[0])
-		n, off := gc.AutoVar(v)
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddrAuto(&p.To, v)
+	case ssa.OpARMUDIVrtcall:
+		p := gc.Prog(obj.ACALL)
 		p.To.Type = obj.TYPE_MEM
-		p.To.Node = n
-		p.To.Sym = gc.Linksym(n.Sym)
-		p.To.Offset = off
-		if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT {
-			p.To.Name = obj.NAME_PARAM
-			p.To.Offset += n.Xoffset
-		} else {
-			p.To.Name = obj.NAME_AUTO
-		}
-	case ssa.OpARMADD:
-		r := gc.SSARegNum(v)
-		r1 := gc.SSARegNum(v.Args[0])
-		r2 := gc.SSARegNum(v.Args[1])
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = obj.Linklookup(gc.Ctxt, "udiv", 0)
+	case ssa.OpARMADD,
+		ssa.OpARMADC,
+		ssa.OpARMSUB,
+		ssa.OpARMSBC,
+		ssa.OpARMRSB,
+		ssa.OpARMAND,
+		ssa.OpARMOR,
+		ssa.OpARMXOR,
+		ssa.OpARMBIC,
+		ssa.OpARMMUL,
+		ssa.OpARMADDF,
+		ssa.OpARMADDD,
+		ssa.OpARMSUBF,
+		ssa.OpARMSUBD,
+		ssa.OpARMMULF,
+		ssa.OpARMMULD,
+		ssa.OpARMDIVF,
+		ssa.OpARMDIVD:
+		r := v.Reg()
+		r1 := v.Args[0].Reg()
+		r2 := v.Args[1].Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r2
+		p.Reg = r1
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.OpARMADDS,
+		ssa.OpARMSUBS:
+		r := v.Reg0()
+		r1 := v.Args[0].Reg()
+		r2 := v.Args[1].Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.Scond = arm.C_SBIT
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r2
+		p.Reg = r1
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.OpARMSLL,
+		ssa.OpARMSRL,
+		ssa.OpARMSRA:
+		r := v.Reg()
+		r1 := v.Args[0].Reg()
+		r2 := v.Args[1].Reg()
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = r1
-		p.Reg = r2
+		p.From.Reg = r2
+		p.Reg = r1
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.OpARMSRAcond:
+		// ARM shift instructions uses only the low-order byte of the shift amount
+		// generate conditional instructions to deal with large shifts
+		// flag is already set
+		// SRA.HS	$31, Rarg0, Rdst // shift 31 bits to get the sign bit
+		// SRA.LO	Rarg1, Rarg0, Rdst
+		r := v.Reg()
+		r1 := v.Args[0].Reg()
+		r2 := v.Args[1].Reg()
+		p := gc.Prog(arm.ASRA)
+		p.Scond = arm.C_SCOND_HS
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = 31
+		p.Reg = r1
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+		p = gc.Prog(arm.ASRA)
+		p.Scond = arm.C_SCOND_LO
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r2
+		p.Reg = r1
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = r
-	case ssa.OpARMADDconst:
+	case ssa.OpARMADDconst,
+		ssa.OpARMADCconst,
+		ssa.OpARMSUBconst,
+		ssa.OpARMSBCconst,
+		ssa.OpARMRSBconst,
+		ssa.OpARMRSCconst,
+		ssa.OpARMANDconst,
+		ssa.OpARMORconst,
+		ssa.OpARMXORconst,
+		ssa.OpARMBICconst,
+		ssa.OpARMSLLconst,
+		ssa.OpARMSRLconst,
+		ssa.OpARMSRAconst:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_CONST
 		p.From.Offset = v.AuxInt
-		if v.Aux != nil {
-			panic("can't handle symbolic constant yet")
-		}
-		p.Reg = gc.SSARegNum(v.Args[0])
+		p.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpARMADDSconst,
+		ssa.OpARMSUBSconst,
+		ssa.OpARMRSBSconst:
+		p := gc.Prog(v.Op.Asm())
+		p.Scond = arm.C_SBIT
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.Reg = v.Args[0].Reg()
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
+		p.To.Reg = v.Reg0()
+	case ssa.OpARMSRRconst:
+		genshift(arm.AMOVW, 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
+	case ssa.OpARMADDshiftLL,
+		ssa.OpARMADCshiftLL,
+		ssa.OpARMSUBshiftLL,
+		ssa.OpARMSBCshiftLL,
+		ssa.OpARMRSBshiftLL,
+		ssa.OpARMRSCshiftLL,
+		ssa.OpARMANDshiftLL,
+		ssa.OpARMORshiftLL,
+		ssa.OpARMXORshiftLL,
+		ssa.OpARMBICshiftLL:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
+	case ssa.OpARMADDSshiftLL,
+		ssa.OpARMSUBSshiftLL,
+		ssa.OpARMRSBSshiftLL:
+		p := genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LL, v.AuxInt)
+		p.Scond = arm.C_SBIT
+	case ssa.OpARMADDshiftRL,
+		ssa.OpARMADCshiftRL,
+		ssa.OpARMSUBshiftRL,
+		ssa.OpARMSBCshiftRL,
+		ssa.OpARMRSBshiftRL,
+		ssa.OpARMRSCshiftRL,
+		ssa.OpARMANDshiftRL,
+		ssa.OpARMORshiftRL,
+		ssa.OpARMXORshiftRL,
+		ssa.OpARMBICshiftRL:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
+	case ssa.OpARMADDSshiftRL,
+		ssa.OpARMSUBSshiftRL,
+		ssa.OpARMRSBSshiftRL:
+		p := genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LR, v.AuxInt)
+		p.Scond = arm.C_SBIT
+	case ssa.OpARMADDshiftRA,
+		ssa.OpARMADCshiftRA,
+		ssa.OpARMSUBshiftRA,
+		ssa.OpARMSBCshiftRA,
+		ssa.OpARMRSBshiftRA,
+		ssa.OpARMRSCshiftRA,
+		ssa.OpARMANDshiftRA,
+		ssa.OpARMORshiftRA,
+		ssa.OpARMXORshiftRA,
+		ssa.OpARMBICshiftRA:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
+	case ssa.OpARMADDSshiftRA,
+		ssa.OpARMSUBSshiftRA,
+		ssa.OpARMRSBSshiftRA:
+		p := genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_AR, v.AuxInt)
+		p.Scond = arm.C_SBIT
+	case ssa.OpARMXORshiftRR:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
+	case ssa.OpARMMVNshiftLL:
+		genshift(v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
+	case ssa.OpARMMVNshiftRL:
+		genshift(v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
+	case ssa.OpARMMVNshiftRA:
+		genshift(v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
+	case ssa.OpARMMVNshiftLLreg:
+		genregshift(v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL)
+	case ssa.OpARMMVNshiftRLreg:
+		genregshift(v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR)
+	case ssa.OpARMMVNshiftRAreg:
+		genregshift(v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR)
+	case ssa.OpARMADDshiftLLreg,
+		ssa.OpARMADCshiftLLreg,
+		ssa.OpARMSUBshiftLLreg,
+		ssa.OpARMSBCshiftLLreg,
+		ssa.OpARMRSBshiftLLreg,
+		ssa.OpARMRSCshiftLLreg,
+		ssa.OpARMANDshiftLLreg,
+		ssa.OpARMORshiftLLreg,
+		ssa.OpARMXORshiftLLreg,
+		ssa.OpARMBICshiftLLreg:
+		genregshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_LL)
+	case ssa.OpARMADDSshiftLLreg,
+		ssa.OpARMSUBSshiftLLreg,
+		ssa.OpARMRSBSshiftLLreg:
+		p := genregshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_LL)
+		p.Scond = arm.C_SBIT
+	case ssa.OpARMADDshiftRLreg,
+		ssa.OpARMADCshiftRLreg,
+		ssa.OpARMSUBshiftRLreg,
+		ssa.OpARMSBCshiftRLreg,
+		ssa.OpARMRSBshiftRLreg,
+		ssa.OpARMRSCshiftRLreg,
+		ssa.OpARMANDshiftRLreg,
+		ssa.OpARMORshiftRLreg,
+		ssa.OpARMXORshiftRLreg,
+		ssa.OpARMBICshiftRLreg:
+		genregshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_LR)
+	case ssa.OpARMADDSshiftRLreg,
+		ssa.OpARMSUBSshiftRLreg,
+		ssa.OpARMRSBSshiftRLreg:
+		p := genregshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_LR)
+		p.Scond = arm.C_SBIT
+	case ssa.OpARMADDshiftRAreg,
+		ssa.OpARMADCshiftRAreg,
+		ssa.OpARMSUBshiftRAreg,
+		ssa.OpARMSBCshiftRAreg,
+		ssa.OpARMRSBshiftRAreg,
+		ssa.OpARMRSCshiftRAreg,
+		ssa.OpARMANDshiftRAreg,
+		ssa.OpARMORshiftRAreg,
+		ssa.OpARMXORshiftRAreg,
+		ssa.OpARMBICshiftRAreg:
+		genregshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_AR)
+	case ssa.OpARMADDSshiftRAreg,
+		ssa.OpARMSUBSshiftRAreg,
+		ssa.OpARMRSBSshiftRAreg:
+		p := genregshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_AR)
+		p.Scond = arm.C_SBIT
+	case ssa.OpARMHMUL,
+		ssa.OpARMHMULU:
+		// 32-bit high multiplication
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_REGREG
+		p.To.Reg = v.Reg()
+		p.To.Offset = arm.REGTMP // throw away low 32-bit into tmp register
+	case ssa.OpARMMULLU:
+		// 32-bit multiplication, results 64-bit, high 32-bit in out0, low 32-bit in out1
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_REGREG
+		p.To.Reg = v.Reg0()           // high 32-bit
+		p.To.Offset = int64(v.Reg1()) // low 32-bit
+	case ssa.OpARMMULA:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_REGREG2
+		p.To.Reg = v.Reg()                   // result
+		p.To.Offset = int64(v.Args[2].Reg()) // addend
 	case ssa.OpARMMOVWconst:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_CONST
 		p.From.Offset = v.AuxInt
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
-	case ssa.OpARMCMP:
+		p.To.Reg = v.Reg()
+	case ssa.OpARMMOVFconst,
+		ssa.OpARMMOVDconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_FCONST
+		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpARMCMP,
+		ssa.OpARMCMN,
+		ssa.OpARMTST,
+		ssa.OpARMTEQ,
+		ssa.OpARMCMPF,
+		ssa.OpARMCMPD:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
 		// Special layout in ARM assembly
 		// Comparing to x86, the operands of ARM's CMP are reversed.
-		p.From.Reg = gc.SSARegNum(v.Args[1])
-		p.Reg = gc.SSARegNum(v.Args[0])
-	case ssa.OpARMMOVWload:
+		p.From.Reg = v.Args[1].Reg()
+		p.Reg = v.Args[0].Reg()
+	case ssa.OpARMCMPconst,
+		ssa.OpARMCMNconst,
+		ssa.OpARMTSTconst,
+		ssa.OpARMTEQconst:
+		// Special layout in ARM assembly
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.Reg = v.Args[0].Reg()
+	case ssa.OpARMCMPF0,
+		ssa.OpARMCMPD0:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+	case ssa.OpARMCMPshiftLL:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LL, v.AuxInt)
+	case ssa.OpARMCMPshiftRL:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LR, v.AuxInt)
+	case ssa.OpARMCMPshiftRA:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_AR, v.AuxInt)
+	case ssa.OpARMCMPshiftLLreg:
+		genregshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LL)
+	case ssa.OpARMCMPshiftRLreg:
+		genregshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LR)
+	case ssa.OpARMCMPshiftRAreg:
+		genregshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_AR)
+	case ssa.OpARMMOVWaddr:
+		p := gc.Prog(arm.AMOVW)
+		p.From.Type = obj.TYPE_ADDR
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+		var wantreg string
+		// MOVW $sym+off(base), R
+		// the assembler expands it as the following:
+		// - base is SP: add constant offset to SP (R13)
+		//               when constant is large, tmp register (R11) may be used
+		// - base is SB: load external address from constant pool (use relocation)
+		switch v.Aux.(type) {
+		default:
+			v.Fatalf("aux is of unknown type %T", v.Aux)
+		case *ssa.ExternSymbol:
+			wantreg = "SB"
+			gc.AddAux(&p.From, v)
+		case *ssa.ArgSymbol, *ssa.AutoSymbol:
+			wantreg = "SP"
+			gc.AddAux(&p.From, v)
+		case nil:
+			// No sym, just MOVW $off(SP), R
+			wantreg = "SP"
+			p.From.Reg = arm.REGSP
+			p.From.Offset = v.AuxInt
+		}
+		if reg := v.Args[0].RegName(); reg != wantreg {
+			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
+		}
+
+	case ssa.OpARMMOVBload,
+		ssa.OpARMMOVBUload,
+		ssa.OpARMMOVHload,
+		ssa.OpARMMOVHUload,
+		ssa.OpARMMOVWload,
+		ssa.OpARMMOVFload,
+		ssa.OpARMMOVDload:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_MEM
-		p.From.Reg = gc.SSARegNum(v.Args[0])
+		p.From.Reg = v.Args[0].Reg()
 		gc.AddAux(&p.From, v)
 		p.To.Type = obj.TYPE_REG
-		p.To.Reg = gc.SSARegNum(v)
-	case ssa.OpARMMOVWstore:
+		p.To.Reg = v.Reg()
+	case ssa.OpARMMOVBstore,
+		ssa.OpARMMOVHstore,
+		ssa.OpARMMOVWstore,
+		ssa.OpARMMOVFstore,
+		ssa.OpARMMOVDstore:
 		p := gc.Prog(v.Op.Asm())
 		p.From.Type = obj.TYPE_REG
-		p.From.Reg = gc.SSARegNum(v.Args[1])
+		p.From.Reg = v.Args[1].Reg()
 		p.To.Type = obj.TYPE_MEM
-		p.To.Reg = gc.SSARegNum(v.Args[0])
+		p.To.Reg = v.Args[0].Reg()
 		gc.AddAux(&p.To, v)
+	case ssa.OpARMMOVWloadidx:
+		// this is just shift 0 bits
+		fallthrough
+	case ssa.OpARMMOVWloadshiftLL:
+		p := genshift(v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
+		p.From.Reg = v.Args[0].Reg()
+	case ssa.OpARMMOVWloadshiftRL:
+		p := genshift(v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
+		p.From.Reg = v.Args[0].Reg()
+	case ssa.OpARMMOVWloadshiftRA:
+		p := genshift(v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
+		p.From.Reg = v.Args[0].Reg()
+	case ssa.OpARMMOVWstoreidx:
+		// this is just shift 0 bits
+		fallthrough
+	case ssa.OpARMMOVWstoreshiftLL:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[2].Reg()
+		p.To.Type = obj.TYPE_SHIFT
+		p.To.Reg = v.Args[0].Reg()
+		p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_LL, v.AuxInt))
+	case ssa.OpARMMOVWstoreshiftRL:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[2].Reg()
+		p.To.Type = obj.TYPE_SHIFT
+		p.To.Reg = v.Args[0].Reg()
+		p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_LR, v.AuxInt))
+	case ssa.OpARMMOVWstoreshiftRA:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[2].Reg()
+		p.To.Type = obj.TYPE_SHIFT
+		p.To.Reg = v.Args[0].Reg()
+		p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_AR, v.AuxInt))
+	case ssa.OpARMMOVBreg,
+		ssa.OpARMMOVBUreg,
+		ssa.OpARMMOVHreg,
+		ssa.OpARMMOVHUreg:
+		a := v.Args[0]
+		for a.Op == ssa.OpCopy || a.Op == ssa.OpARMMOVWreg || a.Op == ssa.OpARMMOVWnop {
+			a = a.Args[0]
+		}
+		if a.Op == ssa.OpLoadReg {
+			t := a.Type
+			switch {
+			case v.Op == ssa.OpARMMOVBreg && t.Size() == 1 && t.IsSigned(),
+				v.Op == ssa.OpARMMOVBUreg && t.Size() == 1 && !t.IsSigned(),
+				v.Op == ssa.OpARMMOVHreg && t.Size() == 2 && t.IsSigned(),
+				v.Op == ssa.OpARMMOVHUreg && t.Size() == 2 && !t.IsSigned():
+				// arg is a proper-typed load, already zero/sign-extended, don't extend again
+				if v.Reg() == v.Args[0].Reg() {
+					return
+				}
+				p := gc.Prog(arm.AMOVW)
+				p.From.Type = obj.TYPE_REG
+				p.From.Reg = v.Args[0].Reg()
+				p.To.Type = obj.TYPE_REG
+				p.To.Reg = v.Reg()
+				return
+			default:
+			}
+		}
+		fallthrough
+	case ssa.OpARMMVN,
+		ssa.OpARMCLZ,
+		ssa.OpARMSQRTD,
+		ssa.OpARMNEGF,
+		ssa.OpARMNEGD,
+		ssa.OpARMMOVWF,
+		ssa.OpARMMOVWD,
+		ssa.OpARMMOVFW,
+		ssa.OpARMMOVDW,
+		ssa.OpARMMOVFD,
+		ssa.OpARMMOVDF:
+		p := gc.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.OpARMMOVWUF,
+		ssa.OpARMMOVWUD,
+		ssa.OpARMMOVFWU,
+		ssa.OpARMMOVDWU:
+		p := gc.Prog(v.Op.Asm())
+		p.Scond = arm.C_UBIT
+		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.OpARMCMOVWHSconst:
+		p := gc.Prog(arm.AMOVW)
+		p.Scond = arm.C_SCOND_HS
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpARMCMOVWLSconst:
+		p := gc.Prog(arm.AMOVW)
+		p.Scond = arm.C_SCOND_LS
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
 	case ssa.OpARMCALLstatic:
-		// TODO: deferreturn
+		if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
+			// Deferred calls will appear to be returning to
+			// the CALL deferreturn(SB) that we are about to emit.
+			// However, the stack trace code will show the line
+			// of the instruction byte before the return PC.
+			// To avoid that being an unrelated instruction,
+			// insert an actual hardware NOP that will have the right line number.
+			// This is different from obj.ANOP, which is a virtual no-op
+			// that doesn't make it into the instruction stream.
+			ginsnop()
+		}
 		p := gc.Prog(obj.ACALL)
 		p.To.Type = obj.TYPE_MEM
 		p.To.Name = obj.NAME_EXTERN
@@ -118,37 +653,279 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
 		if gc.Maxarg < v.AuxInt {
 			gc.Maxarg = v.AuxInt
 		}
+	case ssa.OpARMCALLclosure:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Offset = 0
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpARMCALLdefer:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Deferproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpARMCALLgo:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Newproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpARMCALLinter:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Offset = 0
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpARMDUFFZERO:
+		p := gc.Prog(obj.ADUFFZERO)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
+		p.To.Offset = v.AuxInt
+	case ssa.OpARMDUFFCOPY:
+		p := gc.Prog(obj.ADUFFCOPY)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg))
+		p.To.Offset = v.AuxInt
+	case ssa.OpARMLoweredNilCheck:
+		// Issue a load which will fault if arg is nil.
+		p := gc.Prog(arm.AMOVB)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = arm.REGTMP
+		if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
+			gc.Warnl(v.Line, "generated nil check")
+		}
+	case ssa.OpARMLoweredZero:
+		// MOVW.P	Rarg2, 4(R1)
+		// CMP	Rarg1, R1
+		// BLE	-2(PC)
+		// arg1 is the address of the last element to zero
+		// arg2 is known to be zero
+		// auxint is alignment
+		var sz int64
+		var mov obj.As
+		switch {
+		case v.AuxInt%4 == 0:
+			sz = 4
+			mov = arm.AMOVW
+		case v.AuxInt%2 == 0:
+			sz = 2
+			mov = arm.AMOVH
+		default:
+			sz = 1
+			mov = arm.AMOVB
+		}
+		p := gc.Prog(mov)
+		p.Scond = arm.C_PBIT
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[2].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = arm.REG_R1
+		p.To.Offset = sz
+		p2 := gc.Prog(arm.ACMP)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = v.Args[1].Reg()
+		p2.Reg = arm.REG_R1
+		p3 := gc.Prog(arm.ABLE)
+		p3.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p3, p)
+	case ssa.OpARMLoweredMove:
+		// MOVW.P	4(R1), Rtmp
+		// MOVW.P	Rtmp, 4(R2)
+		// CMP	Rarg2, R1
+		// BLE	-3(PC)
+		// arg2 is the address of the last element of src
+		// auxint is alignment
+		var sz int64
+		var mov obj.As
+		switch {
+		case v.AuxInt%4 == 0:
+			sz = 4
+			mov = arm.AMOVW
+		case v.AuxInt%2 == 0:
+			sz = 2
+			mov = arm.AMOVH
+		default:
+			sz = 1
+			mov = arm.AMOVB
+		}
+		p := gc.Prog(mov)
+		p.Scond = arm.C_PBIT
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = arm.REG_R1
+		p.From.Offset = sz
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = arm.REGTMP
+		p2 := gc.Prog(mov)
+		p2.Scond = arm.C_PBIT
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = arm.REGTMP
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = arm.REG_R2
+		p2.To.Offset = sz
+		p3 := gc.Prog(arm.ACMP)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = v.Args[2].Reg()
+		p3.Reg = arm.REG_R1
+		p4 := gc.Prog(arm.ABLE)
+		p4.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p4, p)
 	case ssa.OpVarDef:
 		gc.Gvardef(v.Aux.(*gc.Node))
 	case ssa.OpVarKill:
 		gc.Gvarkill(v.Aux.(*gc.Node))
 	case ssa.OpVarLive:
 		gc.Gvarlive(v.Aux.(*gc.Node))
-	case ssa.OpARMLessThan:
-		v.Fatalf("pseudo-op made it to output: %s", v.LongString())
+	case ssa.OpKeepAlive:
+		gc.KeepAlive(v)
+	case ssa.OpARMEqual,
+		ssa.OpARMNotEqual,
+		ssa.OpARMLessThan,
+		ssa.OpARMLessEqual,
+		ssa.OpARMGreaterThan,
+		ssa.OpARMGreaterEqual,
+		ssa.OpARMLessThanU,
+		ssa.OpARMLessEqualU,
+		ssa.OpARMGreaterThanU,
+		ssa.OpARMGreaterEqualU:
+		// generate boolean values
+		// use conditional move
+		p := gc.Prog(arm.AMOVW)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = 0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+		p = gc.Prog(arm.AMOVW)
+		p.Scond = condBits[v.Op]
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = 1
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpSelect0, ssa.OpSelect1:
+		// nothing to do
+	case ssa.OpARMLoweredGetClosurePtr:
+		// Closure pointer is R7 (arm.REGCTXT).
+		gc.CheckLoweredGetClosurePtr(v)
+	case ssa.OpARMFlagEQ,
+		ssa.OpARMFlagLT_ULT,
+		ssa.OpARMFlagLT_UGT,
+		ssa.OpARMFlagGT_ULT,
+		ssa.OpARMFlagGT_UGT:
+		v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
+	case ssa.OpARMInvertFlags:
+		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
 	default:
-		v.Unimplementedf("genValue not implemented: %s", v.LongString())
+		v.Fatalf("genValue not implemented: %s", v.LongString())
 	}
 }
 
+var condBits = map[ssa.Op]uint8{
+	ssa.OpARMEqual:         arm.C_SCOND_EQ,
+	ssa.OpARMNotEqual:      arm.C_SCOND_NE,
+	ssa.OpARMLessThan:      arm.C_SCOND_LT,
+	ssa.OpARMLessThanU:     arm.C_SCOND_LO,
+	ssa.OpARMLessEqual:     arm.C_SCOND_LE,
+	ssa.OpARMLessEqualU:    arm.C_SCOND_LS,
+	ssa.OpARMGreaterThan:   arm.C_SCOND_GT,
+	ssa.OpARMGreaterThanU:  arm.C_SCOND_HI,
+	ssa.OpARMGreaterEqual:  arm.C_SCOND_GE,
+	ssa.OpARMGreaterEqualU: arm.C_SCOND_HS,
+}
+
+var blockJump = map[ssa.BlockKind]struct {
+	asm, invasm obj.As
+}{
+	ssa.BlockARMEQ:  {arm.ABEQ, arm.ABNE},
+	ssa.BlockARMNE:  {arm.ABNE, arm.ABEQ},
+	ssa.BlockARMLT:  {arm.ABLT, arm.ABGE},
+	ssa.BlockARMGE:  {arm.ABGE, arm.ABLT},
+	ssa.BlockARMLE:  {arm.ABLE, arm.ABGT},
+	ssa.BlockARMGT:  {arm.ABGT, arm.ABLE},
+	ssa.BlockARMULT: {arm.ABLO, arm.ABHS},
+	ssa.BlockARMUGE: {arm.ABHS, arm.ABLO},
+	ssa.BlockARMUGT: {arm.ABHI, arm.ABLS},
+	ssa.BlockARMULE: {arm.ABLS, arm.ABHI},
+}
+
 func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
 	s.SetLineno(b.Line)
 
 	switch b.Kind {
-	case ssa.BlockCall:
+	case ssa.BlockPlain:
 		if b.Succs[0].Block() != next {
 			p := gc.Prog(obj.AJMP)
 			p.To.Type = obj.TYPE_BRANCH
 			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
 		}
-	case ssa.BlockRet:
-		gc.Prog(obj.ARET)
-	case ssa.BlockARMLT:
-		p := gc.Prog(arm.ABLT)
-		p.To.Type = obj.TYPE_BRANCH
-		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
-		p = gc.Prog(obj.AJMP)
+
+	case ssa.BlockDefer:
+		// defer returns in R0:
+		// 0 if we should continue executing
+		// 1 if we should jump to deferreturn call
+		p := gc.Prog(arm.ACMP)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = 0
+		p.Reg = arm.REG_R0
+		p = gc.Prog(arm.ABNE)
 		p.To.Type = obj.TYPE_BRANCH
 		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+
+	case ssa.BlockExit:
+		gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
+
+	case ssa.BlockRet:
+		gc.Prog(obj.ARET)
+
+	case ssa.BlockRetJmp:
+		p := gc.Prog(obj.ARET)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
+
+	case ssa.BlockARMEQ, ssa.BlockARMNE,
+		ssa.BlockARMLT, ssa.BlockARMGE,
+		ssa.BlockARMLE, ssa.BlockARMGT,
+		ssa.BlockARMULT, ssa.BlockARMUGT,
+		ssa.BlockARMULE, ssa.BlockARMUGE:
+		jmp := blockJump[b.Kind]
+		var p *obj.Prog
+		switch next {
+		case b.Succs[0].Block():
+			p = gc.Prog(jmp.invasm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		case b.Succs[1].Block():
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		default:
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+			q := gc.Prog(obj.AJMP)
+			q.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
+		}
+
+	default:
+		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
 	}
 }
diff --git a/src/cmd/compile/internal/arm64/cgen.go b/src/cmd/compile/internal/arm64/cgen.go
deleted file mode 100644
index 87f3498..0000000
--- a/src/cmd/compile/internal/arm64/cgen.go
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package arm64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/arm64"
-)
-
-func blockcopy(n, res *gc.Node, osrc, odst, w int64) {
-	// determine alignment.
-	// want to avoid unaligned access, so have to use
-	// smaller operations for less aligned types.
-	// for example moving [4]byte must use 4 MOVB not 1 MOVW.
-	align := int(n.Type.Align)
-
-	var op obj.As
-	switch align {
-	default:
-		gc.Fatalf("sgen: invalid alignment %d for %v", align, n.Type)
-
-	case 1:
-		op = arm64.AMOVB
-
-	case 2:
-		op = arm64.AMOVH
-
-	case 4:
-		op = arm64.AMOVW
-
-	case 8:
-		op = arm64.AMOVD
-	}
-
-	if w%int64(align) != 0 {
-		gc.Fatalf("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type)
-	}
-	c := int32(w / int64(align))
-
-	if osrc%int64(align) != 0 || odst%int64(align) != 0 {
-		gc.Fatalf("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align)
-	}
-
-	// if we are copying forward on the stack and
-	// the src and dst overlap, then reverse direction
-	dir := align
-
-	if osrc < odst && odst < osrc+w {
-		dir = -dir
-	}
-
-	var dst gc.Node
-	var src gc.Node
-	if n.Ullman >= res.Ullman {
-		gc.Agenr(n, &dst, res) // temporarily use dst
-		gc.Regalloc(&src, gc.Types[gc.Tptr], nil)
-		gins(arm64.AMOVD, &dst, &src)
-		if res.Op == gc.ONAME {
-			gc.Gvardef(res)
-		}
-		gc.Agen(res, &dst)
-	} else {
-		if res.Op == gc.ONAME {
-			gc.Gvardef(res)
-		}
-		gc.Agenr(res, &dst, res)
-		gc.Agenr(n, &src, nil)
-	}
-
-	var tmp gc.Node
-	gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil)
-
-	// set up end marker
-	var nend gc.Node
-
-	// move src and dest to the end of block if necessary
-	if dir < 0 {
-		if c >= 4 {
-			gc.Regalloc(&nend, gc.Types[gc.Tptr], nil)
-			gins(arm64.AMOVD, &src, &nend)
-		}
-
-		p := gins(arm64.AADD, nil, &src)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-
-		p = gins(arm64.AADD, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-	} else {
-		p := gins(arm64.AADD, nil, &src)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = int64(-dir)
-
-		p = gins(arm64.AADD, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = int64(-dir)
-
-		if c >= 4 {
-			gc.Regalloc(&nend, gc.Types[gc.Tptr], nil)
-			p := gins(arm64.AMOVD, &src, &nend)
-			p.From.Type = obj.TYPE_ADDR
-			p.From.Offset = w
-		}
-	}
-
-	// move
-	// TODO: enable duffcopy for larger copies.
-	if c >= 4 {
-		p := gins(op, &src, &tmp)
-		p.From.Type = obj.TYPE_MEM
-		p.From.Offset = int64(dir)
-		p.Scond = arm64.C_XPRE
-		ploop := p
-
-		p = gins(op, &tmp, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = int64(dir)
-		p.Scond = arm64.C_XPRE
-
-		p = gcmp(arm64.ACMP, &src, &nend)
-
-		gc.Patch(gc.Gbranch(arm64.ABNE, nil, 0), ploop)
-		gc.Regfree(&nend)
-	} else {
-		// TODO(austin): Instead of generating ADD $-8,R8; ADD
-		// $-8,R7; n*(MOVDU 8(R8),R9; MOVDU R9,8(R7);) just
-		// generate the offsets directly and eliminate the
-		// ADDs. That will produce shorter, more
-		// pipeline-able code.
-		var p *obj.Prog
-		for ; c > 0; c-- {
-			p = gins(op, &src, &tmp)
-			p.From.Type = obj.TYPE_MEM
-			p.From.Offset = int64(dir)
-			p.Scond = arm64.C_XPRE
-
-			p = gins(op, &tmp, &dst)
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = int64(dir)
-			p.Scond = arm64.C_XPRE
-		}
-	}
-
-	gc.Regfree(&dst)
-	gc.Regfree(&src)
-	gc.Regfree(&tmp)
-}
diff --git a/src/cmd/compile/internal/arm64/galign.go b/src/cmd/compile/internal/arm64/galign.go
index 7acc4e0..20a67e3 100644
--- a/src/cmd/compile/internal/arm64/galign.go
+++ b/src/cmd/compile/internal/arm64/galign.go
@@ -6,61 +6,19 @@ package arm64
 
 import (
 	"cmd/compile/internal/gc"
+	"cmd/compile/internal/ssa"
 	"cmd/internal/obj/arm64"
 )
 
-func betypeinit() {
-}
-
-func Main() {
+func Init() {
 	gc.Thearch.LinkArch = &arm64.Linkarm64
 	gc.Thearch.REGSP = arm64.REGSP
-	gc.Thearch.REGCTXT = arm64.REGCTXT
-	gc.Thearch.REGCALLX = arm64.REGRT1
-	gc.Thearch.REGCALLX2 = arm64.REGRT2
-	gc.Thearch.REGRETURN = arm64.REG_R0
-	gc.Thearch.REGMIN = arm64.REG_R0
-	gc.Thearch.REGMAX = arm64.REG_R31
-	gc.Thearch.REGZERO = arm64.REGZERO
-	gc.Thearch.FREGMIN = arm64.REG_F0
-	gc.Thearch.FREGMAX = arm64.REG_F31
 	gc.Thearch.MAXWIDTH = 1 << 50
-	gc.Thearch.ReservedRegs = resvd
 
-	gc.Thearch.Betypeinit = betypeinit
-	gc.Thearch.Cgen_hmul = cgen_hmul
-	gc.Thearch.AddSetCarry = AddSetCarry
-	gc.Thearch.RightShiftWithCarry = RightShiftWithCarry
-	gc.Thearch.Cgen_shift = cgen_shift
-	gc.Thearch.Clearfat = clearfat
 	gc.Thearch.Defframe = defframe
-	gc.Thearch.Dodiv = dodiv
-	gc.Thearch.Excise = excise
-	gc.Thearch.Expandchecks = expandchecks
-	gc.Thearch.Getg = getg
-	gc.Thearch.Gins = gins
-	gc.Thearch.Ginscmp = ginscmp
-	gc.Thearch.Ginscon = ginscon
-	gc.Thearch.Ginsnop = ginsnop
-	gc.Thearch.Gmove = gmove
-	gc.Thearch.Peep = peep
 	gc.Thearch.Proginfo = proginfo
-	gc.Thearch.Regtyp = regtyp
-	gc.Thearch.Sameaddr = sameaddr
-	gc.Thearch.Smallindir = smallindir
-	gc.Thearch.Stackaddr = stackaddr
-	gc.Thearch.Blockcopy = blockcopy
-	gc.Thearch.Sudoaddable = sudoaddable
-	gc.Thearch.Sudoclean = sudoclean
-	gc.Thearch.Excludedregs = excludedregs
-	gc.Thearch.RtoB = RtoB
-	gc.Thearch.FtoB = RtoB
-	gc.Thearch.BtoR = BtoR
-	gc.Thearch.BtoF = BtoF
-	gc.Thearch.Optoas = optoas
-	gc.Thearch.Doregbits = doregbits
-	gc.Thearch.Regnames = regnames
 
-	gc.Main()
-	gc.Exit(0)
+	gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
+	gc.Thearch.SSAGenValue = ssaGenValue
+	gc.Thearch.SSAGenBlock = ssaGenBlock
 }
diff --git a/src/cmd/compile/internal/arm64/ggen.go b/src/cmd/compile/internal/arm64/ggen.go
index bddfed6..16813b6 100644
--- a/src/cmd/compile/internal/arm64/ggen.go
+++ b/src/cmd/compile/internal/arm64/ggen.go
@@ -8,7 +8,6 @@ import (
 	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm64"
-	"fmt"
 )
 
 func defframe(ptxt *obj.Prog) {
@@ -43,7 +42,7 @@ func defframe(ptxt *obj.Prog) {
 			gc.Fatalf("needzero class %d", n.Class)
 		}
 		if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
-			gc.Fatalf("var %v has size %d offset %d", gc.Nconv(n, gc.FmtLong), int(n.Type.Width), int(n.Xoffset))
+			gc.Fatalf("var %L has size %d offset %d", n, int(n.Type.Width), int(n.Xoffset))
 		}
 
 		if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) {
@@ -66,7 +65,7 @@ func defframe(ptxt *obj.Prog) {
 	zerorange(p, int64(frame), lo, hi)
 }
 
-var darwin = obj.Getgoos() == "darwin"
+var darwin = obj.GOOS == "darwin"
 
 func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
 	cnt := hi - lo
@@ -75,502 +74,36 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
 	}
 	if cnt < int64(4*gc.Widthptr) {
 		for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
-			p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+frame+lo+i)
+			p = gc.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+frame+lo+i)
 		}
 	} else if cnt <= int64(128*gc.Widthptr) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend
-		p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
-		p = appendpp(p, arm64.AADD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGRT1, 0)
+		p = gc.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
+		p = gc.Appendpp(p, arm64.AADD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGRT1, 0)
 		p.Reg = arm64.REGRT1
-		p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
-		f := gc.Sysfunc("duffzero")
-		gc.Naddr(&p.To, f)
-		gc.Afunclit(&p.To, f)
+		p = gc.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
+		gc.Naddr(&p.To, gc.Sysfunc("duffzero"))
 		p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr))
 	} else {
-		p = appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGTMP, 0)
-		p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
-		p = appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
+		p = gc.Appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGTMP, 0)
+		p = gc.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
+		p = gc.Appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
 		p.Reg = arm64.REGRT1
-		p = appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm64.REGTMP, 0)
-		p = appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT2, 0)
+		p = gc.Appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm64.REGTMP, 0)
+		p = gc.Appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT2, 0)
 		p.Reg = arm64.REGRT1
-		p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(gc.Widthptr))
+		p = gc.Appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(gc.Widthptr))
 		p.Scond = arm64.C_XPRE
 		p1 := p
-		p = appendpp(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0)
+		p = gc.Appendpp(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0)
 		p.Reg = arm64.REGRT2
-		p = appendpp(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
+		p = gc.Appendpp(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
 		gc.Patch(p, p1)
 	}
 
 	return p
 }
 
-func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int64, ttype obj.AddrType, treg int, toffset int64) *obj.Prog {
-	q := gc.Ctxt.NewProg()
-	gc.Clearp(q)
-	q.As = as
-	q.Lineno = p.Lineno
-	q.From.Type = ftype
-	q.From.Reg = int16(freg)
-	q.From.Offset = foffset
-	q.To.Type = ttype
-	q.To.Reg = int16(treg)
-	q.To.Offset = toffset
-	q.Link = p.Link
-	p.Link = q
-	return q
-}
-
 func ginsnop() {
-	var con gc.Node
-	gc.Nodconst(&con, gc.Types[gc.TINT], 0)
-	gins(arm64.AHINT, &con, nil)
-}
-
-var panicdiv *gc.Node
-
-/*
- * generate division.
- * generates one of:
- *	res = nl / nr
- *	res = nl % nr
- * according to op.
- */
-func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	// Have to be careful about handling
-	// most negative int divided by -1 correctly.
-	// The hardware will generate undefined result.
-	// Also need to explicitly trap on division on zero,
-	// the hardware will silently generate undefined result.
-	// DIVW will leave unpredictable result in higher 32-bit,
-	// so always use DIVD/DIVDU.
-	t := nl.Type
-
-	t0 := t
-	check := false
-	if t.IsSigned() {
-		check = true
-		if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<<uint64(t.Width*8-1)) {
-			check = false
-		} else if gc.Isconst(nr, gc.CTINT) && nr.Int64() != -1 {
-			check = false
-		}
-	}
-
-	if t.Width < 8 {
-		if t.IsSigned() {
-			t = gc.Types[gc.TINT64]
-		} else {
-			t = gc.Types[gc.TUINT64]
-		}
-		check = false
-	}
-
-	a := optoas(gc.ODIV, t)
-
-	var tl gc.Node
-	gc.Regalloc(&tl, t0, nil)
-	var tr gc.Node
-	gc.Regalloc(&tr, t0, nil)
-	if nl.Ullman >= nr.Ullman {
-		gc.Cgen(nl, &tl)
-		gc.Cgen(nr, &tr)
-	} else {
-		gc.Cgen(nr, &tr)
-		gc.Cgen(nl, &tl)
-	}
-
-	if t != t0 {
-		// Convert
-		tl2 := tl
-
-		tr2 := tr
-		tl.Type = t
-		tr.Type = t
-		gmove(&tl2, &tl)
-		gmove(&tr2, &tr)
-	}
-
-	// Handle divide-by-zero panic.
-	p1 := gins(optoas(gc.OCMP, t), &tr, nil)
-	p1.Reg = arm64.REGZERO
-	p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1)
-	if panicdiv == nil {
-		panicdiv = gc.Sysfunc("panicdivide")
-	}
-	gc.Ginscall(panicdiv, -1)
-	gc.Patch(p1, gc.Pc)
-
-	var p2 *obj.Prog
-	if check {
-		var nm1 gc.Node
-		gc.Nodconst(&nm1, t, -1)
-		gcmp(optoas(gc.OCMP, t), &tr, &nm1)
-		p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1)
-		if op == gc.ODIV {
-			// a / (-1) is -a.
-			gins(optoas(gc.OMINUS, t), &tl, &tl)
-
-			gmove(&tl, res)
-		} else {
-			// a % (-1) is 0.
-			var nz gc.Node
-			gc.Nodconst(&nz, t, 0)
-
-			gmove(&nz, res)
-		}
-
-		p2 = gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-	}
-
-	p1 = gins(a, &tr, &tl)
-	if op == gc.ODIV {
-		gc.Regfree(&tr)
-		gmove(&tl, res)
-	} else {
-		// A%B = A-(A/B*B)
-		var tm gc.Node
-		gc.Regalloc(&tm, t, nil)
-
-		// patch div to use the 3 register form
-		// TODO(minux): add gins3?
-		p1.Reg = p1.To.Reg
-
-		p1.To.Reg = tm.Reg
-		gins(optoas(gc.OMUL, t), &tr, &tm)
-		gc.Regfree(&tr)
-		gins(optoas(gc.OSUB, t), &tm, &tl)
-		gc.Regfree(&tm)
-		gmove(&tl, res)
-	}
-
-	gc.Regfree(&tl)
-	if check {
-		gc.Patch(p2, gc.Pc)
-	}
-}
-
-// RightShiftWithCarry generates a constant unsigned
-// right shift with carry.
-//
-// res = n >> shift // with carry
-func RightShiftWithCarry(n *gc.Node, shift uint, res *gc.Node) {
-	// Extra 1 is for carry bit.
-	maxshift := uint(n.Type.Width*8 + 1)
-	if shift == 0 {
-		gmove(n, res)
-	} else if shift < maxshift {
-		// 1. clear rightmost bit of target
-		var n1 gc.Node
-		gc.Nodconst(&n1, n.Type, 1)
-		gins(optoas(gc.ORSH, n.Type), &n1, n)
-		gins(optoas(gc.OLSH, n.Type), &n1, n)
-		// 2. add carry flag to target
-		var n2 gc.Node
-		gc.Nodconst(&n1, n.Type, 0)
-		gc.Regalloc(&n2, n.Type, nil)
-		gins(optoas(gc.OAS, n.Type), &n1, &n2)
-		gins(arm64.AADC, &n2, n)
-		// 3. right rotate 1 bit
-		gc.Nodconst(&n1, n.Type, 1)
-		gins(arm64.AROR, &n1, n)
-
-		// ARM64 backend doesn't eliminate shifts by 0. It is manually checked here.
-		if shift > 1 {
-			var n3 gc.Node
-			gc.Nodconst(&n3, n.Type, int64(shift-1))
-			cgen_shift(gc.ORSH, true, n, &n3, res)
-		} else {
-			gmove(n, res)
-		}
-		gc.Regfree(&n2)
-	} else {
-		gc.Fatalf("RightShiftWithCarry: shift(%v) is bigger than max size(%v)", shift, maxshift)
-	}
-}
-
-// AddSetCarry generates add and set carry.
-//
-//   res = nl + nr // with carry flag set
-func AddSetCarry(nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	gins(arm64.AADDS, nl, nr)
-	gmove(nr, res)
-}
-
-/*
- * generate high multiply:
- *   res = (nl*nr) >> width
- */
-func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	// largest ullman on left.
-	if nl.Ullman < nr.Ullman {
-		nl, nr = nr, nl
-	}
-
-	t := nl.Type
-	w := t.Width * 8
-	var n1 gc.Node
-	gc.Cgenr(nl, &n1, res)
-	var n2 gc.Node
-	gc.Cgenr(nr, &n2, nil)
-	switch gc.Simtype[t.Etype] {
-	case gc.TINT8,
-		gc.TINT16,
-		gc.TINT32:
-		gins(optoas(gc.OMUL, t), &n2, &n1)
-		p := gins(arm64.AASR, nil, &n1)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-
-	case gc.TUINT8,
-		gc.TUINT16,
-		gc.TUINT32:
-		gins(optoas(gc.OMUL, t), &n2, &n1)
-		p := gins(arm64.ALSR, nil, &n1)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-
-	case gc.TINT64,
-		gc.TUINT64:
-		if t.IsSigned() {
-			gins(arm64.ASMULH, &n2, &n1)
-		} else {
-			gins(arm64.AUMULH, &n2, &n1)
-		}
-
-	default:
-		gc.Fatalf("cgen_hmul %v", t)
-	}
-
-	gc.Cgen(&n1, res)
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-/*
- * generate shift according to op, one of:
- *	res = nl << nr
- *	res = nl >> nr
- */
-func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	a := optoas(op, nl.Type)
-
-	if nr.Op == gc.OLITERAL {
-		var n1 gc.Node
-		gc.Regalloc(&n1, nl.Type, res)
-		gc.Cgen(nl, &n1)
-		sc := uint64(nr.Int64())
-		if sc >= uint64(nl.Type.Width)*8 {
-			// large shift gets 2 shifts by width-1
-			var n3 gc.Node
-			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
-
-			gins(a, &n3, &n1)
-			gins(a, &n3, &n1)
-		} else {
-			gins(a, nr, &n1)
-		}
-		gmove(&n1, res)
-		gc.Regfree(&n1)
-		return
-	}
-
-	if nl.Ullman >= gc.UINF {
-		var n4 gc.Node
-		gc.Tempname(&n4, nl.Type)
-		gc.Cgen(nl, &n4)
-		nl = &n4
-	}
-
-	if nr.Ullman >= gc.UINF {
-		var n5 gc.Node
-		gc.Tempname(&n5, nr.Type)
-		gc.Cgen(nr, &n5)
-		nr = &n5
-	}
-
-	// Allow either uint32 or uint64 as shift type,
-	// to avoid unnecessary conversion from uint32 to uint64
-	// just to do the comparison.
-	tcount := gc.Types[gc.Simtype[nr.Type.Etype]]
-
-	if tcount.Etype < gc.TUINT32 {
-		tcount = gc.Types[gc.TUINT32]
-	}
-
-	var n1 gc.Node
-	gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX
-	var n3 gc.Node
-	gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX
-
-	var n2 gc.Node
-	gc.Regalloc(&n2, nl.Type, res)
-
-	if nl.Ullman >= nr.Ullman {
-		gc.Cgen(nl, &n2)
-		gc.Cgen(nr, &n1)
-		gmove(&n1, &n3)
-	} else {
-		gc.Cgen(nr, &n1)
-		gmove(&n1, &n3)
-		gc.Cgen(nl, &n2)
-	}
-
-	gc.Regfree(&n3)
-
-	// test and fix up large shifts
-	if !bounded {
-		gc.Nodconst(&n3, tcount, nl.Type.Width*8)
-		gcmp(optoas(gc.OCMP, tcount), &n1, &n3)
-		p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, +1)
-		if op == gc.ORSH && nl.Type.IsSigned() {
-			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
-			gins(a, &n3, &n2)
-		} else {
-			gc.Nodconst(&n3, nl.Type, 0)
-			gmove(&n3, &n2)
-		}
-
-		gc.Patch(p1, gc.Pc)
-	}
-
-	gins(a, &n1, &n2)
-
-	gmove(&n2, res)
-
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-func clearfat(nl *gc.Node) {
-	/* clear a fat object */
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width)
-	}
-
-	w := uint64(nl.Type.Width)
-
-	// Avoid taking the address for simple enough types.
-	if gc.Componentgen(nil, nl) {
-		return
-	}
-
-	c := w % 8 // bytes
-	q := w / 8 // dwords
-
-	var r0 gc.Node
-	gc.Nodreg(&r0, gc.Types[gc.TUINT64], arm64.REGZERO)
-	var dst gc.Node
-
-	// REGRT1 is reserved on arm64, see arm64/gsubr.go.
-	gc.Nodreg(&dst, gc.Types[gc.Tptr], arm64.REGRT1)
-	gc.Agen(nl, &dst)
-
-	var boff uint64
-	if q > 128 {
-		p := gins(arm64.ASUB, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = 8
-
-		var end gc.Node
-		gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
-		p = gins(arm64.AMOVD, &dst, &end)
-		p.From.Type = obj.TYPE_ADDR
-		p.From.Offset = int64(q * 8)
-
-		p = gins(arm64.AMOVD, &r0, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = 8
-		p.Scond = arm64.C_XPRE
-		pl := p
-
-		p = gcmp(arm64.ACMP, &dst, &end)
-		gc.Patch(gc.Gbranch(arm64.ABNE, nil, 0), pl)
-
-		gc.Regfree(&end)
-
-		// The loop leaves R16 on the last zeroed dword
-		boff = 8
-	} else if q >= 4 && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend
-		p := gins(arm64.ASUB, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = 8
-		f := gc.Sysfunc("duffzero")
-		p = gins(obj.ADUFFZERO, nil, f)
-		gc.Afunclit(&p.To, f)
-
-		// 4 and 128 = magic constants: see ../../runtime/asm_arm64x.s
-		p.To.Offset = int64(4 * (128 - q))
-
-		// duffzero leaves R16 on the last zeroed dword
-		boff = 8
-	} else {
-		var p *obj.Prog
-		for t := uint64(0); t < q; t++ {
-			p = gins(arm64.AMOVD, &r0, &dst)
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = int64(8 * t)
-		}
-
-		boff = 8 * q
-	}
-
-	var p *obj.Prog
-	for t := uint64(0); t < c; t++ {
-		p = gins(arm64.AMOVB, &r0, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = int64(t + boff)
-	}
-}
-
-// Called after regopt and peep have run.
-// Expand CHECKNIL pseudo-op into actual nil pointer check.
-func expandchecks(firstp *obj.Prog) {
-	var p1 *obj.Prog
-
-	for p := firstp; p != nil; p = p.Link {
-		if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 {
-			fmt.Printf("expandchecks: %v\n", p)
-		}
-		if p.As != obj.ACHECKNIL {
-			continue
-		}
-		if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers
-			gc.Warnl(p.Lineno, "generated nil check")
-		}
-		if p.From.Type != obj.TYPE_REG {
-			gc.Fatalf("invalid nil check %v\n", p)
-		}
-
-		// check is
-		//	CBNZ arg, 2(PC)
-		//	MOVD ZR, 0(arg)
-		p1 = gc.Ctxt.NewProg()
-		gc.Clearp(p1)
-		p1.Link = p.Link
-		p.Link = p1
-		p1.Lineno = p.Lineno
-		p1.Pc = 9999
-
-		p.As = arm64.ACBNZ
-		p.To.Type = obj.TYPE_BRANCH
-		p.To.Val = p1.Link
-
-		// crash by write to memory address 0.
-		p1.As = arm64.AMOVD
-		p1.From.Type = obj.TYPE_REG
-		p1.From.Reg = arm64.REGZERO
-		p1.To.Type = obj.TYPE_MEM
-		p1.To.Reg = p.From.Reg
-		p1.To.Offset = 0
-	}
-}
-
-// res = runtime.getg()
-func getg(res *gc.Node) {
-	var n1 gc.Node
-	gc.Nodreg(&n1, res.Type, arm64.REGG)
-	gmove(&n1, res)
+	p := gc.Prog(arm64.AHINT)
+	p.From.Type = obj.TYPE_CONST
 }
diff --git a/src/cmd/compile/internal/arm64/gsubr.go b/src/cmd/compile/internal/arm64/gsubr.go
deleted file mode 100644
index ddf2ed9..0000000
--- a/src/cmd/compile/internal/arm64/gsubr.go
+++ /dev/null
@@ -1,983 +0,0 @@
-// Derived from Inferno utils/6c/txt.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package arm64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/arm64"
-	"fmt"
-)
-
-var resvd = []int{
-	arm64.REGTMP,
-	arm64.REGG,
-	arm64.REGRT1,
-	arm64.REGRT2,
-	arm64.REG_R31, // REGZERO and REGSP
-	arm64.FREGZERO,
-	arm64.FREGHALF,
-	arm64.FREGONE,
-	arm64.FREGTWO,
-}
-
-/*
- * generate
- *	as $c, n
- */
-func ginscon(as obj.As, c int64, n2 *gc.Node) {
-	var n1 gc.Node
-
-	gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
-
-	if as != arm64.AMOVD && (c < -arm64.BIG || c > arm64.BIG) || as == arm64.AMUL || n2 != nil && n2.Op != gc.OREGISTER {
-		// cannot have more than 16-bit of immediate in ADD, etc.
-		// instead, MOV into register first.
-		var ntmp gc.Node
-		gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil)
-
-		gins(arm64.AMOVD, &n1, &ntmp)
-		gins(as, &ntmp, n2)
-		gc.Regfree(&ntmp)
-		return
-	}
-
-	rawgins(as, &n1, n2)
-}
-
-/*
- * generate
- *	as n, $c (CMP)
- */
-func ginscon2(as obj.As, n2 *gc.Node, c int64) {
-	var n1 gc.Node
-
-	gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
-
-	switch as {
-	default:
-		gc.Fatalf("ginscon2")
-
-	case arm64.ACMP:
-		if -arm64.BIG <= c && c <= arm64.BIG {
-			gcmp(as, n2, &n1)
-			return
-		}
-	}
-
-	// MOV n1 into register first
-	var ntmp gc.Node
-	gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil)
-
-	rawgins(arm64.AMOVD, &n1, &ntmp)
-	gcmp(as, n2, &ntmp)
-	gc.Regfree(&ntmp)
-}
-
-func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
-	if t.IsInteger() && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL {
-		// Reverse comparison to place constant last.
-		op = gc.Brrev(op)
-		n1, n2 = n2, n1
-	}
-
-	var r1, r2, g1, g2 gc.Node
-	gc.Regalloc(&r1, t, n1)
-	gc.Regalloc(&g1, n1.Type, &r1)
-	gc.Cgen(n1, &g1)
-	gmove(&g1, &r1)
-	if t.IsInteger() && gc.Isconst(n2, gc.CTINT) {
-		ginscon2(optoas(gc.OCMP, t), &r1, n2.Int64())
-	} else {
-		gc.Regalloc(&r2, t, n2)
-		gc.Regalloc(&g2, n1.Type, &r2)
-		gc.Cgen(n2, &g2)
-		gmove(&g2, &r2)
-		gcmp(optoas(gc.OCMP, t), &r1, &r2)
-		gc.Regfree(&g2)
-		gc.Regfree(&r2)
-	}
-	gc.Regfree(&g1)
-	gc.Regfree(&r1)
-	return gc.Gbranch(optoas(op, t), nil, likely)
-}
-
-/*
- * generate move:
- *	t = f
- * hard part is conversions.
- */
-func gmove(f *gc.Node, t *gc.Node) {
-	if gc.Debug['M'] != 0 {
-		fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, gc.FmtLong), gc.Nconv(t, gc.FmtLong))
-	}
-
-	ft := int(gc.Simsimtype(f.Type))
-	tt := int(gc.Simsimtype(t.Type))
-	cvt := t.Type
-
-	if gc.Iscomplex[ft] || gc.Iscomplex[tt] {
-		gc.Complexmove(f, t)
-		return
-	}
-
-	// cannot have two memory operands
-	var r1 gc.Node
-	var a obj.As
-	if gc.Ismem(f) && gc.Ismem(t) {
-		goto hard
-	}
-
-	// convert constant to desired type
-	if f.Op == gc.OLITERAL {
-		var con gc.Node
-		switch tt {
-		default:
-			f.Convconst(&con, t.Type)
-
-		case gc.TINT32,
-			gc.TINT16,
-			gc.TINT8:
-			var con gc.Node
-			f.Convconst(&con, gc.Types[gc.TINT64])
-			var r1 gc.Node
-			gc.Regalloc(&r1, con.Type, t)
-			gins(arm64.AMOVD, &con, &r1)
-			gmove(&r1, t)
-			gc.Regfree(&r1)
-			return
-
-		case gc.TUINT32,
-			gc.TUINT16,
-			gc.TUINT8:
-			var con gc.Node
-			f.Convconst(&con, gc.Types[gc.TUINT64])
-			var r1 gc.Node
-			gc.Regalloc(&r1, con.Type, t)
-			gins(arm64.AMOVD, &con, &r1)
-			gmove(&r1, t)
-			gc.Regfree(&r1)
-			return
-		}
-
-		f = &con
-		ft = tt // so big switch will choose a simple mov
-
-		// constants can't move directly to memory.
-		if gc.Ismem(t) {
-			goto hard
-		}
-	}
-
-	// value -> value copy, first operand in memory.
-	// any floating point operand requires register
-	// src, so goto hard to copy to register first.
-	if gc.Ismem(f) && ft != tt && (gc.Isfloat[ft] || gc.Isfloat[tt]) {
-		cvt = gc.Types[ft]
-		goto hard
-	}
-
-	// value -> value copy, only one memory operand.
-	// figure out the instruction to use.
-	// break out of switch for one-instruction gins.
-	// goto rdst for "destination must be register".
-	// goto hard for "convert to cvt type first".
-	// otherwise handle and return.
-
-	switch uint32(ft)<<16 | uint32(tt) {
-	default:
-		gc.Fatalf("gmove %v -> %v", gc.Tconv(f.Type, gc.FmtLong), gc.Tconv(t.Type, gc.FmtLong))
-
-		/*
-		 * integer copy and truncate
-		 */
-	case gc.TINT8<<16 | gc.TINT8, // same size
-		gc.TUINT8<<16 | gc.TINT8,
-		gc.TINT16<<16 | gc.TINT8,
-		// truncate
-		gc.TUINT16<<16 | gc.TINT8,
-		gc.TINT32<<16 | gc.TINT8,
-		gc.TUINT32<<16 | gc.TINT8,
-		gc.TINT64<<16 | gc.TINT8,
-		gc.TUINT64<<16 | gc.TINT8:
-		a = arm64.AMOVB
-
-	case gc.TINT8<<16 | gc.TUINT8, // same size
-		gc.TUINT8<<16 | gc.TUINT8,
-		gc.TINT16<<16 | gc.TUINT8,
-		// truncate
-		gc.TUINT16<<16 | gc.TUINT8,
-		gc.TINT32<<16 | gc.TUINT8,
-		gc.TUINT32<<16 | gc.TUINT8,
-		gc.TINT64<<16 | gc.TUINT8,
-		gc.TUINT64<<16 | gc.TUINT8:
-		a = arm64.AMOVBU
-
-	case gc.TINT16<<16 | gc.TINT16, // same size
-		gc.TUINT16<<16 | gc.TINT16,
-		gc.TINT32<<16 | gc.TINT16,
-		// truncate
-		gc.TUINT32<<16 | gc.TINT16,
-		gc.TINT64<<16 | gc.TINT16,
-		gc.TUINT64<<16 | gc.TINT16:
-		a = arm64.AMOVH
-
-	case gc.TINT16<<16 | gc.TUINT16, // same size
-		gc.TUINT16<<16 | gc.TUINT16,
-		gc.TINT32<<16 | gc.TUINT16,
-		// truncate
-		gc.TUINT32<<16 | gc.TUINT16,
-		gc.TINT64<<16 | gc.TUINT16,
-		gc.TUINT64<<16 | gc.TUINT16:
-		a = arm64.AMOVHU
-
-	case gc.TINT32<<16 | gc.TINT32, // same size
-		gc.TUINT32<<16 | gc.TINT32,
-		gc.TINT64<<16 | gc.TINT32,
-		// truncate
-		gc.TUINT64<<16 | gc.TINT32:
-		a = arm64.AMOVW
-
-	case gc.TINT32<<16 | gc.TUINT32, // same size
-		gc.TUINT32<<16 | gc.TUINT32,
-		gc.TINT64<<16 | gc.TUINT32,
-		gc.TUINT64<<16 | gc.TUINT32:
-		a = arm64.AMOVWU
-
-	case gc.TINT64<<16 | gc.TINT64, // same size
-		gc.TINT64<<16 | gc.TUINT64,
-		gc.TUINT64<<16 | gc.TINT64,
-		gc.TUINT64<<16 | gc.TUINT64:
-		a = arm64.AMOVD
-
-		/*
-		 * integer up-conversions
-		 */
-	case gc.TINT8<<16 | gc.TINT16, // sign extend int8
-		gc.TINT8<<16 | gc.TUINT16,
-		gc.TINT8<<16 | gc.TINT32,
-		gc.TINT8<<16 | gc.TUINT32,
-		gc.TINT8<<16 | gc.TINT64,
-		gc.TINT8<<16 | gc.TUINT64:
-		a = arm64.AMOVB
-
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8
-		gc.TUINT8<<16 | gc.TUINT16,
-		gc.TUINT8<<16 | gc.TINT32,
-		gc.TUINT8<<16 | gc.TUINT32,
-		gc.TUINT8<<16 | gc.TINT64,
-		gc.TUINT8<<16 | gc.TUINT64:
-		a = arm64.AMOVBU
-
-		goto rdst
-
-	case gc.TINT16<<16 | gc.TINT32, // sign extend int16
-		gc.TINT16<<16 | gc.TUINT32,
-		gc.TINT16<<16 | gc.TINT64,
-		gc.TINT16<<16 | gc.TUINT64:
-		a = arm64.AMOVH
-
-		goto rdst
-
-	case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16
-		gc.TUINT16<<16 | gc.TUINT32,
-		gc.TUINT16<<16 | gc.TINT64,
-		gc.TUINT16<<16 | gc.TUINT64:
-		a = arm64.AMOVHU
-
-		goto rdst
-
-	case gc.TINT32<<16 | gc.TINT64, // sign extend int32
-		gc.TINT32<<16 | gc.TUINT64:
-		a = arm64.AMOVW
-
-		goto rdst
-
-	case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32
-		gc.TUINT32<<16 | gc.TUINT64:
-		a = arm64.AMOVWU
-
-		goto rdst
-
-	/*
-	* float to integer
-	 */
-	case gc.TFLOAT32<<16 | gc.TINT32:
-		a = arm64.AFCVTZSSW
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TINT32:
-		a = arm64.AFCVTZSDW
-		goto rdst
-
-	case gc.TFLOAT32<<16 | gc.TINT64:
-		a = arm64.AFCVTZSS
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TINT64:
-		a = arm64.AFCVTZSD
-		goto rdst
-
-	case gc.TFLOAT32<<16 | gc.TUINT32:
-		a = arm64.AFCVTZUSW
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TUINT32:
-		a = arm64.AFCVTZUDW
-		goto rdst
-
-	case gc.TFLOAT32<<16 | gc.TUINT64:
-		a = arm64.AFCVTZUS
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TUINT64:
-		a = arm64.AFCVTZUD
-		goto rdst
-
-	case gc.TFLOAT32<<16 | gc.TINT16,
-		gc.TFLOAT32<<16 | gc.TINT8,
-		gc.TFLOAT64<<16 | gc.TINT16,
-		gc.TFLOAT64<<16 | gc.TINT8:
-		cvt = gc.Types[gc.TINT32]
-
-		goto hard
-
-	case gc.TFLOAT32<<16 | gc.TUINT16,
-		gc.TFLOAT32<<16 | gc.TUINT8,
-		gc.TFLOAT64<<16 | gc.TUINT16,
-		gc.TFLOAT64<<16 | gc.TUINT8:
-		cvt = gc.Types[gc.TUINT32]
-
-		goto hard
-
-	/*
-	 * integer to float
-	 */
-	case gc.TINT8<<16 | gc.TFLOAT32,
-		gc.TINT16<<16 | gc.TFLOAT32,
-		gc.TINT32<<16 | gc.TFLOAT32:
-		a = arm64.ASCVTFWS
-
-		goto rdst
-
-	case gc.TINT8<<16 | gc.TFLOAT64,
-		gc.TINT16<<16 | gc.TFLOAT64,
-		gc.TINT32<<16 | gc.TFLOAT64:
-		a = arm64.ASCVTFWD
-
-		goto rdst
-
-	case gc.TINT64<<16 | gc.TFLOAT32:
-		a = arm64.ASCVTFS
-		goto rdst
-
-	case gc.TINT64<<16 | gc.TFLOAT64:
-		a = arm64.ASCVTFD
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TFLOAT32,
-		gc.TUINT16<<16 | gc.TFLOAT32,
-		gc.TUINT32<<16 | gc.TFLOAT32:
-		a = arm64.AUCVTFWS
-
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TFLOAT64,
-		gc.TUINT16<<16 | gc.TFLOAT64,
-		gc.TUINT32<<16 | gc.TFLOAT64:
-		a = arm64.AUCVTFWD
-
-		goto rdst
-
-	case gc.TUINT64<<16 | gc.TFLOAT32:
-		a = arm64.AUCVTFS
-		goto rdst
-
-	case gc.TUINT64<<16 | gc.TFLOAT64:
-		a = arm64.AUCVTFD
-		goto rdst
-
-		/*
-		 * float to float
-		 */
-	case gc.TFLOAT32<<16 | gc.TFLOAT32:
-		a = arm64.AFMOVS
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT64:
-		a = arm64.AFMOVD
-
-	case gc.TFLOAT32<<16 | gc.TFLOAT64:
-		a = arm64.AFCVTSD
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT32:
-		a = arm64.AFCVTDS
-		goto rdst
-	}
-
-	gins(a, f, t)
-	return
-
-	// requires register destination
-rdst:
-	gc.Regalloc(&r1, t.Type, t)
-
-	gins(a, f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-
-	// requires register intermediate
-hard:
-	gc.Regalloc(&r1, cvt, t)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-}
-
-// gins is called by the front end.
-// It synthesizes some multiple-instruction sequences
-// so the front end can stay simpler.
-func gins(as obj.As, f, t *gc.Node) *obj.Prog {
-	if as >= obj.A_ARCHSPECIFIC {
-		if x, ok := f.IntLiteral(); ok {
-			ginscon(as, x, t)
-			return nil // caller must not use
-		}
-	}
-	if as == arm64.ACMP {
-		if x, ok := t.IntLiteral(); ok {
-			ginscon2(as, f, x)
-			return nil // caller must not use
-		}
-	}
-	return rawgins(as, f, t)
-}
-
-/*
- * generate one instruction:
- *	as f, t
- */
-func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
-	// TODO(austin): Add self-move test like in 6g (but be careful
-	// of truncation moves)
-
-	p := gc.Prog(as)
-	gc.Naddr(&p.From, f)
-	gc.Naddr(&p.To, t)
-
-	switch as {
-	case arm64.ACMP, arm64.AFCMPS, arm64.AFCMPD:
-		if t != nil {
-			if f.Op != gc.OREGISTER {
-				gc.Fatalf("bad operands to gcmp")
-			}
-			p.From = p.To
-			p.To = obj.Addr{}
-			raddr(f, p)
-		}
-	}
-
-	// Bad things the front end has done to us. Crash to find call stack.
-	switch as {
-	case arm64.AAND, arm64.AMUL:
-		if p.From.Type == obj.TYPE_CONST {
-			gc.Debug['h'] = 1
-			gc.Fatalf("bad inst: %v", p)
-		}
-	case arm64.ACMP:
-		if p.From.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_MEM {
-			gc.Debug['h'] = 1
-			gc.Fatalf("bad inst: %v", p)
-		}
-	}
-
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("%v\n", p)
-	}
-
-	w := int32(0)
-	switch as {
-	case arm64.AMOVB,
-		arm64.AMOVBU:
-		w = 1
-
-	case arm64.AMOVH,
-		arm64.AMOVHU:
-		w = 2
-
-	case arm64.AMOVW,
-		arm64.AMOVWU:
-		w = 4
-
-	case arm64.AMOVD:
-		if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_ADDR {
-			break
-		}
-		w = 8
-	}
-
-	if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Type != obj.TYPE_REG && p.To.Width > int64(w))) {
-		gc.Dump("f", f)
-		gc.Dump("t", t)
-		gc.Fatalf("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width)
-	}
-
-	return p
-}
-
-/*
- * insert n into reg slot of p
- */
-func raddr(n *gc.Node, p *obj.Prog) {
-	var a obj.Addr
-
-	gc.Naddr(&a, n)
-	if a.Type != obj.TYPE_REG {
-		if n != nil {
-			gc.Fatalf("bad in raddr: %v", n.Op)
-		} else {
-			gc.Fatalf("bad in raddr: <null>")
-		}
-		p.Reg = 0
-	} else {
-		p.Reg = a.Reg
-	}
-}
-
-func gcmp(as obj.As, lhs *gc.Node, rhs *gc.Node) *obj.Prog {
-	if lhs.Op != gc.OREGISTER {
-		gc.Fatalf("bad operands to gcmp: %v %v", lhs.Op, rhs.Op)
-	}
-
-	p := rawgins(as, rhs, nil)
-	raddr(lhs, p)
-	return p
-}
-
-/*
- * return Axxx for Oxxx on type t.
- */
-func optoas(op gc.Op, t *gc.Type) obj.As {
-	if t == nil {
-		gc.Fatalf("optoas: t is nil")
-	}
-
-	// avoid constant conversions in switches below
-	const (
-		OMINUS_ = uint32(gc.OMINUS) << 16
-		OLSH_   = uint32(gc.OLSH) << 16
-		ORSH_   = uint32(gc.ORSH) << 16
-		OADD_   = uint32(gc.OADD) << 16
-		OSUB_   = uint32(gc.OSUB) << 16
-		OMUL_   = uint32(gc.OMUL) << 16
-		ODIV_   = uint32(gc.ODIV) << 16
-		OOR_    = uint32(gc.OOR) << 16
-		OAND_   = uint32(gc.OAND) << 16
-		OXOR_   = uint32(gc.OXOR) << 16
-		OEQ_    = uint32(gc.OEQ) << 16
-		ONE_    = uint32(gc.ONE) << 16
-		OLT_    = uint32(gc.OLT) << 16
-		OLE_    = uint32(gc.OLE) << 16
-		OGE_    = uint32(gc.OGE) << 16
-		OGT_    = uint32(gc.OGT) << 16
-		OCMP_   = uint32(gc.OCMP) << 16
-		OAS_    = uint32(gc.OAS) << 16
-		OHMUL_  = uint32(gc.OHMUL) << 16
-		OSQRT_  = uint32(gc.OSQRT) << 16
-	)
-
-	a := obj.AXXX
-	switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
-	default:
-		gc.Fatalf("optoas: no entry for op=%v type=%v", op, t)
-
-	case OEQ_ | gc.TBOOL,
-		OEQ_ | gc.TINT8,
-		OEQ_ | gc.TUINT8,
-		OEQ_ | gc.TINT16,
-		OEQ_ | gc.TUINT16,
-		OEQ_ | gc.TINT32,
-		OEQ_ | gc.TUINT32,
-		OEQ_ | gc.TINT64,
-		OEQ_ | gc.TUINT64,
-		OEQ_ | gc.TPTR32,
-		OEQ_ | gc.TPTR64,
-		OEQ_ | gc.TFLOAT32,
-		OEQ_ | gc.TFLOAT64:
-		a = arm64.ABEQ
-
-	case ONE_ | gc.TBOOL,
-		ONE_ | gc.TINT8,
-		ONE_ | gc.TUINT8,
-		ONE_ | gc.TINT16,
-		ONE_ | gc.TUINT16,
-		ONE_ | gc.TINT32,
-		ONE_ | gc.TUINT32,
-		ONE_ | gc.TINT64,
-		ONE_ | gc.TUINT64,
-		ONE_ | gc.TPTR32,
-		ONE_ | gc.TPTR64,
-		ONE_ | gc.TFLOAT32,
-		ONE_ | gc.TFLOAT64:
-		a = arm64.ABNE
-
-	case OLT_ | gc.TINT8,
-		OLT_ | gc.TINT16,
-		OLT_ | gc.TINT32,
-		OLT_ | gc.TINT64:
-		a = arm64.ABLT
-
-	case OLT_ | gc.TUINT8,
-		OLT_ | gc.TUINT16,
-		OLT_ | gc.TUINT32,
-		OLT_ | gc.TUINT64,
-		OLT_ | gc.TFLOAT32,
-		OLT_ | gc.TFLOAT64:
-		a = arm64.ABLO
-
-	case OLE_ | gc.TINT8,
-		OLE_ | gc.TINT16,
-		OLE_ | gc.TINT32,
-		OLE_ | gc.TINT64:
-		a = arm64.ABLE
-
-	case OLE_ | gc.TUINT8,
-		OLE_ | gc.TUINT16,
-		OLE_ | gc.TUINT32,
-		OLE_ | gc.TUINT64,
-		OLE_ | gc.TFLOAT32,
-		OLE_ | gc.TFLOAT64:
-		a = arm64.ABLS
-
-	case OGT_ | gc.TINT8,
-		OGT_ | gc.TINT16,
-		OGT_ | gc.TINT32,
-		OGT_ | gc.TINT64,
-		OGT_ | gc.TFLOAT32,
-		OGT_ | gc.TFLOAT64:
-		a = arm64.ABGT
-
-	case OGT_ | gc.TUINT8,
-		OGT_ | gc.TUINT16,
-		OGT_ | gc.TUINT32,
-		OGT_ | gc.TUINT64:
-		a = arm64.ABHI
-
-	case OGE_ | gc.TINT8,
-		OGE_ | gc.TINT16,
-		OGE_ | gc.TINT32,
-		OGE_ | gc.TINT64,
-		OGE_ | gc.TFLOAT32,
-		OGE_ | gc.TFLOAT64:
-		a = arm64.ABGE
-
-	case OGE_ | gc.TUINT8,
-		OGE_ | gc.TUINT16,
-		OGE_ | gc.TUINT32,
-		OGE_ | gc.TUINT64:
-		a = arm64.ABHS
-
-	case OCMP_ | gc.TBOOL,
-		OCMP_ | gc.TINT8,
-		OCMP_ | gc.TINT16,
-		OCMP_ | gc.TINT32,
-		OCMP_ | gc.TPTR32,
-		OCMP_ | gc.TINT64,
-		OCMP_ | gc.TUINT8,
-		OCMP_ | gc.TUINT16,
-		OCMP_ | gc.TUINT32,
-		OCMP_ | gc.TUINT64,
-		OCMP_ | gc.TPTR64:
-		a = arm64.ACMP
-
-	case OCMP_ | gc.TFLOAT32:
-		a = arm64.AFCMPS
-
-	case OCMP_ | gc.TFLOAT64:
-		a = arm64.AFCMPD
-
-	case OAS_ | gc.TBOOL,
-		OAS_ | gc.TINT8:
-		a = arm64.AMOVB
-
-	case OAS_ | gc.TUINT8:
-		a = arm64.AMOVBU
-
-	case OAS_ | gc.TINT16:
-		a = arm64.AMOVH
-
-	case OAS_ | gc.TUINT16:
-		a = arm64.AMOVHU
-
-	case OAS_ | gc.TINT32:
-		a = arm64.AMOVW
-
-	case OAS_ | gc.TUINT32,
-		OAS_ | gc.TPTR32:
-		a = arm64.AMOVWU
-
-	case OAS_ | gc.TINT64,
-		OAS_ | gc.TUINT64,
-		OAS_ | gc.TPTR64:
-		a = arm64.AMOVD
-
-	case OAS_ | gc.TFLOAT32:
-		a = arm64.AFMOVS
-
-	case OAS_ | gc.TFLOAT64:
-		a = arm64.AFMOVD
-
-	case OADD_ | gc.TINT8,
-		OADD_ | gc.TUINT8,
-		OADD_ | gc.TINT16,
-		OADD_ | gc.TUINT16,
-		OADD_ | gc.TINT32,
-		OADD_ | gc.TUINT32,
-		OADD_ | gc.TPTR32,
-		OADD_ | gc.TINT64,
-		OADD_ | gc.TUINT64,
-		OADD_ | gc.TPTR64:
-		a = arm64.AADD
-
-	case OADD_ | gc.TFLOAT32:
-		a = arm64.AFADDS
-
-	case OADD_ | gc.TFLOAT64:
-		a = arm64.AFADDD
-
-	case OSUB_ | gc.TINT8,
-		OSUB_ | gc.TUINT8,
-		OSUB_ | gc.TINT16,
-		OSUB_ | gc.TUINT16,
-		OSUB_ | gc.TINT32,
-		OSUB_ | gc.TUINT32,
-		OSUB_ | gc.TPTR32,
-		OSUB_ | gc.TINT64,
-		OSUB_ | gc.TUINT64,
-		OSUB_ | gc.TPTR64:
-		a = arm64.ASUB
-
-	case OSUB_ | gc.TFLOAT32:
-		a = arm64.AFSUBS
-
-	case OSUB_ | gc.TFLOAT64:
-		a = arm64.AFSUBD
-
-	case OMINUS_ | gc.TINT8,
-		OMINUS_ | gc.TUINT8,
-		OMINUS_ | gc.TINT16,
-		OMINUS_ | gc.TUINT16,
-		OMINUS_ | gc.TINT32,
-		OMINUS_ | gc.TUINT32,
-		OMINUS_ | gc.TPTR32,
-		OMINUS_ | gc.TINT64,
-		OMINUS_ | gc.TUINT64,
-		OMINUS_ | gc.TPTR64:
-		a = arm64.ANEG
-
-	case OMINUS_ | gc.TFLOAT32:
-		a = arm64.AFNEGS
-
-	case OMINUS_ | gc.TFLOAT64:
-		a = arm64.AFNEGD
-
-	case OAND_ | gc.TINT8,
-		OAND_ | gc.TUINT8,
-		OAND_ | gc.TINT16,
-		OAND_ | gc.TUINT16,
-		OAND_ | gc.TINT32,
-		OAND_ | gc.TUINT32,
-		OAND_ | gc.TPTR32,
-		OAND_ | gc.TINT64,
-		OAND_ | gc.TUINT64,
-		OAND_ | gc.TPTR64:
-		a = arm64.AAND
-
-	case OOR_ | gc.TINT8,
-		OOR_ | gc.TUINT8,
-		OOR_ | gc.TINT16,
-		OOR_ | gc.TUINT16,
-		OOR_ | gc.TINT32,
-		OOR_ | gc.TUINT32,
-		OOR_ | gc.TPTR32,
-		OOR_ | gc.TINT64,
-		OOR_ | gc.TUINT64,
-		OOR_ | gc.TPTR64:
-		a = arm64.AORR
-
-	case OXOR_ | gc.TINT8,
-		OXOR_ | gc.TUINT8,
-		OXOR_ | gc.TINT16,
-		OXOR_ | gc.TUINT16,
-		OXOR_ | gc.TINT32,
-		OXOR_ | gc.TUINT32,
-		OXOR_ | gc.TPTR32,
-		OXOR_ | gc.TINT64,
-		OXOR_ | gc.TUINT64,
-		OXOR_ | gc.TPTR64:
-		a = arm64.AEOR
-
-		// TODO(minux): handle rotates
-	//case CASE(OLROT, TINT8):
-	//case CASE(OLROT, TUINT8):
-	//case CASE(OLROT, TINT16):
-	//case CASE(OLROT, TUINT16):
-	//case CASE(OLROT, TINT32):
-	//case CASE(OLROT, TUINT32):
-	//case CASE(OLROT, TPTR32):
-	//case CASE(OLROT, TINT64):
-	//case CASE(OLROT, TUINT64):
-	//case CASE(OLROT, TPTR64):
-	//	a = 0//???; RLDC?
-	//	break;
-
-	case OLSH_ | gc.TINT8,
-		OLSH_ | gc.TUINT8,
-		OLSH_ | gc.TINT16,
-		OLSH_ | gc.TUINT16,
-		OLSH_ | gc.TINT32,
-		OLSH_ | gc.TUINT32,
-		OLSH_ | gc.TPTR32,
-		OLSH_ | gc.TINT64,
-		OLSH_ | gc.TUINT64,
-		OLSH_ | gc.TPTR64:
-		a = arm64.ALSL
-
-	case ORSH_ | gc.TUINT8,
-		ORSH_ | gc.TUINT16,
-		ORSH_ | gc.TUINT32,
-		ORSH_ | gc.TPTR32,
-		ORSH_ | gc.TUINT64,
-		ORSH_ | gc.TPTR64:
-		a = arm64.ALSR
-
-	case ORSH_ | gc.TINT8,
-		ORSH_ | gc.TINT16,
-		ORSH_ | gc.TINT32,
-		ORSH_ | gc.TINT64:
-		a = arm64.AASR
-
-	case OHMUL_ | gc.TINT64:
-		a = arm64.ASMULH
-
-	case OHMUL_ | gc.TUINT64,
-		OHMUL_ | gc.TPTR64:
-		a = arm64.AUMULH
-
-	case OMUL_ | gc.TINT8,
-		OMUL_ | gc.TINT16,
-		OMUL_ | gc.TINT32:
-		a = arm64.ASMULL
-
-	case OMUL_ | gc.TINT64:
-		a = arm64.AMUL
-
-	case OMUL_ | gc.TUINT8,
-		OMUL_ | gc.TUINT16,
-		OMUL_ | gc.TUINT32,
-		OMUL_ | gc.TPTR32:
-		// don't use word multiply, the high 32-bit are undefined.
-		a = arm64.AUMULL
-
-	case OMUL_ | gc.TUINT64,
-		OMUL_ | gc.TPTR64:
-		a = arm64.AMUL // for 64-bit multiplies, signedness doesn't matter.
-
-	case OMUL_ | gc.TFLOAT32:
-		a = arm64.AFMULS
-
-	case OMUL_ | gc.TFLOAT64:
-		a = arm64.AFMULD
-
-	case ODIV_ | gc.TINT8,
-		ODIV_ | gc.TINT16,
-		ODIV_ | gc.TINT32,
-		ODIV_ | gc.TINT64:
-		a = arm64.ASDIV
-
-	case ODIV_ | gc.TUINT8,
-		ODIV_ | gc.TUINT16,
-		ODIV_ | gc.TUINT32,
-		ODIV_ | gc.TPTR32,
-		ODIV_ | gc.TUINT64,
-		ODIV_ | gc.TPTR64:
-		a = arm64.AUDIV
-
-	case ODIV_ | gc.TFLOAT32:
-		a = arm64.AFDIVS
-
-	case ODIV_ | gc.TFLOAT64:
-		a = arm64.AFDIVD
-
-	case OSQRT_ | gc.TFLOAT64:
-		a = arm64.AFSQRTD
-	}
-
-	return a
-}
-
-const (
-	ODynam   = 1 << 0
-	OAddable = 1 << 1
-)
-
-func xgen(n *gc.Node, a *gc.Node, o int) bool {
-	// TODO(minux)
-
-	return -1 != 0 /*TypeKind(100016)*/
-}
-
-func sudoclean() {
-	return
-}
-
-/*
- * generate code to compute address of n,
- * a reference to a (perhaps nested) field inside
- * an array or struct.
- * return 0 on failure, 1 on success.
- * on success, leaves usable address in a.
- *
- * caller is responsible for calling sudoclean
- * after successful sudoaddable,
- * to release the register used for a.
- */
-func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool {
-	// TODO(minux)
-
-	*a = obj.Addr{}
-	return false
-}
diff --git a/src/cmd/compile/internal/arm64/peep.go b/src/cmd/compile/internal/arm64/peep.go
deleted file mode 100644
index e32c264..0000000
--- a/src/cmd/compile/internal/arm64/peep.go
+++ /dev/null
@@ -1,797 +0,0 @@
-// Derived from Inferno utils/6c/peep.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package arm64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/arm64"
-	"fmt"
-)
-
-var gactive uint32
-
-func peep(firstp *obj.Prog) {
-	g := gc.Flowstart(firstp, nil)
-	if g == nil {
-		return
-	}
-	gactive = 0
-
-	var p *obj.Prog
-	var r *gc.Flow
-	var t int
-loop1:
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		gc.Dumpit("loop1", g.Start, 0)
-	}
-
-	t = 0
-	for r = g.Start; r != nil; r = r.Link {
-		p = r.Prog
-
-		// TODO(minux) Handle smaller moves. arm and amd64
-		// distinguish between moves that *must* sign/zero
-		// extend and moves that don't care so they
-		// can eliminate moves that don't care without
-		// breaking moves that do care. This might let us
-		// simplify or remove the next peep loop, too.
-		if p.As == arm64.AMOVD || p.As == arm64.AFMOVD {
-			if regtyp(&p.To) {
-				// Try to eliminate reg->reg moves
-				if regtyp(&p.From) {
-					if p.From.Type == p.To.Type {
-						if copyprop(r) {
-							excise(r)
-							t++
-						} else if subprop(r) && copyprop(r) {
-							excise(r)
-							t++
-						}
-					}
-				}
-			}
-		}
-	}
-
-	if t != 0 {
-		goto loop1
-	}
-
-	/*
-	 * look for MOVB x,R; MOVB R,R (for small MOVs not handled above)
-	 */
-	var p1 *obj.Prog
-	var r1 *gc.Flow
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		default:
-			continue
-
-		case arm64.AMOVH,
-			arm64.AMOVHU,
-			arm64.AMOVB,
-			arm64.AMOVBU,
-			arm64.AMOVW,
-			arm64.AMOVWU:
-			if p.To.Type != obj.TYPE_REG {
-				continue
-			}
-		}
-
-		r1 = r.Link
-		if r1 == nil {
-			continue
-		}
-		p1 = r1.Prog
-		if p1.As != p.As {
-			continue
-		}
-		if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
-			continue
-		}
-		if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg {
-			continue
-		}
-		excise(r1)
-	}
-
-	if gc.Debug['D'] > 1 {
-		goto ret /* allow following code improvement to be suppressed */
-	}
-
-	// MOVD $c, R'; ADD R', R (R' unused) -> ADD $c, R
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		default:
-			continue
-
-		case arm64.AMOVD:
-			if p.To.Type != obj.TYPE_REG {
-				continue
-			}
-			if p.From.Type != obj.TYPE_CONST {
-				continue
-			}
-			if p.From.Offset < 0 || 4096 <= p.From.Offset {
-				continue
-			}
-		}
-		r1 = r.Link
-		if r1 == nil {
-			continue
-		}
-		p1 = r1.Prog
-		if p1.As != arm64.AADD && p1.As != arm64.ASUB { // TODO(aram): also logical after we have bimm.
-			continue
-		}
-		if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
-			continue
-		}
-		if p1.To.Type != obj.TYPE_REG {
-			continue
-		}
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("encoding $%d directly into %v in:\n%v\n%v\n", p.From.Offset, obj.Aconv(p1.As), p, p1)
-		}
-		p1.From.Type = obj.TYPE_CONST
-		p1.From = p.From
-		excise(r)
-	}
-
-	/* TODO(minux):
-	 * look for OP x,y,R; CMP R, $0 -> OP.S x,y,R
-	 * when OP can set condition codes correctly
-	 */
-
-ret:
-	gc.Flowend(g)
-}
-
-func excise(r *gc.Flow) {
-	p := r.Prog
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("%v ===delete===\n", p)
-	}
-	obj.Nopout(p)
-	gc.Ostats.Ndelmov++
-}
-
-func regtyp(a *obj.Addr) bool {
-	// TODO(rsc): Floating point register exclusions?
-	return a.Type == obj.TYPE_REG && arm64.REG_R0 <= a.Reg && a.Reg <= arm64.REG_F31 && a.Reg != arm64.REGZERO
-}
-
-/*
- * the idea is to substitute
- * one register for another
- * from one MOV to another
- *	MOV	a, R1
- *	ADD	b, R1	/ no use of R2
- *	MOV	R1, R2
- * would be converted to
- *	MOV	a, R2
- *	ADD	b, R2
- *	MOV	R2, R1
- * hopefully, then the former or latter MOV
- * will be eliminated by copy propagation.
- *
- * r0 (the argument, not the register) is the MOV at the end of the
- * above sequences. This returns 1 if it modified any instructions.
- */
-func subprop(r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	if !regtyp(v1) {
-		return false
-	}
-	v2 := &p.To
-	if !regtyp(v2) {
-		return false
-	}
-	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
-		if gc.Uniqs(r) == nil {
-			break
-		}
-		p = r.Prog
-		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
-			continue
-		}
-		if p.Info.Flags&gc.Call != 0 {
-			return false
-		}
-
-		if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite {
-			if p.To.Type == v1.Type {
-				if p.To.Reg == v1.Reg {
-					copysub(&p.To, v1, v2, true)
-					if gc.Debug['P'] != 0 {
-						fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
-						if p.From.Type == v2.Type {
-							fmt.Printf(" excise")
-						}
-						fmt.Printf("\n")
-					}
-
-					for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
-						p = r.Prog
-						copysub(&p.From, v1, v2, true)
-						copysub1(p, v1, v2, true)
-						copysub(&p.To, v1, v2, true)
-						if gc.Debug['P'] != 0 {
-							fmt.Printf("%v\n", r.Prog)
-						}
-					}
-
-					v1.Reg, v2.Reg = v2.Reg, v1.Reg
-					if gc.Debug['P'] != 0 {
-						fmt.Printf("%v last\n", r.Prog)
-					}
-					return true
-				}
-			}
-		}
-
-		if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) {
-			break
-		}
-		if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) {
-			break
-		}
-	}
-
-	return false
-}
-
-/*
- * The idea is to remove redundant copies.
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	use v2	return fail (v1->v2 move must remain)
- *	-----------------
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	set v2	return success (caller can remove v1->v2 move)
- */
-func copyprop(r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	v2 := &p.To
-	if copyas(v1, v2) {
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("eliminating self-move: %v\n", r0.Prog)
-		}
-		return true
-	}
-
-	gactive++
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog)
-	}
-	return copy1(v1, v2, r0.S1, false)
-}
-
-// copy1 replaces uses of v2 with v1 starting at r and returns 1 if
-// all uses were rewritten.
-func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool {
-	if uint32(r.Active) == gactive {
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("act set; return 1\n")
-		}
-		return true
-	}
-
-	r.Active = int32(gactive)
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("copy1 replace %v with %v f=%v\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f)
-	}
-	for ; r != nil; r = r.S1 {
-		p := r.Prog
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("%v", p)
-		}
-		if !f && gc.Uniqp(r) == nil {
-			// Multiple predecessors; conservatively
-			// assume v1 was set on other path
-			f = true
-
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; merge; f=%v", f)
-			}
-		}
-
-		switch t := copyu(p, v2, nil); t {
-		case 2: /* rar, can't split */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
-			}
-			return false
-
-		case 3: /* set */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
-			}
-			return true
-
-		case 1, /* used, substitute */
-			4: /* use and set */
-			if f {
-				if gc.Debug['P'] == 0 {
-					return false
-				}
-				if t == 4 {
-					fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				} else {
-					fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				}
-				return false
-			}
-
-			if copyu(p, v2, v1) != 0 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; sub fail; return 0\n")
-				}
-				return false
-			}
-
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p)
-			}
-			if t == 4 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
-				}
-				return true
-			}
-		}
-
-		if !f {
-			t := copyu(p, v1, nil)
-			if t == 2 || t == 3 || t == 4 {
-				f = true
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f)
-				}
-			}
-		}
-
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("\n")
-		}
-		if r.S2 != nil {
-			if !copy1(v1, v2, r.S2, f) {
-				return false
-			}
-		}
-	}
-	return true
-}
-
-// If s==nil, copyu returns the set/use of v in p; otherwise, it
-// modifies p to replace reads of v with reads of s and returns 0 for
-// success or non-zero for failure.
-//
-// If s==nil, copy returns one of the following values:
-//	1 if v only used
-//	2 if v is set and used in one address (read-alter-rewrite;
-//	  can't substitute)
-//	3 if v is only set
-//	4 if v is set in one address and used in another (so addresses
-//	  can be rewritten independently)
-//	0 otherwise (not touched)
-func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
-	if p.From3Type() != obj.TYPE_NONE {
-		// 7g never generates a from3
-		fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3))
-	}
-	if p.RegTo2 != obj.REG_NONE {
-		// 7g never generates a to2
-		fmt.Printf("copyu: RegTo2 (%v) not implemented\n", obj.Rconv(int(p.RegTo2)))
-	}
-
-	switch p.As {
-	default:
-		fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As))
-		return 2
-
-	case obj.ANOP, /* read p->from, write p->to */
-		arm64.ANEG,
-		arm64.AFNEGD,
-		arm64.AFNEGS,
-		arm64.AFSQRTD,
-		arm64.AFCVTZSD,
-		arm64.AFCVTZSS,
-		arm64.AFCVTZSDW,
-		arm64.AFCVTZSSW,
-		arm64.AFCVTZUD,
-		arm64.AFCVTZUS,
-		arm64.AFCVTZUDW,
-		arm64.AFCVTZUSW,
-		arm64.AFCVTSD,
-		arm64.AFCVTDS,
-		arm64.ASCVTFD,
-		arm64.ASCVTFS,
-		arm64.ASCVTFWD,
-		arm64.ASCVTFWS,
-		arm64.AUCVTFD,
-		arm64.AUCVTFS,
-		arm64.AUCVTFWD,
-		arm64.AUCVTFWS,
-		arm64.AMOVB,
-		arm64.AMOVBU,
-		arm64.AMOVH,
-		arm64.AMOVHU,
-		arm64.AMOVW,
-		arm64.AMOVWU,
-		arm64.AMOVD,
-		arm64.AFMOVS,
-		arm64.AFMOVD:
-		if p.Scond == 0 {
-			if s != nil {
-				if copysub(&p.From, v, s, true) {
-					return 1
-				}
-
-				// Update only indirect uses of v in p->to
-				if !copyas(&p.To, v) {
-					if copysub(&p.To, v, s, true) {
-						return 1
-					}
-				}
-				return 0
-			}
-
-			if copyas(&p.To, v) {
-				// Fix up implicit from
-				if p.From.Type == obj.TYPE_NONE {
-					p.From = p.To
-				}
-				if copyau(&p.From, v) {
-					return 4
-				}
-				return 3
-			}
-
-			if copyau(&p.From, v) {
-				return 1
-			}
-			if copyau(&p.To, v) {
-				// p->to only indirectly uses v
-				return 1
-			}
-
-			return 0
-		}
-
-		/* rar p->from, write p->to or read p->from, rar p->to */
-		if p.From.Type == obj.TYPE_MEM {
-			if copyas(&p.From, v) {
-				// No s!=nil check; need to fail
-				// anyway in that case
-				return 2
-			}
-
-			if s != nil {
-				if copysub(&p.To, v, s, true) {
-					return 1
-				}
-				return 0
-			}
-
-			if copyas(&p.To, v) {
-				return 3
-			}
-		} else if p.To.Type == obj.TYPE_MEM {
-			if copyas(&p.To, v) {
-				return 2
-			}
-			if s != nil {
-				if copysub(&p.From, v, s, true) {
-					return 1
-				}
-				return 0
-			}
-
-			if copyau(&p.From, v) {
-				return 1
-			}
-		} else {
-			fmt.Printf("copyu: bad %v\n", p)
-		}
-
-		return 0
-
-	case arm64.AADD, /* read p->from, read p->reg, write p->to */
-		arm64.AADDS,
-		arm64.ASUB,
-		arm64.AADC,
-		arm64.AAND,
-		arm64.AORR,
-		arm64.AEOR,
-		arm64.AROR,
-		arm64.AMUL,
-		arm64.ASMULL,
-		arm64.AUMULL,
-		arm64.ASMULH,
-		arm64.AUMULH,
-		arm64.ASDIV,
-		arm64.AUDIV,
-		arm64.ALSL,
-		arm64.ALSR,
-		arm64.AASR,
-		arm64.AFADDD,
-		arm64.AFADDS,
-		arm64.AFSUBD,
-		arm64.AFSUBS,
-		arm64.AFMULD,
-		arm64.AFMULS,
-		arm64.AFDIVD,
-		arm64.AFDIVS:
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if copysub1(p, v, s, true) {
-				return 1
-			}
-
-			// Update only indirect uses of v in p->to
-			if !copyas(&p.To, v) {
-				if copysub(&p.To, v, s, true) {
-					return 1
-				}
-			}
-			return 0
-		}
-
-		if copyas(&p.To, v) {
-			if p.Reg == 0 {
-				// Fix up implicit reg (e.g., ADD
-				// R3,R4 -> ADD R3,R4,R4) so we can
-				// update reg and to separately.
-				p.Reg = p.To.Reg
-			}
-
-			if copyau(&p.From, v) {
-				return 4
-			}
-			if copyau1(p, v) {
-				return 4
-			}
-			return 3
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau1(p, v) {
-			return 1
-		}
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case arm64.ABEQ,
-		arm64.ABNE,
-		arm64.ABGE,
-		arm64.ABLT,
-		arm64.ABGT,
-		arm64.ABLE,
-		arm64.ABLO,
-		arm64.ABLS,
-		arm64.ABHI,
-		arm64.ABHS:
-		return 0
-
-	case obj.ACHECKNIL, /* read p->from */
-		arm64.ACMP, /* read p->from, read p->reg */
-		arm64.AFCMPD,
-		arm64.AFCMPS:
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if copysub1(p, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau1(p, v) {
-			return 1
-		}
-		return 0
-
-	case arm64.AB: /* read p->to */
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case obj.ARET: /* funny */
-		if s != nil {
-			return 0
-		}
-
-		// All registers die at this point, so claim
-		// everything is set (and not used).
-		return 3
-
-	case arm64.ABL: /* funny */
-		if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
-			return 2
-		}
-
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.To, v) {
-			return 4
-		}
-		return 3
-
-	// R31 is zero, used by DUFFZERO, cannot be substituted.
-	// R16 is ptr to memory, used and set, cannot be substituted.
-	case obj.ADUFFZERO:
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == 31 {
-				return 1
-			}
-			if v.Reg == 16 {
-				return 2
-			}
-		}
-
-		return 0
-
-	// R16, R17 are ptr to src, dst, used and set, cannot be substituted.
-	// R27 is scratch, set by DUFFCOPY, cannot be substituted.
-	case obj.ADUFFCOPY:
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == 16 || v.Reg == 17 {
-				return 2
-			}
-			if v.Reg == 27 {
-				return 3
-			}
-		}
-
-		return 0
-
-	case arm64.AHINT,
-		obj.ATEXT,
-		obj.APCDATA,
-		obj.AFUNCDATA,
-		obj.AVARDEF,
-		obj.AVARKILL,
-		obj.AVARLIVE,
-		obj.AUSEFIELD:
-		return 0
-	}
-}
-
-// copyas returns true if a and v address the same register.
-//
-// If a is the from operand, this means this operation reads the
-// register in v. If a is the to operand, this means this operation
-// writes the register in v.
-func copyas(a *obj.Addr, v *obj.Addr) bool {
-	return regtyp(v) && a.Type == v.Type && a.Reg == v.Reg
-}
-
-// copyau returns true if a either directly or indirectly addresses the
-// same register as v.
-//
-// If a is the from operand, this means this operation reads the
-// register in v. If a is the to operand, this means the operation
-// either reads or writes the register in v (if !copyas(a, v), then
-// the operation reads the register in v).
-func copyau(a *obj.Addr, v *obj.Addr) bool {
-	if copyas(a, v) {
-		return true
-	}
-	if v.Type == obj.TYPE_REG {
-		if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) {
-			if v.Reg == a.Reg {
-				return true
-			}
-		}
-	}
-	return false
-}
-
-// copyau1 returns true if p->reg references the same register as v and v
-// is a direct reference.
-func copyau1(p *obj.Prog, v *obj.Addr) bool {
-	return regtyp(v) && v.Reg != 0 && p.Reg == v.Reg
-}
-
-// copysub replaces v with s in a if f==true or indicates it if could if f==false.
-// Returns true on failure to substitute (it always succeeds on arm64).
-// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing.
-func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool {
-	if f && copyau(a, v) {
-		a.Reg = s.Reg
-	}
-	return false
-}
-
-// copysub1 replaces v with s in p1->reg if f==true or indicates if it could if f==false.
-// Returns true on failure to substitute (it always succeeds on arm64).
-// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing.
-func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool {
-	if f && copyau1(p1, v) {
-		p1.Reg = s.Reg
-	}
-	return false
-}
-
-func sameaddr(a *obj.Addr, v *obj.Addr) bool {
-	if a.Type != v.Type {
-		return false
-	}
-	if regtyp(v) && a.Reg == v.Reg {
-		return true
-	}
-	if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM {
-		if v.Offset == a.Offset {
-			return true
-		}
-	}
-	return false
-}
-
-func smallindir(a *obj.Addr, reg *obj.Addr) bool {
-	return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096
-}
-
-func stackaddr(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_REG && a.Reg == arm64.REGSP
-}
diff --git a/src/cmd/compile/internal/arm64/prog.go b/src/cmd/compile/internal/arm64/prog.go
index d504d0f..5d3ec67 100644
--- a/src/cmd/compile/internal/arm64/prog.go
+++ b/src/cmd/compile/internal/arm64/prog.go
@@ -24,14 +24,13 @@ const (
 // size variants of an operation even if we just use a subset.
 //
 // The table is formatted for 8-space tabs.
-var progtable = [arm64.ALAST & obj.AMask]obj.ProgInfo{
+var progtable = [arm64.ALAST & obj.AMask]gc.ProgInfo{
 	obj.ATYPE:     {Flags: gc.Pseudo | gc.Skip},
 	obj.ATEXT:     {Flags: gc.Pseudo},
 	obj.AFUNCDATA: {Flags: gc.Pseudo},
 	obj.APCDATA:   {Flags: gc.Pseudo},
 	obj.AUNDEF:    {Flags: gc.Break},
 	obj.AUSEFIELD: {Flags: gc.OK},
-	obj.ACHECKNIL: {Flags: gc.LeftRead},
 	obj.AVARDEF:   {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARKILL:  {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARLIVE:  {Flags: gc.Pseudo | gc.LeftRead},
@@ -42,26 +41,46 @@ var progtable = [arm64.ALAST & obj.AMask]obj.ProgInfo{
 	arm64.AHINT & obj.AMask: {Flags: gc.OK},
 
 	// Integer
-	arm64.AADD & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.ASUB & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.ANEG & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.AAND & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.AORR & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.AEOR & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.AMUL & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.ASMULL & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.AUMULL & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.ASMULH & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.AUMULH & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.ASDIV & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.AUDIV & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.ALSL & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.ALSR & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.AASR & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.ACMP & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead},
-	arm64.AADC & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite | gc.UseCarry},
-	arm64.AROR & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	arm64.AADDS & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite | gc.SetCarry},
+	arm64.AADD & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ASUB & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ANEG & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite}, // why RegRead? revisit once the old backend gone
+	arm64.AAND & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AORR & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AEOR & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ABIC & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AMVN & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite},
+	arm64.AMUL & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AMULW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ASMULL & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AUMULL & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ASMULH & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AUMULH & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ASDIV & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AUDIV & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ASDIVW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AUDIVW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AREM & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AUREM & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AREMW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AUREMW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ALSL & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ALSR & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AASR & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ACMP & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead},
+	arm64.ACMPW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead},
+	arm64.AADC & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite | gc.UseCarry},
+	arm64.AROR & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.ARORW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	arm64.AADDS & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite | gc.SetCarry},
+	arm64.ACSET & obj.AMask:   {Flags: gc.SizeQ | gc.RightWrite},
+	arm64.ACSEL & obj.AMask:   {Flags: gc.SizeQ | gc.RegRead | gc.RightWrite},
+	arm64.AREV & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite},
+	arm64.AREVW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite},
+	arm64.AREV16W & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite},
+	arm64.ARBIT & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite},
+	arm64.ARBITW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite},
+	arm64.ACLZ & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite},
+	arm64.ACLZW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite},
 
 	// Floating point.
 	arm64.AFADDD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
@@ -103,39 +122,50 @@ var progtable = [arm64.ALAST & obj.AMask]obj.ProgInfo{
 	arm64.AUCVTFWS & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
 
 	// Moves
-	arm64.AMOVB & obj.AMask:  {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	arm64.AMOVBU & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	arm64.AMOVH & obj.AMask:  {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	arm64.AMOVHU & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	arm64.AMOVW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	arm64.AMOVWU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	arm64.AMOVD & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
-	arm64.AFMOVS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	arm64.AFMOVD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.AMOVB & obj.AMask:   {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	arm64.AMOVBU & obj.AMask:  {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	arm64.AMOVH & obj.AMask:   {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	arm64.AMOVHU & obj.AMask:  {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	arm64.AMOVW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	arm64.AMOVWU & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	arm64.AMOVD & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.AFMOVS & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	arm64.AFMOVD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.ALDARW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.ALDAR & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.ALDAXRB & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.ALDAXRW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.ALDAXR & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.ASTLRW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.ASTLR & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.ASTLXRB & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.ASTLXRW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move},
+	arm64.ASTLXR & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
 
 	// Jumps
-	arm64.AB & obj.AMask:    {Flags: gc.Jump | gc.Break},
-	arm64.ABL & obj.AMask:   {Flags: gc.Call},
-	arm64.ABEQ & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ABNE & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ABGE & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ABLT & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ABGT & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ABLE & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ABLO & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ABLS & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ABHI & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ABHS & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ACBZ & obj.AMask:  {Flags: gc.Cjmp},
-	arm64.ACBNZ & obj.AMask: {Flags: gc.Cjmp},
-	obj.ARET:                {Flags: gc.Break},
-	obj.ADUFFZERO:           {Flags: gc.Call},
-	obj.ADUFFCOPY:           {Flags: gc.Call},
+	arm64.AB & obj.AMask:     {Flags: gc.Jump | gc.Break},
+	arm64.ABL & obj.AMask:    {Flags: gc.Call},
+	arm64.ABEQ & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ABNE & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ABGE & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ABLT & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ABGT & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ABLE & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ABLO & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ABLS & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ABHI & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ABHS & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ACBZ & obj.AMask:   {Flags: gc.Cjmp},
+	arm64.ACBNZ & obj.AMask:  {Flags: gc.Cjmp},
+	arm64.ACBZW & obj.AMask:  {Flags: gc.Cjmp},
+	arm64.ACBNZW & obj.AMask: {Flags: gc.Cjmp},
+	obj.ARET:                 {Flags: gc.Break},
+	obj.ADUFFZERO:            {Flags: gc.Call},
+	obj.ADUFFCOPY:            {Flags: gc.Call},
 }
 
-func proginfo(p *obj.Prog) {
-	info := &p.Info
-	*info = progtable[p.As&obj.AMask]
+func proginfo(p *obj.Prog) gc.ProgInfo {
+	info := progtable[p.As&obj.AMask]
 	if info.Flags == 0 {
 		gc.Fatalf("proginfo: unknown instruction %v", p)
 	}
@@ -145,34 +175,10 @@ func proginfo(p *obj.Prog) {
 		info.Flags |= gc.RightRead /*CanRegRead |*/
 	}
 
-	if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 {
-		info.Regindex |= RtoB(int(p.From.Reg))
-		if p.Scond != 0 {
-			info.Regset |= RtoB(int(p.From.Reg))
-		}
-	}
-
-	if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 {
-		info.Regindex |= RtoB(int(p.To.Reg))
-		if p.Scond != 0 {
-			info.Regset |= RtoB(int(p.To.Reg))
-		}
-	}
-
 	if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) {
 		info.Flags &^= gc.LeftRead
 		info.Flags |= gc.LeftAddr
 	}
 
-	if p.As == obj.ADUFFZERO {
-		info.Reguse |= RtoB(arm64.REGRT1)
-		info.Regset |= RtoB(arm64.REGRT1)
-	}
-
-	if p.As == obj.ADUFFCOPY {
-		// TODO(austin) Revisit when duffcopy is implemented
-		info.Reguse |= RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) | RtoB(arm64.REG_R5)
-
-		info.Regset |= RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2)
-	}
+	return info
 }
diff --git a/src/cmd/compile/internal/arm64/reg.go b/src/cmd/compile/internal/arm64/reg.go
deleted file mode 100644
index 6e24dc2..0000000
--- a/src/cmd/compile/internal/arm64/reg.go
+++ /dev/null
@@ -1,169 +0,0 @@
-// Derived from Inferno utils/6c/reg.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package arm64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj/arm64"
-)
-
-const (
-	NREGVAR = 64 /* 32 general + 32 floating */
-)
-
-var regname = []string{
-	".R0",
-	".R1",
-	".R2",
-	".R3",
-	".R4",
-	".R5",
-	".R6",
-	".R7",
-	".R8",
-	".R9",
-	".R10",
-	".R11",
-	".R12",
-	".R13",
-	".R14",
-	".R15",
-	".R16",
-	".R17",
-	".R18",
-	".R19",
-	".R20",
-	".R21",
-	".R22",
-	".R23",
-	".R24",
-	".R25",
-	".R26",
-	".R27",
-	".R28",
-	".R29",
-	".R30",
-	".R31",
-	".F0",
-	".F1",
-	".F2",
-	".F3",
-	".F4",
-	".F5",
-	".F6",
-	".F7",
-	".F8",
-	".F9",
-	".F10",
-	".F11",
-	".F12",
-	".F13",
-	".F14",
-	".F15",
-	".F16",
-	".F17",
-	".F18",
-	".F19",
-	".F20",
-	".F21",
-	".F22",
-	".F23",
-	".F24",
-	".F25",
-	".F26",
-	".F27",
-	".F28",
-	".F29",
-	".F30",
-	".F31",
-}
-
-func regnames(n *int) []string {
-	*n = NREGVAR
-	return regname
-}
-
-func excludedregs() uint64 {
-	// Exclude registers with fixed functions
-	regbits := RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) | RtoB(arm64.REGPR)
-
-	// Exclude R26 - R31.
-	for r := arm64.REGMAX + 1; r <= arm64.REGZERO; r++ {
-		regbits |= RtoB(r)
-	}
-
-	// Also exclude floating point registers with fixed constants
-	regbits |= RtoB(arm64.REG_F27) | RtoB(arm64.REG_F28) | RtoB(arm64.REG_F29) | RtoB(arm64.REG_F30) | RtoB(arm64.REG_F31)
-
-	return regbits
-}
-
-func doregbits(r int) uint64 {
-	return 0
-}
-
-/*
- * track register variables including external registers:
- *	bit	reg
- *	0	R0
- *	1	R1
- *	...	...
- *	31	R31
- *	32+0	F0
- *	32+1	F1
- *	...	...
- *	32+31	F31
- */
-func RtoB(r int) uint64 {
-	if r >= arm64.REG_R0 && r <= arm64.REG_R31 {
-		return 1 << uint(r-arm64.REG_R0)
-	}
-	if r >= arm64.REG_F0 && r <= arm64.REG_F31 {
-		return 1 << uint(32+r-arm64.REG_F0)
-	}
-	return 0
-}
-
-func BtoR(b uint64) int {
-	b &= 0xffffffff
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + arm64.REG_R0
-}
-
-func BtoF(b uint64) int {
-	b >>= 32
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + arm64.REG_F0
-}
diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go
new file mode 100644
index 0000000..5670ef8
--- /dev/null
+++ b/src/cmd/compile/internal/arm64/ssa.go
@@ -0,0 +1,844 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package arm64
+
+import (
+	"math"
+
+	"cmd/compile/internal/gc"
+	"cmd/compile/internal/ssa"
+	"cmd/internal/obj"
+	"cmd/internal/obj/arm64"
+)
+
+// loadByType returns the load instruction of the given type.
+func loadByType(t ssa.Type) obj.As {
+	if t.IsFloat() {
+		switch t.Size() {
+		case 4:
+			return arm64.AFMOVS
+		case 8:
+			return arm64.AFMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			if t.IsSigned() {
+				return arm64.AMOVB
+			} else {
+				return arm64.AMOVBU
+			}
+		case 2:
+			if t.IsSigned() {
+				return arm64.AMOVH
+			} else {
+				return arm64.AMOVHU
+			}
+		case 4:
+			if t.IsSigned() {
+				return arm64.AMOVW
+			} else {
+				return arm64.AMOVWU
+			}
+		case 8:
+			return arm64.AMOVD
+		}
+	}
+	panic("bad load type")
+}
+
+// storeByType returns the store instruction of the given type.
+func storeByType(t ssa.Type) obj.As {
+	if t.IsFloat() {
+		switch t.Size() {
+		case 4:
+			return arm64.AFMOVS
+		case 8:
+			return arm64.AFMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			return arm64.AMOVB
+		case 2:
+			return arm64.AMOVH
+		case 4:
+			return arm64.AMOVW
+		case 8:
+			return arm64.AMOVD
+		}
+	}
+	panic("bad store type")
+}
+
+// makeshift encodes a register shifted by a constant, used as an Offset in Prog
+func makeshift(reg int16, typ int64, s int64) int64 {
+	return int64(reg&31)<<16 | typ | (s&63)<<10
+}
+
+// genshift generates a Prog for r = r0 op (r1 shifted by s)
+func genshift(as obj.As, r0, r1, r int16, typ int64, s int64) *obj.Prog {
+	p := gc.Prog(as)
+	p.From.Type = obj.TYPE_SHIFT
+	p.From.Offset = makeshift(r1, typ, s)
+	p.Reg = r0
+	if r != 0 {
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	}
+	return p
+}
+
+func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+	s.SetLineno(v.Line)
+	switch v.Op {
+	case ssa.OpInitMem:
+		// memory arg needs no code
+	case ssa.OpArg:
+		// input args need no code
+	case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
+		// nothing to do
+	case ssa.OpCopy, ssa.OpARM64MOVDconvert, ssa.OpARM64MOVDreg:
+		if v.Type.IsMemory() {
+			return
+		}
+		x := v.Args[0].Reg()
+		y := v.Reg()
+		if x == y {
+			return
+		}
+		as := arm64.AMOVD
+		if v.Type.IsFloat() {
+			switch v.Type.Size() {
+			case 4:
+				as = arm64.AFMOVS
+			case 8:
+				as = arm64.AFMOVD
+			default:
+				panic("bad float size")
+			}
+		}
+		p := gc.Prog(as)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = y
+	case ssa.OpARM64MOVDnop:
+		if v.Reg() != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		// nothing to do
+	case ssa.OpLoadReg:
+		if v.Type.IsFlags() {
+			v.Fatalf("load flags not implemented: %v", v.LongString())
+			return
+		}
+		p := gc.Prog(loadByType(v.Type))
+		gc.AddrAuto(&p.From, v.Args[0])
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpPhi:
+		gc.CheckLoweredPhi(v)
+	case ssa.OpStoreReg:
+		if v.Type.IsFlags() {
+			v.Fatalf("store flags not implemented: %v", v.LongString())
+			return
+		}
+		p := gc.Prog(storeByType(v.Type))
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddrAuto(&p.To, v)
+	case ssa.OpARM64ADD,
+		ssa.OpARM64SUB,
+		ssa.OpARM64AND,
+		ssa.OpARM64OR,
+		ssa.OpARM64XOR,
+		ssa.OpARM64BIC,
+		ssa.OpARM64MUL,
+		ssa.OpARM64MULW,
+		ssa.OpARM64MULH,
+		ssa.OpARM64UMULH,
+		ssa.OpARM64MULL,
+		ssa.OpARM64UMULL,
+		ssa.OpARM64DIV,
+		ssa.OpARM64UDIV,
+		ssa.OpARM64DIVW,
+		ssa.OpARM64UDIVW,
+		ssa.OpARM64MOD,
+		ssa.OpARM64UMOD,
+		ssa.OpARM64MODW,
+		ssa.OpARM64UMODW,
+		ssa.OpARM64SLL,
+		ssa.OpARM64SRL,
+		ssa.OpARM64SRA,
+		ssa.OpARM64FADDS,
+		ssa.OpARM64FADDD,
+		ssa.OpARM64FSUBS,
+		ssa.OpARM64FSUBD,
+		ssa.OpARM64FMULS,
+		ssa.OpARM64FMULD,
+		ssa.OpARM64FDIVS,
+		ssa.OpARM64FDIVD:
+		r := v.Reg()
+		r1 := v.Args[0].Reg()
+		r2 := v.Args[1].Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r2
+		p.Reg = r1
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.OpARM64ADDconst,
+		ssa.OpARM64SUBconst,
+		ssa.OpARM64ANDconst,
+		ssa.OpARM64ORconst,
+		ssa.OpARM64XORconst,
+		ssa.OpARM64BICconst,
+		ssa.OpARM64SLLconst,
+		ssa.OpARM64SRLconst,
+		ssa.OpARM64SRAconst,
+		ssa.OpARM64RORconst,
+		ssa.OpARM64RORWconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpARM64ADDshiftLL,
+		ssa.OpARM64SUBshiftLL,
+		ssa.OpARM64ANDshiftLL,
+		ssa.OpARM64ORshiftLL,
+		ssa.OpARM64XORshiftLL,
+		ssa.OpARM64BICshiftLL:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt)
+	case ssa.OpARM64ADDshiftRL,
+		ssa.OpARM64SUBshiftRL,
+		ssa.OpARM64ANDshiftRL,
+		ssa.OpARM64ORshiftRL,
+		ssa.OpARM64XORshiftRL,
+		ssa.OpARM64BICshiftRL:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt)
+	case ssa.OpARM64ADDshiftRA,
+		ssa.OpARM64SUBshiftRA,
+		ssa.OpARM64ANDshiftRA,
+		ssa.OpARM64ORshiftRA,
+		ssa.OpARM64XORshiftRA,
+		ssa.OpARM64BICshiftRA:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt)
+	case ssa.OpARM64MOVDconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpARM64FMOVSconst,
+		ssa.OpARM64FMOVDconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_FCONST
+		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpARM64CMP,
+		ssa.OpARM64CMPW,
+		ssa.OpARM64CMN,
+		ssa.OpARM64CMNW,
+		ssa.OpARM64FCMPS,
+		ssa.OpARM64FCMPD:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.Reg = v.Args[0].Reg()
+	case ssa.OpARM64CMPconst,
+		ssa.OpARM64CMPWconst,
+		ssa.OpARM64CMNconst,
+		ssa.OpARM64CMNWconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.Reg = v.Args[0].Reg()
+	case ssa.OpARM64CMPshiftLL:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LL, v.AuxInt)
+	case ssa.OpARM64CMPshiftRL:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt)
+	case ssa.OpARM64CMPshiftRA:
+		genshift(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt)
+	case ssa.OpARM64MOVDaddr:
+		p := gc.Prog(arm64.AMOVD)
+		p.From.Type = obj.TYPE_ADDR
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+		var wantreg string
+		// MOVD $sym+off(base), R
+		// the assembler expands it as the following:
+		// - base is SP: add constant offset to SP (R13)
+		//               when constant is large, tmp register (R11) may be used
+		// - base is SB: load external address from constant pool (use relocation)
+		switch v.Aux.(type) {
+		default:
+			v.Fatalf("aux is of unknown type %T", v.Aux)
+		case *ssa.ExternSymbol:
+			wantreg = "SB"
+			gc.AddAux(&p.From, v)
+		case *ssa.ArgSymbol, *ssa.AutoSymbol:
+			wantreg = "SP"
+			gc.AddAux(&p.From, v)
+		case nil:
+			// No sym, just MOVD $off(SP), R
+			wantreg = "SP"
+			p.From.Reg = arm64.REGSP
+			p.From.Offset = v.AuxInt
+		}
+		if reg := v.Args[0].RegName(); reg != wantreg {
+			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
+		}
+	case ssa.OpARM64MOVBload,
+		ssa.OpARM64MOVBUload,
+		ssa.OpARM64MOVHload,
+		ssa.OpARM64MOVHUload,
+		ssa.OpARM64MOVWload,
+		ssa.OpARM64MOVWUload,
+		ssa.OpARM64MOVDload,
+		ssa.OpARM64FMOVSload,
+		ssa.OpARM64FMOVDload:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpARM64LDAR,
+		ssa.OpARM64LDARW:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg0()
+	case ssa.OpARM64MOVBstore,
+		ssa.OpARM64MOVHstore,
+		ssa.OpARM64MOVWstore,
+		ssa.OpARM64MOVDstore,
+		ssa.OpARM64FMOVSstore,
+		ssa.OpARM64FMOVDstore,
+		ssa.OpARM64STLR,
+		ssa.OpARM64STLRW:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpARM64MOVBstorezero,
+		ssa.OpARM64MOVHstorezero,
+		ssa.OpARM64MOVWstorezero,
+		ssa.OpARM64MOVDstorezero:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = arm64.REGZERO
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpARM64LoweredAtomicExchange64,
+		ssa.OpARM64LoweredAtomicExchange32:
+		// LDAXR	(Rarg0), Rout
+		// STLXR	Rarg1, (Rarg0), Rtmp
+		// CBNZ		Rtmp, -2(PC)
+		ld := arm64.ALDAXR
+		st := arm64.ASTLXR
+		if v.Op == ssa.OpARM64LoweredAtomicExchange32 {
+			ld = arm64.ALDAXRW
+			st = arm64.ASTLXRW
+		}
+		r0 := v.Args[0].Reg()
+		r1 := v.Args[1].Reg()
+		out := v.Reg0()
+		p := gc.Prog(ld)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = r0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = out
+		p1 := gc.Prog(st)
+		p1.From.Type = obj.TYPE_REG
+		p1.From.Reg = r1
+		p1.To.Type = obj.TYPE_MEM
+		p1.To.Reg = r0
+		p1.RegTo2 = arm64.REGTMP
+		p2 := gc.Prog(arm64.ACBNZ)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = arm64.REGTMP
+		p2.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p2, p)
+	case ssa.OpARM64LoweredAtomicAdd64,
+		ssa.OpARM64LoweredAtomicAdd32:
+		// LDAXR	(Rarg0), Rout
+		// ADD		Rarg1, Rout
+		// STLXR	Rout, (Rarg0), Rtmp
+		// CBNZ		Rtmp, -3(PC)
+		ld := arm64.ALDAXR
+		st := arm64.ASTLXR
+		if v.Op == ssa.OpARM64LoweredAtomicAdd32 {
+			ld = arm64.ALDAXRW
+			st = arm64.ASTLXRW
+		}
+		r0 := v.Args[0].Reg()
+		r1 := v.Args[1].Reg()
+		out := v.Reg0()
+		p := gc.Prog(ld)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = r0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = out
+		p1 := gc.Prog(arm64.AADD)
+		p1.From.Type = obj.TYPE_REG
+		p1.From.Reg = r1
+		p1.To.Type = obj.TYPE_REG
+		p1.To.Reg = out
+		p2 := gc.Prog(st)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = out
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = r0
+		p2.RegTo2 = arm64.REGTMP
+		p3 := gc.Prog(arm64.ACBNZ)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = arm64.REGTMP
+		p3.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p3, p)
+	case ssa.OpARM64LoweredAtomicCas64,
+		ssa.OpARM64LoweredAtomicCas32:
+		// LDAXR	(Rarg0), Rtmp
+		// CMP		Rarg1, Rtmp
+		// BNE		3(PC)
+		// STLXR	Rarg2, (Rarg0), Rtmp
+		// CBNZ		Rtmp, -4(PC)
+		// CSET		EQ, Rout
+		ld := arm64.ALDAXR
+		st := arm64.ASTLXR
+		cmp := arm64.ACMP
+		if v.Op == ssa.OpARM64LoweredAtomicCas32 {
+			ld = arm64.ALDAXRW
+			st = arm64.ASTLXRW
+			cmp = arm64.ACMPW
+		}
+		r0 := v.Args[0].Reg()
+		r1 := v.Args[1].Reg()
+		r2 := v.Args[2].Reg()
+		out := v.Reg0()
+		p := gc.Prog(ld)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = r0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = arm64.REGTMP
+		p1 := gc.Prog(cmp)
+		p1.From.Type = obj.TYPE_REG
+		p1.From.Reg = r1
+		p1.Reg = arm64.REGTMP
+		p2 := gc.Prog(arm64.ABNE)
+		p2.To.Type = obj.TYPE_BRANCH
+		p3 := gc.Prog(st)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = r2
+		p3.To.Type = obj.TYPE_MEM
+		p3.To.Reg = r0
+		p3.RegTo2 = arm64.REGTMP
+		p4 := gc.Prog(arm64.ACBNZ)
+		p4.From.Type = obj.TYPE_REG
+		p4.From.Reg = arm64.REGTMP
+		p4.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p4, p)
+		p5 := gc.Prog(arm64.ACSET)
+		p5.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg
+		p5.From.Reg = arm64.COND_EQ
+		p5.To.Type = obj.TYPE_REG
+		p5.To.Reg = out
+		gc.Patch(p2, p5)
+	case ssa.OpARM64LoweredAtomicAnd8,
+		ssa.OpARM64LoweredAtomicOr8:
+		// LDAXRB	(Rarg0), Rtmp
+		// AND/OR	Rarg1, Rtmp
+		// STLXRB	Rtmp, (Rarg0), Rtmp
+		// CBNZ		Rtmp, -3(PC)
+		r0 := v.Args[0].Reg()
+		r1 := v.Args[1].Reg()
+		p := gc.Prog(arm64.ALDAXRB)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = r0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = arm64.REGTMP
+		p1 := gc.Prog(v.Op.Asm())
+		p1.From.Type = obj.TYPE_REG
+		p1.From.Reg = r1
+		p1.To.Type = obj.TYPE_REG
+		p1.To.Reg = arm64.REGTMP
+		p2 := gc.Prog(arm64.ASTLXRB)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = arm64.REGTMP
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = r0
+		p2.RegTo2 = arm64.REGTMP
+		p3 := gc.Prog(arm64.ACBNZ)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = arm64.REGTMP
+		p3.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p3, p)
+	case ssa.OpARM64MOVBreg,
+		ssa.OpARM64MOVBUreg,
+		ssa.OpARM64MOVHreg,
+		ssa.OpARM64MOVHUreg,
+		ssa.OpARM64MOVWreg,
+		ssa.OpARM64MOVWUreg:
+		a := v.Args[0]
+		for a.Op == ssa.OpCopy || a.Op == ssa.OpARM64MOVDreg {
+			a = a.Args[0]
+		}
+		if a.Op == ssa.OpLoadReg {
+			t := a.Type
+			switch {
+			case v.Op == ssa.OpARM64MOVBreg && t.Size() == 1 && t.IsSigned(),
+				v.Op == ssa.OpARM64MOVBUreg && t.Size() == 1 && !t.IsSigned(),
+				v.Op == ssa.OpARM64MOVHreg && t.Size() == 2 && t.IsSigned(),
+				v.Op == ssa.OpARM64MOVHUreg && t.Size() == 2 && !t.IsSigned(),
+				v.Op == ssa.OpARM64MOVWreg && t.Size() == 4 && t.IsSigned(),
+				v.Op == ssa.OpARM64MOVWUreg && t.Size() == 4 && !t.IsSigned():
+				// arg is a proper-typed load, already zero/sign-extended, don't extend again
+				if v.Reg() == v.Args[0].Reg() {
+					return
+				}
+				p := gc.Prog(arm64.AMOVD)
+				p.From.Type = obj.TYPE_REG
+				p.From.Reg = v.Args[0].Reg()
+				p.To.Type = obj.TYPE_REG
+				p.To.Reg = v.Reg()
+				return
+			default:
+			}
+		}
+		fallthrough
+	case ssa.OpARM64MVN,
+		ssa.OpARM64NEG,
+		ssa.OpARM64FNEGS,
+		ssa.OpARM64FNEGD,
+		ssa.OpARM64FSQRTD,
+		ssa.OpARM64FCVTZSSW,
+		ssa.OpARM64FCVTZSDW,
+		ssa.OpARM64FCVTZUSW,
+		ssa.OpARM64FCVTZUDW,
+		ssa.OpARM64FCVTZSS,
+		ssa.OpARM64FCVTZSD,
+		ssa.OpARM64FCVTZUS,
+		ssa.OpARM64FCVTZUD,
+		ssa.OpARM64SCVTFWS,
+		ssa.OpARM64SCVTFWD,
+		ssa.OpARM64SCVTFS,
+		ssa.OpARM64SCVTFD,
+		ssa.OpARM64UCVTFWS,
+		ssa.OpARM64UCVTFWD,
+		ssa.OpARM64UCVTFS,
+		ssa.OpARM64UCVTFD,
+		ssa.OpARM64FCVTSD,
+		ssa.OpARM64FCVTDS,
+		ssa.OpARM64REV,
+		ssa.OpARM64REVW,
+		ssa.OpARM64REV16W,
+		ssa.OpARM64RBIT,
+		ssa.OpARM64RBITW,
+		ssa.OpARM64CLZ,
+		ssa.OpARM64CLZW:
+		p := gc.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.OpARM64CSELULT,
+		ssa.OpARM64CSELULT0:
+		r1 := int16(arm64.REGZERO)
+		if v.Op == ssa.OpARM64CSELULT {
+			r1 = v.Args[1].Reg()
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg
+		p.From.Reg = arm64.COND_LO
+		p.Reg = v.Args[0].Reg()
+		p.From3 = &obj.Addr{Type: obj.TYPE_REG, Reg: r1}
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpARM64DUFFZERO:
+		// runtime.duffzero expects start address - 8 in R16
+		p := gc.Prog(arm64.ASUB)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = 8
+		p.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = arm64.REG_R16
+		p = gc.Prog(obj.ADUFFZERO)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
+		p.To.Offset = v.AuxInt
+	case ssa.OpARM64LoweredZero:
+		// MOVD.P	ZR, 8(R16)
+		// CMP	Rarg1, R16
+		// BLE	-2(PC)
+		// arg1 is the address of the last element to zero
+		p := gc.Prog(arm64.AMOVD)
+		p.Scond = arm64.C_XPOST
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = arm64.REGZERO
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = arm64.REG_R16
+		p.To.Offset = 8
+		p2 := gc.Prog(arm64.ACMP)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = v.Args[1].Reg()
+		p2.Reg = arm64.REG_R16
+		p3 := gc.Prog(arm64.ABLE)
+		p3.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p3, p)
+	case ssa.OpARM64DUFFCOPY:
+		p := gc.Prog(obj.ADUFFCOPY)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg))
+		p.To.Offset = v.AuxInt
+	case ssa.OpARM64LoweredMove:
+		// MOVD.P	8(R16), Rtmp
+		// MOVD.P	Rtmp, 8(R17)
+		// CMP	Rarg2, R16
+		// BLE	-3(PC)
+		// arg2 is the address of the last element of src
+		p := gc.Prog(arm64.AMOVD)
+		p.Scond = arm64.C_XPOST
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = arm64.REG_R16
+		p.From.Offset = 8
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = arm64.REGTMP
+		p2 := gc.Prog(arm64.AMOVD)
+		p2.Scond = arm64.C_XPOST
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = arm64.REGTMP
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = arm64.REG_R17
+		p2.To.Offset = 8
+		p3 := gc.Prog(arm64.ACMP)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = v.Args[2].Reg()
+		p3.Reg = arm64.REG_R16
+		p4 := gc.Prog(arm64.ABLE)
+		p4.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p4, p)
+	case ssa.OpARM64CALLstatic:
+		if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
+			// Deferred calls will appear to be returning to
+			// the CALL deferreturn(SB) that we are about to emit.
+			// However, the stack trace code will show the line
+			// of the instruction byte before the return PC.
+			// To avoid that being an unrelated instruction,
+			// insert an actual hardware NOP that will have the right line number.
+			// This is different from obj.ANOP, which is a virtual no-op
+			// that doesn't make it into the instruction stream.
+			ginsnop()
+		}
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym))
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpARM64CALLclosure:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Offset = 0
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpARM64CALLdefer:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Deferproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpARM64CALLgo:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Newproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpARM64CALLinter:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Offset = 0
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpARM64LoweredNilCheck:
+		// Issue a load which will fault if arg is nil.
+		p := gc.Prog(arm64.AMOVB)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = arm64.REGTMP
+		if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
+			gc.Warnl(v.Line, "generated nil check")
+		}
+	case ssa.OpVarDef:
+		gc.Gvardef(v.Aux.(*gc.Node))
+	case ssa.OpVarKill:
+		gc.Gvarkill(v.Aux.(*gc.Node))
+	case ssa.OpVarLive:
+		gc.Gvarlive(v.Aux.(*gc.Node))
+	case ssa.OpKeepAlive:
+		gc.KeepAlive(v)
+	case ssa.OpARM64Equal,
+		ssa.OpARM64NotEqual,
+		ssa.OpARM64LessThan,
+		ssa.OpARM64LessEqual,
+		ssa.OpARM64GreaterThan,
+		ssa.OpARM64GreaterEqual,
+		ssa.OpARM64LessThanU,
+		ssa.OpARM64LessEqualU,
+		ssa.OpARM64GreaterThanU,
+		ssa.OpARM64GreaterEqualU:
+		// generate boolean values using CSET
+		p := gc.Prog(arm64.ACSET)
+		p.From.Type = obj.TYPE_REG // assembler encodes conditional bits in Reg
+		p.From.Reg = condBits[v.Op]
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpSelect0, ssa.OpSelect1:
+		// nothing to do
+	case ssa.OpARM64LoweredGetClosurePtr:
+		// Closure pointer is R26 (arm64.REGCTXT).
+		gc.CheckLoweredGetClosurePtr(v)
+	case ssa.OpARM64FlagEQ,
+		ssa.OpARM64FlagLT_ULT,
+		ssa.OpARM64FlagLT_UGT,
+		ssa.OpARM64FlagGT_ULT,
+		ssa.OpARM64FlagGT_UGT:
+		v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
+	case ssa.OpARM64InvertFlags:
+		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
+	default:
+		v.Fatalf("genValue not implemented: %s", v.LongString())
+	}
+}
+
+var condBits = map[ssa.Op]int16{
+	ssa.OpARM64Equal:         arm64.COND_EQ,
+	ssa.OpARM64NotEqual:      arm64.COND_NE,
+	ssa.OpARM64LessThan:      arm64.COND_LT,
+	ssa.OpARM64LessThanU:     arm64.COND_LO,
+	ssa.OpARM64LessEqual:     arm64.COND_LE,
+	ssa.OpARM64LessEqualU:    arm64.COND_LS,
+	ssa.OpARM64GreaterThan:   arm64.COND_GT,
+	ssa.OpARM64GreaterThanU:  arm64.COND_HI,
+	ssa.OpARM64GreaterEqual:  arm64.COND_GE,
+	ssa.OpARM64GreaterEqualU: arm64.COND_HS,
+}
+
+var blockJump = map[ssa.BlockKind]struct {
+	asm, invasm obj.As
+}{
+	ssa.BlockARM64EQ:  {arm64.ABEQ, arm64.ABNE},
+	ssa.BlockARM64NE:  {arm64.ABNE, arm64.ABEQ},
+	ssa.BlockARM64LT:  {arm64.ABLT, arm64.ABGE},
+	ssa.BlockARM64GE:  {arm64.ABGE, arm64.ABLT},
+	ssa.BlockARM64LE:  {arm64.ABLE, arm64.ABGT},
+	ssa.BlockARM64GT:  {arm64.ABGT, arm64.ABLE},
+	ssa.BlockARM64ULT: {arm64.ABLO, arm64.ABHS},
+	ssa.BlockARM64UGE: {arm64.ABHS, arm64.ABLO},
+	ssa.BlockARM64UGT: {arm64.ABHI, arm64.ABLS},
+	ssa.BlockARM64ULE: {arm64.ABLS, arm64.ABHI},
+	ssa.BlockARM64Z:   {arm64.ACBZ, arm64.ACBNZ},
+	ssa.BlockARM64NZ:  {arm64.ACBNZ, arm64.ACBZ},
+	ssa.BlockARM64ZW:  {arm64.ACBZW, arm64.ACBNZW},
+	ssa.BlockARM64NZW: {arm64.ACBNZW, arm64.ACBZW},
+}
+
+func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+	s.SetLineno(b.Line)
+
+	switch b.Kind {
+	case ssa.BlockPlain:
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+
+	case ssa.BlockDefer:
+		// defer returns in R0:
+		// 0 if we should continue executing
+		// 1 if we should jump to deferreturn call
+		p := gc.Prog(arm64.ACMP)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = 0
+		p.Reg = arm64.REG_R0
+		p = gc.Prog(arm64.ABNE)
+		p.To.Type = obj.TYPE_BRANCH
+		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+
+	case ssa.BlockExit:
+		gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
+
+	case ssa.BlockRet:
+		gc.Prog(obj.ARET)
+
+	case ssa.BlockRetJmp:
+		p := gc.Prog(obj.ARET)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
+
+	case ssa.BlockARM64EQ, ssa.BlockARM64NE,
+		ssa.BlockARM64LT, ssa.BlockARM64GE,
+		ssa.BlockARM64LE, ssa.BlockARM64GT,
+		ssa.BlockARM64ULT, ssa.BlockARM64UGT,
+		ssa.BlockARM64ULE, ssa.BlockARM64UGE,
+		ssa.BlockARM64Z, ssa.BlockARM64NZ,
+		ssa.BlockARM64ZW, ssa.BlockARM64NZW:
+		jmp := blockJump[b.Kind]
+		var p *obj.Prog
+		switch next {
+		case b.Succs[0].Block():
+			p = gc.Prog(jmp.invasm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		case b.Succs[1].Block():
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		default:
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+			q := gc.Prog(obj.AJMP)
+			q.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
+		}
+		if !b.Control.Type.IsFlags() {
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = b.Control.Reg()
+		}
+
+	default:
+		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
+	}
+}
diff --git a/src/cmd/compile/internal/big/accuracy_string.go b/src/cmd/compile/internal/big/accuracy_string.go
deleted file mode 100644
index 24ef7f1..0000000
--- a/src/cmd/compile/internal/big/accuracy_string.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// generated by stringer -type=Accuracy; DO NOT EDIT
-
-package big
-
-import "fmt"
-
-const _Accuracy_name = "BelowExactAbove"
-
-var _Accuracy_index = [...]uint8{0, 5, 10, 15}
-
-func (i Accuracy) String() string {
-	i -= -1
-	if i < 0 || i+1 >= Accuracy(len(_Accuracy_index)) {
-		return fmt.Sprintf("Accuracy(%d)", i+-1)
-	}
-	return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]]
-}
diff --git a/src/cmd/compile/internal/big/arith.go b/src/cmd/compile/internal/big/arith.go
deleted file mode 100644
index d7ea838..0000000
--- a/src/cmd/compile/internal/big/arith.go
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file provides Go implementations of elementary multi-precision
-// arithmetic operations on word vectors. Needed for platforms without
-// assembly implementations of these routines.
-
-package big
-
-// A Word represents a single digit of a multi-precision unsigned integer.
-type Word uintptr
-
-const (
-	// Compute the size _S of a Word in bytes.
-	_m    = ^Word(0)
-	_logS = _m>>8&1 + _m>>16&1 + _m>>32&1
-	_S    = 1 << _logS
-
-	_W = _S << 3 // word size in bits
-	_B = 1 << _W // digit base
-	_M = _B - 1  // digit mask
-
-	_W2 = _W / 2   // half word size in bits
-	_B2 = 1 << _W2 // half digit base
-	_M2 = _B2 - 1  // half digit mask
-)
-
-// ----------------------------------------------------------------------------
-// Elementary operations on words
-//
-// These operations are used by the vector operations below.
-
-// z1<<_W + z0 = x+y+c, with c == 0 or 1
-func addWW_g(x, y, c Word) (z1, z0 Word) {
-	yc := y + c
-	z0 = x + yc
-	if z0 < x || yc < y {
-		z1 = 1
-	}
-	return
-}
-
-// z1<<_W + z0 = x-y-c, with c == 0 or 1
-func subWW_g(x, y, c Word) (z1, z0 Word) {
-	yc := y + c
-	z0 = x - yc
-	if z0 > x || yc < y {
-		z1 = 1
-	}
-	return
-}
-
-// z1<<_W + z0 = x*y
-// Adapted from Warren, Hacker's Delight, p. 132.
-func mulWW_g(x, y Word) (z1, z0 Word) {
-	x0 := x & _M2
-	x1 := x >> _W2
-	y0 := y & _M2
-	y1 := y >> _W2
-	w0 := x0 * y0
-	t := x1*y0 + w0>>_W2
-	w1 := t & _M2
-	w2 := t >> _W2
-	w1 += x0 * y1
-	z1 = x1*y1 + w2 + w1>>_W2
-	z0 = x * y
-	return
-}
-
-// z1<<_W + z0 = x*y + c
-func mulAddWWW_g(x, y, c Word) (z1, z0 Word) {
-	z1, zz0 := mulWW_g(x, y)
-	if z0 = zz0 + c; z0 < zz0 {
-		z1++
-	}
-	return
-}
-
-// Length of x in bits.
-func bitLen_g(x Word) (n int) {
-	for ; x >= 0x8000; x >>= 16 {
-		n += 16
-	}
-	if x >= 0x80 {
-		x >>= 8
-		n += 8
-	}
-	if x >= 0x8 {
-		x >>= 4
-		n += 4
-	}
-	if x >= 0x2 {
-		x >>= 2
-		n += 2
-	}
-	if x >= 0x1 {
-		n++
-	}
-	return
-}
-
-// log2 computes the integer binary logarithm of x.
-// The result is the integer n for which 2^n <= x < 2^(n+1).
-// If x == 0, the result is -1.
-func log2(x Word) int {
-	return bitLen(x) - 1
-}
-
-// nlz returns the number of leading zeros in x.
-func nlz(x Word) uint {
-	return uint(_W - bitLen(x))
-}
-
-// nlz64 returns the number of leading zeros in x.
-func nlz64(x uint64) uint {
-	switch _W {
-	case 32:
-		w := x >> 32
-		if w == 0 {
-			return 32 + nlz(Word(x))
-		}
-		return nlz(Word(w))
-	case 64:
-		return nlz(Word(x))
-	}
-	panic("unreachable")
-}
-
-// q = (u1<<_W + u0 - r)/y
-// Adapted from Warren, Hacker's Delight, p. 152.
-func divWW_g(u1, u0, v Word) (q, r Word) {
-	if u1 >= v {
-		return 1<<_W - 1, 1<<_W - 1
-	}
-
-	s := nlz(v)
-	v <<= s
-
-	vn1 := v >> _W2
-	vn0 := v & _M2
-	un32 := u1<<s | u0>>(_W-s)
-	un10 := u0 << s
-	un1 := un10 >> _W2
-	un0 := un10 & _M2
-	q1 := un32 / vn1
-	rhat := un32 - q1*vn1
-
-	for q1 >= _B2 || q1*vn0 > _B2*rhat+un1 {
-		q1--
-		rhat += vn1
-		if rhat >= _B2 {
-			break
-		}
-	}
-
-	un21 := un32*_B2 + un1 - q1*v
-	q0 := un21 / vn1
-	rhat = un21 - q0*vn1
-
-	for q0 >= _B2 || q0*vn0 > _B2*rhat+un0 {
-		q0--
-		rhat += vn1
-		if rhat >= _B2 {
-			break
-		}
-	}
-
-	return q1*_B2 + q0, (un21*_B2 + un0 - q0*v) >> s
-}
-
-// Keep for performance debugging.
-// Using addWW_g is likely slower.
-const use_addWW_g = false
-
-// The resulting carry c is either 0 or 1.
-func addVV_g(z, x, y []Word) (c Word) {
-	if use_addWW_g {
-		for i := range z {
-			c, z[i] = addWW_g(x[i], y[i], c)
-		}
-		return
-	}
-
-	for i, xi := range x[:len(z)] {
-		yi := y[i]
-		zi := xi + yi + c
-		z[i] = zi
-		// see "Hacker's Delight", section 2-12 (overflow detection)
-		c = (xi&yi | (xi|yi)&^zi) >> (_W - 1)
-	}
-	return
-}
-
-// The resulting carry c is either 0 or 1.
-func subVV_g(z, x, y []Word) (c Word) {
-	if use_addWW_g {
-		for i := range z {
-			c, z[i] = subWW_g(x[i], y[i], c)
-		}
-		return
-	}
-
-	for i, xi := range x[:len(z)] {
-		yi := y[i]
-		zi := xi - yi - c
-		z[i] = zi
-		// see "Hacker's Delight", section 2-12 (overflow detection)
-		c = (yi&^xi | (yi|^xi)&zi) >> (_W - 1)
-	}
-	return
-}
-
-// The resulting carry c is either 0 or 1.
-func addVW_g(z, x []Word, y Word) (c Word) {
-	if use_addWW_g {
-		c = y
-		for i := range z {
-			c, z[i] = addWW_g(x[i], c, 0)
-		}
-		return
-	}
-
-	c = y
-	for i, xi := range x[:len(z)] {
-		zi := xi + c
-		z[i] = zi
-		c = xi &^ zi >> (_W - 1)
-	}
-	return
-}
-
-func subVW_g(z, x []Word, y Word) (c Word) {
-	if use_addWW_g {
-		c = y
-		for i := range z {
-			c, z[i] = subWW_g(x[i], c, 0)
-		}
-		return
-	}
-
-	c = y
-	for i, xi := range x[:len(z)] {
-		zi := xi - c
-		z[i] = zi
-		c = (zi &^ xi) >> (_W - 1)
-	}
-	return
-}
-
-func shlVU_g(z, x []Word, s uint) (c Word) {
-	if n := len(z); n > 0 {
-		ŝ := _W - s
-		w1 := x[n-1]
-		c = w1 >> ŝ
-		for i := n - 1; i > 0; i-- {
-			w := w1
-			w1 = x[i-1]
-			z[i] = w<<s | w1>>ŝ
-		}
-		z[0] = w1 << s
-	}
-	return
-}
-
-func shrVU_g(z, x []Word, s uint) (c Word) {
-	if n := len(z); n > 0 {
-		ŝ := _W - s
-		w1 := x[0]
-		c = w1 << ŝ
-		for i := 0; i < n-1; i++ {
-			w := w1
-			w1 = x[i+1]
-			z[i] = w>>s | w1<<ŝ
-		}
-		z[n-1] = w1 >> s
-	}
-	return
-}
-
-func mulAddVWW_g(z, x []Word, y, r Word) (c Word) {
-	c = r
-	for i := range z {
-		c, z[i] = mulAddWWW_g(x[i], y, c)
-	}
-	return
-}
-
-// TODO(gri) Remove use of addWW_g here and then we can remove addWW_g and subWW_g.
-func addMulVVW_g(z, x []Word, y Word) (c Word) {
-	for i := range z {
-		z1, z0 := mulAddWWW_g(x[i], y, z[i])
-		c, z[i] = addWW_g(z0, c, 0)
-		c += z1
-	}
-	return
-}
-
-func divWVW_g(z []Word, xn Word, x []Word, y Word) (r Word) {
-	r = xn
-	for i := len(z) - 1; i >= 0; i-- {
-		z[i], r = divWW_g(r, x[i], y)
-	}
-	return
-}
diff --git a/src/cmd/compile/internal/big/arith_decl.go b/src/cmd/compile/internal/big/arith_decl.go
deleted file mode 100644
index d60b7f9..0000000
--- a/src/cmd/compile/internal/big/arith_decl.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-func mulWW(x, y Word) (z1, z0 Word) {
-	return mulWW_g(x, y)
-}
-
-func divWW(x1, x0, y Word) (q, r Word) {
-	return divWW_g(x1, x0, y)
-}
-
-func addVV(z, x, y []Word) (c Word) {
-	return addVV_g(z, x, y)
-}
-
-func subVV(z, x, y []Word) (c Word) {
-	return subVV_g(z, x, y)
-}
-
-func addVW(z, x []Word, y Word) (c Word) {
-	return addVW_g(z, x, y)
-}
-
-func subVW(z, x []Word, y Word) (c Word) {
-	return subVW_g(z, x, y)
-}
-
-func shlVU(z, x []Word, s uint) (c Word) {
-	return shlVU_g(z, x, s)
-}
-
-func shrVU(z, x []Word, s uint) (c Word) {
-	return shrVU_g(z, x, s)
-}
-
-func mulAddVWW(z, x []Word, y, r Word) (c Word) {
-	return mulAddVWW_g(z, x, y, r)
-}
-
-func addMulVVW(z, x []Word, y Word) (c Word) {
-	return addMulVVW_g(z, x, y)
-}
-
-func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) {
-	return divWVW_g(z, xn, x, y)
-}
-
-func bitLen(x Word) (n int) {
-	return bitLen_g(x)
-}
diff --git a/src/cmd/compile/internal/big/arith_test.go b/src/cmd/compile/internal/big/arith_test.go
deleted file mode 100644
index 7d2f69a..0000000
--- a/src/cmd/compile/internal/big/arith_test.go
+++ /dev/null
@@ -1,442 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"math/rand"
-	"testing"
-)
-
-type funWW func(x, y, c Word) (z1, z0 Word)
-type argWW struct {
-	x, y, c, z1, z0 Word
-}
-
-var sumWW = []argWW{
-	{0, 0, 0, 0, 0},
-	{0, 1, 0, 0, 1},
-	{0, 0, 1, 0, 1},
-	{0, 1, 1, 0, 2},
-	{12345, 67890, 0, 0, 80235},
-	{12345, 67890, 1, 0, 80236},
-	{_M, 1, 0, 1, 0},
-	{_M, 0, 1, 1, 0},
-	{_M, 1, 1, 1, 1},
-	{_M, _M, 0, 1, _M - 1},
-	{_M, _M, 1, 1, _M},
-}
-
-func testFunWW(t *testing.T, msg string, f funWW, a argWW) {
-	z1, z0 := f(a.x, a.y, a.c)
-	if z1 != a.z1 || z0 != a.z0 {
-		t.Errorf("%s%+v\n\tgot z1:z0 = %#x:%#x; want %#x:%#x", msg, a, z1, z0, a.z1, a.z0)
-	}
-}
-
-func TestFunWW(t *testing.T) {
-	for _, a := range sumWW {
-		arg := a
-		testFunWW(t, "addWW_g", addWW_g, arg)
-
-		arg = argWW{a.y, a.x, a.c, a.z1, a.z0}
-		testFunWW(t, "addWW_g symmetric", addWW_g, arg)
-
-		arg = argWW{a.z0, a.x, a.c, a.z1, a.y}
-		testFunWW(t, "subWW_g", subWW_g, arg)
-
-		arg = argWW{a.z0, a.y, a.c, a.z1, a.x}
-		testFunWW(t, "subWW_g symmetric", subWW_g, arg)
-	}
-}
-
-type funVV func(z, x, y []Word) (c Word)
-type argVV struct {
-	z, x, y nat
-	c       Word
-}
-
-var sumVV = []argVV{
-	{},
-	{nat{0}, nat{0}, nat{0}, 0},
-	{nat{1}, nat{1}, nat{0}, 0},
-	{nat{0}, nat{_M}, nat{1}, 1},
-	{nat{80235}, nat{12345}, nat{67890}, 0},
-	{nat{_M - 1}, nat{_M}, nat{_M}, 1},
-	{nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, nat{1, 0, 0, 0}, 1},
-	{nat{0, 0, 0, _M}, nat{_M, _M, _M, _M - 1}, nat{1, 0, 0, 0}, 0},
-	{nat{0, 0, 0, 0}, nat{_M, 0, _M, 0}, nat{1, _M, 0, _M}, 1},
-}
-
-func testFunVV(t *testing.T, msg string, f funVV, a argVV) {
-	z := make(nat, len(a.z))
-	c := f(z, a.x, a.y)
-	for i, zi := range z {
-		if zi != a.z[i] {
-			t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i])
-			break
-		}
-	}
-	if c != a.c {
-		t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c)
-	}
-}
-
-func TestFunVV(t *testing.T) {
-	for _, a := range sumVV {
-		arg := a
-		testFunVV(t, "addVV_g", addVV_g, arg)
-		testFunVV(t, "addVV", addVV, arg)
-
-		arg = argVV{a.z, a.y, a.x, a.c}
-		testFunVV(t, "addVV_g symmetric", addVV_g, arg)
-		testFunVV(t, "addVV symmetric", addVV, arg)
-
-		arg = argVV{a.x, a.z, a.y, a.c}
-		testFunVV(t, "subVV_g", subVV_g, arg)
-		testFunVV(t, "subVV", subVV, arg)
-
-		arg = argVV{a.y, a.z, a.x, a.c}
-		testFunVV(t, "subVV_g symmetric", subVV_g, arg)
-		testFunVV(t, "subVV symmetric", subVV, arg)
-	}
-}
-
-// Always the same seed for reproducible results.
-var rnd = rand.New(rand.NewSource(0))
-
-func rndW() Word {
-	return Word(rnd.Int63()<<1 | rnd.Int63n(2))
-}
-
-func rndV(n int) []Word {
-	v := make([]Word, n)
-	for i := range v {
-		v[i] = rndW()
-	}
-	return v
-}
-
-func benchmarkFunVV(b *testing.B, f funVV, n int) {
-	x := rndV(n)
-	y := rndV(n)
-	z := make([]Word, n)
-	b.SetBytes(int64(n * _W))
-	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
-		f(z, x, y)
-	}
-}
-
-func BenchmarkAddVV_1(b *testing.B)   { benchmarkFunVV(b, addVV, 1) }
-func BenchmarkAddVV_2(b *testing.B)   { benchmarkFunVV(b, addVV, 2) }
-func BenchmarkAddVV_3(b *testing.B)   { benchmarkFunVV(b, addVV, 3) }
-func BenchmarkAddVV_4(b *testing.B)   { benchmarkFunVV(b, addVV, 4) }
-func BenchmarkAddVV_5(b *testing.B)   { benchmarkFunVV(b, addVV, 5) }
-func BenchmarkAddVV_1e1(b *testing.B) { benchmarkFunVV(b, addVV, 1e1) }
-func BenchmarkAddVV_1e2(b *testing.B) { benchmarkFunVV(b, addVV, 1e2) }
-func BenchmarkAddVV_1e3(b *testing.B) { benchmarkFunVV(b, addVV, 1e3) }
-func BenchmarkAddVV_1e4(b *testing.B) { benchmarkFunVV(b, addVV, 1e4) }
-func BenchmarkAddVV_1e5(b *testing.B) { benchmarkFunVV(b, addVV, 1e5) }
-
-type funVW func(z, x []Word, y Word) (c Word)
-type argVW struct {
-	z, x nat
-	y    Word
-	c    Word
-}
-
-var sumVW = []argVW{
-	{},
-	{nil, nil, 2, 2},
-	{nat{0}, nat{0}, 0, 0},
-	{nat{1}, nat{0}, 1, 0},
-	{nat{1}, nat{1}, 0, 0},
-	{nat{0}, nat{_M}, 1, 1},
-	{nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1},
-	{nat{585}, nat{314}, 271, 0},
-}
-
-var lshVW = []argVW{
-	{},
-	{nat{0}, nat{0}, 0, 0},
-	{nat{0}, nat{0}, 1, 0},
-	{nat{0}, nat{0}, 20, 0},
-
-	{nat{_M}, nat{_M}, 0, 0},
-	{nat{_M << 1 & _M}, nat{_M}, 1, 1},
-	{nat{_M << 20 & _M}, nat{_M}, 20, _M >> (_W - 20)},
-
-	{nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0},
-	{nat{_M << 1 & _M, _M, _M}, nat{_M, _M, _M}, 1, 1},
-	{nat{_M << 20 & _M, _M, _M}, nat{_M, _M, _M}, 20, _M >> (_W - 20)},
-}
-
-var rshVW = []argVW{
-	{},
-	{nat{0}, nat{0}, 0, 0},
-	{nat{0}, nat{0}, 1, 0},
-	{nat{0}, nat{0}, 20, 0},
-
-	{nat{_M}, nat{_M}, 0, 0},
-	{nat{_M >> 1}, nat{_M}, 1, _M << (_W - 1) & _M},
-	{nat{_M >> 20}, nat{_M}, 20, _M << (_W - 20) & _M},
-
-	{nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0},
-	{nat{_M, _M, _M >> 1}, nat{_M, _M, _M}, 1, _M << (_W - 1) & _M},
-	{nat{_M, _M, _M >> 20}, nat{_M, _M, _M}, 20, _M << (_W - 20) & _M},
-}
-
-func testFunVW(t *testing.T, msg string, f funVW, a argVW) {
-	z := make(nat, len(a.z))
-	c := f(z, a.x, a.y)
-	for i, zi := range z {
-		if zi != a.z[i] {
-			t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i])
-			break
-		}
-	}
-	if c != a.c {
-		t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c)
-	}
-}
-
-func makeFunVW(f func(z, x []Word, s uint) (c Word)) funVW {
-	return func(z, x []Word, s Word) (c Word) {
-		return f(z, x, uint(s))
-	}
-}
-
-func TestFunVW(t *testing.T) {
-	for _, a := range sumVW {
-		arg := a
-		testFunVW(t, "addVW_g", addVW_g, arg)
-		testFunVW(t, "addVW", addVW, arg)
-
-		arg = argVW{a.x, a.z, a.y, a.c}
-		testFunVW(t, "subVW_g", subVW_g, arg)
-		testFunVW(t, "subVW", subVW, arg)
-	}
-
-	shlVW_g := makeFunVW(shlVU_g)
-	shlVW := makeFunVW(shlVU)
-	for _, a := range lshVW {
-		arg := a
-		testFunVW(t, "shlVU_g", shlVW_g, arg)
-		testFunVW(t, "shlVU", shlVW, arg)
-	}
-
-	shrVW_g := makeFunVW(shrVU_g)
-	shrVW := makeFunVW(shrVU)
-	for _, a := range rshVW {
-		arg := a
-		testFunVW(t, "shrVU_g", shrVW_g, arg)
-		testFunVW(t, "shrVU", shrVW, arg)
-	}
-}
-
-func benchmarkFunVW(b *testing.B, f funVW, n int) {
-	x := rndV(n)
-	y := rndW()
-	z := make([]Word, n)
-	b.SetBytes(int64(n * _S))
-	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
-		f(z, x, y)
-	}
-}
-
-func BenchmarkAddVW_1(b *testing.B)   { benchmarkFunVW(b, addVW, 1) }
-func BenchmarkAddVW_2(b *testing.B)   { benchmarkFunVW(b, addVW, 2) }
-func BenchmarkAddVW_3(b *testing.B)   { benchmarkFunVW(b, addVW, 3) }
-func BenchmarkAddVW_4(b *testing.B)   { benchmarkFunVW(b, addVW, 4) }
-func BenchmarkAddVW_5(b *testing.B)   { benchmarkFunVW(b, addVW, 5) }
-func BenchmarkAddVW_1e1(b *testing.B) { benchmarkFunVW(b, addVW, 1e1) }
-func BenchmarkAddVW_1e2(b *testing.B) { benchmarkFunVW(b, addVW, 1e2) }
-func BenchmarkAddVW_1e3(b *testing.B) { benchmarkFunVW(b, addVW, 1e3) }
-func BenchmarkAddVW_1e4(b *testing.B) { benchmarkFunVW(b, addVW, 1e4) }
-func BenchmarkAddVW_1e5(b *testing.B) { benchmarkFunVW(b, addVW, 1e5) }
-
-type funVWW func(z, x []Word, y, r Word) (c Word)
-type argVWW struct {
-	z, x nat
-	y, r Word
-	c    Word
-}
-
-var prodVWW = []argVWW{
-	{},
-	{nat{0}, nat{0}, 0, 0, 0},
-	{nat{991}, nat{0}, 0, 991, 0},
-	{nat{0}, nat{_M}, 0, 0, 0},
-	{nat{991}, nat{_M}, 0, 991, 0},
-	{nat{0}, nat{0}, _M, 0, 0},
-	{nat{991}, nat{0}, _M, 991, 0},
-	{nat{1}, nat{1}, 1, 0, 0},
-	{nat{992}, nat{1}, 1, 991, 0},
-	{nat{22793}, nat{991}, 23, 0, 0},
-	{nat{22800}, nat{991}, 23, 7, 0},
-	{nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0, 0},
-	{nat{7, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 7, 0},
-	{nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0, 0},
-	{nat{991, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 991, 0},
-	{nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0, 0},
-	{nat{991, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 991, 0},
-	{nat{_M << 1 & _M}, nat{_M}, 1 << 1, 0, _M >> (_W - 1)},
-	{nat{_M<<1&_M + 1}, nat{_M}, 1 << 1, 1, _M >> (_W - 1)},
-	{nat{_M << 7 & _M}, nat{_M}, 1 << 7, 0, _M >> (_W - 7)},
-	{nat{_M<<7&_M + 1<<6}, nat{_M}, 1 << 7, 1 << 6, _M >> (_W - 7)},
-	{nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 0, _M >> (_W - 7)},
-	{nat{_M<<7&_M + 1<<6, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 1 << 6, _M >> (_W - 7)},
-}
-
-func testFunVWW(t *testing.T, msg string, f funVWW, a argVWW) {
-	z := make(nat, len(a.z))
-	c := f(z, a.x, a.y, a.r)
-	for i, zi := range z {
-		if zi != a.z[i] {
-			t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i])
-			break
-		}
-	}
-	if c != a.c {
-		t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c)
-	}
-}
-
-// TODO(gri) mulAddVWW and divWVW are symmetric operations but
-//           their signature is not symmetric. Try to unify.
-
-type funWVW func(z []Word, xn Word, x []Word, y Word) (r Word)
-type argWVW struct {
-	z  nat
-	xn Word
-	x  nat
-	y  Word
-	r  Word
-}
-
-func testFunWVW(t *testing.T, msg string, f funWVW, a argWVW) {
-	z := make(nat, len(a.z))
-	r := f(z, a.xn, a.x, a.y)
-	for i, zi := range z {
-		if zi != a.z[i] {
-			t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i])
-			break
-		}
-	}
-	if r != a.r {
-		t.Errorf("%s%+v\n\tgot r = %#x; want %#x", msg, a, r, a.r)
-	}
-}
-
-func TestFunVWW(t *testing.T) {
-	for _, a := range prodVWW {
-		arg := a
-		testFunVWW(t, "mulAddVWW_g", mulAddVWW_g, arg)
-		testFunVWW(t, "mulAddVWW", mulAddVWW, arg)
-
-		if a.y != 0 && a.r < a.y {
-			arg := argWVW{a.x, a.c, a.z, a.y, a.r}
-			testFunWVW(t, "divWVW_g", divWVW_g, arg)
-			testFunWVW(t, "divWVW", divWVW, arg)
-		}
-	}
-}
-
-var mulWWTests = []struct {
-	x, y Word
-	q, r Word
-}{
-	{_M, _M, _M - 1, 1},
-	// 32 bit only: {0xc47dfa8c, 50911, 0x98a4, 0x998587f4},
-}
-
-func TestMulWW(t *testing.T) {
-	for i, test := range mulWWTests {
-		q, r := mulWW_g(test.x, test.y)
-		if q != test.q || r != test.r {
-			t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r)
-		}
-	}
-}
-
-var mulAddWWWTests = []struct {
-	x, y, c Word
-	q, r    Word
-}{
-	// TODO(agl): These will only work on 64-bit platforms.
-	// {15064310297182388543, 0xe7df04d2d35d5d80, 13537600649892366549, 13644450054494335067, 10832252001440893781},
-	// {15064310297182388543, 0xdab2f18048baa68d, 13644450054494335067, 12869334219691522700, 14233854684711418382},
-	{_M, _M, 0, _M - 1, 1},
-	{_M, _M, _M, _M, 0},
-}
-
-func TestMulAddWWW(t *testing.T) {
-	for i, test := range mulAddWWWTests {
-		q, r := mulAddWWW_g(test.x, test.y, test.c)
-		if q != test.q || r != test.r {
-			t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r)
-		}
-	}
-}
-
-func benchmarkAddMulVVW(b *testing.B, n int) {
-	x := rndV(n)
-	y := rndW()
-	z := make([]Word, n)
-	b.SetBytes(int64(n * _W))
-	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
-		addMulVVW(z, x, y)
-	}
-}
-
-func BenchmarkAddMulVVW_1(b *testing.B)   { benchmarkAddMulVVW(b, 1) }
-func BenchmarkAddMulVVW_2(b *testing.B)   { benchmarkAddMulVVW(b, 2) }
-func BenchmarkAddMulVVW_3(b *testing.B)   { benchmarkAddMulVVW(b, 3) }
-func BenchmarkAddMulVVW_4(b *testing.B)   { benchmarkAddMulVVW(b, 4) }
-func BenchmarkAddMulVVW_5(b *testing.B)   { benchmarkAddMulVVW(b, 5) }
-func BenchmarkAddMulVVW_1e1(b *testing.B) { benchmarkAddMulVVW(b, 1e1) }
-func BenchmarkAddMulVVW_1e2(b *testing.B) { benchmarkAddMulVVW(b, 1e2) }
-func BenchmarkAddMulVVW_1e3(b *testing.B) { benchmarkAddMulVVW(b, 1e3) }
-func BenchmarkAddMulVVW_1e4(b *testing.B) { benchmarkAddMulVVW(b, 1e4) }
-func BenchmarkAddMulVVW_1e5(b *testing.B) { benchmarkAddMulVVW(b, 1e5) }
-
-func testWordBitLen(t *testing.T, fname string, f func(Word) int) {
-	for i := 0; i <= _W; i++ {
-		x := Word(1) << uint(i-1) // i == 0 => x == 0
-		n := f(x)
-		if n != i {
-			t.Errorf("got %d; want %d for %s(%#x)", n, i, fname, x)
-		}
-	}
-}
-
-func TestWordBitLen(t *testing.T) {
-	testWordBitLen(t, "bitLen", bitLen)
-	testWordBitLen(t, "bitLen_g", bitLen_g)
-}
-
-// runs b.N iterations of bitLen called on a Word containing (1 << nbits)-1.
-func benchmarkBitLenN(b *testing.B, nbits uint) {
-	testword := Word((uint64(1) << nbits) - 1)
-	for i := 0; i < b.N; i++ {
-		bitLen(testword)
-	}
-}
-
-// Individual bitLen tests. Numbers chosen to examine both sides
-// of powers-of-two boundaries.
-func BenchmarkBitLen0(b *testing.B)  { benchmarkBitLenN(b, 0) }
-func BenchmarkBitLen1(b *testing.B)  { benchmarkBitLenN(b, 1) }
-func BenchmarkBitLen2(b *testing.B)  { benchmarkBitLenN(b, 2) }
-func BenchmarkBitLen3(b *testing.B)  { benchmarkBitLenN(b, 3) }
-func BenchmarkBitLen4(b *testing.B)  { benchmarkBitLenN(b, 4) }
-func BenchmarkBitLen5(b *testing.B)  { benchmarkBitLenN(b, 5) }
-func BenchmarkBitLen8(b *testing.B)  { benchmarkBitLenN(b, 8) }
-func BenchmarkBitLen9(b *testing.B)  { benchmarkBitLenN(b, 9) }
-func BenchmarkBitLen16(b *testing.B) { benchmarkBitLenN(b, 16) }
-func BenchmarkBitLen17(b *testing.B) { benchmarkBitLenN(b, 17) }
-func BenchmarkBitLen31(b *testing.B) { benchmarkBitLenN(b, 31) }
diff --git a/src/cmd/compile/internal/big/bits_test.go b/src/cmd/compile/internal/big/bits_test.go
deleted file mode 100644
index 985b60b..0000000
--- a/src/cmd/compile/internal/big/bits_test.go
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements the Bits type used for testing Float operations
-// via an independent (albeit slower) representations for floating-point
-// numbers.
-
-package big
-
-import (
-	"fmt"
-	"sort"
-	"testing"
-)
-
-// A Bits value b represents a finite floating-point number x of the form
-//
-//	x = 2**b[0] + 2**b[1] + ... 2**b[len(b)-1]
-//
-// The order of slice elements is not significant. Negative elements may be
-// used to form fractions. A Bits value is normalized if each b[i] occurs at
-// most once. For instance Bits{0, 0, 1} is not normalized but represents the
-// same floating-point number as Bits{2}, which is normalized. The zero (nil)
-// value of Bits is a ready to use Bits value and represents the value 0.
-type Bits []int
-
-func (x Bits) add(y Bits) Bits {
-	return append(x, y...)
-}
-
-func (x Bits) mul(y Bits) Bits {
-	var p Bits
-	for _, x := range x {
-		for _, y := range y {
-			p = append(p, x+y)
-		}
-	}
-	return p
-}
-
-func TestMulBits(t *testing.T) {
-	for _, test := range []struct {
-		x, y, want Bits
-	}{
-		{nil, nil, nil},
-		{Bits{}, Bits{}, nil},
-		{Bits{0}, Bits{0}, Bits{0}},
-		{Bits{0}, Bits{1}, Bits{1}},
-		{Bits{1}, Bits{1, 2, 3}, Bits{2, 3, 4}},
-		{Bits{-1}, Bits{1}, Bits{0}},
-		{Bits{-10, -1, 0, 1, 10}, Bits{1, 2, 3}, Bits{-9, -8, -7, 0, 1, 2, 1, 2, 3, 2, 3, 4, 11, 12, 13}},
-	} {
-		got := fmt.Sprintf("%v", test.x.mul(test.y))
-		want := fmt.Sprintf("%v", test.want)
-		if got != want {
-			t.Errorf("%v * %v = %s; want %s", test.x, test.y, got, want)
-		}
-
-	}
-}
-
-// norm returns the normalized bits for x: It removes multiple equal entries
-// by treating them as an addition (e.g., Bits{5, 5} => Bits{6}), and it sorts
-// the result list for reproducible results.
-func (x Bits) norm() Bits {
-	m := make(map[int]bool)
-	for _, b := range x {
-		for m[b] {
-			m[b] = false
-			b++
-		}
-		m[b] = true
-	}
-	var z Bits
-	for b, set := range m {
-		if set {
-			z = append(z, b)
-		}
-	}
-	sort.Ints([]int(z))
-	return z
-}
-
-func TestNormBits(t *testing.T) {
-	for _, test := range []struct {
-		x, want Bits
-	}{
-		{nil, nil},
-		{Bits{}, Bits{}},
-		{Bits{0}, Bits{0}},
-		{Bits{0, 0}, Bits{1}},
-		{Bits{3, 1, 1}, Bits{2, 3}},
-		{Bits{10, 9, 8, 7, 6, 6}, Bits{11}},
-	} {
-		got := fmt.Sprintf("%v", test.x.norm())
-		want := fmt.Sprintf("%v", test.want)
-		if got != want {
-			t.Errorf("normBits(%v) = %s; want %s", test.x, got, want)
-		}
-
-	}
-}
-
-// round returns the Float value corresponding to x after rounding x
-// to prec bits according to mode.
-func (x Bits) round(prec uint, mode RoundingMode) *Float {
-	x = x.norm()
-
-	// determine range
-	var min, max int
-	for i, b := range x {
-		if i == 0 || b < min {
-			min = b
-		}
-		if i == 0 || b > max {
-			max = b
-		}
-	}
-	prec0 := uint(max + 1 - min)
-	if prec >= prec0 {
-		return x.Float()
-	}
-	// prec < prec0
-
-	// determine bit 0, rounding, and sticky bit, and result bits z
-	var bit0, rbit, sbit uint
-	var z Bits
-	r := max - int(prec)
-	for _, b := range x {
-		switch {
-		case b == r:
-			rbit = 1
-		case b < r:
-			sbit = 1
-		default:
-			// b > r
-			if b == r+1 {
-				bit0 = 1
-			}
-			z = append(z, b)
-		}
-	}
-
-	// round
-	f := z.Float() // rounded to zero
-	if mode == ToNearestAway {
-		panic("not yet implemented")
-	}
-	if mode == ToNearestEven && rbit == 1 && (sbit == 1 || sbit == 0 && bit0 != 0) || mode == AwayFromZero {
-		// round away from zero
-		f.SetMode(ToZero).SetPrec(prec)
-		f.Add(f, Bits{int(r) + 1}.Float())
-	}
-	return f
-}
-
-// Float returns the *Float z of the smallest possible precision such that
-// z = sum(2**bits[i]), with i = range bits. If multiple bits[i] are equal,
-// they are added: Bits{0, 1, 0}.Float() == 2**0 + 2**1 + 2**0 = 4.
-func (bits Bits) Float() *Float {
-	// handle 0
-	if len(bits) == 0 {
-		return new(Float)
-	}
-	// len(bits) > 0
-
-	// determine lsb exponent
-	var min int
-	for i, b := range bits {
-		if i == 0 || b < min {
-			min = b
-		}
-	}
-
-	// create bit pattern
-	x := NewInt(0)
-	for _, b := range bits {
-		badj := b - min
-		// propagate carry if necessary
-		for x.Bit(badj) != 0 {
-			x.SetBit(x, badj, 0)
-			badj++
-		}
-		x.SetBit(x, badj, 1)
-	}
-
-	// create corresponding float
-	z := new(Float).SetInt(x) // normalized
-	if e := int64(z.exp) + int64(min); MinExp <= e && e <= MaxExp {
-		z.exp = int32(e)
-	} else {
-		// this should never happen for our test cases
-		panic("exponent out of range")
-	}
-	return z
-}
-
-func TestFromBits(t *testing.T) {
-	for _, test := range []struct {
-		bits Bits
-		want string
-	}{
-		// all different bit numbers
-		{nil, "0"},
-		{Bits{0}, "0x.8p+1"},
-		{Bits{1}, "0x.8p+2"},
-		{Bits{-1}, "0x.8p+0"},
-		{Bits{63}, "0x.8p+64"},
-		{Bits{33, -30}, "0x.8000000000000001p+34"},
-		{Bits{255, 0}, "0x.8000000000000000000000000000000000000000000000000000000000000001p+256"},
-
-		// multiple equal bit numbers
-		{Bits{0, 0}, "0x.8p+2"},
-		{Bits{0, 0, 0, 0}, "0x.8p+3"},
-		{Bits{0, 1, 0}, "0x.8p+3"},
-		{append(Bits{2, 1, 0} /* 7 */, Bits{3, 1} /* 10 */ ...), "0x.88p+5" /* 17 */},
-	} {
-		f := test.bits.Float()
-		if got := f.Text('p', 0); got != test.want {
-			t.Errorf("setBits(%v) = %s; want %s", test.bits, got, test.want)
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/calibrate_test.go b/src/cmd/compile/internal/big/calibrate_test.go
deleted file mode 100644
index f69ffbf..0000000
--- a/src/cmd/compile/internal/big/calibrate_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file prints execution times for the Mul benchmark
-// given different Karatsuba thresholds. The result may be
-// used to manually fine-tune the threshold constant. The
-// results are somewhat fragile; use repeated runs to get
-// a clear picture.
-
-// Usage: go test -run=TestCalibrate -calibrate
-
-package big
-
-import (
-	"flag"
-	"fmt"
-	"testing"
-	"time"
-)
-
-var calibrate = flag.Bool("calibrate", false, "run calibration test")
-
-func karatsubaLoad(b *testing.B) {
-	BenchmarkMul(b)
-}
-
-// measureKaratsuba returns the time to run a Karatsuba-relevant benchmark
-// given Karatsuba threshold th.
-func measureKaratsuba(th int) time.Duration {
-	th, karatsubaThreshold = karatsubaThreshold, th
-	res := testing.Benchmark(karatsubaLoad)
-	karatsubaThreshold = th
-	return time.Duration(res.NsPerOp())
-}
-
-func computeThresholds() {
-	fmt.Printf("Multiplication times for varying Karatsuba thresholds\n")
-	fmt.Printf("(run repeatedly for good results)\n")
-
-	// determine Tk, the work load execution time using basic multiplication
-	Tb := measureKaratsuba(1e9) // th == 1e9 => Karatsuba multiplication disabled
-	fmt.Printf("Tb = %10s\n", Tb)
-
-	// thresholds
-	th := 4
-	th1 := -1
-	th2 := -1
-
-	var deltaOld time.Duration
-	for count := -1; count != 0 && th < 128; count-- {
-		// determine Tk, the work load execution time using Karatsuba multiplication
-		Tk := measureKaratsuba(th)
-
-		// improvement over Tb
-		delta := (Tb - Tk) * 100 / Tb
-
-		fmt.Printf("th = %3d  Tk = %10s  %4d%%", th, Tk, delta)
-
-		// determine break-even point
-		if Tk < Tb && th1 < 0 {
-			th1 = th
-			fmt.Print("  break-even point")
-		}
-
-		// determine diminishing return
-		if 0 < delta && delta < deltaOld && th2 < 0 {
-			th2 = th
-			fmt.Print("  diminishing return")
-		}
-		deltaOld = delta
-
-		fmt.Println()
-
-		// trigger counter
-		if th1 >= 0 && th2 >= 0 && count < 0 {
-			count = 10 // this many extra measurements after we got both thresholds
-		}
-
-		th++
-	}
-}
-
-func TestCalibrate(t *testing.T) {
-	if *calibrate {
-		computeThresholds()
-	}
-}
diff --git a/src/cmd/compile/internal/big/decimal.go b/src/cmd/compile/internal/big/decimal.go
deleted file mode 100644
index 2c0c9da..0000000
--- a/src/cmd/compile/internal/big/decimal.go
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements multi-precision decimal numbers.
-// The implementation is for float to decimal conversion only;
-// not general purpose use.
-// The only operations are precise conversion from binary to
-// decimal and rounding.
-//
-// The key observation and some code (shr) is borrowed from
-// strconv/decimal.go: conversion of binary fractional values can be done
-// precisely in multi-precision decimal because 2 divides 10 (required for
-// >> of mantissa); but conversion of decimal floating-point values cannot
-// be done precisely in binary representation.
-//
-// In contrast to strconv/decimal.go, only right shift is implemented in
-// decimal format - left shift can be done precisely in binary format.
-
-package big
-
-// A decimal represents an unsigned floating-point number in decimal representation.
-// The value of a non-zero decimal d is d.mant * 10**d.exp with 0.5 <= d.mant < 1,
-// with the most-significant mantissa digit at index 0. For the zero decimal, the
-// mantissa length and exponent are 0.
-// The zero value for decimal represents a ready-to-use 0.0.
-type decimal struct {
-	mant []byte // mantissa ASCII digits, big-endian
-	exp  int    // exponent
-}
-
-// at returns the i'th mantissa digit, starting with the most significant digit at 0.
-func (d *decimal) at(i int) byte {
-	if 0 <= i && i < len(d.mant) {
-		return d.mant[i]
-	}
-	return '0'
-}
-
-// Maximum shift amount that can be done in one pass without overflow.
-// A Word has _W bits and (1<<maxShift - 1)*10 + 9 must fit into Word.
-const maxShift = _W - 4
-
-// TODO(gri) Since we know the desired decimal precision when converting
-// a floating-point number, we may be able to limit the number of decimal
-// digits that need to be computed by init by providing an additional
-// precision argument and keeping track of when a number was truncated early
-// (equivalent of "sticky bit" in binary rounding).
-
-// TODO(gri) Along the same lines, enforce some limit to shift magnitudes
-// to avoid "infinitely" long running conversions (until we run out of space).
-
-// Init initializes x to the decimal representation of m << shift (for
-// shift >= 0), or m >> -shift (for shift < 0).
-func (x *decimal) init(m nat, shift int) {
-	// special case 0
-	if len(m) == 0 {
-		x.mant = x.mant[:0]
-		x.exp = 0
-		return
-	}
-
-	// Optimization: If we need to shift right, first remove any trailing
-	// zero bits from m to reduce shift amount that needs to be done in
-	// decimal format (since that is likely slower).
-	if shift < 0 {
-		ntz := m.trailingZeroBits()
-		s := uint(-shift)
-		if s >= ntz {
-			s = ntz // shift at most ntz bits
-		}
-		m = nat(nil).shr(m, s)
-		shift += int(s)
-	}
-
-	// Do any shift left in binary representation.
-	if shift > 0 {
-		m = nat(nil).shl(m, uint(shift))
-		shift = 0
-	}
-
-	// Convert mantissa into decimal representation.
-	s := m.utoa(10)
-	n := len(s)
-	x.exp = n
-	// Trim trailing zeros; instead the exponent is tracking
-	// the decimal point independent of the number of digits.
-	for n > 0 && s[n-1] == '0' {
-		n--
-	}
-	x.mant = append(x.mant[:0], s[:n]...)
-
-	// Do any (remaining) shift right in decimal representation.
-	if shift < 0 {
-		for shift < -maxShift {
-			shr(x, maxShift)
-			shift += maxShift
-		}
-		shr(x, uint(-shift))
-	}
-}
-
-// shr implements x >> s, for s <= maxShift.
-func shr(x *decimal, s uint) {
-	// Division by 1<<s using shift-and-subtract algorithm.
-
-	// pick up enough leading digits to cover first shift
-	r := 0 // read index
-	var n Word
-	for n>>s == 0 && r < len(x.mant) {
-		ch := Word(x.mant[r])
-		r++
-		n = n*10 + ch - '0'
-	}
-	if n == 0 {
-		// x == 0; shouldn't get here, but handle anyway
-		x.mant = x.mant[:0]
-		return
-	}
-	for n>>s == 0 {
-		r++
-		n *= 10
-	}
-	x.exp += 1 - r
-
-	// read a digit, write a digit
-	w := 0 // write index
-	for r < len(x.mant) {
-		ch := Word(x.mant[r])
-		r++
-		d := n >> s
-		n -= d << s
-		x.mant[w] = byte(d + '0')
-		w++
-		n = n*10 + ch - '0'
-	}
-
-	// write extra digits that still fit
-	for n > 0 && w < len(x.mant) {
-		d := n >> s
-		n -= d << s
-		x.mant[w] = byte(d + '0')
-		w++
-		n = n * 10
-	}
-	x.mant = x.mant[:w] // the number may be shorter (e.g. 1024 >> 10)
-
-	// append additional digits that didn't fit
-	for n > 0 {
-		d := n >> s
-		n -= d << s
-		x.mant = append(x.mant, byte(d+'0'))
-		n = n * 10
-	}
-
-	trim(x)
-}
-
-func (x *decimal) String() string {
-	if len(x.mant) == 0 {
-		return "0"
-	}
-
-	var buf []byte
-	switch {
-	case x.exp <= 0:
-		// 0.00ddd
-		buf = append(buf, "0."...)
-		buf = appendZeros(buf, -x.exp)
-		buf = append(buf, x.mant...)
-
-	case /* 0 < */ x.exp < len(x.mant):
-		// dd.ddd
-		buf = append(buf, x.mant[:x.exp]...)
-		buf = append(buf, '.')
-		buf = append(buf, x.mant[x.exp:]...)
-
-	default: // len(x.mant) <= x.exp
-		// ddd00
-		buf = append(buf, x.mant...)
-		buf = appendZeros(buf, x.exp-len(x.mant))
-	}
-
-	return string(buf)
-}
-
-// appendZeros appends n 0 digits to buf and returns buf.
-func appendZeros(buf []byte, n int) []byte {
-	for ; n > 0; n-- {
-		buf = append(buf, '0')
-	}
-	return buf
-}
-
-// shouldRoundUp reports if x should be rounded up
-// if shortened to n digits. n must be a valid index
-// for x.mant.
-func shouldRoundUp(x *decimal, n int) bool {
-	if x.mant[n] == '5' && n+1 == len(x.mant) {
-		// exactly halfway - round to even
-		return n > 0 && (x.mant[n-1]-'0')&1 != 0
-	}
-	// not halfway - digit tells all (x.mant has no trailing zeros)
-	return x.mant[n] >= '5'
-}
-
-// round sets x to (at most) n mantissa digits by rounding it
-// to the nearest even value with n (or fever) mantissa digits.
-// If n < 0, x remains unchanged.
-func (x *decimal) round(n int) {
-	if n < 0 || n >= len(x.mant) {
-		return // nothing to do
-	}
-
-	if shouldRoundUp(x, n) {
-		x.roundUp(n)
-	} else {
-		x.roundDown(n)
-	}
-}
-
-func (x *decimal) roundUp(n int) {
-	if n < 0 || n >= len(x.mant) {
-		return // nothing to do
-	}
-	// 0 <= n < len(x.mant)
-
-	// find first digit < '9'
-	for n > 0 && x.mant[n-1] >= '9' {
-		n--
-	}
-
-	if n == 0 {
-		// all digits are '9's => round up to '1' and update exponent
-		x.mant[0] = '1' // ok since len(x.mant) > n
-		x.mant = x.mant[:1]
-		x.exp++
-		return
-	}
-
-	// n > 0 && x.mant[n-1] < '9'
-	x.mant[n-1]++
-	x.mant = x.mant[:n]
-	// x already trimmed
-}
-
-func (x *decimal) roundDown(n int) {
-	if n < 0 || n >= len(x.mant) {
-		return // nothing to do
-	}
-	x.mant = x.mant[:n]
-	trim(x)
-}
-
-// trim cuts off any trailing zeros from x's mantissa;
-// they are meaningless for the value of x.
-func trim(x *decimal) {
-	i := len(x.mant)
-	for i > 0 && x.mant[i-1] == '0' {
-		i--
-	}
-	x.mant = x.mant[:i]
-	if i == 0 {
-		x.exp = 0
-	}
-}
diff --git a/src/cmd/compile/internal/big/decimal_test.go b/src/cmd/compile/internal/big/decimal_test.go
deleted file mode 100644
index 15bdb18..0000000
--- a/src/cmd/compile/internal/big/decimal_test.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import "testing"
-
-func TestDecimalString(t *testing.T) {
-	for _, test := range []struct {
-		x    decimal
-		want string
-	}{
-		{want: "0"},
-		{decimal{nil, 1000}, "0"}, // exponent of 0 is ignored
-		{decimal{[]byte("12345"), 0}, "0.12345"},
-		{decimal{[]byte("12345"), -3}, "0.00012345"},
-		{decimal{[]byte("12345"), +3}, "123.45"},
-		{decimal{[]byte("12345"), +10}, "1234500000"},
-	} {
-		if got := test.x.String(); got != test.want {
-			t.Errorf("%v == %s; want %s", test.x, got, test.want)
-		}
-	}
-}
-
-func TestDecimalInit(t *testing.T) {
-	for _, test := range []struct {
-		x     Word
-		shift int
-		want  string
-	}{
-		{0, 0, "0"},
-		{0, -100, "0"},
-		{0, 100, "0"},
-		{1, 0, "1"},
-		{1, 10, "1024"},
-		{1, 100, "1267650600228229401496703205376"},
-		{1, -100, "0.0000000000000000000000000000007888609052210118054117285652827862296732064351090230047702789306640625"},
-		{12345678, 8, "3160493568"},
-		{12345678, -8, "48225.3046875"},
-		{195312, 9, "99999744"},
-		{1953125, 9, "1000000000"},
-	} {
-		var d decimal
-		d.init(nat{test.x}.norm(), test.shift)
-		if got := d.String(); got != test.want {
-			t.Errorf("%d << %d == %s; want %s", test.x, test.shift, got, test.want)
-		}
-	}
-}
-
-func TestDecimalRounding(t *testing.T) {
-	for _, test := range []struct {
-		x              uint64
-		n              int
-		down, even, up string
-	}{
-		{0, 0, "0", "0", "0"},
-		{0, 1, "0", "0", "0"},
-
-		{1, 0, "0", "0", "10"},
-		{5, 0, "0", "0", "10"},
-		{9, 0, "0", "10", "10"},
-
-		{15, 1, "10", "20", "20"},
-		{45, 1, "40", "40", "50"},
-		{95, 1, "90", "100", "100"},
-
-		{12344999, 4, "12340000", "12340000", "12350000"},
-		{12345000, 4, "12340000", "12340000", "12350000"},
-		{12345001, 4, "12340000", "12350000", "12350000"},
-		{23454999, 4, "23450000", "23450000", "23460000"},
-		{23455000, 4, "23450000", "23460000", "23460000"},
-		{23455001, 4, "23450000", "23460000", "23460000"},
-
-		{99994999, 4, "99990000", "99990000", "100000000"},
-		{99995000, 4, "99990000", "100000000", "100000000"},
-		{99999999, 4, "99990000", "100000000", "100000000"},
-
-		{12994999, 4, "12990000", "12990000", "13000000"},
-		{12995000, 4, "12990000", "13000000", "13000000"},
-		{12999999, 4, "12990000", "13000000", "13000000"},
-	} {
-		x := nat(nil).setUint64(test.x)
-
-		var d decimal
-		d.init(x, 0)
-		d.roundDown(test.n)
-		if got := d.String(); got != test.down {
-			t.Errorf("roundDown(%d, %d) = %s; want %s", test.x, test.n, got, test.down)
-		}
-
-		d.init(x, 0)
-		d.round(test.n)
-		if got := d.String(); got != test.even {
-			t.Errorf("round(%d, %d) = %s; want %s", test.x, test.n, got, test.even)
-		}
-
-		d.init(x, 0)
-		d.roundUp(test.n)
-		if got := d.String(); got != test.up {
-			t.Errorf("roundUp(%d, %d) = %s; want %s", test.x, test.n, got, test.up)
-		}
-	}
-}
-
-func BenchmarkDecimalConversion(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		for shift := -100; shift <= +100; shift++ {
-			var d decimal
-			d.init(natOne, shift)
-			d.String()
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/example_rat_test.go b/src/cmd/compile/internal/big/example_rat_test.go
deleted file mode 100644
index ef06497..0000000
--- a/src/cmd/compile/internal/big/example_rat_test.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big_test
-
-import (
-	"cmd/compile/internal/big"
-	"fmt"
-)
-
-// Use the classic continued fraction for e
-//     e = [1; 0, 1, 1, 2, 1, 1, ... 2n, 1, 1, ...]
-// i.e., for the nth term, use
-//     1          if   n mod 3 != 1
-//  (n-1)/3 * 2   if   n mod 3 == 1
-func recur(n, lim int64) *big.Rat {
-	term := new(big.Rat)
-	if n%3 != 1 {
-		term.SetInt64(1)
-	} else {
-		term.SetInt64((n - 1) / 3 * 2)
-	}
-
-	if n > lim {
-		return term
-	}
-
-	// Directly initialize frac as the fractional
-	// inverse of the result of recur.
-	frac := new(big.Rat).Inv(recur(n+1, lim))
-
-	return term.Add(term, frac)
-}
-
-// This example demonstrates how to use big.Rat to compute the
-// first 15 terms in the sequence of rational convergents for
-// the constant e (base of natural logarithm).
-func Example_eConvergents() {
-	for i := 1; i <= 15; i++ {
-		r := recur(0, int64(i))
-
-		// Print r both as a fraction and as a floating-point number.
-		// Since big.Rat implements fmt.Formatter, we can use %-13s to
-		// get a left-aligned string representation of the fraction.
-		fmt.Printf("%-13s = %s\n", r, r.FloatString(8))
-	}
-
-	// Output:
-	// 2/1           = 2.00000000
-	// 3/1           = 3.00000000
-	// 8/3           = 2.66666667
-	// 11/4          = 2.75000000
-	// 19/7          = 2.71428571
-	// 87/32         = 2.71875000
-	// 106/39        = 2.71794872
-	// 193/71        = 2.71830986
-	// 1264/465      = 2.71827957
-	// 1457/536      = 2.71828358
-	// 2721/1001     = 2.71828172
-	// 23225/8544    = 2.71828184
-	// 25946/9545    = 2.71828182
-	// 49171/18089   = 2.71828183
-	// 517656/190435 = 2.71828183
-}
diff --git a/src/cmd/compile/internal/big/example_test.go b/src/cmd/compile/internal/big/example_test.go
deleted file mode 100644
index 8a71a08..0000000
--- a/src/cmd/compile/internal/big/example_test.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big_test
-
-import (
-	"cmd/compile/internal/big"
-	"fmt"
-	"log"
-	"math"
-)
-
-func ExampleRat_SetString() {
-	r := new(big.Rat)
-	r.SetString("355/113")
-	fmt.Println(r.FloatString(3))
-	// Output: 3.142
-}
-
-func ExampleInt_SetString() {
-	i := new(big.Int)
-	i.SetString("644", 8) // octal
-	fmt.Println(i)
-	// Output: 420
-}
-
-func ExampleRat_Scan() {
-	// The Scan function is rarely used directly;
-	// the fmt package recognizes it as an implementation of fmt.Scanner.
-	r := new(big.Rat)
-	_, err := fmt.Sscan("1.5000", r)
-	if err != nil {
-		log.Println("error scanning value:", err)
-	} else {
-		fmt.Println(r)
-	}
-	// Output: 3/2
-}
-
-func ExampleInt_Scan() {
-	// The Scan function is rarely used directly;
-	// the fmt package recognizes it as an implementation of fmt.Scanner.
-	i := new(big.Int)
-	_, err := fmt.Sscan("18446744073709551617", i)
-	if err != nil {
-		log.Println("error scanning value:", err)
-	} else {
-		fmt.Println(i)
-	}
-	// Output: 18446744073709551617
-}
-
-// This example demonstrates how to use big.Int to compute the smallest
-// Fibonacci number with 100 decimal digits and to test whether it is prime.
-func Example_fibonacci() {
-	// Initialize two big ints with the first two numbers in the sequence.
-	a := big.NewInt(0)
-	b := big.NewInt(1)
-
-	// Initialize limit as 10^99, the smallest integer with 100 digits.
-	var limit big.Int
-	limit.Exp(big.NewInt(10), big.NewInt(99), nil)
-
-	// Loop while a is smaller than 1e100.
-	for a.Cmp(&limit) < 0 {
-		// Compute the next Fibonacci number, storing it in a.
-		a.Add(a, b)
-		// Swap a and b so that b is the next number in the sequence.
-		a, b = b, a
-	}
-	fmt.Println(a) // 100-digit Fibonacci number
-
-	// Test a for primality.
-	// (ProbablyPrimes' argument sets the number of Miller-Rabin
-	// rounds to be performed. 20 is a good value.)
-	fmt.Println(a.ProbablyPrime(20))
-
-	// Output:
-	// 1344719667586153181419716641724567886890850696275767987106294472017884974410332069524504824747437757
-	// false
-}
-
-// This example shows how to use big.Float to compute the square root of 2 with
-// a precision of 200 bits, and how to print the result as a decimal number.
-func Example_sqrt2() {
-	// We'll do computations with 200 bits of precision in the mantissa.
-	const prec = 200
-
-	// Compute the square root of 2 using Newton's Method. We start with
-	// an initial estimate for sqrt(2), and then iterate:
-	//     x_{n+1} = 1/2 * ( x_n + (2.0 / x_n) )
-
-	// Since Newton's Method doubles the number of correct digits at each
-	// iteration, we need at least log_2(prec) steps.
-	steps := int(math.Log2(prec))
-
-	// Initialize values we need for the computation.
-	two := new(big.Float).SetPrec(prec).SetInt64(2)
-	half := new(big.Float).SetPrec(prec).SetFloat64(0.5)
-
-	// Use 1 as the initial estimate.
-	x := new(big.Float).SetPrec(prec).SetInt64(1)
-
-	// We use t as a temporary variable. There's no need to set its precision
-	// since big.Float values with unset (== 0) precision automatically assume
-	// the largest precision of the arguments when used as the result (receiver)
-	// of a big.Float operation.
-	t := new(big.Float)
-
-	// Iterate.
-	for i := 0; i <= steps; i++ {
-		t.Quo(two, x)  // t = 2.0 / x_n
-		t.Add(x, t)    // t = x_n + (2.0 / x_n)
-		x.Mul(half, t) // x_{n+1} = 0.5 * t
-	}
-
-	// We can use the usual fmt.Printf verbs since big.Float implements fmt.Formatter
-	fmt.Printf("sqrt(2) = %.50f\n", x)
-
-	// Print the error between 2 and x*x.
-	t.Mul(x, x) // t = x*x
-	fmt.Printf("error = %e\n", t.Sub(two, t))
-
-	// Output:
-	// sqrt(2) = 1.41421356237309504880168872420969807856967187537695
-	// error = 0.000000e+00
-}
diff --git a/src/cmd/compile/internal/big/float.go b/src/cmd/compile/internal/big/float.go
deleted file mode 100644
index 4b8ad38..0000000
--- a/src/cmd/compile/internal/big/float.go
+++ /dev/null
@@ -1,1683 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements multi-precision floating-point numbers.
-// Like in the GNU MPFR library (http://www.mpfr.org/), operands
-// can be of mixed precision. Unlike MPFR, the rounding mode is
-// not specified with each operation, but with each operand. The
-// rounding mode of the result operand determines the rounding
-// mode of an operation. This is a from-scratch implementation.
-
-package big
-
-import (
-	"fmt"
-	"math"
-)
-
-const debugFloat = false // enable for debugging
-
-// A nonzero finite Float represents a multi-precision floating point number
-//
-//   sign × mantissa × 2**exponent
-//
-// with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp.
-// A Float may also be zero (+0, -0) or infinite (+Inf, -Inf).
-// All Floats are ordered, and the ordering of two Floats x and y
-// is defined by x.Cmp(y).
-//
-// Each Float value also has a precision, rounding mode, and accuracy.
-// The precision is the maximum number of mantissa bits available to
-// represent the value. The rounding mode specifies how a result should
-// be rounded to fit into the mantissa bits, and accuracy describes the
-// rounding error with respect to the exact result.
-//
-// Unless specified otherwise, all operations (including setters) that
-// specify a *Float variable for the result (usually via the receiver
-// with the exception of MantExp), round the numeric result according
-// to the precision and rounding mode of the result variable.
-//
-// If the provided result precision is 0 (see below), it is set to the
-// precision of the argument with the largest precision value before any
-// rounding takes place, and the rounding mode remains unchanged. Thus,
-// uninitialized Floats provided as result arguments will have their
-// precision set to a reasonable value determined by the operands and
-// their mode is the zero value for RoundingMode (ToNearestEven).
-//
-// By setting the desired precision to 24 or 53 and using matching rounding
-// mode (typically ToNearestEven), Float operations produce the same results
-// as the corresponding float32 or float64 IEEE-754 arithmetic for operands
-// that correspond to normal (i.e., not denormal) float32 or float64 numbers.
-// Exponent underflow and overflow lead to a 0 or an Infinity for different
-// values than IEEE-754 because Float exponents have a much larger range.
-//
-// The zero (uninitialized) value for a Float is ready to use and represents
-// the number +0.0 exactly, with precision 0 and rounding mode ToNearestEven.
-//
-type Float struct {
-	prec uint32
-	mode RoundingMode
-	acc  Accuracy
-	form form
-	neg  bool
-	mant nat
-	exp  int32
-}
-
-// An ErrNaN panic is raised by a Float operation that would lead to
-// a NaN under IEEE-754 rules. An ErrNaN implements the error interface.
-type ErrNaN struct {
-	msg string
-}
-
-func (err ErrNaN) Error() string {
-	return err.msg
-}
-
-// NewFloat allocates and returns a new Float set to x,
-// with precision 53 and rounding mode ToNearestEven.
-// NewFloat panics with ErrNaN if x is a NaN.
-func NewFloat(x float64) *Float {
-	if math.IsNaN(x) {
-		panic(ErrNaN{"NewFloat(NaN)"})
-	}
-	return new(Float).SetFloat64(x)
-}
-
-// Exponent and precision limits.
-const (
-	MaxExp  = math.MaxInt32  // largest supported exponent
-	MinExp  = math.MinInt32  // smallest supported exponent
-	MaxPrec = math.MaxUint32 // largest (theoretically) supported precision; likely memory-limited
-)
-
-// Internal representation: The mantissa bits x.mant of a nonzero finite
-// Float x are stored in a nat slice long enough to hold up to x.prec bits;
-// the slice may (but doesn't have to) be shorter if the mantissa contains
-// trailing 0 bits. x.mant is normalized if the msb of x.mant == 1 (i.e.,
-// the msb is shifted all the way "to the left"). Thus, if the mantissa has
-// trailing 0 bits or x.prec is not a multiple of the the Word size _W,
-// x.mant[0] has trailing zero bits. The msb of the mantissa corresponds
-// to the value 0.5; the exponent x.exp shifts the binary point as needed.
-//
-// A zero or non-finite Float x ignores x.mant and x.exp.
-//
-// x                 form      neg      mant         exp
-// ----------------------------------------------------------
-// ±0                zero      sign     -            -
-// 0 < |x| < +Inf    finite    sign     mantissa     exponent
-// ±Inf              inf       sign     -            -
-
-// A form value describes the internal representation.
-type form byte
-
-// The form value order is relevant - do not change!
-const (
-	zero form = iota
-	finite
-	inf
-)
-
-// RoundingMode determines how a Float value is rounded to the
-// desired precision. Rounding may change the Float value; the
-// rounding error is described by the Float's Accuracy.
-type RoundingMode byte
-
-// These constants define supported rounding modes.
-const (
-	ToNearestEven RoundingMode = iota // == IEEE 754-2008 roundTiesToEven
-	ToNearestAway                     // == IEEE 754-2008 roundTiesToAway
-	ToZero                            // == IEEE 754-2008 roundTowardZero
-	AwayFromZero                      // no IEEE 754-2008 equivalent
-	ToNegativeInf                     // == IEEE 754-2008 roundTowardNegative
-	ToPositiveInf                     // == IEEE 754-2008 roundTowardPositive
-)
-
-//go:generate stringer -type=RoundingMode
-
-// Accuracy describes the rounding error produced by the most recent
-// operation that generated a Float value, relative to the exact value.
-type Accuracy int8
-
-// Constants describing the Accuracy of a Float.
-const (
-	Below Accuracy = -1
-	Exact Accuracy = 0
-	Above Accuracy = +1
-)
-
-//go:generate stringer -type=Accuracy
-
-// SetPrec sets z's precision to prec and returns the (possibly) rounded
-// value of z. Rounding occurs according to z's rounding mode if the mantissa
-// cannot be represented in prec bits without loss of precision.
-// SetPrec(0) maps all finite values to ±0; infinite values remain unchanged.
-// If prec > MaxPrec, it is set to MaxPrec.
-func (z *Float) SetPrec(prec uint) *Float {
-	z.acc = Exact // optimistically assume no rounding is needed
-
-	// special case
-	if prec == 0 {
-		z.prec = 0
-		if z.form == finite {
-			// truncate z to 0
-			z.acc = makeAcc(z.neg)
-			z.form = zero
-		}
-		return z
-	}
-
-	// general case
-	if prec > MaxPrec {
-		prec = MaxPrec
-	}
-	old := z.prec
-	z.prec = uint32(prec)
-	if z.prec < old {
-		z.round(0)
-	}
-	return z
-}
-
-func makeAcc(above bool) Accuracy {
-	if above {
-		return Above
-	}
-	return Below
-}
-
-// SetMode sets z's rounding mode to mode and returns an exact z.
-// z remains unchanged otherwise.
-// z.SetMode(z.Mode()) is a cheap way to set z's accuracy to Exact.
-func (z *Float) SetMode(mode RoundingMode) *Float {
-	z.mode = mode
-	z.acc = Exact
-	return z
-}
-
-// Prec returns the mantissa precision of x in bits.
-// The result may be 0 for |x| == 0 and |x| == Inf.
-func (x *Float) Prec() uint {
-	return uint(x.prec)
-}
-
-// MinPrec returns the minimum precision required to represent x exactly
-// (i.e., the smallest prec before x.SetPrec(prec) would start rounding x).
-// The result is 0 for |x| == 0 and |x| == Inf.
-func (x *Float) MinPrec() uint {
-	if x.form != finite {
-		return 0
-	}
-	return uint(len(x.mant))*_W - x.mant.trailingZeroBits()
-}
-
-// Mode returns the rounding mode of x.
-func (x *Float) Mode() RoundingMode {
-	return x.mode
-}
-
-// Acc returns the accuracy of x produced by the most recent operation.
-func (x *Float) Acc() Accuracy {
-	return x.acc
-}
-
-// Sign returns:
-//
-//	-1 if x <   0
-//	 0 if x is ±0
-//	+1 if x >   0
-//
-func (x *Float) Sign() int {
-	if debugFloat {
-		x.validate()
-	}
-	if x.form == zero {
-		return 0
-	}
-	if x.neg {
-		return -1
-	}
-	return 1
-}
-
-// MantExp breaks x into its mantissa and exponent components
-// and returns the exponent. If a non-nil mant argument is
-// provided its value is set to the mantissa of x, with the
-// same precision and rounding mode as x. The components
-// satisfy x == mant × 2**exp, with 0.5 <= |mant| < 1.0.
-// Calling MantExp with a nil argument is an efficient way to
-// get the exponent of the receiver.
-//
-// Special cases are:
-//
-//	(  ±0).MantExp(mant) = 0, with mant set to   ±0
-//	(±Inf).MantExp(mant) = 0, with mant set to ±Inf
-//
-// x and mant may be the same in which case x is set to its
-// mantissa value.
-func (x *Float) MantExp(mant *Float) (exp int) {
-	if debugFloat {
-		x.validate()
-	}
-	if x.form == finite {
-		exp = int(x.exp)
-	}
-	if mant != nil {
-		mant.Copy(x)
-		if mant.form == finite {
-			mant.exp = 0
-		}
-	}
-	return
-}
-
-func (z *Float) setExpAndRound(exp int64, sbit uint) {
-	if exp < MinExp {
-		// underflow
-		z.acc = makeAcc(z.neg)
-		z.form = zero
-		return
-	}
-
-	if exp > MaxExp {
-		// overflow
-		z.acc = makeAcc(!z.neg)
-		z.form = inf
-		return
-	}
-
-	z.form = finite
-	z.exp = int32(exp)
-	z.round(sbit)
-}
-
-// SetMantExp sets z to mant × 2**exp and and returns z.
-// The result z has the same precision and rounding mode
-// as mant. SetMantExp is an inverse of MantExp but does
-// not require 0.5 <= |mant| < 1.0. Specifically:
-//
-//	mant := new(Float)
-//	new(Float).SetMantExp(mant, x.MantExp(mant)).Cmp(x) == 0
-//
-// Special cases are:
-//
-//	z.SetMantExp(  ±0, exp) =   ±0
-//	z.SetMantExp(±Inf, exp) = ±Inf
-//
-// z and mant may be the same in which case z's exponent
-// is set to exp.
-func (z *Float) SetMantExp(mant *Float, exp int) *Float {
-	if debugFloat {
-		z.validate()
-		mant.validate()
-	}
-	z.Copy(mant)
-	if z.form != finite {
-		return z
-	}
-	z.setExpAndRound(int64(z.exp)+int64(exp), 0)
-	return z
-}
-
-// Signbit returns true if x is negative or negative zero.
-func (x *Float) Signbit() bool {
-	return x.neg
-}
-
-// IsInf reports whether x is +Inf or -Inf.
-func (x *Float) IsInf() bool {
-	return x.form == inf
-}
-
-// IsInt reports whether x is an integer.
-// ±Inf values are not integers.
-func (x *Float) IsInt() bool {
-	if debugFloat {
-		x.validate()
-	}
-	// special cases
-	if x.form != finite {
-		return x.form == zero
-	}
-	// x.form == finite
-	if x.exp <= 0 {
-		return false
-	}
-	// x.exp > 0
-	return x.prec <= uint32(x.exp) || x.MinPrec() <= uint(x.exp) // not enough bits for fractional mantissa
-}
-
-// debugging support
-func (x *Float) validate() {
-	if !debugFloat {
-		// avoid performance bugs
-		panic("validate called but debugFloat is not set")
-	}
-	if x.form != finite {
-		return
-	}
-	m := len(x.mant)
-	if m == 0 {
-		panic("nonzero finite number with empty mantissa")
-	}
-	const msb = 1 << (_W - 1)
-	if x.mant[m-1]&msb == 0 {
-		panic(fmt.Sprintf("msb not set in last word %#x of %s", x.mant[m-1], x.Text('p', 0)))
-	}
-	if x.prec == 0 {
-		panic("zero precision finite number")
-	}
-}
-
-// round rounds z according to z.mode to z.prec bits and sets z.acc accordingly.
-// sbit must be 0 or 1 and summarizes any "sticky bit" information one might
-// have before calling round. z's mantissa must be normalized (with the msb set)
-// or empty.
-//
-// CAUTION: The rounding modes ToNegativeInf, ToPositiveInf are affected by the
-// sign of z. For correct rounding, the sign of z must be set correctly before
-// calling round.
-func (z *Float) round(sbit uint) {
-	if debugFloat {
-		z.validate()
-	}
-
-	z.acc = Exact
-	if z.form != finite {
-		// ±0 or ±Inf => nothing left to do
-		return
-	}
-	// z.form == finite && len(z.mant) > 0
-	// m > 0 implies z.prec > 0 (checked by validate)
-
-	m := uint32(len(z.mant)) // present mantissa length in words
-	bits := m * _W           // present mantissa bits; bits > 0
-	if bits <= z.prec {
-		// mantissa fits => nothing to do
-		return
-	}
-	// bits > z.prec
-
-	// Rounding is based on two bits: the rounding bit (rbit) and the
-	// sticky bit (sbit). The rbit is the bit immediately before the
-	// z.prec leading mantissa bits (the "0.5"). The sbit is set if any
-	// of the bits before the rbit are set (the "0.25", "0.125", etc.):
-	//
-	//   rbit  sbit  => "fractional part"
-	//
-	//   0     0        == 0
-	//   0     1        >  0  , < 0.5
-	//   1     0        == 0.5
-	//   1     1        >  0.5, < 1.0
-
-	// bits > z.prec: mantissa too large => round
-	r := uint(bits - z.prec - 1) // rounding bit position; r >= 0
-	rbit := z.mant.bit(r) & 1    // rounding bit; be safe and ensure it's a single bit
-	if sbit == 0 {
-		// TODO(gri) if rbit != 0 we don't need to compute sbit for some rounding modes (optimization)
-		sbit = z.mant.sticky(r)
-	}
-	sbit &= 1 // be safe and ensure it's a single bit
-
-	// cut off extra words
-	n := (z.prec + (_W - 1)) / _W // mantissa length in words for desired precision
-	if m > n {
-		copy(z.mant, z.mant[m-n:]) // move n last words to front
-		z.mant = z.mant[:n]
-	}
-
-	// determine number of trailing zero bits (ntz) and compute lsb mask of mantissa's least-significant word
-	ntz := n*_W - z.prec // 0 <= ntz < _W
-	lsb := Word(1) << ntz
-
-	// round if result is inexact
-	if rbit|sbit != 0 {
-		// Make rounding decision: The result mantissa is truncated ("rounded down")
-		// by default. Decide if we need to increment, or "round up", the (unsigned)
-		// mantissa.
-		inc := false
-		switch z.mode {
-		case ToNegativeInf:
-			inc = z.neg
-		case ToZero:
-			// nothing to do
-		case ToNearestEven:
-			inc = rbit != 0 && (sbit != 0 || z.mant[0]&lsb != 0)
-		case ToNearestAway:
-			inc = rbit != 0
-		case AwayFromZero:
-			inc = true
-		case ToPositiveInf:
-			inc = !z.neg
-		default:
-			panic("unreachable")
-		}
-
-		// A positive result (!z.neg) is Above the exact result if we increment,
-		// and it's Below if we truncate (Exact results require no rounding).
-		// For a negative result (z.neg) it is exactly the opposite.
-		z.acc = makeAcc(inc != z.neg)
-
-		if inc {
-			// add 1 to mantissa
-			if addVW(z.mant, z.mant, lsb) != 0 {
-				// mantissa overflow => adjust exponent
-				if z.exp >= MaxExp {
-					// exponent overflow
-					z.form = inf
-					return
-				}
-				z.exp++
-				// adjust mantissa: divide by 2 to compensate for exponent adjustment
-				shrVU(z.mant, z.mant, 1)
-				// set msb == carry == 1 from the mantissa overflow above
-				const msb = 1 << (_W - 1)
-				z.mant[n-1] |= msb
-			}
-		}
-	}
-
-	// zero out trailing bits in least-significant word
-	z.mant[0] &^= lsb - 1
-
-	if debugFloat {
-		z.validate()
-	}
-}
-
-func (z *Float) setBits64(neg bool, x uint64) *Float {
-	if z.prec == 0 {
-		z.prec = 64
-	}
-	z.acc = Exact
-	z.neg = neg
-	if x == 0 {
-		z.form = zero
-		return z
-	}
-	// x != 0
-	z.form = finite
-	s := nlz64(x)
-	z.mant = z.mant.setUint64(x << s)
-	z.exp = int32(64 - s) // always fits
-	if z.prec < 64 {
-		z.round(0)
-	}
-	return z
-}
-
-// SetUint64 sets z to the (possibly rounded) value of x and returns z.
-// If z's precision is 0, it is changed to 64 (and rounding will have
-// no effect).
-func (z *Float) SetUint64(x uint64) *Float {
-	return z.setBits64(false, x)
-}
-
-// SetInt64 sets z to the (possibly rounded) value of x and returns z.
-// If z's precision is 0, it is changed to 64 (and rounding will have
-// no effect).
-func (z *Float) SetInt64(x int64) *Float {
-	u := x
-	if u < 0 {
-		u = -u
-	}
-	// We cannot simply call z.SetUint64(uint64(u)) and change
-	// the sign afterwards because the sign affects rounding.
-	return z.setBits64(x < 0, uint64(u))
-}
-
-// SetFloat64 sets z to the (possibly rounded) value of x and returns z.
-// If z's precision is 0, it is changed to 53 (and rounding will have
-// no effect). SetFloat64 panics with ErrNaN if x is a NaN.
-func (z *Float) SetFloat64(x float64) *Float {
-	if z.prec == 0 {
-		z.prec = 53
-	}
-	if math.IsNaN(x) {
-		panic(ErrNaN{"Float.SetFloat64(NaN)"})
-	}
-	z.acc = Exact
-	z.neg = math.Signbit(x) // handle -0, -Inf correctly
-	if x == 0 {
-		z.form = zero
-		return z
-	}
-	if math.IsInf(x, 0) {
-		z.form = inf
-		return z
-	}
-	// normalized x != 0
-	z.form = finite
-	fmant, exp := math.Frexp(x) // get normalized mantissa
-	z.mant = z.mant.setUint64(1<<63 | math.Float64bits(fmant)<<11)
-	z.exp = int32(exp) // always fits
-	if z.prec < 53 {
-		z.round(0)
-	}
-	return z
-}
-
-// fnorm normalizes mantissa m by shifting it to the left
-// such that the msb of the most-significant word (msw) is 1.
-// It returns the shift amount. It assumes that len(m) != 0.
-func fnorm(m nat) int64 {
-	if debugFloat && (len(m) == 0 || m[len(m)-1] == 0) {
-		panic("msw of mantissa is 0")
-	}
-	s := nlz(m[len(m)-1])
-	if s > 0 {
-		c := shlVU(m, m, s)
-		if debugFloat && c != 0 {
-			panic("nlz or shlVU incorrect")
-		}
-	}
-	return int64(s)
-}
-
-// SetInt sets z to the (possibly rounded) value of x and returns z.
-// If z's precision is 0, it is changed to the larger of x.BitLen()
-// or 64 (and rounding will have no effect).
-func (z *Float) SetInt(x *Int) *Float {
-	// TODO(gri) can be more efficient if z.prec > 0
-	// but small compared to the size of x, or if there
-	// are many trailing 0's.
-	bits := uint32(x.BitLen())
-	if z.prec == 0 {
-		z.prec = umax32(bits, 64)
-	}
-	z.acc = Exact
-	z.neg = x.neg
-	if len(x.abs) == 0 {
-		z.form = zero
-		return z
-	}
-	// x != 0
-	z.mant = z.mant.set(x.abs)
-	fnorm(z.mant)
-	z.setExpAndRound(int64(bits), 0)
-	return z
-}
-
-// SetRat sets z to the (possibly rounded) value of x and returns z.
-// If z's precision is 0, it is changed to the largest of a.BitLen(),
-// b.BitLen(), or 64; with x = a/b.
-func (z *Float) SetRat(x *Rat) *Float {
-	if x.IsInt() {
-		return z.SetInt(x.Num())
-	}
-	var a, b Float
-	a.SetInt(x.Num())
-	b.SetInt(x.Denom())
-	if z.prec == 0 {
-		z.prec = umax32(a.prec, b.prec)
-	}
-	return z.Quo(&a, &b)
-}
-
-// SetInf sets z to the infinite Float -Inf if signbit is
-// set, or +Inf if signbit is not set, and returns z. The
-// precision of z is unchanged and the result is always
-// Exact.
-func (z *Float) SetInf(signbit bool) *Float {
-	z.acc = Exact
-	z.form = inf
-	z.neg = signbit
-	return z
-}
-
-// Set sets z to the (possibly rounded) value of x and returns z.
-// If z's precision is 0, it is changed to the precision of x
-// before setting z (and rounding will have no effect).
-// Rounding is performed according to z's precision and rounding
-// mode; and z's accuracy reports the result error relative to the
-// exact (not rounded) result.
-func (z *Float) Set(x *Float) *Float {
-	if debugFloat {
-		x.validate()
-	}
-	z.acc = Exact
-	if z != x {
-		z.form = x.form
-		z.neg = x.neg
-		if x.form == finite {
-			z.exp = x.exp
-			z.mant = z.mant.set(x.mant)
-		}
-		if z.prec == 0 {
-			z.prec = x.prec
-		} else if z.prec < x.prec {
-			z.round(0)
-		}
-	}
-	return z
-}
-
-// Copy sets z to x, with the same precision, rounding mode, and
-// accuracy as x, and returns z. x is not changed even if z and
-// x are the same.
-func (z *Float) Copy(x *Float) *Float {
-	if debugFloat {
-		x.validate()
-	}
-	if z != x {
-		z.prec = x.prec
-		z.mode = x.mode
-		z.acc = x.acc
-		z.form = x.form
-		z.neg = x.neg
-		if z.form == finite {
-			z.mant = z.mant.set(x.mant)
-			z.exp = x.exp
-		}
-	}
-	return z
-}
-
-// msb32 returns the 32 most significant bits of x.
-func msb32(x nat) uint32 {
-	i := len(x) - 1
-	if i < 0 {
-		return 0
-	}
-	if debugFloat && x[i]&(1<<(_W-1)) == 0 {
-		panic("x not normalized")
-	}
-	switch _W {
-	case 32:
-		return uint32(x[i])
-	case 64:
-		return uint32(x[i] >> 32)
-	}
-	panic("unreachable")
-}
-
-// msb64 returns the 64 most significant bits of x.
-func msb64(x nat) uint64 {
-	i := len(x) - 1
-	if i < 0 {
-		return 0
-	}
-	if debugFloat && x[i]&(1<<(_W-1)) == 0 {
-		panic("x not normalized")
-	}
-	switch _W {
-	case 32:
-		v := uint64(x[i]) << 32
-		if i > 0 {
-			v |= uint64(x[i-1])
-		}
-		return v
-	case 64:
-		return uint64(x[i])
-	}
-	panic("unreachable")
-}
-
-// Uint64 returns the unsigned integer resulting from truncating x
-// towards zero. If 0 <= x <= math.MaxUint64, the result is Exact
-// if x is an integer and Below otherwise.
-// The result is (0, Above) for x < 0, and (math.MaxUint64, Below)
-// for x > math.MaxUint64.
-func (x *Float) Uint64() (uint64, Accuracy) {
-	if debugFloat {
-		x.validate()
-	}
-
-	switch x.form {
-	case finite:
-		if x.neg {
-			return 0, Above
-		}
-		// 0 < x < +Inf
-		if x.exp <= 0 {
-			// 0 < x < 1
-			return 0, Below
-		}
-		// 1 <= x < Inf
-		if x.exp <= 64 {
-			// u = trunc(x) fits into a uint64
-			u := msb64(x.mant) >> (64 - uint32(x.exp))
-			if x.MinPrec() <= 64 {
-				return u, Exact
-			}
-			return u, Below // x truncated
-		}
-		// x too large
-		return math.MaxUint64, Below
-
-	case zero:
-		return 0, Exact
-
-	case inf:
-		if x.neg {
-			return 0, Above
-		}
-		return math.MaxUint64, Below
-	}
-
-	panic("unreachable")
-}
-
-// Int64 returns the integer resulting from truncating x towards zero.
-// If math.MinInt64 <= x <= math.MaxInt64, the result is Exact if x is
-// an integer, and Above (x < 0) or Below (x > 0) otherwise.
-// The result is (math.MinInt64, Above) for x < math.MinInt64,
-// and (math.MaxInt64, Below) for x > math.MaxInt64.
-func (x *Float) Int64() (int64, Accuracy) {
-	if debugFloat {
-		x.validate()
-	}
-
-	switch x.form {
-	case finite:
-		// 0 < |x| < +Inf
-		acc := makeAcc(x.neg)
-		if x.exp <= 0 {
-			// 0 < |x| < 1
-			return 0, acc
-		}
-		// x.exp > 0
-
-		// 1 <= |x| < +Inf
-		if x.exp <= 63 {
-			// i = trunc(x) fits into an int64 (excluding math.MinInt64)
-			i := int64(msb64(x.mant) >> (64 - uint32(x.exp)))
-			if x.neg {
-				i = -i
-			}
-			if x.MinPrec() <= uint(x.exp) {
-				return i, Exact
-			}
-			return i, acc // x truncated
-		}
-		if x.neg {
-			// check for special case x == math.MinInt64 (i.e., x == -(0.5 << 64))
-			if x.exp == 64 && x.MinPrec() == 1 {
-				acc = Exact
-			}
-			return math.MinInt64, acc
-		}
-		// x too large
-		return math.MaxInt64, Below
-
-	case zero:
-		return 0, Exact
-
-	case inf:
-		if x.neg {
-			return math.MinInt64, Above
-		}
-		return math.MaxInt64, Below
-	}
-
-	panic("unreachable")
-}
-
-// Float32 returns the float32 value nearest to x. If x is too small to be
-// represented by a float32 (|x| < math.SmallestNonzeroFloat32), the result
-// is (0, Below) or (-0, Above), respectively, depending on the sign of x.
-// If x is too large to be represented by a float32 (|x| > math.MaxFloat32),
-// the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x.
-func (x *Float) Float32() (float32, Accuracy) {
-	if debugFloat {
-		x.validate()
-	}
-
-	switch x.form {
-	case finite:
-		// 0 < |x| < +Inf
-
-		const (
-			fbits = 32                //        float size
-			mbits = 23                //        mantissa size (excluding implicit msb)
-			ebits = fbits - mbits - 1 //     8  exponent size
-			bias  = 1<<(ebits-1) - 1  //   127  exponent bias
-			dmin  = 1 - bias - mbits  //  -149  smallest unbiased exponent (denormal)
-			emin  = 1 - bias          //  -126  smallest unbiased exponent (normal)
-			emax  = bias              //   127  largest unbiased exponent (normal)
-		)
-
-		// Float mantissa m is 0.5 <= m < 1.0; compute exponent e for float32 mantissa.
-		e := x.exp - 1 // exponent for normal mantissa m with 1.0 <= m < 2.0
-
-		// Compute precision p for float32 mantissa.
-		// If the exponent is too small, we have a denormal number before
-		// rounding and fewer than p mantissa bits of precision available
-		// (the exponent remains fixed but the mantissa gets shifted right).
-		p := mbits + 1 // precision of normal float
-		if e < emin {
-			// recompute precision
-			p = mbits + 1 - emin + int(e)
-			// If p == 0, the mantissa of x is shifted so much to the right
-			// that its msb falls immediately to the right of the float32
-			// mantissa space. In other words, if the smallest denormal is
-			// considered "1.0", for p == 0, the mantissa value m is >= 0.5.
-			// If m > 0.5, it is rounded up to 1.0; i.e., the smallest denormal.
-			// If m == 0.5, it is rounded down to even, i.e., 0.0.
-			// If p < 0, the mantissa value m is <= "0.25" which is never rounded up.
-			if p < 0 /* m <= 0.25 */ || p == 0 && x.mant.sticky(uint(len(x.mant))*_W-1) == 0 /* m == 0.5 */ {
-				// underflow to ±0
-				if x.neg {
-					var z float32
-					return -z, Above
-				}
-				return 0.0, Below
-			}
-			// otherwise, round up
-			// We handle p == 0 explicitly because it's easy and because
-			// Float.round doesn't support rounding to 0 bits of precision.
-			if p == 0 {
-				if x.neg {
-					return -math.SmallestNonzeroFloat32, Below
-				}
-				return math.SmallestNonzeroFloat32, Above
-			}
-		}
-		// p > 0
-
-		// round
-		var r Float
-		r.prec = uint32(p)
-		r.Set(x)
-		e = r.exp - 1
-
-		// Rounding may have caused r to overflow to ±Inf
-		// (rounding never causes underflows to 0).
-		// If the exponent is too large, also overflow to ±Inf.
-		if r.form == inf || e > emax {
-			// overflow
-			if x.neg {
-				return float32(math.Inf(-1)), Below
-			}
-			return float32(math.Inf(+1)), Above
-		}
-		// e <= emax
-
-		// Determine sign, biased exponent, and mantissa.
-		var sign, bexp, mant uint32
-		if x.neg {
-			sign = 1 << (fbits - 1)
-		}
-
-		// Rounding may have caused a denormal number to
-		// become normal. Check again.
-		if e < emin {
-			// denormal number: recompute precision
-			// Since rounding may have at best increased precision
-			// and we have eliminated p <= 0 early, we know p > 0.
-			// bexp == 0 for denormals
-			p = mbits + 1 - emin + int(e)
-			mant = msb32(r.mant) >> uint(fbits-p)
-		} else {
-			// normal number: emin <= e <= emax
-			bexp = uint32(e+bias) << mbits
-			mant = msb32(r.mant) >> ebits & (1<<mbits - 1) // cut off msb (implicit 1 bit)
-		}
-
-		return math.Float32frombits(sign | bexp | mant), r.acc
-
-	case zero:
-		if x.neg {
-			var z float32
-			return -z, Exact
-		}
-		return 0.0, Exact
-
-	case inf:
-		if x.neg {
-			return float32(math.Inf(-1)), Exact
-		}
-		return float32(math.Inf(+1)), Exact
-	}
-
-	panic("unreachable")
-}
-
-// Float64 returns the float64 value nearest to x. If x is too small to be
-// represented by a float64 (|x| < math.SmallestNonzeroFloat64), the result
-// is (0, Below) or (-0, Above), respectively, depending on the sign of x.
-// If x is too large to be represented by a float64 (|x| > math.MaxFloat64),
-// the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x.
-func (x *Float) Float64() (float64, Accuracy) {
-	if debugFloat {
-		x.validate()
-	}
-
-	switch x.form {
-	case finite:
-		// 0 < |x| < +Inf
-
-		const (
-			fbits = 64                //        float size
-			mbits = 52                //        mantissa size (excluding implicit msb)
-			ebits = fbits - mbits - 1 //    11  exponent size
-			bias  = 1<<(ebits-1) - 1  //  1023  exponent bias
-			dmin  = 1 - bias - mbits  // -1074  smallest unbiased exponent (denormal)
-			emin  = 1 - bias          // -1022  smallest unbiased exponent (normal)
-			emax  = bias              //  1023  largest unbiased exponent (normal)
-		)
-
-		// Float mantissa m is 0.5 <= m < 1.0; compute exponent e for float64 mantissa.
-		e := x.exp - 1 // exponent for normal mantissa m with 1.0 <= m < 2.0
-
-		// Compute precision p for float64 mantissa.
-		// If the exponent is too small, we have a denormal number before
-		// rounding and fewer than p mantissa bits of precision available
-		// (the exponent remains fixed but the mantissa gets shifted right).
-		p := mbits + 1 // precision of normal float
-		if e < emin {
-			// recompute precision
-			p = mbits + 1 - emin + int(e)
-			// If p == 0, the mantissa of x is shifted so much to the right
-			// that its msb falls immediately to the right of the float64
-			// mantissa space. In other words, if the smallest denormal is
-			// considered "1.0", for p == 0, the mantissa value m is >= 0.5.
-			// If m > 0.5, it is rounded up to 1.0; i.e., the smallest denormal.
-			// If m == 0.5, it is rounded down to even, i.e., 0.0.
-			// If p < 0, the mantissa value m is <= "0.25" which is never rounded up.
-			if p < 0 /* m <= 0.25 */ || p == 0 && x.mant.sticky(uint(len(x.mant))*_W-1) == 0 /* m == 0.5 */ {
-				// underflow to ±0
-				if x.neg {
-					var z float64
-					return -z, Above
-				}
-				return 0.0, Below
-			}
-			// otherwise, round up
-			// We handle p == 0 explicitly because it's easy and because
-			// Float.round doesn't support rounding to 0 bits of precision.
-			if p == 0 {
-				if x.neg {
-					return -math.SmallestNonzeroFloat64, Below
-				}
-				return math.SmallestNonzeroFloat64, Above
-			}
-		}
-		// p > 0
-
-		// round
-		var r Float
-		r.prec = uint32(p)
-		r.Set(x)
-		e = r.exp - 1
-
-		// Rounding may have caused r to overflow to ±Inf
-		// (rounding never causes underflows to 0).
-		// If the exponent is too large, also overflow to ±Inf.
-		if r.form == inf || e > emax {
-			// overflow
-			if x.neg {
-				return float64(math.Inf(-1)), Below
-			}
-			return float64(math.Inf(+1)), Above
-		}
-		// e <= emax
-
-		// Determine sign, biased exponent, and mantissa.
-		var sign, bexp, mant uint64
-		if x.neg {
-			sign = 1 << (fbits - 1)
-		}
-
-		// Rounding may have caused a denormal number to
-		// become normal. Check again.
-		if e < emin {
-			// denormal number: recompute precision
-			// Since rounding may have at best increased precision
-			// and we have eliminated p <= 0 early, we know p > 0.
-			// bexp == 0 for denormals
-			p = mbits + 1 - emin + int(e)
-			mant = msb64(r.mant) >> uint(fbits-p)
-		} else {
-			// normal number: emin <= e <= emax
-			bexp = uint64(e+bias) << mbits
-			mant = msb64(r.mant) >> ebits & (1<<mbits - 1) // cut off msb (implicit 1 bit)
-		}
-
-		return math.Float64frombits(sign | bexp | mant), r.acc
-
-	case zero:
-		if x.neg {
-			var z float64
-			return -z, Exact
-		}
-		return 0.0, Exact
-
-	case inf:
-		if x.neg {
-			return math.Inf(-1), Exact
-		}
-		return math.Inf(+1), Exact
-	}
-
-	panic("unreachable")
-}
-
-// Int returns the result of truncating x towards zero;
-// or nil if x is an infinity.
-// The result is Exact if x.IsInt(); otherwise it is Below
-// for x > 0, and Above for x < 0.
-// If a non-nil *Int argument z is provided, Int stores
-// the result in z instead of allocating a new Int.
-func (x *Float) Int(z *Int) (*Int, Accuracy) {
-	if debugFloat {
-		x.validate()
-	}
-
-	if z == nil && x.form <= finite {
-		z = new(Int)
-	}
-
-	switch x.form {
-	case finite:
-		// 0 < |x| < +Inf
-		acc := makeAcc(x.neg)
-		if x.exp <= 0 {
-			// 0 < |x| < 1
-			return z.SetInt64(0), acc
-		}
-		// x.exp > 0
-
-		// 1 <= |x| < +Inf
-		// determine minimum required precision for x
-		allBits := uint(len(x.mant)) * _W
-		exp := uint(x.exp)
-		if x.MinPrec() <= exp {
-			acc = Exact
-		}
-		// shift mantissa as needed
-		if z == nil {
-			z = new(Int)
-		}
-		z.neg = x.neg
-		switch {
-		case exp > allBits:
-			z.abs = z.abs.shl(x.mant, exp-allBits)
-		default:
-			z.abs = z.abs.set(x.mant)
-		case exp < allBits:
-			z.abs = z.abs.shr(x.mant, allBits-exp)
-		}
-		return z, acc
-
-	case zero:
-		return z.SetInt64(0), Exact
-
-	case inf:
-		return nil, makeAcc(x.neg)
-	}
-
-	panic("unreachable")
-}
-
-// Rat returns the rational number corresponding to x;
-// or nil if x is an infinity.
-// The result is Exact if x is not an Inf.
-// If a non-nil *Rat argument z is provided, Rat stores
-// the result in z instead of allocating a new Rat.
-func (x *Float) Rat(z *Rat) (*Rat, Accuracy) {
-	if debugFloat {
-		x.validate()
-	}
-
-	if z == nil && x.form <= finite {
-		z = new(Rat)
-	}
-
-	switch x.form {
-	case finite:
-		// 0 < |x| < +Inf
-		allBits := int32(len(x.mant)) * _W
-		// build up numerator and denominator
-		z.a.neg = x.neg
-		switch {
-		case x.exp > allBits:
-			z.a.abs = z.a.abs.shl(x.mant, uint(x.exp-allBits))
-			z.b.abs = z.b.abs[:0] // == 1 (see Rat)
-			// z already in normal form
-		default:
-			z.a.abs = z.a.abs.set(x.mant)
-			z.b.abs = z.b.abs[:0] // == 1 (see Rat)
-			// z already in normal form
-		case x.exp < allBits:
-			z.a.abs = z.a.abs.set(x.mant)
-			t := z.b.abs.setUint64(1)
-			z.b.abs = t.shl(t, uint(allBits-x.exp))
-			z.norm()
-		}
-		return z, Exact
-
-	case zero:
-		return z.SetInt64(0), Exact
-
-	case inf:
-		return nil, makeAcc(x.neg)
-	}
-
-	panic("unreachable")
-}
-
-// Abs sets z to the (possibly rounded) value |x| (the absolute value of x)
-// and returns z.
-func (z *Float) Abs(x *Float) *Float {
-	z.Set(x)
-	z.neg = false
-	return z
-}
-
-// Neg sets z to the (possibly rounded) value of x with its sign negated,
-// and returns z.
-func (z *Float) Neg(x *Float) *Float {
-	z.Set(x)
-	z.neg = !z.neg
-	return z
-}
-
-func validateBinaryOperands(x, y *Float) {
-	if !debugFloat {
-		// avoid performance bugs
-		panic("validateBinaryOperands called but debugFloat is not set")
-	}
-	if len(x.mant) == 0 {
-		panic("empty mantissa for x")
-	}
-	if len(y.mant) == 0 {
-		panic("empty mantissa for y")
-	}
-}
-
-// z = x + y, ignoring signs of x and y for the addition
-// but using the sign of z for rounding the result.
-// x and y must have a non-empty mantissa and valid exponent.
-func (z *Float) uadd(x, y *Float) {
-	// Note: This implementation requires 2 shifts most of the
-	// time. It is also inefficient if exponents or precisions
-	// differ by wide margins. The following article describes
-	// an efficient (but much more complicated) implementation
-	// compatible with the internal representation used here:
-	//
-	// Vincent Lefèvre: "The Generic Multiple-Precision Floating-
-	// Point Addition With Exact Rounding (as in the MPFR Library)"
-	// http://www.vinc17.net/research/papers/rnc6.pdf
-
-	if debugFloat {
-		validateBinaryOperands(x, y)
-	}
-
-	// compute exponents ex, ey for mantissa with "binary point"
-	// on the right (mantissa.0) - use int64 to avoid overflow
-	ex := int64(x.exp) - int64(len(x.mant))*_W
-	ey := int64(y.exp) - int64(len(y.mant))*_W
-
-	// TODO(gri) having a combined add-and-shift primitive
-	//           could make this code significantly faster
-	switch {
-	case ex < ey:
-		// cannot re-use z.mant w/o testing for aliasing
-		t := nat(nil).shl(y.mant, uint(ey-ex))
-		z.mant = z.mant.add(x.mant, t)
-	default:
-		// ex == ey, no shift needed
-		z.mant = z.mant.add(x.mant, y.mant)
-	case ex > ey:
-		// cannot re-use z.mant w/o testing for aliasing
-		t := nat(nil).shl(x.mant, uint(ex-ey))
-		z.mant = z.mant.add(t, y.mant)
-		ex = ey
-	}
-	// len(z.mant) > 0
-
-	z.setExpAndRound(ex+int64(len(z.mant))*_W-fnorm(z.mant), 0)
-}
-
-// z = x - y for |x| > |y|, ignoring signs of x and y for the subtraction
-// but using the sign of z for rounding the result.
-// x and y must have a non-empty mantissa and valid exponent.
-func (z *Float) usub(x, y *Float) {
-	// This code is symmetric to uadd.
-	// We have not factored the common code out because
-	// eventually uadd (and usub) should be optimized
-	// by special-casing, and the code will diverge.
-
-	if debugFloat {
-		validateBinaryOperands(x, y)
-	}
-
-	ex := int64(x.exp) - int64(len(x.mant))*_W
-	ey := int64(y.exp) - int64(len(y.mant))*_W
-
-	switch {
-	case ex < ey:
-		// cannot re-use z.mant w/o testing for aliasing
-		t := nat(nil).shl(y.mant, uint(ey-ex))
-		z.mant = t.sub(x.mant, t)
-	default:
-		// ex == ey, no shift needed
-		z.mant = z.mant.sub(x.mant, y.mant)
-	case ex > ey:
-		// cannot re-use z.mant w/o testing for aliasing
-		t := nat(nil).shl(x.mant, uint(ex-ey))
-		z.mant = t.sub(t, y.mant)
-		ex = ey
-	}
-
-	// operands may have canceled each other out
-	if len(z.mant) == 0 {
-		z.acc = Exact
-		z.form = zero
-		z.neg = false
-		return
-	}
-	// len(z.mant) > 0
-
-	z.setExpAndRound(ex+int64(len(z.mant))*_W-fnorm(z.mant), 0)
-}
-
-// z = x * y, ignoring signs of x and y for the multiplication
-// but using the sign of z for rounding the result.
-// x and y must have a non-empty mantissa and valid exponent.
-func (z *Float) umul(x, y *Float) {
-	if debugFloat {
-		validateBinaryOperands(x, y)
-	}
-
-	// Note: This is doing too much work if the precision
-	// of z is less than the sum of the precisions of x
-	// and y which is often the case (e.g., if all floats
-	// have the same precision).
-	// TODO(gri) Optimize this for the common case.
-
-	e := int64(x.exp) + int64(y.exp)
-	z.mant = z.mant.mul(x.mant, y.mant)
-
-	z.setExpAndRound(e-fnorm(z.mant), 0)
-}
-
-// z = x / y, ignoring signs of x and y for the division
-// but using the sign of z for rounding the result.
-// x and y must have a non-empty mantissa and valid exponent.
-func (z *Float) uquo(x, y *Float) {
-	if debugFloat {
-		validateBinaryOperands(x, y)
-	}
-
-	// mantissa length in words for desired result precision + 1
-	// (at least one extra bit so we get the rounding bit after
-	// the division)
-	n := int(z.prec/_W) + 1
-
-	// compute adjusted x.mant such that we get enough result precision
-	xadj := x.mant
-	if d := n - len(x.mant) + len(y.mant); d > 0 {
-		// d extra words needed => add d "0 digits" to x
-		xadj = make(nat, len(x.mant)+d)
-		copy(xadj[d:], x.mant)
-	}
-	// TODO(gri): If we have too many digits (d < 0), we should be able
-	// to shorten x for faster division. But we must be extra careful
-	// with rounding in that case.
-
-	// Compute d before division since there may be aliasing of x.mant
-	// (via xadj) or y.mant with z.mant.
-	d := len(xadj) - len(y.mant)
-
-	// divide
-	var r nat
-	z.mant, r = z.mant.div(nil, xadj, y.mant)
-	e := int64(x.exp) - int64(y.exp) - int64(d-len(z.mant))*_W
-
-	// The result is long enough to include (at least) the rounding bit.
-	// If there's a non-zero remainder, the corresponding fractional part
-	// (if it were computed), would have a non-zero sticky bit (if it were
-	// zero, it couldn't have a non-zero remainder).
-	var sbit uint
-	if len(r) > 0 {
-		sbit = 1
-	}
-
-	z.setExpAndRound(e-fnorm(z.mant), sbit)
-}
-
-// ucmp returns -1, 0, or +1, depending on whether
-// |x| < |y|, |x| == |y|, or |x| > |y|.
-// x and y must have a non-empty mantissa and valid exponent.
-func (x *Float) ucmp(y *Float) int {
-	if debugFloat {
-		validateBinaryOperands(x, y)
-	}
-
-	switch {
-	case x.exp < y.exp:
-		return -1
-	case x.exp > y.exp:
-		return +1
-	}
-	// x.exp == y.exp
-
-	// compare mantissas
-	i := len(x.mant)
-	j := len(y.mant)
-	for i > 0 || j > 0 {
-		var xm, ym Word
-		if i > 0 {
-			i--
-			xm = x.mant[i]
-		}
-		if j > 0 {
-			j--
-			ym = y.mant[j]
-		}
-		switch {
-		case xm < ym:
-			return -1
-		case xm > ym:
-			return +1
-		}
-	}
-
-	return 0
-}
-
-// Handling of sign bit as defined by IEEE 754-2008, section 6.3:
-//
-// When neither the inputs nor result are NaN, the sign of a product or
-// quotient is the exclusive OR of the operands’ signs; the sign of a sum,
-// or of a difference x−y regarded as a sum x+(−y), differs from at most
-// one of the addends’ signs; and the sign of the result of conversions,
-// the quantize operation, the roundToIntegral operations, and the
-// roundToIntegralExact (see 5.3.1) is the sign of the first or only operand.
-// These rules shall apply even when operands or results are zero or infinite.
-//
-// When the sum of two operands with opposite signs (or the difference of
-// two operands with like signs) is exactly zero, the sign of that sum (or
-// difference) shall be +0 in all rounding-direction attributes except
-// roundTowardNegative; under that attribute, the sign of an exact zero
-// sum (or difference) shall be −0. However, x+x = x−(−x) retains the same
-// sign as x even when x is zero.
-//
-// See also: https://play.golang.org/p/RtH3UCt5IH
-
-// Add sets z to the rounded sum x+y and returns z. If z's precision is 0,
-// it is changed to the larger of x's or y's precision before the operation.
-// Rounding is performed according to z's precision and rounding mode; and
-// z's accuracy reports the result error relative to the exact (not rounded)
-// result. Add panics with ErrNaN if x and y are infinities with opposite
-// signs. The value of z is undefined in that case.
-//
-// BUG(gri) When rounding ToNegativeInf, the sign of Float values rounded to 0 is incorrect.
-func (z *Float) Add(x, y *Float) *Float {
-	if debugFloat {
-		x.validate()
-		y.validate()
-	}
-
-	if z.prec == 0 {
-		z.prec = umax32(x.prec, y.prec)
-	}
-
-	if x.form == finite && y.form == finite {
-		// x + y (common case)
-		z.neg = x.neg
-		if x.neg == y.neg {
-			// x + y == x + y
-			// (-x) + (-y) == -(x + y)
-			z.uadd(x, y)
-		} else {
-			// x + (-y) == x - y == -(y - x)
-			// (-x) + y == y - x == -(x - y)
-			if x.ucmp(y) > 0 {
-				z.usub(x, y)
-			} else {
-				z.neg = !z.neg
-				z.usub(y, x)
-			}
-		}
-		return z
-	}
-
-	if x.form == inf && y.form == inf && x.neg != y.neg {
-		// +Inf + -Inf
-		// -Inf + +Inf
-		// value of z is undefined but make sure it's valid
-		z.acc = Exact
-		z.form = zero
-		z.neg = false
-		panic(ErrNaN{"addition of infinities with opposite signs"})
-	}
-
-	if x.form == zero && y.form == zero {
-		// ±0 + ±0
-		z.acc = Exact
-		z.form = zero
-		z.neg = x.neg && y.neg // -0 + -0 == -0
-		return z
-	}
-
-	if x.form == inf || y.form == zero {
-		// ±Inf + y
-		// x + ±0
-		return z.Set(x)
-	}
-
-	// ±0 + y
-	// x + ±Inf
-	return z.Set(y)
-}
-
-// Sub sets z to the rounded difference x-y and returns z.
-// Precision, rounding, and accuracy reporting are as for Add.
-// Sub panics with ErrNaN if x and y are infinities with equal
-// signs. The value of z is undefined in that case.
-func (z *Float) Sub(x, y *Float) *Float {
-	if debugFloat {
-		x.validate()
-		y.validate()
-	}
-
-	if z.prec == 0 {
-		z.prec = umax32(x.prec, y.prec)
-	}
-
-	if x.form == finite && y.form == finite {
-		// x - y (common case)
-		z.neg = x.neg
-		if x.neg != y.neg {
-			// x - (-y) == x + y
-			// (-x) - y == -(x + y)
-			z.uadd(x, y)
-		} else {
-			// x - y == x - y == -(y - x)
-			// (-x) - (-y) == y - x == -(x - y)
-			if x.ucmp(y) > 0 {
-				z.usub(x, y)
-			} else {
-				z.neg = !z.neg
-				z.usub(y, x)
-			}
-		}
-		return z
-	}
-
-	if x.form == inf && y.form == inf && x.neg == y.neg {
-		// +Inf - +Inf
-		// -Inf - -Inf
-		// value of z is undefined but make sure it's valid
-		z.acc = Exact
-		z.form = zero
-		z.neg = false
-		panic(ErrNaN{"subtraction of infinities with equal signs"})
-	}
-
-	if x.form == zero && y.form == zero {
-		// ±0 - ±0
-		z.acc = Exact
-		z.form = zero
-		z.neg = x.neg && !y.neg // -0 - +0 == -0
-		return z
-	}
-
-	if x.form == inf || y.form == zero {
-		// ±Inf - y
-		// x - ±0
-		return z.Set(x)
-	}
-
-	// ±0 - y
-	// x - ±Inf
-	return z.Neg(y)
-}
-
-// Mul sets z to the rounded product x*y and returns z.
-// Precision, rounding, and accuracy reporting are as for Add.
-// Mul panics with ErrNaN if one operand is zero and the other
-// operand an infinity. The value of z is undefined in that case.
-func (z *Float) Mul(x, y *Float) *Float {
-	if debugFloat {
-		x.validate()
-		y.validate()
-	}
-
-	if z.prec == 0 {
-		z.prec = umax32(x.prec, y.prec)
-	}
-
-	z.neg = x.neg != y.neg
-
-	if x.form == finite && y.form == finite {
-		// x * y (common case)
-		z.umul(x, y)
-		return z
-	}
-
-	z.acc = Exact
-	if x.form == zero && y.form == inf || x.form == inf && y.form == zero {
-		// ±0 * ±Inf
-		// ±Inf * ±0
-		// value of z is undefined but make sure it's valid
-		z.form = zero
-		z.neg = false
-		panic(ErrNaN{"multiplication of zero with infinity"})
-	}
-
-	if x.form == inf || y.form == inf {
-		// ±Inf * y
-		// x * ±Inf
-		z.form = inf
-		return z
-	}
-
-	// ±0 * y
-	// x * ±0
-	z.form = zero
-	return z
-}
-
-// Quo sets z to the rounded quotient x/y and returns z.
-// Precision, rounding, and accuracy reporting are as for Add.
-// Quo panics with ErrNaN if both operands are zero or infinities.
-// The value of z is undefined in that case.
-func (z *Float) Quo(x, y *Float) *Float {
-	if debugFloat {
-		x.validate()
-		y.validate()
-	}
-
-	if z.prec == 0 {
-		z.prec = umax32(x.prec, y.prec)
-	}
-
-	z.neg = x.neg != y.neg
-
-	if x.form == finite && y.form == finite {
-		// x / y (common case)
-		z.uquo(x, y)
-		return z
-	}
-
-	z.acc = Exact
-	if x.form == zero && y.form == zero || x.form == inf && y.form == inf {
-		// ±0 / ±0
-		// ±Inf / ±Inf
-		// value of z is undefined but make sure it's valid
-		z.form = zero
-		z.neg = false
-		panic(ErrNaN{"division of zero by zero or infinity by infinity"})
-	}
-
-	if x.form == zero || y.form == inf {
-		// ±0 / y
-		// x / ±Inf
-		z.form = zero
-		return z
-	}
-
-	// x / ±0
-	// ±Inf / y
-	z.form = inf
-	return z
-}
-
-// Cmp compares x and y and returns:
-//
-//   -1 if x <  y
-//    0 if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf)
-//   +1 if x >  y
-//
-func (x *Float) Cmp(y *Float) int {
-	if debugFloat {
-		x.validate()
-		y.validate()
-	}
-
-	mx := x.ord()
-	my := y.ord()
-	switch {
-	case mx < my:
-		return -1
-	case mx > my:
-		return +1
-	}
-	// mx == my
-
-	// only if |mx| == 1 we have to compare the mantissae
-	switch mx {
-	case -1:
-		return y.ucmp(x)
-	case +1:
-		return x.ucmp(y)
-	}
-
-	return 0
-}
-
-// ord classifies x and returns:
-//
-//	-2 if -Inf == x
-//	-1 if -Inf < x < 0
-//	 0 if x == 0 (signed or unsigned)
-//	+1 if 0 < x < +Inf
-//	+2 if x == +Inf
-//
-func (x *Float) ord() int {
-	var m int
-	switch x.form {
-	case finite:
-		m = 1
-	case zero:
-		return 0
-	case inf:
-		m = 2
-	}
-	if x.neg {
-		m = -m
-	}
-	return m
-}
-
-func umax32(x, y uint32) uint32 {
-	if x > y {
-		return x
-	}
-	return y
-}
diff --git a/src/cmd/compile/internal/big/float_test.go b/src/cmd/compile/internal/big/float_test.go
deleted file mode 100644
index 464619b..0000000
--- a/src/cmd/compile/internal/big/float_test.go
+++ /dev/null
@@ -1,1764 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"fmt"
-	"math"
-	"strconv"
-	"strings"
-	"testing"
-)
-
-// Verify that ErrNaN implements the error interface.
-var _ error = ErrNaN{}
-
-func (x *Float) uint64() uint64 {
-	u, acc := x.Uint64()
-	if acc != Exact {
-		panic(fmt.Sprintf("%s is not a uint64", x.Text('g', 10)))
-	}
-	return u
-}
-
-func (x *Float) int64() int64 {
-	i, acc := x.Int64()
-	if acc != Exact {
-		panic(fmt.Sprintf("%s is not an int64", x.Text('g', 10)))
-	}
-	return i
-}
-
-func TestFloatZeroValue(t *testing.T) {
-	// zero (uninitialized) value is a ready-to-use 0.0
-	var x Float
-	if s := x.Text('f', 1); s != "0.0" {
-		t.Errorf("zero value = %s; want 0.0", s)
-	}
-
-	// zero value has precision 0
-	if prec := x.Prec(); prec != 0 {
-		t.Errorf("prec = %d; want 0", prec)
-	}
-
-	// zero value can be used in any and all positions of binary operations
-	make := func(x int) *Float {
-		var f Float
-		if x != 0 {
-			f.SetInt64(int64(x))
-		}
-		// x == 0 translates into the zero value
-		return &f
-	}
-	for _, test := range []struct {
-		z, x, y, want int
-		opname        rune
-		op            func(z, x, y *Float) *Float
-	}{
-		{0, 0, 0, 0, '+', (*Float).Add},
-		{0, 1, 2, 3, '+', (*Float).Add},
-		{1, 2, 0, 2, '+', (*Float).Add},
-		{2, 0, 1, 1, '+', (*Float).Add},
-
-		{0, 0, 0, 0, '-', (*Float).Sub},
-		{0, 1, 2, -1, '-', (*Float).Sub},
-		{1, 2, 0, 2, '-', (*Float).Sub},
-		{2, 0, 1, -1, '-', (*Float).Sub},
-
-		{0, 0, 0, 0, '*', (*Float).Mul},
-		{0, 1, 2, 2, '*', (*Float).Mul},
-		{1, 2, 0, 0, '*', (*Float).Mul},
-		{2, 0, 1, 0, '*', (*Float).Mul},
-
-		// {0, 0, 0, 0, '/', (*Float).Quo}, // panics
-		{0, 2, 1, 2, '/', (*Float).Quo},
-		{1, 2, 0, 0, '/', (*Float).Quo}, // = +Inf
-		{2, 0, 1, 0, '/', (*Float).Quo},
-	} {
-		z := make(test.z)
-		test.op(z, make(test.x), make(test.y))
-		got := 0
-		if !z.IsInf() {
-			got = int(z.int64())
-		}
-		if got != test.want {
-			t.Errorf("%d %c %d = %d; want %d", test.x, test.opname, test.y, got, test.want)
-		}
-	}
-
-	// TODO(gri) test how precision is set for zero value results
-}
-
-func makeFloat(s string) *Float {
-	x, _, err := ParseFloat(s, 0, 1000, ToNearestEven)
-	if err != nil {
-		panic(err)
-	}
-	return x
-}
-
-func TestFloatSetPrec(t *testing.T) {
-	for _, test := range []struct {
-		x    string
-		prec uint
-		want string
-		acc  Accuracy
-	}{
-		// prec 0
-		{"0", 0, "0", Exact},
-		{"-0", 0, "-0", Exact},
-		{"-Inf", 0, "-Inf", Exact},
-		{"+Inf", 0, "+Inf", Exact},
-		{"123", 0, "0", Below},
-		{"-123", 0, "-0", Above},
-
-		// prec at upper limit
-		{"0", MaxPrec, "0", Exact},
-		{"-0", MaxPrec, "-0", Exact},
-		{"-Inf", MaxPrec, "-Inf", Exact},
-		{"+Inf", MaxPrec, "+Inf", Exact},
-
-		// just a few regular cases - general rounding is tested elsewhere
-		{"1.5", 1, "2", Above},
-		{"-1.5", 1, "-2", Below},
-		{"123", 1e6, "123", Exact},
-		{"-123", 1e6, "-123", Exact},
-	} {
-		x := makeFloat(test.x).SetPrec(test.prec)
-		prec := test.prec
-		if prec > MaxPrec {
-			prec = MaxPrec
-		}
-		if got := x.Prec(); got != prec {
-			t.Errorf("%s.SetPrec(%d).Prec() == %d; want %d", test.x, test.prec, got, prec)
-		}
-		if got, acc := x.String(), x.Acc(); got != test.want || acc != test.acc {
-			t.Errorf("%s.SetPrec(%d) = %s (%s); want %s (%s)", test.x, test.prec, got, acc, test.want, test.acc)
-		}
-	}
-}
-
-func TestFloatMinPrec(t *testing.T) {
-	const max = 100
-	for _, test := range []struct {
-		x    string
-		want uint
-	}{
-		{"0", 0},
-		{"-0", 0},
-		{"+Inf", 0},
-		{"-Inf", 0},
-		{"1", 1},
-		{"2", 1},
-		{"3", 2},
-		{"0x8001", 16},
-		{"0x8001p-1000", 16},
-		{"0x8001p+1000", 16},
-		{"0.1", max},
-	} {
-		x := makeFloat(test.x).SetPrec(max)
-		if got := x.MinPrec(); got != test.want {
-			t.Errorf("%s.MinPrec() = %d; want %d", test.x, got, test.want)
-		}
-	}
-}
-
-func TestFloatSign(t *testing.T) {
-	for _, test := range []struct {
-		x string
-		s int
-	}{
-		{"-Inf", -1},
-		{"-1", -1},
-		{"-0", 0},
-		{"+0", 0},
-		{"+1", +1},
-		{"+Inf", +1},
-	} {
-		x := makeFloat(test.x)
-		s := x.Sign()
-		if s != test.s {
-			t.Errorf("%s.Sign() = %d; want %d", test.x, s, test.s)
-		}
-	}
-}
-
-// alike(x, y) is like x.Cmp(y) == 0 but also considers the sign of 0 (0 != -0).
-func alike(x, y *Float) bool {
-	return x.Cmp(y) == 0 && x.Signbit() == y.Signbit()
-}
-
-func alike32(x, y float32) bool {
-	// we can ignore NaNs
-	return x == y && math.Signbit(float64(x)) == math.Signbit(float64(y))
-
-}
-
-func alike64(x, y float64) bool {
-	// we can ignore NaNs
-	return x == y && math.Signbit(x) == math.Signbit(y)
-
-}
-
-func TestFloatMantExp(t *testing.T) {
-	for _, test := range []struct {
-		x    string
-		mant string
-		exp  int
-	}{
-		{"0", "0", 0},
-		{"+0", "0", 0},
-		{"-0", "-0", 0},
-		{"Inf", "+Inf", 0},
-		{"+Inf", "+Inf", 0},
-		{"-Inf", "-Inf", 0},
-		{"1.5", "0.75", 1},
-		{"1.024e3", "0.5", 11},
-		{"-0.125", "-0.5", -2},
-	} {
-		x := makeFloat(test.x)
-		mant := makeFloat(test.mant)
-		m := new(Float)
-		e := x.MantExp(m)
-		if !alike(m, mant) || e != test.exp {
-			t.Errorf("%s.MantExp() = %s, %d; want %s, %d", test.x, m.Text('g', 10), e, test.mant, test.exp)
-		}
-	}
-}
-
-func TestFloatMantExpAliasing(t *testing.T) {
-	x := makeFloat("0.5p10")
-	if e := x.MantExp(x); e != 10 {
-		t.Fatalf("Float.MantExp aliasing error: got %d; want 10", e)
-	}
-	if want := makeFloat("0.5"); !alike(x, want) {
-		t.Fatalf("Float.MantExp aliasing error: got %s; want %s", x.Text('g', 10), want.Text('g', 10))
-	}
-}
-
-func TestFloatSetMantExp(t *testing.T) {
-	for _, test := range []struct {
-		frac string
-		exp  int
-		z    string
-	}{
-		{"0", 0, "0"},
-		{"+0", 0, "0"},
-		{"-0", 0, "-0"},
-		{"Inf", 1234, "+Inf"},
-		{"+Inf", -1234, "+Inf"},
-		{"-Inf", -1234, "-Inf"},
-		{"0", MinExp, "0"},
-		{"0.25", MinExp, "+0"},    // exponent underflow
-		{"-0.25", MinExp, "-0"},   // exponent underflow
-		{"1", MaxExp, "+Inf"},     // exponent overflow
-		{"2", MaxExp - 1, "+Inf"}, // exponent overflow
-		{"0.75", 1, "1.5"},
-		{"0.5", 11, "1024"},
-		{"-0.5", -2, "-0.125"},
-		{"32", 5, "1024"},
-		{"1024", -10, "1"},
-	} {
-		frac := makeFloat(test.frac)
-		want := makeFloat(test.z)
-		var z Float
-		z.SetMantExp(frac, test.exp)
-		if !alike(&z, want) {
-			t.Errorf("SetMantExp(%s, %d) = %s; want %s", test.frac, test.exp, z.Text('g', 10), test.z)
-		}
-		// test inverse property
-		mant := new(Float)
-		if z.SetMantExp(mant, want.MantExp(mant)).Cmp(want) != 0 {
-			t.Errorf("Inverse property not satisfied: got %s; want %s", z.Text('g', 10), test.z)
-		}
-	}
-}
-
-func TestFloatPredicates(t *testing.T) {
-	for _, test := range []struct {
-		x            string
-		sign         int
-		signbit, inf bool
-	}{
-		{x: "-Inf", sign: -1, signbit: true, inf: true},
-		{x: "-1", sign: -1, signbit: true},
-		{x: "-0", signbit: true},
-		{x: "0"},
-		{x: "1", sign: 1},
-		{x: "+Inf", sign: 1, inf: true},
-	} {
-		x := makeFloat(test.x)
-		if got := x.Signbit(); got != test.signbit {
-			t.Errorf("(%s).Signbit() = %v; want %v", test.x, got, test.signbit)
-		}
-		if got := x.Sign(); got != test.sign {
-			t.Errorf("(%s).Sign() = %d; want %d", test.x, got, test.sign)
-		}
-		if got := x.IsInf(); got != test.inf {
-			t.Errorf("(%s).IsInf() = %v; want %v", test.x, got, test.inf)
-		}
-	}
-}
-
-func TestFloatIsInt(t *testing.T) {
-	for _, test := range []string{
-		"0 int",
-		"-0 int",
-		"1 int",
-		"-1 int",
-		"0.5",
-		"1.23",
-		"1.23e1",
-		"1.23e2 int",
-		"0.000000001e+8",
-		"0.000000001e+9 int",
-		"1.2345e200 int",
-		"Inf",
-		"+Inf",
-		"-Inf",
-	} {
-		s := strings.TrimSuffix(test, " int")
-		want := s != test
-		if got := makeFloat(s).IsInt(); got != want {
-			t.Errorf("%s.IsInt() == %t", s, got)
-		}
-	}
-}
-
-func fromBinary(s string) int64 {
-	x, err := strconv.ParseInt(s, 2, 64)
-	if err != nil {
-		panic(err)
-	}
-	return x
-}
-
-func toBinary(x int64) string {
-	return strconv.FormatInt(x, 2)
-}
-
-func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) {
-	// verify test data
-	var ok bool
-	switch mode {
-	case ToNearestEven, ToNearestAway:
-		ok = true // nothing to do for now
-	case ToZero:
-		if x < 0 {
-			ok = r >= x
-		} else {
-			ok = r <= x
-		}
-	case AwayFromZero:
-		if x < 0 {
-			ok = r <= x
-		} else {
-			ok = r >= x
-		}
-	case ToNegativeInf:
-		ok = r <= x
-	case ToPositiveInf:
-		ok = r >= x
-	default:
-		panic("unreachable")
-	}
-	if !ok {
-		t.Fatalf("incorrect test data for prec = %d, %s: x = %s, r = %s", prec, mode, toBinary(x), toBinary(r))
-	}
-
-	// compute expected accuracy
-	a := Exact
-	switch {
-	case r < x:
-		a = Below
-	case r > x:
-		a = Above
-	}
-
-	// round
-	f := new(Float).SetMode(mode).SetInt64(x).SetPrec(prec)
-
-	// check result
-	r1 := f.int64()
-	p1 := f.Prec()
-	a1 := f.Acc()
-	if r1 != r || p1 != prec || a1 != a {
-		t.Errorf("round %s (%d bits, %s) incorrect: got %s (%d bits, %s); want %s (%d bits, %s)",
-			toBinary(x), prec, mode,
-			toBinary(r1), p1, a1,
-			toBinary(r), prec, a)
-		return
-	}
-
-	// g and f should be the same
-	// (rounding by SetPrec after SetInt64 using default precision
-	// should be the same as rounding by SetInt64 after setting the
-	// precision)
-	g := new(Float).SetMode(mode).SetPrec(prec).SetInt64(x)
-	if !alike(g, f) {
-		t.Errorf("round %s (%d bits, %s) not symmetric: got %s and %s; want %s",
-			toBinary(x), prec, mode,
-			toBinary(g.int64()),
-			toBinary(r1),
-			toBinary(r),
-		)
-		return
-	}
-
-	// h and f should be the same
-	// (repeated rounding should be idempotent)
-	h := new(Float).SetMode(mode).SetPrec(prec).Set(f)
-	if !alike(h, f) {
-		t.Errorf("round %s (%d bits, %s) not idempotent: got %s and %s; want %s",
-			toBinary(x), prec, mode,
-			toBinary(h.int64()),
-			toBinary(r1),
-			toBinary(r),
-		)
-		return
-	}
-}
-
-// TestFloatRound tests basic rounding.
-func TestFloatRound(t *testing.T) {
-	for _, test := range []struct {
-		prec                        uint
-		x, zero, neven, naway, away string // input, results rounded to prec bits
-	}{
-		{5, "1000", "1000", "1000", "1000", "1000"},
-		{5, "1001", "1001", "1001", "1001", "1001"},
-		{5, "1010", "1010", "1010", "1010", "1010"},
-		{5, "1011", "1011", "1011", "1011", "1011"},
-		{5, "1100", "1100", "1100", "1100", "1100"},
-		{5, "1101", "1101", "1101", "1101", "1101"},
-		{5, "1110", "1110", "1110", "1110", "1110"},
-		{5, "1111", "1111", "1111", "1111", "1111"},
-
-		{4, "1000", "1000", "1000", "1000", "1000"},
-		{4, "1001", "1001", "1001", "1001", "1001"},
-		{4, "1010", "1010", "1010", "1010", "1010"},
-		{4, "1011", "1011", "1011", "1011", "1011"},
-		{4, "1100", "1100", "1100", "1100", "1100"},
-		{4, "1101", "1101", "1101", "1101", "1101"},
-		{4, "1110", "1110", "1110", "1110", "1110"},
-		{4, "1111", "1111", "1111", "1111", "1111"},
-
-		{3, "1000", "1000", "1000", "1000", "1000"},
-		{3, "1001", "1000", "1000", "1010", "1010"},
-		{3, "1010", "1010", "1010", "1010", "1010"},
-		{3, "1011", "1010", "1100", "1100", "1100"},
-		{3, "1100", "1100", "1100", "1100", "1100"},
-		{3, "1101", "1100", "1100", "1110", "1110"},
-		{3, "1110", "1110", "1110", "1110", "1110"},
-		{3, "1111", "1110", "10000", "10000", "10000"},
-
-		{3, "1000001", "1000000", "1000000", "1000000", "1010000"},
-		{3, "1001001", "1000000", "1010000", "1010000", "1010000"},
-		{3, "1010001", "1010000", "1010000", "1010000", "1100000"},
-		{3, "1011001", "1010000", "1100000", "1100000", "1100000"},
-		{3, "1100001", "1100000", "1100000", "1100000", "1110000"},
-		{3, "1101001", "1100000", "1110000", "1110000", "1110000"},
-		{3, "1110001", "1110000", "1110000", "1110000", "10000000"},
-		{3, "1111001", "1110000", "10000000", "10000000", "10000000"},
-
-		{2, "1000", "1000", "1000", "1000", "1000"},
-		{2, "1001", "1000", "1000", "1000", "1100"},
-		{2, "1010", "1000", "1000", "1100", "1100"},
-		{2, "1011", "1000", "1100", "1100", "1100"},
-		{2, "1100", "1100", "1100", "1100", "1100"},
-		{2, "1101", "1100", "1100", "1100", "10000"},
-		{2, "1110", "1100", "10000", "10000", "10000"},
-		{2, "1111", "1100", "10000", "10000", "10000"},
-
-		{2, "1000001", "1000000", "1000000", "1000000", "1100000"},
-		{2, "1001001", "1000000", "1000000", "1000000", "1100000"},
-		{2, "1010001", "1000000", "1100000", "1100000", "1100000"},
-		{2, "1011001", "1000000", "1100000", "1100000", "1100000"},
-		{2, "1100001", "1100000", "1100000", "1100000", "10000000"},
-		{2, "1101001", "1100000", "1100000", "1100000", "10000000"},
-		{2, "1110001", "1100000", "10000000", "10000000", "10000000"},
-		{2, "1111001", "1100000", "10000000", "10000000", "10000000"},
-
-		{1, "1000", "1000", "1000", "1000", "1000"},
-		{1, "1001", "1000", "1000", "1000", "10000"},
-		{1, "1010", "1000", "1000", "1000", "10000"},
-		{1, "1011", "1000", "1000", "1000", "10000"},
-		{1, "1100", "1000", "10000", "10000", "10000"},
-		{1, "1101", "1000", "10000", "10000", "10000"},
-		{1, "1110", "1000", "10000", "10000", "10000"},
-		{1, "1111", "1000", "10000", "10000", "10000"},
-
-		{1, "1000001", "1000000", "1000000", "1000000", "10000000"},
-		{1, "1001001", "1000000", "1000000", "1000000", "10000000"},
-		{1, "1010001", "1000000", "1000000", "1000000", "10000000"},
-		{1, "1011001", "1000000", "1000000", "1000000", "10000000"},
-		{1, "1100001", "1000000", "10000000", "10000000", "10000000"},
-		{1, "1101001", "1000000", "10000000", "10000000", "10000000"},
-		{1, "1110001", "1000000", "10000000", "10000000", "10000000"},
-		{1, "1111001", "1000000", "10000000", "10000000", "10000000"},
-	} {
-		x := fromBinary(test.x)
-		z := fromBinary(test.zero)
-		e := fromBinary(test.neven)
-		n := fromBinary(test.naway)
-		a := fromBinary(test.away)
-		prec := test.prec
-
-		testFloatRound(t, x, z, prec, ToZero)
-		testFloatRound(t, x, e, prec, ToNearestEven)
-		testFloatRound(t, x, n, prec, ToNearestAway)
-		testFloatRound(t, x, a, prec, AwayFromZero)
-
-		testFloatRound(t, x, z, prec, ToNegativeInf)
-		testFloatRound(t, x, a, prec, ToPositiveInf)
-
-		testFloatRound(t, -x, -a, prec, ToNegativeInf)
-		testFloatRound(t, -x, -z, prec, ToPositiveInf)
-	}
-}
-
-// TestFloatRound24 tests that rounding a float64 to 24 bits
-// matches IEEE-754 rounding to nearest when converting a
-// float64 to a float32 (excluding denormal numbers).
-func TestFloatRound24(t *testing.T) {
-	const x0 = 1<<26 - 0x10 // 11...110000 (26 bits)
-	for d := 0; d <= 0x10; d++ {
-		x := float64(x0 + d)
-		f := new(Float).SetPrec(24).SetFloat64(x)
-		got, _ := f.Float32()
-		want := float32(x)
-		if got != want {
-			t.Errorf("Round(%g, 24) = %g; want %g", x, got, want)
-		}
-	}
-}
-
-func TestFloatSetUint64(t *testing.T) {
-	for _, want := range []uint64{
-		0,
-		1,
-		2,
-		10,
-		100,
-		1<<32 - 1,
-		1 << 32,
-		1<<64 - 1,
-	} {
-		var f Float
-		f.SetUint64(want)
-		if got := f.uint64(); got != want {
-			t.Errorf("got %#x (%s); want %#x", got, f.Text('p', 0), want)
-		}
-	}
-
-	// test basic rounding behavior (exhaustive rounding testing is done elsewhere)
-	const x uint64 = 0x8765432187654321 // 64 bits needed
-	for prec := uint(1); prec <= 64; prec++ {
-		f := new(Float).SetPrec(prec).SetMode(ToZero).SetUint64(x)
-		got := f.uint64()
-		want := x &^ (1<<(64-prec) - 1) // cut off (round to zero) low 64-prec bits
-		if got != want {
-			t.Errorf("got %#x (%s); want %#x", got, f.Text('p', 0), want)
-		}
-	}
-}
-
-func TestFloatSetInt64(t *testing.T) {
-	for _, want := range []int64{
-		0,
-		1,
-		2,
-		10,
-		100,
-		1<<32 - 1,
-		1 << 32,
-		1<<63 - 1,
-	} {
-		for i := range [2]int{} {
-			if i&1 != 0 {
-				want = -want
-			}
-			var f Float
-			f.SetInt64(want)
-			if got := f.int64(); got != want {
-				t.Errorf("got %#x (%s); want %#x", got, f.Text('p', 0), want)
-			}
-		}
-	}
-
-	// test basic rounding behavior (exhaustive rounding testing is done elsewhere)
-	const x int64 = 0x7654321076543210 // 63 bits needed
-	for prec := uint(1); prec <= 63; prec++ {
-		f := new(Float).SetPrec(prec).SetMode(ToZero).SetInt64(x)
-		got := f.int64()
-		want := x &^ (1<<(63-prec) - 1) // cut off (round to zero) low 63-prec bits
-		if got != want {
-			t.Errorf("got %#x (%s); want %#x", got, f.Text('p', 0), want)
-		}
-	}
-}
-
-func TestFloatSetFloat64(t *testing.T) {
-	for _, want := range []float64{
-		0,
-		1,
-		2,
-		12345,
-		1e10,
-		1e100,
-		3.14159265e10,
-		2.718281828e-123,
-		1.0 / 3,
-		math.MaxFloat32,
-		math.MaxFloat64,
-		math.SmallestNonzeroFloat32,
-		math.SmallestNonzeroFloat64,
-		math.Inf(-1),
-		math.Inf(0),
-		-math.Inf(1),
-	} {
-		for i := range [2]int{} {
-			if i&1 != 0 {
-				want = -want
-			}
-			var f Float
-			f.SetFloat64(want)
-			if got, acc := f.Float64(); got != want || acc != Exact {
-				t.Errorf("got %g (%s, %s); want %g (Exact)", got, f.Text('p', 0), acc, want)
-			}
-		}
-	}
-
-	// test basic rounding behavior (exhaustive rounding testing is done elsewhere)
-	const x uint64 = 0x8765432143218 // 53 bits needed
-	for prec := uint(1); prec <= 52; prec++ {
-		f := new(Float).SetPrec(prec).SetMode(ToZero).SetFloat64(float64(x))
-		got, _ := f.Float64()
-		want := float64(x &^ (1<<(52-prec) - 1)) // cut off (round to zero) low 53-prec bits
-		if got != want {
-			t.Errorf("got %g (%s); want %g", got, f.Text('p', 0), want)
-		}
-	}
-
-	// test NaN
-	defer func() {
-		if p, ok := recover().(ErrNaN); !ok {
-			t.Errorf("got %v; want ErrNaN panic", p)
-		}
-	}()
-	var f Float
-	f.SetFloat64(math.NaN())
-	// should not reach here
-	t.Errorf("got %s; want ErrNaN panic", f.Text('p', 0))
-}
-
-func TestFloatSetInt(t *testing.T) {
-	for _, want := range []string{
-		"0",
-		"1",
-		"-1",
-		"1234567890",
-		"123456789012345678901234567890",
-		"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
-	} {
-		var x Int
-		_, ok := x.SetString(want, 0)
-		if !ok {
-			t.Errorf("invalid integer %s", want)
-			continue
-		}
-		n := x.BitLen()
-
-		var f Float
-		f.SetInt(&x)
-
-		// check precision
-		if n < 64 {
-			n = 64
-		}
-		if prec := f.Prec(); prec != uint(n) {
-			t.Errorf("got prec = %d; want %d", prec, n)
-		}
-
-		// check value
-		got := f.Text('g', 100)
-		if got != want {
-			t.Errorf("got %s (%s); want %s", got, f.Text('p', 0), want)
-		}
-	}
-
-	// TODO(gri) test basic rounding behavior
-}
-
-func TestFloatSetRat(t *testing.T) {
-	for _, want := range []string{
-		"0",
-		"1",
-		"-1",
-		"1234567890",
-		"123456789012345678901234567890",
-		"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
-		"1.2",
-		"3.14159265",
-		// TODO(gri) expand
-	} {
-		var x Rat
-		_, ok := x.SetString(want)
-		if !ok {
-			t.Errorf("invalid fraction %s", want)
-			continue
-		}
-		n := max(x.Num().BitLen(), x.Denom().BitLen())
-
-		var f1, f2 Float
-		f2.SetPrec(1000)
-		f1.SetRat(&x)
-		f2.SetRat(&x)
-
-		// check precision when set automatically
-		if n < 64 {
-			n = 64
-		}
-		if prec := f1.Prec(); prec != uint(n) {
-			t.Errorf("got prec = %d; want %d", prec, n)
-		}
-
-		got := f2.Text('g', 100)
-		if got != want {
-			t.Errorf("got %s (%s); want %s", got, f2.Text('p', 0), want)
-		}
-	}
-}
-
-func TestFloatSetInf(t *testing.T) {
-	var f Float
-	for _, test := range []struct {
-		signbit bool
-		prec    uint
-		want    string
-	}{
-		{false, 0, "+Inf"},
-		{true, 0, "-Inf"},
-		{false, 10, "+Inf"},
-		{true, 30, "-Inf"},
-	} {
-		x := f.SetPrec(test.prec).SetInf(test.signbit)
-		if got := x.String(); got != test.want || x.Prec() != test.prec {
-			t.Errorf("SetInf(%v) = %s (prec = %d); want %s (prec = %d)", test.signbit, got, x.Prec(), test.want, test.prec)
-		}
-	}
-}
-
-func TestFloatUint64(t *testing.T) {
-	for _, test := range []struct {
-		x   string
-		out uint64
-		acc Accuracy
-	}{
-		{"-Inf", 0, Above},
-		{"-1", 0, Above},
-		{"-1e-1000", 0, Above},
-		{"-0", 0, Exact},
-		{"0", 0, Exact},
-		{"1e-1000", 0, Below},
-		{"1", 1, Exact},
-		{"1.000000000000000000001", 1, Below},
-		{"12345.0", 12345, Exact},
-		{"12345.000000000000000000001", 12345, Below},
-		{"18446744073709551615", 18446744073709551615, Exact},
-		{"18446744073709551615.000000000000000000001", math.MaxUint64, Below},
-		{"18446744073709551616", math.MaxUint64, Below},
-		{"1e10000", math.MaxUint64, Below},
-		{"+Inf", math.MaxUint64, Below},
-	} {
-		x := makeFloat(test.x)
-		out, acc := x.Uint64()
-		if out != test.out || acc != test.acc {
-			t.Errorf("%s: got %d (%s); want %d (%s)", test.x, out, acc, test.out, test.acc)
-		}
-	}
-}
-
-func TestFloatInt64(t *testing.T) {
-	for _, test := range []struct {
-		x   string
-		out int64
-		acc Accuracy
-	}{
-		{"-Inf", math.MinInt64, Above},
-		{"-1e10000", math.MinInt64, Above},
-		{"-9223372036854775809", math.MinInt64, Above},
-		{"-9223372036854775808.000000000000000000001", math.MinInt64, Above},
-		{"-9223372036854775808", -9223372036854775808, Exact},
-		{"-9223372036854775807.000000000000000000001", -9223372036854775807, Above},
-		{"-9223372036854775807", -9223372036854775807, Exact},
-		{"-12345.000000000000000000001", -12345, Above},
-		{"-12345.0", -12345, Exact},
-		{"-1.000000000000000000001", -1, Above},
-		{"-1.5", -1, Above},
-		{"-1", -1, Exact},
-		{"-1e-1000", 0, Above},
-		{"0", 0, Exact},
-		{"1e-1000", 0, Below},
-		{"1", 1, Exact},
-		{"1.000000000000000000001", 1, Below},
-		{"1.5", 1, Below},
-		{"12345.0", 12345, Exact},
-		{"12345.000000000000000000001", 12345, Below},
-		{"9223372036854775807", 9223372036854775807, Exact},
-		{"9223372036854775807.000000000000000000001", math.MaxInt64, Below},
-		{"9223372036854775808", math.MaxInt64, Below},
-		{"1e10000", math.MaxInt64, Below},
-		{"+Inf", math.MaxInt64, Below},
-	} {
-		x := makeFloat(test.x)
-		out, acc := x.Int64()
-		if out != test.out || acc != test.acc {
-			t.Errorf("%s: got %d (%s); want %d (%s)", test.x, out, acc, test.out, test.acc)
-		}
-	}
-}
-
-func TestFloatFloat32(t *testing.T) {
-	for _, test := range []struct {
-		x   string
-		out float32
-		acc Accuracy
-	}{
-		{"0", 0, Exact},
-
-		// underflow to zero
-		{"1e-1000", 0, Below},
-		{"0x0.000002p-127", 0, Below},
-		{"0x.0000010p-126", 0, Below},
-
-		// denormals
-		{"1.401298464e-45", math.SmallestNonzeroFloat32, Above}, // rounded up to smallest denormal
-		{"0x.ffffff8p-149", math.SmallestNonzeroFloat32, Above}, // rounded up to smallest denormal
-		{"0x.0000018p-126", math.SmallestNonzeroFloat32, Above}, // rounded up to smallest denormal
-		{"0x.0000020p-126", math.SmallestNonzeroFloat32, Exact},
-		{"0x.8p-148", math.SmallestNonzeroFloat32, Exact},
-		{"1p-149", math.SmallestNonzeroFloat32, Exact},
-		{"0x.fffffep-126", math.Float32frombits(0x7fffff), Exact}, // largest denormal
-
-		// special denormal cases (see issues 14553, 14651)
-		{"0x0.0000001p-126", math.Float32frombits(0x00000000), Below}, // underflow to zero
-		{"0x0.0000008p-126", math.Float32frombits(0x00000000), Below}, // underflow to zero
-		{"0x0.0000010p-126", math.Float32frombits(0x00000000), Below}, // rounded down to even
-		{"0x0.0000011p-126", math.Float32frombits(0x00000001), Above}, // rounded up to smallest denormal
-		{"0x0.0000018p-126", math.Float32frombits(0x00000001), Above}, // rounded up to smallest denormal
-
-		{"0x1.0000000p-149", math.Float32frombits(0x00000001), Exact}, // smallest denormal
-		{"0x0.0000020p-126", math.Float32frombits(0x00000001), Exact}, // smallest denormal
-		{"0x0.fffffe0p-126", math.Float32frombits(0x007fffff), Exact}, // largest denormal
-		{"0x1.0000000p-126", math.Float32frombits(0x00800000), Exact}, // smallest normal
-
-		{"0x0.8p-149", math.Float32frombits(0x000000000), Below}, // rounded down to even
-		{"0x0.9p-149", math.Float32frombits(0x000000001), Above}, // rounded up to smallest denormal
-		{"0x0.ap-149", math.Float32frombits(0x000000001), Above}, // rounded up to smallest denormal
-		{"0x0.bp-149", math.Float32frombits(0x000000001), Above}, // rounded up to smallest denormal
-		{"0x0.cp-149", math.Float32frombits(0x000000001), Above}, // rounded up to smallest denormal
-
-		{"0x1.0p-149", math.Float32frombits(0x000000001), Exact}, // smallest denormal
-		{"0x1.7p-149", math.Float32frombits(0x000000001), Below},
-		{"0x1.8p-149", math.Float32frombits(0x000000002), Above},
-		{"0x1.9p-149", math.Float32frombits(0x000000002), Above},
-
-		{"0x2.0p-149", math.Float32frombits(0x000000002), Exact},
-		{"0x2.8p-149", math.Float32frombits(0x000000002), Below}, // rounded down to even
-		{"0x2.9p-149", math.Float32frombits(0x000000003), Above},
-
-		{"0x3.0p-149", math.Float32frombits(0x000000003), Exact},
-		{"0x3.7p-149", math.Float32frombits(0x000000003), Below},
-		{"0x3.8p-149", math.Float32frombits(0x000000004), Above}, // rounded up to even
-
-		{"0x4.0p-149", math.Float32frombits(0x000000004), Exact},
-		{"0x4.8p-149", math.Float32frombits(0x000000004), Below}, // rounded down to even
-		{"0x4.9p-149", math.Float32frombits(0x000000005), Above},
-
-		// specific case from issue 14553
-		{"0x7.7p-149", math.Float32frombits(0x000000007), Below},
-		{"0x7.8p-149", math.Float32frombits(0x000000008), Above},
-		{"0x7.9p-149", math.Float32frombits(0x000000008), Above},
-
-		// normals
-		{"0x.ffffffp-126", math.Float32frombits(0x00800000), Above}, // rounded up to smallest normal
-		{"1p-126", math.Float32frombits(0x00800000), Exact},         // smallest normal
-		{"0x1.fffffep-126", math.Float32frombits(0x00ffffff), Exact},
-		{"0x1.ffffffp-126", math.Float32frombits(0x01000000), Above}, // rounded up
-		{"1", 1, Exact},
-		{"1.000000000000000000001", 1, Below},
-		{"12345.0", 12345, Exact},
-		{"12345.000000000000000000001", 12345, Below},
-		{"0x1.fffffe0p127", math.MaxFloat32, Exact},
-		{"0x1.fffffe8p127", math.MaxFloat32, Below},
-
-		// overflow
-		{"0x1.ffffff0p127", float32(math.Inf(+1)), Above},
-		{"0x1p128", float32(math.Inf(+1)), Above},
-		{"1e10000", float32(math.Inf(+1)), Above},
-		{"0x1.ffffff0p2147483646", float32(math.Inf(+1)), Above}, // overflow in rounding
-
-		// inf
-		{"Inf", float32(math.Inf(+1)), Exact},
-	} {
-		for i := 0; i < 2; i++ {
-			// test both signs
-			tx, tout, tacc := test.x, test.out, test.acc
-			if i != 0 {
-				tx = "-" + tx
-				tout = -tout
-				tacc = -tacc
-			}
-
-			// conversion should match strconv where syntax is agreeable
-			if f, err := strconv.ParseFloat(tx, 32); err == nil && !alike32(float32(f), tout) {
-				t.Errorf("%s: got %g; want %g (incorrect test data)", tx, f, tout)
-			}
-
-			x := makeFloat(tx)
-			out, acc := x.Float32()
-			if !alike32(out, tout) || acc != tacc {
-				t.Errorf("%s: got %g (%#08x, %s); want %g (%#08x, %s)", tx, out, math.Float32bits(out), acc, test.out, math.Float32bits(test.out), tacc)
-			}
-
-			// test that x.SetFloat64(float64(f)).Float32() == f
-			var x2 Float
-			out2, acc2 := x2.SetFloat64(float64(out)).Float32()
-			if !alike32(out2, out) || acc2 != Exact {
-				t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
-			}
-		}
-	}
-}
-
-func TestFloatFloat64(t *testing.T) {
-	const smallestNormalFloat64 = 2.2250738585072014e-308 // 1p-1022
-	for _, test := range []struct {
-		x   string
-		out float64
-		acc Accuracy
-	}{
-		{"0", 0, Exact},
-
-		// underflow to zero
-		{"1e-1000", 0, Below},
-		{"0x0.0000000000001p-1023", 0, Below},
-		{"0x0.00000000000008p-1022", 0, Below},
-
-		// denormals
-		{"0x0.0000000000000cp-1022", math.SmallestNonzeroFloat64, Above}, // rounded up to smallest denormal
-		{"0x0.00000000000010p-1022", math.SmallestNonzeroFloat64, Exact}, // smallest denormal
-		{"0x.8p-1073", math.SmallestNonzeroFloat64, Exact},
-		{"1p-1074", math.SmallestNonzeroFloat64, Exact},
-		{"0x.fffffffffffffp-1022", math.Float64frombits(0x000fffffffffffff), Exact}, // largest denormal
-
-		// special denormal cases (see issues 14553, 14651)
-		{"0x0.00000000000001p-1022", math.Float64frombits(0x00000000000000000), Below}, // underflow to zero
-		{"0x0.00000000000004p-1022", math.Float64frombits(0x00000000000000000), Below}, // underflow to zero
-		{"0x0.00000000000008p-1022", math.Float64frombits(0x00000000000000000), Below}, // rounded down to even
-		{"0x0.00000000000009p-1022", math.Float64frombits(0x00000000000000001), Above}, // rounded up to smallest denormal
-		{"0x0.0000000000000ap-1022", math.Float64frombits(0x00000000000000001), Above}, // rounded up to smallest denormal
-
-		{"0x0.8p-1074", math.Float64frombits(0x00000000000000000), Below}, // rounded down to even
-		{"0x0.9p-1074", math.Float64frombits(0x00000000000000001), Above}, // rounded up to smallest denormal
-		{"0x0.ap-1074", math.Float64frombits(0x00000000000000001), Above}, // rounded up to smallest denormal
-		{"0x0.bp-1074", math.Float64frombits(0x00000000000000001), Above}, // rounded up to smallest denormal
-		{"0x0.cp-1074", math.Float64frombits(0x00000000000000001), Above}, // rounded up to smallest denormal
-
-		{"0x1.0p-1074", math.Float64frombits(0x00000000000000001), Exact},
-		{"0x1.7p-1074", math.Float64frombits(0x00000000000000001), Below},
-		{"0x1.8p-1074", math.Float64frombits(0x00000000000000002), Above},
-		{"0x1.9p-1074", math.Float64frombits(0x00000000000000002), Above},
-
-		{"0x2.0p-1074", math.Float64frombits(0x00000000000000002), Exact},
-		{"0x2.8p-1074", math.Float64frombits(0x00000000000000002), Below}, // rounded down to even
-		{"0x2.9p-1074", math.Float64frombits(0x00000000000000003), Above},
-
-		{"0x3.0p-1074", math.Float64frombits(0x00000000000000003), Exact},
-		{"0x3.7p-1074", math.Float64frombits(0x00000000000000003), Below},
-		{"0x3.8p-1074", math.Float64frombits(0x00000000000000004), Above}, // rounded up to even
-
-		{"0x4.0p-1074", math.Float64frombits(0x00000000000000004), Exact},
-		{"0x4.8p-1074", math.Float64frombits(0x00000000000000004), Below}, // rounded down to even
-		{"0x4.9p-1074", math.Float64frombits(0x00000000000000005), Above},
-
-		// normals
-		{"0x.fffffffffffff8p-1022", math.Float64frombits(0x0010000000000000), Above}, // rounded up to smallest normal
-		{"1p-1022", math.Float64frombits(0x0010000000000000), Exact},                 // smallest normal
-		{"1", 1, Exact},
-		{"1.000000000000000000001", 1, Below},
-		{"12345.0", 12345, Exact},
-		{"12345.000000000000000000001", 12345, Below},
-		{"0x1.fffffffffffff0p1023", math.MaxFloat64, Exact},
-		{"0x1.fffffffffffff4p1023", math.MaxFloat64, Below},
-
-		// overflow
-		{"0x1.fffffffffffff8p1023", math.Inf(+1), Above},
-		{"0x1p1024", math.Inf(+1), Above},
-		{"1e10000", math.Inf(+1), Above},
-		{"0x1.fffffffffffff8p2147483646", math.Inf(+1), Above}, // overflow in rounding
-		{"Inf", math.Inf(+1), Exact},
-
-		// selected denormalized values that were handled incorrectly in the past
-		{"0x.fffffffffffffp-1022", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact},
-		{"4503599627370495p-1074", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact},
-
-		// http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
-		{"2.2250738585072011e-308", 2.225073858507201e-308, Below},
-		// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
-		{"2.2250738585072012e-308", 2.2250738585072014e-308, Above},
-	} {
-		for i := 0; i < 2; i++ {
-			// test both signs
-			tx, tout, tacc := test.x, test.out, test.acc
-			if i != 0 {
-				tx = "-" + tx
-				tout = -tout
-				tacc = -tacc
-			}
-
-			// conversion should match strconv where syntax is agreeable
-			if f, err := strconv.ParseFloat(tx, 64); err == nil && !alike64(f, tout) {
-				t.Errorf("%s: got %g; want %g (incorrect test data)", tx, f, tout)
-			}
-
-			x := makeFloat(tx)
-			out, acc := x.Float64()
-			if !alike64(out, tout) || acc != tacc {
-				t.Errorf("%s: got %g (%#016x, %s); want %g (%#016x, %s)", tx, out, math.Float64bits(out), acc, test.out, math.Float64bits(test.out), tacc)
-			}
-
-			// test that x.SetFloat64(f).Float64() == f
-			var x2 Float
-			out2, acc2 := x2.SetFloat64(out).Float64()
-			if !alike64(out2, out) || acc2 != Exact {
-				t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
-			}
-		}
-	}
-}
-
-func TestFloatInt(t *testing.T) {
-	for _, test := range []struct {
-		x    string
-		want string
-		acc  Accuracy
-	}{
-		{"0", "0", Exact},
-		{"+0", "0", Exact},
-		{"-0", "0", Exact},
-		{"Inf", "nil", Below},
-		{"+Inf", "nil", Below},
-		{"-Inf", "nil", Above},
-		{"1", "1", Exact},
-		{"-1", "-1", Exact},
-		{"1.23", "1", Below},
-		{"-1.23", "-1", Above},
-		{"123e-2", "1", Below},
-		{"123e-3", "0", Below},
-		{"123e-4", "0", Below},
-		{"1e-1000", "0", Below},
-		{"-1e-1000", "0", Above},
-		{"1e+10", "10000000000", Exact},
-		{"1e+100", "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", Exact},
-	} {
-		x := makeFloat(test.x)
-		res, acc := x.Int(nil)
-		got := "nil"
-		if res != nil {
-			got = res.String()
-		}
-		if got != test.want || acc != test.acc {
-			t.Errorf("%s: got %s (%s); want %s (%s)", test.x, got, acc, test.want, test.acc)
-		}
-	}
-
-	// check that supplied *Int is used
-	for _, f := range []string{"0", "1", "-1", "1234"} {
-		x := makeFloat(f)
-		i := new(Int)
-		if res, _ := x.Int(i); res != i {
-			t.Errorf("(%s).Int is not using supplied *Int", f)
-		}
-	}
-}
-
-func TestFloatRat(t *testing.T) {
-	for _, test := range []struct {
-		x, want string
-		acc     Accuracy
-	}{
-		{"0", "0/1", Exact},
-		{"+0", "0/1", Exact},
-		{"-0", "0/1", Exact},
-		{"Inf", "nil", Below},
-		{"+Inf", "nil", Below},
-		{"-Inf", "nil", Above},
-		{"1", "1/1", Exact},
-		{"-1", "-1/1", Exact},
-		{"1.25", "5/4", Exact},
-		{"-1.25", "-5/4", Exact},
-		{"1e10", "10000000000/1", Exact},
-		{"1p10", "1024/1", Exact},
-		{"-1p-10", "-1/1024", Exact},
-		{"3.14159265", "7244019449799623199/2305843009213693952", Exact},
-	} {
-		x := makeFloat(test.x).SetPrec(64)
-		res, acc := x.Rat(nil)
-		got := "nil"
-		if res != nil {
-			got = res.String()
-		}
-		if got != test.want {
-			t.Errorf("%s: got %s; want %s", test.x, got, test.want)
-			continue
-		}
-		if acc != test.acc {
-			t.Errorf("%s: got %s; want %s", test.x, acc, test.acc)
-			continue
-		}
-
-		// inverse conversion
-		if res != nil {
-			got := new(Float).SetPrec(64).SetRat(res)
-			if got.Cmp(x) != 0 {
-				t.Errorf("%s: got %s; want %s", test.x, got, x)
-			}
-		}
-	}
-
-	// check that supplied *Rat is used
-	for _, f := range []string{"0", "1", "-1", "1234"} {
-		x := makeFloat(f)
-		r := new(Rat)
-		if res, _ := x.Rat(r); res != r {
-			t.Errorf("(%s).Rat is not using supplied *Rat", f)
-		}
-	}
-}
-
-func TestFloatAbs(t *testing.T) {
-	for _, test := range []string{
-		"0",
-		"1",
-		"1234",
-		"1.23e-2",
-		"1e-1000",
-		"1e1000",
-		"Inf",
-	} {
-		p := makeFloat(test)
-		a := new(Float).Abs(p)
-		if !alike(a, p) {
-			t.Errorf("%s: got %s; want %s", test, a.Text('g', 10), test)
-		}
-
-		n := makeFloat("-" + test)
-		a.Abs(n)
-		if !alike(a, p) {
-			t.Errorf("-%s: got %s; want %s", test, a.Text('g', 10), test)
-		}
-	}
-}
-
-func TestFloatNeg(t *testing.T) {
-	for _, test := range []string{
-		"0",
-		"1",
-		"1234",
-		"1.23e-2",
-		"1e-1000",
-		"1e1000",
-		"Inf",
-	} {
-		p1 := makeFloat(test)
-		n1 := makeFloat("-" + test)
-		n2 := new(Float).Neg(p1)
-		p2 := new(Float).Neg(n2)
-		if !alike(n2, n1) {
-			t.Errorf("%s: got %s; want %s", test, n2.Text('g', 10), n1.Text('g', 10))
-		}
-		if !alike(p2, p1) {
-			t.Errorf("%s: got %s; want %s", test, p2.Text('g', 10), p1.Text('g', 10))
-		}
-	}
-}
-
-func TestFloatInc(t *testing.T) {
-	const n = 10
-	for _, prec := range precList {
-		if 1<<prec < n {
-			continue // prec must be large enough to hold all numbers from 0 to n
-		}
-		var x, one Float
-		x.SetPrec(prec)
-		one.SetInt64(1)
-		for i := 0; i < n; i++ {
-			x.Add(&x, &one)
-		}
-		if x.Cmp(new(Float).SetInt64(n)) != 0 {
-			t.Errorf("prec = %d: got %s; want %d", prec, &x, n)
-		}
-	}
-}
-
-// Selected precisions with which to run various tests.
-var precList = [...]uint{1, 2, 5, 8, 10, 16, 23, 24, 32, 50, 53, 64, 100, 128, 500, 511, 512, 513, 1000, 10000}
-
-// Selected bits with which to run various tests.
-// Each entry is a list of bits representing a floating-point number (see fromBits).
-var bitsList = [...]Bits{
-	{},           // = 0
-	{0},          // = 1
-	{1},          // = 2
-	{-1},         // = 1/2
-	{10},         // = 2**10 == 1024
-	{-10},        // = 2**-10 == 1/1024
-	{100, 10, 1}, // = 2**100 + 2**10 + 2**1
-	{0, -1, -2, -10},
-	// TODO(gri) add more test cases
-}
-
-// TestFloatAdd tests Float.Add/Sub by comparing the result of a "manual"
-// addition/subtraction of arguments represented by Bits values with the
-// respective Float addition/subtraction for a variety of precisions
-// and rounding modes.
-func TestFloatAdd(t *testing.T) {
-	for _, xbits := range bitsList {
-		for _, ybits := range bitsList {
-			// exact values
-			x := xbits.Float()
-			y := ybits.Float()
-			zbits := xbits.add(ybits)
-			z := zbits.Float()
-
-			for i, mode := range [...]RoundingMode{ToZero, ToNearestEven, AwayFromZero} {
-				for _, prec := range precList {
-					got := new(Float).SetPrec(prec).SetMode(mode)
-					got.Add(x, y)
-					want := zbits.round(prec, mode)
-					if got.Cmp(want) != 0 {
-						t.Errorf("i = %d, prec = %d, %s:\n\t     %s %v\n\t+    %s %v\n\t=    %s\n\twant %s",
-							i, prec, mode, x, xbits, y, ybits, got, want)
-					}
-
-					got.Sub(z, x)
-					want = ybits.round(prec, mode)
-					if got.Cmp(want) != 0 {
-						t.Errorf("i = %d, prec = %d, %s:\n\t     %s %v\n\t-    %s %v\n\t=    %s\n\twant %s",
-							i, prec, mode, z, zbits, x, xbits, got, want)
-					}
-				}
-			}
-		}
-	}
-}
-
-// TestFloatAdd32 tests that Float.Add/Sub of numbers with
-// 24bit mantissa behaves like float32 addition/subtraction
-// (excluding denormal numbers).
-func TestFloatAdd32(t *testing.T) {
-	// chose base such that we cross the mantissa precision limit
-	const base = 1<<26 - 0x10 // 11...110000 (26 bits)
-	for d := 0; d <= 0x10; d++ {
-		for i := range [2]int{} {
-			x0, y0 := float64(base), float64(d)
-			if i&1 != 0 {
-				x0, y0 = y0, x0
-			}
-
-			x := NewFloat(x0)
-			y := NewFloat(y0)
-			z := new(Float).SetPrec(24)
-
-			z.Add(x, y)
-			got, acc := z.Float32()
-			want := float32(y0) + float32(x0)
-			if got != want || acc != Exact {
-				t.Errorf("d = %d: %g + %g = %g (%s); want %g (Exact)", d, x0, y0, got, acc, want)
-			}
-
-			z.Sub(z, y)
-			got, acc = z.Float32()
-			want = float32(want) - float32(y0)
-			if got != want || acc != Exact {
-				t.Errorf("d = %d: %g - %g = %g (%s); want %g (Exact)", d, x0+y0, y0, got, acc, want)
-			}
-		}
-	}
-}
-
-// TestFloatAdd64 tests that Float.Add/Sub of numbers with
-// 53bit mantissa behaves like float64 addition/subtraction.
-func TestFloatAdd64(t *testing.T) {
-	// chose base such that we cross the mantissa precision limit
-	const base = 1<<55 - 0x10 // 11...110000 (55 bits)
-	for d := 0; d <= 0x10; d++ {
-		for i := range [2]int{} {
-			x0, y0 := float64(base), float64(d)
-			if i&1 != 0 {
-				x0, y0 = y0, x0
-			}
-
-			x := NewFloat(x0)
-			y := NewFloat(y0)
-			z := new(Float).SetPrec(53)
-
-			z.Add(x, y)
-			got, acc := z.Float64()
-			want := x0 + y0
-			if got != want || acc != Exact {
-				t.Errorf("d = %d: %g + %g = %g (%s); want %g (Exact)", d, x0, y0, got, acc, want)
-			}
-
-			z.Sub(z, y)
-			got, acc = z.Float64()
-			want -= y0
-			if got != want || acc != Exact {
-				t.Errorf("d = %d: %g - %g = %g (%s); want %g (Exact)", d, x0+y0, y0, got, acc, want)
-			}
-		}
-	}
-}
-
-// TestFloatMul tests Float.Mul/Quo by comparing the result of a "manual"
-// multiplication/division of arguments represented by Bits values with the
-// respective Float multiplication/division for a variety of precisions
-// and rounding modes.
-func TestFloatMul(t *testing.T) {
-	for _, xbits := range bitsList {
-		for _, ybits := range bitsList {
-			// exact values
-			x := xbits.Float()
-			y := ybits.Float()
-			zbits := xbits.mul(ybits)
-			z := zbits.Float()
-
-			for i, mode := range [...]RoundingMode{ToZero, ToNearestEven, AwayFromZero} {
-				for _, prec := range precList {
-					got := new(Float).SetPrec(prec).SetMode(mode)
-					got.Mul(x, y)
-					want := zbits.round(prec, mode)
-					if got.Cmp(want) != 0 {
-						t.Errorf("i = %d, prec = %d, %s:\n\t     %s %v\n\t*    %s %v\n\t=    %s\n\twant %s",
-							i, prec, mode, x, xbits, y, ybits, got, want)
-					}
-
-					if x.Sign() == 0 {
-						continue // ignore div-0 case (not invertable)
-					}
-					got.Quo(z, x)
-					want = ybits.round(prec, mode)
-					if got.Cmp(want) != 0 {
-						t.Errorf("i = %d, prec = %d, %s:\n\t     %s %v\n\t/    %s %v\n\t=    %s\n\twant %s",
-							i, prec, mode, z, zbits, x, xbits, got, want)
-					}
-				}
-			}
-		}
-	}
-}
-
-// TestFloatMul64 tests that Float.Mul/Quo of numbers with
-// 53bit mantissa behaves like float64 multiplication/division.
-func TestFloatMul64(t *testing.T) {
-	for _, test := range []struct {
-		x, y float64
-	}{
-		{0, 0},
-		{0, 1},
-		{1, 1},
-		{1, 1.5},
-		{1.234, 0.5678},
-		{2.718281828, 3.14159265358979},
-		{2.718281828e10, 3.14159265358979e-32},
-		{1.0 / 3, 1e200},
-	} {
-		for i := range [8]int{} {
-			x0, y0 := test.x, test.y
-			if i&1 != 0 {
-				x0 = -x0
-			}
-			if i&2 != 0 {
-				y0 = -y0
-			}
-			if i&4 != 0 {
-				x0, y0 = y0, x0
-			}
-
-			x := NewFloat(x0)
-			y := NewFloat(y0)
-			z := new(Float).SetPrec(53)
-
-			z.Mul(x, y)
-			got, _ := z.Float64()
-			want := x0 * y0
-			if got != want {
-				t.Errorf("%g * %g = %g; want %g", x0, y0, got, want)
-			}
-
-			if y0 == 0 {
-				continue // avoid division-by-zero
-			}
-			z.Quo(z, y)
-			got, _ = z.Float64()
-			want /= y0
-			if got != want {
-				t.Errorf("%g / %g = %g; want %g", x0*y0, y0, got, want)
-			}
-		}
-	}
-}
-
-func TestIssue6866(t *testing.T) {
-	for _, prec := range precList {
-		two := new(Float).SetPrec(prec).SetInt64(2)
-		one := new(Float).SetPrec(prec).SetInt64(1)
-		three := new(Float).SetPrec(prec).SetInt64(3)
-		msix := new(Float).SetPrec(prec).SetInt64(-6)
-		psix := new(Float).SetPrec(prec).SetInt64(+6)
-
-		p := new(Float).SetPrec(prec)
-		z1 := new(Float).SetPrec(prec)
-		z2 := new(Float).SetPrec(prec)
-
-		// z1 = 2 + 1.0/3*-6
-		p.Quo(one, three)
-		p.Mul(p, msix)
-		z1.Add(two, p)
-
-		// z2 = 2 - 1.0/3*+6
-		p.Quo(one, three)
-		p.Mul(p, psix)
-		z2.Sub(two, p)
-
-		if z1.Cmp(z2) != 0 {
-			t.Fatalf("prec %d: got z1 = %s != z2 = %s; want z1 == z2\n", prec, z1, z2)
-		}
-		if z1.Sign() != 0 {
-			t.Errorf("prec %d: got z1 = %s; want 0", prec, z1)
-		}
-		if z2.Sign() != 0 {
-			t.Errorf("prec %d: got z2 = %s; want 0", prec, z2)
-		}
-	}
-}
-
-func TestFloatQuo(t *testing.T) {
-	// TODO(gri) make the test vary these precisions
-	preci := 200 // precision of integer part
-	precf := 20  // precision of fractional part
-
-	for i := 0; i < 8; i++ {
-		// compute accurate (not rounded) result z
-		bits := Bits{preci - 1}
-		if i&3 != 0 {
-			bits = append(bits, 0)
-		}
-		if i&2 != 0 {
-			bits = append(bits, -1)
-		}
-		if i&1 != 0 {
-			bits = append(bits, -precf)
-		}
-		z := bits.Float()
-
-		// compute accurate x as z*y
-		y := NewFloat(3.14159265358979323e123)
-
-		x := new(Float).SetPrec(z.Prec() + y.Prec()).SetMode(ToZero)
-		x.Mul(z, y)
-
-		// leave for debugging
-		// fmt.Printf("x = %s\ny = %s\nz = %s\n", x, y, z)
-
-		if got := x.Acc(); got != Exact {
-			t.Errorf("got acc = %s; want exact", got)
-		}
-
-		// round accurate z for a variety of precisions and
-		// modes and compare against result of x / y.
-		for _, mode := range [...]RoundingMode{ToZero, ToNearestEven, AwayFromZero} {
-			for d := -5; d < 5; d++ {
-				prec := uint(preci + d)
-				got := new(Float).SetPrec(prec).SetMode(mode).Quo(x, y)
-				want := bits.round(prec, mode)
-				if got.Cmp(want) != 0 {
-					t.Errorf("i = %d, prec = %d, %s:\n\t     %s\n\t/    %s\n\t=    %s\n\twant %s",
-						i, prec, mode, x, y, got, want)
-				}
-			}
-		}
-	}
-}
-
-// TestFloatQuoSmoke tests all divisions x/y for values x, y in the range [-n, +n];
-// it serves as a smoke test for basic correctness of division.
-func TestFloatQuoSmoke(t *testing.T) {
-	n := 1000
-	if testing.Short() {
-		n = 10
-	}
-
-	const dprec = 3         // max. precision variation
-	const prec = 10 + dprec // enough bits to hold n precisely
-	for x := -n; x <= n; x++ {
-		for y := -n; y < n; y++ {
-			if y == 0 {
-				continue
-			}
-
-			a := float64(x)
-			b := float64(y)
-			c := a / b
-
-			// vary operand precision (only ok as long as a, b can be represented correctly)
-			for ad := -dprec; ad <= dprec; ad++ {
-				for bd := -dprec; bd <= dprec; bd++ {
-					A := new(Float).SetPrec(uint(prec + ad)).SetFloat64(a)
-					B := new(Float).SetPrec(uint(prec + bd)).SetFloat64(b)
-					C := new(Float).SetPrec(53).Quo(A, B) // C has float64 mantissa width
-
-					cc, acc := C.Float64()
-					if cc != c {
-						t.Errorf("%g/%g = %s; want %.5g\n", a, b, C.Text('g', 5), c)
-						continue
-					}
-					if acc != Exact {
-						t.Errorf("%g/%g got %s result; want exact result", a, b, acc)
-					}
-				}
-			}
-		}
-	}
-}
-
-// TestFloatArithmeticSpecialValues tests that Float operations produce the
-// correct results for combinations of zero (±0), finite (±1 and ±2.71828),
-// and infinite (±Inf) operands.
-func TestFloatArithmeticSpecialValues(t *testing.T) {
-	zero := 0.0
-	args := []float64{math.Inf(-1), -2.71828, -1, -zero, zero, 1, 2.71828, math.Inf(1)}
-	xx := new(Float)
-	yy := new(Float)
-	got := new(Float)
-	want := new(Float)
-	for i := 0; i < 4; i++ {
-		for _, x := range args {
-			xx.SetFloat64(x)
-			// check conversion is correct
-			// (no need to do this for y, since we see exactly the
-			// same values there)
-			if got, acc := xx.Float64(); got != x || acc != Exact {
-				t.Errorf("Float(%g) == %g (%s)", x, got, acc)
-			}
-			for _, y := range args {
-				yy.SetFloat64(y)
-				var (
-					op string
-					z  float64
-					f  func(z, x, y *Float) *Float
-				)
-				switch i {
-				case 0:
-					op = "+"
-					z = x + y
-					f = (*Float).Add
-				case 1:
-					op = "-"
-					z = x - y
-					f = (*Float).Sub
-				case 2:
-					op = "*"
-					z = x * y
-					f = (*Float).Mul
-				case 3:
-					op = "/"
-					z = x / y
-					f = (*Float).Quo
-				default:
-					panic("unreachable")
-				}
-				var errnan bool // set if execution of f panicked with ErrNaN
-				// protect execution of f
-				func() {
-					defer func() {
-						if p := recover(); p != nil {
-							_ = p.(ErrNaN) // re-panic if not ErrNaN
-							errnan = true
-						}
-					}()
-					f(got, xx, yy)
-				}()
-				if math.IsNaN(z) {
-					if !errnan {
-						t.Errorf("%5g %s %5g = %5s; want ErrNaN panic", x, op, y, got)
-					}
-					continue
-				}
-				if errnan {
-					t.Errorf("%5g %s %5g panicked with ErrNan; want %5s", x, op, y, want)
-					continue
-				}
-				want.SetFloat64(z)
-				if !alike(got, want) {
-					t.Errorf("%5g %s %5g = %5s; want %5s", x, op, y, got, want)
-				}
-			}
-		}
-	}
-}
-
-func TestFloatArithmeticOverflow(t *testing.T) {
-	for _, test := range []struct {
-		prec       uint
-		mode       RoundingMode
-		op         byte
-		x, y, want string
-		acc        Accuracy
-	}{
-		{4, ToNearestEven, '+', "0", "0", "0", Exact},                   // smoke test
-		{4, ToNearestEven, '+', "0x.8p+0", "0x.8p+0", "0x.8p+1", Exact}, // smoke test
-
-		{4, ToNearestEven, '+', "0", "0x.8p2147483647", "0x.8p+2147483647", Exact},
-		{4, ToNearestEven, '+', "0x.8p2147483500", "0x.8p2147483647", "0x.8p+2147483647", Below}, // rounded to zero
-		{4, ToNearestEven, '+', "0x.8p2147483647", "0x.8p2147483647", "+Inf", Above},             // exponent overflow in +
-		{4, ToNearestEven, '+', "-0x.8p2147483647", "-0x.8p2147483647", "-Inf", Below},           // exponent overflow in +
-		{4, ToNearestEven, '-', "-0x.8p2147483647", "0x.8p2147483647", "-Inf", Below},            // exponent overflow in -
-
-		{4, ToZero, '+', "0x.fp2147483647", "0x.8p2147483643", "0x.fp+2147483647", Below}, // rounded to zero
-		{4, ToNearestEven, '+', "0x.fp2147483647", "0x.8p2147483643", "+Inf", Above},      // exponent overflow in rounding
-		{4, AwayFromZero, '+', "0x.fp2147483647", "0x.8p2147483643", "+Inf", Above},       // exponent overflow in rounding
-
-		{4, AwayFromZero, '-', "-0x.fp2147483647", "0x.8p2147483644", "-Inf", Below},        // exponent overflow in rounding
-		{4, ToNearestEven, '-', "-0x.fp2147483647", "0x.8p2147483643", "-Inf", Below},       // exponent overflow in rounding
-		{4, ToZero, '-', "-0x.fp2147483647", "0x.8p2147483643", "-0x.fp+2147483647", Above}, // rounded to zero
-
-		{4, ToNearestEven, '+', "0", "0x.8p-2147483648", "0x.8p-2147483648", Exact},
-		{4, ToNearestEven, '+', "0x.8p-2147483648", "0x.8p-2147483648", "0x.8p-2147483647", Exact},
-
-		{4, ToNearestEven, '*', "1", "0x.8p2147483647", "0x.8p+2147483647", Exact},
-		{4, ToNearestEven, '*', "2", "0x.8p2147483647", "+Inf", Above},  // exponent overflow in *
-		{4, ToNearestEven, '*', "-2", "0x.8p2147483647", "-Inf", Below}, // exponent overflow in *
-
-		{4, ToNearestEven, '/', "0.5", "0x.8p2147483647", "0x.8p-2147483646", Exact},
-		{4, ToNearestEven, '/', "0x.8p+0", "0x.8p2147483647", "0x.8p-2147483646", Exact},
-		{4, ToNearestEven, '/', "0x.8p-1", "0x.8p2147483647", "0x.8p-2147483647", Exact},
-		{4, ToNearestEven, '/', "0x.8p-2", "0x.8p2147483647", "0x.8p-2147483648", Exact},
-		{4, ToNearestEven, '/', "0x.8p-3", "0x.8p2147483647", "0", Below}, // exponent underflow in /
-	} {
-		x := makeFloat(test.x)
-		y := makeFloat(test.y)
-		z := new(Float).SetPrec(test.prec).SetMode(test.mode)
-		switch test.op {
-		case '+':
-			z.Add(x, y)
-		case '-':
-			z.Sub(x, y)
-		case '*':
-			z.Mul(x, y)
-		case '/':
-			z.Quo(x, y)
-		default:
-			panic("unreachable")
-		}
-		if got := z.Text('p', 0); got != test.want || z.Acc() != test.acc {
-			t.Errorf(
-				"prec = %d (%s): %s %c %s = %s (%s); want %s (%s)",
-				test.prec, test.mode, x.Text('p', 0), test.op, y.Text('p', 0), got, z.Acc(), test.want, test.acc,
-			)
-		}
-	}
-}
-
-// TODO(gri) Add tests that check correctness in the presence of aliasing.
-
-// For rounding modes ToNegativeInf and ToPositiveInf, rounding is affected
-// by the sign of the value to be rounded. Test that rounding happens after
-// the sign of a result has been set.
-// This test uses specific values that are known to fail if rounding is
-// "factored" out before setting the result sign.
-func TestFloatArithmeticRounding(t *testing.T) {
-	for _, test := range []struct {
-		mode       RoundingMode
-		prec       uint
-		x, y, want int64
-		op         byte
-	}{
-		{ToZero, 3, -0x8, -0x1, -0x8, '+'},
-		{AwayFromZero, 3, -0x8, -0x1, -0xa, '+'},
-		{ToNegativeInf, 3, -0x8, -0x1, -0xa, '+'},
-
-		{ToZero, 3, -0x8, 0x1, -0x8, '-'},
-		{AwayFromZero, 3, -0x8, 0x1, -0xa, '-'},
-		{ToNegativeInf, 3, -0x8, 0x1, -0xa, '-'},
-
-		{ToZero, 3, -0x9, 0x1, -0x8, '*'},
-		{AwayFromZero, 3, -0x9, 0x1, -0xa, '*'},
-		{ToNegativeInf, 3, -0x9, 0x1, -0xa, '*'},
-
-		{ToZero, 3, -0x9, 0x1, -0x8, '/'},
-		{AwayFromZero, 3, -0x9, 0x1, -0xa, '/'},
-		{ToNegativeInf, 3, -0x9, 0x1, -0xa, '/'},
-	} {
-		var x, y, z Float
-		x.SetInt64(test.x)
-		y.SetInt64(test.y)
-		z.SetPrec(test.prec).SetMode(test.mode)
-		switch test.op {
-		case '+':
-			z.Add(&x, &y)
-		case '-':
-			z.Sub(&x, &y)
-		case '*':
-			z.Mul(&x, &y)
-		case '/':
-			z.Quo(&x, &y)
-		default:
-			panic("unreachable")
-		}
-		if got, acc := z.Int64(); got != test.want || acc != Exact {
-			t.Errorf("%s, %d bits: %d %c %d = %d (%s); want %d (Exact)",
-				test.mode, test.prec, test.x, test.op, test.y, got, acc, test.want,
-			)
-		}
-	}
-}
-
-// TestFloatCmpSpecialValues tests that Cmp produces the correct results for
-// combinations of zero (±0), finite (±1 and ±2.71828), and infinite (±Inf)
-// operands.
-func TestFloatCmpSpecialValues(t *testing.T) {
-	zero := 0.0
-	args := []float64{math.Inf(-1), -2.71828, -1, -zero, zero, 1, 2.71828, math.Inf(1)}
-	xx := new(Float)
-	yy := new(Float)
-	for i := 0; i < 4; i++ {
-		for _, x := range args {
-			xx.SetFloat64(x)
-			// check conversion is correct
-			// (no need to do this for y, since we see exactly the
-			// same values there)
-			if got, acc := xx.Float64(); got != x || acc != Exact {
-				t.Errorf("Float(%g) == %g (%s)", x, got, acc)
-			}
-			for _, y := range args {
-				yy.SetFloat64(y)
-				got := xx.Cmp(yy)
-				want := 0
-				switch {
-				case x < y:
-					want = -1
-				case x > y:
-					want = +1
-				}
-				if got != want {
-					t.Errorf("(%g).Cmp(%g) = %v; want %v", x, y, got, want)
-				}
-			}
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/floatconv.go b/src/cmd/compile/internal/big/floatconv.go
deleted file mode 100644
index a884df6..0000000
--- a/src/cmd/compile/internal/big/floatconv.go
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements string-to-Float conversion functions.
-
-package big
-
-import (
-	"fmt"
-	"io"
-	"strings"
-)
-
-// SetString sets z to the value of s and returns z and a boolean indicating
-// success. s must be a floating-point number of the same format as accepted
-// by Parse, with base argument 0.
-func (z *Float) SetString(s string) (*Float, bool) {
-	if f, _, err := z.Parse(s, 0); err == nil {
-		return f, true
-	}
-	return nil, false
-}
-
-// scan is like Parse but reads the longest possible prefix representing a valid
-// floating point number from an io.ByteScanner rather than a string. It serves
-// as the implementation of Parse. It does not recognize ±Inf and does not expect
-// EOF at the end.
-func (z *Float) scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
-	prec := z.prec
-	if prec == 0 {
-		prec = 64
-	}
-
-	// A reasonable value in case of an error.
-	z.form = zero
-
-	// sign
-	z.neg, err = scanSign(r)
-	if err != nil {
-		return
-	}
-
-	// mantissa
-	var fcount int // fractional digit count; valid if <= 0
-	z.mant, b, fcount, err = z.mant.scan(r, base, true)
-	if err != nil {
-		return
-	}
-
-	// exponent
-	var exp int64
-	var ebase int
-	exp, ebase, err = scanExponent(r, true)
-	if err != nil {
-		return
-	}
-
-	// special-case 0
-	if len(z.mant) == 0 {
-		z.prec = prec
-		z.acc = Exact
-		z.form = zero
-		f = z
-		return
-	}
-	// len(z.mant) > 0
-
-	// The mantissa may have a decimal point (fcount <= 0) and there
-	// may be a nonzero exponent exp. The decimal point amounts to a
-	// division by b**(-fcount). An exponent means multiplication by
-	// ebase**exp. Finally, mantissa normalization (shift left) requires
-	// a correcting multiplication by 2**(-shiftcount). Multiplications
-	// are commutative, so we can apply them in any order as long as there
-	// is no loss of precision. We only have powers of 2 and 10, and
-	// we split powers of 10 into the product of the same powers of
-	// 2 and 5. This reduces the size of the multiplication factor
-	// needed for base-10 exponents.
-
-	// normalize mantissa and determine initial exponent contributions
-	exp2 := int64(len(z.mant))*_W - fnorm(z.mant)
-	exp5 := int64(0)
-
-	// determine binary or decimal exponent contribution of decimal point
-	if fcount < 0 {
-		// The mantissa has a "decimal" point ddd.dddd; and
-		// -fcount is the number of digits to the right of '.'.
-		// Adjust relevant exponent accordingly.
-		d := int64(fcount)
-		switch b {
-		case 10:
-			exp5 = d
-			fallthrough // 10**e == 5**e * 2**e
-		case 2:
-			exp2 += d
-		case 16:
-			exp2 += d * 4 // hexadecimal digits are 4 bits each
-		default:
-			panic("unexpected mantissa base")
-		}
-		// fcount consumed - not needed anymore
-	}
-
-	// take actual exponent into account
-	switch ebase {
-	case 10:
-		exp5 += exp
-		fallthrough
-	case 2:
-		exp2 += exp
-	default:
-		panic("unexpected exponent base")
-	}
-	// exp consumed - not needed anymore
-
-	// apply 2**exp2
-	if MinExp <= exp2 && exp2 <= MaxExp {
-		z.prec = prec
-		z.form = finite
-		z.exp = int32(exp2)
-		f = z
-	} else {
-		err = fmt.Errorf("exponent overflow")
-		return
-	}
-
-	if exp5 == 0 {
-		// no decimal exponent contribution
-		z.round(0)
-		return
-	}
-	// exp5 != 0
-
-	// apply 5**exp5
-	p := new(Float).SetPrec(z.Prec() + 64) // use more bits for p -- TODO(gri) what is the right number?
-	if exp5 < 0 {
-		z.Quo(z, p.pow5(uint64(-exp5)))
-	} else {
-		z.Mul(z, p.pow5(uint64(exp5)))
-	}
-
-	return
-}
-
-// These powers of 5 fit into a uint64.
-//
-//	for p, q := uint64(0), uint64(1); p < q; p, q = q, q*5 {
-//		fmt.Println(q)
-//	}
-//
-var pow5tab = [...]uint64{
-	1,
-	5,
-	25,
-	125,
-	625,
-	3125,
-	15625,
-	78125,
-	390625,
-	1953125,
-	9765625,
-	48828125,
-	244140625,
-	1220703125,
-	6103515625,
-	30517578125,
-	152587890625,
-	762939453125,
-	3814697265625,
-	19073486328125,
-	95367431640625,
-	476837158203125,
-	2384185791015625,
-	11920928955078125,
-	59604644775390625,
-	298023223876953125,
-	1490116119384765625,
-	7450580596923828125,
-}
-
-// pow5 sets z to 5**n and returns z.
-// n must not be negative.
-func (z *Float) pow5(n uint64) *Float {
-	const m = uint64(len(pow5tab) - 1)
-	if n <= m {
-		return z.SetUint64(pow5tab[n])
-	}
-	// n > m
-
-	z.SetUint64(pow5tab[m])
-	n -= m
-
-	// use more bits for f than for z
-	// TODO(gri) what is the right number?
-	f := new(Float).SetPrec(z.Prec() + 64).SetUint64(5)
-
-	for n > 0 {
-		if n&1 != 0 {
-			z.Mul(z, f)
-		}
-		f.Mul(f, f)
-		n >>= 1
-	}
-
-	return z
-}
-
-// Parse parses s which must contain a text representation of a floating-
-// point number with a mantissa in the given conversion base (the exponent
-// is always a decimal number), or a string representing an infinite value.
-//
-// It sets z to the (possibly rounded) value of the corresponding floating-
-// point value, and returns z, the actual base b, and an error err, if any.
-// If z's precision is 0, it is changed to 64 before rounding takes effect.
-// The number must be of the form:
-//
-//	number   = [ sign ] [ prefix ] mantissa [ exponent ] | infinity .
-//	sign     = "+" | "-" .
-//      prefix   = "0" ( "x" | "X" | "b" | "B" ) .
-//	mantissa = digits | digits "." [ digits ] | "." digits .
-//	exponent = ( "E" | "e" | "p" ) [ sign ] digits .
-//	digits   = digit { digit } .
-//	digit    = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
-//      infinity = [ sign ] ( "inf" | "Inf" ) .
-//
-// The base argument must be 0, 2, 10, or 16. Providing an invalid base
-// argument will lead to a run-time panic.
-//
-// For base 0, the number prefix determines the actual base: A prefix of
-// "0x" or "0X" selects base 16, and a "0b" or "0B" prefix selects
-// base 2; otherwise, the actual base is 10 and no prefix is accepted.
-// The octal prefix "0" is not supported (a leading "0" is simply
-// considered a "0").
-//
-// A "p" exponent indicates a binary (rather then decimal) exponent;
-// for instance "0x1.fffffffffffffp1023" (using base 0) represents the
-// maximum float64 value. For hexadecimal mantissae, the exponent must
-// be binary, if present (an "e" or "E" exponent indicator cannot be
-// distinguished from a mantissa digit).
-//
-// The returned *Float f is nil and the value of z is valid but not
-// defined if an error is reported.
-//
-func (z *Float) Parse(s string, base int) (f *Float, b int, err error) {
-	// scan doesn't handle ±Inf
-	if len(s) == 3 && (s == "Inf" || s == "inf") {
-		f = z.SetInf(false)
-		return
-	}
-	if len(s) == 4 && (s[0] == '+' || s[0] == '-') && (s[1:] == "Inf" || s[1:] == "inf") {
-		f = z.SetInf(s[0] == '-')
-		return
-	}
-
-	r := strings.NewReader(s)
-	if f, b, err = z.scan(r, base); err != nil {
-		return
-	}
-
-	// entire string must have been consumed
-	if ch, err2 := r.ReadByte(); err2 == nil {
-		err = fmt.Errorf("expected end of string, found %q", ch)
-	} else if err2 != io.EOF {
-		err = err2
-	}
-
-	return
-}
-
-// ParseFloat is like f.Parse(s, base) with f set to the given precision
-// and rounding mode.
-func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) {
-	return new(Float).SetPrec(prec).SetMode(mode).Parse(s, base)
-}
diff --git a/src/cmd/compile/internal/big/floatconv_test.go b/src/cmd/compile/internal/big/floatconv_test.go
deleted file mode 100644
index b6f9993..0000000
--- a/src/cmd/compile/internal/big/floatconv_test.go
+++ /dev/null
@@ -1,662 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"fmt"
-	"math"
-	"strconv"
-	"testing"
-)
-
-func TestFloatSetFloat64String(t *testing.T) {
-	inf := math.Inf(0)
-	nan := math.NaN()
-
-	for _, test := range []struct {
-		s string
-		x float64 // NaNs represent invalid inputs
-	}{
-		// basics
-		{"0", 0},
-		{"-0", -0},
-		{"+0", 0},
-		{"1", 1},
-		{"-1", -1},
-		{"+1", 1},
-		{"1.234", 1.234},
-		{"-1.234", -1.234},
-		{"+1.234", 1.234},
-		{".1", 0.1},
-		{"1.", 1},
-		{"+1.", 1},
-
-		// various zeros
-		{"0e100", 0},
-		{"-0e+100", 0},
-		{"+0e-100", 0},
-		{"0E100", 0},
-		{"-0E+100", 0},
-		{"+0E-100", 0},
-
-		// various decimal exponent formats
-		{"1.e10", 1e10},
-		{"1e+10", 1e10},
-		{"+1e-10", 1e-10},
-		{"1E10", 1e10},
-		{"1.E+10", 1e10},
-		{"+1E-10", 1e-10},
-
-		// infinities
-		{"Inf", inf},
-		{"+Inf", inf},
-		{"-Inf", -inf},
-		{"inf", inf},
-		{"+inf", inf},
-		{"-inf", -inf},
-
-		// invalid numbers
-		{"", nan},
-		{"-", nan},
-		{"0x", nan},
-		{"0e", nan},
-		{"1.2ef", nan},
-		{"2..3", nan},
-		{"123..", nan},
-		{"infinity", nan},
-		{"foobar", nan},
-
-		// misc decimal values
-		{"3.14159265", 3.14159265},
-		{"-687436.79457e-245", -687436.79457e-245},
-		{"-687436.79457E245", -687436.79457e245},
-		{".0000000000000000000000000000000000000001", 1e-40},
-		{"+10000000000000000000000000000000000000000e-0", 1e40},
-
-		// decimal mantissa, binary exponent
-		{"0p0", 0},
-		{"-0p0", -0},
-		{"1p10", 1 << 10},
-		{"1p+10", 1 << 10},
-		{"+1p-10", 1.0 / (1 << 10)},
-		{"1024p-12", 0.25},
-		{"-1p10", -1024},
-		{"1.5p1", 3},
-
-		// binary mantissa, decimal exponent
-		{"0b0", 0},
-		{"-0b0", -0},
-		{"0b0e+10", 0},
-		{"-0b0e-10", -0},
-		{"0b1010", 10},
-		{"0B1010E2", 1000},
-		{"0b.1", 0.5},
-		{"0b.001", 0.125},
-		{"0b.001e3", 125},
-
-		// binary mantissa, binary exponent
-		{"0b0p+10", 0},
-		{"-0b0p-10", -0},
-		{"0b.1010p4", 10},
-		{"0b1p-1", 0.5},
-		{"0b001p-3", 0.125},
-		{"0b.001p3", 1},
-		{"0b0.01p2", 1},
-
-		// hexadecimal mantissa and exponent
-		{"0x0", 0},
-		{"-0x0", -0},
-		{"0x0p+10", 0},
-		{"-0x0p-10", -0},
-		{"0xff", 255},
-		{"0X.8p1", 1},
-		{"-0X0.00008p16", -0.5},
-		{"0x0.0000000000001p-1022", math.SmallestNonzeroFloat64},
-		{"0x1.fffffffffffffp1023", math.MaxFloat64},
-	} {
-		var x Float
-		x.SetPrec(53)
-		_, ok := x.SetString(test.s)
-		if math.IsNaN(test.x) {
-			// test.s is invalid
-			if ok {
-				t.Errorf("%s: want parse error", test.s)
-			}
-			continue
-		}
-		// test.s is valid
-		if !ok {
-			t.Errorf("%s: got parse error", test.s)
-			continue
-		}
-		f, _ := x.Float64()
-		want := new(Float).SetFloat64(test.x)
-		if x.Cmp(want) != 0 {
-			t.Errorf("%s: got %s (%v); want %v", test.s, &x, f, test.x)
-		}
-	}
-}
-
-func fdiv(a, b float64) float64 { return a / b }
-
-const (
-	below1e23 = 99999999999999974834176
-	above1e23 = 100000000000000008388608
-)
-
-func TestFloat64Text(t *testing.T) {
-	for _, test := range []struct {
-		x      float64
-		format byte
-		prec   int
-		want   string
-	}{
-		{0, 'f', 0, "0"},
-		{math.Copysign(0, -1), 'f', 0, "-0"},
-		{1, 'f', 0, "1"},
-		{-1, 'f', 0, "-1"},
-
-		{0.001, 'e', 0, "1e-03"},
-		{0.459, 'e', 0, "5e-01"},
-		{1.459, 'e', 0, "1e+00"},
-		{2.459, 'e', 1, "2.5e+00"},
-		{3.459, 'e', 2, "3.46e+00"},
-		{4.459, 'e', 3, "4.459e+00"},
-		{5.459, 'e', 4, "5.4590e+00"},
-
-		{0.001, 'f', 0, "0"},
-		{0.459, 'f', 0, "0"},
-		{1.459, 'f', 0, "1"},
-		{2.459, 'f', 1, "2.5"},
-		{3.459, 'f', 2, "3.46"},
-		{4.459, 'f', 3, "4.459"},
-		{5.459, 'f', 4, "5.4590"},
-
-		{0, 'b', 0, "0"},
-		{math.Copysign(0, -1), 'b', 0, "-0"},
-		{1.0, 'b', 0, "4503599627370496p-52"},
-		{-1.0, 'b', 0, "-4503599627370496p-52"},
-		{4503599627370496, 'b', 0, "4503599627370496p+0"},
-
-		{0, 'p', 0, "0"},
-		{math.Copysign(0, -1), 'p', 0, "-0"},
-		{1024.0, 'p', 0, "0x.8p+11"},
-		{-1024.0, 'p', 0, "-0x.8p+11"},
-
-		// all test cases below from strconv/ftoa_test.go
-		{1, 'e', 5, "1.00000e+00"},
-		{1, 'f', 5, "1.00000"},
-		{1, 'g', 5, "1"},
-		{1, 'g', -1, "1"},
-		{20, 'g', -1, "20"},
-		{1234567.8, 'g', -1, "1.2345678e+06"},
-		{200000, 'g', -1, "200000"},
-		{2000000, 'g', -1, "2e+06"},
-
-		// g conversion and zero suppression
-		{400, 'g', 2, "4e+02"},
-		{40, 'g', 2, "40"},
-		{4, 'g', 2, "4"},
-		{.4, 'g', 2, "0.4"},
-		{.04, 'g', 2, "0.04"},
-		{.004, 'g', 2, "0.004"},
-		{.0004, 'g', 2, "0.0004"},
-		{.00004, 'g', 2, "4e-05"},
-		{.000004, 'g', 2, "4e-06"},
-
-		{0, 'e', 5, "0.00000e+00"},
-		{0, 'f', 5, "0.00000"},
-		{0, 'g', 5, "0"},
-		{0, 'g', -1, "0"},
-
-		{-1, 'e', 5, "-1.00000e+00"},
-		{-1, 'f', 5, "-1.00000"},
-		{-1, 'g', 5, "-1"},
-		{-1, 'g', -1, "-1"},
-
-		{12, 'e', 5, "1.20000e+01"},
-		{12, 'f', 5, "12.00000"},
-		{12, 'g', 5, "12"},
-		{12, 'g', -1, "12"},
-
-		{123456700, 'e', 5, "1.23457e+08"},
-		{123456700, 'f', 5, "123456700.00000"},
-		{123456700, 'g', 5, "1.2346e+08"},
-		{123456700, 'g', -1, "1.234567e+08"},
-
-		{1.2345e6, 'e', 5, "1.23450e+06"},
-		{1.2345e6, 'f', 5, "1234500.00000"},
-		{1.2345e6, 'g', 5, "1.2345e+06"},
-
-		{1e23, 'e', 17, "9.99999999999999916e+22"},
-		{1e23, 'f', 17, "99999999999999991611392.00000000000000000"},
-		{1e23, 'g', 17, "9.9999999999999992e+22"},
-
-		{1e23, 'e', -1, "1e+23"},
-		{1e23, 'f', -1, "100000000000000000000000"},
-		{1e23, 'g', -1, "1e+23"},
-
-		{below1e23, 'e', 17, "9.99999999999999748e+22"},
-		{below1e23, 'f', 17, "99999999999999974834176.00000000000000000"},
-		{below1e23, 'g', 17, "9.9999999999999975e+22"},
-
-		{below1e23, 'e', -1, "9.999999999999997e+22"},
-		{below1e23, 'f', -1, "99999999999999970000000"},
-		{below1e23, 'g', -1, "9.999999999999997e+22"},
-
-		{above1e23, 'e', 17, "1.00000000000000008e+23"},
-		{above1e23, 'f', 17, "100000000000000008388608.00000000000000000"},
-		{above1e23, 'g', 17, "1.0000000000000001e+23"},
-
-		{above1e23, 'e', -1, "1.0000000000000001e+23"},
-		{above1e23, 'f', -1, "100000000000000010000000"},
-		{above1e23, 'g', -1, "1.0000000000000001e+23"},
-
-		{5e-304 / 1e20, 'g', -1, "5e-324"},
-		{-5e-304 / 1e20, 'g', -1, "-5e-324"},
-		{fdiv(5e-304, 1e20), 'g', -1, "5e-324"},   // avoid constant arithmetic
-		{fdiv(-5e-304, 1e20), 'g', -1, "-5e-324"}, // avoid constant arithmetic
-
-		{32, 'g', -1, "32"},
-		{32, 'g', 0, "3e+01"},
-
-		{100, 'x', -1, "%x"},
-
-		// {math.NaN(), 'g', -1, "NaN"},  // Float doesn't support NaNs
-		// {-math.NaN(), 'g', -1, "NaN"}, // Float doesn't support NaNs
-		{math.Inf(0), 'g', -1, "+Inf"},
-		{math.Inf(-1), 'g', -1, "-Inf"},
-		{-math.Inf(0), 'g', -1, "-Inf"},
-
-		{-1, 'b', -1, "-4503599627370496p-52"},
-
-		// fixed bugs
-		{0.9, 'f', 1, "0.9"},
-		{0.09, 'f', 1, "0.1"},
-		{0.0999, 'f', 1, "0.1"},
-		{0.05, 'f', 1, "0.1"},
-		{0.05, 'f', 0, "0"},
-		{0.5, 'f', 1, "0.5"},
-		{0.5, 'f', 0, "0"},
-		{1.5, 'f', 0, "2"},
-
-		// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
-		{2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"},
-		// http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
-		{2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"},
-
-		// Issue 2625.
-		{383260575764816448, 'f', 0, "383260575764816448"},
-		{383260575764816448, 'g', -1, "3.8326057576481645e+17"},
-	} {
-		// The test cases are from the strconv package which tests float64 values.
-		// When formatting values with prec = -1 (shortest representation),
-		// the actually available mantissa precision matters.
-		// For denormalized values, that precision is < 53 (SetFloat64 default).
-		// Compute and set the actual precision explicitly.
-		f := new(Float).SetPrec(actualPrec(test.x)).SetFloat64(test.x)
-		got := f.Text(test.format, test.prec)
-		if got != test.want {
-			t.Errorf("%v: got %s; want %s", test, got, test.want)
-			continue
-		}
-
-		if test.format == 'b' && test.x == 0 {
-			continue // 'b' format in strconv.Float requires knowledge of bias for 0.0
-		}
-		if test.format == 'p' {
-			continue // 'p' format not supported in strconv.Format
-		}
-
-		// verify that Float format matches strconv format
-		want := strconv.FormatFloat(test.x, test.format, test.prec, 64)
-		if got != want {
-			t.Errorf("%v: got %s; want %s (strconv)", test, got, want)
-		}
-	}
-}
-
-// actualPrec returns the number of actually used mantissa bits.
-func actualPrec(x float64) uint {
-	if bits := math.Float64bits(x); x != 0 && bits&(0x7ff<<52) == 0 {
-		// x is denormalized
-		return 64 - nlz64(bits&(1<<52-1))
-	}
-	return 53
-}
-
-func TestFloatText(t *testing.T) {
-	for _, test := range []struct {
-		x      string
-		prec   uint
-		format byte
-		digits int
-		want   string
-	}{
-		{"0", 10, 'f', 0, "0"},
-		{"-0", 10, 'f', 0, "-0"},
-		{"1", 10, 'f', 0, "1"},
-		{"-1", 10, 'f', 0, "-1"},
-
-		{"1.459", 100, 'e', 0, "1e+00"},
-		{"2.459", 100, 'e', 1, "2.5e+00"},
-		{"3.459", 100, 'e', 2, "3.46e+00"},
-		{"4.459", 100, 'e', 3, "4.459e+00"},
-		{"5.459", 100, 'e', 4, "5.4590e+00"},
-
-		{"1.459", 100, 'E', 0, "1E+00"},
-		{"2.459", 100, 'E', 1, "2.5E+00"},
-		{"3.459", 100, 'E', 2, "3.46E+00"},
-		{"4.459", 100, 'E', 3, "4.459E+00"},
-		{"5.459", 100, 'E', 4, "5.4590E+00"},
-
-		{"1.459", 100, 'f', 0, "1"},
-		{"2.459", 100, 'f', 1, "2.5"},
-		{"3.459", 100, 'f', 2, "3.46"},
-		{"4.459", 100, 'f', 3, "4.459"},
-		{"5.459", 100, 'f', 4, "5.4590"},
-
-		{"1.459", 100, 'g', 0, "1"},
-		{"2.459", 100, 'g', 1, "2"},
-		{"3.459", 100, 'g', 2, "3.5"},
-		{"4.459", 100, 'g', 3, "4.46"},
-		{"5.459", 100, 'g', 4, "5.459"},
-
-		{"1459", 53, 'g', 0, "1e+03"},
-		{"2459", 53, 'g', 1, "2e+03"},
-		{"3459", 53, 'g', 2, "3.5e+03"},
-		{"4459", 53, 'g', 3, "4.46e+03"},
-		{"5459", 53, 'g', 4, "5459"},
-
-		{"1459", 53, 'G', 0, "1E+03"},
-		{"2459", 53, 'G', 1, "2E+03"},
-		{"3459", 53, 'G', 2, "3.5E+03"},
-		{"4459", 53, 'G', 3, "4.46E+03"},
-		{"5459", 53, 'G', 4, "5459"},
-
-		{"3", 10, 'e', 40, "3.0000000000000000000000000000000000000000e+00"},
-		{"3", 10, 'f', 40, "3.0000000000000000000000000000000000000000"},
-		{"3", 10, 'g', 40, "3"},
-
-		{"3e40", 100, 'e', 40, "3.0000000000000000000000000000000000000000e+40"},
-		{"3e40", 100, 'f', 4, "30000000000000000000000000000000000000000.0000"},
-		{"3e40", 100, 'g', 40, "3e+40"},
-
-		// make sure "stupid" exponents don't stall the machine
-		{"1e1000000", 64, 'p', 0, "0x.88b3a28a05eade3ap+3321929"},
-		{"1e646456992", 64, 'p', 0, "0x.e883a0c5c8c7c42ap+2147483644"},
-		{"1e646456993", 64, 'p', 0, "+Inf"},
-		{"1e1000000000", 64, 'p', 0, "+Inf"},
-		{"1e-1000000", 64, 'p', 0, "0x.efb4542cc8ca418ap-3321928"},
-		{"1e-646456993", 64, 'p', 0, "0x.e17c8956983d9d59p-2147483647"},
-		{"1e-646456994", 64, 'p', 0, "0"},
-		{"1e-1000000000", 64, 'p', 0, "0"},
-
-		// minimum and maximum values
-		{"1p2147483646", 64, 'p', 0, "0x.8p+2147483647"},
-		{"0x.8p2147483647", 64, 'p', 0, "0x.8p+2147483647"},
-		{"0x.8p-2147483647", 64, 'p', 0, "0x.8p-2147483647"},
-		{"1p-2147483649", 64, 'p', 0, "0x.8p-2147483648"},
-
-		// TODO(gri) need tests for actual large Floats
-
-		{"0", 53, 'b', 0, "0"},
-		{"-0", 53, 'b', 0, "-0"},
-		{"1.0", 53, 'b', 0, "4503599627370496p-52"},
-		{"-1.0", 53, 'b', 0, "-4503599627370496p-52"},
-		{"4503599627370496", 53, 'b', 0, "4503599627370496p+0"},
-
-		// issue 9939
-		{"3", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"},
-		{"03", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"},
-		{"3.", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"},
-		{"3.0", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"},
-		{"3.00", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"},
-		{"3.000", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"},
-
-		{"3", 350, 'p', 0, "0x.cp+2"},
-		{"03", 350, 'p', 0, "0x.cp+2"},
-		{"3.", 350, 'p', 0, "0x.cp+2"},
-		{"3.0", 350, 'p', 0, "0x.cp+2"},
-		{"3.00", 350, 'p', 0, "0x.cp+2"},
-		{"3.000", 350, 'p', 0, "0x.cp+2"},
-
-		{"0", 64, 'p', 0, "0"},
-		{"-0", 64, 'p', 0, "-0"},
-		{"1024.0", 64, 'p', 0, "0x.8p+11"},
-		{"-1024.0", 64, 'p', 0, "-0x.8p+11"},
-
-		// unsupported format
-		{"3.14", 64, 'x', 0, "%x"},
-		{"-3.14", 64, 'x', 0, "%x"},
-	} {
-		f, _, err := ParseFloat(test.x, 0, test.prec, ToNearestEven)
-		if err != nil {
-			t.Errorf("%v: %s", test, err)
-			continue
-		}
-
-		got := f.Text(test.format, test.digits)
-		if got != test.want {
-			t.Errorf("%v: got %s; want %s", test, got, test.want)
-		}
-
-		// compare with strconv.FormatFloat output if possible
-		// ('p' format is not supported by strconv.FormatFloat,
-		// and its output for 0.0 prints a biased exponent value
-		// as in 0p-1074 which makes no sense to emulate here)
-		if test.prec == 53 && test.format != 'p' && f.Sign() != 0 {
-			f64, acc := f.Float64()
-			if acc != Exact {
-				t.Errorf("%v: expected exact conversion to float64", test)
-				continue
-			}
-			got := strconv.FormatFloat(f64, test.format, test.digits, 64)
-			if got != test.want {
-				t.Errorf("%v: got %s; want %s", test, got, test.want)
-			}
-		}
-	}
-}
-
-func TestFloatFormat(t *testing.T) {
-	for _, test := range []struct {
-		format string
-		value  interface{} // float32, float64, or string (== 512bit *Float)
-		want   string
-	}{
-		// from fmt/fmt_test.go
-		{"%+.3e", 0.0, "+0.000e+00"},
-		{"%+.3e", 1.0, "+1.000e+00"},
-		{"%+.3f", -1.0, "-1.000"},
-		{"%+.3F", -1.0, "-1.000"},
-		{"%+.3F", float32(-1.0), "-1.000"},
-		{"%+07.2f", 1.0, "+001.00"},
-		{"%+07.2f", -1.0, "-001.00"},
-		{"%+10.2f", +1.0, "     +1.00"},
-		{"%+10.2f", -1.0, "     -1.00"},
-		{"% .3E", -1.0, "-1.000E+00"},
-		{"% .3e", 1.0, " 1.000e+00"},
-		{"%+.3g", 0.0, "+0"},
-		{"%+.3g", 1.0, "+1"},
-		{"%+.3g", -1.0, "-1"},
-		{"% .3g", -1.0, "-1"},
-		{"% .3g", 1.0, " 1"},
-		{"%b", float32(1.0), "8388608p-23"},
-		{"%b", 1.0, "4503599627370496p-52"},
-
-		// from fmt/fmt_test.go: old test/fmt_test.go
-		{"%e", 1.0, "1.000000e+00"},
-		{"%e", 1234.5678e3, "1.234568e+06"},
-		{"%e", 1234.5678e-8, "1.234568e-05"},
-		{"%e", -7.0, "-7.000000e+00"},
-		{"%e", -1e-9, "-1.000000e-09"},
-		{"%f", 1234.5678e3, "1234567.800000"},
-		{"%f", 1234.5678e-8, "0.000012"},
-		{"%f", -7.0, "-7.000000"},
-		{"%f", -1e-9, "-0.000000"},
-		{"%g", 1234.5678e3, "1.2345678e+06"},
-		{"%g", float32(1234.5678e3), "1.2345678e+06"},
-		{"%g", 1234.5678e-8, "1.2345678e-05"},
-		{"%g", -7.0, "-7"},
-		{"%g", -1e-9, "-1e-09"},
-		{"%g", float32(-1e-9), "-1e-09"},
-		{"%E", 1.0, "1.000000E+00"},
-		{"%E", 1234.5678e3, "1.234568E+06"},
-		{"%E", 1234.5678e-8, "1.234568E-05"},
-		{"%E", -7.0, "-7.000000E+00"},
-		{"%E", -1e-9, "-1.000000E-09"},
-		{"%G", 1234.5678e3, "1.2345678E+06"},
-		{"%G", float32(1234.5678e3), "1.2345678E+06"},
-		{"%G", 1234.5678e-8, "1.2345678E-05"},
-		{"%G", -7.0, "-7"},
-		{"%G", -1e-9, "-1E-09"},
-		{"%G", float32(-1e-9), "-1E-09"},
-
-		{"%20.6e", 1.2345e3, "        1.234500e+03"},
-		{"%20.6e", 1.2345e-3, "        1.234500e-03"},
-		{"%20e", 1.2345e3, "        1.234500e+03"},
-		{"%20e", 1.2345e-3, "        1.234500e-03"},
-		{"%20.8e", 1.2345e3, "      1.23450000e+03"},
-		{"%20f", 1.23456789e3, "         1234.567890"},
-		{"%20f", 1.23456789e-3, "            0.001235"},
-		{"%20f", 12345678901.23456789, "  12345678901.234568"},
-		{"%-20f", 1.23456789e3, "1234.567890         "},
-		{"%20.8f", 1.23456789e3, "       1234.56789000"},
-		{"%20.8f", 1.23456789e-3, "          0.00123457"},
-		{"%g", 1.23456789e3, "1234.56789"},
-		{"%g", 1.23456789e-3, "0.00123456789"},
-		{"%g", 1.23456789e20, "1.23456789e+20"},
-		{"%20e", math.Inf(1), "                +Inf"},
-		{"%-20f", math.Inf(-1), "-Inf                "},
-
-		// from fmt/fmt_test.go: comparison of padding rules with C printf
-		{"%.2f", 1.0, "1.00"},
-		{"%.2f", -1.0, "-1.00"},
-		{"% .2f", 1.0, " 1.00"},
-		{"% .2f", -1.0, "-1.00"},
-		{"%+.2f", 1.0, "+1.00"},
-		{"%+.2f", -1.0, "-1.00"},
-		{"%7.2f", 1.0, "   1.00"},
-		{"%7.2f", -1.0, "  -1.00"},
-		{"% 7.2f", 1.0, "   1.00"},
-		{"% 7.2f", -1.0, "  -1.00"},
-		{"%+7.2f", 1.0, "  +1.00"},
-		{"%+7.2f", -1.0, "  -1.00"},
-		{"%07.2f", 1.0, "0001.00"},
-		{"%07.2f", -1.0, "-001.00"},
-		{"% 07.2f", 1.0, " 001.00"},
-		{"% 07.2f", -1.0, "-001.00"},
-		{"%+07.2f", 1.0, "+001.00"},
-		{"%+07.2f", -1.0, "-001.00"},
-
-		// from fmt/fmt_test.go: zero padding does not apply to infinities
-		{"%020f", math.Inf(-1), "                -Inf"},
-		{"%020f", math.Inf(+1), "                +Inf"},
-		{"% 020f", math.Inf(-1), "                -Inf"},
-		{"% 020f", math.Inf(+1), "                 Inf"},
-		{"%+020f", math.Inf(-1), "                -Inf"},
-		{"%+020f", math.Inf(+1), "                +Inf"},
-		{"%20f", -1.0, "           -1.000000"},
-
-		// handle %v like %g
-		{"%v", 0.0, "0"},
-		{"%v", -7.0, "-7"},
-		{"%v", -1e-9, "-1e-09"},
-		{"%v", float32(-1e-9), "-1e-09"},
-		{"%010v", 0.0, "0000000000"},
-
-		// *Float cases
-		{"%.20f", "1e-20", "0.00000000000000000001"},
-		{"%.20f", "-1e-20", "-0.00000000000000000001"},
-		{"%30.20f", "-1e-20", "       -0.00000000000000000001"},
-		{"%030.20f", "-1e-20", "-00000000.00000000000000000001"},
-		{"%030.20f", "+1e-20", "000000000.00000000000000000001"},
-		{"% 030.20f", "+1e-20", " 00000000.00000000000000000001"},
-
-		// erroneous formats
-		{"%s", 1.0, "%!s(*big.Float=1)"},
-	} {
-		value := new(Float)
-		switch v := test.value.(type) {
-		case float32:
-			value.SetPrec(24).SetFloat64(float64(v))
-		case float64:
-			value.SetPrec(53).SetFloat64(v)
-		case string:
-			value.SetPrec(512).Parse(v, 0)
-		default:
-			t.Fatalf("unsupported test value: %v (%T)", v, v)
-		}
-
-		if got := fmt.Sprintf(test.format, value); got != test.want {
-			t.Errorf("%v: got %q; want %q", test, got, test.want)
-		}
-	}
-}
-
-func BenchmarkParseFloatSmallExp(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		for _, s := range []string{
-			"1e0",
-			"1e-1",
-			"1e-2",
-			"1e-3",
-			"1e-4",
-			"1e-5",
-			"1e-10",
-			"1e-20",
-			"1e-50",
-			"1e1",
-			"1e2",
-			"1e3",
-			"1e4",
-			"1e5",
-			"1e10",
-			"1e20",
-			"1e50",
-		} {
-			var x Float
-			_, _, err := x.Parse(s, 0)
-			if err != nil {
-				b.Fatalf("%s: %v", s, err)
-			}
-		}
-	}
-}
-
-func BenchmarkParseFloatLargeExp(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		for _, s := range []string{
-			"1e0",
-			"1e-10",
-			"1e-20",
-			"1e-30",
-			"1e-40",
-			"1e-50",
-			"1e-100",
-			"1e-500",
-			"1e-1000",
-			"1e-5000",
-			"1e-10000",
-			"1e10",
-			"1e20",
-			"1e30",
-			"1e40",
-			"1e50",
-			"1e100",
-			"1e500",
-			"1e1000",
-			"1e5000",
-			"1e10000",
-		} {
-			var x Float
-			_, _, err := x.Parse(s, 0)
-			if err != nil {
-				b.Fatalf("%s: %v", s, err)
-			}
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/floatexample_test.go b/src/cmd/compile/internal/big/floatexample_test.go
deleted file mode 100644
index 83c6bda..0000000
--- a/src/cmd/compile/internal/big/floatexample_test.go
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big_test
-
-import (
-	"cmd/compile/internal/big"
-	"fmt"
-	"math"
-)
-
-func ExampleFloat_Add() {
-	// Operating on numbers of different precision.
-	var x, y, z big.Float
-	x.SetInt64(1000)          // x is automatically set to 64bit precision
-	y.SetFloat64(2.718281828) // y is automatically set to 53bit precision
-	z.SetPrec(32)
-	z.Add(&x, &y)
-	fmt.Printf("x = %.10g (%s, prec = %d, acc = %s)\n", &x, x.Text('p', 0), x.Prec(), x.Acc())
-	fmt.Printf("y = %.10g (%s, prec = %d, acc = %s)\n", &y, y.Text('p', 0), y.Prec(), y.Acc())
-	fmt.Printf("z = %.10g (%s, prec = %d, acc = %s)\n", &z, z.Text('p', 0), z.Prec(), z.Acc())
-	// Output:
-	// x = 1000 (0x.fap+10, prec = 64, acc = Exact)
-	// y = 2.718281828 (0x.adf85458248cd8p+2, prec = 53, acc = Exact)
-	// z = 1002.718282 (0x.faadf854p+10, prec = 32, acc = Below)
-}
-
-func Example_Shift() {
-	// Implementing Float "shift" by modifying the (binary) exponents directly.
-	for s := -5; s <= 5; s++ {
-		x := big.NewFloat(0.5)
-		x.SetMantExp(x, x.MantExp(nil)+s) // shift x by s
-		fmt.Println(x)
-	}
-	// Output:
-	// 0.015625
-	// 0.03125
-	// 0.0625
-	// 0.125
-	// 0.25
-	// 0.5
-	// 1
-	// 2
-	// 4
-	// 8
-	// 16
-}
-
-func ExampleFloat_Cmp() {
-	inf := math.Inf(1)
-	zero := 0.0
-
-	operands := []float64{-inf, -1.2, -zero, 0, +1.2, +inf}
-
-	fmt.Println("   x     y  cmp")
-	fmt.Println("---------------")
-	for _, x64 := range operands {
-		x := big.NewFloat(x64)
-		for _, y64 := range operands {
-			y := big.NewFloat(y64)
-			fmt.Printf("%4g  %4g  %3d\n", x, y, x.Cmp(y))
-		}
-		fmt.Println()
-	}
-
-	// Output:
-	//    x     y  cmp
-	// ---------------
-	// -Inf  -Inf    0
-	// -Inf  -1.2   -1
-	// -Inf    -0   -1
-	// -Inf     0   -1
-	// -Inf   1.2   -1
-	// -Inf  +Inf   -1
-	//
-	// -1.2  -Inf    1
-	// -1.2  -1.2    0
-	// -1.2    -0   -1
-	// -1.2     0   -1
-	// -1.2   1.2   -1
-	// -1.2  +Inf   -1
-	//
-	//   -0  -Inf    1
-	//   -0  -1.2    1
-	//   -0    -0    0
-	//   -0     0    0
-	//   -0   1.2   -1
-	//   -0  +Inf   -1
-	//
-	//    0  -Inf    1
-	//    0  -1.2    1
-	//    0    -0    0
-	//    0     0    0
-	//    0   1.2   -1
-	//    0  +Inf   -1
-	//
-	//  1.2  -Inf    1
-	//  1.2  -1.2    1
-	//  1.2    -0    1
-	//  1.2     0    1
-	//  1.2   1.2    0
-	//  1.2  +Inf   -1
-	//
-	// +Inf  -Inf    1
-	// +Inf  -1.2    1
-	// +Inf    -0    1
-	// +Inf     0    1
-	// +Inf   1.2    1
-	// +Inf  +Inf    0
-}
-
-func ExampleRoundingMode() {
-	operands := []float64{2.6, 2.5, 2.1, -2.1, -2.5, -2.6}
-
-	fmt.Print("   x")
-	for mode := big.ToNearestEven; mode <= big.ToPositiveInf; mode++ {
-		fmt.Printf("  %s", mode)
-	}
-	fmt.Println()
-
-	for _, f64 := range operands {
-		fmt.Printf("%4g", f64)
-		for mode := big.ToNearestEven; mode <= big.ToPositiveInf; mode++ {
-			// sample operands above require 2 bits to represent mantissa
-			// set binary precision to 2 to round them to integer values
-			f := new(big.Float).SetPrec(2).SetMode(mode).SetFloat64(f64)
-			fmt.Printf("  %*g", len(mode.String()), f)
-		}
-		fmt.Println()
-	}
-
-	// Output:
-	//    x  ToNearestEven  ToNearestAway  ToZero  AwayFromZero  ToNegativeInf  ToPositiveInf
-	//  2.6              3              3       2             3              2              3
-	//  2.5              2              3       2             3              2              3
-	//  2.1              2              2       2             3              2              3
-	// -2.1             -2             -2      -2            -3             -3             -2
-	// -2.5             -2             -3      -2            -3             -3             -2
-	// -2.6             -3             -3      -2            -3             -3             -2
-}
diff --git a/src/cmd/compile/internal/big/floatmarsh.go b/src/cmd/compile/internal/big/floatmarsh.go
deleted file mode 100644
index 44987ee..0000000
--- a/src/cmd/compile/internal/big/floatmarsh.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements encoding/decoding of Floats.
-
-package big
-
-import "fmt"
-
-// MarshalText implements the encoding.TextMarshaler interface.
-// Only the Float value is marshaled (in full precision), other
-// attributes such as precision or accuracy are ignored.
-func (x *Float) MarshalText() (text []byte, err error) {
-	if x == nil {
-		return []byte("<nil>"), nil
-	}
-	var buf []byte
-	return x.Append(buf, 'g', -1), nil
-}
-
-// UnmarshalText implements the encoding.TextUnmarshaler interface.
-// The result is rounded per the precision and rounding mode of z.
-// If z's precision is 0, it is changed to 64 before rounding takes
-// effect.
-func (z *Float) UnmarshalText(text []byte) error {
-	// TODO(gri): get rid of the []byte/string conversion
-	_, _, err := z.Parse(string(text), 0)
-	if err != nil {
-		err = fmt.Errorf("math/big: cannot unmarshal %q into a *big.Float (%v)", text, err)
-	}
-	return err
-}
diff --git a/src/cmd/compile/internal/big/floatmarsh_test.go b/src/cmd/compile/internal/big/floatmarsh_test.go
deleted file mode 100644
index d7ef2fc..0000000
--- a/src/cmd/compile/internal/big/floatmarsh_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"encoding/json"
-	"testing"
-)
-
-var floatVals = []string{
-	"0",
-	"1",
-	"0.1",
-	"2.71828",
-	"1234567890",
-	"3.14e1234",
-	"3.14e-1234",
-	"0.738957395793475734757349579759957975985497e100",
-	"0.73895739579347546656564656573475734957975995797598589749859834759476745986795497e100",
-	"inf",
-	"Inf",
-}
-
-func TestFloatJSONEncoding(t *testing.T) {
-	for _, test := range floatVals {
-		for _, sign := range []string{"", "+", "-"} {
-			for _, prec := range []uint{0, 1, 2, 10, 53, 64, 100, 1000} {
-				x := sign + test
-				var tx Float
-				_, _, err := tx.SetPrec(prec).Parse(x, 0)
-				if err != nil {
-					t.Errorf("parsing of %s (prec = %d) failed (invalid test case): %v", x, prec, err)
-					continue
-				}
-				b, err := json.Marshal(&tx)
-				if err != nil {
-					t.Errorf("marshaling of %v (prec = %d) failed: %v", &tx, prec, err)
-					continue
-				}
-				var rx Float
-				rx.SetPrec(prec)
-				if err := json.Unmarshal(b, &rx); err != nil {
-					t.Errorf("unmarshaling of %v (prec = %d) failed: %v", &tx, prec, err)
-					continue
-				}
-				if rx.Cmp(&tx) != 0 {
-					t.Errorf("JSON encoding of %v (prec = %d) failed: got %v want %v", &tx, prec, &rx, &tx)
-				}
-			}
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/ftoa.go b/src/cmd/compile/internal/big/ftoa.go
deleted file mode 100644
index 624ea5e..0000000
--- a/src/cmd/compile/internal/big/ftoa.go
+++ /dev/null
@@ -1,456 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements Float-to-string conversion functions.
-// It is closely following the corresponding implementation
-// in strconv/ftoa.go, but modified and simplified for Float.
-
-package big
-
-import (
-	"bytes"
-	"fmt"
-	"strconv"
-)
-
-// Text converts the floating-point number x to a string according
-// to the given format and precision prec. The format is one of:
-//
-//	'e'	-d.dddde±dd, decimal exponent, at least two (possibly 0) exponent digits
-//	'E'	-d.ddddE±dd, decimal exponent, at least two (possibly 0) exponent digits
-//	'f'	-ddddd.dddd, no exponent
-//	'g'	like 'e' for large exponents, like 'f' otherwise
-//	'G'	like 'E' for large exponents, like 'f' otherwise
-//	'b'	-ddddddp±dd, binary exponent
-//	'p'	-0x.dddp±dd, binary exponent, hexadecimal mantissa
-//
-// For the binary exponent formats, the mantissa is printed in normalized form:
-//
-//	'b'	decimal integer mantissa using x.Prec() bits, or -0
-//	'p'	hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0
-//
-// If format is a different character, Text returns a "%" followed by the
-// unrecognized format character.
-//
-// The precision prec controls the number of digits (excluding the exponent)
-// printed by the 'e', 'E', 'f', 'g', and 'G' formats. For 'e', 'E', and 'f'
-// it is the number of digits after the decimal point. For 'g' and 'G' it is
-// the total number of digits. A negative precision selects the smallest
-// number of decimal digits necessary to identify the value x uniquely using
-// x.Prec() mantissa bits.
-// The prec value is ignored for the 'b' or 'p' format.
-func (x *Float) Text(format byte, prec int) string {
-	const extra = 10 // TODO(gri) determine a good/better value here
-	return string(x.Append(make([]byte, 0, prec+extra), format, prec))
-}
-
-// String formats x like x.Text('g', 10).
-// (String must be called explicitly, Float.Format does not support %s verb.)
-func (x *Float) String() string {
-	return x.Text('g', 10)
-}
-
-// Append appends to buf the string form of the floating-point number x,
-// as generated by x.Text, and returns the extended buffer.
-func (x *Float) Append(buf []byte, fmt byte, prec int) []byte {
-	// sign
-	if x.neg {
-		buf = append(buf, '-')
-	}
-
-	// Inf
-	if x.form == inf {
-		if !x.neg {
-			buf = append(buf, '+')
-		}
-		return append(buf, "Inf"...)
-	}
-
-	// pick off easy formats
-	switch fmt {
-	case 'b':
-		return x.fmtB(buf)
-	case 'p':
-		return x.fmtP(buf)
-	}
-
-	// Algorithm:
-	//   1) convert Float to multiprecision decimal
-	//   2) round to desired precision
-	//   3) read digits out and format
-
-	// 1) convert Float to multiprecision decimal
-	var d decimal // == 0.0
-	if x.form == finite {
-		// x != 0
-		d.init(x.mant, int(x.exp)-x.mant.bitLen())
-	}
-
-	// 2) round to desired precision
-	shortest := false
-	if prec < 0 {
-		shortest = true
-		roundShortest(&d, x)
-		// Precision for shortest representation mode.
-		switch fmt {
-		case 'e', 'E':
-			prec = len(d.mant) - 1
-		case 'f':
-			prec = max(len(d.mant)-d.exp, 0)
-		case 'g', 'G':
-			prec = len(d.mant)
-		}
-	} else {
-		// round appropriately
-		switch fmt {
-		case 'e', 'E':
-			// one digit before and number of digits after decimal point
-			d.round(1 + prec)
-		case 'f':
-			// number of digits before and after decimal point
-			d.round(d.exp + prec)
-		case 'g', 'G':
-			if prec == 0 {
-				prec = 1
-			}
-			d.round(prec)
-		}
-	}
-
-	// 3) read digits out and format
-	switch fmt {
-	case 'e', 'E':
-		return fmtE(buf, fmt, prec, d)
-	case 'f':
-		return fmtF(buf, prec, d)
-	case 'g', 'G':
-		// trim trailing fractional zeros in %e format
-		eprec := prec
-		if eprec > len(d.mant) && len(d.mant) >= d.exp {
-			eprec = len(d.mant)
-		}
-		// %e is used if the exponent from the conversion
-		// is less than -4 or greater than or equal to the precision.
-		// If precision was the shortest possible, use eprec = 6 for
-		// this decision.
-		if shortest {
-			eprec = 6
-		}
-		exp := d.exp - 1
-		if exp < -4 || exp >= eprec {
-			if prec > len(d.mant) {
-				prec = len(d.mant)
-			}
-			return fmtE(buf, fmt+'e'-'g', prec-1, d)
-		}
-		if prec > d.exp {
-			prec = len(d.mant)
-		}
-		return fmtF(buf, max(prec-d.exp, 0), d)
-	}
-
-	// unknown format
-	if x.neg {
-		buf = buf[:len(buf)-1] // sign was added prematurely - remove it again
-	}
-	return append(buf, '%', fmt)
-}
-
-func roundShortest(d *decimal, x *Float) {
-	// if the mantissa is zero, the number is zero - stop now
-	if len(d.mant) == 0 {
-		return
-	}
-
-	// Approach: All numbers in the interval [x - 1/2ulp, x + 1/2ulp]
-	// (possibly exclusive) round to x for the given precision of x.
-	// Compute the lower and upper bound in decimal form and find the
-	// shortest decimal number d such that lower <= d <= upper.
-
-	// TODO(gri) strconv/ftoa.do describes a shortcut in some cases.
-	// See if we can use it (in adjusted form) here as well.
-
-	// 1) Compute normalized mantissa mant and exponent exp for x such
-	// that the lsb of mant corresponds to 1/2 ulp for the precision of
-	// x (i.e., for mant we want x.prec + 1 bits).
-	mant := nat(nil).set(x.mant)
-	exp := int(x.exp) - mant.bitLen()
-	s := mant.bitLen() - int(x.prec+1)
-	switch {
-	case s < 0:
-		mant = mant.shl(mant, uint(-s))
-	case s > 0:
-		mant = mant.shr(mant, uint(+s))
-	}
-	exp += s
-	// x = mant * 2**exp with lsb(mant) == 1/2 ulp of x.prec
-
-	// 2) Compute lower bound by subtracting 1/2 ulp.
-	var lower decimal
-	var tmp nat
-	lower.init(tmp.sub(mant, natOne), exp)
-
-	// 3) Compute upper bound by adding 1/2 ulp.
-	var upper decimal
-	upper.init(tmp.add(mant, natOne), exp)
-
-	// The upper and lower bounds are possible outputs only if
-	// the original mantissa is even, so that ToNearestEven rounding
-	// would round to the original mantissa and not the neighbors.
-	inclusive := mant[0]&2 == 0 // test bit 1 since original mantissa was shifted by 1
-
-	// Now we can figure out the minimum number of digits required.
-	// Walk along until d has distinguished itself from upper and lower.
-	for i, m := range d.mant {
-		l := lower.at(i)
-		u := upper.at(i)
-
-		// Okay to round down (truncate) if lower has a different digit
-		// or if lower is inclusive and is exactly the result of rounding
-		// down (i.e., and we have reached the final digit of lower).
-		okdown := l != m || inclusive && i+1 == len(lower.mant)
-
-		// Okay to round up if upper has a different digit and either upper
-		// is inclusive or upper is bigger than the result of rounding up.
-		okup := m != u && (inclusive || m+1 < u || i+1 < len(upper.mant))
-
-		// If it's okay to do either, then round to the nearest one.
-		// If it's okay to do only one, do it.
-		switch {
-		case okdown && okup:
-			d.round(i + 1)
-			return
-		case okdown:
-			d.roundDown(i + 1)
-			return
-		case okup:
-			d.roundUp(i + 1)
-			return
-		}
-	}
-}
-
-// %e: d.ddddde±dd
-func fmtE(buf []byte, fmt byte, prec int, d decimal) []byte {
-	// first digit
-	ch := byte('0')
-	if len(d.mant) > 0 {
-		ch = d.mant[0]
-	}
-	buf = append(buf, ch)
-
-	// .moredigits
-	if prec > 0 {
-		buf = append(buf, '.')
-		i := 1
-		m := min(len(d.mant), prec+1)
-		if i < m {
-			buf = append(buf, d.mant[i:m]...)
-			i = m
-		}
-		for ; i <= prec; i++ {
-			buf = append(buf, '0')
-		}
-	}
-
-	// e±
-	buf = append(buf, fmt)
-	var exp int64
-	if len(d.mant) > 0 {
-		exp = int64(d.exp) - 1 // -1 because first digit was printed before '.'
-	}
-	if exp < 0 {
-		ch = '-'
-		exp = -exp
-	} else {
-		ch = '+'
-	}
-	buf = append(buf, ch)
-
-	// dd...d
-	if exp < 10 {
-		buf = append(buf, '0') // at least 2 exponent digits
-	}
-	return strconv.AppendInt(buf, exp, 10)
-}
-
-// %f: ddddddd.ddddd
-func fmtF(buf []byte, prec int, d decimal) []byte {
-	// integer, padded with zeros as needed
-	if d.exp > 0 {
-		m := min(len(d.mant), d.exp)
-		buf = append(buf, d.mant[:m]...)
-		for ; m < d.exp; m++ {
-			buf = append(buf, '0')
-		}
-	} else {
-		buf = append(buf, '0')
-	}
-
-	// fraction
-	if prec > 0 {
-		buf = append(buf, '.')
-		for i := 0; i < prec; i++ {
-			buf = append(buf, d.at(d.exp+i))
-		}
-	}
-
-	return buf
-}
-
-// fmtB appends the string of x in the format mantissa "p" exponent
-// with a decimal mantissa and a binary exponent, or 0" if x is zero,
-// and returns the extended buffer.
-// The mantissa is normalized such that is uses x.Prec() bits in binary
-// representation.
-// The sign of x is ignored, and x must not be an Inf.
-func (x *Float) fmtB(buf []byte) []byte {
-	if x.form == zero {
-		return append(buf, '0')
-	}
-
-	if debugFloat && x.form != finite {
-		panic("non-finite float")
-	}
-	// x != 0
-
-	// adjust mantissa to use exactly x.prec bits
-	m := x.mant
-	switch w := uint32(len(x.mant)) * _W; {
-	case w < x.prec:
-		m = nat(nil).shl(m, uint(x.prec-w))
-	case w > x.prec:
-		m = nat(nil).shr(m, uint(w-x.prec))
-	}
-
-	buf = append(buf, m.utoa(10)...)
-	buf = append(buf, 'p')
-	e := int64(x.exp) - int64(x.prec)
-	if e >= 0 {
-		buf = append(buf, '+')
-	}
-	return strconv.AppendInt(buf, e, 10)
-}
-
-// fmtP appends the string of x in the format "0x." mantissa "p" exponent
-// with a hexadecimal mantissa and a binary exponent, or "0" if x is zero,
-// and returns the extended buffer.
-// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0.
-// The sign of x is ignored, and x must not be an Inf.
-func (x *Float) fmtP(buf []byte) []byte {
-	if x.form == zero {
-		return append(buf, '0')
-	}
-
-	if debugFloat && x.form != finite {
-		panic("non-finite float")
-	}
-	// x != 0
-
-	// remove trailing 0 words early
-	// (no need to convert to hex 0's and trim later)
-	m := x.mant
-	i := 0
-	for i < len(m) && m[i] == 0 {
-		i++
-	}
-	m = m[i:]
-
-	buf = append(buf, "0x."...)
-	buf = append(buf, bytes.TrimRight(m.utoa(16), "0")...)
-	buf = append(buf, 'p')
-	if x.exp >= 0 {
-		buf = append(buf, '+')
-	}
-	return strconv.AppendInt(buf, int64(x.exp), 10)
-}
-
-func min(x, y int) int {
-	if x < y {
-		return x
-	}
-	return y
-}
-
-// Format implements fmt.Formatter. It accepts all the regular
-// formats for floating-point numbers ('b', 'e', 'E', 'f', 'F',
-// 'g', 'G') as well as 'p' and 'v'. See (*Float).Text for the
-// interpretation of 'p'. The 'v' format is handled like 'g'.
-// Format also supports specification of the minimum precision
-// in digits, the output field width, as well as the format flags
-// '+' and ' ' for sign control, '0' for space or zero padding,
-// and '-' for left or right justification. See the fmt package
-// for details.
-func (x *Float) Format(s fmt.State, format rune) {
-	prec, hasPrec := s.Precision()
-	if !hasPrec {
-		prec = 6 // default precision for 'e', 'f'
-	}
-
-	switch format {
-	case 'e', 'E', 'f', 'b', 'p':
-		// nothing to do
-	case 'F':
-		// (*Float).Text doesn't support 'F'; handle like 'f'
-		format = 'f'
-	case 'v':
-		// handle like 'g'
-		format = 'g'
-		fallthrough
-	case 'g', 'G':
-		if !hasPrec {
-			prec = -1 // default precision for 'g', 'G'
-		}
-	default:
-		fmt.Fprintf(s, "%%!%c(*big.Float=%s)", format, x.String())
-		return
-	}
-	var buf []byte
-	buf = x.Append(buf, byte(format), prec)
-	if len(buf) == 0 {
-		buf = []byte("?") // should never happen, but don't crash
-	}
-	// len(buf) > 0
-
-	var sign string
-	switch {
-	case buf[0] == '-':
-		sign = "-"
-		buf = buf[1:]
-	case buf[0] == '+':
-		// +Inf
-		sign = "+"
-		if s.Flag(' ') {
-			sign = " "
-		}
-		buf = buf[1:]
-	case s.Flag('+'):
-		sign = "+"
-	case s.Flag(' '):
-		sign = " "
-	}
-
-	var padding int
-	if width, hasWidth := s.Width(); hasWidth && width > len(sign)+len(buf) {
-		padding = width - len(sign) - len(buf)
-	}
-
-	switch {
-	case s.Flag('0') && !x.IsInf():
-		// 0-padding on left
-		writeMultiple(s, sign, 1)
-		writeMultiple(s, "0", padding)
-		s.Write(buf)
-	case s.Flag('-'):
-		// padding on right
-		writeMultiple(s, sign, 1)
-		s.Write(buf)
-		writeMultiple(s, " ", padding)
-	default:
-		// padding on left
-		writeMultiple(s, " ", padding)
-		writeMultiple(s, sign, 1)
-		s.Write(buf)
-	}
-}
diff --git a/src/cmd/compile/internal/big/gcd_test.go b/src/cmd/compile/internal/big/gcd_test.go
deleted file mode 100644
index c0b9f58..0000000
--- a/src/cmd/compile/internal/big/gcd_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements a GCD benchmark.
-// Usage: go test math/big -test.bench GCD
-
-package big
-
-import (
-	"math/rand"
-	"testing"
-)
-
-// randInt returns a pseudo-random Int in the range [1<<(size-1), (1<<size) - 1]
-func randInt(r *rand.Rand, size uint) *Int {
-	n := new(Int).Lsh(intOne, size-1)
-	x := new(Int).Rand(r, n)
-	return x.Add(x, n) // make sure result > 1<<(size-1)
-}
-
-func runGCD(b *testing.B, aSize, bSize uint) {
-	b.StopTimer()
-	var r = rand.New(rand.NewSource(1234))
-	aa := randInt(r, aSize)
-	bb := randInt(r, bSize)
-	b.StartTimer()
-	for i := 0; i < b.N; i++ {
-		new(Int).GCD(nil, nil, aa, bb)
-	}
-}
-
-func BenchmarkGCD10x10(b *testing.B)         { runGCD(b, 10, 10) }
-func BenchmarkGCD10x100(b *testing.B)        { runGCD(b, 10, 100) }
-func BenchmarkGCD10x1000(b *testing.B)       { runGCD(b, 10, 1000) }
-func BenchmarkGCD10x10000(b *testing.B)      { runGCD(b, 10, 10000) }
-func BenchmarkGCD10x100000(b *testing.B)     { runGCD(b, 10, 100000) }
-func BenchmarkGCD100x100(b *testing.B)       { runGCD(b, 100, 100) }
-func BenchmarkGCD100x1000(b *testing.B)      { runGCD(b, 100, 1000) }
-func BenchmarkGCD100x10000(b *testing.B)     { runGCD(b, 100, 10000) }
-func BenchmarkGCD100x100000(b *testing.B)    { runGCD(b, 100, 100000) }
-func BenchmarkGCD1000x1000(b *testing.B)     { runGCD(b, 1000, 1000) }
-func BenchmarkGCD1000x10000(b *testing.B)    { runGCD(b, 1000, 10000) }
-func BenchmarkGCD1000x100000(b *testing.B)   { runGCD(b, 1000, 100000) }
-func BenchmarkGCD10000x10000(b *testing.B)   { runGCD(b, 10000, 10000) }
-func BenchmarkGCD10000x100000(b *testing.B)  { runGCD(b, 10000, 100000) }
-func BenchmarkGCD100000x100000(b *testing.B) { runGCD(b, 100000, 100000) }
diff --git a/src/cmd/compile/internal/big/hilbert_test.go b/src/cmd/compile/internal/big/hilbert_test.go
deleted file mode 100644
index 1a84341..0000000
--- a/src/cmd/compile/internal/big/hilbert_test.go
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// A little test program and benchmark for rational arithmetics.
-// Computes a Hilbert matrix, its inverse, multiplies them
-// and verifies that the product is the identity matrix.
-
-package big
-
-import (
-	"fmt"
-	"testing"
-)
-
-type matrix struct {
-	n, m int
-	a    []*Rat
-}
-
-func (a *matrix) at(i, j int) *Rat {
-	if !(0 <= i && i < a.n && 0 <= j && j < a.m) {
-		panic("index out of range")
-	}
-	return a.a[i*a.m+j]
-}
-
-func (a *matrix) set(i, j int, x *Rat) {
-	if !(0 <= i && i < a.n && 0 <= j && j < a.m) {
-		panic("index out of range")
-	}
-	a.a[i*a.m+j] = x
-}
-
-func newMatrix(n, m int) *matrix {
-	if !(0 <= n && 0 <= m) {
-		panic("illegal matrix")
-	}
-	a := new(matrix)
-	a.n = n
-	a.m = m
-	a.a = make([]*Rat, n*m)
-	return a
-}
-
-func newUnit(n int) *matrix {
-	a := newMatrix(n, n)
-	for i := 0; i < n; i++ {
-		for j := 0; j < n; j++ {
-			x := NewRat(0, 1)
-			if i == j {
-				x.SetInt64(1)
-			}
-			a.set(i, j, x)
-		}
-	}
-	return a
-}
-
-func newHilbert(n int) *matrix {
-	a := newMatrix(n, n)
-	for i := 0; i < n; i++ {
-		for j := 0; j < n; j++ {
-			a.set(i, j, NewRat(1, int64(i+j+1)))
-		}
-	}
-	return a
-}
-
-func newInverseHilbert(n int) *matrix {
-	a := newMatrix(n, n)
-	for i := 0; i < n; i++ {
-		for j := 0; j < n; j++ {
-			x1 := new(Rat).SetInt64(int64(i + j + 1))
-			x2 := new(Rat).SetInt(new(Int).Binomial(int64(n+i), int64(n-j-1)))
-			x3 := new(Rat).SetInt(new(Int).Binomial(int64(n+j), int64(n-i-1)))
-			x4 := new(Rat).SetInt(new(Int).Binomial(int64(i+j), int64(i)))
-
-			x1.Mul(x1, x2)
-			x1.Mul(x1, x3)
-			x1.Mul(x1, x4)
-			x1.Mul(x1, x4)
-
-			if (i+j)&1 != 0 {
-				x1.Neg(x1)
-			}
-
-			a.set(i, j, x1)
-		}
-	}
-	return a
-}
-
-func (a *matrix) mul(b *matrix) *matrix {
-	if a.m != b.n {
-		panic("illegal matrix multiply")
-	}
-	c := newMatrix(a.n, b.m)
-	for i := 0; i < c.n; i++ {
-		for j := 0; j < c.m; j++ {
-			x := NewRat(0, 1)
-			for k := 0; k < a.m; k++ {
-				x.Add(x, new(Rat).Mul(a.at(i, k), b.at(k, j)))
-			}
-			c.set(i, j, x)
-		}
-	}
-	return c
-}
-
-func (a *matrix) eql(b *matrix) bool {
-	if a.n != b.n || a.m != b.m {
-		return false
-	}
-	for i := 0; i < a.n; i++ {
-		for j := 0; j < a.m; j++ {
-			if a.at(i, j).Cmp(b.at(i, j)) != 0 {
-				return false
-			}
-		}
-	}
-	return true
-}
-
-func (a *matrix) String() string {
-	s := ""
-	for i := 0; i < a.n; i++ {
-		for j := 0; j < a.m; j++ {
-			s += fmt.Sprintf("\t%s", a.at(i, j))
-		}
-		s += "\n"
-	}
-	return s
-}
-
-func doHilbert(t *testing.T, n int) {
-	a := newHilbert(n)
-	b := newInverseHilbert(n)
-	I := newUnit(n)
-	ab := a.mul(b)
-	if !ab.eql(I) {
-		if t == nil {
-			panic("Hilbert failed")
-		}
-		t.Errorf("a   = %s\n", a)
-		t.Errorf("b   = %s\n", b)
-		t.Errorf("a*b = %s\n", ab)
-		t.Errorf("I   = %s\n", I)
-	}
-}
-
-func TestHilbert(t *testing.T) {
-	doHilbert(t, 10)
-}
-
-func BenchmarkHilbert(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		doHilbert(nil, 10)
-	}
-}
diff --git a/src/cmd/compile/internal/big/int.go b/src/cmd/compile/internal/big/int.go
deleted file mode 100644
index 67ab704..0000000
--- a/src/cmd/compile/internal/big/int.go
+++ /dev/null
@@ -1,934 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements signed multi-precision integers.
-
-package big
-
-import (
-	"fmt"
-	"io"
-	"math/rand"
-	"strings"
-)
-
-// An Int represents a signed multi-precision integer.
-// The zero value for an Int represents the value 0.
-type Int struct {
-	neg bool // sign
-	abs nat  // absolute value of the integer
-}
-
-var intOne = &Int{false, natOne}
-
-// Sign returns:
-//
-//	-1 if x <  0
-//	 0 if x == 0
-//	+1 if x >  0
-//
-func (x *Int) Sign() int {
-	if len(x.abs) == 0 {
-		return 0
-	}
-	if x.neg {
-		return -1
-	}
-	return 1
-}
-
-// SetInt64 sets z to x and returns z.
-func (z *Int) SetInt64(x int64) *Int {
-	neg := false
-	if x < 0 {
-		neg = true
-		x = -x
-	}
-	z.abs = z.abs.setUint64(uint64(x))
-	z.neg = neg
-	return z
-}
-
-// SetUint64 sets z to x and returns z.
-func (z *Int) SetUint64(x uint64) *Int {
-	z.abs = z.abs.setUint64(x)
-	z.neg = false
-	return z
-}
-
-// NewInt allocates and returns a new Int set to x.
-func NewInt(x int64) *Int {
-	return new(Int).SetInt64(x)
-}
-
-// Set sets z to x and returns z.
-func (z *Int) Set(x *Int) *Int {
-	if z != x {
-		z.abs = z.abs.set(x.abs)
-		z.neg = x.neg
-	}
-	return z
-}
-
-// Bits provides raw (unchecked but fast) access to x by returning its
-// absolute value as a little-endian Word slice. The result and x share
-// the same underlying array.
-// Bits is intended to support implementation of missing low-level Int
-// functionality outside this package; it should be avoided otherwise.
-func (x *Int) Bits() []Word {
-	return x.abs
-}
-
-// SetBits provides raw (unchecked but fast) access to z by setting its
-// value to abs, interpreted as a little-endian Word slice, and returning
-// z. The result and abs share the same underlying array.
-// SetBits is intended to support implementation of missing low-level Int
-// functionality outside this package; it should be avoided otherwise.
-func (z *Int) SetBits(abs []Word) *Int {
-	z.abs = nat(abs).norm()
-	z.neg = false
-	return z
-}
-
-// Abs sets z to |x| (the absolute value of x) and returns z.
-func (z *Int) Abs(x *Int) *Int {
-	z.Set(x)
-	z.neg = false
-	return z
-}
-
-// Neg sets z to -x and returns z.
-func (z *Int) Neg(x *Int) *Int {
-	z.Set(x)
-	z.neg = len(z.abs) > 0 && !z.neg // 0 has no sign
-	return z
-}
-
-// Add sets z to the sum x+y and returns z.
-func (z *Int) Add(x, y *Int) *Int {
-	neg := x.neg
-	if x.neg == y.neg {
-		// x + y == x + y
-		// (-x) + (-y) == -(x + y)
-		z.abs = z.abs.add(x.abs, y.abs)
-	} else {
-		// x + (-y) == x - y == -(y - x)
-		// (-x) + y == y - x == -(x - y)
-		if x.abs.cmp(y.abs) >= 0 {
-			z.abs = z.abs.sub(x.abs, y.abs)
-		} else {
-			neg = !neg
-			z.abs = z.abs.sub(y.abs, x.abs)
-		}
-	}
-	z.neg = len(z.abs) > 0 && neg // 0 has no sign
-	return z
-}
-
-// Sub sets z to the difference x-y and returns z.
-func (z *Int) Sub(x, y *Int) *Int {
-	neg := x.neg
-	if x.neg != y.neg {
-		// x - (-y) == x + y
-		// (-x) - y == -(x + y)
-		z.abs = z.abs.add(x.abs, y.abs)
-	} else {
-		// x - y == x - y == -(y - x)
-		// (-x) - (-y) == y - x == -(x - y)
-		if x.abs.cmp(y.abs) >= 0 {
-			z.abs = z.abs.sub(x.abs, y.abs)
-		} else {
-			neg = !neg
-			z.abs = z.abs.sub(y.abs, x.abs)
-		}
-	}
-	z.neg = len(z.abs) > 0 && neg // 0 has no sign
-	return z
-}
-
-// Mul sets z to the product x*y and returns z.
-func (z *Int) Mul(x, y *Int) *Int {
-	// x * y == x * y
-	// x * (-y) == -(x * y)
-	// (-x) * y == -(x * y)
-	// (-x) * (-y) == x * y
-	z.abs = z.abs.mul(x.abs, y.abs)
-	z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign
-	return z
-}
-
-// MulRange sets z to the product of all integers
-// in the range [a, b] inclusively and returns z.
-// If a > b (empty range), the result is 1.
-func (z *Int) MulRange(a, b int64) *Int {
-	switch {
-	case a > b:
-		return z.SetInt64(1) // empty range
-	case a <= 0 && b >= 0:
-		return z.SetInt64(0) // range includes 0
-	}
-	// a <= b && (b < 0 || a > 0)
-
-	neg := false
-	if a < 0 {
-		neg = (b-a)&1 == 0
-		a, b = -b, -a
-	}
-
-	z.abs = z.abs.mulRange(uint64(a), uint64(b))
-	z.neg = neg
-	return z
-}
-
-// Binomial sets z to the binomial coefficient of (n, k) and returns z.
-func (z *Int) Binomial(n, k int64) *Int {
-	// reduce the number of multiplications by reducing k
-	if n/2 < k && k <= n {
-		k = n - k // Binomial(n, k) == Binomial(n, n-k)
-	}
-	var a, b Int
-	a.MulRange(n-k+1, n)
-	b.MulRange(1, k)
-	return z.Quo(&a, &b)
-}
-
-// Quo sets z to the quotient x/y for y != 0 and returns z.
-// If y == 0, a division-by-zero run-time panic occurs.
-// Quo implements truncated division (like Go); see QuoRem for more details.
-func (z *Int) Quo(x, y *Int) *Int {
-	z.abs, _ = z.abs.div(nil, x.abs, y.abs)
-	z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign
-	return z
-}
-
-// Rem sets z to the remainder x%y for y != 0 and returns z.
-// If y == 0, a division-by-zero run-time panic occurs.
-// Rem implements truncated modulus (like Go); see QuoRem for more details.
-func (z *Int) Rem(x, y *Int) *Int {
-	_, z.abs = nat(nil).div(z.abs, x.abs, y.abs)
-	z.neg = len(z.abs) > 0 && x.neg // 0 has no sign
-	return z
-}
-
-// QuoRem sets z to the quotient x/y and r to the remainder x%y
-// and returns the pair (z, r) for y != 0.
-// If y == 0, a division-by-zero run-time panic occurs.
-//
-// QuoRem implements T-division and modulus (like Go):
-//
-//	q = x/y      with the result truncated to zero
-//	r = x - y*q
-//
-// (See Daan Leijen, ``Division and Modulus for Computer Scientists''.)
-// See DivMod for Euclidean division and modulus (unlike Go).
-//
-func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) {
-	z.abs, r.abs = z.abs.div(r.abs, x.abs, y.abs)
-	z.neg, r.neg = len(z.abs) > 0 && x.neg != y.neg, len(r.abs) > 0 && x.neg // 0 has no sign
-	return z, r
-}
-
-// Div sets z to the quotient x/y for y != 0 and returns z.
-// If y == 0, a division-by-zero run-time panic occurs.
-// Div implements Euclidean division (unlike Go); see DivMod for more details.
-func (z *Int) Div(x, y *Int) *Int {
-	y_neg := y.neg // z may be an alias for y
-	var r Int
-	z.QuoRem(x, y, &r)
-	if r.neg {
-		if y_neg {
-			z.Add(z, intOne)
-		} else {
-			z.Sub(z, intOne)
-		}
-	}
-	return z
-}
-
-// Mod sets z to the modulus x%y for y != 0 and returns z.
-// If y == 0, a division-by-zero run-time panic occurs.
-// Mod implements Euclidean modulus (unlike Go); see DivMod for more details.
-func (z *Int) Mod(x, y *Int) *Int {
-	y0 := y // save y
-	if z == y || alias(z.abs, y.abs) {
-		y0 = new(Int).Set(y)
-	}
-	var q Int
-	q.QuoRem(x, y, z)
-	if z.neg {
-		if y0.neg {
-			z.Sub(z, y0)
-		} else {
-			z.Add(z, y0)
-		}
-	}
-	return z
-}
-
-// DivMod sets z to the quotient x div y and m to the modulus x mod y
-// and returns the pair (z, m) for y != 0.
-// If y == 0, a division-by-zero run-time panic occurs.
-//
-// DivMod implements Euclidean division and modulus (unlike Go):
-//
-//	q = x div y  such that
-//	m = x - y*q  with 0 <= m < |y|
-//
-// (See Raymond T. Boute, ``The Euclidean definition of the functions
-// div and mod''. ACM Transactions on Programming Languages and
-// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992.
-// ACM press.)
-// See QuoRem for T-division and modulus (like Go).
-//
-func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) {
-	y0 := y // save y
-	if z == y || alias(z.abs, y.abs) {
-		y0 = new(Int).Set(y)
-	}
-	z.QuoRem(x, y, m)
-	if m.neg {
-		if y0.neg {
-			z.Add(z, intOne)
-			m.Sub(m, y0)
-		} else {
-			z.Sub(z, intOne)
-			m.Add(m, y0)
-		}
-	}
-	return z, m
-}
-
-// Cmp compares x and y and returns:
-//
-//   -1 if x <  y
-//    0 if x == y
-//   +1 if x >  y
-//
-func (x *Int) Cmp(y *Int) (r int) {
-	// x cmp y == x cmp y
-	// x cmp (-y) == x
-	// (-x) cmp y == y
-	// (-x) cmp (-y) == -(x cmp y)
-	switch {
-	case x.neg == y.neg:
-		r = x.abs.cmp(y.abs)
-		if x.neg {
-			r = -r
-		}
-	case x.neg:
-		r = -1
-	default:
-		r = 1
-	}
-	return
-}
-
-// low32 returns the least significant 32 bits of z.
-func low32(z nat) uint32 {
-	if len(z) == 0 {
-		return 0
-	}
-	return uint32(z[0])
-}
-
-// low64 returns the least significant 64 bits of z.
-func low64(z nat) uint64 {
-	if len(z) == 0 {
-		return 0
-	}
-	v := uint64(z[0])
-	if _W == 32 && len(z) > 1 {
-		v |= uint64(z[1]) << 32
-	}
-	return v
-}
-
-// Int64 returns the int64 representation of x.
-// If x cannot be represented in an int64, the result is undefined.
-func (x *Int) Int64() int64 {
-	v := int64(low64(x.abs))
-	if x.neg {
-		v = -v
-	}
-	return v
-}
-
-// Uint64 returns the uint64 representation of x.
-// If x cannot be represented in a uint64, the result is undefined.
-func (x *Int) Uint64() uint64 {
-	return low64(x.abs)
-}
-
-// SetString sets z to the value of s, interpreted in the given base,
-// and returns z and a boolean indicating success. If SetString fails,
-// the value of z is undefined but the returned value is nil.
-//
-// The base argument must be 0 or a value between 2 and MaxBase. If the base
-// is 0, the string prefix determines the actual conversion base. A prefix of
-// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a
-// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10.
-//
-func (z *Int) SetString(s string, base int) (*Int, bool) {
-	r := strings.NewReader(s)
-	_, _, err := z.scan(r, base)
-	if err != nil {
-		return nil, false
-	}
-	_, err = r.ReadByte()
-	if err != io.EOF {
-		return nil, false
-	}
-	return z, true // err == io.EOF => scan consumed all of s
-}
-
-// SetBytes interprets buf as the bytes of a big-endian unsigned
-// integer, sets z to that value, and returns z.
-func (z *Int) SetBytes(buf []byte) *Int {
-	z.abs = z.abs.setBytes(buf)
-	z.neg = false
-	return z
-}
-
-// Bytes returns the absolute value of x as a big-endian byte slice.
-func (x *Int) Bytes() []byte {
-	buf := make([]byte, len(x.abs)*_S)
-	return buf[x.abs.bytes(buf):]
-}
-
-// BitLen returns the length of the absolute value of x in bits.
-// The bit length of 0 is 0.
-func (x *Int) BitLen() int {
-	return x.abs.bitLen()
-}
-
-// Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z.
-// If y <= 0, the result is 1 mod |m|; if m == nil or m == 0, z = x**y.
-// See Knuth, volume 2, section 4.6.3.
-func (z *Int) Exp(x, y, m *Int) *Int {
-	var yWords nat
-	if !y.neg {
-		yWords = y.abs
-	}
-	// y >= 0
-
-	var mWords nat
-	if m != nil {
-		mWords = m.abs // m.abs may be nil for m == 0
-	}
-
-	z.abs = z.abs.expNN(x.abs, yWords, mWords)
-	z.neg = len(z.abs) > 0 && x.neg && len(yWords) > 0 && yWords[0]&1 == 1 // 0 has no sign
-	if z.neg && len(mWords) > 0 {
-		// make modulus result positive
-		z.abs = z.abs.sub(mWords, z.abs) // z == x**y mod |m| && 0 <= z < |m|
-		z.neg = false
-	}
-
-	return z
-}
-
-// GCD sets z to the greatest common divisor of a and b, which both must
-// be > 0, and returns z.
-// If x and y are not nil, GCD sets x and y such that z = a*x + b*y.
-// If either a or b is <= 0, GCD sets z = x = y = 0.
-func (z *Int) GCD(x, y, a, b *Int) *Int {
-	if a.Sign() <= 0 || b.Sign() <= 0 {
-		z.SetInt64(0)
-		if x != nil {
-			x.SetInt64(0)
-		}
-		if y != nil {
-			y.SetInt64(0)
-		}
-		return z
-	}
-	if x == nil && y == nil {
-		return z.binaryGCD(a, b)
-	}
-
-	A := new(Int).Set(a)
-	B := new(Int).Set(b)
-
-	X := new(Int)
-	Y := new(Int).SetInt64(1)
-
-	lastX := new(Int).SetInt64(1)
-	lastY := new(Int)
-
-	q := new(Int)
-	temp := new(Int)
-
-	for len(B.abs) > 0 {
-		r := new(Int)
-		q, r = q.QuoRem(A, B, r)
-
-		A, B = B, r
-
-		temp.Set(X)
-		X.Mul(X, q)
-		X.neg = !X.neg
-		X.Add(X, lastX)
-		lastX.Set(temp)
-
-		temp.Set(Y)
-		Y.Mul(Y, q)
-		Y.neg = !Y.neg
-		Y.Add(Y, lastY)
-		lastY.Set(temp)
-	}
-
-	if x != nil {
-		*x = *lastX
-	}
-
-	if y != nil {
-		*y = *lastY
-	}
-
-	*z = *A
-	return z
-}
-
-// binaryGCD sets z to the greatest common divisor of a and b, which both must
-// be > 0, and returns z.
-// See Knuth, The Art of Computer Programming, Vol. 2, Section 4.5.2, Algorithm B.
-func (z *Int) binaryGCD(a, b *Int) *Int {
-	u := z
-	v := new(Int)
-
-	// use one Euclidean iteration to ensure that u and v are approx. the same size
-	switch {
-	case len(a.abs) > len(b.abs):
-		// must set v before u since u may be alias for a or b (was issue #11284)
-		v.Rem(a, b)
-		u.Set(b)
-	case len(a.abs) < len(b.abs):
-		v.Rem(b, a)
-		u.Set(a)
-	default:
-		v.Set(b)
-		u.Set(a)
-	}
-	// a, b must not be used anymore (may be aliases with u)
-
-	// v might be 0 now
-	if len(v.abs) == 0 {
-		return u
-	}
-	// u > 0 && v > 0
-
-	// determine largest k such that u = u' << k, v = v' << k
-	k := u.abs.trailingZeroBits()
-	if vk := v.abs.trailingZeroBits(); vk < k {
-		k = vk
-	}
-	u.Rsh(u, k)
-	v.Rsh(v, k)
-
-	// determine t (we know that u > 0)
-	t := new(Int)
-	if u.abs[0]&1 != 0 {
-		// u is odd
-		t.Neg(v)
-	} else {
-		t.Set(u)
-	}
-
-	for len(t.abs) > 0 {
-		// reduce t
-		t.Rsh(t, t.abs.trailingZeroBits())
-		if t.neg {
-			v, t = t, v
-			v.neg = len(v.abs) > 0 && !v.neg // 0 has no sign
-		} else {
-			u, t = t, u
-		}
-		t.Sub(u, v)
-	}
-
-	return z.Lsh(u, k)
-}
-
-// ProbablyPrime performs n Miller-Rabin tests to check whether x is prime.
-// If x is prime, it returns true.
-// If x is not prime, it returns false with probability at least 1 - ¼ⁿ.
-//
-// It is not suitable for judging primes that an adversary may have crafted
-// to fool this test.
-func (x *Int) ProbablyPrime(n int) bool {
-	if n <= 0 {
-		panic("non-positive n for ProbablyPrime")
-	}
-	return !x.neg && x.abs.probablyPrime(n)
-}
-
-// Rand sets z to a pseudo-random number in [0, n) and returns z.
-func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int {
-	z.neg = false
-	if n.neg == true || len(n.abs) == 0 {
-		z.abs = nil
-		return z
-	}
-	z.abs = z.abs.random(rnd, n.abs, n.abs.bitLen())
-	return z
-}
-
-// ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ
-// and returns z. If g and n are not relatively prime, the result is undefined.
-func (z *Int) ModInverse(g, n *Int) *Int {
-	var d Int
-	d.GCD(z, nil, g, n)
-	// x and y are such that g*x + n*y = d. Since g and n are
-	// relatively prime, d = 1. Taking that modulo n results in
-	// g*x = 1, therefore x is the inverse element.
-	if z.neg {
-		z.Add(z, n)
-	}
-	return z
-}
-
-// Jacobi returns the Jacobi symbol (x/y), either +1, -1, or 0.
-// The y argument must be an odd integer.
-func Jacobi(x, y *Int) int {
-	if len(y.abs) == 0 || y.abs[0]&1 == 0 {
-		panic(fmt.Sprintf("big: invalid 2nd argument to Int.Jacobi: need odd integer but got %s", y))
-	}
-
-	// We use the formulation described in chapter 2, section 2.4,
-	// "The Yacas Book of Algorithms":
-	// http://yacas.sourceforge.net/Algo.book.pdf
-
-	var a, b, c Int
-	a.Set(x)
-	b.Set(y)
-	j := 1
-
-	if b.neg {
-		if a.neg {
-			j = -1
-		}
-		b.neg = false
-	}
-
-	for {
-		if b.Cmp(intOne) == 0 {
-			return j
-		}
-		if len(a.abs) == 0 {
-			return 0
-		}
-		a.Mod(&a, &b)
-		if len(a.abs) == 0 {
-			return 0
-		}
-		// a > 0
-
-		// handle factors of 2 in 'a'
-		s := a.abs.trailingZeroBits()
-		if s&1 != 0 {
-			bmod8 := b.abs[0] & 7
-			if bmod8 == 3 || bmod8 == 5 {
-				j = -j
-			}
-		}
-		c.Rsh(&a, s) // a = 2^s*c
-
-		// swap numerator and denominator
-		if b.abs[0]&3 == 3 && c.abs[0]&3 == 3 {
-			j = -j
-		}
-		a.Set(&b)
-		b.Set(&c)
-	}
-}
-
-// modSqrt3Mod4 uses the identity
-//      (a^((p+1)/4))^2  mod p
-//   == u^(p+1)          mod p
-//   == u^2              mod p
-// to calculate the square root of any quadratic residue mod p quickly for 3
-// mod 4 primes.
-func (z *Int) modSqrt3Mod4Prime(x, p *Int) *Int {
-	z.Set(p)         // z = p
-	z.Add(z, intOne) // z = p + 1
-	z.Rsh(z, 2)      // z = (p + 1) / 4
-	z.Exp(x, z, p)   // z = x^z mod p
-	return z
-}
-
-// modSqrtTonelliShanks uses the Tonelli-Shanks algorithm to find the square
-// root of a quadratic residue modulo any prime.
-func (z *Int) modSqrtTonelliShanks(x, p *Int) *Int {
-	// Break p-1 into s*2^e such that s is odd.
-	var s Int
-	s.Sub(p, intOne)
-	e := s.abs.trailingZeroBits()
-	s.Rsh(&s, e)
-
-	// find some non-square n
-	var n Int
-	n.SetInt64(2)
-	for Jacobi(&n, p) != -1 {
-		n.Add(&n, intOne)
-	}
-
-	// Core of the Tonelli-Shanks algorithm. Follows the description in
-	// section 6 of "Square roots from 1; 24, 51, 10 to Dan Shanks" by Ezra
-	// Brown:
-	// https://www.maa.org/sites/default/files/pdf/upload_library/22/Polya/07468342.di020786.02p0470a.pdf
-	var y, b, g, t Int
-	y.Add(&s, intOne)
-	y.Rsh(&y, 1)
-	y.Exp(x, &y, p)  // y = x^((s+1)/2)
-	b.Exp(x, &s, p)  // b = x^s
-	g.Exp(&n, &s, p) // g = n^s
-	r := e
-	for {
-		// find the least m such that ord_p(b) = 2^m
-		var m uint
-		t.Set(&b)
-		for t.Cmp(intOne) != 0 {
-			t.Mul(&t, &t).Mod(&t, p)
-			m++
-		}
-
-		if m == 0 {
-			return z.Set(&y)
-		}
-
-		t.SetInt64(0).SetBit(&t, int(r-m-1), 1).Exp(&g, &t, p)
-		// t = g^(2^(r-m-1)) mod p
-		g.Mul(&t, &t).Mod(&g, p) // g = g^(2^(r-m)) mod p
-		y.Mul(&y, &t).Mod(&y, p)
-		b.Mul(&b, &g).Mod(&b, p)
-		r = m
-	}
-}
-
-// ModSqrt sets z to a square root of x mod p if such a square root exists, and
-// returns z. The modulus p must be an odd prime. If x is not a square mod p,
-// ModSqrt leaves z unchanged and returns nil. This function panics if p is
-// not an odd integer.
-func (z *Int) ModSqrt(x, p *Int) *Int {
-	switch Jacobi(x, p) {
-	case -1:
-		return nil // x is not a square mod p
-	case 0:
-		return z.SetInt64(0) // sqrt(0) mod p = 0
-	case 1:
-		break
-	}
-	if x.neg || x.Cmp(p) >= 0 { // ensure 0 <= x < p
-		x = new(Int).Mod(x, p)
-	}
-
-	// Check whether p is 3 mod 4, and if so, use the faster algorithm.
-	if len(p.abs) > 0 && p.abs[0]%4 == 3 {
-		return z.modSqrt3Mod4Prime(x, p)
-	}
-	// Otherwise, use Tonelli-Shanks.
-	return z.modSqrtTonelliShanks(x, p)
-}
-
-// Lsh sets z = x << n and returns z.
-func (z *Int) Lsh(x *Int, n uint) *Int {
-	z.abs = z.abs.shl(x.abs, n)
-	z.neg = x.neg
-	return z
-}
-
-// Rsh sets z = x >> n and returns z.
-func (z *Int) Rsh(x *Int, n uint) *Int {
-	if x.neg {
-		// (-x) >> s == ^(x-1) >> s == ^((x-1) >> s) == -(((x-1) >> s) + 1)
-		t := z.abs.sub(x.abs, natOne) // no underflow because |x| > 0
-		t = t.shr(t, n)
-		z.abs = t.add(t, natOne)
-		z.neg = true // z cannot be zero if x is negative
-		return z
-	}
-
-	z.abs = z.abs.shr(x.abs, n)
-	z.neg = false
-	return z
-}
-
-// Bit returns the value of the i'th bit of x. That is, it
-// returns (x>>i)&1. The bit index i must be >= 0.
-func (x *Int) Bit(i int) uint {
-	if i == 0 {
-		// optimization for common case: odd/even test of x
-		if len(x.abs) > 0 {
-			return uint(x.abs[0] & 1) // bit 0 is same for -x
-		}
-		return 0
-	}
-	if i < 0 {
-		panic("negative bit index")
-	}
-	if x.neg {
-		t := nat(nil).sub(x.abs, natOne)
-		return t.bit(uint(i)) ^ 1
-	}
-
-	return x.abs.bit(uint(i))
-}
-
-// SetBit sets z to x, with x's i'th bit set to b (0 or 1).
-// That is, if b is 1 SetBit sets z = x | (1 << i);
-// if b is 0 SetBit sets z = x &^ (1 << i). If b is not 0 or 1,
-// SetBit will panic.
-func (z *Int) SetBit(x *Int, i int, b uint) *Int {
-	if i < 0 {
-		panic("negative bit index")
-	}
-	if x.neg {
-		t := z.abs.sub(x.abs, natOne)
-		t = t.setBit(t, uint(i), b^1)
-		z.abs = t.add(t, natOne)
-		z.neg = len(z.abs) > 0
-		return z
-	}
-	z.abs = z.abs.setBit(x.abs, uint(i), b)
-	z.neg = false
-	return z
-}
-
-// And sets z = x & y and returns z.
-func (z *Int) And(x, y *Int) *Int {
-	if x.neg == y.neg {
-		if x.neg {
-			// (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1)
-			x1 := nat(nil).sub(x.abs, natOne)
-			y1 := nat(nil).sub(y.abs, natOne)
-			z.abs = z.abs.add(z.abs.or(x1, y1), natOne)
-			z.neg = true // z cannot be zero if x and y are negative
-			return z
-		}
-
-		// x & y == x & y
-		z.abs = z.abs.and(x.abs, y.abs)
-		z.neg = false
-		return z
-	}
-
-	// x.neg != y.neg
-	if x.neg {
-		x, y = y, x // & is symmetric
-	}
-
-	// x & (-y) == x & ^(y-1) == x &^ (y-1)
-	y1 := nat(nil).sub(y.abs, natOne)
-	z.abs = z.abs.andNot(x.abs, y1)
-	z.neg = false
-	return z
-}
-
-// AndNot sets z = x &^ y and returns z.
-func (z *Int) AndNot(x, y *Int) *Int {
-	if x.neg == y.neg {
-		if x.neg {
-			// (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1)
-			x1 := nat(nil).sub(x.abs, natOne)
-			y1 := nat(nil).sub(y.abs, natOne)
-			z.abs = z.abs.andNot(y1, x1)
-			z.neg = false
-			return z
-		}
-
-		// x &^ y == x &^ y
-		z.abs = z.abs.andNot(x.abs, y.abs)
-		z.neg = false
-		return z
-	}
-
-	if x.neg {
-		// (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1)
-		x1 := nat(nil).sub(x.abs, natOne)
-		z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne)
-		z.neg = true // z cannot be zero if x is negative and y is positive
-		return z
-	}
-
-	// x &^ (-y) == x &^ ^(y-1) == x & (y-1)
-	y1 := nat(nil).sub(y.abs, natOne)
-	z.abs = z.abs.and(x.abs, y1)
-	z.neg = false
-	return z
-}
-
-// Or sets z = x | y and returns z.
-func (z *Int) Or(x, y *Int) *Int {
-	if x.neg == y.neg {
-		if x.neg {
-			// (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1)
-			x1 := nat(nil).sub(x.abs, natOne)
-			y1 := nat(nil).sub(y.abs, natOne)
-			z.abs = z.abs.add(z.abs.and(x1, y1), natOne)
-			z.neg = true // z cannot be zero if x and y are negative
-			return z
-		}
-
-		// x | y == x | y
-		z.abs = z.abs.or(x.abs, y.abs)
-		z.neg = false
-		return z
-	}
-
-	// x.neg != y.neg
-	if x.neg {
-		x, y = y, x // | is symmetric
-	}
-
-	// x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1)
-	y1 := nat(nil).sub(y.abs, natOne)
-	z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne)
-	z.neg = true // z cannot be zero if one of x or y is negative
-	return z
-}
-
-// Xor sets z = x ^ y and returns z.
-func (z *Int) Xor(x, y *Int) *Int {
-	if x.neg == y.neg {
-		if x.neg {
-			// (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1)
-			x1 := nat(nil).sub(x.abs, natOne)
-			y1 := nat(nil).sub(y.abs, natOne)
-			z.abs = z.abs.xor(x1, y1)
-			z.neg = false
-			return z
-		}
-
-		// x ^ y == x ^ y
-		z.abs = z.abs.xor(x.abs, y.abs)
-		z.neg = false
-		return z
-	}
-
-	// x.neg != y.neg
-	if x.neg {
-		x, y = y, x // ^ is symmetric
-	}
-
-	// x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1)
-	y1 := nat(nil).sub(y.abs, natOne)
-	z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne)
-	z.neg = true // z cannot be zero if only one of x or y is negative
-	return z
-}
-
-// Not sets z = ^x and returns z.
-func (z *Int) Not(x *Int) *Int {
-	if x.neg {
-		// ^(-x) == ^(^(x-1)) == x-1
-		z.abs = z.abs.sub(x.abs, natOne)
-		z.neg = false
-		return z
-	}
-
-	// ^x == -x-1 == -(x+1)
-	z.abs = z.abs.add(x.abs, natOne)
-	z.neg = true // z cannot be zero if x is positive
-	return z
-}
diff --git a/src/cmd/compile/internal/big/int_test.go b/src/cmd/compile/internal/big/int_test.go
deleted file mode 100644
index 45a3765..0000000
--- a/src/cmd/compile/internal/big/int_test.go
+++ /dev/null
@@ -1,1482 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"bytes"
-	"encoding/hex"
-	"fmt"
-	"math/rand"
-	"testing"
-	"testing/quick"
-)
-
-func isNormalized(x *Int) bool {
-	if len(x.abs) == 0 {
-		return !x.neg
-	}
-	// len(x.abs) > 0
-	return x.abs[len(x.abs)-1] != 0
-}
-
-type funZZ func(z, x, y *Int) *Int
-type argZZ struct {
-	z, x, y *Int
-}
-
-var sumZZ = []argZZ{
-	{NewInt(0), NewInt(0), NewInt(0)},
-	{NewInt(1), NewInt(1), NewInt(0)},
-	{NewInt(1111111110), NewInt(123456789), NewInt(987654321)},
-	{NewInt(-1), NewInt(-1), NewInt(0)},
-	{NewInt(864197532), NewInt(-123456789), NewInt(987654321)},
-	{NewInt(-1111111110), NewInt(-123456789), NewInt(-987654321)},
-}
-
-var prodZZ = []argZZ{
-	{NewInt(0), NewInt(0), NewInt(0)},
-	{NewInt(0), NewInt(1), NewInt(0)},
-	{NewInt(1), NewInt(1), NewInt(1)},
-	{NewInt(-991 * 991), NewInt(991), NewInt(-991)},
-	// TODO(gri) add larger products
-}
-
-func TestSignZ(t *testing.T) {
-	var zero Int
-	for _, a := range sumZZ {
-		s := a.z.Sign()
-		e := a.z.Cmp(&zero)
-		if s != e {
-			t.Errorf("got %d; want %d for z = %v", s, e, a.z)
-		}
-	}
-}
-
-func TestSetZ(t *testing.T) {
-	for _, a := range sumZZ {
-		var z Int
-		z.Set(a.z)
-		if !isNormalized(&z) {
-			t.Errorf("%v is not normalized", z)
-		}
-		if (&z).Cmp(a.z) != 0 {
-			t.Errorf("got z = %v; want %v", z, a.z)
-		}
-	}
-}
-
-func TestAbsZ(t *testing.T) {
-	var zero Int
-	for _, a := range sumZZ {
-		var z Int
-		z.Abs(a.z)
-		var e Int
-		e.Set(a.z)
-		if e.Cmp(&zero) < 0 {
-			e.Sub(&zero, &e)
-		}
-		if z.Cmp(&e) != 0 {
-			t.Errorf("got z = %v; want %v", z, e)
-		}
-	}
-}
-
-func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) {
-	var z Int
-	f(&z, a.x, a.y)
-	if !isNormalized(&z) {
-		t.Errorf("%s%v is not normalized", msg, z)
-	}
-	if (&z).Cmp(a.z) != 0 {
-		t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z)
-	}
-}
-
-func TestSumZZ(t *testing.T) {
-	AddZZ := func(z, x, y *Int) *Int { return z.Add(x, y) }
-	SubZZ := func(z, x, y *Int) *Int { return z.Sub(x, y) }
-	for _, a := range sumZZ {
-		arg := a
-		testFunZZ(t, "AddZZ", AddZZ, arg)
-
-		arg = argZZ{a.z, a.y, a.x}
-		testFunZZ(t, "AddZZ symmetric", AddZZ, arg)
-
-		arg = argZZ{a.x, a.z, a.y}
-		testFunZZ(t, "SubZZ", SubZZ, arg)
-
-		arg = argZZ{a.y, a.z, a.x}
-		testFunZZ(t, "SubZZ symmetric", SubZZ, arg)
-	}
-}
-
-func TestProdZZ(t *testing.T) {
-	MulZZ := func(z, x, y *Int) *Int { return z.Mul(x, y) }
-	for _, a := range prodZZ {
-		arg := a
-		testFunZZ(t, "MulZZ", MulZZ, arg)
-
-		arg = argZZ{a.z, a.y, a.x}
-		testFunZZ(t, "MulZZ symmetric", MulZZ, arg)
-	}
-}
-
-// mulBytes returns x*y via grade school multiplication. Both inputs
-// and the result are assumed to be in big-endian representation (to
-// match the semantics of Int.Bytes and Int.SetBytes).
-func mulBytes(x, y []byte) []byte {
-	z := make([]byte, len(x)+len(y))
-
-	// multiply
-	k0 := len(z) - 1
-	for j := len(y) - 1; j >= 0; j-- {
-		d := int(y[j])
-		if d != 0 {
-			k := k0
-			carry := 0
-			for i := len(x) - 1; i >= 0; i-- {
-				t := int(z[k]) + int(x[i])*d + carry
-				z[k], carry = byte(t), t>>8
-				k--
-			}
-			z[k] = byte(carry)
-		}
-		k0--
-	}
-
-	// normalize (remove leading 0's)
-	i := 0
-	for i < len(z) && z[i] == 0 {
-		i++
-	}
-
-	return z[i:]
-}
-
-func checkMul(a, b []byte) bool {
-	var x, y, z1 Int
-	x.SetBytes(a)
-	y.SetBytes(b)
-	z1.Mul(&x, &y)
-
-	var z2 Int
-	z2.SetBytes(mulBytes(a, b))
-
-	return z1.Cmp(&z2) == 0
-}
-
-func TestMul(t *testing.T) {
-	if err := quick.Check(checkMul, nil); err != nil {
-		t.Error(err)
-	}
-}
-
-var mulRangesZ = []struct {
-	a, b int64
-	prod string
-}{
-	// entirely positive ranges are covered by mulRangesN
-	{-1, 1, "0"},
-	{-2, -1, "2"},
-	{-3, -2, "6"},
-	{-3, -1, "-6"},
-	{1, 3, "6"},
-	{-10, -10, "-10"},
-	{0, -1, "1"},                      // empty range
-	{-1, -100, "1"},                   // empty range
-	{-1, 1, "0"},                      // range includes 0
-	{-1e9, 0, "0"},                    // range includes 0
-	{-1e9, 1e9, "0"},                  // range includes 0
-	{-10, -1, "3628800"},              // 10!
-	{-20, -2, "-2432902008176640000"}, // -20!
-	{-99, -1,
-		"-933262154439441526816992388562667004907159682643816214685929" +
-			"638952175999932299156089414639761565182862536979208272237582" +
-			"511852109168640000000000000000000000", // -99!
-	},
-}
-
-func TestMulRangeZ(t *testing.T) {
-	var tmp Int
-	// test entirely positive ranges
-	for i, r := range mulRangesN {
-		prod := tmp.MulRange(int64(r.a), int64(r.b)).String()
-		if prod != r.prod {
-			t.Errorf("#%da: got %s; want %s", i, prod, r.prod)
-		}
-	}
-	// test other ranges
-	for i, r := range mulRangesZ {
-		prod := tmp.MulRange(r.a, r.b).String()
-		if prod != r.prod {
-			t.Errorf("#%db: got %s; want %s", i, prod, r.prod)
-		}
-	}
-}
-
-func TestBinomial(t *testing.T) {
-	var z Int
-	for _, test := range []struct {
-		n, k int64
-		want string
-	}{
-		{0, 0, "1"},
-		{0, 1, "0"},
-		{1, 0, "1"},
-		{1, 1, "1"},
-		{1, 10, "0"},
-		{4, 0, "1"},
-		{4, 1, "4"},
-		{4, 2, "6"},
-		{4, 3, "4"},
-		{4, 4, "1"},
-		{10, 1, "10"},
-		{10, 9, "10"},
-		{10, 5, "252"},
-		{11, 5, "462"},
-		{11, 6, "462"},
-		{100, 10, "17310309456440"},
-		{100, 90, "17310309456440"},
-		{1000, 10, "263409560461970212832400"},
-		{1000, 990, "263409560461970212832400"},
-	} {
-		if got := z.Binomial(test.n, test.k).String(); got != test.want {
-			t.Errorf("Binomial(%d, %d) = %s; want %s", test.n, test.k, got, test.want)
-		}
-	}
-}
-
-func BenchmarkBinomial(b *testing.B) {
-	var z Int
-	for i := b.N - 1; i >= 0; i-- {
-		z.Binomial(1000, 990)
-	}
-}
-
-// Examples from the Go Language Spec, section "Arithmetic operators"
-var divisionSignsTests = []struct {
-	x, y int64
-	q, r int64 // T-division
-	d, m int64 // Euclidian division
-}{
-	{5, 3, 1, 2, 1, 2},
-	{-5, 3, -1, -2, -2, 1},
-	{5, -3, -1, 2, -1, 2},
-	{-5, -3, 1, -2, 2, 1},
-	{1, 2, 0, 1, 0, 1},
-	{8, 4, 2, 0, 2, 0},
-}
-
-func TestDivisionSigns(t *testing.T) {
-	for i, test := range divisionSignsTests {
-		x := NewInt(test.x)
-		y := NewInt(test.y)
-		q := NewInt(test.q)
-		r := NewInt(test.r)
-		d := NewInt(test.d)
-		m := NewInt(test.m)
-
-		q1 := new(Int).Quo(x, y)
-		r1 := new(Int).Rem(x, y)
-		if !isNormalized(q1) {
-			t.Errorf("#%d Quo: %v is not normalized", i, *q1)
-		}
-		if !isNormalized(r1) {
-			t.Errorf("#%d Rem: %v is not normalized", i, *r1)
-		}
-		if q1.Cmp(q) != 0 || r1.Cmp(r) != 0 {
-			t.Errorf("#%d QuoRem: got (%s, %s), want (%s, %s)", i, q1, r1, q, r)
-		}
-
-		q2, r2 := new(Int).QuoRem(x, y, new(Int))
-		if !isNormalized(q2) {
-			t.Errorf("#%d Quo: %v is not normalized", i, *q2)
-		}
-		if !isNormalized(r2) {
-			t.Errorf("#%d Rem: %v is not normalized", i, *r2)
-		}
-		if q2.Cmp(q) != 0 || r2.Cmp(r) != 0 {
-			t.Errorf("#%d QuoRem: got (%s, %s), want (%s, %s)", i, q2, r2, q, r)
-		}
-
-		d1 := new(Int).Div(x, y)
-		m1 := new(Int).Mod(x, y)
-		if !isNormalized(d1) {
-			t.Errorf("#%d Div: %v is not normalized", i, *d1)
-		}
-		if !isNormalized(m1) {
-			t.Errorf("#%d Mod: %v is not normalized", i, *m1)
-		}
-		if d1.Cmp(d) != 0 || m1.Cmp(m) != 0 {
-			t.Errorf("#%d DivMod: got (%s, %s), want (%s, %s)", i, d1, m1, d, m)
-		}
-
-		d2, m2 := new(Int).DivMod(x, y, new(Int))
-		if !isNormalized(d2) {
-			t.Errorf("#%d Div: %v is not normalized", i, *d2)
-		}
-		if !isNormalized(m2) {
-			t.Errorf("#%d Mod: %v is not normalized", i, *m2)
-		}
-		if d2.Cmp(d) != 0 || m2.Cmp(m) != 0 {
-			t.Errorf("#%d DivMod: got (%s, %s), want (%s, %s)", i, d2, m2, d, m)
-		}
-	}
-}
-
-func norm(x nat) nat {
-	i := len(x)
-	for i > 0 && x[i-1] == 0 {
-		i--
-	}
-	return x[:i]
-}
-
-func TestBits(t *testing.T) {
-	for _, test := range []nat{
-		nil,
-		{0},
-		{1},
-		{0, 1, 2, 3, 4},
-		{4, 3, 2, 1, 0},
-		{4, 3, 2, 1, 0, 0, 0, 0},
-	} {
-		var z Int
-		z.neg = true
-		got := z.SetBits(test)
-		want := norm(test)
-		if got.abs.cmp(want) != 0 {
-			t.Errorf("SetBits(%v) = %v; want %v", test, got.abs, want)
-		}
-
-		if got.neg {
-			t.Errorf("SetBits(%v): got negative result", test)
-		}
-
-		bits := nat(z.Bits())
-		if bits.cmp(want) != 0 {
-			t.Errorf("%v.Bits() = %v; want %v", z.abs, bits, want)
-		}
-	}
-}
-
-func checkSetBytes(b []byte) bool {
-	hex1 := hex.EncodeToString(new(Int).SetBytes(b).Bytes())
-	hex2 := hex.EncodeToString(b)
-
-	for len(hex1) < len(hex2) {
-		hex1 = "0" + hex1
-	}
-
-	for len(hex1) > len(hex2) {
-		hex2 = "0" + hex2
-	}
-
-	return hex1 == hex2
-}
-
-func TestSetBytes(t *testing.T) {
-	if err := quick.Check(checkSetBytes, nil); err != nil {
-		t.Error(err)
-	}
-}
-
-func checkBytes(b []byte) bool {
-	// trim leading zero bytes since Bytes() won't return them
-	// (was issue 12231)
-	for len(b) > 0 && b[0] == 0 {
-		b = b[1:]
-	}
-	b2 := new(Int).SetBytes(b).Bytes()
-	return bytes.Equal(b, b2)
-}
-
-func TestBytes(t *testing.T) {
-	if err := quick.Check(checkBytes, nil); err != nil {
-		t.Error(err)
-	}
-}
-
-func checkQuo(x, y []byte) bool {
-	u := new(Int).SetBytes(x)
-	v := new(Int).SetBytes(y)
-
-	if len(v.abs) == 0 {
-		return true
-	}
-
-	r := new(Int)
-	q, r := new(Int).QuoRem(u, v, r)
-
-	if r.Cmp(v) >= 0 {
-		return false
-	}
-
-	uprime := new(Int).Set(q)
-	uprime.Mul(uprime, v)
-	uprime.Add(uprime, r)
-
-	return uprime.Cmp(u) == 0
-}
-
-var quoTests = []struct {
-	x, y string
-	q, r string
-}{
-	{
-		"476217953993950760840509444250624797097991362735329973741718102894495832294430498335824897858659711275234906400899559094370964723884706254265559534144986498357",
-		"9353930466774385905609975137998169297361893554149986716853295022578535724979483772383667534691121982974895531435241089241440253066816724367338287092081996",
-		"50911",
-		"1",
-	},
-	{
-		"11510768301994997771168",
-		"1328165573307167369775",
-		"8",
-		"885443715537658812968",
-	},
-}
-
-func TestQuo(t *testing.T) {
-	if err := quick.Check(checkQuo, nil); err != nil {
-		t.Error(err)
-	}
-
-	for i, test := range quoTests {
-		x, _ := new(Int).SetString(test.x, 10)
-		y, _ := new(Int).SetString(test.y, 10)
-		expectedQ, _ := new(Int).SetString(test.q, 10)
-		expectedR, _ := new(Int).SetString(test.r, 10)
-
-		r := new(Int)
-		q, r := new(Int).QuoRem(x, y, r)
-
-		if q.Cmp(expectedQ) != 0 || r.Cmp(expectedR) != 0 {
-			t.Errorf("#%d got (%s, %s) want (%s, %s)", i, q, r, expectedQ, expectedR)
-		}
-	}
-}
-
-func TestQuoStepD6(t *testing.T) {
-	// See Knuth, Volume 2, section 4.3.1, exercise 21. This code exercises
-	// a code path which only triggers 1 in 10^{-19} cases.
-
-	u := &Int{false, nat{0, 0, 1 + 1<<(_W-1), _M ^ (1 << (_W - 1))}}
-	v := &Int{false, nat{5, 2 + 1<<(_W-1), 1 << (_W - 1)}}
-
-	r := new(Int)
-	q, r := new(Int).QuoRem(u, v, r)
-	const expectedQ64 = "18446744073709551613"
-	const expectedR64 = "3138550867693340382088035895064302439801311770021610913807"
-	const expectedQ32 = "4294967293"
-	const expectedR32 = "39614081266355540837921718287"
-	if q.String() != expectedQ64 && q.String() != expectedQ32 ||
-		r.String() != expectedR64 && r.String() != expectedR32 {
-		t.Errorf("got (%s, %s) want (%s, %s) or (%s, %s)", q, r, expectedQ64, expectedR64, expectedQ32, expectedR32)
-	}
-}
-
-var bitLenTests = []struct {
-	in  string
-	out int
-}{
-	{"-1", 1},
-	{"0", 0},
-	{"1", 1},
-	{"2", 2},
-	{"4", 3},
-	{"0xabc", 12},
-	{"0x8000", 16},
-	{"0x80000000", 32},
-	{"0x800000000000", 48},
-	{"0x8000000000000000", 64},
-	{"0x80000000000000000000", 80},
-	{"-0x4000000000000000000000", 87},
-}
-
-func TestBitLen(t *testing.T) {
-	for i, test := range bitLenTests {
-		x, ok := new(Int).SetString(test.in, 0)
-		if !ok {
-			t.Errorf("#%d test input invalid: %s", i, test.in)
-			continue
-		}
-
-		if n := x.BitLen(); n != test.out {
-			t.Errorf("#%d got %d want %d", i, n, test.out)
-		}
-	}
-}
-
-var expTests = []struct {
-	x, y, m string
-	out     string
-}{
-	// y <= 0
-	{"0", "0", "", "1"},
-	{"1", "0", "", "1"},
-	{"-10", "0", "", "1"},
-	{"1234", "-1", "", "1"},
-
-	// m == 1
-	{"0", "0", "1", "0"},
-	{"1", "0", "1", "0"},
-	{"-10", "0", "1", "0"},
-	{"1234", "-1", "1", "0"},
-
-	// misc
-	{"5", "1", "3", "2"},
-	{"5", "-7", "", "1"},
-	{"-5", "-7", "", "1"},
-	{"5", "0", "", "1"},
-	{"-5", "0", "", "1"},
-	{"5", "1", "", "5"},
-	{"-5", "1", "", "-5"},
-	{"-5", "1", "7", "2"},
-	{"-2", "3", "2", "0"},
-	{"5", "2", "", "25"},
-	{"1", "65537", "2", "1"},
-	{"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"},
-	{"0x8000000000000000", "2", "6719", "4944"},
-	{"0x8000000000000000", "3", "6719", "5447"},
-	{"0x8000000000000000", "1000", "6719", "1603"},
-	{"0x8000000000000000", "1000000", "6719", "3199"},
-	{"0x8000000000000000", "-1000000", "6719", "1"},
-
-	{"0xffffffffffffffffffffffffffffffff", "0x12345678123456781234567812345678123456789", "0x01112222333344445555666677778889", "0x36168FA1DB3AAE6C8CE647E137F97A"},
-
-	{
-		"2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347",
-		"298472983472983471903246121093472394872319615612417471234712061",
-		"29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464",
-		"23537740700184054162508175125554701713153216681790245129157191391322321508055833908509185839069455749219131480588829346291",
-	},
-	// test case for issue 8822
-	{
-		"110012891183630896460173593721179634992505463752690475427779280061032468766887567357609056806046466243531968695727526232851404087554203740493176464281852700795553727635031156460546028675936629238941409408374795071949342675328316945655164667650254349023483145256274185156465881609558628390220513536530529470731360847807427297278748034576438481974995482975700269269275025056342970795272990042677697807685656954599452355868926270591788849987729893975050612063954555915037716775009312694775035 [...]
-		"0xB08FFB20760FFED58FADA86DFEF71AD72AA0FA763219618FE022C197E54708BB1191C66470250FCE8879487507CEE41381CA4D932F81C2B3F1AB20B539D50DCD",
-		"0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E37 [...]
-		"214842521977763024996399388837777103219931130979872010505011829095813593576185795667465563725893853616836105247305090413288550665149633855225708948390358847130516401714741865487135466864767613064364341464751401562843891818086750165768458333404948482836810888865842197505544080605567694866280290287207273932931116788263564804554339092335205041120744013761330771504712375494741491902420104695390064495966115766125739557543490423291306311282346379247864665857034884605402284774408534933920862 [...]
-	},
-	{
-		"-0x1BCE04427D8032319A89E5C4136456671AC620883F2C4139E57F91307C485AD2D6204F4F87A58262652DB5DBBAC72B0613E51B835E7153BEC6068F5C8D696B74DBD18FEC316AEF73985CF0475663208EB46B4F17DD9DA55367B03323E5491A70997B90C059FB34809E6EE55BCFBD5F2F52233BFE62E6AA9E4E26A1D4C2439883D14F2633D55D8AA66A1ACD5595E778AC3A280517F1157989E70C1A437B849F1877B779CC3CDDEDE2DAA6594A6C66D181A00A5F777EE60596D8773998F6E988DEAE4CCA60E4DDCF9590543C89F74F603259FCAD71660D30294FBBE6490300F78A9D63FA660DC9417B8B9DDA28BEB3977B621B98 [...]
-		"0xB08FFB20760FFED58FADA86DFEF71AD72AA0FA763219618FE022C197E54708BB1191C66470250FCE8879487507CEE41381CA4D932F81C2B3F1AB20B539D50DCD",
-		"0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E37 [...]
-		"214842521977763024996399388837777103219931130979872010505011829095813593576185795667465563725893853616836105247305090413288550665149633855225708948390358847130516401714741865487135466864767613064364341464751401562843891818086750165768458333404948482836810888865842197505544080605567694866280290287207273932931116788263564804554339092335205041120744013761330771504712375494741491902420104695390064495966115766125739557543490423291306311282346379247864665857034884605402284774408534933920862 [...]
-	},
-
-	// test cases for issue 13907
-	{"0xffffffff00000001", "0xffffffff00000001", "0xffffffff00000001", "0"},
-	{"0xffffffffffffffff00000001", "0xffffffffffffffff00000001", "0xffffffffffffffff00000001", "0"},
-	{"0xffffffffffffffffffffffff00000001", "0xffffffffffffffffffffffff00000001", "0xffffffffffffffffffffffff00000001", "0"},
-	{"0xffffffffffffffffffffffffffffffff00000001", "0xffffffffffffffffffffffffffffffff00000001", "0xffffffffffffffffffffffffffffffff00000001", "0"},
-}
-
-func TestExp(t *testing.T) {
-	for i, test := range expTests {
-		x, ok1 := new(Int).SetString(test.x, 0)
-		y, ok2 := new(Int).SetString(test.y, 0)
-		out, ok3 := new(Int).SetString(test.out, 0)
-
-		var ok4 bool
-		var m *Int
-
-		if len(test.m) == 0 {
-			m, ok4 = nil, true
-		} else {
-			m, ok4 = new(Int).SetString(test.m, 0)
-		}
-
-		if !ok1 || !ok2 || !ok3 || !ok4 {
-			t.Errorf("#%d: error in input", i)
-			continue
-		}
-
-		z1 := new(Int).Exp(x, y, m)
-		if !isNormalized(z1) {
-			t.Errorf("#%d: %v is not normalized", i, *z1)
-		}
-		if z1.Cmp(out) != 0 {
-			t.Errorf("#%d: got %x want %x", i, z1, out)
-		}
-
-		if m == nil {
-			// The result should be the same as for m == 0;
-			// specifically, there should be no div-zero panic.
-			m = &Int{abs: nat{}} // m != nil && len(m.abs) == 0
-			z2 := new(Int).Exp(x, y, m)
-			if z2.Cmp(z1) != 0 {
-				t.Errorf("#%d: got %x want %x", i, z2, z1)
-			}
-		}
-	}
-}
-
-func checkGcd(aBytes, bBytes []byte) bool {
-	x := new(Int)
-	y := new(Int)
-	a := new(Int).SetBytes(aBytes)
-	b := new(Int).SetBytes(bBytes)
-
-	d := new(Int).GCD(x, y, a, b)
-	x.Mul(x, a)
-	y.Mul(y, b)
-	x.Add(x, y)
-
-	return x.Cmp(d) == 0
-}
-
-var gcdTests = []struct {
-	d, x, y, a, b string
-}{
-	// a <= 0 || b <= 0
-	{"0", "0", "0", "0", "0"},
-	{"0", "0", "0", "0", "7"},
-	{"0", "0", "0", "11", "0"},
-	{"0", "0", "0", "-77", "35"},
-	{"0", "0", "0", "64515", "-24310"},
-	{"0", "0", "0", "-64515", "-24310"},
-
-	{"1", "-9", "47", "120", "23"},
-	{"7", "1", "-2", "77", "35"},
-	{"935", "-3", "8", "64515", "24310"},
-	{"935000000000000000", "-3", "8", "64515000000000000000", "24310000000000000000"},
-	{"1", "-221", "22059940471369027483332068679400581064239780177629666810348940098015901108344", "98920366548084643601728869055592650835572950932266967461790948584315647051443", "991"},
-
-	// test early exit (after one Euclidean iteration) in binaryGCD
-	{"1", "", "", "1", "98920366548084643601728869055592650835572950932266967461790948584315647051443"},
-}
-
-func testGcd(t *testing.T, d, x, y, a, b *Int) {
-	var X *Int
-	if x != nil {
-		X = new(Int)
-	}
-	var Y *Int
-	if y != nil {
-		Y = new(Int)
-	}
-
-	D := new(Int).GCD(X, Y, a, b)
-	if D.Cmp(d) != 0 {
-		t.Errorf("GCD(%s, %s): got d = %s, want %s", a, b, D, d)
-	}
-	if x != nil && X.Cmp(x) != 0 {
-		t.Errorf("GCD(%s, %s): got x = %s, want %s", a, b, X, x)
-	}
-	if y != nil && Y.Cmp(y) != 0 {
-		t.Errorf("GCD(%s, %s): got y = %s, want %s", a, b, Y, y)
-	}
-
-	// binaryGCD requires a > 0 && b > 0
-	if a.Sign() <= 0 || b.Sign() <= 0 {
-		return
-	}
-
-	D.binaryGCD(a, b)
-	if D.Cmp(d) != 0 {
-		t.Errorf("binaryGcd(%s, %s): got d = %s, want %s", a, b, D, d)
-	}
-
-	// check results in presence of aliasing (issue #11284)
-	a2 := new(Int).Set(a)
-	b2 := new(Int).Set(b)
-	a2.binaryGCD(a2, b2) // result is same as 1st argument
-	if a2.Cmp(d) != 0 {
-		t.Errorf("binaryGcd(%s, %s): got d = %s, want %s", a, b, a2, d)
-	}
-
-	a2 = new(Int).Set(a)
-	b2 = new(Int).Set(b)
-	b2.binaryGCD(a2, b2) // result is same as 2nd argument
-	if b2.Cmp(d) != 0 {
-		t.Errorf("binaryGcd(%s, %s): got d = %s, want %s", a, b, b2, d)
-	}
-}
-
-func TestGcd(t *testing.T) {
-	for _, test := range gcdTests {
-		d, _ := new(Int).SetString(test.d, 0)
-		x, _ := new(Int).SetString(test.x, 0)
-		y, _ := new(Int).SetString(test.y, 0)
-		a, _ := new(Int).SetString(test.a, 0)
-		b, _ := new(Int).SetString(test.b, 0)
-
-		testGcd(t, d, nil, nil, a, b)
-		testGcd(t, d, x, nil, a, b)
-		testGcd(t, d, nil, y, a, b)
-		testGcd(t, d, x, y, a, b)
-	}
-
-	if err := quick.Check(checkGcd, nil); err != nil {
-		t.Error(err)
-	}
-}
-
-var primes = []string{
-	"2",
-	"3",
-	"5",
-	"7",
-	"11",
-
-	"13756265695458089029",
-	"13496181268022124907",
-	"10953742525620032441",
-	"17908251027575790097",
-
-	// https://golang.org/issue/638
-	"18699199384836356663",
-
-	"98920366548084643601728869055592650835572950932266967461790948584315647051443",
-	"94560208308847015747498523884063394671606671904944666360068158221458669711639",
-
-	// http://primes.utm.edu/lists/small/small3.html
-	"449417999055441493994709297093108513015373787049558499205492347871729927573118262811508386655998299074566974373711472560655026288668094291699357843464363003144674940345912431129144354948751003607115263071543163",
-	"230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593",
-	"5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993",
-	"203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123",
-
-	// ECC primes: http://tools.ietf.org/html/draft-ladd-safecurves-02
-	"3618502788666131106986593281521497120414687020801267626233049500247285301239",                                                                                  // Curve1174: 2^251-9
-	"57896044618658097711785492504343953926634992332820282019728792003956564819949",                                                                                 // Curve25519: 2^255-19
-	"9850501549098619803069760025035903451269934817616361666987073351061430442874302652853566563721228910201656997576599",                                           // E-382: 2^382-105
-	"42307582002575910332922579714097346549017899709713998034217522897561970639123926132812109468141778230245837569601494931472367",                                 // Curve41417: 2^414-17
-	"6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", // E-521: 2^521-1
-}
-
-var composites = []string{
-	"0",
-	"1",
-	"21284175091214687912771199898307297748211672914763848041968395774954376176754",
-	"6084766654921918907427900243509372380954290099172559290432744450051395395951",
-	"84594350493221918389213352992032324280367711247940675652888030554255915464401",
-	"82793403787388584738507275144194252681",
-}
-
-func TestProbablyPrime(t *testing.T) {
-	nreps := 20
-	if testing.Short() {
-		nreps = 1
-	}
-	for i, s := range primes {
-		p, _ := new(Int).SetString(s, 10)
-		if !p.ProbablyPrime(nreps) {
-			t.Errorf("#%d prime found to be non-prime (%s)", i, s)
-		}
-	}
-
-	for i, s := range composites {
-		c, _ := new(Int).SetString(s, 10)
-		if c.ProbablyPrime(nreps) {
-			t.Errorf("#%d composite found to be prime (%s)", i, s)
-		}
-		if testing.Short() {
-			break
-		}
-	}
-
-	// check that ProbablyPrime panics if n <= 0
-	c := NewInt(11) // a prime
-	for _, n := range []int{-1, 0, 1} {
-		func() {
-			defer func() {
-				if n <= 0 && recover() == nil {
-					t.Fatalf("expected panic from ProbablyPrime(%d)", n)
-				}
-			}()
-			if !c.ProbablyPrime(n) {
-				t.Fatalf("%v should be a prime", c)
-			}
-		}()
-	}
-}
-
-type intShiftTest struct {
-	in    string
-	shift uint
-	out   string
-}
-
-var rshTests = []intShiftTest{
-	{"0", 0, "0"},
-	{"-0", 0, "0"},
-	{"0", 1, "0"},
-	{"0", 2, "0"},
-	{"1", 0, "1"},
-	{"1", 1, "0"},
-	{"1", 2, "0"},
-	{"2", 0, "2"},
-	{"2", 1, "1"},
-	{"-1", 0, "-1"},
-	{"-1", 1, "-1"},
-	{"-1", 10, "-1"},
-	{"-100", 2, "-25"},
-	{"-100", 3, "-13"},
-	{"-100", 100, "-1"},
-	{"4294967296", 0, "4294967296"},
-	{"4294967296", 1, "2147483648"},
-	{"4294967296", 2, "1073741824"},
-	{"18446744073709551616", 0, "18446744073709551616"},
-	{"18446744073709551616", 1, "9223372036854775808"},
-	{"18446744073709551616", 2, "4611686018427387904"},
-	{"18446744073709551616", 64, "1"},
-	{"340282366920938463463374607431768211456", 64, "18446744073709551616"},
-	{"340282366920938463463374607431768211456", 128, "1"},
-}
-
-func TestRsh(t *testing.T) {
-	for i, test := range rshTests {
-		in, _ := new(Int).SetString(test.in, 10)
-		expected, _ := new(Int).SetString(test.out, 10)
-		out := new(Int).Rsh(in, test.shift)
-
-		if !isNormalized(out) {
-			t.Errorf("#%d: %v is not normalized", i, *out)
-		}
-		if out.Cmp(expected) != 0 {
-			t.Errorf("#%d: got %s want %s", i, out, expected)
-		}
-	}
-}
-
-func TestRshSelf(t *testing.T) {
-	for i, test := range rshTests {
-		z, _ := new(Int).SetString(test.in, 10)
-		expected, _ := new(Int).SetString(test.out, 10)
-		z.Rsh(z, test.shift)
-
-		if !isNormalized(z) {
-			t.Errorf("#%d: %v is not normalized", i, *z)
-		}
-		if z.Cmp(expected) != 0 {
-			t.Errorf("#%d: got %s want %s", i, z, expected)
-		}
-	}
-}
-
-var lshTests = []intShiftTest{
-	{"0", 0, "0"},
-	{"0", 1, "0"},
-	{"0", 2, "0"},
-	{"1", 0, "1"},
-	{"1", 1, "2"},
-	{"1", 2, "4"},
-	{"2", 0, "2"},
-	{"2", 1, "4"},
-	{"2", 2, "8"},
-	{"-87", 1, "-174"},
-	{"4294967296", 0, "4294967296"},
-	{"4294967296", 1, "8589934592"},
-	{"4294967296", 2, "17179869184"},
-	{"18446744073709551616", 0, "18446744073709551616"},
-	{"9223372036854775808", 1, "18446744073709551616"},
-	{"4611686018427387904", 2, "18446744073709551616"},
-	{"1", 64, "18446744073709551616"},
-	{"18446744073709551616", 64, "340282366920938463463374607431768211456"},
-	{"1", 128, "340282366920938463463374607431768211456"},
-}
-
-func TestLsh(t *testing.T) {
-	for i, test := range lshTests {
-		in, _ := new(Int).SetString(test.in, 10)
-		expected, _ := new(Int).SetString(test.out, 10)
-		out := new(Int).Lsh(in, test.shift)
-
-		if !isNormalized(out) {
-			t.Errorf("#%d: %v is not normalized", i, *out)
-		}
-		if out.Cmp(expected) != 0 {
-			t.Errorf("#%d: got %s want %s", i, out, expected)
-		}
-	}
-}
-
-func TestLshSelf(t *testing.T) {
-	for i, test := range lshTests {
-		z, _ := new(Int).SetString(test.in, 10)
-		expected, _ := new(Int).SetString(test.out, 10)
-		z.Lsh(z, test.shift)
-
-		if !isNormalized(z) {
-			t.Errorf("#%d: %v is not normalized", i, *z)
-		}
-		if z.Cmp(expected) != 0 {
-			t.Errorf("#%d: got %s want %s", i, z, expected)
-		}
-	}
-}
-
-func TestLshRsh(t *testing.T) {
-	for i, test := range rshTests {
-		in, _ := new(Int).SetString(test.in, 10)
-		out := new(Int).Lsh(in, test.shift)
-		out = out.Rsh(out, test.shift)
-
-		if !isNormalized(out) {
-			t.Errorf("#%d: %v is not normalized", i, *out)
-		}
-		if in.Cmp(out) != 0 {
-			t.Errorf("#%d: got %s want %s", i, out, in)
-		}
-	}
-	for i, test := range lshTests {
-		in, _ := new(Int).SetString(test.in, 10)
-		out := new(Int).Lsh(in, test.shift)
-		out.Rsh(out, test.shift)
-
-		if !isNormalized(out) {
-			t.Errorf("#%d: %v is not normalized", i, *out)
-		}
-		if in.Cmp(out) != 0 {
-			t.Errorf("#%d: got %s want %s", i, out, in)
-		}
-	}
-}
-
-var int64Tests = []int64{
-	0,
-	1,
-	-1,
-	4294967295,
-	-4294967295,
-	4294967296,
-	-4294967296,
-	9223372036854775807,
-	-9223372036854775807,
-	-9223372036854775808,
-}
-
-func TestInt64(t *testing.T) {
-	for i, testVal := range int64Tests {
-		in := NewInt(testVal)
-		out := in.Int64()
-
-		if out != testVal {
-			t.Errorf("#%d got %d want %d", i, out, testVal)
-		}
-	}
-}
-
-var uint64Tests = []uint64{
-	0,
-	1,
-	4294967295,
-	4294967296,
-	8589934591,
-	8589934592,
-	9223372036854775807,
-	9223372036854775808,
-	18446744073709551615, // 1<<64 - 1
-}
-
-func TestUint64(t *testing.T) {
-	in := new(Int)
-	for i, testVal := range uint64Tests {
-		in.SetUint64(testVal)
-		out := in.Uint64()
-
-		if out != testVal {
-			t.Errorf("#%d got %d want %d", i, out, testVal)
-		}
-
-		str := fmt.Sprint(testVal)
-		strOut := in.String()
-		if strOut != str {
-			t.Errorf("#%d.String got %s want %s", i, strOut, str)
-		}
-	}
-}
-
-var bitwiseTests = []struct {
-	x, y                 string
-	and, or, xor, andNot string
-}{
-	{"0x00", "0x00", "0x00", "0x00", "0x00", "0x00"},
-	{"0x00", "0x01", "0x00", "0x01", "0x01", "0x00"},
-	{"0x01", "0x00", "0x00", "0x01", "0x01", "0x01"},
-	{"-0x01", "0x00", "0x00", "-0x01", "-0x01", "-0x01"},
-	{"-0xaf", "-0x50", "-0xf0", "-0x0f", "0xe1", "0x41"},
-	{"0x00", "-0x01", "0x00", "-0x01", "-0x01", "0x00"},
-	{"0x01", "0x01", "0x01", "0x01", "0x00", "0x00"},
-	{"-0x01", "-0x01", "-0x01", "-0x01", "0x00", "0x00"},
-	{"0x07", "0x08", "0x00", "0x0f", "0x0f", "0x07"},
-	{"0x05", "0x0f", "0x05", "0x0f", "0x0a", "0x00"},
-	{"0xff", "-0x0a", "0xf6", "-0x01", "-0xf7", "0x09"},
-	{"0x013ff6", "0x9a4e", "0x1a46", "0x01bffe", "0x01a5b8", "0x0125b0"},
-	{"-0x013ff6", "0x9a4e", "0x800a", "-0x0125b2", "-0x01a5bc", "-0x01c000"},
-	{"-0x013ff6", "-0x9a4e", "-0x01bffe", "-0x1a46", "0x01a5b8", "0x8008"},
-	{
-		"0x1000009dc6e3d9822cba04129bcbe3401",
-		"0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd",
-		"0x1000001186210100001000009048c2001",
-		"0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd",
-		"0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc",
-		"0x8c40c2d8822caa04120b8321400",
-	},
-	{
-		"0x1000009dc6e3d9822cba04129bcbe3401",
-		"-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd",
-		"0x8c40c2d8822caa04120b8321401",
-		"-0xb9bd7d543685789d57ca918e82229142459020483cd2014001fd",
-		"-0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fe",
-		"0x1000001186210100001000009048c2000",
-	},
-	{
-		"-0x1000009dc6e3d9822cba04129bcbe3401",
-		"-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd",
-		"-0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd",
-		"-0x1000001186210100001000009048c2001",
-		"0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc",
-		"0xb9bd7d543685789d57ca918e82229142459020483cd2014001fc",
-	},
-}
-
-type bitFun func(z, x, y *Int) *Int
-
-func testBitFun(t *testing.T, msg string, f bitFun, x, y *Int, exp string) {
-	expected := new(Int)
-	expected.SetString(exp, 0)
-
-	out := f(new(Int), x, y)
-	if out.Cmp(expected) != 0 {
-		t.Errorf("%s: got %s want %s", msg, out, expected)
-	}
-}
-
-func testBitFunSelf(t *testing.T, msg string, f bitFun, x, y *Int, exp string) {
-	self := new(Int)
-	self.Set(x)
-	expected := new(Int)
-	expected.SetString(exp, 0)
-
-	self = f(self, self, y)
-	if self.Cmp(expected) != 0 {
-		t.Errorf("%s: got %s want %s", msg, self, expected)
-	}
-}
-
-func altBit(x *Int, i int) uint {
-	z := new(Int).Rsh(x, uint(i))
-	z = z.And(z, NewInt(1))
-	if z.Cmp(new(Int)) != 0 {
-		return 1
-	}
-	return 0
-}
-
-func altSetBit(z *Int, x *Int, i int, b uint) *Int {
-	one := NewInt(1)
-	m := one.Lsh(one, uint(i))
-	switch b {
-	case 1:
-		return z.Or(x, m)
-	case 0:
-		return z.AndNot(x, m)
-	}
-	panic("set bit is not 0 or 1")
-}
-
-func testBitset(t *testing.T, x *Int) {
-	n := x.BitLen()
-	z := new(Int).Set(x)
-	z1 := new(Int).Set(x)
-	for i := 0; i < n+10; i++ {
-		old := z.Bit(i)
-		old1 := altBit(z1, i)
-		if old != old1 {
-			t.Errorf("bitset: inconsistent value for Bit(%s, %d), got %v want %v", z1, i, old, old1)
-		}
-		z := new(Int).SetBit(z, i, 1)
-		z1 := altSetBit(new(Int), z1, i, 1)
-		if z.Bit(i) == 0 {
-			t.Errorf("bitset: bit %d of %s got 0 want 1", i, x)
-		}
-		if z.Cmp(z1) != 0 {
-			t.Errorf("bitset: inconsistent value after SetBit 1, got %s want %s", z, z1)
-		}
-		z.SetBit(z, i, 0)
-		altSetBit(z1, z1, i, 0)
-		if z.Bit(i) != 0 {
-			t.Errorf("bitset: bit %d of %s got 1 want 0", i, x)
-		}
-		if z.Cmp(z1) != 0 {
-			t.Errorf("bitset: inconsistent value after SetBit 0, got %s want %s", z, z1)
-		}
-		altSetBit(z1, z1, i, old)
-		z.SetBit(z, i, old)
-		if z.Cmp(z1) != 0 {
-			t.Errorf("bitset: inconsistent value after SetBit old, got %s want %s", z, z1)
-		}
-	}
-	if z.Cmp(x) != 0 {
-		t.Errorf("bitset: got %s want %s", z, x)
-	}
-}
-
-var bitsetTests = []struct {
-	x string
-	i int
-	b uint
-}{
-	{"0", 0, 0},
-	{"0", 200, 0},
-	{"1", 0, 1},
-	{"1", 1, 0},
-	{"-1", 0, 1},
-	{"-1", 200, 1},
-	{"0x2000000000000000000000000000", 108, 0},
-	{"0x2000000000000000000000000000", 109, 1},
-	{"0x2000000000000000000000000000", 110, 0},
-	{"-0x2000000000000000000000000001", 108, 1},
-	{"-0x2000000000000000000000000001", 109, 0},
-	{"-0x2000000000000000000000000001", 110, 1},
-}
-
-func TestBitSet(t *testing.T) {
-	for _, test := range bitwiseTests {
-		x := new(Int)
-		x.SetString(test.x, 0)
-		testBitset(t, x)
-		x = new(Int)
-		x.SetString(test.y, 0)
-		testBitset(t, x)
-	}
-	for i, test := range bitsetTests {
-		x := new(Int)
-		x.SetString(test.x, 0)
-		b := x.Bit(test.i)
-		if b != test.b {
-			t.Errorf("#%d got %v want %v", i, b, test.b)
-		}
-	}
-	z := NewInt(1)
-	z.SetBit(NewInt(0), 2, 1)
-	if z.Cmp(NewInt(4)) != 0 {
-		t.Errorf("destination leaked into result; got %s want 4", z)
-	}
-}
-
-func BenchmarkBitset(b *testing.B) {
-	z := new(Int)
-	z.SetBit(z, 512, 1)
-	b.ResetTimer()
-	b.StartTimer()
-	for i := b.N - 1; i >= 0; i-- {
-		z.SetBit(z, i&512, 1)
-	}
-}
-
-func BenchmarkBitsetNeg(b *testing.B) {
-	z := NewInt(-1)
-	z.SetBit(z, 512, 0)
-	b.ResetTimer()
-	b.StartTimer()
-	for i := b.N - 1; i >= 0; i-- {
-		z.SetBit(z, i&512, 0)
-	}
-}
-
-func BenchmarkBitsetOrig(b *testing.B) {
-	z := new(Int)
-	altSetBit(z, z, 512, 1)
-	b.ResetTimer()
-	b.StartTimer()
-	for i := b.N - 1; i >= 0; i-- {
-		altSetBit(z, z, i&512, 1)
-	}
-}
-
-func BenchmarkBitsetNegOrig(b *testing.B) {
-	z := NewInt(-1)
-	altSetBit(z, z, 512, 0)
-	b.ResetTimer()
-	b.StartTimer()
-	for i := b.N - 1; i >= 0; i-- {
-		altSetBit(z, z, i&512, 0)
-	}
-}
-
-// tri generates the trinomial 2**(n*2) - 2**n - 1, which is always 3 mod 4 and
-// 7 mod 8, so that 2 is always a quadratic residue.
-func tri(n uint) *Int {
-	x := NewInt(1)
-	x.Lsh(x, n)
-	x2 := new(Int).Lsh(x, n)
-	x2.Sub(x2, x)
-	x2.Sub(x2, intOne)
-	return x2
-}
-
-func BenchmarkModSqrt225_Tonelli(b *testing.B) {
-	p := tri(225)
-	x := NewInt(2)
-	for i := 0; i < b.N; i++ {
-		x.SetUint64(2)
-		x.modSqrtTonelliShanks(x, p)
-	}
-}
-
-func BenchmarkModSqrt224_3Mod4(b *testing.B) {
-	p := tri(225)
-	x := new(Int).SetUint64(2)
-	for i := 0; i < b.N; i++ {
-		x.SetUint64(2)
-		x.modSqrt3Mod4Prime(x, p)
-	}
-}
-
-func BenchmarkModSqrt5430_Tonelli(b *testing.B) {
-	p := tri(5430)
-	x := new(Int).SetUint64(2)
-	for i := 0; i < b.N; i++ {
-		x.SetUint64(2)
-		x.modSqrtTonelliShanks(x, p)
-	}
-}
-
-func BenchmarkModSqrt5430_3Mod4(b *testing.B) {
-	p := tri(5430)
-	x := new(Int).SetUint64(2)
-	for i := 0; i < b.N; i++ {
-		x.SetUint64(2)
-		x.modSqrt3Mod4Prime(x, p)
-	}
-}
-
-func TestBitwise(t *testing.T) {
-	x := new(Int)
-	y := new(Int)
-	for _, test := range bitwiseTests {
-		x.SetString(test.x, 0)
-		y.SetString(test.y, 0)
-
-		testBitFun(t, "and", (*Int).And, x, y, test.and)
-		testBitFunSelf(t, "and", (*Int).And, x, y, test.and)
-		testBitFun(t, "andNot", (*Int).AndNot, x, y, test.andNot)
-		testBitFunSelf(t, "andNot", (*Int).AndNot, x, y, test.andNot)
-		testBitFun(t, "or", (*Int).Or, x, y, test.or)
-		testBitFunSelf(t, "or", (*Int).Or, x, y, test.or)
-		testBitFun(t, "xor", (*Int).Xor, x, y, test.xor)
-		testBitFunSelf(t, "xor", (*Int).Xor, x, y, test.xor)
-	}
-}
-
-var notTests = []struct {
-	in  string
-	out string
-}{
-	{"0", "-1"},
-	{"1", "-2"},
-	{"7", "-8"},
-	{"0", "-1"},
-	{"-81910", "81909"},
-	{
-		"298472983472983471903246121093472394872319615612417471234712061",
-		"-298472983472983471903246121093472394872319615612417471234712062",
-	},
-}
-
-func TestNot(t *testing.T) {
-	in := new(Int)
-	out := new(Int)
-	expected := new(Int)
-	for i, test := range notTests {
-		in.SetString(test.in, 10)
-		expected.SetString(test.out, 10)
-		out = out.Not(in)
-		if out.Cmp(expected) != 0 {
-			t.Errorf("#%d: got %s want %s", i, out, expected)
-		}
-		out = out.Not(out)
-		if out.Cmp(in) != 0 {
-			t.Errorf("#%d: got %s want %s", i, out, in)
-		}
-	}
-}
-
-var modInverseTests = []struct {
-	element string
-	modulus string
-}{
-	{"1234567", "458948883992"},
-	{"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"},
-}
-
-func TestModInverse(t *testing.T) {
-	var element, modulus, gcd, inverse Int
-	one := NewInt(1)
-	for i, test := range modInverseTests {
-		(&element).SetString(test.element, 10)
-		(&modulus).SetString(test.modulus, 10)
-		(&inverse).ModInverse(&element, &modulus)
-		(&inverse).Mul(&inverse, &element)
-		(&inverse).Mod(&inverse, &modulus)
-		if (&inverse).Cmp(one) != 0 {
-			t.Errorf("#%d: failed (e·e^(-1)=%s)", i, &inverse)
-		}
-	}
-	// exhaustive test for small values
-	for n := 2; n < 100; n++ {
-		(&modulus).SetInt64(int64(n))
-		for x := 1; x < n; x++ {
-			(&element).SetInt64(int64(x))
-			(&gcd).GCD(nil, nil, &element, &modulus)
-			if (&gcd).Cmp(one) != 0 {
-				continue
-			}
-			(&inverse).ModInverse(&element, &modulus)
-			(&inverse).Mul(&inverse, &element)
-			(&inverse).Mod(&inverse, &modulus)
-			if (&inverse).Cmp(one) != 0 {
-				t.Errorf("ModInverse(%d,%d)*%d%%%d=%d, not 1", &element, &modulus, &element, &modulus, &inverse)
-			}
-		}
-	}
-}
-
-// testModSqrt is a helper for TestModSqrt,
-// which checks that ModSqrt can compute a square-root of elt^2.
-func testModSqrt(t *testing.T, elt, mod, sq, sqrt *Int) bool {
-	var sqChk, sqrtChk, sqrtsq Int
-	sq.Mul(elt, elt)
-	sq.Mod(sq, mod)
-	z := sqrt.ModSqrt(sq, mod)
-	if z != sqrt {
-		t.Errorf("ModSqrt returned wrong value %s", z)
-	}
-
-	// test ModSqrt arguments outside the range [0,mod)
-	sqChk.Add(sq, mod)
-	z = sqrtChk.ModSqrt(&sqChk, mod)
-	if z != &sqrtChk || z.Cmp(sqrt) != 0 {
-		t.Errorf("ModSqrt returned inconsistent value %s", z)
-	}
-	sqChk.Sub(sq, mod)
-	z = sqrtChk.ModSqrt(&sqChk, mod)
-	if z != &sqrtChk || z.Cmp(sqrt) != 0 {
-		t.Errorf("ModSqrt returned inconsistent value %s", z)
-	}
-
-	// make sure we actually got a square root
-	if sqrt.Cmp(elt) == 0 {
-		return true // we found the "desired" square root
-	}
-	sqrtsq.Mul(sqrt, sqrt) // make sure we found the "other" one
-	sqrtsq.Mod(&sqrtsq, mod)
-	return sq.Cmp(&sqrtsq) == 0
-}
-
-func TestModSqrt(t *testing.T) {
-	var elt, mod, modx4, sq, sqrt Int
-	r := rand.New(rand.NewSource(9))
-	for i, s := range primes[1:] { // skip 2, use only odd primes
-		mod.SetString(s, 10)
-		modx4.Lsh(&mod, 2)
-
-		// test a few random elements per prime
-		for x := 1; x < 5; x++ {
-			elt.Rand(r, &modx4)
-			elt.Sub(&elt, &mod) // test range [-mod, 3*mod)
-			if !testModSqrt(t, &elt, &mod, &sq, &sqrt) {
-				t.Errorf("#%d: failed (sqrt(e) = %s)", i, &sqrt)
-			}
-		}
-
-		if testing.Short() && i > 2 {
-			break
-		}
-	}
-
-	if testing.Short() {
-		return
-	}
-
-	// exhaustive test for small values
-	for n := 3; n < 100; n++ {
-		mod.SetInt64(int64(n))
-		if !mod.ProbablyPrime(10) {
-			continue
-		}
-		isSquare := make([]bool, n)
-
-		// test all the squares
-		for x := 1; x < n; x++ {
-			elt.SetInt64(int64(x))
-			if !testModSqrt(t, &elt, &mod, &sq, &sqrt) {
-				t.Errorf("#%d: failed (sqrt(%d,%d) = %s)", x, &elt, &mod, &sqrt)
-			}
-			isSquare[sq.Uint64()] = true
-		}
-
-		// test all non-squares
-		for x := 1; x < n; x++ {
-			sq.SetInt64(int64(x))
-			z := sqrt.ModSqrt(&sq, &mod)
-			if !isSquare[x] && z != nil {
-				t.Errorf("#%d: failed (sqrt(%d,%d) = nil)", x, &sqrt, &mod)
-			}
-		}
-	}
-}
-
-func TestJacobi(t *testing.T) {
-	testCases := []struct {
-		x, y   int64
-		result int
-	}{
-		{0, 1, 1},
-		{0, -1, 1},
-		{1, 1, 1},
-		{1, -1, 1},
-		{0, 5, 0},
-		{1, 5, 1},
-		{2, 5, -1},
-		{-2, 5, -1},
-		{2, -5, -1},
-		{-2, -5, 1},
-		{3, 5, -1},
-		{5, 5, 0},
-		{-5, 5, 0},
-		{6, 5, 1},
-		{6, -5, 1},
-		{-6, 5, 1},
-		{-6, -5, -1},
-	}
-
-	var x, y Int
-
-	for i, test := range testCases {
-		x.SetInt64(test.x)
-		y.SetInt64(test.y)
-		expected := test.result
-		actual := Jacobi(&x, &y)
-		if actual != expected {
-			t.Errorf("#%d: Jacobi(%d, %d) = %d, but expected %d", i, test.x, test.y, actual, expected)
-		}
-	}
-}
-
-func TestJacobiPanic(t *testing.T) {
-	const failureMsg = "test failure"
-	defer func() {
-		msg := recover()
-		if msg == nil || msg == failureMsg {
-			panic(msg)
-		}
-		t.Log(msg)
-	}()
-	x := NewInt(1)
-	y := NewInt(2)
-	// Jacobi should panic when the second argument is even.
-	Jacobi(x, y)
-	panic(failureMsg)
-}
-
-func TestIssue2607(t *testing.T) {
-	// This code sequence used to hang.
-	n := NewInt(10)
-	n.Rand(rand.New(rand.NewSource(9)), n)
-}
diff --git a/src/cmd/compile/internal/big/intconv.go b/src/cmd/compile/internal/big/intconv.go
deleted file mode 100644
index daf674a..0000000
--- a/src/cmd/compile/internal/big/intconv.go
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements int-to-string conversion functions.
-
-package big
-
-import (
-	"errors"
-	"fmt"
-	"io"
-)
-
-// TODO(gri) Should rename itoa to utoa (there's no sign). That
-// would permit the introduction of itoa which is like utoa but
-// reserves a byte for a possible sign that's passed in. That
-// would permit Int.Text to be implemented w/o the need for
-// string copy if the number is negative.
-
-// Text returns the string representation of x in the given base.
-// Base must be between 2 and 36, inclusive. The result uses the
-// lower-case letters 'a' to 'z' for digit values >= 10. No base
-// prefix (such as "0x") is added to the string.
-func (x *Int) Text(base int) string {
-	if x == nil {
-		return "<nil>"
-	}
-	return string(x.abs.itoa(x.neg, base))
-}
-
-// Append appends the string representation of x, as generated by
-// x.Text(base), to buf and returns the extended buffer.
-func (x *Int) Append(buf []byte, base int) []byte {
-	if x == nil {
-		return append(buf, "<nil>"...)
-	}
-	return append(buf, x.abs.itoa(x.neg, base)...)
-}
-
-func (x *Int) String() string {
-	return x.Text(10)
-}
-
-// write count copies of text to s
-func writeMultiple(s fmt.State, text string, count int) {
-	if len(text) > 0 {
-		b := []byte(text)
-		for ; count > 0; count-- {
-			s.Write(b)
-		}
-	}
-}
-
-// Format implements fmt.Formatter. It accepts the formats
-// 'b' (binary), 'o' (octal), 'd' (decimal), 'x' (lowercase
-// hexadecimal), and 'X' (uppercase hexadecimal).
-// Also supported are the full suite of package fmt's format
-// flags for integral types, including '+' and ' ' for sign
-// control, '#' for leading zero in octal and for hexadecimal,
-// a leading "0x" or "0X" for "%#x" and "%#X" respectively,
-// specification of minimum digits precision, output field
-// width, space or zero padding, and '-' for left or right
-// justification.
-//
-func (x *Int) Format(s fmt.State, ch rune) {
-	// determine base
-	var base int
-	switch ch {
-	case 'b':
-		base = 2
-	case 'o':
-		base = 8
-	case 'd', 's', 'v':
-		base = 10
-	case 'x', 'X':
-		base = 16
-	default:
-		// unknown format
-		fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String())
-		return
-	}
-
-	if x == nil {
-		fmt.Fprint(s, "<nil>")
-		return
-	}
-
-	// determine sign character
-	sign := ""
-	switch {
-	case x.neg:
-		sign = "-"
-	case s.Flag('+'): // supersedes ' ' when both specified
-		sign = "+"
-	case s.Flag(' '):
-		sign = " "
-	}
-
-	// determine prefix characters for indicating output base
-	prefix := ""
-	if s.Flag('#') {
-		switch ch {
-		case 'o': // octal
-			prefix = "0"
-		case 'x': // hexadecimal
-			prefix = "0x"
-		case 'X':
-			prefix = "0X"
-		}
-	}
-
-	digits := x.abs.utoa(base)
-	if ch == 'X' {
-		// faster than bytes.ToUpper
-		for i, d := range digits {
-			if 'a' <= d && d <= 'z' {
-				digits[i] = 'A' + (d - 'a')
-			}
-		}
-	}
-
-	// number of characters for the three classes of number padding
-	var left int  // space characters to left of digits for right justification ("%8d")
-	var zeros int // zero characters (actually cs[0]) as left-most digits ("%.8d")
-	var right int // space characters to right of digits for left justification ("%-8d")
-
-	// determine number padding from precision: the least number of digits to output
-	precision, precisionSet := s.Precision()
-	if precisionSet {
-		switch {
-		case len(digits) < precision:
-			zeros = precision - len(digits) // count of zero padding
-		case len(digits) == 1 && digits[0] == '0' && precision == 0:
-			return // print nothing if zero value (x == 0) and zero precision ("." or ".0")
-		}
-	}
-
-	// determine field pad from width: the least number of characters to output
-	length := len(sign) + len(prefix) + zeros + len(digits)
-	if width, widthSet := s.Width(); widthSet && length < width { // pad as specified
-		switch d := width - length; {
-		case s.Flag('-'):
-			// pad on the right with spaces; supersedes '0' when both specified
-			right = d
-		case s.Flag('0') && !precisionSet:
-			// pad with zeros unless precision also specified
-			zeros = d
-		default:
-			// pad on the left with spaces
-			left = d
-		}
-	}
-
-	// print number as [left pad][sign][prefix][zero pad][digits][right pad]
-	writeMultiple(s, " ", left)
-	writeMultiple(s, sign, 1)
-	writeMultiple(s, prefix, 1)
-	writeMultiple(s, "0", zeros)
-	s.Write(digits)
-	writeMultiple(s, " ", right)
-}
-
-// scan sets z to the integer value corresponding to the longest possible prefix
-// read from r representing a signed integer number in a given conversion base.
-// It returns z, the actual conversion base used, and an error, if any. In the
-// error case, the value of z is undefined but the returned value is nil. The
-// syntax follows the syntax of integer literals in Go.
-//
-// The base argument must be 0 or a value from 2 through MaxBase. If the base
-// is 0, the string prefix determines the actual conversion base. A prefix of
-// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a
-// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10.
-//
-func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) {
-	// determine sign
-	neg, err := scanSign(r)
-	if err != nil {
-		return nil, 0, err
-	}
-
-	// determine mantissa
-	z.abs, base, _, err = z.abs.scan(r, base, false)
-	if err != nil {
-		return nil, base, err
-	}
-	z.neg = len(z.abs) > 0 && neg // 0 has no sign
-
-	return z, base, nil
-}
-
-func scanSign(r io.ByteScanner) (neg bool, err error) {
-	var ch byte
-	if ch, err = r.ReadByte(); err != nil {
-		return false, err
-	}
-	switch ch {
-	case '-':
-		neg = true
-	case '+':
-		// nothing to do
-	default:
-		r.UnreadByte()
-	}
-	return
-}
-
-// byteReader is a local wrapper around fmt.ScanState;
-// it implements the ByteReader interface.
-type byteReader struct {
-	fmt.ScanState
-}
-
-func (r byteReader) ReadByte() (byte, error) {
-	ch, size, err := r.ReadRune()
-	if size != 1 && err == nil {
-		err = fmt.Errorf("invalid rune %#U", ch)
-	}
-	return byte(ch), err
-}
-
-func (r byteReader) UnreadByte() error {
-	return r.UnreadRune()
-}
-
-// Scan is a support routine for fmt.Scanner; it sets z to the value of
-// the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
-// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
-func (z *Int) Scan(s fmt.ScanState, ch rune) error {
-	s.SkipSpace() // skip leading space characters
-	base := 0
-	switch ch {
-	case 'b':
-		base = 2
-	case 'o':
-		base = 8
-	case 'd':
-		base = 10
-	case 'x', 'X':
-		base = 16
-	case 's', 'v':
-		// let scan determine the base
-	default:
-		return errors.New("Int.Scan: invalid verb")
-	}
-	_, _, err := z.scan(byteReader{s}, base)
-	return err
-}
diff --git a/src/cmd/compile/internal/big/intconv_test.go b/src/cmd/compile/internal/big/intconv_test.go
deleted file mode 100644
index 5142081..0000000
--- a/src/cmd/compile/internal/big/intconv_test.go
+++ /dev/null
@@ -1,391 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"bytes"
-	"fmt"
-	"testing"
-)
-
-var stringTests = []struct {
-	in   string
-	out  string
-	base int
-	val  int64
-	ok   bool
-}{
-	{in: ""},
-	{in: "a"},
-	{in: "z"},
-	{in: "+"},
-	{in: "-"},
-	{in: "0b"},
-	{in: "0x"},
-	{in: "2", base: 2},
-	{in: "0b2", base: 0},
-	{in: "08"},
-	{in: "8", base: 8},
-	{in: "0xg", base: 0},
-	{in: "g", base: 16},
-	{"0", "0", 0, 0, true},
-	{"0", "0", 10, 0, true},
-	{"0", "0", 16, 0, true},
-	{"+0", "0", 0, 0, true},
-	{"-0", "0", 0, 0, true},
-	{"10", "10", 0, 10, true},
-	{"10", "10", 10, 10, true},
-	{"10", "10", 16, 16, true},
-	{"-10", "-10", 16, -16, true},
-	{"+10", "10", 16, 16, true},
-	{"0x10", "16", 0, 16, true},
-	{in: "0x10", base: 16},
-	{"-0x10", "-16", 0, -16, true},
-	{"+0x10", "16", 0, 16, true},
-	{"00", "0", 0, 0, true},
-	{"0", "0", 8, 0, true},
-	{"07", "7", 0, 7, true},
-	{"7", "7", 8, 7, true},
-	{"023", "19", 0, 19, true},
-	{"23", "23", 8, 19, true},
-	{"cafebabe", "cafebabe", 16, 0xcafebabe, true},
-	{"0b0", "0", 0, 0, true},
-	{"-111", "-111", 2, -7, true},
-	{"-0b111", "-7", 0, -7, true},
-	{"0b1001010111", "599", 0, 0x257, true},
-	{"1001010111", "1001010111", 2, 0x257, true},
-}
-
-func TestIntText(t *testing.T) {
-	z := new(Int)
-	for _, test := range stringTests {
-		if !test.ok {
-			continue
-		}
-
-		_, ok := z.SetString(test.in, test.base)
-		if !ok {
-			t.Errorf("%v: failed to parse", test)
-			continue
-		}
-
-		base := test.base
-		if base == 0 {
-			base = 10
-		}
-
-		if got := z.Text(base); got != test.out {
-			t.Errorf("%v: got %s; want %s", test, got, test.out)
-		}
-	}
-}
-
-func TestAppendText(t *testing.T) {
-	z := new(Int)
-	var buf []byte
-	for _, test := range stringTests {
-		if !test.ok {
-			continue
-		}
-
-		_, ok := z.SetString(test.in, test.base)
-		if !ok {
-			t.Errorf("%v: failed to parse", test)
-			continue
-		}
-
-		base := test.base
-		if base == 0 {
-			base = 10
-		}
-
-		i := len(buf)
-		buf = z.Append(buf, base)
-		if got := string(buf[i:]); got != test.out {
-			t.Errorf("%v: got %s; want %s", test, got, test.out)
-		}
-	}
-}
-
-func format(base int) string {
-	switch base {
-	case 2:
-		return "%b"
-	case 8:
-		return "%o"
-	case 16:
-		return "%x"
-	}
-	return "%d"
-}
-
-func TestGetString(t *testing.T) {
-	z := new(Int)
-	for i, test := range stringTests {
-		if !test.ok {
-			continue
-		}
-		z.SetInt64(test.val)
-
-		if test.base == 10 {
-			if got := z.String(); got != test.out {
-				t.Errorf("#%da got %s; want %s", i, got, test.out)
-			}
-		}
-
-		if got := fmt.Sprintf(format(test.base), z); got != test.out {
-			t.Errorf("#%db got %s; want %s", i, got, test.out)
-		}
-	}
-}
-
-func TestSetString(t *testing.T) {
-	tmp := new(Int)
-	for i, test := range stringTests {
-		// initialize to a non-zero value so that issues with parsing
-		// 0 are detected
-		tmp.SetInt64(1234567890)
-		n1, ok1 := new(Int).SetString(test.in, test.base)
-		n2, ok2 := tmp.SetString(test.in, test.base)
-		expected := NewInt(test.val)
-		if ok1 != test.ok || ok2 != test.ok {
-			t.Errorf("#%d (input '%s') ok incorrect (should be %t)", i, test.in, test.ok)
-			continue
-		}
-		if !ok1 {
-			if n1 != nil {
-				t.Errorf("#%d (input '%s') n1 != nil", i, test.in)
-			}
-			continue
-		}
-		if !ok2 {
-			if n2 != nil {
-				t.Errorf("#%d (input '%s') n2 != nil", i, test.in)
-			}
-			continue
-		}
-
-		if ok1 && !isNormalized(n1) {
-			t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n1)
-		}
-		if ok2 && !isNormalized(n2) {
-			t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n2)
-		}
-
-		if n1.Cmp(expected) != 0 {
-			t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val)
-		}
-		if n2.Cmp(expected) != 0 {
-			t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val)
-		}
-	}
-}
-
-var formatTests = []struct {
-	input  string
-	format string
-	output string
-}{
-	{"<nil>", "%x", "<nil>"},
-	{"<nil>", "%#x", "<nil>"},
-	{"<nil>", "%#y", "%!y(big.Int=<nil>)"},
-
-	{"10", "%b", "1010"},
-	{"10", "%o", "12"},
-	{"10", "%d", "10"},
-	{"10", "%v", "10"},
-	{"10", "%x", "a"},
-	{"10", "%X", "A"},
-	{"-10", "%X", "-A"},
-	{"10", "%y", "%!y(big.Int=10)"},
-	{"-10", "%y", "%!y(big.Int=-10)"},
-
-	{"10", "%#b", "1010"},
-	{"10", "%#o", "012"},
-	{"10", "%#d", "10"},
-	{"10", "%#v", "10"},
-	{"10", "%#x", "0xa"},
-	{"10", "%#X", "0XA"},
-	{"-10", "%#X", "-0XA"},
-	{"10", "%#y", "%!y(big.Int=10)"},
-	{"-10", "%#y", "%!y(big.Int=-10)"},
-
-	{"1234", "%d", "1234"},
-	{"1234", "%3d", "1234"},
-	{"1234", "%4d", "1234"},
-	{"-1234", "%d", "-1234"},
-	{"1234", "% 5d", " 1234"},
-	{"1234", "%+5d", "+1234"},
-	{"1234", "%-5d", "1234 "},
-	{"1234", "%x", "4d2"},
-	{"1234", "%X", "4D2"},
-	{"-1234", "%3x", "-4d2"},
-	{"-1234", "%4x", "-4d2"},
-	{"-1234", "%5x", " -4d2"},
-	{"-1234", "%-5x", "-4d2 "},
-	{"1234", "%03d", "1234"},
-	{"1234", "%04d", "1234"},
-	{"1234", "%05d", "01234"},
-	{"1234", "%06d", "001234"},
-	{"-1234", "%06d", "-01234"},
-	{"1234", "%+06d", "+01234"},
-	{"1234", "% 06d", " 01234"},
-	{"1234", "%-6d", "1234  "},
-	{"1234", "%-06d", "1234  "},
-	{"-1234", "%-06d", "-1234 "},
-
-	{"1234", "%.3d", "1234"},
-	{"1234", "%.4d", "1234"},
-	{"1234", "%.5d", "01234"},
-	{"1234", "%.6d", "001234"},
-	{"-1234", "%.3d", "-1234"},
-	{"-1234", "%.4d", "-1234"},
-	{"-1234", "%.5d", "-01234"},
-	{"-1234", "%.6d", "-001234"},
-
-	{"1234", "%8.3d", "    1234"},
-	{"1234", "%8.4d", "    1234"},
-	{"1234", "%8.5d", "   01234"},
-	{"1234", "%8.6d", "  001234"},
-	{"-1234", "%8.3d", "   -1234"},
-	{"-1234", "%8.4d", "   -1234"},
-	{"-1234", "%8.5d", "  -01234"},
-	{"-1234", "%8.6d", " -001234"},
-
-	{"1234", "%+8.3d", "   +1234"},
-	{"1234", "%+8.4d", "   +1234"},
-	{"1234", "%+8.5d", "  +01234"},
-	{"1234", "%+8.6d", " +001234"},
-	{"-1234", "%+8.3d", "   -1234"},
-	{"-1234", "%+8.4d", "   -1234"},
-	{"-1234", "%+8.5d", "  -01234"},
-	{"-1234", "%+8.6d", " -001234"},
-
-	{"1234", "% 8.3d", "    1234"},
-	{"1234", "% 8.4d", "    1234"},
-	{"1234", "% 8.5d", "   01234"},
-	{"1234", "% 8.6d", "  001234"},
-	{"-1234", "% 8.3d", "   -1234"},
-	{"-1234", "% 8.4d", "   -1234"},
-	{"-1234", "% 8.5d", "  -01234"},
-	{"-1234", "% 8.6d", " -001234"},
-
-	{"1234", "%.3x", "4d2"},
-	{"1234", "%.4x", "04d2"},
-	{"1234", "%.5x", "004d2"},
-	{"1234", "%.6x", "0004d2"},
-	{"-1234", "%.3x", "-4d2"},
-	{"-1234", "%.4x", "-04d2"},
-	{"-1234", "%.5x", "-004d2"},
-	{"-1234", "%.6x", "-0004d2"},
-
-	{"1234", "%8.3x", "     4d2"},
-	{"1234", "%8.4x", "    04d2"},
-	{"1234", "%8.5x", "   004d2"},
-	{"1234", "%8.6x", "  0004d2"},
-	{"-1234", "%8.3x", "    -4d2"},
-	{"-1234", "%8.4x", "   -04d2"},
-	{"-1234", "%8.5x", "  -004d2"},
-	{"-1234", "%8.6x", " -0004d2"},
-
-	{"1234", "%+8.3x", "    +4d2"},
-	{"1234", "%+8.4x", "   +04d2"},
-	{"1234", "%+8.5x", "  +004d2"},
-	{"1234", "%+8.6x", " +0004d2"},
-	{"-1234", "%+8.3x", "    -4d2"},
-	{"-1234", "%+8.4x", "   -04d2"},
-	{"-1234", "%+8.5x", "  -004d2"},
-	{"-1234", "%+8.6x", " -0004d2"},
-
-	{"1234", "% 8.3x", "     4d2"},
-	{"1234", "% 8.4x", "    04d2"},
-	{"1234", "% 8.5x", "   004d2"},
-	{"1234", "% 8.6x", "  0004d2"},
-	{"1234", "% 8.7x", " 00004d2"},
-	{"1234", "% 8.8x", " 000004d2"},
-	{"-1234", "% 8.3x", "    -4d2"},
-	{"-1234", "% 8.4x", "   -04d2"},
-	{"-1234", "% 8.5x", "  -004d2"},
-	{"-1234", "% 8.6x", " -0004d2"},
-	{"-1234", "% 8.7x", "-00004d2"},
-	{"-1234", "% 8.8x", "-000004d2"},
-
-	{"1234", "%-8.3d", "1234    "},
-	{"1234", "%-8.4d", "1234    "},
-	{"1234", "%-8.5d", "01234   "},
-	{"1234", "%-8.6d", "001234  "},
-	{"1234", "%-8.7d", "0001234 "},
-	{"1234", "%-8.8d", "00001234"},
-	{"-1234", "%-8.3d", "-1234   "},
-	{"-1234", "%-8.4d", "-1234   "},
-	{"-1234", "%-8.5d", "-01234  "},
-	{"-1234", "%-8.6d", "-001234 "},
-	{"-1234", "%-8.7d", "-0001234"},
-	{"-1234", "%-8.8d", "-00001234"},
-
-	{"16777215", "%b", "111111111111111111111111"}, // 2**24 - 1
-
-	{"0", "%.d", ""},
-	{"0", "%.0d", ""},
-	{"0", "%3.d", ""},
-}
-
-func TestFormat(t *testing.T) {
-	for i, test := range formatTests {
-		var x *Int
-		if test.input != "<nil>" {
-			var ok bool
-			x, ok = new(Int).SetString(test.input, 0)
-			if !ok {
-				t.Errorf("#%d failed reading input %s", i, test.input)
-			}
-		}
-		output := fmt.Sprintf(test.format, x)
-		if output != test.output {
-			t.Errorf("#%d got %q; want %q, {%q, %q, %q}", i, output, test.output, test.input, test.format, test.output)
-		}
-	}
-}
-
-var scanTests = []struct {
-	input     string
-	format    string
-	output    string
-	remaining int
-}{
-	{"1010", "%b", "10", 0},
-	{"0b1010", "%v", "10", 0},
-	{"12", "%o", "10", 0},
-	{"012", "%v", "10", 0},
-	{"10", "%d", "10", 0},
-	{"10", "%v", "10", 0},
-	{"a", "%x", "10", 0},
-	{"0xa", "%v", "10", 0},
-	{"A", "%X", "10", 0},
-	{"-A", "%X", "-10", 0},
-	{"+0b1011001", "%v", "89", 0},
-	{"0xA", "%v", "10", 0},
-	{"0 ", "%v", "0", 1},
-	{"2+3", "%v", "2", 2},
-	{"0XABC 12", "%v", "2748", 3},
-}
-
-func TestScan(t *testing.T) {
-	var buf bytes.Buffer
-	for i, test := range scanTests {
-		x := new(Int)
-		buf.Reset()
-		buf.WriteString(test.input)
-		if _, err := fmt.Fscanf(&buf, test.format, x); err != nil {
-			t.Errorf("#%d error: %s", i, err)
-		}
-		if x.String() != test.output {
-			t.Errorf("#%d got %s; want %s", i, x.String(), test.output)
-		}
-		if buf.Len() != test.remaining {
-			t.Errorf("#%d got %d bytes remaining; want %d", i, buf.Len(), test.remaining)
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/intmarsh.go b/src/cmd/compile/internal/big/intmarsh.go
deleted file mode 100644
index 4ff57b6..0000000
--- a/src/cmd/compile/internal/big/intmarsh.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements encoding/decoding of Ints.
-
-package big
-
-import "fmt"
-
-// Gob codec version. Permits backward-compatible changes to the encoding.
-const intGobVersion byte = 1
-
-// GobEncode implements the gob.GobEncoder interface.
-func (x *Int) GobEncode() ([]byte, error) {
-	if x == nil {
-		return nil, nil
-	}
-	buf := make([]byte, 1+len(x.abs)*_S) // extra byte for version and sign bit
-	i := x.abs.bytes(buf) - 1            // i >= 0
-	b := intGobVersion << 1              // make space for sign bit
-	if x.neg {
-		b |= 1
-	}
-	buf[i] = b
-	return buf[i:], nil
-}
-
-// GobDecode implements the gob.GobDecoder interface.
-func (z *Int) GobDecode(buf []byte) error {
-	if len(buf) == 0 {
-		// Other side sent a nil or default value.
-		*z = Int{}
-		return nil
-	}
-	b := buf[0]
-	if b>>1 != intGobVersion {
-		return fmt.Errorf("Int.GobDecode: encoding version %d not supported", b>>1)
-	}
-	z.neg = b&1 != 0
-	z.abs = z.abs.setBytes(buf[1:])
-	return nil
-}
-
-// MarshalText implements the encoding.TextMarshaler interface.
-func (x *Int) MarshalText() (text []byte, err error) {
-	if x == nil {
-		return []byte("<nil>"), nil
-	}
-	return x.abs.itoa(x.neg, 10), nil
-}
-
-// UnmarshalText implements the encoding.TextUnmarshaler interface.
-func (z *Int) UnmarshalText(text []byte) error {
-	// TODO(gri): get rid of the []byte/string conversion
-	if _, ok := z.SetString(string(text), 0); !ok {
-		return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Int", text)
-	}
-	return nil
-}
-
-// The JSON marshallers are only here for API backward compatibility
-// (programs that explicitly look for these two methods). JSON works
-// fine with the TextMarshaler only.
-
-// MarshalJSON implements the json.Marshaler interface.
-func (x *Int) MarshalJSON() ([]byte, error) {
-	return x.MarshalText()
-}
-
-// UnmarshalJSON implements the json.Unmarshaler interface.
-func (z *Int) UnmarshalJSON(text []byte) error {
-	return z.UnmarshalText(text)
-}
diff --git a/src/cmd/compile/internal/big/intmarsh_test.go b/src/cmd/compile/internal/big/intmarsh_test.go
deleted file mode 100644
index f82956c..0000000
--- a/src/cmd/compile/internal/big/intmarsh_test.go
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"bytes"
-	"encoding/gob"
-	"encoding/json"
-	"encoding/xml"
-	"testing"
-)
-
-var encodingTests = []string{
-	"0",
-	"1",
-	"2",
-	"10",
-	"1000",
-	"1234567890",
-	"298472983472983471903246121093472394872319615612417471234712061",
-}
-
-func TestIntGobEncoding(t *testing.T) {
-	var medium bytes.Buffer
-	enc := gob.NewEncoder(&medium)
-	dec := gob.NewDecoder(&medium)
-	for _, test := range encodingTests {
-		for _, sign := range []string{"", "+", "-"} {
-			x := sign + test
-			medium.Reset() // empty buffer for each test case (in case of failures)
-			var tx Int
-			tx.SetString(x, 10)
-			if err := enc.Encode(&tx); err != nil {
-				t.Errorf("encoding of %s failed: %s", &tx, err)
-				continue
-			}
-			var rx Int
-			if err := dec.Decode(&rx); err != nil {
-				t.Errorf("decoding of %s failed: %s", &tx, err)
-				continue
-			}
-			if rx.Cmp(&tx) != 0 {
-				t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx)
-			}
-		}
-	}
-}
-
-// Sending a nil Int pointer (inside a slice) on a round trip through gob should yield a zero.
-// TODO: top-level nils.
-func TestGobEncodingNilIntInSlice(t *testing.T) {
-	buf := new(bytes.Buffer)
-	enc := gob.NewEncoder(buf)
-	dec := gob.NewDecoder(buf)
-
-	var in = make([]*Int, 1)
-	err := enc.Encode(&in)
-	if err != nil {
-		t.Errorf("gob encode failed: %q", err)
-	}
-	var out []*Int
-	err = dec.Decode(&out)
-	if err != nil {
-		t.Fatalf("gob decode failed: %q", err)
-	}
-	if len(out) != 1 {
-		t.Fatalf("wrong len; want 1 got %d", len(out))
-	}
-	var zero Int
-	if out[0].Cmp(&zero) != 0 {
-		t.Fatalf("transmission of (*Int)(nil) failed: got %s want 0", out)
-	}
-}
-
-func TestIntJSONEncoding(t *testing.T) {
-	for _, test := range encodingTests {
-		for _, sign := range []string{"", "+", "-"} {
-			x := sign + test
-			var tx Int
-			tx.SetString(x, 10)
-			b, err := json.Marshal(&tx)
-			if err != nil {
-				t.Errorf("marshaling of %s failed: %s", &tx, err)
-				continue
-			}
-			var rx Int
-			if err := json.Unmarshal(b, &rx); err != nil {
-				t.Errorf("unmarshaling of %s failed: %s", &tx, err)
-				continue
-			}
-			if rx.Cmp(&tx) != 0 {
-				t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx)
-			}
-		}
-	}
-}
-
-func TestIntXMLEncoding(t *testing.T) {
-	for _, test := range encodingTests {
-		for _, sign := range []string{"", "+", "-"} {
-			x := sign + test
-			var tx Int
-			tx.SetString(x, 0)
-			b, err := xml.Marshal(&tx)
-			if err != nil {
-				t.Errorf("marshaling of %s failed: %s", &tx, err)
-				continue
-			}
-			var rx Int
-			if err := xml.Unmarshal(b, &rx); err != nil {
-				t.Errorf("unmarshaling of %s failed: %s", &tx, err)
-				continue
-			}
-			if rx.Cmp(&tx) != 0 {
-				t.Errorf("XML encoding of %s failed: got %s want %s", &tx, &rx, &tx)
-			}
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/nat.go b/src/cmd/compile/internal/big/nat.go
deleted file mode 100644
index 7668b64..0000000
--- a/src/cmd/compile/internal/big/nat.go
+++ /dev/null
@@ -1,1282 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements unsigned multi-precision integers (natural
-// numbers). They are the building blocks for the implementation
-// of signed integers, rationals, and floating-point numbers.
-
-package big
-
-import "math/rand"
-
-// An unsigned integer x of the form
-//
-//   x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0]
-//
-// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n,
-// with the digits x[i] as the slice elements.
-//
-// A number is normalized if the slice contains no leading 0 digits.
-// During arithmetic operations, denormalized values may occur but are
-// always normalized before returning the final result. The normalized
-// representation of 0 is the empty or nil slice (length = 0).
-//
-type nat []Word
-
-var (
-	natOne = nat{1}
-	natTwo = nat{2}
-	natTen = nat{10}
-)
-
-func (z nat) clear() {
-	for i := range z {
-		z[i] = 0
-	}
-}
-
-func (z nat) norm() nat {
-	i := len(z)
-	for i > 0 && z[i-1] == 0 {
-		i--
-	}
-	return z[0:i]
-}
-
-func (z nat) make(n int) nat {
-	if n <= cap(z) {
-		return z[:n] // reuse z
-	}
-	// Choosing a good value for e has significant performance impact
-	// because it increases the chance that a value can be reused.
-	const e = 4 // extra capacity
-	return make(nat, n, n+e)
-}
-
-func (z nat) setWord(x Word) nat {
-	if x == 0 {
-		return z[:0]
-	}
-	z = z.make(1)
-	z[0] = x
-	return z
-}
-
-func (z nat) setUint64(x uint64) nat {
-	// single-digit values
-	if w := Word(x); uint64(w) == x {
-		return z.setWord(w)
-	}
-
-	// compute number of words n required to represent x
-	n := 0
-	for t := x; t > 0; t >>= _W {
-		n++
-	}
-
-	// split x into n words
-	z = z.make(n)
-	for i := range z {
-		z[i] = Word(x & _M)
-		x >>= _W
-	}
-
-	return z
-}
-
-func (z nat) set(x nat) nat {
-	z = z.make(len(x))
-	copy(z, x)
-	return z
-}
-
-func (z nat) add(x, y nat) nat {
-	m := len(x)
-	n := len(y)
-
-	switch {
-	case m < n:
-		return z.add(y, x)
-	case m == 0:
-		// n == 0 because m >= n; result is 0
-		return z[:0]
-	case n == 0:
-		// result is x
-		return z.set(x)
-	}
-	// m > 0
-
-	z = z.make(m + 1)
-	c := addVV(z[0:n], x, y)
-	if m > n {
-		c = addVW(z[n:m], x[n:], c)
-	}
-	z[m] = c
-
-	return z.norm()
-}
-
-func (z nat) sub(x, y nat) nat {
-	m := len(x)
-	n := len(y)
-
-	switch {
-	case m < n:
-		panic("underflow")
-	case m == 0:
-		// n == 0 because m >= n; result is 0
-		return z[:0]
-	case n == 0:
-		// result is x
-		return z.set(x)
-	}
-	// m > 0
-
-	z = z.make(m)
-	c := subVV(z[0:n], x, y)
-	if m > n {
-		c = subVW(z[n:], x[n:], c)
-	}
-	if c != 0 {
-		panic("underflow")
-	}
-
-	return z.norm()
-}
-
-func (x nat) cmp(y nat) (r int) {
-	m := len(x)
-	n := len(y)
-	if m != n || m == 0 {
-		switch {
-		case m < n:
-			r = -1
-		case m > n:
-			r = 1
-		}
-		return
-	}
-
-	i := m - 1
-	for i > 0 && x[i] == y[i] {
-		i--
-	}
-
-	switch {
-	case x[i] < y[i]:
-		r = -1
-	case x[i] > y[i]:
-		r = 1
-	}
-	return
-}
-
-func (z nat) mulAddWW(x nat, y, r Word) nat {
-	m := len(x)
-	if m == 0 || y == 0 {
-		return z.setWord(r) // result is r
-	}
-	// m > 0
-
-	z = z.make(m + 1)
-	z[m] = mulAddVWW(z[0:m], x, y, r)
-
-	return z.norm()
-}
-
-// basicMul multiplies x and y and leaves the result in z.
-// The (non-normalized) result is placed in z[0 : len(x) + len(y)].
-func basicMul(z, x, y nat) {
-	z[0 : len(x)+len(y)].clear() // initialize z
-	for i, d := range y {
-		if d != 0 {
-			z[len(x)+i] = addMulVVW(z[i:i+len(x)], x, d)
-		}
-	}
-}
-
-// montgomery computes z mod m = x*y*2**(-n*_W) mod m,
-// assuming k = -1/m mod 2**_W.
-// z is used for storing the result which is returned;
-// z must not alias x, y or m.
-// See Gueron, "Efficient Software Implementations of Modular Exponentiation".
-// https://eprint.iacr.org/2011/239.pdf
-// In the terminology of that paper, this is an "Almost Montgomery Multiplication":
-// x and y are required to satisfy 0 <= z < 2**(n*_W) and then the result
-// z is guaranteed to satisfy 0 <= z < 2**(n*_W), but it may not be < m.
-func (z nat) montgomery(x, y, m nat, k Word, n int) nat {
-	// This code assumes x, y, m are all the same length, n.
-	// (required by addMulVVW and the for loop).
-	// It also assumes that x, y are already reduced mod m,
-	// or else the result will not be properly reduced.
-	if len(x) != n || len(y) != n || len(m) != n {
-		panic("math/big: mismatched montgomery number lengths")
-	}
-	z = z.make(n)
-	z.clear()
-	var c Word
-	for i := 0; i < n; i++ {
-		d := y[i]
-		c2 := addMulVVW(z, x, d)
-		t := z[0] * k
-		c3 := addMulVVW(z, m, t)
-		copy(z, z[1:])
-		cx := c + c2
-		cy := cx + c3
-		z[n-1] = cy
-		if cx < c2 || cy < c3 {
-			c = 1
-		} else {
-			c = 0
-		}
-	}
-	if c != 0 {
-		subVV(z, z, m)
-	}
-	return z
-}
-
-// Fast version of z[0:n+n>>1].add(z[0:n+n>>1], x[0:n]) w/o bounds checks.
-// Factored out for readability - do not use outside karatsuba.
-func karatsubaAdd(z, x nat, n int) {
-	if c := addVV(z[0:n], z, x); c != 0 {
-		addVW(z[n:n+n>>1], z[n:], c)
-	}
-}
-
-// Like karatsubaAdd, but does subtract.
-func karatsubaSub(z, x nat, n int) {
-	if c := subVV(z[0:n], z, x); c != 0 {
-		subVW(z[n:n+n>>1], z[n:], c)
-	}
-}
-
-// Operands that are shorter than karatsubaThreshold are multiplied using
-// "grade school" multiplication; for longer operands the Karatsuba algorithm
-// is used.
-var karatsubaThreshold int = 40 // computed by calibrate.go
-
-// karatsuba multiplies x and y and leaves the result in z.
-// Both x and y must have the same length n and n must be a
-// power of 2. The result vector z must have len(z) >= 6*n.
-// The (non-normalized) result is placed in z[0 : 2*n].
-func karatsuba(z, x, y nat) {
-	n := len(y)
-
-	// Switch to basic multiplication if numbers are odd or small.
-	// (n is always even if karatsubaThreshold is even, but be
-	// conservative)
-	if n&1 != 0 || n < karatsubaThreshold || n < 2 {
-		basicMul(z, x, y)
-		return
-	}
-	// n&1 == 0 && n >= karatsubaThreshold && n >= 2
-
-	// Karatsuba multiplication is based on the observation that
-	// for two numbers x and y with:
-	//
-	//   x = x1*b + x0
-	//   y = y1*b + y0
-	//
-	// the product x*y can be obtained with 3 products z2, z1, z0
-	// instead of 4:
-	//
-	//   x*y = x1*y1*b*b + (x1*y0 + x0*y1)*b + x0*y0
-	//       =    z2*b*b +              z1*b +    z0
-	//
-	// with:
-	//
-	//   xd = x1 - x0
-	//   yd = y0 - y1
-	//
-	//   z1 =      xd*yd                    + z2 + z0
-	//      = (x1-x0)*(y0 - y1)             + z2 + z0
-	//      = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z2 + z0
-	//      = x1*y0 -    z2 -    z0 + x0*y1 + z2 + z0
-	//      = x1*y0                 + x0*y1
-
-	// split x, y into "digits"
-	n2 := n >> 1              // n2 >= 1
-	x1, x0 := x[n2:], x[0:n2] // x = x1*b + y0
-	y1, y0 := y[n2:], y[0:n2] // y = y1*b + y0
-
-	// z is used for the result and temporary storage:
-	//
-	//   6*n     5*n     4*n     3*n     2*n     1*n     0*n
-	// z = [z2 copy|z0 copy| xd*yd | yd:xd | x1*y1 | x0*y0 ]
-	//
-	// For each recursive call of karatsuba, an unused slice of
-	// z is passed in that has (at least) half the length of the
-	// caller's z.
-
-	// compute z0 and z2 with the result "in place" in z
-	karatsuba(z, x0, y0)     // z0 = x0*y0
-	karatsuba(z[n:], x1, y1) // z2 = x1*y1
-
-	// compute xd (or the negative value if underflow occurs)
-	s := 1 // sign of product xd*yd
-	xd := z[2*n : 2*n+n2]
-	if subVV(xd, x1, x0) != 0 { // x1-x0
-		s = -s
-		subVV(xd, x0, x1) // x0-x1
-	}
-
-	// compute yd (or the negative value if underflow occurs)
-	yd := z[2*n+n2 : 3*n]
-	if subVV(yd, y0, y1) != 0 { // y0-y1
-		s = -s
-		subVV(yd, y1, y0) // y1-y0
-	}
-
-	// p = (x1-x0)*(y0-y1) == x1*y0 - x1*y1 - x0*y0 + x0*y1 for s > 0
-	// p = (x0-x1)*(y0-y1) == x0*y0 - x0*y1 - x1*y0 + x1*y1 for s < 0
-	p := z[n*3:]
-	karatsuba(p, xd, yd)
-
-	// save original z2:z0
-	// (ok to use upper half of z since we're done recursing)
-	r := z[n*4:]
-	copy(r, z[:n*2])
-
-	// add up all partial products
-	//
-	//   2*n     n     0
-	// z = [ z2  | z0  ]
-	//   +    [ z0  ]
-	//   +    [ z2  ]
-	//   +    [  p  ]
-	//
-	karatsubaAdd(z[n2:], r, n)
-	karatsubaAdd(z[n2:], r[n:], n)
-	if s > 0 {
-		karatsubaAdd(z[n2:], p, n)
-	} else {
-		karatsubaSub(z[n2:], p, n)
-	}
-}
-
-// alias reports whether x and y share the same base array.
-func alias(x, y nat) bool {
-	return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1]
-}
-
-// addAt implements z += x<<(_W*i); z must be long enough.
-// (we don't use nat.add because we need z to stay the same
-// slice, and we don't need to normalize z after each addition)
-func addAt(z, x nat, i int) {
-	if n := len(x); n > 0 {
-		if c := addVV(z[i:i+n], z[i:], x); c != 0 {
-			j := i + n
-			if j < len(z) {
-				addVW(z[j:], z[j:], c)
-			}
-		}
-	}
-}
-
-func max(x, y int) int {
-	if x > y {
-		return x
-	}
-	return y
-}
-
-// karatsubaLen computes an approximation to the maximum k <= n such that
-// k = p<<i for a number p <= karatsubaThreshold and an i >= 0. Thus, the
-// result is the largest number that can be divided repeatedly by 2 before
-// becoming about the value of karatsubaThreshold.
-func karatsubaLen(n int) int {
-	i := uint(0)
-	for n > karatsubaThreshold {
-		n >>= 1
-		i++
-	}
-	return n << i
-}
-
-func (z nat) mul(x, y nat) nat {
-	m := len(x)
-	n := len(y)
-
-	switch {
-	case m < n:
-		return z.mul(y, x)
-	case m == 0 || n == 0:
-		return z[:0]
-	case n == 1:
-		return z.mulAddWW(x, y[0], 0)
-	}
-	// m >= n > 1
-
-	// determine if z can be reused
-	if alias(z, x) || alias(z, y) {
-		z = nil // z is an alias for x or y - cannot reuse
-	}
-
-	// use basic multiplication if the numbers are small
-	if n < karatsubaThreshold {
-		z = z.make(m + n)
-		basicMul(z, x, y)
-		return z.norm()
-	}
-	// m >= n && n >= karatsubaThreshold && n >= 2
-
-	// determine Karatsuba length k such that
-	//
-	//   x = xh*b + x0  (0 <= x0 < b)
-	//   y = yh*b + y0  (0 <= y0 < b)
-	//   b = 1<<(_W*k)  ("base" of digits xi, yi)
-	//
-	k := karatsubaLen(n)
-	// k <= n
-
-	// multiply x0 and y0 via Karatsuba
-	x0 := x[0:k]              // x0 is not normalized
-	y0 := y[0:k]              // y0 is not normalized
-	z = z.make(max(6*k, m+n)) // enough space for karatsuba of x0*y0 and full result of x*y
-	karatsuba(z, x0, y0)
-	z = z[0 : m+n]  // z has final length but may be incomplete
-	z[2*k:].clear() // upper portion of z is garbage (and 2*k <= m+n since k <= n <= m)
-
-	// If xh != 0 or yh != 0, add the missing terms to z. For
-	//
-	//   xh = xi*b^i + ... + x2*b^2 + x1*b (0 <= xi < b)
-	//   yh =                         y1*b (0 <= y1 < b)
-	//
-	// the missing terms are
-	//
-	//   x0*y1*b and xi*y0*b^i, xi*y1*b^(i+1) for i > 0
-	//
-	// since all the yi for i > 1 are 0 by choice of k: If any of them
-	// were > 0, then yh >= b^2 and thus y >= b^2. Then k' = k*2 would
-	// be a larger valid threshold contradicting the assumption about k.
-	//
-	if k < n || m != n {
-		var t nat
-
-		// add x0*y1*b
-		x0 := x0.norm()
-		y1 := y[k:]       // y1 is normalized because y is
-		t = t.mul(x0, y1) // update t so we don't lose t's underlying array
-		addAt(z, t, k)
-
-		// add xi*y0<<i, xi*y1*b<<(i+k)
-		y0 := y0.norm()
-		for i := k; i < len(x); i += k {
-			xi := x[i:]
-			if len(xi) > k {
-				xi = xi[:k]
-			}
-			xi = xi.norm()
-			t = t.mul(xi, y0)
-			addAt(z, t, i)
-			t = t.mul(xi, y1)
-			addAt(z, t, i+k)
-		}
-	}
-
-	return z.norm()
-}
-
-// mulRange computes the product of all the unsigned integers in the
-// range [a, b] inclusively. If a > b (empty range), the result is 1.
-func (z nat) mulRange(a, b uint64) nat {
-	switch {
-	case a == 0:
-		// cut long ranges short (optimization)
-		return z.setUint64(0)
-	case a > b:
-		return z.setUint64(1)
-	case a == b:
-		return z.setUint64(a)
-	case a+1 == b:
-		return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b))
-	}
-	m := (a + b) / 2
-	return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b))
-}
-
-// q = (x-r)/y, with 0 <= r < y
-func (z nat) divW(x nat, y Word) (q nat, r Word) {
-	m := len(x)
-	switch {
-	case y == 0:
-		panic("division by zero")
-	case y == 1:
-		q = z.set(x) // result is x
-		return
-	case m == 0:
-		q = z[:0] // result is 0
-		return
-	}
-	// m > 0
-	z = z.make(m)
-	r = divWVW(z, 0, x, y)
-	q = z.norm()
-	return
-}
-
-func (z nat) div(z2, u, v nat) (q, r nat) {
-	if len(v) == 0 {
-		panic("division by zero")
-	}
-
-	if u.cmp(v) < 0 {
-		q = z[:0]
-		r = z2.set(u)
-		return
-	}
-
-	if len(v) == 1 {
-		var r2 Word
-		q, r2 = z.divW(u, v[0])
-		r = z2.setWord(r2)
-		return
-	}
-
-	q, r = z.divLarge(z2, u, v)
-	return
-}
-
-// q = (uIn-r)/v, with 0 <= r < y
-// Uses z as storage for q, and u as storage for r if possible.
-// See Knuth, Volume 2, section 4.3.1, Algorithm D.
-// Preconditions:
-//    len(v) >= 2
-//    len(uIn) >= len(v)
-func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
-	n := len(v)
-	m := len(uIn) - n
-
-	// determine if z can be reused
-	// TODO(gri) should find a better solution - this if statement
-	//           is very costly (see e.g. time pidigits -s -n 10000)
-	if alias(z, uIn) || alias(z, v) {
-		z = nil // z is an alias for uIn or v - cannot reuse
-	}
-	q = z.make(m + 1)
-
-	qhatv := make(nat, n+1)
-	if alias(u, uIn) || alias(u, v) {
-		u = nil // u is an alias for uIn or v - cannot reuse
-	}
-	u = u.make(len(uIn) + 1)
-	u.clear() // TODO(gri) no need to clear if we allocated a new u
-
-	// D1.
-	shift := nlz(v[n-1])
-	if shift > 0 {
-		// do not modify v, it may be used by another goroutine simultaneously
-		v1 := make(nat, n)
-		shlVU(v1, v, shift)
-		v = v1
-	}
-	u[len(uIn)] = shlVU(u[0:len(uIn)], uIn, shift)
-
-	// D2.
-	for j := m; j >= 0; j-- {
-		// D3.
-		qhat := Word(_M)
-		if u[j+n] != v[n-1] {
-			var rhat Word
-			qhat, rhat = divWW(u[j+n], u[j+n-1], v[n-1])
-
-			// x1 | x2 = q̂v_{n-2}
-			x1, x2 := mulWW(qhat, v[n-2])
-			// test if q̂v_{n-2} > br̂ + u_{j+n-2}
-			for greaterThan(x1, x2, rhat, u[j+n-2]) {
-				qhat--
-				prevRhat := rhat
-				rhat += v[n-1]
-				// v[n-1] >= 0, so this tests for overflow.
-				if rhat < prevRhat {
-					break
-				}
-				x1, x2 = mulWW(qhat, v[n-2])
-			}
-		}
-
-		// D4.
-		qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0)
-
-		c := subVV(u[j:j+len(qhatv)], u[j:], qhatv)
-		if c != 0 {
-			c := addVV(u[j:j+n], u[j:], v)
-			u[j+n] += c
-			qhat--
-		}
-
-		q[j] = qhat
-	}
-
-	q = q.norm()
-	shrVU(u, u, shift)
-	r = u.norm()
-
-	return q, r
-}
-
-// Length of x in bits. x must be normalized.
-func (x nat) bitLen() int {
-	if i := len(x) - 1; i >= 0 {
-		return i*_W + bitLen(x[i])
-	}
-	return 0
-}
-
-const deBruijn32 = 0x077CB531
-
-var deBruijn32Lookup = []byte{
-	0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
-	31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9,
-}
-
-const deBruijn64 = 0x03f79d71b4ca8b09
-
-var deBruijn64Lookup = []byte{
-	0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
-	62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
-	63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
-	54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
-}
-
-// trailingZeroBits returns the number of consecutive least significant zero
-// bits of x.
-func trailingZeroBits(x Word) uint {
-	// x & -x leaves only the right-most bit set in the word. Let k be the
-	// index of that bit. Since only a single bit is set, the value is two
-	// to the power of k. Multiplying by a power of two is equivalent to
-	// left shifting, in this case by k bits. The de Bruijn constant is
-	// such that all six bit, consecutive substrings are distinct.
-	// Therefore, if we have a left shifted version of this constant we can
-	// find by how many bits it was shifted by looking at which six bit
-	// substring ended up at the top of the word.
-	// (Knuth, volume 4, section 7.3.1)
-	switch _W {
-	case 32:
-		return uint(deBruijn32Lookup[((x&-x)*deBruijn32)>>27])
-	case 64:
-		return uint(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58])
-	default:
-		panic("unknown word size")
-	}
-}
-
-// trailingZeroBits returns the number of consecutive least significant zero
-// bits of x.
-func (x nat) trailingZeroBits() uint {
-	if len(x) == 0 {
-		return 0
-	}
-	var i uint
-	for x[i] == 0 {
-		i++
-	}
-	// x[i] != 0
-	return i*_W + trailingZeroBits(x[i])
-}
-
-// z = x << s
-func (z nat) shl(x nat, s uint) nat {
-	m := len(x)
-	if m == 0 {
-		return z[:0]
-	}
-	// m > 0
-
-	n := m + int(s/_W)
-	z = z.make(n + 1)
-	z[n] = shlVU(z[n-m:n], x, s%_W)
-	z[0 : n-m].clear()
-
-	return z.norm()
-}
-
-// z = x >> s
-func (z nat) shr(x nat, s uint) nat {
-	m := len(x)
-	n := m - int(s/_W)
-	if n <= 0 {
-		return z[:0]
-	}
-	// n > 0
-
-	z = z.make(n)
-	shrVU(z, x[m-n:], s%_W)
-
-	return z.norm()
-}
-
-func (z nat) setBit(x nat, i uint, b uint) nat {
-	j := int(i / _W)
-	m := Word(1) << (i % _W)
-	n := len(x)
-	switch b {
-	case 0:
-		z = z.make(n)
-		copy(z, x)
-		if j >= n {
-			// no need to grow
-			return z
-		}
-		z[j] &^= m
-		return z.norm()
-	case 1:
-		if j >= n {
-			z = z.make(j + 1)
-			z[n:].clear()
-		} else {
-			z = z.make(n)
-		}
-		copy(z, x)
-		z[j] |= m
-		// no need to normalize
-		return z
-	}
-	panic("set bit is not 0 or 1")
-}
-
-// bit returns the value of the i'th bit, with lsb == bit 0.
-func (x nat) bit(i uint) uint {
-	j := i / _W
-	if j >= uint(len(x)) {
-		return 0
-	}
-	// 0 <= j < len(x)
-	return uint(x[j] >> (i % _W) & 1)
-}
-
-// sticky returns 1 if there's a 1 bit within the
-// i least significant bits, otherwise it returns 0.
-func (x nat) sticky(i uint) uint {
-	j := i / _W
-	if j >= uint(len(x)) {
-		if len(x) == 0 {
-			return 0
-		}
-		return 1
-	}
-	// 0 <= j < len(x)
-	for _, x := range x[:j] {
-		if x != 0 {
-			return 1
-		}
-	}
-	if x[j]<<(_W-i%_W) != 0 {
-		return 1
-	}
-	return 0
-}
-
-func (z nat) and(x, y nat) nat {
-	m := len(x)
-	n := len(y)
-	if m > n {
-		m = n
-	}
-	// m <= n
-
-	z = z.make(m)
-	for i := 0; i < m; i++ {
-		z[i] = x[i] & y[i]
-	}
-
-	return z.norm()
-}
-
-func (z nat) andNot(x, y nat) nat {
-	m := len(x)
-	n := len(y)
-	if n > m {
-		n = m
-	}
-	// m >= n
-
-	z = z.make(m)
-	for i := 0; i < n; i++ {
-		z[i] = x[i] &^ y[i]
-	}
-	copy(z[n:m], x[n:m])
-
-	return z.norm()
-}
-
-func (z nat) or(x, y nat) nat {
-	m := len(x)
-	n := len(y)
-	s := x
-	if m < n {
-		n, m = m, n
-		s = y
-	}
-	// m >= n
-
-	z = z.make(m)
-	for i := 0; i < n; i++ {
-		z[i] = x[i] | y[i]
-	}
-	copy(z[n:m], s[n:m])
-
-	return z.norm()
-}
-
-func (z nat) xor(x, y nat) nat {
-	m := len(x)
-	n := len(y)
-	s := x
-	if m < n {
-		n, m = m, n
-		s = y
-	}
-	// m >= n
-
-	z = z.make(m)
-	for i := 0; i < n; i++ {
-		z[i] = x[i] ^ y[i]
-	}
-	copy(z[n:m], s[n:m])
-
-	return z.norm()
-}
-
-// greaterThan reports whether (x1<<_W + x2) > (y1<<_W + y2)
-func greaterThan(x1, x2, y1, y2 Word) bool {
-	return x1 > y1 || x1 == y1 && x2 > y2
-}
-
-// modW returns x % d.
-func (x nat) modW(d Word) (r Word) {
-	// TODO(agl): we don't actually need to store the q value.
-	var q nat
-	q = q.make(len(x))
-	return divWVW(q, 0, x, d)
-}
-
-// random creates a random integer in [0..limit), using the space in z if
-// possible. n is the bit length of limit.
-func (z nat) random(rand *rand.Rand, limit nat, n int) nat {
-	if alias(z, limit) {
-		z = nil // z is an alias for limit - cannot reuse
-	}
-	z = z.make(len(limit))
-
-	bitLengthOfMSW := uint(n % _W)
-	if bitLengthOfMSW == 0 {
-		bitLengthOfMSW = _W
-	}
-	mask := Word((1 << bitLengthOfMSW) - 1)
-
-	for {
-		switch _W {
-		case 32:
-			for i := range z {
-				z[i] = Word(rand.Uint32())
-			}
-		case 64:
-			for i := range z {
-				z[i] = Word(rand.Uint32()) | Word(rand.Uint32())<<32
-			}
-		default:
-			panic("unknown word size")
-		}
-		z[len(limit)-1] &= mask
-		if z.cmp(limit) < 0 {
-			break
-		}
-	}
-
-	return z.norm()
-}
-
-// If m != 0 (i.e., len(m) != 0), expNN sets z to x**y mod m;
-// otherwise it sets z to x**y. The result is the value of z.
-func (z nat) expNN(x, y, m nat) nat {
-	if alias(z, x) || alias(z, y) {
-		// We cannot allow in-place modification of x or y.
-		z = nil
-	}
-
-	// x**y mod 1 == 0
-	if len(m) == 1 && m[0] == 1 {
-		return z.setWord(0)
-	}
-	// m == 0 || m > 1
-
-	// x**0 == 1
-	if len(y) == 0 {
-		return z.setWord(1)
-	}
-	// y > 0
-
-	// x**1 mod m == x mod m
-	if len(y) == 1 && y[0] == 1 && len(m) != 0 {
-		_, z = z.div(z, x, m)
-		return z
-	}
-	// y > 1
-
-	if len(m) != 0 {
-		// We likely end up being as long as the modulus.
-		z = z.make(len(m))
-	}
-	z = z.set(x)
-
-	// If the base is non-trivial and the exponent is large, we use
-	// 4-bit, windowed exponentiation. This involves precomputing 14 values
-	// (x^2...x^15) but then reduces the number of multiply-reduces by a
-	// third. Even for a 32-bit exponent, this reduces the number of
-	// operations. Uses Montgomery method for odd moduli.
-	if len(x) > 1 && len(y) > 1 && len(m) > 0 {
-		if m[0]&1 == 1 {
-			return z.expNNMontgomery(x, y, m)
-		}
-		return z.expNNWindowed(x, y, m)
-	}
-
-	v := y[len(y)-1] // v > 0 because y is normalized and y > 0
-	shift := nlz(v) + 1
-	v <<= shift
-	var q nat
-
-	const mask = 1 << (_W - 1)
-
-	// We walk through the bits of the exponent one by one. Each time we
-	// see a bit, we square, thus doubling the power. If the bit is a one,
-	// we also multiply by x, thus adding one to the power.
-
-	w := _W - int(shift)
-	// zz and r are used to avoid allocating in mul and div as
-	// otherwise the arguments would alias.
-	var zz, r nat
-	for j := 0; j < w; j++ {
-		zz = zz.mul(z, z)
-		zz, z = z, zz
-
-		if v&mask != 0 {
-			zz = zz.mul(z, x)
-			zz, z = z, zz
-		}
-
-		if len(m) != 0 {
-			zz, r = zz.div(r, z, m)
-			zz, r, q, z = q, z, zz, r
-		}
-
-		v <<= 1
-	}
-
-	for i := len(y) - 2; i >= 0; i-- {
-		v = y[i]
-
-		for j := 0; j < _W; j++ {
-			zz = zz.mul(z, z)
-			zz, z = z, zz
-
-			if v&mask != 0 {
-				zz = zz.mul(z, x)
-				zz, z = z, zz
-			}
-
-			if len(m) != 0 {
-				zz, r = zz.div(r, z, m)
-				zz, r, q, z = q, z, zz, r
-			}
-
-			v <<= 1
-		}
-	}
-
-	return z.norm()
-}
-
-// expNNWindowed calculates x**y mod m using a fixed, 4-bit window.
-func (z nat) expNNWindowed(x, y, m nat) nat {
-	// zz and r are used to avoid allocating in mul and div as otherwise
-	// the arguments would alias.
-	var zz, r nat
-
-	const n = 4
-	// powers[i] contains x^i.
-	var powers [1 << n]nat
-	powers[0] = natOne
-	powers[1] = x
-	for i := 2; i < 1<<n; i += 2 {
-		p2, p, p1 := &powers[i/2], &powers[i], &powers[i+1]
-		*p = p.mul(*p2, *p2)
-		zz, r = zz.div(r, *p, m)
-		*p, r = r, *p
-		*p1 = p1.mul(*p, x)
-		zz, r = zz.div(r, *p1, m)
-		*p1, r = r, *p1
-	}
-
-	z = z.setWord(1)
-
-	for i := len(y) - 1; i >= 0; i-- {
-		yi := y[i]
-		for j := 0; j < _W; j += n {
-			if i != len(y)-1 || j != 0 {
-				// Unrolled loop for significant performance
-				// gain. Use go test -bench=".*" in crypto/rsa
-				// to check performance before making changes.
-				zz = zz.mul(z, z)
-				zz, z = z, zz
-				zz, r = zz.div(r, z, m)
-				z, r = r, z
-
-				zz = zz.mul(z, z)
-				zz, z = z, zz
-				zz, r = zz.div(r, z, m)
-				z, r = r, z
-
-				zz = zz.mul(z, z)
-				zz, z = z, zz
-				zz, r = zz.div(r, z, m)
-				z, r = r, z
-
-				zz = zz.mul(z, z)
-				zz, z = z, zz
-				zz, r = zz.div(r, z, m)
-				z, r = r, z
-			}
-
-			zz = zz.mul(z, powers[yi>>(_W-n)])
-			zz, z = z, zz
-			zz, r = zz.div(r, z, m)
-			z, r = r, z
-
-			yi <<= n
-		}
-	}
-
-	return z.norm()
-}
-
-// expNNMontgomery calculates x**y mod m using a fixed, 4-bit window.
-// Uses Montgomery representation.
-func (z nat) expNNMontgomery(x, y, m nat) nat {
-	numWords := len(m)
-
-	// We want the lengths of x and m to be equal.
-	// It is OK if x >= m as long as len(x) == len(m).
-	if len(x) > numWords {
-		_, x = nat(nil).div(nil, x, m)
-		// Note: now len(x) <= numWords, not guaranteed ==.
-	}
-	if len(x) < numWords {
-		rr := make(nat, numWords)
-		copy(rr, x)
-		x = rr
-	}
-
-	// Ideally the precomputations would be performed outside, and reused
-	// k0 = -m**-1 mod 2**_W. Algorithm from: Dumas, J.G. "On Newton–Raphson
-	// Iteration for Multiplicative Inverses Modulo Prime Powers".
-	k0 := 2 - m[0]
-	t := m[0] - 1
-	for i := 1; i < _W; i <<= 1 {
-		t *= t
-		k0 *= (t + 1)
-	}
-	k0 = -k0
-
-	// RR = 2**(2*_W*len(m)) mod m
-	RR := nat(nil).setWord(1)
-	zz := nat(nil).shl(RR, uint(2*numWords*_W))
-	_, RR = RR.div(RR, zz, m)
-	if len(RR) < numWords {
-		zz = zz.make(numWords)
-		copy(zz, RR)
-		RR = zz
-	}
-	// one = 1, with equal length to that of m
-	one := make(nat, numWords)
-	one[0] = 1
-
-	const n = 4
-	// powers[i] contains x^i
-	var powers [1 << n]nat
-	powers[0] = powers[0].montgomery(one, RR, m, k0, numWords)
-	powers[1] = powers[1].montgomery(x, RR, m, k0, numWords)
-	for i := 2; i < 1<<n; i++ {
-		powers[i] = powers[i].montgomery(powers[i-1], powers[1], m, k0, numWords)
-	}
-
-	// initialize z = 1 (Montgomery 1)
-	z = z.make(numWords)
-	copy(z, powers[0])
-
-	zz = zz.make(numWords)
-
-	// same windowed exponent, but with Montgomery multiplications
-	for i := len(y) - 1; i >= 0; i-- {
-		yi := y[i]
-		for j := 0; j < _W; j += n {
-			if i != len(y)-1 || j != 0 {
-				zz = zz.montgomery(z, z, m, k0, numWords)
-				z = z.montgomery(zz, zz, m, k0, numWords)
-				zz = zz.montgomery(z, z, m, k0, numWords)
-				z = z.montgomery(zz, zz, m, k0, numWords)
-			}
-			zz = zz.montgomery(z, powers[yi>>(_W-n)], m, k0, numWords)
-			z, zz = zz, z
-			yi <<= n
-		}
-	}
-	// convert to regular number
-	zz = zz.montgomery(z, one, m, k0, numWords)
-
-	// One last reduction, just in case.
-	// See golang.org/issue/13907.
-	if zz.cmp(m) >= 0 {
-		// Common case is m has high bit set; in that case,
-		// since zz is the same length as m, there can be just
-		// one multiple of m to remove. Just subtract.
-		// We think that the subtract should be sufficient in general,
-		// so do that unconditionally, but double-check,
-		// in case our beliefs are wrong.
-		// The div is not expected to be reached.
-		zz = zz.sub(zz, m)
-		if zz.cmp(m) >= 0 {
-			_, zz = nat(nil).div(nil, zz, m)
-		}
-	}
-
-	return zz.norm()
-}
-
-// probablyPrime performs n Miller-Rabin tests to check whether x is prime.
-// If x is prime, it returns true.
-// If x is not prime, it returns false with probability at least 1 - ¼ⁿ.
-//
-// It is not suitable for judging primes that an adversary may have crafted
-// to fool this test.
-func (n nat) probablyPrime(reps int) bool {
-	if len(n) == 0 {
-		return false
-	}
-
-	if len(n) == 1 {
-		if n[0] < 2 {
-			return false
-		}
-
-		if n[0]%2 == 0 {
-			return n[0] == 2
-		}
-
-		// We have to exclude these cases because we reject all
-		// multiples of these numbers below.
-		switch n[0] {
-		case 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53:
-			return true
-		}
-	}
-
-	if n[0]&1 == 0 {
-		return false // n is even
-	}
-
-	const primesProduct32 = 0xC0CFD797         // Π {p ∈ primes, 2 < p <= 29}
-	const primesProduct64 = 0xE221F97C30E94E1D // Π {p ∈ primes, 2 < p <= 53}
-
-	var r Word
-	switch _W {
-	case 32:
-		r = n.modW(primesProduct32)
-	case 64:
-		r = n.modW(primesProduct64 & _M)
-	default:
-		panic("Unknown word size")
-	}
-
-	if r%3 == 0 || r%5 == 0 || r%7 == 0 || r%11 == 0 ||
-		r%13 == 0 || r%17 == 0 || r%19 == 0 || r%23 == 0 || r%29 == 0 {
-		return false
-	}
-
-	if _W == 64 && (r%31 == 0 || r%37 == 0 || r%41 == 0 ||
-		r%43 == 0 || r%47 == 0 || r%53 == 0) {
-		return false
-	}
-
-	nm1 := nat(nil).sub(n, natOne)
-	// determine q, k such that nm1 = q << k
-	k := nm1.trailingZeroBits()
-	q := nat(nil).shr(nm1, k)
-
-	nm3 := nat(nil).sub(nm1, natTwo)
-	rand := rand.New(rand.NewSource(int64(n[0])))
-
-	var x, y, quotient nat
-	nm3Len := nm3.bitLen()
-
-NextRandom:
-	for i := 0; i < reps; i++ {
-		x = x.random(rand, nm3, nm3Len)
-		x = x.add(x, natTwo)
-		y = y.expNN(x, q, n)
-		if y.cmp(natOne) == 0 || y.cmp(nm1) == 0 {
-			continue
-		}
-		for j := uint(1); j < k; j++ {
-			y = y.mul(y, y)
-			quotient, y = quotient.div(y, y, n)
-			if y.cmp(nm1) == 0 {
-				continue NextRandom
-			}
-			if y.cmp(natOne) == 0 {
-				return false
-			}
-		}
-		return false
-	}
-
-	return true
-}
-
-// bytes writes the value of z into buf using big-endian encoding.
-// len(buf) must be >= len(z)*_S. The value of z is encoded in the
-// slice buf[i:]. The number i of unused bytes at the beginning of
-// buf is returned as result.
-func (z nat) bytes(buf []byte) (i int) {
-	i = len(buf)
-	for _, d := range z {
-		for j := 0; j < _S; j++ {
-			i--
-			buf[i] = byte(d)
-			d >>= 8
-		}
-	}
-
-	for i < len(buf) && buf[i] == 0 {
-		i++
-	}
-
-	return
-}
-
-// setBytes interprets buf as the bytes of a big-endian unsigned
-// integer, sets z to that value, and returns z.
-func (z nat) setBytes(buf []byte) nat {
-	z = z.make((len(buf) + _S - 1) / _S)
-
-	k := 0
-	s := uint(0)
-	var d Word
-	for i := len(buf); i > 0; i-- {
-		d |= Word(buf[i-1]) << s
-		if s += 8; s == _S*8 {
-			z[k] = d
-			k++
-			s = 0
-			d = 0
-		}
-	}
-	if k < len(z) {
-		z[k] = d
-	}
-
-	return z.norm()
-}
diff --git a/src/cmd/compile/internal/big/nat_test.go b/src/cmd/compile/internal/big/nat_test.go
deleted file mode 100644
index 563ccb3..0000000
--- a/src/cmd/compile/internal/big/nat_test.go
+++ /dev/null
@@ -1,654 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"runtime"
-	"strings"
-	"testing"
-)
-
-var cmpTests = []struct {
-	x, y nat
-	r    int
-}{
-	{nil, nil, 0},
-	{nil, nat(nil), 0},
-	{nat(nil), nil, 0},
-	{nat(nil), nat(nil), 0},
-	{nat{0}, nat{0}, 0},
-	{nat{0}, nat{1}, -1},
-	{nat{1}, nat{0}, 1},
-	{nat{1}, nat{1}, 0},
-	{nat{0, _M}, nat{1}, 1},
-	{nat{1}, nat{0, _M}, -1},
-	{nat{1, _M}, nat{0, _M}, 1},
-	{nat{0, _M}, nat{1, _M}, -1},
-	{nat{16, 571956, 8794, 68}, nat{837, 9146, 1, 754489}, -1},
-	{nat{34986, 41, 105, 1957}, nat{56, 7458, 104, 1957}, 1},
-}
-
-func TestCmp(t *testing.T) {
-	for i, a := range cmpTests {
-		r := a.x.cmp(a.y)
-		if r != a.r {
-			t.Errorf("#%d got r = %v; want %v", i, r, a.r)
-		}
-	}
-}
-
-type funNN func(z, x, y nat) nat
-type argNN struct {
-	z, x, y nat
-}
-
-var sumNN = []argNN{
-	{},
-	{nat{1}, nil, nat{1}},
-	{nat{1111111110}, nat{123456789}, nat{987654321}},
-	{nat{0, 0, 0, 1}, nil, nat{0, 0, 0, 1}},
-	{nat{0, 0, 0, 1111111110}, nat{0, 0, 0, 123456789}, nat{0, 0, 0, 987654321}},
-	{nat{0, 0, 0, 1}, nat{0, 0, _M}, nat{0, 0, 1}},
-}
-
-var prodNN = []argNN{
-	{},
-	{nil, nil, nil},
-	{nil, nat{991}, nil},
-	{nat{991}, nat{991}, nat{1}},
-	{nat{991 * 991}, nat{991}, nat{991}},
-	{nat{0, 0, 991 * 991}, nat{0, 991}, nat{0, 991}},
-	{nat{1 * 991, 2 * 991, 3 * 991, 4 * 991}, nat{1, 2, 3, 4}, nat{991}},
-	{nat{4, 11, 20, 30, 20, 11, 4}, nat{1, 2, 3, 4}, nat{4, 3, 2, 1}},
-	// 3^100 * 3^28 = 3^128
-	{
-		natFromString("11790184577738583171520872861412518665678211592275841109096961"),
-		natFromString("515377520732011331036461129765621272702107522001"),
-		natFromString("22876792454961"),
-	},
-	// z = 111....1 (70000 digits)
-	// x = 10^(99*700) + ... + 10^1400 + 10^700 + 1
-	// y = 111....1 (700 digits, larger than Karatsuba threshold on 32-bit and 64-bit)
-	{
-		natFromString(strings.Repeat("1", 70000)),
-		natFromString("1" + strings.Repeat(strings.Repeat("0", 699)+"1", 99)),
-		natFromString(strings.Repeat("1", 700)),
-	},
-	// z = 111....1 (20000 digits)
-	// x = 10^10000 + 1
-	// y = 111....1 (10000 digits)
-	{
-		natFromString(strings.Repeat("1", 20000)),
-		natFromString("1" + strings.Repeat("0", 9999) + "1"),
-		natFromString(strings.Repeat("1", 10000)),
-	},
-}
-
-func natFromString(s string) nat {
-	x, _, _, err := nat(nil).scan(strings.NewReader(s), 0, false)
-	if err != nil {
-		panic(err)
-	}
-	return x
-}
-
-func TestSet(t *testing.T) {
-	for _, a := range sumNN {
-		z := nat(nil).set(a.z)
-		if z.cmp(a.z) != 0 {
-			t.Errorf("got z = %v; want %v", z, a.z)
-		}
-	}
-}
-
-func testFunNN(t *testing.T, msg string, f funNN, a argNN) {
-	z := f(nil, a.x, a.y)
-	if z.cmp(a.z) != 0 {
-		t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, z, a.z)
-	}
-}
-
-func TestFunNN(t *testing.T) {
-	for _, a := range sumNN {
-		arg := a
-		testFunNN(t, "add", nat.add, arg)
-
-		arg = argNN{a.z, a.y, a.x}
-		testFunNN(t, "add symmetric", nat.add, arg)
-
-		arg = argNN{a.x, a.z, a.y}
-		testFunNN(t, "sub", nat.sub, arg)
-
-		arg = argNN{a.y, a.z, a.x}
-		testFunNN(t, "sub symmetric", nat.sub, arg)
-	}
-
-	for _, a := range prodNN {
-		arg := a
-		testFunNN(t, "mul", nat.mul, arg)
-
-		arg = argNN{a.z, a.y, a.x}
-		testFunNN(t, "mul symmetric", nat.mul, arg)
-	}
-}
-
-var mulRangesN = []struct {
-	a, b uint64
-	prod string
-}{
-	{0, 0, "0"},
-	{1, 1, "1"},
-	{1, 2, "2"},
-	{1, 3, "6"},
-	{10, 10, "10"},
-	{0, 100, "0"},
-	{0, 1e9, "0"},
-	{1, 0, "1"},                    // empty range
-	{100, 1, "1"},                  // empty range
-	{1, 10, "3628800"},             // 10!
-	{1, 20, "2432902008176640000"}, // 20!
-	{1, 100,
-		"933262154439441526816992388562667004907159682643816214685929" +
-			"638952175999932299156089414639761565182862536979208272237582" +
-			"51185210916864000000000000000000000000", // 100!
-	},
-}
-
-func TestMulRangeN(t *testing.T) {
-	for i, r := range mulRangesN {
-		prod := string(nat(nil).mulRange(r.a, r.b).utoa(10))
-		if prod != r.prod {
-			t.Errorf("#%d: got %s; want %s", i, prod, r.prod)
-		}
-	}
-}
-
-// allocBytes returns the number of bytes allocated by invoking f.
-func allocBytes(f func()) uint64 {
-	var stats runtime.MemStats
-	runtime.ReadMemStats(&stats)
-	t := stats.TotalAlloc
-	f()
-	runtime.ReadMemStats(&stats)
-	return stats.TotalAlloc - t
-}
-
-// TestMulUnbalanced tests that multiplying numbers of different lengths
-// does not cause deep recursion and in turn allocate too much memory.
-// Test case for issue 3807.
-func TestMulUnbalanced(t *testing.T) {
-	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
-	x := rndNat(50000)
-	y := rndNat(40)
-	allocSize := allocBytes(func() {
-		nat(nil).mul(x, y)
-	})
-	inputSize := uint64(len(x)+len(y)) * _S
-	if ratio := allocSize / uint64(inputSize); ratio > 10 {
-		t.Errorf("multiplication uses too much memory (%d > %d times the size of inputs)", allocSize, ratio)
-	}
-}
-
-func rndNat(n int) nat {
-	return nat(rndV(n)).norm()
-}
-
-func BenchmarkMul(b *testing.B) {
-	mulx := rndNat(1e4)
-	muly := rndNat(1e4)
-	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
-		var z nat
-		z.mul(mulx, muly)
-	}
-}
-
-func TestNLZ(t *testing.T) {
-	var x Word = _B >> 1
-	for i := 0; i <= _W; i++ {
-		if int(nlz(x)) != i {
-			t.Errorf("failed at %x: got %d want %d", x, nlz(x), i)
-		}
-		x >>= 1
-	}
-}
-
-type shiftTest struct {
-	in    nat
-	shift uint
-	out   nat
-}
-
-var leftShiftTests = []shiftTest{
-	{nil, 0, nil},
-	{nil, 1, nil},
-	{natOne, 0, natOne},
-	{natOne, 1, natTwo},
-	{nat{1 << (_W - 1)}, 1, nat{0}},
-	{nat{1 << (_W - 1), 0}, 1, nat{0, 1}},
-}
-
-func TestShiftLeft(t *testing.T) {
-	for i, test := range leftShiftTests {
-		var z nat
-		z = z.shl(test.in, test.shift)
-		for j, d := range test.out {
-			if j >= len(z) || z[j] != d {
-				t.Errorf("#%d: got: %v want: %v", i, z, test.out)
-				break
-			}
-		}
-	}
-}
-
-var rightShiftTests = []shiftTest{
-	{nil, 0, nil},
-	{nil, 1, nil},
-	{natOne, 0, natOne},
-	{natOne, 1, nil},
-	{natTwo, 1, natOne},
-	{nat{0, 1}, 1, nat{1 << (_W - 1)}},
-	{nat{2, 1, 1}, 1, nat{1<<(_W-1) + 1, 1 << (_W - 1)}},
-}
-
-func TestShiftRight(t *testing.T) {
-	for i, test := range rightShiftTests {
-		var z nat
-		z = z.shr(test.in, test.shift)
-		for j, d := range test.out {
-			if j >= len(z) || z[j] != d {
-				t.Errorf("#%d: got: %v want: %v", i, z, test.out)
-				break
-			}
-		}
-	}
-}
-
-type modWTest struct {
-	in       string
-	dividend string
-	out      string
-}
-
-var modWTests32 = []modWTest{
-	{"23492635982634928349238759823742", "252341", "220170"},
-}
-
-var modWTests64 = []modWTest{
-	{"6527895462947293856291561095690465243862946", "524326975699234", "375066989628668"},
-}
-
-func runModWTests(t *testing.T, tests []modWTest) {
-	for i, test := range tests {
-		in, _ := new(Int).SetString(test.in, 10)
-		d, _ := new(Int).SetString(test.dividend, 10)
-		out, _ := new(Int).SetString(test.out, 10)
-
-		r := in.abs.modW(d.abs[0])
-		if r != out.abs[0] {
-			t.Errorf("#%d failed: got %d want %s", i, r, out)
-		}
-	}
-}
-
-func TestModW(t *testing.T) {
-	if _W >= 32 {
-		runModWTests(t, modWTests32)
-	}
-	if _W >= 64 {
-		runModWTests(t, modWTests64)
-	}
-}
-
-func TestTrailingZeroBits(t *testing.T) {
-	// test 0 case explicitly
-	if n := trailingZeroBits(0); n != 0 {
-		t.Errorf("got trailingZeroBits(0) = %d; want 0", n)
-	}
-
-	x := Word(1)
-	for i := uint(0); i < _W; i++ {
-		n := trailingZeroBits(x)
-		if n != i {
-			t.Errorf("got trailingZeroBits(%#x) = %d; want %d", x, n, i%_W)
-		}
-		x <<= 1
-	}
-
-	// test 0 case explicitly
-	if n := nat(nil).trailingZeroBits(); n != 0 {
-		t.Errorf("got nat(nil).trailingZeroBits() = %d; want 0", n)
-	}
-
-	y := nat(nil).set(natOne)
-	for i := uint(0); i <= 3*_W; i++ {
-		n := y.trailingZeroBits()
-		if n != i {
-			t.Errorf("got 0x%s.trailingZeroBits() = %d; want %d", y.utoa(16), n, i)
-		}
-		y = y.shl(y, 1)
-	}
-}
-
-var montgomeryTests = []struct {
-	x, y, m      string
-	k0           uint64
-	out32, out64 string
-}{
-	{
-		"0xffffffffffffffffffffffffffffffffffffffffffffffffe",
-		"0xffffffffffffffffffffffffffffffffffffffffffffffffe",
-		"0xfffffffffffffffffffffffffffffffffffffffffffffffff",
-		1,
-		"0x1000000000000000000000000000000000000000000",
-		"0x10000000000000000000000000000000000",
-	},
-	{
-		"0x000000000ffffff5",
-		"0x000000000ffffff0",
-		"0x0000000010000001",
-		0xff0000000fffffff,
-		"0x000000000bfffff4",
-		"0x0000000003400001",
-	},
-	{
-		"0x0000000080000000",
-		"0x00000000ffffffff",
-		"0x1000000000000001",
-		0xfffffffffffffff,
-		"0x0800000008000001",
-		"0x0800000008000001",
-	},
-	{
-		"0x0000000080000000",
-		"0x0000000080000000",
-		"0xffffffff00000001",
-		0xfffffffeffffffff,
-		"0xbfffffff40000001",
-		"0xbfffffff40000001",
-	},
-	{
-		"0x0000000080000000",
-		"0x0000000080000000",
-		"0x00ffffff00000001",
-		0xfffffeffffffff,
-		"0xbfffff40000001",
-		"0xbfffff40000001",
-	},
-	{
-		"0x0000000080000000",
-		"0x0000000080000000",
-		"0x0000ffff00000001",
-		0xfffeffffffff,
-		"0xbfff40000001",
-		"0xbfff40000001",
-	},
-	{
-		"0x3321ffffffffffffffffffffffffffff00000000000022222623333333332bbbb888c0",
-		"0x3321ffffffffffffffffffffffffffff00000000000022222623333333332bbbb888c0",
-		"0x33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1",
-		0xdecc8f1249812adf,
-		"0x04eb0e11d72329dc0915f86784820fc403275bf2f6620a20e0dd344c5cd0875e50deb5",
-		"0x0d7144739a7d8e11d72329dc0915f86784820fc403275bf2f61ed96f35dd34dbb3d6a0",
-	},
-	{
-		"0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000022222223333333333444444444",
-		"0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff999999999999999aaabbbbbbbbcccccccccccc",
-		"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1",
-		0xdecc8f1249812adf,
-		"0x5c0d52f451aec609b15da8e5e5626c4eaa88723bdeac9d25ca9b961269400410ca208a16af9c2fb07d7a11c7772cba02c22f9711078d51a3797eb18e691295293284d988e349fa6deba46b25a4ecd9f715",
-		"0x92fcad4b5c0d52f451aec609b15da8e5e5626c4eaa88723bdeac9d25ca9b961269400410ca208a16af9c2fb07d799c32fe2f3cc5422f9711078d51a3797eb18e691295293284d8f5e69caf6decddfe1df6",
-	},
-}
-
-func TestMontgomery(t *testing.T) {
-	one := NewInt(1)
-	_B := new(Int).Lsh(one, _W)
-	for i, test := range montgomeryTests {
-		x := natFromString(test.x)
-		y := natFromString(test.y)
-		m := natFromString(test.m)
-		for len(x) < len(m) {
-			x = append(x, 0)
-		}
-		for len(y) < len(m) {
-			y = append(y, 0)
-		}
-
-		if x.cmp(m) > 0 {
-			_, r := nat(nil).div(nil, x, m)
-			t.Errorf("#%d: x > m (0x%s > 0x%s; use 0x%s)", i, x.utoa(16), m.utoa(16), r.utoa(16))
-		}
-		if y.cmp(m) > 0 {
-			_, r := nat(nil).div(nil, x, m)
-			t.Errorf("#%d: y > m (0x%s > 0x%s; use 0x%s)", i, y.utoa(16), m.utoa(16), r.utoa(16))
-		}
-
-		var out nat
-		if _W == 32 {
-			out = natFromString(test.out32)
-		} else {
-			out = natFromString(test.out64)
-		}
-
-		// t.Logf("#%d: len=%d\n", i, len(m))
-
-		// check output in table
-		xi := &Int{abs: x}
-		yi := &Int{abs: y}
-		mi := &Int{abs: m}
-		p := new(Int).Mod(new(Int).Mul(xi, new(Int).Mul(yi, new(Int).ModInverse(new(Int).Lsh(one, uint(len(m))*_W), mi))), mi)
-		if out.cmp(p.abs.norm()) != 0 {
-			t.Errorf("#%d: out in table=0x%s, computed=0x%s", i, out.utoa(16), p.abs.norm().utoa(16))
-		}
-
-		// check k0 in table
-		k := new(Int).Mod(&Int{abs: m}, _B)
-		k = new(Int).Sub(_B, k)
-		k = new(Int).Mod(k, _B)
-		k0 := Word(new(Int).ModInverse(k, _B).Uint64())
-		if k0 != Word(test.k0) {
-			t.Errorf("#%d: k0 in table=%#x, computed=%#x\n", i, test.k0, k0)
-		}
-
-		// check montgomery with correct k0 produces correct output
-		z := nat(nil).montgomery(x, y, m, k0, len(m))
-		z = z.norm()
-		if z.cmp(out) != 0 {
-			t.Errorf("#%d: got 0x%s want 0x%s", i, z.utoa(16), out.utoa(16))
-		}
-	}
-}
-
-var expNNTests = []struct {
-	x, y, m string
-	out     string
-}{
-	{"0", "0", "0", "1"},
-	{"0", "0", "1", "0"},
-	{"1", "1", "1", "0"},
-	{"2", "1", "1", "0"},
-	{"2", "2", "1", "0"},
-	{"10", "100000000000", "1", "0"},
-	{"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"},
-	{"0x8000000000000000", "2", "6719", "4944"},
-	{"0x8000000000000000", "3", "6719", "5447"},
-	{"0x8000000000000000", "1000", "6719", "1603"},
-	{"0x8000000000000000", "1000000", "6719", "3199"},
-	{
-		"2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347",
-		"298472983472983471903246121093472394872319615612417471234712061",
-		"29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464",
-		"23537740700184054162508175125554701713153216681790245129157191391322321508055833908509185839069455749219131480588829346291",
-	},
-	{
-		"11521922904531591643048817447554701904414021819823889996244743037378330903763518501116638828335352811871131385129455853417360623007349090150042001944696604737499160174391019030572483602867266711107136838523916077674888297896995042968746762200926853379",
-		"426343618817810911523",
-		"444747819283133684179",
-		"42",
-	},
-}
-
-func TestExpNN(t *testing.T) {
-	for i, test := range expNNTests {
-		x := natFromString(test.x)
-		y := natFromString(test.y)
-		out := natFromString(test.out)
-
-		var m nat
-		if len(test.m) > 0 {
-			m = natFromString(test.m)
-		}
-
-		z := nat(nil).expNN(x, y, m)
-		if z.cmp(out) != 0 {
-			t.Errorf("#%d got %s want %s", i, z.utoa(10), out.utoa(10))
-		}
-	}
-}
-
-func ExpHelper(b *testing.B, x, y Word) {
-	var z nat
-	for i := 0; i < b.N; i++ {
-		z.expWW(x, y)
-	}
-}
-
-func BenchmarkExp3Power0x10(b *testing.B)     { ExpHelper(b, 3, 0x10) }
-func BenchmarkExp3Power0x40(b *testing.B)     { ExpHelper(b, 3, 0x40) }
-func BenchmarkExp3Power0x100(b *testing.B)    { ExpHelper(b, 3, 0x100) }
-func BenchmarkExp3Power0x400(b *testing.B)    { ExpHelper(b, 3, 0x400) }
-func BenchmarkExp3Power0x1000(b *testing.B)   { ExpHelper(b, 3, 0x1000) }
-func BenchmarkExp3Power0x4000(b *testing.B)   { ExpHelper(b, 3, 0x4000) }
-func BenchmarkExp3Power0x10000(b *testing.B)  { ExpHelper(b, 3, 0x10000) }
-func BenchmarkExp3Power0x40000(b *testing.B)  { ExpHelper(b, 3, 0x40000) }
-func BenchmarkExp3Power0x100000(b *testing.B) { ExpHelper(b, 3, 0x100000) }
-func BenchmarkExp3Power0x400000(b *testing.B) { ExpHelper(b, 3, 0x400000) }
-
-func fibo(n int) nat {
-	switch n {
-	case 0:
-		return nil
-	case 1:
-		return nat{1}
-	}
-	f0 := fibo(0)
-	f1 := fibo(1)
-	var f2 nat
-	for i := 1; i < n; i++ {
-		f2 = f2.add(f0, f1)
-		f0, f1, f2 = f1, f2, f0
-	}
-	return f1
-}
-
-var fiboNums = []string{
-	"0",
-	"55",
-	"6765",
-	"832040",
-	"102334155",
-	"12586269025",
-	"1548008755920",
-	"190392490709135",
-	"23416728348467685",
-	"2880067194370816120",
-	"354224848179261915075",
-}
-
-func TestFibo(t *testing.T) {
-	for i, want := range fiboNums {
-		n := i * 10
-		got := string(fibo(n).utoa(10))
-		if got != want {
-			t.Errorf("fibo(%d) failed: got %s want %s", n, got, want)
-		}
-	}
-}
-
-func BenchmarkFibo(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		fibo(1e0)
-		fibo(1e1)
-		fibo(1e2)
-		fibo(1e3)
-		fibo(1e4)
-		fibo(1e5)
-	}
-}
-
-var bitTests = []struct {
-	x    string
-	i    uint
-	want uint
-}{
-	{"0", 0, 0},
-	{"0", 1, 0},
-	{"0", 1000, 0},
-
-	{"0x1", 0, 1},
-	{"0x10", 0, 0},
-	{"0x10", 3, 0},
-	{"0x10", 4, 1},
-	{"0x10", 5, 0},
-
-	{"0x8000000000000000", 62, 0},
-	{"0x8000000000000000", 63, 1},
-	{"0x8000000000000000", 64, 0},
-
-	{"0x3" + strings.Repeat("0", 32), 127, 0},
-	{"0x3" + strings.Repeat("0", 32), 128, 1},
-	{"0x3" + strings.Repeat("0", 32), 129, 1},
-	{"0x3" + strings.Repeat("0", 32), 130, 0},
-}
-
-func TestBit(t *testing.T) {
-	for i, test := range bitTests {
-		x := natFromString(test.x)
-		if got := x.bit(test.i); got != test.want {
-			t.Errorf("#%d: %s.bit(%d) = %v; want %v", i, test.x, test.i, got, test.want)
-		}
-	}
-}
-
-var stickyTests = []struct {
-	x    string
-	i    uint
-	want uint
-}{
-	{"0", 0, 0},
-	{"0", 1, 0},
-	{"0", 1000, 0},
-
-	{"0x1", 0, 0},
-	{"0x1", 1, 1},
-
-	{"0x1350", 0, 0},
-	{"0x1350", 4, 0},
-	{"0x1350", 5, 1},
-
-	{"0x8000000000000000", 63, 0},
-	{"0x8000000000000000", 64, 1},
-
-	{"0x1" + strings.Repeat("0", 100), 400, 0},
-	{"0x1" + strings.Repeat("0", 100), 401, 1},
-}
-
-func TestSticky(t *testing.T) {
-	for i, test := range stickyTests {
-		x := natFromString(test.x)
-		if got := x.sticky(test.i); got != test.want {
-			t.Errorf("#%d: %s.sticky(%d) = %v; want %v", i, test.x, test.i, got, test.want)
-		}
-		if test.want == 1 {
-			// all subsequent i's should also return 1
-			for d := uint(1); d <= 3; d++ {
-				if got := x.sticky(test.i + d); got != 1 {
-					t.Errorf("#%d: %s.sticky(%d) = %v; want %v", i, test.x, test.i+d, got, 1)
-				}
-			}
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/natconv.go b/src/cmd/compile/internal/big/natconv.go
deleted file mode 100644
index d2ce667..0000000
--- a/src/cmd/compile/internal/big/natconv.go
+++ /dev/null
@@ -1,492 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements nat-to-string conversion functions.
-
-package big
-
-import (
-	"errors"
-	"fmt"
-	"io"
-	"math"
-	"sync"
-)
-
-const digits = "0123456789abcdefghijklmnopqrstuvwxyz"
-
-// Note: MaxBase = len(digits), but it must remain a rune constant
-//       for API compatibility.
-
-// MaxBase is the largest number base accepted for string conversions.
-const MaxBase = 'z' - 'a' + 10 + 1
-
-// maxPow returns (b**n, n) such that b**n is the largest power b**n <= _M.
-// For instance maxPow(10) == (1e19, 19) for 19 decimal digits in a 64bit Word.
-// In other words, at most n digits in base b fit into a Word.
-// TODO(gri) replace this with a table, generated at build time.
-func maxPow(b Word) (p Word, n int) {
-	p, n = b, 1 // assuming b <= _M
-	for max := _M / b; p <= max; {
-		// p == b**n && p <= max
-		p *= b
-		n++
-	}
-	// p == b**n && p <= _M
-	return
-}
-
-// pow returns x**n for n > 0, and 1 otherwise.
-func pow(x Word, n int) (p Word) {
-	// n == sum of bi * 2**i, for 0 <= i < imax, and bi is 0 or 1
-	// thus x**n == product of x**(2**i) for all i where bi == 1
-	// (Russian Peasant Method for exponentiation)
-	p = 1
-	for n > 0 {
-		if n&1 != 0 {
-			p *= x
-		}
-		x *= x
-		n >>= 1
-	}
-	return
-}
-
-// scan scans the number corresponding to the longest possible prefix
-// from r representing an unsigned number in a given conversion base.
-// It returns the corresponding natural number res, the actual base b,
-// a digit count, and a read or syntax error err, if any.
-//
-//	number   = [ prefix ] mantissa .
-//	prefix   = "0" [ "x" | "X" | "b" | "B" ] .
-//      mantissa = digits | digits "." [ digits ] | "." digits .
-//	digits   = digit { digit } .
-//	digit    = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
-//
-// Unless fracOk is set, the base argument must be 0 or a value between
-// 2 and MaxBase. If fracOk is set, the base argument must be one of
-// 0, 2, 10, or 16. Providing an invalid base argument leads to a run-
-// time panic.
-//
-// For base 0, the number prefix determines the actual base: A prefix of
-// ``0x'' or ``0X'' selects base 16; if fracOk is not set, the ``0'' prefix
-// selects base 8, and a ``0b'' or ``0B'' prefix selects base 2. Otherwise
-// the selected base is 10 and no prefix is accepted.
-//
-// If fracOk is set, an octal prefix is ignored (a leading ``0'' simply
-// stands for a zero digit), and a period followed by a fractional part
-// is permitted. The result value is computed as if there were no period
-// present; and the count value is used to determine the fractional part.
-//
-// A result digit count > 0 corresponds to the number of (non-prefix) digits
-// parsed. A digit count <= 0 indicates the presence of a period (if fracOk
-// is set, only), and -count is the number of fractional digits found.
-// In this case, the actual value of the scanned number is res * b**count.
-//
-func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count int, err error) {
-	// reject illegal bases
-	baseOk := base == 0 ||
-		!fracOk && 2 <= base && base <= MaxBase ||
-		fracOk && (base == 2 || base == 10 || base == 16)
-	if !baseOk {
-		panic(fmt.Sprintf("illegal number base %d", base))
-	}
-
-	// one char look-ahead
-	ch, err := r.ReadByte()
-	if err != nil {
-		return
-	}
-
-	// determine actual base
-	b = base
-	if base == 0 {
-		// actual base is 10 unless there's a base prefix
-		b = 10
-		if ch == '0' {
-			count = 1
-			switch ch, err = r.ReadByte(); err {
-			case nil:
-				// possibly one of 0x, 0X, 0b, 0B
-				if !fracOk {
-					b = 8
-				}
-				switch ch {
-				case 'x', 'X':
-					b = 16
-				case 'b', 'B':
-					b = 2
-				}
-				switch b {
-				case 16, 2:
-					count = 0 // prefix is not counted
-					if ch, err = r.ReadByte(); err != nil {
-						// io.EOF is also an error in this case
-						return
-					}
-				case 8:
-					count = 0 // prefix is not counted
-				}
-			case io.EOF:
-				// input is "0"
-				res = z[:0]
-				err = nil
-				return
-			default:
-				// read error
-				return
-			}
-		}
-	}
-
-	// convert string
-	// Algorithm: Collect digits in groups of at most n digits in di
-	// and then use mulAddWW for every such group to add them to the
-	// result.
-	z = z[:0]
-	b1 := Word(b)
-	bn, n := maxPow(b1) // at most n digits in base b1 fit into Word
-	di := Word(0)       // 0 <= di < b1**i < bn
-	i := 0              // 0 <= i < n
-	dp := -1            // position of decimal point
-	for {
-		if fracOk && ch == '.' {
-			fracOk = false
-			dp = count
-			// advance
-			if ch, err = r.ReadByte(); err != nil {
-				if err == io.EOF {
-					err = nil
-					break
-				}
-				return
-			}
-		}
-
-		// convert rune into digit value d1
-		var d1 Word
-		switch {
-		case '0' <= ch && ch <= '9':
-			d1 = Word(ch - '0')
-		case 'a' <= ch && ch <= 'z':
-			d1 = Word(ch - 'a' + 10)
-		case 'A' <= ch && ch <= 'Z':
-			d1 = Word(ch - 'A' + 10)
-		default:
-			d1 = MaxBase + 1
-		}
-		if d1 >= b1 {
-			r.UnreadByte() // ch does not belong to number anymore
-			break
-		}
-		count++
-
-		// collect d1 in di
-		di = di*b1 + d1
-		i++
-
-		// if di is "full", add it to the result
-		if i == n {
-			z = z.mulAddWW(z, bn, di)
-			di = 0
-			i = 0
-		}
-
-		// advance
-		if ch, err = r.ReadByte(); err != nil {
-			if err == io.EOF {
-				err = nil
-				break
-			}
-			return
-		}
-	}
-
-	if count == 0 {
-		// no digits found
-		switch {
-		case base == 0 && b == 8:
-			// there was only the octal prefix 0 (possibly followed by digits > 7);
-			// count as one digit and return base 10, not 8
-			count = 1
-			b = 10
-		case base != 0 || b != 8:
-			// there was neither a mantissa digit nor the octal prefix 0
-			err = errors.New("syntax error scanning number")
-		}
-		return
-	}
-	// count > 0
-
-	// add remaining digits to result
-	if i > 0 {
-		z = z.mulAddWW(z, pow(b1, i), di)
-	}
-	res = z.norm()
-
-	// adjust for fraction, if any
-	if dp >= 0 {
-		// 0 <= dp <= count > 0
-		count = dp - count
-	}
-
-	return
-}
-
-// utoa converts x to an ASCII representation in the given base;
-// base must be between 2 and MaxBase, inclusive.
-func (x nat) utoa(base int) []byte {
-	return x.itoa(false, base)
-}
-
-// itoa is like utoa but it prepends a '-' if neg && x != 0.
-func (x nat) itoa(neg bool, base int) []byte {
-	if base < 2 || base > MaxBase {
-		panic("invalid base")
-	}
-
-	// x == 0
-	if len(x) == 0 {
-		return []byte("0")
-	}
-	// len(x) > 0
-
-	// allocate buffer for conversion
-	i := int(float64(x.bitLen())/math.Log2(float64(base))) + 1 // off by 1 at most
-	if neg {
-		i++
-	}
-	s := make([]byte, i)
-
-	// convert power of two and non power of two bases separately
-	if b := Word(base); b == b&-b {
-		// shift is base b digit size in bits
-		shift := trailingZeroBits(b) // shift > 0 because b >= 2
-		mask := Word(1<<shift - 1)
-		w := x[0]         // current word
-		nbits := uint(_W) // number of unprocessed bits in w
-
-		// convert less-significant words (include leading zeros)
-		for k := 1; k < len(x); k++ {
-			// convert full digits
-			for nbits >= shift {
-				i--
-				s[i] = digits[w&mask]
-				w >>= shift
-				nbits -= shift
-			}
-
-			// convert any partial leading digit and advance to next word
-			if nbits == 0 {
-				// no partial digit remaining, just advance
-				w = x[k]
-				nbits = _W
-			} else {
-				// partial digit in current word w (== x[k-1]) and next word x[k]
-				w |= x[k] << nbits
-				i--
-				s[i] = digits[w&mask]
-
-				// advance
-				w = x[k] >> (shift - nbits)
-				nbits = _W - (shift - nbits)
-			}
-		}
-
-		// convert digits of most-significant word w (omit leading zeros)
-		for w != 0 {
-			i--
-			s[i] = digits[w&mask]
-			w >>= shift
-		}
-
-	} else {
-		bb, ndigits := maxPow(Word(b))
-
-		// construct table of successive squares of bb*leafSize to use in subdivisions
-		// result (table != nil) <=> (len(x) > leafSize > 0)
-		table := divisors(len(x), b, ndigits, bb)
-
-		// preserve x, create local copy for use by convertWords
-		q := nat(nil).set(x)
-
-		// convert q to string s in base b
-		q.convertWords(s, b, ndigits, bb, table)
-
-		// strip leading zeros
-		// (x != 0; thus s must contain at least one non-zero digit
-		// and the loop will terminate)
-		i = 0
-		for s[i] == '0' {
-			i++
-		}
-	}
-
-	if neg {
-		i--
-		s[i] = '-'
-	}
-
-	return s[i:]
-}
-
-// Convert words of q to base b digits in s. If q is large, it is recursively "split in half"
-// by nat/nat division using tabulated divisors. Otherwise, it is converted iteratively using
-// repeated nat/Word division.
-//
-// The iterative method processes n Words by n divW() calls, each of which visits every Word in the
-// incrementally shortened q for a total of n + (n-1) + (n-2) ... + 2 + 1, or n(n+1)/2 divW()'s.
-// Recursive conversion divides q by its approximate square root, yielding two parts, each half
-// the size of q. Using the iterative method on both halves means 2 * (n/2)(n/2 + 1)/2 divW()'s
-// plus the expensive long div(). Asymptotically, the ratio is favorable at 1/2 the divW()'s, and
-// is made better by splitting the subblocks recursively. Best is to split blocks until one more
-// split would take longer (because of the nat/nat div()) than the twice as many divW()'s of the
-// iterative approach. This threshold is represented by leafSize. Benchmarking of leafSize in the
-// range 2..64 shows that values of 8 and 16 work well, with a 4x speedup at medium lengths and
-// ~30x for 20000 digits. Use nat_test.go's BenchmarkLeafSize tests to optimize leafSize for
-// specific hardware.
-//
-func (q nat) convertWords(s []byte, b Word, ndigits int, bb Word, table []divisor) {
-	// split larger blocks recursively
-	if table != nil {
-		// len(q) > leafSize > 0
-		var r nat
-		index := len(table) - 1
-		for len(q) > leafSize {
-			// find divisor close to sqrt(q) if possible, but in any case < q
-			maxLength := q.bitLen()     // ~= log2 q, or at of least largest possible q of this bit length
-			minLength := maxLength >> 1 // ~= log2 sqrt(q)
-			for index > 0 && table[index-1].nbits > minLength {
-				index-- // desired
-			}
-			if table[index].nbits >= maxLength && table[index].bbb.cmp(q) >= 0 {
-				index--
-				if index < 0 {
-					panic("internal inconsistency")
-				}
-			}
-
-			// split q into the two digit number (q'*bbb + r) to form independent subblocks
-			q, r = q.div(r, q, table[index].bbb)
-
-			// convert subblocks and collect results in s[:h] and s[h:]
-			h := len(s) - table[index].ndigits
-			r.convertWords(s[h:], b, ndigits, bb, table[0:index])
-			s = s[:h] // == q.convertWords(s, b, ndigits, bb, table[0:index+1])
-		}
-	}
-
-	// having split any large blocks now process the remaining (small) block iteratively
-	i := len(s)
-	var r Word
-	if b == 10 {
-		// hard-coding for 10 here speeds this up by 1.25x (allows for / and % by constants)
-		for len(q) > 0 {
-			// extract least significant, base bb "digit"
-			q, r = q.divW(q, bb)
-			for j := 0; j < ndigits && i > 0; j++ {
-				i--
-				// avoid % computation since r%10 == r - int(r/10)*10;
-				// this appears to be faster for BenchmarkString10000Base10
-				// and smaller strings (but a bit slower for larger ones)
-				t := r / 10
-				s[i] = '0' + byte(r-t<<3-t-t) // TODO(gri) replace w/ t*10 once compiler produces better code
-				r = t
-			}
-		}
-	} else {
-		for len(q) > 0 {
-			// extract least significant, base bb "digit"
-			q, r = q.divW(q, bb)
-			for j := 0; j < ndigits && i > 0; j++ {
-				i--
-				s[i] = digits[r%b]
-				r /= b
-			}
-		}
-	}
-
-	// prepend high-order zeros
-	for i > 0 { // while need more leading zeros
-		i--
-		s[i] = '0'
-	}
-}
-
-// Split blocks greater than leafSize Words (or set to 0 to disable recursive conversion)
-// Benchmark and configure leafSize using: go test -bench="Leaf"
-//   8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines)
-//   8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU
-var leafSize int = 8 // number of Word-size binary values treat as a monolithic block
-
-type divisor struct {
-	bbb     nat // divisor
-	nbits   int // bit length of divisor (discounting leading zeros) ~= log2(bbb)
-	ndigits int // digit length of divisor in terms of output base digits
-}
-
-var cacheBase10 struct {
-	sync.Mutex
-	table [64]divisor // cached divisors for base 10
-}
-
-// expWW computes x**y
-func (z nat) expWW(x, y Word) nat {
-	return z.expNN(nat(nil).setWord(x), nat(nil).setWord(y), nil)
-}
-
-// construct table of powers of bb*leafSize to use in subdivisions
-func divisors(m int, b Word, ndigits int, bb Word) []divisor {
-	// only compute table when recursive conversion is enabled and x is large
-	if leafSize == 0 || m <= leafSize {
-		return nil
-	}
-
-	// determine k where (bb**leafSize)**(2**k) >= sqrt(x)
-	k := 1
-	for words := leafSize; words < m>>1 && k < len(cacheBase10.table); words <<= 1 {
-		k++
-	}
-
-	// reuse and extend existing table of divisors or create new table as appropriate
-	var table []divisor // for b == 10, table overlaps with cacheBase10.table
-	if b == 10 {
-		cacheBase10.Lock()
-		table = cacheBase10.table[0:k] // reuse old table for this conversion
-	} else {
-		table = make([]divisor, k) // create new table for this conversion
-	}
-
-	// extend table
-	if table[k-1].ndigits == 0 {
-		// add new entries as needed
-		var larger nat
-		for i := 0; i < k; i++ {
-			if table[i].ndigits == 0 {
-				if i == 0 {
-					table[0].bbb = nat(nil).expWW(bb, Word(leafSize))
-					table[0].ndigits = ndigits * leafSize
-				} else {
-					table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb)
-					table[i].ndigits = 2 * table[i-1].ndigits
-				}
-
-				// optimization: exploit aggregated extra bits in macro blocks
-				larger = nat(nil).set(table[i].bbb)
-				for mulAddVWW(larger, larger, b, 0) == 0 {
-					table[i].bbb = table[i].bbb.set(larger)
-					table[i].ndigits++
-				}
-
-				table[i].nbits = table[i].bbb.bitLen()
-			}
-		}
-	}
-
-	if b == 10 {
-		cacheBase10.Unlock()
-	}
-
-	return table
-}
diff --git a/src/cmd/compile/internal/big/natconv_test.go b/src/cmd/compile/internal/big/natconv_test.go
deleted file mode 100644
index 028e5a8..0000000
--- a/src/cmd/compile/internal/big/natconv_test.go
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"bytes"
-	"io"
-	"strings"
-	"testing"
-)
-
-func itoa(x nat, base int) []byte {
-	// special cases
-	switch {
-	case base < 2:
-		panic("illegal base")
-	case len(x) == 0:
-		return []byte("0")
-	}
-
-	// allocate buffer for conversion
-	i := x.bitLen()/log2(Word(base)) + 1 // +1: round up
-	s := make([]byte, i)
-
-	// don't destroy x
-	q := nat(nil).set(x)
-
-	// convert
-	for len(q) > 0 {
-		i--
-		var r Word
-		q, r = q.divW(q, Word(base))
-		s[i] = digits[r]
-	}
-
-	return s[i:]
-}
-
-var strTests = []struct {
-	x nat    // nat value to be converted
-	b int    // conversion base
-	s string // expected result
-}{
-	{nil, 2, "0"},
-	{nat{1}, 2, "1"},
-	{nat{0xc5}, 2, "11000101"},
-	{nat{03271}, 8, "3271"},
-	{nat{10}, 10, "10"},
-	{nat{1234567890}, 10, "1234567890"},
-	{nat{0xdeadbeef}, 16, "deadbeef"},
-	{nat{0x229be7}, 17, "1a2b3c"},
-	{nat{0x309663e6}, 32, "o9cov6"},
-}
-
-func TestString(t *testing.T) {
-	// test invalid base explicitly
-	var panicStr string
-	func() {
-		defer func() {
-			panicStr = recover().(string)
-		}()
-		natOne.utoa(1)
-	}()
-	if panicStr != "invalid base" {
-		t.Errorf("expected panic for invalid base")
-	}
-
-	for _, a := range strTests {
-		s := string(a.x.utoa(a.b))
-		if s != a.s {
-			t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s)
-		}
-
-		x, b, _, err := nat(nil).scan(strings.NewReader(a.s), a.b, false)
-		if x.cmp(a.x) != 0 {
-			t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
-		}
-		if b != a.b {
-			t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.b)
-		}
-		if err != nil {
-			t.Errorf("scan%+v\n\tgot error = %s", a, err)
-		}
-	}
-}
-
-var natScanTests = []struct {
-	s     string // string to be scanned
-	base  int    // input base
-	frac  bool   // fraction ok
-	x     nat    // expected nat
-	b     int    // expected base
-	count int    // expected digit count
-	ok    bool   // expected success
-	next  rune   // next character (or 0, if at EOF)
-}{
-	// error: no mantissa
-	{},
-	{s: "?"},
-	{base: 10},
-	{base: 36},
-	{s: "?", base: 10},
-	{s: "0x"},
-	{s: "345", base: 2},
-
-	// error: incorrect use of decimal point
-	{s: ".0"},
-	{s: ".0", base: 10},
-	{s: ".", base: 0},
-	{s: "0x.0"},
-
-	// no errors
-	{"0", 0, false, nil, 10, 1, true, 0},
-	{"0", 10, false, nil, 10, 1, true, 0},
-	{"0", 36, false, nil, 36, 1, true, 0},
-	{"1", 0, false, nat{1}, 10, 1, true, 0},
-	{"1", 10, false, nat{1}, 10, 1, true, 0},
-	{"0 ", 0, false, nil, 10, 1, true, ' '},
-	{"08", 0, false, nil, 10, 1, true, '8'},
-	{"08", 10, false, nat{8}, 10, 2, true, 0},
-	{"018", 0, false, nat{1}, 8, 1, true, '8'},
-	{"0b1", 0, false, nat{1}, 2, 1, true, 0},
-	{"0b11000101", 0, false, nat{0xc5}, 2, 8, true, 0},
-	{"03271", 0, false, nat{03271}, 8, 4, true, 0},
-	{"10ab", 0, false, nat{10}, 10, 2, true, 'a'},
-	{"1234567890", 0, false, nat{1234567890}, 10, 10, true, 0},
-	{"xyz", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, 0},
-	{"xyz?", 36, false, nat{(33*36+34)*36 + 35}, 36, 3, true, '?'},
-	{"0x", 16, false, nil, 16, 1, true, 'x'},
-	{"0xdeadbeef", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
-	{"0XDEADBEEF", 0, false, nat{0xdeadbeef}, 16, 8, true, 0},
-
-	// no errors, decimal point
-	{"0.", 0, false, nil, 10, 1, true, '.'},
-	{"0.", 10, true, nil, 10, 0, true, 0},
-	{"0.1.2", 10, true, nat{1}, 10, -1, true, '.'},
-	{".000", 10, true, nil, 10, -3, true, 0},
-	{"12.3", 10, true, nat{123}, 10, -1, true, 0},
-	{"012.345", 10, true, nat{12345}, 10, -3, true, 0},
-}
-
-func TestScanBase(t *testing.T) {
-	for _, a := range natScanTests {
-		r := strings.NewReader(a.s)
-		x, b, count, err := nat(nil).scan(r, a.base, a.frac)
-		if err == nil && !a.ok {
-			t.Errorf("scan%+v\n\texpected error", a)
-		}
-		if err != nil {
-			if a.ok {
-				t.Errorf("scan%+v\n\tgot error = %s", a, err)
-			}
-			continue
-		}
-		if x.cmp(a.x) != 0 {
-			t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x)
-		}
-		if b != a.b {
-			t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.base)
-		}
-		if count != a.count {
-			t.Errorf("scan%+v\n\tgot count = %d; want %d", a, count, a.count)
-		}
-		next, _, err := r.ReadRune()
-		if err == io.EOF {
-			next = 0
-			err = nil
-		}
-		if err == nil && next != a.next {
-			t.Errorf("scan%+v\n\tgot next = %q; want %q", a, next, a.next)
-		}
-	}
-}
-
-var pi = "3" +
-	"14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651" +
-	"32823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461" +
-	"28475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920" +
-	"96282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179" +
-	"31051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798" +
-	"60943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901" +
-	"22495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837" +
-	"29780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083" +
-	"81420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909" +
-	"21642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" +
-	"55748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035" +
-	"63707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104" +
-	"75216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992" +
-	"45863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818" +
-	"34797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548" +
-	"16136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179" +
-	"04946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886" +
-	"26945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645" +
-	"99581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745" +
-	"53050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" +
-	"68683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244" +
-	"13654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767" +
-	"88952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288" +
-	"79710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821" +
-	"68299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610" +
-	"21359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435" +
-	"06430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675" +
-	"14269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672" +
-	"21825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539" +
-	"05796268561005508106658796998163574736384052571459102897064140110971206280439039759515677157700420337869936007" +
-	"23055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816" +
-	"90915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398" +
-	"31501970165151168517143765761835155650884909989859982387345528331635507647918535893226185489632132933089857064" +
-	"20467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325" +
-	"97463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100" +
-	"44929321516084244485963766983895228684783123552658213144957685726243344189303968642624341077322697802807318915" +
-	"44110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201" +
-	"85581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318" +
-	"58676975145661406800700237877659134401712749470420562230538994561314071127000407854733269939081454664645880797" +
-	"27082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923" +
-	"09907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111" +
-	"79042978285647503203198691514028708085990480109412147221317947647772622414254854540332157185306142288137585043" +
-	"06332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120" +
-	"91807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862" +
-	"94726547364252308177036751590673502350728354056704038674351362222477158915049530984448933309634087807693259939" +
-	"78054193414473774418426312986080998886874132604721569516239658645730216315981931951673538129741677294786724229" +
-	"24654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001" +
-	"59377647165122893578601588161755782973523344604281512627203734314653197777416031990665541876397929334419521541" +
-	"34189948544473456738316249934191318148092777710386387734317720754565453220777092120190516609628049092636019759" +
-	"88281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267" +
-	"94561275318134078330336254232783944975382437205835311477119926063813346776879695970309833913077109870408591337"
-
-// Test case for BenchmarkScanPi.
-func TestScanPi(t *testing.T) {
-	var x nat
-	z, _, _, err := x.scan(strings.NewReader(pi), 10, false)
-	if err != nil {
-		t.Errorf("scanning pi: %s", err)
-	}
-	if s := string(z.utoa(10)); s != pi {
-		t.Errorf("scanning pi: got %s", s)
-	}
-}
-
-func TestScanPiParallel(t *testing.T) {
-	const n = 2
-	c := make(chan int)
-	for i := 0; i < n; i++ {
-		go func() {
-			TestScanPi(t)
-			c <- 0
-		}()
-	}
-	for i := 0; i < n; i++ {
-		<-c
-	}
-}
-
-func BenchmarkScanPi(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		var x nat
-		x.scan(strings.NewReader(pi), 10, false)
-	}
-}
-
-func BenchmarkStringPiParallel(b *testing.B) {
-	var x nat
-	x, _, _, _ = x.scan(strings.NewReader(pi), 0, false)
-	if string(x.utoa(10)) != pi {
-		panic("benchmark incorrect: conversion failed")
-	}
-	b.RunParallel(func(pb *testing.PB) {
-		for pb.Next() {
-			x.utoa(10)
-		}
-	})
-}
-
-func BenchmarkScan10Base2(b *testing.B)     { ScanHelper(b, 2, 10, 10) }
-func BenchmarkScan100Base2(b *testing.B)    { ScanHelper(b, 2, 10, 100) }
-func BenchmarkScan1000Base2(b *testing.B)   { ScanHelper(b, 2, 10, 1000) }
-func BenchmarkScan10000Base2(b *testing.B)  { ScanHelper(b, 2, 10, 10000) }
-func BenchmarkScan100000Base2(b *testing.B) { ScanHelper(b, 2, 10, 100000) }
-
-func BenchmarkScan10Base8(b *testing.B)     { ScanHelper(b, 8, 10, 10) }
-func BenchmarkScan100Base8(b *testing.B)    { ScanHelper(b, 8, 10, 100) }
-func BenchmarkScan1000Base8(b *testing.B)   { ScanHelper(b, 8, 10, 1000) }
-func BenchmarkScan10000Base8(b *testing.B)  { ScanHelper(b, 8, 10, 10000) }
-func BenchmarkScan100000Base8(b *testing.B) { ScanHelper(b, 8, 10, 100000) }
-
-func BenchmarkScan10Base10(b *testing.B)     { ScanHelper(b, 10, 10, 10) }
-func BenchmarkScan100Base10(b *testing.B)    { ScanHelper(b, 10, 10, 100) }
-func BenchmarkScan1000Base10(b *testing.B)   { ScanHelper(b, 10, 10, 1000) }
-func BenchmarkScan10000Base10(b *testing.B)  { ScanHelper(b, 10, 10, 10000) }
-func BenchmarkScan100000Base10(b *testing.B) { ScanHelper(b, 10, 10, 100000) }
-
-func BenchmarkScan10Base16(b *testing.B)     { ScanHelper(b, 16, 10, 10) }
-func BenchmarkScan100Base16(b *testing.B)    { ScanHelper(b, 16, 10, 100) }
-func BenchmarkScan1000Base16(b *testing.B)   { ScanHelper(b, 16, 10, 1000) }
-func BenchmarkScan10000Base16(b *testing.B)  { ScanHelper(b, 16, 10, 10000) }
-func BenchmarkScan100000Base16(b *testing.B) { ScanHelper(b, 16, 10, 100000) }
-
-func ScanHelper(b *testing.B, base int, x, y Word) {
-	b.StopTimer()
-	var z nat
-	z = z.expWW(x, y)
-
-	s := z.utoa(base)
-	if t := itoa(z, base); !bytes.Equal(s, t) {
-		b.Fatalf("scanning: got %s; want %s", s, t)
-	}
-	b.StartTimer()
-
-	for i := 0; i < b.N; i++ {
-		z.scan(bytes.NewReader(s), base, false)
-	}
-}
-
-func BenchmarkString10Base2(b *testing.B)     { StringHelper(b, 2, 10, 10) }
-func BenchmarkString100Base2(b *testing.B)    { StringHelper(b, 2, 10, 100) }
-func BenchmarkString1000Base2(b *testing.B)   { StringHelper(b, 2, 10, 1000) }
-func BenchmarkString10000Base2(b *testing.B)  { StringHelper(b, 2, 10, 10000) }
-func BenchmarkString100000Base2(b *testing.B) { StringHelper(b, 2, 10, 100000) }
-
-func BenchmarkString10Base8(b *testing.B)     { StringHelper(b, 8, 10, 10) }
-func BenchmarkString100Base8(b *testing.B)    { StringHelper(b, 8, 10, 100) }
-func BenchmarkString1000Base8(b *testing.B)   { StringHelper(b, 8, 10, 1000) }
-func BenchmarkString10000Base8(b *testing.B)  { StringHelper(b, 8, 10, 10000) }
-func BenchmarkString100000Base8(b *testing.B) { StringHelper(b, 8, 10, 100000) }
-
-func BenchmarkString10Base10(b *testing.B)     { StringHelper(b, 10, 10, 10) }
-func BenchmarkString100Base10(b *testing.B)    { StringHelper(b, 10, 10, 100) }
-func BenchmarkString1000Base10(b *testing.B)   { StringHelper(b, 10, 10, 1000) }
-func BenchmarkString10000Base10(b *testing.B)  { StringHelper(b, 10, 10, 10000) }
-func BenchmarkString100000Base10(b *testing.B) { StringHelper(b, 10, 10, 100000) }
-
-func BenchmarkString10Base16(b *testing.B)     { StringHelper(b, 16, 10, 10) }
-func BenchmarkString100Base16(b *testing.B)    { StringHelper(b, 16, 10, 100) }
-func BenchmarkString1000Base16(b *testing.B)   { StringHelper(b, 16, 10, 1000) }
-func BenchmarkString10000Base16(b *testing.B)  { StringHelper(b, 16, 10, 10000) }
-func BenchmarkString100000Base16(b *testing.B) { StringHelper(b, 16, 10, 100000) }
-
-func StringHelper(b *testing.B, base int, x, y Word) {
-	b.StopTimer()
-	var z nat
-	z = z.expWW(x, y)
-	z.utoa(base) // warm divisor cache
-	b.StartTimer()
-
-	for i := 0; i < b.N; i++ {
-		_ = z.utoa(base)
-	}
-}
-
-func BenchmarkLeafSize0(b *testing.B)  { LeafSizeHelper(b, 10, 0) } // test without splitting
-func BenchmarkLeafSize1(b *testing.B)  { LeafSizeHelper(b, 10, 1) }
-func BenchmarkLeafSize2(b *testing.B)  { LeafSizeHelper(b, 10, 2) }
-func BenchmarkLeafSize3(b *testing.B)  { LeafSizeHelper(b, 10, 3) }
-func BenchmarkLeafSize4(b *testing.B)  { LeafSizeHelper(b, 10, 4) }
-func BenchmarkLeafSize5(b *testing.B)  { LeafSizeHelper(b, 10, 5) }
-func BenchmarkLeafSize6(b *testing.B)  { LeafSizeHelper(b, 10, 6) }
-func BenchmarkLeafSize7(b *testing.B)  { LeafSizeHelper(b, 10, 7) }
-func BenchmarkLeafSize8(b *testing.B)  { LeafSizeHelper(b, 10, 8) }
-func BenchmarkLeafSize9(b *testing.B)  { LeafSizeHelper(b, 10, 9) }
-func BenchmarkLeafSize10(b *testing.B) { LeafSizeHelper(b, 10, 10) }
-func BenchmarkLeafSize11(b *testing.B) { LeafSizeHelper(b, 10, 11) }
-func BenchmarkLeafSize12(b *testing.B) { LeafSizeHelper(b, 10, 12) }
-func BenchmarkLeafSize13(b *testing.B) { LeafSizeHelper(b, 10, 13) }
-func BenchmarkLeafSize14(b *testing.B) { LeafSizeHelper(b, 10, 14) }
-func BenchmarkLeafSize15(b *testing.B) { LeafSizeHelper(b, 10, 15) }
-func BenchmarkLeafSize16(b *testing.B) { LeafSizeHelper(b, 10, 16) }
-func BenchmarkLeafSize32(b *testing.B) { LeafSizeHelper(b, 10, 32) } // try some large lengths
-func BenchmarkLeafSize64(b *testing.B) { LeafSizeHelper(b, 10, 64) }
-
-func LeafSizeHelper(b *testing.B, base, size int) {
-	b.StopTimer()
-	originalLeafSize := leafSize
-	resetTable(cacheBase10.table[:])
-	leafSize = size
-	b.StartTimer()
-
-	for d := 1; d <= 10000; d *= 10 {
-		b.StopTimer()
-		var z nat
-		z = z.expWW(Word(base), Word(d)) // build target number
-		_ = z.utoa(base)                 // warm divisor cache
-		b.StartTimer()
-
-		for i := 0; i < b.N; i++ {
-			_ = z.utoa(base)
-		}
-	}
-
-	b.StopTimer()
-	resetTable(cacheBase10.table[:])
-	leafSize = originalLeafSize
-	b.StartTimer()
-}
-
-func resetTable(table []divisor) {
-	if table != nil && table[0].bbb != nil {
-		for i := 0; i < len(table); i++ {
-			table[i].bbb = nil
-			table[i].nbits = 0
-			table[i].ndigits = 0
-		}
-	}
-}
-
-func TestStringPowers(t *testing.T) {
-	var p Word
-	for b := 2; b <= 16; b++ {
-		for p = 0; p <= 512; p++ {
-			x := nat(nil).expWW(Word(b), p)
-			xs := x.utoa(b)
-			xs2 := itoa(x, b)
-			if !bytes.Equal(xs, xs2) {
-				t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2)
-			}
-		}
-		if b >= 3 && testing.Short() {
-			break
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/rat.go b/src/cmd/compile/internal/big/rat.go
deleted file mode 100644
index 56ce33d..0000000
--- a/src/cmd/compile/internal/big/rat.go
+++ /dev/null
@@ -1,510 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements multi-precision rational numbers.
-
-package big
-
-import (
-	"fmt"
-	"math"
-)
-
-// A Rat represents a quotient a/b of arbitrary precision.
-// The zero value for a Rat represents the value 0.
-type Rat struct {
-	// To make zero values for Rat work w/o initialization,
-	// a zero value of b (len(b) == 0) acts like b == 1.
-	// a.neg determines the sign of the Rat, b.neg is ignored.
-	a, b Int
-}
-
-// NewRat creates a new Rat with numerator a and denominator b.
-func NewRat(a, b int64) *Rat {
-	return new(Rat).SetFrac64(a, b)
-}
-
-// SetFloat64 sets z to exactly f and returns z.
-// If f is not finite, SetFloat returns nil.
-func (z *Rat) SetFloat64(f float64) *Rat {
-	const expMask = 1<<11 - 1
-	bits := math.Float64bits(f)
-	mantissa := bits & (1<<52 - 1)
-	exp := int((bits >> 52) & expMask)
-	switch exp {
-	case expMask: // non-finite
-		return nil
-	case 0: // denormal
-		exp -= 1022
-	default: // normal
-		mantissa |= 1 << 52
-		exp -= 1023
-	}
-
-	shift := 52 - exp
-
-	// Optimization (?): partially pre-normalise.
-	for mantissa&1 == 0 && shift > 0 {
-		mantissa >>= 1
-		shift--
-	}
-
-	z.a.SetUint64(mantissa)
-	z.a.neg = f < 0
-	z.b.Set(intOne)
-	if shift > 0 {
-		z.b.Lsh(&z.b, uint(shift))
-	} else {
-		z.a.Lsh(&z.a, uint(-shift))
-	}
-	return z.norm()
-}
-
-// quotToFloat32 returns the non-negative float32 value
-// nearest to the quotient a/b, using round-to-even in
-// halfway cases. It does not mutate its arguments.
-// Preconditions: b is non-zero; a and b have no common factors.
-func quotToFloat32(a, b nat) (f float32, exact bool) {
-	const (
-		// float size in bits
-		Fsize = 32
-
-		// mantissa
-		Msize  = 23
-		Msize1 = Msize + 1 // incl. implicit 1
-		Msize2 = Msize1 + 1
-
-		// exponent
-		Esize = Fsize - Msize1
-		Ebias = 1<<(Esize-1) - 1
-		Emin  = 1 - Ebias
-		Emax  = Ebias
-	)
-
-	// TODO(adonovan): specialize common degenerate cases: 1.0, integers.
-	alen := a.bitLen()
-	if alen == 0 {
-		return 0, true
-	}
-	blen := b.bitLen()
-	if blen == 0 {
-		panic("division by zero")
-	}
-
-	// 1. Left-shift A or B such that quotient A/B is in [1<<Msize1, 1<<(Msize2+1)
-	// (Msize2 bits if A < B when they are left-aligned, Msize2+1 bits if A >= B).
-	// This is 2 or 3 more than the float32 mantissa field width of Msize:
-	// - the optional extra bit is shifted away in step 3 below.
-	// - the high-order 1 is omitted in "normal" representation;
-	// - the low-order 1 will be used during rounding then discarded.
-	exp := alen - blen
-	var a2, b2 nat
-	a2 = a2.set(a)
-	b2 = b2.set(b)
-	if shift := Msize2 - exp; shift > 0 {
-		a2 = a2.shl(a2, uint(shift))
-	} else if shift < 0 {
-		b2 = b2.shl(b2, uint(-shift))
-	}
-
-	// 2. Compute quotient and remainder (q, r).  NB: due to the
-	// extra shift, the low-order bit of q is logically the
-	// high-order bit of r.
-	var q nat
-	q, r := q.div(a2, a2, b2) // (recycle a2)
-	mantissa := low32(q)
-	haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half
-
-	// 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1
-	// (in effect---we accomplish this incrementally).
-	if mantissa>>Msize2 == 1 {
-		if mantissa&1 == 1 {
-			haveRem = true
-		}
-		mantissa >>= 1
-		exp++
-	}
-	if mantissa>>Msize1 != 1 {
-		panic(fmt.Sprintf("expected exactly %d bits of result", Msize2))
-	}
-
-	// 4. Rounding.
-	if Emin-Msize <= exp && exp <= Emin {
-		// Denormal case; lose 'shift' bits of precision.
-		shift := uint(Emin - (exp - 1)) // [1..Esize1)
-		lostbits := mantissa & (1<<shift - 1)
-		haveRem = haveRem || lostbits != 0
-		mantissa >>= shift
-		exp = 2 - Ebias // == exp + shift
-	}
-	// Round q using round-half-to-even.
-	exact = !haveRem
-	if mantissa&1 != 0 {
-		exact = false
-		if haveRem || mantissa&2 != 0 {
-			if mantissa++; mantissa >= 1<<Msize2 {
-				// Complete rollover 11...1 => 100...0, so shift is safe
-				mantissa >>= 1
-				exp++
-			}
-		}
-	}
-	mantissa >>= 1 // discard rounding bit.  Mantissa now scaled by 1<<Msize1.
-
-	f = float32(math.Ldexp(float64(mantissa), exp-Msize1))
-	if math.IsInf(float64(f), 0) {
-		exact = false
-	}
-	return
-}
-
-// quotToFloat64 returns the non-negative float64 value
-// nearest to the quotient a/b, using round-to-even in
-// halfway cases. It does not mutate its arguments.
-// Preconditions: b is non-zero; a and b have no common factors.
-func quotToFloat64(a, b nat) (f float64, exact bool) {
-	const (
-		// float size in bits
-		Fsize = 64
-
-		// mantissa
-		Msize  = 52
-		Msize1 = Msize + 1 // incl. implicit 1
-		Msize2 = Msize1 + 1
-
-		// exponent
-		Esize = Fsize - Msize1
-		Ebias = 1<<(Esize-1) - 1
-		Emin  = 1 - Ebias
-		Emax  = Ebias
-	)
-
-	// TODO(adonovan): specialize common degenerate cases: 1.0, integers.
-	alen := a.bitLen()
-	if alen == 0 {
-		return 0, true
-	}
-	blen := b.bitLen()
-	if blen == 0 {
-		panic("division by zero")
-	}
-
-	// 1. Left-shift A or B such that quotient A/B is in [1<<Msize1, 1<<(Msize2+1)
-	// (Msize2 bits if A < B when they are left-aligned, Msize2+1 bits if A >= B).
-	// This is 2 or 3 more than the float64 mantissa field width of Msize:
-	// - the optional extra bit is shifted away in step 3 below.
-	// - the high-order 1 is omitted in "normal" representation;
-	// - the low-order 1 will be used during rounding then discarded.
-	exp := alen - blen
-	var a2, b2 nat
-	a2 = a2.set(a)
-	b2 = b2.set(b)
-	if shift := Msize2 - exp; shift > 0 {
-		a2 = a2.shl(a2, uint(shift))
-	} else if shift < 0 {
-		b2 = b2.shl(b2, uint(-shift))
-	}
-
-	// 2. Compute quotient and remainder (q, r).  NB: due to the
-	// extra shift, the low-order bit of q is logically the
-	// high-order bit of r.
-	var q nat
-	q, r := q.div(a2, a2, b2) // (recycle a2)
-	mantissa := low64(q)
-	haveRem := len(r) > 0 // mantissa&1 && !haveRem => remainder is exactly half
-
-	// 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1
-	// (in effect---we accomplish this incrementally).
-	if mantissa>>Msize2 == 1 {
-		if mantissa&1 == 1 {
-			haveRem = true
-		}
-		mantissa >>= 1
-		exp++
-	}
-	if mantissa>>Msize1 != 1 {
-		panic(fmt.Sprintf("expected exactly %d bits of result", Msize2))
-	}
-
-	// 4. Rounding.
-	if Emin-Msize <= exp && exp <= Emin {
-		// Denormal case; lose 'shift' bits of precision.
-		shift := uint(Emin - (exp - 1)) // [1..Esize1)
-		lostbits := mantissa & (1<<shift - 1)
-		haveRem = haveRem || lostbits != 0
-		mantissa >>= shift
-		exp = 2 - Ebias // == exp + shift
-	}
-	// Round q using round-half-to-even.
-	exact = !haveRem
-	if mantissa&1 != 0 {
-		exact = false
-		if haveRem || mantissa&2 != 0 {
-			if mantissa++; mantissa >= 1<<Msize2 {
-				// Complete rollover 11...1 => 100...0, so shift is safe
-				mantissa >>= 1
-				exp++
-			}
-		}
-	}
-	mantissa >>= 1 // discard rounding bit.  Mantissa now scaled by 1<<Msize1.
-
-	f = math.Ldexp(float64(mantissa), exp-Msize1)
-	if math.IsInf(f, 0) {
-		exact = false
-	}
-	return
-}
-
-// Float32 returns the nearest float32 value for x and a bool indicating
-// whether f represents x exactly. If the magnitude of x is too large to
-// be represented by a float32, f is an infinity and exact is false.
-// The sign of f always matches the sign of x, even if f == 0.
-func (x *Rat) Float32() (f float32, exact bool) {
-	b := x.b.abs
-	if len(b) == 0 {
-		b = b.set(natOne) // materialize denominator
-	}
-	f, exact = quotToFloat32(x.a.abs, b)
-	if x.a.neg {
-		f = -f
-	}
-	return
-}
-
-// Float64 returns the nearest float64 value for x and a bool indicating
-// whether f represents x exactly. If the magnitude of x is too large to
-// be represented by a float64, f is an infinity and exact is false.
-// The sign of f always matches the sign of x, even if f == 0.
-func (x *Rat) Float64() (f float64, exact bool) {
-	b := x.b.abs
-	if len(b) == 0 {
-		b = b.set(natOne) // materialize denominator
-	}
-	f, exact = quotToFloat64(x.a.abs, b)
-	if x.a.neg {
-		f = -f
-	}
-	return
-}
-
-// SetFrac sets z to a/b and returns z.
-func (z *Rat) SetFrac(a, b *Int) *Rat {
-	z.a.neg = a.neg != b.neg
-	babs := b.abs
-	if len(babs) == 0 {
-		panic("division by zero")
-	}
-	if &z.a == b || alias(z.a.abs, babs) {
-		babs = nat(nil).set(babs) // make a copy
-	}
-	z.a.abs = z.a.abs.set(a.abs)
-	z.b.abs = z.b.abs.set(babs)
-	return z.norm()
-}
-
-// SetFrac64 sets z to a/b and returns z.
-func (z *Rat) SetFrac64(a, b int64) *Rat {
-	z.a.SetInt64(a)
-	if b == 0 {
-		panic("division by zero")
-	}
-	if b < 0 {
-		b = -b
-		z.a.neg = !z.a.neg
-	}
-	z.b.abs = z.b.abs.setUint64(uint64(b))
-	return z.norm()
-}
-
-// SetInt sets z to x (by making a copy of x) and returns z.
-func (z *Rat) SetInt(x *Int) *Rat {
-	z.a.Set(x)
-	z.b.abs = z.b.abs[:0]
-	return z
-}
-
-// SetInt64 sets z to x and returns z.
-func (z *Rat) SetInt64(x int64) *Rat {
-	z.a.SetInt64(x)
-	z.b.abs = z.b.abs[:0]
-	return z
-}
-
-// Set sets z to x (by making a copy of x) and returns z.
-func (z *Rat) Set(x *Rat) *Rat {
-	if z != x {
-		z.a.Set(&x.a)
-		z.b.Set(&x.b)
-	}
-	return z
-}
-
-// Abs sets z to |x| (the absolute value of x) and returns z.
-func (z *Rat) Abs(x *Rat) *Rat {
-	z.Set(x)
-	z.a.neg = false
-	return z
-}
-
-// Neg sets z to -x and returns z.
-func (z *Rat) Neg(x *Rat) *Rat {
-	z.Set(x)
-	z.a.neg = len(z.a.abs) > 0 && !z.a.neg // 0 has no sign
-	return z
-}
-
-// Inv sets z to 1/x and returns z.
-func (z *Rat) Inv(x *Rat) *Rat {
-	if len(x.a.abs) == 0 {
-		panic("division by zero")
-	}
-	z.Set(x)
-	a := z.b.abs
-	if len(a) == 0 {
-		a = a.set(natOne) // materialize numerator
-	}
-	b := z.a.abs
-	if b.cmp(natOne) == 0 {
-		b = b[:0] // normalize denominator
-	}
-	z.a.abs, z.b.abs = a, b // sign doesn't change
-	return z
-}
-
-// Sign returns:
-//
-//	-1 if x <  0
-//	 0 if x == 0
-//	+1 if x >  0
-//
-func (x *Rat) Sign() int {
-	return x.a.Sign()
-}
-
-// IsInt reports whether the denominator of x is 1.
-func (x *Rat) IsInt() bool {
-	return len(x.b.abs) == 0 || x.b.abs.cmp(natOne) == 0
-}
-
-// Num returns the numerator of x; it may be <= 0.
-// The result is a reference to x's numerator; it
-// may change if a new value is assigned to x, and vice versa.
-// The sign of the numerator corresponds to the sign of x.
-func (x *Rat) Num() *Int {
-	return &x.a
-}
-
-// Denom returns the denominator of x; it is always > 0.
-// The result is a reference to x's denominator; it
-// may change if a new value is assigned to x, and vice versa.
-func (x *Rat) Denom() *Int {
-	x.b.neg = false // the result is always >= 0
-	if len(x.b.abs) == 0 {
-		x.b.abs = x.b.abs.set(natOne) // materialize denominator
-	}
-	return &x.b
-}
-
-func (z *Rat) norm() *Rat {
-	switch {
-	case len(z.a.abs) == 0:
-		// z == 0 - normalize sign and denominator
-		z.a.neg = false
-		z.b.abs = z.b.abs[:0]
-	case len(z.b.abs) == 0:
-		// z is normalized int - nothing to do
-	case z.b.abs.cmp(natOne) == 0:
-		// z is int - normalize denominator
-		z.b.abs = z.b.abs[:0]
-	default:
-		neg := z.a.neg
-		z.a.neg = false
-		z.b.neg = false
-		if f := NewInt(0).binaryGCD(&z.a, &z.b); f.Cmp(intOne) != 0 {
-			z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs)
-			z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs)
-			if z.b.abs.cmp(natOne) == 0 {
-				// z is int - normalize denominator
-				z.b.abs = z.b.abs[:0]
-			}
-		}
-		z.a.neg = neg
-	}
-	return z
-}
-
-// mulDenom sets z to the denominator product x*y (by taking into
-// account that 0 values for x or y must be interpreted as 1) and
-// returns z.
-func mulDenom(z, x, y nat) nat {
-	switch {
-	case len(x) == 0:
-		return z.set(y)
-	case len(y) == 0:
-		return z.set(x)
-	}
-	return z.mul(x, y)
-}
-
-// scaleDenom computes x*f.
-// If f == 0 (zero value of denominator), the result is (a copy of) x.
-func scaleDenom(x *Int, f nat) *Int {
-	var z Int
-	if len(f) == 0 {
-		return z.Set(x)
-	}
-	z.abs = z.abs.mul(x.abs, f)
-	z.neg = x.neg
-	return &z
-}
-
-// Cmp compares x and y and returns:
-//
-//   -1 if x <  y
-//    0 if x == y
-//   +1 if x >  y
-//
-func (x *Rat) Cmp(y *Rat) int {
-	return scaleDenom(&x.a, y.b.abs).Cmp(scaleDenom(&y.a, x.b.abs))
-}
-
-// Add sets z to the sum x+y and returns z.
-func (z *Rat) Add(x, y *Rat) *Rat {
-	a1 := scaleDenom(&x.a, y.b.abs)
-	a2 := scaleDenom(&y.a, x.b.abs)
-	z.a.Add(a1, a2)
-	z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs)
-	return z.norm()
-}
-
-// Sub sets z to the difference x-y and returns z.
-func (z *Rat) Sub(x, y *Rat) *Rat {
-	a1 := scaleDenom(&x.a, y.b.abs)
-	a2 := scaleDenom(&y.a, x.b.abs)
-	z.a.Sub(a1, a2)
-	z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs)
-	return z.norm()
-}
-
-// Mul sets z to the product x*y and returns z.
-func (z *Rat) Mul(x, y *Rat) *Rat {
-	z.a.Mul(&x.a, &y.a)
-	z.b.abs = mulDenom(z.b.abs, x.b.abs, y.b.abs)
-	return z.norm()
-}
-
-// Quo sets z to the quotient x/y and returns z.
-// If y == 0, a division-by-zero run-time panic occurs.
-func (z *Rat) Quo(x, y *Rat) *Rat {
-	if len(y.a.abs) == 0 {
-		panic("division by zero")
-	}
-	a := scaleDenom(&x.a, y.b.abs)
-	b := scaleDenom(&y.a, x.b.abs)
-	z.a.abs = a.abs
-	z.b.abs = b.abs
-	z.a.neg = a.neg != b.neg
-	return z.norm()
-}
diff --git a/src/cmd/compile/internal/big/rat_test.go b/src/cmd/compile/internal/big/rat_test.go
deleted file mode 100644
index 3a06fca..0000000
--- a/src/cmd/compile/internal/big/rat_test.go
+++ /dev/null
@@ -1,622 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"math"
-	"testing"
-)
-
-func TestZeroRat(t *testing.T) {
-	var x, y, z Rat
-	y.SetFrac64(0, 42)
-
-	if x.Cmp(&y) != 0 {
-		t.Errorf("x and y should be both equal and zero")
-	}
-
-	if s := x.String(); s != "0/1" {
-		t.Errorf("got x = %s, want 0/1", s)
-	}
-
-	if s := x.RatString(); s != "0" {
-		t.Errorf("got x = %s, want 0", s)
-	}
-
-	z.Add(&x, &y)
-	if s := z.RatString(); s != "0" {
-		t.Errorf("got x+y = %s, want 0", s)
-	}
-
-	z.Sub(&x, &y)
-	if s := z.RatString(); s != "0" {
-		t.Errorf("got x-y = %s, want 0", s)
-	}
-
-	z.Mul(&x, &y)
-	if s := z.RatString(); s != "0" {
-		t.Errorf("got x*y = %s, want 0", s)
-	}
-
-	// check for division by zero
-	defer func() {
-		if s := recover(); s == nil || s.(string) != "division by zero" {
-			panic(s)
-		}
-	}()
-	z.Quo(&x, &y)
-}
-
-func TestRatSign(t *testing.T) {
-	zero := NewRat(0, 1)
-	for _, a := range setStringTests {
-		x, ok := new(Rat).SetString(a.in)
-		if !ok {
-			continue
-		}
-		s := x.Sign()
-		e := x.Cmp(zero)
-		if s != e {
-			t.Errorf("got %d; want %d for z = %v", s, e, &x)
-		}
-	}
-}
-
-var ratCmpTests = []struct {
-	rat1, rat2 string
-	out        int
-}{
-	{"0", "0/1", 0},
-	{"1/1", "1", 0},
-	{"-1", "-2/2", 0},
-	{"1", "0", 1},
-	{"0/1", "1/1", -1},
-	{"-5/1434770811533343057144", "-5/1434770811533343057145", -1},
-	{"49832350382626108453/8964749413", "49832350382626108454/8964749413", -1},
-	{"-37414950961700930/7204075375675961", "37414950961700930/7204075375675961", -1},
-	{"37414950961700930/7204075375675961", "74829901923401860/14408150751351922", 0},
-}
-
-func TestRatCmp(t *testing.T) {
-	for i, test := range ratCmpTests {
-		x, _ := new(Rat).SetString(test.rat1)
-		y, _ := new(Rat).SetString(test.rat2)
-
-		out := x.Cmp(y)
-		if out != test.out {
-			t.Errorf("#%d got out = %v; want %v", i, out, test.out)
-		}
-	}
-}
-
-func TestIsInt(t *testing.T) {
-	one := NewInt(1)
-	for _, a := range setStringTests {
-		x, ok := new(Rat).SetString(a.in)
-		if !ok {
-			continue
-		}
-		i := x.IsInt()
-		e := x.Denom().Cmp(one) == 0
-		if i != e {
-			t.Errorf("got IsInt(%v) == %v; want %v", x, i, e)
-		}
-	}
-}
-
-func TestRatAbs(t *testing.T) {
-	zero := new(Rat)
-	for _, a := range setStringTests {
-		x, ok := new(Rat).SetString(a.in)
-		if !ok {
-			continue
-		}
-		e := new(Rat).Set(x)
-		if e.Cmp(zero) < 0 {
-			e.Sub(zero, e)
-		}
-		z := new(Rat).Abs(x)
-		if z.Cmp(e) != 0 {
-			t.Errorf("got Abs(%v) = %v; want %v", x, z, e)
-		}
-	}
-}
-
-func TestRatNeg(t *testing.T) {
-	zero := new(Rat)
-	for _, a := range setStringTests {
-		x, ok := new(Rat).SetString(a.in)
-		if !ok {
-			continue
-		}
-		e := new(Rat).Sub(zero, x)
-		z := new(Rat).Neg(x)
-		if z.Cmp(e) != 0 {
-			t.Errorf("got Neg(%v) = %v; want %v", x, z, e)
-		}
-	}
-}
-
-func TestRatInv(t *testing.T) {
-	zero := new(Rat)
-	for _, a := range setStringTests {
-		x, ok := new(Rat).SetString(a.in)
-		if !ok {
-			continue
-		}
-		if x.Cmp(zero) == 0 {
-			continue // avoid division by zero
-		}
-		e := new(Rat).SetFrac(x.Denom(), x.Num())
-		z := new(Rat).Inv(x)
-		if z.Cmp(e) != 0 {
-			t.Errorf("got Inv(%v) = %v; want %v", x, z, e)
-		}
-	}
-}
-
-type ratBinFun func(z, x, y *Rat) *Rat
-type ratBinArg struct {
-	x, y, z string
-}
-
-func testRatBin(t *testing.T, i int, name string, f ratBinFun, a ratBinArg) {
-	x, _ := new(Rat).SetString(a.x)
-	y, _ := new(Rat).SetString(a.y)
-	z, _ := new(Rat).SetString(a.z)
-	out := f(new(Rat), x, y)
-
-	if out.Cmp(z) != 0 {
-		t.Errorf("%s #%d got %s want %s", name, i, out, z)
-	}
-}
-
-var ratBinTests = []struct {
-	x, y      string
-	sum, prod string
-}{
-	{"0", "0", "0", "0"},
-	{"0", "1", "1", "0"},
-	{"-1", "0", "-1", "0"},
-	{"-1", "1", "0", "-1"},
-	{"1", "1", "2", "1"},
-	{"1/2", "1/2", "1", "1/4"},
-	{"1/4", "1/3", "7/12", "1/12"},
-	{"2/5", "-14/3", "-64/15", "-28/15"},
-	{"4707/49292519774798173060", "-3367/70976135186689855734", "84058377121001851123459/1749296273614329067191168098769082663020", "-1760941/388732505247628681598037355282018369560"},
-	{"-61204110018146728334/3", "-31052192278051565633/2", "-215564796870448153567/6", "950260896245257153059642991192710872711/3"},
-	{"-854857841473707320655/4237645934602118692642972629634714039", "-18/31750379913563777419", "-27/133467566250814981", "15387441146526731771790/134546868362786310073779084329032722548987800600710485341"},
-	{"618575745270541348005638912139/19198433543745179392300736", "-19948846211000086/637313996471", "27674141753240653/30123979153216", "-6169936206128396568797607742807090270137721977/6117715203873571641674006593837351328"},
-	{"-3/26206484091896184128", "5/2848423294177090248", "15310893822118706237/9330894968229805033368778458685147968", "-5/24882386581946146755650075889827061248"},
-	{"26946729/330400702820", "41563965/225583428284", "1238218672302860271/4658307703098666660055", "224002580204097/14906584649915733312176"},
-	{"-8259900599013409474/7", "-84829337473700364773/56707961321161574960", "-468402123685491748914621885145127724451/396955729248131024720", "350340947706464153265156004876107029701/198477864624065512360"},
-	{"575775209696864/1320203974639986246357", "29/712593081308", "410331716733912717985762465/940768218243776489278275419794956", "808/45524274987585732633"},
-	{"1786597389946320496771/2066653520653241", "6269770/1992362624741777", "3559549865190272133656109052308126637/4117523232840525481453983149257", "8967230/3296219033"},
-	{"-36459180403360509753/32150500941194292113930", "9381566963714/9633539", "301622077145533298008420642898530153/309723104686531919656937098270", "-3784609207827/3426986245"},
-}
-
-func TestRatBin(t *testing.T) {
-	for i, test := range ratBinTests {
-		arg := ratBinArg{test.x, test.y, test.sum}
-		testRatBin(t, i, "Add", (*Rat).Add, arg)
-
-		arg = ratBinArg{test.y, test.x, test.sum}
-		testRatBin(t, i, "Add symmetric", (*Rat).Add, arg)
-
-		arg = ratBinArg{test.sum, test.x, test.y}
-		testRatBin(t, i, "Sub", (*Rat).Sub, arg)
-
-		arg = ratBinArg{test.sum, test.y, test.x}
-		testRatBin(t, i, "Sub symmetric", (*Rat).Sub, arg)
-
-		arg = ratBinArg{test.x, test.y, test.prod}
-		testRatBin(t, i, "Mul", (*Rat).Mul, arg)
-
-		arg = ratBinArg{test.y, test.x, test.prod}
-		testRatBin(t, i, "Mul symmetric", (*Rat).Mul, arg)
-
-		if test.x != "0" {
-			arg = ratBinArg{test.prod, test.x, test.y}
-			testRatBin(t, i, "Quo", (*Rat).Quo, arg)
-		}
-
-		if test.y != "0" {
-			arg = ratBinArg{test.prod, test.y, test.x}
-			testRatBin(t, i, "Quo symmetric", (*Rat).Quo, arg)
-		}
-	}
-}
-
-func TestIssue820(t *testing.T) {
-	x := NewRat(3, 1)
-	y := NewRat(2, 1)
-	z := y.Quo(x, y)
-	q := NewRat(3, 2)
-	if z.Cmp(q) != 0 {
-		t.Errorf("got %s want %s", z, q)
-	}
-
-	y = NewRat(3, 1)
-	x = NewRat(2, 1)
-	z = y.Quo(x, y)
-	q = NewRat(2, 3)
-	if z.Cmp(q) != 0 {
-		t.Errorf("got %s want %s", z, q)
-	}
-
-	x = NewRat(3, 1)
-	z = x.Quo(x, x)
-	q = NewRat(3, 3)
-	if z.Cmp(q) != 0 {
-		t.Errorf("got %s want %s", z, q)
-	}
-}
-
-var setFrac64Tests = []struct {
-	a, b int64
-	out  string
-}{
-	{0, 1, "0"},
-	{0, -1, "0"},
-	{1, 1, "1"},
-	{-1, 1, "-1"},
-	{1, -1, "-1"},
-	{-1, -1, "1"},
-	{-9223372036854775808, -9223372036854775808, "1"},
-}
-
-func TestRatSetFrac64Rat(t *testing.T) {
-	for i, test := range setFrac64Tests {
-		x := new(Rat).SetFrac64(test.a, test.b)
-		if x.RatString() != test.out {
-			t.Errorf("#%d got %s want %s", i, x.RatString(), test.out)
-		}
-	}
-}
-
-func TestIssue2379(t *testing.T) {
-	// 1) no aliasing
-	q := NewRat(3, 2)
-	x := new(Rat)
-	x.SetFrac(NewInt(3), NewInt(2))
-	if x.Cmp(q) != 0 {
-		t.Errorf("1) got %s want %s", x, q)
-	}
-
-	// 2) aliasing of numerator
-	x = NewRat(2, 3)
-	x.SetFrac(NewInt(3), x.Num())
-	if x.Cmp(q) != 0 {
-		t.Errorf("2) got %s want %s", x, q)
-	}
-
-	// 3) aliasing of denominator
-	x = NewRat(2, 3)
-	x.SetFrac(x.Denom(), NewInt(2))
-	if x.Cmp(q) != 0 {
-		t.Errorf("3) got %s want %s", x, q)
-	}
-
-	// 4) aliasing of numerator and denominator
-	x = NewRat(2, 3)
-	x.SetFrac(x.Denom(), x.Num())
-	if x.Cmp(q) != 0 {
-		t.Errorf("4) got %s want %s", x, q)
-	}
-
-	// 5) numerator and denominator are the same
-	q = NewRat(1, 1)
-	x = new(Rat)
-	n := NewInt(7)
-	x.SetFrac(n, n)
-	if x.Cmp(q) != 0 {
-		t.Errorf("5) got %s want %s", x, q)
-	}
-}
-
-func TestIssue3521(t *testing.T) {
-	a := new(Int)
-	b := new(Int)
-	a.SetString("64375784358435883458348587", 0)
-	b.SetString("4789759874531", 0)
-
-	// 0) a raw zero value has 1 as denominator
-	zero := new(Rat)
-	one := NewInt(1)
-	if zero.Denom().Cmp(one) != 0 {
-		t.Errorf("0) got %s want %s", zero.Denom(), one)
-	}
-
-	// 1a) a zero value remains zero independent of denominator
-	x := new(Rat)
-	x.Denom().Set(new(Int).Neg(b))
-	if x.Cmp(zero) != 0 {
-		t.Errorf("1a) got %s want %s", x, zero)
-	}
-
-	// 1b) a zero value may have a denominator != 0 and != 1
-	x.Num().Set(a)
-	qab := new(Rat).SetFrac(a, b)
-	if x.Cmp(qab) != 0 {
-		t.Errorf("1b) got %s want %s", x, qab)
-	}
-
-	// 2a) an integral value becomes a fraction depending on denominator
-	x.SetFrac64(10, 2)
-	x.Denom().SetInt64(3)
-	q53 := NewRat(5, 3)
-	if x.Cmp(q53) != 0 {
-		t.Errorf("2a) got %s want %s", x, q53)
-	}
-
-	// 2b) an integral value becomes a fraction depending on denominator
-	x = NewRat(10, 2)
-	x.Denom().SetInt64(3)
-	if x.Cmp(q53) != 0 {
-		t.Errorf("2b) got %s want %s", x, q53)
-	}
-
-	// 3) changing the numerator/denominator of a Rat changes the Rat
-	x.SetFrac(a, b)
-	a = x.Num()
-	b = x.Denom()
-	a.SetInt64(5)
-	b.SetInt64(3)
-	if x.Cmp(q53) != 0 {
-		t.Errorf("3) got %s want %s", x, q53)
-	}
-}
-
-func TestFloat32Distribution(t *testing.T) {
-	// Generate a distribution of (sign, mantissa, exp) values
-	// broader than the float32 range, and check Rat.Float32()
-	// always picks the closest float32 approximation.
-	var add = []int64{
-		0,
-		1,
-		3,
-		5,
-		7,
-		9,
-		11,
-	}
-	var winc, einc = uint64(1), 1 // soak test (~1.5s on x86-64)
-	if testing.Short() {
-		winc, einc = 5, 15 // quick test (~60ms on x86-64)
-	}
-
-	for _, sign := range "+-" {
-		for _, a := range add {
-			for wid := uint64(0); wid < 30; wid += winc {
-				b := 1<<wid + a
-				if sign == '-' {
-					b = -b
-				}
-				for exp := -150; exp < 150; exp += einc {
-					num, den := NewInt(b), NewInt(1)
-					if exp > 0 {
-						num.Lsh(num, uint(exp))
-					} else {
-						den.Lsh(den, uint(-exp))
-					}
-					r := new(Rat).SetFrac(num, den)
-					f, _ := r.Float32()
-
-					if !checkIsBestApprox32(t, f, r) {
-						// Append context information.
-						t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)",
-							b, exp, f, f, math.Ldexp(float64(b), exp), r)
-					}
-
-					checkNonLossyRoundtrip32(t, f)
-				}
-			}
-		}
-	}
-}
-
-func TestFloat64Distribution(t *testing.T) {
-	// Generate a distribution of (sign, mantissa, exp) values
-	// broader than the float64 range, and check Rat.Float64()
-	// always picks the closest float64 approximation.
-	var add = []int64{
-		0,
-		1,
-		3,
-		5,
-		7,
-		9,
-		11,
-	}
-	var winc, einc = uint64(1), 1 // soak test (~75s on x86-64)
-	if testing.Short() {
-		winc, einc = 10, 500 // quick test (~12ms on x86-64)
-	}
-
-	for _, sign := range "+-" {
-		for _, a := range add {
-			for wid := uint64(0); wid < 60; wid += winc {
-				b := 1<<wid + a
-				if sign == '-' {
-					b = -b
-				}
-				for exp := -1100; exp < 1100; exp += einc {
-					num, den := NewInt(b), NewInt(1)
-					if exp > 0 {
-						num.Lsh(num, uint(exp))
-					} else {
-						den.Lsh(den, uint(-exp))
-					}
-					r := new(Rat).SetFrac(num, den)
-					f, _ := r.Float64()
-
-					if !checkIsBestApprox64(t, f, r) {
-						// Append context information.
-						t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)",
-							b, exp, f, f, math.Ldexp(float64(b), exp), r)
-					}
-
-					checkNonLossyRoundtrip64(t, f)
-				}
-			}
-		}
-	}
-}
-
-// TestSetFloat64NonFinite checks that SetFloat64 of a non-finite value
-// returns nil.
-func TestSetFloat64NonFinite(t *testing.T) {
-	for _, f := range []float64{math.NaN(), math.Inf(+1), math.Inf(-1)} {
-		var r Rat
-		if r2 := r.SetFloat64(f); r2 != nil {
-			t.Errorf("SetFloat64(%g) was %v, want nil", f, r2)
-		}
-	}
-}
-
-// checkNonLossyRoundtrip32 checks that a float->Rat->float roundtrip is
-// non-lossy for finite f.
-func checkNonLossyRoundtrip32(t *testing.T, f float32) {
-	if !isFinite(float64(f)) {
-		return
-	}
-	r := new(Rat).SetFloat64(float64(f))
-	if r == nil {
-		t.Errorf("Rat.SetFloat64(float64(%g) (%b)) == nil", f, f)
-		return
-	}
-	f2, exact := r.Float32()
-	if f != f2 || !exact {
-		t.Errorf("Rat.SetFloat64(float64(%g)).Float32() = %g (%b), %v, want %g (%b), %v; delta = %b",
-			f, f2, f2, exact, f, f, true, f2-f)
-	}
-}
-
-// checkNonLossyRoundtrip64 checks that a float->Rat->float roundtrip is
-// non-lossy for finite f.
-func checkNonLossyRoundtrip64(t *testing.T, f float64) {
-	if !isFinite(f) {
-		return
-	}
-	r := new(Rat).SetFloat64(f)
-	if r == nil {
-		t.Errorf("Rat.SetFloat64(%g (%b)) == nil", f, f)
-		return
-	}
-	f2, exact := r.Float64()
-	if f != f2 || !exact {
-		t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta = %b",
-			f, f2, f2, exact, f, f, true, f2-f)
-	}
-}
-
-// delta returns the absolute difference between r and f.
-func delta(r *Rat, f float64) *Rat {
-	d := new(Rat).Sub(r, new(Rat).SetFloat64(f))
-	return d.Abs(d)
-}
-
-// checkIsBestApprox32 checks that f is the best possible float32
-// approximation of r.
-// Returns true on success.
-func checkIsBestApprox32(t *testing.T, f float32, r *Rat) bool {
-	if math.Abs(float64(f)) >= math.MaxFloat32 {
-		// Cannot check +Inf, -Inf, nor the float next to them (MaxFloat32).
-		// But we have tests for these special cases.
-		return true
-	}
-
-	// r must be strictly between f0 and f1, the floats bracketing f.
-	f0 := math.Nextafter32(f, float32(math.Inf(-1)))
-	f1 := math.Nextafter32(f, float32(math.Inf(+1)))
-
-	// For f to be correct, r must be closer to f than to f0 or f1.
-	df := delta(r, float64(f))
-	df0 := delta(r, float64(f0))
-	df1 := delta(r, float64(f1))
-	if df.Cmp(df0) > 0 {
-		t.Errorf("Rat(%v).Float32() = %g (%b), but previous float32 %g (%b) is closer", r, f, f, f0, f0)
-		return false
-	}
-	if df.Cmp(df1) > 0 {
-		t.Errorf("Rat(%v).Float32() = %g (%b), but next float32 %g (%b) is closer", r, f, f, f1, f1)
-		return false
-	}
-	if df.Cmp(df0) == 0 && !isEven32(f) {
-		t.Errorf("Rat(%v).Float32() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0)
-		return false
-	}
-	if df.Cmp(df1) == 0 && !isEven32(f) {
-		t.Errorf("Rat(%v).Float32() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1)
-		return false
-	}
-	return true
-}
-
-// checkIsBestApprox64 checks that f is the best possible float64
-// approximation of r.
-// Returns true on success.
-func checkIsBestApprox64(t *testing.T, f float64, r *Rat) bool {
-	if math.Abs(f) >= math.MaxFloat64 {
-		// Cannot check +Inf, -Inf, nor the float next to them (MaxFloat64).
-		// But we have tests for these special cases.
-		return true
-	}
-
-	// r must be strictly between f0 and f1, the floats bracketing f.
-	f0 := math.Nextafter(f, math.Inf(-1))
-	f1 := math.Nextafter(f, math.Inf(+1))
-
-	// For f to be correct, r must be closer to f than to f0 or f1.
-	df := delta(r, f)
-	df0 := delta(r, f0)
-	df1 := delta(r, f1)
-	if df.Cmp(df0) > 0 {
-		t.Errorf("Rat(%v).Float64() = %g (%b), but previous float64 %g (%b) is closer", r, f, f, f0, f0)
-		return false
-	}
-	if df.Cmp(df1) > 0 {
-		t.Errorf("Rat(%v).Float64() = %g (%b), but next float64 %g (%b) is closer", r, f, f, f1, f1)
-		return false
-	}
-	if df.Cmp(df0) == 0 && !isEven64(f) {
-		t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0)
-		return false
-	}
-	if df.Cmp(df1) == 0 && !isEven64(f) {
-		t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1)
-		return false
-	}
-	return true
-}
-
-func isEven32(f float32) bool { return math.Float32bits(f)&1 == 0 }
-func isEven64(f float64) bool { return math.Float64bits(f)&1 == 0 }
-
-func TestIsFinite(t *testing.T) {
-	finites := []float64{
-		1.0 / 3,
-		4891559871276714924261e+222,
-		math.MaxFloat64,
-		math.SmallestNonzeroFloat64,
-		-math.MaxFloat64,
-		-math.SmallestNonzeroFloat64,
-	}
-	for _, f := range finites {
-		if !isFinite(f) {
-			t.Errorf("!IsFinite(%g (%b))", f, f)
-		}
-	}
-	nonfinites := []float64{
-		math.NaN(),
-		math.Inf(-1),
-		math.Inf(+1),
-	}
-	for _, f := range nonfinites {
-		if isFinite(f) {
-			t.Errorf("IsFinite(%g, (%b))", f, f)
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/ratconv.go b/src/cmd/compile/internal/big/ratconv.go
deleted file mode 100644
index 57df124..0000000
--- a/src/cmd/compile/internal/big/ratconv.go
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements rat-to-string conversion functions.
-
-package big
-
-import (
-	"errors"
-	"fmt"
-	"io"
-	"strconv"
-	"strings"
-)
-
-func ratTok(ch rune) bool {
-	return strings.ContainsRune("+-/0123456789.eE", ch)
-}
-
-// Scan is a support routine for fmt.Scanner. It accepts the formats
-// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent.
-func (z *Rat) Scan(s fmt.ScanState, ch rune) error {
-	tok, err := s.Token(true, ratTok)
-	if err != nil {
-		return err
-	}
-	if !strings.ContainsRune("efgEFGv", ch) {
-		return errors.New("Rat.Scan: invalid verb")
-	}
-	if _, ok := z.SetString(string(tok)); !ok {
-		return errors.New("Rat.Scan: invalid syntax")
-	}
-	return nil
-}
-
-// SetString sets z to the value of s and returns z and a boolean indicating
-// success. s can be given as a fraction "a/b" or as a floating-point number
-// optionally followed by an exponent. If the operation failed, the value of
-// z is undefined but the returned value is nil.
-func (z *Rat) SetString(s string) (*Rat, bool) {
-	if len(s) == 0 {
-		return nil, false
-	}
-	// len(s) > 0
-
-	// parse fraction a/b, if any
-	if sep := strings.Index(s, "/"); sep >= 0 {
-		if _, ok := z.a.SetString(s[:sep], 0); !ok {
-			return nil, false
-		}
-		s = s[sep+1:]
-		var err error
-		if z.b.abs, _, _, err = z.b.abs.scan(strings.NewReader(s), 0, false); err != nil {
-			return nil, false
-		}
-		if len(z.b.abs) == 0 {
-			return nil, false
-		}
-		return z.norm(), true
-	}
-
-	// parse floating-point number
-	r := strings.NewReader(s)
-
-	// sign
-	neg, err := scanSign(r)
-	if err != nil {
-		return nil, false
-	}
-
-	// mantissa
-	var ecorr int
-	z.a.abs, _, ecorr, err = z.a.abs.scan(r, 10, true)
-	if err != nil {
-		return nil, false
-	}
-
-	// exponent
-	var exp int64
-	exp, _, err = scanExponent(r, false)
-	if err != nil {
-		return nil, false
-	}
-
-	// there should be no unread characters left
-	if _, err = r.ReadByte(); err != io.EOF {
-		return nil, false
-	}
-
-	// correct exponent
-	if ecorr < 0 {
-		exp += int64(ecorr)
-	}
-
-	// compute exponent power
-	expabs := exp
-	if expabs < 0 {
-		expabs = -expabs
-	}
-	powTen := nat(nil).expNN(natTen, nat(nil).setWord(Word(expabs)), nil)
-
-	// complete fraction
-	if exp < 0 {
-		z.b.abs = powTen
-		z.norm()
-	} else {
-		z.a.abs = z.a.abs.mul(z.a.abs, powTen)
-		z.b.abs = z.b.abs[:0]
-	}
-
-	z.a.neg = neg && len(z.a.abs) > 0 // 0 has no sign
-
-	return z, true
-}
-
-// scanExponent scans the longest possible prefix of r representing a decimal
-// ('e', 'E') or binary ('p') exponent, if any. It returns the exponent, the
-// exponent base (10 or 2), or a read or syntax error, if any.
-//
-//	exponent = ( "E" | "e" | "p" ) [ sign ] digits .
-//	sign     = "+" | "-" .
-//	digits   = digit { digit } .
-//	digit    = "0" ... "9" .
-//
-// A binary exponent is only permitted if binExpOk is set.
-func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err error) {
-	base = 10
-
-	var ch byte
-	if ch, err = r.ReadByte(); err != nil {
-		if err == io.EOF {
-			err = nil // no exponent; same as e0
-		}
-		return
-	}
-
-	switch ch {
-	case 'e', 'E':
-		// ok
-	case 'p':
-		if binExpOk {
-			base = 2
-			break // ok
-		}
-		fallthrough // binary exponent not permitted
-	default:
-		r.UnreadByte()
-		return // no exponent; same as e0
-	}
-
-	var neg bool
-	if neg, err = scanSign(r); err != nil {
-		return
-	}
-
-	var digits []byte
-	if neg {
-		digits = append(digits, '-')
-	}
-
-	// no need to use nat.scan for exponent digits
-	// since we only care about int64 values - the
-	// from-scratch scan is easy enough and faster
-	for i := 0; ; i++ {
-		if ch, err = r.ReadByte(); err != nil {
-			if err != io.EOF || i == 0 {
-				return
-			}
-			err = nil
-			break // i > 0
-		}
-		if ch < '0' || '9' < ch {
-			if i == 0 {
-				r.UnreadByte()
-				err = fmt.Errorf("invalid exponent (missing digits)")
-				return
-			}
-			break // i > 0
-		}
-		digits = append(digits, byte(ch))
-	}
-	// i > 0 => we have at least one digit
-
-	exp, err = strconv.ParseInt(string(digits), 10, 64)
-	return
-}
-
-// String returns a string representation of x in the form "a/b" (even if b == 1).
-func (x *Rat) String() string {
-	var buf []byte
-	buf = x.a.Append(buf, 10)
-	buf = append(buf, '/')
-	if len(x.b.abs) != 0 {
-		buf = x.b.Append(buf, 10)
-	} else {
-		buf = append(buf, '1')
-	}
-	return string(buf)
-}
-
-// RatString returns a string representation of x in the form "a/b" if b != 1,
-// and in the form "a" if b == 1.
-func (x *Rat) RatString() string {
-	if x.IsInt() {
-		return x.a.String()
-	}
-	return x.String()
-}
-
-// FloatString returns a string representation of x in decimal form with prec
-// digits of precision after the decimal point. The last digit is rounded to
-// nearest, with halves rounded away from zero.
-func (x *Rat) FloatString(prec int) string {
-	var buf []byte
-
-	if x.IsInt() {
-		buf = x.a.Append(buf, 10)
-		if prec > 0 {
-			buf = append(buf, '.')
-			for i := prec; i > 0; i-- {
-				buf = append(buf, '0')
-			}
-		}
-		return string(buf)
-	}
-	// x.b.abs != 0
-
-	q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs)
-
-	p := natOne
-	if prec > 0 {
-		p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil)
-	}
-
-	r = r.mul(r, p)
-	r, r2 := r.div(nat(nil), r, x.b.abs)
-
-	// see if we need to round up
-	r2 = r2.add(r2, r2)
-	if x.b.abs.cmp(r2) <= 0 {
-		r = r.add(r, natOne)
-		if r.cmp(p) >= 0 {
-			q = nat(nil).add(q, natOne)
-			r = nat(nil).sub(r, p)
-		}
-	}
-
-	if x.a.neg {
-		buf = append(buf, '-')
-	}
-	buf = append(buf, q.utoa(10)...) // itoa ignores sign if q == 0
-
-	if prec > 0 {
-		buf = append(buf, '.')
-		rs := r.utoa(10)
-		for i := prec - len(rs); i > 0; i-- {
-			buf = append(buf, '0')
-		}
-		buf = append(buf, rs...)
-	}
-
-	return string(buf)
-}
diff --git a/src/cmd/compile/internal/big/ratconv_test.go b/src/cmd/compile/internal/big/ratconv_test.go
deleted file mode 100644
index 17bda47..0000000
--- a/src/cmd/compile/internal/big/ratconv_test.go
+++ /dev/null
@@ -1,453 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"bytes"
-	"fmt"
-	"math"
-	"strconv"
-	"strings"
-	"testing"
-)
-
-type StringTest struct {
-	in, out string
-	ok      bool
-}
-
-var setStringTests = []StringTest{
-	{"0", "0", true},
-	{"-0", "0", true},
-	{"1", "1", true},
-	{"-1", "-1", true},
-	{"1.", "1", true},
-	{"1e0", "1", true},
-	{"1.e1", "10", true},
-	{in: "1e"},
-	{in: "1.e"},
-	{in: "1e+14e-5"},
-	{in: "1e4.5"},
-	{in: "r"},
-	{in: "a/b"},
-	{in: "a.b"},
-	{"-0.1", "-1/10", true},
-	{"-.1", "-1/10", true},
-	{"2/4", "1/2", true},
-	{".25", "1/4", true},
-	{"-1/5", "-1/5", true},
-	{"8129567.7690E14", "812956776900000000000", true},
-	{"78189e+4", "781890000", true},
-	{"553019.8935e+8", "55301989350000", true},
-	{"98765432109876543210987654321e-10", "98765432109876543210987654321/10000000000", true},
-	{"9877861857500000E-7", "3951144743/4", true},
-	{"2169378.417e-3", "2169378417/1000000", true},
-	{"884243222337379604041632732738665534", "884243222337379604041632732738665534", true},
-	{"53/70893980658822810696", "53/70893980658822810696", true},
-	{"106/141787961317645621392", "53/70893980658822810696", true},
-	{"204211327800791583.81095", "4084226556015831676219/20000", true},
-	{in: "1/0"},
-}
-
-// These are not supported by fmt.Fscanf.
-var setStringTests2 = []StringTest{
-	{"0x10", "16", true},
-	{"-010/1", "-8", true}, // TODO(gri) should we even permit octal here?
-	{"-010.", "-10", true},
-	{"0x10/0x20", "1/2", true},
-	{"0b1000/3", "8/3", true},
-	// TODO(gri) add more tests
-}
-
-func TestRatSetString(t *testing.T) {
-	var tests []StringTest
-	tests = append(tests, setStringTests...)
-	tests = append(tests, setStringTests2...)
-
-	for i, test := range tests {
-		x, ok := new(Rat).SetString(test.in)
-
-		if ok {
-			if !test.ok {
-				t.Errorf("#%d SetString(%q) expected failure", i, test.in)
-			} else if x.RatString() != test.out {
-				t.Errorf("#%d SetString(%q) got %s want %s", i, test.in, x.RatString(), test.out)
-			}
-		} else if x != nil {
-			t.Errorf("#%d SetString(%q) got %p want nil", i, test.in, x)
-		}
-	}
-}
-
-func TestRatScan(t *testing.T) {
-	var buf bytes.Buffer
-	for i, test := range setStringTests {
-		x := new(Rat)
-		buf.Reset()
-		buf.WriteString(test.in)
-
-		_, err := fmt.Fscanf(&buf, "%v", x)
-		if err == nil != test.ok {
-			if test.ok {
-				t.Errorf("#%d (%s) error: %s", i, test.in, err)
-			} else {
-				t.Errorf("#%d (%s) expected error", i, test.in)
-			}
-			continue
-		}
-		if err == nil && x.RatString() != test.out {
-			t.Errorf("#%d got %s want %s", i, x.RatString(), test.out)
-		}
-	}
-}
-
-var floatStringTests = []struct {
-	in   string
-	prec int
-	out  string
-}{
-	{"0", 0, "0"},
-	{"0", 4, "0.0000"},
-	{"1", 0, "1"},
-	{"1", 2, "1.00"},
-	{"-1", 0, "-1"},
-	{"0.05", 1, "0.1"},
-	{"-0.05", 1, "-0.1"},
-	{".25", 2, "0.25"},
-	{".25", 1, "0.3"},
-	{".25", 3, "0.250"},
-	{"-1/3", 3, "-0.333"},
-	{"-2/3", 4, "-0.6667"},
-	{"0.96", 1, "1.0"},
-	{"0.999", 2, "1.00"},
-	{"0.9", 0, "1"},
-	{".25", -1, "0"},
-	{".55", -1, "1"},
-}
-
-func TestFloatString(t *testing.T) {
-	for i, test := range floatStringTests {
-		x, _ := new(Rat).SetString(test.in)
-
-		if x.FloatString(test.prec) != test.out {
-			t.Errorf("#%d got %s want %s", i, x.FloatString(test.prec), test.out)
-		}
-	}
-}
-
-// Test inputs to Rat.SetString. The prefix "long:" causes the test
-// to be skipped in --test.short mode.  (The threshold is about 500us.)
-var float64inputs = []string{
-	// Constants plundered from strconv/testfp.txt.
-
-	// Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP
-	"5e+125",
-	"69e+267",
-	"999e-026",
-	"7861e-034",
-	"75569e-254",
-	"928609e-261",
-	"9210917e+080",
-	"84863171e+114",
-	"653777767e+273",
-	"5232604057e-298",
-	"27235667517e-109",
-	"653532977297e-123",
-	"3142213164987e-294",
-	"46202199371337e-072",
-	"231010996856685e-073",
-	"9324754620109615e+212",
-	"78459735791271921e+049",
-	"272104041512242479e+200",
-	"6802601037806061975e+198",
-	"20505426358836677347e-221",
-	"836168422905420598437e-234",
-	"4891559871276714924261e+222",
-
-	// Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP
-	"9e-265",
-	"85e-037",
-	"623e+100",
-	"3571e+263",
-	"81661e+153",
-	"920657e-023",
-	"4603285e-024",
-	"87575437e-309",
-	"245540327e+122",
-	"6138508175e+120",
-	"83356057653e+193",
-	"619534293513e+124",
-	"2335141086879e+218",
-	"36167929443327e-159",
-	"609610927149051e-255",
-	"3743626360493413e-165",
-	"94080055902682397e-242",
-	"899810892172646163e+283",
-	"7120190517612959703e+120",
-	"25188282901709339043e-252",
-	"308984926168550152811e-052",
-	"6372891218502368041059e+064",
-
-	// Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP
-	"5e-20",
-	"67e+14",
-	"985e+15",
-	"7693e-42",
-	"55895e-16",
-	"996622e-44",
-	"7038531e-32",
-	"60419369e-46",
-	"702990899e-20",
-	"6930161142e-48",
-	"25933168707e+13",
-	"596428896559e+20",
-
-	// Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP
-	"3e-23",
-	"57e+18",
-	"789e-35",
-	"2539e-18",
-	"76173e+28",
-	"887745e-11",
-	"5382571e-37",
-	"82381273e-35",
-	"750486563e-38",
-	"3752432815e-39",
-	"75224575729e-45",
-	"459926601011e+15",
-
-	// Constants plundered from strconv/atof_test.go.
-
-	"0",
-	"1",
-	"+1",
-	"1e23",
-	"1E23",
-	"100000000000000000000000",
-	"1e-100",
-	"123456700",
-	"99999999999999974834176",
-	"100000000000000000000001",
-	"100000000000000008388608",
-	"100000000000000016777215",
-	"100000000000000016777216",
-	"-1",
-	"-0.1",
-	"-0", // NB: exception made for this input
-	"1e-20",
-	"625e-3",
-
-	// largest float64
-	"1.7976931348623157e308",
-	"-1.7976931348623157e308",
-	// next float64 - too large
-	"1.7976931348623159e308",
-	"-1.7976931348623159e308",
-	// the border is ...158079
-	// borderline - okay
-	"1.7976931348623158e308",
-	"-1.7976931348623158e308",
-	// borderline - too large
-	"1.797693134862315808e308",
-	"-1.797693134862315808e308",
-
-	// a little too large
-	"1e308",
-	"2e308",
-	"1e309",
-
-	// way too large
-	"1e310",
-	"-1e310",
-	"1e400",
-	"-1e400",
-	"long:1e400000",
-	"long:-1e400000",
-
-	// denormalized
-	"1e-305",
-	"1e-306",
-	"1e-307",
-	"1e-308",
-	"1e-309",
-	"1e-310",
-	"1e-322",
-	// smallest denormal
-	"5e-324",
-	"4e-324",
-	"3e-324",
-	// too small
-	"2e-324",
-	// way too small
-	"1e-350",
-	"long:1e-400000",
-	// way too small, negative
-	"-1e-350",
-	"long:-1e-400000",
-
-	// try to overflow exponent
-	// [Disabled: too slow and memory-hungry with rationals.]
-	// "1e-4294967296",
-	// "1e+4294967296",
-	// "1e-18446744073709551616",
-	// "1e+18446744073709551616",
-
-	// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
-	"2.2250738585072012e-308",
-	// http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
-	"2.2250738585072011e-308",
-
-	// A very large number (initially wrongly parsed by the fast algorithm).
-	"4.630813248087435e+307",
-
-	// A different kind of very large number.
-	"22.222222222222222",
-	"long:2." + strings.Repeat("2", 4000) + "e+1",
-
-	// Exactly halfway between 1 and math.Nextafter(1, 2).
-	// Round to even (down).
-	"1.00000000000000011102230246251565404236316680908203125",
-	// Slightly lower; still round down.
-	"1.00000000000000011102230246251565404236316680908203124",
-	// Slightly higher; round up.
-	"1.00000000000000011102230246251565404236316680908203126",
-	// Slightly higher, but you have to read all the way to the end.
-	"long:1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1",
-
-	// Smallest denormal, 2^(-1022-52)
-	"4.940656458412465441765687928682213723651e-324",
-	// Half of smallest denormal, 2^(-1022-53)
-	"2.470328229206232720882843964341106861825e-324",
-	// A little more than the exact half of smallest denormal
-	// 2^-1075 + 2^-1100.  (Rounds to 1p-1074.)
-	"2.470328302827751011111470718709768633275e-324",
-	// The exact halfway between smallest normal and largest denormal:
-	// 2^-1022 - 2^-1075.  (Rounds to 2^-1022.)
-	"2.225073858507201136057409796709131975935e-308",
-
-	"1152921504606846975",  //   1<<60 - 1
-	"-1152921504606846975", // -(1<<60 - 1)
-	"1152921504606846977",  //   1<<60 + 1
-	"-1152921504606846977", // -(1<<60 + 1)
-
-	"1/3",
-}
-
-// isFinite reports whether f represents a finite rational value.
-// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0).
-func isFinite(f float64) bool {
-	return math.Abs(f) <= math.MaxFloat64
-}
-
-func TestFloat32SpecialCases(t *testing.T) {
-	for _, input := range float64inputs {
-		if strings.HasPrefix(input, "long:") {
-			if testing.Short() {
-				continue
-			}
-			input = input[len("long:"):]
-		}
-
-		r, ok := new(Rat).SetString(input)
-		if !ok {
-			t.Errorf("Rat.SetString(%q) failed", input)
-			continue
-		}
-		f, exact := r.Float32()
-
-		// 1. Check string -> Rat -> float32 conversions are
-		// consistent with strconv.ParseFloat.
-		// Skip this check if the input uses "a/b" rational syntax.
-		if !strings.Contains(input, "/") {
-			e64, _ := strconv.ParseFloat(input, 32)
-			e := float32(e64)
-
-			// Careful: negative Rats too small for
-			// float64 become -0, but Rat obviously cannot
-			// preserve the sign from SetString("-0").
-			switch {
-			case math.Float32bits(e) == math.Float32bits(f):
-				// Ok: bitwise equal.
-			case f == 0 && r.Num().BitLen() == 0:
-				// Ok: Rat(0) is equivalent to both +/- float64(0).
-			default:
-				t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
-			}
-		}
-
-		if !isFinite(float64(f)) {
-			continue
-		}
-
-		// 2. Check f is best approximation to r.
-		if !checkIsBestApprox32(t, f, r) {
-			// Append context information.
-			t.Errorf("(input was %q)", input)
-		}
-
-		// 3. Check f->R->f roundtrip is non-lossy.
-		checkNonLossyRoundtrip32(t, f)
-
-		// 4. Check exactness using slow algorithm.
-		if wasExact := new(Rat).SetFloat64(float64(f)).Cmp(r) == 0; wasExact != exact {
-			t.Errorf("Rat.SetString(%q).Float32().exact = %t, want %t", input, exact, wasExact)
-		}
-	}
-}
-
-func TestFloat64SpecialCases(t *testing.T) {
-	for _, input := range float64inputs {
-		if strings.HasPrefix(input, "long:") {
-			if testing.Short() {
-				continue
-			}
-			input = input[len("long:"):]
-		}
-
-		r, ok := new(Rat).SetString(input)
-		if !ok {
-			t.Errorf("Rat.SetString(%q) failed", input)
-			continue
-		}
-		f, exact := r.Float64()
-
-		// 1. Check string -> Rat -> float64 conversions are
-		// consistent with strconv.ParseFloat.
-		// Skip this check if the input uses "a/b" rational syntax.
-		if !strings.Contains(input, "/") {
-			e, _ := strconv.ParseFloat(input, 64)
-
-			// Careful: negative Rats too small for
-			// float64 become -0, but Rat obviously cannot
-			// preserve the sign from SetString("-0").
-			switch {
-			case math.Float64bits(e) == math.Float64bits(f):
-				// Ok: bitwise equal.
-			case f == 0 && r.Num().BitLen() == 0:
-				// Ok: Rat(0) is equivalent to both +/- float64(0).
-			default:
-				t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
-			}
-		}
-
-		if !isFinite(f) {
-			continue
-		}
-
-		// 2. Check f is best approximation to r.
-		if !checkIsBestApprox64(t, f, r) {
-			// Append context information.
-			t.Errorf("(input was %q)", input)
-		}
-
-		// 3. Check f->R->f roundtrip is non-lossy.
-		checkNonLossyRoundtrip64(t, f)
-
-		// 4. Check exactness using slow algorithm.
-		if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact {
-			t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact)
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/ratmarsh.go b/src/cmd/compile/internal/big/ratmarsh.go
deleted file mode 100644
index b82e8d4..0000000
--- a/src/cmd/compile/internal/big/ratmarsh.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements encoding/decoding of Rats.
-
-package big
-
-import (
-	"encoding/binary"
-	"errors"
-	"fmt"
-)
-
-// Gob codec version. Permits backward-compatible changes to the encoding.
-const ratGobVersion byte = 1
-
-// GobEncode implements the gob.GobEncoder interface.
-func (x *Rat) GobEncode() ([]byte, error) {
-	if x == nil {
-		return nil, nil
-	}
-	buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4)
-	i := x.b.abs.bytes(buf)
-	j := x.a.abs.bytes(buf[:i])
-	n := i - j
-	if int(uint32(n)) != n {
-		// this should never happen
-		return nil, errors.New("Rat.GobEncode: numerator too large")
-	}
-	binary.BigEndian.PutUint32(buf[j-4:j], uint32(n))
-	j -= 1 + 4
-	b := ratGobVersion << 1 // make space for sign bit
-	if x.a.neg {
-		b |= 1
-	}
-	buf[j] = b
-	return buf[j:], nil
-}
-
-// GobDecode implements the gob.GobDecoder interface.
-func (z *Rat) GobDecode(buf []byte) error {
-	if len(buf) == 0 {
-		// Other side sent a nil or default value.
-		*z = Rat{}
-		return nil
-	}
-	b := buf[0]
-	if b>>1 != ratGobVersion {
-		return fmt.Errorf("Rat.GobDecode: encoding version %d not supported", b>>1)
-	}
-	const j = 1 + 4
-	i := j + binary.BigEndian.Uint32(buf[j-4:j])
-	z.a.neg = b&1 != 0
-	z.a.abs = z.a.abs.setBytes(buf[j:i])
-	z.b.abs = z.b.abs.setBytes(buf[i:])
-	return nil
-}
-
-// MarshalText implements the encoding.TextMarshaler interface.
-func (x *Rat) MarshalText() (text []byte, err error) {
-	// TODO(gri): get rid of the []byte/string conversion
-	return []byte(x.RatString()), nil
-}
-
-// UnmarshalText implements the encoding.TextUnmarshaler interface.
-func (z *Rat) UnmarshalText(text []byte) error {
-	// TODO(gri): get rid of the []byte/string conversion
-	if _, ok := z.SetString(string(text)); !ok {
-		return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Rat", text)
-	}
-	return nil
-}
diff --git a/src/cmd/compile/internal/big/ratmarsh_test.go b/src/cmd/compile/internal/big/ratmarsh_test.go
deleted file mode 100644
index 351d109..0000000
--- a/src/cmd/compile/internal/big/ratmarsh_test.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package big
-
-import (
-	"bytes"
-	"encoding/gob"
-	"encoding/json"
-	"encoding/xml"
-	"testing"
-)
-
-func TestRatGobEncoding(t *testing.T) {
-	var medium bytes.Buffer
-	enc := gob.NewEncoder(&medium)
-	dec := gob.NewDecoder(&medium)
-	for _, test := range encodingTests {
-		medium.Reset() // empty buffer for each test case (in case of failures)
-		var tx Rat
-		tx.SetString(test + ".14159265")
-		if err := enc.Encode(&tx); err != nil {
-			t.Errorf("encoding of %s failed: %s", &tx, err)
-			continue
-		}
-		var rx Rat
-		if err := dec.Decode(&rx); err != nil {
-			t.Errorf("decoding of %s failed: %s", &tx, err)
-			continue
-		}
-		if rx.Cmp(&tx) != 0 {
-			t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx)
-		}
-	}
-}
-
-// Sending a nil Rat pointer (inside a slice) on a round trip through gob should yield a zero.
-// TODO: top-level nils.
-func TestGobEncodingNilRatInSlice(t *testing.T) {
-	buf := new(bytes.Buffer)
-	enc := gob.NewEncoder(buf)
-	dec := gob.NewDecoder(buf)
-
-	var in = make([]*Rat, 1)
-	err := enc.Encode(&in)
-	if err != nil {
-		t.Errorf("gob encode failed: %q", err)
-	}
-	var out []*Rat
-	err = dec.Decode(&out)
-	if err != nil {
-		t.Fatalf("gob decode failed: %q", err)
-	}
-	if len(out) != 1 {
-		t.Fatalf("wrong len; want 1 got %d", len(out))
-	}
-	var zero Rat
-	if out[0].Cmp(&zero) != 0 {
-		t.Fatalf("transmission of (*Int)(nil) failed: got %s want 0", out)
-	}
-}
-
-var ratNums = []string{
-	"-141592653589793238462643383279502884197169399375105820974944592307816406286",
-	"-1415926535897932384626433832795028841971",
-	"-141592653589793",
-	"-1",
-	"0",
-	"1",
-	"141592653589793",
-	"1415926535897932384626433832795028841971",
-	"141592653589793238462643383279502884197169399375105820974944592307816406286",
-}
-
-var ratDenoms = []string{
-	"1",
-	"718281828459045",
-	"7182818284590452353602874713526624977572",
-	"718281828459045235360287471352662497757247093699959574966967627724076630353",
-}
-
-func TestRatJSONEncoding(t *testing.T) {
-	for _, num := range ratNums {
-		for _, denom := range ratDenoms {
-			var tx Rat
-			tx.SetString(num + "/" + denom)
-			b, err := json.Marshal(&tx)
-			if err != nil {
-				t.Errorf("marshaling of %s failed: %s", &tx, err)
-				continue
-			}
-			var rx Rat
-			if err := json.Unmarshal(b, &rx); err != nil {
-				t.Errorf("unmarshaling of %s failed: %s", &tx, err)
-				continue
-			}
-			if rx.Cmp(&tx) != 0 {
-				t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx)
-			}
-		}
-	}
-}
-
-func TestRatXMLEncoding(t *testing.T) {
-	for _, num := range ratNums {
-		for _, denom := range ratDenoms {
-			var tx Rat
-			tx.SetString(num + "/" + denom)
-			b, err := xml.Marshal(&tx)
-			if err != nil {
-				t.Errorf("marshaling of %s failed: %s", &tx, err)
-				continue
-			}
-			var rx Rat
-			if err := xml.Unmarshal(b, &rx); err != nil {
-				t.Errorf("unmarshaling of %s failed: %s", &tx, err)
-				continue
-			}
-			if rx.Cmp(&tx) != 0 {
-				t.Errorf("XML encoding of %s failed: got %s want %s", &tx, &rx, &tx)
-			}
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/big/roundingmode_string.go b/src/cmd/compile/internal/big/roundingmode_string.go
deleted file mode 100644
index 05024b8..0000000
--- a/src/cmd/compile/internal/big/roundingmode_string.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// generated by stringer -type=RoundingMode; DO NOT EDIT
-
-package big
-
-import "fmt"
-
-const _RoundingMode_name = "ToNearestEvenToNearestAwayToZeroAwayFromZeroToNegativeInfToPositiveInf"
-
-var _RoundingMode_index = [...]uint8{0, 13, 26, 32, 44, 57, 70}
-
-func (i RoundingMode) String() string {
-	if i+1 >= RoundingMode(len(_RoundingMode_index)) {
-		return fmt.Sprintf("RoundingMode(%d)", i)
-	}
-	return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]]
-}
diff --git a/src/cmd/compile/internal/big/vendor.bash b/src/cmd/compile/internal/big/vendor.bash
deleted file mode 100755
index ac3ec9b..0000000
--- a/src/cmd/compile/internal/big/vendor.bash
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright 2015 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Run this script to obtain an up-to-date vendored version of math/big.
-
-BIGDIR=../../../../math/big
-
-# Start from scratch.
-rm *.go
-
-# We don't want any assembly files.
-cp $BIGDIR/*.go .
-
-# Use pure Go arith ops w/o build tag.
-sed 's|^// \+build math_big_pure_go$||' arith_decl_pure.go > arith_decl.go
-rm arith_decl_pure.go
-
-# Import vendored math/big in external tests (e.g., floatexample_test.go).
-for f in *_test.go; do
-	sed 's|"math/big"|"cmd/compile/internal/big"|' $f > foo.go
-	mv foo.go $f
-done
-
-# gofmt to clean up after sed
-gofmt -w .
-
-# Test that it works
-go test -short
diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go
index 136612d..8113710 100644
--- a/src/cmd/compile/internal/gc/alg.go
+++ b/src/cmd/compile/internal/gc/alg.go
@@ -191,20 +191,20 @@ func genhash(sym *Sym, t *Type) {
 	markdcl()
 
 	// func sym(p *T, h uintptr) uintptr
-	fn := Nod(ODCLFUNC, nil, nil)
+	fn := nod(ODCLFUNC, nil, nil)
 
 	fn.Func.Nname = newname(sym)
 	fn.Func.Nname.Class = PFUNC
-	tfn := Nod(OTFUNC, nil, nil)
+	tfn := nod(OTFUNC, nil, nil)
 	fn.Func.Nname.Name.Param.Ntype = tfn
 
-	n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
+	n := nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)))
 	tfn.List.Append(n)
 	np := n.Left
-	n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR]))
+	n = nod(ODCLFIELD, newname(lookup("h")), typenod(Types[TUINTPTR]))
 	tfn.List.Append(n)
 	nh := n.Left
-	n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value
+	n = nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value
 	tfn.Rlist.Append(n)
 
 	funchdr(fn)
@@ -223,8 +223,8 @@ func genhash(sym *Sym, t *Type) {
 		// pure memory.
 		hashel := hashfor(t.Elem())
 
-		n := Nod(ORANGE, nil, Nod(OIND, np, nil))
-		ni := newname(Lookup("i"))
+		n := nod(ORANGE, nil, nod(OIND, np, nil))
+		ni := newname(lookup("i"))
 		ni.Type = Types[TINT]
 		n.List.Set1(ni)
 		n.Colas = true
@@ -232,15 +232,15 @@ func genhash(sym *Sym, t *Type) {
 		ni = n.List.First()
 
 		// h = hashel(&p[i], h)
-		call := Nod(OCALL, hashel, nil)
+		call := nod(OCALL, hashel, nil)
 
-		nx := Nod(OINDEX, np, ni)
+		nx := nod(OINDEX, np, ni)
 		nx.Bounded = true
-		na := Nod(OADDR, nx, nil)
+		na := nod(OADDR, nx, nil)
 		na.Etype = 1 // no escape to heap
 		call.List.Append(na)
 		call.List.Append(nh)
-		n.Nbody.Append(Nod(OAS, nh, call))
+		n.Nbody.Append(nod(OAS, nh, call))
 
 		fn.Nbody.Append(n)
 
@@ -259,13 +259,13 @@ func genhash(sym *Sym, t *Type) {
 			// Hash non-memory fields with appropriate hash function.
 			if !f.Type.IsRegularMemory() {
 				hashel := hashfor(f.Type)
-				call := Nod(OCALL, hashel, nil)
-				nx := NodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
-				na := Nod(OADDR, nx, nil)
+				call := nod(OCALL, hashel, nil)
+				nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
+				na := nod(OADDR, nx, nil)
 				na.Etype = 1 // no escape to heap
 				call.List.Append(na)
 				call.List.Append(nh)
-				fn.Nbody.Append(Nod(OAS, nh, call))
+				fn.Nbody.Append(nod(OAS, nh, call))
 				i++
 				continue
 			}
@@ -275,20 +275,20 @@ func genhash(sym *Sym, t *Type) {
 
 			// h = hashel(&p.first, size, h)
 			hashel := hashmem(f.Type)
-			call := Nod(OCALL, hashel, nil)
-			nx := NodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
-			na := Nod(OADDR, nx, nil)
+			call := nod(OCALL, hashel, nil)
+			nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
+			na := nod(OADDR, nx, nil)
 			na.Etype = 1 // no escape to heap
 			call.List.Append(na)
 			call.List.Append(nh)
-			call.List.Append(Nodintconst(size))
-			fn.Nbody.Append(Nod(OAS, nh, call))
+			call.List.Append(nodintconst(size))
+			fn.Nbody.Append(nod(OAS, nh, call))
 
 			i = next
 		}
 	}
 
-	r := Nod(ORETURN, nil, nil)
+	r := nod(ORETURN, nil, nil)
 	r.List.Append(nh)
 	fn.Nbody.Append(r)
 
@@ -313,9 +313,9 @@ func genhash(sym *Sym, t *Type) {
 	old_safemode := safemode
 	safemode = false
 
-	Disable_checknil++
+	disable_checknil++
 	funccompile(fn)
-	Disable_checknil--
+	disable_checknil--
 
 	safemode = old_safemode
 }
@@ -346,10 +346,10 @@ func hashfor(t *Type) *Node {
 
 	n := newname(sym)
 	n.Class = PFUNC
-	tfn := Nod(OTFUNC, nil, nil)
-	tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
-	tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
-	tfn.Rlist.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
+	tfn := nod(OTFUNC, nil, nil)
+	tfn.List.Append(nod(ODCLFIELD, nil, typenod(ptrto(t))))
+	tfn.List.Append(nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
+	tfn.Rlist.Append(nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
 	tfn = typecheck(tfn, Etype)
 	n.Type = tfn.Type
 	return n
@@ -367,20 +367,20 @@ func geneq(sym *Sym, t *Type) {
 	markdcl()
 
 	// func sym(p, q *T) bool
-	fn := Nod(ODCLFUNC, nil, nil)
+	fn := nod(ODCLFUNC, nil, nil)
 
 	fn.Func.Nname = newname(sym)
 	fn.Func.Nname.Class = PFUNC
-	tfn := Nod(OTFUNC, nil, nil)
+	tfn := nod(OTFUNC, nil, nil)
 	fn.Func.Nname.Name.Param.Ntype = tfn
 
-	n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
+	n := nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)))
 	tfn.List.Append(n)
 	np := n.Left
-	n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t)))
+	n = nod(ODCLFIELD, newname(lookup("q")), typenod(ptrto(t)))
 	tfn.List.Append(n)
 	nq := n.Left
-	n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
+	n = nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
 	tfn.Rlist.Append(n)
 
 	funchdr(fn)
@@ -399,9 +399,9 @@ func geneq(sym *Sym, t *Type) {
 		// pure memory. Even if we unrolled the range loop,
 		// each iteration would be a function call, so don't bother
 		// unrolling.
-		nrange := Nod(ORANGE, nil, Nod(OIND, np, nil))
+		nrange := nod(ORANGE, nil, nod(OIND, np, nil))
 
-		ni := newname(Lookup("i"))
+		ni := newname(lookup("i"))
 		ni.Type = Types[TINT]
 		nrange.List.Set1(ni)
 		nrange.Colas = true
@@ -409,23 +409,23 @@ func geneq(sym *Sym, t *Type) {
 		ni = nrange.List.First()
 
 		// if p[i] != q[i] { return false }
-		nx := Nod(OINDEX, np, ni)
+		nx := nod(OINDEX, np, ni)
 
 		nx.Bounded = true
-		ny := Nod(OINDEX, nq, ni)
+		ny := nod(OINDEX, nq, ni)
 		ny.Bounded = true
 
-		nif := Nod(OIF, nil, nil)
-		nif.Left = Nod(ONE, nx, ny)
-		r := Nod(ORETURN, nil, nil)
-		r.List.Append(Nodbool(false))
+		nif := nod(OIF, nil, nil)
+		nif.Left = nod(ONE, nx, ny)
+		r := nod(ORETURN, nil, nil)
+		r.List.Append(nodbool(false))
 		nif.Nbody.Append(r)
 		nrange.Nbody.Append(nif)
 		fn.Nbody.Append(nrange)
 
 		// return true
-		ret := Nod(ORETURN, nil, nil)
-		ret.List.Append(Nodbool(true))
+		ret := nod(ORETURN, nil, nil)
+		ret.List.Append(nodbool(true))
 		fn.Nbody.Append(ret)
 
 	case TSTRUCT:
@@ -435,7 +435,7 @@ func geneq(sym *Sym, t *Type) {
 				cond = n
 				return
 			}
-			cond = Nod(OANDAND, cond, n)
+			cond = nod(OANDAND, cond, n)
 		}
 
 		// Walk the struct using memequal for runs of AMEM
@@ -474,10 +474,10 @@ func geneq(sym *Sym, t *Type) {
 		}
 
 		if cond == nil {
-			cond = Nodbool(true)
+			cond = nodbool(true)
 		}
 
-		ret := Nod(ORETURN, nil, nil)
+		ret := nod(ORETURN, nil, nil)
 		ret.List.Append(cond)
 		fn.Nbody.Append(ret)
 	}
@@ -507,39 +507,39 @@ func geneq(sym *Sym, t *Type) {
 	// We are comparing a struct or an array,
 	// neither of which can be nil, and our comparisons
 	// are shallow.
-	Disable_checknil++
+	disable_checknil++
 
 	funccompile(fn)
 
 	safemode = old_safemode
-	Disable_checknil--
+	disable_checknil--
 }
 
 // eqfield returns the node
 // 	p.field == q.field
 func eqfield(p *Node, q *Node, field *Sym) *Node {
-	nx := NodSym(OXDOT, p, field)
-	ny := NodSym(OXDOT, q, field)
-	ne := Nod(OEQ, nx, ny)
+	nx := nodSym(OXDOT, p, field)
+	ny := nodSym(OXDOT, q, field)
+	ne := nod(OEQ, nx, ny)
 	return ne
 }
 
 // eqmem returns the node
 // 	memequal(&p.field, &q.field [, size])
 func eqmem(p *Node, q *Node, field *Sym, size int64) *Node {
-	nx := Nod(OADDR, NodSym(OXDOT, p, field), nil)
+	nx := nod(OADDR, nodSym(OXDOT, p, field), nil)
 	nx.Etype = 1 // does not escape
-	ny := Nod(OADDR, NodSym(OXDOT, q, field), nil)
+	ny := nod(OADDR, nodSym(OXDOT, q, field), nil)
 	ny.Etype = 1 // does not escape
 	nx = typecheck(nx, Erv)
 	ny = typecheck(ny, Erv)
 
 	fn, needsize := eqmemfunc(size, nx.Type.Elem())
-	call := Nod(OCALL, fn, nil)
+	call := nod(OCALL, fn, nil)
 	call.List.Append(nx)
 	call.List.Append(ny)
 	if needsize {
-		call.List.Append(Nodintconst(size))
+		call.List.Append(nodintconst(size))
 	}
 
 	return call
diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go
index 2b62405..eee801f 100644
--- a/src/cmd/compile/internal/gc/align.go
+++ b/src/cmd/compile/internal/gc/align.go
@@ -21,7 +21,7 @@ func offmod(t *Type) {
 		f.Offset = int64(o)
 		o += int32(Widthptr)
 		if int64(o) >= Thearch.MAXWIDTH {
-			Yyerror("interface too large")
+			yyerror("interface too large")
 			o = int32(Widthptr)
 		}
 	}
@@ -75,7 +75,7 @@ func widstruct(errtype *Type, t *Type, o int64, flag int) int64 {
 		}
 		o += w
 		if o >= Thearch.MAXWIDTH {
-			Yyerror("type %v too large", Tconv(errtype, FmtLong))
+			yyerror("type %L too large", errtype)
 			o = 8 // small but nonzero
 		}
 	}
@@ -148,8 +148,8 @@ func dowidth(t *Type) {
 
 	// simtype == 0 during bootstrap
 	default:
-		if Simtype[t.Etype] != 0 {
-			et = Simtype[t.Etype]
+		if simtype[t.Etype] != 0 {
+			et = simtype[t.Etype]
 		}
 	}
 
@@ -169,10 +169,14 @@ func dowidth(t *Type) {
 	case TINT32, TUINT32, TFLOAT32:
 		w = 4
 
-	case TINT64, TUINT64, TFLOAT64, TCOMPLEX64:
+	case TINT64, TUINT64, TFLOAT64:
 		w = 8
 		t.Align = uint8(Widthreg)
 
+	case TCOMPLEX64:
+		w = 8
+		t.Align = 4
+
 	case TCOMPLEX128:
 		w = 16
 		t.Align = uint8(Widthreg)
@@ -208,7 +212,7 @@ func dowidth(t *Type) {
 		t1 := t.ChanArgs()
 		dowidth(t1) // just in case
 		if t1.Elem().Width >= 1<<16 {
-			Yyerror("channel element type too large (>64kB)")
+			yyerror("channel element type too large (>64kB)")
 		}
 		t.Width = 1
 
@@ -219,16 +223,13 @@ func dowidth(t *Type) {
 
 	case TFORW: // should have been filled in
 		if !t.Broke {
-			Yyerror("invalid recursive type %v", t)
+			yyerror("invalid recursive type %v", t)
 		}
 		w = 1 // anything will do
 
-	// dummy type; should be replaced before use.
 	case TANY:
-		if Debug['A'] == 0 {
-			Fatalf("dowidth any")
-		}
-		w = 1 // anything will do
+		// dummy type; should be replaced before use.
+		Fatalf("dowidth any")
 
 	case TSTRING:
 		if sizeof_String == 0 {
@@ -243,7 +244,7 @@ func dowidth(t *Type) {
 		}
 		if t.isDDDArray() {
 			if !t.Broke {
-				Yyerror("use of [...] array outside of array literal")
+				yyerror("use of [...] array outside of array literal")
 				t.Broke = true
 			}
 			break
@@ -253,7 +254,7 @@ func dowidth(t *Type) {
 		if t.Elem().Width != 0 {
 			cap := (uint64(Thearch.MAXWIDTH) - 1) / uint64(t.Elem().Width)
 			if uint64(t.NumElem()) > cap {
-				Yyerror("type %v larger than address space", Tconv(t, FmtLong))
+				yyerror("type %L larger than address space", t)
 			}
 		}
 		w = t.NumElem() * t.Elem().Width
@@ -295,7 +296,7 @@ func dowidth(t *Type) {
 	}
 
 	if Widthptr == 4 && w != int64(int32(w)) {
-		Yyerror("type %v too large", t)
+		yyerror("type %v too large", t)
 	}
 
 	t.Width = w
@@ -378,22 +379,3 @@ func resumecheckwidth() {
 
 	defercalc = 0
 }
-
-// compute total size of f's in/out arguments.
-func Argsize(t *Type) int {
-	var w int64
-
-	for _, p := range recvsParamsResults {
-		for _, f := range p(t).Fields().Slice() {
-			if x := f.End(); x > w {
-				w = x
-			}
-		}
-	}
-
-	w = Rnd(w, int64(Widthptr))
-	if int64(int(w)) != w {
-		Fatalf("argsize too big")
-	}
-	return int(w)
-}
diff --git a/src/cmd/compile/internal/gc/asm_test.go b/src/cmd/compile/internal/gc/asm_test.go
index 21b5910..2e5d7e7 100644
--- a/src/cmd/compile/internal/gc/asm_test.go
+++ b/src/cmd/compile/internal/gc/asm_test.go
@@ -20,8 +20,10 @@ import (
 
 // TestAssembly checks to make sure the assembly generated for
 // functions contains certain expected instructions.
-// Note: this test will fail if -ssa=0.
 func TestAssembly(t *testing.T) {
+	if testing.Short() {
+		t.Skip("slow test; skipping")
+	}
 	testenv.MustHaveGoBuild(t)
 	if runtime.GOOS == "windows" {
 		// TODO: remove if we can get "go tool compile -S" to work on windows.
@@ -34,7 +36,7 @@ func TestAssembly(t *testing.T) {
 	defer os.RemoveAll(dir)
 
 	for _, test := range asmTests {
-		asm := compileToAsm(dir, test.arch, fmt.Sprintf(template, test.function))
+		asm := compileToAsm(t, dir, test.arch, test.os, fmt.Sprintf(template, test.function))
 		// Get rid of code for "".init. Also gets rid of type algorithms & other junk.
 		if i := strings.Index(asm, "\n\"\".init "); i >= 0 {
 			asm = asm[:i+1]
@@ -49,7 +51,7 @@ func TestAssembly(t *testing.T) {
 
 // compile compiles the package pkg for architecture arch and
 // returns the generated assembly.  dir is a scratch directory.
-func compileToAsm(dir, arch, pkg string) string {
+func compileToAsm(t *testing.T, dir, goarch, goos, pkg string) string {
 	// Create source.
 	src := filepath.Join(dir, "test.go")
 	f, err := os.Create(src)
@@ -59,9 +61,27 @@ func compileToAsm(dir, arch, pkg string) string {
 	f.Write([]byte(pkg))
 	f.Close()
 
+	// First, install any dependencies we need.  This builds the required export data
+	// for any packages that are imported.
+	// TODO: extract dependencies automatically?
 	var stdout, stderr bytes.Buffer
-	cmd := exec.Command("go", "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
-	cmd.Env = mergeEnvLists([]string{"GOARCH=" + arch}, os.Environ())
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", filepath.Join(dir, "encoding/binary.a"), "encoding/binary")
+	cmd.Env = mergeEnvLists([]string{"GOARCH=" + goarch, "GOOS=" + goos}, os.Environ())
+	cmd.Stdout = &stdout
+	cmd.Stderr = &stderr
+	if err := cmd.Run(); err != nil {
+		panic(err)
+	}
+	if s := stdout.String(); s != "" {
+		panic(fmt.Errorf("Stdout = %s\nWant empty", s))
+	}
+	if s := stderr.String(); s != "" {
+		panic(fmt.Errorf("Stderr = %s\nWant empty", s))
+	}
+
+	// Now, compile the individual file for which we want to see the generated assembly.
+	cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-I", dir, "-S", "-o", filepath.Join(dir, "out.o"), src)
+	cmd.Env = mergeEnvLists([]string{"GOARCH=" + goarch, "GOOS=" + goos}, os.Environ())
 	cmd.Stdout = &stdout
 	cmd.Stderr = &stderr
 	if err := cmd.Run(); err != nil {
@@ -82,6 +102,8 @@ package main
 type asmTest struct {
 	// architecture to compile to
 	arch string
+	// os to compile to
+	os string
 	// function to compile
 	function string
 	// regexps that must match the generated assembly
@@ -89,19 +111,100 @@ type asmTest struct {
 }
 
 var asmTests = [...]asmTest{
-	{"amd64", `
+	{"amd64", "linux", `
 func f(x int) int {
 	return x * 64
 }
 `,
 		[]string{"\tSHLQ\t\\$6,"},
 	},
-	{"amd64", `
+	{"amd64", "linux", `
 func f(x int) int {
 	return x * 96
 }`,
 		[]string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"},
 	},
+	// Load-combining tests.
+	{"amd64", "linux", `
+import "encoding/binary"
+func f(b []byte) uint64 {
+	return binary.LittleEndian.Uint64(b)
+}
+`,
+		[]string{"\tMOVQ\t\\(.*\\),"},
+	},
+	{"amd64", "linux", `
+import "encoding/binary"
+func f(b []byte, i int) uint64 {
+	return binary.LittleEndian.Uint64(b[i:])
+}
+`,
+		[]string{"\tMOVQ\t\\(.*\\)\\(.*\\*1\\),"},
+	},
+	{"amd64", "linux", `
+import "encoding/binary"
+func f(b []byte) uint32 {
+	return binary.LittleEndian.Uint32(b)
+}
+`,
+		[]string{"\tMOVL\t\\(.*\\),"},
+	},
+	{"amd64", "linux", `
+import "encoding/binary"
+func f(b []byte, i int) uint32 {
+	return binary.LittleEndian.Uint32(b[i:])
+}
+`,
+		[]string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"},
+	},
+	{"amd64", "linux", `
+import "encoding/binary"
+func f(b []byte) uint64 {
+	return binary.BigEndian.Uint64(b)
+}
+`,
+		[]string{"\tBSWAPQ\t"},
+	},
+	{"amd64", "linux", `
+import "encoding/binary"
+func f(b []byte, i int) uint64 {
+	return binary.BigEndian.Uint64(b[i:])
+}
+`,
+		[]string{"\tBSWAPQ\t"},
+	},
+	{"amd64", "linux", `
+import "encoding/binary"
+func f(b []byte) uint32 {
+	return binary.BigEndian.Uint32(b)
+}
+`,
+		[]string{"\tBSWAPL\t"},
+	},
+	{"amd64", "linux", `
+import "encoding/binary"
+func f(b []byte, i int) uint32 {
+	return binary.BigEndian.Uint32(b[i:])
+}
+`,
+		[]string{"\tBSWAPL\t"},
+	},
+	{"386", "linux", `
+import "encoding/binary"
+func f(b []byte) uint32 {
+	return binary.LittleEndian.Uint32(b)
+}
+`,
+		[]string{"\tMOVL\t\\(.*\\),"},
+	},
+	{"386", "linux", `
+import "encoding/binary"
+func f(b []byte, i int) uint32 {
+	return binary.LittleEndian.Uint32(b[i:])
+}
+`,
+		[]string{"\tMOVL\t\\(.*\\)\\(.*\\*1\\),"},
+	},
 }
 
 // mergeEnvLists merges the two environment lists such that
@@ -139,7 +242,7 @@ func TestLineNumber(t *testing.T) {
 		t.Fatalf("could not write file: %v", err)
 	}
 
-	cmd := exec.Command("go", "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
+	cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
 	out, err := cmd.CombinedOutput()
 	if err != nil {
 		t.Fatalf("fail to run go tool compile: %v", err)
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index 38e035e..1da5b69 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -3,16 +3,17 @@
 // license that can be found in the LICENSE file.
 
 // Binary package export.
-// (see fmt.go, parser.go as "documentation" for how to use/setup data structures)
 
 /*
 1) Export data encoding principles:
 
 The export data is a serialized description of the graph of exported
-"objects": constants, types, variables, and functions. In general,
-types - but also objects referred to from inlined function bodies -
-can be reexported and so we need to know which package they are coming
-from. Therefore, packages are also part of the export graph.
+"objects": constants, types, variables, and functions. Aliases may be
+directly reexported, and unaliased types may be indirectly reexported
+(as part of the type of a directly exported object). More generally,
+objects referred to from inlined function bodies can be reexported.
+We need to know which package declares these reexported objects, and
+therefore packages are also part of the export graph.
 
 The roots of the graph are two lists of objects. The 1st list (phase 1,
 see Export) contains all objects that are exported at the package level.
@@ -30,9 +31,9 @@ function bodies. The format of this representation is compiler specific.
 
 The graph is serialized in in-order fashion, starting with the roots.
 Each object in the graph is serialized by writing its fields sequentially.
-If the field is a pointer to another object, that object is serialized,
-recursively. Otherwise the field is written. Non-pointer fields are all
-encoded as integer or string values.
+If the field is a pointer to another object, that object is serialized in
+place, recursively. Otherwise the field is written in place. Non-pointer
+fields are all encoded as integer or string values.
 
 Some objects (packages, types) may be referred to more than once. When
 reaching an object that was not serialized before, an integer _index_
@@ -43,7 +44,7 @@ If the object was already serialized, the encoding is simply the object
 index >= 0. An importer can trivially determine if an object needs to
 be read in for the first time (tag < 0) and entered into the respective
 object table, or if the object was seen already (index >= 0), in which
-case the index is used to look up the object in a table.
+case the index is used to look up the object in the respective table.
 
 Before exporting or importing, the type tables are populated with the
 predeclared types (int, string, error, unsafe.Pointer, etc.). This way
@@ -51,13 +52,16 @@ they are automatically encoded with a known and fixed type index.
 
 2) Encoding format:
 
-The export data starts with a single byte indicating the encoding format
-(compact, or with debugging information), followed by a version string
-(so we can evolve the encoding if need be), and then the package object
-for the exported package (with an empty path).
+The export data starts with two newline-terminated strings: a version
+string and either an empty string, or "debug", when emitting the debug
+format. These strings are followed by version-specific encoding options.
 
-After this header, two lists of objects and the list of inlined function
-bodies follows.
+(The Go1.7 version starts with a couple of bytes specifying the format.
+That format encoding is no longer used but is supported to avoid spurious
+errors when importing old installed package files.)
+
+This header is followed by the package object for the exported package,
+two lists of objects, and the list of inlined function bodies.
 
 The encoding of objects is straight-forward: Constants, variables, and
 functions start with their name, type, and possibly a value. Named types
@@ -66,6 +70,9 @@ same type was imported before via another import, the importer must use
 the previously imported type pointer so that we have exactly one version
 (i.e., one pointer) for each named type (and read but discard the current
 type encoding). Unnamed types simply encode their respective fields.
+Aliases are encoded starting with their name followed by the qualified
+identifier denoting the original (aliased) object, which was exported
+earlier.
 
 In the encoding, some lists start with the list length. Some lists are
 terminated with an end marker (usually for lists where we may not know
@@ -77,7 +84,8 @@ Strings are canonicalized similar to objects that may occur multiple times:
 If the string was exported already, it is represented by its index only.
 Otherwise, the export data starts with the negative string length (negative,
 so we can distinguish from string index), followed by the string bytes.
-The empty string is mapped to index 0.
+The empty string is mapped to index 0. (The initial format string is an
+exception; it is encoded as the string bytes followed by a newline).
 
 The exporter and importer are completely symmetric in implementation: For
 each encoding routine there is a matching and symmetric decoding routine.
@@ -97,30 +105,8 @@ compatibility with both the last release of the compiler, and with the
 corresponding compiler at tip. That change is necessarily more involved,
 as it must switch based on the version number in the export data file.
 
-It is recommended to turn on debugFormat when working on format changes
-as it will help finding encoding/decoding inconsistencies quickly.
-
-Special care must be taken to update builtin.go when the export format
-changes: builtin.go contains the export data obtained by compiling the
-builtin/runtime.go and builtin/unsafe.go files; those compilations in
-turn depend on importing the data in builtin.go. Thus, when the export
-data format changes, the compiler must be able to import the data in
-builtin.go even if its format has not yet changed. Proceed in several
-steps as follows:
-
-- Change the exporter to use the new format, and use a different version
-  string as well.
-- Update the importer accordingly, but accept both the old and the new
-  format depending on the version string.
-- all.bash should pass at this point.
-- Run mkbuiltin.go: this will create a new builtin.go using the new
-  export format.
-- go test -run Builtin should pass at this point.
-- Remove importer support for the old export format and (maybe) revert
-  the version string again (it's only needed to mark the transition).
-- all.bash should still pass.
-
-Don't forget to set debugFormat to false.
+It is recommended to turn on debugFormat temporarily when working on format
+changes as it will help finding encoding/decoding inconsistencies quickly.
 */
 
 package gc
@@ -128,9 +114,9 @@ package gc
 import (
 	"bufio"
 	"bytes"
-	"cmd/compile/internal/big"
 	"encoding/binary"
 	"fmt"
+	"math/big"
 	"sort"
 	"strings"
 )
@@ -153,13 +139,12 @@ const debugFormat = false // default: false
 // TODO(gri) disable and remove once there is only one export format again
 const forceObjFileStability = true
 
-// Supported export format versions.
-// TODO(gri) Make this more systematic (issue #16244).
-const (
-	exportVersion0 = "v0"
-	exportVersion1 = "v1"
-	exportVersion  = exportVersion1
-)
+// Current export format version. Increase with each format change.
+// 3: added aliasTag and export of aliases
+// 2: removed unused bool in ODCL export
+// 1: header format change (more regular), export package for _ struct fields
+// 0: Go1.7 encoding
+const exportVersion = 3
 
 // exportInlined enables the export of inlined function bodies and related
 // dependencies. The compiler should work w/o any loss of functionality with
@@ -174,8 +159,8 @@ const exportInlined = true // default: true
 // errors.
 // If disabled, only named types are tracked, possibly leading to slightly
 // less efficient encoding in rare cases. It also prevents the export of
-// some corner-case type declarations (but those are not handled correctly
-// with with the textual export format either).
+// some corner-case type declarations (but those were not handled correctly
+// with the former textual export format either).
 // TODO(gri) enable and remove once issues caused by it are fixed
 const trackAllTypes = false
 
@@ -197,59 +182,37 @@ type exporter struct {
 	written int // bytes written
 	indent  int // for p.trace
 	trace   bool
+
+	// work-around for issue #16369 only
+	nesting int // amount of "nesting" of interface types
 }
 
 // export writes the exportlist for localpkg to out and returns the number of bytes written.
 func export(out *bufio.Writer, trace bool) int {
 	p := exporter{
-		out:      out,
-		strIndex: map[string]int{"": 0}, // empty string is mapped to 0
-		pkgIndex: make(map[*Pkg]int),
-		typIndex: make(map[*Type]int),
-		// don't emit pos info for builtin packages
-		// (not needed and avoids path name diffs in builtin.go between
-		// Windows and non-Windows machines, exposed via builtin_test.go)
-		posInfoFormat: Debug['A'] == 0,
+		out:           out,
+		strIndex:      map[string]int{"": 0}, // empty string is mapped to 0
+		pkgIndex:      make(map[*Pkg]int),
+		typIndex:      make(map[*Type]int),
+		posInfoFormat: true,
 		trace:         trace,
 	}
 
-	// TODO(gri) clean up the ad-hoc encoding of the file format below
-	// (we need this so we can read the builtin package export data
-	// easily w/o being affected by format changes)
-
-	// first byte indicates low-level encoding format
-	var format byte = 'c' // compact
+	// write version info
+	// The version string must start with "version %d" where %d is the version
+	// number. Additional debugging information may follow after a blank; that
+	// text is ignored by the importer.
+	p.rawStringln(fmt.Sprintf("version %d", exportVersion))
+	var debug string
 	if debugFormat {
-		format = 'd'
-	}
-	p.rawByte(format)
-
-	format = 'n' // track named types only
-	if trackAllTypes {
-		format = 'a'
+		debug = "debug"
 	}
-	p.rawByte(format)
-
-	// posInfo exported or not?
+	p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly
+	p.bool(trackAllTypes)
 	p.bool(p.posInfoFormat)
 
 	// --- generic export data ---
 
-	if p.trace {
-		p.tracef("\n--- package ---\n")
-		if p.indent != 0 {
-			Fatalf("exporter: incorrect indentation %d", p.indent)
-		}
-	}
-
-	if p.trace {
-		p.tracef("version = ")
-	}
-	p.string(exportVersion)
-	if p.trace {
-		p.tracef("\n")
-	}
-
 	// populate type map with predeclared "known" types
 	predecl := predeclared()
 	for index, typ := range predecl {
@@ -343,7 +306,6 @@ func export(out *bufio.Writer, trace bool) int {
 	}
 
 	// write compiler-specific flags
-	p.bool(safemode)
 	if p.trace {
 		p.tracef("\n")
 	}
@@ -388,6 +350,11 @@ func export(out *bufio.Writer, trace bool) int {
 		if p.trace {
 			p.tracef("\n")
 		}
+
+		if sym.Flags&SymAlias != 0 {
+			Fatalf("exporter: unexpected alias %v in inlined function body", sym)
+		}
+
 		p.obj(sym)
 		objcount++
 	}
@@ -417,7 +384,7 @@ func export(out *bufio.Writer, trace bool) int {
 			// function has inlineable body:
 			// write index and body
 			if p.trace {
-				p.tracef("\n----\nfunc { %s }\n", hconv(f.Inl, FmtSharp))
+				p.tracef("\n----\nfunc { %#v }\n", f.Inl)
 			}
 			p.int(i)
 			p.stmtList(f.Inl)
@@ -479,16 +446,41 @@ func unidealType(typ *Type, val Val) *Type {
 }
 
 func (p *exporter) obj(sym *Sym) {
+	if sym.Flags&SymAlias != 0 {
+		p.tag(aliasTag)
+		p.pos(nil) // TODO(gri) fix position information
+		// Aliases can only be exported from the package that
+		// declares them (aliases to aliases are resolved to the
+		// original object, and so are uses of aliases in inlined
+		// exported function bodies). Thus, we only need the alias
+		// name without package qualification.
+		if sym.Pkg != localpkg {
+			Fatalf("exporter: export of non-local alias: %v", sym)
+		}
+		p.string(sym.Name)
+		orig := sym.Def.Sym
+		if orig.Flags&SymAlias != 0 {
+			Fatalf("exporter: original object %v marked as alias", sym)
+		}
+		p.qualifiedName(orig)
+		return
+	}
+
+	if sym != sym.Def.Sym {
+		Fatalf("exporter: exported object %v is not original %v", sym, sym.Def.Sym)
+	}
+
 	// Exported objects may be from different packages because they
-	// may be re-exported as depencies when exporting inlined function
-	// bodies. Thus, exported object names must be fully qualified.
+	// may be re-exported via an exported alias or as dependencies in
+	// exported inlined function bodies. Thus, exported object names
+	// must be fully qualified.
 	//
-	// TODO(gri) This can only happen if exportInlined is enabled
-	// (default), and during phase 2 of object export. Objects exported
-	// in phase 1 (compiler-indendepent objects) are by definition only
-	// the objects from the current package and not pulled in via inlined
-	// function bodies. In that case the package qualifier is not needed.
-	// Possible space optimization.
+	// (This can only happen for aliased objects or during phase 2
+	// (exportInlined enabled) of object export. Unaliased Objects
+	// exported in phase 1 (compiler-indendepent objects) are by
+	// definition only the objects from the current package and not
+	// pulled in via inlined function bodies. In that case the package
+	// qualifier is not needed. Possible space optimization.)
 
 	n := sym.Def
 	switch n.Op {
@@ -732,14 +724,7 @@ func (p *exporter) typ(t *Type) {
 			p.paramList(sig.Recvs(), inlineable)
 			p.paramList(sig.Params(), inlineable)
 			p.paramList(sig.Results(), inlineable)
-
-			// for issue #16243
-			// We make this conditional for 1.7 to avoid consistency problems
-			// with installed packages compiled with an older version.
-			// TODO(gri) Clean up after 1.7 is out (issue #16244)
-			if exportVersion == exportVersion1 {
-				p.bool(m.Nointerface)
-			}
+			p.bool(m.Nointerface) // record go:nointerface pragma value (see also #16243)
 
 			var f *Func
 			if inlineable {
@@ -790,11 +775,39 @@ func (p *exporter) typ(t *Type) {
 
 	case TINTER:
 		p.tag(interfaceTag)
-
 		// gc doesn't separate between embedded interfaces
 		// and methods declared explicitly with an interface
 		p.int(0) // no embedded interfaces
+
+		// Because the compiler flattens interfaces containing
+		// embedded interfaces, it is possible to create interface
+		// types that recur through an unnamed type.
+		// If trackAllTypes is disabled, such recursion is not
+		// detected, leading to a stack overflow during export
+		// (issue #16369).
+		// As a crude work-around we terminate deep recursion
+		// through interface types with an empty interface and
+		// report an error.
+		// This will catch endless recursion, but is unlikely
+		// to trigger for valid, deeply nested types given the
+		// high threshold.
+		// It would be ok to continue without reporting an error
+		// since the export format is valid. But a subsequent
+		// import would import an incorrect type. The textual
+		// exporter does not report an error but importing the
+		// resulting package will lead to a syntax error during
+		// import.
+		// TODO(gri) remove this once we have a permanent fix
+		// for the issue.
+		if p.nesting > 100 {
+			p.int(0) // 0 methods to indicate empty interface
+			yyerrorl(t.Lineno, "cannot export unnamed recursive interface")
+			break
+		}
+
+		p.nesting++
 		p.methodList(t)
+		p.nesting--
 
 	case TMAP:
 		p.tag(mapTag)
@@ -807,14 +820,11 @@ func (p *exporter) typ(t *Type) {
 		p.typ(t.Elem())
 
 	default:
-		Fatalf("exporter: unexpected type: %s (Etype = %d)", Tconv(t, 0), t.Etype)
+		Fatalf("exporter: unexpected type: %v (Etype = %d)", t, t.Etype)
 	}
 }
 
 func (p *exporter) qualifiedName(sym *Sym) {
-	if strings.Contains(sym.Name, ".") {
-		Fatalf("exporter: invalid symbol name: %s", sym.Name)
-	}
 	p.string(sym.Name)
 	p.pkg(sym.Pkg)
 }
@@ -836,7 +846,7 @@ func (p *exporter) fieldList(t *Type) {
 
 func (p *exporter) field(f *Field) {
 	p.pos(f.Nname)
-	p.fieldName(f.Sym, f)
+	p.fieldName(f)
 	p.typ(f.Type)
 	p.string(f.Note)
 }
@@ -858,37 +868,24 @@ func (p *exporter) methodList(t *Type) {
 
 func (p *exporter) method(m *Field) {
 	p.pos(m.Nname)
-	p.fieldName(m.Sym, m)
+	p.fieldName(m)
 	p.paramList(m.Type.Params(), false)
 	p.paramList(m.Type.Results(), false)
 }
 
-// fieldName is like qualifiedName but it doesn't record the package
-// for blank (_) or exported names.
-func (p *exporter) fieldName(sym *Sym, t *Field) {
-	if t != nil && sym != t.Sym {
-		Fatalf("exporter: invalid fieldName parameters")
-	}
-
-	name := sym.Name
-	if t != nil {
-		if t.Embedded == 0 {
-			name = sym.Name
-		} else if bname := basetypeName(t.Type); bname != "" && !exportname(bname) {
-			// anonymous field with unexported base type name: use "?" as field name
-			// (bname != "" per spec, but we are conservative in case of errors)
-			name = "?"
-		} else {
-			name = ""
+// fieldName is like qualifiedName but it doesn't record the package for exported names.
+func (p *exporter) fieldName(t *Field) {
+	name := t.Sym.Name
+	if t.Embedded != 0 {
+		name = "" // anonymous field
+		if bname := basetypeName(t.Type); bname != "" && !exportname(bname) {
+			// anonymous field with unexported base type name
+			name = "?" // unexported name to force export of package
 		}
 	}
-
-	if strings.Contains(name, ".") {
-		Fatalf("exporter: invalid symbol name: %s", name)
-	}
 	p.string(name)
-	if name == "?" || name != "_" && name != "" && !exportname(name) {
-		p.pkg(sym.Pkg)
+	if name != "" && !exportname(name) {
+		p.pkg(t.Sym.Pkg)
 	}
 }
 
@@ -897,10 +894,8 @@ func basetypeName(t *Type) string {
 	if s == nil && t.IsPtr() {
 		s = t.Elem().Sym // deref
 	}
+	// s should exist, but be conservative
 	if s != nil {
-		if strings.Contains(s.Name, ".") {
-			Fatalf("exporter: invalid symbol name: %s", s.Name)
-		}
 		return s.Name
 	}
 	return ""
@@ -1034,7 +1029,7 @@ func (p *exporter) value(x Val) {
 		p.tag(tag)
 
 	case *Mpint:
-		if Minintval[TINT64].Cmp(x) <= 0 && x.Cmp(Maxintval[TINT64]) <= 0 {
+		if minintval[TINT64].Cmp(x) <= 0 && x.Cmp(maxintval[TINT64]) <= 0 {
 			// common case: x fits into an int64 - use compact encoding
 			p.tag(int64Tag)
 			p.int64(x.Int64())
@@ -1172,8 +1167,8 @@ func (p *exporter) elemList(list Nodes) {
 		if p.trace {
 			p.tracef("\n")
 		}
-		p.fieldSym(n.Left.Sym, false)
-		p.expr(n.Right)
+		p.fieldSym(n.Sym, false)
+		p.expr(n.Left)
 	}
 }
 
@@ -1206,9 +1201,6 @@ func (p *exporter) expr(n *Node) {
 	// case ODDDARG:
 	//	unimplemented - handled by default case
 
-	// case OREGISTER:
-	//	unimplemented - handled by default case
-
 	case OLITERAL:
 		if n.Val().Ctype() == CTNIL && n.Orig != nil && n.Orig != n {
 			p.expr(n.Orig)
@@ -1276,7 +1268,7 @@ func (p *exporter) expr(n *Node) {
 		p.typ(n.Type)
 		p.elemList(n.List) // special handling of field names
 
-	case OARRAYLIT, OMAPLIT:
+	case OARRAYLIT, OSLICELIT, OMAPLIT:
 		p.op(OCOMPLIT)
 		p.typ(n.Type)
 		p.exprList(n.List)
@@ -1285,6 +1277,9 @@ func (p *exporter) expr(n *Node) {
 		p.op(OKEY)
 		p.exprsOrNil(n.Left, n.Right)
 
+	// case OSTRUCTKEY:
+	//	unreachable - handled in case OSTRUCTLIT by elemList
+
 	// case OCALLPART:
 	//	unimplemented - handled by default case
 
@@ -1405,8 +1400,8 @@ func (p *exporter) expr(n *Node) {
 		p.op(ODCLCONST)
 
 	default:
-		Fatalf("cannot export %s (%d) node\n"+
-			"==> please file an issue and assign to gri@\n", n.Op, n.Op)
+		Fatalf("cannot export %v (%d) node\n"+
+			"==> please file an issue and assign to gri@\n", n.Op, int(n.Op))
 	}
 }
 
@@ -1436,17 +1431,7 @@ func (p *exporter) stmt(n *Node) {
 	switch op := n.Op; op {
 	case ODCL:
 		p.op(ODCL)
-		switch n.Left.Class {
-		case PPARAM, PPARAMOUT, PAUTO, PAUTOHEAP:
-			// TODO(gri) when is this not PAUTO?
-			// Also, originally this didn't look like
-			// the default case. Investigate.
-			fallthrough
-		default:
-			// TODO(gri) Can we ever reach here?
-			p.bool(false)
-			p.sym(n.Left)
-		}
+		p.sym(n.Left)
 		p.typ(n.Left.Type)
 
 	// case ODCLFIELD:
@@ -1531,7 +1516,7 @@ func (p *exporter) stmt(n *Node) {
 		p.expr(n.Left)
 
 	default:
-		Fatalf("exporter: CANNOT EXPORT: %s\nPlease notify gri@\n", n.Op)
+		Fatalf("exporter: CANNOT EXPORT: %v\nPlease notify gri@\n", n.Op)
 	}
 }
 
@@ -1623,7 +1608,7 @@ func (p *exporter) bool(b bool) bool {
 func (p *exporter) op(op Op) {
 	if p.trace {
 		p.tracef("[")
-		defer p.tracef("= %s] ", op)
+		defer p.tracef("= %v] ", op)
 	}
 
 	p.int(int(op))
@@ -1706,7 +1691,7 @@ func (p *exporter) marker(m byte) {
 	p.rawInt64(int64(p.written))
 }
 
-// rawInt64 should only be used by low-level encoders
+// rawInt64 should only be used by low-level encoders.
 func (p *exporter) rawInt64(x int64) {
 	var tmp [binary.MaxVarintLen64]byte
 	n := binary.PutVarint(tmp[:], x)
@@ -1715,6 +1700,14 @@ func (p *exporter) rawInt64(x int64) {
 	}
 }
 
+// rawStringln should only be used to emit the initial version string.
+func (p *exporter) rawStringln(s string) {
+	for i := 0; i < len(s); i++ {
+		p.rawByte(s[i])
+	}
+	p.rawByte('\n')
+}
+
 // rawByte is the bottleneck interface to write to p.out.
 // rawByte escapes b as follows (any encoding does that
 // hides '$'):
@@ -1803,6 +1796,9 @@ const (
 	stringTag
 	nilTag
 	unknownTag // not used by gc (only appears in packages with errors)
+
+	// Aliases
+	aliasTag
 )
 
 // Debugging support.
@@ -1838,6 +1834,9 @@ var tagString = [...]string{
 	-stringTag:   "string",
 	-nilTag:      "nil",
 	-unknownTag:  "unknown",
+
+	// Aliases
+	-aliasTag: "alias",
 }
 
 // untype returns the "pseudo" untyped type for a Ctype (import/export use only).
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index 2b666cc..1d66841 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -10,9 +10,11 @@ package gc
 
 import (
 	"bufio"
-	"cmd/compile/internal/big"
 	"encoding/binary"
 	"fmt"
+	"math/big"
+	"strconv"
+	"strings"
 )
 
 // The overall structure of Import is symmetric to Export: For each
@@ -23,7 +25,7 @@ import (
 type importer struct {
 	in      *bufio.Reader
 	buf     []byte // reused for reading strings
-	version string
+	version int    // export format version
 
 	// object lists, in order of deserialization
 	strList       []string
@@ -49,30 +51,56 @@ type importer struct {
 func Import(in *bufio.Reader) {
 	p := importer{
 		in:      in,
+		version: -1,           // unknown version
 		strList: []string{""}, // empty string is mapped to 0
 	}
 
-	// read low-level encoding format
-	switch format := p.rawByte(); format {
-	case 'c':
-		// compact format - nothing to do
-	case 'd':
-		p.debugFormat = true
-	default:
-		Fatalf("importer: invalid encoding format in export data: got %q; want 'c' or 'd'", format)
+	// read version info
+	var versionstr string
+	if b := p.rawByte(); b == 'c' || b == 'd' {
+		// Go1.7 encoding; first byte encodes low-level
+		// encoding format (compact vs debug).
+		// For backward-compatibility only (avoid problems with
+		// old installed packages). Newly compiled packages use
+		// the extensible format string.
+		// TODO(gri) Remove this support eventually; after Go1.8.
+		if b == 'd' {
+			p.debugFormat = true
+		}
+		p.trackAllTypes = p.rawByte() == 'a'
+		p.posInfoFormat = p.bool()
+		versionstr = p.string()
+		if versionstr == "v1" {
+			p.version = 0
+		}
+	} else {
+		// Go1.8 extensible encoding
+		// read version string and extract version number (ignore anything after the version number)
+		versionstr = p.rawStringln(b)
+		if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" {
+			if v, err := strconv.Atoi(s[1]); err == nil && v > 0 {
+				p.version = v
+			}
+		}
 	}
 
-	p.trackAllTypes = p.rawByte() == 'a'
-
-	p.posInfoFormat = p.bool()
+	// read version specific flags - extend as necessary
+	switch p.version {
+	// case 4:
+	// 	...
+	//	fallthrough
+	case 3, 2, 1:
+		p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
+		p.trackAllTypes = p.bool()
+		p.posInfoFormat = p.bool()
+	case 0:
+		// Go1.7 encoding format - nothing to do here
+	default:
+		formatErrorf("unknown export format version %d (%q)", p.version, versionstr)
+	}
 
 	// --- generic export data ---
 
-	p.version = p.string()
-	if p.version != exportVersion0 && p.version != exportVersion1 {
-		Fatalf("importer: unknown export data version: %s", p.version)
-	}
-
 	// populate typList with predeclared "known" types
 	p.typList = append(p.typList, predeclared()...)
 
@@ -80,7 +108,6 @@ func Import(in *bufio.Reader) {
 	p.pkg()
 
 	// defer some type-checking until all types are read in completely
-	// (parser.go:import_package)
 	tcok := typecheckok
 	typecheckok = true
 	defercheckwidth()
@@ -100,16 +127,13 @@ func Import(in *bufio.Reader) {
 
 	// self-verification
 	if count := p.int(); count != objcount {
-		Fatalf("importer: got %d objects; want %d", objcount, count)
+		formatErrorf("got %d objects; want %d", objcount, count)
 	}
 
 	// --- compiler-specific export data ---
 
 	// read compiler-specific flags
 
-	// read but ignore safemode bit (see issue #15772)
-	p.bool() // formerly: importpkg.Safe = p.bool()
-
 	// phase 2
 	objcount = 0
 	for {
@@ -123,12 +147,12 @@ func Import(in *bufio.Reader) {
 
 	// self-verification
 	if count := p.int(); count != objcount {
-		Fatalf("importer: got %d objects; want %d", objcount, count)
+		formatErrorf("got %d objects; want %d", objcount, count)
 	}
 
 	// read inlineable functions bodies
 	if dclcontext != PEXTERN {
-		Fatalf("importer: unexpected context %d", dclcontext)
+		formatErrorf("unexpected context %d", dclcontext)
 	}
 
 	objcount = 0
@@ -140,12 +164,12 @@ func Import(in *bufio.Reader) {
 
 		// don't process the same function twice
 		if i <= i0 {
-			Fatalf("importer: index not increasing: %d <= %d", i, i0)
+			formatErrorf("index not increasing: %d <= %d", i, i0)
 		}
 		i0 = i
 
-		if Funcdepth != 0 {
-			Fatalf("importer: unexpected Funcdepth %d", Funcdepth)
+		if funcdepth != 0 {
+			formatErrorf("unexpected Funcdepth %d", funcdepth)
 		}
 
 		// Note: In the original code, funchdr and funcbody are called for
@@ -163,7 +187,7 @@ func Import(in *bufio.Reader) {
 				// (not doing so can cause significant performance
 				// degradation due to unnecessary calls to empty
 				// functions).
-				body = []*Node{Nod(OEMPTY, nil, nil)}
+				body = []*Node{nod(OEMPTY, nil, nil)}
 			}
 			f.Func.Inl.Set(body)
 			funcbody(f)
@@ -179,11 +203,11 @@ func Import(in *bufio.Reader) {
 
 	// self-verification
 	if count := p.int(); count != objcount {
-		Fatalf("importer: got %d functions; want %d", objcount, count)
+		formatErrorf("got %d functions; want %d", objcount, count)
 	}
 
 	if dclcontext != PEXTERN {
-		Fatalf("importer: unexpected context %d", dclcontext)
+		formatErrorf("unexpected context %d", dclcontext)
 	}
 
 	p.verifyTypes()
@@ -196,19 +220,32 @@ func Import(in *bufio.Reader) {
 	testdclstack() // debugging only
 }
 
+func formatErrorf(format string, args ...interface{}) {
+	if debugFormat {
+		Fatalf(format, args...)
+	}
+
+	yyerror("cannot import %q due to version skew - reinstall package (%s)",
+		importpkg.Path, fmt.Sprintf(format, args...))
+	errorexit()
+}
+
 func (p *importer) verifyTypes() {
 	for _, pair := range p.cmpList {
 		pt := pair.pt
 		t := pair.t
-		if !Eqtype(pt.Orig, t) {
-			// TODO(gri) Is this a possible regular error (stale files)
-			// or can this only happen if export/import is flawed?
-			// (if the latter, change to Fatalf here)
-			Yyerror("inconsistent definition for type %v during import\n\t%v (in %q)\n\t%v (in %q)", pt.Sym, Tconv(pt, FmtLong), pt.Sym.Importdef.Path, Tconv(t, FmtLong), importpkg.Path)
+		if !eqtype(pt.Orig, t) {
+			formatErrorf("inconsistent definition for type %v during import\n\t%L (in %q)\n\t%L (in %q)", pt.Sym, pt, pt.Sym.Importdef.Path, t, importpkg.Path)
 		}
 	}
 }
 
+// numImport tracks how often a package with a given name is imported.
+// It is used to provide a better error message (by using the package
+// path to disambiguate) if a package that appears multiple times with
+// the same name appears in an error message.
+var numImport = make(map[string]int)
+
 func (p *importer) pkg() *Pkg {
 	// if the package was seen before, i is its index (>= 0)
 	i := p.tagOrIndex()
@@ -218,7 +255,7 @@ func (p *importer) pkg() *Pkg {
 
 	// otherwise, i is the package tag (< 0)
 	if i != packageTag {
-		Fatalf("importer: expected package tag, found tag = %d", i)
+		formatErrorf("expected package tag, found tag = %d", i)
 	}
 
 	// read package data
@@ -227,21 +264,21 @@ func (p *importer) pkg() *Pkg {
 
 	// we should never see an empty package name
 	if name == "" {
-		Fatalf("importer: empty package name for path %q", path)
+		formatErrorf("empty package name for path %q", path)
 	}
 
 	// we should never see a bad import path
 	if isbadimport(path) {
-		Fatalf("importer: bad package path %q for package %s", path, name)
+		formatErrorf("bad package path %q for package %s", path, name)
 	}
 
 	// an empty path denotes the package we are currently importing;
 	// it must be the first package we see
 	if (path == "") != (len(p.pkgList) == 0) {
-		Fatalf("importer: package path %q for pkg index %d", path, len(p.pkgList))
+		formatErrorf("package path %q for pkg index %d", path, len(p.pkgList))
 	}
 
-	// see importimport (export.go)
+	// add package to pkgList
 	pkg := importpkg
 	if path != "" {
 		pkg = mkpkg(path)
@@ -250,10 +287,10 @@ func (p *importer) pkg() *Pkg {
 		pkg.Name = name
 		numImport[name]++
 	} else if pkg.Name != name {
-		Yyerror("importer: conflicting package names %s and %s for path %q", pkg.Name, name, path)
+		yyerror("conflicting package names %s and %s for path %q", pkg.Name, name, path)
 	}
-	if incannedimport == 0 && myimportpath != "" && path == myimportpath {
-		Yyerror("import %q: package depends on %q (import cycle)", importpkg.Path, path)
+	if myimportpath != "" && path == myimportpath {
+		yyerror("import %q: package depends on %q (import cycle)", importpkg.Path, path)
 		errorexit()
 	}
 	p.pkgList = append(p.pkgList, pkg)
@@ -293,12 +330,12 @@ func (p *importer) obj(tag int) {
 		params := p.paramList()
 		result := p.paramList()
 
-		sig := functype(nil, params, result)
+		sig := functypefield(nil, params, result)
 		importsym(sym, ONAME)
 		if sym.Def != nil && sym.Def.Op == ONAME {
 			// function was imported before (via another import)
-			if !Eqtype(sig, sym.Def.Type) {
-				Fatalf("importer: inconsistent definition for func %v during import\n\t%v\n\t%v", sym, sym.Def.Type, sig)
+			if !eqtype(sig, sym.Def.Type) {
+				formatErrorf("inconsistent definition for func %v during import\n\t%v\n\t%v", sym, sym.Def.Type, sig)
 			}
 			p.funcList = append(p.funcList, nil)
 			break
@@ -317,8 +354,19 @@ func (p *importer) obj(tag int) {
 			}
 		}
 
+	case aliasTag:
+		p.pos()
+		alias := importpkg.Lookup(p.string())
+		orig := p.qualifiedName()
+
+		// Although the protocol allows the alias to precede the original,
+		// this never happens in files produced by gc.
+		alias.Flags |= SymAlias
+		alias.Def = orig.Def
+		importsym(alias, orig.Def.Op)
+
 	default:
-		Fatalf("importer: unexpected object (tag = %d)", tag)
+		formatErrorf("unexpected object (tag = %d)", tag)
 	}
 }
 
@@ -351,35 +399,29 @@ func (p *importer) newtyp(etype EType) *Type {
 	return t
 }
 
-// This is like the function importtype but it delays the
-// type identity check for types that have been seen already.
-// importer.importtype and importtype and (export.go) need to
-// remain in sync.
+// importtype declares that pt, an imported named type, has underlying type t.
 func (p *importer) importtype(pt, t *Type) {
-	// override declaration in unsafe.go for Pointer.
-	// there is no way in Go code to define unsafe.Pointer
-	// so we have to supply it.
-	if incannedimport != 0 && importpkg.Name == "unsafe" && pt.Nod.Sym.Name == "Pointer" {
-		t = Types[TUNSAFEPTR]
-	}
-
 	if pt.Etype == TFORW {
-		n := pt.Nod
-		copytype(pt.Nod, t)
-		pt.Nod = n // unzero nod
+		n := pt.nod
+		copytype(pt.nod, t)
+		pt.nod = n // unzero nod
 		pt.Sym.Importdef = importpkg
 		pt.Sym.Lastlineno = lineno
 		declare(n, PEXTERN)
 		checkwidth(pt)
 	} else {
-		// pt.Orig and t must be identical. Since t may not be
-		// fully set up yet, collect the types and verify identity
-		// later.
-		p.cmpList = append(p.cmpList, struct{ pt, t *Type }{pt, t})
+		// pt.Orig and t must be identical.
+		if p.trackAllTypes {
+			// If we track all types, t may not be fully set up yet.
+			// Collect the types and verify identity later.
+			p.cmpList = append(p.cmpList, struct{ pt, t *Type }{pt, t})
+		} else if !eqtype(pt.Orig, t) {
+			yyerror("inconsistent definition for type %v during import\n\t%L (in %q)\n\t%L (in %q)", pt.Sym, pt, pt.Sym.Importdef.Path, t, importpkg.Path)
+		}
 	}
 
 	if Debug['E'] != 0 {
-		fmt.Printf("import type %v %v\n", pt, Tconv(t, FmtLong))
+		fmt.Printf("import type %v %L\n", pt, t)
 	}
 }
 
@@ -394,24 +436,15 @@ func (p *importer) typ() *Type {
 	var t *Type
 	switch i {
 	case namedTag:
-		// parser.go:hidden_importsym
 		p.pos()
 		tsym := p.qualifiedName()
 
-		// parser.go:hidden_pkgtype
 		t = pkgtype(tsym)
 		p.typList = append(p.typList, t)
 
 		// read underlying type
-		// parser.go:hidden_type
 		t0 := p.typ()
-		if p.trackAllTypes {
-			// If we track all types, we cannot check equality of previously
-			// imported types until later. Use customized version of importtype.
-			p.importtype(t, t0)
-		} else {
-			importtype(t, t0)
-		}
+		p.importtype(t, t0)
 
 		// interfaces don't have associated methods
 		if t0.IsInterface() {
@@ -425,24 +458,30 @@ func (p *importer) typ() *Type {
 
 		// read associated methods
 		for i := p.int(); i > 0; i-- {
-			// parser.go:hidden_fndcl
-
 			p.pos()
 			sym := p.fieldSym()
 
+			// during import unexported method names should be in the type's package
+			if !exportname(sym.Name) && sym.Pkg != tsym.Pkg {
+				Fatalf("imported method name %+v in wrong package %s\n", sym, tsym.Pkg.Name)
+			}
+
 			recv := p.paramList() // TODO(gri) do we need a full param list for the receiver?
 			params := p.paramList()
 			result := p.paramList()
+			nointerface := p.bool()
 
-			nointerface := false
-			if p.version == exportVersion1 {
-				nointerface = p.bool()
+			base := recv[0].Type
+			star := false
+			if base.IsPtr() {
+				base = base.Elem()
+				star = true
 			}
 
-			n := methodname1(newname(sym), recv[0].Right)
-			n.Type = functype(recv[0], params, result)
+			n := methodname0(sym, star, base.Sym)
+			n.Type = functypefield(recv[0], params, result)
 			checkwidth(n.Type)
-			addmethod(sym, n.Type, tsym.Pkg, false, nointerface)
+			addmethod(sym, n.Type, false, nointerface)
 			p.funcList = append(p.funcList, n)
 			importlist = append(importlist, n)
 
@@ -480,7 +519,8 @@ func (p *importer) typ() *Type {
 
 	case structTag:
 		t = p.newtyp(TSTRUCT)
-		tostruct0(t, p.fieldList())
+		t.SetFields(p.fieldList())
+		checkwidth(t)
 
 	case pointerTag:
 		t = p.newtyp(Tptr)
@@ -490,14 +530,15 @@ func (p *importer) typ() *Type {
 		t = p.newtyp(TFUNC)
 		params := p.paramList()
 		result := p.paramList()
-		functype0(t, nil, params, result)
+		functypefield0(t, nil, params, result)
 
 	case interfaceTag:
 		t = p.newtyp(TINTER)
 		if p.int() != 0 {
-			Fatalf("importer: unexpected embedded interface")
+			formatErrorf("unexpected embedded interface")
 		}
-		tointerface0(t, p.methodList())
+		t.SetFields(p.methodList())
+		checkwidth(t)
 
 	case mapTag:
 		t = p.newtyp(TMAP)
@@ -512,11 +553,11 @@ func (p *importer) typ() *Type {
 		ct.Elem = p.typ()
 
 	default:
-		Fatalf("importer: unexpected type (tag = %d)", i)
+		formatErrorf("unexpected type (tag = %d)", i)
 	}
 
 	if t == nil {
-		Fatalf("importer: nil type (type tag = %d)", i)
+		formatErrorf("nil type (type tag = %d)", i)
 	}
 
 	return t
@@ -528,10 +569,9 @@ func (p *importer) qualifiedName() *Sym {
 	return pkg.Lookup(name)
 }
 
-// parser.go:hidden_structdcl_list
-func (p *importer) fieldList() (fields []*Node) {
+func (p *importer) fieldList() (fields []*Field) {
 	if n := p.int(); n > 0 {
-		fields = make([]*Node, n)
+		fields = make([]*Field, n)
 		for i := range fields {
 			fields[i] = p.field()
 		}
@@ -539,38 +579,34 @@ func (p *importer) fieldList() (fields []*Node) {
 	return
 }
 
-// parser.go:hidden_structdcl
-func (p *importer) field() *Node {
+func (p *importer) field() *Field {
 	p.pos()
 	sym := p.fieldName()
 	typ := p.typ()
 	note := p.string()
 
-	var n *Node
-	if sym.Name != "" {
-		n = Nod(ODCLFIELD, newname(sym), typenod(typ))
-	} else {
+	f := newField()
+	if sym.Name == "" {
 		// anonymous field - typ must be T or *T and T must be a type name
 		s := typ.Sym
 		if s == nil && typ.IsPtr() {
 			s = typ.Elem().Sym // deref
 		}
-		pkg := importpkg
-		if sym != nil {
-			pkg = sym.Pkg
-		}
-		n = embedded(s, pkg)
-		n.Right = typenod(typ)
+		sym = sym.Pkg.Lookup(s.Name)
+		f.Embedded = 1
 	}
-	n.SetVal(Val{U: note})
 
-	return n
+	f.Sym = sym
+	f.Nname = newname(sym)
+	f.Type = typ
+	f.Note = note
+
+	return f
 }
 
-// parser.go:hidden_interfacedcl_list
-func (p *importer) methodList() (methods []*Node) {
+func (p *importer) methodList() (methods []*Field) {
 	if n := p.int(); n > 0 {
-		methods = make([]*Node, n)
+		methods = make([]*Field, n)
 		for i := range methods {
 			methods[i] = p.method()
 		}
@@ -578,25 +614,28 @@ func (p *importer) methodList() (methods []*Node) {
 	return
 }
 
-// parser.go:hidden_interfacedcl
-func (p *importer) method() *Node {
+func (p *importer) method() *Field {
 	p.pos()
 	sym := p.fieldName()
 	params := p.paramList()
 	result := p.paramList()
-	return Nod(ODCLFIELD, newname(sym), typenod(functype(fakethis(), params, result)))
+
+	f := newField()
+	f.Sym = sym
+	f.Nname = newname(sym)
+	f.Type = functypefield(fakethisfield(), params, result)
+	return f
 }
 
-// parser.go:sym,hidden_importsym
 func (p *importer) fieldName() *Sym {
 	name := p.string()
+	if p.version == 0 && name == "_" {
+		// version 0 didn't export a package for _ fields
+		// but used the builtin package instead
+		return builtinpkg.Lookup(name)
+	}
 	pkg := localpkg
-	if name == "_" {
-		// During imports, unqualified non-exported identifiers are from builtinpkg
-		// (see parser.go:sym). The binary exporter only exports blank as a non-exported
-		// identifier without qualification.
-		pkg = builtinpkg
-	} else if name == "?" || name != "" && !exportname(name) {
+	if name != "" && !exportname(name) {
 		if name == "?" {
 			name = ""
 		}
@@ -605,8 +644,7 @@ func (p *importer) fieldName() *Sym {
 	return pkg.Lookup(name)
 }
 
-// parser.go:ohidden_funarg_list
-func (p *importer) paramList() []*Node {
+func (p *importer) paramList() []*Field {
 	i := p.int()
 	if i == 0 {
 		return nil
@@ -618,31 +656,26 @@ func (p *importer) paramList() []*Node {
 		named = false
 	}
 	// i > 0
-	n := make([]*Node, i)
-	for i := range n {
-		n[i] = p.param(named)
+	fs := make([]*Field, i)
+	for i := range fs {
+		fs[i] = p.param(named)
 	}
-	return n
+	return fs
 }
 
-// parser.go:hidden_funarg
-func (p *importer) param(named bool) *Node {
-	typ := p.typ()
-
-	isddd := false
-	if typ.Etype == TDDDFIELD {
+func (p *importer) param(named bool) *Field {
+	f := newField()
+	f.Type = p.typ()
+	if f.Type.Etype == TDDDFIELD {
 		// TDDDFIELD indicates wrapped ... slice type
-		typ = typSlice(typ.DDDField())
-		isddd = true
+		f.Type = typSlice(f.Type.DDDField())
+		f.Isddd = true
 	}
 
-	n := Nod(ODCLFIELD, nil, typenod(typ))
-	n.Isddd = isddd
-
 	if named {
 		name := p.string()
 		if name == "" {
-			Fatalf("importer: expected named parameter")
+			formatErrorf("expected named parameter")
 		}
 		// TODO(gri) Supply function/method package rather than
 		// encoding the package for each parameter repeatedly.
@@ -650,14 +683,15 @@ func (p *importer) param(named bool) *Node {
 		if name != "_" {
 			pkg = p.pkg()
 		}
-		n.Left = newname(pkg.Lookup(name))
+		f.Sym = pkg.Lookup(name)
+		f.Nname = newname(f.Sym)
 	}
 
 	// TODO(gri) This is compiler-specific (escape info).
 	// Move into compiler-specific section eventually?
-	n.SetVal(Val{U: p.string()})
+	f.Note = p.string()
 
-	return n
+	return f
 }
 
 func (p *importer) value(typ *Type) (x Val) {
@@ -696,18 +730,18 @@ func (p *importer) value(typ *Type) (x Val) {
 		x.U = p.string()
 
 	case unknownTag:
-		Fatalf("importer: unknown constant (importing package with errors)")
+		formatErrorf("unknown constant (importing package with errors)")
 
 	case nilTag:
 		x.U = new(NilVal)
 
 	default:
-		Fatalf("importer: unexpected value tag %d", tag)
+		formatErrorf("unexpected value tag %d", tag)
 	}
 
 	// verify ideal type
 	if typ.IsUntyped() && untype(x.Ctype()) != typ {
-		Fatalf("importer: value %v and type %v don't match", x, typ)
+		formatErrorf("value %v and type %v don't match", x, typ)
 	}
 
 	return
@@ -778,7 +812,8 @@ func (p *importer) elemList() []*Node {
 	c := p.int()
 	list := make([]*Node, c)
 	for i := range list {
-		list[i] = Nod(OKEY, mkname(p.fieldSym()), p.expr())
+		s := p.fieldSym()
+		list[i] = nodSym(OSTRUCTKEY, p.expr(), s)
 	}
 	return list
 }
@@ -801,14 +836,20 @@ func (p *importer) node() *Node {
 	// case ODDDARG:
 	//	unimplemented
 
-	// case OREGISTER:
-	//	unimplemented
-
 	case OLITERAL:
 		typ := p.typ()
 		n := nodlit(p.value(typ))
 		if !typ.IsUntyped() {
-			conv := Nod(OCALL, typenod(typ), nil)
+			// Type-checking simplifies unsafe.Pointer(uintptr(c))
+			// to unsafe.Pointer(c) which then cannot type-checked
+			// again. Re-introduce explicit uintptr(c) conversion.
+			// (issue 16317).
+			if typ.IsUnsafePtr() {
+				conv := nod(OCALL, typenod(Types[TUINTPTR]), nil)
+				conv.List.Set1(n)
+				n = conv
+			}
+			conv := nod(OCALL, typenod(typ), nil)
 			conv.List.Set1(n)
 			n = conv
 		}
@@ -837,30 +878,33 @@ func (p *importer) node() *Node {
 		if !p.bool() /* !implicit, i.e. '&' operator */ {
 			if n.Op == OCOMPLIT {
 				// Special case for &T{...}: turn into (*T){...}.
-				n.Right = Nod(OIND, n.Right, nil)
+				n.Right = nod(OIND, n.Right, nil)
 				n.Right.Implicit = true
 			} else {
-				n = Nod(OADDR, n, nil)
+				n = nod(OADDR, n, nil)
 			}
 		}
 		return n
 
 	case OSTRUCTLIT:
-		n := Nod(OCOMPLIT, nil, typenod(p.typ()))
+		n := nod(OCOMPLIT, nil, typenod(p.typ()))
 		n.List.Set(p.elemList()) // special handling of field names
 		return n
 
-	// case OARRAYLIT, OMAPLIT:
+	// case OARRAYLIT, OSLICELIT, OMAPLIT:
 	// 	unreachable - mapped to case OCOMPLIT below by exporter
 
 	case OCOMPLIT:
-		n := Nod(OCOMPLIT, nil, typenod(p.typ()))
+		n := nod(OCOMPLIT, nil, typenod(p.typ()))
 		n.List.Set(p.exprList())
 		return n
 
 	case OKEY:
 		left, right := p.exprsOrNil()
-		return Nod(OKEY, left, right)
+		return nod(OKEY, left, right)
+
+	// case OSTRUCTKEY:
+	//	unreachable - handled in case OSTRUCTLIT by elemList
 
 	// case OCALLPART:
 	//	unimplemented
@@ -870,13 +914,13 @@ func (p *importer) node() *Node {
 
 	case OXDOT:
 		// see parser.new_dotname
-		return NodSym(OXDOT, p.expr(), p.fieldSym())
+		return nodSym(OXDOT, p.expr(), p.fieldSym())
 
 	// case ODOTTYPE, ODOTTYPE2:
 	// 	unreachable - mapped to case ODOTTYPE below by exporter
 
 	case ODOTTYPE:
-		n := Nod(ODOTTYPE, p.expr(), nil)
+		n := nod(ODOTTYPE, p.expr(), nil)
 		if p.bool() {
 			n.Right = p.expr()
 		} else {
@@ -888,10 +932,10 @@ func (p *importer) node() *Node {
 	// 	unreachable - mapped to cases below by exporter
 
 	case OINDEX:
-		return Nod(op, p.expr(), p.expr())
+		return nod(op, p.expr(), p.expr())
 
 	case OSLICE, OSLICE3:
-		n := Nod(op, p.expr(), nil)
+		n := nod(op, p.expr(), nil)
 		low, high := p.exprsOrNil()
 		var max *Node
 		if n.Op.IsSlice3() {
@@ -904,7 +948,7 @@ func (p *importer) node() *Node {
 	// 	unreachable - mapped to OCONV case below by exporter
 
 	case OCONV:
-		n := Nod(OCALL, typenod(p.typ()), nil)
+		n := nod(OCALL, typenod(p.typ()), nil)
 		n.List.Set(p.exprList())
 		return n
 
@@ -920,7 +964,7 @@ func (p *importer) node() *Node {
 	// 	unreachable - mapped to OCALL case below by exporter
 
 	case OCALL:
-		n := Nod(OCALL, p.expr(), nil)
+		n := nod(OCALL, p.expr(), nil)
 		n.List.Set(p.exprList())
 		n.Isddd = p.bool()
 		return n
@@ -933,18 +977,18 @@ func (p *importer) node() *Node {
 
 	// unary expressions
 	case OPLUS, OMINUS, OADDR, OCOM, OIND, ONOT, ORECV:
-		return Nod(op, p.expr(), nil)
+		return nod(op, p.expr(), nil)
 
 	// binary expressions
 	case OADD, OAND, OANDAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT,
 		OLSH, OMOD, OMUL, ONE, OOR, OOROR, ORSH, OSEND, OSUB, OXOR:
-		return Nod(op, p.expr(), p.expr())
+		return nod(op, p.expr(), p.expr())
 
 	case OADDSTR:
 		list := p.exprList()
 		x := list[0]
 		for _, y := range list[1:] {
-			x = Nod(OADD, x, y)
+			x = nod(OADD, x, y)
 		}
 		return x
 
@@ -953,19 +997,19 @@ func (p *importer) node() *Node {
 
 	case ODCLCONST:
 		// TODO(gri) these should not be exported in the first place
-		return Nod(OEMPTY, nil, nil)
+		return nod(OEMPTY, nil, nil)
 
 	// --------------------------------------------------------------------
 	// statements
 	case ODCL:
-		var lhs *Node
-		if p.bool() {
-			lhs = p.expr()
-		} else {
-			lhs = dclname(p.sym())
+		if p.version < 2 {
+			// versions 0 and 1 exported a bool here but it
+			// was always false - simply ignore in this case
+			p.bool()
 		}
-		// TODO(gri) avoid list created here!
-		return liststmt(variter([]*Node{lhs}, typenod(p.typ()), nil))
+		lhs := dclname(p.sym())
+		typ := typenod(p.typ())
+		return liststmt(variter([]*Node{lhs}, typ, nil)) // TODO(gri) avoid list creation
 
 	// case ODCLFIELD:
 	//	unimplemented
@@ -974,14 +1018,14 @@ func (p *importer) node() *Node {
 	// 	unreachable - mapped to OAS case below by exporter
 
 	case OAS:
-		return Nod(OAS, p.expr(), p.expr())
+		return nod(OAS, p.expr(), p.expr())
 
 	case OASOP:
-		n := Nod(OASOP, nil, nil)
+		n := nod(OASOP, nil, nil)
 		n.Etype = EType(p.int())
 		n.Left = p.expr()
 		if !p.bool() {
-			n.Right = Nodintconst(1)
+			n.Right = nodintconst(1)
 			n.Implicit = true
 		} else {
 			n.Right = p.expr()
@@ -992,13 +1036,13 @@ func (p *importer) node() *Node {
 	// 	unreachable - mapped to OAS2 case below by exporter
 
 	case OAS2:
-		n := Nod(OAS2, nil, nil)
+		n := nod(OAS2, nil, nil)
 		n.List.Set(p.exprList())
 		n.Rlist.Set(p.exprList())
 		return n
 
 	case ORETURN:
-		n := Nod(ORETURN, nil, nil)
+		n := nod(ORETURN, nil, nil)
 		n.List.Set(p.exprList())
 		return n
 
@@ -1006,11 +1050,11 @@ func (p *importer) node() *Node {
 	// 	unreachable - generated by compiler for trampolin routines (not exported)
 
 	case OPROC, ODEFER:
-		return Nod(op, p.expr(), nil)
+		return nod(op, p.expr(), nil)
 
 	case OIF:
 		markdcl()
-		n := Nod(OIF, nil, nil)
+		n := nod(OIF, nil, nil)
 		n.Ninit.Set(p.stmtList())
 		n.Left = p.expr()
 		n.Nbody.Set(p.stmtList())
@@ -1020,7 +1064,7 @@ func (p *importer) node() *Node {
 
 	case OFOR:
 		markdcl()
-		n := Nod(OFOR, nil, nil)
+		n := nod(OFOR, nil, nil)
 		n.Ninit.Set(p.stmtList())
 		n.Left, n.Right = p.exprsOrNil()
 		n.Nbody.Set(p.stmtList())
@@ -1029,7 +1073,7 @@ func (p *importer) node() *Node {
 
 	case ORANGE:
 		markdcl()
-		n := Nod(ORANGE, nil, nil)
+		n := nod(ORANGE, nil, nil)
 		n.List.Set(p.stmtList())
 		n.Right = p.expr()
 		n.Nbody.Set(p.stmtList())
@@ -1038,7 +1082,7 @@ func (p *importer) node() *Node {
 
 	case OSELECT, OSWITCH:
 		markdcl()
-		n := Nod(op, nil, nil)
+		n := nod(op, nil, nil)
 		n.Ninit.Set(p.stmtList())
 		n.Left, _ = p.exprsOrNil()
 		n.List.Set(p.stmtList())
@@ -1050,7 +1094,7 @@ func (p *importer) node() *Node {
 
 	case OXCASE:
 		markdcl()
-		n := Nod(OXCASE, nil, nil)
+		n := nod(OXCASE, nil, nil)
 		n.Xoffset = int64(block)
 		n.List.Set(p.exprList())
 		// TODO(gri) eventually we must declare variables for type switch
@@ -1063,7 +1107,7 @@ func (p *importer) node() *Node {
 	// 	unreachable - mapped to OXFALL case below by exporter
 
 	case OXFALL:
-		n := Nod(OXFALL, nil, nil)
+		n := nod(OXFALL, nil, nil)
 		n.Xoffset = int64(block)
 		return n
 
@@ -1072,13 +1116,13 @@ func (p *importer) node() *Node {
 		if left != nil {
 			left = newname(left.Sym)
 		}
-		return Nod(op, left, nil)
+		return nod(op, left, nil)
 
 	// case OEMPTY:
 	// 	unreachable - not emitted by exporter
 
 	case OGOTO, OLABEL:
-		n := Nod(op, newname(p.expr().Sym), nil)
+		n := nod(op, newname(p.expr().Sym), nil)
 		n.Sym = dclstack // context, for goto restrictions
 		return n
 
@@ -1086,14 +1130,14 @@ func (p *importer) node() *Node {
 		return nil
 
 	default:
-		Fatalf("cannot import %s (%d) node\n"+
-			"==> please file an issue and assign to gri@\n", op, op)
+		Fatalf("cannot import %v (%d) node\n"+
+			"==> please file an issue and assign to gri@\n", op, int(op))
 		panic("unreachable") // satisfy compiler
 	}
 }
 
 func builtinCall(op Op) *Node {
-	return Nod(OCALL, mkname(builtinpkg.Lookup(goopnames[op])), nil)
+	return nod(OCALL, mkname(builtinpkg.Lookup(goopnames[op])), nil)
 }
 
 func (p *importer) exprsOrNil() (a, b *Node) {
@@ -1147,7 +1191,7 @@ func (p *importer) tagOrIndex() int {
 func (p *importer) int() int {
 	x := p.int64()
 	if int64(int(x)) != x {
-		Fatalf("importer: exported integer too large")
+		formatErrorf("exported integer too large")
 	}
 	return int(x)
 }
@@ -1186,24 +1230,34 @@ func (p *importer) string() string {
 
 func (p *importer) marker(want byte) {
 	if got := p.rawByte(); got != want {
-		Fatalf("importer: incorrect marker: got %c; want %c (pos = %d)", got, want, p.read)
+		formatErrorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read)
 	}
 
 	pos := p.read
 	if n := int(p.rawInt64()); n != pos {
-		Fatalf("importer: incorrect position: got %d; want %d", n, pos)
+		formatErrorf("incorrect position: got %d; want %d", n, pos)
 	}
 }
 
-// rawInt64 should only be used by low-level decoders
+// rawInt64 should only be used by low-level decoders.
 func (p *importer) rawInt64() int64 {
 	i, err := binary.ReadVarint(p)
 	if err != nil {
-		Fatalf("importer: read error: %v", err)
+		formatErrorf("read error: %v", err)
 	}
 	return i
 }
 
+// rawStringln should only be used to read the initial version string.
+func (p *importer) rawStringln(b byte) string {
+	p.buf = p.buf[:0]
+	for b != '\n' {
+		p.buf = append(p.buf, b)
+		b = p.rawByte()
+	}
+	return string(p.buf)
+}
+
 // needed for binary.ReadVarint in rawInt64
 func (p *importer) ReadByte() (byte, error) {
 	return p.rawByte(), nil
@@ -1216,13 +1270,13 @@ func (p *importer) rawByte() byte {
 	c, err := p.in.ReadByte()
 	p.read++
 	if err != nil {
-		Fatalf("importer: read error: %v", err)
+		formatErrorf("read error: %v", err)
 	}
 	if c == '|' {
 		c, err = p.in.ReadByte()
 		p.read++
 		if err != nil {
-			Fatalf("importer: read error: %v", err)
+			formatErrorf("read error: %v", err)
 		}
 		switch c {
 		case 'S':
@@ -1230,7 +1284,7 @@ func (p *importer) rawByte() byte {
 		case '|':
 			// nothing to do
 		default:
-			Fatalf("importer: unexpected escape sequence in export data")
+			formatErrorf("unexpected escape sequence in export data")
 		}
 	}
 	return c
diff --git a/src/cmd/compile/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go
index c1a6418..e02e2fe 100644
--- a/src/cmd/compile/internal/gc/builtin.go
+++ b/src/cmd/compile/internal/gc/builtin.go
@@ -2,109 +2,238 @@
 
 package gc
 
-const runtimeimport = "" +
-	"cn\x00\x03v1\x01\rruntime\x00\t\x11newobject\x00\x02\x17\"\vtyp·2\x00\x00" +
-	"\x01\x17:\x00\t\x13panicindex\x00\x00\x00\t\x13panicslice\x00\x00\x00\t\x15pani" +
-	"cdivide\x00\x00\x00\t\x15throwreturn\x00\x00\x00\t\x11throwinit\x00\x00\x00" +
-	"\t\x11panicwrap\x00\x05 \x00 \x00 \x00\x00\t\rgopanic\x00\x01\x1b\x00\x00\x00\x00\t\x11go" +
-	"recover\x00\x01\x17\b\x00\x01\x1b\x00\x00\x00\t\x11printbool\x00\x01\x00\x00\x00\t\x13print" +
-	"float\x00\x01\x1a\x00\x00\t\x0fprintint\x00\x01\n\x00\x00\t\x0fprinthex\x00\x01\x14\x00\x00" +
-	"\t\x11printuint\x00\x01\x14\x00\x00\t\x17printcomplex\x00\x01\x1e\x00\x00\t\x15pri" +
-	"ntstring\x00\x01 \x00\x00\t\x17printpointer\x00\x01:\x00\x00\t\x13printi" +
-	"face\x00\x01:\x00\x00\t\x13printeface\x00\x01:\x00\x00\t\x13printslice\x00\x01" +
-	":\x00\x00\t\rprintnl\x00\x00\x00\t\rprintsp\x00\x00\x00\t\x11printlock\x00\x00" +
-	"\x00\t\x15printunlock\x00\x00\x00\t\x19concatstring2\x00\x05\x17\x0f@\"\x00 " +
-	"\x00 \x00\x01 \x00\t\x19concatstring3\x00\a\x17\x0f@\"\x00 \x00 \x00 \x00\x01 \x00\t\x19c" +
-	"oncatstring4\x00\t\x17\x0f@\"\x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatst" +
-	"ring5\x00\v\x17\x0f@\"\x00 \x00 \x00 \x00 \x00 \x00\x01 \x00\t\x19concatstrings" +
-	"\x00\x03\x17\x0f@\"\x00\x11 \x00\x01 \x00\t\x11cmpstring\x00\x03 \x00 \x00\x01\x02\x00\t\x0feqstr" +
-	"ing\x00\x03 \x00 \x00\x01\x00\x00\t\x11intstring\x00\x03\x17\x0f\b\"\x00\n\x00\x01 \x00\t!sli" +
-	"cebytetostring\x00\x03\x17\x0f@\"\x00\x11\"\x00\x01 \x00\t'slicebyteto" +
-	"stringtmp\x00\x01\x11\"\x00\x01 \x00\t!slicerunetostring\x00\x03\x17\x0f" +
-	"@\"\x00\x11|S\x00\x01 \x00\t!stringtoslicebyte\x00\x03\x17\x0f@\"\x00 \x00\x01\x11" +
-	"\"\x00\t'stringtoslicebytetmp\x00\x01 \x00\x01\x11\"\x00\t!string" +
-	"toslicerune\x00\x03\x17\x0f@|S\x00 \x00\x01\x11|S\x00\t\x13stringiter\x00\x03" +
-	" \x00\x02\x00\x01\x02\x00\t\x15stringiter2\x00\x03 \x00\x02\x00\x04\x02\rretk·1\x00\x00|S" +
-	"\rretv·2\x00\x00\t\x11slicecopy\x00\x06:\tto·2\x00\x00:\tfr·3\x00" +
-	"\x00\x16\vwid·4\x00\x1bunsafe-uintptr\x01\x02\x00\t\x1dslicestrin" +
-	"gcopy\x00\x04:^\x00\x00:`\x00\x00\x01\x02\x00\t\rconvI2E\x00\x02:\relem·2\x00\x00" +
-	"\x02:\vret·1\x00\x00\t\rconvI2I\x00\x04\x17\"\b\x00\x00:\relem·3\x00\x00\x02:" +
-	"l\x00\x00\t\rconvT2E\x00\x06\x17\"\b\x00\x00\x17:p\x00\x00\x17:\vbuf·4\x00\x00\x02:l\x00\x00" +
-	"\t\rconvT2I\x00\x06\x17\"\vtab·2\x00\x00\x17:p\x00\x00\x17:t\x00\x00\x02:l\x00\x00\t\x11a" +
-	"ssertE2E\x00\x06\x17\"\vtyp·1\x00\x00:\x0fiface·2\x00\x00\x17:\vret\xc2" +
-	"\xb73\x00\x00\x00\t\x13assertE2E2\x00\x06\x17\"\b\x00\x00:\x0fiface·3\x00\x00\x17:\vr" +
-	"et·4\x00\x00\x01\x00\x00\t\x11assertE2I\x00\x06\x17\"||\x00\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00" +
-	"\t\x13assertE2I2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00\x17:\x86\x01\x00\x00\x01\x00\x00\t\x11asser" +
-	"tE2T\x00\x06\x17\"||\x00\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00\t\x13assertE2T2\x00\x06\x17\"\b" +
-	"\x00\x00:\x84\x01\x00\x00\x17:\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2E\x00\x06\x17\"||\x00\x00:~\x00\x00\x17" +
-	":\x80\x01\x00\x00\x00\t\x13assertI2E2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00\x17:\x86\x01\x00\x00\x01\x00\x00\t" +
-	"\x11assertI2I\x00\x06\x17\"||\x00\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00\t\x13assertI2I" +
-	"2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00\x17:\x86\x01\x00\x00\x01\x00\x00\t\x11assertI2T\x00\x06\x17\"||\x00" +
-	"\x00:~\x00\x00\x17:\x80\x01\x00\x00\x00\t\x13assertI2T2\x00\x06\x17\"\b\x00\x00:\x84\x01\x00\x00\x17:\x86\x01" +
-	"\x00\x00\x01\x00\x00\t\x17panicdottype\x00\x06\x17\"\rhave·1\x00\x00\x17\"\rwant" +
-	"·2\x00\x00\x17\"\x84\x01\x00\x00\x00\t\rifaceeq\x00\x04:\ti1·2\x00\x00:\ti2·3\x00" +
-	"\x00\x02\x00l\x00\x00\t\refaceeq\x00\x04:\xa4\x01\x00\x00:\xa6\x01\x00\x00\x02\x00l\x00\x00\t\rmakema" +
-	"p\x00\b\x17\"\x13mapType·2\x00\x00\n\rhint·3\x00\x00\x17:\x11mapbuf·" +
-	"4\x00\x00\x17:\x17bucketbuf·5\x00\x00\x02\x1d::\rhmap·1\x00\x00\t\x13mapa" +
-	"ccess1\x00\x06\x17\"\xac\x01\x00\x00\x1d::\rhmap·3\x00\x00\x17:\vkey·4\x00\x00\x02\x17" +
-	":\vval·1\x00\x00\t!mapaccess1_fast32\x00\x06\x17\"\xac\x01\x00\x00\x1d::" +
-	"\xb8\x01\x00\x00:\xba\x01\x00\x00\x02\x17:\xbc\x01\x00\x00\t!mapaccess1_fast64\x00\x06\x17\"\xac" +
-	"\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02\x17:\xbc\x01\x00\x00\t#mapaccess1_fasts" +
-	"tr\x00\x06\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00:\xba\x01\x00\x00\x02\x17:\xbc\x01\x00\x00\t\x1bmapaccess" +
-	"1_fat\x00\b\x17\"\xac\x01\x00\x00\x1d::\xb8\x01\x00\x00\x17:\xba\x01\x00\x00\x17\"\rzero·5\x00\x00\x02\x17" +
-	":\xbc\x01\x00\x00\t\x13mapaccess2\x00\x06\x17\"\x13mapType·3\x00\x00\x1d::\rhm" +
-	"ap·4\x00\x00\x17:\vkey·5\x00\x00\x04\x17:\xbc\x01\x00\x00\x00\rpres·2\x00\x00\t!ma" +
-	"paccess2_fast32\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00:\xce\x01\x00\x00\x04\x17:\xbc\x01" +
-	"\x00\x00\x00\xd0\x01\x00\x00\t!mapaccess2_fast64\x00\x06\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00" +
-	"\x00:\xce\x01\x00\x00\x04\x17:\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t#mapaccess2_faststr\x00\x06" +
-	"\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00:\xce\x01\x00\x00\x04\x17:\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t\x1bmapacces" +
-	"s2_fat\x00\b\x17\"\xca\x01\x00\x00\x1d::\xcc\x01\x00\x00\x17:\xce\x01\x00\x00\x17\"\rzero·6\x00\x00\x04" +
-	"\x17:\xbc\x01\x00\x00\x00\xd0\x01\x00\x00\t\x13mapassign1\x00\b\x17\"\x13mapType·1\x00\x00" +
-	"\x1d::\rhmap·2\x00\x00\x17:\vkey·3\x00\x00\x17:\vval·4\x00\x00\x00\t\x15ma" +
-	"piterinit\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00\x17:\x0fhiter·3\x00\x00\x00\t\x11" +
-	"mapdelete\x00\x06\x17\"\xde\x01\x00\x00\x1d::\xe0\x01\x00\x00\x17:\xe2\x01\x00\x00\x00\t\x15mapiter" +
-	"next\x00\x02\x17:\x0fhiter·1\x00\x00\x00\t\x0fmakechan\x00\x04\x17\"\x15chanT" +
-	"ype·2\x00\x00\n\xae\x01\x00\x00\x02\x1f\x06:\x0fhchan·1\x00\x00\t\x11chanrecv1\x00" +
-	"\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0fhchan·2\x00\x00\x17:p\x00\x00\x00\t\x11c" +
-	"hanrecv2\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x02:\x0fhchan·3\x00\x00\x17:\relem·4" +
-	"\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17\"\xf8\x01\x00\x00\x1f\x04:\xfa\x01\x00\x00\x17:p\x00\x00\x00\t\x11c" +
-	"losechan\x00\x02:\xf4\x01\x00\x00\x00\a\x17writeBarrier\x00\x15\x06\renable" +
-	"d\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00\t\x1dwritebarrierptr\x00\x04" +
-	"\x17:\vdst·1\x00\x00:\vsrc·2\x00\x00\x00\t\x17typedmemmove\x00\x06\x17\"" +
-	"||\x00\x00\x17:\vdst·2\x00\x00\x17:\vsrc·3\x00\x00\x00\t\x1btypedslicec" +
-	"opy\x00\x06\x17\"\b\x00\x00:\vdst·3\x00\x00:\vsrc·4\x00\x00\x01\x02\x00\t\x17selec" +
-	"tnbsend\x00\x06\x17\"\xf2\x01\x00\x00\x1f\x04:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x01\x00\x00\t\x17selectn" +
-	"brecv\x00\x06\x17\"\xf2\x01\x00\x00\x17:p\x00\x00\x1f\x02:\x0fhchan·4\x00\x00\x01\x00\x00\t\x19sel" +
-	"ectnbrecv2\x00\b\x17\"\xf2\x01\x00\x00\x17:p\x00\x00\x17\x00\x15received·4\x00\x00\x1f" +
-	"\x02:\x0fhchan·5\x00\x00\x01\x00\x00\t\x11newselect\x00\x06\x17\"\vsel·1\x00\x00" +
-	"\n\x13selsize·2\x00\x00\b\rsize·3\x00\x00\x00\t\x13selectsend\x00\x06" +
-	"\x17\"\vsel·2\x00\x00\x1f\x04:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x02\x00\x15selected·1\x00\x00" +
-	"\t\x13selectrecv\x00\x06\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x02\x00\xb8\x02\x00\x00\t" +
-	"\x15selectrecv2\x00\b\x17\"\xb6\x02\x00\x00\x1f\x02:\xfe\x01\x00\x00\x17:\x80\x02\x00\x00\x17\x00\x15rece" +
-	"ived·5\x00\x00\x02\x00\xb8\x02\x00\x00\t\x19selectdefault\x00\x02\x17\"\xb6\x02\x00\x00\x02\x00" +
-	"\xb8\x02\x00\x00\t\x0fselectgo\x00\x02\x17\"\xae\x02\x00\x00\x00\t\tblock\x00\x00\x00\t\x11makes" +
-	"lice\x00\x06\x17\"\b\x00\x00\n\vnel·3\x00\x00\n\vcap·4\x00\x00\x02\x11:\vary·" +
-	"1\x00\x00\t\x11growslice\x00\x06\x17\"\b\x00\x00\x11:\vold·3\x00\x00\x02\xca\x02\x00\x00\x02\x11:" +
-	"\xcc\x02\x00\x00\t\rmemmove\x00\x06\x17:\tto·1\x00\x00\x17:\vfrm·2\x00\x00\x16\x11le" +
-	"ngth·3\x00d\x00\t\vmemclr\x00\x04\x17\"\vptr·1\x00\x00\x16\x11length\xc2" +
-	"\xb72\x00d\x00\t\x0fmemequal\x00\x06\x17:\ax·2\x00\x00\x17:\ay·3\x00\x00\x16\rsiz" +
-	"e·4\x00d\x01\x00\x00\t\x11memequal8\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x13m" +
-	"emequal16\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal32\x00\x04" +
-	"\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x13memequal64\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00" +
-	"\x00\x01\x00\x00\t\x15memequal128\x00\x04\x17:\xe2\x02\x00\x00\x17:\xe4\x02\x00\x00\x01\x00\x00\t\x0fint6" +
-	"4div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x0fint64" +
-	"mod\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod\x00\x03\x14\x00\x14\x00\x01\x14\x00\t\x1bfloat6" +
-	"4toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64touint64\x00\x01\x1a\x00\x01\x14\x00\t" +
-	"\x1bint64tofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1duint64tofloat64\x00" +
-	"\x01\x14\x00\x01\x1a\x00\t\x19complex128div\x00\x04\x1e\vnum·2\x00\x00\x1e\vden·" +
-	"3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19racefuncenter\x00\x01\x16d\x00\t\x17race" +
-	"funcexit\x00\x00\x00\t\x0fraceread\x00\x01\x16d\x00\t\x11racewrite\x00\x01\x16" +
-	"d\x00\t\x19racereadrange\x00\x04\x16\raddr·1\x00d\x16\rsize·2\x00" +
-	"d\x00\t\x1bracewriterange\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\t\x0fmsanrea" +
-	"d\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\t\x11msanwrite\x00\x04\x16\x94\x03\x00d\x16\x96\x03\x00d\x00\v\xf4" +
-	"\x01\x02\v\x00\x01\x00\n$$\n"
+var runtimeDecls = [...]struct {
+	name string
+	tag  int
+	typ  int
+}{
+	{"newobject", funcTag, 4},
+	{"panicindex", funcTag, 5},
+	{"panicslice", funcTag, 5},
+	{"panicdivide", funcTag, 5},
+	{"throwinit", funcTag, 5},
+	{"panicwrap", funcTag, 7},
+	{"gopanic", funcTag, 9},
+	{"gorecover", funcTag, 12},
+	{"printbool", funcTag, 14},
+	{"printfloat", funcTag, 16},
+	{"printint", funcTag, 18},
+	{"printhex", funcTag, 20},
+	{"printuint", funcTag, 20},
+	{"printcomplex", funcTag, 22},
+	{"printstring", funcTag, 23},
+	{"printpointer", funcTag, 24},
+	{"printiface", funcTag, 24},
+	{"printeface", funcTag, 24},
+	{"printslice", funcTag, 24},
+	{"printnl", funcTag, 5},
+	{"printsp", funcTag, 5},
+	{"printlock", funcTag, 5},
+	{"printunlock", funcTag, 5},
+	{"concatstring2", funcTag, 27},
+	{"concatstring3", funcTag, 28},
+	{"concatstring4", funcTag, 29},
+	{"concatstring5", funcTag, 30},
+	{"concatstrings", funcTag, 32},
+	{"cmpstring", funcTag, 34},
+	{"eqstring", funcTag, 35},
+	{"intstring", funcTag, 38},
+	{"slicebytetostring", funcTag, 40},
+	{"slicebytetostringtmp", funcTag, 41},
+	{"slicerunetostring", funcTag, 44},
+	{"stringtoslicebyte", funcTag, 45},
+	{"stringtoslicerune", funcTag, 48},
+	{"decoderune", funcTag, 49},
+	{"slicecopy", funcTag, 51},
+	{"slicestringcopy", funcTag, 52},
+	{"convI2I", funcTag, 53},
+	{"convT2E", funcTag, 54},
+	{"convT2I", funcTag, 54},
+	{"assertE2I", funcTag, 53},
+	{"assertE2I2", funcTag, 55},
+	{"assertI2I", funcTag, 53},
+	{"assertI2I2", funcTag, 55},
+	{"panicdottype", funcTag, 56},
+	{"panicnildottype", funcTag, 57},
+	{"ifaceeq", funcTag, 58},
+	{"efaceeq", funcTag, 58},
+	{"makemap", funcTag, 60},
+	{"mapaccess1", funcTag, 61},
+	{"mapaccess1_fast32", funcTag, 62},
+	{"mapaccess1_fast64", funcTag, 62},
+	{"mapaccess1_faststr", funcTag, 62},
+	{"mapaccess1_fat", funcTag, 63},
+	{"mapaccess2", funcTag, 64},
+	{"mapaccess2_fast32", funcTag, 65},
+	{"mapaccess2_fast64", funcTag, 65},
+	{"mapaccess2_faststr", funcTag, 65},
+	{"mapaccess2_fat", funcTag, 66},
+	{"mapassign", funcTag, 61},
+	{"mapiterinit", funcTag, 67},
+	{"mapdelete", funcTag, 67},
+	{"mapiternext", funcTag, 68},
+	{"makechan", funcTag, 70},
+	{"chanrecv1", funcTag, 72},
+	{"chanrecv2", funcTag, 73},
+	{"chansend1", funcTag, 75},
+	{"closechan", funcTag, 24},
+	{"writeBarrier", varTag, 76},
+	{"writebarrierptr", funcTag, 77},
+	{"typedmemmove", funcTag, 78},
+	{"typedmemclr", funcTag, 79},
+	{"typedslicecopy", funcTag, 80},
+	{"selectnbsend", funcTag, 81},
+	{"selectnbrecv", funcTag, 82},
+	{"selectnbrecv2", funcTag, 84},
+	{"newselect", funcTag, 85},
+	{"selectsend", funcTag, 81},
+	{"selectrecv", funcTag, 73},
+	{"selectrecv2", funcTag, 86},
+	{"selectdefault", funcTag, 87},
+	{"selectgo", funcTag, 57},
+	{"block", funcTag, 5},
+	{"makeslice", funcTag, 89},
+	{"makeslice64", funcTag, 90},
+	{"growslice", funcTag, 91},
+	{"memmove", funcTag, 92},
+	{"memclrNoHeapPointers", funcTag, 93},
+	{"memclrHasPointers", funcTag, 93},
+	{"memequal", funcTag, 94},
+	{"memequal8", funcTag, 95},
+	{"memequal16", funcTag, 95},
+	{"memequal32", funcTag, 95},
+	{"memequal64", funcTag, 95},
+	{"memequal128", funcTag, 95},
+	{"int64div", funcTag, 96},
+	{"uint64div", funcTag, 97},
+	{"int64mod", funcTag, 96},
+	{"uint64mod", funcTag, 97},
+	{"float64toint64", funcTag, 98},
+	{"float64touint64", funcTag, 99},
+	{"float64touint32", funcTag, 101},
+	{"int64tofloat64", funcTag, 102},
+	{"uint64tofloat64", funcTag, 103},
+	{"uint32tofloat64", funcTag, 104},
+	{"complex128div", funcTag, 105},
+	{"racefuncenter", funcTag, 106},
+	{"racefuncexit", funcTag, 5},
+	{"raceread", funcTag, 106},
+	{"racewrite", funcTag, 106},
+	{"racereadrange", funcTag, 107},
+	{"racewriterange", funcTag, 107},
+	{"msanread", funcTag, 107},
+	{"msanwrite", funcTag, 107},
+}
 
-const unsafeimport = "" +
-	"cn\x00\x03v1\x01\vunsafe\x00\x05\r\rPointer\x00\x16\x00\t\x0fOffsetof\x00\x01" +
-	":\x00\x01\x16\x00\t\vSizeof\x00\x01:\x00\x01\x16\x00\t\rAlignof\x00\x01:\x00\x01\x16\x00\v\b\x00\v" +
-	"\x00\x01\x00\n$$\n"
+func runtimeTypes() []*Type {
+	var typs [108]*Type
+	typs[0] = bytetype
+	typs[1] = typPtr(typs[0])
+	typs[2] = Types[TANY]
+	typs[3] = typPtr(typs[2])
+	typs[4] = functype(nil, []*Node{anonfield(typs[1])}, []*Node{anonfield(typs[3])})
+	typs[5] = functype(nil, nil, nil)
+	typs[6] = Types[TSTRING]
+	typs[7] = functype(nil, []*Node{anonfield(typs[6]), anonfield(typs[6]), anonfield(typs[6])}, nil)
+	typs[8] = Types[TINTER]
+	typs[9] = functype(nil, []*Node{anonfield(typs[8])}, nil)
+	typs[10] = Types[TINT32]
+	typs[11] = typPtr(typs[10])
+	typs[12] = functype(nil, []*Node{anonfield(typs[11])}, []*Node{anonfield(typs[8])})
+	typs[13] = Types[TBOOL]
+	typs[14] = functype(nil, []*Node{anonfield(typs[13])}, nil)
+	typs[15] = Types[TFLOAT64]
+	typs[16] = functype(nil, []*Node{anonfield(typs[15])}, nil)
+	typs[17] = Types[TINT64]
+	typs[18] = functype(nil, []*Node{anonfield(typs[17])}, nil)
+	typs[19] = Types[TUINT64]
+	typs[20] = functype(nil, []*Node{anonfield(typs[19])}, nil)
+	typs[21] = Types[TCOMPLEX128]
+	typs[22] = functype(nil, []*Node{anonfield(typs[21])}, nil)
+	typs[23] = functype(nil, []*Node{anonfield(typs[6])}, nil)
+	typs[24] = functype(nil, []*Node{anonfield(typs[2])}, nil)
+	typs[25] = typArray(typs[0], 32)
+	typs[26] = typPtr(typs[25])
+	typs[27] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[6]), anonfield(typs[6])}, []*Node{anonfield(typs[6])})
+	typs[28] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[6]), anonfield(typs[6]), anonfield(typs[6])}, []*Node{anonfield(typs[6])})
+	typs[29] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[6]), anonfield(typs[6]), anonfield(typs[6]), anonfield(typs[6])}, []*Node{anonfield(typs[6])})
+	typs[30] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[6]), anonfield(typs[6]), anonfield(typs[6]), anonfield(typs[6]), anonfield(typs[6])}, []*Node{anonfield(typs[6])})
+	typs[31] = typSlice(typs[6])
+	typs[32] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[31])}, []*Node{anonfield(typs[6])})
+	typs[33] = Types[TINT]
+	typs[34] = functype(nil, []*Node{anonfield(typs[6]), anonfield(typs[6])}, []*Node{anonfield(typs[33])})
+	typs[35] = functype(nil, []*Node{anonfield(typs[6]), anonfield(typs[6])}, []*Node{anonfield(typs[13])})
+	typs[36] = typArray(typs[0], 4)
+	typs[37] = typPtr(typs[36])
+	typs[38] = functype(nil, []*Node{anonfield(typs[37]), anonfield(typs[17])}, []*Node{anonfield(typs[6])})
+	typs[39] = typSlice(typs[0])
+	typs[40] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[39])}, []*Node{anonfield(typs[6])})
+	typs[41] = functype(nil, []*Node{anonfield(typs[39])}, []*Node{anonfield(typs[6])})
+	typs[42] = runetype
+	typs[43] = typSlice(typs[42])
+	typs[44] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[43])}, []*Node{anonfield(typs[6])})
+	typs[45] = functype(nil, []*Node{anonfield(typs[26]), anonfield(typs[6])}, []*Node{anonfield(typs[39])})
+	typs[46] = typArray(typs[42], 32)
+	typs[47] = typPtr(typs[46])
+	typs[48] = functype(nil, []*Node{anonfield(typs[47]), anonfield(typs[6])}, []*Node{anonfield(typs[43])})
+	typs[49] = functype(nil, []*Node{anonfield(typs[6]), anonfield(typs[33])}, []*Node{anonfield(typs[42]), anonfield(typs[33])})
+	typs[50] = Types[TUINTPTR]
+	typs[51] = functype(nil, []*Node{anonfield(typs[2]), anonfield(typs[2]), anonfield(typs[50])}, []*Node{anonfield(typs[33])})
+	typs[52] = functype(nil, []*Node{anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[33])})
+	typs[53] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])})
+	typs[54] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])})
+	typs[55] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[13])})
+	typs[56] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil)
+	typs[57] = functype(nil, []*Node{anonfield(typs[1])}, nil)
+	typs[58] = functype(nil, []*Node{anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[13])})
+	typs[59] = typMap(typs[2], typs[2])
+	typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[17]), anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[59])})
+	typs[61] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3])}, []*Node{anonfield(typs[3])})
+	typs[62] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[2])}, []*Node{anonfield(typs[3])})
+	typs[63] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])})
+	typs[64] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[13])})
+	typs[65] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[13])})
+	typs[66] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[13])})
+	typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3])}, nil)
+	typs[68] = functype(nil, []*Node{anonfield(typs[3])}, nil)
+	typs[69] = typChan(typs[2], Cboth)
+	typs[70] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[17])}, []*Node{anonfield(typs[69])})
+	typs[71] = typChan(typs[2], Crecv)
+	typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[71]), anonfield(typs[3])}, nil)
+	typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[71]), anonfield(typs[3])}, []*Node{anonfield(typs[13])})
+	typs[74] = typChan(typs[2], Csend)
+	typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[74]), anonfield(typs[3])}, nil)
+	typs[76] = tostruct([]*Node{namedfield("enabled", typs[13]), namedfield("needed", typs[13]), namedfield("cgo", typs[13])})
+	typs[77] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[2])}, nil)
+	typs[78] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil)
+	typs[79] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil)
+	typs[80] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[33])})
+	typs[81] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[74]), anonfield(typs[3])}, []*Node{anonfield(typs[13])})
+	typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[71])}, []*Node{anonfield(typs[13])})
+	typs[83] = typPtr(typs[13])
+	typs[84] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[83]), anonfield(typs[71])}, []*Node{anonfield(typs[13])})
+	typs[85] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[17]), anonfield(typs[10])}, nil)
+	typs[86] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[71]), anonfield(typs[3]), anonfield(typs[83])}, []*Node{anonfield(typs[13])})
+	typs[87] = functype(nil, []*Node{anonfield(typs[1])}, []*Node{anonfield(typs[13])})
+	typs[88] = typSlice(typs[2])
+	typs[89] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[33]), anonfield(typs[33])}, []*Node{anonfield(typs[88])})
+	typs[90] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[17]), anonfield(typs[17])}, []*Node{anonfield(typs[88])})
+	typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[88]), anonfield(typs[33])}, []*Node{anonfield(typs[88])})
+	typs[92] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[50])}, nil)
+	typs[93] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[50])}, nil)
+	typs[94] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[50])}, []*Node{anonfield(typs[13])})
+	typs[95] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[13])})
+	typs[96] = functype(nil, []*Node{anonfield(typs[17]), anonfield(typs[17])}, []*Node{anonfield(typs[17])})
+	typs[97] = functype(nil, []*Node{anonfield(typs[19]), anonfield(typs[19])}, []*Node{anonfield(typs[19])})
+	typs[98] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[17])})
+	typs[99] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[19])})
+	typs[100] = Types[TUINT32]
+	typs[101] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[100])})
+	typs[102] = functype(nil, []*Node{anonfield(typs[17])}, []*Node{anonfield(typs[15])})
+	typs[103] = functype(nil, []*Node{anonfield(typs[19])}, []*Node{anonfield(typs[15])})
+	typs[104] = functype(nil, []*Node{anonfield(typs[100])}, []*Node{anonfield(typs[15])})
+	typs[105] = functype(nil, []*Node{anonfield(typs[21]), anonfield(typs[21])}, []*Node{anonfield(typs[21])})
+	typs[106] = functype(nil, []*Node{anonfield(typs[50])}, nil)
+	typs[107] = functype(nil, []*Node{anonfield(typs[50]), anonfield(typs[50])}, nil)
+	return typs[:]
+}
diff --git a/src/cmd/compile/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go
index e9316cb..98e25fe 100644
--- a/src/cmd/compile/internal/gc/builtin/runtime.go
+++ b/src/cmd/compile/internal/gc/builtin/runtime.go
@@ -16,7 +16,6 @@ func newobject(typ *byte) *any
 func panicindex()
 func panicslice()
 func panicdivide()
-func throwreturn()
 func throwinit()
 func panicwrap(string, string, string)
 
@@ -52,33 +51,23 @@ func slicebytetostring(*[32]byte, []byte) string
 func slicebytetostringtmp([]byte) string
 func slicerunetostring(*[32]byte, []rune) string
 func stringtoslicebyte(*[32]byte, string) []byte
-func stringtoslicebytetmp(string) []byte
 func stringtoslicerune(*[32]rune, string) []rune
-func stringiter(string, int) int
-func stringiter2(string, int) (retk int, retv rune)
+func decoderune(string, int) (retv rune, retk int)
 func slicecopy(to any, fr any, wid uintptr) int
 func slicestringcopy(to any, fr any) int
 
 // interface conversions
-func convI2E(elem any) (ret any)
 func convI2I(typ *byte, elem any) (ret any)
-func convT2E(typ *byte, elem, buf *any) (ret any)
-func convT2I(tab *byte, elem, buf *any) (ret any)
+func convT2E(typ *byte, elem *any) (ret any)
+func convT2I(tab *byte, elem *any) (ret any)
 
 // interface type assertions  x.(T)
-func assertE2E(typ *byte, iface any, ret *any)
-func assertE2E2(typ *byte, iface any, ret *any) bool
-func assertE2I(typ *byte, iface any, ret *any)
-func assertE2I2(typ *byte, iface any, ret *any) bool
-func assertE2T(typ *byte, iface any, ret *any)
-func assertE2T2(typ *byte, iface any, ret *any) bool
-func assertI2E(typ *byte, iface any, ret *any)
-func assertI2E2(typ *byte, iface any, ret *any) bool
-func assertI2I(typ *byte, iface any, ret *any)
-func assertI2I2(typ *byte, iface any, ret *any) bool
-func assertI2T(typ *byte, iface any, ret *any)
-func assertI2T2(typ *byte, iface any, ret *any) bool
+func assertE2I(typ *byte, iface any) (ret any)
+func assertE2I2(typ *byte, iface any) (ret any, b bool)
+func assertI2I(typ *byte, iface any) (ret any)
+func assertI2I2(typ *byte, iface any) (ret any, b bool)
 func panicdottype(have, want, iface *byte)
+func panicnildottype(want *byte)
 
 func ifaceeq(i1 any, i2 any) (ret bool)
 func efaceeq(i1 any, i2 any) (ret bool)
@@ -95,7 +84,7 @@ func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres
 func mapaccess2_fast64(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
 func mapaccess2_faststr(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
 func mapaccess2_fat(mapType *byte, hmap map[any]any, key *any, zero *byte) (val *any, pres bool)
-func mapassign1(mapType *byte, hmap map[any]any, key *any, val *any)
+func mapassign(mapType *byte, hmap map[any]any, key *any) (val *any)
 func mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
 func mapdelete(mapType *byte, hmap map[any]any, key *any)
 func mapiternext(hiter *any)
@@ -117,6 +106,7 @@ func writebarrierptr(dst *any, src any)
 
 // *byte is really *runtime.Type
 func typedmemmove(typ *byte, dst *any, src *any)
+func typedmemclr(typ *byte, dst *any)
 func typedslicecopy(typ *byte, dst any, src any) int
 
 func selectnbsend(chanType *byte, hchan chan<- any, elem *any) bool
@@ -131,10 +121,12 @@ func selectdefault(sel *byte) (selected bool)
 func selectgo(sel *byte)
 func block()
 
-func makeslice(typ *byte, nel int64, cap int64) (ary []any)
+func makeslice(typ *byte, len int, cap int) (ary []any)
+func makeslice64(typ *byte, len int64, cap int64) (ary []any)
 func growslice(typ *byte, old []any, cap int) (ary []any)
 func memmove(to *any, frm *any, length uintptr)
-func memclr(ptr *byte, length uintptr)
+func memclrNoHeapPointers(ptr *byte, length uintptr)
+func memclrHasPointers(ptr *byte, length uintptr)
 
 func memequal(x, y *any, size uintptr) bool
 func memequal8(x, y *any) bool
@@ -150,8 +142,10 @@ func int64mod(int64, int64) int64
 func uint64mod(uint64, uint64) uint64
 func float64toint64(float64) int64
 func float64touint64(float64) uint64
+func float64touint32(float64) uint32
 func int64tofloat64(int64) float64
 func uint64tofloat64(uint64) float64
+func uint32tofloat64(uint32) float64
 
 func complex128div(num complex128, den complex128) (quo complex128)
 
diff --git a/src/cmd/compile/internal/gc/builtin/unsafe.go b/src/cmd/compile/internal/gc/builtin/unsafe.go
deleted file mode 100644
index 6e25db6..0000000
--- a/src/cmd/compile/internal/gc/builtin/unsafe.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// NOTE: If you change this file you must run "go generate"
-// to update builtin.go. This is not done automatically
-// to avoid depending on having a working compiler binary.
-
-// +build ignore
-
-package unsafe
-
-type Pointer uintptr // not really; filled in by compiler
-
-// return types here are ignored; see unsafe.go
-func Offsetof(any) uintptr
-func Sizeof(any) uintptr
-func Alignof(any) uintptr
diff --git a/src/cmd/compile/internal/gc/builtin_test.go b/src/cmd/compile/internal/gc/builtin_test.go
index 94111e6..31b0785 100644
--- a/src/cmd/compile/internal/gc/builtin_test.go
+++ b/src/cmd/compile/internal/gc/builtin_test.go
@@ -20,7 +20,7 @@ func TestBuiltin(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	new, err := exec.Command("go", "run", "mkbuiltin.go", "-stdout").Output()
+	new, err := exec.Command(testenv.GoToolPath(t), "run", "mkbuiltin.go", "-stdout").Output()
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/src/cmd/compile/internal/gc/bv.go b/src/cmd/compile/internal/gc/bv.go
index d1c2192..183105f 100644
--- a/src/cmd/compile/internal/gc/bv.go
+++ b/src/cmd/compile/internal/gc/bv.go
@@ -4,8 +4,6 @@
 
 package gc
 
-import "fmt"
-
 const (
 	WORDBITS  = 32
 	WORDMASK  = WORDBITS - 1
@@ -44,14 +42,7 @@ func (b *bulkBvec) next() bvec {
 	return out
 }
 
-// difference
-func bvandnot(dst bvec, src1 bvec, src2 bvec) {
-	for i, x := range src1.b {
-		dst.b[i] = x &^ src2.b[i]
-	}
-}
-
-func bveq(bv1 bvec, bv2 bvec) bool {
+func (bv1 bvec) Eq(bv2 bvec) bool {
 	if bv1.n != bv2.n {
 		Fatalf("bvequal: lengths %d and %d are not equal", bv1.n, bv2.n)
 	}
@@ -63,22 +54,31 @@ func bveq(bv1 bvec, bv2 bvec) bool {
 	return true
 }
 
-func bvcopy(dst bvec, src bvec) {
+func (dst bvec) Copy(src bvec) {
 	for i, x := range src.b {
 		dst.b[i] = x
 	}
 }
 
-func bvget(bv bvec, i int32) int {
+func (bv bvec) Get(i int32) bool {
 	if i < 0 || i >= bv.n {
 		Fatalf("bvget: index %d is out of bounds with length %d\n", i, bv.n)
 	}
-	return int((bv.b[i>>WORDSHIFT] >> uint(i&WORDMASK)) & 1)
+	mask := uint32(1 << uint(i%WORDBITS))
+	return bv.b[i>>WORDSHIFT]&mask != 0
+}
+
+func (bv bvec) Set(i int32) {
+	if i < 0 || i >= bv.n {
+		Fatalf("bvset: index %d is out of bounds with length %d\n", i, bv.n)
+	}
+	mask := uint32(1 << uint(i%WORDBITS))
+	bv.b[i/WORDBITS] |= mask
 }
 
 // bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
 // If there is no such index, bvnext returns -1.
-func bvnext(bv bvec, i int32) int32 {
+func (bv bvec) Next(i int32) int32 {
 	if i >= bv.n {
 		return -1
 	}
@@ -107,7 +107,7 @@ func bvnext(bv bvec, i int32) int32 {
 	return i
 }
 
-func bvisempty(bv bvec) bool {
+func (bv bvec) IsEmpty() bool {
 	for i := int32(0); i < bv.n; i += WORDBITS {
 		if bv.b[i>>WORDSHIFT] != 0 {
 			return false
@@ -116,7 +116,7 @@ func bvisempty(bv bvec) bool {
 	return true
 }
 
-func bvnot(bv bvec) {
+func (bv bvec) Not() {
 	i := int32(0)
 	w := int32(0)
 	for ; i < bv.n; i, w = i+WORDBITS, w+1 {
@@ -125,36 +125,41 @@ func bvnot(bv bvec) {
 }
 
 // union
-func bvor(dst bvec, src1 bvec, src2 bvec) {
+func (dst bvec) Or(src1, src2 bvec) {
 	for i, x := range src1.b {
 		dst.b[i] = x | src2.b[i]
 	}
 }
 
 // intersection
-func bvand(dst bvec, src1 bvec, src2 bvec) {
+func (dst bvec) And(src1, src2 bvec) {
 	for i, x := range src1.b {
 		dst.b[i] = x & src2.b[i]
 	}
 }
 
-func bvprint(bv bvec) {
-	fmt.Printf("#*")
-	for i := int32(0); i < bv.n; i++ {
-		fmt.Printf("%d", bvget(bv, i))
+// difference
+func (dst bvec) AndNot(src1, src2 bvec) {
+	for i, x := range src1.b {
+		dst.b[i] = x &^ src2.b[i]
 	}
 }
 
-func bvresetall(bv bvec) {
-	for i := range bv.b {
-		bv.b[i] = 0
+func (bv bvec) String() string {
+	s := make([]byte, 2+bv.n)
+	copy(s, "#*")
+	for i := int32(0); i < bv.n; i++ {
+		ch := byte('0')
+		if bv.Get(i) {
+			ch = '1'
+		}
+		s[2+i] = ch
 	}
+	return string(s)
 }
 
-func bvset(bv bvec, i int32) {
-	if i < 0 || i >= bv.n {
-		Fatalf("bvset: index %d is out of bounds with length %d\n", i, bv.n)
+func (bv bvec) Clear() {
+	for i := range bv.b {
+		bv.b[i] = 0
 	}
-	mask := uint32(1 << uint(i%WORDBITS))
-	bv.b[i/WORDBITS] |= mask
 }
diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go
deleted file mode 100644
index 74fe463..0000000
--- a/src/cmd/compile/internal/gc/cgen.go
+++ /dev/null
@@ -1,3600 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gc
-
-import (
-	"cmd/internal/obj"
-	"cmd/internal/obj/ppc64"
-	"cmd/internal/sys"
-	"fmt"
-)
-
-// generate:
-//	res = n;
-// simplifies and calls Thearch.Gmove.
-// if wb is true, need to emit write barriers.
-func Cgen(n, res *Node) {
-	cgen_wb(n, res, false)
-}
-
-func cgen_wb(n, res *Node, wb bool) {
-	if Debug['g'] != 0 {
-		op := "cgen"
-		if wb {
-			op = "cgen_wb"
-		}
-		Dump("\n"+op+"-n", n)
-		Dump(op+"-res", res)
-	}
-
-	if n == nil || n.Type == nil {
-		return
-	}
-
-	if res == nil || res.Type == nil {
-		Fatalf("cgen: res nil")
-	}
-
-	for n.Op == OCONVNOP {
-		n = n.Left
-	}
-
-	switch n.Op {
-	case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
-		cgen_slice(n, res, wb)
-		return
-
-	case OEFACE:
-		if res.Op != ONAME || !res.Addable || wb {
-			var n1 Node
-			Tempname(&n1, n.Type)
-			Cgen_eface(n, &n1)
-			cgen_wb(&n1, res, wb)
-		} else {
-			Cgen_eface(n, res)
-		}
-		return
-
-	case ODOTTYPE:
-		cgen_dottype(n, res, nil, wb)
-		return
-
-	case OAPPEND:
-		cgen_append(n, res)
-		return
-	}
-
-	if n.Ullman >= UINF {
-		if n.Op == OINDREG {
-			Fatalf("cgen: this is going to miscompile")
-		}
-		if res.Ullman >= UINF {
-			var n1 Node
-			Tempname(&n1, n.Type)
-			Cgen(n, &n1)
-			cgen_wb(&n1, res, wb)
-			return
-		}
-	}
-
-	if Isfat(n.Type) {
-		if n.Type.Width < 0 {
-			Fatalf("forgot to compute width for %v", n.Type)
-		}
-		sgen_wb(n, res, n.Type.Width, wb)
-		return
-	}
-
-	if !res.Addable {
-		if n.Ullman > res.Ullman {
-			if Ctxt.Arch.RegSize == 4 && Is64(n.Type) {
-				var n1 Node
-				Tempname(&n1, n.Type)
-				Cgen(n, &n1)
-				cgen_wb(&n1, res, wb)
-				return
-			}
-
-			var n1 Node
-			Regalloc(&n1, n.Type, res)
-			Cgen(n, &n1)
-			if n1.Ullman > res.Ullman {
-				Dump("n1", &n1)
-				Dump("res", res)
-				Fatalf("loop in cgen")
-			}
-
-			cgen_wb(&n1, res, wb)
-			Regfree(&n1)
-			return
-		}
-
-		if res.Ullman < UINF {
-			if Complexop(n, res) {
-				Complexgen(n, res)
-				return
-			}
-
-			f := true // gen through register
-			switch n.Op {
-			case OLITERAL:
-				if Smallintconst(n) {
-					f = false
-				}
-
-			case OREGISTER:
-				f = false
-			}
-
-			if !n.Type.IsComplex() && Ctxt.Arch.RegSize == 8 && !wb {
-				a := Thearch.Optoas(OAS, res.Type)
-				var addr obj.Addr
-				if Thearch.Sudoaddable(a, res, &addr) {
-					var p1 *obj.Prog
-					if f {
-						var n2 Node
-						Regalloc(&n2, res.Type, nil)
-						Cgen(n, &n2)
-						p1 = Thearch.Gins(a, &n2, nil)
-						Regfree(&n2)
-					} else {
-						p1 = Thearch.Gins(a, n, nil)
-					}
-					p1.To = addr
-					if Debug['g'] != 0 {
-						fmt.Printf("%v [ignore previous line]\n", p1)
-					}
-					Thearch.Sudoclean()
-					return
-				}
-			}
-		}
-
-		if Ctxt.Arch.Family == sys.I386 {
-			// no registers to speak of
-			var n1, n2 Node
-			Tempname(&n1, n.Type)
-			Cgen(n, &n1)
-			Igen(res, &n2, nil)
-			cgen_wb(&n1, &n2, wb)
-			Regfree(&n2)
-			return
-		}
-
-		var n1 Node
-		Igen(res, &n1, nil)
-		cgen_wb(n, &n1, wb)
-		Regfree(&n1)
-		return
-	}
-
-	// update addressability for string, slice
-	// can't do in walk because n->left->addable
-	// changes if n->left is an escaping local variable.
-	switch n.Op {
-	case OSPTR, OLEN:
-		if n.Left.Type.IsSlice() || n.Left.Type.IsString() {
-			n.Addable = n.Left.Addable
-		}
-
-	case OCAP:
-		if n.Left.Type.IsSlice() {
-			n.Addable = n.Left.Addable
-		}
-
-	case OITAB:
-		n.Addable = n.Left.Addable
-	}
-
-	if wb {
-		if Simtype[res.Type.Etype] != Tptr {
-			Fatalf("cgen_wb of type %v", res.Type)
-		}
-		if n.Ullman >= UINF {
-			var n1 Node
-			Tempname(&n1, n.Type)
-			Cgen(n, &n1)
-			n = &n1
-		}
-		cgen_wbptr(n, res)
-		return
-	}
-
-	// Write barrier now handled. Code below this line can ignore wb.
-
-	if Ctxt.Arch.Family == sys.ARM { // TODO(rsc): Maybe more often?
-		// if both are addressable, move
-		if n.Addable && res.Addable {
-			if Is64(n.Type) || Is64(res.Type) || n.Op == OREGISTER || res.Op == OREGISTER || n.Type.IsComplex() || res.Type.IsComplex() {
-				Thearch.Gmove(n, res)
-			} else {
-				var n1 Node
-				Regalloc(&n1, n.Type, nil)
-				Thearch.Gmove(n, &n1)
-				Cgen(&n1, res)
-				Regfree(&n1)
-			}
-
-			return
-		}
-
-		// if both are not addressable, use a temporary.
-		if !n.Addable && !res.Addable {
-			// could use regalloc here sometimes,
-			// but have to check for ullman >= UINF.
-			var n1 Node
-			Tempname(&n1, n.Type)
-			Cgen(n, &n1)
-			Cgen(&n1, res)
-			return
-		}
-
-		// if result is not addressable directly but n is,
-		// compute its address and then store via the address.
-		if !res.Addable {
-			var n1 Node
-			Igen(res, &n1, nil)
-			Cgen(n, &n1)
-			Regfree(&n1)
-			return
-		}
-	}
-
-	if Complexop(n, res) {
-		Complexgen(n, res)
-		return
-	}
-
-	if Ctxt.Arch.InFamily(sys.AMD64, sys.I386, sys.S390X) && n.Addable {
-		Thearch.Gmove(n, res)
-		return
-	}
-
-	if Ctxt.Arch.InFamily(sys.ARM64, sys.MIPS64, sys.PPC64) {
-		// if both are addressable, move
-		if n.Addable {
-			if n.Op == OREGISTER || res.Op == OREGISTER {
-				Thearch.Gmove(n, res)
-			} else {
-				var n1 Node
-				Regalloc(&n1, n.Type, nil)
-				Thearch.Gmove(n, &n1)
-				Cgen(&n1, res)
-				Regfree(&n1)
-			}
-			return
-		}
-	}
-
-	// if n is sudoaddable generate addr and move
-	if Ctxt.Arch.Family == sys.ARM && !Is64(n.Type) && !Is64(res.Type) && !n.Type.IsComplex() && !res.Type.IsComplex() {
-		a := Thearch.Optoas(OAS, n.Type)
-		var addr obj.Addr
-		if Thearch.Sudoaddable(a, n, &addr) {
-			if res.Op != OREGISTER {
-				var n2 Node
-				Regalloc(&n2, res.Type, nil)
-				p1 := Thearch.Gins(a, nil, &n2)
-				p1.From = addr
-				if Debug['g'] != 0 {
-					fmt.Printf("%v [ignore previous line]\n", p1)
-				}
-				Thearch.Gmove(&n2, res)
-				Regfree(&n2)
-			} else {
-				p1 := Thearch.Gins(a, nil, res)
-				p1.From = addr
-				if Debug['g'] != 0 {
-					fmt.Printf("%v [ignore previous line]\n", p1)
-				}
-			}
-			Thearch.Sudoclean()
-			return
-		}
-	}
-
-	nl := n.Left
-	nr := n.Right
-
-	if nl != nil && nl.Ullman >= UINF {
-		if nr != nil && nr.Ullman >= UINF {
-			var n1 Node
-			Tempname(&n1, nl.Type)
-			Cgen(nl, &n1)
-			n2 := *n
-			n2.Left = &n1
-			Cgen(&n2, res)
-			return
-		}
-	}
-
-	// 64-bit ops are hard on 32-bit machine.
-	if Ctxt.Arch.RegSize == 4 && (Is64(n.Type) || Is64(res.Type) || n.Left != nil && Is64(n.Left.Type)) {
-		switch n.Op {
-		// math goes to cgen64.
-		case OMINUS,
-			OCOM,
-			OADD,
-			OSUB,
-			OMUL,
-			OLROT,
-			OLSH,
-			ORSH,
-			OAND,
-			OOR,
-			OXOR:
-			Thearch.Cgen64(n, res)
-			return
-		}
-	}
-
-	if Thearch.Cgen_float != nil && nl != nil && n.Type.IsFloat() && nl.Type.IsFloat() {
-		Thearch.Cgen_float(n, res)
-		return
-	}
-
-	if !n.Type.IsComplex() && Ctxt.Arch.RegSize == 8 {
-		a := Thearch.Optoas(OAS, n.Type)
-		var addr obj.Addr
-		if Thearch.Sudoaddable(a, n, &addr) {
-			if res.Op == OREGISTER {
-				p1 := Thearch.Gins(a, nil, res)
-				p1.From = addr
-			} else {
-				var n2 Node
-				Regalloc(&n2, n.Type, nil)
-				p1 := Thearch.Gins(a, nil, &n2)
-				p1.From = addr
-				Thearch.Gins(a, &n2, res)
-				Regfree(&n2)
-			}
-
-			Thearch.Sudoclean()
-			return
-		}
-	}
-
-	var a obj.As
-	switch n.Op {
-	default:
-		Dump("cgen", n)
-		Dump("cgen-res", res)
-		Fatalf("cgen: unknown op %v", Nconv(n, FmtShort|FmtSign))
-
-	case OOROR, OANDAND,
-		OEQ, ONE,
-		OLT, OLE,
-		OGE, OGT,
-		ONOT:
-		Bvgen(n, res, true)
-		return
-
-	case OPLUS:
-		Cgen(nl, res)
-		return
-
-		// unary
-	case OCOM:
-		a := Thearch.Optoas(OXOR, nl.Type)
-
-		var n1 Node
-		Regalloc(&n1, nl.Type, nil)
-		Cgen(nl, &n1)
-		var n2 Node
-		Nodconst(&n2, nl.Type, -1)
-		Thearch.Gins(a, &n2, &n1)
-		cgen_norm(n, &n1, res)
-		return
-
-	case OMINUS:
-		if nl.Type.IsFloat() {
-			nr = Nodintconst(-1)
-			nr = convlit(nr, n.Type)
-			a = Thearch.Optoas(OMUL, nl.Type)
-			goto sbop
-		}
-
-		a := Thearch.Optoas(n.Op, nl.Type)
-		// unary
-		var n1 Node
-		Regalloc(&n1, nl.Type, res)
-
-		Cgen(nl, &n1)
-		if Ctxt.Arch.Family == sys.ARM {
-			var n2 Node
-			Nodconst(&n2, nl.Type, 0)
-			Thearch.Gins(a, &n2, &n1)
-		} else if Ctxt.Arch.Family == sys.ARM64 {
-			Thearch.Gins(a, &n1, &n1)
-		} else {
-			Thearch.Gins(a, nil, &n1)
-		}
-		cgen_norm(n, &n1, res)
-		return
-
-	case OSQRT:
-		var n1 Node
-		Regalloc(&n1, nl.Type, res)
-		Cgen(n.Left, &n1)
-		Thearch.Gins(Thearch.Optoas(OSQRT, nl.Type), &n1, &n1)
-		Thearch.Gmove(&n1, res)
-		Regfree(&n1)
-		return
-
-	case OGETG:
-		Thearch.Getg(res)
-		return
-
-		// symmetric binary
-	case OAND,
-		OOR,
-		OXOR,
-		OADD,
-		OMUL:
-		if n.Op == OMUL && Thearch.Cgen_bmul != nil && Thearch.Cgen_bmul(n.Op, nl, nr, res) {
-			break
-		}
-		a = Thearch.Optoas(n.Op, nl.Type)
-		goto sbop
-
-		// asymmetric binary
-	case OSUB:
-		a = Thearch.Optoas(n.Op, nl.Type)
-		goto abop
-
-	case OHMUL:
-		Thearch.Cgen_hmul(nl, nr, res)
-
-	case OCONV:
-		if Eqtype(n.Type, nl.Type) || Noconv(n.Type, nl.Type) {
-			Cgen(nl, res)
-			return
-		}
-
-		if Ctxt.Arch.Family == sys.I386 {
-			var n1 Node
-			var n2 Node
-			Tempname(&n2, n.Type)
-			Mgen(nl, &n1, res)
-			Thearch.Gmove(&n1, &n2)
-			Thearch.Gmove(&n2, res)
-			Mfree(&n1)
-			break
-		}
-
-		var n1 Node
-		var n2 Node
-		if Ctxt.Arch.Family == sys.ARM {
-			if nl.Addable && !Is64(nl.Type) {
-				Regalloc(&n1, nl.Type, res)
-				Thearch.Gmove(nl, &n1)
-			} else {
-				if n.Type.Width > int64(Widthptr) || Is64(nl.Type) || nl.Type.IsFloat() {
-					Tempname(&n1, nl.Type)
-				} else {
-					Regalloc(&n1, nl.Type, res)
-				}
-				Cgen(nl, &n1)
-			}
-			if n.Type.Width > int64(Widthptr) || Is64(n.Type) || n.Type.IsFloat() {
-				Tempname(&n2, n.Type)
-			} else {
-				Regalloc(&n2, n.Type, nil)
-			}
-		} else {
-			if n.Type.Width > nl.Type.Width {
-				// If loading from memory, do conversion during load,
-				// so as to avoid use of 8-bit register in, say, int(*byteptr).
-				switch nl.Op {
-				case ODOT, ODOTPTR, OINDEX, OIND, ONAME:
-					Igen(nl, &n1, res)
-					Regalloc(&n2, n.Type, res)
-					Thearch.Gmove(&n1, &n2)
-					Thearch.Gmove(&n2, res)
-					Regfree(&n2)
-					Regfree(&n1)
-					return
-				}
-			}
-			Regalloc(&n1, nl.Type, res)
-			Regalloc(&n2, n.Type, &n1)
-			Cgen(nl, &n1)
-		}
-
-		// if we do the conversion n1 -> n2 here
-		// reusing the register, then gmove won't
-		// have to allocate its own register.
-		Thearch.Gmove(&n1, &n2)
-		Thearch.Gmove(&n2, res)
-		if n2.Op == OREGISTER {
-			Regfree(&n2)
-		}
-		if n1.Op == OREGISTER {
-			Regfree(&n1)
-		}
-
-	case ODOT,
-		ODOTPTR,
-		OINDEX,
-		OIND:
-		var n1 Node
-		Igen(n, &n1, res)
-
-		Thearch.Gmove(&n1, res)
-		Regfree(&n1)
-
-		// interface table is first word of interface value
-	case OITAB:
-		var n1 Node
-		Igen(nl, &n1, res)
-
-		n1.Type = n.Type
-		Thearch.Gmove(&n1, res)
-		Regfree(&n1)
-
-	case OSPTR:
-		// pointer is the first word of string or slice.
-		if Isconst(nl, CTSTR) {
-			var n1 Node
-			Regalloc(&n1, Types[Tptr], res)
-			p1 := Thearch.Gins(Thearch.Optoas(OAS, n1.Type), nil, &n1)
-			Datastring(nl.Val().U.(string), &p1.From)
-			p1.From.Type = obj.TYPE_ADDR
-			Thearch.Gmove(&n1, res)
-			Regfree(&n1)
-			break
-		}
-
-		var n1 Node
-		Igen(nl, &n1, res)
-		n1.Type = n.Type
-		Thearch.Gmove(&n1, res)
-		Regfree(&n1)
-
-	case OLEN:
-		if nl.Type.IsMap() || nl.Type.IsChan() {
-			// map and chan have len in the first int-sized word.
-			// a zero pointer means zero length
-			var n1 Node
-			Regalloc(&n1, Types[Tptr], res)
-
-			Cgen(nl, &n1)
-
-			var n2 Node
-			Nodconst(&n2, Types[Tptr], 0)
-			p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0)
-
-			n2 = n1
-			n2.Op = OINDREG
-			n2.Type = Types[Simtype[TINT]]
-			Thearch.Gmove(&n2, &n1)
-
-			Patch(p1, Pc)
-
-			Thearch.Gmove(&n1, res)
-			Regfree(&n1)
-			break
-		}
-
-		if nl.Type.IsString() || nl.Type.IsSlice() {
-			// both slice and string have len one pointer into the struct.
-			// a zero pointer means zero length
-			var n1 Node
-			Igen(nl, &n1, res)
-
-			n1.Type = Types[Simtype[TUINT]]
-			n1.Xoffset += int64(Array_nel)
-			Thearch.Gmove(&n1, res)
-			Regfree(&n1)
-			break
-		}
-
-		Fatalf("cgen: OLEN: unknown type %v", Tconv(nl.Type, FmtLong))
-
-	case OCAP:
-		if nl.Type.IsChan() {
-			// chan has cap in the second int-sized word.
-			// a zero pointer means zero length
-			var n1 Node
-			Regalloc(&n1, Types[Tptr], res)
-
-			Cgen(nl, &n1)
-
-			var n2 Node
-			Nodconst(&n2, Types[Tptr], 0)
-			p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0)
-
-			n2 = n1
-			n2.Op = OINDREG
-			n2.Xoffset = int64(Widthint)
-			n2.Type = Types[Simtype[TINT]]
-			Thearch.Gmove(&n2, &n1)
-
-			Patch(p1, Pc)
-
-			Thearch.Gmove(&n1, res)
-			Regfree(&n1)
-			break
-		}
-
-		if nl.Type.IsSlice() {
-			var n1 Node
-			Igen(nl, &n1, res)
-			n1.Type = Types[Simtype[TUINT]]
-			n1.Xoffset += int64(Array_cap)
-			Thearch.Gmove(&n1, res)
-			Regfree(&n1)
-			break
-		}
-
-		Fatalf("cgen: OCAP: unknown type %v", Tconv(nl.Type, FmtLong))
-
-	case OADDR:
-		if n.Bounded { // let race detector avoid nil checks
-			Disable_checknil++
-		}
-		Agen(nl, res)
-		if n.Bounded {
-			Disable_checknil--
-		}
-
-	case OCALLMETH:
-		cgen_callmeth(n, 0)
-		cgen_callret(n, res)
-
-	case OCALLINTER:
-		cgen_callinter(n, res, 0)
-		cgen_callret(n, res)
-
-	case OCALLFUNC:
-		cgen_call(n, 0)
-		cgen_callret(n, res)
-
-	case OMOD, ODIV:
-		if n.Type.IsFloat() || Thearch.Dodiv == nil {
-			a = Thearch.Optoas(n.Op, nl.Type)
-			goto abop
-		}
-
-		if nl.Ullman >= nr.Ullman {
-			var n1 Node
-			Regalloc(&n1, nl.Type, res)
-			Cgen(nl, &n1)
-			cgen_div(n.Op, &n1, nr, res)
-			Regfree(&n1)
-		} else {
-			var n2 Node
-			if !Smallintconst(nr) {
-				Regalloc(&n2, nr.Type, res)
-				Cgen(nr, &n2)
-			} else {
-				n2 = *nr
-			}
-
-			cgen_div(n.Op, nl, &n2, res)
-			if n2.Op != OLITERAL {
-				Regfree(&n2)
-			}
-		}
-
-	case OLSH, ORSH, OLROT:
-		Thearch.Cgen_shift(n.Op, n.Bounded, nl, nr, res)
-	}
-
-	return
-
-	// put simplest on right - we'll generate into left
-	// and then adjust it using the computation of right.
-	// constants and variables have the same ullman
-	// count, so look for constants specially.
-	//
-	// an integer constant we can use as an immediate
-	// is simpler than a variable - we can use the immediate
-	// in the adjustment instruction directly - so it goes
-	// on the right.
-	//
-	// other constants, like big integers or floating point
-	// constants, require a mov into a register, so those
-	// might as well go on the left, so we can reuse that
-	// register for the computation.
-sbop: // symmetric binary
-	if nl.Ullman < nr.Ullman || (nl.Ullman == nr.Ullman && (Smallintconst(nl) || (nr.Op == OLITERAL && !Smallintconst(nr)))) {
-		nl, nr = nr, nl
-	}
-
-abop: // asymmetric binary
-	var n1 Node
-	var n2 Node
-	if Ctxt.Arch.Family == sys.I386 {
-		// no registers, sigh
-		if Smallintconst(nr) {
-			var n1 Node
-			Mgen(nl, &n1, res)
-			var n2 Node
-			Regalloc(&n2, nl.Type, &n1)
-			Thearch.Gmove(&n1, &n2)
-			Thearch.Gins(a, nr, &n2)
-			Thearch.Gmove(&n2, res)
-			Regfree(&n2)
-			Mfree(&n1)
-		} else if nl.Ullman >= nr.Ullman {
-			var nt Node
-			Tempname(&nt, nl.Type)
-			Cgen(nl, &nt)
-			var n2 Node
-			Mgen(nr, &n2, nil)
-			var n1 Node
-			Regalloc(&n1, nl.Type, res)
-			Thearch.Gmove(&nt, &n1)
-			Thearch.Gins(a, &n2, &n1)
-			Thearch.Gmove(&n1, res)
-			Regfree(&n1)
-			Mfree(&n2)
-		} else {
-			var n2 Node
-			Regalloc(&n2, nr.Type, res)
-			Cgen(nr, &n2)
-			var n1 Node
-			Regalloc(&n1, nl.Type, nil)
-			Cgen(nl, &n1)
-			Thearch.Gins(a, &n2, &n1)
-			Regfree(&n2)
-			Thearch.Gmove(&n1, res)
-			Regfree(&n1)
-		}
-		return
-	}
-
-	if nl.Ullman >= nr.Ullman {
-		Regalloc(&n1, nl.Type, res)
-		Cgen(nl, &n1)
-
-		if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.ARM && Ctxt.Arch.Family != sys.ARM64 && Ctxt.Arch.Family != sys.PPC64 { // TODO(rsc): Check opcode for arm
-			n2 = *nr
-		} else {
-			Regalloc(&n2, nr.Type, nil)
-			Cgen(nr, &n2)
-		}
-	} else {
-		if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.ARM && Ctxt.Arch.Family != sys.ARM64 && Ctxt.Arch.Family != sys.PPC64 { // TODO(rsc): Check opcode for arm
-			n2 = *nr
-		} else {
-			Regalloc(&n2, nr.Type, res)
-			Cgen(nr, &n2)
-		}
-
-		Regalloc(&n1, nl.Type, nil)
-		Cgen(nl, &n1)
-	}
-
-	Thearch.Gins(a, &n2, &n1)
-	if n2.Op != OLITERAL {
-		Regfree(&n2)
-	}
-	cgen_norm(n, &n1, res)
-}
-
-var sys_wbptr *Node
-
-func cgen_wbptr(n, res *Node) {
-	if Curfn != nil {
-		if Curfn.Func.Pragma&Nowritebarrier != 0 {
-			Yyerror("write barrier prohibited")
-		}
-		if Curfn.Func.WBLineno == 0 {
-			Curfn.Func.WBLineno = lineno
-		}
-	}
-	if Debug_wb > 0 {
-		Warn("write barrier")
-	}
-
-	var dst, src Node
-	Igen(res, &dst, nil)
-	if n.Op == OREGISTER {
-		src = *n
-		Regrealloc(&src)
-	} else {
-		Cgenr(n, &src, nil)
-	}
-
-	wbVar := syslook("writeBarrier")
-	wbEnabled := NodSym(ODOT, wbVar, wbVar.Type.Field(0).Sym)
-	wbEnabled = typecheck(wbEnabled, Erv)
-	pbr := Thearch.Ginscmp(ONE, Types[TUINT8], wbEnabled, Nodintconst(0), -1)
-	Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, &dst)
-	pjmp := Gbranch(obj.AJMP, nil, 0)
-	Patch(pbr, Pc)
-	var adst Node
-	Agenr(&dst, &adst, &dst)
-	p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &adst, nil)
-	a := &p.To
-	a.Type = obj.TYPE_MEM
-	a.Reg = int16(Thearch.REGSP)
-	a.Offset = Ctxt.FixedFrameSize()
-	p2 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil)
-	p2.To = p.To
-	p2.To.Offset += int64(Widthptr)
-	Regfree(&adst)
-	if sys_wbptr == nil {
-		sys_wbptr = writebarrierfn("writebarrierptr", Types[Tptr], Types[Tptr])
-	}
-	Ginscall(sys_wbptr, 0)
-	Patch(pjmp, Pc)
-
-	Regfree(&dst)
-	Regfree(&src)
-}
-
-func cgen_wbfat(n, res *Node) {
-	if Curfn != nil {
-		if Curfn.Func.Pragma&Nowritebarrier != 0 {
-			Yyerror("write barrier prohibited")
-		}
-		if Curfn.Func.WBLineno == 0 {
-			Curfn.Func.WBLineno = lineno
-		}
-	}
-	if Debug_wb > 0 {
-		Warn("write barrier")
-	}
-	needType := true
-	funcName := "typedmemmove"
-	var dst, src Node
-	if n.Ullman >= res.Ullman {
-		Agenr(n, &src, nil)
-		Agenr(res, &dst, nil)
-	} else {
-		Agenr(res, &dst, nil)
-		Agenr(n, &src, nil)
-	}
-	p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &dst, nil)
-	a := &p.To
-	a.Type = obj.TYPE_MEM
-	a.Reg = int16(Thearch.REGSP)
-	a.Offset = Ctxt.FixedFrameSize()
-	if needType {
-		a.Offset += int64(Widthptr)
-	}
-	p2 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil)
-	p2.To = p.To
-	p2.To.Offset += int64(Widthptr)
-	Regfree(&dst)
-	if needType {
-		src.Type = Types[Tptr]
-		Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), typename(n.Type), &src)
-		p3 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil)
-		p3.To = p2.To
-		p3.To.Offset -= 2 * int64(Widthptr)
-	}
-	Regfree(&src)
-	Ginscall(writebarrierfn(funcName, Types[Tptr], Types[Tptr]), 0)
-}
-
-// cgen_norm moves n1 to res, truncating to expected type if necessary.
-// n1 is a register, and cgen_norm frees it.
-func cgen_norm(n, n1, res *Node) {
-	switch Ctxt.Arch.Family {
-	case sys.AMD64, sys.I386:
-		// We use sized math, so the result is already truncated.
-	default:
-		switch n.Op {
-		case OADD, OSUB, OMUL, ODIV, OCOM, OMINUS:
-			// TODO(rsc): What about left shift?
-			Thearch.Gins(Thearch.Optoas(OAS, n.Type), n1, n1)
-		}
-	}
-
-	Thearch.Gmove(n1, res)
-	Regfree(n1)
-}
-
-func Mgen(n *Node, n1 *Node, rg *Node) {
-	n1.Op = OEMPTY
-
-	if n.Addable {
-		*n1 = *n
-		if n1.Op == OREGISTER || n1.Op == OINDREG {
-			reg[n.Reg-int16(Thearch.REGMIN)]++
-		}
-		return
-	}
-
-	Tempname(n1, n.Type)
-	Cgen(n, n1)
-	if n.Type.Width <= int64(Widthptr) || n.Type.IsFloat() {
-		n2 := *n1
-		Regalloc(n1, n.Type, rg)
-		Thearch.Gmove(&n2, n1)
-	}
-}
-
-func Mfree(n *Node) {
-	if n.Op == OREGISTER {
-		Regfree(n)
-	}
-}
-
-// allocate a register (reusing res if possible) and generate
-//	a = n
-// The caller must call Regfree(a).
-func Cgenr(n *Node, a *Node, res *Node) {
-	if Debug['g'] != 0 {
-		Dump("cgenr-n", n)
-	}
-
-	if Isfat(n.Type) {
-		Fatalf("cgenr on fat node")
-	}
-
-	if n.Addable {
-		Regalloc(a, n.Type, res)
-		Thearch.Gmove(n, a)
-		return
-	}
-
-	switch n.Op {
-	case ONAME,
-		ODOT,
-		ODOTPTR,
-		OINDEX,
-		OCALLFUNC,
-		OCALLMETH,
-		OCALLINTER:
-		var n1 Node
-		Igen(n, &n1, res)
-		Regalloc(a, n.Type, &n1)
-		Thearch.Gmove(&n1, a)
-		Regfree(&n1)
-
-	default:
-		Regalloc(a, n.Type, res)
-		Cgen(n, a)
-	}
-}
-
-// allocate a register (reusing res if possible) and generate
-//	a = &n
-// The caller must call Regfree(a).
-// The generated code checks that the result is not nil.
-func Agenr(n *Node, a *Node, res *Node) {
-	if Debug['g'] != 0 {
-		Dump("\nagenr-n", n)
-	}
-
-	nl := n.Left
-	nr := n.Right
-
-	switch n.Op {
-	case ODOT, ODOTPTR, OCALLFUNC, OCALLMETH, OCALLINTER:
-		var n1 Node
-		Igen(n, &n1, res)
-		Regalloc(a, Types[Tptr], &n1)
-		Agen(&n1, a)
-		Regfree(&n1)
-
-	case OIND:
-		Cgenr(n.Left, a, res)
-		if !n.Left.NonNil {
-			Cgen_checknil(a)
-		} else if Debug_checknil != 0 && n.Lineno > 1 {
-			Warnl(n.Lineno, "removed nil check")
-		}
-
-	case OINDEX:
-		if Ctxt.Arch.Family == sys.ARM {
-			var p2 *obj.Prog // to be patched to panicindex.
-			w := uint32(n.Type.Width)
-			bounded := Debug['B'] != 0 || n.Bounded
-			var n1 Node
-			var n3 Node
-			if nr.Addable {
-				var tmp Node
-				if !Isconst(nr, CTINT) {
-					Tempname(&tmp, Types[TINT32])
-				}
-				if !Isconst(nl, CTSTR) {
-					Agenr(nl, &n3, res)
-				}
-				if !Isconst(nr, CTINT) {
-					p2 = Thearch.Cgenindex(nr, &tmp, bounded)
-					Regalloc(&n1, tmp.Type, nil)
-					Thearch.Gmove(&tmp, &n1)
-				}
-			} else if nl.Addable {
-				if !Isconst(nr, CTINT) {
-					var tmp Node
-					Tempname(&tmp, Types[TINT32])
-					p2 = Thearch.Cgenindex(nr, &tmp, bounded)
-					Regalloc(&n1, tmp.Type, nil)
-					Thearch.Gmove(&tmp, &n1)
-				}
-
-				if !Isconst(nl, CTSTR) {
-					Agenr(nl, &n3, res)
-				}
-			} else {
-				var tmp Node
-				Tempname(&tmp, Types[TINT32])
-				p2 = Thearch.Cgenindex(nr, &tmp, bounded)
-				nr = &tmp
-				if !Isconst(nl, CTSTR) {
-					Agenr(nl, &n3, res)
-				}
-				Regalloc(&n1, tmp.Type, nil)
-				Thearch.Gins(Thearch.Optoas(OAS, tmp.Type), &tmp, &n1)
-			}
-
-			// &a is in &n3 (allocated in res)
-			// i is in &n1 (if not constant)
-			// w is width
-
-			// constant index
-			if Isconst(nr, CTINT) {
-				if Isconst(nl, CTSTR) {
-					Fatalf("constant string constant index")
-				}
-				v := uint64(nr.Int64())
-				var n2 Node
-				if nl.Type.IsSlice() || nl.Type.IsString() {
-					if Debug['B'] == 0 && !n.Bounded {
-						n1 = n3
-						n1.Op = OINDREG
-						n1.Type = Types[Tptr]
-						n1.Xoffset = int64(Array_nel)
-						Nodconst(&n2, Types[TUINT32], int64(v))
-						p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &n1, &n2, +1)
-						Ginscall(Panicindex, -1)
-						Patch(p1, Pc)
-					}
-
-					n1 = n3
-					n1.Op = OINDREG
-					n1.Type = Types[Tptr]
-					n1.Xoffset = int64(Array_array)
-					Thearch.Gmove(&n1, &n3)
-				}
-
-				Nodconst(&n2, Types[Tptr], int64(v*uint64(w)))
-				Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3)
-				*a = n3
-				break
-			}
-
-			var n2 Node
-			Regalloc(&n2, Types[TINT32], &n1) // i
-			Thearch.Gmove(&n1, &n2)
-			Regfree(&n1)
-
-			var n4 Node
-			if Debug['B'] == 0 && !n.Bounded {
-				// check bounds
-				if Isconst(nl, CTSTR) {
-					Nodconst(&n4, Types[TUINT32], int64(len(nl.Val().U.(string))))
-				} else if nl.Type.IsSlice() || nl.Type.IsString() {
-					n1 = n3
-					n1.Op = OINDREG
-					n1.Type = Types[Tptr]
-					n1.Xoffset = int64(Array_nel)
-					Regalloc(&n4, Types[TUINT32], nil)
-					Thearch.Gmove(&n1, &n4)
-				} else {
-					Nodconst(&n4, Types[TUINT32], nl.Type.NumElem())
-				}
-				p1 := Thearch.Ginscmp(OLT, Types[TUINT32], &n2, &n4, +1)
-				if n4.Op == OREGISTER {
-					Regfree(&n4)
-				}
-				if p2 != nil {
-					Patch(p2, Pc)
-				}
-				Ginscall(Panicindex, -1)
-				Patch(p1, Pc)
-			}
-
-			if Isconst(nl, CTSTR) {
-				Regalloc(&n3, Types[Tptr], res)
-				p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3)
-				Datastring(nl.Val().U.(string), &p1.From)
-				p1.From.Type = obj.TYPE_ADDR
-			} else if nl.Type.IsSlice() || nl.Type.IsString() {
-				n1 = n3
-				n1.Op = OINDREG
-				n1.Type = Types[Tptr]
-				n1.Xoffset = int64(Array_array)
-				Thearch.Gmove(&n1, &n3)
-			}
-
-			if w == 0 {
-				// nothing to do
-			} else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) {
-				// done by back end
-			} else if w == 1 {
-				Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3)
-			} else {
-				if w&(w-1) == 0 {
-					// Power of 2.  Use shift.
-					Thearch.Ginscon(Thearch.Optoas(OLSH, Types[TUINT32]), int64(log2(uint64(w))), &n2)
-				} else {
-					// Not a power of 2.  Use multiply.
-					Regalloc(&n4, Types[TUINT32], nil)
-					Nodconst(&n1, Types[TUINT32], int64(w))
-					Thearch.Gmove(&n1, &n4)
-					Thearch.Gins(Thearch.Optoas(OMUL, Types[TUINT32]), &n4, &n2)
-					Regfree(&n4)
-				}
-				Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3)
-			}
-			*a = n3
-			Regfree(&n2)
-			break
-		}
-		if Ctxt.Arch.Family == sys.I386 {
-			var p2 *obj.Prog // to be patched to panicindex.
-			w := uint32(n.Type.Width)
-			bounded := Debug['B'] != 0 || n.Bounded
-			var n3 Node
-			var tmp Node
-			var n1 Node
-			if nr.Addable {
-				// Generate &nl first, and move nr into register.
-				if !Isconst(nl, CTSTR) {
-					Igen(nl, &n3, res)
-				}
-				if !Isconst(nr, CTINT) {
-					p2 = Thearch.Igenindex(nr, &tmp, bounded)
-					Regalloc(&n1, tmp.Type, nil)
-					Thearch.Gmove(&tmp, &n1)
-				}
-			} else if nl.Addable {
-				// Generate nr first, and move &nl into register.
-				if !Isconst(nr, CTINT) {
-					p2 = Thearch.Igenindex(nr, &tmp, bounded)
-					Regalloc(&n1, tmp.Type, nil)
-					Thearch.Gmove(&tmp, &n1)
-				}
-
-				if !Isconst(nl, CTSTR) {
-					Igen(nl, &n3, res)
-				}
-			} else {
-				p2 = Thearch.Igenindex(nr, &tmp, bounded)
-				nr = &tmp
-				if !Isconst(nl, CTSTR) {
-					Igen(nl, &n3, res)
-				}
-				Regalloc(&n1, tmp.Type, nil)
-				Thearch.Gins(Thearch.Optoas(OAS, tmp.Type), &tmp, &n1)
-			}
-
-			// For fixed array we really want the pointer in n3.
-			var n2 Node
-			if nl.Type.IsArray() {
-				Regalloc(&n2, Types[Tptr], &n3)
-				Agen(&n3, &n2)
-				Regfree(&n3)
-				n3 = n2
-			}
-
-			// &a[0] is in n3 (allocated in res)
-			// i is in n1 (if not constant)
-			// len(a) is in nlen (if needed)
-			// w is width
-
-			// constant index
-			if Isconst(nr, CTINT) {
-				if Isconst(nl, CTSTR) {
-					Fatalf("constant string constant index") // front end should handle
-				}
-				v := uint64(nr.Int64())
-				if nl.Type.IsSlice() || nl.Type.IsString() {
-					if Debug['B'] == 0 && !n.Bounded {
-						nlen := n3
-						nlen.Type = Types[TUINT32]
-						nlen.Xoffset += int64(Array_nel)
-						Nodconst(&n2, Types[TUINT32], int64(v))
-						p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &nlen, &n2, +1)
-						Ginscall(Panicindex, -1)
-						Patch(p1, Pc)
-					}
-				}
-
-				// Load base pointer in n2 = n3.
-				Regalloc(&n2, Types[Tptr], &n3)
-
-				n3.Type = Types[Tptr]
-				n3.Xoffset += int64(Array_array)
-				Thearch.Gmove(&n3, &n2)
-				Regfree(&n3)
-				if v*uint64(w) != 0 {
-					Nodconst(&n1, Types[Tptr], int64(v*uint64(w)))
-					Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n1, &n2)
-				}
-				*a = n2
-				break
-			}
-
-			// i is in register n1, extend to 32 bits.
-			t := Types[TUINT32]
-
-			if n1.Type.IsSigned() {
-				t = Types[TINT32]
-			}
-
-			Regalloc(&n2, t, &n1) // i
-			Thearch.Gmove(&n1, &n2)
-			Regfree(&n1)
-
-			if Debug['B'] == 0 && !n.Bounded {
-				// check bounds
-				t := Types[TUINT32]
-
-				var nlen Node
-				if Isconst(nl, CTSTR) {
-					Nodconst(&nlen, t, int64(len(nl.Val().U.(string))))
-				} else if nl.Type.IsSlice() || nl.Type.IsString() {
-					nlen = n3
-					nlen.Type = t
-					nlen.Xoffset += int64(Array_nel)
-				} else {
-					Nodconst(&nlen, t, nl.Type.NumElem())
-				}
-
-				p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1)
-				if p2 != nil {
-					Patch(p2, Pc)
-				}
-				Ginscall(Panicindex, -1)
-				Patch(p1, Pc)
-			}
-
-			if Isconst(nl, CTSTR) {
-				Regalloc(&n3, Types[Tptr], res)
-				p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3)
-				Datastring(nl.Val().U.(string), &p1.From)
-				p1.From.Type = obj.TYPE_ADDR
-				Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3)
-				goto indexdone1
-			}
-
-			// Load base pointer in n3.
-			Regalloc(&tmp, Types[Tptr], &n3)
-
-			if nl.Type.IsSlice() || nl.Type.IsString() {
-				n3.Type = Types[Tptr]
-				n3.Xoffset += int64(Array_array)
-				Thearch.Gmove(&n3, &tmp)
-			}
-
-			Regfree(&n3)
-			n3 = tmp
-
-			if w == 0 {
-				// nothing to do
-			} else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) {
-				// done by back end
-			} else if w == 1 {
-				Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3)
-			} else {
-				if w&(w-1) == 0 {
-					// Power of 2.  Use shift.
-					Thearch.Ginscon(Thearch.Optoas(OLSH, Types[TUINT32]), int64(log2(uint64(w))), &n2)
-				} else {
-					// Not a power of 2.  Use multiply.
-					Thearch.Ginscon(Thearch.Optoas(OMUL, Types[TUINT32]), int64(w), &n2)
-				}
-				Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3)
-			}
-
-		indexdone1:
-			*a = n3
-			Regfree(&n2)
-			break
-		}
-
-		freelen := 0
-		w := uint64(n.Type.Width)
-
-		// Generate the non-addressable child first.
-		var n3 Node
-		var nlen Node
-		var tmp Node
-		var n1 Node
-		if nr.Addable {
-			goto irad
-		}
-		if nl.Addable {
-			Cgenr(nr, &n1, nil)
-			if !Isconst(nl, CTSTR) {
-				if nl.Type.IsArray() {
-					Agenr(nl, &n3, res)
-				} else {
-					Igen(nl, &nlen, res)
-					freelen = 1
-					nlen.Type = Types[Tptr]
-					nlen.Xoffset += int64(Array_array)
-					Regalloc(&n3, Types[Tptr], res)
-					Thearch.Gmove(&nlen, &n3)
-					nlen.Type = Types[Simtype[TUINT]]
-					nlen.Xoffset += int64(Array_nel) - int64(Array_array)
-				}
-			}
-
-			goto index
-		}
-
-		Tempname(&tmp, nr.Type)
-		Cgen(nr, &tmp)
-		nr = &tmp
-
-	irad:
-		if !Isconst(nl, CTSTR) {
-			if nl.Type.IsArray() {
-				Agenr(nl, &n3, res)
-			} else {
-				if !nl.Addable {
-					if res != nil && res.Op == OREGISTER { // give up res, which we don't need yet.
-						Regfree(res)
-					}
-
-					// igen will need an addressable node.
-					var tmp2 Node
-					Tempname(&tmp2, nl.Type)
-					Cgen(nl, &tmp2)
-					nl = &tmp2
-
-					if res != nil && res.Op == OREGISTER { // reacquire res
-						Regrealloc(res)
-					}
-				}
-
-				Igen(nl, &nlen, res)
-				freelen = 1
-				nlen.Type = Types[Tptr]
-				nlen.Xoffset += int64(Array_array)
-				Regalloc(&n3, Types[Tptr], res)
-				Thearch.Gmove(&nlen, &n3)
-				nlen.Type = Types[Simtype[TUINT]]
-				nlen.Xoffset += int64(Array_nel) - int64(Array_array)
-			}
-		}
-
-		if !Isconst(nr, CTINT) {
-			Cgenr(nr, &n1, nil)
-		}
-
-		goto index
-
-		// &a is in &n3 (allocated in res)
-		// i is in &n1 (if not constant)
-		// len(a) is in nlen (if needed)
-		// w is width
-
-		// constant index
-	index:
-		if Isconst(nr, CTINT) {
-			if Isconst(nl, CTSTR) {
-				Fatalf("constant string constant index") // front end should handle
-			}
-			v := uint64(nr.Int64())
-			if nl.Type.IsSlice() || nl.Type.IsString() {
-				if Debug['B'] == 0 && !n.Bounded {
-					p1 := Thearch.Ginscmp(OGT, Types[Simtype[TUINT]], &nlen, Nodintconst(int64(v)), +1)
-					Ginscall(Panicindex, -1)
-					Patch(p1, Pc)
-				}
-
-				Regfree(&nlen)
-			}
-
-			if v*w != 0 {
-				Thearch.Ginscon(Thearch.Optoas(OADD, Types[Tptr]), int64(v*w), &n3)
-			}
-			*a = n3
-			break
-		}
-
-		// type of the index
-		t := Types[TUINT64]
-
-		if n1.Type.IsSigned() {
-			t = Types[TINT64]
-		}
-
-		var n2 Node
-		Regalloc(&n2, t, &n1) // i
-		Thearch.Gmove(&n1, &n2)
-		Regfree(&n1)
-
-		if Debug['B'] == 0 && !n.Bounded {
-			// check bounds
-			t = Types[Simtype[TUINT]]
-
-			if Is64(nr.Type) {
-				t = Types[TUINT64]
-			}
-			if Isconst(nl, CTSTR) {
-				Nodconst(&nlen, t, int64(len(nl.Val().U.(string))))
-			} else if nl.Type.IsSlice() || nl.Type.IsString() {
-				// nlen already initialized
-			} else {
-				Nodconst(&nlen, t, nl.Type.NumElem())
-			}
-
-			p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1)
-			Ginscall(Panicindex, -1)
-			Patch(p1, Pc)
-		}
-
-		if Isconst(nl, CTSTR) {
-			Regalloc(&n3, Types[Tptr], res)
-			p1 := Thearch.Gins(Thearch.Optoas(OAS, n3.Type), nil, &n3) // XXX was LEAQ!
-			Datastring(nl.Val().U.(string), &p1.From)
-			p1.From.Type = obj.TYPE_ADDR
-			Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3)
-			goto indexdone
-		}
-
-		if w == 0 {
-			// nothing to do
-		} else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) {
-			// done by back end
-		} else if w == 1 {
-			Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3)
-		} else {
-			if w&(w-1) == 0 {
-				// Power of 2.  Use shift.
-				Thearch.Ginscon(Thearch.Optoas(OLSH, t), int64(log2(w)), &n2)
-			} else {
-				// Not a power of 2.  Use multiply.
-				Thearch.Ginscon(Thearch.Optoas(OMUL, t), int64(w), &n2)
-			}
-			Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3)
-		}
-
-	indexdone:
-		*a = n3
-		Regfree(&n2)
-		if freelen != 0 {
-			Regfree(&nlen)
-		}
-
-	default:
-		Regalloc(a, Types[Tptr], res)
-		Agen(n, a)
-	}
-}
-
-// log2 returns the logarithm base 2 of n.  n must be a power of 2.
-func log2(n uint64) int {
-	x := 0
-	for n>>uint(x) != 1 {
-		x++
-	}
-	return x
-}
-
-// generate:
-//	res = &n;
-// The generated code checks that the result is not nil.
-func Agen(n *Node, res *Node) {
-	if Debug['g'] != 0 {
-		Dump("\nagen-res", res)
-		Dump("agen-r", n)
-	}
-
-	if n == nil || n.Type == nil {
-		return
-	}
-
-	for n.Op == OCONVNOP {
-		n = n.Left
-	}
-
-	if Isconst(n, CTNIL) && n.Type.Width > int64(Widthptr) {
-		// Use of a nil interface or nil slice.
-		// Create a temporary we can take the address of and read.
-		// The generated code is just going to panic, so it need not
-		// be terribly efficient. See issue 3670.
-		var n1 Node
-		Tempname(&n1, n.Type)
-
-		Gvardef(&n1)
-		Thearch.Clearfat(&n1)
-		var n2 Node
-		Regalloc(&n2, Types[Tptr], res)
-		var n3 Node
-		n3.Op = OADDR
-		n3.Left = &n1
-		Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &n3, &n2)
-		Thearch.Gmove(&n2, res)
-		Regfree(&n2)
-		return
-	}
-
-	if n.Op == OINDREG && n.Xoffset == 0 {
-		// Generate MOVW R0, R1 instead of MOVW $0(R0), R1.
-		// This allows better move propagation in the back ends
-		// (and maybe it helps the processor).
-		n1 := *n
-		n1.Op = OREGISTER
-		n1.Type = res.Type
-		Thearch.Gmove(&n1, res)
-		return
-	}
-
-	if n.Addable {
-		if n.Op == OREGISTER {
-			Fatalf("agen OREGISTER")
-		}
-		var n1 Node
-		n1.Op = OADDR
-		n1.Left = n
-		var n2 Node
-		Regalloc(&n2, Types[Tptr], res)
-		Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &n1, &n2)
-		Thearch.Gmove(&n2, res)
-		Regfree(&n2)
-		return
-	}
-
-	nl := n.Left
-
-	switch n.Op {
-	default:
-		Dump("bad agen", n)
-		Fatalf("agen: unknown op %v", Nconv(n, FmtShort|FmtSign))
-
-	case OCALLMETH:
-		cgen_callmeth(n, 0)
-		cgen_aret(n, res)
-
-	case OCALLINTER:
-		cgen_callinter(n, res, 0)
-		cgen_aret(n, res)
-
-	case OCALLFUNC:
-		cgen_call(n, 0)
-		cgen_aret(n, res)
-
-	case OEFACE, ODOTTYPE, OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
-		var n1 Node
-		Tempname(&n1, n.Type)
-		Cgen(n, &n1)
-		Agen(&n1, res)
-
-	case OINDEX:
-		var n1 Node
-		Agenr(n, &n1, res)
-		Thearch.Gmove(&n1, res)
-		Regfree(&n1)
-
-	case OIND:
-		Cgen(nl, res)
-		if !nl.NonNil {
-			Cgen_checknil(res)
-		} else if Debug_checknil != 0 && n.Lineno > 1 {
-			Warnl(n.Lineno, "removed nil check")
-		}
-
-	case ODOT:
-		Agen(nl, res)
-		if n.Xoffset != 0 {
-			addOffset(res, n.Xoffset)
-		}
-
-	case ODOTPTR:
-		Cgen(nl, res)
-		if !nl.NonNil {
-			Cgen_checknil(res)
-		} else if Debug_checknil != 0 && n.Lineno > 1 {
-			Warnl(n.Lineno, "removed nil check")
-		}
-		if n.Xoffset != 0 {
-			addOffset(res, n.Xoffset)
-		}
-	}
-}
-
-func addOffset(res *Node, offset int64) {
-	if Ctxt.Arch.InFamily(sys.AMD64, sys.I386) {
-		Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), Nodintconst(offset), res)
-		return
-	}
-
-	var n1, n2 Node
-	Regalloc(&n1, Types[Tptr], nil)
-	Thearch.Gmove(res, &n1)
-	Regalloc(&n2, Types[Tptr], nil)
-	Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), Nodintconst(offset), &n2)
-	Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n1)
-	Thearch.Gmove(&n1, res)
-	Regfree(&n1)
-	Regfree(&n2)
-}
-
-// Igen computes the address &n, stores it in a register r,
-// and rewrites a to refer to *r. The chosen r may be the
-// stack pointer, it may be borrowed from res, or it may
-// be a newly allocated register. The caller must call Regfree(a)
-// to free r when the address is no longer needed.
-// The generated code ensures that &n is not nil.
-func Igen(n *Node, a *Node, res *Node) {
-	if Debug['g'] != 0 {
-		Dump("\nigen-n", n)
-	}
-
-	switch n.Op {
-	case ONAME:
-		if n.Class == PAUTOHEAP {
-			Dump("igen", n)
-			Fatalf("bad name")
-		}
-		*a = *n
-		return
-
-	case OINDREG:
-		// Increase the refcount of the register so that igen's caller
-		// has to call Regfree.
-		if n.Reg != int16(Thearch.REGSP) {
-			reg[n.Reg-int16(Thearch.REGMIN)]++
-		}
-		*a = *n
-		return
-
-	case ODOT:
-		Igen(n.Left, a, res)
-		a.Xoffset += n.Xoffset
-		a.Type = n.Type
-		Fixlargeoffset(a)
-		return
-
-	case ODOTPTR:
-		Cgenr(n.Left, a, res)
-		if !n.Left.NonNil {
-			Cgen_checknil(a)
-		} else if Debug_checknil != 0 && n.Lineno > 1 {
-			Warnl(n.Lineno, "removed nil check")
-		}
-		a.Op = OINDREG
-		a.Xoffset += n.Xoffset
-		a.Type = n.Type
-		Fixlargeoffset(a)
-		return
-
-	case OCALLFUNC, OCALLMETH, OCALLINTER:
-		switch n.Op {
-		case OCALLFUNC:
-			cgen_call(n, 0)
-
-		case OCALLMETH:
-			cgen_callmeth(n, 0)
-
-		case OCALLINTER:
-			cgen_callinter(n, nil, 0)
-		}
-
-		fp := n.Left.Type.Results().Field(0)
-		*a = Node{}
-		a.Op = OINDREG
-		a.Reg = int16(Thearch.REGSP)
-		a.Addable = true
-		a.Xoffset = fp.Offset + Ctxt.FixedFrameSize()
-		a.Type = n.Type
-		return
-
-	case OINDEX:
-		// Index of fixed-size array by constant can
-		// put the offset in the addressing.
-		// Could do the same for slice except that we need
-		// to use the real index for the bounds checking.
-		if n.Left.Type.IsArray() || (n.Left.Type.IsPtr() && n.Left.Left.Type.IsArray()) {
-			if Isconst(n.Right, CTINT) {
-				// Compute &a.
-				if !n.Left.Type.IsPtr() {
-					Igen(n.Left, a, res)
-				} else {
-					var n1 Node
-					Igen(n.Left, &n1, res)
-					Cgen_checknil(&n1)
-					Regalloc(a, Types[Tptr], res)
-					Thearch.Gmove(&n1, a)
-					Regfree(&n1)
-					a.Op = OINDREG
-				}
-
-				// Compute &a[i] as &a + i*width.
-				a.Type = n.Type
-
-				a.Xoffset += n.Right.Int64() * n.Type.Width
-				Fixlargeoffset(a)
-				return
-			}
-		}
-	}
-
-	Agenr(n, a, res)
-	a.Op = OINDREG
-	a.Type = n.Type
-}
-
-// Bgen generates code for branches:
-//
-// 	if n == wantTrue {
-// 		goto to
-// 	}
-func Bgen(n *Node, wantTrue bool, likely int, to *obj.Prog) {
-	bgenx(n, nil, wantTrue, likely, to)
-}
-
-// Bvgen generates code for calculating boolean values:
-// 	res = n == wantTrue
-func Bvgen(n, res *Node, wantTrue bool) {
-	if Thearch.Ginsboolval == nil {
-		// Direct value generation not implemented for this architecture.
-		// Implement using jumps.
-		bvgenjump(n, res, wantTrue, true)
-		return
-	}
-	bgenx(n, res, wantTrue, 0, nil)
-}
-
-// bvgenjump implements boolean value generation using jumps:
-// 	if n == wantTrue {
-// 		res = 1
-// 	} else {
-// 		res = 0
-// 	}
-// geninit controls whether n's Ninit is generated.
-func bvgenjump(n, res *Node, wantTrue, geninit bool) {
-	init := n.Ninit
-	if !geninit {
-		n.Ninit.Set(nil)
-	}
-	p1 := Gbranch(obj.AJMP, nil, 0)
-	p2 := Pc
-	Thearch.Gmove(Nodbool(true), res)
-	p3 := Gbranch(obj.AJMP, nil, 0)
-	Patch(p1, Pc)
-	Bgen(n, wantTrue, 0, p2)
-	Thearch.Gmove(Nodbool(false), res)
-	Patch(p3, Pc)
-	n.Ninit.MoveNodes(&init)
-}
-
-// bgenx is the backend for Bgen and Bvgen.
-// If res is nil, it generates a branch.
-// Otherwise, it generates a boolean value.
-func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
-	if Debug['g'] != 0 {
-		fmt.Printf("\nbgenx wantTrue=%t likely=%d to=%v\n", wantTrue, likely, to)
-		Dump("n", n)
-		Dump("res", res)
-	}
-
-	genval := res != nil
-
-	if n == nil {
-		n = Nodbool(true)
-	}
-
-	Genlist(n.Ninit)
-
-	if n.Type == nil {
-		n = convlit(n, Types[TBOOL])
-		if n.Type == nil {
-			return
-		}
-	}
-
-	if !n.Type.IsBoolean() {
-		Fatalf("bgen: bad type %v for %v", n.Type, n.Op)
-	}
-
-	for n.Op == OCONVNOP {
-		n = n.Left
-		Genlist(n.Ninit)
-	}
-
-	if Thearch.Bgen_float != nil && n.Left != nil && n.Left.Type.IsFloat() {
-		if genval {
-			bvgenjump(n, res, wantTrue, false)
-			return
-		}
-		Thearch.Bgen_float(n, wantTrue, likely, to)
-		return
-	}
-
-	switch n.Op {
-	default:
-		if genval {
-			Cgen(n, res)
-			if !wantTrue {
-				Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res)
-			}
-			return
-		}
-
-		var tmp Node
-		Regalloc(&tmp, n.Type, nil)
-		Cgen(n, &tmp)
-		bgenNonZero(&tmp, nil, wantTrue, likely, to)
-		Regfree(&tmp)
-		return
-
-	case ONAME:
-		// Some architectures might need a temporary or other help here,
-		// but they don't support direct generation of a bool value yet.
-		// We can fix that as we go.
-		mayNeedTemp := Ctxt.Arch.InFamily(sys.ARM, sys.ARM64, sys.MIPS64, sys.PPC64, sys.S390X)
-
-		if genval {
-			if mayNeedTemp {
-				Fatalf("genval ONAMES not fully implemented")
-			}
-			Cgen(n, res)
-			if !wantTrue {
-				Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res)
-			}
-			return
-		}
-
-		if n.Addable && !mayNeedTemp {
-			// no need for a temporary
-			bgenNonZero(n, nil, wantTrue, likely, to)
-			return
-		}
-		var tmp Node
-		Regalloc(&tmp, n.Type, nil)
-		Cgen(n, &tmp)
-		bgenNonZero(&tmp, nil, wantTrue, likely, to)
-		Regfree(&tmp)
-		return
-
-	case OLITERAL:
-		// n is a constant.
-		if !Isconst(n, CTBOOL) {
-			Fatalf("bgen: non-bool const %v\n", Nconv(n, FmtLong))
-		}
-		if genval {
-			Cgen(Nodbool(wantTrue == n.Val().U.(bool)), res)
-			return
-		}
-		// If n == wantTrue, jump; otherwise do nothing.
-		if wantTrue == n.Val().U.(bool) {
-			Patch(Gbranch(obj.AJMP, nil, likely), to)
-		}
-		return
-
-	case OANDAND, OOROR:
-		and := (n.Op == OANDAND) == wantTrue
-		if genval {
-			p1 := Gbranch(obj.AJMP, nil, 0)
-			p2 := Gbranch(obj.AJMP, nil, 0)
-			Patch(p2, Pc)
-			Cgen(Nodbool(!and), res)
-			p3 := Gbranch(obj.AJMP, nil, 0)
-			Patch(p1, Pc)
-			Bgen(n.Left, wantTrue != and, 0, p2)
-			Bvgen(n.Right, res, wantTrue)
-			Patch(p3, Pc)
-			return
-		}
-
-		if and {
-			p1 := Gbranch(obj.AJMP, nil, 0)
-			p2 := Gbranch(obj.AJMP, nil, 0)
-			Patch(p1, Pc)
-			Bgen(n.Left, !wantTrue, -likely, p2)
-			Bgen(n.Right, !wantTrue, -likely, p2)
-			p1 = Gbranch(obj.AJMP, nil, 0)
-			Patch(p1, to)
-			Patch(p2, Pc)
-		} else {
-			Bgen(n.Left, wantTrue, likely, to)
-			Bgen(n.Right, wantTrue, likely, to)
-		}
-		return
-
-	case ONOT: // unary
-		if n.Left == nil || n.Left.Type == nil {
-			return
-		}
-		bgenx(n.Left, res, !wantTrue, likely, to)
-		return
-
-	case OEQ, ONE, OLT, OGT, OLE, OGE:
-		if n.Left == nil || n.Left.Type == nil || n.Right == nil || n.Right.Type == nil {
-			return
-		}
-	}
-
-	// n.Op is one of OEQ, ONE, OLT, OGT, OLE, OGE
-	nl := n.Left
-	nr := n.Right
-	op := n.Op
-
-	if !wantTrue {
-		if nr.Type.IsFloat() {
-			// Brcom is not valid on floats when NaN is involved.
-			ll := n.Ninit // avoid re-genning Ninit
-			n.Ninit.Set(nil)
-			if genval {
-				bgenx(n, res, true, likely, to)
-				Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res) // res = !res
-				n.Ninit.Set(ll.Slice())
-				return
-			}
-			p1 := Gbranch(obj.AJMP, nil, 0)
-			p2 := Gbranch(obj.AJMP, nil, 0)
-			Patch(p1, Pc)
-			bgenx(n, res, true, -likely, p2)
-			Patch(Gbranch(obj.AJMP, nil, 0), to)
-			Patch(p2, Pc)
-			n.Ninit.Set(ll.Slice())
-			return
-		}
-
-		op = Brcom(op)
-	}
-	wantTrue = true
-
-	// make simplest on right
-	if nl.Op == OLITERAL || (nl.Ullman < nr.Ullman && nl.Ullman < UINF) {
-		op = Brrev(op)
-		nl, nr = nr, nl
-	}
-
-	if nl.Type.IsSlice() || nl.Type.IsInterface() {
-		// front end should only leave cmp to literal nil
-		if (op != OEQ && op != ONE) || nr.Op != OLITERAL {
-			if nl.Type.IsSlice() {
-				Yyerror("illegal slice comparison")
-			} else {
-				Yyerror("illegal interface comparison")
-			}
-			return
-		}
-
-		var ptr Node
-		Igen(nl, &ptr, nil)
-		if nl.Type.IsSlice() {
-			ptr.Xoffset += int64(Array_array)
-		}
-		ptr.Type = Types[Tptr]
-		var tmp Node
-		Regalloc(&tmp, ptr.Type, &ptr)
-		Cgen(&ptr, &tmp)
-		Regfree(&ptr)
-		bgenNonZero(&tmp, res, op == OEQ != wantTrue, likely, to)
-		Regfree(&tmp)
-		return
-	}
-
-	if nl.Type.IsComplex() {
-		complexbool(op, nl, nr, res, wantTrue, likely, to)
-		return
-	}
-
-	if Ctxt.Arch.RegSize == 4 && Is64(nr.Type) {
-		if genval {
-			// TODO: Teach Cmp64 to generate boolean values and remove this.
-			bvgenjump(n, res, wantTrue, false)
-			return
-		}
-		if !nl.Addable || Isconst(nl, CTINT) {
-			nl = CgenTemp(nl)
-		}
-		if !nr.Addable {
-			nr = CgenTemp(nr)
-		}
-		Thearch.Cmp64(nl, nr, op, likely, to)
-		return
-	}
-
-	if nr.Ullman >= UINF {
-		var n1 Node
-		Regalloc(&n1, nl.Type, nil)
-		Cgen(nl, &n1)
-		nl = &n1
-
-		var tmp Node
-		Tempname(&tmp, nl.Type)
-		Thearch.Gmove(&n1, &tmp)
-		Regfree(&n1)
-
-		var n2 Node
-		Regalloc(&n2, nr.Type, nil)
-		Cgen(nr, &n2)
-		nr = &n2
-
-		Regalloc(&n1, nl.Type, nil)
-		Cgen(&tmp, &n1)
-		Regfree(&n1)
-		Regfree(&n2)
-	} else {
-		var n1 Node
-		if !nl.Addable && Ctxt.Arch.Family == sys.I386 {
-			Tempname(&n1, nl.Type)
-		} else {
-			Regalloc(&n1, nl.Type, nil)
-			defer Regfree(&n1)
-		}
-		Cgen(nl, &n1)
-		nl = &n1
-
-		if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.PPC64 {
-			Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), nl, nr)
-			bins(nr.Type, res, op, likely, to)
-			return
-		}
-
-		if !nr.Addable && Ctxt.Arch.Family == sys.I386 {
-			nr = CgenTemp(nr)
-		}
-
-		var n2 Node
-		Regalloc(&n2, nr.Type, nil)
-		Cgen(nr, &n2)
-		nr = &n2
-		Regfree(&n2)
-	}
-
-	l, r := nl, nr
-
-	// On x86, only < and <= work right with NaN; reverse if needed
-	if Ctxt.Arch.Family == sys.AMD64 && nl.Type.IsFloat() && (op == OGT || op == OGE) {
-		l, r = r, l
-		op = Brrev(op)
-	}
-
-	// MIPS does not have CMP instruction
-	if Ctxt.Arch.Family == sys.MIPS64 {
-		p := Thearch.Ginscmp(op, nr.Type, l, r, likely)
-		Patch(p, to)
-		return
-	}
-
-	// Do the comparison.
-	Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), l, r)
-
-	// Handle floating point special cases.
-	// Note that 8g has Bgen_float and is handled above.
-	if nl.Type.IsFloat() {
-		switch Ctxt.Arch.Family {
-		case sys.ARM:
-			if genval {
-				Fatalf("genval 5g Isfloat special cases not implemented")
-			}
-			switch n.Op {
-			case ONE:
-				Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, likely), to)
-				Patch(Gbranch(Thearch.Optoas(op, nr.Type), nr.Type, likely), to)
-			default:
-				p := Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, -likely)
-				Patch(Gbranch(Thearch.Optoas(op, nr.Type), nr.Type, likely), to)
-				Patch(p, Pc)
-			}
-			return
-		case sys.AMD64:
-			switch n.Op {
-			case OEQ:
-				// neither NE nor P
-				if genval {
-					var reg Node
-					Regalloc(&reg, Types[TBOOL], nil)
-					Thearch.Ginsboolval(Thearch.Optoas(OEQ, nr.Type), &reg)
-					Thearch.Ginsboolval(Thearch.Optoas(OPC, nr.Type), res)
-					Thearch.Gins(Thearch.Optoas(OAND, Types[TBOOL]), &reg, res)
-					Regfree(&reg)
-				} else {
-					p1 := Gbranch(Thearch.Optoas(ONE, nr.Type), nil, -likely)
-					p2 := Gbranch(Thearch.Optoas(OPS, nr.Type), nil, -likely)
-					Patch(Gbranch(obj.AJMP, nil, 0), to)
-					Patch(p1, Pc)
-					Patch(p2, Pc)
-				}
-				return
-			case ONE:
-				// either NE or P
-				if genval {
-					var reg Node
-					Regalloc(&reg, Types[TBOOL], nil)
-					Thearch.Ginsboolval(Thearch.Optoas(ONE, nr.Type), &reg)
-					Thearch.Ginsboolval(Thearch.Optoas(OPS, nr.Type), res)
-					Thearch.Gins(Thearch.Optoas(OOR, Types[TBOOL]), &reg, res)
-					Regfree(&reg)
-				} else {
-					Patch(Gbranch(Thearch.Optoas(ONE, nr.Type), nil, likely), to)
-					Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nil, likely), to)
-				}
-				return
-			}
-		case sys.ARM64, sys.PPC64:
-			if genval {
-				Fatalf("genval 7g, 9g Isfloat special cases not implemented")
-			}
-			switch n.Op {
-			// On arm64 and ppc64, <= and >= mishandle NaN. Must decompose into < or > and =.
-			// TODO(josh): Convert a <= b to b > a instead?
-			case OLE, OGE:
-				if op == OLE {
-					op = OLT
-				} else {
-					op = OGT
-				}
-				Patch(Gbranch(Thearch.Optoas(op, nr.Type), nr.Type, likely), to)
-				Patch(Gbranch(Thearch.Optoas(OEQ, nr.Type), nr.Type, likely), to)
-				return
-			}
-		}
-	}
-
-	// Not a special case. Insert the conditional jump or value gen.
-	bins(nr.Type, res, op, likely, to)
-}
-
-func bgenNonZero(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
-	// TODO: Optimize on systems that can compare to zero easily.
-	var op Op = ONE
-	if !wantTrue {
-		op = OEQ
-	}
-
-	// MIPS does not have CMP instruction
-	if Thearch.LinkArch.Family == sys.MIPS64 {
-		p := Gbranch(Thearch.Optoas(op, n.Type), n.Type, likely)
-		Naddr(&p.From, n)
-		Patch(p, to)
-		return
-	}
-
-	var zero Node
-	Nodconst(&zero, n.Type, 0)
-	Thearch.Gins(Thearch.Optoas(OCMP, n.Type), n, &zero)
-	bins(n.Type, res, op, likely, to)
-}
-
-// bins inserts an instruction to handle the result of a compare.
-// If res is non-nil, it inserts appropriate value generation instructions.
-// If res is nil, it inserts a branch to to.
-func bins(typ *Type, res *Node, op Op, likely int, to *obj.Prog) {
-	a := Thearch.Optoas(op, typ)
-	if res != nil {
-		// value gen
-		Thearch.Ginsboolval(a, res)
-	} else {
-		// jump
-		Patch(Gbranch(a, typ, likely), to)
-	}
-}
-
-// stkof returns n's offset from SP if n is on the stack
-// (either a local variable or the return value from a function call
-// or the arguments to a function call).
-// If n is not on the stack, stkof returns -1000.
-// If n is on the stack but in an unknown location
-// (due to array index arithmetic), stkof returns +1000.
-//
-// NOTE(rsc): It is possible that the ODOT and OINDEX cases
-// are not relevant here, since it shouldn't be possible for them
-// to be involved in an overlapping copy. Only function results
-// from one call and the arguments to the next can overlap in
-// any non-trivial way. If they can be dropped, then this function
-// becomes much simpler and also more trustworthy.
-// The fact that it works at all today is probably due to the fact
-// that ODOT and OINDEX are irrelevant.
-func stkof(n *Node) int64 {
-	switch n.Op {
-	case OINDREG:
-		if n.Reg != int16(Thearch.REGSP) {
-			return -1000 // not on stack
-		}
-		return n.Xoffset
-
-	case ODOT:
-		t := n.Left.Type
-		if t.IsPtr() {
-			break
-		}
-		off := stkof(n.Left)
-		if off == -1000 || off == +1000 {
-			return off
-		}
-		return off + n.Xoffset
-
-	case OINDEX:
-		t := n.Left.Type
-		if !t.IsArray() {
-			break
-		}
-		off := stkof(n.Left)
-		if off == -1000 || off == +1000 {
-			return off
-		}
-		if Isconst(n.Right, CTINT) {
-			return off + t.Elem().Width*n.Right.Int64()
-		}
-		return +1000 // on stack but not sure exactly where
-
-	case OCALLMETH, OCALLINTER, OCALLFUNC:
-		t := n.Left.Type
-		if t.IsPtr() {
-			t = t.Elem()
-		}
-
-		f := t.Results().Field(0)
-		if f != nil {
-			return f.Offset + Ctxt.FixedFrameSize()
-		}
-	}
-
-	// botch - probably failing to recognize address
-	// arithmetic on the above. eg INDEX and DOT
-	return -1000 // not on stack
-}
-
-// block copy:
-//	memmove(&ns, &n, w);
-// if wb is true, needs write barrier.
-func sgen_wb(n *Node, ns *Node, w int64, wb bool) {
-	if Debug['g'] != 0 {
-		op := "sgen"
-		if wb {
-			op = "sgen-wb"
-		}
-		fmt.Printf("\n%s w=%d\n", op, w)
-		Dump("r", n)
-		Dump("res", ns)
-	}
-
-	if n.Ullman >= UINF && ns.Ullman >= UINF {
-		Fatalf("sgen UINF")
-	}
-
-	if w < 0 {
-		Fatalf("sgen copy %d", w)
-	}
-
-	// If copying .args, that's all the results, so record definition sites
-	// for them for the liveness analysis.
-	if ns.Op == ONAME && ns.Sym.Name == ".args" {
-		for _, ln := range Curfn.Func.Dcl {
-			if ln.Class == PPARAMOUT {
-				Gvardef(ln)
-			}
-		}
-	}
-
-	// Avoid taking the address for simple enough types.
-	if componentgen_wb(n, ns, wb) {
-		return
-	}
-
-	if w == 0 {
-		// evaluate side effects only
-		var nodr Node
-		Regalloc(&nodr, Types[Tptr], nil)
-		Agen(ns, &nodr)
-		Agen(n, &nodr)
-		Regfree(&nodr)
-		return
-	}
-
-	// offset on the stack
-	osrc := stkof(n)
-	odst := stkof(ns)
-
-	if odst != -1000 {
-		// on stack, write barrier not needed after all
-		wb = false
-	}
-
-	if osrc != -1000 && odst != -1000 && (osrc == 1000 || odst == 1000) || wb && osrc != -1000 {
-		// osrc and odst both on stack, and at least one is in
-		// an unknown position. Could generate code to test
-		// for forward/backward copy, but instead just copy
-		// to a temporary location first.
-		//
-		// OR: write barrier needed and source is on stack.
-		// Invoking the write barrier will use the stack to prepare its call.
-		// Copy to temporary.
-		var tmp Node
-		Tempname(&tmp, n.Type)
-		sgen_wb(n, &tmp, w, false)
-		sgen_wb(&tmp, ns, w, wb)
-		return
-	}
-
-	if wb {
-		cgen_wbfat(n, ns)
-		return
-	}
-
-	Thearch.Blockcopy(n, ns, osrc, odst, w)
-}
-
-// generate:
-//	call f
-//	proc=-1	normal call but no return
-//	proc=0	normal call
-//	proc=1	goroutine run in new proc
-//	proc=2	defer call save away stack
-//	proc=3	normal call to C pointer (not Go func value)
-func Ginscall(f *Node, proc int) {
-	if f.Type != nil {
-		extra := int32(0)
-		if proc == 1 || proc == 2 {
-			extra = 2 * int32(Widthptr)
-		}
-		Setmaxarg(f.Type, extra)
-	}
-
-	switch proc {
-	default:
-		Fatalf("Ginscall: bad proc %d", proc)
-
-	case 0, // normal call
-		-1: // normal call but no return
-		if f.Op == ONAME && f.Class == PFUNC {
-			if f == Deferreturn {
-				// Deferred calls will appear to be returning to the CALL
-				// deferreturn(SB) that we are about to emit. However, the
-				// stack scanning code will think that the instruction
-				// before the CALL is executing. To avoid the scanning
-				// code making bad assumptions (both cosmetic such as
-				// showing the wrong line number and fatal, such as being
-				// confused over whether a stack slot contains a pointer
-				// or a scalar) insert an actual hardware NOP that will
-				// have the right line number. This is different from
-				// obj.ANOP, which is a virtual no-op that doesn't make it
-				// into the instruction stream.
-				Thearch.Ginsnop()
-
-				if Thearch.LinkArch.Family == sys.PPC64 {
-					// On ppc64, when compiling Go into position
-					// independent code on ppc64le we insert an
-					// instruction to reload the TOC pointer from the
-					// stack as well. See the long comment near
-					// jmpdefer in runtime/asm_ppc64.s for why.
-					// If the MOVD is not needed, insert a hardware NOP
-					// so that the same number of instructions are used
-					// on ppc64 in both shared and non-shared modes.
-					if Ctxt.Flag_shared {
-						p := Thearch.Gins(ppc64.AMOVD, nil, nil)
-						p.From.Type = obj.TYPE_MEM
-						p.From.Offset = 24
-						p.From.Reg = ppc64.REGSP
-						p.To.Type = obj.TYPE_REG
-						p.To.Reg = ppc64.REG_R2
-					} else {
-						Thearch.Ginsnop()
-					}
-				}
-			}
-
-			p := Thearch.Gins(obj.ACALL, nil, f)
-			Afunclit(&p.To, f)
-			if proc == -1 || Noreturn(p) {
-				Thearch.Gins(obj.AUNDEF, nil, nil)
-			}
-			break
-		}
-
-		var reg Node
-		Nodreg(&reg, Types[Tptr], Thearch.REGCTXT)
-		var r1 Node
-		Nodreg(&r1, Types[Tptr], Thearch.REGCALLX)
-		Thearch.Gmove(f, &reg)
-		reg.Op = OINDREG
-		Thearch.Gmove(&reg, &r1)
-		reg.Op = OREGISTER
-		Thearch.Gins(obj.ACALL, &reg, &r1)
-
-	case 3: // normal call of c function pointer
-		Thearch.Gins(obj.ACALL, nil, f)
-
-	case 1, // call in new proc (go)
-		2: // deferred call (defer)
-		var stk Node
-
-		// size of arguments at 0(SP)
-		stk.Op = OINDREG
-		stk.Reg = int16(Thearch.REGSP)
-		stk.Xoffset = Ctxt.FixedFrameSize()
-		Thearch.Ginscon(Thearch.Optoas(OAS, Types[TINT32]), int64(Argsize(f.Type)), &stk)
-
-		// FuncVal* at 8(SP)
-		stk.Xoffset = int64(Widthptr) + Ctxt.FixedFrameSize()
-
-		var reg Node
-		Nodreg(&reg, Types[Tptr], Thearch.REGCALLX2)
-		Thearch.Gmove(f, &reg)
-		Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &reg, &stk)
-
-		if proc == 1 {
-			Ginscall(Newproc, 0)
-		} else {
-			if !hasdefer {
-				Fatalf("hasdefer=0 but has defer")
-			}
-			Ginscall(Deferproc, 0)
-		}
-
-		if proc == 2 {
-			Nodreg(&reg, Types[TINT32], Thearch.REGRETURN)
-			p := Thearch.Ginscmp(OEQ, Types[TINT32], &reg, Nodintconst(0), +1)
-			cgen_ret(nil)
-			Patch(p, Pc)
-		}
-	}
-}
-
-// n is call to interface method.
-// generate res = n.
-func cgen_callinter(n *Node, res *Node, proc int) {
-	i := n.Left
-	if i.Op != ODOTINTER {
-		Fatalf("cgen_callinter: not ODOTINTER %v", i.Op)
-	}
-
-	i = i.Left // interface
-
-	if !i.Addable {
-		var tmpi Node
-		Tempname(&tmpi, i.Type)
-		Cgen(i, &tmpi)
-		i = &tmpi
-	}
-
-	Genlist(n.List) // assign the args
-
-	// i is now addable, prepare an indirected
-	// register to hold its address.
-	var nodi Node
-	Igen(i, &nodi, res) // REG = &inter
-
-	var nodsp Node
-	Nodindreg(&nodsp, Types[Tptr], Thearch.REGSP)
-	nodsp.Xoffset = Ctxt.FixedFrameSize()
-	if proc != 0 {
-		nodsp.Xoffset += 2 * int64(Widthptr) // leave room for size & fn
-	}
-	nodi.Type = Types[Tptr]
-	nodi.Xoffset += int64(Widthptr)
-	Cgen(&nodi, &nodsp) // {0, 8(nacl), or 16}(SP) = 8(REG) -- i.data
-
-	var nodo Node
-	Regalloc(&nodo, Types[Tptr], res)
-
-	nodi.Type = Types[Tptr]
-	nodi.Xoffset -= int64(Widthptr)
-	Cgen(&nodi, &nodo) // REG = 0(REG) -- i.tab
-	Regfree(&nodi)
-
-	var nodr Node
-	Regalloc(&nodr, Types[Tptr], &nodo)
-	if n.Left.Xoffset == BADWIDTH {
-		Fatalf("cgen_callinter: badwidth")
-	}
-	Cgen_checknil(&nodo) // in case offset is huge
-	nodo.Op = OINDREG
-	nodo.Xoffset = n.Left.Xoffset + 3*int64(Widthptr) + 8
-	if proc == 0 {
-		// plain call: use direct c function pointer - more efficient
-		Cgen(&nodo, &nodr) // REG = 32+offset(REG) -- i.tab->fun[f]
-		proc = 3
-	} else {
-		// go/defer. generate go func value.
-		Agen(&nodo, &nodr) // REG = &(32+offset(REG)) -- i.tab->fun[f]
-	}
-
-	nodr.Type = n.Left.Type
-	Ginscall(&nodr, proc)
-
-	Regfree(&nodr)
-	Regfree(&nodo)
-}
-
-// generate function call;
-//	proc=0	normal call
-//	proc=1	goroutine run in new proc
-//	proc=2	defer call save away stack
-func cgen_call(n *Node, proc int) {
-	if n == nil {
-		return
-	}
-
-	var afun Node
-	if n.Left.Ullman >= UINF {
-		// if name involves a fn call
-		// precompute the address of the fn
-		Tempname(&afun, Types[Tptr])
-
-		Cgen(n.Left, &afun)
-	}
-
-	Genlist(n.List) // assign the args
-	t := n.Left.Type
-
-	// call tempname pointer
-	if n.Left.Ullman >= UINF {
-		var nod Node
-		Regalloc(&nod, Types[Tptr], nil)
-		Cgen_as(&nod, &afun)
-		nod.Type = t
-		Ginscall(&nod, proc)
-		Regfree(&nod)
-		return
-	}
-
-	// call pointer
-	if n.Left.Op != ONAME || n.Left.Class != PFUNC {
-		var nod Node
-		Regalloc(&nod, Types[Tptr], nil)
-		Cgen_as(&nod, n.Left)
-		nod.Type = t
-		Ginscall(&nod, proc)
-		Regfree(&nod)
-		return
-	}
-
-	// call direct
-	n.Left.Name.Method = true
-
-	Ginscall(n.Left, proc)
-}
-
-// call to n has already been generated.
-// generate:
-//	res = return value from call.
-func cgen_callret(n *Node, res *Node) {
-	t := n.Left.Type
-	if t.Etype == TPTR32 || t.Etype == TPTR64 {
-		t = t.Elem()
-	}
-
-	fp := t.Results().Field(0)
-	if fp == nil {
-		Fatalf("cgen_callret: nil")
-	}
-
-	var nod Node
-	nod.Op = OINDREG
-	nod.Reg = int16(Thearch.REGSP)
-	nod.Addable = true
-
-	nod.Xoffset = fp.Offset + Ctxt.FixedFrameSize()
-	nod.Type = fp.Type
-	Cgen_as(res, &nod)
-}
-
-// call to n has already been generated.
-// generate:
-//	res = &return value from call.
-func cgen_aret(n *Node, res *Node) {
-	t := n.Left.Type
-	if t.IsPtr() {
-		t = t.Elem()
-	}
-
-	fp := t.Results().Field(0)
-	if fp == nil {
-		Fatalf("cgen_aret: nil")
-	}
-
-	var nod1 Node
-	nod1.Op = OINDREG
-	nod1.Reg = int16(Thearch.REGSP)
-	nod1.Addable = true
-	nod1.Xoffset = fp.Offset + Ctxt.FixedFrameSize()
-	nod1.Type = fp.Type
-
-	if res.Op != OREGISTER {
-		var nod2 Node
-		Regalloc(&nod2, Types[Tptr], res)
-		Agen(&nod1, &nod2)
-		Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &nod2, res)
-		Regfree(&nod2)
-	} else {
-		Agen(&nod1, res)
-	}
-}
-
-// generate return.
-// n->left is assignments to return values.
-func cgen_ret(n *Node) {
-	if n != nil {
-		Genlist(n.List) // copy out args
-	}
-	if hasdefer {
-		Ginscall(Deferreturn, 0)
-	}
-	Genlist(Curfn.Func.Exit)
-	p := Thearch.Gins(obj.ARET, nil, nil)
-	if n != nil && n.Op == ORETJMP {
-		p.To.Type = obj.TYPE_MEM
-		p.To.Name = obj.NAME_EXTERN
-		p.To.Sym = Linksym(n.Left.Sym)
-	}
-}
-
-// hasHMUL64 reports whether the architecture supports 64-bit
-// signed and unsigned high multiplication (OHMUL).
-func hasHMUL64() bool {
-	switch Ctxt.Arch.Family {
-	case sys.AMD64, sys.S390X, sys.ARM64:
-		return true
-	case sys.ARM, sys.I386, sys.MIPS64, sys.PPC64:
-		return false
-	}
-	Fatalf("unknown architecture")
-	return false
-}
-
-// hasRROTC64 reports whether the architecture supports 64-bit
-// rotate through carry instructions (ORROTC).
-func hasRROTC64() bool {
-	switch Ctxt.Arch.Family {
-	case sys.AMD64:
-		return true
-	case sys.ARM, sys.ARM64, sys.I386, sys.MIPS64, sys.PPC64, sys.S390X:
-		return false
-	}
-	Fatalf("unknown architecture")
-	return false
-}
-
-func hasRightShiftWithCarry() bool {
-	switch Ctxt.Arch.Family {
-	case sys.ARM64:
-		return true
-	case sys.AMD64, sys.ARM, sys.I386, sys.MIPS64, sys.PPC64, sys.S390X:
-		return false
-	}
-	Fatalf("unknown architecture")
-	return false
-}
-
-func hasAddSetCarry() bool {
-	switch Ctxt.Arch.Family {
-	case sys.ARM64:
-		return true
-	case sys.AMD64, sys.ARM, sys.I386, sys.MIPS64, sys.PPC64, sys.S390X:
-		return false
-	}
-	Fatalf("unknown architecture")
-	return false
-}
-
-// generate division according to op, one of:
-//	res = nl / nr
-//	res = nl % nr
-func cgen_div(op Op, nl *Node, nr *Node, res *Node) {
-	var w int
-
-	// Architectures need to support 64-bit high multiplications
-	// (OHMUL) in order to perform divide by constant optimizations.
-	if nr.Op != OLITERAL || !hasHMUL64() {
-		goto longdiv
-	}
-	w = int(nl.Type.Width * 8)
-
-	// Front end handled 32-bit division. We only need to handle 64-bit.
-	// Try to do division using multiplication: (2^w)/d.
-	// See Hacker's Delight, chapter 10.
-	switch Simtype[nl.Type.Etype] {
-	default:
-		goto longdiv
-
-	case TUINT64:
-		var m Magic
-		m.W = w
-		m.Ud = uint64(nr.Int64())
-		Umagic(&m)
-		if m.Bad != 0 {
-			break
-		}
-
-		// In order to add the numerator we need to be able to
-		// avoid overflow. This is done by shifting the result of the
-		// addition right by 1 and inserting the carry bit into
-		// the MSB. For now this needs the RROTC instruction.
-		// TODO(mundaym): Hacker's Delight 2nd ed. chapter 10 proposes
-		// an alternative sequence of instructions for architectures
-		// (TODO: MIPS64, PPC64, S390X) that do not have a shift
-		// right with carry instruction.
-		if m.Ua != 0 && !hasRROTC64() && !hasRightShiftWithCarry() {
-			goto longdiv
-		}
-		if op == OMOD {
-			goto longmod
-		}
-
-		var n1 Node
-		Cgenr(nl, &n1, nil)
-		var n2 Node
-		Nodconst(&n2, nl.Type, int64(m.Um))
-		var n3 Node
-		Regalloc(&n3, nl.Type, res)
-		Thearch.Cgen_hmul(&n1, &n2, &n3)
-
-		if m.Ua != 0 {
-			// Need to add numerator accounting for overflow.
-			if hasAddSetCarry() {
-				Thearch.AddSetCarry(&n1, &n3, &n3)
-			} else {
-				Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3)
-			}
-
-			if !hasRROTC64() {
-				Thearch.RightShiftWithCarry(&n3, uint(m.S), &n3)
-			} else {
-				Nodconst(&n2, nl.Type, 1)
-				Thearch.Gins(Thearch.Optoas(ORROTC, nl.Type), &n2, &n3)
-				Nodconst(&n2, nl.Type, int64(m.S)-1)
-				Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3)
-			}
-		} else {
-			Nodconst(&n2, nl.Type, int64(m.S))
-			Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) // shift dx
-		}
-
-		Thearch.Gmove(&n3, res)
-		Regfree(&n1)
-		Regfree(&n3)
-		return
-
-	case TINT64:
-		var m Magic
-		m.W = w
-		m.Sd = nr.Int64()
-		Smagic(&m)
-		if m.Bad != 0 {
-			break
-		}
-		if op == OMOD {
-			goto longmod
-		}
-
-		var n1 Node
-		Cgenr(nl, &n1, res)
-		var n2 Node
-		Nodconst(&n2, nl.Type, m.Sm)
-		var n3 Node
-		Regalloc(&n3, nl.Type, nil)
-		Thearch.Cgen_hmul(&n1, &n2, &n3)
-
-		if m.Sm < 0 {
-			// Need to add numerator (cannot overflow).
-			Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3)
-		}
-
-		Nodconst(&n2, nl.Type, int64(m.S))
-		Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) // shift n3
-
-		Nodconst(&n2, nl.Type, int64(w)-1)
-
-		Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n1) // -1 iff num is neg
-		Thearch.Gins(Thearch.Optoas(OSUB, nl.Type), &n1, &n3) // added
-
-		if m.Sd < 0 {
-			// This could probably be removed by factoring it into
-			// the multiplier.
-			Thearch.Gins(Thearch.Optoas(OMINUS, nl.Type), nil, &n3)
-		}
-
-		Thearch.Gmove(&n3, res)
-		Regfree(&n1)
-		Regfree(&n3)
-		return
-	}
-
-	goto longdiv
-
-	// Division and mod using (slow) hardware instruction.
-longdiv:
-	Thearch.Dodiv(op, nl, nr, res)
-
-	return
-
-	// Mod using formula A%B = A-(A/B*B) but
-	// we know that there is a fast algorithm for A/B.
-longmod:
-	var n1 Node
-	Regalloc(&n1, nl.Type, res)
-
-	Cgen(nl, &n1)
-	var n2 Node
-	Regalloc(&n2, nl.Type, nil)
-	cgen_div(ODIV, &n1, nr, &n2)
-	a := Thearch.Optoas(OMUL, nl.Type)
-
-	if !Smallintconst(nr) {
-		var n3 Node
-		Regalloc(&n3, nl.Type, nil)
-		Cgen(nr, &n3)
-		Thearch.Gins(a, &n3, &n2)
-		Regfree(&n3)
-	} else {
-		Thearch.Gins(a, nr, &n2)
-	}
-	Thearch.Gins(Thearch.Optoas(OSUB, nl.Type), &n2, &n1)
-	Thearch.Gmove(&n1, res)
-	Regfree(&n1)
-	Regfree(&n2)
-}
-
-func Fixlargeoffset(n *Node) {
-	if n == nil {
-		return
-	}
-	if n.Op != OINDREG {
-		return
-	}
-	if n.Reg == int16(Thearch.REGSP) { // stack offset cannot be large
-		return
-	}
-	if n.Xoffset != int64(int32(n.Xoffset)) {
-		// offset too large, add to register instead.
-		a := *n
-
-		a.Op = OREGISTER
-		a.Type = Types[Tptr]
-		a.Xoffset = 0
-		Cgen_checknil(&a)
-		Thearch.Ginscon(Thearch.Optoas(OADD, Types[Tptr]), n.Xoffset, &a)
-		n.Xoffset = 0
-	}
-}
-
-func cgen_append(n, res *Node) {
-	if Debug['g'] != 0 {
-		Dump("cgen_append-n", n)
-		Dump("cgen_append-res", res)
-	}
-	for _, n1 := range n.List.Slice() {
-		if n1.Ullman >= UINF {
-			Fatalf("append with function call arguments")
-		}
-	}
-
-	// res = append(src, x, y, z)
-	//
-	// If res and src are the same, we can avoid writing to base and cap
-	// unless we grow the underlying array.
-	needFullUpdate := !samesafeexpr(res, n.List.First())
-
-	// Copy src triple into base, len, cap.
-	base := temp(Types[Tptr])
-	len := temp(Types[TUINT])
-	cap := temp(Types[TUINT])
-
-	var src Node
-	Igen(n.List.First(), &src, nil)
-	src.Type = Types[Tptr]
-	Thearch.Gmove(&src, base)
-	src.Type = Types[TUINT]
-	src.Xoffset += int64(Widthptr)
-	Thearch.Gmove(&src, len)
-	src.Xoffset += int64(Widthptr)
-	Thearch.Gmove(&src, cap)
-
-	// if len+argc <= cap goto L1
-	var rlen Node
-	Regalloc(&rlen, Types[TUINT], nil)
-	Thearch.Gmove(len, &rlen)
-	Thearch.Ginscon(Thearch.Optoas(OADD, Types[TUINT]), int64(n.List.Len()-1), &rlen)
-	p := Thearch.Ginscmp(OLE, Types[TUINT], &rlen, cap, +1)
-	// Note: rlen and src are Regrealloc'ed below at the target of the
-	// branch we just emitted; do not reuse these Go variables for
-	// other purposes. They need to still describe the same things
-	// below that they describe right here.
-	Regfree(&src)
-
-	// base, len, cap = growslice(type, base, len, cap, newlen)
-	var arg Node
-	arg.Op = OINDREG
-	arg.Reg = int16(Thearch.REGSP)
-	arg.Addable = true
-	arg.Xoffset = Ctxt.FixedFrameSize()
-	arg.Type = Ptrto(Types[TUINT8])
-	Cgen(typename(res.Type.Elem()), &arg)
-	arg.Xoffset += int64(Widthptr)
-
-	arg.Type = Types[Tptr]
-	Cgen(base, &arg)
-	arg.Xoffset += int64(Widthptr)
-
-	arg.Type = Types[TUINT]
-	Cgen(len, &arg)
-	arg.Xoffset += int64(Widthptr)
-
-	arg.Type = Types[TUINT]
-	Cgen(cap, &arg)
-	arg.Xoffset += int64(Widthptr)
-
-	arg.Type = Types[TUINT]
-	Cgen(&rlen, &arg)
-	arg.Xoffset += int64(Widthptr)
-	Regfree(&rlen)
-
-	fn := syslook("growslice")
-	fn = substArgTypes(fn, res.Type.Elem(), res.Type.Elem())
-	Ginscall(fn, 0)
-
-	if Widthptr == 4 && Widthreg == 8 {
-		arg.Xoffset += 4
-	}
-
-	arg.Type = Types[Tptr]
-	Cgen(&arg, base)
-	arg.Xoffset += int64(Widthptr)
-
-	arg.Type = Types[TUINT]
-	Cgen(&arg, len)
-	arg.Xoffset += int64(Widthptr)
-
-	arg.Type = Types[TUINT]
-	Cgen(&arg, cap)
-
-	// Update res with base, len+argc, cap.
-	if needFullUpdate {
-		if Debug_append > 0 {
-			Warn("append: full update")
-		}
-		Patch(p, Pc)
-	}
-	if res.Op == ONAME {
-		Gvardef(res)
-	}
-	var dst, r1 Node
-	Igen(res, &dst, nil)
-	dst.Type = Types[TUINT]
-	dst.Xoffset += int64(Widthptr)
-	Regalloc(&r1, Types[TUINT], nil)
-	Thearch.Gmove(len, &r1)
-	Thearch.Ginscon(Thearch.Optoas(OADD, Types[TUINT]), int64(n.List.Len()-1), &r1)
-	Thearch.Gmove(&r1, &dst)
-	Regfree(&r1)
-	dst.Xoffset += int64(Widthptr)
-	Thearch.Gmove(cap, &dst)
-	dst.Type = Types[Tptr]
-	dst.Xoffset -= 2 * int64(Widthptr)
-	cgen_wb(base, &dst, needwritebarrier(&dst, base))
-	Regfree(&dst)
-
-	if !needFullUpdate {
-		if Debug_append > 0 {
-			Warn("append: len-only update")
-		}
-		// goto L2;
-		// L1:
-		//	update len only
-		// L2:
-		q := Gbranch(obj.AJMP, nil, 0)
-		Patch(p, Pc)
-		// At the goto above, src refers to cap and rlen holds the new len
-		if src.Op == OREGISTER || src.Op == OINDREG {
-			Regrealloc(&src)
-		}
-		Regrealloc(&rlen)
-		src.Xoffset -= int64(Widthptr)
-		Thearch.Gmove(&rlen, &src)
-		Regfree(&src)
-		Regfree(&rlen)
-		Patch(q, Pc)
-	}
-
-	// Copy data into place.
-	// Could do write barrier check around entire copy instead of each element.
-	// Could avoid reloading registers on each iteration if we know the cgen_wb
-	// is not going to use a write barrier.
-	i := 0
-	var r2 Node
-	for _, n2 := range n.List.Slice()[1:] {
-		Regalloc(&r1, Types[Tptr], nil)
-		Thearch.Gmove(base, &r1)
-		Regalloc(&r2, Types[TUINT], nil)
-		Thearch.Gmove(len, &r2)
-		if i > 0 {
-			Thearch.Gins(Thearch.Optoas(OADD, Types[TUINT]), Nodintconst(int64(i)), &r2)
-		}
-		w := res.Type.Elem().Width
-		if Thearch.AddIndex != nil && Thearch.AddIndex(&r2, w, &r1) {
-			// r1 updated by back end
-		} else if w == 1 {
-			Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &r2, &r1)
-		} else {
-			Thearch.Ginscon(Thearch.Optoas(OMUL, Types[TUINT]), w, &r2)
-			Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &r2, &r1)
-		}
-		Regfree(&r2)
-
-		r1.Op = OINDREG
-		r1.Type = res.Type.Elem()
-		cgen_wb(n2, &r1, needwritebarrier(&r1, n2))
-		Regfree(&r1)
-		i++
-	}
-}
-
-// Generate res = n, where n is x[i:j] or x[i:j:k].
-// If wb is true, need write barrier updating res's base pointer.
-// On systems with 32-bit ints, i, j, k are guaranteed to be 32-bit values.
-func cgen_slice(n, res *Node, wb bool) {
-	if Debug['g'] != 0 {
-		Dump("cgen_slice-n", n)
-		Dump("cgen_slice-res", res)
-	}
-
-	needFullUpdate := !samesafeexpr(n.Left, res)
-
-	// orderexpr has made sure that x is safe (but possibly expensive)
-	// and i, j, k are cheap. On a system with registers (anything but 386)
-	// we can evaluate x first and then know we have enough registers
-	// for i, j, k as well.
-	var x, xbase, xlen, xcap, i, j, k Node
-	if n.Op != OSLICEARR && n.Op != OSLICE3ARR {
-		Igen(n.Left, &x, nil)
-	}
-
-	indexRegType := Types[TUINT]
-	if Widthreg > Widthptr { // amd64p32
-		indexRegType = Types[TUINT64]
-	}
-
-	// On most systems, we use registers.
-	// The 386 has basically no registers, so substitute functions
-	// that can work with temporaries instead.
-	regalloc := Regalloc
-	ginscon := Thearch.Ginscon
-	gins := Thearch.Gins
-	if Thearch.LinkArch.Family == sys.I386 {
-		regalloc = func(n *Node, t *Type, reuse *Node) {
-			Tempname(n, t)
-		}
-		ginscon = func(as obj.As, c int64, n *Node) {
-			var n1 Node
-			Regalloc(&n1, n.Type, n)
-			Thearch.Gmove(n, &n1)
-			Thearch.Ginscon(as, c, &n1)
-			Thearch.Gmove(&n1, n)
-			Regfree(&n1)
-		}
-		gins = func(as obj.As, f, t *Node) *obj.Prog {
-			var n1 Node
-			Regalloc(&n1, t.Type, t)
-			Thearch.Gmove(t, &n1)
-			Thearch.Gins(as, f, &n1)
-			Thearch.Gmove(&n1, t)
-			Regfree(&n1)
-			return nil
-		}
-	}
-
-	panics := make([]*obj.Prog, 0, 6) // 3 loads + 3 checks
-
-	loadlen := func() {
-		if xlen.Op != 0 {
-			return
-		}
-		if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
-			Nodconst(&xlen, indexRegType, n.Left.Type.Elem().NumElem())
-			return
-		}
-		if n.Op == OSLICESTR && Isconst(n.Left, CTSTR) {
-			Nodconst(&xlen, indexRegType, int64(len(n.Left.Val().U.(string))))
-			return
-		}
-		regalloc(&xlen, indexRegType, nil)
-		x.Xoffset += int64(Widthptr)
-		x.Type = Types[TUINT]
-		Thearch.Gmove(&x, &xlen)
-		x.Xoffset -= int64(Widthptr)
-	}
-
-	loadcap := func() {
-		if xcap.Op != 0 {
-			return
-		}
-		if n.Op == OSLICEARR || n.Op == OSLICE3ARR || n.Op == OSLICESTR {
-			loadlen()
-			xcap = xlen
-			if xcap.Op == OREGISTER {
-				Regrealloc(&xcap)
-			}
-			return
-		}
-		regalloc(&xcap, indexRegType, nil)
-		x.Xoffset += 2 * int64(Widthptr)
-		x.Type = Types[TUINT]
-		Thearch.Gmove(&x, &xcap)
-		x.Xoffset -= 2 * int64(Widthptr)
-	}
-
-	x1, x2, x3 := n.SliceBounds() // unevaluated index arguments
-
-	// load computes src into targ, but if src refers to the len or cap of n.Left,
-	// load copies those from xlen, xcap, loading xlen if needed.
-	// If targ.Op == OREGISTER on return, it must be Regfreed,
-	// but it should not be modified without first checking whether it is
-	// xlen or xcap's register.
-	load := func(src, targ *Node) {
-		if src == nil {
-			return
-		}
-		switch src.Op {
-		case OLITERAL:
-			*targ = *src
-			return
-		case OLEN:
-			// NOTE(rsc): This doesn't actually trigger, because order.go
-			// has pulled all the len and cap calls into separate assignments
-			// to temporaries. There are tests in test/sliceopt.go that could
-			// be enabled if this is fixed.
-			if samesafeexpr(n.Left, src.Left) {
-				if Debug_slice > 0 {
-					Warn("slice: reuse len")
-				}
-				loadlen()
-				*targ = xlen
-				if targ.Op == OREGISTER {
-					Regrealloc(targ)
-				}
-				return
-			}
-		case OCAP:
-			// NOTE(rsc): This doesn't actually trigger; see note in case OLEN above.
-			if samesafeexpr(n.Left, src.Left) {
-				if Debug_slice > 0 {
-					Warn("slice: reuse cap")
-				}
-				loadcap()
-				*targ = xcap
-				if targ.Op == OREGISTER {
-					Regrealloc(targ)
-				}
-				return
-			}
-		}
-		if i.Op != 0 && samesafeexpr(x1, src) {
-			if Debug_slice > 0 {
-				Warn("slice: reuse 1st index")
-			}
-			*targ = i
-			if targ.Op == OREGISTER {
-				Regrealloc(targ)
-			}
-			return
-		}
-		if j.Op != 0 && samesafeexpr(x2, src) {
-			if Debug_slice > 0 {
-				Warn("slice: reuse 2nd index")
-			}
-			*targ = j
-			if targ.Op == OREGISTER {
-				Regrealloc(targ)
-			}
-			return
-		}
-		if Thearch.Cgenindex != nil {
-			regalloc(targ, indexRegType, nil)
-			p := Thearch.Cgenindex(src, targ, false)
-			if p != nil {
-				panics = append(panics, p)
-			}
-		} else if Thearch.Igenindex != nil {
-			p := Thearch.Igenindex(src, targ, false)
-			if p != nil {
-				panics = append(panics, p)
-			}
-		} else {
-			regalloc(targ, indexRegType, nil)
-			var tmp Node
-			Cgenr(src, &tmp, targ)
-			Thearch.Gmove(&tmp, targ)
-			Regfree(&tmp)
-		}
-	}
-
-	load(x1, &i)
-	load(x2, &j)
-	load(x3, &k)
-
-	// i defaults to 0.
-	if i.Op == 0 {
-		Nodconst(&i, indexRegType, 0)
-	}
-
-	// j defaults to len(x)
-	if j.Op == 0 {
-		loadlen()
-		j = xlen
-		if j.Op == OREGISTER {
-			Regrealloc(&j)
-		}
-	}
-
-	// k defaults to cap(x)
-	// Only need to load it if we're recalculating cap or doing a full update.
-	if k.Op == 0 && n.Op != OSLICESTR && (!iszero(&i) || needFullUpdate) {
-		loadcap()
-		k = xcap
-		if k.Op == OREGISTER {
-			Regrealloc(&k)
-		}
-	}
-
-	// Check constant indexes for negative values, and against constant length if known.
-	// The func obvious below checks for out-of-order constant indexes.
-	var bound int64 = -1
-	if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
-		bound = n.Left.Type.Elem().NumElem()
-	} else if n.Op == OSLICESTR && Isconst(n.Left, CTSTR) {
-		bound = int64(len(n.Left.Val().U.(string)))
-	}
-	if Isconst(&i, CTINT) {
-		if i.Val().U.(*Mpint).CmpInt64(0) < 0 || bound >= 0 && i.Val().U.(*Mpint).CmpInt64(bound) > 0 {
-			Yyerror("slice index out of bounds")
-		}
-	}
-	if Isconst(&j, CTINT) {
-		if j.Val().U.(*Mpint).CmpInt64(0) < 0 || bound >= 0 && j.Val().U.(*Mpint).CmpInt64(bound) > 0 {
-			Yyerror("slice index out of bounds")
-		}
-	}
-	if Isconst(&k, CTINT) {
-		if k.Val().U.(*Mpint).CmpInt64(0) < 0 || bound >= 0 && k.Val().U.(*Mpint).CmpInt64(bound) > 0 {
-			Yyerror("slice index out of bounds")
-		}
-	}
-
-	// same reports whether n1 and n2 are the same register or constant.
-	same := func(n1, n2 *Node) bool {
-		return n1.Op == OREGISTER && n2.Op == OREGISTER && n1.Reg == n2.Reg ||
-			n1.Op == ONAME && n2.Op == ONAME && n1.Orig == n2.Orig && n1.Type == n2.Type && n1.Xoffset == n2.Xoffset ||
-			n1.Op == OLITERAL && n2.Op == OLITERAL && n1.Val().U.(*Mpint).Cmp(n2.Val().U.(*Mpint)) == 0
-	}
-
-	// obvious reports whether n1 <= n2 is obviously true,
-	// and it calls Yyerror if n1 <= n2 is obviously false.
-	obvious := func(n1, n2 *Node) bool {
-		if Debug['B'] != 0 { // -B disables bounds checks
-			return true
-		}
-		if same(n1, n2) {
-			return true // n1 == n2
-		}
-		if iszero(n1) {
-			return true // using unsigned compare, so 0 <= n2 always true
-		}
-		if xlen.Op != 0 && same(n1, &xlen) && xcap.Op != 0 && same(n2, &xcap) {
-			return true // len(x) <= cap(x) always true
-		}
-		if Isconst(n1, CTINT) && Isconst(n2, CTINT) {
-			if n1.Val().U.(*Mpint).Cmp(n2.Val().U.(*Mpint)) <= 0 {
-				return true // n1, n2 constants such that n1 <= n2
-			}
-			Yyerror("slice index out of bounds")
-			return true
-		}
-		return false
-	}
-
-	compare := func(n1, n2 *Node) {
-		// n1 might be a 64-bit constant, even on 32-bit architectures,
-		// but it will be represented in 32 bits.
-		if Ctxt.Arch.RegSize == 4 && Is64(n1.Type) {
-			if n1.Val().U.(*Mpint).CmpInt64(1<<31) >= 0 {
-				Fatalf("missed slice out of bounds check")
-			}
-			var tmp Node
-			Nodconst(&tmp, indexRegType, n1.Int64())
-			n1 = &tmp
-		}
-		p := Thearch.Ginscmp(OGT, indexRegType, n1, n2, -1)
-		panics = append(panics, p)
-	}
-
-	loadcap()
-	max := &xcap
-	if k.Op != 0 && (n.Op == OSLICE3 || n.Op == OSLICE3ARR) {
-		if obvious(&k, max) {
-			if Debug_slice > 0 {
-				Warn("slice: omit check for 3rd index")
-			}
-		} else {
-			compare(&k, max)
-		}
-		max = &k
-	}
-	if j.Op != 0 {
-		if obvious(&j, max) {
-			if Debug_slice > 0 {
-				Warn("slice: omit check for 2nd index")
-			}
-		} else {
-			compare(&j, max)
-		}
-		max = &j
-	}
-	if i.Op != 0 {
-		if obvious(&i, max) {
-			if Debug_slice > 0 {
-				Warn("slice: omit check for 1st index")
-			}
-		} else {
-			compare(&i, max)
-		}
-		max = &i
-	}
-	if k.Op != 0 && i.Op != 0 {
-		obvious(&i, &k) // emit compile-time error for x[3:n:2]
-	}
-
-	if len(panics) > 0 {
-		p := Gbranch(obj.AJMP, nil, 0)
-		for _, q := range panics {
-			Patch(q, Pc)
-		}
-		Ginscall(panicslice, -1)
-		Patch(p, Pc)
-	}
-
-	// Checks are done.
-	// Compute new len as j-i, cap as k-i.
-	// If i and j are same register, len is constant 0.
-	// If i and k are same register, cap is constant 0.
-	// If j and k are same register, len and cap are same.
-
-	// Done with xlen and xcap.
-	// Now safe to modify j and k even if they alias xlen, xcap.
-	if xlen.Op == OREGISTER {
-		Regfree(&xlen)
-	}
-	if xcap.Op == OREGISTER {
-		Regfree(&xcap)
-	}
-
-	// are j and k the same value?
-	sameJK := same(&j, &k)
-
-	if i.Op != 0 {
-		// j -= i
-		if same(&i, &j) {
-			if Debug_slice > 0 {
-				Warn("slice: result len == 0")
-			}
-			if j.Op == OREGISTER {
-				Regfree(&j)
-			}
-			Nodconst(&j, indexRegType, 0)
-		} else {
-			switch j.Op {
-			case OLITERAL:
-				if Isconst(&i, CTINT) {
-					Nodconst(&j, indexRegType, j.Int64()-i.Int64())
-					if Debug_slice > 0 {
-						Warn("slice: result len == %d", j.Int64())
-					}
-					break
-				}
-				fallthrough
-			case ONAME:
-				if !istemp(&j) {
-					var r Node
-					regalloc(&r, indexRegType, nil)
-					Thearch.Gmove(&j, &r)
-					j = r
-				}
-				fallthrough
-			case OREGISTER:
-				if i.Op == OLITERAL {
-					v := i.Int64()
-					if v != 0 {
-						ginscon(Thearch.Optoas(OSUB, indexRegType), v, &j)
-					}
-				} else {
-					gins(Thearch.Optoas(OSUB, indexRegType), &i, &j)
-				}
-			}
-		}
-
-		// k -= i if k different from j and cap is needed.j
-		// (The modifications to j above cannot affect i: if j and i were aliased,
-		// we replace j with a constant 0 instead of doing a subtraction,
-		// leaving i unmodified.)
-		if k.Op == 0 {
-			if Debug_slice > 0 && n.Op != OSLICESTR {
-				Warn("slice: result cap not computed")
-			}
-			// no need
-		} else if same(&i, &k) {
-			if k.Op == OREGISTER {
-				Regfree(&k)
-			}
-			Nodconst(&k, indexRegType, 0)
-			if Debug_slice > 0 {
-				Warn("slice: result cap == 0")
-			}
-		} else if sameJK {
-			if Debug_slice > 0 {
-				Warn("slice: result cap == result len")
-			}
-			// k and j were the same value; make k-i the same as j-i.
-			if k.Op == OREGISTER {
-				Regfree(&k)
-			}
-			k = j
-			if k.Op == OREGISTER {
-				Regrealloc(&k)
-			}
-		} else {
-			switch k.Op {
-			case OLITERAL:
-				if Isconst(&i, CTINT) {
-					Nodconst(&k, indexRegType, k.Int64()-i.Int64())
-					if Debug_slice > 0 {
-						Warn("slice: result cap == %d", k.Int64())
-					}
-					break
-				}
-				fallthrough
-			case ONAME:
-				if !istemp(&k) {
-					var r Node
-					regalloc(&r, indexRegType, nil)
-					Thearch.Gmove(&k, &r)
-					k = r
-				}
-				fallthrough
-			case OREGISTER:
-				if same(&i, &k) {
-					Regfree(&k)
-					Nodconst(&k, indexRegType, 0)
-					if Debug_slice > 0 {
-						Warn("slice: result cap == 0")
-					}
-				} else if i.Op == OLITERAL {
-					v := i.Int64()
-					if v != 0 {
-						ginscon(Thearch.Optoas(OSUB, indexRegType), v, &k)
-					}
-				} else {
-					gins(Thearch.Optoas(OSUB, indexRegType), &i, &k)
-				}
-			}
-		}
-	}
-
-	adjustBase := true
-	if i.Op == 0 || iszero(&i) {
-		if Debug_slice > 0 {
-			Warn("slice: skip base adjustment for 1st index 0")
-		}
-		adjustBase = false
-	} else if k.Op != 0 && iszero(&k) || k.Op == 0 && iszero(&j) {
-		if Debug_slice > 0 {
-			if n.Op == OSLICESTR {
-				Warn("slice: skip base adjustment for string len == 0")
-			} else {
-				Warn("slice: skip base adjustment for cap == 0")
-			}
-		}
-		adjustBase = false
-	}
-
-	if !adjustBase && !needFullUpdate {
-		if Debug_slice > 0 {
-			if k.Op != 0 {
-				Warn("slice: len/cap-only update")
-			} else {
-				Warn("slice: len-only update")
-			}
-		}
-		if i.Op == OREGISTER {
-			Regfree(&i)
-		}
-		// Write len (and cap if needed) back to x.
-		x.Xoffset += int64(Widthptr)
-		x.Type = Types[TUINT]
-		Thearch.Gmove(&j, &x)
-		x.Xoffset -= int64(Widthptr)
-		if k.Op != 0 {
-			x.Xoffset += 2 * int64(Widthptr)
-			x.Type = Types[TUINT]
-			Thearch.Gmove(&k, &x)
-			x.Xoffset -= 2 * int64(Widthptr)
-		}
-		Regfree(&x)
-	} else {
-		// Compute new base. May smash i.
-		if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
-			Cgenr(n.Left, &xbase, nil)
-			Cgen_checknil(&xbase)
-		} else {
-			var ptr *Type
-			if n.Op == OSLICESTR {
-				ptr = ptrToUint8
-			} else {
-				ptr = Ptrto(n.Type.Elem())
-			}
-			regalloc(&xbase, ptr, nil)
-			x.Type = xbase.Type
-			Thearch.Gmove(&x, &xbase)
-			Regfree(&x)
-		}
-		if i.Op != 0 && adjustBase {
-			// Branch around the base adjustment if the resulting cap will be 0.
-			var p *obj.Prog
-			size := &k
-			if k.Op == 0 {
-				size = &j
-			}
-			if Isconst(size, CTINT) {
-				// zero was checked above, must be non-zero.
-			} else {
-				var tmp Node
-				Nodconst(&tmp, indexRegType, 0)
-				p = Thearch.Ginscmp(OEQ, indexRegType, size, &tmp, -1)
-			}
-			var w int64
-			if n.Op == OSLICESTR {
-				w = 1 // res is string, elem size is 1 (byte)
-			} else {
-				w = res.Type.Elem().Width // res is []T, elem size is T.width
-			}
-			if Isconst(&i, CTINT) {
-				ginscon(Thearch.Optoas(OADD, xbase.Type), i.Int64()*w, &xbase)
-			} else if Thearch.AddIndex != nil && Thearch.AddIndex(&i, w, &xbase) {
-				// done by back end
-			} else if w == 1 {
-				gins(Thearch.Optoas(OADD, xbase.Type), &i, &xbase)
-			} else {
-				if i.Op == ONAME && !istemp(&i) {
-					var tmp Node
-					Tempname(&tmp, i.Type)
-					Thearch.Gmove(&i, &tmp)
-					i = tmp
-				}
-				ginscon(Thearch.Optoas(OMUL, i.Type), w, &i)
-				gins(Thearch.Optoas(OADD, xbase.Type), &i, &xbase)
-			}
-			if p != nil {
-				Patch(p, Pc)
-			}
-		}
-		if i.Op == OREGISTER {
-			Regfree(&i)
-		}
-
-		// Write len, cap, base to result.
-		if res.Op == ONAME {
-			Gvardef(res)
-		}
-		Igen(res, &x, nil)
-		x.Xoffset += int64(Widthptr)
-		x.Type = Types[TUINT]
-		Thearch.Gmove(&j, &x)
-		x.Xoffset -= int64(Widthptr)
-		if k.Op != 0 {
-			x.Xoffset += 2 * int64(Widthptr)
-			Thearch.Gmove(&k, &x)
-			x.Xoffset -= 2 * int64(Widthptr)
-		}
-		x.Type = xbase.Type
-		cgen_wb(&xbase, &x, wb)
-		Regfree(&xbase)
-		Regfree(&x)
-	}
-
-	if j.Op == OREGISTER {
-		Regfree(&j)
-	}
-	if k.Op == OREGISTER {
-		Regfree(&k)
-	}
-}
diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go
index 66c710f..5183510 100644
--- a/src/cmd/compile/internal/gc/closure.go
+++ b/src/cmd/compile/internal/gc/closure.go
@@ -10,12 +10,9 @@ import (
 
 // function literals aka closures
 func closurehdr(ntype *Node) {
-	var name *Node
-	var a *Node
-
-	n := Nod(OCLOSURE, nil, nil)
+	n := nod(OCLOSURE, nil, nil)
 	n.Func.Ntype = ntype
-	n.Func.Depth = Funcdepth
+	n.Func.Depth = funcdepth
 	n.Func.Outerfunc = Curfn
 
 	funchdr(n)
@@ -31,11 +28,11 @@ func closurehdr(ntype *Node) {
 	ntype.List.Set(nil)
 	ntype.Rlist.Set(nil)
 	for _, n1 := range n.List.Slice() {
-		name = n1.Left
+		name := n1.Left
 		if name != nil {
 			name = newname(name.Sym)
 		}
-		a = Nod(ODCLFIELD, name, n1.Right)
+		a := nod(ODCLFIELD, name, n1.Right)
 		a.Isddd = n1.Isddd
 		if name != nil {
 			name.Isddd = a.Isddd
@@ -43,17 +40,17 @@ func closurehdr(ntype *Node) {
 		ntype.List.Append(a)
 	}
 	for _, n2 := range n.Rlist.Slice() {
-		name = n2.Left
+		name := n2.Left
 		if name != nil {
 			name = newname(name.Sym)
 		}
-		ntype.Rlist.Append(Nod(ODCLFIELD, name, n2.Right))
+		ntype.Rlist.Append(nod(ODCLFIELD, name, n2.Right))
 	}
 }
 
 func closurebody(body []*Node) *Node {
 	if len(body) == 0 {
-		body = []*Node{Nod(OEMPTY, nil, nil)}
+		body = []*Node{nod(OEMPTY, nil, nil)}
 	}
 
 	func_ := Curfn
@@ -110,7 +107,7 @@ func typecheckclosure(func_ *Node, top int) {
 		if !n.Name.Captured {
 			n.Name.Captured = true
 			if n.Name.Decldepth == 0 {
-				Fatalf("typecheckclosure: var %v does not have decldepth assigned", Nconv(n, FmtShort))
+				Fatalf("typecheckclosure: var %S does not have decldepth assigned", n)
 			}
 
 			// Ignore assignments to the variable in straightline code
@@ -164,14 +161,15 @@ func closurename(n *Node) *Sym {
 	gen := 0
 	outer := ""
 	prefix := ""
-	if n.Func.Outerfunc == nil {
+	switch {
+	case n.Func.Outerfunc == nil:
 		// Global closure.
 		outer = "glob."
 
 		prefix = "func"
 		closurename_closgen++
 		gen = closurename_closgen
-	} else if n.Func.Outerfunc.Op == ODCLFUNC {
+	case n.Func.Outerfunc.Op == ODCLFUNC:
 		// The outermost closure inside of a named function.
 		outer = n.Func.Outerfunc.Func.Nname.Sym.Name
 
@@ -187,30 +185,30 @@ func closurename(n *Node) *Sym {
 			closurename_closgen++
 			gen = closurename_closgen
 		}
-	} else if n.Func.Outerfunc.Op == OCLOSURE {
+	case n.Func.Outerfunc.Op == OCLOSURE:
 		// Nested closure, recurse.
 		outer = closurename(n.Func.Outerfunc).Name
 
 		prefix = ""
 		n.Func.Outerfunc.Func.Closgen++
 		gen = n.Func.Outerfunc.Func.Closgen
-	} else {
-		Fatalf("closurename called for %v", Nconv(n, FmtShort))
+	default:
+		Fatalf("closurename called for %S", n)
 	}
-	n.Sym = Lookupf("%s.%s%d", outer, prefix, gen)
+	n.Sym = lookupf("%s.%s%d", outer, prefix, gen)
 	return n.Sym
 }
 
 func makeclosure(func_ *Node) *Node {
 	// wrap body in external function
 	// that begins by reading closure parameters.
-	xtype := Nod(OTFUNC, nil, nil)
+	xtype := nod(OTFUNC, nil, nil)
 
 	xtype.List.Set(func_.List.Slice())
 	xtype.Rlist.Set(func_.Rlist.Slice())
 
 	// create the function
-	xfunc := Nod(ODCLFUNC, nil, nil)
+	xfunc := nod(ODCLFUNC, nil, nil)
 
 	xfunc.Func.Nname = newfuncname(closurename(func_))
 	xfunc.Func.Nname.Sym.Flags |= SymExported // disable export
@@ -220,7 +218,9 @@ func makeclosure(func_ *Node) *Node {
 	xfunc.Func.Nname.Name.Funcdepth = func_.Func.Depth
 	xfunc.Func.Depth = func_.Func.Depth
 	xfunc.Func.Endlineno = func_.Func.Endlineno
-	makefuncsym(xfunc.Func.Nname.Sym)
+	if Ctxt.Flag_dynlink {
+		makefuncsym(xfunc.Func.Nname.Sym)
+	}
 
 	xfunc.Nbody.Set(func_.Nbody.Slice())
 	xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...)
@@ -276,7 +276,7 @@ func capturevars(xfunc *Node) {
 			v.Name.Byval = true
 		} else {
 			outermost.Addrtaken = true
-			outer = Nod(OADDR, outer, nil)
+			outer = nod(OADDR, outer, nil)
 		}
 
 		if Debug['m'] > 1 {
@@ -344,8 +344,8 @@ func transformclosure(xfunc *Node) {
 				// we introduce function param &v *T
 				// and v remains PAUTOHEAP with &v heapaddr
 				// (accesses will implicitly deref &v).
-				addr := newname(Lookupf("&%s", v.Sym.Name))
-				addr.Type = Ptrto(v.Type)
+				addr := newname(lookupf("&%s", v.Sym.Name))
+				addr.Type = ptrto(v.Type)
 				addr.Class = PPARAM
 				v.Name.Heapaddr = addr
 				fld.Nname = addr
@@ -374,19 +374,17 @@ func transformclosure(xfunc *Node) {
 		// The closure is not called, so it is going to stay as closure.
 		var body []*Node
 		offset := int64(Widthptr)
-		var addr *Node
-		var cv *Node
 		for _, v := range func_.Func.Cvars.Slice() {
 			if v.Op == OXXX {
 				continue
 			}
 
 			// cv refers to the field inside of closure OSTRUCTLIT.
-			cv = Nod(OCLOSUREVAR, nil, nil)
+			cv := nod(OCLOSUREVAR, nil, nil)
 
 			cv.Type = v.Type
 			if !v.Name.Byval {
-				cv.Type = Ptrto(v.Type)
+				cv.Type = ptrto(v.Type)
 			}
 			offset = Rnd(offset, int64(cv.Type.Align))
 			cv.Xoffset = offset
@@ -397,21 +395,21 @@ func transformclosure(xfunc *Node) {
 				v.Class = PAUTO
 				v.Ullman = 1
 				xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
-				body = append(body, Nod(OAS, v, cv))
+				body = append(body, nod(OAS, v, cv))
 			} else {
 				// Declare variable holding addresses taken from closure
 				// and initialize in entry prologue.
-				addr = newname(Lookupf("&%s", v.Sym.Name))
-				addr.Name.Param.Ntype = Nod(OIND, typenod(v.Type), nil)
+				addr := newname(lookupf("&%s", v.Sym.Name))
+				addr.Name.Param.Ntype = nod(OIND, typenod(v.Type), nil)
 				addr.Class = PAUTO
 				addr.Used = true
 				addr.Name.Curfn = xfunc
 				xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
 				v.Name.Heapaddr = addr
 				if v.Name.Byval {
-					cv = Nod(OADDR, cv, nil)
+					cv = nod(OADDR, cv, nil)
 				}
-				body = append(body, Nod(OAS, addr, cv))
+				body = append(body, nod(OAS, addr, cv))
 			}
 		}
 
@@ -478,28 +476,27 @@ func walkclosure(func_ *Node, init *Nodes) *Node {
 	// the struct is unnamed so that closures in multiple packages with the
 	// same struct type can share the descriptor.
 
-	typ := Nod(OTSTRUCT, nil, nil)
+	typ := nod(OTSTRUCT, nil, nil)
 
-	typ.List.Set1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR])))
-	var typ1 *Node
+	typ.List.Set1(nod(ODCLFIELD, newname(lookup(".F")), typenod(Types[TUINTPTR])))
 	for _, v := range func_.Func.Cvars.Slice() {
 		if v.Op == OXXX {
 			continue
 		}
-		typ1 = typenod(v.Type)
+		typ1 := typenod(v.Type)
 		if !v.Name.Byval {
-			typ1 = Nod(OIND, typ1, nil)
+			typ1 = nod(OIND, typ1, nil)
 		}
-		typ.List.Append(Nod(ODCLFIELD, newname(v.Sym), typ1))
+		typ.List.Append(nod(ODCLFIELD, newname(v.Sym), typ1))
 	}
 
-	clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil))
+	clos := nod(OCOMPLIT, nil, nod(OIND, typ, nil))
 	clos.Esc = func_.Esc
 	clos.Right.Implicit = true
-	clos.List.Set(append([]*Node{Nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)}, func_.Func.Enter.Slice()...))
+	clos.List.Set(append([]*Node{nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)}, func_.Func.Enter.Slice()...))
 
 	// Force type conversion from *struct to the func type.
-	clos = Nod(OCONVNOP, clos, nil)
+	clos = nod(OCONVNOP, clos, nil)
 
 	clos.Type = func_.Type
 
@@ -545,9 +542,9 @@ func makepartialcall(fn *Node, t0 *Type, meth *Sym) *Node {
 
 	rcvrtype := fn.Left.Type
 	if exportname(meth.Name) {
-		p = fmt.Sprintf("(%v).%s-fm", Tconv(rcvrtype, FmtLeft|FmtShort), meth.Name)
+		p = fmt.Sprintf("(%-S).%s-fm", rcvrtype, meth.Name)
 	} else {
-		p = fmt.Sprintf("(%v).(%v)-fm", Tconv(rcvrtype, FmtLeft|FmtShort), sconv(meth, FmtLeft))
+		p = fmt.Sprintf("(%-S).(%-v)-fm", rcvrtype, meth)
 	}
 	basetype := rcvrtype
 	if rcvrtype.IsPtr() {
@@ -578,22 +575,18 @@ func makepartialcall(fn *Node, t0 *Type, meth *Sym) *Node {
 	savecurfn := Curfn
 	Curfn = nil
 
-	xtype := Nod(OTFUNC, nil, nil)
-	i := 0
+	xtype := nod(OTFUNC, nil, nil)
 	var l []*Node
 	var callargs []*Node
 	ddd := false
-	xfunc := Nod(ODCLFUNC, nil, nil)
+	xfunc := nod(ODCLFUNC, nil, nil)
 	Curfn = xfunc
-	var fld *Node
-	var n *Node
-	for _, t := range t0.Params().Fields().Slice() {
-		n = newname(LookupN("a", i))
-		i++
+	for i, t := range t0.Params().Fields().Slice() {
+		n := newname(lookupN("a", i))
 		n.Class = PPARAM
 		xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
 		callargs = append(callargs, n)
-		fld = Nod(ODCLFIELD, n, typenod(t.Type))
+		fld := nod(ODCLFIELD, n, typenod(t.Type))
 		if t.Isddd {
 			fld.Isddd = true
 			ddd = true
@@ -603,16 +596,14 @@ func makepartialcall(fn *Node, t0 *Type, meth *Sym) *Node {
 	}
 
 	xtype.List.Set(l)
-	i = 0
 	l = nil
 	var retargs []*Node
-	for _, t := range t0.Results().Fields().Slice() {
-		n = newname(LookupN("r", i))
-		i++
+	for i, t := range t0.Results().Fields().Slice() {
+		n := newname(lookupN("r", i))
 		n.Class = PPARAMOUT
 		xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
 		retargs = append(retargs, n)
-		l = append(l, Nod(ODCLFIELD, n, typenod(t.Type)))
+		l = append(l, nod(ODCLFIELD, n, typenod(t.Type)))
 	}
 
 	xtype.Rlist.Set(l)
@@ -627,14 +618,14 @@ func makepartialcall(fn *Node, t0 *Type, meth *Sym) *Node {
 	// Declare and initialize variable holding receiver.
 
 	xfunc.Func.Needctxt = true
-	cv := Nod(OCLOSUREVAR, nil, nil)
+	cv := nod(OCLOSUREVAR, nil, nil)
 	cv.Xoffset = int64(Widthptr)
 	cv.Type = rcvrtype
 	if int(cv.Type.Align) > Widthptr {
 		cv.Xoffset = int64(cv.Type.Align)
 	}
-	ptr := Nod(ONAME, nil, nil)
-	ptr.Sym = Lookup("rcvr")
+	ptr := nod(ONAME, nil, nil)
+	ptr.Sym = lookup("rcvr")
 	ptr.Class = PAUTO
 	ptr.Addable = true
 	ptr.Ullman = 1
@@ -645,23 +636,23 @@ func makepartialcall(fn *Node, t0 *Type, meth *Sym) *Node {
 	var body []*Node
 	if rcvrtype.IsPtr() || rcvrtype.IsInterface() {
 		ptr.Name.Param.Ntype = typenod(rcvrtype)
-		body = append(body, Nod(OAS, ptr, cv))
+		body = append(body, nod(OAS, ptr, cv))
 	} else {
-		ptr.Name.Param.Ntype = typenod(Ptrto(rcvrtype))
-		body = append(body, Nod(OAS, ptr, Nod(OADDR, cv, nil)))
+		ptr.Name.Param.Ntype = typenod(ptrto(rcvrtype))
+		body = append(body, nod(OAS, ptr, nod(OADDR, cv, nil)))
 	}
 
-	call := Nod(OCALL, NodSym(OXDOT, ptr, meth), nil)
+	call := nod(OCALL, nodSym(OXDOT, ptr, meth), nil)
 	call.List.Set(callargs)
 	call.Isddd = ddd
 	if t0.Results().NumFields() == 0 {
 		body = append(body, call)
 	} else {
-		n := Nod(OAS2, nil, nil)
+		n := nod(OAS2, nil, nil)
 		n.List.Set(retargs)
 		n.Rlist.Set1(call)
 		body = append(body, n)
-		n = Nod(ORETURN, nil, nil)
+		n = nod(ORETURN, nil, nil)
 		body = append(body, n)
 	}
 
@@ -691,18 +682,18 @@ func walkpartialcall(n *Node, init *Nodes) *Node {
 		checknil(n.Left, init)
 	}
 
-	typ := Nod(OTSTRUCT, nil, nil)
-	typ.List.Set1(Nod(ODCLFIELD, newname(Lookup("F")), typenod(Types[TUINTPTR])))
-	typ.List.Append(Nod(ODCLFIELD, newname(Lookup("R")), typenod(n.Left.Type)))
+	typ := nod(OTSTRUCT, nil, nil)
+	typ.List.Set1(nod(ODCLFIELD, newname(lookup("F")), typenod(Types[TUINTPTR])))
+	typ.List.Append(nod(ODCLFIELD, newname(lookup("R")), typenod(n.Left.Type)))
 
-	clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil))
+	clos := nod(OCOMPLIT, nil, nod(OIND, typ, nil))
 	clos.Esc = n.Esc
 	clos.Right.Implicit = true
-	clos.List.Set1(Nod(OCFUNC, n.Func.Nname, nil))
+	clos.List.Set1(nod(OCFUNC, n.Func.Nname, nil))
 	clos.List.Append(n.Left)
 
 	// Force type conversion from *struct to the func type.
-	clos = Nod(OCONVNOP, clos, nil)
+	clos = nod(OCONVNOP, clos, nil)
 
 	clos.Type = n.Type
 
diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go
index 882daec..2b25558 100644
--- a/src/cmd/compile/internal/gc/const.go
+++ b/src/cmd/compile/internal/gc/const.go
@@ -4,11 +4,7 @@
 
 package gc
 
-import (
-	"cmd/compile/internal/big"
-	"cmd/internal/obj"
-	"strings"
-)
+import "strings"
 
 // Ctype describes the constant kind of an "ideal" (untyped) constant.
 type Ctype int8
@@ -61,21 +57,57 @@ func (v Val) Ctype() Ctype {
 	}
 }
 
-type NilVal struct{}
+func eqval(a, b Val) bool {
+	if a.Ctype() != b.Ctype() {
+		return false
+	}
+	switch x := a.U.(type) {
+	default:
+		Fatalf("unexpected Ctype for %T", a.U)
+		panic("not reached")
+	case *NilVal:
+		return true
+	case bool:
+		y := b.U.(bool)
+		return x == y
+	case *Mpint:
+		y := b.U.(*Mpint)
+		return x.Cmp(y) == 0
+	case *Mpflt:
+		y := b.U.(*Mpflt)
+		return x.Cmp(y) == 0
+	case *Mpcplx:
+		y := b.U.(*Mpcplx)
+		return x.Real.Cmp(&y.Real) == 0 && x.Imag.Cmp(&y.Imag) == 0
+	case string:
+		y := b.U.(string)
+		return x == y
+	}
+}
 
-// IntLiteral returns the Node's literal value as an integer.
-func (n *Node) IntLiteral() (x int64, ok bool) {
-	switch {
-	case n == nil:
-		return
-	case Isconst(n, CTINT):
-		return n.Int64(), true
-	case Isconst(n, CTBOOL):
-		return int64(obj.Bool2int(n.Bool())), true
+// Interface returns the constant value stored in v as an interface{}.
+// It returns int64s for ints and runes, float64s for floats,
+// complex128s for complex values, and nil for constant nils.
+func (v Val) Interface() interface{} {
+	switch x := v.U.(type) {
+	default:
+		Fatalf("unexpected Interface for %T", v.U)
+		panic("not reached")
+	case *NilVal:
+		return nil
+	case bool, string:
+		return x
+	case *Mpint:
+		return x.Int64()
+	case *Mpflt:
+		return x.Float64()
+	case *Mpcplx:
+		return complex(x.Real.Float64(), x.Imag.Float64())
 	}
-	return
 }
 
+type NilVal struct{}
+
 // Int64 returns n as an int64.
 // n must be an integer or rune constant.
 func (n *Node) Int64() int64 {
@@ -85,33 +117,6 @@ func (n *Node) Int64() int64 {
 	return n.Val().U.(*Mpint).Int64()
 }
 
-// SetInt sets n's value to i.
-// n must be an integer constant.
-func (n *Node) SetInt(i int64) {
-	if !Isconst(n, CTINT) {
-		Fatalf("SetInt(%v)", n)
-	}
-	n.Val().U.(*Mpint).SetInt64(i)
-}
-
-// SetBigInt sets n's value to x.
-// n must be an integer constant.
-func (n *Node) SetBigInt(x *big.Int) {
-	if !Isconst(n, CTINT) {
-		Fatalf("SetBigInt(%v)", n)
-	}
-	n.Val().U.(*Mpint).Val.Set(x)
-}
-
-// Bool returns n as an bool.
-// n must be an boolean constant.
-func (n *Node) Bool() bool {
-	if !Isconst(n, CTBOOL) {
-		Fatalf("Int(%v)", n)
-	}
-	return n.Val().U.(bool)
-}
-
 // truncate float literal fv to 32-bit or 64-bit precision
 // according to type; return truncated value.
 func truncfltlit(oldv *Mpflt, t *Type) *Mpflt {
@@ -141,13 +146,6 @@ func truncfltlit(oldv *Mpflt, t *Type) *Mpflt {
 	return fv
 }
 
-// NegOne returns a Node of type t with value -1.
-func NegOne(t *Type) *Node {
-	n := Nodintconst(-1)
-	n = convlit(n, t)
-	return n
-}
-
 // canReuseNode indicates whether it is known to be safe
 // to reuse a Node.
 type canReuseNode bool
@@ -216,7 +214,7 @@ func convlit1(n *Node, t *Type, explicit bool, reuse canReuseNode) *Node {
 			n.SetVal(toint(n.Val()))
 		}
 		if t != nil && !t.IsInteger() {
-			Yyerror("invalid operation: %v (shift of type %v)", n, t)
+			yyerror("invalid operation: %v (shift of type %v)", n, t)
 			t = nil
 		}
 
@@ -247,7 +245,7 @@ func convlit1(n *Node, t *Type, explicit bool, reuse canReuseNode) *Node {
 	}
 
 	// avoided repeated calculations, errors
-	if Eqtype(n.Type, t) {
+	if eqtype(n.Type, t) {
 		return n
 	}
 
@@ -293,7 +291,7 @@ func convlit1(n *Node, t *Type, explicit bool, reuse canReuseNode) *Node {
 			TUNSAFEPTR:
 			break
 
-			// A nil literal may be converted to uintptr
+		// A nil literal may be converted to uintptr
 		// if it is an unsafe.Pointer
 		case TUINTPTR:
 			if n.Type.Etype == TUNSAFEPTR {
@@ -314,7 +312,7 @@ func convlit1(n *Node, t *Type, explicit bool, reuse canReuseNode) *Node {
 			goto bad
 		}
 		ct := n.Val().Ctype()
-		if Isint[et] {
+		if isInt[et] {
 			switch ct {
 			default:
 				goto bad
@@ -326,7 +324,7 @@ func convlit1(n *Node, t *Type, explicit bool, reuse canReuseNode) *Node {
 			case CTINT:
 				overflow(n.Val(), t)
 			}
-		} else if Isfloat[et] {
+		} else if isFloat[et] {
 			switch ct {
 			default:
 				goto bad
@@ -338,7 +336,7 @@ func convlit1(n *Node, t *Type, explicit bool, reuse canReuseNode) *Node {
 			case CTFLT:
 				n.SetVal(Val{truncfltlit(n.Val().U.(*Mpflt), t)})
 			}
-		} else if Iscomplex[et] {
+		} else if isComplex[et] {
 			switch ct {
 			default:
 				goto bad
@@ -361,11 +359,11 @@ func convlit1(n *Node, t *Type, explicit bool, reuse canReuseNode) *Node {
 	return n
 
 bad:
-	if n.Diag == 0 {
+	if !n.Diag {
 		if !t.Broke {
-			Yyerror("cannot convert %v to type %v", n, t)
+			yyerror("cannot convert %v to type %v", n, t)
 		}
-		n.Diag = 1
+		n.Diag = true
 	}
 
 	if n.Type.IsUntyped() {
@@ -426,7 +424,7 @@ func toflt(v Val) Val {
 		f := newMpflt()
 		f.Set(&u.Real)
 		if u.Imag.CmpFloat64(0) != 0 {
-			Yyerror("constant %v%vi truncated to real", fconv(&u.Real, FmtSharp), fconv(&u.Imag, FmtSharp|FmtSign))
+			yyerror("constant %v%vi truncated to real", fconv(&u.Real, FmtSharp), fconv(&u.Imag, FmtSharp|FmtSign))
 		}
 		v.U = f
 	}
@@ -451,17 +449,17 @@ func toint(v Val) Val {
 			if u.Val.IsInt() {
 				msg = "constant %v overflows integer"
 			}
-			Yyerror(msg, fconv(u, FmtSharp))
+			yyerror(msg, fconv(u, FmtSharp))
 		}
 		v.U = i
 
 	case *Mpcplx:
 		i := new(Mpint)
 		if i.SetFloat(&u.Real) < 0 {
-			Yyerror("constant %v%vi truncated to integer", fconv(&u.Real, FmtSharp), fconv(&u.Imag, FmtSharp|FmtSign))
+			yyerror("constant %v%vi truncated to integer", fconv(&u.Real, FmtSharp), fconv(&u.Imag, FmtSharp|FmtSign))
 		}
 		if u.Imag.CmpFloat64(0) != 0 {
-			Yyerror("constant %v%vi truncated to real", fconv(&u.Real, FmtSharp), fconv(&u.Imag, FmtSharp|FmtSign))
+			yyerror("constant %v%vi truncated to real", fconv(&u.Real, FmtSharp), fconv(&u.Imag, FmtSharp|FmtSign))
 		}
 		v.U = i
 	}
@@ -475,7 +473,7 @@ func doesoverflow(v Val, t *Type) bool {
 		if !t.IsInteger() {
 			Fatalf("overflow: %v integer constant", t)
 		}
-		return u.Cmp(Minintval[t.Etype]) < 0 || u.Cmp(Maxintval[t.Etype]) > 0
+		return u.Cmp(minintval[t.Etype]) < 0 || u.Cmp(maxintval[t.Etype]) > 0
 
 	case *Mpflt:
 		if !t.IsFloat() {
@@ -507,7 +505,7 @@ func overflow(v Val, t *Type) {
 	}
 
 	if doesoverflow(v, t) {
-		Yyerror("constant %s overflows %v", vconv(v, 0), t)
+		yyerror("constant %v overflows %v", v, t)
 	}
 }
 
@@ -515,7 +513,7 @@ func tostr(v Val) Val {
 	switch u := v.U.(type) {
 	case *Mpint:
 		var i int64 = 0xFFFD
-		if u.Cmp(Minintval[TUINT32]) >= 0 && u.Cmp(Maxintval[TUINT32]) <= 0 {
+		if u.Cmp(minintval[TUINT32]) >= 0 && u.Cmp(maxintval[TUINT32]) <= 0 {
 			i = u.Int64()
 		}
 		v.U = string(i)
@@ -546,7 +544,7 @@ func Isconst(n *Node, ct Ctype) bool {
 func saveorig(n *Node) *Node {
 	if n == n.Orig {
 		// duplicate node for n->orig.
-		n1 := Nod(OLITERAL, nil, nil)
+		n1 := nod(OLITERAL, nil, nil)
 
 		n.Orig = n1
 		*n1 = *n
@@ -636,7 +634,7 @@ func evconst(n *Node) {
 		return
 	}
 	wl := nl.Type.Etype
-	if Isint[wl] || Isfloat[wl] || Iscomplex[wl] {
+	if isInt[wl] || isFloat[wl] || isComplex[wl] {
 		wl = TIDEAL
 	}
 
@@ -694,9 +692,9 @@ func evconst(n *Node) {
 
 		switch uint32(n.Op)<<16 | uint32(v.Ctype()) {
 		default:
-			if n.Diag == 0 {
-				Yyerror("illegal constant expression %v %v", n.Op, nl.Type)
-				n.Diag = 1
+			if !n.Diag {
+				yyerror("illegal constant expression %v %v", n.Op, nl.Type)
+				n.Diag = true
 			}
 			return
 
@@ -746,7 +744,7 @@ func evconst(n *Node) {
 				TUINT64,
 				TUINT,
 				TUINTPTR:
-				b.Set(Maxintval[et])
+				b.Set(maxintval[et])
 			}
 
 			v.U.(*Mpint).Xor(&b)
@@ -779,12 +777,15 @@ func evconst(n *Node) {
 		return
 	}
 	wr = nr.Type.Etype
-	if Isint[wr] || Isfloat[wr] || Iscomplex[wr] {
+	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 {
+			goto setfalse
+		}
 		goto illegal
 	}
 
@@ -806,7 +807,7 @@ func evconst(n *Node) {
 			goto illegal
 		}
 
-		// right must be unsigned.
+	// right must be unsigned.
 	// left can be ideal.
 	case OLSH, ORSH:
 		nr = defaultlit(nr, Types[TUINT])
@@ -890,7 +891,7 @@ func evconst(n *Node) {
 	case ODIV_ | CTINT_,
 		ODIV_ | CTRUNE_:
 		if rv.U.(*Mpint).CmpInt64(0) == 0 {
-			Yyerror("division by zero")
+			yyerror("division by zero")
 			v.U.(*Mpint).SetOverflow()
 			break
 		}
@@ -900,7 +901,7 @@ func evconst(n *Node) {
 	case OMOD_ | CTINT_,
 		OMOD_ | CTRUNE_:
 		if rv.U.(*Mpint).CmpInt64(0) == 0 {
-			Yyerror("division by zero")
+			yyerror("division by zero")
 			v.U.(*Mpint).SetOverflow()
 			break
 		}
@@ -942,19 +943,19 @@ func evconst(n *Node) {
 
 	case ODIV_ | CTFLT_:
 		if rv.U.(*Mpflt).CmpFloat64(0) == 0 {
-			Yyerror("division by zero")
+			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',
+	// The default case above would print 'ideal % ideal',
 	// which is not quite an ideal error.
 	case OMOD_ | CTFLT_:
-		if n.Diag == 0 {
-			Yyerror("illegal constant expression: floating-point %% operation")
-			n.Diag = 1
+		if !n.Diag {
+			yyerror("illegal constant expression: floating-point %% operation")
+			n.Diag = true
 		}
 
 		return
@@ -972,7 +973,7 @@ func evconst(n *Node) {
 
 	case ODIV_ | CTCPLX_:
 		if rv.U.(*Mpcplx).Real.CmpFloat64(0) == 0 && rv.U.(*Mpcplx).Imag.CmpFloat64(0) == 0 {
-			Yyerror("complex division by zero")
+			yyerror("complex division by zero")
 			rv.U.(*Mpcplx).Real.SetFloat64(1.0)
 			rv.U.(*Mpcplx).Imag.SetFloat64(0.0)
 			break
@@ -1160,7 +1161,7 @@ ret:
 	return
 
 settrue:
-	nn = Nodbool(true)
+	nn = nodbool(true)
 	nn.Orig = saveorig(n)
 	if !iscmp[n.Op] {
 		nn.Type = nl.Type
@@ -1169,7 +1170,7 @@ settrue:
 	return
 
 setfalse:
-	nn = Nodbool(false)
+	nn = nodbool(false)
 	nn.Orig = saveorig(n)
 	if !iscmp[n.Op] {
 		nn.Type = nl.Type
@@ -1178,14 +1179,14 @@ setfalse:
 	return
 
 illegal:
-	if n.Diag == 0 {
-		Yyerror("illegal constant expression: %v %v %v", nl.Type, n.Op, nr.Type)
-		n.Diag = 1
+	if !n.Diag {
+		yyerror("illegal constant expression: %v %v %v", nl.Type, n.Op, nr.Type)
+		n.Diag = true
 	}
 }
 
 func nodlit(v Val) *Node {
-	n := Nod(OLITERAL, nil, nil)
+	n := nod(OLITERAL, nil, nil)
 	n.SetVal(v)
 	switch v.Ctype() {
 	default:
@@ -1212,7 +1213,7 @@ func nodcplxlit(r Val, i Val) *Node {
 	i = toflt(i)
 
 	c := new(Mpcplx)
-	n := Nod(OLITERAL, nil, nil)
+	n := nod(OLITERAL, nil, nil)
 	n.Type = Types[TIDEAL]
 	n.SetVal(Val{c})
 
@@ -1319,9 +1320,9 @@ func defaultlitreuse(n *Node, t *Type, reuse canReuseNode) *Node {
 
 		if n.Val().Ctype() == CTNIL {
 			lineno = lno
-			if n.Diag == 0 {
-				Yyerror("use of untyped nil")
-				n.Diag = 1
+			if !n.Diag {
+				yyerror("use of untyped nil")
+				n.Diag = true
 			}
 
 			n.Type = nil
@@ -1334,10 +1335,10 @@ func defaultlitreuse(n *Node, t *Type, reuse canReuseNode) *Node {
 			break
 		}
 
-		Yyerror("defaultlit: unknown literal: %v", n)
+		yyerror("defaultlit: unknown literal: %v", n)
 
 	case CTxxx:
-		Fatalf("defaultlit: idealkind is CTxxx: %v", Nconv(n, FmtSign))
+		Fatalf("defaultlit: idealkind is CTxxx: %+v", n)
 
 	case CTBOOL:
 		t1 := Types[TBOOL]
@@ -1454,9 +1455,9 @@ func strlit(n *Node) string {
 	return n.Val().U.(string)
 }
 
-func Smallintconst(n *Node) bool {
+func smallintconst(n *Node) bool {
 	if n.Op == OLITERAL && Isconst(n, CTINT) && n.Type != nil {
-		switch Simtype[n.Type.Etype] {
+		switch simtype[n.Type.Etype] {
 		case TINT8,
 			TUINT8,
 			TINT16,
@@ -1468,122 +1469,33 @@ func Smallintconst(n *Node) bool {
 			return true
 
 		case TIDEAL, TINT64, TUINT64, TPTR64:
-			if n.Val().U.(*Mpint).Cmp(Minintval[TINT32]) < 0 || n.Val().U.(*Mpint).Cmp(Maxintval[TINT32]) > 0 {
-				break
+			v, ok := n.Val().U.(*Mpint)
+			if ok && v.Cmp(minintval[TINT32]) > 0 && v.Cmp(maxintval[TINT32]) < 0 {
+				return true
 			}
-			return true
 		}
 	}
 
 	return false
 }
 
-func nonnegconst(n *Node) int {
-	if n.Op == OLITERAL && n.Type != nil {
-		switch Simtype[n.Type.Etype] {
-		// check negative and 2^31
-		case TINT8,
-			TUINT8,
-			TINT16,
-			TUINT16,
-			TINT32,
-			TUINT32,
-			TINT64,
-			TUINT64,
-			TIDEAL:
-			if n.Val().U.(*Mpint).Cmp(Minintval[TUINT32]) < 0 || n.Val().U.(*Mpint).Cmp(Maxintval[TINT32]) > 0 {
-				break
-			}
-			return int(n.Int64())
-		}
-	}
-
-	return -1
-}
-
-// convert x to type et and back to int64
-// for sign extension and truncation.
-func iconv(x int64, et EType) int64 {
-	switch et {
-	case TINT8:
-		x = int64(int8(x))
-
-	case TUINT8:
-		x = int64(uint8(x))
-
-	case TINT16:
-		x = int64(int16(x))
-
-	case TUINT16:
-		x = int64(uint64(x))
-
-	case TINT32:
-		x = int64(int32(x))
-
-	case TUINT32:
-		x = int64(uint32(x))
-
-	case TINT64, TUINT64:
-		break
-	}
-
-	return x
-}
-
-// Convconst converts constant node n to type t and
-// places the result in con.
-func (n *Node) Convconst(con *Node, t *Type) {
-	tt := Simsimtype(t)
-
-	// copy the constant for conversion
-	Nodconst(con, Types[TINT8], 0)
-
-	con.Type = t
-	con.SetVal(n.Val())
-
-	if Isint[tt] {
-		con.SetVal(Val{new(Mpint)})
-		var i int64
-		switch n.Val().Ctype() {
-		default:
-			Fatalf("convconst ctype=%d %v", n.Val().Ctype(), Tconv(t, FmtLong))
-
-		case CTINT, CTRUNE:
-			i = n.Int64()
-
-		case CTBOOL:
-			i = int64(obj.Bool2int(n.Val().U.(bool)))
-
-		case CTNIL:
-			i = 0
-		}
-
-		i = iconv(i, tt)
-		con.Val().U.(*Mpint).SetInt64(i)
-		return
-	}
-
-	if Isfloat[tt] {
-		con.SetVal(toflt(con.Val()))
-		if con.Val().Ctype() != CTFLT {
-			Fatalf("convconst ctype=%d %v", con.Val().Ctype(), t)
-		}
-		if tt == TFLOAT32 {
-			con.SetVal(Val{truncfltlit(con.Val().U.(*Mpflt), t)})
-		}
-		return
+// nonnegintconst checks if Node n contains a constant expression
+// representable as a non-negative small integer, and returns its
+// (integer) value if that's the case. Otherwise, it returns -1.
+func nonnegintconst(n *Node) int64 {
+	if n.Op != OLITERAL {
+		return -1
 	}
 
-	if Iscomplex[tt] {
-		con.SetVal(tocplx(con.Val()))
-		if tt == TCOMPLEX64 {
-			con.Val().U.(*Mpcplx).Real = *truncfltlit(&con.Val().U.(*Mpcplx).Real, Types[TFLOAT32])
-			con.Val().U.(*Mpcplx).Imag = *truncfltlit(&con.Val().U.(*Mpcplx).Imag, Types[TFLOAT32])
-		}
-		return
+	// toint will leave n.Val unchanged if it's not castable to an
+	// Mpint, so we still have to guard the conversion.
+	v := toint(n.Val())
+	vi, ok := v.U.(*Mpint)
+	if !ok || vi.Val.Sign() < 0 || vi.Cmp(maxintval[TINT32]) > 0 {
+		return -1
 	}
 
-	Fatalf("convconst %v constant", Tconv(t, FmtLong))
+	return vi.Int64()
 }
 
 // complex multiply v *= rv
@@ -1745,19 +1657,8 @@ func isgoconst(n *Node) bool {
 			return true
 		}
 
-		// Only constant calls are unsafe.Alignof, Offsetof, and Sizeof.
-	case OCALL:
-		l := n.Left
-
-		for l.Op == OPAREN {
-			l = l.Left
-		}
-		if l.Op != ONAME || l.Sym.Pkg != unsafepkg {
-			break
-		}
-		if l.Sym.Name == "Alignof" || l.Sym.Name == "Offsetof" || l.Sym.Name == "Sizeof" {
-			return true
-		}
+	case OALIGNOF, OOFFSETOF, OSIZEOF:
+		return true
 	}
 
 	//dump("nonconst", n);
diff --git a/src/cmd/compile/internal/gc/constFold_test.go b/src/cmd/compile/internal/gc/constFold_test.go
index ef6a3c1..fe57717 100644
--- a/src/cmd/compile/internal/gc/constFold_test.go
+++ b/src/cmd/compile/internal/gc/constFold_test.go
@@ -8,85 +8,85 @@ func TestConstFolduint64add(t *testing.T) {
 	y = 0
 	r = x + y
 	if r != 0 {
-		t.Errorf("0 + 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 1 {
-		t.Errorf("0 + 1 = %d, want 1", r)
+		t.Errorf("0 %s 1 = %d, want 1", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != 4294967296 {
-		t.Errorf("0 + 4294967296 = %d, want 4294967296", r)
+		t.Errorf("0 %s 4294967296 = %d, want 4294967296", "+", r)
 	}
 	y = 18446744073709551615
 	r = x + y
 	if r != 18446744073709551615 {
-		t.Errorf("0 + 18446744073709551615 = %d, want 18446744073709551615", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 18446744073709551615", "+", r)
 	}
 	x = 1
 	y = 0
 	r = x + y
 	if r != 1 {
-		t.Errorf("1 + 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 2 {
-		t.Errorf("1 + 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != 4294967297 {
-		t.Errorf("1 + 4294967296 = %d, want 4294967297", r)
+		t.Errorf("1 %s 4294967296 = %d, want 4294967297", "+", r)
 	}
 	y = 18446744073709551615
 	r = x + y
 	if r != 0 {
-		t.Errorf("1 + 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", "+", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x + y
 	if r != 4294967296 {
-		t.Errorf("4294967296 + 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 4294967297 {
-		t.Errorf("4294967296 + 1 = %d, want 4294967297", r)
+		t.Errorf("4294967296 %s 1 = %d, want 4294967297", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != 8589934592 {
-		t.Errorf("4294967296 + 4294967296 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 8589934592", "+", r)
 	}
 	y = 18446744073709551615
 	r = x + y
 	if r != 4294967295 {
-		t.Errorf("4294967296 + 18446744073709551615 = %d, want 4294967295", r)
+		t.Errorf("4294967296 %s 18446744073709551615 = %d, want 4294967295", "+", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x + y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 + 0 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 18446744073709551615", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 0 {
-		t.Errorf("18446744073709551615 + 1 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 0", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != 4294967295 {
-		t.Errorf("18446744073709551615 + 4294967296 = %d, want 4294967295", r)
+		t.Errorf("18446744073709551615 %s 4294967296 = %d, want 4294967295", "+", r)
 	}
 	y = 18446744073709551615
 	r = x + y
 	if r != 18446744073709551614 {
-		t.Errorf("18446744073709551615 + 18446744073709551615 = %d, want 18446744073709551614", r)
+		t.Errorf("18446744073709551615 %s 18446744073709551615 = %d, want 18446744073709551614", "+", r)
 	}
 }
 func TestConstFolduint64sub(t *testing.T) {
@@ -95,85 +95,85 @@ func TestConstFolduint64sub(t *testing.T) {
 	y = 0
 	r = x - y
 	if r != 0 {
-		t.Errorf("0 - 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 18446744073709551615 {
-		t.Errorf("0 - 1 = %d, want 18446744073709551615", r)
+		t.Errorf("0 %s 1 = %d, want 18446744073709551615", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != 18446744069414584320 {
-		t.Errorf("0 - 4294967296 = %d, want 18446744069414584320", r)
+		t.Errorf("0 %s 4294967296 = %d, want 18446744069414584320", "-", r)
 	}
 	y = 18446744073709551615
 	r = x - y
 	if r != 1 {
-		t.Errorf("0 - 18446744073709551615 = %d, want 1", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 1", "-", r)
 	}
 	x = 1
 	y = 0
 	r = x - y
 	if r != 1 {
-		t.Errorf("1 - 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 0 {
-		t.Errorf("1 - 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != 18446744069414584321 {
-		t.Errorf("1 - 4294967296 = %d, want 18446744069414584321", r)
+		t.Errorf("1 %s 4294967296 = %d, want 18446744069414584321", "-", r)
 	}
 	y = 18446744073709551615
 	r = x - y
 	if r != 2 {
-		t.Errorf("1 - 18446744073709551615 = %d, want 2", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 2", "-", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x - y
 	if r != 4294967296 {
-		t.Errorf("4294967296 - 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 4294967295 {
-		t.Errorf("4294967296 - 1 = %d, want 4294967295", r)
+		t.Errorf("4294967296 %s 1 = %d, want 4294967295", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != 0 {
-		t.Errorf("4294967296 - 4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 0", "-", r)
 	}
 	y = 18446744073709551615
 	r = x - y
 	if r != 4294967297 {
-		t.Errorf("4294967296 - 18446744073709551615 = %d, want 4294967297", r)
+		t.Errorf("4294967296 %s 18446744073709551615 = %d, want 4294967297", "-", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x - y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 - 0 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 18446744073709551615", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 18446744073709551614 {
-		t.Errorf("18446744073709551615 - 1 = %d, want 18446744073709551614", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 18446744073709551614", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != 18446744069414584319 {
-		t.Errorf("18446744073709551615 - 4294967296 = %d, want 18446744069414584319", r)
+		t.Errorf("18446744073709551615 %s 4294967296 = %d, want 18446744069414584319", "-", r)
 	}
 	y = 18446744073709551615
 	r = x - y
 	if r != 0 {
-		t.Errorf("18446744073709551615 - 18446744073709551615 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 18446744073709551615 = %d, want 0", "-", r)
 	}
 }
 func TestConstFolduint64div(t *testing.T) {
@@ -182,65 +182,65 @@ func TestConstFolduint64div(t *testing.T) {
 	y = 1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "/", r)
 	}
 	y = 18446744073709551615
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "/", r)
 	}
 	x = 1
 	y = 1
 	r = x / y
 	if r != 1 {
-		t.Errorf("1 / 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", "/", r)
 	}
 	y = 18446744073709551615
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", "/", r)
 	}
 	x = 4294967296
 	y = 1
 	r = x / y
 	if r != 4294967296 {
-		t.Errorf("4294967296 / 1 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 1 = %d, want 4294967296", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != 1 {
-		t.Errorf("4294967296 / 4294967296 = %d, want 1", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 1", "/", r)
 	}
 	y = 18446744073709551615
 	r = x / y
 	if r != 0 {
-		t.Errorf("4294967296 / 18446744073709551615 = %d, want 0", r)
+		t.Errorf("4294967296 %s 18446744073709551615 = %d, want 0", "/", r)
 	}
 	x = 18446744073709551615
 	y = 1
 	r = x / y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 / 1 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 18446744073709551615", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != 4294967295 {
-		t.Errorf("18446744073709551615 / 4294967296 = %d, want 4294967295", r)
+		t.Errorf("18446744073709551615 %s 4294967296 = %d, want 4294967295", "/", r)
 	}
 	y = 18446744073709551615
 	r = x / y
 	if r != 1 {
-		t.Errorf("18446744073709551615 / 18446744073709551615 = %d, want 1", r)
+		t.Errorf("18446744073709551615 %s 18446744073709551615 = %d, want 1", "/", r)
 	}
 }
 func TestConstFolduint64mul(t *testing.T) {
@@ -249,85 +249,85 @@ func TestConstFolduint64mul(t *testing.T) {
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "*", r)
 	}
 	y = 18446744073709551615
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "*", r)
 	}
 	x = 1
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("1 * 0 = %d, want 0", r)
+		t.Errorf("1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 1 {
-		t.Errorf("1 * 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != 4294967296 {
-		t.Errorf("1 * 4294967296 = %d, want 4294967296", r)
+		t.Errorf("1 %s 4294967296 = %d, want 4294967296", "*", r)
 	}
 	y = 18446744073709551615
 	r = x * y
 	if r != 18446744073709551615 {
-		t.Errorf("1 * 18446744073709551615 = %d, want 18446744073709551615", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 18446744073709551615", "*", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("4294967296 * 0 = %d, want 0", r)
+		t.Errorf("4294967296 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 4294967296 {
-		t.Errorf("4294967296 * 1 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 1 = %d, want 4294967296", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != 0 {
-		t.Errorf("4294967296 * 4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 0", "*", r)
 	}
 	y = 18446744073709551615
 	r = x * y
 	if r != 18446744069414584320 {
-		t.Errorf("4294967296 * 18446744073709551615 = %d, want 18446744069414584320", r)
+		t.Errorf("4294967296 %s 18446744073709551615 = %d, want 18446744069414584320", "*", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("18446744073709551615 * 0 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 * 1 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 18446744073709551615", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != 18446744069414584320 {
-		t.Errorf("18446744073709551615 * 4294967296 = %d, want 18446744069414584320", r)
+		t.Errorf("18446744073709551615 %s 4294967296 = %d, want 18446744069414584320", "*", r)
 	}
 	y = 18446744073709551615
 	r = x * y
 	if r != 1 {
-		t.Errorf("18446744073709551615 * 18446744073709551615 = %d, want 1", r)
+		t.Errorf("18446744073709551615 %s 18446744073709551615 = %d, want 1", "*", r)
 	}
 }
 func TestConstFolduint64mod(t *testing.T) {
@@ -336,65 +336,65 @@ func TestConstFolduint64mod(t *testing.T) {
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "%", r)
 	}
 	y = 18446744073709551615
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "%", r)
 	}
 	x = 1
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 4294967296 = %d, want 1", r)
+		t.Errorf("1 %s 4294967296 = %d, want 1", "%", r)
 	}
 	y = 18446744073709551615
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 18446744073709551615 = %d, want 1", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 1", "%", r)
 	}
 	x = 4294967296
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("4294967296 % 1 = %d, want 0", r)
+		t.Errorf("4294967296 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 0 {
-		t.Errorf("4294967296 % 4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 0", "%", r)
 	}
 	y = 18446744073709551615
 	r = x % y
 	if r != 4294967296 {
-		t.Errorf("4294967296 % 18446744073709551615 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 18446744073709551615 = %d, want 4294967296", "%", r)
 	}
 	x = 18446744073709551615
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("18446744073709551615 % 1 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 4294967295 {
-		t.Errorf("18446744073709551615 % 4294967296 = %d, want 4294967295", r)
+		t.Errorf("18446744073709551615 %s 4294967296 = %d, want 4294967295", "%", r)
 	}
 	y = 18446744073709551615
 	r = x % y
 	if r != 0 {
-		t.Errorf("18446744073709551615 % 18446744073709551615 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 18446744073709551615 = %d, want 0", "%", r)
 	}
 }
 func TestConstFoldint64add(t *testing.T) {
@@ -403,415 +403,415 @@ func TestConstFoldint64add(t *testing.T) {
 	y = -9223372036854775808
 	r = x + y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 + -9223372036854775808 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s -9223372036854775808 = %d, want 0", "+", r)
 	}
 	y = -9223372036854775807
 	r = x + y
 	if r != 1 {
-		t.Errorf("-9223372036854775808 + -9223372036854775807 = %d, want 1", r)
+		t.Errorf("-9223372036854775808 %s -9223372036854775807 = %d, want 1", "+", r)
 	}
 	y = -4294967296
 	r = x + y
 	if r != 9223372032559808512 {
-		t.Errorf("-9223372036854775808 + -4294967296 = %d, want 9223372032559808512", r)
+		t.Errorf("-9223372036854775808 %s -4294967296 = %d, want 9223372032559808512", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 9223372036854775807 {
-		t.Errorf("-9223372036854775808 + -1 = %d, want 9223372036854775807", r)
+		t.Errorf("-9223372036854775808 %s -1 = %d, want 9223372036854775807", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 + 0 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want -9223372036854775808", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775808 + 1 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want -9223372036854775807", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != -9223372032559808512 {
-		t.Errorf("-9223372036854775808 + 4294967296 = %d, want -9223372032559808512", r)
+		t.Errorf("-9223372036854775808 %s 4294967296 = %d, want -9223372032559808512", "+", r)
 	}
 	y = 9223372036854775806
 	r = x + y
 	if r != -2 {
-		t.Errorf("-9223372036854775808 + 9223372036854775806 = %d, want -2", r)
+		t.Errorf("-9223372036854775808 %s 9223372036854775806 = %d, want -2", "+", r)
 	}
 	y = 9223372036854775807
 	r = x + y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 + 9223372036854775807 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s 9223372036854775807 = %d, want -1", "+", r)
 	}
 	x = -9223372036854775807
 	y = -9223372036854775808
 	r = x + y
 	if r != 1 {
-		t.Errorf("-9223372036854775807 + -9223372036854775808 = %d, want 1", r)
+		t.Errorf("-9223372036854775807 %s -9223372036854775808 = %d, want 1", "+", r)
 	}
 	y = -9223372036854775807
 	r = x + y
 	if r != 2 {
-		t.Errorf("-9223372036854775807 + -9223372036854775807 = %d, want 2", r)
+		t.Errorf("-9223372036854775807 %s -9223372036854775807 = %d, want 2", "+", r)
 	}
 	y = -4294967296
 	r = x + y
 	if r != 9223372032559808513 {
-		t.Errorf("-9223372036854775807 + -4294967296 = %d, want 9223372032559808513", r)
+		t.Errorf("-9223372036854775807 %s -4294967296 = %d, want 9223372032559808513", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775807 + -1 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775807 %s -1 = %d, want -9223372036854775808", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 + 0 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want -9223372036854775807", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -9223372036854775806 {
-		t.Errorf("-9223372036854775807 + 1 = %d, want -9223372036854775806", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want -9223372036854775806", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != -9223372032559808511 {
-		t.Errorf("-9223372036854775807 + 4294967296 = %d, want -9223372032559808511", r)
+		t.Errorf("-9223372036854775807 %s 4294967296 = %d, want -9223372032559808511", "+", r)
 	}
 	y = 9223372036854775806
 	r = x + y
 	if r != -1 {
-		t.Errorf("-9223372036854775807 + 9223372036854775806 = %d, want -1", r)
+		t.Errorf("-9223372036854775807 %s 9223372036854775806 = %d, want -1", "+", r)
 	}
 	y = 9223372036854775807
 	r = x + y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 + 9223372036854775807 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s 9223372036854775807 = %d, want 0", "+", r)
 	}
 	x = -4294967296
 	y = -9223372036854775808
 	r = x + y
 	if r != 9223372032559808512 {
-		t.Errorf("-4294967296 + -9223372036854775808 = %d, want 9223372032559808512", r)
+		t.Errorf("-4294967296 %s -9223372036854775808 = %d, want 9223372032559808512", "+", r)
 	}
 	y = -9223372036854775807
 	r = x + y
 	if r != 9223372032559808513 {
-		t.Errorf("-4294967296 + -9223372036854775807 = %d, want 9223372032559808513", r)
+		t.Errorf("-4294967296 %s -9223372036854775807 = %d, want 9223372032559808513", "+", r)
 	}
 	y = -4294967296
 	r = x + y
 	if r != -8589934592 {
-		t.Errorf("-4294967296 + -4294967296 = %d, want -8589934592", r)
+		t.Errorf("-4294967296 %s -4294967296 = %d, want -8589934592", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -4294967297 {
-		t.Errorf("-4294967296 + -1 = %d, want -4294967297", r)
+		t.Errorf("-4294967296 %s -1 = %d, want -4294967297", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 + 0 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 0 = %d, want -4294967296", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -4294967295 {
-		t.Errorf("-4294967296 + 1 = %d, want -4294967295", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -4294967295", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != 0 {
-		t.Errorf("-4294967296 + 4294967296 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 4294967296 = %d, want 0", "+", r)
 	}
 	y = 9223372036854775806
 	r = x + y
 	if r != 9223372032559808510 {
-		t.Errorf("-4294967296 + 9223372036854775806 = %d, want 9223372032559808510", r)
+		t.Errorf("-4294967296 %s 9223372036854775806 = %d, want 9223372032559808510", "+", r)
 	}
 	y = 9223372036854775807
 	r = x + y
 	if r != 9223372032559808511 {
-		t.Errorf("-4294967296 + 9223372036854775807 = %d, want 9223372032559808511", r)
+		t.Errorf("-4294967296 %s 9223372036854775807 = %d, want 9223372032559808511", "+", r)
 	}
 	x = -1
 	y = -9223372036854775808
 	r = x + y
 	if r != 9223372036854775807 {
-		t.Errorf("-1 + -9223372036854775808 = %d, want 9223372036854775807", r)
+		t.Errorf("-1 %s -9223372036854775808 = %d, want 9223372036854775807", "+", r)
 	}
 	y = -9223372036854775807
 	r = x + y
 	if r != -9223372036854775808 {
-		t.Errorf("-1 + -9223372036854775807 = %d, want -9223372036854775808", r)
+		t.Errorf("-1 %s -9223372036854775807 = %d, want -9223372036854775808", "+", r)
 	}
 	y = -4294967296
 	r = x + y
 	if r != -4294967297 {
-		t.Errorf("-1 + -4294967296 = %d, want -4294967297", r)
+		t.Errorf("-1 %s -4294967296 = %d, want -4294967297", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -2 {
-		t.Errorf("-1 + -1 = %d, want -2", r)
+		t.Errorf("-1 %s -1 = %d, want -2", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -1 {
-		t.Errorf("-1 + 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 0 {
-		t.Errorf("-1 + 1 = %d, want 0", r)
+		t.Errorf("-1 %s 1 = %d, want 0", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != 4294967295 {
-		t.Errorf("-1 + 4294967296 = %d, want 4294967295", r)
+		t.Errorf("-1 %s 4294967296 = %d, want 4294967295", "+", r)
 	}
 	y = 9223372036854775806
 	r = x + y
 	if r != 9223372036854775805 {
-		t.Errorf("-1 + 9223372036854775806 = %d, want 9223372036854775805", r)
+		t.Errorf("-1 %s 9223372036854775806 = %d, want 9223372036854775805", "+", r)
 	}
 	y = 9223372036854775807
 	r = x + y
 	if r != 9223372036854775806 {
-		t.Errorf("-1 + 9223372036854775807 = %d, want 9223372036854775806", r)
+		t.Errorf("-1 %s 9223372036854775807 = %d, want 9223372036854775806", "+", r)
 	}
 	x = 0
 	y = -9223372036854775808
 	r = x + y
 	if r != -9223372036854775808 {
-		t.Errorf("0 + -9223372036854775808 = %d, want -9223372036854775808", r)
+		t.Errorf("0 %s -9223372036854775808 = %d, want -9223372036854775808", "+", r)
 	}
 	y = -9223372036854775807
 	r = x + y
 	if r != -9223372036854775807 {
-		t.Errorf("0 + -9223372036854775807 = %d, want -9223372036854775807", r)
+		t.Errorf("0 %s -9223372036854775807 = %d, want -9223372036854775807", "+", r)
 	}
 	y = -4294967296
 	r = x + y
 	if r != -4294967296 {
-		t.Errorf("0 + -4294967296 = %d, want -4294967296", r)
+		t.Errorf("0 %s -4294967296 = %d, want -4294967296", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -1 {
-		t.Errorf("0 + -1 = %d, want -1", r)
+		t.Errorf("0 %s -1 = %d, want -1", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 0 {
-		t.Errorf("0 + 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 1 {
-		t.Errorf("0 + 1 = %d, want 1", r)
+		t.Errorf("0 %s 1 = %d, want 1", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != 4294967296 {
-		t.Errorf("0 + 4294967296 = %d, want 4294967296", r)
+		t.Errorf("0 %s 4294967296 = %d, want 4294967296", "+", r)
 	}
 	y = 9223372036854775806
 	r = x + y
 	if r != 9223372036854775806 {
-		t.Errorf("0 + 9223372036854775806 = %d, want 9223372036854775806", r)
+		t.Errorf("0 %s 9223372036854775806 = %d, want 9223372036854775806", "+", r)
 	}
 	y = 9223372036854775807
 	r = x + y
 	if r != 9223372036854775807 {
-		t.Errorf("0 + 9223372036854775807 = %d, want 9223372036854775807", r)
+		t.Errorf("0 %s 9223372036854775807 = %d, want 9223372036854775807", "+", r)
 	}
 	x = 1
 	y = -9223372036854775808
 	r = x + y
 	if r != -9223372036854775807 {
-		t.Errorf("1 + -9223372036854775808 = %d, want -9223372036854775807", r)
+		t.Errorf("1 %s -9223372036854775808 = %d, want -9223372036854775807", "+", r)
 	}
 	y = -9223372036854775807
 	r = x + y
 	if r != -9223372036854775806 {
-		t.Errorf("1 + -9223372036854775807 = %d, want -9223372036854775806", r)
+		t.Errorf("1 %s -9223372036854775807 = %d, want -9223372036854775806", "+", r)
 	}
 	y = -4294967296
 	r = x + y
 	if r != -4294967295 {
-		t.Errorf("1 + -4294967296 = %d, want -4294967295", r)
+		t.Errorf("1 %s -4294967296 = %d, want -4294967295", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 0 {
-		t.Errorf("1 + -1 = %d, want 0", r)
+		t.Errorf("1 %s -1 = %d, want 0", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 1 {
-		t.Errorf("1 + 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 2 {
-		t.Errorf("1 + 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != 4294967297 {
-		t.Errorf("1 + 4294967296 = %d, want 4294967297", r)
+		t.Errorf("1 %s 4294967296 = %d, want 4294967297", "+", r)
 	}
 	y = 9223372036854775806
 	r = x + y
 	if r != 9223372036854775807 {
-		t.Errorf("1 + 9223372036854775806 = %d, want 9223372036854775807", r)
+		t.Errorf("1 %s 9223372036854775806 = %d, want 9223372036854775807", "+", r)
 	}
 	y = 9223372036854775807
 	r = x + y
 	if r != -9223372036854775808 {
-		t.Errorf("1 + 9223372036854775807 = %d, want -9223372036854775808", r)
+		t.Errorf("1 %s 9223372036854775807 = %d, want -9223372036854775808", "+", r)
 	}
 	x = 4294967296
 	y = -9223372036854775808
 	r = x + y
 	if r != -9223372032559808512 {
-		t.Errorf("4294967296 + -9223372036854775808 = %d, want -9223372032559808512", r)
+		t.Errorf("4294967296 %s -9223372036854775808 = %d, want -9223372032559808512", "+", r)
 	}
 	y = -9223372036854775807
 	r = x + y
 	if r != -9223372032559808511 {
-		t.Errorf("4294967296 + -9223372036854775807 = %d, want -9223372032559808511", r)
+		t.Errorf("4294967296 %s -9223372036854775807 = %d, want -9223372032559808511", "+", r)
 	}
 	y = -4294967296
 	r = x + y
 	if r != 0 {
-		t.Errorf("4294967296 + -4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s -4294967296 = %d, want 0", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 4294967295 {
-		t.Errorf("4294967296 + -1 = %d, want 4294967295", r)
+		t.Errorf("4294967296 %s -1 = %d, want 4294967295", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 4294967296 {
-		t.Errorf("4294967296 + 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 4294967297 {
-		t.Errorf("4294967296 + 1 = %d, want 4294967297", r)
+		t.Errorf("4294967296 %s 1 = %d, want 4294967297", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != 8589934592 {
-		t.Errorf("4294967296 + 4294967296 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 8589934592", "+", r)
 	}
 	y = 9223372036854775806
 	r = x + y
 	if r != -9223372032559808514 {
-		t.Errorf("4294967296 + 9223372036854775806 = %d, want -9223372032559808514", r)
+		t.Errorf("4294967296 %s 9223372036854775806 = %d, want -9223372032559808514", "+", r)
 	}
 	y = 9223372036854775807
 	r = x + y
 	if r != -9223372032559808513 {
-		t.Errorf("4294967296 + 9223372036854775807 = %d, want -9223372032559808513", r)
+		t.Errorf("4294967296 %s 9223372036854775807 = %d, want -9223372032559808513", "+", r)
 	}
 	x = 9223372036854775806
 	y = -9223372036854775808
 	r = x + y
 	if r != -2 {
-		t.Errorf("9223372036854775806 + -9223372036854775808 = %d, want -2", r)
+		t.Errorf("9223372036854775806 %s -9223372036854775808 = %d, want -2", "+", r)
 	}
 	y = -9223372036854775807
 	r = x + y
 	if r != -1 {
-		t.Errorf("9223372036854775806 + -9223372036854775807 = %d, want -1", r)
+		t.Errorf("9223372036854775806 %s -9223372036854775807 = %d, want -1", "+", r)
 	}
 	y = -4294967296
 	r = x + y
 	if r != 9223372032559808510 {
-		t.Errorf("9223372036854775806 + -4294967296 = %d, want 9223372032559808510", r)
+		t.Errorf("9223372036854775806 %s -4294967296 = %d, want 9223372032559808510", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 9223372036854775805 {
-		t.Errorf("9223372036854775806 + -1 = %d, want 9223372036854775805", r)
+		t.Errorf("9223372036854775806 %s -1 = %d, want 9223372036854775805", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 + 0 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 9223372036854775806", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775806 + 1 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want 9223372036854775807", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != -9223372032559808514 {
-		t.Errorf("9223372036854775806 + 4294967296 = %d, want -9223372032559808514", r)
+		t.Errorf("9223372036854775806 %s 4294967296 = %d, want -9223372032559808514", "+", r)
 	}
 	y = 9223372036854775806
 	r = x + y
 	if r != -4 {
-		t.Errorf("9223372036854775806 + 9223372036854775806 = %d, want -4", r)
+		t.Errorf("9223372036854775806 %s 9223372036854775806 = %d, want -4", "+", r)
 	}
 	y = 9223372036854775807
 	r = x + y
 	if r != -3 {
-		t.Errorf("9223372036854775806 + 9223372036854775807 = %d, want -3", r)
+		t.Errorf("9223372036854775806 %s 9223372036854775807 = %d, want -3", "+", r)
 	}
 	x = 9223372036854775807
 	y = -9223372036854775808
 	r = x + y
 	if r != -1 {
-		t.Errorf("9223372036854775807 + -9223372036854775808 = %d, want -1", r)
+		t.Errorf("9223372036854775807 %s -9223372036854775808 = %d, want -1", "+", r)
 	}
 	y = -9223372036854775807
 	r = x + y
 	if r != 0 {
-		t.Errorf("9223372036854775807 + -9223372036854775807 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s -9223372036854775807 = %d, want 0", "+", r)
 	}
 	y = -4294967296
 	r = x + y
 	if r != 9223372032559808511 {
-		t.Errorf("9223372036854775807 + -4294967296 = %d, want 9223372032559808511", r)
+		t.Errorf("9223372036854775807 %s -4294967296 = %d, want 9223372032559808511", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775807 + -1 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775807 %s -1 = %d, want 9223372036854775806", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 + 0 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 9223372036854775807", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -9223372036854775808 {
-		t.Errorf("9223372036854775807 + 1 = %d, want -9223372036854775808", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want -9223372036854775808", "+", r)
 	}
 	y = 4294967296
 	r = x + y
 	if r != -9223372032559808513 {
-		t.Errorf("9223372036854775807 + 4294967296 = %d, want -9223372032559808513", r)
+		t.Errorf("9223372036854775807 %s 4294967296 = %d, want -9223372032559808513", "+", r)
 	}
 	y = 9223372036854775806
 	r = x + y
 	if r != -3 {
-		t.Errorf("9223372036854775807 + 9223372036854775806 = %d, want -3", r)
+		t.Errorf("9223372036854775807 %s 9223372036854775806 = %d, want -3", "+", r)
 	}
 	y = 9223372036854775807
 	r = x + y
 	if r != -2 {
-		t.Errorf("9223372036854775807 + 9223372036854775807 = %d, want -2", r)
+		t.Errorf("9223372036854775807 %s 9223372036854775807 = %d, want -2", "+", r)
 	}
 }
 func TestConstFoldint64sub(t *testing.T) {
@@ -820,415 +820,415 @@ func TestConstFoldint64sub(t *testing.T) {
 	y = -9223372036854775808
 	r = x - y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 - -9223372036854775808 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s -9223372036854775808 = %d, want 0", "-", r)
 	}
 	y = -9223372036854775807
 	r = x - y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 - -9223372036854775807 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s -9223372036854775807 = %d, want -1", "-", r)
 	}
 	y = -4294967296
 	r = x - y
 	if r != -9223372032559808512 {
-		t.Errorf("-9223372036854775808 - -4294967296 = %d, want -9223372032559808512", r)
+		t.Errorf("-9223372036854775808 %s -4294967296 = %d, want -9223372032559808512", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775808 - -1 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775808 %s -1 = %d, want -9223372036854775807", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 - 0 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want -9223372036854775808", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 9223372036854775807 {
-		t.Errorf("-9223372036854775808 - 1 = %d, want 9223372036854775807", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want 9223372036854775807", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != 9223372032559808512 {
-		t.Errorf("-9223372036854775808 - 4294967296 = %d, want 9223372032559808512", r)
+		t.Errorf("-9223372036854775808 %s 4294967296 = %d, want 9223372032559808512", "-", r)
 	}
 	y = 9223372036854775806
 	r = x - y
 	if r != 2 {
-		t.Errorf("-9223372036854775808 - 9223372036854775806 = %d, want 2", r)
+		t.Errorf("-9223372036854775808 %s 9223372036854775806 = %d, want 2", "-", r)
 	}
 	y = 9223372036854775807
 	r = x - y
 	if r != 1 {
-		t.Errorf("-9223372036854775808 - 9223372036854775807 = %d, want 1", r)
+		t.Errorf("-9223372036854775808 %s 9223372036854775807 = %d, want 1", "-", r)
 	}
 	x = -9223372036854775807
 	y = -9223372036854775808
 	r = x - y
 	if r != 1 {
-		t.Errorf("-9223372036854775807 - -9223372036854775808 = %d, want 1", r)
+		t.Errorf("-9223372036854775807 %s -9223372036854775808 = %d, want 1", "-", r)
 	}
 	y = -9223372036854775807
 	r = x - y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 - -9223372036854775807 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s -9223372036854775807 = %d, want 0", "-", r)
 	}
 	y = -4294967296
 	r = x - y
 	if r != -9223372032559808511 {
-		t.Errorf("-9223372036854775807 - -4294967296 = %d, want -9223372032559808511", r)
+		t.Errorf("-9223372036854775807 %s -4294967296 = %d, want -9223372032559808511", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -9223372036854775806 {
-		t.Errorf("-9223372036854775807 - -1 = %d, want -9223372036854775806", r)
+		t.Errorf("-9223372036854775807 %s -1 = %d, want -9223372036854775806", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 - 0 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want -9223372036854775807", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775807 - 1 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want -9223372036854775808", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != 9223372032559808513 {
-		t.Errorf("-9223372036854775807 - 4294967296 = %d, want 9223372032559808513", r)
+		t.Errorf("-9223372036854775807 %s 4294967296 = %d, want 9223372032559808513", "-", r)
 	}
 	y = 9223372036854775806
 	r = x - y
 	if r != 3 {
-		t.Errorf("-9223372036854775807 - 9223372036854775806 = %d, want 3", r)
+		t.Errorf("-9223372036854775807 %s 9223372036854775806 = %d, want 3", "-", r)
 	}
 	y = 9223372036854775807
 	r = x - y
 	if r != 2 {
-		t.Errorf("-9223372036854775807 - 9223372036854775807 = %d, want 2", r)
+		t.Errorf("-9223372036854775807 %s 9223372036854775807 = %d, want 2", "-", r)
 	}
 	x = -4294967296
 	y = -9223372036854775808
 	r = x - y
 	if r != 9223372032559808512 {
-		t.Errorf("-4294967296 - -9223372036854775808 = %d, want 9223372032559808512", r)
+		t.Errorf("-4294967296 %s -9223372036854775808 = %d, want 9223372032559808512", "-", r)
 	}
 	y = -9223372036854775807
 	r = x - y
 	if r != 9223372032559808511 {
-		t.Errorf("-4294967296 - -9223372036854775807 = %d, want 9223372032559808511", r)
+		t.Errorf("-4294967296 %s -9223372036854775807 = %d, want 9223372032559808511", "-", r)
 	}
 	y = -4294967296
 	r = x - y
 	if r != 0 {
-		t.Errorf("-4294967296 - -4294967296 = %d, want 0", r)
+		t.Errorf("-4294967296 %s -4294967296 = %d, want 0", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -4294967295 {
-		t.Errorf("-4294967296 - -1 = %d, want -4294967295", r)
+		t.Errorf("-4294967296 %s -1 = %d, want -4294967295", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 - 0 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 0 = %d, want -4294967296", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -4294967297 {
-		t.Errorf("-4294967296 - 1 = %d, want -4294967297", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -4294967297", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != -8589934592 {
-		t.Errorf("-4294967296 - 4294967296 = %d, want -8589934592", r)
+		t.Errorf("-4294967296 %s 4294967296 = %d, want -8589934592", "-", r)
 	}
 	y = 9223372036854775806
 	r = x - y
 	if r != 9223372032559808514 {
-		t.Errorf("-4294967296 - 9223372036854775806 = %d, want 9223372032559808514", r)
+		t.Errorf("-4294967296 %s 9223372036854775806 = %d, want 9223372032559808514", "-", r)
 	}
 	y = 9223372036854775807
 	r = x - y
 	if r != 9223372032559808513 {
-		t.Errorf("-4294967296 - 9223372036854775807 = %d, want 9223372032559808513", r)
+		t.Errorf("-4294967296 %s 9223372036854775807 = %d, want 9223372032559808513", "-", r)
 	}
 	x = -1
 	y = -9223372036854775808
 	r = x - y
 	if r != 9223372036854775807 {
-		t.Errorf("-1 - -9223372036854775808 = %d, want 9223372036854775807", r)
+		t.Errorf("-1 %s -9223372036854775808 = %d, want 9223372036854775807", "-", r)
 	}
 	y = -9223372036854775807
 	r = x - y
 	if r != 9223372036854775806 {
-		t.Errorf("-1 - -9223372036854775807 = %d, want 9223372036854775806", r)
+		t.Errorf("-1 %s -9223372036854775807 = %d, want 9223372036854775806", "-", r)
 	}
 	y = -4294967296
 	r = x - y
 	if r != 4294967295 {
-		t.Errorf("-1 - -4294967296 = %d, want 4294967295", r)
+		t.Errorf("-1 %s -4294967296 = %d, want 4294967295", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 0 {
-		t.Errorf("-1 - -1 = %d, want 0", r)
+		t.Errorf("-1 %s -1 = %d, want 0", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -1 {
-		t.Errorf("-1 - 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -2 {
-		t.Errorf("-1 - 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != -4294967297 {
-		t.Errorf("-1 - 4294967296 = %d, want -4294967297", r)
+		t.Errorf("-1 %s 4294967296 = %d, want -4294967297", "-", r)
 	}
 	y = 9223372036854775806
 	r = x - y
 	if r != -9223372036854775807 {
-		t.Errorf("-1 - 9223372036854775806 = %d, want -9223372036854775807", r)
+		t.Errorf("-1 %s 9223372036854775806 = %d, want -9223372036854775807", "-", r)
 	}
 	y = 9223372036854775807
 	r = x - y
 	if r != -9223372036854775808 {
-		t.Errorf("-1 - 9223372036854775807 = %d, want -9223372036854775808", r)
+		t.Errorf("-1 %s 9223372036854775807 = %d, want -9223372036854775808", "-", r)
 	}
 	x = 0
 	y = -9223372036854775808
 	r = x - y
 	if r != -9223372036854775808 {
-		t.Errorf("0 - -9223372036854775808 = %d, want -9223372036854775808", r)
+		t.Errorf("0 %s -9223372036854775808 = %d, want -9223372036854775808", "-", r)
 	}
 	y = -9223372036854775807
 	r = x - y
 	if r != 9223372036854775807 {
-		t.Errorf("0 - -9223372036854775807 = %d, want 9223372036854775807", r)
+		t.Errorf("0 %s -9223372036854775807 = %d, want 9223372036854775807", "-", r)
 	}
 	y = -4294967296
 	r = x - y
 	if r != 4294967296 {
-		t.Errorf("0 - -4294967296 = %d, want 4294967296", r)
+		t.Errorf("0 %s -4294967296 = %d, want 4294967296", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 1 {
-		t.Errorf("0 - -1 = %d, want 1", r)
+		t.Errorf("0 %s -1 = %d, want 1", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 0 {
-		t.Errorf("0 - 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -1 {
-		t.Errorf("0 - 1 = %d, want -1", r)
+		t.Errorf("0 %s 1 = %d, want -1", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != -4294967296 {
-		t.Errorf("0 - 4294967296 = %d, want -4294967296", r)
+		t.Errorf("0 %s 4294967296 = %d, want -4294967296", "-", r)
 	}
 	y = 9223372036854775806
 	r = x - y
 	if r != -9223372036854775806 {
-		t.Errorf("0 - 9223372036854775806 = %d, want -9223372036854775806", r)
+		t.Errorf("0 %s 9223372036854775806 = %d, want -9223372036854775806", "-", r)
 	}
 	y = 9223372036854775807
 	r = x - y
 	if r != -9223372036854775807 {
-		t.Errorf("0 - 9223372036854775807 = %d, want -9223372036854775807", r)
+		t.Errorf("0 %s 9223372036854775807 = %d, want -9223372036854775807", "-", r)
 	}
 	x = 1
 	y = -9223372036854775808
 	r = x - y
 	if r != -9223372036854775807 {
-		t.Errorf("1 - -9223372036854775808 = %d, want -9223372036854775807", r)
+		t.Errorf("1 %s -9223372036854775808 = %d, want -9223372036854775807", "-", r)
 	}
 	y = -9223372036854775807
 	r = x - y
 	if r != -9223372036854775808 {
-		t.Errorf("1 - -9223372036854775807 = %d, want -9223372036854775808", r)
+		t.Errorf("1 %s -9223372036854775807 = %d, want -9223372036854775808", "-", r)
 	}
 	y = -4294967296
 	r = x - y
 	if r != 4294967297 {
-		t.Errorf("1 - -4294967296 = %d, want 4294967297", r)
+		t.Errorf("1 %s -4294967296 = %d, want 4294967297", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 2 {
-		t.Errorf("1 - -1 = %d, want 2", r)
+		t.Errorf("1 %s -1 = %d, want 2", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 1 {
-		t.Errorf("1 - 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 0 {
-		t.Errorf("1 - 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != -4294967295 {
-		t.Errorf("1 - 4294967296 = %d, want -4294967295", r)
+		t.Errorf("1 %s 4294967296 = %d, want -4294967295", "-", r)
 	}
 	y = 9223372036854775806
 	r = x - y
 	if r != -9223372036854775805 {
-		t.Errorf("1 - 9223372036854775806 = %d, want -9223372036854775805", r)
+		t.Errorf("1 %s 9223372036854775806 = %d, want -9223372036854775805", "-", r)
 	}
 	y = 9223372036854775807
 	r = x - y
 	if r != -9223372036854775806 {
-		t.Errorf("1 - 9223372036854775807 = %d, want -9223372036854775806", r)
+		t.Errorf("1 %s 9223372036854775807 = %d, want -9223372036854775806", "-", r)
 	}
 	x = 4294967296
 	y = -9223372036854775808
 	r = x - y
 	if r != -9223372032559808512 {
-		t.Errorf("4294967296 - -9223372036854775808 = %d, want -9223372032559808512", r)
+		t.Errorf("4294967296 %s -9223372036854775808 = %d, want -9223372032559808512", "-", r)
 	}
 	y = -9223372036854775807
 	r = x - y
 	if r != -9223372032559808513 {
-		t.Errorf("4294967296 - -9223372036854775807 = %d, want -9223372032559808513", r)
+		t.Errorf("4294967296 %s -9223372036854775807 = %d, want -9223372032559808513", "-", r)
 	}
 	y = -4294967296
 	r = x - y
 	if r != 8589934592 {
-		t.Errorf("4294967296 - -4294967296 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s -4294967296 = %d, want 8589934592", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 4294967297 {
-		t.Errorf("4294967296 - -1 = %d, want 4294967297", r)
+		t.Errorf("4294967296 %s -1 = %d, want 4294967297", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 4294967296 {
-		t.Errorf("4294967296 - 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 4294967295 {
-		t.Errorf("4294967296 - 1 = %d, want 4294967295", r)
+		t.Errorf("4294967296 %s 1 = %d, want 4294967295", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != 0 {
-		t.Errorf("4294967296 - 4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 0", "-", r)
 	}
 	y = 9223372036854775806
 	r = x - y
 	if r != -9223372032559808510 {
-		t.Errorf("4294967296 - 9223372036854775806 = %d, want -9223372032559808510", r)
+		t.Errorf("4294967296 %s 9223372036854775806 = %d, want -9223372032559808510", "-", r)
 	}
 	y = 9223372036854775807
 	r = x - y
 	if r != -9223372032559808511 {
-		t.Errorf("4294967296 - 9223372036854775807 = %d, want -9223372032559808511", r)
+		t.Errorf("4294967296 %s 9223372036854775807 = %d, want -9223372032559808511", "-", r)
 	}
 	x = 9223372036854775806
 	y = -9223372036854775808
 	r = x - y
 	if r != -2 {
-		t.Errorf("9223372036854775806 - -9223372036854775808 = %d, want -2", r)
+		t.Errorf("9223372036854775806 %s -9223372036854775808 = %d, want -2", "-", r)
 	}
 	y = -9223372036854775807
 	r = x - y
 	if r != -3 {
-		t.Errorf("9223372036854775806 - -9223372036854775807 = %d, want -3", r)
+		t.Errorf("9223372036854775806 %s -9223372036854775807 = %d, want -3", "-", r)
 	}
 	y = -4294967296
 	r = x - y
 	if r != -9223372032559808514 {
-		t.Errorf("9223372036854775806 - -4294967296 = %d, want -9223372032559808514", r)
+		t.Errorf("9223372036854775806 %s -4294967296 = %d, want -9223372032559808514", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775806 - -1 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775806 %s -1 = %d, want 9223372036854775807", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 - 0 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 9223372036854775806", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 9223372036854775805 {
-		t.Errorf("9223372036854775806 - 1 = %d, want 9223372036854775805", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want 9223372036854775805", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != 9223372032559808510 {
-		t.Errorf("9223372036854775806 - 4294967296 = %d, want 9223372032559808510", r)
+		t.Errorf("9223372036854775806 %s 4294967296 = %d, want 9223372032559808510", "-", r)
 	}
 	y = 9223372036854775806
 	r = x - y
 	if r != 0 {
-		t.Errorf("9223372036854775806 - 9223372036854775806 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 9223372036854775806 = %d, want 0", "-", r)
 	}
 	y = 9223372036854775807
 	r = x - y
 	if r != -1 {
-		t.Errorf("9223372036854775806 - 9223372036854775807 = %d, want -1", r)
+		t.Errorf("9223372036854775806 %s 9223372036854775807 = %d, want -1", "-", r)
 	}
 	x = 9223372036854775807
 	y = -9223372036854775808
 	r = x - y
 	if r != -1 {
-		t.Errorf("9223372036854775807 - -9223372036854775808 = %d, want -1", r)
+		t.Errorf("9223372036854775807 %s -9223372036854775808 = %d, want -1", "-", r)
 	}
 	y = -9223372036854775807
 	r = x - y
 	if r != -2 {
-		t.Errorf("9223372036854775807 - -9223372036854775807 = %d, want -2", r)
+		t.Errorf("9223372036854775807 %s -9223372036854775807 = %d, want -2", "-", r)
 	}
 	y = -4294967296
 	r = x - y
 	if r != -9223372032559808513 {
-		t.Errorf("9223372036854775807 - -4294967296 = %d, want -9223372032559808513", r)
+		t.Errorf("9223372036854775807 %s -4294967296 = %d, want -9223372032559808513", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -9223372036854775808 {
-		t.Errorf("9223372036854775807 - -1 = %d, want -9223372036854775808", r)
+		t.Errorf("9223372036854775807 %s -1 = %d, want -9223372036854775808", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 - 0 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 9223372036854775807", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775807 - 1 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want 9223372036854775806", "-", r)
 	}
 	y = 4294967296
 	r = x - y
 	if r != 9223372032559808511 {
-		t.Errorf("9223372036854775807 - 4294967296 = %d, want 9223372032559808511", r)
+		t.Errorf("9223372036854775807 %s 4294967296 = %d, want 9223372032559808511", "-", r)
 	}
 	y = 9223372036854775806
 	r = x - y
 	if r != 1 {
-		t.Errorf("9223372036854775807 - 9223372036854775806 = %d, want 1", r)
+		t.Errorf("9223372036854775807 %s 9223372036854775806 = %d, want 1", "-", r)
 	}
 	y = 9223372036854775807
 	r = x - y
 	if r != 0 {
-		t.Errorf("9223372036854775807 - 9223372036854775807 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 9223372036854775807 = %d, want 0", "-", r)
 	}
 }
 func TestConstFoldint64div(t *testing.T) {
@@ -1237,370 +1237,370 @@ func TestConstFoldint64div(t *testing.T) {
 	y = -9223372036854775808
 	r = x / y
 	if r != 1 {
-		t.Errorf("-9223372036854775808 / -9223372036854775808 = %d, want 1", r)
+		t.Errorf("-9223372036854775808 %s -9223372036854775808 = %d, want 1", "/", r)
 	}
 	y = -9223372036854775807
 	r = x / y
 	if r != 1 {
-		t.Errorf("-9223372036854775808 / -9223372036854775807 = %d, want 1", r)
+		t.Errorf("-9223372036854775808 %s -9223372036854775807 = %d, want 1", "/", r)
 	}
 	y = -4294967296
 	r = x / y
 	if r != 2147483648 {
-		t.Errorf("-9223372036854775808 / -4294967296 = %d, want 2147483648", r)
+		t.Errorf("-9223372036854775808 %s -4294967296 = %d, want 2147483648", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 / -1 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s -1 = %d, want -9223372036854775808", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 / 1 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want -9223372036854775808", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != -2147483648 {
-		t.Errorf("-9223372036854775808 / 4294967296 = %d, want -2147483648", r)
+		t.Errorf("-9223372036854775808 %s 4294967296 = %d, want -2147483648", "/", r)
 	}
 	y = 9223372036854775806
 	r = x / y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 / 9223372036854775806 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s 9223372036854775806 = %d, want -1", "/", r)
 	}
 	y = 9223372036854775807
 	r = x / y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 / 9223372036854775807 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s 9223372036854775807 = %d, want -1", "/", r)
 	}
 	x = -9223372036854775807
 	y = -9223372036854775808
 	r = x / y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 / -9223372036854775808 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s -9223372036854775808 = %d, want 0", "/", r)
 	}
 	y = -9223372036854775807
 	r = x / y
 	if r != 1 {
-		t.Errorf("-9223372036854775807 / -9223372036854775807 = %d, want 1", r)
+		t.Errorf("-9223372036854775807 %s -9223372036854775807 = %d, want 1", "/", r)
 	}
 	y = -4294967296
 	r = x / y
 	if r != 2147483647 {
-		t.Errorf("-9223372036854775807 / -4294967296 = %d, want 2147483647", r)
+		t.Errorf("-9223372036854775807 %s -4294967296 = %d, want 2147483647", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 9223372036854775807 {
-		t.Errorf("-9223372036854775807 / -1 = %d, want 9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s -1 = %d, want 9223372036854775807", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 / 1 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want -9223372036854775807", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != -2147483647 {
-		t.Errorf("-9223372036854775807 / 4294967296 = %d, want -2147483647", r)
+		t.Errorf("-9223372036854775807 %s 4294967296 = %d, want -2147483647", "/", r)
 	}
 	y = 9223372036854775806
 	r = x / y
 	if r != -1 {
-		t.Errorf("-9223372036854775807 / 9223372036854775806 = %d, want -1", r)
+		t.Errorf("-9223372036854775807 %s 9223372036854775806 = %d, want -1", "/", r)
 	}
 	y = 9223372036854775807
 	r = x / y
 	if r != -1 {
-		t.Errorf("-9223372036854775807 / 9223372036854775807 = %d, want -1", r)
+		t.Errorf("-9223372036854775807 %s 9223372036854775807 = %d, want -1", "/", r)
 	}
 	x = -4294967296
 	y = -9223372036854775808
 	r = x / y
 	if r != 0 {
-		t.Errorf("-4294967296 / -9223372036854775808 = %d, want 0", r)
+		t.Errorf("-4294967296 %s -9223372036854775808 = %d, want 0", "/", r)
 	}
 	y = -9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("-4294967296 / -9223372036854775807 = %d, want 0", r)
+		t.Errorf("-4294967296 %s -9223372036854775807 = %d, want 0", "/", r)
 	}
 	y = -4294967296
 	r = x / y
 	if r != 1 {
-		t.Errorf("-4294967296 / -4294967296 = %d, want 1", r)
+		t.Errorf("-4294967296 %s -4294967296 = %d, want 1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 4294967296 {
-		t.Errorf("-4294967296 / -1 = %d, want 4294967296", r)
+		t.Errorf("-4294967296 %s -1 = %d, want 4294967296", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 / 1 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -4294967296", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != -1 {
-		t.Errorf("-4294967296 / 4294967296 = %d, want -1", r)
+		t.Errorf("-4294967296 %s 4294967296 = %d, want -1", "/", r)
 	}
 	y = 9223372036854775806
 	r = x / y
 	if r != 0 {
-		t.Errorf("-4294967296 / 9223372036854775806 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 9223372036854775806 = %d, want 0", "/", r)
 	}
 	y = 9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("-4294967296 / 9223372036854775807 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 9223372036854775807 = %d, want 0", "/", r)
 	}
 	x = -1
 	y = -9223372036854775808
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / -9223372036854775808 = %d, want 0", r)
+		t.Errorf("-1 %s -9223372036854775808 = %d, want 0", "/", r)
 	}
 	y = -9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / -9223372036854775807 = %d, want 0", r)
+		t.Errorf("-1 %s -9223372036854775807 = %d, want 0", "/", r)
 	}
 	y = -4294967296
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / -4294967296 = %d, want 0", r)
+		t.Errorf("-1 %s -4294967296 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 1 {
-		t.Errorf("-1 / -1 = %d, want 1", r)
+		t.Errorf("-1 %s -1 = %d, want 1", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -1 {
-		t.Errorf("-1 / 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / 4294967296 = %d, want 0", r)
+		t.Errorf("-1 %s 4294967296 = %d, want 0", "/", r)
 	}
 	y = 9223372036854775806
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / 9223372036854775806 = %d, want 0", r)
+		t.Errorf("-1 %s 9223372036854775806 = %d, want 0", "/", r)
 	}
 	y = 9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / 9223372036854775807 = %d, want 0", r)
+		t.Errorf("-1 %s 9223372036854775807 = %d, want 0", "/", r)
 	}
 	x = 0
 	y = -9223372036854775808
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -9223372036854775808 = %d, want 0", r)
+		t.Errorf("0 %s -9223372036854775808 = %d, want 0", "/", r)
 	}
 	y = -9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -9223372036854775807 = %d, want 0", r)
+		t.Errorf("0 %s -9223372036854775807 = %d, want 0", "/", r)
 	}
 	y = -4294967296
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -4294967296 = %d, want 0", r)
+		t.Errorf("0 %s -4294967296 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "/", r)
 	}
 	y = 9223372036854775806
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 9223372036854775806 = %d, want 0", r)
+		t.Errorf("0 %s 9223372036854775806 = %d, want 0", "/", r)
 	}
 	y = 9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 9223372036854775807 = %d, want 0", r)
+		t.Errorf("0 %s 9223372036854775807 = %d, want 0", "/", r)
 	}
 	x = 1
 	y = -9223372036854775808
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / -9223372036854775808 = %d, want 0", r)
+		t.Errorf("1 %s -9223372036854775808 = %d, want 0", "/", r)
 	}
 	y = -9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / -9223372036854775807 = %d, want 0", r)
+		t.Errorf("1 %s -9223372036854775807 = %d, want 0", "/", r)
 	}
 	y = -4294967296
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / -4294967296 = %d, want 0", r)
+		t.Errorf("1 %s -4294967296 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -1 {
-		t.Errorf("1 / -1 = %d, want -1", r)
+		t.Errorf("1 %s -1 = %d, want -1", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 1 {
-		t.Errorf("1 / 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", "/", r)
 	}
 	y = 9223372036854775806
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 9223372036854775806 = %d, want 0", r)
+		t.Errorf("1 %s 9223372036854775806 = %d, want 0", "/", r)
 	}
 	y = 9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 9223372036854775807 = %d, want 0", r)
+		t.Errorf("1 %s 9223372036854775807 = %d, want 0", "/", r)
 	}
 	x = 4294967296
 	y = -9223372036854775808
 	r = x / y
 	if r != 0 {
-		t.Errorf("4294967296 / -9223372036854775808 = %d, want 0", r)
+		t.Errorf("4294967296 %s -9223372036854775808 = %d, want 0", "/", r)
 	}
 	y = -9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("4294967296 / -9223372036854775807 = %d, want 0", r)
+		t.Errorf("4294967296 %s -9223372036854775807 = %d, want 0", "/", r)
 	}
 	y = -4294967296
 	r = x / y
 	if r != -1 {
-		t.Errorf("4294967296 / -4294967296 = %d, want -1", r)
+		t.Errorf("4294967296 %s -4294967296 = %d, want -1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -4294967296 {
-		t.Errorf("4294967296 / -1 = %d, want -4294967296", r)
+		t.Errorf("4294967296 %s -1 = %d, want -4294967296", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 4294967296 {
-		t.Errorf("4294967296 / 1 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 1 = %d, want 4294967296", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != 1 {
-		t.Errorf("4294967296 / 4294967296 = %d, want 1", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 1", "/", r)
 	}
 	y = 9223372036854775806
 	r = x / y
 	if r != 0 {
-		t.Errorf("4294967296 / 9223372036854775806 = %d, want 0", r)
+		t.Errorf("4294967296 %s 9223372036854775806 = %d, want 0", "/", r)
 	}
 	y = 9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("4294967296 / 9223372036854775807 = %d, want 0", r)
+		t.Errorf("4294967296 %s 9223372036854775807 = %d, want 0", "/", r)
 	}
 	x = 9223372036854775806
 	y = -9223372036854775808
 	r = x / y
 	if r != 0 {
-		t.Errorf("9223372036854775806 / -9223372036854775808 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s -9223372036854775808 = %d, want 0", "/", r)
 	}
 	y = -9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("9223372036854775806 / -9223372036854775807 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s -9223372036854775807 = %d, want 0", "/", r)
 	}
 	y = -4294967296
 	r = x / y
 	if r != -2147483647 {
-		t.Errorf("9223372036854775806 / -4294967296 = %d, want -2147483647", r)
+		t.Errorf("9223372036854775806 %s -4294967296 = %d, want -2147483647", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -9223372036854775806 {
-		t.Errorf("9223372036854775806 / -1 = %d, want -9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s -1 = %d, want -9223372036854775806", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 / 1 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want 9223372036854775806", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != 2147483647 {
-		t.Errorf("9223372036854775806 / 4294967296 = %d, want 2147483647", r)
+		t.Errorf("9223372036854775806 %s 4294967296 = %d, want 2147483647", "/", r)
 	}
 	y = 9223372036854775806
 	r = x / y
 	if r != 1 {
-		t.Errorf("9223372036854775806 / 9223372036854775806 = %d, want 1", r)
+		t.Errorf("9223372036854775806 %s 9223372036854775806 = %d, want 1", "/", r)
 	}
 	y = 9223372036854775807
 	r = x / y
 	if r != 0 {
-		t.Errorf("9223372036854775806 / 9223372036854775807 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 9223372036854775807 = %d, want 0", "/", r)
 	}
 	x = 9223372036854775807
 	y = -9223372036854775808
 	r = x / y
 	if r != 0 {
-		t.Errorf("9223372036854775807 / -9223372036854775808 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s -9223372036854775808 = %d, want 0", "/", r)
 	}
 	y = -9223372036854775807
 	r = x / y
 	if r != -1 {
-		t.Errorf("9223372036854775807 / -9223372036854775807 = %d, want -1", r)
+		t.Errorf("9223372036854775807 %s -9223372036854775807 = %d, want -1", "/", r)
 	}
 	y = -4294967296
 	r = x / y
 	if r != -2147483647 {
-		t.Errorf("9223372036854775807 / -4294967296 = %d, want -2147483647", r)
+		t.Errorf("9223372036854775807 %s -4294967296 = %d, want -2147483647", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -9223372036854775807 {
-		t.Errorf("9223372036854775807 / -1 = %d, want -9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s -1 = %d, want -9223372036854775807", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 / 1 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want 9223372036854775807", "/", r)
 	}
 	y = 4294967296
 	r = x / y
 	if r != 2147483647 {
-		t.Errorf("9223372036854775807 / 4294967296 = %d, want 2147483647", r)
+		t.Errorf("9223372036854775807 %s 4294967296 = %d, want 2147483647", "/", r)
 	}
 	y = 9223372036854775806
 	r = x / y
 	if r != 1 {
-		t.Errorf("9223372036854775807 / 9223372036854775806 = %d, want 1", r)
+		t.Errorf("9223372036854775807 %s 9223372036854775806 = %d, want 1", "/", r)
 	}
 	y = 9223372036854775807
 	r = x / y
 	if r != 1 {
-		t.Errorf("9223372036854775807 / 9223372036854775807 = %d, want 1", r)
+		t.Errorf("9223372036854775807 %s 9223372036854775807 = %d, want 1", "/", r)
 	}
 }
 func TestConstFoldint64mul(t *testing.T) {
@@ -1609,415 +1609,415 @@ func TestConstFoldint64mul(t *testing.T) {
 	y = -9223372036854775808
 	r = x * y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 * -9223372036854775808 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s -9223372036854775808 = %d, want 0", "*", r)
 	}
 	y = -9223372036854775807
 	r = x * y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 * -9223372036854775807 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s -9223372036854775807 = %d, want -9223372036854775808", "*", r)
 	}
 	y = -4294967296
 	r = x * y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 * -4294967296 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s -4294967296 = %d, want 0", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 * -1 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s -1 = %d, want -9223372036854775808", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 * 0 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 * 1 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want -9223372036854775808", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 * 4294967296 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 4294967296 = %d, want 0", "*", r)
 	}
 	y = 9223372036854775806
 	r = x * y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 * 9223372036854775806 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 9223372036854775806 = %d, want 0", "*", r)
 	}
 	y = 9223372036854775807
 	r = x * y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 * 9223372036854775807 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 9223372036854775807 = %d, want -9223372036854775808", "*", r)
 	}
 	x = -9223372036854775807
 	y = -9223372036854775808
 	r = x * y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775807 * -9223372036854775808 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775807 %s -9223372036854775808 = %d, want -9223372036854775808", "*", r)
 	}
 	y = -9223372036854775807
 	r = x * y
 	if r != 1 {
-		t.Errorf("-9223372036854775807 * -9223372036854775807 = %d, want 1", r)
+		t.Errorf("-9223372036854775807 %s -9223372036854775807 = %d, want 1", "*", r)
 	}
 	y = -4294967296
 	r = x * y
 	if r != -4294967296 {
-		t.Errorf("-9223372036854775807 * -4294967296 = %d, want -4294967296", r)
+		t.Errorf("-9223372036854775807 %s -4294967296 = %d, want -4294967296", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 9223372036854775807 {
-		t.Errorf("-9223372036854775807 * -1 = %d, want 9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s -1 = %d, want 9223372036854775807", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 * 0 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 * 1 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want -9223372036854775807", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != 4294967296 {
-		t.Errorf("-9223372036854775807 * 4294967296 = %d, want 4294967296", r)
+		t.Errorf("-9223372036854775807 %s 4294967296 = %d, want 4294967296", "*", r)
 	}
 	y = 9223372036854775806
 	r = x * y
 	if r != 9223372036854775806 {
-		t.Errorf("-9223372036854775807 * 9223372036854775806 = %d, want 9223372036854775806", r)
+		t.Errorf("-9223372036854775807 %s 9223372036854775806 = %d, want 9223372036854775806", "*", r)
 	}
 	y = 9223372036854775807
 	r = x * y
 	if r != -1 {
-		t.Errorf("-9223372036854775807 * 9223372036854775807 = %d, want -1", r)
+		t.Errorf("-9223372036854775807 %s 9223372036854775807 = %d, want -1", "*", r)
 	}
 	x = -4294967296
 	y = -9223372036854775808
 	r = x * y
 	if r != 0 {
-		t.Errorf("-4294967296 * -9223372036854775808 = %d, want 0", r)
+		t.Errorf("-4294967296 %s -9223372036854775808 = %d, want 0", "*", r)
 	}
 	y = -9223372036854775807
 	r = x * y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 * -9223372036854775807 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s -9223372036854775807 = %d, want -4294967296", "*", r)
 	}
 	y = -4294967296
 	r = x * y
 	if r != 0 {
-		t.Errorf("-4294967296 * -4294967296 = %d, want 0", r)
+		t.Errorf("-4294967296 %s -4294967296 = %d, want 0", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 4294967296 {
-		t.Errorf("-4294967296 * -1 = %d, want 4294967296", r)
+		t.Errorf("-4294967296 %s -1 = %d, want 4294967296", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-4294967296 * 0 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 * 1 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -4294967296", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != 0 {
-		t.Errorf("-4294967296 * 4294967296 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 4294967296 = %d, want 0", "*", r)
 	}
 	y = 9223372036854775806
 	r = x * y
 	if r != 8589934592 {
-		t.Errorf("-4294967296 * 9223372036854775806 = %d, want 8589934592", r)
+		t.Errorf("-4294967296 %s 9223372036854775806 = %d, want 8589934592", "*", r)
 	}
 	y = 9223372036854775807
 	r = x * y
 	if r != 4294967296 {
-		t.Errorf("-4294967296 * 9223372036854775807 = %d, want 4294967296", r)
+		t.Errorf("-4294967296 %s 9223372036854775807 = %d, want 4294967296", "*", r)
 	}
 	x = -1
 	y = -9223372036854775808
 	r = x * y
 	if r != -9223372036854775808 {
-		t.Errorf("-1 * -9223372036854775808 = %d, want -9223372036854775808", r)
+		t.Errorf("-1 %s -9223372036854775808 = %d, want -9223372036854775808", "*", r)
 	}
 	y = -9223372036854775807
 	r = x * y
 	if r != 9223372036854775807 {
-		t.Errorf("-1 * -9223372036854775807 = %d, want 9223372036854775807", r)
+		t.Errorf("-1 %s -9223372036854775807 = %d, want 9223372036854775807", "*", r)
 	}
 	y = -4294967296
 	r = x * y
 	if r != 4294967296 {
-		t.Errorf("-1 * -4294967296 = %d, want 4294967296", r)
+		t.Errorf("-1 %s -4294967296 = %d, want 4294967296", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 1 {
-		t.Errorf("-1 * -1 = %d, want 1", r)
+		t.Errorf("-1 %s -1 = %d, want 1", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-1 * 0 = %d, want 0", r)
+		t.Errorf("-1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -1 {
-		t.Errorf("-1 * 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != -4294967296 {
-		t.Errorf("-1 * 4294967296 = %d, want -4294967296", r)
+		t.Errorf("-1 %s 4294967296 = %d, want -4294967296", "*", r)
 	}
 	y = 9223372036854775806
 	r = x * y
 	if r != -9223372036854775806 {
-		t.Errorf("-1 * 9223372036854775806 = %d, want -9223372036854775806", r)
+		t.Errorf("-1 %s 9223372036854775806 = %d, want -9223372036854775806", "*", r)
 	}
 	y = 9223372036854775807
 	r = x * y
 	if r != -9223372036854775807 {
-		t.Errorf("-1 * 9223372036854775807 = %d, want -9223372036854775807", r)
+		t.Errorf("-1 %s 9223372036854775807 = %d, want -9223372036854775807", "*", r)
 	}
 	x = 0
 	y = -9223372036854775808
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -9223372036854775808 = %d, want 0", r)
+		t.Errorf("0 %s -9223372036854775808 = %d, want 0", "*", r)
 	}
 	y = -9223372036854775807
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -9223372036854775807 = %d, want 0", r)
+		t.Errorf("0 %s -9223372036854775807 = %d, want 0", "*", r)
 	}
 	y = -4294967296
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -4294967296 = %d, want 0", r)
+		t.Errorf("0 %s -4294967296 = %d, want 0", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "*", r)
 	}
 	y = 9223372036854775806
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 9223372036854775806 = %d, want 0", r)
+		t.Errorf("0 %s 9223372036854775806 = %d, want 0", "*", r)
 	}
 	y = 9223372036854775807
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 9223372036854775807 = %d, want 0", r)
+		t.Errorf("0 %s 9223372036854775807 = %d, want 0", "*", r)
 	}
 	x = 1
 	y = -9223372036854775808
 	r = x * y
 	if r != -9223372036854775808 {
-		t.Errorf("1 * -9223372036854775808 = %d, want -9223372036854775808", r)
+		t.Errorf("1 %s -9223372036854775808 = %d, want -9223372036854775808", "*", r)
 	}
 	y = -9223372036854775807
 	r = x * y
 	if r != -9223372036854775807 {
-		t.Errorf("1 * -9223372036854775807 = %d, want -9223372036854775807", r)
+		t.Errorf("1 %s -9223372036854775807 = %d, want -9223372036854775807", "*", r)
 	}
 	y = -4294967296
 	r = x * y
 	if r != -4294967296 {
-		t.Errorf("1 * -4294967296 = %d, want -4294967296", r)
+		t.Errorf("1 %s -4294967296 = %d, want -4294967296", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -1 {
-		t.Errorf("1 * -1 = %d, want -1", r)
+		t.Errorf("1 %s -1 = %d, want -1", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("1 * 0 = %d, want 0", r)
+		t.Errorf("1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 1 {
-		t.Errorf("1 * 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != 4294967296 {
-		t.Errorf("1 * 4294967296 = %d, want 4294967296", r)
+		t.Errorf("1 %s 4294967296 = %d, want 4294967296", "*", r)
 	}
 	y = 9223372036854775806
 	r = x * y
 	if r != 9223372036854775806 {
-		t.Errorf("1 * 9223372036854775806 = %d, want 9223372036854775806", r)
+		t.Errorf("1 %s 9223372036854775806 = %d, want 9223372036854775806", "*", r)
 	}
 	y = 9223372036854775807
 	r = x * y
 	if r != 9223372036854775807 {
-		t.Errorf("1 * 9223372036854775807 = %d, want 9223372036854775807", r)
+		t.Errorf("1 %s 9223372036854775807 = %d, want 9223372036854775807", "*", r)
 	}
 	x = 4294967296
 	y = -9223372036854775808
 	r = x * y
 	if r != 0 {
-		t.Errorf("4294967296 * -9223372036854775808 = %d, want 0", r)
+		t.Errorf("4294967296 %s -9223372036854775808 = %d, want 0", "*", r)
 	}
 	y = -9223372036854775807
 	r = x * y
 	if r != 4294967296 {
-		t.Errorf("4294967296 * -9223372036854775807 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s -9223372036854775807 = %d, want 4294967296", "*", r)
 	}
 	y = -4294967296
 	r = x * y
 	if r != 0 {
-		t.Errorf("4294967296 * -4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s -4294967296 = %d, want 0", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -4294967296 {
-		t.Errorf("4294967296 * -1 = %d, want -4294967296", r)
+		t.Errorf("4294967296 %s -1 = %d, want -4294967296", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("4294967296 * 0 = %d, want 0", r)
+		t.Errorf("4294967296 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 4294967296 {
-		t.Errorf("4294967296 * 1 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 1 = %d, want 4294967296", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != 0 {
-		t.Errorf("4294967296 * 4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 0", "*", r)
 	}
 	y = 9223372036854775806
 	r = x * y
 	if r != -8589934592 {
-		t.Errorf("4294967296 * 9223372036854775806 = %d, want -8589934592", r)
+		t.Errorf("4294967296 %s 9223372036854775806 = %d, want -8589934592", "*", r)
 	}
 	y = 9223372036854775807
 	r = x * y
 	if r != -4294967296 {
-		t.Errorf("4294967296 * 9223372036854775807 = %d, want -4294967296", r)
+		t.Errorf("4294967296 %s 9223372036854775807 = %d, want -4294967296", "*", r)
 	}
 	x = 9223372036854775806
 	y = -9223372036854775808
 	r = x * y
 	if r != 0 {
-		t.Errorf("9223372036854775806 * -9223372036854775808 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s -9223372036854775808 = %d, want 0", "*", r)
 	}
 	y = -9223372036854775807
 	r = x * y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 * -9223372036854775807 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s -9223372036854775807 = %d, want 9223372036854775806", "*", r)
 	}
 	y = -4294967296
 	r = x * y
 	if r != 8589934592 {
-		t.Errorf("9223372036854775806 * -4294967296 = %d, want 8589934592", r)
+		t.Errorf("9223372036854775806 %s -4294967296 = %d, want 8589934592", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -9223372036854775806 {
-		t.Errorf("9223372036854775806 * -1 = %d, want -9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s -1 = %d, want -9223372036854775806", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("9223372036854775806 * 0 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 * 1 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want 9223372036854775806", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != -8589934592 {
-		t.Errorf("9223372036854775806 * 4294967296 = %d, want -8589934592", r)
+		t.Errorf("9223372036854775806 %s 4294967296 = %d, want -8589934592", "*", r)
 	}
 	y = 9223372036854775806
 	r = x * y
 	if r != 4 {
-		t.Errorf("9223372036854775806 * 9223372036854775806 = %d, want 4", r)
+		t.Errorf("9223372036854775806 %s 9223372036854775806 = %d, want 4", "*", r)
 	}
 	y = 9223372036854775807
 	r = x * y
 	if r != -9223372036854775806 {
-		t.Errorf("9223372036854775806 * 9223372036854775807 = %d, want -9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 9223372036854775807 = %d, want -9223372036854775806", "*", r)
 	}
 	x = 9223372036854775807
 	y = -9223372036854775808
 	r = x * y
 	if r != -9223372036854775808 {
-		t.Errorf("9223372036854775807 * -9223372036854775808 = %d, want -9223372036854775808", r)
+		t.Errorf("9223372036854775807 %s -9223372036854775808 = %d, want -9223372036854775808", "*", r)
 	}
 	y = -9223372036854775807
 	r = x * y
 	if r != -1 {
-		t.Errorf("9223372036854775807 * -9223372036854775807 = %d, want -1", r)
+		t.Errorf("9223372036854775807 %s -9223372036854775807 = %d, want -1", "*", r)
 	}
 	y = -4294967296
 	r = x * y
 	if r != 4294967296 {
-		t.Errorf("9223372036854775807 * -4294967296 = %d, want 4294967296", r)
+		t.Errorf("9223372036854775807 %s -4294967296 = %d, want 4294967296", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -9223372036854775807 {
-		t.Errorf("9223372036854775807 * -1 = %d, want -9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s -1 = %d, want -9223372036854775807", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("9223372036854775807 * 0 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 * 1 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want 9223372036854775807", "*", r)
 	}
 	y = 4294967296
 	r = x * y
 	if r != -4294967296 {
-		t.Errorf("9223372036854775807 * 4294967296 = %d, want -4294967296", r)
+		t.Errorf("9223372036854775807 %s 4294967296 = %d, want -4294967296", "*", r)
 	}
 	y = 9223372036854775806
 	r = x * y
 	if r != -9223372036854775806 {
-		t.Errorf("9223372036854775807 * 9223372036854775806 = %d, want -9223372036854775806", r)
+		t.Errorf("9223372036854775807 %s 9223372036854775806 = %d, want -9223372036854775806", "*", r)
 	}
 	y = 9223372036854775807
 	r = x * y
 	if r != 1 {
-		t.Errorf("9223372036854775807 * 9223372036854775807 = %d, want 1", r)
+		t.Errorf("9223372036854775807 %s 9223372036854775807 = %d, want 1", "*", r)
 	}
 }
 func TestConstFoldint64mod(t *testing.T) {
@@ -2026,370 +2026,370 @@ func TestConstFoldint64mod(t *testing.T) {
 	y = -9223372036854775808
 	r = x % y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 % -9223372036854775808 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s -9223372036854775808 = %d, want 0", "%", r)
 	}
 	y = -9223372036854775807
 	r = x % y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 % -9223372036854775807 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s -9223372036854775807 = %d, want -1", "%", r)
 	}
 	y = -4294967296
 	r = x % y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 % -4294967296 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s -4294967296 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 % -1 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 % 1 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 % 4294967296 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 4294967296 = %d, want 0", "%", r)
 	}
 	y = 9223372036854775806
 	r = x % y
 	if r != -2 {
-		t.Errorf("-9223372036854775808 % 9223372036854775806 = %d, want -2", r)
+		t.Errorf("-9223372036854775808 %s 9223372036854775806 = %d, want -2", "%", r)
 	}
 	y = 9223372036854775807
 	r = x % y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 % 9223372036854775807 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s 9223372036854775807 = %d, want -1", "%", r)
 	}
 	x = -9223372036854775807
 	y = -9223372036854775808
 	r = x % y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 % -9223372036854775808 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s -9223372036854775808 = %d, want -9223372036854775807", "%", r)
 	}
 	y = -9223372036854775807
 	r = x % y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 % -9223372036854775807 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s -9223372036854775807 = %d, want 0", "%", r)
 	}
 	y = -4294967296
 	r = x % y
 	if r != -4294967295 {
-		t.Errorf("-9223372036854775807 % -4294967296 = %d, want -4294967295", r)
+		t.Errorf("-9223372036854775807 %s -4294967296 = %d, want -4294967295", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 % -1 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 % 1 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != -4294967295 {
-		t.Errorf("-9223372036854775807 % 4294967296 = %d, want -4294967295", r)
+		t.Errorf("-9223372036854775807 %s 4294967296 = %d, want -4294967295", "%", r)
 	}
 	y = 9223372036854775806
 	r = x % y
 	if r != -1 {
-		t.Errorf("-9223372036854775807 % 9223372036854775806 = %d, want -1", r)
+		t.Errorf("-9223372036854775807 %s 9223372036854775806 = %d, want -1", "%", r)
 	}
 	y = 9223372036854775807
 	r = x % y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 % 9223372036854775807 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s 9223372036854775807 = %d, want 0", "%", r)
 	}
 	x = -4294967296
 	y = -9223372036854775808
 	r = x % y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 % -9223372036854775808 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s -9223372036854775808 = %d, want -4294967296", "%", r)
 	}
 	y = -9223372036854775807
 	r = x % y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 % -9223372036854775807 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s -9223372036854775807 = %d, want -4294967296", "%", r)
 	}
 	y = -4294967296
 	r = x % y
 	if r != 0 {
-		t.Errorf("-4294967296 % -4294967296 = %d, want 0", r)
+		t.Errorf("-4294967296 %s -4294967296 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-4294967296 % -1 = %d, want 0", r)
+		t.Errorf("-4294967296 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-4294967296 % 1 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 0 {
-		t.Errorf("-4294967296 % 4294967296 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 4294967296 = %d, want 0", "%", r)
 	}
 	y = 9223372036854775806
 	r = x % y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 % 9223372036854775806 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 9223372036854775806 = %d, want -4294967296", "%", r)
 	}
 	y = 9223372036854775807
 	r = x % y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 % 9223372036854775807 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 9223372036854775807 = %d, want -4294967296", "%", r)
 	}
 	x = -1
 	y = -9223372036854775808
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % -9223372036854775808 = %d, want -1", r)
+		t.Errorf("-1 %s -9223372036854775808 = %d, want -1", "%", r)
 	}
 	y = -9223372036854775807
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % -9223372036854775807 = %d, want -1", r)
+		t.Errorf("-1 %s -9223372036854775807 = %d, want -1", "%", r)
 	}
 	y = -4294967296
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % -4294967296 = %d, want -1", r)
+		t.Errorf("-1 %s -4294967296 = %d, want -1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-1 % -1 = %d, want 0", r)
+		t.Errorf("-1 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-1 % 1 = %d, want 0", r)
+		t.Errorf("-1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % 4294967296 = %d, want -1", r)
+		t.Errorf("-1 %s 4294967296 = %d, want -1", "%", r)
 	}
 	y = 9223372036854775806
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % 9223372036854775806 = %d, want -1", r)
+		t.Errorf("-1 %s 9223372036854775806 = %d, want -1", "%", r)
 	}
 	y = 9223372036854775807
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % 9223372036854775807 = %d, want -1", r)
+		t.Errorf("-1 %s 9223372036854775807 = %d, want -1", "%", r)
 	}
 	x = 0
 	y = -9223372036854775808
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -9223372036854775808 = %d, want 0", r)
+		t.Errorf("0 %s -9223372036854775808 = %d, want 0", "%", r)
 	}
 	y = -9223372036854775807
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -9223372036854775807 = %d, want 0", r)
+		t.Errorf("0 %s -9223372036854775807 = %d, want 0", "%", r)
 	}
 	y = -4294967296
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -4294967296 = %d, want 0", r)
+		t.Errorf("0 %s -4294967296 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "%", r)
 	}
 	y = 9223372036854775806
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 9223372036854775806 = %d, want 0", r)
+		t.Errorf("0 %s 9223372036854775806 = %d, want 0", "%", r)
 	}
 	y = 9223372036854775807
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 9223372036854775807 = %d, want 0", r)
+		t.Errorf("0 %s 9223372036854775807 = %d, want 0", "%", r)
 	}
 	x = 1
 	y = -9223372036854775808
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % -9223372036854775808 = %d, want 1", r)
+		t.Errorf("1 %s -9223372036854775808 = %d, want 1", "%", r)
 	}
 	y = -9223372036854775807
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % -9223372036854775807 = %d, want 1", r)
+		t.Errorf("1 %s -9223372036854775807 = %d, want 1", "%", r)
 	}
 	y = -4294967296
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % -4294967296 = %d, want 1", r)
+		t.Errorf("1 %s -4294967296 = %d, want 1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % -1 = %d, want 0", r)
+		t.Errorf("1 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 4294967296 = %d, want 1", r)
+		t.Errorf("1 %s 4294967296 = %d, want 1", "%", r)
 	}
 	y = 9223372036854775806
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 9223372036854775806 = %d, want 1", r)
+		t.Errorf("1 %s 9223372036854775806 = %d, want 1", "%", r)
 	}
 	y = 9223372036854775807
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 9223372036854775807 = %d, want 1", r)
+		t.Errorf("1 %s 9223372036854775807 = %d, want 1", "%", r)
 	}
 	x = 4294967296
 	y = -9223372036854775808
 	r = x % y
 	if r != 4294967296 {
-		t.Errorf("4294967296 % -9223372036854775808 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s -9223372036854775808 = %d, want 4294967296", "%", r)
 	}
 	y = -9223372036854775807
 	r = x % y
 	if r != 4294967296 {
-		t.Errorf("4294967296 % -9223372036854775807 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s -9223372036854775807 = %d, want 4294967296", "%", r)
 	}
 	y = -4294967296
 	r = x % y
 	if r != 0 {
-		t.Errorf("4294967296 % -4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s -4294967296 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("4294967296 % -1 = %d, want 0", r)
+		t.Errorf("4294967296 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("4294967296 % 1 = %d, want 0", r)
+		t.Errorf("4294967296 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 0 {
-		t.Errorf("4294967296 % 4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 0", "%", r)
 	}
 	y = 9223372036854775806
 	r = x % y
 	if r != 4294967296 {
-		t.Errorf("4294967296 % 9223372036854775806 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 9223372036854775806 = %d, want 4294967296", "%", r)
 	}
 	y = 9223372036854775807
 	r = x % y
 	if r != 4294967296 {
-		t.Errorf("4294967296 % 9223372036854775807 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 9223372036854775807 = %d, want 4294967296", "%", r)
 	}
 	x = 9223372036854775806
 	y = -9223372036854775808
 	r = x % y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 % -9223372036854775808 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s -9223372036854775808 = %d, want 9223372036854775806", "%", r)
 	}
 	y = -9223372036854775807
 	r = x % y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 % -9223372036854775807 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s -9223372036854775807 = %d, want 9223372036854775806", "%", r)
 	}
 	y = -4294967296
 	r = x % y
 	if r != 4294967294 {
-		t.Errorf("9223372036854775806 % -4294967296 = %d, want 4294967294", r)
+		t.Errorf("9223372036854775806 %s -4294967296 = %d, want 4294967294", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("9223372036854775806 % -1 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("9223372036854775806 % 1 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 4294967294 {
-		t.Errorf("9223372036854775806 % 4294967296 = %d, want 4294967294", r)
+		t.Errorf("9223372036854775806 %s 4294967296 = %d, want 4294967294", "%", r)
 	}
 	y = 9223372036854775806
 	r = x % y
 	if r != 0 {
-		t.Errorf("9223372036854775806 % 9223372036854775806 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 9223372036854775806 = %d, want 0", "%", r)
 	}
 	y = 9223372036854775807
 	r = x % y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 % 9223372036854775807 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 9223372036854775807 = %d, want 9223372036854775806", "%", r)
 	}
 	x = 9223372036854775807
 	y = -9223372036854775808
 	r = x % y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 % -9223372036854775808 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s -9223372036854775808 = %d, want 9223372036854775807", "%", r)
 	}
 	y = -9223372036854775807
 	r = x % y
 	if r != 0 {
-		t.Errorf("9223372036854775807 % -9223372036854775807 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s -9223372036854775807 = %d, want 0", "%", r)
 	}
 	y = -4294967296
 	r = x % y
 	if r != 4294967295 {
-		t.Errorf("9223372036854775807 % -4294967296 = %d, want 4294967295", r)
+		t.Errorf("9223372036854775807 %s -4294967296 = %d, want 4294967295", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("9223372036854775807 % -1 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("9223372036854775807 % 1 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967296
 	r = x % y
 	if r != 4294967295 {
-		t.Errorf("9223372036854775807 % 4294967296 = %d, want 4294967295", r)
+		t.Errorf("9223372036854775807 %s 4294967296 = %d, want 4294967295", "%", r)
 	}
 	y = 9223372036854775806
 	r = x % y
 	if r != 1 {
-		t.Errorf("9223372036854775807 % 9223372036854775806 = %d, want 1", r)
+		t.Errorf("9223372036854775807 %s 9223372036854775806 = %d, want 1", "%", r)
 	}
 	y = 9223372036854775807
 	r = x % y
 	if r != 0 {
-		t.Errorf("9223372036854775807 % 9223372036854775807 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 9223372036854775807 = %d, want 0", "%", r)
 	}
 }
 func TestConstFolduint32add(t *testing.T) {
@@ -2398,49 +2398,49 @@ func TestConstFolduint32add(t *testing.T) {
 	y = 0
 	r = x + y
 	if r != 0 {
-		t.Errorf("0 + 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 1 {
-		t.Errorf("0 + 1 = %d, want 1", r)
+		t.Errorf("0 %s 1 = %d, want 1", "+", r)
 	}
 	y = 4294967295
 	r = x + y
 	if r != 4294967295 {
-		t.Errorf("0 + 4294967295 = %d, want 4294967295", r)
+		t.Errorf("0 %s 4294967295 = %d, want 4294967295", "+", r)
 	}
 	x = 1
 	y = 0
 	r = x + y
 	if r != 1 {
-		t.Errorf("1 + 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 2 {
-		t.Errorf("1 + 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "+", r)
 	}
 	y = 4294967295
 	r = x + y
 	if r != 0 {
-		t.Errorf("1 + 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", "+", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x + y
 	if r != 4294967295 {
-		t.Errorf("4294967295 + 0 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 0 = %d, want 4294967295", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 0 {
-		t.Errorf("4294967295 + 1 = %d, want 0", r)
+		t.Errorf("4294967295 %s 1 = %d, want 0", "+", r)
 	}
 	y = 4294967295
 	r = x + y
 	if r != 4294967294 {
-		t.Errorf("4294967295 + 4294967295 = %d, want 4294967294", r)
+		t.Errorf("4294967295 %s 4294967295 = %d, want 4294967294", "+", r)
 	}
 }
 func TestConstFolduint32sub(t *testing.T) {
@@ -2449,49 +2449,49 @@ func TestConstFolduint32sub(t *testing.T) {
 	y = 0
 	r = x - y
 	if r != 0 {
-		t.Errorf("0 - 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 4294967295 {
-		t.Errorf("0 - 1 = %d, want 4294967295", r)
+		t.Errorf("0 %s 1 = %d, want 4294967295", "-", r)
 	}
 	y = 4294967295
 	r = x - y
 	if r != 1 {
-		t.Errorf("0 - 4294967295 = %d, want 1", r)
+		t.Errorf("0 %s 4294967295 = %d, want 1", "-", r)
 	}
 	x = 1
 	y = 0
 	r = x - y
 	if r != 1 {
-		t.Errorf("1 - 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 0 {
-		t.Errorf("1 - 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "-", r)
 	}
 	y = 4294967295
 	r = x - y
 	if r != 2 {
-		t.Errorf("1 - 4294967295 = %d, want 2", r)
+		t.Errorf("1 %s 4294967295 = %d, want 2", "-", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x - y
 	if r != 4294967295 {
-		t.Errorf("4294967295 - 0 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 0 = %d, want 4294967295", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 4294967294 {
-		t.Errorf("4294967295 - 1 = %d, want 4294967294", r)
+		t.Errorf("4294967295 %s 1 = %d, want 4294967294", "-", r)
 	}
 	y = 4294967295
 	r = x - y
 	if r != 0 {
-		t.Errorf("4294967295 - 4294967295 = %d, want 0", r)
+		t.Errorf("4294967295 %s 4294967295 = %d, want 0", "-", r)
 	}
 }
 func TestConstFolduint32div(t *testing.T) {
@@ -2500,34 +2500,34 @@ func TestConstFolduint32div(t *testing.T) {
 	y = 1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "/", r)
 	}
 	y = 4294967295
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "/", r)
 	}
 	x = 1
 	y = 1
 	r = x / y
 	if r != 1 {
-		t.Errorf("1 / 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "/", r)
 	}
 	y = 4294967295
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", "/", r)
 	}
 	x = 4294967295
 	y = 1
 	r = x / y
 	if r != 4294967295 {
-		t.Errorf("4294967295 / 1 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 1 = %d, want 4294967295", "/", r)
 	}
 	y = 4294967295
 	r = x / y
 	if r != 1 {
-		t.Errorf("4294967295 / 4294967295 = %d, want 1", r)
+		t.Errorf("4294967295 %s 4294967295 = %d, want 1", "/", r)
 	}
 }
 func TestConstFolduint32mul(t *testing.T) {
@@ -2536,49 +2536,49 @@ func TestConstFolduint32mul(t *testing.T) {
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "*", r)
 	}
 	y = 4294967295
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "*", r)
 	}
 	x = 1
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("1 * 0 = %d, want 0", r)
+		t.Errorf("1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 1 {
-		t.Errorf("1 * 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "*", r)
 	}
 	y = 4294967295
 	r = x * y
 	if r != 4294967295 {
-		t.Errorf("1 * 4294967295 = %d, want 4294967295", r)
+		t.Errorf("1 %s 4294967295 = %d, want 4294967295", "*", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("4294967295 * 0 = %d, want 0", r)
+		t.Errorf("4294967295 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 4294967295 {
-		t.Errorf("4294967295 * 1 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 1 = %d, want 4294967295", "*", r)
 	}
 	y = 4294967295
 	r = x * y
 	if r != 1 {
-		t.Errorf("4294967295 * 4294967295 = %d, want 1", r)
+		t.Errorf("4294967295 %s 4294967295 = %d, want 1", "*", r)
 	}
 }
 func TestConstFolduint32mod(t *testing.T) {
@@ -2587,34 +2587,34 @@ func TestConstFolduint32mod(t *testing.T) {
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967295
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "%", r)
 	}
 	x = 1
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967295
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 4294967295 = %d, want 1", r)
+		t.Errorf("1 %s 4294967295 = %d, want 1", "%", r)
 	}
 	x = 4294967295
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("4294967295 % 1 = %d, want 0", r)
+		t.Errorf("4294967295 %s 1 = %d, want 0", "%", r)
 	}
 	y = 4294967295
 	r = x % y
 	if r != 0 {
-		t.Errorf("4294967295 % 4294967295 = %d, want 0", r)
+		t.Errorf("4294967295 %s 4294967295 = %d, want 0", "%", r)
 	}
 }
 func TestConstFoldint32add(t *testing.T) {
@@ -2623,187 +2623,187 @@ func TestConstFoldint32add(t *testing.T) {
 	y = -2147483648
 	r = x + y
 	if r != 0 {
-		t.Errorf("-2147483648 + -2147483648 = %d, want 0", r)
+		t.Errorf("-2147483648 %s -2147483648 = %d, want 0", "+", r)
 	}
 	y = -2147483647
 	r = x + y
 	if r != 1 {
-		t.Errorf("-2147483648 + -2147483647 = %d, want 1", r)
+		t.Errorf("-2147483648 %s -2147483647 = %d, want 1", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 2147483647 {
-		t.Errorf("-2147483648 + -1 = %d, want 2147483647", r)
+		t.Errorf("-2147483648 %s -1 = %d, want 2147483647", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 + 0 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 0 = %d, want -2147483648", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -2147483647 {
-		t.Errorf("-2147483648 + 1 = %d, want -2147483647", r)
+		t.Errorf("-2147483648 %s 1 = %d, want -2147483647", "+", r)
 	}
 	y = 2147483647
 	r = x + y
 	if r != -1 {
-		t.Errorf("-2147483648 + 2147483647 = %d, want -1", r)
+		t.Errorf("-2147483648 %s 2147483647 = %d, want -1", "+", r)
 	}
 	x = -2147483647
 	y = -2147483648
 	r = x + y
 	if r != 1 {
-		t.Errorf("-2147483647 + -2147483648 = %d, want 1", r)
+		t.Errorf("-2147483647 %s -2147483648 = %d, want 1", "+", r)
 	}
 	y = -2147483647
 	r = x + y
 	if r != 2 {
-		t.Errorf("-2147483647 + -2147483647 = %d, want 2", r)
+		t.Errorf("-2147483647 %s -2147483647 = %d, want 2", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -2147483648 {
-		t.Errorf("-2147483647 + -1 = %d, want -2147483648", r)
+		t.Errorf("-2147483647 %s -1 = %d, want -2147483648", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 + 0 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 0 = %d, want -2147483647", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -2147483646 {
-		t.Errorf("-2147483647 + 1 = %d, want -2147483646", r)
+		t.Errorf("-2147483647 %s 1 = %d, want -2147483646", "+", r)
 	}
 	y = 2147483647
 	r = x + y
 	if r != 0 {
-		t.Errorf("-2147483647 + 2147483647 = %d, want 0", r)
+		t.Errorf("-2147483647 %s 2147483647 = %d, want 0", "+", r)
 	}
 	x = -1
 	y = -2147483648
 	r = x + y
 	if r != 2147483647 {
-		t.Errorf("-1 + -2147483648 = %d, want 2147483647", r)
+		t.Errorf("-1 %s -2147483648 = %d, want 2147483647", "+", r)
 	}
 	y = -2147483647
 	r = x + y
 	if r != -2147483648 {
-		t.Errorf("-1 + -2147483647 = %d, want -2147483648", r)
+		t.Errorf("-1 %s -2147483647 = %d, want -2147483648", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -2 {
-		t.Errorf("-1 + -1 = %d, want -2", r)
+		t.Errorf("-1 %s -1 = %d, want -2", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -1 {
-		t.Errorf("-1 + 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 0 {
-		t.Errorf("-1 + 1 = %d, want 0", r)
+		t.Errorf("-1 %s 1 = %d, want 0", "+", r)
 	}
 	y = 2147483647
 	r = x + y
 	if r != 2147483646 {
-		t.Errorf("-1 + 2147483647 = %d, want 2147483646", r)
+		t.Errorf("-1 %s 2147483647 = %d, want 2147483646", "+", r)
 	}
 	x = 0
 	y = -2147483648
 	r = x + y
 	if r != -2147483648 {
-		t.Errorf("0 + -2147483648 = %d, want -2147483648", r)
+		t.Errorf("0 %s -2147483648 = %d, want -2147483648", "+", r)
 	}
 	y = -2147483647
 	r = x + y
 	if r != -2147483647 {
-		t.Errorf("0 + -2147483647 = %d, want -2147483647", r)
+		t.Errorf("0 %s -2147483647 = %d, want -2147483647", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -1 {
-		t.Errorf("0 + -1 = %d, want -1", r)
+		t.Errorf("0 %s -1 = %d, want -1", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 0 {
-		t.Errorf("0 + 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 1 {
-		t.Errorf("0 + 1 = %d, want 1", r)
+		t.Errorf("0 %s 1 = %d, want 1", "+", r)
 	}
 	y = 2147483647
 	r = x + y
 	if r != 2147483647 {
-		t.Errorf("0 + 2147483647 = %d, want 2147483647", r)
+		t.Errorf("0 %s 2147483647 = %d, want 2147483647", "+", r)
 	}
 	x = 1
 	y = -2147483648
 	r = x + y
 	if r != -2147483647 {
-		t.Errorf("1 + -2147483648 = %d, want -2147483647", r)
+		t.Errorf("1 %s -2147483648 = %d, want -2147483647", "+", r)
 	}
 	y = -2147483647
 	r = x + y
 	if r != -2147483646 {
-		t.Errorf("1 + -2147483647 = %d, want -2147483646", r)
+		t.Errorf("1 %s -2147483647 = %d, want -2147483646", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 0 {
-		t.Errorf("1 + -1 = %d, want 0", r)
+		t.Errorf("1 %s -1 = %d, want 0", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 1 {
-		t.Errorf("1 + 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 2 {
-		t.Errorf("1 + 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "+", r)
 	}
 	y = 2147483647
 	r = x + y
 	if r != -2147483648 {
-		t.Errorf("1 + 2147483647 = %d, want -2147483648", r)
+		t.Errorf("1 %s 2147483647 = %d, want -2147483648", "+", r)
 	}
 	x = 2147483647
 	y = -2147483648
 	r = x + y
 	if r != -1 {
-		t.Errorf("2147483647 + -2147483648 = %d, want -1", r)
+		t.Errorf("2147483647 %s -2147483648 = %d, want -1", "+", r)
 	}
 	y = -2147483647
 	r = x + y
 	if r != 0 {
-		t.Errorf("2147483647 + -2147483647 = %d, want 0", r)
+		t.Errorf("2147483647 %s -2147483647 = %d, want 0", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 2147483646 {
-		t.Errorf("2147483647 + -1 = %d, want 2147483646", r)
+		t.Errorf("2147483647 %s -1 = %d, want 2147483646", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 2147483647 {
-		t.Errorf("2147483647 + 0 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 0 = %d, want 2147483647", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -2147483648 {
-		t.Errorf("2147483647 + 1 = %d, want -2147483648", r)
+		t.Errorf("2147483647 %s 1 = %d, want -2147483648", "+", r)
 	}
 	y = 2147483647
 	r = x + y
 	if r != -2 {
-		t.Errorf("2147483647 + 2147483647 = %d, want -2", r)
+		t.Errorf("2147483647 %s 2147483647 = %d, want -2", "+", r)
 	}
 }
 func TestConstFoldint32sub(t *testing.T) {
@@ -2812,187 +2812,187 @@ func TestConstFoldint32sub(t *testing.T) {
 	y = -2147483648
 	r = x - y
 	if r != 0 {
-		t.Errorf("-2147483648 - -2147483648 = %d, want 0", r)
+		t.Errorf("-2147483648 %s -2147483648 = %d, want 0", "-", r)
 	}
 	y = -2147483647
 	r = x - y
 	if r != -1 {
-		t.Errorf("-2147483648 - -2147483647 = %d, want -1", r)
+		t.Errorf("-2147483648 %s -2147483647 = %d, want -1", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -2147483647 {
-		t.Errorf("-2147483648 - -1 = %d, want -2147483647", r)
+		t.Errorf("-2147483648 %s -1 = %d, want -2147483647", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 - 0 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 0 = %d, want -2147483648", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 2147483647 {
-		t.Errorf("-2147483648 - 1 = %d, want 2147483647", r)
+		t.Errorf("-2147483648 %s 1 = %d, want 2147483647", "-", r)
 	}
 	y = 2147483647
 	r = x - y
 	if r != 1 {
-		t.Errorf("-2147483648 - 2147483647 = %d, want 1", r)
+		t.Errorf("-2147483648 %s 2147483647 = %d, want 1", "-", r)
 	}
 	x = -2147483647
 	y = -2147483648
 	r = x - y
 	if r != 1 {
-		t.Errorf("-2147483647 - -2147483648 = %d, want 1", r)
+		t.Errorf("-2147483647 %s -2147483648 = %d, want 1", "-", r)
 	}
 	y = -2147483647
 	r = x - y
 	if r != 0 {
-		t.Errorf("-2147483647 - -2147483647 = %d, want 0", r)
+		t.Errorf("-2147483647 %s -2147483647 = %d, want 0", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -2147483646 {
-		t.Errorf("-2147483647 - -1 = %d, want -2147483646", r)
+		t.Errorf("-2147483647 %s -1 = %d, want -2147483646", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 - 0 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 0 = %d, want -2147483647", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -2147483648 {
-		t.Errorf("-2147483647 - 1 = %d, want -2147483648", r)
+		t.Errorf("-2147483647 %s 1 = %d, want -2147483648", "-", r)
 	}
 	y = 2147483647
 	r = x - y
 	if r != 2 {
-		t.Errorf("-2147483647 - 2147483647 = %d, want 2", r)
+		t.Errorf("-2147483647 %s 2147483647 = %d, want 2", "-", r)
 	}
 	x = -1
 	y = -2147483648
 	r = x - y
 	if r != 2147483647 {
-		t.Errorf("-1 - -2147483648 = %d, want 2147483647", r)
+		t.Errorf("-1 %s -2147483648 = %d, want 2147483647", "-", r)
 	}
 	y = -2147483647
 	r = x - y
 	if r != 2147483646 {
-		t.Errorf("-1 - -2147483647 = %d, want 2147483646", r)
+		t.Errorf("-1 %s -2147483647 = %d, want 2147483646", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 0 {
-		t.Errorf("-1 - -1 = %d, want 0", r)
+		t.Errorf("-1 %s -1 = %d, want 0", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -1 {
-		t.Errorf("-1 - 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -2 {
-		t.Errorf("-1 - 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "-", r)
 	}
 	y = 2147483647
 	r = x - y
 	if r != -2147483648 {
-		t.Errorf("-1 - 2147483647 = %d, want -2147483648", r)
+		t.Errorf("-1 %s 2147483647 = %d, want -2147483648", "-", r)
 	}
 	x = 0
 	y = -2147483648
 	r = x - y
 	if r != -2147483648 {
-		t.Errorf("0 - -2147483648 = %d, want -2147483648", r)
+		t.Errorf("0 %s -2147483648 = %d, want -2147483648", "-", r)
 	}
 	y = -2147483647
 	r = x - y
 	if r != 2147483647 {
-		t.Errorf("0 - -2147483647 = %d, want 2147483647", r)
+		t.Errorf("0 %s -2147483647 = %d, want 2147483647", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 1 {
-		t.Errorf("0 - -1 = %d, want 1", r)
+		t.Errorf("0 %s -1 = %d, want 1", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 0 {
-		t.Errorf("0 - 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -1 {
-		t.Errorf("0 - 1 = %d, want -1", r)
+		t.Errorf("0 %s 1 = %d, want -1", "-", r)
 	}
 	y = 2147483647
 	r = x - y
 	if r != -2147483647 {
-		t.Errorf("0 - 2147483647 = %d, want -2147483647", r)
+		t.Errorf("0 %s 2147483647 = %d, want -2147483647", "-", r)
 	}
 	x = 1
 	y = -2147483648
 	r = x - y
 	if r != -2147483647 {
-		t.Errorf("1 - -2147483648 = %d, want -2147483647", r)
+		t.Errorf("1 %s -2147483648 = %d, want -2147483647", "-", r)
 	}
 	y = -2147483647
 	r = x - y
 	if r != -2147483648 {
-		t.Errorf("1 - -2147483647 = %d, want -2147483648", r)
+		t.Errorf("1 %s -2147483647 = %d, want -2147483648", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 2 {
-		t.Errorf("1 - -1 = %d, want 2", r)
+		t.Errorf("1 %s -1 = %d, want 2", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 1 {
-		t.Errorf("1 - 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 0 {
-		t.Errorf("1 - 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "-", r)
 	}
 	y = 2147483647
 	r = x - y
 	if r != -2147483646 {
-		t.Errorf("1 - 2147483647 = %d, want -2147483646", r)
+		t.Errorf("1 %s 2147483647 = %d, want -2147483646", "-", r)
 	}
 	x = 2147483647
 	y = -2147483648
 	r = x - y
 	if r != -1 {
-		t.Errorf("2147483647 - -2147483648 = %d, want -1", r)
+		t.Errorf("2147483647 %s -2147483648 = %d, want -1", "-", r)
 	}
 	y = -2147483647
 	r = x - y
 	if r != -2 {
-		t.Errorf("2147483647 - -2147483647 = %d, want -2", r)
+		t.Errorf("2147483647 %s -2147483647 = %d, want -2", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -2147483648 {
-		t.Errorf("2147483647 - -1 = %d, want -2147483648", r)
+		t.Errorf("2147483647 %s -1 = %d, want -2147483648", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 2147483647 {
-		t.Errorf("2147483647 - 0 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 0 = %d, want 2147483647", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 2147483646 {
-		t.Errorf("2147483647 - 1 = %d, want 2147483646", r)
+		t.Errorf("2147483647 %s 1 = %d, want 2147483646", "-", r)
 	}
 	y = 2147483647
 	r = x - y
 	if r != 0 {
-		t.Errorf("2147483647 - 2147483647 = %d, want 0", r)
+		t.Errorf("2147483647 %s 2147483647 = %d, want 0", "-", r)
 	}
 }
 func TestConstFoldint32div(t *testing.T) {
@@ -3001,157 +3001,157 @@ func TestConstFoldint32div(t *testing.T) {
 	y = -2147483648
 	r = x / y
 	if r != 1 {
-		t.Errorf("-2147483648 / -2147483648 = %d, want 1", r)
+		t.Errorf("-2147483648 %s -2147483648 = %d, want 1", "/", r)
 	}
 	y = -2147483647
 	r = x / y
 	if r != 1 {
-		t.Errorf("-2147483648 / -2147483647 = %d, want 1", r)
+		t.Errorf("-2147483648 %s -2147483647 = %d, want 1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 / -1 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s -1 = %d, want -2147483648", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 / 1 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 1 = %d, want -2147483648", "/", r)
 	}
 	y = 2147483647
 	r = x / y
 	if r != -1 {
-		t.Errorf("-2147483648 / 2147483647 = %d, want -1", r)
+		t.Errorf("-2147483648 %s 2147483647 = %d, want -1", "/", r)
 	}
 	x = -2147483647
 	y = -2147483648
 	r = x / y
 	if r != 0 {
-		t.Errorf("-2147483647 / -2147483648 = %d, want 0", r)
+		t.Errorf("-2147483647 %s -2147483648 = %d, want 0", "/", r)
 	}
 	y = -2147483647
 	r = x / y
 	if r != 1 {
-		t.Errorf("-2147483647 / -2147483647 = %d, want 1", r)
+		t.Errorf("-2147483647 %s -2147483647 = %d, want 1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 2147483647 {
-		t.Errorf("-2147483647 / -1 = %d, want 2147483647", r)
+		t.Errorf("-2147483647 %s -1 = %d, want 2147483647", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 / 1 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 1 = %d, want -2147483647", "/", r)
 	}
 	y = 2147483647
 	r = x / y
 	if r != -1 {
-		t.Errorf("-2147483647 / 2147483647 = %d, want -1", r)
+		t.Errorf("-2147483647 %s 2147483647 = %d, want -1", "/", r)
 	}
 	x = -1
 	y = -2147483648
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / -2147483648 = %d, want 0", r)
+		t.Errorf("-1 %s -2147483648 = %d, want 0", "/", r)
 	}
 	y = -2147483647
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / -2147483647 = %d, want 0", r)
+		t.Errorf("-1 %s -2147483647 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 1 {
-		t.Errorf("-1 / -1 = %d, want 1", r)
+		t.Errorf("-1 %s -1 = %d, want 1", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -1 {
-		t.Errorf("-1 / 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", "/", r)
 	}
 	y = 2147483647
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / 2147483647 = %d, want 0", r)
+		t.Errorf("-1 %s 2147483647 = %d, want 0", "/", r)
 	}
 	x = 0
 	y = -2147483648
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -2147483648 = %d, want 0", r)
+		t.Errorf("0 %s -2147483648 = %d, want 0", "/", r)
 	}
 	y = -2147483647
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -2147483647 = %d, want 0", r)
+		t.Errorf("0 %s -2147483647 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "/", r)
 	}
 	y = 2147483647
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 2147483647 = %d, want 0", r)
+		t.Errorf("0 %s 2147483647 = %d, want 0", "/", r)
 	}
 	x = 1
 	y = -2147483648
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / -2147483648 = %d, want 0", r)
+		t.Errorf("1 %s -2147483648 = %d, want 0", "/", r)
 	}
 	y = -2147483647
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / -2147483647 = %d, want 0", r)
+		t.Errorf("1 %s -2147483647 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -1 {
-		t.Errorf("1 / -1 = %d, want -1", r)
+		t.Errorf("1 %s -1 = %d, want -1", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 1 {
-		t.Errorf("1 / 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "/", r)
 	}
 	y = 2147483647
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 2147483647 = %d, want 0", r)
+		t.Errorf("1 %s 2147483647 = %d, want 0", "/", r)
 	}
 	x = 2147483647
 	y = -2147483648
 	r = x / y
 	if r != 0 {
-		t.Errorf("2147483647 / -2147483648 = %d, want 0", r)
+		t.Errorf("2147483647 %s -2147483648 = %d, want 0", "/", r)
 	}
 	y = -2147483647
 	r = x / y
 	if r != -1 {
-		t.Errorf("2147483647 / -2147483647 = %d, want -1", r)
+		t.Errorf("2147483647 %s -2147483647 = %d, want -1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -2147483647 {
-		t.Errorf("2147483647 / -1 = %d, want -2147483647", r)
+		t.Errorf("2147483647 %s -1 = %d, want -2147483647", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 2147483647 {
-		t.Errorf("2147483647 / 1 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 1 = %d, want 2147483647", "/", r)
 	}
 	y = 2147483647
 	r = x / y
 	if r != 1 {
-		t.Errorf("2147483647 / 2147483647 = %d, want 1", r)
+		t.Errorf("2147483647 %s 2147483647 = %d, want 1", "/", r)
 	}
 }
 func TestConstFoldint32mul(t *testing.T) {
@@ -3160,187 +3160,187 @@ func TestConstFoldint32mul(t *testing.T) {
 	y = -2147483648
 	r = x * y
 	if r != 0 {
-		t.Errorf("-2147483648 * -2147483648 = %d, want 0", r)
+		t.Errorf("-2147483648 %s -2147483648 = %d, want 0", "*", r)
 	}
 	y = -2147483647
 	r = x * y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 * -2147483647 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s -2147483647 = %d, want -2147483648", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 * -1 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s -1 = %d, want -2147483648", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-2147483648 * 0 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 * 1 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 1 = %d, want -2147483648", "*", r)
 	}
 	y = 2147483647
 	r = x * y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 * 2147483647 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 2147483647 = %d, want -2147483648", "*", r)
 	}
 	x = -2147483647
 	y = -2147483648
 	r = x * y
 	if r != -2147483648 {
-		t.Errorf("-2147483647 * -2147483648 = %d, want -2147483648", r)
+		t.Errorf("-2147483647 %s -2147483648 = %d, want -2147483648", "*", r)
 	}
 	y = -2147483647
 	r = x * y
 	if r != 1 {
-		t.Errorf("-2147483647 * -2147483647 = %d, want 1", r)
+		t.Errorf("-2147483647 %s -2147483647 = %d, want 1", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 2147483647 {
-		t.Errorf("-2147483647 * -1 = %d, want 2147483647", r)
+		t.Errorf("-2147483647 %s -1 = %d, want 2147483647", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-2147483647 * 0 = %d, want 0", r)
+		t.Errorf("-2147483647 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 * 1 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 1 = %d, want -2147483647", "*", r)
 	}
 	y = 2147483647
 	r = x * y
 	if r != -1 {
-		t.Errorf("-2147483647 * 2147483647 = %d, want -1", r)
+		t.Errorf("-2147483647 %s 2147483647 = %d, want -1", "*", r)
 	}
 	x = -1
 	y = -2147483648
 	r = x * y
 	if r != -2147483648 {
-		t.Errorf("-1 * -2147483648 = %d, want -2147483648", r)
+		t.Errorf("-1 %s -2147483648 = %d, want -2147483648", "*", r)
 	}
 	y = -2147483647
 	r = x * y
 	if r != 2147483647 {
-		t.Errorf("-1 * -2147483647 = %d, want 2147483647", r)
+		t.Errorf("-1 %s -2147483647 = %d, want 2147483647", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 1 {
-		t.Errorf("-1 * -1 = %d, want 1", r)
+		t.Errorf("-1 %s -1 = %d, want 1", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-1 * 0 = %d, want 0", r)
+		t.Errorf("-1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -1 {
-		t.Errorf("-1 * 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", "*", r)
 	}
 	y = 2147483647
 	r = x * y
 	if r != -2147483647 {
-		t.Errorf("-1 * 2147483647 = %d, want -2147483647", r)
+		t.Errorf("-1 %s 2147483647 = %d, want -2147483647", "*", r)
 	}
 	x = 0
 	y = -2147483648
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -2147483648 = %d, want 0", r)
+		t.Errorf("0 %s -2147483648 = %d, want 0", "*", r)
 	}
 	y = -2147483647
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -2147483647 = %d, want 0", r)
+		t.Errorf("0 %s -2147483647 = %d, want 0", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "*", r)
 	}
 	y = 2147483647
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 2147483647 = %d, want 0", r)
+		t.Errorf("0 %s 2147483647 = %d, want 0", "*", r)
 	}
 	x = 1
 	y = -2147483648
 	r = x * y
 	if r != -2147483648 {
-		t.Errorf("1 * -2147483648 = %d, want -2147483648", r)
+		t.Errorf("1 %s -2147483648 = %d, want -2147483648", "*", r)
 	}
 	y = -2147483647
 	r = x * y
 	if r != -2147483647 {
-		t.Errorf("1 * -2147483647 = %d, want -2147483647", r)
+		t.Errorf("1 %s -2147483647 = %d, want -2147483647", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -1 {
-		t.Errorf("1 * -1 = %d, want -1", r)
+		t.Errorf("1 %s -1 = %d, want -1", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("1 * 0 = %d, want 0", r)
+		t.Errorf("1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 1 {
-		t.Errorf("1 * 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "*", r)
 	}
 	y = 2147483647
 	r = x * y
 	if r != 2147483647 {
-		t.Errorf("1 * 2147483647 = %d, want 2147483647", r)
+		t.Errorf("1 %s 2147483647 = %d, want 2147483647", "*", r)
 	}
 	x = 2147483647
 	y = -2147483648
 	r = x * y
 	if r != -2147483648 {
-		t.Errorf("2147483647 * -2147483648 = %d, want -2147483648", r)
+		t.Errorf("2147483647 %s -2147483648 = %d, want -2147483648", "*", r)
 	}
 	y = -2147483647
 	r = x * y
 	if r != -1 {
-		t.Errorf("2147483647 * -2147483647 = %d, want -1", r)
+		t.Errorf("2147483647 %s -2147483647 = %d, want -1", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -2147483647 {
-		t.Errorf("2147483647 * -1 = %d, want -2147483647", r)
+		t.Errorf("2147483647 %s -1 = %d, want -2147483647", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("2147483647 * 0 = %d, want 0", r)
+		t.Errorf("2147483647 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 2147483647 {
-		t.Errorf("2147483647 * 1 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 1 = %d, want 2147483647", "*", r)
 	}
 	y = 2147483647
 	r = x * y
 	if r != 1 {
-		t.Errorf("2147483647 * 2147483647 = %d, want 1", r)
+		t.Errorf("2147483647 %s 2147483647 = %d, want 1", "*", r)
 	}
 }
 func TestConstFoldint32mod(t *testing.T) {
@@ -3349,157 +3349,157 @@ func TestConstFoldint32mod(t *testing.T) {
 	y = -2147483648
 	r = x % y
 	if r != 0 {
-		t.Errorf("-2147483648 % -2147483648 = %d, want 0", r)
+		t.Errorf("-2147483648 %s -2147483648 = %d, want 0", "%", r)
 	}
 	y = -2147483647
 	r = x % y
 	if r != -1 {
-		t.Errorf("-2147483648 % -2147483647 = %d, want -1", r)
+		t.Errorf("-2147483648 %s -2147483647 = %d, want -1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-2147483648 % -1 = %d, want 0", r)
+		t.Errorf("-2147483648 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-2147483648 % 1 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 1 = %d, want 0", "%", r)
 	}
 	y = 2147483647
 	r = x % y
 	if r != -1 {
-		t.Errorf("-2147483648 % 2147483647 = %d, want -1", r)
+		t.Errorf("-2147483648 %s 2147483647 = %d, want -1", "%", r)
 	}
 	x = -2147483647
 	y = -2147483648
 	r = x % y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 % -2147483648 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s -2147483648 = %d, want -2147483647", "%", r)
 	}
 	y = -2147483647
 	r = x % y
 	if r != 0 {
-		t.Errorf("-2147483647 % -2147483647 = %d, want 0", r)
+		t.Errorf("-2147483647 %s -2147483647 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-2147483647 % -1 = %d, want 0", r)
+		t.Errorf("-2147483647 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-2147483647 % 1 = %d, want 0", r)
+		t.Errorf("-2147483647 %s 1 = %d, want 0", "%", r)
 	}
 	y = 2147483647
 	r = x % y
 	if r != 0 {
-		t.Errorf("-2147483647 % 2147483647 = %d, want 0", r)
+		t.Errorf("-2147483647 %s 2147483647 = %d, want 0", "%", r)
 	}
 	x = -1
 	y = -2147483648
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % -2147483648 = %d, want -1", r)
+		t.Errorf("-1 %s -2147483648 = %d, want -1", "%", r)
 	}
 	y = -2147483647
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % -2147483647 = %d, want -1", r)
+		t.Errorf("-1 %s -2147483647 = %d, want -1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-1 % -1 = %d, want 0", r)
+		t.Errorf("-1 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-1 % 1 = %d, want 0", r)
+		t.Errorf("-1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 2147483647
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % 2147483647 = %d, want -1", r)
+		t.Errorf("-1 %s 2147483647 = %d, want -1", "%", r)
 	}
 	x = 0
 	y = -2147483648
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -2147483648 = %d, want 0", r)
+		t.Errorf("0 %s -2147483648 = %d, want 0", "%", r)
 	}
 	y = -2147483647
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -2147483647 = %d, want 0", r)
+		t.Errorf("0 %s -2147483647 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "%", r)
 	}
 	y = 2147483647
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 2147483647 = %d, want 0", r)
+		t.Errorf("0 %s 2147483647 = %d, want 0", "%", r)
 	}
 	x = 1
 	y = -2147483648
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % -2147483648 = %d, want 1", r)
+		t.Errorf("1 %s -2147483648 = %d, want 1", "%", r)
 	}
 	y = -2147483647
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % -2147483647 = %d, want 1", r)
+		t.Errorf("1 %s -2147483647 = %d, want 1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % -1 = %d, want 0", r)
+		t.Errorf("1 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 2147483647
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 2147483647 = %d, want 1", r)
+		t.Errorf("1 %s 2147483647 = %d, want 1", "%", r)
 	}
 	x = 2147483647
 	y = -2147483648
 	r = x % y
 	if r != 2147483647 {
-		t.Errorf("2147483647 % -2147483648 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s -2147483648 = %d, want 2147483647", "%", r)
 	}
 	y = -2147483647
 	r = x % y
 	if r != 0 {
-		t.Errorf("2147483647 % -2147483647 = %d, want 0", r)
+		t.Errorf("2147483647 %s -2147483647 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("2147483647 % -1 = %d, want 0", r)
+		t.Errorf("2147483647 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("2147483647 % 1 = %d, want 0", r)
+		t.Errorf("2147483647 %s 1 = %d, want 0", "%", r)
 	}
 	y = 2147483647
 	r = x % y
 	if r != 0 {
-		t.Errorf("2147483647 % 2147483647 = %d, want 0", r)
+		t.Errorf("2147483647 %s 2147483647 = %d, want 0", "%", r)
 	}
 }
 func TestConstFolduint16add(t *testing.T) {
@@ -3508,49 +3508,49 @@ func TestConstFolduint16add(t *testing.T) {
 	y = 0
 	r = x + y
 	if r != 0 {
-		t.Errorf("0 + 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 1 {
-		t.Errorf("0 + 1 = %d, want 1", r)
+		t.Errorf("0 %s 1 = %d, want 1", "+", r)
 	}
 	y = 65535
 	r = x + y
 	if r != 65535 {
-		t.Errorf("0 + 65535 = %d, want 65535", r)
+		t.Errorf("0 %s 65535 = %d, want 65535", "+", r)
 	}
 	x = 1
 	y = 0
 	r = x + y
 	if r != 1 {
-		t.Errorf("1 + 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 2 {
-		t.Errorf("1 + 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "+", r)
 	}
 	y = 65535
 	r = x + y
 	if r != 0 {
-		t.Errorf("1 + 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", "+", r)
 	}
 	x = 65535
 	y = 0
 	r = x + y
 	if r != 65535 {
-		t.Errorf("65535 + 0 = %d, want 65535", r)
+		t.Errorf("65535 %s 0 = %d, want 65535", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 0 {
-		t.Errorf("65535 + 1 = %d, want 0", r)
+		t.Errorf("65535 %s 1 = %d, want 0", "+", r)
 	}
 	y = 65535
 	r = x + y
 	if r != 65534 {
-		t.Errorf("65535 + 65535 = %d, want 65534", r)
+		t.Errorf("65535 %s 65535 = %d, want 65534", "+", r)
 	}
 }
 func TestConstFolduint16sub(t *testing.T) {
@@ -3559,49 +3559,49 @@ func TestConstFolduint16sub(t *testing.T) {
 	y = 0
 	r = x - y
 	if r != 0 {
-		t.Errorf("0 - 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 65535 {
-		t.Errorf("0 - 1 = %d, want 65535", r)
+		t.Errorf("0 %s 1 = %d, want 65535", "-", r)
 	}
 	y = 65535
 	r = x - y
 	if r != 1 {
-		t.Errorf("0 - 65535 = %d, want 1", r)
+		t.Errorf("0 %s 65535 = %d, want 1", "-", r)
 	}
 	x = 1
 	y = 0
 	r = x - y
 	if r != 1 {
-		t.Errorf("1 - 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 0 {
-		t.Errorf("1 - 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "-", r)
 	}
 	y = 65535
 	r = x - y
 	if r != 2 {
-		t.Errorf("1 - 65535 = %d, want 2", r)
+		t.Errorf("1 %s 65535 = %d, want 2", "-", r)
 	}
 	x = 65535
 	y = 0
 	r = x - y
 	if r != 65535 {
-		t.Errorf("65535 - 0 = %d, want 65535", r)
+		t.Errorf("65535 %s 0 = %d, want 65535", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 65534 {
-		t.Errorf("65535 - 1 = %d, want 65534", r)
+		t.Errorf("65535 %s 1 = %d, want 65534", "-", r)
 	}
 	y = 65535
 	r = x - y
 	if r != 0 {
-		t.Errorf("65535 - 65535 = %d, want 0", r)
+		t.Errorf("65535 %s 65535 = %d, want 0", "-", r)
 	}
 }
 func TestConstFolduint16div(t *testing.T) {
@@ -3610,34 +3610,34 @@ func TestConstFolduint16div(t *testing.T) {
 	y = 1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "/", r)
 	}
 	y = 65535
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "/", r)
 	}
 	x = 1
 	y = 1
 	r = x / y
 	if r != 1 {
-		t.Errorf("1 / 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "/", r)
 	}
 	y = 65535
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", "/", r)
 	}
 	x = 65535
 	y = 1
 	r = x / y
 	if r != 65535 {
-		t.Errorf("65535 / 1 = %d, want 65535", r)
+		t.Errorf("65535 %s 1 = %d, want 65535", "/", r)
 	}
 	y = 65535
 	r = x / y
 	if r != 1 {
-		t.Errorf("65535 / 65535 = %d, want 1", r)
+		t.Errorf("65535 %s 65535 = %d, want 1", "/", r)
 	}
 }
 func TestConstFolduint16mul(t *testing.T) {
@@ -3646,49 +3646,49 @@ func TestConstFolduint16mul(t *testing.T) {
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "*", r)
 	}
 	y = 65535
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "*", r)
 	}
 	x = 1
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("1 * 0 = %d, want 0", r)
+		t.Errorf("1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 1 {
-		t.Errorf("1 * 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "*", r)
 	}
 	y = 65535
 	r = x * y
 	if r != 65535 {
-		t.Errorf("1 * 65535 = %d, want 65535", r)
+		t.Errorf("1 %s 65535 = %d, want 65535", "*", r)
 	}
 	x = 65535
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("65535 * 0 = %d, want 0", r)
+		t.Errorf("65535 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 65535 {
-		t.Errorf("65535 * 1 = %d, want 65535", r)
+		t.Errorf("65535 %s 1 = %d, want 65535", "*", r)
 	}
 	y = 65535
 	r = x * y
 	if r != 1 {
-		t.Errorf("65535 * 65535 = %d, want 1", r)
+		t.Errorf("65535 %s 65535 = %d, want 1", "*", r)
 	}
 }
 func TestConstFolduint16mod(t *testing.T) {
@@ -3697,34 +3697,34 @@ func TestConstFolduint16mod(t *testing.T) {
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "%", r)
 	}
 	y = 65535
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "%", r)
 	}
 	x = 1
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 65535
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 65535 = %d, want 1", r)
+		t.Errorf("1 %s 65535 = %d, want 1", "%", r)
 	}
 	x = 65535
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("65535 % 1 = %d, want 0", r)
+		t.Errorf("65535 %s 1 = %d, want 0", "%", r)
 	}
 	y = 65535
 	r = x % y
 	if r != 0 {
-		t.Errorf("65535 % 65535 = %d, want 0", r)
+		t.Errorf("65535 %s 65535 = %d, want 0", "%", r)
 	}
 }
 func TestConstFoldint16add(t *testing.T) {
@@ -3733,253 +3733,253 @@ func TestConstFoldint16add(t *testing.T) {
 	y = -32768
 	r = x + y
 	if r != 0 {
-		t.Errorf("-32768 + -32768 = %d, want 0", r)
+		t.Errorf("-32768 %s -32768 = %d, want 0", "+", r)
 	}
 	y = -32767
 	r = x + y
 	if r != 1 {
-		t.Errorf("-32768 + -32767 = %d, want 1", r)
+		t.Errorf("-32768 %s -32767 = %d, want 1", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 32767 {
-		t.Errorf("-32768 + -1 = %d, want 32767", r)
+		t.Errorf("-32768 %s -1 = %d, want 32767", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -32768 {
-		t.Errorf("-32768 + 0 = %d, want -32768", r)
+		t.Errorf("-32768 %s 0 = %d, want -32768", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -32767 {
-		t.Errorf("-32768 + 1 = %d, want -32767", r)
+		t.Errorf("-32768 %s 1 = %d, want -32767", "+", r)
 	}
 	y = 32766
 	r = x + y
 	if r != -2 {
-		t.Errorf("-32768 + 32766 = %d, want -2", r)
+		t.Errorf("-32768 %s 32766 = %d, want -2", "+", r)
 	}
 	y = 32767
 	r = x + y
 	if r != -1 {
-		t.Errorf("-32768 + 32767 = %d, want -1", r)
+		t.Errorf("-32768 %s 32767 = %d, want -1", "+", r)
 	}
 	x = -32767
 	y = -32768
 	r = x + y
 	if r != 1 {
-		t.Errorf("-32767 + -32768 = %d, want 1", r)
+		t.Errorf("-32767 %s -32768 = %d, want 1", "+", r)
 	}
 	y = -32767
 	r = x + y
 	if r != 2 {
-		t.Errorf("-32767 + -32767 = %d, want 2", r)
+		t.Errorf("-32767 %s -32767 = %d, want 2", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -32768 {
-		t.Errorf("-32767 + -1 = %d, want -32768", r)
+		t.Errorf("-32767 %s -1 = %d, want -32768", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -32767 {
-		t.Errorf("-32767 + 0 = %d, want -32767", r)
+		t.Errorf("-32767 %s 0 = %d, want -32767", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -32766 {
-		t.Errorf("-32767 + 1 = %d, want -32766", r)
+		t.Errorf("-32767 %s 1 = %d, want -32766", "+", r)
 	}
 	y = 32766
 	r = x + y
 	if r != -1 {
-		t.Errorf("-32767 + 32766 = %d, want -1", r)
+		t.Errorf("-32767 %s 32766 = %d, want -1", "+", r)
 	}
 	y = 32767
 	r = x + y
 	if r != 0 {
-		t.Errorf("-32767 + 32767 = %d, want 0", r)
+		t.Errorf("-32767 %s 32767 = %d, want 0", "+", r)
 	}
 	x = -1
 	y = -32768
 	r = x + y
 	if r != 32767 {
-		t.Errorf("-1 + -32768 = %d, want 32767", r)
+		t.Errorf("-1 %s -32768 = %d, want 32767", "+", r)
 	}
 	y = -32767
 	r = x + y
 	if r != -32768 {
-		t.Errorf("-1 + -32767 = %d, want -32768", r)
+		t.Errorf("-1 %s -32767 = %d, want -32768", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -2 {
-		t.Errorf("-1 + -1 = %d, want -2", r)
+		t.Errorf("-1 %s -1 = %d, want -2", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -1 {
-		t.Errorf("-1 + 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 0 {
-		t.Errorf("-1 + 1 = %d, want 0", r)
+		t.Errorf("-1 %s 1 = %d, want 0", "+", r)
 	}
 	y = 32766
 	r = x + y
 	if r != 32765 {
-		t.Errorf("-1 + 32766 = %d, want 32765", r)
+		t.Errorf("-1 %s 32766 = %d, want 32765", "+", r)
 	}
 	y = 32767
 	r = x + y
 	if r != 32766 {
-		t.Errorf("-1 + 32767 = %d, want 32766", r)
+		t.Errorf("-1 %s 32767 = %d, want 32766", "+", r)
 	}
 	x = 0
 	y = -32768
 	r = x + y
 	if r != -32768 {
-		t.Errorf("0 + -32768 = %d, want -32768", r)
+		t.Errorf("0 %s -32768 = %d, want -32768", "+", r)
 	}
 	y = -32767
 	r = x + y
 	if r != -32767 {
-		t.Errorf("0 + -32767 = %d, want -32767", r)
+		t.Errorf("0 %s -32767 = %d, want -32767", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -1 {
-		t.Errorf("0 + -1 = %d, want -1", r)
+		t.Errorf("0 %s -1 = %d, want -1", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 0 {
-		t.Errorf("0 + 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 1 {
-		t.Errorf("0 + 1 = %d, want 1", r)
+		t.Errorf("0 %s 1 = %d, want 1", "+", r)
 	}
 	y = 32766
 	r = x + y
 	if r != 32766 {
-		t.Errorf("0 + 32766 = %d, want 32766", r)
+		t.Errorf("0 %s 32766 = %d, want 32766", "+", r)
 	}
 	y = 32767
 	r = x + y
 	if r != 32767 {
-		t.Errorf("0 + 32767 = %d, want 32767", r)
+		t.Errorf("0 %s 32767 = %d, want 32767", "+", r)
 	}
 	x = 1
 	y = -32768
 	r = x + y
 	if r != -32767 {
-		t.Errorf("1 + -32768 = %d, want -32767", r)
+		t.Errorf("1 %s -32768 = %d, want -32767", "+", r)
 	}
 	y = -32767
 	r = x + y
 	if r != -32766 {
-		t.Errorf("1 + -32767 = %d, want -32766", r)
+		t.Errorf("1 %s -32767 = %d, want -32766", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 0 {
-		t.Errorf("1 + -1 = %d, want 0", r)
+		t.Errorf("1 %s -1 = %d, want 0", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 1 {
-		t.Errorf("1 + 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 2 {
-		t.Errorf("1 + 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "+", r)
 	}
 	y = 32766
 	r = x + y
 	if r != 32767 {
-		t.Errorf("1 + 32766 = %d, want 32767", r)
+		t.Errorf("1 %s 32766 = %d, want 32767", "+", r)
 	}
 	y = 32767
 	r = x + y
 	if r != -32768 {
-		t.Errorf("1 + 32767 = %d, want -32768", r)
+		t.Errorf("1 %s 32767 = %d, want -32768", "+", r)
 	}
 	x = 32766
 	y = -32768
 	r = x + y
 	if r != -2 {
-		t.Errorf("32766 + -32768 = %d, want -2", r)
+		t.Errorf("32766 %s -32768 = %d, want -2", "+", r)
 	}
 	y = -32767
 	r = x + y
 	if r != -1 {
-		t.Errorf("32766 + -32767 = %d, want -1", r)
+		t.Errorf("32766 %s -32767 = %d, want -1", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 32765 {
-		t.Errorf("32766 + -1 = %d, want 32765", r)
+		t.Errorf("32766 %s -1 = %d, want 32765", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 32766 {
-		t.Errorf("32766 + 0 = %d, want 32766", r)
+		t.Errorf("32766 %s 0 = %d, want 32766", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 32767 {
-		t.Errorf("32766 + 1 = %d, want 32767", r)
+		t.Errorf("32766 %s 1 = %d, want 32767", "+", r)
 	}
 	y = 32766
 	r = x + y
 	if r != -4 {
-		t.Errorf("32766 + 32766 = %d, want -4", r)
+		t.Errorf("32766 %s 32766 = %d, want -4", "+", r)
 	}
 	y = 32767
 	r = x + y
 	if r != -3 {
-		t.Errorf("32766 + 32767 = %d, want -3", r)
+		t.Errorf("32766 %s 32767 = %d, want -3", "+", r)
 	}
 	x = 32767
 	y = -32768
 	r = x + y
 	if r != -1 {
-		t.Errorf("32767 + -32768 = %d, want -1", r)
+		t.Errorf("32767 %s -32768 = %d, want -1", "+", r)
 	}
 	y = -32767
 	r = x + y
 	if r != 0 {
-		t.Errorf("32767 + -32767 = %d, want 0", r)
+		t.Errorf("32767 %s -32767 = %d, want 0", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 32766 {
-		t.Errorf("32767 + -1 = %d, want 32766", r)
+		t.Errorf("32767 %s -1 = %d, want 32766", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 32767 {
-		t.Errorf("32767 + 0 = %d, want 32767", r)
+		t.Errorf("32767 %s 0 = %d, want 32767", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -32768 {
-		t.Errorf("32767 + 1 = %d, want -32768", r)
+		t.Errorf("32767 %s 1 = %d, want -32768", "+", r)
 	}
 	y = 32766
 	r = x + y
 	if r != -3 {
-		t.Errorf("32767 + 32766 = %d, want -3", r)
+		t.Errorf("32767 %s 32766 = %d, want -3", "+", r)
 	}
 	y = 32767
 	r = x + y
 	if r != -2 {
-		t.Errorf("32767 + 32767 = %d, want -2", r)
+		t.Errorf("32767 %s 32767 = %d, want -2", "+", r)
 	}
 }
 func TestConstFoldint16sub(t *testing.T) {
@@ -3988,253 +3988,253 @@ func TestConstFoldint16sub(t *testing.T) {
 	y = -32768
 	r = x - y
 	if r != 0 {
-		t.Errorf("-32768 - -32768 = %d, want 0", r)
+		t.Errorf("-32768 %s -32768 = %d, want 0", "-", r)
 	}
 	y = -32767
 	r = x - y
 	if r != -1 {
-		t.Errorf("-32768 - -32767 = %d, want -1", r)
+		t.Errorf("-32768 %s -32767 = %d, want -1", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -32767 {
-		t.Errorf("-32768 - -1 = %d, want -32767", r)
+		t.Errorf("-32768 %s -1 = %d, want -32767", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -32768 {
-		t.Errorf("-32768 - 0 = %d, want -32768", r)
+		t.Errorf("-32768 %s 0 = %d, want -32768", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 32767 {
-		t.Errorf("-32768 - 1 = %d, want 32767", r)
+		t.Errorf("-32768 %s 1 = %d, want 32767", "-", r)
 	}
 	y = 32766
 	r = x - y
 	if r != 2 {
-		t.Errorf("-32768 - 32766 = %d, want 2", r)
+		t.Errorf("-32768 %s 32766 = %d, want 2", "-", r)
 	}
 	y = 32767
 	r = x - y
 	if r != 1 {
-		t.Errorf("-32768 - 32767 = %d, want 1", r)
+		t.Errorf("-32768 %s 32767 = %d, want 1", "-", r)
 	}
 	x = -32767
 	y = -32768
 	r = x - y
 	if r != 1 {
-		t.Errorf("-32767 - -32768 = %d, want 1", r)
+		t.Errorf("-32767 %s -32768 = %d, want 1", "-", r)
 	}
 	y = -32767
 	r = x - y
 	if r != 0 {
-		t.Errorf("-32767 - -32767 = %d, want 0", r)
+		t.Errorf("-32767 %s -32767 = %d, want 0", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -32766 {
-		t.Errorf("-32767 - -1 = %d, want -32766", r)
+		t.Errorf("-32767 %s -1 = %d, want -32766", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -32767 {
-		t.Errorf("-32767 - 0 = %d, want -32767", r)
+		t.Errorf("-32767 %s 0 = %d, want -32767", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -32768 {
-		t.Errorf("-32767 - 1 = %d, want -32768", r)
+		t.Errorf("-32767 %s 1 = %d, want -32768", "-", r)
 	}
 	y = 32766
 	r = x - y
 	if r != 3 {
-		t.Errorf("-32767 - 32766 = %d, want 3", r)
+		t.Errorf("-32767 %s 32766 = %d, want 3", "-", r)
 	}
 	y = 32767
 	r = x - y
 	if r != 2 {
-		t.Errorf("-32767 - 32767 = %d, want 2", r)
+		t.Errorf("-32767 %s 32767 = %d, want 2", "-", r)
 	}
 	x = -1
 	y = -32768
 	r = x - y
 	if r != 32767 {
-		t.Errorf("-1 - -32768 = %d, want 32767", r)
+		t.Errorf("-1 %s -32768 = %d, want 32767", "-", r)
 	}
 	y = -32767
 	r = x - y
 	if r != 32766 {
-		t.Errorf("-1 - -32767 = %d, want 32766", r)
+		t.Errorf("-1 %s -32767 = %d, want 32766", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 0 {
-		t.Errorf("-1 - -1 = %d, want 0", r)
+		t.Errorf("-1 %s -1 = %d, want 0", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -1 {
-		t.Errorf("-1 - 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -2 {
-		t.Errorf("-1 - 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "-", r)
 	}
 	y = 32766
 	r = x - y
 	if r != -32767 {
-		t.Errorf("-1 - 32766 = %d, want -32767", r)
+		t.Errorf("-1 %s 32766 = %d, want -32767", "-", r)
 	}
 	y = 32767
 	r = x - y
 	if r != -32768 {
-		t.Errorf("-1 - 32767 = %d, want -32768", r)
+		t.Errorf("-1 %s 32767 = %d, want -32768", "-", r)
 	}
 	x = 0
 	y = -32768
 	r = x - y
 	if r != -32768 {
-		t.Errorf("0 - -32768 = %d, want -32768", r)
+		t.Errorf("0 %s -32768 = %d, want -32768", "-", r)
 	}
 	y = -32767
 	r = x - y
 	if r != 32767 {
-		t.Errorf("0 - -32767 = %d, want 32767", r)
+		t.Errorf("0 %s -32767 = %d, want 32767", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 1 {
-		t.Errorf("0 - -1 = %d, want 1", r)
+		t.Errorf("0 %s -1 = %d, want 1", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 0 {
-		t.Errorf("0 - 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -1 {
-		t.Errorf("0 - 1 = %d, want -1", r)
+		t.Errorf("0 %s 1 = %d, want -1", "-", r)
 	}
 	y = 32766
 	r = x - y
 	if r != -32766 {
-		t.Errorf("0 - 32766 = %d, want -32766", r)
+		t.Errorf("0 %s 32766 = %d, want -32766", "-", r)
 	}
 	y = 32767
 	r = x - y
 	if r != -32767 {
-		t.Errorf("0 - 32767 = %d, want -32767", r)
+		t.Errorf("0 %s 32767 = %d, want -32767", "-", r)
 	}
 	x = 1
 	y = -32768
 	r = x - y
 	if r != -32767 {
-		t.Errorf("1 - -32768 = %d, want -32767", r)
+		t.Errorf("1 %s -32768 = %d, want -32767", "-", r)
 	}
 	y = -32767
 	r = x - y
 	if r != -32768 {
-		t.Errorf("1 - -32767 = %d, want -32768", r)
+		t.Errorf("1 %s -32767 = %d, want -32768", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 2 {
-		t.Errorf("1 - -1 = %d, want 2", r)
+		t.Errorf("1 %s -1 = %d, want 2", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 1 {
-		t.Errorf("1 - 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 0 {
-		t.Errorf("1 - 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "-", r)
 	}
 	y = 32766
 	r = x - y
 	if r != -32765 {
-		t.Errorf("1 - 32766 = %d, want -32765", r)
+		t.Errorf("1 %s 32766 = %d, want -32765", "-", r)
 	}
 	y = 32767
 	r = x - y
 	if r != -32766 {
-		t.Errorf("1 - 32767 = %d, want -32766", r)
+		t.Errorf("1 %s 32767 = %d, want -32766", "-", r)
 	}
 	x = 32766
 	y = -32768
 	r = x - y
 	if r != -2 {
-		t.Errorf("32766 - -32768 = %d, want -2", r)
+		t.Errorf("32766 %s -32768 = %d, want -2", "-", r)
 	}
 	y = -32767
 	r = x - y
 	if r != -3 {
-		t.Errorf("32766 - -32767 = %d, want -3", r)
+		t.Errorf("32766 %s -32767 = %d, want -3", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 32767 {
-		t.Errorf("32766 - -1 = %d, want 32767", r)
+		t.Errorf("32766 %s -1 = %d, want 32767", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 32766 {
-		t.Errorf("32766 - 0 = %d, want 32766", r)
+		t.Errorf("32766 %s 0 = %d, want 32766", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 32765 {
-		t.Errorf("32766 - 1 = %d, want 32765", r)
+		t.Errorf("32766 %s 1 = %d, want 32765", "-", r)
 	}
 	y = 32766
 	r = x - y
 	if r != 0 {
-		t.Errorf("32766 - 32766 = %d, want 0", r)
+		t.Errorf("32766 %s 32766 = %d, want 0", "-", r)
 	}
 	y = 32767
 	r = x - y
 	if r != -1 {
-		t.Errorf("32766 - 32767 = %d, want -1", r)
+		t.Errorf("32766 %s 32767 = %d, want -1", "-", r)
 	}
 	x = 32767
 	y = -32768
 	r = x - y
 	if r != -1 {
-		t.Errorf("32767 - -32768 = %d, want -1", r)
+		t.Errorf("32767 %s -32768 = %d, want -1", "-", r)
 	}
 	y = -32767
 	r = x - y
 	if r != -2 {
-		t.Errorf("32767 - -32767 = %d, want -2", r)
+		t.Errorf("32767 %s -32767 = %d, want -2", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -32768 {
-		t.Errorf("32767 - -1 = %d, want -32768", r)
+		t.Errorf("32767 %s -1 = %d, want -32768", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 32767 {
-		t.Errorf("32767 - 0 = %d, want 32767", r)
+		t.Errorf("32767 %s 0 = %d, want 32767", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 32766 {
-		t.Errorf("32767 - 1 = %d, want 32766", r)
+		t.Errorf("32767 %s 1 = %d, want 32766", "-", r)
 	}
 	y = 32766
 	r = x - y
 	if r != 1 {
-		t.Errorf("32767 - 32766 = %d, want 1", r)
+		t.Errorf("32767 %s 32766 = %d, want 1", "-", r)
 	}
 	y = 32767
 	r = x - y
 	if r != 0 {
-		t.Errorf("32767 - 32767 = %d, want 0", r)
+		t.Errorf("32767 %s 32767 = %d, want 0", "-", r)
 	}
 }
 func TestConstFoldint16div(t *testing.T) {
@@ -4243,218 +4243,218 @@ func TestConstFoldint16div(t *testing.T) {
 	y = -32768
 	r = x / y
 	if r != 1 {
-		t.Errorf("-32768 / -32768 = %d, want 1", r)
+		t.Errorf("-32768 %s -32768 = %d, want 1", "/", r)
 	}
 	y = -32767
 	r = x / y
 	if r != 1 {
-		t.Errorf("-32768 / -32767 = %d, want 1", r)
+		t.Errorf("-32768 %s -32767 = %d, want 1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -32768 {
-		t.Errorf("-32768 / -1 = %d, want -32768", r)
+		t.Errorf("-32768 %s -1 = %d, want -32768", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -32768 {
-		t.Errorf("-32768 / 1 = %d, want -32768", r)
+		t.Errorf("-32768 %s 1 = %d, want -32768", "/", r)
 	}
 	y = 32766
 	r = x / y
 	if r != -1 {
-		t.Errorf("-32768 / 32766 = %d, want -1", r)
+		t.Errorf("-32768 %s 32766 = %d, want -1", "/", r)
 	}
 	y = 32767
 	r = x / y
 	if r != -1 {
-		t.Errorf("-32768 / 32767 = %d, want -1", r)
+		t.Errorf("-32768 %s 32767 = %d, want -1", "/", r)
 	}
 	x = -32767
 	y = -32768
 	r = x / y
 	if r != 0 {
-		t.Errorf("-32767 / -32768 = %d, want 0", r)
+		t.Errorf("-32767 %s -32768 = %d, want 0", "/", r)
 	}
 	y = -32767
 	r = x / y
 	if r != 1 {
-		t.Errorf("-32767 / -32767 = %d, want 1", r)
+		t.Errorf("-32767 %s -32767 = %d, want 1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 32767 {
-		t.Errorf("-32767 / -1 = %d, want 32767", r)
+		t.Errorf("-32767 %s -1 = %d, want 32767", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -32767 {
-		t.Errorf("-32767 / 1 = %d, want -32767", r)
+		t.Errorf("-32767 %s 1 = %d, want -32767", "/", r)
 	}
 	y = 32766
 	r = x / y
 	if r != -1 {
-		t.Errorf("-32767 / 32766 = %d, want -1", r)
+		t.Errorf("-32767 %s 32766 = %d, want -1", "/", r)
 	}
 	y = 32767
 	r = x / y
 	if r != -1 {
-		t.Errorf("-32767 / 32767 = %d, want -1", r)
+		t.Errorf("-32767 %s 32767 = %d, want -1", "/", r)
 	}
 	x = -1
 	y = -32768
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / -32768 = %d, want 0", r)
+		t.Errorf("-1 %s -32768 = %d, want 0", "/", r)
 	}
 	y = -32767
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / -32767 = %d, want 0", r)
+		t.Errorf("-1 %s -32767 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 1 {
-		t.Errorf("-1 / -1 = %d, want 1", r)
+		t.Errorf("-1 %s -1 = %d, want 1", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -1 {
-		t.Errorf("-1 / 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", "/", r)
 	}
 	y = 32766
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / 32766 = %d, want 0", r)
+		t.Errorf("-1 %s 32766 = %d, want 0", "/", r)
 	}
 	y = 32767
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / 32767 = %d, want 0", r)
+		t.Errorf("-1 %s 32767 = %d, want 0", "/", r)
 	}
 	x = 0
 	y = -32768
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -32768 = %d, want 0", r)
+		t.Errorf("0 %s -32768 = %d, want 0", "/", r)
 	}
 	y = -32767
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -32767 = %d, want 0", r)
+		t.Errorf("0 %s -32767 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "/", r)
 	}
 	y = 32766
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 32766 = %d, want 0", r)
+		t.Errorf("0 %s 32766 = %d, want 0", "/", r)
 	}
 	y = 32767
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 32767 = %d, want 0", r)
+		t.Errorf("0 %s 32767 = %d, want 0", "/", r)
 	}
 	x = 1
 	y = -32768
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / -32768 = %d, want 0", r)
+		t.Errorf("1 %s -32768 = %d, want 0", "/", r)
 	}
 	y = -32767
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / -32767 = %d, want 0", r)
+		t.Errorf("1 %s -32767 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -1 {
-		t.Errorf("1 / -1 = %d, want -1", r)
+		t.Errorf("1 %s -1 = %d, want -1", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 1 {
-		t.Errorf("1 / 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "/", r)
 	}
 	y = 32766
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 32766 = %d, want 0", r)
+		t.Errorf("1 %s 32766 = %d, want 0", "/", r)
 	}
 	y = 32767
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 32767 = %d, want 0", r)
+		t.Errorf("1 %s 32767 = %d, want 0", "/", r)
 	}
 	x = 32766
 	y = -32768
 	r = x / y
 	if r != 0 {
-		t.Errorf("32766 / -32768 = %d, want 0", r)
+		t.Errorf("32766 %s -32768 = %d, want 0", "/", r)
 	}
 	y = -32767
 	r = x / y
 	if r != 0 {
-		t.Errorf("32766 / -32767 = %d, want 0", r)
+		t.Errorf("32766 %s -32767 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -32766 {
-		t.Errorf("32766 / -1 = %d, want -32766", r)
+		t.Errorf("32766 %s -1 = %d, want -32766", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 32766 {
-		t.Errorf("32766 / 1 = %d, want 32766", r)
+		t.Errorf("32766 %s 1 = %d, want 32766", "/", r)
 	}
 	y = 32766
 	r = x / y
 	if r != 1 {
-		t.Errorf("32766 / 32766 = %d, want 1", r)
+		t.Errorf("32766 %s 32766 = %d, want 1", "/", r)
 	}
 	y = 32767
 	r = x / y
 	if r != 0 {
-		t.Errorf("32766 / 32767 = %d, want 0", r)
+		t.Errorf("32766 %s 32767 = %d, want 0", "/", r)
 	}
 	x = 32767
 	y = -32768
 	r = x / y
 	if r != 0 {
-		t.Errorf("32767 / -32768 = %d, want 0", r)
+		t.Errorf("32767 %s -32768 = %d, want 0", "/", r)
 	}
 	y = -32767
 	r = x / y
 	if r != -1 {
-		t.Errorf("32767 / -32767 = %d, want -1", r)
+		t.Errorf("32767 %s -32767 = %d, want -1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -32767 {
-		t.Errorf("32767 / -1 = %d, want -32767", r)
+		t.Errorf("32767 %s -1 = %d, want -32767", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 32767 {
-		t.Errorf("32767 / 1 = %d, want 32767", r)
+		t.Errorf("32767 %s 1 = %d, want 32767", "/", r)
 	}
 	y = 32766
 	r = x / y
 	if r != 1 {
-		t.Errorf("32767 / 32766 = %d, want 1", r)
+		t.Errorf("32767 %s 32766 = %d, want 1", "/", r)
 	}
 	y = 32767
 	r = x / y
 	if r != 1 {
-		t.Errorf("32767 / 32767 = %d, want 1", r)
+		t.Errorf("32767 %s 32767 = %d, want 1", "/", r)
 	}
 }
 func TestConstFoldint16mul(t *testing.T) {
@@ -4463,253 +4463,253 @@ func TestConstFoldint16mul(t *testing.T) {
 	y = -32768
 	r = x * y
 	if r != 0 {
-		t.Errorf("-32768 * -32768 = %d, want 0", r)
+		t.Errorf("-32768 %s -32768 = %d, want 0", "*", r)
 	}
 	y = -32767
 	r = x * y
 	if r != -32768 {
-		t.Errorf("-32768 * -32767 = %d, want -32768", r)
+		t.Errorf("-32768 %s -32767 = %d, want -32768", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -32768 {
-		t.Errorf("-32768 * -1 = %d, want -32768", r)
+		t.Errorf("-32768 %s -1 = %d, want -32768", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-32768 * 0 = %d, want 0", r)
+		t.Errorf("-32768 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -32768 {
-		t.Errorf("-32768 * 1 = %d, want -32768", r)
+		t.Errorf("-32768 %s 1 = %d, want -32768", "*", r)
 	}
 	y = 32766
 	r = x * y
 	if r != 0 {
-		t.Errorf("-32768 * 32766 = %d, want 0", r)
+		t.Errorf("-32768 %s 32766 = %d, want 0", "*", r)
 	}
 	y = 32767
 	r = x * y
 	if r != -32768 {
-		t.Errorf("-32768 * 32767 = %d, want -32768", r)
+		t.Errorf("-32768 %s 32767 = %d, want -32768", "*", r)
 	}
 	x = -32767
 	y = -32768
 	r = x * y
 	if r != -32768 {
-		t.Errorf("-32767 * -32768 = %d, want -32768", r)
+		t.Errorf("-32767 %s -32768 = %d, want -32768", "*", r)
 	}
 	y = -32767
 	r = x * y
 	if r != 1 {
-		t.Errorf("-32767 * -32767 = %d, want 1", r)
+		t.Errorf("-32767 %s -32767 = %d, want 1", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 32767 {
-		t.Errorf("-32767 * -1 = %d, want 32767", r)
+		t.Errorf("-32767 %s -1 = %d, want 32767", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-32767 * 0 = %d, want 0", r)
+		t.Errorf("-32767 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -32767 {
-		t.Errorf("-32767 * 1 = %d, want -32767", r)
+		t.Errorf("-32767 %s 1 = %d, want -32767", "*", r)
 	}
 	y = 32766
 	r = x * y
 	if r != 32766 {
-		t.Errorf("-32767 * 32766 = %d, want 32766", r)
+		t.Errorf("-32767 %s 32766 = %d, want 32766", "*", r)
 	}
 	y = 32767
 	r = x * y
 	if r != -1 {
-		t.Errorf("-32767 * 32767 = %d, want -1", r)
+		t.Errorf("-32767 %s 32767 = %d, want -1", "*", r)
 	}
 	x = -1
 	y = -32768
 	r = x * y
 	if r != -32768 {
-		t.Errorf("-1 * -32768 = %d, want -32768", r)
+		t.Errorf("-1 %s -32768 = %d, want -32768", "*", r)
 	}
 	y = -32767
 	r = x * y
 	if r != 32767 {
-		t.Errorf("-1 * -32767 = %d, want 32767", r)
+		t.Errorf("-1 %s -32767 = %d, want 32767", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 1 {
-		t.Errorf("-1 * -1 = %d, want 1", r)
+		t.Errorf("-1 %s -1 = %d, want 1", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-1 * 0 = %d, want 0", r)
+		t.Errorf("-1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -1 {
-		t.Errorf("-1 * 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", "*", r)
 	}
 	y = 32766
 	r = x * y
 	if r != -32766 {
-		t.Errorf("-1 * 32766 = %d, want -32766", r)
+		t.Errorf("-1 %s 32766 = %d, want -32766", "*", r)
 	}
 	y = 32767
 	r = x * y
 	if r != -32767 {
-		t.Errorf("-1 * 32767 = %d, want -32767", r)
+		t.Errorf("-1 %s 32767 = %d, want -32767", "*", r)
 	}
 	x = 0
 	y = -32768
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -32768 = %d, want 0", r)
+		t.Errorf("0 %s -32768 = %d, want 0", "*", r)
 	}
 	y = -32767
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -32767 = %d, want 0", r)
+		t.Errorf("0 %s -32767 = %d, want 0", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "*", r)
 	}
 	y = 32766
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 32766 = %d, want 0", r)
+		t.Errorf("0 %s 32766 = %d, want 0", "*", r)
 	}
 	y = 32767
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 32767 = %d, want 0", r)
+		t.Errorf("0 %s 32767 = %d, want 0", "*", r)
 	}
 	x = 1
 	y = -32768
 	r = x * y
 	if r != -32768 {
-		t.Errorf("1 * -32768 = %d, want -32768", r)
+		t.Errorf("1 %s -32768 = %d, want -32768", "*", r)
 	}
 	y = -32767
 	r = x * y
 	if r != -32767 {
-		t.Errorf("1 * -32767 = %d, want -32767", r)
+		t.Errorf("1 %s -32767 = %d, want -32767", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -1 {
-		t.Errorf("1 * -1 = %d, want -1", r)
+		t.Errorf("1 %s -1 = %d, want -1", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("1 * 0 = %d, want 0", r)
+		t.Errorf("1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 1 {
-		t.Errorf("1 * 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "*", r)
 	}
 	y = 32766
 	r = x * y
 	if r != 32766 {
-		t.Errorf("1 * 32766 = %d, want 32766", r)
+		t.Errorf("1 %s 32766 = %d, want 32766", "*", r)
 	}
 	y = 32767
 	r = x * y
 	if r != 32767 {
-		t.Errorf("1 * 32767 = %d, want 32767", r)
+		t.Errorf("1 %s 32767 = %d, want 32767", "*", r)
 	}
 	x = 32766
 	y = -32768
 	r = x * y
 	if r != 0 {
-		t.Errorf("32766 * -32768 = %d, want 0", r)
+		t.Errorf("32766 %s -32768 = %d, want 0", "*", r)
 	}
 	y = -32767
 	r = x * y
 	if r != 32766 {
-		t.Errorf("32766 * -32767 = %d, want 32766", r)
+		t.Errorf("32766 %s -32767 = %d, want 32766", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -32766 {
-		t.Errorf("32766 * -1 = %d, want -32766", r)
+		t.Errorf("32766 %s -1 = %d, want -32766", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("32766 * 0 = %d, want 0", r)
+		t.Errorf("32766 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 32766 {
-		t.Errorf("32766 * 1 = %d, want 32766", r)
+		t.Errorf("32766 %s 1 = %d, want 32766", "*", r)
 	}
 	y = 32766
 	r = x * y
 	if r != 4 {
-		t.Errorf("32766 * 32766 = %d, want 4", r)
+		t.Errorf("32766 %s 32766 = %d, want 4", "*", r)
 	}
 	y = 32767
 	r = x * y
 	if r != -32766 {
-		t.Errorf("32766 * 32767 = %d, want -32766", r)
+		t.Errorf("32766 %s 32767 = %d, want -32766", "*", r)
 	}
 	x = 32767
 	y = -32768
 	r = x * y
 	if r != -32768 {
-		t.Errorf("32767 * -32768 = %d, want -32768", r)
+		t.Errorf("32767 %s -32768 = %d, want -32768", "*", r)
 	}
 	y = -32767
 	r = x * y
 	if r != -1 {
-		t.Errorf("32767 * -32767 = %d, want -1", r)
+		t.Errorf("32767 %s -32767 = %d, want -1", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -32767 {
-		t.Errorf("32767 * -1 = %d, want -32767", r)
+		t.Errorf("32767 %s -1 = %d, want -32767", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("32767 * 0 = %d, want 0", r)
+		t.Errorf("32767 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 32767 {
-		t.Errorf("32767 * 1 = %d, want 32767", r)
+		t.Errorf("32767 %s 1 = %d, want 32767", "*", r)
 	}
 	y = 32766
 	r = x * y
 	if r != -32766 {
-		t.Errorf("32767 * 32766 = %d, want -32766", r)
+		t.Errorf("32767 %s 32766 = %d, want -32766", "*", r)
 	}
 	y = 32767
 	r = x * y
 	if r != 1 {
-		t.Errorf("32767 * 32767 = %d, want 1", r)
+		t.Errorf("32767 %s 32767 = %d, want 1", "*", r)
 	}
 }
 func TestConstFoldint16mod(t *testing.T) {
@@ -4718,218 +4718,218 @@ func TestConstFoldint16mod(t *testing.T) {
 	y = -32768
 	r = x % y
 	if r != 0 {
-		t.Errorf("-32768 % -32768 = %d, want 0", r)
+		t.Errorf("-32768 %s -32768 = %d, want 0", "%", r)
 	}
 	y = -32767
 	r = x % y
 	if r != -1 {
-		t.Errorf("-32768 % -32767 = %d, want -1", r)
+		t.Errorf("-32768 %s -32767 = %d, want -1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-32768 % -1 = %d, want 0", r)
+		t.Errorf("-32768 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-32768 % 1 = %d, want 0", r)
+		t.Errorf("-32768 %s 1 = %d, want 0", "%", r)
 	}
 	y = 32766
 	r = x % y
 	if r != -2 {
-		t.Errorf("-32768 % 32766 = %d, want -2", r)
+		t.Errorf("-32768 %s 32766 = %d, want -2", "%", r)
 	}
 	y = 32767
 	r = x % y
 	if r != -1 {
-		t.Errorf("-32768 % 32767 = %d, want -1", r)
+		t.Errorf("-32768 %s 32767 = %d, want -1", "%", r)
 	}
 	x = -32767
 	y = -32768
 	r = x % y
 	if r != -32767 {
-		t.Errorf("-32767 % -32768 = %d, want -32767", r)
+		t.Errorf("-32767 %s -32768 = %d, want -32767", "%", r)
 	}
 	y = -32767
 	r = x % y
 	if r != 0 {
-		t.Errorf("-32767 % -32767 = %d, want 0", r)
+		t.Errorf("-32767 %s -32767 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-32767 % -1 = %d, want 0", r)
+		t.Errorf("-32767 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-32767 % 1 = %d, want 0", r)
+		t.Errorf("-32767 %s 1 = %d, want 0", "%", r)
 	}
 	y = 32766
 	r = x % y
 	if r != -1 {
-		t.Errorf("-32767 % 32766 = %d, want -1", r)
+		t.Errorf("-32767 %s 32766 = %d, want -1", "%", r)
 	}
 	y = 32767
 	r = x % y
 	if r != 0 {
-		t.Errorf("-32767 % 32767 = %d, want 0", r)
+		t.Errorf("-32767 %s 32767 = %d, want 0", "%", r)
 	}
 	x = -1
 	y = -32768
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % -32768 = %d, want -1", r)
+		t.Errorf("-1 %s -32768 = %d, want -1", "%", r)
 	}
 	y = -32767
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % -32767 = %d, want -1", r)
+		t.Errorf("-1 %s -32767 = %d, want -1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-1 % -1 = %d, want 0", r)
+		t.Errorf("-1 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-1 % 1 = %d, want 0", r)
+		t.Errorf("-1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 32766
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % 32766 = %d, want -1", r)
+		t.Errorf("-1 %s 32766 = %d, want -1", "%", r)
 	}
 	y = 32767
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % 32767 = %d, want -1", r)
+		t.Errorf("-1 %s 32767 = %d, want -1", "%", r)
 	}
 	x = 0
 	y = -32768
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -32768 = %d, want 0", r)
+		t.Errorf("0 %s -32768 = %d, want 0", "%", r)
 	}
 	y = -32767
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -32767 = %d, want 0", r)
+		t.Errorf("0 %s -32767 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "%", r)
 	}
 	y = 32766
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 32766 = %d, want 0", r)
+		t.Errorf("0 %s 32766 = %d, want 0", "%", r)
 	}
 	y = 32767
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 32767 = %d, want 0", r)
+		t.Errorf("0 %s 32767 = %d, want 0", "%", r)
 	}
 	x = 1
 	y = -32768
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % -32768 = %d, want 1", r)
+		t.Errorf("1 %s -32768 = %d, want 1", "%", r)
 	}
 	y = -32767
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % -32767 = %d, want 1", r)
+		t.Errorf("1 %s -32767 = %d, want 1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % -1 = %d, want 0", r)
+		t.Errorf("1 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 32766
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 32766 = %d, want 1", r)
+		t.Errorf("1 %s 32766 = %d, want 1", "%", r)
 	}
 	y = 32767
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 32767 = %d, want 1", r)
+		t.Errorf("1 %s 32767 = %d, want 1", "%", r)
 	}
 	x = 32766
 	y = -32768
 	r = x % y
 	if r != 32766 {
-		t.Errorf("32766 % -32768 = %d, want 32766", r)
+		t.Errorf("32766 %s -32768 = %d, want 32766", "%", r)
 	}
 	y = -32767
 	r = x % y
 	if r != 32766 {
-		t.Errorf("32766 % -32767 = %d, want 32766", r)
+		t.Errorf("32766 %s -32767 = %d, want 32766", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("32766 % -1 = %d, want 0", r)
+		t.Errorf("32766 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("32766 % 1 = %d, want 0", r)
+		t.Errorf("32766 %s 1 = %d, want 0", "%", r)
 	}
 	y = 32766
 	r = x % y
 	if r != 0 {
-		t.Errorf("32766 % 32766 = %d, want 0", r)
+		t.Errorf("32766 %s 32766 = %d, want 0", "%", r)
 	}
 	y = 32767
 	r = x % y
 	if r != 32766 {
-		t.Errorf("32766 % 32767 = %d, want 32766", r)
+		t.Errorf("32766 %s 32767 = %d, want 32766", "%", r)
 	}
 	x = 32767
 	y = -32768
 	r = x % y
 	if r != 32767 {
-		t.Errorf("32767 % -32768 = %d, want 32767", r)
+		t.Errorf("32767 %s -32768 = %d, want 32767", "%", r)
 	}
 	y = -32767
 	r = x % y
 	if r != 0 {
-		t.Errorf("32767 % -32767 = %d, want 0", r)
+		t.Errorf("32767 %s -32767 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("32767 % -1 = %d, want 0", r)
+		t.Errorf("32767 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("32767 % 1 = %d, want 0", r)
+		t.Errorf("32767 %s 1 = %d, want 0", "%", r)
 	}
 	y = 32766
 	r = x % y
 	if r != 1 {
-		t.Errorf("32767 % 32766 = %d, want 1", r)
+		t.Errorf("32767 %s 32766 = %d, want 1", "%", r)
 	}
 	y = 32767
 	r = x % y
 	if r != 0 {
-		t.Errorf("32767 % 32767 = %d, want 0", r)
+		t.Errorf("32767 %s 32767 = %d, want 0", "%", r)
 	}
 }
 func TestConstFolduint8add(t *testing.T) {
@@ -4938,49 +4938,49 @@ func TestConstFolduint8add(t *testing.T) {
 	y = 0
 	r = x + y
 	if r != 0 {
-		t.Errorf("0 + 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 1 {
-		t.Errorf("0 + 1 = %d, want 1", r)
+		t.Errorf("0 %s 1 = %d, want 1", "+", r)
 	}
 	y = 255
 	r = x + y
 	if r != 255 {
-		t.Errorf("0 + 255 = %d, want 255", r)
+		t.Errorf("0 %s 255 = %d, want 255", "+", r)
 	}
 	x = 1
 	y = 0
 	r = x + y
 	if r != 1 {
-		t.Errorf("1 + 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 2 {
-		t.Errorf("1 + 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "+", r)
 	}
 	y = 255
 	r = x + y
 	if r != 0 {
-		t.Errorf("1 + 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", "+", r)
 	}
 	x = 255
 	y = 0
 	r = x + y
 	if r != 255 {
-		t.Errorf("255 + 0 = %d, want 255", r)
+		t.Errorf("255 %s 0 = %d, want 255", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 0 {
-		t.Errorf("255 + 1 = %d, want 0", r)
+		t.Errorf("255 %s 1 = %d, want 0", "+", r)
 	}
 	y = 255
 	r = x + y
 	if r != 254 {
-		t.Errorf("255 + 255 = %d, want 254", r)
+		t.Errorf("255 %s 255 = %d, want 254", "+", r)
 	}
 }
 func TestConstFolduint8sub(t *testing.T) {
@@ -4989,49 +4989,49 @@ func TestConstFolduint8sub(t *testing.T) {
 	y = 0
 	r = x - y
 	if r != 0 {
-		t.Errorf("0 - 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 255 {
-		t.Errorf("0 - 1 = %d, want 255", r)
+		t.Errorf("0 %s 1 = %d, want 255", "-", r)
 	}
 	y = 255
 	r = x - y
 	if r != 1 {
-		t.Errorf("0 - 255 = %d, want 1", r)
+		t.Errorf("0 %s 255 = %d, want 1", "-", r)
 	}
 	x = 1
 	y = 0
 	r = x - y
 	if r != 1 {
-		t.Errorf("1 - 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 0 {
-		t.Errorf("1 - 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "-", r)
 	}
 	y = 255
 	r = x - y
 	if r != 2 {
-		t.Errorf("1 - 255 = %d, want 2", r)
+		t.Errorf("1 %s 255 = %d, want 2", "-", r)
 	}
 	x = 255
 	y = 0
 	r = x - y
 	if r != 255 {
-		t.Errorf("255 - 0 = %d, want 255", r)
+		t.Errorf("255 %s 0 = %d, want 255", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 254 {
-		t.Errorf("255 - 1 = %d, want 254", r)
+		t.Errorf("255 %s 1 = %d, want 254", "-", r)
 	}
 	y = 255
 	r = x - y
 	if r != 0 {
-		t.Errorf("255 - 255 = %d, want 0", r)
+		t.Errorf("255 %s 255 = %d, want 0", "-", r)
 	}
 }
 func TestConstFolduint8div(t *testing.T) {
@@ -5040,34 +5040,34 @@ func TestConstFolduint8div(t *testing.T) {
 	y = 1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "/", r)
 	}
 	y = 255
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "/", r)
 	}
 	x = 1
 	y = 1
 	r = x / y
 	if r != 1 {
-		t.Errorf("1 / 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "/", r)
 	}
 	y = 255
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", "/", r)
 	}
 	x = 255
 	y = 1
 	r = x / y
 	if r != 255 {
-		t.Errorf("255 / 1 = %d, want 255", r)
+		t.Errorf("255 %s 1 = %d, want 255", "/", r)
 	}
 	y = 255
 	r = x / y
 	if r != 1 {
-		t.Errorf("255 / 255 = %d, want 1", r)
+		t.Errorf("255 %s 255 = %d, want 1", "/", r)
 	}
 }
 func TestConstFolduint8mul(t *testing.T) {
@@ -5076,49 +5076,49 @@ func TestConstFolduint8mul(t *testing.T) {
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "*", r)
 	}
 	y = 255
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "*", r)
 	}
 	x = 1
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("1 * 0 = %d, want 0", r)
+		t.Errorf("1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 1 {
-		t.Errorf("1 * 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "*", r)
 	}
 	y = 255
 	r = x * y
 	if r != 255 {
-		t.Errorf("1 * 255 = %d, want 255", r)
+		t.Errorf("1 %s 255 = %d, want 255", "*", r)
 	}
 	x = 255
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("255 * 0 = %d, want 0", r)
+		t.Errorf("255 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 255 {
-		t.Errorf("255 * 1 = %d, want 255", r)
+		t.Errorf("255 %s 1 = %d, want 255", "*", r)
 	}
 	y = 255
 	r = x * y
 	if r != 1 {
-		t.Errorf("255 * 255 = %d, want 1", r)
+		t.Errorf("255 %s 255 = %d, want 1", "*", r)
 	}
 }
 func TestConstFolduint8mod(t *testing.T) {
@@ -5127,34 +5127,34 @@ func TestConstFolduint8mod(t *testing.T) {
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "%", r)
 	}
 	y = 255
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "%", r)
 	}
 	x = 1
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 255
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 255 = %d, want 1", r)
+		t.Errorf("1 %s 255 = %d, want 1", "%", r)
 	}
 	x = 255
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("255 % 1 = %d, want 0", r)
+		t.Errorf("255 %s 1 = %d, want 0", "%", r)
 	}
 	y = 255
 	r = x % y
 	if r != 0 {
-		t.Errorf("255 % 255 = %d, want 0", r)
+		t.Errorf("255 %s 255 = %d, want 0", "%", r)
 	}
 }
 func TestConstFoldint8add(t *testing.T) {
@@ -5163,253 +5163,253 @@ func TestConstFoldint8add(t *testing.T) {
 	y = -128
 	r = x + y
 	if r != 0 {
-		t.Errorf("-128 + -128 = %d, want 0", r)
+		t.Errorf("-128 %s -128 = %d, want 0", "+", r)
 	}
 	y = -127
 	r = x + y
 	if r != 1 {
-		t.Errorf("-128 + -127 = %d, want 1", r)
+		t.Errorf("-128 %s -127 = %d, want 1", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 127 {
-		t.Errorf("-128 + -1 = %d, want 127", r)
+		t.Errorf("-128 %s -1 = %d, want 127", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -128 {
-		t.Errorf("-128 + 0 = %d, want -128", r)
+		t.Errorf("-128 %s 0 = %d, want -128", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -127 {
-		t.Errorf("-128 + 1 = %d, want -127", r)
+		t.Errorf("-128 %s 1 = %d, want -127", "+", r)
 	}
 	y = 126
 	r = x + y
 	if r != -2 {
-		t.Errorf("-128 + 126 = %d, want -2", r)
+		t.Errorf("-128 %s 126 = %d, want -2", "+", r)
 	}
 	y = 127
 	r = x + y
 	if r != -1 {
-		t.Errorf("-128 + 127 = %d, want -1", r)
+		t.Errorf("-128 %s 127 = %d, want -1", "+", r)
 	}
 	x = -127
 	y = -128
 	r = x + y
 	if r != 1 {
-		t.Errorf("-127 + -128 = %d, want 1", r)
+		t.Errorf("-127 %s -128 = %d, want 1", "+", r)
 	}
 	y = -127
 	r = x + y
 	if r != 2 {
-		t.Errorf("-127 + -127 = %d, want 2", r)
+		t.Errorf("-127 %s -127 = %d, want 2", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -128 {
-		t.Errorf("-127 + -1 = %d, want -128", r)
+		t.Errorf("-127 %s -1 = %d, want -128", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -127 {
-		t.Errorf("-127 + 0 = %d, want -127", r)
+		t.Errorf("-127 %s 0 = %d, want -127", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -126 {
-		t.Errorf("-127 + 1 = %d, want -126", r)
+		t.Errorf("-127 %s 1 = %d, want -126", "+", r)
 	}
 	y = 126
 	r = x + y
 	if r != -1 {
-		t.Errorf("-127 + 126 = %d, want -1", r)
+		t.Errorf("-127 %s 126 = %d, want -1", "+", r)
 	}
 	y = 127
 	r = x + y
 	if r != 0 {
-		t.Errorf("-127 + 127 = %d, want 0", r)
+		t.Errorf("-127 %s 127 = %d, want 0", "+", r)
 	}
 	x = -1
 	y = -128
 	r = x + y
 	if r != 127 {
-		t.Errorf("-1 + -128 = %d, want 127", r)
+		t.Errorf("-1 %s -128 = %d, want 127", "+", r)
 	}
 	y = -127
 	r = x + y
 	if r != -128 {
-		t.Errorf("-1 + -127 = %d, want -128", r)
+		t.Errorf("-1 %s -127 = %d, want -128", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -2 {
-		t.Errorf("-1 + -1 = %d, want -2", r)
+		t.Errorf("-1 %s -1 = %d, want -2", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != -1 {
-		t.Errorf("-1 + 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 0 {
-		t.Errorf("-1 + 1 = %d, want 0", r)
+		t.Errorf("-1 %s 1 = %d, want 0", "+", r)
 	}
 	y = 126
 	r = x + y
 	if r != 125 {
-		t.Errorf("-1 + 126 = %d, want 125", r)
+		t.Errorf("-1 %s 126 = %d, want 125", "+", r)
 	}
 	y = 127
 	r = x + y
 	if r != 126 {
-		t.Errorf("-1 + 127 = %d, want 126", r)
+		t.Errorf("-1 %s 127 = %d, want 126", "+", r)
 	}
 	x = 0
 	y = -128
 	r = x + y
 	if r != -128 {
-		t.Errorf("0 + -128 = %d, want -128", r)
+		t.Errorf("0 %s -128 = %d, want -128", "+", r)
 	}
 	y = -127
 	r = x + y
 	if r != -127 {
-		t.Errorf("0 + -127 = %d, want -127", r)
+		t.Errorf("0 %s -127 = %d, want -127", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != -1 {
-		t.Errorf("0 + -1 = %d, want -1", r)
+		t.Errorf("0 %s -1 = %d, want -1", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 0 {
-		t.Errorf("0 + 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 1 {
-		t.Errorf("0 + 1 = %d, want 1", r)
+		t.Errorf("0 %s 1 = %d, want 1", "+", r)
 	}
 	y = 126
 	r = x + y
 	if r != 126 {
-		t.Errorf("0 + 126 = %d, want 126", r)
+		t.Errorf("0 %s 126 = %d, want 126", "+", r)
 	}
 	y = 127
 	r = x + y
 	if r != 127 {
-		t.Errorf("0 + 127 = %d, want 127", r)
+		t.Errorf("0 %s 127 = %d, want 127", "+", r)
 	}
 	x = 1
 	y = -128
 	r = x + y
 	if r != -127 {
-		t.Errorf("1 + -128 = %d, want -127", r)
+		t.Errorf("1 %s -128 = %d, want -127", "+", r)
 	}
 	y = -127
 	r = x + y
 	if r != -126 {
-		t.Errorf("1 + -127 = %d, want -126", r)
+		t.Errorf("1 %s -127 = %d, want -126", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 0 {
-		t.Errorf("1 + -1 = %d, want 0", r)
+		t.Errorf("1 %s -1 = %d, want 0", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 1 {
-		t.Errorf("1 + 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 2 {
-		t.Errorf("1 + 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "+", r)
 	}
 	y = 126
 	r = x + y
 	if r != 127 {
-		t.Errorf("1 + 126 = %d, want 127", r)
+		t.Errorf("1 %s 126 = %d, want 127", "+", r)
 	}
 	y = 127
 	r = x + y
 	if r != -128 {
-		t.Errorf("1 + 127 = %d, want -128", r)
+		t.Errorf("1 %s 127 = %d, want -128", "+", r)
 	}
 	x = 126
 	y = -128
 	r = x + y
 	if r != -2 {
-		t.Errorf("126 + -128 = %d, want -2", r)
+		t.Errorf("126 %s -128 = %d, want -2", "+", r)
 	}
 	y = -127
 	r = x + y
 	if r != -1 {
-		t.Errorf("126 + -127 = %d, want -1", r)
+		t.Errorf("126 %s -127 = %d, want -1", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 125 {
-		t.Errorf("126 + -1 = %d, want 125", r)
+		t.Errorf("126 %s -1 = %d, want 125", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 126 {
-		t.Errorf("126 + 0 = %d, want 126", r)
+		t.Errorf("126 %s 0 = %d, want 126", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != 127 {
-		t.Errorf("126 + 1 = %d, want 127", r)
+		t.Errorf("126 %s 1 = %d, want 127", "+", r)
 	}
 	y = 126
 	r = x + y
 	if r != -4 {
-		t.Errorf("126 + 126 = %d, want -4", r)
+		t.Errorf("126 %s 126 = %d, want -4", "+", r)
 	}
 	y = 127
 	r = x + y
 	if r != -3 {
-		t.Errorf("126 + 127 = %d, want -3", r)
+		t.Errorf("126 %s 127 = %d, want -3", "+", r)
 	}
 	x = 127
 	y = -128
 	r = x + y
 	if r != -1 {
-		t.Errorf("127 + -128 = %d, want -1", r)
+		t.Errorf("127 %s -128 = %d, want -1", "+", r)
 	}
 	y = -127
 	r = x + y
 	if r != 0 {
-		t.Errorf("127 + -127 = %d, want 0", r)
+		t.Errorf("127 %s -127 = %d, want 0", "+", r)
 	}
 	y = -1
 	r = x + y
 	if r != 126 {
-		t.Errorf("127 + -1 = %d, want 126", r)
+		t.Errorf("127 %s -1 = %d, want 126", "+", r)
 	}
 	y = 0
 	r = x + y
 	if r != 127 {
-		t.Errorf("127 + 0 = %d, want 127", r)
+		t.Errorf("127 %s 0 = %d, want 127", "+", r)
 	}
 	y = 1
 	r = x + y
 	if r != -128 {
-		t.Errorf("127 + 1 = %d, want -128", r)
+		t.Errorf("127 %s 1 = %d, want -128", "+", r)
 	}
 	y = 126
 	r = x + y
 	if r != -3 {
-		t.Errorf("127 + 126 = %d, want -3", r)
+		t.Errorf("127 %s 126 = %d, want -3", "+", r)
 	}
 	y = 127
 	r = x + y
 	if r != -2 {
-		t.Errorf("127 + 127 = %d, want -2", r)
+		t.Errorf("127 %s 127 = %d, want -2", "+", r)
 	}
 }
 func TestConstFoldint8sub(t *testing.T) {
@@ -5418,253 +5418,253 @@ func TestConstFoldint8sub(t *testing.T) {
 	y = -128
 	r = x - y
 	if r != 0 {
-		t.Errorf("-128 - -128 = %d, want 0", r)
+		t.Errorf("-128 %s -128 = %d, want 0", "-", r)
 	}
 	y = -127
 	r = x - y
 	if r != -1 {
-		t.Errorf("-128 - -127 = %d, want -1", r)
+		t.Errorf("-128 %s -127 = %d, want -1", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -127 {
-		t.Errorf("-128 - -1 = %d, want -127", r)
+		t.Errorf("-128 %s -1 = %d, want -127", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -128 {
-		t.Errorf("-128 - 0 = %d, want -128", r)
+		t.Errorf("-128 %s 0 = %d, want -128", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 127 {
-		t.Errorf("-128 - 1 = %d, want 127", r)
+		t.Errorf("-128 %s 1 = %d, want 127", "-", r)
 	}
 	y = 126
 	r = x - y
 	if r != 2 {
-		t.Errorf("-128 - 126 = %d, want 2", r)
+		t.Errorf("-128 %s 126 = %d, want 2", "-", r)
 	}
 	y = 127
 	r = x - y
 	if r != 1 {
-		t.Errorf("-128 - 127 = %d, want 1", r)
+		t.Errorf("-128 %s 127 = %d, want 1", "-", r)
 	}
 	x = -127
 	y = -128
 	r = x - y
 	if r != 1 {
-		t.Errorf("-127 - -128 = %d, want 1", r)
+		t.Errorf("-127 %s -128 = %d, want 1", "-", r)
 	}
 	y = -127
 	r = x - y
 	if r != 0 {
-		t.Errorf("-127 - -127 = %d, want 0", r)
+		t.Errorf("-127 %s -127 = %d, want 0", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -126 {
-		t.Errorf("-127 - -1 = %d, want -126", r)
+		t.Errorf("-127 %s -1 = %d, want -126", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -127 {
-		t.Errorf("-127 - 0 = %d, want -127", r)
+		t.Errorf("-127 %s 0 = %d, want -127", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -128 {
-		t.Errorf("-127 - 1 = %d, want -128", r)
+		t.Errorf("-127 %s 1 = %d, want -128", "-", r)
 	}
 	y = 126
 	r = x - y
 	if r != 3 {
-		t.Errorf("-127 - 126 = %d, want 3", r)
+		t.Errorf("-127 %s 126 = %d, want 3", "-", r)
 	}
 	y = 127
 	r = x - y
 	if r != 2 {
-		t.Errorf("-127 - 127 = %d, want 2", r)
+		t.Errorf("-127 %s 127 = %d, want 2", "-", r)
 	}
 	x = -1
 	y = -128
 	r = x - y
 	if r != 127 {
-		t.Errorf("-1 - -128 = %d, want 127", r)
+		t.Errorf("-1 %s -128 = %d, want 127", "-", r)
 	}
 	y = -127
 	r = x - y
 	if r != 126 {
-		t.Errorf("-1 - -127 = %d, want 126", r)
+		t.Errorf("-1 %s -127 = %d, want 126", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 0 {
-		t.Errorf("-1 - -1 = %d, want 0", r)
+		t.Errorf("-1 %s -1 = %d, want 0", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != -1 {
-		t.Errorf("-1 - 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -2 {
-		t.Errorf("-1 - 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "-", r)
 	}
 	y = 126
 	r = x - y
 	if r != -127 {
-		t.Errorf("-1 - 126 = %d, want -127", r)
+		t.Errorf("-1 %s 126 = %d, want -127", "-", r)
 	}
 	y = 127
 	r = x - y
 	if r != -128 {
-		t.Errorf("-1 - 127 = %d, want -128", r)
+		t.Errorf("-1 %s 127 = %d, want -128", "-", r)
 	}
 	x = 0
 	y = -128
 	r = x - y
 	if r != -128 {
-		t.Errorf("0 - -128 = %d, want -128", r)
+		t.Errorf("0 %s -128 = %d, want -128", "-", r)
 	}
 	y = -127
 	r = x - y
 	if r != 127 {
-		t.Errorf("0 - -127 = %d, want 127", r)
+		t.Errorf("0 %s -127 = %d, want 127", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 1 {
-		t.Errorf("0 - -1 = %d, want 1", r)
+		t.Errorf("0 %s -1 = %d, want 1", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 0 {
-		t.Errorf("0 - 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != -1 {
-		t.Errorf("0 - 1 = %d, want -1", r)
+		t.Errorf("0 %s 1 = %d, want -1", "-", r)
 	}
 	y = 126
 	r = x - y
 	if r != -126 {
-		t.Errorf("0 - 126 = %d, want -126", r)
+		t.Errorf("0 %s 126 = %d, want -126", "-", r)
 	}
 	y = 127
 	r = x - y
 	if r != -127 {
-		t.Errorf("0 - 127 = %d, want -127", r)
+		t.Errorf("0 %s 127 = %d, want -127", "-", r)
 	}
 	x = 1
 	y = -128
 	r = x - y
 	if r != -127 {
-		t.Errorf("1 - -128 = %d, want -127", r)
+		t.Errorf("1 %s -128 = %d, want -127", "-", r)
 	}
 	y = -127
 	r = x - y
 	if r != -128 {
-		t.Errorf("1 - -127 = %d, want -128", r)
+		t.Errorf("1 %s -127 = %d, want -128", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 2 {
-		t.Errorf("1 - -1 = %d, want 2", r)
+		t.Errorf("1 %s -1 = %d, want 2", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 1 {
-		t.Errorf("1 - 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 0 {
-		t.Errorf("1 - 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "-", r)
 	}
 	y = 126
 	r = x - y
 	if r != -125 {
-		t.Errorf("1 - 126 = %d, want -125", r)
+		t.Errorf("1 %s 126 = %d, want -125", "-", r)
 	}
 	y = 127
 	r = x - y
 	if r != -126 {
-		t.Errorf("1 - 127 = %d, want -126", r)
+		t.Errorf("1 %s 127 = %d, want -126", "-", r)
 	}
 	x = 126
 	y = -128
 	r = x - y
 	if r != -2 {
-		t.Errorf("126 - -128 = %d, want -2", r)
+		t.Errorf("126 %s -128 = %d, want -2", "-", r)
 	}
 	y = -127
 	r = x - y
 	if r != -3 {
-		t.Errorf("126 - -127 = %d, want -3", r)
+		t.Errorf("126 %s -127 = %d, want -3", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != 127 {
-		t.Errorf("126 - -1 = %d, want 127", r)
+		t.Errorf("126 %s -1 = %d, want 127", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 126 {
-		t.Errorf("126 - 0 = %d, want 126", r)
+		t.Errorf("126 %s 0 = %d, want 126", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 125 {
-		t.Errorf("126 - 1 = %d, want 125", r)
+		t.Errorf("126 %s 1 = %d, want 125", "-", r)
 	}
 	y = 126
 	r = x - y
 	if r != 0 {
-		t.Errorf("126 - 126 = %d, want 0", r)
+		t.Errorf("126 %s 126 = %d, want 0", "-", r)
 	}
 	y = 127
 	r = x - y
 	if r != -1 {
-		t.Errorf("126 - 127 = %d, want -1", r)
+		t.Errorf("126 %s 127 = %d, want -1", "-", r)
 	}
 	x = 127
 	y = -128
 	r = x - y
 	if r != -1 {
-		t.Errorf("127 - -128 = %d, want -1", r)
+		t.Errorf("127 %s -128 = %d, want -1", "-", r)
 	}
 	y = -127
 	r = x - y
 	if r != -2 {
-		t.Errorf("127 - -127 = %d, want -2", r)
+		t.Errorf("127 %s -127 = %d, want -2", "-", r)
 	}
 	y = -1
 	r = x - y
 	if r != -128 {
-		t.Errorf("127 - -1 = %d, want -128", r)
+		t.Errorf("127 %s -1 = %d, want -128", "-", r)
 	}
 	y = 0
 	r = x - y
 	if r != 127 {
-		t.Errorf("127 - 0 = %d, want 127", r)
+		t.Errorf("127 %s 0 = %d, want 127", "-", r)
 	}
 	y = 1
 	r = x - y
 	if r != 126 {
-		t.Errorf("127 - 1 = %d, want 126", r)
+		t.Errorf("127 %s 1 = %d, want 126", "-", r)
 	}
 	y = 126
 	r = x - y
 	if r != 1 {
-		t.Errorf("127 - 126 = %d, want 1", r)
+		t.Errorf("127 %s 126 = %d, want 1", "-", r)
 	}
 	y = 127
 	r = x - y
 	if r != 0 {
-		t.Errorf("127 - 127 = %d, want 0", r)
+		t.Errorf("127 %s 127 = %d, want 0", "-", r)
 	}
 }
 func TestConstFoldint8div(t *testing.T) {
@@ -5673,218 +5673,218 @@ func TestConstFoldint8div(t *testing.T) {
 	y = -128
 	r = x / y
 	if r != 1 {
-		t.Errorf("-128 / -128 = %d, want 1", r)
+		t.Errorf("-128 %s -128 = %d, want 1", "/", r)
 	}
 	y = -127
 	r = x / y
 	if r != 1 {
-		t.Errorf("-128 / -127 = %d, want 1", r)
+		t.Errorf("-128 %s -127 = %d, want 1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -128 {
-		t.Errorf("-128 / -1 = %d, want -128", r)
+		t.Errorf("-128 %s -1 = %d, want -128", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -128 {
-		t.Errorf("-128 / 1 = %d, want -128", r)
+		t.Errorf("-128 %s 1 = %d, want -128", "/", r)
 	}
 	y = 126
 	r = x / y
 	if r != -1 {
-		t.Errorf("-128 / 126 = %d, want -1", r)
+		t.Errorf("-128 %s 126 = %d, want -1", "/", r)
 	}
 	y = 127
 	r = x / y
 	if r != -1 {
-		t.Errorf("-128 / 127 = %d, want -1", r)
+		t.Errorf("-128 %s 127 = %d, want -1", "/", r)
 	}
 	x = -127
 	y = -128
 	r = x / y
 	if r != 0 {
-		t.Errorf("-127 / -128 = %d, want 0", r)
+		t.Errorf("-127 %s -128 = %d, want 0", "/", r)
 	}
 	y = -127
 	r = x / y
 	if r != 1 {
-		t.Errorf("-127 / -127 = %d, want 1", r)
+		t.Errorf("-127 %s -127 = %d, want 1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 127 {
-		t.Errorf("-127 / -1 = %d, want 127", r)
+		t.Errorf("-127 %s -1 = %d, want 127", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -127 {
-		t.Errorf("-127 / 1 = %d, want -127", r)
+		t.Errorf("-127 %s 1 = %d, want -127", "/", r)
 	}
 	y = 126
 	r = x / y
 	if r != -1 {
-		t.Errorf("-127 / 126 = %d, want -1", r)
+		t.Errorf("-127 %s 126 = %d, want -1", "/", r)
 	}
 	y = 127
 	r = x / y
 	if r != -1 {
-		t.Errorf("-127 / 127 = %d, want -1", r)
+		t.Errorf("-127 %s 127 = %d, want -1", "/", r)
 	}
 	x = -1
 	y = -128
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / -128 = %d, want 0", r)
+		t.Errorf("-1 %s -128 = %d, want 0", "/", r)
 	}
 	y = -127
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / -127 = %d, want 0", r)
+		t.Errorf("-1 %s -127 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 1 {
-		t.Errorf("-1 / -1 = %d, want 1", r)
+		t.Errorf("-1 %s -1 = %d, want 1", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != -1 {
-		t.Errorf("-1 / 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", "/", r)
 	}
 	y = 126
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / 126 = %d, want 0", r)
+		t.Errorf("-1 %s 126 = %d, want 0", "/", r)
 	}
 	y = 127
 	r = x / y
 	if r != 0 {
-		t.Errorf("-1 / 127 = %d, want 0", r)
+		t.Errorf("-1 %s 127 = %d, want 0", "/", r)
 	}
 	x = 0
 	y = -128
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -128 = %d, want 0", r)
+		t.Errorf("0 %s -128 = %d, want 0", "/", r)
 	}
 	y = -127
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -127 = %d, want 0", r)
+		t.Errorf("0 %s -127 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "/", r)
 	}
 	y = 126
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 126 = %d, want 0", r)
+		t.Errorf("0 %s 126 = %d, want 0", "/", r)
 	}
 	y = 127
 	r = x / y
 	if r != 0 {
-		t.Errorf("0 / 127 = %d, want 0", r)
+		t.Errorf("0 %s 127 = %d, want 0", "/", r)
 	}
 	x = 1
 	y = -128
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / -128 = %d, want 0", r)
+		t.Errorf("1 %s -128 = %d, want 0", "/", r)
 	}
 	y = -127
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / -127 = %d, want 0", r)
+		t.Errorf("1 %s -127 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -1 {
-		t.Errorf("1 / -1 = %d, want -1", r)
+		t.Errorf("1 %s -1 = %d, want -1", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 1 {
-		t.Errorf("1 / 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "/", r)
 	}
 	y = 126
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 126 = %d, want 0", r)
+		t.Errorf("1 %s 126 = %d, want 0", "/", r)
 	}
 	y = 127
 	r = x / y
 	if r != 0 {
-		t.Errorf("1 / 127 = %d, want 0", r)
+		t.Errorf("1 %s 127 = %d, want 0", "/", r)
 	}
 	x = 126
 	y = -128
 	r = x / y
 	if r != 0 {
-		t.Errorf("126 / -128 = %d, want 0", r)
+		t.Errorf("126 %s -128 = %d, want 0", "/", r)
 	}
 	y = -127
 	r = x / y
 	if r != 0 {
-		t.Errorf("126 / -127 = %d, want 0", r)
+		t.Errorf("126 %s -127 = %d, want 0", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -126 {
-		t.Errorf("126 / -1 = %d, want -126", r)
+		t.Errorf("126 %s -1 = %d, want -126", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 126 {
-		t.Errorf("126 / 1 = %d, want 126", r)
+		t.Errorf("126 %s 1 = %d, want 126", "/", r)
 	}
 	y = 126
 	r = x / y
 	if r != 1 {
-		t.Errorf("126 / 126 = %d, want 1", r)
+		t.Errorf("126 %s 126 = %d, want 1", "/", r)
 	}
 	y = 127
 	r = x / y
 	if r != 0 {
-		t.Errorf("126 / 127 = %d, want 0", r)
+		t.Errorf("126 %s 127 = %d, want 0", "/", r)
 	}
 	x = 127
 	y = -128
 	r = x / y
 	if r != 0 {
-		t.Errorf("127 / -128 = %d, want 0", r)
+		t.Errorf("127 %s -128 = %d, want 0", "/", r)
 	}
 	y = -127
 	r = x / y
 	if r != -1 {
-		t.Errorf("127 / -127 = %d, want -1", r)
+		t.Errorf("127 %s -127 = %d, want -1", "/", r)
 	}
 	y = -1
 	r = x / y
 	if r != -127 {
-		t.Errorf("127 / -1 = %d, want -127", r)
+		t.Errorf("127 %s -1 = %d, want -127", "/", r)
 	}
 	y = 1
 	r = x / y
 	if r != 127 {
-		t.Errorf("127 / 1 = %d, want 127", r)
+		t.Errorf("127 %s 1 = %d, want 127", "/", r)
 	}
 	y = 126
 	r = x / y
 	if r != 1 {
-		t.Errorf("127 / 126 = %d, want 1", r)
+		t.Errorf("127 %s 126 = %d, want 1", "/", r)
 	}
 	y = 127
 	r = x / y
 	if r != 1 {
-		t.Errorf("127 / 127 = %d, want 1", r)
+		t.Errorf("127 %s 127 = %d, want 1", "/", r)
 	}
 }
 func TestConstFoldint8mul(t *testing.T) {
@@ -5893,253 +5893,253 @@ func TestConstFoldint8mul(t *testing.T) {
 	y = -128
 	r = x * y
 	if r != 0 {
-		t.Errorf("-128 * -128 = %d, want 0", r)
+		t.Errorf("-128 %s -128 = %d, want 0", "*", r)
 	}
 	y = -127
 	r = x * y
 	if r != -128 {
-		t.Errorf("-128 * -127 = %d, want -128", r)
+		t.Errorf("-128 %s -127 = %d, want -128", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -128 {
-		t.Errorf("-128 * -1 = %d, want -128", r)
+		t.Errorf("-128 %s -1 = %d, want -128", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-128 * 0 = %d, want 0", r)
+		t.Errorf("-128 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -128 {
-		t.Errorf("-128 * 1 = %d, want -128", r)
+		t.Errorf("-128 %s 1 = %d, want -128", "*", r)
 	}
 	y = 126
 	r = x * y
 	if r != 0 {
-		t.Errorf("-128 * 126 = %d, want 0", r)
+		t.Errorf("-128 %s 126 = %d, want 0", "*", r)
 	}
 	y = 127
 	r = x * y
 	if r != -128 {
-		t.Errorf("-128 * 127 = %d, want -128", r)
+		t.Errorf("-128 %s 127 = %d, want -128", "*", r)
 	}
 	x = -127
 	y = -128
 	r = x * y
 	if r != -128 {
-		t.Errorf("-127 * -128 = %d, want -128", r)
+		t.Errorf("-127 %s -128 = %d, want -128", "*", r)
 	}
 	y = -127
 	r = x * y
 	if r != 1 {
-		t.Errorf("-127 * -127 = %d, want 1", r)
+		t.Errorf("-127 %s -127 = %d, want 1", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 127 {
-		t.Errorf("-127 * -1 = %d, want 127", r)
+		t.Errorf("-127 %s -1 = %d, want 127", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-127 * 0 = %d, want 0", r)
+		t.Errorf("-127 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -127 {
-		t.Errorf("-127 * 1 = %d, want -127", r)
+		t.Errorf("-127 %s 1 = %d, want -127", "*", r)
 	}
 	y = 126
 	r = x * y
 	if r != 126 {
-		t.Errorf("-127 * 126 = %d, want 126", r)
+		t.Errorf("-127 %s 126 = %d, want 126", "*", r)
 	}
 	y = 127
 	r = x * y
 	if r != -1 {
-		t.Errorf("-127 * 127 = %d, want -1", r)
+		t.Errorf("-127 %s 127 = %d, want -1", "*", r)
 	}
 	x = -1
 	y = -128
 	r = x * y
 	if r != -128 {
-		t.Errorf("-1 * -128 = %d, want -128", r)
+		t.Errorf("-1 %s -128 = %d, want -128", "*", r)
 	}
 	y = -127
 	r = x * y
 	if r != 127 {
-		t.Errorf("-1 * -127 = %d, want 127", r)
+		t.Errorf("-1 %s -127 = %d, want 127", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 1 {
-		t.Errorf("-1 * -1 = %d, want 1", r)
+		t.Errorf("-1 %s -1 = %d, want 1", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("-1 * 0 = %d, want 0", r)
+		t.Errorf("-1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != -1 {
-		t.Errorf("-1 * 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", "*", r)
 	}
 	y = 126
 	r = x * y
 	if r != -126 {
-		t.Errorf("-1 * 126 = %d, want -126", r)
+		t.Errorf("-1 %s 126 = %d, want -126", "*", r)
 	}
 	y = 127
 	r = x * y
 	if r != -127 {
-		t.Errorf("-1 * 127 = %d, want -127", r)
+		t.Errorf("-1 %s 127 = %d, want -127", "*", r)
 	}
 	x = 0
 	y = -128
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -128 = %d, want 0", r)
+		t.Errorf("0 %s -128 = %d, want 0", "*", r)
 	}
 	y = -127
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -127 = %d, want 0", r)
+		t.Errorf("0 %s -127 = %d, want 0", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "*", r)
 	}
 	y = 126
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 126 = %d, want 0", r)
+		t.Errorf("0 %s 126 = %d, want 0", "*", r)
 	}
 	y = 127
 	r = x * y
 	if r != 0 {
-		t.Errorf("0 * 127 = %d, want 0", r)
+		t.Errorf("0 %s 127 = %d, want 0", "*", r)
 	}
 	x = 1
 	y = -128
 	r = x * y
 	if r != -128 {
-		t.Errorf("1 * -128 = %d, want -128", r)
+		t.Errorf("1 %s -128 = %d, want -128", "*", r)
 	}
 	y = -127
 	r = x * y
 	if r != -127 {
-		t.Errorf("1 * -127 = %d, want -127", r)
+		t.Errorf("1 %s -127 = %d, want -127", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -1 {
-		t.Errorf("1 * -1 = %d, want -1", r)
+		t.Errorf("1 %s -1 = %d, want -1", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("1 * 0 = %d, want 0", r)
+		t.Errorf("1 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 1 {
-		t.Errorf("1 * 1 = %d, want 1", r)
+		t.Errorf("1 %s 1 = %d, want 1", "*", r)
 	}
 	y = 126
 	r = x * y
 	if r != 126 {
-		t.Errorf("1 * 126 = %d, want 126", r)
+		t.Errorf("1 %s 126 = %d, want 126", "*", r)
 	}
 	y = 127
 	r = x * y
 	if r != 127 {
-		t.Errorf("1 * 127 = %d, want 127", r)
+		t.Errorf("1 %s 127 = %d, want 127", "*", r)
 	}
 	x = 126
 	y = -128
 	r = x * y
 	if r != 0 {
-		t.Errorf("126 * -128 = %d, want 0", r)
+		t.Errorf("126 %s -128 = %d, want 0", "*", r)
 	}
 	y = -127
 	r = x * y
 	if r != 126 {
-		t.Errorf("126 * -127 = %d, want 126", r)
+		t.Errorf("126 %s -127 = %d, want 126", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -126 {
-		t.Errorf("126 * -1 = %d, want -126", r)
+		t.Errorf("126 %s -1 = %d, want -126", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("126 * 0 = %d, want 0", r)
+		t.Errorf("126 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 126 {
-		t.Errorf("126 * 1 = %d, want 126", r)
+		t.Errorf("126 %s 1 = %d, want 126", "*", r)
 	}
 	y = 126
 	r = x * y
 	if r != 4 {
-		t.Errorf("126 * 126 = %d, want 4", r)
+		t.Errorf("126 %s 126 = %d, want 4", "*", r)
 	}
 	y = 127
 	r = x * y
 	if r != -126 {
-		t.Errorf("126 * 127 = %d, want -126", r)
+		t.Errorf("126 %s 127 = %d, want -126", "*", r)
 	}
 	x = 127
 	y = -128
 	r = x * y
 	if r != -128 {
-		t.Errorf("127 * -128 = %d, want -128", r)
+		t.Errorf("127 %s -128 = %d, want -128", "*", r)
 	}
 	y = -127
 	r = x * y
 	if r != -1 {
-		t.Errorf("127 * -127 = %d, want -1", r)
+		t.Errorf("127 %s -127 = %d, want -1", "*", r)
 	}
 	y = -1
 	r = x * y
 	if r != -127 {
-		t.Errorf("127 * -1 = %d, want -127", r)
+		t.Errorf("127 %s -1 = %d, want -127", "*", r)
 	}
 	y = 0
 	r = x * y
 	if r != 0 {
-		t.Errorf("127 * 0 = %d, want 0", r)
+		t.Errorf("127 %s 0 = %d, want 0", "*", r)
 	}
 	y = 1
 	r = x * y
 	if r != 127 {
-		t.Errorf("127 * 1 = %d, want 127", r)
+		t.Errorf("127 %s 1 = %d, want 127", "*", r)
 	}
 	y = 126
 	r = x * y
 	if r != -126 {
-		t.Errorf("127 * 126 = %d, want -126", r)
+		t.Errorf("127 %s 126 = %d, want -126", "*", r)
 	}
 	y = 127
 	r = x * y
 	if r != 1 {
-		t.Errorf("127 * 127 = %d, want 1", r)
+		t.Errorf("127 %s 127 = %d, want 1", "*", r)
 	}
 }
 func TestConstFoldint8mod(t *testing.T) {
@@ -6148,218 +6148,218 @@ func TestConstFoldint8mod(t *testing.T) {
 	y = -128
 	r = x % y
 	if r != 0 {
-		t.Errorf("-128 % -128 = %d, want 0", r)
+		t.Errorf("-128 %s -128 = %d, want 0", "%", r)
 	}
 	y = -127
 	r = x % y
 	if r != -1 {
-		t.Errorf("-128 % -127 = %d, want -1", r)
+		t.Errorf("-128 %s -127 = %d, want -1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-128 % -1 = %d, want 0", r)
+		t.Errorf("-128 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-128 % 1 = %d, want 0", r)
+		t.Errorf("-128 %s 1 = %d, want 0", "%", r)
 	}
 	y = 126
 	r = x % y
 	if r != -2 {
-		t.Errorf("-128 % 126 = %d, want -2", r)
+		t.Errorf("-128 %s 126 = %d, want -2", "%", r)
 	}
 	y = 127
 	r = x % y
 	if r != -1 {
-		t.Errorf("-128 % 127 = %d, want -1", r)
+		t.Errorf("-128 %s 127 = %d, want -1", "%", r)
 	}
 	x = -127
 	y = -128
 	r = x % y
 	if r != -127 {
-		t.Errorf("-127 % -128 = %d, want -127", r)
+		t.Errorf("-127 %s -128 = %d, want -127", "%", r)
 	}
 	y = -127
 	r = x % y
 	if r != 0 {
-		t.Errorf("-127 % -127 = %d, want 0", r)
+		t.Errorf("-127 %s -127 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-127 % -1 = %d, want 0", r)
+		t.Errorf("-127 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-127 % 1 = %d, want 0", r)
+		t.Errorf("-127 %s 1 = %d, want 0", "%", r)
 	}
 	y = 126
 	r = x % y
 	if r != -1 {
-		t.Errorf("-127 % 126 = %d, want -1", r)
+		t.Errorf("-127 %s 126 = %d, want -1", "%", r)
 	}
 	y = 127
 	r = x % y
 	if r != 0 {
-		t.Errorf("-127 % 127 = %d, want 0", r)
+		t.Errorf("-127 %s 127 = %d, want 0", "%", r)
 	}
 	x = -1
 	y = -128
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % -128 = %d, want -1", r)
+		t.Errorf("-1 %s -128 = %d, want -1", "%", r)
 	}
 	y = -127
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % -127 = %d, want -1", r)
+		t.Errorf("-1 %s -127 = %d, want -1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-1 % -1 = %d, want 0", r)
+		t.Errorf("-1 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("-1 % 1 = %d, want 0", r)
+		t.Errorf("-1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 126
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % 126 = %d, want -1", r)
+		t.Errorf("-1 %s 126 = %d, want -1", "%", r)
 	}
 	y = 127
 	r = x % y
 	if r != -1 {
-		t.Errorf("-1 % 127 = %d, want -1", r)
+		t.Errorf("-1 %s 127 = %d, want -1", "%", r)
 	}
 	x = 0
 	y = -128
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -128 = %d, want 0", r)
+		t.Errorf("0 %s -128 = %d, want 0", "%", r)
 	}
 	y = -127
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -127 = %d, want 0", r)
+		t.Errorf("0 %s -127 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % -1 = %d, want 0", r)
+		t.Errorf("0 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "%", r)
 	}
 	y = 126
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 126 = %d, want 0", r)
+		t.Errorf("0 %s 126 = %d, want 0", "%", r)
 	}
 	y = 127
 	r = x % y
 	if r != 0 {
-		t.Errorf("0 % 127 = %d, want 0", r)
+		t.Errorf("0 %s 127 = %d, want 0", "%", r)
 	}
 	x = 1
 	y = -128
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % -128 = %d, want 1", r)
+		t.Errorf("1 %s -128 = %d, want 1", "%", r)
 	}
 	y = -127
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % -127 = %d, want 1", r)
+		t.Errorf("1 %s -127 = %d, want 1", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % -1 = %d, want 0", r)
+		t.Errorf("1 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("1 % 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", "%", r)
 	}
 	y = 126
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 126 = %d, want 1", r)
+		t.Errorf("1 %s 126 = %d, want 1", "%", r)
 	}
 	y = 127
 	r = x % y
 	if r != 1 {
-		t.Errorf("1 % 127 = %d, want 1", r)
+		t.Errorf("1 %s 127 = %d, want 1", "%", r)
 	}
 	x = 126
 	y = -128
 	r = x % y
 	if r != 126 {
-		t.Errorf("126 % -128 = %d, want 126", r)
+		t.Errorf("126 %s -128 = %d, want 126", "%", r)
 	}
 	y = -127
 	r = x % y
 	if r != 126 {
-		t.Errorf("126 % -127 = %d, want 126", r)
+		t.Errorf("126 %s -127 = %d, want 126", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("126 % -1 = %d, want 0", r)
+		t.Errorf("126 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("126 % 1 = %d, want 0", r)
+		t.Errorf("126 %s 1 = %d, want 0", "%", r)
 	}
 	y = 126
 	r = x % y
 	if r != 0 {
-		t.Errorf("126 % 126 = %d, want 0", r)
+		t.Errorf("126 %s 126 = %d, want 0", "%", r)
 	}
 	y = 127
 	r = x % y
 	if r != 126 {
-		t.Errorf("126 % 127 = %d, want 126", r)
+		t.Errorf("126 %s 127 = %d, want 126", "%", r)
 	}
 	x = 127
 	y = -128
 	r = x % y
 	if r != 127 {
-		t.Errorf("127 % -128 = %d, want 127", r)
+		t.Errorf("127 %s -128 = %d, want 127", "%", r)
 	}
 	y = -127
 	r = x % y
 	if r != 0 {
-		t.Errorf("127 % -127 = %d, want 0", r)
+		t.Errorf("127 %s -127 = %d, want 0", "%", r)
 	}
 	y = -1
 	r = x % y
 	if r != 0 {
-		t.Errorf("127 % -1 = %d, want 0", r)
+		t.Errorf("127 %s -1 = %d, want 0", "%", r)
 	}
 	y = 1
 	r = x % y
 	if r != 0 {
-		t.Errorf("127 % 1 = %d, want 0", r)
+		t.Errorf("127 %s 1 = %d, want 0", "%", r)
 	}
 	y = 126
 	r = x % y
 	if r != 1 {
-		t.Errorf("127 % 126 = %d, want 1", r)
+		t.Errorf("127 %s 126 = %d, want 1", "%", r)
 	}
 	y = 127
 	r = x % y
 	if r != 0 {
-		t.Errorf("127 % 127 = %d, want 0", r)
+		t.Errorf("127 %s 127 = %d, want 0", "%", r)
 	}
 }
 func TestConstFolduint64uint64lsh(t *testing.T) {
@@ -6369,85 +6369,85 @@ func TestConstFolduint64uint64lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x << y
 	if r != 4294967296 {
-		t.Errorf("4294967296 << 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 8589934592 {
-		t.Errorf("4294967296 << 1 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s 1 = %d, want 8589934592", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967296 << 4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967296 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("4294967296 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x << y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 << 0 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 18446744073709551615", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 18446744073709551614 {
-		t.Errorf("18446744073709551615 << 1 = %d, want 18446744073709551614", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 18446744073709551614", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("18446744073709551615 << 4294967296 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("18446744073709551615 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint64uint64rsh(t *testing.T) {
@@ -6457,85 +6457,85 @@ func TestConstFolduint64uint64rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x >> y
 	if r != 4294967296 {
-		t.Errorf("4294967296 >> 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483648 {
-		t.Errorf("4294967296 >> 1 = %d, want 2147483648", r)
+		t.Errorf("4294967296 %s 1 = %d, want 2147483648", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967296 >> 4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967296 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("4294967296 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x >> y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 >> 0 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 18446744073709551615", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 9223372036854775807 {
-		t.Errorf("18446744073709551615 >> 1 = %d, want 9223372036854775807", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 9223372036854775807", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("18446744073709551615 >> 4294967296 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("18446744073709551615 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint64uint32lsh(t *testing.T) {
@@ -6545,65 +6545,65 @@ func TestConstFolduint64uint32lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x << y
 	if r != 4294967296 {
-		t.Errorf("4294967296 << 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 8589934592 {
-		t.Errorf("4294967296 << 1 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s 1 = %d, want 8589934592", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967296 << 4294967295 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x << y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 << 0 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 18446744073709551615", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 18446744073709551614 {
-		t.Errorf("18446744073709551615 << 1 = %d, want 18446744073709551614", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 18446744073709551614", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("18446744073709551615 << 4294967295 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 4294967295 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint64uint32rsh(t *testing.T) {
@@ -6613,65 +6613,65 @@ func TestConstFolduint64uint32rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x >> y
 	if r != 4294967296 {
-		t.Errorf("4294967296 >> 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483648 {
-		t.Errorf("4294967296 >> 1 = %d, want 2147483648", r)
+		t.Errorf("4294967296 %s 1 = %d, want 2147483648", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967296 >> 4294967295 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x >> y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 >> 0 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 18446744073709551615", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 9223372036854775807 {
-		t.Errorf("18446744073709551615 >> 1 = %d, want 9223372036854775807", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 9223372036854775807", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("18446744073709551615 >> 4294967295 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 4294967295 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint64uint16lsh(t *testing.T) {
@@ -6681,65 +6681,65 @@ func TestConstFolduint64uint16lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x << y
 	if r != 4294967296 {
-		t.Errorf("4294967296 << 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 8589934592 {
-		t.Errorf("4294967296 << 1 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s 1 = %d, want 8589934592", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967296 << 65535 = %d, want 0", r)
+		t.Errorf("4294967296 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x << y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 << 0 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 18446744073709551615", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 18446744073709551614 {
-		t.Errorf("18446744073709551615 << 1 = %d, want 18446744073709551614", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 18446744073709551614", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("18446744073709551615 << 65535 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 65535 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint64uint16rsh(t *testing.T) {
@@ -6749,65 +6749,65 @@ func TestConstFolduint64uint16rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x >> y
 	if r != 4294967296 {
-		t.Errorf("4294967296 >> 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483648 {
-		t.Errorf("4294967296 >> 1 = %d, want 2147483648", r)
+		t.Errorf("4294967296 %s 1 = %d, want 2147483648", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967296 >> 65535 = %d, want 0", r)
+		t.Errorf("4294967296 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x >> y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 >> 0 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 18446744073709551615", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 9223372036854775807 {
-		t.Errorf("18446744073709551615 >> 1 = %d, want 9223372036854775807", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 9223372036854775807", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("18446744073709551615 >> 65535 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 65535 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint64uint8lsh(t *testing.T) {
@@ -6817,65 +6817,65 @@ func TestConstFolduint64uint8lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x << y
 	if r != 4294967296 {
-		t.Errorf("4294967296 << 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 8589934592 {
-		t.Errorf("4294967296 << 1 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s 1 = %d, want 8589934592", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967296 << 255 = %d, want 0", r)
+		t.Errorf("4294967296 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x << y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 << 0 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 18446744073709551615", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 18446744073709551614 {
-		t.Errorf("18446744073709551615 << 1 = %d, want 18446744073709551614", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 18446744073709551614", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("18446744073709551615 << 255 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 255 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint64uint8rsh(t *testing.T) {
@@ -6885,65 +6885,65 @@ func TestConstFolduint64uint8rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x >> y
 	if r != 4294967296 {
-		t.Errorf("4294967296 >> 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483648 {
-		t.Errorf("4294967296 >> 1 = %d, want 2147483648", r)
+		t.Errorf("4294967296 %s 1 = %d, want 2147483648", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967296 >> 255 = %d, want 0", r)
+		t.Errorf("4294967296 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 18446744073709551615
 	y = 0
 	r = x >> y
 	if r != 18446744073709551615 {
-		t.Errorf("18446744073709551615 >> 0 = %d, want 18446744073709551615", r)
+		t.Errorf("18446744073709551615 %s 0 = %d, want 18446744073709551615", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 9223372036854775807 {
-		t.Errorf("18446744073709551615 >> 1 = %d, want 9223372036854775807", r)
+		t.Errorf("18446744073709551615 %s 1 = %d, want 9223372036854775807", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("18446744073709551615 >> 255 = %d, want 0", r)
+		t.Errorf("18446744073709551615 %s 255 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint64uint64lsh(t *testing.T) {
@@ -6953,190 +6953,190 @@ func TestConstFoldint64uint64lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 << 0 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want -9223372036854775808", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 << 1 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 << 4294967296 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = -9223372036854775807
 	y = 0
 	r = x << y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 << 0 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want -9223372036854775807", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-9223372036854775807 << 1 = %d, want 2", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 << 4294967296 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = -4294967296
 	y = 0
 	r = x << y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 << 0 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 0 = %d, want -4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -8589934592 {
-		t.Errorf("-4294967296 << 1 = %d, want -8589934592", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -8589934592", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-4294967296 << 4294967296 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-4294967296 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 4294967296 = %d, want 0", r)
+		t.Errorf("-1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x << y
 	if r != 4294967296 {
-		t.Errorf("4294967296 << 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 8589934592 {
-		t.Errorf("4294967296 << 1 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s 1 = %d, want 8589934592", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967296 << 4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967296 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("4294967296 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 9223372036854775806
 	y = 0
 	r = x << y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 << 0 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 9223372036854775806", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("9223372036854775806 << 1 = %d, want -4", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("9223372036854775806 << 4294967296 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("9223372036854775806 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 9223372036854775807
 	y = 0
 	r = x << y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 << 0 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 9223372036854775807", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("9223372036854775807 << 1 = %d, want -2", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("9223372036854775807 << 4294967296 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("9223372036854775807 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint64uint64rsh(t *testing.T) {
@@ -7146,190 +7146,190 @@ func TestConstFoldint64uint64rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 >> 0 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want -9223372036854775808", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -4611686018427387904 {
-		t.Errorf("-9223372036854775808 >> 1 = %d, want -4611686018427387904", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want -4611686018427387904", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = -9223372036854775807
 	y = 0
 	r = x >> y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 >> 0 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want -9223372036854775807", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -4611686018427387904 {
-		t.Errorf("-9223372036854775807 >> 1 = %d, want -4611686018427387904", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want -4611686018427387904", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-9223372036854775807 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-9223372036854775807 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-9223372036854775807 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-9223372036854775807 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = -4294967296
 	y = 0
 	r = x >> y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 >> 0 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 0 = %d, want -4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -2147483648 {
-		t.Errorf("-4294967296 >> 1 = %d, want -2147483648", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -2147483648", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-4294967296 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-4294967296 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-4294967296 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-4294967296 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-1 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-1 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x >> y
 	if r != 4294967296 {
-		t.Errorf("4294967296 >> 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483648 {
-		t.Errorf("4294967296 >> 1 = %d, want 2147483648", r)
+		t.Errorf("4294967296 %s 1 = %d, want 2147483648", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967296 >> 4294967296 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967296 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("4294967296 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 9223372036854775806
 	y = 0
 	r = x >> y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 >> 0 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 9223372036854775806", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 4611686018427387903 {
-		t.Errorf("9223372036854775806 >> 1 = %d, want 4611686018427387903", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want 4611686018427387903", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("9223372036854775806 >> 4294967296 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("9223372036854775806 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 9223372036854775807
 	y = 0
 	r = x >> y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 >> 0 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 9223372036854775807", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 4611686018427387903 {
-		t.Errorf("9223372036854775807 >> 1 = %d, want 4611686018427387903", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want 4611686018427387903", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("9223372036854775807 >> 4294967296 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("9223372036854775807 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint64uint32lsh(t *testing.T) {
@@ -7339,145 +7339,145 @@ func TestConstFoldint64uint32lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 << 0 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want -9223372036854775808", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 << 1 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 << 4294967295 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = -9223372036854775807
 	y = 0
 	r = x << y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 << 0 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want -9223372036854775807", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-9223372036854775807 << 1 = %d, want 2", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 << 4294967295 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = -4294967296
 	y = 0
 	r = x << y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 << 0 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 0 = %d, want -4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -8589934592 {
-		t.Errorf("-4294967296 << 1 = %d, want -8589934592", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -8589934592", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-4294967296 << 4294967295 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 4294967295 = %d, want 0", r)
+		t.Errorf("-1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x << y
 	if r != 4294967296 {
-		t.Errorf("4294967296 << 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 8589934592 {
-		t.Errorf("4294967296 << 1 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s 1 = %d, want 8589934592", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967296 << 4294967295 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 9223372036854775806
 	y = 0
 	r = x << y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 << 0 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 9223372036854775806", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("9223372036854775806 << 1 = %d, want -4", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("9223372036854775806 << 4294967295 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 9223372036854775807
 	y = 0
 	r = x << y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 << 0 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 9223372036854775807", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("9223372036854775807 << 1 = %d, want -2", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("9223372036854775807 << 4294967295 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 4294967295 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint64uint32rsh(t *testing.T) {
@@ -7487,145 +7487,145 @@ func TestConstFoldint64uint32rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 >> 0 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want -9223372036854775808", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -4611686018427387904 {
-		t.Errorf("-9223372036854775808 >> 1 = %d, want -4611686018427387904", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want -4611686018427387904", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = -9223372036854775807
 	y = 0
 	r = x >> y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 >> 0 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want -9223372036854775807", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -4611686018427387904 {
-		t.Errorf("-9223372036854775807 >> 1 = %d, want -4611686018427387904", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want -4611686018427387904", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-9223372036854775807 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-9223372036854775807 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = -4294967296
 	y = 0
 	r = x >> y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 >> 0 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 0 = %d, want -4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -2147483648 {
-		t.Errorf("-4294967296 >> 1 = %d, want -2147483648", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -2147483648", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-4294967296 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-4294967296 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-1 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x >> y
 	if r != 4294967296 {
-		t.Errorf("4294967296 >> 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483648 {
-		t.Errorf("4294967296 >> 1 = %d, want 2147483648", r)
+		t.Errorf("4294967296 %s 1 = %d, want 2147483648", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967296 >> 4294967295 = %d, want 0", r)
+		t.Errorf("4294967296 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 9223372036854775806
 	y = 0
 	r = x >> y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 >> 0 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 9223372036854775806", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 4611686018427387903 {
-		t.Errorf("9223372036854775806 >> 1 = %d, want 4611686018427387903", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want 4611686018427387903", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("9223372036854775806 >> 4294967295 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 9223372036854775807
 	y = 0
 	r = x >> y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 >> 0 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 9223372036854775807", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 4611686018427387903 {
-		t.Errorf("9223372036854775807 >> 1 = %d, want 4611686018427387903", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want 4611686018427387903", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("9223372036854775807 >> 4294967295 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 4294967295 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint64uint16lsh(t *testing.T) {
@@ -7635,145 +7635,145 @@ func TestConstFoldint64uint16lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 << 0 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want -9223372036854775808", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 << 1 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 << 65535 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = -9223372036854775807
 	y = 0
 	r = x << y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 << 0 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want -9223372036854775807", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-9223372036854775807 << 1 = %d, want 2", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 << 65535 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = -4294967296
 	y = 0
 	r = x << y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 << 0 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 0 = %d, want -4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -8589934592 {
-		t.Errorf("-4294967296 << 1 = %d, want -8589934592", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -8589934592", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-4294967296 << 65535 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 65535 = %d, want 0", r)
+		t.Errorf("-1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x << y
 	if r != 4294967296 {
-		t.Errorf("4294967296 << 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 8589934592 {
-		t.Errorf("4294967296 << 1 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s 1 = %d, want 8589934592", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967296 << 65535 = %d, want 0", r)
+		t.Errorf("4294967296 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 9223372036854775806
 	y = 0
 	r = x << y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 << 0 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 9223372036854775806", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("9223372036854775806 << 1 = %d, want -4", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("9223372036854775806 << 65535 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 9223372036854775807
 	y = 0
 	r = x << y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 << 0 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 9223372036854775807", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("9223372036854775807 << 1 = %d, want -2", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("9223372036854775807 << 65535 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 65535 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint64uint16rsh(t *testing.T) {
@@ -7783,145 +7783,145 @@ func TestConstFoldint64uint16rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 >> 0 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want -9223372036854775808", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -4611686018427387904 {
-		t.Errorf("-9223372036854775808 >> 1 = %d, want -4611686018427387904", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want -4611686018427387904", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 >> 65535 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = -9223372036854775807
 	y = 0
 	r = x >> y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 >> 0 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want -9223372036854775807", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -4611686018427387904 {
-		t.Errorf("-9223372036854775807 >> 1 = %d, want -4611686018427387904", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want -4611686018427387904", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-9223372036854775807 >> 65535 = %d, want -1", r)
+		t.Errorf("-9223372036854775807 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = -4294967296
 	y = 0
 	r = x >> y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 >> 0 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 0 = %d, want -4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -2147483648 {
-		t.Errorf("-4294967296 >> 1 = %d, want -2147483648", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -2147483648", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-4294967296 >> 65535 = %d, want -1", r)
+		t.Errorf("-4294967296 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 65535 = %d, want -1", r)
+		t.Errorf("-1 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x >> y
 	if r != 4294967296 {
-		t.Errorf("4294967296 >> 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483648 {
-		t.Errorf("4294967296 >> 1 = %d, want 2147483648", r)
+		t.Errorf("4294967296 %s 1 = %d, want 2147483648", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967296 >> 65535 = %d, want 0", r)
+		t.Errorf("4294967296 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 9223372036854775806
 	y = 0
 	r = x >> y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 >> 0 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 9223372036854775806", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 4611686018427387903 {
-		t.Errorf("9223372036854775806 >> 1 = %d, want 4611686018427387903", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want 4611686018427387903", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("9223372036854775806 >> 65535 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 9223372036854775807
 	y = 0
 	r = x >> y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 >> 0 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 9223372036854775807", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 4611686018427387903 {
-		t.Errorf("9223372036854775807 >> 1 = %d, want 4611686018427387903", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want 4611686018427387903", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("9223372036854775807 >> 65535 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 65535 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint64uint8lsh(t *testing.T) {
@@ -7931,145 +7931,145 @@ func TestConstFoldint64uint8lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 << 0 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want -9223372036854775808", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 << 1 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775808 << 255 = %d, want 0", r)
+		t.Errorf("-9223372036854775808 %s 255 = %d, want 0", "<<", r)
 	}
 	x = -9223372036854775807
 	y = 0
 	r = x << y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 << 0 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want -9223372036854775807", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-9223372036854775807 << 1 = %d, want 2", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-9223372036854775807 << 255 = %d, want 0", r)
+		t.Errorf("-9223372036854775807 %s 255 = %d, want 0", "<<", r)
 	}
 	x = -4294967296
 	y = 0
 	r = x << y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 << 0 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 0 = %d, want -4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -8589934592 {
-		t.Errorf("-4294967296 << 1 = %d, want -8589934592", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -8589934592", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-4294967296 << 255 = %d, want 0", r)
+		t.Errorf("-4294967296 %s 255 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 255 = %d, want 0", r)
+		t.Errorf("-1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x << y
 	if r != 4294967296 {
-		t.Errorf("4294967296 << 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 8589934592 {
-		t.Errorf("4294967296 << 1 = %d, want 8589934592", r)
+		t.Errorf("4294967296 %s 1 = %d, want 8589934592", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967296 << 255 = %d, want 0", r)
+		t.Errorf("4294967296 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 9223372036854775806
 	y = 0
 	r = x << y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 << 0 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 9223372036854775806", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("9223372036854775806 << 1 = %d, want -4", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("9223372036854775806 << 255 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 9223372036854775807
 	y = 0
 	r = x << y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 << 0 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 9223372036854775807", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("9223372036854775807 << 1 = %d, want -2", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("9223372036854775807 << 255 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 255 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint64uint8rsh(t *testing.T) {
@@ -8079,145 +8079,145 @@ func TestConstFoldint64uint8rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -9223372036854775808 {
-		t.Errorf("-9223372036854775808 >> 0 = %d, want -9223372036854775808", r)
+		t.Errorf("-9223372036854775808 %s 0 = %d, want -9223372036854775808", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -4611686018427387904 {
-		t.Errorf("-9223372036854775808 >> 1 = %d, want -4611686018427387904", r)
+		t.Errorf("-9223372036854775808 %s 1 = %d, want -4611686018427387904", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-9223372036854775808 >> 255 = %d, want -1", r)
+		t.Errorf("-9223372036854775808 %s 255 = %d, want -1", ">>", r)
 	}
 	x = -9223372036854775807
 	y = 0
 	r = x >> y
 	if r != -9223372036854775807 {
-		t.Errorf("-9223372036854775807 >> 0 = %d, want -9223372036854775807", r)
+		t.Errorf("-9223372036854775807 %s 0 = %d, want -9223372036854775807", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -4611686018427387904 {
-		t.Errorf("-9223372036854775807 >> 1 = %d, want -4611686018427387904", r)
+		t.Errorf("-9223372036854775807 %s 1 = %d, want -4611686018427387904", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-9223372036854775807 >> 255 = %d, want -1", r)
+		t.Errorf("-9223372036854775807 %s 255 = %d, want -1", ">>", r)
 	}
 	x = -4294967296
 	y = 0
 	r = x >> y
 	if r != -4294967296 {
-		t.Errorf("-4294967296 >> 0 = %d, want -4294967296", r)
+		t.Errorf("-4294967296 %s 0 = %d, want -4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -2147483648 {
-		t.Errorf("-4294967296 >> 1 = %d, want -2147483648", r)
+		t.Errorf("-4294967296 %s 1 = %d, want -2147483648", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-4294967296 >> 255 = %d, want -1", r)
+		t.Errorf("-4294967296 %s 255 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 255 = %d, want -1", r)
+		t.Errorf("-1 %s 255 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 4294967296
 	y = 0
 	r = x >> y
 	if r != 4294967296 {
-		t.Errorf("4294967296 >> 0 = %d, want 4294967296", r)
+		t.Errorf("4294967296 %s 0 = %d, want 4294967296", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483648 {
-		t.Errorf("4294967296 >> 1 = %d, want 2147483648", r)
+		t.Errorf("4294967296 %s 1 = %d, want 2147483648", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967296 >> 255 = %d, want 0", r)
+		t.Errorf("4294967296 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 9223372036854775806
 	y = 0
 	r = x >> y
 	if r != 9223372036854775806 {
-		t.Errorf("9223372036854775806 >> 0 = %d, want 9223372036854775806", r)
+		t.Errorf("9223372036854775806 %s 0 = %d, want 9223372036854775806", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 4611686018427387903 {
-		t.Errorf("9223372036854775806 >> 1 = %d, want 4611686018427387903", r)
+		t.Errorf("9223372036854775806 %s 1 = %d, want 4611686018427387903", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("9223372036854775806 >> 255 = %d, want 0", r)
+		t.Errorf("9223372036854775806 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 9223372036854775807
 	y = 0
 	r = x >> y
 	if r != 9223372036854775807 {
-		t.Errorf("9223372036854775807 >> 0 = %d, want 9223372036854775807", r)
+		t.Errorf("9223372036854775807 %s 0 = %d, want 9223372036854775807", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 4611686018427387903 {
-		t.Errorf("9223372036854775807 >> 1 = %d, want 4611686018427387903", r)
+		t.Errorf("9223372036854775807 %s 1 = %d, want 4611686018427387903", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("9223372036854775807 >> 255 = %d, want 0", r)
+		t.Errorf("9223372036854775807 %s 255 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint32uint64lsh(t *testing.T) {
@@ -8227,64 +8227,64 @@ func TestConstFolduint32uint64lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x << y
 	if r != 4294967295 {
-		t.Errorf("4294967295 << 0 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 0 = %d, want 4294967295", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 4294967294 {
-		t.Errorf("4294967295 << 1 = %d, want 4294967294", r)
+		t.Errorf("4294967295 %s 1 = %d, want 4294967294", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967295 << 4294967296 = %d, want 0", r)
+		t.Errorf("4294967295 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967295 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("4294967295 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint32uint64rsh(t *testing.T) {
@@ -8294,64 +8294,64 @@ func TestConstFolduint32uint64rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x >> y
 	if r != 4294967295 {
-		t.Errorf("4294967295 >> 0 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 0 = %d, want 4294967295", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483647 {
-		t.Errorf("4294967295 >> 1 = %d, want 2147483647", r)
+		t.Errorf("4294967295 %s 1 = %d, want 2147483647", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967295 >> 4294967296 = %d, want 0", r)
+		t.Errorf("4294967295 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967295 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("4294967295 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint32uint32lsh(t *testing.T) {
@@ -8361,49 +8361,49 @@ func TestConstFolduint32uint32lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x << y
 	if r != 4294967295 {
-		t.Errorf("4294967295 << 0 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 0 = %d, want 4294967295", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 4294967294 {
-		t.Errorf("4294967295 << 1 = %d, want 4294967294", r)
+		t.Errorf("4294967295 %s 1 = %d, want 4294967294", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967295 << 4294967295 = %d, want 0", r)
+		t.Errorf("4294967295 %s 4294967295 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint32uint32rsh(t *testing.T) {
@@ -8413,49 +8413,49 @@ func TestConstFolduint32uint32rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x >> y
 	if r != 4294967295 {
-		t.Errorf("4294967295 >> 0 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 0 = %d, want 4294967295", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483647 {
-		t.Errorf("4294967295 >> 1 = %d, want 2147483647", r)
+		t.Errorf("4294967295 %s 1 = %d, want 2147483647", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967295 >> 4294967295 = %d, want 0", r)
+		t.Errorf("4294967295 %s 4294967295 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint32uint16lsh(t *testing.T) {
@@ -8465,49 +8465,49 @@ func TestConstFolduint32uint16lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x << y
 	if r != 4294967295 {
-		t.Errorf("4294967295 << 0 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 0 = %d, want 4294967295", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 4294967294 {
-		t.Errorf("4294967295 << 1 = %d, want 4294967294", r)
+		t.Errorf("4294967295 %s 1 = %d, want 4294967294", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967295 << 65535 = %d, want 0", r)
+		t.Errorf("4294967295 %s 65535 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint32uint16rsh(t *testing.T) {
@@ -8517,49 +8517,49 @@ func TestConstFolduint32uint16rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x >> y
 	if r != 4294967295 {
-		t.Errorf("4294967295 >> 0 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 0 = %d, want 4294967295", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483647 {
-		t.Errorf("4294967295 >> 1 = %d, want 2147483647", r)
+		t.Errorf("4294967295 %s 1 = %d, want 2147483647", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967295 >> 65535 = %d, want 0", r)
+		t.Errorf("4294967295 %s 65535 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint32uint8lsh(t *testing.T) {
@@ -8569,49 +8569,49 @@ func TestConstFolduint32uint8lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x << y
 	if r != 4294967295 {
-		t.Errorf("4294967295 << 0 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 0 = %d, want 4294967295", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 4294967294 {
-		t.Errorf("4294967295 << 1 = %d, want 4294967294", r)
+		t.Errorf("4294967295 %s 1 = %d, want 4294967294", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("4294967295 << 255 = %d, want 0", r)
+		t.Errorf("4294967295 %s 255 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint32uint8rsh(t *testing.T) {
@@ -8621,49 +8621,49 @@ func TestConstFolduint32uint8rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 4294967295
 	y = 0
 	r = x >> y
 	if r != 4294967295 {
-		t.Errorf("4294967295 >> 0 = %d, want 4294967295", r)
+		t.Errorf("4294967295 %s 0 = %d, want 4294967295", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 2147483647 {
-		t.Errorf("4294967295 >> 1 = %d, want 2147483647", r)
+		t.Errorf("4294967295 %s 1 = %d, want 2147483647", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("4294967295 >> 255 = %d, want 0", r)
+		t.Errorf("4294967295 %s 255 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint32uint64lsh(t *testing.T) {
@@ -8673,127 +8673,127 @@ func TestConstFoldint32uint64lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 << 0 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 0 = %d, want -2147483648", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483648 << 1 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483648 << 4294967296 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483648 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = -2147483647
 	y = 0
 	r = x << y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 << 0 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 0 = %d, want -2147483647", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-2147483647 << 1 = %d, want 2", r)
+		t.Errorf("-2147483647 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483647 << 4294967296 = %d, want 0", r)
+		t.Errorf("-2147483647 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483647 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-2147483647 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 4294967296 = %d, want 0", r)
+		t.Errorf("-1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 2147483647
 	y = 0
 	r = x << y
 	if r != 2147483647 {
-		t.Errorf("2147483647 << 0 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 0 = %d, want 2147483647", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("2147483647 << 1 = %d, want -2", r)
+		t.Errorf("2147483647 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("2147483647 << 4294967296 = %d, want 0", r)
+		t.Errorf("2147483647 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("2147483647 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("2147483647 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint32uint64rsh(t *testing.T) {
@@ -8803,127 +8803,127 @@ func TestConstFoldint32uint64rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 >> 0 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 0 = %d, want -2147483648", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1073741824 {
-		t.Errorf("-2147483648 >> 1 = %d, want -1073741824", r)
+		t.Errorf("-2147483648 %s 1 = %d, want -1073741824", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-2147483648 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-2147483648 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-2147483648 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-2147483648 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = -2147483647
 	y = 0
 	r = x >> y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 >> 0 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 0 = %d, want -2147483647", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1073741824 {
-		t.Errorf("-2147483647 >> 1 = %d, want -1073741824", r)
+		t.Errorf("-2147483647 %s 1 = %d, want -1073741824", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-2147483647 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-2147483647 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-2147483647 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-2147483647 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-1 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-1 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 2147483647
 	y = 0
 	r = x >> y
 	if r != 2147483647 {
-		t.Errorf("2147483647 >> 0 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 0 = %d, want 2147483647", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 1073741823 {
-		t.Errorf("2147483647 >> 1 = %d, want 1073741823", r)
+		t.Errorf("2147483647 %s 1 = %d, want 1073741823", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("2147483647 >> 4294967296 = %d, want 0", r)
+		t.Errorf("2147483647 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("2147483647 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("2147483647 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint32uint32lsh(t *testing.T) {
@@ -8933,97 +8933,97 @@ func TestConstFoldint32uint32lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 << 0 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 0 = %d, want -2147483648", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483648 << 1 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483648 << 4294967295 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = -2147483647
 	y = 0
 	r = x << y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 << 0 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 0 = %d, want -2147483647", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-2147483647 << 1 = %d, want 2", r)
+		t.Errorf("-2147483647 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483647 << 4294967295 = %d, want 0", r)
+		t.Errorf("-2147483647 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 4294967295 = %d, want 0", r)
+		t.Errorf("-1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 2147483647
 	y = 0
 	r = x << y
 	if r != 2147483647 {
-		t.Errorf("2147483647 << 0 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 0 = %d, want 2147483647", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("2147483647 << 1 = %d, want -2", r)
+		t.Errorf("2147483647 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("2147483647 << 4294967295 = %d, want 0", r)
+		t.Errorf("2147483647 %s 4294967295 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint32uint32rsh(t *testing.T) {
@@ -9033,97 +9033,97 @@ func TestConstFoldint32uint32rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 >> 0 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 0 = %d, want -2147483648", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1073741824 {
-		t.Errorf("-2147483648 >> 1 = %d, want -1073741824", r)
+		t.Errorf("-2147483648 %s 1 = %d, want -1073741824", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-2147483648 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-2147483648 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = -2147483647
 	y = 0
 	r = x >> y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 >> 0 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 0 = %d, want -2147483647", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1073741824 {
-		t.Errorf("-2147483647 >> 1 = %d, want -1073741824", r)
+		t.Errorf("-2147483647 %s 1 = %d, want -1073741824", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-2147483647 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-2147483647 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-1 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 2147483647
 	y = 0
 	r = x >> y
 	if r != 2147483647 {
-		t.Errorf("2147483647 >> 0 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 0 = %d, want 2147483647", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 1073741823 {
-		t.Errorf("2147483647 >> 1 = %d, want 1073741823", r)
+		t.Errorf("2147483647 %s 1 = %d, want 1073741823", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("2147483647 >> 4294967295 = %d, want 0", r)
+		t.Errorf("2147483647 %s 4294967295 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint32uint16lsh(t *testing.T) {
@@ -9133,97 +9133,97 @@ func TestConstFoldint32uint16lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 << 0 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 0 = %d, want -2147483648", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483648 << 1 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483648 << 65535 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = -2147483647
 	y = 0
 	r = x << y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 << 0 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 0 = %d, want -2147483647", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-2147483647 << 1 = %d, want 2", r)
+		t.Errorf("-2147483647 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483647 << 65535 = %d, want 0", r)
+		t.Errorf("-2147483647 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 65535 = %d, want 0", r)
+		t.Errorf("-1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 2147483647
 	y = 0
 	r = x << y
 	if r != 2147483647 {
-		t.Errorf("2147483647 << 0 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 0 = %d, want 2147483647", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("2147483647 << 1 = %d, want -2", r)
+		t.Errorf("2147483647 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("2147483647 << 65535 = %d, want 0", r)
+		t.Errorf("2147483647 %s 65535 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint32uint16rsh(t *testing.T) {
@@ -9233,97 +9233,97 @@ func TestConstFoldint32uint16rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 >> 0 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 0 = %d, want -2147483648", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1073741824 {
-		t.Errorf("-2147483648 >> 1 = %d, want -1073741824", r)
+		t.Errorf("-2147483648 %s 1 = %d, want -1073741824", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-2147483648 >> 65535 = %d, want -1", r)
+		t.Errorf("-2147483648 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = -2147483647
 	y = 0
 	r = x >> y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 >> 0 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 0 = %d, want -2147483647", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1073741824 {
-		t.Errorf("-2147483647 >> 1 = %d, want -1073741824", r)
+		t.Errorf("-2147483647 %s 1 = %d, want -1073741824", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-2147483647 >> 65535 = %d, want -1", r)
+		t.Errorf("-2147483647 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 65535 = %d, want -1", r)
+		t.Errorf("-1 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 2147483647
 	y = 0
 	r = x >> y
 	if r != 2147483647 {
-		t.Errorf("2147483647 >> 0 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 0 = %d, want 2147483647", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 1073741823 {
-		t.Errorf("2147483647 >> 1 = %d, want 1073741823", r)
+		t.Errorf("2147483647 %s 1 = %d, want 1073741823", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("2147483647 >> 65535 = %d, want 0", r)
+		t.Errorf("2147483647 %s 65535 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint32uint8lsh(t *testing.T) {
@@ -9333,97 +9333,97 @@ func TestConstFoldint32uint8lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 << 0 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 0 = %d, want -2147483648", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483648 << 1 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483648 << 255 = %d, want 0", r)
+		t.Errorf("-2147483648 %s 255 = %d, want 0", "<<", r)
 	}
 	x = -2147483647
 	y = 0
 	r = x << y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 << 0 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 0 = %d, want -2147483647", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-2147483647 << 1 = %d, want 2", r)
+		t.Errorf("-2147483647 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-2147483647 << 255 = %d, want 0", r)
+		t.Errorf("-2147483647 %s 255 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 255 = %d, want 0", r)
+		t.Errorf("-1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 2147483647
 	y = 0
 	r = x << y
 	if r != 2147483647 {
-		t.Errorf("2147483647 << 0 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 0 = %d, want 2147483647", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("2147483647 << 1 = %d, want -2", r)
+		t.Errorf("2147483647 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("2147483647 << 255 = %d, want 0", r)
+		t.Errorf("2147483647 %s 255 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint32uint8rsh(t *testing.T) {
@@ -9433,97 +9433,97 @@ func TestConstFoldint32uint8rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -2147483648 {
-		t.Errorf("-2147483648 >> 0 = %d, want -2147483648", r)
+		t.Errorf("-2147483648 %s 0 = %d, want -2147483648", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1073741824 {
-		t.Errorf("-2147483648 >> 1 = %d, want -1073741824", r)
+		t.Errorf("-2147483648 %s 1 = %d, want -1073741824", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-2147483648 >> 255 = %d, want -1", r)
+		t.Errorf("-2147483648 %s 255 = %d, want -1", ">>", r)
 	}
 	x = -2147483647
 	y = 0
 	r = x >> y
 	if r != -2147483647 {
-		t.Errorf("-2147483647 >> 0 = %d, want -2147483647", r)
+		t.Errorf("-2147483647 %s 0 = %d, want -2147483647", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1073741824 {
-		t.Errorf("-2147483647 >> 1 = %d, want -1073741824", r)
+		t.Errorf("-2147483647 %s 1 = %d, want -1073741824", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-2147483647 >> 255 = %d, want -1", r)
+		t.Errorf("-2147483647 %s 255 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 255 = %d, want -1", r)
+		t.Errorf("-1 %s 255 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 2147483647
 	y = 0
 	r = x >> y
 	if r != 2147483647 {
-		t.Errorf("2147483647 >> 0 = %d, want 2147483647", r)
+		t.Errorf("2147483647 %s 0 = %d, want 2147483647", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 1073741823 {
-		t.Errorf("2147483647 >> 1 = %d, want 1073741823", r)
+		t.Errorf("2147483647 %s 1 = %d, want 1073741823", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("2147483647 >> 255 = %d, want 0", r)
+		t.Errorf("2147483647 %s 255 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint16uint64lsh(t *testing.T) {
@@ -9533,64 +9533,64 @@ func TestConstFolduint16uint64lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 65535
 	y = 0
 	r = x << y
 	if r != 65535 {
-		t.Errorf("65535 << 0 = %d, want 65535", r)
+		t.Errorf("65535 %s 0 = %d, want 65535", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 65534 {
-		t.Errorf("65535 << 1 = %d, want 65534", r)
+		t.Errorf("65535 %s 1 = %d, want 65534", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("65535 << 4294967296 = %d, want 0", r)
+		t.Errorf("65535 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("65535 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("65535 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint16uint64rsh(t *testing.T) {
@@ -9600,64 +9600,64 @@ func TestConstFolduint16uint64rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 65535
 	y = 0
 	r = x >> y
 	if r != 65535 {
-		t.Errorf("65535 >> 0 = %d, want 65535", r)
+		t.Errorf("65535 %s 0 = %d, want 65535", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 32767 {
-		t.Errorf("65535 >> 1 = %d, want 32767", r)
+		t.Errorf("65535 %s 1 = %d, want 32767", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("65535 >> 4294967296 = %d, want 0", r)
+		t.Errorf("65535 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("65535 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("65535 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint16uint32lsh(t *testing.T) {
@@ -9667,49 +9667,49 @@ func TestConstFolduint16uint32lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 65535
 	y = 0
 	r = x << y
 	if r != 65535 {
-		t.Errorf("65535 << 0 = %d, want 65535", r)
+		t.Errorf("65535 %s 0 = %d, want 65535", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 65534 {
-		t.Errorf("65535 << 1 = %d, want 65534", r)
+		t.Errorf("65535 %s 1 = %d, want 65534", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("65535 << 4294967295 = %d, want 0", r)
+		t.Errorf("65535 %s 4294967295 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint16uint32rsh(t *testing.T) {
@@ -9719,49 +9719,49 @@ func TestConstFolduint16uint32rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 65535
 	y = 0
 	r = x >> y
 	if r != 65535 {
-		t.Errorf("65535 >> 0 = %d, want 65535", r)
+		t.Errorf("65535 %s 0 = %d, want 65535", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 32767 {
-		t.Errorf("65535 >> 1 = %d, want 32767", r)
+		t.Errorf("65535 %s 1 = %d, want 32767", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("65535 >> 4294967295 = %d, want 0", r)
+		t.Errorf("65535 %s 4294967295 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint16uint16lsh(t *testing.T) {
@@ -9771,49 +9771,49 @@ func TestConstFolduint16uint16lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 65535
 	y = 0
 	r = x << y
 	if r != 65535 {
-		t.Errorf("65535 << 0 = %d, want 65535", r)
+		t.Errorf("65535 %s 0 = %d, want 65535", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 65534 {
-		t.Errorf("65535 << 1 = %d, want 65534", r)
+		t.Errorf("65535 %s 1 = %d, want 65534", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("65535 << 65535 = %d, want 0", r)
+		t.Errorf("65535 %s 65535 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint16uint16rsh(t *testing.T) {
@@ -9823,49 +9823,49 @@ func TestConstFolduint16uint16rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 65535
 	y = 0
 	r = x >> y
 	if r != 65535 {
-		t.Errorf("65535 >> 0 = %d, want 65535", r)
+		t.Errorf("65535 %s 0 = %d, want 65535", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 32767 {
-		t.Errorf("65535 >> 1 = %d, want 32767", r)
+		t.Errorf("65535 %s 1 = %d, want 32767", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("65535 >> 65535 = %d, want 0", r)
+		t.Errorf("65535 %s 65535 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint16uint8lsh(t *testing.T) {
@@ -9875,49 +9875,49 @@ func TestConstFolduint16uint8lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 65535
 	y = 0
 	r = x << y
 	if r != 65535 {
-		t.Errorf("65535 << 0 = %d, want 65535", r)
+		t.Errorf("65535 %s 0 = %d, want 65535", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 65534 {
-		t.Errorf("65535 << 1 = %d, want 65534", r)
+		t.Errorf("65535 %s 1 = %d, want 65534", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("65535 << 255 = %d, want 0", r)
+		t.Errorf("65535 %s 255 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint16uint8rsh(t *testing.T) {
@@ -9927,49 +9927,49 @@ func TestConstFolduint16uint8rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 65535
 	y = 0
 	r = x >> y
 	if r != 65535 {
-		t.Errorf("65535 >> 0 = %d, want 65535", r)
+		t.Errorf("65535 %s 0 = %d, want 65535", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 32767 {
-		t.Errorf("65535 >> 1 = %d, want 32767", r)
+		t.Errorf("65535 %s 1 = %d, want 32767", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("65535 >> 255 = %d, want 0", r)
+		t.Errorf("65535 %s 255 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint16uint64lsh(t *testing.T) {
@@ -9979,148 +9979,148 @@ func TestConstFoldint16uint64lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -32768 {
-		t.Errorf("-32768 << 0 = %d, want -32768", r)
+		t.Errorf("-32768 %s 0 = %d, want -32768", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32768 << 1 = %d, want 0", r)
+		t.Errorf("-32768 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32768 << 4294967296 = %d, want 0", r)
+		t.Errorf("-32768 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32768 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-32768 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = -32767
 	y = 0
 	r = x << y
 	if r != -32767 {
-		t.Errorf("-32767 << 0 = %d, want -32767", r)
+		t.Errorf("-32767 %s 0 = %d, want -32767", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-32767 << 1 = %d, want 2", r)
+		t.Errorf("-32767 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32767 << 4294967296 = %d, want 0", r)
+		t.Errorf("-32767 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32767 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-32767 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 4294967296 = %d, want 0", r)
+		t.Errorf("-1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 32766
 	y = 0
 	r = x << y
 	if r != 32766 {
-		t.Errorf("32766 << 0 = %d, want 32766", r)
+		t.Errorf("32766 %s 0 = %d, want 32766", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("32766 << 1 = %d, want -4", r)
+		t.Errorf("32766 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("32766 << 4294967296 = %d, want 0", r)
+		t.Errorf("32766 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("32766 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("32766 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 32767
 	y = 0
 	r = x << y
 	if r != 32767 {
-		t.Errorf("32767 << 0 = %d, want 32767", r)
+		t.Errorf("32767 %s 0 = %d, want 32767", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("32767 << 1 = %d, want -2", r)
+		t.Errorf("32767 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("32767 << 4294967296 = %d, want 0", r)
+		t.Errorf("32767 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("32767 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("32767 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint16uint64rsh(t *testing.T) {
@@ -10130,148 +10130,148 @@ func TestConstFoldint16uint64rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -32768 {
-		t.Errorf("-32768 >> 0 = %d, want -32768", r)
+		t.Errorf("-32768 %s 0 = %d, want -32768", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -16384 {
-		t.Errorf("-32768 >> 1 = %d, want -16384", r)
+		t.Errorf("-32768 %s 1 = %d, want -16384", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-32768 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-32768 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-32768 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-32768 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = -32767
 	y = 0
 	r = x >> y
 	if r != -32767 {
-		t.Errorf("-32767 >> 0 = %d, want -32767", r)
+		t.Errorf("-32767 %s 0 = %d, want -32767", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -16384 {
-		t.Errorf("-32767 >> 1 = %d, want -16384", r)
+		t.Errorf("-32767 %s 1 = %d, want -16384", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-32767 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-32767 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-32767 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-32767 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-1 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-1 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 32766
 	y = 0
 	r = x >> y
 	if r != 32766 {
-		t.Errorf("32766 >> 0 = %d, want 32766", r)
+		t.Errorf("32766 %s 0 = %d, want 32766", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 16383 {
-		t.Errorf("32766 >> 1 = %d, want 16383", r)
+		t.Errorf("32766 %s 1 = %d, want 16383", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("32766 >> 4294967296 = %d, want 0", r)
+		t.Errorf("32766 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("32766 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("32766 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 32767
 	y = 0
 	r = x >> y
 	if r != 32767 {
-		t.Errorf("32767 >> 0 = %d, want 32767", r)
+		t.Errorf("32767 %s 0 = %d, want 32767", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 16383 {
-		t.Errorf("32767 >> 1 = %d, want 16383", r)
+		t.Errorf("32767 %s 1 = %d, want 16383", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("32767 >> 4294967296 = %d, want 0", r)
+		t.Errorf("32767 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("32767 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("32767 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint16uint32lsh(t *testing.T) {
@@ -10281,113 +10281,113 @@ func TestConstFoldint16uint32lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -32768 {
-		t.Errorf("-32768 << 0 = %d, want -32768", r)
+		t.Errorf("-32768 %s 0 = %d, want -32768", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32768 << 1 = %d, want 0", r)
+		t.Errorf("-32768 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32768 << 4294967295 = %d, want 0", r)
+		t.Errorf("-32768 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = -32767
 	y = 0
 	r = x << y
 	if r != -32767 {
-		t.Errorf("-32767 << 0 = %d, want -32767", r)
+		t.Errorf("-32767 %s 0 = %d, want -32767", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-32767 << 1 = %d, want 2", r)
+		t.Errorf("-32767 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32767 << 4294967295 = %d, want 0", r)
+		t.Errorf("-32767 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 4294967295 = %d, want 0", r)
+		t.Errorf("-1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 32766
 	y = 0
 	r = x << y
 	if r != 32766 {
-		t.Errorf("32766 << 0 = %d, want 32766", r)
+		t.Errorf("32766 %s 0 = %d, want 32766", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("32766 << 1 = %d, want -4", r)
+		t.Errorf("32766 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("32766 << 4294967295 = %d, want 0", r)
+		t.Errorf("32766 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 32767
 	y = 0
 	r = x << y
 	if r != 32767 {
-		t.Errorf("32767 << 0 = %d, want 32767", r)
+		t.Errorf("32767 %s 0 = %d, want 32767", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("32767 << 1 = %d, want -2", r)
+		t.Errorf("32767 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("32767 << 4294967295 = %d, want 0", r)
+		t.Errorf("32767 %s 4294967295 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint16uint32rsh(t *testing.T) {
@@ -10397,113 +10397,113 @@ func TestConstFoldint16uint32rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -32768 {
-		t.Errorf("-32768 >> 0 = %d, want -32768", r)
+		t.Errorf("-32768 %s 0 = %d, want -32768", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -16384 {
-		t.Errorf("-32768 >> 1 = %d, want -16384", r)
+		t.Errorf("-32768 %s 1 = %d, want -16384", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-32768 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-32768 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = -32767
 	y = 0
 	r = x >> y
 	if r != -32767 {
-		t.Errorf("-32767 >> 0 = %d, want -32767", r)
+		t.Errorf("-32767 %s 0 = %d, want -32767", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -16384 {
-		t.Errorf("-32767 >> 1 = %d, want -16384", r)
+		t.Errorf("-32767 %s 1 = %d, want -16384", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-32767 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-32767 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-1 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 32766
 	y = 0
 	r = x >> y
 	if r != 32766 {
-		t.Errorf("32766 >> 0 = %d, want 32766", r)
+		t.Errorf("32766 %s 0 = %d, want 32766", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 16383 {
-		t.Errorf("32766 >> 1 = %d, want 16383", r)
+		t.Errorf("32766 %s 1 = %d, want 16383", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("32766 >> 4294967295 = %d, want 0", r)
+		t.Errorf("32766 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 32767
 	y = 0
 	r = x >> y
 	if r != 32767 {
-		t.Errorf("32767 >> 0 = %d, want 32767", r)
+		t.Errorf("32767 %s 0 = %d, want 32767", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 16383 {
-		t.Errorf("32767 >> 1 = %d, want 16383", r)
+		t.Errorf("32767 %s 1 = %d, want 16383", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("32767 >> 4294967295 = %d, want 0", r)
+		t.Errorf("32767 %s 4294967295 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint16uint16lsh(t *testing.T) {
@@ -10513,113 +10513,113 @@ func TestConstFoldint16uint16lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -32768 {
-		t.Errorf("-32768 << 0 = %d, want -32768", r)
+		t.Errorf("-32768 %s 0 = %d, want -32768", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32768 << 1 = %d, want 0", r)
+		t.Errorf("-32768 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32768 << 65535 = %d, want 0", r)
+		t.Errorf("-32768 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = -32767
 	y = 0
 	r = x << y
 	if r != -32767 {
-		t.Errorf("-32767 << 0 = %d, want -32767", r)
+		t.Errorf("-32767 %s 0 = %d, want -32767", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-32767 << 1 = %d, want 2", r)
+		t.Errorf("-32767 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32767 << 65535 = %d, want 0", r)
+		t.Errorf("-32767 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 65535 = %d, want 0", r)
+		t.Errorf("-1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 32766
 	y = 0
 	r = x << y
 	if r != 32766 {
-		t.Errorf("32766 << 0 = %d, want 32766", r)
+		t.Errorf("32766 %s 0 = %d, want 32766", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("32766 << 1 = %d, want -4", r)
+		t.Errorf("32766 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("32766 << 65535 = %d, want 0", r)
+		t.Errorf("32766 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 32767
 	y = 0
 	r = x << y
 	if r != 32767 {
-		t.Errorf("32767 << 0 = %d, want 32767", r)
+		t.Errorf("32767 %s 0 = %d, want 32767", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("32767 << 1 = %d, want -2", r)
+		t.Errorf("32767 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("32767 << 65535 = %d, want 0", r)
+		t.Errorf("32767 %s 65535 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint16uint16rsh(t *testing.T) {
@@ -10629,113 +10629,113 @@ func TestConstFoldint16uint16rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -32768 {
-		t.Errorf("-32768 >> 0 = %d, want -32768", r)
+		t.Errorf("-32768 %s 0 = %d, want -32768", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -16384 {
-		t.Errorf("-32768 >> 1 = %d, want -16384", r)
+		t.Errorf("-32768 %s 1 = %d, want -16384", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-32768 >> 65535 = %d, want -1", r)
+		t.Errorf("-32768 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = -32767
 	y = 0
 	r = x >> y
 	if r != -32767 {
-		t.Errorf("-32767 >> 0 = %d, want -32767", r)
+		t.Errorf("-32767 %s 0 = %d, want -32767", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -16384 {
-		t.Errorf("-32767 >> 1 = %d, want -16384", r)
+		t.Errorf("-32767 %s 1 = %d, want -16384", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-32767 >> 65535 = %d, want -1", r)
+		t.Errorf("-32767 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 65535 = %d, want -1", r)
+		t.Errorf("-1 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 32766
 	y = 0
 	r = x >> y
 	if r != 32766 {
-		t.Errorf("32766 >> 0 = %d, want 32766", r)
+		t.Errorf("32766 %s 0 = %d, want 32766", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 16383 {
-		t.Errorf("32766 >> 1 = %d, want 16383", r)
+		t.Errorf("32766 %s 1 = %d, want 16383", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("32766 >> 65535 = %d, want 0", r)
+		t.Errorf("32766 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 32767
 	y = 0
 	r = x >> y
 	if r != 32767 {
-		t.Errorf("32767 >> 0 = %d, want 32767", r)
+		t.Errorf("32767 %s 0 = %d, want 32767", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 16383 {
-		t.Errorf("32767 >> 1 = %d, want 16383", r)
+		t.Errorf("32767 %s 1 = %d, want 16383", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("32767 >> 65535 = %d, want 0", r)
+		t.Errorf("32767 %s 65535 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint16uint8lsh(t *testing.T) {
@@ -10745,113 +10745,113 @@ func TestConstFoldint16uint8lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -32768 {
-		t.Errorf("-32768 << 0 = %d, want -32768", r)
+		t.Errorf("-32768 %s 0 = %d, want -32768", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32768 << 1 = %d, want 0", r)
+		t.Errorf("-32768 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32768 << 255 = %d, want 0", r)
+		t.Errorf("-32768 %s 255 = %d, want 0", "<<", r)
 	}
 	x = -32767
 	y = 0
 	r = x << y
 	if r != -32767 {
-		t.Errorf("-32767 << 0 = %d, want -32767", r)
+		t.Errorf("-32767 %s 0 = %d, want -32767", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-32767 << 1 = %d, want 2", r)
+		t.Errorf("-32767 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-32767 << 255 = %d, want 0", r)
+		t.Errorf("-32767 %s 255 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 255 = %d, want 0", r)
+		t.Errorf("-1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 32766
 	y = 0
 	r = x << y
 	if r != 32766 {
-		t.Errorf("32766 << 0 = %d, want 32766", r)
+		t.Errorf("32766 %s 0 = %d, want 32766", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("32766 << 1 = %d, want -4", r)
+		t.Errorf("32766 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("32766 << 255 = %d, want 0", r)
+		t.Errorf("32766 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 32767
 	y = 0
 	r = x << y
 	if r != 32767 {
-		t.Errorf("32767 << 0 = %d, want 32767", r)
+		t.Errorf("32767 %s 0 = %d, want 32767", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("32767 << 1 = %d, want -2", r)
+		t.Errorf("32767 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("32767 << 255 = %d, want 0", r)
+		t.Errorf("32767 %s 255 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint16uint8rsh(t *testing.T) {
@@ -10861,113 +10861,113 @@ func TestConstFoldint16uint8rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -32768 {
-		t.Errorf("-32768 >> 0 = %d, want -32768", r)
+		t.Errorf("-32768 %s 0 = %d, want -32768", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -16384 {
-		t.Errorf("-32768 >> 1 = %d, want -16384", r)
+		t.Errorf("-32768 %s 1 = %d, want -16384", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-32768 >> 255 = %d, want -1", r)
+		t.Errorf("-32768 %s 255 = %d, want -1", ">>", r)
 	}
 	x = -32767
 	y = 0
 	r = x >> y
 	if r != -32767 {
-		t.Errorf("-32767 >> 0 = %d, want -32767", r)
+		t.Errorf("-32767 %s 0 = %d, want -32767", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -16384 {
-		t.Errorf("-32767 >> 1 = %d, want -16384", r)
+		t.Errorf("-32767 %s 1 = %d, want -16384", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-32767 >> 255 = %d, want -1", r)
+		t.Errorf("-32767 %s 255 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 255 = %d, want -1", r)
+		t.Errorf("-1 %s 255 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 32766
 	y = 0
 	r = x >> y
 	if r != 32766 {
-		t.Errorf("32766 >> 0 = %d, want 32766", r)
+		t.Errorf("32766 %s 0 = %d, want 32766", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 16383 {
-		t.Errorf("32766 >> 1 = %d, want 16383", r)
+		t.Errorf("32766 %s 1 = %d, want 16383", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("32766 >> 255 = %d, want 0", r)
+		t.Errorf("32766 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 32767
 	y = 0
 	r = x >> y
 	if r != 32767 {
-		t.Errorf("32767 >> 0 = %d, want 32767", r)
+		t.Errorf("32767 %s 0 = %d, want 32767", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 16383 {
-		t.Errorf("32767 >> 1 = %d, want 16383", r)
+		t.Errorf("32767 %s 1 = %d, want 16383", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("32767 >> 255 = %d, want 0", r)
+		t.Errorf("32767 %s 255 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint8uint64lsh(t *testing.T) {
@@ -10977,64 +10977,64 @@ func TestConstFolduint8uint64lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 255
 	y = 0
 	r = x << y
 	if r != 255 {
-		t.Errorf("255 << 0 = %d, want 255", r)
+		t.Errorf("255 %s 0 = %d, want 255", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 254 {
-		t.Errorf("255 << 1 = %d, want 254", r)
+		t.Errorf("255 %s 1 = %d, want 254", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("255 << 4294967296 = %d, want 0", r)
+		t.Errorf("255 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("255 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("255 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint8uint64rsh(t *testing.T) {
@@ -11044,64 +11044,64 @@ func TestConstFolduint8uint64rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 255
 	y = 0
 	r = x >> y
 	if r != 255 {
-		t.Errorf("255 >> 0 = %d, want 255", r)
+		t.Errorf("255 %s 0 = %d, want 255", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 127 {
-		t.Errorf("255 >> 1 = %d, want 127", r)
+		t.Errorf("255 %s 1 = %d, want 127", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("255 >> 4294967296 = %d, want 0", r)
+		t.Errorf("255 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("255 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("255 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint8uint32lsh(t *testing.T) {
@@ -11111,49 +11111,49 @@ func TestConstFolduint8uint32lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 255
 	y = 0
 	r = x << y
 	if r != 255 {
-		t.Errorf("255 << 0 = %d, want 255", r)
+		t.Errorf("255 %s 0 = %d, want 255", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 254 {
-		t.Errorf("255 << 1 = %d, want 254", r)
+		t.Errorf("255 %s 1 = %d, want 254", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("255 << 4294967295 = %d, want 0", r)
+		t.Errorf("255 %s 4294967295 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint8uint32rsh(t *testing.T) {
@@ -11163,49 +11163,49 @@ func TestConstFolduint8uint32rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 255
 	y = 0
 	r = x >> y
 	if r != 255 {
-		t.Errorf("255 >> 0 = %d, want 255", r)
+		t.Errorf("255 %s 0 = %d, want 255", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 127 {
-		t.Errorf("255 >> 1 = %d, want 127", r)
+		t.Errorf("255 %s 1 = %d, want 127", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("255 >> 4294967295 = %d, want 0", r)
+		t.Errorf("255 %s 4294967295 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint8uint16lsh(t *testing.T) {
@@ -11215,49 +11215,49 @@ func TestConstFolduint8uint16lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 255
 	y = 0
 	r = x << y
 	if r != 255 {
-		t.Errorf("255 << 0 = %d, want 255", r)
+		t.Errorf("255 %s 0 = %d, want 255", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 254 {
-		t.Errorf("255 << 1 = %d, want 254", r)
+		t.Errorf("255 %s 1 = %d, want 254", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("255 << 65535 = %d, want 0", r)
+		t.Errorf("255 %s 65535 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint8uint16rsh(t *testing.T) {
@@ -11267,49 +11267,49 @@ func TestConstFolduint8uint16rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 255
 	y = 0
 	r = x >> y
 	if r != 255 {
-		t.Errorf("255 >> 0 = %d, want 255", r)
+		t.Errorf("255 %s 0 = %d, want 255", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 127 {
-		t.Errorf("255 >> 1 = %d, want 127", r)
+		t.Errorf("255 %s 1 = %d, want 127", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("255 >> 65535 = %d, want 0", r)
+		t.Errorf("255 %s 65535 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFolduint8uint8lsh(t *testing.T) {
@@ -11319,49 +11319,49 @@ func TestConstFolduint8uint8lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 255
 	y = 0
 	r = x << y
 	if r != 255 {
-		t.Errorf("255 << 0 = %d, want 255", r)
+		t.Errorf("255 %s 0 = %d, want 255", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 254 {
-		t.Errorf("255 << 1 = %d, want 254", r)
+		t.Errorf("255 %s 1 = %d, want 254", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("255 << 255 = %d, want 0", r)
+		t.Errorf("255 %s 255 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFolduint8uint8rsh(t *testing.T) {
@@ -11371,49 +11371,49 @@ func TestConstFolduint8uint8rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 255
 	y = 0
 	r = x >> y
 	if r != 255 {
-		t.Errorf("255 >> 0 = %d, want 255", r)
+		t.Errorf("255 %s 0 = %d, want 255", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 127 {
-		t.Errorf("255 >> 1 = %d, want 127", r)
+		t.Errorf("255 %s 1 = %d, want 127", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("255 >> 255 = %d, want 0", r)
+		t.Errorf("255 %s 255 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint8uint64lsh(t *testing.T) {
@@ -11423,148 +11423,148 @@ func TestConstFoldint8uint64lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -128 {
-		t.Errorf("-128 << 0 = %d, want -128", r)
+		t.Errorf("-128 %s 0 = %d, want -128", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-128 << 1 = %d, want 0", r)
+		t.Errorf("-128 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-128 << 4294967296 = %d, want 0", r)
+		t.Errorf("-128 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-128 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-128 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = -127
 	y = 0
 	r = x << y
 	if r != -127 {
-		t.Errorf("-127 << 0 = %d, want -127", r)
+		t.Errorf("-127 %s 0 = %d, want -127", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-127 << 1 = %d, want 2", r)
+		t.Errorf("-127 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-127 << 4294967296 = %d, want 0", r)
+		t.Errorf("-127 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-127 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-127 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 4294967296 = %d, want 0", r)
+		t.Errorf("-1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("-1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 126
 	y = 0
 	r = x << y
 	if r != 126 {
-		t.Errorf("126 << 0 = %d, want 126", r)
+		t.Errorf("126 %s 0 = %d, want 126", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("126 << 1 = %d, want -4", r)
+		t.Errorf("126 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("126 << 4294967296 = %d, want 0", r)
+		t.Errorf("126 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("126 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("126 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 	x = 127
 	y = 0
 	r = x << y
 	if r != 127 {
-		t.Errorf("127 << 0 = %d, want 127", r)
+		t.Errorf("127 %s 0 = %d, want 127", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("127 << 1 = %d, want -2", r)
+		t.Errorf("127 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967296
 	r = x << y
 	if r != 0 {
-		t.Errorf("127 << 4294967296 = %d, want 0", r)
+		t.Errorf("127 %s 4294967296 = %d, want 0", "<<", r)
 	}
 	y = 18446744073709551615
 	r = x << y
 	if r != 0 {
-		t.Errorf("127 << 18446744073709551615 = %d, want 0", r)
+		t.Errorf("127 %s 18446744073709551615 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint8uint64rsh(t *testing.T) {
@@ -11574,148 +11574,148 @@ func TestConstFoldint8uint64rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -128 {
-		t.Errorf("-128 >> 0 = %d, want -128", r)
+		t.Errorf("-128 %s 0 = %d, want -128", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -64 {
-		t.Errorf("-128 >> 1 = %d, want -64", r)
+		t.Errorf("-128 %s 1 = %d, want -64", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-128 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-128 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-128 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-128 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = -127
 	y = 0
 	r = x >> y
 	if r != -127 {
-		t.Errorf("-127 >> 0 = %d, want -127", r)
+		t.Errorf("-127 %s 0 = %d, want -127", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -64 {
-		t.Errorf("-127 >> 1 = %d, want -64", r)
+		t.Errorf("-127 %s 1 = %d, want -64", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-127 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-127 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-127 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-127 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 4294967296 = %d, want -1", r)
+		t.Errorf("-1 %s 4294967296 = %d, want -1", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 18446744073709551615 = %d, want -1", r)
+		t.Errorf("-1 %s 18446744073709551615 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967296 = %d, want 0", r)
+		t.Errorf("0 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("0 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967296 = %d, want 0", r)
+		t.Errorf("1 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("1 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 126
 	y = 0
 	r = x >> y
 	if r != 126 {
-		t.Errorf("126 >> 0 = %d, want 126", r)
+		t.Errorf("126 %s 0 = %d, want 126", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 63 {
-		t.Errorf("126 >> 1 = %d, want 63", r)
+		t.Errorf("126 %s 1 = %d, want 63", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("126 >> 4294967296 = %d, want 0", r)
+		t.Errorf("126 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("126 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("126 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 	x = 127
 	y = 0
 	r = x >> y
 	if r != 127 {
-		t.Errorf("127 >> 0 = %d, want 127", r)
+		t.Errorf("127 %s 0 = %d, want 127", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 63 {
-		t.Errorf("127 >> 1 = %d, want 63", r)
+		t.Errorf("127 %s 1 = %d, want 63", ">>", r)
 	}
 	y = 4294967296
 	r = x >> y
 	if r != 0 {
-		t.Errorf("127 >> 4294967296 = %d, want 0", r)
+		t.Errorf("127 %s 4294967296 = %d, want 0", ">>", r)
 	}
 	y = 18446744073709551615
 	r = x >> y
 	if r != 0 {
-		t.Errorf("127 >> 18446744073709551615 = %d, want 0", r)
+		t.Errorf("127 %s 18446744073709551615 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint8uint32lsh(t *testing.T) {
@@ -11725,113 +11725,113 @@ func TestConstFoldint8uint32lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -128 {
-		t.Errorf("-128 << 0 = %d, want -128", r)
+		t.Errorf("-128 %s 0 = %d, want -128", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-128 << 1 = %d, want 0", r)
+		t.Errorf("-128 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-128 << 4294967295 = %d, want 0", r)
+		t.Errorf("-128 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = -127
 	y = 0
 	r = x << y
 	if r != -127 {
-		t.Errorf("-127 << 0 = %d, want -127", r)
+		t.Errorf("-127 %s 0 = %d, want -127", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-127 << 1 = %d, want 2", r)
+		t.Errorf("-127 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-127 << 4294967295 = %d, want 0", r)
+		t.Errorf("-127 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 4294967295 = %d, want 0", r)
+		t.Errorf("-1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 126
 	y = 0
 	r = x << y
 	if r != 126 {
-		t.Errorf("126 << 0 = %d, want 126", r)
+		t.Errorf("126 %s 0 = %d, want 126", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("126 << 1 = %d, want -4", r)
+		t.Errorf("126 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("126 << 4294967295 = %d, want 0", r)
+		t.Errorf("126 %s 4294967295 = %d, want 0", "<<", r)
 	}
 	x = 127
 	y = 0
 	r = x << y
 	if r != 127 {
-		t.Errorf("127 << 0 = %d, want 127", r)
+		t.Errorf("127 %s 0 = %d, want 127", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("127 << 1 = %d, want -2", r)
+		t.Errorf("127 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 4294967295
 	r = x << y
 	if r != 0 {
-		t.Errorf("127 << 4294967295 = %d, want 0", r)
+		t.Errorf("127 %s 4294967295 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint8uint32rsh(t *testing.T) {
@@ -11841,113 +11841,113 @@ func TestConstFoldint8uint32rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -128 {
-		t.Errorf("-128 >> 0 = %d, want -128", r)
+		t.Errorf("-128 %s 0 = %d, want -128", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -64 {
-		t.Errorf("-128 >> 1 = %d, want -64", r)
+		t.Errorf("-128 %s 1 = %d, want -64", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-128 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-128 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = -127
 	y = 0
 	r = x >> y
 	if r != -127 {
-		t.Errorf("-127 >> 0 = %d, want -127", r)
+		t.Errorf("-127 %s 0 = %d, want -127", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -64 {
-		t.Errorf("-127 >> 1 = %d, want -64", r)
+		t.Errorf("-127 %s 1 = %d, want -64", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-127 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-127 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 4294967295 = %d, want -1", r)
+		t.Errorf("-1 %s 4294967295 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 4294967295 = %d, want 0", r)
+		t.Errorf("0 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 4294967295 = %d, want 0", r)
+		t.Errorf("1 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 126
 	y = 0
 	r = x >> y
 	if r != 126 {
-		t.Errorf("126 >> 0 = %d, want 126", r)
+		t.Errorf("126 %s 0 = %d, want 126", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 63 {
-		t.Errorf("126 >> 1 = %d, want 63", r)
+		t.Errorf("126 %s 1 = %d, want 63", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("126 >> 4294967295 = %d, want 0", r)
+		t.Errorf("126 %s 4294967295 = %d, want 0", ">>", r)
 	}
 	x = 127
 	y = 0
 	r = x >> y
 	if r != 127 {
-		t.Errorf("127 >> 0 = %d, want 127", r)
+		t.Errorf("127 %s 0 = %d, want 127", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 63 {
-		t.Errorf("127 >> 1 = %d, want 63", r)
+		t.Errorf("127 %s 1 = %d, want 63", ">>", r)
 	}
 	y = 4294967295
 	r = x >> y
 	if r != 0 {
-		t.Errorf("127 >> 4294967295 = %d, want 0", r)
+		t.Errorf("127 %s 4294967295 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint8uint16lsh(t *testing.T) {
@@ -11957,113 +11957,113 @@ func TestConstFoldint8uint16lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -128 {
-		t.Errorf("-128 << 0 = %d, want -128", r)
+		t.Errorf("-128 %s 0 = %d, want -128", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-128 << 1 = %d, want 0", r)
+		t.Errorf("-128 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-128 << 65535 = %d, want 0", r)
+		t.Errorf("-128 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = -127
 	y = 0
 	r = x << y
 	if r != -127 {
-		t.Errorf("-127 << 0 = %d, want -127", r)
+		t.Errorf("-127 %s 0 = %d, want -127", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-127 << 1 = %d, want 2", r)
+		t.Errorf("-127 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-127 << 65535 = %d, want 0", r)
+		t.Errorf("-127 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 65535 = %d, want 0", r)
+		t.Errorf("-1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 126
 	y = 0
 	r = x << y
 	if r != 126 {
-		t.Errorf("126 << 0 = %d, want 126", r)
+		t.Errorf("126 %s 0 = %d, want 126", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("126 << 1 = %d, want -4", r)
+		t.Errorf("126 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("126 << 65535 = %d, want 0", r)
+		t.Errorf("126 %s 65535 = %d, want 0", "<<", r)
 	}
 	x = 127
 	y = 0
 	r = x << y
 	if r != 127 {
-		t.Errorf("127 << 0 = %d, want 127", r)
+		t.Errorf("127 %s 0 = %d, want 127", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("127 << 1 = %d, want -2", r)
+		t.Errorf("127 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 65535
 	r = x << y
 	if r != 0 {
-		t.Errorf("127 << 65535 = %d, want 0", r)
+		t.Errorf("127 %s 65535 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint8uint16rsh(t *testing.T) {
@@ -12073,113 +12073,113 @@ func TestConstFoldint8uint16rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -128 {
-		t.Errorf("-128 >> 0 = %d, want -128", r)
+		t.Errorf("-128 %s 0 = %d, want -128", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -64 {
-		t.Errorf("-128 >> 1 = %d, want -64", r)
+		t.Errorf("-128 %s 1 = %d, want -64", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-128 >> 65535 = %d, want -1", r)
+		t.Errorf("-128 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = -127
 	y = 0
 	r = x >> y
 	if r != -127 {
-		t.Errorf("-127 >> 0 = %d, want -127", r)
+		t.Errorf("-127 %s 0 = %d, want -127", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -64 {
-		t.Errorf("-127 >> 1 = %d, want -64", r)
+		t.Errorf("-127 %s 1 = %d, want -64", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-127 >> 65535 = %d, want -1", r)
+		t.Errorf("-127 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 65535 = %d, want -1", r)
+		t.Errorf("-1 %s 65535 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 65535 = %d, want 0", r)
+		t.Errorf("0 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 65535 = %d, want 0", r)
+		t.Errorf("1 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 126
 	y = 0
 	r = x >> y
 	if r != 126 {
-		t.Errorf("126 >> 0 = %d, want 126", r)
+		t.Errorf("126 %s 0 = %d, want 126", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 63 {
-		t.Errorf("126 >> 1 = %d, want 63", r)
+		t.Errorf("126 %s 1 = %d, want 63", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("126 >> 65535 = %d, want 0", r)
+		t.Errorf("126 %s 65535 = %d, want 0", ">>", r)
 	}
 	x = 127
 	y = 0
 	r = x >> y
 	if r != 127 {
-		t.Errorf("127 >> 0 = %d, want 127", r)
+		t.Errorf("127 %s 0 = %d, want 127", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 63 {
-		t.Errorf("127 >> 1 = %d, want 63", r)
+		t.Errorf("127 %s 1 = %d, want 63", ">>", r)
 	}
 	y = 65535
 	r = x >> y
 	if r != 0 {
-		t.Errorf("127 >> 65535 = %d, want 0", r)
+		t.Errorf("127 %s 65535 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldint8uint8lsh(t *testing.T) {
@@ -12189,113 +12189,113 @@ func TestConstFoldint8uint8lsh(t *testing.T) {
 	y = 0
 	r = x << y
 	if r != -128 {
-		t.Errorf("-128 << 0 = %d, want -128", r)
+		t.Errorf("-128 %s 0 = %d, want -128", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("-128 << 1 = %d, want 0", r)
+		t.Errorf("-128 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-128 << 255 = %d, want 0", r)
+		t.Errorf("-128 %s 255 = %d, want 0", "<<", r)
 	}
 	x = -127
 	y = 0
 	r = x << y
 	if r != -127 {
-		t.Errorf("-127 << 0 = %d, want -127", r)
+		t.Errorf("-127 %s 0 = %d, want -127", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("-127 << 1 = %d, want 2", r)
+		t.Errorf("-127 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-127 << 255 = %d, want 0", r)
+		t.Errorf("-127 %s 255 = %d, want 0", "<<", r)
 	}
 	x = -1
 	y = 0
 	r = x << y
 	if r != -1 {
-		t.Errorf("-1 << 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("-1 << 1 = %d, want -2", r)
+		t.Errorf("-1 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("-1 << 255 = %d, want 0", r)
+		t.Errorf("-1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 0
 	y = 0
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("0 << 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 1
 	y = 0
 	r = x << y
 	if r != 1 {
-		t.Errorf("1 << 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != 2 {
-		t.Errorf("1 << 1 = %d, want 2", r)
+		t.Errorf("1 %s 1 = %d, want 2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("1 << 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 126
 	y = 0
 	r = x << y
 	if r != 126 {
-		t.Errorf("126 << 0 = %d, want 126", r)
+		t.Errorf("126 %s 0 = %d, want 126", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -4 {
-		t.Errorf("126 << 1 = %d, want -4", r)
+		t.Errorf("126 %s 1 = %d, want -4", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("126 << 255 = %d, want 0", r)
+		t.Errorf("126 %s 255 = %d, want 0", "<<", r)
 	}
 	x = 127
 	y = 0
 	r = x << y
 	if r != 127 {
-		t.Errorf("127 << 0 = %d, want 127", r)
+		t.Errorf("127 %s 0 = %d, want 127", "<<", r)
 	}
 	y = 1
 	r = x << y
 	if r != -2 {
-		t.Errorf("127 << 1 = %d, want -2", r)
+		t.Errorf("127 %s 1 = %d, want -2", "<<", r)
 	}
 	y = 255
 	r = x << y
 	if r != 0 {
-		t.Errorf("127 << 255 = %d, want 0", r)
+		t.Errorf("127 %s 255 = %d, want 0", "<<", r)
 	}
 }
 func TestConstFoldint8uint8rsh(t *testing.T) {
@@ -12305,113 +12305,113 @@ func TestConstFoldint8uint8rsh(t *testing.T) {
 	y = 0
 	r = x >> y
 	if r != -128 {
-		t.Errorf("-128 >> 0 = %d, want -128", r)
+		t.Errorf("-128 %s 0 = %d, want -128", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -64 {
-		t.Errorf("-128 >> 1 = %d, want -64", r)
+		t.Errorf("-128 %s 1 = %d, want -64", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-128 >> 255 = %d, want -1", r)
+		t.Errorf("-128 %s 255 = %d, want -1", ">>", r)
 	}
 	x = -127
 	y = 0
 	r = x >> y
 	if r != -127 {
-		t.Errorf("-127 >> 0 = %d, want -127", r)
+		t.Errorf("-127 %s 0 = %d, want -127", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -64 {
-		t.Errorf("-127 >> 1 = %d, want -64", r)
+		t.Errorf("-127 %s 1 = %d, want -64", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-127 >> 255 = %d, want -1", r)
+		t.Errorf("-127 %s 255 = %d, want -1", ">>", r)
 	}
 	x = -1
 	y = 0
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 0 = %d, want -1", r)
+		t.Errorf("-1 %s 0 = %d, want -1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 1 = %d, want -1", r)
+		t.Errorf("-1 %s 1 = %d, want -1", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != -1 {
-		t.Errorf("-1 >> 255 = %d, want -1", r)
+		t.Errorf("-1 %s 255 = %d, want -1", ">>", r)
 	}
 	x = 0
 	y = 0
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 0 = %d, want 0", r)
+		t.Errorf("0 %s 0 = %d, want 0", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 1 = %d, want 0", r)
+		t.Errorf("0 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("0 >> 255 = %d, want 0", r)
+		t.Errorf("0 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 1
 	y = 0
 	r = x >> y
 	if r != 1 {
-		t.Errorf("1 >> 0 = %d, want 1", r)
+		t.Errorf("1 %s 0 = %d, want 1", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 1 = %d, want 0", r)
+		t.Errorf("1 %s 1 = %d, want 0", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("1 >> 255 = %d, want 0", r)
+		t.Errorf("1 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 126
 	y = 0
 	r = x >> y
 	if r != 126 {
-		t.Errorf("126 >> 0 = %d, want 126", r)
+		t.Errorf("126 %s 0 = %d, want 126", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 63 {
-		t.Errorf("126 >> 1 = %d, want 63", r)
+		t.Errorf("126 %s 1 = %d, want 63", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("126 >> 255 = %d, want 0", r)
+		t.Errorf("126 %s 255 = %d, want 0", ">>", r)
 	}
 	x = 127
 	y = 0
 	r = x >> y
 	if r != 127 {
-		t.Errorf("127 >> 0 = %d, want 127", r)
+		t.Errorf("127 %s 0 = %d, want 127", ">>", r)
 	}
 	y = 1
 	r = x >> y
 	if r != 63 {
-		t.Errorf("127 >> 1 = %d, want 63", r)
+		t.Errorf("127 %s 1 = %d, want 63", ">>", r)
 	}
 	y = 255
 	r = x >> y
 	if r != 0 {
-		t.Errorf("127 >> 255 = %d, want 0", r)
+		t.Errorf("127 %s 255 = %d, want 0", ">>", r)
 	}
 }
 func TestConstFoldCompareuint64(t *testing.T) {
diff --git a/src/cmd/compile/internal/gc/cplx.go b/src/cmd/compile/internal/gc/cplx.go
deleted file mode 100644
index 96a1dfb..0000000
--- a/src/cmd/compile/internal/gc/cplx.go
+++ /dev/null
@@ -1,474 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gc
-
-import "cmd/internal/obj"
-
-func overlap_cplx(f *Node, t *Node) bool {
-	// check whether f and t could be overlapping stack references.
-	// not exact, because it's hard to check for the stack register
-	// in portable code.  close enough: worst case we will allocate
-	// an extra temporary and the registerizer will clean it up.
-	return f.Op == OINDREG && t.Op == OINDREG && f.Xoffset+f.Type.Width >= t.Xoffset && t.Xoffset+t.Type.Width >= f.Xoffset
-}
-
-func complexbool(op Op, nl, nr, res *Node, wantTrue bool, likely int, to *obj.Prog) {
-	// make both sides addable in ullman order
-	if nr != nil {
-		if nl.Ullman > nr.Ullman && !nl.Addable {
-			nl = CgenTemp(nl)
-		}
-
-		if !nr.Addable {
-			nr = CgenTemp(nr)
-		}
-	}
-	if !nl.Addable {
-		nl = CgenTemp(nl)
-	}
-
-	// Break nl and nr into real and imaginary components.
-	var lreal, limag, rreal, rimag Node
-	subnode(&lreal, &limag, nl)
-	subnode(&rreal, &rimag, nr)
-
-	// build tree
-	// if branching:
-	// 	real(l) == real(r) && imag(l) == imag(r)
-	// if generating a value, use a branch-free version:
-	// 	real(l) == real(r) & imag(l) == imag(r)
-	realeq := Node{
-		Op:    OEQ,
-		Left:  &lreal,
-		Right: &rreal,
-		Type:  Types[TBOOL],
-	}
-	imageq := Node{
-		Op:    OEQ,
-		Left:  &limag,
-		Right: &rimag,
-		Type:  Types[TBOOL],
-	}
-	and := Node{
-		Op:    OANDAND,
-		Left:  &realeq,
-		Right: &imageq,
-		Type:  Types[TBOOL],
-	}
-
-	if res != nil {
-		// generating a value
-		and.Op = OAND
-		if op == ONE {
-			and.Op = OOR
-			realeq.Op = ONE
-			imageq.Op = ONE
-		}
-		Bvgen(&and, res, true)
-		return
-	}
-
-	// generating a branch
-	if op == ONE {
-		wantTrue = !wantTrue
-	}
-
-	Bgen(&and, wantTrue, likely, to)
-}
-
-// break addable nc-complex into nr-real and ni-imaginary
-func subnode(nr *Node, ni *Node, nc *Node) {
-	if !nc.Addable {
-		Fatalf("subnode not addable")
-	}
-
-	tc := Simsimtype(nc.Type)
-	tc = cplxsubtype(tc)
-	t := Types[tc]
-
-	if nc.Op == OLITERAL {
-		u := nc.Val().U.(*Mpcplx)
-		nodfconst(nr, t, &u.Real)
-		nodfconst(ni, t, &u.Imag)
-		return
-	}
-
-	*nr = *nc
-	nr.Type = t
-
-	*ni = *nc
-	ni.Type = t
-	ni.Xoffset += t.Width
-}
-
-// generate code res = -nl
-func minus(nl *Node, res *Node) {
-	var ra Node
-	ra.Op = OMINUS
-	ra.Left = nl
-	ra.Type = nl.Type
-	Cgen(&ra, res)
-}
-
-// build and execute tree
-//	real(res) = -real(nl)
-//	imag(res) = -imag(nl)
-func complexminus(nl *Node, res *Node) {
-	var n1 Node
-	var n2 Node
-	var n5 Node
-	var n6 Node
-
-	subnode(&n1, &n2, nl)
-	subnode(&n5, &n6, res)
-
-	minus(&n1, &n5)
-	minus(&n2, &n6)
-}
-
-// build and execute tree
-//	real(res) = real(nl) op real(nr)
-//	imag(res) = imag(nl) op imag(nr)
-func complexadd(op Op, nl *Node, nr *Node, res *Node) {
-	var n1 Node
-	var n2 Node
-	var n3 Node
-	var n4 Node
-	var n5 Node
-	var n6 Node
-
-	subnode(&n1, &n2, nl)
-	subnode(&n3, &n4, nr)
-	subnode(&n5, &n6, res)
-
-	var ra Node
-	ra.Op = op
-	ra.Left = &n1
-	ra.Right = &n3
-	ra.Type = n1.Type
-	Cgen(&ra, &n5)
-
-	ra = Node{}
-	ra.Op = op
-	ra.Left = &n2
-	ra.Right = &n4
-	ra.Type = n2.Type
-	Cgen(&ra, &n6)
-}
-
-// build and execute tree
-//	tmp       = real(nl)*real(nr) - imag(nl)*imag(nr)
-//	imag(res) = real(nl)*imag(nr) + imag(nl)*real(nr)
-//	real(res) = tmp
-func complexmul(nl *Node, nr *Node, res *Node) {
-	var n1 Node
-	var n2 Node
-	var n3 Node
-	var n4 Node
-	var n5 Node
-	var n6 Node
-	var tmp Node
-
-	subnode(&n1, &n2, nl)
-	subnode(&n3, &n4, nr)
-	subnode(&n5, &n6, res)
-	Tempname(&tmp, n5.Type)
-
-	// real part -> tmp
-	var rm1 Node
-
-	rm1.Op = OMUL
-	rm1.Left = &n1
-	rm1.Right = &n3
-	rm1.Type = n1.Type
-
-	var rm2 Node
-	rm2.Op = OMUL
-	rm2.Left = &n2
-	rm2.Right = &n4
-	rm2.Type = n2.Type
-
-	var ra Node
-	ra.Op = OSUB
-	ra.Left = &rm1
-	ra.Right = &rm2
-	ra.Type = rm1.Type
-	Cgen(&ra, &tmp)
-
-	// imag part
-	rm1 = Node{}
-
-	rm1.Op = OMUL
-	rm1.Left = &n1
-	rm1.Right = &n4
-	rm1.Type = n1.Type
-
-	rm2 = Node{}
-	rm2.Op = OMUL
-	rm2.Left = &n2
-	rm2.Right = &n3
-	rm2.Type = n2.Type
-
-	ra = Node{}
-	ra.Op = OADD
-	ra.Left = &rm1
-	ra.Right = &rm2
-	ra.Type = rm1.Type
-	Cgen(&ra, &n6)
-
-	// tmp ->real part
-	Cgen(&tmp, &n5)
-}
-
-func nodfconst(n *Node, t *Type, fval *Mpflt) {
-	*n = Node{}
-	n.Op = OLITERAL
-	n.Addable = true
-	ullmancalc(n)
-	n.SetVal(Val{fval})
-	n.Type = t
-
-	if !t.IsFloat() {
-		Fatalf("nodfconst: bad type %v", t)
-	}
-}
-
-func Complexop(n *Node, res *Node) bool {
-	if n != nil && n.Type != nil {
-		if n.Type.IsComplex() {
-			goto maybe
-		}
-	}
-
-	if res != nil && res.Type != nil {
-		if res.Type.IsComplex() {
-			goto maybe
-		}
-	}
-
-	if n.Op == OREAL || n.Op == OIMAG {
-		//dump("\ncomplex-yes", n);
-		return true
-	}
-
-	//dump("\ncomplex-no", n);
-	return false
-
-maybe:
-	switch n.Op {
-	case OCONV, // implemented ops
-		OADD,
-		OSUB,
-		OMUL,
-		OMINUS,
-		OCOMPLEX,
-		OREAL,
-		OIMAG:
-		//dump("\ncomplex-yes", n);
-		return true
-
-	case ODOT,
-		ODOTPTR,
-		OINDEX,
-		OIND,
-		ONAME:
-		//dump("\ncomplex-yes", n);
-		return true
-	}
-
-	//dump("\ncomplex-no", n);
-	return false
-}
-
-func Complexmove(f *Node, t *Node) {
-	if Debug['g'] != 0 {
-		Dump("\ncomplexmove-f", f)
-		Dump("complexmove-t", t)
-	}
-
-	if !t.Addable {
-		Fatalf("complexmove: to not addable")
-	}
-
-	ft := Simsimtype(f.Type)
-	tt := Simsimtype(t.Type)
-	// complex to complex move/convert.
-	// make f addable.
-	// also use temporary if possible stack overlap.
-	if (ft == TCOMPLEX64 || ft == TCOMPLEX128) && (tt == TCOMPLEX64 || tt == TCOMPLEX128) {
-		if !f.Addable || overlap_cplx(f, t) {
-			var tmp Node
-			Tempname(&tmp, f.Type)
-			Complexmove(f, &tmp)
-			f = &tmp
-		}
-
-		var n1 Node
-		var n2 Node
-		subnode(&n1, &n2, f)
-		var n4 Node
-		var n3 Node
-		subnode(&n3, &n4, t)
-
-		Cgen(&n1, &n3)
-		Cgen(&n2, &n4)
-	} else {
-		Fatalf("complexmove: unknown conversion: %v -> %v\n", f.Type, t.Type)
-	}
-}
-
-func Complexgen(n *Node, res *Node) {
-	if Debug['g'] != 0 {
-		Dump("\ncomplexgen-n", n)
-		Dump("complexgen-res", res)
-	}
-
-	for n.Op == OCONVNOP {
-		n = n.Left
-	}
-
-	// pick off float/complex opcodes
-	switch n.Op {
-	case OCOMPLEX:
-		if res.Addable {
-			var n1 Node
-			var n2 Node
-			subnode(&n1, &n2, res)
-			var tmp Node
-			Tempname(&tmp, n1.Type)
-			Cgen(n.Left, &tmp)
-			Cgen(n.Right, &n2)
-			Cgen(&tmp, &n1)
-			return
-		}
-
-	case OREAL, OIMAG:
-		nl := n.Left
-		if !nl.Addable {
-			var tmp Node
-			Tempname(&tmp, nl.Type)
-			Complexgen(nl, &tmp)
-			nl = &tmp
-		}
-
-		var n1 Node
-		var n2 Node
-		subnode(&n1, &n2, nl)
-		if n.Op == OREAL {
-			Cgen(&n1, res)
-			return
-		}
-
-		Cgen(&n2, res)
-		return
-	}
-
-	// perform conversion from n to res
-	tl := Simsimtype(res.Type)
-
-	tl = cplxsubtype(tl)
-	tr := Simsimtype(n.Type)
-	tr = cplxsubtype(tr)
-	if tl != tr {
-		if !n.Addable {
-			var n1 Node
-			Tempname(&n1, n.Type)
-			Complexmove(n, &n1)
-			n = &n1
-		}
-
-		Complexmove(n, res)
-		return
-	}
-
-	if !res.Addable {
-		var n1 Node
-		Igen(res, &n1, nil)
-		Cgen(n, &n1)
-		Regfree(&n1)
-		return
-	}
-
-	if n.Addable {
-		Complexmove(n, res)
-		return
-	}
-
-	switch n.Op {
-	default:
-		Dump("complexgen: unknown op", n)
-		Fatalf("complexgen: unknown op %v", n.Op)
-
-	case ODOT,
-		ODOTPTR,
-		OINDEX,
-		OIND,
-		OCALLFUNC,
-		OCALLMETH,
-		OCALLINTER:
-		var n1 Node
-		Igen(n, &n1, res)
-
-		Complexmove(&n1, res)
-		Regfree(&n1)
-		return
-
-	case OCONV,
-		OADD,
-		OSUB,
-		OMUL,
-		OMINUS,
-		OCOMPLEX,
-		OREAL,
-		OIMAG:
-		break
-	}
-
-	nl := n.Left
-	if nl == nil {
-		return
-	}
-	nr := n.Right
-
-	// make both sides addable in ullman order
-	var tnl Node
-	if nr != nil {
-		if nl.Ullman > nr.Ullman && !nl.Addable {
-			Tempname(&tnl, nl.Type)
-			Cgen(nl, &tnl)
-			nl = &tnl
-		}
-
-		if !nr.Addable {
-			var tnr Node
-			Tempname(&tnr, nr.Type)
-			Cgen(nr, &tnr)
-			nr = &tnr
-		}
-	}
-
-	if !nl.Addable {
-		Tempname(&tnl, nl.Type)
-		Cgen(nl, &tnl)
-		nl = &tnl
-	}
-
-	switch n.Op {
-	default:
-		Fatalf("complexgen: unknown op %v", n.Op)
-
-	case OCONV:
-		Complexmove(nl, res)
-
-	case OMINUS:
-		complexminus(nl, res)
-
-	case OADD, OSUB:
-		complexadd(n.Op, nl, nr, res)
-
-	case OMUL:
-		complexmul(nl, nr, res)
-	}
-}
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index a4b98ec..3cdd71d 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -106,7 +106,7 @@ func testdclstack() {
 			if nerrors != 0 {
 				errorexit()
 			}
-			Yyerror("mark left on the stack")
+			yyerror("mark left on the stack")
 		}
 	}
 }
@@ -121,7 +121,8 @@ func redeclare(s *Sym, where string) {
 			tmp = s.Pkg.Path
 		}
 		pkgstr := tmp
-		Yyerror("%v redeclared %s\n"+"\tprevious declaration during import %q", s, where, pkgstr)
+		yyerror("%v redeclared %s\n"+
+			"\tprevious declaration during import %q", s, where, pkgstr)
 	} else {
 		line1 := lineno
 		line2 := s.Lastlineno
@@ -135,7 +136,8 @@ func redeclare(s *Sym, where string) {
 			line1 = s.Lastlineno
 		}
 
-		yyerrorl(line1, "%v redeclared %s\n"+"\tprevious declaration at %v", s, where, linestr(line2))
+		yyerrorl(line1, "%v redeclared %s\n"+
+			"\tprevious declaration at %v", s, where, linestr(line2))
 	}
 }
 
@@ -165,11 +167,11 @@ func declare(n *Node, ctxt Class) {
 
 	// kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later.
 	if importpkg == nil && !typecheckok && s.Pkg != localpkg {
-		Yyerror("cannot declare name %v", s)
+		yyerror("cannot declare name %v", s)
 	}
 
 	if ctxt == PEXTERN && s.Name == "init" {
-		Yyerror("cannot declare init - must be func")
+		yyerror("cannot declare init - must be func")
 	}
 
 	gen := 0
@@ -209,7 +211,7 @@ func declare(n *Node, ctxt Class) {
 	s.Lastlineno = lineno
 	s.Def = n
 	n.Name.Vargen = int32(gen)
-	n.Name.Funcdepth = Funcdepth
+	n.Name.Funcdepth = funcdepth
 	n.Class = ctxt
 
 	autoexport(n, ctxt)
@@ -233,7 +235,7 @@ func variter(vl []*Node, t *Node, el []*Node) []*Node {
 
 	if len(el) == 1 && len(vl) > 1 {
 		e := el[0]
-		as2 := Nod(OAS2, nil, nil)
+		as2 := nod(OAS2, nil, nil)
 		as2.List.Set(vl)
 		as2.Rlist.Set1(e)
 		for _, v := range vl {
@@ -241,8 +243,8 @@ func variter(vl []*Node, t *Node, el []*Node) []*Node {
 			declare(v, dclcontext)
 			v.Name.Param.Ntype = t
 			v.Name.Defn = as2
-			if Funcdepth > 0 {
-				init = append(init, Nod(ODCL, v, nil))
+			if funcdepth > 0 {
+				init = append(init, nod(ODCL, v, nil))
 			}
 		}
 
@@ -253,7 +255,7 @@ func variter(vl []*Node, t *Node, el []*Node) []*Node {
 		var e *Node
 		if doexpr {
 			if len(el) == 0 {
-				Yyerror("missing expression in var declaration")
+				yyerror("missing expression in var declaration")
 				break
 			}
 			e = el[0]
@@ -264,11 +266,11 @@ func variter(vl []*Node, t *Node, el []*Node) []*Node {
 		declare(v, dclcontext)
 		v.Name.Param.Ntype = t
 
-		if e != nil || Funcdepth > 0 || isblank(v) {
-			if Funcdepth > 0 {
-				init = append(init, Nod(ODCL, v, nil))
+		if e != nil || funcdepth > 0 || isblank(v) {
+			if funcdepth > 0 {
+				init = append(init, nod(ODCL, v, nil))
 			}
-			e = Nod(OAS, v, e)
+			e = nod(OAS, v, e)
 			init = append(init, e)
 			if e.Right != nil {
 				v.Name.Defn = e
@@ -277,7 +279,7 @@ func variter(vl []*Node, t *Node, el []*Node) []*Node {
 	}
 
 	if len(el) != 0 {
-		Yyerror("extra expression in var declaration")
+		yyerror("extra expression in var declaration")
 	}
 	return init
 }
@@ -288,7 +290,7 @@ func constiter(vl []*Node, t *Node, cl []*Node) []*Node {
 	lno := int32(0) // default is to leave line number alone in listtreecopy
 	if len(cl) == 0 {
 		if t != nil {
-			Yyerror("const declaration cannot have type without expression")
+			yyerror("const declaration cannot have type without expression")
 		}
 		cl = lastconst
 		t = lasttype
@@ -302,7 +304,7 @@ func constiter(vl []*Node, t *Node, cl []*Node) []*Node {
 	var vv []*Node
 	for _, v := range vl {
 		if len(clcopy) == 0 {
-			Yyerror("missing value in const declaration")
+			yyerror("missing value in const declaration")
 			break
 		}
 
@@ -315,11 +317,11 @@ func constiter(vl []*Node, t *Node, cl []*Node) []*Node {
 		v.Name.Param.Ntype = t
 		v.Name.Defn = c
 
-		vv = append(vv, Nod(ODCLCONST, v, nil))
+		vv = append(vv, nod(ODCLCONST, v, nil))
 	}
 
 	if len(clcopy) != 0 {
-		Yyerror("extra expression in const declaration")
+		yyerror("extra expression in const declaration")
 	}
 	iota_ += 1
 	return vv
@@ -330,10 +332,21 @@ func newname(s *Sym) *Node {
 	if s == nil {
 		Fatalf("newname nil")
 	}
+	n := nod(ONAME, nil, nil)
+	n.Sym = s
+	n.Addable = true
+	n.Ullman = 1
+	n.Xoffset = 0
+	return n
+}
 
-	n := Nod(ONAME, nil, nil)
+// newnoname returns a new ONONAME Node associated with symbol s.
+func newnoname(s *Sym) *Node {
+	if s == nil {
+		Fatalf("newnoname nil")
+	}
+	n := nod(ONONAME, nil, nil)
 	n.Sym = s
-	n.Type = nil
 	n.Addable = true
 	n.Ullman = 1
 	n.Xoffset = 0
@@ -345,7 +358,7 @@ func newname(s *Sym) *Node {
 func newfuncname(s *Sym) *Node {
 	n := newname(s)
 	n.Func = new(Func)
-	n.Func.FCurfn = Curfn
+	n.Func.IsHiddenClosure = Curfn != nil
 	return n
 }
 
@@ -361,13 +374,21 @@ func typenod(t *Type) *Node {
 	// if we copied another type with *t = *u
 	// then t->nod might be out of date, so
 	// check t->nod->type too
-	if t.Nod == nil || t.Nod.Type != t {
-		t.Nod = Nod(OTYPE, nil, nil)
-		t.Nod.Type = t
-		t.Nod.Sym = t.Sym
+	if t.nod == nil || t.nod.Type != t {
+		t.nod = nod(OTYPE, nil, nil)
+		t.nod.Type = t
+		t.nod.Sym = t.Sym
 	}
 
-	return t.Nod
+	return t.nod
+}
+
+func anonfield(typ *Type) *Node {
+	return nod(ODCLFIELD, nil, typenod(typ))
+}
+
+func namedfield(s string, typ *Type) *Node {
+	return nod(ODCLFIELD, newname(lookup(s)), typenod(typ))
 }
 
 // oldname returns the Node that declares symbol s in the current scope.
@@ -378,13 +399,12 @@ func oldname(s *Sym) *Node {
 		// Maybe a top-level declaration will come along later to
 		// define s. resolve will check s.Def again once all input
 		// source has been processed.
-		n = newname(s)
-		n.Op = ONONAME
-		n.Name.Iota = iota_ // save current iota value in const declarations
+		n = newnoname(s)
+		n.SetIota(iota_) // save current iota value in const declarations
 		return n
 	}
 
-	if Curfn != nil && n.Op == ONAME && n.Name.Funcdepth > 0 && n.Name.Funcdepth != Funcdepth {
+	if Curfn != nil && n.Op == ONAME && n.Name.Funcdepth > 0 && n.Name.Funcdepth != funcdepth {
 		// Inner func is referring to var in outer func.
 		//
 		// TODO(rsc): If there is an outer variable x and we
@@ -392,9 +412,9 @@ func oldname(s *Sym) *Node {
 		// the := it looks like a reference to the outer x so we'll
 		// make x a closure variable unnecessarily.
 		c := n.Name.Param.Innermost
-		if c == nil || c.Name.Funcdepth != Funcdepth {
+		if c == nil || c.Name.Funcdepth != funcdepth {
 			// Do not have a closure var for the active closure yet; make one.
-			c = Nod(ONAME, nil, nil)
+			c = nod(ONAME, nil, nil)
 			c.Sym = s
 			c.Class = PAUTOHEAP
 			c.setIsClosureVar(true)
@@ -402,7 +422,7 @@ func oldname(s *Sym) *Node {
 			c.Name.Defn = n
 			c.Addable = false
 			c.Ullman = 2
-			c.Name.Funcdepth = Funcdepth
+			c.Name.Funcdepth = funcdepth
 
 			// Link into list of active closure variables.
 			// Popped from list in func closurebody.
@@ -454,7 +474,7 @@ func colasdefn(left []*Node, defn *Node) {
 
 		if n.Sym.Flags&SymUniq == 0 {
 			yyerrorl(defn.Lineno, "%v repeated on left side of :=", n.Sym)
-			n.Diag++
+			n.Diag = true
 			nerr++
 			continue
 		}
@@ -468,7 +488,7 @@ func colasdefn(left []*Node, defn *Node) {
 		n = newname(n.Sym)
 		declare(n, dclcontext)
 		n.Name.Defn = defn
-		defn.Ninit.Append(Nod(ODCL, n, nil))
+		defn.Ninit.Append(nod(ODCL, n, nil))
 		left[i] = n
 	}
 
@@ -477,23 +497,6 @@ func colasdefn(left []*Node, defn *Node) {
 	}
 }
 
-func colas(left, right []*Node, lno int32) *Node {
-	n := Nod(OAS, nil, nil) // assume common case
-	n.Colas = true
-	n.Lineno = lno     // set before calling colasdefn for correct error line
-	colasdefn(left, n) // modifies left, call before using left[0] in common case
-	if len(left) == 1 && len(right) == 1 {
-		// common case
-		n.Left = left[0]
-		n.Right = right[0]
-	} else {
-		n.Op = OAS2
-		n.List.Set(left)
-		n.Rlist.Set(right)
-	}
-	return n
-}
-
 // declare the arguments in an
 // interface field declaration.
 func ifacedcl(n *Node) {
@@ -502,23 +505,8 @@ func ifacedcl(n *Node) {
 	}
 
 	if isblank(n.Left) {
-		Yyerror("methods must have a unique non-blank name")
+		yyerror("methods must have a unique non-blank name")
 	}
-
-	n.Func = new(Func)
-	n.Func.FCurfn = Curfn
-	dclcontext = PPARAM
-
-	funcstart(n)
-	funcargs(n.Right)
-
-	// funcbody is normally called after the parser has
-	// seen the body of a function but since an interface
-	// field declaration does not have a body, we must
-	// call it now to pop the current declaration context.
-	dclcontext = PAUTO
-
-	funcbody(n)
 }
 
 // declare the function proper
@@ -527,11 +515,11 @@ func ifacedcl(n *Node) {
 // returns in auto-declaration context.
 func funchdr(n *Node) {
 	// change the declaration context from extern to auto
-	if Funcdepth == 0 && dclcontext != PEXTERN {
+	if funcdepth == 0 && dclcontext != PEXTERN {
 		Fatalf("funchdr: dclcontext = %d", dclcontext)
 	}
 
-	if importpkg == nil && n.Func.Nname != nil {
+	if Ctxt.Flag_dynlink && importpkg == nil && n.Func.Nname != nil {
 		makefuncsym(n.Func.Nname.Sym)
 	}
 
@@ -601,7 +589,7 @@ func funcargs(nt *Node) {
 
 		if n.Left == nil {
 			// Name so that escape analysis can track it. ~r stands for 'result'.
-			n.Left = newname(LookupN("~r", gen))
+			n.Left = newname(lookupN("~r", gen))
 			gen++
 		}
 
@@ -619,7 +607,7 @@ func funcargs(nt *Node) {
 			// Having multiple names causes too much confusion in later passes.
 			nn := *n.Left
 			nn.Orig = &nn
-			nn.Sym = LookupN("~b", gen)
+			nn.Sym = lookupN("~b", gen)
 			gen++
 			n.Left = &nn
 		}
@@ -670,14 +658,14 @@ func funcargs2(t *Type) {
 }
 
 var funcstack []*Node // stack of previous values of Curfn
-var Funcdepth int32   // len(funcstack) during parsing, but then forced to be the same later during compilation
+var funcdepth int32   // len(funcstack) during parsing, but then forced to be the same later during compilation
 
 // start the function.
 // called before funcargs; undone at end of funcbody.
 func funcstart(n *Node) {
 	markdcl()
 	funcstack = append(funcstack, Curfn)
-	Funcdepth++
+	funcdepth++
 	Curfn = n
 }
 
@@ -691,8 +679,8 @@ func funcbody(n *Node) {
 	}
 	popdcl()
 	funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1]
-	Funcdepth--
-	if Funcdepth == 0 {
+	funcdepth--
+	if funcdepth == 0 {
 		dclcontext = PEXTERN
 	}
 }
@@ -711,7 +699,7 @@ func typedcl0(s *Sym) *Node {
 func typedcl1(n *Node, t *Node, local bool) *Node {
 	n.Name.Param.Ntype = t
 	n.Local = local
-	return Nod(ODCLTYPE, n, nil)
+	return nod(ODCLTYPE, n, nil)
 }
 
 // structs, functions, and methods.
@@ -724,12 +712,12 @@ func checkembeddedtype(t *Type) {
 	if t.Sym == nil && t.IsPtr() {
 		t = t.Elem()
 		if t.IsInterface() {
-			Yyerror("embedded type cannot be a pointer to interface")
+			yyerror("embedded type cannot be a pointer to interface")
 		}
 	}
 
 	if t.IsPtr() || t.IsUnsafePtr() {
-		Yyerror("embedded type cannot be a pointer")
+		yyerror("embedded type cannot be a pointer")
 	} else if t.Etype == TFORW && t.ForwardType().Embedlineno == 0 {
 		t.ForwardType().Embedlineno = lineno
 	}
@@ -768,7 +756,7 @@ func structfield(n *Node) *Field {
 	case string:
 		f.Note = u
 	default:
-		Yyerror("field annotation must be string")
+		yyerror("field annotation must be string")
 	case nil:
 		// noop
 	}
@@ -796,7 +784,7 @@ func checkdupfields(what string, ts ...*Type) {
 			}
 			if seen[f.Sym] {
 				lineno = f.Nname.Lineno
-				Yyerror("duplicate %s %s", what, f.Sym.Name)
+				yyerror("duplicate %s %s", what, f.Sym.Name)
 				continue
 			}
 			seen[f.Sym] = true
@@ -858,6 +846,22 @@ func tofunargs(l []*Node, funarg Funarg) *Type {
 	return t
 }
 
+func tofunargsfield(fields []*Field, funarg Funarg) *Type {
+	t := typ(TSTRUCT)
+	t.StructType().Funarg = funarg
+
+	for _, f := range fields {
+		f.Funarg = funarg
+
+		// esc.go needs to find f given a PPARAM to add the tag.
+		if f.Nname != nil && f.Nname.Class == PPARAM {
+			f.Nname.Name.Param.Field = f
+		}
+	}
+	t.SetFields(fields)
+	return t
+}
+
 func interfacefield(n *Node) *Field {
 	lno := lineno
 	lineno = n.Lineno
@@ -867,7 +871,7 @@ func interfacefield(n *Node) *Field {
 	}
 
 	if n.Val().Ctype() != CTxxx {
-		Yyerror("interface method cannot have annotation")
+		yyerror("interface method cannot have annotation")
 	}
 
 	f := newField()
@@ -902,11 +906,11 @@ func interfacefield(n *Node) *Field {
 					break
 
 				case TFORW:
-					Yyerror("interface type loop involving %v", n.Type)
+					yyerror("interface type loop involving %v", n.Type)
 					f.Broke = true
 
 				default:
-					Yyerror("interface contains embedded non-interface %v", n.Type)
+					yyerror("interface contains embedded non-interface %v", n.Type)
 					f.Broke = true
 				}
 			}
@@ -982,40 +986,42 @@ func embedded(s *Sym, pkg *Pkg) *Node {
 
 	var n *Node
 	if exportname(name) {
-		n = newname(Lookup(name))
+		n = newname(lookup(name))
 	} else if s.Pkg == builtinpkg {
 		// The name of embedded builtins belongs to pkg.
 		n = newname(Pkglookup(name, pkg))
 	} else {
 		n = newname(Pkglookup(name, s.Pkg))
 	}
-	n = Nod(ODCLFIELD, n, oldname(s))
+	n = nod(ODCLFIELD, n, oldname(s))
 	n.Embedded = 1
 	return n
 }
 
+// thisT is the singleton type used for interface method receivers.
+var thisT *Type
+
 func fakethis() *Node {
-	n := Nod(ODCLFIELD, nil, typenod(Ptrto(typ(TSTRUCT))))
-	return n
+	if thisT == nil {
+		thisT = ptrto(typ(TSTRUCT))
+	}
+	return nod(ODCLFIELD, nil, typenod(thisT))
+}
+
+func fakethisfield() *Field {
+	if thisT == nil {
+		thisT = ptrto(typ(TSTRUCT))
+	}
+	f := newField()
+	f.Type = thisT
+	return f
 }
 
 // Is this field a method on an interface?
-// Those methods have an anonymous *struct{} as the receiver.
+// Those methods have thisT as the receiver.
 // (See fakethis above.)
 func isifacemethod(f *Type) bool {
-	rcvr := f.Recv()
-	if rcvr.Sym != nil {
-		return false
-	}
-	t := rcvr.Type
-	if !t.IsPtr() {
-		return false
-	}
-	t = t.Elem()
-	if t.Sym != nil || !t.IsStruct() || t.NumFields() != 0 {
-		return false
-	}
-	return true
+	return f.Recv().Type == thisT
 }
 
 // turn a parsed function declaration into a type
@@ -1034,9 +1040,9 @@ func functype0(t *Type, this *Node, in, out []*Node) {
 	if this != nil {
 		rcvr = []*Node{this}
 	}
-	*t.RecvsP() = tofunargs(rcvr, FunargRcvr)
-	*t.ResultsP() = tofunargs(out, FunargResults)
-	*t.ParamsP() = tofunargs(in, FunargParams)
+	t.FuncType().Receiver = tofunargs(rcvr, FunargRcvr)
+	t.FuncType().Results = tofunargs(out, FunargResults)
+	t.FuncType().Params = tofunargs(in, FunargParams)
 
 	checkdupfields("argument", t.Recvs(), t.Results(), t.Params())
 
@@ -1053,6 +1059,30 @@ func functype0(t *Type, this *Node, in, out []*Node) {
 	}
 }
 
+func functypefield(this *Field, in, out []*Field) *Type {
+	t := typ(TFUNC)
+	functypefield0(t, this, in, out)
+	return t
+}
+
+func functypefield0(t *Type, this *Field, in, out []*Field) {
+	var rcvr []*Field
+	if this != nil {
+		rcvr = []*Field{this}
+	}
+	t.FuncType().Receiver = tofunargsfield(rcvr, FunargRcvr)
+	t.FuncType().Results = tofunargsfield(out, FunargRcvr)
+	t.FuncType().Params = tofunargsfield(in, FunargRcvr)
+
+	t.FuncType().Outnamed = false
+	if len(out) > 0 && out[0].Nname != nil && out[0].Nname.Orig != nil {
+		s := out[0].Nname.Orig.Sym
+		if s != nil && (s.Name[0] != '~' || s.Name[1] != 'r') { // ~r%d is the name invented for an unnamed result
+			t.FuncType().Outnamed = true
+		}
+	}
+}
+
 var methodsym_toppkg *Pkg
 
 func methodsym(nsym *Sym, t0 *Type, iface int) *Sym {
@@ -1082,7 +1112,7 @@ func methodsym(nsym *Sym, t0 *Type, iface int) *Sym {
 	// if t0 == *t and t0 has a sym,
 	// we want to see *t, not t0, in the method name.
 	if t != t0 && t0.Sym != nil {
-		t0 = Ptrto(t)
+		t0 = ptrto(t)
 	}
 
 	suffix = ""
@@ -1095,15 +1125,15 @@ func methodsym(nsym *Sym, t0 *Type, iface int) *Sym {
 
 	if (spkg == nil || nsym.Pkg != spkg) && !exportname(nsym.Name) {
 		if t0.Sym == nil && t0.IsPtr() {
-			p = fmt.Sprintf("(%v).%s.%s%s", Tconv(t0, FmtLeft|FmtShort), nsym.Pkg.Prefix, nsym.Name, suffix)
+			p = fmt.Sprintf("(%-S).%s.%s%s", t0, nsym.Pkg.Prefix, nsym.Name, suffix)
 		} else {
-			p = fmt.Sprintf("%v.%s.%s%s", Tconv(t0, FmtLeft|FmtShort), nsym.Pkg.Prefix, nsym.Name, suffix)
+			p = fmt.Sprintf("%-S.%s.%s%s", t0, nsym.Pkg.Prefix, nsym.Name, suffix)
 		}
 	} else {
 		if t0.Sym == nil && t0.IsPtr() {
-			p = fmt.Sprintf("(%v).%s%s", Tconv(t0, FmtLeft|FmtShort), nsym.Name, suffix)
+			p = fmt.Sprintf("(%-S).%s%s", t0, nsym.Name, suffix)
 		} else {
-			p = fmt.Sprintf("%v.%s%s", Tconv(t0, FmtLeft|FmtShort), nsym.Name, suffix)
+			p = fmt.Sprintf("%-S.%s%s", t0, nsym.Name, suffix)
 		}
 	}
 
@@ -1119,50 +1149,45 @@ func methodsym(nsym *Sym, t0 *Type, iface int) *Sym {
 	return s
 
 bad:
-	Yyerror("illegal receiver type: %v", t0)
+	yyerror("illegal receiver type: %v", t0)
 	return nil
 }
 
-func methodname(n *Node, t *Type) *Node {
-	s := methodsym(n.Sym, t, 0)
-	if s == nil {
-		return n
-	}
-	return newname(s)
-}
-
-func methodname1(n *Node, t *Node) *Node {
-	star := ""
+func methodname(n *Node, t *Node) *Node {
+	star := false
 	if t.Op == OIND {
-		star = "*"
+		star = true
 		t = t.Left
 	}
 
-	if t.Sym == nil || isblank(n) {
-		return newfuncname(n.Sym)
+	return methodname0(n.Sym, star, t.Sym)
+}
+
+func methodname0(s *Sym, star bool, tsym *Sym) *Node {
+	if tsym == nil || isblanksym(s) {
+		return newfuncname(s)
 	}
 
 	var p string
-	if star != "" {
-		p = fmt.Sprintf("(%s%v).%v", star, t.Sym, n.Sym)
+	if star {
+		p = fmt.Sprintf("(*%v).%v", tsym, s)
 	} else {
-		p = fmt.Sprintf("%v.%v", t.Sym, n.Sym)
+		p = fmt.Sprintf("%v.%v", tsym, s)
 	}
 
-	if exportname(t.Sym.Name) {
-		n = newfuncname(Lookup(p))
+	if exportname(tsym.Name) {
+		s = lookup(p)
 	} else {
-		n = newfuncname(Pkglookup(p, t.Sym.Pkg))
+		s = Pkglookup(p, tsym.Pkg)
 	}
 
-	return n
+	return newfuncname(s)
 }
 
 // Add a method, declared as a function.
 // - msym is the method symbol
 // - t is function type (with receiver)
-// - tpkg is the package of the type declaring the method during import, or nil (ignored) --- for verification only
-func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) {
+func addmethod(msym *Sym, t *Type, local, nointerface bool) {
 	// get field sym
 	if msym == nil {
 		Fatalf("no method symbol")
@@ -1171,56 +1196,41 @@ func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) {
 	// get parent type sym
 	rf := t.Recv() // ptr to this structure
 	if rf == nil {
-		Yyerror("missing receiver")
+		yyerror("missing receiver")
 		return
 	}
 
-	pa := rf.Type // base type
-	mt := methtype(pa, 1)
-	if mt == nil {
-		t = pa
-		if t == nil { // rely on typecheck having complained before
-			return
-		}
-		if t != nil {
-			if t.IsPtr() {
-				if t.Sym != nil {
-					Yyerror("invalid receiver type %v (%v is a pointer type)", pa, t)
-					return
-				}
-
-				t = t.Elem()
-			}
-
-			if t.Broke { // rely on typecheck having complained before
-				return
-			}
-			if t.Sym == nil {
-				Yyerror("invalid receiver type %v (%v is an unnamed type)", pa, t)
-				return
-			}
-
-			if t.IsPtr() {
-				Yyerror("invalid receiver type %v (%v is a pointer type)", pa, t)
-				return
-			}
-
-			if t.IsInterface() {
-				Yyerror("invalid receiver type %v (%v is an interface type)", pa, t)
+	mt := methtype(rf.Type)
+	if mt == nil || mt.Sym == nil {
+		pa := rf.Type
+		t := pa
+		if t != nil && t.IsPtr() {
+			if t.Sym != nil {
+				yyerror("invalid receiver type %v (%v is a pointer type)", pa, t)
 				return
 			}
+			t = t.Elem()
 		}
 
-		// Should have picked off all the reasons above,
-		// but just in case, fall back to generic error.
-		Yyerror("invalid receiver type %v (%v / %v)", pa, Tconv(pa, FmtLong), Tconv(t, FmtLong))
-
+		switch {
+		case t == nil || t.Broke:
+			// rely on typecheck having complained before
+		case t.Sym == nil:
+			yyerror("invalid receiver type %v (%v is an unnamed type)", pa, t)
+		case t.IsPtr():
+			yyerror("invalid receiver type %v (%v is a pointer type)", pa, t)
+		case t.IsInterface():
+			yyerror("invalid receiver type %v (%v is an interface type)", pa, t)
+		default:
+			// Should have picked off all the reasons above,
+			// but just in case, fall back to generic error.
+			yyerror("invalid receiver type %v (%L / %L)", pa, pa, t)
+		}
 		return
 	}
 
-	pa = mt
-	if local && !pa.Local {
-		Yyerror("cannot define new methods on non-local type %v", pa)
+	if local && !mt.Local {
+		yyerror("cannot define new methods on non-local type %v", mt)
 		return
 	}
 
@@ -1228,39 +1238,34 @@ func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) {
 		return
 	}
 
-	if pa.IsStruct() {
-		for _, f := range pa.Fields().Slice() {
+	if mt.IsStruct() {
+		for _, f := range mt.Fields().Slice() {
 			if f.Sym == msym {
-				Yyerror("type %v has both field and method named %v", pa, msym)
+				yyerror("type %v has both field and method named %v", mt, msym)
 				return
 			}
 		}
 	}
 
-	n := Nod(ODCLFIELD, newname(msym), nil)
-	n.Type = t
-
-	for _, f := range pa.Methods().Slice() {
+	for _, f := range mt.Methods().Slice() {
 		if msym.Name != f.Sym.Name {
 			continue
 		}
-		// Eqtype only checks that incoming and result parameters match,
+		// 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) {
-			Yyerror("method redeclared: %v.%v\n\t%v\n\t%v", pa, msym, f.Type, t)
+		if !eqtype(t, f.Type) || !eqtype(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 := structfield(n)
+	f := newField()
+	f.Sym = msym
+	f.Nname = newname(msym)
+	f.Type = t
 	f.Nointerface = nointerface
 
-	// during import unexported method names should be in the type's package
-	if tpkg != nil && f.Sym != nil && !exportname(f.Sym.Name) && f.Sym.Pkg != tpkg {
-		Fatalf("imported method name %v in wrong package %s\n", sconv(f.Sym, FmtSign), tpkg.Name)
-	}
-
-	pa.Methods().Append(f)
+	mt.Methods().Append(f)
 }
 
 func funccompile(n *Node) {
@@ -1283,19 +1288,16 @@ func funccompile(n *Node) {
 
 	Stksize = 0
 	dclcontext = PAUTO
-	Funcdepth = n.Func.Depth + 1
+	funcdepth = n.Func.Depth + 1
 	compile(n)
 	Curfn = nil
-	Pc = nil
-	continpc = nil
-	breakpc = nil
-	Funcdepth = 0
+	pc = nil
+	funcdepth = 0
 	dclcontext = PEXTERN
 	if nerrors != 0 {
 		// If we have compile errors, ignore any assembler/linker errors.
 		Ctxt.DiagFunc = func(string, ...interface{}) {}
 	}
-	flushdata()
 	obj.Flushplist(Ctxt) // convert from Prog list to machine code
 }
 
@@ -1305,6 +1307,11 @@ func funcsym(s *Sym) *Sym {
 	}
 
 	s1 := Pkglookup(s.Name+"·f", s.Pkg)
+	if !Ctxt.Flag_dynlink && s1.Def == nil {
+		s1.Def = newfuncname(s1)
+		s1.Def.Func.Shortname = newname(s)
+		funcsyms = append(funcsyms, s1.Def)
+	}
 	s.Fsym = s1
 	return s1
 }
@@ -1347,7 +1354,7 @@ func checknowritebarrierrec() {
 	visitBottomUp(xtop, func(list []*Node, recursive bool) {
 		// Functions with write barriers have depth 0.
 		for _, n := range list {
-			if n.Func.WBLineno != 0 {
+			if n.Func.WBLineno != 0 && n.Func.Pragma&Yeswritebarrierrec == 0 {
 				c.best[n] = nowritebarrierrecCall{target: nil, depth: 0, lineno: n.Func.WBLineno}
 			}
 		}
@@ -1359,6 +1366,12 @@ func checknowritebarrierrec() {
 		for _ = range list {
 			c.stable = false
 			for _, n := range list {
+				if n.Func.Pragma&Yeswritebarrierrec != 0 {
+					// Don't propagate write
+					// barrier up to a
+					// yeswritebarrierrec function.
+					continue
+				}
 				if n.Func.WBLineno == 0 {
 					c.curfn = n
 					c.visitcodelist(n.Nbody)
@@ -1423,9 +1436,6 @@ func (c *nowritebarrierrecChecker) visitcall(n *Node) {
 	if fn == nil || fn.Op != ONAME || fn.Class != PFUNC || fn.Name.Defn == nil {
 		return
 	}
-	if (compiling_runtime || fn.Sym.Pkg == Runtimepkg) && fn.Sym.Name == "allocm" {
-		return
-	}
 	defn := fn.Name.Defn
 
 	fnbest, ok := c.best[defn]
diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
index 69b5964..4f37ff0 100644
--- a/src/cmd/compile/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -14,11 +14,11 @@ import (
 // or single non-recursive functions, bottom up.
 //
 // Finding these sets is finding strongly connected components
-// in the static call graph. The algorithm for doing that is taken
-// from Sedgewick, Algorithms, Second Edition, p. 482, with two
-// adaptations.
+// by reverse topological order in the static call graph.
+// The algorithm (known as Tarjan's algorithm) for doing that is taken from
+// Sedgewick, Algorithms, Second Edition, p. 482, with two adaptations.
 //
-// First, a hidden closure function (n->curfn != N) cannot be the
+// First, a hidden closure function (n.Func.IsHiddenClosure) cannot be the
 // root of a connected component. Refusing to use it as a root
 // forces it into the component of the function in which it appears.
 // This is more convenient for escape analysis.
@@ -58,7 +58,7 @@ func visitBottomUp(list []*Node, analyze func(list []*Node, recursive bool)) {
 	v.analyze = analyze
 	v.nodeID = make(map[*Node]uint32)
 	for _, n := range list {
-		if n.Op == ODCLFUNC && n.Func.FCurfn == nil {
+		if n.Op == ODCLFUNC && !n.Func.IsHiddenClosure {
 			v.visit(n)
 		}
 	}
@@ -78,11 +78,11 @@ func (v *bottomUpVisitor) visit(n *Node) uint32 {
 
 	v.stack = append(v.stack, n)
 	min = v.visitcodelist(n.Nbody, min)
-	if (min == id || min == id+1) && n.Func.FCurfn == nil {
+	if (min == id || min == id+1) && !n.Func.IsHiddenClosure {
 		// This node is the root of a strongly connected component.
 
-		// The original min passed to visitcodelist was n->walkgen+1.
-		// If visitcodelist found its way back to n->walkgen, then this
+		// The original min passed to visitcodelist was v.nodeID[n]+1.
+		// If visitcodelist found its way back to v.nodeID[n], then this
 		// block is a set of mutually recursive functions.
 		// Otherwise it's just a lone function that does not recurse.
 		recursive := min == id
@@ -160,7 +160,7 @@ func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 {
 //
 // First escfunc, esc and escassign recurse over the ast of each
 // function to dig out flow(dst,src) edges between any
-// pointer-containing nodes and store them in dst->escflowsrc. For
+// pointer-containing nodes and store them in e.nodeEscState(dst).Flowsrc. For
 // variables assigned to a variable in an outer scope or used as a
 // return value, they store a flow(theSink, src) edge to a fake node
 // 'the Sink'.  For variables referenced in closures, an edge
@@ -302,6 +302,7 @@ func (l Level) guaranteedDereference() int {
 // heap allocation.
 type EscStep struct {
 	src, dst *Node    // the endpoints of this edge in the escape-to-heap chain.
+	where    *Node    // sometimes the endpoints don't match source locations; set 'where' to make that right
 	parent   *EscStep // used in flood to record path
 	why      string   // explanation for this step in the escape-to-heap chain
 	busy     bool     // used in prevent to snip cycles.
@@ -309,10 +310,10 @@ type EscStep struct {
 
 type NodeEscState struct {
 	Curfn             *Node
-	Escflowsrc        []EscStep // flow(this, src)
-	Escretval         Nodes     // on OCALLxxx, list of dummy return values
-	Escloopdepth      int32     // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
-	Esclevel          Level
+	Flowsrc           []EscStep // flow(this, src)
+	Retval            Nodes     // on OCALLxxx, list of dummy return values
+	Loopdepth         int32     // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
+	Level             Level
 	Walkgen           uint32
 	Maxextraloopdepth int32
 }
@@ -324,8 +325,9 @@ func (e *EscState) nodeEscState(n *Node) *NodeEscState {
 	if n.Opt() != nil {
 		Fatalf("nodeEscState: opt in use (%T)", n.Opt())
 	}
-	nE := new(NodeEscState)
-	nE.Curfn = Curfn
+	nE := &NodeEscState{
+		Curfn: Curfn,
+	}
 	n.SetOpt(nE)
 	e.opts = append(e.opts, n)
 	return nE
@@ -337,7 +339,7 @@ func (e *EscState) track(n *Node) {
 	}
 	n.Esc = EscNone // until proven otherwise
 	nE := e.nodeEscState(n)
-	nE.Escloopdepth = e.loopdepth
+	nE.Loopdepth = e.loopdepth
 	e.noesc = append(e.noesc, n)
 }
 
@@ -346,12 +348,9 @@ func (e *EscState) track(n *Node) {
 // EscNever which is sticky, eX < eY means that eY is more exposed
 // than eX, and hence replaces it in a conservative analysis.
 const (
-	EscUnknown = iota
-	EscNone    // Does not escape to heap, result, or parameters.
-	EscReturn  // Is returned or reachable from returned.
-	EscScope   // Allocated in an inner loop scope, assigned to an outer loop scope,
-	// which allows the construction of non-escaping but arbitrarily large linked
-	// data structures (i.e., not eligible for allocation in a fixed-size stack frame).
+	EscUnknown        = iota
+	EscNone           // Does not escape to heap, result, or parameters.
+	EscReturn         // Is returned or reachable from returned.
 	EscHeap           // Reachable from the heap
 	EscNever          // By construction will not escape.
 	EscBits           = 3
@@ -364,10 +363,10 @@ const (
 // escMax returns the maximum of an existing escape value
 // (and its additional parameter flow flags) and a new escape type.
 func escMax(e, etype uint16) uint16 {
-	if e&EscMask >= EscScope {
+	if e&EscMask >= EscHeap {
 		// normalize
 		if e&^EscMask != 0 {
-			Fatalf("Escape information had unexpected return encoding bits (w/ EscScope, EscHeap, EscNever), e&EscMask=%v", e&EscMask)
+			Fatalf("Escape information had unexpected return encoding bits (w/ EscHeap, EscNever), e&EscMask=%v", e&EscMask)
 		}
 	}
 	if e&EscMask > etype {
@@ -409,6 +408,17 @@ type EscState struct {
 	walkgen   uint32
 }
 
+func newEscState(recursive bool) *EscState {
+	e := new(EscState)
+	e.theSink.Op = ONAME
+	e.theSink.Orig = &e.theSink
+	e.theSink.Class = PEXTERN
+	e.theSink.Sym = lookup(".sink")
+	e.nodeEscState(&e.theSink).Loopdepth = -1
+	e.recursive = recursive
+	return e
+}
+
 func (e *EscState) stepWalk(dst, src *Node, why string, parent *EscStep) *EscStep {
 	// TODO: keep a cache of these, mark entry/exit in escwalk to avoid allocation
 	// Or perhaps never mind, since it is disabled unless printing is on.
@@ -426,11 +436,27 @@ func (e *EscState) stepAssign(step *EscStep, dst, src *Node, why string) *EscSte
 		return nil
 	}
 	if step != nil { // Caller may have known better.
+		if step.why == "" {
+			step.why = why
+		}
+		if step.dst == nil {
+			step.dst = dst
+		}
+		if step.src == nil {
+			step.src = src
+		}
 		return step
 	}
 	return &EscStep{src: src, dst: dst, why: why}
 }
 
+func (e *EscState) stepAssignWhere(dst, src *Node, why string, where *Node) *EscStep {
+	if Debug['m'] == 0 {
+		return nil
+	}
+	return &EscStep{src: src, dst: dst, why: why, where: where}
+}
+
 // funcSym returns fn.Func.Nname.Sym if no nils are encountered along the way.
 func funcSym(fn *Node) *Sym {
 	if fn == nil || fn.Func.Nname == nil {
@@ -446,14 +472,7 @@ func (e *EscState) curfnSym(n *Node) *Sym {
 }
 
 func escAnalyze(all []*Node, recursive bool) {
-	var es EscState
-	e := &es
-	e.theSink.Op = ONAME
-	e.theSink.Orig = &e.theSink
-	e.theSink.Class = PEXTERN
-	e.theSink.Sym = Lookup(".sink")
-	e.nodeEscState(&e.theSink).Escloopdepth = -1
-	e.recursive = recursive
+	e := newEscState(recursive)
 
 	for _, n := range all {
 		if n.Op == ODCLFUNC {
@@ -464,11 +483,11 @@ func escAnalyze(all []*Node, recursive bool) {
 	// flow-analyze functions
 	for _, n := range all {
 		if n.Op == ODCLFUNC {
-			escfunc(e, n)
+			e.escfunc(n)
 		}
 	}
 
-	// print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount);
+	// print("escapes: %d e.dsts, %d edges\n", e.dstcount, e.edgecount);
 
 	// visit the upstream of each dst, mark address nodes with
 	// addrescapes, mark parameters unsafe
@@ -477,7 +496,7 @@ func escAnalyze(all []*Node, recursive bool) {
 		escapes[i] = n.Esc
 	}
 	for _, n := range e.dsts {
-		escflood(e, n)
+		e.escflood(n)
 	}
 	for {
 		done := true
@@ -488,7 +507,7 @@ func escAnalyze(all []*Node, recursive bool) {
 					Warnl(n.Lineno, "Reflooding %v %S", e.curfnSym(n), n)
 				}
 				escapes[i] = n.Esc
-				escflood(e, n)
+				e.escflood(n)
 			}
 		}
 		if done {
@@ -499,46 +518,47 @@ func escAnalyze(all []*Node, recursive bool) {
 	// for all top level functions, tag the typenodes corresponding to the param nodes
 	for _, n := range all {
 		if n.Op == ODCLFUNC {
-			esctag(e, n)
+			e.esctag(n)
 		}
 	}
 
 	if Debug['m'] != 0 {
 		for _, n := range e.noesc {
 			if n.Esc == EscNone {
-				Warnl(n.Lineno, "%v %v does not escape", e.curfnSym(n), Nconv(n, FmtShort))
+				Warnl(n.Lineno, "%v %S does not escape", e.curfnSym(n), n)
 			}
 		}
 	}
+
 	for _, x := range e.opts {
 		x.SetOpt(nil)
 	}
 }
 
-func escfunc(e *EscState, func_ *Node) {
-	//	print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
-	if func_.Esc != 1 {
-		Fatalf("repeat escfunc %v", func_.Func.Nname)
+func (e *EscState) escfunc(fn *Node) {
+	//	print("escfunc %N %s\n", fn.Func.Nname, e.recursive?"(recursive)":"");
+	if fn.Esc != EscFuncPlanned {
+		Fatalf("repeat escfunc %v", fn.Func.Nname)
 	}
-	func_.Esc = EscFuncStarted
+	fn.Esc = EscFuncStarted
 
 	saveld := e.loopdepth
 	e.loopdepth = 1
 	savefn := Curfn
-	Curfn = func_
+	Curfn = fn
 
 	for _, ln := range Curfn.Func.Dcl {
 		if ln.Op != ONAME {
 			continue
 		}
-		llNE := e.nodeEscState(ln)
+		lnE := e.nodeEscState(ln)
 		switch ln.Class {
 		// out params are in a loopdepth between the sink and all local variables
 		case PPARAMOUT:
-			llNE.Escloopdepth = 0
+			lnE.Loopdepth = 0
 
 		case PPARAM:
-			llNE.Escloopdepth = 1
+			lnE.Loopdepth = 1
 			if ln.Type != nil && !haspointers(ln.Type) {
 				break
 			}
@@ -555,52 +575,53 @@ func escfunc(e *EscState, func_ *Node) {
 	if e.recursive {
 		for _, ln := range Curfn.Func.Dcl {
 			if ln.Op == ONAME && ln.Class == PPARAMOUT {
-				escflows(e, &e.theSink, ln, e.stepAssign(nil, ln, ln, "returned from recursive function"))
+				e.escflows(&e.theSink, ln, e.stepAssign(nil, ln, ln, "returned from recursive function"))
 			}
 		}
 	}
 
-	escloopdepthlist(e, Curfn.Nbody)
-	esclist(e, Curfn.Nbody, Curfn)
+	e.escloopdepthlist(Curfn.Nbody)
+	e.esclist(Curfn.Nbody, Curfn)
 	Curfn = savefn
 	e.loopdepth = saveld
 }
 
-// Mark labels that have no backjumps to them as not increasing e->loopdepth.
-// Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat
+// Mark labels that have no backjumps to them as not increasing e.loopdepth.
+// Walk hasn't generated (goto|label).Left.Sym.Label yet, so we'll cheat
 // and set it to one of the following two. Then in esc we'll clear it again.
-var looping Label
-
-var nonlooping Label
+var (
+	looping    Node
+	nonlooping Node
+)
 
-func escloopdepthlist(e *EscState, l Nodes) {
+func (e *EscState) escloopdepthlist(l Nodes) {
 	for _, n := range l.Slice() {
-		escloopdepth(e, n)
+		e.escloopdepth(n)
 	}
 }
 
-func escloopdepth(e *EscState, n *Node) {
+func (e *EscState) escloopdepth(n *Node) {
 	if n == nil {
 		return
 	}
 
-	escloopdepthlist(e, n.Ninit)
+	e.escloopdepthlist(n.Ninit)
 
 	switch n.Op {
 	case OLABEL:
 		if n.Left == nil || n.Left.Sym == nil {
-			Fatalf("esc:label without label: %v", Nconv(n, FmtSign))
+			Fatalf("esc:label without label: %+v", n)
 		}
 
 		// Walk will complain about this label being already defined, but that's not until
 		// after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc
-		// if(n->left->sym->label != nil)
+		// if(n.Left.Sym.Label != nil)
 		//	fatal("escape analysis messed up analyzing label: %+N", n);
 		n.Left.Sym.Label = &nonlooping
 
 	case OGOTO:
 		if n.Left == nil || n.Left.Sym == nil {
-			Fatalf("esc:goto without label: %v", Nconv(n, FmtSign))
+			Fatalf("esc:goto without label: %+v", n)
 		}
 
 		// If we come past one that's uninitialized, this must be a (harmless) forward jump
@@ -610,34 +631,28 @@ func escloopdepth(e *EscState, n *Node) {
 		}
 	}
 
-	escloopdepth(e, n.Left)
-	escloopdepth(e, n.Right)
-	escloopdepthlist(e, n.List)
-	escloopdepthlist(e, n.Nbody)
-	escloopdepthlist(e, n.Rlist)
+	e.escloopdepth(n.Left)
+	e.escloopdepth(n.Right)
+	e.escloopdepthlist(n.List)
+	e.escloopdepthlist(n.Nbody)
+	e.escloopdepthlist(n.Rlist)
 }
 
-func esclist(e *EscState, l Nodes, up *Node) {
+func (e *EscState) esclist(l Nodes, parent *Node) {
 	for _, n := range l.Slice() {
-		esc(e, n, up)
+		e.esc(n, parent)
 	}
 }
 
-func esc(e *EscState, n *Node, up *Node) {
+func (e *EscState) esc(n *Node, parent *Node) {
 	if n == nil {
 		return
 	}
-	if n.Type == structkey {
-		// This is the left side of x:y in a struct literal.
-		// x is syntax, not an expression.
-		// See #14405.
-		return
-	}
 
 	lno := setlineno(n)
 
 	// ninit logically runs at a different loopdepth than the rest of the for loop.
-	esclist(e, n.Ninit, n)
+	e.esclist(n.Ninit, n)
 
 	if n.Op == OFOR || n.Op == ORANGE {
 		e.loopdepth++
@@ -651,7 +666,7 @@ func esc(e *EscState, n *Node, up *Node) {
 		for _, n1 := range n.List.Slice() { // cases
 			// it.N().Rlist is the variable per case
 			if n1.Rlist.Len() != 0 {
-				e.nodeEscState(n1.Rlist.First()).Escloopdepth = e.loopdepth
+				e.nodeEscState(n1.Rlist.First()).Loopdepth = e.loopdepth
 			}
 		}
 	}
@@ -667,14 +682,14 @@ func esc(e *EscState, n *Node, up *Node) {
 		}
 		n.Esc = EscHeap
 		addrescapes(n)
-		escassignSinkNilWhy(e, n, n, "too large for stack") // TODO category: tooLarge
+		e.escassignSinkWhy(n, n, "too large for stack") // TODO category: tooLarge
 	}
 
-	esc(e, n.Left, n)
-	esc(e, n.Right, n)
-	esclist(e, n.Nbody, n)
-	esclist(e, n.List, n)
-	esclist(e, n.Rlist, n)
+	e.esc(n.Left, n)
+	e.esc(n.Right, n)
+	e.esclist(n.Nbody, n)
+	e.esclist(n.List, n)
+	e.esclist(n.Rlist, n)
 
 	if n.Op == OFOR || n.Op == ORANGE {
 		e.loopdepth--
@@ -688,7 +703,7 @@ func esc(e *EscState, n *Node, up *Node) {
 	// Record loop depth at declaration.
 	case ODCL:
 		if n.Left != nil {
-			e.nodeEscState(n.Left).Escloopdepth = e.loopdepth
+			e.nodeEscState(n.Left).Loopdepth = e.loopdepth
 		}
 
 	case OLABEL:
@@ -704,7 +719,7 @@ func esc(e *EscState, n *Node, up *Node) {
 		}
 
 		// See case OLABEL in escloopdepth above
-		// else if(n->left->sym->label == nil)
+		// else if(n.Left.Sym.Label == nil)
 		//	fatal("escape analysis missed or messed up a label: %+N", n);
 
 		n.Left.Sym.Label = nil
@@ -717,10 +732,10 @@ func esc(e *EscState, n *Node, up *Node) {
 			// 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)) {
-				escassignNilWhy(e, n.List.Second(), n.Right, "range")
+				!(n.Right.Type.IsPtr() && eqtype(n.Right.Type.Elem(), n.Type)) {
+				e.escassignWhyWhere(n.List.Second(), n.Right, "range", n)
 			} else {
-				escassignDereference(e, n.List.Second(), n.Right, e.stepAssign(nil, n.List.Second(), n.Right, "range-deref"))
+				e.escassignDereference(n.List.Second(), n.Right, e.stepAssignWhere(n.List.Second(), n.Right, "range-deref", n))
 			}
 		}
 
@@ -731,12 +746,12 @@ func esc(e *EscState, n *Node, up *Node) {
 				// n.Left.Right is the argument of the .(type),
 				// it.N().Rlist is the variable per case
 				if n2.Rlist.Len() != 0 {
-					escassignNilWhy(e, n2.Rlist.First(), n.Left.Right, "switch case")
+					e.escassignWhyWhere(n2.Rlist.First(), n.Left.Right, "switch case", n)
 				}
 			}
 		}
 
-		// Filter out the following special case.
+	// Filter out the following special case.
 	//
 	//	func (b *Buffer) Foo() {
 	//		n, m := ...
@@ -763,31 +778,31 @@ func esc(e *EscState, n *Node, up *Node) {
 			// b escapes as well. If we ignore such OSLICEARR, we will conclude
 			// that b does not escape when b contents do.
 			if Debug['m'] != 0 {
-				Warnl(n.Lineno, "%v ignoring self-assignment to %v", e.curfnSym(n), Nconv(n.Left, FmtShort))
+				Warnl(n.Lineno, "%v ignoring self-assignment to %S", e.curfnSym(n), n.Left)
 			}
 
 			break
 		}
 
-		escassign(e, n.Left, n.Right, nil)
+		e.escassign(n.Left, n.Right, e.stepAssignWhere(nil, nil, "", n))
 
 	case OAS2: // x,y = a,b
 		if n.List.Len() == n.Rlist.Len() {
 			rs := n.Rlist.Slice()
 			for i, n := range n.List.Slice() {
-				escassignNilWhy(e, n, rs[i], "assign-pair")
+				e.escassignWhyWhere(n, rs[i], "assign-pair", n)
 			}
 		}
 
 	case OAS2RECV: // v, ok = <-ch
-		escassignNilWhy(e, n.List.First(), n.Rlist.First(), "assign-pair-receive")
+		e.escassignWhyWhere(n.List.First(), n.Rlist.First(), "assign-pair-receive", n)
 	case OAS2MAPR: // v, ok = m[k]
-		escassignNilWhy(e, n.List.First(), n.Rlist.First(), "assign-pair-mapr")
+		e.escassignWhyWhere(n.List.First(), n.Rlist.First(), "assign-pair-mapr", n)
 	case OAS2DOTTYPE: // v, ok = x.(type)
-		escassignNilWhy(e, n.List.First(), n.Rlist.First(), "assign-pair-dot-type")
+		e.escassignWhyWhere(n.List.First(), n.Rlist.First(), "assign-pair-dot-type", n)
 
 	case OSEND: // ch <- x
-		escassignSinkNilWhy(e, n, n.Right, "send")
+		e.escassignSinkWhy(n, n.Right, "send")
 
 	case ODEFER:
 		if e.loopdepth == 1 { // top level
@@ -796,128 +811,136 @@ func esc(e *EscState, n *Node, up *Node) {
 		// arguments leak out of scope
 		// TODO: leak to a dummy node instead
 		// defer f(x) - f and x escape
-		escassignSinkNilWhy(e, n, n.Left.Left, "defer func")
+		e.escassignSinkWhy(n, n.Left.Left, "defer func")
 
-		escassignSinkNilWhy(e, n, n.Left.Right, "defer func ...") // ODDDARG for call
+		e.escassignSinkWhy(n, n.Left.Right, "defer func ...") // ODDDARG for call
 		for _, n4 := range n.Left.List.Slice() {
-			escassignSinkNilWhy(e, n, n4, "defer func arg")
+			e.escassignSinkWhy(n, n4, "defer func arg")
 		}
 
 	case OPROC:
 		// go f(x) - f and x escape
-		escassignSinkNilWhy(e, n, n.Left.Left, "go func")
+		e.escassignSinkWhy(n, n.Left.Left, "go func")
 
-		escassignSinkNilWhy(e, n, n.Left.Right, "go func ...") // ODDDARG for call
+		e.escassignSinkWhy(n, n.Left.Right, "go func ...") // ODDDARG for call
 		for _, n4 := range n.Left.List.Slice() {
-			escassignSinkNilWhy(e, n, n4, "go func arg")
+			e.escassignSinkWhy(n, n4, "go func arg")
 		}
 
 	case OCALLMETH, OCALLFUNC, OCALLINTER:
-		esccall(e, n, up)
+		e.esccall(n, parent)
 
-		// esccall already done on n->rlist->n. tie it's escretval to n->list
+		// esccall already done on n.Rlist.First(). tie it's Retval to n.List
 	case OAS2FUNC: // x,y = f()
-		rs := e.nodeEscState(n.Rlist.First()).Escretval.Slice()
+		rs := e.nodeEscState(n.Rlist.First()).Retval.Slice()
 		for i, n := range n.List.Slice() {
 			if i >= len(rs) {
 				break
 			}
-			escassignNilWhy(e, n, rs[i], "assign-pair-func-call")
+			e.escassignWhyWhere(n, rs[i], "assign-pair-func-call", n)
 		}
 		if n.List.Len() != len(rs) {
 			Fatalf("esc oas2func")
 		}
 
 	case ORETURN:
-		ll := n.List
-		if n.List.Len() == 1 && Curfn.Type.Results().NumFields() > 1 {
+		retList := n.List
+		if retList.Len() == 1 && Curfn.Type.Results().NumFields() > 1 {
 			// OAS2FUNC in disguise
-			// esccall already done on n->list->n
-			// tie n->list->n->escretval to curfn->dcl PPARAMOUT's
-			ll = e.nodeEscState(n.List.First()).Escretval
+			// esccall already done on n.List.First()
+			// tie e.nodeEscState(n.List.First()).Retval to Curfn.Func.Dcl PPARAMOUT's
+			retList = e.nodeEscState(n.List.First()).Retval
 		}
 
 		i := 0
 		for _, lrn := range Curfn.Func.Dcl {
-			if i >= ll.Len() {
+			if i >= retList.Len() {
 				break
 			}
 			if lrn.Op != ONAME || lrn.Class != PPARAMOUT {
 				continue
 			}
-			escassignNilWhy(e, lrn, ll.Index(i), "return")
+			e.escassignWhyWhere(lrn, retList.Index(i), "return", n)
 			i++
 		}
 
-		if i < ll.Len() {
+		if i < retList.Len() {
 			Fatalf("esc return list")
 		}
 
 		// Argument could leak through recover.
 	case OPANIC:
-		escassignSinkNilWhy(e, n, n.Left, "panic")
+		e.escassignSinkWhy(n, n.Left, "panic")
 
 	case OAPPEND:
 		if !n.Isddd {
 			for _, nn := range n.List.Slice()[1:] {
-				escassignSinkNilWhy(e, n, nn, "appended to slice") // lose track of assign to dereference
+				e.escassignSinkWhy(n, nn, "appended to slice") // lose track of assign to dereference
 			}
 		} else {
 			// append(slice1, slice2...) -- slice2 itself does not escape, but contents do.
 			slice2 := n.List.Second()
-			escassignDereference(e, &e.theSink, slice2, e.stepAssign(nil, n, slice2, "appended slice...")) // lose track of assign of dereference
+			e.escassignDereference(&e.theSink, slice2, e.stepAssignWhere(n, slice2, "appended slice...", n)) // lose track of assign of dereference
 			if Debug['m'] > 3 {
-				Warnl(n.Lineno, "%v special treatment of append(slice1, slice2...) %v", e.curfnSym(n), Nconv(n, FmtShort))
+				Warnl(n.Lineno, "%v special treatment of append(slice1, slice2...) %S", e.curfnSym(n), n)
 			}
 		}
-		escassignDereference(e, &e.theSink, n.List.First(), e.stepAssign(nil, n, n.List.First(), "appendee slice")) // The original elements are now leaked, too
+		e.escassignDereference(&e.theSink, n.List.First(), e.stepAssignWhere(n, n.List.First(), "appendee slice", n)) // The original elements are now leaked, too
 
 	case OCOPY:
-		escassignDereference(e, &e.theSink, n.Right, e.stepAssign(nil, n, n.Right, "copied slice")) // lose track of assign of dereference
+		e.escassignDereference(&e.theSink, n.Right, e.stepAssignWhere(n, n.Right, "copied slice", n)) // lose track of assign of dereference
 
 	case OCONV, OCONVNOP:
-		escassignNilWhy(e, n, n.Left, "converted")
+		e.escassignWhyWhere(n, n.Left, "converted", n)
 
 	case OCONVIFACE:
 		e.track(n)
-		escassignNilWhy(e, n, n.Left, "interface-converted")
+		e.escassignWhyWhere(n, n.Left, "interface-converted", n)
 
 	case OARRAYLIT:
-		why := "array literal element"
-		if n.Type.IsSlice() {
-			// Slice itself is not leaked until proven otherwise
-			e.track(n)
-			why = "slice literal element"
+		// Link values to array
+		for _, n2 := range n.List.Slice() {
+			if n2.Op == OKEY {
+				n2 = n2.Right
+			}
+			e.escassign(n, n2, e.stepAssignWhere(n, n2, "array literal element", n))
 		}
-		// Link values to array/slice
-		for _, n5 := range n.List.Slice() {
-			escassign(e, n, n5.Right, e.stepAssign(nil, n, n5.Right, why))
+
+	case OSLICELIT:
+		// Slice is not leaked until proven otherwise
+		e.track(n)
+		// Link values to slice
+		for _, n2 := range n.List.Slice() {
+			if n2.Op == OKEY {
+				n2 = n2.Right
+			}
+			e.escassign(n, n2, e.stepAssignWhere(n, n2, "slice literal element", n))
 		}
 
 		// Link values to struct.
 	case OSTRUCTLIT:
 		for _, n6 := range n.List.Slice() {
-			escassignNilWhy(e, n, n6.Right, "struct literal element")
+			e.escassignWhyWhere(n, n6.Left, "struct literal element", n)
 		}
 
 	case OPTRLIT:
 		e.track(n)
 
 		// Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too.
-		escassignNilWhy(e, n, n.Left, "pointer literal [assign]")
+		e.escassignWhyWhere(n, n.Left, "pointer literal [assign]", n)
 
 	case OCALLPART:
 		e.track(n)
 
 		// Contents make it to memory, lose track.
-		escassignSinkNilWhy(e, n, n.Left, "call part")
+		e.escassignSinkWhy(n, n.Left, "call part")
 
 	case OMAPLIT:
 		e.track(n)
 		// Keys and values make it to memory, lose track.
 		for _, n7 := range n.List.Slice() {
-			escassignSinkNilWhy(e, n, n7.Left, "map literal key")
-			escassignSinkNilWhy(e, n, n7.Right, "map literal value")
+			e.escassignSinkWhy(n, n7.Left, "map literal key")
+			e.escassignSinkWhy(n, n7.Right, "map literal value")
 		}
 
 	case OCLOSURE:
@@ -928,13 +951,13 @@ func esc(e *EscState, n *Node, up *Node) {
 			}
 			a := v.Name.Defn
 			if !v.Name.Byval {
-				a = Nod(OADDR, a, nil)
+				a = nod(OADDR, a, nil)
 				a.Lineno = v.Lineno
-				e.nodeEscState(a).Escloopdepth = e.loopdepth
+				e.nodeEscState(a).Loopdepth = e.loopdepth
 				a = typecheck(a, Erv)
 			}
 
-			escassignNilWhy(e, n, a, "captured by a closure")
+			e.escassignWhyWhere(n, a, "captured by a closure", n)
 		}
 		fallthrough
 
@@ -966,11 +989,11 @@ func esc(e *EscState, n *Node, up *Node) {
 			case PAUTO:
 				nE := e.nodeEscState(n)
 				leftE := e.nodeEscState(n.Left)
-				if leftE.Escloopdepth != 0 {
-					nE.Escloopdepth = leftE.Escloopdepth
+				if leftE.Loopdepth != 0 {
+					nE.Loopdepth = leftE.Loopdepth
 				}
 
-				// PPARAM is loop depth 1 always.
+			// PPARAM is loop depth 1 always.
 			// PPARAMOUT is loop depth 0 for writes
 			// but considered loop depth 1 for address-of,
 			// so that writing the address of one result
@@ -978,7 +1001,7 @@ func esc(e *EscState, n *Node, up *Node) {
 			// first result move to the heap.
 			case PPARAM, PPARAMOUT:
 				nE := e.nodeEscState(n)
-				nE.Escloopdepth = 1
+				nE.Loopdepth = 1
 			}
 		}
 	}
@@ -986,40 +1009,50 @@ func esc(e *EscState, n *Node, up *Node) {
 	lineno = lno
 }
 
-// escassignNilWhy bundles a common case of
-// escassign(e, dst, src, e.stepAssign(nil, dst, src, reason))
-func escassignNilWhy(e *EscState, dst, src *Node, reason string) {
+// escassignWhyWhere bundles a common case of
+// escassign(e, dst, src, e.stepAssignWhere(dst, src, reason, where))
+func (e *EscState) escassignWhyWhere(dst, src *Node, reason string, where *Node) {
 	var step *EscStep
 	if Debug['m'] != 0 {
-		step = e.stepAssign(nil, dst, src, reason)
+		step = e.stepAssignWhere(dst, src, reason, where)
 	}
-	escassign(e, dst, src, step)
+	e.escassign(dst, src, step)
 }
 
-// escassignSinkNilWhy bundles a common case of
+// escassignSinkWhy bundles a common case of
 // escassign(e, &e.theSink, src, e.stepAssign(nil, dst, src, reason))
-func escassignSinkNilWhy(e *EscState, dst, src *Node, reason string) {
+func (e *EscState) escassignSinkWhy(dst, src *Node, reason string) {
 	var step *EscStep
 	if Debug['m'] != 0 {
 		step = e.stepAssign(nil, dst, src, reason)
 	}
-	escassign(e, &e.theSink, src, step)
+	e.escassign(&e.theSink, src, step)
+}
+
+// escassignSinkWhyWhere is escassignSinkWhy but includes a call site
+// for accurate location reporting.
+func (e *EscState) escassignSinkWhyWhere(dst, src *Node, reason string, call *Node) {
+	var step *EscStep
+	if Debug['m'] != 0 {
+		step = e.stepAssignWhere(dst, src, reason, call)
+	}
+	e.escassign(&e.theSink, src, step)
 }
 
 // Assert that expr somehow gets assigned to dst, if non nil.  for
 // dst==nil, any name node expr still must be marked as being
 // evaluated in curfn.	For expr==nil, dst must still be examined for
 // evaluations inside it (e.g *f(x) = y)
-func escassign(e *EscState, dst, src *Node, step *EscStep) {
+func (e *EscState) escassign(dst, src *Node, step *EscStep) {
 	if isblank(dst) || dst == nil || src == nil || src.Op == ONONAME || src.Op == OXXX {
 		return
 	}
 
 	if Debug['m'] > 2 {
-		fmt.Printf("%v:[%d] %v escassign: %v(%v)[%v] = %v(%v)[%v]\n",
+		fmt.Printf("%v:[%d] %v escassign: %S(%0j)[%v] = %S(%0j)[%v]\n",
 			linestr(lineno), e.loopdepth, funcSym(Curfn),
-			Nconv(dst, FmtShort), jconv(dst, FmtShort), dst.Op,
-			Nconv(src, FmtShort), jconv(src, FmtShort), src.Op)
+			dst, dst, dst.Op,
+			src, src, src.Op)
 	}
 
 	setlineno(dst)
@@ -1028,13 +1061,14 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
 	dstwhy := "assigned"
 
 	// Analyze lhs of assignment.
-	// Replace dst with e->theSink if we can't track it.
+	// Replace dst with &e.theSink if we can't track it.
 	switch dst.Op {
 	default:
 		Dump("dst", dst)
 		Fatalf("escassign: unexpected dst")
 
 	case OARRAYLIT,
+		OSLICELIT,
 		OCLOSURE,
 		OCONV,
 		OCONVIFACE,
@@ -1044,7 +1078,6 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
 		OPTRLIT,
 		ODDDARG,
 		OCALLPART:
-		break
 
 	case ONAME:
 		if dst.Class == PEXTERN {
@@ -1053,12 +1086,12 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
 		}
 
 	case ODOT: // treat "dst.x = src" as "dst = src"
-		escassign(e, dst.Left, src, e.stepAssign(step, originalDst, src, "dot-equals"))
+		e.escassign(dst.Left, src, e.stepAssign(step, originalDst, src, "dot-equals"))
 		return
 
 	case OINDEX:
 		if dst.Left.Type.IsArray() {
-			escassign(e, dst.Left, src, e.stepAssign(step, originalDst, src, "array-element-equals"))
+			e.escassign(dst.Left, src, e.stepAssign(step, originalDst, src, "array-element-equals"))
 			return
 		}
 
@@ -1075,7 +1108,7 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
 
 		// lose track of key and value
 	case OINDEXMAP:
-		escassign(e, &e.theSink, dst.Right, e.stepAssign(nil, originalDst, src, "key of map put"))
+		e.escassign(&e.theSink, dst.Right, e.stepAssign(nil, originalDst, src, "key of map put"))
 		dstwhy = "value of map put"
 		dst = &e.theSink
 	}
@@ -1091,6 +1124,7 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
 		ODDDARG,
 		OPTRLIT,
 		OARRAYLIT,
+		OSLICELIT,
 		OMAPLIT,
 		OSTRUCTLIT,
 		OMAKECHAN,
@@ -1105,22 +1139,22 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
 		OCALLPART,
 		ORUNESTR,
 		OCONVIFACE:
-		escflows(e, dst, src, e.stepAssign(step, originalDst, src, dstwhy))
+		e.escflows(dst, src, e.stepAssign(step, originalDst, src, dstwhy))
 
 	case OCLOSURE:
 		// OCLOSURE is lowered to OPTRLIT,
 		// insert OADDR to account for the additional indirection.
-		a := Nod(OADDR, src, nil)
+		a := nod(OADDR, src, nil)
 		a.Lineno = src.Lineno
-		e.nodeEscState(a).Escloopdepth = e.nodeEscState(src).Escloopdepth
-		a.Type = Ptrto(src.Type)
-		escflows(e, dst, a, e.stepAssign(nil, originalDst, src, dstwhy))
+		e.nodeEscState(a).Loopdepth = e.nodeEscState(src).Loopdepth
+		a.Type = ptrto(src.Type)
+		e.escflows(dst, a, e.stepAssign(nil, originalDst, src, dstwhy))
 
 	// Flowing multiple returns to a single dst happens when
 	// analyzing "go f(g())": here g() flows to sink (issue 4529).
 	case OCALLMETH, OCALLFUNC, OCALLINTER:
-		for _, n := range e.nodeEscState(src).Escretval.Slice() {
-			escflows(e, dst, n, e.stepAssign(nil, originalDst, n, dstwhy))
+		for _, n := range e.nodeEscState(src).Retval.Slice() {
+			e.escflows(dst, n, e.stepAssign(nil, originalDst, n, dstwhy))
 		}
 
 		// A non-pointer escaping from a struct does not concern us.
@@ -1136,35 +1170,35 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
 		ODOTMETH,
 		// treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC
 		// iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here
-		ODOTTYPE2,
 		OSLICE,
 		OSLICE3,
 		OSLICEARR,
 		OSLICE3ARR,
 		OSLICESTR:
 		// Conversions, field access, slice all preserve the input value.
-		escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy))
+		e.escassign(dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy))
 
-	case ODOTTYPE:
+	case ODOTTYPE,
+		ODOTTYPE2:
 		if src.Type != nil && !haspointers(src.Type) {
 			break
 		}
-		escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy))
+		e.escassign(dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy))
 
 	case OAPPEND:
 		// Append returns first argument.
 		// Subsequent arguments are already leaked because they are operands to append.
-		escassign(e, dst, src.List.First(), e.stepAssign(step, dst, src.List.First(), dstwhy))
+		e.escassign(dst, src.List.First(), e.stepAssign(step, dst, src.List.First(), dstwhy))
 
 	case OINDEX:
 		// Index of array preserves input value.
 		if src.Left.Type.IsArray() {
-			escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy))
+			e.escassign(dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy))
 		} else {
-			escflows(e, dst, src, e.stepAssign(step, originalDst, src, dstwhy))
+			e.escflows(dst, src, e.stepAssign(step, originalDst, src, dstwhy))
 		}
 
-		// Might be pointer arithmetic, in which case
+	// Might be pointer arithmetic, in which case
 	// the operands flow into the result.
 	// TODO(rsc): Decide what the story is here. This is unsettling.
 	case OADD,
@@ -1181,9 +1215,9 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
 		OPLUS,
 		OMINUS,
 		OCOM:
-		escassign(e, dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy))
+		e.escassign(dst, src.Left, e.stepAssign(step, originalDst, src, dstwhy))
 
-		escassign(e, dst, src.Right, e.stepAssign(step, originalDst, src, dstwhy))
+		e.escassign(dst, src.Right, e.stepAssign(step, originalDst, src, dstwhy))
 	}
 
 	e.pdepth--
@@ -1203,8 +1237,6 @@ var tags [1 << (bitsPerOutputInTag + EscReturnBits)]string
 func mktag(mask int) string {
 	switch mask & EscMask {
 	case EscNone, EscReturn:
-		break
-
 	default:
 		Fatalf("escape mktag")
 	}
@@ -1256,9 +1288,6 @@ func describeEscape(em uint16) string {
 	if em&EscMask == EscReturn {
 		s = "EscReturn"
 	}
-	if em&EscMask == EscScope {
-		s = "EscScope"
-	}
 	if em&EscContentEscapes != 0 {
 		if s != "" {
 			s += " "
@@ -1287,19 +1316,19 @@ func describeEscape(em uint16) string {
 
 // escassignfromtag models the input-to-output assignment flow of one of a function
 // calls arguments, where the flow is encoded in "note".
-func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 {
+func (e *EscState) escassignfromtag(note string, dsts Nodes, src, call *Node) uint16 {
 	em := parsetag(note)
 	if src.Op == OLITERAL {
 		return em
 	}
 
 	if Debug['m'] > 3 {
-		fmt.Printf("%v::assignfromtag:: src=%v, em=%s\n",
-			linestr(lineno), Nconv(src, FmtShort), describeEscape(em))
+		fmt.Printf("%v::assignfromtag:: src=%S, em=%s\n",
+			linestr(lineno), src, describeEscape(em))
 	}
 
 	if em == EscUnknown {
-		escassignSinkNilWhy(e, src, src, "passed to function[unknown]")
+		e.escassignSinkWhyWhere(src, src, "passed to call[argument escapes]", call)
 		return em
 	}
 
@@ -1310,7 +1339,7 @@ func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 {
 	// If content inside parameter (reached via indirection)
 	// escapes to heap, mark as such.
 	if em&EscContentEscapes != 0 {
-		escassign(e, &e.theSink, e.addDereference(src), e.stepAssign(nil, src, src, "passed to function[content escapes]"))
+		e.escassign(&e.theSink, e.addDereference(src), e.stepAssignWhere(src, src, "passed to call[argument content escapes]", call))
 	}
 
 	em0 := em
@@ -1327,7 +1356,7 @@ func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 {
 			for i := uint16(0); i < embits-1; i++ {
 				n = e.addDereference(n) // encode level>0 as indirections
 			}
-			escassign(e, dsts.Index(dstsi), n, e.stepAssign(nil, dsts.Index(dstsi), src, "passed-to-and-returned-from-function"))
+			e.escassign(dsts.Index(dstsi), n, e.stepAssignWhere(dsts.Index(dstsi), src, "passed-to-and-returned-from-call", call))
 		}
 		dstsi++
 	}
@@ -1341,19 +1370,19 @@ func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 {
 	return em0
 }
 
-func escassignDereference(e *EscState, dst *Node, src *Node, step *EscStep) {
+func (e *EscState) escassignDereference(dst *Node, src *Node, step *EscStep) {
 	if src.Op == OLITERAL {
 		return
 	}
-	escassign(e, dst, e.addDereference(src), step)
+	e.escassign(dst, e.addDereference(src), step)
 }
 
 // addDereference constructs a suitable OIND note applied to src.
 // Because this is for purposes of escape accounting, not execution,
 // some semantically dubious node combinations are (currently) possible.
 func (e *EscState) addDereference(n *Node) *Node {
-	ind := Nod(OIND, n, nil)
-	e.nodeEscState(ind).Escloopdepth = e.nodeEscState(n).Escloopdepth
+	ind := nod(OIND, n, nil)
+	e.nodeEscState(ind).Loopdepth = e.nodeEscState(n).Loopdepth
 	ind.Lineno = n.Lineno
 	t := n.Type
 	if t.IsKind(Tptr) {
@@ -1400,147 +1429,145 @@ func escNoteOutputParamFlow(e uint16, vargen int32, level Level) uint16 {
 	return (e &^ (bitsMaskForTag << shift)) | encodedFlow
 }
 
-func initEscretval(e *EscState, n *Node, fntype *Type) {
-	i := 0
-	nE := e.nodeEscState(n)
-	nE.Escretval.Set(nil) // Suspect this is not nil for indirect calls.
-	for _, t := range fntype.Results().Fields().Slice() {
-		src := Nod(ONAME, nil, nil)
+func (e *EscState) initEscRetval(call *Node, fntype *Type) {
+	cE := e.nodeEscState(call)
+	cE.Retval.Set(nil) // Suspect this is not nil for indirect calls.
+	for i, f := range fntype.Results().Fields().Slice() {
+		ret := nod(ONAME, nil, nil)
 		buf := fmt.Sprintf(".out%d", i)
-		i++
-		src.Sym = Lookup(buf)
-		src.Type = t.Type
-		src.Class = PAUTO
-		src.Name.Curfn = Curfn
-		e.nodeEscState(src).Escloopdepth = e.loopdepth
-		src.Used = true
-		src.Lineno = n.Lineno
-		nE.Escretval.Append(src)
+		ret.Sym = lookup(buf)
+		ret.Type = f.Type
+		ret.Class = PAUTO
+		ret.Name.Curfn = Curfn
+		e.nodeEscState(ret).Loopdepth = e.loopdepth
+		ret.Used = true
+		ret.Lineno = call.Lineno
+		cE.Retval.Append(ret)
 	}
 }
 
 // This is a bit messier than fortunate, pulled out of esc's big
-// switch for clarity.	We either have the paramnodes, which may be
+// switch for clarity. We either have the paramnodes, which may be
 // connected to other things through flows or we have the parameter type
 // nodes, which may be marked "noescape". Navigating the ast is slightly
 // different for methods vs plain functions and for imported vs
 // this-package
-func esccall(e *EscState, n *Node, up *Node) {
+func (e *EscState) esccall(call *Node, parent *Node) {
 	var fntype *Type
 	var indirect bool
 	var fn *Node
-	switch n.Op {
+	switch call.Op {
 	default:
 		Fatalf("esccall")
 
 	case OCALLFUNC:
-		fn = n.Left
+		fn = call.Left
 		fntype = fn.Type
 		indirect = fn.Op != ONAME || fn.Class != PFUNC
 
 	case OCALLMETH:
-		fn = n.Left.Sym.Def
+		fn = call.Left.Sym.Def
 		if fn != nil {
 			fntype = fn.Type
 		} else {
-			fntype = n.Left.Type
+			fntype = call.Left.Type
 		}
 
 	case OCALLINTER:
-		fntype = n.Left.Type
+		fntype = call.Left.Type
 		indirect = true
 	}
 
-	ll := n.List
-	if n.List.Len() == 1 {
-		a := n.List.First()
-		if a.Type.IsFuncArgStruct() { // f(g())
-			ll = e.nodeEscState(a).Escretval
+	argList := call.List
+	if argList.Len() == 1 {
+		arg := argList.First()
+		if arg.Type.IsFuncArgStruct() { // f(g())
+			argList = e.nodeEscState(arg).Retval
 		}
 	}
 
+	args := argList.Slice()
+
 	if indirect {
 		// We know nothing!
 		// Leak all the parameters
-		for _, n1 := range ll.Slice() {
-			escassignSinkNilWhy(e, n, n1, "parameter to indirect call")
+		for _, arg := range args {
+			e.escassignSinkWhy(call, arg, "parameter to indirect call")
 			if Debug['m'] > 3 {
-				fmt.Printf("%v::esccall:: indirect call <- %v, untracked\n", linestr(lineno), Nconv(n1, FmtShort))
+				fmt.Printf("%v::esccall:: indirect call <- %S, untracked\n", linestr(lineno), arg)
 			}
 		}
 		// Set up bogus outputs
-		initEscretval(e, n, fntype)
+		e.initEscRetval(call, fntype)
 		// If there is a receiver, it also leaks to heap.
-		if n.Op != OCALLFUNC {
-			t := fntype.Recv()
-			src := n.Left.Left
-			if haspointers(t.Type) {
-				escassignSinkNilWhy(e, n, src, "receiver in indirect call")
+		if call.Op != OCALLFUNC {
+			rf := fntype.Recv()
+			r := call.Left.Left
+			if haspointers(rf.Type) {
+				e.escassignSinkWhy(call, r, "receiver in indirect call")
 			}
 		} else { // indirect and OCALLFUNC = could be captured variables, too. (#14409)
-			ll := e.nodeEscState(n).Escretval.Slice()
-			for _, llN := range ll {
-				escassignDereference(e, llN, fn, e.stepAssign(nil, llN, fn, "captured by called closure"))
+			rets := e.nodeEscState(call).Retval.Slice()
+			for _, ret := range rets {
+				e.escassignDereference(ret, fn, e.stepAssignWhere(ret, fn, "captured by called closure", call))
 			}
 		}
 		return
 	}
 
-	nE := e.nodeEscState(n)
+	cE := e.nodeEscState(call)
 	if fn != nil && fn.Op == ONAME && fn.Class == PFUNC &&
 		fn.Name.Defn != nil && fn.Name.Defn.Nbody.Len() != 0 && fn.Name.Param.Ntype != nil && fn.Name.Defn.Esc < EscFuncTagged {
 		if Debug['m'] > 3 {
-			fmt.Printf("%v::esccall:: %v in recursive group\n", linestr(lineno), Nconv(n, FmtShort))
+			fmt.Printf("%v::esccall:: %S in recursive group\n", linestr(lineno), call)
 		}
 
 		// function in same mutually recursive group. Incorporate into flow graph.
-		//		print("esc local fn: %N\n", fn->ntype);
-		if fn.Name.Defn.Esc == EscFuncUnknown || nE.Escretval.Len() != 0 {
+		//		print("esc local fn: %N\n", fn.Func.Ntype);
+		if fn.Name.Defn.Esc == EscFuncUnknown || cE.Retval.Len() != 0 {
 			Fatalf("graph inconsistency")
 		}
 
-		lls := ll.Slice()
 		sawRcvr := false
-		var src *Node
-		for _, n2 := range fn.Name.Defn.Func.Dcl {
-			switch n2.Class {
+		for _, n := range fn.Name.Defn.Func.Dcl {
+			switch n.Class {
 			case PPARAM:
-				if n.Op != OCALLFUNC && !sawRcvr {
-					escassignNilWhy(e, n2, n.Left.Left, "call receiver")
+				if call.Op != OCALLFUNC && !sawRcvr {
+					e.escassignWhyWhere(n, call.Left.Left, "call receiver", call)
 					sawRcvr = true
 					continue
 				}
-				if len(lls) == 0 {
+				if len(args) == 0 {
 					continue
 				}
-				src = lls[0]
-				if n2.Isddd && !n.Isddd {
+				arg := args[0]
+				if n.Isddd && !call.Isddd {
 					// Introduce ODDDARG node to represent ... allocation.
-					src = Nod(ODDDARG, nil, nil)
-					arr := typArray(n2.Type.Elem(), int64(len(lls)))
-					src.Type = Ptrto(arr) // make pointer so it will be tracked
-					src.Lineno = n.Lineno
-					e.track(src)
-					n.Right = src
+					arg = nod(ODDDARG, nil, nil)
+					arr := typArray(n.Type.Elem(), int64(len(args)))
+					arg.Type = ptrto(arr) // make pointer so it will be tracked
+					arg.Lineno = call.Lineno
+					e.track(arg)
+					call.Right = arg
 				}
-				escassignNilWhy(e, n2, src, "arg to recursive call")
-				if src != lls[0] {
+				e.escassignWhyWhere(n, arg, "arg to recursive call", call) // TODO this message needs help.
+				if arg != args[0] {
 					// "..." arguments are untracked
-					for _, n2 := range lls {
+					for _, a := range args {
 						if Debug['m'] > 3 {
-							fmt.Printf("%v::esccall:: ... <- %v, untracked\n", linestr(lineno), Nconv(n2, FmtShort))
+							fmt.Printf("%v::esccall:: ... <- %S, untracked\n", linestr(lineno), a)
 						}
-						escassignSinkNilWhy(e, src, n2, "... arg to recursive call")
+						e.escassignSinkWhyWhere(arg, a, "... arg to recursive call", call)
 					}
 					// No more PPARAM processing, but keep
 					// going for PPARAMOUT.
-					lls = nil
+					args = nil
 					continue
 				}
-				lls = lls[1:]
+				args = args[1:]
 
 			case PPARAMOUT:
-				nE.Escretval.Append(n2)
+				cE.Retval.Append(n)
 			}
 		}
 
@@ -1548,48 +1575,48 @@ func esccall(e *EscState, n *Node, up *Node) {
 	}
 
 	// Imported or completely analyzed function. Use the escape tags.
-	if nE.Escretval.Len() != 0 {
-		Fatalf("esc already decorated call %v\n", Nconv(n, FmtSign))
+	if cE.Retval.Len() != 0 {
+		Fatalf("esc already decorated call %+v\n", call)
 	}
 
 	if Debug['m'] > 3 {
-		fmt.Printf("%v::esccall:: %v not recursive\n", linestr(lineno), Nconv(n, FmtShort))
+		fmt.Printf("%v::esccall:: %S not recursive\n", linestr(lineno), call)
 	}
 
 	// set up out list on this call node with dummy auto ONAMES in the current (calling) function.
-	initEscretval(e, n, fntype)
+	e.initEscRetval(call, fntype)
 
-	//	print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
+	//	print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, e.nodeEscState(call).Retval);
 
 	// Receiver.
-	if n.Op != OCALLFUNC {
-		t := fntype.Recv()
-		src := n.Left.Left
-		if haspointers(t.Type) {
-			escassignfromtag(e, t.Note, nE.Escretval, src)
+	if call.Op != OCALLFUNC {
+		rf := fntype.Recv()
+		r := call.Left.Left
+		if haspointers(rf.Type) {
+			e.escassignfromtag(rf.Note, cE.Retval, r, call)
 		}
 	}
 
-	var src *Node
-	note := ""
+	var arg *Node
+	var note string
+	param, it := iterFields(fntype.Params())
 	i := 0
-	lls := ll.Slice()
-	for t, it := IterFields(fntype.Params()); i < len(lls); i++ {
-		src = lls[i]
-		note = t.Note
-		if t.Isddd && !n.Isddd {
+	for ; i < len(args); i++ {
+		arg = args[i]
+		note = param.Note
+		if param.Isddd && !call.Isddd {
 			// Introduce ODDDARG node to represent ... allocation.
-			src = Nod(ODDDARG, nil, nil)
-			src.Lineno = n.Lineno
-			arr := typArray(t.Type.Elem(), int64(len(lls)-i))
-			src.Type = Ptrto(arr) // make pointer so it will be tracked
-			e.track(src)
-			n.Right = src
+			arg = nod(ODDDARG, nil, nil)
+			arg.Lineno = call.Lineno
+			arr := typArray(param.Type.Elem(), int64(len(args)-i))
+			arg.Type = ptrto(arr) // make pointer so it will be tracked
+			e.track(arg)
+			call.Right = arg
 		}
 
-		if haspointers(t.Type) {
-			if escassignfromtag(e, note, nE.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC {
-				a := src
+		if haspointers(param.Type) {
+			if e.escassignfromtag(note, cE.Retval, arg, call)&EscMask == EscNone && parent.Op != ODEFER && parent.Op != OPROC {
+				a := arg
 				for a.Op == OCONVNOP {
 					a = a.Left
 				}
@@ -1600,13 +1627,14 @@ func esccall(e *EscState, n *Node, up *Node) {
 				// synthesizing this expression can be reclaimed when
 				// the function returns.
 				// This 'noescape' is even stronger than the usual esc == EscNone.
-				// src->esc == EscNone means that src does not escape the current function.
-				// src->noescape = 1 here means that src does not escape this statement
+				// arg.Esc == EscNone means that arg does not escape the current function.
+				// arg.Noescape = true here means that arg does not escape this statement
 				// in the current function.
 				case OCALLPART,
 					OCLOSURE,
 					ODDDARG,
 					OARRAYLIT,
+					OSLICELIT,
 					OPTRLIT,
 					OSTRUCTLIT:
 					a.Noescape = true
@@ -1614,34 +1642,34 @@ func esccall(e *EscState, n *Node, up *Node) {
 			}
 		}
 
-		if src != lls[i] {
-			// This occurs when function parameter type Isddd and n not Isddd
+		if arg != args[i] {
+			// This occurs when function parameter field Isddd and call not Isddd
 			break
 		}
 
 		if note == uintptrEscapesTag {
-			escassignSinkNilWhy(e, src, src, "escaping uintptr")
+			e.escassignSinkWhy(arg, arg, "escaping uintptr")
 		}
 
-		t = it.Next()
+		param = it.Next()
 	}
 
 	// Store arguments into slice for ... arg.
-	for ; i < len(lls); i++ {
+	for ; i < len(args); i++ {
 		if Debug['m'] > 3 {
-			fmt.Printf("%v::esccall:: ... <- %v\n", linestr(lineno), Nconv(lls[i], FmtShort))
+			fmt.Printf("%v::esccall:: ... <- %S\n", linestr(lineno), args[i])
 		}
 		if note == uintptrEscapesTag {
-			escassignSinkNilWhy(e, src, lls[i], "arg to uintptrescapes ...")
+			e.escassignSinkWhyWhere(arg, args[i], "arg to uintptrescapes ...", call)
 		} else {
-			escassignNilWhy(e, src, lls[i], "arg to ...")
+			e.escassignWhyWhere(arg, args[i], "arg to ...", call)
 		}
 	}
 }
 
 // escflows records the link src->dst in dst, throwing out some quick wins,
 // and also ensuring that dst is noted as a flow destination.
-func escflows(e *EscState, dst, src *Node, why *EscStep) {
+func (e *EscState) escflows(dst, src *Node, why *EscStep) {
 	if dst == nil || src == nil || dst == src {
 		return
 	}
@@ -1652,11 +1680,11 @@ func escflows(e *EscState, dst, src *Node, why *EscStep) {
 	}
 
 	if Debug['m'] > 3 {
-		fmt.Printf("%v::flows:: %v <- %v\n", linestr(lineno), Nconv(dst, FmtShort), Nconv(src, FmtShort))
+		fmt.Printf("%v::flows:: %S <- %S\n", linestr(lineno), dst, src)
 	}
 
 	dstE := e.nodeEscState(dst)
-	if len(dstE.Escflowsrc) == 0 {
+	if len(dstE.Flowsrc) == 0 {
 		e.dsts = append(e.dsts, dst)
 		e.dstcount++
 	}
@@ -1664,11 +1692,11 @@ func escflows(e *EscState, dst, src *Node, why *EscStep) {
 	e.edgecount++
 
 	if why == nil {
-		dstE.Escflowsrc = append(dstE.Escflowsrc, EscStep{src: src})
+		dstE.Flowsrc = append(dstE.Flowsrc, EscStep{src: src})
 	} else {
 		starwhy := *why
 		starwhy.src = src // TODO: need to reconcile this w/ needs of explanations.
-		dstE.Escflowsrc = append(dstE.Escflowsrc, starwhy)
+		dstE.Flowsrc = append(dstE.Flowsrc, starwhy)
 	}
 }
 
@@ -1678,27 +1706,26 @@ func escflows(e *EscState, dst, src *Node, why *EscStep) {
 // so this address doesn't leak (yet).
 // If level == 0, it means the /value/ of this node can reach the root of this flood.
 // so if this node is an OADDR, its argument should be marked as escaping iff
-// its currfn/e->loopdepth are different from the flood's root.
+// its currfn/e.loopdepth are different from the flood's root.
 // Once an object has been moved to the heap, all of its upstream should be considered
 // escaping to the global scope.
-func escflood(e *EscState, dst *Node) {
+func (e *EscState) escflood(dst *Node) {
 	switch dst.Op {
 	case ONAME, OCLOSURE:
-		break
-
 	default:
 		return
 	}
 
 	dstE := e.nodeEscState(dst)
 	if Debug['m'] > 2 {
-		fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", e.walkgen, Nconv(dst, FmtShort), e.curfnSym(dst), dstE.Escloopdepth)
+		fmt.Printf("\nescflood:%d: dst %S scope:%v[%d]\n", e.walkgen, dst, e.curfnSym(dst), dstE.Loopdepth)
 	}
 
-	for i, l := range dstE.Escflowsrc {
+	for i := range dstE.Flowsrc {
 		e.walkgen++
-		dstE.Escflowsrc[i].parent = nil
-		escwalk(e, levelFrom(0), dst, l.src, &dstE.Escflowsrc[i])
+		s := &dstE.Flowsrc[i]
+		s.parent = nil
+		e.escwalk(levelFrom(0), dst, s.src, s)
 	}
 }
 
@@ -1723,10 +1750,14 @@ func (es *EscStep) describe(src *Node) {
 		// case it is step.dst.
 		nextDest := step.parent
 		dst := step.dst
+		where := step.where
 		if nextDest != nil {
 			dst = nextDest.src
 		}
-		Warnl(src.Lineno, "\tfrom %s (%s) at %s", dst, step.why, dst.Line())
+		if where == nil {
+			where = dst
+		}
+		Warnl(src.Lineno, "\tfrom %v (%s) at %s", dst, step.why, where.Line())
 	}
 	for step := step0; step != nil && step.busy; step = step.parent {
 		step.busy = false
@@ -1735,11 +1766,11 @@ func (es *EscStep) describe(src *Node) {
 
 const NOTALOOPDEPTH = -1
 
-func escwalk(e *EscState, level Level, dst *Node, src *Node, step *EscStep) {
-	escwalkBody(e, level, dst, src, step, NOTALOOPDEPTH)
+func (e *EscState) escwalk(level Level, dst *Node, src *Node, step *EscStep) {
+	e.escwalkBody(level, dst, src, step, NOTALOOPDEPTH)
 }
 
-func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep, extraloopdepth int32) {
+func (e *EscState) escwalkBody(level Level, dst *Node, src *Node, step *EscStep, extraloopdepth int32) {
 	if src.Op == OLITERAL {
 		return
 	}
@@ -1748,12 +1779,12 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 		// Esclevels are vectors, do not compare as integers,
 		// and must use "min" of old and new to guarantee
 		// convergence.
-		level = level.min(srcE.Esclevel)
-		if level == srcE.Esclevel {
+		level = level.min(srcE.Level)
+		if level == srcE.Level {
 			// Have we been here already with an extraloopdepth,
 			// or is the extraloopdepth provided no improvement on
 			// what's already been seen?
-			if srcE.Maxextraloopdepth >= extraloopdepth || srcE.Escloopdepth >= extraloopdepth {
+			if srcE.Maxextraloopdepth >= extraloopdepth || srcE.Loopdepth >= extraloopdepth {
 				return
 			}
 			srcE.Maxextraloopdepth = extraloopdepth
@@ -1763,16 +1794,16 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 	}
 
 	srcE.Walkgen = e.walkgen
-	srcE.Esclevel = level
-	modSrcLoopdepth := srcE.Escloopdepth
+	srcE.Level = level
+	modSrcLoopdepth := srcE.Loopdepth
 
 	if extraloopdepth > modSrcLoopdepth {
 		modSrcLoopdepth = extraloopdepth
 	}
 
 	if Debug['m'] > 2 {
-		fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %v(%v) scope:%v[%d] extraloopdepth=%v\n",
-			level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", src.Op, Nconv(src, FmtShort), jconv(src, FmtShort), e.curfnSym(src), srcE.Escloopdepth, extraloopdepth)
+		fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %S(%0j) scope:%v[%d] extraloopdepth=%v\n",
+			level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", src.Op, src, src, e.curfnSym(src), srcE.Loopdepth, extraloopdepth)
 	}
 
 	e.pdepth++
@@ -1782,7 +1813,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 	var osrcesc uint16 // used to prevent duplicate error messages
 
 	dstE := e.nodeEscState(dst)
-	if funcOutputAndInput(dst, src) && src.Esc&EscMask < EscScope && dst.Esc != EscHeap {
+	if funcOutputAndInput(dst, src) && src.Esc&EscMask < EscHeap && dst.Esc != EscHeap {
 		// This case handles:
 		// 1. return in
 		// 2. return &in
@@ -1790,10 +1821,10 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 		// 4. return *in
 		if Debug['m'] != 0 {
 			if Debug['m'] <= 2 {
-				Warnl(src.Lineno, "leaking param: %v to result %v level=%v", Nconv(src, FmtShort), dst.Sym, level.int())
+				Warnl(src.Lineno, "leaking param: %S to result %v level=%v", src, dst.Sym, level.int())
 				step.describe(src)
 			} else {
-				Warnl(src.Lineno, "leaking param: %v to result %v level=%v", Nconv(src, FmtShort), dst.Sym, level)
+				Warnl(src.Lineno, "leaking param: %S to result %v level=%v", src, dst.Sym, level)
 			}
 		}
 		if src.Esc&EscMask != EscReturn {
@@ -1806,45 +1837,44 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 	// If parameter content escapes to heap, set EscContentEscapes
 	// Note minor confusion around escape from pointer-to-struct vs escape from struct
 	if dst.Esc == EscHeap &&
-		src.Op == ONAME && src.Class == PPARAM && src.Esc&EscMask < EscScope &&
+		src.Op == ONAME && src.Class == PPARAM && src.Esc&EscMask < EscHeap &&
 		level.int() > 0 {
 		src.Esc = escMax(EscContentEscapes|src.Esc, EscNone)
 		if Debug['m'] != 0 {
-			Warnl(src.Lineno, "mark escaped content: %v", Nconv(src, FmtShort))
+			Warnl(src.Lineno, "mark escaped content: %S", src)
 			step.describe(src)
 		}
 	}
 
-	leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Escloopdepth < modSrcLoopdepth
+	leaks = level.int() <= 0 && level.guaranteedDereference() <= 0 && dstE.Loopdepth < modSrcLoopdepth
 	leaks = leaks || level.int() <= 0 && dst.Esc&EscMask == EscHeap
 
 	osrcesc = src.Esc
 	switch src.Op {
 	case ONAME:
-		if src.Class == PPARAM && (leaks || dstE.Escloopdepth < 0) && src.Esc&EscMask < EscScope {
+		if src.Class == PPARAM && (leaks || dstE.Loopdepth < 0) && src.Esc&EscMask < EscHeap {
 			if level.guaranteedDereference() > 0 {
 				src.Esc = escMax(EscContentEscapes|src.Esc, EscNone)
 				if Debug['m'] != 0 {
 					if Debug['m'] <= 2 {
 						if osrcesc != src.Esc {
-							Warnl(src.Lineno, "leaking param content: %v", Nconv(src, FmtShort))
+							Warnl(src.Lineno, "leaking param content: %S", src)
 							step.describe(src)
 						}
 					} else {
-						Warnl(src.Lineno, "leaking param content: %v level=%v dst.eld=%v src.eld=%v dst=%v",
-							Nconv(src, FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth, Nconv(dst, FmtShort))
+						Warnl(src.Lineno, "leaking param content: %S level=%v dst.eld=%v src.eld=%v dst=%S",
+							src, level, dstE.Loopdepth, modSrcLoopdepth, dst)
 					}
 				}
 			} else {
-				src.Esc = EscScope
+				src.Esc = EscHeap
 				if Debug['m'] != 0 {
-
 					if Debug['m'] <= 2 {
-						Warnl(src.Lineno, "leaking param: %v", Nconv(src, FmtShort))
+						Warnl(src.Lineno, "leaking param: %S", src)
 						step.describe(src)
 					} else {
-						Warnl(src.Lineno, "leaking param: %v level=%v dst.eld=%v src.eld=%v dst=%v",
-							Nconv(src, FmtShort), level, dstE.Escloopdepth, modSrcLoopdepth, Nconv(dst, FmtShort))
+						Warnl(src.Lineno, "leaking param: %S level=%v dst.eld=%v src.eld=%v dst=%S",
+							src, level, dstE.Loopdepth, modSrcLoopdepth, dst)
 					}
 				}
 			}
@@ -1854,10 +1884,10 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 		// original variable.
 		if src.isClosureVar() {
 			if leaks && Debug['m'] != 0 {
-				Warnl(src.Lineno, "leaking closure reference %v", Nconv(src, FmtShort))
+				Warnl(src.Lineno, "leaking closure reference %S", src)
 				step.describe(src)
 			}
-			escwalk(e, level, dst, src.Name.Defn, e.stepWalk(dst, src.Name.Defn, "closure-var", step))
+			e.escwalk(level, dst, src.Name.Defn, e.stepWalk(dst, src.Name.Defn, "closure-var", step))
 		}
 
 	case OPTRLIT, OADDR:
@@ -1873,28 +1903,28 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 					p = p.Left // merely to satisfy error messages in tests
 				}
 				if Debug['m'] > 2 {
-					Warnl(src.Lineno, "%v escapes to heap, level=%v, dst=%v dst.eld=%v, src.eld=%v",
-						Nconv(p, FmtShort), level, dst, dstE.Escloopdepth, modSrcLoopdepth)
+					Warnl(src.Lineno, "%S escapes to heap, level=%v, dst=%v dst.eld=%v, src.eld=%v",
+						p, level, dst, dstE.Loopdepth, modSrcLoopdepth)
 				} else {
-					Warnl(src.Lineno, "%v escapes to heap", Nconv(p, FmtShort))
+					Warnl(src.Lineno, "%S escapes to heap", p)
 					step.describe(src)
 				}
 			}
 			addrescapes(src.Left)
-			escwalkBody(e, level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step), modSrcLoopdepth)
+			e.escwalkBody(level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step), modSrcLoopdepth)
 			extraloopdepth = modSrcLoopdepth // passes to recursive case, seems likely a no-op
 		} else {
-			escwalk(e, level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step))
+			e.escwalk(level.dec(), dst, src.Left, e.stepWalk(dst, src.Left, why, step))
 		}
 
 	case OAPPEND:
-		escwalk(e, level, dst, src.List.First(), e.stepWalk(dst, src.List.First(), "append-first-arg", step))
+		e.escwalk(level, dst, src.List.First(), e.stepWalk(dst, src.List.First(), "append-first-arg", step))
 
 	case ODDDARG:
 		if leaks {
 			src.Esc = EscHeap
 			if Debug['m'] != 0 && osrcesc != src.Esc {
-				Warnl(src.Lineno, "%v escapes to heap", Nconv(src, FmtShort))
+				Warnl(src.Lineno, "%S escapes to heap", src)
 				step.describe(src)
 			}
 			extraloopdepth = modSrcLoopdepth
@@ -1902,12 +1932,12 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 		// similar to a slice arraylit and its args.
 		level = level.dec()
 
-	case OARRAYLIT:
-		if src.Type.IsArray() {
-			break
-		}
+	case OSLICELIT:
 		for _, n1 := range src.List.Slice() {
-			escwalk(e, level.dec(), dst, n1.Right, e.stepWalk(dst, n1.Right, "slice-literal-element", step))
+			if n1.Op == OKEY {
+				n1 = n1.Right
+			}
+			e.escwalk(level.dec(), dst, n1, e.stepWalk(dst, n1, "slice-literal-element", step))
 		}
 
 		fallthrough
@@ -1929,7 +1959,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 		if leaks {
 			src.Esc = EscHeap
 			if Debug['m'] != 0 && osrcesc != src.Esc {
-				Warnl(src.Lineno, "%v escapes to heap", Nconv(src, FmtShort))
+				Warnl(src.Lineno, "%S escapes to heap", src)
 				step.describe(src)
 			}
 			extraloopdepth = modSrcLoopdepth
@@ -1937,7 +1967,7 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 
 	case ODOT,
 		ODOTTYPE:
-		escwalk(e, level, dst, src.Left, e.stepWalk(dst, src.Left, "dot", step))
+		e.escwalk(level, dst, src.Left, e.stepWalk(dst, src.Left, "dot", step))
 
 	case
 		OSLICE,
@@ -1945,21 +1975,21 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 		OSLICE3,
 		OSLICE3ARR,
 		OSLICESTR:
-		escwalk(e, level, dst, src.Left, e.stepWalk(dst, src.Left, "slice", step))
+		e.escwalk(level, dst, src.Left, e.stepWalk(dst, src.Left, "slice", step))
 
 	case OINDEX:
 		if src.Left.Type.IsArray() {
-			escwalk(e, level, dst, src.Left, e.stepWalk(dst, src.Left, "fixed-array-index-of", step))
+			e.escwalk(level, dst, src.Left, e.stepWalk(dst, src.Left, "fixed-array-index-of", step))
 			break
 		}
 		fallthrough
 
 	case ODOTPTR:
-		escwalk(e, level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "dot of pointer", step))
+		e.escwalk(level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "dot of pointer", step))
 	case OINDEXMAP:
-		escwalk(e, level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "map index", step))
+		e.escwalk(level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "map index", step))
 	case OIND:
-		escwalk(e, level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "indirection", step))
+		e.escwalk(level.inc(), dst, src.Left, e.stepWalk(dst, src.Left, "indirection", step))
 
 	// In this case a link went directly to a call, but should really go
 	// to the dummy .outN outputs that were created for the call that
@@ -1967,23 +1997,25 @@ func escwalkBody(e *EscState, level Level, dst *Node, src *Node, step *EscStep,
 	// See e.g. #10466
 	// This can only happen with functions returning a single result.
 	case OCALLMETH, OCALLFUNC, OCALLINTER:
-		if srcE.Escretval.Len() != 0 {
+		if srcE.Retval.Len() != 0 {
 			if Debug['m'] > 2 {
-				fmt.Printf("%v:[%d] dst %v escwalk replace src: %v with %v\n",
+				fmt.Printf("%v:[%d] dst %S escwalk replace src: %S with %S\n",
 					linestr(lineno), e.loopdepth,
-					Nconv(dst, FmtShort), Nconv(src, FmtShort), Nconv(srcE.Escretval.First(), FmtShort))
+					dst, src, srcE.Retval.First())
 			}
-			src = srcE.Escretval.First()
+			src = srcE.Retval.First()
 			srcE = e.nodeEscState(src)
 		}
 	}
 
 recurse:
 	level = level.copy()
-	for i, ll := range srcE.Escflowsrc {
-		srcE.Escflowsrc[i].parent = step
-		escwalkBody(e, level, dst, ll.src, &srcE.Escflowsrc[i], extraloopdepth)
-		srcE.Escflowsrc[i].parent = nil
+
+	for i := range srcE.Flowsrc {
+		s := &srcE.Flowsrc[i]
+		s.parent = step
+		e.escwalkBody(level, dst, s.src, s, extraloopdepth)
+		s.parent = nil
 	}
 
 	e.pdepth--
@@ -2000,8 +2032,8 @@ var unsafeUintptrTag = "unsafe-uintptr"
 // marked go:uintptrescapes.
 const uintptrEscapesTag = "uintptr-escapes"
 
-func esctag(e *EscState, func_ *Node) {
-	func_.Esc = EscFuncTagged
+func (e *EscState) esctag(fn *Node) {
+	fn.Esc = EscFuncTagged
 
 	name := func(s *Sym, narg int) string {
 		if s != nil {
@@ -2012,11 +2044,11 @@ func esctag(e *EscState, func_ *Node) {
 
 	// External functions are assumed unsafe,
 	// unless //go:noescape is given before the declaration.
-	if func_.Nbody.Len() == 0 {
-		if func_.Noescape {
-			for _, t := range func_.Type.Params().Fields().Slice() {
-				if haspointers(t.Type) {
-					t.Note = mktag(EscNone)
+	if fn.Nbody.Len() == 0 {
+		if fn.Noescape {
+			for _, f := range fn.Type.Params().Fields().Slice() {
+				if haspointers(f.Type) {
+					f.Note = mktag(EscNone)
 				}
 			}
 		}
@@ -2028,44 +2060,41 @@ func esctag(e *EscState, func_ *Node) {
 		// but we are reusing the ability to annotate an individual function
 		// argument and pass those annotations along to importing code.
 		narg := 0
-		for _, t := range func_.Type.Params().Fields().Slice() {
+		for _, f := range fn.Type.Params().Fields().Slice() {
 			narg++
-			if t.Type.Etype == TUINTPTR {
+			if f.Type.Etype == TUINTPTR {
 				if Debug['m'] != 0 {
-					Warnl(func_.Lineno, "%v assuming %v is unsafe uintptr", funcSym(func_), name(t.Sym, narg))
+					Warnl(fn.Lineno, "%v assuming %v is unsafe uintptr", funcSym(fn), name(f.Sym, narg))
 				}
-				t.Note = unsafeUintptrTag
+				f.Note = unsafeUintptrTag
 			}
 		}
 
 		return
 	}
 
-	if func_.Func.Pragma&UintptrEscapes != 0 {
+	if fn.Func.Pragma&UintptrEscapes != 0 {
 		narg := 0
-		for _, t := range func_.Type.Params().Fields().Slice() {
+		for _, f := range fn.Type.Params().Fields().Slice() {
 			narg++
-			if t.Type.Etype == TUINTPTR {
+			if f.Type.Etype == TUINTPTR {
 				if Debug['m'] != 0 {
-					Warnl(func_.Lineno, "%v marking %v as escaping uintptr", funcSym(func_), name(t.Sym, narg))
+					Warnl(fn.Lineno, "%v marking %v as escaping uintptr", funcSym(fn), name(f.Sym, narg))
 				}
-				t.Note = uintptrEscapesTag
+				f.Note = uintptrEscapesTag
 			}
 
-			if t.Isddd && t.Type.Elem().Etype == TUINTPTR {
+			if f.Isddd && f.Type.Elem().Etype == TUINTPTR {
 				// final argument is ...uintptr.
 				if Debug['m'] != 0 {
-					Warnl(func_.Lineno, "%v marking %v as escaping ...uintptr", funcSym(func_), name(t.Sym, narg))
+					Warnl(fn.Lineno, "%v marking %v as escaping ...uintptr", funcSym(fn), name(f.Sym, narg))
 				}
-				t.Note = uintptrEscapesTag
+				f.Note = uintptrEscapesTag
 			}
 		}
 	}
 
-	savefn := Curfn
-	Curfn = func_
-
-	for _, ln := range Curfn.Func.Dcl {
+	for _, ln := range fn.Func.Dcl {
 		if ln.Op != ONAME {
 			continue
 		}
@@ -2079,11 +2108,7 @@ func esctag(e *EscState, func_ *Node) {
 				}
 			}
 
-		case EscHeap, // touched by escflood, moved to heap
-			EscScope: // touched by escflood, value leaves scope
-			break
+		case EscHeap: // touched by escflood, moved to heap
 		}
 	}
-
-	Curfn = savefn
 }
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index 911ef0f..b4c15e4 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -9,14 +9,12 @@ import (
 	"bytes"
 	"cmd/internal/bio"
 	"fmt"
-	"sort"
 	"unicode"
 	"unicode/utf8"
 )
 
 var (
-	newexport    bool // if set, use new export format
-	Debug_export int  // if set, print debugging information about export data
+	Debug_export int // if set, print debugging information about export data
 	exportsize   int
 )
 
@@ -37,16 +35,21 @@ func exportsym(n *Node) {
 	}
 	if n.Sym.Flags&(SymExport|SymPackage) != 0 {
 		if n.Sym.Flags&SymPackage != 0 {
-			Yyerror("export/package mismatch: %v", n.Sym)
+			yyerror("export/package mismatch: %v", n.Sym)
 		}
 		return
 	}
 
 	n.Sym.Flags |= SymExport
-
 	if Debug['E'] != 0 {
 		fmt.Printf("export symbol %v\n", n.Sym)
 	}
+
+	// Ensure original object is on exportlist before aliases.
+	if n.Sym.Flags&SymAlias != 0 {
+		exportlist = append(exportlist, n.Sym.Def)
+	}
+
 	exportlist = append(exportlist, n)
 }
 
@@ -84,8 +87,7 @@ func autoexport(n *Node, ctxt Class) {
 		return
 	}
 
-	// -A is for cmd/gc/mkbuiltin script, so export everything
-	if Debug['A'] != 0 || exportname(n.Sym.Name) || initname(n.Sym.Name) {
+	if exportname(n.Sym.Name) || initname(n.Sym.Name) {
 		exportsym(n)
 	}
 	if asmhdr != "" && n.Sym.Pkg == localpkg && n.Sym.Flags&SymAsm == 0 {
@@ -94,18 +96,6 @@ func autoexport(n *Node, ctxt Class) {
 	}
 }
 
-func dumppkg(p *Pkg) {
-	if p == nil || p == localpkg || p.Exported || p == builtinpkg {
-		return
-	}
-	p.Exported = true
-	suffix := ""
-	if !p.Direct {
-		suffix = " // indirect"
-	}
-	exportf("\timport %s %q%s\n", p.Name, p.Path, suffix)
-}
-
 // Look for anything we need for the inline body
 func reexportdeplist(ll Nodes) {
 	for _, n := range ll.Slice() {
@@ -130,7 +120,7 @@ func reexportdep(n *Node) {
 			}
 
 			// nodes for method calls.
-			if n.Type == nil || n.Type.Recv() != nil {
+			if n.Type == nil || n.IsMethod() {
 				break
 			}
 			fallthrough
@@ -196,6 +186,7 @@ func reexportdep(n *Node) {
 		ODOTTYPE2,
 		OSTRUCTLIT,
 		OARRAYLIT,
+		OSLICELIT,
 		OPTRLIT,
 		OMAKEMAP,
 		OMAKESLICE,
@@ -224,53 +215,6 @@ func reexportdep(n *Node) {
 	reexportdeplist(n.Nbody)
 }
 
-func dumpexportconst(s *Sym) {
-	n := typecheck(s.Def, Erv)
-	if n == nil || n.Op != OLITERAL {
-		Fatalf("dumpexportconst: oconst nil: %v", s)
-	}
-
-	t := n.Type // may or may not be specified
-	dumpexporttype(t)
-
-	if t != nil && !t.IsUntyped() {
-		exportf("\tconst %v %v = %v\n", sconv(s, FmtSharp), Tconv(t, FmtSharp), vconv(n.Val(), FmtSharp))
-	} else {
-		exportf("\tconst %v = %v\n", sconv(s, FmtSharp), vconv(n.Val(), FmtSharp))
-	}
-}
-
-func dumpexportvar(s *Sym) {
-	n := s.Def
-	n = typecheck(n, Erv|Ecall)
-	if n == nil || n.Type == nil {
-		Yyerror("variable exported but not defined: %v", s)
-		return
-	}
-
-	t := n.Type
-	dumpexporttype(t)
-
-	if t.Etype == TFUNC && n.Class == PFUNC {
-		if n.Func != nil && n.Func.Inl.Len() != 0 {
-			// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
-			// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
-			if Debug['l'] < 2 {
-				typecheckinl(n)
-			}
-
-			// NOTE: The space after %#S here is necessary for ld's export data parser.
-			exportf("\tfunc %v %v { %v }\n", sconv(s, FmtSharp), Tconv(t, FmtShort|FmtSharp), hconv(n.Func.Inl, FmtSharp|FmtBody))
-
-			reexportdeplist(n.Func.Inl)
-		} else {
-			exportf("\tfunc %v %v\n", sconv(s, FmtSharp), Tconv(t, FmtShort|FmtSharp))
-		}
-	} else {
-		exportf("\tvar %v %v\n", sconv(s, FmtSharp), Tconv(t, FmtSharp))
-	}
-}
-
 // methodbyname sorts types by symbol name.
 type methodbyname []*Field
 
@@ -278,167 +222,44 @@ func (x methodbyname) Len() int           { return len(x) }
 func (x methodbyname) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
 func (x methodbyname) Less(i, j int) bool { return x[i].Sym.Name < x[j].Sym.Name }
 
-func dumpexporttype(t *Type) {
-	if t == nil {
-		return
-	}
-	if t.Printed || t == Types[t.Etype] || t == bytetype || t == runetype || t == errortype {
-		return
-	}
-	t.Printed = true
-
-	if t.Sym != nil {
-		dumppkg(t.Sym.Pkg)
-	}
-
-	switch t.Etype {
-	case TSTRUCT, TINTER:
-		for _, f := range t.Fields().Slice() {
-			dumpexporttype(f.Type)
-		}
-	case TFUNC:
-		dumpexporttype(t.Recvs())
-		dumpexporttype(t.Results())
-		dumpexporttype(t.Params())
-	case TMAP:
-		dumpexporttype(t.Val())
-		dumpexporttype(t.Key())
-	case TARRAY, TCHAN, TPTR32, TPTR64, TSLICE:
-		dumpexporttype(t.Elem())
-	}
-
-	if t.Sym == nil {
-		return
-	}
-
-	var m []*Field
-	for _, f := range t.Methods().Slice() {
-		dumpexporttype(f.Type)
-		m = append(m, f)
-	}
-	sort.Sort(methodbyname(m))
-
-	exportf("\ttype %v %v\n", sconv(t.Sym, FmtSharp), Tconv(t, FmtSharp|FmtLong))
-	for _, f := range m {
-		if f.Nointerface {
-			exportf("\t//go:nointerface\n")
-		}
-		if f.Type.Nname() != nil && f.Type.Nname().Func.Inl.Len() != 0 { // nname was set by caninl
-
-			// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
-			// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
-			if Debug['l'] < 2 {
-				typecheckinl(f.Type.Nname())
-			}
-			exportf("\tfunc %v %v %v { %v }\n", Tconv(f.Type.Recvs(), FmtSharp), sconv(f.Sym, FmtShort|FmtByte|FmtSharp), Tconv(f.Type, FmtShort|FmtSharp), hconv(f.Type.Nname().Func.Inl, FmtSharp|FmtBody))
-			reexportdeplist(f.Type.Nname().Func.Inl)
-		} else {
-			exportf("\tfunc %v %v %v\n", Tconv(f.Type.Recvs(), FmtSharp), sconv(f.Sym, FmtShort|FmtByte|FmtSharp), Tconv(f.Type, FmtShort|FmtSharp))
-		}
-	}
-}
-
-func dumpsym(s *Sym) {
-	if s.Flags&SymExported != 0 {
-		return
-	}
-	s.Flags |= SymExported
-
-	if s.Def == nil {
-		Yyerror("unknown export symbol: %v", s)
-		return
-	}
-
-	//	print("dumpsym %O %+S\n", s->def->op, s);
-	dumppkg(s.Pkg)
-
-	switch s.Def.Op {
-	default:
-		Yyerror("unexpected export symbol: %v %v", s.Def.Op, s)
-
-	case OLITERAL:
-		dumpexportconst(s)
-
-	case OTYPE:
-		if s.Def.Type.Etype == TFORW {
-			Yyerror("export of incomplete type %v", s)
-		} else {
-			dumpexporttype(s.Def.Type)
-		}
-
-	case ONAME:
-		dumpexportvar(s)
-	}
-}
-
 func dumpexport() {
 	if buildid != "" {
 		exportf("build id %q\n", buildid)
 	}
 
 	size := 0 // size of export section without enclosing markers
-	if newexport {
-		// binary export
-		// The linker also looks for the $$ marker - use char after $$ to distinguish format.
-		exportf("\n$$B\n") // indicate binary format
-		if debugFormat {
-			// save a copy of the export data
-			var copy bytes.Buffer
-			bcopy := bufio.NewWriter(&copy)
-			size = export(bcopy, Debug_export != 0)
-			bcopy.Flush() // flushing to bytes.Buffer cannot fail
-			if n, err := bout.Write(copy.Bytes()); n != size || err != nil {
-				Fatalf("error writing export data: got %d bytes, want %d bytes, err = %v", n, size, err)
-			}
-			// export data must contain no '$' so that we can find the end by searching for "$$"
-			if bytes.IndexByte(copy.Bytes(), '$') >= 0 {
-				Fatalf("export data contains $")
-			}
-
-			// verify that we can read the copied export data back in
-			// (use empty package map to avoid collisions)
-			savedPkgMap := pkgMap
-			savedPkgs := pkgs
-			pkgMap = make(map[string]*Pkg)
-			pkgs = nil
-			importpkg = mkpkg("")
-			Import(bufio.NewReader(&copy)) // must not die
-			importpkg = nil
-			pkgs = savedPkgs
-			pkgMap = savedPkgMap
-		} else {
-			size = export(bout.Writer, Debug_export != 0)
-		}
-		exportf("\n$$\n")
-	} else {
-		// textual export
-		lno := lineno
-
-		exportf("\n$$\n") // indicate textual format
-		exportsize = 0
-		exportf("package %s", localpkg.Name)
-		if safemode {
-			exportf(" safe")
-		}
-		exportf("\n")
-
-		for _, p := range pkgs {
-			if p.Direct {
-				dumppkg(p)
-			}
+	// The linker also looks for the $$ marker - use char after $$ to distinguish format.
+	exportf("\n$$B\n") // indicate binary export format
+	if debugFormat {
+		// save a copy of the export data
+		var copy bytes.Buffer
+		bcopy := bufio.NewWriter(&copy)
+		size = export(bcopy, Debug_export != 0)
+		bcopy.Flush() // flushing to bytes.Buffer cannot fail
+		if n, err := bout.Write(copy.Bytes()); n != size || err != nil {
+			Fatalf("error writing export data: got %d bytes, want %d bytes, err = %v", n, size, err)
 		}
-
-		// exportlist grows during iteration - cannot use range
-		for i := 0; i < len(exportlist); i++ {
-			n := exportlist[i]
-			lineno = n.Lineno
-			dumpsym(n.Sym)
+		// export data must contain no '$' so that we can find the end by searching for "$$"
+		// TODO(gri) is this still needed?
+		if bytes.IndexByte(copy.Bytes(), '$') >= 0 {
+			Fatalf("export data contains $")
 		}
 
-		size = exportsize
-		exportf("\n$$\n")
-		lineno = lno
+		// verify that we can read the copied export data back in
+		// (use empty package map to avoid collisions)
+		savedPkgMap := pkgMap
+		savedPkgs := pkgs
+		pkgMap = make(map[string]*Pkg)
+		pkgs = nil
+		importpkg = mkpkg("")
+		Import(bufio.NewReader(&copy)) // must not die
+		importpkg = nil
+		pkgs = savedPkgs
+		pkgMap = savedPkgMap
+	} else {
+		size = export(bout.Writer, Debug_export != 0)
 	}
+	exportf("\n$$\n")
 
 	if Debug_export != 0 {
 		fmt.Printf("export data size = %d bytes\n", size)
@@ -454,7 +275,7 @@ func importsym(s *Sym, op Op) {
 
 	// mark the symbol so it is not reexported
 	if s.Def == nil {
-		if Debug['A'] != 0 || exportname(s.Name) || initname(s.Name) {
+		if exportname(s.Name) || initname(s.Name) {
 			s.Flags |= SymExport
 		} else {
 			s.Flags |= SymPackage // package scope
@@ -474,39 +295,11 @@ func pkgtype(s *Sym) *Type {
 	}
 
 	if s.Def.Type == nil {
-		Yyerror("pkgtype %v", s)
+		yyerror("pkgtype %v", s)
 	}
 	return s.Def.Type
 }
 
-// numImport tracks how often a package with a given name is imported.
-// It is used to provide a better error message (by using the package
-// path to disambiguate) if a package that appears multiple times with
-// the same name appears in an error message.
-var numImport = make(map[string]int)
-
-func importimport(s *Sym, path string) {
-	// Informational: record package name
-	// associated with import path, for use in
-	// human-readable messages.
-
-	if isbadimport(path) {
-		errorexit()
-	}
-	p := mkpkg(path)
-	if p.Name == "" {
-		p.Name = s.Name
-		numImport[s.Name]++
-	} else if p.Name != s.Name {
-		Yyerror("conflicting names %s and %s for package %q", p.Name, s.Name, p.Path)
-	}
-
-	if incannedimport == 0 && myimportpath != "" && path == myimportpath {
-		Yyerror("import %q: package depends on %q (import cycle)", importpkg.Path, path)
-		errorexit()
-	}
-}
-
 // importconst declares symbol s as an imported constant with type t and value n.
 func importconst(s *Sym, t *Type, n *Node) {
 	importsym(s, OLITERAL)
@@ -517,7 +310,7 @@ func importconst(s *Sym, t *Type, n *Node) {
 	}
 
 	if n.Op != OLITERAL {
-		Yyerror("expression must be a constant")
+		yyerror("expression must be a constant")
 		return
 	}
 
@@ -539,10 +332,10 @@ func importconst(s *Sym, t *Type, n *Node) {
 func importvar(s *Sym, t *Type) {
 	importsym(s, ONAME)
 	if s.Def != nil && s.Def.Op == ONAME {
-		if Eqtype(t, s.Def.Type) {
+		if eqtype(t, s.Def.Type) {
 			return
 		}
-		Yyerror("inconsistent definition for var %v during import\n\t%v (in %q)\n\t%v (in %q)", s, s.Def.Type, s.Importdef.Path, t, importpkg.Path)
+		yyerror("inconsistent definition for var %v during import\n\t%v (in %q)\n\t%v (in %q)", s, s.Def.Type, s.Importdef.Path, t, importpkg.Path)
 	}
 
 	n := newname(s)
@@ -551,33 +344,7 @@ func importvar(s *Sym, t *Type) {
 	declare(n, PEXTERN)
 
 	if Debug['E'] != 0 {
-		fmt.Printf("import var %v %v\n", s, Tconv(t, FmtLong))
-	}
-}
-
-// importtype and importer.importtype (bimport.go) need to remain in sync.
-func importtype(pt *Type, t *Type) {
-	// override declaration in unsafe.go for Pointer.
-	// there is no way in Go code to define unsafe.Pointer
-	// so we have to supply it.
-	if incannedimport != 0 && importpkg.Name == "unsafe" && pt.Nod.Sym.Name == "Pointer" {
-		t = Types[TUNSAFEPTR]
-	}
-
-	if pt.Etype == TFORW {
-		n := pt.Nod
-		copytype(pt.Nod, t)
-		pt.Nod = n // unzero nod
-		pt.Sym.Importdef = importpkg
-		pt.Sym.Lastlineno = lineno
-		declare(n, PEXTERN)
-		checkwidth(pt)
-	} else if !Eqtype(pt.Orig, t) {
-		Yyerror("inconsistent definition for type %v during import\n\t%v (in %q)\n\t%v (in %q)", pt.Sym, Tconv(pt, FmtLong), pt.Sym.Importdef.Path, Tconv(t, FmtLong), importpkg.Path)
-	}
-
-	if Debug['E'] != 0 {
-		fmt.Printf("import type %v %v\n", pt, Tconv(t, FmtLong))
+		fmt.Printf("import var %v %L\n", s, t)
 	}
 }
 
@@ -593,7 +360,7 @@ func dumpasmhdr() {
 		}
 		switch n.Op {
 		case OLITERAL:
-			fmt.Fprintf(b, "#define const_%s %v\n", n.Sym.Name, vconv(n.Val(), FmtSharp))
+			fmt.Fprintf(b, "#define const_%s %#v\n", n.Sym.Name, n.Val())
 
 		case OTYPE:
 			t := n.Type
diff --git a/src/cmd/compile/internal/gc/fixedbugs_test.go b/src/cmd/compile/internal/gc/fixedbugs_test.go
index 19b1d9a..095b816 100644
--- a/src/cmd/compile/internal/gc/fixedbugs_test.go
+++ b/src/cmd/compile/internal/gc/fixedbugs_test.go
@@ -18,7 +18,7 @@ func makeT() T {
 
 var g T
 
-var sink []byte
+var sink interface{}
 
 func TestIssue15854(t *testing.T) {
 	for i := 0; i < 10000; i++ {
diff --git a/src/cmd/compile/internal/gc/float_test.go b/src/cmd/compile/internal/gc/float_test.go
index c761e96..4fdcc7e 100644
--- a/src/cmd/compile/internal/gc/float_test.go
+++ b/src/cmd/compile/internal/gc/float_test.go
@@ -74,6 +74,27 @@ func cvt8(a float32) int32 {
 	return int32(a)
 }
 
+// make sure to cover int, uint cases (issue #16738)
+//go:noinline
+func cvt9(a float64) int {
+	return int(a)
+}
+
+//go:noinline
+func cvt10(a float64) uint {
+	return uint(a)
+}
+
+//go:noinline
+func cvt11(a float32) int {
+	return int(a)
+}
+
+//go:noinline
+func cvt12(a float32) uint {
+	return uint(a)
+}
+
 func TestFloatConvert(t *testing.T) {
 	if got := cvt1(3.5); got != 3 {
 		t.Errorf("cvt1 got %d, wanted 3", got)
@@ -99,4 +120,16 @@ func TestFloatConvert(t *testing.T) {
 	if got := cvt8(3.5); got != 3 {
 		t.Errorf("cvt8 got %d, wanted 3", got)
 	}
+	if got := cvt9(3.5); got != 3 {
+		t.Errorf("cvt9 got %d, wanted 3", got)
+	}
+	if got := cvt10(3.5); got != 3 {
+		t.Errorf("cvt10 got %d, wanted 3", got)
+	}
+	if got := cvt11(3.5); got != 3 {
+		t.Errorf("cvt11 got %d, wanted 3", got)
+	}
+	if got := cvt12(3.5); got != 3 {
+		t.Errorf("cvt12 got %d, wanted 3", got)
+	}
 }
diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
index 3d26a1d..fffce44 100644
--- a/src/cmd/compile/internal/gc/fmt.go
+++ b/src/cmd/compile/internal/gc/fmt.go
@@ -5,8 +5,6 @@
 package gc
 
 import (
-	"bytes"
-	"cmd/internal/obj"
 	"fmt"
 	"strconv"
 	"strings"
@@ -18,120 +16,138 @@ import (
 // See the respective function's documentation for details.
 type FmtFlag int
 
-const (
-	FmtWidth    FmtFlag = 1 << iota
-	FmtLeft             // "-"
-	FmtSharp            // "#"
-	FmtSign             // "+"
-	FmtUnsigned         // "u"
-	FmtShort            // "h"
-	FmtLong             // "l"
-	FmtComma            // ","
-	FmtByte             // "hh"
-	FmtBody             // for printing export bodies
+// TODO(gri) The ' ' flag is not used anymore in %-formats.
+//           Eliminate eventually.
+
+const ( //                                 fmt.Format flag/prec or verb
+	FmtLeft     FmtFlag = 1 << iota // '-'
+	FmtSharp                        // '#'
+	FmtSign                         // '+'
+	FmtUnsigned                     // ' '               (historic: u flag)
+	FmtShort                        // verb == 'S'       (historic: h flag)
+	FmtLong                         // verb == 'L'       (historic: l flag)
+	FmtComma                        // '.' (== hasPrec)  (historic: , flag)
+	FmtByte                         // '0'               (historic: hh flag)
 )
 
+// fmtFlag computes the (internal) FmtFlag
+// value given the fmt.State and format verb.
+func fmtFlag(s fmt.State, verb rune) FmtFlag {
+	var flag FmtFlag
+	if s.Flag('-') {
+		flag |= FmtLeft
+	}
+	if s.Flag('#') {
+		flag |= FmtSharp
+	}
+	if s.Flag('+') {
+		flag |= FmtSign
+	}
+	if s.Flag(' ') {
+		flag |= FmtUnsigned
+	}
+	if _, ok := s.Precision(); ok {
+		flag |= FmtComma
+	}
+	if s.Flag('0') {
+		flag |= FmtByte
+	}
+	switch verb {
+	case 'S':
+		flag |= FmtShort
+	case 'L':
+		flag |= FmtLong
+	}
+	return flag
+}
+
+// Format conversions:
+// TODO(gri) verify these; eliminate those not used anymore
 //
-// Format conversions
-//	%L int		Line numbers
-//
-//	%E int		etype values (aka 'Kind')
-//
-//	%O int		Node Opcodes
-//		Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg)
-//
-//	%J Node*	Node details
-//		Flags: "%hJ" suppresses things not relevant until walk.
-//
-//	%V Val*		Constant values
+//	%v Op		Node opcodes
+//		Flags:  #: print Go syntax (automatic unless fmtmode == FDbg)
 //
-//	%S Sym*		Symbols
-//		Flags: +,- #: mode (see below)
-//			"%hS"	unqualified identifier in any mode
-//			"%hhS"  in export mode: unqualified identifier if exported, qualified if not
+//	%j *Node	Node details
+//		Flags:  0: suppresses things not relevant until walk
 //
-//	%T Type*	Types
-//		Flags: +,- #: mode (see below)
-//			'l' definition instead of name.
-//			'h' omit "func" and receiver in function types
-//			'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix.
+//	%v *Val		Constant values
 //
-//	%N Node*	Nodes
-//		Flags: +,- #: mode (see below)
-//			'h' (only in +/debug mode) suppress recursion
-//			'l' (only in Error mode) print "foo (type Bar)"
+//	%v *Sym		Symbols
+//	%S              unqualified identifier in any mode
+//		Flags:  +,- #: mode (see below)
+//			0: in export mode: unqualified identifier if exported, qualified if not
 //
-//	%H Nodes	Nodes
-//		Flags: those of %N
-//			','  separate items with ',' instead of ';'
+//	%v *Type	Types
+//	%S              omit "func" and receiver in function types
+//	%L              definition instead of name.
+//		Flags:  +,- #: mode (see below)
+//			' ' (only in -/Sym mode) print type identifiers wit package name instead of prefix.
 //
-//   In mparith2.go and mparith3.go:
-//		%B Mpint*	Big integers
-//		%F Mpflt*	Big floats
+//	%v *Node	Nodes
+//	%S              (only in +/debug mode) suppress recursion
+//	%L              (only in Error mode) print "foo (type Bar)"
+//		Flags:  +,- #: mode (see below)
 //
-//   %S, %T and %N obey use the following flags to set the format mode:
+//	%v Nodes	Node lists
+//		Flags:  those of *Node
+//			.: separate items with ',' instead of ';'
+
+// *Sym, *Type, and *Node types use the flags below to set the format mode
 const (
 	FErr = iota
 	FDbg
-	FExp
 	FTypeId
 )
 
 var fmtmode int = FErr
 
-var fmtpkgpfx int // %uT stickyness
+var fmtpkgpfx int // "% v" stickyness for *Type objects
 
-var fmtbody bool
-
-//
-// E.g. for %S:	%+S %#S %-S	print an identifier properly qualified for debug/export/internal mode.
+// The mode flags '+', '-', and '#' are sticky; they persist through
+// recursions of *Node, *Type, and *Sym values. The ' ' flag is
+// sticky only on *Type recursions and only used in %-/*Sym mode.
 //
-// The mode flags  +, - and # are sticky, meaning they persist through
-// recursions of %N, %T and %S, but not the h and l flags. The u flag is
-// sticky only on %T recursions and only used in %-/Sym mode.
+// Example: given a *Sym: %+v %#v %-v print an identifier properly qualified for debug/export/internal mode
 
-//
 // Useful format combinations:
+// TODO(gri): verify these
 //
-//	%+N   %+H	multiline recursive debug dump of node/nodelist
-//	%+hN  %+hH	non recursive debug dump
-//
-//	%#N   %#T	export format
-//	%#lT		type definition instead of name
-//	%#hT		omit"func" and receiver in function signature
+// *Node, Nodes:
+//   %+v    multiline recursive debug dump of *Node/Nodes
+//   %+S    non-recursive debug dump
 //
-//	%lN		"foo (type Bar)" for error messages
+// *Node:
+//   %#v    Go format
+//   %L     "foo (type Bar)" for error messages
 //
-//	%-T		type identifiers
-//	%-hT		type identifiers without "func" and arg names in type signatures (methodsym)
-//	%-uT		type identifiers with package name instead of prefix (typesym, dcommontype, typehash)
+// *Type:
+//   %#v    Go format
+//   %#L    type definition instead of name
+//   %#S    omit"func" and receiver in function signature
 //
+//   %-v    type identifiers
+//   %-S    type identifiers without "func" and arg names in type signatures (methodsym)
+//   %- v   type identifiers with package name instead of prefix (typesym, dcommontype, typehash)
 
-func setfmode(flags *FmtFlag) (fm int, fb bool) {
+func setfmode(flags *FmtFlag) (fm int) {
 	fm = fmtmode
-	fb = fmtbody
 	if *flags&FmtSign != 0 {
 		fmtmode = FDbg
 	} else if *flags&FmtSharp != 0 {
-		fmtmode = FExp
+		// ignore (textual export format no longer supported)
 	} else if *flags&FmtLeft != 0 {
 		fmtmode = FTypeId
 	}
 
-	if *flags&FmtBody != 0 {
-		fmtbody = true
-	}
-
-	*flags &^= (FmtSharp | FmtLeft | FmtSign | FmtBody)
+	*flags &^= (FmtSharp | FmtLeft | FmtSign)
 	return
 }
 
-// Fmt "%L": Linenumbers
-
 var goopnames = []string{
 	OADDR:     "&",
 	OADD:      "+",
 	OADDSTR:   "+",
+	OALIGNOF:  "unsafe.Alignof",
 	OANDAND:   "&&",
 	OANDNOT:   "&^",
 	OAND:      "&",
@@ -172,6 +188,7 @@ var goopnames = []string{
 	ONEW:      "new",
 	ONE:       "!=",
 	ONOT:      "!",
+	OOFFSETOF: "unsafe.Offsetof",
 	OOROR:     "||",
 	OOR:       "|",
 	OPANIC:    "panic",
@@ -186,6 +203,7 @@ var goopnames = []string{
 	ORSH:      ">>",
 	OSELECT:   "select",
 	OSEND:     "<-",
+	OSIZEOF:   "unsafe.Sizeof",
 	OSUB:      "-",
 	OSWITCH:   "switch",
 	OXOR:      "^",
@@ -193,25 +211,37 @@ var goopnames = []string{
 }
 
 func (o Op) String() string {
-	return oconv(o, 0)
+	return fmt.Sprint(o)
 }
 
 func (o Op) GoString() string {
-	return oconv(o, FmtSharp)
+	return fmt.Sprintf("%#v", o)
 }
 
-func oconv(o Op, flag FmtFlag) string {
+func (o Op) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 'v':
+		o.oconv(s, fmtFlag(s, verb))
+
+	default:
+		fmt.Fprintf(s, "%%!%c(Op=%d)", verb, int(o))
+	}
+}
+
+func (o Op) oconv(s fmt.State, flag FmtFlag) {
 	if (flag&FmtSharp != 0) || fmtmode != FDbg {
 		if o >= 0 && int(o) < len(goopnames) && goopnames[o] != "" {
-			return goopnames[o]
+			fmt.Fprint(s, goopnames[o])
+			return
 		}
 	}
 
 	if o >= 0 && int(o) < len(opnames) && opnames[o] != "" {
-		return opnames[o]
+		fmt.Fprint(s, opnames[o])
+		return
 	}
 
-	return fmt.Sprintf("O-%d", o)
+	fmt.Fprintf(s, "O-%d", int(o))
 }
 
 var classnames = []string{
@@ -224,49 +254,60 @@ var classnames = []string{
 	"PFUNC",
 }
 
-// Fmt "%J": Node details.
-func jconv(n *Node, flag FmtFlag) string {
-	var buf bytes.Buffer
+func (n *Node) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 'v', 'S', 'L':
+		n.Nconv(s, fmtFlag(s, verb))
+
+	case 'j':
+		n.jconv(s, fmtFlag(s, verb))
+
+	default:
+		fmt.Fprintf(s, "%%!%c(*Node=%p)", verb, n)
+	}
+}
 
+// *Node details
+func (n *Node) jconv(s fmt.State, flag FmtFlag) {
 	c := flag & FmtShort
 
 	if c == 0 && n.Ullman != 0 {
-		fmt.Fprintf(&buf, " u(%d)", n.Ullman)
+		fmt.Fprintf(s, " u(%d)", n.Ullman)
 	}
 
 	if c == 0 && n.Addable {
-		fmt.Fprintf(&buf, " a(%v)", n.Addable)
+		fmt.Fprintf(s, " a(%v)", n.Addable)
 	}
 
 	if c == 0 && n.Name != nil && n.Name.Vargen != 0 {
-		fmt.Fprintf(&buf, " g(%d)", n.Name.Vargen)
+		fmt.Fprintf(s, " g(%d)", n.Name.Vargen)
 	}
 
 	if n.Lineno != 0 {
-		fmt.Fprintf(&buf, " l(%d)", n.Lineno)
+		fmt.Fprintf(s, " l(%d)", n.Lineno)
 	}
 
 	if c == 0 && n.Xoffset != BADWIDTH {
-		fmt.Fprintf(&buf, " x(%d%+d)", n.Xoffset, stkdelta[n])
+		fmt.Fprintf(s, " x(%d)", n.Xoffset)
 	}
 
 	if n.Class != 0 {
 		if int(n.Class) < len(classnames) {
-			fmt.Fprintf(&buf, " class(%s)", classnames[n.Class])
+			fmt.Fprintf(s, " class(%s)", classnames[n.Class])
 		} else {
-			fmt.Fprintf(&buf, " class(%d?)", n.Class)
+			fmt.Fprintf(s, " class(%d?)", n.Class)
 		}
 	}
 
 	if n.Colas {
-		fmt.Fprintf(&buf, " colas(%v)", n.Colas)
+		fmt.Fprintf(s, " colas(%v)", n.Colas)
 	}
 
 	if n.Name != nil && n.Name.Funcdepth != 0 {
-		fmt.Fprintf(&buf, " f(%d)", n.Name.Funcdepth)
+		fmt.Fprintf(s, " f(%d)", n.Name.Funcdepth)
 	}
 	if n.Func != nil && n.Func.Depth != 0 {
-		fmt.Fprintf(&buf, " ff(%d)", n.Func.Depth)
+		fmt.Fprintf(s, " ff(%d)", n.Func.Depth)
 	}
 
 	switch n.Esc {
@@ -274,125 +315,141 @@ func jconv(n *Node, flag FmtFlag) string {
 		break
 
 	case EscHeap:
-		buf.WriteString(" esc(h)")
-
-	case EscScope:
-		buf.WriteString(" esc(s)")
+		fmt.Fprint(s, " esc(h)")
 
 	case EscNone:
-		buf.WriteString(" esc(no)")
+		fmt.Fprint(s, " esc(no)")
 
 	case EscNever:
 		if c == 0 {
-			buf.WriteString(" esc(N)")
+			fmt.Fprint(s, " esc(N)")
 		}
 
 	default:
-		fmt.Fprintf(&buf, " esc(%d)", n.Esc)
+		fmt.Fprintf(s, " esc(%d)", n.Esc)
 	}
 
-	if e, ok := n.Opt().(*NodeEscState); ok && e.Escloopdepth != 0 {
-		fmt.Fprintf(&buf, " ld(%d)", e.Escloopdepth)
+	if e, ok := n.Opt().(*NodeEscState); ok && e.Loopdepth != 0 {
+		fmt.Fprintf(s, " ld(%d)", e.Loopdepth)
 	}
 
 	if c == 0 && n.Typecheck != 0 {
-		fmt.Fprintf(&buf, " tc(%d)", n.Typecheck)
+		fmt.Fprintf(s, " tc(%d)", n.Typecheck)
 	}
 
-	if c == 0 && n.Dodata != 0 {
-		fmt.Fprintf(&buf, " dd(%d)", n.Dodata)
+	if c == 0 && n.IsStatic {
+		fmt.Fprint(s, " static")
 	}
 
 	if n.Isddd {
-		fmt.Fprintf(&buf, " isddd(%v)", n.Isddd)
+		fmt.Fprintf(s, " isddd(%v)", n.Isddd)
 	}
 
 	if n.Implicit {
-		fmt.Fprintf(&buf, " implicit(%v)", n.Implicit)
+		fmt.Fprintf(s, " implicit(%v)", n.Implicit)
 	}
 
 	if n.Embedded != 0 {
-		fmt.Fprintf(&buf, " embedded(%d)", n.Embedded)
+		fmt.Fprintf(s, " embedded(%d)", n.Embedded)
 	}
 
 	if n.Addrtaken {
-		buf.WriteString(" addrtaken")
+		fmt.Fprint(s, " addrtaken")
 	}
 
 	if n.Assigned {
-		buf.WriteString(" assigned")
+		fmt.Fprint(s, " assigned")
 	}
 	if n.Bounded {
-		buf.WriteString(" bounded")
+		fmt.Fprint(s, " bounded")
 	}
 	if n.NonNil {
-		buf.WriteString(" nonnil")
+		fmt.Fprint(s, " nonnil")
 	}
 
 	if c == 0 && n.Used {
-		fmt.Fprintf(&buf, " used(%v)", n.Used)
+		fmt.Fprintf(s, " used(%v)", n.Used)
+	}
+}
+
+func (v Val) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 'v':
+		v.vconv(s, fmtFlag(s, verb))
+
+	default:
+		fmt.Fprintf(s, "%%!%c(Val=%T)", verb, v)
 	}
-	return buf.String()
 }
 
-// Fmt "%V": Values
-func vconv(v Val, flag FmtFlag) string {
+func (v Val) vconv(s fmt.State, flag FmtFlag) {
 	switch u := v.U.(type) {
 	case *Mpint:
 		if !u.Rune {
-			if (flag&FmtSharp != 0) || fmtmode == FExp {
-				return bconv(u, FmtSharp)
+			if flag&FmtSharp != 0 {
+				fmt.Fprint(s, bconv(u, FmtSharp))
+				return
 			}
-			return bconv(u, 0)
+			fmt.Fprint(s, bconv(u, 0))
+			return
 		}
 
-		x := u.Int64()
-		if ' ' <= x && x < utf8.RuneSelf && x != '\\' && x != '\'' {
-			return fmt.Sprintf("'%c'", int(x))
-		}
-		if 0 <= x && x < 1<<16 {
-			return fmt.Sprintf("'\\u%04x'", uint(int(x)))
-		}
-		if 0 <= x && x <= utf8.MaxRune {
-			return fmt.Sprintf("'\\U%08x'", uint64(x))
+		switch x := u.Int64(); {
+		case ' ' <= x && x < utf8.RuneSelf && x != '\\' && x != '\'':
+			fmt.Fprintf(s, "'%c'", int(x))
+
+		case 0 <= x && x < 1<<16:
+			fmt.Fprintf(s, "'\\u%04x'", uint(int(x)))
+
+		case 0 <= x && x <= utf8.MaxRune:
+			fmt.Fprintf(s, "'\\U%08x'", uint64(x))
+
+		default:
+			fmt.Fprintf(s, "('\\x00' + %v)", u)
 		}
-		return fmt.Sprintf("('\\x00' + %v)", u)
 
 	case *Mpflt:
-		if (flag&FmtSharp != 0) || fmtmode == FExp {
-			return fconv(u, 0)
+		if flag&FmtSharp != 0 {
+			fmt.Fprint(s, fconv(u, 0))
+			return
 		}
-		return fconv(u, FmtSharp)
+		fmt.Fprint(s, fconv(u, FmtSharp))
+		return
 
 	case *Mpcplx:
-		if (flag&FmtSharp != 0) || fmtmode == FExp {
-			return fmt.Sprintf("(%v+%vi)", &u.Real, &u.Imag)
-		}
-		if v.U.(*Mpcplx).Real.CmpFloat64(0) == 0 {
-			return fmt.Sprintf("%vi", fconv(&u.Imag, FmtSharp))
-		}
-		if v.U.(*Mpcplx).Imag.CmpFloat64(0) == 0 {
-			return fconv(&u.Real, FmtSharp)
-		}
-		if v.U.(*Mpcplx).Imag.CmpFloat64(0) < 0 {
-			return fmt.Sprintf("(%v%vi)", fconv(&u.Real, FmtSharp), fconv(&u.Imag, FmtSharp))
+		switch {
+		case flag&FmtSharp != 0:
+			fmt.Fprintf(s, "(%v+%vi)", &u.Real, &u.Imag)
+
+		case v.U.(*Mpcplx).Real.CmpFloat64(0) == 0:
+			fmt.Fprintf(s, "%vi", fconv(&u.Imag, FmtSharp))
+
+		case v.U.(*Mpcplx).Imag.CmpFloat64(0) == 0:
+			fmt.Fprint(s, fconv(&u.Real, FmtSharp))
+
+		case v.U.(*Mpcplx).Imag.CmpFloat64(0) < 0:
+			fmt.Fprintf(s, "(%v%vi)", fconv(&u.Real, FmtSharp), fconv(&u.Imag, FmtSharp))
+
+		default:
+			fmt.Fprintf(s, "(%v+%vi)", fconv(&u.Real, FmtSharp), fconv(&u.Imag, FmtSharp))
 		}
-		return fmt.Sprintf("(%v+%vi)", fconv(&u.Real, FmtSharp), fconv(&u.Imag, FmtSharp))
 
 	case string:
-		return strconv.Quote(u)
+		fmt.Fprint(s, strconv.Quote(u))
 
 	case bool:
+		t := "false"
 		if u {
-			return "true"
+			t = "true"
 		}
-		return "false"
+		fmt.Fprint(s, t)
 
 	case *NilVal:
-		return "nil"
-	}
+		fmt.Fprint(s, "nil")
 
-	return fmt.Sprintf("<ctype=%d>", v.Ctype())
+	default:
+		fmt.Fprintf(s, "<ctype=%d>", v.Ctype())
+	}
 }
 
 /*
@@ -451,8 +508,7 @@ func (et EType) String() string {
 	return fmt.Sprintf("E-%d", et)
 }
 
-// Fmt "%S": syms
-func symfmt(s *Sym, flag FmtFlag) string {
+func (s *Sym) symfmt(flag FmtFlag) string {
 	if s.Pkg != nil && flag&FmtShort == 0 {
 		switch fmtmode {
 		case FErr: // This is for the user
@@ -474,31 +530,22 @@ func symfmt(s *Sym, flag FmtFlag) string {
 				return s.Pkg.Name + "." + s.Name // dcommontype, typehash
 			}
 			return s.Pkg.Prefix + "." + s.Name // (methodsym), typesym, weaksym
-
-		case FExp:
-			if s.Name != "" && s.Name[0] == '.' {
-				Fatalf("exporting synthetic symbol %s", s.Name)
-			}
-			if s.Pkg != builtinpkg {
-				return fmt.Sprintf("@%q.%s", s.Pkg.Path, s.Name)
-			}
 		}
 	}
 
 	if flag&FmtByte != 0 {
 		// FmtByte (hh) implies FmtShort (h)
 		// skip leading "type." in method name
-		p := s.Name
-		if i := strings.LastIndex(s.Name, "."); i >= 0 {
-			p = s.Name[i+1:]
+		name := s.Name
+		if i := strings.LastIndex(name, "."); i >= 0 {
+			name = name[i+1:]
 		}
 
-		// exportname needs to see the name without the prefix too.
-		if (fmtmode == FExp && !exportname(p)) || fmtmode == FDbg {
-			return fmt.Sprintf("@%q.%s", s.Pkg.Path, p)
+		if fmtmode == FDbg {
+			return fmt.Sprintf("@%q.%s", s.Pkg.Path, name)
 		}
 
-		return p
+		return name
 	}
 
 	return s.Name
@@ -528,7 +575,7 @@ var basicnames = []string{
 	TBLANK:      "blank",
 }
 
-func typefmt(t *Type, flag FmtFlag) string {
+func (t *Type) typefmt(flag FmtFlag) string {
 	if t == nil {
 		return "<T>"
 	}
@@ -536,7 +583,7 @@ func typefmt(t *Type, flag FmtFlag) string {
 	if t == bytetype || t == runetype {
 		// in %-T mode collapse rune and byte with their originals.
 		if fmtmode != FTypeId {
-			return sconv(t.Sym, FmtShort)
+			return t.Sym.sconv(FmtShort)
 		}
 		t = Types[t.Etype]
 	}
@@ -551,23 +598,21 @@ func typefmt(t *Type, flag FmtFlag) string {
 		case FTypeId:
 			if flag&FmtShort != 0 {
 				if t.Vargen != 0 {
-					return fmt.Sprintf("%v·%d", sconv(t.Sym, FmtShort), t.Vargen)
+					return fmt.Sprintf("%v·%d", t.Sym.sconv(FmtShort), t.Vargen)
 				}
-				return sconv(t.Sym, FmtShort)
+				return t.Sym.sconv(FmtShort)
 			}
 
 			if flag&FmtUnsigned != 0 {
-				return sconv(t.Sym, FmtUnsigned)
+				return t.Sym.sconv(FmtUnsigned)
 			}
-			fallthrough
 
-		case FExp:
 			if t.Sym.Pkg == localpkg && t.Vargen != 0 {
 				return fmt.Sprintf("%v·%d", t.Sym, t.Vargen)
 			}
 		}
 
-		return sconv(t.Sym, 0)
+		return t.Sym.String()
 	}
 
 	if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" {
@@ -580,7 +625,7 @@ func typefmt(t *Type, flag FmtFlag) string {
 
 	if fmtmode == FDbg {
 		fmtmode = 0
-		str := t.Etype.String() + "-" + typefmt(t, flag)
+		str := t.Etype.String() + "-" + t.typefmt(flag)
 		fmtmode = FDbg
 		return str
 	}
@@ -588,7 +633,7 @@ func typefmt(t *Type, flag FmtFlag) string {
 	switch t.Etype {
 	case TPTR32, TPTR64:
 		if fmtmode == FTypeId && (flag&FmtShort != 0) {
-			return "*" + Tconv(t.Elem(), FmtShort)
+			return "*" + t.Elem().tconv(FmtShort)
 		}
 		return "*" + t.Elem().String()
 
@@ -619,62 +664,61 @@ func typefmt(t *Type, flag FmtFlag) string {
 		return "map[" + t.Key().String() + "]" + t.Val().String()
 
 	case TINTER:
-		var buf bytes.Buffer
-		buf.WriteString("interface {")
+		if t.IsEmptyInterface() {
+			return "interface {}"
+		}
+		buf := make([]byte, 0, 64)
+		buf = append(buf, "interface {"...)
 		for i, f := range t.Fields().Slice() {
 			if i != 0 {
-				buf.WriteString(";")
+				buf = append(buf, ';')
 			}
-			buf.WriteString(" ")
+			buf = append(buf, ' ')
 			switch {
 			case f.Sym == nil:
 				// Check first that a symbol is defined for this type.
 				// Wrong interface definitions may have types lacking a symbol.
 				break
 			case exportname(f.Sym.Name):
-				buf.WriteString(sconv(f.Sym, FmtShort))
+				buf = append(buf, f.Sym.sconv(FmtShort)...)
 			default:
-				buf.WriteString(sconv(f.Sym, FmtUnsigned))
+				buf = append(buf, f.Sym.sconv(FmtUnsigned)...)
 			}
-			buf.WriteString(Tconv(f.Type, FmtShort))
+			buf = append(buf, f.Type.tconv(FmtShort)...)
 		}
 		if t.NumFields() != 0 {
-			buf.WriteString(" ")
+			buf = append(buf, ' ')
 		}
-		buf.WriteString("}")
-		return buf.String()
+		buf = append(buf, '}')
+		return string(buf)
 
 	case TFUNC:
-		var buf bytes.Buffer
+		buf := make([]byte, 0, 64)
 		if flag&FmtShort != 0 {
 			// no leading func
 		} else {
 			if t.Recv() != nil {
-				buf.WriteString("method")
-				buf.WriteString(Tconv(t.Recvs(), 0))
-				buf.WriteString(" ")
+				buf = append(buf, "method"...)
+				buf = append(buf, t.Recvs().String()...)
+				buf = append(buf, ' ')
 			}
-			buf.WriteString("func")
+			buf = append(buf, "func"...)
 		}
-		buf.WriteString(Tconv(t.Params(), 0))
+		buf = append(buf, t.Params().String()...)
 
 		switch t.Results().NumFields() {
 		case 0:
-			break
+			// nothing to do
 
 		case 1:
-			if fmtmode != FExp {
-				buf.WriteString(" ")
-				buf.WriteString(Tconv(t.Results().Field(0).Type, 0)) // struct->field->field's type
-				break
-			}
-			fallthrough
+			buf = append(buf, ' ')
+			buf = append(buf, t.Results().Field(0).Type.String()...) // struct->field->field's type
 
 		default:
-			buf.WriteString(" ")
-			buf.WriteString(Tconv(t.Results(), 0))
+			buf = append(buf, ' ')
+			buf = append(buf, t.Results().String()...)
 		}
-		return buf.String()
+		return string(buf)
 
 	case TSTRUCT:
 		if m := t.StructType().Map; m != nil {
@@ -693,38 +737,38 @@ func typefmt(t *Type, flag FmtFlag) string {
 				return "map.iter[" + m.Key().String() + "]" + m.Val().String()
 			}
 
-			Yyerror("unknown internal map type")
+			yyerror("unknown internal map type")
 		}
 
-		var buf bytes.Buffer
+		buf := make([]byte, 0, 64)
 		if t.IsFuncArgStruct() {
-			buf.WriteString("(")
+			buf = append(buf, '(')
 			var flag1 FmtFlag
 			if fmtmode == FTypeId || fmtmode == FErr { // no argument names on function signature, and no "noescape"/"nosplit" tags
 				flag1 = FmtShort
 			}
 			for i, f := range t.Fields().Slice() {
 				if i != 0 {
-					buf.WriteString(", ")
+					buf = append(buf, ", "...)
 				}
-				buf.WriteString(Fldconv(f, flag1))
+				buf = append(buf, fldconv(f, flag1)...)
 			}
-			buf.WriteString(")")
+			buf = append(buf, ')')
 		} else {
-			buf.WriteString("struct {")
+			buf = append(buf, "struct {"...)
 			for i, f := range t.Fields().Slice() {
 				if i != 0 {
-					buf.WriteString(";")
+					buf = append(buf, ';')
 				}
-				buf.WriteString(" ")
-				buf.WriteString(Fldconv(f, FmtLong))
+				buf = append(buf, ' ')
+				buf = append(buf, fldconv(f, FmtLong)...)
 			}
 			if t.NumFields() != 0 {
-				buf.WriteString(" ")
+				buf = append(buf, ' ')
 			}
-			buf.WriteString("}")
+			buf = append(buf, '}')
 		}
-		return buf.String()
+		return string(buf)
 
 	case TFORW:
 		if t.Sym != nil {
@@ -733,25 +777,15 @@ func typefmt(t *Type, flag FmtFlag) string {
 		return "undefined"
 
 	case TUNSAFEPTR:
-		if fmtmode == FExp {
-			return "@\"unsafe\".Pointer"
-		}
 		return "unsafe.Pointer"
 
 	case TDDDFIELD:
-		if fmtmode == FExp {
-			Fatalf("cannot use TDDDFIELD with old exporter")
-		}
 		return fmt.Sprintf("%v <%v> %v", t.Etype, t.Sym, t.DDDField())
 
 	case Txxx:
 		return "Txxx"
 	}
 
-	if fmtmode == FExp {
-		Fatalf("missing %v case during export", t.Etype)
-	}
-
 	// Don't know how to handle - fall back to detailed prints.
 	return fmt.Sprintf("%v <%v> %v", t.Etype, t.Sym, t.Elem())
 }
@@ -766,9 +800,7 @@ func stmtwithinit(op Op) bool {
 	return false
 }
 
-func stmtfmt(n *Node) string {
-	var f string
-
+func (n *Node) stmtfmt(s fmt.State) {
 	// some statements allow for an init, but at most one,
 	// but we may have an arbitrary number added, eg by typecheck
 	// and inlining. If it doesn't fit the syntax, emit an enclosing
@@ -784,150 +816,155 @@ func stmtfmt(n *Node) string {
 	extrablock := complexinit && stmtwithinit(n.Op)
 
 	if extrablock {
-		f += "{"
+		fmt.Fprint(s, "{")
 	}
 
 	if complexinit {
-		f += fmt.Sprintf(" %v; ", n.Ninit)
+		fmt.Fprintf(s, " %v; ", n.Ninit)
 	}
 
 	switch n.Op {
 	case ODCL:
-		if fmtmode == FExp {
-			switch n.Left.Class {
-			case PPARAM, PPARAMOUT, PAUTO, PAUTOHEAP:
-				f += fmt.Sprintf("var %v %v", n.Left, n.Left.Type)
-				goto ret
-			}
-		}
-
-		f += fmt.Sprintf("var %v %v", n.Left.Sym, n.Left.Type)
+		fmt.Fprintf(s, "var %v %v", n.Left.Sym, n.Left.Type)
 
 	case ODCLFIELD:
 		if n.Left != nil {
-			f += fmt.Sprintf("%v %v", n.Left, n.Right)
+			fmt.Fprintf(s, "%v %v", n.Left, n.Right)
 		} else {
-			f += Nconv(n.Right, 0)
+			fmt.Fprintf(s, "%v", n.Right)
 		}
 
 	// Don't export "v = <N>" initializing statements, hope they're always
 	// preceded by the DCL which will be re-parsed and typechecked to reproduce
 	// the "v = <N>" again.
 	case OAS, OASWB:
-		if fmtmode == FExp && n.Right == nil {
-			break
-		}
-
 		if n.Colas && !complexinit {
-			f += fmt.Sprintf("%v := %v", n.Left, n.Right)
+			fmt.Fprintf(s, "%v := %v", n.Left, n.Right)
 		} else {
-			f += fmt.Sprintf("%v = %v", n.Left, n.Right)
+			fmt.Fprintf(s, "%v = %v", n.Left, n.Right)
 		}
 
 	case OASOP:
 		if n.Implicit {
 			if Op(n.Etype) == OADD {
-				f += fmt.Sprintf("%v++", n.Left)
+				fmt.Fprintf(s, "%v++", n.Left)
 			} else {
-				f += fmt.Sprintf("%v--", n.Left)
+				fmt.Fprintf(s, "%v--", n.Left)
 			}
 			break
 		}
 
-		f += fmt.Sprintf("%v %#v= %v", n.Left, Op(n.Etype), n.Right)
+		fmt.Fprintf(s, "%v %#v= %v", n.Left, Op(n.Etype), n.Right)
 
 	case OAS2:
 		if n.Colas && !complexinit {
-			f += fmt.Sprintf("%v := %v", hconv(n.List, FmtComma), hconv(n.Rlist, FmtComma))
+			fmt.Fprintf(s, "%.v := %.v", n.List, n.Rlist)
 			break
 		}
 		fallthrough
 
 	case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
-		f += fmt.Sprintf("%v = %v", hconv(n.List, FmtComma), hconv(n.Rlist, FmtComma))
+		fmt.Fprintf(s, "%.v = %.v", n.List, n.Rlist)
 
 	case ORETURN:
-		f += fmt.Sprintf("return %v", hconv(n.List, FmtComma))
+		fmt.Fprintf(s, "return %.v", n.List)
 
 	case ORETJMP:
-		f += fmt.Sprintf("retjmp %v", n.Sym)
+		fmt.Fprintf(s, "retjmp %v", n.Sym)
 
 	case OPROC:
-		f += fmt.Sprintf("go %v", n.Left)
+		fmt.Fprintf(s, "go %v", n.Left)
 
 	case ODEFER:
-		f += fmt.Sprintf("defer %v", n.Left)
+		fmt.Fprintf(s, "defer %v", n.Left)
 
 	case OIF:
 		if simpleinit {
-			f += fmt.Sprintf("if %v; %v { %v }", n.Ninit.First(), n.Left, n.Nbody)
+			fmt.Fprintf(s, "if %v; %v { %v }", n.Ninit.First(), n.Left, n.Nbody)
 		} else {
-			f += fmt.Sprintf("if %v { %v }", n.Left, n.Nbody)
+			fmt.Fprintf(s, "if %v { %v }", n.Left, n.Nbody)
 		}
 		if n.Rlist.Len() != 0 {
-			f += fmt.Sprintf(" else { %v }", n.Rlist)
+			fmt.Fprintf(s, " else { %v }", n.Rlist)
 		}
 
 	case OFOR:
 		if fmtmode == FErr { // TODO maybe only if FmtShort, same below
-			f += "for loop"
+			fmt.Fprint(s, "for loop")
 			break
 		}
 
-		f += "for"
+		fmt.Fprint(s, "for")
 		if simpleinit {
-			f += fmt.Sprintf(" %v;", n.Ninit.First())
+			fmt.Fprintf(s, " %v;", n.Ninit.First())
 		} else if n.Right != nil {
-			f += " ;"
+			fmt.Fprint(s, " ;")
 		}
 
 		if n.Left != nil {
-			f += fmt.Sprintf(" %v", n.Left)
+			fmt.Fprintf(s, " %v", n.Left)
 		}
 
 		if n.Right != nil {
-			f += fmt.Sprintf("; %v", n.Right)
+			fmt.Fprintf(s, "; %v", n.Right)
 		} else if simpleinit {
-			f += ";"
+			fmt.Fprint(s, ";")
 		}
 
-		f += fmt.Sprintf(" { %v }", n.Nbody)
+		fmt.Fprintf(s, " { %v }", n.Nbody)
 
 	case ORANGE:
 		if fmtmode == FErr {
-			f += "for loop"
+			fmt.Fprint(s, "for loop")
 			break
 		}
 
 		if n.List.Len() == 0 {
-			f += fmt.Sprintf("for range %v { %v }", n.Right, n.Nbody)
+			fmt.Fprintf(s, "for range %v { %v }", n.Right, n.Nbody)
 			break
 		}
 
-		f += fmt.Sprintf("for %v = range %v { %v }", hconv(n.List, FmtComma), n.Right, n.Nbody)
+		fmt.Fprintf(s, "for %.v = range %v { %v }", n.List, n.Right, n.Nbody)
 
 	case OSELECT, OSWITCH:
 		if fmtmode == FErr {
-			f += fmt.Sprintf("%v statement", n.Op)
+			fmt.Fprintf(s, "%v statement", n.Op)
 			break
 		}
 
-		f += n.Op.GoString() // %#v
+		fmt.Fprint(s, n.Op.GoString()) // %#v
 		if simpleinit {
-			f += fmt.Sprintf(" %v;", n.Ninit.First())
+			fmt.Fprintf(s, " %v;", n.Ninit.First())
 		}
 		if n.Left != nil {
-			f += fmt.Sprintf(" %s ", Nconv(n.Left, 0))
+			fmt.Fprintf(s, " %v ", n.Left)
 		}
 
-		f += fmt.Sprintf(" { %v }", n.List)
+		fmt.Fprintf(s, " { %v }", n.List)
 
-	case OCASE, OXCASE:
+	case OXCASE:
 		if n.List.Len() != 0 {
-			f += fmt.Sprintf("case %v: %v", hconv(n.List, FmtComma), n.Nbody)
+			fmt.Fprintf(s, "case %.v", n.List)
 		} else {
-			f += fmt.Sprintf("default: %v", n.Nbody)
+			fmt.Fprint(s, "default")
+		}
+		fmt.Fprintf(s, ": %v", n.Nbody)
+
+	case OCASE:
+		switch {
+		case n.Left != nil:
+			// single element
+			fmt.Fprintf(s, "case %v", n.Left)
+		case n.List.Len() > 0:
+			// range
+			if n.List.Len() != 2 {
+				Fatalf("bad OCASE list length %d", n.List.Len())
+			}
+			fmt.Fprintf(s, "case %v..%v", n.List.First(), n.List.Second())
+		default:
+			fmt.Fprint(s, "default")
 		}
+		fmt.Fprintf(s, ": %v", n.Nbody)
 
 	case OBREAK,
 		OCONTINUE,
@@ -935,30 +972,29 @@ func stmtfmt(n *Node) string {
 		OFALL,
 		OXFALL:
 		if n.Left != nil {
-			f += fmt.Sprintf("%#v %v", n.Op, n.Left)
+			fmt.Fprintf(s, "%#v %v", n.Op, n.Left)
 		} else {
-			f += n.Op.GoString() // %#v
+			fmt.Fprint(s, n.Op.GoString()) // %#v
 		}
 
 	case OEMPTY:
 		break
 
 	case OLABEL:
-		f += fmt.Sprintf("%v: ", n.Left)
+		fmt.Fprintf(s, "%v: ", n.Left)
 	}
 
-ret:
 	if extrablock {
-		f += "}"
+		fmt.Fprint(s, "}")
 	}
-
-	return f
 }
 
 var opprec = []int{
+	OALIGNOF:      8,
 	OAPPEND:       8,
 	OARRAYBYTESTR: 8,
 	OARRAYLIT:     8,
+	OSLICELIT:     8,
 	OARRAYRUNESTR: 8,
 	OCALLFUNC:     8,
 	OCALLINTER:    8,
@@ -980,12 +1016,14 @@ var opprec = []int{
 	ONAME:         8,
 	ONEW:          8,
 	ONONAME:       8,
+	OOFFSETOF:     8,
 	OPACK:         8,
 	OPANIC:        8,
 	OPAREN:        8,
 	OPRINTN:       8,
 	OPRINT:        8,
 	ORUNESTR:      8,
+	OSIZEOF:       8,
 	OSTRARRAYBYTE: 8,
 	OSTRARRAYRUNE: 8,
 	OSTRUCTLIT:    8,
@@ -1069,13 +1107,14 @@ var opprec = []int{
 	OEND:        0,
 }
 
-func exprfmt(n *Node, prec int) string {
+func (n *Node) exprfmt(s fmt.State, prec int) {
 	for n != nil && n.Implicit && (n.Op == OIND || n.Op == OADDR) {
 		n = n.Left
 	}
 
 	if n == nil {
-		return "<N>"
+		fmt.Fprint(s, "<N>")
+		return
 	}
 
 	nprec := opprec[n.Op]
@@ -1084,254 +1123,206 @@ func exprfmt(n *Node, prec int) string {
 	}
 
 	if prec > nprec {
-		return fmt.Sprintf("(%v)", n)
+		fmt.Fprintf(s, "(%v)", n)
+		return
 	}
 
 	switch n.Op {
 	case OPAREN:
-		return fmt.Sprintf("(%v)", n.Left)
+		fmt.Fprintf(s, "(%v)", n.Left)
 
 	case ODDDARG:
-		return "... argument"
-
-	case OREGISTER:
-		return obj.Rconv(int(n.Reg))
+		fmt.Fprint(s, "... argument")
 
 	case OLITERAL: // this is a bit of a mess
 		if fmtmode == FErr {
 			if n.Orig != nil && n.Orig != n {
-				return exprfmt(n.Orig, prec)
+				n.Orig.exprfmt(s, prec)
+				return
 			}
 			if n.Sym != nil {
-				return sconv(n.Sym, 0)
+				fmt.Fprint(s, n.Sym.String())
+				return
 			}
 		}
 		if n.Val().Ctype() == CTNIL && n.Orig != nil && n.Orig != n {
-			return exprfmt(n.Orig, prec)
+			n.Orig.exprfmt(s, prec)
+			return
 		}
 		if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != idealbool && n.Type != idealstring {
 			// Need parens when type begins with what might
 			// be misinterpreted as a unary operator: * or <-.
 			if n.Type.IsPtr() || (n.Type.IsChan() && n.Type.ChanDir() == Crecv) {
-				return fmt.Sprintf("(%v)(%v)", n.Type, vconv(n.Val(), 0))
+				fmt.Fprintf(s, "(%v)(%v)", n.Type, n.Val())
+				return
 			} else {
-				return fmt.Sprintf("%v(%v)", n.Type, vconv(n.Val(), 0))
+				fmt.Fprintf(s, "%v(%v)", n.Type, n.Val())
+				return
 			}
 		}
 
-		return vconv(n.Val(), 0)
+		fmt.Fprintf(s, "%v", n.Val())
 
 	// Special case: name used as local variable in export.
 	// _ becomes ~b%d internally; print as _ for export
 	case ONAME:
-		if (fmtmode == FExp || fmtmode == FErr) && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' {
-			return "_"
-		}
-		if fmtmode == FExp && n.Sym != nil && !isblank(n) && n.Name.Vargen > 0 {
-			return fmt.Sprintf("%v·%d", n.Sym, n.Name.Vargen)
-		}
-
-		// Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method,
-		// but for export, this should be rendered as (*pkg.T).meth.
-		// These nodes have the special property that they are names with a left OTYPE and a right ONAME.
-		if fmtmode == FExp && n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME {
-			if n.Left.Type.IsPtr() {
-				return fmt.Sprintf("(%v).%v", n.Left.Type, sconv(n.Right.Sym, FmtShort|FmtByte))
-			} else {
-				return fmt.Sprintf("%v.%v", n.Left.Type, sconv(n.Right.Sym, FmtShort|FmtByte))
-			}
+		if fmtmode == FErr && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' {
+			fmt.Fprint(s, "_")
+			return
 		}
 		fallthrough
-
 	case OPACK, ONONAME:
-		return sconv(n.Sym, 0)
+		fmt.Fprint(s, n.Sym.String())
 
 	case OTYPE:
 		if n.Type == nil && n.Sym != nil {
-			return sconv(n.Sym, 0)
+			fmt.Fprint(s, n.Sym.String())
+			return
 		}
-		return Tconv(n.Type, 0)
+		fmt.Fprintf(s, "%v", n.Type)
 
 	case OTARRAY:
 		if n.Left != nil {
-			return fmt.Sprintf("[]%v", n.Left)
+			fmt.Fprintf(s, "[]%v", n.Left)
+			return
 		}
-		return fmt.Sprintf("[]%v", n.Right) // happens before typecheck
+		fmt.Fprintf(s, "[]%v", n.Right) // happens before typecheck
 
 	case OTMAP:
-		return fmt.Sprintf("map[%v]%v", n.Left, n.Right)
+		fmt.Fprintf(s, "map[%v]%v", n.Left, n.Right)
 
 	case OTCHAN:
 		switch ChanDir(n.Etype) {
 		case Crecv:
-			return fmt.Sprintf("<-chan %v", n.Left)
+			fmt.Fprintf(s, "<-chan %v", n.Left)
 
 		case Csend:
-			return fmt.Sprintf("chan<- %v", n.Left)
+			fmt.Fprintf(s, "chan<- %v", n.Left)
 
 		default:
 			if n.Left != nil && n.Left.Op == OTCHAN && n.Left.Sym == nil && ChanDir(n.Left.Etype) == Crecv {
-				return fmt.Sprintf("chan (%v)", n.Left)
+				fmt.Fprintf(s, "chan (%v)", n.Left)
 			} else {
-				return fmt.Sprintf("chan %v", n.Left)
+				fmt.Fprintf(s, "chan %v", n.Left)
 			}
 		}
 
 	case OTSTRUCT:
-		return "<struct>"
+		fmt.Fprint(s, "<struct>")
 
 	case OTINTER:
-		return "<inter>"
+		fmt.Fprint(s, "<inter>")
 
 	case OTFUNC:
-		return "<func>"
+		fmt.Fprint(s, "<func>")
 
 	case OCLOSURE:
 		if fmtmode == FErr {
-			return "func literal"
+			fmt.Fprint(s, "func literal")
+			return
 		}
 		if n.Nbody.Len() != 0 {
-			return fmt.Sprintf("%v { %v }", n.Type, n.Nbody)
+			fmt.Fprintf(s, "%v { %v }", n.Type, n.Nbody)
+			return
 		}
-		return fmt.Sprintf("%v { %v }", n.Type, n.Func.Closure.Nbody)
+		fmt.Fprintf(s, "%v { %v }", n.Type, n.Func.Closure.Nbody)
 
 	case OCOMPLIT:
 		ptrlit := n.Right != nil && n.Right.Implicit && n.Right.Type != nil && n.Right.Type.IsPtr()
 		if fmtmode == FErr {
 			if n.Right != nil && n.Right.Type != nil && !n.Implicit {
 				if ptrlit {
-					return fmt.Sprintf("&%v literal", n.Right.Type.Elem())
+					fmt.Fprintf(s, "&%v literal", n.Right.Type.Elem())
+					return
 				} else {
-					return fmt.Sprintf("%v literal", n.Right.Type)
+					fmt.Fprintf(s, "%v literal", n.Right.Type)
+					return
 				}
 			}
 
-			return "composite literal"
+			fmt.Fprint(s, "composite literal")
+			return
 		}
-
-		if fmtmode == FExp && ptrlit {
-			// typecheck has overwritten OIND by OTYPE with pointer type.
-			return fmt.Sprintf("(&%v{ %v })", n.Right.Type.Elem(), hconv(n.List, FmtComma))
-		}
-
-		return fmt.Sprintf("(%v{ %v })", n.Right, hconv(n.List, FmtComma))
+		fmt.Fprintf(s, "(%v{ %.v })", n.Right, n.List)
 
 	case OPTRLIT:
-		if fmtmode == FExp && n.Left.Implicit {
-			return Nconv(n.Left, 0)
-		}
-		return fmt.Sprintf("&%v", n.Left)
+		fmt.Fprintf(s, "&%v", n.Left)
 
-	case OSTRUCTLIT:
-		if fmtmode == FExp { // requires special handling of field names
-			var f string
-			if n.Implicit {
-				f += "{"
-			} else {
-				f += fmt.Sprintf("(%v{", n.Type)
-			}
-			for i1, n1 := range n.List.Slice() {
-				f += fmt.Sprintf(" %v:%v", sconv(n1.Left.Sym, FmtShort|FmtByte), n1.Right)
-
-				if i1+1 < n.List.Len() {
-					f += ","
-				} else {
-					f += " "
-				}
-			}
-
-			if !n.Implicit {
-				f += "})"
-				return f
-			}
-			f += "}"
-			return f
-		}
-		fallthrough
-
-	case OARRAYLIT, OMAPLIT:
+	case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:
 		if fmtmode == FErr {
-			return fmt.Sprintf("%v literal", n.Type)
+			fmt.Fprintf(s, "%v literal", n.Type)
+			return
 		}
-		if fmtmode == FExp && n.Implicit {
-			return fmt.Sprintf("{ %v }", hconv(n.List, FmtComma))
-		}
-		return fmt.Sprintf("(%v{ %v })", n.Type, hconv(n.List, FmtComma))
+		fmt.Fprintf(s, "(%v{ %.v })", n.Type, n.List)
 
 	case OKEY:
 		if n.Left != nil && n.Right != nil {
-			if fmtmode == FExp && n.Left.Type == structkey {
-				// requires special handling of field names
-				return fmt.Sprintf("%v:%v", sconv(n.Left.Sym, FmtShort|FmtByte), n.Right)
-			} else {
-				return fmt.Sprintf("%v:%v", n.Left, n.Right)
-			}
+			fmt.Fprintf(s, "%v:%v", n.Left, n.Right)
+			return
 		}
 
 		if n.Left == nil && n.Right != nil {
-			return fmt.Sprintf(":%v", n.Right)
+			fmt.Fprintf(s, ":%v", n.Right)
+			return
 		}
 		if n.Left != nil && n.Right == nil {
-			return fmt.Sprintf("%v:", n.Left)
+			fmt.Fprintf(s, "%v:", n.Left)
+			return
 		}
-		return ":"
+		fmt.Fprint(s, ":")
+
+	case OSTRUCTKEY:
+		fmt.Fprintf(s, "%v:%v", n.Sym, n.Left)
 
 	case OCALLPART:
-		var f string
-		f += exprfmt(n.Left, nprec)
+		n.Left.exprfmt(s, nprec)
 		if n.Right == nil || n.Right.Sym == nil {
-			f += ".<nil>"
-			return f
+			fmt.Fprint(s, ".<nil>")
+			return
 		}
-		f += fmt.Sprintf(".%v", sconv(n.Right.Sym, FmtShort|FmtByte))
-		return f
+		fmt.Fprintf(s, ".%0S", n.Right.Sym)
 
 	case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH:
-		var f string
-		f += exprfmt(n.Left, nprec)
+		n.Left.exprfmt(s, nprec)
 		if n.Sym == nil {
-			f += ".<nil>"
-			return f
+			fmt.Fprint(s, ".<nil>")
+			return
 		}
-		f += fmt.Sprintf(".%v", sconv(n.Sym, FmtShort|FmtByte))
-		return f
+		fmt.Fprintf(s, ".%0S", n.Sym)
 
 	case ODOTTYPE, ODOTTYPE2:
-		var f string
-		f += exprfmt(n.Left, nprec)
+		n.Left.exprfmt(s, nprec)
 		if n.Right != nil {
-			f += fmt.Sprintf(".(%v)", n.Right)
-			return f
+			fmt.Fprintf(s, ".(%v)", n.Right)
+			return
 		}
-		f += fmt.Sprintf(".(%v)", n.Type)
-		return f
+		fmt.Fprintf(s, ".(%v)", n.Type)
 
 	case OINDEX, OINDEXMAP:
-		return fmt.Sprintf("%s[%v]", exprfmt(n.Left, nprec), n.Right)
+		n.Left.exprfmt(s, nprec)
+		fmt.Fprintf(s, "[%v]", n.Right)
 
 	case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
-		var buf bytes.Buffer
-		buf.WriteString(exprfmt(n.Left, nprec))
-		buf.WriteString("[")
+		n.Left.exprfmt(s, nprec)
+		fmt.Fprint(s, "[")
 		low, high, max := n.SliceBounds()
 		if low != nil {
-			buf.WriteString(low.String())
+			fmt.Fprint(s, low.String())
 		}
-		buf.WriteString(":")
+		fmt.Fprint(s, ":")
 		if high != nil {
-			buf.WriteString(high.String())
+			fmt.Fprint(s, high.String())
 		}
 		if n.Op.IsSlice3() {
-			buf.WriteString(":")
+			fmt.Fprint(s, ":")
 			if max != nil {
-				buf.WriteString(max.String())
+				fmt.Fprint(s, max.String())
 			}
 		}
-		buf.WriteString("]")
-		return buf.String()
+		fmt.Fprint(s, "]")
 
 	case OCOPY, OCOMPLEX:
-		return fmt.Sprintf("%#v(%v, %v)", n.Op, n.Left, n.Right)
+		fmt.Fprintf(s, "%#v(%v, %v)", n.Op, n.Left, n.Right)
 
 	case OCONV,
 		OCONVIFACE,
@@ -1342,12 +1333,14 @@ func exprfmt(n *Node, prec int) string {
 		OSTRARRAYRUNE,
 		ORUNESTR:
 		if n.Type == nil || n.Type.Sym == nil {
-			return fmt.Sprintf("(%v)(%v)", n.Type, n.Left)
+			fmt.Fprintf(s, "(%v)(%v)", n.Type, n.Left)
+			return
 		}
 		if n.Left != nil {
-			return fmt.Sprintf("%v(%v)", n.Type, n.Left)
+			fmt.Fprintf(s, "%v(%v)", n.Type, n.Left)
+			return
 		}
-		return fmt.Sprintf("%v(%v)", n.Type, hconv(n.List, FmtComma))
+		fmt.Fprintf(s, "%v(%.v)", n.Type, n.List)
 
 	case OREAL,
 		OIMAG,
@@ -1360,37 +1353,43 @@ func exprfmt(n *Node, prec int) string {
 		ONEW,
 		OPANIC,
 		ORECOVER,
+		OALIGNOF,
+		OOFFSETOF,
+		OSIZEOF,
 		OPRINT,
 		OPRINTN:
 		if n.Left != nil {
-			return fmt.Sprintf("%#v(%v)", n.Op, n.Left)
+			fmt.Fprintf(s, "%#v(%v)", n.Op, n.Left)
+			return
 		}
 		if n.Isddd {
-			return fmt.Sprintf("%#v(%v...)", n.Op, hconv(n.List, FmtComma))
+			fmt.Fprintf(s, "%#v(%.v...)", n.Op, n.List)
+			return
 		}
-		return fmt.Sprintf("%#v(%v)", n.Op, hconv(n.List, FmtComma))
+		fmt.Fprintf(s, "%#v(%.v)", n.Op, n.List)
 
 	case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG:
-		var f string
-		f += exprfmt(n.Left, nprec)
+		n.Left.exprfmt(s, nprec)
 		if n.Isddd {
-			f += fmt.Sprintf("(%v...)", hconv(n.List, FmtComma))
-			return f
+			fmt.Fprintf(s, "(%.v...)", n.List)
+			return
 		}
-		f += fmt.Sprintf("(%v)", hconv(n.List, FmtComma))
-		return f
+		fmt.Fprintf(s, "(%.v)", n.List)
 
 	case OMAKEMAP, OMAKECHAN, OMAKESLICE:
 		if n.List.Len() != 0 { // pre-typecheck
-			return fmt.Sprintf("make(%v, %v)", n.Type, hconv(n.List, FmtComma))
+			fmt.Fprintf(s, "make(%v, %.v)", n.Type, n.List)
+			return
 		}
 		if n.Right != nil {
-			return fmt.Sprintf("make(%v, %v, %v)", n.Type, n.Left, n.Right)
+			fmt.Fprintf(s, "make(%v, %v, %v)", n.Type, n.Left, n.Right)
+			return
 		}
 		if n.Left != nil && (n.Op == OMAKESLICE || !n.Left.Type.IsUntyped()) {
-			return fmt.Sprintf("make(%v, %v)", n.Type, n.Left)
+			fmt.Fprintf(s, "make(%v, %v)", n.Type, n.Left)
+			return
 		}
-		return fmt.Sprintf("make(%v)", n.Type)
+		fmt.Fprintf(s, "make(%v)", n.Type)
 
 		// Unary
 	case OPLUS,
@@ -1400,12 +1399,11 @@ func exprfmt(n *Node, prec int) string {
 		OIND,
 		ONOT,
 		ORECV:
-		f := n.Op.GoString() // %#v
+		fmt.Fprint(s, n.Op.GoString()) // %#v
 		if n.Left.Op == n.Op {
-			f += " "
+			fmt.Fprint(s, " ")
 		}
-		f += exprfmt(n.Left, nprec+1)
-		return f
+		n.Left.exprfmt(s, nprec+1)
 
 		// Binary
 	case OADD,
@@ -1428,175 +1426,161 @@ func exprfmt(n *Node, prec int) string {
 		OSEND,
 		OSUB,
 		OXOR:
-		var f string
-		f += exprfmt(n.Left, nprec)
-
-		f += fmt.Sprintf(" %#v ", n.Op)
-		f += exprfmt(n.Right, nprec+1)
-		return f
+		n.Left.exprfmt(s, nprec)
+		fmt.Fprintf(s, " %#v ", n.Op)
+		n.Right.exprfmt(s, nprec+1)
 
 	case OADDSTR:
-		var f string
 		i := 0
 		for _, n1 := range n.List.Slice() {
 			if i != 0 {
-				f += " + "
+				fmt.Fprint(s, " + ")
 			}
-			f += exprfmt(n1, nprec)
+			n1.exprfmt(s, nprec)
 			i++
 		}
 
-		return f
-
 	case OCMPSTR, OCMPIFACE:
-		var f string
-		f += exprfmt(n.Left, nprec)
+		n.Left.exprfmt(s, nprec)
 		// TODO(marvin): Fix Node.EType type union.
-		f += fmt.Sprintf(" %#v ", Op(n.Etype))
-		f += exprfmt(n.Right, nprec+1)
-		return f
+		fmt.Fprintf(s, " %#v ", Op(n.Etype))
+		n.Right.exprfmt(s, nprec+1)
 
-	case ODCLCONST:
-		// if exporting, DCLCONST should just be removed as its usage
-		// has already been replaced with literals
-		if fmtbody {
-			return ""
-		}
+	default:
+		fmt.Fprintf(s, "<node %v>", n.Op)
 	}
-
-	return fmt.Sprintf("<node %v>", n.Op)
 }
 
-func nodefmt(n *Node, flag FmtFlag) string {
+func (n *Node) nodefmt(s fmt.State, flag FmtFlag) {
 	t := n.Type
 
 	// we almost always want the original, except in export mode for literals
 	// this saves the importer some work, and avoids us having to redo some
 	// special casing for package unsafe
-	if (fmtmode != FExp || n.Op != OLITERAL) && n.Orig != nil {
+	if n.Op != OLITERAL && n.Orig != nil {
 		n = n.Orig
 	}
 
 	if flag&FmtLong != 0 && t != nil {
 		if t.Etype == TNIL {
-			return "nil"
+			fmt.Fprint(s, "nil")
 		} else {
-			return fmt.Sprintf("%v (type %v)", n, t)
+			fmt.Fprintf(s, "%v (type %v)", n, t)
 		}
+		return
 	}
 
 	// TODO inlining produces expressions with ninits. we can't print these yet.
 
 	if opprec[n.Op] < 0 {
-		return stmtfmt(n)
+		n.stmtfmt(s)
+		return
 	}
 
-	return exprfmt(n, 0)
-}
-
-var dumpdepth int
-
-func indent(buf *bytes.Buffer) {
-	buf.WriteString("\n")
-	for i := 0; i < dumpdepth; i++ {
-		buf.WriteString(".   ")
-	}
+	n.exprfmt(s, 0)
 }
 
-func nodedump(n *Node, flag FmtFlag) string {
+func (n *Node) nodedump(s fmt.State, flag FmtFlag) {
 	if n == nil {
-		return ""
+		return
 	}
 
 	recur := flag&FmtShort == 0
 
-	var buf bytes.Buffer
 	if recur {
-		indent(&buf)
+		indent(s)
 		if dumpdepth > 10 {
-			buf.WriteString("...")
-			return buf.String()
+			fmt.Fprint(s, "...")
+			return
 		}
 
 		if n.Ninit.Len() != 0 {
-			fmt.Fprintf(&buf, "%v-init%v", n.Op, n.Ninit)
-			indent(&buf)
+			fmt.Fprintf(s, "%v-init%v", n.Op, n.Ninit)
+			indent(s)
 		}
 	}
 
 	switch n.Op {
 	default:
-		fmt.Fprintf(&buf, "%v%v", n.Op, jconv(n, 0))
+		fmt.Fprintf(s, "%v%j", n.Op, n)
 
-	case OREGISTER, OINDREG:
-		fmt.Fprintf(&buf, "%v-%v%v", n.Op, obj.Rconv(int(n.Reg)), jconv(n, 0))
+	case OINDREGSP:
+		fmt.Fprintf(s, "%v-SP%j", n.Op, n)
 
 	case OLITERAL:
-		fmt.Fprintf(&buf, "%v-%v%v", n.Op, vconv(n.Val(), 0), jconv(n, 0))
+		fmt.Fprintf(s, "%v-%v%j", n.Op, n.Val(), n)
 
 	case ONAME, ONONAME:
 		if n.Sym != nil {
-			fmt.Fprintf(&buf, "%v-%v%v", n.Op, n.Sym, jconv(n, 0))
+			fmt.Fprintf(s, "%v-%v%j", n.Op, n.Sym, n)
 		} else {
-			fmt.Fprintf(&buf, "%v%v", n.Op, jconv(n, 0))
+			fmt.Fprintf(s, "%v%j", n.Op, n)
 		}
 		if recur && n.Type == nil && n.Name != nil && n.Name.Param != nil && n.Name.Param.Ntype != nil {
-			indent(&buf)
-			fmt.Fprintf(&buf, "%v-ntype%v", n.Op, n.Name.Param.Ntype)
+			indent(s)
+			fmt.Fprintf(s, "%v-ntype%v", n.Op, n.Name.Param.Ntype)
 		}
 
 	case OASOP:
-		fmt.Fprintf(&buf, "%v-%v%v", n.Op, Op(n.Etype), jconv(n, 0))
+		fmt.Fprintf(s, "%v-%v%j", n.Op, Op(n.Etype), n)
 
 	case OTYPE:
-		fmt.Fprintf(&buf, "%v %v%v type=%v", n.Op, n.Sym, jconv(n, 0), n.Type)
+		fmt.Fprintf(s, "%v %v%j type=%v", n.Op, n.Sym, n, n.Type)
 		if recur && n.Type == nil && n.Name.Param.Ntype != nil {
-			indent(&buf)
-			fmt.Fprintf(&buf, "%v-ntype%v", n.Op, n.Name.Param.Ntype)
+			indent(s)
+			fmt.Fprintf(s, "%v-ntype%v", n.Op, n.Name.Param.Ntype)
 		}
 	}
 
 	if n.Sym != nil && n.Op != ONAME {
-		fmt.Fprintf(&buf, " %v", n.Sym)
+		fmt.Fprintf(s, " %v", n.Sym)
 	}
 
 	if n.Type != nil {
-		fmt.Fprintf(&buf, " %v", n.Type)
+		fmt.Fprintf(s, " %v", n.Type)
 	}
 
 	if recur {
 		if n.Left != nil {
-			buf.WriteString(Nconv(n.Left, 0))
+			fmt.Fprintf(s, "%v", n.Left)
 		}
 		if n.Right != nil {
-			buf.WriteString(Nconv(n.Right, 0))
+			fmt.Fprintf(s, "%v", n.Right)
 		}
 		if n.List.Len() != 0 {
-			indent(&buf)
-			fmt.Fprintf(&buf, "%v-list%v", n.Op, n.List)
+			indent(s)
+			fmt.Fprintf(s, "%v-list%v", n.Op, n.List)
 		}
 
 		if n.Rlist.Len() != 0 {
-			indent(&buf)
-			fmt.Fprintf(&buf, "%v-rlist%v", n.Op, n.Rlist)
+			indent(s)
+			fmt.Fprintf(s, "%v-rlist%v", n.Op, n.Rlist)
 		}
 
 		if n.Nbody.Len() != 0 {
-			indent(&buf)
-			fmt.Fprintf(&buf, "%v-body%v", n.Op, n.Nbody)
+			indent(s)
+			fmt.Fprintf(s, "%v-body%v", n.Op, n.Nbody)
 		}
 	}
+}
+
+// "%S" suppresses qualifying with package
+func (s *Sym) Format(f fmt.State, verb rune) {
+	switch verb {
+	case 'v', 'S':
+		fmt.Fprint(f, s.sconv(fmtFlag(f, verb)))
 
-	return buf.String()
+	default:
+		fmt.Fprintf(f, "%%!%c(*Sym=%p)", verb, s)
+	}
 }
 
 func (s *Sym) String() string {
-	return sconv(s, 0)
+	return s.sconv(0)
 }
 
-// Fmt "%S": syms
-// Flags:  "%hS" suppresses qualifying with package
-func sconv(s *Sym, flag FmtFlag) string {
+// See #16897 before changing the implementation of sconv.
+func (s *Sym) sconv(flag FmtFlag) string {
 	if flag&FmtLong != 0 {
 		panic("linksymfmt")
 	}
@@ -1610,25 +1594,24 @@ func sconv(s *Sym, flag FmtFlag) string {
 	}
 
 	sf := flag
-	sm, sb := setfmode(&flag)
-	str := symfmt(s, flag)
+	sm := setfmode(&flag)
+	str := s.symfmt(flag)
 	flag = sf
 	fmtmode = sm
-	fmtbody = sb
 	return str
 }
 
 func (t *Type) String() string {
-	return Tconv(t, 0)
+	return t.tconv(0)
 }
 
-func Fldconv(f *Field, flag FmtFlag) string {
+func fldconv(f *Field, flag FmtFlag) string {
 	if f == nil {
 		return "<T>"
 	}
 
 	sf := flag
-	sm, sb := setfmode(&flag)
+	sm := setfmode(&flag)
 
 	if fmtmode == FTypeId && (sf&FmtUnsigned != 0) {
 		fmtpkgpfx++
@@ -1643,14 +1626,14 @@ func Fldconv(f *Field, flag FmtFlag) string {
 
 		// Take the name from the original, lest we substituted it with ~r%d or ~b%d.
 		// ~r%d is a (formerly) unnamed result.
-		if (fmtmode == FErr || fmtmode == FExp) && f.Nname != nil {
+		if fmtmode == FErr && f.Nname != nil {
 			if f.Nname.Orig != nil {
 				s = f.Nname.Orig.Sym
 				if s != nil && s.Name[0] == '~' {
 					if s.Name[1] == 'r' { // originally an unnamed result
 						s = nil
 					} else if s.Name[1] == 'b' { // originally the blank identifier _
-						s = Lookup("_")
+						s = lookup("_")
 					}
 				}
 			} else {
@@ -1660,26 +1643,23 @@ func Fldconv(f *Field, flag FmtFlag) string {
 
 		if s != nil && f.Embedded == 0 {
 			if f.Funarg != FunargNone {
-				name = Nconv(f.Nname, 0)
+				name = f.Nname.String()
 			} else if flag&FmtLong != 0 {
-				name = sconv(s, FmtShort|FmtByte) // qualify non-exported names (used on structs, not on funarg)
-			} else {
-				name = sconv(s, 0)
-			}
-		} else if fmtmode == FExp {
-			if f.Embedded != 0 && s.Pkg != nil && len(s.Pkg.Path) > 0 {
-				name = fmt.Sprintf("@%q.?", s.Pkg.Path)
+				name = fmt.Sprintf("%0S", s)
+				if !exportname(name) && flag&FmtUnsigned == 0 {
+					name = s.String() // qualify non-exported names (used on structs, not on funarg)
+				}
 			} else {
-				name = "?"
+				name = s.String()
 			}
 		}
 	}
 
 	var typ string
 	if f.Isddd {
-		typ = "..." + Tconv(f.Type.Elem(), 0)
+		typ = fmt.Sprintf("...%v", f.Type.Elem())
 	} else {
-		typ = Tconv(f.Type, 0)
+		typ = fmt.Sprintf("%v", f.Type)
 	}
 
 	str := typ
@@ -1687,12 +1667,7 @@ func Fldconv(f *Field, flag FmtFlag) string {
 		str = name + " " + typ
 	}
 
-	// The fmtbody flag is intended to suppress escape analysis annotations
-	// when printing a function type used in a function body.
-	// (The escape analysis tags do not apply to func vars.)
-	// But it must not suppress struct field tags.
-	// See golang.org/issue/13777 and golang.org/issue/14331.
-	if flag&FmtShort == 0 && (!fmtbody || f.Funarg == FunargNone) && f.Note != "" {
+	if flag&FmtShort == 0 && f.Funarg == FunargNone && f.Note != "" {
 		str += " " + strconv.Quote(f.Note)
 	}
 
@@ -1701,16 +1676,25 @@ func Fldconv(f *Field, flag FmtFlag) string {
 	}
 
 	flag = sf
-	fmtbody = sb
 	fmtmode = sm
 	return str
 }
 
-// Fmt "%T": types.
-// Flags: 'l' print definition, not name
-//	  'h' omit 'func' and receiver from function types, short type names
-//	  'u' package name, not prefix (FTypeId mode, sticky)
-func Tconv(t *Type, flag FmtFlag) string {
+// "%L"  print definition, not name
+// "%S"  omit 'func' and receiver from function types, short type names
+// "% v" package name, not prefix (FTypeId mode, sticky)
+func (t *Type) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 'v', 'S', 'L':
+		fmt.Fprint(s, t.tconv(fmtFlag(s, verb)))
+
+	default:
+		fmt.Fprintf(s, "%%!%c(*Type=%p)", verb, t)
+	}
+}
+
+// See #16897 before changing the implementation of tconv.
+func (t *Type) tconv(flag FmtFlag) string {
 	if t == nil {
 		return "<T>"
 	}
@@ -1721,7 +1705,7 @@ func Tconv(t *Type, flag FmtFlag) string {
 
 	t.Trecur++
 	sf := flag
-	sm, sb := setfmode(&flag)
+	sm := setfmode(&flag)
 
 	if fmtmode == FTypeId && (sf&FmtUnsigned != 0) {
 		fmtpkgpfx++
@@ -1730,66 +1714,73 @@ func Tconv(t *Type, flag FmtFlag) string {
 		flag |= FmtUnsigned
 	}
 
-	str := typefmt(t, flag)
+	str := t.typefmt(flag)
 
 	if fmtmode == FTypeId && (sf&FmtUnsigned != 0) {
 		fmtpkgpfx--
 	}
 
 	flag = sf
-	fmtbody = sb
 	fmtmode = sm
 	t.Trecur--
 	return str
 }
 
 func (n *Node) String() string {
-	return Nconv(n, 0)
+	return fmt.Sprint(n)
 }
 
-// Fmt '%N': Nodes.
-// Flags: 'l' suffix with "(type %T)" where possible
-//	  '+h' in debug mode, don't recurse, no multiline output
-func Nconv(n *Node, flag FmtFlag) string {
+// "%L"  suffix with "(type %T)" where possible
+// "%+S" in debug mode, don't recurse, no multiline output
+func (n *Node) Nconv(s fmt.State, flag FmtFlag) {
 	if n == nil {
-		return "<N>"
+		fmt.Fprint(s, "<N>")
+		return
 	}
+
 	sf := flag
-	sm, sb := setfmode(&flag)
+	sm := setfmode(&flag)
 
-	var str string
 	switch fmtmode {
-	case FErr, FExp:
-		str = nodefmt(n, flag)
+	case FErr:
+		n.nodefmt(s, flag)
 
 	case FDbg:
 		dumpdepth++
-		str = nodedump(n, flag)
+		n.nodedump(s, flag)
 		dumpdepth--
 
 	default:
-		Fatalf("unhandled %%N mode")
+		Fatalf("unhandled %%N mode: %d", fmtmode)
 	}
 
 	flag = sf
-	fmtbody = sb
 	fmtmode = sm
-	return str
+}
+
+func (l Nodes) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 'v':
+		l.hconv(s, fmtFlag(s, verb))
+
+	default:
+		fmt.Fprintf(s, "%%!%c(Nodes)", verb)
+	}
 }
 
 func (n Nodes) String() string {
-	return hconv(n, 0)
+	return fmt.Sprint(n)
 }
 
-// Fmt '%H': Nodes.
-// Flags: all those of %N plus ',': separate with comma's instead of semicolons.
-func hconv(l Nodes, flag FmtFlag) string {
+// Flags: all those of %N plus '.': separate with comma's instead of semicolons.
+func (l Nodes) hconv(s fmt.State, flag FmtFlag) {
 	if l.Len() == 0 && fmtmode == FDbg {
-		return "<nil>"
+		fmt.Fprint(s, "<nil>")
+		return
 	}
 
 	sf := flag
-	sm, sb := setfmode(&flag)
+	sm := setfmode(&flag)
 	sep := "; "
 	if fmtmode == FDbg {
 		sep = "\n"
@@ -1797,24 +1788,32 @@ func hconv(l Nodes, flag FmtFlag) string {
 		sep = ", "
 	}
 
-	var buf bytes.Buffer
 	for i, n := range l.Slice() {
-		buf.WriteString(Nconv(n, 0))
+		fmt.Fprint(s, n)
 		if i+1 < l.Len() {
-			buf.WriteString(sep)
+			fmt.Fprint(s, sep)
 		}
 	}
 
 	flag = sf
-	fmtbody = sb
 	fmtmode = sm
-	return buf.String()
 }
 
 func dumplist(s string, l Nodes) {
-	fmt.Printf("%s%v\n", s, hconv(l, FmtSign))
+	fmt.Printf("%s%+v\n", s, l)
 }
 
 func Dump(s string, n *Node) {
-	fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, FmtSign))
+	fmt.Printf("%s [%p]%+v\n", s, n, n)
+}
+
+// TODO(gri) make variable local somehow
+var dumpdepth int
+
+// indent prints indentation to s.
+func indent(s fmt.State) {
+	fmt.Fprint(s, "\n")
+	for i := 0; i < dumpdepth; i++ {
+		fmt.Fprint(s, ".   ")
+	}
 }
diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go
index fc0003d..c3d2c44 100644
--- a/src/cmd/compile/internal/gc/gen.go
+++ b/src/cmd/compile/internal/gc/gen.go
@@ -6,14 +6,7 @@
 
 package gc
 
-import (
-	"cmd/internal/obj"
-	"cmd/internal/sys"
-	"fmt"
-)
-
-// TODO: labellist should become part of a "compilation state" for functions.
-var labellist []*Label
+import "fmt"
 
 func Sysfunc(name string) *Node {
 	n := newname(Pkglookup(name, Runtimepkg))
@@ -103,30 +96,13 @@ func (n *Node) isParamHeapCopy() bool {
 	return n.Op == ONAME && n.Class == PAUTOHEAP && n.Name.Param.Stackcopy != nil
 }
 
-// paramClass reports the parameter class (PPARAM or PPARAMOUT)
-// of the node, which may be an unmoved on-stack parameter
-// or the on-heap or on-stack copy of a parameter that moved to the heap.
-// If the node is not a parameter, paramClass returns Pxxx.
-func (n *Node) paramClass() Class {
-	if n.Op != ONAME {
-		return Pxxx
-	}
-	if n.Class == PPARAM || n.Class == PPARAMOUT {
-		return n.Class
-	}
-	if n.isParamHeapCopy() {
-		return n.Name.Param.Stackcopy.Class
-	}
-	return Pxxx
-}
-
 // moveToHeap records the parameter or local variable n as moved to the heap.
 func moveToHeap(n *Node) {
 	if Debug['r'] != 0 {
 		Dump("MOVE", n)
 	}
 	if compiling_runtime {
-		Yyerror("%v escapes to heap, not allowed in runtime.", n)
+		yyerror("%v escapes to heap, not allowed in runtime.", n)
 	}
 	if n.Class == PAUTOHEAP {
 		Dump("n", n)
@@ -135,10 +111,15 @@ func moveToHeap(n *Node) {
 
 	// Allocate a local stack variable to hold the pointer to the heap copy.
 	// temp will add it to the function declaration list automatically.
-	heapaddr := temp(Ptrto(n.Type))
-	heapaddr.Sym = Lookup("&" + n.Sym.Name)
+	heapaddr := temp(ptrto(n.Type))
+	heapaddr.Sym = lookup("&" + n.Sym.Name)
 	heapaddr.Orig.Sym = heapaddr.Sym
 
+	// Unset AutoTemp to persist the &foo variable name through SSA to
+	// liveness analysis.
+	// TODO(mdempsky/drchase): Cleaner solution?
+	heapaddr.Name.AutoTemp = false
+
 	// Parameters have a local stack copy used at function start/end
 	// in addition to the copy in the heap that may live longer than
 	// the function.
@@ -151,15 +132,12 @@ func moveToHeap(n *Node) {
 		// Preserve a copy so we can still write code referring to the original,
 		// and substitute that copy into the function declaration list
 		// so that analyses of the local (on-stack) variables use it.
-		stackcopy := Nod(ONAME, nil, nil)
+		stackcopy := nod(ONAME, nil, nil)
 		stackcopy.Sym = n.Sym
 		stackcopy.Type = n.Type
 		stackcopy.Xoffset = n.Xoffset
 		stackcopy.Class = n.Class
 		stackcopy.Name.Heapaddr = heapaddr
-		if n.Class == PPARAM {
-			stackcopy.SetNotLiveAtEnd(true)
-		}
 		if n.Class == PPARAMOUT {
 			// Make sure the pointer to the heap copy is kept live throughout the function.
 			// The function could panic at any point, and then a defer could recover.
@@ -203,492 +181,27 @@ func moveToHeap(n *Node) {
 	}
 }
 
-func clearlabels() {
-	for _, l := range labellist {
-		l.Sym.Label = nil
-	}
-	labellist = labellist[:0]
-}
-
-func newlab(n *Node) *Label {
-	s := n.Left.Sym
-	lab := s.Label
-	if lab == nil {
-		lab = new(Label)
-		lab.Sym = s
-		s.Label = lab
-		labellist = append(labellist, lab)
-	}
-
-	if n.Op == OLABEL {
-		if lab.Def != nil {
-			Yyerror("label %v already defined at %v", s, lab.Def.Line())
-		} else {
-			lab.Def = n
-		}
-	} else {
-		lab.Use = append(lab.Use, n)
-	}
-
-	return lab
-}
-
-// There is a copy of checkgoto in the new SSA backend.
-// Please keep them in sync.
-func checkgoto(from *Node, to *Node) {
-	if from.Sym == to.Sym {
-		return
-	}
-
-	nf := 0
-	for fs := from.Sym; fs != nil; fs = fs.Link {
-		nf++
-	}
-	nt := 0
-	for fs := to.Sym; fs != nil; fs = fs.Link {
-		nt++
-	}
-	fs := from.Sym
-	for ; nf > nt; nf-- {
-		fs = fs.Link
-	}
-	if fs != to.Sym {
-		lno := lineno
-		setlineno(from)
-
-		// decide what to complain about.
-		// prefer to complain about 'into block' over declarations,
-		// so scan backward to find most recent block or else dcl.
-		var block *Sym
-
-		var dcl *Sym
-		ts := to.Sym
-		for ; nt > nf; nt-- {
-			if ts.Pkg == nil {
-				block = ts
-			} else {
-				dcl = ts
-			}
-			ts = ts.Link
-		}
-
-		for ts != fs {
-			if ts.Pkg == nil {
-				block = ts
-			} else {
-				dcl = ts
-			}
-			ts = ts.Link
-			fs = fs.Link
-		}
-
-		if block != nil {
-			Yyerror("goto %v jumps into block starting at %v", from.Left.Sym, linestr(block.Lastlineno))
-		} else {
-			Yyerror("goto %v jumps over declaration of %v at %v", from.Left.Sym, dcl, linestr(dcl.Lastlineno))
-		}
-		lineno = lno
-	}
-}
-
-func stmtlabel(n *Node) *Label {
-	if n.Sym != nil {
-		lab := n.Sym.Label
-		if lab != nil {
-			if lab.Def != nil {
-				if lab.Def.Name.Defn == n {
-					return lab
-				}
-			}
-		}
-	}
-	return nil
-}
-
-// compile statements
-func Genlist(l Nodes) {
-	for _, n := range l.Slice() {
-		gen(n)
-	}
-}
-
-// generate code to start new proc running call n.
-func cgen_proc(n *Node, proc int) {
-	switch n.Left.Op {
-	default:
-		Fatalf("cgen_proc: unknown call %v", n.Left.Op)
-
-	case OCALLMETH:
-		cgen_callmeth(n.Left, proc)
-
-	case OCALLINTER:
-		cgen_callinter(n.Left, nil, proc)
-
-	case OCALLFUNC:
-		cgen_call(n.Left, proc)
-	}
-}
-
-// generate declaration.
-// have to allocate heap copy
-// for escaped variables.
-func cgen_dcl(n *Node) {
-	if Debug['g'] != 0 {
-		Dump("\ncgen-dcl", n)
-	}
-	if n.Op != ONAME {
-		Dump("cgen_dcl", n)
-		Fatalf("cgen_dcl")
-	}
-
-	if n.Class == PAUTOHEAP {
-		Fatalf("cgen_dcl %v", n)
-	}
-}
-
-// generate discard of value
-func cgen_discard(nr *Node) {
-	if nr == nil {
-		return
-	}
-
-	switch nr.Op {
-	case ONAME:
-		if nr.Class != PAUTOHEAP && nr.Class != PEXTERN && nr.Class != PFUNC {
-			gused(nr)
-		}
-
-		// unary
-	case OADD,
-		OAND,
-		ODIV,
-		OEQ,
-		OGE,
-		OGT,
-		OLE,
-		OLSH,
-		OLT,
-		OMOD,
-		OMUL,
-		ONE,
-		OOR,
-		ORSH,
-		OSUB,
-		OXOR:
-		cgen_discard(nr.Left)
-
-		cgen_discard(nr.Right)
-
-		// binary
-	case OCAP,
-		OCOM,
-		OLEN,
-		OMINUS,
-		ONOT,
-		OPLUS:
-		cgen_discard(nr.Left)
-
-	case OIND:
-		Cgen_checknil(nr.Left)
-
-		// special enough to just evaluate
-	default:
-		var tmp Node
-		Tempname(&tmp, nr.Type)
-
-		Cgen_as(&tmp, nr)
-		gused(&tmp)
-	}
-}
-
-// clearslim generates code to zero a slim node.
-func Clearslim(n *Node) {
-	var z Node
-	z.Op = OLITERAL
-	z.Type = n.Type
-	z.Addable = true
-
-	switch Simtype[n.Type.Etype] {
-	case TCOMPLEX64, TCOMPLEX128:
-		z.SetVal(Val{new(Mpcplx)})
-		z.Val().U.(*Mpcplx).Real.SetFloat64(0.0)
-		z.Val().U.(*Mpcplx).Imag.SetFloat64(0.0)
-
-	case TFLOAT32, TFLOAT64:
-		var zero Mpflt
-		zero.SetFloat64(0.0)
-		z.SetVal(Val{&zero})
-
-	case TPTR32, TPTR64, TCHAN, TMAP:
-		z.SetVal(Val{new(NilVal)})
-
-	case TBOOL:
-		z.SetVal(Val{false})
-
-	case TINT8,
-		TINT16,
-		TINT32,
-		TINT64,
-		TUINT8,
-		TUINT16,
-		TUINT32,
-		TUINT64:
-		z.SetVal(Val{new(Mpint)})
-		z.Val().U.(*Mpint).SetInt64(0)
-
-	default:
-		Fatalf("clearslim called on type %v", n.Type)
-	}
-
-	ullmancalc(&z)
-	Cgen(&z, n)
-}
-
-// generate:
-//	res = iface{typ, data}
-// n->left is typ
-// n->right is data
-func Cgen_eface(n *Node, res *Node) {
-	// the right node of an eface may contain function calls that uses res as an argument,
-	// so it's important that it is done first
-
-	tmp := temp(Types[Tptr])
-	Cgen(n.Right, tmp)
-
-	Gvardef(res)
-
-	dst := *res
-	dst.Type = Types[Tptr]
-	dst.Xoffset += int64(Widthptr)
-	Cgen(tmp, &dst)
-
-	dst.Xoffset -= int64(Widthptr)
-	Cgen(n.Left, &dst)
-}
-
-// generate one of:
-//	res, resok = x.(T)
-//	res = x.(T) (when resok == nil)
-// n.Left is x
-// n.Type is T
-func cgen_dottype(n *Node, res, resok *Node, wb bool) {
-	if Debug_typeassert > 0 {
-		Warn("type assertion inlined")
-	}
-	//	iface := n.Left
-	//	r1 := iword(iface)
-	//	if n.Left is non-empty interface {
-	//		r1 = *r1
-	//	}
-	//	if r1 == T {
-	//		res = idata(iface)
-	//		resok = true
-	//	} else {
-	//		assert[EI]2T(x, T, nil) // (when resok == nil; does not return)
-	//		resok = false // (when resok != nil)
-	//	}
-	//
-	var iface Node
-	Igen(n.Left, &iface, res)
-	var r1, r2 Node
-	byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte)
-	Regalloc(&r1, byteptr, nil)
-	iface.Type = byteptr
-	Cgen(&iface, &r1)
-	if !n.Left.Type.IsEmptyInterface() {
-		// Holding itab, want concrete type in second word.
-		p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1)
-		r2 = r1
-		r2.Op = OINDREG
-		r2.Xoffset = int64(Widthptr)
-		Cgen(&r2, &r1)
-		Patch(p, Pc)
-	}
-	Regalloc(&r2, byteptr, nil)
-	Cgen(typename(n.Type), &r2)
-	p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1)
-	Regfree(&r2) // not needed for success path; reclaimed on one failure path
-	iface.Xoffset += int64(Widthptr)
-	Cgen(&iface, &r1)
-	Regfree(&iface)
-
-	if resok == nil {
-		r1.Type = res.Type
-		cgen_wb(&r1, res, wb)
-		q := Gbranch(obj.AJMP, nil, 0)
-		Patch(p, Pc)
-		Regrealloc(&r2) // reclaim from above, for this failure path
-		fn := syslook("panicdottype")
-		dowidth(fn.Type)
-		call := Nod(OCALLFUNC, fn, nil)
-		r1.Type = byteptr
-		r2.Type = byteptr
-		call.List.Set([]*Node{&r1, &r2, typename(n.Left.Type)})
-		call.List.Set(ascompatte(OCALLFUNC, call, false, fn.Type.Params(), call.List.Slice(), 0, nil))
-		gen(call)
-		Regfree(&r1)
-		Regfree(&r2)
-		Thearch.Gins(obj.AUNDEF, nil, nil)
-		Patch(q, Pc)
-	} else {
-		// This half is handling the res, resok = x.(T) case,
-		// which is called from gen, not cgen, and is consequently fussier
-		// about blank assignments. We have to avoid calling cgen for those.
-		r1.Type = res.Type
-		if !isblank(res) {
-			cgen_wb(&r1, res, wb)
-		}
-		Regfree(&r1)
-		if !isblank(resok) {
-			Cgen(Nodbool(true), resok)
-		}
-		q := Gbranch(obj.AJMP, nil, 0)
-		Patch(p, Pc)
-		if !isblank(res) {
-			n := nodnil()
-			n.Type = res.Type
-			Cgen(n, res)
-		}
-		if !isblank(resok) {
-			Cgen(Nodbool(false), resok)
-		}
-		Patch(q, Pc)
-	}
-}
-
-// generate:
-//	res, resok = x.(T)
-// n.Left is x
-// n.Type is T
-func Cgen_As2dottype(n, res, resok *Node) {
-	if Debug_typeassert > 0 {
-		Warn("type assertion inlined")
-	}
-	//	iface := n.Left
-	//	r1 := iword(iface)
-	//	if n.Left is non-empty interface {
-	//		r1 = *r1
-	//	}
-	//	if r1 == T {
-	//		res = idata(iface)
-	//		resok = true
-	//	} else {
-	//		res = nil
-	//		resok = false
-	//	}
-	//
-	var iface Node
-	Igen(n.Left, &iface, nil)
-	var r1, r2 Node
-	byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte)
-	Regalloc(&r1, byteptr, res)
-	iface.Type = byteptr
-	Cgen(&iface, &r1)
-	if !n.Left.Type.IsEmptyInterface() {
-		// Holding itab, want concrete type in second word.
-		p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1)
-		r2 = r1
-		r2.Op = OINDREG
-		r2.Xoffset = int64(Widthptr)
-		Cgen(&r2, &r1)
-		Patch(p, Pc)
-	}
-	Regalloc(&r2, byteptr, nil)
-	Cgen(typename(n.Type), &r2)
-	p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1)
-	iface.Type = n.Type
-	iface.Xoffset += int64(Widthptr)
-	Cgen(&iface, &r1)
-	if iface.Op != 0 {
-		Regfree(&iface)
-	}
-	Cgen(&r1, res)
-	q := Gbranch(obj.AJMP, nil, 0)
-	Patch(p, Pc)
-
-	fn := syslook("panicdottype")
-	dowidth(fn.Type)
-	call := Nod(OCALLFUNC, fn, nil)
-	call.List.Set([]*Node{&r1, &r2, typename(n.Left.Type)})
-	call.List.Set(ascompatte(OCALLFUNC, call, false, fn.Type.Params(), call.List.Slice(), 0, nil))
-	gen(call)
-	Regfree(&r1)
-	Regfree(&r2)
-	Thearch.Gins(obj.AUNDEF, nil, nil)
-	Patch(q, Pc)
-}
-
-// gather series of offsets
-// >=0 is direct addressed field
-// <0 is pointer to next field (+1)
-func Dotoffset(n *Node, oary []int64, nn **Node) int {
-	var i int
-
-	switch n.Op {
-	case ODOT:
-		if n.Xoffset == BADWIDTH {
-			Dump("bad width in dotoffset", n)
-			Fatalf("bad width in dotoffset")
-		}
-
-		i = Dotoffset(n.Left, oary, nn)
-		if i > 0 {
-			if oary[i-1] >= 0 {
-				oary[i-1] += n.Xoffset
-			} else {
-				oary[i-1] -= n.Xoffset
-			}
-			break
-		}
-
-		if i < 10 {
-			oary[i] = n.Xoffset
-			i++
-		}
-
-	case ODOTPTR:
-		if n.Xoffset == BADWIDTH {
-			Dump("bad width in dotoffset", n)
-			Fatalf("bad width in dotoffset")
-		}
-
-		i = Dotoffset(n.Left, oary, nn)
-		if i < 10 {
-			oary[i] = -(n.Xoffset + 1)
-			i++
-		}
-
-	default:
-		*nn = n
-		return 0
-	}
-
-	if i >= 10 {
-		*nn = nil
-	}
-	return i
-}
-
-// make a new off the books
-func Tempname(nn *Node, t *Type) {
+// make a new Node off the books
+func tempname(nn *Node, t *Type) {
 	if Curfn == nil {
 		Fatalf("no curfn for tempname")
 	}
 	if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
-		Dump("Tempname", Curfn)
+		Dump("tempname", Curfn)
 		Fatalf("adding tempname to wrong closure function")
 	}
 
 	if t == nil {
-		Yyerror("tempname called with nil type")
+		yyerror("tempname called with nil type")
 		t = Types[TINT32]
 	}
 
 	// give each tmp a different name so that there
-	// a chance to registerizer them
-	s := LookupN("autotmp_", statuniqgen)
+	// a chance to registerizer them.
+	// Add a preceding . to avoid clash with legal names.
+	s := lookupN(".autotmp_", statuniqgen)
 	statuniqgen++
-	n := Nod(ONAME, nil, nil)
+	n := nod(ONAME, nil, nil)
 	n.Sym = s
 	s.Def = n
 	n.Type = t
@@ -697,6 +210,7 @@ func Tempname(nn *Node, t *Type) {
 	n.Ullman = 1
 	n.Esc = EscNever
 	n.Name.Curfn = Curfn
+	n.Name.AutoTemp = true
 	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
 
 	dowidth(t)
@@ -706,620 +220,7 @@ func Tempname(nn *Node, t *Type) {
 
 func temp(t *Type) *Node {
 	var n Node
-	Tempname(&n, t)
+	tempname(&n, t)
 	n.Sym.Def.Used = true
 	return n.Orig
 }
-
-func gen(n *Node) {
-	//dump("gen", n);
-
-	lno := setlineno(n)
-
-	wasregalloc := Anyregalloc()
-
-	if n == nil {
-		goto ret
-	}
-
-	if n.Ninit.Len() > 0 {
-		Genlist(n.Ninit)
-	}
-
-	setlineno(n)
-
-	switch n.Op {
-	default:
-		Fatalf("gen: unknown op %v", Nconv(n, FmtShort|FmtSign))
-
-	case OCASE,
-		OFALL,
-		OXCASE,
-		OXFALL,
-		ODCLCONST,
-		ODCLFUNC,
-		ODCLTYPE:
-		break
-
-	case OEMPTY:
-		break
-
-	case OBLOCK:
-		Genlist(n.List)
-
-	case OLABEL:
-		if isblanksym(n.Left.Sym) {
-			break
-		}
-
-		lab := newlab(n)
-
-		// if there are pending gotos, resolve them all to the current pc.
-		var p2 *obj.Prog
-		for p1 := lab.Gotopc; p1 != nil; p1 = p2 {
-			p2 = unpatch(p1)
-			Patch(p1, Pc)
-		}
-
-		lab.Gotopc = nil
-		if lab.Labelpc == nil {
-			lab.Labelpc = Pc
-		}
-
-		if n.Name.Defn != nil {
-			switch n.Name.Defn.Op {
-			// so stmtlabel can find the label
-			case OFOR, OSWITCH, OSELECT:
-				n.Name.Defn.Sym = lab.Sym
-			}
-		}
-
-		// if label is defined, emit jump to it.
-	// otherwise save list of pending gotos in lab->gotopc.
-	// the list is linked through the normal jump target field
-	// to avoid a second list.  (the jumps are actually still
-	// valid code, since they're just going to another goto
-	// to the same label.  we'll unwind it when we learn the pc
-	// of the label in the OLABEL case above.)
-	case OGOTO:
-		lab := newlab(n)
-
-		if lab.Labelpc != nil {
-			gjmp(lab.Labelpc)
-		} else {
-			lab.Gotopc = gjmp(lab.Gotopc)
-		}
-
-	case OBREAK:
-		if n.Left != nil {
-			lab := n.Left.Sym.Label
-			if lab == nil {
-				Yyerror("break label not defined: %v", n.Left.Sym)
-				break
-			}
-
-			lab.Used = true
-			if lab.Breakpc == nil {
-				Yyerror("invalid break label %v", n.Left.Sym)
-				break
-			}
-
-			gjmp(lab.Breakpc)
-			break
-		}
-
-		if breakpc == nil {
-			Yyerror("break is not in a loop")
-			break
-		}
-
-		gjmp(breakpc)
-
-	case OCONTINUE:
-		if n.Left != nil {
-			lab := n.Left.Sym.Label
-			if lab == nil {
-				Yyerror("continue label not defined: %v", n.Left.Sym)
-				break
-			}
-
-			lab.Used = true
-			if lab.Continpc == nil {
-				Yyerror("invalid continue label %v", n.Left.Sym)
-				break
-			}
-
-			gjmp(lab.Continpc)
-			break
-		}
-
-		if continpc == nil {
-			Yyerror("continue is not in a loop")
-			break
-		}
-
-		gjmp(continpc)
-
-	case OFOR:
-		sbreak := breakpc
-		p1 := gjmp(nil)     //		goto test
-		breakpc = gjmp(nil) // break:	goto done
-		scontin := continpc
-		continpc = Pc
-
-		// define break and continue labels
-		lab := stmtlabel(n)
-		if lab != nil {
-			lab.Breakpc = breakpc
-			lab.Continpc = continpc
-		}
-
-		gen(n.Right)                     // contin:	incr
-		Patch(p1, Pc)                    // test:
-		Bgen(n.Left, false, -1, breakpc) //		if(!test) goto break
-		Genlist(n.Nbody)                 //		body
-		gjmp(continpc)
-		Patch(breakpc, Pc) // done:
-		continpc = scontin
-		breakpc = sbreak
-		if lab != nil {
-			lab.Breakpc = nil
-			lab.Continpc = nil
-		}
-
-	case OIF:
-		p1 := gjmp(nil)                         //		goto test
-		p2 := gjmp(nil)                         // p2:		goto else
-		Patch(p1, Pc)                           // test:
-		Bgen(n.Left, false, int(-n.Likely), p2) //		if(!test) goto p2
-		Genlist(n.Nbody)                        //		then
-		p3 := gjmp(nil)                         //		goto done
-		Patch(p2, Pc)                           // else:
-		Genlist(n.Rlist)                        //		else
-		Patch(p3, Pc)                           // done:
-
-	case OSWITCH:
-		sbreak := breakpc
-		p1 := gjmp(nil)     //		goto test
-		breakpc = gjmp(nil) // break:	goto done
-
-		// define break label
-		lab := stmtlabel(n)
-		if lab != nil {
-			lab.Breakpc = breakpc
-		}
-
-		Patch(p1, Pc)      // test:
-		Genlist(n.Nbody)   //		switch(test) body
-		Patch(breakpc, Pc) // done:
-		breakpc = sbreak
-		if lab != nil {
-			lab.Breakpc = nil
-		}
-
-	case OSELECT:
-		sbreak := breakpc
-		p1 := gjmp(nil)     //		goto test
-		breakpc = gjmp(nil) // break:	goto done
-
-		// define break label
-		lab := stmtlabel(n)
-		if lab != nil {
-			lab.Breakpc = breakpc
-		}
-
-		Patch(p1, Pc)      // test:
-		Genlist(n.Nbody)   //		select() body
-		Patch(breakpc, Pc) // done:
-		breakpc = sbreak
-		if lab != nil {
-			lab.Breakpc = nil
-		}
-
-	case ODCL:
-		cgen_dcl(n.Left)
-
-	case OAS:
-		if gen_as_init(n, false) {
-			break
-		}
-		Cgen_as(n.Left, n.Right)
-
-	case OASWB:
-		Cgen_as_wb(n.Left, n.Right, true)
-
-	case OAS2DOTTYPE:
-		cgen_dottype(n.Rlist.First(), n.List.First(), n.List.Second(), needwritebarrier(n.List.First(), n.Rlist.First()))
-
-	case OCALLMETH:
-		cgen_callmeth(n, 0)
-
-	case OCALLINTER:
-		cgen_callinter(n, nil, 0)
-
-	case OCALLFUNC:
-		cgen_call(n, 0)
-
-	case OPROC:
-		cgen_proc(n, 1)
-
-	case ODEFER:
-		cgen_proc(n, 2)
-
-	case ORETURN, ORETJMP:
-		cgen_ret(n)
-
-	// Function calls turned into compiler intrinsics.
-	// At top level, can just ignore the call and make sure to preserve side effects in the argument, if any.
-	case OGETG:
-		// nothing
-	case OSQRT:
-		cgen_discard(n.Left)
-
-	case OCHECKNIL:
-		Cgen_checknil(n.Left)
-
-	case OVARKILL:
-		Gvarkill(n.Left)
-
-	case OVARLIVE:
-		Gvarlive(n.Left)
-	}
-
-ret:
-	if Anyregalloc() != wasregalloc {
-		Dump("node", n)
-		Fatalf("registers left allocated")
-	}
-
-	lineno = lno
-}
-
-func Cgen_as(nl, nr *Node) {
-	Cgen_as_wb(nl, nr, false)
-}
-
-func Cgen_as_wb(nl, nr *Node, wb bool) {
-	if Debug['g'] != 0 {
-		op := "cgen_as"
-		if wb {
-			op = "cgen_as_wb"
-		}
-		Dump(op, nl)
-		Dump(op+" = ", nr)
-	}
-
-	for nr != nil && nr.Op == OCONVNOP {
-		nr = nr.Left
-	}
-
-	if nl == nil || isblank(nl) {
-		cgen_discard(nr)
-		return
-	}
-
-	if nr == nil || iszero(nr) {
-		tl := nl.Type
-		if tl == nil {
-			return
-		}
-		if Isfat(tl) {
-			if nl.Op == ONAME {
-				Gvardef(nl)
-			}
-			Thearch.Clearfat(nl)
-			return
-		}
-
-		Clearslim(nl)
-		return
-	}
-
-	tl := nl.Type
-	if tl == nil {
-		return
-	}
-
-	cgen_wb(nr, nl, wb)
-}
-
-func cgen_callmeth(n *Node, proc int) {
-	// generate a rewrite in n2 for the method call
-	// (p.f)(...) goes to (f)(p,...)
-
-	l := n.Left
-
-	if l.Op != ODOTMETH {
-		Fatalf("cgen_callmeth: not dotmethod: %v", l)
-	}
-
-	n2 := *n
-	n2.Op = OCALLFUNC
-	n2.Left = newname(l.Sym)
-	n2.Left.Type = l.Type
-
-	if n2.Left.Op == ONAME {
-		n2.Left.Class = PFUNC
-	}
-	cgen_call(&n2, proc)
-}
-
-// CgenTemp creates a temporary node, assigns n to it, and returns it.
-func CgenTemp(n *Node) *Node {
-	var tmp Node
-	Tempname(&tmp, n.Type)
-	Cgen(n, &tmp)
-	return &tmp
-}
-
-func checklabels() {
-	for _, lab := range labellist {
-		if lab.Def == nil {
-			for _, n := range lab.Use {
-				yyerrorl(n.Lineno, "label %v not defined", lab.Sym)
-			}
-			continue
-		}
-
-		if lab.Use == nil && !lab.Used {
-			yyerrorl(lab.Def.Lineno, "label %v defined and not used", lab.Sym)
-			continue
-		}
-
-		if lab.Gotopc != nil {
-			Fatalf("label %v never resolved", lab.Sym)
-		}
-		for _, n := range lab.Use {
-			checkgoto(n, lab.Def)
-		}
-	}
-}
-
-// Componentgen copies a composite value by moving its individual components.
-// Slices, strings and interfaces are supported. Small structs or arrays with
-// elements of basic type are also supported.
-// nr is nil when assigning a zero value.
-func Componentgen(nr, nl *Node) bool {
-	return componentgen_wb(nr, nl, false)
-}
-
-// componentgen_wb is like componentgen but if wb==true emits write barriers for pointer updates.
-func componentgen_wb(nr, nl *Node, wb bool) bool {
-	// Don't generate any code for complete copy of a variable into itself.
-	// It's useless, and the VARDEF will incorrectly mark the old value as dead.
-	// (This check assumes that the arguments passed to componentgen did not
-	// themselves come from Igen, or else we could have Op==ONAME but
-	// with a Type and Xoffset describing an individual field, not the entire
-	// variable.)
-	if nl.Op == ONAME && nl == nr {
-		return true
-	}
-
-	// Count number of moves required to move components.
-	// If using write barrier, can only emit one pointer.
-	// TODO(rsc): Allow more pointers, for reflect.Value.
-	const maxMoves = 8
-	n := 0
-	numPtr := 0
-	visitComponents(nl.Type, 0, func(t *Type, offset int64) bool {
-		n++
-		if Simtype[t.Etype] == Tptr && t != itable {
-			numPtr++
-		}
-		return n <= maxMoves && (!wb || numPtr <= 1)
-	})
-	if n > maxMoves || wb && numPtr > 1 {
-		return false
-	}
-
-	// Must call emitVardef after evaluating rhs but before writing to lhs.
-	emitVardef := func() {
-		// Emit vardef if needed.
-		if nl.Op == ONAME {
-			switch nl.Type.Etype {
-			case TARRAY, TSLICE, TSTRING, TINTER, TSTRUCT:
-				Gvardef(nl)
-			}
-		}
-	}
-
-	isConstString := Isconst(nr, CTSTR)
-
-	if !cadable(nl) && nr != nil && !cadable(nr) && !isConstString {
-		return false
-	}
-
-	var nodl Node
-	if cadable(nl) {
-		nodl = *nl
-	} else {
-		if nr != nil && !cadable(nr) && !isConstString {
-			return false
-		}
-		if nr == nil || isConstString || nl.Ullman >= nr.Ullman {
-			Igen(nl, &nodl, nil)
-			defer Regfree(&nodl)
-		}
-	}
-	lbase := nodl.Xoffset
-
-	// Special case: zeroing.
-	var nodr Node
-	if nr == nil {
-		// When zeroing, prepare a register containing zero.
-		// TODO(rsc): Check that this is actually generating the best code.
-		if Thearch.REGZERO != 0 {
-			// cpu has a dedicated zero register
-			Nodreg(&nodr, Types[TUINT], Thearch.REGZERO)
-		} else {
-			// no dedicated zero register
-			var zero Node
-			Nodconst(&zero, nl.Type, 0)
-			Regalloc(&nodr, Types[TUINT], nil)
-			Thearch.Gmove(&zero, &nodr)
-			defer Regfree(&nodr)
-		}
-
-		emitVardef()
-		visitComponents(nl.Type, 0, func(t *Type, offset int64) bool {
-			nodl.Type = t
-			nodl.Xoffset = lbase + offset
-			nodr.Type = t
-			if t.IsFloat() {
-				// TODO(rsc): Cache zero register like we do for integers?
-				Clearslim(&nodl)
-			} else {
-				Thearch.Gmove(&nodr, &nodl)
-			}
-			return true
-		})
-		return true
-	}
-
-	// Special case: assignment of string constant.
-	if isConstString {
-		emitVardef()
-
-		// base
-		nodl.Type = Ptrto(Types[TUINT8])
-		Regalloc(&nodr, Types[Tptr], nil)
-		p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &nodr)
-		Datastring(nr.Val().U.(string), &p.From)
-		p.From.Type = obj.TYPE_ADDR
-		Thearch.Gmove(&nodr, &nodl)
-		Regfree(&nodr)
-
-		// length
-		nodl.Type = Types[Simtype[TUINT]]
-		nodl.Xoffset += int64(Array_nel) - int64(Array_array)
-		Nodconst(&nodr, nodl.Type, int64(len(nr.Val().U.(string))))
-		Thearch.Gmove(&nodr, &nodl)
-		return true
-	}
-
-	// General case: copy nl = nr.
-	nodr = *nr
-	if !cadable(nr) {
-		if nr.Ullman >= UINF && nodl.Op == OINDREG {
-			Fatalf("miscompile")
-		}
-		Igen(nr, &nodr, nil)
-		defer Regfree(&nodr)
-	}
-	rbase := nodr.Xoffset
-
-	if nodl.Op == 0 {
-		Igen(nl, &nodl, nil)
-		defer Regfree(&nodl)
-		lbase = nodl.Xoffset
-	}
-
-	emitVardef()
-	var (
-		ptrType   *Type
-		ptrOffset int64
-	)
-	visitComponents(nl.Type, 0, func(t *Type, offset int64) bool {
-		if wb && Simtype[t.Etype] == Tptr && t != itable {
-			if ptrType != nil {
-				Fatalf("componentgen_wb %v", Tconv(nl.Type, 0))
-			}
-			ptrType = t
-			ptrOffset = offset
-			return true
-		}
-		nodl.Type = t
-		nodl.Xoffset = lbase + offset
-		nodr.Type = t
-		nodr.Xoffset = rbase + offset
-		Thearch.Gmove(&nodr, &nodl)
-		return true
-	})
-	if ptrType != nil {
-		nodl.Type = ptrType
-		nodl.Xoffset = lbase + ptrOffset
-		nodr.Type = ptrType
-		nodr.Xoffset = rbase + ptrOffset
-		cgen_wbptr(&nodr, &nodl)
-	}
-	return true
-}
-
-// visitComponents walks the individual components of the type t,
-// walking into array elements, struct fields, the real and imaginary
-// parts of complex numbers, and on 32-bit systems the high and
-// low halves of 64-bit integers.
-// It calls f for each such component, passing the component (aka element)
-// type and memory offset, assuming t starts at startOffset.
-// If f ever returns false, visitComponents returns false without any more
-// calls to f. Otherwise visitComponents returns true.
-func visitComponents(t *Type, startOffset int64, f func(elem *Type, elemOffset int64) bool) bool {
-	switch t.Etype {
-	case TINT64:
-		if Widthreg == 8 {
-			break
-		}
-		// NOTE: Assuming little endian (signed top half at offset 4).
-		// We don't have any 32-bit big-endian systems.
-		if !Thearch.LinkArch.InFamily(sys.ARM, sys.I386) {
-			Fatalf("unknown 32-bit architecture")
-		}
-		return f(Types[TUINT32], startOffset) &&
-			f(Types[TINT32], startOffset+4)
-
-	case TUINT64:
-		if Widthreg == 8 {
-			break
-		}
-		return f(Types[TUINT32], startOffset) &&
-			f(Types[TUINT32], startOffset+4)
-
-	case TCOMPLEX64:
-		return f(Types[TFLOAT32], startOffset) &&
-			f(Types[TFLOAT32], startOffset+4)
-
-	case TCOMPLEX128:
-		return f(Types[TFLOAT64], startOffset) &&
-			f(Types[TFLOAT64], startOffset+8)
-
-	case TINTER:
-		return f(itable, startOffset) &&
-			f(Ptrto(Types[TUINT8]), startOffset+int64(Widthptr))
-
-	case TSTRING:
-		return f(Ptrto(Types[TUINT8]), startOffset) &&
-			f(Types[Simtype[TUINT]], startOffset+int64(Widthptr))
-
-	case TSLICE:
-		return f(Ptrto(t.Elem()), startOffset+int64(Array_array)) &&
-			f(Types[Simtype[TUINT]], startOffset+int64(Array_nel)) &&
-			f(Types[Simtype[TUINT]], startOffset+int64(Array_cap))
-
-	case TARRAY:
-		// Short-circuit [1e6]struct{}.
-		if t.Elem().Width == 0 {
-			return true
-		}
-
-		for i := int64(0); i < t.NumElem(); i++ {
-			if !visitComponents(t.Elem(), startOffset+i*t.Elem().Width, f) {
-				return false
-			}
-		}
-		return true
-
-	case TSTRUCT:
-		for _, field := range t.Fields().Slice() {
-			if !visitComponents(field.Type, startOffset+field.Offset, f) {
-				return false
-			}
-		}
-		return true
-	}
-	return f(t, startOffset)
-}
-
-func cadable(n *Node) bool {
-	// Note: Not sure why you can have n.Op == ONAME without n.Addable, but you can.
-	return n.Addable && n.Op == ONAME
-}
diff --git a/src/cmd/compile/internal/gc/global_test.go b/src/cmd/compile/internal/gc/global_test.go
index f0139e7..857cf96 100644
--- a/src/cmd/compile/internal/gc/global_test.go
+++ b/src/cmd/compile/internal/gc/global_test.go
@@ -47,14 +47,14 @@ func main() {
 	dst := filepath.Join(dir, "test")
 
 	// Compile source.
-	cmd := exec.Command("go", "build", "-o", dst, src)
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dst, src)
 	out, err := cmd.CombinedOutput()
 	if err != nil {
 		log.Fatalf("could not build target: %v", err)
 	}
 
 	// Check destination to see if scanf code was included.
-	cmd = exec.Command("go", "tool", "nm", dst)
+	cmd = exec.Command(testenv.GoToolPath(t), "tool", "nm", dst)
 	out, err = cmd.CombinedOutput()
 	if err != nil {
 		log.Fatalf("could not read target: %v", err)
@@ -91,7 +91,7 @@ func main() {
 	f.Close()
 
 	// Compile source.
-	cmd := exec.Command("go", "build", "-gcflags", "-S", "-o", filepath.Join(dir, "test"), src)
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags", "-S", "-o", filepath.Join(dir, "test"), src)
 	out, err := cmd.CombinedOutput()
 	if err != nil {
 		log.Fatalf("could not build target: %v", err)
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index 2e4caca..ff33e9c 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -5,7 +5,6 @@
 package gc
 
 import (
-	"bufio"
 	"cmd/compile/internal/ssa"
 	"cmd/internal/bio"
 	"cmd/internal/obj"
@@ -23,9 +22,7 @@ type Pkg struct {
 	Pathsym  *obj.LSym
 	Prefix   string // escaped path for use in symbol table
 	Imported bool   // export data of this package was parsed
-	Exported bool   // import line written in export data
 	Direct   bool   // imported directly
-	Safe     bool   // whether the package is marked as safe
 	Syms     map[string]*Sym
 }
 
@@ -33,7 +30,7 @@ type Pkg struct {
 // an object declared within a package, but Syms are also used to name internal
 // synthesized objects.
 //
-// As a special exception, field and method names that are exported use the Sym
+// As an exception, field and method names that are exported use the Sym
 // associated with localpkg instead of the package that declared them. This
 // allows using Sym pointer equality to test for Go identifier uniqueness when
 // handling selector expressions.
@@ -45,31 +42,17 @@ type Sym struct {
 
 	// saved and restored by dcopy
 	Pkg        *Pkg
-	Name       string // variable name
+	Name       string // object name
 	Def        *Node  // definition: ONAME OTYPE OPACK or OLITERAL
 	Block      int32  // blocknumber to catch redeclaration
 	Lastlineno int32  // last declaration for diagnostic
 
-	Label   *Label // corresponding label (ephemeral)
-	Origpkg *Pkg   // original package for . import
+	Label   *Node // corresponding label (ephemeral)
+	Origpkg *Pkg  // original package for . import
 	Lsym    *obj.LSym
 	Fsym    *Sym // funcsym
 }
 
-type Label struct {
-	Sym *Sym
-	Def *Node
-	Use []*Node
-
-	// for use during gen
-	Gotopc   *obj.Prog // pointer to unresolved gotos
-	Labelpc  *obj.Prog // pointer to code
-	Breakpc  *obj.Prog // pointer to code
-	Continpc *obj.Prog // pointer to code
-
-	Used bool
-}
-
 type SymFlags uint8
 
 const (
@@ -80,6 +63,7 @@ const (
 	SymSiggen
 	SymAsm
 	SymAlgGen
+	SymAlias // alias, original is Sym.Def.Sym
 )
 
 // The Class of a variable/function describes the "storage class"
@@ -108,11 +92,11 @@ const (
 // 	uchar	nel[4];		// number of elements
 // 	uchar	cap[4];		// allocated number of elements
 // } Array;
-var Array_array int // runtime offsetof(Array,array) - same for String
+var array_array int // runtime offsetof(Array,array) - same for String
 
-var Array_nel int // runtime offsetof(Array,nel) - same for String
+var array_nel int // runtime offsetof(Array,nel) - same for String
 
-var Array_cap int // runtime offsetof(Array,cap)
+var array_cap int // runtime offsetof(Array,cap)
 
 var sizeof_Array int // runtime sizeof(Array)
 
@@ -135,8 +119,12 @@ var linkobj string
 
 var bout *bio.Writer
 
+// nerrors is the number of compiler errors reported
+// since the last call to saveerrors.
 var nerrors int
 
+// nsavederrors is the total number of compiler errors
+// reported before the last call to saveerrors.
 var nsavederrors int
 
 var nsyntaxerrors int
@@ -156,8 +144,6 @@ var Debug_typeassert int
 
 var localpkg *Pkg // package being compiled
 
-var autopkg *Pkg // fake package for allocating auto variables
-
 var importpkg *Pkg // package being imported
 
 var itabpkg *Pkg // fake pkg for itab entries
@@ -187,13 +173,13 @@ var localimport string
 
 var asmhdr string
 
-var Simtype [NTYPE]EType
+var simtype [NTYPE]EType
 
 var (
 	isforw    [NTYPE]bool
-	Isint     [NTYPE]bool
-	Isfloat   [NTYPE]bool
-	Iscomplex [NTYPE]bool
+	isInt     [NTYPE]bool
+	isFloat   [NTYPE]bool
+	isComplex [NTYPE]bool
 	issimple  [NTYPE]bool
 )
 
@@ -215,9 +201,9 @@ var (
 	iscmp [OEND]bool
 )
 
-var Minintval [NTYPE]*Mpint
+var minintval [NTYPE]*Mpint
 
-var Maxintval [NTYPE]*Mpint
+var maxintval [NTYPE]*Mpint
 
 var minfltval [NTYPE]*Mpflt
 
@@ -233,11 +219,9 @@ var funcsyms []*Node
 
 var dclcontext Class // PEXTERN/PAUTO
 
-var incannedimport int
-
 var statuniqgen int // name generator for static temps
 
-var iota_ int32
+var iota_ int64
 
 var lastconst []*Node
 
@@ -289,19 +273,13 @@ var Ctxt *obj.Link
 
 var writearchive bool
 
-var bstdout *bufio.Writer
-
 var Nacl bool
 
-var continpc *obj.Prog
-
-var breakpc *obj.Prog
-
-var Pc *obj.Prog
+var pc *obj.Prog
 
 var nodfp *Node
 
-var Disable_checknil int
+var disable_checknil int
 
 // interface to back end
 
@@ -359,89 +337,20 @@ const (
 
 	// Instruction updates whichever of from/to is type D_OREG. (ppc64)
 	PostInc = 1 << 29
+
+	// Optional 3rd input operand, only ever read.
+	From3Read = 1 << 30
 )
 
 type Arch struct {
 	LinkArch *obj.LinkArch
 
-	REGSP        int
-	REGCTXT      int
-	REGCALLX     int // BX
-	REGCALLX2    int // AX
-	REGRETURN    int // AX
-	REGMIN       int
-	REGMAX       int
-	REGZERO      int // architectural zero register, if available
-	FREGMIN      int
-	FREGMAX      int
-	MAXWIDTH     int64
-	ReservedRegs []int
-
-	AddIndex            func(*Node, int64, *Node) bool // optional
-	Betypeinit          func()
-	Bgen_float          func(*Node, bool, int, *obj.Prog) // optional
-	Cgen64              func(*Node, *Node)                // only on 32-bit systems
-	Cgenindex           func(*Node, *Node, bool) *obj.Prog
-	Cgen_bmul           func(Op, *Node, *Node, *Node) bool
-	Cgen_float          func(*Node, *Node) // optional
-	Cgen_hmul           func(*Node, *Node, *Node)
-	RightShiftWithCarry func(*Node, uint, *Node)  // only on systems without RROTC instruction
-	AddSetCarry         func(*Node, *Node, *Node) // only on systems when ADD does not update carry flag
-	Cgen_shift          func(Op, bool, *Node, *Node, *Node)
-	Clearfat            func(*Node)
-	Cmp64               func(*Node, *Node, Op, int, *obj.Prog) // only on 32-bit systems
-	Defframe            func(*obj.Prog)
-	Dodiv               func(Op, *Node, *Node, *Node)
-	Excise              func(*Flow)
-	Expandchecks        func(*obj.Prog)
-	Getg                func(*Node)
-	Gins                func(obj.As, *Node, *Node) *obj.Prog
-
-	// Ginscmp generates code comparing n1 to n2 and jumping away if op is satisfied.
-	// The returned prog should be Patch'ed with the jump target.
-	// If op is not satisfied, code falls through to the next emitted instruction.
-	// Likely is the branch prediction hint: +1 for likely, -1 for unlikely, 0 for no opinion.
-	//
-	// Ginscmp must be able to handle all kinds of arguments for n1 and n2,
-	// not just simple registers, although it can assume that there are no
-	// function calls needed during the evaluation, and on 32-bit systems
-	// the values are guaranteed not to be 64-bit values, so no in-memory
-	// temporaries are necessary.
-	Ginscmp func(op Op, t *Type, n1, n2 *Node, likely int) *obj.Prog
-
-	// Ginsboolval inserts instructions to convert the result
-	// of a just-completed comparison to a boolean value.
-	// The first argument is the conditional jump instruction
-	// corresponding to the desired value.
-	// The second argument is the destination.
-	// If not present, Ginsboolval will be emulated with jumps.
-	Ginsboolval func(obj.As, *Node)
-
-	Ginscon      func(obj.As, int64, *Node)
-	Ginsnop      func()
-	Gmove        func(*Node, *Node)
-	Igenindex    func(*Node, *Node, bool) *obj.Prog
-	Peep         func(*obj.Prog)
-	Proginfo     func(*obj.Prog) // fills in Prog.Info
-	Regtyp       func(*obj.Addr) bool
-	Sameaddr     func(*obj.Addr, *obj.Addr) bool
-	Smallindir   func(*obj.Addr, *obj.Addr) bool
-	Stackaddr    func(*obj.Addr) bool
-	Blockcopy    func(*Node, *Node, int64, int64, int64)
-	Sudoaddable  func(obj.As, *Node, *obj.Addr) bool
-	Sudoclean    func()
-	Excludedregs func() uint64
-	RtoB         func(int) uint64
-	FtoB         func(int) uint64
-	BtoR         func(uint64) int
-	BtoF         func(uint64) int
-	Optoas       func(Op, *Type) obj.As
-	Doregbits    func(int) uint64
-	Regnames     func(*int) []string
-	Use387       bool // should 8g use 387 FP instructions instead of sse2.
-
-	// SSARegToReg maps ssa register numbers to obj register numbers.
-	SSARegToReg []int16
+	REGSP    int
+	MAXWIDTH int64
+
+	Defframe func(*obj.Prog)
+	Proginfo func(*obj.Prog) ProgInfo
+	Use387   bool // should 8g use 387 FP instructions instead of sse2.
 
 	// SSAMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
 	SSAMarkMoves func(*SSAGenState, *ssa.Block)
@@ -458,23 +367,18 @@ var pcloc int32
 
 var Thearch Arch
 
-var Newproc *Node
-
-var Deferproc *Node
-
-var Deferreturn *Node
-
-var Panicindex *Node
-
-var panicslice *Node
-
-var panicdivide *Node
-
-var throwreturn *Node
-
-var growslice *Node
-
-var writebarrierptr *Node
-var typedmemmove *Node
-
-var panicdottype *Node
+var (
+	Newproc,
+	Deferproc,
+	Deferreturn,
+	panicindex,
+	panicslice,
+	panicdivide,
+	growslice,
+	panicdottype,
+	panicnildottype,
+	assertE2I,
+	assertE2I2,
+	assertI2I,
+	assertI2I2 *Node
+)
diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go
index 4943d9d..1e86363 100644
--- a/src/cmd/compile/internal/gc/gsubr.go
+++ b/src/cmd/compile/internal/gc/gsubr.go
@@ -1,5 +1,5 @@
 // Derived from Inferno utils/6c/txt.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/txt.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -30,96 +30,15 @@
 
 package gc
 
-import (
-	"cmd/internal/obj"
-	"cmd/internal/sys"
-	"fmt"
-	"runtime"
-	"strings"
-)
-
-var (
-	ddumped bool
-	dfirst  *obj.Prog
-	dpc     *obj.Prog
-)
-
-// Is this node a memory operand?
-func Ismem(n *Node) bool {
-	switch n.Op {
-	case OITAB,
-		OSPTR,
-		OLEN,
-		OCAP,
-		OINDREG,
-		ONAME,
-		OCLOSUREVAR:
-		return true
-
-	case OADDR:
-		// amd64 and s390x use PC relative addressing.
-		// TODO(rsc): not sure why ppc64 needs this too.
-		return Thearch.LinkArch.InFamily(sys.AMD64, sys.PPC64, sys.S390X)
-	}
-
-	return false
-}
-
-func Samereg(a *Node, b *Node) bool {
-	if a == nil || b == nil {
-		return false
-	}
-	if a.Op != OREGISTER {
-		return false
-	}
-	if b.Op != OREGISTER {
-		return false
-	}
-	if a.Reg != b.Reg {
-		return false
-	}
-	return true
-}
-
-func Gbranch(as obj.As, t *Type, likely int) *obj.Prog {
-	p := Prog(as)
-	p.To.Type = obj.TYPE_BRANCH
-	p.To.Val = nil
-	if as != obj.AJMP && likely != 0 && !Thearch.LinkArch.InFamily(sys.PPC64, sys.ARM64, sys.MIPS64, sys.S390X) {
-		p.From.Type = obj.TYPE_CONST
-		if likely > 0 {
-			p.From.Offset = 1
-		}
-	}
-
-	if Debug['g'] != 0 {
-		fmt.Printf("%v\n", p)
-	}
-
-	return p
-}
+import "cmd/internal/obj"
 
 func Prog(as obj.As) *obj.Prog {
 	var p *obj.Prog
 
-	if as == obj.AGLOBL {
-		if ddumped {
-			Fatalf("already dumped data")
-		}
-		if dpc == nil {
-			dpc = Ctxt.NewProg()
-			dfirst = dpc
-		}
-
-		p = dpc
-		dpc = Ctxt.NewProg()
-		p.Link = dpc
-	} else {
-		p = Pc
-		Pc = Ctxt.NewProg()
-		Clearp(Pc)
-		p.Link = Pc
-	}
+	p = pc
+	pc = Ctxt.NewProg()
+	Clearp(pc)
+	p.Link = pc
 
 	if lineno == 0 && Debug['K'] != 0 {
 		Warn("prog: line 0")
@@ -130,31 +49,6 @@ func Prog(as obj.As) *obj.Prog {
 	return p
 }
 
-func Nodreg(n *Node, t *Type, r int) {
-	if t == nil {
-		Fatalf("nodreg: t nil")
-	}
-
-	*n = Node{}
-	n.Op = OREGISTER
-	n.Addable = true
-	ullmancalc(n)
-	n.Reg = int16(r)
-	n.Type = t
-}
-
-func Nodindreg(n *Node, t *Type, r int) {
-	Nodreg(n, t, r)
-	n.Op = OINDREG
-}
-
-func Afunclit(a *obj.Addr, n *Node) {
-	if a.Type == obj.TYPE_ADDR && a.Name == obj.NAME_EXTERN {
-		a.Type = obj.TYPE_MEM
-		a.Sym = Linksym(n.Sym)
-	}
-}
-
 func Clearp(p *obj.Prog) {
 	obj.Nopout(p)
 	p.As = obj.AEND
@@ -162,77 +56,33 @@ func Clearp(p *obj.Prog) {
 	pcloc++
 }
 
-func dumpdata() {
-	ddumped = true
-	if dfirst == nil {
-		return
-	}
-	newplist()
-	*Pc = *dfirst
-	Pc = dpc
-	Clearp(Pc)
-}
-
-func flushdata() {
-	if dfirst == nil {
-		return
-	}
-	newplist()
-	*Pc = *dfirst
-	Pc = dpc
-	Clearp(Pc)
-	dfirst = nil
-	dpc = nil
-}
-
-// Fixup instructions after allocauto (formerly compactframe) has moved all autos around.
-func fixautoused(p *obj.Prog) {
-	for lp := &p; ; {
-		p = *lp
-		if p == nil {
-			break
-		}
-		if p.As == obj.ATYPE && p.From.Node != nil && p.From.Name == obj.NAME_AUTO && !((p.From.Node).(*Node)).Used {
-			*lp = p.Link
-			continue
-		}
-
-		if (p.As == obj.AVARDEF || p.As == obj.AVARKILL || p.As == obj.AVARLIVE) && p.To.Node != nil && !((p.To.Node).(*Node)).Used {
-			// Cannot remove VARDEF instruction, because - unlike TYPE handled above -
-			// VARDEFs are interspersed with other code, and a jump might be using the
-			// VARDEF as a target. Replace with a no-op instead. A later pass will remove
-			// the no-ops.
-			obj.Nopout(p)
-
-			continue
-		}
-
-		if p.From.Name == obj.NAME_AUTO && p.From.Node != nil {
-			p.From.Offset += stkdelta[p.From.Node.(*Node)]
-		}
-
-		if p.To.Name == obj.NAME_AUTO && p.To.Node != nil {
-			p.To.Offset += stkdelta[p.To.Node.(*Node)]
-		}
-
-		lp = &p.Link
-	}
+func Appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
+	q := Ctxt.NewProg()
+	Clearp(q)
+	q.As = as
+	q.Lineno = p.Lineno
+	q.From.Type = ftype
+	q.From.Reg = freg
+	q.From.Offset = foffset
+	q.To.Type = ttype
+	q.To.Reg = treg
+	q.To.Offset = toffset
+	q.Link = p.Link
+	p.Link = q
+	return q
 }
 
 func ggloblnod(nam *Node) {
-	p := Thearch.Gins(obj.AGLOBL, nam, nil)
-	p.Lineno = nam.Lineno
-	p.From.Sym.Gotype = Linksym(ngotype(nam))
-	p.To.Sym = nil
-	p.To.Type = obj.TYPE_CONST
-	p.To.Offset = nam.Type.Width
-	p.From3 = new(obj.Addr)
+	s := Linksym(nam.Sym)
+	s.Gotype = Linksym(ngotype(nam))
+	flags := 0
 	if nam.Name.Readonly {
-		p.From3.Offset = obj.RODATA
+		flags = obj.RODATA
 	}
 	if nam.Type != nil && !haspointers(nam.Type) {
-		p.From3.Offset |= obj.NOPTR
+		flags |= obj.NOPTR
 	}
+	Ctxt.Globl(s, nam.Type.Width, flags)
 }
 
 func ggloblsym(s *Sym, width int32, flags int16) {
@@ -240,40 +90,21 @@ func ggloblsym(s *Sym, width int32, flags int16) {
 }
 
 func ggloblLSym(s *obj.LSym, width int32, flags int16) {
-	p := Thearch.Gins(obj.AGLOBL, nil, nil)
-	p.From.Type = obj.TYPE_MEM
-	p.From.Name = obj.NAME_EXTERN
-	p.From.Sym = s
 	if flags&obj.LOCAL != 0 {
-		p.From.Sym.Local = true
+		s.Set(obj.AttrLocal, true)
 		flags &^= obj.LOCAL
 	}
-	p.To.Type = obj.TYPE_CONST
-	p.To.Offset = int64(width)
-	p.From3 = new(obj.Addr)
-	p.From3.Offset = int64(flags)
-}
-
-func gjmp(to *obj.Prog) *obj.Prog {
-	p := Gbranch(obj.AJMP, nil, 0)
-	if to != nil {
-		Patch(p, to)
-	}
-	return p
+	Ctxt.Globl(s, int64(width), int(flags))
 }
 
 func gtrack(s *Sym) {
-	p := Thearch.Gins(obj.AUSEFIELD, nil, nil)
+	p := Gins(obj.AUSEFIELD, nil, nil)
 	p.From.Type = obj.TYPE_MEM
 	p.From.Name = obj.NAME_EXTERN
 	p.From.Sym = Linksym(s)
 }
 
-func gused(n *Node) {
-	Thearch.Gins(obj.ANOP, n, nil) // used
-}
-
-func Isfat(t *Type) bool {
+func isfat(t *Type) bool {
 	if t != nil {
 		switch t.Etype {
 		case TSTRUCT, TARRAY, TSLICE, TSTRING,
@@ -285,23 +116,6 @@ func Isfat(t *Type) bool {
 	return false
 }
 
-// Sweep the prog list to mark any used nodes.
-func markautoused(p *obj.Prog) {
-	for ; p != nil; p = p.Link {
-		if p.As == obj.ATYPE || p.As == obj.AVARDEF || p.As == obj.AVARKILL {
-			continue
-		}
-
-		if p.From.Node != nil {
-			((p.From.Node).(*Node)).Used = true
-		}
-
-		if p.To.Node != nil {
-			((p.To.Node).(*Node)).Used = true
-		}
-	}
-}
-
 // Naddr rewrites a to refer to n.
 // It assumes that a is zeroed on entry.
 func Naddr(a *obj.Addr, n *Node) {
@@ -309,208 +123,50 @@ func Naddr(a *obj.Addr, n *Node) {
 		return
 	}
 
-	if n.Type != nil && n.Type.Etype != TIDEAL {
-		// TODO(rsc): This is undone by the selective clearing of width below,
-		// to match architectures that were not as aggressive in setting width
-		// during naddr. Those widths must be cleared to avoid triggering
-		// failures in gins when it detects real but heretofore latent (and one
-		// hopes innocuous) type mismatches.
-		// The type mismatches should be fixed and the clearing below removed.
-		dowidth(n.Type)
-
-		a.Width = n.Type.Width
-	}
-
-	switch n.Op {
-	default:
-		a := a // copy to let escape into Ctxt.Dconv
+	if n.Op != ONAME {
 		Debug['h'] = 1
 		Dump("naddr", n)
 		Fatalf("naddr: bad %v %v", n.Op, Ctxt.Dconv(a))
+	}
 
-	case OREGISTER:
-		a.Type = obj.TYPE_REG
-		a.Reg = n.Reg
-		a.Sym = nil
-		if Thearch.LinkArch.Family == sys.I386 { // TODO(rsc): Never clear a->width.
-			a.Width = 0
-		}
-
-	case OINDREG:
-		a.Type = obj.TYPE_MEM
-		a.Reg = n.Reg
-		a.Sym = Linksym(n.Sym)
-		a.Offset = n.Xoffset
-		if a.Offset != int64(int32(a.Offset)) {
-			Yyerror("offset %d too large for OINDREG", a.Offset)
-		}
-		if Thearch.LinkArch.Family == sys.I386 { // TODO(rsc): Never clear a->width.
-			a.Width = 0
-		}
-
-	case OCLOSUREVAR:
-		if !Curfn.Func.Needctxt {
-			Fatalf("closurevar without needctxt")
-		}
-		a.Type = obj.TYPE_MEM
-		a.Reg = int16(Thearch.REGCTXT)
-		a.Sym = nil
-		a.Offset = n.Xoffset
-
-	case OCFUNC:
-		Naddr(a, n.Left)
-		a.Sym = Linksym(n.Left.Sym)
-
-	case ONAME:
-		a.Etype = 0
-		if n.Type != nil {
-			a.Etype = uint8(Simtype[n.Type.Etype])
-		}
-		a.Offset = n.Xoffset
-		s := n.Sym
-		a.Node = n.Orig
-
-		//if(a->node >= (Node*)&n)
-		//	fatal("stack node");
-		if s == nil {
-			s = Lookup(".noname")
-		}
-		if n.Name.Method && n.Type != nil && n.Type.Sym != nil && n.Type.Sym.Pkg != nil {
-			s = Pkglookup(s.Name, n.Type.Sym.Pkg)
-		}
-
-		a.Type = obj.TYPE_MEM
-		switch n.Class {
-		default:
-			Fatalf("naddr: ONAME class %v %d\n", n.Sym, n.Class)
-
-		case PEXTERN:
-			a.Name = obj.NAME_EXTERN
-
-		case PAUTO:
-			a.Name = obj.NAME_AUTO
-
-		case PPARAM, PPARAMOUT:
-			a.Name = obj.NAME_PARAM
-
-		case PFUNC:
-			a.Name = obj.NAME_EXTERN
-			a.Type = obj.TYPE_ADDR
-			a.Width = int64(Widthptr)
-			s = funcsym(s)
-		}
-
-		a.Sym = Linksym(s)
-
-	case ODOT:
-		// A special case to make write barriers more efficient.
-		// Taking the address of the first field of a named struct
-		// is the same as taking the address of the struct.
-		if !n.Left.Type.IsStruct() || n.Left.Type.Field(0).Sym != n.Sym {
-			Debug['h'] = 1
-			Dump("naddr", n)
-			Fatalf("naddr: bad %v %v", n.Op, Ctxt.Dconv(a))
-		}
-		Naddr(a, n.Left)
-
-	case OLITERAL:
-		if Thearch.LinkArch.Family == sys.I386 {
-			a.Width = 0
-		}
-		switch u := n.Val().U.(type) {
-		default:
-			Fatalf("naddr: const %v", Tconv(n.Type, FmtLong))
-
-		case *Mpflt:
-			a.Type = obj.TYPE_FCONST
-			a.Val = u.Float64()
-
-		case *Mpint:
-			a.Sym = nil
-			a.Type = obj.TYPE_CONST
-			a.Offset = u.Int64()
-
-		case string:
-			datagostring(u, a)
-
-		case bool:
-			a.Sym = nil
-			a.Type = obj.TYPE_CONST
-			a.Offset = int64(obj.Bool2int(u))
-
-		case *NilVal:
-			a.Sym = nil
-			a.Type = obj.TYPE_CONST
-			a.Offset = 0
-		}
-
-	case OADDR:
-		Naddr(a, n.Left)
-		a.Etype = uint8(Tptr)
-		if !Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) { // TODO(rsc): Do this even for these architectures.
-			a.Width = int64(Widthptr)
-		}
-		if a.Type != obj.TYPE_MEM {
-			a := a // copy to let escape into Ctxt.Dconv
-			Fatalf("naddr: OADDR %v (from %v)", Ctxt.Dconv(a), n.Left.Op)
-		}
-		a.Type = obj.TYPE_ADDR
-
-		// itable of interface value
-	case OITAB:
-		Naddr(a, n.Left)
+	a.Offset = n.Xoffset
+	s := n.Sym
+	a.Node = n.Orig
 
-		if a.Type == obj.TYPE_CONST && a.Offset == 0 {
-			break // itab(nil)
-		}
-		a.Etype = uint8(Tptr)
-		a.Width = int64(Widthptr)
+	if s == nil {
+		Fatalf("naddr: nil sym %v", n)
+	}
 
-		// pointer in a string or slice
-	case OSPTR:
-		Naddr(a, n.Left)
+	a.Type = obj.TYPE_MEM
+	switch n.Class {
+	default:
+		Fatalf("naddr: ONAME class %v %d\n", n.Sym, n.Class)
 
-		if a.Type == obj.TYPE_CONST && a.Offset == 0 {
-			break // ptr(nil)
-		}
-		a.Etype = uint8(Simtype[Tptr])
-		a.Offset += int64(Array_array)
-		a.Width = int64(Widthptr)
+	case PEXTERN, PFUNC:
+		a.Name = obj.NAME_EXTERN
 
-		// len of string or slice
-	case OLEN:
-		Naddr(a, n.Left)
+	case PAUTO:
+		a.Name = obj.NAME_AUTO
 
-		if a.Type == obj.TYPE_CONST && a.Offset == 0 {
-			break // len(nil)
-		}
-		a.Etype = uint8(Simtype[TUINT])
-		a.Offset += int64(Array_nel)
-		if Thearch.LinkArch.Family != sys.ARM { // TODO(rsc): Do this even on arm.
-			a.Width = int64(Widthint)
-		}
+	case PPARAM, PPARAMOUT:
+		a.Name = obj.NAME_PARAM
+	}
 
-		// cap of string or slice
-	case OCAP:
-		Naddr(a, n.Left)
+	a.Sym = Linksym(s)
+}
 
-		if a.Type == obj.TYPE_CONST && a.Offset == 0 {
-			break // cap(nil)
-		}
-		a.Etype = uint8(Simtype[TUINT])
-		a.Offset += int64(Array_cap)
-		if Thearch.LinkArch.Family != sys.ARM { // TODO(rsc): Do this even on arm.
-			a.Width = int64(Widthint)
-		}
-	}
+func Addrconst(a *obj.Addr, v int64) {
+	a.Sym = nil
+	a.Type = obj.TYPE_CONST
+	a.Offset = v
 }
 
 func newplist() *obj.Plist {
 	pl := obj.Linknewplist(Ctxt)
 
-	Pc = Ctxt.NewProg()
-	Clearp(Pc)
-	pl.Firstpc = Pc
+	pc = Ctxt.NewProg()
+	Clearp(pc)
+	pl.Firstpc = pc
 
 	return pl
 }
@@ -545,8 +201,8 @@ func nodarg(t interface{}, fp int) *Node {
 		funarg = t.StructType().Funarg
 
 		// Build fake variable name for whole arg struct.
-		n = Nod(ONAME, nil, nil)
-		n.Sym = Lookup(".args")
+		n = nod(ONAME, nil, nil)
+		n.Sym = lookup(".args")
 		n.Type = t
 		first := t.Field(0)
 		if first == nil {
@@ -594,7 +250,7 @@ func nodarg(t interface{}, fp int) *Node {
 		// Build fake name for individual variable.
 		// This is safe because if there was a real declared name
 		// we'd have used it above.
-		n = Nod(ONAME, nil, nil)
+		n = nod(ONAME, nil, nil)
 		n.Type = t.Type
 		n.Sym = t.Sym
 		if t.Offset == BADWIDTH {
@@ -609,7 +265,7 @@ func nodarg(t interface{}, fp int) *Node {
 	// or else the assignment to _ will be
 	// discarded during code generation.
 	if isblank(n) {
-		n.Sym = Lookup("__")
+		n.Sym = lookup("__")
 	}
 
 	switch fp {
@@ -617,8 +273,7 @@ func nodarg(t interface{}, fp int) *Node {
 		Fatalf("bad fp")
 
 	case 0: // preparing arguments for call
-		n.Op = OINDREG
-		n.Reg = int16(Thearch.REGSP)
+		n.Op = OINDREGSP
 		n.Xoffset += Ctxt.FixedFrameSize()
 
 	case 1: // reading arguments inside call
@@ -641,246 +296,17 @@ func Patch(p *obj.Prog, to *obj.Prog) {
 	p.To.Offset = to.Pc
 }
 
-func unpatch(p *obj.Prog) *obj.Prog {
-	if p.To.Type != obj.TYPE_BRANCH {
-		Fatalf("unpatch: not a branch")
-	}
-	q, _ := p.To.Val.(*obj.Prog)
-	p.To.Val = nil
-	p.To.Offset = 0
-	return q
-}
-
-var reg [100]int       // count of references to reg
-var regstk [100][]byte // allocation sites, when -v is given
-
-func GetReg(r int) int {
-	return reg[r-Thearch.REGMIN]
-}
-func SetReg(r, v int) {
-	reg[r-Thearch.REGMIN] = v
-}
-
-func ginit() {
-	for r := range reg {
-		reg[r] = 1
-	}
-
-	for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ {
-		reg[r-Thearch.REGMIN] = 0
-	}
-	for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ {
-		reg[r-Thearch.REGMIN] = 0
-	}
-
-	for _, r := range Thearch.ReservedRegs {
-		reg[r-Thearch.REGMIN] = 1
-	}
-}
-
-func gclean() {
-	for _, r := range Thearch.ReservedRegs {
-		reg[r-Thearch.REGMIN]--
-	}
-
-	for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ {
-		n := reg[r-Thearch.REGMIN]
-		if n != 0 {
-			if Debug['v'] != 0 {
-				Regdump()
-			}
-			Yyerror("reg %v left allocated", obj.Rconv(r))
-		}
-	}
-
-	for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ {
-		n := reg[r-Thearch.REGMIN]
-		if n != 0 {
-			if Debug['v'] != 0 {
-				Regdump()
-			}
-			Yyerror("reg %v left allocated", obj.Rconv(r))
-		}
-	}
-}
-
-func Anyregalloc() bool {
-	n := 0
-	for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ {
-		if reg[r-Thearch.REGMIN] == 0 {
-			n++
-		}
-	}
-	return n > len(Thearch.ReservedRegs)
-}
-
-// allocate register of type t, leave in n.
-// if o != N, o may be reusable register.
-// caller must Regfree(n).
-func Regalloc(n *Node, t *Type, o *Node) {
-	if t == nil {
-		Fatalf("regalloc: t nil")
-	}
-	et := Simtype[t.Etype]
-	if Ctxt.Arch.RegSize == 4 && (et == TINT64 || et == TUINT64) {
-		Fatalf("regalloc 64bit")
-	}
-
-	var i int
-Switch:
-	switch et {
+// Gins inserts instruction as. f is from, t is to.
+func Gins(as obj.As, f, t *Node) *obj.Prog {
+	switch as {
+	case obj.AVARKILL, obj.AVARLIVE, obj.AVARDEF, obj.ATYPE,
+		obj.ATEXT, obj.AFUNCDATA, obj.AUSEFIELD:
 	default:
-		Fatalf("regalloc: unknown type %v", t)
-
-	case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TPTR32, TPTR64, TBOOL:
-		if o != nil && o.Op == OREGISTER {
-			i = int(o.Reg)
-			if Thearch.REGMIN <= i && i <= Thearch.REGMAX {
-				break Switch
-			}
-		}
-		for i = Thearch.REGMIN; i <= Thearch.REGMAX; i++ {
-			if reg[i-Thearch.REGMIN] == 0 {
-				break Switch
-			}
-		}
-		Flusherrors()
-		Regdump()
-		Fatalf("out of fixed registers")
-
-	case TFLOAT32, TFLOAT64:
-		if Thearch.Use387 {
-			i = Thearch.FREGMIN // x86.REG_F0
-			break Switch
-		}
-		if o != nil && o.Op == OREGISTER {
-			i = int(o.Reg)
-			if Thearch.FREGMIN <= i && i <= Thearch.FREGMAX {
-				break Switch
-			}
-		}
-		for i = Thearch.FREGMIN; i <= Thearch.FREGMAX; i++ {
-			if reg[i-Thearch.REGMIN] == 0 { // note: REGMIN, not FREGMIN
-				break Switch
-			}
-		}
-		Flusherrors()
-		Regdump()
-		Fatalf("out of floating registers")
-
-	case TCOMPLEX64, TCOMPLEX128:
-		Tempname(n, t)
-		return
-	}
-
-	ix := i - Thearch.REGMIN
-	if reg[ix] == 0 && Debug['v'] > 0 {
-		if regstk[ix] == nil {
-			regstk[ix] = make([]byte, 4096)
-		}
-		stk := regstk[ix]
-		n := runtime.Stack(stk[:cap(stk)], false)
-		regstk[ix] = stk[:n]
-	}
-	reg[ix]++
-	Nodreg(n, t, i)
-}
-
-func Regfree(n *Node) {
-	if n.Op == ONAME {
-		return
-	}
-	if n.Op != OREGISTER && n.Op != OINDREG {
-		Fatalf("regfree: not a register")
-	}
-	i := int(n.Reg)
-	if i == Thearch.REGSP {
-		return
-	}
-	switch {
-	case Thearch.REGMIN <= i && i <= Thearch.REGMAX,
-		Thearch.FREGMIN <= i && i <= Thearch.FREGMAX:
-		// ok
-	default:
-		Fatalf("regfree: reg out of range")
-	}
-
-	i -= Thearch.REGMIN
-	if reg[i] <= 0 {
-		Fatalf("regfree: reg not allocated")
-	}
-	reg[i]--
-	if reg[i] == 0 {
-		regstk[i] = regstk[i][:0]
-	}
-}
-
-// Reginuse reports whether r is in use.
-func Reginuse(r int) bool {
-	switch {
-	case Thearch.REGMIN <= r && r <= Thearch.REGMAX,
-		Thearch.FREGMIN <= r && r <= Thearch.FREGMAX:
-		// ok
-	default:
-		Fatalf("reginuse: reg out of range")
+		Fatalf("unhandled gins op %v", as)
 	}
 
-	return reg[r-Thearch.REGMIN] > 0
-}
-
-// Regrealloc(n) undoes the effect of Regfree(n),
-// so that a register can be given up but then reclaimed.
-func Regrealloc(n *Node) {
-	if n.Op != OREGISTER && n.Op != OINDREG {
-		Fatalf("regrealloc: not a register")
-	}
-	i := int(n.Reg)
-	if i == Thearch.REGSP {
-		return
-	}
-	switch {
-	case Thearch.REGMIN <= i && i <= Thearch.REGMAX,
-		Thearch.FREGMIN <= i && i <= Thearch.FREGMAX:
-		// ok
-	default:
-		Fatalf("regrealloc: reg out of range")
-	}
-
-	i -= Thearch.REGMIN
-	if reg[i] == 0 && Debug['v'] > 0 {
-		if regstk[i] == nil {
-			regstk[i] = make([]byte, 4096)
-		}
-		stk := regstk[i]
-		n := runtime.Stack(stk[:cap(stk)], false)
-		regstk[i] = stk[:n]
-	}
-	reg[i]++
-}
-
-func Regdump() {
-	if Debug['v'] == 0 {
-		fmt.Printf("run compiler with -v for register allocation sites\n")
-		return
-	}
-
-	dump := func(r int) {
-		stk := regstk[r-Thearch.REGMIN]
-		if len(stk) == 0 {
-			return
-		}
-		fmt.Printf("reg %v allocated at:\n", obj.Rconv(r))
-		fmt.Printf("\t%s\n", strings.Replace(strings.TrimSpace(string(stk)), "\n", "\n\t", -1))
-	}
-
-	for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ {
-		if reg[r-Thearch.REGMIN] != 0 {
-			dump(r)
-		}
-	}
-	for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ {
-		if reg[r-Thearch.REGMIN] == 0 {
-			dump(r)
-		}
-	}
+	p := Prog(as)
+	Naddr(&p.From, f)
+	Naddr(&p.To, t)
+	return p
 }
diff --git a/src/cmd/compile/internal/gc/iface_test.go b/src/cmd/compile/internal/gc/iface_test.go
new file mode 100644
index 0000000..21c6587
--- /dev/null
+++ b/src/cmd/compile/internal/gc/iface_test.go
@@ -0,0 +1,128 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gc
+
+// Test to make sure we make copies of the values we
+// put in interfaces.
+
+import (
+	"testing"
+)
+
+var x int
+
+func TestEfaceConv1(t *testing.T) {
+	a := 5
+	i := interface{}(a)
+	a += 2
+	if got := i.(int); got != 5 {
+		t.Errorf("wanted 5, got %d\n", got)
+	}
+}
+
+func TestEfaceConv2(t *testing.T) {
+	a := 5
+	sink = &a
+	i := interface{}(a)
+	a += 2
+	if got := i.(int); got != 5 {
+		t.Errorf("wanted 5, got %d\n", got)
+	}
+}
+
+func TestEfaceConv3(t *testing.T) {
+	x = 5
+	if got := e2int3(x); got != 5 {
+		t.Errorf("wanted 5, got %d\n", got)
+	}
+}
+
+//go:noinline
+func e2int3(i interface{}) int {
+	x = 7
+	return i.(int)
+}
+
+func TestEfaceConv4(t *testing.T) {
+	a := 5
+	if got := e2int4(a, &a); got != 5 {
+		t.Errorf("wanted 5, got %d\n", got)
+	}
+}
+
+//go:noinline
+func e2int4(i interface{}, p *int) int {
+	*p = 7
+	return i.(int)
+}
+
+type Int int
+
+var y Int
+
+type I interface {
+	foo()
+}
+
+func (i Int) foo() {
+}
+
+func TestIfaceConv1(t *testing.T) {
+	a := Int(5)
+	i := interface{}(a)
+	a += 2
+	if got := i.(Int); got != 5 {
+		t.Errorf("wanted 5, got %d\n", int(got))
+	}
+}
+
+func TestIfaceConv2(t *testing.T) {
+	a := Int(5)
+	sink = &a
+	i := interface{}(a)
+	a += 2
+	if got := i.(Int); got != 5 {
+		t.Errorf("wanted 5, got %d\n", int(got))
+	}
+}
+
+func TestIfaceConv3(t *testing.T) {
+	y = 5
+	if got := i2Int3(y); got != 5 {
+		t.Errorf("wanted 5, got %d\n", int(got))
+	}
+}
+
+//go:noinline
+func i2Int3(i I) Int {
+	y = 7
+	return i.(Int)
+}
+
+func TestIfaceConv4(t *testing.T) {
+	a := Int(5)
+	if got := i2Int4(a, &a); got != 5 {
+		t.Errorf("wanted 5, got %d\n", int(got))
+	}
+}
+
+//go:noinline
+func i2Int4(i I, p *Int) Int {
+	*p = 7
+	return i.(Int)
+}
+
+func BenchmarkEfaceInteger(b *testing.B) {
+	sum := 0
+	for i := 0; i < b.N; i++ {
+		sum += i2int(i)
+	}
+	sink = sum
+}
+
+//go:noinline
+func i2int(i interface{}) int {
+	return i.(int)
+}
diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go
index 67a050a..5693052 100644
--- a/src/cmd/compile/internal/gc/init.go
+++ b/src/cmd/compile/internal/gc/init.go
@@ -4,19 +4,6 @@
 
 package gc
 
-//	case OADD:
-//		if(n->right->op == OLITERAL) {
-//			v = n->right->vconst;
-//			naddr(n->left, a, canemitcode);
-//		} else
-//		if(n->left->op == OLITERAL) {
-//			v = n->left->vconst;
-//			naddr(n->right, a, canemitcode);
-//		} else
-//			goto bad;
-//		a->offset += v;
-//		break;
-
 // a function named init is a special case.
 // it is called by the initialization before
 // main is run. to make it unique within a
@@ -27,7 +14,7 @@ var renameinit_initgen int
 
 func renameinit() *Sym {
 	renameinit_initgen++
-	return LookupN("init.", renameinit_initgen)
+	return lookupN("init.", renameinit_initgen)
 }
 
 // hand-craft the following initialization code
@@ -70,7 +57,7 @@ func anyinit(n []*Node) bool {
 	}
 
 	// is there an explicit init function
-	s := Lookup("init.1")
+	s := lookup("init.1")
 
 	if s.Def != nil {
 		return true
@@ -88,11 +75,6 @@ func anyinit(n []*Node) bool {
 }
 
 func fninit(n []*Node) {
-	if Debug['A'] != 0 {
-		// sys.go or unsafe.go during compiler build
-		return
-	}
-
 	nf := initfix(n)
 	if !anyinit(nf) {
 		return
@@ -101,40 +83,40 @@ func fninit(n []*Node) {
 	var r []*Node
 
 	// (1)
-	gatevar := newname(Lookup("initdone·"))
+	gatevar := newname(lookup("initdone·"))
 	addvar(gatevar, Types[TUINT8], PEXTERN)
 
 	// (2)
 	Maxarg = 0
 
-	fn := Nod(ODCLFUNC, nil, nil)
-	initsym := Lookup("init")
+	fn := nod(ODCLFUNC, nil, nil)
+	initsym := lookup("init")
 	fn.Func.Nname = newname(initsym)
 	fn.Func.Nname.Name.Defn = fn
-	fn.Func.Nname.Name.Param.Ntype = Nod(OTFUNC, nil, nil)
+	fn.Func.Nname.Name.Param.Ntype = nod(OTFUNC, nil, nil)
 	declare(fn.Func.Nname, PFUNC)
 	funchdr(fn)
 
 	// (3)
-	a := Nod(OIF, nil, nil)
-	a.Left = Nod(OGT, gatevar, Nodintconst(1))
+	a := nod(OIF, nil, nil)
+	a.Left = nod(OGT, gatevar, nodintconst(1))
 	a.Likely = 1
 	r = append(r, a)
 	// (3a)
-	a.Nbody.Set1(Nod(ORETURN, nil, nil))
+	a.Nbody.Set1(nod(ORETURN, nil, nil))
 
 	// (4)
-	b := Nod(OIF, nil, nil)
-	b.Left = Nod(OEQ, gatevar, Nodintconst(1))
+	b := nod(OIF, nil, nil)
+	b.Left = nod(OEQ, gatevar, nodintconst(1))
 	// this actually isn't likely, but code layout is better
 	// like this: no JMP needed after the call.
 	b.Likely = 1
 	r = append(r, b)
 	// (4a)
-	b.Nbody.Set1(Nod(OCALL, syslook("throwinit"), nil))
+	b.Nbody.Set1(nod(OCALL, syslook("throwinit"), nil))
 
 	// (5)
-	a = Nod(OAS, gatevar, Nodintconst(1))
+	a = nod(OAS, gatevar, nodintconst(1))
 
 	r = append(r, a)
 
@@ -142,7 +124,7 @@ func fninit(n []*Node) {
 	for _, s := range initSyms {
 		if s.Def != nil && s != initsym {
 			// could check that it is fn of no args/returns
-			a = Nod(OCALL, s.Def, nil)
+			a = nod(OCALL, s.Def, nil)
 			r = append(r, a)
 		}
 	}
@@ -153,21 +135,21 @@ func fninit(n []*Node) {
 	// (8)
 	// could check that it is fn of no args/returns
 	for i := 1; ; i++ {
-		s := LookupN("init.", i)
+		s := lookupN("init.", i)
 		if s.Def == nil {
 			break
 		}
-		a = Nod(OCALL, s.Def, nil)
+		a = nod(OCALL, s.Def, nil)
 		r = append(r, a)
 	}
 
 	// (9)
-	a = Nod(OAS, gatevar, Nodintconst(2))
+	a = nod(OAS, gatevar, nodintconst(2))
 
 	r = append(r, a)
 
 	// (10)
-	a = Nod(ORETURN, nil, nil)
+	a = nod(ORETURN, nil, nil)
 
 	r = append(r, a)
 	exportsym(fn.Func.Nname)
diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go
index 0c1b050..d8f1f24 100644
--- a/src/cmd/compile/internal/gc/inl.go
+++ b/src/cmd/compile/internal/gc/inl.go
@@ -32,7 +32,7 @@ import "fmt"
 // Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
 // the ->sym can be re-used in the local package, so peel it off the receiver's type.
 func fnpkg(fn *Node) *Pkg {
-	if fn.Type.Recv() != nil {
+	if fn.IsMethod() {
 		// method
 		rcvr := fn.Type.Recv().Type
 
@@ -40,7 +40,7 @@ func fnpkg(fn *Node) *Pkg {
 			rcvr = rcvr.Elem()
 		}
 		if rcvr.Sym == nil {
-			Fatalf("receiver with no sym: [%v] %v  (%v)", fn.Sym, Nconv(fn, FmtLong), rcvr)
+			Fatalf("receiver with no sym: [%v] %L  (%v)", fn.Sym, fn, rcvr)
 		}
 		return rcvr.Sym.Pkg
 	}
@@ -65,7 +65,7 @@ func typecheckinl(fn *Node) {
 	}
 
 	if Debug['m'] > 2 || Debug_export != 0 {
-		fmt.Printf("typecheck import [%v] %v { %v }\n", fn.Sym, Nconv(fn, FmtLong), hconv(fn.Func.Inl, FmtSharp))
+		fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, fn.Func.Inl)
 	}
 
 	save_safemode := safemode
@@ -89,16 +89,27 @@ func caninl(fn *Node) {
 		Fatalf("caninl %v", fn)
 	}
 	if fn.Func.Nname == nil {
-		Fatalf("caninl no nname %v", Nconv(fn, FmtSign))
+		Fatalf("caninl no nname %+v", fn)
+	}
+
+	var reason string // reason, if any, that the function was not inlined
+	if Debug['m'] > 1 {
+		defer func() {
+			if reason != "" {
+				fmt.Printf("%v: cannot inline %v: %s\n", fn.Line(), fn.Func.Nname, reason)
+			}
+		}()
 	}
 
 	// If marked "go:noinline", don't inline
 	if fn.Func.Pragma&Noinline != 0 {
+		reason = "marked go:noinline"
 		return
 	}
 
 	// If fn has no body (is defined outside of Go), cannot inline it.
 	if fn.Nbody.Len() == 0 {
+		reason = "no function body"
 		return
 	}
 
@@ -111,6 +122,7 @@ func caninl(fn *Node) {
 		f := fn.Type.Params().Fields()
 		if len := f.Len(); len > 0 {
 			if t := f.Index(len - 1); t.Isddd {
+				reason = "has ... args"
 				return
 			}
 		}
@@ -123,12 +135,17 @@ func caninl(fn *Node) {
 	// The example that we observed is inlining of LockOSThread,
 	// which lead to false race reports on m contents.
 	if instrumenting && myimportpath == "runtime" {
+		reason = "instrumenting and is runtime function"
 		return
 	}
 
 	const maxBudget = 80
 	budget := int32(maxBudget) // allowed hairyness
-	if ishairylist(fn.Nbody, &budget) || budget < 0 {
+	if ishairylist(fn.Nbody, &budget, &reason) {
+		return
+	}
+	if budget < 0 {
+		reason = "function too complex"
 		return
 	}
 
@@ -148,7 +165,7 @@ func caninl(fn *Node) {
 	fn.Type.SetNname(n)
 
 	if Debug['m'] > 1 {
-		fmt.Printf("%v: can inline %v as: %v { %v }\n", fn.Line(), Nconv(n, FmtSharp), Tconv(fn.Type, FmtSharp), hconv(n.Func.Inl, FmtSharp))
+		fmt.Printf("%v: can inline %#v as: %#v { %#v }\n", fn.Line(), n, fn.Type, n.Func.Inl)
 	} else if Debug['m'] != 0 {
 		fmt.Printf("%v: can inline %v\n", fn.Line(), n)
 	}
@@ -157,16 +174,16 @@ func caninl(fn *Node) {
 }
 
 // Look for anything we want to punt on.
-func ishairylist(ll Nodes, budget *int32) bool {
+func ishairylist(ll Nodes, budget *int32, reason *string) bool {
 	for _, n := range ll.Slice() {
-		if ishairy(n, budget) {
+		if ishairy(n, budget, reason) {
 			return true
 		}
 	}
 	return false
 }
 
-func ishairy(n *Node, budget *int32) bool {
+func ishairy(n *Node, budget *int32, reason *string) bool {
 	if n == nil {
 		return false
 	}
@@ -179,13 +196,14 @@ func ishairy(n *Node, budget *int32) bool {
 			break
 		}
 
-		if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions
+		if n.isMethodCalledAsFunction() {
 			if d := n.Left.Sym.Def; d != nil && d.Func.Inl.Len() != 0 {
 				*budget -= d.Func.InlCost
 				break
 			}
 		}
 		if Debug['l'] < 4 {
+			*reason = "non-leaf function"
 			return true
 		}
 
@@ -193,22 +211,24 @@ func ishairy(n *Node, budget *int32) bool {
 	case OCALLMETH:
 		t := n.Left.Type
 		if t == nil {
-			Fatalf("no function type for [%p] %v\n", n.Left, Nconv(n.Left, FmtSign))
+			Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
 		}
 		if t.Nname() == nil {
-			Fatalf("no function definition for [%p] %v\n", t, Tconv(t, FmtSign))
+			Fatalf("no function definition for [%p] %+v\n", t, t)
 		}
 		if inlfn := t.Nname().Func; inlfn.Inl.Len() != 0 {
 			*budget -= inlfn.InlCost
 			break
 		}
 		if Debug['l'] < 4 {
+			*reason = "non-leaf method"
 			return true
 		}
 
 	// Things that are too hairy, irrespective of the budget
 	case OCALL, OCALLINTER, OPANIC, ORECOVER:
 		if Debug['l'] < 4 {
+			*reason = "non-leaf op " + n.Op.String()
 			return true
 		}
 
@@ -223,12 +243,25 @@ func ishairy(n *Node, budget *int32) bool {
 		ODCLTYPE, // can't print yet
 		OBREAK,
 		ORETJMP:
+		*reason = "unhandled op " + n.Op.String()
 		return true
 	}
 
 	(*budget)--
+	// TODO(mdempsky/josharian): Hacks to appease toolstash; remove.
+	// See issue 17566 and CL 31674 for discussion.
+	switch n.Op {
+	case OSTRUCTKEY:
+		(*budget)--
+	case OSLICE, OSLICEARR, OSLICESTR:
+		(*budget)--
+	case OSLICE3, OSLICE3ARR:
+		*budget -= 2
+	}
 
-	return *budget < 0 || ishairy(n.Left, budget) || ishairy(n.Right, budget) || ishairylist(n.List, budget) || ishairylist(n.Rlist, budget) || ishairylist(n.Ninit, budget) || ishairylist(n.Nbody, budget)
+	return *budget < 0 || ishairy(n.Left, budget, reason) || ishairy(n.Right, budget, reason) ||
+		ishairylist(n.List, budget, reason) || ishairylist(n.Rlist, budget, reason) ||
+		ishairylist(n.Ninit, budget, reason) || ishairylist(n.Nbody, budget, reason)
 }
 
 // Inlcopy and inlcopylist recursively copy the body of a function.
@@ -304,7 +337,7 @@ func inlconv2expr(n *Node) *Node {
 // statements.
 func inlconv2list(n *Node) []*Node {
 	if n.Op != OINLCALL || n.Rlist.Len() == 0 {
-		Fatalf("inlconv2list %v\n", Nconv(n, FmtSign))
+		Fatalf("inlconv2list %+v\n", n)
 	}
 
 	s := n.Rlist.Slice()
@@ -342,12 +375,11 @@ func inlnode(n *Node) *Node {
 	case ODEFER, OPROC:
 		switch n.Left.Op {
 		case OCALLFUNC, OCALLMETH:
-			// TODO(marvin): Fix Node.EType type union.
-			n.Left.Etype = EType(n.Op)
+			n.Left.setNoInline(true)
 		}
 		fallthrough
 
-		// TODO do them here (or earlier),
+	// TODO do them here (or earlier),
 	// so escape analysis can avoid more heapmoves.
 	case OCLOSURE:
 		return n
@@ -385,7 +417,7 @@ func inlnode(n *Node) *Node {
 			}
 		}
 
-		// if we just replaced arg in f(arg()) or return arg with an inlined call
+	// if we just replaced arg in f(arg()) or return arg with an inlined call
 	// and arg returns multiple values, glue as list
 	case ORETURN,
 		OCALLFUNC,
@@ -402,7 +434,7 @@ func inlnode(n *Node) *Node {
 	default:
 		s := n.List.Slice()
 		for i1, n1 := range s {
-			if n1.Op == OINLCALL {
+			if n1 != nil && n1.Op == OINLCALL {
 				s[i1] = inlconv2expr(s[i1])
 			}
 		}
@@ -445,8 +477,7 @@ func inlnode(n *Node) *Node {
 	// switch at the top of this function.
 	switch n.Op {
 	case OCALLFUNC, OCALLMETH:
-		// TODO(marvin): Fix Node.EType type union.
-		if n.Etype == EType(OPROC) || n.Etype == EType(ODEFER) {
+		if n.noInline() {
 			return n
 		}
 	}
@@ -454,28 +485,26 @@ func inlnode(n *Node) *Node {
 	switch n.Op {
 	case OCALLFUNC:
 		if Debug['m'] > 3 {
-			fmt.Printf("%v:call to func %v\n", n.Line(), Nconv(n.Left, FmtSign))
+			fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left)
 		}
-		if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall1(n) { // normal case
+		if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall(n) { // normal case
 			n = mkinlcall(n, n.Left, n.Isddd)
-		} else if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions
-			if n.Left.Sym.Def != nil {
-				n = mkinlcall(n, n.Left.Sym.Def, n.Isddd)
-			}
+		} else if n.isMethodCalledAsFunction() && n.Left.Sym.Def != nil {
+			n = mkinlcall(n, n.Left.Sym.Def, n.Isddd)
 		}
 
 	case OCALLMETH:
 		if Debug['m'] > 3 {
-			fmt.Printf("%v:call to meth %v\n", n.Line(), Nconv(n.Left.Right, FmtLong))
+			fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right)
 		}
 
 		// typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
 		if n.Left.Type == nil {
-			Fatalf("no function type for [%p] %v\n", n.Left, Nconv(n.Left, FmtSign))
+			Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
 		}
 
 		if n.Left.Type.Nname() == nil {
-			Fatalf("no function definition for [%p] %v\n", n.Left.Type, Tconv(n.Left.Type, FmtSign))
+			Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type)
 		}
 
 		n = mkinlcall(n, n.Left.Type.Nname(), n.Isddd)
@@ -502,12 +531,13 @@ func mkinlcall(n *Node, fn *Node, isddd bool) *Node {
 	return n
 }
 
-func tinlvar(t *Field) *Node {
+func tinlvar(t *Field, inlvars map[*Node]*Node) *Node {
 	if t.Nname != nil && !isblank(t.Nname) {
-		if t.Nname.Name.Inlvar == nil {
+		inlvar := inlvars[t.Nname]
+		if inlvar == nil {
 			Fatalf("missing inlvar for %v\n", t.Nname)
 		}
-		return t.Nname.Name.Inlvar
+		return inlvar
 	}
 
 	return typecheck(nblank, Erv|Easgn)
@@ -531,19 +561,21 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 		return n
 	}
 
+	inlvars := make(map[*Node]*Node)
+
 	if Debug['l'] < 2 {
 		typecheckinl(fn)
 	}
 
 	// Bingo, we have a function node, and it has an inlineable body
 	if Debug['m'] > 1 {
-		fmt.Printf("%v: inlining call to %v %v { %v }\n", n.Line(), fn.Sym, Tconv(fn.Type, FmtSharp), hconv(fn.Func.Inl, FmtSharp))
+		fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, fn.Func.Inl)
 	} else if Debug['m'] != 0 {
 		fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
 	}
 
 	if Debug['m'] > 2 {
-		fmt.Printf("%v: Before inlining: %v\n", n.Line(), Nconv(n, FmtSign))
+		fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n)
 	}
 
 	ninit := n.Ninit
@@ -571,9 +603,9 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 			continue
 		}
 		if ln.Op == ONAME {
-			ln.Name.Inlvar = typecheck(inlvar(ln), Erv)
+			inlvars[ln] = typecheck(inlvar(ln), Erv)
 			if ln.Class == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class == PPARAM {
-				ninit.Append(Nod(ODCL, ln.Name.Inlvar, nil))
+				ninit.Append(nod(ODCL, inlvars[ln], nil))
 			}
 		}
 	}
@@ -584,32 +616,32 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 		if t != nil && t.Nname != nil && !isblank(t.Nname) {
 			m = inlvar(t.Nname)
 			m = typecheck(m, Erv)
-			t.Nname.Name.Inlvar = m
+			inlvars[t.Nname] = m
 		} else {
 			// anonymous return values, synthesize names for use in assignment that replaces return
 			m = retvar(t, i)
 			i++
 		}
 
-		ninit.Append(Nod(ODCL, m, nil))
+		ninit.Append(nod(ODCL, m, nil))
 		retvars = append(retvars, m)
 	}
 
 	// assign receiver.
-	if fn.Type.Recv() != nil && n.Left.Op == ODOTMETH {
+	if fn.IsMethod() && n.Left.Op == ODOTMETH {
 		// method call with a receiver.
 		t := fn.Type.Recv()
 
-		if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Name.Inlvar == nil {
+		if t != nil && t.Nname != nil && !isblank(t.Nname) && inlvars[t.Nname] == nil {
 			Fatalf("missing inlvar for %v\n", t.Nname)
 		}
 		if n.Left.Left == nil {
-			Fatalf("method call without receiver: %v", Nconv(n, FmtSign))
+			Fatalf("method call without receiver: %+v", n)
 		}
 		if t == nil {
-			Fatalf("method call unknown receiver type: %v", Nconv(n, FmtSign))
+			Fatalf("method call unknown receiver type: %+v", n)
 		}
-		as := Nod(OAS, tinlvar(t), n.Left.Left)
+		as := nod(OAS, tinlvar(t, inlvars), n.Left.Left)
 		if as != nil {
 			as = typecheck(as, Etop)
 			ninit.Append(as)
@@ -654,28 +686,28 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 	}
 
 	// assign arguments to the parameters' temp names
-	as := Nod(OAS2, nil, nil)
+	as := nod(OAS2, nil, nil)
 
 	as.Rlist.Set(n.List.Slice())
 	li := 0
 
 	// TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call?
-	if fn.Type.Recv() != nil && n.Left.Op != ODOTMETH {
+	if fn.IsMethod() && n.Left.Op != ODOTMETH {
 		// non-method call to method
 		if n.List.Len() == 0 {
-			Fatalf("non-method call to method without first arg: %v", Nconv(n, FmtSign))
+			Fatalf("non-method call to method without first arg: %+v", n)
 		}
 
 		// append receiver inlvar to LHS.
 		t := fn.Type.Recv()
 
-		if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Name.Inlvar == nil {
+		if t != nil && t.Nname != nil && !isblank(t.Nname) && inlvars[t.Nname] == nil {
 			Fatalf("missing inlvar for %v\n", t.Nname)
 		}
 		if t == nil {
-			Fatalf("method call unknown receiver type: %v", Nconv(n, FmtSign))
+			Fatalf("method call unknown receiver type: %+v", n)
 		}
-		as.List.Append(tinlvar(t))
+		as.List.Append(tinlvar(t, inlvars))
 		li++
 	}
 
@@ -689,7 +721,7 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 		var i int
 		for _, t := range fn.Type.Params().Fields().Slice() {
 			if variadic && t.Isddd {
-				vararg = tinlvar(t)
+				vararg = tinlvar(t, inlvars)
 				for i = 0; i < varargcount && li < n.List.Len(); i++ {
 					m = argvar(varargtype, i)
 					varargs = append(varargs, m)
@@ -699,11 +731,11 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 				break
 			}
 
-			as.List.Append(tinlvar(t))
+			as.List.Append(tinlvar(t, inlvars))
 		}
 	} else {
 		// match arguments except final variadic (unless the call is dotted itself)
-		t, it := IterFields(fn.Type.Params())
+		t, it := iterFields(fn.Type.Params())
 		for t != nil {
 			if li >= n.List.Len() {
 				break
@@ -711,14 +743,14 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 			if variadic && t.Isddd {
 				break
 			}
-			as.List.Append(tinlvar(t))
+			as.List.Append(tinlvar(t, inlvars))
 			t = it.Next()
 			li++
 		}
 
 		// match varargcount arguments with variadic parameters.
 		if variadic && t != nil && t.Isddd {
-			vararg = tinlvar(t)
+			vararg = tinlvar(t, inlvars)
 			var i int
 			for i = 0; i < varargcount && li < n.List.Len(); i++ {
 				m = argvar(varargtype, i)
@@ -733,7 +765,7 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 		}
 
 		if li < n.List.Len() || t != nil {
-			Fatalf("arg count mismatch: %v  vs %v\n", Tconv(fn.Type.Params(), FmtSharp), hconv(n.List, FmtComma))
+			Fatalf("arg count mismatch: %#v vs %.v\n", fn.Type.Params(), n.List)
 		}
 	}
 
@@ -744,15 +776,14 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 
 	// turn the variadic args into a slice.
 	if variadic {
-		as = Nod(OAS, vararg, nil)
+		as = nod(OAS, vararg, nil)
 		if varargcount == 0 {
 			as.Right = nodnil()
 			as.Right.Type = varargtype
 		} else {
-			vararrtype := typArray(varargtype.Elem(), int64(varargcount))
-			as.Right = Nod(OCOMPLIT, nil, typenod(vararrtype))
+			varslicetype := typSlice(varargtype.Elem())
+			as.Right = nod(OCOMPLIT, nil, typenod(varslicetype))
 			as.Right.List.Set(varargs)
-			as.Right = Nod(OSLICE, as.Right, nil)
 		}
 
 		as = typecheck(as, Etop)
@@ -761,29 +792,33 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 
 	// zero the outparams
 	for _, n := range retvars {
-		as = Nod(OAS, n, nil)
+		as = nod(OAS, n, nil)
 		as = typecheck(as, Etop)
 		ninit.Append(as)
 	}
 
-	retlabel := newlabel_inl()
+	retlabel := autolabel(".i")
+	retlabel.Etype = 1 // flag 'safe' for escape analysis (no backjumps)
+
 	inlgen++
 
 	subst := inlsubst{
 		retlabel: retlabel,
 		retvars:  retvars,
+		inlvars:  inlvars,
 	}
 
 	body := subst.list(fn.Func.Inl)
 
-	body = append(body, Nod(OGOTO, retlabel, nil)) // avoid 'not used' when function doesn't have return
-	body = append(body, Nod(OLABEL, retlabel, nil))
+	lab := nod(OLABEL, retlabel, nil)
+	lab.Used = true // avoid 'not used' when function doesn't have return
+	body = append(body, lab)
 
 	typecheckslice(body, Etop)
 
 	//dumplist("ninit post", ninit);
 
-	call := Nod(OINLCALL, nil, nil)
+	call := nod(OINLCALL, nil, nil)
 
 	call.Ninit.Set(ninit.Slice())
 	call.Nbody.Set(body)
@@ -821,7 +856,7 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 	fn.Func.Inl.Set(body)
 
 	if Debug['m'] > 2 {
-		fmt.Printf("%v: After inlining %v\n\n", n.Line(), Nconv(n, FmtSign))
+		fmt.Printf("%v: After inlining %+v\n\n", n.Line(), n)
 	}
 
 	return n
@@ -832,7 +867,7 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
 // PPARAM's, PAUTOS and PPARAMOUTs of the called function.
 func inlvar(var_ *Node) *Node {
 	if Debug['m'] > 3 {
-		fmt.Printf("inlvar %v\n", Nconv(var_, FmtSign))
+		fmt.Printf("inlvar %+v\n", var_)
 	}
 
 	n := newname(var_.Sym)
@@ -842,20 +877,13 @@ func inlvar(var_ *Node) *Node {
 	n.Name.Curfn = Curfn // the calling function, not the called one
 	n.Addrtaken = var_.Addrtaken
 
-	// This may no longer be necessary now that we run escape analysis
-	// after wrapper generation, but for 1.5 this is conservatively left
-	// unchanged. See bugs 11053 and 9537.
-	if var_.Esc == EscHeap {
-		addrescapes(n)
-	}
-
 	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
 	return n
 }
 
 // Synthesize a variable to store the inlined function's results in.
 func retvar(t *Field, i int) *Node {
-	n := newname(LookupN("~r", i))
+	n := newname(lookupN("~r", i))
 	n.Type = t.Type
 	n.Class = PAUTO
 	n.Used = true
@@ -867,7 +895,7 @@ func retvar(t *Field, i int) *Node {
 // Synthesize a variable to store the inlined function's arguments
 // when they come from a multiple return call.
 func argvar(t *Type, i int) *Node {
-	n := newname(LookupN("~arg", i))
+	n := newname(lookupN("~arg", i))
 	n.Type = t.Elem()
 	n.Class = PAUTO
 	n.Used = true
@@ -876,15 +904,6 @@ func argvar(t *Type, i int) *Node {
 	return n
 }
 
-var newlabel_inl_label int
-
-func newlabel_inl() *Node {
-	newlabel_inl_label++
-	n := newname(LookupN(".inlret", newlabel_inl_label))
-	n.Etype = 1 // flag 'safe' for escape analysis (no backjumps)
-	return n
-}
-
 // The inlsubst type implements the actual inlining of a single
 // function call.
 type inlsubst struct {
@@ -893,6 +912,8 @@ type inlsubst struct {
 
 	// Temporary result variables.
 	retvars []*Node
+
+	inlvars map[*Node]*Node
 }
 
 // list inlines a list of nodes.
@@ -915,15 +936,15 @@ func (subst *inlsubst) node(n *Node) *Node {
 
 	switch n.Op {
 	case ONAME:
-		if n.Name.Inlvar != nil { // These will be set during inlnode
+		if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
 			if Debug['m'] > 2 {
-				fmt.Printf("substituting name %v  ->  %v\n", Nconv(n, FmtSign), Nconv(n.Name.Inlvar, FmtSign))
+				fmt.Printf("substituting name %+v  ->  %+v\n", n, inlvar)
 			}
-			return n.Name.Inlvar
+			return inlvar
 		}
 
 		if Debug['m'] > 2 {
-			fmt.Printf("not substituting name %v\n", Nconv(n, FmtSign))
+			fmt.Printf("not substituting name %+v\n", n)
 		}
 		return n
 
@@ -934,12 +955,12 @@ func (subst *inlsubst) node(n *Node) *Node {
 
 	//		dump("Return before substitution", n);
 	case ORETURN:
-		m := Nod(OGOTO, subst.retlabel, nil)
+		m := nod(OGOTO, subst.retlabel, nil)
 
 		m.Ninit.Set(subst.list(n.Ninit))
 
 		if len(subst.retvars) != 0 && n.List.Len() != 0 {
-			as := Nod(OAS2, nil, nil)
+			as := nod(OAS2, nil, nil)
 
 			// Make a shallow copy of retvars.
 			// Otherwise OINLCALL.Rlist will be the same list,
@@ -959,20 +980,20 @@ func (subst *inlsubst) node(n *Node) *Node {
 		return m
 
 	case OGOTO, OLABEL:
-		m := Nod(OXXX, nil, nil)
+		m := nod(OXXX, nil, nil)
 		*m = *n
 		m.Ninit.Set(nil)
 		p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen)
-		m.Left = newname(Lookup(p))
+		m.Left = newname(lookup(p))
 
 		return m
 	default:
-		m := Nod(OXXX, nil, nil)
+		m := nod(OXXX, nil, nil)
 		*m = *n
 		m.Ninit.Set(nil)
 
 		if n.Op == OCLOSURE {
-			Fatalf("cannot inline function containing closure: %v", Nconv(n, FmtSign))
+			Fatalf("cannot inline function containing closure: %+v", n)
 		}
 
 		m.Left = subst.node(n.Left)
@@ -1010,3 +1031,7 @@ func setlno(n *Node, lno int32) {
 	setlnolist(n.Ninit, lno)
 	setlnolist(n.Nbody, lno)
 }
+
+func (n *Node) isMethodCalledAsFunction() bool {
+	return n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME
+}
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index f38819d..87f9bf2 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -5,20 +5,10 @@
 package gc
 
 import (
-	"bufio"
-	"bytes"
+	"cmd/compile/internal/syntax"
 	"cmd/internal/obj"
 	"fmt"
-	"io"
-	"strconv"
 	"strings"
-	"unicode"
-	"unicode/utf8"
-)
-
-const (
-	EOF = -1
-	BOM = 0xFEFF
 )
 
 // lexlineno is the line number _after_ the most recently read rune.
@@ -28,22 +18,10 @@ var lexlineno int32
 // lineno is the line number at the start of the most recently lexed token.
 var lineno int32
 
-var lexbuf bytes.Buffer
-var strbuf bytes.Buffer
-var litbuf string // LLITERAL value for use in syntax error messages
-
 func isSpace(c rune) bool {
 	return c == ' ' || c == '\t' || c == '\n' || c == '\r'
 }
 
-func isLetter(c rune) bool {
-	return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_'
-}
-
-func isDigit(c rune) bool {
-	return '0' <= c && c <= '9'
-}
-
 func isQuoted(s string) bool {
 	return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
 }
@@ -60,791 +38,82 @@ func plan9quote(s string) string {
 	return s
 }
 
-type Pragma uint16
-
-const (
-	Nointerface       Pragma = 1 << iota
-	Noescape                 // func parameters don't escape
-	Norace                   // func must not have race detector annotations
-	Nosplit                  // func should not execute on separate stack
-	Noinline                 // func should not be inlined
-	Systemstack              // func must run on system stack
-	Nowritebarrier           // emit compiler error instead of write barrier
-	Nowritebarrierrec        // error on write barrier in this or recursive callees
-	CgoUnsafeArgs            // treat a pointer to one arg as a pointer to them all
-	UintptrEscapes           // pointers converted to uintptr escape
-)
-
-type lexer struct {
-	// source
-	bin        *bufio.Reader
-	prevlineno int32 // line no. of most recently read character
-
-	nlsemi bool // if set, '\n' and EOF translate to ';'
-
-	// pragma flags
-	// accumulated by lexer; reset by parser
-	pragma Pragma
-
-	// current token
-	tok  int32
-	sym_ *Sym   // valid if tok == LNAME
-	val  Val    // valid if tok == LLITERAL
-	op   Op     // valid if tok == LOPER, LASOP, or LINCOP, or prec > 0
-	prec OpPrec // operator precedence; 0 if not a binary operator
-}
-
-type OpPrec int
-
-const (
-	// Precedences of binary operators (must be > 0).
-	PCOMM OpPrec = 1 + iota
-	POROR
-	PANDAND
-	PCMP
-	PADD
-	PMUL
-)
+type Pragma syntax.Pragma
 
 const (
-	// The value of single-char tokens is just their character's Unicode value.
-	// They are all below utf8.RuneSelf. Shift other tokens up to avoid conflicts.
-
-	// names and literals
-	LNAME = utf8.RuneSelf + iota
-	LLITERAL
-
-	// operator-based operations
-	LOPER
-	LASOP
-	LINCOP
-
-	// miscellaneous
-	LCOLAS
-	LCOMM
-	LDDD
-
-	// keywords
-	LBREAK
-	LCASE
-	LCHAN
-	LCONST
-	LCONTINUE
-	LDEFAULT
-	LDEFER
-	LELSE
-	LFALL
-	LFOR
-	LFUNC
-	LGO
-	LGOTO
-	LIF
-	LIMPORT
-	LINTERFACE
-	LMAP
-	LPACKAGE
-	LRANGE
-	LRETURN
-	LSELECT
-	LSTRUCT
-	LSWITCH
-	LTYPE
-	LVAR
-
-	LIGNORE
+	// Func pragmas.
+	Nointerface    Pragma = 1 << iota
+	Noescape              // func parameters don't escape
+	Norace                // func must not have race detector annotations
+	Nosplit               // func should not execute on separate stack
+	Noinline              // func should not be inlined
+	CgoUnsafeArgs         // treat a pointer to one arg as a pointer to them all
+	UintptrEscapes        // pointers converted to uintptr escape
+
+	// Runtime-only func pragmas.
+	// See ../../../../runtime/README.md for detailed descriptions.
+	Systemstack        // func must run on system stack
+	Nowritebarrier     // emit compiler error instead of write barrier
+	Nowritebarrierrec  // error on write barrier in this or recursive callees
+	Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees
+
+	// Runtime-only type pragmas
+	NotInHeap // values of this type must not be heap allocated
 )
 
-var lexn = map[rune]string{
-	LNAME:    "NAME",
-	LLITERAL: "LITERAL",
-
-	LOPER:  "OPER",
-	LASOP:  "ASOP",
-	LINCOP: "INCOP",
-
-	LCOLAS: "COLAS",
-	LCOMM:  "COMM",
-	LDDD:   "DDD",
-
-	LBREAK:     "BREAK",
-	LCASE:      "CASE",
-	LCHAN:      "CHAN",
-	LCONST:     "CONST",
-	LCONTINUE:  "CONTINUE",
-	LDEFAULT:   "DEFAULT",
-	LDEFER:     "DEFER",
-	LELSE:      "ELSE",
-	LFALL:      "FALL",
-	LFOR:       "FOR",
-	LFUNC:      "FUNC",
-	LGO:        "GO",
-	LGOTO:      "GOTO",
-	LIF:        "IF",
-	LIMPORT:    "IMPORT",
-	LINTERFACE: "INTERFACE",
-	LMAP:       "MAP",
-	LPACKAGE:   "PACKAGE",
-	LRANGE:     "RANGE",
-	LRETURN:    "RETURN",
-	LSELECT:    "SELECT",
-	LSTRUCT:    "STRUCT",
-	LSWITCH:    "SWITCH",
-	LTYPE:      "TYPE",
-	LVAR:       "VAR",
-
-	// LIGNORE is never escaping lexer.next
-}
-
-func lexname(lex rune) string {
-	if s, ok := lexn[lex]; ok {
-		return s
-	}
-	return fmt.Sprintf("LEX-%d", lex)
-}
-
-func (l *lexer) next() {
-	nlsemi := l.nlsemi
-	l.nlsemi = false
-	l.prec = 0
-
-l0:
-	// skip white space
-	c := l.getr()
-	for isSpace(c) {
-		if c == '\n' && nlsemi {
-			if Debug['x'] != 0 {
-				fmt.Printf("lex: implicit semi\n")
-			}
-			// Insert implicit semicolon on previous line,
-			// before the newline character.
-			lineno = lexlineno - 1
-			l.tok = ';'
-			return
-		}
-		c = l.getr()
-	}
-
-	// start of token
-	lineno = lexlineno
-
-	// identifiers and keywords
-	// (for better error messages consume all chars >= utf8.RuneSelf for identifiers)
-	if isLetter(c) || c >= utf8.RuneSelf {
-		l.ident(c)
-		if l.tok == LIGNORE {
-			goto l0
-		}
-		return
-	}
-	// c < utf8.RuneSelf
-
-	var c1 rune
-	var op Op
-	var prec OpPrec
-
-	switch c {
-	case EOF:
-		l.ungetr()
-		// Treat EOF as "end of line" for the purposes
-		// of inserting a semicolon.
-		if nlsemi {
-			if Debug['x'] != 0 {
-				fmt.Printf("lex: implicit semi\n")
-			}
-			l.tok = ';'
-			return
-		}
-		l.tok = -1
-		return
-
-	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
-		l.number(c)
-		return
-
-	case '.':
-		c1 = l.getr()
-		if isDigit(c1) {
-			l.ungetr()
-			l.number('.')
-			return
-		}
-
-		if c1 == '.' {
-			p, err := l.bin.Peek(1)
-			if err == nil && p[0] == '.' {
-				l.getr()
-				c = LDDD
-				goto lx
-			}
-
-			l.ungetr()
-			c1 = '.'
-		}
-
-	case '"':
-		l.stdString()
-		return
-
-	case '`':
-		l.rawString()
-		return
-
-	case '\'':
-		l.rune()
-		return
-
-	case '/':
-		c1 = l.getr()
-		if c1 == '*' {
-			c = l.getr()
-			for {
-				if c == '*' {
-					c = l.getr()
-					if c == '/' {
-						break
-					}
-					continue
-				}
-				if c == EOF {
-					Yyerror("eof in comment")
-					errorexit()
-				}
-				c = l.getr()
-			}
-
-			// A comment containing newlines acts like a newline.
-			if lexlineno > lineno && nlsemi {
-				if Debug['x'] != 0 {
-					fmt.Printf("lex: implicit semi\n")
-				}
-				l.tok = ';'
-				return
-			}
-			goto l0
-		}
-
-		if c1 == '/' {
-			c = l.getlinepragma()
-			for {
-				if c == '\n' || c == EOF {
-					l.ungetr()
-					goto l0
-				}
-
-				c = l.getr()
-			}
-		}
-
-		op = ODIV
-		prec = PMUL
-		goto binop1
-
-	case ':':
-		c1 = l.getr()
-		if c1 == '=' {
-			c = LCOLAS
-			goto lx
-		}
-
-	case '*':
-		op = OMUL
-		prec = PMUL
-		goto binop
-
-	case '%':
-		op = OMOD
-		prec = PMUL
-		goto binop
-
-	case '+':
-		op = OADD
-		goto incop
-
-	case '-':
-		op = OSUB
-		goto incop
-
-	case '>':
-		c = LOPER
-		c1 = l.getr()
-		if c1 == '>' {
-			op = ORSH
-			prec = PMUL
-			goto binop
-		}
-
-		l.prec = PCMP
-		if c1 == '=' {
-			l.op = OGE
-			goto lx
-		}
-		l.op = OGT
-
-	case '<':
-		c = LOPER
-		c1 = l.getr()
-		if c1 == '<' {
-			op = OLSH
-			prec = PMUL
-			goto binop
-		}
-
-		if c1 == '-' {
-			c = LCOMM
-			// Not a binary operator, but parsed as one
-			// so we can give a good error message when used
-			// in an expression context.
-			l.prec = PCOMM
-			l.op = OSEND
-			goto lx
-		}
-
-		l.prec = PCMP
-		if c1 == '=' {
-			l.op = OLE
-			goto lx
-		}
-		l.op = OLT
-
-	case '=':
-		c1 = l.getr()
-		if c1 == '=' {
-			c = LOPER
-			l.prec = PCMP
-			l.op = OEQ
-			goto lx
-		}
-
-	case '!':
-		c1 = l.getr()
-		if c1 == '=' {
-			c = LOPER
-			l.prec = PCMP
-			l.op = ONE
-			goto lx
-		}
-
-	case '&':
-		c1 = l.getr()
-		if c1 == '&' {
-			c = LOPER
-			l.prec = PANDAND
-			l.op = OANDAND
-			goto lx
-		}
-
-		if c1 == '^' {
-			c = LOPER
-			op = OANDNOT
-			prec = PMUL
-			goto binop
-		}
-
-		op = OAND
-		prec = PMUL
-		goto binop1
-
-	case '|':
-		c1 = l.getr()
-		if c1 == '|' {
-			c = LOPER
-			l.prec = POROR
-			l.op = OOROR
-			goto lx
-		}
-
-		op = OOR
-		prec = PADD
-		goto binop1
-
-	case '^':
-		op = OXOR
-		prec = PADD
-		goto binop
-
-	case '(', '[', '{', ',', ';':
-		goto lx
-
-	case ')', ']', '}':
-		l.nlsemi = true
-		goto lx
-
-	case '#', '$', '?', '@', '\\':
-		if importpkg != nil {
-			goto lx
-		}
-		fallthrough
-
-	default:
-		// anything else is illegal
-		Yyerror("syntax error: illegal character %#U", c)
-		goto l0
-	}
-
-	l.ungetr()
-
-lx:
-	if Debug['x'] != 0 {
-		if c >= utf8.RuneSelf {
-			fmt.Printf("%v lex: TOKEN %s\n", linestr(lineno), lexname(c))
-		} else {
-			fmt.Printf("%v lex: TOKEN '%c'\n", linestr(lineno), c)
-		}
-	}
-
-	l.tok = c
-	return
-
-incop:
-	c1 = l.getr()
-	if c1 == c {
-		l.nlsemi = true
-		l.op = op
-		c = LINCOP
-		goto lx
-	}
-	prec = PADD
-	goto binop1
-
-binop:
-	c1 = l.getr()
-binop1:
-	if c1 != '=' {
-		l.ungetr()
-		l.op = op
-		l.prec = prec
-		goto lx
-	}
-
-	l.op = op
-	if Debug['x'] != 0 {
-		fmt.Printf("lex: TOKEN ASOP %s=\n", goopnames[op])
-	}
-	l.tok = LASOP
-}
-
-func (l *lexer) ident(c rune) {
-	cp := &lexbuf
-	cp.Reset()
-
-	// accelerate common case (7bit ASCII)
-	for isLetter(c) || isDigit(c) {
-		cp.WriteByte(byte(c))
-		c = l.getr()
-	}
-
-	// general case
-	for {
-		if c >= utf8.RuneSelf {
-			if unicode.IsLetter(c) || c == '_' || unicode.IsDigit(c) || importpkg != nil && c == 0xb7 {
-				if cp.Len() == 0 && unicode.IsDigit(c) {
-					Yyerror("identifier cannot begin with digit %#U", c)
-				}
-			} else {
-				Yyerror("invalid identifier character %#U", c)
-			}
-			cp.WriteRune(c)
-		} else if isLetter(c) || isDigit(c) {
-			cp.WriteByte(byte(c))
-		} else {
-			break
-		}
-		c = l.getr()
-	}
-
-	cp = nil
-	l.ungetr()
-
-	name := lexbuf.Bytes()
-
-	if len(name) >= 2 {
-		if tok, ok := keywords[string(name)]; ok {
-			if Debug['x'] != 0 {
-				fmt.Printf("lex: %s\n", lexname(tok))
-			}
-			switch tok {
-			case LBREAK, LCONTINUE, LFALL, LRETURN:
-				l.nlsemi = true
-			}
-			l.tok = tok
-			return
-		}
-	}
-
-	s := LookupBytes(name)
-	if Debug['x'] != 0 {
-		fmt.Printf("lex: ident %s\n", s)
-	}
-	l.sym_ = s
-	l.nlsemi = true
-	l.tok = LNAME
-}
-
-var keywords = map[string]int32{
-	"break":       LBREAK,
-	"case":        LCASE,
-	"chan":        LCHAN,
-	"const":       LCONST,
-	"continue":    LCONTINUE,
-	"default":     LDEFAULT,
-	"defer":       LDEFER,
-	"else":        LELSE,
-	"fallthrough": LFALL,
-	"for":         LFOR,
-	"func":        LFUNC,
-	"go":          LGO,
-	"goto":        LGOTO,
-	"if":          LIF,
-	"import":      LIMPORT,
-	"interface":   LINTERFACE,
-	"map":         LMAP,
-	"package":     LPACKAGE,
-	"range":       LRANGE,
-	"return":      LRETURN,
-	"select":      LSELECT,
-	"struct":      LSTRUCT,
-	"switch":      LSWITCH,
-	"type":        LTYPE,
-	"var":         LVAR,
-
-	// 💩
-	"notwithstanding":      LIGNORE,
-	"thetruthofthematter":  LIGNORE,
-	"despiteallobjections": LIGNORE,
-	"whereas":              LIGNORE,
-	"insofaras":            LIGNORE,
-}
-
-func (l *lexer) number(c rune) {
-	cp := &lexbuf
-	cp.Reset()
-
-	// parse mantissa before decimal point or exponent
-	isInt := false
-	malformedOctal := false
-	if c != '.' {
-		if c != '0' {
-			// decimal or float
-			for isDigit(c) {
-				cp.WriteByte(byte(c))
-				c = l.getr()
-			}
-
-		} else {
-			// c == 0
-			cp.WriteByte('0')
-			c = l.getr()
-			if c == 'x' || c == 'X' {
-				isInt = true // must be int
-				cp.WriteByte(byte(c))
-				c = l.getr()
-				for isDigit(c) || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
-					cp.WriteByte(byte(c))
-					c = l.getr()
-				}
-				if lexbuf.Len() == 2 {
-					Yyerror("malformed hex constant")
-				}
-			} else {
-				// decimal 0, octal, or float
-				for isDigit(c) {
-					if c > '7' {
-						malformedOctal = true
-					}
-					cp.WriteByte(byte(c))
-					c = l.getr()
-				}
-			}
-		}
-	}
-
-	// unless we have a hex number, parse fractional part or exponent, if any
-	var str string
-	if !isInt {
-		isInt = true // assume int unless proven otherwise
-
-		// fraction
-		if c == '.' {
-			isInt = false
-			cp.WriteByte('.')
-			c = l.getr()
-			for isDigit(c) {
-				cp.WriteByte(byte(c))
-				c = l.getr()
-			}
-			// Falling through to exponent parsing here permits invalid
-			// floating-point numbers with fractional mantissa and base-2
-			// (p or P) exponent. We don't care because base-2 exponents
-			// can only show up in machine-generated textual export data
-			// which will use correct formatting.
-		}
-
-		// exponent
-		// base-2 exponent (p or P) is only allowed in export data (see #9036)
-		// TODO(gri) Once we switch to binary import data, importpkg will
-		// always be nil in this function. Simplify the code accordingly.
-		if c == 'e' || c == 'E' || importpkg != nil && (c == 'p' || c == 'P') {
-			isInt = false
-			cp.WriteByte(byte(c))
-			c = l.getr()
-			if c == '+' || c == '-' {
-				cp.WriteByte(byte(c))
-				c = l.getr()
-			}
-			if !isDigit(c) {
-				Yyerror("malformed floating point constant exponent")
-			}
-			for isDigit(c) {
-				cp.WriteByte(byte(c))
-				c = l.getr()
-			}
-		}
-
-		// imaginary constant
-		if c == 'i' {
-			str = lexbuf.String()
-			x := new(Mpcplx)
-			x.Real.SetFloat64(0.0)
-			x.Imag.SetString(str)
-			if x.Imag.Val.IsInf() {
-				Yyerror("overflow in imaginary constant")
-				x.Imag.SetFloat64(0.0)
-			}
-			l.val.U = x
-
-			if Debug['x'] != 0 {
-				fmt.Printf("lex: imaginary literal\n")
-			}
-			goto done
-		}
-	}
-
-	l.ungetr()
-
-	if isInt {
-		if malformedOctal {
-			Yyerror("malformed octal constant")
-		}
-
-		str = lexbuf.String()
-		x := new(Mpint)
-		x.SetString(str)
-		if x.Ovf {
-			Yyerror("overflow in constant")
-			x.SetInt64(0)
-		}
-		l.val.U = x
-
-		if Debug['x'] != 0 {
-			fmt.Printf("lex: integer literal\n")
-		}
-
-	} else { // float
-
-		str = lexbuf.String()
-		x := newMpflt()
-		x.SetString(str)
-		if x.Val.IsInf() {
-			Yyerror("overflow in float constant")
-			x.SetFloat64(0.0)
-		}
-		l.val.U = x
-
-		if Debug['x'] != 0 {
-			fmt.Printf("lex: floating literal\n")
-		}
-	}
-
-done:
-	litbuf = "" // lazily initialized in (*parser).syntax_error
-	l.nlsemi = true
-	l.tok = LLITERAL
-}
-
-func (l *lexer) stdString() {
-	lexbuf.Reset()
-	lexbuf.WriteString(`"<string>"`)
-
-	cp := &strbuf
-	cp.Reset()
-
-	for {
-		r, b, ok := l.onechar('"')
-		if !ok {
-			break
-		}
-		if r == 0 {
-			cp.WriteByte(b)
-		} else {
-			cp.WriteRune(r)
-		}
-	}
-
-	l.val.U = internString(cp.Bytes())
-	if Debug['x'] != 0 {
-		fmt.Printf("lex: string literal\n")
-	}
-	litbuf = "string literal"
-	l.nlsemi = true
-	l.tok = LLITERAL
-}
-
-func (l *lexer) rawString() {
-	lexbuf.Reset()
-	lexbuf.WriteString("`<string>`")
-
-	cp := &strbuf
-	cp.Reset()
-
-	for {
-		c := l.getr()
-		if c == '\r' {
-			continue
-		}
-		if c == EOF {
-			Yyerror("eof in string")
-			break
-		}
-		if c == '`' {
-			break
-		}
-		cp.WriteRune(c)
-	}
-
-	l.val.U = internString(cp.Bytes())
-	if Debug['x'] != 0 {
-		fmt.Printf("lex: string literal\n")
-	}
-	litbuf = "string literal"
-	l.nlsemi = true
-	l.tok = LLITERAL
-}
-
-func (l *lexer) rune() {
-	r, b, ok := l.onechar('\'')
-	if !ok {
-		Yyerror("empty character literal or unescaped ' in character literal")
-		r = '\''
-	}
-	if r == 0 {
-		r = rune(b)
-	}
-
-	if c := l.getr(); c != '\'' {
-		Yyerror("missing '")
-		l.ungetr()
-	}
-
-	x := new(Mpint)
-	l.val.U = x
-	x.SetInt64(int64(r))
-	x.Rune = true
-	if Debug['x'] != 0 {
-		fmt.Printf("lex: codepoint literal\n")
-	}
-	litbuf = "rune literal"
-	l.nlsemi = true
-	l.tok = LLITERAL
+func pragmaValue(verb string) Pragma {
+	switch verb {
+	case "go:nointerface":
+		if obj.Fieldtrack_enabled != 0 {
+			return Nointerface
+		}
+	case "go:noescape":
+		return Noescape
+	case "go:norace":
+		return Norace
+	case "go:nosplit":
+		return Nosplit
+	case "go:noinline":
+		return Noinline
+	case "go:systemstack":
+		if !compiling_runtime {
+			yyerror("//go:systemstack only allowed in runtime")
+		}
+		return Systemstack
+	case "go:nowritebarrier":
+		if !compiling_runtime {
+			yyerror("//go:nowritebarrier only allowed in runtime")
+		}
+		return Nowritebarrier
+	case "go:nowritebarrierrec":
+		if !compiling_runtime {
+			yyerror("//go:nowritebarrierrec only allowed in runtime")
+		}
+		return Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
+	case "go:yeswritebarrierrec":
+		if !compiling_runtime {
+			yyerror("//go:yeswritebarrierrec only allowed in runtime")
+		}
+		return Yeswritebarrierrec
+	case "go:cgo_unsafe_args":
+		return CgoUnsafeArgs
+	case "go:uintptrescapes":
+		// For the next function declared in the file
+		// any uintptr arguments may be pointer values
+		// converted to uintptr. This directive
+		// ensures that the referenced allocated
+		// object, if any, is retained and not moved
+		// until the call completes, even though from
+		// the types alone it would appear that the
+		// object is no longer needed during the
+		// call. The conversion to uintptr must appear
+		// in the argument list.
+		// Used in syscall/dll_windows.go.
+		return UintptrEscapes
+	case "go:notinheap":
+		return NotInHeap
+	}
+	return 0
 }
 
 var internedStrings = map[string]string{}
@@ -858,148 +127,6 @@ func internString(b []byte) string {
 	return s
 }
 
-// read and interpret syntax that looks like
-// //line parse.y:15
-// as a discontinuity in sequential line numbers.
-// the next line of input comes from parse.y:15
-func (l *lexer) getlinepragma() rune {
-	c := l.getr()
-	if c == 'g' { // check for //go: directive
-		cp := &lexbuf
-		cp.Reset()
-		cp.WriteByte('g') // already read
-		for {
-			c = l.getr()
-			if c == EOF || c >= utf8.RuneSelf {
-				return c
-			}
-			if c == '\n' {
-				break
-			}
-			cp.WriteByte(byte(c))
-		}
-		cp = nil
-
-		text := strings.TrimSuffix(lexbuf.String(), "\r")
-
-		if strings.HasPrefix(text, "go:cgo_") {
-			pragcgobuf += pragcgo(text)
-		}
-
-		verb := text
-		if i := strings.Index(text, " "); i >= 0 {
-			verb = verb[:i]
-		}
-
-		switch verb {
-		case "go:linkname":
-			if !imported_unsafe {
-				Yyerror("//go:linkname only allowed in Go files that import \"unsafe\"")
-			}
-			f := strings.Fields(text)
-			if len(f) != 3 {
-				Yyerror("usage: //go:linkname localname linkname")
-				break
-			}
-			Lookup(f[1]).Linkname = f[2]
-		case "go:nointerface":
-			if obj.Fieldtrack_enabled != 0 {
-				l.pragma |= Nointerface
-			}
-		case "go:noescape":
-			l.pragma |= Noescape
-		case "go:norace":
-			l.pragma |= Norace
-		case "go:nosplit":
-			l.pragma |= Nosplit
-		case "go:noinline":
-			l.pragma |= Noinline
-		case "go:systemstack":
-			if !compiling_runtime {
-				Yyerror("//go:systemstack only allowed in runtime")
-			}
-			l.pragma |= Systemstack
-		case "go:nowritebarrier":
-			if !compiling_runtime {
-				Yyerror("//go:nowritebarrier only allowed in runtime")
-			}
-			l.pragma |= Nowritebarrier
-		case "go:nowritebarrierrec":
-			if !compiling_runtime {
-				Yyerror("//go:nowritebarrierrec only allowed in runtime")
-			}
-			l.pragma |= Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
-		case "go:cgo_unsafe_args":
-			l.pragma |= CgoUnsafeArgs
-		case "go:uintptrescapes":
-			// For the next function declared in the file
-			// any uintptr arguments may be pointer values
-			// converted to uintptr. This directive
-			// ensures that the referenced allocated
-			// object, if any, is retained and not moved
-			// until the call completes, even though from
-			// the types alone it would appear that the
-			// object is no longer needed during the
-			// call. The conversion to uintptr must appear
-			// in the argument list.
-			// Used in syscall/dll_windows.go.
-			l.pragma |= UintptrEscapes
-		}
-		return c
-	}
-
-	// check for //line directive
-	if c != 'l' {
-		return c
-	}
-	for i := 1; i < 5; i++ {
-		c = l.getr()
-		if c != rune("line "[i]) {
-			return c
-		}
-	}
-
-	cp := &lexbuf
-	cp.Reset()
-	linep := 0
-	for {
-		c = l.getr()
-		if c == EOF {
-			return c
-		}
-		if c == '\n' {
-			break
-		}
-		if c == ' ' {
-			continue
-		}
-		if c == ':' {
-			linep = cp.Len() + 1
-		}
-		cp.WriteByte(byte(c))
-	}
-	cp = nil
-
-	if linep == 0 {
-		return c
-	}
-	text := strings.TrimSuffix(lexbuf.String(), "\r")
-	n, err := strconv.Atoi(text[linep:])
-	if err != nil {
-		return c // todo: make this an error instead? it is almost certainly a bug.
-	}
-	if n > 1e8 {
-		Yyerror("line number out of range")
-		errorexit()
-	}
-	if n <= 0 {
-		return c
-	}
-
-	linehistupdate(text[:linep-1], n)
-	return c
-}
-
 func pragcgo(text string) string {
 	f := pragmaFields(text)
 
@@ -1017,7 +144,7 @@ func pragcgo(text string) string {
 			return fmt.Sprintln(verb, local, remote)
 
 		default:
-			Yyerror(`usage: //go:%s local [remote]`, verb)
+			yyerror(`usage: //go:%s local [remote]`, verb)
 		}
 	case "cgo_import_dynamic":
 		switch {
@@ -1037,7 +164,7 @@ func pragcgo(text string) string {
 			return fmt.Sprintln(verb, local, remote, library)
 
 		default:
-			Yyerror(`usage: //go:cgo_import_dynamic local [remote ["library"]]`)
+			yyerror(`usage: //go:cgo_import_dynamic local [remote ["library"]]`)
 		}
 	case "cgo_import_static":
 		switch {
@@ -1046,7 +173,7 @@ func pragcgo(text string) string {
 			return fmt.Sprintln(verb, local)
 
 		default:
-			Yyerror(`usage: //go:cgo_import_static local`)
+			yyerror(`usage: //go:cgo_import_static local`)
 		}
 	case "cgo_dynamic_linker":
 		switch {
@@ -1055,7 +182,7 @@ func pragcgo(text string) string {
 			return fmt.Sprintln(verb, path)
 
 		default:
-			Yyerror(`usage: //go:cgo_dynamic_linker "path"`)
+			yyerror(`usage: //go:cgo_dynamic_linker "path"`)
 		}
 	case "cgo_ldflag":
 		switch {
@@ -1064,7 +191,7 @@ func pragcgo(text string) string {
 			return fmt.Sprintln(verb, arg)
 
 		default:
-			Yyerror(`usage: //go:cgo_ldflag "arg"`)
+			yyerror(`usage: //go:cgo_ldflag "arg"`)
 		}
 	}
 	return ""
@@ -1109,150 +236,3 @@ func pragmaFields(s string) []string {
 	}
 	return a
 }
-
-func (l *lexer) getr() rune {
-redo:
-	l.prevlineno = lexlineno
-	r, w, err := l.bin.ReadRune()
-	if err != nil {
-		if err != io.EOF {
-			Fatalf("io error: %v", err)
-		}
-		return -1
-	}
-	switch r {
-	case 0:
-		yyerrorl(lexlineno, "illegal NUL byte")
-	case '\n':
-		if importpkg == nil {
-			lexlineno++
-		}
-	case utf8.RuneError:
-		if w == 1 {
-			yyerrorl(lexlineno, "illegal UTF-8 sequence")
-		}
-	case BOM:
-		yyerrorl(lexlineno, "Unicode (UTF-8) BOM in middle of file")
-		goto redo
-	}
-
-	return r
-}
-
-func (l *lexer) ungetr() {
-	l.bin.UnreadRune()
-	lexlineno = l.prevlineno
-}
-
-// onechar lexes a single character within a rune or interpreted string literal,
-// handling escape sequences as necessary.
-func (l *lexer) onechar(quote rune) (r rune, b byte, ok bool) {
-	c := l.getr()
-	switch c {
-	case EOF:
-		Yyerror("eof in string")
-		l.ungetr()
-		return
-
-	case '\n':
-		Yyerror("newline in string")
-		l.ungetr()
-		return
-
-	case '\\':
-		break
-
-	case quote:
-		return
-
-	default:
-		return c, 0, true
-	}
-
-	c = l.getr()
-	switch c {
-	case 'x':
-		return 0, byte(l.hexchar(2)), true
-
-	case 'u':
-		return l.unichar(4), 0, true
-
-	case 'U':
-		return l.unichar(8), 0, true
-
-	case '0', '1', '2', '3', '4', '5', '6', '7':
-		x := c - '0'
-		for i := 2; i > 0; i-- {
-			c = l.getr()
-			if c >= '0' && c <= '7' {
-				x = x*8 + c - '0'
-				continue
-			}
-
-			Yyerror("non-octal character in escape sequence: %c", c)
-			l.ungetr()
-		}
-
-		if x > 255 {
-			Yyerror("octal escape value > 255: %d", x)
-		}
-
-		return 0, byte(x), true
-
-	case 'a':
-		c = '\a'
-	case 'b':
-		c = '\b'
-	case 'f':
-		c = '\f'
-	case 'n':
-		c = '\n'
-	case 'r':
-		c = '\r'
-	case 't':
-		c = '\t'
-	case 'v':
-		c = '\v'
-	case '\\':
-		c = '\\'
-
-	default:
-		if c != quote {
-			Yyerror("unknown escape sequence: %c", c)
-		}
-	}
-
-	return c, 0, true
-}
-
-func (l *lexer) unichar(n int) rune {
-	x := l.hexchar(n)
-	if x > utf8.MaxRune || 0xd800 <= x && x < 0xe000 {
-		Yyerror("invalid Unicode code point in escape sequence: %#x", x)
-		x = utf8.RuneError
-	}
-	return rune(x)
-}
-
-func (l *lexer) hexchar(n int) uint32 {
-	var x uint32
-
-	for ; n > 0; n-- {
-		var d uint32
-		switch c := l.getr(); {
-		case isDigit(c):
-			d = uint32(c - '0')
-		case 'a' <= c && c <= 'f':
-			d = uint32(c - 'a' + 10)
-		case 'A' <= c && c <= 'F':
-			d = uint32(c - 'A' + 10)
-		default:
-			Yyerror("non-hex character in escape sequence: %c", c)
-			l.ungetr()
-			return x
-		}
-		x = x*16 + d
-	}
-
-	return x
-}
diff --git a/src/cmd/compile/internal/gc/magic.go b/src/cmd/compile/internal/gc/magic.go
index f8eb182..4da30c4 100644
--- a/src/cmd/compile/internal/gc/magic.go
+++ b/src/cmd/compile/internal/gc/magic.go
@@ -25,7 +25,7 @@ type Magic struct {
 
 // magic number for signed division
 // see hacker's delight chapter 10
-func Smagic(m *Magic) {
+func smagic(m *Magic) {
 	var mask uint64
 
 	m.Bad = 0
@@ -120,7 +120,7 @@ func Smagic(m *Magic) {
 
 // magic number for unsigned division
 // see hacker's delight chapter 10
-func Umagic(m *Magic) {
+func umagic(m *Magic) {
 	var mask uint64
 
 	m.Bad = 0
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index b4df7ed..75f58a7 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -8,6 +8,7 @@ package gc
 
 import (
 	"bufio"
+	"bytes"
 	"cmd/compile/internal/ssa"
 	"cmd/internal/obj"
 	"cmd/internal/sys"
@@ -25,9 +26,6 @@ import (
 var imported_unsafe bool
 
 var (
-	goos    string
-	goarch  string
-	goroot  string
 	buildid string
 )
 
@@ -49,7 +47,7 @@ var debugtab = []struct {
 }{
 	{"append", &Debug_append},         // print information about append compilation
 	{"closure", &Debug_closure},       // print information about closure compilation
-	{"disablenil", &Disable_checknil}, // disable nil checks
+	{"disablenil", &disable_checknil}, // disable nil checks
 	{"gcprog", &Debug_gcprog},         // print dump of GC programs
 	{"nil", &Debug_checknil},          // print information about nil checks
 	{"panic", &Debug_panic},           // do not hide any compiler panic
@@ -86,7 +84,7 @@ func doversion() {
 	if p != "" {
 		sep = " "
 	}
-	fmt.Printf("compile version %s%s%s\n", obj.Getgoversion(), sep, p)
+	fmt.Printf("compile version %s%s%s\n", obj.Version, sep, p)
 	os.Exit(0)
 }
 
@@ -96,20 +94,24 @@ func supportsDynlink(arch *sys.Arch) bool {
 	return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X)
 }
 
+// timing data for compiler phases
+var timings Timings
+var benchfile string
+
+// Main parses flags and Go source files specified in the command-line
+// arguments, type-checks the parsed Go package, compiles functions to machine
+// code, and finally writes the compiled package definition to disk.
 func Main() {
-	defer hidePanic()
+	timings.Start("fe", "init")
 
-	goarch = obj.Getgoarch()
+	defer hidePanic()
 
 	Ctxt = obj.Linknew(Thearch.LinkArch)
-	Ctxt.DiagFunc = Yyerror
-	bstdout = bufio.NewWriter(os.Stdout)
-	Ctxt.Bso = bstdout
+	Ctxt.DiagFunc = yyerror
+	Ctxt.Bso = bufio.NewWriter(os.Stdout)
 
 	localpkg = mkpkg("")
 	localpkg.Prefix = "\"\""
-	autopkg = mkpkg("")
-	autopkg.Prefix = "\"\""
 
 	// pseudo-package, for scoping
 	builtinpkg = mkpkg("go.builtin")
@@ -144,26 +146,19 @@ func Main() {
 	mappkg.Name = "go.map"
 	mappkg.Prefix = "go.map"
 
-	goroot = obj.Getgoroot()
-	goos = obj.Getgoos()
-
-	Nacl = goos == "nacl"
+	Nacl = obj.GOOS == "nacl"
 	if Nacl {
 		flag_largemodel = true
 	}
 
 	flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
 	obj.Flagcount("%", "debug non-static initializers", &Debug['%'])
-	obj.Flagcount("A", "for bootstrapping, allow 'any' type", &Debug['A'])
 	obj.Flagcount("B", "disable bounds checking", &Debug['B'])
 	flag.StringVar(&localimport, "D", "", "set relative `path` for local imports")
 	obj.Flagcount("E", "debug symbol export", &Debug['E'])
 	obj.Flagfn1("I", "add `directory` to import search path", addidir)
 	obj.Flagcount("K", "debug missing line numbers", &Debug['K'])
-	obj.Flagcount("M", "debug move generation", &Debug['M'])
 	obj.Flagcount("N", "disable optimizations", &Debug['N'])
-	obj.Flagcount("P", "debug peephole optimizer", &Debug['P'])
-	obj.Flagcount("R", "debug register optimizer", &Debug['R'])
 	obj.Flagcount("S", "print assembly listing", &Debug['S'])
 	obj.Flagfn0("V", "print compiler version", doversion)
 	obj.Flagcount("W", "debug parse tree after type checking", &Debug['W'])
@@ -173,7 +168,6 @@ func Main() {
 	flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`")
 	obj.Flagcount("e", "no limit on number of errors reported", &Debug['e'])
 	obj.Flagcount("f", "debug stack frames", &Debug['f'])
-	obj.Flagcount("g", "debug code generation", &Debug['g'])
 	obj.Flagcount("h", "halt on error", &Debug['h'])
 	obj.Flagcount("i", "debug line number stack", &Debug['i'])
 	obj.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
@@ -184,7 +178,6 @@ func Main() {
 	obj.Flagcount("live", "debug liveness analysis", &debuglive)
 	obj.Flagcount("m", "print optimization decisions", &Debug['m'])
 	flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer")
-	flag.BoolVar(&newexport, "newexport", true, "use new export format") // TODO(gri) remove eventually (issue 15323)
 	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`")
@@ -197,7 +190,6 @@ func Main() {
 	obj.Flagcount("v", "increase debug verbosity", &Debug['v'])
 	obj.Flagcount("w", "debug type checking", &Debug['w'])
 	flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier")
-	obj.Flagcount("x", "debug lexer", &Debug['x'])
 	var flag_shared bool
 	var flag_dynlink bool
 	if supportsDynlink(Thearch.LinkArch.Arch) {
@@ -210,7 +202,8 @@ func Main() {
 	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
 	flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`")
 	flag.Int64Var(&memprofilerate, "memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
-	flag.BoolVar(&ssaEnabled, "ssa", true, "use SSA backend to generate code")
+	flag.StringVar(&traceprofile, "traceprofile", "", "write an execution trace to `file`")
+	flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
 	obj.Flagparse(usage)
 
 	Ctxt.Flag_shared = flag_dynlink || flag_shared
@@ -248,6 +241,7 @@ func Main() {
 				continue
 			}
 			val := 1
+			valstring := ""
 			if i := strings.Index(name, "="); i >= 0 {
 				var err error
 				val, err = strconv.Atoi(name[i+1:])
@@ -255,6 +249,9 @@ func Main() {
 					log.Fatalf("invalid debug value %v", name)
 				}
 				name = name[:i]
+			} else if i := strings.Index(name, ":"); i >= 0 {
+				valstring = name[i+1:]
+				name = name[:i]
 			}
 			for _, t := range debugtab {
 				if t.name == name {
@@ -275,7 +272,7 @@ func Main() {
 					flag = phase[i+1:]
 					phase = phase[:i]
 				}
-				err := ssa.PhaseOption(phase, flag, val)
+				err := ssa.PhaseOption(phase, flag, val, valstring)
 				if err != "" {
 					log.Fatalf(err)
 				}
@@ -293,7 +290,6 @@ func Main() {
 		Debug['l'] = 1 - Debug['l']
 	}
 
-	Thearch.Betypeinit()
 	Widthint = Thearch.LinkArch.IntSize
 	Widthptr = Thearch.LinkArch.PtrSize
 	Widthreg = Thearch.LinkArch.RegSize
@@ -305,33 +301,17 @@ func Main() {
 	nerrors = 0
 	lexlineno = 1
 
+	timings.Start("fe", "loadsys")
 	loadsys()
 
+	timings.Start("fe", "parse")
+	lexlineno0 := lexlineno
 	for _, infile = range flag.Args() {
-		if trace && Debug['x'] != 0 {
-			fmt.Printf("--- %s ---\n", infile)
-		}
-
 		linehistpush(infile)
-
-		f, err := os.Open(infile)
-		if err != nil {
-			fmt.Printf("open %s: %v\n", infile, err)
-			errorexit()
-		}
-		bin := bufio.NewReader(f)
-
-		// Skip initial BOM if present.
-		if r, _, _ := bin.ReadRune(); r != BOM {
-			bin.UnreadRune()
-		}
-
 		block = 1
 		iota_ = -1000000
-
 		imported_unsafe = false
-
-		parse_file(bin)
+		parseFile(infile)
 		if nsyntaxerrors != 0 {
 			errorexit()
 		}
@@ -340,10 +320,10 @@ func Main() {
 		// for the line history to work, and which then has to be corrected elsewhere,
 		// just add a line here.
 		lexlineno++
-
 		linehistpop()
-		f.Close()
 	}
+	timings.Stop()
+	timings.AddEvent(int64(lexlineno-lexlineno0), "lines")
 
 	testdclstack()
 	mkpackage(localpkg.Name) // final import not used checks
@@ -362,6 +342,7 @@ func Main() {
 	defercheckwidth()
 
 	// Don't use range--typecheck can add closures to xtop.
+	timings.Start("fe", "typecheck", "top1")
 	for i := 0; i < len(xtop); i++ {
 		if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 {
 			xtop[i] = typecheck(xtop[i], Etop)
@@ -372,6 +353,7 @@ func Main() {
 	//   To check interface assignments, depends on phase 1.
 
 	// Don't use range--typecheck can add closures to xtop.
+	timings.Start("fe", "typecheck", "top2")
 	for i := 0; i < len(xtop); i++ {
 		if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 {
 			xtop[i] = typecheck(xtop[i], Etop)
@@ -381,6 +363,8 @@ func Main() {
 
 	// Phase 3: Type check function bodies.
 	// Don't use range--typecheck can add closures to xtop.
+	timings.Start("fe", "typecheck", "func")
+	var fcount int64
 	for i := 0; i < len(xtop); i++ {
 		if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE {
 			Curfn = xtop[i]
@@ -391,12 +375,15 @@ func Main() {
 			if nerrors != 0 {
 				Curfn.Nbody.Set(nil) // type errors; do not compile
 			}
+			fcount++
 		}
 	}
+	timings.AddEvent(fcount, "funcs")
 
 	// Phase 4: Decide how to capture closed variables.
 	// This needs to run before escape analysis,
 	// because variables captured by value do not escape.
+	timings.Start("fe", "capturevars")
 	for _, n := range xtop {
 		if n.Op == ODCLFUNC && n.Func.Closure != nil {
 			Curfn = n
@@ -411,6 +398,7 @@ func Main() {
 	}
 
 	// Phase 5: Inlining
+	timings.Start("fe", "inlining")
 	if Debug['l'] > 1 {
 		// Typecheck imported function bodies if debug['l'] > 1,
 		// otherwise lazily when used or re-exported.
@@ -430,10 +418,14 @@ func Main() {
 		// Find functions that can be inlined and clone them before walk expands them.
 		visitBottomUp(xtop, func(list []*Node, recursive bool) {
 			for _, n := range list {
-				if n.Op == ODCLFUNC {
+				if !recursive {
 					caninl(n)
-					inlcalls(n)
+				} else {
+					if Debug['m'] > 1 {
+						fmt.Printf("%v: cannot inline %v: recursive\n", n.Line(), n.Func.Nname)
+					}
 				}
+				inlcalls(n)
 			}
 		})
 	}
@@ -446,11 +438,13 @@ func Main() {
 	// or else the stack copier will not update it.
 	// Large values are also moved off stack in escape analysis;
 	// because large values may contain pointers, it must happen early.
+	timings.Start("fe", "escapes")
 	escapes(xtop)
 
 	// 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
@@ -462,11 +456,15 @@ func Main() {
 
 	// 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++ {
 		if xtop[i].Op == ODCLFUNC {
 			funccompile(xtop[i])
+			fcount++
 		}
 	}
+	timings.AddEvent(fcount, "funcs")
 
 	if nsavederrors+nerrors == 0 {
 		fninit(xtop)
@@ -477,6 +475,7 @@ func Main() {
 	}
 
 	// Phase 9: Check external declarations.
+	timings.Start("be", "externaldcls")
 	for i, n := range externdcl {
 		if n.Op == ONAME {
 			externdcl[i] = typecheck(externdcl[i], Erv)
@@ -487,8 +486,9 @@ func Main() {
 		errorexit()
 	}
 
+	// Write object data to disk.
+	timings.Start("be", "dumpobj")
 	dumpobj()
-
 	if asmhdr != "" {
 		dumpasmhdr()
 	}
@@ -497,7 +497,37 @@ func Main() {
 		errorexit()
 	}
 
-	Flusherrors()
+	flusherrors()
+	timings.Stop()
+
+	if benchfile != "" {
+		if err := writebench(benchfile); err != nil {
+			log.Fatalf("cannot write benchmark data: %v", err)
+		}
+	}
+}
+
+func writebench(filename string) error {
+	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
+	if err != nil {
+		return err
+	}
+
+	var buf bytes.Buffer
+	fmt.Fprintln(&buf, "commit:", obj.Version)
+	fmt.Fprintln(&buf, "goos:", runtime.GOOS)
+	fmt.Fprintln(&buf, "goarch:", runtime.GOARCH)
+	timings.Write(&buf, "BenchmarkCompile:"+myimportpath+":")
+
+	n, err := f.Write(buf.Bytes())
+	if err != nil {
+		return err
+	}
+	if n != buf.Len() {
+		panic("bad writer")
+	}
+
+	return f.Close()
 }
 
 var importMap = map[string]string{}
@@ -592,7 +622,7 @@ func findpkg(name string) (file string, ok bool) {
 	// don't want to see "encoding/../encoding/base64"
 	// as different from "encoding/base64".
 	if q := path.Clean(name); q != name {
-		Yyerror("non-canonical import path %q (should be %q)", name, q)
+		yyerror("non-canonical import path %q (should be %q)", name, q)
 		return "", false
 	}
 
@@ -607,7 +637,7 @@ func findpkg(name string) (file string, ok bool) {
 		}
 	}
 
-	if goroot != "" {
+	if obj.GOROOT != "" {
 		suffix := ""
 		suffixsep := ""
 		if flag_installsuffix != "" {
@@ -621,11 +651,11 @@ func findpkg(name string) (file string, ok bool) {
 			suffix = "msan"
 		}
 
-		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", goroot, goos, goarch, suffixsep, suffix, name)
+		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", obj.GOROOT, obj.GOOS, obj.GOARCH, suffixsep, suffix, name)
 		if _, err := os.Stat(file); err == nil {
 			return file, true
 		}
-		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", goroot, goos, goarch, suffixsep, suffix, name)
+		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", obj.GOROOT, obj.GOOS, obj.GOARCH, suffixsep, suffix, name)
 		if _, err := os.Stat(file); err == nil {
 			return file, true
 		}
@@ -634,39 +664,37 @@ func findpkg(name string) (file string, ok bool) {
 	return "", false
 }
 
-// loadsys loads the definitions for the low-level runtime and unsafe functions,
+// loadsys loads the definitions for the low-level runtime functions,
 // so that the compiler can generate calls to them,
-// but does not make the names "runtime" or "unsafe" visible as packages.
+// but does not make them visible to user code.
 func loadsys() {
-	if Debug['A'] != 0 {
-		return
-	}
-
 	block = 1
 	iota_ = -1000000
-	incannedimport = 1
-
-	// The first byte in the binary export format is a 'c' or 'd'
-	// specifying the encoding format. We could just check that
-	// byte, but this is a perhaps more robust. Also, it is not
-	// speed-critical.
-	// TODO(gri) simplify once textual export format has gone
-	if strings.HasPrefix(runtimeimport, "package") {
-		// textual export format
-		importpkg = Runtimepkg
-		parse_import(bufio.NewReader(strings.NewReader(runtimeimport)), nil)
-		importpkg = unsafepkg
-		parse_import(bufio.NewReader(strings.NewReader(unsafeimport)), nil)
-	} else {
-		// binary export format
-		importpkg = Runtimepkg
-		Import(bufio.NewReader(strings.NewReader(runtimeimport)))
-		importpkg = unsafepkg
-		Import(bufio.NewReader(strings.NewReader(unsafeimport)))
+
+	importpkg = Runtimepkg
+	typecheckok = true
+	defercheckwidth()
+
+	typs := runtimeTypes()
+	for _, d := range runtimeDecls {
+		sym := Pkglookup(d.name, importpkg)
+		typ := typs[d.typ]
+		switch d.tag {
+		case funcTag:
+			importsym(sym, ONAME)
+			n := newfuncname(sym)
+			n.Type = typ
+			declare(n, PFUNC)
+		case varTag:
+			importvar(sym, typ)
+		default:
+			Fatalf("unhandled declaration tag %v", d.tag)
+		}
 	}
 
+	typecheckok = false
+	resumecheckwidth()
 	importpkg = nil
-	incannedimport = 0
 }
 
 func importfile(f *Val, indent []byte) {
@@ -676,12 +704,12 @@ func importfile(f *Val, indent []byte) {
 
 	path_, ok := f.U.(string)
 	if !ok {
-		Yyerror("import statement not a string")
+		yyerror("import statement not a string")
 		return
 	}
 
 	if len(path_) == 0 {
-		Yyerror("import path is empty")
+		yyerror("import path is empty")
 		return
 	}
 
@@ -694,12 +722,12 @@ func importfile(f *Val, indent []byte) {
 	// the main package, just as we reserve the import
 	// path "math" to identify the standard math package.
 	if path_ == "main" {
-		Yyerror("cannot import \"main\"")
+		yyerror("cannot import \"main\"")
 		errorexit()
 	}
 
 	if myimportpath != "" && path_ == myimportpath {
-		Yyerror("import %q while compiling that package (import cycle)", path_)
+		yyerror("import %q while compiling that package (import cycle)", path_)
 		errorexit()
 	}
 
@@ -709,7 +737,7 @@ func importfile(f *Val, indent []byte) {
 
 	if path_ == "unsafe" {
 		if safemode {
-			Yyerror("cannot import package unsafe")
+			yyerror("cannot import package unsafe")
 			errorexit()
 		}
 
@@ -720,7 +748,7 @@ func importfile(f *Val, indent []byte) {
 
 	if islocalname(path_) {
 		if path_[0] == '/' {
-			Yyerror("import path cannot be absolute path")
+			yyerror("import path cannot be absolute path")
 			return
 		}
 
@@ -737,7 +765,7 @@ func importfile(f *Val, indent []byte) {
 
 	file, found := findpkg(path_)
 	if !found {
-		Yyerror("can't find import: %q", path_)
+		yyerror("can't find import: %q", path_)
 		errorexit()
 	}
 
@@ -751,7 +779,7 @@ func importfile(f *Val, indent []byte) {
 
 	impf, err := os.Open(file)
 	if err != nil {
-		Yyerror("can't open import: %q: %v", path_, err)
+		yyerror("can't open import: %q: %v", path_, err)
 		errorexit()
 	}
 	defer impf.Close()
@@ -759,7 +787,7 @@ func importfile(f *Val, indent []byte) {
 
 	if strings.HasSuffix(file, ".a") {
 		if !skiptopkgdef(imp) {
-			Yyerror("import %s: not a package file", file)
+			yyerror("import %s: not a package file", file)
 			errorexit()
 		}
 	}
@@ -775,18 +803,19 @@ func importfile(f *Val, indent []byte) {
 
 	if p != "empty archive" {
 		if !strings.HasPrefix(p, "go object ") {
-			Yyerror("import %s: not a go object file: %s", file, p)
+			yyerror("import %s: not a go object file: %s", file, p)
 			errorexit()
 		}
 
-		q := fmt.Sprintf("%s %s %s %s", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring())
+		q := fmt.Sprintf("%s %s %s %s", obj.GOOS, obj.GOARCH, obj.Version, obj.Expstring())
 		if p[10:] != q {
-			Yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q)
+			yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q)
 			errorexit()
 		}
 	}
 
 	// process header lines
+	safe := false
 	for {
 		p, err = imp.ReadString('\n')
 		if err != nil {
@@ -796,18 +825,21 @@ func importfile(f *Val, indent []byte) {
 			break // header ends with blank line
 		}
 		if strings.HasPrefix(p, "safe") {
-			importpkg.Safe = true
+			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.
 	linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib
 
 	// In the importfile, if we find:
-	// $$\n  (old format): position the input right after $$\n and return
-	// $$B\n (new format): import directly, then feed the lexer a dummy statement
+	// $$\n  (textual format): not supported anymore
+	// $$B\n (binary format) : import directly, then feed the lexer a dummy statement
 
 	// look for $$
 	var c byte
@@ -831,11 +863,9 @@ func importfile(f *Val, indent []byte) {
 
 	switch c {
 	case '\n':
-		// old export format
-		parse_import(imp, indent)
+		yyerror("cannot import %s: old export format no longer supported (recompile library)", path_)
 
 	case 'B':
-		// new export format
 		if Debug_export != 0 {
 			fmt.Printf("importing %s (%s)\n", path_, file)
 		}
@@ -843,13 +873,9 @@ func importfile(f *Val, indent []byte) {
 		Import(imp)
 
 	default:
-		Yyerror("no import in %q", path_)
+		yyerror("no import in %q", path_)
 		errorexit()
 	}
-
-	if safemode && !importpkg.Safe {
-		Yyerror("cannot import unsafe package %q", importpkg.Path)
-	}
 }
 
 func pkgnotused(lineno int32, path string, name string) {
@@ -873,12 +899,12 @@ func pkgnotused(lineno int32, path string, name string) {
 func mkpackage(pkgname string) {
 	if localpkg.Name == "" {
 		if pkgname == "_" {
-			Yyerror("invalid package name _")
+			yyerror("invalid package name _")
 		}
 		localpkg.Name = pkgname
 	} else {
 		if pkgname != localpkg.Name {
-			Yyerror("package %s; expected %s", pkgname, localpkg.Name)
+			yyerror("package %s; expected %s", pkgname, localpkg.Name)
 		}
 		for _, s := range localpkg.Syms {
 			if s.Def == nil {
@@ -897,7 +923,7 @@ func mkpackage(pkgname string) {
 				continue
 			}
 
-			if s.Def.Sym != s {
+			if s.Def.Sym != s && s.Flags&SymAlias == 0 {
 				// throw away top-level name left over
 				// from previous import . "x"
 				if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
diff --git a/src/cmd/compile/internal/gc/mkbuiltin.go b/src/cmd/compile/internal/gc/mkbuiltin.go
index 58cbd24..995f5be 100644
--- a/src/cmd/compile/internal/gc/mkbuiltin.go
+++ b/src/cmd/compile/internal/gc/mkbuiltin.go
@@ -1,25 +1,28 @@
-// Copyright 2009 The Go Authors. All rights reserved.
+// Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
 // +build ignore
 
-// Generate builtin.go from builtin/runtime.go and builtin/unsafe.go.
-// Run this after changing builtin/runtime.go and builtin/unsafe.go
-// or after changing the export metadata format in the compiler.
-// Either way, you need to have a working compiler binary first.
-// See bexport.go for how to make an export metadata format change.
+// Generate builtin.go from builtin/runtime.go.
+
 package main
 
 import (
 	"bytes"
 	"flag"
 	"fmt"
+	"go/ast"
+	"go/format"
+	"go/parser"
+	"go/token"
 	"io"
 	"io/ioutil"
 	"log"
 	"os"
-	"os/exec"
+	"path/filepath"
+	"strconv"
+	"strings"
 )
 
 var stdout = flag.Bool("stdout", false, "write to stdout instead of builtin.go")
@@ -29,85 +32,181 @@ func main() {
 
 	var b bytes.Buffer
 	fmt.Fprintln(&b, "// AUTO-GENERATED by mkbuiltin.go; DO NOT EDIT")
-	fmt.Fprintln(&b, "")
+	fmt.Fprintln(&b)
 	fmt.Fprintln(&b, "package gc")
 
 	mkbuiltin(&b, "runtime")
-	mkbuiltin(&b, "unsafe")
 
-	var err error
+	out, err := format.Source(b.Bytes())
+	if err != nil {
+		log.Fatal(err)
+	}
 	if *stdout {
-		_, err = os.Stdout.Write(b.Bytes())
+		_, err = os.Stdout.Write(out)
 	} else {
-		err = ioutil.WriteFile("builtin.go", b.Bytes(), 0666)
+		err = ioutil.WriteFile("builtin.go", out, 0666)
 	}
 	if err != nil {
 		log.Fatal(err)
 	}
 }
 
-// Compile .go file, import data from .o file, and write Go string version.
 func mkbuiltin(w io.Writer, name string) {
-	args := []string{"tool", "compile", "-A"}
-	if name == "runtime" {
-		args = append(args, "-u")
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, filepath.Join("builtin", name+".go"), nil, 0)
+	if err != nil {
+		log.Fatal(err)
 	}
-	args = append(args, "builtin/"+name+".go")
 
-	if err := exec.Command("go", args...).Run(); err != nil {
-		log.Fatal(err)
+	var interner typeInterner
+
+	fmt.Fprintf(w, "var %sDecls = [...]struct { name string; tag int; typ int }{\n", name)
+	for _, decl := range f.Decls {
+		switch decl := decl.(type) {
+		case *ast.FuncDecl:
+			if decl.Recv != nil {
+				log.Fatal("methods unsupported")
+			}
+			if decl.Body != nil {
+				log.Fatal("unexpected function body")
+			}
+			fmt.Fprintf(w, "{%q, funcTag, %d},\n", decl.Name.Name, interner.intern(decl.Type))
+		case *ast.GenDecl:
+			if decl.Tok != token.VAR {
+				log.Fatal("unhandled declaration kind", decl.Tok)
+			}
+			for _, spec := range decl.Specs {
+				spec := spec.(*ast.ValueSpec)
+				if len(spec.Values) != 0 {
+					log.Fatal("unexpected values")
+				}
+				typ := interner.intern(spec.Type)
+				for _, name := range spec.Names {
+					fmt.Fprintf(w, "{%q, varTag, %d},\n", name.Name, typ)
+				}
+			}
+		default:
+			log.Fatal("unhandled decl type", decl)
+		}
 	}
-	obj := name + ".o"
-	defer os.Remove(obj)
+	fmt.Fprintln(w, "}")
 
-	b, err := ioutil.ReadFile(obj)
-	if err != nil {
-		log.Fatal(err)
+	fmt.Fprintln(w)
+	fmt.Fprintf(w, "func %sTypes() []*Type {\n", name)
+	fmt.Fprintf(w, "var typs [%d]*Type\n", len(interner.typs))
+	for i, typ := range interner.typs {
+		fmt.Fprintf(w, "typs[%d] = %s\n", i, typ)
 	}
+	fmt.Fprintln(w, "return typs[:]")
+	fmt.Fprintln(w, "}")
+}
+
+// typeInterner maps Go type expressions to compiler code that
+// constructs the denoted type. It recognizes and reuses common
+// subtype expressions.
+type typeInterner struct {
+	typs []string
+	hash map[string]int
+}
 
-	// Look for $$B that introduces binary export data.
-	textual := false // TODO(gri) remove once we switched to binary export format
-	i := bytes.Index(b, []byte("\n$$B\n"))
-	if i < 0 {
-		// Look for $$ that introduces textual export data.
-		i = bytes.Index(b, []byte("\n$$\n"))
-		if i < 0 {
-			log.Fatal("did not find beginning of export data")
+func (i *typeInterner) intern(t ast.Expr) int {
+	x := i.mktype(t)
+	v, ok := i.hash[x]
+	if !ok {
+		v = len(i.typs)
+		if i.hash == nil {
+			i.hash = make(map[string]int)
 		}
-		textual = true
-		i-- // textual data doesn't have B
+		i.hash[x] = v
+		i.typs = append(i.typs, x)
 	}
-	b = b[i+5:]
+	return v
+}
+
+func (i *typeInterner) subtype(t ast.Expr) string {
+	return fmt.Sprintf("typs[%d]", i.intern(t))
+}
+
+func (i *typeInterner) mktype(t ast.Expr) string {
+	switch t := t.(type) {
+	case *ast.Ident:
+		switch t.Name {
+		case "byte":
+			return "bytetype"
+		case "rune":
+			return "runetype"
+		}
+		return fmt.Sprintf("Types[T%s]", strings.ToUpper(t.Name))
+
+	case *ast.ArrayType:
+		if t.Len == nil {
+			return fmt.Sprintf("typSlice(%s)", i.subtype(t.Elt))
+		}
+		return fmt.Sprintf("typArray(%s, %d)", i.subtype(t.Elt), intconst(t.Len))
+	case *ast.ChanType:
+		dir := "Cboth"
+		switch t.Dir {
+		case ast.SEND:
+			dir = "Csend"
+		case ast.RECV:
+			dir = "Crecv"
+		}
+		return fmt.Sprintf("typChan(%s, %s)", i.subtype(t.Value), dir)
+	case *ast.FuncType:
+		return fmt.Sprintf("functype(nil, %s, %s)", i.fields(t.Params, false), i.fields(t.Results, false))
+	case *ast.InterfaceType:
+		if len(t.Methods.List) != 0 {
+			log.Fatal("non-empty interfaces unsupported")
+		}
+		return "Types[TINTER]"
+	case *ast.MapType:
+		return fmt.Sprintf("typMap(%s, %s)", i.subtype(t.Key), i.subtype(t.Value))
+	case *ast.StarExpr:
+		return fmt.Sprintf("typPtr(%s)", i.subtype(t.X))
+	case *ast.StructType:
+		return fmt.Sprintf("tostruct(%s)", i.fields(t.Fields, true))
 
-	// Look for $$ that closes export data.
-	i = bytes.Index(b, []byte("\n$$\n"))
-	if i < 0 {
-		log.Fatal("did not find end of export data")
+	default:
+		log.Fatalf("unhandled type: %#v", t)
+		panic("unreachable")
 	}
-	b = b[:i+4]
-
-	// Process and reformat export data.
-	fmt.Fprintf(w, "\nconst %simport = \"\"", name)
-	if textual {
-		for _, p := range bytes.SplitAfter(b, []byte("\n")) {
-			// Chop leading white space.
-			p = bytes.TrimLeft(p, " \t")
-			if len(p) == 0 {
-				continue
+}
+
+func (i *typeInterner) fields(fl *ast.FieldList, keepNames bool) string {
+	if fl == nil || len(fl.List) == 0 {
+		return "nil"
+	}
+	var res []string
+	for _, f := range fl.List {
+		typ := i.subtype(f.Type)
+		if len(f.Names) == 0 {
+			res = append(res, fmt.Sprintf("anonfield(%s)", typ))
+		} else {
+			for _, name := range f.Names {
+				if keepNames {
+					res = append(res, fmt.Sprintf("namedfield(%q, %s)", name.Name, typ))
+				} else {
+					res = append(res, fmt.Sprintf("anonfield(%s)", typ))
+				}
 			}
+		}
+	}
+	return fmt.Sprintf("[]*Node{%s}", strings.Join(res, ", "))
+}
 
-			fmt.Fprintf(w, " +\n\t%q", p)
+func intconst(e ast.Expr) int64 {
+	switch e := e.(type) {
+	case *ast.BasicLit:
+		if e.Kind != token.INT {
+			log.Fatalf("expected INT, got %v", e.Kind)
 		}
-	} else {
-		const n = 40 // number of bytes per line
-		for len(b) > 0 {
-			i := len(b)
-			if i > n {
-				i = n
-			}
-			fmt.Fprintf(w, " +\n\t%q", b[:i])
-			b = b[i:]
+		x, err := strconv.ParseInt(e.Value, 0, 64)
+		if err != nil {
+			log.Fatal(err)
 		}
+		return x
+	default:
+		log.Fatalf("unhandled expr: %#v", e)
+		panic("unreachable")
 	}
-	fmt.Fprintf(w, "\n")
 }
diff --git a/src/cmd/compile/internal/gc/mpfloat.go b/src/cmd/compile/internal/gc/mpfloat.go
index a0f15a9..c851022 100644
--- a/src/cmd/compile/internal/gc/mpfloat.go
+++ b/src/cmd/compile/internal/gc/mpfloat.go
@@ -5,9 +5,9 @@
 package gc
 
 import (
-	"cmd/compile/internal/big"
 	"fmt"
 	"math"
+	"math/big"
 )
 
 // implements float arithmetic
@@ -128,7 +128,7 @@ func (a *Mpflt) Float64() float64 {
 
 	// check for overflow
 	if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
-		Yyerror("mpgetflt ovf")
+		yyerror("ovf in Mpflt Float64")
 	}
 
 	return x + 0 // avoid -0 (should not be needed, but be conservative)
@@ -140,7 +140,7 @@ func (a *Mpflt) Float32() float64 {
 
 	// check for overflow
 	if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
-		Yyerror("mpgetflt32 ovf")
+		yyerror("ovf in Mpflt Float32")
 	}
 
 	return x + 0 // avoid -0 (should not be needed, but be conservative)
@@ -187,13 +187,13 @@ func (a *Mpflt) SetString(as string) {
 		// - constant exponent out of range
 		// - decimal point and binary point in constant
 		// TODO(gri) use different conversion function or check separately
-		Yyerror("malformed constant: %s", as)
+		yyerror("malformed constant: %s", as)
 		a.Val.SetFloat64(0)
 		return
 	}
 
 	if f.IsInf() {
-		Yyerror("constant too large: %s", as)
+		yyerror("constant too large: %s", as)
 		a.Val.SetFloat64(0)
 		return
 	}
diff --git a/src/cmd/compile/internal/gc/mpint.go b/src/cmd/compile/internal/gc/mpint.go
index fe37baa..fba8260 100644
--- a/src/cmd/compile/internal/gc/mpint.go
+++ b/src/cmd/compile/internal/gc/mpint.go
@@ -5,8 +5,8 @@
 package gc
 
 import (
-	"cmd/compile/internal/big"
 	"fmt"
+	"math/big"
 )
 
 // implements integer arithmetic
@@ -71,7 +71,7 @@ func (a *Mpint) SetFloat(b *Mpflt) int {
 func (a *Mpint) Add(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mpaddfixfix")
+			yyerror("ovf in Mpint Add")
 		}
 		a.SetOverflow()
 		return
@@ -80,14 +80,14 @@ func (a *Mpint) Add(b *Mpint) {
 	a.Val.Add(&a.Val, &b.Val)
 
 	if a.checkOverflow(0) {
-		Yyerror("constant addition overflow")
+		yyerror("constant addition overflow")
 	}
 }
 
 func (a *Mpint) Sub(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mpsubfixfix")
+			yyerror("ovf in Mpint Sub")
 		}
 		a.SetOverflow()
 		return
@@ -96,14 +96,14 @@ func (a *Mpint) Sub(b *Mpint) {
 	a.Val.Sub(&a.Val, &b.Val)
 
 	if a.checkOverflow(0) {
-		Yyerror("constant subtraction overflow")
+		yyerror("constant subtraction overflow")
 	}
 }
 
 func (a *Mpint) Mul(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mpmulfixfix")
+			yyerror("ovf in Mpint Mul")
 		}
 		a.SetOverflow()
 		return
@@ -112,14 +112,14 @@ func (a *Mpint) Mul(b *Mpint) {
 	a.Val.Mul(&a.Val, &b.Val)
 
 	if a.checkOverflow(0) {
-		Yyerror("constant multiplication overflow")
+		yyerror("constant multiplication overflow")
 	}
 }
 
 func (a *Mpint) Quo(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mpdivfixfix")
+			yyerror("ovf in Mpint Quo")
 		}
 		a.SetOverflow()
 		return
@@ -129,14 +129,14 @@ func (a *Mpint) Quo(b *Mpint) {
 
 	if a.checkOverflow(0) {
 		// can only happen for div-0 which should be checked elsewhere
-		Yyerror("constant division overflow")
+		yyerror("constant division overflow")
 	}
 }
 
 func (a *Mpint) Rem(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mpmodfixfix")
+			yyerror("ovf in Mpint Rem")
 		}
 		a.SetOverflow()
 		return
@@ -146,14 +146,14 @@ func (a *Mpint) Rem(b *Mpint) {
 
 	if a.checkOverflow(0) {
 		// should never happen
-		Yyerror("constant modulo overflow")
+		yyerror("constant modulo overflow")
 	}
 }
 
 func (a *Mpint) Or(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mporfixfix")
+			yyerror("ovf in Mpint Or")
 		}
 		a.SetOverflow()
 		return
@@ -165,7 +165,7 @@ func (a *Mpint) Or(b *Mpint) {
 func (a *Mpint) And(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mpandfixfix")
+			yyerror("ovf in Mpint And")
 		}
 		a.SetOverflow()
 		return
@@ -177,7 +177,7 @@ func (a *Mpint) And(b *Mpint) {
 func (a *Mpint) AndNot(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mpandnotfixfix")
+			yyerror("ovf in Mpint AndNot")
 		}
 		a.SetOverflow()
 		return
@@ -189,7 +189,7 @@ func (a *Mpint) AndNot(b *Mpint) {
 func (a *Mpint) Xor(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mpxorfixfix")
+			yyerror("ovf in Mpint Xor")
 		}
 		a.SetOverflow()
 		return
@@ -201,7 +201,7 @@ func (a *Mpint) Xor(b *Mpint) {
 func (a *Mpint) Lsh(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mplshfixfix")
+			yyerror("ovf in Mpint Lsh")
 		}
 		a.SetOverflow()
 		return
@@ -213,13 +213,13 @@ func (a *Mpint) Lsh(b *Mpint) {
 		if s < 0 {
 			msg = "invalid negative shift count"
 		}
-		Yyerror("%s: %d", msg, s)
+		yyerror("%s: %d", msg, s)
 		a.SetInt64(0)
 		return
 	}
 
 	if a.checkOverflow(int(s)) {
-		Yyerror("constant shift overflow")
+		yyerror("constant shift overflow")
 		return
 	}
 	a.Val.Lsh(&a.Val, uint(s))
@@ -228,7 +228,7 @@ func (a *Mpint) Lsh(b *Mpint) {
 func (a *Mpint) Rsh(b *Mpint) {
 	if a.Ovf || b.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("ovf in mprshfixfix")
+			yyerror("ovf in Mpint Rsh")
 		}
 		a.SetOverflow()
 		return
@@ -236,7 +236,7 @@ func (a *Mpint) Rsh(b *Mpint) {
 
 	s := b.Int64()
 	if s < 0 {
-		Yyerror("invalid negative shift count: %d", s)
+		yyerror("invalid negative shift count: %d", s)
 		if a.Val.Sign() < 0 {
 			a.SetInt64(-1)
 		} else {
@@ -266,7 +266,7 @@ func (a *Mpint) Neg() {
 func (a *Mpint) Int64() int64 {
 	if a.Ovf {
 		if nsavederrors+nerrors == 0 {
-			Yyerror("constant overflow")
+			yyerror("constant overflow")
 		}
 		return 0
 	}
@@ -288,12 +288,12 @@ func (a *Mpint) SetString(as string) {
 		// - malformed octal constant
 		// - malformed decimal constant
 		// TODO(gri) use different conversion function
-		Yyerror("malformed integer constant: %s", as)
+		yyerror("malformed integer constant: %s", as)
 		a.Val.SetUint64(0)
 		return
 	}
 	if a.checkOverflow(0) {
-		Yyerror("constant too large: %s", as)
+		yyerror("constant too large: %s", as)
 	}
 }
 
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
new file mode 100644
index 0000000..f9de48a
--- /dev/null
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -0,0 +1,1083 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gc
+
+import (
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+	"unicode/utf8"
+
+	"cmd/compile/internal/syntax"
+)
+
+func parseFile(filename string) {
+	src, err := os.Open(filename)
+	if err != nil {
+		fmt.Println(err)
+		errorexit()
+	}
+	defer src.Close()
+
+	p := noder{baseline: lexlineno}
+	file, _ := syntax.Parse(src, p.error, p.pragma, 0) // errors are tracked via p.error
+
+	p.file(file)
+
+	if !imported_unsafe {
+		for _, x := range p.linknames {
+			p.error(syntax.Error{Line: x, Msg: "//go:linkname only allowed in Go files that import \"unsafe\""})
+		}
+	}
+
+	if nsyntaxerrors == 0 {
+		testdclstack()
+	}
+}
+
+// noder transforms package syntax's AST into a Nod tree.
+type noder struct {
+	baseline  int32
+	linknames []int // tracks //go:linkname lines
+}
+
+func (p *noder) file(file *syntax.File) {
+	p.lineno(file.PkgName)
+	mkpackage(file.PkgName.Value)
+
+	xtop = append(xtop, p.decls(file.DeclList)...)
+
+	lexlineno = p.baseline + int32(file.Lines) - 1
+	lineno = lexlineno
+}
+
+func (p *noder) decls(decls []syntax.Decl) (l []*Node) {
+	var lastConstGroup *syntax.Group
+	var lastConstRHS []*Node
+	var iotaVal int64
+
+	for _, decl := range decls {
+		p.lineno(decl)
+		switch decl := decl.(type) {
+		case *syntax.ImportDecl:
+			p.importDecl(decl)
+
+		case *syntax.VarDecl:
+			l = append(l, p.varDecl(decl)...)
+
+		case *syntax.ConstDecl:
+			// Tricky to handle golang.org/issue/15550 correctly.
+
+			prevIota := iota_
+
+			if decl.Group == nil || decl.Group != lastConstGroup {
+				iotaVal = 0
+				lastConstRHS = nil
+			}
+
+			iota_ = iotaVal
+			lastconst = lastConstRHS
+
+			l = append(l, p.constDecl(decl)...)
+
+			lastConstRHS = lastconst
+			lastconst = nil
+
+			iota_ = prevIota
+			iotaVal++
+
+			lastConstGroup = decl.Group
+
+		case *syntax.TypeDecl:
+			l = append(l, p.typeDecl(decl))
+
+		case *syntax.FuncDecl:
+			l = append(l, p.funcDecl(decl))
+
+		default:
+			panic("unhandled Decl")
+		}
+	}
+
+	return
+}
+
+func (p *noder) importDecl(imp *syntax.ImportDecl) {
+	val := p.basicLit(imp.Path)
+	importfile(&val, nil)
+	ipkg := importpkg
+	importpkg = nil
+
+	if ipkg == nil {
+		if nerrors == 0 {
+			Fatalf("phase error in import")
+		}
+		return
+	}
+
+	ipkg.Direct = true
+
+	var my *Sym
+	if imp.LocalPkgName != nil {
+		my = p.name(imp.LocalPkgName)
+	} else {
+		my = lookup(ipkg.Name)
+	}
+
+	pack := p.nod(imp, OPACK, nil, nil)
+	pack.Sym = my
+	pack.Name.Pkg = ipkg
+
+	if my.Name == "." {
+		importdot(ipkg, pack)
+		return
+	}
+	if my.Name == "init" {
+		yyerrorl(pack.Lineno, "cannot import package as init - init must be a func")
+		return
+	}
+	if my.Name == "_" {
+		return
+	}
+	if my.Def != nil {
+		lineno = pack.Lineno
+		redeclare(my, "as imported package name")
+	}
+	my.Def = pack
+	my.Lastlineno = pack.Lineno
+	my.Block = 1 // at top level
+}
+
+func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
+	names := p.declNames(decl.NameList)
+
+	var typ *Node
+	if decl.Type != nil {
+		typ = p.typeExpr(decl.Type)
+	}
+
+	var exprs []*Node
+	if decl.Values != nil {
+		exprs = p.exprList(decl.Values)
+	}
+
+	p.lineno(decl)
+	return variter(names, typ, exprs)
+}
+
+func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
+	names := p.declNames(decl.NameList)
+
+	var typ *Node
+	if decl.Type != nil {
+		typ = p.typeExpr(decl.Type)
+	}
+
+	var exprs []*Node
+	if decl.Values != nil {
+		exprs = p.exprList(decl.Values)
+	}
+
+	return constiter(names, typ, exprs)
+}
+
+func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
+	name := typedcl0(p.name(decl.Name))
+	name.Name.Param.Pragma = Pragma(decl.Pragma)
+
+	var typ *Node
+	if decl.Type != nil {
+		typ = p.typeExpr(decl.Type)
+	}
+
+	return typedcl1(name, typ, true)
+}
+
+func (p *noder) declNames(names []*syntax.Name) []*Node {
+	var nodes []*Node
+	for _, name := range names {
+		nodes = append(nodes, p.declName(name))
+	}
+	return nodes
+}
+
+func (p *noder) declName(name *syntax.Name) *Node {
+	// TODO(mdempsky): Set lineno?
+	return dclname(p.name(name))
+}
+
+func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
+	f := p.funcHeader(fun)
+	if f == nil {
+		return nil
+	}
+
+	var body []*Node
+	if fun.Body != nil {
+		body = p.stmts(fun.Body)
+		if body == nil {
+			body = []*Node{p.nod(fun, OEMPTY, nil, nil)}
+		}
+	}
+
+	pragma := Pragma(fun.Pragma)
+
+	f.Nbody.Set(body)
+	f.Noescape = pragma&Noescape != 0
+	if f.Noescape && len(body) != 0 {
+		yyerror("can only use //go:noescape with external func implementations")
+	}
+	f.Func.Pragma = pragma
+	lineno = p.baseline + int32(fun.EndLine) - 1
+	f.Func.Endlineno = lineno
+
+	funcbody(f)
+
+	return f
+}
+
+func (p *noder) funcHeader(fun *syntax.FuncDecl) *Node {
+	name := p.name(fun.Name)
+	t := p.signature(fun.Recv, fun.Type)
+	f := p.nod(fun, ODCLFUNC, nil, nil)
+
+	if fun.Recv == nil {
+		// FunctionName Signature
+		if name.Name == "init" {
+			name = renameinit()
+			if t.List.Len() > 0 || t.Rlist.Len() > 0 {
+				yyerror("func init must have no arguments and no return values")
+			}
+		}
+
+		if localpkg.Name == "main" && name.Name == "main" {
+			if t.List.Len() > 0 || t.Rlist.Len() > 0 {
+				yyerror("func main must have no arguments and no return values")
+			}
+		}
+
+		f.Func.Nname = newfuncname(name)
+	} else {
+		// Receiver MethodName Signature
+
+		f.Func.Shortname = newfuncname(name)
+		f.Func.Nname = methodname(f.Func.Shortname, t.Left.Right)
+	}
+
+	f.Func.Nname.Name.Defn = f
+	f.Func.Nname.Name.Param.Ntype = t // TODO: check if nname already has an ntype
+
+	declare(f.Func.Nname, PFUNC)
+	funchdr(f)
+	return f
+}
+
+func (p *noder) signature(recv *syntax.Field, typ *syntax.FuncType) *Node {
+	n := p.nod(typ, OTFUNC, nil, nil)
+	if recv != nil {
+		n.Left = p.param(recv, false, false)
+	}
+	n.List.Set(p.params(typ.ParamList, true))
+	n.Rlist.Set(p.params(typ.ResultList, false))
+	return n
+}
+
+func (p *noder) params(params []*syntax.Field, dddOk bool) []*Node {
+	var nodes []*Node
+	for i, param := range params {
+		p.lineno(param)
+		nodes = append(nodes, p.param(param, dddOk, i+1 == len(params)))
+	}
+	return nodes
+}
+
+func (p *noder) param(param *syntax.Field, dddOk, final bool) *Node {
+	var name *Node
+	if param.Name != nil {
+		name = p.newname(param.Name)
+	}
+
+	typ := p.typeExpr(param.Type)
+	n := p.nod(param, ODCLFIELD, name, typ)
+
+	// rewrite ...T parameter
+	if typ.Op == ODDD {
+		if !dddOk {
+			yyerror("cannot use ... in receiver or result parameter list")
+		} else if !final {
+			yyerror("can only use ... with final parameter in list")
+		}
+		typ.Op = OTARRAY
+		typ.Right = typ.Left
+		typ.Left = nil
+		n.Isddd = true
+		if n.Left != nil {
+			n.Left.Isddd = true
+		}
+	}
+
+	return n
+}
+
+func (p *noder) exprList(expr syntax.Expr) []*Node {
+	if list, ok := expr.(*syntax.ListExpr); ok {
+		return p.exprs(list.ElemList)
+	}
+	return []*Node{p.expr(expr)}
+}
+
+func (p *noder) exprs(exprs []syntax.Expr) []*Node {
+	var nodes []*Node
+	for _, expr := range exprs {
+		nodes = append(nodes, p.expr(expr))
+	}
+	return nodes
+}
+
+func (p *noder) expr(expr syntax.Expr) *Node {
+	p.lineno(expr)
+	switch expr := expr.(type) {
+	case nil:
+		return nil
+	case *syntax.Name:
+		return p.mkname(expr)
+	case *syntax.BasicLit:
+		return p.setlineno(expr, nodlit(p.basicLit(expr)))
+
+	case *syntax.CompositeLit:
+		n := p.nod(expr, OCOMPLIT, nil, nil)
+		if expr.Type != nil {
+			n.Right = p.expr(expr.Type)
+		}
+		l := p.exprs(expr.ElemList)
+		for i, e := range l {
+			l[i] = p.wrapname(expr.ElemList[i], e)
+		}
+		n.List.Set(l)
+		lineno = p.baseline + int32(expr.EndLine) - 1
+		return n
+	case *syntax.KeyValueExpr:
+		return p.nod(expr, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
+	case *syntax.FuncLit:
+		closurehdr(p.typeExpr(expr.Type))
+		body := p.stmts(expr.Body)
+		lineno = p.baseline + int32(expr.EndLine) - 1
+		return p.setlineno(expr, closurebody(body))
+	case *syntax.ParenExpr:
+		return p.nod(expr, OPAREN, p.expr(expr.X), nil)
+	case *syntax.SelectorExpr:
+		// parser.new_dotname
+		obj := p.expr(expr.X)
+		if obj.Op == OPACK {
+			obj.Used = true
+			return oldname(restrictlookup(expr.Sel.Value, obj.Name.Pkg))
+		}
+		return p.setlineno(expr, nodSym(OXDOT, obj, p.name(expr.Sel)))
+	case *syntax.IndexExpr:
+		return p.nod(expr, OINDEX, p.expr(expr.X), p.expr(expr.Index))
+	case *syntax.SliceExpr:
+		op := OSLICE
+		if expr.Full {
+			op = OSLICE3
+		}
+		n := p.nod(expr, op, p.expr(expr.X), nil)
+		var index [3]*Node
+		for i, x := range expr.Index {
+			if x != nil {
+				index[i] = p.expr(x)
+			}
+		}
+		n.SetSliceBounds(index[0], index[1], index[2])
+		return n
+	case *syntax.AssertExpr:
+		if expr.Type == nil {
+			panic("unexpected AssertExpr")
+		}
+		// TODO(mdempsky): parser.pexpr uses p.expr(), but
+		// seems like the type field should be parsed with
+		// ntype? Shrug, doesn't matter here.
+		return p.nod(expr, ODOTTYPE, p.expr(expr.X), p.expr(expr.Type))
+	case *syntax.Operation:
+		x := p.expr(expr.X)
+		if expr.Y == nil {
+			if expr.Op == syntax.And {
+				x = unparen(x) // TODO(mdempsky): Needed?
+				if x.Op == OCOMPLIT {
+					// Special case for &T{...}: turn into (*T){...}.
+					// TODO(mdempsky): Switch back to p.nod after we
+					// get rid of gcCompat.
+					x.Right = nod(OIND, x.Right, nil)
+					x.Right.Implicit = true
+					return x
+				}
+			}
+			return p.nod(expr, p.unOp(expr.Op), x, nil)
+		}
+		return p.nod(expr, p.binOp(expr.Op), x, p.expr(expr.Y))
+	case *syntax.CallExpr:
+		n := p.nod(expr, OCALL, p.expr(expr.Fun), nil)
+		n.List.Set(p.exprs(expr.ArgList))
+		n.Isddd = expr.HasDots
+		return n
+
+	case *syntax.ArrayType:
+		var len *Node
+		if expr.Len != nil {
+			len = p.expr(expr.Len)
+		} else {
+			len = p.nod(expr, ODDD, nil, nil)
+		}
+		return p.nod(expr, OTARRAY, len, p.typeExpr(expr.Elem))
+	case *syntax.SliceType:
+		return p.nod(expr, OTARRAY, nil, p.typeExpr(expr.Elem))
+	case *syntax.DotsType:
+		return p.nod(expr, ODDD, p.typeExpr(expr.Elem), nil)
+	case *syntax.StructType:
+		return p.structType(expr)
+	case *syntax.InterfaceType:
+		return p.interfaceType(expr)
+	case *syntax.FuncType:
+		return p.signature(nil, expr)
+	case *syntax.MapType:
+		return p.nod(expr, OTMAP, p.typeExpr(expr.Key), p.typeExpr(expr.Value))
+	case *syntax.ChanType:
+		n := p.nod(expr, OTCHAN, p.typeExpr(expr.Elem), nil)
+		n.Etype = EType(p.chanDir(expr.Dir))
+		return n
+
+	case *syntax.TypeSwitchGuard:
+		n := p.nod(expr, OTYPESW, nil, p.expr(expr.X))
+		if expr.Lhs != nil {
+			n.Left = p.declName(expr.Lhs)
+			if isblank(n.Left) {
+				yyerror("invalid variable name %v in type switch", n.Left)
+			}
+		}
+		return n
+	}
+	panic("unhandled Expr")
+}
+
+func (p *noder) typeExpr(typ syntax.Expr) *Node {
+	// TODO(mdempsky): Be stricter? typecheck should handle errors anyway.
+	return p.expr(typ)
+}
+
+func (p *noder) chanDir(dir syntax.ChanDir) ChanDir {
+	switch dir {
+	case 0:
+		return Cboth
+	case syntax.SendOnly:
+		return Csend
+	case syntax.RecvOnly:
+		return Crecv
+	}
+	panic("unhandled ChanDir")
+}
+
+func (p *noder) structType(expr *syntax.StructType) *Node {
+	var l []*Node
+	for i, field := range expr.FieldList {
+		p.lineno(field)
+		var n *Node
+		if field.Name == nil {
+			n = p.embedded(field.Type)
+		} else {
+			n = p.nod(field, ODCLFIELD, p.newname(field.Name), p.typeExpr(field.Type))
+		}
+		if i < len(expr.TagList) && expr.TagList[i] != nil {
+			n.SetVal(p.basicLit(expr.TagList[i]))
+		}
+		l = append(l, n)
+	}
+
+	p.lineno(expr)
+	n := p.nod(expr, OTSTRUCT, nil, nil)
+	n.List.Set(l)
+	return n
+}
+
+func (p *noder) interfaceType(expr *syntax.InterfaceType) *Node {
+	var l []*Node
+	for _, method := range expr.MethodList {
+		p.lineno(method)
+		var n *Node
+		if method.Name == nil {
+			n = p.nod(method, ODCLFIELD, nil, oldname(p.packname(method.Type)))
+		} else {
+			mname := p.newname(method.Name)
+			sig := p.typeExpr(method.Type)
+			sig.Left = fakethis()
+			n = p.nod(method, ODCLFIELD, mname, sig)
+			ifacedcl(n)
+		}
+		l = append(l, n)
+	}
+
+	n := p.nod(expr, OTINTER, nil, nil)
+	n.List.Set(l)
+	return n
+}
+
+func (p *noder) packname(expr syntax.Expr) *Sym {
+	switch expr := expr.(type) {
+	case *syntax.Name:
+		name := p.name(expr)
+		if n := oldname(name); n.Name != nil && n.Name.Pack != nil {
+			n.Name.Pack.Used = true
+		}
+		return name
+	case *syntax.SelectorExpr:
+		name := p.name(expr.X.(*syntax.Name))
+		var pkg *Pkg
+		if name.Def == nil || name.Def.Op != OPACK {
+			yyerror("%v is not a package", name)
+			pkg = localpkg
+		} else {
+			name.Def.Used = true
+			pkg = name.Def.Name.Pkg
+		}
+		return restrictlookup(expr.Sel.Value, pkg)
+	}
+	panic(fmt.Sprintf("unexpected packname: %#v", expr))
+}
+
+func (p *noder) embedded(typ syntax.Expr) *Node {
+	op, isStar := typ.(*syntax.Operation)
+	if isStar {
+		if op.Op != syntax.Mul || op.Y != nil {
+			panic("unexpected Operation")
+		}
+		typ = op.X
+	}
+	n := embedded(p.packname(typ), localpkg)
+	if isStar {
+		n.Right = p.nod(op, OIND, n.Right, nil)
+	}
+	return n
+}
+
+func (p *noder) stmts(stmts []syntax.Stmt) []*Node {
+	var nodes []*Node
+	for _, stmt := range stmts {
+		s := p.stmt(stmt)
+		if s == nil {
+		} else if s.Op == OBLOCK && s.Ninit.Len() == 0 {
+			nodes = append(nodes, s.List.Slice()...)
+		} else {
+			nodes = append(nodes, s)
+		}
+	}
+	return nodes
+}
+
+func (p *noder) stmt(stmt syntax.Stmt) *Node {
+	p.lineno(stmt)
+	switch stmt := stmt.(type) {
+	case *syntax.EmptyStmt:
+		return nil
+	case *syntax.LabeledStmt:
+		return p.labeledStmt(stmt)
+	case *syntax.BlockStmt:
+		return p.body(stmt.Body)
+	case *syntax.ExprStmt:
+		return p.wrapname(stmt, p.expr(stmt.X))
+	case *syntax.SendStmt:
+		return p.nod(stmt, OSEND, p.expr(stmt.Chan), p.expr(stmt.Value))
+	case *syntax.DeclStmt:
+		return liststmt(p.decls(stmt.DeclList))
+	case *syntax.AssignStmt:
+		if stmt.Op != 0 && stmt.Op != syntax.Def {
+			n := p.nod(stmt, OASOP, p.expr(stmt.Lhs), p.expr(stmt.Rhs))
+			n.Implicit = stmt.Rhs == syntax.ImplicitOne
+			n.Etype = EType(p.binOp(stmt.Op))
+			return n
+		}
+
+		lhs := p.exprList(stmt.Lhs)
+		rhs := p.exprList(stmt.Rhs)
+
+		n := p.nod(stmt, OAS, nil, nil) // assume common case
+
+		if stmt.Op == syntax.Def {
+			n.Colas = true
+			colasdefn(lhs, n) // modifies lhs, call before using lhs[0] in common case
+		}
+
+		if len(lhs) == 1 && len(rhs) == 1 {
+			// common case
+			n.Left = lhs[0]
+			n.Right = rhs[0]
+		} else {
+			n.Op = OAS2
+			n.List.Set(lhs)
+			n.Rlist.Set(rhs)
+		}
+		return n
+
+	case *syntax.BranchStmt:
+		var op Op
+		switch stmt.Tok {
+		case syntax.Break:
+			op = OBREAK
+		case syntax.Continue:
+			op = OCONTINUE
+		case syntax.Fallthrough:
+			op = OXFALL
+		case syntax.Goto:
+			op = OGOTO
+		default:
+			panic("unhandled BranchStmt")
+		}
+		n := p.nod(stmt, op, nil, nil)
+		if stmt.Label != nil {
+			n.Left = p.newname(stmt.Label)
+		}
+		if op == OGOTO {
+			n.Sym = dclstack // context, for goto restriction
+		}
+		if op == OXFALL {
+			n.Xoffset = int64(block)
+		}
+		return n
+	case *syntax.CallStmt:
+		var op Op
+		switch stmt.Tok {
+		case syntax.Defer:
+			op = ODEFER
+		case syntax.Go:
+			op = OPROC
+		default:
+			panic("unhandled CallStmt")
+		}
+		return p.nod(stmt, op, p.expr(stmt.Call), nil)
+	case *syntax.ReturnStmt:
+		var results []*Node
+		if stmt.Results != nil {
+			results = p.exprList(stmt.Results)
+		}
+		n := p.nod(stmt, ORETURN, nil, nil)
+		n.List.Set(results)
+		if n.List.Len() == 0 && Curfn != nil {
+			for _, ln := range Curfn.Func.Dcl {
+				if ln.Class == PPARAM {
+					continue
+				}
+				if ln.Class != PPARAMOUT {
+					break
+				}
+				if ln.Sym.Def != ln {
+					yyerror("%s is shadowed during return", ln.Sym.Name)
+				}
+			}
+		}
+		return n
+	case *syntax.IfStmt:
+		return p.ifStmt(stmt)
+	case *syntax.ForStmt:
+		return p.forStmt(stmt)
+	case *syntax.SwitchStmt:
+		return p.switchStmt(stmt)
+	case *syntax.SelectStmt:
+		return p.selectStmt(stmt)
+	}
+	panic("unhandled Stmt")
+}
+
+func (p *noder) body(body []syntax.Stmt) *Node {
+	l := p.bodyList(body)
+	if len(l) == 0 {
+		// TODO(mdempsky): Line number?
+		return nod(OEMPTY, nil, nil)
+	}
+	return liststmt(l)
+}
+
+func (p *noder) bodyList(body []syntax.Stmt) []*Node {
+	markdcl()
+	nodes := p.stmts(body)
+	popdcl()
+	return nodes
+}
+
+func (p *noder) ifStmt(stmt *syntax.IfStmt) *Node {
+	markdcl()
+	n := p.nod(stmt, OIF, nil, nil)
+	if stmt.Init != nil {
+		n.Ninit.Set1(p.stmt(stmt.Init))
+	}
+	if stmt.Cond != nil {
+		n.Left = p.expr(stmt.Cond)
+	}
+	n.Nbody.Set(p.bodyList(stmt.Then))
+	if stmt.Else != nil {
+		e := p.stmt(stmt.Else)
+		if e.Op == OBLOCK && e.Ninit.Len() == 0 {
+			n.Rlist.Set(e.List.Slice())
+		} else {
+			n.Rlist.Set1(e)
+		}
+	}
+	popdcl()
+	return n
+}
+
+func (p *noder) forStmt(stmt *syntax.ForStmt) *Node {
+	markdcl()
+	var n *Node
+	if r, ok := stmt.Init.(*syntax.RangeClause); ok {
+		if stmt.Cond != nil || stmt.Post != nil {
+			panic("unexpected RangeClause")
+		}
+
+		n = p.nod(r, ORANGE, nil, p.expr(r.X))
+		if r.Lhs != nil {
+			lhs := p.exprList(r.Lhs)
+			n.List.Set(lhs)
+			if r.Def {
+				n.Colas = true
+				colasdefn(lhs, n)
+			}
+		}
+	} else {
+		n = p.nod(stmt, OFOR, nil, nil)
+		if stmt.Init != nil {
+			n.Ninit.Set1(p.stmt(stmt.Init))
+		}
+		if stmt.Cond != nil {
+			n.Left = p.expr(stmt.Cond)
+		}
+		if stmt.Post != nil {
+			n.Right = p.stmt(stmt.Post)
+		}
+	}
+	n.Nbody.Set(p.bodyList(stmt.Body))
+	popdcl()
+	return n
+}
+
+func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node {
+	markdcl()
+	n := p.nod(stmt, OSWITCH, nil, nil)
+	if stmt.Init != nil {
+		n.Ninit.Set1(p.stmt(stmt.Init))
+	}
+	if stmt.Tag != nil {
+		n.Left = p.expr(stmt.Tag)
+	}
+
+	tswitch := n.Left
+	if tswitch != nil && (tswitch.Op != OTYPESW || tswitch.Left == nil) {
+		tswitch = nil
+	}
+
+	n.List.Set(p.caseClauses(stmt.Body, tswitch))
+
+	popdcl()
+	return n
+}
+
+func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node) []*Node {
+	var nodes []*Node
+	for _, clause := range clauses {
+		p.lineno(clause)
+		markdcl()
+		n := p.nod(clause, OXCASE, nil, nil)
+		if clause.Cases != nil {
+			n.List.Set(p.exprList(clause.Cases))
+		}
+		if tswitch != nil {
+			nn := newname(tswitch.Left.Sym)
+			declare(nn, dclcontext)
+			n.Rlist.Set1(nn)
+			// keep track of the instances for reporting unused
+			nn.Name.Defn = tswitch
+		}
+		n.Xoffset = int64(block)
+		n.Nbody.Set(p.stmts(clause.Body))
+		popdcl()
+		nodes = append(nodes, n)
+	}
+	return nodes
+}
+
+func (p *noder) selectStmt(stmt *syntax.SelectStmt) *Node {
+	n := p.nod(stmt, OSELECT, nil, nil)
+	n.List.Set(p.commClauses(stmt.Body))
+	return n
+}
+
+func (p *noder) commClauses(clauses []*syntax.CommClause) []*Node {
+	var nodes []*Node
+	for _, clause := range clauses {
+		p.lineno(clause)
+		markdcl()
+		n := p.nod(clause, OXCASE, nil, nil)
+		if clause.Comm != nil {
+			n.List.Set1(p.stmt(clause.Comm))
+		}
+		n.Xoffset = int64(block)
+		n.Nbody.Set(p.stmts(clause.Body))
+		popdcl()
+		nodes = append(nodes, n)
+	}
+	return nodes
+}
+
+func (p *noder) labeledStmt(label *syntax.LabeledStmt) *Node {
+	lhs := p.nod(label, OLABEL, p.newname(label.Label), nil)
+	lhs.Sym = dclstack
+
+	var ls *Node
+	if label.Stmt != nil { // TODO(mdempsky): Should always be present.
+		ls = p.stmt(label.Stmt)
+	}
+
+	lhs.Name.Defn = ls
+	l := []*Node{lhs}
+	if ls != nil {
+		if ls.Op == OBLOCK && ls.Ninit.Len() == 0 {
+			l = append(l, ls.List.Slice()...)
+		} else {
+			l = append(l, ls)
+		}
+	}
+	return liststmt(l)
+}
+
+var unOps = [...]Op{
+	syntax.Recv: ORECV,
+	syntax.Mul:  OIND,
+	syntax.And:  OADDR,
+
+	syntax.Not: ONOT,
+	syntax.Xor: OCOM,
+	syntax.Add: OPLUS,
+	syntax.Sub: OMINUS,
+}
+
+func (p *noder) unOp(op syntax.Operator) Op {
+	if uint64(op) >= uint64(len(unOps)) || unOps[op] == 0 {
+		panic("invalid Operator")
+	}
+	return unOps[op]
+}
+
+var binOps = [...]Op{
+	syntax.OrOr:   OOROR,
+	syntax.AndAnd: OANDAND,
+
+	syntax.Eql: OEQ,
+	syntax.Neq: ONE,
+	syntax.Lss: OLT,
+	syntax.Leq: OLE,
+	syntax.Gtr: OGT,
+	syntax.Geq: OGE,
+
+	syntax.Add: OADD,
+	syntax.Sub: OSUB,
+	syntax.Or:  OOR,
+	syntax.Xor: OXOR,
+
+	syntax.Mul:    OMUL,
+	syntax.Div:    ODIV,
+	syntax.Rem:    OMOD,
+	syntax.And:    OAND,
+	syntax.AndNot: OANDNOT,
+	syntax.Shl:    OLSH,
+	syntax.Shr:    ORSH,
+}
+
+func (p *noder) binOp(op syntax.Operator) Op {
+	if uint64(op) >= uint64(len(binOps)) || binOps[op] == 0 {
+		panic("invalid Operator")
+	}
+	return binOps[op]
+}
+
+func (p *noder) basicLit(lit *syntax.BasicLit) Val {
+	// TODO: Don't try to convert if we had syntax errors (conversions may fail).
+	//       Use dummy values so we can continue to compile. Eventually, use a
+	//       form of "unknown" literals that are ignored during type-checking so
+	//       we can continue type-checking w/o spurious follow-up errors.
+	switch s := lit.Value; lit.Kind {
+	case syntax.IntLit:
+		x := new(Mpint)
+		x.SetString(s)
+		return Val{U: x}
+
+	case syntax.FloatLit:
+		x := newMpflt()
+		x.SetString(s)
+		return Val{U: x}
+
+	case syntax.ImagLit:
+		x := new(Mpcplx)
+		x.Imag.SetString(strings.TrimSuffix(s, "i"))
+		return Val{U: x}
+
+	case syntax.RuneLit:
+		var r rune
+		if u, err := strconv.Unquote(s); err == nil && len(u) > 0 {
+			// Package syntax already reported any errors.
+			// Check for them again though because 0 is a
+			// better fallback value for invalid rune
+			// literals than 0xFFFD.
+			if len(u) == 1 {
+				r = rune(u[0])
+			} else {
+				r, _ = utf8.DecodeRuneInString(u)
+			}
+		}
+		x := new(Mpint)
+		x.SetInt64(int64(r))
+		x.Rune = true
+		return Val{U: x}
+
+	case syntax.StringLit:
+		if len(s) > 0 && s[0] == '`' {
+			// strip carriage returns from raw string
+			s = strings.Replace(s, "\r", "", -1)
+		}
+		// Ignore errors because package syntax already reported them.
+		u, _ := strconv.Unquote(s)
+		return Val{U: u}
+
+	default:
+		panic("unhandled BasicLit kind")
+	}
+}
+
+func (p *noder) name(name *syntax.Name) *Sym {
+	return lookup(name.Value)
+}
+
+func (p *noder) mkname(name *syntax.Name) *Node {
+	// TODO(mdempsky): Set line number?
+	return mkname(p.name(name))
+}
+
+func (p *noder) newname(name *syntax.Name) *Node {
+	// TODO(mdempsky): Set line number?
+	return newname(p.name(name))
+}
+
+func (p *noder) wrapname(n syntax.Node, x *Node) *Node {
+	// These nodes do not carry line numbers.
+	// Introduce a wrapper node to give them the correct line.
+	switch x.Op {
+	case OTYPE, OLITERAL:
+		if x.Sym == nil {
+			break
+		}
+		fallthrough
+	case ONAME, ONONAME, OPACK:
+		x = p.nod(n, OPAREN, x, nil)
+		x.Implicit = true
+	}
+	return x
+}
+
+func (p *noder) nod(orig syntax.Node, op Op, left, right *Node) *Node {
+	return p.setlineno(orig, nod(op, left, right))
+}
+
+func (p *noder) setlineno(src syntax.Node, dst *Node) *Node {
+	l := int32(src.Line())
+	if l == 0 {
+		// TODO(mdempsky): Shouldn't happen. Fix package syntax.
+		return dst
+	}
+	dst.Lineno = p.baseline + l - 1
+	return dst
+}
+
+func (p *noder) lineno(n syntax.Node) {
+	if n == nil {
+		return
+	}
+	l := int32(n.Line())
+	if l == 0 {
+		// TODO(mdempsky): Shouldn't happen. Fix package syntax.
+		return
+	}
+	lineno = p.baseline + l - 1
+}
+
+func (p *noder) error(err error) {
+	line := p.baseline
+	var msg string
+	if err, ok := err.(syntax.Error); ok {
+		line += int32(err.Line) - 1
+		msg = err.Msg
+	} else {
+		msg = err.Error()
+	}
+	yyerrorl(line, "%s", msg)
+}
+
+func (p *noder) pragma(pos, line int, text string) syntax.Pragma {
+	switch {
+	case strings.HasPrefix(text, "line "):
+		i := strings.IndexByte(text, ':')
+		if i < 0 {
+			break
+		}
+		n, err := strconv.Atoi(text[i+1:])
+		if err != nil {
+			// todo: make this an error instead? it is almost certainly a bug.
+			break
+		}
+		if n > 1e8 {
+			p.error(syntax.Error{Pos: pos, Line: line, Msg: "line number out of range"})
+			errorexit()
+		}
+		if n <= 0 {
+			break
+		}
+		lexlineno = p.baseline + int32(line)
+		linehistupdate(text[5:i], n)
+
+	case strings.HasPrefix(text, "go:linkname "):
+		// Record line number so we can emit an error later if
+		// the file doesn't import package unsafe.
+		p.linknames = append(p.linknames, line)
+
+		f := strings.Fields(text)
+		if len(f) != 3 {
+			p.error(syntax.Error{Pos: pos, Line: line, Msg: "usage: //go:linkname localname linkname"})
+			break
+		}
+		lookup(f[1]).Linkname = f[2]
+
+	case strings.HasPrefix(text, "go:cgo_"):
+		pragcgobuf += pragcgo(text)
+		fallthrough // because of //go:cgo_unsafe_args
+	default:
+		verb := text
+		if i := strings.Index(text, " "); i >= 0 {
+			verb = verb[:i]
+		}
+		return syntax.Pragma(pragmaValue(verb))
+	}
+
+	return 0
+}
+
+func mkname(sym *Sym) *Node {
+	n := oldname(sym)
+	if n.Name != nil && n.Name.Pack != nil {
+		n.Name.Pack.Used = true
+	}
+	return n
+}
+
+func unparen(x *Node) *Node {
+	for x.Op == OPAREN {
+		x = x.Left
+	}
+	return x
+}
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index b5c06d1..08ed560 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -53,7 +53,7 @@ func dumpobj1(outfile string, mode int) {
 	var err error
 	bout, err = bio.Create(outfile)
 	if err != nil {
-		Flusherrors()
+		flusherrors()
 		fmt.Printf("can't create %s: %v\n", outfile, err)
 		errorexit()
 	}
@@ -68,7 +68,7 @@ func dumpobj1(outfile string, mode int) {
 	}
 
 	printheader := func() {
-		fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring())
+		fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.GOOS, obj.GOARCH, obj.Version, obj.Expstring())
 		if buildid != "" {
 			fmt.Fprintf(bout, "build id %q\n", buildid)
 		}
@@ -130,6 +130,7 @@ func dumpobj1(outfile string, mode int) {
 	externs := len(externdcl)
 
 	dumpglobls()
+	dumpptabs()
 	dumptypestructs()
 
 	// Dump extra globals.
@@ -146,7 +147,6 @@ func dumpobj1(outfile string, mode int) {
 		ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA)
 	}
 
-	dumpdata()
 	obj.Writeobjdirect(Ctxt, bout.Writer)
 
 	if writearchive {
@@ -163,6 +163,35 @@ func dumpobj1(outfile string, mode int) {
 	bout.Close()
 }
 
+func dumpptabs() {
+	if !Ctxt.Flag_dynlink || localpkg.Name != "main" {
+		return
+	}
+	for _, exportn := range exportlist {
+		s := exportn.Sym
+		n := s.Def
+		if n == nil {
+			continue
+		}
+		if n.Op != ONAME {
+			continue
+		}
+		if !exportname(s.Name) {
+			continue
+		}
+		if s.Pkg.Name != "main" {
+			continue
+		}
+		if n.Type.Etype == TFUNC && n.Class == PFUNC {
+			// function
+			ptabs = append(ptabs, ptabEntry{s: s, t: s.Def.Type})
+		} else {
+			// variable
+			ptabs = append(ptabs, ptabEntry{s: s, t: typPtr(s.Def.Type)})
+		}
+	}
+}
+
 func dumpglobls() {
 	// add globals
 	for _, n := range externdcl {
@@ -242,18 +271,16 @@ func duintptr(s *Sym, off int, v uint64) int {
 	return duintxx(s, off, v, Widthptr)
 }
 
-// stringConstantSyms holds the pair of symbols we create for a
-// constant string.
-type stringConstantSyms struct {
-	hdr  *obj.LSym // string header
-	data *obj.LSym // actual string data
+func dbvec(s *Sym, off int, bv bvec) int {
+	// Runtime reads the bitmaps as byte arrays. Oblige.
+	for j := 0; int32(j) < bv.n; j += 8 {
+		word := bv.b[j/32]
+		off = duint8(s, off, uint8(word>>(uint(j)%32)))
+	}
+	return off
 }
 
-// stringConstants maps from the symbol name we use for the string
-// contents to the pair of linker symbols for that string.
-var stringConstants = make(map[string]stringConstantSyms, 100)
-
-func stringsym(s string) (hdr, data *obj.LSym) {
+func stringsym(s string) (data *obj.LSym) {
 	var symname string
 	if len(s) > 100 {
 		// Huge strings are hashed to avoid long names in object files.
@@ -270,33 +297,15 @@ func stringsym(s string) (hdr, data *obj.LSym) {
 	const prefix = "go.string."
 	symdataname := prefix + symname
 
-	// All the strings have the same prefix, so ignore it for map
-	// purposes, but use a slice of the symbol name string to
-	// reduce long-term memory overhead.
-	key := symdataname[len(prefix):]
-
-	if syms, ok := stringConstants[key]; ok {
-		return syms.hdr, syms.data
-	}
-
-	symhdrname := "go.string.hdr." + symname
-
-	symhdr := obj.Linklookup(Ctxt, symhdrname, 0)
 	symdata := obj.Linklookup(Ctxt, symdataname, 0)
 
-	stringConstants[key] = stringConstantSyms{symhdr, symdata}
-
-	// string header
-	off := 0
-	off = dsymptrLSym(symhdr, off, symdata, 0)
-	off = duintxxLSym(symhdr, off, uint64(len(s)), Widthint)
-	ggloblLSym(symhdr, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
-
-	// string data
-	off = dsnameLSym(symdata, 0, s)
-	ggloblLSym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
+	if !symdata.SeenGlobl() {
+		// string data
+		off := dsnameLSym(symdata, 0, s)
+		ggloblLSym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
+	}
 
-	return symhdr, symdata
+	return symdata
 }
 
 var slicebytes_gen int
@@ -319,42 +328,6 @@ func slicebytes(nam *Node, s string, len int) {
 	duintxx(nam.Sym, off, uint64(len), Widthint)
 }
 
-func Datastring(s string, a *obj.Addr) {
-	_, symdata := stringsym(s)
-	a.Type = obj.TYPE_MEM
-	a.Name = obj.NAME_EXTERN
-	a.Sym = symdata
-	a.Offset = 0
-	a.Etype = uint8(Simtype[TINT])
-}
-
-func datagostring(sval string, a *obj.Addr) {
-	symhdr, _ := stringsym(sval)
-	a.Type = obj.TYPE_MEM
-	a.Name = obj.NAME_EXTERN
-	a.Sym = symhdr
-	a.Offset = 0
-	a.Etype = uint8(TSTRING)
-}
-
-func dgostringptr(s *Sym, off int, str string) int {
-	if str == "" {
-		return duintptr(s, off, 0)
-	}
-	return dgostrlitptr(s, off, &str)
-}
-
-func dgostrlitptr(s *Sym, off int, lit *string) int {
-	if lit == nil {
-		return duintptr(s, off, 0)
-	}
-	off = int(Rnd(int64(off), int64(Widthptr)))
-	symhdr, _ := stringsym(*lit)
-	Linksym(s).WriteAddr(Ctxt, int64(off), Widthptr, symhdr, 0)
-	off += Widthptr
-	return off
-}
-
 func dsname(s *Sym, off int, t string) int {
 	return dsnameLSym(Linksym(s), off, t)
 }
@@ -381,6 +354,12 @@ func dsymptrOffLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
 	return off
 }
 
+func dsymptrWeakOffLSym(s *obj.LSym, off int, x *obj.LSym) int {
+	s.WriteWeakOff(Ctxt, int64(off), x, 0)
+	off += 4
+	return off
+}
+
 func gdata(nam *Node, nr *Node, wid int) {
 	if nam.Op != ONAME {
 		Fatalf("gdata nam op %v", nam.Op)
@@ -388,25 +367,19 @@ func gdata(nam *Node, nr *Node, wid int) {
 	if nam.Sym == nil {
 		Fatalf("gdata nil nam sym")
 	}
+	s := Linksym(nam.Sym)
 
 	switch nr.Op {
 	case OLITERAL:
 		switch u := nr.Val().U.(type) {
-		case *Mpcplx:
-			gdatacomplex(nam, u)
-
-		case string:
-			gdatastring(nam, u)
-
 		case bool:
 			i := int64(obj.Bool2int(u))
-			Linksym(nam.Sym).WriteInt(Ctxt, nam.Xoffset, wid, i)
+			s.WriteInt(Ctxt, nam.Xoffset, wid, i)
 
 		case *Mpint:
-			Linksym(nam.Sym).WriteInt(Ctxt, nam.Xoffset, wid, u.Int64())
+			s.WriteInt(Ctxt, nam.Xoffset, wid, u.Int64())
 
 		case *Mpflt:
-			s := Linksym(nam.Sym)
 			f := u.Float64()
 			switch nam.Type.Etype {
 			case TFLOAT32:
@@ -415,47 +388,41 @@ func gdata(nam *Node, nr *Node, wid int) {
 				s.WriteFloat64(Ctxt, nam.Xoffset, f)
 			}
 
+		case *Mpcplx:
+			r := u.Real.Float64()
+			i := u.Imag.Float64()
+			switch nam.Type.Etype {
+			case TCOMPLEX64:
+				s.WriteFloat32(Ctxt, nam.Xoffset, float32(r))
+				s.WriteFloat32(Ctxt, nam.Xoffset+4, float32(i))
+			case TCOMPLEX128:
+				s.WriteFloat64(Ctxt, nam.Xoffset, r)
+				s.WriteFloat64(Ctxt, nam.Xoffset+8, i)
+			}
+
+		case string:
+			symdata := stringsym(u)
+			s.WriteAddr(Ctxt, nam.Xoffset, Widthptr, symdata, 0)
+			s.WriteInt(Ctxt, nam.Xoffset+int64(Widthptr), Widthint, int64(len(u)))
+
 		default:
 			Fatalf("gdata unhandled OLITERAL %v", nr)
 		}
 
 	case OADDR:
 		if nr.Left.Op != ONAME {
-			Fatalf("gdata ADDR left op %s", nr.Left.Op)
+			Fatalf("gdata ADDR left op %v", nr.Left.Op)
 		}
 		to := nr.Left
-		Linksym(nam.Sym).WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(to.Sym), to.Xoffset)
+		s.WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(to.Sym), to.Xoffset)
 
 	case ONAME:
 		if nr.Class != PFUNC {
 			Fatalf("gdata NAME not PFUNC %d", nr.Class)
 		}
-		Linksym(nam.Sym).WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(funcsym(nr.Sym)), nr.Xoffset)
+		s.WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(funcsym(nr.Sym)), nr.Xoffset)
 
 	default:
 		Fatalf("gdata unhandled op %v %v\n", nr, nr.Op)
 	}
 }
-
-func gdatacomplex(nam *Node, cval *Mpcplx) {
-	t := Types[cplxsubtype(nam.Type.Etype)]
-	r := cval.Real.Float64()
-	i := cval.Imag.Float64()
-	s := Linksym(nam.Sym)
-
-	switch t.Etype {
-	case TFLOAT32:
-		s.WriteFloat32(Ctxt, nam.Xoffset, float32(r))
-		s.WriteFloat32(Ctxt, nam.Xoffset+4, float32(i))
-	case TFLOAT64:
-		s.WriteFloat64(Ctxt, nam.Xoffset, r)
-		s.WriteFloat64(Ctxt, nam.Xoffset+8, i)
-	}
-}
-
-func gdatastring(nam *Node, sval string) {
-	s := Linksym(nam.Sym)
-	_, symdata := stringsym(sval)
-	s.WriteAddr(Ctxt, nam.Xoffset, Widthptr, symdata, 0)
-	s.WriteInt(Ctxt, nam.Xoffset+int64(Widthptr), Widthint, int64(len(sval)))
-}
diff --git a/src/cmd/compile/internal/gc/opnames.go b/src/cmd/compile/internal/gc/opnames.go
index bcdae6c..bd56506 100644
--- a/src/cmd/compile/internal/gc/opnames.go
+++ b/src/cmd/compile/internal/gc/opnames.go
@@ -48,6 +48,7 @@ var opnames = []string{
 	OMAPLIT:          "MAPLIT",
 	OSTRUCTLIT:       "STRUCTLIT",
 	OARRAYLIT:        "ARRAYLIT",
+	OSLICELIT:        "SLICELIT",
 	OPTRLIT:          "PTRLIT",
 	OCONV:            "CONV",
 	OCONVIFACE:       "CONVIFACE",
@@ -76,6 +77,7 @@ var opnames = []string{
 	OINDEX:           "INDEX",
 	OINDEXMAP:        "INDEXMAP",
 	OKEY:             "KEY",
+	OSTRUCTKEY:       "STRUCTKEY",
 	OLEN:             "LEN",
 	OMAKE:            "MAKE",
 	OMAKECHAN:        "MAKECHAN",
@@ -143,14 +145,14 @@ var opnames = []string{
 	OINLCALL:         "INLCALL",
 	OEFACE:           "EFACE",
 	OITAB:            "ITAB",
+	OIDATA:           "IDATA",
 	OSPTR:            "SPTR",
 	OCLOSUREVAR:      "CLOSUREVAR",
 	OCFUNC:           "CFUNC",
 	OCHECKNIL:        "CHECKNIL",
 	OVARKILL:         "VARKILL",
 	OVARLIVE:         "VARLIVE",
-	OREGISTER:        "REGISTER",
-	OINDREG:          "INDREG",
+	OINDREGSP:        "INDREGSP",
 	OCMP:             "CMP",
 	ODEC:             "DEC",
 	OINC:             "INC",
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
index f3b1028..e3d65e5 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -6,7 +6,6 @@ package gc
 
 import (
 	"fmt"
-	"strings"
 )
 
 // Rewrite tree to use separate statements to enforce
@@ -62,7 +61,7 @@ func order(fn *Node) {
 func ordertemp(t *Type, order *Order, clear bool) *Node {
 	var_ := temp(t)
 	if clear {
-		a := Nod(OAS, var_, nil)
+		a := nod(OAS, var_, nil)
 		a = typecheck(a, Etop)
 		order.out = append(order.out, a)
 	}
@@ -85,7 +84,7 @@ func ordertemp(t *Type, order *Order, clear bool) *Node {
 // to be filled in.)
 func ordercopyexpr(n *Node, t *Type, order *Order, clear int) *Node {
 	var_ := ordertemp(t, order, clear != 0)
-	a := Nod(OAS, var_, n)
+	a := nod(OAS, var_, n)
 	a = typecheck(a, Etop)
 	order.out = append(order.out, a)
 	return var_
@@ -170,14 +169,6 @@ func ordersafeexpr(n *Node, order *Order) *Node {
 	}
 }
 
-// Istemp reports whether n is a temporary variable.
-func istemp(n *Node) bool {
-	if n.Op != ONAME {
-		return false
-	}
-	return strings.HasPrefix(n.Sym.Name, "autotmp_")
-}
-
 // Isaddrokay reports whether it is okay to pass n's address to runtime routines.
 // Taking the address of a variable makes the liveness and optimization analyses
 // lose track of where the variable's lifetime ends. To avoid hurting the analyses
@@ -185,12 +176,12 @@ func istemp(n *Node) bool {
 // because we emit explicit VARKILL instructions marking the end of those
 // temporaries' lifetimes.
 func isaddrokay(n *Node) bool {
-	return islvalue(n) && (n.Op != ONAME || n.Class == PEXTERN || istemp(n))
+	return islvalue(n) && (n.Op != ONAME || n.Class == PEXTERN || n.IsAutoTmp())
 }
 
-// Orderaddrtemp ensures that *np is okay to pass by address to runtime routines.
-// If the original argument *np is not okay, orderaddrtemp creates a tmp, emits
-// tmp = *np, and then sets *np to the tmp variable.
+// Orderaddrtemp ensures that n is okay to pass by address to runtime routines.
+// If the original argument n is not okay, orderaddrtemp creates a tmp, emits
+// tmp = n, and then returns tmp.
 func orderaddrtemp(n *Node, order *Order) *Node {
 	if isaddrokay(n) {
 		return n
@@ -222,11 +213,11 @@ func cleantempnopop(mark ordermarker, order *Order, out *[]*Node) {
 		if n.Name.Keepalive {
 			n.Name.Keepalive = false
 			n.Addrtaken = true // ensure SSA keeps the n variable
-			kill = Nod(OVARLIVE, n, nil)
+			kill = nod(OVARLIVE, n, nil)
 			kill = typecheck(kill, Etop)
 			*out = append(*out, kill)
 		}
-		kill = Nod(OVARKILL, n, nil)
+		kill = nod(OVARKILL, n, nil)
 		kill = typecheck(kill, Etop)
 		*out = append(*out, kill)
 	}
@@ -336,7 +327,7 @@ func copyret(n *Node, order *Order) []*Node {
 		l2 = append(l2, tmp)
 	}
 
-	as := Nod(OAS2, nil, nil)
+	as := nod(OAS2, nil, nil)
 	as.List.Set(l1)
 	as.Rlist.Set1(n)
 	as = typecheck(as, Etop)
@@ -363,7 +354,7 @@ func ordercall(n *Node, order *Order) {
 	ordercallargs(&n.List, order)
 
 	if n.Op == OCALLFUNC {
-		t, it := IterFields(n.Left.Type.Params())
+		t, it := iterFields(n.Left.Type.Params())
 		for i := range n.List.Slice() {
 			// Check for "unsafe-uintptr" tag provided by escape analysis.
 			// If present and the argument is really a pointer being converted
@@ -395,13 +386,8 @@ func ordercall(n *Node, order *Order) {
 }
 
 // Ordermapassign appends n to order->out, introducing temporaries
-// to make sure that all map assignments have the form m[k] = x,
-// where x is addressable.
-// (Orderexpr has already been called on n, so we know k is addressable.)
-//
-// If n is m[k] = x where x is not addressable, the rewrite is:
-//	tmp = x
-//	m[k] = tmp
+// to make sure that all map assignments have the form m[k] = x.
+// (Note: orderexpr has already been called on n, so we know k is addressable.)
 //
 // If n is the multiple assignment form ..., m[k], ... = ..., the rewrite is
 //	t1 = m
@@ -428,10 +414,10 @@ func ordermapassign(n *Node, order *Order) {
 		// We call writebarrierfat only for values > 4 pointers long. See walk.go.
 		// TODO(mdempsky): writebarrierfat doesn't exist anymore, but removing that
 		// logic causes net/http's tests to become flaky; see CL 21242.
-		if (n.Left.Op == OINDEXMAP || (needwritebarrier(n.Left, n.Right) && n.Left.Type.Width > int64(4*Widthptr))) && !isaddrokay(n.Right) {
+		if needwritebarrier(n.Left, n.Right) && n.Left.Type.Width > int64(4*Widthptr) && n.Right != nil && !isaddrokay(n.Right) {
 			m := n.Left
 			n.Left = ordertemp(m.Type, order, false)
-			a := Nod(OAS, m, n.Left)
+			a := nod(OAS, m, n.Left)
 			a = typecheck(a, Etop)
 			order.out = append(order.out, a)
 		}
@@ -443,21 +429,21 @@ func ordermapassign(n *Node, order *Order) {
 		for i1, n1 := range n.List.Slice() {
 			if n1.Op == OINDEXMAP {
 				m = n1
-				if !istemp(m.Left) {
+				if !m.Left.IsAutoTmp() {
 					m.Left = ordercopyexpr(m.Left, m.Left.Type, order, 0)
 				}
-				if !istemp(m.Right) {
+				if !m.Right.IsAutoTmp() {
 					m.Right = ordercopyexpr(m.Right, m.Right.Type, order, 0)
 				}
 				n.List.SetIndex(i1, ordertemp(m.Type, order, false))
-				a = Nod(OAS, m, n.List.Index(i1))
+				a = nod(OAS, m, n.List.Index(i1))
 				a = typecheck(a, Etop)
 				post = append(post, a)
 			} else if instrumenting && n.Op == OAS2FUNC && !isblank(n.List.Index(i1)) {
 				m = n.List.Index(i1)
 				t := ordertemp(m.Type, order, false)
 				n.List.SetIndex(i1, t)
-				a = Nod(OAS, m, t)
+				a = nod(OAS, m, t)
 				a = typecheck(a, Etop)
 				post = append(post, a)
 			}
@@ -530,7 +516,7 @@ func orderstmt(n *Node, order *Order) {
 		}
 		tmp1 = ordercopyexpr(tmp1, n.Left.Type, order, 0)
 		// TODO(marvin): Fix Node.EType type union.
-		n.Right = Nod(Op(n.Etype), tmp1, n.Right)
+		n.Right = nod(Op(n.Etype), tmp1, n.Right)
 		n.Right = typecheck(n.Right, Erv)
 		n.Right = orderexpr(n.Right, order, nil)
 		n.Etype = 0
@@ -538,7 +524,7 @@ func orderstmt(n *Node, order *Order) {
 		ordermapassign(n, order)
 		cleantemp(t, order)
 
-		// Special: make sure key is addressable,
+	// Special: make sure key is addressable,
 	// and make sure OINDEXMAP is not copied out.
 	case OAS2MAPR:
 		t := marktemp(order)
@@ -556,7 +542,7 @@ func orderstmt(n *Node, order *Order) {
 		ordermapassign(n, order)
 		cleantemp(t, order)
 
-		// Special: avoid copy of func call n->rlist->n.
+	// Special: avoid copy of func call n->rlist->n.
 	case OAS2FUNC:
 		t := marktemp(order)
 
@@ -565,7 +551,7 @@ func orderstmt(n *Node, order *Order) {
 		ordermapassign(n, order)
 		cleantemp(t, order)
 
-		// Special: use temporary variables to hold result,
+	// Special: use temporary variables to hold result,
 	// so that assertI2Tetc can take address of temporary.
 	// No temporary for blank assignment.
 	case OAS2DOTTYPE:
@@ -573,21 +559,34 @@ func orderstmt(n *Node, order *Order) {
 
 		orderexprlist(n.List, order)
 		n.Rlist.First().Left = orderexpr(n.Rlist.First().Left, order, nil) // i in i.(T)
-		if isblank(n.List.First()) {
-			order.out = append(order.out, n)
-		} else {
+
+		var tmp1, tmp2 *Node
+		if !isblank(n.List.First()) {
 			typ := n.Rlist.First().Type
-			tmp1 := ordertemp(typ, order, haspointers(typ))
-			order.out = append(order.out, n)
-			r := Nod(OAS, n.List.First(), tmp1)
+			tmp1 = ordertemp(typ, order, haspointers(typ))
+		}
+		if !isblank(n.List.Second()) && !n.List.Second().Type.IsBoolean() {
+			tmp2 = ordertemp(Types[TBOOL], order, false)
+		}
+
+		order.out = append(order.out, n)
+
+		if tmp1 != nil {
+			r := nod(OAS, n.List.First(), tmp1)
 			r = typecheck(r, Etop)
 			ordermapassign(r, order)
-			n.List.Set([]*Node{tmp1, n.List.Second()})
+			n.List.SetIndex(0, tmp1)
+		}
+		if tmp2 != nil {
+			r := okas(n.List.Second(), tmp2)
+			r = typecheck(r, Etop)
+			ordermapassign(r, order)
+			n.List.SetIndex(1, tmp2)
 		}
 
 		cleantemp(t, order)
 
-		// Special: use temporary variables to hold result,
+	// Special: use temporary variables to hold result,
 	// so that chanrecv can take address of temporary.
 	case OAS2RECV:
 		t := marktemp(order)
@@ -596,27 +595,22 @@ func orderstmt(n *Node, order *Order) {
 		n.Rlist.First().Left = orderexpr(n.Rlist.First().Left, order, nil) // arg to recv
 		ch := n.Rlist.First().Left.Type
 		tmp1 := ordertemp(ch.Elem(), order, haspointers(ch.Elem()))
-		var tmp2 *Node
-		if !isblank(n.List.Second()) {
-			tmp2 = ordertemp(n.List.Second().Type, order, false)
-		} else {
-			tmp2 = ordertemp(Types[TBOOL], order, false)
-		}
+		tmp2 := ordertemp(Types[TBOOL], order, false)
 		order.out = append(order.out, n)
-		r := Nod(OAS, n.List.First(), tmp1)
+		r := nod(OAS, n.List.First(), tmp1)
 		r = typecheck(r, Etop)
 		ordermapassign(r, order)
-		r = Nod(OAS, n.List.Second(), tmp2)
+		r = okas(n.List.Second(), tmp2)
 		r = typecheck(r, Etop)
 		ordermapassign(r, order)
 		n.List.Set([]*Node{tmp1, tmp2})
 		cleantemp(t, order)
 
-		// Special: does not save n onto out.
+	// Special: does not save n onto out.
 	case OBLOCK, OEMPTY:
 		orderstmtlist(n.List, order)
 
-		// Special: n->left is not an expression; save as is.
+	// Special: n->left is not an expression; save as is.
 	case OBREAK,
 		OCONTINUE,
 		ODCL,
@@ -629,7 +623,7 @@ func orderstmt(n *Node, order *Order) {
 		ORETJMP:
 		order.out = append(order.out, n)
 
-		// Special: handle call arguments.
+	// Special: handle call arguments.
 	case OCALLFUNC, OCALLINTER, OCALLMETH:
 		t := marktemp(order)
 
@@ -637,7 +631,7 @@ func orderstmt(n *Node, order *Order) {
 		order.out = append(order.out, n)
 		cleantemp(t, order)
 
-		// Special: order arguments to inner call but not call itself.
+	// Special: order arguments to inner call but not call itself.
 	case ODEFER, OPROC:
 		t := marktemp(order)
 
@@ -668,7 +662,7 @@ func orderstmt(n *Node, order *Order) {
 		order.out = append(order.out, n)
 		cleantemp(t, order)
 
-		// Clean temporaries from condition evaluation at
+	// Clean temporaries from condition evaluation at
 	// beginning of loop body and after for statement.
 	case OFOR:
 		t := marktemp(order)
@@ -676,13 +670,13 @@ func orderstmt(n *Node, order *Order) {
 		n.Left = orderexprinplace(n.Left, order)
 		var l []*Node
 		cleantempnopop(t, order, &l)
-		n.Nbody.Set(append(l, n.Nbody.Slice()...))
+		n.Nbody.Prepend(l...)
 		orderblockNodes(&n.Nbody)
 		n.Right = orderstmtinplace(n.Right)
 		order.out = append(order.out, n)
 		cleantemp(t, order)
 
-		// Clean temporaries from condition at
+	// Clean temporaries from condition at
 	// beginning of both branches.
 	case OIF:
 		t := marktemp(order)
@@ -690,16 +684,16 @@ func orderstmt(n *Node, order *Order) {
 		n.Left = orderexprinplace(n.Left, order)
 		var l []*Node
 		cleantempnopop(t, order, &l)
-		n.Nbody.Set(append(l, n.Nbody.Slice()...))
+		n.Nbody.Prepend(l...)
 		l = nil
 		cleantempnopop(t, order, &l)
-		n.Rlist.Set(append(l, n.Rlist.Slice()...))
+		n.Rlist.Prepend(l...)
 		poptemp(t, order)
 		orderblockNodes(&n.Nbody)
 		n.Rlist.Set(orderblock(n.Rlist))
 		order.out = append(order.out, n)
 
-		// Special: argument will be converted to interface using convT2E
+	// Special: argument will be converted to interface using convT2E
 	// so make sure it is an addressable temporary.
 	case OPANIC:
 		t := marktemp(order)
@@ -749,7 +743,7 @@ func orderstmt(n *Node, order *Order) {
 			r := n.Right
 
 			if r.Type.IsString() && r.Type != Types[TSTRING] {
-				r = Nod(OCONV, r, nil)
+				r = nod(OCONV, r, nil)
 				r.Type = Types[TSTRING]
 				r = typecheck(r, Erv)
 			}
@@ -807,7 +801,7 @@ func orderstmt(n *Node, order *Order) {
 			if r != nil {
 				switch r.Op {
 				default:
-					Yyerror("unknown op in select %v", r.Op)
+					yyerror("unknown op in select %v", r.Op)
 					Dump("select case", r)
 
 				// If this is case x := <-ch or case x, y := <-ch, the case has
@@ -829,7 +823,7 @@ func orderstmt(n *Node, order *Order) {
 					}
 
 					if r.Ninit.Len() != 0 {
-						Yyerror("ninit on select recv")
+						yyerror("ninit on select recv")
 						dumplist("ninit", r.Ninit)
 					}
 
@@ -860,13 +854,13 @@ func orderstmt(n *Node, order *Order) {
 						tmp1 = r.Left
 
 						if r.Colas {
-							tmp2 = Nod(ODCL, tmp1, nil)
+							tmp2 = nod(ODCL, tmp1, nil)
 							tmp2 = typecheck(tmp2, Etop)
 							n2.Ninit.Append(tmp2)
 						}
 
 						r.Left = ordertemp(r.Right.Left.Type.Elem(), order, haspointers(r.Right.Left.Type.Elem()))
-						tmp2 = Nod(OAS, tmp1, r.Left)
+						tmp2 = nod(OAS, tmp1, r.Left)
 						tmp2 = typecheck(tmp2, Etop)
 						n2.Ninit.Append(tmp2)
 					}
@@ -877,13 +871,13 @@ func orderstmt(n *Node, order *Order) {
 					if r.List.Len() != 0 {
 						tmp1 = r.List.First()
 						if r.Colas {
-							tmp2 = Nod(ODCL, tmp1, nil)
+							tmp2 = nod(ODCL, tmp1, nil)
 							tmp2 = typecheck(tmp2, Etop)
 							n2.Ninit.Append(tmp2)
 						}
 
-						r.List.Set1(ordertemp(tmp1.Type, order, false))
-						tmp2 = Nod(OAS, tmp1, r.List.First())
+						r.List.Set1(ordertemp(Types[TBOOL], order, false))
+						tmp2 = okas(tmp1, r.List.First())
 						tmp2 = typecheck(tmp2, Etop)
 						n2.Ninit.Append(tmp2)
 					}
@@ -891,7 +885,7 @@ func orderstmt(n *Node, order *Order) {
 
 				case OSEND:
 					if r.Ninit.Len() != 0 {
-						Yyerror("ninit on select send")
+						yyerror("ninit on select send")
 						dumplist("ninit", r.Ninit)
 					}
 
@@ -899,11 +893,11 @@ func orderstmt(n *Node, order *Order) {
 					// r->left is c, r->right is x, both are always evaluated.
 					r.Left = orderexpr(r.Left, order, nil)
 
-					if !istemp(r.Left) {
+					if !r.Left.IsAutoTmp() {
 						r.Left = ordercopyexpr(r.Left, r.Left.Type, order, 0)
 					}
 					r.Right = orderexpr(r.Right, order, nil)
-					if !istemp(r.Right) {
+					if !r.Right.IsAutoTmp() {
 						r.Right = ordercopyexpr(r.Right, r.Right.Type, order, 0)
 					}
 				}
@@ -917,14 +911,14 @@ func orderstmt(n *Node, order *Order) {
 		for _, n3 := range n.List.Slice() {
 			s := n3.Ninit.Slice()
 			cleantempnopop(t, order, &s)
-			n3.Nbody.Set(append(s, n3.Nbody.Slice()...))
+			n3.Nbody.Prepend(s...)
 			n3.Ninit.Set(nil)
 		}
 
 		order.out = append(order.out, n)
 		poptemp(t, order)
 
-		// Special: value being sent is passed as a pointer; make it addressable.
+	// Special: value being sent is passed as a pointer; make it addressable.
 	case OSEND:
 		t := marktemp(order)
 
@@ -934,7 +928,7 @@ func orderstmt(n *Node, order *Order) {
 		order.out = append(order.out, n)
 		cleantemp(t, order)
 
-		// TODO(rsc): Clean temporaries more aggressively.
+	// TODO(rsc): Clean temporaries more aggressively.
 	// Note that because walkswitch will rewrite some of the
 	// switch into a binary search, this is not as easy as it looks.
 	// (If we ran that code here we could invoke orderstmt on
@@ -1002,7 +996,7 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
 		orderexprlist(n.List, order)
 		orderexprlist(n.Rlist, order)
 
-		// Addition of strings turns into a function call.
+	// Addition of strings turns into a function call.
 	// Allocate a temporary to hold the strings.
 	// Fewer than 5 strings use direct runtime helpers.
 	case OADDSTR:
@@ -1053,8 +1047,14 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
 		// key must be addressable
 	case OINDEXMAP:
 		n.Left = orderexpr(n.Left, order, nil)
-
 		n.Right = orderexpr(n.Right, order, nil)
+		needCopy := false
+
+		if n.Etype == 0 && instrumenting {
+			// Race detector needs the copy so it can
+			// call treecopy on the result.
+			needCopy = true
+		}
 
 		// For x = m[string(k)] where k is []byte, the allocation of
 		// backing bytes for the string can be avoided by reusing
@@ -1068,16 +1068,17 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
 		// conversion (by the ordercopyexpr a few lines below).
 		if n.Etype == 0 && n.Right.Op == OARRAYBYTESTR {
 			n.Right.Op = OARRAYBYTESTRTMP
+			needCopy = true
 		}
 
+		// Map calls need to take the address of the key.
 		n.Right = orderaddrtemp(n.Right, order)
-		if n.Etype == 0 {
-			// use of value (not being assigned);
-			// make copy in temporary.
+
+		if needCopy {
 			n = ordercopyexpr(n, n.Type, order, 0)
 		}
 
-		// concrete type (not interface) argument must be addressable
+	// concrete type (not interface) argument must be addressable
 	// temporary to pass to runtime.
 	case OCONVIFACE:
 		n.Left = orderexpr(n.Left, order, nil)
@@ -1110,7 +1111,7 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
 		var s []*Node
 
 		cleantempnopop(mark, order, &s)
-		n.Right.Ninit.Set(append(s, n.Right.Ninit.Slice()...))
+		n.Right.Ninit.Prepend(s...)
 		n.Right = orderexprinplace(n.Right, order)
 
 	case OCALLFUNC,
@@ -1160,7 +1161,7 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
 			prealloc[n] = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type
 		}
 
-	case OARRAYLIT, OCALLPART:
+	case OARRAYLIT, OSLICELIT, OCALLPART:
 		n.Left = orderexpr(n.Left, order, nil)
 		n.Right = orderexpr(n.Right, order, nil)
 		orderexprlist(n.List, order)
@@ -1180,10 +1181,10 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
 
 	case ODOTTYPE, ODOTTYPE2:
 		n.Left = orderexpr(n.Left, order, nil)
-		// TODO(rsc): The Isfat is for consistency with componentgen and walkexpr.
+		// TODO(rsc): The isfat is for consistency with componentgen and walkexpr.
 		// It needs to be removed in all three places.
 		// That would allow inlining x.(struct{*int}) the same as x.(*int).
-		if !isdirectiface(n.Type) || Isfat(n.Type) || instrumenting {
+		if !isdirectiface(n.Type) || isfat(n.Type) || instrumenting {
 			n = ordercopyexpr(n, n.Type, order, 1)
 		}
 
@@ -1206,3 +1207,12 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
 	lineno = lno
 	return n
 }
+
+// okas creates and returns an assignment of val to ok,
+// including an explicit conversion if necessary.
+func okas(ok, val *Node) *Node {
+	if !isblank(ok) {
+		val = conv(val, ok.Type)
+	}
+	return nod(OAS, ok, val)
+}
diff --git a/src/cmd/compile/internal/gc/parser.go b/src/cmd/compile/internal/gc/parser.go
deleted file mode 100644
index 3897db9..0000000
--- a/src/cmd/compile/internal/gc/parser.go
+++ /dev/null
@@ -1,3353 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gc
-
-// The recursive-descent parser is built around a slighty modified grammar
-// of Go to accommodate for the constraints imposed by strict one token look-
-// ahead, and for better error handling. Subsequent checks of the constructed
-// syntax tree restrict the language accepted by the compiler to proper Go.
-//
-// Semicolons are inserted by the lexer. The parser uses one-token look-ahead
-// to handle optional commas and semicolons before a closing ) or } .
-
-import (
-	"bufio"
-	"fmt"
-	"strconv"
-	"strings"
-)
-
-const trace = false // if set, parse tracing can be enabled with -x
-
-// parse_import parses the export data of a package that is imported.
-func parse_import(bin *bufio.Reader, indent []byte) {
-	newparser(bin, indent).import_package()
-}
-
-// parse_file parses a single Go source file.
-func parse_file(bin *bufio.Reader) {
-	newparser(bin, nil).file()
-}
-
-type parser struct {
-	lexer
-	fnest  int    // function nesting level (for error handling)
-	xnest  int    // expression nesting level (for complit ambiguity resolution)
-	indent []byte // tracing support
-
-	// TODO(gri) remove this once we switch to binary export format
-	structpkg *Pkg // for verification in addmethod only
-}
-
-// newparser returns a new parser ready to parse from src.
-// indent is the initial indentation for tracing output.
-func newparser(src *bufio.Reader, indent []byte) *parser {
-	var p parser
-	p.bin = src
-	p.indent = indent
-	p.next()
-	return &p
-}
-
-func (p *parser) got(tok int32) bool {
-	if p.tok == tok {
-		p.next()
-		return true
-	}
-	return false
-}
-
-func (p *parser) want(tok int32) {
-	if !p.got(tok) {
-		p.syntax_error("expecting " + tokstring(tok))
-		p.advance()
-	}
-}
-
-// ----------------------------------------------------------------------------
-// Syntax error handling
-
-func (p *parser) syntax_error(msg string) {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("syntax_error (" + msg + ")")()
-	}
-
-	if p.tok == EOF && nerrors > 0 {
-		return // avoid meaningless follow-up errors
-	}
-
-	// add punctuation etc. as needed to msg
-	switch {
-	case msg == "":
-		// nothing to do
-	case strings.HasPrefix(msg, "in"), strings.HasPrefix(msg, "at"), strings.HasPrefix(msg, "after"):
-		msg = " " + msg
-	case strings.HasPrefix(msg, "expecting"):
-		msg = ", " + msg
-	default:
-		// plain error - we don't care about current token
-		Yyerror("syntax error: %s", msg)
-		return
-	}
-
-	// determine token string
-	var tok string
-	switch p.tok {
-	case LNAME:
-		if p.sym_ != nil && p.sym_.Name != "" {
-			tok = p.sym_.Name
-		} else {
-			tok = "name"
-		}
-	case LLITERAL:
-		if litbuf == "" {
-			litbuf = "literal " + lexbuf.String()
-		}
-		tok = litbuf
-	case LOPER:
-		tok = goopnames[p.op]
-	case LASOP:
-		tok = goopnames[p.op] + "="
-	case LINCOP:
-		tok = goopnames[p.op] + goopnames[p.op]
-	default:
-		tok = tokstring(p.tok)
-	}
-
-	Yyerror("syntax error: unexpected %s", tok+msg)
-}
-
-// Like syntax_error, but reports error at given line rather than current lexer line.
-func (p *parser) syntax_error_at(lno int32, msg string) {
-	defer func(lno int32) {
-		lineno = lno
-	}(lineno)
-	lineno = lno
-	p.syntax_error(msg)
-}
-
-// The stoplist contains keywords that start a statement.
-// They are good synchronization points in case of syntax
-// errors and (usually) shouldn't be skipped over.
-var stoplist = map[int32]bool{
-	LBREAK:    true,
-	LCONST:    true,
-	LCONTINUE: true,
-	LDEFER:    true,
-	LFALL:     true,
-	LFOR:      true,
-	LFUNC:     true,
-	LGO:       true,
-	LGOTO:     true,
-	LIF:       true,
-	LRETURN:   true,
-	LSELECT:   true,
-	LSWITCH:   true,
-	LTYPE:     true,
-	LVAR:      true,
-}
-
-// Advance consumes tokens until it finds a token of the stop- or followlist.
-// The stoplist is only considered if we are inside a function (p.fnest > 0).
-// The followlist is the list of valid tokens that can follow a production;
-// if it is empty, exactly one token is consumed to ensure progress.
-func (p *parser) advance(followlist ...int32) {
-	if len(followlist) == 0 {
-		p.next()
-		return
-	}
-	for p.tok != EOF {
-		if p.fnest > 0 && stoplist[p.tok] {
-			return
-		}
-		for _, follow := range followlist {
-			if p.tok == follow {
-				return
-			}
-		}
-		p.next()
-	}
-}
-
-func tokstring(tok int32) string {
-	switch tok {
-	case EOF:
-		return "EOF"
-	case ',':
-		return "comma"
-	case ';':
-		return "semicolon or newline"
-	}
-	if 0 <= tok && tok < 128 {
-		// get invisibles properly backslashed
-		s := strconv.QuoteRune(tok)
-		if n := len(s); n > 0 && s[0] == '\'' && s[n-1] == '\'' {
-			s = s[1 : n-1]
-		}
-		return s
-	}
-	if s := tokstrings[tok]; s != "" {
-		return s
-	}
-	// catchall
-	return fmt.Sprintf("tok-%v", tok)
-}
-
-var tokstrings = map[int32]string{
-	LNAME:    "NAME",
-	LLITERAL: "LITERAL",
-
-	LOPER:  "op",
-	LASOP:  "op=",
-	LINCOP: "opop",
-
-	LCOLAS: ":=",
-	LCOMM:  "<-",
-	LDDD:   "...",
-
-	LBREAK:     "break",
-	LCASE:      "case",
-	LCHAN:      "chan",
-	LCONST:     "const",
-	LCONTINUE:  "continue",
-	LDEFAULT:   "default",
-	LDEFER:     "defer",
-	LELSE:      "else",
-	LFALL:      "fallthrough",
-	LFOR:       "for",
-	LFUNC:      "func",
-	LGO:        "go",
-	LGOTO:      "goto",
-	LIF:        "if",
-	LIMPORT:    "import",
-	LINTERFACE: "interface",
-	LMAP:       "map",
-	LPACKAGE:   "package",
-	LRANGE:     "range",
-	LRETURN:    "return",
-	LSELECT:    "select",
-	LSTRUCT:    "struct",
-	LSWITCH:    "switch",
-	LTYPE:      "type",
-	LVAR:       "var",
-}
-
-// usage: defer p.trace(msg)()
-func (p *parser) trace(msg string) func() {
-	fmt.Printf("%5d: %s%s (\n", lineno, p.indent, msg)
-	const tab = ". "
-	p.indent = append(p.indent, tab...)
-	return func() {
-		p.indent = p.indent[:len(p.indent)-len(tab)]
-		if x := recover(); x != nil {
-			panic(x) // skip print_trace
-		}
-		fmt.Printf("%5d: %s)\n", lineno, p.indent)
-	}
-}
-
-// ----------------------------------------------------------------------------
-// Parsing package files
-//
-// Parse methods are annotated with matching Go productions as appropriate.
-// The annotations are intended as guidelines only since a single Go grammar
-// rule may be covered by multiple parse methods and vice versa.
-
-// SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
-func (p *parser) file() {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("file")()
-	}
-
-	p.package_()
-	p.want(';')
-
-	for p.tok == LIMPORT {
-		p.import_()
-		p.want(';')
-	}
-
-	xtop = append(xtop, p.xdcl_list()...)
-
-	p.want(EOF)
-}
-
-// PackageClause = "package" PackageName .
-// PackageName   = identifier .
-func (p *parser) package_() {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("package_")()
-	}
-
-	if !p.got(LPACKAGE) {
-		p.syntax_error("package statement must be first")
-		errorexit()
-	}
-	mkpackage(p.sym().Name)
-}
-
-// ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
-func (p *parser) import_() {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("import_")()
-	}
-
-	p.want(LIMPORT)
-	if p.got('(') {
-		for p.tok != EOF && p.tok != ')' {
-			p.importdcl()
-			if !p.osemi(')') {
-				break
-			}
-		}
-		p.want(')')
-	} else {
-		p.importdcl()
-	}
-}
-
-// ImportSpec = [ "." | PackageName ] ImportPath .
-// ImportPath = string_lit .
-func (p *parser) importdcl() {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("importdcl")()
-	}
-
-	var my *Sym
-	switch p.tok {
-	case LNAME, '@', '?':
-		// import with given name
-		my = p.sym()
-
-	case '.':
-		// import into my name space
-		my = Lookup(".")
-		p.next()
-	}
-
-	if p.tok != LLITERAL {
-		p.syntax_error("missing import path; require quoted string")
-		p.advance(';', ')')
-		return
-	}
-
-	line := lineno
-
-	// We need to clear importpkg before calling p.next(),
-	// otherwise it will affect lexlineno.
-	// TODO(mdempsky): Fix this clumsy API.
-	importfile(&p.val, p.indent)
-	ipkg := importpkg
-	importpkg = nil
-
-	p.next()
-	if ipkg == nil {
-		if nerrors == 0 {
-			Fatalf("phase error in import")
-		}
-		return
-	}
-
-	ipkg.Direct = true
-
-	if my == nil {
-		my = Lookup(ipkg.Name)
-	}
-
-	pack := Nod(OPACK, nil, nil)
-	pack.Sym = my
-	pack.Name.Pkg = ipkg
-	pack.Lineno = line
-
-	if strings.HasPrefix(my.Name, ".") {
-		importdot(ipkg, pack)
-		return
-	}
-	if my.Name == "init" {
-		lineno = line
-		Yyerror("cannot import package as init - init must be a func")
-		return
-	}
-	if my.Name == "_" {
-		return
-	}
-	if my.Def != nil {
-		lineno = line
-		redeclare(my, "as imported package name")
-	}
-	my.Def = pack
-	my.Lastlineno = line
-	my.Block = 1 // at top level
-}
-
-// import_package parses the header of an imported package as exported
-// in textual format from another package.
-func (p *parser) import_package() {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("import_package")()
-	}
-
-	p.want(LPACKAGE)
-	var name string
-	if p.tok == LNAME {
-		name = p.sym_.Name
-		p.next()
-	} else {
-		p.import_error()
-	}
-
-	// read but skip "safe" bit (see issue #15772)
-	if p.tok == LNAME {
-		p.next()
-	}
-	p.want(';')
-
-	if importpkg.Name == "" {
-		importpkg.Name = name
-		numImport[name]++
-	} else if importpkg.Name != name {
-		Yyerror("conflicting names %s and %s for package %q", importpkg.Name, name, importpkg.Path)
-	}
-
-	typecheckok = true
-	defercheckwidth()
-
-	p.hidden_import_list()
-	p.want('$')
-	// don't read past 2nd '$'
-	if p.tok != '$' {
-		p.import_error()
-	}
-
-	resumecheckwidth()
-	typecheckok = false
-}
-
-// Declaration = ConstDecl | TypeDecl | VarDecl .
-// ConstDecl   = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
-// TypeDecl    = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
-// VarDecl     = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
-func (p *parser) common_dcl() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("common_dcl")()
-	}
-
-	var dcl func() []*Node
-	switch p.tok {
-	case LVAR:
-		dcl = p.vardcl
-
-	case LCONST:
-		iota_ = 0
-		dcl = p.constdcl
-
-	case LTYPE:
-		dcl = p.typedcl
-
-	default:
-		panic("unreachable")
-	}
-
-	p.next()
-	var s []*Node
-	if p.got('(') {
-		for p.tok != EOF && p.tok != ')' {
-			s = append(s, dcl()...)
-			if !p.osemi(')') {
-				break
-			}
-		}
-		p.want(')')
-	} else {
-		s = dcl()
-	}
-
-	iota_ = -100000
-	lastconst = nil
-
-	return s
-}
-
-// VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
-func (p *parser) vardcl() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("vardcl")()
-	}
-
-	names := p.dcl_name_list()
-	var typ *Node
-	var exprs []*Node
-	if p.got('=') {
-		exprs = p.expr_list()
-	} else {
-		typ = p.ntype()
-		if p.got('=') {
-			exprs = p.expr_list()
-		}
-	}
-
-	return variter(names, typ, exprs)
-}
-
-// ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
-func (p *parser) constdcl() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("constdcl")()
-	}
-
-	names := p.dcl_name_list()
-	var typ *Node
-	var exprs []*Node
-	if p.tok != EOF && p.tok != ';' && p.tok != ')' {
-		typ = p.try_ntype()
-		if p.got('=') {
-			exprs = p.expr_list()
-		}
-	}
-
-	return constiter(names, typ, exprs)
-}
-
-// TypeSpec = identifier Type .
-func (p *parser) typedcl() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("typedcl")()
-	}
-
-	name := typedcl0(p.sym())
-
-	typ := p.try_ntype()
-	// handle case where type is missing
-	if typ == nil {
-		p.syntax_error("in type declaration")
-		p.advance(';', ')')
-	}
-
-	return []*Node{typedcl1(name, typ, true)}
-}
-
-// SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
-//
-// simple_stmt may return missing_stmt if labelOk is set.
-func (p *parser) simple_stmt(labelOk, rangeOk bool) *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("simple_stmt")()
-	}
-
-	if rangeOk && p.got(LRANGE) {
-		// LRANGE expr
-		r := Nod(ORANGE, nil, p.expr())
-		r.Etype = 0 // := flag
-		return r
-	}
-
-	lhs := p.expr_list()
-
-	if len(lhs) == 1 && p.tok != '=' && p.tok != LCOLAS && p.tok != LRANGE {
-		// expr
-		lhs := lhs[0]
-		switch p.tok {
-		case LASOP:
-			// expr LASOP expr
-			op := p.op
-			p.next()
-			rhs := p.expr()
-
-			stmt := Nod(OASOP, lhs, rhs)
-			stmt.Etype = EType(op) // rathole to pass opcode
-			return stmt
-
-		case LINCOP:
-			// expr LINCOP
-			p.next()
-
-			stmt := Nod(OASOP, lhs, Nodintconst(1))
-			stmt.Implicit = true
-			stmt.Etype = EType(p.op)
-			return stmt
-
-		case ':':
-			// labelname ':' stmt
-			if labelOk {
-				// If we have a labelname, it was parsed by operand
-				// (calling p.name()) and given an ONAME, ONONAME, OTYPE, OPACK, or OLITERAL node.
-				// We only have a labelname if there is a symbol (was issue 14006).
-				switch lhs.Op {
-				case ONAME, ONONAME, OTYPE, OPACK, OLITERAL:
-					if lhs.Sym != nil {
-						lhs = newname(lhs.Sym)
-						break
-					}
-					fallthrough
-				default:
-					p.syntax_error("expecting semicolon or newline or }")
-					// we already progressed, no need to advance
-				}
-				lhs := Nod(OLABEL, lhs, nil)
-				lhs.Sym = dclstack // context, for goto restrictions
-				p.next()           // consume ':' after making label node for correct lineno
-				return p.labeled_stmt(lhs)
-			}
-			fallthrough
-
-		default:
-			// expr
-			// Since a bare name used as an expression is an error,
-			// introduce a wrapper node where necessary to give the
-			// correct line.
-			return wrapname(lhs)
-		}
-	}
-
-	// expr_list
-	switch p.tok {
-	case '=':
-		p.next()
-		if rangeOk && p.got(LRANGE) {
-			// expr_list '=' LRANGE expr
-			r := Nod(ORANGE, nil, p.expr())
-			r.List.Set(lhs)
-			r.Etype = 0 // := flag
-			return r
-		}
-
-		// expr_list '=' expr_list
-		rhs := p.expr_list()
-
-		if len(lhs) == 1 && len(rhs) == 1 {
-			// simple
-			return Nod(OAS, lhs[0], rhs[0])
-		}
-		// multiple
-		stmt := Nod(OAS2, nil, nil)
-		stmt.List.Set(lhs)
-		stmt.Rlist.Set(rhs)
-		return stmt
-
-	case LCOLAS:
-		lno := lineno
-		p.next()
-
-		if rangeOk && p.got(LRANGE) {
-			// expr_list LCOLAS LRANGE expr
-			r := Nod(ORANGE, nil, p.expr())
-			r.List.Set(lhs)
-			r.Colas = true
-			colasdefn(lhs, r)
-			return r
-		}
-
-		// expr_list LCOLAS expr_list
-		rhs := p.expr_list()
-
-		if rhs[0].Op == OTYPESW {
-			ts := Nod(OTYPESW, nil, rhs[0].Right)
-			if len(rhs) > 1 {
-				Yyerror("expr.(type) must be alone in list")
-			}
-			if len(lhs) > 1 {
-				Yyerror("argument count mismatch: %d = %d", len(lhs), 1)
-			} else if (lhs[0].Op != ONAME && lhs[0].Op != OTYPE && lhs[0].Op != ONONAME && (lhs[0].Op != OLITERAL || lhs[0].Name == nil)) || isblank(lhs[0]) {
-				Yyerror("invalid variable name %s in type switch", lhs[0])
-			} else {
-				ts.Left = dclname(lhs[0].Sym)
-			} // it's a colas, so must not re-use an oldname
-			return ts
-		}
-		return colas(lhs, rhs, lno)
-
-	default:
-		p.syntax_error("expecting := or = or comma")
-		p.advance(';', '}')
-		return nil
-	}
-}
-
-// LabeledStmt = Label ":" Statement .
-// Label       = identifier .
-func (p *parser) labeled_stmt(label *Node) *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("labeled_stmt")()
-	}
-
-	var ls *Node // labeled statement
-	if p.tok != '}' && p.tok != EOF {
-		ls = p.stmt()
-		if ls == missing_stmt {
-			// report error at line of ':' token
-			p.syntax_error_at(label.Lineno, "missing statement after label")
-			// we are already at the end of the labeled statement - no need to advance
-			return missing_stmt
-		}
-	}
-
-	label.Name.Defn = ls
-	l := []*Node{label}
-	if ls != nil {
-		if ls.Op == OBLOCK && ls.Ninit.Len() == 0 {
-			l = append(l, ls.List.Slice()...)
-		} else {
-			l = append(l, ls)
-		}
-	}
-	return liststmt(l)
-}
-
-// case_ parses a superset of switch and select statement cases.
-// Later checks restrict the syntax to valid forms.
-//
-// ExprSwitchCase = "case" ExpressionList | "default" .
-// TypeSwitchCase = "case" TypeList | "default" .
-// TypeList       = Type { "," Type } .
-// CommCase       = "case" ( SendStmt | RecvStmt ) | "default" .
-// RecvStmt       = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
-// RecvExpr       = Expression .
-func (p *parser) case_(tswitch *Node) *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("case_")()
-	}
-
-	switch p.tok {
-	case LCASE:
-		p.next()
-		cases := p.expr_list() // expr_or_type_list
-		switch p.tok {
-		case ':':
-			// LCASE expr_or_type_list ':'
-
-			// will be converted to OCASE
-			// right will point to next case
-			// done in casebody()
-			markdcl() // matching popdcl in caseblock
-			stmt := Nod(OXCASE, nil, nil)
-			stmt.List.Set(cases)
-			if tswitch != nil {
-				if n := tswitch.Left; n != nil {
-					// type switch - declare variable
-					nn := newname(n.Sym)
-					declare(nn, dclcontext)
-					stmt.Rlist.Set1(nn)
-
-					// keep track of the instances for reporting unused
-					nn.Name.Defn = tswitch
-				}
-			}
-
-			p.next() // consume ':' after declaring type switch var for correct lineno
-			return stmt
-
-		case '=':
-			// LCASE expr_or_type_list '=' expr ':'
-			p.next()
-			rhs := p.expr()
-
-			// will be converted to OCASE
-			// right will point to next case
-			// done in casebody()
-			markdcl() // matching popdcl in caseblock
-			stmt := Nod(OXCASE, nil, nil)
-			var n *Node
-			if len(cases) == 1 {
-				n = Nod(OAS, cases[0], rhs)
-			} else {
-				n = Nod(OAS2, nil, nil)
-				n.List.Set(cases)
-				n.Rlist.Set1(rhs)
-			}
-			stmt.List.Set1(n)
-
-			p.want(':') // consume ':' after declaring select cases for correct lineno
-			return stmt
-
-		case LCOLAS:
-			// LCASE expr_or_type_list LCOLAS expr ':'
-			lno := lineno
-			p.next()
-			rhs := p.expr()
-
-			// will be converted to OCASE
-			// right will point to next case
-			// done in casebody()
-			markdcl() // matching popdcl in caseblock
-			stmt := Nod(OXCASE, nil, nil)
-			stmt.List.Set1(colas(cases, []*Node{rhs}, lno))
-
-			p.want(':') // consume ':' after declaring select cases for correct lineno
-			return stmt
-
-		default:
-			markdcl()                     // for matching popdcl in caseblock
-			stmt := Nod(OXCASE, nil, nil) // don't return nil
-			p.syntax_error("expecting := or = or : or comma")
-			p.advance(LCASE, LDEFAULT, '}')
-			return stmt
-		}
-
-	case LDEFAULT:
-		// LDEFAULT ':'
-		p.next()
-
-		markdcl() // matching popdcl in caseblock
-		stmt := Nod(OXCASE, nil, nil)
-		if tswitch != nil {
-			if n := tswitch.Left; n != nil {
-				// type switch - declare variable
-				nn := newname(n.Sym)
-				declare(nn, dclcontext)
-				stmt.Rlist.Set1(nn)
-
-				// keep track of the instances for reporting unused
-				nn.Name.Defn = tswitch
-			}
-		}
-
-		p.want(':') // consume ':' after declaring type switch var for correct lineno
-		return stmt
-
-	default:
-		markdcl()                     // matching popdcl in caseblock
-		stmt := Nod(OXCASE, nil, nil) // don't return nil
-		p.syntax_error("expecting case or default or }")
-		p.advance(LCASE, LDEFAULT, '}')
-		return stmt
-	}
-}
-
-// Block         = "{" StatementList "}" .
-// StatementList = { Statement ";" } .
-func (p *parser) compound_stmt() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("compound_stmt")()
-	}
-
-	markdcl()
-	p.want('{')
-	l := p.stmt_list()
-	p.want('}')
-	popdcl()
-
-	if len(l) == 0 {
-		return Nod(OEMPTY, nil, nil)
-	}
-	return liststmt(l)
-}
-
-// caseblock parses a superset of switch and select clauses.
-//
-// ExprCaseClause = ExprSwitchCase ":" StatementList .
-// TypeCaseClause = TypeSwitchCase ":" StatementList .
-// CommClause     = CommCase ":" StatementList .
-func (p *parser) caseblock(tswitch *Node) *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("caseblock")()
-	}
-
-	stmt := p.case_(tswitch) // does markdcl
-	stmt.Xoffset = int64(block)
-	stmt.Nbody.Set(p.stmt_list())
-
-	popdcl()
-
-	return stmt
-}
-
-// caseblock_list parses a superset of switch and select clause lists.
-func (p *parser) caseblock_list(tswitch *Node) (l []*Node) {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("caseblock_list")()
-	}
-
-	if !p.got('{') {
-		p.syntax_error("missing { after switch clause")
-		p.advance(LCASE, LDEFAULT, '}')
-	}
-
-	for p.tok != EOF && p.tok != '}' {
-		l = append(l, p.caseblock(tswitch))
-	}
-	p.want('}')
-	return
-}
-
-// loop_body parses if and for statement bodies.
-func (p *parser) loop_body(context string) []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("loop_body")()
-	}
-
-	markdcl()
-	if !p.got('{') {
-		p.syntax_error("missing { after " + context)
-		p.advance(LNAME, '}')
-	}
-
-	body := p.stmt_list()
-	popdcl()
-	p.want('}')
-
-	return body
-}
-
-// for_header parses the header portion of a for statement.
-//
-// ForStmt   = "for" [ Condition | ForClause | RangeClause ] Block .
-// Condition = Expression .
-func (p *parser) for_header() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("for_header")()
-	}
-
-	init, cond, post := p.header(true)
-
-	if init != nil || post != nil {
-		// init ; test ; incr
-		if post != nil && post.Colas {
-			Yyerror("cannot declare in the for-increment")
-		}
-		h := Nod(OFOR, nil, nil)
-		if init != nil {
-			h.Ninit.Set1(init)
-		}
-		h.Left = cond
-		h.Right = post
-		return h
-	}
-
-	if cond != nil && cond.Op == ORANGE {
-		// range_stmt - handled by pexpr
-		return cond
-	}
-
-	// normal test
-	h := Nod(OFOR, nil, nil)
-	h.Left = cond
-	return h
-}
-
-func (p *parser) for_body() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("for_body")()
-	}
-
-	stmt := p.for_header()
-	body := p.loop_body("for clause")
-
-	stmt.Nbody.Append(body...)
-	return stmt
-}
-
-// ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
-func (p *parser) for_stmt() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("for_stmt")()
-	}
-
-	p.want(LFOR)
-	markdcl()
-	body := p.for_body()
-	popdcl()
-
-	return body
-}
-
-// header parses a combination of if, switch, and for statement headers:
-//
-// Header   = [ InitStmt ";" ] [ Expression ] .
-// Header   = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .  // for_stmt only
-// InitStmt = SimpleStmt .
-// PostStmt = SimpleStmt .
-func (p *parser) header(for_stmt bool) (init, cond, post *Node) {
-	if p.tok == '{' {
-		return
-	}
-
-	outer := p.xnest
-	p.xnest = -1
-
-	if p.tok != ';' {
-		// accept potential vardcl but complain
-		// (for test/syntax/forvar.go)
-		if for_stmt && p.tok == LVAR {
-			Yyerror("var declaration not allowed in for initializer")
-			p.next()
-		}
-		init = p.simple_stmt(false, for_stmt)
-		// If we have a range clause, we are done.
-		if for_stmt && init.Op == ORANGE {
-			cond = init
-			init = nil
-
-			p.xnest = outer
-			return
-		}
-	}
-	if p.got(';') {
-		if for_stmt {
-			if p.tok != ';' {
-				cond = p.simple_stmt(false, false)
-			}
-			p.want(';')
-			if p.tok != '{' {
-				post = p.simple_stmt(false, false)
-			}
-		} else if p.tok != '{' {
-			cond = p.simple_stmt(false, false)
-		}
-	} else {
-		cond = init
-		init = nil
-	}
-
-	p.xnest = outer
-	return
-}
-
-func (p *parser) if_header() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("if_header")()
-	}
-
-	init, cond, _ := p.header(false)
-	h := Nod(OIF, nil, nil)
-	if init != nil {
-		h.Ninit.Set1(init)
-	}
-	h.Left = cond
-	return h
-}
-
-// IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
-func (p *parser) if_stmt() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("if_stmt")()
-	}
-
-	p.want(LIF)
-
-	markdcl()
-
-	stmt := p.if_header()
-	if stmt.Left == nil {
-		Yyerror("missing condition in if statement")
-	}
-
-	stmt.Nbody.Set(p.loop_body("if clause"))
-
-	if p.got(LELSE) {
-		switch p.tok {
-		case LIF:
-			stmt.Rlist.Set1(p.if_stmt())
-		case '{':
-			cs := p.compound_stmt()
-			if cs.Op == OBLOCK && cs.Ninit.Len() == 0 {
-				stmt.Rlist.Set(cs.List.Slice())
-			} else {
-				stmt.Rlist.Set1(cs)
-			}
-		default:
-			p.syntax_error("else must be followed by if or statement block")
-			p.advance(LNAME, '}')
-		}
-	}
-
-	popdcl()
-	return stmt
-}
-
-// switch_stmt parses both expression and type switch statements.
-//
-// SwitchStmt     = ExprSwitchStmt | TypeSwitchStmt .
-// ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
-// TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
-func (p *parser) switch_stmt() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("switch_stmt")()
-	}
-
-	p.want(LSWITCH)
-	markdcl()
-
-	hdr := p.if_header()
-	hdr.Op = OSWITCH
-
-	tswitch := hdr.Left
-	if tswitch != nil && tswitch.Op != OTYPESW {
-		tswitch = nil
-	}
-
-	hdr.List.Set(p.caseblock_list(tswitch))
-	popdcl()
-
-	return hdr
-}
-
-// SelectStmt = "select" "{" { CommClause } "}" .
-func (p *parser) select_stmt() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("select_stmt")()
-	}
-
-	p.want(LSELECT)
-	hdr := Nod(OSELECT, nil, nil)
-	hdr.List.Set(p.caseblock_list(nil))
-	return hdr
-}
-
-// Expression = UnaryExpr | Expression binary_op Expression .
-func (p *parser) bexpr(prec OpPrec) *Node {
-	// don't trace bexpr - only leads to overly nested trace output
-
-	// prec is precedence of the prior/enclosing binary operator (if any),
-	// so we only want to parse tokens of greater precedence.
-
-	x := p.uexpr()
-	for p.prec > prec {
-		op, prec1 := p.op, p.prec
-		p.next()
-		x = Nod(op, x, p.bexpr(prec1))
-	}
-	return x
-}
-
-func (p *parser) expr() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("expr")()
-	}
-
-	return p.bexpr(0)
-}
-
-func unparen(x *Node) *Node {
-	for x.Op == OPAREN {
-		x = x.Left
-	}
-	return x
-}
-
-// UnaryExpr = PrimaryExpr | unary_op UnaryExpr .
-func (p *parser) uexpr() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("uexpr")()
-	}
-
-	var op Op
-	switch p.tok {
-	case '*':
-		op = OIND
-
-	case '&':
-		p.next()
-		// uexpr may have returned a parenthesized composite literal
-		// (see comment in operand) - remove parentheses if any
-		x := unparen(p.uexpr())
-		if x.Op == OCOMPLIT {
-			// Special case for &T{...}: turn into (*T){...}.
-			x.Right = Nod(OIND, x.Right, nil)
-			x.Right.Implicit = true
-		} else {
-			x = Nod(OADDR, x, nil)
-		}
-		return x
-
-	case '+':
-		op = OPLUS
-
-	case '-':
-		op = OMINUS
-
-	case '!':
-		op = ONOT
-
-	case '^':
-		op = OCOM
-
-	case LCOMM:
-		// receive op (<-x) or receive-only channel (<-chan E)
-		p.next()
-
-		// If the next token is LCHAN we still don't know if it is
-		// a channel (<-chan int) or a receive op (<-chan int(ch)).
-		// We only know once we have found the end of the uexpr.
-
-		x := p.uexpr()
-
-		// There are two cases:
-		//
-		//   <-chan...  => <-x is a channel type
-		//   <-x        => <-x is a receive operation
-		//
-		// In the first case, <- must be re-associated with
-		// the channel type parsed already:
-		//
-		//   <-(chan E)   =>  (<-chan E)
-		//   <-(chan<-E)  =>  (<-chan (<-E))
-
-		if x.Op == OTCHAN {
-			// x is a channel type => re-associate <-
-			dir := Csend
-			t := x
-			for ; t.Op == OTCHAN && dir == Csend; t = t.Left {
-				dir = ChanDir(t.Etype)
-				if dir == Crecv {
-					// t is type <-chan E but <-<-chan E is not permitted
-					// (report same error as for "type _ <-<-chan E")
-					p.syntax_error("unexpected <-, expecting chan")
-					// already progressed, no need to advance
-				}
-				t.Etype = EType(Crecv)
-			}
-			if dir == Csend {
-				// channel dir is <- but channel element E is not a channel
-				// (report same error as for "type _ <-chan<-E")
-				p.syntax_error(fmt.Sprintf("unexpected %v, expecting chan", t))
-				// already progressed, no need to advance
-			}
-			return x
-		}
-
-		// x is not a channel type => we have a receive op
-		return Nod(ORECV, x, nil)
-
-	default:
-		return p.pexpr(false)
-	}
-
-	// simple uexpr
-	p.next()
-	return Nod(op, p.uexpr(), nil)
-}
-
-// pseudocall parses call-like statements that can be preceded by 'defer' and 'go'.
-func (p *parser) pseudocall() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("pseudocall")()
-	}
-
-	x := p.pexpr(p.tok == '(') // keep_parens so we can report error below
-	switch x.Op {
-	case OCALL:
-		return x
-	case OPAREN:
-		Yyerror("expression in go/defer must not be parenthesized")
-		// already progressed, no need to advance
-	default:
-		Yyerror("expression in go/defer must be function call")
-		// already progressed, no need to advance
-	}
-	return nil
-}
-
-// Operand     = Literal | OperandName | MethodExpr | "(" Expression ")" .
-// Literal     = BasicLit | CompositeLit | FunctionLit .
-// BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
-// OperandName = identifier | QualifiedIdent.
-func (p *parser) operand(keep_parens bool) *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("operand")()
-	}
-
-	switch p.tok {
-	case LLITERAL:
-		x := nodlit(p.val)
-		p.next()
-		return x
-
-	case LNAME, '@', '?':
-		return p.name()
-
-	case '(':
-		p.next()
-		p.xnest++
-		x := p.expr() // expr_or_type
-		p.xnest--
-		p.want(')')
-
-		// Optimization: Record presence of ()'s only where needed
-		// for error reporting. Don't bother in other cases; it is
-		// just a waste of memory and time.
-
-		// Parentheses are not permitted on lhs of := .
-		switch x.Op {
-		case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW:
-			keep_parens = true
-		}
-
-		// Parentheses are not permitted around T in a composite
-		// literal T{}. If the next token is a {, assume x is a
-		// composite literal type T (it may not be, { could be
-		// the opening brace of a block, but we don't know yet).
-		if p.tok == '{' {
-			keep_parens = true
-		}
-
-		// Parentheses are also not permitted around the expression
-		// in a go/defer statement. In that case, operand is called
-		// with keep_parens set.
-		if keep_parens {
-			x = Nod(OPAREN, x, nil)
-		}
-		return x
-
-	case LFUNC:
-		t := p.ntype() // fntype
-		if p.tok == '{' {
-			// fnlitdcl
-			closurehdr(t)
-			// fnliteral
-			p.next() // consume '{'
-			p.fnest++
-			p.xnest++
-			body := p.stmt_list()
-			p.xnest--
-			p.fnest--
-			p.want('}')
-			return closurebody(body)
-		}
-		return t
-
-	case '[', LCHAN, LMAP, LSTRUCT, LINTERFACE:
-		return p.ntype() // othertype
-
-	case '{':
-		// common case: p.header is missing simple_stmt before { in if, for, switch
-		p.syntax_error("missing operand")
-		// '{' will be consumed in pexpr - no need to consume it here
-		return nil
-
-	default:
-		p.syntax_error("expecting expression")
-		p.advance()
-		return nil
-	}
-
-	// Syntactically, composite literals are operands. Because a complit
-	// type may be a qualified identifier which is handled by pexpr
-	// (together with selector expressions), complits are parsed there
-	// as well (operand is only called from pexpr).
-}
-
-// PrimaryExpr =
-// 	Operand |
-// 	Conversion |
-// 	PrimaryExpr Selector |
-// 	PrimaryExpr Index |
-// 	PrimaryExpr Slice |
-// 	PrimaryExpr TypeAssertion |
-// 	PrimaryExpr Arguments .
-//
-// Selector       = "." identifier .
-// Index          = "[" Expression "]" .
-// Slice          = "[" ( [ Expression ] ":" [ Expression ] ) |
-//                      ( [ Expression ] ":" Expression ":" Expression )
-//                  "]" .
-// TypeAssertion  = "." "(" Type ")" .
-// Arguments      = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
-func (p *parser) pexpr(keep_parens bool) *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("pexpr")()
-	}
-
-	x := p.operand(keep_parens)
-
-loop:
-	for {
-		switch p.tok {
-		case '.':
-			p.next()
-			switch p.tok {
-			case LNAME, '@', '?':
-				// pexpr '.' sym
-				x = p.new_dotname(x)
-
-			case '(':
-				p.next()
-				switch p.tok {
-				default:
-					// pexpr '.' '(' expr_or_type ')'
-					t := p.expr() // expr_or_type
-					p.want(')')
-					x = Nod(ODOTTYPE, x, t)
-
-				case LTYPE:
-					// pexpr '.' '(' LTYPE ')'
-					p.next()
-					p.want(')')
-					x = Nod(OTYPESW, nil, x)
-				}
-
-			default:
-				p.syntax_error("expecting name or (")
-				p.advance(';', '}')
-			}
-
-		case '[':
-			p.next()
-			p.xnest++
-			var index [3]*Node
-			if p.tok != ':' {
-				index[0] = p.expr()
-			}
-			ncol := 0
-			for ncol < len(index)-1 && p.got(':') {
-				ncol++
-				if p.tok != EOF && p.tok != ':' && p.tok != ']' {
-					index[ncol] = p.expr()
-				}
-			}
-			p.xnest--
-			p.want(']')
-
-			switch ncol {
-			case 0:
-				i := index[0]
-				if i == nil {
-					Yyerror("missing index in index expression")
-				}
-				x = Nod(OINDEX, x, i)
-			case 1:
-				x = Nod(OSLICE, x, nil)
-				x.SetSliceBounds(index[0], index[1], nil)
-			case 2:
-				if index[1] == nil {
-					Yyerror("middle index required in 3-index slice")
-				}
-				if index[2] == nil {
-					Yyerror("final index required in 3-index slice")
-				}
-				x = Nod(OSLICE3, x, nil)
-				x.SetSliceBounds(index[0], index[1], index[2])
-
-			default:
-				panic("unreachable")
-			}
-
-		case '(':
-			// convtype '(' expr ocomma ')'
-			args, ddd := p.arg_list()
-
-			// call or conversion
-			x = Nod(OCALL, x, nil)
-			x.List.Set(args)
-			x.Isddd = ddd
-
-		case '{':
-			// operand may have returned a parenthesized complit
-			// type; accept it but complain if we have a complit
-			t := unparen(x)
-			// determine if '{' belongs to a complit or a compound_stmt
-			complit_ok := false
-			switch t.Op {
-			case ONAME, ONONAME, OTYPE, OPACK, OXDOT, ODOT:
-				if p.xnest >= 0 {
-					// x is considered a comptype
-					complit_ok = true
-				}
-			case OTARRAY, OTSTRUCT, OTMAP:
-				// x is a comptype
-				complit_ok = true
-			}
-			if !complit_ok {
-				break loop
-			}
-			if t != x {
-				p.syntax_error("cannot parenthesize type in composite literal")
-				// already progressed, no need to advance
-			}
-			n := p.complitexpr()
-			n.Right = x
-			x = n
-
-		default:
-			break loop
-		}
-	}
-
-	return x
-}
-
-// KeyedElement = [ Key ":" ] Element .
-func (p *parser) keyval() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("keyval")()
-	}
-
-	// A composite literal commonly spans several lines,
-	// so the line number on errors may be misleading.
-	// Wrap values (but not keys!) that don't carry line
-	// numbers.
-
-	x := p.bare_complitexpr()
-
-	if p.got(':') {
-		// key ':' value
-		return Nod(OKEY, x, wrapname(p.bare_complitexpr()))
-	}
-
-	// value
-	return wrapname(x)
-}
-
-func wrapname(x *Node) *Node {
-	// These nodes do not carry line numbers.
-	// Introduce a wrapper node to give the correct line.
-	switch x.Op {
-	case ONAME, ONONAME, OTYPE, OPACK, OLITERAL:
-		x = Nod(OPAREN, x, nil)
-		x.Implicit = true
-	}
-	return x
-}
-
-// Element = Expression | LiteralValue .
-func (p *parser) bare_complitexpr() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("bare_complitexpr")()
-	}
-
-	if p.tok == '{' {
-		// '{' start_complit braced_keyval_list '}'
-		return p.complitexpr()
-	}
-
-	return p.expr()
-}
-
-// LiteralValue = "{" [ ElementList [ "," ] ] "}" .
-func (p *parser) complitexpr() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("complitexpr")()
-	}
-
-	// make node early so we get the right line number
-	n := Nod(OCOMPLIT, nil, nil)
-
-	p.want('{')
-	p.xnest++
-
-	var l []*Node
-	for p.tok != EOF && p.tok != '}' {
-		l = append(l, p.keyval())
-		if !p.ocomma('}') {
-			break
-		}
-	}
-
-	p.xnest--
-	p.want('}')
-
-	n.List.Set(l)
-	return n
-}
-
-// names and types
-//	newname is used before declared
-//	oldname is used after declared
-func (p *parser) new_name(sym *Sym) *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("new_name")()
-	}
-
-	if sym != nil {
-		return newname(sym)
-	}
-	return nil
-}
-
-func (p *parser) dcl_name() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("dcl_name")()
-	}
-
-	symlineno := lineno
-	sym := p.sym()
-	if sym == nil {
-		yyerrorl(symlineno, "invalid declaration")
-		return nil
-	}
-	return dclname(sym)
-}
-
-func (p *parser) onew_name() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("onew_name")()
-	}
-
-	switch p.tok {
-	case LNAME, '@', '?':
-		return p.new_name(p.sym())
-	}
-	return nil
-}
-
-func (p *parser) sym() *Sym {
-	switch p.tok {
-	case LNAME:
-		s := p.sym_ // from localpkg
-		p.next()
-		// during imports, unqualified non-exported identifiers are from builtinpkg
-		if importpkg != nil && !exportname(s.Name) {
-			s = Pkglookup(s.Name, builtinpkg)
-		}
-		return s
-
-	case '@':
-		return p.hidden_importsym()
-
-	case '?':
-		p.next()
-		return nil
-
-	default:
-		p.syntax_error("expecting name")
-		p.advance()
-		return new(Sym)
-	}
-}
-
-func mkname(sym *Sym) *Node {
-	n := oldname(sym)
-	if n.Name != nil && n.Name.Pack != nil {
-		n.Name.Pack.Used = true
-	}
-	return n
-}
-
-func (p *parser) name() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("name")()
-	}
-
-	return mkname(p.sym())
-}
-
-// [ "..." ] Type
-func (p *parser) dotdotdot() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("dotdotdot")()
-	}
-
-	p.want(LDDD)
-	if typ := p.try_ntype(); typ != nil {
-		return Nod(ODDD, typ, nil)
-	}
-
-	Yyerror("final argument in variadic function missing type")
-	return Nod(ODDD, typenod(typ(TINTER)), nil)
-}
-
-func (p *parser) ntype() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("ntype")()
-	}
-
-	if typ := p.try_ntype(); typ != nil {
-		return typ
-	}
-
-	p.syntax_error("")
-	p.advance()
-	return nil
-}
-
-// signature parses a function signature and returns an OTFUNC node.
-//
-// Signature = Parameters [ Result ] .
-func (p *parser) signature(recv *Node) *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("signature")()
-	}
-
-	params := p.param_list(true)
-
-	var result []*Node
-	if p.tok == '(' {
-		result = p.param_list(false)
-	} else if t := p.try_ntype(); t != nil {
-		result = []*Node{Nod(ODCLFIELD, nil, t)}
-	}
-
-	typ := Nod(OTFUNC, recv, nil)
-	typ.List.Set(params)
-	typ.Rlist.Set(result)
-
-	return typ
-}
-
-// try_ntype is like ntype but it returns nil if there was no type
-// instead of reporting an error.
-//
-// Type     = TypeName | TypeLit | "(" Type ")" .
-// TypeName = identifier | QualifiedIdent .
-// TypeLit  = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
-// 	      SliceType | MapType | ChannelType .
-func (p *parser) try_ntype() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("try_ntype")()
-	}
-
-	switch p.tok {
-	case LCOMM:
-		// recvchantype
-		p.next()
-		p.want(LCHAN)
-		t := Nod(OTCHAN, p.chan_elem(), nil)
-		t.Etype = EType(Crecv)
-		return t
-
-	case LFUNC:
-		// fntype
-		p.next()
-		return p.signature(nil)
-
-	case '[':
-		// '[' oexpr ']' ntype
-		// '[' LDDD ']' ntype
-		p.next()
-		p.xnest++
-		var len *Node
-		if p.tok != ']' {
-			if p.got(LDDD) {
-				len = Nod(ODDD, nil, nil)
-			} else {
-				len = p.expr()
-			}
-		}
-		p.xnest--
-		p.want(']')
-		return Nod(OTARRAY, len, p.ntype())
-
-	case LCHAN:
-		// LCHAN non_recvchantype
-		// LCHAN LCOMM ntype
-		p.next()
-		var dir = EType(Cboth)
-		if p.got(LCOMM) {
-			dir = EType(Csend)
-		}
-		t := Nod(OTCHAN, p.chan_elem(), nil)
-		t.Etype = dir
-		return t
-
-	case LMAP:
-		// LMAP '[' ntype ']' ntype
-		p.next()
-		p.want('[')
-		key := p.ntype()
-		p.want(']')
-		val := p.ntype()
-		return Nod(OTMAP, key, val)
-
-	case LSTRUCT:
-		return p.structtype()
-
-	case LINTERFACE:
-		return p.interfacetype()
-
-	case '*':
-		// ptrtype
-		p.next()
-		return Nod(OIND, p.ntype(), nil)
-
-	case LNAME, '@', '?':
-		return p.dotname()
-
-	case '(':
-		p.next()
-		t := p.ntype()
-		p.want(')')
-		return t
-
-	default:
-		return nil
-	}
-}
-
-func (p *parser) chan_elem() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("chan_elem")()
-	}
-
-	if typ := p.try_ntype(); typ != nil {
-		return typ
-	}
-
-	p.syntax_error("missing channel element type")
-	// assume element type is simply absent - don't advance
-	return nil
-}
-
-func (p *parser) new_dotname(obj *Node) *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("new_dotname")()
-	}
-
-	sel := p.sym()
-	if obj.Op == OPACK {
-		s := restrictlookup(sel.Name, obj.Name.Pkg)
-		obj.Used = true
-		return oldname(s)
-	}
-	return NodSym(OXDOT, obj, sel)
-}
-
-func (p *parser) dotname() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("dotname")()
-	}
-
-	name := p.name()
-	if p.got('.') {
-		return p.new_dotname(name)
-	}
-	return name
-}
-
-// StructType = "struct" "{" { FieldDecl ";" } "}" .
-func (p *parser) structtype() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("structtype")()
-	}
-
-	p.want(LSTRUCT)
-	p.want('{')
-	var l []*Node
-	for p.tok != EOF && p.tok != '}' {
-		l = append(l, p.structdcl()...)
-		if !p.osemi('}') {
-			break
-		}
-	}
-	p.want('}')
-
-	t := Nod(OTSTRUCT, nil, nil)
-	t.List.Set(l)
-	return t
-}
-
-// InterfaceType = "interface" "{" { MethodSpec ";" } "}" .
-func (p *parser) interfacetype() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("interfacetype")()
-	}
-
-	p.want(LINTERFACE)
-	p.want('{')
-	var l []*Node
-	for p.tok != EOF && p.tok != '}' {
-		l = append(l, p.interfacedcl())
-		if !p.osemi('}') {
-			break
-		}
-	}
-	p.want('}')
-
-	t := Nod(OTINTER, nil, nil)
-	t.List.Set(l)
-	return t
-}
-
-// Function stuff.
-// All in one place to show how crappy it all is.
-
-func (p *parser) xfndcl() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("xfndcl")()
-	}
-
-	p.want(LFUNC)
-	f := p.fndcl()
-	body := p.fnbody()
-
-	if f == nil {
-		return nil
-	}
-
-	f.Nbody.Set(body)
-	f.Noescape = p.pragma&Noescape != 0
-	if f.Noescape && len(body) != 0 {
-		Yyerror("can only use //go:noescape with external func implementations")
-	}
-	f.Func.Pragma = p.pragma
-	f.Func.Endlineno = lineno
-
-	funcbody(f)
-
-	return f
-}
-
-// FunctionDecl = "func" FunctionName ( Function | Signature ) .
-// FunctionName = identifier .
-// Function     = Signature FunctionBody .
-// MethodDecl   = "func" Receiver MethodName ( Function | Signature ) .
-// Receiver     = Parameters .
-func (p *parser) fndcl() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("fndcl")()
-	}
-
-	switch p.tok {
-	case LNAME, '@', '?':
-		// FunctionName Signature
-		name := p.sym()
-		t := p.signature(nil)
-
-		if name.Name == "init" {
-			name = renameinit()
-			if t.List.Len() > 0 || t.Rlist.Len() > 0 {
-				Yyerror("func init must have no arguments and no return values")
-			}
-		}
-
-		if localpkg.Name == "main" && name.Name == "main" {
-			if t.List.Len() > 0 || t.Rlist.Len() > 0 {
-				Yyerror("func main must have no arguments and no return values")
-			}
-		}
-
-		f := Nod(ODCLFUNC, nil, nil)
-		f.Func.Nname = newfuncname(name)
-		f.Func.Nname.Name.Defn = f
-		f.Func.Nname.Name.Param.Ntype = t // TODO: check if nname already has an ntype
-		declare(f.Func.Nname, PFUNC)
-
-		funchdr(f)
-		return f
-
-	case '(':
-		// Receiver MethodName Signature
-		rparam := p.param_list(false)
-		var recv *Node
-		if len(rparam) > 0 {
-			recv = rparam[0]
-		}
-		name := p.sym()
-		t := p.signature(recv)
-
-		// check after parsing header for fault-tolerance
-		if recv == nil {
-			Yyerror("method has no receiver")
-			return nil
-		}
-
-		if len(rparam) > 1 {
-			Yyerror("method has multiple receivers")
-			return nil
-		}
-
-		if recv.Op != ODCLFIELD {
-			Yyerror("bad receiver in method")
-			return nil
-		}
-
-		f := Nod(ODCLFUNC, nil, nil)
-		f.Func.Shortname = newfuncname(name)
-		f.Func.Nname = methodname1(f.Func.Shortname, recv.Right)
-		f.Func.Nname.Name.Defn = f
-		f.Func.Nname.Name.Param.Ntype = t
-		declare(f.Func.Nname, PFUNC)
-
-		funchdr(f)
-		return f
-
-	default:
-		p.syntax_error("expecting name or (")
-		p.advance('{', ';')
-		return nil
-	}
-}
-
-func (p *parser) hidden_fndcl() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_fndcl")()
-	}
-
-	switch p.tok {
-	default:
-		// hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres
-		s1 := p.hidden_pkg_importsym()
-		p.want('(')
-		s3 := p.ohidden_funarg_list()
-		p.want(')')
-		s5 := p.ohidden_funres()
-
-		s := s1
-		t := functype(nil, s3, s5)
-
-		importsym(s, ONAME)
-		if s.Def != nil && s.Def.Op == ONAME {
-			if Eqtype(t, s.Def.Type) {
-				dclcontext = PDISCARD // since we skip funchdr below
-				return nil
-			}
-			Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", s, s.Def.Type, t)
-		}
-
-		ss := newfuncname(s)
-		ss.Type = t
-		declare(ss, PFUNC)
-
-		funchdr(ss)
-		return ss
-
-	case '(':
-		// '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres
-		p.next()
-		s2 := p.hidden_funarg_list()
-		p.want(')')
-		s4 := p.sym()
-		p.want('(')
-		s6 := p.ohidden_funarg_list()
-		p.want(')')
-		s8 := p.ohidden_funres()
-
-		ss := methodname1(newname(s4), s2[0].Right)
-		ss.Type = functype(s2[0], s6, s8)
-
-		checkwidth(ss.Type)
-		addmethod(s4, ss.Type, p.structpkg, false, p.pragma&Nointerface != 0)
-		p.pragma = 0
-		funchdr(ss)
-
-		// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
-		// (dotmeth's type).Nname.Inl, and dotmeth's type has been pulled
-		// out by typecheck's lookdot as this $$.ttype. So by providing
-		// this back link here we avoid special casing there.
-		ss.Type.SetNname(ss)
-		return ss
-	}
-}
-
-// FunctionBody = Block .
-func (p *parser) fnbody() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("fnbody")()
-	}
-
-	if p.got('{') {
-		p.fnest++
-		body := p.stmt_list()
-		p.fnest--
-		p.want('}')
-		if body == nil {
-			body = []*Node{Nod(OEMPTY, nil, nil)}
-		}
-		return body
-	}
-
-	return nil
-}
-
-// Declaration  = ConstDecl | TypeDecl | VarDecl .
-// TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
-func (p *parser) xdcl_list() (l []*Node) {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("xdcl_list")()
-	}
-
-	for p.tok != EOF {
-		switch p.tok {
-		case LVAR, LCONST, LTYPE:
-			l = append(l, p.common_dcl()...)
-
-		case LFUNC:
-			l = append(l, p.xfndcl())
-
-		default:
-			if p.tok == '{' && len(l) != 0 && l[len(l)-1].Op == ODCLFUNC && l[len(l)-1].Nbody.Len() == 0 {
-				// opening { of function declaration on next line
-				p.syntax_error("unexpected semicolon or newline before {")
-			} else {
-				p.syntax_error("non-declaration statement outside function body")
-			}
-			p.advance(LVAR, LCONST, LTYPE, LFUNC)
-			continue
-		}
-
-		// Reset p.pragma BEFORE advancing to the next token (consuming ';')
-		// since comments before may set pragmas for the next function decl.
-		p.pragma = 0
-
-		if p.tok != EOF && !p.got(';') {
-			p.syntax_error("after top level declaration")
-			p.advance(LVAR, LCONST, LTYPE, LFUNC)
-		}
-	}
-
-	if nsyntaxerrors == 0 {
-		testdclstack()
-	}
-	return
-}
-
-// FieldDecl      = (IdentifierList Type | AnonymousField) [ Tag ] .
-// AnonymousField = [ "*" ] TypeName .
-// Tag            = string_lit .
-func (p *parser) structdcl() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("structdcl")()
-	}
-
-	var sym *Sym
-	switch p.tok {
-	case LNAME:
-		sym = p.sym_
-		p.next()
-		if sym == nil {
-			panic("unreachable") // we must have a sym for LNAME
-		}
-		if p.tok == '.' || p.tok == LLITERAL || p.tok == ';' || p.tok == '}' {
-			// embed oliteral
-			field := p.embed(sym)
-			tag := p.oliteral()
-
-			field.SetVal(tag)
-			return []*Node{field}
-		}
-
-		// LNAME belongs to first *Sym of new_name_list
-		//
-		// during imports, unqualified non-exported identifiers are from builtinpkg
-		if importpkg != nil && !exportname(sym.Name) {
-			sym = Pkglookup(sym.Name, builtinpkg)
-			if sym == nil {
-				p.import_error()
-			}
-		}
-		fallthrough
-
-	case '@', '?':
-		// new_name_list ntype oliteral
-		fields := p.new_name_list(sym)
-		typ := p.ntype()
-		tag := p.oliteral()
-
-		if len(fields) == 0 || fields[0].Sym.Name == "?" {
-			// ? symbol, during import
-			n := typ
-			if n.Op == OIND {
-				n = n.Left
-			}
-			n = embedded(n.Sym, importpkg)
-			n.Right = typ
-			n.SetVal(tag)
-			return []*Node{n}
-		}
-
-		for i, n := range fields {
-			fields[i] = Nod(ODCLFIELD, n, typ)
-			fields[i].SetVal(tag)
-		}
-		return fields
-
-	case '(':
-		p.next()
-		if p.got('*') {
-			// '(' '*' embed ')' oliteral
-			field := p.embed(nil)
-			p.want(')')
-			tag := p.oliteral()
-
-			field.Right = Nod(OIND, field.Right, nil)
-			field.SetVal(tag)
-			Yyerror("cannot parenthesize embedded type")
-			return []*Node{field}
-
-		} else {
-			// '(' embed ')' oliteral
-			field := p.embed(nil)
-			p.want(')')
-			tag := p.oliteral()
-
-			field.SetVal(tag)
-			Yyerror("cannot parenthesize embedded type")
-			return []*Node{field}
-		}
-
-	case '*':
-		p.next()
-		if p.got('(') {
-			// '*' '(' embed ')' oliteral
-			field := p.embed(nil)
-			p.want(')')
-			tag := p.oliteral()
-
-			field.Right = Nod(OIND, field.Right, nil)
-			field.SetVal(tag)
-			Yyerror("cannot parenthesize embedded type")
-			return []*Node{field}
-
-		} else {
-			// '*' embed oliteral
-			field := p.embed(nil)
-			tag := p.oliteral()
-
-			field.Right = Nod(OIND, field.Right, nil)
-			field.SetVal(tag)
-			return []*Node{field}
-		}
-
-	default:
-		p.syntax_error("expecting field name or embedded type")
-		p.advance(';', '}')
-		return nil
-	}
-}
-
-func (p *parser) oliteral() (v Val) {
-	if p.tok == LLITERAL {
-		v = p.val
-		p.next()
-	}
-	return
-}
-
-func (p *parser) packname(name *Sym) *Sym {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("embed")()
-	}
-
-	if name != nil {
-		// LNAME was already consumed and is coming in as name
-	} else if p.tok == LNAME {
-		name = p.sym_
-		p.next()
-	} else {
-		p.syntax_error("expecting name")
-		p.advance('.', ';', '}')
-		name = new(Sym)
-	}
-
-	if p.got('.') {
-		// LNAME '.' sym
-		s := p.sym()
-
-		var pkg *Pkg
-		if name.Def == nil || name.Def.Op != OPACK {
-			Yyerror("%v is not a package", name)
-			pkg = localpkg
-		} else {
-			name.Def.Used = true
-			pkg = name.Def.Name.Pkg
-		}
-		return restrictlookup(s.Name, pkg)
-	}
-
-	// LNAME
-	if n := oldname(name); n.Name != nil && n.Name.Pack != nil {
-		n.Name.Pack.Used = true
-	}
-	return name
-}
-
-func (p *parser) embed(sym *Sym) *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("embed")()
-	}
-
-	pkgname := p.packname(sym)
-	return embedded(pkgname, localpkg)
-}
-
-// MethodSpec        = MethodName Signature | InterfaceTypeName .
-// MethodName        = identifier .
-// InterfaceTypeName = TypeName .
-func (p *parser) interfacedcl() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("interfacedcl")()
-	}
-
-	switch p.tok {
-	case LNAME:
-		sym := p.sym_
-		p.next()
-
-		// accept potential name list but complain
-		hasNameList := false
-		for p.got(',') {
-			p.sym()
-			hasNameList = true
-		}
-		if hasNameList {
-			p.syntax_error("name list not allowed in interface type")
-			// already progressed, no need to advance
-		}
-
-		if p.tok != '(' {
-			// packname
-			pname := p.packname(sym)
-			return Nod(ODCLFIELD, nil, oldname(pname))
-		}
-
-		// MethodName Signature
-		mname := newname(sym)
-		sig := p.signature(fakethis())
-
-		meth := Nod(ODCLFIELD, mname, sig)
-		ifacedcl(meth)
-		return meth
-
-	case '@', '?':
-		// MethodName Signature
-		//
-		// We arrive here when parsing an interface type declared inside
-		// an exported and inlineable function and the interface declares
-		// unexported methods (which are then package-qualified).
-		//
-		// Since the compiler always flattens embedded interfaces, we
-		// will never see an embedded package-qualified interface in export
-		// data; i.e., when we reach here we know it must be a method.
-		//
-		// See also issue 14164.
-		mname := newname(p.sym())
-		sig := p.signature(fakethis())
-
-		meth := Nod(ODCLFIELD, mname, sig)
-		ifacedcl(meth)
-		return meth
-
-	case '(':
-		p.next()
-		pname := p.packname(nil)
-		p.want(')')
-		n := Nod(ODCLFIELD, nil, oldname(pname))
-		Yyerror("cannot parenthesize embedded type")
-		return n
-
-	default:
-		p.syntax_error("")
-		p.advance(';', '}')
-		return nil
-	}
-}
-
-// param parses and returns a function parameter list entry which may be
-// a parameter name and type pair (name, typ), a single type (nil, typ),
-// or a single name (name, nil). In the last case, the name may still be
-// a type name. The result is (nil, nil) in case of a syntax error.
-//
-// [ParameterName] Type
-func (p *parser) param() (name *Sym, typ *Node) {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("param")()
-	}
-
-	switch p.tok {
-	case LNAME, '@', '?':
-		name = p.sym() // nil if p.tok == '?' (importing only)
-		switch p.tok {
-		case LCOMM, LFUNC, '[', LCHAN, LMAP, LSTRUCT, LINTERFACE, '*', LNAME, '@', '?', '(':
-			// sym name_or_type
-			typ = p.ntype()
-
-		case LDDD:
-			// sym dotdotdot
-			typ = p.dotdotdot()
-
-		default:
-			// name_or_type
-			if p.got('.') {
-				// a qualified name cannot be a parameter name
-				typ = p.new_dotname(mkname(name))
-				name = nil
-			}
-		}
-
-	case LDDD:
-		// dotdotdot
-		typ = p.dotdotdot()
-
-	case LCOMM, LFUNC, '[', LCHAN, LMAP, LSTRUCT, LINTERFACE, '*', '(':
-		// name_or_type
-		typ = p.ntype()
-
-	default:
-		p.syntax_error("expecting )")
-		p.advance(',', ')')
-	}
-
-	return
-}
-
-// Parameters    = "(" [ ParameterList [ "," ] ] ")" .
-// ParameterList = ParameterDecl { "," ParameterDecl } .
-// ParameterDecl = [ IdentifierList ] [ "..." ] Type .
-func (p *parser) param_list(dddOk bool) []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("param_list")()
-	}
-
-	type param struct {
-		name *Sym
-		typ  *Node
-	}
-	var params []param
-	var named int // number of parameters that have a name and type
-
-	p.want('(')
-	for p.tok != EOF && p.tok != ')' {
-		name, typ := p.param()
-		params = append(params, param{name, typ})
-		if name != nil && typ != nil {
-			named++
-		}
-		if !p.ocomma(')') {
-			break
-		}
-	}
-	p.want(')')
-	// 0 <= named <= len(params)
-
-	// There are 3 cases:
-	//
-	// 1) named == 0:
-	//    No parameter list entry has both a name and a type; i.e. there are only
-	//    unnamed parameters. Any name must be a type name; they are "converted"
-	//    to types when creating the final parameter list.
-	//    In case of a syntax error, there is neither a name nor a type.
-	//    Nil checks take care of this.
-	//
-	// 2) named == len(names):
-	//    All parameter list entries have both a name and a type.
-	//
-	// 3) Otherwise:
-	if named != 0 && named != len(params) {
-		// Some parameter list entries have both a name and a type:
-		// Distribute types backwards and check that there are no
-		// mixed named and unnamed parameters.
-		var T *Node // type T in a parameter sequence: a, b, c T
-		for i := len(params) - 1; i >= 0; i-- {
-			p := &params[i]
-			if t := p.typ; t != nil {
-				// explicit type: use type for earlier parameters
-				T = t
-				// an explicitly typed entry must have a name
-				// TODO(gri) remove extra importpkg == nil check below
-				//           after switch to binary eport format
-				// Exported inlined function bodies containing function
-				// literals may print parameter names as '?' resulting
-				// in nil *Sym and thus nil names. Don't report an error
-				// in this case.
-				if p.name == nil && importpkg == nil {
-					T = nil // error
-				}
-			} else {
-				// no explicit type: use type of next parameter
-				p.typ = T
-			}
-			if T == nil {
-				Yyerror("mixed named and unnamed function parameters")
-				break
-			}
-		}
-		// Unless there was an error, now all parameter entries have a type.
-	}
-
-	// create final parameter list
-	list := make([]*Node, len(params))
-	for i, p := range params {
-		// create dcl node
-		var name, typ *Node
-		if p.typ != nil {
-			typ = p.typ
-			if p.name != nil {
-				// name must be a parameter name
-				name = newname(p.name)
-			}
-		} else if p.name != nil {
-			// p.name must be a type name (or nil in case of syntax error)
-			typ = mkname(p.name)
-		}
-		n := Nod(ODCLFIELD, name, typ)
-
-		// rewrite ...T parameter
-		if typ != nil && typ.Op == ODDD {
-			if !dddOk {
-				Yyerror("cannot use ... in receiver or result parameter list")
-			} else if i+1 < len(params) {
-				Yyerror("can only use ... with final parameter in list")
-			}
-			typ.Op = OTARRAY
-			typ.Right = typ.Left
-			typ.Left = nil
-			n.Isddd = true
-			if n.Left != nil {
-				n.Left.Isddd = true
-			}
-		}
-
-		list[i] = n
-	}
-
-	return list
-}
-
-var missing_stmt = Nod(OXXX, nil, nil)
-
-// Statement =
-// 	Declaration | LabeledStmt | SimpleStmt |
-// 	GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
-// 	FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
-// 	DeferStmt .
-//
-// stmt may return missing_stmt.
-func (p *parser) stmt() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("stmt")()
-	}
-
-	switch p.tok {
-	case '{':
-		return p.compound_stmt()
-
-	case LVAR, LCONST, LTYPE:
-		return liststmt(p.common_dcl())
-
-	case LNAME, '@', '?', LLITERAL, LFUNC, '(', // operands
-		'[', LSTRUCT, LMAP, LCHAN, LINTERFACE, // composite types
-		'+', '-', '*', '&', '^', LCOMM, '!': // unary operators
-		return p.simple_stmt(true, false)
-
-	case LFOR:
-		return p.for_stmt()
-
-	case LSWITCH:
-		return p.switch_stmt()
-
-	case LSELECT:
-		return p.select_stmt()
-
-	case LIF:
-		return p.if_stmt()
-
-	case LFALL:
-		p.next()
-		// will be converted to OFALL
-		stmt := Nod(OXFALL, nil, nil)
-		stmt.Xoffset = int64(block)
-		return stmt
-
-	case LBREAK:
-		p.next()
-		return Nod(OBREAK, p.onew_name(), nil)
-
-	case LCONTINUE:
-		p.next()
-		return Nod(OCONTINUE, p.onew_name(), nil)
-
-	case LGO:
-		p.next()
-		return Nod(OPROC, p.pseudocall(), nil)
-
-	case LDEFER:
-		p.next()
-		return Nod(ODEFER, p.pseudocall(), nil)
-
-	case LGOTO:
-		p.next()
-		stmt := Nod(OGOTO, p.new_name(p.sym()), nil)
-		stmt.Sym = dclstack // context, for goto restrictions
-		return stmt
-
-	case LRETURN:
-		p.next()
-		var results []*Node
-		if p.tok != ';' && p.tok != '}' {
-			results = p.expr_list()
-		}
-
-		stmt := Nod(ORETURN, nil, nil)
-		stmt.List.Set(results)
-		if stmt.List.Len() == 0 && Curfn != nil {
-			for _, ln := range Curfn.Func.Dcl {
-				if ln.Class == PPARAM {
-					continue
-				}
-				if ln.Class != PPARAMOUT {
-					break
-				}
-				if ln.Sym.Def != ln {
-					Yyerror("%s is shadowed during return", ln.Sym.Name)
-				}
-			}
-		}
-
-		return stmt
-
-	case ';':
-		return nil
-
-	default:
-		return missing_stmt
-	}
-}
-
-// StatementList = { Statement ";" } .
-func (p *parser) stmt_list() (l []*Node) {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("stmt_list")()
-	}
-
-	for p.tok != EOF && p.tok != '}' && p.tok != LCASE && p.tok != LDEFAULT {
-		s := p.stmt()
-		if s == missing_stmt {
-			break
-		}
-		if s == nil {
-		} else if s.Op == OBLOCK && s.Ninit.Len() == 0 {
-			l = append(l, s.List.Slice()...)
-		} else {
-			l = append(l, s)
-		}
-		// customized version of osemi:
-		// ';' is optional before a closing ')' or '}'
-		if p.tok == ')' || p.tok == '}' {
-			continue
-		}
-		if !p.got(';') {
-			p.syntax_error("at end of statement")
-			p.advance(';', '}')
-		}
-	}
-	return
-}
-
-// IdentifierList = identifier { "," identifier } .
-//
-// If first != nil we have the first symbol already.
-func (p *parser) new_name_list(first *Sym) []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("new_name_list")()
-	}
-
-	if first == nil {
-		first = p.sym() // may still be nil
-	}
-	var l []*Node
-	n := p.new_name(first)
-	if n != nil {
-		l = append(l, n)
-	}
-	for p.got(',') {
-		n = p.new_name(p.sym())
-		if n != nil {
-			l = append(l, n)
-		}
-	}
-	return l
-}
-
-// IdentifierList = identifier { "," identifier } .
-func (p *parser) dcl_name_list() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("dcl_name_list")()
-	}
-
-	s := []*Node{p.dcl_name()}
-	for p.got(',') {
-		s = append(s, p.dcl_name())
-	}
-	return s
-}
-
-// ExpressionList = Expression { "," Expression } .
-func (p *parser) expr_list() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("expr_list")()
-	}
-
-	l := []*Node{p.expr()}
-	for p.got(',') {
-		l = append(l, p.expr())
-	}
-	return l
-}
-
-// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
-func (p *parser) arg_list() (l []*Node, ddd bool) {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("arg_list")()
-	}
-
-	p.want('(')
-	p.xnest++
-
-	for p.tok != EOF && p.tok != ')' && !ddd {
-		l = append(l, p.expr()) // expr_or_type
-		ddd = p.got(LDDD)
-		if !p.ocomma(')') {
-			break
-		}
-	}
-
-	p.xnest--
-	p.want(')')
-
-	return
-}
-
-// osemi parses an optional semicolon.
-func (p *parser) osemi(follow int32) bool {
-	switch p.tok {
-	case ';':
-		p.next()
-		return true
-
-	case ')', '}':
-		// semicolon is optional before ) or }
-		return true
-	}
-
-	p.syntax_error("expecting semicolon, newline, or " + tokstring(follow))
-	p.advance(follow)
-	return false
-}
-
-// ocomma parses an optional comma.
-func (p *parser) ocomma(follow int32) bool {
-	switch p.tok {
-	case ',':
-		p.next()
-		return true
-
-	case ')', '}':
-		// comma is optional before ) or }
-		return true
-	}
-
-	p.syntax_error("expecting comma or " + tokstring(follow))
-	p.advance(follow)
-	return false
-}
-
-// ----------------------------------------------------------------------------
-// Importing packages
-
-func (p *parser) import_error() {
-	p.syntax_error("in export data of imported package")
-	p.next()
-}
-
-// The methods below reflect a 1:1 translation of the original (and now defunct)
-// go.y yacc productions. They could be simplified significantly and also use better
-// variable names. However, we will be able to delete them once we enable the
-// new export format by default, so it's not worth the effort (issue 13241).
-
-func (p *parser) hidden_importsym() *Sym {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_importsym")()
-	}
-
-	p.want('@')
-	var s2 Val
-	if p.tok == LLITERAL {
-		s2 = p.val
-		p.next()
-	} else {
-		p.import_error()
-	}
-	p.want('.')
-
-	switch p.tok {
-	case LNAME:
-		s4 := p.sym_
-		p.next()
-
-		var p *Pkg
-
-		if s2.U.(string) == "" {
-			p = importpkg
-		} else {
-			if isbadimport(s2.U.(string)) {
-				errorexit()
-			}
-			p = mkpkg(s2.U.(string))
-		}
-		return Pkglookup(s4.Name, p)
-
-	case '?':
-		p.next()
-
-		var p *Pkg
-
-		if s2.U.(string) == "" {
-			p = importpkg
-		} else {
-			if isbadimport(s2.U.(string)) {
-				errorexit()
-			}
-			p = mkpkg(s2.U.(string))
-		}
-		return Pkglookup("?", p)
-
-	default:
-		p.import_error()
-		return nil
-	}
-}
-
-func (p *parser) ohidden_funarg_list() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("ohidden_funarg_list")()
-	}
-
-	var ss []*Node
-	if p.tok != ')' {
-		ss = p.hidden_funarg_list()
-	}
-	return ss
-}
-
-func (p *parser) ohidden_structdcl_list() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("ohidden_structdcl_list")()
-	}
-
-	var ss []*Node
-	if p.tok != '}' {
-		ss = p.hidden_structdcl_list()
-	}
-	return ss
-}
-
-func (p *parser) ohidden_interfacedcl_list() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("ohidden_interfacedcl_list")()
-	}
-
-	var ss []*Node
-	if p.tok != '}' {
-		ss = p.hidden_interfacedcl_list()
-	}
-	return ss
-}
-
-// import syntax from package header
-func (p *parser) hidden_import() {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_import")()
-	}
-
-	switch p.tok {
-	case LIMPORT:
-		// LIMPORT LNAME LLITERAL ';'
-		p.next()
-		var s2 *Sym
-		if p.tok == LNAME {
-			s2 = p.sym_
-			p.next()
-		} else {
-			p.import_error()
-		}
-		var s3 Val
-		if p.tok == LLITERAL {
-			s3 = p.val
-			p.next()
-		} else {
-			p.import_error()
-		}
-		p.want(';')
-
-		importimport(s2, s3.U.(string))
-
-	case LVAR:
-		// LVAR hidden_pkg_importsym hidden_type ';'
-		p.next()
-		s2 := p.hidden_pkg_importsym()
-		s3 := p.hidden_type()
-		p.want(';')
-
-		importvar(s2, s3)
-
-	case LCONST:
-		// LCONST hidden_pkg_importsym '=' hidden_constant ';'
-		// LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';'
-		p.next()
-		s2 := p.hidden_pkg_importsym()
-		var s3 *Type = Types[TIDEAL]
-		if p.tok != '=' {
-			s3 = p.hidden_type()
-		}
-		p.want('=')
-		s4 := p.hidden_constant()
-		p.want(';')
-
-		importconst(s2, s3, s4)
-
-	case LTYPE:
-		// LTYPE hidden_pkgtype hidden_type ';'
-		p.next()
-		s2 := p.hidden_pkgtype()
-		s3 := p.hidden_type()
-		p.want(';')
-
-		importtype(s2, s3)
-
-	case LFUNC:
-		// LFUNC hidden_fndcl fnbody ';'
-		p.next()
-		s2 := p.hidden_fndcl()
-		s3 := p.fnbody()
-		p.want(';')
-
-		if s2 == nil {
-			dclcontext = PEXTERN // since we skip the funcbody below
-			return
-		}
-
-		s2.Func.Inl.Set(s3)
-
-		funcbody(s2)
-		importlist = append(importlist, s2)
-
-		if Debug['E'] > 0 {
-			fmt.Printf("import [%q] func %v \n", importpkg.Path, s2)
-			if Debug['m'] > 2 && s2.Func.Inl.Len() != 0 {
-				fmt.Printf("inl body:%v\n", s2.Func.Inl)
-			}
-		}
-
-	default:
-		p.import_error()
-	}
-}
-
-func (p *parser) hidden_pkg_importsym() *Sym {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_pkg_importsym")()
-	}
-
-	s := p.hidden_importsym()
-	p.structpkg = s.Pkg
-	return s
-}
-
-func (p *parser) hidden_pkgtype() *Type {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_pkgtype")()
-	}
-
-	return pkgtype(p.hidden_pkg_importsym())
-}
-
-// ----------------------------------------------------------------------------
-// Importing types
-
-func (p *parser) hidden_type() *Type {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_type")()
-	}
-
-	switch p.tok {
-	default:
-		return p.hidden_type_misc()
-	case LCOMM:
-		return p.hidden_type_recv_chan()
-	case LFUNC:
-		return p.hidden_type_func()
-	}
-}
-
-func (p *parser) hidden_type_non_recv_chan() *Type {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_type_non_recv_chan")()
-	}
-
-	switch p.tok {
-	default:
-		return p.hidden_type_misc()
-	case LFUNC:
-		return p.hidden_type_func()
-	}
-}
-
-func (p *parser) hidden_type_misc() *Type {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_type_misc")()
-	}
-
-	switch p.tok {
-	case '@':
-		// hidden_importsym
-		s1 := p.hidden_importsym()
-		return pkgtype(s1)
-
-	case LNAME:
-		// LNAME
-		s1 := p.sym_
-		p.next()
-
-		// predefined name like uint8
-		s1 = Pkglookup(s1.Name, builtinpkg)
-		if s1.Def == nil || s1.Def.Op != OTYPE {
-			Yyerror("%s is not a type", s1.Name)
-			return nil
-		} else {
-			return s1.Def.Type
-		}
-
-	case '[':
-		// '[' ']' hidden_type
-		// '[' LLITERAL ']' hidden_type
-		p.next()
-		var s2 *Node
-		if p.tok == LLITERAL {
-			s2 = nodlit(p.val)
-			p.next()
-		}
-		p.want(']')
-		s4 := p.hidden_type()
-
-		return aindex(s2, s4)
-
-	case LMAP:
-		// LMAP '[' hidden_type ']' hidden_type
-		p.next()
-		p.want('[')
-		s3 := p.hidden_type()
-		p.want(']')
-		s5 := p.hidden_type()
-
-		return typMap(s3, s5)
-
-	case LSTRUCT:
-		// LSTRUCT '{' ohidden_structdcl_list '}'
-		p.next()
-		p.want('{')
-		s3 := p.ohidden_structdcl_list()
-		p.want('}')
-
-		return tostruct(s3)
-
-	case LINTERFACE:
-		// LINTERFACE '{' ohidden_interfacedcl_list '}'
-		p.next()
-		p.want('{')
-		s3 := p.ohidden_interfacedcl_list()
-		p.want('}')
-
-		return tointerface(s3)
-
-	case '*':
-		// '*' hidden_type
-		p.next()
-		s2 := p.hidden_type()
-		return Ptrto(s2)
-
-	case LCHAN:
-		p.next()
-		switch p.tok {
-		default:
-			// LCHAN hidden_type_non_recv_chan
-			s2 := p.hidden_type_non_recv_chan()
-			ss := typChan(s2, Cboth)
-			return ss
-
-		case '(':
-			// LCHAN '(' hidden_type_recv_chan ')'
-			p.next()
-			s3 := p.hidden_type_recv_chan()
-			p.want(')')
-			ss := typChan(s3, Cboth)
-			return ss
-
-		case LCOMM:
-			// LCHAN hidden_type
-			p.next()
-			s3 := p.hidden_type()
-			ss := typChan(s3, Csend)
-			return ss
-		}
-
-	default:
-		p.import_error()
-		return nil
-	}
-}
-
-func (p *parser) hidden_type_recv_chan() *Type {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_type_recv_chan")()
-	}
-
-	p.want(LCOMM)
-	p.want(LCHAN)
-	s3 := p.hidden_type()
-
-	ss := typChan(s3, Crecv)
-	return ss
-}
-
-func (p *parser) hidden_type_func() *Type {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_type_func")()
-	}
-
-	p.want(LFUNC)
-	p.want('(')
-	s3 := p.ohidden_funarg_list()
-	p.want(')')
-	s5 := p.ohidden_funres()
-
-	return functype(nil, s3, s5)
-}
-
-func (p *parser) hidden_funarg() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_funarg")()
-	}
-
-	s1 := p.sym()
-	switch p.tok {
-	default:
-		s2 := p.hidden_type()
-		s3 := p.oliteral()
-
-		ss := Nod(ODCLFIELD, nil, typenod(s2))
-		if s1 != nil {
-			ss.Left = newname(s1)
-		}
-		ss.SetVal(s3)
-		return ss
-
-	case LDDD:
-		p.next()
-		s3 := p.hidden_type()
-		s4 := p.oliteral()
-
-		t := typSlice(s3)
-
-		ss := Nod(ODCLFIELD, nil, typenod(t))
-		if s1 != nil {
-			ss.Left = newname(s1)
-		}
-		ss.Isddd = true
-		ss.SetVal(s4)
-
-		return ss
-	}
-}
-
-func (p *parser) hidden_structdcl() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_structdcl")()
-	}
-
-	s1 := p.sym()
-	s2 := p.hidden_type()
-	s3 := p.oliteral()
-
-	var ss *Node
-	if s1 != nil && s1.Name != "?" {
-		ss = Nod(ODCLFIELD, newname(s1), typenod(s2))
-		ss.SetVal(s3)
-	} else {
-		s := s2.Sym
-		if s == nil && s2.IsPtr() {
-			s = s2.Elem().Sym
-		}
-		pkg := importpkg
-		if s1 != nil {
-			pkg = s1.Pkg
-		}
-		ss = embedded(s, pkg)
-		ss.Right = typenod(s2)
-		ss.SetVal(s3)
-	}
-
-	return ss
-}
-
-func (p *parser) hidden_interfacedcl() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_interfacedcl")()
-	}
-
-	// The original (now defunct) grammar in go.y accepted both a method
-	// or an (embedded) type:
-	//
-	// hidden_interfacedcl:
-	// 	sym '(' ohidden_funarg_list ')' ohidden_funres
-	// 	{
-	// 		$$ = Nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5)));
-	// 	}
-	// |	hidden_type
-	// 	{
-	// 		$$ = Nod(ODCLFIELD, nil, typenod($1));
-	// 	}
-	//
-	// But the current textual export code only exports (inlined) methods,
-	// even if the methods came from embedded interfaces. Furthermore, in
-	// the original grammar, hidden_type may also start with a sym (LNAME
-	// or '@'), complicating matters further. Since we never have embedded
-	// types, only parse methods here.
-
-	s1 := p.sym()
-	p.want('(')
-	s3 := p.ohidden_funarg_list()
-	p.want(')')
-	s5 := p.ohidden_funres()
-
-	return Nod(ODCLFIELD, newname(s1), typenod(functype(fakethis(), s3, s5)))
-}
-
-func (p *parser) ohidden_funres() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("ohidden_funres")()
-	}
-
-	switch p.tok {
-	default:
-		return nil
-
-	case '(', '@', LNAME, '[', LMAP, LSTRUCT, LINTERFACE, '*', LCHAN, LCOMM, LFUNC:
-		return p.hidden_funres()
-	}
-}
-
-func (p *parser) hidden_funres() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_funres")()
-	}
-
-	switch p.tok {
-	case '(':
-		p.next()
-		s2 := p.ohidden_funarg_list()
-		p.want(')')
-		return s2
-
-	default:
-		s1 := p.hidden_type()
-		return []*Node{Nod(ODCLFIELD, nil, typenod(s1))}
-	}
-}
-
-// ----------------------------------------------------------------------------
-// Importing constants
-
-func (p *parser) hidden_literal() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_literal")()
-	}
-
-	switch p.tok {
-	case LLITERAL:
-		ss := nodlit(p.val)
-		p.next()
-		return ss
-
-	case '-':
-		p.next()
-		if p.tok == LLITERAL {
-			ss := nodlit(p.val)
-			p.next()
-			switch u := ss.Val().U.(type) {
-			case *Mpint:
-				u.Neg()
-			case *Mpflt:
-				u.Neg()
-			case *Mpcplx:
-				u.Real.Neg()
-				u.Imag.Neg()
-			default:
-				Yyerror("bad negated constant")
-			}
-			return ss
-		} else {
-			p.import_error()
-			return nil
-		}
-
-	case LNAME, '@', '?':
-		s1 := p.sym()
-		ss := oldname(Pkglookup(s1.Name, builtinpkg))
-		if ss.Op != OLITERAL {
-			Yyerror("bad constant %v", ss.Sym)
-		}
-		return ss
-
-	default:
-		p.import_error()
-		return nil
-	}
-}
-
-func (p *parser) hidden_constant() *Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_constant")()
-	}
-
-	switch p.tok {
-	default:
-		return p.hidden_literal()
-	case '(':
-		p.next()
-		s2 := p.hidden_literal()
-		p.want('+')
-		s4 := p.hidden_literal()
-		p.want(')')
-
-		if s2.Val().Ctype() == CTRUNE && s4.Val().Ctype() == CTINT {
-			ss := s2
-			s2.Val().U.(*Mpint).Add(s4.Val().U.(*Mpint))
-			return ss
-		}
-		s4.Val().U.(*Mpcplx).Real = s4.Val().U.(*Mpcplx).Imag
-		s4.Val().U.(*Mpcplx).Imag.SetFloat64(0.0)
-		return nodcplxlit(s2.Val(), s4.Val())
-	}
-}
-
-func (p *parser) hidden_import_list() {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_import_list")()
-	}
-
-	for p.tok != '$' {
-		p.hidden_import()
-	}
-}
-
-func (p *parser) hidden_funarg_list() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_funarg_list")()
-	}
-
-	s1 := p.hidden_funarg()
-	ss := []*Node{s1}
-	for p.got(',') {
-		s3 := p.hidden_funarg()
-		ss = append(ss, s3)
-	}
-	return ss
-}
-
-func (p *parser) hidden_structdcl_list() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_structdcl_list")()
-	}
-
-	s1 := p.hidden_structdcl()
-	ss := []*Node{s1}
-	for p.got(';') {
-		s3 := p.hidden_structdcl()
-		ss = append(ss, s3)
-	}
-	return ss
-}
-
-func (p *parser) hidden_interfacedcl_list() []*Node {
-	if trace && Debug['x'] != 0 {
-		defer p.trace("hidden_interfacedcl_list")()
-	}
-
-	s1 := p.hidden_interfacedcl()
-	ss := []*Node{s1}
-	for p.got(';') {
-		s3 := p.hidden_interfacedcl()
-		ss = append(ss, s3)
-	}
-	return ss
-}
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index da2e675..acea790 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -18,14 +18,12 @@ import (
 var makefuncdatasym_nsym int
 
 func makefuncdatasym(nameprefix string, funcdatakind int64) *Sym {
-	var nod Node
-
-	sym := LookupN(nameprefix, makefuncdatasym_nsym)
+	sym := lookupN(nameprefix, makefuncdatasym_nsym)
 	makefuncdatasym_nsym++
 	pnod := newname(sym)
 	pnod.Class = PEXTERN
-	Nodconst(&nod, Types[TINT32], funcdatakind)
-	Thearch.Gins(obj.AFUNCDATA, &nod, pnod)
+	p := Gins(obj.AFUNCDATA, nil, pnod)
+	Addrconst(&p.From, funcdatakind)
 	return sym
 }
 
@@ -90,16 +88,21 @@ func gvardefx(n *Node, as obj.As) {
 		Fatalf("gvardef nil")
 	}
 	if n.Op != ONAME {
-		Yyerror("gvardef %#v; %v", n.Op, n)
+		yyerror("gvardef %#v; %v", n.Op, n)
 		return
 	}
 
 	switch n.Class {
 	case PAUTO, PPARAM, PPARAMOUT:
+		if !n.Used {
+			Prog(obj.ANOP)
+			return
+		}
+
 		if as == obj.AVARLIVE {
-			Thearch.Gins(as, n, nil)
+			Gins(as, n, nil)
 		} else {
-			Thearch.Gins(as, nil, n)
+			Gins(as, nil, n)
 		}
 	}
 }
@@ -133,7 +136,7 @@ func emitptrargsmap() {
 	if Curfn.Func.Nname.Sym.Name == "_" {
 		return
 	}
-	sym := Lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Func.Nname.Sym.Name))
+	sym := lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Func.Nname.Sym.Name))
 
 	nptr := int(Curfn.Type.ArgWidth() / int64(Widthptr))
 	bv := bvalloc(int32(nptr) * 2)
@@ -144,7 +147,7 @@ func emitptrargsmap() {
 	off := duint32(sym, 0, uint32(nbitmap))
 	off = duint32(sym, off, uint32(bv.n))
 	var xoffset int64
-	if Curfn.Type.Recv() != nil {
+	if Curfn.IsMethod() {
 		xoffset = 0
 		onebitwalktype1(Curfn.Type.Recvs(), &xoffset, bv)
 	}
@@ -154,15 +157,11 @@ func emitptrargsmap() {
 		onebitwalktype1(Curfn.Type.Params(), &xoffset, bv)
 	}
 
-	for j := 0; int32(j) < bv.n; j += 32 {
-		off = duint32(sym, off, bv.b[j/32])
-	}
+	off = dbvec(sym, off, bv)
 	if Curfn.Type.Results().NumFields() > 0 {
 		xoffset = 0
 		onebitwalktype1(Curfn.Type.Results(), &xoffset, bv)
-		for j := 0; int32(j) < bv.n; j += 32 {
-			off = duint32(sym, off, bv.b[j/32])
-		}
+		off = dbvec(sym, off, bv)
 	}
 
 	ggloblsym(sym, int32(off), obj.RODATA|obj.LOCAL)
@@ -217,20 +216,12 @@ func (s byStackVar) Len() int           { return len(s) }
 func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
 func (s byStackVar) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
 
-// stkdelta records the stack offset delta for a node
-// during the compaction of the stack frame to remove
-// unused stack slots.
-var stkdelta = map[*Node]int64{}
+var scratchFpMem *Node
 
-// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from.
-func allocauto(ptxt *obj.Prog) {
+func (s *ssaExport) AllocFrame(f *ssa.Func) {
 	Stksize = 0
 	stkptrsize = 0
 
-	if len(Curfn.Func.Dcl) == 0 {
-		return
-	}
-
 	// Mark the PAUTO's unused.
 	for _, ln := range Curfn.Func.Dcl {
 		if ln.Class == PAUTO {
@@ -238,37 +229,48 @@ func allocauto(ptxt *obj.Prog) {
 		}
 	}
 
-	markautoused(ptxt)
+	for _, l := range f.RegAlloc {
+		if ls, ok := l.(ssa.LocalSlot); ok {
+			ls.N.(*Node).Used = true
+		}
 
-	sort.Sort(byStackVar(Curfn.Func.Dcl))
+	}
 
-	// Unused autos are at the end, chop 'em off.
-	n := Curfn.Func.Dcl[0]
-	if n.Class == PAUTO && n.Op == ONAME && !n.Used {
-		// No locals used at all
-		Curfn.Func.Dcl = nil
+	scratchUsed := false
+	for _, b := range f.Blocks {
+		for _, v := range b.Values {
+			switch a := v.Aux.(type) {
+			case *ssa.ArgSymbol:
+				a.Node.(*Node).Used = true
+			case *ssa.AutoSymbol:
+				a.Node.(*Node).Used = true
+			}
 
-		fixautoused(ptxt)
-		return
+			if !scratchUsed {
+				scratchUsed = v.Op.UsesScratch()
+			}
+		}
 	}
 
-	for i := 1; i < len(Curfn.Func.Dcl); i++ {
-		n = Curfn.Func.Dcl[i]
-		if n.Class == PAUTO && n.Op == ONAME && !n.Used {
-			Curfn.Func.Dcl = Curfn.Func.Dcl[:i]
-			break
-		}
+	if f.Config.NeedsFpScratch {
+		scratchFpMem = temp(Types[TUINT64])
+		scratchFpMem.Used = scratchUsed
 	}
 
-	// Reassign stack offsets of the locals that are still there.
-	var w int64
-	for _, n := range Curfn.Func.Dcl {
-		if n.Class != PAUTO || n.Op != ONAME {
+	sort.Sort(byStackVar(Curfn.Func.Dcl))
+
+	// Reassign stack offsets of the locals that are used.
+	for i, n := range Curfn.Func.Dcl {
+		if n.Op != ONAME || n.Class != PAUTO {
 			continue
 		}
+		if !n.Used {
+			Curfn.Func.Dcl = Curfn.Func.Dcl[:i]
+			break
+		}
 
 		dowidth(n.Type)
-		w = n.Type.Width
+		w := n.Type.Width
 		if w >= Thearch.MAXWIDTH || w < 0 {
 			Fatalf("bad width")
 		}
@@ -277,58 +279,19 @@ func allocauto(ptxt *obj.Prog) {
 		if haspointers(n.Type) {
 			stkptrsize = Stksize
 		}
-		if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
+		if Thearch.LinkArch.InFamily(sys.MIPS, sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
 			Stksize = Rnd(Stksize, int64(Widthptr))
 		}
 		if Stksize >= 1<<31 {
 			setlineno(Curfn)
-			Yyerror("stack frame too large (>2GB)")
+			yyerror("stack frame too large (>2GB)")
 		}
 
-		stkdelta[n] = -Stksize - n.Xoffset
+		n.Xoffset = -Stksize
 	}
 
 	Stksize = Rnd(Stksize, int64(Widthreg))
 	stkptrsize = Rnd(stkptrsize, int64(Widthreg))
-
-	fixautoused(ptxt)
-
-	// The debug information needs accurate offsets on the symbols.
-	for _, ln := range Curfn.Func.Dcl {
-		if ln.Class != PAUTO || ln.Op != ONAME {
-			continue
-		}
-		ln.Xoffset += stkdelta[ln]
-		delete(stkdelta, ln)
-	}
-}
-
-func Cgen_checknil(n *Node) {
-	if Disable_checknil != 0 {
-		return
-	}
-
-	// Ideally we wouldn't see any integer types here, but we do.
-	if n.Type == nil || (!n.Type.IsPtr() && !n.Type.IsInteger() && n.Type.Etype != TUNSAFEPTR) {
-		Dump("checknil", n)
-		Fatalf("bad checknil")
-	}
-
-	// Most architectures require that the address to be checked is
-	// in a register (it could be in memory).
-	needsReg := !Thearch.LinkArch.InFamily(sys.AMD64, sys.I386)
-
-	// Move the address to be checked into a register if necessary.
-	if (needsReg && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL {
-		var reg Node
-		Regalloc(&reg, Types[Tptr], n)
-		Cgen(n, &reg)
-		Thearch.Gins(obj.ACHECKNIL, &reg, nil)
-		Regfree(&reg)
-		return
-	}
-
-	Thearch.Gins(obj.ACHECKNIL, n, nil)
 }
 
 func compile(fn *Node) {
@@ -336,14 +299,16 @@ func compile(fn *Node) {
 		Newproc = Sysfunc("newproc")
 		Deferproc = Sysfunc("deferproc")
 		Deferreturn = Sysfunc("deferreturn")
-		Panicindex = Sysfunc("panicindex")
+		panicindex = Sysfunc("panicindex")
 		panicslice = Sysfunc("panicslice")
 		panicdivide = Sysfunc("panicdivide")
-		throwreturn = Sysfunc("throwreturn")
 		growslice = Sysfunc("growslice")
-		writebarrierptr = Sysfunc("writebarrierptr")
-		typedmemmove = Sysfunc("typedmemmove")
 		panicdottype = Sysfunc("panicdottype")
+		panicnildottype = Sysfunc("panicnildottype")
+		assertE2I = Sysfunc("assertE2I")
+		assertE2I2 = Sysfunc("assertE2I2")
+		assertI2I = Sysfunc("assertI2I")
+		assertI2I2 = Sysfunc("assertI2I2")
 	}
 
 	defer func(lno int32) {
@@ -355,29 +320,23 @@ func compile(fn *Node) {
 
 	if fn.Nbody.Len() == 0 {
 		if pure_go || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") {
-			Yyerror("missing function body for %q", fn.Func.Nname.Sym.Name)
+			yyerror("missing function body for %q", fn.Func.Nname.Sym.Name)
 			return
 		}
 
-		if Debug['A'] != 0 {
-			return
-		}
 		emitptrargsmap()
 		return
 	}
 
 	saveerrors()
 
-	// set up domain for labels
-	clearlabels()
-
 	if Curfn.Type.FuncType().Outnamed {
 		// add clearing of the output parameters
 		for _, t := range Curfn.Type.Results().Fields().Slice() {
 			if t.Nname != nil {
-				n := Nod(OAS, t.Nname, nil)
+				n := nod(OAS, t.Nname, nil)
 				n = typecheck(n, Etop)
-				Curfn.Nbody.Set(append([]*Node{n}, Curfn.Nbody.Slice()...))
+				Curfn.Nbody.Prepend(n)
 			}
 		}
 	}
@@ -400,27 +359,20 @@ func compile(fn *Node) {
 	}
 
 	// Build an SSA backend function.
-	var ssafn *ssa.Func
-	if shouldssa(Curfn) {
-		ssafn = buildssa(Curfn)
+	ssafn := buildssa(Curfn)
+	if nerrors != 0 {
+		return
 	}
 
-	continpc = nil
-	breakpc = nil
-
-	pl := newplist()
-	pl.Name = Linksym(Curfn.Func.Nname.Sym)
+	newplist()
 
 	setlineno(Curfn)
 
-	var nod1 Node
-	Nodconst(&nod1, Types[TINT32], 0)
 	nam := Curfn.Func.Nname
 	if isblank(nam) {
 		nam = nil
 	}
-	ptxt := Thearch.Gins(obj.ATEXT, nam, &nod1)
-	Afunclit(&ptxt.From, Curfn.Func.Nname)
+	ptxt := Gins(obj.ATEXT, nam, nil)
 	ptxt.From3 = new(obj.Addr)
 	if fn.Func.Dupok {
 		ptxt.From3.Offset |= obj.DUPOK
@@ -438,7 +390,7 @@ func compile(fn *Node) {
 		ptxt.From3.Offset |= obj.REFLECTMETHOD
 	}
 	if fn.Func.Pragma&Systemstack != 0 {
-		ptxt.From.Sym.Cfunc = true
+		ptxt.From.Sym.Set(obj.AttrCFunc, true)
 	}
 
 	// Clumsy but important.
@@ -450,8 +402,6 @@ func compile(fn *Node) {
 		}
 	}
 
-	ginit()
-
 	gcargs := makefuncdatasym("gcargs·", obj.FUNCDATA_ArgsPointerMaps)
 	gclocals := makefuncdatasym("gclocals·", obj.FUNCDATA_LocalsPointerMaps)
 
@@ -471,19 +421,24 @@ func compile(fn *Node) {
 			continue
 		}
 		switch n.Class {
-		case PAUTO, PPARAM, PPARAMOUT:
-			Nodconst(&nod1, Types[TUINTPTR], n.Type.Width)
-			p := Thearch.Gins(obj.ATYPE, n, &nod1)
-			p.From.Gotype = Linksym(ngotype(n))
+		case PAUTO:
+			if !n.Used {
+				continue
+			}
+			fallthrough
+		case PPARAM, PPARAMOUT:
+			// The symbol is excluded later from debugging info if its name begins ".autotmp_", but the type is still necessary.
+			// See bugs #17644 and #17830 and cmd/internal/dwarf/dwarf.go
+			p := Gins(obj.ATYPE, n, nil)
+			p.From.Sym = obj.Linklookup(Ctxt, n.Sym.Name, 0)
+			p.To.Type = obj.TYPE_MEM
+			p.To.Name = obj.NAME_EXTERN
+			p.To.Sym = Linksym(ngotype(n))
 		}
 	}
 
-	if ssafn != nil {
-		genssa(ssafn, ptxt, gcargs, gclocals)
-		ssafn.Free()
-	} else {
-		genlegacy(ptxt, gcargs, gclocals)
-	}
+	genssa(ssafn, ptxt, gcargs, gclocals)
+	ssafn.Free()
 }
 
 type symByName []*Sym
@@ -491,70 +446,3 @@ type symByName []*Sym
 func (a symByName) Len() int           { return len(a) }
 func (a symByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
 func (a symByName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-
-// genlegacy compiles Curfn using the legacy non-SSA code generator.
-func genlegacy(ptxt *obj.Prog, gcargs, gclocals *Sym) {
-	Genlist(Curfn.Func.Enter)
-	Genlist(Curfn.Nbody)
-	gclean()
-	checklabels()
-	if nerrors != 0 {
-		return
-	}
-	if Curfn.Func.Endlineno != 0 {
-		lineno = Curfn.Func.Endlineno
-	}
-
-	if Curfn.Type.Results().NumFields() != 0 {
-		Ginscall(throwreturn, 0)
-	}
-
-	ginit()
-
-	// TODO: Determine when the final cgen_ret can be omitted. Perhaps always?
-	cgen_ret(nil)
-
-	if hasdefer {
-		// deferreturn pretends to have one uintptr argument.
-		// Reserve space for it so stack scanner is happy.
-		if Maxarg < int64(Widthptr) {
-			Maxarg = int64(Widthptr)
-		}
-	}
-
-	gclean()
-	if nerrors != 0 {
-		return
-	}
-
-	Pc.As = obj.ARET // overwrite AEND
-	Pc.Lineno = lineno
-
-	fixjmp(ptxt)
-	if Debug['N'] == 0 || Debug['R'] != 0 || Debug['P'] != 0 {
-		regopt(ptxt)
-		nilopt(ptxt)
-	}
-
-	Thearch.Expandchecks(ptxt)
-
-	allocauto(ptxt)
-
-	setlineno(Curfn)
-	if Stksize+Maxarg > 1<<31 {
-		Yyerror("stack frame too large (>2GB)")
-		return
-	}
-
-	// Emit garbage collection symbols.
-	liveness(Curfn, ptxt, gcargs, gclocals)
-
-	Thearch.Defframe(ptxt)
-
-	if Debug['f'] != 0 {
-		frame(0)
-	}
-
-	// Remove leftover instrumentation from the instruction stream.
-	removevardef(ptxt)
-}
diff --git a/src/cmd/compile/internal/gc/phi.go b/src/cmd/compile/internal/gc/phi.go
new file mode 100644
index 0000000..0d4dbb5
--- /dev/null
+++ b/src/cmd/compile/internal/gc/phi.go
@@ -0,0 +1,521 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gc
+
+import (
+	"cmd/compile/internal/ssa"
+	"container/heap"
+	"fmt"
+)
+
+// This file contains the algorithm to place phi nodes in a function.
+// For small functions, we use Braun, Buchwald, Hack, Leißa, Mallon, and Zwinkau.
+// http://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf
+// For large functions, we use Sreedhar & Gao: A Linear Time Algorithm for Placing Φ-Nodes.
+// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.8.1979&rep=rep1&type=pdf
+
+const smallBlocks = 500
+
+const debugPhi = false
+
+// insertPhis finds all the places in the function where a phi is
+// necessary and inserts them.
+// Uses FwdRef ops to find all uses of variables, and s.defvars to find
+// all definitions.
+// Phi values are inserted, and all FwdRefs are changed to a Copy
+// of the appropriate phi or definition.
+// TODO: make this part of cmd/compile/internal/ssa somehow?
+func (s *state) insertPhis() {
+	if len(s.f.Blocks) <= smallBlocks {
+		sps := simplePhiState{s: s, f: s.f, defvars: s.defvars}
+		sps.insertPhis()
+		return
+	}
+	ps := phiState{s: s, f: s.f, defvars: s.defvars}
+	ps.insertPhis()
+}
+
+type phiState struct {
+	s       *state                 // SSA state
+	f       *ssa.Func              // function to work on
+	defvars []map[*Node]*ssa.Value // defined variables at end of each block
+
+	varnum map[*Node]int32 // variable numbering
+
+	// properties of the dominator tree
+	idom  []*ssa.Block // dominator parents
+	tree  []domBlock   // dominator child+sibling
+	level []int32      // level in dominator tree (0 = root or unreachable, 1 = children of root, ...)
+
+	// scratch locations
+	priq   blockHeap    // priority queue of blocks, higher level (toward leaves) = higher priority
+	q      []*ssa.Block // inner loop queue
+	queued *sparseSet   // has been put in q
+	hasPhi *sparseSet   // has a phi
+	hasDef *sparseSet   // has a write of the variable we're processing
+
+	// miscellaneous
+	placeholder *ssa.Value // dummy value to use as a "not set yet" placeholder.
+}
+
+func (s *phiState) insertPhis() {
+	if debugPhi {
+		fmt.Println(s.f.String())
+	}
+
+	// Find all the variables for which we need to match up reads & writes.
+	// This step prunes any basic-block-only variables from consideration.
+	// Generate a numbering for these variables.
+	s.varnum = map[*Node]int32{}
+	var vars []*Node
+	var vartypes []ssa.Type
+	for _, b := range s.f.Blocks {
+		for _, v := range b.Values {
+			if v.Op != ssa.OpFwdRef {
+				continue
+			}
+			var_ := v.Aux.(*Node)
+
+			// Optimization: look back 1 block for the definition.
+			if len(b.Preds) == 1 {
+				c := b.Preds[0].Block()
+				if w := s.defvars[c.ID][var_]; w != nil {
+					v.Op = ssa.OpCopy
+					v.Aux = nil
+					v.AddArg(w)
+					continue
+				}
+			}
+
+			if _, ok := s.varnum[var_]; ok {
+				continue
+			}
+			s.varnum[var_] = int32(len(vartypes))
+			if debugPhi {
+				fmt.Printf("var%d = %v\n", len(vartypes), var_)
+			}
+			vars = append(vars, var_)
+			vartypes = append(vartypes, v.Type)
+		}
+	}
+
+	if len(vartypes) == 0 {
+		return
+	}
+
+	// Find all definitions of the variables we need to process.
+	// defs[n] contains all the blocks in which variable number n is assigned.
+	defs := make([][]*ssa.Block, len(vartypes))
+	for _, b := range s.f.Blocks {
+		for var_ := range s.defvars[b.ID] { // TODO: encode defvars some other way (explicit ops)? make defvars[n] a slice instead of a map.
+			if n, ok := s.varnum[var_]; ok {
+				defs[n] = append(defs[n], b)
+			}
+		}
+	}
+
+	// Make dominator tree.
+	s.idom = s.f.Idom()
+	s.tree = make([]domBlock, s.f.NumBlocks())
+	for _, b := range s.f.Blocks {
+		p := s.idom[b.ID]
+		if p != nil {
+			s.tree[b.ID].sibling = s.tree[p.ID].firstChild
+			s.tree[p.ID].firstChild = b
+		}
+	}
+	// Compute levels in dominator tree.
+	// With parent pointers we can do a depth-first walk without
+	// any auxiliary storage.
+	s.level = make([]int32, s.f.NumBlocks())
+	b := s.f.Entry
+levels:
+	for {
+		if p := s.idom[b.ID]; p != nil {
+			s.level[b.ID] = s.level[p.ID] + 1
+			if debugPhi {
+				fmt.Printf("level %s = %d\n", b, s.level[b.ID])
+			}
+		}
+		if c := s.tree[b.ID].firstChild; c != nil {
+			b = c
+			continue
+		}
+		for {
+			if c := s.tree[b.ID].sibling; c != nil {
+				b = c
+				continue levels
+			}
+			b = s.idom[b.ID]
+			if b == nil {
+				break levels
+			}
+		}
+	}
+
+	// Allocate scratch locations.
+	s.priq.level = s.level
+	s.q = make([]*ssa.Block, 0, s.f.NumBlocks())
+	s.queued = newSparseSet(s.f.NumBlocks())
+	s.hasPhi = newSparseSet(s.f.NumBlocks())
+	s.hasDef = newSparseSet(s.f.NumBlocks())
+	s.placeholder = s.s.entryNewValue0(ssa.OpUnknown, ssa.TypeInvalid)
+
+	// Generate phi ops for each variable.
+	for n := range vartypes {
+		s.insertVarPhis(n, vars[n], defs[n], vartypes[n])
+	}
+
+	// Resolve FwdRefs to the correct write or phi.
+	s.resolveFwdRefs()
+
+	// Erase variable numbers stored in AuxInt fields of phi ops. They are no longer needed.
+	for _, b := range s.f.Blocks {
+		for _, v := range b.Values {
+			if v.Op == ssa.OpPhi {
+				v.AuxInt = 0
+			}
+		}
+	}
+}
+
+func (s *phiState) insertVarPhis(n int, var_ *Node, defs []*ssa.Block, typ ssa.Type) {
+	priq := &s.priq
+	q := s.q
+	queued := s.queued
+	queued.clear()
+	hasPhi := s.hasPhi
+	hasPhi.clear()
+	hasDef := s.hasDef
+	hasDef.clear()
+
+	// Add defining blocks to priority queue.
+	for _, b := range defs {
+		priq.a = append(priq.a, b)
+		hasDef.add(b.ID)
+		if debugPhi {
+			fmt.Printf("def of var%d in %s\n", n, b)
+		}
+	}
+	heap.Init(priq)
+
+	// Visit blocks defining variable n, from deepest to shallowest.
+	for len(priq.a) > 0 {
+		currentRoot := heap.Pop(priq).(*ssa.Block)
+		if debugPhi {
+			fmt.Printf("currentRoot %s\n", currentRoot)
+		}
+		// Walk subtree below definition.
+		// Skip subtrees we've done in previous iterations.
+		// Find edges exiting tree dominated by definition (the dominance frontier).
+		// Insert phis at target blocks.
+		if queued.contains(currentRoot.ID) {
+			s.s.Fatalf("root already in queue")
+		}
+		q = append(q, currentRoot)
+		queued.add(currentRoot.ID)
+		for len(q) > 0 {
+			b := q[len(q)-1]
+			q = q[:len(q)-1]
+			if debugPhi {
+				fmt.Printf("  processing %s\n", b)
+			}
+
+			for _, e := range b.Succs {
+				c := e.Block()
+				// TODO: if the variable is dead at c, skip it.
+				if s.level[c.ID] > s.level[currentRoot.ID] {
+					// a D-edge, or an edge whose target is in currentRoot's subtree.
+					continue
+				}
+				if !hasPhi.contains(c.ID) {
+					// Add a phi to block c for variable n.
+					hasPhi.add(c.ID)
+					v := c.NewValue0I(currentRoot.Line, ssa.OpPhi, typ, int64(n)) // TODO: line number right?
+					// Note: we store the variable number in the phi's AuxInt field. Used temporarily by phi building.
+					s.s.addNamedValue(var_, v)
+					for i := 0; i < len(c.Preds); i++ {
+						v.AddArg(s.placeholder) // Actual args will be filled in by resolveFwdRefs.
+					}
+					if debugPhi {
+						fmt.Printf("new phi for var%d in %s: %s\n", n, c, v)
+					}
+					if !hasDef.contains(c.ID) {
+						// There's now a new definition of this variable in block c.
+						// Add it to the priority queue to explore.
+						heap.Push(priq, c)
+						hasDef.add(c.ID)
+					}
+				}
+			}
+
+			// Visit children if they have not been visited yet.
+			for c := s.tree[b.ID].firstChild; c != nil; c = s.tree[c.ID].sibling {
+				if !queued.contains(c.ID) {
+					q = append(q, c)
+					queued.add(c.ID)
+				}
+			}
+		}
+	}
+}
+
+// resolveFwdRefs links all FwdRef uses up to their nearest dominating definition.
+func (s *phiState) resolveFwdRefs() {
+	// Do a depth-first walk of the dominator tree, keeping track
+	// of the most-recently-seen value for each variable.
+
+	// Map from variable ID to SSA value at the current point of the walk.
+	values := make([]*ssa.Value, len(s.varnum))
+	for i := range values {
+		values[i] = s.placeholder
+	}
+
+	// Stack of work to do.
+	type stackEntry struct {
+		b *ssa.Block // block to explore
+
+		// variable/value pair to reinstate on exit
+		n int32 // variable ID
+		v *ssa.Value
+
+		// Note: only one of b or n,v will be set.
+	}
+	var stk []stackEntry
+
+	stk = append(stk, stackEntry{b: s.f.Entry})
+	for len(stk) > 0 {
+		work := stk[len(stk)-1]
+		stk = stk[:len(stk)-1]
+
+		b := work.b
+		if b == nil {
+			// On exit from a block, this case will undo any assignments done below.
+			values[work.n] = work.v
+			continue
+		}
+
+		// Process phis as new defs. They come before FwdRefs in this block.
+		for _, v := range b.Values {
+			if v.Op != ssa.OpPhi {
+				continue
+			}
+			n := int32(v.AuxInt)
+			// Remember the old assignment so we can undo it when we exit b.
+			stk = append(stk, stackEntry{n: n, v: values[n]})
+			// Record the new assignment.
+			values[n] = v
+		}
+
+		// Replace a FwdRef op with the current incoming value for its variable.
+		for _, v := range b.Values {
+			if v.Op != ssa.OpFwdRef {
+				continue
+			}
+			n := s.varnum[v.Aux.(*Node)]
+			v.Op = ssa.OpCopy
+			v.Aux = nil
+			v.AddArg(values[n])
+		}
+
+		// Establish values for variables defined in b.
+		for var_, v := range s.defvars[b.ID] {
+			n, ok := s.varnum[var_]
+			if !ok {
+				// some variable not live across a basic block boundary.
+				continue
+			}
+			// Remember the old assignment so we can undo it when we exit b.
+			stk = append(stk, stackEntry{n: n, v: values[n]})
+			// Record the new assignment.
+			values[n] = v
+		}
+
+		// Replace phi args in successors with the current incoming value.
+		for _, e := range b.Succs {
+			c, i := e.Block(), e.Index()
+			for j := len(c.Values) - 1; j >= 0; j-- {
+				v := c.Values[j]
+				if v.Op != ssa.OpPhi {
+					break // All phis will be at the end of the block during phi building.
+				}
+				v.SetArg(i, values[v.AuxInt])
+			}
+		}
+
+		// Walk children in dominator tree.
+		for c := s.tree[b.ID].firstChild; c != nil; c = s.tree[c.ID].sibling {
+			stk = append(stk, stackEntry{b: c})
+		}
+	}
+}
+
+// domBlock contains extra per-block information to record the dominator tree.
+type domBlock struct {
+	firstChild *ssa.Block // first child of block in dominator tree
+	sibling    *ssa.Block // next child of parent in dominator tree
+}
+
+// A block heap is used as a priority queue to implement the PiggyBank
+// from Sreedhar and Gao.  That paper uses an array which is better
+// asymptotically but worse in the common case when the PiggyBank
+// holds a sparse set of blocks.
+type blockHeap struct {
+	a     []*ssa.Block // block IDs in heap
+	level []int32      // depth in dominator tree (static, used for determining priority)
+}
+
+func (h *blockHeap) Len() int      { return len(h.a) }
+func (h *blockHeap) Swap(i, j int) { a := h.a; a[i], a[j] = a[j], a[i] }
+
+func (h *blockHeap) Push(x interface{}) {
+	v := x.(*ssa.Block)
+	h.a = append(h.a, v)
+}
+func (h *blockHeap) Pop() interface{} {
+	old := h.a
+	n := len(old)
+	x := old[n-1]
+	h.a = old[:n-1]
+	return x
+}
+func (h *blockHeap) Less(i, j int) bool {
+	return h.level[h.a[i].ID] > h.level[h.a[j].ID]
+}
+
+// TODO: stop walking the iterated domininance frontier when
+// the variable is dead. Maybe detect that by checking if the
+// node we're on is reverse dominated by all the reads?
+// Reverse dominated by the highest common successor of all the reads?
+
+// copy of ../ssa/sparseset.go
+// TODO: move this file to ../ssa, then use sparseSet there.
+type sparseSet struct {
+	dense  []ssa.ID
+	sparse []int32
+}
+
+// newSparseSet returns a sparseSet that can represent
+// integers between 0 and n-1
+func newSparseSet(n int) *sparseSet {
+	return &sparseSet{dense: nil, sparse: make([]int32, n)}
+}
+
+func (s *sparseSet) contains(x ssa.ID) bool {
+	i := s.sparse[x]
+	return i < int32(len(s.dense)) && s.dense[i] == x
+}
+
+func (s *sparseSet) add(x ssa.ID) {
+	i := s.sparse[x]
+	if i < int32(len(s.dense)) && s.dense[i] == x {
+		return
+	}
+	s.dense = append(s.dense, x)
+	s.sparse[x] = int32(len(s.dense)) - 1
+}
+
+func (s *sparseSet) clear() {
+	s.dense = s.dense[:0]
+}
+
+// Variant to use for small functions.
+type simplePhiState struct {
+	s       *state                 // SSA state
+	f       *ssa.Func              // function to work on
+	fwdrefs []*ssa.Value           // list of FwdRefs to be processed
+	defvars []map[*Node]*ssa.Value // defined variables at end of each block
+}
+
+func (s *simplePhiState) insertPhis() {
+	// Find FwdRef ops.
+	for _, b := range s.f.Blocks {
+		for _, v := range b.Values {
+			if v.Op != ssa.OpFwdRef {
+				continue
+			}
+			s.fwdrefs = append(s.fwdrefs, v)
+			var_ := v.Aux.(*Node)
+			if _, ok := s.defvars[b.ID][var_]; !ok {
+				s.defvars[b.ID][var_] = v // treat FwdDefs as definitions.
+			}
+		}
+	}
+
+	var args []*ssa.Value
+
+loop:
+	for len(s.fwdrefs) > 0 {
+		v := s.fwdrefs[len(s.fwdrefs)-1]
+		s.fwdrefs = s.fwdrefs[:len(s.fwdrefs)-1]
+		b := v.Block
+		var_ := v.Aux.(*Node)
+		if len(b.Preds) == 0 {
+			if b == s.f.Entry {
+				// No variable should be live at entry.
+				s.s.Fatalf("Value live at entry. It shouldn't be. func %s, node %v, value %v", s.f.Name, var_, v)
+			}
+			// This block is dead; it has no predecessors and it is not the entry block.
+			// It doesn't matter what we use here as long as it is well-formed.
+			v.Op = ssa.OpUnknown
+			v.Aux = nil
+			continue
+		}
+		// Find variable value on each predecessor.
+		args = args[:0]
+		for _, e := range b.Preds {
+			args = append(args, s.lookupVarOutgoing(e.Block(), v.Type, var_, v.Line))
+		}
+
+		// Decide if we need a phi or not. We need a phi if there
+		// are two different args (which are both not v).
+		var w *ssa.Value
+		for _, a := range args {
+			if a == v {
+				continue // self-reference
+			}
+			if a == w {
+				continue // already have this witness
+			}
+			if w != nil {
+				// two witnesses, need a phi value
+				v.Op = ssa.OpPhi
+				v.AddArgs(args...)
+				v.Aux = nil
+				continue loop
+			}
+			w = a // save witness
+		}
+		if w == nil {
+			s.s.Fatalf("no witness for reachable phi %s", v)
+		}
+		// One witness. Make v a copy of w.
+		v.Op = ssa.OpCopy
+		v.Aux = nil
+		v.AddArg(w)
+	}
+}
+
+// lookupVarOutgoing finds the variable's value at the end of block b.
+func (s *simplePhiState) lookupVarOutgoing(b *ssa.Block, t ssa.Type, var_ *Node, line int32) *ssa.Value {
+	for {
+		if v := s.defvars[b.ID][var_]; v != nil {
+			return v
+		}
+		// The variable is not defined by b and we haven't looked it up yet.
+		// If b has exactly one predecessor, loop to look it up there.
+		// Otherwise, give up and insert a new FwdRef and resolve it later.
+		if len(b.Preds) != 1 {
+			break
+		}
+		b = b.Preds[0].Block()
+	}
+	// Generate a FwdRef for the variable and return that.
+	v := b.NewValue0A(line, ssa.OpFwdRef, t, var_)
+	s.defvars[b.ID][var_] = v
+	s.s.addNamedValue(var_, v)
+	s.fwdrefs = append(s.fwdrefs, v)
+	return v
+}
diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go
index ca0421d..5fa8645 100644
--- a/src/cmd/compile/internal/gc/plive.go
+++ b/src/cmd/compile/internal/gc/plive.go
@@ -96,18 +96,35 @@ type Liveness struct {
 	livepointers     []bvec
 }
 
+// ProgInfo holds information about the instruction for use
+// by clients such as the compiler. The exact meaning of this
+// data is up to the client and is not interpreted by the cmd/internal/obj/... packages.
+type ProgInfo struct {
+	_     struct{} // to prevent unkeyed literals. Trailing zero-sized field will take space.
+	Flags uint32   // flag bits
+}
+
 // Constructs a new basic block containing a single instruction.
 func newblock(prog *obj.Prog) *BasicBlock {
 	if prog == nil {
 		Fatalf("newblock: prog cannot be nil")
 	}
-	result := new(BasicBlock)
+	// type block allows us to allocate a BasicBlock
+	// and its pred/succ slice together.
+	type block struct {
+		result BasicBlock
+		pred   [2]*BasicBlock
+		succ   [2]*BasicBlock
+	}
+	b := new(block)
+
+	result := &b.result
 	result.rpo = -1
 	result.mark = UNVISITED
 	result.first = prog
 	result.last = prog
-	result.pred = make([]*BasicBlock, 0, 2)
-	result.succ = make([]*BasicBlock, 0, 2)
+	result.pred = b.pred[:0]
+	result.succ = b.succ[:0]
 	return result
 }
 
@@ -400,7 +417,6 @@ func newcfg(firstp *obj.Prog) []*BasicBlock {
 	bb := newblock(firstp)
 	cfg = append(cfg, bb)
 	for p := firstp; p != nil && p.As != obj.AEND; p = p.Link {
-		Thearch.Proginfo(p)
 		if p.To.Type == obj.TYPE_BRANCH {
 			if p.To.Val == nil {
 				Fatalf("prog branch to nil")
@@ -538,54 +554,46 @@ func isfunny(n *Node) bool {
 // initialized, because any use of a variable must come after its
 // initialization.
 func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarinit bvec) {
-	bvresetall(uevar)
-	bvresetall(varkill)
-	bvresetall(avarinit)
+	uevar.Clear()
+	varkill.Clear()
+	avarinit.Clear()
+
+	// A return instruction with a p.to is a tail return, which brings
+	// the stack pointer back up (if it ever went down) and then jumps
+	// to a new function entirely. That form of instruction must read
+	// all the parameters for correctness, and similarly it must not
+	// read the out arguments - they won't be set until the new
+	// function runs.
+	if (prog.As == obj.AJMP || prog.As == obj.ARET) && prog.To.Type == obj.TYPE_MEM && prog.To.Name == obj.NAME_EXTERN {
+		// This is a tail call. Ensure the arguments are still alive.
+		// See issue 16016.
+		for i, node := range vars {
+			if node.Class == PPARAM {
+				uevar.Set(int32(i))
+			}
+		}
+	}
 
 	if prog.As == obj.ARET {
-		// Return instructions implicitly read all the arguments. For
-		// the sake of correctness, out arguments must be read. For the
-		// sake of backtrace quality, we read in arguments as well.
-		//
-		// A return instruction with a p.to is a tail return, which brings
-		// the stack pointer back up (if it ever went down) and then jumps
-		// to a new function entirely. That form of instruction must read
-		// all the parameters for correctness, and similarly it must not
-		// read the out arguments - they won't be set until the new
-		// function runs.
+		// Return instructions read all of the out arguments.
 		for i, node := range vars {
 			switch node.Class {
-			case PPARAM:
-				if !node.NotLiveAtEnd() {
-					bvset(uevar, int32(i))
-				}
-
-				// If the result had its address taken, it is being tracked
+			// If the result had its address taken, it is being tracked
 			// by the avarinit code, which does not use uevar.
 			// If we added it to uevar too, we'd not see any kill
 			// and decide that the variable was live entry, which it is not.
 			// So only use uevar in the non-addrtaken case.
-			// The p.to.type == thearch.D_NONE limits the bvset to
-			// non-tail-call return instructions; see note above
-			// the for loop for details.
+			// The p.to.type == obj.TYPE_NONE limits the bvset to
+			// non-tail-call return instructions; see note below for details.
 			case PPARAMOUT:
 				if !node.Addrtaken && prog.To.Type == obj.TYPE_NONE {
-					bvset(uevar, int32(i))
+					uevar.Set(int32(i))
 				}
 			}
 		}
 
 		return
 	}
-	if prog.As == obj.AJMP && prog.To.Type == obj.TYPE_MEM && prog.To.Name == obj.NAME_EXTERN {
-		// This is a tail call. Ensure the arguments are still alive.
-		// See issue 16016.
-		for i, node := range vars {
-			if node.Class == PPARAM {
-				bvset(uevar, int32(i))
-			}
-		}
-	}
 
 	if prog.As == obj.ATEXT {
 		// A text instruction marks the entry point to a function and
@@ -594,29 +602,31 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
 			switch node.Class {
 			case PPARAM:
 				if node.Addrtaken {
-					bvset(avarinit, int32(i))
+					avarinit.Set(int32(i))
 				}
-				bvset(varkill, int32(i))
+				varkill.Set(int32(i))
 			}
 		}
 
 		return
 	}
 
-	if prog.Info.Flags&(LeftRead|LeftWrite|LeftAddr) != 0 {
+	info := Thearch.Proginfo(prog)
+
+	if info.Flags&(LeftRead|LeftWrite|LeftAddr) != 0 {
 		from := &prog.From
 		if from.Node != nil && from.Sym != nil {
 			n := from.Node.(*Node)
 			if pos := liveIndex(n, vars); pos >= 0 {
 				if n.Addrtaken {
-					bvset(avarinit, pos)
+					avarinit.Set(pos)
 				} else {
-					if prog.Info.Flags&(LeftRead|LeftAddr) != 0 {
-						bvset(uevar, pos)
+					if info.Flags&(LeftRead|LeftAddr) != 0 {
+						uevar.Set(pos)
 					}
-					if prog.Info.Flags&LeftWrite != 0 {
-						if !Isfat(n.Type) {
-							bvset(varkill, pos)
+					if info.Flags&LeftWrite != 0 {
+						if !isfat(n.Type) {
+							varkill.Set(pos)
 						}
 					}
 				}
@@ -624,17 +634,31 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
 		}
 	}
 
-	if prog.Info.Flags&(RightRead|RightWrite|RightAddr) != 0 {
+	if info.Flags&From3Read != 0 {
+		from := prog.From3
+		if from.Node != nil && from.Sym != nil {
+			n := from.Node.(*Node)
+			if pos := liveIndex(n, vars); pos >= 0 {
+				if n.Addrtaken {
+					avarinit.Set(pos)
+				} else {
+					uevar.Set(pos)
+				}
+			}
+		}
+	}
+
+	if info.Flags&(RightRead|RightWrite|RightAddr) != 0 {
 		to := &prog.To
 		if to.Node != nil && to.Sym != nil {
 			n := to.Node.(*Node)
 			if pos := liveIndex(n, vars); pos >= 0 {
 				if n.Addrtaken {
 					if prog.As != obj.AVARKILL {
-						bvset(avarinit, pos)
+						avarinit.Set(pos)
 					}
 					if prog.As == obj.AVARDEF || prog.As == obj.AVARKILL {
-						bvset(varkill, pos)
+						varkill.Set(pos)
 					}
 				} else {
 					// RightRead is a read, obviously.
@@ -645,12 +669,12 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
 					// It is not a read. It is equivalent to RightWrite except that
 					// having the RightAddr bit set keeps the registerizer from
 					// trying to substitute a register for the memory location.
-					if (prog.Info.Flags&RightRead != 0) || prog.Info.Flags&(RightAddr|RightWrite) == RightAddr {
-						bvset(uevar, pos)
+					if (info.Flags&RightRead != 0) || info.Flags&(RightAddr|RightWrite) == RightAddr {
+						uevar.Set(pos)
 					}
-					if prog.Info.Flags&RightWrite != 0 {
-						if !Isfat(n.Type) || prog.As == obj.AVARDEF {
-							bvset(varkill, pos)
+					if info.Flags&RightWrite != 0 {
+						if !isfat(n.Type) || prog.As == obj.AVARDEF {
+							varkill.Set(pos)
 						}
 					}
 				}
@@ -704,14 +728,10 @@ func newliveness(fn *Node, ptxt *obj.Prog, cfg []*BasicBlock, vars []*Node) *Liv
 }
 
 func printeffects(p *obj.Prog, uevar bvec, varkill bvec, avarinit bvec) {
-	fmt.Printf("effects of %v", p)
-	fmt.Printf("\nuevar: ")
-	bvprint(uevar)
-	fmt.Printf("\nvarkill: ")
-	bvprint(varkill)
-	fmt.Printf("\navarinit: ")
-	bvprint(avarinit)
-	fmt.Printf("\n")
+	fmt.Printf("effects of %v\n", p)
+	fmt.Println("uevar:", uevar)
+	fmt.Println("varkill:", varkill)
+	fmt.Println("avarinit:", avarinit)
 }
 
 // Pretty print a variable node. Uses Pascal like conventions for pointers and
@@ -733,7 +753,7 @@ func printnode(node *Node) {
 func printvars(name string, bv bvec, vars []*Node) {
 	fmt.Printf("%s:", name)
 	for i, node := range vars {
-		if bvget(bv, int32(i)) != 0 {
+		if bv.Get(int32(i)) {
 			printnode(node)
 		}
 	}
@@ -771,8 +791,7 @@ func livenessprintblock(lv *Liveness, bb *BasicBlock) {
 		if prog.As == obj.APCDATA && prog.From.Offset == obj.PCDATA_StackMapIndex {
 			pos := int32(prog.To.Offset)
 			live := lv.livepointers[pos]
-			fmt.Printf(" ")
-			bvprint(live)
+			fmt.Printf(" %s", live.String())
 		}
 
 		fmt.Printf("\n")
@@ -806,7 +825,7 @@ func checkauto(fn *Node, p *obj.Prog, n *Node) {
 	for _, ln := range fn.Func.Dcl {
 		fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class)
 	}
-	Yyerror("checkauto: invariant lost")
+	yyerror("checkauto: invariant lost")
 }
 
 func checkparam(fn *Node, p *obj.Prog, n *Node) {
@@ -823,7 +842,7 @@ func checkparam(fn *Node, p *obj.Prog, n *Node) {
 	for _, ln := range fn.Func.Dcl {
 		fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class)
 	}
-	Yyerror("checkparam: invariant lost")
+	yyerror("checkparam: invariant lost")
 }
 
 func checkprog(fn *Node, p *obj.Prog) {
@@ -855,7 +874,7 @@ func checkptxt(fn *Node, firstp *obj.Prog) {
 		if false {
 			fmt.Printf("analyzing '%v'\n", p)
 		}
-		if p.As != obj.AGLOBL && p.As != obj.ATYPE {
+		if p.As != obj.ATYPE {
 			checkprog(fn, p)
 		}
 	}
@@ -898,7 +917,7 @@ func onebitwalktype1(t *Type, xoffset *int64, bv bvec) {
 		if *xoffset&int64(Widthptr-1) != 0 {
 			Fatalf("onebitwalktype1: invalid alignment, %v", t)
 		}
-		bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer
+		bv.Set(int32(*xoffset / int64(Widthptr))) // pointer
 		*xoffset += t.Width
 
 	case TSTRING:
@@ -906,7 +925,7 @@ func onebitwalktype1(t *Type, xoffset *int64, bv bvec) {
 		if *xoffset&int64(Widthptr-1) != 0 {
 			Fatalf("onebitwalktype1: invalid alignment, %v", t)
 		}
-		bvset(bv, int32(*xoffset/int64(Widthptr))) //pointer in first slot
+		bv.Set(int32(*xoffset / int64(Widthptr))) //pointer in first slot
 		*xoffset += t.Width
 
 	case TINTER:
@@ -916,8 +935,8 @@ func onebitwalktype1(t *Type, xoffset *int64, bv bvec) {
 		if *xoffset&int64(Widthptr-1) != 0 {
 			Fatalf("onebitwalktype1: invalid alignment, %v", t)
 		}
-		bvset(bv, int32(*xoffset/int64(Widthptr)))   // pointer in first slot
-		bvset(bv, int32(*xoffset/int64(Widthptr)+1)) // pointer in second slot
+		bv.Set(int32(*xoffset / int64(Widthptr)))   // pointer in first slot
+		bv.Set(int32(*xoffset/int64(Widthptr) + 1)) // pointer in second slot
 		*xoffset += t.Width
 
 	case TSLICE:
@@ -925,7 +944,7 @@ func onebitwalktype1(t *Type, xoffset *int64, bv bvec) {
 		if *xoffset&int64(Widthptr-1) != 0 {
 			Fatalf("onebitwalktype1: invalid TARRAY alignment, %v", t)
 		}
-		bvset(bv, int32(*xoffset/int64(Widthptr))) // pointer in first slot (BitsPointer)
+		bv.Set(int32(*xoffset / int64(Widthptr))) // pointer in first slot (BitsPointer)
 		*xoffset += t.Width
 
 	case TARRAY:
@@ -966,7 +985,7 @@ func onebitlivepointermap(lv *Liveness, liveout bvec, vars []*Node, args bvec, l
 	var xoffset int64
 
 	for i := int32(0); ; i++ {
-		i = bvnext(liveout, i)
+		i = liveout.Next(i)
 		if i < 0 {
 			break
 		}
@@ -1025,22 +1044,22 @@ func livenessprologue(lv *Liveness) {
 			if debuglive >= 3 {
 				printeffects(p, uevar, varkill, avarinit)
 			}
-			bvor(bb.varkill, bb.varkill, varkill)
-			bvandnot(bb.uevar, bb.uevar, varkill)
-			bvor(bb.uevar, bb.uevar, uevar)
+			bb.varkill.Or(bb.varkill, varkill)
+			bb.uevar.AndNot(bb.uevar, varkill)
+			bb.uevar.Or(bb.uevar, uevar)
 		}
 
 		// Walk the block instructions forward to update avarinit bits.
 		// avarinit describes the effect at the end of the block, not the beginning.
-		bvresetall(varkill)
+		varkill.Clear()
 
 		for p := bb.first; ; p = p.Link {
 			progeffects(p, lv.vars, uevar, varkill, avarinit)
 			if debuglive >= 3 {
 				printeffects(p, uevar, varkill, avarinit)
 			}
-			bvandnot(bb.avarinit, bb.avarinit, varkill)
-			bvor(bb.avarinit, bb.avarinit, avarinit)
+			bb.avarinit.AndNot(bb.avarinit, varkill)
+			bb.avarinit.Or(bb.avarinit, avarinit)
 			if p == bb.last {
 				break
 			}
@@ -1063,41 +1082,41 @@ func livenesssolve(lv *Liveness) {
 	// avarinitany says the addressed var is initialized along some path reaching the block exit.
 	for i, bb := range lv.cfg {
 		if i == 0 {
-			bvcopy(bb.avarinitall, bb.avarinit)
+			bb.avarinitall.Copy(bb.avarinit)
 		} else {
-			bvresetall(bb.avarinitall)
-			bvnot(bb.avarinitall)
+			bb.avarinitall.Clear()
+			bb.avarinitall.Not()
 		}
-		bvcopy(bb.avarinitany, bb.avarinit)
+		bb.avarinitany.Copy(bb.avarinit)
 	}
 
 	for change := true; change; {
 		change = false
 		for _, bb := range lv.cfg {
-			bvresetall(any)
-			bvresetall(all)
+			any.Clear()
+			all.Clear()
 			for j, pred := range bb.pred {
 				if j == 0 {
-					bvcopy(any, pred.avarinitany)
-					bvcopy(all, pred.avarinitall)
+					any.Copy(pred.avarinitany)
+					all.Copy(pred.avarinitall)
 				} else {
-					bvor(any, any, pred.avarinitany)
-					bvand(all, all, pred.avarinitall)
+					any.Or(any, pred.avarinitany)
+					all.And(all, pred.avarinitall)
 				}
 			}
 
-			bvandnot(any, any, bb.varkill)
-			bvandnot(all, all, bb.varkill)
-			bvor(any, any, bb.avarinit)
-			bvor(all, all, bb.avarinit)
-			if !bveq(any, bb.avarinitany) {
+			any.AndNot(any, bb.varkill)
+			all.AndNot(all, bb.varkill)
+			any.Or(any, bb.avarinit)
+			all.Or(all, bb.avarinit)
+			if !any.Eq(bb.avarinitany) {
 				change = true
-				bvcopy(bb.avarinitany, any)
+				bb.avarinitany.Copy(any)
 			}
 
-			if !bveq(all, bb.avarinitall) {
+			if !all.Eq(bb.avarinitall) {
 				change = true
-				bvcopy(bb.avarinitall, all)
+				bb.avarinitall.Copy(all)
 			}
 		}
 	}
@@ -1118,14 +1137,14 @@ func livenesssolve(lv *Liveness) {
 			// if it is live on input to some successor.
 			//
 			// out[b] = \bigcup_{s \in succ[b]} in[s]
-			bvresetall(newliveout)
+			newliveout.Clear()
 			for _, succ := range bb.succ {
-				bvor(newliveout, newliveout, succ.livein)
+				newliveout.Or(newliveout, succ.livein)
 			}
 
-			if !bveq(bb.liveout, newliveout) {
+			if !bb.liveout.Eq(newliveout) {
 				change = true
-				bvcopy(bb.liveout, newliveout)
+				bb.liveout.Copy(newliveout)
 			}
 
 			// A variable is live on input to this block
@@ -1133,9 +1152,9 @@ func livenesssolve(lv *Liveness) {
 			// not set by the code in this block.
 			//
 			// in[b] = uevar[b] \cup (out[b] \setminus varkill[b])
-			bvandnot(newlivein, bb.liveout, bb.varkill)
+			newlivein.AndNot(bb.liveout, bb.varkill)
 
-			bvor(bb.livein, newlivein, bb.uevar)
+			bb.livein.Or(newlivein, bb.uevar)
 		}
 	}
 }
@@ -1146,14 +1165,14 @@ func islive(n *Node, args bvec, locals bvec) bool {
 	switch n.Class {
 	case PPARAM, PPARAMOUT:
 		for i := 0; int64(i) < n.Type.Width/int64(Widthptr); i++ {
-			if bvget(args, int32(n.Xoffset/int64(Widthptr)+int64(i))) != 0 {
+			if args.Get(int32(n.Xoffset/int64(Widthptr) + int64(i))) {
 				return true
 			}
 		}
 
 	case PAUTO:
 		for i := 0; int64(i) < n.Type.Width/int64(Widthptr); i++ {
-			if bvget(locals, int32((n.Xoffset+stkptrsize)/int64(Widthptr)+int64(i))) != 0 {
+			if locals.Get(int32((n.Xoffset+stkptrsize)/int64(Widthptr) + int64(i))) {
 				return true
 			}
 		}
@@ -1173,17 +1192,18 @@ func livenessepilogue(lv *Liveness) {
 	avarinit := bvalloc(nvars)
 	any := bvalloc(nvars)
 	all := bvalloc(nvars)
-	ambig := bvalloc(localswords())
+	pparamout := bvalloc(localswords())
 
-	// Set ambig bit for the pointers to heap-allocated pparamout variables.
-	// These are implicitly read by post-deferreturn code and thus must be
-	// kept live throughout the function (if there is any defer that recovers).
+	// Record pointers to heap-allocated pparamout variables.  These
+	// are implicitly read by post-deferreturn code and thus must be
+	// kept live throughout the function (if there is any defer that
+	// recovers).
 	if hasdefer {
 		for _, n := range lv.vars {
 			if n.IsOutputParamHeapAddr() {
 				n.Name.Needzero = true
 				xoffset := n.Xoffset + stkptrsize
-				onebitwalktype1(n.Type, &xoffset, ambig)
+				onebitwalktype1(n.Type, &xoffset, pparamout)
 			}
 		}
 	}
@@ -1192,17 +1212,17 @@ func livenessepilogue(lv *Liveness) {
 		// Compute avarinitany and avarinitall for entry to block.
 		// This duplicates information known during livenesssolve
 		// but avoids storing two more vectors for each block.
-		bvresetall(any)
+		any.Clear()
 
-		bvresetall(all)
+		all.Clear()
 		for j := 0; j < len(bb.pred); j++ {
 			pred := bb.pred[j]
 			if j == 0 {
-				bvcopy(any, pred.avarinitany)
-				bvcopy(all, pred.avarinitall)
+				any.Copy(pred.avarinitany)
+				all.Copy(pred.avarinitall)
 			} else {
-				bvor(any, any, pred.avarinitany)
-				bvand(all, all, pred.avarinitall)
+				any.Or(any, pred.avarinitany)
+				all.And(all, pred.avarinitall)
 			}
 		}
 
@@ -1211,35 +1231,30 @@ func livenessepilogue(lv *Liveness) {
 		// Seed the maps with information about the addrtaken variables.
 		for p := bb.first; ; p = p.Link {
 			progeffects(p, lv.vars, uevar, varkill, avarinit)
-			bvandnot(any, any, varkill)
-			bvandnot(all, all, varkill)
-			bvor(any, any, avarinit)
-			bvor(all, all, avarinit)
+			any.AndNot(any, varkill)
+			all.AndNot(all, varkill)
+			any.Or(any, avarinit)
+			all.Or(all, avarinit)
 
 			if issafepoint(p) {
 				// Annotate ambiguously live variables so that they can
 				// be zeroed at function entry.
 				// livein and liveout are dead here and used as temporaries.
-				bvresetall(livein)
+				livein.Clear()
 
-				bvandnot(liveout, any, all)
-				if !bvisempty(liveout) {
+				liveout.AndNot(any, all)
+				if !liveout.IsEmpty() {
 					for pos := int32(0); pos < liveout.n; pos++ {
-						if bvget(liveout, pos) == 0 {
+						if !liveout.Get(pos) {
 							continue
 						}
-						bvset(all, pos) // silence future warnings in this block
+						all.Set(pos) // silence future warnings in this block
 						n := lv.vars[pos]
 						if !n.Name.Needzero {
 							n.Name.Needzero = true
 							if debuglive >= 1 {
-								Warnl(p.Lineno, "%v: %v is ambiguously live", Curfn.Func.Nname, Nconv(n, FmtLong))
+								Warnl(p.Lineno, "%v: %L is ambiguously live", Curfn.Func.Nname, n)
 							}
-
-							// Record in 'ambiguous' bitmap.
-							xoffset := n.Xoffset + stkptrsize
-
-							onebitwalktype1(n.Type, &xoffset, ambig)
 						}
 					}
 				}
@@ -1296,7 +1311,7 @@ func livenessepilogue(lv *Liveness) {
 			Fatalf("livenessepilogue")
 		}
 
-		bvcopy(livein, bb.liveout)
+		livein.Copy(bb.liveout)
 		var next *obj.Prog
 		for p := bb.last; p != nil; p = next {
 			next = p.Opt.(*obj.Prog) // splicebefore modifies p.opt
@@ -1304,9 +1319,9 @@ func livenessepilogue(lv *Liveness) {
 			// Propagate liveness information
 			progeffects(p, lv.vars, uevar, varkill, avarinit)
 
-			bvcopy(liveout, livein)
-			bvandnot(livein, liveout, varkill)
-			bvor(livein, livein, uevar)
+			liveout.Copy(livein)
+			livein.AndNot(liveout, varkill)
+			livein.Or(livein, uevar)
 			if debuglive >= 3 && issafepoint(p) {
 				fmt.Printf("%v\n", p)
 				printvars("uevar", uevar, lv.vars)
@@ -1324,12 +1339,12 @@ func livenessepilogue(lv *Liveness) {
 				// input parameters.
 				if p.As == obj.ATEXT {
 					for j := int32(0); j < liveout.n; j++ {
-						if bvget(liveout, j) == 0 {
+						if !liveout.Get(j) {
 							continue
 						}
 						n := lv.vars[j]
 						if n.Class != PPARAM {
-							yyerrorl(p.Lineno, "internal error: %v %v recorded as live on entry, p.Pc=%v", Curfn.Func.Nname, Nconv(n, FmtLong), p.Pc)
+							yyerrorl(p.Lineno, "internal error: %v %L recorded as live on entry, p.Pc=%v", Curfn.Func.Nname, n, p.Pc)
 						}
 					}
 				}
@@ -1340,11 +1355,9 @@ func livenessepilogue(lv *Liveness) {
 				locals := lv.livepointers[pos]
 				onebitlivepointermap(lv, liveout, lv.vars, args, locals)
 
-				// Ambiguously live variables are zeroed immediately after
-				// function entry. Mark them live for all the non-entry bitmaps
-				// so that GODEBUG=gcdead=1 mode does not poison them.
+				// Mark pparamout variables (as described above)
 				if p.As == obj.ACALL {
-					bvor(locals, locals, ambig)
+					locals.Or(locals, pparamout)
 				}
 
 				// Show live pointer bitmaps.
@@ -1423,7 +1436,7 @@ func livenessepilogue(lv *Liveness) {
 		}
 	}
 
-	Flusherrors()
+	flusherrors()
 }
 
 // FNV-1 hash function constants.
@@ -1496,7 +1509,7 @@ func livenesscompact(lv *Liveness) {
 			}
 			jlocal := lv.livepointers[j]
 			jarg := lv.argslivepointers[j]
-			if bveq(local, jlocal) && bveq(arg, jarg) {
+			if local.Eq(jlocal) && arg.Eq(jarg) {
 				remap[i] = j
 				goto Next
 			}
@@ -1539,7 +1552,7 @@ func livenesscompact(lv *Liveness) {
 func printbitset(printed bool, name string, vars []*Node, bits bvec) bool {
 	started := false
 	for i, n := range vars {
-		if bvget(bits, int32(i)) == 0 {
+		if !bits.Get(int32(i)) {
 			continue
 		}
 		if !started {
@@ -1662,7 +1675,7 @@ func livenessprintdebug(lv *Liveness) {
 // Dumps a slice of bitmaps to a symbol as a sequence of uint32 values. The
 // first word dumped is the total number of bitmaps. The second word is the
 // length of the bitmaps. All bitmaps are assumed to be of equal length. The
-// words that are followed are the raw bitmap words.
+// remaining bytes are the raw bitmaps.
 func onebitwritesymbol(arr []bvec, sym *Sym) {
 	off := 4                                  // number of bitmaps, to fill in later
 	off = duint32(sym, off, uint32(arr[0].n)) // number of bits in each bitmap
@@ -1674,22 +1687,13 @@ func onebitwritesymbol(arr []bvec, sym *Sym) {
 		if bv.b == nil {
 			break
 		}
-		for j := 0; int32(j) < bv.n; j += 32 {
-			word := bv.b[j/32]
-
-			// Runtime reads the bitmaps as byte arrays. Oblige.
-			off = duint8(sym, off, uint8(word))
-
-			off = duint8(sym, off, uint8(word>>8))
-			off = duint8(sym, off, uint8(word>>16))
-			off = duint8(sym, off, uint8(word>>24))
-		}
+		off = dbvec(sym, off, bv)
 	}
 
 	duint32(sym, 0, uint32(i)) // number of bitmaps
 	ls := Linksym(sym)
 	ls.Name = fmt.Sprintf("gclocals·%x", md5.Sum(ls.P))
-	ls.Dupok = true
+	ls.Set(obj.AttrDuplicateOK, true)
 	sv := obj.SymVer{Name: ls.Name, Version: 0}
 	ls2, ok := Ctxt.Hash[sv]
 	if ok {
diff --git a/src/cmd/compile/internal/gc/popt.go b/src/cmd/compile/internal/gc/popt.go
deleted file mode 100644
index 8e2a7ba..0000000
--- a/src/cmd/compile/internal/gc/popt.go
+++ /dev/null
@@ -1,1094 +0,0 @@
-// Derived from Inferno utils/6c/gc.h
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/gc.h
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-// "Portable" optimizations.
-
-package gc
-
-import (
-	"cmd/internal/obj"
-	"fmt"
-	"sort"
-	"strings"
-)
-
-type OptStats struct {
-	Ncvtreg int32
-	Nspill  int32
-	Nreload int32
-	Ndelmov int32
-	Nvar    int32
-	Naddr   int32
-}
-
-var Ostats OptStats
-
-var noreturn_symlist [10]*Sym
-
-// p is a call instruction. Does the call fail to return?
-func Noreturn(p *obj.Prog) bool {
-	if noreturn_symlist[0] == nil {
-		noreturn_symlist[0] = Pkglookup("panicindex", Runtimepkg)
-		noreturn_symlist[1] = Pkglookup("panicslice", Runtimepkg)
-		noreturn_symlist[2] = Pkglookup("throwinit", Runtimepkg)
-		noreturn_symlist[3] = Pkglookup("gopanic", Runtimepkg)
-		noreturn_symlist[4] = Pkglookup("panicwrap", Runtimepkg)
-		noreturn_symlist[5] = Pkglookup("throwreturn", Runtimepkg)
-		noreturn_symlist[6] = Pkglookup("selectgo", Runtimepkg)
-		noreturn_symlist[7] = Pkglookup("block", Runtimepkg)
-	}
-
-	if p.To.Node == nil {
-		return false
-	}
-	s := ((p.To.Node).(*Node)).Sym
-	if s == nil {
-		return false
-	}
-	for i := 0; noreturn_symlist[i] != nil; i++ {
-		if s == noreturn_symlist[i] {
-			return true
-		}
-	}
-	return false
-}
-
-// JMP chasing and removal.
-//
-// The code generator depends on being able to write out jump
-// instructions that it can jump to now but fill in later.
-// the linker will resolve them nicely, but they make the code
-// longer and more difficult to follow during debugging.
-// Remove them.
-
-// what instruction does a JMP to p eventually land on?
-func chasejmp(p *obj.Prog, jmploop *int) *obj.Prog {
-	n := 0
-	for p != nil && p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH {
-		n++
-		if n > 10 {
-			*jmploop = 1
-			break
-		}
-
-		p = p.To.Val.(*obj.Prog)
-	}
-
-	return p
-}
-
-// reuse reg pointer for mark/sweep state.
-// leave reg==nil at end because alive==nil.
-var alive interface{} = nil
-var dead interface{} = 1
-
-// mark all code reachable from firstp as alive
-func mark(firstp *obj.Prog) {
-	for p := firstp; p != nil; p = p.Link {
-		if p.Opt != dead {
-			break
-		}
-		p.Opt = alive
-		if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil {
-			mark(p.To.Val.(*obj.Prog))
-		}
-		if p.As == obj.AJMP || p.As == obj.ARET || p.As == obj.AUNDEF {
-			break
-		}
-	}
-}
-
-func fixjmp(firstp *obj.Prog) {
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		fmt.Printf("\nfixjmp\n")
-	}
-
-	// pass 1: resolve jump to jump, mark all code as dead.
-	jmploop := 0
-
-	for p := firstp; p != nil; p = p.Link {
-		if Debug['R'] != 0 && Debug['v'] != 0 {
-			fmt.Printf("%v\n", p)
-		}
-		if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.AJMP {
-			if Debug['N'] == 0 {
-				p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop)
-				if Debug['R'] != 0 && Debug['v'] != 0 {
-					fmt.Printf("->%v\n", p)
-				}
-			}
-		}
-
-		p.Opt = dead
-	}
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		fmt.Printf("\n")
-	}
-
-	// pass 2: mark all reachable code alive
-	mark(firstp)
-
-	// pass 3: delete dead code (mostly JMPs).
-	var last *obj.Prog
-
-	for p := firstp; p != nil; p = p.Link {
-		if p.Opt == dead {
-			if p.Link == nil && p.As == obj.ARET && last != nil && last.As != obj.ARET {
-				// This is the final ARET, and the code so far doesn't have one.
-				// Let it stay. The register allocator assumes that all live code in
-				// the function can be traversed by starting at all the RET instructions
-				// and following predecessor links. If we remove the final RET,
-				// this assumption will not hold in the case of an infinite loop
-				// at the end of a function.
-				// Keep the RET but mark it dead for the liveness analysis.
-				p.Mode = 1
-			} else {
-				if Debug['R'] != 0 && Debug['v'] != 0 {
-					fmt.Printf("del %v\n", p)
-				}
-				continue
-			}
-		}
-
-		if last != nil {
-			last.Link = p
-		}
-		last = p
-	}
-
-	last.Link = nil
-
-	// pass 4: elide JMP to next instruction.
-	// only safe if there are no jumps to JMPs anymore.
-	if jmploop == 0 && Debug['N'] == 0 {
-		var last *obj.Prog
-		for p := firstp; p != nil; p = p.Link {
-			if p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH && p.To.Val == p.Link {
-				if Debug['R'] != 0 && Debug['v'] != 0 {
-					fmt.Printf("del %v\n", p)
-				}
-				continue
-			}
-
-			if last != nil {
-				last.Link = p
-			}
-			last = p
-		}
-
-		last.Link = nil
-	}
-
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		fmt.Printf("\n")
-		for p := firstp; p != nil; p = p.Link {
-			fmt.Printf("%v\n", p)
-		}
-		fmt.Printf("\n")
-	}
-}
-
-// Control flow analysis. The Flow structures hold predecessor and successor
-// information as well as basic loop analysis.
-//
-//	graph = Flowstart(firstp, nil)
-//	... use flow graph ...
-//	Flowend(graph) // free graph
-//
-// Typical uses of the flow graph are to iterate over all the flow-relevant instructions:
-//
-//	for f := graph.Start; f != nil; f = f.Link {}
-//
-// or, given an instruction f, to iterate over all the predecessors, which is
-// f.P1 and this list:
-//
-//	for f2 := f.P2; f2 != nil; f2 = f2.P2link {}
-//
-// The second argument (newData) to Flowstart specifies a func to create object
-// for every f.Data field, for use by the client.
-// If newData is nil, f.Data will be nil.
-
-type Graph struct {
-	Start *Flow
-	Num   int
-
-	// After calling flowrpo, rpo lists the flow nodes in reverse postorder,
-	// and each non-dead Flow node f has g->rpo[f->rpo] == f.
-	Rpo []*Flow
-}
-
-type Flow struct {
-	Prog   *obj.Prog // actual instruction
-	P1     *Flow     // predecessors of this instruction: p1,
-	P2     *Flow     // and then p2 linked though p2link.
-	P2link *Flow
-	S1     *Flow // successors of this instruction (at most two: s1 and s2).
-	S2     *Flow
-	Link   *Flow // next instruction in function code
-
-	Active int32 // usable by client
-
-	Id     int32  // sequence number in flow graph
-	Rpo    int32  // reverse post ordering
-	Loop   uint16 // x5 for every loop
-	Refset bool   // diagnostic generated
-
-	Data interface{} // for use by client
-}
-
-var flowmark int
-
-// MaxFlowProg is the maximum size program (counted in instructions)
-// for which the flow code will build a graph. Functions larger than this limit
-// will not have flow graphs and consequently will not be optimized.
-const MaxFlowProg = 50000
-
-var ffcache []Flow // reusable []Flow, to reduce allocation
-
-func growffcache(n int) {
-	if n > cap(ffcache) {
-		n = (n * 5) / 4
-		if n > MaxFlowProg {
-			n = MaxFlowProg
-		}
-		ffcache = make([]Flow, n)
-	}
-	ffcache = ffcache[:n]
-}
-
-func Flowstart(firstp *obj.Prog, newData func() interface{}) *Graph {
-	// Count and mark instructions to annotate.
-	nf := 0
-
-	for p := firstp; p != nil; p = p.Link {
-		p.Opt = nil // should be already, but just in case
-		Thearch.Proginfo(p)
-		if p.Info.Flags&Skip != 0 {
-			continue
-		}
-		p.Opt = &flowmark
-		nf++
-	}
-
-	if nf == 0 {
-		return nil
-	}
-
-	if nf >= MaxFlowProg {
-		if Debug['v'] != 0 {
-			Warn("%v is too big (%d instructions)", Curfn.Func.Nname.Sym, nf)
-		}
-		return nil
-	}
-
-	// Allocate annotations and assign to instructions.
-	graph := new(Graph)
-
-	growffcache(nf)
-	ff := ffcache
-	start := &ff[0]
-	id := 0
-	var last *Flow
-	for p := firstp; p != nil; p = p.Link {
-		if p.Opt == nil {
-			continue
-		}
-		f := &ff[0]
-		ff = ff[1:]
-		p.Opt = f
-		f.Prog = p
-		if last != nil {
-			last.Link = f
-		}
-		last = f
-		if newData != nil {
-			f.Data = newData()
-		}
-		f.Id = int32(id)
-		id++
-	}
-
-	// Fill in pred/succ information.
-	var f1 *Flow
-	var p *obj.Prog
-	for f := start; f != nil; f = f.Link {
-		p = f.Prog
-		if p.Info.Flags&Break == 0 {
-			f1 = f.Link
-			f.S1 = f1
-			f1.P1 = f
-		}
-
-		if p.To.Type == obj.TYPE_BRANCH {
-			if p.To.Val == nil {
-				Fatalf("pnil %v", p)
-			}
-			f1 = p.To.Val.(*obj.Prog).Opt.(*Flow)
-			if f1 == nil {
-				Fatalf("fnil %v / %v", p, p.To.Val.(*obj.Prog))
-			}
-			if f1 == f {
-				//fatal("self loop %v", p);
-				continue
-			}
-
-			f.S2 = f1
-			f.P2link = f1.P2
-			f1.P2 = f
-		}
-	}
-
-	graph.Start = start
-	graph.Num = nf
-	return graph
-}
-
-func Flowend(graph *Graph) {
-	for f := graph.Start; f != nil; f = f.Link {
-		f.Prog.Info.Flags = 0 // drop cached proginfo
-		f.Prog.Opt = nil
-	}
-	clear := ffcache[:graph.Num]
-	for i := range clear {
-		clear[i] = Flow{}
-	}
-}
-
-// find looping structure
-//
-// 1) find reverse postordering
-// 2) find approximate dominators,
-//	the actual dominators if the flow graph is reducible
-//	otherwise, dominators plus some other non-dominators.
-//	See Matthew S. Hecht and Jeffrey D. Ullman,
-//	"Analysis of a Simple Algorithm for Global Data Flow Problems",
-//	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
-//	Oct. 1-3, 1973, pp.  207-217.
-// 3) find all nodes with a predecessor dominated by the current node.
-//	such a node is a loop head.
-//	recursively, all preds with a greater rpo number are in the loop
-func postorder(r *Flow, rpo2r []*Flow, n int32) int32 {
-	r.Rpo = 1
-	r1 := r.S1
-	if r1 != nil && r1.Rpo == 0 {
-		n = postorder(r1, rpo2r, n)
-	}
-	r1 = r.S2
-	if r1 != nil && r1.Rpo == 0 {
-		n = postorder(r1, rpo2r, n)
-	}
-	rpo2r[n] = r
-	n++
-	return n
-}
-
-func rpolca(idom []int32, rpo1 int32, rpo2 int32) int32 {
-	if rpo1 == -1 {
-		return rpo2
-	}
-	var t int32
-	for rpo1 != rpo2 {
-		if rpo1 > rpo2 {
-			t = rpo2
-			rpo2 = rpo1
-			rpo1 = t
-		}
-
-		for rpo1 < rpo2 {
-			t = idom[rpo2]
-			if t >= rpo2 {
-				Fatalf("bad idom")
-			}
-			rpo2 = t
-		}
-	}
-
-	return rpo1
-}
-
-func doms(idom []int32, r int32, s int32) bool {
-	for s > r {
-		s = idom[s]
-	}
-	return s == r
-}
-
-func loophead(idom []int32, r *Flow) bool {
-	src := r.Rpo
-	if r.P1 != nil && doms(idom, src, r.P1.Rpo) {
-		return true
-	}
-	for r = r.P2; r != nil; r = r.P2link {
-		if doms(idom, src, r.Rpo) {
-			return true
-		}
-	}
-	return false
-}
-
-func loopmark(rpo2r **Flow, head int32, r *Flow) {
-	if r.Rpo < head || r.Active == head {
-		return
-	}
-	r.Active = head
-	r.Loop += LOOP
-	if r.P1 != nil {
-		loopmark(rpo2r, head, r.P1)
-	}
-	for r = r.P2; r != nil; r = r.P2link {
-		loopmark(rpo2r, head, r)
-	}
-}
-
-func flowrpo(g *Graph) {
-	g.Rpo = make([]*Flow, g.Num)
-	idom := make([]int32, g.Num)
-
-	for r1 := g.Start; r1 != nil; r1 = r1.Link {
-		r1.Active = 0
-	}
-
-	rpo2r := g.Rpo
-	d := postorder(g.Start, rpo2r, 0)
-	nr := int32(g.Num)
-	if d > nr {
-		Fatalf("too many reg nodes %d %d", d, nr)
-	}
-	nr = d
-	var r1 *Flow
-	for i := int32(0); i < nr/2; i++ {
-		r1 = rpo2r[i]
-		rpo2r[i] = rpo2r[nr-1-i]
-		rpo2r[nr-1-i] = r1
-	}
-
-	for i := int32(0); i < nr; i++ {
-		rpo2r[i].Rpo = i
-	}
-
-	idom[0] = 0
-	var me int32
-	for i := int32(0); i < nr; i++ {
-		r1 = rpo2r[i]
-		me = r1.Rpo
-		d = -1
-
-		// rpo2r[r.Rpo] == r protects against considering dead code,
-		// which has r.Rpo == 0.
-		if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me {
-			d = r1.P1.Rpo
-		}
-		for r1 = r1.P2; r1 != nil; r1 = r1.P2link {
-			if rpo2r[r1.Rpo] == r1 && r1.Rpo < me {
-				d = rpolca(idom, d, r1.Rpo)
-			}
-		}
-		idom[i] = d
-	}
-
-	for i := int32(0); i < nr; i++ {
-		r1 = rpo2r[i]
-		r1.Loop++
-		if r1.P2 != nil && loophead(idom, r1) {
-			loopmark(&rpo2r[0], i, r1)
-		}
-	}
-
-	for r1 := g.Start; r1 != nil; r1 = r1.Link {
-		r1.Active = 0
-	}
-}
-
-func Uniqp(r *Flow) *Flow {
-	r1 := r.P1
-	if r1 == nil {
-		r1 = r.P2
-		if r1 == nil || r1.P2link != nil {
-			return nil
-		}
-	} else if r.P2 != nil {
-		return nil
-	}
-	return r1
-}
-
-func Uniqs(r *Flow) *Flow {
-	r1 := r.S1
-	if r1 == nil {
-		r1 = r.S2
-		if r1 == nil {
-			return nil
-		}
-	} else if r.S2 != nil {
-		return nil
-	}
-	return r1
-}
-
-// The compilers assume they can generate temporary variables
-// as needed to preserve the right semantics or simplify code
-// generation and the back end will still generate good code.
-// This results in a large number of ephemeral temporary variables.
-// Merge temps with non-overlapping lifetimes and equal types using the
-// greedy algorithm in Poletto and Sarkar, "Linear Scan Register Allocation",
-// ACM TOPLAS 1999.
-
-type TempVar struct {
-	node    *Node
-	def     *Flow    // definition of temp var
-	use     *Flow    // use list, chained through Flow.data
-	merge   *TempVar // merge var with this one
-	start   int64    // smallest Prog.pc in live range
-	end     int64    // largest Prog.pc in live range
-	addr    bool     // address taken - no accurate end
-	removed bool     // removed from program
-}
-
-// startcmp sorts TempVars by start, then id, then symbol name.
-type startcmp []*TempVar
-
-func (x startcmp) Len() int      { return len(x) }
-func (x startcmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (x startcmp) Less(i, j int) bool {
-	a := x[i]
-	b := x[j]
-
-	if a.start < b.start {
-		return true
-	}
-	if a.start > b.start {
-		return false
-	}
-
-	// Order what's left by id or symbol name,
-	// just so that sort is forced into a specific ordering,
-	// so that the result of the sort does not depend on
-	// the sort implementation.
-	if a.def != b.def {
-		return int(a.def.Id-b.def.Id) < 0
-	}
-	if a.node != b.node {
-		return a.node.Sym.Name < b.node.Sym.Name
-	}
-	return false
-}
-
-// Is n available for merging?
-func canmerge(n *Node) bool {
-	return n.Class == PAUTO && strings.HasPrefix(n.Sym.Name, "autotmp")
-}
-
-func mergetemp(firstp *obj.Prog) {
-	const (
-		debugmerge = 0
-	)
-
-	g := Flowstart(firstp, nil)
-	if g == nil {
-		return
-	}
-
-	// Build list of all mergeable variables.
-	var vars []*TempVar
-	for _, n := range Curfn.Func.Dcl {
-		if canmerge(n) {
-			v := &TempVar{}
-			vars = append(vars, v)
-			n.SetOpt(v)
-			v.node = n
-		}
-	}
-
-	// Build list of uses.
-	// We assume that the earliest reference to a temporary is its definition.
-	// This is not true of variables in general but our temporaries are all
-	// single-use (that's why we have so many!).
-	for f := g.Start; f != nil; f = f.Link {
-		p := f.Prog
-		if p.From.Node != nil && ((p.From.Node).(*Node)).Opt() != nil && p.To.Node != nil && ((p.To.Node).(*Node)).Opt() != nil {
-			Fatalf("double node %v", p)
-		}
-		var v *TempVar
-		n, _ := p.From.Node.(*Node)
-		if n != nil {
-			v, _ = n.Opt().(*TempVar)
-		}
-		if v == nil {
-			n, _ = p.To.Node.(*Node)
-			if n != nil {
-				v, _ = n.Opt().(*TempVar)
-			}
-		}
-		if v != nil {
-			if v.def == nil {
-				v.def = f
-			}
-			f.Data = v.use
-			v.use = f
-			if n == p.From.Node && (p.Info.Flags&LeftAddr != 0) {
-				v.addr = true
-			}
-		}
-	}
-
-	if debugmerge > 1 && Debug['v'] != 0 {
-		Dumpit("before", g.Start, 0)
-	}
-
-	nkill := 0
-
-	// Special case.
-	for _, v := range vars {
-		if v.addr {
-			continue
-		}
-
-		// Used in only one instruction, which had better be a write.
-		f := v.use
-		if f != nil && f.Data.(*Flow) == nil {
-			p := f.Prog
-			if p.To.Node == v.node && (p.Info.Flags&RightWrite != 0) && p.Info.Flags&RightRead == 0 {
-				p.As = obj.ANOP
-				p.To = obj.Addr{}
-				v.removed = true
-				if debugmerge > 0 && Debug['v'] != 0 {
-					fmt.Printf("drop write-only %v\n", v.node.Sym)
-				}
-			} else {
-				Fatalf("temp used and not set: %v", p)
-			}
-			nkill++
-			continue
-		}
-
-		// Written in one instruction, read in the next, otherwise unused,
-		// no jumps to the next instruction. Happens mainly in 386 compiler.
-		f = v.use
-		if f != nil && f.Link == f.Data.(*Flow) && (f.Data.(*Flow)).Data.(*Flow) == nil && Uniqp(f.Link) == f {
-			p := f.Prog
-			p1 := f.Link.Prog
-			const (
-				SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD
-			)
-			if p.From.Node == v.node && p1.To.Node == v.node && (p.Info.Flags&Move != 0) && (p.Info.Flags|p1.Info.Flags)&(LeftAddr|RightAddr) == 0 && p.Info.Flags&SizeAny == p1.Info.Flags&SizeAny {
-				p1.From = p.From
-				Thearch.Excise(f)
-				v.removed = true
-				if debugmerge > 0 && Debug['v'] != 0 {
-					fmt.Printf("drop immediate-use %v\n", v.node.Sym)
-				}
-			}
-
-			nkill++
-			continue
-		}
-	}
-
-	// Traverse live range of each variable to set start, end.
-	// Each flood uses a new value of gen so that we don't have
-	// to clear all the r.Active words after each variable.
-	gen := uint32(0)
-
-	for _, v := range vars {
-		gen++
-		for f := v.use; f != nil; f = f.Data.(*Flow) {
-			mergewalk(v, f, gen)
-		}
-		if v.addr {
-			gen++
-			for f := v.use; f != nil; f = f.Data.(*Flow) {
-				varkillwalk(v, f, gen)
-			}
-		}
-	}
-
-	// Sort variables by start.
-	bystart := make([]*TempVar, len(vars))
-	copy(bystart, vars)
-	sort.Sort(startcmp(bystart))
-
-	// List of in-use variables, sorted by end, so that the ones that
-	// will last the longest are the earliest ones in the array.
-	// The tail inuse[nfree:] holds no-longer-used variables.
-	// In theory we should use a sorted tree so that insertions are
-	// guaranteed O(log n) and then the loop is guaranteed O(n log n).
-	// In practice, it doesn't really matter.
-	inuse := make([]*TempVar, len(bystart))
-
-	ninuse := 0
-	nfree := len(bystart)
-	for _, v := range bystart {
-		if debugmerge > 0 && Debug['v'] != 0 {
-			fmt.Printf("consider %v: removed=%t\n", Nconv(v.node, FmtSharp), v.removed)
-		}
-
-		if v.removed {
-			continue
-		}
-
-		// Expire no longer in use.
-		for ninuse > 0 && inuse[ninuse-1].end < v.start {
-			ninuse--
-			nfree--
-			inuse[nfree] = inuse[ninuse]
-		}
-
-		if debugmerge > 0 && Debug['v'] != 0 {
-			fmt.Printf("consider %v: removed=%t nfree=%d nvar=%d\n", Nconv(v.node, FmtSharp), v.removed, nfree, len(bystart))
-		}
-
-		// Find old temp to reuse if possible.
-		t := v.node.Type
-
-		for j := nfree; j < len(inuse); j++ {
-			v1 := inuse[j]
-			if debugmerge > 0 && Debug['v'] != 0 {
-				fmt.Printf("consider %v: maybe %v: type=%v,%v addrtaken=%v,%v\n", Nconv(v.node, FmtSharp), Nconv(v1.node, FmtSharp), t, v1.node.Type, v.node.Addrtaken, v1.node.Addrtaken)
-			}
-
-			// Require the types to match but also require the addrtaken bits to match.
-			// If a variable's address is taken, that disables registerization for the individual
-			// words of the variable (for example, the base,len,cap of a slice).
-			// We don't want to merge a non-addressed var with an addressed one and
-			// inhibit registerization of the former.
-			if Eqtype(t, v1.node.Type) && v.node.Addrtaken == v1.node.Addrtaken {
-				inuse[j] = inuse[nfree]
-				nfree++
-				if v1.merge != nil {
-					v.merge = v1.merge
-				} else {
-					v.merge = v1
-				}
-				nkill++
-				break
-			}
-		}
-
-		// Sort v into inuse.
-		j := ninuse
-		ninuse++
-
-		for j > 0 && inuse[j-1].end < v.end {
-			inuse[j] = inuse[j-1]
-			j--
-		}
-
-		inuse[j] = v
-	}
-
-	if debugmerge > 0 && Debug['v'] != 0 {
-		fmt.Printf("%v [%d - %d]\n", Curfn.Func.Nname.Sym, len(vars), nkill)
-		for _, v := range vars {
-			fmt.Printf("var %v %v %d-%d", Nconv(v.node, FmtSharp), v.node.Type, v.start, v.end)
-			if v.addr {
-				fmt.Printf(" addr=true")
-			}
-			if v.removed {
-				fmt.Printf(" removed=true")
-			}
-			if v.merge != nil {
-				fmt.Printf(" merge %v", Nconv(v.merge.node, FmtSharp))
-			}
-			if v.start == v.end && v.def != nil {
-				fmt.Printf(" %v", v.def.Prog)
-			}
-			fmt.Printf("\n")
-		}
-
-		if debugmerge > 1 && Debug['v'] != 0 {
-			Dumpit("after", g.Start, 0)
-		}
-	}
-
-	// Update node references to use merged temporaries.
-	for f := g.Start; f != nil; f = f.Link {
-		p := f.Prog
-		n, _ := p.From.Node.(*Node)
-		if n != nil {
-			v, _ := n.Opt().(*TempVar)
-			if v != nil && v.merge != nil {
-				p.From.Node = v.merge.node
-			}
-		}
-		n, _ = p.To.Node.(*Node)
-		if n != nil {
-			v, _ := n.Opt().(*TempVar)
-			if v != nil && v.merge != nil {
-				p.To.Node = v.merge.node
-			}
-		}
-	}
-
-	// Delete merged nodes from declaration list.
-	dcl := make([]*Node, 0, len(Curfn.Func.Dcl)-nkill)
-	for _, n := range Curfn.Func.Dcl {
-		v, _ := n.Opt().(*TempVar)
-		if v != nil && (v.merge != nil || v.removed) {
-			continue
-		}
-		dcl = append(dcl, n)
-	}
-	Curfn.Func.Dcl = dcl
-
-	// Clear aux structures.
-	for _, v := range vars {
-		v.node.SetOpt(nil)
-	}
-
-	Flowend(g)
-}
-
-func mergewalk(v *TempVar, f0 *Flow, gen uint32) {
-	var p *obj.Prog
-	var f1 *Flow
-
-	for f1 = f0; f1 != nil; f1 = f1.P1 {
-		if uint32(f1.Active) == gen {
-			break
-		}
-		f1.Active = int32(gen)
-		p = f1.Prog
-		if v.end < p.Pc {
-			v.end = p.Pc
-		}
-		if f1 == v.def {
-			v.start = p.Pc
-			break
-		}
-	}
-
-	var f2 *Flow
-	for f := f0; f != f1; f = f.P1 {
-		for f2 = f.P2; f2 != nil; f2 = f2.P2link {
-			mergewalk(v, f2, gen)
-		}
-	}
-}
-
-func varkillwalk(v *TempVar, f0 *Flow, gen uint32) {
-	var p *obj.Prog
-	var f1 *Flow
-
-	for f1 = f0; f1 != nil; f1 = f1.S1 {
-		if uint32(f1.Active) == gen {
-			break
-		}
-		f1.Active = int32(gen)
-		p = f1.Prog
-		if v.end < p.Pc {
-			v.end = p.Pc
-		}
-		if v.start > p.Pc {
-			v.start = p.Pc
-		}
-		if p.As == obj.ARET || (p.As == obj.AVARKILL && p.To.Node == v.node) {
-			break
-		}
-	}
-
-	for f := f0; f != f1; f = f.S1 {
-		varkillwalk(v, f.S2, gen)
-	}
-}
-
-// Eliminate redundant nil pointer checks.
-//
-// The code generation pass emits a CHECKNIL for every possibly nil pointer.
-// This pass removes a CHECKNIL if every predecessor path has already
-// checked this value for nil.
-//
-// Simple backwards flood from check to definition.
-// Run prog loop backward from end of program to beginning to avoid quadratic
-// behavior removing a run of checks.
-//
-// Assume that stack variables with address not taken can be loaded multiple times
-// from memory without being rechecked. Other variables need to be checked on
-// each load.
-
-var killed int // f.Data is either nil or &killed
-
-func nilopt(firstp *obj.Prog) {
-	g := Flowstart(firstp, nil)
-	if g == nil {
-		return
-	}
-
-	if Debug_checknil > 1 { // || strcmp(curfn->nname->sym->name, "f1") == 0
-		Dumpit("nilopt", g.Start, 0)
-	}
-
-	ncheck := 0
-	nkill := 0
-	var p *obj.Prog
-	for f := g.Start; f != nil; f = f.Link {
-		p = f.Prog
-		if p.As != obj.ACHECKNIL || !Thearch.Regtyp(&p.From) {
-			continue
-		}
-		ncheck++
-		if Thearch.Stackaddr(&p.From) {
-			if Debug_checknil != 0 && p.Lineno > 1 {
-				Warnl(p.Lineno, "removed nil check of SP address")
-			}
-			f.Data = &killed
-			continue
-		}
-
-		nilwalkfwd(f)
-		if f.Data != nil {
-			if Debug_checknil != 0 && p.Lineno > 1 {
-				Warnl(p.Lineno, "removed nil check before indirect")
-			}
-			continue
-		}
-
-		nilwalkback(f)
-		if f.Data != nil {
-			if Debug_checknil != 0 && p.Lineno > 1 {
-				Warnl(p.Lineno, "removed repeated nil check")
-			}
-			continue
-		}
-	}
-
-	for f := g.Start; f != nil; f = f.Link {
-		if f.Data != nil {
-			nkill++
-			Thearch.Excise(f)
-		}
-	}
-
-	Flowend(g)
-
-	if Debug_checknil > 1 {
-		fmt.Printf("%v: removed %d of %d nil checks\n", Curfn.Func.Nname.Sym, nkill, ncheck)
-	}
-}
-
-func nilwalkback(fcheck *Flow) {
-	for f := fcheck; f != nil; f = Uniqp(f) {
-		p := f.Prog
-		if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) {
-			// Found initialization of value we're checking for nil.
-			// without first finding the check, so this one is unchecked.
-			return
-		}
-
-		if f != fcheck && p.As == obj.ACHECKNIL && Thearch.Sameaddr(&p.From, &fcheck.Prog.From) {
-			fcheck.Data = &killed
-			return
-		}
-	}
-}
-
-// Here is a more complex version that scans backward across branches.
-// It assumes fcheck->kill = 1 has been set on entry, and its job is to find a reason
-// to keep the check (setting fcheck->kill = 0).
-// It doesn't handle copying of aggregates as well as I would like,
-// nor variables with their address taken,
-// and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3.
-/*
-for(f1 = f0; f1 != nil; f1 = f1->p1) {
-	if(f1->active == gen)
-		break;
-	f1->active = gen;
-	p = f1->prog;
-
-	// If same check, stop this loop but still check
-	// alternate predecessors up to this point.
-	if(f1 != fcheck && p->as == ACHECKNIL && thearch.sameaddr(&p->from, &fcheck->prog->from))
-		break;
-
-	if((p.Info.flags & RightWrite) && thearch.sameaddr(&p->to, &fcheck->prog->from)) {
-		// Found initialization of value we're checking for nil.
-		// without first finding the check, so this one is unchecked.
-		fcheck->kill = 0;
-		return;
-	}
-
-	if(f1->p1 == nil && f1->p2 == nil) {
-		print("lost pred for %v\n", fcheck->prog);
-		for(f1=f0; f1!=nil; f1=f1->p1) {
-			thearch.proginfo(&info, f1->prog);
-			print("\t%v %d %d %D %D\n", r1->prog, info.flags&RightWrite, thearch.sameaddr(&f1->prog->to, &fcheck->prog->from), &f1->prog->to, &fcheck->prog->from);
-		}
-		fatal("lost pred trail");
-	}
-}
-
-for(f = f0; f != f1; f = f->p1)
-	for(f2 = f->p2; f2 != nil; f2 = f2->p2link)
-		nilwalkback(fcheck, f2, gen);
-*/
-
-func nilwalkfwd(fcheck *Flow) {
-	// If the path down from rcheck dereferences the address
-	// (possibly with a small offset) before writing to memory
-	// and before any subsequent checks, it's okay to wait for
-	// that implicit check. Only consider this basic block to
-	// avoid problems like:
-	//	_ = *x // should panic
-	//	for {} // no writes but infinite loop may be considered visible
-
-	var last *Flow
-	for f := Uniqs(fcheck); f != nil; f = Uniqs(f) {
-		p := f.Prog
-		if (p.Info.Flags&LeftRead != 0) && Thearch.Smallindir(&p.From, &fcheck.Prog.From) {
-			fcheck.Data = &killed
-			return
-		}
-
-		if (p.Info.Flags&(RightRead|RightWrite) != 0) && Thearch.Smallindir(&p.To, &fcheck.Prog.From) {
-			fcheck.Data = &killed
-			return
-		}
-
-		// Stop if another nil check happens.
-		if p.As == obj.ACHECKNIL {
-			return
-		}
-
-		// Stop if value is lost.
-		if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) {
-			return
-		}
-
-		// Stop if memory write.
-		if (p.Info.Flags&RightWrite != 0) && !Thearch.Regtyp(&p.To) {
-			return
-		}
-
-		// Stop if we jump backward.
-		if last != nil && f.Id <= last.Id {
-			return
-		}
-		last = f
-	}
-}
diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
index ad2bba9..bfb82b9 100644
--- a/src/cmd/compile/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/gc/racewalk.go
@@ -69,9 +69,10 @@ func instrument(fn *Node) {
 		nodpc.Type = Types[TUINTPTR]
 		nodpc.Xoffset = int64(-Widthptr)
 		nd := mkcall("racefuncenter", nil, nil, &nodpc)
-		fn.Func.Enter.Set(append([]*Node{nd}, fn.Func.Enter.Slice()...))
+		fn.Func.Enter.Prepend(nd)
 		nd = mkcall("racefuncexit", nil, nil)
 		fn.Func.Exit.Append(nd)
+		fn.Func.Dcl = append(fn.Func.Dcl, &nodpc)
 	}
 
 	if Debug['W'] != 0 {
@@ -144,36 +145,21 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
 		goto ret
 
 	case OBLOCK:
-		var out []*Node
 		ls := n.List.Slice()
-		for i := 0; i < len(ls); i++ {
-			switch ls[i].Op {
-			case OCALLFUNC, OCALLMETH, OCALLINTER:
-				instrumentnode(&ls[i], &ls[i].Ninit, 0, 0)
-				out = append(out, ls[i])
-				// Scan past OAS nodes copying results off stack.
-				// Those must not be instrumented, because the
-				// instrumentation calls will smash the results.
-				// The assignments are to temporaries, so they cannot
-				// be involved in races and need not be instrumented.
-				for i+1 < len(ls) && ls[i+1].Op == OAS && iscallret(ls[i+1].Right) {
-					i++
-					out = append(out, ls[i])
-				}
-			default:
-				var outn Nodes
-				outn.Set(out)
-				instrumentnode(&ls[i], &outn, 0, 0)
-				if ls[i].Op != OAS && ls[i].Op != OASWB && ls[i].Op != OAS2FUNC || ls[i].Ninit.Len() == 0 {
-					out = append(outn.Slice(), ls[i])
-				} else {
-					// Splice outn onto end of ls[i].Ninit
-					ls[i].Ninit.AppendNodes(&outn)
-					out = append(out, ls[i])
-				}
+		afterCall := false
+		for i := range ls {
+			op := ls[i].Op
+			// Scan past OAS nodes copying results off stack.
+			// Those must not be instrumented, because the
+			// instrumentation calls will smash the results.
+			// The assignments are to temporaries, so they cannot
+			// be involved in races and need not be instrumented.
+			if afterCall && op == OAS && iscallret(ls[i].Right) {
+				continue
 			}
+			instrumentnode(&ls[i], &ls[i].Ninit, 0, 0)
+			afterCall = (op == OCALLFUNC || op == OCALLMETH || op == OCALLINTER)
 		}
-		n.List.Set(out)
 		goto ret
 
 	case ODEFER:
@@ -188,7 +174,7 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
 		instrumentnode(&n.Left, init, 0, 0)
 		goto ret
 
-		// Instrument dst argument of runtime.writebarrier* calls
+	// Instrument dst argument of runtime.writebarrier* calls
 	// as we do not instrument runtime code.
 	// typedslicecopy is instrumented in runtime.
 	case OCALLFUNC:
@@ -229,9 +215,9 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
 	case OSPTR, OLEN, OCAP:
 		instrumentnode(&n.Left, init, 0, 0)
 		if n.Left.Type.IsMap() {
-			n1 := Nod(OCONVNOP, n.Left, nil)
-			n1.Type = Ptrto(Types[TUINT8])
-			n1 = Nod(OIND, n1, nil)
+			n1 := nod(OCONVNOP, n.Left, nil)
+			n1.Type = ptrto(Types[TUINT8])
+			n1 = nod(OIND, n1, nil)
 			n1 = typecheck(n1, Erv)
 			callinstr(&n1, init, 0, skip)
 		}
@@ -314,11 +300,6 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
 		n.SetSliceBounds(low, high, max)
 		goto ret
 
-	case OKEY:
-		instrumentnode(&n.Left, init, 0, 0)
-		instrumentnode(&n.Right, init, 0, 0)
-		goto ret
-
 	case OADDR:
 		instrumentnode(&n.Left, init, 0, 1)
 		goto ret
@@ -329,7 +310,20 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
 
 		goto ret
 
-	case OITAB:
+	case OITAB, OIDATA:
+		instrumentnode(&n.Left, init, 0, 0)
+		goto ret
+
+	case OSTRARRAYBYTETMP:
+		instrumentnode(&n.Left, init, 0, 0)
+		goto ret
+
+	case OAS2DOTTYPE:
+		instrumentnode(&n.Left, init, 1, 0)
+		instrumentnode(&n.Right, init, 0, 0)
+		goto ret
+
+	case ODOTTYPE, ODOTTYPE2:
 		instrumentnode(&n.Left, init, 0, 0)
 		goto ret
 
@@ -360,31 +354,29 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
 		// lowered to call
 		OCMPSTR,
 		OADDSTR,
-		ODOTTYPE,
-		ODOTTYPE2,
-		OAS2DOTTYPE,
 		OCALLPART,
 		// lowered to PTRLIT
 		OCLOSURE,  // lowered to PTRLIT
 		ORANGE,    // lowered to ordinary for loop
 		OARRAYLIT, // lowered to assignments
+		OSLICELIT,
 		OMAPLIT,
 		OSTRUCTLIT,
 		OAS2,
 		OAS2RECV,
 		OAS2MAPR,
 		OASOP:
-		Yyerror("instrument: %v must be lowered by now", n.Op)
+		yyerror("instrument: %v must be lowered by now", n.Op)
 
 		goto ret
 
 		// impossible nodes: only appear in backend.
 	case ORROTC, OEXTEND:
-		Yyerror("instrument: %v cannot exist now", n.Op)
+		yyerror("instrument: %v cannot exist now", n.Op)
 		goto ret
 
 	case OGETG:
-		Yyerror("instrument: OGETG can happen only in runtime which we don't instrument")
+		yyerror("instrument: OGETG can happen only in runtime which we don't instrument")
 		goto ret
 
 	case OFOR:
@@ -421,7 +413,7 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
 		OCHECKNIL,   // always followed by a read.
 		OCLOSUREVAR, // immutable pointer to captured variable
 		ODOTMETH,    // either part of CALLMETH or CALLPART (lowered to PTRLIT)
-		OINDREG,     // at this stage, only n(SP) nodes from nodarg
+		OINDREGSP,   // at this stage, only n(SP) nodes from nodarg
 		ODCL,        // declarations (without value) cannot be races
 		ODCLCONST,
 		ODCLTYPE,
@@ -451,7 +443,7 @@ func isartificial(n *Node) bool {
 		}
 
 		// autotmp's are always local
-		if strings.HasPrefix(n.Sym.Name, "autotmp_") {
+		if n.IsAutoTmp() {
 			return true
 		}
 
@@ -472,8 +464,8 @@ func isartificial(n *Node) bool {
 func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
 	n := *np
 
-	//print("callinstr for %+N [ %O ] etype=%E class=%d\n",
-	//	  n, n->op, n->type ? n->type->etype : -1, n->class);
+	//fmt.Printf("callinstr for %v [ %v ] etype=%v class=%v\n",
+	//	n, n.Op, n.Type.Etype, n.Class)
 
 	if skip != 0 || n.Type == nil || n.Type.Etype >= TIDEAL {
 		return false
@@ -517,7 +509,7 @@ func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
 			if w == BADWIDTH {
 				Fatalf("instrument: %v badwidth", t)
 			}
-			f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(w))
+			f = mkcall(name, nil, init, uintptraddr(n), nodintconst(w))
 		} else if flag_race && (t.IsStruct() || t.IsArray()) {
 			name := "racereadrange"
 			if wr != 0 {
@@ -529,7 +521,7 @@ func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
 			if w == BADWIDTH {
 				Fatalf("instrument: %v badwidth", t)
 			}
-			f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(w))
+			f = mkcall(name, nil, init, uintptraddr(n), nodintconst(w))
 		} else if flag_race {
 			name := "raceread"
 			if wr != 0 {
@@ -577,7 +569,7 @@ func makeaddable(n *Node) {
 }
 
 func uintptraddr(n *Node) *Node {
-	r := Nod(OADDR, n, nil)
+	r := nod(OADDR, n, nil)
 	r.Bounded = true
 	r = conv(r, Types[TUNSAFEPTR])
 	r = conv(r, Types[TUINTPTR])
@@ -585,13 +577,13 @@ func uintptraddr(n *Node) *Node {
 }
 
 func detachexpr(n *Node, init *Nodes) *Node {
-	addr := Nod(OADDR, n, nil)
-	l := temp(Ptrto(n.Type))
-	as := Nod(OAS, l, addr)
+	addr := nod(OADDR, n, nil)
+	l := temp(ptrto(n.Type))
+	as := nod(OAS, l, addr)
 	as = typecheck(as, Etop)
 	as = walkexpr(as, init)
 	init.Append(as)
-	ind := Nod(OIND, l, nil)
+	ind := nod(OIND, l, nil)
 	ind = typecheck(ind, Erv)
 	ind = walkexpr(ind, init)
 	return ind
@@ -637,7 +629,7 @@ func appendinit(np **Node, init Nodes) {
 	// There may be multiple refs to this node;
 	// introduce OCONVNOP to hold init list.
 	case ONAME, OLITERAL:
-		n = Nod(OCONVNOP, n, nil)
+		n = nod(OCONVNOP, n, nil)
 
 		n.Type = n.Left.Type
 		n.Typecheck = 1
diff --git a/src/cmd/compile/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go
index 9d3f79c..b590474 100644
--- a/src/cmd/compile/internal/gc/range.go
+++ b/src/cmd/compile/internal/gc/range.go
@@ -4,6 +4,8 @@
 
 package gc
 
+import "unicode/utf8"
+
 // range
 func typecheckrange(n *Node) {
 	var toomany int
@@ -46,7 +48,7 @@ func typecheckrange(n *Node) {
 	toomany = 0
 	switch t.Etype {
 	default:
-		Yyerror("cannot range over %v", Nconv(n.Right, FmtLong))
+		yyerror("cannot range over %L", n.Right)
 		goto out
 
 	case TARRAY, TSLICE:
@@ -59,7 +61,7 @@ func typecheckrange(n *Node) {
 
 	case TCHAN:
 		if !t.ChanDir().CanRecv() {
-			Yyerror("invalid operation: range %v (receive from send-only type %v)", n.Right, n.Right.Type)
+			yyerror("invalid operation: range %v (receive from send-only type %v)", n.Right, n.Right.Type)
 			goto out
 		}
 
@@ -75,7 +77,7 @@ func typecheckrange(n *Node) {
 	}
 
 	if n.List.Len() > 2 || toomany != 0 {
-		Yyerror("too many variables in range")
+		yyerror("too many variables in range")
 	}
 
 	v1 = nil
@@ -102,7 +104,7 @@ func typecheckrange(n *Node) {
 		if v1.Name != nil && v1.Name.Defn == n {
 			v1.Type = t1
 		} else if v1.Type != nil && assignop(t1, v1.Type, &why) == 0 {
-			Yyerror("cannot assign type %v to %v in range%s", t1, Nconv(v1, FmtLong), why)
+			yyerror("cannot assign type %v to %L in range%s", t1, v1, why)
 		}
 		checkassign(n, v1)
 	}
@@ -111,7 +113,7 @@ func typecheckrange(n *Node) {
 		if v2.Name != nil && v2.Name.Defn == n {
 			v2.Type = t2
 		} else if v2.Type != nil && assignop(t2, v2.Type, &why) == 0 {
-			Yyerror("cannot assign type %v to %v in range%s", t2, Nconv(v2, FmtLong), why)
+			yyerror("cannot assign type %v to %L in range%s", t2, v2, why)
 		}
 		checkassign(n, v2)
 	}
@@ -177,25 +179,25 @@ func walkrange(n *Node) {
 		hn := temp(Types[TINT])
 		var hp *Node
 
-		init = append(init, Nod(OAS, hv1, nil))
-		init = append(init, Nod(OAS, hn, Nod(OLEN, ha, nil)))
+		init = append(init, nod(OAS, hv1, nil))
+		init = append(init, nod(OAS, hn, nod(OLEN, ha, nil)))
 		if v2 != nil {
-			hp = temp(Ptrto(n.Type.Elem()))
-			tmp := Nod(OINDEX, ha, Nodintconst(0))
+			hp = temp(ptrto(n.Type.Elem()))
+			tmp := nod(OINDEX, ha, nodintconst(0))
 			tmp.Bounded = true
-			init = append(init, Nod(OAS, hp, Nod(OADDR, tmp, nil)))
+			init = append(init, nod(OAS, hp, nod(OADDR, tmp, nil)))
 		}
 
-		n.Left = Nod(OLT, hv1, hn)
-		n.Right = Nod(OAS, hv1, Nod(OADD, hv1, Nodintconst(1)))
+		n.Left = nod(OLT, hv1, hn)
+		n.Right = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1)))
 		if v1 == nil {
 			body = nil
 		} else if v2 == nil {
-			body = []*Node{Nod(OAS, v1, hv1)}
+			body = []*Node{nod(OAS, v1, hv1)}
 		} else {
-			a := Nod(OAS2, nil, nil)
+			a := nod(OAS2, nil, nil)
 			a.List.Set([]*Node{v1, v2})
-			a.Rlist.Set([]*Node{hv1, Nod(OIND, hp, nil)})
+			a.Rlist.Set([]*Node{hv1, nod(OIND, hp, nil)})
 			body = []*Node{a}
 
 			// Advance pointer as part of increment.
@@ -206,13 +208,13 @@ func walkrange(n *Node) {
 			// Advancing during the increment ensures that the pointer p only points
 			// pass the end of the array during the final "p++; i++; if(i >= len(x)) break;",
 			// after which p is dead, so it cannot confuse the collector.
-			tmp := Nod(OADD, hp, Nodintconst(t.Elem().Width))
+			tmp := nod(OADD, hp, nodintconst(t.Elem().Width))
 
 			tmp.Type = hp.Type
 			tmp.Typecheck = 1
 			tmp.Right.Type = Types[Tptr]
 			tmp.Right.Typecheck = 1
-			a = Nod(OAS, hp, tmp)
+			a = nod(OAS, hp, tmp)
 			a = typecheck(a, Etop)
 			n.Right.Ninit.Set1(a)
 		}
@@ -232,23 +234,23 @@ func walkrange(n *Node) {
 		fn := syslook("mapiterinit")
 
 		fn = substArgTypes(fn, t.Key(), t.Val(), th)
-		init = append(init, mkcall1(fn, nil, nil, typename(t), ha, Nod(OADDR, hit, nil)))
-		n.Left = Nod(ONE, NodSym(ODOT, hit, keysym), nodnil())
+		init = append(init, mkcall1(fn, nil, nil, typename(t), ha, nod(OADDR, hit, nil)))
+		n.Left = nod(ONE, nodSym(ODOT, hit, keysym), nodnil())
 
 		fn = syslook("mapiternext")
 		fn = substArgTypes(fn, th)
-		n.Right = mkcall1(fn, nil, nil, Nod(OADDR, hit, nil))
+		n.Right = mkcall1(fn, nil, nil, nod(OADDR, hit, nil))
 
-		key := NodSym(ODOT, hit, keysym)
-		key = Nod(OIND, key, nil)
+		key := nodSym(ODOT, hit, keysym)
+		key = nod(OIND, key, nil)
 		if v1 == nil {
 			body = nil
 		} else if v2 == nil {
-			body = []*Node{Nod(OAS, v1, key)}
+			body = []*Node{nod(OAS, v1, key)}
 		} else {
-			val := NodSym(ODOT, hit, valsym)
-			val = Nod(OIND, val, nil)
-			a := Nod(OAS2, nil, nil)
+			val := nodSym(ODOT, hit, valsym)
+			val = nod(OIND, val, nil)
+			a := nod(OAS2, nil, nil)
 			a.List.Set([]*Node{v1, v2})
 			a.Rlist.Set([]*Node{key, val})
 			body = []*Node{a}
@@ -263,56 +265,85 @@ func walkrange(n *Node) {
 		hv1 := temp(t.Elem())
 		hv1.Typecheck = 1
 		if haspointers(t.Elem()) {
-			init = append(init, Nod(OAS, hv1, nil))
+			init = append(init, nod(OAS, hv1, nil))
 		}
 		hb := temp(Types[TBOOL])
 
-		n.Left = Nod(ONE, hb, Nodbool(false))
-		a := Nod(OAS2RECV, nil, nil)
+		n.Left = nod(ONE, hb, nodbool(false))
+		a := nod(OAS2RECV, nil, nil)
 		a.Typecheck = 1
 		a.List.Set([]*Node{hv1, hb})
-		a.Rlist.Set1(Nod(ORECV, ha, nil))
+		a.Rlist.Set1(nod(ORECV, ha, nil))
 		n.Left.Ninit.Set1(a)
 		if v1 == nil {
 			body = nil
 		} else {
-			body = []*Node{Nod(OAS, v1, hv1)}
+			body = []*Node{nod(OAS, v1, hv1)}
 		}
 		// Zero hv1. This prevents hv1 from being the sole, inaccessible
 		// reference to an otherwise GC-able value during the next channel receive.
 		// See issue 15281.
-		body = append(body, Nod(OAS, hv1, nil))
+		body = append(body, nod(OAS, hv1, nil))
 
 	case TSTRING:
+		// Transform string range statements like "for v1, v2 = range a" into
+		//
+		// ha := a
+		// for hv1 := 0; hv1 < len(ha); {
+		//   v1 = hv1
+		//   hv2 := rune(ha[hv1])
+		//   if hv2 < utf8.RuneSelf {
+		//      hv1++
+		//   } else {
+		//      hv2, hv1 = decoderune(ha, hv1)
+		//   }
+		//   v2 = hv2
+		//   // original body
+		// }
+
 		// orderstmt arranged for a copy of the string variable.
 		ha := a
 
-		ohv1 := temp(Types[TINT])
-
 		hv1 := temp(Types[TINT])
-		init = append(init, Nod(OAS, hv1, nil))
+		hv2 := temp(runetype)
 
-		var a *Node
-		var hv2 *Node
-		if v2 == nil {
-			a = Nod(OAS, hv1, mkcall("stringiter", Types[TINT], nil, ha, hv1))
-		} else {
-			hv2 = temp(runetype)
-			a = Nod(OAS2, nil, nil)
-			a.List.Set([]*Node{hv1, hv2})
-			fn := syslook("stringiter2")
-			a.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, ha, hv1))
-		}
+		// hv1 := 0
+		init = append(init, nod(OAS, hv1, nil))
 
-		n.Left = Nod(ONE, hv1, Nodintconst(0))
-		n.Left.Ninit.Set([]*Node{Nod(OAS, ohv1, hv1), a})
+		// hv1 < len(ha)
+		n.Left = nod(OLT, hv1, nod(OLEN, ha, nil))
 
-		body = nil
 		if v1 != nil {
-			body = []*Node{Nod(OAS, v1, ohv1)}
+			// v1 = hv1
+			body = append(body, nod(OAS, v1, hv1))
 		}
+
+		// hv2 := ha[hv1]
+		nind := nod(OINDEX, ha, hv1)
+		nind.Bounded = true
+		body = append(body, nod(OAS, hv2, conv(nind, runetype)))
+
+		// if hv2 < utf8.RuneSelf
+		nif := nod(OIF, nil, nil)
+		nif.Left = nod(OLT, nind, nodintconst(utf8.RuneSelf))
+
+		// hv1++
+		nif.Nbody.Set1(nod(OAS, hv1, nod(OADD, hv1, nodintconst(1))))
+
+		// } else {
+		eif := nod(OAS2, nil, nil)
+		nif.Rlist.Set1(eif)
+
+		// hv2, hv1 = decoderune(ha, hv1)
+		eif.List.Set2(hv2, hv1)
+		fn := syslook("decoderune")
+		eif.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, ha, hv1))
+
+		body = append(body, nif)
+
 		if v2 != nil {
-			body = append(body, Nod(OAS, v2, hv2))
+			// v2 = hv2
+			body = append(body, nod(OAS, v2, hv2))
 		}
 	}
 
@@ -323,7 +354,7 @@ func walkrange(n *Node) {
 	n.Left = typecheck(n.Left, Erv)
 	n.Right = typecheck(n.Right, Etop)
 	typecheckslice(body, Etop)
-	n.Nbody.Set(append(body, n.Nbody.Slice()...))
+	n.Nbody.Prepend(body...)
 	n = walkstmt(n)
 
 	lineno = lno
@@ -366,39 +397,45 @@ func memclrrange(n, v1, v2, a *Node) bool {
 	// if len(a) != 0 {
 	// 	hp = &a[0]
 	// 	hn = len(a)*sizeof(elem(a))
-	// 	memclr(hp, hn)
+	// 	memclr{NoHeap,Has}Pointers(hp, hn)
 	// 	i = len(a) - 1
 	// }
 	n.Op = OIF
 
 	n.Nbody.Set(nil)
-	n.Left = Nod(ONE, Nod(OLEN, a, nil), Nodintconst(0))
+	n.Left = nod(ONE, nod(OLEN, a, nil), nodintconst(0))
 
 	// hp = &a[0]
-	hp := temp(Ptrto(Types[TUINT8]))
+	hp := temp(ptrto(Types[TUINT8]))
 
-	tmp := Nod(OINDEX, a, Nodintconst(0))
+	tmp := nod(OINDEX, a, nodintconst(0))
 	tmp.Bounded = true
-	tmp = Nod(OADDR, tmp, nil)
-	tmp = Nod(OCONVNOP, tmp, nil)
-	tmp.Type = Ptrto(Types[TUINT8])
-	n.Nbody.Append(Nod(OAS, hp, tmp))
+	tmp = nod(OADDR, tmp, nil)
+	tmp = nod(OCONVNOP, tmp, nil)
+	tmp.Type = ptrto(Types[TUINT8])
+	n.Nbody.Append(nod(OAS, hp, tmp))
 
 	// hn = len(a) * sizeof(elem(a))
 	hn := temp(Types[TUINTPTR])
 
-	tmp = Nod(OLEN, a, nil)
-	tmp = Nod(OMUL, tmp, Nodintconst(elemsize))
+	tmp = nod(OLEN, a, nil)
+	tmp = nod(OMUL, tmp, nodintconst(elemsize))
 	tmp = conv(tmp, Types[TUINTPTR])
-	n.Nbody.Append(Nod(OAS, hn, tmp))
-
-	// memclr(hp, hn)
-	fn := mkcall("memclr", nil, nil, hp, hn)
+	n.Nbody.Append(nod(OAS, hn, tmp))
+
+	var fn *Node
+	if haspointers(a.Type.Elem()) {
+		// memclrHasPointers(hp, hn)
+		fn = mkcall("memclrHasPointers", nil, nil, hp, hn)
+	} else {
+		// memclrNoHeapPointers(hp, hn)
+		fn = mkcall("memclrNoHeapPointers", nil, nil, hp, hn)
+	}
 
 	n.Nbody.Append(fn)
 
 	// i = len(a) - 1
-	v1 = Nod(OAS, v1, Nod(OSUB, Nod(OLEN, a, nil), Nodintconst(1)))
+	v1 = nod(OAS, v1, nod(OSUB, nod(OLEN, a, nil), nodintconst(1)))
 
 	n.Nbody.Append(v1)
 
diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
index cff1acc..4f9d92e 100644
--- a/src/cmd/compile/internal/gc/reflect.go
+++ b/src/cmd/compile/internal/gc/reflect.go
@@ -18,9 +18,15 @@ type itabEntry struct {
 	sym      *Sym
 }
 
+type ptabEntry struct {
+	s *Sym
+	t *Type
+}
+
 // runtime interface and reflection data structures
 var signatlist []*Node
 var itabs []itabEntry
+var ptabs []ptabEntry
 
 type Sig struct {
 	name   string
@@ -96,10 +102,10 @@ func mapbucket(t *Type) *Type {
 	dowidth(keytype)
 	dowidth(valtype)
 	if keytype.Width > MAXKEYSIZE {
-		keytype = Ptrto(keytype)
+		keytype = ptrto(keytype)
 	}
 	if valtype.Width > MAXVALSIZE {
-		valtype = Ptrto(valtype)
+		valtype = ptrto(valtype)
 	}
 
 	field := make([]*Field, 0, 5)
@@ -143,7 +149,7 @@ func mapbucket(t *Type) *Type {
 	// Arrange for the bucket to have no pointers by changing
 	// the type of the overflow field to uintptr in this case.
 	// See comment on hmap.overflow in ../../../../runtime/hashmap.go.
-	otyp := Ptrto(bucket)
+	otyp := ptrto(bucket)
 	if !haspointers(t.Val()) && !haspointers(t.Key()) && t.Val().Width <= MAXVALSIZE && t.Key().Width <= MAXKEYSIZE {
 		otyp = Types[TUINTPTR]
 	}
@@ -159,7 +165,7 @@ func mapbucket(t *Type) *Type {
 	// Double-check that overflow field is final memory in struct,
 	// with no padding at end. See comment above.
 	if ovf.Offset != bucket.Width-int64(Widthptr) {
-		Yyerror("bad math in mapbucket for %v", t)
+		yyerror("bad math in mapbucket for %v", t)
 	}
 
 	t.MapType().Bucket = bucket
@@ -176,20 +182,22 @@ func hmap(t *Type) *Type {
 	}
 
 	bucket := mapbucket(t)
-	var field [8]*Field
-	field[0] = makefield("count", Types[TINT])
-	field[1] = makefield("flags", Types[TUINT8])
-	field[2] = makefield("B", Types[TUINT8])
-	field[3] = makefield("hash0", Types[TUINT32])
-	field[4] = makefield("buckets", Ptrto(bucket))
-	field[5] = makefield("oldbuckets", Ptrto(bucket))
-	field[6] = makefield("nevacuate", Types[TUINTPTR])
-	field[7] = makefield("overflow", Types[TUNSAFEPTR])
+	fields := []*Field{
+		makefield("count", Types[TINT]),
+		makefield("flags", Types[TUINT8]),
+		makefield("B", Types[TUINT8]),
+		makefield("noverflow", Types[TUINT16]),
+		makefield("hash0", Types[TUINT32]),
+		makefield("buckets", ptrto(bucket)),
+		makefield("oldbuckets", ptrto(bucket)),
+		makefield("nevacuate", Types[TUINTPTR]),
+		makefield("overflow", Types[TUNSAFEPTR]),
+	}
 
 	h := typ(TSTRUCT)
 	h.Noalg = true
 	h.Local = t.Local
-	h.SetFields(field[:])
+	h.SetFields(fields)
 	dowidth(h)
 	t.MapType().Hmap = h
 	h.StructType().Map = t
@@ -218,12 +226,12 @@ func hiter(t *Type) *Type {
 	// }
 	// must match ../../../../runtime/hashmap.go:hiter.
 	var field [12]*Field
-	field[0] = makefield("key", Ptrto(t.Key()))
-	field[1] = makefield("val", Ptrto(t.Val()))
-	field[2] = makefield("t", Ptrto(Types[TUINT8]))
-	field[3] = makefield("h", Ptrto(hmap(t)))
-	field[4] = makefield("buckets", Ptrto(mapbucket(t)))
-	field[5] = makefield("bptr", Ptrto(mapbucket(t)))
+	field[0] = makefield("key", ptrto(t.Key()))
+	field[1] = makefield("val", ptrto(t.Val()))
+	field[2] = makefield("t", ptrto(Types[TUINT8]))
+	field[3] = makefield("h", ptrto(hmap(t)))
+	field[4] = makefield("buckets", ptrto(mapbucket(t)))
+	field[5] = makefield("bptr", ptrto(mapbucket(t)))
 	field[6] = makefield("overflow0", Types[TUNSAFEPTR])
 	field[7] = makefield("overflow1", Types[TUNSAFEPTR])
 	field[8] = makefield("startBucket", Types[TUINTPTR])
@@ -237,7 +245,7 @@ func hiter(t *Type) *Type {
 	i.SetFields(field[:])
 	dowidth(i)
 	if i.Width != int64(12*Widthptr) {
-		Yyerror("hash_iter size not correct %d %d", i.Width, 12*Widthptr)
+		yyerror("hash_iter size not correct %d %d", i.Width, 12*Widthptr)
 	}
 	t.MapType().Hiter = i
 	i.StructType().Map = t
@@ -249,14 +257,14 @@ func hiter(t *Type) *Type {
 func methodfunc(f *Type, receiver *Type) *Type {
 	var in []*Node
 	if receiver != nil {
-		d := Nod(ODCLFIELD, nil, nil)
+		d := nod(ODCLFIELD, nil, nil)
 		d.Type = receiver
 		in = append(in, d)
 	}
 
 	var d *Node
 	for _, t := range f.Params().Fields().Slice() {
-		d = Nod(ODCLFIELD, nil, nil)
+		d = nod(ODCLFIELD, nil, nil)
 		d.Type = t.Type
 		d.Isddd = t.Isddd
 		in = append(in, d)
@@ -264,7 +272,7 @@ func methodfunc(f *Type, receiver *Type) *Type {
 
 	var out []*Node
 	for _, t := range f.Results().Fields().Slice() {
-		d = Nod(ODCLFIELD, nil, nil)
+		d = nod(ODCLFIELD, nil, nil)
 		d.Type = t.Type
 		out = append(out, d)
 	}
@@ -282,7 +290,7 @@ func methodfunc(f *Type, receiver *Type) *Type {
 // Generates stub functions as needed.
 func methods(t *Type) []*Sig {
 	// method type
-	mt := methtype(t, 0)
+	mt := methtype(t)
 
 	if mt == nil {
 		return nil
@@ -293,7 +301,7 @@ func methods(t *Type) []*Sig {
 	it := t
 
 	if !isdirectiface(it) {
-		it = Ptrto(t)
+		it = ptrto(t)
 	}
 
 	// make list of methods for t,
@@ -346,7 +354,7 @@ func methods(t *Type) []*Sig {
 
 		if sig.isym.Flags&SymSiggen == 0 {
 			sig.isym.Flags |= SymSiggen
-			if !Eqtype(this, it) || this.Width < Types[Tptr].Width {
+			if !eqtype(this, it) || this.Width < Types[Tptr].Width {
 				compiling_wrappers = 1
 				genwrapper(it, f, sig.isym, 1)
 				compiling_wrappers = 0
@@ -355,7 +363,7 @@ func methods(t *Type) []*Sig {
 
 		if sig.tsym.Flags&SymSiggen == 0 {
 			sig.tsym.Flags |= SymSiggen
-			if !Eqtype(this, t) {
+			if !eqtype(this, t) {
 				compiling_wrappers = 1
 				genwrapper(t, f, sig.tsym, 0)
 				compiling_wrappers = 0
@@ -486,26 +494,31 @@ func dgopkgpathOffLSym(s *obj.LSym, ot int, pkg *Pkg) int {
 }
 
 // isExportedField reports whether a struct field is exported.
-func isExportedField(ft *Field) bool {
+// It also returns the package to use for PkgPath for an unexported field.
+func isExportedField(ft *Field) (bool, *Pkg) {
 	if ft.Sym != nil && ft.Embedded == 0 {
-		return exportname(ft.Sym.Name)
+		return exportname(ft.Sym.Name), ft.Sym.Pkg
 	} else {
 		if ft.Type.Sym != nil &&
 			(ft.Type.Sym.Pkg == builtinpkg || !exportname(ft.Type.Sym.Name)) {
-			return false
+			return false, ft.Type.Sym.Pkg
 		} else {
-			return true
+			return true, nil
 		}
 	}
 }
 
 // dnameField dumps a reflect.name for a struct field.
-func dnameField(s *Sym, ot int, ft *Field) int {
+func dnameField(s *Sym, ot int, spkg *Pkg, ft *Field) int {
 	var name string
 	if ft.Sym != nil && ft.Embedded == 0 {
 		name = ft.Sym.Name
 	}
-	nsym := dname(name, ft.Note, nil, isExportedField(ft))
+	isExported, fpkg := isExportedField(ft)
+	if isExported || fpkg == spkg {
+		fpkg = nil
+	}
+	nsym := dname(name, ft.Note, fpkg, isExported)
 	return dsymptrLSym(Linksym(s), ot, nsym, 0)
 }
 
@@ -595,7 +608,7 @@ func dextratype(s *Sym, ot int, t *Type, dataAdd int) int {
 	}
 	noff := int(Rnd(int64(ot), int64(Widthptr)))
 	if noff != ot {
-		Fatalf("unexpected alignment in dextratype for %s", t)
+		Fatalf("unexpected alignment in dextratype for %v", t)
 	}
 
 	for _, a := range m {
@@ -607,10 +620,10 @@ func dextratype(s *Sym, ot int, t *Type, dataAdd int) int {
 	dataAdd += uncommonSize(t)
 	mcount := len(m)
 	if mcount != int(uint16(mcount)) {
-		Fatalf("too many methods on %s: %d", t, mcount)
+		Fatalf("too many methods on %v: %d", t, mcount)
 	}
 	if dataAdd != int(uint32(dataAdd)) {
-		Fatalf("methods are too far away on %s: %d", t, dataAdd)
+		Fatalf("methods are too far away on %v: %d", t, dataAdd)
 	}
 
 	ot = duint16(s, ot, uint16(mcount))
@@ -821,9 +834,13 @@ func dcommontype(s *Sym, ot int, t *Type) int {
 		algsym = dalgsym(t)
 	}
 
+	sptrWeak := true
 	var sptr *Sym
-	tptr := Ptrto(t)
-	if !t.IsPtr() && (t.Sym != nil || methods(tptr) != nil) {
+	if !t.IsPtr() || t.ptrTo != nil {
+		tptr := ptrto(t)
+		if t.Sym != nil || methods(tptr) != nil {
+			sptrWeak = false
+		}
 		sptr = dtypesym(tptr)
 	}
 
@@ -858,7 +875,7 @@ func dcommontype(s *Sym, ot int, t *Type) int {
 	}
 
 	exported := false
-	p := Tconv(t, FmtLeft|FmtUnsigned)
+	p := t.tconv(FmtLeft | FmtUnsigned)
 	// If we're writing out type T,
 	// we are very likely to write out type *T as well.
 	// Use the string "*T"[1:] for "T", so that the two
@@ -910,32 +927,37 @@ func dcommontype(s *Sym, ot int, t *Type) int {
 
 	nsym := dname(p, "", nil, exported)
 	ot = dsymptrOffLSym(Linksym(s), ot, nsym, 0) // str
+	// ptrToThis
 	if sptr == nil {
 		ot = duint32(s, ot, 0)
+	} else if sptrWeak {
+		ot = dsymptrWeakOffLSym(Linksym(s), ot, Linksym(sptr))
 	} else {
-		ot = dsymptrOffLSym(Linksym(s), ot, Linksym(sptr), 0) // ptrToThis
+		ot = dsymptrOffLSym(Linksym(s), ot, Linksym(sptr), 0)
 	}
 
 	return ot
 }
 
 func typesym(t *Type) *Sym {
-	return Pkglookup(Tconv(t, FmtLeft), typepkg)
+	name := t.tconv(FmtLeft)
+
+	// Use a separate symbol name for Noalg types for #17752.
+	if a, bad := algtype1(t); a == ANOEQ && bad.Noalg {
+		name = "noalg." + name
+	}
+
+	return Pkglookup(name, typepkg)
 }
 
 // tracksym returns the symbol for tracking use of field/method f, assumed
 // to be a member of struct/interface type t.
 func tracksym(t *Type, f *Field) *Sym {
-	return Pkglookup(Tconv(t, FmtLeft)+"."+f.Sym.Name, trackpkg)
-}
-
-func typelinkLSym(t *Type) *obj.LSym {
-	name := "go.typelink." + Tconv(t, FmtLeft) // complete, unambiguous type name
-	return obj.Linklookup(Ctxt, name, 0)
+	return Pkglookup(t.tconv(FmtLeft)+"."+f.Sym.Name, trackpkg)
 }
 
 func typesymprefix(prefix string, t *Type) *Sym {
-	p := prefix + "." + Tconv(t, FmtLeft)
+	p := prefix + "." + t.tconv(FmtLeft)
 	s := Pkglookup(p, typepkg)
 
 	//print("algsym: %s -> %+S\n", p, s);
@@ -963,8 +985,8 @@ func typenamesym(t *Type) *Sym {
 
 func typename(t *Type) *Node {
 	s := typenamesym(t)
-	n := Nod(OADDR, s.Def, nil)
-	n.Type = Ptrto(s.Def.Type)
+	n := nod(OADDR, s.Def, nil)
+	n.Type = ptrto(s.Def.Type)
 	n.Addable = true
 	n.Ullman = 2
 	n.Typecheck = 1
@@ -972,10 +994,11 @@ func typename(t *Type) *Node {
 }
 
 func itabname(t, itype *Type) *Node {
-	if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() {
-		Fatalf("itabname %v", t)
+	if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() || !itype.IsInterface() || itype.IsEmptyInterface() {
+		Fatalf("itabname(%v, %v)", t, itype)
 	}
-	s := Pkglookup(Tconv(t, FmtLeft)+","+Tconv(itype, FmtLeft), itabpkg)
+	s := Pkglookup(t.tconv(FmtLeft)+","+itype.tconv(FmtLeft), itabpkg)
+	Linksym(s).Set(obj.AttrLocal, true)
 	if s.Def == nil {
 		n := newname(s)
 		n.Type = Types[TUINT8]
@@ -986,8 +1009,8 @@ func itabname(t, itype *Type) *Node {
 		itabs = append(itabs, itabEntry{t: t, itype: itype, sym: s})
 	}
 
-	n := Nod(OADDR, s.Def, nil)
-	n.Type = Ptrto(s.Def.Type)
+	n := nod(OADDR, s.Def, nil)
+	n.Type = ptrto(s.Def.Type)
 	n.Addable = true
 	n.Ullman = 2
 	n.Typecheck = 1
@@ -1301,6 +1324,15 @@ ok:
 		pkg := localpkg
 		if t.Sym != nil {
 			pkg = t.Sym.Pkg
+		} else {
+			// Unnamed type. Grab the package from the first field, if any.
+			for _, f := range t.Fields().Slice() {
+				if f.Embedded != 0 {
+					continue
+				}
+				pkg = f.Sym.Pkg
+				break
+			}
 		}
 		ot = dgopkgpath(s, ot, pkg)
 		ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+uncommonSize(t))
@@ -1312,7 +1344,7 @@ ok:
 
 		for _, f := range t.Fields().Slice() {
 			// ../../../../runtime/type.go:/structField
-			ot = dnameField(s, ot, f)
+			ot = dnameField(s, ot, pkg, f)
 			ot = dsymptr(s, ot, dtypesym(f.Type), 0)
 			ot = duintptr(s, ot, uint64(f.Offset))
 		}
@@ -1321,8 +1353,6 @@ ok:
 	ot = dextratypeData(s, ot, t)
 	ggloblsym(s, int32(ot), int16(dupok|obj.RODATA))
 
-	// generate typelink.foo pointing at s = type.foo.
-	//
 	// The linker will leave a table of all the typelinks for
 	// types in the binary, so the runtime can find them.
 	//
@@ -1340,11 +1370,7 @@ ok:
 			keep = true
 		}
 	}
-	if keep {
-		slink := typelinkLSym(t)
-		dsymptrOffLSym(slink, 0, Linksym(s), 0)
-		ggloblLSym(slink, 4, int16(dupok|obj.RODATA))
-	}
+	s.Lsym.Set(obj.AttrMakeTypelink, keep)
 
 	return s
 }
@@ -1368,7 +1394,7 @@ func dumptypestructs() {
 		t := n.Type
 		dtypesym(t)
 		if t.Sym != nil {
-			dtypesym(Ptrto(t))
+			dtypesym(ptrto(t))
 		}
 	}
 
@@ -1389,11 +1415,36 @@ func dumptypestructs() {
 		o += len(imethods(i.itype)) * Widthptr // skip fun method pointers
 		// at runtime the itab will contain pointers to types, other itabs and
 		// method functions. None are allocated on heap, so we can use obj.NOPTR.
-		ggloblsym(i.sym, int32(o), int16(obj.DUPOK|obj.NOPTR))
+		ggloblsym(i.sym, int32(o), int16(obj.DUPOK|obj.NOPTR|obj.LOCAL))
 
-		ilink := Pkglookup(Tconv(i.t, FmtLeft)+","+Tconv(i.itype, FmtLeft), itablinkpkg)
+		ilink := Pkglookup(i.t.tconv(FmtLeft)+","+i.itype.tconv(FmtLeft), itablinkpkg)
 		dsymptr(ilink, 0, i.sym, 0)
-		ggloblsym(ilink, int32(Widthptr), int16(obj.DUPOK|obj.RODATA))
+		ggloblsym(ilink, int32(Widthptr), int16(obj.DUPOK|obj.RODATA|obj.LOCAL))
+	}
+
+	// process ptabs
+	if localpkg.Name == "main" && len(ptabs) > 0 {
+		ot := 0
+		s := obj.Linklookup(Ctxt, "go.plugin.tabs", 0)
+		for _, p := range ptabs {
+			// Dump ptab symbol into go.pluginsym package.
+			//
+			// type ptab struct {
+			//	name nameOff
+			//	typ  typeOff // pointer to symbol
+			// }
+			nsym := dname(p.s.Name, "", nil, true)
+			ot = dsymptrOffLSym(s, ot, nsym, 0)
+			ot = dsymptrOffLSym(s, ot, Linksym(dtypesym(p.t)), 0)
+		}
+		ggloblLSym(s, int32(ot), int16(obj.RODATA))
+
+		ot = 0
+		s = obj.Linklookup(Ctxt, "go.plugin.exports", 0)
+		for _, p := range ptabs {
+			ot = dsymptrLSym(s, ot, Linksym(p.s), 0)
+		}
+		ggloblLSym(s, int32(ot), int16(obj.RODATA))
 	}
 
 	// generate import strings for imported packages
@@ -1416,16 +1467,16 @@ func dumptypestructs() {
 	// but using runtime means fewer copies in .6 files.
 	if myimportpath == "runtime" {
 		for i := EType(1); i <= TBOOL; i++ {
-			dtypesym(Ptrto(Types[i]))
+			dtypesym(ptrto(Types[i]))
 		}
-		dtypesym(Ptrto(Types[TSTRING]))
-		dtypesym(Ptrto(Types[TUNSAFEPTR]))
+		dtypesym(ptrto(Types[TSTRING]))
+		dtypesym(ptrto(Types[TUNSAFEPTR]))
 
 		// emit type structs for error and func(error) string.
 		// The latter is the type of an auto-generated wrapper.
-		dtypesym(Ptrto(errortype))
+		dtypesym(ptrto(errortype))
 
-		dtypesym(functype(nil, []*Node{Nod(ODCLFIELD, nil, typenod(errortype))}, []*Node{Nod(ODCLFIELD, nil, typenod(Types[TSTRING]))}))
+		dtypesym(functype(nil, []*Node{nod(ODCLFIELD, nil, typenod(errortype))}, []*Node{nod(ODCLFIELD, nil, typenod(Types[TSTRING]))}))
 
 		// add paths for runtime and main, which 6l imports implicitly.
 		dimportpath(Runtimepkg)
@@ -1597,7 +1648,7 @@ func fillptrmask(t *Type, ptrmask []byte) {
 
 	nptr := typeptrdata(t) / int64(Widthptr)
 	for i := int64(0); i < nptr; i++ {
-		if bvget(vec, int32(i)) == 1 {
+		if vec.Get(int32(i)) {
 			ptrmask[i/8] |= 1 << (uint(i) % 8)
 		}
 	}
@@ -1727,8 +1778,8 @@ func zeroaddr(size int64) *Node {
 		x.Typecheck = 1
 		s.Def = x
 	}
-	z := Nod(OADDR, s.Def, nil)
-	z.Type = Ptrto(Types[TUINT8])
+	z := nod(OADDR, s.Def, nil)
+	z.Type = ptrto(Types[TUINT8])
 	z.Addable = true
 	z.Typecheck = 1
 	return z
diff --git a/src/cmd/compile/internal/gc/reg.go b/src/cmd/compile/internal/gc/reg.go
deleted file mode 100644
index a80d72b..0000000
--- a/src/cmd/compile/internal/gc/reg.go
+++ /dev/null
@@ -1,1532 +0,0 @@
-// Derived from Inferno utils/6c/reg.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package gc
-
-import (
-	"bytes"
-	"cmd/internal/obj"
-	"cmd/internal/sys"
-	"fmt"
-	"sort"
-	"strings"
-)
-
-// A Var represents a single variable that may be stored in a register.
-// That variable may itself correspond to a hardware register,
-// to represent the use of registers in the unoptimized instruction stream.
-type Var struct {
-	offset     int64
-	node       *Node
-	nextinnode *Var
-	width      int
-	id         int // index in vars
-	name       int8
-	etype      EType
-	addr       int8
-}
-
-// Bits represents a set of Vars, stored as a bit set of var numbers
-// (the index in vars, or equivalently v.id).
-type Bits struct {
-	b [BITS]uint64
-}
-
-const (
-	BITS = 3
-	NVAR = BITS * 64
-)
-
-var (
-	vars [NVAR]Var // variables under consideration
-	nvar int       // number of vars
-
-	regbits uint64 // bits for hardware registers
-
-	zbits   Bits // zero
-	externs Bits // global variables
-	params  Bits // function parameters and results
-	ivar    Bits // function parameters (inputs)
-	ovar    Bits // function results (outputs)
-	consts  Bits // constant values
-	addrs   Bits // variables with address taken
-)
-
-// A Reg is a wrapper around a single Prog (one instruction) that holds
-// register optimization information while the optimizer runs.
-// r->prog is the instruction.
-type Reg struct {
-	set  Bits // regopt variables written by this instruction.
-	use1 Bits // regopt variables read by prog->from.
-	use2 Bits // regopt variables read by prog->to.
-
-	// refahead/refbehind are the regopt variables whose current
-	// value may be used in the following/preceding instructions
-	// up to a CALL (or the value is clobbered).
-	refbehind Bits
-	refahead  Bits
-
-	// calahead/calbehind are similar, but for variables in
-	// instructions that are reachable after hitting at least one
-	// CALL.
-	calbehind Bits
-	calahead  Bits
-
-	regdiff Bits
-	act     Bits
-	regu    uint64 // register used bitmap
-}
-
-// A Rgn represents a single regopt variable over a region of code
-// where a register could potentially be dedicated to that variable.
-// The code encompassed by a Rgn is defined by the flow graph,
-// starting at enter, flood-filling forward while varno is refahead
-// and backward while varno is refbehind, and following branches.
-// A single variable may be represented by multiple disjoint Rgns and
-// each Rgn may choose a different register for that variable.
-// Registers are allocated to regions greedily in order of descending
-// cost.
-type Rgn struct {
-	enter *Flow
-	cost  int16
-	varno int16
-	regno int16
-}
-
-// The Plan 9 C compilers used a limit of 600 regions,
-// but the yacc-generated parser in y.go has 3100 regions.
-// We set MaxRgn large enough to handle that.
-// There's not a huge cost to having too many regions:
-// the main processing traces the live area for each variable,
-// which is limited by the number of variables times the area,
-// not the raw region count. If there are many regions, they
-// are almost certainly small and easy to trace.
-// The only operation that scales with region count is the
-// sorting by cost, which uses sort.Sort and is therefore
-// guaranteed n log n.
-const MaxRgn = 6000
-
-var (
-	region  []Rgn
-	nregion int
-)
-
-type rcmp []Rgn
-
-func (x rcmp) Len() int {
-	return len(x)
-}
-
-func (x rcmp) Swap(i, j int) {
-	x[i], x[j] = x[j], x[i]
-}
-
-func (x rcmp) Less(i, j int) bool {
-	p1 := &x[i]
-	p2 := &x[j]
-	if p1.cost != p2.cost {
-		return int(p2.cost)-int(p1.cost) < 0
-	}
-	if p1.varno != p2.varno {
-		return int(p2.varno)-int(p1.varno) < 0
-	}
-	if p1.enter != p2.enter {
-		return int(p2.enter.Id-p1.enter.Id) < 0
-	}
-	return false
-}
-
-func setaddrs(bit Bits) {
-	var i int
-	var n int
-	var v *Var
-	var node *Node
-
-	for bany(&bit) {
-		// convert each bit to a variable
-		i = bnum(&bit)
-
-		node = vars[i].node
-		n = int(vars[i].name)
-		biclr(&bit, uint(i))
-
-		// disable all pieces of that variable
-		for i = 0; i < nvar; i++ {
-			v = &vars[i]
-			if v.node == node && int(v.name) == n {
-				v.addr = 2
-			}
-		}
-	}
-}
-
-var regnodes [64]*Node
-
-func walkvardef(n *Node, f *Flow, active int) {
-	var f1 *Flow
-	var bn int
-	var v *Var
-
-	for f1 = f; f1 != nil; f1 = f1.S1 {
-		if f1.Active == int32(active) {
-			break
-		}
-		f1.Active = int32(active)
-		if f1.Prog.As == obj.AVARKILL && f1.Prog.To.Node == n {
-			break
-		}
-		for v, _ = n.Opt().(*Var); v != nil; v = v.nextinnode {
-			bn = v.id
-			biset(&(f1.Data.(*Reg)).act, uint(bn))
-		}
-
-		if f1.Prog.As == obj.ACALL {
-			break
-		}
-	}
-
-	for f2 := f; f2 != f1; f2 = f2.S1 {
-		if f2.S2 != nil {
-			walkvardef(n, f2.S2, active)
-		}
-	}
-}
-
-// add mov b,rn
-// just after r
-func addmove(r *Flow, bn int, rn int, f int) {
-	p1 := Ctxt.NewProg()
-	Clearp(p1)
-	p1.Pc = 9999
-
-	p := r.Prog
-	p1.Link = p.Link
-	p.Link = p1
-	p1.Lineno = p.Lineno
-
-	v := &vars[bn]
-
-	a := &p1.To
-	a.Offset = v.offset
-	a.Etype = uint8(v.etype)
-	a.Type = obj.TYPE_MEM
-	a.Name = v.name
-	a.Node = v.node
-	a.Sym = Linksym(v.node.Sym)
-
-	/* NOTE(rsc): 9g did
-	if(a->etype == TARRAY)
-		a->type = TYPE_ADDR;
-	else if(a->sym == nil)
-		a->type = TYPE_CONST;
-	*/
-	p1.As = Thearch.Optoas(OAS, Types[uint8(v.etype)])
-
-	// TODO(rsc): Remove special case here.
-	if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) && v.etype == TBOOL {
-		p1.As = Thearch.Optoas(OAS, Types[TUINT8])
-	}
-	p1.From.Type = obj.TYPE_REG
-	p1.From.Reg = int16(rn)
-	p1.From.Name = obj.NAME_NONE
-	if f == 0 {
-		p1.From = *a
-		*a = obj.Addr{}
-		a.Type = obj.TYPE_REG
-		a.Reg = int16(rn)
-	}
-
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		fmt.Printf("%v ===add=== %v\n", p, p1)
-	}
-	Ostats.Nspill++
-}
-
-func overlap_reg(o1 int64, w1 int, o2 int64, w2 int) bool {
-	t1 := o1 + int64(w1)
-	t2 := o2 + int64(w2)
-
-	if t1 <= o2 || t2 <= o1 {
-		return false
-	}
-
-	return true
-}
-
-func mkvar(f *Flow, a *obj.Addr) Bits {
-	// mark registers used
-	if a.Type == obj.TYPE_NONE {
-		return zbits
-	}
-
-	r := f.Data.(*Reg)
-	r.use1.b[0] |= Thearch.Doregbits(int(a.Index)) // TODO: Use RtoB
-
-	var n int
-	switch a.Type {
-	default:
-		regu := Thearch.Doregbits(int(a.Reg)) | Thearch.RtoB(int(a.Reg)) // TODO: Use RtoB
-		if regu == 0 {
-			return zbits
-		}
-		bit := zbits
-		bit.b[0] = regu
-		return bit
-
-		// TODO(rsc): Remove special case here.
-	case obj.TYPE_ADDR:
-		var bit Bits
-		if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) {
-			goto memcase
-		}
-		a.Type = obj.TYPE_MEM
-		bit = mkvar(f, a)
-		setaddrs(bit)
-		a.Type = obj.TYPE_ADDR
-		Ostats.Naddr++
-		return zbits
-
-	memcase:
-		fallthrough
-
-	case obj.TYPE_MEM:
-		if r != nil {
-			r.use1.b[0] |= Thearch.RtoB(int(a.Reg))
-		}
-
-		/* NOTE: 5g did
-		if(r->f.prog->scond & (C_PBIT|C_WBIT))
-			r->set.b[0] |= RtoB(a->reg);
-		*/
-		switch a.Name {
-		default:
-			// Note: This case handles NAME_EXTERN and NAME_STATIC.
-			// We treat these as requiring eager writes to memory, due to
-			// the possibility of a fault handler looking at them, so there is
-			// not much point in registerizing the loads.
-			// If we later choose the set of candidate variables from a
-			// larger list, these cases could be deprioritized instead of
-			// removed entirely.
-			return zbits
-
-		case obj.NAME_PARAM,
-			obj.NAME_AUTO:
-			n = int(a.Name)
-		}
-	}
-
-	node, _ := a.Node.(*Node)
-	if node == nil || node.Op != ONAME || node.Orig == nil {
-		return zbits
-	}
-	node = node.Orig
-	if node.Orig != node {
-		Fatalf("%v: bad node", Ctxt.Dconv(a))
-	}
-	if node.Sym == nil || node.Sym.Name[0] == '.' {
-		return zbits
-	}
-	et := EType(a.Etype)
-	o := a.Offset
-	w := a.Width
-	if w < 0 {
-		Fatalf("bad width %d for %v", w, Ctxt.Dconv(a))
-	}
-
-	flag := 0
-	var v *Var
-	for i := 0; i < nvar; i++ {
-		v = &vars[i]
-		if v.node == node && int(v.name) == n {
-			if v.offset == o {
-				if v.etype == et {
-					if int64(v.width) == w {
-						// TODO(rsc): Remove special case for arm here.
-						if flag == 0 || Thearch.LinkArch.Family != sys.ARM {
-							return blsh(uint(i))
-						}
-					}
-				}
-			}
-
-			// if they overlap, disable both
-			if overlap_reg(v.offset, v.width, o, int(w)) {
-				//				print("disable overlap %s %d %d %d %d, %E != %E\n", s->name, v->offset, v->width, o, w, v->etype, et);
-				v.addr = 1
-
-				flag = 1
-			}
-		}
-	}
-
-	switch et {
-	case 0, TFUNC:
-		return zbits
-	}
-
-	if nvar >= NVAR {
-		if Debug['w'] > 1 && node != nil {
-			Fatalf("variable not optimized: %v", Nconv(node, FmtSharp))
-		}
-		if Debug['v'] > 0 {
-			Warn("variable not optimized: %v", Nconv(node, FmtSharp))
-		}
-
-		// If we're not tracking a word in a variable, mark the rest as
-		// having its address taken, so that we keep the whole thing
-		// live at all calls. otherwise we might optimize away part of
-		// a variable but not all of it.
-		var v *Var
-		for i := 0; i < nvar; i++ {
-			v = &vars[i]
-			if v.node == node {
-				v.addr = 1
-			}
-		}
-
-		return zbits
-	}
-
-	i := nvar
-	nvar++
-	v = &vars[i]
-	v.id = i
-	v.offset = o
-	v.name = int8(n)
-	v.etype = et
-	v.width = int(w)
-	v.addr = int8(flag) // funny punning
-	v.node = node
-
-	// node->opt is the head of a linked list
-	// of Vars within the given Node, so that
-	// we can start at a Var and find all the other
-	// Vars in the same Go variable.
-	v.nextinnode, _ = node.Opt().(*Var)
-
-	node.SetOpt(v)
-
-	bit := blsh(uint(i))
-	if n == obj.NAME_EXTERN || n == obj.NAME_STATIC {
-		for z := 0; z < BITS; z++ {
-			externs.b[z] |= bit.b[z]
-		}
-	}
-	if n == obj.NAME_PARAM {
-		for z := 0; z < BITS; z++ {
-			params.b[z] |= bit.b[z]
-		}
-	}
-
-	if node.Class == PPARAM {
-		for z := 0; z < BITS; z++ {
-			ivar.b[z] |= bit.b[z]
-		}
-	}
-	if node.Class == PPARAMOUT {
-		for z := 0; z < BITS; z++ {
-			ovar.b[z] |= bit.b[z]
-		}
-	}
-
-	// Treat values with their address taken as live at calls,
-	// because the garbage collector's liveness analysis in plive.go does.
-	// These must be consistent or else we will elide stores and the garbage
-	// collector will see uninitialized data.
-	// The typical case where our own analysis is out of sync is when the
-	// node appears to have its address taken but that code doesn't actually
-	// get generated and therefore doesn't show up as an address being
-	// taken when we analyze the instruction stream.
-	// One instance of this case is when a closure uses the same name as
-	// an outer variable for one of its own variables declared with :=.
-	// The parser flags the outer variable as possibly shared, and therefore
-	// sets addrtaken, even though it ends up not being actually shared.
-	// If we were better about _ elision, _ = &x would suffice too.
-	// The broader := in a closure problem is mentioned in a comment in
-	// closure.go:/^typecheckclosure and dcl.go:/^oldname.
-	if node.Addrtaken {
-		v.addr = 1
-	}
-
-	// Disable registerization for globals, because:
-	// (1) we might panic at any time and we want the recovery code
-	// to see the latest values (issue 1304).
-	// (2) we don't know what pointers might point at them and we want
-	// loads via those pointers to see updated values and vice versa (issue 7995).
-	//
-	// Disable registerization for results if using defer, because the deferred func
-	// might recover and return, causing the current values to be used.
-	if node.Class == PEXTERN || (hasdefer && node.Class == PPARAMOUT) {
-		v.addr = 1
-	}
-
-	if Debug['R'] != 0 {
-		fmt.Printf("bit=%2d et=%v w=%d+%d %v %v flag=%d\n", i, et, o, w, Nconv(node, FmtSharp), Ctxt.Dconv(a), v.addr)
-	}
-	Ostats.Nvar++
-
-	return bit
-}
-
-var change int
-
-func prop(f *Flow, ref Bits, cal Bits) {
-	var f1 *Flow
-	var r1 *Reg
-	var z int
-	var i int
-	var v *Var
-	var v1 *Var
-
-	for f1 = f; f1 != nil; f1 = f1.P1 {
-		r1 = f1.Data.(*Reg)
-		for z = 0; z < BITS; z++ {
-			ref.b[z] |= r1.refahead.b[z]
-			if ref.b[z] != r1.refahead.b[z] {
-				r1.refahead.b[z] = ref.b[z]
-				change = 1
-			}
-
-			cal.b[z] |= r1.calahead.b[z]
-			if cal.b[z] != r1.calahead.b[z] {
-				r1.calahead.b[z] = cal.b[z]
-				change = 1
-			}
-		}
-
-		switch f1.Prog.As {
-		case obj.ACALL:
-			if Noreturn(f1.Prog) {
-				break
-			}
-
-			// Mark all input variables (ivar) as used, because that's what the
-			// liveness bitmaps say. The liveness bitmaps say that so that a
-			// panic will not show stale values in the parameter dump.
-			// Mark variables with a recent VARDEF (r1->act) as used,
-			// so that the optimizer flushes initializations to memory,
-			// so that if a garbage collection happens during this CALL,
-			// the collector will see initialized memory. Again this is to
-			// match what the liveness bitmaps say.
-			for z = 0; z < BITS; z++ {
-				cal.b[z] |= ref.b[z] | externs.b[z] | ivar.b[z] | r1.act.b[z]
-				ref.b[z] = 0
-			}
-
-			// cal.b is the current approximation of what's live across the call.
-			// Every bit in cal.b is a single stack word. For each such word,
-			// find all the other tracked stack words in the same Go variable
-			// (struct/slice/string/interface) and mark them live too.
-			// This is necessary because the liveness analysis for the garbage
-			// collector works at variable granularity, not at word granularity.
-			// It is fundamental for slice/string/interface: the garbage collector
-			// needs the whole value, not just some of the words, in order to
-			// interpret the other bits correctly. Specifically, slice needs a consistent
-			// ptr and cap, string needs a consistent ptr and len, and interface
-			// needs a consistent type word and data word.
-			for z = 0; z < BITS; z++ {
-				if cal.b[z] == 0 {
-					continue
-				}
-				for i = 0; i < 64; i++ {
-					if z*64+i >= nvar || (cal.b[z]>>uint(i))&1 == 0 {
-						continue
-					}
-					v = &vars[z*64+i]
-					if v.node.Opt() == nil { // v represents fixed register, not Go variable
-						continue
-					}
-
-					// v->node->opt is the head of a linked list of Vars
-					// corresponding to tracked words from the Go variable v->node.
-					// Walk the list and set all the bits.
-					// For a large struct this could end up being quadratic:
-					// after the first setting, the outer loop (for z, i) would see a 1 bit
-					// for all of the remaining words in the struct, and for each such
-					// word would go through and turn on all the bits again.
-					// To avoid the quadratic behavior, we only turn on the bits if
-					// v is the head of the list or if the head's bit is not yet turned on.
-					// This will set the bits at most twice, keeping the overall loop linear.
-					v1, _ = v.node.Opt().(*Var)
-
-					if v == v1 || !btest(&cal, uint(v1.id)) {
-						for ; v1 != nil; v1 = v1.nextinnode {
-							biset(&cal, uint(v1.id))
-						}
-					}
-				}
-			}
-
-		case obj.ATEXT:
-			for z = 0; z < BITS; z++ {
-				cal.b[z] = 0
-				ref.b[z] = 0
-			}
-
-		case obj.ARET:
-			for z = 0; z < BITS; z++ {
-				cal.b[z] = externs.b[z] | ovar.b[z]
-				ref.b[z] = 0
-			}
-		}
-
-		for z = 0; z < BITS; z++ {
-			ref.b[z] = ref.b[z]&^r1.set.b[z] | r1.use1.b[z] | r1.use2.b[z]
-			cal.b[z] &^= (r1.set.b[z] | r1.use1.b[z] | r1.use2.b[z])
-			r1.refbehind.b[z] = ref.b[z]
-			r1.calbehind.b[z] = cal.b[z]
-		}
-
-		if f1.Active != 0 {
-			break
-		}
-		f1.Active = 1
-	}
-
-	var r *Reg
-	var f2 *Flow
-	for ; f != f1; f = f.P1 {
-		r = f.Data.(*Reg)
-		for f2 = f.P2; f2 != nil; f2 = f2.P2link {
-			prop(f2, r.refbehind, r.calbehind)
-		}
-	}
-}
-
-func synch(f *Flow, dif Bits) {
-	var r1 *Reg
-	var z int
-
-	for f1 := f; f1 != nil; f1 = f1.S1 {
-		r1 = f1.Data.(*Reg)
-		for z = 0; z < BITS; z++ {
-			dif.b[z] = dif.b[z]&^(^r1.refbehind.b[z]&r1.refahead.b[z]) | r1.set.b[z] | r1.regdiff.b[z]
-			if dif.b[z] != r1.regdiff.b[z] {
-				r1.regdiff.b[z] = dif.b[z]
-				change = 1
-			}
-		}
-
-		if f1.Active != 0 {
-			break
-		}
-		f1.Active = 1
-		for z = 0; z < BITS; z++ {
-			dif.b[z] &^= (^r1.calbehind.b[z] & r1.calahead.b[z])
-		}
-		if f1.S2 != nil {
-			synch(f1.S2, dif)
-		}
-	}
-}
-
-func allreg(b uint64, r *Rgn) uint64 {
-	v := &vars[r.varno]
-	r.regno = 0
-	switch v.etype {
-	default:
-		Fatalf("unknown etype %d/%v", Bitno(b), v.etype)
-
-	case TINT8,
-		TUINT8,
-		TINT16,
-		TUINT16,
-		TINT32,
-		TUINT32,
-		TINT64,
-		TUINT64,
-		TINT,
-		TUINT,
-		TUINTPTR,
-		TBOOL,
-		TPTR32,
-		TPTR64:
-		i := Thearch.BtoR(^b)
-		if i != 0 && r.cost > 0 {
-			r.regno = int16(i)
-			return Thearch.RtoB(i)
-		}
-
-	case TFLOAT32, TFLOAT64:
-		i := Thearch.BtoF(^b)
-		if i != 0 && r.cost > 0 {
-			r.regno = int16(i)
-			return Thearch.FtoB(i)
-		}
-	}
-
-	return 0
-}
-
-func LOAD(r *Reg, z int) uint64 {
-	return ^r.refbehind.b[z] & r.refahead.b[z]
-}
-
-func STORE(r *Reg, z int) uint64 {
-	return ^r.calbehind.b[z] & r.calahead.b[z]
-}
-
-// Cost parameters
-const (
-	CLOAD = 5 // cost of load
-	CREF  = 5 // cost of reference if not registerized
-	LOOP  = 3 // loop execution count (applied in popt.go)
-)
-
-func paint1(f *Flow, bn int) {
-	z := bn / 64
-	bb := uint64(1 << uint(bn%64))
-	r := f.Data.(*Reg)
-	if r.act.b[z]&bb != 0 {
-		return
-	}
-	var f1 *Flow
-	var r1 *Reg
-	for {
-		if r.refbehind.b[z]&bb == 0 {
-			break
-		}
-		f1 = f.P1
-		if f1 == nil {
-			break
-		}
-		r1 = f1.Data.(*Reg)
-		if r1.refahead.b[z]&bb == 0 {
-			break
-		}
-		if r1.act.b[z]&bb != 0 {
-			break
-		}
-		f = f1
-		r = r1
-	}
-
-	if LOAD(r, z)&^(r.set.b[z]&^(r.use1.b[z]|r.use2.b[z]))&bb != 0 {
-		change -= CLOAD * int(f.Loop)
-	}
-
-	for {
-		r.act.b[z] |= bb
-
-		if f.Prog.As != obj.ANOP { // don't give credit for NOPs
-			if r.use1.b[z]&bb != 0 {
-				change += CREF * int(f.Loop)
-			}
-			if (r.use2.b[z]|r.set.b[z])&bb != 0 {
-				change += CREF * int(f.Loop)
-			}
-		}
-
-		if STORE(r, z)&r.regdiff.b[z]&bb != 0 {
-			change -= CLOAD * int(f.Loop)
-		}
-
-		if r.refbehind.b[z]&bb != 0 {
-			for f1 = f.P2; f1 != nil; f1 = f1.P2link {
-				if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 {
-					paint1(f1, bn)
-				}
-			}
-		}
-
-		if r.refahead.b[z]&bb == 0 {
-			break
-		}
-		f1 = f.S2
-		if f1 != nil {
-			if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 {
-				paint1(f1, bn)
-			}
-		}
-		f = f.S1
-		if f == nil {
-			break
-		}
-		r = f.Data.(*Reg)
-		if r.act.b[z]&bb != 0 {
-			break
-		}
-		if r.refbehind.b[z]&bb == 0 {
-			break
-		}
-	}
-}
-
-func paint2(f *Flow, bn int, depth int) uint64 {
-	z := bn / 64
-	bb := uint64(1 << uint(bn%64))
-	vreg := regbits
-	r := f.Data.(*Reg)
-	if r.act.b[z]&bb == 0 {
-		return vreg
-	}
-	var r1 *Reg
-	var f1 *Flow
-	for {
-		if r.refbehind.b[z]&bb == 0 {
-			break
-		}
-		f1 = f.P1
-		if f1 == nil {
-			break
-		}
-		r1 = f1.Data.(*Reg)
-		if r1.refahead.b[z]&bb == 0 {
-			break
-		}
-		if r1.act.b[z]&bb == 0 {
-			break
-		}
-		f = f1
-		r = r1
-	}
-
-	for {
-		if Debug['R'] != 0 && Debug['v'] != 0 {
-			fmt.Printf("  paint2 %d %v\n", depth, f.Prog)
-		}
-
-		r.act.b[z] &^= bb
-
-		vreg |= r.regu
-
-		if r.refbehind.b[z]&bb != 0 {
-			for f1 = f.P2; f1 != nil; f1 = f1.P2link {
-				if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 {
-					vreg |= paint2(f1, bn, depth+1)
-				}
-			}
-		}
-
-		if r.refahead.b[z]&bb == 0 {
-			break
-		}
-		f1 = f.S2
-		if f1 != nil {
-			if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 {
-				vreg |= paint2(f1, bn, depth+1)
-			}
-		}
-		f = f.S1
-		if f == nil {
-			break
-		}
-		r = f.Data.(*Reg)
-		if r.act.b[z]&bb == 0 {
-			break
-		}
-		if r.refbehind.b[z]&bb == 0 {
-			break
-		}
-	}
-
-	return vreg
-}
-
-func paint3(f *Flow, bn int, rb uint64, rn int) {
-	z := bn / 64
-	bb := uint64(1 << uint(bn%64))
-	r := f.Data.(*Reg)
-	if r.act.b[z]&bb != 0 {
-		return
-	}
-	var r1 *Reg
-	var f1 *Flow
-	for {
-		if r.refbehind.b[z]&bb == 0 {
-			break
-		}
-		f1 = f.P1
-		if f1 == nil {
-			break
-		}
-		r1 = f1.Data.(*Reg)
-		if r1.refahead.b[z]&bb == 0 {
-			break
-		}
-		if r1.act.b[z]&bb != 0 {
-			break
-		}
-		f = f1
-		r = r1
-	}
-
-	if LOAD(r, z)&^(r.set.b[z]&^(r.use1.b[z]|r.use2.b[z]))&bb != 0 {
-		addmove(f, bn, rn, 0)
-	}
-	var p *obj.Prog
-	for {
-		r.act.b[z] |= bb
-		p = f.Prog
-
-		if r.use1.b[z]&bb != 0 {
-			if Debug['R'] != 0 && Debug['v'] != 0 {
-				fmt.Printf("%v", p)
-			}
-			addreg(&p.From, rn)
-			if Debug['R'] != 0 && Debug['v'] != 0 {
-				fmt.Printf(" ===change== %v\n", p)
-			}
-		}
-
-		if (r.use2.b[z]|r.set.b[z])&bb != 0 {
-			if Debug['R'] != 0 && Debug['v'] != 0 {
-				fmt.Printf("%v", p)
-			}
-			addreg(&p.To, rn)
-			if Debug['R'] != 0 && Debug['v'] != 0 {
-				fmt.Printf(" ===change== %v\n", p)
-			}
-		}
-
-		if STORE(r, z)&r.regdiff.b[z]&bb != 0 {
-			addmove(f, bn, rn, 1)
-		}
-		r.regu |= rb
-
-		if r.refbehind.b[z]&bb != 0 {
-			for f1 = f.P2; f1 != nil; f1 = f1.P2link {
-				if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 {
-					paint3(f1, bn, rb, rn)
-				}
-			}
-		}
-
-		if r.refahead.b[z]&bb == 0 {
-			break
-		}
-		f1 = f.S2
-		if f1 != nil {
-			if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 {
-				paint3(f1, bn, rb, rn)
-			}
-		}
-		f = f.S1
-		if f == nil {
-			break
-		}
-		r = f.Data.(*Reg)
-		if r.act.b[z]&bb != 0 {
-			break
-		}
-		if r.refbehind.b[z]&bb == 0 {
-			break
-		}
-	}
-}
-
-func addreg(a *obj.Addr, rn int) {
-	a.Sym = nil
-	a.Node = nil
-	a.Offset = 0
-	a.Type = obj.TYPE_REG
-	a.Reg = int16(rn)
-	a.Name = 0
-
-	Ostats.Ncvtreg++
-}
-
-func dumpone(f *Flow, isreg int) {
-	fmt.Printf("%d:%v", f.Loop, f.Prog)
-	if isreg != 0 {
-		r := f.Data.(*Reg)
-		var bit Bits
-		for z := 0; z < BITS; z++ {
-			bit.b[z] = r.set.b[z] | r.use1.b[z] | r.use2.b[z] | r.refbehind.b[z] | r.refahead.b[z] | r.calbehind.b[z] | r.calahead.b[z] | r.regdiff.b[z] | r.act.b[z] | 0
-		}
-		if bany(&bit) {
-			fmt.Printf("\t")
-			if bany(&r.set) {
-				fmt.Printf(" s:%v", &r.set)
-			}
-			if bany(&r.use1) {
-				fmt.Printf(" u1:%v", &r.use1)
-			}
-			if bany(&r.use2) {
-				fmt.Printf(" u2:%v", &r.use2)
-			}
-			if bany(&r.refbehind) {
-				fmt.Printf(" rb:%v ", &r.refbehind)
-			}
-			if bany(&r.refahead) {
-				fmt.Printf(" ra:%v ", &r.refahead)
-			}
-			if bany(&r.calbehind) {
-				fmt.Printf(" cb:%v ", &r.calbehind)
-			}
-			if bany(&r.calahead) {
-				fmt.Printf(" ca:%v ", &r.calahead)
-			}
-			if bany(&r.regdiff) {
-				fmt.Printf(" d:%v ", &r.regdiff)
-			}
-			if bany(&r.act) {
-				fmt.Printf(" a:%v ", &r.act)
-			}
-		}
-	}
-
-	fmt.Printf("\n")
-}
-
-func Dumpit(str string, r0 *Flow, isreg int) {
-	var r1 *Flow
-
-	fmt.Printf("\n%s\n", str)
-	for r := r0; r != nil; r = r.Link {
-		dumpone(r, isreg)
-		r1 = r.P2
-		if r1 != nil {
-			fmt.Printf("\tpred:")
-			for ; r1 != nil; r1 = r1.P2link {
-				fmt.Printf(" %.4d", uint(int(r1.Prog.Pc)))
-			}
-			if r.P1 != nil {
-				fmt.Printf(" (and %.4d)", uint(int(r.P1.Prog.Pc)))
-			} else {
-				fmt.Printf(" (only)")
-			}
-			fmt.Printf("\n")
-		}
-
-		// Print successors if it's not just the next one
-		if r.S1 != r.Link || r.S2 != nil {
-			fmt.Printf("\tsucc:")
-			if r.S1 != nil {
-				fmt.Printf(" %.4d", uint(int(r.S1.Prog.Pc)))
-			}
-			if r.S2 != nil {
-				fmt.Printf(" %.4d", uint(int(r.S2.Prog.Pc)))
-			}
-			fmt.Printf("\n")
-		}
-	}
-}
-
-func regopt(firstp *obj.Prog) {
-	mergetemp(firstp)
-
-	// control flow is more complicated in generated go code
-	// than in generated c code.  define pseudo-variables for
-	// registers, so we have complete register usage information.
-	var nreg int
-	regnames := Thearch.Regnames(&nreg)
-
-	nvar = nreg
-	for i := 0; i < nreg; i++ {
-		vars[i] = Var{}
-	}
-	for i := 0; i < nreg; i++ {
-		if regnodes[i] == nil {
-			regnodes[i] = newname(Lookup(regnames[i]))
-		}
-		vars[i].node = regnodes[i]
-	}
-
-	regbits = Thearch.Excludedregs()
-	externs = zbits
-	params = zbits
-	consts = zbits
-	addrs = zbits
-	ivar = zbits
-	ovar = zbits
-
-	// pass 1
-	// build aux data structure
-	// allocate pcs
-	// find use and set of variables
-	g := Flowstart(firstp, func() interface{} { return new(Reg) })
-	if g == nil {
-		for i := 0; i < nvar; i++ {
-			vars[i].node.SetOpt(nil)
-		}
-		return
-	}
-
-	firstf := g.Start
-
-	for f := firstf; f != nil; f = f.Link {
-		p := f.Prog
-		// AVARLIVE must be considered a use, do not skip it.
-		// Otherwise the variable will be optimized away,
-		// and the whole point of AVARLIVE is to keep it on the stack.
-		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
-			continue
-		}
-
-		// Avoid making variables for direct-called functions.
-		if p.As == obj.ACALL && p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_EXTERN {
-			continue
-		}
-
-		// from vs to doesn't matter for registers.
-		r := f.Data.(*Reg)
-		r.use1.b[0] |= p.Info.Reguse | p.Info.Regindex
-		r.set.b[0] |= p.Info.Regset
-
-		bit := mkvar(f, &p.From)
-		if bany(&bit) {
-			if p.Info.Flags&LeftAddr != 0 {
-				setaddrs(bit)
-			}
-			if p.Info.Flags&LeftRead != 0 {
-				for z := 0; z < BITS; z++ {
-					r.use1.b[z] |= bit.b[z]
-				}
-			}
-			if p.Info.Flags&LeftWrite != 0 {
-				for z := 0; z < BITS; z++ {
-					r.set.b[z] |= bit.b[z]
-				}
-			}
-		}
-
-		// Compute used register for reg
-		if p.Info.Flags&RegRead != 0 {
-			r.use1.b[0] |= Thearch.RtoB(int(p.Reg))
-		}
-
-		// Currently we never generate three register forms.
-		// If we do, this will need to change.
-		if p.From3Type() != obj.TYPE_NONE && p.From3Type() != obj.TYPE_CONST {
-			Fatalf("regopt not implemented for from3")
-		}
-
-		bit = mkvar(f, &p.To)
-		if bany(&bit) {
-			if p.Info.Flags&RightAddr != 0 {
-				setaddrs(bit)
-			}
-			if p.Info.Flags&RightRead != 0 {
-				for z := 0; z < BITS; z++ {
-					r.use2.b[z] |= bit.b[z]
-				}
-			}
-			if p.Info.Flags&RightWrite != 0 {
-				for z := 0; z < BITS; z++ {
-					r.set.b[z] |= bit.b[z]
-				}
-			}
-		}
-	}
-
-	for i := 0; i < nvar; i++ {
-		v := &vars[i]
-		if v.addr != 0 {
-			bit := blsh(uint(i))
-			for z := 0; z < BITS; z++ {
-				addrs.b[z] |= bit.b[z]
-			}
-		}
-
-		if Debug['R'] != 0 && Debug['v'] != 0 {
-			fmt.Printf("bit=%2d addr=%d et=%v w=%-2d s=%v + %d\n", i, v.addr, v.etype, v.width, v.node, v.offset)
-		}
-	}
-
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		Dumpit("pass1", firstf, 1)
-	}
-
-	// pass 2
-	// find looping structure
-	flowrpo(g)
-
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		Dumpit("pass2", firstf, 1)
-	}
-
-	// pass 2.5
-	// iterate propagating fat vardef covering forward
-	// r->act records vars with a VARDEF since the last CALL.
-	// (r->act will be reused in pass 5 for something else,
-	// but we'll be done with it by then.)
-	active := 0
-
-	for f := firstf; f != nil; f = f.Link {
-		f.Active = 0
-		r := f.Data.(*Reg)
-		r.act = zbits
-	}
-
-	for f := firstf; f != nil; f = f.Link {
-		p := f.Prog
-		if p.As == obj.AVARDEF && Isfat(((p.To.Node).(*Node)).Type) && ((p.To.Node).(*Node)).Opt() != nil {
-			active++
-			walkvardef(p.To.Node.(*Node), f, active)
-		}
-	}
-
-	// pass 3
-	// iterate propagating usage
-	// 	back until flow graph is complete
-	var f1 *Flow
-	var i int
-	var f *Flow
-loop1:
-	change = 0
-
-	for f = firstf; f != nil; f = f.Link {
-		f.Active = 0
-	}
-	for f = firstf; f != nil; f = f.Link {
-		if f.Prog.As == obj.ARET {
-			prop(f, zbits, zbits)
-		}
-	}
-
-	// pick up unreachable code
-loop11:
-	i = 0
-
-	for f = firstf; f != nil; f = f1 {
-		f1 = f.Link
-		if f1 != nil && f1.Active != 0 && f.Active == 0 {
-			prop(f, zbits, zbits)
-			i = 1
-		}
-	}
-
-	if i != 0 {
-		goto loop11
-	}
-	if change != 0 {
-		goto loop1
-	}
-
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		Dumpit("pass3", firstf, 1)
-	}
-
-	// pass 4
-	// iterate propagating register/variable synchrony
-	// 	forward until graph is complete
-loop2:
-	change = 0
-
-	for f = firstf; f != nil; f = f.Link {
-		f.Active = 0
-	}
-	synch(firstf, zbits)
-	if change != 0 {
-		goto loop2
-	}
-
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		Dumpit("pass4", firstf, 1)
-	}
-
-	// pass 4.5
-	// move register pseudo-variables into regu.
-	mask := uint64((1 << uint(nreg)) - 1)
-	for f := firstf; f != nil; f = f.Link {
-		r := f.Data.(*Reg)
-		r.regu = (r.refbehind.b[0] | r.set.b[0]) & mask
-		r.set.b[0] &^= mask
-		r.use1.b[0] &^= mask
-		r.use2.b[0] &^= mask
-		r.refbehind.b[0] &^= mask
-		r.refahead.b[0] &^= mask
-		r.calbehind.b[0] &^= mask
-		r.calahead.b[0] &^= mask
-		r.regdiff.b[0] &^= mask
-		r.act.b[0] &^= mask
-	}
-
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		Dumpit("pass4.5", firstf, 1)
-	}
-
-	// pass 5
-	// isolate regions
-	// calculate costs (paint1)
-	var bit Bits
-	if f := firstf; f != nil {
-		r := f.Data.(*Reg)
-		for z := 0; z < BITS; z++ {
-			bit.b[z] = (r.refahead.b[z] | r.calahead.b[z]) &^ (externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z])
-		}
-		if bany(&bit) && !f.Refset {
-			// should never happen - all variables are preset
-			if Debug['w'] != 0 {
-				fmt.Printf("%v: used and not set: %v\n", f.Prog.Line(), &bit)
-			}
-			f.Refset = true
-		}
-	}
-
-	for f := firstf; f != nil; f = f.Link {
-		(f.Data.(*Reg)).act = zbits
-	}
-	nregion = 0
-	region = region[:0]
-	for f := firstf; f != nil; f = f.Link {
-		r := f.Data.(*Reg)
-		for z := 0; z < BITS; z++ {
-			bit.b[z] = r.set.b[z] &^ (r.refahead.b[z] | r.calahead.b[z] | addrs.b[z])
-		}
-		if bany(&bit) && !f.Refset {
-			if Debug['w'] != 0 {
-				fmt.Printf("%v: set and not used: %v\n", f.Prog.Line(), &bit)
-			}
-			f.Refset = true
-			Thearch.Excise(f)
-		}
-
-		for z := 0; z < BITS; z++ {
-			bit.b[z] = LOAD(r, z) &^ (r.act.b[z] | addrs.b[z])
-		}
-		for bany(&bit) {
-			i = bnum(&bit)
-			change = 0
-			paint1(f, i)
-			biclr(&bit, uint(i))
-			if change <= 0 {
-				continue
-			}
-			if nregion >= MaxRgn {
-				nregion++
-				continue
-			}
-
-			region = append(region, Rgn{
-				enter: f,
-				cost:  int16(change),
-				varno: int16(i),
-			})
-			nregion++
-		}
-	}
-
-	if false && Debug['v'] != 0 && strings.Contains(Curfn.Func.Nname.Sym.Name, "Parse") {
-		Warn("regions: %d\n", nregion)
-	}
-	if nregion >= MaxRgn {
-		if Debug['v'] != 0 {
-			Warn("too many regions: %d\n", nregion)
-		}
-		nregion = MaxRgn
-	}
-
-	sort.Sort(rcmp(region[:nregion]))
-
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		Dumpit("pass5", firstf, 1)
-	}
-
-	// pass 6
-	// determine used registers (paint2)
-	// replace code (paint3)
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		fmt.Printf("\nregisterizing\n")
-	}
-	for i := 0; i < nregion; i++ {
-		rgp := &region[i]
-		if Debug['R'] != 0 && Debug['v'] != 0 {
-			fmt.Printf("region %d: cost %d varno %d enter %d\n", i, rgp.cost, rgp.varno, rgp.enter.Prog.Pc)
-		}
-		bit = blsh(uint(rgp.varno))
-		usedreg := paint2(rgp.enter, int(rgp.varno), 0)
-		vreg := allreg(usedreg, rgp)
-		if rgp.regno != 0 {
-			if Debug['R'] != 0 && Debug['v'] != 0 {
-				v := &vars[rgp.varno]
-				fmt.Printf("registerize %v+%d (bit=%2d et=%v) in %v usedreg=%#x vreg=%#x\n", v.node, v.offset, rgp.varno, v.etype, obj.Rconv(int(rgp.regno)), usedreg, vreg)
-			}
-
-			paint3(rgp.enter, int(rgp.varno), vreg, int(rgp.regno))
-		}
-	}
-
-	// free aux structures. peep allocates new ones.
-	for i := 0; i < nvar; i++ {
-		vars[i].node.SetOpt(nil)
-	}
-	Flowend(g)
-	firstf = nil
-
-	if Debug['R'] != 0 && Debug['v'] != 0 {
-		// Rebuild flow graph, since we inserted instructions
-		g := Flowstart(firstp, nil)
-		firstf = g.Start
-		Dumpit("pass6", firstf, 0)
-		Flowend(g)
-		firstf = nil
-	}
-
-	// pass 7
-	// peep-hole on basic block
-	if Debug['R'] == 0 || Debug['P'] != 0 {
-		Thearch.Peep(firstp)
-	}
-
-	// eliminate nops
-	for p := firstp; p != nil; p = p.Link {
-		for p.Link != nil && p.Link.As == obj.ANOP {
-			p.Link = p.Link.Link
-		}
-		if p.To.Type == obj.TYPE_BRANCH {
-			for p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.ANOP {
-				p.To.Val = p.To.Val.(*obj.Prog).Link
-			}
-		}
-	}
-
-	if Debug['R'] != 0 {
-		if Ostats.Ncvtreg != 0 || Ostats.Nspill != 0 || Ostats.Nreload != 0 || Ostats.Ndelmov != 0 || Ostats.Nvar != 0 || Ostats.Naddr != 0 || false {
-			fmt.Printf("\nstats\n")
-		}
-
-		if Ostats.Ncvtreg != 0 {
-			fmt.Printf("\t%4d cvtreg\n", Ostats.Ncvtreg)
-		}
-		if Ostats.Nspill != 0 {
-			fmt.Printf("\t%4d spill\n", Ostats.Nspill)
-		}
-		if Ostats.Nreload != 0 {
-			fmt.Printf("\t%4d reload\n", Ostats.Nreload)
-		}
-		if Ostats.Ndelmov != 0 {
-			fmt.Printf("\t%4d delmov\n", Ostats.Ndelmov)
-		}
-		if Ostats.Nvar != 0 {
-			fmt.Printf("\t%4d var\n", Ostats.Nvar)
-		}
-		if Ostats.Naddr != 0 {
-			fmt.Printf("\t%4d addr\n", Ostats.Naddr)
-		}
-
-		Ostats = OptStats{}
-	}
-}
-
-// bany reports whether any bits in a are set.
-func bany(a *Bits) bool {
-	for _, x := range &a.b { // & to avoid making a copy of a.b
-		if x != 0 {
-			return true
-		}
-	}
-	return false
-}
-
-// bnum reports the lowest index of a 1 bit in a.
-func bnum(a *Bits) int {
-	for i, x := range &a.b { // & to avoid making a copy of a.b
-		if x != 0 {
-			return 64*i + Bitno(x)
-		}
-	}
-
-	Fatalf("bad in bnum")
-	return 0
-}
-
-// blsh returns a Bits with 1 at index n, 0 elsewhere (1<<n).
-func blsh(n uint) Bits {
-	c := zbits
-	c.b[n/64] = 1 << (n % 64)
-	return c
-}
-
-// btest reports whether bit n is 1.
-func btest(a *Bits, n uint) bool {
-	return a.b[n/64]&(1<<(n%64)) != 0
-}
-
-// biset sets bit n to 1.
-func biset(a *Bits, n uint) {
-	a.b[n/64] |= 1 << (n % 64)
-}
-
-// biclr sets bit n to 0.
-func biclr(a *Bits, n uint) {
-	a.b[n/64] &^= (1 << (n % 64))
-}
-
-// Bitno reports the lowest index of a 1 bit in b.
-// It calls Fatalf if there is no 1 bit.
-func Bitno(b uint64) int {
-	if b == 0 {
-		Fatalf("bad in bitno")
-	}
-	n := 0
-	if b&(1<<32-1) == 0 {
-		n += 32
-		b >>= 32
-	}
-	if b&(1<<16-1) == 0 {
-		n += 16
-		b >>= 16
-	}
-	if b&(1<<8-1) == 0 {
-		n += 8
-		b >>= 8
-	}
-	if b&(1<<4-1) == 0 {
-		n += 4
-		b >>= 4
-	}
-	if b&(1<<2-1) == 0 {
-		n += 2
-		b >>= 2
-	}
-	if b&1 == 0 {
-		n++
-	}
-	return n
-}
-
-// String returns a space-separated list of the variables represented by bits.
-func (bits Bits) String() string {
-	// Note: This method takes a value receiver, both for convenience
-	// and to make it safe to modify the bits as we process them.
-	// Even so, most prints above use &bits, because then the value
-	// being stored in the interface{} is a pointer and does not require
-	// an allocation and copy to create the interface{}.
-	var buf bytes.Buffer
-	sep := ""
-	for bany(&bits) {
-		i := bnum(&bits)
-		buf.WriteString(sep)
-		sep = " "
-		v := &vars[i]
-		if v.node == nil || v.node.Sym == nil {
-			fmt.Fprintf(&buf, "$%d", i)
-		} else {
-			fmt.Fprintf(&buf, "%s(%d)", v.node.Sym.Name, i)
-			if v.offset != 0 {
-				fmt.Fprintf(&buf, "%+d", v.offset)
-			}
-		}
-		biclr(&bits, uint(i))
-	}
-	return buf.String()
-}
diff --git a/src/cmd/compile/internal/gc/select.go b/src/cmd/compile/internal/gc/select.go
index 120a9b8..d999190 100644
--- a/src/cmd/compile/internal/gc/select.go
+++ b/src/cmd/compile/internal/gc/select.go
@@ -24,12 +24,12 @@ func typecheckselect(sel *Node) {
 		if ncase.List.Len() == 0 {
 			// default
 			if def != nil {
-				Yyerror("multiple defaults in select (first at %v)", def.Line())
+				yyerror("multiple defaults in select (first at %v)", def.Line())
 			} else {
 				def = ncase
 			}
 		} else if ncase.List.Len() > 1 {
-			Yyerror("select cases cannot be lists")
+			yyerror("select cases cannot be lists")
 		} else {
 			ncase.List.SetIndex(0, typecheck(ncase.List.Index(0), Etop))
 			n = ncase.List.Index(0)
@@ -38,9 +38,9 @@ func typecheckselect(sel *Node) {
 			setlineno(n)
 			switch n.Op {
 			default:
-				Yyerror("select case must be receive, send or assign recv")
+				yyerror("select case must be receive, send or assign recv")
 
-				// convert x = <-c into OSELRECV(x, <-c).
+			// convert x = <-c into OSELRECV(x, <-c).
 			// remove implicit conversions; the eventual assignment
 			// will reintroduce them.
 			case OAS:
@@ -49,7 +49,7 @@ func typecheckselect(sel *Node) {
 				}
 
 				if n.Right.Op != ORECV {
-					Yyerror("select assignment must have receive on right hand side")
+					yyerror("select assignment must have receive on right hand side")
 					break
 				}
 
@@ -58,7 +58,7 @@ func typecheckselect(sel *Node) {
 				// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
 			case OAS2RECV:
 				if n.Rlist.First().Op != ORECV {
-					Yyerror("select assignment must have receive on right hand side")
+					yyerror("select assignment must have receive on right hand side")
 					break
 				}
 
@@ -70,7 +70,7 @@ func typecheckselect(sel *Node) {
 
 				// convert <-c into OSELRECV(N, <-c)
 			case ORECV:
-				n = Nod(OSELRECV, nil, n)
+				n = nod(OSELRECV, nil, n)
 
 				n.Typecheck = 1
 				ncase.Left = n
@@ -143,7 +143,7 @@ func walkselect(sel *Node) {
 				}
 
 				n.Op = OAS2
-				n.List.Set(append([]*Node{n.Left}, n.List.Slice()...))
+				n.List.Prepend(n.Left)
 				n.Rlist.Set1(n.Right)
 				n.Right = nil
 				n.Left = nil
@@ -152,9 +152,9 @@ func walkselect(sel *Node) {
 			}
 
 			// if ch == nil { block() }; n;
-			a := Nod(OIF, nil, nil)
+			a := nod(OIF, nil, nil)
 
-			a.Left = Nod(OEQ, ch, nodnil())
+			a.Left = nod(OEQ, ch, nodnil())
 			var ln Nodes
 			ln.Set(l)
 			a.Nbody.Set1(mkcall("block", nil, &ln))
@@ -179,7 +179,7 @@ func walkselect(sel *Node) {
 		}
 		switch n.Op {
 		case OSEND:
-			n.Right = Nod(OADDR, n.Right, nil)
+			n.Right = nod(OADDR, n.Right, nil)
 			n.Right = typecheck(n.Right, Erv)
 
 		case OSELRECV, OSELRECV2:
@@ -187,14 +187,14 @@ func walkselect(sel *Node) {
 				n.Op = OSELRECV
 			}
 			if n.Op == OSELRECV2 {
-				n.List.SetIndex(0, Nod(OADDR, n.List.First(), nil))
+				n.List.SetIndex(0, nod(OADDR, n.List.First(), nil))
 				n.List.SetIndex(0, typecheck(n.List.Index(0), Erv))
 			}
 
 			if n.Left == nil {
 				n.Left = nodnil()
 			} else {
-				n.Left = Nod(OADDR, n.Left, nil)
+				n.Left = nod(OADDR, n.Left, nil)
 				n.Left = typecheck(n.Left, Erv)
 			}
 		}
@@ -214,7 +214,7 @@ func walkselect(sel *Node) {
 
 		n := cas.Left
 		setlineno(n)
-		r := Nod(OIF, nil, nil)
+		r := nod(OIF, nil, nil)
 		r.Ninit.Set(cas.Ninit.Slice())
 		switch n.Op {
 		default:
@@ -228,7 +228,7 @@ func walkselect(sel *Node) {
 
 			// if c != nil && selectnbrecv(&v, c) { body } else { default body }
 		case OSELRECV:
-			r = Nod(OIF, nil, nil)
+			r = nod(OIF, nil, nil)
 
 			r.Ninit.Set(cas.Ninit.Slice())
 			ch := n.Right.Left
@@ -236,7 +236,7 @@ func walkselect(sel *Node) {
 
 			// if c != nil && selectnbrecv2(&v, c) { body } else { default body }
 		case OSELRECV2:
-			r = Nod(OIF, nil, nil)
+			r = nod(OIF, nil, nil)
 
 			r.Ninit.Set(cas.Ninit.Slice())
 			ch := n.Right.Left
@@ -257,18 +257,18 @@ func walkselect(sel *Node) {
 	setlineno(sel)
 
 	selv = temp(selecttype(int32(sel.Xoffset)))
-	r = Nod(OAS, selv, nil)
+	r = nod(OAS, selv, nil)
 	r = typecheck(r, Etop)
 	init = append(init, r)
-	var_ = conv(conv(Nod(OADDR, selv, nil), Types[TUNSAFEPTR]), Ptrto(Types[TUINT8]))
-	r = mkcall("newselect", nil, nil, var_, Nodintconst(selv.Type.Width), Nodintconst(sel.Xoffset))
+	var_ = conv(conv(nod(OADDR, selv, nil), Types[TUNSAFEPTR]), ptrto(Types[TUINT8]))
+	r = mkcall("newselect", nil, nil, var_, nodintconst(selv.Type.Width), nodintconst(sel.Xoffset))
 	r = typecheck(r, Etop)
 	init = append(init, r)
 	// register cases
 	for _, cas := range sel.List.Slice() {
 		setlineno(cas)
 		n = cas.Left
-		r = Nod(OIF, nil, nil)
+		r = nod(OIF, nil, nil)
 		r.Ninit.Set(cas.Ninit.Slice())
 		cas.Ninit.Set(nil)
 		if n != nil {
@@ -299,10 +299,10 @@ func walkselect(sel *Node) {
 		}
 
 		// selv is no longer alive after use.
-		r.Nbody.Append(Nod(OVARKILL, selv, nil))
+		r.Nbody.Append(nod(OVARKILL, selv, nil))
 
 		r.Nbody.AppendNodes(&cas.Nbody)
-		r.Nbody.Append(Nod(OBREAK, nil, nil))
+		r.Nbody.Append(nod(OBREAK, nil, nil))
 		init = append(init, r)
 	}
 
@@ -323,29 +323,29 @@ func selecttype(size int32) *Type {
 	// TODO(dvyukov): it's possible to generate Scase only once
 	// and then cache; and also cache Select per size.
 
-	scase := Nod(OTSTRUCT, nil, nil)
-	scase.List.Append(Nod(ODCLFIELD, newname(Lookup("elem")), typenod(Ptrto(Types[TUINT8]))))
-	scase.List.Append(Nod(ODCLFIELD, newname(Lookup("chan")), typenod(Ptrto(Types[TUINT8]))))
-	scase.List.Append(Nod(ODCLFIELD, newname(Lookup("pc")), typenod(Types[TUINTPTR])))
-	scase.List.Append(Nod(ODCLFIELD, newname(Lookup("kind")), typenod(Types[TUINT16])))
-	scase.List.Append(Nod(ODCLFIELD, newname(Lookup("so")), typenod(Types[TUINT16])))
-	scase.List.Append(Nod(ODCLFIELD, newname(Lookup("receivedp")), typenod(Ptrto(Types[TUINT8]))))
-	scase.List.Append(Nod(ODCLFIELD, newname(Lookup("releasetime")), typenod(Types[TUINT64])))
+	scase := nod(OTSTRUCT, nil, nil)
+	scase.List.Append(nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(Types[TUINT8]))))
+	scase.List.Append(nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(Types[TUINT8]))))
+	scase.List.Append(nod(ODCLFIELD, newname(lookup("pc")), typenod(Types[TUINTPTR])))
+	scase.List.Append(nod(ODCLFIELD, newname(lookup("kind")), typenod(Types[TUINT16])))
+	scase.List.Append(nod(ODCLFIELD, newname(lookup("so")), typenod(Types[TUINT16])))
+	scase.List.Append(nod(ODCLFIELD, newname(lookup("receivedp")), typenod(ptrto(Types[TUINT8]))))
+	scase.List.Append(nod(ODCLFIELD, newname(lookup("releasetime")), typenod(Types[TUINT64])))
 	scase = typecheck(scase, Etype)
 	scase.Type.Noalg = true
 	scase.Type.Local = true
 
-	sel := Nod(OTSTRUCT, nil, nil)
-	sel.List.Append(Nod(ODCLFIELD, newname(Lookup("tcase")), typenod(Types[TUINT16])))
-	sel.List.Append(Nod(ODCLFIELD, newname(Lookup("ncase")), typenod(Types[TUINT16])))
-	sel.List.Append(Nod(ODCLFIELD, newname(Lookup("pollorder")), typenod(Ptrto(Types[TUINT8]))))
-	sel.List.Append(Nod(ODCLFIELD, newname(Lookup("lockorder")), typenod(Ptrto(Types[TUINT8]))))
-	arr := Nod(OTARRAY, Nodintconst(int64(size)), scase)
-	sel.List.Append(Nod(ODCLFIELD, newname(Lookup("scase")), arr))
-	arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Types[TUINT16]))
-	sel.List.Append(Nod(ODCLFIELD, newname(Lookup("lockorderarr")), arr))
-	arr = Nod(OTARRAY, Nodintconst(int64(size)), typenod(Types[TUINT16]))
-	sel.List.Append(Nod(ODCLFIELD, newname(Lookup("pollorderarr")), arr))
+	sel := nod(OTSTRUCT, nil, nil)
+	sel.List.Append(nod(ODCLFIELD, newname(lookup("tcase")), typenod(Types[TUINT16])))
+	sel.List.Append(nod(ODCLFIELD, newname(lookup("ncase")), typenod(Types[TUINT16])))
+	sel.List.Append(nod(ODCLFIELD, newname(lookup("pollorder")), typenod(ptrto(Types[TUINT8]))))
+	sel.List.Append(nod(ODCLFIELD, newname(lookup("lockorder")), typenod(ptrto(Types[TUINT8]))))
+	arr := nod(OTARRAY, nodintconst(int64(size)), scase)
+	sel.List.Append(nod(ODCLFIELD, newname(lookup("scase")), arr))
+	arr = nod(OTARRAY, nodintconst(int64(size)), typenod(Types[TUINT16]))
+	sel.List.Append(nod(ODCLFIELD, newname(lookup("lockorderarr")), arr))
+	arr = nod(OTARRAY, nodintconst(int64(size)), typenod(Types[TUINT16]))
+	sel.List.Append(nod(ODCLFIELD, newname(lookup("pollorderarr")), arr))
 	sel = typecheck(sel, Etype)
 	sel.Type.Noalg = true
 	sel.Type.Local = true
diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
index 4469d71..1192f3f 100644
--- a/src/cmd/compile/internal/gc/sinit.go
+++ b/src/cmd/compile/internal/gc/sinit.go
@@ -4,9 +4,7 @@
 
 package gc
 
-import (
-	"fmt"
-)
+import "fmt"
 
 // static initialization
 const (
@@ -164,7 +162,7 @@ func foundinitloop(node, visited *Node) {
 	// those errors probably confused us and
 	// there might not be a loop. Let the user
 	// fix those first.
-	Flusherrors()
+	flusherrors()
 	if nerrors > 0 {
 		errorexit()
 	}
@@ -201,7 +199,7 @@ func init2(n *Node, out *[]*Node) {
 	}
 
 	if n.Op == ONAME && n.Ninit.Len() != 0 {
-		Fatalf("name %v with ninit: %v\n", n.Sym, Nconv(n, FmtSign))
+		Fatalf("name %v with ninit: %+v\n", n.Sym, n)
 	}
 
 	init1(n, out)
@@ -288,7 +286,7 @@ func staticcopy(l *Node, r *Node, out *[]*Node) bool {
 	orig := r
 	r = r.Name.Defn.Right
 
-	for r.Op == OCONVNOP {
+	for r.Op == OCONVNOP && !eqtype(r.Type, l.Type) {
 		r = r.Left
 	}
 
@@ -297,7 +295,7 @@ func staticcopy(l *Node, r *Node, out *[]*Node) bool {
 		if staticcopy(l, r, out) {
 			return true
 		}
-		*out = append(*out, Nod(OAS, l, r))
+		*out = append(*out, nod(OAS, l, r))
 		return true
 
 	case OLITERAL:
@@ -316,33 +314,26 @@ func staticcopy(l *Node, r *Node, out *[]*Node) bool {
 
 	case OPTRLIT:
 		switch r.Left.Op {
-		//dump("not static addr", r);
-		default:
-			break
-
+		case OARRAYLIT, OSLICELIT, OSTRUCTLIT, OMAPLIT:
 			// copy pointer
-		case OARRAYLIT, OSTRUCTLIT, OMAPLIT:
-			gdata(l, Nod(OADDR, inittemps[r], nil), int(l.Type.Width))
-
+			gdata(l, nod(OADDR, inittemps[r], nil), int(l.Type.Width))
 			return true
 		}
 
-	case OARRAYLIT:
-		if r.Type.IsSlice() {
-			// copy slice
-			a := inittemps[r]
+	case OSLICELIT:
+		// copy slice
+		a := inittemps[r]
 
-			n := *l
-			n.Xoffset = l.Xoffset + int64(Array_array)
-			gdata(&n, Nod(OADDR, a, nil), Widthptr)
-			n.Xoffset = l.Xoffset + int64(Array_nel)
-			gdata(&n, r.Right, Widthint)
-			n.Xoffset = l.Xoffset + int64(Array_cap)
-			gdata(&n, r.Right, Widthint)
-			return true
-		}
-		fallthrough
-	case OSTRUCTLIT:
+		n := *l
+		n.Xoffset = l.Xoffset + int64(array_array)
+		gdata(&n, nod(OADDR, a, nil), Widthptr)
+		n.Xoffset = l.Xoffset + int64(array_nel)
+		gdata(&n, r.Right, Widthint)
+		n.Xoffset = l.Xoffset + int64(array_cap)
+		gdata(&n, r.Right, Widthint)
+		return true
+
+	case OARRAYLIT, OSTRUCTLIT:
 		p := initplans[r]
 
 		n := *l
@@ -353,20 +344,20 @@ func staticcopy(l *Node, r *Node, out *[]*Node) bool {
 			if e.Expr.Op == OLITERAL {
 				gdata(&n, e.Expr, int(n.Type.Width))
 			} else {
-				ll := Nod(OXXX, nil, nil)
+				ll := nod(OXXX, nil, nil)
 				*ll = n
 				ll.Orig = ll // completely separate copy
 				if !staticassign(ll, e.Expr, out) {
 					// Requires computation, but we're
 					// copying someone else's computation.
-					rr := Nod(OXXX, nil, nil)
+					rr := nod(OXXX, nil, nil)
 
 					*rr = *orig
 					rr.Orig = rr // completely separate copy
 					rr.Type = ll.Type
 					rr.Xoffset += e.Xoffset
 					setlineno(rr)
-					*out = append(*out, Nod(OAS, ll, rr))
+					*out = append(*out, nod(OAS, ll, rr))
 				}
 			}
 		}
@@ -405,16 +396,16 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool {
 
 	case OPTRLIT:
 		switch r.Left.Op {
-		case OARRAYLIT, OMAPLIT, OSTRUCTLIT:
+		case OARRAYLIT, OSLICELIT, OMAPLIT, OSTRUCTLIT:
 			// Init pointer.
-			a := staticname(r.Left.Type, 1)
+			a := staticname(r.Left.Type)
 
 			inittemps[r] = a
-			gdata(l, Nod(OADDR, a, nil), int(l.Type.Width))
+			gdata(l, nod(OADDR, a, nil), int(l.Type.Width))
 
 			// Init underlying literal.
 			if !staticassign(a, r.Left, out) {
-				*out = append(*out, Nod(OAS, a, r.Left))
+				*out = append(*out, nod(OAS, a, r.Left))
 			}
 			return true
 		}
@@ -427,28 +418,26 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool {
 			return true
 		}
 
-	case OARRAYLIT:
+	case OSLICELIT:
 		initplan(r)
-		if r.Type.IsSlice() {
-			// Init slice.
-			bound := r.Right.Int64()
-			ta := typArray(r.Type.Elem(), bound)
-			a := staticname(ta, 1)
-			inittemps[r] = a
-			n := *l
-			n.Xoffset = l.Xoffset + int64(Array_array)
-			gdata(&n, Nod(OADDR, a, nil), Widthptr)
-			n.Xoffset = l.Xoffset + int64(Array_nel)
-			gdata(&n, r.Right, Widthint)
-			n.Xoffset = l.Xoffset + int64(Array_cap)
-			gdata(&n, r.Right, Widthint)
-
-			// Fall through to init underlying array.
-			l = a
-		}
+		// Init slice.
+		bound := r.Right.Int64()
+		ta := typArray(r.Type.Elem(), bound)
+		a := staticname(ta)
+		inittemps[r] = a
+		n := *l
+		n.Xoffset = l.Xoffset + int64(array_array)
+		gdata(&n, nod(OADDR, a, nil), Widthptr)
+		n.Xoffset = l.Xoffset + int64(array_nel)
+		gdata(&n, r.Right, Widthint)
+		n.Xoffset = l.Xoffset + int64(array_cap)
+		gdata(&n, r.Right, Widthint)
+
+		// Fall through to init underlying array.
+		l = a
 		fallthrough
 
-	case OSTRUCTLIT:
+	case OARRAYLIT, OSTRUCTLIT:
 		initplan(r)
 
 		p := initplans[r]
@@ -461,11 +450,11 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool {
 				gdata(&n, e.Expr, int(n.Type.Width))
 			} else {
 				setlineno(e.Expr)
-				a := Nod(OXXX, nil, nil)
+				a := nod(OXXX, nil, nil)
 				*a = n
 				a.Orig = a // completely separate copy
 				if !staticassign(a, e.Expr, out) {
-					*out = append(*out, Nod(OAS, a, e.Expr))
+					*out = append(*out, nod(OAS, a, e.Expr))
 				}
 			}
 		}
@@ -473,7 +462,6 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool {
 		return true
 
 	case OMAPLIT:
-		// TODO: Table-driven map insert.
 		break
 
 	case OCLOSURE:
@@ -489,23 +477,102 @@ func staticassign(l *Node, r *Node, out *[]*Node) bool {
 		} else {
 			closuredebugruntimecheck(r)
 		}
+
+	case OCONVIFACE:
+		// This logic is mirrored in isStaticCompositeLiteral.
+		// If you change something here, change it there, and vice versa.
+
+		// Determine the underlying concrete type and value we are converting from.
+		val := r
+		for val.Op == OCONVIFACE {
+			val = val.Left
+		}
+		if val.Type.IsInterface() {
+			// val is an interface type.
+			// If val is nil, we can statically initialize l;
+			// both words are zero and so there no work to do, so report success.
+			// If val is non-nil, we have no concrete type to record,
+			// and we won't be able to statically initialize its value, so report failure.
+			return Isconst(val, CTNIL)
+		}
+
+		var itab *Node
+		if l.Type.IsEmptyInterface() {
+			itab = typename(val.Type)
+		} else {
+			itab = itabname(val.Type, l.Type)
+		}
+
+		// Create a copy of l to modify while we emit data.
+		n := *l
+
+		// Emit itab, advance offset.
+		gdata(&n, itab, Widthptr)
+		n.Xoffset += int64(Widthptr)
+
+		// Emit data.
+		if isdirectiface(val.Type) {
+			if Isconst(val, CTNIL) {
+				// Nil is zero, nothing to do.
+				return true
+			}
+			// Copy val directly into n.
+			n.Type = val.Type
+			setlineno(val)
+			a := nod(OXXX, nil, nil)
+			*a = n
+			a.Orig = a
+			if !staticassign(a, val, out) {
+				*out = append(*out, nod(OAS, a, val))
+			}
+		} else {
+			// Construct temp to hold val, write pointer to temp into n.
+			a := staticname(val.Type)
+			inittemps[val] = a
+			if !staticassign(a, val, out) {
+				*out = append(*out, nod(OAS, a, val))
+			}
+			ptr := nod(OADDR, a, nil)
+			n.Type = ptrto(val.Type)
+			gdata(&n, ptr, Widthptr)
+		}
+
+		return true
 	}
 
 	//dump("not static", r);
 	return false
 }
 
+// initContext is the context in which static data is populated.
+// It is either in an init function or in any other function.
+// Static data populated in an init function will be written either
+// zero times (as a readonly, static data symbol) or
+// one time (during init function execution).
+// Either way, there is no opportunity for races or further modification,
+// so the data can be written to a (possibly readonly) data symbol.
+// Static data populated in any other function needs to be local to
+// that function to allow multiple instances of that function
+// to execute concurrently without clobbering each others' data.
+type initContext uint8
+
+const (
+	inInitFunction initContext = iota
+	inNonInitFunction
+)
+
 // from here down is the walk analysis
 // of composite literals.
 // most of the work is to generate
 // data statements for the constant
 // part of the composite literal.
-func staticname(t *Type, ctxt int) *Node {
-	n := newname(LookupN("statictmp_", statuniqgen))
+
+// staticname returns a name backed by a static data symbol.
+// Callers should set n.Name.Readonly = true on the
+// returned node for readonly nodes.
+func staticname(t *Type) *Node {
+	n := newname(lookupN("statictmp_", statuniqgen))
 	statuniqgen++
-	if ctxt == 0 {
-		n.Name.Readonly = true
-	}
 	addvar(n, t, PEXTERN)
 	return n
 }
@@ -520,7 +587,7 @@ func (n *Node) isSimpleName() bool {
 }
 
 func litas(l *Node, r *Node, init *Nodes) {
-	a := Nod(OAS, l, r)
+	a := nod(OAS, l, r)
 	a = typecheck(a, Etop)
 	a = walkexpr(a, init)
 	init.Append(a)
@@ -534,7 +601,9 @@ const (
 	initConst                           // contains some constant values, which may be written into data symbols
 )
 
-func getdyn(n *Node, top int) initGenType {
+// getdyn calculates the initGenType for n.
+// If top is false, getdyn is recursing.
+func getdyn(n *Node, top bool) initGenType {
 	switch n.Op {
 	default:
 		if isliteral(n) {
@@ -542,18 +611,23 @@ func getdyn(n *Node, top int) initGenType {
 		}
 		return initDynamic
 
-	case OARRAYLIT:
-		if top == 0 && n.Type.IsSlice() {
+	case OSLICELIT:
+		if !top {
 			return initDynamic
 		}
 
-	case OSTRUCTLIT:
+	case OARRAYLIT, OSTRUCTLIT:
 	}
 
 	var mode initGenType
 	for _, n1 := range n.List.Slice() {
-		value := n1.Right
-		mode |= getdyn(value, 0)
+		switch n1.Op {
+		case OKEY:
+			n1 = n1.Right
+		case OSTRUCTKEY:
+			n1 = n1.Left
+		}
+		mode |= getdyn(n1, false)
 		if mode == initDynamic|initConst {
 			break
 		}
@@ -564,174 +638,156 @@ func getdyn(n *Node, top int) initGenType {
 // isStaticCompositeLiteral reports whether n is a compile-time constant.
 func isStaticCompositeLiteral(n *Node) bool {
 	switch n.Op {
+	case OSLICELIT:
+		return false
 	case OARRAYLIT:
-		if n.Type.IsSlice() {
-			return false
+		for _, r := range n.List.Slice() {
+			if r.Op == OKEY {
+				r = r.Right
+			}
+			if !isStaticCompositeLiteral(r) {
+				return false
+			}
 		}
+		return true
 	case OSTRUCTLIT:
+		for _, r := range n.List.Slice() {
+			if r.Op != OSTRUCTKEY {
+				Fatalf("isStaticCompositeLiteral: rhs not OSTRUCTKEY: %v", r)
+			}
+			if !isStaticCompositeLiteral(r.Left) {
+				return false
+			}
+		}
+		return true
 	case OLITERAL:
 		return true
-	default:
-		return false
-	}
-	for _, r := range n.List.Slice() {
-		if r.Op != OKEY {
-			Fatalf("isStaticCompositeLiteral: rhs not OKEY: %v", r)
+	case OCONVIFACE:
+		// See staticassign's OCONVIFACE case for comments.
+		val := n
+		for val.Op == OCONVIFACE {
+			val = val.Left
 		}
-		index := r.Left
-		if n.Op == OARRAYLIT && index.Op != OLITERAL {
-			return false
+		if val.Type.IsInterface() {
+			return Isconst(val, CTNIL)
 		}
-		value := r.Right
-		if !isStaticCompositeLiteral(value) {
-			return false
+		if isdirectiface(val.Type) && Isconst(val, CTNIL) {
+			return true
 		}
+		return isStaticCompositeLiteral(val)
 	}
-	return true
+	return false
 }
 
-func structlit(ctxt int, pass int, n *Node, var_ *Node, init *Nodes) {
-	for _, r := range n.List.Slice() {
-		if r.Op != OKEY {
-			Fatalf("structlit: rhs not OKEY: %v", r)
-		}
-		index := r.Left
-		value := r.Right
-
-		switch value.Op {
-		case OARRAYLIT:
-			if value.Type.IsSlice() {
-				if pass == 1 && ctxt != 0 {
-					a := NodSym(ODOT, var_, index.Sym)
-					slicelit(ctxt, value, a, init)
-				} else if pass == 2 && ctxt == 0 {
-					a := NodSym(ODOT, var_, index.Sym)
-					slicelit(ctxt, value, a, init)
-				} else if pass == 3 {
-					break
-				}
-				continue
-			}
-
-			a := NodSym(ODOT, var_, index.Sym)
-			arraylit(ctxt, pass, value, a, init)
-			continue
+// initKind is a kind of static initialization: static, dynamic, or local.
+// Static initialization represents literals and
+// literal components of composite literals.
+// Dynamic initialization represents non-literals and
+// non-literal components of composite literals.
+// LocalCode initializion represents initialization
+// that occurs purely in generated code local to the function of use.
+// Initialization code is sometimes generated in passes,
+// first static then dynamic.
+type initKind uint8
 
-		case OSTRUCTLIT:
-			a := NodSym(ODOT, var_, index.Sym)
-			structlit(ctxt, pass, value, a, init)
-			continue
-		}
+const (
+	initKindStatic initKind = iota + 1
+	initKindDynamic
+	initKindLocalCode
+)
 
-		if isliteral(value) {
-			if pass == 2 {
-				continue
+// fixedlit handles struct, array, and slice literals.
+// TODO: expand documentation.
+func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes) {
+	var splitnode func(*Node) (a *Node, value *Node)
+	switch n.Op {
+	case OARRAYLIT, OSLICELIT:
+		var k int64
+		splitnode = func(r *Node) (*Node, *Node) {
+			if r.Op == OKEY {
+				k = nonnegintconst(r.Left)
+				r = r.Right
 			}
-		} else if pass == 1 {
-			continue
+			a := nod(OINDEX, var_, nodintconst(k))
+			k++
+			return a, r
 		}
-
-		// build list of var.field = expr
-		setlineno(value)
-		a := NodSym(ODOT, var_, index.Sym)
-
-		a = Nod(OAS, a, value)
-		a = typecheck(a, Etop)
-		if pass == 1 {
-			a = walkexpr(a, init) // add any assignments in r to top
-			if a.Op != OAS {
-				Fatalf("structlit: not as")
+	case OSTRUCTLIT:
+		splitnode = func(r *Node) (*Node, *Node) {
+			if r.Op != OSTRUCTKEY {
+				Fatalf("fixedlit: rhs not OSTRUCTKEY: %v", r)
 			}
-			a.Dodata = 2
-		} else {
-			a = orderstmtinplace(a)
-			a = walkstmt(a)
+			return nodSym(ODOT, var_, r.Sym), r.Left
 		}
-
-		init.Append(a)
+	default:
+		Fatalf("fixedlit bad op: %v", n.Op)
 	}
-}
 
-func arraylit(ctxt int, pass int, n *Node, var_ *Node, init *Nodes) {
 	for _, r := range n.List.Slice() {
-		if r.Op != OKEY {
-			Fatalf("arraylit: rhs not OKEY: %v", r)
-		}
-		index := r.Left
-		value := r.Right
+		a, value := splitnode(r)
 
 		switch value.Op {
-		case OARRAYLIT:
-			if value.Type.IsSlice() {
-				if pass == 1 && ctxt != 0 {
-					a := Nod(OINDEX, var_, index)
-					slicelit(ctxt, value, a, init)
-				} else if pass == 2 && ctxt == 0 {
-					a := Nod(OINDEX, var_, index)
-					slicelit(ctxt, value, a, init)
-				} else if pass == 3 {
-					break
-				}
+		case OSLICELIT:
+			if (kind == initKindStatic && ctxt == inNonInitFunction) || (kind == initKindDynamic && ctxt == inInitFunction) {
+				slicelit(ctxt, value, a, init)
 				continue
 			}
 
-			a := Nod(OINDEX, var_, index)
-			arraylit(ctxt, pass, value, a, init)
-			continue
-
-		case OSTRUCTLIT:
-			a := Nod(OINDEX, var_, index)
-			structlit(ctxt, pass, value, a, init)
+		case OARRAYLIT, OSTRUCTLIT:
+			fixedlit(ctxt, kind, value, a, init)
 			continue
 		}
 
-		if isliteral(index) && isliteral(value) {
-			if pass == 2 {
-				continue
-			}
-		} else if pass == 1 {
+		islit := isliteral(value)
+		if (kind == initKindStatic && !islit) || (kind == initKindDynamic && islit) {
 			continue
 		}
 
-		// build list of var[index] = value
+		// build list of assignments: var[index] = expr
 		setlineno(value)
-		a := Nod(OINDEX, var_, index)
-
-		a = Nod(OAS, a, value)
+		a = nod(OAS, a, value)
 		a = typecheck(a, Etop)
-		if pass == 1 {
-			a = walkexpr(a, init)
+		switch kind {
+		case initKindStatic:
+			a = walkexpr(a, init) // add any assignments in r to top
+			if a.Op == OASWB {
+				// Static initialization never needs
+				// write barriers.
+				a.Op = OAS
+			}
 			if a.Op != OAS {
-				Fatalf("arraylit: not as")
+				Fatalf("fixedlit: not as, is %v", a)
 			}
-			a.Dodata = 2
-		} else {
+			a.IsStatic = true
+		case initKindDynamic, initKindLocalCode:
 			a = orderstmtinplace(a)
 			a = walkstmt(a)
+		default:
+			Fatalf("fixedlit: bad kind %d", kind)
 		}
 
 		init.Append(a)
 	}
 }
 
-func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
+func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
 	// make an array type corresponding the number of elements we have
 	t := typArray(n.Type.Elem(), n.Right.Int64())
 	dowidth(t)
 
-	if ctxt != 0 {
+	if ctxt == inNonInitFunction {
 		// put everything into static array
-		vstat := staticname(t, ctxt)
+		vstat := staticname(t)
 
-		arraylit(ctxt, 1, n, vstat, init)
-		arraylit(ctxt, 2, n, vstat, init)
+		fixedlit(ctxt, initKindStatic, n, vstat, init)
+		fixedlit(ctxt, initKindDynamic, n, vstat, init)
 
 		// copy static to slice
-		a := Nod(OSLICE, vstat, nil)
+		a := nod(OSLICE, vstat, nil)
 
-		a = Nod(OAS, var_, a)
+		a = nod(OAS, var_, a)
 		a = typecheck(a, Etop)
-		a.Dodata = 2
+		a.IsStatic = true
 		init.Append(a)
 		return
 	}
@@ -759,14 +815,17 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
 	// make static initialized array (1),(2)
 	var vstat *Node
 
-	mode := getdyn(n, 1)
+	mode := getdyn(n, true)
 	if mode&initConst != 0 {
-		vstat = staticname(t, ctxt)
-		arraylit(ctxt, 1, n, vstat, init)
+		vstat = staticname(t)
+		if ctxt == inInitFunction {
+			vstat.Name.Readonly = true
+		}
+		fixedlit(ctxt, initKindStatic, n, vstat, init)
 	}
 
 	// make new auto *array (3 declare)
-	vauto := temp(Ptrto(t))
+	vauto := temp(ptrto(t))
 
 	// set auto to point at new temp or heap (3 assign)
 	var a *Node
@@ -775,74 +834,72 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
 		x.Type = t
 
 		if vstat == nil {
-			a = Nod(OAS, x, nil)
+			a = nod(OAS, x, nil)
 			a = typecheck(a, Etop)
 			init.Append(a) // zero new temp
 		}
 
-		a = Nod(OADDR, x, nil)
+		a = nod(OADDR, x, nil)
 	} else if n.Esc == EscNone {
 		a = temp(t)
 		if vstat == nil {
-			a = Nod(OAS, temp(t), nil)
+			a = nod(OAS, temp(t), nil)
 			a = typecheck(a, Etop)
 			init.Append(a) // zero new temp
 			a = a.Left
 		}
 
-		a = Nod(OADDR, a, nil)
+		a = nod(OADDR, a, nil)
 	} else {
-		a = Nod(ONEW, nil, nil)
+		a = nod(ONEW, nil, nil)
 		a.List.Set1(typenod(t))
 	}
 
-	a = Nod(OAS, vauto, a)
+	a = nod(OAS, vauto, a)
 	a = typecheck(a, Etop)
 	a = walkexpr(a, init)
 	init.Append(a)
 
 	if vstat != nil {
 		// copy static to heap (4)
-		a = Nod(OIND, vauto, nil)
+		a = nod(OIND, vauto, nil)
 
-		a = Nod(OAS, a, vstat)
+		a = nod(OAS, a, vstat)
 		a = typecheck(a, Etop)
 		a = walkexpr(a, init)
 		init.Append(a)
 	}
 
 	// put dynamics into array (5)
+	var index int64
 	for _, r := range n.List.Slice() {
-		if r.Op != OKEY {
-			Fatalf("slicelit: rhs not OKEY: %v", r)
+		value := r
+		if r.Op == OKEY {
+			index = nonnegintconst(r.Left)
+			value = r.Right
 		}
-		index := r.Left
-		value := r.Right
-		a := Nod(OINDEX, vauto, index)
+		a := nod(OINDEX, vauto, nodintconst(index))
 		a.Bounded = true
+		index++
 
 		// TODO need to check bounds?
 
 		switch value.Op {
-		case OARRAYLIT:
-			if value.Type.IsSlice() {
-				break
-			}
-			arraylit(ctxt, 2, value, a, init)
-			continue
+		case OSLICELIT:
+			break
 
-		case OSTRUCTLIT:
-			structlit(ctxt, 2, value, a, init)
+		case OARRAYLIT, OSTRUCTLIT:
+			fixedlit(ctxt, initKindDynamic, value, a, init)
 			continue
 		}
 
-		if isliteral(index) && isliteral(value) {
+		if isliteral(value) {
 			continue
 		}
 
 		// build list of vauto[c] = expr
 		setlineno(value)
-		a = Nod(OAS, a, value)
+		a = nod(OAS, a, value)
 
 		a = typecheck(a, Etop)
 		a = orderstmtinplace(a)
@@ -851,7 +908,7 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
 	}
 
 	// make slice out of heap (6)
-	a = Nod(OAS, var_, Nod(OSLICE, vauto, nil))
+	a = nod(OAS, var_, nod(OSLICE, vauto, nil))
 
 	a = typecheck(a, Etop)
 	a = orderstmtinplace(a)
@@ -859,15 +916,13 @@ func slicelit(ctxt int, n *Node, var_ *Node, init *Nodes) {
 	init.Append(a)
 }
 
-func maplit(ctxt int, n *Node, var_ *Node, init *Nodes) {
-	ctxt = 0
-
+func maplit(n *Node, m *Node, init *Nodes) {
 	// make the map var
 	nerr := nerrors
 
-	a := Nod(OMAKE, nil, nil)
-	a.List.Set1(typenod(n.Type))
-	litas(var_, a, init)
+	a := nod(OMAKE, nil, nil)
+	a.List.Set2(typenod(n.Type), nodintconst(int64(len(n.List.Slice()))))
+	litas(m, a, init)
 
 	// count the initializers
 	b := 0
@@ -884,32 +939,19 @@ func maplit(ctxt int, n *Node, var_ *Node, init *Nodes) {
 	}
 
 	if b != 0 {
-		// build type [count]struct { a Tindex, b Tvalue }
-		t := n.Type
-		tk := t.Key()
-		tv := t.Val()
-
-		syma := Lookup("a")
-		symb := Lookup("b")
-
-		var fields [2]*Field
-		fields[0] = newField()
-		fields[0].Type = tk
-		fields[0].Sym = syma
-		fields[1] = newField()
-		fields[1].Type = tv
-		fields[1].Sym = symb
-
-		tstruct := typ(TSTRUCT)
-		tstruct.SetFields(fields[:])
-
-		tarr := typArray(tstruct, int64(b))
+		// build types [count]Tindex and [count]Tvalue
+		tk := typArray(n.Type.Key(), int64(b))
+		tv := typArray(n.Type.Val(), int64(b))
 
 		// TODO(josharian): suppress alg generation for these types?
-		dowidth(tarr)
+		dowidth(tk)
+		dowidth(tv)
 
-		// make and initialize static array
-		vstat := staticname(tarr, ctxt)
+		// make and initialize static arrays
+		vstatk := staticname(tk)
+		vstatk.Name.Readonly = true
+		vstatv := staticname(tv)
+		vstatv.Name.Readonly = true
 
 		b := int64(0)
 		for _, r := range n.List.Slice() {
@@ -920,61 +962,52 @@ func maplit(ctxt int, n *Node, var_ *Node, init *Nodes) {
 			value := r.Right
 
 			if isliteral(index) && isliteral(value) {
-				// build vstat[b].a = key;
+				// build vstatk[b] = index
 				setlineno(index)
-				a = Nodintconst(b)
-
-				a = Nod(OINDEX, vstat, a)
-				a = NodSym(ODOT, a, syma)
-				a = Nod(OAS, a, index)
-				a = typecheck(a, Etop)
-				a = walkexpr(a, init)
-				a.Dodata = 2
-				init.Append(a)
-
-				// build vstat[b].b = value;
+				lhs := nod(OINDEX, vstatk, nodintconst(b))
+				as := nod(OAS, lhs, index)
+				as = typecheck(as, Etop)
+				as = walkexpr(as, init)
+				as.IsStatic = true
+				init.Append(as)
+
+				// build vstatv[b] = value
 				setlineno(value)
-				a = Nodintconst(b)
-
-				a = Nod(OINDEX, vstat, a)
-				a = NodSym(ODOT, a, symb)
-				a = Nod(OAS, a, value)
-				a = typecheck(a, Etop)
-				a = walkexpr(a, init)
-				a.Dodata = 2
-				init.Append(a)
+				lhs = nod(OINDEX, vstatv, nodintconst(b))
+				as = nod(OAS, lhs, value)
+				as = typecheck(as, Etop)
+				as = walkexpr(as, init)
+				as.IsStatic = true
+				init.Append(as)
 
 				b++
 			}
 		}
 
 		// loop adding structure elements to map
-		// for i = 0; i < len(vstat); i++ {
-		//	map[vstat[i].a] = vstat[i].b
+		// for i = 0; i < len(vstatk); i++ {
+		//	map[vstatk[i]] = vstatv[i]
 		// }
-		index := temp(Types[TINT])
-
-		a = Nod(OINDEX, vstat, index)
-		a.Bounded = true
-		a = NodSym(ODOT, a, symb)
-
-		r := Nod(OINDEX, vstat, index)
-		r.Bounded = true
-		r = NodSym(ODOT, r, syma)
-		r = Nod(OINDEX, var_, r)
+		i := temp(Types[TINT])
+		rhs := nod(OINDEX, vstatv, i)
+		rhs.Bounded = true
 
-		r = Nod(OAS, r, a)
+		kidx := nod(OINDEX, vstatk, i)
+		kidx.Bounded = true
+		lhs := nod(OINDEX, m, kidx)
 
-		a = Nod(OFOR, nil, nil)
-		a.Nbody.Set1(r)
+		zero := nod(OAS, i, nodintconst(0))
+		cond := nod(OLT, i, nodintconst(tk.NumElem()))
+		incr := nod(OAS, i, nod(OADD, i, nodintconst(1)))
+		body := nod(OAS, lhs, rhs)
 
-		a.Ninit.Set1(Nod(OAS, index, Nodintconst(0)))
-		a.Left = Nod(OLT, index, Nodintconst(tarr.NumElem()))
-		a.Right = Nod(OAS, index, Nod(OADD, index, Nodintconst(1)))
+		loop := nod(OFOR, cond, incr)
+		loop.Nbody.Set1(body)
+		loop.Ninit.Set1(zero)
 
-		a = typecheck(a, Etop)
-		a = walkstmt(a)
-		init.Append(a)
+		loop = typecheck(loop, Etop)
+		loop = walkstmt(loop)
+		init.Append(loop)
 	}
 
 	// put in dynamic entries one-at-a-time
@@ -993,23 +1026,24 @@ func maplit(ctxt int, n *Node, var_ *Node, init *Nodes) {
 		// build list of var[c] = expr.
 		// use temporary so that mapassign1 can have addressable key, val.
 		if key == nil {
-			key = temp(var_.Type.Key())
-			val = temp(var_.Type.Val())
+			key = temp(m.Type.Key())
+			val = temp(m.Type.Val())
 		}
 
-		setlineno(r.Left)
-		a = Nod(OAS, key, r.Left)
+		setlineno(index)
+		a = nod(OAS, key, index)
 		a = typecheck(a, Etop)
 		a = walkstmt(a)
 		init.Append(a)
-		setlineno(r.Right)
-		a = Nod(OAS, val, r.Right)
+
+		setlineno(value)
+		a = nod(OAS, val, value)
 		a = typecheck(a, Etop)
 		a = walkstmt(a)
 		init.Append(a)
 
 		setlineno(val)
-		a = Nod(OAS, Nod(OINDEX, var_, key), val)
+		a = nod(OAS, nod(OINDEX, m, key), val)
 		a = typecheck(a, Etop)
 		a = walkstmt(a)
 		init.Append(a)
@@ -1020,16 +1054,16 @@ func maplit(ctxt int, n *Node, var_ *Node, init *Nodes) {
 	}
 
 	if key != nil {
-		a = Nod(OVARKILL, key, nil)
+		a = nod(OVARKILL, key, nil)
 		a = typecheck(a, Etop)
 		init.Append(a)
-		a = Nod(OVARKILL, val, nil)
+		a = nod(OVARKILL, val, nil)
 		a = typecheck(a, Etop)
 		init.Append(a)
 	}
 }
 
-func anylit(ctxt int, n *Node, var_ *Node, init *Nodes) {
+func anylit(n *Node, var_ *Node, init *Nodes) {
 	t := n.Type
 	switch n.Op {
 	default:
@@ -1042,154 +1076,109 @@ func anylit(ctxt int, n *Node, var_ *Node, init *Nodes) {
 
 		var r *Node
 		if n.Right != nil {
-			r = Nod(OADDR, n.Right, nil)
+			r = nod(OADDR, n.Right, nil)
 			r = typecheck(r, Erv)
 		} else {
-			r = Nod(ONEW, nil, nil)
+			r = nod(ONEW, nil, nil)
 			r.Typecheck = 1
 			r.Type = t
 			r.Esc = n.Esc
 		}
 
 		r = walkexpr(r, init)
-		a := Nod(OAS, var_, r)
+		a := nod(OAS, var_, r)
 
 		a = typecheck(a, Etop)
 		init.Append(a)
 
-		var_ = Nod(OIND, var_, nil)
+		var_ = nod(OIND, var_, nil)
 		var_ = typecheck(var_, Erv|Easgn)
-		anylit(ctxt, n.Left, var_, init)
+		anylit(n.Left, var_, init)
 
-	case OSTRUCTLIT:
-		if !t.IsStruct() {
-			Fatalf("anylit: not struct")
+	case OSTRUCTLIT, OARRAYLIT:
+		if !t.IsStruct() && !t.IsArray() {
+			Fatalf("anylit: not struct/array")
 		}
 
 		if var_.isSimpleName() && n.List.Len() > 4 {
-			if ctxt == 0 {
-				// lay out static data
-				vstat := staticname(t, ctxt)
+			// lay out static data
+			vstat := staticname(t)
+			vstat.Name.Readonly = true
 
-				structlit(ctxt, 1, n, vstat, init)
-
-				// copy static to var
-				a := Nod(OAS, var_, vstat)
-
-				a = typecheck(a, Etop)
-				a = walkexpr(a, init)
-				init.Append(a)
-
-				// add expressions to automatic
-				structlit(ctxt, 2, n, var_, init)
-
-				break
+			ctxt := inInitFunction
+			if n.Op == OARRAYLIT {
+				ctxt = inNonInitFunction
 			}
+			fixedlit(ctxt, initKindStatic, n, vstat, init)
 
-			structlit(ctxt, 1, n, var_, init)
-			structlit(ctxt, 2, n, var_, init)
-			break
-		}
+			// copy static to var
+			a := nod(OAS, var_, vstat)
 
-		// initialize of not completely specified
-		if var_.isSimpleName() || n.List.Len() < t.NumFields() {
-			a := Nod(OAS, var_, nil)
 			a = typecheck(a, Etop)
 			a = walkexpr(a, init)
 			init.Append(a)
-		}
-
-		structlit(ctxt, 3, n, var_, init)
 
-	case OARRAYLIT:
-		if t.IsSlice() {
-			slicelit(ctxt, n, var_, init)
+			// add expressions to automatic
+			fixedlit(inInitFunction, initKindDynamic, n, var_, init)
 			break
 		}
-		if !t.IsArray() {
-			Fatalf("anylit: not array")
-		}
-
-		if var_.isSimpleName() && n.List.Len() > 4 {
-			if ctxt == 0 {
-				// lay out static data
-				vstat := staticname(t, ctxt)
 
-				arraylit(1, 1, n, vstat, init)
-
-				// copy static to automatic
-				a := Nod(OAS, var_, vstat)
-
-				a = typecheck(a, Etop)
-				a = walkexpr(a, init)
-				init.Append(a)
-
-				// add expressions to automatic
-				arraylit(ctxt, 2, n, var_, init)
-
-				break
-			}
-
-			arraylit(ctxt, 1, n, var_, init)
-			arraylit(ctxt, 2, n, var_, init)
-			break
+		var components int64
+		if n.Op == OARRAYLIT {
+			components = t.NumElem()
+		} else {
+			components = int64(t.NumFields())
 		}
-
-		// initialize of not completely specified
-		if var_.isSimpleName() || int64(n.List.Len()) < t.NumElem() {
-			a := Nod(OAS, var_, nil)
+		// initialization of an array or struct with unspecified components (missing fields or arrays)
+		if var_.isSimpleName() || int64(n.List.Len()) < components {
+			a := nod(OAS, var_, nil)
 			a = typecheck(a, Etop)
 			a = walkexpr(a, init)
 			init.Append(a)
 		}
 
-		arraylit(ctxt, 3, n, var_, init)
+		fixedlit(inInitFunction, initKindLocalCode, n, var_, init)
+
+	case OSLICELIT:
+		slicelit(inInitFunction, n, var_, init)
 
 	case OMAPLIT:
 		if !t.IsMap() {
 			Fatalf("anylit: not map")
 		}
-		maplit(ctxt, n, var_, init)
+		maplit(n, var_, init)
 	}
 }
 
 func oaslit(n *Node, init *Nodes) bool {
 	if n.Left == nil || n.Right == nil {
-		// not a special composit literal assignment
+		// not a special composite literal assignment
 		return false
 	}
 	if n.Left.Type == nil || n.Right.Type == nil {
-		// not a special composit literal assignment
+		// not a special composite literal assignment
 		return false
 	}
 	if !n.Left.isSimpleName() {
-		// not a special composit literal assignment
+		// not a special composite literal assignment
 		return false
 	}
-	if !Eqtype(n.Left.Type, n.Right.Type) {
-		// not a special composit literal assignment
+	if !eqtype(n.Left.Type, n.Right.Type) {
+		// not a special composite literal assignment
 		return false
 	}
 
-	// context is init() function.
-	// implies generated data executed
-	// exactly once and not subject to races.
-	ctxt := 0
-
-	//	if(n->dodata == 1)
-	//		ctxt = 1;
-
 	switch n.Right.Op {
 	default:
-		// not a special composit literal assignment
+		// not a special composite literal assignment
 		return false
 
-	case OSTRUCTLIT, OARRAYLIT, OMAPLIT:
+	case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:
 		if vmatch1(n.Left, n.Right) {
-			// not a special composit literal assignment
+			// not a special composite literal assignment
 			return false
 		}
-		anylit(ctxt, n.Right, n.Left, init)
+		anylit(n.Right, n.Left, init)
 	}
 
 	n.Op = OEMPTY
@@ -1198,7 +1187,7 @@ func oaslit(n *Node, init *Nodes) bool {
 }
 
 func getlit(lit *Node) int {
-	if Smallintconst(lit) {
+	if smallintconst(lit) {
 		return int(lit.Int64())
 	}
 	return -1
@@ -1257,20 +1246,23 @@ func initplan(n *Node) {
 	default:
 		Fatalf("initplan")
 
-	case OARRAYLIT:
+	case OARRAYLIT, OSLICELIT:
+		var k int64
 		for _, a := range n.List.Slice() {
-			if a.Op != OKEY || !Smallintconst(a.Left) {
-				Fatalf("initplan arraylit")
+			if a.Op == OKEY {
+				k = nonnegintconst(a.Left)
+				a = a.Right
 			}
-			addvalue(p, n.Type.Elem().Width*a.Left.Int64(), a.Right)
+			addvalue(p, k*n.Type.Elem().Width, a)
+			k++
 		}
 
 	case OSTRUCTLIT:
 		for _, a := range n.List.Slice() {
-			if a.Op != OKEY || a.Left.Type != structkey {
-				Fatalf("initplan structlit")
+			if a.Op != OSTRUCTKEY {
+				Fatalf("initplan fixedlit")
 			}
-			addvalue(p, a.Left.Xoffset, a.Right)
+			addvalue(p, a.Xoffset, a.Left)
 		}
 
 	case OMAPLIT:
@@ -1327,13 +1319,19 @@ func iszero(n *Node) bool {
 		}
 
 	case OARRAYLIT:
-		if n.Type.IsSlice() {
-			break
+		for _, n1 := range n.List.Slice() {
+			if n1.Op == OKEY {
+				n1 = n1.Right
+			}
+			if !iszero(n1) {
+				return false
+			}
 		}
-		fallthrough
+		return true
+
 	case OSTRUCTLIT:
 		for _, n1 := range n.List.Slice() {
-			if !iszero(n1.Right) {
+			if !iszero(n1.Left) {
 				return false
 			}
 		}
@@ -1344,22 +1342,22 @@ func iszero(n *Node) bool {
 }
 
 func isvaluelit(n *Node) bool {
-	return (n.Op == OARRAYLIT && n.Type.IsArray()) || n.Op == OSTRUCTLIT
+	return n.Op == OARRAYLIT || n.Op == OSTRUCTLIT
 }
 
 // gen_as_init attempts to emit static data for n and reports whether it succeeded.
 // If reportOnly is true, it does not emit static data and does not modify the AST.
 func gen_as_init(n *Node, reportOnly bool) bool {
 	success := genAsInitNoCheck(n, reportOnly)
-	if !success && n.Dodata == 2 {
+	if !success && n.IsStatic {
 		Dump("\ngen_as_init", n)
-		Fatalf("gen_as_init couldn't make data statement")
+		Fatalf("gen_as_init couldn't generate static data")
 	}
 	return success
 }
 
 func genAsInitNoCheck(n *Node, reportOnly bool) bool {
-	if n.Dodata == 0 {
+	if !n.IsStatic {
 		return false
 	}
 
@@ -1370,7 +1368,7 @@ func genAsInitNoCheck(n *Node, reportOnly bool) bool {
 		return stataddr(&nam, nl) && nam.Class == PEXTERN
 	}
 
-	if nr.Type == nil || !Eqtype(nl.Type, nr.Type) {
+	if nr.Type == nil || !eqtype(nl.Type, nr.Type) {
 		return false
 	}
 
@@ -1411,47 +1409,24 @@ func genAsInitNoCheck(n *Node, reportOnly bool) bool {
 		}
 
 		if !reportOnly {
-			nam.Xoffset += int64(Array_array)
+			nam.Xoffset += int64(array_array)
 			gdata(&nam, ptr, Widthptr)
 
-			nam.Xoffset += int64(Array_nel) - int64(Array_array)
+			nam.Xoffset += int64(array_nel) - int64(array_array)
 			var nod1 Node
 			Nodconst(&nod1, Types[TINT], nr.Type.NumElem())
 			gdata(&nam, &nod1, Widthint)
 
-			nam.Xoffset += int64(Array_cap) - int64(Array_nel)
+			nam.Xoffset += int64(array_cap) - int64(array_nel)
 			gdata(&nam, &nod1, Widthint)
 		}
 
 		return true
 
 	case OLITERAL:
-		break
-	}
-
-	switch nr.Type.Etype {
-	default:
-		return false
-
-	case TBOOL, TINT8, TUINT8, TINT16, TUINT16,
-		TINT32, TUINT32, TINT64, TUINT64,
-		TINT, TUINT, TUINTPTR,
-		TPTR32, TPTR64,
-		TFLOAT32, TFLOAT64:
 		if !reportOnly {
 			gdata(&nam, nr, int(nr.Type.Width))
 		}
-
-	case TCOMPLEX64, TCOMPLEX128:
-		if !reportOnly {
-			gdatacomplex(&nam, nr.Val().U.(*Mpcplx))
-		}
-
-	case TSTRING:
-		if !reportOnly {
-			gdatastring(&nam, nr.Val().U.(string))
-		}
+		return true
 	}
-
-	return true
 }
diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go
index c474c47..b86188c 100644
--- a/src/cmd/compile/internal/gc/sizeof_test.go
+++ b/src/cmd/compile/internal/gc/sizeof_test.go
@@ -22,12 +22,12 @@ func TestSizeof(t *testing.T) {
 		_32bit uintptr     // size on 32bit platforms
 		_64bit uintptr     // size on 64bit platforms
 	}{
-		{Flow{}, 52, 88},
 		{Func{}, 92, 160},
-		{Name{}, 52, 80},
+		{Name{}, 44, 72},
+		{Param{}, 24, 48},
 		{Node{}, 92, 144},
 		{Sym{}, 60, 112},
-		{Type{}, 52, 80},
+		{Type{}, 60, 96},
 		{MapType{}, 20, 40},
 		{ForwardType{}, 16, 32},
 		{FuncType{}, 28, 48},
diff --git a/src/cmd/compile/internal/gc/sparselocatephifunctions.go b/src/cmd/compile/internal/gc/sparselocatephifunctions.go
deleted file mode 100644
index 43cc50b..0000000
--- a/src/cmd/compile/internal/gc/sparselocatephifunctions.go
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gc
-
-import (
-	"cmd/compile/internal/ssa"
-	"fmt"
-	"math"
-)
-
-// sparseDefState contains a Go map from ONAMEs (*Node) to sparse definition trees, and
-// a search helper for the CFG's dominator tree in which those definitions are embedded.
-// Once initialized, given a use of an ONAME within a block, the ssa definition for
-// that ONAME can be discovered in time roughly proportional to the log of the number
-// of SSA definitions of that ONAME (thus avoiding pathological quadratic behavior for
-// very large programs).  The helper contains state (a dominator tree numbering) common
-// to all the sparse definition trees, as well as some necessary data obtained from
-// the ssa package.
-//
-// This algorithm has improved asymptotic complexity, but the constant factor is
-// rather large and thus it is only preferred for very large inputs containing
-// 1000s of blocks and variables.
-type sparseDefState struct {
-	helper         *ssa.SparseTreeHelper // contains one copy of information needed to do sparse mapping
-	defmapForOname map[*Node]*onameDefs  // for each ONAME, its definition set (normal and phi)
-}
-
-// onameDefs contains a record of definitions (ordinary and implied phi function) for a single OName.
-// stm is the set of definitions for the OName.
-// firstdef and lastuse are postorder block numberings that
-// conservatively bracket the entire lifetime of the OName.
-type onameDefs struct {
-	stm *ssa.SparseTreeMap
-	// firstdef and lastuse define an interval in the postorder numbering
-	// that is guaranteed to include the entire lifetime of an ONAME.
-	// In the postorder numbering, math.MaxInt32 is before anything,
-	// and 0 is after-or-equal all exit nodes and infinite loops.
-	firstdef int32 // the first definition of this ONAME *in the postorder numbering*
-	lastuse  int32 // the last use of this ONAME *in the postorder numbering*
-}
-
-// defsFor finds or creates-and-inserts-in-map the definition information
-// (sparse tree and live range) for a given OName.
-func (m *sparseDefState) defsFor(n *Node) *onameDefs {
-	d := m.defmapForOname[n]
-	if d != nil {
-		return d
-	}
-	// Reminder: firstdef/lastuse are postorder indices, not block indices,
-	// so these default values define an empty interval, not the entire one.
-	d = &onameDefs{stm: m.helper.NewTree(), firstdef: 0, lastuse: math.MaxInt32}
-	m.defmapForOname[n] = d
-	return d
-}
-
-// Insert adds a definition at b (with specified before/within/after adjustment)
-// to sparse tree onameDefs.  The lifetime is extended as necessary.
-func (m *sparseDefState) Insert(tree *onameDefs, b *ssa.Block, adjust int32) {
-	bponum := m.helper.Ponums[b.ID]
-	if bponum > tree.firstdef {
-		tree.firstdef = bponum
-	}
-	tree.stm.Insert(b, adjust, b, m.helper)
-}
-
-// Use updates tree to record a use within b, extending the lifetime as necessary.
-func (m *sparseDefState) Use(tree *onameDefs, b *ssa.Block) {
-	bponum := m.helper.Ponums[b.ID]
-	if bponum < tree.lastuse {
-		tree.lastuse = bponum
-	}
-}
-
-// locatePotentialPhiFunctions finds all the places where phi functions
-// will be inserted into a program and records those and ordinary definitions
-// in a "map" (not a Go map) that given an OName and use site, returns the
-// SSA definition for that OName that will reach the use site (that is,
-// the use site's nearest def/phi site in the dominator tree.)
-func (s *state) locatePotentialPhiFunctions(fn *Node) *sparseDefState {
-	// s.config.SparsePhiCutoff() is compared with product of numblocks and numvalues,
-	// if product is smaller than cutoff, use old non-sparse method.
-	// cutoff == 0 implies all sparse
-	// cutoff == uint(-1) implies all non-sparse
-	if uint64(s.f.NumValues())*uint64(s.f.NumBlocks()) < s.config.SparsePhiCutoff() {
-		return nil
-	}
-
-	helper := ssa.NewSparseTreeHelper(s.f)
-	po := helper.Po // index by block.ID to obtain postorder # of block.
-	trees := make(map[*Node]*onameDefs)
-	dm := &sparseDefState{defmapForOname: trees, helper: helper}
-
-	// Process params, taking note of their special lifetimes
-	b := s.f.Entry
-	for _, n := range fn.Func.Dcl {
-		switch n.Class {
-		case PPARAM, PPARAMOUT:
-			t := dm.defsFor(n)
-			dm.Insert(t, b, ssa.AdjustBefore) // define param at entry block
-			if n.Class == PPARAMOUT {
-				dm.Use(t, po[0]) // Explicitly use PPARAMOUT at very last block
-			}
-		default:
-		}
-	}
-
-	// Process memory variable.
-	t := dm.defsFor(&memVar)
-	dm.Insert(t, b, ssa.AdjustBefore) // define memory at entry block
-	dm.Use(t, po[0])                  // Explicitly use memory at last block
-
-	// Next load the map w/ basic definitions for ONames recorded per-block
-	// Iterate over po to avoid unreachable blocks.
-	for i := len(po) - 1; i >= 0; i-- {
-		b := po[i]
-		m := s.defvars[b.ID]
-		for n := range m { // no specified order, but per-node trees are independent.
-			t := dm.defsFor(n)
-			dm.Insert(t, b, ssa.AdjustWithin)
-		}
-	}
-
-	// Find last use of each variable
-	for _, v := range s.fwdRefs {
-		b := v.Block
-		name := v.Aux.(*Node)
-		t := dm.defsFor(name)
-		dm.Use(t, b)
-	}
-
-	for _, t := range trees {
-		// iterating over names in the outer loop
-		for change := true; change; {
-			change = false
-			for i := t.firstdef; i >= t.lastuse; i-- {
-				// Iterating in reverse of post-order reduces number of 'change' iterations;
-				// all possible forward flow goes through each time.
-				b := po[i]
-				// Within tree t, would a use at b require a phi function to ensure a single definition?
-				// TODO: perhaps more efficient to record specific use sites instead of range?
-				if len(b.Preds) < 2 {
-					continue // no phi possible
-				}
-				phi := t.stm.Find(b, ssa.AdjustWithin, helper) // Look for defs in earlier block or AdjustBefore in this one.
-				if phi != nil && phi.(*ssa.Block) == b {
-					continue // has a phi already in this block.
-				}
-				var defseen interface{}
-				// Do preds see different definitions? if so, need a phi function.
-				for _, e := range b.Preds {
-					p := e.Block()
-					dm.Use(t, p)                                // always count phi pred as "use"; no-op except for loop edges, which matter.
-					x := t.stm.Find(p, ssa.AdjustAfter, helper) // Look for defs reaching or within predecessors.
-					if x == nil {                               // nil def from a predecessor means a backedge that will be visited soon.
-						continue
-					}
-					if defseen == nil {
-						defseen = x
-					}
-					if defseen != x {
-						// Need to insert a phi function here because predecessors's definitions differ.
-						change = true
-						// Phi insertion is at AdjustBefore, visible with find in same block at AdjustWithin or AdjustAfter.
-						dm.Insert(t, b, ssa.AdjustBefore)
-						break
-					}
-				}
-			}
-		}
-	}
-	return dm
-}
-
-// FindBetterDefiningBlock tries to find a better block for a definition of OName name
-// reaching (or within) p than p itself.  If it cannot, it returns p instead.
-// This aids in more efficient location of phi functions, since it can skip over
-// branch code that might contain a definition of name if it actually does not.
-func (m *sparseDefState) FindBetterDefiningBlock(name *Node, p *ssa.Block) *ssa.Block {
-	if m == nil {
-		return p
-	}
-	t := m.defmapForOname[name]
-	// For now this is fail-soft, since the old algorithm still works using the unimproved block.
-	if t == nil {
-		return p
-	}
-	x := t.stm.Find(p, ssa.AdjustAfter, m.helper)
-	if x == nil {
-		return p
-	}
-	b := x.(*ssa.Block)
-	if b == nil {
-		return p
-	}
-	return b
-}
-
-func (d *onameDefs) String() string {
-	return fmt.Sprintf("onameDefs:first=%d,last=%d,tree=%s", d.firstdef, d.lastuse, d.stm.String())
-}
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 62ea44f..55ee3c0 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -6,83 +6,29 @@ package gc
 
 import (
 	"bytes"
+	"encoding/binary"
 	"fmt"
 	"html"
 	"os"
-	"strings"
+	"sort"
 
 	"cmd/compile/internal/ssa"
 	"cmd/internal/obj"
 	"cmd/internal/sys"
 )
 
-var ssaEnabled = true
-
 var ssaConfig *ssa.Config
 var ssaExp ssaExport
 
 func initssa() *ssa.Config {
-	ssaExp.unimplemented = false
-	ssaExp.mustImplement = true
 	if ssaConfig == nil {
 		ssaConfig = ssa.NewConfig(Thearch.LinkArch.Name, &ssaExp, Ctxt, Debug['N'] == 0)
-	}
-	return ssaConfig
-}
-
-func shouldssa(fn *Node) bool {
-	switch Thearch.LinkArch.Name {
-	default:
-		// Only available for testing.
-		if os.Getenv("SSATEST") == "" {
-			return false
+		if Thearch.LinkArch.Name == "386" {
+			ssaConfig.Set387(Thearch.Use387)
 		}
-		// Generally available.
-	case "amd64":
-	}
-	if !ssaEnabled {
-		return false
-	}
-
-	// Environment variable control of SSA CG
-	// 1. IF GOSSAFUNC == current function name THEN
-	//       compile this function with SSA and log output to ssa.html
-
-	// 2. IF GOSSAHASH == "" THEN
-	//       compile this function (and everything else) with SSA
-
-	// 3. IF GOSSAHASH == "n" or "N"
-	//       IF GOSSAPKG == current package name THEN
-	//          compile this function (and everything in this package) with SSA
-	//       ELSE
-	//          use the old back end for this function.
-	//       This is for compatibility with existing test harness and should go away.
-
-	// 4. IF GOSSAHASH is a suffix of the binary-rendered SHA1 hash of the function name THEN
-	//          compile this function with SSA
-	//       ELSE
-	//          compile this function with the old back end.
-
-	// Plan is for 3 to be removed when the tests are revised.
-	// SSA is now default, and is disabled by setting
-	// GOSSAHASH to n or N, or selectively with strings of
-	// 0 and 1.
-
-	name := fn.Func.Nname.Sym.Name
-
-	funcname := os.Getenv("GOSSAFUNC")
-	if funcname != "" {
-		// If GOSSAFUNC is set, compile only that function.
-		return name == funcname
 	}
-
-	pkg := os.Getenv("GOSSAPKG")
-	if pkg != "" {
-		// If GOSSAPKG is set, compile only that package.
-		return localpkg.Name == pkg
-	}
-
-	return initssa().DebugHashMatch("GOSSAHASH", name)
+	ssaConfig.HTML = nil
+	return ssaConfig
 }
 
 // buildssa builds an SSA function.
@@ -120,6 +66,7 @@ func buildssa(fn *Node) *ssa.Func {
 	s.f.Name = name
 	s.exitCode = fn.Func.Exit
 	s.panics = map[funcLine]*ssa.Block{}
+	s.config.DebugTest = s.config.DebugHashMatch("GOSSAHASH", name)
 
 	if name == os.Getenv("GOSSAFUNC") {
 		// TODO: tempfile? it is handy to have the location
@@ -127,11 +74,6 @@ func buildssa(fn *Node) *ssa.Func {
 		s.config.HTML = ssa.NewHTMLWriter("ssa.html", s.config, name)
 		// TODO: generate and print a mapping from nodes to values and blocks
 	}
-	defer func() {
-		if !printssa {
-			s.config.HTML.Close()
-		}
-	}()
 
 	// Allocate starting block
 	s.f.Entry = s.f.NewBlock(ssa.BlockPlain)
@@ -139,6 +81,7 @@ func buildssa(fn *Node) *ssa.Func {
 	// Allocate starting values
 	s.labels = map[string]*ssaLabel{}
 	s.labeledNodes = map[*Node]*ssaLabel{}
+	s.fwdVars = map[*Node]*ssa.Value{}
 	s.startmem = s.entryNewValue0(ssa.OpInitMem, ssa.TypeMem)
 	s.sp = s.entryNewValue0(ssa.OpSP, Types[TUINTPTR]) // TODO: use generic pointer type (unsafe.Pointer?) instead
 	s.sb = s.entryNewValue0(ssa.OpSB, Types[TUINTPTR])
@@ -154,17 +97,13 @@ func buildssa(fn *Node) *ssa.Func {
 		switch n.Class {
 		case PPARAM, PPARAMOUT:
 			aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n})
-			s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp)
+			s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, ptrto(n.Type), aux, s.sp)
 			if n.Class == PPARAMOUT && s.canSSA(n) {
 				// Save ssa-able PPARAMOUT variables so we can
 				// store them back to the stack at the end of
 				// the function.
 				s.returns = append(s.returns, n)
 			}
-			if n.Class == PPARAM && s.canSSA(n) && n.Type.IsPtrShaped() {
-				s.ptrargs = append(s.ptrargs, n)
-				n.SetNotLiveAtEnd(true) // SSA takes care of this explicitly
-			}
 		case PAUTO:
 			// processed at each use, to prevent Addr coming
 			// before the decl.
@@ -173,13 +112,28 @@ func buildssa(fn *Node) *ssa.Func {
 		case PFUNC:
 			// local function - already handled by frontend
 		default:
-			s.Unimplementedf("local variable with class %s unimplemented", classnames[n.Class])
+			s.Fatalf("local variable with class %s unimplemented", classnames[n.Class])
 		}
 	}
 
+	// Populate arguments.
+	for _, n := range fn.Func.Dcl {
+		if n.Class != PPARAM {
+			continue
+		}
+		var v *ssa.Value
+		if s.canSSA(n) {
+			v = s.newValue0A(ssa.OpArg, n.Type, n)
+		} else {
+			// Not SSAable. Load it.
+			v = s.newValue2(ssa.OpLoad, n.Type, s.decladdrs[n], s.startmem)
+		}
+		s.vars[n] = v
+	}
+
 	// Convert the AST-based IR to the SSA-based IR
-	s.stmts(fn.Func.Enter)
-	s.stmts(fn.Nbody)
+	s.stmtList(fn.Func.Enter)
+	s.stmtList(fn.Nbody)
 
 	// fallthrough to exit
 	if s.curBlock != nil {
@@ -190,7 +144,7 @@ func buildssa(fn *Node) *ssa.Func {
 
 	// Check that we used all labels
 	for name, lab := range s.labels {
-		if !lab.used() && !lab.reported {
+		if !lab.used() && !lab.reported && !lab.defNode.Used {
 			yyerrorl(lab.defNode.Lineno, "label %v defined and not used", name)
 			lab.reported = true
 		}
@@ -214,16 +168,7 @@ func buildssa(fn *Node) *ssa.Func {
 		return nil
 	}
 
-	prelinkNumvars := s.f.NumValues()
-	sparseDefState := s.locatePotentialPhiFunctions(fn)
-
-	// Link up variable uses to variable definitions
-	s.linkForwardReferences(sparseDefState)
-
-	if ssa.BuildStats > 0 {
-		s.f.LogStat("build", s.f.NumBlocks(), "blocks", prelinkNumvars, "vars_before",
-			s.f.NumValues(), "vars_after", prelinkNumvars*s.f.NumBlocks(), "ssa_phi_loc_cutoff_score")
-	}
+	s.insertPhis()
 
 	// Don't carry reference this around longer than necessary
 	s.exitCode = Nodes{}
@@ -260,8 +205,14 @@ type state struct {
 
 	// variable assignments in the current block (map from variable symbol to ssa value)
 	// *Node is the unique identifier (an ONAME Node) for the variable.
+	// TODO: keep a single varnum map, then make all of these maps slices instead?
 	vars map[*Node]*ssa.Value
 
+	// fwdVars are variables that are used before they are defined in the current block.
+	// This map exists just to coalesce multiple references into a single FwdRef op.
+	// *Node is the unique identifier (an ONAME Node) for the variable.
+	fwdVars map[*Node]*ssa.Value
+
 	// all defined variables at the end of each block. Indexed by block ID.
 	defvars []map[*Node]*ssa.Value
 
@@ -283,15 +234,11 @@ type state struct {
 	// Used to deduplicate panic calls.
 	panics map[funcLine]*ssa.Block
 
-	// list of FwdRef values.
-	fwdRefs []*ssa.Value
-
 	// list of PPARAMOUT (return) variables.
 	returns []*Node
 
-	// list of PPARAM SSA-able pointer-shaped args. We ensure these are live
-	// throughout the function to help users avoid premature finalizers.
-	ptrargs []*Node
+	// A dummy value used during phi construction.
+	placeholder *ssa.Value
 
 	cgoUnsafeArgs bool
 	noWB          bool
@@ -331,12 +278,9 @@ func (s *state) label(sym *Sym) *ssaLabel {
 	return lab
 }
 
-func (s *state) Logf(msg string, args ...interface{})   { s.config.Logf(msg, args...) }
-func (s *state) Log() bool                              { return s.config.Log() }
-func (s *state) Fatalf(msg string, args ...interface{}) { s.config.Fatalf(s.peekLine(), msg, args...) }
-func (s *state) Unimplementedf(msg string, args ...interface{}) {
-	s.config.Unimplementedf(s.peekLine(), msg, args...)
-}
+func (s *state) Logf(msg string, args ...interface{})              { s.config.Logf(msg, args...) }
+func (s *state) Log() bool                                         { return s.config.Log() }
+func (s *state) Fatalf(msg string, args ...interface{})            { s.config.Fatalf(s.peekLine(), msg, args...) }
 func (s *state) Warnl(line int32, msg string, args ...interface{}) { s.config.Warnl(line, msg, args...) }
 func (s *state) Debug_checknil() bool                              { return s.config.Debug_checknil() }
 
@@ -350,9 +294,7 @@ var (
 	newlenVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "newlen"}}
 	capVar    = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "cap"}}
 	typVar    = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "typ"}}
-	idataVar  = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "idata"}}
 	okVar     = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ok"}}
-	deltaVar  = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "delta"}}
 )
 
 // startBlock sets the current block we're generating code in to b.
@@ -362,6 +304,9 @@ func (s *state) startBlock(b *ssa.Block) {
 	}
 	s.curBlock = b
 	s.vars = map[*Node]*ssa.Value{}
+	for n := range s.fwdVars {
+		delete(s.fwdVars, n)
+	}
 }
 
 // endBlock marks the end of generating code for the current block.
@@ -459,6 +404,11 @@ func (s *state) newValue3I(op ssa.Op, t ssa.Type, aux int64, arg0, arg1, arg2 *s
 	return s.curBlock.NewValue3I(s.peekLine(), op, t, aux, arg0, arg1, arg2)
 }
 
+// newValue4 adds a new value with four arguments to the current block.
+func (s *state) newValue4(op ssa.Op, t ssa.Type, arg0, arg1, arg2, arg3 *ssa.Value) *ssa.Value {
+	return s.curBlock.NewValue4(s.peekLine(), op, t, arg0, arg1, arg2, arg3)
+}
+
 // entryNewValue0 adds a new value with no arguments to the entry block.
 func (s *state) entryNewValue0(op ssa.Op, t ssa.Type) *ssa.Value {
 	return s.f.Entry.NewValue0(s.peekLine(), op, t)
@@ -530,20 +480,14 @@ func (s *state) constInt(t ssa.Type, c int64) *ssa.Value {
 	return s.constInt32(t, int32(c))
 }
 
-func (s *state) stmts(a Nodes) {
-	for _, x := range a.Slice() {
-		s.stmt(x)
-	}
-}
-
-// ssaStmtList converts the statement n to SSA and adds it to s.
+// stmtList converts the statement list n to SSA and adds it to s.
 func (s *state) stmtList(l Nodes) {
 	for _, n := range l.Slice() {
 		s.stmt(n)
 	}
 }
 
-// ssaStmt converts the statement n to SSA and adds it to s.
+// stmt converts the statement n to SSA and adds it to s.
 func (s *state) stmt(n *Node) {
 	s.pushLine(n.Lineno)
 	defer s.popLine()
@@ -568,18 +512,26 @@ func (s *state) stmt(n *Node) {
 	case OEMPTY, ODCLCONST, ODCLTYPE, OFALL:
 
 	// Expression statements
-	case OCALLFUNC, OCALLMETH, OCALLINTER:
+	case OCALLFUNC:
+		if isIntrinsicCall(n) {
+			s.intrinsicCall(n)
+			return
+		}
+		fallthrough
+
+	case OCALLMETH, OCALLINTER:
 		s.call(n, callNormal)
-		if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class == PFUNC &&
-			(compiling_runtime && n.Left.Sym.Name == "throw" ||
-				n.Left.Sym.Pkg == Runtimepkg && (n.Left.Sym.Name == "gopanic" || n.Left.Sym.Name == "selectgo" || n.Left.Sym.Name == "block")) {
-			m := s.mem()
-			b := s.endBlock()
-			b.Kind = ssa.BlockExit
-			b.SetControl(m)
-			// TODO: never rewrite OPANIC to OCALLFUNC in the
-			// first place. Need to wait until all backends
-			// go through SSA.
+		if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class == PFUNC {
+			if fn := n.Left.Sym.Name; compiling_runtime && fn == "throw" ||
+				n.Left.Sym.Pkg == Runtimepkg && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "selectgo" || fn == "block") {
+				m := s.mem()
+				b := s.endBlock()
+				b.Kind = ssa.BlockExit
+				b.SetControl(m)
+				// TODO: never rewrite OPANIC to OCALLFUNC in the
+				// first place. Need to wait until all backends
+				// go through SSA.
+			}
 		}
 	case ODEFER:
 		s.call(n.Left, callDefer)
@@ -588,10 +540,41 @@ func (s *state) stmt(n *Node) {
 
 	case OAS2DOTTYPE:
 		res, resok := s.dottype(n.Rlist.First(), true)
-		s.assign(n.List.First(), res, needwritebarrier(n.List.First(), n.Rlist.First()), false, n.Lineno, 0, false)
+		deref := false
+		if !canSSAType(n.Rlist.First().Type) {
+			if res.Op != ssa.OpLoad {
+				s.Fatalf("dottype of non-load")
+			}
+			mem := s.mem()
+			if mem.Op == ssa.OpVarKill {
+				mem = mem.Args[0]
+			}
+			if res.Args[1] != mem {
+				s.Fatalf("memory no longer live from 2-result dottype load")
+			}
+			deref = true
+			res = res.Args[0]
+		}
+		s.assign(n.List.First(), res, needwritebarrier(n.List.First(), n.Rlist.First()), deref, n.Lineno, 0, false)
 		s.assign(n.List.Second(), resok, false, false, n.Lineno, 0, false)
 		return
 
+	case OAS2FUNC:
+		// We come here only when it is an intrinsic call returning two values.
+		if !isIntrinsicCall(n.Rlist.First()) {
+			s.Fatalf("non-intrinsic AS2FUNC not expanded %v", n.Rlist.First())
+		}
+		v := s.intrinsicCall(n.Rlist.First())
+		v1 := s.newValue1(ssa.OpSelect0, n.List.First().Type, v)
+		v2 := s.newValue1(ssa.OpSelect1, n.List.Second().Type, v)
+		// Make a fake node to mimic loading return value, ONLY for write barrier test.
+		// This is future-proofing against non-scalar 2-result intrinsics.
+		// Currently we only have scalar ones, which result in no write barrier.
+		fakeret := &Node{Op: OINDREGSP}
+		s.assign(n.List.First(), v1, needwritebarrier(n.List.First(), fakeret), false, n.Lineno, 0, false)
+		s.assign(n.List.Second(), v2, needwritebarrier(n.List.Second(), fakeret), false, n.Lineno, 0, false)
+		return
+
 	case ODCL:
 		if n.Left.Class == PAUTOHEAP {
 			Fatalf("DCL %v", n)
@@ -687,7 +670,7 @@ func (s *state) stmt(n *Node) {
 		rhs := n.Right
 		if rhs != nil {
 			switch rhs.Op {
-			case OSTRUCTLIT, OARRAYLIT:
+			case OSTRUCTLIT, OARRAYLIT, OSLICELIT:
 				// All literals with nonzero fields have already been
 				// rewritten during walk. Any that remain are just T{}
 				// or equivalents. Use the zero value.
@@ -701,15 +684,24 @@ func (s *state) stmt(n *Node) {
 				// If the slice can be SSA'd, it'll be on the stack,
 				// so there will be no write barriers,
 				// so there's no need to attempt to prevent them.
-				if samesafeexpr(n.Left, rhs.List.First()) && !s.canSSA(n.Left) {
-					s.append(rhs, true)
-					return
+				if samesafeexpr(n.Left, rhs.List.First()) {
+					if !s.canSSA(n.Left) {
+						if Debug_append > 0 {
+							Warnl(n.Lineno, "append: len-only update")
+						}
+						s.append(rhs, true)
+						return
+					} else {
+						if Debug_append > 0 { // replicating old diagnostic message
+							Warnl(n.Lineno, "append: len-only update (in local slice)")
+						}
+					}
 				}
 			}
 		}
 		var r *ssa.Value
 		var isVolatile bool
-		needwb := n.Op == OASWB && rhs != nil
+		needwb := n.Op == OASWB
 		deref := !canSSAType(t)
 		if deref {
 			if rhs == nil {
@@ -724,7 +716,7 @@ func (s *state) stmt(n *Node) {
 				r = s.expr(rhs)
 			}
 		}
-		if rhs != nil && rhs.Op == OAPPEND {
+		if rhs != nil && rhs.Op == OAPPEND && needwritebarrier(n.Left, rhs) {
 			// The frontend gets rid of the write barrier to enable the special OAPPEND
 			// handling above, but since this is not a special case, we need it.
 			// TODO: just add a ptr graying to the end of growslice?
@@ -778,7 +770,7 @@ func (s *state) stmt(n *Node) {
 		}
 
 		s.startBlock(bThen)
-		s.stmts(n.Nbody)
+		s.stmtList(n.Nbody)
 		if b := s.endBlock(); b != nil {
 			b.AddEdgeTo(bEnd)
 		}
@@ -888,7 +880,7 @@ func (s *state) stmt(n *Node) {
 
 		// generate body
 		s.startBlock(bBody)
-		s.stmts(n.Nbody)
+		s.stmtList(n.Nbody)
 
 		// tear down continue/break
 		s.continueTo = prevContinue
@@ -927,7 +919,7 @@ func (s *state) stmt(n *Node) {
 		}
 
 		// generate body code
-		s.stmts(n.Nbody)
+		s.stmtList(n.Nbody)
 
 		s.breakTo = prevBreak
 		if lab != nil {
@@ -955,7 +947,7 @@ func (s *state) stmt(n *Node) {
 	case OVARLIVE:
 		// Insert a varlive op to record that a variable is still live.
 		if !n.Left.Addrtaken {
-			s.Fatalf("VARLIVE variable %s must have Addrtaken set", n.Left)
+			s.Fatalf("VARLIVE variable %v must have Addrtaken set", n.Left)
 		}
 		s.vars[&memVar] = s.newValue1A(ssa.OpVarLive, ssa.TypeMem, n.Left, s.mem())
 
@@ -963,8 +955,11 @@ func (s *state) stmt(n *Node) {
 		p := s.expr(n.Left)
 		s.nilCheck(p)
 
+	case OSQRT:
+		s.expr(n.Left)
+
 	default:
-		s.Unimplementedf("unhandled stmt %s", n.Op)
+		s.Fatalf("unhandled stmt %v", n.Op)
 	}
 }
 
@@ -978,7 +973,7 @@ func (s *state) exit() *ssa.Block {
 
 	// Run exit code. Typically, this code copies heap-allocated PPARAMOUT
 	// variables back to the stack.
-	s.stmts(s.exitCode)
+	s.stmtList(s.exitCode)
 
 	// Store SSAable PPARAMOUT variables back to stack locations.
 	for _, n := range s.returns {
@@ -991,16 +986,6 @@ func (s *state) exit() *ssa.Block {
 		// currently.
 	}
 
-	// Keep input pointer args live until the return. This is a bandaid
-	// fix for 1.7 for what will become in 1.8 explicit runtime.KeepAlive calls.
-	// For <= 1.7 we guarantee that pointer input arguments live to the end of
-	// the function to prevent premature (from the user's point of view)
-	// execution of finalizers. See issue 15277.
-	// TODO: remove for 1.8?
-	for _, n := range s.ptrargs {
-		s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem())
-	}
-
 	// Do actual return.
 	m := s.mem()
 	b := s.endBlock()
@@ -1146,6 +1131,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, TUINTPTR}:   ssa.OpEqPtr,
 	opAndType{OEQ, TUNSAFEPTR}: ssa.OpEqPtr,
@@ -1166,6 +1152,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, TUINTPTR}:   ssa.OpNeqPtr,
 	opAndType{ONE, TUNSAFEPTR}: ssa.OpNeqPtr,
@@ -1251,7 +1238,7 @@ func (s *state) ssaOp(op Op, t *Type) ssa.Op {
 	etype := s.concreteEtype(t)
 	x, ok := opToSSA[opAndType{op, etype}]
 	if !ok {
-		s.Unimplementedf("unhandled binary op %s %s", op, etype)
+		s.Fatalf("unhandled binary op %v %s", op, etype)
 	}
 	return x
 }
@@ -1330,6 +1317,23 @@ var fpConvOpToSSA = map[twoTypes]twoOpsAndType{
 	twoTypes{TFLOAT32, TFLOAT64}: twoOpsAndType{ssa.OpCvt32Fto64F, ssa.OpCopy, TFLOAT64},
 }
 
+// this map is used only for 32-bit arch, and only includes the difference
+// on 32-bit arch, don't use int64<->float conversion for uint32
+var fpConvOpToSSA32 = map[twoTypes]twoOpsAndType{
+	twoTypes{TUINT32, TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32Uto32F, TUINT32},
+	twoTypes{TUINT32, TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt32Uto64F, TUINT32},
+	twoTypes{TFLOAT32, TUINT32}: twoOpsAndType{ssa.OpCvt32Fto32U, ssa.OpCopy, TUINT32},
+	twoTypes{TFLOAT64, TUINT32}: twoOpsAndType{ssa.OpCvt64Fto32U, ssa.OpCopy, TUINT32},
+}
+
+// uint64<->float conversions, only on machines that have intructions for that
+var uint64fpConvOpToSSA = map[twoTypes]twoOpsAndType{
+	twoTypes{TUINT64, TFLOAT32}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64Uto32F, TUINT64},
+	twoTypes{TUINT64, TFLOAT64}: twoOpsAndType{ssa.OpCopy, ssa.OpCvt64Uto64F, TUINT64},
+	twoTypes{TFLOAT32, TUINT64}: twoOpsAndType{ssa.OpCvt32Fto64U, ssa.OpCopy, TUINT64},
+	twoTypes{TFLOAT64, TUINT64}: twoOpsAndType{ssa.OpCvt64Fto64U, ssa.OpCopy, TUINT64},
+}
+
 var shiftOpToSSA = map[opAndTwoTypes]ssa.Op{
 	opAndTwoTypes{OLSH, TINT8, TUINT8}:   ssa.OpLsh8x8,
 	opAndTwoTypes{OLSH, TUINT8, TUINT8}:  ssa.OpLsh8x8,
@@ -1409,7 +1413,7 @@ func (s *state) ssaShiftOp(op Op, t *Type, u *Type) ssa.Op {
 	etype2 := s.concreteEtype(u)
 	x, ok := shiftOpToSSA[opAndTwoTypes{op, etype1, etype2}]
 	if !ok {
-		s.Unimplementedf("unhandled shift op %s etype=%s/%s", op, etype1, etype2)
+		s.Fatalf("unhandled shift op %v etype=%s/%s", op, etype1, etype2)
 	}
 	return x
 }
@@ -1418,7 +1422,7 @@ func (s *state) ssaRotateOp(op Op, t *Type) ssa.Op {
 	etype1 := s.concreteEtype(t)
 	x, ok := opToSSA[opAndType{op, etype1}]
 	if !ok {
-		s.Unimplementedf("unhandled rotate op %s etype=%s", op, etype1)
+		s.Fatalf("unhandled rotate op %v etype=%s", op, etype1)
 	}
 	return x
 }
@@ -1434,6 +1438,16 @@ func (s *state) expr(n *Node) *ssa.Value {
 
 	s.stmtList(n.Ninit)
 	switch n.Op {
+	case OARRAYBYTESTRTMP:
+		slice := s.expr(n.Left)
+		ptr := s.newValue1(ssa.OpSlicePtr, ptrto(Types[TUINT8]), slice)
+		len := s.newValue1(ssa.OpSliceLen, Types[TINT], slice)
+		return s.newValue2(ssa.OpStringMake, n.Type, ptr, len)
+	case OSTRARRAYBYTETMP:
+		str := s.expr(n.Left)
+		ptr := s.newValue1(ssa.OpStringPtr, ptrto(Types[TUINT8]), str)
+		len := s.newValue1(ssa.OpStringLen, Types[TINT], str)
+		return s.newValue3(ssa.OpSliceMake, n.Type, ptr, len, len)
 	case OCFUNC:
 		aux := s.lookupSymbol(n, &ssa.ExternSymbol{Typ: n.Type, Sym: n.Left.Sym})
 		return s.entryNewValue1A(ssa.OpAddr, n.Type, aux, s.sb)
@@ -1442,7 +1456,7 @@ func (s *state) expr(n *Node) *ssa.Value {
 			// "value" of a function is the address of the function's closure
 			sym := funcsym(n.Sym)
 			aux := &ssa.ExternSymbol{Typ: n.Type, Sym: sym}
-			return s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sb)
+			return s.entryNewValue1A(ssa.OpAddr, ptrto(n.Type), aux, s.sb)
 		}
 		if s.canSSA(n) {
 			return s.variable(n, n.Type)
@@ -1516,7 +1530,7 @@ func (s *state) expr(n *Node) *ssa.Value {
 			}
 
 		default:
-			s.Unimplementedf("unhandled OLITERAL %v", n.Val().Ctype())
+			s.Fatalf("unhandled OLITERAL %v", n.Val().Ctype())
 			return nil
 		}
 	case OCONVNOP:
@@ -1602,7 +1616,7 @@ func (s *state) expr(n *Node) *ssa.Value {
 				case 84:
 					op = ssa.OpTrunc64to32
 				default:
-					s.Fatalf("weird integer truncation %s -> %s", ft, tt)
+					s.Fatalf("weird integer truncation %v -> %v", ft, tt)
 				}
 			} else if ft.IsSigned() {
 				// sign extension
@@ -1620,7 +1634,7 @@ func (s *state) expr(n *Node) *ssa.Value {
 				case 48:
 					op = ssa.OpSignExt32to64
 				default:
-					s.Fatalf("bad integer sign extension %s -> %s", ft, tt)
+					s.Fatalf("bad integer sign extension %v -> %v", ft, tt)
 				}
 			} else {
 				// zero extension
@@ -1638,7 +1652,7 @@ func (s *state) expr(n *Node) *ssa.Value {
 				case 48:
 					op = ssa.OpZeroExt32to64
 				default:
-					s.Fatalf("weird integer sign extension %s -> %s", ft, tt)
+					s.Fatalf("weird integer sign extension %v -> %v", ft, tt)
 				}
 			}
 			return s.newValue1(op, n.Type, x)
@@ -1646,8 +1660,39 @@ func (s *state) expr(n *Node) *ssa.Value {
 
 		if ft.IsFloat() || tt.IsFloat() {
 			conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]
+			if s.config.IntSize == 4 && Thearch.LinkArch.Name != "amd64p32" && Thearch.LinkArch.Family != sys.MIPS {
+				if conv1, ok1 := fpConvOpToSSA32[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
+					conv = conv1
+				}
+			}
+			if Thearch.LinkArch.Name == "arm64" {
+				if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
+					conv = conv1
+				}
+			}
+
+			if Thearch.LinkArch.Family == sys.MIPS {
+				if ft.Size() == 4 && ft.IsInteger() && !ft.IsSigned() {
+					// tt is float32 or float64, and ft is also unsigned
+					if tt.Size() == 4 {
+						return s.uint32Tofloat32(n, x, ft, tt)
+					}
+					if tt.Size() == 8 {
+						return s.uint32Tofloat64(n, x, ft, tt)
+					}
+				} else if tt.Size() == 4 && tt.IsInteger() && !tt.IsSigned() {
+					// ft is float32 or float64, and tt is unsigned integer
+					if ft.Size() == 4 {
+						return s.float32ToUint32(n, x, ft, tt)
+					}
+					if ft.Size() == 8 {
+						return s.float64ToUint32(n, x, ft, tt)
+					}
+				}
+			}
+
 			if !ok {
-				s.Fatalf("weird float conversion %s -> %s", ft, tt)
+				s.Fatalf("weird float conversion %v -> %v", ft, tt)
 			}
 			op1, op2, it := conv.op1, conv.op2, conv.intermediateType
 
@@ -1666,23 +1711,23 @@ func (s *state) expr(n *Node) *ssa.Value {
 			}
 			// Tricky 64-bit unsigned cases.
 			if ft.IsInteger() {
-				// therefore tt is float32 or float64, and ft is also unsigned
+				// tt is float32 or float64, and ft is also unsigned
 				if tt.Size() == 4 {
 					return s.uint64Tofloat32(n, x, ft, tt)
 				}
 				if tt.Size() == 8 {
 					return s.uint64Tofloat64(n, x, ft, tt)
 				}
-				s.Fatalf("weird unsigned integer to float conversion %s -> %s", ft, tt)
+				s.Fatalf("weird unsigned integer to float conversion %v -> %v", ft, tt)
 			}
-			// therefore ft is float32 or float64, and tt is unsigned integer
+			// ft is float32 or float64, and tt is unsigned integer
 			if ft.Size() == 4 {
 				return s.float32ToUint64(n, x, ft, tt)
 			}
 			if ft.Size() == 8 {
 				return s.float64ToUint64(n, x, ft, tt)
 			}
-			s.Fatalf("weird float to unsigned integer conversion %s -> %s", ft, tt)
+			s.Fatalf("weird float to unsigned integer conversion %v -> %v", ft, tt)
 			return nil
 		}
 
@@ -1695,7 +1740,7 @@ func (s *state) expr(n *Node) *ssa.Value {
 			} else if ft.Size() == 16 && tt.Size() == 8 {
 				op = ssa.OpCvt64Fto32F
 			} else {
-				s.Fatalf("weird complex conversion %s -> %s", ft, tt)
+				s.Fatalf("weird complex conversion %v -> %v", ft, tt)
 			}
 			ftp := floatForComplex(ft)
 			ttp := floatForComplex(tt)
@@ -1704,7 +1749,7 @@ func (s *state) expr(n *Node) *ssa.Value {
 				s.newValue1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, x)))
 		}
 
-		s.Unimplementedf("unhandled OCONV %s -> %s", n.Left.Type.Etype, n.Type.Etype)
+		s.Fatalf("unhandled OCONV %s -> %s", n.Left.Type.Etype, n.Type.Etype)
 		return nil
 
 	case ODOTTYPE:
@@ -1720,14 +1765,14 @@ func (s *state) expr(n *Node) *ssa.Value {
 			op := s.ssaOp(OEQ, pt)
 			r := s.newValue2(op, Types[TBOOL], s.newValue1(ssa.OpComplexReal, pt, a), s.newValue1(ssa.OpComplexReal, pt, b))
 			i := s.newValue2(op, Types[TBOOL], s.newValue1(ssa.OpComplexImag, pt, a), s.newValue1(ssa.OpComplexImag, pt, b))
-			c := s.newValue2(ssa.OpAnd8, Types[TBOOL], r, i)
+			c := s.newValue2(ssa.OpAndB, Types[TBOOL], r, i)
 			switch n.Op {
 			case OEQ:
 				return c
 			case ONE:
 				return s.newValue1(ssa.OpNot, Types[TBOOL], c)
 			default:
-				s.Fatalf("ordered complex compare %s", n.Op)
+				s.Fatalf("ordered complex compare %v", n.Op)
 			}
 		}
 		return s.newValue2(s.ssaOp(n.Op, n.Left.Type), Types[TBOOL], a, b)
@@ -1810,19 +1855,12 @@ func (s *state) expr(n *Node) *ssa.Value {
 		}
 		if n.Type.IsFloat() {
 			return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
-		} else {
-			// do a size-appropriate check for zero
-			cmp := s.newValue2(s.ssaOp(ONE, n.Type), Types[TBOOL], b, s.zeroVal(n.Type))
-			s.check(cmp, panicdivide)
-			return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
 		}
+		return s.intDivide(n, a, b)
 	case OMOD:
 		a := s.expr(n.Left)
 		b := s.expr(n.Right)
-		// do a size-appropriate check for zero
-		cmp := s.newValue2(s.ssaOp(ONE, n.Type), Types[TBOOL], b, s.zeroVal(n.Type))
-		s.check(cmp, panicdivide)
-		return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
+		return s.intDivide(n, a, b)
 	case OADD, OSUB:
 		a := s.expr(n.Left)
 		b := s.expr(n.Right)
@@ -1923,12 +1961,8 @@ func (s *state) expr(n *Node) *ssa.Value {
 		// Note we know the volatile result is false because you can't write &f() in Go.
 		return a
 
-	case OINDREG:
-		if int(n.Reg) != Thearch.REGSP {
-			s.Unimplementedf("OINDREG of non-SP register %s in expr: %v", obj.Rconv(int(n.Reg)), n)
-			return nil
-		}
-		addr := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(n.Type), n.Xoffset, s.sp)
+	case OINDREGSP:
+		addr := s.entryNewValue1I(ssa.OpOffPtr, ptrto(n.Type), n.Xoffset, s.sp)
 		return s.newValue2(ssa.OpLoad, n.Type, addr, s.mem())
 
 	case OIND:
@@ -1952,14 +1986,20 @@ func (s *state) expr(n *Node) *ssa.Value {
 	case OINDEX:
 		switch {
 		case n.Left.Type.IsString():
+			if n.Bounded && Isconst(n.Left, CTSTR) && Isconst(n.Right, CTINT) {
+				// Replace "abc"[1] with 'b'.
+				// Delayed until now because "abc"[1] is not an ideal constant.
+				// See test/fixedbugs/issue11370.go.
+				return s.newValue0I(ssa.OpConst8, Types[TUINT8], int64(int8(n.Left.Val().U.(string)[n.Right.Int64()])))
+			}
 			a := s.expr(n.Left)
 			i := s.expr(n.Right)
-			i = s.extendIndex(i)
+			i = s.extendIndex(i, panicindex)
 			if !n.Bounded {
 				len := s.newValue1(ssa.OpStringLen, Types[TINT], a)
 				s.boundsCheck(i, len)
 			}
-			ptrtyp := Ptrto(Types[TUINT8])
+			ptrtyp := ptrto(Types[TUINT8])
 			ptr := s.newValue1(ssa.OpStringPtr, ptrtyp, a)
 			if Isconst(n.Right, CTINT) {
 				ptr = s.newValue1I(ssa.OpOffPtr, ptrtyp, n.Right.Int64(), ptr)
@@ -1971,7 +2011,22 @@ func (s *state) expr(n *Node) *ssa.Value {
 			p, _ := s.addr(n, false)
 			return s.newValue2(ssa.OpLoad, n.Left.Type.Elem(), p, s.mem())
 		case n.Left.Type.IsArray():
-			// TODO: fix when we can SSA arrays of length 1.
+			if bound := n.Left.Type.NumElem(); bound <= 1 {
+				// SSA can handle arrays of length at most 1.
+				a := s.expr(n.Left)
+				i := s.expr(n.Right)
+				if bound == 0 {
+					// Bounds check will never succeed.  Might as well
+					// use constants for the bounds check.
+					z := s.constInt(Types[TINT], 0)
+					s.boundsCheck(z, z)
+					// The return value won't be live, return junk.
+					return s.newValue0(ssa.OpUnknown, n.Type)
+				}
+				i = s.extendIndex(i, panicindex)
+				s.boundsCheck(i, s.constInt(Types[TINT], bound))
+				return s.newValue1I(ssa.OpArraySelect, n.Type, 0, a)
+			}
 			p, _ := s.addr(n, false)
 			return s.newValue2(ssa.OpLoad, n.Left.Type.Elem(), p, s.mem())
 		default:
@@ -2007,35 +2062,13 @@ func (s *state) expr(n *Node) *ssa.Value {
 		a := s.expr(n.Left)
 		return s.newValue1(ssa.OpITab, n.Type, a)
 
+	case OIDATA:
+		a := s.expr(n.Left)
+		return s.newValue1(ssa.OpIData, n.Type, a)
+
 	case OEFACE:
 		tab := s.expr(n.Left)
 		data := s.expr(n.Right)
-		// The frontend allows putting things like struct{*byte} in
-		// the data portion of an eface. But we don't want struct{*byte}
-		// as a register type because (among other reasons) the liveness
-		// analysis is confused by the "fat" variables that result from
-		// such types being spilled.
-		// So here we ensure that we are selecting the underlying pointer
-		// when we build an eface.
-		// TODO: get rid of this now that structs can be SSA'd?
-		for !data.Type.IsPtrShaped() {
-			switch {
-			case data.Type.IsArray():
-				data = s.newValue1I(ssa.OpArrayIndex, data.Type.ElemType(), 0, data)
-			case data.Type.IsStruct():
-				for i := data.Type.NumFields() - 1; i >= 0; i-- {
-					f := data.Type.FieldType(i)
-					if f.Size() == 0 {
-						// eface type could also be struct{p *byte; q [0]int}
-						continue
-					}
-					data = s.newValue1I(ssa.OpStructSelect, f, int64(i), data)
-					break
-				}
-			default:
-				s.Fatalf("type being put into an eface isn't a pointer")
-			}
-		}
 		return s.newValue2(ssa.OpIMake, n.Type, tab, data)
 
 	case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR:
@@ -2043,13 +2076,13 @@ func (s *state) expr(n *Node) *ssa.Value {
 		var i, j, k *ssa.Value
 		low, high, max := n.SliceBounds()
 		if low != nil {
-			i = s.extendIndex(s.expr(low))
+			i = s.extendIndex(s.expr(low), panicslice)
 		}
 		if high != nil {
-			j = s.extendIndex(s.expr(high))
+			j = s.extendIndex(s.expr(high), panicslice)
 		}
 		if max != nil {
-			k = s.extendIndex(s.expr(max))
+			k = s.extendIndex(s.expr(max), panicslice)
 		}
 		p, l, c := s.slice(n.Left.Type, v, i, j, k)
 		return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
@@ -2059,17 +2092,17 @@ func (s *state) expr(n *Node) *ssa.Value {
 		var i, j *ssa.Value
 		low, high, _ := n.SliceBounds()
 		if low != nil {
-			i = s.extendIndex(s.expr(low))
+			i = s.extendIndex(s.expr(low), panicslice)
 		}
 		if high != nil {
-			j = s.extendIndex(s.expr(high))
+			j = s.extendIndex(s.expr(high), panicslice)
 		}
 		p, l, _ := s.slice(n.Left.Type, v, i, j, nil)
 		return s.newValue2(ssa.OpStringMake, n.Type, p, l)
 
 	case OCALLFUNC:
-		if isIntrinsicCall1(n) {
-			return s.intrinsicCall1(n)
+		if isIntrinsicCall(n) {
+			return s.intrinsicCall(n)
 		}
 		fallthrough
 
@@ -2084,7 +2117,7 @@ func (s *state) expr(n *Node) *ssa.Value {
 		return s.append(n, false)
 
 	default:
-		s.Unimplementedf("unhandled expr %s", n.Op)
+		s.Fatalf("unhandled expr %v", n.Op)
 		return nil
 	}
 }
@@ -2130,7 +2163,7 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
 	// *(ptr+len+2) = e3
 
 	et := n.Type.Elem()
-	pt := Ptrto(et)
+	pt := ptrto(et)
 
 	// Evaluate slice
 	sn := n.List.First() // the slice node is the first in the list
@@ -2182,9 +2215,13 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
 			// Tell liveness we're about to build a new slice
 			s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, sn, s.mem())
 		}
-		capaddr := s.newValue1I(ssa.OpOffPtr, pt, int64(Array_cap), addr)
+		capaddr := s.newValue1I(ssa.OpOffPtr, pt, int64(array_cap), addr)
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, capaddr, r[2], s.mem())
-		s.insertWBstore(pt, addr, r[0], n.Lineno, 0)
+		if ssa.IsStackAddr(addr) {
+			s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, pt.Size(), addr, r[0], s.mem())
+		} else {
+			s.insertWBstore(pt, addr, r[0], n.Lineno, 0)
+		}
 		// load the value we just stored to avoid having to spill it
 		s.vars[&ptrVar] = s.newValue2(ssa.OpLoad, pt, addr, s.mem())
 		s.vars[&lenVar] = r[1] // avoid a spill in the fast path
@@ -2203,7 +2240,7 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
 	if inplace {
 		l = s.variable(&lenVar, Types[TINT]) // generates phi for len
 		nl = s.newValue2(s.ssaOp(OADD, Types[TINT]), Types[TINT], l, s.constInt(Types[TINT], nargs))
-		lenaddr := s.newValue1I(ssa.OpOffPtr, pt, int64(Array_nel), addr)
+		lenaddr := s.newValue1I(ssa.OpOffPtr, pt, int64(array_nel), addr)
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, lenaddr, nl, s.mem())
 	}
 
@@ -2246,7 +2283,7 @@ func (s *state) append(n *Node, inplace bool) *ssa.Value {
 			if haspointers(et) {
 				s.insertWBmove(et, addr, arg.v, n.Lineno, arg.isVolatile)
 			} else {
-				s.vars[&memVar] = s.newValue3I(ssa.OpMove, ssa.TypeMem, et.Size(), addr, arg.v, s.mem())
+				s.vars[&memVar] = s.newValue3I(ssa.OpMove, ssa.TypeMem, sizeAlignAuxInt(et), addr, arg.v, s.mem())
 			}
 		}
 	}
@@ -2329,7 +2366,7 @@ func (s *state) assign(left *Node, right *ssa.Value, wb, deref bool, line int32,
 	dowidth(t)
 	if s.canSSA(left) {
 		if deref {
-			s.Fatalf("can SSA LHS %s but not RHS %s", left, right)
+			s.Fatalf("can SSA LHS %v but not RHS %s", left, right)
 		}
 		if left.Op == ODOT {
 			// We're assigning to a field of an ssa-able value.
@@ -2366,6 +2403,30 @@ func (s *state) assign(left *Node, right *ssa.Value, wb, deref bool, line int32,
 			// TODO: do we need to update named values here?
 			return
 		}
+		if left.Op == OINDEX && left.Left.Type.IsArray() {
+			// We're assigning to an element of an ssa-able array.
+			// a[i] = v
+			t := left.Left.Type
+			n := t.NumElem()
+
+			i := s.expr(left.Right) // index
+			if n == 0 {
+				// The bounds check must fail.  Might as well
+				// ignore the actual index and just use zeros.
+				z := s.constInt(Types[TINT], 0)
+				s.boundsCheck(z, z)
+				return
+			}
+			if n != 1 {
+				s.Fatalf("assigning to non-1-length array")
+			}
+			// Rewrite to a = [1]{v}
+			i = s.extendIndex(i, panicindex)
+			s.boundsCheck(i, s.constInt(Types[TINT], 1))
+			v := s.newValue1(ssa.OpArrayMake1, t, right)
+			s.assign(left.Left, v, false, false, line, 0, rightIsVolatile)
+			return
+		}
 		// Update variable assignment.
 		s.vars[left] = right
 		s.addNamedValue(left, right)
@@ -2378,19 +2439,19 @@ func (s *state) assign(left *Node, right *ssa.Value, wb, deref bool, line int32,
 	}
 	if deref {
 		// Treat as a mem->mem move.
-		if right == nil {
-			s.vars[&memVar] = s.newValue2I(ssa.OpZero, ssa.TypeMem, t.Size(), addr, s.mem())
+		if wb && !ssa.IsStackAddr(addr) {
+			s.insertWBmove(t, addr, right, line, rightIsVolatile)
 			return
 		}
-		if wb {
-			s.insertWBmove(t, addr, right, line, rightIsVolatile)
+		if right == nil {
+			s.vars[&memVar] = s.newValue2I(ssa.OpZero, ssa.TypeMem, sizeAlignAuxInt(t), addr, s.mem())
 			return
 		}
-		s.vars[&memVar] = s.newValue3I(ssa.OpMove, ssa.TypeMem, t.Size(), addr, right, s.mem())
+		s.vars[&memVar] = s.newValue3I(ssa.OpMove, ssa.TypeMem, sizeAlignAuxInt(t), addr, right, s.mem())
 		return
 	}
 	// Treat as a store.
-	if wb {
+	if wb && !ssa.IsStackAddr(addr) {
 		if skip&skipPtr != 0 {
 			// Special case: if we don't write back the pointers, don't bother
 			// doing the write barrier check.
@@ -2424,7 +2485,7 @@ func (s *state) zeroVal(t *Type) *ssa.Value {
 		case 8:
 			return s.constInt64(t, 0)
 		default:
-			s.Fatalf("bad sized integer type %s", t)
+			s.Fatalf("bad sized integer type %v", t)
 		}
 	case t.IsFloat():
 		switch t.Size() {
@@ -2433,7 +2494,7 @@ func (s *state) zeroVal(t *Type) *ssa.Value {
 		case 8:
 			return s.constFloat64(t, 0)
 		default:
-			s.Fatalf("bad sized float type %s", t)
+			s.Fatalf("bad sized float type %v", t)
 		}
 	case t.IsComplex():
 		switch t.Size() {
@@ -2444,7 +2505,7 @@ func (s *state) zeroVal(t *Type) *ssa.Value {
 			z := s.constFloat64(Types[TFLOAT64], 0)
 			return s.entryNewValue2(ssa.OpComplexMake, t, z, z)
 		default:
-			s.Fatalf("bad sized complex type %s", t)
+			s.Fatalf("bad sized complex type %v", t)
 		}
 
 	case t.IsString():
@@ -2464,8 +2525,15 @@ func (s *state) zeroVal(t *Type) *ssa.Value {
 			v.AddArg(s.zeroVal(t.FieldType(i).(*Type)))
 		}
 		return v
+	case t.IsArray():
+		switch t.NumElem() {
+		case 0:
+			return s.entryNewValue0(ssa.OpArrayMake0, t)
+		case 1:
+			return s.entryNewValue1(ssa.OpArrayMake1, t, s.zeroVal(t.Elem()))
+		}
 	}
-	s.Unimplementedf("zero for type %v not implemented", t)
+	s.Fatalf("zero for type %v not implemented", t)
 	return nil
 }
 
@@ -2477,73 +2545,371 @@ const (
 	callGo
 )
 
-// isSSAIntrinsic1 returns true if n is a call to a recognized 1-arg intrinsic
-// that can be handled by the SSA backend.
-// SSA uses this, but so does the front end to see if should not
-// inline a function because it is a candidate for intrinsic
-// substitution.
-func isSSAIntrinsic1(s *Sym) bool {
-	// The test below is not quite accurate -- in the event that
-	// a function is disabled on a per-function basis, for example
-	// because of hash-keyed binary failure search, SSA might be
-	// disabled for that function but it would not be noted here,
-	// and thus an inlining would not occur (in practice, inlining
-	// so far has only been noticed for Bswap32 and the 16-bit count
-	// leading/trailing instructions, but heuristics might change
-	// in the future or on different architectures).
-	if !ssaEnabled || ssa.IntrinsicsDisable || Thearch.LinkArch.Family != sys.AMD64 {
-		return false
+// TODO: make this a field of a configuration object instead of a global.
+var intrinsics *intrinsicInfo
+
+type intrinsicInfo struct {
+	std      map[intrinsicKey]intrinsicBuilder
+	intSized map[sizedIntrinsicKey]intrinsicBuilder
+	ptrSized map[sizedIntrinsicKey]intrinsicBuilder
+}
+
+// An intrinsicBuilder converts a call node n into an ssa value that
+// implements that call as an intrinsic. args is a list of arguments to the func.
+type intrinsicBuilder func(s *state, n *Node, args []*ssa.Value) *ssa.Value
+
+type intrinsicKey struct {
+	pkg string
+	fn  string
+}
+
+type sizedIntrinsicKey struct {
+	pkg  string
+	fn   string
+	size int
+}
+
+// disableForInstrumenting returns nil when instrumenting, fn otherwise
+func disableForInstrumenting(fn intrinsicBuilder) intrinsicBuilder {
+	if instrumenting {
+		return nil
 	}
-	if s != nil && s.Pkg != nil && s.Pkg.Path == "runtime/internal/sys" {
-		switch s.Name {
-		case
-			"Ctz64", "Ctz32", "Ctz16",
-			"Bswap64", "Bswap32":
-			return true
-		}
+	return fn
+}
+
+// enableOnArch returns fn on given archs, nil otherwise
+func enableOnArch(fn intrinsicBuilder, archs ...sys.ArchFamily) intrinsicBuilder {
+	if Thearch.LinkArch.InFamily(archs...) {
+		return fn
+	}
+	return nil
+}
+
+func intrinsicInit() {
+	i := &intrinsicInfo{}
+	intrinsics = i
+
+	// initial set of intrinsics.
+	i.std = map[intrinsicKey]intrinsicBuilder{
+		/******** runtime ********/
+		intrinsicKey{"runtime", "slicebytetostringtmp"}: disableForInstrumenting(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			// Compiler frontend optimizations emit OARRAYBYTESTRTMP nodes
+			// for the backend instead of slicebytetostringtmp calls
+			// when not instrumenting.
+			slice := args[0]
+			ptr := s.newValue1(ssa.OpSlicePtr, ptrto(Types[TUINT8]), slice)
+			len := s.newValue1(ssa.OpSliceLen, Types[TINT], slice)
+			return s.newValue2(ssa.OpStringMake, n.Type, ptr, len)
+		}),
+		intrinsicKey{"runtime", "KeepAlive"}: func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			data := s.newValue1(ssa.OpIData, ptrto(Types[TUINT8]), args[0])
+			s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, data, s.mem())
+			return nil
+		},
+
+		/******** runtime/internal/sys ********/
+		intrinsicKey{"runtime/internal/sys", "Ctz32"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			return s.newValue1(ssa.OpCtz32, Types[TUINT32], args[0])
+		}, sys.AMD64, sys.ARM64, sys.ARM, sys.S390X, sys.MIPS),
+		intrinsicKey{"runtime/internal/sys", "Ctz64"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			return s.newValue1(ssa.OpCtz64, Types[TUINT64], args[0])
+		}, sys.AMD64, sys.ARM64, sys.ARM, sys.S390X, sys.MIPS),
+		intrinsicKey{"runtime/internal/sys", "Bswap32"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			return s.newValue1(ssa.OpBswap32, Types[TUINT32], args[0])
+		}, sys.AMD64, sys.ARM64, sys.ARM, sys.S390X),
+		intrinsicKey{"runtime/internal/sys", "Bswap64"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			return s.newValue1(ssa.OpBswap64, Types[TUINT64], args[0])
+		}, sys.AMD64, sys.ARM64, sys.ARM, sys.S390X),
+
+		/******** runtime/internal/atomic ********/
+		intrinsicKey{"runtime/internal/atomic", "Load"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			v := s.newValue2(ssa.OpAtomicLoad32, ssa.MakeTuple(Types[TUINT32], ssa.TypeMem), args[0], s.mem())
+			s.vars[&memVar] = s.newValue1(ssa.OpSelect1, ssa.TypeMem, v)
+			return s.newValue1(ssa.OpSelect0, Types[TUINT32], v)
+		}, sys.AMD64, sys.ARM64, sys.S390X, sys.MIPS),
+		intrinsicKey{"runtime/internal/atomic", "Load64"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			v := s.newValue2(ssa.OpAtomicLoad64, ssa.MakeTuple(Types[TUINT64], ssa.TypeMem), args[0], s.mem())
+			s.vars[&memVar] = s.newValue1(ssa.OpSelect1, ssa.TypeMem, v)
+			return s.newValue1(ssa.OpSelect0, Types[TUINT64], v)
+		}, sys.AMD64, sys.ARM64, sys.S390X),
+		intrinsicKey{"runtime/internal/atomic", "Loadp"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			v := s.newValue2(ssa.OpAtomicLoadPtr, ssa.MakeTuple(ptrto(Types[TUINT8]), ssa.TypeMem), args[0], s.mem())
+			s.vars[&memVar] = s.newValue1(ssa.OpSelect1, ssa.TypeMem, v)
+			return s.newValue1(ssa.OpSelect0, ptrto(Types[TUINT8]), v)
+		}, sys.AMD64, sys.ARM64, sys.S390X, sys.MIPS),
+
+		intrinsicKey{"runtime/internal/atomic", "Store"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			s.vars[&memVar] = s.newValue3(ssa.OpAtomicStore32, ssa.TypeMem, args[0], args[1], s.mem())
+			return nil
+		}, sys.AMD64, sys.ARM64, sys.S390X, sys.MIPS),
+		intrinsicKey{"runtime/internal/atomic", "Store64"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			s.vars[&memVar] = s.newValue3(ssa.OpAtomicStore64, ssa.TypeMem, args[0], args[1], s.mem())
+			return nil
+		}, sys.AMD64, sys.ARM64, sys.S390X),
+		intrinsicKey{"runtime/internal/atomic", "StorepNoWB"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			s.vars[&memVar] = s.newValue3(ssa.OpAtomicStorePtrNoWB, ssa.TypeMem, args[0], args[1], s.mem())
+			return nil
+		}, sys.AMD64, sys.ARM64, sys.S390X, sys.MIPS),
+
+		intrinsicKey{"runtime/internal/atomic", "Xchg"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			v := s.newValue3(ssa.OpAtomicExchange32, ssa.MakeTuple(Types[TUINT32], ssa.TypeMem), args[0], args[1], s.mem())
+			s.vars[&memVar] = s.newValue1(ssa.OpSelect1, ssa.TypeMem, v)
+			return s.newValue1(ssa.OpSelect0, Types[TUINT32], v)
+		}, sys.AMD64, sys.ARM64, sys.S390X, sys.MIPS),
+		intrinsicKey{"runtime/internal/atomic", "Xchg64"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			v := s.newValue3(ssa.OpAtomicExchange64, ssa.MakeTuple(Types[TUINT64], ssa.TypeMem), args[0], args[1], s.mem())
+			s.vars[&memVar] = s.newValue1(ssa.OpSelect1, ssa.TypeMem, v)
+			return s.newValue1(ssa.OpSelect0, Types[TUINT64], v)
+		}, sys.AMD64, sys.ARM64, sys.S390X),
+
+		intrinsicKey{"runtime/internal/atomic", "Xadd"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			v := s.newValue3(ssa.OpAtomicAdd32, ssa.MakeTuple(Types[TUINT32], ssa.TypeMem), args[0], args[1], s.mem())
+			s.vars[&memVar] = s.newValue1(ssa.OpSelect1, ssa.TypeMem, v)
+			return s.newValue1(ssa.OpSelect0, Types[TUINT32], v)
+		}, sys.AMD64, sys.ARM64, sys.S390X, sys.MIPS),
+		intrinsicKey{"runtime/internal/atomic", "Xadd64"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			v := s.newValue3(ssa.OpAtomicAdd64, ssa.MakeTuple(Types[TUINT64], ssa.TypeMem), args[0], args[1], s.mem())
+			s.vars[&memVar] = s.newValue1(ssa.OpSelect1, ssa.TypeMem, v)
+			return s.newValue1(ssa.OpSelect0, Types[TUINT64], v)
+		}, sys.AMD64, sys.ARM64, sys.S390X),
+
+		intrinsicKey{"runtime/internal/atomic", "Cas"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			v := s.newValue4(ssa.OpAtomicCompareAndSwap32, ssa.MakeTuple(Types[TBOOL], ssa.TypeMem), args[0], args[1], args[2], s.mem())
+			s.vars[&memVar] = s.newValue1(ssa.OpSelect1, ssa.TypeMem, v)
+			return s.newValue1(ssa.OpSelect0, Types[TBOOL], v)
+		}, sys.AMD64, sys.ARM64, sys.S390X, sys.MIPS),
+		intrinsicKey{"runtime/internal/atomic", "Cas64"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			v := s.newValue4(ssa.OpAtomicCompareAndSwap64, ssa.MakeTuple(Types[TBOOL], ssa.TypeMem), args[0], args[1], args[2], s.mem())
+			s.vars[&memVar] = s.newValue1(ssa.OpSelect1, ssa.TypeMem, v)
+			return s.newValue1(ssa.OpSelect0, Types[TBOOL], v)
+		}, sys.AMD64, sys.ARM64, sys.S390X),
+
+		intrinsicKey{"runtime/internal/atomic", "And8"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			s.vars[&memVar] = s.newValue3(ssa.OpAtomicAnd8, ssa.TypeMem, args[0], args[1], s.mem())
+			return nil
+		}, sys.AMD64, sys.ARM64, sys.MIPS),
+		intrinsicKey{"runtime/internal/atomic", "Or8"}: enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			s.vars[&memVar] = s.newValue3(ssa.OpAtomicOr8, ssa.TypeMem, args[0], args[1], s.mem())
+			return nil
+		}, sys.AMD64, sys.ARM64, sys.MIPS),
+	}
+
+	// aliases internal to runtime/internal/atomic
+	i.std[intrinsicKey{"runtime/internal/atomic", "Loadint64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Load64"}]
+	i.std[intrinsicKey{"runtime/internal/atomic", "Xaddint64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xadd64"}]
+
+	// intrinsics which vary depending on the size of int/ptr.
+	i.intSized = map[sizedIntrinsicKey]intrinsicBuilder{
+		sizedIntrinsicKey{"runtime/internal/atomic", "Loaduint", 4}: i.std[intrinsicKey{"runtime/internal/atomic", "Load"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Loaduint", 8}: i.std[intrinsicKey{"runtime/internal/atomic", "Load64"}],
+	}
+	i.ptrSized = map[sizedIntrinsicKey]intrinsicBuilder{
+		sizedIntrinsicKey{"runtime/internal/atomic", "Loaduintptr", 4}:  i.std[intrinsicKey{"runtime/internal/atomic", "Load"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Loaduintptr", 8}:  i.std[intrinsicKey{"runtime/internal/atomic", "Load64"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Storeuintptr", 4}: i.std[intrinsicKey{"runtime/internal/atomic", "Store"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Storeuintptr", 8}: i.std[intrinsicKey{"runtime/internal/atomic", "Store64"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Xchguintptr", 4}:  i.std[intrinsicKey{"runtime/internal/atomic", "Xchg"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Xchguintptr", 8}:  i.std[intrinsicKey{"runtime/internal/atomic", "Xchg64"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Xadduintptr", 4}:  i.std[intrinsicKey{"runtime/internal/atomic", "Xadd"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Xadduintptr", 8}:  i.std[intrinsicKey{"runtime/internal/atomic", "Xadd64"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Casuintptr", 4}:   i.std[intrinsicKey{"runtime/internal/atomic", "Cas"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Casuintptr", 8}:   i.std[intrinsicKey{"runtime/internal/atomic", "Cas64"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Casp1", 4}:        i.std[intrinsicKey{"runtime/internal/atomic", "Cas"}],
+		sizedIntrinsicKey{"runtime/internal/atomic", "Casp1", 8}:        i.std[intrinsicKey{"runtime/internal/atomic", "Cas64"}],
+	}
+
+	/******** sync/atomic ********/
+	if flag_race {
+		// The race detector needs to be able to intercept these calls.
+		// We can't intrinsify them.
+		return
+	}
+	// these are all aliases to runtime/internal/atomic implementations.
+	i.std[intrinsicKey{"sync/atomic", "LoadInt32"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Load"}]
+	i.std[intrinsicKey{"sync/atomic", "LoadInt64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Load64"}]
+	i.std[intrinsicKey{"sync/atomic", "LoadPointer"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Loadp"}]
+	i.std[intrinsicKey{"sync/atomic", "LoadUint32"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Load"}]
+	i.std[intrinsicKey{"sync/atomic", "LoadUint64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Load64"}]
+	i.ptrSized[sizedIntrinsicKey{"sync/atomic", "LoadUintptr", 4}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Load"}]
+	i.ptrSized[sizedIntrinsicKey{"sync/atomic", "LoadUintptr", 8}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Load64"}]
+
+	i.std[intrinsicKey{"sync/atomic", "StoreInt32"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Store"}]
+	i.std[intrinsicKey{"sync/atomic", "StoreInt64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Store64"}]
+	// Note: not StorePointer, that needs a write barrier.  Same below for {CompareAnd}Swap.
+	i.std[intrinsicKey{"sync/atomic", "StoreUint32"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Store"}]
+	i.std[intrinsicKey{"sync/atomic", "StoreUint64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Store64"}]
+	i.ptrSized[sizedIntrinsicKey{"sync/atomic", "StoreUintptr", 4}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Store"}]
+	i.ptrSized[sizedIntrinsicKey{"sync/atomic", "StoreUintptr", 8}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Store64"}]
+
+	i.std[intrinsicKey{"sync/atomic", "SwapInt32"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xchg"}]
+	i.std[intrinsicKey{"sync/atomic", "SwapInt64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xchg64"}]
+	i.std[intrinsicKey{"sync/atomic", "SwapUint32"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xchg"}]
+	i.std[intrinsicKey{"sync/atomic", "SwapUint64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xchg64"}]
+	i.ptrSized[sizedIntrinsicKey{"sync/atomic", "SwapUintptr", 4}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xchg"}]
+	i.ptrSized[sizedIntrinsicKey{"sync/atomic", "SwapUintptr", 8}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xchg64"}]
+
+	i.std[intrinsicKey{"sync/atomic", "CompareAndSwapInt32"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Cas"}]
+	i.std[intrinsicKey{"sync/atomic", "CompareAndSwapInt64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Cas64"}]
+	i.std[intrinsicKey{"sync/atomic", "CompareAndSwapUint32"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Cas"}]
+	i.std[intrinsicKey{"sync/atomic", "CompareAndSwapUint64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Cas64"}]
+	i.ptrSized[sizedIntrinsicKey{"sync/atomic", "CompareAndSwapUintptr", 4}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Cas"}]
+	i.ptrSized[sizedIntrinsicKey{"sync/atomic", "CompareAndSwapUintptr", 8}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Cas64"}]
+
+	i.std[intrinsicKey{"sync/atomic", "AddInt32"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xadd"}]
+	i.std[intrinsicKey{"sync/atomic", "AddInt64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xadd64"}]
+	i.std[intrinsicKey{"sync/atomic", "AddUint32"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xadd"}]
+	i.std[intrinsicKey{"sync/atomic", "AddUint64"}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xadd64"}]
+	i.ptrSized[sizedIntrinsicKey{"sync/atomic", "AddUintptr", 4}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xadd"}]
+	i.ptrSized[sizedIntrinsicKey{"sync/atomic", "AddUintptr", 8}] =
+		i.std[intrinsicKey{"runtime/internal/atomic", "Xadd64"}]
+
+	/******** math/big ********/
+	i.intSized[sizedIntrinsicKey{"math/big", "mulWW", 8}] =
+		enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			return s.newValue2(ssa.OpMul64uhilo, ssa.MakeTuple(Types[TUINT64], Types[TUINT64]), args[0], args[1])
+		}, sys.AMD64)
+	i.intSized[sizedIntrinsicKey{"math/big", "divWW", 8}] =
+		enableOnArch(func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
+			return s.newValue3(ssa.OpDiv128u, ssa.MakeTuple(Types[TUINT64], Types[TUINT64]), args[0], args[1], args[2])
+		}, sys.AMD64)
+}
+
+// findIntrinsic returns a function which builds the SSA equivalent of the
+// function identified by the symbol sym.  If sym is not an intrinsic call, returns nil.
+func findIntrinsic(sym *Sym) intrinsicBuilder {
+	if ssa.IntrinsicsDisable {
+		return nil
+	}
+	if sym == nil || sym.Pkg == nil {
+		return nil
 	}
-	return false
+	if intrinsics == nil {
+		intrinsicInit()
+	}
+	pkg := sym.Pkg.Path
+	if sym.Pkg == localpkg {
+		pkg = myimportpath
+	}
+	fn := sym.Name
+	f := intrinsics.std[intrinsicKey{pkg, fn}]
+	if f != nil {
+		return f
+	}
+	f = intrinsics.intSized[sizedIntrinsicKey{pkg, fn, Widthint}]
+	if f != nil {
+		return f
+	}
+	return intrinsics.ptrSized[sizedIntrinsicKey{pkg, fn, Widthptr}]
 }
 
-func isIntrinsicCall1(n *Node) bool {
+func isIntrinsicCall(n *Node) bool {
 	if n == nil || n.Left == nil {
 		return false
 	}
-	return isSSAIntrinsic1(n.Left.Sym)
+	return findIntrinsic(n.Left.Sym) != nil
 }
 
-// intrinsicFirstArg extracts arg from n.List and eval
-func (s *state) intrinsicFirstArg(n *Node) *ssa.Value {
-	x := n.List.First()
-	if x.Op == OAS {
-		x = x.Right
+// intrinsicCall converts a call to a recognized intrinsic function into the intrinsic SSA operation.
+func (s *state) intrinsicCall(n *Node) *ssa.Value {
+	v := findIntrinsic(n.Left.Sym)(s, n, s.intrinsicArgs(n))
+	if ssa.IntrinsicsDebug > 0 {
+		x := v
+		if x == nil {
+			x = s.mem()
+		}
+		if x.Op == ssa.OpSelect0 || x.Op == ssa.OpSelect1 {
+			x = x.Args[0]
+		}
+		Warnl(n.Lineno, "intrinsic substitution for %v with %s", n.Left.Sym.Name, x.LongString())
 	}
-	return s.expr(x)
+	return v
 }
 
-// intrinsicCall1 converts a call to a recognized 1-arg intrinsic
-// into the intrinsic
-func (s *state) intrinsicCall1(n *Node) *ssa.Value {
-	var result *ssa.Value
-	switch n.Left.Sym.Name {
-	case "Ctz64":
-		result = s.newValue1(ssa.OpCtz64, Types[TUINT64], s.intrinsicFirstArg(n))
-	case "Ctz32":
-		result = s.newValue1(ssa.OpCtz32, Types[TUINT32], s.intrinsicFirstArg(n))
-	case "Ctz16":
-		result = s.newValue1(ssa.OpCtz16, Types[TUINT16], s.intrinsicFirstArg(n))
-	case "Bswap64":
-		result = s.newValue1(ssa.OpBswap64, Types[TUINT64], s.intrinsicFirstArg(n))
-	case "Bswap32":
-		result = s.newValue1(ssa.OpBswap32, Types[TUINT32], s.intrinsicFirstArg(n))
-	}
-	if result == nil {
-		Fatalf("Unknown special call: %v", n.Left.Sym)
+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
+	temps := map[*Node]*ssa.Value{}
+	for _, a := range n.List.Slice() {
+		if a.Op != OAS {
+			s.Fatalf("non-assignment as a function argument %s", opnames[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: %s", opnames[l.Op])
+		}
 	}
-	if ssa.IntrinsicsDebug > 0 {
-		Warnl(n.Lineno, "intrinsic substitution for %v with %s", n.Left.Sym.Name, result.LongString())
+	sort.Sort(byOffset(args))
+	res := make([]*ssa.Value, len(args))
+	for i, a := range args {
+		res[i] = a.v
 	}
-	return result
+	return res
 }
 
 // Calls the function n using the specified call type.
@@ -2569,9 +2935,15 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
 			sym = fn.Sym
 			break
 		}
+		// Make a name n2 for the function.
+		// fn.Sym might be sync.(*Mutex).Unlock.
+		// Make a PFUNC node out of that, then evaluate it.
+		// We get back an SSA value representing &sync.(*Mutex).Unlock·f.
+		// We can then pass that to defer or go.
 		n2 := newname(fn.Sym)
 		n2.Class = PFUNC
 		n2.Lineno = fn.Lineno
+		n2.Type = 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
 		// want to set it here.
@@ -2585,7 +2957,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
 			s.nilCheck(itab)
 		}
 		itabidx := fn.Xoffset + 3*int64(Widthptr) + 8 // offset of fun field in runtime.itab
-		itab = s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], itabidx, itab)
+		itab = s.newValue1I(ssa.OpOffPtr, ptrto(Types[TUINTPTR]), itabidx, itab)
 		if k == callNormal {
 			codeptr = s.newValue2(ssa.OpLoad, Types[TUINTPTR], itab, s.mem())
 		} else {
@@ -2608,22 +2980,23 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
 		if k != callNormal {
 			argStart += int64(2 * Widthptr)
 		}
-		addr := s.entryNewValue1I(ssa.OpOffPtr, Types[TUINTPTR], argStart, s.sp)
+		addr := s.entryNewValue1I(ssa.OpOffPtr, ptrto(Types[TUINTPTR]), argStart, s.sp)
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), addr, rcvr, s.mem())
 	}
 
 	// Defer/go args
 	if k != callNormal {
 		// Write argsize and closure (args to Newproc/Deferproc).
+		argStart := Ctxt.FixedFrameSize()
 		argsize := s.constInt32(Types[TUINT32], int32(stksize))
-		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, 4, s.sp, argsize, s.mem())
-		addr := s.entryNewValue1I(ssa.OpOffPtr, Ptrto(Types[TUINTPTR]), int64(Widthptr), s.sp)
+		addr := s.entryNewValue1I(ssa.OpOffPtr, ptrto(Types[TUINT32]), argStart, s.sp)
+		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, 4, addr, argsize, s.mem())
+		addr = s.entryNewValue1I(ssa.OpOffPtr, ptrto(Types[TUINTPTR]), argStart+int64(Widthptr), s.sp)
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), addr, closure, s.mem())
 		stksize += 2 * int64(Widthptr)
 	}
 
 	// call target
-	bNext := s.f.NewBlock(ssa.BlockPlain)
 	var call *ssa.Value
 	switch {
 	case k == callDefer:
@@ -2638,39 +3011,34 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
 	case sym != nil:
 		call = s.newValue1A(ssa.OpStaticCall, ssa.TypeMem, sym, s.mem())
 	default:
-		Fatalf("bad call type %s %v", n.Op, n)
+		Fatalf("bad call type %v %v", n.Op, n)
 	}
 	call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
-
-	// Finish call block
 	s.vars[&memVar] = call
-	b := s.endBlock()
-	b.Kind = ssa.BlockCall
-	b.SetControl(call)
-	b.AddEdgeTo(bNext)
+
+	// Finish block for defers
 	if k == callDefer {
-		// Add recover edge to exit code.
+		b := s.endBlock()
 		b.Kind = ssa.BlockDefer
+		b.SetControl(call)
+		bNext := s.f.NewBlock(ssa.BlockPlain)
+		b.AddEdgeTo(bNext)
+		// Add recover edge to exit code.
 		r := s.f.NewBlock(ssa.BlockPlain)
 		s.startBlock(r)
 		s.exit()
 		b.AddEdgeTo(r)
 		b.Likely = ssa.BranchLikely
+		s.startBlock(bNext)
 	}
 
-	// Start exit block, find address of result.
-	s.startBlock(bNext)
-	// Keep input pointer args live across calls.  This is a bandaid until 1.8.
-	for _, n := range s.ptrargs {
-		s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem())
-	}
 	res := n.Left.Type.Results()
 	if res.NumFields() == 0 || k != callNormal {
 		// call has no return value. Continue with the next statement.
 		return nil
 	}
 	fp := res.Field(0)
-	return s.entryNewValue1I(ssa.OpOffPtr, Ptrto(fp.Type), fp.Offset+Ctxt.FixedFrameSize(), s.sp)
+	return s.entryNewValue1I(ssa.OpOffPtr, ptrto(fp.Type), fp.Offset+Ctxt.FixedFrameSize(), s.sp)
 }
 
 // etypesign returns the signed-ness of e, for integer/pointer etypes.
@@ -2711,7 +3079,7 @@ func (s *state) lookupSymbol(n *Node, sym interface{}) interface{} {
 // If bounded is true then this address does not require a nil check for its operand
 // even if that would otherwise be implied.
 func (s *state) addr(n *Node, bounded bool) (*ssa.Value, bool) {
-	t := Ptrto(n.Type)
+	t := ptrto(n.Type)
 	switch n.Op {
 	case ONAME:
 		switch n.Class {
@@ -2730,9 +3098,8 @@ func (s *state) addr(n *Node, bounded bool) (*ssa.Value, bool) {
 			if v != nil {
 				return v, false
 			}
-			if n.String() == ".fp" {
-				// Special arg that points to the frame pointer.
-				// (Used by the race detector, others?)
+			if n == nodfp {
+				// Special arg that points to the frame pointer (Used by ORECOVER).
 				aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n})
 				return s.entryNewValue1A(ssa.OpAddr, t, aux, s.sp), false
 			}
@@ -2747,22 +3114,18 @@ func (s *state) addr(n *Node, bounded bool) (*ssa.Value, bool) {
 			aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n})
 			return s.newValue1A(ssa.OpAddr, t, aux, s.sp), false
 		default:
-			s.Unimplementedf("variable address class %v not implemented", classnames[n.Class])
+			s.Fatalf("variable address class %v not implemented", classnames[n.Class])
 			return nil, false
 		}
-	case OINDREG:
-		// indirect off a register
+	case OINDREGSP:
+		// indirect off REGSP
 		// used for storing/loading arguments/returns to/from callees
-		if int(n.Reg) != Thearch.REGSP {
-			s.Unimplementedf("OINDREG of non-SP register %s in addr: %v", obj.Rconv(int(n.Reg)), n)
-			return nil, false
-		}
 		return s.entryNewValue1I(ssa.OpOffPtr, t, n.Xoffset, s.sp), true
 	case OINDEX:
 		if n.Left.Type.IsSlice() {
 			a := s.expr(n.Left)
 			i := s.expr(n.Right)
-			i = s.extendIndex(i)
+			i = s.extendIndex(i, panicindex)
 			len := s.newValue1(ssa.OpSliceLen, Types[TINT], a)
 			if !n.Bounded {
 				s.boundsCheck(i, len)
@@ -2772,12 +3135,12 @@ func (s *state) addr(n *Node, bounded bool) (*ssa.Value, bool) {
 		} else { // array
 			a, isVolatile := s.addr(n.Left, bounded)
 			i := s.expr(n.Right)
-			i = s.extendIndex(i)
+			i = s.extendIndex(i, panicindex)
 			len := s.constInt(Types[TINT], n.Left.Type.NumElem())
 			if !n.Bounded {
 				s.boundsCheck(i, len)
 			}
-			return s.newValue2(ssa.OpPtrIndex, Ptrto(n.Left.Type.Elem()), a, i), isVolatile
+			return s.newValue2(ssa.OpPtrIndex, ptrto(n.Left.Type.Elem()), a, i), isVolatile
 		}
 	case OIND:
 		return s.exprPtr(n.Left, bounded, n.Lineno), false
@@ -2789,15 +3152,23 @@ func (s *state) addr(n *Node, bounded bool) (*ssa.Value, bool) {
 		return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset, p), false
 	case OCLOSUREVAR:
 		return s.newValue1I(ssa.OpOffPtr, t, n.Xoffset,
-			s.entryNewValue0(ssa.OpGetClosurePtr, Ptrto(Types[TUINT8]))), false
+			s.entryNewValue0(ssa.OpGetClosurePtr, ptrto(Types[TUINT8]))), false
 	case OCONVNOP:
 		addr, isVolatile := s.addr(n.Left, bounded)
 		return s.newValue1(ssa.OpCopy, t, addr), isVolatile // ensure that addr has the right type
 	case OCALLFUNC, OCALLINTER, OCALLMETH:
 		return s.call(n, callNormal), true
-
+	case ODOTTYPE:
+		v, _ := s.dottype(n, false)
+		if v.Op != ssa.OpLoad {
+			s.Fatalf("dottype of non-load")
+		}
+		if v.Args[1] != s.mem() {
+			s.Fatalf("memory no longer live from dottype load")
+		}
+		return v.Args[0], false
 	default:
-		s.Unimplementedf("unhandled addr %v", n.Op)
+		s.Fatalf("unhandled addr %v", n.Op)
 		return nil, false
 	}
 }
@@ -2808,7 +3179,7 @@ func (s *state) canSSA(n *Node) bool {
 	if Debug['N'] != 0 {
 		return false
 	}
-	for n.Op == ODOT {
+	for n.Op == ODOT || (n.Op == OINDEX && n.Left.Type.IsArray()) {
 		n = n.Left
 	}
 	if n.Op != ONAME {
@@ -2860,11 +3231,15 @@ func canSSAType(t *Type) bool {
 	}
 	switch t.Etype {
 	case TARRAY:
-		// We can't do arrays because dynamic indexing is
+		// We can't do larger arrays because dynamic indexing is
 		// not supported on SSA variables.
-		// TODO: maybe allow if length is <=1?  All indexes
-		// are constant?  Might be good for the arrays
-		// introduced by the compiler for variadic functions.
+		// TODO: allow if all indexes are constant.
+		if t.NumElem() == 0 {
+			return true
+		}
+		if t.NumElem() == 1 {
+			return canSSAType(t.Elem())
+		}
 		return false
 	case TSTRUCT:
 		if t.NumFields() > ssa.MaxStruct {
@@ -2895,51 +3270,42 @@ func (s *state) exprPtr(n *Node, bounded bool, lineno int32) *ssa.Value {
 }
 
 // nilCheck generates nil pointer checking code.
-// Starts a new block on return, unless nil checks are disabled.
 // Used only for automatically inserted nil checks,
 // not for user code like 'x != nil'.
 func (s *state) nilCheck(ptr *ssa.Value) {
-	if Disable_checknil != 0 {
+	if disable_checknil != 0 {
 		return
 	}
-	chk := s.newValue2(ssa.OpNilCheck, ssa.TypeVoid, ptr, s.mem())
-	b := s.endBlock()
-	b.Kind = ssa.BlockCheck
-	b.SetControl(chk)
-	bNext := s.f.NewBlock(ssa.BlockPlain)
-	b.AddEdgeTo(bNext)
-	s.startBlock(bNext)
+	s.newValue2(ssa.OpNilCheck, ssa.TypeVoid, ptr, s.mem())
 }
 
 // boundsCheck generates bounds checking code. Checks if 0 <= idx < len, branches to exit if not.
 // Starts a new block on return.
+// idx is already converted to full int width.
 func (s *state) boundsCheck(idx, len *ssa.Value) {
 	if Debug['B'] != 0 {
 		return
 	}
-	// TODO: convert index to full width?
-	// TODO: if index is 64-bit and we're compiling to 32-bit, check that high 32 bits are zero.
 
 	// bounds check
 	cmp := s.newValue2(ssa.OpIsInBounds, Types[TBOOL], idx, len)
-	s.check(cmp, Panicindex)
+	s.check(cmp, panicindex)
 }
 
 // sliceBoundsCheck generates slice bounds checking code. Checks if 0 <= idx <= len, branches to exit if not.
 // Starts a new block on return.
+// idx and len are already converted to full int width.
 func (s *state) sliceBoundsCheck(idx, len *ssa.Value) {
 	if Debug['B'] != 0 {
 		return
 	}
-	// TODO: convert index to full width?
-	// TODO: if index is 64-bit and we're compiling to 32-bit, check that high 32 bits are zero.
 
 	// bounds check
 	cmp := s.newValue2(ssa.OpIsSliceInBounds, Types[TBOOL], idx, len)
 	s.check(cmp, panicslice)
 }
 
-// If cmp (a bool) is true, panic using the given function.
+// If cmp (a bool) is false, panic using the given function.
 func (s *state) check(cmp *ssa.Value, fn *Node) {
 	b := s.endBlock()
 	b.Kind = ssa.BlockIf
@@ -2961,53 +3327,61 @@ func (s *state) check(cmp *ssa.Value, fn *Node) {
 	s.startBlock(bNext)
 }
 
+func (s *state) intDivide(n *Node, a, b *ssa.Value) *ssa.Value {
+	needcheck := true
+	switch b.Op {
+	case ssa.OpConst8, ssa.OpConst16, ssa.OpConst32, ssa.OpConst64:
+		if b.AuxInt != 0 {
+			needcheck = false
+		}
+	}
+	if needcheck {
+		// do a size-appropriate check for zero
+		cmp := s.newValue2(s.ssaOp(ONE, n.Type), Types[TBOOL], b, s.zeroVal(n.Type))
+		s.check(cmp, panicdivide)
+	}
+	return s.newValue2(s.ssaOp(n.Op, n.Type), a.Type, a, b)
+}
+
 // rtcall issues a call to the given runtime function fn with the listed args.
 // Returns a slice of results of the given result types.
 // The call is added to the end of the current block.
 // If returns is false, the block is marked as an exit block.
-// If returns is true, the block is marked as a call block. A new block
-// is started to load the return values.
 func (s *state) rtcall(fn *Node, returns bool, results []*Type, args ...*ssa.Value) []*ssa.Value {
 	// Write args to the stack
-	var off int64 // TODO: arch-dependent starting offset?
+	off := Ctxt.FixedFrameSize()
 	for _, arg := range args {
 		t := arg.Type
 		off = Rnd(off, t.Alignment())
 		ptr := s.sp
 		if off != 0 {
-			ptr = s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], off, s.sp)
+			ptr = s.newValue1I(ssa.OpOffPtr, t.PtrTo(), off, s.sp)
 		}
 		size := t.Size()
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, size, ptr, arg, s.mem())
 		off += size
 	}
 	off = Rnd(off, int64(Widthptr))
+	if Thearch.LinkArch.Name == "amd64p32" {
+		// amd64p32 wants 8-byte alignment of the start of the return values.
+		off = Rnd(off, 8)
+	}
 
 	// Issue call
 	call := s.newValue1A(ssa.OpStaticCall, ssa.TypeMem, fn.Sym, s.mem())
 	s.vars[&memVar] = call
 
-	// Finish block
-	b := s.endBlock()
 	if !returns {
+		// Finish block
+		b := s.endBlock()
 		b.Kind = ssa.BlockExit
 		b.SetControl(call)
-		call.AuxInt = off
+		call.AuxInt = off - Ctxt.FixedFrameSize()
 		if len(results) > 0 {
 			Fatalf("panic call can't have results")
 		}
 		return nil
 	}
-	b.Kind = ssa.BlockCall
-	b.SetControl(call)
-	bNext := s.f.NewBlock(ssa.BlockPlain)
-	b.AddEdgeTo(bNext)
-	s.startBlock(bNext)
-
-	// Keep input pointer args live across calls.  This is a bandaid until 1.8.
-	for _, n := range s.ptrargs {
-		s.vars[&memVar] = s.newValue2(ssa.OpKeepAlive, ssa.TypeMem, s.variable(n, n.Type), s.mem())
-	}
 
 	// Load results
 	res := make([]*ssa.Value, len(results))
@@ -3015,7 +3389,7 @@ func (s *state) rtcall(fn *Node, returns bool, results []*Type, args ...*ssa.Val
 		off = Rnd(off, t.Alignment())
 		ptr := s.sp
 		if off != 0 {
-			ptr = s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], off, s.sp)
+			ptr = s.newValue1I(ssa.OpOffPtr, ptrto(t), off, s.sp)
 		}
 		res[i] = s.newValue2(ssa.OpLoad, t, ptr, s.mem())
 		off += t.Size()
@@ -3030,67 +3404,51 @@ func (s *state) rtcall(fn *Node, returns bool, results []*Type, args ...*ssa.Val
 
 // insertWBmove inserts the assignment *left = *right including a write barrier.
 // t is the type being assigned.
+// If right == nil, then we're zeroing *left.
 func (s *state) insertWBmove(t *Type, left, right *ssa.Value, line int32, rightIsVolatile bool) {
 	// if writeBarrier.enabled {
 	//   typedmemmove(&t, left, right)
 	// } else {
 	//   *left = *right
 	// }
+	//
+	// or
+	//
+	// if writeBarrier.enabled {
+	//   typedmemclr(&t, left)
+	// } else {
+	//   *left = zeroValue
+	// }
 
 	if s.noWB {
-		s.Fatalf("write barrier prohibited")
+		s.Error("write barrier prohibited")
 	}
 	if s.WBLineno == 0 {
 		s.WBLineno = left.Line
 	}
-	bThen := s.f.NewBlock(ssa.BlockPlain)
-	bElse := s.f.NewBlock(ssa.BlockPlain)
-	bEnd := s.f.NewBlock(ssa.BlockPlain)
 
-	aux := &ssa.ExternSymbol{Typ: Types[TBOOL], Sym: syslook("writeBarrier").Sym}
-	flagaddr := s.newValue1A(ssa.OpAddr, Ptrto(Types[TUINT32]), aux, s.sb)
-	// TODO: select the .enabled field. It is currently first, so not needed for now.
-	// Load word, test byte, avoiding partial register write from load byte.
-	flag := s.newValue2(ssa.OpLoad, Types[TUINT32], flagaddr, s.mem())
-	flag = s.newValue1(ssa.OpTrunc64to8, Types[TBOOL], flag)
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.Likely = ssa.BranchUnlikely
-	b.SetControl(flag)
-	b.AddEdgeTo(bThen)
-	b.AddEdgeTo(bElse)
-
-	s.startBlock(bThen)
-
-	if !rightIsVolatile {
-		// Issue typedmemmove call.
-		taddr := s.newValue1A(ssa.OpAddr, Types[TUINTPTR], &ssa.ExternSymbol{Typ: Types[TUINTPTR], Sym: typenamesym(t)}, s.sb)
-		s.rtcall(typedmemmove, true, nil, taddr, left, right)
+	var val *ssa.Value
+	if right == nil {
+		val = s.newValue2I(ssa.OpZeroWB, ssa.TypeMem, sizeAlignAuxInt(t), left, s.mem())
 	} else {
-		// Copy to temp location if the source is volatile (will be clobbered by
-		// a function call).  Marshaling the args to typedmemmove might clobber the
-		// value we're trying to move.
-		tmp := temp(t)
-		s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, tmp, s.mem())
-		tmpaddr, _ := s.addr(tmp, true)
-		s.vars[&memVar] = s.newValue3I(ssa.OpMove, ssa.TypeMem, t.Size(), tmpaddr, right, s.mem())
-		// Issue typedmemmove call.
-		taddr := s.newValue1A(ssa.OpAddr, Types[TUINTPTR], &ssa.ExternSymbol{Typ: Types[TUINTPTR], Sym: typenamesym(t)}, s.sb)
-		s.rtcall(typedmemmove, true, nil, taddr, left, tmpaddr)
-		// Mark temp as dead.
-		s.vars[&memVar] = s.newValue1A(ssa.OpVarKill, ssa.TypeMem, tmp, s.mem())
+		var op ssa.Op
+		if rightIsVolatile {
+			op = ssa.OpMoveWBVolatile
+		} else {
+			op = ssa.OpMoveWB
+		}
+		val = s.newValue3I(op, ssa.TypeMem, sizeAlignAuxInt(t), left, right, s.mem())
 	}
-	s.endBlock().AddEdgeTo(bEnd)
-
-	s.startBlock(bElse)
-	s.vars[&memVar] = s.newValue3I(ssa.OpMove, ssa.TypeMem, t.Size(), left, right, s.mem())
-	s.endBlock().AddEdgeTo(bEnd)
+	val.Aux = &ssa.ExternSymbol{Typ: Types[TUINTPTR], Sym: typenamesym(t)}
+	s.vars[&memVar] = val
 
-	s.startBlock(bEnd)
-
-	if Debug_wb > 0 {
-		Warnl(line, "write barrier")
-	}
+	// WB ops will be expanded to branches at writebarrier phase.
+	// To make it easy, we put WB ops at the end of a block, so
+	// that it does not need to split a block into two parts when
+	// expanding WB ops.
+	b := s.f.NewBlock(ssa.BlockPlain)
+	s.endBlock().AddEdgeTo(b)
+	s.startBlock(b)
 }
 
 // insertWBstore inserts the assignment *left = right including a write barrier.
@@ -3104,45 +3462,21 @@ func (s *state) insertWBstore(t *Type, left, right *ssa.Value, line int32, skip
 	// }
 
 	if s.noWB {
-		s.Fatalf("write barrier prohibited")
+		s.Error("write barrier prohibited")
 	}
 	if s.WBLineno == 0 {
 		s.WBLineno = left.Line
 	}
 	s.storeTypeScalars(t, left, right, skip)
-
-	bThen := s.f.NewBlock(ssa.BlockPlain)
-	bElse := s.f.NewBlock(ssa.BlockPlain)
-	bEnd := s.f.NewBlock(ssa.BlockPlain)
-
-	aux := &ssa.ExternSymbol{Typ: Types[TBOOL], Sym: syslook("writeBarrier").Sym}
-	flagaddr := s.newValue1A(ssa.OpAddr, Ptrto(Types[TUINT32]), aux, s.sb)
-	// TODO: select the .enabled field. It is currently first, so not needed for now.
-	// Load word, test byte, avoiding partial register write from load byte.
-	flag := s.newValue2(ssa.OpLoad, Types[TUINT32], flagaddr, s.mem())
-	flag = s.newValue1(ssa.OpTrunc64to8, Types[TBOOL], flag)
-	b := s.endBlock()
-	b.Kind = ssa.BlockIf
-	b.Likely = ssa.BranchUnlikely
-	b.SetControl(flag)
-	b.AddEdgeTo(bThen)
-	b.AddEdgeTo(bElse)
-
-	// Issue write barriers for pointer writes.
-	s.startBlock(bThen)
 	s.storeTypePtrsWB(t, left, right)
-	s.endBlock().AddEdgeTo(bEnd)
-
-	// Issue regular stores for pointer writes.
-	s.startBlock(bElse)
-	s.storeTypePtrs(t, left, right)
-	s.endBlock().AddEdgeTo(bEnd)
 
-	s.startBlock(bEnd)
-
-	if Debug_wb > 0 {
-		Warnl(line, "write barrier")
-	}
+	// WB ops will be expanded to branches at writebarrier phase.
+	// To make it easy, we put WB ops at the end of a block, so
+	// that it does not need to split a block into two parts when
+	// expanding WB ops.
+	b := s.f.NewBlock(ssa.BlockPlain)
+	s.endBlock().AddEdgeTo(b)
+	s.startBlock(b)
 }
 
 // do *left = right for all scalar (non-pointer) parts of t.
@@ -3157,22 +3491,22 @@ func (s *state) storeTypeScalars(t *Type, left, right *ssa.Value, skip skipMask)
 			return
 		}
 		len := s.newValue1(ssa.OpStringLen, Types[TINT], right)
-		lenAddr := s.newValue1I(ssa.OpOffPtr, Ptrto(Types[TINT]), s.config.IntSize, left)
+		lenAddr := s.newValue1I(ssa.OpOffPtr, ptrto(Types[TINT]), s.config.IntSize, left)
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, lenAddr, len, s.mem())
 	case t.IsSlice():
 		if skip&skipLen == 0 {
 			len := s.newValue1(ssa.OpSliceLen, Types[TINT], right)
-			lenAddr := s.newValue1I(ssa.OpOffPtr, Ptrto(Types[TINT]), s.config.IntSize, left)
+			lenAddr := s.newValue1I(ssa.OpOffPtr, ptrto(Types[TINT]), s.config.IntSize, left)
 			s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, lenAddr, len, s.mem())
 		}
 		if skip&skipCap == 0 {
 			cap := s.newValue1(ssa.OpSliceCap, Types[TINT], right)
-			capAddr := s.newValue1I(ssa.OpOffPtr, Ptrto(Types[TINT]), 2*s.config.IntSize, left)
+			capAddr := s.newValue1I(ssa.OpOffPtr, ptrto(Types[TINT]), 2*s.config.IntSize, left)
 			s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, capAddr, cap, s.mem())
 		}
 	case t.IsInterface():
 		// itab field doesn't need a write barrier (even though it is a pointer).
-		itab := s.newValue1(ssa.OpITab, Ptrto(Types[TUINT8]), right)
+		itab := s.newValue1(ssa.OpITab, ptrto(Types[TUINT8]), right)
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.IntSize, left, itab, s.mem())
 	case t.IsStruct():
 		n := t.NumFields()
@@ -3182,8 +3516,12 @@ func (s *state) storeTypeScalars(t *Type, left, right *ssa.Value, skip skipMask)
 			val := s.newValue1I(ssa.OpStructSelect, ft, int64(i), right)
 			s.storeTypeScalars(ft.(*Type), addr, val, 0)
 		}
+	case t.IsArray() && t.NumElem() == 0:
+		// nothing
+	case t.IsArray() && t.NumElem() == 1:
+		s.storeTypeScalars(t.Elem(), left, s.newValue1I(ssa.OpArraySelect, t.Elem(), 0, right), 0)
 	default:
-		s.Fatalf("bad write barrier type %s", t)
+		s.Fatalf("bad write barrier type %v", t)
 	}
 }
 
@@ -3193,15 +3531,15 @@ func (s *state) storeTypePtrs(t *Type, left, right *ssa.Value) {
 	case t.IsPtrShaped():
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, left, right, s.mem())
 	case t.IsString():
-		ptr := s.newValue1(ssa.OpStringPtr, Ptrto(Types[TUINT8]), right)
+		ptr := s.newValue1(ssa.OpStringPtr, ptrto(Types[TUINT8]), right)
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, left, ptr, s.mem())
 	case t.IsSlice():
-		ptr := s.newValue1(ssa.OpSlicePtr, Ptrto(Types[TUINT8]), right)
+		ptr := s.newValue1(ssa.OpSlicePtr, ptrto(Types[TUINT8]), right)
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, left, ptr, s.mem())
 	case t.IsInterface():
 		// itab field is treated as a scalar.
-		idata := s.newValue1(ssa.OpIData, Ptrto(Types[TUINT8]), right)
-		idataAddr := s.newValue1I(ssa.OpOffPtr, Ptrto(Types[TUINT8]), s.config.PtrSize, left)
+		idata := s.newValue1(ssa.OpIData, ptrto(Types[TUINT8]), right)
+		idataAddr := s.newValue1I(ssa.OpOffPtr, ptrto(Types[TUINT8]), s.config.PtrSize, left)
 		s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, s.config.PtrSize, idataAddr, idata, s.mem())
 	case t.IsStruct():
 		n := t.NumFields()
@@ -3214,26 +3552,31 @@ func (s *state) storeTypePtrs(t *Type, left, right *ssa.Value) {
 			val := s.newValue1I(ssa.OpStructSelect, ft, int64(i), right)
 			s.storeTypePtrs(ft.(*Type), addr, val)
 		}
+	case t.IsArray() && t.NumElem() == 0:
+		// nothing
+	case t.IsArray() && t.NumElem() == 1:
+		s.storeTypePtrs(t.Elem(), left, s.newValue1I(ssa.OpArraySelect, t.Elem(), 0, right))
 	default:
-		s.Fatalf("bad write barrier type %s", t)
+		s.Fatalf("bad write barrier type %v", t)
 	}
 }
 
-// do *left = right with a write barrier for all pointer parts of t.
+// do *left = right for all pointer parts of t, with write barriers if necessary.
 func (s *state) storeTypePtrsWB(t *Type, left, right *ssa.Value) {
 	switch {
 	case t.IsPtrShaped():
-		s.rtcall(writebarrierptr, true, nil, left, right)
+		s.vars[&memVar] = s.newValue3I(ssa.OpStoreWB, ssa.TypeMem, s.config.PtrSize, left, right, s.mem())
 	case t.IsString():
-		ptr := s.newValue1(ssa.OpStringPtr, Ptrto(Types[TUINT8]), right)
-		s.rtcall(writebarrierptr, true, nil, left, ptr)
+		ptr := s.newValue1(ssa.OpStringPtr, ptrto(Types[TUINT8]), right)
+		s.vars[&memVar] = s.newValue3I(ssa.OpStoreWB, ssa.TypeMem, s.config.PtrSize, left, ptr, s.mem())
 	case t.IsSlice():
-		ptr := s.newValue1(ssa.OpSlicePtr, Ptrto(Types[TUINT8]), right)
-		s.rtcall(writebarrierptr, true, nil, left, ptr)
+		ptr := s.newValue1(ssa.OpSlicePtr, ptrto(Types[TUINT8]), right)
+		s.vars[&memVar] = s.newValue3I(ssa.OpStoreWB, ssa.TypeMem, s.config.PtrSize, left, ptr, s.mem())
 	case t.IsInterface():
-		idata := s.newValue1(ssa.OpIData, Ptrto(Types[TUINT8]), right)
-		idataAddr := s.newValue1I(ssa.OpOffPtr, Ptrto(Types[TUINT8]), s.config.PtrSize, left)
-		s.rtcall(writebarrierptr, true, nil, idataAddr, idata)
+		// itab field is treated as a scalar.
+		idata := s.newValue1(ssa.OpIData, ptrto(Types[TUINT8]), right)
+		idataAddr := s.newValue1I(ssa.OpOffPtr, ptrto(Types[TUINT8]), s.config.PtrSize, left)
+		s.vars[&memVar] = s.newValue3I(ssa.OpStoreWB, ssa.TypeMem, s.config.PtrSize, idataAddr, idata, s.mem())
 	case t.IsStruct():
 		n := t.NumFields()
 		for i := 0; i < n; i++ {
@@ -3245,8 +3588,12 @@ func (s *state) storeTypePtrsWB(t *Type, left, right *ssa.Value) {
 			val := s.newValue1I(ssa.OpStructSelect, ft, int64(i), right)
 			s.storeTypePtrsWB(ft.(*Type), addr, val)
 		}
+	case t.IsArray() && t.NumElem() == 0:
+		// nothing
+	case t.IsArray() && t.NumElem() == 1:
+		s.storeTypePtrsWB(t.Elem(), left, s.newValue1I(ssa.OpArraySelect, t.Elem(), 0, right))
 	default:
-		s.Fatalf("bad write barrier type %s", t)
+		s.Fatalf("bad write barrier type %v", t)
 	}
 }
 
@@ -3263,13 +3610,13 @@ func (s *state) slice(t *Type, v, i, j, k *ssa.Value) (p, l, c *ssa.Value) {
 	switch {
 	case t.IsSlice():
 		elemtype = t.Elem()
-		ptrtype = Ptrto(elemtype)
+		ptrtype = ptrto(elemtype)
 		ptr = s.newValue1(ssa.OpSlicePtr, ptrtype, v)
 		len = s.newValue1(ssa.OpSliceLen, Types[TINT], v)
 		cap = s.newValue1(ssa.OpSliceCap, Types[TINT], v)
 	case t.IsString():
 		elemtype = Types[TUINT8]
-		ptrtype = Ptrto(elemtype)
+		ptrtype = ptrto(elemtype)
 		ptr = s.newValue1(ssa.OpStringPtr, ptrtype, v)
 		len = s.newValue1(ssa.OpStringLen, Types[TINT], v)
 		cap = len
@@ -3278,7 +3625,7 @@ func (s *state) slice(t *Type, v, i, j, k *ssa.Value) (p, l, c *ssa.Value) {
 			s.Fatalf("bad ptr to array in slice %v\n", t)
 		}
 		elemtype = t.Elem().Elem()
-		ptrtype = Ptrto(elemtype)
+		ptrtype = ptrto(elemtype)
 		s.nilCheck(v)
 		ptr = v
 		len = s.constInt(Types[TINT], t.Elem().NumElem())
@@ -3308,19 +3655,17 @@ func (s *state) slice(t *Type, v, i, j, k *ssa.Value) (p, l, c *ssa.Value) {
 	}
 
 	// Generate the following code assuming that indexes are in bounds.
-	// The conditional is to make sure that we don't generate a slice
+	// The masking is to make sure that we don't generate a slice
 	// that points to the next object in memory.
-	// rlen = j-i
-	// rcap = k-i
-	// delta = i*elemsize
-	// if rcap == 0 {
-	//    delta = 0
-	// }
-	// rptr = p+delta
+	// rlen = j - i
+	// rcap = k - i
+	// delta = i * elemsize
+	// rptr = p + delta&mask(rcap)
 	// result = (SliceMake rptr rlen rcap)
+	// where mask(x) is 0 if x==0 and -1 if x>0.
 	subOp := s.ssaOp(OSUB, Types[TINT])
-	eqOp := s.ssaOp(OEQ, Types[TINT])
 	mulOp := s.ssaOp(OMUL, Types[TINT])
+	andOp := s.ssaOp(OAND, Types[TINT])
 	rlen := s.newValue2(subOp, Types[TINT], j, i)
 	var rcap *ssa.Value
 	switch {
@@ -3335,47 +3680,30 @@ func (s *state) slice(t *Type, v, i, j, k *ssa.Value) (p, l, c *ssa.Value) {
 		rcap = s.newValue2(subOp, Types[TINT], k, i)
 	}
 
-	// delta = # of elements to offset pointer by.
-	s.vars[&deltaVar] = i
-
-	// Generate code to set delta=0 if the resulting capacity is zero.
-	if !((i.Op == ssa.OpConst64 && i.AuxInt == 0) ||
-		(i.Op == ssa.OpConst32 && int32(i.AuxInt) == 0)) {
-		cmp := s.newValue2(eqOp, Types[TBOOL], rcap, zero)
-
-		b := s.endBlock()
-		b.Kind = ssa.BlockIf
-		b.Likely = ssa.BranchUnlikely
-		b.SetControl(cmp)
-
-		// Generate block which zeros the delta variable.
-		nz := s.f.NewBlock(ssa.BlockPlain)
-		b.AddEdgeTo(nz)
-		s.startBlock(nz)
-		s.vars[&deltaVar] = zero
-		s.endBlock()
-
-		// All done.
-		merge := s.f.NewBlock(ssa.BlockPlain)
-		b.AddEdgeTo(merge)
-		nz.AddEdgeTo(merge)
-		s.startBlock(merge)
-
-		// TODO: use conditional moves somehow?
+	var rptr *ssa.Value
+	if (i.Op == ssa.OpConst64 || i.Op == ssa.OpConst32) && i.AuxInt == 0 {
+		// No pointer arithmetic necessary.
+		rptr = ptr
+	} else {
+		// delta = # of bytes to offset pointer by.
+		delta := s.newValue2(mulOp, Types[TINT], i, s.constInt(Types[TINT], elemtype.Width))
+		// If we're slicing to the point where the capacity is zero,
+		// zero out the delta.
+		mask := s.newValue1(ssa.OpSlicemask, Types[TINT], rcap)
+		delta = s.newValue2(andOp, Types[TINT], delta, mask)
+		// Compute rptr = ptr + delta
+		rptr = s.newValue2(ssa.OpAddPtr, ptrtype, ptr, delta)
 	}
 
-	// Compute rptr = ptr + delta * elemsize
-	rptr := s.newValue2(ssa.OpAddPtr, ptrtype, ptr, s.newValue2(mulOp, Types[TINT], s.variable(&deltaVar, Types[TINT]), s.constInt(Types[TINT], elemtype.Width)))
-	delete(s.vars, &deltaVar)
 	return rptr, rlen, rcap
 }
 
-type u2fcvtTab struct {
+type u642fcvtTab struct {
 	geq, cvt2F, and, rsh, or, add ssa.Op
 	one                           func(*state, ssa.Type, int64) *ssa.Value
 }
 
-var u64_f64 u2fcvtTab = u2fcvtTab{
+var u64_f64 u642fcvtTab = u642fcvtTab{
 	geq:   ssa.OpGeq64,
 	cvt2F: ssa.OpCvt64to64F,
 	and:   ssa.OpAnd64,
@@ -3385,7 +3713,7 @@ var u64_f64 u2fcvtTab = u2fcvtTab{
 	one:   (*state).constInt64,
 }
 
-var u64_f32 u2fcvtTab = u2fcvtTab{
+var u64_f32 u642fcvtTab = u642fcvtTab{
 	geq:   ssa.OpGeq64,
 	cvt2F: ssa.OpCvt64to32F,
 	and:   ssa.OpAnd64,
@@ -3395,29 +3723,15 @@ var u64_f32 u2fcvtTab = u2fcvtTab{
 	one:   (*state).constInt64,
 }
 
-// Excess generality on a machine with 64-bit integer registers.
-// Not used on AMD64.
-var u32_f32 u2fcvtTab = u2fcvtTab{
-	geq:   ssa.OpGeq32,
-	cvt2F: ssa.OpCvt32to32F,
-	and:   ssa.OpAnd32,
-	rsh:   ssa.OpRsh32Ux32,
-	or:    ssa.OpOr32,
-	add:   ssa.OpAdd32F,
-	one: func(s *state, t ssa.Type, x int64) *ssa.Value {
-		return s.constInt32(t, int32(x))
-	},
-}
-
 func (s *state) uint64Tofloat64(n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
-	return s.uintTofloat(&u64_f64, n, x, ft, tt)
+	return s.uint64Tofloat(&u64_f64, n, x, ft, tt)
 }
 
 func (s *state) uint64Tofloat32(n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
-	return s.uintTofloat(&u64_f32, n, x, ft, tt)
+	return s.uint64Tofloat(&u64_f32, n, x, ft, tt)
 }
 
-func (s *state) uintTofloat(cvttab *u2fcvtTab, n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
+func (s *state) uint64Tofloat(cvttab *u642fcvtTab, n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
 	// if x >= 0 {
 	//    result = (floatY) x
 	// } else {
@@ -3476,6 +3790,66 @@ func (s *state) uintTofloat(cvttab *u2fcvtTab, n *Node, x *ssa.Value, ft, tt *Ty
 	return s.variable(n, n.Type)
 }
 
+type u322fcvtTab struct {
+	cvtI2F, cvtF2F ssa.Op
+}
+
+var u32_f64 u322fcvtTab = u322fcvtTab{
+	cvtI2F: ssa.OpCvt32to64F,
+	cvtF2F: ssa.OpCopy,
+}
+
+var u32_f32 u322fcvtTab = u322fcvtTab{
+	cvtI2F: ssa.OpCvt32to32F,
+	cvtF2F: ssa.OpCvt64Fto32F,
+}
+
+func (s *state) uint32Tofloat64(n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
+	return s.uint32Tofloat(&u32_f64, n, x, ft, tt)
+}
+
+func (s *state) uint32Tofloat32(n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
+	return s.uint32Tofloat(&u32_f32, n, x, ft, tt)
+}
+
+func (s *state) uint32Tofloat(cvttab *u322fcvtTab, n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
+	// if x >= 0 {
+	// 	result = floatY(x)
+	// } else {
+	// 	result = floatY(float64(x) + (1<<32))
+	// }
+	cmp := s.newValue2(ssa.OpGeq32, Types[TBOOL], x, s.zeroVal(ft))
+	b := s.endBlock()
+	b.Kind = ssa.BlockIf
+	b.SetControl(cmp)
+	b.Likely = ssa.BranchLikely
+
+	bThen := s.f.NewBlock(ssa.BlockPlain)
+	bElse := s.f.NewBlock(ssa.BlockPlain)
+	bAfter := s.f.NewBlock(ssa.BlockPlain)
+
+	b.AddEdgeTo(bThen)
+	s.startBlock(bThen)
+	a0 := s.newValue1(cvttab.cvtI2F, tt, x)
+	s.vars[n] = a0
+	s.endBlock()
+	bThen.AddEdgeTo(bAfter)
+
+	b.AddEdgeTo(bElse)
+	s.startBlock(bElse)
+	a1 := s.newValue1(ssa.OpCvt32to64F, Types[TFLOAT64], x)
+	twoToThe32 := s.constFloat64(Types[TFLOAT64], float64(1<<32))
+	a2 := s.newValue2(ssa.OpAdd64F, Types[TFLOAT64], a1, twoToThe32)
+	a3 := s.newValue1(cvttab.cvtF2F, tt, a2)
+
+	s.vars[n] = a3
+	s.endBlock()
+	bElse.AddEdgeTo(bAfter)
+
+	s.startBlock(bAfter)
+	return s.variable(n, n.Type)
+}
+
 // referenceTypeBuiltin generates code for the len/cap builtins for maps and channels.
 func (s *state) referenceTypeBuiltin(n *Node, x *ssa.Value) *ssa.Value {
 	if !n.Left.Type.IsMap() && !n.Left.Type.IsChan() {
@@ -3528,22 +3902,50 @@ func (s *state) referenceTypeBuiltin(n *Node, x *ssa.Value) *ssa.Value {
 }
 
 type f2uCvtTab struct {
-	ltf, cvt2U, subf ssa.Op
-	value            func(*state, ssa.Type, float64) *ssa.Value
+	ltf, cvt2U, subf, or ssa.Op
+	floatValue           func(*state, ssa.Type, float64) *ssa.Value
+	intValue             func(*state, ssa.Type, int64) *ssa.Value
+	cutoff               uint64
 }
 
 var f32_u64 f2uCvtTab = f2uCvtTab{
-	ltf:   ssa.OpLess32F,
-	cvt2U: ssa.OpCvt32Fto64,
-	subf:  ssa.OpSub32F,
-	value: (*state).constFloat32,
+	ltf:        ssa.OpLess32F,
+	cvt2U:      ssa.OpCvt32Fto64,
+	subf:       ssa.OpSub32F,
+	or:         ssa.OpOr64,
+	floatValue: (*state).constFloat32,
+	intValue:   (*state).constInt64,
+	cutoff:     9223372036854775808,
 }
 
 var f64_u64 f2uCvtTab = f2uCvtTab{
-	ltf:   ssa.OpLess64F,
-	cvt2U: ssa.OpCvt64Fto64,
-	subf:  ssa.OpSub64F,
-	value: (*state).constFloat64,
+	ltf:        ssa.OpLess64F,
+	cvt2U:      ssa.OpCvt64Fto64,
+	subf:       ssa.OpSub64F,
+	or:         ssa.OpOr64,
+	floatValue: (*state).constFloat64,
+	intValue:   (*state).constInt64,
+	cutoff:     9223372036854775808,
+}
+
+var f32_u32 f2uCvtTab = f2uCvtTab{
+	ltf:        ssa.OpLess32F,
+	cvt2U:      ssa.OpCvt32Fto32,
+	subf:       ssa.OpSub32F,
+	or:         ssa.OpOr32,
+	floatValue: (*state).constFloat32,
+	intValue:   func(s *state, t ssa.Type, v int64) *ssa.Value { return s.constInt32(t, int32(v)) },
+	cutoff:     2147483648,
+}
+
+var f64_u32 f2uCvtTab = f2uCvtTab{
+	ltf:        ssa.OpLess64F,
+	cvt2U:      ssa.OpCvt64Fto32,
+	subf:       ssa.OpSub64F,
+	or:         ssa.OpOr32,
+	floatValue: (*state).constFloat64,
+	intValue:   func(s *state, t ssa.Type, v int64) *ssa.Value { return s.constInt32(t, int32(v)) },
+	cutoff:     2147483648,
 }
 
 func (s *state) float32ToUint64(n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
@@ -3553,16 +3955,25 @@ func (s *state) float64ToUint64(n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value
 	return s.floatToUint(&f64_u64, n, x, ft, tt)
 }
 
+func (s *state) float32ToUint32(n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
+	return s.floatToUint(&f32_u32, n, x, ft, tt)
+}
+
+func (s *state) float64ToUint32(n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
+	return s.floatToUint(&f64_u32, n, x, ft, tt)
+}
+
 func (s *state) floatToUint(cvttab *f2uCvtTab, n *Node, x *ssa.Value, ft, tt *Type) *ssa.Value {
-	// if x < 9223372036854775808.0 {
+	// cutoff:=1<<(intY_Size-1)
+	// if x < floatX(cutoff) {
 	// 	result = uintY(x)
 	// } else {
-	// 	y = x - 9223372036854775808.0
+	// 	y = x - floatX(cutoff)
 	// 	z = uintY(y)
-	// 	result = z | -9223372036854775808
+	// 	result = z | -(cutoff)
 	// }
-	twoToThe63 := cvttab.value(s, ft, 9223372036854775808.0)
-	cmp := s.newValue2(cvttab.ltf, Types[TBOOL], x, twoToThe63)
+	cutoff := cvttab.floatValue(s, ft, float64(cvttab.cutoff))
+	cmp := s.newValue2(cvttab.ltf, Types[TBOOL], x, cutoff)
 	b := s.endBlock()
 	b.Kind = ssa.BlockIf
 	b.SetControl(cmp)
@@ -3581,10 +3992,10 @@ func (s *state) floatToUint(cvttab *f2uCvtTab, n *Node, x *ssa.Value, ft, tt *Ty
 
 	b.AddEdgeTo(bElse)
 	s.startBlock(bElse)
-	y := s.newValue2(cvttab.subf, ft, x, twoToThe63)
+	y := s.newValue2(cvttab.subf, ft, x, cutoff)
 	y = s.newValue1(cvttab.cvt2U, tt, y)
-	z := s.constInt64(tt, -9223372036854775808)
-	a1 := s.newValue2(ssa.OpOr64, tt, y, z)
+	z := cvttab.intValue(s, tt, int64(-cvttab.cutoff))
+	a1 := s.newValue2(cvttab.or, tt, y, z)
 	s.vars[n] = a1
 	s.endBlock()
 	bElse.AddEdgeTo(bAfter)
@@ -3594,20 +4005,20 @@ func (s *state) floatToUint(cvttab *f2uCvtTab, n *Node, x *ssa.Value, ft, tt *Ty
 }
 
 // ifaceType returns the value for the word containing the type.
-// n is the node for the interface expression.
+// t is the type of the interface expression.
 // v is the corresponding value.
-func (s *state) ifaceType(n *Node, v *ssa.Value) *ssa.Value {
-	byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte)
+func (s *state) ifaceType(t *Type, v *ssa.Value) *ssa.Value {
+	byteptr := ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte)
 
-	if n.Type.IsEmptyInterface() {
-		// Have *eface. The type is the first word in the struct.
+	if t.IsEmptyInterface() {
+		// Have eface. The type is the first word in the struct.
 		return s.newValue1(ssa.OpITab, byteptr, v)
 	}
 
-	// Have *iface.
-	// The first word in the struct is the *itab.
-	// If the *itab is nil, return 0.
-	// Otherwise, the second word in the *itab is the type.
+	// Have iface.
+	// The first word in the struct is the itab.
+	// If the itab is nil, return 0.
+	// Otherwise, the second word in the itab is the type.
 
 	tab := s.newValue1(ssa.OpITab, byteptr, v)
 	s.vars[&typVar] = tab
@@ -3639,18 +4050,120 @@ func (s *state) ifaceType(n *Node, v *ssa.Value) *ssa.Value {
 // commaok indicates whether to panic or return a bool.
 // If commaok is false, resok will be nil.
 func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
-	iface := s.expr(n.Left)
-	typ := s.ifaceType(n.Left, iface)  // actual concrete type
+	iface := s.expr(n.Left)            // input interface
 	target := s.expr(typename(n.Type)) // target type
-	if !isdirectiface(n.Type) {
-		// walk rewrites ODOTTYPE/OAS2DOTTYPE into runtime calls except for this case.
-		Fatalf("dottype needs a direct iface type %s", n.Type)
+	byteptr := ptrto(Types[TUINT8])
+
+	if n.Type.IsInterface() {
+		if n.Type.IsEmptyInterface() {
+			// Converting to an empty interface.
+			// Input could be an empty or nonempty interface.
+			if Debug_typeassert > 0 {
+				Warnl(n.Lineno, "type assertion inlined")
+			}
+
+			// Get itab/type field from input.
+			itab := s.newValue1(ssa.OpITab, byteptr, iface)
+			// Conversion succeeds iff that field is not nil.
+			cond := s.newValue2(ssa.OpNeqPtr, Types[TBOOL], itab, s.constNil(byteptr))
+
+			if n.Left.Type.IsEmptyInterface() && commaok {
+				// Converting empty interface to empty interface with ,ok is just a nil check.
+				return iface, cond
+			}
+
+			// Branch on nilness.
+			b := s.endBlock()
+			b.Kind = ssa.BlockIf
+			b.SetControl(cond)
+			b.Likely = ssa.BranchLikely
+			bOk := s.f.NewBlock(ssa.BlockPlain)
+			bFail := s.f.NewBlock(ssa.BlockPlain)
+			b.AddEdgeTo(bOk)
+			b.AddEdgeTo(bFail)
+
+			if !commaok {
+				// On failure, panic by calling panicnildottype.
+				s.startBlock(bFail)
+				s.rtcall(panicnildottype, false, nil, target)
+
+				// On success, return (perhaps modified) input interface.
+				s.startBlock(bOk)
+				if n.Left.Type.IsEmptyInterface() {
+					res = iface // Use input interface unchanged.
+					return
+				}
+				// Load type out of itab, build interface with existing idata.
+				off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(Widthptr), itab)
+				typ := s.newValue2(ssa.OpLoad, byteptr, off, s.mem())
+				idata := s.newValue1(ssa.OpIData, n.Type, iface)
+				res = s.newValue2(ssa.OpIMake, n.Type, typ, idata)
+				return
+			}
+
+			s.startBlock(bOk)
+			// nonempty -> empty
+			// Need to load type from itab
+			off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(Widthptr), itab)
+			s.vars[&typVar] = s.newValue2(ssa.OpLoad, byteptr, off, s.mem())
+			s.endBlock()
+
+			// itab is nil, might as well use that as the nil result.
+			s.startBlock(bFail)
+			s.vars[&typVar] = itab
+			s.endBlock()
+
+			// Merge point.
+			bEnd := s.f.NewBlock(ssa.BlockPlain)
+			bOk.AddEdgeTo(bEnd)
+			bFail.AddEdgeTo(bEnd)
+			s.startBlock(bEnd)
+			idata := s.newValue1(ssa.OpIData, n.Type, iface)
+			res = s.newValue2(ssa.OpIMake, n.Type, s.variable(&typVar, byteptr), idata)
+			resok = cond
+			delete(s.vars, &typVar)
+			return
+		}
+		// converting to a nonempty interface needs a runtime call.
+		if Debug_typeassert > 0 {
+			Warnl(n.Lineno, "type assertion not inlined")
+		}
+		if n.Left.Type.IsEmptyInterface() {
+			if commaok {
+				call := s.rtcall(assertE2I2, true, []*Type{n.Type, Types[TBOOL]}, target, iface)
+				return call[0], call[1]
+			}
+			return s.rtcall(assertE2I, true, []*Type{n.Type}, target, iface)[0], nil
+		}
+		if commaok {
+			call := s.rtcall(assertI2I2, true, []*Type{n.Type, Types[TBOOL]}, target, iface)
+			return call[0], call[1]
+		}
+		return s.rtcall(assertI2I, true, []*Type{n.Type}, target, iface)[0], nil
 	}
 
 	if Debug_typeassert > 0 {
 		Warnl(n.Lineno, "type assertion inlined")
 	}
 
+	// Converting to a concrete type.
+	direct := isdirectiface(n.Type)
+	typ := s.ifaceType(n.Left.Type, iface) // actual concrete type of input interface
+
+	if Debug_typeassert > 0 {
+		Warnl(n.Lineno, "type assertion inlined")
+	}
+
+	var tmp *Node       // temporary for use with large types
+	var addr *ssa.Value // address of tmp
+	if commaok && !canSSAType(n.Type) {
+		// unSSAable type, use temporary.
+		// TODO: get rid of some of these temporaries.
+		tmp = temp(n.Type)
+		addr, _ = s.addr(tmp, false)
+		s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, tmp, s.mem())
+	}
+
 	// TODO:  If we have a nonempty interface and its itab field is nil,
 	// then this test is redundant and ifaceType should just branch directly to bFail.
 	cond := s.newValue2(ssa.OpEqPtr, Types[TBOOL], typ, target)
@@ -3659,8 +4172,6 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
 	b.SetControl(cond)
 	b.Likely = ssa.BranchLikely
 
-	byteptr := Ptrto(Types[TUINT8])
-
 	bOk := s.f.NewBlock(ssa.BlockPlain)
 	bFail := s.f.NewBlock(ssa.BlockPlain)
 	b.AddEdgeTo(bOk)
@@ -3672,34 +4183,60 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
 		taddr := s.newValue1A(ssa.OpAddr, byteptr, &ssa.ExternSymbol{Typ: byteptr, Sym: typenamesym(n.Left.Type)}, s.sb)
 		s.rtcall(panicdottype, false, nil, typ, target, taddr)
 
-		// on success, return idata field
+		// on success, return data from interface
 		s.startBlock(bOk)
-		return s.newValue1(ssa.OpIData, n.Type, iface), nil
+		if direct {
+			return s.newValue1(ssa.OpIData, n.Type, iface), nil
+		}
+		p := s.newValue1(ssa.OpIData, ptrto(n.Type), iface)
+		return s.newValue2(ssa.OpLoad, n.Type, p, s.mem()), nil
 	}
 
 	// commaok is the more complicated case because we have
 	// a control flow merge point.
 	bEnd := s.f.NewBlock(ssa.BlockPlain)
+	// Note that we need a new valVar each time (unlike okVar where we can
+	// reuse the variable) because it might have a different type every time.
+	valVar := &Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "val"}}
 
 	// type assertion succeeded
 	s.startBlock(bOk)
-	s.vars[&idataVar] = s.newValue1(ssa.OpIData, n.Type, iface)
+	if tmp == nil {
+		if direct {
+			s.vars[valVar] = s.newValue1(ssa.OpIData, n.Type, iface)
+		} else {
+			p := s.newValue1(ssa.OpIData, ptrto(n.Type), iface)
+			s.vars[valVar] = s.newValue2(ssa.OpLoad, n.Type, p, s.mem())
+		}
+	} else {
+		p := s.newValue1(ssa.OpIData, ptrto(n.Type), iface)
+		s.vars[&memVar] = s.newValue3I(ssa.OpMove, ssa.TypeMem, sizeAlignAuxInt(n.Type), addr, p, s.mem())
+	}
 	s.vars[&okVar] = s.constBool(true)
 	s.endBlock()
 	bOk.AddEdgeTo(bEnd)
 
 	// type assertion failed
 	s.startBlock(bFail)
-	s.vars[&idataVar] = s.constNil(byteptr)
+	if tmp == nil {
+		s.vars[valVar] = s.zeroVal(n.Type)
+	} else {
+		s.vars[&memVar] = s.newValue2I(ssa.OpZero, ssa.TypeMem, sizeAlignAuxInt(n.Type), addr, s.mem())
+	}
 	s.vars[&okVar] = s.constBool(false)
 	s.endBlock()
 	bFail.AddEdgeTo(bEnd)
 
 	// merge point
 	s.startBlock(bEnd)
-	res = s.variable(&idataVar, byteptr)
+	if tmp == nil {
+		res = s.variable(valVar, n.Type)
+		delete(s.vars, valVar)
+	} else {
+		res = s.newValue2(ssa.OpLoad, n.Type, addr, s.mem())
+		s.vars[&memVar] = s.newValue1A(ssa.OpVarKill, ssa.TypeMem, tmp, s.mem())
+	}
 	resok = s.variable(&okVar, Types[TBOOL])
-	delete(s.vars, &idataVar)
 	delete(s.vars, &okVar)
 	return res, resok
 }
@@ -3765,139 +4302,37 @@ func (s *state) checkgoto(from *Node, to *Node) {
 // variable returns the value of a variable at the current location.
 func (s *state) variable(name *Node, t ssa.Type) *ssa.Value {
 	v := s.vars[name]
-	if v == nil {
-		v = s.newValue0A(ssa.OpFwdRef, t, name)
-		s.fwdRefs = append(s.fwdRefs, v)
-		s.vars[name] = v
-		s.addNamedValue(name, v)
-	}
-	return v
-}
-
-func (s *state) mem() *ssa.Value {
-	return s.variable(&memVar, ssa.TypeMem)
-}
-
-func (s *state) linkForwardReferences(dm *sparseDefState) {
-
-	// Build SSA graph. Each variable on its first use in a basic block
-	// leaves a FwdRef in that block representing the incoming value
-	// of that variable. This function links that ref up with possible definitions,
-	// inserting Phi values as needed. This is essentially the algorithm
-	// described by Braun, Buchwald, Hack, Leißa, Mallon, and Zwinkau:
-	// http://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf
-	// Differences:
-	//   - We use FwdRef nodes to postpone phi building until the CFG is
-	//     completely built. That way we can avoid the notion of "sealed"
-	//     blocks.
-	//   - Phi optimization is a separate pass (in ../ssa/phielim.go).
-	for len(s.fwdRefs) > 0 {
-		v := s.fwdRefs[len(s.fwdRefs)-1]
-		s.fwdRefs = s.fwdRefs[:len(s.fwdRefs)-1]
-		s.resolveFwdRef(v, dm)
-	}
-}
-
-// resolveFwdRef modifies v to be the variable's value at the start of its block.
-// v must be a FwdRef op.
-func (s *state) resolveFwdRef(v *ssa.Value, dm *sparseDefState) {
-	b := v.Block
-	name := v.Aux.(*Node)
-	v.Aux = nil
-	if b == s.f.Entry {
-		// Live variable at start of function.
-		if s.canSSA(name) {
-			if strings.HasPrefix(name.Sym.Name, "autotmp_") {
-				// It's likely that this is an uninitialized variable in the entry block.
-				s.Fatalf("Treating auto as if it were arg, func %s, node %v, value %v", b.Func.Name, name, v)
-			}
-			v.Op = ssa.OpArg
-			v.Aux = name
-			return
-		}
-		// Not SSAable. Load it.
-		addr := s.decladdrs[name]
-		if addr == nil {
-			// TODO: closure args reach here.
-			s.Unimplementedf("unhandled closure arg %s at entry to function %s", name, b.Func.Name)
-		}
-		if _, ok := addr.Aux.(*ssa.ArgSymbol); !ok {
-			s.Fatalf("variable live at start of function %s is not an argument %s", b.Func.Name, name)
-		}
-		v.Op = ssa.OpLoad
-		v.AddArgs(addr, s.startmem)
-		return
-	}
-	if len(b.Preds) == 0 {
-		// This block is dead; we have no predecessors and we're not the entry block.
-		// It doesn't matter what we use here as long as it is well-formed.
-		v.Op = ssa.OpUnknown
-		return
-	}
-	// Find variable value on each predecessor.
-	var argstore [4]*ssa.Value
-	args := argstore[:0]
-	for _, e := range b.Preds {
-		p := e.Block()
-		p = dm.FindBetterDefiningBlock(name, p) // try sparse improvement on p
-		args = append(args, s.lookupVarOutgoing(p, v.Type, name, v.Line))
-	}
-
-	// Decide if we need a phi or not. We need a phi if there
-	// are two different args (which are both not v).
-	var w *ssa.Value
-	for _, a := range args {
-		if a == v {
-			continue // self-reference
-		}
-		if a == w {
-			continue // already have this witness
-		}
-		if w != nil {
-			// two witnesses, need a phi value
-			v.Op = ssa.OpPhi
-			v.AddArgs(args...)
-			return
-		}
-		w = a // save witness
+	if v != nil {
+		return v
 	}
-	if w == nil {
-		s.Fatalf("no witness for reachable phi %s", v)
+	v = s.fwdVars[name]
+	if v != nil {
+		return v
 	}
-	// One witness. Make v a copy of w.
-	v.Op = ssa.OpCopy
-	v.AddArg(w)
-}
 
-// lookupVarOutgoing finds the variable's value at the end of block b.
-func (s *state) lookupVarOutgoing(b *ssa.Block, t ssa.Type, name *Node, line int32) *ssa.Value {
-	for {
-		if v, ok := s.defvars[b.ID][name]; ok {
-			return v
-		}
-		// The variable is not defined by b and we haven't looked it up yet.
-		// If b has exactly one predecessor, loop to look it up there.
-		// Otherwise, give up and insert a new FwdRef and resolve it later.
-		if len(b.Preds) != 1 {
-			break
-		}
-		b = b.Preds[0].Block()
+	if s.curBlock == s.f.Entry {
+		// No variable should be live at entry.
+		s.Fatalf("Value live at entry. It shouldn't be. func %s, node %v, value %v", s.f.Name, name, v)
 	}
-	// Generate a FwdRef for the variable and return that.
-	v := b.NewValue0A(line, ssa.OpFwdRef, t, name)
-	s.fwdRefs = append(s.fwdRefs, v)
-	s.defvars[b.ID][name] = v
+	// Make a FwdRef, which records a value that's live on block input.
+	// We'll find the matching definition as part of insertPhis.
+	v = s.newValue0A(ssa.OpFwdRef, t, name)
+	s.fwdVars[name] = v
 	s.addNamedValue(name, v)
 	return v
 }
 
+func (s *state) mem() *ssa.Value {
+	return s.variable(&memVar, ssa.TypeMem)
+}
+
 func (s *state) addNamedValue(n *Node, v *ssa.Value) {
 	if n.Class == Pxxx {
 		// Don't track our dummy nodes (&memVar etc.).
 		return
 	}
-	if strings.HasPrefix(n.Sym.Name, "autotmp_") {
-		// Don't track autotmp_ variables.
+	if n.IsAutoTmp() {
+		// Don't track temporary variables.
 		return
 	}
 	if n.Class == PPARAMOUT {
@@ -3906,7 +4341,7 @@ func (s *state) addNamedValue(n *Node, v *ssa.Value) {
 		return
 	}
 	if n.Class == PAUTO && n.Xoffset != 0 {
-		s.Fatalf("AUTO var with offset %s %d", n, n.Xoffset)
+		s.Fatalf("AUTO var with offset %v %d", n, n.Xoffset)
 	}
 	loc := ssa.LocalSlot{N: n, Type: n.Type, Off: 0}
 	values, ok := s.f.NamedValues[loc]
@@ -3930,11 +4365,16 @@ type SSAGenState struct {
 
 	// bstart remembers where each block starts (indexed by block ID)
 	bstart []*obj.Prog
+
+	// 387 port: maps from SSE registers (REG_X?) to 387 registers (REG_F?)
+	SSEto387 map[int16]int16
+	// Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include x86-387, PPC, and Sparc V8.
+	ScratchFpMem *Node
 }
 
 // Pc returns the current Prog.
 func (s *SSAGenState) Pc() *obj.Prog {
-	return Pc
+	return pc
 }
 
 // SetLineno sets the current source line number.
@@ -3948,10 +4388,6 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) {
 	var s SSAGenState
 
 	e := f.Config.Frontend().(*ssaExport)
-	// We're about to emit a bunch of Progs.
-	// Since the only way to get here is to explicitly request it,
-	// just fail on unimplemented instead of trying to unwind our mess.
-	e.mustImplement = true
 
 	// Remember where each block starts.
 	s.bstart = make([]*obj.Prog, f.NumBlocks())
@@ -3963,36 +4399,43 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) {
 		valueProgs = make(map[*obj.Prog]*ssa.Value, f.NumValues())
 		blockProgs = make(map[*obj.Prog]*ssa.Block, f.NumBlocks())
 		f.Logf("genssa %s\n", f.Name)
-		blockProgs[Pc] = f.Blocks[0]
+		blockProgs[pc] = f.Blocks[0]
+	}
+
+	if Thearch.Use387 {
+		s.SSEto387 = map[int16]int16{}
 	}
 
+	s.ScratchFpMem = scratchFpMem
+	scratchFpMem = nil
+
 	// Emit basic blocks
 	for i, b := range f.Blocks {
-		s.bstart[b.ID] = Pc
+		s.bstart[b.ID] = pc
 		// Emit values in block
 		Thearch.SSAMarkMoves(&s, b)
 		for _, v := range b.Values {
-			x := Pc
+			x := pc
 			Thearch.SSAGenValue(&s, v)
 			if logProgs {
-				for ; x != Pc; x = x.Link {
+				for ; x != pc; x = x.Link {
 					valueProgs[x] = v
 				}
 			}
 		}
 		// Emit control flow instructions for block
 		var next *ssa.Block
-		if i < len(f.Blocks)-1 && (Debug['N'] == 0 || b.Kind == ssa.BlockCall) {
+		if i < len(f.Blocks)-1 && Debug['N'] == 0 {
 			// If -N, leave next==nil so every block with successors
 			// ends in a JMP (except call blocks - plive doesn't like
 			// select{send,recv} followed by a JMP call).  Helps keep
 			// line numbers for otherwise empty blocks.
 			next = f.Blocks[i+1]
 		}
-		x := Pc
+		x := pc
 		Thearch.SSAGenBlock(&s, b, next)
 		if logProgs {
-			for ; x != Pc; x = x.Link {
+			for ; x != pc; x = x.Link {
 				blockProgs[x] = b
 			}
 		}
@@ -4050,9 +4493,6 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) {
 		}
 	}
 
-	// Allocate stack frame
-	allocauto(ptxt)
-
 	// Generate gc bitmaps.
 	liveness(Curfn, ptxt, gcargs, gclocals)
 
@@ -4066,20 +4506,7 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) {
 	removevardef(ptxt)
 
 	f.Config.HTML.Close()
-}
-
-// movZero generates a register indirect move with a 0 immediate and keeps track of bytes left and next offset
-func movZero(as obj.As, width int64, nbytes int64, offset int64, regnum int16) (nleft int64, noff int64) {
-	p := Prog(as)
-	// TODO: use zero register on archs that support it.
-	p.From.Type = obj.TYPE_CONST
-	p.From.Offset = 0
-	p.To.Type = obj.TYPE_MEM
-	p.To.Reg = regnum
-	p.To.Offset = offset
-	offset += width
-	nleft = nbytes - width
-	return nleft, offset
+	f.Config.HTML = nil
 }
 
 type FloatingEQNEJump struct {
@@ -4128,12 +4555,25 @@ func SSAGenFPJump(s *SSAGenState, b, next *ssa.Block, jumps *[2][2]FloatingEQNEJ
 	}
 }
 
+func AuxOffset(v *ssa.Value) (offset int64) {
+	if v.Aux == nil {
+		return 0
+	}
+	switch sym := v.Aux.(type) {
+
+	case *ssa.AutoSymbol:
+		n := sym.Node.(*Node)
+		return n.Xoffset
+	}
+	return 0
+}
+
 // AddAux adds the offset in the aux fields (AuxInt and Aux) of v to a.
 func AddAux(a *obj.Addr, v *ssa.Value) {
 	AddAux2(a, v, v.AuxInt)
 }
 func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) {
-	if a.Type != obj.TYPE_MEM {
+	if a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR {
 		v.Fatalf("bad AddAux addr %v", a)
 	}
 	// add integer offset
@@ -4160,28 +4600,39 @@ func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) {
 		a.Name = obj.NAME_PARAM
 		a.Node = n
 		a.Sym = Linksym(n.Orig.Sym)
-		a.Offset += n.Xoffset // TODO: why do I have to add this here?  I don't for auto variables.
+		a.Offset += n.Xoffset
 	case *ssa.AutoSymbol:
 		n := sym.Node.(*Node)
 		a.Name = obj.NAME_AUTO
 		a.Node = n
 		a.Sym = Linksym(n.Sym)
+		a.Offset += n.Xoffset
 	default:
 		v.Fatalf("aux in %s not implemented %#v", v, v.Aux)
 	}
 }
 
+// sizeAlignAuxInt returns an AuxInt encoding the size and alignment of type t.
+func sizeAlignAuxInt(t *Type) int64 {
+	return ssa.MakeSizeAndAlign(t.Size(), t.Alignment()).Int64()
+}
+
 // extendIndex extends v to a full int width.
-func (s *state) extendIndex(v *ssa.Value) *ssa.Value {
+// panic using the given function if v does not fit in an int (only on 32-bit archs).
+func (s *state) extendIndex(v *ssa.Value, panicfn *Node) *ssa.Value {
 	size := v.Type.Size()
 	if size == s.config.IntSize {
 		return v
 	}
 	if size > s.config.IntSize {
-		// TODO: truncate 64-bit indexes on 32-bit pointer archs. We'd need to test
-		// the high word and branch to out-of-bounds failure if it is not 0.
-		s.Unimplementedf("64->32 index truncation not implemented")
-		return v
+		// truncate 64-bit indexes on 32-bit pointer archs. Test the
+		// high word and branch to out-of-bounds failure if it is not 0.
+		if Debug['B'] == 0 {
+			hi := s.newValue1(ssa.OpInt64Hi, Types[TUINT32], v)
+			cmp := s.newValue2(ssa.OpEq32, Types[TBOOL], hi, s.constInt32(Types[TUINT32], 0))
+			s.check(cmp, panicfn)
+		}
+		return s.newValue1(ssa.OpTrunc64to32, Types[TINT], v)
 	}
 
 	// Extend value to the required size
@@ -4220,17 +4671,52 @@ func (s *state) extendIndex(v *ssa.Value) *ssa.Value {
 	return s.newValue1(op, Types[TINT], v)
 }
 
-// SSARegNum returns the register (in cmd/internal/obj numbering) to
-// which v has been allocated. Panics if v is not assigned to a
-// register.
-// TODO: Make this panic again once it stops happening routinely.
-func SSARegNum(v *ssa.Value) int16 {
-	reg := v.Block.Func.RegAlloc[v.ID]
-	if reg == nil {
-		v.Unimplementedf("nil regnum for value: %s\n%s\n", v.LongString(), v.Block.Func)
-		return 0
+// CheckLoweredPhi checks that regalloc and stackalloc correctly handled phi values.
+// Called during ssaGenValue.
+func CheckLoweredPhi(v *ssa.Value) {
+	if v.Op != ssa.OpPhi {
+		v.Fatalf("CheckLoweredPhi called with non-phi value: %v", v.LongString())
+	}
+	if v.Type.IsMemory() {
+		return
+	}
+	f := v.Block.Func
+	loc := f.RegAlloc[v.ID]
+	for _, a := range v.Args {
+		if aloc := f.RegAlloc[a.ID]; aloc != loc { // TODO: .Equal() instead?
+			v.Fatalf("phi arg at different location than phi: %v @ %v, but arg %v @ %v\n%s\n", v, loc, a, aloc, v.Block.Func)
+		}
+	}
+}
+
+// CheckLoweredGetClosurePtr checks that v is the first instruction in the function's entry block.
+// The output of LoweredGetClosurePtr is generally hardwired to the correct register.
+// That register contains the closure pointer on closure entry.
+func CheckLoweredGetClosurePtr(v *ssa.Value) {
+	entry := v.Block.Func.Entry
+	if entry != v.Block || entry.Values[0] != v {
+		Fatalf("in %s, badly placed LoweredGetClosurePtr: %v %v", v.Block.Func.Name, v.Block, v)
 	}
-	return Thearch.SSARegToReg[reg.(*ssa.Register).Num]
+}
+
+// KeepAlive marks the variable referenced by OpKeepAlive as live.
+// Called during ssaGenValue.
+func KeepAlive(v *ssa.Value) {
+	if v.Op != ssa.OpKeepAlive {
+		v.Fatalf("KeepAlive called with non-KeepAlive value: %v", v.LongString())
+	}
+	if !v.Args[0].Type.IsPtrShaped() {
+		v.Fatalf("keeping non-pointer alive %v", v.Args[0])
+	}
+	n, _ := AutoVar(v.Args[0])
+	if n == nil {
+		v.Fatalf("KeepAlive with non-spilled value %s %s", v, v.Args[0])
+	}
+	// Note: KeepAlive arg may be a small part of a larger variable n.  We keep the
+	// whole variable n alive at this point. (Typically, this happens when
+	// we are requested to keep the idata portion of an interface{} alive, and
+	// we end up keeping the whole interface{} alive.  That's ok.)
+	Gvarlive(n)
 }
 
 // AutoVar returns a *Node and int64 representing the auto variable and offset within it
@@ -4243,6 +4729,31 @@ func AutoVar(v *ssa.Value) (*Node, int64) {
 	return loc.N.(*Node), loc.Off
 }
 
+func AddrAuto(a *obj.Addr, v *ssa.Value) {
+	n, off := AutoVar(v)
+	a.Type = obj.TYPE_MEM
+	a.Node = n
+	a.Sym = Linksym(n.Sym)
+	a.Offset = n.Xoffset + off
+	if n.Class == PPARAM || n.Class == PPARAMOUT {
+		a.Name = obj.NAME_PARAM
+	} else {
+		a.Name = obj.NAME_AUTO
+	}
+}
+
+func (s *SSAGenState) AddrScratch(a *obj.Addr) {
+	if s.ScratchFpMem == nil {
+		panic("no scratch memory available; forgot to declare usesScratch for Op?")
+	}
+	a.Type = obj.TYPE_MEM
+	a.Name = obj.NAME_AUTO
+	a.Node = s.ScratchFpMem
+	a.Sym = Linksym(s.ScratchFpMem.Sym)
+	a.Reg = int16(Thearch.REGSP)
+	a.Offset = s.ScratchFpMem.Xoffset
+}
+
 // fieldIdx finds the index of the field referred to by the ODOT node n.
 func fieldIdx(n *Node) int {
 	t := n.Left.Type
@@ -4262,7 +4773,7 @@ func fieldIdx(n *Node) int {
 		}
 		return i
 	}
-	panic(fmt.Sprintf("can't find field in expr %s\n", n))
+	panic(fmt.Sprintf("can't find field in expr %v\n", n))
 
 	// TODO: keep the result of this function somewhere in the ODOT Node
 	// so we don't have to recompute it each time we need it.
@@ -4270,9 +4781,7 @@ func fieldIdx(n *Node) int {
 
 // ssaExport exports a bunch of compiler services for the ssa backend.
 type ssaExport struct {
-	log           bool
-	unimplemented bool
-	mustImplement bool
+	log bool
 }
 
 func (s *ssaExport) TypeBool() ssa.Type    { return Types[TBOOL] }
@@ -4289,25 +4798,24 @@ func (s *ssaExport) TypeFloat64() ssa.Type { return Types[TFLOAT64] }
 func (s *ssaExport) TypeInt() ssa.Type     { return Types[TINT] }
 func (s *ssaExport) TypeUintptr() ssa.Type { return Types[TUINTPTR] }
 func (s *ssaExport) TypeString() ssa.Type  { return Types[TSTRING] }
-func (s *ssaExport) TypeBytePtr() ssa.Type { return Ptrto(Types[TUINT8]) }
+func (s *ssaExport) TypeBytePtr() ssa.Type { return ptrto(Types[TUINT8]) }
 
 // StringData returns a symbol (a *Sym wrapped in an interface) which
 // is the data component of a global string constant containing s.
 func (*ssaExport) StringData(s string) interface{} {
 	// TODO: is idealstring correct?  It might not matter...
-	_, data := stringsym(s)
+	data := stringsym(s)
 	return &ssa.ExternSymbol{Typ: idealstring, Sym: data}
 }
 
 func (e *ssaExport) Auto(t ssa.Type) ssa.GCNode {
-	n := temp(t.(*Type))   // Note: adds new auto to Curfn.Func.Dcl list
-	e.mustImplement = true // This modifies the input to SSA, so we want to make sure we succeed from here!
+	n := temp(t.(*Type)) // Note: adds new auto to Curfn.Func.Dcl list
 	return n
 }
 
 func (e *ssaExport) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
 	n := name.N.(*Node)
-	ptrType := Ptrto(Types[TUINT8])
+	ptrType := ptrto(Types[TUINT8])
 	lenType := Types[TINT]
 	if n.Class == PAUTO && !n.Addrtaken {
 		// Split this string up into two separate variables.
@@ -4321,7 +4829,7 @@ func (e *ssaExport) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlo
 
 func (e *ssaExport) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
 	n := name.N.(*Node)
-	t := Ptrto(Types[TUINT8])
+	t := ptrto(Types[TUINT8])
 	if n.Class == PAUTO && !n.Addrtaken {
 		// Split this interface up into two separate variables.
 		f := ".itab"
@@ -4338,7 +4846,7 @@ func (e *ssaExport) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.Local
 
 func (e *ssaExport) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) {
 	n := name.N.(*Node)
-	ptrType := Ptrto(name.Type.ElemType().(*Type))
+	ptrType := ptrto(name.Type.ElemType().(*Type))
 	lenType := Types[TINT]
 	if n.Class == PAUTO && !n.Addrtaken {
 		// Split this slice up into three separate variables.
@@ -4372,6 +4880,27 @@ func (e *ssaExport) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSl
 	return ssa.LocalSlot{N: n, Type: t, Off: name.Off}, ssa.LocalSlot{N: n, Type: t, Off: name.Off + s}
 }
 
+func (e *ssaExport) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
+	n := name.N.(*Node)
+	var t *Type
+	if name.Type.IsSigned() {
+		t = Types[TINT32]
+	} else {
+		t = Types[TUINT32]
+	}
+	if n.Class == PAUTO && !n.Addrtaken {
+		// Split this int64 up into two separate variables.
+		h := e.namedAuto(n.Sym.Name+".hi", t)
+		l := e.namedAuto(n.Sym.Name+".lo", Types[TUINT32])
+		return ssa.LocalSlot{N: h, Type: t, Off: 0}, ssa.LocalSlot{N: l, Type: Types[TUINT32], Off: 0}
+	}
+	// Return the two parts of the larger variable.
+	if Thearch.LinkArch.ByteOrder == binary.BigEndian {
+		return ssa.LocalSlot{N: n, Type: t, Off: name.Off}, ssa.LocalSlot{N: n, Type: Types[TUINT32], Off: name.Off + 4}
+	}
+	return ssa.LocalSlot{N: n, Type: t, Off: name.Off + 4}, ssa.LocalSlot{N: n, Type: Types[TUINT32], Off: name.Off}
+}
+
 func (e *ssaExport) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
 	n := name.N.(*Node)
 	st := name.Type
@@ -4386,11 +4915,26 @@ func (e *ssaExport) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
 	return ssa.LocalSlot{N: n, Type: ft, Off: name.Off + st.FieldOff(i)}
 }
 
+func (e *ssaExport) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
+	n := name.N.(*Node)
+	at := name.Type
+	if at.NumElem() != 1 {
+		Fatalf("bad array size")
+	}
+	et := at.ElemType()
+	if n.Class == PAUTO && !n.Addrtaken {
+		x := e.namedAuto(n.Sym.Name+"[0]", et)
+		return ssa.LocalSlot{N: x, Type: et, Off: 0}
+	}
+	return ssa.LocalSlot{N: n, Type: et, Off: name.Off}
+}
+
 // namedAuto returns a new AUTO variable with the given name and type.
+// These are exposed to the debugger.
 func (e *ssaExport) namedAuto(name string, typ ssa.Type) ssa.GCNode {
 	t := typ.(*Type)
-	s := &Sym{Name: name, Pkg: autopkg}
-	n := Nod(ONAME, nil, nil)
+	s := &Sym{Name: name, Pkg: localpkg}
+	n := nod(ONAME, nil, nil)
 	s.Def = n
 	s.Def.Used = true
 	n.Sym = s
@@ -4404,8 +4948,6 @@ func (e *ssaExport) namedAuto(name string, typ ssa.Type) ssa.GCNode {
 	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
 
 	dowidth(t)
-	e.mustImplement = true
-
 	return n
 }
 
@@ -4419,8 +4961,7 @@ func (e *ssaExport) Line(line int32) string {
 
 // Log logs a message from the compiler.
 func (e *ssaExport) Logf(msg string, args ...interface{}) {
-	// If e was marked as unimplemented, anything could happen. Ignore.
-	if e.log && !e.unimplemented {
+	if e.log {
 		fmt.Printf(msg, args...)
 	}
 }
@@ -4431,26 +4972,8 @@ func (e *ssaExport) Log() bool {
 
 // Fatal reports a compiler error and exits.
 func (e *ssaExport) Fatalf(line int32, msg string, args ...interface{}) {
-	// If e was marked as unimplemented, anything could happen. Ignore.
-	if !e.unimplemented {
-		lineno = line
-		Fatalf(msg, args...)
-	}
-}
-
-// Unimplemented reports that the function cannot be compiled.
-// It will be removed once SSA work is complete.
-func (e *ssaExport) Unimplementedf(line int32, msg string, args ...interface{}) {
-	if e.mustImplement {
-		lineno = line
-		Fatalf(msg, args...)
-	}
-	const alwaysLog = false // enable to calculate top unimplemented features
-	if !e.unimplemented && (e.log || alwaysLog) {
-		// first implementation failure, print explanation
-		fmt.Printf("SSA unimplemented: "+msg+"\n", args...)
-	}
-	e.unimplemented = true
+	lineno = line
+	Fatalf(msg, args...)
 }
 
 // Warnl reports a "warning", which is usually flag-triggered
@@ -4463,6 +4986,14 @@ func (e *ssaExport) Debug_checknil() bool {
 	return Debug_checknil != 0
 }
 
+func (e *ssaExport) Debug_wb() bool {
+	return Debug_wb != 0
+}
+
+func (e *ssaExport) Syslook(name string) interface{} {
+	return syslook(name).Sym
+}
+
 func (n *Node) Typ() ssa.Type {
 	return n.Type
 }
diff --git a/src/cmd/compile/internal/gc/ssa_test.go b/src/cmd/compile/internal/gc/ssa_test.go
index c89917d..1aebd90 100644
--- a/src/cmd/compile/internal/gc/ssa_test.go
+++ b/src/cmd/compile/internal/gc/ssa_test.go
@@ -9,7 +9,6 @@ import (
 	"internal/testenv"
 	"os/exec"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"testing"
 )
@@ -17,15 +16,17 @@ import (
 // TODO: move all these tests elsewhere?
 // Perhaps teach test/run.go how to run them with a new action verb.
 func runTest(t *testing.T, filename string) {
+	t.Parallel()
 	doTest(t, filename, "run")
 }
 func buildTest(t *testing.T, filename string) {
+	t.Parallel()
 	doTest(t, filename, "build")
 }
 func doTest(t *testing.T, filename string, kind string) {
 	testenv.MustHaveGoBuild(t)
 	var stdout, stderr bytes.Buffer
-	cmd := exec.Command("go", kind, filepath.Join("testdata", filename))
+	cmd := exec.Command(testenv.GoToolPath(t), kind, filepath.Join("testdata", filename))
 	cmd.Stdout = &stdout
 	cmd.Stderr = &stderr
 	if err := cmd.Run(); err != nil {
@@ -40,66 +41,63 @@ func doTest(t *testing.T, filename string, kind string) {
 }
 
 // TestShortCircuit tests OANDAND and OOROR expressions and short circuiting.
-func TestShortCircuit(t *testing.T) { runTest(t, "short_ssa.go") }
+func TestShortCircuit(t *testing.T) { runTest(t, "short.go") }
 
 // TestBreakContinue tests that continue and break statements do what they say.
-func TestBreakContinue(t *testing.T) { runTest(t, "break_ssa.go") }
+func TestBreakContinue(t *testing.T) { runTest(t, "break.go") }
 
 // TestTypeAssertion tests type assertions.
-func TestTypeAssertion(t *testing.T) { runTest(t, "assert_ssa.go") }
+func TestTypeAssertion(t *testing.T) { runTest(t, "assert.go") }
 
 // TestArithmetic tests that both backends have the same result for arithmetic expressions.
-func TestArithmetic(t *testing.T) {
-	if runtime.GOARCH == "386" {
-		t.Skip("legacy 386 compiler can't handle this test")
-	}
-	runTest(t, "arith_ssa.go")
-}
+func TestArithmetic(t *testing.T) { runTest(t, "arith.go") }
 
 // TestFP tests that both backends have the same result for floating point expressions.
-func TestFP(t *testing.T) { runTest(t, "fp_ssa.go") }
+func TestFP(t *testing.T) { runTest(t, "fp.go") }
 
 // TestArithmeticBoundary tests boundary results for arithmetic operations.
-func TestArithmeticBoundary(t *testing.T) { runTest(t, "arithBoundary_ssa.go") }
+func TestArithmeticBoundary(t *testing.T) { runTest(t, "arithBoundary.go") }
 
 // TestArithmeticConst tests results for arithmetic operations against constants.
-func TestArithmeticConst(t *testing.T) { runTest(t, "arithConst_ssa.go") }
+func TestArithmeticConst(t *testing.T) { runTest(t, "arithConst.go") }
 
-func TestChan(t *testing.T) { runTest(t, "chan_ssa.go") }
+func TestChan(t *testing.T) { runTest(t, "chan.go") }
 
-func TestCompound(t *testing.T) { runTest(t, "compound_ssa.go") }
+func TestCompound(t *testing.T) { runTest(t, "compound.go") }
 
-func TestCtl(t *testing.T) { runTest(t, "ctl_ssa.go") }
+func TestCtl(t *testing.T) { runTest(t, "ctl.go") }
 
-func TestLoadStore(t *testing.T) { runTest(t, "loadstore_ssa.go") }
+func TestLoadStore(t *testing.T) { runTest(t, "loadstore.go") }
 
-func TestMap(t *testing.T) { runTest(t, "map_ssa.go") }
+func TestMap(t *testing.T) { runTest(t, "map.go") }
 
-func TestRegalloc(t *testing.T) { runTest(t, "regalloc_ssa.go") }
+func TestRegalloc(t *testing.T) { runTest(t, "regalloc.go") }
 
-func TestString(t *testing.T) { runTest(t, "string_ssa.go") }
+func TestString(t *testing.T) { runTest(t, "string.go") }
 
-func TestDeferNoReturn(t *testing.T) { buildTest(t, "deferNoReturn_ssa.go") }
+func TestDeferNoReturn(t *testing.T) { buildTest(t, "deferNoReturn.go") }
 
 // TestClosure tests closure related behavior.
-func TestClosure(t *testing.T) { runTest(t, "closure_ssa.go") }
+func TestClosure(t *testing.T) { runTest(t, "closure.go") }
 
-func TestArray(t *testing.T) { runTest(t, "array_ssa.go") }
+func TestArray(t *testing.T) { runTest(t, "array.go") }
 
-func TestAppend(t *testing.T) { runTest(t, "append_ssa.go") }
+func TestAppend(t *testing.T) { runTest(t, "append.go") }
 
-func TestZero(t *testing.T) { runTest(t, "zero_ssa.go") }
+func TestZero(t *testing.T) { runTest(t, "zero.go") }
 
-func TestAddressed(t *testing.T) { runTest(t, "addressed_ssa.go") }
+func TestAddressed(t *testing.T) { runTest(t, "addressed.go") }
 
-func TestCopy(t *testing.T) { runTest(t, "copy_ssa.go") }
+func TestCopy(t *testing.T) { runTest(t, "copy.go") }
 
-func TestUnsafe(t *testing.T) { runTest(t, "unsafe_ssa.go") }
+func TestUnsafe(t *testing.T) { runTest(t, "unsafe.go") }
 
-func TestPhi(t *testing.T) { runTest(t, "phi_ssa.go") }
+func TestPhi(t *testing.T) { runTest(t, "phi.go") }
 
 func TestSlice(t *testing.T) { runTest(t, "slice.go") }
 
 func TestNamedReturn(t *testing.T) { runTest(t, "namedReturn.go") }
 
 func TestDuplicateLoad(t *testing.T) { runTest(t, "dupLoad.go") }
+
+func TestSqrt(t *testing.T) { runTest(t, "sqrt_const.go") }
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 1db1cba..a53ba1f 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -27,7 +27,7 @@ type Error struct {
 var errors []Error
 
 func errorexit() {
-	Flusherrors()
+	flusherrors()
 	if outfile != "" {
 		os.Remove(outfile)
 	}
@@ -58,8 +58,10 @@ func (x byLineno) Len() int           { return len(x) }
 func (x byLineno) Less(i, j int) bool { return x[i].lineno < x[j].lineno }
 func (x byLineno) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
 
-func Flusherrors() {
-	bstdout.Flush()
+// flusherrors sorts errors seen so far by line number, prints them to stdout,
+// and empties the errors array.
+func flusherrors() {
+	Ctxt.Bso.Flush()
 	if len(errors) == 0 {
 		return
 	}
@@ -74,7 +76,7 @@ func Flusherrors() {
 
 func hcrash() {
 	if Debug['h'] != 0 {
-		Flusherrors()
+		flusherrors()
 		if outfile != "" {
 			os.Remove(outfile)
 		}
@@ -108,7 +110,7 @@ func yyerrorl(line int32, format string, args ...interface{}) {
 		lasterror.syntax = line
 	} else {
 		// only one of multiple equal non-syntax errors per line
-		// (Flusherrors shows only one of them, so we filter them
+		// (flusherrors shows only one of them, so we filter them
 		// here as best as we can (they may not appear in order)
 		// so that we don't count them here and exit early, and
 		// then have nothing to show for.)
@@ -124,13 +126,13 @@ func yyerrorl(line int32, format string, args ...interface{}) {
 	hcrash()
 	nerrors++
 	if nsavederrors+nerrors >= 10 && Debug['e'] == 0 {
-		Flusherrors()
+		flusherrors()
 		fmt.Printf("%v: too many errors\n", linestr(line))
 		errorexit()
 	}
 }
 
-func Yyerror(format string, args ...interface{}) {
+func yyerror(format string, args ...interface{}) {
 	yyerrorl(lineno, format, args...)
 }
 
@@ -143,19 +145,19 @@ func Warn(fmt_ string, args ...interface{}) {
 func Warnl(line int32, fmt_ string, args ...interface{}) {
 	adderr(line, fmt_, args...)
 	if Debug['m'] != 0 {
-		Flusherrors()
+		flusherrors()
 	}
 }
 
 func Fatalf(fmt_ string, args ...interface{}) {
-	Flusherrors()
+	flusherrors()
 
 	fmt.Printf("%v: internal compiler error: ", linestr(lineno))
 	fmt.Printf(fmt_, args...)
 	fmt.Printf("\n")
 
 	// If this is a released compiler version, ask for a bug report.
-	if strings.HasPrefix(obj.Getgoversion(), "release") {
+	if strings.HasPrefix(obj.Version, "release") {
 		fmt.Printf("\n")
 		fmt.Printf("Please file a bug report including a short program that triggers the error.\n")
 		fmt.Printf("https://golang.org/issue/new\n")
@@ -202,10 +204,10 @@ func setlineno(n *Node) int32 {
 	lno := lineno
 	if n != nil {
 		switch n.Op {
-		case ONAME, OTYPE, OPACK:
+		case ONAME, OPACK:
 			break
 
-		case OLITERAL:
+		case OLITERAL, OTYPE:
 			if n.Sym != nil {
 				break
 			}
@@ -225,25 +227,44 @@ func setlineno(n *Node) int32 {
 	return lno
 }
 
-func Lookup(name string) *Sym {
+func lookup(name string) *Sym {
 	return localpkg.Lookup(name)
 }
 
-func Lookupf(format string, a ...interface{}) *Sym {
-	return Lookup(fmt.Sprintf(format, a...))
+func lookupf(format string, a ...interface{}) *Sym {
+	return lookup(fmt.Sprintf(format, a...))
 }
 
-func LookupBytes(name []byte) *Sym {
+func lookupBytes(name []byte) *Sym {
 	return localpkg.LookupBytes(name)
 }
 
-// LookupN looks up the symbol starting with prefix and ending with
-// the decimal n. If prefix is too long, LookupN panics.
-func LookupN(prefix string, n int) *Sym {
+// lookupN looks up the symbol starting with prefix and ending with
+// the decimal n. If prefix is too long, lookupN panics.
+func lookupN(prefix string, n int) *Sym {
 	var buf [20]byte // plenty long enough for all current users
 	copy(buf[:], prefix)
 	b := strconv.AppendInt(buf[:len(prefix)], int64(n), 10)
-	return LookupBytes(b)
+	return lookupBytes(b)
+}
+
+// autolabel generates a new Name node for use with
+// an automatically generated label.
+// prefix is a short mnemonic (e.g. ".s" for switch)
+// to help with debugging.
+// It should begin with "." to avoid conflicts with
+// user labels.
+func autolabel(prefix string) *Node {
+	if prefix[0] != '.' {
+		Fatalf("autolabel prefix must start with '.', have %q", prefix)
+	}
+	fn := Curfn
+	if Curfn == nil {
+		Fatalf("autolabel outside function")
+	}
+	n := fn.Func.Label
+	fn.Func.Label++
+	return newname(lookupN(prefix, int(n)))
 }
 
 var initSyms []*Sym
@@ -288,7 +309,7 @@ func Pkglookup(name string, pkg *Pkg) *Sym {
 
 func restrictlookup(name string, pkg *Pkg) *Sym {
 	if !exportname(name) && pkg != localpkg {
-		Yyerror("cannot refer to unexported name %s.%s", pkg.Name, name)
+		yyerror("cannot refer to unexported name %s.%s", pkg.Name, name)
 	}
 	return Pkglookup(name, pkg)
 }
@@ -307,7 +328,7 @@ func importdot(opkg *Pkg, pack *Node) {
 		if !exportname(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot
 			continue
 		}
-		s1 = Lookup(s.Name)
+		s1 = lookup(s.Name)
 		if s1.Def != nil {
 			pkgerror = fmt.Sprintf("during import %q", opkg.Path)
 			redeclare(s1, pkgerror)
@@ -331,7 +352,7 @@ func importdot(opkg *Pkg, pack *Node) {
 	}
 }
 
-func Nod(op Op, nleft *Node, nright *Node) *Node {
+func nod(op Op, nleft *Node, nright *Node) *Node {
 	n := new(Node)
 	n.Op = op
 	n.Left = nleft
@@ -342,19 +363,12 @@ func Nod(op Op, nleft *Node, nright *Node) *Node {
 	switch op {
 	case OCLOSURE, ODCLFUNC:
 		n.Func = new(Func)
-		n.Func.FCurfn = Curfn
+		n.Func.IsHiddenClosure = Curfn != nil
 	case ONAME:
 		n.Name = new(Name)
 		n.Name.Param = new(Param)
 	case OLABEL, OPACK:
 		n.Name = new(Name)
-	case ODCLFIELD:
-		if nleft != nil {
-			n.Name = nleft.Name
-		} else {
-			n.Name = new(Name)
-			n.Name.Param = new(Param)
-		}
 	}
 	if n.Name != nil {
 		n.Name.Curfn = Curfn
@@ -362,10 +376,10 @@ func Nod(op Op, nleft *Node, nright *Node) *Node {
 	return n
 }
 
-// NodSym makes a Node with Op op and with the Left field set to left
+// nodSym makes a Node with Op op and with the Left field set to left
 // and the Sym field set to sym. This is for ODOT and friends.
-func NodSym(op Op, left *Node, sym *Sym) *Node {
-	n := Nod(op, left, nil)
+func nodSym(op Op, left *Node, sym *Sym) *Node {
+	n := nod(op, left, nil)
 	n.Sym = sym
 	return n
 }
@@ -374,7 +388,7 @@ func saveorignode(n *Node) {
 	if n.Orig != nil {
 		return
 	}
-	norig := Nod(n.Op, nil, nil)
+	norig := nod(n.Op, nil, nil)
 	*norig = *n
 	n.Orig = norig
 }
@@ -408,8 +422,8 @@ func (x methcmp) Less(i, j int) bool {
 	return false
 }
 
-func Nodintconst(v int64) *Node {
-	c := Nod(OLITERAL, nil, nil)
+func nodintconst(v int64) *Node {
+	c := nod(OLITERAL, nil, nil)
 	c.Addable = true
 	c.SetVal(Val{new(Mpint)})
 	c.Val().U.(*Mpint).SetInt64(v)
@@ -419,7 +433,7 @@ func Nodintconst(v int64) *Node {
 }
 
 func nodfltconst(v *Mpflt) *Node {
-	c := Nod(OLITERAL, nil, nil)
+	c := nod(OLITERAL, nil, nil)
 	c.Addable = true
 	c.SetVal(Val{newMpflt()})
 	c.Val().U.(*Mpflt).Set(v)
@@ -443,43 +457,19 @@ func Nodconst(n *Node, t *Type, v int64) {
 }
 
 func nodnil() *Node {
-	c := Nodintconst(0)
+	c := nodintconst(0)
 	c.SetVal(Val{new(NilVal)})
 	c.Type = Types[TNIL]
 	return c
 }
 
-func Nodbool(b bool) *Node {
-	c := Nodintconst(0)
+func nodbool(b bool) *Node {
+	c := nodintconst(0)
 	c.SetVal(Val{b})
 	c.Type = idealbool
 	return c
 }
 
-func aindex(b *Node, t *Type) *Type {
-	hasbound := false
-	var bound int64
-	b = typecheck(b, Erv)
-	if b != nil {
-		switch consttype(b) {
-		default:
-			Yyerror("array bound must be an integer expression")
-
-		case CTINT, CTRUNE:
-			hasbound = true
-			bound = b.Int64()
-			if bound < 0 {
-				Yyerror("array bound must be non negative")
-			}
-		}
-	}
-
-	if !hasbound {
-		return typSlice(t)
-	}
-	return typArray(t, bound)
-}
-
 // treecopy recursively copies n, with the exception of
 // ONAME, OLITERAL, OTYPE, and non-iota ONONAME leaves.
 // Copies of iota ONONAME nodes are assigned the current
@@ -507,7 +497,7 @@ func treecopy(n *Node, lineno int32) *Node {
 		return &m
 
 	case ONONAME:
-		if n.Sym == Lookup("iota") {
+		if n.Sym == lookup("iota") {
 			// Not sure yet whether this is the real iota,
 			// but make a copy of the Node* just in case,
 			// so that all the copies of this const definition
@@ -516,9 +506,7 @@ func treecopy(n *Node, lineno int32) *Node {
 			if lineno != 0 {
 				m.Lineno = lineno
 			}
-			m.Name = new(Name)
-			*m.Name = *n.Name
-			m.Name.Iota = iota_
+			m.SetIota(iota_)
 			return &m
 		}
 		return n
@@ -570,14 +558,15 @@ func isblanksym(s *Sym) bool {
 	return s != nil && s.Name == "_"
 }
 
-// given receiver of type t (t == r or t == *r)
-// return type to hang methods off (r).
-func methtype(t *Type, mustname int) *Type {
+// methtype returns the underlying type, if any,
+// that owns methods with receiver parameter t.
+// The result is either a named type or an anonymous struct.
+func methtype(t *Type) *Type {
 	if t == nil {
 		return nil
 	}
 
-	// strip away pointer if it's there
+	// Strip away pointer if it's there.
 	if t.IsPtr() {
 		if t.Sym != nil {
 			return nil
@@ -588,29 +577,20 @@ func methtype(t *Type, mustname int) *Type {
 		}
 	}
 
-	// need a type name
-	if t.Sym == nil && (mustname != 0 || !t.IsStruct()) {
+	// Must be a named type or anonymous struct.
+	if t.Sym == nil && !t.IsStruct() {
 		return nil
 	}
 
-	// check types
-	if !issimple[t.Etype] {
-		switch t.Etype {
-		default:
-			return nil
-
-		case TSTRUCT,
-			TARRAY,
-			TSLICE,
-			TMAP,
-			TCHAN,
-			TSTRING,
-			TFUNC:
-			break
-		}
+	// Check types.
+	if issimple[t.Etype] {
+		return t
 	}
-
-	return t
+	switch t.Etype {
+	case TARRAY, TCHAN, TFUNC, TMAP, TSLICE, TSTRING, TSTRUCT:
+		return t
+	}
+	return nil
 }
 
 func cplxsubtype(et EType) EType {
@@ -626,14 +606,19 @@ func cplxsubtype(et EType) EType {
 	return 0
 }
 
-// Eqtype reports whether t1 and t2 are identical, following the spec rules.
+// 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 *Type) bool {
-	return eqtype1(t1, t2, nil)
+func eqtype(t1, t2 *Type) bool {
+	return eqtype1(t1, t2, true, nil)
+}
+
+// eqtypeIgnoreTags is like eqtype but it ignores struct tags for struct identity.
+func eqtypeIgnoreTags(t1, t2 *Type) bool {
+	return eqtype1(t1, t2, false, nil)
 }
 
 type typePair struct {
@@ -641,7 +626,7 @@ type typePair struct {
 	t2 *Type
 }
 
-func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
+func eqtype1(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool {
 	if t1 == t2 {
 		return true
 	}
@@ -670,10 +655,10 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
 
 	switch t1.Etype {
 	case TINTER, TSTRUCT:
-		t1, i1 := IterFields(t1)
-		t2, i2 := IterFields(t2)
+		t1, i1 := iterFields(t1)
+		t2, i2 := iterFields(t2)
 		for ; t1 != nil && t2 != nil; t1, t2 = i1.Next(), i2.Next() {
-			if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, assumedEqual) || t1.Note != t2.Note {
+			if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, cmpTags, assumedEqual) || cmpTags && t1.Note != t2.Note {
 				return false
 			}
 		}
@@ -689,10 +674,10 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
 		// equality, because they're never relevant.
 		for _, f := range paramsResults {
 			// Loop over fields in structs, ignoring argument names.
-			ta, ia := IterFields(f(t1))
-			tb, ib := IterFields(f(t2))
+			ta, ia := iterFields(f(t1))
+			tb, ib := iterFields(f(t2))
 			for ; ta != nil && tb != nil; ta, tb = ia.Next(), ib.Next() {
-				if ta.Isddd != tb.Isddd || !eqtype1(ta.Type, tb.Type, assumedEqual) {
+				if ta.Isddd != tb.Isddd || !eqtype1(ta.Type, tb.Type, cmpTags, assumedEqual) {
 					return false
 				}
 			}
@@ -713,13 +698,13 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
 		}
 
 	case TMAP:
-		if !eqtype1(t1.Key(), t2.Key(), assumedEqual) {
+		if !eqtype1(t1.Key(), t2.Key(), cmpTags, assumedEqual) {
 			return false
 		}
-		return eqtype1(t1.Val(), t2.Val(), assumedEqual)
+		return eqtype1(t1.Val(), t2.Val(), cmpTags, assumedEqual)
 	}
 
-	return eqtype1(t1.Elem(), t2.Elem(), assumedEqual)
+	return eqtype1(t1.Elem(), t2.Elem(), cmpTags, assumedEqual)
 }
 
 // Are t1 and t2 equal struct types when field names are ignored?
@@ -730,10 +715,10 @@ func eqtypenoname(t1 *Type, t2 *Type) bool {
 		return false
 	}
 
-	f1, i1 := IterFields(t1)
-	f2, i2 := IterFields(t2)
+	f1, i1 := iterFields(t1)
+	f2, i2 := iterFields(t2)
 	for {
-		if !Eqtype(f1.Type, f2.Type) {
+		if !eqtype(f1.Type, f2.Type) {
 			return false
 		}
 		if f1 == nil {
@@ -755,7 +740,7 @@ func assignop(src *Type, dst *Type, why *string) Op {
 	// TODO(rsc,lvd): This behaves poorly in the presence of inlining.
 	// https://golang.org/issue/2795
 	if safemode && importpkg == nil && src != nil && src.Etype == TUNSAFEPTR {
-		Yyerror("cannot use unsafe.Pointer")
+		yyerror("cannot use unsafe.Pointer")
 		errorexit()
 	}
 
@@ -767,7 +752,7 @@ func assignop(src *Type, dst *Type, why *string) Op {
 	}
 
 	// 1. src type is identical to dst.
-	if Eqtype(src, dst) {
+	if eqtype(src, dst) {
 		return OCONVNOP
 	}
 
@@ -776,7 +761,7 @@ func assignop(src *Type, dst *Type, why *string) Op {
 	// both are empty interface types.
 	// For assignable but different non-empty interface types,
 	// we want to recompute the itab.
-	if Eqtype(src.Orig, dst.Orig) && (src.Sym == nil || dst.Sym == nil || src.IsEmptyInterface()) {
+	if eqtype(src.Orig, dst.Orig) && (src.Sym == nil || dst.Sym == nil || src.IsEmptyInterface()) {
 		return OCONVNOP
 	}
 
@@ -799,11 +784,13 @@ func assignop(src *Type, dst *Type, why *string) Op {
 			} else if have != nil && have.Sym == missing.Sym && have.Nointerface {
 				*why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym)
 			} else if have != nil && have.Sym == missing.Sym {
-				*why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", src, dst, missing.Sym, have.Sym, Tconv(have.Type, FmtShort|FmtByte), missing.Sym, Tconv(missing.Type, FmtShort|FmtByte))
+				*why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+
+					"\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
 			} else if ptr != 0 {
 				*why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym)
 			} else if have != nil {
-				*why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", src, dst, missing.Sym, have.Sym, Tconv(have.Type, FmtShort|FmtByte), missing.Sym, Tconv(missing.Type, FmtShort|FmtByte))
+				*why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+
+					"\t\thave %v%0S\n\t\twant %v%0S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
 			} else {
 				*why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym)
 			}
@@ -832,7 +819,7 @@ func assignop(src *Type, dst *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() == Cboth && dst.IsChan() {
-		if Eqtype(src.Elem(), dst.Elem()) && (src.Sym == nil || dst.Sym == nil) {
+		if eqtype(src.Elem(), dst.Elem()) && (src.Sym == nil || dst.Sym == nil) {
 			return OCONVNOP
 		}
 	}
@@ -876,6 +863,16 @@ func convertop(src *Type, dst *Type, why *string) Op {
 		return 0
 	}
 
+	// Conversions from regular to go:notinheap are not allowed
+	// (unless it's unsafe.Pointer). This is a runtime-specific
+	// rule.
+	if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap && !src.Elem().NotInHeap {
+		if why != nil {
+			*why = fmt.Sprintf(":\n\t%v is go:notinheap, but %v is not", dst.Elem(), src.Elem())
+		}
+		return 0
+	}
+
 	// 1. src can be assigned to dst.
 	op := assignop(src, dst, why)
 	if op != 0 {
@@ -893,22 +890,22 @@ func convertop(src *Type, dst *Type, why *string) Op {
 		*why = ""
 	}
 
-	// 2. src and dst have identical underlying types.
-	if Eqtype(src.Orig, dst.Orig) {
+	// 2. Ignoring struct tags, src and dst have identical underlying types.
+	if eqtypeIgnoreTags(src.Orig, dst.Orig) {
 		return OCONVNOP
 	}
 
-	// 3. src and dst are unnamed pointer types
-	// and their base types have identical underlying types.
+	// 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 Eqtype(src.Elem().Orig, dst.Elem().Orig) {
+		if eqtypeIgnoreTags(src.Elem().Orig, dst.Elem().Orig) {
 			return OCONVNOP
 		}
 	}
 
 	// 4. src and dst are both integer or floating point types.
 	if (src.IsInteger() || src.IsFloat()) && (dst.IsInteger() || dst.IsFloat()) {
-		if Simtype[src.Etype] == Simtype[dst.Etype] {
+		if simtype[src.Etype] == simtype[dst.Etype] {
 			return OCONVNOP
 		}
 		return OCONV
@@ -916,7 +913,7 @@ func convertop(src *Type, dst *Type, why *string) Op {
 
 	// 5. src and dst are both complex types.
 	if src.IsComplex() && dst.IsComplex() {
-		if Simtype[src.Etype] == Simtype[dst.Etype] {
+		if simtype[src.Etype] == simtype[dst.Etype] {
 			return OCONVNOP
 		}
 		return OCONV
@@ -972,13 +969,14 @@ func assignconvfn(n *Node, t *Type, context func() string) *Node {
 	}
 
 	if t.Etype == TBLANK && n.Type.Etype == TNIL {
-		Yyerror("use of untyped nil")
+		yyerror("use of untyped nil")
 	}
 
 	old := n
-	old.Diag++ // silence errors about n; we'll issue one below
+	od := old.Diag
+	old.Diag = true // silence errors about n; we'll issue one below
 	n = defaultlit(n, t)
-	old.Diag--
+	old.Diag = od
 	if t.Etype == TBLANK {
 		return n
 	}
@@ -987,7 +985,7 @@ func assignconvfn(n *Node, t *Type, context func() string) *Node {
 	// if the next step is non-bool (like interface{}).
 	if n.Type == idealbool && !t.IsBoolean() {
 		if n.Op == ONAME || n.Op == OLITERAL {
-			r := Nod(OCONVNOP, n, nil)
+			r := nod(OCONVNOP, n, nil)
 			r.Type = Types[TBOOL]
 			r.Typecheck = 1
 			r.Implicit = true
@@ -995,18 +993,18 @@ func assignconvfn(n *Node, t *Type, context func() string) *Node {
 		}
 	}
 
-	if Eqtype(n.Type, t) {
+	if eqtype(n.Type, t) {
 		return n
 	}
 
 	var why string
 	op := assignop(n.Type, t, &why)
 	if op == 0 {
-		Yyerror("cannot use %v as type %v in %s%s", Nconv(n, FmtLong), t, context(), why)
+		yyerror("cannot use %L as type %v in %s%s", n, t, context(), why)
 		op = OCONV
 	}
 
-	r := Nod(op, n, nil)
+	r := nod(op, n, nil)
 	r.Type = t
 	r.Typecheck = 1
 	r.Implicit = true
@@ -1014,38 +1012,28 @@ func assignconvfn(n *Node, t *Type, context func() string) *Node {
 	return r
 }
 
-// Is this a 64-bit type?
-func Is64(t *Type) bool {
-	if t == nil {
-		return false
-	}
-	switch Simtype[t.Etype] {
-	case TINT64, TUINT64, TPTR64:
-		return true
-	}
-
-	return false
+// IsMethod reports whether n is a method.
+// n must be a function or a method.
+func (n *Node) IsMethod() bool {
+	return n.Type.Recv() != nil
 }
 
 // SliceBounds returns n's slice bounds: low, high, and max in expr[low:high:max].
 // n must be a slice expression. max is nil if n is a simple slice expression.
 func (n *Node) SliceBounds() (low, high, max *Node) {
+	if n.List.Len() == 0 {
+		return nil, nil, nil
+	}
+
 	switch n.Op {
 	case OSLICE, OSLICEARR, OSLICESTR:
-		if n.Right == nil {
-			return nil, nil, nil
-		}
-		if n.Right.Op != OKEY {
-			Fatalf("SliceBounds right %s", opnames[n.Right.Op])
-		}
-		return n.Right.Left, n.Right.Right, nil
+		s := n.List.Slice()
+		return s[0], s[1], nil
 	case OSLICE3, OSLICE3ARR:
-		if n.Right.Op != OKEY || n.Right.Right.Op != OKEY {
-			Fatalf("SliceBounds right %s %s", opnames[n.Right.Op], opnames[n.Right.Right.Op])
-		}
-		return n.Right.Left, n.Right.Right.Left, n.Right.Right.Right
+		s := n.List.Slice()
+		return s[0], s[1], s[2]
 	}
-	Fatalf("SliceBounds op %s: %v", n.Op, n)
+	Fatalf("SliceBounds op %v: %v", n.Op, n)
 	return nil, nil, nil
 }
 
@@ -1055,25 +1043,34 @@ func (n *Node) SetSliceBounds(low, high, max *Node) {
 	switch n.Op {
 	case OSLICE, OSLICEARR, OSLICESTR:
 		if max != nil {
-			Fatalf("SetSliceBounds %s given three bounds", n.Op)
+			Fatalf("SetSliceBounds %v given three bounds", n.Op)
 		}
-		if n.Right == nil {
-			n.Right = Nod(OKEY, low, high)
+		s := n.List.Slice()
+		if s == nil {
+			if low == nil && high == nil {
+				return
+			}
+			n.List.Set([]*Node{low, high})
 			return
 		}
-		n.Right.Left = low
-		n.Right.Right = high
+		s[0] = low
+		s[1] = high
 		return
 	case OSLICE3, OSLICE3ARR:
-		if n.Right == nil {
-			n.Right = Nod(OKEY, low, Nod(OKEY, high, max))
+		s := n.List.Slice()
+		if s == nil {
+			if low == nil && high == nil && max == nil {
+				return
+			}
+			n.List.Set([]*Node{low, high, max})
+			return
 		}
-		n.Right.Left = low
-		n.Right.Right.Left = high
-		n.Right.Right.Right = max
+		s[0] = low
+		s[1] = high
+		s[2] = max
 		return
 	}
-	Fatalf("SetSliceBounds op %s: %v", n.Op, n)
+	Fatalf("SetSliceBounds op %v: %v", n.Op, n)
 }
 
 // IsSlice3 reports whether o is a slice3 op (OSLICE3, OSLICE3ARR).
@@ -1089,34 +1086,6 @@ func (o Op) IsSlice3() bool {
 	return false
 }
 
-// Is a conversion between t1 and t2 a no-op?
-func Noconv(t1 *Type, t2 *Type) bool {
-	e1 := Simtype[t1.Etype]
-	e2 := Simtype[t2.Etype]
-
-	switch e1 {
-	case TINT8, TUINT8:
-		return e2 == TINT8 || e2 == TUINT8
-
-	case TINT16, TUINT16:
-		return e2 == TINT16 || e2 == TUINT16
-
-	case TINT32, TUINT32, TPTR32:
-		return e2 == TINT32 || e2 == TUINT32 || e2 == TPTR32
-
-	case TINT64, TUINT64, TPTR64:
-		return e2 == TINT64 || e2 == TUINT64 || e2 == TPTR64
-
-	case TFLOAT32:
-		return e2 == TFLOAT32
-
-	case TFLOAT64:
-		return e2 == TFLOAT64
-	}
-
-	return false
-}
-
 func syslook(name string) *Node {
 	s := Pkglookup(name, Runtimepkg)
 	if s == nil || s.Def == nil {
@@ -1128,56 +1097,25 @@ func syslook(name string) *Node {
 // typehash computes a hash value for type t to use in type switch
 // statements.
 func typehash(t *Type) uint32 {
-	// Tconv already contains all the necessary logic to generate
-	// a representation that completely describes the type, so using
+	// t.tconv(FmtLeft | FmtUnsigned) already contains all the necessary logic
+	// to generate a representation that completely describes the type, so using
 	// it here avoids duplicating that code.
-	p := Tconv(t, FmtLeft|FmtUnsigned)
+	// See the comments in exprSwitch.checkDupCases.
+	p := t.tconv(FmtLeft | FmtUnsigned)
 
 	// Using MD5 is overkill, but reduces accidental collisions.
 	h := md5.Sum([]byte(p))
 	return binary.LittleEndian.Uint32(h[:4])
 }
 
-var initPtrtoDone bool
-
-var (
-	ptrToUint8  *Type
-	ptrToAny    *Type
-	ptrToString *Type
-	ptrToBool   *Type
-	ptrToInt32  *Type
-)
-
-func initPtrto() {
-	ptrToUint8 = typPtr(Types[TUINT8])
-	ptrToAny = typPtr(Types[TANY])
-	ptrToString = typPtr(Types[TSTRING])
-	ptrToBool = typPtr(Types[TBOOL])
-	ptrToInt32 = typPtr(Types[TINT32])
-}
-
-// Ptrto returns the Type *t.
+// ptrto returns the Type *t.
 // The returned struct must not be modified.
-func Ptrto(t *Type) *Type {
+func ptrto(t *Type) *Type {
 	if Tptr == 0 {
 		Fatalf("ptrto: no tptr")
 	}
-	// Reduce allocations by pre-creating common cases.
-	if !initPtrtoDone {
-		initPtrto()
-		initPtrtoDone = true
-	}
-	switch t {
-	case Types[TUINT8]:
-		return ptrToUint8
-	case Types[TINT32]:
-		return ptrToInt32
-	case Types[TANY]:
-		return ptrToAny
-	case Types[TSTRING]:
-		return ptrToString
-	case Types[TBOOL]:
-		return ptrToBool
+	if t == nil {
+		Fatalf("ptrto: nil ptr")
 	}
 	return typPtr(t)
 }
@@ -1229,7 +1167,7 @@ func ullmancalc(n *Node) {
 	}
 
 	switch n.Op {
-	case OREGISTER, OLITERAL, ONAME:
+	case OLITERAL, ONAME:
 		ul = 1
 		if n.Class == PAUTOHEAP {
 			ul++
@@ -1246,6 +1184,12 @@ func ullmancalc(n *Node) {
 			ul = UINF
 			goto out
 		}
+	case OINDEX, OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR,
+		OIND, ODOTPTR, ODOTTYPE, ODIV, OMOD:
+		// These ops might panic, make sure they are done
+		// before we start marshaling args for a call. See issue 16760.
+		ul = UINF
+		goto out
 	}
 
 	ul = 1
@@ -1289,12 +1233,12 @@ func badtype(op Op, tl *Type, tr *Type) {
 	}
 
 	s := fmt_
-	Yyerror("illegal types for operand: %v%s", op, s)
+	yyerror("illegal types for operand: %v%s", op, s)
 }
 
-// Brcom returns !(op).
-// For example, Brcom(==) is !=.
-func Brcom(op Op) Op {
+// brcom returns !(op).
+// For example, brcom(==) is !=.
+func brcom(op Op) Op {
 	switch op {
 	case OEQ:
 		return ONE
@@ -1313,9 +1257,9 @@ func Brcom(op Op) Op {
 	return op
 }
 
-// Brrev returns reverse(op).
+// brrev returns reverse(op).
 // For example, Brrev(<) is >.
-func Brrev(op Op) Op {
+func brrev(op Op) Op {
 	switch op {
 	case OEQ:
 		return OEQ
@@ -1355,7 +1299,7 @@ func safeexpr(n *Node, init *Nodes) *Node {
 		if l == n.Left {
 			return n
 		}
-		r := Nod(OXXX, nil, nil)
+		r := nod(OXXX, nil, nil)
 		*r = *n
 		r.Left = l
 		r = typecheck(r, Erv)
@@ -1367,7 +1311,7 @@ func safeexpr(n *Node, init *Nodes) *Node {
 		if l == n.Left {
 			return n
 		}
-		a := Nod(OXXX, nil, nil)
+		a := nod(OXXX, nil, nil)
 		*a = *n
 		a.Left = l
 		a = walkexpr(a, init)
@@ -1379,14 +1323,14 @@ func safeexpr(n *Node, init *Nodes) *Node {
 		if l == n.Left && r == n.Right {
 			return n
 		}
-		a := Nod(OXXX, nil, nil)
+		a := nod(OXXX, nil, nil)
 		*a = *n
 		a.Left = l
 		a.Right = r
 		a = walkexpr(a, init)
 		return a
 
-	case OSTRUCTLIT, OARRAYLIT:
+	case OSTRUCTLIT, OARRAYLIT, OSLICELIT:
 		if isStaticCompositeLiteral(n) {
 			return n
 		}
@@ -1401,7 +1345,7 @@ func safeexpr(n *Node, init *Nodes) *Node {
 
 func copyexpr(n *Node, t *Type, init *Nodes) *Node {
 	l := temp(t)
-	a := Nod(OAS, l, n)
+	a := nod(OAS, l, n)
 	a = typecheck(a, Etop)
 	a = walkexpr(a, init)
 	init.Append(a)
@@ -1419,21 +1363,6 @@ func cheapexpr(n *Node, init *Nodes) *Node {
 	return copyexpr(n, n.Type, init)
 }
 
-func Setmaxarg(t *Type, extra int32) {
-	dowidth(t)
-	w := t.ArgWidth()
-	if w >= Thearch.MAXWIDTH {
-		Fatalf("bad argwid %v", t)
-	}
-	w += int64(extra)
-	if w >= Thearch.MAXWIDTH {
-		Fatalf("bad argwid %d + %v", extra, t)
-	}
-	if w > Maxarg {
-		Maxarg = w
-	}
-}
-
 // Code to resolve elided DOTs in embedded types.
 
 // A Dlist stores a pointer to a TFIELD Type embedded within
@@ -1468,7 +1397,7 @@ func lookdot0(s *Sym, t *Type, save **Field, ignorecase bool) int {
 		}
 	}
 
-	u = methtype(t, 0)
+	u = methtype(t)
 	if u != nil {
 		for _, f := range u.Methods().Slice() {
 			if f.Embedded == 0 && (f.Sym == s || (ignorecase && strings.EqualFold(f.Sym.Name, s.Name))) {
@@ -1570,7 +1499,9 @@ func dotpath(s *Sym, t *Type, save **Field, ignorecase bool) (path []Dlist, ambi
 // modify the tree with missing type names.
 func adddot(n *Node) *Node {
 	n.Left = typecheck(n.Left, Etype|Erv)
-	n.Diag |= n.Left.Diag
+	if n.Left.Diag {
+		n.Diag = true
+	}
 	t := n.Left.Type
 	if t == nil {
 		return n
@@ -1589,11 +1520,11 @@ func adddot(n *Node) *Node {
 	case path != nil:
 		// rebuild elided dots
 		for c := len(path) - 1; c >= 0; c-- {
-			n.Left = NodSym(ODOT, n.Left, path[c].field.Sym)
+			n.Left = nodSym(ODOT, n.Left, path[c].field.Sym)
 			n.Left.Implicit = true
 		}
 	case ambig:
-		Yyerror("ambiguous selector %v", n)
+		yyerror("ambiguous selector %v", n)
 		n.Left = nil
 	}
 
@@ -1634,7 +1565,7 @@ func expand0(t *Type, followptr bool) {
 		return
 	}
 
-	u = methtype(t, 0)
+	u = methtype(t)
 	if u != nil {
 		for _, f := range u.Methods().Slice() {
 			if f.Sym.Flags&SymUniq != 0 {
@@ -1738,11 +1669,11 @@ func structargs(tl *Type, mustname bool) []*Node {
 			// invent a name so that we can refer to it in the trampoline
 			buf := fmt.Sprintf(".anon%d", gen)
 			gen++
-			n = newname(Lookup(buf))
+			n = newname(lookup(buf))
 		} else if t.Sym != nil {
 			n = newname(t.Sym)
 		}
-		a := Nod(ODCLFIELD, n, typenod(t.Type))
+		a := nod(ODCLFIELD, n, typenod(t.Type))
 		a.Isddd = t.Isddd
 		if n != nil {
 			n.Isddd = t.Isddd
@@ -1794,12 +1725,12 @@ func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) {
 	dclcontext = PEXTERN
 	markdcl()
 
-	this := Nod(ODCLFIELD, newname(Lookup(".this")), typenod(rcvr))
+	this := nod(ODCLFIELD, newname(lookup(".this")), typenod(rcvr))
 	this.Left.Name.Param.Ntype = this.Right
 	in := structargs(method.Type.Params(), true)
 	out := structargs(method.Type.Results(), false)
 
-	t := Nod(OTFUNC, nil, nil)
+	t := nod(OTFUNC, nil, nil)
 	l := []*Node{this}
 	if iface != 0 && rcvr.Width < Types[Tptr].Width {
 		// Building method for interface table and receiver
@@ -1808,14 +1739,14 @@ func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) {
 		// Add a dummy padding argument after the
 		// receiver to make up the difference.
 		tpad := typArray(Types[TUINT8], Types[Tptr].Width-rcvr.Width)
-		pad := Nod(ODCLFIELD, newname(Lookup(".pad")), typenod(tpad))
+		pad := nod(ODCLFIELD, newname(lookup(".pad")), typenod(tpad))
 		l = append(l, pad)
 	}
 
 	t.List.Set(append(l, in...))
 	t.Rlist.Set(out)
 
-	fn := Nod(ODCLFUNC, nil, nil)
+	fn := nod(ODCLFUNC, nil, nil)
 	fn.Func.Nname = newname(newnam)
 	fn.Func.Nname.Name.Defn = fn
 	fn.Func.Nname.Name.Param.Ntype = t
@@ -1836,9 +1767,9 @@ func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) {
 	// generate nil pointer check for better error
 	if rcvr.IsPtr() && rcvr.Elem() == methodrcvr {
 		// generating wrapper from *T to T.
-		n := Nod(OIF, nil, nil)
+		n := nod(OIF, nil, nil)
 
-		n.Left = Nod(OEQ, this.Left, nodnil())
+		n.Left = nod(OEQ, this.Left, nodnil())
 
 		// these strings are already in the reflect tables,
 		// so no space cost to use them here.
@@ -1851,13 +1782,13 @@ func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) {
 		l = append(l, nodlit(v))
 		v.U = method.Sym.Name
 		l = append(l, nodlit(v)) // method name
-		call := Nod(OCALL, syslook("panicwrap"), nil)
+		call := nod(OCALL, syslook("panicwrap"), nil)
 		call.List.Set(l)
 		n.Nbody.Set1(call)
 		fn.Nbody.Append(n)
 	}
 
-	dot := adddot(NodSym(OXDOT, this.Left, method.Sym))
+	dot := adddot(nodSym(OXDOT, this.Left, method.Sym))
 
 	// generate call
 	// It's not possible to use a tail call when dynamic linking on ppc64le. The
@@ -1871,21 +1802,21 @@ func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) {
 		dot = dot.Left // skip final .M
 		// TODO(mdempsky): Remove dependency on dotlist.
 		if !dotlist[0].field.Type.IsPtr() {
-			dot = Nod(OADDR, dot, nil)
+			dot = nod(OADDR, dot, nil)
 		}
-		as := Nod(OAS, this.Left, Nod(OCONVNOP, dot, nil))
+		as := nod(OAS, this.Left, nod(OCONVNOP, dot, nil))
 		as.Right.Type = rcvr
 		fn.Nbody.Append(as)
-		n := Nod(ORETJMP, nil, nil)
+		n := nod(ORETJMP, nil, nil)
 		n.Left = newname(methodsym(method.Sym, methodrcvr, 0))
 		fn.Nbody.Append(n)
 	} else {
 		fn.Func.Wrapper = true // ignore frame for panic+recover matching
-		call := Nod(OCALL, dot, nil)
+		call := nod(OCALL, dot, nil)
 		call.List.Set(args)
 		call.Isddd = isddd
 		if method.Type.Results().NumFields() > 0 {
-			n := Nod(ORETURN, nil, nil)
+			n := nod(ORETURN, nil, nil)
 			n.List.Set1(call)
 			call = n
 		}
@@ -1921,11 +1852,11 @@ func hashmem(t *Type) *Node {
 
 	n := newname(sym)
 	n.Class = PFUNC
-	tfn := Nod(OTFUNC, nil, nil)
-	tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
-	tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
-	tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
-	tfn.Rlist.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
+	tfn := nod(OTFUNC, nil, nil)
+	tfn.List.Append(nod(ODCLFIELD, nil, typenod(ptrto(t))))
+	tfn.List.Append(nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
+	tfn.List.Append(nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
+	tfn.Rlist.Append(nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
 	tfn = typecheck(tfn, Etype)
 	n.Type = tfn.Type
 	return n
@@ -1942,7 +1873,7 @@ func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase bool) *Field {
 	path, ambig := dotpath(s, t, &m, ignorecase)
 	if path == nil {
 		if ambig {
-			Yyerror("%v.%v is ambiguous", t, s)
+			yyerror("%v.%v is ambiguous", t, s)
 		}
 		return nil
 	}
@@ -1955,7 +1886,7 @@ func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase bool) *Field {
 	}
 
 	if m.Type.Etype != TFUNC || m.Type.Recv() == nil {
-		Yyerror("%v.%v is a field, not a method", t, s)
+		yyerror("%v.%v is a field, not a method", t, s)
 		return nil
 	}
 
@@ -1976,7 +1907,7 @@ func implements(t, iface *Type, m, samename **Field, ptr *int) bool {
 		for _, im := range iface.Fields().Slice() {
 			for _, tm := range t.Fields().Slice() {
 				if tm.Sym == im.Sym {
-					if Eqtype(tm.Type, im.Type) {
+					if eqtype(tm.Type, im.Type) {
 						goto found
 					}
 					*m = im
@@ -1996,7 +1927,7 @@ func implements(t, iface *Type, m, samename **Field, ptr *int) bool {
 		return true
 	}
 
-	t = methtype(t, 0)
+	t = methtype(t)
 	if t != nil {
 		expandmeth(t)
 	}
@@ -2006,7 +1937,7 @@ func implements(t, iface *Type, m, samename **Field, ptr *int) bool {
 		}
 		var followptr bool
 		tm := ifacelookdot(im.Sym, t, &followptr, false)
-		if tm == nil || tm.Nointerface || !Eqtype(tm.Type, im.Type) {
+		if tm == nil || tm.Nointerface || !eqtype(tm.Type, im.Type) {
 			if tm == nil {
 				tm = ifacelookdot(im.Sym, t, &followptr, true)
 			}
@@ -2022,7 +1953,7 @@ func implements(t, iface *Type, m, samename **Field, ptr *int) bool {
 
 		if rcvr.IsPtr() && !t0.IsPtr() && !followptr && !isifacemethod(tm.Type) {
 			if false && Debug['r'] != 0 {
-				Yyerror("interface pointer mismatch")
+				yyerror("interface pointer mismatch")
 			}
 
 			*m = im
@@ -2043,7 +1974,7 @@ func Simsimtype(t *Type) EType {
 		return 0
 	}
 
-	et := Simtype[t.Etype]
+	et := simtype[t.Etype]
 	switch et {
 	case TPTR32:
 		et = TUINT32
@@ -2067,7 +1998,7 @@ func listtreecopy(l []*Node, lineno int32) []*Node {
 }
 
 func liststmt(l []*Node) *Node {
-	n := Nod(OBLOCK, nil, nil)
+	n := nod(OBLOCK, nil, nil)
 	n.List.Set(l)
 	if len(l) != 0 {
 		n.Lineno = l[0].Lineno
@@ -2111,37 +2042,6 @@ func powtwo(n *Node) int {
 	return -1
 }
 
-// return the unsigned type for
-// a signed integer type.
-// returns T if input is not a
-// signed integer type.
-func tounsigned(t *Type) *Type {
-	// this is types[et+1], but not sure
-	// that this relation is immutable
-	switch t.Etype {
-	default:
-		fmt.Printf("tounsigned: unknown type %v\n", t)
-		t = nil
-
-	case TINT:
-		t = Types[TUINT]
-
-	case TINT8:
-		t = Types[TUINT8]
-
-	case TINT16:
-		t = Types[TUINT16]
-
-	case TINT32:
-		t = Types[TUINT32]
-
-	case TINT64:
-		t = Types[TUINT64]
-	}
-
-	return t
-}
-
 func ngotype(n *Node) *Sym {
 	if n.Type != nil {
 		return typenamesym(n.Type)
@@ -2204,12 +2104,12 @@ func addinit(n *Node, init []*Node) *Node {
 	// There may be multiple refs to this node;
 	// introduce OCONVNOP to hold init list.
 	case ONAME, OLITERAL:
-		n = Nod(OCONVNOP, n, nil)
+		n = nod(OCONVNOP, n, nil)
 		n.Type = n.Left.Type
 		n.Typecheck = 1
 	}
 
-	n.Ninit.Set(append(init, n.Ninit.Slice()...))
+	n.Ninit.Prepend(init...)
 	n.Ullman = UINF
 	return n
 }
@@ -2221,40 +2121,40 @@ var reservedimports = []string{
 
 func isbadimport(path string) bool {
 	if strings.Contains(path, "\x00") {
-		Yyerror("import path contains NUL")
+		yyerror("import path contains NUL")
 		return true
 	}
 
 	for _, ri := range reservedimports {
 		if path == ri {
-			Yyerror("import path %q is reserved and cannot be used", path)
+			yyerror("import path %q is reserved and cannot be used", path)
 			return true
 		}
 	}
 
 	for _, r := range path {
 		if r == utf8.RuneError {
-			Yyerror("import path contains invalid UTF-8 sequence: %q", path)
+			yyerror("import path contains invalid UTF-8 sequence: %q", path)
 			return true
 		}
 
 		if r < 0x20 || r == 0x7f {
-			Yyerror("import path contains control character: %q", path)
+			yyerror("import path contains control character: %q", path)
 			return true
 		}
 
 		if r == '\\' {
-			Yyerror("import path contains backslash; use slash: %q", path)
+			yyerror("import path contains backslash; use slash: %q", path)
 			return true
 		}
 
 		if unicode.IsSpace(r) {
-			Yyerror("import path contains space character: %q", path)
+			yyerror("import path contains space character: %q", path)
 			return true
 		}
 
 		if strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r) {
-			Yyerror("import path contains invalid character '%c': %q", r, path)
+			yyerror("import path contains invalid character '%c': %q", r, path)
 			return true
 		}
 	}
@@ -2265,11 +2165,11 @@ func isbadimport(path string) bool {
 func checknil(x *Node, init *Nodes) {
 	x = walkexpr(x, nil) // caller has not done this yet
 	if x.Type.IsInterface() {
-		x = Nod(OITAB, x, nil)
+		x = nod(OITAB, x, nil)
 		x = typecheck(x, Erv)
 	}
 
-	n := Nod(OCHECKNIL, x, nil)
+	n := nod(OCHECKNIL, x, nil)
 	n.Typecheck = 1
 	init.Append(n)
 }
@@ -2298,6 +2198,35 @@ func isdirectiface(t *Type) bool {
 	return false
 }
 
+// itabType loads the _type field from a runtime.itab struct.
+func itabType(itab *Node) *Node {
+	typ := nodSym(ODOTPTR, itab, nil)
+	typ.Type = ptrto(Types[TUINT8])
+	typ.Typecheck = 1
+	typ.Xoffset = int64(Widthptr) // offset of _type in runtime.itab
+	typ.Bounded = true            // guaranteed not to fault
+	return typ
+}
+
+// ifaceData loads the data field from an interface.
+// The concrete type must be known to have type t.
+// It follows the pointer if !isdirectiface(t).
+func ifaceData(n *Node, t *Type) *Node {
+	ptr := nodSym(OIDATA, n, nil)
+	if isdirectiface(t) {
+		ptr.Type = t
+		ptr.Typecheck = 1
+		return ptr
+	}
+	ptr.Type = ptrto(t)
+	ptr.Bounded = true
+	ptr.Typecheck = 1
+	ind := nod(OIND, ptr, nil)
+	ind.Type = t
+	ind.Typecheck = 1
+	return ind
+}
+
 // iet returns 'T' if t is a concrete type,
 // 'I' if t is an interface type, and 'E' if t is an empty interface type.
 // It is used to build calls to the conv* and assert* runtime routines.
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
index 4940c97..3d3496d 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -4,36 +4,20 @@
 
 package gc
 
-import (
-	"sort"
-	"strconv"
-)
+import "sort"
 
 const (
 	// expression switch
 	switchKindExpr  = iota // switch a {...} or switch 5 {...}
 	switchKindTrue         // switch true {...} or switch {...}
 	switchKindFalse        // switch false {...}
-
-	// type switch
-	switchKindType // switch a.(type) {...}
 )
 
 const (
-	caseKindDefault = iota // default:
-
-	// expression switch
-	caseKindExprConst // case 5:
-	caseKindExprVar   // case x:
-
-	// type switch
-	caseKindTypeNil   // case nil:
-	caseKindTypeConst // case time.Time: (concrete type, has type hash)
-	caseKindTypeVar   // case io.Reader: (interface type)
+	binarySearchMin = 4 // minimum number of cases for binary search
+	integerRangeMin = 2 // minimum size of integer ranges
 )
 
-const binarySearchMin = 4 // minimum number of cases for binary search
-
 // An exprSwitch walks an expression switch.
 type exprSwitch struct {
 	exprname *Node // node for the expression being switched on
@@ -52,7 +36,18 @@ type caseClause struct {
 	node    *Node  // points at case statement
 	ordinal int    // position in switch
 	hash    uint32 // hash of a type switch
-	typ     uint8  // type of case
+	// isconst indicates whether this case clause is a constant,
+	// for the purposes of the switch code generation.
+	// For expression switches, that's generally literals (case 5:, not case x:).
+	// For type switches, that's concrete types (case time.Time:), not interfaces (case io.Reader:).
+	isconst bool
+}
+
+// caseClauses are all the case clauses in a switch statement.
+type caseClauses struct {
+	list   []caseClause // general cases
+	defjmp *Node        // OGOTO for default case or OBREAK if no default case present
+	niljmp *Node        // OGOTO for nil type case in a type switch
 }
 
 // typecheckswitch typechecks a switch statement.
@@ -70,7 +65,7 @@ func typecheckswitch(n *Node) {
 		n.Left.Right = typecheck(n.Left.Right, Erv)
 		t = n.Left.Right.Type
 		if t != nil && !t.IsInterface() {
-			Yyerror("cannot type switch on non-interface value %v", Nconv(n.Left.Right, FmtLong))
+			yyerror("cannot type switch on non-interface value %L", n.Left.Right)
 		}
 	} else {
 		// expression switch
@@ -85,14 +80,14 @@ func typecheckswitch(n *Node) {
 		if t != nil {
 			switch {
 			case !okforeq[t.Etype]:
-				Yyerror("cannot switch on %v", Nconv(n.Left, FmtLong))
+				yyerror("cannot switch on %L", n.Left)
 			case t.IsSlice():
 				nilonly = "slice"
 			case t.IsArray() && !t.IsComparable():
-				Yyerror("cannot switch on %v", Nconv(n.Left, FmtLong))
+				yyerror("cannot switch on %L", n.Left)
 			case t.IsStruct():
 				if f := t.IncomparableField(); f != nil {
-					Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Left, FmtLong), f.Type)
+					yyerror("cannot switch on %L (struct containing %v cannot be compared)", n.Left, f.Type)
 				}
 			case t.Etype == TFUNC:
 				nilonly = "func"
@@ -110,7 +105,8 @@ func typecheckswitch(n *Node) {
 		if ncase.List.Len() == 0 {
 			// default
 			if def != nil {
-				Yyerror("multiple defaults in switch (first at %v)", def.Line())
+				setlineno(ncase)
+				yyerror("multiple defaults in switch (first at %v)", def.Line())
 			} else {
 				def = ncase
 			}
@@ -131,17 +127,17 @@ func typecheckswitch(n *Node) {
 					n1 = ls[i1]
 					switch {
 					case n1.Op == OTYPE:
-						Yyerror("type %v is not an expression", n1.Type)
+						yyerror("type %v is not an expression", n1.Type)
 					case n1.Type != nil && assignop(n1.Type, t, nil) == 0 && assignop(t, n1.Type, nil) == 0:
 						if n.Left != nil {
-							Yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t)
+							yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t)
 						} else {
-							Yyerror("invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type)
+							yyerror("invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type)
 						}
 					case nilonly != "" && !isnil(n1):
-						Yyerror("invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
+						yyerror("invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
 					case t.IsInterface() && !n1.Type.IsInterface() && !n1.Type.IsComparable():
-						Yyerror("invalid case %v in switch (incomparable type)", Nconv(n1, FmtLong))
+						yyerror("invalid case %L in switch (incomparable type)", n1)
 					}
 
 				// type switch
@@ -152,20 +148,22 @@ func typecheckswitch(n *Node) {
 					case n1.Op == OLITERAL && n1.Type.IsKind(TNIL):
 						// case nil:
 						if niltype != nil {
-							Yyerror("multiple nil cases in type switch (first at %v)", niltype.Line())
+							yyerror("multiple nil cases in type switch (first at %v)", niltype.Line())
 						} else {
 							niltype = ncase
 						}
 					case n1.Op != OTYPE && n1.Type != nil: // should this be ||?
-						Yyerror("%v is not a type", Nconv(n1, FmtLong))
+						yyerror("%L is not a type", n1)
 						// reset to original type
 						n1 = n.Left.Right
 						ls[i1] = n1
 					case !n1.Type.IsInterface() && t.IsInterface() && !implements(n1.Type, t, &missing, &have, &ptr):
 						if have != nil && !missing.Broke && !have.Broke {
-							Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (wrong type for %v method)\n\thave %v%v\n\twant %v%v", Nconv(n.Left.Right, FmtLong), n1.Type, missing.Sym, have.Sym, Tconv(have.Type, FmtShort), missing.Sym, Tconv(missing.Type, FmtShort))
+							yyerror("impossible type switch case: %L cannot have dynamic type %v"+
+								" (wrong type for %v method)\n\thave %v%S\n\twant %v%S", n.Left.Right, n1.Type, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
 						} else if !missing.Broke {
-							Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (missing %v method)", Nconv(n.Left.Right, FmtLong), n1.Type, missing.Sym)
+							yyerror("impossible type switch case: %L cannot have dynamic type %v"+
+								" (missing %v method)", n.Left.Right, n1.Type, missing.Sym)
 						}
 					}
 				}
@@ -199,7 +197,7 @@ func typecheckswitch(n *Node) {
 func walkswitch(sw *Node) {
 	// convert switch {...} to switch true {...}
 	if sw.Left == nil {
-		sw.Left = Nodbool(true)
+		sw.Left = nodbool(true)
 		sw.Left = typecheck(sw.Left, Erv)
 	}
 
@@ -240,31 +238,25 @@ func (s *exprSwitch) walk(sw *Node) {
 	// convert the switch into OIF statements
 	var cas []*Node
 	if s.kind == switchKindTrue || s.kind == switchKindFalse {
-		s.exprname = Nodbool(s.kind == switchKindTrue)
+		s.exprname = nodbool(s.kind == switchKindTrue)
 	} else if consttype(cond) >= 0 {
 		// leave constants to enable dead code elimination (issue 9608)
 		s.exprname = cond
 	} else {
 		s.exprname = temp(cond.Type)
-		cas = []*Node{Nod(OAS, s.exprname, cond)}
+		cas = []*Node{nod(OAS, s.exprname, cond)}
 		typecheckslice(cas, Etop)
 	}
 
-	// enumerate the cases, and lop off the default case
-	cc := caseClauses(sw, s.kind)
+	// Enumerate the cases and prepare the default case.
+	clauses := s.genCaseClauses(sw.List.Slice())
 	sw.List.Set(nil)
-	var def *Node
-	if len(cc) > 0 && cc[0].typ == caseKindDefault {
-		def = cc[0].node.Right
-		cc = cc[1:]
-	} else {
-		def = Nod(OBREAK, nil, nil)
-	}
+	cc := clauses.list
 
 	// handle the cases in order
 	for len(cc) > 0 {
 		// deal with expressions one at a time
-		if !okforcmp[t.Etype] || cc[0].typ != caseKindExprConst {
+		if !okforcmp[t.Etype] || !cc[0].isconst {
 			a := s.walkCases(cc[:1])
 			cas = append(cas, a)
 			cc = cc[1:]
@@ -273,11 +265,11 @@ func (s *exprSwitch) walk(sw *Node) {
 
 		// do binary search on runs of constants
 		var run int
-		for run = 1; run < len(cc) && cc[run].typ == caseKindExprConst; run++ {
+		for run = 1; run < len(cc) && cc[run].isconst; run++ {
 		}
 
 		// sort and compile constants
-		sort.Sort(caseClauseByExpr(cc[:run]))
+		sort.Sort(caseClauseByConstVal(cc[:run]))
 		a := s.walkCases(cc[:run])
 		cas = append(cas, a)
 		cc = cc[run:]
@@ -285,14 +277,14 @@ func (s *exprSwitch) walk(sw *Node) {
 
 	// handle default case
 	if nerrors == 0 {
-		cas = append(cas, def)
-		sw.Nbody.Set(append(cas, sw.Nbody.Slice()...))
+		cas = append(cas, clauses.defjmp)
+		sw.Nbody.Prepend(cas...)
 		walkstmtlist(sw.Nbody.Slice())
 	}
 }
 
 // walkCases generates an AST implementing the cases in cc.
-func (s *exprSwitch) walkCases(cc []*caseClause) *Node {
+func (s *exprSwitch) walkCases(cc []caseClause) *Node {
 	if len(cc) < binarySearchMin {
 		// linear search
 		var cas []*Node
@@ -300,15 +292,26 @@ func (s *exprSwitch) walkCases(cc []*caseClause) *Node {
 			n := c.node
 			lno := setlineno(n)
 
-			a := Nod(OIF, nil, nil)
-			if (s.kind != switchKindTrue && s.kind != switchKindFalse) || assignop(n.Left.Type, s.exprname.Type, nil) == OCONVIFACE || assignop(s.exprname.Type, n.Left.Type, nil) == OCONVIFACE {
-				a.Left = Nod(OEQ, s.exprname, n.Left) // if name == val
+			a := nod(OIF, nil, nil)
+			if rng := n.List.Slice(); rng != nil {
+				// Integer range.
+				// exprname is a temp or a constant,
+				// so it is safe to evaluate twice.
+				// In most cases, this conjunction will be
+				// rewritten by walkinrange into a single comparison.
+				low := nod(OGE, s.exprname, rng[0])
+				high := nod(OLE, s.exprname, rng[1])
+				a.Left = nod(OANDAND, low, high)
+				a.Left = typecheck(a.Left, Erv)
+				a.Left = walkexpr(a.Left, nil) // give walk the opportunity to optimize the range check
+			} else if (s.kind != switchKindTrue && s.kind != switchKindFalse) || assignop(n.Left.Type, s.exprname.Type, nil) == OCONVIFACE || assignop(s.exprname.Type, n.Left.Type, nil) == OCONVIFACE {
+				a.Left = nod(OEQ, s.exprname, n.Left) // if name == val
 				a.Left = typecheck(a.Left, Erv)
 			} else if s.kind == switchKindTrue {
 				a.Left = n.Left // if val
 			} else {
 				// s.kind == switchKindFalse
-				a.Left = Nod(ONOT, n.Left, nil) // if !val
+				a.Left = nod(ONOT, n.Left, nil) // if !val
 				a.Left = typecheck(a.Left, Erv)
 			}
 			a.Nbody.Set1(n.Right) // goto l
@@ -321,14 +324,20 @@ func (s *exprSwitch) walkCases(cc []*caseClause) *Node {
 
 	// find the middle and recur
 	half := len(cc) / 2
-	a := Nod(OIF, nil, nil)
-	mid := cc[half-1].node.Left
-	le := Nod(OLE, s.exprname, mid)
+	a := nod(OIF, nil, nil)
+	n := cc[half-1].node
+	var mid *Node
+	if rng := n.List.Slice(); rng != nil {
+		mid = rng[1] // high end of range
+	} else {
+		mid = n.Left
+	}
+	le := nod(OLE, s.exprname, mid)
 	if Isconst(mid, CTSTR) {
-		// Search by length and then by value; see exprcmp.
-		lenlt := Nod(OLT, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil))
-		leneq := Nod(OEQ, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil))
-		a.Left = Nod(OOROR, lenlt, Nod(OANDAND, leneq, le))
+		// Search by length and then by value; see caseClauseByConstVal.
+		lenlt := nod(OLT, nod(OLEN, s.exprname, nil), nod(OLEN, mid, nil))
+		leneq := nod(OEQ, nod(OLEN, s.exprname, nil), nod(OLEN, mid, nil))
+		a.Left = nod(OOROR, lenlt, nod(OANDAND, leneq, le))
 	} else {
 		a.Left = le
 	}
@@ -351,7 +360,7 @@ func casebody(sw *Node, typeswvar *Node) {
 	var cas []*Node  // cases
 	var stat []*Node // statements
 	var def *Node    // defaults
-	br := Nod(OBREAK, nil, nil)
+	br := nod(OBREAK, nil, nil)
 
 	for i, n := range sw.List.Slice() {
 		setlineno(n)
@@ -361,51 +370,103 @@ func casebody(sw *Node, typeswvar *Node) {
 		n.Op = OCASE
 		needvar := n.List.Len() != 1 || n.List.First().Op == OLITERAL
 
-		jmp := Nod(OGOTO, newCaseLabel(), nil)
-		if n.List.Len() == 0 {
+		jmp := nod(OGOTO, autolabel(".s"), nil)
+		switch n.List.Len() {
+		case 0:
+			// default
 			if def != nil {
-				Yyerror("more than one default case")
+				yyerror("more than one default case")
 			}
 			// reuse original default case
 			n.Right = jmp
 			def = n
-		}
-
-		if n.List.Len() == 1 {
+		case 1:
 			// one case -- reuse OCASE node
 			n.Left = n.List.First()
 			n.Right = jmp
 			n.List.Set(nil)
 			cas = append(cas, n)
-		} else {
-			// expand multi-valued cases
-			for _, n1 := range n.List.Slice() {
-				cas = append(cas, Nod(OCASE, n1, jmp))
+		default:
+			// Expand multi-valued cases and detect ranges of integer cases.
+			if typeswvar != nil || sw.Left.Type.IsInterface() || !n.List.First().Type.IsInteger() || n.List.Len() < integerRangeMin {
+				// Can't use integer ranges. Expand each case into a separate node.
+				for _, n1 := range n.List.Slice() {
+					cas = append(cas, nod(OCASE, n1, jmp))
+				}
+				break
+			}
+			// Find integer ranges within runs of constants.
+			s := n.List.Slice()
+			j := 0
+			for j < len(s) {
+				// Find a run of constants.
+				var run int
+				for run = j; run < len(s) && Isconst(s[run], CTINT); run++ {
+				}
+				if run-j >= integerRangeMin {
+					// Search for integer ranges in s[j:run].
+					// Typechecking is done, so all values are already in an appropriate range.
+					search := s[j:run]
+					sort.Sort(constIntNodesByVal(search))
+					for beg, end := 0, 1; end <= len(search); end++ {
+						if end < len(search) && search[end].Int64() == search[end-1].Int64()+1 {
+							continue
+						}
+						if end-beg >= integerRangeMin {
+							// Record range in List.
+							c := nod(OCASE, nil, jmp)
+							c.List.Set2(search[beg], search[end-1])
+							cas = append(cas, c)
+						} else {
+							// Not large enough for range; record separately.
+							for _, n := range search[beg:end] {
+								cas = append(cas, nod(OCASE, n, jmp))
+							}
+						}
+						beg = end
+					}
+					j = run
+				}
+				// Advance to next constant, adding individual non-constant
+				// or as-yet-unhandled constant cases as we go.
+				for ; j < len(s) && (j < run || !Isconst(s[j], CTINT)); j++ {
+					cas = append(cas, nod(OCASE, s[j], jmp))
+				}
 			}
 		}
 
-		stat = append(stat, Nod(OLABEL, jmp.Left, nil))
+		stat = append(stat, nod(OLABEL, jmp.Left, nil))
 		if typeswvar != nil && needvar && n.Rlist.Len() != 0 {
 			l := []*Node{
-				Nod(ODCL, n.Rlist.First(), nil),
-				Nod(OAS, n.Rlist.First(), typeswvar),
+				nod(ODCL, n.Rlist.First(), nil),
+				nod(OAS, n.Rlist.First(), typeswvar),
 			}
 			typecheckslice(l, Etop)
 			stat = append(stat, l...)
 		}
 		stat = append(stat, n.Nbody.Slice()...)
 
+		// Search backwards for the index of the fallthrough
+		// statement. Do not assume it'll be in the last
+		// position, since in some cases (e.g. when the statement
+		// list contains autotmp_ variables), one or more OVARKILL
+		// nodes will be at the end of the list.
+		fallIndex := len(stat) - 1
+		for stat[fallIndex].Op == OVARKILL {
+			fallIndex--
+		}
+		last := stat[fallIndex]
+
 		// botch - shouldn't fall through declaration
-		last := stat[len(stat)-1]
 		if last.Xoffset == n.Xoffset && last.Op == OXFALL {
 			if typeswvar != nil {
 				setlineno(last)
-				Yyerror("cannot fallthrough in type switch")
+				yyerror("cannot fallthrough in type switch")
 			}
 
 			if i+1 >= sw.List.Len() {
 				setlineno(last)
-				Yyerror("cannot fallthrough final case in switch")
+				yyerror("cannot fallthrough final case in switch")
 			}
 
 			last.Op = OFALL
@@ -424,94 +485,188 @@ func casebody(sw *Node, typeswvar *Node) {
 	lineno = lno
 }
 
-// nSwitchLabel is the number of switch labels generated.
-// This should be per-function, but it is a global counter for now.
-var nSwitchLabel int
+// genCaseClauses generates the caseClauses value for clauses.
+func (s *exprSwitch) genCaseClauses(clauses []*Node) caseClauses {
+	var cc caseClauses
+	for _, n := range clauses {
+		if n.Left == nil && n.List.Len() == 0 {
+			// default case
+			if cc.defjmp != nil {
+				Fatalf("duplicate default case not detected during typechecking")
+			}
+			cc.defjmp = n.Right
+			continue
+		}
+		c := caseClause{node: n, ordinal: len(cc.list)}
+		if n.List.Len() > 0 {
+			c.isconst = true
+		}
+		switch consttype(n.Left) {
+		case CTFLT, CTINT, CTRUNE, CTSTR:
+			c.isconst = true
+		}
+		cc.list = append(cc.list, c)
+	}
 
-func newCaseLabel() *Node {
-	label := strconv.Itoa(nSwitchLabel)
-	nSwitchLabel++
-	return newname(Lookup(label))
+	if cc.defjmp == nil {
+		cc.defjmp = nod(OBREAK, nil, nil)
+	}
+
+	// diagnose duplicate cases
+	s.checkDupCases(cc.list)
+	return cc
 }
 
-// caseClauses generates a slice of caseClauses
-// corresponding to the clauses in the switch statement sw.
-// Kind is the kind of switch statement.
-func caseClauses(sw *Node, kind int) []*caseClause {
-	var cc []*caseClause
-	for _, n := range sw.List.Slice() {
-		c := new(caseClause)
-		cc = append(cc, c)
-		c.ordinal = len(cc)
-		c.node = n
-
-		if n.Left == nil {
-			c.typ = caseKindDefault
+// genCaseClauses generates the caseClauses value for clauses.
+func (s *typeSwitch) genCaseClauses(clauses []*Node) caseClauses {
+	var cc caseClauses
+	for _, n := range clauses {
+		switch {
+		case n.Left == nil:
+			// default case
+			if cc.defjmp != nil {
+				Fatalf("duplicate default case not detected during typechecking")
+			}
+			cc.defjmp = n.Right
+			continue
+		case n.Left.Op == OLITERAL:
+			// nil case in type switch
+			if cc.niljmp != nil {
+				Fatalf("duplicate nil case not detected during typechecking")
+			}
+			cc.niljmp = n.Right
 			continue
 		}
 
-		if kind == switchKindType {
-			// type switch
-			switch {
-			case n.Left.Op == OLITERAL:
-				c.typ = caseKindTypeNil
-			case n.Left.Type.IsInterface():
-				c.typ = caseKindTypeVar
-			default:
-				c.typ = caseKindTypeConst
-				c.hash = typehash(n.Left.Type)
-			}
-		} else {
-			// expression switch
-			switch consttype(n.Left) {
-			case CTFLT, CTINT, CTRUNE, CTSTR:
-				c.typ = caseKindExprConst
-			default:
-				c.typ = caseKindExprVar
-			}
+		// general case
+		c := caseClause{
+			node:    n,
+			ordinal: len(cc.list),
+			isconst: !n.Left.Type.IsInterface(),
+			hash:    typehash(n.Left.Type),
 		}
+		cc.list = append(cc.list, c)
 	}
 
-	if cc == nil {
-		return nil
+	if cc.defjmp == nil {
+		cc.defjmp = nod(OBREAK, nil, nil)
 	}
 
-	// sort by value and diagnose duplicate cases
-	if kind == switchKindType {
-		// type switch
-		sort.Sort(caseClauseByType(cc))
-		for i, c1 := range cc {
-			if c1.typ == caseKindTypeNil || c1.typ == caseKindDefault {
-				break
+	// diagnose duplicate cases
+	s.checkDupCases(cc.list)
+	return cc
+}
+
+func (s *typeSwitch) checkDupCases(cc []caseClause) {
+	if len(cc) < 2 {
+		return
+	}
+	// We store seen types in a map keyed by type hash.
+	// It is possible, but very unlikely, for multiple distinct types to have the same hash.
+	seen := make(map[uint32][]*Node)
+	// To avoid many small allocations of length 1 slices,
+	// also set up a single large slice to slice into.
+	nn := make([]*Node, 0, len(cc))
+Outer:
+	for _, c := range cc {
+		prev, ok := seen[c.hash]
+		if !ok {
+			// First entry for this hash.
+			nn = append(nn, c.node)
+			seen[c.hash] = nn[len(nn)-1 : len(nn):len(nn)]
+			continue
+		}
+		for _, n := range prev {
+			if eqtype(n.Left.Type, c.node.Left.Type) {
+				yyerrorl(c.node.Lineno, "duplicate case %v in type switch\n\tprevious case at %v", c.node.Left.Type, n.Line())
+				// avoid double-reporting errors
+				continue Outer
 			}
-			for _, c2 := range cc[i+1:] {
-				if c2.typ == caseKindTypeNil || c2.typ == caseKindDefault || c1.hash != c2.hash {
-					break
+		}
+		seen[c.hash] = append(seen[c.hash], c.node)
+	}
+}
+
+func (s *exprSwitch) checkDupCases(cc []caseClause) {
+	if len(cc) < 2 {
+		return
+	}
+	// The common case is that s's expression is not an interface.
+	// In that case, all constant clauses have the same type,
+	// so checking for duplicates can be done solely by value.
+	if !s.exprname.Type.IsInterface() {
+		seen := make(map[interface{}]*Node)
+		for _, c := range cc {
+			switch {
+			case c.node.Left != nil:
+				// Single constant.
+
+				// Can't check for duplicates that aren't constants, per the spec. Issue 15896.
+				// Don't check for duplicate bools. Although the spec allows it,
+				// (1) the compiler hasn't checked it in the past, so compatibility mandates it, and
+				// (2) it would disallow useful things like
+				//       case GOARCH == "arm" && GOARM == "5":
+				//       case GOARCH == "arm":
+				//     which would both evaluate to false for non-ARM compiles.
+				if ct := consttype(c.node.Left); ct < 0 || ct == CTBOOL {
+					continue
 				}
-				if Eqtype(c1.node.Left.Type, c2.node.Left.Type) {
-					yyerrorl(c2.node.Lineno, "duplicate case %v in type switch\n\tprevious case at %v", c2.node.Left.Type, c1.node.Line())
+
+				val := c.node.Left.Val().Interface()
+				prev, dup := seen[val]
+				if !dup {
+					seen[val] = c.node
+					continue
+				}
+				setlineno(c.node)
+				yyerror("duplicate case %#v in switch\n\tprevious case at %v", val, prev.Line())
+
+			case c.node.List.Len() == 2:
+				// Range of integers.
+				low := c.node.List.Index(0).Int64()
+				high := c.node.List.Index(1).Int64()
+				for i := low; i <= high; i++ {
+					prev, dup := seen[i]
+					if !dup {
+						seen[i] = c.node
+						continue
+					}
+					setlineno(c.node)
+					yyerror("duplicate case %d in switch\n\tprevious case at %v", i, prev.Line())
 				}
+
+			default:
+				Fatalf("bad caseClause node in checkDupCases: %v", c.node)
 			}
 		}
-	} else {
-		// expression switch
-		sort.Sort(caseClauseByExpr(cc))
-		for i, c1 := range cc {
-			if i+1 == len(cc) {
-				break
-			}
-			c2 := cc[i+1]
-			if exprcmp(c1, c2) != 0 {
-				continue
-			}
-			setlineno(c2.node)
-			Yyerror("duplicate case %v in switch\n\tprevious case at %v", c1.node.Left, c1.node.Line())
+		return
+	}
+	// s's expression is an interface. This is fairly rare, so keep this simple.
+	// Duplicates are only duplicates if they have the same type and the same value.
+	type typeVal struct {
+		typ string
+		val interface{}
+	}
+	seen := make(map[typeVal]*Node)
+	for _, c := range cc {
+		if ct := consttype(c.node.Left); ct < 0 || ct == CTBOOL {
+			continue
 		}
+		n := c.node.Left
+		tv := typeVal{
+			// n.Type.tconv(FmtLeft | FmtUnsigned) here serves to completely describe the type.
+			// See the comments in func typehash.
+			typ: n.Type.tconv(FmtLeft | FmtUnsigned),
+			val: n.Val().Interface(),
+		}
+		prev, dup := seen[tv]
+		if !dup {
+			seen[tv] = c.node
+			continue
+		}
+		setlineno(c.node)
+		yyerror("duplicate case %v in switch\n\tprevious case at %v", prev.Left, prev.Line())
 	}
-
-	// put list back in processing order
-	sort.Sort(caseClauseByOrd(cc))
-	return cc
 }
 
 // walk generates an AST that implements sw,
@@ -529,13 +684,13 @@ func (s *typeSwitch) walk(sw *Node) {
 	}
 	if cond.Right == nil {
 		setlineno(sw)
-		Yyerror("type switch must have an assignment")
+		yyerror("type switch must have an assignment")
 		return
 	}
 
 	cond.Right = walkexpr(cond.Right, &sw.Ninit)
 	if !cond.Right.Type.IsInterface() {
-		Yyerror("type switch must be on an interface")
+		yyerror("type switch must be on an interface")
 		return
 	}
 
@@ -544,7 +699,7 @@ func (s *typeSwitch) walk(sw *Node) {
 	// predeclare temporary variables and the boolean var
 	s.facename = temp(cond.Right.Type)
 
-	a := Nod(OAS, s.facename, cond.Right)
+	a := nod(OAS, s.facename, cond.Right)
 	a = typecheck(a, Etop)
 	cas = append(cas, a)
 
@@ -557,20 +712,9 @@ func (s *typeSwitch) walk(sw *Node) {
 	// set up labels and jumps
 	casebody(sw, s.facename)
 
-	cc := caseClauses(sw, switchKindType)
+	clauses := s.genCaseClauses(sw.List.Slice())
 	sw.List.Set(nil)
-	var def *Node
-	if len(cc) > 0 && cc[0].typ == caseKindDefault {
-		def = cc[0].node.Right
-		cc = cc[1:]
-	} else {
-		def = Nod(OBREAK, nil, nil)
-	}
-	var typenil *Node
-	if len(cc) > 0 && cc[0].typ == caseKindTypeNil {
-		typenil = cc[0].node.Right
-		cc = cc[1:]
-	}
+	def := clauses.defjmp
 
 	// For empty interfaces, do:
 	//     if e._type == nil {
@@ -580,21 +724,21 @@ func (s *typeSwitch) walk(sw *Node) {
 	// Use a similar strategy for non-empty interfaces.
 
 	// Get interface descriptor word.
-	typ := Nod(OITAB, s.facename, nil)
+	typ := nod(OITAB, s.facename, nil)
 
 	// Check for nil first.
-	i := Nod(OIF, nil, nil)
-	i.Left = Nod(OEQ, typ, nodnil())
-	if typenil != nil {
+	i := nod(OIF, nil, nil)
+	i.Left = nod(OEQ, typ, nodnil())
+	if clauses.niljmp != nil {
 		// Do explicit nil case right here.
-		i.Nbody.Set1(typenil)
+		i.Nbody.Set1(clauses.niljmp)
 	} else {
 		// Jump to default case.
-		lbl := newCaseLabel()
-		i.Nbody.Set1(Nod(OGOTO, lbl, nil))
+		lbl := autolabel(".s")
+		i.Nbody.Set1(nod(OGOTO, lbl, nil))
 		// Wrap default case with label.
-		blk := Nod(OBLOCK, nil, nil)
-		blk.List.Set([]*Node{Nod(OLABEL, lbl, nil), def})
+		blk := nod(OBLOCK, nil, nil)
+		blk.List.Set([]*Node{nod(OLABEL, lbl, nil), def})
 		def = blk
 	}
 	i.Left = typecheck(i.Left, Erv)
@@ -602,36 +746,28 @@ func (s *typeSwitch) walk(sw *Node) {
 
 	if !cond.Right.Type.IsEmptyInterface() {
 		// Load type from itab.
-		typ = NodSym(ODOTPTR, typ, nil)
-		typ.Type = Ptrto(Types[TUINT8])
-		typ.Typecheck = 1
-		typ.Xoffset = int64(Widthptr) // offset of _type in runtime.itab
-		typ.Bounded = true            // guaranteed not to fault
+		typ = itabType(typ)
 	}
 	// Load hash from type.
-	h := NodSym(ODOTPTR, typ, nil)
+	h := nodSym(ODOTPTR, typ, nil)
 	h.Type = Types[TUINT32]
 	h.Typecheck = 1
 	h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
 	h.Bounded = true                // guaranteed not to fault
-	a = Nod(OAS, s.hashname, h)
+	a = nod(OAS, s.hashname, h)
 	a = typecheck(a, Etop)
 	cas = append(cas, a)
 
+	cc := clauses.list
+
 	// insert type equality check into each case block
 	for _, c := range cc {
-		n := c.node
-		switch c.typ {
-		case caseKindTypeVar, caseKindTypeConst:
-			n.Right = s.typeone(n)
-		default:
-			Fatalf("typeSwitch with bad kind: %d", c.typ)
-		}
+		c.node.Right = s.typeone(c.node)
 	}
 
 	// generate list of if statements, binary search for constant sequences
 	for len(cc) > 0 {
-		if cc[0].typ != caseKindTypeConst {
+		if !cc[0].isconst {
 			n := cc[0].node
 			cas = append(cas, n.Right)
 			cc = cc[1:]
@@ -640,7 +776,7 @@ func (s *typeSwitch) walk(sw *Node) {
 
 		// identify run of constants
 		var run int
-		for run = 1; run < len(cc) && cc[run].typ == caseKindTypeConst; run++ {
+		for run = 1; run < len(cc) && cc[run].isconst; run++ {
 		}
 
 		// sort by hash
@@ -674,7 +810,7 @@ func (s *typeSwitch) walk(sw *Node) {
 	// handle default case
 	if nerrors == 0 {
 		cas = append(cas, def)
-		sw.Nbody.Set(append(cas, sw.Nbody.Slice()...))
+		sw.Nbody.Prepend(cas...)
 		sw.List.Set(nil)
 		walkstmtlist(sw.Nbody.Slice())
 	}
@@ -690,21 +826,21 @@ func (s *typeSwitch) typeone(t *Node) *Node {
 		nblank = typecheck(nblank, Erv|Easgn)
 	} else {
 		name = t.Rlist.First()
-		init = []*Node{Nod(ODCL, name, nil)}
-		a := Nod(OAS, name, nil)
+		init = []*Node{nod(ODCL, name, nil)}
+		a := nod(OAS, name, nil)
 		a = typecheck(a, Etop)
 		init = append(init, a)
 	}
 
-	a := Nod(OAS2, nil, nil)
+	a := nod(OAS2, nil, nil)
 	a.List.Set([]*Node{name, s.okname}) // name, ok =
-	b := Nod(ODOTTYPE, s.facename, nil)
+	b := nod(ODOTTYPE, s.facename, nil)
 	b.Type = t.Left.Type // interface.(type)
 	a.Rlist.Set1(b)
 	a = typecheck(a, Etop)
 	init = append(init, a)
 
-	c := Nod(OIF, nil, nil)
+	c := nod(OIF, nil, nil)
 	c.Left = s.okname
 	c.Nbody.Set1(t.Right) // if ok { goto l }
 
@@ -712,16 +848,16 @@ func (s *typeSwitch) typeone(t *Node) *Node {
 }
 
 // walkCases generates an AST implementing the cases in cc.
-func (s *typeSwitch) walkCases(cc []*caseClause) *Node {
+func (s *typeSwitch) walkCases(cc []caseClause) *Node {
 	if len(cc) < binarySearchMin {
 		var cas []*Node
 		for _, c := range cc {
 			n := c.node
-			if c.typ != caseKindTypeConst {
+			if !c.isconst {
 				Fatalf("typeSwitch walkCases")
 			}
-			a := Nod(OIF, nil, nil)
-			a.Left = Nod(OEQ, s.hashname, Nodintconst(int64(c.hash)))
+			a := nod(OIF, nil, nil)
+			a.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash)))
 			a.Left = typecheck(a.Left, Erv)
 			a.Nbody.Set1(n.Right)
 			cas = append(cas, a)
@@ -731,123 +867,78 @@ func (s *typeSwitch) walkCases(cc []*caseClause) *Node {
 
 	// find the middle and recur
 	half := len(cc) / 2
-	a := Nod(OIF, nil, nil)
-	a.Left = Nod(OLE, s.hashname, Nodintconst(int64(cc[half-1].hash)))
+	a := nod(OIF, nil, nil)
+	a.Left = nod(OLE, s.hashname, nodintconst(int64(cc[half-1].hash)))
 	a.Left = typecheck(a.Left, Erv)
 	a.Nbody.Set1(s.walkCases(cc[:half]))
 	a.Rlist.Set1(s.walkCases(cc[half:]))
 	return a
 }
 
-type caseClauseByOrd []*caseClause
-
-func (x caseClauseByOrd) Len() int      { return len(x) }
-func (x caseClauseByOrd) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (x caseClauseByOrd) Less(i, j int) bool {
-	c1, c2 := x[i], x[j]
-	switch {
-	// sort default first
-	case c1.typ == caseKindDefault:
-		return true
-	case c2.typ == caseKindDefault:
-		return false
-
-	// sort nil second
-	case c1.typ == caseKindTypeNil:
-		return true
-	case c2.typ == caseKindTypeNil:
-		return false
-	}
-
-	// sort by ordinal
-	return c1.ordinal < c2.ordinal
-}
-
-type caseClauseByExpr []*caseClause
-
-func (x caseClauseByExpr) Len() int      { return len(x) }
-func (x caseClauseByExpr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (x caseClauseByExpr) Less(i, j int) bool {
-	return exprcmp(x[i], x[j]) < 0
-}
-
-func exprcmp(c1, c2 *caseClause) int {
-	// sort non-constants last
-	if c1.typ != caseKindExprConst {
-		return +1
-	}
-	if c2.typ != caseKindExprConst {
-		return -1
+// caseClauseByConstVal sorts clauses by constant value to enable binary search.
+type caseClauseByConstVal []caseClause
+
+func (x caseClauseByConstVal) Len() int      { return len(x) }
+func (x caseClauseByConstVal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x caseClauseByConstVal) Less(i, j int) bool {
+	// n1 and n2 might be individual constants or integer ranges.
+	// We have checked for duplicates already,
+	// so ranges can be safely represented by any value in the range.
+	n1 := x[i].node
+	var v1 interface{}
+	if s := n1.List.Slice(); s != nil {
+		v1 = s[0].Val().U
+	} else {
+		v1 = n1.Left.Val().U
 	}
 
-	n1 := c1.node.Left
-	n2 := c2.node.Left
-
-	// sort by type (for switches on interface)
-	ct := n1.Val().Ctype()
-	if ct > n2.Val().Ctype() {
-		return +1
-	}
-	if ct < n2.Val().Ctype() {
-		return -1
-	}
-	if !Eqtype(n1.Type, n2.Type) {
-		if n1.Type.Vargen > n2.Type.Vargen {
-			return +1
-		} else {
-			return -1
-		}
+	n2 := x[j].node
+	var v2 interface{}
+	if s := n2.List.Slice(); s != nil {
+		v2 = s[0].Val().U
+	} else {
+		v2 = n2.Left.Val().U
 	}
 
-	// sort by constant value to enable binary search
-	switch ct {
-	case CTFLT:
-		return n1.Val().U.(*Mpflt).Cmp(n2.Val().U.(*Mpflt))
-	case CTINT, CTRUNE:
-		return n1.Val().U.(*Mpint).Cmp(n2.Val().U.(*Mpint))
-	case CTSTR:
+	switch v1 := v1.(type) {
+	case *Mpflt:
+		return v1.Cmp(v2.(*Mpflt)) < 0
+	case *Mpint:
+		return v1.Cmp(v2.(*Mpint)) < 0
+	case string:
 		// Sort strings by length and then by value.
 		// It is much cheaper to compare lengths than values,
 		// and all we need here is consistency.
 		// We respect this sorting in exprSwitch.walkCases.
-		a := n1.Val().U.(string)
-		b := n2.Val().U.(string)
-		if len(a) < len(b) {
-			return -1
+		a := v1
+		b := v2.(string)
+		if len(a) != len(b) {
+			return len(a) < len(b)
 		}
-		if len(a) > len(b) {
-			return +1
-		}
-		if a == b {
-			return 0
-		}
-		if a < b {
-			return -1
-		}
-		return +1
+		return a < b
 	}
 
-	return 0
+	Fatalf("caseClauseByConstVal passed bad clauses %v < %v", x[i].node.Left, x[j].node.Left)
+	return false
 }
 
-type caseClauseByType []*caseClause
+type caseClauseByType []caseClause
 
 func (x caseClauseByType) Len() int      { return len(x) }
 func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
 func (x caseClauseByType) Less(i, j int) bool {
 	c1, c2 := x[i], x[j]
-	switch {
-	// sort non-constants last
-	case c1.typ != caseKindTypeConst:
-		return false
-	case c2.typ != caseKindTypeConst:
-		return true
-
-	// sort by hash code
-	case c1.hash != c2.hash:
+	// sort by hash code, then ordinal (for the rare case of hash collisions)
+	if c1.hash != c2.hash {
 		return c1.hash < c2.hash
 	}
-
-	// sort by ordinal
 	return c1.ordinal < c2.ordinal
 }
+
+type constIntNodesByVal []*Node
+
+func (x constIntNodesByVal) Len() int      { return len(x) }
+func (x constIntNodesByVal) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x constIntNodesByVal) Less(i, j int) bool {
+	return x[i].Val().U.(*Mpint).Cmp(x[j].Val().U.(*Mpint)) < 0
+}
diff --git a/src/cmd/compile/internal/gc/swt_test.go b/src/cmd/compile/internal/gc/swt_test.go
index c1ee895..4139898 100644
--- a/src/cmd/compile/internal/gc/swt_test.go
+++ b/src/cmd/compile/internal/gc/swt_test.go
@@ -5,140 +5,42 @@
 package gc
 
 import (
-	"cmd/compile/internal/big"
+	"math/big"
 	"testing"
 )
 
-func TestExprcmp(t *testing.T) {
-	testdata := []struct {
-		a, b caseClause
-		want int
+func nodrune(r rune) *Node {
+	return nodlit(Val{&Mpint{Val: *big.NewInt(int64(r)), Rune: true}})
+}
+
+func nodflt(f float64) *Node {
+	return nodlit(Val{&Mpflt{Val: *big.NewFloat(f)}})
+}
+
+func TestCaseClauseByConstVal(t *testing.T) {
+	tests := []struct {
+		a, b *Node
 	}{
-		// Non-constants.
-		{
-			caseClause{node: Nod(OXXX, nil, nil), typ: caseKindExprVar},
-			caseClause{node: Nod(OXXX, nil, nil), typ: caseKindExprConst},
-			+1,
-		},
-		{
-			caseClause{node: Nod(OXXX, nil, nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nil, nil), typ: caseKindExprVar},
-			-1,
-		},
-		// Type switches
-		{
-			caseClause{node: Nod(OXXX, Nodintconst(0), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, Nodbool(true), nil), typ: caseKindExprConst},
-			-1,
-		},
-		{
-			caseClause{node: Nod(OXXX, Nodbool(true), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, Nodintconst(1), nil), typ: caseKindExprConst},
-			+1,
-		},
-		{
-			caseClause{node: Nod(OXXX, &Node{Type: &Type{Etype: TBOOL, Vargen: 1}}, nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, &Node{Type: &Type{Etype: TINT, Vargen: 0}}, nil), typ: caseKindExprConst},
-			+1,
-		},
-		{
-			caseClause{node: Nod(OXXX, &Node{Type: &Type{Etype: TBOOL, Vargen: 1}}, nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, &Node{Type: &Type{Etype: TINT, Vargen: 1}}, nil), typ: caseKindExprConst},
-			-1,
-		},
-		{
-			caseClause{node: Nod(OXXX, &Node{Type: &Type{Etype: TBOOL, Vargen: 0}}, nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, &Node{Type: &Type{Etype: TINT, Vargen: 1}}, nil), typ: caseKindExprConst},
-			-1,
-		},
-		// Constant values.
 		// CTFLT
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpflt{Val: *big.NewFloat(0.1)}}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpflt{Val: *big.NewFloat(0.2)}}), nil), typ: caseKindExprConst},
-			-1,
-		},
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpflt{Val: *big.NewFloat(0.1)}}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpflt{Val: *big.NewFloat(0.1)}}), nil), typ: caseKindExprConst},
-			0,
-		},
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpflt{Val: *big.NewFloat(0.2)}}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpflt{Val: *big.NewFloat(0.1)}}), nil), typ: caseKindExprConst},
-			+1,
-		},
+		{nodflt(0.1), nodflt(0.2)},
 		// CTINT
-		{
-			caseClause{node: Nod(OXXX, Nodintconst(0), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, Nodintconst(1), nil), typ: caseKindExprConst},
-			-1,
-		},
-		{
-			caseClause{node: Nod(OXXX, Nodintconst(1), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, Nodintconst(1), nil), typ: caseKindExprConst},
-			0,
-		},
-		{
-			caseClause{node: Nod(OXXX, Nodintconst(1), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, Nodintconst(0), nil), typ: caseKindExprConst},
-			+1,
-		},
+		{nodintconst(0), nodintconst(1)},
 		// CTRUNE
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpint{Val: *big.NewInt('a'), Rune: true}}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpint{Val: *big.NewInt('b'), Rune: true}}), nil), typ: caseKindExprConst},
-			-1,
-		},
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpint{Val: *big.NewInt('b'), Rune: true}}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpint{Val: *big.NewInt('b'), Rune: true}}), nil), typ: caseKindExprConst},
-			0,
-		},
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpint{Val: *big.NewInt('b'), Rune: true}}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{&Mpint{Val: *big.NewInt('a'), Rune: true}}), nil), typ: caseKindExprConst},
-			+1,
-		},
+		{nodrune('a'), nodrune('b')},
 		// CTSTR
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{"ab"}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{"abc"}), nil), typ: caseKindExprConst},
-			-1,
-		},
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{"abc"}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{"xyz"}), nil), typ: caseKindExprConst},
-			-1,
-		},
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{"abc"}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{"abc"}), nil), typ: caseKindExprConst},
-			0,
-		},
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{"abc"}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{"ab"}), nil), typ: caseKindExprConst},
-			+1,
-		},
-		{
-			caseClause{node: Nod(OXXX, nodlit(Val{"xyz"}), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodlit(Val{"abc"}), nil), typ: caseKindExprConst},
-			+1,
-		},
-		// Everything else should compare equal.
-		{
-			caseClause{node: Nod(OXXX, nodnil(), nil), typ: caseKindExprConst},
-			caseClause{node: Nod(OXXX, nodnil(), nil), typ: caseKindExprConst},
-			0,
-		},
+		{nodlit(Val{"ab"}), nodlit(Val{"abc"})},
+		{nodlit(Val{"ab"}), nodlit(Val{"xyz"})},
+		{nodlit(Val{"abc"}), nodlit(Val{"xyz"})},
 	}
-	for i, d := range testdata {
-		got := exprcmp(&d.a, &d.b)
-		if d.want != got {
-			t.Errorf("%d: exprcmp(a, b) = %d; want %d", i, got, d.want)
-			t.Logf("\ta = caseClause{node: %#v, typ: %#v}", d.a.node, d.a.typ)
-			t.Logf("\tb = caseClause{node: %#v, typ: %#v}", d.b.node, d.b.typ)
+	for i, test := range tests {
+		a := caseClause{node: nod(OXXX, test.a, nil)}
+		b := caseClause{node: nod(OXXX, test.b, nil)}
+		s := caseClauseByConstVal{a, b}
+		if less := s.Less(0, 1); !less {
+			t.Errorf("%d: caseClauseByConstVal(%v, %v) = false", i, test.a, test.b)
+		}
+		if less := s.Less(1, 0); less {
+			t.Errorf("%d: caseClauseByConstVal(%v, %v) = true", i, test.a, test.b)
 		}
 	}
 }
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index fab8697..8b06d3a 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -33,39 +33,36 @@ type Node struct {
 	Sym *Sym        // various
 	E   interface{} // Opt or Val, see methods below
 
-	// Various. Usually an offset into a struct. For example, ONAME nodes
-	// that refer to local variables use it to identify their stack frame
-	// position. ODOT, ODOTPTR, and OINDREG use it to indicate offset
-	// relative to their base address. ONAME nodes on the left side of an
-	// OKEY within an OSTRUCTLIT use it to store the named field's offset.
-	// OXCASE and OXFALL use it to validate the use of fallthrough.
+	// Various. Usually an offset into a struct. For example:
+	// - 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.
+	// - OXCASE and OXFALL use it to validate the use of fallthrough.
+	// - ONONAME uses it to store the current value of iota, see Node.Iota
 	// Possibly still more uses. If you find any, document them.
 	Xoffset int64
 
 	Lineno int32
 
-	// OREGISTER, OINDREG
-	Reg int16
-
 	Esc uint16 // EscXXX
 
 	Op        Op
 	Ullman    uint8 // sethi/ullman number
 	Addable   bool  // addressable
-	Etype     EType // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg, ChanDir for OTCHAN
+	Etype     EType // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg, ChanDir for OTCHAN, for OINDEXMAP 1=LHS,0=RHS
 	Bounded   bool  // bounds check unnecessary
 	NonNil    bool  // guaranteed to be non-nil
 	Class     Class // PPARAM, PAUTO, PEXTERN, etc
 	Embedded  uint8 // ODCLFIELD embedded type
 	Colas     bool  // OAS resulting from :=
-	Diag      uint8 // already printed error about this
+	Diag      bool  // already printed error about this
 	Noescape  bool  // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
-	Walkdef   uint8
-	Typecheck uint8
+	Walkdef   uint8 // tracks state during typecheckdef; 2 == loop detected
+	Typecheck uint8 // tracks state during typechecking; 2 == loop detected
 	Local     bool
-	Dodata    uint8
+	IsStatic  bool // whether this Node will be converted to purely static data
 	Initorder uint8
-	Used      bool
+	Used      bool // for variable/label declared and not used error
 	Isddd     bool // is the argument variadic
 	Implicit  bool
 	Addrtaken bool  // address taken, even if not moved to heap
@@ -75,11 +72,20 @@ type Node struct {
 	flags     uint8 // TODO: store more bool fields in this flag field
 }
 
+// IsAutoTmp indicates if n was created by the compiler as a temporary,
+// based on the setting of the .AutoTemp flag in n's Name.
+func (n *Node) IsAutoTmp() bool {
+	if n == nil || n.Op != ONAME {
+		return false
+	}
+	return n.Name.AutoTemp
+}
+
 const (
 	hasBreak = 1 << iota
-	notLiveAtEnd
 	isClosureVar
 	isOutputParamHeapAddr
+	noInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
 )
 
 func (n *Node) HasBreak() bool {
@@ -92,16 +98,6 @@ func (n *Node) SetHasBreak(b bool) {
 		n.flags &^= hasBreak
 	}
 }
-func (n *Node) NotLiveAtEnd() bool {
-	return n.flags&notLiveAtEnd != 0
-}
-func (n *Node) SetNotLiveAtEnd(b bool) {
-	if b {
-		n.flags |= notLiveAtEnd
-	} else {
-		n.flags &^= notLiveAtEnd
-	}
-}
 func (n *Node) isClosureVar() bool {
 	return n.flags&isClosureVar != 0
 }
@@ -112,6 +108,16 @@ func (n *Node) setIsClosureVar(b bool) {
 		n.flags &^= isClosureVar
 	}
 }
+func (n *Node) noInline() bool {
+	return n.flags&noInline != 0
+}
+func (n *Node) setNoInline(b bool) {
+	if b {
+		n.flags |= noInline
+	} else {
+		n.flags &^= noInline
+	}
+}
 
 func (n *Node) IsOutputParamHeapAddr() bool {
 	return n.flags&isOutputParamHeapAddr != 0
@@ -166,25 +172,31 @@ func (n *Node) SetOpt(x interface{}) {
 	n.E = x
 }
 
-// Name holds Node fields used only by named nodes (ONAME, OPACK, OLABEL, ODCLFIELD, some OLITERAL).
+func (n *Node) Iota() int64 {
+	return n.Xoffset
+}
+
+func (n *Node) SetIota(x int64) {
+	n.Xoffset = x
+}
+
+// Name holds Node fields used only by named nodes (ONAME, OPACK, OLABEL, some OLITERAL).
 type Name struct {
 	Pack      *Node  // real package for import . names
 	Pkg       *Pkg   // pkg for OPACK nodes
 	Heapaddr  *Node  // temp holding heap address of param (could move to Param?)
-	Inlvar    *Node  // ONAME substitute while inlining (could move to Param?)
 	Defn      *Node  // initializing assignment
 	Curfn     *Node  // function for local variables
-	Param     *Param // additional fields for ONAME, ODCLFIELD
+	Param     *Param // additional fields for ONAME
 	Decldepth int32  // declaration loop depth, increased for every loop or label
 	Vargen    int32  // unique name for ONAME within a function.  Function outputs are numbered starting at one.
-	Iota      int32  // value if this name is iota
 	Funcdepth int32
-	Method    bool // OCALLMETH name
 	Readonly  bool
 	Captured  bool // is the variable captured by a closure
 	Byval     bool // is the variable captured by value or by reference
 	Needzero  bool // if it contains pointers, needs to be zeroed on function entry
 	Keepalive bool // mark value live across unknown assembly call
+	AutoTemp  bool // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
 }
 
 type Param struct {
@@ -267,6 +279,11 @@ type Param struct {
 	// and x.Innermost/Outer means x.Name.Param.Innermost/Outer.
 	Innermost *Node
 	Outer     *Node
+
+	// OTYPE pragmas
+	//
+	// TODO: Should Func pragmas also be stored on the Name?
+	Pragma Pragma
 }
 
 // Func holds Node fields used only with function-like nodes.
@@ -283,21 +300,23 @@ type Func struct {
 	Ntype      *Node // signature
 	Top        int   // top context (Ecall, Eproc, etc)
 	Closure    *Node // OCLOSURE <-> ODCLFUNC
-	FCurfn     *Node
 	Nname      *Node
 
 	Inl     Nodes // copy of the body for use in inlining
 	InlCost int32
 	Depth   int32
 
+	Label int32 // largest auto-generated label in this function
+
 	Endlineno int32
 	WBLineno  int32 // line number of first write barrier
 
-	Pragma        Pragma // go:xxx function annotations
-	Dupok         bool   // duplicate definitions ok
-	Wrapper       bool   // is method wrapper
-	Needctxt      bool   // function uses context register (has closure variables)
-	ReflectMethod bool   // function calls reflect.Type.Method or MethodByName
+	Pragma          Pragma // go:xxx function annotations
+	Dupok           bool   // duplicate definitions ok
+	Wrapper         bool   // is method wrapper
+	Needctxt        bool   // function uses context register (has closure variables)
+	ReflectMethod   bool   // function calls reflect.Type.Method or MethodByName
+	IsHiddenClosure bool
 }
 
 type Op uint8
@@ -349,7 +368,8 @@ const (
 	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 or slice)
+	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)
@@ -380,8 +400,8 @@ const (
 	OIND       // *Left
 	OINDEX     // Left[Right] (index of array or slice)
 	OINDEXMAP  // Left[Right] (index of map)
-	OKEY       // Left:Right (key:value in struct/array/map literal, or slice index pair)
-	_          // was OPARAM, but cannot remove without breaking binary blob in builtin.go
+	OKEY       // Left:Right (key:value in struct/array/map literal)
+	OSTRUCTKEY // Sym:Left (key:value in struct literal, after type checking)
 	OLEN       // len(Left)
 	OMAKE      // make(List) (before type checking converts to one of the following)
 	OMAKECHAN  // make(Type, Left) (type is chan)
@@ -405,11 +425,11 @@ const (
 	OPRINTN    // println(List)
 	OPAREN     // (Left)
 	OSEND      // Left <- Right
-	OSLICE     // Left[Right.Left : Right.Right] (Left is untypechecked or slice; Right.Op==OKEY)
-	OSLICEARR  // Left[Right.Left : Right.Right] (Left is array)
-	OSLICESTR  // Left[Right.Left : Right.Right] (Left is string)
-	OSLICE3    // Left[R.Left : R.R.Left : R.R.R] (R=Right; Left is untypedchecked or slice; R.Op and R.R.Op==OKEY)
-	OSLICE3ARR // Left[R.Left : R.R.Left : R.R.R] (R=Right; Left is array; R.Op and R.R.Op==OKEY)
+	OSLICE     // Left[List[0] : List[1]] (Left is untypechecked or slice)
+	OSLICEARR  // Left[List[0] : List[1]] (Left is array)
+	OSLICESTR  // Left[List[0] : List[1]] (Left is string)
+	OSLICE3    // Left[List[0] : List[1] : List[2]] (Left is untypedchecked or slice)
+	OSLICE3ARR // Left[List[0] : List[1] : List[2]] (Left is array)
 	ORECOVER   // recover()
 	ORECV      // <-Left
 	ORUNESTR   // Type(Left) (Type is string, Left is rune)
@@ -419,11 +439,14 @@ const (
 	OREAL      // real(Left)
 	OIMAG      // imag(Left)
 	OCOMPLEX   // complex(Left, Right)
+	OALIGNOF   // unsafe.Alignof(Left)
+	OOFFSETOF  // unsafe.Offsetof(Left)
+	OSIZEOF    // unsafe.Sizeof(Left)
 
 	// statements
 	OBLOCK    // { List } (block of code)
 	OBREAK    // break
-	OCASE     // case List: Nbody (select case after processing; List==nil means default)
+	OCASE     // case Left or List[0]..List[1]: Nbody (select case after processing; Left==nil and List==nil means default)
 	OXCASE    // case List: Nbody (select case before processing; List==nil means default)
 	OCONTINUE // continue
 	ODEFER    // defer Left (Left must be call)
@@ -455,16 +478,14 @@ const (
 	OINLCALL    // intermediary representation of an inlined call.
 	OEFACE      // itable and data words of an empty-interface value.
 	OITAB       // itable word of an interface value.
+	OIDATA      // data word of an interface value in Left
 	OSPTR       // base pointer of a slice or string.
 	OCLOSUREVAR // variable reference at beginning of closure function
 	OCFUNC      // reference to c function pointer (not go func value)
 	OCHECKNIL   // emit code to ensure pointer/interface not nil
 	OVARKILL    // variable is dead
 	OVARLIVE    // variable is alive
-
-	// thearch-specific registers
-	OREGISTER // a register, such as AX.
-	OINDREG   // offset plus indirect of a register, such as 8(SP).
+	OINDREGSP   // offset plus indirect of REGSP, such as 8(SP).
 
 	// arch-specific opcodes
 	OCMP    // compare: ACMP.
@@ -543,6 +564,11 @@ func (n *Nodes) Set1(node *Node) {
 	n.slice = &[]*Node{node}
 }
 
+// Set2 sets n to a slice containing two nodes.
+func (n *Nodes) Set2(n1, n2 *Node) {
+	n.slice = &[]*Node{n1, n2}
+}
+
 // MoveNodes sets n to the contents of n2, then clears n2.
 func (n *Nodes) MoveNodes(n2 *Nodes) {
 	n.slice = n2.slice
@@ -564,15 +590,29 @@ func (n Nodes) Addr(i int) **Node {
 // Append appends entries to Nodes.
 // If a slice is passed in, this will take ownership of it.
 func (n *Nodes) Append(a ...*Node) {
+	if len(a) == 0 {
+		return
+	}
 	if n.slice == nil {
-		if len(a) > 0 {
-			n.slice = &a
-		}
+		n.slice = &a
 	} else {
 		*n.slice = append(*n.slice, a...)
 	}
 }
 
+// Prepend prepends entries to Nodes.
+// If a slice is passed in, this will take ownership of it.
+func (n *Nodes) Prepend(a ...*Node) {
+	if len(a) == 0 {
+		return
+	}
+	if n.slice == nil {
+		n.slice = &a
+	} else {
+		*n.slice = append(a, *n.slice...)
+	}
+}
+
 // AppendNodes appends the contents of *n2 to n, then clears n2.
 func (n *Nodes) AppendNodes(n2 *Nodes) {
 	switch {
diff --git a/src/cmd/compile/internal/gc/testdata/addressed_ssa.go b/src/cmd/compile/internal/gc/testdata/addressed.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/addressed_ssa.go
rename to src/cmd/compile/internal/gc/testdata/addressed.go
diff --git a/src/cmd/compile/internal/gc/testdata/append_ssa.go b/src/cmd/compile/internal/gc/testdata/append.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/append_ssa.go
rename to src/cmd/compile/internal/gc/testdata/append.go
diff --git a/src/cmd/compile/internal/gc/testdata/arith.go b/src/cmd/compile/internal/gc/testdata/arith.go
new file mode 100644
index 0000000..d850ce2
--- /dev/null
+++ b/src/cmd/compile/internal/gc/testdata/arith.go
@@ -0,0 +1,1020 @@
+// run
+
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Tests arithmetic expressions
+
+package main
+
+import "fmt"
+
+const (
+	y = 0x0fffFFFF
+)
+
+//go:noinline
+func lshNop1(x uint64) uint64 {
+	// two outer shifts should be removed
+	return (((x << 5) >> 2) << 2)
+}
+
+//go:noinline
+func lshNop2(x uint64) uint64 {
+	return (((x << 5) >> 2) << 3)
+}
+
+//go:noinline
+func lshNop3(x uint64) uint64 {
+	return (((x << 5) >> 2) << 6)
+}
+
+//go:noinline
+func lshNotNop(x uint64) uint64 {
+	// outer shift can't be removed
+	return (((x << 5) >> 2) << 1)
+}
+
+//go:noinline
+func rshNop1(x uint64) uint64 {
+	return (((x >> 5) << 2) >> 2)
+}
+
+//go:noinline
+func rshNop2(x uint64) uint64 {
+	return (((x >> 5) << 2) >> 3)
+}
+
+//go:noinline
+func rshNop3(x uint64) uint64 {
+	return (((x >> 5) << 2) >> 6)
+}
+
+//go:noinline
+func rshNotNop(x uint64) uint64 {
+	return (((x >> 5) << 2) >> 1)
+}
+
+func testShiftRemoval() {
+	allSet := ^uint64(0)
+	if want, got := uint64(0x7ffffffffffffff), rshNop1(allSet); want != got {
+		println("testShiftRemoval rshNop1 failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint64(0x3ffffffffffffff), rshNop2(allSet); want != got {
+		println("testShiftRemoval rshNop2 failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint64(0x7fffffffffffff), rshNop3(allSet); want != got {
+		println("testShiftRemoval rshNop3 failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint64(0xffffffffffffffe), rshNotNop(allSet); want != got {
+		println("testShiftRemoval rshNotNop failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint64(0xffffffffffffffe0), lshNop1(allSet); want != got {
+		println("testShiftRemoval lshNop1 failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint64(0xffffffffffffffc0), lshNop2(allSet); want != got {
+		println("testShiftRemoval lshNop2 failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint64(0xfffffffffffffe00), lshNop3(allSet); want != got {
+		println("testShiftRemoval lshNop3 failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint64(0x7ffffffffffffff0), lshNotNop(allSet); want != got {
+		println("testShiftRemoval lshNotNop failed, wanted", want, "got", got)
+		failed = true
+	}
+}
+
+//go:noinline
+func parseLE64(b []byte) uint64 {
+	// skip the first two bytes, and parse the remaining 8 as a uint64
+	return uint64(b[2]) | uint64(b[3])<<8 | uint64(b[4])<<16 | uint64(b[5])<<24 |
+		uint64(b[6])<<32 | uint64(b[7])<<40 | uint64(b[8])<<48 | uint64(b[9])<<56
+}
+
+//go:noinline
+func parseLE32(b []byte) uint32 {
+	return uint32(b[2]) | uint32(b[3])<<8 | uint32(b[4])<<16 | uint32(b[5])<<24
+}
+
+//go:noinline
+func parseLE16(b []byte) uint16 {
+	return uint16(b[2]) | uint16(b[3])<<8
+}
+
+// testLoadCombine tests for issue #14694 where load combining didn't respect the pointer offset.
+func testLoadCombine() {
+	testData := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}
+	if want, got := uint64(0x0908070605040302), parseLE64(testData); want != got {
+		println("testLoadCombine failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint32(0x05040302), parseLE32(testData); want != got {
+		println("testLoadCombine failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint16(0x0302), parseLE16(testData); want != got {
+		println("testLoadCombine failed, wanted", want, "got", got)
+		failed = true
+	}
+}
+
+var loadSymData = [...]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
+
+func testLoadSymCombine() {
+	w2 := uint16(0x0201)
+	g2 := uint16(loadSymData[0]) | uint16(loadSymData[1])<<8
+	if g2 != w2 {
+		println("testLoadSymCombine failed, wanted", w2, "got", g2)
+		failed = true
+	}
+	w4 := uint32(0x04030201)
+	g4 := uint32(loadSymData[0]) | uint32(loadSymData[1])<<8 |
+		uint32(loadSymData[2])<<16 | uint32(loadSymData[3])<<24
+	if g4 != w4 {
+		println("testLoadSymCombine failed, wanted", w4, "got", g4)
+		failed = true
+	}
+	w8 := uint64(0x0807060504030201)
+	g8 := uint64(loadSymData[0]) | uint64(loadSymData[1])<<8 |
+		uint64(loadSymData[2])<<16 | uint64(loadSymData[3])<<24 |
+		uint64(loadSymData[4])<<32 | uint64(loadSymData[5])<<40 |
+		uint64(loadSymData[6])<<48 | uint64(loadSymData[7])<<56
+	if g8 != w8 {
+		println("testLoadSymCombine failed, wanted", w8, "got", g8)
+		failed = true
+	}
+}
+
+//go:noinline
+func invalidAdd_ssa(x uint32) uint32 {
+	return x + y + y + y + y + y + y + y + y + y + y + y + y + y + y + y + y + y
+}
+
+//go:noinline
+func invalidSub_ssa(x uint32) uint32 {
+	return x - y - y - y - y - y - y - y - y - y - y - y - y - y - y - y - y - y
+}
+
+//go:noinline
+func invalidMul_ssa(x uint32) uint32 {
+	return x * y * y * y * y * y * y * y * y * y * y * y * y * y * y * y * y * y
+}
+
+// testLargeConst tests a situation where larger than 32 bit consts were passed to ADDL
+// causing an invalid instruction error.
+func testLargeConst() {
+	if want, got := uint32(268435440), invalidAdd_ssa(1); want != got {
+		println("testLargeConst add failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint32(4026531858), invalidSub_ssa(1); want != got {
+		println("testLargeConst sub failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint32(268435455), invalidMul_ssa(1); want != got {
+		println("testLargeConst mul failed, wanted", want, "got", got)
+		failed = true
+	}
+}
+
+// testArithRshConst ensures that "const >> const" right shifts correctly perform
+// sign extension on the lhs constant
+func testArithRshConst() {
+	wantu := uint64(0x4000000000000000)
+	if got := arithRshuConst_ssa(); got != wantu {
+		println("arithRshuConst failed, wanted", wantu, "got", got)
+		failed = true
+	}
+
+	wants := int64(-0x4000000000000000)
+	if got := arithRshConst_ssa(); got != wants {
+		println("arithRshuConst failed, wanted", wants, "got", got)
+		failed = true
+	}
+}
+
+//go:noinline
+func arithRshuConst_ssa() uint64 {
+	y := uint64(0x8000000000000001)
+	z := uint64(1)
+	return uint64(y >> z)
+}
+
+//go:noinline
+func arithRshConst_ssa() int64 {
+	y := int64(-0x8000000000000000)
+	z := uint64(1)
+	return int64(y >> z)
+}
+
+//go:noinline
+func arithConstShift_ssa(x int64) int64 {
+	return x >> 100
+}
+
+// testArithConstShift tests that right shift by large constants preserve
+// the sign of the input.
+func testArithConstShift() {
+	want := int64(-1)
+	if got := arithConstShift_ssa(-1); want != got {
+		println("arithConstShift_ssa(-1) failed, wanted", want, "got", got)
+		failed = true
+	}
+	want = 0
+	if got := arithConstShift_ssa(1); want != got {
+		println("arithConstShift_ssa(1) failed, wanted", want, "got", got)
+		failed = true
+	}
+}
+
+// overflowConstShift_ssa verifes that constant folding for shift
+// doesn't wrap (i.e. x << MAX_INT << 1 doesn't get folded to x << 0).
+//go:noinline
+func overflowConstShift64_ssa(x int64) int64 {
+	return x << uint64(0xffffffffffffffff) << uint64(1)
+}
+
+//go:noinline
+func overflowConstShift32_ssa(x int64) int32 {
+	return int32(x) << uint32(0xffffffff) << uint32(1)
+}
+
+//go:noinline
+func overflowConstShift16_ssa(x int64) int16 {
+	return int16(x) << uint16(0xffff) << uint16(1)
+}
+
+//go:noinline
+func overflowConstShift8_ssa(x int64) int8 {
+	return int8(x) << uint8(0xff) << uint8(1)
+}
+
+func testOverflowConstShift() {
+	want := int64(0)
+	for x := int64(-127); x < int64(127); x++ {
+		got := overflowConstShift64_ssa(x)
+		if want != got {
+			fmt.Printf("overflowShift64 failed, wanted %d got %d\n", want, got)
+		}
+		got = int64(overflowConstShift32_ssa(x))
+		if want != got {
+			fmt.Printf("overflowShift32 failed, wanted %d got %d\n", want, got)
+		}
+		got = int64(overflowConstShift16_ssa(x))
+		if want != got {
+			fmt.Printf("overflowShift16 failed, wanted %d got %d\n", want, got)
+		}
+		got = int64(overflowConstShift8_ssa(x))
+		if want != got {
+			fmt.Printf("overflowShift8 failed, wanted %d got %d\n", want, got)
+		}
+	}
+}
+
+// test64BitConstMult tests that rewrite rules don't fold 64 bit constants
+// into multiply instructions.
+func test64BitConstMult() {
+	want := int64(103079215109)
+	if got := test64BitConstMult_ssa(1, 2); want != got {
+		println("test64BitConstMult failed, wanted", want, "got", got)
+		failed = true
+	}
+}
+
+//go:noinline
+func test64BitConstMult_ssa(a, b int64) int64 {
+	return 34359738369*a + b*34359738370
+}
+
+// test64BitConstAdd tests that rewrite rules don't fold 64 bit constants
+// into add instructions.
+func test64BitConstAdd() {
+	want := int64(3567671782835376650)
+	if got := test64BitConstAdd_ssa(1, 2); want != got {
+		println("test64BitConstAdd failed, wanted", want, "got", got)
+		failed = true
+	}
+}
+
+//go:noinline
+func test64BitConstAdd_ssa(a, b int64) int64 {
+	return a + 575815584948629622 + b + 2991856197886747025
+}
+
+// testRegallocCVSpill tests that regalloc spills a value whose last use is the
+// current value.
+func testRegallocCVSpill() {
+	want := int8(-9)
+	if got := testRegallocCVSpill_ssa(1, 2, 3, 4); want != got {
+		println("testRegallocCVSpill failed, wanted", want, "got", got)
+		failed = true
+	}
+}
+
+//go:noinline
+func testRegallocCVSpill_ssa(a, b, c, d int8) int8 {
+	return a + -32 + b + 63*c*-87*d
+}
+
+func testBitwiseLogic() {
+	a, b := uint32(57623283), uint32(1314713839)
+	if want, got := uint32(38551779), testBitwiseAnd_ssa(a, b); want != got {
+		println("testBitwiseAnd failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint32(1333785343), testBitwiseOr_ssa(a, b); want != got {
+		println("testBitwiseOr failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint32(1295233564), testBitwiseXor_ssa(a, b); want != got {
+		println("testBitwiseXor failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := int32(832), testBitwiseLsh_ssa(13, 4, 2); want != got {
+		println("testBitwiseLsh failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := int32(0), testBitwiseLsh_ssa(13, 25, 15); want != got {
+		println("testBitwiseLsh failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := int32(0), testBitwiseLsh_ssa(-13, 25, 15); want != got {
+		println("testBitwiseLsh failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := int32(-13), testBitwiseRsh_ssa(-832, 4, 2); want != got {
+		println("testBitwiseRsh failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := int32(0), testBitwiseRsh_ssa(13, 25, 15); want != got {
+		println("testBitwiseRsh failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := int32(-1), testBitwiseRsh_ssa(-13, 25, 15); want != got {
+		println("testBitwiseRsh failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint32(0x3ffffff), testBitwiseRshU_ssa(0xffffffff, 4, 2); want != got {
+		println("testBitwiseRshU failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint32(0), testBitwiseRshU_ssa(13, 25, 15); want != got {
+		println("testBitwiseRshU failed, wanted", want, "got", got)
+		failed = true
+	}
+	if want, got := uint32(0), testBitwiseRshU_ssa(0x8aaaaaaa, 25, 15); want != got {
+		println("testBitwiseRshU failed, wanted", want, "got", got)
+		failed = true
+	}
+}
+
+//go:noinline
+func testBitwiseAnd_ssa(a, b uint32) uint32 {
+	return a & b
+}
+
+//go:noinline
+func testBitwiseOr_ssa(a, b uint32) uint32 {
+	return a | b
+}
+
+//go:noinline
+func testBitwiseXor_ssa(a, b uint32) uint32 {
+	return a ^ b
+}
+
+//go:noinline
+func testBitwiseLsh_ssa(a int32, b, c uint32) int32 {
+	return a << b << c
+}
+
+//go:noinline
+func testBitwiseRsh_ssa(a int32, b, c uint32) int32 {
+	return a >> b >> c
+}
+
+//go:noinline
+func testBitwiseRshU_ssa(a uint32, b, c uint32) uint32 {
+	return a >> b >> c
+}
+
+//go:noinline
+func testShiftCX_ssa() int {
+	v1 := uint8(3)
+	v4 := (v1 * v1) ^ v1 | v1 - v1 - v1&v1 ^ uint8(3+2) + v1*1>>0 - v1 | 1 | v1<<(2*3|0-0*0^1)
+	v5 := v4>>(3-0-uint(3)) | v1 | v1 + v1 ^ v4<<(0+1|3&1)<<(uint64(1)<<0*2*0<<0) ^ v1
+	v6 := v5 ^ (v1+v1)*v1 | v1 | v1*v1>>(v1&v1)>>(uint(1)<<0*uint(3)>>1)*v1<<2*v1<<v1 - v1>>2 | (v4 - v1) ^ v1 + v1 ^ v1>>1 | v1 + v1 - v1 ^ v1
+	v7 := v6 & v5 << 0
+	v1++
+	v11 := 2&1 ^ 0 + 3 | int(0^0)<<1>>(1*0*3) ^ 0*0 ^ 3&0*3&3 ^ 3*3 ^ 1 ^ int(2)<<(2*3) + 2 | 2 | 2 ^ 2 + 1 | 3 | 0 ^ int(1)>>1 ^ 2 // int
+	v7--
+	return int(uint64(2*1)<<(3-2)<<uint(3>>v7)-2)&v11 | v11 - int(2)<<0>>(2-1)*(v11*0&v11<<1<<(uint8(2)+v4))
+}
+
+func testShiftCX() {
+	want := 141
+	if got := testShiftCX_ssa(); want != got {
+		println("testShiftCX failed, wanted", want, "got", got)
+		failed = true
+	}
+}
+
+// testSubqToNegq ensures that the SUBQ -> NEGQ translation works correctly.
+func testSubqToNegq() {
+	want := int64(-318294940372190156)
+	if got := testSubqToNegq_ssa(1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2); want != got {
+		println("testSubqToNegq failed, wanted", want, "got", got)
+		failed = true
+	}
+}
+
+//go:noinline
+func testSubqToNegq_ssa(a, b, c, d, e, f, g, h, i, j, k int64) int64 {
+	return a + 8207351403619448057 - b - 1779494519303207690 + c*8810076340510052032*d - 4465874067674546219 - e*4361839741470334295 - f + 8688847565426072650*g*8065564729145417479
+}
+
+func testOcom() {
+	want1, want2 := int32(0x55555555), int32(-0x55555556)
+	if got1, got2 := testOcom_ssa(0x55555555, 0x55555555); want1 != got1 || want2 != got2 {
+		println("testSubqToNegq failed, wanted", want1, "and", want2,
+			"got", got1, "and", got2)
+		failed = true
+	}
+}
+
+//go:noinline
+func testOcom_ssa(a, b int32) (int32, int32) {
+	return ^^^^a, ^^^^^b
+}
+
+func lrot1_ssa(w uint8, x uint16, y uint32, z uint64) (a uint8, b uint16, c uint32, d uint64) {
+	a = (w << 5) | (w >> 3)
+	b = (x << 13) | (x >> 3)
+	c = (y << 29) | (y >> 3)
+	d = (z << 61) | (z >> 3)
+	return
+}
+
+//go:noinline
+func lrot2_ssa(w, n uint32) uint32 {
+	// Want to be sure that a "rotate by 32" which
+	// is really 0 | (w >> 0) == w
+	// is correctly compiled.
+	return (w << n) | (w >> (32 - n))
+}
+
+//go:noinline
+func lrot3_ssa(w uint32) uint32 {
+	// Want to be sure that a "rotate by 32" which
+	// is really 0 | (w >> 0) == w
+	// is correctly compiled.
+	return (w << 32) | (w >> (32 - 32))
+}
+
+func testLrot() {
+	wantA, wantB, wantC, wantD := uint8(0xe1), uint16(0xe001),
+		uint32(0xe0000001), uint64(0xe000000000000001)
+	a, b, c, d := lrot1_ssa(0xf, 0xf, 0xf, 0xf)
+	if a != wantA || b != wantB || c != wantC || d != wantD {
+		println("lrot1_ssa(0xf, 0xf, 0xf, 0xf)=",
+			wantA, wantB, wantC, wantD, ", got", a, b, c, d)
+		failed = true
+	}
+	x := lrot2_ssa(0xb0000001, 32)
+	wantX := uint32(0xb0000001)
+	if x != wantX {
+		println("lrot2_ssa(0xb0000001, 32)=",
+			wantX, ", got", x)
+		failed = true
+	}
+	x = lrot3_ssa(0xb0000001)
+	if x != wantX {
+		println("lrot3_ssa(0xb0000001)=",
+			wantX, ", got", x)
+		failed = true
+	}
+
+}
+
+//go:noinline
+func sub1_ssa() uint64 {
+	v1 := uint64(3) // uint64
+	return v1*v1 - (v1&v1)&v1
+}
+
+//go:noinline
+func sub2_ssa() uint8 {
+	v1 := uint8(0)
+	v3 := v1 + v1 + v1 ^ v1 | 3 + v1 ^ v1 | v1 ^ v1
+	v1-- // dev.ssa doesn't see this one
+	return v1 ^ v1*v1 - v3
+}
+
+func testSubConst() {
+	x1 := sub1_ssa()
+	want1 := uint64(6)
+	if x1 != want1 {
+		println("sub1_ssa()=", want1, ", got", x1)
+		failed = true
+	}
+	x2 := sub2_ssa()
+	want2 := uint8(251)
+	if x2 != want2 {
+		println("sub2_ssa()=", want2, ", got", x2)
+		failed = true
+	}
+}
+
+//go:noinline
+func orPhi_ssa(a bool, x int) int {
+	v := 0
+	if a {
+		v = -1
+	} else {
+		v = -1
+	}
+	return x | v
+}
+
+func testOrPhi() {
+	if want, got := -1, orPhi_ssa(true, 4); got != want {
+		println("orPhi_ssa(true, 4)=", got, " want ", want)
+	}
+	if want, got := -1, orPhi_ssa(false, 0); got != want {
+		println("orPhi_ssa(false, 0)=", got, " want ", want)
+	}
+}
+
+//go:noinline
+func addshiftLL_ssa(a, b uint32) uint32 {
+	return a + b<<3
+}
+
+//go:noinline
+func subshiftLL_ssa(a, b uint32) uint32 {
+	return a - b<<3
+}
+
+//go:noinline
+func rsbshiftLL_ssa(a, b uint32) uint32 {
+	return a<<3 - b
+}
+
+//go:noinline
+func andshiftLL_ssa(a, b uint32) uint32 {
+	return a & (b << 3)
+}
+
+//go:noinline
+func orshiftLL_ssa(a, b uint32) uint32 {
+	return a | b<<3
+}
+
+//go:noinline
+func xorshiftLL_ssa(a, b uint32) uint32 {
+	return a ^ b<<3
+}
+
+//go:noinline
+func bicshiftLL_ssa(a, b uint32) uint32 {
+	return a &^ (b << 3)
+}
+
+//go:noinline
+func notshiftLL_ssa(a uint32) uint32 {
+	return ^(a << 3)
+}
+
+//go:noinline
+func addshiftRL_ssa(a, b uint32) uint32 {
+	return a + b>>3
+}
+
+//go:noinline
+func subshiftRL_ssa(a, b uint32) uint32 {
+	return a - b>>3
+}
+
+//go:noinline
+func rsbshiftRL_ssa(a, b uint32) uint32 {
+	return a>>3 - b
+}
+
+//go:noinline
+func andshiftRL_ssa(a, b uint32) uint32 {
+	return a & (b >> 3)
+}
+
+//go:noinline
+func orshiftRL_ssa(a, b uint32) uint32 {
+	return a | b>>3
+}
+
+//go:noinline
+func xorshiftRL_ssa(a, b uint32) uint32 {
+	return a ^ b>>3
+}
+
+//go:noinline
+func bicshiftRL_ssa(a, b uint32) uint32 {
+	return a &^ (b >> 3)
+}
+
+//go:noinline
+func notshiftRL_ssa(a uint32) uint32 {
+	return ^(a >> 3)
+}
+
+//go:noinline
+func addshiftRA_ssa(a, b int32) int32 {
+	return a + b>>3
+}
+
+//go:noinline
+func subshiftRA_ssa(a, b int32) int32 {
+	return a - b>>3
+}
+
+//go:noinline
+func rsbshiftRA_ssa(a, b int32) int32 {
+	return a>>3 - b
+}
+
+//go:noinline
+func andshiftRA_ssa(a, b int32) int32 {
+	return a & (b >> 3)
+}
+
+//go:noinline
+func orshiftRA_ssa(a, b int32) int32 {
+	return a | b>>3
+}
+
+//go:noinline
+func xorshiftRA_ssa(a, b int32) int32 {
+	return a ^ b>>3
+}
+
+//go:noinline
+func bicshiftRA_ssa(a, b int32) int32 {
+	return a &^ (b >> 3)
+}
+
+//go:noinline
+func notshiftRA_ssa(a int32) int32 {
+	return ^(a >> 3)
+}
+
+//go:noinline
+func addshiftLLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a + b<<s
+}
+
+//go:noinline
+func subshiftLLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a - b<<s
+}
+
+//go:noinline
+func rsbshiftLLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a<<s - b
+}
+
+//go:noinline
+func andshiftLLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a & (b << s)
+}
+
+//go:noinline
+func orshiftLLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a | b<<s
+}
+
+//go:noinline
+func xorshiftLLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a ^ b<<s
+}
+
+//go:noinline
+func bicshiftLLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a &^ (b << s)
+}
+
+//go:noinline
+func notshiftLLreg_ssa(a uint32, s uint8) uint32 {
+	return ^(a << s)
+}
+
+//go:noinline
+func addshiftRLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a + b>>s
+}
+
+//go:noinline
+func subshiftRLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a - b>>s
+}
+
+//go:noinline
+func rsbshiftRLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a>>s - b
+}
+
+//go:noinline
+func andshiftRLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a & (b >> s)
+}
+
+//go:noinline
+func orshiftRLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a | b>>s
+}
+
+//go:noinline
+func xorshiftRLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a ^ b>>s
+}
+
+//go:noinline
+func bicshiftRLreg_ssa(a, b uint32, s uint8) uint32 {
+	return a &^ (b >> s)
+}
+
+//go:noinline
+func notshiftRLreg_ssa(a uint32, s uint8) uint32 {
+	return ^(a >> s)
+}
+
+//go:noinline
+func addshiftRAreg_ssa(a, b int32, s uint8) int32 {
+	return a + b>>s
+}
+
+//go:noinline
+func subshiftRAreg_ssa(a, b int32, s uint8) int32 {
+	return a - b>>s
+}
+
+//go:noinline
+func rsbshiftRAreg_ssa(a, b int32, s uint8) int32 {
+	return a>>s - b
+}
+
+//go:noinline
+func andshiftRAreg_ssa(a, b int32, s uint8) int32 {
+	return a & (b >> s)
+}
+
+//go:noinline
+func orshiftRAreg_ssa(a, b int32, s uint8) int32 {
+	return a | b>>s
+}
+
+//go:noinline
+func xorshiftRAreg_ssa(a, b int32, s uint8) int32 {
+	return a ^ b>>s
+}
+
+//go:noinline
+func bicshiftRAreg_ssa(a, b int32, s uint8) int32 {
+	return a &^ (b >> s)
+}
+
+//go:noinline
+func notshiftRAreg_ssa(a int32, s uint8) int32 {
+	return ^(a >> s)
+}
+
+// test ARM shifted ops
+func testShiftedOps() {
+	a, b := uint32(10), uint32(42)
+	if want, got := a+b<<3, addshiftLL_ssa(a, b); got != want {
+		println("addshiftLL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a-b<<3, subshiftLL_ssa(a, b); got != want {
+		println("subshiftLL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a<<3-b, rsbshiftLL_ssa(a, b); got != want {
+		println("rsbshiftLL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a&(b<<3), andshiftLL_ssa(a, b); got != want {
+		println("andshiftLL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a|b<<3, orshiftLL_ssa(a, b); got != want {
+		println("orshiftLL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a^b<<3, xorshiftLL_ssa(a, b); got != want {
+		println("xorshiftLL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a&^(b<<3), bicshiftLL_ssa(a, b); got != want {
+		println("bicshiftLL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := ^(a << 3), notshiftLL_ssa(a); got != want {
+		println("notshiftLL_ssa(10) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a+b>>3, addshiftRL_ssa(a, b); got != want {
+		println("addshiftRL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a-b>>3, subshiftRL_ssa(a, b); got != want {
+		println("subshiftRL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a>>3-b, rsbshiftRL_ssa(a, b); got != want {
+		println("rsbshiftRL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a&(b>>3), andshiftRL_ssa(a, b); got != want {
+		println("andshiftRL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a|b>>3, orshiftRL_ssa(a, b); got != want {
+		println("orshiftRL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a^b>>3, xorshiftRL_ssa(a, b); got != want {
+		println("xorshiftRL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a&^(b>>3), bicshiftRL_ssa(a, b); got != want {
+		println("bicshiftRL_ssa(10, 42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := ^(a >> 3), notshiftRL_ssa(a); got != want {
+		println("notshiftRL_ssa(10) =", got, " want ", want)
+		failed = true
+	}
+	c, d := int32(10), int32(-42)
+	if want, got := c+d>>3, addshiftRA_ssa(c, d); got != want {
+		println("addshiftRA_ssa(10, -42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c-d>>3, subshiftRA_ssa(c, d); got != want {
+		println("subshiftRA_ssa(10, -42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c>>3-d, rsbshiftRA_ssa(c, d); got != want {
+		println("rsbshiftRA_ssa(10, -42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c&(d>>3), andshiftRA_ssa(c, d); got != want {
+		println("andshiftRA_ssa(10, -42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c|d>>3, orshiftRA_ssa(c, d); got != want {
+		println("orshiftRA_ssa(10, -42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c^d>>3, xorshiftRA_ssa(c, d); got != want {
+		println("xorshiftRA_ssa(10, -42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c&^(d>>3), bicshiftRA_ssa(c, d); got != want {
+		println("bicshiftRA_ssa(10, -42) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := ^(d >> 3), notshiftRA_ssa(d); got != want {
+		println("notshiftRA_ssa(-42) =", got, " want ", want)
+		failed = true
+	}
+	s := uint8(3)
+	if want, got := a+b<<s, addshiftLLreg_ssa(a, b, s); got != want {
+		println("addshiftLLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a-b<<s, subshiftLLreg_ssa(a, b, s); got != want {
+		println("subshiftLLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a<<s-b, rsbshiftLLreg_ssa(a, b, s); got != want {
+		println("rsbshiftLLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a&(b<<s), andshiftLLreg_ssa(a, b, s); got != want {
+		println("andshiftLLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a|b<<s, orshiftLLreg_ssa(a, b, s); got != want {
+		println("orshiftLLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a^b<<s, xorshiftLLreg_ssa(a, b, s); got != want {
+		println("xorshiftLLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a&^(b<<s), bicshiftLLreg_ssa(a, b, s); got != want {
+		println("bicshiftLLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := ^(a << s), notshiftLLreg_ssa(a, s); got != want {
+		println("notshiftLLreg_ssa(10) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a+b>>s, addshiftRLreg_ssa(a, b, s); got != want {
+		println("addshiftRLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a-b>>s, subshiftRLreg_ssa(a, b, s); got != want {
+		println("subshiftRLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a>>s-b, rsbshiftRLreg_ssa(a, b, s); got != want {
+		println("rsbshiftRLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a&(b>>s), andshiftRLreg_ssa(a, b, s); got != want {
+		println("andshiftRLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a|b>>s, orshiftRLreg_ssa(a, b, s); got != want {
+		println("orshiftRLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a^b>>s, xorshiftRLreg_ssa(a, b, s); got != want {
+		println("xorshiftRLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := a&^(b>>s), bicshiftRLreg_ssa(a, b, s); got != want {
+		println("bicshiftRLreg_ssa(10, 42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := ^(a >> s), notshiftRLreg_ssa(a, s); got != want {
+		println("notshiftRLreg_ssa(10) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c+d>>s, addshiftRAreg_ssa(c, d, s); got != want {
+		println("addshiftRAreg_ssa(10, -42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c-d>>s, subshiftRAreg_ssa(c, d, s); got != want {
+		println("subshiftRAreg_ssa(10, -42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c>>s-d, rsbshiftRAreg_ssa(c, d, s); got != want {
+		println("rsbshiftRAreg_ssa(10, -42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c&(d>>s), andshiftRAreg_ssa(c, d, s); got != want {
+		println("andshiftRAreg_ssa(10, -42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c|d>>s, orshiftRAreg_ssa(c, d, s); got != want {
+		println("orshiftRAreg_ssa(10, -42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c^d>>s, xorshiftRAreg_ssa(c, d, s); got != want {
+		println("xorshiftRAreg_ssa(10, -42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := c&^(d>>s), bicshiftRAreg_ssa(c, d, s); got != want {
+		println("bicshiftRAreg_ssa(10, -42, 3) =", got, " want ", want)
+		failed = true
+	}
+	if want, got := ^(d >> s), notshiftRAreg_ssa(d, s); got != want {
+		println("notshiftRAreg_ssa(-42, 3) =", got, " want ", want)
+		failed = true
+	}
+}
+
+var failed = false
+
+func main() {
+
+	test64BitConstMult()
+	test64BitConstAdd()
+	testRegallocCVSpill()
+	testSubqToNegq()
+	testBitwiseLogic()
+	testOcom()
+	testLrot()
+	testShiftCX()
+	testSubConst()
+	testOverflowConstShift()
+	testArithConstShift()
+	testArithRshConst()
+	testLargeConst()
+	testLoadCombine()
+	testLoadSymCombine()
+	testShiftRemoval()
+	testShiftedOps()
+
+	if failed {
+		panic("failed")
+	}
+}
diff --git a/src/cmd/compile/internal/gc/testdata/arithBoundary_ssa.go b/src/cmd/compile/internal/gc/testdata/arithBoundary.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/arithBoundary_ssa.go
rename to src/cmd/compile/internal/gc/testdata/arithBoundary.go
diff --git a/src/cmd/compile/internal/gc/testdata/arithConst_ssa.go b/src/cmd/compile/internal/gc/testdata/arithConst.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/arithConst_ssa.go
rename to src/cmd/compile/internal/gc/testdata/arithConst.go
diff --git a/src/cmd/compile/internal/gc/testdata/arith_ssa.go b/src/cmd/compile/internal/gc/testdata/arith_ssa.go
deleted file mode 100644
index 7c82bbd..0000000
--- a/src/cmd/compile/internal/gc/testdata/arith_ssa.go
+++ /dev/null
@@ -1,580 +0,0 @@
-// run
-
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Tests arithmetic expressions
-
-package main
-
-import "fmt"
-
-const (
-	y = 0x0fffFFFF
-)
-
-//go:noinline
-func lshNop1(x uint64) uint64 {
-	// two outer shifts should be removed
-	return (((x << 5) >> 2) << 2)
-}
-
-//go:noinline
-func lshNop2(x uint64) uint64 {
-	return (((x << 5) >> 2) << 3)
-}
-
-//go:noinline
-func lshNop3(x uint64) uint64 {
-	return (((x << 5) >> 2) << 6)
-}
-
-//go:noinline
-func lshNotNop(x uint64) uint64 {
-	// outer shift can't be removed
-	return (((x << 5) >> 2) << 1)
-}
-
-//go:noinline
-func rshNop1(x uint64) uint64 {
-	return (((x >> 5) << 2) >> 2)
-}
-
-//go:noinline
-func rshNop2(x uint64) uint64 {
-	return (((x >> 5) << 2) >> 3)
-}
-
-//go:noinline
-func rshNop3(x uint64) uint64 {
-	return (((x >> 5) << 2) >> 6)
-}
-
-//go:noinline
-func rshNotNop(x uint64) uint64 {
-	return (((x >> 5) << 2) >> 1)
-}
-
-func testShiftRemoval() {
-	allSet := ^uint64(0)
-	if want, got := uint64(0x7ffffffffffffff), rshNop1(allSet); want != got {
-		println("testShiftRemoval rshNop1 failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint64(0x3ffffffffffffff), rshNop2(allSet); want != got {
-		println("testShiftRemoval rshNop2 failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint64(0x7fffffffffffff), rshNop3(allSet); want != got {
-		println("testShiftRemoval rshNop3 failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint64(0xffffffffffffffe), rshNotNop(allSet); want != got {
-		println("testShiftRemoval rshNotNop failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint64(0xffffffffffffffe0), lshNop1(allSet); want != got {
-		println("testShiftRemoval lshNop1 failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint64(0xffffffffffffffc0), lshNop2(allSet); want != got {
-		println("testShiftRemoval lshNop2 failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint64(0xfffffffffffffe00), lshNop3(allSet); want != got {
-		println("testShiftRemoval lshNop3 failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint64(0x7ffffffffffffff0), lshNotNop(allSet); want != got {
-		println("testShiftRemoval lshNotNop failed, wanted", want, "got", got)
-		failed = true
-	}
-}
-
-//go:noinline
-func parseLE64(b []byte) uint64 {
-	// skip the first two bytes, and parse the remaining 8 as a uint64
-	return uint64(b[2]) | uint64(b[3])<<8 | uint64(b[4])<<16 | uint64(b[5])<<24 |
-		uint64(b[6])<<32 | uint64(b[7])<<40 | uint64(b[8])<<48 | uint64(b[9])<<56
-}
-
-//go:noinline
-func parseLE32(b []byte) uint32 {
-	return uint32(b[2]) | uint32(b[3])<<8 | uint32(b[4])<<16 | uint32(b[5])<<24
-}
-
-//go:noinline
-func parseLE16(b []byte) uint16 {
-	return uint16(b[2]) | uint16(b[3])<<8
-}
-
-// testLoadCombine tests for issue #14694 where load combining didn't respect the pointer offset.
-func testLoadCombine() {
-	testData := []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}
-	if want, got := uint64(0x0908070605040302), parseLE64(testData); want != got {
-		println("testLoadCombine failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint32(0x05040302), parseLE32(testData); want != got {
-		println("testLoadCombine failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint16(0x0302), parseLE16(testData); want != got {
-		println("testLoadCombine failed, wanted", want, "got", got)
-		failed = true
-	}
-}
-
-var loadSymData = [...]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
-
-func testLoadSymCombine() {
-	w2 := uint16(0x0201)
-	g2 := uint16(loadSymData[0]) | uint16(loadSymData[1])<<8
-	if g2 != w2 {
-		println("testLoadSymCombine failed, wanted", w2, "got", g2)
-		failed = true
-	}
-	w4 := uint32(0x04030201)
-	g4 := uint32(loadSymData[0]) | uint32(loadSymData[1])<<8 |
-		uint32(loadSymData[2])<<16 | uint32(loadSymData[3])<<24
-	if g4 != w4 {
-		println("testLoadSymCombine failed, wanted", w4, "got", g4)
-		failed = true
-	}
-	w8 := uint64(0x0807060504030201)
-	g8 := uint64(loadSymData[0]) | uint64(loadSymData[1])<<8 |
-		uint64(loadSymData[2])<<16 | uint64(loadSymData[3])<<24 |
-		uint64(loadSymData[4])<<32 | uint64(loadSymData[5])<<40 |
-		uint64(loadSymData[6])<<48 | uint64(loadSymData[7])<<56
-	if g8 != w8 {
-		println("testLoadSymCombine failed, wanted", w8, "got", g8)
-		failed = true
-	}
-}
-
-//go:noinline
-func invalidAdd_ssa(x uint32) uint32 {
-	return x + y + y + y + y + y + y + y + y + y + y + y + y + y + y + y + y + y
-}
-
-//go:noinline
-func invalidSub_ssa(x uint32) uint32 {
-	return x - y - y - y - y - y - y - y - y - y - y - y - y - y - y - y - y - y
-}
-
-//go:noinline
-func invalidMul_ssa(x uint32) uint32 {
-	return x * y * y * y * y * y * y * y * y * y * y * y * y * y * y * y * y * y
-}
-
-// testLargeConst tests a situation where larger than 32 bit consts were passed to ADDL
-// causing an invalid instruction error.
-func testLargeConst() {
-	if want, got := uint32(268435440), invalidAdd_ssa(1); want != got {
-		println("testLargeConst add failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint32(4026531858), invalidSub_ssa(1); want != got {
-		println("testLargeConst sub failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint32(268435455), invalidMul_ssa(1); want != got {
-		println("testLargeConst mul failed, wanted", want, "got", got)
-		failed = true
-	}
-}
-
-// testArithRshConst ensures that "const >> const" right shifts correctly perform
-// sign extension on the lhs constant
-func testArithRshConst() {
-	wantu := uint64(0x4000000000000000)
-	if got := arithRshuConst_ssa(); got != wantu {
-		println("arithRshuConst failed, wanted", wantu, "got", got)
-		failed = true
-	}
-
-	wants := int64(-0x4000000000000000)
-	if got := arithRshConst_ssa(); got != wants {
-		println("arithRshuConst failed, wanted", wants, "got", got)
-		failed = true
-	}
-}
-
-//go:noinline
-func arithRshuConst_ssa() uint64 {
-	y := uint64(0x8000000000000001)
-	z := uint64(1)
-	return uint64(y >> z)
-}
-
-//go:noinline
-func arithRshConst_ssa() int64 {
-	y := int64(-0x8000000000000000)
-	z := uint64(1)
-	return int64(y >> z)
-}
-
-//go:noinline
-func arithConstShift_ssa(x int64) int64 {
-	return x >> 100
-}
-
-// testArithConstShift tests that right shift by large constants preserve
-// the sign of the input.
-func testArithConstShift() {
-	want := int64(-1)
-	if got := arithConstShift_ssa(-1); want != got {
-		println("arithConstShift_ssa(-1) failed, wanted", want, "got", got)
-		failed = true
-	}
-	want = 0
-	if got := arithConstShift_ssa(1); want != got {
-		println("arithConstShift_ssa(1) failed, wanted", want, "got", got)
-		failed = true
-	}
-}
-
-// overflowConstShift_ssa verifes that constant folding for shift
-// doesn't wrap (i.e. x << MAX_INT << 1 doesn't get folded to x << 0).
-//go:noinline
-func overflowConstShift64_ssa(x int64) int64 {
-	return x << uint64(0xffffffffffffffff) << uint64(1)
-}
-
-//go:noinline
-func overflowConstShift32_ssa(x int64) int32 {
-	return int32(x) << uint32(0xffffffff) << uint32(1)
-}
-
-//go:noinline
-func overflowConstShift16_ssa(x int64) int16 {
-	return int16(x) << uint16(0xffff) << uint16(1)
-}
-
-//go:noinline
-func overflowConstShift8_ssa(x int64) int8 {
-	return int8(x) << uint8(0xff) << uint8(1)
-}
-
-func testOverflowConstShift() {
-	want := int64(0)
-	for x := int64(-127); x < int64(127); x++ {
-		got := overflowConstShift64_ssa(x)
-		if want != got {
-			fmt.Printf("overflowShift64 failed, wanted %d got %d\n", want, got)
-		}
-		got = int64(overflowConstShift32_ssa(x))
-		if want != got {
-			fmt.Printf("overflowShift32 failed, wanted %d got %d\n", want, got)
-		}
-		got = int64(overflowConstShift16_ssa(x))
-		if want != got {
-			fmt.Printf("overflowShift16 failed, wanted %d got %d\n", want, got)
-		}
-		got = int64(overflowConstShift8_ssa(x))
-		if want != got {
-			fmt.Printf("overflowShift8 failed, wanted %d got %d\n", want, got)
-		}
-	}
-}
-
-// test64BitConstMult tests that rewrite rules don't fold 64 bit constants
-// into multiply instructions.
-func test64BitConstMult() {
-	want := int64(103079215109)
-	if got := test64BitConstMult_ssa(1, 2); want != got {
-		println("test64BitConstMult failed, wanted", want, "got", got)
-		failed = true
-	}
-}
-
-//go:noinline
-func test64BitConstMult_ssa(a, b int64) int64 {
-	return 34359738369*a + b*34359738370
-}
-
-// test64BitConstAdd tests that rewrite rules don't fold 64 bit constants
-// into add instructions.
-func test64BitConstAdd() {
-	want := int64(3567671782835376650)
-	if got := test64BitConstAdd_ssa(1, 2); want != got {
-		println("test64BitConstAdd failed, wanted", want, "got", got)
-		failed = true
-	}
-}
-
-//go:noinline
-func test64BitConstAdd_ssa(a, b int64) int64 {
-	return a + 575815584948629622 + b + 2991856197886747025
-}
-
-// testRegallocCVSpill tests that regalloc spills a value whose last use is the
-// current value.
-func testRegallocCVSpill() {
-	want := int8(-9)
-	if got := testRegallocCVSpill_ssa(1, 2, 3, 4); want != got {
-		println("testRegallocCVSpill failed, wanted", want, "got", got)
-		failed = true
-	}
-}
-
-//go:noinline
-func testRegallocCVSpill_ssa(a, b, c, d int8) int8 {
-	return a + -32 + b + 63*c*-87*d
-}
-
-func testBitwiseLogic() {
-	a, b := uint32(57623283), uint32(1314713839)
-	if want, got := uint32(38551779), testBitwiseAnd_ssa(a, b); want != got {
-		println("testBitwiseAnd failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint32(1333785343), testBitwiseOr_ssa(a, b); want != got {
-		println("testBitwiseOr failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint32(1295233564), testBitwiseXor_ssa(a, b); want != got {
-		println("testBitwiseXor failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := int32(832), testBitwiseLsh_ssa(13, 4, 2); want != got {
-		println("testBitwiseLsh failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := int32(0), testBitwiseLsh_ssa(13, 25, 15); want != got {
-		println("testBitwiseLsh failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := int32(0), testBitwiseLsh_ssa(-13, 25, 15); want != got {
-		println("testBitwiseLsh failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := int32(-13), testBitwiseRsh_ssa(-832, 4, 2); want != got {
-		println("testBitwiseRsh failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := int32(0), testBitwiseRsh_ssa(13, 25, 15); want != got {
-		println("testBitwiseRsh failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := int32(-1), testBitwiseRsh_ssa(-13, 25, 15); want != got {
-		println("testBitwiseRsh failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint32(0x3ffffff), testBitwiseRshU_ssa(0xffffffff, 4, 2); want != got {
-		println("testBitwiseRshU failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint32(0), testBitwiseRshU_ssa(13, 25, 15); want != got {
-		println("testBitwiseRshU failed, wanted", want, "got", got)
-		failed = true
-	}
-	if want, got := uint32(0), testBitwiseRshU_ssa(0x8aaaaaaa, 25, 15); want != got {
-		println("testBitwiseRshU failed, wanted", want, "got", got)
-		failed = true
-	}
-}
-
-//go:noinline
-func testBitwiseAnd_ssa(a, b uint32) uint32 {
-	return a & b
-}
-
-//go:noinline
-func testBitwiseOr_ssa(a, b uint32) uint32 {
-	return a | b
-}
-
-//go:noinline
-func testBitwiseXor_ssa(a, b uint32) uint32 {
-	return a ^ b
-}
-
-//go:noinline
-func testBitwiseLsh_ssa(a int32, b, c uint32) int32 {
-	return a << b << c
-}
-
-//go:noinline
-func testBitwiseRsh_ssa(a int32, b, c uint32) int32 {
-	return a >> b >> c
-}
-
-//go:noinline
-func testBitwiseRshU_ssa(a uint32, b, c uint32) uint32 {
-	return a >> b >> c
-}
-
-//go:noinline
-func testShiftCX_ssa() int {
-	v1 := uint8(3)
-	v4 := (v1 * v1) ^ v1 | v1 - v1 - v1&v1 ^ uint8(3+2) + v1*1>>0 - v1 | 1 | v1<<(2*3|0-0*0^1)
-	v5 := v4>>(3-0-uint(3)) | v1 | v1 + v1 ^ v4<<(0+1|3&1)<<(uint64(1)<<0*2*0<<0) ^ v1
-	v6 := v5 ^ (v1+v1)*v1 | v1 | v1*v1>>(v1&v1)>>(uint(1)<<0*uint(3)>>1)*v1<<2*v1<<v1 - v1>>2 | (v4 - v1) ^ v1 + v1 ^ v1>>1 | v1 + v1 - v1 ^ v1
-	v7 := v6 & v5 << 0
-	v1++
-	v11 := 2&1 ^ 0 + 3 | int(0^0)<<1>>(1*0*3) ^ 0*0 ^ 3&0*3&3 ^ 3*3 ^ 1 ^ int(2)<<(2*3) + 2 | 2 | 2 ^ 2 + 1 | 3 | 0 ^ int(1)>>1 ^ 2 // int
-	v7--
-	return int(uint64(2*1)<<(3-2)<<uint(3>>v7)-2)&v11 | v11 - int(2)<<0>>(2-1)*(v11*0&v11<<1<<(uint8(2)+v4))
-}
-
-func testShiftCX() {
-	want := 141
-	if got := testShiftCX_ssa(); want != got {
-		println("testShiftCX failed, wanted", want, "got", got)
-		failed = true
-	}
-}
-
-// testSubqToNegq ensures that the SUBQ -> NEGQ translation works correctly.
-func testSubqToNegq() {
-	want := int64(-318294940372190156)
-	if got := testSubqToNegq_ssa(1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2); want != got {
-		println("testSubqToNegq failed, wanted", want, "got", got)
-		failed = true
-	}
-}
-
-//go:noinline
-func testSubqToNegq_ssa(a, b, c, d, e, f, g, h, i, j, k int64) int64 {
-	return a + 8207351403619448057 - b - 1779494519303207690 + c*8810076340510052032*d - 4465874067674546219 - e*4361839741470334295 - f + 8688847565426072650*g*8065564729145417479
-}
-
-func testOcom() {
-	want1, want2 := int32(0x55555555), int32(-0x55555556)
-	if got1, got2 := testOcom_ssa(0x55555555, 0x55555555); want1 != got1 || want2 != got2 {
-		println("testSubqToNegq failed, wanted", want1, "and", want2,
-			"got", got1, "and", got2)
-		failed = true
-	}
-}
-
-//go:noinline
-func testOcom_ssa(a, b int32) (int32, int32) {
-	return ^^^^a, ^^^^^b
-}
-
-func lrot1_ssa(w uint8, x uint16, y uint32, z uint64) (a uint8, b uint16, c uint32, d uint64) {
-	a = (w << 5) | (w >> 3)
-	b = (x << 13) | (x >> 3)
-	c = (y << 29) | (y >> 3)
-	d = (z << 61) | (z >> 3)
-	return
-}
-
-//go:noinline
-func lrot2_ssa(w, n uint32) uint32 {
-	// Want to be sure that a "rotate by 32" which
-	// is really 0 | (w >> 0) == w
-	// is correctly compiled.
-	return (w << n) | (w >> (32 - n))
-}
-
-//go:noinline
-func lrot3_ssa(w uint32) uint32 {
-	// Want to be sure that a "rotate by 32" which
-	// is really 0 | (w >> 0) == w
-	// is correctly compiled.
-	return (w << 32) | (w >> (32 - 32))
-}
-
-func testLrot() {
-	wantA, wantB, wantC, wantD := uint8(0xe1), uint16(0xe001),
-		uint32(0xe0000001), uint64(0xe000000000000001)
-	a, b, c, d := lrot1_ssa(0xf, 0xf, 0xf, 0xf)
-	if a != wantA || b != wantB || c != wantC || d != wantD {
-		println("lrot1_ssa(0xf, 0xf, 0xf, 0xf)=",
-			wantA, wantB, wantC, wantD, ", got", a, b, c, d)
-		failed = true
-	}
-	x := lrot2_ssa(0xb0000001, 32)
-	wantX := uint32(0xb0000001)
-	if x != wantX {
-		println("lrot2_ssa(0xb0000001, 32)=",
-			wantX, ", got", x)
-		failed = true
-	}
-	x = lrot3_ssa(0xb0000001)
-	if x != wantX {
-		println("lrot3_ssa(0xb0000001)=",
-			wantX, ", got", x)
-		failed = true
-	}
-
-}
-
-//go:noinline
-func sub1_ssa() uint64 {
-	v1 := uint64(3) // uint64
-	return v1*v1 - (v1&v1)&v1
-}
-
-//go:noinline
-func sub2_ssa() uint8 {
-	v1 := uint8(0)
-	v3 := v1 + v1 + v1 ^ v1 | 3 + v1 ^ v1 | v1 ^ v1
-	v1-- // dev.ssa doesn't see this one
-	return v1 ^ v1*v1 - v3
-}
-
-func testSubConst() {
-	x1 := sub1_ssa()
-	want1 := uint64(6)
-	if x1 != want1 {
-		println("sub1_ssa()=", want1, ", got", x1)
-		failed = true
-	}
-	x2 := sub2_ssa()
-	want2 := uint8(251)
-	if x2 != want2 {
-		println("sub2_ssa()=", want2, ", got", x2)
-		failed = true
-	}
-}
-
-//go:noinline
-func orPhi_ssa(a bool, x int) int {
-	v := 0
-	if a {
-		v = -1
-	} else {
-		v = -1
-	}
-	return x | v
-}
-
-func testOrPhi() {
-	if want, got := -1, orPhi_ssa(true, 4); got != want {
-		println("orPhi_ssa(true, 4)=", got, " want ", want)
-	}
-	if want, got := -1, orPhi_ssa(false, 0); got != want {
-		println("orPhi_ssa(false, 0)=", got, " want ", want)
-	}
-}
-
-var failed = false
-
-func main() {
-
-	test64BitConstMult()
-	test64BitConstAdd()
-	testRegallocCVSpill()
-	testSubqToNegq()
-	testBitwiseLogic()
-	testOcom()
-	testLrot()
-	testShiftCX()
-	testSubConst()
-	testOverflowConstShift()
-	testArithConstShift()
-	testArithRshConst()
-	testLargeConst()
-	testLoadCombine()
-	testLoadSymCombine()
-	testShiftRemoval()
-
-	if failed {
-		panic("failed")
-	}
-}
diff --git a/src/cmd/compile/internal/gc/testdata/array_ssa.go b/src/cmd/compile/internal/gc/testdata/array.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/array_ssa.go
rename to src/cmd/compile/internal/gc/testdata/array.go
diff --git a/src/cmd/compile/internal/gc/testdata/assert_ssa.go b/src/cmd/compile/internal/gc/testdata/assert.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/assert_ssa.go
rename to src/cmd/compile/internal/gc/testdata/assert.go
diff --git a/src/cmd/compile/internal/gc/testdata/break_ssa.go b/src/cmd/compile/internal/gc/testdata/break.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/break_ssa.go
rename to src/cmd/compile/internal/gc/testdata/break.go
diff --git a/src/cmd/compile/internal/gc/testdata/chan_ssa.go b/src/cmd/compile/internal/gc/testdata/chan.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/chan_ssa.go
rename to src/cmd/compile/internal/gc/testdata/chan.go
diff --git a/src/cmd/compile/internal/gc/testdata/closure_ssa.go b/src/cmd/compile/internal/gc/testdata/closure.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/closure_ssa.go
rename to src/cmd/compile/internal/gc/testdata/closure.go
diff --git a/src/cmd/compile/internal/gc/testdata/cmp_ssa.go b/src/cmd/compile/internal/gc/testdata/cmp.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/cmp_ssa.go
rename to src/cmd/compile/internal/gc/testdata/cmp.go
diff --git a/src/cmd/compile/internal/gc/testdata/compound_ssa.go b/src/cmd/compile/internal/gc/testdata/compound.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/compound_ssa.go
rename to src/cmd/compile/internal/gc/testdata/compound.go
diff --git a/src/cmd/compile/internal/gc/testdata/copy_ssa.go b/src/cmd/compile/internal/gc/testdata/copy.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/copy_ssa.go
rename to src/cmd/compile/internal/gc/testdata/copy.go
diff --git a/src/cmd/compile/internal/gc/testdata/ctl_ssa.go b/src/cmd/compile/internal/gc/testdata/ctl.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/ctl_ssa.go
rename to src/cmd/compile/internal/gc/testdata/ctl.go
diff --git a/src/cmd/compile/internal/gc/testdata/deferNoReturn_ssa.go b/src/cmd/compile/internal/gc/testdata/deferNoReturn.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/deferNoReturn_ssa.go
rename to src/cmd/compile/internal/gc/testdata/deferNoReturn.go
diff --git a/src/cmd/compile/internal/gc/testdata/divbyzero_ssa.go b/src/cmd/compile/internal/gc/testdata/divbyzero.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/divbyzero_ssa.go
rename to src/cmd/compile/internal/gc/testdata/divbyzero.go
diff --git a/src/cmd/compile/internal/gc/testdata/fp_ssa.go b/src/cmd/compile/internal/gc/testdata/fp.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/fp_ssa.go
rename to src/cmd/compile/internal/gc/testdata/fp.go
diff --git a/src/cmd/compile/internal/gc/testdata/gen/arithBoundaryGen.go b/src/cmd/compile/internal/gc/testdata/gen/arithBoundaryGen.go
index be0aad5..866431e 100644
--- a/src/cmd/compile/internal/gc/testdata/gen/arithBoundaryGen.go
+++ b/src/cmd/compile/internal/gc/testdata/gen/arithBoundaryGen.go
@@ -5,7 +5,7 @@
 // This program generates a test to verify that the standard arithmetic
 // operators properly handle some special cases. The test file should be
 // generated with a known working version of go.
-// launch with `go run arithBoundaryGen.go` a file called arithBoundary_ssa.go
+// launch with `go run arithBoundaryGen.go` a file called arithBoundary.go
 // will be written into the parent directory containing the tests
 
 package main
@@ -207,7 +207,7 @@ func main() {
 	}
 
 	// write to file
-	err = ioutil.WriteFile("../arithBoundary_ssa.go", src, 0666)
+	err = ioutil.WriteFile("../arithBoundary.go", src, 0666)
 	if err != nil {
 		log.Fatalf("can't write output: %v\n", err)
 	}
diff --git a/src/cmd/compile/internal/gc/testdata/gen/arithConstGen.go b/src/cmd/compile/internal/gc/testdata/gen/arithConstGen.go
index 5559050..97434ea 100644
--- a/src/cmd/compile/internal/gc/testdata/gen/arithConstGen.go
+++ b/src/cmd/compile/internal/gc/testdata/gen/arithConstGen.go
@@ -5,7 +5,7 @@
 // This program generates a test to verify that the standard arithmetic
 // operators properly handle const cases. The test file should be
 // generated with a known working version of go.
-// launch with `go run arithConstGen.go` a file called arithConst_ssa.go
+// launch with `go run arithConstGen.go` a file called arithConst.go
 // will be written into the parent directory containing the tests
 
 package main
@@ -295,7 +295,7 @@ func main() {
 	}
 
 	// write to file
-	err = ioutil.WriteFile("../arithConst_ssa.go", src, 0666)
+	err = ioutil.WriteFile("../arithConst.go", src, 0666)
 	if err != nil {
 		log.Fatalf("can't write output: %v\n", err)
 	}
diff --git a/src/cmd/compile/internal/gc/testdata/gen/constFoldGen.go b/src/cmd/compile/internal/gc/testdata/gen/constFoldGen.go
index 28cdcc3..f266749 100644
--- a/src/cmd/compile/internal/gc/testdata/gen/constFoldGen.go
+++ b/src/cmd/compile/internal/gc/testdata/gen/constFoldGen.go
@@ -144,7 +144,7 @@ func main() {
 					fmt.Fprintf(w, "\tr = x %s y\n", o.symbol)
 					want := ansU(c, d, s.name, o.symbol)
 					fmt.Fprintf(w, "\tif r != %s {\n", want)
-					fmt.Fprintf(w, "\t\tt.Errorf(\"%d %s %d = %%d, want %s\", r)\n", c, o.symbol, d, want)
+					fmt.Fprintf(w, "\t\tt.Errorf(\"%d %%s %d = %%d, want %s\", %q, r)\n", c, d, want, o.symbol)
 					fmt.Fprintf(w, "\t}\n")
 				}
 			}
@@ -159,7 +159,7 @@ func main() {
 					fmt.Fprintf(w, "\tr = x %s y\n", o.symbol)
 					want := ansS(c, d, s.name, o.symbol)
 					fmt.Fprintf(w, "\tif r != %s {\n", want)
-					fmt.Fprintf(w, "\t\tt.Errorf(\"%d %s %d = %%d, want %s\", r)\n", c, o.symbol, d, want)
+					fmt.Fprintf(w, "\t\tt.Errorf(\"%d %%s %d = %%d, want %s\", %q, r)\n", c, d, want, o.symbol)
 					fmt.Fprintf(w, "\t}\n")
 				}
 			}
@@ -188,7 +188,7 @@ func main() {
 						fmt.Fprintf(w, "\tr = x %s y\n", o.symbol)
 						want := ansU(c, d, ls.name, o.symbol)
 						fmt.Fprintf(w, "\tif r != %s {\n", want)
-						fmt.Fprintf(w, "\t\tt.Errorf(\"%d %s %d = %%d, want %s\", r)\n", c, o.symbol, d, want)
+						fmt.Fprintf(w, "\t\tt.Errorf(\"%d %%s %d = %%d, want %s\", %q, r)\n", c, d, want, o.symbol)
 						fmt.Fprintf(w, "\t}\n")
 					}
 				}
@@ -200,7 +200,7 @@ func main() {
 						fmt.Fprintf(w, "\tr = x %s y\n", o.symbol)
 						want := ansS(c, int64(d), ls.name, o.symbol)
 						fmt.Fprintf(w, "\tif r != %s {\n", want)
-						fmt.Fprintf(w, "\t\tt.Errorf(\"%d %s %d = %%d, want %s\", r)\n", c, o.symbol, d, want)
+						fmt.Fprintf(w, "\t\tt.Errorf(\"%d %%s %d = %%d, want %s\", %q, r)\n", c, d, want, o.symbol)
 						fmt.Fprintf(w, "\t}\n")
 					}
 				}
diff --git a/src/cmd/compile/internal/gc/testdata/gen/copyGen.go b/src/cmd/compile/internal/gc/testdata/gen/copyGen.go
index a699fac..2d2240c 100644
--- a/src/cmd/compile/internal/gc/testdata/gen/copyGen.go
+++ b/src/cmd/compile/internal/gc/testdata/gen/copyGen.go
@@ -15,7 +15,7 @@ import (
 // This program generates tests to verify that copying operations
 // copy the data they are supposed to and clobber no adjacent values.
 
-// run as `go run copyGen.go`.  A file called copy_ssa.go
+// run as `go run copyGen.go`.  A file called copy.go
 // will be written into the parent directory containing the tests.
 
 var sizes = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 17, 23, 24, 25, 31, 32, 33, 63, 64, 65, 1023, 1024, 1025, 1024 + 7, 1024 + 8, 1024 + 9, 1024 + 15, 1024 + 16, 1024 + 17}
@@ -86,7 +86,7 @@ func main() {
 	}
 
 	// write to file
-	err = ioutil.WriteFile("../copy_ssa.go", src, 0666)
+	err = ioutil.WriteFile("../copy.go", src, 0666)
 	if err != nil {
 		log.Fatalf("can't write output: %v\n", err)
 	}
diff --git a/src/cmd/compile/internal/gc/testdata/gen/zeroGen.go b/src/cmd/compile/internal/gc/testdata/gen/zeroGen.go
index 90e8029..6482f07 100644
--- a/src/cmd/compile/internal/gc/testdata/gen/zeroGen.go
+++ b/src/cmd/compile/internal/gc/testdata/gen/zeroGen.go
@@ -15,7 +15,7 @@ import (
 // This program generates tests to verify that zeroing operations
 // zero the data they are supposed to and clobber no adjacent values.
 
-// run as `go run zeroGen.go`.  A file called zero_ssa.go
+// run as `go run zeroGen.go`.  A file called zero.go
 // will be written into the parent directory containing the tests.
 
 var sizes = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 17, 23, 24, 25, 31, 32, 33, 63, 64, 65, 1023, 1024, 1025}
@@ -81,7 +81,7 @@ func main() {
 	}
 
 	// write to file
-	err = ioutil.WriteFile("../zero_ssa.go", src, 0666)
+	err = ioutil.WriteFile("../zero.go", src, 0666)
 	if err != nil {
 		log.Fatalf("can't write output: %v\n", err)
 	}
diff --git a/src/cmd/compile/internal/gc/testdata/loadstore_ssa.go b/src/cmd/compile/internal/gc/testdata/loadstore.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/loadstore_ssa.go
rename to src/cmd/compile/internal/gc/testdata/loadstore.go
diff --git a/src/cmd/compile/internal/gc/testdata/map_ssa.go b/src/cmd/compile/internal/gc/testdata/map.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/map_ssa.go
rename to src/cmd/compile/internal/gc/testdata/map.go
diff --git a/src/cmd/compile/internal/gc/testdata/phi_ssa.go b/src/cmd/compile/internal/gc/testdata/phi.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/phi_ssa.go
rename to src/cmd/compile/internal/gc/testdata/phi.go
diff --git a/src/cmd/compile/internal/gc/testdata/regalloc_ssa.go b/src/cmd/compile/internal/gc/testdata/regalloc.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/regalloc_ssa.go
rename to src/cmd/compile/internal/gc/testdata/regalloc.go
diff --git a/src/cmd/compile/internal/gc/testdata/short_ssa.go b/src/cmd/compile/internal/gc/testdata/short.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/short_ssa.go
rename to src/cmd/compile/internal/gc/testdata/short.go
diff --git a/src/cmd/compile/internal/gc/testdata/sqrt_const.go b/src/cmd/compile/internal/gc/testdata/sqrt_const.go
new file mode 100644
index 0000000..1f25d9a
--- /dev/null
+++ b/src/cmd/compile/internal/gc/testdata/sqrt_const.go
@@ -0,0 +1,59 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+var tests = [...]struct {
+	name string
+	in   float64 // used for error messages, not an input
+	got  float64
+	want float64
+}{
+	{"sqrt0", 0, math.Sqrt(0), 0},
+	{"sqrt1", 1, math.Sqrt(1), 1},
+	{"sqrt2", 2, math.Sqrt(2), math.Sqrt2},
+	{"sqrt4", 4, math.Sqrt(4), 2},
+	{"sqrt100", 100, math.Sqrt(100), 10},
+	{"sqrt101", 101, math.Sqrt(101), 10.04987562112089},
+}
+
+var nanTests = [...]struct {
+	name string
+	in   float64 // used for error messages, not an input
+	got  float64
+}{
+	{"sqrtNaN", math.NaN(), math.Sqrt(math.NaN())},
+	{"sqrtNegative", -1, math.Sqrt(-1)},
+	{"sqrtNegInf", math.Inf(-1), math.Sqrt(math.Inf(-1))},
+}
+
+var failed = false
+
+func main() {
+	for _, test := range tests {
+		if test.got != test.want {
+			fmt.Printf("%s: math.Sqrt(%f): got %f, want %f\n", test.name, test.in, test.got, test.want)
+			failed = true
+		}
+	}
+	for _, test := range nanTests {
+		if math.IsNaN(test.got) != true {
+			fmt.Printf("%s: math.Sqrt(%f): got %f, want NaN\n", test.name, test.in, test.got)
+			failed = true
+		}
+	}
+	if got := math.Sqrt(math.Inf(1)); !math.IsInf(got, 1) {
+		fmt.Printf("math.Sqrt(+Inf), got %f, want +Inf\n", got)
+		failed = true
+	}
+
+	if failed {
+		panic("failed")
+	}
+}
diff --git a/src/cmd/compile/internal/gc/testdata/string.go b/src/cmd/compile/internal/gc/testdata/string.go
new file mode 100644
index 0000000..897e874
--- /dev/null
+++ b/src/cmd/compile/internal/gc/testdata/string.go
@@ -0,0 +1,224 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// string_ssa.go tests string operations.
+package main
+
+var failed = false
+
+//go:noinline
+func testStringSlice1_ssa(a string, i, j int) string {
+	return a[i:]
+}
+
+//go:noinline
+func testStringSlice2_ssa(a string, i, j int) string {
+	return a[:j]
+}
+
+//go:noinline
+func testStringSlice12_ssa(a string, i, j int) string {
+	return a[i:j]
+}
+
+func testStringSlice() {
+	tests := [...]struct {
+		fn        func(string, int, int) string
+		s         string
+		low, high int
+		want      string
+	}{
+		// -1 means the value is not used.
+		{testStringSlice1_ssa, "foobar", 0, -1, "foobar"},
+		{testStringSlice1_ssa, "foobar", 3, -1, "bar"},
+		{testStringSlice1_ssa, "foobar", 6, -1, ""},
+		{testStringSlice2_ssa, "foobar", -1, 0, ""},
+		{testStringSlice2_ssa, "foobar", -1, 3, "foo"},
+		{testStringSlice2_ssa, "foobar", -1, 6, "foobar"},
+		{testStringSlice12_ssa, "foobar", 0, 6, "foobar"},
+		{testStringSlice12_ssa, "foobar", 0, 0, ""},
+		{testStringSlice12_ssa, "foobar", 6, 6, ""},
+		{testStringSlice12_ssa, "foobar", 1, 5, "ooba"},
+		{testStringSlice12_ssa, "foobar", 3, 3, ""},
+		{testStringSlice12_ssa, "", 0, 0, ""},
+	}
+
+	for i, t := range tests {
+		if got := t.fn(t.s, t.low, t.high); t.want != got {
+			println("#", i, " ", t.s, "[", t.low, ":", t.high, "] = ", got, " want ", t.want)
+			failed = true
+		}
+	}
+}
+
+type prefix struct {
+	prefix string
+}
+
+func (p *prefix) slice_ssa() {
+	p.prefix = p.prefix[:3]
+}
+
+//go:noinline
+func testStructSlice() {
+	p := &prefix{"prefix"}
+	p.slice_ssa()
+	if "pre" != p.prefix {
+		println("wrong field slice: wanted %s got %s", "pre", p.prefix)
+		failed = true
+	}
+}
+
+func testStringSlicePanic() {
+	defer func() {
+		if r := recover(); r != nil {
+			println("paniced as expected")
+		}
+	}()
+
+	str := "foobar"
+	println("got ", testStringSlice12_ssa(str, 3, 9))
+	println("expected to panic, but didn't")
+	failed = true
+}
+
+const _Accuracy_name = "BelowExactAbove"
+
+var _Accuracy_index = [...]uint8{0, 5, 10, 15}
+
+//go:noinline
+func testSmallIndexType_ssa(i int) string {
+	return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]]
+}
+
+func testSmallIndexType() {
+	tests := []struct {
+		i    int
+		want string
+	}{
+		{0, "Below"},
+		{1, "Exact"},
+		{2, "Above"},
+	}
+
+	for i, t := range tests {
+		if got := testSmallIndexType_ssa(t.i); got != t.want {
+			println("#", i, "got ", got, ", wanted", t.want)
+			failed = true
+		}
+	}
+}
+
+//go:noinline
+func testInt64Index_ssa(s string, i int64) byte {
+	return s[i]
+}
+
+//go:noinline
+func testInt64Slice_ssa(s string, i, j int64) string {
+	return s[i:j]
+}
+
+func testInt64Index() {
+	tests := []struct {
+		i int64
+		j int64
+		b byte
+		s string
+	}{
+		{0, 5, 'B', "Below"},
+		{5, 10, 'E', "Exact"},
+		{10, 15, 'A', "Above"},
+	}
+
+	str := "BelowExactAbove"
+	for i, t := range tests {
+		if got := testInt64Index_ssa(str, t.i); got != t.b {
+			println("#", i, "got ", got, ", wanted", t.b)
+			failed = true
+		}
+		if got := testInt64Slice_ssa(str, t.i, t.j); got != t.s {
+			println("#", i, "got ", got, ", wanted", t.s)
+			failed = true
+		}
+	}
+}
+
+func testInt64IndexPanic() {
+	defer func() {
+		if r := recover(); r != nil {
+			println("paniced as expected")
+		}
+	}()
+
+	str := "foobar"
+	println("got ", testInt64Index_ssa(str, 1<<32+1))
+	println("expected to panic, but didn't")
+	failed = true
+}
+
+func testInt64SlicePanic() {
+	defer func() {
+		if r := recover(); r != nil {
+			println("paniced as expected")
+		}
+	}()
+
+	str := "foobar"
+	println("got ", testInt64Slice_ssa(str, 1<<32, 1<<32+1))
+	println("expected to panic, but didn't")
+	failed = true
+}
+
+//go:noinline
+func testStringElem_ssa(s string, i int) byte {
+	return s[i]
+}
+
+func testStringElem() {
+	tests := []struct {
+		s string
+		i int
+		n byte
+	}{
+		{"foobar", 3, 98},
+		{"foobar", 0, 102},
+		{"foobar", 5, 114},
+	}
+	for _, t := range tests {
+		if got := testStringElem_ssa(t.s, t.i); got != t.n {
+			print("testStringElem \"", t.s, "\"[", t.i, "]=", got, ", wanted ", t.n, "\n")
+			failed = true
+		}
+	}
+}
+
+//go:noinline
+func testStringElemConst_ssa(i int) byte {
+	s := "foobar"
+	return s[i]
+}
+
+func testStringElemConst() {
+	if got := testStringElemConst_ssa(3); got != 98 {
+		println("testStringElemConst=", got, ", wanted 98")
+		failed = true
+	}
+}
+
+func main() {
+	testStringSlice()
+	testStringSlicePanic()
+	testStructSlice()
+	testSmallIndexType()
+	testStringElem()
+	testStringElemConst()
+	testInt64Index()
+	testInt64IndexPanic()
+	testInt64SlicePanic()
+
+	if failed {
+		panic("failed")
+	}
+}
diff --git a/src/cmd/compile/internal/gc/testdata/string_ssa.go b/src/cmd/compile/internal/gc/testdata/string_ssa.go
deleted file mode 100644
index b47c2f1..0000000
--- a/src/cmd/compile/internal/gc/testdata/string_ssa.go
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// string_ssa.go tests string operations.
-package main
-
-var failed = false
-
-//go:noinline
-func testStringSlice1_ssa(a string, i, j int) string {
-	return a[i:]
-}
-
-//go:noinline
-func testStringSlice2_ssa(a string, i, j int) string {
-	return a[:j]
-}
-
-//go:noinline
-func testStringSlice12_ssa(a string, i, j int) string {
-	return a[i:j]
-}
-
-func testStringSlice() {
-	tests := [...]struct {
-		fn        func(string, int, int) string
-		s         string
-		low, high int
-		want      string
-	}{
-		// -1 means the value is not used.
-		{testStringSlice1_ssa, "foobar", 0, -1, "foobar"},
-		{testStringSlice1_ssa, "foobar", 3, -1, "bar"},
-		{testStringSlice1_ssa, "foobar", 6, -1, ""},
-		{testStringSlice2_ssa, "foobar", -1, 0, ""},
-		{testStringSlice2_ssa, "foobar", -1, 3, "foo"},
-		{testStringSlice2_ssa, "foobar", -1, 6, "foobar"},
-		{testStringSlice12_ssa, "foobar", 0, 6, "foobar"},
-		{testStringSlice12_ssa, "foobar", 0, 0, ""},
-		{testStringSlice12_ssa, "foobar", 6, 6, ""},
-		{testStringSlice12_ssa, "foobar", 1, 5, "ooba"},
-		{testStringSlice12_ssa, "foobar", 3, 3, ""},
-		{testStringSlice12_ssa, "", 0, 0, ""},
-	}
-
-	for i, t := range tests {
-		if got := t.fn(t.s, t.low, t.high); t.want != got {
-			println("#", i, " ", t.s, "[", t.low, ":", t.high, "] = ", got, " want ", t.want)
-			failed = true
-		}
-	}
-}
-
-type prefix struct {
-	prefix string
-}
-
-func (p *prefix) slice_ssa() {
-	p.prefix = p.prefix[:3]
-}
-
-//go:noinline
-func testStructSlice() {
-	p := &prefix{"prefix"}
-	p.slice_ssa()
-	if "pre" != p.prefix {
-		println("wrong field slice: wanted %s got %s", "pre", p.prefix)
-		failed = true
-	}
-}
-
-func testStringSlicePanic() {
-	defer func() {
-		if r := recover(); r != nil {
-			println("paniced as expected")
-		}
-	}()
-
-	str := "foobar"
-	println("got ", testStringSlice12_ssa(str, 3, 9))
-	println("expected to panic, but didn't")
-	failed = true
-}
-
-const _Accuracy_name = "BelowExactAbove"
-
-var _Accuracy_index = [...]uint8{0, 5, 10, 15}
-
-//go:noinline
-func testSmallIndexType_ssa(i int) string {
-	return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]]
-}
-
-func testSmallIndexType() {
-	tests := []struct {
-		i    int
-		want string
-	}{
-		{0, "Below"},
-		{1, "Exact"},
-		{2, "Above"},
-	}
-
-	for i, t := range tests {
-		if got := testSmallIndexType_ssa(t.i); got != t.want {
-			println("#", i, "got ", got, ", wanted", t.want)
-			failed = true
-		}
-	}
-}
-
-//go:noinline
-func testStringElem_ssa(s string, i int) byte {
-	return s[i]
-}
-
-func testStringElem() {
-	tests := []struct {
-		s string
-		i int
-		n byte
-	}{
-		{"foobar", 3, 98},
-		{"foobar", 0, 102},
-		{"foobar", 5, 114},
-	}
-	for _, t := range tests {
-		if got := testStringElem_ssa(t.s, t.i); got != t.n {
-			print("testStringElem \"", t.s, "\"[", t.i, "]=", got, ", wanted ", t.n, "\n")
-			failed = true
-		}
-	}
-}
-
-//go:noinline
-func testStringElemConst_ssa(i int) byte {
-	s := "foobar"
-	return s[i]
-}
-
-func testStringElemConst() {
-	if got := testStringElemConst_ssa(3); got != 98 {
-		println("testStringElemConst=", got, ", wanted 98")
-		failed = true
-	}
-}
-
-func main() {
-	testStringSlice()
-	testStringSlicePanic()
-	testStructSlice()
-	testSmallIndexType()
-	testStringElem()
-	testStringElemConst()
-
-	if failed {
-		panic("failed")
-	}
-}
diff --git a/src/cmd/compile/internal/gc/testdata/unsafe_ssa.go b/src/cmd/compile/internal/gc/testdata/unsafe.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/unsafe_ssa.go
rename to src/cmd/compile/internal/gc/testdata/unsafe.go
diff --git a/src/cmd/compile/internal/gc/testdata/zero_ssa.go b/src/cmd/compile/internal/gc/testdata/zero.go
similarity index 100%
rename from src/cmd/compile/internal/gc/testdata/zero_ssa.go
rename to src/cmd/compile/internal/gc/testdata/zero.go
diff --git a/src/cmd/compile/internal/gc/timings.go b/src/cmd/compile/internal/gc/timings.go
new file mode 100644
index 0000000..56b3899
--- /dev/null
+++ b/src/cmd/compile/internal/gc/timings.go
@@ -0,0 +1,235 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gc
+
+import (
+	"fmt"
+	"io"
+	"strings"
+	"time"
+)
+
+// Timings collects the execution times of labeled phases
+// which are added trough a sequence of Start/Stop calls.
+// Events may be associated with each phase via AddEvent.
+type Timings struct {
+	list   []timestamp
+	events map[int][]*event // lazily allocated
+}
+
+type timestamp struct {
+	time  time.Time
+	label string
+	start bool
+}
+
+type event struct {
+	size int64  // count or amount of data processed (allocations, data size, lines, funcs, ...)
+	unit string // unit of size measure (count, MB, lines, funcs, ...)
+}
+
+func (t *Timings) append(labels []string, start bool) {
+	t.list = append(t.list, timestamp{time.Now(), strings.Join(labels, ":"), start})
+}
+
+// Start marks the beginning of a new phase and implicitly stops the previous phase.
+// The phase name is the colon-separated concatenation of the labels.
+func (t *Timings) Start(labels ...string) {
+	t.append(labels, true)
+}
+
+// Stop marks the end of a phase and implicitly starts a new phase.
+// The labels are added to the labels of the ended phase.
+func (t *Timings) Stop(labels ...string) {
+	t.append(labels, false)
+}
+
+// AddEvent associates an event, i.e., a count, or an amount of data,
+// with the most recently started or stopped phase; or the very first
+// phase if Start or Stop hasn't been called yet. The unit specifies
+// the unit of measurement (e.g., MB, lines, no. of funcs, etc.).
+func (t *Timings) AddEvent(size int64, unit string) {
+	m := t.events
+	if m == nil {
+		m = make(map[int][]*event)
+		t.events = m
+	}
+	i := len(t.list)
+	if i > 0 {
+		i--
+	}
+	m[i] = append(m[i], &event{size, unit})
+}
+
+// Write prints the phase times to w.
+// The prefix is printed at the start of each line.
+func (t *Timings) Write(w io.Writer, prefix string) {
+	if len(t.list) > 0 {
+		var lines lines
+
+		// group of phases with shared non-empty label prefix
+		var group struct {
+			label string        // label prefix
+			tot   time.Duration // accumulated phase time
+			size  int           // number of phases collected in group
+		}
+
+		// accumulated time between Stop/Start timestamps
+		var unaccounted time.Duration
+
+		// process Start/Stop timestamps
+		pt := &t.list[0] // previous timestamp
+		tot := t.list[len(t.list)-1].time.Sub(pt.time)
+		for i := 1; i < len(t.list); i++ {
+			qt := &t.list[i] // current timestamp
+			dt := qt.time.Sub(pt.time)
+
+			var label string
+			var events []*event
+			if pt.start {
+				// previous phase started
+				label = pt.label
+				events = t.events[i-1]
+				if qt.start {
+					// start implicitly ended previous phase; nothing to do
+				} else {
+					// stop ended previous phase; append stop labels, if any
+					if qt.label != "" {
+						label += ":" + qt.label
+					}
+					// events associated with stop replace prior events
+					if e := t.events[i]; e != nil {
+						events = e
+					}
+				}
+			} else {
+				// previous phase stopped
+				if qt.start {
+					// between a stopped and started phase; unaccounted time
+					unaccounted += dt
+				} else {
+					// previous stop implicitly started current phase
+					label = qt.label
+					events = t.events[i]
+				}
+			}
+			if label != "" {
+				// add phase to existing group, or start a new group
+				l := commonPrefix(group.label, label)
+				if group.size == 1 && l != "" || group.size > 1 && l == group.label {
+					// add to existing group
+					group.label = l
+					group.tot += dt
+					group.size++
+				} else {
+					// start a new group
+					if group.size > 1 {
+						lines.add(prefix+group.label+"subtotal", 1, group.tot, tot, nil)
+					}
+					group.label = label
+					group.tot = dt
+					group.size = 1
+				}
+
+				// write phase
+				lines.add(prefix+label, 1, dt, tot, events)
+			}
+
+			pt = qt
+		}
+
+		if group.size > 1 {
+			lines.add(prefix+group.label+"subtotal", 1, group.tot, tot, nil)
+		}
+
+		if unaccounted != 0 {
+			lines.add(prefix+"unaccounted", 1, unaccounted, tot, nil)
+		}
+
+		lines.add(prefix+"total", 1, tot, tot, nil)
+
+		lines.write(w)
+	}
+}
+
+func commonPrefix(a, b string) string {
+	i := 0
+	for i < len(a) && i < len(b) && a[i] == b[i] {
+		i++
+	}
+	return a[:i]
+}
+
+type lines [][]string
+
+func (lines *lines) add(label string, n int, dt, tot time.Duration, events []*event) {
+	var line []string
+	add := func(format string, args ...interface{}) {
+		line = append(line, fmt.Sprintf(format, args...))
+	}
+
+	add("%s", label)
+	add("    %d", n)
+	add("    %d ns/op", dt)
+	add("    %.2f %%", float64(dt)/float64(tot)*100)
+
+	for _, e := range events {
+		add("    %d", e.size)
+		add(" %s", e.unit)
+		add("    %d", int64(float64(e.size)/dt.Seconds()+0.5))
+		add(" %s/s", e.unit)
+	}
+
+	*lines = append(*lines, line)
+}
+
+func (lines lines) write(w io.Writer) {
+	// determine column widths and contents
+	var widths []int
+	var number []bool
+	for _, line := range lines {
+		for i, col := range line {
+			if i < len(widths) {
+				if len(col) > widths[i] {
+					widths[i] = len(col)
+				}
+			} else {
+				widths = append(widths, len(col))
+				number = append(number, isnumber(col)) // first line determines column contents
+			}
+		}
+	}
+
+	// make column widths a multiple of align for more stable output
+	const align = 1 // set to a value > 1 to enable
+	if align > 1 {
+		for i, w := range widths {
+			w += align - 1
+			widths[i] = w - w%align
+		}
+	}
+
+	// print lines taking column widths and contents into account
+	for _, line := range lines {
+		for i, col := range line {
+			format := "%-*s"
+			if number[i] {
+				format = "%*s" // numbers are right-aligned
+			}
+			fmt.Fprintf(w, format, widths[i], col)
+		}
+		fmt.Fprintln(w)
+	}
+}
+
+func isnumber(s string) bool {
+	for _, ch := range s {
+		if ch <= ' ' {
+			continue // ignore leading whitespace
+		}
+		return '0' <= ch && ch <= '9' || ch == '.' || ch == '-' || ch == '+'
+	}
+	return false
+}
diff --git a/src/cmd/compile/internal/gc/trace.go b/src/cmd/compile/internal/gc/trace.go
new file mode 100644
index 0000000..ed4b5a2
--- /dev/null
+++ b/src/cmd/compile/internal/gc/trace.go
@@ -0,0 +1,27 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.7
+
+package gc
+
+import (
+	"os"
+	tracepkg "runtime/trace"
+)
+
+func init() {
+	traceHandler = traceHandlerGo17
+}
+
+func traceHandlerGo17(traceprofile string) {
+	f, err := os.Create(traceprofile)
+	if err != nil {
+		Fatalf("%v", err)
+	}
+	if err := tracepkg.Start(f); err != nil {
+		Fatalf("%v", err)
+	}
+	atExit(tracepkg.Stop)
+}
diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go
index ab13df6..29048f1 100644
--- a/src/cmd/compile/internal/gc/type.go
+++ b/src/cmd/compile/internal/gc/type.go
@@ -143,9 +143,12 @@ type Type struct {
 	methods    Fields
 	allMethods Fields
 
-	Nod  *Node // canonical OTYPE node
+	nod  *Node // canonical OTYPE node
 	Orig *Type // original type (type literal or predefined type)
 
+	sliceOf *Type
+	ptrTo   *Type
+
 	Sym    *Sym  // symbol containing name, for named types
 	Vargen int32 // unique name for OTYPE/ONAME
 	Lineno int32 // line at which this type was declared, implicitly or explicitly
@@ -153,11 +156,11 @@ type Type struct {
 	Etype      EType // kind of type
 	Noalg      bool  // suppress hash and eq algorithm generation
 	Trecur     uint8 // to detect loops
-	Printed    bool  // prevent duplicate export printing
 	Local      bool  // created in this file
 	Deferwidth bool
 	Broke      bool  // broken type definition.
 	Align      uint8 // the required alignment of this type, in bytes
+	NotInHeap  bool  // type cannot be heap allocated
 }
 
 // MapType contains Type fields specific to maps.
@@ -412,13 +415,22 @@ func typArray(elem *Type, bound int64) *Type {
 	}
 	t := typ(TARRAY)
 	t.Extra = &ArrayType{Elem: elem, Bound: bound}
+	t.NotInHeap = elem.NotInHeap
 	return t
 }
 
-// typSlice returns a new slice Type.
+// typSlice returns the slice Type with element type elem.
 func typSlice(elem *Type) *Type {
+	if t := elem.sliceOf; t != nil {
+		if t.Elem() != elem {
+			Fatalf("elem mismatch")
+		}
+		return t
+	}
+
 	t := typ(TSLICE)
 	t.Extra = SliceType{Elem: elem}
+	elem.sliceOf = t
 	return t
 }
 
@@ -426,6 +438,7 @@ func typSlice(elem *Type) *Type {
 func typDDDArray(elem *Type) *Type {
 	t := typ(TARRAY)
 	t.Extra = &ArrayType{Elem: elem, Bound: -1}
+	t.NotInHeap = elem.NotInHeap
 	return t
 }
 
@@ -447,12 +460,20 @@ func typMap(k, v *Type) *Type {
 	return t
 }
 
-// typPtr returns a new pointer type pointing to t.
+// typPtr returns the pointer type pointing to t.
 func typPtr(elem *Type) *Type {
+	if t := elem.ptrTo; t != nil {
+		if t.Elem() != elem {
+			Fatalf("elem mismatch")
+		}
+		return t
+	}
+
 	t := typ(Tptr)
 	t.Extra = PtrType{Elem: elem}
 	t.Width = int64(Widthptr)
 	t.Align = uint8(Widthptr)
+	elem.ptrTo = t
 	return t
 }
 
@@ -561,18 +582,10 @@ func substAny(t *Type, types *[]*Type) *Type {
 		params := substAny(t.Params(), types)
 		results := substAny(t.Results(), types)
 		if recvs != t.Recvs() || params != t.Params() || results != t.Results() {
-			// Note that this code has to be aware of the
-			// representation underlying Recvs/Results/Params.
-			if recvs == t.Recvs() {
-				recvs = recvs.Copy()
-			}
-			if results == t.Results() {
-				results = results.Copy()
-			}
 			t = t.Copy()
-			*t.RecvsP() = recvs
-			*t.ResultsP() = results
-			*t.ParamsP() = params
+			t.FuncType().Receiver = recvs
+			t.FuncType().Results = results
+			t.FuncType().Params = params
 		}
 
 	case TSTRUCT:
@@ -646,9 +659,9 @@ type Iter struct {
 	s []*Field
 }
 
-// IterFields returns the first field or method in struct or interface type t
+// iterFields returns the first field or method in struct or interface type t
 // and an Iter value to continue iterating across the rest.
-func IterFields(t *Type) (*Field, Iter) {
+func iterFields(t *Type) (*Field, Iter) {
 	return t.Fields().Iter()
 }
 
@@ -677,30 +690,9 @@ func (t *Type) wantEtype(et EType) {
 	}
 }
 
-func (t *Type) wantEtype2(et1, et2 EType) {
-	if t.Etype != et1 && t.Etype != et2 {
-		Fatalf("want %v or %v, but have %v", et1, et2, t)
-	}
-}
-
-func (t *Type) RecvsP() **Type {
-	t.wantEtype(TFUNC)
-	return &t.Extra.(*FuncType).Receiver
-}
-
-func (t *Type) ParamsP() **Type {
-	t.wantEtype(TFUNC)
-	return &t.Extra.(*FuncType).Params
-}
-
-func (t *Type) ResultsP() **Type {
-	t.wantEtype(TFUNC)
-	return &t.Extra.(*FuncType).Results
-}
-
-func (t *Type) Recvs() *Type   { return *t.RecvsP() }
-func (t *Type) Params() *Type  { return *t.ParamsP() }
-func (t *Type) Results() *Type { return *t.ResultsP() }
+func (t *Type) Recvs() *Type   { return t.FuncType().Receiver }
+func (t *Type) Params() *Type  { return t.FuncType().Params }
+func (t *Type) Results() *Type { return t.FuncType().Results }
 
 // Recv returns the receiver of function type t, if any.
 func (t *Type) Recv() *Field {
@@ -833,6 +825,17 @@ func (t *Type) FieldSlice() []*Field {
 
 // SetFields sets struct/interface type t's fields/methods to fields.
 func (t *Type) SetFields(fields []*Field) {
+	for _, f := range fields {
+		// If type T contains a field F with a go:notinheap
+		// type, then T must also be go:notinheap. Otherwise,
+		// you could heap allocate T and then get a pointer F,
+		// which would be a heap pointer to a go:notinheap
+		// type.
+		if f.Type != nil && f.Type.NotInHeap {
+			t.NotInHeap = true
+			break
+		}
+	}
 	t.Fields().Set(fields)
 }
 
@@ -922,7 +925,7 @@ func (r *Sym) cmpsym(s *Sym) ssa.Cmp {
 // ssa.CMPeq, ssa.CMPgt as t<x, t==x, t>x, for an arbitrary
 // and optimizer-centric notion of comparison.
 func (t *Type) cmp(x *Type) ssa.Cmp {
-	// This follows the structure of Eqtype in subr.go
+	// This follows the structure of eqtype in subr.go
 	// with two exceptions.
 	// 1. Symbols are compared more carefully because a <,=,> result is desired.
 	// 2. Maps are treated specially to avoid endless recursion -- maps
@@ -1008,8 +1011,8 @@ func (t *Type) cmp(x *Type) ssa.Cmp {
 
 		fallthrough
 	case TINTER:
-		t1, ti := IterFields(t)
-		x1, xi := IterFields(x)
+		t1, ti := iterFields(t)
+		x1, xi := iterFields(x)
 		for ; t1 != nil && x1 != nil; t1, x1 = ti.Next(), xi.Next() {
 			if t1.Embedded != x1.Embedded {
 				return cmpForNe(t1.Embedded < x1.Embedded)
@@ -1032,8 +1035,8 @@ func (t *Type) cmp(x *Type) ssa.Cmp {
 	case TFUNC:
 		for _, f := range recvsParamsResults {
 			// Loop over fields in structs, ignoring argument names.
-			ta, ia := IterFields(f(t))
-			tb, ib := IterFields(f(x))
+			ta, ia := iterFields(f(t))
+			tb, ib := iterFields(f(x))
 			for ; ta != nil && tb != nil; ta, tb = ia.Next(), ib.Next() {
 				if ta.Isddd != tb.Isddd {
 					return cmpForNe(!ta.Isddd)
@@ -1059,7 +1062,7 @@ func (t *Type) cmp(x *Type) ssa.Cmp {
 		}
 
 	default:
-		e := fmt.Sprintf("Do not know how to compare %s with %s", t, x)
+		e := fmt.Sprintf("Do not know how to compare %v with %v", t, x)
 		panic(e)
 	}
 
@@ -1076,6 +1079,28 @@ func (t *Type) IsBoolean() bool {
 	return t.Etype == TBOOL
 }
 
+var unsignedEType = [...]EType{
+	TINT8:    TUINT8,
+	TUINT8:   TUINT8,
+	TINT16:   TUINT16,
+	TUINT16:  TUINT16,
+	TINT32:   TUINT32,
+	TUINT32:  TUINT32,
+	TINT64:   TUINT64,
+	TUINT64:  TUINT64,
+	TINT:     TUINT,
+	TUINT:    TUINT,
+	TUINTPTR: TUINTPTR,
+}
+
+// toUnsigned returns the unsigned equivalent of integer type t.
+func (t *Type) toUnsigned() *Type {
+	if !t.IsInteger() {
+		Fatalf("unsignedType(%v)", t)
+	}
+	return Types[unsignedEType[t.Etype]]
+}
+
 func (t *Type) IsInteger() bool {
 	switch t.Etype {
 	case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TINT, TUINT, TUINTPTR:
@@ -1160,7 +1185,7 @@ func (t *Type) ElemType() ssa.Type {
 	return t.Elem()
 }
 func (t *Type) PtrTo() ssa.Type {
-	return Ptrto(t)
+	return ptrto(t)
 }
 
 func (t *Type) NumFields() int {
@@ -1207,6 +1232,7 @@ func (t *Type) ChanDir() ChanDir {
 func (t *Type) IsMemory() bool { return false }
 func (t *Type) IsFlags() bool  { return false }
 func (t *Type) IsVoid() bool   { return false }
+func (t *Type) IsTuple() bool  { return false }
 
 // IsUntyped reports whether t is an untyped type.
 func (t *Type) IsUntyped() bool {
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index c8ee941..23c60fa 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -35,8 +35,8 @@ func resolve(n *Node) *Node {
 		if r != nil {
 			if r.Op != OIOTA {
 				n = r
-			} else if n.Name.Iota >= 0 {
-				n = Nodintconst(int64(n.Name.Iota))
+			} else if n.Iota() >= 0 {
+				n = nodintconst(n.Iota())
 			}
 		}
 	}
@@ -157,12 +157,12 @@ func typecheck(n *Node, top int) *Node {
 		// We can already diagnose variables used as types.
 		case ONAME:
 			if top&(Erv|Etype) == Etype {
-				Yyerror("%v is not a type", n)
+				yyerror("%v is not a type", n)
 			}
 
 		case OLITERAL:
 			if top&(Erv|Etype) == Etype {
-				Yyerror("%v is not a type", n)
+				yyerror("%v is not a type", n)
 				break
 			}
 			sprint_depchain(&fmt_, typecheck_tcstack, n, n)
@@ -175,7 +175,7 @@ func typecheck(n *Node, top int) *Node {
 				x := typecheck_tcstack[i]
 				fmt_ += fmt.Sprintf("\n\t%v %v", x.Line(), x)
 			}
-			Yyerror("typechecking loop involving %v%s", n, fmt_)
+			yyerror("typechecking loop involving %v%s", n, fmt_)
 		}
 
 		lineno = lno
@@ -258,7 +258,7 @@ func typecheck1(n *Node, top int) *Node {
 	default:
 		if n.Sym != nil {
 			if n.Op == ONAME && n.Etype != 0 && top&Ecall == 0 {
-				Yyerror("use of builtin %v not in function call", n.Sym)
+				yyerror("use of builtin %v not in function call", n.Sym)
 				n.Type = nil
 				return n
 			}
@@ -305,7 +305,7 @@ OpSwitch:
 		if top&Easgn == 0 {
 			// not a write to the variable
 			if isblank(n) {
-				Yyerror("cannot use _ as value")
+				yyerror("cannot use _ as value")
 				n.Type = nil
 				return n
 			}
@@ -313,17 +313,11 @@ OpSwitch:
 			n.Used = true
 		}
 
-		if top&Ecall == 0 && isunsafebuiltin(n) {
-			Yyerror("%v is not an expression, must be called", n)
-			n.Type = nil
-			return n
-		}
-
 		ok |= Erv
 		break OpSwitch
 
 	case OPACK:
-		Yyerror("use of package %v without selector", n.Sym)
+		yyerror("use of package %v without selector", n.Sym)
 		n.Type = nil
 		return n
 
@@ -340,53 +334,48 @@ OpSwitch:
 
 	case OTARRAY:
 		ok |= Etype
-		var t *Type
-		l := n.Left
-		r := n.Right
-		r = typecheck(r, Etype)
+		r := typecheck(n.Right, Etype)
 		if r.Type == nil {
 			n.Type = nil
 			return n
 		}
 
-		if l == nil {
+		var t *Type
+		if n.Left == nil {
 			t = typSlice(r.Type)
-		} else if l.Op == ODDD {
-			t = typDDDArray(r.Type)
-			if top&Ecomplit == 0 && n.Diag == 0 {
-				t.Broke = true
-				n.Diag = 1
-				Yyerror("use of [...] array outside of array literal")
+		} else if n.Left.Op == ODDD {
+			if top&Ecomplit == 0 {
+				if !n.Diag {
+					n.Diag = true
+					yyerror("use of [...] array outside of array literal")
+				}
+				n.Type = nil
+				return n
 			}
+			t = typDDDArray(r.Type)
 		} else {
-			n.Left = typecheck(n.Left, Erv)
+			n.Left = indexlit(typecheck(n.Left, Erv))
 			l := n.Left
-			var v Val
-			switch consttype(l) {
-			case CTINT, CTRUNE:
-				v = l.Val()
-
-			case CTFLT:
-				v = toint(l.Val())
-
-			default:
+			if consttype(l) != CTINT {
 				if l.Type != nil && l.Type.IsInteger() && l.Op != OLITERAL {
-					Yyerror("non-constant array bound %v", l)
+					yyerror("non-constant array bound %v", l)
 				} else {
-					Yyerror("invalid array bound %v", l)
+					yyerror("invalid array bound %v", l)
 				}
 				n.Type = nil
 				return n
 			}
 
+			v := l.Val()
 			if doesoverflow(v, Types[TINT]) {
-				Yyerror("array bound is too large")
+				yyerror("array bound is too large")
 				n.Type = nil
 				return n
 			}
+
 			bound := v.U.(*Mpint).Int64()
 			if bound < 0 {
-				Yyerror("array bound must be non-negative")
+				yyerror("array bound must be non-negative")
 				n.Type = nil
 				return n
 			}
@@ -411,6 +400,12 @@ OpSwitch:
 			n.Type = nil
 			return n
 		}
+		if l.Type.NotInHeap {
+			yyerror("go:notinheap map key not allowed")
+		}
+		if r.Type.NotInHeap {
+			yyerror("go:notinheap map value not allowed")
+		}
 		n.Op = OTYPE
 		n.Type = typMap(l.Type, r.Type)
 
@@ -422,7 +417,7 @@ OpSwitch:
 				mapqueue = append(mapqueue, mapqueueval{l, n.Lineno})
 			} else if bad.Etype != TANY {
 				// no need to queue, key is already bad
-				Yyerror("invalid map key type %v", l.Type)
+				yyerror("invalid map key type %v", l.Type)
 			}
 		}
 		n.Left = nil
@@ -436,6 +431,9 @@ OpSwitch:
 			n.Type = nil
 			return n
 		}
+		if l.Type.NotInHeap {
+			yyerror("chan of go:notinheap type not allowed")
+		}
 		t := typChan(l.Type, ChanDir(n.Etype)) // TODO(marvin): Fix Node.EType type union.
 		n.Op = OTYPE
 		n.Type = t
@@ -483,14 +481,14 @@ OpSwitch:
 		if l.Op == OTYPE {
 			ok |= Etype
 			n.Op = OTYPE
-			n.Type = Ptrto(l.Type)
+			n.Type = ptrto(l.Type)
 			n.Left = nil
 			break OpSwitch
 		}
 
 		if !t.IsPtr() {
 			if top&(Erv|Etop) != 0 {
-				Yyerror("invalid indirect of %v", Nconv(n.Left, FmtLong))
+				yyerror("invalid indirect of %L", n.Left)
 				n.Type = nil
 				return n
 			}
@@ -539,7 +537,7 @@ OpSwitch:
 				return n
 			}
 			if n.Implicit && !okforarith[l.Type.Etype] {
-				Yyerror("invalid operation: %v (non-numeric type %v)", n, l.Type)
+				yyerror("invalid operation: %v (non-numeric type %v)", n, l.Type)
 				n.Type = nil
 				return n
 			}
@@ -562,14 +560,14 @@ OpSwitch:
 			n.Right = r
 			t := r.Type
 			if !t.IsInteger() || t.IsSigned() {
-				Yyerror("invalid operation: %v (shift count type %v, must be unsigned integer)", n, r.Type)
+				yyerror("invalid operation: %v (shift count type %v, must be unsigned integer)", n, r.Type)
 				n.Type = nil
 				return n
 			}
 
 			t = l.Type
 			if t != nil && t.Etype != TIDEAL && !t.IsInteger() {
-				Yyerror("invalid operation: %v (shift of type %v)", n, t)
+				yyerror("invalid operation: %v (shift of type %v)", n, t)
 				n.Type = nil
 				return n
 			}
@@ -599,7 +597,7 @@ OpSwitch:
 			et = TINT
 		}
 		var aop Op = OXXX
-		if iscmp[n.Op] && t.Etype != TIDEAL && !Eqtype(l.Type, r.Type) {
+		if iscmp[n.Op] && t.Etype != TIDEAL && !eqtype(l.Type, r.Type) {
 			// comparison is okay as long as one side is
 			// assignable to the other.  convert so they have
 			// the same type.
@@ -611,14 +609,14 @@ OpSwitch:
 				aop = assignop(l.Type, r.Type, nil)
 				if aop != 0 {
 					if r.Type.IsInterface() && !l.Type.IsInterface() && !l.Type.IsComparable() {
-						Yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(l.Type))
+						yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(l.Type))
 						n.Type = nil
 						return n
 					}
 
 					dowidth(l.Type)
 					if r.Type.IsInterface() == l.Type.IsInterface() || l.Type.Width >= 1<<16 {
-						l = Nod(aop, l, nil)
+						l = nod(aop, l, nil)
 						l.Type = r.Type
 						l.Typecheck = 1
 						n.Left = l
@@ -633,14 +631,14 @@ OpSwitch:
 				aop = assignop(r.Type, l.Type, nil)
 				if aop != 0 {
 					if l.Type.IsInterface() && !r.Type.IsInterface() && !r.Type.IsComparable() {
-						Yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(r.Type))
+						yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(r.Type))
 						n.Type = nil
 						return n
 					}
 
 					dowidth(r.Type)
 					if r.Type.IsInterface() == l.Type.IsInterface() || r.Type.Width >= 1<<16 {
-						r = Nod(aop, r, nil)
+						r = nod(aop, r, nil)
 						r.Type = l.Type
 						r.Typecheck = 1
 						n.Right = r
@@ -654,17 +652,17 @@ OpSwitch:
 			et = t.Etype
 		}
 
-		if t.Etype != TIDEAL && !Eqtype(l.Type, r.Type) {
+		if t.Etype != TIDEAL && !eqtype(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)
+				yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
 				n.Type = nil
 				return n
 			}
 		}
 
 		if !okfor[op][et] {
-			Yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t))
+			yyerror("invalid operation: %v (operator %v not defined on %s)", n, op, typekind(t))
 			n.Type = nil
 			return n
 		}
@@ -672,32 +670,32 @@ OpSwitch:
 		// okfor allows any array == array, map == map, func == func.
 		// restrict to slice/map/func == nil and nil == slice/map/func.
 		if l.Type.IsArray() && !l.Type.IsComparable() {
-			Yyerror("invalid operation: %v (%v cannot be compared)", n, l.Type)
+			yyerror("invalid operation: %v (%v cannot be compared)", n, l.Type)
 			n.Type = nil
 			return n
 		}
 
 		if l.Type.IsSlice() && !isnil(l) && !isnil(r) {
-			Yyerror("invalid operation: %v (slice can only be compared to nil)", n)
+			yyerror("invalid operation: %v (slice can only be compared to nil)", n)
 			n.Type = nil
 			return n
 		}
 
 		if l.Type.IsMap() && !isnil(l) && !isnil(r) {
-			Yyerror("invalid operation: %v (map can only be compared to nil)", n)
+			yyerror("invalid operation: %v (map can only be compared to nil)", n)
 			n.Type = nil
 			return n
 		}
 
 		if l.Type.Etype == TFUNC && !isnil(l) && !isnil(r) {
-			Yyerror("invalid operation: %v (func can only be compared to nil)", n)
+			yyerror("invalid operation: %v (func can only be compared to nil)", n)
 			n.Type = nil
 			return n
 		}
 
 		if l.Type.IsStruct() {
 			if f := l.Type.IncomparableField(); f != nil {
-				Yyerror("invalid operation: %v (struct containing %v cannot be compared)", n, f.Type)
+				yyerror("invalid operation: %v (struct containing %v cannot be compared)", n, f.Type)
 				n.Type = nil
 				return n
 			}
@@ -755,7 +753,7 @@ OpSwitch:
 
 		if (op == ODIV || op == OMOD) && Isconst(r, CTINT) {
 			if r.Val().U.(*Mpint).CmpInt64(0) == 0 {
-				Yyerror("division by zero")
+				yyerror("division by zero")
 				n.Type = nil
 				return n
 			}
@@ -774,7 +772,7 @@ OpSwitch:
 			return n
 		}
 		if !okfor[n.Op][t.Etype] {
-			Yyerror("invalid operation: %v %v", n.Op, t)
+			yyerror("invalid operation: %v %v", n.Op, t)
 			n.Type = nil
 			return n
 		}
@@ -815,7 +813,7 @@ OpSwitch:
 			n.Type = nil
 			return n
 		}
-		n.Type = Ptrto(t)
+		n.Type = ptrto(t)
 		break OpSwitch
 
 	case OCOMPLIT:
@@ -852,16 +850,16 @@ OpSwitch:
 		if n.Left.Op == OTYPE {
 			if !looktypedot(n, t, 0) {
 				if looktypedot(n, t, 1) {
-					Yyerror("%v undefined (cannot refer to unexported method %v)", n, n.Sym)
+					yyerror("%v undefined (cannot refer to unexported method %v)", n, n.Sym)
 				} else {
-					Yyerror("%v undefined (type %v has no method %v)", n, t, n.Sym)
+					yyerror("%v undefined (type %v has no method %v)", n, t, n.Sym)
 				}
 				n.Type = nil
 				return n
 			}
 
-			if n.Type.Etype != TFUNC || n.Type.Recv() == nil {
-				Yyerror("type %v has no method %v", n.Left.Type, sconv(n.Right.Sym, FmtShort))
+			if n.Type.Etype != TFUNC || !n.IsMethod() {
+				yyerror("type %v has no method %S", n.Left.Type, n.Right.Sym)
 				n.Type = nil
 				return n
 			}
@@ -889,7 +887,7 @@ OpSwitch:
 		}
 
 		if isblanksym(n.Sym) {
-			Yyerror("cannot refer to blank field or method")
+			yyerror("cannot refer to blank field or method")
 			n.Type = nil
 			return n
 		}
@@ -898,21 +896,21 @@ OpSwitch:
 			// Legitimate field or method lookup failed, try to explain the error
 			switch {
 			case t.IsEmptyInterface():
-				Yyerror("%v undefined (type %v is interface with no methods)", n, n.Left.Type)
+				yyerror("%v undefined (type %v is interface with no methods)", n, n.Left.Type)
 
 			case t.IsPtr() && t.Elem().IsInterface():
 				// Pointer to interface is almost always a mistake.
-				Yyerror("%v undefined (type %v is pointer to interface, not interface)", n, n.Left.Type)
+				yyerror("%v undefined (type %v is pointer to interface, not interface)", n, n.Left.Type)
 
 			case lookdot(n, t, 1) != nil:
 				// Field or method matches by name, but it is not exported.
-				Yyerror("%v undefined (cannot refer to unexported field or method %v)", n, n.Sym)
+				yyerror("%v undefined (cannot refer to unexported field or method %v)", n, n.Sym)
 
 			default:
 				if mt := lookdot(n, t, 2); mt != nil { // Case-insensitive lookup.
-					Yyerror("%v undefined (type %v has no field or method %v, but does have %v)", n, n.Left.Type, n.Sym, mt.Sym)
+					yyerror("%v undefined (type %v has no field or method %v, but does have %v)", n, n.Left.Type, n.Sym, mt.Sym)
 				} else {
-					Yyerror("%v undefined (type %v has no field or method %v)", n, n.Left.Type, n.Sym)
+					yyerror("%v undefined (type %v has no field or method %v)", n, n.Left.Type, n.Sym)
 				}
 			}
 			n.Type = nil
@@ -945,7 +943,7 @@ OpSwitch:
 			return n
 		}
 		if !t.IsInterface() {
-			Yyerror("invalid type assertion: %v (non-interface type %v on left)", n, t)
+			yyerror("invalid type assertion: %v (non-interface type %v on left)", n, t)
 			n.Type = nil
 			return n
 		}
@@ -964,13 +962,15 @@ OpSwitch:
 			var ptr int
 			if !implements(n.Type, t, &missing, &have, &ptr) {
 				if have != nil && have.Sym == missing.Sym {
-					Yyerror("impossible type assertion:\n\t%v does not implement %v (wrong type for %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", n.Type, t, missing.Sym, have.Sym, Tconv(have.Type, FmtShort|FmtByte), missing.Sym, Tconv(missing.Type, FmtShort|FmtByte))
+					yyerror("impossible type assertion:\n\t%v does not implement %v (wrong type for %v method)\n"+
+						"\t\thave %v%0S\n\t\twant %v%0S", n.Type, t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
 				} else if ptr != 0 {
-					Yyerror("impossible type assertion:\n\t%v does not implement %v (%v method has pointer receiver)", n.Type, t, missing.Sym)
+					yyerror("impossible type assertion:\n\t%v does not implement %v (%v method has pointer receiver)", n.Type, t, missing.Sym)
 				} else if have != nil {
-					Yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)\n"+"\t\thave %v%v\n\t\twant %v%v", n.Type, t, missing.Sym, have.Sym, Tconv(have.Type, FmtShort|FmtByte), missing.Sym, Tconv(missing.Type, FmtShort|FmtByte))
+					yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)\n"+
+						"\t\thave %v%0S\n\t\twant %v%0S", n.Type, t, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
 				} else {
-					Yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)", n.Type, t, missing.Sym)
+					yyerror("impossible type assertion:\n\t%v does not implement %v (missing %v method)", n.Type, t, missing.Sym)
 				}
 				n.Type = nil
 				return n
@@ -994,7 +994,7 @@ OpSwitch:
 		}
 		switch t.Etype {
 		default:
-			Yyerror("invalid operation: %v (type %v does not support indexing)", n, t)
+			yyerror("invalid operation: %v (type %v does not support indexing)", n, t)
 			n.Type = nil
 			return n
 
@@ -1013,20 +1013,20 @@ OpSwitch:
 			}
 
 			if n.Right.Type != nil && !n.Right.Type.IsInteger() {
-				Yyerror("non-integer %s index %v", why, n.Right)
+				yyerror("non-integer %s index %v", why, n.Right)
 				break
 			}
 
 			if !n.Bounded && Isconst(n.Right, CTINT) {
 				x := n.Right.Int64()
 				if x < 0 {
-					Yyerror("invalid %s index %v (index must be non-negative)", why, n.Right)
+					yyerror("invalid %s index %v (index must be non-negative)", why, n.Right)
 				} else if t.IsArray() && x >= t.NumElem() {
-					Yyerror("invalid array index %v (out of bounds for %d-element array)", n.Right, t.NumElem())
+					yyerror("invalid array index %v (out of bounds for %d-element array)", n.Right, t.NumElem())
 				} else if Isconst(n.Left, CTSTR) && x >= int64(len(n.Left.Val().U.(string))) {
-					Yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.Val().U.(string)))
-				} else if n.Right.Val().U.(*Mpint).Cmp(Maxintval[TINT]) > 0 {
-					Yyerror("invalid %s index %v (index too large)", why, n.Right)
+					yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.Val().U.(string)))
+				} else if n.Right.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
+					yyerror("invalid %s index %v (index too large)", why, n.Right)
 				}
 			}
 
@@ -1053,13 +1053,13 @@ OpSwitch:
 			return n
 		}
 		if !t.IsChan() {
-			Yyerror("invalid operation: %v (receive from non-chan type %v)", n, t)
+			yyerror("invalid operation: %v (receive from non-chan type %v)", n, t)
 			n.Type = nil
 			return n
 		}
 
 		if !t.ChanDir().CanRecv() {
-			Yyerror("invalid operation: %v (receive from send-only type %v)", n, t)
+			yyerror("invalid operation: %v (receive from send-only type %v)", n, t)
 			n.Type = nil
 			return n
 		}
@@ -1080,13 +1080,13 @@ OpSwitch:
 			return n
 		}
 		if !t.IsChan() {
-			Yyerror("invalid operation: %v (send to non-chan type %v)", n, t)
+			yyerror("invalid operation: %v (send to non-chan type %v)", n, t)
 			n.Type = nil
 			return n
 		}
 
 		if !t.ChanDir().CanSend() {
-			Yyerror("invalid operation: %v (send to receive-only type %v)", n, t)
+			yyerror("invalid operation: %v (send to receive-only type %v)", n, t)
 			n.Type = nil
 			return n
 		}
@@ -1121,12 +1121,12 @@ OpSwitch:
 		l := n.Left
 		if l.Type.IsArray() {
 			if !islvalue(n.Left) {
-				Yyerror("invalid operation %v (slice of unaddressable value)", n)
+				yyerror("invalid operation %v (slice of unaddressable value)", n)
 				n.Type = nil
 				return n
 			}
 
-			n.Left = Nod(OADDR, n.Left, nil)
+			n.Left = nod(OADDR, n.Left, nil)
 			n.Left.Implicit = true
 			n.Left = typecheck(n.Left, Erv)
 			l = n.Left
@@ -1140,7 +1140,7 @@ OpSwitch:
 		var tp *Type
 		if t.IsString() {
 			if hasmax {
-				Yyerror("invalid operation %v (3-index slice of string)", n)
+				yyerror("invalid operation %v (3-index slice of string)", n)
 				n.Type = nil
 				return n
 			}
@@ -1158,7 +1158,7 @@ OpSwitch:
 		} else if t.IsSlice() {
 			n.Type = t
 		} else {
-			Yyerror("cannot slice %v (type %v)", l, t)
+			yyerror("cannot slice %v (type %v)", l, t)
 			n.Type = nil
 			return n
 		}
@@ -1183,33 +1183,22 @@ OpSwitch:
 
 	// call and call like
 	case OCALL:
-		l := n.Left
-
-		if l.Op == ONAME {
-			r := unsafenmagic(n)
-			if r != nil {
-				if n.Isddd {
-					Yyerror("invalid use of ... with builtin %v", l)
-				}
-				n = r
-				n = typecheck1(n, top)
-				return n
-			}
+		n.Left = typecheck(n.Left, Erv|Etype|Ecall)
+		if n.Left.Diag {
+			n.Diag = true
 		}
 
-		n.Left = typecheck(n.Left, Erv|Etype|Ecall)
-		n.Diag |= n.Left.Diag
-		l = n.Left
+		l := n.Left
+
 		if l.Op == ONAME && l.Etype != 0 {
 			// TODO(marvin): Fix Node.EType type union.
 			if n.Isddd && Op(l.Etype) != OAPPEND {
-				Yyerror("invalid use of ... with builtin %v", l)
+				yyerror("invalid use of ... with builtin %v", l)
 			}
 
 			// builtin: OLEN, OCAP, etc.
 			// TODO(marvin): Fix Node.EType type union.
 			n.Op = Op(l.Etype)
-
 			n.Left = n.Right
 			n.Right = nil
 			n = typecheck1(n, top)
@@ -1221,9 +1210,9 @@ OpSwitch:
 		if l.Op == OTYPE {
 			if n.Isddd || l.Type.isDDDArray() {
 				if !l.Type.Broke {
-					Yyerror("invalid use of ... in type conversion to %v", l.Type)
+					yyerror("invalid use of ... in type conversion to %v", l.Type)
 				}
-				n.Diag = 1
+				n.Diag = true
 			}
 
 			// pick off before type-checking arguments
@@ -1267,14 +1256,14 @@ OpSwitch:
 			// 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 || !eqtype(l.Left.Type, tp) {
 				Fatalf("method receiver")
 			}
 
 		default:
 			n.Op = OCALLFUNC
 			if t.Etype != TFUNC {
-				Yyerror("cannot call non-function %v (type %v)", l, t)
+				yyerror("cannot call non-function %v (type %v)", l, t)
 				n.Type = nil
 				return n
 			}
@@ -1304,7 +1293,7 @@ OpSwitch:
 
 		// multiple return
 		if top&(Efnstruct|Etop) == 0 {
-			Yyerror("multiple-value %v() in single-value context", l)
+			yyerror("multiple-value %v() in single-value context", l)
 			break OpSwitch
 		}
 
@@ -1312,6 +1301,21 @@ OpSwitch:
 
 		break OpSwitch
 
+	case OALIGNOF, OOFFSETOF, OSIZEOF:
+		ok |= Erv
+		if !onearg(n, "%v", n.Op) {
+			n.Type = nil
+			return n
+		}
+
+		// any side effects disappear; ignore init
+		var r Node
+		Nodconst(&r, Types[TUINTPTR], evalunsafe(n))
+		r.Orig = n
+		n = &r
+
+		break OpSwitch
+
 	case OCAP, OLEN, OREAL, OIMAG:
 		ok |= Erv
 		if !onearg(n, "%v", n.Op) {
@@ -1380,7 +1384,7 @@ OpSwitch:
 		break OpSwitch
 
 	badcall1:
-		Yyerror("invalid argument %v for %v", Nconv(n.Left, FmtLong), n.Op)
+		yyerror("invalid argument %L for %v", n.Left, n.Op)
 		n.Type = nil
 		return n
 
@@ -1391,14 +1395,18 @@ OpSwitch:
 		if n.List.Len() == 1 {
 			typecheckslice(n.List.Slice(), Efnstruct)
 			if n.List.First().Op != OCALLFUNC && n.List.First().Op != OCALLMETH {
-				Yyerror("invalid operation: complex expects two arguments")
+				yyerror("invalid operation: complex expects two arguments")
 				n.Type = nil
 				return n
 			}
 
 			t := n.List.First().Left.Type
+			if !t.IsKind(TFUNC) {
+				// Bail. This error will be reported elsewhere.
+				return n
+			}
 			if t.Results().NumFields() != 2 {
-				Yyerror("invalid operation: complex expects two arguments, %v returns %d results", n.List.First(), t.Results().NumFields())
+				yyerror("invalid operation: complex expects two arguments, %v returns %d results", n.List.First(), t.Results().NumFields())
 				n.Type = nil
 				return n
 			}
@@ -1428,8 +1436,8 @@ OpSwitch:
 			n.Right = r
 		}
 
-		if !Eqtype(l.Type, r.Type) {
-			Yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
+		if !eqtype(l.Type, r.Type) {
+			yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
 			n.Type = nil
 			return n
 		}
@@ -1437,7 +1445,7 @@ OpSwitch:
 		var t *Type
 		switch l.Type.Etype {
 		default:
-			Yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type)
+			yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type)
 			n.Type = nil
 			return n
 
@@ -1476,13 +1484,13 @@ OpSwitch:
 			return n
 		}
 		if !t.IsChan() {
-			Yyerror("invalid operation: %v (non-chan type %v)", n, t)
+			yyerror("invalid operation: %v (non-chan type %v)", n, t)
 			n.Type = nil
 			return n
 		}
 
 		if !t.ChanDir().CanSend() {
-			Yyerror("invalid operation: %v (cannot close receive-only channel)", n)
+			yyerror("invalid operation: %v (cannot close receive-only channel)", n)
 			n.Type = nil
 			return n
 		}
@@ -1493,19 +1501,19 @@ OpSwitch:
 	case ODELETE:
 		args := n.List
 		if args.Len() == 0 {
-			Yyerror("missing arguments to delete")
+			yyerror("missing arguments to delete")
 			n.Type = nil
 			return n
 		}
 
 		if args.Len() == 1 {
-			Yyerror("missing second (key) argument to delete")
+			yyerror("missing second (key) argument to delete")
 			n.Type = nil
 			return n
 		}
 
 		if args.Len() != 2 {
-			Yyerror("too many arguments to delete")
+			yyerror("too many arguments to delete")
 			n.Type = nil
 			return n
 		}
@@ -1515,7 +1523,7 @@ OpSwitch:
 		l := args.First()
 		r := args.Second()
 		if l.Type != nil && !l.Type.IsMap() {
-			Yyerror("first argument to delete must be map; have %v", Tconv(l.Type, FmtLong))
+			yyerror("first argument to delete must be map; have %L", l.Type)
 			n.Type = nil
 			return n
 		}
@@ -1527,7 +1535,7 @@ OpSwitch:
 		ok |= Erv
 		args := n.List
 		if args.Len() == 0 {
-			Yyerror("missing arguments to append")
+			yyerror("missing arguments to append")
 			n.Type = nil
 			return n
 		}
@@ -1554,25 +1562,25 @@ OpSwitch:
 		n.Type = t
 		if !t.IsSlice() {
 			if Isconst(args.First(), CTNIL) {
-				Yyerror("first argument to append must be typed slice; have untyped nil")
+				yyerror("first argument to append must be typed slice; have untyped nil")
 				n.Type = nil
 				return n
 			}
 
-			Yyerror("first argument to append must be slice; have %v", Tconv(t, FmtLong))
+			yyerror("first argument to append must be slice; have %L", t)
 			n.Type = nil
 			return n
 		}
 
 		if n.Isddd {
 			if args.Len() == 1 {
-				Yyerror("cannot use ... on first argument to append")
+				yyerror("cannot use ... on first argument to append")
 				n.Type = nil
 				return n
 			}
 
 			if args.Len() != 2 {
-				Yyerror("too many arguments to append")
+				yyerror("too many arguments to append")
 				n.Type = nil
 				return n
 			}
@@ -1587,10 +1595,10 @@ OpSwitch:
 		}
 
 		if funarg != nil {
-			_, it := IterFields(funarg) // Skip first field
+			_, it := iterFields(funarg) // Skip first field
 			for t := it.Next(); t != nil; t = it.Next() {
 				if assignop(t.Type, n.Type.Elem(), nil) == 0 {
-					Yyerror("cannot append %v value to []%v", t.Type, n.Type.Elem())
+					yyerror("cannot append %v value to []%v", t.Type, n.Type.Elem())
 				}
 			}
 		} else {
@@ -1609,13 +1617,13 @@ OpSwitch:
 		ok |= Etop | Erv
 		args := n.List
 		if args.Len() < 2 {
-			Yyerror("missing arguments to copy")
+			yyerror("missing arguments to copy")
 			n.Type = nil
 			return n
 		}
 
 		if args.Len() > 2 {
-			Yyerror("too many arguments to copy")
+			yyerror("too many arguments to copy")
 			n.Type = nil
 			return n
 		}
@@ -1639,28 +1647,28 @@ OpSwitch:
 
 		// copy([]byte, string)
 		if n.Left.Type.IsSlice() && n.Right.Type.IsString() {
-			if Eqtype(n.Left.Type.Elem(), bytetype) {
+			if eqtype(n.Left.Type.Elem(), bytetype) {
 				break OpSwitch
 			}
-			Yyerror("arguments to copy have different element types: %v and string", Tconv(n.Left.Type, FmtLong))
+			yyerror("arguments to copy have different element types: %L and string", n.Left.Type)
 			n.Type = nil
 			return n
 		}
 
 		if !n.Left.Type.IsSlice() || !n.Right.Type.IsSlice() {
 			if !n.Left.Type.IsSlice() && !n.Right.Type.IsSlice() {
-				Yyerror("arguments to copy must be slices; have %v, %v", Tconv(n.Left.Type, FmtLong), Tconv(n.Right.Type, FmtLong))
+				yyerror("arguments to copy must be slices; have %L, %L", n.Left.Type, n.Right.Type)
 			} else if !n.Left.Type.IsSlice() {
-				Yyerror("first argument to copy should be slice; have %v", Tconv(n.Left.Type, FmtLong))
+				yyerror("first argument to copy should be slice; have %L", n.Left.Type)
 			} else {
-				Yyerror("second argument to copy should be slice or string; have %v", Tconv(n.Right.Type, FmtLong))
+				yyerror("second argument to copy should be slice or string; have %L", n.Right.Type)
 			}
 			n.Type = nil
 			return n
 		}
 
-		if !Eqtype(n.Left.Type.Elem(), n.Right.Type.Elem()) {
-			Yyerror("arguments to copy have different element types: %v and %v", Tconv(n.Left.Type, FmtLong), Tconv(n.Right.Type, FmtLong))
+		if !eqtype(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
 		}
@@ -1680,9 +1688,9 @@ OpSwitch:
 		var why string
 		n.Op = convertop(t, n.Type, &why)
 		if n.Op == 0 {
-			if n.Diag == 0 && !n.Type.Broke {
-				Yyerror("cannot convert %v to type %v%s", Nconv(n.Left, FmtLong), n.Type, why)
-				n.Diag = 1
+			if !n.Diag && !n.Type.Broke {
+				yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why)
+				n.Diag = true
 			}
 
 			n.Op = OCONV
@@ -1691,7 +1699,7 @@ OpSwitch:
 		switch n.Op {
 		case OCONVNOP:
 			if n.Left.Op == OLITERAL {
-				r := Nod(OXXX, nil, nil)
+				r := nod(OXXX, nil, nil)
 				n.Op = OCONV
 				n.Orig = r
 				*r = *n
@@ -1699,7 +1707,7 @@ OpSwitch:
 				n.SetVal(n.Left.Val())
 			}
 
-			// do not use stringtoarraylit.
+		// do not use stringtoarraylit.
 		// generated code and compiler memory footprint is better without it.
 		case OSTRARRAYBYTE:
 			break
@@ -1716,7 +1724,7 @@ OpSwitch:
 		ok |= Erv
 		args := n.List.Slice()
 		if len(args) == 0 {
-			Yyerror("missing argument to make")
+			yyerror("missing argument to make")
 			n.Type = nil
 			return n
 		}
@@ -1733,13 +1741,13 @@ OpSwitch:
 		i := 1
 		switch t.Etype {
 		default:
-			Yyerror("cannot make type %v", t)
+			yyerror("cannot make type %v", t)
 			n.Type = nil
 			return n
 
 		case TSLICE:
 			if i >= len(args) {
-				Yyerror("missing len argument to make(%v)", t)
+				yyerror("missing len argument to make(%v)", t)
 				n.Type = nil
 				return n
 			}
@@ -1763,7 +1771,7 @@ OpSwitch:
 				return n
 			}
 			if Isconst(l, CTINT) && r != nil && Isconst(r, CTINT) && l.Val().U.(*Mpint).Cmp(r.Val().U.(*Mpint)) > 0 {
-				Yyerror("len larger than cap in make(%v)", t)
+				yyerror("len larger than cap in make(%v)", t)
 				n.Type = nil
 				return n
 			}
@@ -1788,7 +1796,7 @@ OpSwitch:
 				}
 				n.Left = l
 			} else {
-				n.Left = Nodintconst(0)
+				n.Left = nodintconst(0)
 			}
 			n.Op = OMAKEMAP
 
@@ -1809,13 +1817,13 @@ OpSwitch:
 				}
 				n.Left = l
 			} else {
-				n.Left = Nodintconst(0)
+				n.Left = nodintconst(0)
 			}
 			n.Op = OMAKECHAN
 		}
 
 		if i < len(args) {
-			Yyerror("too many arguments to make(%v)", t)
+			yyerror("too many arguments to make(%v)", t)
 			n.Op = OMAKE
 			n.Type = nil
 			return n
@@ -1828,7 +1836,7 @@ OpSwitch:
 		ok |= Erv
 		args := n.List
 		if args.Len() == 0 {
-			Yyerror("missing argument to new")
+			yyerror("missing argument to new")
 			n.Type = nil
 			return n
 		}
@@ -1841,13 +1849,13 @@ OpSwitch:
 			return n
 		}
 		if args.Len() > 1 {
-			Yyerror("too many arguments to new(%v)", t)
+			yyerror("too many arguments to new(%v)", t)
 			n.Type = nil
 			return n
 		}
 
 		n.Left = l
-		n.Type = Ptrto(t)
+		n.Type = ptrto(t)
 		break OpSwitch
 
 	case OPRINT, OPRINTN:
@@ -1882,7 +1890,7 @@ OpSwitch:
 	case ORECOVER:
 		ok |= Erv | Etop
 		if n.List.Len() != 0 {
-			Yyerror("too many arguments to recover")
+			yyerror("too many arguments to recover")
 			n.Type = nil
 			return n
 		}
@@ -1909,7 +1917,13 @@ OpSwitch:
 		if !t.IsInterface() {
 			Fatalf("OITAB of %v", t)
 		}
-		n.Type = Ptrto(Types[TUINTPTR])
+		n.Type = ptrto(Types[TUINTPTR])
+		break OpSwitch
+
+	case OIDATA:
+		// Whoever creates the OIDATA node must know a priori the concrete type at that moment,
+		// usually by just having checked the OITAB.
+		Fatalf("cannot typecheck interface data %v", n)
 		break OpSwitch
 
 	case OSPTR:
@@ -1924,9 +1938,9 @@ OpSwitch:
 			Fatalf("OSPTR of %v", t)
 		}
 		if t.IsString() {
-			n.Type = Ptrto(Types[TUINT8])
+			n.Type = ptrto(Types[TUINT8])
 		} else {
-			n.Type = Ptrto(t.Elem())
+			n.Type = ptrto(t.Elem())
 		}
 		break OpSwitch
 
@@ -1952,7 +1966,7 @@ OpSwitch:
 		typecheckas(n)
 
 		// Code that creates temps does not bother to set defn, so do it here.
-		if n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") {
+		if n.Left.Op == ONAME && n.Left.IsAutoTmp() {
 			n.Left.Name.Defn = n
 		}
 		break OpSwitch
@@ -1981,7 +1995,7 @@ OpSwitch:
 	case ODEFER:
 		ok |= Etop
 		n.Left = typecheck(n.Left, Etop|Erv)
-		if n.Left.Diag == 0 {
+		if !n.Left.Diag {
 			checkdefergo(n)
 		}
 		break OpSwitch
@@ -2000,7 +2014,7 @@ OpSwitch:
 		if n.Left != nil {
 			t := n.Left.Type
 			if t != nil && !t.IsBoolean() {
-				Yyerror("non-bool %v used as for condition", Nconv(n.Left, FmtLong))
+				yyerror("non-bool %L used as for condition", n.Left)
 			}
 		}
 		n.Right = typecheck(n.Right, Etop)
@@ -2015,7 +2029,7 @@ OpSwitch:
 		if n.Left != nil {
 			t := n.Left.Type
 			if t != nil && !t.IsBoolean() {
-				Yyerror("non-bool %v used as if condition", Nconv(n.Left, FmtLong))
+				yyerror("non-bool %L used as if condition", n.Left)
 			}
 		}
 		typecheckslice(n.Nbody.Slice(), Etop)
@@ -2030,7 +2044,7 @@ OpSwitch:
 			typecheckslice(n.List.Slice(), Erv)
 		}
 		if Curfn == nil {
-			Yyerror("return outside function")
+			yyerror("return outside function")
 			n.Type = nil
 			return n
 		}
@@ -2061,7 +2075,7 @@ OpSwitch:
 		break OpSwitch
 
 	case OTYPESW:
-		Yyerror("use of .(type) outside type switch")
+		yyerror("use of .(type) outside type switch")
 		n.Type = nil
 		return n
 
@@ -2084,8 +2098,12 @@ OpSwitch:
 	case ODCLTYPE:
 		ok |= Etop
 		n.Left = typecheck(n.Left, Etype)
-		if incannedimport == 0 {
-			checkwidth(n.Left.Type)
+		checkwidth(n.Left.Type)
+		if n.Left.Type != nil && n.Left.Type.NotInHeap && n.Left.Name.Param.Pragma&NotInHeap == 0 {
+			// The type contains go:notinheap types, so it
+			// must be marked as such (alternatively, we
+			// could silently propagate go:notinheap).
+			yyerror("type %v must be go:notinheap", n.Left.Type)
 		}
 		break OpSwitch
 	}
@@ -2102,34 +2120,34 @@ OpSwitch:
 		}
 	}
 
-	if safemode && incannedimport == 0 && importpkg == nil && compiling_wrappers == 0 && t != nil && t.Etype == TUNSAFEPTR {
-		Yyerror("cannot use unsafe.Pointer")
+	if safemode && importpkg == nil && compiling_wrappers == 0 && t != nil && t.Etype == TUNSAFEPTR {
+		yyerror("cannot use unsafe.Pointer")
 	}
 
 	evconst(n)
 	if n.Op == OTYPE && top&Etype == 0 {
-		Yyerror("type %v is not an expression", n.Type)
+		yyerror("type %v is not an expression", n.Type)
 		n.Type = nil
 		return n
 	}
 
 	if top&(Erv|Etype) == Etype && n.Op != OTYPE {
-		Yyerror("%v is not a type", n)
+		yyerror("%v is not a type", n)
 		n.Type = nil
 		return n
 	}
 
 	// TODO(rsc): simplify
 	if (top&(Ecall|Erv|Etype) != 0) && top&Etop == 0 && ok&(Erv|Etype|Ecall) == 0 {
-		Yyerror("%v used as value", n)
+		yyerror("%v used as value", n)
 		n.Type = nil
 		return n
 	}
 
 	if (top&Etop != 0) && top&(Ecall|Erv|Etype) == 0 && ok&Etop == 0 {
-		if n.Diag == 0 {
-			Yyerror("%v evaluated but not used", n)
-			n.Diag = 1
+		if !n.Diag {
+			yyerror("%v evaluated but not used", n)
+			n.Diag = true
 		}
 
 		n.Type = nil
@@ -2149,22 +2167,22 @@ func checksliceindex(l *Node, r *Node, tp *Type) bool {
 		return false
 	}
 	if !t.IsInteger() {
-		Yyerror("invalid slice index %v (type %v)", r, t)
+		yyerror("invalid slice index %v (type %v)", r, t)
 		return false
 	}
 
 	if r.Op == OLITERAL {
 		if r.Int64() < 0 {
-			Yyerror("invalid slice index %v (index must be non-negative)", r)
+			yyerror("invalid slice index %v (index must be non-negative)", r)
 			return false
 		} else if tp != nil && tp.NumElem() > 0 && r.Int64() > tp.NumElem() {
-			Yyerror("invalid slice index %v (out of bounds for %d-element array)", r, tp.NumElem())
+			yyerror("invalid slice index %v (out of bounds for %d-element array)", r, tp.NumElem())
 			return false
 		} else if Isconst(l, CTSTR) && r.Int64() > int64(len(l.Val().U.(string))) {
-			Yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.Val().U.(string)))
+			yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.Val().U.(string)))
 			return false
-		} else if r.Val().U.(*Mpint).Cmp(Maxintval[TINT]) > 0 {
-			Yyerror("invalid slice index %v (index too large)", r)
+		} else if r.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
+			yyerror("invalid slice index %v (index too large)", r)
 			return false
 		}
 	}
@@ -2174,7 +2192,7 @@ func checksliceindex(l *Node, r *Node, tp *Type) bool {
 
 func checksliceconst(lo *Node, hi *Node) bool {
 	if lo != nil && hi != nil && lo.Op == OLITERAL && hi.Op == OLITERAL && lo.Val().U.(*Mpint).Cmp(hi.Val().U.(*Mpint)) > 0 {
-		Yyerror("invalid slice index: %v > %v", lo, hi)
+		yyerror("invalid slice index: %v > %v", lo, hi)
 		return false
 	}
 
@@ -2216,7 +2234,7 @@ func checkdefergo(n *Node) {
 		if n.Left.Orig != nil && n.Left.Orig.Op == OCONV {
 			break
 		}
-		Yyerror("%s discards result of %v", what, n.Left)
+		yyerror("%s discards result of %v", what, n.Left)
 		return
 	}
 
@@ -2226,12 +2244,11 @@ func checkdefergo(n *Node) {
 		return
 	}
 
-	if n.Diag == 0 {
+	if !n.Diag {
 		// The syntax made sure it was a call, so this must be
 		// a conversion.
-		n.Diag = 1
-
-		Yyerror("%s requires function call, not conversion", what)
+		n.Diag = true
+		yyerror("%s requires function call, not conversion", what)
 	}
 }
 
@@ -2250,7 +2267,7 @@ func implicitstar(n *Node) *Node {
 	if !t.IsArray() {
 		return n
 	}
-	n = Nod(OIND, n, nil)
+	n = nod(OIND, n, nil)
 	n.Implicit = true
 	n = typecheck(n, Erv)
 	return n
@@ -2262,13 +2279,13 @@ func onearg(n *Node, f string, args ...interface{}) bool {
 	}
 	if n.List.Len() == 0 {
 		p := fmt.Sprintf(f, args...)
-		Yyerror("missing argument to %s: %v", p, n)
+		yyerror("missing argument to %s: %v", p, n)
 		return false
 	}
 
 	if n.List.Len() > 1 {
 		p := fmt.Sprintf(f, args...)
-		Yyerror("too many arguments to %s: %v", p, n)
+		yyerror("too many arguments to %s: %v", p, n)
 		n.Left = n.List.First()
 		n.List.Set(nil)
 		return false
@@ -2284,19 +2301,19 @@ func twoarg(n *Node) bool {
 		return true
 	}
 	if n.List.Len() == 0 {
-		Yyerror("missing argument to %v - %v", n.Op, n)
+		yyerror("missing argument to %v - %v", n.Op, n)
 		return false
 	}
 
 	n.Left = n.List.First()
 	if n.List.Len() == 1 {
-		Yyerror("missing argument to %v - %v", n.Op, n)
+		yyerror("missing argument to %v - %v", n.Op, n)
 		n.List.Set(nil)
 		return false
 	}
 
 	if n.List.Len() > 2 {
-		Yyerror("too many arguments to %v - %v", n.Op, n)
+		yyerror("too many arguments to %v - %v", n.Op, n)
 		n.List.Set(nil)
 		return false
 	}
@@ -2320,11 +2337,11 @@ func lookdot1(errnode *Node, s *Sym, t *Type, fs *Fields, dostrcmp int) *Field {
 		}
 		if r != nil {
 			if errnode != nil {
-				Yyerror("ambiguous selector %v", errnode)
+				yyerror("ambiguous selector %v", errnode)
 			} else if t.IsPtr() {
-				Yyerror("ambiguous selector (%v).%v", t, s)
+				yyerror("ambiguous selector (%v).%v", t, s)
 			} else {
-				Yyerror("ambiguous selector %v.%v", t, s)
+				yyerror("ambiguous selector %v.%v", t, s)
 			}
 			break
 		}
@@ -2353,7 +2370,7 @@ func looktypedot(n *Node, t *Type, dostrcmp int) bool {
 
 	// Find the base type: methtype will fail if t
 	// is not of the form T or *T.
-	mt := methtype(t, 0)
+	mt := methtype(t)
 	if mt == nil {
 		return false
 	}
@@ -2366,7 +2383,7 @@ func looktypedot(n *Node, t *Type, dostrcmp int) bool {
 
 	// disallow T.m if m requires *T receiver
 	if f2.Type.Recv().Type.IsPtr() && !t.IsPtr() && f2.Embedded != 2 && !isifacemethod(f2.Type) {
-		Yyerror("invalid method expression %v (needs pointer receiver: (*%v).%v)", n, t, sconv(f2.Sym, FmtShort))
+		yyerror("invalid method expression %v (needs pointer receiver: (*%v).%S)", n, t, f2.Sym)
 		return false
 	}
 
@@ -2404,7 +2421,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field {
 
 	var f2 *Field
 	if n.Left.Type == t || n.Left.Type.Sym == nil {
-		mt := methtype(t, 0)
+		mt := methtype(t)
 		if mt != nil {
 			// Use f2->method, not f2->xmethod: adddot has
 			// already inserted all the necessary embedded dots.
@@ -2418,7 +2435,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field {
 			return f1
 		}
 		if f2 != nil {
-			Yyerror("%v is both field and method", n.Sym)
+			yyerror("%v is both field and method", n.Sym)
 		}
 		if f1.Offset == BADWIDTH {
 			Fatalf("lookdot badwidth %v %p", f1, f1)
@@ -2430,7 +2447,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field {
 		}
 		if t.IsInterface() {
 			if n.Left.Type.IsPtr() {
-				n.Left = Nod(OIND, n.Left, nil) // implicitstar
+				n.Left = nod(OIND, n.Left, nil) // implicitstar
 				n.Left.Implicit = true
 				n.Left = typecheck(n.Left, Erv)
 			}
@@ -2449,24 +2466,24 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field {
 		tt := n.Left.Type
 		dowidth(tt)
 		rcvr := f2.Type.Recv().Type
-		if !Eqtype(rcvr, tt) {
-			if rcvr.Etype == Tptr && Eqtype(rcvr.Elem(), tt) {
+		if !eqtype(rcvr, tt) {
+			if rcvr.Etype == Tptr && eqtype(rcvr.Elem(), tt) {
 				checklvalue(n.Left, "call pointer method on")
-				n.Left = Nod(OADDR, n.Left, nil)
+				n.Left = nod(OADDR, n.Left, nil)
 				n.Left.Implicit = true
 				n.Left = typecheck(n.Left, Etype|Erv)
-			} else if tt.Etype == Tptr && rcvr.Etype != Tptr && Eqtype(tt.Elem(), rcvr) {
-				n.Left = Nod(OIND, n.Left, nil)
+			} else if tt.Etype == Tptr && rcvr.Etype != Tptr && eqtype(tt.Elem(), rcvr) {
+				n.Left = nod(OIND, n.Left, nil)
 				n.Left.Implicit = true
 				n.Left = typecheck(n.Left, Etype|Erv)
-			} else if tt.Etype == Tptr && tt.Elem().Etype == Tptr && Eqtype(derefall(tt), derefall(rcvr)) {
-				Yyerror("calling method %v with receiver %v requires explicit dereference", n.Sym, Nconv(n.Left, FmtLong))
+			} else if tt.Etype == Tptr && tt.Elem().Etype == Tptr && eqtype(derefall(tt), derefall(rcvr)) {
+				yyerror("calling method %v with receiver %L requires explicit dereference", n.Sym, n.Left)
 				for tt.Etype == Tptr {
 					// Stop one level early for method with pointer receiver.
 					if rcvr.Etype == Tptr && tt.Elem().Etype != Tptr {
 						break
 					}
-					n.Left = Nod(OIND, n.Left, nil)
+					n.Left = nod(OIND, n.Left, nil)
 					n.Left.Implicit = true
 					n.Left = typecheck(n.Left, Etype|Erv)
 					tt = tt.Elem()
@@ -2504,7 +2521,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field {
 
 func nokeys(l Nodes) bool {
 	for _, n := range l.Slice() {
-		if n.Op == OKEY {
+		if n.Op == OKEY || n.Op == OSTRUCTKEY {
 			return false
 		}
 	}
@@ -2551,16 +2568,16 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *Type, nl Nodes, desc
 					}
 				}
 
-				tn, it := IterFields(n.Type)
+				tn, it := iterFields(n.Type)
 				var why string
 				for _, tl := range tstruct.Fields().Slice() {
 					if tl.Isddd {
 						for ; tn != nil; tn = it.Next() {
 							if assignop(tn.Type, tl.Type.Elem(), &why) == 0 {
 								if call != nil {
-									Yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type.Elem(), call, why)
+									yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type.Elem(), call, why)
 								} else {
-									Yyerror("cannot use %v as type %v in %s%s", tn.Type, tl.Type.Elem(), desc(), why)
+									yyerror("cannot use %v as type %v in %s%s", tn.Type, tl.Type.Elem(), desc(), why)
 								}
 							}
 						}
@@ -2573,9 +2590,9 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *Type, nl Nodes, desc
 					}
 					if assignop(tn.Type, tl.Type, &why) == 0 {
 						if call != nil {
-							Yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type, call, why)
+							yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type, call, why)
 						} else {
-							Yyerror("cannot use %v as type %v in %s%s", tn.Type, tl.Type, desc(), why)
+							yyerror("cannot use %v as type %v in %s%s", tn.Type, tl.Type, desc(), why)
 						}
 					}
 
@@ -2660,9 +2677,9 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *Type, nl Nodes, desc
 	}
 	if isddd {
 		if call != nil {
-			Yyerror("invalid use of ... in call to %v", call)
+			yyerror("invalid use of ... in call to %v", call)
 		} else {
-			Yyerror("invalid use of ... in %v", op)
+			yyerror("invalid use of ... in %v", op)
 		}
 	}
 
@@ -2671,21 +2688,21 @@ out:
 	return
 
 notenough:
-	if n == nil || n.Diag == 0 {
+	if n == nil || !n.Diag {
 		if call != nil {
 			// call is the expression being called, not the overall call.
 			// Method expressions have the form T.M, and the compiler has
 			// rewritten those to ONAME nodes but left T in Left.
 			if call.Op == ONAME && call.Left != nil && call.Left.Op == OTYPE {
-				Yyerror("not enough arguments in call to method expression %v", call)
+				yyerror("not enough arguments in call to method expression %v\n\thave %s\n\twant %v", call, nl.retsigerr(isddd), tstruct)
 			} else {
-				Yyerror("not enough arguments in call to %v", call)
+				yyerror("not enough arguments in call to %v\n\thave %s\n\twant %v", call, nl.retsigerr(isddd), tstruct)
 			}
 		} else {
-			Yyerror("not enough arguments to %v", op)
+			yyerror("not enough arguments to %v\n\thave %s\n\twant %v", op, nl.retsigerr(isddd), tstruct)
 		}
 		if n != nil {
-			n.Diag = 1
+			n.Diag = true
 		}
 	}
 
@@ -2693,21 +2710,63 @@ notenough:
 
 toomany:
 	if call != nil {
-		Yyerror("too many arguments in call to %v", call)
+		yyerror("too many arguments in call to %v\n\thave %s\n\twant %v", call, nl.retsigerr(isddd), tstruct)
 	} else {
-		Yyerror("too many arguments to %v", op)
+		yyerror("too many arguments to %v\n\thave %s\n\twant %v", op, nl.retsigerr(isddd), tstruct)
 	}
 	goto out
 }
 
-// type check composite
-func fielddup(n *Node, hash map[string]bool) {
-	if n.Op != ONAME {
-		Fatalf("fielddup: not ONAME")
+// sigrepr is a type's representation to the outside world,
+// in string representations of return signatures
+// e.g in error messages about wrong arguments to return.
+func (t *Type) sigrepr() string {
+	switch t {
+	default:
+		return t.String()
+
+	case Types[TIDEAL]:
+		// "untyped number" is not commonly used
+		// outside of the compiler, so let's use "number".
+		return "number"
+
+	case idealstring:
+		return "string"
+
+	case idealbool:
+		return "bool"
+	}
+}
+
+// retsigerr returns the signature of the types
+// at the respective return call site of a function.
+func (nl Nodes) retsigerr(isddd bool) string {
+	if nl.Len() < 1 {
+		return "()"
+	}
+
+	var typeStrings []string
+	if nl.Len() == 1 && nl.First().Type != nil && nl.First().Type.IsFuncArgStruct() {
+		for _, f := range nl.First().Type.Fields().Slice() {
+			typeStrings = append(typeStrings, f.Type.sigrepr())
+		}
+	} else {
+		for _, n := range nl.Slice() {
+			typeStrings = append(typeStrings, n.Type.sigrepr())
+		}
 	}
-	name := n.Sym.Name
+
+	ddd := ""
+	if isddd {
+		ddd = "..."
+	}
+	return fmt.Sprintf("(%s%s)", strings.Join(typeStrings, ", "), ddd)
+}
+
+// type check composite
+func fielddup(name string, hash map[string]bool) {
 	if hash[name] {
-		Yyerror("duplicate field name in struct literal: %s", name)
+		yyerror("duplicate field name in struct literal: %s", name)
 		return
 	}
 	hash[name] = true
@@ -2753,7 +2812,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 !eqtype(a.Type, n.Type) {
 			continue
 		}
 		cmp.Right = a
@@ -2763,7 +2822,7 @@ func keydup(n *Node, hash map[uint32][]*Node) {
 			continue
 		}
 		if cmp.Val().U.(bool) {
-			Yyerror("duplicate key %v in map literal", n)
+			yyerror("duplicate key %v in map literal", n)
 			return
 		}
 	}
@@ -2771,19 +2830,6 @@ func keydup(n *Node, hash map[uint32][]*Node) {
 	hash[h] = append(hash[h], orign)
 }
 
-func indexdup(n *Node, hash map[int64]*Node) {
-	if n.Op != OLITERAL {
-		Fatalf("indexdup: not OLITERAL")
-	}
-
-	v := n.Int64()
-	if hash[v] != nil {
-		Yyerror("duplicate index in array literal: %d", v)
-		return
-	}
-	hash[v] = n
-}
-
 // iscomptype reports whether type t is a composite literal type
 // or a pointer to one.
 func iscomptype(t *Type) bool {
@@ -2810,17 +2856,12 @@ func pushtype(n *Node, t *Type) {
 		n.Right.Implicit = 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 && eqtype(n.Right.Type, t) {
 			fmt.Printf("%v: redundant type: %v\n", n.Line(), t)
 		}
 	}
 }
 
-// Marker type so esc, fmt, and sinit can recognize the LHS of an OKEY node
-// in a struct literal.
-// TODO(mdempsky): Find a nicer solution.
-var structkey = typ(Txxx)
-
 // The result of typecheckcomplit MUST be assigned back to n, e.g.
 // 	n.Left = typecheckcomplit(n.Left)
 func typecheckcomplit(n *Node) *Node {
@@ -2833,13 +2874,13 @@ func typecheckcomplit(n *Node) *Node {
 		if n.List.Len() != 0 {
 			setlineno(n.List.First())
 		}
-		Yyerror("missing type in composite literal")
+		yyerror("missing type in composite literal")
 		n.Type = nil
 		return n
 	}
 
 	// Save original node (including n->right)
-	norig := Nod(n.Op, nil, nil)
+	norig := nod(n.Op, nil, nil)
 
 	*norig = *n
 
@@ -2858,14 +2899,14 @@ func typecheckcomplit(n *Node) *Node {
 		// For better or worse, we don't allow pointers as the composite literal type,
 		// except when using the &T syntax, which sets implicit on the OIND.
 		if !n.Right.Implicit {
-			Yyerror("invalid pointer type %v for composite literal (use &%v instead)", t, t.Elem())
+			yyerror("invalid pointer type %v for composite literal (use &%v instead)", t, t.Elem())
 			n.Type = nil
 			return n
 		}
 
 		// Also, the underlying type must be a struct, map, slice, or array.
 		if !iscomptype(t) {
-			Yyerror("invalid pointer type %v for composite literal", t)
+			yyerror("invalid pointer type %v for composite literal", t)
 			n.Type = nil
 			return n
 		}
@@ -2873,84 +2914,86 @@ func typecheckcomplit(n *Node) *Node {
 		t = t.Elem()
 	}
 
-	var r *Node
 	switch t.Etype {
 	default:
-		Yyerror("invalid type for composite literal: %v", t)
+		yyerror("invalid type for composite literal: %v", t)
 		n.Type = nil
 
 	case TARRAY, TSLICE:
-		// Only allocate hash if there are some key/value pairs.
-		var hash map[int64]*Node
+		// If there are key/value pairs, create a map to keep seen
+		// keys so we can check for duplicate indices.
+		var indices map[int64]bool
 		for _, n1 := range n.List.Slice() {
 			if n1.Op == OKEY {
-				hash = make(map[int64]*Node)
+				indices = make(map[int64]bool)
 				break
 			}
 		}
-		length := int64(0)
-		i := 0
+
+		var length, i int64
 		checkBounds := t.IsArray() && !t.isDDDArray()
-		for i2, n2 := range n.List.Slice() {
-			l := n2
+		nl := n.List.Slice()
+		for i2, l := range nl {
 			setlineno(l)
-			if l.Op != OKEY {
-				l = Nod(OKEY, Nodintconst(int64(i)), l)
-				l.Left.Type = Types[TINT]
-				l.Left.Typecheck = 1
-				n.List.SetIndex(i2, l)
+			vp := &nl[i2]
+			if l.Op == OKEY {
+				l.Left = typecheck(l.Left, Erv)
+				evconst(l.Left)
+				i = nonnegintconst(l.Left)
+				if i < 0 && !l.Left.Diag {
+					yyerror("index must be non-negative integer constant")
+					l.Left.Diag = true
+					i = -(1 << 30) // stay negative for a while
+				}
+				vp = &l.Right
 			}
 
-			l.Left = typecheck(l.Left, Erv)
-			evconst(l.Left)
-			i = nonnegconst(l.Left)
-			if i < 0 && l.Left.Diag == 0 {
-				Yyerror("index must be non-negative integer constant")
-				l.Left.Diag = 1
-				i = -(1 << 30) // stay negative for a while
+			if i >= 0 && indices != nil {
+				if indices[i] {
+					yyerror("duplicate index in array literal: %d", i)
+				} else {
+					indices[i] = true
+				}
 			}
 
-			if i >= 0 && hash != nil {
-				indexdup(l.Left, hash)
-			}
+			r := *vp
+			pushtype(r, t.Elem())
+			r = typecheck(r, Erv)
+			r = defaultlit(r, t.Elem())
+			*vp = assignconv(r, t.Elem(), "array or slice literal")
+
 			i++
-			if int64(i) > length {
-				length = int64(i)
+			if i > length {
+				length = i
 				if checkBounds && length > t.NumElem() {
 					setlineno(l)
-					Yyerror("array index %d out of bounds [0:%d]", length-1, t.NumElem())
+					yyerror("array index %d out of bounds [0:%d]", length-1, t.NumElem())
 					checkBounds = false
 				}
 			}
-
-			r = l.Right
-			pushtype(r, t.Elem())
-			r = typecheck(r, Erv)
-			r = defaultlit(r, t.Elem())
-			l.Right = assignconv(r, t.Elem(), "array or slice literal")
 		}
 
 		if t.isDDDArray() {
 			t.SetNumElem(length)
 		}
 		if t.IsSlice() {
-			n.Right = Nodintconst(length)
+			n.Right = nodintconst(length)
+			n.Op = OSLICELIT
+		} else {
+			n.Op = OARRAYLIT
 		}
-		n.Op = OARRAYLIT
 
 	case TMAP:
 		hash := make(map[uint32][]*Node)
-		var l *Node
-		for i3, n3 := range n.List.Slice() {
-			l = n3
+		for i3, l := range n.List.Slice() {
 			setlineno(l)
 			if l.Op != OKEY {
 				n.List.SetIndex(i3, typecheck(n.List.Index(i3), Erv))
-				Yyerror("missing key in map literal")
+				yyerror("missing key in map literal")
 				continue
 			}
 
-			r = l.Left
+			r := l.Left
 			pushtype(r, t.Key())
 			r = typecheck(r, Erv)
 			r = defaultlit(r, t.Key())
@@ -2975,9 +3018,8 @@ func typecheckcomplit(n *Node) *Node {
 		bad := 0
 		if n.List.Len() != 0 && nokeys(n.List) {
 			// simple list of variables
-			f, it := IterFields(t)
+			f, it := iterFields(t)
 
-			var s *Sym
 			ls := n.List.Slice()
 			for i1, n1 := range ls {
 				setlineno(n1)
@@ -2985,28 +3027,26 @@ func typecheckcomplit(n *Node) *Node {
 				n1 = ls[i1]
 				if f == nil {
 					if bad == 0 {
-						Yyerror("too many values in struct initializer")
+						yyerror("too many values in struct initializer")
 					}
 					bad++
 					continue
 				}
 
-				s = f.Sym
+				s := f.Sym
 				if s != nil && !exportname(s.Name) && s.Pkg != localpkg {
-					Yyerror("implicit assignment of unexported field '%s' in %v literal", s.Name, t)
+					yyerror("implicit assignment of unexported field '%s' in %v literal", s.Name, t)
 				}
 				// No pushtype allowed here. Must name fields for that.
 				n1 = assignconv(n1, f.Type, "field value")
-				n1 = Nod(OKEY, newname(f.Sym), n1)
-				n1.Left.Type = structkey
-				n1.Left.Xoffset = f.Offset
-				n1.Left.Typecheck = 1
+				n1 = nodSym(OSTRUCTKEY, n1, f.Sym)
+				n1.Xoffset = f.Offset
 				ls[i1] = n1
 				f = it.Next()
 			}
 
 			if f != nil {
-				Yyerror("too few values in struct initializer")
+				yyerror("too few values in struct initializer")
 			}
 		} else {
 			hash := make(map[string]bool)
@@ -3015,55 +3055,57 @@ func typecheckcomplit(n *Node) *Node {
 			ls := n.List.Slice()
 			for i, l := range ls {
 				setlineno(l)
-				if l.Op != OKEY {
-					if bad == 0 {
-						Yyerror("mixture of field:value and value initializers")
-					}
-					bad++
-					ls[i] = typecheck(ls[i], Erv)
-					continue
-				}
 
-				s := l.Left.Sym
+				if l.Op == OKEY {
+					key := l.Left
+
+					l.Op = OSTRUCTKEY
+					l.Left = l.Right
+					l.Right = nil
+
+					// An OXDOT uses the Sym field to hold
+					// the field to the right of the dot,
+					// so s will be non-nil, but an OXDOT
+					// is never a valid struct literal key.
+					if key.Sym == nil || key.Op == OXDOT {
+						yyerror("invalid field name %v in struct initializer", key)
+						l.Left = typecheck(l.Left, Erv)
+						continue
+					}
 
-				// An OXDOT uses the Sym field to hold
-				// the field to the right of the dot,
-				// so s will be non-nil, but an OXDOT
-				// is never a valid struct literal key.
-				if s == nil || l.Left.Op == OXDOT {
-					Yyerror("invalid field name %v in struct initializer", l.Left)
-					l.Right = typecheck(l.Right, Erv)
-					continue
+					// Sym might have resolved to name in other top-level
+					// package, because of import dot. Redirect to correct sym
+					// before we do the lookup.
+					s := key.Sym
+					if s.Pkg != localpkg && exportname(s.Name) {
+						s1 := lookup(s.Name)
+						if s1.Origpkg == s.Pkg {
+							s = s1
+						}
+					}
+					l.Sym = s
 				}
 
-				// Sym might have resolved to name in other top-level
-				// package, because of import dot. Redirect to correct sym
-				// before we do the lookup.
-				if s.Pkg != localpkg && exportname(s.Name) {
-					s1 := Lookup(s.Name)
-					if s1.Origpkg == s.Pkg {
-						s = s1
+				if l.Op != OSTRUCTKEY {
+					if bad == 0 {
+						yyerror("mixture of field:value and value initializers")
 					}
+					bad++
+					ls[i] = typecheck(ls[i], Erv)
+					continue
 				}
 
-				f := lookdot1(nil, s, t, t.Fields(), 0)
+				f := lookdot1(nil, l.Sym, t, t.Fields(), 0)
 				if f == nil {
-					Yyerror("unknown %v field '%v' in struct literal", t, s)
+					yyerror("unknown field '%v' in struct literal of type %v", l.Sym, t)
 					continue
 				}
-
-				l.Left = newname(s)
-				l.Left.Type = structkey
-				l.Left.Xoffset = f.Offset
-				l.Left.Typecheck = 1
-				s = f.Sym
-				fielddup(newname(s), hash)
-				r = l.Right
+				fielddup(f.Sym.Name, hash)
+				l.Xoffset = f.Offset
 
 				// No pushtype allowed here. Tried and rejected.
-				r = typecheck(r, Erv)
-
-				l.Right = assignconv(r, f.Type, "field value")
+				l.Left = typecheck(l.Left, Erv)
+				l.Left = assignconv(l.Left, f.Type, "field value")
 			}
 		}
 
@@ -3071,13 +3113,12 @@ func typecheckcomplit(n *Node) *Node {
 	}
 
 	if nerr != nerrors {
-		n.Type = nil
 		return n
 	}
 
 	n.Orig = norig
 	if n.Type.IsPtr() {
-		n = Nod(OPTRLIT, n, nil)
+		n = nod(OPTRLIT, n, nil)
 		n.Typecheck = 1
 		n.Type = n.Left.Type
 		n.Left.Type = t
@@ -3117,7 +3158,7 @@ func islvalue(n *Node) bool {
 
 func checklvalue(n *Node, verb string) {
 	if !islvalue(n) {
-		Yyerror("cannot %s %v", verb, n)
+		yyerror("cannot %s %v", verb, n)
 	}
 }
 
@@ -3153,11 +3194,11 @@ func checkassign(stmt *Node, n *Node) {
 	}
 
 	if n.Op == ODOT && n.Left.Op == OINDEXMAP {
-		Yyerror("cannot assign to struct field %v in map", n)
+		yyerror("cannot assign to struct field %v in map", n)
 		return
 	}
 
-	Yyerror("cannot assign to %v", n)
+	yyerror("cannot assign to %v", n)
 }
 
 func checkassignlist(stmt *Node, l Nodes) {
@@ -3169,7 +3210,7 @@ func checkassignlist(stmt *Node, l Nodes) {
 // Check whether l and r are the same side effect-free expression,
 // so that it is safe to reuse one instead of computing both.
 func samesafeexpr(l *Node, r *Node) bool {
-	if l.Op != r.Op || !Eqtype(l.Type, r.Type) {
+	if l.Op != r.Op || !eqtype(l.Type, r.Type) {
 		return false
 	}
 
@@ -3180,11 +3221,19 @@ func samesafeexpr(l *Node, r *Node) bool {
 	case ODOT, ODOTPTR:
 		return l.Sym != nil && r.Sym != nil && l.Sym == r.Sym && samesafeexpr(l.Left, r.Left)
 
-	case OIND:
+	case OIND, OCONVNOP:
 		return samesafeexpr(l.Left, r.Left)
 
+	case OCONV:
+		// Some conversions can't be reused, such as []byte(str).
+		// Allow only numeric-ish types. This is a bit conservative.
+		return issimple[l.Type.Etype] && samesafeexpr(l.Left, r.Left)
+
 	case OINDEX:
 		return samesafeexpr(l.Left, r.Left) && samesafeexpr(l.Right, r.Right)
+
+	case OLITERAL:
+		return eqval(l.Val(), r.Val())
 	}
 
 	return false
@@ -3234,7 +3283,7 @@ func checkassignto(src *Type, dst *Node) {
 	var why string
 
 	if assignop(src, dst.Type, &why) == 0 {
-		Yyerror("cannot assign %v to %v in multiple assignment%s", src, Nconv(dst, FmtLong), why)
+		yyerror("cannot assign %v to %L in multiple assignment%s", src, dst, why)
 		return
 	}
 }
@@ -3298,7 +3347,7 @@ func typecheckas2(n *Node) {
 				goto mismatch
 			}
 			n.Op = OAS2FUNC
-			t, s := IterFields(r.Type)
+			t, s := iterFields(r.Type)
 			for _, n3 := range n.List.Slice() {
 				if t.Type != nil && n3.Type != nil {
 					checkassignto(t.Type, n3)
@@ -3350,7 +3399,7 @@ func typecheckas2(n *Node) {
 	}
 
 mismatch:
-	Yyerror("assignment count mismatch: %d = %d", cl, cr)
+	yyerror("assignment count mismatch: %d = %d", cl, cr)
 
 	// second half of dance
 out:
@@ -3365,6 +3414,12 @@ out:
 
 // type check function definition
 func typecheckfunc(n *Node) {
+	for _, ln := range n.Func.Dcl {
+		if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
+			ln.Name.Decldepth = 1
+		}
+	}
+
 	n.Func.Nname = typecheck(n.Func.Nname, Erv|Easgn)
 	t := n.Func.Nname.Type
 	if t == nil {
@@ -3374,13 +3429,7 @@ func typecheckfunc(n *Node) {
 	t.SetNname(n.Func.Nname)
 	rcvr := t.Recv()
 	if rcvr != nil && n.Func.Shortname != nil {
-		addmethod(n.Func.Shortname.Sym, t, nil, true, n.Func.Pragma&Nointerface != 0)
-	}
-
-	for _, ln := range n.Func.Dcl {
-		if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
-			ln.Name.Decldepth = 1
-		}
+		addmethod(n.Func.Shortname.Sym, t, true, n.Func.Pragma&Nointerface != 0)
 	}
 }
 
@@ -3396,18 +3445,18 @@ func stringtoarraylit(n *Node) *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]))))
+			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))))
+			l = append(l, nod(OKEY, nodintconst(int64(i)), nodintconst(int64(r))))
 			i++
 		}
 	}
 
-	nn := Nod(OCOMPLIT, nil, typenod(n.Type))
+	nn := nod(OCOMPLIT, nil, typenod(n.Type))
 	nn.List.Set(l)
 	nn = typecheck(nn, Erv)
 	return nn
@@ -3424,7 +3473,7 @@ func domethod(n *Node) {
 		// type check failed; leave empty func
 		// TODO(mdempsky): Fix Type rekinding.
 		n.Type.Etype = TFUNC
-		n.Type.Nod = nil
+		n.Type.nod = nil
 		return
 	}
 
@@ -3444,7 +3493,7 @@ func domethod(n *Node) {
 
 	// TODO(mdempsky): Fix Type rekinding.
 	*n.Type = *nt.Type
-	n.Type.Nod = nil
+	n.Type.nod = nil
 	checkwidth(n.Type)
 }
 
@@ -3466,6 +3515,9 @@ func copytype(n *Node, t *Type) {
 	embedlineno := n.Type.ForwardType().Embedlineno
 	l := n.Type.ForwardType().Copyto
 
+	ptrTo := n.Type.ptrTo
+	sliceOf := n.Type.sliceOf
+
 	// TODO(mdempsky): Fix Type rekinding.
 	*n.Type = *t
 
@@ -3477,9 +3529,15 @@ func copytype(n *Node, t *Type) {
 	}
 	t.methods = Fields{}
 	t.allMethods = Fields{}
-	t.Nod = nil
-	t.Printed = false
+	t.nod = nil
 	t.Deferwidth = false
+	t.ptrTo = ptrTo
+	t.sliceOf = sliceOf
+
+	// Propagate go:notinheap pragma from the Name to the Type.
+	if n.Name != nil && n.Name.Param != nil && n.Name.Param.Pragma&NotInHeap != 0 {
+		t.NotInHeap = true
+	}
 
 	// Update nodes waiting on this type.
 	for _, n := range l {
@@ -3492,7 +3550,7 @@ func copytype(n *Node, t *Type) {
 	if embedlineno != 0 {
 		lineno = embedlineno
 		if t.IsPtr() || t.IsUnsafePtr() {
-			Yyerror("embedded type cannot be a pointer")
+			yyerror("embedded type cannot be a pointer")
 		}
 	}
 
@@ -3508,13 +3566,13 @@ func typecheckdeftype(n *Node) {
 	n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, Etype)
 	t := n.Name.Param.Ntype.Type
 	if t == nil {
-		n.Diag = 1
+		n.Diag = true
 		n.Type = nil
 		goto ret
 	}
 
 	if n.Type == nil {
-		n.Diag = 1
+		n.Diag = true
 		goto ret
 	}
 
@@ -3544,7 +3602,7 @@ ret:
 		for _, e := range mapqueue {
 			lineno = e.lno
 			if !e.n.Type.IsComparable() {
-				Yyerror("invalid map key type %v", e.n.Type)
+				yyerror("invalid map key type %v", e.n.Type)
 			}
 		}
 		mapqueue = nil
@@ -3568,15 +3626,15 @@ func typecheckdef(n *Node) *Node {
 	setlineno(n)
 
 	if n.Op == ONONAME {
-		if n.Diag == 0 {
-			n.Diag = 1
+		if !n.Diag {
+			n.Diag = true
 			if n.Lineno != 0 {
 				lineno = n.Lineno
 			}
 
 			// Note: adderrorname looks for this string and
 			// adds context about the outer expression
-			Yyerror("undefined: %v", n.Sym)
+			yyerror("undefined: %v", n.Sym)
 		}
 
 		return n
@@ -3588,7 +3646,7 @@ func typecheckdef(n *Node) *Node {
 
 	typecheckdefstack = append(typecheckdefstack, n)
 	if n.Walkdef == 2 {
-		Flusherrors()
+		flusherrors()
 		fmt.Printf("typecheckdef loop:")
 		for i := len(typecheckdefstack) - 1; i >= 0; i-- {
 			n := typecheckdefstack[i]
@@ -3608,9 +3666,8 @@ func typecheckdef(n *Node) *Node {
 	default:
 		Fatalf("typecheckdef %v", n.Op)
 
-		// not really syms
-	case OGOTO, OLABEL:
-		break
+	case OGOTO, OLABEL, OPACK:
+		// nothing to do here
 
 	case OLITERAL:
 		if n.Name.Param.Ntype != nil {
@@ -3618,7 +3675,7 @@ func typecheckdef(n *Node) *Node {
 			n.Type = n.Name.Param.Ntype.Type
 			n.Name.Param.Ntype = nil
 			if n.Type == nil {
-				n.Diag = 1
+				n.Diag = true
 				goto ret
 			}
 		}
@@ -3628,19 +3685,19 @@ func typecheckdef(n *Node) *Node {
 		if e == nil {
 			lineno = n.Lineno
 			Dump("typecheckdef nil defn", n)
-			Yyerror("xxx")
+			yyerror("xxx")
 		}
 
 		e = typecheck(e, Erv)
 		if Isconst(e, CTNIL) {
-			Yyerror("const initializer cannot be nil")
+			yyerror("const initializer cannot be nil")
 			goto ret
 		}
 
 		if e.Type != nil && e.Op != OLITERAL || !isgoconst(e) {
-			if e.Diag == 0 {
-				Yyerror("const initializer %v is not a constant", e)
-				e.Diag = 1
+			if !e.Diag {
+				yyerror("const initializer %v is not a constant", e)
+				e.Diag = true
 			}
 
 			goto ret
@@ -3649,12 +3706,12 @@ func typecheckdef(n *Node) *Node {
 		t := n.Type
 		if t != nil {
 			if !okforconst[t.Etype] {
-				Yyerror("invalid constant type %v", t)
+				yyerror("invalid constant type %v", t)
 				goto ret
 			}
 
-			if !e.Type.IsUntyped() && !Eqtype(t, e.Type) {
-				Yyerror("cannot use %v as type %v in const initializer", Nconv(e, FmtLong), t)
+			if !e.Type.IsUntyped() && !eqtype(t, e.Type) {
+				yyerror("cannot use %L as type %v in const initializer", e, t)
 				goto ret
 			}
 
@@ -3669,7 +3726,7 @@ func typecheckdef(n *Node) *Node {
 			n.Name.Param.Ntype = typecheck(n.Name.Param.Ntype, Etype)
 			n.Type = n.Name.Param.Ntype.Type
 			if n.Type == nil {
-				n.Diag = 1
+				n.Diag = true
 				goto ret
 			}
 		}
@@ -3718,10 +3775,6 @@ func typecheckdef(n *Node) *Node {
 		if Curfn != nil {
 			resumecheckwidth()
 		}
-
-		// nothing to see here
-	case OPACK:
-		break
 	}
 
 ret:
@@ -3741,37 +3794,27 @@ ret:
 }
 
 func checkmake(t *Type, arg string, n *Node) bool {
-	if n.Op == OLITERAL {
-		switch n.Val().Ctype() {
-		case CTINT, CTRUNE, CTFLT, CTCPLX:
-			n.SetVal(toint(n.Val()))
-			if n.Val().U.(*Mpint).CmpInt64(0) < 0 {
-				Yyerror("negative %s argument in make(%v)", arg, t)
-				return false
-			}
-
-			if n.Val().U.(*Mpint).Cmp(Maxintval[TINT]) > 0 {
-				Yyerror("%s argument too large in make(%v)", arg, t)
-				return false
-			}
-
-			// Delay defaultlit until after we've checked range, to avoid
-			// a redundant "constant NNN overflows int" error.
-			n = defaultlit(n, Types[TINT])
-
-			return true
-
-		default:
-			break
-		}
-	}
-
 	if !n.Type.IsInteger() && n.Type.Etype != TIDEAL {
-		Yyerror("non-integer %s argument in make(%v) - %v", arg, t, n.Type)
+		yyerror("non-integer %s argument in make(%v) - %v", arg, t, n.Type)
 		return false
 	}
 
-	// Defaultlit still necessary for non-constant: n might be 1<<k.
+	// Do range checks for constants before defaultlit
+	// to avoid redundant "constant NNN overflows int" errors.
+	switch consttype(n) {
+	case CTINT, CTRUNE, CTFLT, CTCPLX:
+		n.SetVal(toint(n.Val()))
+		if n.Val().U.(*Mpint).CmpInt64(0) < 0 {
+			yyerror("negative %s argument in make(%v)", arg, t)
+			return false
+		}
+		if n.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
+			yyerror("%s argument too large in make(%v)", arg, t)
+			return false
+		}
+	}
+
+	// defaultlit is necessary for non-constants too: n might be 1.1<<k.
 	n = defaultlit(n, Types[TINT])
 
 	return true
@@ -3791,7 +3834,7 @@ func markbreak(n *Node, implicit *Node) {
 		} else {
 			lab := n.Left.Sym.Label
 			if lab != nil {
-				lab.Def.SetHasBreak(true)
+				lab.SetHasBreak(true)
 			}
 		}
 
@@ -3816,12 +3859,13 @@ func markbreaklist(l Nodes, implicit *Node) {
 	s := l.Slice()
 	for i := 0; i < len(s); i++ {
 		n := s[i]
+		if n == nil {
+			continue
+		}
 		if n.Op == OLABEL && i+1 < len(s) && n.Name.Defn == s[i+1] {
 			switch n.Name.Defn.Op {
 			case OFOR, OSWITCH, OTYPESW, OSELECT, ORANGE:
-				lab := new(Label)
-				lab.Def = n.Name.Defn
-				n.Left.Sym.Label = lab
+				n.Left.Sym.Label = n.Name.Defn
 				markbreak(n.Name.Defn, n.Name.Defn)
 				n.Left.Sym.Label = nil
 				i++
diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go
index 270d4c3..30c9c37 100644
--- a/src/cmd/compile/internal/gc/universe.go
+++ b/src/cmd/compile/internal/gc/universe.go
@@ -27,7 +27,6 @@ var basicTypes = [...]struct {
 	{"complex128", TCOMPLEX128},
 	{"bool", TBOOL},
 	{"string", TSTRING},
-	{"any", TANY},
 }
 
 var typedefs = [...]struct {
@@ -63,6 +62,15 @@ var builtinFuncs = [...]struct {
 	{"recover", ORECOVER},
 }
 
+var unsafeFuncs = [...]struct {
+	name string
+	op   Op
+}{
+	{"Alignof", OALIGNOF},
+	{"Offsetof", OOFFSETOF},
+	{"Sizeof", OSIZEOF},
+}
+
 // initUniverse initializes the universe block.
 func initUniverse() {
 	lexinit()
@@ -94,29 +102,37 @@ func lexinit() {
 	for _, s := range builtinFuncs {
 		// TODO(marvin): Fix Node.EType type union.
 		s2 := Pkglookup(s.name, builtinpkg)
-		s2.Def = Nod(ONAME, nil, nil)
+		s2.Def = nod(ONAME, nil, nil)
+		s2.Def.Sym = s2
+		s2.Def.Etype = EType(s.op)
+	}
+
+	for _, s := range unsafeFuncs {
+		s2 := Pkglookup(s.name, unsafepkg)
+		s2.Def = nod(ONAME, nil, nil)
 		s2.Def.Sym = s2
 		s2.Def.Etype = EType(s.op)
 	}
 
 	idealstring = typ(TSTRING)
 	idealbool = typ(TBOOL)
+	Types[TANY] = typ(TANY)
 
 	s := Pkglookup("true", builtinpkg)
-	s.Def = Nodbool(true)
-	s.Def.Sym = Lookup("true")
+	s.Def = nodbool(true)
+	s.Def.Sym = lookup("true")
 	s.Def.Name = new(Name)
 	s.Def.Type = idealbool
 
 	s = Pkglookup("false", builtinpkg)
-	s.Def = Nodbool(false)
-	s.Def.Sym = Lookup("false")
+	s.Def = nodbool(false)
+	s.Def.Sym = lookup("false")
 	s.Def.Name = new(Name)
 	s.Def.Type = idealbool
 
-	s = Lookup("_")
+	s = lookup("_")
 	s.Block = -100
-	s.Def = Nod(ONAME, nil, nil)
+	s.Def = nod(ONAME, nil, nil)
 	s.Def.Sym = s
 	Types[TBLANK] = typ(TBLANK)
 	s.Def.Type = Types[TBLANK]
@@ -124,7 +140,7 @@ func lexinit() {
 
 	s = Pkglookup("_", builtinpkg)
 	s.Block = -100
-	s.Def = Nod(ONAME, nil, nil)
+	s.Def = nod(ONAME, nil, nil)
 	s.Def.Sym = s
 	Types[TBLANK] = typ(TBLANK)
 	s.Def.Type = Types[TBLANK]
@@ -138,7 +154,7 @@ func lexinit() {
 	s.Def.Name = new(Name)
 
 	s = Pkglookup("iota", builtinpkg)
-	s.Def = Nod(OIOTA, nil, nil)
+	s.Def = nod(OIOTA, nil, nil)
 	s.Def.Sym = s
 	s.Def.Name = new(Name)
 }
@@ -149,7 +165,7 @@ func typeinit() {
 	}
 
 	for et := EType(0); et < NTYPE; et++ {
-		Simtype[et] = et
+		simtype[et] = et
 	}
 
 	Types[TPTR32] = typ(TPTR32)
@@ -163,7 +179,6 @@ func typeinit() {
 	t.Sym = Pkglookup("Pointer", unsafepkg)
 	t.Sym.Def = typenod(t)
 	t.Sym.Def.Name = new(Name)
-
 	dowidth(Types[TUNSAFEPTR])
 
 	Tptr = TPTR32
@@ -172,23 +187,23 @@ func typeinit() {
 	}
 
 	for et := TINT8; et <= TUINT64; et++ {
-		Isint[et] = true
+		isInt[et] = true
 	}
-	Isint[TINT] = true
-	Isint[TUINT] = true
-	Isint[TUINTPTR] = true
+	isInt[TINT] = true
+	isInt[TUINT] = true
+	isInt[TUINTPTR] = true
 
-	Isfloat[TFLOAT32] = true
-	Isfloat[TFLOAT64] = true
+	isFloat[TFLOAT32] = true
+	isFloat[TFLOAT64] = true
 
-	Iscomplex[TCOMPLEX64] = true
-	Iscomplex[TCOMPLEX128] = true
+	isComplex[TCOMPLEX64] = true
+	isComplex[TCOMPLEX128] = true
 
 	isforw[TFORW] = true
 
 	// initialize okfor
 	for et := EType(0); et < NTYPE; et++ {
-		if Isint[et] || et == TIDEAL {
+		if isInt[et] || et == TIDEAL {
 			okforeq[et] = true
 			okforcmp[et] = true
 			okforarith[et] = true
@@ -196,11 +211,11 @@ func typeinit() {
 			okforand[et] = true
 			okforconst[et] = true
 			issimple[et] = true
-			Minintval[et] = new(Mpint)
-			Maxintval[et] = new(Mpint)
+			minintval[et] = new(Mpint)
+			maxintval[et] = new(Mpint)
 		}
 
-		if Isfloat[et] {
+		if isFloat[et] {
 			okforeq[et] = true
 			okforcmp[et] = true
 			okforadd[et] = true
@@ -211,7 +226,7 @@ func typeinit() {
 			maxfltval[et] = newMpflt()
 		}
 
-		if Iscomplex[et] {
+		if isComplex[et] {
 			okforeq[et] = true
 			okforadd[et] = true
 			okforarith[et] = true
@@ -303,19 +318,19 @@ func typeinit() {
 	iscmp[OEQ] = true
 	iscmp[ONE] = true
 
-	Maxintval[TINT8].SetString("0x7f")
-	Minintval[TINT8].SetString("-0x80")
-	Maxintval[TINT16].SetString("0x7fff")
-	Minintval[TINT16].SetString("-0x8000")
-	Maxintval[TINT32].SetString("0x7fffffff")
-	Minintval[TINT32].SetString("-0x80000000")
-	Maxintval[TINT64].SetString("0x7fffffffffffffff")
-	Minintval[TINT64].SetString("-0x8000000000000000")
+	maxintval[TINT8].SetString("0x7f")
+	minintval[TINT8].SetString("-0x80")
+	maxintval[TINT16].SetString("0x7fff")
+	minintval[TINT16].SetString("-0x8000")
+	maxintval[TINT32].SetString("0x7fffffff")
+	minintval[TINT32].SetString("-0x80000000")
+	maxintval[TINT64].SetString("0x7fffffffffffffff")
+	minintval[TINT64].SetString("-0x8000000000000000")
 
-	Maxintval[TUINT8].SetString("0xff")
-	Maxintval[TUINT16].SetString("0xffff")
-	Maxintval[TUINT32].SetString("0xffffffff")
-	Maxintval[TUINT64].SetString("0xffffffffffffffff")
+	maxintval[TUINT8].SetString("0xff")
+	maxintval[TUINT16].SetString("0xffff")
+	maxintval[TUINT32].SetString("0xffffffff")
+	maxintval[TUINT64].SetString("0xffffffffffffffff")
 
 	// f is valid float if min < f < max.  (min and max are not themselves valid.)
 	maxfltval[TFLOAT32].SetString("33554431p103") // 2^24-1 p (127-23) + 1/2 ulp
@@ -338,19 +353,19 @@ func typeinit() {
 	Types[TINTER] = typ(TINTER)
 
 	// simple aliases
-	Simtype[TMAP] = Tptr
+	simtype[TMAP] = Tptr
 
-	Simtype[TCHAN] = Tptr
-	Simtype[TFUNC] = Tptr
-	Simtype[TUNSAFEPTR] = 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(Widthint)))
-	Array_cap = int(Rnd(int64(Array_nel)+int64(Widthint), int64(Widthint)))
-	sizeof_Array = int(Rnd(int64(Array_cap)+int64(Widthint), int64(Widthptr)))
+	array_array = int(Rnd(0, int64(Widthptr)))
+	array_nel = int(Rnd(int64(array_array)+int64(Widthptr), int64(Widthint)))
+	array_cap = int(Rnd(int64(array_nel)+int64(Widthint), int64(Widthint)))
+	sizeof_Array = int(Rnd(int64(array_cap)+int64(Widthint), int64(Widthptr)))
 
 	// string is same as slice wo the cap
-	sizeof_String = int(Rnd(int64(Array_nel)+int64(Widthint), int64(Widthptr)))
+	sizeof_String = int(Rnd(int64(array_nel)+int64(Widthint), int64(Widthptr)))
 
 	dowidth(Types[TSTRING])
 	dowidth(idealstring)
@@ -359,32 +374,16 @@ func typeinit() {
 }
 
 func makeErrorInterface() *Type {
-	rcvr := typ(TSTRUCT)
-	rcvr.StructType().Funarg = FunargRcvr
 	field := newField()
-	field.Type = Ptrto(typ(TSTRUCT))
-	rcvr.SetFields([]*Field{field})
-
-	in := typ(TSTRUCT)
-	in.StructType().Funarg = FunargParams
-
-	out := typ(TSTRUCT)
-	out.StructType().Funarg = FunargResults
-	field = newField()
 	field.Type = Types[TSTRING]
-	out.SetFields([]*Field{field})
-
-	f := typ(TFUNC)
-	*f.RecvsP() = rcvr
-	*f.ResultsP() = out
-	*f.ParamsP() = in
+	f := functypefield(fakethisfield(), nil, []*Field{field})
 
-	t := typ(TINTER)
 	field = newField()
-	field.Sym = Lookup("Error")
+	field.Sym = lookup("Error")
 	field.Type = f
-	t.SetFields([]*Field{field})
 
+	t := typ(TINTER)
+	t.SetFields([]*Field{field})
 	return t
 }
 
@@ -422,11 +421,11 @@ func lexinit1() {
 			sameas = s.sameas64
 		}
 
-		Simtype[s.etype] = sameas
+		simtype[s.etype] = sameas
 		minfltval[s.etype] = minfltval[sameas]
 		maxfltval[s.etype] = maxfltval[sameas]
-		Minintval[s.etype] = Minintval[sameas]
-		Maxintval[s.etype] = Maxintval[sameas]
+		minintval[s.etype] = minintval[sameas]
+		maxintval[s.etype] = maxintval[sameas]
 
 		t := typ(s.etype)
 		t.Sym = s1
@@ -446,10 +445,10 @@ func finishUniverse() {
 	// package block rather than emitting a redeclared symbol error.
 
 	for _, s := range builtinpkg.Syms {
-		if s.Def == nil || (s.Name == "any" && Debug['A'] == 0) {
+		if s.Def == nil {
 			continue
 		}
-		s1 := Lookup(s.Name)
+		s1 := lookup(s.Name)
 		if s1.Def != nil {
 			continue
 		}
@@ -458,9 +457,9 @@ func finishUniverse() {
 		s1.Block = s.Block
 	}
 
-	nodfp = Nod(ONAME, nil, nil)
+	nodfp = nod(ONAME, nil, nil)
 	nodfp.Type = Types[TINT32]
 	nodfp.Xoffset = 0
 	nodfp.Class = PPARAM
-	nodfp.Sym = Lookup(".fp")
+	nodfp.Sym = lookup(".fp")
 }
diff --git a/src/cmd/compile/internal/gc/unsafe.go b/src/cmd/compile/internal/gc/unsafe.go
index fc6ed1f..0ae97b4 100644
--- a/src/cmd/compile/internal/gc/unsafe.go
+++ b/src/cmd/compile/internal/gc/unsafe.go
@@ -4,126 +4,71 @@
 
 package gc
 
-// unsafenmagic rewrites calls to package unsafe's functions into constants.
-func unsafenmagic(nn *Node) *Node {
-	fn := nn.Left
-	args := nn.List
-
-	if safemode || fn == nil || fn.Op != ONAME {
-		return nil
-	}
-	s := fn.Sym
-	if s == nil {
-		return nil
-	}
-	if s.Pkg != unsafepkg {
-		return nil
-	}
-
-	if args.Len() == 0 {
-		Yyerror("missing argument for %v", s)
-		return nil
-	}
-
-	r := args.First()
-
-	var v int64
-	switch s.Name {
-	case "Alignof", "Sizeof":
-		r = typecheck(r, Erv)
-		r = defaultlit(r, nil)
-		tr := r.Type
+// evalunsafe evaluates a package unsafe operation and returns the result.
+func evalunsafe(n *Node) int64 {
+	switch n.Op {
+	case OALIGNOF, OSIZEOF:
+		n.Left = typecheck(n.Left, Erv)
+		n.Left = defaultlit(n.Left, nil)
+		tr := n.Left.Type
 		if tr == nil {
-			goto bad
+			yyerror("invalid expression %v", n)
+			return 0
 		}
 		dowidth(tr)
-		if s.Name == "Alignof" {
-			v = int64(tr.Align)
-		} else {
-			v = tr.Width
+		if n.Op == OALIGNOF {
+			return int64(tr.Align)
 		}
+		return tr.Width
 
-	case "Offsetof":
+	case OOFFSETOF:
 		// must be a selector.
-		if r.Op != OXDOT {
-			goto bad
+		if n.Left.Op != OXDOT {
+			yyerror("invalid expression %v", n)
+			return 0
 		}
 
 		// Remember base of selector to find it back after dot insertion.
 		// Since r->left may be mutated by typechecking, check it explicitly
 		// first to track it correctly.
-		r.Left = typecheck(r.Left, Erv)
-		base := r.Left
+		n.Left.Left = typecheck(n.Left.Left, Erv)
+		base := n.Left.Left
 
-		r = typecheck(r, Erv)
-		switch r.Op {
+		n.Left = typecheck(n.Left, Erv)
+		switch n.Left.Op {
 		case ODOT, ODOTPTR:
 			break
 		case OCALLPART:
-			Yyerror("invalid expression %v: argument is a method value", nn)
-			goto ret
+			yyerror("invalid expression %v: argument is a method value", n)
+			return 0
 		default:
-			goto bad
+			yyerror("invalid expression %v", n)
+			return 0
 		}
 
 		// Sum offsets for dots until we reach base.
-		for r1 := r; r1 != base; r1 = r1.Left {
-			switch r1.Op {
+		var v int64
+		for r := n.Left; r != base; r = r.Left {
+			switch r.Op {
 			case ODOTPTR:
 				// For Offsetof(s.f), s may itself be a pointer,
 				// but accessing f must not otherwise involve
 				// indirection via embedded pointer types.
-				if r1.Left != base {
-					Yyerror("invalid expression %v: selector implies indirection of embedded %v", nn, r1.Left)
-					goto ret
+				if r.Left != base {
+					yyerror("invalid expression %v: selector implies indirection of embedded %v", n, r.Left)
+					return 0
 				}
 				fallthrough
 			case ODOT:
-				v += r1.Xoffset
+				v += r.Xoffset
 			default:
-				Dump("unsafenmagic", r)
-				Fatalf("impossible %#v node after dot insertion", r1.Op)
-				goto bad
+				Dump("unsafenmagic", n.Left)
+				Fatalf("impossible %#v node after dot insertion", r.Op)
 			}
 		}
-
-	default:
-		return nil
+		return v
 	}
 
-	if args.Len() > 1 {
-		Yyerror("extra arguments for %v", s)
-	}
-	goto ret
-
-bad:
-	Yyerror("invalid expression %v", nn)
-
-ret:
-	// any side effects disappear; ignore init
-	var val Val
-	val.U = new(Mpint)
-	val.U.(*Mpint).SetInt64(v)
-	n := Nod(OLITERAL, nil, nil)
-	n.Orig = nn
-	n.SetVal(val)
-	n.Type = Types[TUINTPTR]
-	nn.Type = Types[TUINTPTR]
-	return n
-}
-
-func isunsafebuiltin(n *Node) bool {
-	if n == nil || n.Op != ONAME || n.Sym == nil || n.Sym.Pkg != unsafepkg {
-		return false
-	}
-	if n.Sym.Name == "Sizeof" {
-		return true
-	}
-	if n.Sym.Name == "Offsetof" {
-		return true
-	}
-	if n.Sym.Name == "Alignof" {
-		return true
-	}
-	return false
+	Fatalf("unexpected op %v", n.Op)
+	return 0
 }
diff --git a/src/cmd/compile/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go
index 18e990a..bb5cede 100644
--- a/src/cmd/compile/internal/gc/util.go
+++ b/src/cmd/compile/internal/gc/util.go
@@ -16,7 +16,7 @@ func (n *Node) Line() string {
 
 var atExitFuncs []func()
 
-func AtExit(f func()) {
+func atExit(f func()) {
 	atExitFuncs = append(atExitFuncs, f)
 }
 
@@ -33,6 +33,8 @@ var (
 	cpuprofile     string
 	memprofile     string
 	memprofilerate int64
+	traceprofile   string
+	traceHandler   func(string)
 )
 
 func startProfile() {
@@ -44,7 +46,7 @@ func startProfile() {
 		if err := pprof.StartCPUProfile(f); err != nil {
 			Fatalf("%v", err)
 		}
-		AtExit(pprof.StopCPUProfile)
+		atExit(pprof.StopCPUProfile)
 	}
 	if memprofile != "" {
 		if memprofilerate != 0 {
@@ -54,11 +56,14 @@ func startProfile() {
 		if err != nil {
 			Fatalf("%v", err)
 		}
-		AtExit(func() {
+		atExit(func() {
 			runtime.GC() // profile all outstanding allocations
 			if err := pprof.WriteHeapProfile(f); err != nil {
 				Fatalf("%v", err)
 			}
 		})
 	}
+	if traceprofile != "" && traceHandler != nil {
+		traceHandler(traceprofile)
+	}
 }
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 66eb7e9..8248d50 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -50,11 +50,11 @@ func walk(fn *Node) {
 				continue
 			}
 			lineno = defn.Left.Lineno
-			Yyerror("%v declared and not used", ln.Sym)
+			yyerror("%v declared and not used", ln.Sym)
 			defn.Left.Used = true // suppress repeats
 		} else {
 			lineno = ln.Lineno
-			Yyerror("%v declared and not used", ln.Sym)
+			yyerror("%v declared and not used", ln.Sym)
 		}
 	}
 
@@ -119,7 +119,7 @@ func adjustargs(n *Node, adjust int) {
 	callfunc := n.Left
 	for _, arg = range callfunc.List.Slice() {
 		if arg.Op != OAS {
-			Yyerror("call arg not assignment")
+			yyerror("call arg not assignment")
 		}
 		lhs = arg.Left
 		if lhs.Op == ONAME {
@@ -128,8 +128,8 @@ func adjustargs(n *Node, adjust int) {
 			continue
 		}
 
-		if lhs.Op != OINDREG {
-			Yyerror("call argument store does not use OINDREG")
+		if lhs.Op != OINDREGSP {
+			yyerror("call argument store does not use OINDREGSP")
 		}
 
 		// can't really check this in machine-indep code.
@@ -145,7 +145,7 @@ func walkstmt(n *Node) *Node {
 	if n == nil {
 		return n
 	}
-	if n.Dodata == 2 { // don't walk, generated by anylit.
+	if n.IsStatic { // don't walk, generated by anylit.
 		return n
 	}
 
@@ -156,9 +156,9 @@ func walkstmt(n *Node) *Node {
 	switch n.Op {
 	default:
 		if n.Op == ONAME {
-			Yyerror("%v is not a top level statement", n.Sym)
+			yyerror("%v is not a top level statement", n.Sym)
 		} else {
-			Yyerror("%v is not a top level statement", n.Op)
+			yyerror("%v is not a top level statement", n.Op)
 		}
 		Dump("nottop", n)
 
@@ -184,7 +184,7 @@ func walkstmt(n *Node) *Node {
 		ORECOVER,
 		OGETG:
 		if n.Typecheck == 0 {
-			Fatalf("missing typecheck: %v", Nconv(n, FmtSign))
+			Fatalf("missing typecheck: %+v", n)
 		}
 		wascopy := n.Op == OCOPY
 		init := n.Ninit
@@ -195,11 +195,11 @@ func walkstmt(n *Node) *Node {
 			n.Op = OEMPTY // don't leave plain values as statements.
 		}
 
-		// special case for a receive where we throw away
+	// special case for a receive where we throw away
 	// the value received.
 	case ORECV:
 		if n.Typecheck == 0 {
-			Fatalf("missing typecheck: %v", Nconv(n, FmtSign))
+			Fatalf("missing typecheck: %+v", n)
 		}
 		init := n.Ninit
 		n.Ninit.Set(nil)
@@ -226,12 +226,12 @@ func walkstmt(n *Node) *Node {
 		v := n.Left
 		if v.Class == PAUTOHEAP {
 			if compiling_runtime {
-				Yyerror("%v escapes to heap, not allowed in runtime.", v)
+				yyerror("%v escapes to heap, not allowed in runtime.", v)
 			}
 			if prealloc[v] == nil {
 				prealloc[v] = callnew(v.Type)
 			}
-			nn := Nod(OAS, v.Name.Heapaddr, prealloc[v])
+			nn := nod(OAS, v.Name.Heapaddr, prealloc[v])
 			nn.Colas = true
 			nn = typecheck(nn, Etop)
 			return walkstmt(nn)
@@ -241,7 +241,7 @@ func walkstmt(n *Node) *Node {
 		walkstmtlist(n.List.Slice())
 
 	case OXCASE:
-		Yyerror("case statement out of place")
+		yyerror("case statement out of place")
 		n.Op = OCASE
 		fallthrough
 
@@ -314,7 +314,7 @@ func walkstmt(n *Node) *Node {
 				}
 				if cl == PPARAMOUT {
 					if ln.isParamStackCopy() {
-						ln = walkexpr(typecheck(Nod(OIND, ln.Name.Heapaddr, nil), Erv), nil)
+						ln = walkexpr(typecheck(nod(OIND, ln.Name.Heapaddr, nil), Erv), nil)
 					}
 					rl = append(rl, ln)
 				}
@@ -361,12 +361,12 @@ func walkstmt(n *Node) *Node {
 		walkrange(n)
 
 	case OXFALL:
-		Yyerror("fallthrough statement out of place")
+		yyerror("fallthrough statement out of place")
 		n.Op = OFALL
 	}
 
 	if n.Op == ONAME {
-		Fatalf("walkstmt ended up with name: %v", Nconv(n, FmtSign))
+		Fatalf("walkstmt ended up with name: %+v", n)
 	}
 	return n
 }
@@ -382,7 +382,7 @@ func isSmallMakeSlice(n *Node) bool {
 	}
 	t := n.Type
 
-	return Smallintconst(l) && Smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < (1<<16)/t.Elem().Width)
+	return smallintconst(l) && smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < (1<<16)/t.Elem().Width)
 }
 
 // walk the whole tree of the body of an
@@ -410,16 +410,14 @@ func walkexprlistcheap(s []*Node, init *Nodes) {
 	}
 }
 
-// Build name of function: convI2E etc.
+// Build name of function for interface conversion.
 // Not all names are possible
-// (e.g., we'll never generate convE2E or convE2I).
+// (e.g., we'll never generate convE2E or convE2I or convI2E).
 func convFuncName(from, to *Type) string {
 	tkind := to.iet()
 	switch from.iet() {
 	case 'I':
 		switch tkind {
-		case 'E':
-			return "convI2E"
 		case 'I':
 			return "convI2I"
 		}
@@ -435,38 +433,6 @@ func convFuncName(from, to *Type) string {
 	panic("unreachable")
 }
 
-// Build name of function: assertI2E etc.
-// If with2suffix is true, the form ending in "2" is returned".
-func assertFuncName(from, to *Type, with2suffix bool) string {
-	l := len("assertX2X2")
-	if !with2suffix {
-		l--
-	}
-	tkind := to.iet()
-	switch from.iet() {
-	case 'E':
-		switch tkind {
-		case 'I':
-			return "assertE2I2"[:l]
-		case 'E':
-			return "assertE2E2"[:l]
-		case 'T':
-			return "assertE2T2"[:l]
-		}
-	case 'I':
-		switch tkind {
-		case 'I':
-			return "assertI2I2"[:l]
-		case 'E':
-			return "assertI2E2"[:l]
-		case 'T':
-			return "assertI2T2"[:l]
-		}
-	}
-	Fatalf("unknown assert func %c2%c", from.iet(), to.iet())
-	panic("unreachable")
-}
-
 // The result of walkexpr MUST be assigned back to n, e.g.
 // 	n.Left = walkexpr(n.Left, init)
 func walkexpr(n *Node, init *Nodes) *Node {
@@ -486,13 +452,6 @@ func walkexpr(n *Node, init *Nodes) *Node {
 		init.AppendNodes(&n.Ninit)
 	}
 
-	// annoying case - not typechecked
-	if n.Op == OKEY {
-		n.Left = walkexpr(n.Left, init)
-		n.Right = walkexpr(n.Right, init)
-		return n
-	}
-
 	lno := setlineno(n)
 
 	if Debug['w'] > 1 {
@@ -500,24 +459,26 @@ func walkexpr(n *Node, init *Nodes) *Node {
 	}
 
 	if n.Typecheck != 1 {
-		Fatalf("missed typecheck: %v\n", Nconv(n, FmtSign))
+		Fatalf("missed typecheck: %+v", n)
 	}
 
 	if n.Op == ONAME && n.Class == PAUTOHEAP {
-		nn := Nod(OIND, n.Name.Heapaddr, nil)
+		nn := nod(OIND, n.Name.Heapaddr, nil)
 		nn = typecheck(nn, Erv)
-		return walkexpr(nn, init)
+		nn = walkexpr(nn, init)
+		nn.Left.NonNil = true
+		return nn
 	}
 
 opswitch:
 	switch n.Op {
 	default:
 		Dump("walk", n)
-		Fatalf("walkexpr: switch 1 unknown op %v", Nconv(n, FmtShort|FmtSign))
+		Fatalf("walkexpr: switch 1 unknown op %+S", n)
 
 	case OTYPE,
 		ONONAME,
-		OINDREG,
+		OINDREGSP,
 		OEMPTY,
 		OGETG:
 
@@ -553,7 +514,7 @@ opswitch:
 		n.Left = walkexpr(n.Left, init)
 		n.Right = walkexpr(n.Right, init)
 
-	case OSPTR, OITAB:
+	case OSPTR, OITAB, OIDATA:
 		n.Left = walkexpr(n.Left, init)
 
 	case OLEN, OCAP:
@@ -629,6 +590,7 @@ opswitch:
 
 		n.Right = walkexpr(n.Right, &ll)
 		n.Right = addinit(n.Right, ll.Slice())
+		n = walkinrange(n, init)
 
 	case OPRINT, OPRINTN:
 		walkexprlist(n.List.Slice(), init)
@@ -638,7 +600,7 @@ opswitch:
 		n = mkcall("gopanic", nil, init, n.Left)
 
 	case ORECOVER:
-		n = mkcall("gorecover", n.Type, init, Nod(OADDR, nodfp, nil))
+		n = mkcall("gorecover", n.Type, init, nod(OADDR, nodfp, nil))
 
 	case OLITERAL:
 		n.Addable = true
@@ -666,7 +628,7 @@ opswitch:
 			// transformclosure already did all preparation work.
 
 			// Prepend captured variables to argument list.
-			n.List.Set(append(n.Left.Func.Enter.Slice(), n.List.Slice()...))
+			n.List.Prepend(n.Left.Func.Enter.Slice()...)
 
 			n.Left.Func.Enter.Set(nil)
 
@@ -690,8 +652,9 @@ opswitch:
 		n.Left = walkexpr(n.Left, init)
 		walkexprlist(n.List.Slice(), init)
 
-		if n.Left.Op == ONAME && n.Left.Sym.Name == "Sqrt" && n.Left.Sym.Pkg.Path == "math" {
-			if Thearch.LinkArch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
+		if n.Left.Op == ONAME && n.Left.Sym.Name == "Sqrt" &&
+			(n.Left.Sym.Pkg.Path == "math" || n.Left.Sym.Pkg == localpkg && myimportpath == "math") {
+			if Thearch.LinkArch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.MIPS, sys.PPC64, sys.S390X) {
 				n.Op = OSQRT
 				n.Left = n.List.First()
 				n.List.Set(nil)
@@ -726,7 +689,8 @@ opswitch:
 			break
 		}
 
-		if n.Right == nil || iszero(n.Right) && !instrumenting {
+		if n.Right == nil {
+			// TODO(austin): Check all "implicit zeroing"
 			break
 		}
 
@@ -734,40 +698,12 @@ opswitch:
 		default:
 			n.Right = walkexpr(n.Right, init)
 
-		case ODOTTYPE:
-			// TODO(rsc): The Isfat is for consistency with componentgen and orderexpr.
-			// It needs to be removed in all three places.
-			// That would allow inlining x.(struct{*int}) the same as x.(*int).
-			if isdirectiface(n.Right.Type) && !Isfat(n.Right.Type) && !instrumenting {
-				// handled directly during cgen
-				n.Right = walkexpr(n.Right, init)
-				break
-			}
-
-			// x = i.(T); n.Left is x, n.Right.Left is i.
-			// orderstmt made sure x is addressable.
-			n.Right.Left = walkexpr(n.Right.Left, init)
-
-			n1 := Nod(OADDR, n.Left, nil)
-			r := n.Right // i.(T)
-
-			if Debug_typeassert > 0 {
-				Warn("type assertion not inlined")
-			}
-
-			fn := syslook(assertFuncName(r.Left.Type, r.Type, false))
-			fn = substArgTypes(fn, r.Left.Type, r.Type)
-
-			n = mkcall1(fn, nil, init, typename(r.Type), r.Left, n1)
-			n = walkexpr(n, init)
-			break opswitch
-
 		case ORECV:
 			// x = <-c; n.Left is x, n.Right.Left is c.
 			// orderstmt made sure x is addressable.
 			n.Right.Left = walkexpr(n.Right.Left, init)
 
-			n1 := Nod(OADDR, n.Left, nil)
+			n1 := nod(OADDR, n.Left, nil)
 			r := n.Right.Left // the channel
 			n = mkcall1(chanfn("chanrecv1", 2, r.Type), nil, init, typename(r.Type), r, n1)
 			n = walkexpr(n, init)
@@ -776,6 +712,9 @@ opswitch:
 		case OAPPEND:
 			// x = append(...)
 			r := n.Right
+			if r.Type.Elem().NotInHeap {
+				yyerror("%v is go:notinheap; heap allocation disallowed", r.Type.Elem())
+			}
 			if r.Isddd {
 				r = appendslice(r, init) // also works for append(slice, string).
 			} else {
@@ -792,9 +731,9 @@ opswitch:
 		}
 
 		if n.Left != nil && n.Right != nil {
-			dd := n.Dodata
+			static := n.IsStatic
 			n = convas(n, init)
-			n.Dodata = dd
+			n.IsStatic = static
 			n = applywritebarrier(n)
 		}
 
@@ -809,7 +748,7 @@ opswitch:
 		}
 		n = liststmt(ll)
 
-		// a,b,... = fn()
+	// a,b,... = fn()
 	case OAS2FUNC:
 		init.AppendNodes(&n.Ninit)
 
@@ -817,13 +756,19 @@ opswitch:
 		walkexprlistsafe(n.List.Slice(), init)
 		r = walkexpr(r, init)
 
-		ll := ascompatet(n.Op, n.List, r.Type, 0, init)
+		if isIntrinsicCall(r) {
+			n.Rlist.Set1(r)
+			break
+		}
+		init.Append(r)
+
+		ll := ascompatet(n.Op, n.List, r.Type)
 		for i, n := range ll {
 			ll[i] = applywritebarrier(n)
 		}
-		n = liststmt(append([]*Node{r}, ll...))
+		n = liststmt(ll)
 
-		// x, y = <-c
+	// x, y = <-c
 	// orderstmt made sure x is addressable.
 	case OAS2RECV:
 		init.AppendNodes(&n.Ninit)
@@ -835,12 +780,13 @@ opswitch:
 		if isblank(n.List.First()) {
 			n1 = nodnil()
 		} else {
-			n1 = Nod(OADDR, n.List.First(), nil)
+			n1 = nod(OADDR, n.List.First(), nil)
 		}
 		n1.Etype = 1 // addr does not escape
 		fn := chanfn("chanrecv2", 2, r.Left.Type)
-		r = mkcall1(fn, n.List.Second().Type, init, typename(r.Left.Type), r.Left, n1)
-		n = Nod(OAS, n.List.Second(), r)
+		ok := n.List.Second()
+		call := mkcall1(fn, ok.Type, init, typename(r.Left.Type), r.Left, n1)
+		n = nod(OAS, ok, call)
 		n = typecheck(n, Etop)
 
 		// a,b = m[i];
@@ -871,7 +817,7 @@ opswitch:
 		} else {
 			// standard version takes key by reference
 			// orderexpr made sure key is addressable.
-			key = Nod(OADDR, r.Right, nil)
+			key = nod(OADDR, r.Right, nil)
 
 			p = "mapaccess2"
 		}
@@ -895,21 +841,21 @@ opswitch:
 		// mapaccess2* returns a typed bool, but due to spec changes,
 		// the boolean result of i.(T) is now untyped so we make it the
 		// same type as the variable on the lhs.
-		if !isblank(n.List.Second()) {
-			r.Type.Field(1).Type = n.List.Second().Type
+		if ok := n.List.Second(); !isblank(ok) && ok.Type.IsBoolean() {
+			r.Type.Field(1).Type = ok.Type
 		}
 		n.Rlist.Set1(r)
 		n.Op = OAS2FUNC
 
 		// don't generate a = *var if a is _
 		if !isblank(a) {
-			var_ := temp(Ptrto(t.Val()))
+			var_ := temp(ptrto(t.Val()))
 			var_.Typecheck = 1
 			var_.NonNil = true // mapaccess always returns a non-nil pointer
 			n.List.SetIndex(0, var_)
 			n = walkexpr(n, init)
 			init.Append(n)
-			n = Nod(OAS, a, Nod(OIND, var_, nil))
+			n = nod(OAS, a, nod(OIND, var_, nil))
 		}
 
 		n = typecheck(n, Etop)
@@ -923,88 +869,17 @@ opswitch:
 		key = walkexpr(key, init)
 
 		// orderstmt made sure key is addressable.
-		key = Nod(OADDR, key, nil)
+		key = nod(OADDR, key, nil)
 
 		t := map_.Type
 		n = mkcall1(mapfndel("mapdelete", t), nil, init, typename(t), map_, key)
 
 	case OAS2DOTTYPE:
-		e := n.Rlist.First() // i.(T)
-		// TODO(rsc): The Isfat is for consistency with componentgen and orderexpr.
-		// It needs to be removed in all three places.
-		// That would allow inlining x.(struct{*int}) the same as x.(*int).
-		if isdirectiface(e.Type) && !Isfat(e.Type) && !instrumenting {
-			// handled directly during gen.
-			walkexprlistsafe(n.List.Slice(), init)
-			e.Left = walkexpr(e.Left, init)
-			break
-		}
-
-		// res, ok = i.(T)
-		// orderstmt made sure a is addressable.
-		init.AppendNodes(&n.Ninit)
-
 		walkexprlistsafe(n.List.Slice(), init)
+		e := n.Rlist.First() // i.(T)
 		e.Left = walkexpr(e.Left, init)
-		t := e.Type    // T
-		from := e.Left // i
-
-		oktype := Types[TBOOL]
-		ok := n.List.Second()
-		if !isblank(ok) {
-			oktype = ok.Type
-		}
-
-		fromKind := from.Type.iet()
-		toKind := t.iet()
-
-		// Avoid runtime calls in a few cases of the form _, ok := i.(T).
-		// This is faster and shorter and allows the corresponding assertX2X2
-		// routines to skip nil checks on their last argument.
-		if isblank(n.List.First()) {
-			var fast *Node
-			switch {
-			case fromKind == 'E' && toKind == 'T':
-				tab := Nod(OITAB, from, nil) // type:eface::tab:iface
-				typ := Nod(OCONVNOP, typename(t), nil)
-				typ.Type = Ptrto(Types[TUINTPTR])
-				fast = Nod(OEQ, tab, typ)
-			case fromKind == 'I' && toKind == 'E',
-				fromKind == 'E' && toKind == 'E':
-				tab := Nod(OITAB, from, nil)
-				fast = Nod(ONE, nodnil(), tab)
-			}
-			if fast != nil {
-				if Debug_typeassert > 0 {
-					Warn("type assertion (ok only) inlined")
-				}
-				n = Nod(OAS, ok, fast)
-				n = typecheck(n, Etop)
-				break
-			}
-		}
-
-		var resptr *Node // &res
-		if isblank(n.List.First()) {
-			resptr = nodnil()
-		} else {
-			resptr = Nod(OADDR, n.List.First(), nil)
-		}
-		resptr.Etype = 1 // addr does not escape
-
-		if Debug_typeassert > 0 {
-			Warn("type assertion not inlined")
-		}
-		fn := syslook(assertFuncName(from.Type, t, true))
-		fn = substArgTypes(fn, from.Type, t)
-		call := mkcall1(fn, oktype, init, typename(t), from, resptr)
-		n = Nod(OAS, ok, call)
-		n = typecheck(n, Etop)
 
 	case ODOTTYPE, ODOTTYPE2:
-		if !isdirectiface(n.Type) || Isfat(n.Type) {
-			Fatalf("walkexpr ODOTTYPE") // should see inside OAS only
-		}
 		n.Left = walkexpr(n.Left, init)
 
 	case OCONVIFACE:
@@ -1018,12 +893,59 @@ opswitch:
 			} else {
 				t = itabname(n.Left.Type, n.Type)
 			}
-			l := Nod(OEFACE, t, n.Left)
+			l := nod(OEFACE, t, n.Left)
 			l.Type = n.Type
 			l.Typecheck = n.Typecheck
 			n = l
 			break
 		}
+		// Optimize convT2{E,I} when T is not pointer-shaped.
+		// We make the interface by initializing a stack temporary to
+		// the value we want to put in the interface, then using the address of
+		// that stack temporary for the interface data word.
+		if !n.Left.Type.IsInterface() && n.Esc == EscNone && n.Left.Type.Width <= 1024 {
+			tmp := temp(n.Left.Type)
+			init.Append(typecheck(nod(OAS, tmp, n.Left), Etop))
+			var t *Node
+			if n.Type.IsEmptyInterface() {
+				t = typename(n.Left.Type)
+			} else {
+				t = itabname(n.Left.Type, n.Type)
+			}
+			l := nod(OEFACE, t, typecheck(nod(OADDR, tmp, nil), Erv))
+			l.Type = n.Type
+			l.Typecheck = n.Typecheck
+			n = l
+			break
+		}
+
+		// Implement interface to empty interface conversion.
+		// tmp = i.itab
+		// if tmp != nil {
+		//    tmp = tmp.type
+		// }
+		// e = iface{tmp, i.data}
+		if n.Type.IsEmptyInterface() && n.Left.Type.IsInterface() && !n.Left.Type.IsEmptyInterface() {
+			// Evaluate the input interface.
+			c := temp(n.Left.Type)
+			init.Append(nod(OAS, c, n.Left))
+
+			// Get the itab out of the interface.
+			tmp := temp(ptrto(Types[TUINT8]))
+			init.Append(nod(OAS, tmp, typecheck(nod(OITAB, c, nil), Erv)))
+
+			// Get the type out of the itab.
+			nif := nod(OIF, typecheck(nod(ONE, tmp, nodnil()), Erv), nil)
+			nif.Nbody.Set1(nod(OAS, tmp, itabType(tmp)))
+			init.Append(nif)
+
+			// Build the result.
+			e := nod(OEFACE, tmp, ifaceData(c, ptrto(Types[TUINT8])))
+			e.Type = n.Type // assign type manually, typecheck doesn't understand OEFACE.
+			e.Typecheck = 1
+			n = e
+			break
+		}
 
 		var ll []*Node
 		if n.Type.IsEmptyInterface() {
@@ -1048,38 +970,23 @@ opswitch:
 			// with non-interface cases, is not visible to orderstmt, so we
 			// have to fall back on allocating a temp here.
 			if islvalue(n.Left) {
-				ll = append(ll, Nod(OADDR, n.Left, nil))
+				ll = append(ll, nod(OADDR, n.Left, nil))
 			} else {
-				ll = append(ll, Nod(OADDR, copyexpr(n.Left, n.Left.Type, init), nil))
+				ll = append(ll, nod(OADDR, copyexpr(n.Left, n.Left.Type, init), nil))
 			}
 			dowidth(n.Left.Type)
-			r := nodnil()
-			if n.Esc == EscNone && n.Left.Type.Width <= 1024 {
-				// Allocate stack buffer for value stored in interface.
-				r = temp(n.Left.Type)
-				r = Nod(OAS, r, nil) // zero temp
-				r = typecheck(r, Etop)
-				init.Append(r)
-				r = Nod(OADDR, r.Left, nil)
-				r = typecheck(r, Erv)
-			}
-			ll = append(ll, r)
 		}
 
 		fn := syslook(convFuncName(n.Left.Type, n.Type))
-		if !n.Left.Type.IsInterface() {
-			fn = substArgTypes(fn, n.Left.Type, n.Left.Type, n.Type)
-		} else {
-			fn = substArgTypes(fn, n.Left.Type, n.Type)
-		}
+		fn = substArgTypes(fn, n.Left.Type, n.Type)
 		dowidth(fn.Type)
-		n = Nod(OCALL, fn, nil)
+		n = nod(OCALL, fn, nil)
 		n.List.Set(ll)
 		n = typecheck(n, Erv)
 		n = walkexpr(n, init)
 
 	case OCONV, OCONVNOP:
-		if Thearch.LinkArch.Family == sys.ARM {
+		if Thearch.LinkArch.Family == sys.ARM || Thearch.LinkArch.Family == sys.MIPS {
 			if n.Left.Type.IsFloat() {
 				if n.Type.Etype == TINT64 {
 					n = mkcall("float64toint64", n.Type, init, conv(n.Left, Types[TFLOAT64]))
@@ -1094,12 +1001,45 @@ opswitch:
 
 			if n.Type.IsFloat() {
 				if n.Left.Type.Etype == TINT64 {
-					n = mkcall("int64tofloat64", n.Type, init, conv(n.Left, Types[TINT64]))
+					n = conv(mkcall("int64tofloat64", Types[TFLOAT64], init, conv(n.Left, Types[TINT64])), n.Type)
 					break
 				}
 
 				if n.Left.Type.Etype == TUINT64 {
-					n = mkcall("uint64tofloat64", n.Type, init, conv(n.Left, Types[TUINT64]))
+					n = conv(mkcall("uint64tofloat64", Types[TFLOAT64], init, conv(n.Left, Types[TUINT64])), n.Type)
+					break
+				}
+			}
+		}
+
+		if Thearch.LinkArch.Family == sys.I386 {
+			if n.Left.Type.IsFloat() {
+				if n.Type.Etype == TINT64 {
+					n = mkcall("float64toint64", n.Type, init, conv(n.Left, Types[TFLOAT64]))
+					break
+				}
+
+				if n.Type.Etype == TUINT64 {
+					n = mkcall("float64touint64", n.Type, init, conv(n.Left, Types[TFLOAT64]))
+					break
+				}
+				if n.Type.Etype == TUINT32 || n.Type.Etype == TUINT || n.Type.Etype == TUINTPTR {
+					n = mkcall("float64touint32", n.Type, init, conv(n.Left, Types[TFLOAT64]))
+					break
+				}
+			}
+			if n.Type.IsFloat() {
+				if n.Left.Type.Etype == TINT64 {
+					n = conv(mkcall("int64tofloat64", Types[TFLOAT64], init, conv(n.Left, Types[TINT64])), n.Type)
+					break
+				}
+
+				if n.Left.Type.Etype == TUINT64 {
+					n = conv(mkcall("uint64tofloat64", Types[TFLOAT64], init, conv(n.Left, Types[TUINT64])), n.Type)
+					break
+				}
+				if n.Left.Type.Etype == TUINT32 || n.Left.Type.Etype == TUINT || n.Left.Type.Etype == TUINTPTR {
+					n = conv(mkcall("uint32tofloat64", Types[TFLOAT64], init, conv(n.Left, Types[TUINT32])), n.Type)
 					break
 				}
 			}
@@ -1110,7 +1050,7 @@ opswitch:
 	case OANDNOT:
 		n.Left = walkexpr(n.Left, init)
 		n.Op = OAND
-		n.Right = Nod(OCOM, n.Right, nil)
+		n.Right = nod(OCOM, n.Right, nil)
 		n.Right = typecheck(n.Right, Erv)
 		n.Right = walkexpr(n.Right, init)
 
@@ -1126,7 +1066,7 @@ opswitch:
 		// rewrite complex div into function call.
 		et := n.Left.Type.Etype
 
-		if Iscomplex[et] && n.Op == ODIV {
+		if isComplex[et] && n.Op == ODIV {
 			t := n.Type
 			n = mkcall("complex128div", Types[TCOMPLEX128], init, conv(n.Left, Types[TCOMPLEX128]), conv(n.Right, Types[TCOMPLEX128]))
 			n = conv(n, t)
@@ -1134,7 +1074,7 @@ opswitch:
 		}
 
 		// Nothing to do for float divisions.
-		if Isfloat[et] {
+		if isFloat[et] {
 			break
 		}
 
@@ -1185,75 +1125,69 @@ opswitch:
 			if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) {
 				Warn("index bounds check elided")
 			}
-			if Smallintconst(n.Right) && !n.Bounded {
-				Yyerror("index out of bounds")
+			if smallintconst(n.Right) && !n.Bounded {
+				yyerror("index out of bounds")
 			}
 		} else if Isconst(n.Left, CTSTR) {
 			n.Bounded = bounded(r, int64(len(n.Left.Val().U.(string))))
 			if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) {
 				Warn("index bounds check elided")
 			}
-			if Smallintconst(n.Right) {
-				if !n.Bounded {
-					Yyerror("index out of bounds")
-				} else {
-					// replace "abc"[1] with 'b'.
-					// delayed until now because "abc"[1] is not
-					// an ideal constant.
-					v := n.Right.Int64()
-
-					Nodconst(n, n.Type, int64(n.Left.Val().U.(string)[v]))
-					n.Typecheck = 1
-				}
+			if smallintconst(n.Right) && !n.Bounded {
+				yyerror("index out of bounds")
 			}
 		}
 
 		if Isconst(n.Right, CTINT) {
-			if n.Right.Val().U.(*Mpint).CmpInt64(0) < 0 || n.Right.Val().U.(*Mpint).Cmp(Maxintval[TINT]) > 0 {
-				Yyerror("index out of bounds")
+			if n.Right.Val().U.(*Mpint).CmpInt64(0) < 0 || n.Right.Val().U.(*Mpint).Cmp(maxintval[TINT]) > 0 {
+				yyerror("index out of bounds")
 			}
 		}
 
 	case OINDEXMAP:
-		if n.Etype == 1 {
-			break
-		}
+		// Replace m[k] with *map{access1,assign}(maptype, m, &k)
 		n.Left = walkexpr(n.Left, init)
 		n.Right = walkexpr(n.Right, init)
-
-		t := n.Left.Type
-		p := ""
-		if t.Val().Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing.
-			switch algtype(t.Key()) {
-			case AMEM32:
-				p = "mapaccess1_fast32"
-			case AMEM64:
-				p = "mapaccess1_fast64"
-			case ASTRING:
-				p = "mapaccess1_faststr"
+		map_ := n.Left
+		key := n.Right
+		t := map_.Type
+		if n.Etype == 1 {
+			// This m[k] expression is on the left-hand side of an assignment.
+			// orderexpr made sure key is addressable.
+			key = nod(OADDR, key, nil)
+			n = mkcall1(mapfn("mapassign", t), nil, init, typename(t), map_, key)
+		} else {
+			// m[k] is not the target of an assignment.
+			p := ""
+			if t.Val().Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing.
+				switch algtype(t.Key()) {
+				case AMEM32:
+					p = "mapaccess1_fast32"
+				case AMEM64:
+					p = "mapaccess1_fast64"
+				case ASTRING:
+					p = "mapaccess1_faststr"
+				}
 			}
-		}
 
-		var key *Node
-		if p != "" {
-			// fast versions take key by value
-			key = n.Right
-		} else {
-			// standard version takes key by reference.
-			// orderexpr made sure key is addressable.
-			key = Nod(OADDR, n.Right, nil)
-			p = "mapaccess1"
-		}
+			if p == "" {
+				// standard version takes key by reference.
+				// orderexpr made sure key is addressable.
+				key = nod(OADDR, key, nil)
+				p = "mapaccess1"
+			}
 
-		if w := t.Val().Width; w <= 1024 { // 1024 must match ../../../../runtime/hashmap.go:maxZero
-			n = mkcall1(mapfn(p, t), Ptrto(t.Val()), init, typename(t), n.Left, key)
-		} else {
-			p = "mapaccess1_fat"
-			z := zeroaddr(w)
-			n = mkcall1(mapfn(p, t), Ptrto(t.Val()), init, typename(t), n.Left, key, z)
+			if w := t.Val().Width; w <= 1024 { // 1024 must match ../../../../runtime/hashmap.go:maxZero
+				n = mkcall1(mapfn(p, t), ptrto(t.Val()), init, typename(t), map_, key)
+			} else {
+				p = "mapaccess1_fat"
+				z := zeroaddr(w)
+				n = mkcall1(mapfn(p, t), ptrto(t.Val()), init, typename(t), map_, key, z)
+			}
 		}
-		n.NonNil = true // mapaccess always returns a non-nil pointer
-		n = Nod(OIND, n, nil)
+		n.Type = ptrto(t.Val())
+		n.NonNil = true // mapaccess1* and mapassign always return non-nil pointers.
+		n = nod(OIND, n, nil)
 		n.Type = t.Val()
 		n.Typecheck = 1
 
@@ -1294,34 +1228,21 @@ opswitch:
 				Fatalf("large ONEW with EscNone: %v", n)
 			}
 			r := temp(n.Type.Elem())
-			r = Nod(OAS, r, nil) // zero temp
+			r = nod(OAS, r, nil) // zero temp
 			r = typecheck(r, Etop)
 			init.Append(r)
-			r = Nod(OADDR, r.Left, nil)
+			r = nod(OADDR, r.Left, nil)
 			r = typecheck(r, Erv)
 			n = r
 		} else {
 			n = callnew(n.Type.Elem())
 		}
 
-		// If one argument to the comparison is an empty string,
-	// comparing the lengths instead will yield the same result
-	// without the function call.
 	case OCMPSTR:
-		if (Isconst(n.Left, CTSTR) && len(n.Left.Val().U.(string)) == 0) || (Isconst(n.Right, CTSTR) && len(n.Right.Val().U.(string)) == 0) {
-			// TODO(marvin): Fix Node.EType type union.
-			r := Nod(Op(n.Etype), Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil))
-			r = typecheck(r, Erv)
-			r = walkexpr(r, init)
-			r.Type = n.Type
-			n = r
-			break
-		}
-
 		// s + "badgerbadgerbadger" == "badgerbadgerbadger"
 		if (Op(n.Etype) == OEQ || Op(n.Etype) == ONE) && Isconst(n.Right, CTSTR) && n.Left.Op == OADDSTR && n.Left.List.Len() == 2 && Isconst(n.Left.List.Second(), CTSTR) && strlit(n.Right) == strlit(n.Left.List.Second()) {
 			// TODO(marvin): Fix Node.EType type union.
-			r := Nod(Op(n.Etype), Nod(OLEN, n.Left.List.First(), nil), Nodintconst(0))
+			r := nod(Op(n.Etype), nod(OLEN, n.Left.List.First(), nil), nodintconst(0))
 			r = typecheck(r, Erv)
 			r = walkexpr(r, init)
 			r.Type = n.Type
@@ -1329,12 +1250,61 @@ opswitch:
 			break
 		}
 
+		// Rewrite comparisons to short constant strings as length+byte-wise comparisons.
+		var cs, ncs *Node // const string, non-const string
+		switch {
+		case Isconst(n.Left, CTSTR) && Isconst(n.Right, CTSTR):
+			// ignore; will be constant evaluated
+		case Isconst(n.Left, CTSTR):
+			cs = n.Left
+			ncs = n.Right
+		case Isconst(n.Right, CTSTR):
+			cs = n.Right
+			ncs = n.Left
+		}
+		if cs != nil {
+			cmp := Op(n.Etype)
+			// maxRewriteLen was chosen empirically.
+			// It is the value that minimizes cmd/go file size
+			// across most architectures.
+			// See the commit description for CL 26758 for details.
+			maxRewriteLen := 6
+			var and Op
+			switch cmp {
+			case OEQ:
+				and = OANDAND
+			case ONE:
+				and = OOROR
+			default:
+				// Don't do byte-wise comparisons for <, <=, etc.
+				// They're fairly complicated.
+				// Length-only checks are ok, though.
+				maxRewriteLen = 0
+			}
+			if s := cs.Val().U.(string); len(s) <= maxRewriteLen {
+				if len(s) > 0 {
+					ncs = safeexpr(ncs, init)
+				}
+				// TODO(marvin): Fix Node.EType type union.
+				r := nod(cmp, nod(OLEN, ncs, nil), nodintconst(int64(len(s))))
+				for i := 0; i < len(s); i++ {
+					cb := nodintconst(int64(s[i]))
+					ncb := nod(OINDEX, ncs, nodintconst(int64(i)))
+					r = nod(and, r, nod(cmp, ncb, cb))
+				}
+				r = typecheck(r, Erv)
+				r = walkexpr(r, init)
+				r.Type = n.Type
+				n = r
+				break
+			}
+		}
+
 		var r *Node
 		// TODO(marvin): Fix Node.EType type union.
 		if Op(n.Etype) == OEQ || Op(n.Etype) == ONE {
 			// prepare for rewrite below
 			n.Left = cheapexpr(n.Left, init)
-
 			n.Right = cheapexpr(n.Right, init)
 
 			r = mkcall("eqstring", Types[TBOOL], init, conv(n.Left, Types[TSTRING]), conv(n.Right, Types[TSTRING]))
@@ -1344,12 +1314,11 @@ opswitch:
 			// TODO(marvin): Fix Node.EType type union.
 			if Op(n.Etype) == OEQ {
 				// len(left) == len(right) && eqstring(left, right)
-				r = Nod(OANDAND, Nod(OEQ, Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)), r)
+				r = nod(OANDAND, nod(OEQ, nod(OLEN, n.Left, nil), nod(OLEN, n.Right, nil)), r)
 			} else {
 				// len(left) != len(right) || !eqstring(left, right)
-				r = Nod(ONOT, r, nil)
-
-				r = Nod(OOROR, Nod(ONE, Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)), r)
+				r = nod(ONOT, r, nil)
+				r = nod(OOROR, nod(ONE, nod(OLEN, n.Left, nil), nod(OLEN, n.Right, nil)), r)
 			}
 
 			r = typecheck(r, Erv)
@@ -1357,9 +1326,8 @@ opswitch:
 		} else {
 			// sys_cmpstring(s1, s2) :: 0
 			r = mkcall("cmpstring", Types[TINT], init, conv(n.Left, Types[TSTRING]), conv(n.Right, Types[TSTRING]))
-
 			// TODO(marvin): Fix Node.EType type union.
-			r = Nod(Op(n.Etype), r, Nodintconst(0))
+			r = nod(Op(n.Etype), r, nodintconst(0))
 		}
 
 		r = typecheck(r, Erv)
@@ -1377,7 +1345,7 @@ opswitch:
 		Fatalf("append outside assignment")
 
 	case OCOPY:
-		n = copyany(n, init, instrumenting)
+		n = copyany(n, init, instrumenting && !compiling_runtime)
 
 		// cannot use chanfn - closechan takes any, not chan any
 	case OCLOSE:
@@ -1398,20 +1366,20 @@ opswitch:
 			// Allocate hmap buffer on stack.
 			var_ := temp(hmap(t))
 
-			a = Nod(OAS, var_, nil) // zero temp
+			a = nod(OAS, var_, nil) // zero temp
 			a = typecheck(a, Etop)
 			init.Append(a)
-			a = Nod(OADDR, var_, nil)
+			a = nod(OADDR, var_, nil)
 
 			// Allocate one bucket on stack.
 			// Maximum key/value size is 128 bytes, larger objects
 			// are stored with an indirection. So max bucket size is 2048+eps.
 			var_ = temp(mapbucket(t))
 
-			r = Nod(OAS, var_, nil) // zero temp
+			r = nod(OAS, var_, nil) // zero temp
 			r = typecheck(r, Etop)
 			init.Append(r)
-			r = Nod(OADDR, var_, nil)
+			r = nod(OADDR, var_, nil)
 		}
 
 		fn := syslook("makemap")
@@ -1432,31 +1400,51 @@ opswitch:
 			}
 			// var arr [r]T
 			// n = arr[:l]
-			t = aindex(r, t.Elem()) // [r]T
+			t = typArray(t.Elem(), nonnegintconst(r)) // [r]T
 			var_ := temp(t)
-			a := Nod(OAS, var_, nil) // zero temp
+			a := nod(OAS, var_, nil) // zero temp
 			a = typecheck(a, Etop)
 			init.Append(a)
-			r := Nod(OSLICE, var_, nil) // arr[:l]
+			r := nod(OSLICE, var_, nil) // arr[:l]
 			r.SetSliceBounds(nil, l, nil)
 			r = conv(r, n.Type) // in case n.Type is named.
 			r = typecheck(r, Erv)
 			r = walkexpr(r, init)
 			n = r
 		} else {
-			// makeslice(et *Type, nel int64, max int64) (ary []any)
-			fn := syslook("makeslice")
+			// n escapes; set up a call to makeslice.
+			// When len and cap can fit into int, use makeslice instead of
+			// makeslice64, which is faster and shorter on 32 bit platforms.
+
+			if t.Elem().NotInHeap {
+				yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem())
+			}
+
+			len, cap := l, r
+
+			fnname := "makeslice64"
+			argtype := Types[TINT64]
+
+			// typechecking guarantees that TIDEAL len/cap are positive and fit in an int.
+			// The case of len or cap overflow when converting TUINT or TUINTPTR to TINT
+			// will be handled by the negative range checks in makeslice during runtime.
+			if (len.Type.IsKind(TIDEAL) || maxintval[len.Type.Etype].Cmp(maxintval[TUINT]) <= 0) &&
+				(cap.Type.IsKind(TIDEAL) || maxintval[cap.Type.Etype].Cmp(maxintval[TUINT]) <= 0) {
+				fnname = "makeslice"
+				argtype = Types[TINT]
+			}
 
+			fn := syslook(fnname)
 			fn = substArgTypes(fn, t.Elem()) // any-1
-			n = mkcall1(fn, n.Type, init, typename(t.Elem()), conv(l, Types[TINT64]), conv(r, Types[TINT64]))
+			n = mkcall1(fn, t, init, typename(t.Elem()), conv(len, argtype), conv(cap, argtype))
 		}
 
 	case ORUNESTR:
 		a := nodnil()
 		if n.Esc == EscNone {
-			t := aindex(Nodintconst(4), Types[TUINT8])
+			t := typArray(Types[TUINT8], 4)
 			var_ := temp(t)
-			a = Nod(OADDR, var_, nil)
+			a = nod(OADDR, var_, nil)
 		}
 
 		// intstring(*[4]byte, rune)
@@ -1466,9 +1454,9 @@ opswitch:
 		a := nodnil()
 		if n.Esc == EscNone {
 			// Create temporary buffer for string on stack.
-			t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
+			t := typArray(Types[TUINT8], tmpstringbufsize)
 
-			a = Nod(OADDR, temp(t), nil)
+			a = nod(OADDR, temp(t), nil)
 		}
 
 		// slicebytetostring(*[32]byte, []byte) string;
@@ -1476,6 +1464,14 @@ opswitch:
 
 		// slicebytetostringtmp([]byte) string;
 	case OARRAYBYTESTRTMP:
+		n.Left = walkexpr(n.Left, init)
+
+		if !instrumenting {
+			// Let the backend handle OARRAYBYTESTRTMP directly
+			// to avoid a function call to slicebytetostringtmp.
+			break
+		}
+
 		n = mkcall("slicebytetostringtmp", n.Type, init, n.Left)
 
 		// slicerunetostring(*[32]byte, []rune) string;
@@ -1484,9 +1480,9 @@ opswitch:
 
 		if n.Esc == EscNone {
 			// Create temporary buffer for string on stack.
-			t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
+			t := typArray(Types[TUINT8], tmpstringbufsize)
 
-			a = Nod(OADDR, temp(t), nil)
+			a = nod(OADDR, temp(t), nil)
 		}
 
 		n = mkcall("slicerunetostring", n.Type, init, a, n.Left)
@@ -1497,16 +1493,22 @@ opswitch:
 
 		if n.Esc == EscNone {
 			// Create temporary buffer for slice on stack.
-			t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
+			t := typArray(Types[TUINT8], tmpstringbufsize)
 
-			a = Nod(OADDR, temp(t), nil)
+			a = nod(OADDR, temp(t), nil)
 		}
 
 		n = mkcall("stringtoslicebyte", n.Type, init, a, conv(n.Left, Types[TSTRING]))
 
-		// stringtoslicebytetmp(string) []byte;
 	case OSTRARRAYBYTETMP:
-		n = mkcall("stringtoslicebytetmp", n.Type, init, conv(n.Left, Types[TSTRING]))
+		// []byte(string) conversion that creates a slice
+		// referring to the actual string bytes.
+		// This conversion is handled later by the backend and
+		// is only for use by internal compiler optimizations
+		// that know that the slice won't be mutated.
+		// The only such case today is:
+		// for i, c := range []byte(string)
+		n.Left = walkexpr(n.Left, init)
 
 		// stringtoslicerune(*[32]rune, string) []rune
 	case OSTRARRAYRUNE:
@@ -1514,16 +1516,16 @@ opswitch:
 
 		if n.Esc == EscNone {
 			// Create temporary buffer for slice on stack.
-			t := aindex(Nodintconst(tmpstringbufsize), Types[TINT32])
+			t := typArray(Types[TINT32], tmpstringbufsize)
 
-			a = Nod(OADDR, temp(t), nil)
+			a = nod(OADDR, temp(t), nil)
 		}
 
 		n = mkcall("stringtoslicerune", n.Type, init, a, n.Left)
 
 		// ifaceeq(i1 any-1, i2 any-2) (ret bool);
 	case OCMPIFACE:
-		if !Eqtype(n.Left.Type, n.Right.Type) {
+		if !eqtype(n.Left.Type, n.Right.Type) {
 			Fatalf("ifaceeq %v %v %v", n.Op, n.Left.Type, n.Right.Type)
 		}
 		var fn *Node
@@ -1539,44 +1541,41 @@ opswitch:
 		r := mkcall1(fn, n.Type, init, n.Left, n.Right)
 		// TODO(marvin): Fix Node.EType type union.
 		if Op(n.Etype) == ONE {
-			r = Nod(ONOT, r, nil)
+			r = nod(ONOT, r, nil)
 		}
 
 		// check itable/type before full compare.
 		// TODO(marvin): Fix Node.EType type union.
 		if Op(n.Etype) == OEQ {
-			r = Nod(OANDAND, Nod(OEQ, Nod(OITAB, n.Left, nil), Nod(OITAB, n.Right, nil)), r)
+			r = nod(OANDAND, nod(OEQ, nod(OITAB, n.Left, nil), nod(OITAB, n.Right, nil)), r)
 		} else {
-			r = Nod(OOROR, Nod(ONE, Nod(OITAB, n.Left, nil), Nod(OITAB, n.Right, nil)), r)
+			r = nod(OOROR, nod(ONE, nod(OITAB, n.Left, nil), nod(OITAB, n.Right, nil)), r)
 		}
 		r = typecheck(r, Erv)
 		r = walkexpr(r, init)
 		r.Type = n.Type
 		n = r
 
-	case OARRAYLIT, OMAPLIT, OSTRUCTLIT, OPTRLIT:
+	case OARRAYLIT, OSLICELIT, OMAPLIT, OSTRUCTLIT, OPTRLIT:
 		if isStaticCompositeLiteral(n) {
 			// n can be directly represented in the read-only data section.
 			// Make direct reference to the static data. See issue 12841.
-			vstat := staticname(n.Type, 0)
-			if n.Op == OSTRUCTLIT {
-				structlit(0, 1, n, vstat, init)
-			} else {
-				arraylit(0, 1, n, vstat, init)
-			}
+			vstat := staticname(n.Type)
+			vstat.Name.Readonly = true
+			fixedlit(inInitFunction, initKindStatic, n, vstat, init)
 			n = vstat
 			n = typecheck(n, Erv)
 			break
 		}
 		var_ := temp(n.Type)
-		anylit(0, n, var_, init)
+		anylit(n, var_, init)
 		n = var_
 
 	case OSEND:
 		n1 := n.Right
 		n1 = assignconv(n1, n.Left.Type.Elem(), "chan send")
 		n1 = walkexpr(n1, init)
-		n1 = Nod(OADDR, n1, nil)
+		n1 = nod(OADDR, n1, nil)
 		n = mkcall1(chanfn("chansend1", 2, n.Left.Type), nil, init, typename(n.Left.Type), n.Left, n1)
 
 	case OCLOSURE:
@@ -1630,7 +1629,7 @@ func reduceSlice(n *Node) *Node {
 func ascompatee1(op Op, l *Node, r *Node, init *Nodes) *Node {
 	// convas will turn map assigns into function calls,
 	// making it impossible for reorder3 to work.
-	n := Nod(OAS, l, r)
+	n := nod(OAS, l, r)
 
 	if l.Op == OINDEXMAP {
 		return n
@@ -1670,7 +1669,7 @@ func ascompatee(op Op, nl, nr []*Node, init *Nodes) []*Node {
 		var nln, nrn Nodes
 		nln.Set(nl)
 		nrn.Set(nr)
-		Yyerror("error in shape across %v %v %v / %d %d [%s]", hconv(nln, FmtSign), op, hconv(nrn, FmtSign), len(nl), len(nr), Curfn.Func.Nname.Sym.Name)
+		yyerror("error in shape across %+v %v %+v / %d %d [%s]", nln, op, nrn, len(nl), len(nr), Curfn.Func.Nname.Sym.Name)
 	}
 	return nn
 }
@@ -1687,7 +1686,7 @@ func fncall(l *Node, rt *Type) bool {
 	if needwritebarrier(l, &r) {
 		return true
 	}
-	if Eqtype(l.Type, rt) {
+	if eqtype(l.Type, rt) {
 		return false
 	}
 	return true
@@ -1696,10 +1695,10 @@ func fncall(l *Node, rt *Type) bool {
 // check assign type list to
 // a expression list. called in
 //	expr-list = func()
-func ascompatet(op Op, nl Nodes, nr *Type, fp int, init *Nodes) []*Node {
-	r, saver := IterFields(nr)
+func ascompatet(op Op, nl Nodes, nr *Type) []*Node {
+	r, saver := iterFields(nr)
 
-	var nn, mm []*Node
+	var nn, mm Nodes
 	var ullmanOverflow bool
 	var i int
 	for i = 0; i < nl.Len(); i++ {
@@ -1718,32 +1717,32 @@ func ascompatet(op Op, nl Nodes, nr *Type, fp int, init *Nodes) []*Node {
 		if fncall(l, r.Type) {
 			tmp := temp(r.Type)
 			tmp = typecheck(tmp, Erv)
-			a := Nod(OAS, l, tmp)
-			a = convas(a, init)
-			mm = append(mm, a)
+			a := nod(OAS, l, tmp)
+			a = convas(a, &mm)
+			mm.Append(a)
 			l = tmp
 		}
 
-		a := Nod(OAS, l, nodarg(r, fp))
-		a = convas(a, init)
+		a := nod(OAS, l, nodarg(r, 0))
+		a = convas(a, &nn)
 		ullmancalc(a)
 		if a.Ullman >= UINF {
 			Dump("ascompatet ucount", a)
 			ullmanOverflow = true
 		}
 
-		nn = append(nn, a)
+		nn.Append(a)
 		r = saver.Next()
 	}
 
 	if i < nl.Len() || r != nil {
-		Yyerror("ascompatet: assignment count mismatch: %d = %d", nl.Len(), nr.NumFields())
+		yyerror("ascompatet: assignment count mismatch: %d = %d", nl.Len(), nr.NumFields())
 	}
 
 	if ullmanOverflow {
 		Fatalf("ascompatet: too many function calls evaluating parameters")
 	}
-	return append(nn, mm...)
+	return append(nn.Slice(), mm.Slice()...)
 }
 
 // package all the arguments that match a ... T parameter into a []T.
@@ -1754,14 +1753,13 @@ func mkdotargslice(lr0, nn []*Node, l *Field, fp int, init *Nodes, ddd *Node) []
 	}
 
 	tslice := typSlice(l.Type.Elem())
-	tslice.Noalg = true
 
 	var n *Node
 	if len(lr0) == 0 {
 		n = nodnil()
 		n.Type = tslice
 	} else {
-		n = Nod(OCOMPLIT, nil, typenod(tslice))
+		n = nod(OCOMPLIT, nil, typenod(tslice))
 		if ddd != nil && prealloc[ddd] != nil {
 			prealloc[n] = prealloc[ddd] // temporary to use
 		}
@@ -1774,7 +1772,7 @@ func mkdotargslice(lr0, nn []*Node, l *Field, fp int, init *Nodes, ddd *Node) []
 		n = walkexpr(n, init)
 	}
 
-	a := Nod(OAS, nodarg(l, fp), n)
+	a := nod(OAS, nodarg(l, fp), n)
 	nn = append(nn, convas(a, init))
 	return nn
 }
@@ -1786,7 +1784,7 @@ func dumptypes(nl *Type, what string) string {
 		if s != "" {
 			s += ", "
 		}
-		s += Fldconv(l, 0)
+		s += fldconv(l, 0)
 	}
 	if s == "" {
 		s = fmt.Sprintf("[no arguments %s]", what)
@@ -1800,7 +1798,7 @@ func dumpnodetypes(l []*Node, what string) string {
 		if s != "" {
 			s += ", "
 		}
-		s += Tconv(r.Type, 0)
+		s += r.Type.String()
 	}
 	if s == "" {
 		s = fmt.Sprintf("[no arguments %s]", what)
@@ -1814,7 +1812,7 @@ func dumpnodetypes(l []*Node, what string) string {
 //	func(expr-list)
 func ascompatte(op Op, call *Node, isddd bool, nl *Type, lr []*Node, fp int, init *Nodes) []*Node {
 	lr0 := lr
-	l, savel := IterFields(nl)
+	l, savel := iterFields(nl)
 	var r *Node
 	if len(lr) > 0 {
 		r = lr[0]
@@ -1826,9 +1824,9 @@ func ascompatte(op Op, call *Node, isddd bool, nl *Type, lr []*Node, fp int, ini
 		// optimization - can do block copy
 		if eqtypenoname(r.Type, nl) {
 			arg := nodarg(nl, fp)
-			r = Nod(OCONVNOP, r, nil)
+			r = nod(OCONVNOP, r, nil)
 			r.Type = arg.Type
-			nn = []*Node{convas(Nod(OAS, arg, r), init)}
+			nn = []*Node{convas(nod(OAS, arg, r), init)}
 			goto ret
 		}
 
@@ -1841,7 +1839,7 @@ func ascompatte(op Op, call *Node, isddd bool, nl *Type, lr []*Node, fp int, ini
 			alist = append(alist, tmp)
 		}
 
-		a := Nod(OAS2, nil, nil)
+		a := nod(OAS2, nil, nil)
 		a.List.Set(alist)
 		a.Rlist.Set(lr)
 		a = typecheck(a, Etop)
@@ -1849,7 +1847,7 @@ func ascompatte(op Op, call *Node, isddd bool, nl *Type, lr []*Node, fp int, ini
 		init.Append(a)
 		lr = alist
 		r = lr[0]
-		l, savel = IterFields(nl)
+		l, savel = iterFields(nl)
 	}
 
 	for {
@@ -1858,15 +1856,15 @@ func ascompatte(op Op, call *Node, isddd bool, nl *Type, lr []*Node, fp int, ini
 			ll := savel.Next()
 
 			if ll != nil {
-				Yyerror("... must be last argument")
+				yyerror("... must be last argument")
 			}
 
 			// special case --
 			// only if we are assigning a single ddd
 			// argument to a ddd parameter then it is
 			// passed through unencapsulated
-			if r != nil && len(lr) <= 1 && isddd && Eqtype(l.Type, r.Type) {
-				a := Nod(OAS, nodarg(l, fp), r)
+			if r != nil && len(lr) <= 1 && isddd && eqtype(l.Type, r.Type) {
+				a := nod(OAS, nodarg(l, fp), r)
 				a = convas(a, init)
 				nn = append(nn, a)
 				break
@@ -1885,16 +1883,16 @@ func ascompatte(op Op, call *Node, isddd bool, nl *Type, lr []*Node, fp int, ini
 				l1 := dumptypes(nl, "expected")
 				l2 := dumpnodetypes(lr0, "given")
 				if l != nil {
-					Yyerror("not enough arguments to %v\n\t%s\n\t%s", op, l1, l2)
+					yyerror("not enough arguments to %v\n\t%s\n\t%s", op, l1, l2)
 				} else {
-					Yyerror("too many arguments to %v\n\t%s\n\t%s", op, l1, l2)
+					yyerror("too many arguments to %v\n\t%s\n\t%s", op, l1, l2)
 				}
 			}
 
 			break
 		}
 
-		a := Nod(OAS, nodarg(l, fp), r)
+		a := nod(OAS, nodarg(l, fp), r)
 		a = convas(a, init)
 		nn = append(nn, a)
 
@@ -1975,7 +1973,7 @@ func walkprint(nn *Node, init *Nodes) *Node {
 		} else if n.Type.IsSlice() {
 			on = syslook("printslice")
 			on = substArgTypes(on, n.Type) // any-1
-		} else if Isint[et] {
+		} else if isInt[et] {
 			if et == TUINT64 {
 				if (t.Sym.Pkg == Runtimepkg || compiling_runtime) && t.Sym.Name == "hex" {
 					on = syslook("printhex")
@@ -1985,9 +1983,9 @@ func walkprint(nn *Node, init *Nodes) *Node {
 			} else {
 				on = syslook("printint")
 			}
-		} else if Isfloat[et] {
+		} else if isFloat[et] {
 			on = syslook("printfloat")
-		} else if Iscomplex[et] {
+		} else if isComplex[et] {
 			on = syslook("printcomplex")
 		} else if et == TBOOL {
 			on = syslook("printbool")
@@ -2000,12 +1998,12 @@ func walkprint(nn *Node, init *Nodes) *Node {
 
 		t = on.Type.Params().Field(0).Type
 
-		if !Eqtype(t, n.Type) {
-			n = Nod(OCONV, n, nil)
+		if !eqtype(t, n.Type) {
+			n = nod(OCONV, n, nil)
 			n.Type = t
 		}
 
-		r = Nod(OCALL, on, nil)
+		r = nod(OCALL, on, nil)
 		r.List.Append(n)
 		calls = append(calls, r)
 	}
@@ -2019,7 +2017,7 @@ func walkprint(nn *Node, init *Nodes) *Node {
 	typecheckslice(calls, Etop)
 	walkexprlist(calls, init)
 
-	r = Nod(OEMPTY, nil, nil)
+	r = nod(OEMPTY, nil, nil)
 	r = typecheck(r, Etop)
 	r = walkexpr(r, init)
 	r.Ninit.Set(calls)
@@ -2027,17 +2025,20 @@ func walkprint(nn *Node, init *Nodes) *Node {
 }
 
 func callnew(t *Type) *Node {
+	if t.NotInHeap {
+		yyerror("%v is go:notinheap; heap allocation disallowed", t)
+	}
 	dowidth(t)
 	fn := syslook("newobject")
 	fn = substArgTypes(fn, t)
-	v := mkcall1(fn, Ptrto(t), nil, typename(t))
+	v := mkcall1(fn, ptrto(t), nil, typename(t))
 	v.NonNil = true
 	return v
 }
 
 func iscallret(n *Node) bool {
 	n = outervalue(n)
-	return n.Op == OINDREG && n.Reg == int16(Thearch.REGSP)
+	return n.Op == OINDREGSP
 }
 
 func isstack(n *Node) bool {
@@ -2045,7 +2046,7 @@ func isstack(n *Node) bool {
 
 	// If n is *autotmp and autotmp = &foo, replace n with foo.
 	// We introduce such temps when initializing struct literals.
-	if n.Op == OIND && n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") {
+	if n.Op == OIND && n.Left.Op == ONAME && n.Left.IsAutoTmp() {
 		defn := n.Left.Name.Defn
 		if defn != nil && defn.Op == OAS && defn.Right.Op == OADDR {
 			n = defn.Right.Left
@@ -2053,8 +2054,8 @@ func isstack(n *Node) bool {
 	}
 
 	switch n.Op {
-	case OINDREG:
-		return n.Reg == int16(Thearch.REGSP)
+	case OINDREGSP:
+		return true
 
 	case ONAME:
 		switch n.Class {
@@ -2066,20 +2067,6 @@ func isstack(n *Node) bool {
 	return false
 }
 
-func isglobal(n *Node) bool {
-	n = outervalue(n)
-
-	switch n.Op {
-	case ONAME:
-		switch n.Class {
-		case PEXTERN:
-			return true
-		}
-	}
-
-	return false
-}
-
 // Do we need a write barrier for the assignment l = r?
 func needwritebarrier(l *Node, r *Node) bool {
 	if !use_writebarrier {
@@ -2102,11 +2089,20 @@ func needwritebarrier(l *Node, r *Node) bool {
 		return false
 	}
 
-	// No write barrier for implicit zeroing.
-	if r == nil {
+	// No write barrier if this is a pointer to a go:notinheap
+	// type, since the write barrier's inheap(ptr) check will fail.
+	if l.Type.IsPtr() && l.Type.Elem().NotInHeap {
 		return false
 	}
 
+	// Implicit zeroing is still zeroing, so it needs write
+	// barriers. In practice, these are all to stack variables
+	// (even if isstack isn't smart enough to figure that out), so
+	// they'll be eliminated by the backend.
+	if r == nil {
+		return true
+	}
+
 	// Ignore no-op conversions when making decision.
 	// Ensures that xp = unsafe.Pointer(&x) is treated
 	// the same as xp = &x.
@@ -2114,15 +2110,13 @@ func needwritebarrier(l *Node, r *Node) bool {
 		r = r.Left
 	}
 
-	// No write barrier for zeroing or initialization to constant.
-	if iszero(r) || r.Op == OLITERAL {
-		return false
-	}
-
-	// No write barrier for storing static (read-only) data.
-	if r.Op == ONAME && strings.HasPrefix(r.Sym.Name, "statictmp_") {
-		return false
-	}
+	// TODO: We can eliminate write barriers if we know *both* the
+	// current and new content of the slot must already be shaded.
+	// We know a pointer is shaded if it's nil, or points to
+	// static data, a global (variable or function), or the stack.
+	// The nil optimization could be particularly useful for
+	// writes to just-allocated objects. Unfortunately, knowing
+	// the "current" value of the slot requires flow analysis.
 
 	// No write barrier for storing address of stack values,
 	// which are guaranteed only to be written to the stack.
@@ -2130,18 +2124,6 @@ func needwritebarrier(l *Node, r *Node) bool {
 		return false
 	}
 
-	// No write barrier for storing address of global, which
-	// is live no matter what.
-	if r.Op == OADDR && isglobal(r.Left) {
-		return false
-	}
-
-	// No write barrier for storing global function, which is live
-	// no matter what.
-	if r.Op == ONAME && r.Class == PFUNC {
-		return false
-	}
-
 	// Otherwise, be conservative and use write barrier.
 	return true
 }
@@ -2151,7 +2133,7 @@ func needwritebarrier(l *Node, r *Node) bool {
 func applywritebarrier(n *Node) *Node {
 	if n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) {
 		if Debug_wb > 1 {
-			Warnl(n.Lineno, "marking %v for barrier", Nconv(n.Left, 0))
+			Warnl(n.Lineno, "marking %v for barrier", n.Left)
 		}
 		n.Op = OASWB
 		return n
@@ -2183,23 +2165,7 @@ func convas(n *Node, init *Nodes) *Node {
 		goto out
 	}
 
-	if n.Left.Op == OINDEXMAP {
-		map_ := n.Left.Left
-		key := n.Left.Right
-		val := n.Right
-		map_ = walkexpr(map_, init)
-		key = walkexpr(key, init)
-		val = walkexpr(val, init)
-
-		// orderexpr made sure key and val are addressable.
-		key = Nod(OADDR, key, nil)
-
-		val = Nod(OADDR, val, nil)
-		n = mkcall1(mapfn("mapassign1", map_.Type), nil, init, typename(map_.Type), map_, key, val)
-		goto out
-	}
-
-	if !Eqtype(lt, rt) {
+	if !eqtype(lt, rt) {
 		n.Right = assignconv(n.Right, lt, "assignment")
 		n.Right = walkexpr(n.Right, init)
 	}
@@ -2251,7 +2217,7 @@ func reorder1(all []*Node) []*Node {
 		// make assignment of fncall to tempname
 		a = temp(n.Right.Type)
 
-		a = Nod(OAS, a, n.Right)
+		a = nod(OAS, a, n.Right)
 		g = append(g, a)
 
 		// put normal arg assignment on list
@@ -2340,7 +2306,7 @@ func reorder3save(n *Node, all []*Node, i int, early *[]*Node) *Node {
 	}
 
 	q := temp(n.Type)
-	q = Nod(OAS, q, n)
+	q = nod(OAS, q, n)
 	q = typecheck(q, Etop)
 	*early = append(*early, q)
 	return q.Left
@@ -2537,7 +2503,7 @@ func vmatch1(l *Node, r *Node) bool {
 		case PPARAM, PAUTO:
 			break
 
-			// assignment to non-stack variable
+		// assignment to non-stack variable
 		// must be delayed if right has function calls.
 		default:
 			if r.Ullman >= UINF {
@@ -2578,7 +2544,7 @@ func paramstoheap(params *Type) []*Node {
 			// Defer might stop a panic and show the
 			// return values as they exist at the time of panic.
 			// Make sure to zero them on entry to the function.
-			nn = append(nn, Nod(OAS, nodarg(t, 1), nil))
+			nn = append(nn, nod(OAS, nodarg(t, 1), nil))
 		}
 
 		v := t.Nname
@@ -2590,9 +2556,9 @@ func paramstoheap(params *Type) []*Node {
 		}
 
 		if stackcopy := v.Name.Param.Stackcopy; stackcopy != nil {
-			nn = append(nn, walkstmt(Nod(ODCL, v, nil)))
+			nn = append(nn, walkstmt(nod(ODCL, v, nil)))
 			if stackcopy.Class == PPARAM {
-				nn = append(nn, walkstmt(typecheck(Nod(OAS, v, stackcopy), Etop)))
+				nn = append(nn, walkstmt(typecheck(nod(OAS, v, stackcopy), Etop)))
 			}
 		}
 	}
@@ -2610,7 +2576,7 @@ func returnsfromheap(params *Type) []*Node {
 			continue
 		}
 		if stackcopy := v.Name.Param.Stackcopy; stackcopy != nil && stackcopy.Class == PPARAMOUT {
-			nn = append(nn, walkstmt(typecheck(Nod(OAS, stackcopy, v), Etop)))
+			nn = append(nn, walkstmt(typecheck(nod(OAS, stackcopy, v), Etop)))
 		}
 	}
 
@@ -2639,7 +2605,7 @@ func vmkcall(fn *Node, t *Type, init *Nodes, va []*Node) *Node {
 
 	n := fn.Type.Params().NumFields()
 
-	r := Nod(OCALL, fn, nil)
+	r := nod(OCALL, fn, nil)
 	r.List.Set(va[:n])
 	if fn.Type.Results().NumFields() > 0 {
 		r = typecheck(r, Erv|Efnstruct)
@@ -2660,10 +2626,10 @@ func mkcall1(fn *Node, t *Type, init *Nodes, args ...*Node) *Node {
 }
 
 func conv(n *Node, t *Type) *Node {
-	if Eqtype(n.Type, t) {
+	if eqtype(n.Type, t) {
 		return n
 	}
-	n = Nod(OCONV, n, nil)
+	n = nod(OCONV, n, nil)
 	n.Type = t
 	n = typecheck(n, Erv)
 	return n
@@ -2714,7 +2680,7 @@ func addstr(n *Node, init *Nodes) *Node {
 	c := n.List.Len()
 
 	if c < 2 {
-		Yyerror("addstr count %d too small", c)
+		yyerror("addstr count %d too small", c)
 	}
 
 	buf := nodnil()
@@ -2729,9 +2695,9 @@ func addstr(n *Node, init *Nodes) *Node {
 		// Don't allocate the buffer if the result won't fit.
 		if sz < tmpstringbufsize {
 			// Create temporary buffer for result string on stack.
-			t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
+			t := typArray(Types[TUINT8], tmpstringbufsize)
 
-			buf = Nod(OADDR, temp(t), nil)
+			buf = nod(OADDR, temp(t), nil)
 		}
 	}
 
@@ -2751,7 +2717,7 @@ func addstr(n *Node, init *Nodes) *Node {
 		fn = "concatstrings"
 
 		t := typSlice(Types[TSTRING])
-		slice := Nod(OCOMPLIT, nil, typenod(t))
+		slice := nod(OCOMPLIT, nil, typenod(t))
 		if prealloc[n] != nil {
 			prealloc[slice] = prealloc[n]
 		}
@@ -2761,7 +2727,7 @@ func addstr(n *Node, init *Nodes) *Node {
 	}
 
 	cat := syslook(fn)
-	r := Nod(OCALL, cat, nil)
+	r := nod(OCALL, cat, nil)
 	r.List.Set(args)
 	r = typecheck(r, Erv)
 	r = walkexpr(r, init)
@@ -2802,15 +2768,15 @@ func appendslice(n *Node, init *Nodes) *Node {
 
 	// var s []T
 	s := temp(l1.Type)
-	l = append(l, Nod(OAS, s, l1)) // s = l1
+	l = append(l, nod(OAS, s, l1)) // s = l1
 
 	// n := len(s) + len(l2)
 	nn := temp(Types[TINT])
-	l = append(l, Nod(OAS, nn, Nod(OADD, Nod(OLEN, s, nil), Nod(OLEN, l2, nil))))
+	l = append(l, nod(OAS, nn, nod(OADD, nod(OLEN, s, nil), nod(OLEN, l2, nil))))
 
 	// if uint(n) > uint(cap(s))
-	nif := Nod(OIF, nil, nil)
-	nif.Left = Nod(OGT, Nod(OCONV, nn, nil), Nod(OCONV, Nod(OCAP, s, nil), nil))
+	nif := nod(OIF, nil, nil)
+	nif.Left = nod(OGT, nod(OCONV, nn, nil), nod(OCONV, nod(OCAP, s, nil), nil))
 	nif.Left.Left.Type = Types[TUINT]
 	nif.Left.Right.Type = Types[TUINT]
 
@@ -2819,19 +2785,19 @@ func appendslice(n *Node, init *Nodes) *Node {
 	fn = substArgTypes(fn, s.Type.Elem(), s.Type.Elem())
 
 	// s = growslice(T, s, n)
-	nif.Nbody.Set1(Nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type.Elem()), s, nn)))
+	nif.Nbody.Set1(nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type.Elem()), s, nn)))
 	l = append(l, nif)
 
 	// s = s[:n]
-	nt := Nod(OSLICE, s, nil)
+	nt := nod(OSLICE, s, nil)
 	nt.SetSliceBounds(nil, nn, nil)
 	nt.Etype = 1
-	l = append(l, Nod(OAS, s, nt))
+	l = append(l, nod(OAS, s, nt))
 
 	if haspointers(l1.Type.Elem()) {
 		// copy(s[len(l1):], l2)
-		nptr1 := Nod(OSLICE, s, nil)
-		nptr1.SetSliceBounds(Nod(OLEN, l1, nil), nil, nil)
+		nptr1 := nod(OSLICE, s, nil)
+		nptr1.SetSliceBounds(nod(OLEN, l1, nil), nil, nil)
 		nptr1.Etype = 1
 		nptr2 := l2
 		fn := syslook("typedslicecopy")
@@ -2840,11 +2806,11 @@ func appendslice(n *Node, init *Nodes) *Node {
 		ln.Set(l)
 		nt := mkcall1(fn, Types[TINT], &ln, typename(l1.Type.Elem()), nptr1, nptr2)
 		l = append(ln.Slice(), nt)
-	} else if instrumenting {
+	} else if instrumenting && !compiling_runtime {
 		// rely on runtime to instrument copy.
 		// copy(s[len(l1):], l2)
-		nptr1 := Nod(OSLICE, s, nil)
-		nptr1.SetSliceBounds(Nod(OLEN, l1, nil), nil, nil)
+		nptr1 := nod(OSLICE, s, nil)
+		nptr1.SetSliceBounds(nod(OLEN, l1, nil), nil, nil)
 		nptr1.Etype = 1
 		nptr2 := l2
 		var fn *Node
@@ -2856,25 +2822,25 @@ func appendslice(n *Node, init *Nodes) *Node {
 		fn = substArgTypes(fn, l1.Type, l2.Type)
 		var ln Nodes
 		ln.Set(l)
-		nt := mkcall1(fn, Types[TINT], &ln, nptr1, nptr2, Nodintconst(s.Type.Elem().Width))
+		nt := mkcall1(fn, Types[TINT], &ln, nptr1, nptr2, nodintconst(s.Type.Elem().Width))
 		l = append(ln.Slice(), nt)
 	} else {
 		// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
-		nptr1 := Nod(OINDEX, s, Nod(OLEN, l1, nil))
+		nptr1 := nod(OINDEX, s, nod(OLEN, l1, nil))
 		nptr1.Bounded = true
 
-		nptr1 = Nod(OADDR, nptr1, nil)
+		nptr1 = nod(OADDR, nptr1, nil)
 
-		nptr2 := Nod(OSPTR, l2, nil)
+		nptr2 := nod(OSPTR, l2, nil)
 
 		fn := syslook("memmove")
 		fn = substArgTypes(fn, s.Type.Elem(), s.Type.Elem())
 
 		var ln Nodes
 		ln.Set(l)
-		nwid := cheapexpr(conv(Nod(OLEN, l2, nil), Types[TUINTPTR]), &ln)
+		nwid := cheapexpr(conv(nod(OLEN, l2, nil), Types[TUINTPTR]), &ln)
 
-		nwid = Nod(OMUL, nwid, Nodintconst(s.Type.Elem().Width))
+		nwid = nod(OMUL, nwid, nodintconst(s.Type.Elem().Width))
 		nt := mkcall1(fn, nil, &ln, nptr1, nptr2, nwid)
 		l = append(ln.Slice(), nt)
 	}
@@ -2933,43 +2899,43 @@ func walkappend(n *Node, init *Nodes, dst *Node) *Node {
 
 	// General case, with no function calls left as arguments.
 	// Leave for gen, except that instrumentation requires old form.
-	if !instrumenting {
+	if !instrumenting || compiling_runtime {
 		return n
 	}
 
 	var l []*Node
 
 	ns := temp(nsrc.Type)
-	l = append(l, Nod(OAS, ns, nsrc)) // s = src
+	l = append(l, nod(OAS, ns, nsrc)) // s = src
 
-	na := Nodintconst(int64(argc)) // const argc
-	nx := Nod(OIF, nil, nil)       // if cap(s) - len(s) < argc
-	nx.Left = Nod(OLT, Nod(OSUB, Nod(OCAP, ns, nil), Nod(OLEN, ns, nil)), na)
+	na := nodintconst(int64(argc)) // const argc
+	nx := nod(OIF, nil, nil)       // if cap(s) - len(s) < argc
+	nx.Left = nod(OLT, nod(OSUB, nod(OCAP, ns, nil), nod(OLEN, ns, nil)), na)
 
 	fn := syslook("growslice") //   growslice(<type>, old []T, mincap int) (ret []T)
 	fn = substArgTypes(fn, ns.Type.Elem(), ns.Type.Elem())
 
-	nx.Nbody.Set1(Nod(OAS, ns,
+	nx.Nbody.Set1(nod(OAS, ns,
 		mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type.Elem()), ns,
-			Nod(OADD, Nod(OLEN, ns, nil), na))))
+			nod(OADD, nod(OLEN, ns, nil), na))))
 
 	l = append(l, nx)
 
 	nn := temp(Types[TINT])
-	l = append(l, Nod(OAS, nn, Nod(OLEN, ns, nil))) // n = len(s)
+	l = append(l, nod(OAS, nn, nod(OLEN, ns, nil))) // n = len(s)
 
-	nx = Nod(OSLICE, ns, nil) // ...s[:n+argc]
-	nx.SetSliceBounds(nil, Nod(OADD, nn, na), nil)
+	nx = nod(OSLICE, ns, nil) // ...s[:n+argc]
+	nx.SetSliceBounds(nil, nod(OADD, nn, na), nil)
 	nx.Etype = 1
-	l = append(l, Nod(OAS, ns, nx)) // s = s[:n+argc]
+	l = append(l, nod(OAS, ns, nx)) // s = s[:n+argc]
 
 	ls = n.List.Slice()[1:]
 	for i, n := range ls {
-		nx = Nod(OINDEX, ns, nn) // s[n] ...
+		nx = nod(OINDEX, ns, nn) // s[n] ...
 		nx.Bounded = true
-		l = append(l, Nod(OAS, nx, n)) // s[n] = arg
+		l = append(l, nod(OAS, nx, n)) // s[n] = arg
 		if i+1 < len(ls) {
-			l = append(l, Nod(OAS, nn, Nod(OADD, nn, Nodintconst(1)))) // n = n + 1
+			l = append(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))) // n = n + 1
 		}
 	}
 
@@ -3004,7 +2970,7 @@ func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
 			fn = syslook("slicecopy")
 		}
 		fn = substArgTypes(fn, n.Left.Type, n.Right.Type)
-		return mkcall1(fn, n.Type, init, n.Left, n.Right, Nodintconst(n.Left.Type.Elem().Width))
+		return mkcall1(fn, n.Type, init, n.Left, n.Right, nodintconst(n.Left.Type.Elem().Width))
 	}
 
 	n.Left = walkexpr(n.Left, init)
@@ -3012,22 +2978,22 @@ func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
 	nl := temp(n.Left.Type)
 	nr := temp(n.Right.Type)
 	var l []*Node
-	l = append(l, Nod(OAS, nl, n.Left))
-	l = append(l, Nod(OAS, nr, n.Right))
+	l = append(l, nod(OAS, nl, n.Left))
+	l = append(l, nod(OAS, nr, n.Right))
 
-	nfrm := Nod(OSPTR, nr, nil)
-	nto := Nod(OSPTR, nl, nil)
+	nfrm := nod(OSPTR, nr, nil)
+	nto := nod(OSPTR, nl, nil)
 
 	nlen := temp(Types[TINT])
 
 	// n = len(to)
-	l = append(l, Nod(OAS, nlen, Nod(OLEN, nl, nil)))
+	l = append(l, nod(OAS, nlen, nod(OLEN, nl, nil)))
 
 	// if n > len(frm) { n = len(frm) }
-	nif := Nod(OIF, nil, nil)
+	nif := nod(OIF, nil, nil)
 
-	nif.Left = Nod(OGT, nlen, Nod(OLEN, nr, nil))
-	nif.Nbody.Append(Nod(OAS, nlen, Nod(OLEN, nr, nil)))
+	nif.Left = nod(OGT, nlen, nod(OLEN, nr, nil))
+	nif.Nbody.Append(nod(OAS, nlen, nod(OLEN, nr, nil)))
 	l = append(l, nif)
 
 	// Call memmove.
@@ -3035,8 +3001,8 @@ func copyany(n *Node, init *Nodes, runtimecall bool) *Node {
 
 	fn = substArgTypes(fn, nl.Type.Elem(), nl.Type.Elem())
 	nwid := temp(Types[TUINTPTR])
-	l = append(l, Nod(OAS, nwid, conv(nlen, Types[TUINTPTR])))
-	nwid = Nod(OMUL, nwid, Nodintconst(nl.Type.Elem().Width))
+	l = append(l, nod(OAS, nwid, conv(nlen, Types[TUINTPTR])))
+	nwid = nod(OMUL, nwid, nodintconst(nl.Type.Elem().Width))
 	l = append(l, mkcall1(fn, nil, init, nto, nfrm, nwid))
 
 	typecheckslice(l, Etop)
@@ -3060,10 +3026,10 @@ func eqfor(t *Type, needsize *int) *Node {
 		sym := typesymprefix(".eq", t)
 		n := newname(sym)
 		n.Class = PFUNC
-		ntype := Nod(OTFUNC, nil, nil)
-		ntype.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
-		ntype.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
-		ntype.Rlist.Append(Nod(ODCLFIELD, nil, typenod(Types[TBOOL])))
+		ntype := nod(OTFUNC, nil, nil)
+		ntype.List.Append(nod(ODCLFIELD, nil, typenod(ptrto(t))))
+		ntype.List.Append(nod(ODCLFIELD, nil, typenod(ptrto(t))))
+		ntype.Rlist.Append(nod(ODCLFIELD, nil, typenod(Types[TBOOL])))
 		ntype = typecheck(ntype, Etype)
 		n.Type = ntype.Type
 		*needsize = 0
@@ -3078,14 +3044,9 @@ func eqfor(t *Type, needsize *int) *Node {
 func walkcompare(n *Node, init *Nodes) *Node {
 	// Given interface value l and concrete value r, rewrite
 	//   l == r
-	// to
-	//   x, ok := l.(type(r)); ok && x == r
-	// Handle != similarly.
-	// This avoids the allocation that would be required
-	// to convert r to l for comparison.
-	var l *Node
-
-	var r *Node
+	// into types-equal && data-equal.
+	// This is efficient, avoids allocations, and avoids runtime calls.
+	var l, r *Node
 	if n.Left.Type.IsInterface() && !n.Right.Type.IsInterface() {
 		l = n.Left
 		r = n.Right
@@ -3095,48 +3056,52 @@ func walkcompare(n *Node, init *Nodes) *Node {
 	}
 
 	if l != nil {
-		x := temp(r.Type)
-		if haspointers(r.Type) {
-			a := Nod(OAS, x, nil)
-			a = typecheck(a, Etop)
-			init.Append(a)
-		}
-		ok := temp(Types[TBOOL])
-
-		// l.(type(r))
-		a := Nod(ODOTTYPE, l, nil)
-
-		a.Type = r.Type
-
-		// x, ok := l.(type(r))
-		expr := Nod(OAS2, nil, nil)
-
-		expr.List.Append(x)
-		expr.List.Append(ok)
-		expr.Rlist.Append(a)
-		expr = typecheck(expr, Etop)
-		expr = walkexpr(expr, init)
-
-		if n.Op == OEQ {
-			r = Nod(OANDAND, ok, Nod(OEQ, x, r))
+		// Handle both == and !=.
+		eq := n.Op
+		var andor Op
+		if eq == OEQ {
+			andor = OANDAND
 		} else {
-			r = Nod(OOROR, Nod(ONOT, ok, nil), Nod(ONE, x, r))
-		}
-		init.Append(expr)
-		n = finishcompare(n, r, init)
+			andor = OOROR
+		}
+		// Check for types equal.
+		// For empty interface, this is:
+		//   l.tab == type(r)
+		// For non-empty interface, this is:
+		//   l.tab != nil && l.tab._type == type(r)
+		var eqtype *Node
+		tab := nod(OITAB, l, nil)
+		rtyp := typename(r.Type)
+		if l.Type.IsEmptyInterface() {
+			tab.Type = ptrto(Types[TUINT8])
+			tab.Typecheck = 1
+			eqtype = nod(eq, tab, rtyp)
+		} else {
+			nonnil := nod(brcom(eq), nodnil(), tab)
+			match := nod(eq, itabType(tab), rtyp)
+			eqtype = nod(andor, nonnil, match)
+		}
+		// Check for data equal.
+		eqdata := nod(eq, ifaceData(l, r.Type), r)
+		// Put it all together.
+		expr := nod(andor, eqtype, eqdata)
+		n = finishcompare(n, expr, init)
 		return n
 	}
 
 	// Must be comparison of array or struct.
 	// Otherwise back end handles it.
+	// While we're here, decide whether to
+	// inline or call an eq alg.
 	t := n.Left.Type
-
+	var inline bool
 	switch t.Etype {
 	default:
 		return n
-
-	case TARRAY, TSTRUCT:
-		break
+	case TARRAY:
+		inline = t.NumElem() <= 1 || (t.NumElem() <= 4 && issimple[t.Elem().Etype])
+	case TSTRUCT:
+		inline = t.NumFields() <= 4
 	}
 
 	cmpl := n.Left
@@ -3152,103 +3117,75 @@ func walkcompare(n *Node, init *Nodes) *Node {
 		Fatalf("arguments of comparison must be lvalues - %v %v", cmpl, cmpr)
 	}
 
-	l = temp(Ptrto(t))
-	a := Nod(OAS, l, Nod(OADDR, cmpl, nil))
-	a.Right.Etype = 1 // addr does not escape
-	a = typecheck(a, Etop)
-	init.Append(a)
-
-	r = temp(Ptrto(t))
-	a = Nod(OAS, r, Nod(OADDR, cmpr, nil))
-	a.Right.Etype = 1 // addr does not escape
-	a = typecheck(a, Etop)
-	init.Append(a)
+	// Chose not to inline. Call equality function directly.
+	if !inline {
+		// eq algs take pointers
+		pl := temp(ptrto(t))
+		al := nod(OAS, pl, nod(OADDR, cmpl, nil))
+		al.Right.Etype = 1 // addr does not escape
+		al = typecheck(al, Etop)
+		init.Append(al)
+
+		pr := temp(ptrto(t))
+		ar := nod(OAS, pr, nod(OADDR, cmpr, nil))
+		ar.Right.Etype = 1 // addr does not escape
+		ar = typecheck(ar, Etop)
+		init.Append(ar)
+
+		var needsize int
+		call := nod(OCALL, eqfor(t, &needsize), nil)
+		call.List.Append(pl)
+		call.List.Append(pr)
+		if needsize != 0 {
+			call.List.Append(nodintconst(t.Width))
+		}
+		res := call
+		if n.Op != OEQ {
+			res = nod(ONOT, res, nil)
+		}
+		n = finishcompare(n, res, init)
+		return n
+	}
 
-	var andor Op = OANDAND
+	// inline: build boolean expression comparing element by element
+	andor := OANDAND
 	if n.Op == ONE {
 		andor = OOROR
 	}
-
 	var expr *Node
-	if t.Etype == TARRAY && t.NumElem() <= 4 && issimple[t.Elem().Etype] {
-		// Four or fewer elements of a basic type.
-		// Unroll comparisons.
-		var li *Node
-		var ri *Node
-		for i := 0; int64(i) < t.NumElem(); i++ {
-			li = Nod(OINDEX, l, Nodintconst(int64(i)))
-			ri = Nod(OINDEX, r, Nodintconst(int64(i)))
-			a = Nod(n.Op, li, ri)
-			if expr == nil {
-				expr = a
-			} else {
-				expr = Nod(andor, expr, a)
-			}
-		}
-
+	compare := func(el, er *Node) {
+		a := nod(n.Op, el, er)
 		if expr == nil {
-			expr = Nodbool(n.Op == OEQ)
-		}
-		n = finishcompare(n, expr, init)
-		return n
-	}
-
-	if t.Etype == TARRAY {
-		// Zero- or single-element array, of any type.
-		switch t.NumElem() {
-		case 0:
-			n = finishcompare(n, Nodbool(n.Op == OEQ), init)
-			return n
-		case 1:
-			l0 := Nod(OINDEX, l, Nodintconst(0))
-			r0 := Nod(OINDEX, r, Nodintconst(0))
-			a := Nod(n.Op, l0, r0)
-			n = finishcompare(n, a, init)
-			return n
+			expr = a
+		} else {
+			expr = nod(andor, expr, a)
 		}
 	}
-
-	if t.IsStruct() && t.NumFields() <= 4 {
-		// Struct of four or fewer fields.
-		// Inline comparisons.
-		var li *Node
-		var ri *Node
-		for _, t1 := range t.Fields().Slice() {
-			if isblanksym(t1.Sym) {
+	cmpl = safeexpr(cmpl, init)
+	cmpr = safeexpr(cmpr, init)
+	if t.IsStruct() {
+		for _, f := range t.Fields().Slice() {
+			sym := f.Sym
+			if isblanksym(sym) {
 				continue
 			}
-			li = NodSym(OXDOT, l, t1.Sym)
-			ri = NodSym(OXDOT, r, t1.Sym)
-			a = Nod(n.Op, li, ri)
-			if expr == nil {
-				expr = a
-			} else {
-				expr = Nod(andor, expr, a)
-			}
+			compare(
+				nodSym(OXDOT, cmpl, sym),
+				nodSym(OXDOT, cmpr, sym),
+			)
 		}
-
-		if expr == nil {
-			expr = Nodbool(n.Op == OEQ)
+	} else {
+		for i := 0; int64(i) < t.NumElem(); i++ {
+			compare(
+				nod(OINDEX, cmpl, nodintconst(int64(i))),
+				nod(OINDEX, cmpr, nodintconst(int64(i))),
+			)
 		}
-		n = finishcompare(n, expr, init)
-		return n
 	}
-
-	// Chose not to inline. Call equality function directly.
-	var needsize int
-	call := Nod(OCALL, eqfor(t, &needsize), nil)
-
-	call.List.Append(l)
-	call.List.Append(r)
-	if needsize != 0 {
-		call.List.Append(Nodintconst(t.Width))
+	if expr == nil {
+		expr = nodbool(n.Op == OEQ)
 	}
-	r = call
-	if n.Op != OEQ {
-		r = Nod(ONOT, r, nil)
-	}
-
-	n = finishcompare(n, r, init)
+	n = finishcompare(n, expr, init)
 	return n
 }
 
@@ -3261,7 +3198,7 @@ func finishcompare(n, r *Node, init *Nodes) *Node {
 	nn = walkexpr(nn, init)
 	r = nn
 	if r.Type != n.Type {
-		r = Nod(OCONVNOP, r, nil)
+		r = nod(OCONVNOP, r, nil)
 		r.Type = n.Type
 		r.Typecheck = 1
 		nn = r
@@ -3303,7 +3240,7 @@ func samecheap(a *Node, b *Node) bool {
 // The result of walkrotate MUST be assigned back to n, e.g.
 // 	n.Left = walkrotate(n.Left)
 func walkrotate(n *Node) *Node {
-	if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM64, sys.PPC64) {
+	if Thearch.LinkArch.InFamily(sys.MIPS, sys.MIPS64, sys.PPC64) {
 		return n
 	}
 
@@ -3328,7 +3265,7 @@ func walkrotate(n *Node) *Node {
 		return n
 	}
 
-	if Smallintconst(l.Right) && Smallintconst(r.Right) {
+	if smallintconst(l.Right) && smallintconst(r.Right) {
 		sl := int(l.Right.Int64())
 		if sl >= 0 {
 			sr := int(r.Right.Int64())
@@ -3357,6 +3294,137 @@ func walkrotate(n *Node) *Node {
 	return n
 }
 
+// isIntOrdering reports whether n is a <, ≤, >, or ≥ ordering between integers.
+func (n *Node) isIntOrdering() bool {
+	switch n.Op {
+	case OLE, OLT, OGE, OGT:
+	default:
+		return false
+	}
+	return n.Left.Type.IsInteger() && n.Right.Type.IsInteger()
+}
+
+// walkinrange optimizes integer-in-range checks, such as 4 <= x && x < 10.
+// n must be an OANDAND or OOROR node.
+// The result of walkinrange MUST be assigned back to n, e.g.
+// 	n.Left = walkinrange(n.Left)
+func walkinrange(n *Node, init *Nodes) *Node {
+	// We are looking for something equivalent to a opl b OP b opr c, where:
+	// * a, b, and c have integer type
+	// * b is side-effect-free
+	// * opl and opr are each < or ≤
+	// * OP is &&
+	l := n.Left
+	r := n.Right
+	if !l.isIntOrdering() || !r.isIntOrdering() {
+		return n
+	}
+
+	// Find b, if it exists, and rename appropriately.
+	// Input is: l.Left l.Op l.Right ANDAND/OROR r.Left r.Op r.Right
+	// Output is: a opl b(==x) ANDAND/OROR b(==x) opr c
+	a, opl, b := l.Left, l.Op, l.Right
+	x, opr, c := r.Left, r.Op, r.Right
+	for i := 0; ; i++ {
+		if samesafeexpr(b, x) {
+			break
+		}
+		if i == 3 {
+			// Tried all permutations and couldn't find an appropriate b == x.
+			return n
+		}
+		if i&1 == 0 {
+			a, opl, b = b, brrev(opl), a
+		} else {
+			x, opr, c = c, brrev(opr), x
+		}
+	}
+
+	// If n.Op is ||, apply de Morgan.
+	// Negate the internal ops now; we'll negate the top level op at the end.
+	// Henceforth assume &&.
+	negateResult := n.Op == OOROR
+	if negateResult {
+		opl = brcom(opl)
+		opr = brcom(opr)
+	}
+
+	cmpdir := func(o Op) int {
+		switch o {
+		case OLE, OLT:
+			return -1
+		case OGE, OGT:
+			return +1
+		}
+		Fatalf("walkinrange cmpdir %v", o)
+		return 0
+	}
+	if cmpdir(opl) != cmpdir(opr) {
+		// Not a range check; something like b < a && b < c.
+		return n
+	}
+
+	switch opl {
+	case OGE, OGT:
+		// We have something like a > b && b ≥ c.
+		// Switch and reverse ops and rename constants,
+		// to make it look like a ≤ b && b < c.
+		a, c = c, a
+		opl, opr = brrev(opr), brrev(opl)
+	}
+
+	// We must ensure that c-a is non-negative.
+	// For now, require a and c to be constants.
+	// In the future, we could also support a == 0 and c == len/cap(...).
+	// Unfortunately, by this point, most len/cap expressions have been
+	// stored into temporary variables.
+	if !Isconst(a, CTINT) || !Isconst(c, CTINT) {
+		return n
+	}
+
+	if opl == OLT {
+		// We have a < b && ...
+		// We need a ≤ b && ... to safely use unsigned comparison tricks.
+		// If a is not the maximum constant for b's type,
+		// we can increment a and switch to ≤.
+		if a.Int64() >= maxintval[b.Type.Etype].Int64() {
+			return n
+		}
+		a = nodintconst(a.Int64() + 1)
+		opl = OLE
+	}
+
+	bound := c.Int64() - a.Int64()
+	if bound < 0 {
+		// Bad news. Something like 5 <= x && x < 3.
+		// Rare in practice, and we still need to generate side-effects,
+		// so just leave it alone.
+		return n
+	}
+
+	// We have a ≤ b && b < c (or a ≤ b && b ≤ c).
+	// This is equivalent to (a-a) ≤ (b-a) && (b-a) < (c-a),
+	// which is equivalent to 0 ≤ (b-a) && (b-a) < (c-a),
+	// which is equivalent to uint(b-a) < uint(c-a).
+	ut := b.Type.toUnsigned()
+	lhs := conv(nod(OSUB, b, a), ut)
+	rhs := nodintconst(bound)
+	if negateResult {
+		// Negate top level.
+		opr = brcom(opr)
+	}
+	cmp := nod(opr, lhs, rhs)
+	cmp.Lineno = n.Lineno
+	cmp = addinit(cmp, l.Ninit.Slice())
+	cmp = addinit(cmp, r.Ninit.Slice())
+	// Typecheck the AST rooted at cmp...
+	cmp = typecheck(cmp, Erv)
+	// ...but then reset cmp's type to match n's type.
+	cmp.Type = n.Type
+	cmp = walkexpr(cmp, init)
+	return cmp
+}
+
 // walkmul rewrites integer multiplication by powers of two as shifts.
 // The result of walkmul MUST be assigned back to n, e.g.
 // 	n.Left = walkmul(n.Left, init)
@@ -3415,11 +3483,11 @@ func walkmul(n *Node, init *Nodes) *Node {
 		goto ret
 	}
 
-	n = Nod(OLSH, nl, Nodintconst(int64(pow)))
+	n = nod(OLSH, nl, nodintconst(int64(pow)))
 
 ret:
 	if neg != 0 {
-		n = Nod(OMINUS, n, nil)
+		n = nod(OMINUS, n, nil)
 	}
 
 	n = typecheck(n, Erv)
@@ -3434,11 +3502,6 @@ ret:
 func walkdiv(n *Node, init *Nodes) *Node {
 	// if >= 0, nr is 1<<pow // 1 if nr is negative.
 
-	// TODO(minux)
-	if Thearch.LinkArch.InFamily(sys.MIPS64, sys.PPC64) {
-		return n
-	}
-
 	if n.Right.Op != OLITERAL {
 		return n
 	}
@@ -3475,10 +3538,10 @@ func walkdiv(n *Node, init *Nodes) *Node {
 
 		if nl.Type.IsSigned() {
 			m.Sd = nr.Int64()
-			Smagic(&m)
+			smagic(&m)
 		} else {
 			m.Ud = uint64(nr.Int64())
-			Umagic(&m)
+			umagic(&m)
 		}
 
 		if m.Bad != 0 {
@@ -3489,24 +3552,14 @@ func walkdiv(n *Node, init *Nodes) *Node {
 		// for modulo too.
 		if n.Op == OMOD {
 			// rewrite as A%B = A - (A/B*B).
-			n1 := Nod(ODIV, nl, nr)
+			n1 := nod(ODIV, nl, nr)
 
-			n2 := Nod(OMUL, n1, nr)
-			n = Nod(OSUB, nl, n2)
+			n2 := nod(OMUL, n1, nr)
+			n = nod(OSUB, nl, n2)
 			goto ret
 		}
 
-		// TODO(zhongwei) Test shows that TUINT8, TINT8, TUINT16 and TINT16's "quick division" method
-		// on current arm64 backend is slower than hardware div instruction on ARM64 due to unnecessary
-		// data movement between registers. It could be enabled when generated code is good enough.
-		if Thearch.LinkArch.Family == sys.ARM64 {
-			switch Simtype[nl.Type.Etype] {
-			case TUINT8, TINT8, TUINT16, TINT16:
-				return n
-			}
-		}
-
-		switch Simtype[nl.Type.Etype] {
+		switch simtype[nl.Type.Etype] {
 		default:
 			return n
 
@@ -3515,12 +3568,12 @@ func walkdiv(n *Node, init *Nodes) *Node {
 			var nc Node
 
 			Nodconst(&nc, nl.Type, int64(m.Um))
-			n1 := Nod(OHMUL, nl, &nc)
+			n1 := nod(OHMUL, nl, &nc)
 			n1 = typecheck(n1, Erv)
 			if m.Ua != 0 {
 				// Select a Go type with (at least) twice the width.
 				var twide *Type
-				switch Simtype[nl.Type.Etype] {
+				switch simtype[nl.Type.Etype] {
 				default:
 					return n
 
@@ -3539,19 +3592,19 @@ func walkdiv(n *Node, init *Nodes) *Node {
 
 				// add numerator (might overflow).
 				// n2 = (n1 + nl)
-				n2 := Nod(OADD, conv(n1, twide), conv(nl, twide))
+				n2 := nod(OADD, conv(n1, twide), conv(nl, twide))
 
 				// shift by m.s
 				var nc Node
 
 				Nodconst(&nc, Types[TUINT], int64(m.S))
-				n = conv(Nod(ORSH, n2, &nc), nl.Type)
+				n = conv(nod(ORSH, n2, &nc), nl.Type)
 			} else {
 				// n = n1 >> m.s
 				var nc Node
 
 				Nodconst(&nc, Types[TUINT], int64(m.S))
-				n = Nod(ORSH, n1, &nc)
+				n = nod(ORSH, n1, &nc)
 			}
 
 			// n1 = nl * magic >> w
@@ -3559,29 +3612,29 @@ func walkdiv(n *Node, init *Nodes) *Node {
 			var nc Node
 
 			Nodconst(&nc, nl.Type, m.Sm)
-			n1 := Nod(OHMUL, nl, &nc)
+			n1 := nod(OHMUL, nl, &nc)
 			n1 = typecheck(n1, Erv)
 			if m.Sm < 0 {
 				// add the numerator.
-				n1 = Nod(OADD, n1, nl)
+				n1 = nod(OADD, n1, nl)
 			}
 
 			// shift by m.s
 			var ns Node
 
 			Nodconst(&ns, Types[TUINT], int64(m.S))
-			n2 := conv(Nod(ORSH, n1, &ns), nl.Type)
+			n2 := conv(nod(ORSH, n1, &ns), nl.Type)
 
 			// add 1 iff n1 is negative.
 			var nneg Node
 
 			Nodconst(&nneg, Types[TUINT], int64(w)-1)
-			n3 := Nod(ORSH, nl, &nneg) // n4 = -1 iff n1 is negative.
-			n = Nod(OSUB, n2, n3)
+			n3 := nod(ORSH, nl, &nneg) // n4 = -1 iff n1 is negative.
+			n = nod(OSUB, n2, n3)
 
 			// apply sign.
 			if m.Sd < 0 {
-				n = Nod(OMINUS, n, nil)
+				n = nod(OMINUS, n, nil)
 			}
 		}
 
@@ -3611,31 +3664,31 @@ func walkdiv(n *Node, init *Nodes) *Node {
 				// nl & (2^pow-1) is (nl+1)%2^pow - 1.
 				var nc Node
 
-				Nodconst(&nc, Types[Simtype[TUINT]], int64(w)-1)
-				n1 := Nod(ORSH, nl, &nc) // n1 = -1 iff nl < 0.
+				Nodconst(&nc, Types[simtype[TUINT]], int64(w)-1)
+				n1 := nod(ORSH, nl, &nc) // n1 = -1 iff nl < 0.
 				if pow == 1 {
 					n1 = typecheck(n1, Erv)
 					n1 = cheapexpr(n1, init)
 
 					// n = (nl+ε)&1 -ε where ε=1 iff nl<0.
-					n2 := Nod(OSUB, nl, n1)
+					n2 := nod(OSUB, nl, n1)
 
 					var nc Node
 					Nodconst(&nc, nl.Type, 1)
-					n3 := Nod(OAND, n2, &nc)
-					n = Nod(OADD, n3, n1)
+					n3 := nod(OAND, n2, &nc)
+					n = nod(OADD, n3, n1)
 				} else {
 					// n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0.
 					var nc Node
 
 					Nodconst(&nc, nl.Type, (1<<uint(pow))-1)
-					n2 := Nod(OAND, n1, &nc) // n2 = 2^pow-1 iff nl<0.
+					n2 := nod(OAND, n1, &nc) // n2 = 2^pow-1 iff nl<0.
 					n2 = typecheck(n2, Erv)
 					n2 = cheapexpr(n2, init)
 
-					n3 := Nod(OADD, nl, n2)
-					n4 := Nod(OAND, n3, &nc)
-					n = Nod(OSUB, n4, n2)
+					n3 := nod(OADD, nl, n2)
+					n4 := nod(OAND, n3, &nc)
+					n = nod(OSUB, n4, n2)
 				}
 
 				break
@@ -3645,31 +3698,31 @@ func walkdiv(n *Node, init *Nodes) *Node {
 				// if nl < 0, we want to add 2^n-1 first.
 				var nc Node
 
-				Nodconst(&nc, Types[Simtype[TUINT]], int64(w)-1)
-				n1 := Nod(ORSH, nl, &nc) // n1 = -1 iff nl < 0.
+				Nodconst(&nc, Types[simtype[TUINT]], int64(w)-1)
+				n1 := nod(ORSH, nl, &nc) // n1 = -1 iff nl < 0.
 				if pow == 1 {
 					// nl+1 is nl-(-1)
-					n.Left = Nod(OSUB, nl, n1)
+					n.Left = nod(OSUB, nl, n1)
 				} else {
 					// Do a logical right right on -1 to keep pow bits.
 					var nc Node
 
-					Nodconst(&nc, Types[Simtype[TUINT]], int64(w)-int64(pow))
-					n2 := Nod(ORSH, conv(n1, tounsigned(nl.Type)), &nc)
-					n.Left = Nod(OADD, nl, conv(n2, nl.Type))
+					Nodconst(&nc, Types[simtype[TUINT]], int64(w)-int64(pow))
+					n2 := nod(ORSH, conv(n1, nl.Type.toUnsigned()), &nc)
+					n.Left = nod(OADD, nl, conv(n2, nl.Type))
 				}
 
 				// n = (nl + 2^pow-1) >> pow
 				n.Op = ORSH
 
 				var n2 Node
-				Nodconst(&n2, Types[Simtype[TUINT]], int64(pow))
+				Nodconst(&n2, Types[simtype[TUINT]], int64(pow))
 				n.Right = &n2
 				n.Typecheck = 0
 			}
 
 			if s != 0 {
-				n = Nod(OMINUS, n, nil)
+				n = nod(OMINUS, n, nil)
 			}
 			break
 		}
@@ -3684,7 +3737,7 @@ func walkdiv(n *Node, init *Nodes) *Node {
 			// n = nl >> pow
 			n.Op = ORSH
 
-			Nodconst(&nc, Types[Simtype[TUINT]], int64(pow))
+			Nodconst(&nc, Types[simtype[TUINT]], int64(pow))
 		}
 
 		n.Typecheck = 0
@@ -3708,7 +3761,7 @@ func bounded(n *Node, max int64) bool {
 	sign := n.Type.IsSigned()
 	bits := int32(8 * n.Type.Width)
 
-	if Smallintconst(n) {
+	if smallintconst(n) {
 		v := n.Int64()
 		return 0 <= v && v < max
 	}
@@ -3716,9 +3769,9 @@ func bounded(n *Node, max int64) bool {
 	switch n.Op {
 	case OAND:
 		v := int64(-1)
-		if Smallintconst(n.Left) {
+		if smallintconst(n.Left) {
 			v = n.Left.Int64()
-		} else if Smallintconst(n.Right) {
+		} else if smallintconst(n.Right) {
 			v = n.Right.Int64()
 		}
 
@@ -3727,7 +3780,7 @@ func bounded(n *Node, max int64) bool {
 		}
 
 	case OMOD:
-		if !sign && Smallintconst(n.Right) {
+		if !sign && smallintconst(n.Right) {
 			v := n.Right.Int64()
 			if 0 <= v && v <= max {
 				return true
@@ -3735,7 +3788,7 @@ func bounded(n *Node, max int64) bool {
 		}
 
 	case ODIV:
-		if !sign && Smallintconst(n.Right) {
+		if !sign && smallintconst(n.Right) {
 			v := n.Right.Int64()
 			for bits > 0 && v >= 2 {
 				bits--
@@ -3744,7 +3797,7 @@ func bounded(n *Node, max int64) bool {
 		}
 
 	case ORSH:
-		if !sign && Smallintconst(n.Right) {
+		if !sign && smallintconst(n.Right) {
 			v := n.Right.Int64()
 			if v > int64(bits) {
 				return true
@@ -3795,7 +3848,7 @@ func usemethod(n *Node) {
 			return
 		}
 	}
-	if Tconv(res0.Type, 0) != "reflect.Method" {
+	if res0.Type.String() != "reflect.Method" {
 		return
 	}
 
@@ -3837,10 +3890,10 @@ func usefield(n *Node) {
 		outer = outer.Elem()
 	}
 	if outer.Sym == nil {
-		Yyerror("tracked field must be in named struct type")
+		yyerror("tracked field must be in named struct type")
 	}
 	if !exportname(field.Sym.Name) {
-		Yyerror("tracked field must be exported (upper case)")
+		yyerror("tracked field must be exported (upper case)")
 	}
 
 	sym := tracksym(outer, field)
@@ -3892,6 +3945,7 @@ func candiscard(n *Node) bool {
 		OMAPLIT,
 		OSTRUCTLIT,
 		OARRAYLIT,
+		OSLICELIT,
 		OPTRLIT,
 		OCONV,
 		OCONVIFACE,
@@ -3904,6 +3958,7 @@ func candiscard(n *Node) bool {
 		OGT,
 		OGE,
 		OKEY,
+		OSTRUCTKEY,
 		OLEN,
 		OMUL,
 		OLSH,
@@ -3970,7 +4025,7 @@ func walkprintfunc(n *Node, init *Nodes) *Node {
 		init.AppendNodes(&n.Ninit)
 	}
 
-	t := Nod(OTFUNC, nil, nil)
+	t := nod(OTFUNC, nil, nil)
 	num := 0
 	var printargs []*Node
 	var a *Node
@@ -3978,15 +4033,15 @@ func walkprintfunc(n *Node, init *Nodes) *Node {
 	for _, n1 := range n.List.Slice() {
 		buf = fmt.Sprintf("a%d", num)
 		num++
-		a = Nod(ODCLFIELD, newname(Lookup(buf)), typenod(n1.Type))
+		a = nod(ODCLFIELD, newname(lookup(buf)), typenod(n1.Type))
 		t.List.Append(a)
 		printargs = append(printargs, a.Left)
 	}
 
-	fn := Nod(ODCLFUNC, nil, nil)
+	fn := nod(ODCLFUNC, nil, nil)
 	walkprintfunc_prgen++
 	buf = fmt.Sprintf("print·%d", walkprintfunc_prgen)
-	fn.Func.Nname = newname(Lookup(buf))
+	fn.Func.Nname = newname(lookup(buf))
 	fn.Func.Nname.Name.Defn = fn
 	fn.Func.Nname.Name.Param.Ntype = t
 	declare(fn.Func.Nname, PFUNC)
@@ -3995,7 +4050,7 @@ func walkprintfunc(n *Node, init *Nodes) *Node {
 	Curfn = nil
 	funchdr(fn)
 
-	a = Nod(n.Op, nil, nil)
+	a = nod(n.Op, nil, nil)
 	a.List.Set(printargs)
 	a = typecheck(a, Etop)
 	a = walkstmt(a)
@@ -4009,7 +4064,7 @@ func walkprintfunc(n *Node, init *Nodes) *Node {
 	xtop = append(xtop, fn)
 	Curfn = oldfn
 
-	a = Nod(OCALL, nil, nil)
+	a = nod(OCALL, nil, nil)
 	a.Left = fn.Func.Nname
 	a.List.Set(n.List.Slice())
 	a = typecheck(a, Etop)
diff --git a/src/cmd/compile/internal/mips/galign.go b/src/cmd/compile/internal/mips/galign.go
new file mode 100644
index 0000000..39f5d2b
--- /dev/null
+++ b/src/cmd/compile/internal/mips/galign.go
@@ -0,0 +1,26 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mips
+
+import (
+	"cmd/compile/internal/gc"
+	"cmd/compile/internal/ssa"
+	"cmd/internal/obj"
+	"cmd/internal/obj/mips"
+)
+
+func Init() {
+	gc.Thearch.LinkArch = &mips.Linkmips
+	if obj.GOARCH == "mipsle" {
+		gc.Thearch.LinkArch = &mips.Linkmipsle
+	}
+	gc.Thearch.REGSP = mips.REGSP
+	gc.Thearch.MAXWIDTH = (1 << 31) - 1
+	gc.Thearch.Defframe = defframe
+	gc.Thearch.Proginfo = proginfo
+	gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
+	gc.Thearch.SSAGenValue = ssaGenValue
+	gc.Thearch.SSAGenBlock = ssaGenBlock
+}
diff --git a/src/cmd/compile/internal/mips/ggen.go b/src/cmd/compile/internal/mips/ggen.go
new file mode 100644
index 0000000..ec540f8
--- /dev/null
+++ b/src/cmd/compile/internal/mips/ggen.go
@@ -0,0 +1,101 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mips
+
+import (
+	"cmd/compile/internal/gc"
+	"cmd/internal/obj"
+	"cmd/internal/obj/mips"
+)
+
+func defframe(ptxt *obj.Prog) {
+	// fill in argument size, stack size
+	ptxt.To.Type = obj.TYPE_TEXTSIZE
+
+	ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.ArgWidth(), int64(gc.Widthptr)))
+	frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg)))
+	ptxt.To.Offset = int64(frame)
+
+	// insert code to zero ambiguously live variables
+	// so that the garbage collector only sees initialized values
+	// when it looks for pointers.
+	p := ptxt
+
+	hi := int64(0)
+	lo := hi
+
+	// iterate through declarations - they are sorted in decreasing xoffset order.
+	for _, n := range gc.Curfn.Func.Dcl {
+		if !n.Name.Needzero {
+			continue
+		}
+		if n.Class != gc.PAUTO {
+			gc.Fatalf("needzero class %d", n.Class)
+		}
+		if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
+			gc.Fatalf("var %L has size %d offset %d", n, int(n.Type.Width), int(n.Xoffset))
+		}
+
+		if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) {
+			// merge with range we already have
+			lo = n.Xoffset
+
+			continue
+		}
+
+		// zero old range
+		p = zerorange(p, int64(frame), lo, hi)
+
+		// set new range
+		hi = n.Xoffset + n.Type.Width
+
+		lo = n.Xoffset
+	}
+
+	// zero final range
+	zerorange(p, int64(frame), lo, hi)
+}
+
+// TODO(mips): implement DUFFZERO
+func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
+
+	cnt := hi - lo
+	if cnt == 0 {
+		return p
+	}
+	if cnt < int64(4*gc.Widthptr) {
+		for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
+			p = gc.Appendpp(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, gc.Ctxt.FixedFrameSize()+frame+lo+i)
+		}
+	} else {
+		//fmt.Printf("zerorange frame:%v, lo: %v, hi:%v \n", frame ,lo, hi)
+		//	ADD 	$(FIXED_FRAME+frame+lo-4), SP, r1
+		//	ADD 	$cnt, r1, r2
+		// loop:
+		//	MOVW	R0, (Widthptr)r1
+		//	ADD 	$Widthptr, r1
+		//	BNE		r1, r2, loop
+		p = gc.Appendpp(p, mips.AADD, obj.TYPE_CONST, 0, gc.Ctxt.FixedFrameSize()+frame+lo-4, obj.TYPE_REG, mips.REGRT1, 0)
+		p.Reg = mips.REGSP
+		p = gc.Appendpp(p, mips.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
+		p.Reg = mips.REGRT1
+		p = gc.Appendpp(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGRT1, int64(gc.Widthptr))
+		p1 := p
+		p = gc.Appendpp(p, mips.AADD, obj.TYPE_CONST, 0, int64(gc.Widthptr), obj.TYPE_REG, mips.REGRT1, 0)
+		p = gc.Appendpp(p, mips.ABNE, obj.TYPE_REG, mips.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
+		p.Reg = mips.REGRT2
+		gc.Patch(p, p1)
+	}
+
+	return p
+}
+
+func ginsnop() {
+	p := gc.Prog(mips.ANOR)
+	p.From.Type = obj.TYPE_REG
+	p.From.Reg = mips.REG_R0
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = mips.REG_R0
+}
diff --git a/src/cmd/compile/internal/mips/prog.go b/src/cmd/compile/internal/mips/prog.go
new file mode 100644
index 0000000..32805f0
--- /dev/null
+++ b/src/cmd/compile/internal/mips/prog.go
@@ -0,0 +1,157 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mips
+
+import (
+	"cmd/compile/internal/gc"
+	"cmd/internal/obj"
+	"cmd/internal/obj/mips"
+)
+
+const (
+	LeftRdwr  uint32 = gc.LeftRead | gc.LeftWrite
+	RightRdwr uint32 = gc.RightRead | gc.RightWrite
+)
+
+// This table gives the basic information about instruction
+// generated by the compiler and processed in the optimizer.
+// See opt.h for bit definitions.
+//
+// Instructions not generated need not be listed.
+// As an exception to that rule, we typically write down all the
+// size variants of an operation even if we just use a subset.
+//
+// The table is formatted for 8-space tabs.
+var progtable = [mips.ALAST & obj.AMask]gc.ProgInfo{
+	obj.ATYPE:     {Flags: gc.Pseudo | gc.Skip},
+	obj.ATEXT:     {Flags: gc.Pseudo},
+	obj.AFUNCDATA: {Flags: gc.Pseudo},
+	obj.APCDATA:   {Flags: gc.Pseudo},
+	obj.AUNDEF:    {Flags: gc.Break},
+	obj.AUSEFIELD: {Flags: gc.OK},
+	obj.AVARDEF:   {Flags: gc.Pseudo | gc.RightWrite},
+	obj.AVARKILL:  {Flags: gc.Pseudo | gc.RightWrite},
+	obj.AVARLIVE:  {Flags: gc.Pseudo | gc.LeftRead},
+
+	// NOP is an internal no-op that also stands
+	// for USED and SET annotations, not the MIPS opcode.
+	obj.ANOP: {Flags: gc.LeftRead | gc.RightWrite},
+
+	// Integer
+	mips.AADD & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.AADDU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ASUB & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ASUBU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.AAND & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.AOR & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.AXOR & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ANOR & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.AMUL & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead},
+	mips.AMULU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead},
+	mips.ADIV & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead},
+	mips.ADIVU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead},
+	mips.ASLL & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ASRA & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ASRL & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ASGT & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ASGTU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+
+	mips.ACLZ & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
+	mips.ACLO & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
+
+	// Floating point.
+	mips.AADDF & obj.AMask:    {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.AADDD & obj.AMask:    {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ASUBF & obj.AMask:    {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ASUBD & obj.AMask:    {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.AMULF & obj.AMask:    {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.AMULD & obj.AMask:    {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ADIVF & obj.AMask:    {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.ADIVD & obj.AMask:    {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	mips.AABSF & obj.AMask:    {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite},
+	mips.AABSD & obj.AMask:    {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
+	mips.ANEGF & obj.AMask:    {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite},
+	mips.ANEGD & obj.AMask:    {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
+	mips.ACMPEQF & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | gc.RegRead},
+	mips.ACMPEQD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RegRead},
+	mips.ACMPGTF & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | gc.RegRead},
+	mips.ACMPGTD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RegRead},
+	mips.ACMPGEF & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | gc.RegRead},
+	mips.ACMPGED & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RegRead},
+	mips.AMOVFD & obj.AMask:   {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
+	mips.AMOVDF & obj.AMask:   {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv},
+	mips.AMOVFW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+	mips.AMOVWF & obj.AMask:   {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv},
+	mips.AMOVDW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+	mips.AMOVWD & obj.AMask:   {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
+	mips.ATRUNCFW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+	mips.ATRUNCDW & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Conv},
+
+	mips.ASQRTF & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite},
+	mips.ASQRTD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
+
+	// Moves
+	mips.AMOVB & obj.AMask:  {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	mips.AMOVBU & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	mips.AMOVH & obj.AMask:  {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	mips.AMOVHU & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	mips.AMOVW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	mips.AMOVF & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	mips.AMOVD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+
+	// Conditional moves
+	mips.ACMOVN & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | RightRdwr},
+	mips.ACMOVZ & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | RightRdwr},
+	mips.ACMOVT & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | RightRdwr},
+	mips.ACMOVF & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | RightRdwr},
+
+	// Conditional trap
+	mips.ATEQ & obj.AMask: {Flags: gc.SizeL | gc.RegRead | gc.RightRead},
+	mips.ATNE & obj.AMask: {Flags: gc.SizeL | gc.RegRead | gc.RightRead},
+
+	// Atomic
+	mips.ASYNC & obj.AMask: {Flags: gc.OK},
+	mips.ALL & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite},
+	mips.ASC & obj.AMask:   {Flags: gc.SizeL | LeftRdwr | gc.RightRead},
+
+	// Jumps
+	mips.AJMP & obj.AMask:  {Flags: gc.Jump | gc.Break},
+	mips.AJAL & obj.AMask:  {Flags: gc.Call},
+	mips.ABEQ & obj.AMask:  {Flags: gc.Cjmp},
+	mips.ABNE & obj.AMask:  {Flags: gc.Cjmp},
+	mips.ABGEZ & obj.AMask: {Flags: gc.Cjmp},
+	mips.ABLTZ & obj.AMask: {Flags: gc.Cjmp},
+	mips.ABGTZ & obj.AMask: {Flags: gc.Cjmp},
+	mips.ABLEZ & obj.AMask: {Flags: gc.Cjmp},
+	mips.ABFPF & obj.AMask: {Flags: gc.Cjmp},
+	mips.ABFPT & obj.AMask: {Flags: gc.Cjmp},
+	mips.ARET & obj.AMask:  {Flags: gc.Break},
+	obj.ADUFFZERO:          {Flags: gc.Call},
+	obj.ADUFFCOPY:          {Flags: gc.Call},
+}
+
+func proginfo(p *obj.Prog) gc.ProgInfo {
+	info := progtable[p.As&obj.AMask]
+
+	if info.Flags == 0 {
+		gc.Fatalf("proginfo: unknown instruction %v", p)
+	}
+
+	if (info.Flags&gc.RegRead != 0) && p.Reg == 0 {
+		info.Flags &^= gc.RegRead
+		info.Flags |= gc.RightRead
+	}
+
+	if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) {
+		info.Flags &^= gc.LeftRead
+		info.Flags |= gc.LeftAddr
+	}
+
+	if p.As == mips.AMUL && p.To.Reg != 0 {
+		info.Flags |= gc.RightWrite
+	}
+
+	return info
+}
diff --git a/src/cmd/compile/internal/mips/ssa.go b/src/cmd/compile/internal/mips/ssa.go
new file mode 100644
index 0000000..aef1f90
--- /dev/null
+++ b/src/cmd/compile/internal/mips/ssa.go
@@ -0,0 +1,907 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mips
+
+import (
+	"math"
+
+	"cmd/compile/internal/gc"
+	"cmd/compile/internal/ssa"
+	"cmd/internal/obj"
+	"cmd/internal/obj/mips"
+)
+
+// isFPreg returns whether r is an FP register
+func isFPreg(r int16) bool {
+	return mips.REG_F0 <= r && r <= mips.REG_F31
+}
+
+// isHILO returns whether r is HI or LO register
+func isHILO(r int16) bool {
+	return r == mips.REG_HI || r == mips.REG_LO
+}
+
+// loadByType returns the load instruction of the given type.
+func loadByType(t ssa.Type, r int16) obj.As {
+	if isFPreg(r) {
+		if t.Size() == 4 { // float32 or int32
+			return mips.AMOVF
+		} else { // float64 or int64
+			return mips.AMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			if t.IsSigned() {
+				return mips.AMOVB
+			} else {
+				return mips.AMOVBU
+			}
+		case 2:
+			if t.IsSigned() {
+				return mips.AMOVH
+			} else {
+				return mips.AMOVHU
+			}
+		case 4:
+			return mips.AMOVW
+		}
+	}
+	panic("bad load type")
+}
+
+// storeByType returns the store instruction of the given type.
+func storeByType(t ssa.Type, r int16) obj.As {
+	if isFPreg(r) {
+		if t.Size() == 4 { // float32 or int32
+			return mips.AMOVF
+		} else { // float64 or int64
+			return mips.AMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			return mips.AMOVB
+		case 2:
+			return mips.AMOVH
+		case 4:
+			return mips.AMOVW
+		}
+	}
+	panic("bad store type")
+}
+
+func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+	s.SetLineno(v.Line)
+	switch v.Op {
+	case ssa.OpInitMem:
+		// memory arg needs no code
+	case ssa.OpArg:
+		// input args need no code
+	case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
+		// nothing to do
+	case ssa.OpSelect0, ssa.OpSelect1:
+		// nothing to do
+	case ssa.OpCopy, ssa.OpMIPSMOVWconvert, ssa.OpMIPSMOVWreg:
+		t := v.Type
+		if t.IsMemory() {
+			return
+		}
+		x := v.Args[0].Reg()
+		y := v.Reg()
+		if x == y {
+			return
+		}
+		as := mips.AMOVW
+		if isFPreg(x) && isFPreg(y) {
+			as = mips.AMOVF
+			if t.Size() == 8 {
+				as = mips.AMOVD
+			}
+		}
+
+		p := gc.Prog(as)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = y
+		if isHILO(x) && isHILO(y) || isHILO(x) && isFPreg(y) || isFPreg(x) && isHILO(y) {
+			// cannot move between special registers, use TMP as intermediate
+			p.To.Reg = mips.REGTMP
+			p = gc.Prog(mips.AMOVW)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = mips.REGTMP
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = y
+		}
+	case ssa.OpMIPSMOVWnop:
+		if v.Reg() != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		// nothing to do
+	case ssa.OpLoadReg:
+		if v.Type.IsFlags() {
+			v.Fatalf("load flags not implemented: %v", v.LongString())
+			return
+		}
+		r := v.Reg()
+		p := gc.Prog(loadByType(v.Type, r))
+		gc.AddrAuto(&p.From, v.Args[0])
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+		if isHILO(r) {
+			// cannot directly load, load to TMP and move
+			p.To.Reg = mips.REGTMP
+			p = gc.Prog(mips.AMOVW)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = mips.REGTMP
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = r
+		}
+	case ssa.OpStoreReg:
+		if v.Type.IsFlags() {
+			v.Fatalf("store flags not implemented: %v", v.LongString())
+			return
+		}
+		r := v.Args[0].Reg()
+		if isHILO(r) {
+			// cannot directly store, move to TMP and store
+			p := gc.Prog(mips.AMOVW)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = r
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = mips.REGTMP
+			r = mips.REGTMP
+		}
+		p := gc.Prog(storeByType(v.Type, r))
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r
+		gc.AddrAuto(&p.To, v)
+	case ssa.OpMIPSADD,
+		ssa.OpMIPSSUB,
+		ssa.OpMIPSAND,
+		ssa.OpMIPSOR,
+		ssa.OpMIPSXOR,
+		ssa.OpMIPSNOR,
+		ssa.OpMIPSSLL,
+		ssa.OpMIPSSRL,
+		ssa.OpMIPSSRA,
+		ssa.OpMIPSADDF,
+		ssa.OpMIPSADDD,
+		ssa.OpMIPSSUBF,
+		ssa.OpMIPSSUBD,
+		ssa.OpMIPSMULF,
+		ssa.OpMIPSMULD,
+		ssa.OpMIPSDIVF,
+		ssa.OpMIPSDIVD,
+		ssa.OpMIPSMUL:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPSSGT,
+		ssa.OpMIPSSGTU:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPSSGTzero,
+		ssa.OpMIPSSGTUzero:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.Reg = mips.REGZERO
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPSADDconst,
+		ssa.OpMIPSSUBconst,
+		ssa.OpMIPSANDconst,
+		ssa.OpMIPSORconst,
+		ssa.OpMIPSXORconst,
+		ssa.OpMIPSNORconst,
+		ssa.OpMIPSSLLconst,
+		ssa.OpMIPSSRLconst,
+		ssa.OpMIPSSRAconst,
+		ssa.OpMIPSSGTconst,
+		ssa.OpMIPSSGTUconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPSMULT,
+		ssa.OpMIPSMULTU,
+		ssa.OpMIPSDIV,
+		ssa.OpMIPSDIVU:
+		// result in hi,lo
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.Reg = v.Args[0].Reg()
+	case ssa.OpMIPSMOVWconst:
+		r := v.Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+		if isFPreg(r) || isHILO(r) {
+			// cannot move into FP or special registers, use TMP as intermediate
+			p.To.Reg = mips.REGTMP
+			p = gc.Prog(mips.AMOVW)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = mips.REGTMP
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = r
+		}
+	case ssa.OpMIPSMOVFconst,
+		ssa.OpMIPSMOVDconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_FCONST
+		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPSCMOVZ:
+		if v.Reg() != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[2].Reg()
+		p.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPSCMOVZzero:
+		if v.Reg() != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.Reg = mips.REGZERO
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPSCMPEQF,
+		ssa.OpMIPSCMPEQD,
+		ssa.OpMIPSCMPGEF,
+		ssa.OpMIPSCMPGED,
+		ssa.OpMIPSCMPGTF,
+		ssa.OpMIPSCMPGTD:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.Reg = v.Args[1].Reg()
+	case ssa.OpMIPSMOVWaddr:
+		p := gc.Prog(mips.AMOVW)
+		p.From.Type = obj.TYPE_ADDR
+		var wantreg string
+		// MOVW $sym+off(base), R
+		// the assembler expands it as the following:
+		// - base is SP: add constant offset to SP (R29)
+		//               when constant is large, tmp register (R23) may be used
+		// - base is SB: load external address with relocation
+		switch v.Aux.(type) {
+		default:
+			v.Fatalf("aux is of unknown type %T", v.Aux)
+		case *ssa.ExternSymbol:
+			wantreg = "SB"
+			gc.AddAux(&p.From, v)
+		case *ssa.ArgSymbol, *ssa.AutoSymbol:
+			wantreg = "SP"
+			gc.AddAux(&p.From, v)
+		case nil:
+			// No sym, just MOVW $off(SP), R
+			wantreg = "SP"
+			p.From.Reg = mips.REGSP
+			p.From.Offset = v.AuxInt
+		}
+		if reg := v.Args[0].RegName(); reg != wantreg {
+			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
+		}
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPSMOVBload,
+		ssa.OpMIPSMOVBUload,
+		ssa.OpMIPSMOVHload,
+		ssa.OpMIPSMOVHUload,
+		ssa.OpMIPSMOVWload,
+		ssa.OpMIPSMOVFload,
+		ssa.OpMIPSMOVDload:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPSMOVBstore,
+		ssa.OpMIPSMOVHstore,
+		ssa.OpMIPSMOVWstore,
+		ssa.OpMIPSMOVFstore,
+		ssa.OpMIPSMOVDstore:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpMIPSMOVBstorezero,
+		ssa.OpMIPSMOVHstorezero,
+		ssa.OpMIPSMOVWstorezero:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = mips.REGZERO
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpMIPSMOVBreg,
+		ssa.OpMIPSMOVBUreg,
+		ssa.OpMIPSMOVHreg,
+		ssa.OpMIPSMOVHUreg:
+		a := v.Args[0]
+		for a.Op == ssa.OpCopy || a.Op == ssa.OpMIPSMOVWreg || a.Op == ssa.OpMIPSMOVWnop {
+			a = a.Args[0]
+		}
+		if a.Op == ssa.OpLoadReg {
+			t := a.Type
+			switch {
+			case v.Op == ssa.OpMIPSMOVBreg && t.Size() == 1 && t.IsSigned(),
+				v.Op == ssa.OpMIPSMOVBUreg && t.Size() == 1 && !t.IsSigned(),
+				v.Op == ssa.OpMIPSMOVHreg && t.Size() == 2 && t.IsSigned(),
+				v.Op == ssa.OpMIPSMOVHUreg && t.Size() == 2 && !t.IsSigned():
+				// arg is a proper-typed load, already zero/sign-extended, don't extend again
+				if v.Reg() == v.Args[0].Reg() {
+					return
+				}
+				p := gc.Prog(mips.AMOVW)
+				p.From.Type = obj.TYPE_REG
+				p.From.Reg = v.Args[0].Reg()
+				p.To.Type = obj.TYPE_REG
+				p.To.Reg = v.Reg()
+				return
+			default:
+			}
+		}
+		fallthrough
+	case ssa.OpMIPSMOVWF,
+		ssa.OpMIPSMOVWD,
+		ssa.OpMIPSTRUNCFW,
+		ssa.OpMIPSTRUNCDW,
+		ssa.OpMIPSMOVFD,
+		ssa.OpMIPSMOVDF,
+		ssa.OpMIPSNEGF,
+		ssa.OpMIPSNEGD,
+		ssa.OpMIPSSQRTD,
+		ssa.OpMIPSCLZ:
+		p := gc.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.OpMIPSNEG:
+		// SUB from REGZERO
+		p := gc.Prog(mips.ASUBU)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.Reg = mips.REGZERO
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPSLoweredZero:
+		// SUBU	$4, R1
+		// MOVW	R0, 4(R1)
+		// ADDU	$4, R1
+		// BNE	Rarg1, R1, -2(PC)
+		// arg1 is the address of the last element to zero
+		var sz int64
+		var mov obj.As
+		switch {
+		case v.AuxInt%4 == 0:
+			sz = 4
+			mov = mips.AMOVW
+		case v.AuxInt%2 == 0:
+			sz = 2
+			mov = mips.AMOVH
+		default:
+			sz = 1
+			mov = mips.AMOVB
+		}
+		p := gc.Prog(mips.ASUBU)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = sz
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = mips.REG_R1
+		p2 := gc.Prog(mov)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = mips.REGZERO
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = mips.REG_R1
+		p2.To.Offset = sz
+		p3 := gc.Prog(mips.AADDU)
+		p3.From.Type = obj.TYPE_CONST
+		p3.From.Offset = sz
+		p3.To.Type = obj.TYPE_REG
+		p3.To.Reg = mips.REG_R1
+		p4 := gc.Prog(mips.ABNE)
+		p4.From.Type = obj.TYPE_REG
+		p4.From.Reg = v.Args[1].Reg()
+		p4.Reg = mips.REG_R1
+		p4.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p4, p2)
+	case ssa.OpMIPSLoweredMove:
+		// SUBU	$4, R1
+		// MOVW	4(R1), Rtmp
+		// MOVW	Rtmp, (R2)
+		// ADDU	$4, R1
+		// ADDU	$4, R2
+		// BNE	Rarg2, R1, -4(PC)
+		// arg2 is the address of the last element of src
+		var sz int64
+		var mov obj.As
+		switch {
+		case v.AuxInt%4 == 0:
+			sz = 4
+			mov = mips.AMOVW
+		case v.AuxInt%2 == 0:
+			sz = 2
+			mov = mips.AMOVH
+		default:
+			sz = 1
+			mov = mips.AMOVB
+		}
+		p := gc.Prog(mips.ASUBU)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = sz
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = mips.REG_R1
+		p2 := gc.Prog(mov)
+		p2.From.Type = obj.TYPE_MEM
+		p2.From.Reg = mips.REG_R1
+		p2.From.Offset = sz
+		p2.To.Type = obj.TYPE_REG
+		p2.To.Reg = mips.REGTMP
+		p3 := gc.Prog(mov)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = mips.REGTMP
+		p3.To.Type = obj.TYPE_MEM
+		p3.To.Reg = mips.REG_R2
+		p4 := gc.Prog(mips.AADDU)
+		p4.From.Type = obj.TYPE_CONST
+		p4.From.Offset = sz
+		p4.To.Type = obj.TYPE_REG
+		p4.To.Reg = mips.REG_R1
+		p5 := gc.Prog(mips.AADDU)
+		p5.From.Type = obj.TYPE_CONST
+		p5.From.Offset = sz
+		p5.To.Type = obj.TYPE_REG
+		p5.To.Reg = mips.REG_R2
+		p6 := gc.Prog(mips.ABNE)
+		p6.From.Type = obj.TYPE_REG
+		p6.From.Reg = v.Args[2].Reg()
+		p6.Reg = mips.REG_R1
+		p6.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p6, p2)
+	case ssa.OpMIPSCALLstatic:
+		if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
+			// Deferred calls will appear to be returning to
+			// the CALL deferreturn(SB) that we are about to emit.
+			// However, the stack trace code will show the line
+			// of the instruction byte before the return PC.
+			// To avoid that being an unrelated instruction,
+			// insert an actual hardware NOP that will have the right line number.
+			// This is different from obj.ANOP, which is a virtual no-op
+			// that doesn't make it into the instruction stream.
+			ginsnop()
+		}
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym))
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpMIPSCALLclosure:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Offset = 0
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpMIPSCALLdefer:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Deferproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpMIPSCALLgo:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Newproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpMIPSCALLinter:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Offset = 0
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpMIPSLoweredAtomicLoad:
+		gc.Prog(mips.ASYNC)
+
+		p := gc.Prog(mips.AMOVW)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg0()
+
+		gc.Prog(mips.ASYNC)
+	case ssa.OpMIPSLoweredAtomicStore:
+		gc.Prog(mips.ASYNC)
+
+		p := gc.Prog(mips.AMOVW)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+
+		gc.Prog(mips.ASYNC)
+	case ssa.OpMIPSLoweredAtomicStorezero:
+		gc.Prog(mips.ASYNC)
+
+		p := gc.Prog(mips.AMOVW)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = mips.REGZERO
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+
+		gc.Prog(mips.ASYNC)
+	case ssa.OpMIPSLoweredAtomicExchange:
+		// SYNC
+		// MOVW Rarg1, Rtmp
+		// LL	(Rarg0), Rout
+		// SC	Rtmp, (Rarg0)
+		// BEQ	Rtmp, -3(PC)
+		// SYNC
+		gc.Prog(mips.ASYNC)
+
+		p := gc.Prog(mips.AMOVW)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = mips.REGTMP
+
+		p1 := gc.Prog(mips.ALL)
+		p1.From.Type = obj.TYPE_MEM
+		p1.From.Reg = v.Args[0].Reg()
+		p1.To.Type = obj.TYPE_REG
+		p1.To.Reg = v.Reg0()
+
+		p2 := gc.Prog(mips.ASC)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = mips.REGTMP
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = v.Args[0].Reg()
+
+		p3 := gc.Prog(mips.ABEQ)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = mips.REGTMP
+		p3.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p3, p)
+
+		gc.Prog(mips.ASYNC)
+	case ssa.OpMIPSLoweredAtomicAdd:
+		// SYNC
+		// LL	(Rarg0), Rout
+		// ADDU Rarg1, Rout, Rtmp
+		// SC	Rtmp, (Rarg0)
+		// BEQ	Rtmp, -3(PC)
+		// SYNC
+		// ADDU Rarg1, Rout
+		gc.Prog(mips.ASYNC)
+
+		p := gc.Prog(mips.ALL)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg0()
+
+		p1 := gc.Prog(mips.AADDU)
+		p1.From.Type = obj.TYPE_REG
+		p1.From.Reg = v.Args[1].Reg()
+		p1.Reg = v.Reg0()
+		p1.To.Type = obj.TYPE_REG
+		p1.To.Reg = mips.REGTMP
+
+		p2 := gc.Prog(mips.ASC)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = mips.REGTMP
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = v.Args[0].Reg()
+
+		p3 := gc.Prog(mips.ABEQ)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = mips.REGTMP
+		p3.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p3, p)
+
+		gc.Prog(mips.ASYNC)
+
+		p4 := gc.Prog(mips.AADDU)
+		p4.From.Type = obj.TYPE_REG
+		p4.From.Reg = v.Args[1].Reg()
+		p4.Reg = v.Reg0()
+		p4.To.Type = obj.TYPE_REG
+		p4.To.Reg = v.Reg0()
+
+	case ssa.OpMIPSLoweredAtomicAddconst:
+		// SYNC
+		// LL	(Rarg0), Rout
+		// ADDU $auxInt, Rout, Rtmp
+		// SC	Rtmp, (Rarg0)
+		// BEQ	Rtmp, -3(PC)
+		// SYNC
+		// ADDU $auxInt, Rout
+		gc.Prog(mips.ASYNC)
+
+		p := gc.Prog(mips.ALL)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg0()
+
+		p1 := gc.Prog(mips.AADDU)
+		p1.From.Type = obj.TYPE_CONST
+		p1.From.Offset = v.AuxInt
+		p1.Reg = v.Reg0()
+		p1.To.Type = obj.TYPE_REG
+		p1.To.Reg = mips.REGTMP
+
+		p2 := gc.Prog(mips.ASC)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = mips.REGTMP
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = v.Args[0].Reg()
+
+		p3 := gc.Prog(mips.ABEQ)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = mips.REGTMP
+		p3.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p3, p)
+
+		gc.Prog(mips.ASYNC)
+
+		p4 := gc.Prog(mips.AADDU)
+		p4.From.Type = obj.TYPE_CONST
+		p4.From.Offset = v.AuxInt
+		p4.Reg = v.Reg0()
+		p4.To.Type = obj.TYPE_REG
+		p4.To.Reg = v.Reg0()
+
+	case ssa.OpMIPSLoweredAtomicAnd,
+		ssa.OpMIPSLoweredAtomicOr:
+		// SYNC
+		// LL	(Rarg0), Rtmp
+		// AND/OR	Rarg1, Rtmp
+		// SC	Rtmp, (Rarg0)
+		// BEQ	Rtmp, -3(PC)
+		// SYNC
+		gc.Prog(mips.ASYNC)
+
+		p := gc.Prog(mips.ALL)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = mips.REGTMP
+
+		p1 := gc.Prog(v.Op.Asm())
+		p1.From.Type = obj.TYPE_REG
+		p1.From.Reg = v.Args[1].Reg()
+		p1.Reg = mips.REGTMP
+		p1.To.Type = obj.TYPE_REG
+		p1.To.Reg = mips.REGTMP
+
+		p2 := gc.Prog(mips.ASC)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = mips.REGTMP
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = v.Args[0].Reg()
+
+		p3 := gc.Prog(mips.ABEQ)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = mips.REGTMP
+		p3.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p3, p)
+
+		gc.Prog(mips.ASYNC)
+
+	case ssa.OpMIPSLoweredAtomicCas:
+		// MOVW $0, Rout
+		// SYNC
+		// LL	(Rarg0), Rtmp
+		// BNE	Rtmp, Rarg1, 4(PC)
+		// MOVW Rarg2, Rout
+		// SC	Rout, (Rarg0)
+		// BEQ	Rout, -4(PC)
+		// SYNC
+		p := gc.Prog(mips.AMOVW)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = mips.REGZERO
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg0()
+
+		gc.Prog(mips.ASYNC)
+
+		p1 := gc.Prog(mips.ALL)
+		p1.From.Type = obj.TYPE_MEM
+		p1.From.Reg = v.Args[0].Reg()
+		p1.To.Type = obj.TYPE_REG
+		p1.To.Reg = mips.REGTMP
+
+		p2 := gc.Prog(mips.ABNE)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = v.Args[1].Reg()
+		p2.Reg = mips.REGTMP
+		p2.To.Type = obj.TYPE_BRANCH
+
+		p3 := gc.Prog(mips.AMOVW)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = v.Args[2].Reg()
+		p3.To.Type = obj.TYPE_REG
+		p3.To.Reg = v.Reg0()
+
+		p4 := gc.Prog(mips.ASC)
+		p4.From.Type = obj.TYPE_REG
+		p4.From.Reg = v.Reg0()
+		p4.To.Type = obj.TYPE_MEM
+		p4.To.Reg = v.Args[0].Reg()
+
+		p5 := gc.Prog(mips.ABEQ)
+		p5.From.Type = obj.TYPE_REG
+		p5.From.Reg = v.Reg0()
+		p5.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p5, p1)
+
+		gc.Prog(mips.ASYNC)
+
+		p6 := gc.Prog(obj.ANOP)
+		gc.Patch(p2, p6)
+
+	case ssa.OpVarDef:
+		gc.Gvardef(v.Aux.(*gc.Node))
+	case ssa.OpVarKill:
+		gc.Gvarkill(v.Aux.(*gc.Node))
+	case ssa.OpVarLive:
+		gc.Gvarlive(v.Aux.(*gc.Node))
+	case ssa.OpKeepAlive:
+		gc.KeepAlive(v)
+	case ssa.OpPhi:
+		gc.CheckLoweredPhi(v)
+	case ssa.OpMIPSLoweredNilCheck:
+		// Issue a load which will fault if arg is nil.
+		p := gc.Prog(mips.AMOVB)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = mips.REGTMP
+		if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
+			gc.Warnl(v.Line, "generated nil check")
+		}
+	case ssa.OpMIPSFPFlagTrue,
+		ssa.OpMIPSFPFlagFalse:
+		// MOVW		$1, r
+		// CMOVF	R0, r
+
+		cmov := mips.ACMOVF
+		if v.Op == ssa.OpMIPSFPFlagFalse {
+			cmov = mips.ACMOVT
+		}
+		p := gc.Prog(mips.AMOVW)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = 1
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+		p1 := gc.Prog(cmov)
+		p1.From.Type = obj.TYPE_REG
+		p1.From.Reg = mips.REGZERO
+		p1.To.Type = obj.TYPE_REG
+		p1.To.Reg = v.Reg()
+
+	case ssa.OpMIPSLoweredGetClosurePtr:
+		// Closure pointer is R22 (mips.REGCTXT).
+		gc.CheckLoweredGetClosurePtr(v)
+	default:
+		v.Fatalf("genValue not implemented: %s", v.LongString())
+	}
+}
+
+var blockJump = map[ssa.BlockKind]struct {
+	asm, invasm obj.As
+}{
+	ssa.BlockMIPSEQ:  {mips.ABEQ, mips.ABNE},
+	ssa.BlockMIPSNE:  {mips.ABNE, mips.ABEQ},
+	ssa.BlockMIPSLTZ: {mips.ABLTZ, mips.ABGEZ},
+	ssa.BlockMIPSGEZ: {mips.ABGEZ, mips.ABLTZ},
+	ssa.BlockMIPSLEZ: {mips.ABLEZ, mips.ABGTZ},
+	ssa.BlockMIPSGTZ: {mips.ABGTZ, mips.ABLEZ},
+	ssa.BlockMIPSFPT: {mips.ABFPT, mips.ABFPF},
+	ssa.BlockMIPSFPF: {mips.ABFPF, mips.ABFPT},
+}
+
+func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+	s.SetLineno(b.Line)
+
+	switch b.Kind {
+	case ssa.BlockPlain:
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+	case ssa.BlockDefer:
+		// defer returns in R1:
+		// 0 if we should continue executing
+		// 1 if we should jump to deferreturn call
+		p := gc.Prog(mips.ABNE)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = mips.REGZERO
+		p.Reg = mips.REG_R1
+		p.To.Type = obj.TYPE_BRANCH
+		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+	case ssa.BlockExit:
+		gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
+	case ssa.BlockRet:
+		gc.Prog(obj.ARET)
+	case ssa.BlockRetJmp:
+		p := gc.Prog(obj.ARET)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
+	case ssa.BlockMIPSEQ, ssa.BlockMIPSNE,
+		ssa.BlockMIPSLTZ, ssa.BlockMIPSGEZ,
+		ssa.BlockMIPSLEZ, ssa.BlockMIPSGTZ,
+		ssa.BlockMIPSFPT, ssa.BlockMIPSFPF:
+		jmp := blockJump[b.Kind]
+		var p *obj.Prog
+		switch next {
+		case b.Succs[0].Block():
+			p = gc.Prog(jmp.invasm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		case b.Succs[1].Block():
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		default:
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+			q := gc.Prog(obj.AJMP)
+			q.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
+		}
+		if !b.Control.Type.IsFlags() {
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = b.Control.Reg()
+		}
+	default:
+		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
+	}
+}
diff --git a/src/cmd/compile/internal/mips64/cgen.go b/src/cmd/compile/internal/mips64/cgen.go
deleted file mode 100644
index 998afea..0000000
--- a/src/cmd/compile/internal/mips64/cgen.go
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package mips64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/mips"
-)
-
-func blockcopy(n, res *gc.Node, osrc, odst, w int64) {
-	// determine alignment.
-	// want to avoid unaligned access, so have to use
-	// smaller operations for less aligned types.
-	// for example moving [4]byte must use 4 MOVB not 1 MOVW.
-	align := int(n.Type.Align)
-
-	var op obj.As
-	switch align {
-	default:
-		gc.Fatalf("sgen: invalid alignment %d for %v", align, n.Type)
-
-	case 1:
-		op = mips.AMOVB
-
-	case 2:
-		op = mips.AMOVH
-
-	case 4:
-		op = mips.AMOVW
-
-	case 8:
-		op = mips.AMOVV
-	}
-
-	if w%int64(align) != 0 {
-		gc.Fatalf("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type)
-	}
-	c := int32(w / int64(align))
-
-	// if we are copying forward on the stack and
-	// the src and dst overlap, then reverse direction
-	dir := align
-
-	if osrc < odst && odst < osrc+w {
-		dir = -dir
-	}
-
-	var dst gc.Node
-	var src gc.Node
-	if n.Ullman >= res.Ullman {
-		gc.Agenr(n, &dst, res) // temporarily use dst
-		gc.Regalloc(&src, gc.Types[gc.Tptr], nil)
-		gins(mips.AMOVV, &dst, &src)
-		if res.Op == gc.ONAME {
-			gc.Gvardef(res)
-		}
-		gc.Agen(res, &dst)
-	} else {
-		if res.Op == gc.ONAME {
-			gc.Gvardef(res)
-		}
-		gc.Agenr(res, &dst, res)
-		gc.Agenr(n, &src, nil)
-	}
-
-	var tmp gc.Node
-	gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil)
-
-	// set up end marker
-	var nend gc.Node
-
-	// move src and dest to the end of block if necessary
-	if dir < 0 {
-		if c >= 4 {
-			gc.Regalloc(&nend, gc.Types[gc.Tptr], nil)
-			gins(mips.AMOVV, &src, &nend)
-		}
-
-		p := gins(mips.AADDV, nil, &src)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-
-		p = gins(mips.AADDV, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-	} else {
-		p := gins(mips.AADDV, nil, &src)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = int64(-dir)
-
-		p = gins(mips.AADDV, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = int64(-dir)
-
-		if c >= 4 {
-			gc.Regalloc(&nend, gc.Types[gc.Tptr], nil)
-			p := gins(mips.AMOVV, &src, &nend)
-			p.From.Type = obj.TYPE_ADDR
-			p.From.Offset = w
-		}
-	}
-
-	// move
-	// TODO: enable duffcopy for larger copies.
-	if c >= 4 {
-		p := gins(op, &src, &tmp)
-		p.From.Type = obj.TYPE_MEM
-		p.From.Offset = int64(dir)
-		ploop := p
-
-		p = gins(mips.AADDV, nil, &src)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = int64(dir)
-
-		p = gins(op, &tmp, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = int64(dir)
-
-		p = gins(mips.AADDV, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = int64(dir)
-
-		gc.Patch(ginsbranch(mips.ABNE, nil, &src, &nend, 0), ploop)
-		gc.Regfree(&nend)
-	} else {
-		// TODO: Instead of generating ADDV $-8,R8; ADDV
-		// $-8,R7; n*(MOVV 8(R8),R9; ADDV $8,R8; MOVV R9,8(R7);
-		// ADDV $8,R7;) just generate the offsets directly and
-		// eliminate the ADDs. That will produce shorter, more
-		// pipeline-able code.
-		var p *obj.Prog
-		for ; c > 0; c-- {
-			p = gins(op, &src, &tmp)
-			p.From.Type = obj.TYPE_MEM
-			p.From.Offset = int64(dir)
-
-			p = gins(mips.AADDV, nil, &src)
-			p.From.Type = obj.TYPE_CONST
-			p.From.Offset = int64(dir)
-
-			p = gins(op, &tmp, &dst)
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = int64(dir)
-
-			p = gins(mips.AADDV, nil, &dst)
-			p.From.Type = obj.TYPE_CONST
-			p.From.Offset = int64(dir)
-		}
-	}
-
-	gc.Regfree(&dst)
-	gc.Regfree(&src)
-	gc.Regfree(&tmp)
-}
diff --git a/src/cmd/compile/internal/mips64/galign.go b/src/cmd/compile/internal/mips64/galign.go
index 22890c8..4a36a4c 100644
--- a/src/cmd/compile/internal/mips64/galign.go
+++ b/src/cmd/compile/internal/mips64/galign.go
@@ -6,62 +6,23 @@ package mips64
 
 import (
 	"cmd/compile/internal/gc"
+	"cmd/compile/internal/ssa"
 	"cmd/internal/obj"
 	"cmd/internal/obj/mips"
 )
 
-func betypeinit() {
-}
-
-func Main() {
+func Init() {
 	gc.Thearch.LinkArch = &mips.Linkmips64
-	if obj.Getgoarch() == "mips64le" {
+	if obj.GOARCH == "mips64le" {
 		gc.Thearch.LinkArch = &mips.Linkmips64le
 	}
 	gc.Thearch.REGSP = mips.REGSP
-	gc.Thearch.REGCTXT = mips.REGCTXT
-	gc.Thearch.REGCALLX = mips.REG_R1
-	gc.Thearch.REGCALLX2 = mips.REG_R2
-	gc.Thearch.REGRETURN = mips.REGRET
-	gc.Thearch.REGMIN = mips.REG_R0
-	gc.Thearch.REGMAX = mips.REG_R31
-	gc.Thearch.FREGMIN = mips.REG_F0
-	gc.Thearch.FREGMAX = mips.REG_F31
 	gc.Thearch.MAXWIDTH = 1 << 50
-	gc.Thearch.ReservedRegs = resvd
 
-	gc.Thearch.Betypeinit = betypeinit
-	gc.Thearch.Cgen_hmul = cgen_hmul
-	gc.Thearch.Cgen_shift = cgen_shift
-	gc.Thearch.Clearfat = clearfat
 	gc.Thearch.Defframe = defframe
-	gc.Thearch.Dodiv = dodiv
-	gc.Thearch.Excise = excise
-	gc.Thearch.Expandchecks = expandchecks
-	gc.Thearch.Getg = getg
-	gc.Thearch.Gins = gins
-	gc.Thearch.Ginscmp = ginscmp
-	gc.Thearch.Ginscon = ginscon
-	gc.Thearch.Ginsnop = ginsnop
-	gc.Thearch.Gmove = gmove
-	gc.Thearch.Peep = peep
 	gc.Thearch.Proginfo = proginfo
-	gc.Thearch.Regtyp = regtyp
-	gc.Thearch.Sameaddr = sameaddr
-	gc.Thearch.Smallindir = smallindir
-	gc.Thearch.Stackaddr = stackaddr
-	gc.Thearch.Blockcopy = blockcopy
-	gc.Thearch.Sudoaddable = sudoaddable
-	gc.Thearch.Sudoclean = sudoclean
-	gc.Thearch.Excludedregs = excludedregs
-	gc.Thearch.RtoB = RtoB
-	gc.Thearch.FtoB = RtoB
-	gc.Thearch.BtoR = BtoR
-	gc.Thearch.BtoF = BtoF
-	gc.Thearch.Optoas = optoas
-	gc.Thearch.Doregbits = doregbits
-	gc.Thearch.Regnames = regnames
 
-	gc.Main()
-	gc.Exit(0)
+	gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
+	gc.Thearch.SSAGenValue = ssaGenValue
+	gc.Thearch.SSAGenBlock = ssaGenBlock
 }
diff --git a/src/cmd/compile/internal/mips64/ggen.go b/src/cmd/compile/internal/mips64/ggen.go
index e0c4de2..2af4a8b 100644
--- a/src/cmd/compile/internal/mips64/ggen.go
+++ b/src/cmd/compile/internal/mips64/ggen.go
@@ -8,7 +8,6 @@ import (
 	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/mips"
-	"fmt"
 )
 
 func defframe(ptxt *obj.Prog) {
@@ -36,7 +35,7 @@ func defframe(ptxt *obj.Prog) {
 			gc.Fatalf("needzero class %d", n.Class)
 		}
 		if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
-			gc.Fatalf("var %v has size %d offset %d", gc.Nconv(n, gc.FmtLong), int(n.Type.Width), int(n.Xoffset))
+			gc.Fatalf("var %L has size %d offset %d", n, int(n.Type.Width), int(n.Xoffset))
 		}
 
 		if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) {
@@ -66,18 +65,13 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
 	}
 	if cnt < int64(4*gc.Widthptr) {
 		for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
-			p = appendpp(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, 8+frame+lo+i)
+			p = gc.Appendpp(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, 8+frame+lo+i)
 		}
-		// TODO(dfc): https://golang.org/issue/12108
-		// If DUFFZERO is used inside a tail call (see genwrapper) it will
-		// overwrite the link register.
-	} else if false && cnt <= int64(128*gc.Widthptr) {
-		p = appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, mips.REGRT1, 0)
+	} else if cnt <= int64(128*gc.Widthptr) {
+		p = gc.Appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, mips.REGRT1, 0)
 		p.Reg = mips.REGSP
-		p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
-		f := gc.Sysfunc("duffzero")
-		gc.Naddr(&p.To, f)
-		gc.Afunclit(&p.To, f)
+		p = gc.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
+		gc.Naddr(&p.To, gc.Sysfunc("duffzero"))
 		p.To.Offset = 8 * (128 - cnt/int64(gc.Widthptr))
 	} else {
 		//	ADDV	$(8+frame+lo-8), SP, r1
@@ -86,14 +80,14 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
 		//	MOVV	R0, (Widthptr)r1
 		//	ADDV	$Widthptr, r1
 		//	BNE		r1, r2, loop
-		p = appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, mips.REGRT1, 0)
+		p = gc.Appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, mips.REGRT1, 0)
 		p.Reg = mips.REGSP
-		p = appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
+		p = gc.Appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
 		p.Reg = mips.REGRT1
-		p = appendpp(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGRT1, int64(gc.Widthptr))
+		p = gc.Appendpp(p, mips.AMOVV, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGRT1, int64(gc.Widthptr))
 		p1 := p
-		p = appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, int64(gc.Widthptr), obj.TYPE_REG, mips.REGRT1, 0)
-		p = appendpp(p, mips.ABNE, obj.TYPE_REG, mips.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
+		p = gc.Appendpp(p, mips.AADDV, obj.TYPE_CONST, 0, int64(gc.Widthptr), obj.TYPE_REG, mips.REGRT1, 0)
+		p = gc.Appendpp(p, mips.ABNE, obj.TYPE_REG, mips.REGRT1, 0, obj.TYPE_BRANCH, 0, 0)
 		p.Reg = mips.REGRT2
 		gc.Patch(p, p1)
 	}
@@ -101,391 +95,10 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
 	return p
 }
 
-func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int64, ttype obj.AddrType, treg int, toffset int64) *obj.Prog {
-	q := gc.Ctxt.NewProg()
-	gc.Clearp(q)
-	q.As = as
-	q.Lineno = p.Lineno
-	q.From.Type = ftype
-	q.From.Reg = int16(freg)
-	q.From.Offset = foffset
-	q.To.Type = ttype
-	q.To.Reg = int16(treg)
-	q.To.Offset = toffset
-	q.Link = p.Link
-	p.Link = q
-	return q
-}
-
 func ginsnop() {
-	var reg gc.Node
-	gc.Nodreg(&reg, gc.Types[gc.TINT], mips.REG_R0)
-	gins(mips.ANOR, &reg, &reg)
-}
-
-var panicdiv *gc.Node
-
-/*
- * generate division.
- * generates one of:
- *	res = nl / nr
- *	res = nl % nr
- * according to op.
- */
-func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	t := nl.Type
-
-	t0 := t
-
-	if t.Width < 8 {
-		if t.IsSigned() {
-			t = gc.Types[gc.TINT64]
-		} else {
-			t = gc.Types[gc.TUINT64]
-		}
-	}
-
-	a := optoas(gc.ODIV, t)
-
-	var tl gc.Node
-	gc.Regalloc(&tl, t0, nil)
-	var tr gc.Node
-	gc.Regalloc(&tr, t0, nil)
-	if nl.Ullman >= nr.Ullman {
-		gc.Cgen(nl, &tl)
-		gc.Cgen(nr, &tr)
-	} else {
-		gc.Cgen(nr, &tr)
-		gc.Cgen(nl, &tl)
-	}
-
-	if t != t0 {
-		// Convert
-		tl2 := tl
-
-		tr2 := tr
-		tl.Type = t
-		tr.Type = t
-		gmove(&tl2, &tl)
-		gmove(&tr2, &tr)
-	}
-
-	// Handle divide-by-zero panic.
-	p1 := ginsbranch(mips.ABNE, nil, &tr, nil, 0)
-	if panicdiv == nil {
-		panicdiv = gc.Sysfunc("panicdivide")
-	}
-	gc.Ginscall(panicdiv, -1)
-	gc.Patch(p1, gc.Pc)
-
-	gins3(a, &tr, &tl, nil)
-	gc.Regfree(&tr)
-	if op == gc.ODIV {
-		var lo gc.Node
-		gc.Nodreg(&lo, gc.Types[gc.TUINT64], mips.REG_LO)
-		gins(mips.AMOVV, &lo, &tl)
-	} else { // remainder in REG_HI
-		var hi gc.Node
-		gc.Nodreg(&hi, gc.Types[gc.TUINT64], mips.REG_HI)
-		gins(mips.AMOVV, &hi, &tl)
-	}
-	gmove(&tl, res)
-	gc.Regfree(&tl)
-}
-
-/*
- * generate high multiply:
- *   res = (nl*nr) >> width
- */
-func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	// largest ullman on left.
-	if nl.Ullman < nr.Ullman {
-		nl, nr = nr, nl
-	}
-
-	t := nl.Type
-	w := t.Width * 8
-	var n1 gc.Node
-	gc.Cgenr(nl, &n1, res)
-	var n2 gc.Node
-	gc.Cgenr(nr, &n2, nil)
-	switch gc.Simtype[t.Etype] {
-	case gc.TINT8,
-		gc.TINT16,
-		gc.TINT32:
-		gins3(optoas(gc.OMUL, t), &n2, &n1, nil)
-		var lo gc.Node
-		gc.Nodreg(&lo, gc.Types[gc.TUINT64], mips.REG_LO)
-		gins(mips.AMOVV, &lo, &n1)
-		p := gins(mips.ASRAV, nil, &n1)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-
-	case gc.TUINT8,
-		gc.TUINT16,
-		gc.TUINT32:
-		gins3(optoas(gc.OMUL, t), &n2, &n1, nil)
-		var lo gc.Node
-		gc.Nodreg(&lo, gc.Types[gc.TUINT64], mips.REG_LO)
-		gins(mips.AMOVV, &lo, &n1)
-		p := gins(mips.ASRLV, nil, &n1)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-
-	case gc.TINT64,
-		gc.TUINT64:
-		if t.IsSigned() {
-			gins3(mips.AMULV, &n2, &n1, nil)
-		} else {
-			gins3(mips.AMULVU, &n2, &n1, nil)
-		}
-		var hi gc.Node
-		gc.Nodreg(&hi, gc.Types[gc.TUINT64], mips.REG_HI)
-		gins(mips.AMOVV, &hi, &n1)
-
-	default:
-		gc.Fatalf("cgen_hmul %v", t)
-	}
-
-	gc.Cgen(&n1, res)
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-/*
- * generate shift according to op, one of:
- *	res = nl << nr
- *	res = nl >> nr
- */
-func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	a := optoas(op, nl.Type)
-
-	if nr.Op == gc.OLITERAL {
-		var n1 gc.Node
-		gc.Regalloc(&n1, nl.Type, res)
-		gc.Cgen(nl, &n1)
-		sc := uint64(nr.Int64())
-		if sc >= uint64(nl.Type.Width*8) {
-			// large shift gets 2 shifts by width-1
-			var n3 gc.Node
-			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
-
-			gins(a, &n3, &n1)
-			gins(a, &n3, &n1)
-		} else {
-			gins(a, nr, &n1)
-		}
-		gmove(&n1, res)
-		gc.Regfree(&n1)
-		return
-	}
-
-	if nl.Ullman >= gc.UINF {
-		var n4 gc.Node
-		gc.Tempname(&n4, nl.Type)
-		gc.Cgen(nl, &n4)
-		nl = &n4
-	}
-
-	if nr.Ullman >= gc.UINF {
-		var n5 gc.Node
-		gc.Tempname(&n5, nr.Type)
-		gc.Cgen(nr, &n5)
-		nr = &n5
-	}
-
-	// Allow either uint32 or uint64 as shift type,
-	// to avoid unnecessary conversion from uint32 to uint64
-	// just to do the comparison.
-	tcount := gc.Types[gc.Simtype[nr.Type.Etype]]
-
-	if tcount.Etype < gc.TUINT32 {
-		tcount = gc.Types[gc.TUINT32]
-	}
-
-	var n1 gc.Node
-	gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX
-	var n3 gc.Node
-	gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX
-
-	var n2 gc.Node
-	gc.Regalloc(&n2, nl.Type, res)
-
-	if nl.Ullman >= nr.Ullman {
-		gc.Cgen(nl, &n2)
-		gc.Cgen(nr, &n1)
-		gmove(&n1, &n3)
-	} else {
-		gc.Cgen(nr, &n1)
-		gmove(&n1, &n3)
-		gc.Cgen(nl, &n2)
-	}
-
-	gc.Regfree(&n3)
-
-	// test and fix up large shifts
-	if !bounded {
-		var rtmp gc.Node
-		gc.Nodreg(&rtmp, tcount, mips.REGTMP)
-		gc.Nodconst(&n3, tcount, nl.Type.Width*8)
-		gins3(mips.ASGTU, &n3, &n1, &rtmp)
-		p1 := ginsbranch(mips.ABNE, nil, &rtmp, nil, 0)
-		if op == gc.ORSH && nl.Type.IsSigned() {
-			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
-			gins(a, &n3, &n2)
-		} else {
-			gc.Nodconst(&n3, nl.Type, 0)
-			gmove(&n3, &n2)
-		}
-
-		gc.Patch(p1, gc.Pc)
-	}
-
-	gins(a, &n1, &n2)
-
-	gmove(&n2, res)
-
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-func clearfat(nl *gc.Node) {
-	/* clear a fat object */
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width)
-	}
-
-	w := uint64(nl.Type.Width)
-
-	// Avoid taking the address for simple enough types.
-	if gc.Componentgen(nil, nl) {
-		return
-	}
-
-	c := w % 8 // bytes
-	q := w / 8 // dwords
-
-	if gc.Reginuse(mips.REGRT1) {
-		gc.Fatalf("%v in use during clearfat", obj.Rconv(mips.REGRT1))
-	}
-
-	var r0 gc.Node
-	gc.Nodreg(&r0, gc.Types[gc.TUINT64], mips.REGZERO)
-	var dst gc.Node
-	gc.Nodreg(&dst, gc.Types[gc.Tptr], mips.REGRT1)
-	gc.Regrealloc(&dst)
-	gc.Agen(nl, &dst)
-
-	var boff uint64
-	if q > 128 {
-		p := gins(mips.ASUBV, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = 8
-
-		var end gc.Node
-		gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
-		p = gins(mips.AMOVV, &dst, &end)
-		p.From.Type = obj.TYPE_ADDR
-		p.From.Offset = int64(q * 8)
-
-		p = gins(mips.AMOVV, &r0, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = 8
-		pl := p
-
-		p = gins(mips.AADDV, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = 8
-
-		gc.Patch(ginsbranch(mips.ABNE, nil, &dst, &end, 0), pl)
-
-		gc.Regfree(&end)
-
-		// The loop leaves R1 on the last zeroed dword
-		boff = 8
-		// TODO(dfc): https://golang.org/issue/12108
-		// If DUFFZERO is used inside a tail call (see genwrapper) it will
-		// overwrite the link register.
-	} else if false && q >= 4 {
-		p := gins(mips.ASUBV, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = 8
-		f := gc.Sysfunc("duffzero")
-		p = gins(obj.ADUFFZERO, nil, f)
-		gc.Afunclit(&p.To, f)
-
-		// 8 and 128 = magic constants: see ../../runtime/asm_mips64x.s
-		p.To.Offset = int64(8 * (128 - q))
-
-		// duffzero leaves R1 on the last zeroed dword
-		boff = 8
-	} else {
-		var p *obj.Prog
-		for t := uint64(0); t < q; t++ {
-			p = gins(mips.AMOVV, &r0, &dst)
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = int64(8 * t)
-		}
-
-		boff = 8 * q
-	}
-
-	var p *obj.Prog
-	for t := uint64(0); t < c; t++ {
-		p = gins(mips.AMOVB, &r0, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = int64(t + boff)
-	}
-
-	gc.Regfree(&dst)
-}
-
-// Called after regopt and peep have run.
-// Expand CHECKNIL pseudo-op into actual nil pointer check.
-func expandchecks(firstp *obj.Prog) {
-	var p1 *obj.Prog
-
-	for p := firstp; p != nil; p = p.Link {
-		if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 {
-			fmt.Printf("expandchecks: %v\n", p)
-		}
-		if p.As != obj.ACHECKNIL {
-			continue
-		}
-		if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers
-			gc.Warnl(p.Lineno, "generated nil check")
-		}
-		if p.From.Type != obj.TYPE_REG {
-			gc.Fatalf("invalid nil check %v\n", p)
-		}
-
-		// check is
-		//	BNE arg, 2(PC)
-		//	MOVV R0, 0(R0)
-		p1 = gc.Ctxt.NewProg()
-		gc.Clearp(p1)
-		p1.Link = p.Link
-		p.Link = p1
-		p1.Lineno = p.Lineno
-		p1.Pc = 9999
-
-		p.As = mips.ABNE
-		p.To.Type = obj.TYPE_BRANCH
-		p.To.Val = p1.Link
-
-		// crash by write to memory address 0.
-		p1.As = mips.AMOVV
-		p1.From.Type = obj.TYPE_REG
-		p1.From.Reg = mips.REGZERO
-		p1.To.Type = obj.TYPE_MEM
-		p1.To.Reg = mips.REGZERO
-		p1.To.Offset = 0
-	}
-}
-
-// res = runtime.getg()
-func getg(res *gc.Node) {
-	var n1 gc.Node
-	gc.Nodreg(&n1, res.Type, mips.REGG)
-	gmove(&n1, res)
+	p := gc.Prog(mips.ANOR)
+	p.From.Type = obj.TYPE_REG
+	p.From.Reg = mips.REG_R0
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = mips.REG_R0
 }
diff --git a/src/cmd/compile/internal/mips64/gsubr.go b/src/cmd/compile/internal/mips64/gsubr.go
deleted file mode 100644
index eb56d8b..0000000
--- a/src/cmd/compile/internal/mips64/gsubr.go
+++ /dev/null
@@ -1,1071 +0,0 @@
-// Derived from Inferno utils/6c/txt.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package mips64
-
-import (
-	"cmd/compile/internal/big"
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/mips"
-	"fmt"
-)
-
-var resvd = []int{
-	mips.REGZERO,
-	mips.REGSP,   // reserved for SP
-	mips.REGSB,   // reserved for SB
-	mips.REGLINK, // reserved for link
-	mips.REGG,
-	mips.REGTMP,
-	mips.REG_R26, // kernel
-	mips.REG_R27, // kernel
-	mips.FREGZERO,
-	mips.FREGHALF,
-	mips.FREGONE,
-	mips.FREGTWO,
-}
-
-/*
- * generate
- *	as $c, n
- */
-func ginscon(as obj.As, c int64, n2 *gc.Node) {
-	var n1 gc.Node
-
-	gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
-
-	if as != mips.AMOVV && (c < -mips.BIG || c > mips.BIG) || n2.Op != gc.OREGISTER || as == mips.AMUL || as == mips.AMULU || as == mips.AMULV || as == mips.AMULVU {
-		// cannot have more than 16-bit of immediate in ADD, etc.
-		// instead, MOV into register first.
-		var ntmp gc.Node
-		gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil)
-
-		rawgins(mips.AMOVV, &n1, &ntmp)
-		rawgins(as, &ntmp, n2)
-		gc.Regfree(&ntmp)
-		return
-	}
-
-	rawgins(as, &n1, n2)
-}
-
-// generate branch
-// n1, n2 are registers
-func ginsbranch(as obj.As, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
-	p := gc.Gbranch(as, t, likely)
-	gc.Naddr(&p.From, n1)
-	if n2 != nil {
-		p.Reg = n2.Reg
-	}
-	return p
-}
-
-func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
-	if !t.IsFloat() && (op == gc.OLT || op == gc.OGE) {
-		// swap nodes to fit SGT instruction
-		n1, n2 = n2, n1
-	}
-	if t.IsFloat() && (op == gc.OLT || op == gc.OLE) {
-		// swap nodes to fit CMPGT, CMPGE instructions and reverse relation
-		n1, n2 = n2, n1
-		if op == gc.OLT {
-			op = gc.OGT
-		} else {
-			op = gc.OGE
-		}
-	}
-
-	var r1, r2, g1, g2 gc.Node
-	gc.Regalloc(&r1, t, n1)
-	gc.Regalloc(&g1, n1.Type, &r1)
-	gc.Cgen(n1, &g1)
-	gmove(&g1, &r1)
-
-	gc.Regalloc(&r2, t, n2)
-	gc.Regalloc(&g2, n1.Type, &r2)
-	gc.Cgen(n2, &g2)
-	gmove(&g2, &r2)
-
-	var p *obj.Prog
-	var ntmp gc.Node
-	gc.Nodreg(&ntmp, gc.Types[gc.TINT], mips.REGTMP)
-
-	switch gc.Simtype[t.Etype] {
-	case gc.TINT8,
-		gc.TINT16,
-		gc.TINT32,
-		gc.TINT64:
-		if op == gc.OEQ || op == gc.ONE {
-			p = ginsbranch(optoas(op, t), nil, &r1, &r2, likely)
-		} else {
-			gins3(mips.ASGT, &r1, &r2, &ntmp)
-
-			p = ginsbranch(optoas(op, t), nil, &ntmp, nil, likely)
-		}
-
-	case gc.TBOOL,
-		gc.TUINT8,
-		gc.TUINT16,
-		gc.TUINT32,
-		gc.TUINT64,
-		gc.TPTR32,
-		gc.TPTR64:
-		if op == gc.OEQ || op == gc.ONE {
-			p = ginsbranch(optoas(op, t), nil, &r1, &r2, likely)
-		} else {
-			gins3(mips.ASGTU, &r1, &r2, &ntmp)
-
-			p = ginsbranch(optoas(op, t), nil, &ntmp, nil, likely)
-		}
-
-	case gc.TFLOAT32:
-		switch op {
-		default:
-			gc.Fatalf("ginscmp: no entry for op=%s type=%v", op, t)
-
-		case gc.OEQ,
-			gc.ONE:
-			gins3(mips.ACMPEQF, &r1, &r2, nil)
-
-		case gc.OGE:
-			gins3(mips.ACMPGEF, &r1, &r2, nil)
-
-		case gc.OGT:
-			gins3(mips.ACMPGTF, &r1, &r2, nil)
-		}
-		p = gc.Gbranch(optoas(op, t), nil, likely)
-
-	case gc.TFLOAT64:
-		switch op {
-		default:
-			gc.Fatalf("ginscmp: no entry for op=%s type=%v", op, t)
-
-		case gc.OEQ,
-			gc.ONE:
-			gins3(mips.ACMPEQD, &r1, &r2, nil)
-
-		case gc.OGE:
-			gins3(mips.ACMPGED, &r1, &r2, nil)
-
-		case gc.OGT:
-			gins3(mips.ACMPGTD, &r1, &r2, nil)
-		}
-		p = gc.Gbranch(optoas(op, t), nil, likely)
-	}
-
-	gc.Regfree(&g2)
-	gc.Regfree(&r2)
-	gc.Regfree(&g1)
-	gc.Regfree(&r1)
-
-	return p
-}
-
-// set up nodes representing 2^63
-var (
-	bigi         gc.Node
-	bigf         gc.Node
-	bignodes_did bool
-)
-
-func bignodes() {
-	if bignodes_did {
-		return
-	}
-	bignodes_did = true
-
-	var i big.Int
-	i.SetInt64(1)
-	i.Lsh(&i, 63)
-
-	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
-	bigi.SetBigInt(&i)
-
-	bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64])
-}
-
-/*
- * generate move:
- *	t = f
- * hard part is conversions.
- */
-func gmove(f *gc.Node, t *gc.Node) {
-	if gc.Debug['M'] != 0 {
-		fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, gc.FmtLong), gc.Nconv(t, gc.FmtLong))
-	}
-
-	ft := int(gc.Simsimtype(f.Type))
-	tt := int(gc.Simsimtype(t.Type))
-	cvt := t.Type
-
-	if gc.Iscomplex[ft] || gc.Iscomplex[tt] {
-		gc.Complexmove(f, t)
-		return
-	}
-
-	// cannot have two memory operands
-	var r2 gc.Node
-	var r1 gc.Node
-	var a obj.As
-	if gc.Ismem(f) && gc.Ismem(t) {
-		goto hard
-	}
-
-	// convert constant to desired type
-	if f.Op == gc.OLITERAL {
-		var con gc.Node
-		switch tt {
-		default:
-			f.Convconst(&con, t.Type)
-
-		case gc.TINT32,
-			gc.TINT16,
-			gc.TINT8:
-			var con gc.Node
-			f.Convconst(&con, gc.Types[gc.TINT64])
-			var r1 gc.Node
-			gc.Regalloc(&r1, con.Type, t)
-			gins(mips.AMOVV, &con, &r1)
-			gmove(&r1, t)
-			gc.Regfree(&r1)
-			return
-
-		case gc.TUINT32,
-			gc.TUINT16,
-			gc.TUINT8:
-			var con gc.Node
-			f.Convconst(&con, gc.Types[gc.TUINT64])
-			var r1 gc.Node
-			gc.Regalloc(&r1, con.Type, t)
-			gins(mips.AMOVV, &con, &r1)
-			gmove(&r1, t)
-			gc.Regfree(&r1)
-			return
-		}
-
-		f = &con
-		ft = tt // so big switch will choose a simple mov
-
-		// constants can't move directly to memory.
-		if gc.Ismem(t) {
-			goto hard
-		}
-	}
-
-	// value -> value copy, first operand in memory.
-	// any floating point operand requires register
-	// src, so goto hard to copy to register first.
-	if gc.Ismem(f) && ft != tt && (gc.Isfloat[ft] || gc.Isfloat[tt]) {
-		cvt = gc.Types[ft]
-		goto hard
-	}
-
-	// value -> value copy, only one memory operand.
-	// figure out the instruction to use.
-	// break out of switch for one-instruction gins.
-	// goto rdst for "destination must be register".
-	// goto hard for "convert to cvt type first".
-	// otherwise handle and return.
-
-	switch uint32(ft)<<16 | uint32(tt) {
-	default:
-		gc.Fatalf("gmove %v -> %v", gc.Tconv(f.Type, gc.FmtLong), gc.Tconv(t.Type, gc.FmtLong))
-
-		/*
-		 * integer copy and truncate
-		 */
-	case gc.TINT8<<16 | gc.TINT8, // same size
-		gc.TUINT8<<16 | gc.TINT8,
-		gc.TINT16<<16 | gc.TINT8, // truncate
-		gc.TUINT16<<16 | gc.TINT8,
-		gc.TINT32<<16 | gc.TINT8,
-		gc.TUINT32<<16 | gc.TINT8,
-		gc.TINT64<<16 | gc.TINT8,
-		gc.TUINT64<<16 | gc.TINT8:
-		a = mips.AMOVB
-
-	case gc.TINT8<<16 | gc.TUINT8, // same size
-		gc.TUINT8<<16 | gc.TUINT8,
-		gc.TINT16<<16 | gc.TUINT8, // truncate
-		gc.TUINT16<<16 | gc.TUINT8,
-		gc.TINT32<<16 | gc.TUINT8,
-		gc.TUINT32<<16 | gc.TUINT8,
-		gc.TINT64<<16 | gc.TUINT8,
-		gc.TUINT64<<16 | gc.TUINT8:
-		a = mips.AMOVBU
-
-	case gc.TINT16<<16 | gc.TINT16, // same size
-		gc.TUINT16<<16 | gc.TINT16,
-		gc.TINT32<<16 | gc.TINT16, // truncate
-		gc.TUINT32<<16 | gc.TINT16,
-		gc.TINT64<<16 | gc.TINT16,
-		gc.TUINT64<<16 | gc.TINT16:
-		a = mips.AMOVH
-
-	case gc.TINT16<<16 | gc.TUINT16, // same size
-		gc.TUINT16<<16 | gc.TUINT16,
-		gc.TINT32<<16 | gc.TUINT16, // truncate
-		gc.TUINT32<<16 | gc.TUINT16,
-		gc.TINT64<<16 | gc.TUINT16,
-		gc.TUINT64<<16 | gc.TUINT16:
-		a = mips.AMOVHU
-
-	case gc.TINT32<<16 | gc.TINT32, // same size
-		gc.TUINT32<<16 | gc.TINT32,
-		gc.TINT64<<16 | gc.TINT32, // truncate
-		gc.TUINT64<<16 | gc.TINT32:
-		a = mips.AMOVW
-
-	case gc.TINT32<<16 | gc.TUINT32, // same size
-		gc.TUINT32<<16 | gc.TUINT32,
-		gc.TINT64<<16 | gc.TUINT32, // truncate
-		gc.TUINT64<<16 | gc.TUINT32:
-		a = mips.AMOVWU
-
-	case gc.TINT64<<16 | gc.TINT64, // same size
-		gc.TINT64<<16 | gc.TUINT64,
-		gc.TUINT64<<16 | gc.TINT64,
-		gc.TUINT64<<16 | gc.TUINT64:
-		a = mips.AMOVV
-
-		/*
-		 * integer up-conversions
-		 */
-	case gc.TINT8<<16 | gc.TINT16, // sign extend int8
-		gc.TINT8<<16 | gc.TUINT16,
-		gc.TINT8<<16 | gc.TINT32,
-		gc.TINT8<<16 | gc.TUINT32,
-		gc.TINT8<<16 | gc.TINT64,
-		gc.TINT8<<16 | gc.TUINT64:
-		a = mips.AMOVB
-
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8
-		gc.TUINT8<<16 | gc.TUINT16,
-		gc.TUINT8<<16 | gc.TINT32,
-		gc.TUINT8<<16 | gc.TUINT32,
-		gc.TUINT8<<16 | gc.TINT64,
-		gc.TUINT8<<16 | gc.TUINT64:
-		a = mips.AMOVBU
-
-		goto rdst
-
-	case gc.TINT16<<16 | gc.TINT32, // sign extend int16
-		gc.TINT16<<16 | gc.TUINT32,
-		gc.TINT16<<16 | gc.TINT64,
-		gc.TINT16<<16 | gc.TUINT64:
-		a = mips.AMOVH
-
-		goto rdst
-
-	case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16
-		gc.TUINT16<<16 | gc.TUINT32,
-		gc.TUINT16<<16 | gc.TINT64,
-		gc.TUINT16<<16 | gc.TUINT64:
-		a = mips.AMOVHU
-
-		goto rdst
-
-	case gc.TINT32<<16 | gc.TINT64, // sign extend int32
-		gc.TINT32<<16 | gc.TUINT64:
-		a = mips.AMOVW
-
-		goto rdst
-
-	case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32
-		gc.TUINT32<<16 | gc.TUINT64:
-		a = mips.AMOVWU
-
-		goto rdst
-
-		//warn("gmove: convert float to int not implemented: %N -> %N\n", f, t);
-	//return;
-	// algorithm is:
-	//	if small enough, use native float64 -> int64 conversion.
-	//	otherwise, subtract 2^63, convert, and add it back.
-	/*
-	* float to integer
-	 */
-	case gc.TFLOAT32<<16 | gc.TINT32,
-		gc.TFLOAT64<<16 | gc.TINT32,
-		gc.TFLOAT32<<16 | gc.TINT64,
-		gc.TFLOAT64<<16 | gc.TINT64,
-		gc.TFLOAT32<<16 | gc.TINT16,
-		gc.TFLOAT32<<16 | gc.TINT8,
-		gc.TFLOAT32<<16 | gc.TUINT16,
-		gc.TFLOAT32<<16 | gc.TUINT8,
-		gc.TFLOAT64<<16 | gc.TINT16,
-		gc.TFLOAT64<<16 | gc.TINT8,
-		gc.TFLOAT64<<16 | gc.TUINT16,
-		gc.TFLOAT64<<16 | gc.TUINT8,
-		gc.TFLOAT32<<16 | gc.TUINT32,
-		gc.TFLOAT64<<16 | gc.TUINT32,
-		gc.TFLOAT32<<16 | gc.TUINT64,
-		gc.TFLOAT64<<16 | gc.TUINT64:
-		bignodes()
-
-		gc.Regalloc(&r1, gc.Types[gc.TFLOAT64], nil)
-		gmove(f, &r1)
-		if tt == gc.TUINT64 {
-			gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], nil)
-			gmove(&bigf, &r2)
-			gins3(mips.ACMPGED, &r1, &r2, nil)
-			p1 := gc.Gbranch(mips.ABFPF, nil, 0)
-			gins(mips.ASUBD, &r2, &r1)
-			gc.Patch(p1, gc.Pc)
-			gc.Regfree(&r2)
-		}
-
-		gc.Regalloc(&r2, gc.Types[gc.TINT64], t)
-		gins(mips.ATRUNCDV, &r1, &r1)
-		gins(mips.AMOVV, &r1, &r2)
-		gc.Regfree(&r1)
-
-		if tt == gc.TUINT64 {
-			p1 := gc.Gbranch(mips.ABFPF, nil, 0) // use FCR0 here again
-			gc.Nodreg(&r1, gc.Types[gc.TINT64], mips.REGTMP)
-			gmove(&bigi, &r1)
-			gins(mips.AADDVU, &r1, &r2)
-			gc.Patch(p1, gc.Pc)
-		}
-
-		gmove(&r2, t)
-		gc.Regfree(&r2)
-		return
-
-		//warn("gmove: convert int to float not implemented: %N -> %N\n", f, t);
-	//return;
-	// algorithm is:
-	//	if small enough, use native int64 -> float64 conversion.
-	//	otherwise, halve (x -> (x>>1)|(x&1)), convert, and double.
-	/*
-	 * integer to float
-	 */
-	case gc.TINT32<<16 | gc.TFLOAT32,
-		gc.TINT32<<16 | gc.TFLOAT64,
-		gc.TINT64<<16 | gc.TFLOAT32,
-		gc.TINT64<<16 | gc.TFLOAT64,
-		gc.TINT16<<16 | gc.TFLOAT32,
-		gc.TINT16<<16 | gc.TFLOAT64,
-		gc.TINT8<<16 | gc.TFLOAT32,
-		gc.TINT8<<16 | gc.TFLOAT64,
-		gc.TUINT16<<16 | gc.TFLOAT32,
-		gc.TUINT16<<16 | gc.TFLOAT64,
-		gc.TUINT8<<16 | gc.TFLOAT32,
-		gc.TUINT8<<16 | gc.TFLOAT64,
-		gc.TUINT32<<16 | gc.TFLOAT32,
-		gc.TUINT32<<16 | gc.TFLOAT64,
-		gc.TUINT64<<16 | gc.TFLOAT32,
-		gc.TUINT64<<16 | gc.TFLOAT64:
-		bignodes()
-
-		var rtmp gc.Node
-		gc.Regalloc(&r1, gc.Types[gc.TINT64], nil)
-		gmove(f, &r1)
-		if ft == gc.TUINT64 {
-			gc.Nodreg(&rtmp, gc.Types[gc.TUINT64], mips.REGTMP)
-			gmove(&bigi, &rtmp)
-			gins(mips.AAND, &r1, &rtmp)
-			p1 := ginsbranch(mips.ABEQ, nil, &rtmp, nil, 0)
-			var r3 gc.Node
-			gc.Regalloc(&r3, gc.Types[gc.TUINT64], nil)
-			p2 := gins3(mips.AAND, nil, &r1, &r3)
-			p2.From.Type = obj.TYPE_CONST
-			p2.From.Offset = 1
-			p3 := gins(mips.ASRLV, nil, &r1)
-			p3.From.Type = obj.TYPE_CONST
-			p3.From.Offset = 1
-			gins(mips.AOR, &r3, &r1)
-			gc.Regfree(&r3)
-			gc.Patch(p1, gc.Pc)
-		}
-
-		gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], t)
-		gins(mips.AMOVV, &r1, &r2)
-		gins(mips.AMOVVD, &r2, &r2)
-		gc.Regfree(&r1)
-
-		if ft == gc.TUINT64 {
-			p1 := ginsbranch(mips.ABEQ, nil, &rtmp, nil, 0)
-			gc.Nodreg(&r1, gc.Types[gc.TFLOAT64], mips.FREGTWO)
-			gins(mips.AMULD, &r1, &r2)
-			gc.Patch(p1, gc.Pc)
-		}
-
-		gmove(&r2, t)
-		gc.Regfree(&r2)
-		return
-
-		/*
-		 * float to float
-		 */
-	case gc.TFLOAT32<<16 | gc.TFLOAT32:
-		a = mips.AMOVF
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT64:
-		a = mips.AMOVD
-
-	case gc.TFLOAT32<<16 | gc.TFLOAT64:
-		a = mips.AMOVFD
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT32:
-		a = mips.AMOVDF
-		goto rdst
-	}
-
-	gins(a, f, t)
-	return
-
-	// requires register destination
-rdst:
-	{
-		gc.Regalloc(&r1, t.Type, t)
-
-		gins(a, f, &r1)
-		gmove(&r1, t)
-		gc.Regfree(&r1)
-		return
-	}
-
-	// requires register intermediate
-hard:
-	gc.Regalloc(&r1, cvt, t)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-}
-
-// gins is called by the front end.
-// It synthesizes some multiple-instruction sequences
-// so the front end can stay simpler.
-func gins(as obj.As, f, t *gc.Node) *obj.Prog {
-	if as >= obj.A_ARCHSPECIFIC {
-		if x, ok := f.IntLiteral(); ok {
-			ginscon(as, x, t)
-			return nil // caller must not use
-		}
-	}
-	return rawgins(as, f, t)
-}
-
-/*
- * generate one instruction:
- *	as f, r, t
- * r must be register, if not nil
- */
-func gins3(as obj.As, f, r, t *gc.Node) *obj.Prog {
-	p := rawgins(as, f, t)
-	if r != nil {
-		p.Reg = r.Reg
-	}
-	return p
-}
-
-/*
- * generate one instruction:
- *	as f, t
- */
-func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
-	// TODO(austin): Add self-move test like in 6g (but be careful
-	// of truncation moves)
-
-	p := gc.Prog(as)
-	gc.Naddr(&p.From, f)
-	gc.Naddr(&p.To, t)
-
-	switch as {
-	case obj.ACALL:
-		if p.To.Type == obj.TYPE_REG {
-			// Allow front end to emit CALL REG, and rewrite into CALL (REG).
-			p.From = obj.Addr{}
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = 0
-
-			if gc.Debug['g'] != 0 {
-				fmt.Printf("%v\n", p)
-			}
-
-			return p
-		}
-
-	// Bad things the front end has done to us. Crash to find call stack.
-	case mips.AAND:
-		if p.From.Type == obj.TYPE_CONST {
-			gc.Debug['h'] = 1
-			gc.Fatalf("bad inst: %v", p)
-		}
-	case mips.ASGT, mips.ASGTU:
-		if p.From.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_MEM {
-			gc.Debug['h'] = 1
-			gc.Fatalf("bad inst: %v", p)
-		}
-
-	// Special cases
-	case mips.AMUL, mips.AMULU, mips.AMULV, mips.AMULVU:
-		if p.From.Type == obj.TYPE_CONST {
-			gc.Debug['h'] = 1
-			gc.Fatalf("bad inst: %v", p)
-		}
-
-		pp := gc.Prog(mips.AMOVV)
-		pp.From.Type = obj.TYPE_REG
-		pp.From.Reg = mips.REG_LO
-		pp.To = p.To
-
-		p.Reg = p.To.Reg
-		p.To = obj.Addr{}
-
-	case mips.ASUBVU:
-		// unary
-		if f == nil {
-			p.From = p.To
-			p.Reg = mips.REGZERO
-		}
-	}
-
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("%v\n", p)
-	}
-
-	w := int32(0)
-	switch as {
-	case mips.AMOVB,
-		mips.AMOVBU:
-		w = 1
-
-	case mips.AMOVH,
-		mips.AMOVHU:
-		w = 2
-
-	case mips.AMOVW,
-		mips.AMOVWU:
-		w = 4
-
-	case mips.AMOVV:
-		if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_ADDR {
-			break
-		}
-		w = 8
-	}
-
-	if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Type != obj.TYPE_REG && p.To.Width > int64(w))) {
-		gc.Dump("f", f)
-		gc.Dump("t", t)
-		gc.Fatalf("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width)
-	}
-
-	return p
-}
-
-/*
- * return Axxx for Oxxx on type t.
- */
-func optoas(op gc.Op, t *gc.Type) obj.As {
-	if t == nil {
-		gc.Fatalf("optoas: t is nil")
-	}
-
-	// avoid constant conversions in switches below
-	const (
-		OMINUS_ = uint32(gc.OMINUS) << 16
-		OLSH_   = uint32(gc.OLSH) << 16
-		ORSH_   = uint32(gc.ORSH) << 16
-		OADD_   = uint32(gc.OADD) << 16
-		OSUB_   = uint32(gc.OSUB) << 16
-		OMUL_   = uint32(gc.OMUL) << 16
-		ODIV_   = uint32(gc.ODIV) << 16
-		OOR_    = uint32(gc.OOR) << 16
-		OAND_   = uint32(gc.OAND) << 16
-		OXOR_   = uint32(gc.OXOR) << 16
-		OEQ_    = uint32(gc.OEQ) << 16
-		ONE_    = uint32(gc.ONE) << 16
-		OLT_    = uint32(gc.OLT) << 16
-		OLE_    = uint32(gc.OLE) << 16
-		OGE_    = uint32(gc.OGE) << 16
-		OGT_    = uint32(gc.OGT) << 16
-		OCMP_   = uint32(gc.OCMP) << 16
-		OAS_    = uint32(gc.OAS) << 16
-		OHMUL_  = uint32(gc.OHMUL) << 16
-	)
-
-	a := obj.AXXX
-	switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
-	default:
-		gc.Fatalf("optoas: no entry for op=%s type=%v", op, t)
-
-	case OEQ_ | gc.TBOOL,
-		OEQ_ | gc.TINT8,
-		OEQ_ | gc.TUINT8,
-		OEQ_ | gc.TINT16,
-		OEQ_ | gc.TUINT16,
-		OEQ_ | gc.TINT32,
-		OEQ_ | gc.TUINT32,
-		OEQ_ | gc.TINT64,
-		OEQ_ | gc.TUINT64,
-		OEQ_ | gc.TPTR32,
-		OEQ_ | gc.TPTR64:
-		a = mips.ABEQ
-
-	case OEQ_ | gc.TFLOAT32, // ACMPEQF
-		OEQ_ | gc.TFLOAT64: // ACMPEQD
-		a = mips.ABFPT
-
-	case ONE_ | gc.TBOOL,
-		ONE_ | gc.TINT8,
-		ONE_ | gc.TUINT8,
-		ONE_ | gc.TINT16,
-		ONE_ | gc.TUINT16,
-		ONE_ | gc.TINT32,
-		ONE_ | gc.TUINT32,
-		ONE_ | gc.TINT64,
-		ONE_ | gc.TUINT64,
-		ONE_ | gc.TPTR32,
-		ONE_ | gc.TPTR64:
-		a = mips.ABNE
-
-	case ONE_ | gc.TFLOAT32, // ACMPEQF
-		ONE_ | gc.TFLOAT64: // ACMPEQD
-		a = mips.ABFPF
-
-	case OLT_ | gc.TINT8, // ASGT
-		OLT_ | gc.TINT16,
-		OLT_ | gc.TINT32,
-		OLT_ | gc.TINT64,
-		OLT_ | gc.TUINT8, // ASGTU
-		OLT_ | gc.TUINT16,
-		OLT_ | gc.TUINT32,
-		OLT_ | gc.TUINT64:
-		a = mips.ABNE
-
-	case OLT_ | gc.TFLOAT32, // ACMPGEF
-		OLT_ | gc.TFLOAT64: // ACMPGED
-		a = mips.ABFPT
-
-	case OLE_ | gc.TINT8, // ASGT
-		OLE_ | gc.TINT16,
-		OLE_ | gc.TINT32,
-		OLE_ | gc.TINT64,
-		OLE_ | gc.TUINT8, // ASGTU
-		OLE_ | gc.TUINT16,
-		OLE_ | gc.TUINT32,
-		OLE_ | gc.TUINT64:
-		a = mips.ABEQ
-
-	case OLE_ | gc.TFLOAT32, // ACMPGTF
-		OLE_ | gc.TFLOAT64: // ACMPGTD
-		a = mips.ABFPT
-
-	case OGT_ | gc.TINT8, // ASGT
-		OGT_ | gc.TINT16,
-		OGT_ | gc.TINT32,
-		OGT_ | gc.TINT64,
-		OGT_ | gc.TUINT8, // ASGTU
-		OGT_ | gc.TUINT16,
-		OGT_ | gc.TUINT32,
-		OGT_ | gc.TUINT64:
-		a = mips.ABNE
-
-	case OGT_ | gc.TFLOAT32, // ACMPGTF
-		OGT_ | gc.TFLOAT64: // ACMPGTD
-		a = mips.ABFPT
-
-	case OGE_ | gc.TINT8, // ASGT
-		OGE_ | gc.TINT16,
-		OGE_ | gc.TINT32,
-		OGE_ | gc.TINT64,
-		OGE_ | gc.TUINT8, // ASGTU
-		OGE_ | gc.TUINT16,
-		OGE_ | gc.TUINT32,
-		OGE_ | gc.TUINT64:
-		a = mips.ABEQ
-
-	case OGE_ | gc.TFLOAT32, // ACMPGEF
-		OGE_ | gc.TFLOAT64: // ACMPGED
-		a = mips.ABFPT
-
-	case OAS_ | gc.TBOOL,
-		OAS_ | gc.TINT8:
-		a = mips.AMOVB
-
-	case OAS_ | gc.TUINT8:
-		a = mips.AMOVBU
-
-	case OAS_ | gc.TINT16:
-		a = mips.AMOVH
-
-	case OAS_ | gc.TUINT16:
-		a = mips.AMOVHU
-
-	case OAS_ | gc.TINT32:
-		a = mips.AMOVW
-
-	case OAS_ | gc.TUINT32,
-		OAS_ | gc.TPTR32:
-		a = mips.AMOVWU
-
-	case OAS_ | gc.TINT64,
-		OAS_ | gc.TUINT64,
-		OAS_ | gc.TPTR64:
-		a = mips.AMOVV
-
-	case OAS_ | gc.TFLOAT32:
-		a = mips.AMOVF
-
-	case OAS_ | gc.TFLOAT64:
-		a = mips.AMOVD
-
-	case OADD_ | gc.TINT8,
-		OADD_ | gc.TUINT8,
-		OADD_ | gc.TINT16,
-		OADD_ | gc.TUINT16,
-		OADD_ | gc.TINT32,
-		OADD_ | gc.TUINT32,
-		OADD_ | gc.TPTR32:
-		a = mips.AADDU
-
-	case OADD_ | gc.TINT64,
-		OADD_ | gc.TUINT64,
-		OADD_ | gc.TPTR64:
-		a = mips.AADDVU
-
-	case OADD_ | gc.TFLOAT32:
-		a = mips.AADDF
-
-	case OADD_ | gc.TFLOAT64:
-		a = mips.AADDD
-
-	case OSUB_ | gc.TINT8,
-		OSUB_ | gc.TUINT8,
-		OSUB_ | gc.TINT16,
-		OSUB_ | gc.TUINT16,
-		OSUB_ | gc.TINT32,
-		OSUB_ | gc.TUINT32,
-		OSUB_ | gc.TPTR32:
-		a = mips.ASUBU
-
-	case OSUB_ | gc.TINT64,
-		OSUB_ | gc.TUINT64,
-		OSUB_ | gc.TPTR64:
-		a = mips.ASUBVU
-
-	case OSUB_ | gc.TFLOAT32:
-		a = mips.ASUBF
-
-	case OSUB_ | gc.TFLOAT64:
-		a = mips.ASUBD
-
-	case OMINUS_ | gc.TINT8,
-		OMINUS_ | gc.TUINT8,
-		OMINUS_ | gc.TINT16,
-		OMINUS_ | gc.TUINT16,
-		OMINUS_ | gc.TINT32,
-		OMINUS_ | gc.TUINT32,
-		OMINUS_ | gc.TPTR32,
-		OMINUS_ | gc.TINT64,
-		OMINUS_ | gc.TUINT64,
-		OMINUS_ | gc.TPTR64:
-		a = mips.ASUBVU
-
-	case OAND_ | gc.TINT8,
-		OAND_ | gc.TUINT8,
-		OAND_ | gc.TINT16,
-		OAND_ | gc.TUINT16,
-		OAND_ | gc.TINT32,
-		OAND_ | gc.TUINT32,
-		OAND_ | gc.TPTR32,
-		OAND_ | gc.TINT64,
-		OAND_ | gc.TUINT64,
-		OAND_ | gc.TPTR64:
-		a = mips.AAND
-
-	case OOR_ | gc.TINT8,
-		OOR_ | gc.TUINT8,
-		OOR_ | gc.TINT16,
-		OOR_ | gc.TUINT16,
-		OOR_ | gc.TINT32,
-		OOR_ | gc.TUINT32,
-		OOR_ | gc.TPTR32,
-		OOR_ | gc.TINT64,
-		OOR_ | gc.TUINT64,
-		OOR_ | gc.TPTR64:
-		a = mips.AOR
-
-	case OXOR_ | gc.TINT8,
-		OXOR_ | gc.TUINT8,
-		OXOR_ | gc.TINT16,
-		OXOR_ | gc.TUINT16,
-		OXOR_ | gc.TINT32,
-		OXOR_ | gc.TUINT32,
-		OXOR_ | gc.TPTR32,
-		OXOR_ | gc.TINT64,
-		OXOR_ | gc.TUINT64,
-		OXOR_ | gc.TPTR64:
-		a = mips.AXOR
-
-		// TODO(minux): handle rotates
-	//case CASE(OLROT, TINT8):
-	//case CASE(OLROT, TUINT8):
-	//case CASE(OLROT, TINT16):
-	//case CASE(OLROT, TUINT16):
-	//case CASE(OLROT, TINT32):
-	//case CASE(OLROT, TUINT32):
-	//case CASE(OLROT, TPTR32):
-	//case CASE(OLROT, TINT64):
-	//case CASE(OLROT, TUINT64):
-	//case CASE(OLROT, TPTR64):
-	//	a = 0//???; RLDC?
-	//	break;
-
-	case OLSH_ | gc.TINT8,
-		OLSH_ | gc.TUINT8,
-		OLSH_ | gc.TINT16,
-		OLSH_ | gc.TUINT16,
-		OLSH_ | gc.TINT32,
-		OLSH_ | gc.TUINT32,
-		OLSH_ | gc.TPTR32,
-		OLSH_ | gc.TINT64,
-		OLSH_ | gc.TUINT64,
-		OLSH_ | gc.TPTR64:
-		a = mips.ASLLV
-
-	case ORSH_ | gc.TUINT8,
-		ORSH_ | gc.TUINT16,
-		ORSH_ | gc.TUINT32,
-		ORSH_ | gc.TPTR32,
-		ORSH_ | gc.TUINT64,
-		ORSH_ | gc.TPTR64:
-		a = mips.ASRLV
-
-	case ORSH_ | gc.TINT8,
-		ORSH_ | gc.TINT16,
-		ORSH_ | gc.TINT32,
-		ORSH_ | gc.TINT64:
-		a = mips.ASRAV
-
-		// TODO(minux): handle rotates
-	//case CASE(ORROTC, TINT8):
-	//case CASE(ORROTC, TUINT8):
-	//case CASE(ORROTC, TINT16):
-	//case CASE(ORROTC, TUINT16):
-	//case CASE(ORROTC, TINT32):
-	//case CASE(ORROTC, TUINT32):
-	//case CASE(ORROTC, TINT64):
-	//case CASE(ORROTC, TUINT64):
-	//	a = 0//??? RLDC??
-	//	break;
-
-	case OHMUL_ | gc.TINT64:
-		a = mips.AMULV
-
-	case OHMUL_ | gc.TUINT64,
-		OHMUL_ | gc.TPTR64:
-		a = mips.AMULVU
-
-	case OMUL_ | gc.TINT8,
-		OMUL_ | gc.TINT16,
-		OMUL_ | gc.TINT32,
-		OMUL_ | gc.TINT64:
-		a = mips.AMULV
-
-	case OMUL_ | gc.TUINT8,
-		OMUL_ | gc.TUINT16,
-		OMUL_ | gc.TUINT32,
-		OMUL_ | gc.TPTR32,
-		OMUL_ | gc.TUINT64,
-		OMUL_ | gc.TPTR64:
-		a = mips.AMULVU
-
-	case OMUL_ | gc.TFLOAT32:
-		a = mips.AMULF
-
-	case OMUL_ | gc.TFLOAT64:
-		a = mips.AMULD
-
-	case ODIV_ | gc.TINT8,
-		ODIV_ | gc.TINT16,
-		ODIV_ | gc.TINT32,
-		ODIV_ | gc.TINT64:
-		a = mips.ADIVV
-
-	case ODIV_ | gc.TUINT8,
-		ODIV_ | gc.TUINT16,
-		ODIV_ | gc.TUINT32,
-		ODIV_ | gc.TPTR32,
-		ODIV_ | gc.TUINT64,
-		ODIV_ | gc.TPTR64:
-		a = mips.ADIVVU
-
-	case ODIV_ | gc.TFLOAT32:
-		a = mips.ADIVF
-
-	case ODIV_ | gc.TFLOAT64:
-		a = mips.ADIVD
-	}
-
-	return a
-}
-
-const (
-	ODynam   = 1 << 0
-	OAddable = 1 << 1
-)
-
-func xgen(n *gc.Node, a *gc.Node, o int) bool {
-	// TODO(minux)
-
-	return -1 != 0 /*TypeKind(100016)*/
-}
-
-func sudoclean() {
-	return
-}
-
-/*
- * generate code to compute address of n,
- * a reference to a (perhaps nested) field inside
- * an array or struct.
- * return 0 on failure, 1 on success.
- * on success, leaves usable address in a.
- *
- * caller is responsible for calling sudoclean
- * after successful sudoaddable,
- * to release the register used for a.
- */
-func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool {
-	// TODO(minux)
-
-	*a = obj.Addr{}
-	return false
-}
diff --git a/src/cmd/compile/internal/mips64/peep.go b/src/cmd/compile/internal/mips64/peep.go
deleted file mode 100644
index 6bb5158..0000000
--- a/src/cmd/compile/internal/mips64/peep.go
+++ /dev/null
@@ -1,772 +0,0 @@
-// Derived from Inferno utils/6c/peep.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package mips64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/mips"
-	"fmt"
-)
-
-var gactive uint32
-
-func peep(firstp *obj.Prog) {
-	g := gc.Flowstart(firstp, nil)
-	if g == nil {
-		return
-	}
-	gactive = 0
-
-	var p *obj.Prog
-	var r *gc.Flow
-	var t int
-loop1:
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		gc.Dumpit("loop1", g.Start, 0)
-	}
-
-	t = 0
-	for r = g.Start; r != nil; r = r.Link {
-		p = r.Prog
-
-		// TODO(austin) Handle smaller moves.  arm and amd64
-		// distinguish between moves that moves that *must*
-		// sign/zero extend and moves that don't care so they
-		// can eliminate moves that don't care without
-		// breaking moves that do care. This might let us
-		// simplify or remove the next peep loop, too.
-		if p.As == mips.AMOVV || p.As == mips.AMOVF || p.As == mips.AMOVD {
-			if regtyp(&p.To) {
-				// Try to eliminate reg->reg moves
-				if regtyp(&p.From) {
-					if isfreg(&p.From) == isfreg(&p.To) {
-						if copyprop(r) {
-							excise(r)
-							t++
-						} else if subprop(r) && copyprop(r) {
-							excise(r)
-							t++
-						}
-					}
-				}
-
-				// Convert uses to $0 to uses of R0 and
-				// propagate R0
-				if regzer(&p.From) {
-					if p.To.Type == obj.TYPE_REG && !isfreg(&p.To) {
-						p.From.Type = obj.TYPE_REG
-						p.From.Reg = mips.REGZERO
-						if copyprop(r) {
-							excise(r)
-							t++
-						} else if subprop(r) && copyprop(r) {
-							excise(r)
-							t++
-						}
-					}
-				}
-			}
-		}
-	}
-
-	if t != 0 {
-		goto loop1
-	}
-
-	/*
-	 * look for MOVB x,R; MOVB R,R (for small MOVs not handled above)
-	 */
-	var p1 *obj.Prog
-	var r1 *gc.Flow
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		default:
-			continue
-
-		case mips.AMOVH,
-			mips.AMOVHU,
-			mips.AMOVB,
-			mips.AMOVBU,
-			mips.AMOVW,
-			mips.AMOVWU:
-			if p.To.Type != obj.TYPE_REG {
-				continue
-			}
-		}
-
-		r1 = r.Link
-		if r1 == nil {
-			continue
-		}
-		p1 = r1.Prog
-		if p1.As != p.As {
-			continue
-		}
-		if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
-			continue
-		}
-		if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg {
-			continue
-		}
-		excise(r1)
-	}
-
-	gc.Flowend(g)
-}
-
-func excise(r *gc.Flow) {
-	p := r.Prog
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("%v ===delete===\n", p)
-	}
-	obj.Nopout(p)
-	gc.Ostats.Ndelmov++
-}
-
-// regzer returns true if a's value is 0 (a is R0 or $0)
-func regzer(a *obj.Addr) bool {
-	if a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_ADDR {
-		if a.Sym == nil && a.Reg == 0 {
-			if a.Offset == 0 {
-				return true
-			}
-		}
-	}
-	return a.Type == obj.TYPE_REG && a.Reg == mips.REGZERO
-}
-
-func regtyp(a *obj.Addr) bool {
-	// TODO(rsc): Floating point register exclusions?
-	return a.Type == obj.TYPE_REG && mips.REG_R0 <= a.Reg && a.Reg <= mips.REG_F31 && a.Reg != mips.REGZERO
-}
-
-func isfreg(a *obj.Addr) bool {
-	return mips.REG_F0 <= a.Reg && a.Reg <= mips.REG_F31
-}
-
-/*
- * the idea is to substitute
- * one register for another
- * from one MOV to another
- *	MOV	a, R1
- *	ADD	b, R1	/ no use of R2
- *	MOV	R1, R2
- * would be converted to
- *	MOV	a, R2
- *	ADD	b, R2
- *	MOV	R2, R1
- * hopefully, then the former or latter MOV
- * will be eliminated by copy propagation.
- *
- * r0 (the argument, not the register) is the MOV at the end of the
- * above sequences.  This returns 1 if it modified any instructions.
- */
-func subprop(r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	if !regtyp(v1) {
-		return false
-	}
-	v2 := &p.To
-	if !regtyp(v2) {
-		return false
-	}
-	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
-		if gc.Uniqs(r) == nil {
-			break
-		}
-		p = r.Prog
-		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
-			continue
-		}
-		if p.Info.Flags&gc.Call != 0 {
-			return false
-		}
-
-		if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite {
-			if p.To.Type == v1.Type {
-				if p.To.Reg == v1.Reg {
-					copysub(&p.To, v1, v2, true)
-					if gc.Debug['P'] != 0 {
-						fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
-						if p.From.Type == v2.Type {
-							fmt.Printf(" excise")
-						}
-						fmt.Printf("\n")
-					}
-
-					for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
-						p = r.Prog
-						copysub(&p.From, v1, v2, true)
-						copysub1(p, v1, v2, true)
-						copysub(&p.To, v1, v2, true)
-						if gc.Debug['P'] != 0 {
-							fmt.Printf("%v\n", r.Prog)
-						}
-					}
-
-					v1.Reg, v2.Reg = v2.Reg, v1.Reg
-					if gc.Debug['P'] != 0 {
-						fmt.Printf("%v last\n", r.Prog)
-					}
-					return true
-				}
-			}
-		}
-
-		if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) {
-			break
-		}
-		if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) {
-			break
-		}
-	}
-
-	return false
-}
-
-/*
- * The idea is to remove redundant copies.
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	use v2	return fail (v1->v2 move must remain)
- *	-----------------
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	set v2	return success (caller can remove v1->v2 move)
- */
-func copyprop(r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	v2 := &p.To
-	if copyas(v1, v2) {
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("eliminating self-move: %v\n", r0.Prog)
-		}
-		return true
-	}
-
-	gactive++
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog)
-	}
-	return copy1(v1, v2, r0.S1, false)
-}
-
-// copy1 replaces uses of v2 with v1 starting at r and returns true if
-// all uses were rewritten.
-func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool {
-	if uint32(r.Active) == gactive {
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("act set; return 1\n")
-		}
-		return true
-	}
-
-	r.Active = int32(gactive)
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("copy1 replace %v with %v f=%v\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f)
-	}
-	for ; r != nil; r = r.S1 {
-		p := r.Prog
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("%v", p)
-		}
-		if !f && gc.Uniqp(r) == nil {
-			// Multiple predecessors; conservatively
-			// assume v1 was set on other path
-			f = true
-
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; merge; f=%v", f)
-			}
-		}
-
-		switch t := copyu(p, v2, nil); t {
-		case 2: /* rar, can't split */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
-			}
-			return false
-
-		case 3: /* set */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
-			}
-			return true
-
-		case 1, /* used, substitute */
-			4: /* use and set */
-			if f {
-				if gc.Debug['P'] == 0 {
-					return false
-				}
-				if t == 4 {
-					fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				} else {
-					fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				}
-				return false
-			}
-
-			if copyu(p, v2, v1) != 0 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; sub fail; return 0\n")
-				}
-				return false
-			}
-
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p)
-			}
-			if t == 4 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
-				}
-				return true
-			}
-		}
-
-		if !f {
-			t := copyu(p, v1, nil)
-			if t == 2 || t == 3 || t == 4 {
-				f = true
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f)
-				}
-			}
-		}
-
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("\n")
-		}
-		if r.S2 != nil {
-			if !copy1(v1, v2, r.S2, f) {
-				return false
-			}
-		}
-	}
-
-	return true
-}
-
-// If s==nil, copyu returns the set/use of v in p; otherwise, it
-// modifies p to replace reads of v with reads of s and returns 0 for
-// success or non-zero for failure.
-//
-// If s==nil, copy returns one of the following values:
-// 	1 if v only used
-//	2 if v is set and used in one address (read-alter-rewrite;
-// 	  can't substitute)
-//	3 if v is only set
-//	4 if v is set in one address and used in another (so addresses
-// 	  can be rewritten independently)
-//	0 otherwise (not touched)
-func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
-	if p.From3Type() != obj.TYPE_NONE {
-		// never generates a from3
-		fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3))
-	}
-
-	switch p.As {
-	default:
-		fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As))
-		return 2
-
-	case obj.ANOP, /* read p->from, write p->to */
-		mips.AMOVV,
-		mips.AMOVF,
-		mips.AMOVD,
-		mips.AMOVH,
-		mips.AMOVHU,
-		mips.AMOVB,
-		mips.AMOVBU,
-		mips.AMOVW,
-		mips.AMOVWU,
-		mips.AMOVFD,
-		mips.AMOVDF,
-		mips.AMOVDW,
-		mips.AMOVWD,
-		mips.AMOVFW,
-		mips.AMOVWF,
-		mips.AMOVDV,
-		mips.AMOVVD,
-		mips.AMOVFV,
-		mips.AMOVVF,
-		mips.ATRUNCFV,
-		mips.ATRUNCDV,
-		mips.ATRUNCFW,
-		mips.ATRUNCDW:
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-
-			// Update only indirect uses of v in p->to
-			if !copyas(&p.To, v) {
-				if copysub(&p.To, v, s, true) {
-					return 1
-				}
-			}
-			return 0
-		}
-
-		if copyas(&p.To, v) {
-			// Fix up implicit from
-			if p.From.Type == obj.TYPE_NONE {
-				p.From = p.To
-			}
-			if copyau(&p.From, v) {
-				return 4
-			}
-			return 3
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau(&p.To, v) {
-			// p->to only indirectly uses v
-			return 1
-		}
-
-		return 0
-
-	case mips.ASGT, /* read p->from, read p->reg, write p->to */
-		mips.ASGTU,
-
-		mips.AADD,
-		mips.AADDU,
-		mips.ASUB,
-		mips.ASUBU,
-		mips.ASLL,
-		mips.ASRL,
-		mips.ASRA,
-		mips.AOR,
-		mips.ANOR,
-		mips.AAND,
-		mips.AXOR,
-
-		mips.AADDV,
-		mips.AADDVU,
-		mips.ASUBV,
-		mips.ASUBVU,
-		mips.ASLLV,
-		mips.ASRLV,
-		mips.ASRAV,
-
-		mips.AADDF,
-		mips.AADDD,
-		mips.ASUBF,
-		mips.ASUBD,
-		mips.AMULF,
-		mips.AMULD,
-		mips.ADIVF,
-		mips.ADIVD:
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if copysub1(p, v, s, true) {
-				return 1
-			}
-
-			// Update only indirect uses of v in p->to
-			if !copyas(&p.To, v) {
-				if copysub(&p.To, v, s, true) {
-					return 1
-				}
-			}
-			return 0
-		}
-
-		if copyas(&p.To, v) {
-			if p.Reg == 0 {
-				// Fix up implicit reg (e.g., ADD
-				// R3,R4 -> ADD R3,R4,R4) so we can
-				// update reg and to separately.
-				p.Reg = p.To.Reg
-			}
-
-			if copyau(&p.From, v) {
-				return 4
-			}
-			if copyau1(p, v) {
-				return 4
-			}
-			return 3
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau1(p, v) {
-			return 1
-		}
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case obj.ACHECKNIL, /* read p->from */
-		mips.ABEQ, /* read p->from, read p->reg */
-		mips.ABNE,
-		mips.ABGTZ,
-		mips.ABGEZ,
-		mips.ABLTZ,
-		mips.ABLEZ,
-
-		mips.ACMPEQD,
-		mips.ACMPEQF,
-		mips.ACMPGED,
-		mips.ACMPGEF,
-		mips.ACMPGTD,
-		mips.ACMPGTF,
-		mips.ABFPF,
-		mips.ABFPT,
-
-		mips.AMUL,
-		mips.AMULU,
-		mips.ADIV,
-		mips.ADIVU,
-		mips.AMULV,
-		mips.AMULVU,
-		mips.ADIVV,
-		mips.ADIVVU:
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if copysub1(p, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau1(p, v) {
-			return 1
-		}
-		return 0
-
-	case mips.AJMP: /* read p->to */
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case mips.ARET: /* funny */
-		if s != nil {
-			return 0
-		}
-
-		// All registers die at this point, so claim
-		// everything is set (and not used).
-		return 3
-
-	case mips.AJAL: /* funny */
-		if v.Type == obj.TYPE_REG {
-			// TODO(rsc): REG_R0 and REG_F0 used to be
-			// (when register numbers started at 0) exregoffset and exfregoffset,
-			// which are unset entirely.
-			// It's strange that this handles R0 and F0 differently from the other
-			// registers. Possible failure to optimize?
-			if mips.REG_R0 < v.Reg && v.Reg <= mips.REG_R31 {
-				return 2
-			}
-			if v.Reg == mips.REGARG {
-				return 2
-			}
-			if mips.REG_F0 < v.Reg && v.Reg <= mips.REG_F31 {
-				return 2
-			}
-		}
-
-		if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
-			return 2
-		}
-
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.To, v) {
-			return 4
-		}
-		return 3
-
-	// R0 is zero, used by DUFFZERO, cannot be substituted.
-	// R1 is ptr to memory, used and set, cannot be substituted.
-	case obj.ADUFFZERO:
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == 0 {
-				return 1
-			}
-			if v.Reg == 1 {
-				return 2
-			}
-		}
-
-		return 0
-
-	// R1, R2 are ptr to src, dst, used and set, cannot be substituted.
-	// R3 is scratch, set by DUFFCOPY, cannot be substituted.
-	case obj.ADUFFCOPY:
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == 1 || v.Reg == 2 {
-				return 2
-			}
-			if v.Reg == 3 {
-				return 3
-			}
-		}
-
-		return 0
-
-	case obj.ATEXT: /* funny */
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == mips.REGARG {
-				return 3
-			}
-		}
-		return 0
-
-	case obj.APCDATA,
-		obj.AFUNCDATA,
-		obj.AVARDEF,
-		obj.AVARKILL,
-		obj.AVARLIVE,
-		obj.AUSEFIELD:
-		return 0
-	}
-}
-
-// copyas returns 1 if a and v address the same register.
-//
-// If a is the from operand, this means this operation reads the
-// register in v. If a is the to operand, this means this operation
-// writes the register in v.
-func copyas(a *obj.Addr, v *obj.Addr) bool {
-	if regtyp(v) {
-		if a.Type == v.Type {
-			if a.Reg == v.Reg {
-				return true
-			}
-		}
-	}
-	return false
-}
-
-// copyau returns 1 if a either directly or indirectly addresses the
-// same register as v.
-//
-// If a is the from operand, this means this operation reads the
-// register in v. If a is the to operand, this means the operation
-// either reads or writes the register in v (if !copyas(a, v), then
-// the operation reads the register in v).
-func copyau(a *obj.Addr, v *obj.Addr) bool {
-	if copyas(a, v) {
-		return true
-	}
-	if v.Type == obj.TYPE_REG {
-		if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) {
-			if v.Reg == a.Reg {
-				return true
-			}
-		}
-	}
-	return false
-}
-
-// copyau1 returns true if p->reg references the same register as v and v
-// is a direct reference.
-func copyau1(p *obj.Prog, v *obj.Addr) bool {
-	return regtyp(v) && v.Reg != 0 && p.Reg == v.Reg
-}
-
-// copysub replaces v with s in a if f==true or indicates it if could if f==false.
-// Returns true on failure to substitute (it always succeeds on mips).
-// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing.
-func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool {
-	if f && copyau(a, v) {
-		a.Reg = s.Reg
-	}
-	return false
-}
-
-// copysub1 replaces v with s in p1->reg if f==true or indicates if it could if f==false.
-// Returns true on failure to substitute (it always succeeds on mips).
-// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing.
-func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool {
-	if f && copyau1(p1, v) {
-		p1.Reg = s.Reg
-	}
-	return false
-}
-
-func sameaddr(a *obj.Addr, v *obj.Addr) bool {
-	if a.Type != v.Type {
-		return false
-	}
-	if regtyp(v) && a.Reg == v.Reg {
-		return true
-	}
-	if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM {
-		if v.Offset == a.Offset {
-			return true
-		}
-	}
-	return false
-}
-
-func smallindir(a *obj.Addr, reg *obj.Addr) bool {
-	return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096
-}
-
-func stackaddr(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_REG && a.Reg == mips.REGSP
-}
diff --git a/src/cmd/compile/internal/mips64/prog.go b/src/cmd/compile/internal/mips64/prog.go
index caf8482..74c735c 100644
--- a/src/cmd/compile/internal/mips64/prog.go
+++ b/src/cmd/compile/internal/mips64/prog.go
@@ -24,14 +24,13 @@ const (
 // size variants of an operation even if we just use a subset.
 //
 // The table is formatted for 8-space tabs.
-var progtable = [mips.ALAST & obj.AMask]obj.ProgInfo{
+var progtable = [mips.ALAST & obj.AMask]gc.ProgInfo{
 	obj.ATYPE:     {Flags: gc.Pseudo | gc.Skip},
 	obj.ATEXT:     {Flags: gc.Pseudo},
 	obj.AFUNCDATA: {Flags: gc.Pseudo},
 	obj.APCDATA:   {Flags: gc.Pseudo},
 	obj.AUNDEF:    {Flags: gc.Break},
 	obj.AUSEFIELD: {Flags: gc.OK},
-	obj.ACHECKNIL: {Flags: gc.LeftRead},
 	obj.AVARDEF:   {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARKILL:  {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARLIVE:  {Flags: gc.Pseudo | gc.LeftRead},
@@ -135,9 +134,8 @@ var progtable = [mips.ALAST & obj.AMask]obj.ProgInfo{
 	obj.ADUFFCOPY:          {Flags: gc.Call},
 }
 
-func proginfo(p *obj.Prog) {
-	info := &p.Info
-	*info = progtable[p.As&obj.AMask]
+func proginfo(p *obj.Prog) gc.ProgInfo {
+	info := progtable[p.As&obj.AMask]
 	if info.Flags == 0 {
 		gc.Fatalf("proginfo: unknown instruction %v", p)
 	}
@@ -147,28 +145,10 @@ func proginfo(p *obj.Prog) {
 		info.Flags |= gc.RightRead /*CanRegRead |*/
 	}
 
-	if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 {
-		info.Regindex |= RtoB(int(p.From.Reg))
-	}
-
-	if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 {
-		info.Regindex |= RtoB(int(p.To.Reg))
-	}
-
 	if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) {
 		info.Flags &^= gc.LeftRead
 		info.Flags |= gc.LeftAddr
 	}
 
-	if p.As == obj.ADUFFZERO {
-		info.Reguse |= 1<<0 | RtoB(mips.REGRT1)
-		info.Regset |= RtoB(mips.REGRT1)
-	}
-
-	if p.As == obj.ADUFFCOPY {
-		// TODO(austin) Revisit when duffcopy is implemented
-		info.Reguse |= RtoB(mips.REGRT1) | RtoB(mips.REGRT2) | RtoB(mips.REG_R3)
-
-		info.Regset |= RtoB(mips.REGRT1) | RtoB(mips.REGRT2)
-	}
+	return info
 }
diff --git a/src/cmd/compile/internal/mips64/reg.go b/src/cmd/compile/internal/mips64/reg.go
deleted file mode 100644
index 2d2a773..0000000
--- a/src/cmd/compile/internal/mips64/reg.go
+++ /dev/null
@@ -1,162 +0,0 @@
-// Derived from Inferno utils/6c/reg.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package mips64
-
-import "cmd/internal/obj/mips"
-import "cmd/compile/internal/gc"
-
-const (
-	NREGVAR = 64 /* 32 general + 32 floating */
-)
-
-var regname = []string{
-	".R0",
-	".R1",
-	".R2",
-	".R3",
-	".R4",
-	".R5",
-	".R6",
-	".R7",
-	".R8",
-	".R9",
-	".R10",
-	".R11",
-	".R12",
-	".R13",
-	".R14",
-	".R15",
-	".R16",
-	".R17",
-	".R18",
-	".R19",
-	".R20",
-	".R21",
-	".R22",
-	".R23",
-	".R24",
-	".R25",
-	".R26",
-	".R27",
-	".R28",
-	".R29",
-	".R30",
-	".R31",
-	".F0",
-	".F1",
-	".F2",
-	".F3",
-	".F4",
-	".F5",
-	".F6",
-	".F7",
-	".F8",
-	".F9",
-	".F10",
-	".F11",
-	".F12",
-	".F13",
-	".F14",
-	".F15",
-	".F16",
-	".F17",
-	".F18",
-	".F19",
-	".F20",
-	".F21",
-	".F22",
-	".F23",
-	".F24",
-	".F25",
-	".F26",
-	".F27",
-	".F28",
-	".F29",
-	".F30",
-	".F31",
-}
-
-func regnames(n *int) []string {
-	*n = NREGVAR
-	return regname
-}
-
-func excludedregs() uint64 {
-	// Exclude registers with fixed functions
-	regbits := 1<<0 | RtoB(mips.REGSP) | RtoB(mips.REGG) | RtoB(mips.REGSB) | RtoB(mips.REGTMP) | RtoB(mips.REGLINK) | RtoB(mips.REG_R26) | RtoB(mips.REG_R27)
-
-	// Also exclude floating point registers with fixed constants
-	regbits |= RtoB(mips.FREGZERO) | RtoB(mips.FREGHALF) | RtoB(mips.FREGONE) | RtoB(mips.FREGTWO)
-
-	return regbits
-}
-
-func doregbits(r int) uint64 {
-	return 0
-}
-
-/*
- * track register variables including external registers:
- *	bit	reg
- *	0	R0
- *	1	R1
- *	...	...
- *	31	R31
- *	32+0	F0
- *	32+1	F1
- *	...	...
- *	32+31	F31
- */
-func RtoB(r int) uint64 {
-	if r > mips.REG_R0 && r <= mips.REG_R31 {
-		return 1 << uint(r-mips.REG_R0)
-	}
-	if r >= mips.REG_F0 && r <= mips.REG_F31 {
-		return 1 << uint(32+r-mips.REG_F0)
-	}
-	return 0
-}
-
-func BtoR(b uint64) int {
-	b &= 0xffffffff
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + mips.REG_R0
-}
-
-func BtoF(b uint64) int {
-	b >>= 32
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + mips.REG_F0
-}
diff --git a/src/cmd/compile/internal/mips64/ssa.go b/src/cmd/compile/internal/mips64/ssa.go
new file mode 100644
index 0000000..1432c6c
--- /dev/null
+++ b/src/cmd/compile/internal/mips64/ssa.go
@@ -0,0 +1,672 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mips64
+
+import (
+	"math"
+
+	"cmd/compile/internal/gc"
+	"cmd/compile/internal/ssa"
+	"cmd/internal/obj"
+	"cmd/internal/obj/mips"
+)
+
+// isFPreg returns whether r is an FP register
+func isFPreg(r int16) bool {
+	return mips.REG_F0 <= r && r <= mips.REG_F31
+}
+
+// isHILO returns whether r is HI or LO register
+func isHILO(r int16) bool {
+	return r == mips.REG_HI || r == mips.REG_LO
+}
+
+// loadByType returns the load instruction of the given type.
+func loadByType(t ssa.Type, r int16) obj.As {
+	if isFPreg(r) {
+		if t.Size() == 4 { // float32 or int32
+			return mips.AMOVF
+		} else { // float64 or int64
+			return mips.AMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			if t.IsSigned() {
+				return mips.AMOVB
+			} else {
+				return mips.AMOVBU
+			}
+		case 2:
+			if t.IsSigned() {
+				return mips.AMOVH
+			} else {
+				return mips.AMOVHU
+			}
+		case 4:
+			if t.IsSigned() {
+				return mips.AMOVW
+			} else {
+				return mips.AMOVWU
+			}
+		case 8:
+			return mips.AMOVV
+		}
+	}
+	panic("bad load type")
+}
+
+// storeByType returns the store instruction of the given type.
+func storeByType(t ssa.Type, r int16) obj.As {
+	if isFPreg(r) {
+		if t.Size() == 4 { // float32 or int32
+			return mips.AMOVF
+		} else { // float64 or int64
+			return mips.AMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			return mips.AMOVB
+		case 2:
+			return mips.AMOVH
+		case 4:
+			return mips.AMOVW
+		case 8:
+			return mips.AMOVV
+		}
+	}
+	panic("bad store type")
+}
+
+func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+	s.SetLineno(v.Line)
+	switch v.Op {
+	case ssa.OpInitMem:
+		// memory arg needs no code
+	case ssa.OpArg:
+		// input args need no code
+	case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
+		// nothing to do
+	case ssa.OpCopy, ssa.OpMIPS64MOVVconvert, ssa.OpMIPS64MOVVreg:
+		if v.Type.IsMemory() {
+			return
+		}
+		x := v.Args[0].Reg()
+		y := v.Reg()
+		if x == y {
+			return
+		}
+		as := mips.AMOVV
+		if isFPreg(x) && isFPreg(y) {
+			as = mips.AMOVD
+		}
+		p := gc.Prog(as)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = y
+		if isHILO(x) && isHILO(y) || isHILO(x) && isFPreg(y) || isFPreg(x) && isHILO(y) {
+			// cannot move between special registers, use TMP as intermediate
+			p.To.Reg = mips.REGTMP
+			p = gc.Prog(mips.AMOVV)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = mips.REGTMP
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = y
+		}
+	case ssa.OpMIPS64MOVVnop:
+		if v.Reg() != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		// nothing to do
+	case ssa.OpLoadReg:
+		if v.Type.IsFlags() {
+			v.Fatalf("load flags not implemented: %v", v.LongString())
+			return
+		}
+		r := v.Reg()
+		p := gc.Prog(loadByType(v.Type, r))
+		gc.AddrAuto(&p.From, v.Args[0])
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+		if isHILO(r) {
+			// cannot directly load, load to TMP and move
+			p.To.Reg = mips.REGTMP
+			p = gc.Prog(mips.AMOVV)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = mips.REGTMP
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = r
+		}
+	case ssa.OpPhi:
+		gc.CheckLoweredPhi(v)
+	case ssa.OpStoreReg:
+		if v.Type.IsFlags() {
+			v.Fatalf("store flags not implemented: %v", v.LongString())
+			return
+		}
+		r := v.Args[0].Reg()
+		if isHILO(r) {
+			// cannot directly store, move to TMP and store
+			p := gc.Prog(mips.AMOVV)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = r
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = mips.REGTMP
+			r = mips.REGTMP
+		}
+		p := gc.Prog(storeByType(v.Type, r))
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r
+		gc.AddrAuto(&p.To, v)
+	case ssa.OpMIPS64ADDV,
+		ssa.OpMIPS64SUBV,
+		ssa.OpMIPS64AND,
+		ssa.OpMIPS64OR,
+		ssa.OpMIPS64XOR,
+		ssa.OpMIPS64NOR,
+		ssa.OpMIPS64SLLV,
+		ssa.OpMIPS64SRLV,
+		ssa.OpMIPS64SRAV,
+		ssa.OpMIPS64ADDF,
+		ssa.OpMIPS64ADDD,
+		ssa.OpMIPS64SUBF,
+		ssa.OpMIPS64SUBD,
+		ssa.OpMIPS64MULF,
+		ssa.OpMIPS64MULD,
+		ssa.OpMIPS64DIVF,
+		ssa.OpMIPS64DIVD:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPS64SGT,
+		ssa.OpMIPS64SGTU:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPS64ADDVconst,
+		ssa.OpMIPS64SUBVconst,
+		ssa.OpMIPS64ANDconst,
+		ssa.OpMIPS64ORconst,
+		ssa.OpMIPS64XORconst,
+		ssa.OpMIPS64NORconst,
+		ssa.OpMIPS64SLLVconst,
+		ssa.OpMIPS64SRLVconst,
+		ssa.OpMIPS64SRAVconst,
+		ssa.OpMIPS64SGTconst,
+		ssa.OpMIPS64SGTUconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPS64MULV,
+		ssa.OpMIPS64MULVU,
+		ssa.OpMIPS64DIVV,
+		ssa.OpMIPS64DIVVU:
+		// result in hi,lo
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.Reg = v.Args[0].Reg()
+	case ssa.OpMIPS64MOVVconst:
+		r := v.Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+		if isFPreg(r) || isHILO(r) {
+			// cannot move into FP or special registers, use TMP as intermediate
+			p.To.Reg = mips.REGTMP
+			p = gc.Prog(mips.AMOVV)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = mips.REGTMP
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = r
+		}
+	case ssa.OpMIPS64MOVFconst,
+		ssa.OpMIPS64MOVDconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_FCONST
+		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPS64CMPEQF,
+		ssa.OpMIPS64CMPEQD,
+		ssa.OpMIPS64CMPGEF,
+		ssa.OpMIPS64CMPGED,
+		ssa.OpMIPS64CMPGTF,
+		ssa.OpMIPS64CMPGTD:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.Reg = v.Args[1].Reg()
+	case ssa.OpMIPS64MOVVaddr:
+		p := gc.Prog(mips.AMOVV)
+		p.From.Type = obj.TYPE_ADDR
+		var wantreg string
+		// MOVV $sym+off(base), R
+		// the assembler expands it as the following:
+		// - base is SP: add constant offset to SP (R29)
+		//               when constant is large, tmp register (R23) may be used
+		// - base is SB: load external address with relocation
+		switch v.Aux.(type) {
+		default:
+			v.Fatalf("aux is of unknown type %T", v.Aux)
+		case *ssa.ExternSymbol:
+			wantreg = "SB"
+			gc.AddAux(&p.From, v)
+		case *ssa.ArgSymbol, *ssa.AutoSymbol:
+			wantreg = "SP"
+			gc.AddAux(&p.From, v)
+		case nil:
+			// No sym, just MOVV $off(SP), R
+			wantreg = "SP"
+			p.From.Reg = mips.REGSP
+			p.From.Offset = v.AuxInt
+		}
+		if reg := v.Args[0].RegName(); reg != wantreg {
+			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
+		}
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPS64MOVBload,
+		ssa.OpMIPS64MOVBUload,
+		ssa.OpMIPS64MOVHload,
+		ssa.OpMIPS64MOVHUload,
+		ssa.OpMIPS64MOVWload,
+		ssa.OpMIPS64MOVWUload,
+		ssa.OpMIPS64MOVVload,
+		ssa.OpMIPS64MOVFload,
+		ssa.OpMIPS64MOVDload:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPS64MOVBstore,
+		ssa.OpMIPS64MOVHstore,
+		ssa.OpMIPS64MOVWstore,
+		ssa.OpMIPS64MOVVstore,
+		ssa.OpMIPS64MOVFstore,
+		ssa.OpMIPS64MOVDstore:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpMIPS64MOVBstorezero,
+		ssa.OpMIPS64MOVHstorezero,
+		ssa.OpMIPS64MOVWstorezero,
+		ssa.OpMIPS64MOVVstorezero:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = mips.REGZERO
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpMIPS64MOVBreg,
+		ssa.OpMIPS64MOVBUreg,
+		ssa.OpMIPS64MOVHreg,
+		ssa.OpMIPS64MOVHUreg,
+		ssa.OpMIPS64MOVWreg,
+		ssa.OpMIPS64MOVWUreg:
+		a := v.Args[0]
+		for a.Op == ssa.OpCopy || a.Op == ssa.OpMIPS64MOVVreg {
+			a = a.Args[0]
+		}
+		if a.Op == ssa.OpLoadReg {
+			t := a.Type
+			switch {
+			case v.Op == ssa.OpMIPS64MOVBreg && t.Size() == 1 && t.IsSigned(),
+				v.Op == ssa.OpMIPS64MOVBUreg && t.Size() == 1 && !t.IsSigned(),
+				v.Op == ssa.OpMIPS64MOVHreg && t.Size() == 2 && t.IsSigned(),
+				v.Op == ssa.OpMIPS64MOVHUreg && t.Size() == 2 && !t.IsSigned(),
+				v.Op == ssa.OpMIPS64MOVWreg && t.Size() == 4 && t.IsSigned(),
+				v.Op == ssa.OpMIPS64MOVWUreg && t.Size() == 4 && !t.IsSigned():
+				// arg is a proper-typed load, already zero/sign-extended, don't extend again
+				if v.Reg() == v.Args[0].Reg() {
+					return
+				}
+				p := gc.Prog(mips.AMOVV)
+				p.From.Type = obj.TYPE_REG
+				p.From.Reg = v.Args[0].Reg()
+				p.To.Type = obj.TYPE_REG
+				p.To.Reg = v.Reg()
+				return
+			default:
+			}
+		}
+		fallthrough
+	case ssa.OpMIPS64MOVWF,
+		ssa.OpMIPS64MOVWD,
+		ssa.OpMIPS64TRUNCFW,
+		ssa.OpMIPS64TRUNCDW,
+		ssa.OpMIPS64MOVVF,
+		ssa.OpMIPS64MOVVD,
+		ssa.OpMIPS64TRUNCFV,
+		ssa.OpMIPS64TRUNCDV,
+		ssa.OpMIPS64MOVFD,
+		ssa.OpMIPS64MOVDF,
+		ssa.OpMIPS64NEGF,
+		ssa.OpMIPS64NEGD:
+		p := gc.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.OpMIPS64NEGV:
+		// SUB from REGZERO
+		p := gc.Prog(mips.ASUBVU)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.Reg = mips.REGZERO
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpMIPS64DUFFZERO:
+		// runtime.duffzero expects start address - 8 in R1
+		p := gc.Prog(mips.ASUBVU)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = 8
+		p.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = mips.REG_R1
+		p = gc.Prog(obj.ADUFFZERO)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
+		p.To.Offset = v.AuxInt
+	case ssa.OpMIPS64LoweredZero:
+		// SUBV	$8, R1
+		// MOVV	R0, 8(R1)
+		// ADDV	$8, R1
+		// BNE	Rarg1, R1, -2(PC)
+		// arg1 is the address of the last element to zero
+		var sz int64
+		var mov obj.As
+		switch {
+		case v.AuxInt%8 == 0:
+			sz = 8
+			mov = mips.AMOVV
+		case v.AuxInt%4 == 0:
+			sz = 4
+			mov = mips.AMOVW
+		case v.AuxInt%2 == 0:
+			sz = 2
+			mov = mips.AMOVH
+		default:
+			sz = 1
+			mov = mips.AMOVB
+		}
+		p := gc.Prog(mips.ASUBVU)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = sz
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = mips.REG_R1
+		p2 := gc.Prog(mov)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = mips.REGZERO
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = mips.REG_R1
+		p2.To.Offset = sz
+		p3 := gc.Prog(mips.AADDVU)
+		p3.From.Type = obj.TYPE_CONST
+		p3.From.Offset = sz
+		p3.To.Type = obj.TYPE_REG
+		p3.To.Reg = mips.REG_R1
+		p4 := gc.Prog(mips.ABNE)
+		p4.From.Type = obj.TYPE_REG
+		p4.From.Reg = v.Args[1].Reg()
+		p4.Reg = mips.REG_R1
+		p4.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p4, p2)
+	case ssa.OpMIPS64LoweredMove:
+		// SUBV	$8, R1
+		// MOVV	8(R1), Rtmp
+		// MOVV	Rtmp, (R2)
+		// ADDV	$8, R1
+		// ADDV	$8, R2
+		// BNE	Rarg2, R1, -4(PC)
+		// arg2 is the address of the last element of src
+		var sz int64
+		var mov obj.As
+		switch {
+		case v.AuxInt%8 == 0:
+			sz = 8
+			mov = mips.AMOVV
+		case v.AuxInt%4 == 0:
+			sz = 4
+			mov = mips.AMOVW
+		case v.AuxInt%2 == 0:
+			sz = 2
+			mov = mips.AMOVH
+		default:
+			sz = 1
+			mov = mips.AMOVB
+		}
+		p := gc.Prog(mips.ASUBVU)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = sz
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = mips.REG_R1
+		p2 := gc.Prog(mov)
+		p2.From.Type = obj.TYPE_MEM
+		p2.From.Reg = mips.REG_R1
+		p2.From.Offset = sz
+		p2.To.Type = obj.TYPE_REG
+		p2.To.Reg = mips.REGTMP
+		p3 := gc.Prog(mov)
+		p3.From.Type = obj.TYPE_REG
+		p3.From.Reg = mips.REGTMP
+		p3.To.Type = obj.TYPE_MEM
+		p3.To.Reg = mips.REG_R2
+		p4 := gc.Prog(mips.AADDVU)
+		p4.From.Type = obj.TYPE_CONST
+		p4.From.Offset = sz
+		p4.To.Type = obj.TYPE_REG
+		p4.To.Reg = mips.REG_R1
+		p5 := gc.Prog(mips.AADDVU)
+		p5.From.Type = obj.TYPE_CONST
+		p5.From.Offset = sz
+		p5.To.Type = obj.TYPE_REG
+		p5.To.Reg = mips.REG_R2
+		p6 := gc.Prog(mips.ABNE)
+		p6.From.Type = obj.TYPE_REG
+		p6.From.Reg = v.Args[2].Reg()
+		p6.Reg = mips.REG_R1
+		p6.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p6, p2)
+	case ssa.OpMIPS64CALLstatic:
+		if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
+			// Deferred calls will appear to be returning to
+			// the CALL deferreturn(SB) that we are about to emit.
+			// However, the stack trace code will show the line
+			// of the instruction byte before the return PC.
+			// To avoid that being an unrelated instruction,
+			// insert an actual hardware NOP that will have the right line number.
+			// This is different from obj.ANOP, which is a virtual no-op
+			// that doesn't make it into the instruction stream.
+			ginsnop()
+		}
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym))
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpMIPS64CALLclosure:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Offset = 0
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpMIPS64CALLdefer:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Deferproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpMIPS64CALLgo:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Newproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpMIPS64CALLinter:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Offset = 0
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpMIPS64LoweredNilCheck:
+		// Issue a load which will fault if arg is nil.
+		p := gc.Prog(mips.AMOVB)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = mips.REGTMP
+		if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
+			gc.Warnl(v.Line, "generated nil check")
+		}
+	case ssa.OpVarDef:
+		gc.Gvardef(v.Aux.(*gc.Node))
+	case ssa.OpVarKill:
+		gc.Gvarkill(v.Aux.(*gc.Node))
+	case ssa.OpVarLive:
+		gc.Gvarlive(v.Aux.(*gc.Node))
+	case ssa.OpKeepAlive:
+		gc.KeepAlive(v)
+	case ssa.OpMIPS64FPFlagTrue,
+		ssa.OpMIPS64FPFlagFalse:
+		// MOVV	$0, r
+		// BFPF	2(PC)
+		// MOVV	$1, r
+		branch := mips.ABFPF
+		if v.Op == ssa.OpMIPS64FPFlagFalse {
+			branch = mips.ABFPT
+		}
+		p := gc.Prog(mips.AMOVV)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = mips.REGZERO
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+		p2 := gc.Prog(branch)
+		p2.To.Type = obj.TYPE_BRANCH
+		p3 := gc.Prog(mips.AMOVV)
+		p3.From.Type = obj.TYPE_CONST
+		p3.From.Offset = 1
+		p3.To.Type = obj.TYPE_REG
+		p3.To.Reg = v.Reg()
+		p4 := gc.Prog(obj.ANOP) // not a machine instruction, for branch to land
+		gc.Patch(p2, p4)
+	case ssa.OpSelect0, ssa.OpSelect1:
+		// nothing to do
+	case ssa.OpMIPS64LoweredGetClosurePtr:
+		// Closure pointer is R22 (mips.REGCTXT).
+		gc.CheckLoweredGetClosurePtr(v)
+	default:
+		v.Fatalf("genValue not implemented: %s", v.LongString())
+	}
+}
+
+var blockJump = map[ssa.BlockKind]struct {
+	asm, invasm obj.As
+}{
+	ssa.BlockMIPS64EQ:  {mips.ABEQ, mips.ABNE},
+	ssa.BlockMIPS64NE:  {mips.ABNE, mips.ABEQ},
+	ssa.BlockMIPS64LTZ: {mips.ABLTZ, mips.ABGEZ},
+	ssa.BlockMIPS64GEZ: {mips.ABGEZ, mips.ABLTZ},
+	ssa.BlockMIPS64LEZ: {mips.ABLEZ, mips.ABGTZ},
+	ssa.BlockMIPS64GTZ: {mips.ABGTZ, mips.ABLEZ},
+	ssa.BlockMIPS64FPT: {mips.ABFPT, mips.ABFPF},
+	ssa.BlockMIPS64FPF: {mips.ABFPF, mips.ABFPT},
+}
+
+func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+	s.SetLineno(b.Line)
+
+	switch b.Kind {
+	case ssa.BlockPlain:
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+	case ssa.BlockDefer:
+		// defer returns in R1:
+		// 0 if we should continue executing
+		// 1 if we should jump to deferreturn call
+		p := gc.Prog(mips.ABNE)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = mips.REGZERO
+		p.Reg = mips.REG_R1
+		p.To.Type = obj.TYPE_BRANCH
+		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+	case ssa.BlockExit:
+		gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
+	case ssa.BlockRet:
+		gc.Prog(obj.ARET)
+	case ssa.BlockRetJmp:
+		p := gc.Prog(obj.ARET)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
+	case ssa.BlockMIPS64EQ, ssa.BlockMIPS64NE,
+		ssa.BlockMIPS64LTZ, ssa.BlockMIPS64GEZ,
+		ssa.BlockMIPS64LEZ, ssa.BlockMIPS64GTZ,
+		ssa.BlockMIPS64FPT, ssa.BlockMIPS64FPF:
+		jmp := blockJump[b.Kind]
+		var p *obj.Prog
+		switch next {
+		case b.Succs[0].Block():
+			p = gc.Prog(jmp.invasm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		case b.Succs[1].Block():
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		default:
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+			q := gc.Prog(obj.AJMP)
+			q.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
+		}
+		if !b.Control.Type.IsFlags() {
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = b.Control.Reg()
+		}
+	default:
+		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
+	}
+}
diff --git a/src/cmd/compile/internal/ppc64/cgen.go b/src/cmd/compile/internal/ppc64/cgen.go
deleted file mode 100644
index f4cc9c4..0000000
--- a/src/cmd/compile/internal/ppc64/cgen.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package ppc64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/ppc64"
-)
-
-func blockcopy(n, res *gc.Node, osrc, odst, w int64) {
-	// determine alignment.
-	// want to avoid unaligned access, so have to use
-	// smaller operations for less aligned types.
-	// for example moving [4]byte must use 4 MOVB not 1 MOVW.
-	align := int(n.Type.Align)
-
-	var op obj.As
-	switch align {
-	default:
-		gc.Fatalf("sgen: invalid alignment %d for %v", align, n.Type)
-
-	case 1:
-		op = ppc64.AMOVBU
-
-	case 2:
-		op = ppc64.AMOVHU
-
-	case 4:
-		op = ppc64.AMOVWZU // there is no lwau, only lwaux
-
-	case 8:
-		op = ppc64.AMOVDU
-	}
-
-	if w%int64(align) != 0 {
-		gc.Fatalf("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type)
-	}
-	c := int32(w / int64(align))
-
-	// if we are copying forward on the stack and
-	// the src and dst overlap, then reverse direction
-	dir := align
-
-	if osrc < odst && odst < osrc+w {
-		dir = -dir
-	}
-
-	var dst gc.Node
-	var src gc.Node
-	if n.Ullman >= res.Ullman {
-		gc.Agenr(n, &dst, res) // temporarily use dst
-		gc.Regalloc(&src, gc.Types[gc.Tptr], nil)
-		gins(ppc64.AMOVD, &dst, &src)
-		if res.Op == gc.ONAME {
-			gc.Gvardef(res)
-		}
-		gc.Agen(res, &dst)
-	} else {
-		if res.Op == gc.ONAME {
-			gc.Gvardef(res)
-		}
-		gc.Agenr(res, &dst, res)
-		gc.Agenr(n, &src, nil)
-	}
-
-	var tmp gc.Node
-	gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil)
-
-	// set up end marker
-	var nend gc.Node
-
-	// move src and dest to the end of block if necessary
-	if dir < 0 {
-		if c >= 4 {
-			gc.Regalloc(&nend, gc.Types[gc.Tptr], nil)
-			gins(ppc64.AMOVD, &src, &nend)
-		}
-
-		p := gins(ppc64.AADD, nil, &src)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-
-		p = gins(ppc64.AADD, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-	} else {
-		p := gins(ppc64.AADD, nil, &src)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = int64(-dir)
-
-		p = gins(ppc64.AADD, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = int64(-dir)
-
-		if c >= 4 {
-			gc.Regalloc(&nend, gc.Types[gc.Tptr], nil)
-			p := gins(ppc64.AMOVD, &src, &nend)
-			p.From.Type = obj.TYPE_ADDR
-			p.From.Offset = w
-		}
-	}
-
-	// move
-	// TODO: enable duffcopy for larger copies.
-	if c >= 4 {
-		p := gins(op, &src, &tmp)
-		p.From.Type = obj.TYPE_MEM
-		p.From.Offset = int64(dir)
-		ploop := p
-
-		p = gins(op, &tmp, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = int64(dir)
-
-		p = gins(ppc64.ACMP, &src, &nend)
-
-		gc.Patch(gc.Gbranch(ppc64.ABNE, nil, 0), ploop)
-		gc.Regfree(&nend)
-	} else {
-		// TODO(austin): Instead of generating ADD $-8,R8; ADD
-		// $-8,R7; n*(MOVDU 8(R8),R9; MOVDU R9,8(R7);) just
-		// generate the offsets directly and eliminate the
-		// ADDs. That will produce shorter, more
-		// pipeline-able code.
-		var p *obj.Prog
-		for ; c > 0; c-- {
-			p = gins(op, &src, &tmp)
-			p.From.Type = obj.TYPE_MEM
-			p.From.Offset = int64(dir)
-
-			p = gins(op, &tmp, &dst)
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = int64(dir)
-		}
-	}
-
-	gc.Regfree(&dst)
-	gc.Regfree(&src)
-	gc.Regfree(&tmp)
-}
diff --git a/src/cmd/compile/internal/ppc64/galign.go b/src/cmd/compile/internal/ppc64/galign.go
index a83dff9..186aa29 100644
--- a/src/cmd/compile/internal/ppc64/galign.go
+++ b/src/cmd/compile/internal/ppc64/galign.go
@@ -10,65 +10,21 @@ import (
 	"cmd/internal/obj/ppc64"
 )
 
-func betypeinit() {
-	if gc.Ctxt.Flag_shared {
-		gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, ppc64.REG_R2)
-		gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, ppc64.REG_R12)
-	}
-}
-
-func Main() {
+func Init() {
 	gc.Thearch.LinkArch = &ppc64.Linkppc64
-	if obj.Getgoarch() == "ppc64le" {
+	if obj.GOARCH == "ppc64le" {
 		gc.Thearch.LinkArch = &ppc64.Linkppc64le
 	}
 	gc.Thearch.REGSP = ppc64.REGSP
-	gc.Thearch.REGCTXT = ppc64.REGCTXT
-	gc.Thearch.REGCALLX = ppc64.REG_R3
-	gc.Thearch.REGCALLX2 = ppc64.REG_R4
-	gc.Thearch.REGRETURN = ppc64.REG_R3
-	gc.Thearch.REGMIN = ppc64.REG_R0
-	gc.Thearch.REGMAX = ppc64.REG_R31
-	gc.Thearch.FREGMIN = ppc64.REG_F0
-	gc.Thearch.FREGMAX = ppc64.REG_F31
 	gc.Thearch.MAXWIDTH = 1 << 50
-	gc.Thearch.ReservedRegs = resvd
 
-	gc.Thearch.Betypeinit = betypeinit
-	gc.Thearch.Cgen_hmul = cgen_hmul
-	gc.Thearch.Cgen_shift = cgen_shift
-	gc.Thearch.Clearfat = clearfat
 	gc.Thearch.Defframe = defframe
-	gc.Thearch.Dodiv = dodiv
-	gc.Thearch.Excise = excise
-	gc.Thearch.Expandchecks = expandchecks
-	gc.Thearch.Getg = getg
-	gc.Thearch.Gins = gins
-	gc.Thearch.Ginscmp = ginscmp
-	gc.Thearch.Ginscon = ginscon
-	gc.Thearch.Ginsnop = ginsnop
-	gc.Thearch.Gmove = gmove
-	gc.Thearch.Peep = peep
 	gc.Thearch.Proginfo = proginfo
-	gc.Thearch.Regtyp = regtyp
-	gc.Thearch.Sameaddr = sameaddr
-	gc.Thearch.Smallindir = smallindir
-	gc.Thearch.Stackaddr = stackaddr
-	gc.Thearch.Blockcopy = blockcopy
-	gc.Thearch.Sudoaddable = sudoaddable
-	gc.Thearch.Sudoclean = sudoclean
-	gc.Thearch.Excludedregs = excludedregs
-	gc.Thearch.RtoB = RtoB
-	gc.Thearch.FtoB = RtoB
-	gc.Thearch.BtoR = BtoR
-	gc.Thearch.BtoF = BtoF
-	gc.Thearch.Optoas = optoas
-	gc.Thearch.Doregbits = doregbits
-	gc.Thearch.Regnames = regnames
+
+	gc.Thearch.SSAMarkMoves = ssaMarkMoves
+	gc.Thearch.SSAGenValue = ssaGenValue
+	gc.Thearch.SSAGenBlock = ssaGenBlock
 
 	initvariants()
 	initproginfo()
-
-	gc.Main()
-	gc.Exit(0)
 }
diff --git a/src/cmd/compile/internal/ppc64/ggen.go b/src/cmd/compile/internal/ppc64/ggen.go
index a89ed8f..b3ce968 100644
--- a/src/cmd/compile/internal/ppc64/ggen.go
+++ b/src/cmd/compile/internal/ppc64/ggen.go
@@ -8,7 +8,6 @@ import (
 	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/ppc64"
-	"fmt"
 )
 
 func defframe(ptxt *obj.Prog) {
@@ -36,7 +35,7 @@ func defframe(ptxt *obj.Prog) {
 			gc.Fatalf("needzero class %d", n.Class)
 		}
 		if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
-			gc.Fatalf("var %v has size %d offset %d", gc.Nconv(n, gc.FmtLong), int(n.Type.Width), int(n.Xoffset))
+			gc.Fatalf("var %L has size %d offset %d", n, int(n.Type.Width), int(n.Xoffset))
 		}
 
 		if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) {
@@ -66,488 +65,35 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
 	}
 	if cnt < int64(4*gc.Widthptr) {
 		for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
-			p = appendpp(p, ppc64.AMOVD, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGSP, gc.Ctxt.FixedFrameSize()+frame+lo+i)
+			p = gc.Appendpp(p, ppc64.AMOVD, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGSP, gc.Ctxt.FixedFrameSize()+frame+lo+i)
 		}
 	} else if cnt <= int64(128*gc.Widthptr) {
-		p = appendpp(p, ppc64.AADD, obj.TYPE_CONST, 0, gc.Ctxt.FixedFrameSize()+frame+lo-8, obj.TYPE_REG, ppc64.REGRT1, 0)
+		p = gc.Appendpp(p, ppc64.AADD, obj.TYPE_CONST, 0, gc.Ctxt.FixedFrameSize()+frame+lo-8, obj.TYPE_REG, ppc64.REGRT1, 0)
 		p.Reg = ppc64.REGSP
-		p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
-		f := gc.Sysfunc("duffzero")
-		gc.Naddr(&p.To, f)
-		gc.Afunclit(&p.To, f)
+		p = gc.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
+		gc.Naddr(&p.To, gc.Sysfunc("duffzero"))
 		p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr))
 	} else {
-		p = appendpp(p, ppc64.AMOVD, obj.TYPE_CONST, 0, gc.Ctxt.FixedFrameSize()+frame+lo-8, obj.TYPE_REG, ppc64.REGTMP, 0)
-		p = appendpp(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT1, 0)
+		p = gc.Appendpp(p, ppc64.AMOVD, obj.TYPE_CONST, 0, gc.Ctxt.FixedFrameSize()+frame+lo-8, obj.TYPE_REG, ppc64.REGTMP, 0)
+		p = gc.Appendpp(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT1, 0)
 		p.Reg = ppc64.REGSP
-		p = appendpp(p, ppc64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, ppc64.REGTMP, 0)
-		p = appendpp(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT2, 0)
+		p = gc.Appendpp(p, ppc64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, ppc64.REGTMP, 0)
+		p = gc.Appendpp(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT2, 0)
 		p.Reg = ppc64.REGRT1
-		p = appendpp(p, ppc64.AMOVDU, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGRT1, int64(gc.Widthptr))
+		p = gc.Appendpp(p, ppc64.AMOVDU, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGRT1, int64(gc.Widthptr))
 		p1 := p
-		p = appendpp(p, ppc64.ACMP, obj.TYPE_REG, ppc64.REGRT1, 0, obj.TYPE_REG, ppc64.REGRT2, 0)
-		p = appendpp(p, ppc64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
+		p = gc.Appendpp(p, ppc64.ACMP, obj.TYPE_REG, ppc64.REGRT1, 0, obj.TYPE_REG, ppc64.REGRT2, 0)
+		p = gc.Appendpp(p, ppc64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
 		gc.Patch(p, p1)
 	}
 
 	return p
 }
 
-func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int64, ttype obj.AddrType, treg int, toffset int64) *obj.Prog {
-	q := gc.Ctxt.NewProg()
-	gc.Clearp(q)
-	q.As = as
-	q.Lineno = p.Lineno
-	q.From.Type = ftype
-	q.From.Reg = int16(freg)
-	q.From.Offset = foffset
-	q.To.Type = ttype
-	q.To.Reg = int16(treg)
-	q.To.Offset = toffset
-	q.Link = p.Link
-	p.Link = q
-	return q
-}
-
 func ginsnop() {
-	var reg gc.Node
-	gc.Nodreg(&reg, gc.Types[gc.TINT], ppc64.REG_R0)
-	gins(ppc64.AOR, &reg, &reg)
-}
-
-var panicdiv *gc.Node
-
-/*
- * generate division.
- * generates one of:
- *	res = nl / nr
- *	res = nl % nr
- * according to op.
- */
-func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	// Have to be careful about handling
-	// most negative int divided by -1 correctly.
-	// The hardware will generate undefined result.
-	// Also need to explicitly trap on division on zero,
-	// the hardware will silently generate undefined result.
-	// DIVW will leave unpredictable result in higher 32-bit,
-	// so always use DIVD/DIVDU.
-	t := nl.Type
-
-	t0 := t
-	check := false
-	if t.IsSigned() {
-		check = true
-		if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<<uint64(t.Width*8-1)) {
-			check = false
-		} else if gc.Isconst(nr, gc.CTINT) && nr.Int64() != -1 {
-			check = false
-		}
-	}
-
-	if t.Width < 8 {
-		if t.IsSigned() {
-			t = gc.Types[gc.TINT64]
-		} else {
-			t = gc.Types[gc.TUINT64]
-		}
-		check = false
-	}
-
-	a := optoas(gc.ODIV, t)
-
-	var tl gc.Node
-	gc.Regalloc(&tl, t0, nil)
-	var tr gc.Node
-	gc.Regalloc(&tr, t0, nil)
-	if nl.Ullman >= nr.Ullman {
-		gc.Cgen(nl, &tl)
-		gc.Cgen(nr, &tr)
-	} else {
-		gc.Cgen(nr, &tr)
-		gc.Cgen(nl, &tl)
-	}
-
-	if t != t0 {
-		// Convert
-		tl2 := tl
-
-		tr2 := tr
-		tl.Type = t
-		tr.Type = t
-		gmove(&tl2, &tl)
-		gmove(&tr2, &tr)
-	}
-
-	// Handle divide-by-zero panic.
-	p1 := gins(optoas(gc.OCMP, t), &tr, nil)
-
-	p1.To.Type = obj.TYPE_REG
-	p1.To.Reg = ppc64.REGZERO
-	p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1)
-	if panicdiv == nil {
-		panicdiv = gc.Sysfunc("panicdivide")
-	}
-	gc.Ginscall(panicdiv, -1)
-	gc.Patch(p1, gc.Pc)
-
-	var p2 *obj.Prog
-	if check {
-		var nm1 gc.Node
-		gc.Nodconst(&nm1, t, -1)
-		gins(optoas(gc.OCMP, t), &tr, &nm1)
-		p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1)
-		if op == gc.ODIV {
-			// a / (-1) is -a.
-			gins(optoas(gc.OMINUS, t), nil, &tl)
-
-			gmove(&tl, res)
-		} else {
-			// a % (-1) is 0.
-			var nz gc.Node
-			gc.Nodconst(&nz, t, 0)
-
-			gmove(&nz, res)
-		}
-
-		p2 = gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-	}
-
-	p1 = gins(a, &tr, &tl)
-	if op == gc.ODIV {
-		gc.Regfree(&tr)
-		gmove(&tl, res)
-	} else {
-		// A%B = A-(A/B*B)
-		var tm gc.Node
-		gc.Regalloc(&tm, t, nil)
-
-		// patch div to use the 3 register form
-		// TODO(minux): add gins3?
-		p1.Reg = p1.To.Reg
-
-		p1.To.Reg = tm.Reg
-		gins(optoas(gc.OMUL, t), &tr, &tm)
-		gc.Regfree(&tr)
-		gins(optoas(gc.OSUB, t), &tm, &tl)
-		gc.Regfree(&tm)
-		gmove(&tl, res)
-	}
-
-	gc.Regfree(&tl)
-	if check {
-		gc.Patch(p2, gc.Pc)
-	}
-}
-
-/*
- * generate high multiply:
- *   res = (nl*nr) >> width
- */
-func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	// largest ullman on left.
-	if nl.Ullman < nr.Ullman {
-		nl, nr = nr, nl
-	}
-
-	t := nl.Type
-	w := t.Width * 8
-	var n1 gc.Node
-	gc.Cgenr(nl, &n1, res)
-	var n2 gc.Node
-	gc.Cgenr(nr, &n2, nil)
-	switch gc.Simtype[t.Etype] {
-	case gc.TINT8,
-		gc.TINT16,
-		gc.TINT32:
-		gins(optoas(gc.OMUL, t), &n2, &n1)
-		p := gins(ppc64.ASRAD, nil, &n1)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-
-	case gc.TUINT8,
-		gc.TUINT16,
-		gc.TUINT32:
-		gins(optoas(gc.OMUL, t), &n2, &n1)
-		p := gins(ppc64.ASRD, nil, &n1)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = w
-
-	case gc.TINT64,
-		gc.TUINT64:
-		if t.IsSigned() {
-			gins(ppc64.AMULHD, &n2, &n1)
-		} else {
-			gins(ppc64.AMULHDU, &n2, &n1)
-		}
-
-	default:
-		gc.Fatalf("cgen_hmul %v", t)
-	}
-
-	gc.Cgen(&n1, res)
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-/*
- * generate shift according to op, one of:
- *	res = nl << nr
- *	res = nl >> nr
- */
-func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	a := optoas(op, nl.Type)
-
-	if nr.Op == gc.OLITERAL {
-		var n1 gc.Node
-		gc.Regalloc(&n1, nl.Type, res)
-		gc.Cgen(nl, &n1)
-		sc := uint64(nr.Int64())
-		if sc >= uint64(nl.Type.Width*8) {
-			// large shift gets 2 shifts by width-1
-			var n3 gc.Node
-			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
-
-			gins(a, &n3, &n1)
-			gins(a, &n3, &n1)
-		} else {
-			gins(a, nr, &n1)
-		}
-		gmove(&n1, res)
-		gc.Regfree(&n1)
-		return
-	}
-
-	if nl.Ullman >= gc.UINF {
-		var n4 gc.Node
-		gc.Tempname(&n4, nl.Type)
-		gc.Cgen(nl, &n4)
-		nl = &n4
-	}
-
-	if nr.Ullman >= gc.UINF {
-		var n5 gc.Node
-		gc.Tempname(&n5, nr.Type)
-		gc.Cgen(nr, &n5)
-		nr = &n5
-	}
-
-	// Allow either uint32 or uint64 as shift type,
-	// to avoid unnecessary conversion from uint32 to uint64
-	// just to do the comparison.
-	tcount := gc.Types[gc.Simtype[nr.Type.Etype]]
-
-	if tcount.Etype < gc.TUINT32 {
-		tcount = gc.Types[gc.TUINT32]
-	}
-
-	var n1 gc.Node
-	gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX
-	var n3 gc.Node
-	gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX
-
-	var n2 gc.Node
-	gc.Regalloc(&n2, nl.Type, res)
-
-	if nl.Ullman >= nr.Ullman {
-		gc.Cgen(nl, &n2)
-		gc.Cgen(nr, &n1)
-		gmove(&n1, &n3)
-	} else {
-		gc.Cgen(nr, &n1)
-		gmove(&n1, &n3)
-		gc.Cgen(nl, &n2)
-	}
-
-	gc.Regfree(&n3)
-
-	// test and fix up large shifts
-	if !bounded {
-		gc.Nodconst(&n3, tcount, nl.Type.Width*8)
-		gins(optoas(gc.OCMP, tcount), &n1, &n3)
-		p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, +1)
-		if op == gc.ORSH && nl.Type.IsSigned() {
-			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
-			gins(a, &n3, &n2)
-		} else {
-			gc.Nodconst(&n3, nl.Type, 0)
-			gmove(&n3, &n2)
-		}
-
-		gc.Patch(p1, gc.Pc)
-	}
-
-	gins(a, &n1, &n2)
-
-	gmove(&n2, res)
-
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-func clearfat(nl *gc.Node) {
-	/* clear a fat object */
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width)
-	}
-
-	w := uint64(nl.Type.Width)
-
-	// Avoid taking the address for simple enough types.
-	if gc.Componentgen(nil, nl) {
-		return
-	}
-
-	c := w % 8 // bytes
-	q := w / 8 // dwords
-
-	if gc.Reginuse(ppc64.REGRT1) {
-		gc.Fatalf("%v in use during clearfat", obj.Rconv(ppc64.REGRT1))
-	}
-
-	var r0 gc.Node
-	gc.Nodreg(&r0, gc.Types[gc.TUINT64], ppc64.REGZERO)
-	var dst gc.Node
-	gc.Nodreg(&dst, gc.Types[gc.Tptr], ppc64.REGRT1)
-	gc.Regrealloc(&dst)
-	gc.Agen(nl, &dst)
-
-	var boff uint64
-	if q > 128 {
-		p := gins(ppc64.ASUB, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = 8
-
-		var end gc.Node
-		gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
-		p = gins(ppc64.AMOVD, &dst, &end)
-		p.From.Type = obj.TYPE_ADDR
-		p.From.Offset = int64(q * 8)
-
-		p = gins(ppc64.AMOVDU, &r0, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = 8
-		pl := p
-
-		p = gins(ppc64.ACMP, &dst, &end)
-		gc.Patch(gc.Gbranch(ppc64.ABNE, nil, 0), pl)
-
-		gc.Regfree(&end)
-
-		// The loop leaves R3 on the last zeroed dword
-		boff = 8
-	} else if q >= 4 {
-		p := gins(ppc64.ASUB, nil, &dst)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = 8
-		f := gc.Sysfunc("duffzero")
-		p = gins(obj.ADUFFZERO, nil, f)
-		gc.Afunclit(&p.To, f)
-
-		// 4 and 128 = magic constants: see ../../runtime/asm_ppc64x.s
-		p.To.Offset = int64(4 * (128 - q))
-
-		// duffzero leaves R3 on the last zeroed dword
-		boff = 8
-	} else {
-		var p *obj.Prog
-		for t := uint64(0); t < q; t++ {
-			p = gins(ppc64.AMOVD, &r0, &dst)
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = int64(8 * t)
-		}
-
-		boff = 8 * q
-	}
-
-	var p *obj.Prog
-	for t := uint64(0); t < c; t++ {
-		p = gins(ppc64.AMOVB, &r0, &dst)
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = int64(t + boff)
-	}
-
-	gc.Regfree(&dst)
-}
-
-// Called after regopt and peep have run.
-// Expand CHECKNIL pseudo-op into actual nil pointer check.
-func expandchecks(firstp *obj.Prog) {
-	var p1 *obj.Prog
-	var p2 *obj.Prog
-
-	for p := firstp; p != nil; p = p.Link {
-		if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 {
-			fmt.Printf("expandchecks: %v\n", p)
-		}
-		if p.As != obj.ACHECKNIL {
-			continue
-		}
-		if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers
-			gc.Warnl(p.Lineno, "generated nil check")
-		}
-		if p.From.Type != obj.TYPE_REG {
-			gc.Fatalf("invalid nil check %v\n", p)
-		}
-
-		/*
-			// check is
-			//	TD $4, R0, arg (R0 is always zero)
-			// eqv. to:
-			// 	tdeq r0, arg
-			// NOTE: this needs special runtime support to make SIGTRAP recoverable.
-			reg = p->from.reg;
-			p->as = ATD;
-			p->from = p->to = p->from3 = zprog.from;
-			p->from.type = TYPE_CONST;
-			p->from.offset = 4;
-			p->from.reg = 0;
-			p->reg = REGZERO;
-			p->to.type = TYPE_REG;
-			p->to.reg = reg;
-		*/
-		// check is
-		//	CMP arg, R0
-		//	BNE 2(PC) [likely]
-		//	MOVD R0, 0(R0)
-		p1 = gc.Ctxt.NewProg()
-
-		p2 = gc.Ctxt.NewProg()
-		gc.Clearp(p1)
-		gc.Clearp(p2)
-		p1.Link = p2
-		p2.Link = p.Link
-		p.Link = p1
-		p1.Lineno = p.Lineno
-		p2.Lineno = p.Lineno
-		p1.Pc = 9999
-		p2.Pc = 9999
-		p.As = ppc64.ACMP
-		p.To.Type = obj.TYPE_REG
-		p.To.Reg = ppc64.REGZERO
-		p1.As = ppc64.ABNE
-
-		//p1->from.type = TYPE_CONST;
-		//p1->from.offset = 1; // likely
-		p1.To.Type = obj.TYPE_BRANCH
-
-		p1.To.Val = p2.Link
-
-		// crash by write to memory address 0.
-		p2.As = ppc64.AMOVD
-
-		p2.From.Type = obj.TYPE_REG
-		p2.From.Reg = ppc64.REGZERO
-		p2.To.Type = obj.TYPE_MEM
-		p2.To.Reg = ppc64.REGZERO
-		p2.To.Offset = 0
-	}
-}
-
-// res = runtime.getg()
-func getg(res *gc.Node) {
-	var n1 gc.Node
-	gc.Nodreg(&n1, res.Type, ppc64.REGG)
-	gmove(&n1, res)
+	p := gc.Prog(ppc64.AOR)
+	p.From.Type = obj.TYPE_REG
+	p.From.Reg = ppc64.REG_R0
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = ppc64.REG_R0
 }
diff --git a/src/cmd/compile/internal/ppc64/gsubr.go b/src/cmd/compile/internal/ppc64/gsubr.go
deleted file mode 100644
index f875999..0000000
--- a/src/cmd/compile/internal/ppc64/gsubr.go
+++ /dev/null
@@ -1,1076 +0,0 @@
-// Derived from Inferno utils/6c/txt.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package ppc64
-
-import (
-	"cmd/compile/internal/big"
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/ppc64"
-	"fmt"
-)
-
-var resvd = []int{
-	ppc64.REGZERO,
-	ppc64.REGSP, // reserved for SP
-	// We need to preserve the C ABI TLS pointer because sigtramp
-	// may happen during C code and needs to access the g. C
-	// clobbers REGG, so if Go were to clobber REGTLS, sigtramp
-	// won't know which convention to use. By preserving REGTLS,
-	// we can just retrieve g from TLS when we aren't sure.
-	ppc64.REGTLS,
-
-	// TODO(austin): Consolidate REGTLS and REGG?
-	ppc64.REGG,
-	ppc64.REGTMP, // REGTMP
-	ppc64.FREGCVI,
-	ppc64.FREGZERO,
-	ppc64.FREGHALF,
-	ppc64.FREGONE,
-	ppc64.FREGTWO,
-}
-
-/*
- * generate
- *	as $c, n
- */
-func ginscon(as obj.As, c int64, n2 *gc.Node) {
-	var n1 gc.Node
-
-	gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
-
-	if as != ppc64.AMOVD && (c < -ppc64.BIG || c > ppc64.BIG) || n2.Op != gc.OREGISTER || as == ppc64.AMULLD {
-		// cannot have more than 16-bit of immediate in ADD, etc.
-		// instead, MOV into register first.
-		var ntmp gc.Node
-		gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil)
-
-		rawgins(ppc64.AMOVD, &n1, &ntmp)
-		rawgins(as, &ntmp, n2)
-		gc.Regfree(&ntmp)
-		return
-	}
-
-	rawgins(as, &n1, n2)
-}
-
-/*
- * generate
- *	as n, $c (CMP/CMPU)
- */
-func ginscon2(as obj.As, n2 *gc.Node, c int64) {
-	var n1 gc.Node
-
-	gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
-
-	switch as {
-	default:
-		gc.Fatalf("ginscon2")
-
-	case ppc64.ACMP:
-		if -ppc64.BIG <= c && c <= ppc64.BIG {
-			rawgins(as, n2, &n1)
-			return
-		}
-
-	case ppc64.ACMPU:
-		if 0 <= c && c <= 2*ppc64.BIG {
-			rawgins(as, n2, &n1)
-			return
-		}
-	}
-
-	// MOV n1 into register first
-	var ntmp gc.Node
-	gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil)
-
-	rawgins(ppc64.AMOVD, &n1, &ntmp)
-	rawgins(as, n2, &ntmp)
-	gc.Regfree(&ntmp)
-}
-
-func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
-	if t.IsInteger() && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL {
-		// Reverse comparison to place constant last.
-		op = gc.Brrev(op)
-		n1, n2 = n2, n1
-	}
-
-	var r1, r2, g1, g2 gc.Node
-	gc.Regalloc(&r1, t, n1)
-	gc.Regalloc(&g1, n1.Type, &r1)
-	gc.Cgen(n1, &g1)
-	gmove(&g1, &r1)
-	if t.IsInteger() && gc.Isconst(n2, gc.CTINT) {
-		ginscon2(optoas(gc.OCMP, t), &r1, n2.Int64())
-	} else {
-		gc.Regalloc(&r2, t, n2)
-		gc.Regalloc(&g2, n1.Type, &r2)
-		gc.Cgen(n2, &g2)
-		gmove(&g2, &r2)
-		rawgins(optoas(gc.OCMP, t), &r1, &r2)
-		gc.Regfree(&g2)
-		gc.Regfree(&r2)
-	}
-	gc.Regfree(&g1)
-	gc.Regfree(&r1)
-	return gc.Gbranch(optoas(op, t), nil, likely)
-}
-
-// set up nodes representing 2^63
-var (
-	bigi         gc.Node
-	bigf         gc.Node
-	bignodes_did bool
-)
-
-func bignodes() {
-	if bignodes_did {
-		return
-	}
-	bignodes_did = true
-
-	var i big.Int
-	i.SetInt64(1)
-	i.Lsh(&i, 63)
-
-	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
-	bigi.SetBigInt(&i)
-
-	bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64])
-}
-
-/*
- * generate move:
- *	t = f
- * hard part is conversions.
- */
-func gmove(f *gc.Node, t *gc.Node) {
-	if gc.Debug['M'] != 0 {
-		fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, gc.FmtLong), gc.Nconv(t, gc.FmtLong))
-	}
-
-	ft := int(gc.Simsimtype(f.Type))
-	tt := int(gc.Simsimtype(t.Type))
-	cvt := t.Type
-
-	if gc.Iscomplex[ft] || gc.Iscomplex[tt] {
-		gc.Complexmove(f, t)
-		return
-	}
-
-	// cannot have two memory operands
-	var r2 gc.Node
-	var r1 gc.Node
-	var a obj.As
-	if gc.Ismem(f) && gc.Ismem(t) {
-		goto hard
-	}
-
-	// convert constant to desired type
-	if f.Op == gc.OLITERAL {
-		var con gc.Node
-		switch tt {
-		default:
-			f.Convconst(&con, t.Type)
-
-		case gc.TINT32,
-			gc.TINT16,
-			gc.TINT8:
-			var con gc.Node
-			f.Convconst(&con, gc.Types[gc.TINT64])
-			var r1 gc.Node
-			gc.Regalloc(&r1, con.Type, t)
-			gins(ppc64.AMOVD, &con, &r1)
-			gmove(&r1, t)
-			gc.Regfree(&r1)
-			return
-
-		case gc.TUINT32,
-			gc.TUINT16,
-			gc.TUINT8:
-			var con gc.Node
-			f.Convconst(&con, gc.Types[gc.TUINT64])
-			var r1 gc.Node
-			gc.Regalloc(&r1, con.Type, t)
-			gins(ppc64.AMOVD, &con, &r1)
-			gmove(&r1, t)
-			gc.Regfree(&r1)
-			return
-		}
-
-		f = &con
-		ft = tt // so big switch will choose a simple mov
-
-		// constants can't move directly to memory.
-		if gc.Ismem(t) {
-			goto hard
-		}
-	}
-
-	// float constants come from memory.
-	//if(isfloat[tt])
-	//	goto hard;
-
-	// 64-bit immediates are also from memory.
-	//if(isint[tt])
-	//	goto hard;
-	//// 64-bit immediates are really 32-bit sign-extended
-	//// unless moving into a register.
-	//if(isint[tt]) {
-	//	if(mpcmpfixfix(con.val.u.xval, minintval[TINT32]) < 0)
-	//		goto hard;
-	//	if(mpcmpfixfix(con.val.u.xval, maxintval[TINT32]) > 0)
-	//		goto hard;
-	//}
-
-	// value -> value copy, only one memory operand.
-	// figure out the instruction to use.
-	// break out of switch for one-instruction gins.
-	// goto rdst for "destination must be register".
-	// goto hard for "convert to cvt type first".
-	// otherwise handle and return.
-
-	switch uint32(ft)<<16 | uint32(tt) {
-	default:
-		gc.Fatalf("gmove %v -> %v", gc.Tconv(f.Type, gc.FmtLong), gc.Tconv(t.Type, gc.FmtLong))
-
-		/*
-		 * integer copy and truncate
-		 */
-	case gc.TINT8<<16 | gc.TINT8, // same size
-		gc.TUINT8<<16 | gc.TINT8,
-		gc.TINT16<<16 | gc.TINT8,
-		// truncate
-		gc.TUINT16<<16 | gc.TINT8,
-		gc.TINT32<<16 | gc.TINT8,
-		gc.TUINT32<<16 | gc.TINT8,
-		gc.TINT64<<16 | gc.TINT8,
-		gc.TUINT64<<16 | gc.TINT8:
-		a = ppc64.AMOVB
-
-	case gc.TINT8<<16 | gc.TUINT8, // same size
-		gc.TUINT8<<16 | gc.TUINT8,
-		gc.TINT16<<16 | gc.TUINT8,
-		// truncate
-		gc.TUINT16<<16 | gc.TUINT8,
-		gc.TINT32<<16 | gc.TUINT8,
-		gc.TUINT32<<16 | gc.TUINT8,
-		gc.TINT64<<16 | gc.TUINT8,
-		gc.TUINT64<<16 | gc.TUINT8:
-		a = ppc64.AMOVBZ
-
-	case gc.TINT16<<16 | gc.TINT16, // same size
-		gc.TUINT16<<16 | gc.TINT16,
-		gc.TINT32<<16 | gc.TINT16,
-		// truncate
-		gc.TUINT32<<16 | gc.TINT16,
-		gc.TINT64<<16 | gc.TINT16,
-		gc.TUINT64<<16 | gc.TINT16:
-		a = ppc64.AMOVH
-
-	case gc.TINT16<<16 | gc.TUINT16, // same size
-		gc.TUINT16<<16 | gc.TUINT16,
-		gc.TINT32<<16 | gc.TUINT16,
-		// truncate
-		gc.TUINT32<<16 | gc.TUINT16,
-		gc.TINT64<<16 | gc.TUINT16,
-		gc.TUINT64<<16 | gc.TUINT16:
-		a = ppc64.AMOVHZ
-
-	case gc.TINT32<<16 | gc.TINT32, // same size
-		gc.TUINT32<<16 | gc.TINT32,
-		gc.TINT64<<16 | gc.TINT32,
-		// truncate
-		gc.TUINT64<<16 | gc.TINT32:
-		a = ppc64.AMOVW
-
-	case gc.TINT32<<16 | gc.TUINT32, // same size
-		gc.TUINT32<<16 | gc.TUINT32,
-		gc.TINT64<<16 | gc.TUINT32,
-		gc.TUINT64<<16 | gc.TUINT32:
-		a = ppc64.AMOVWZ
-
-	case gc.TINT64<<16 | gc.TINT64, // same size
-		gc.TINT64<<16 | gc.TUINT64,
-		gc.TUINT64<<16 | gc.TINT64,
-		gc.TUINT64<<16 | gc.TUINT64:
-		a = ppc64.AMOVD
-
-		/*
-		 * integer up-conversions
-		 */
-	case gc.TINT8<<16 | gc.TINT16, // sign extend int8
-		gc.TINT8<<16 | gc.TUINT16,
-		gc.TINT8<<16 | gc.TINT32,
-		gc.TINT8<<16 | gc.TUINT32,
-		gc.TINT8<<16 | gc.TINT64,
-		gc.TINT8<<16 | gc.TUINT64:
-		a = ppc64.AMOVB
-
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8
-		gc.TUINT8<<16 | gc.TUINT16,
-		gc.TUINT8<<16 | gc.TINT32,
-		gc.TUINT8<<16 | gc.TUINT32,
-		gc.TUINT8<<16 | gc.TINT64,
-		gc.TUINT8<<16 | gc.TUINT64:
-		a = ppc64.AMOVBZ
-
-		goto rdst
-
-	case gc.TINT16<<16 | gc.TINT32, // sign extend int16
-		gc.TINT16<<16 | gc.TUINT32,
-		gc.TINT16<<16 | gc.TINT64,
-		gc.TINT16<<16 | gc.TUINT64:
-		a = ppc64.AMOVH
-
-		goto rdst
-
-	case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16
-		gc.TUINT16<<16 | gc.TUINT32,
-		gc.TUINT16<<16 | gc.TINT64,
-		gc.TUINT16<<16 | gc.TUINT64:
-		a = ppc64.AMOVHZ
-
-		goto rdst
-
-	case gc.TINT32<<16 | gc.TINT64, // sign extend int32
-		gc.TINT32<<16 | gc.TUINT64:
-		a = ppc64.AMOVW
-
-		goto rdst
-
-	case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32
-		gc.TUINT32<<16 | gc.TUINT64:
-		a = ppc64.AMOVWZ
-
-		goto rdst
-
-		//warn("gmove: convert float to int not implemented: %N -> %N\n", f, t);
-	//return;
-	// algorithm is:
-	//	if small enough, use native float64 -> int64 conversion.
-	//	otherwise, subtract 2^63, convert, and add it back.
-	/*
-	* float to integer
-	 */
-	case gc.TFLOAT32<<16 | gc.TINT32,
-		gc.TFLOAT64<<16 | gc.TINT32,
-		gc.TFLOAT32<<16 | gc.TINT64,
-		gc.TFLOAT64<<16 | gc.TINT64,
-		gc.TFLOAT32<<16 | gc.TINT16,
-		gc.TFLOAT32<<16 | gc.TINT8,
-		gc.TFLOAT32<<16 | gc.TUINT16,
-		gc.TFLOAT32<<16 | gc.TUINT8,
-		gc.TFLOAT64<<16 | gc.TINT16,
-		gc.TFLOAT64<<16 | gc.TINT8,
-		gc.TFLOAT64<<16 | gc.TUINT16,
-		gc.TFLOAT64<<16 | gc.TUINT8,
-		gc.TFLOAT32<<16 | gc.TUINT32,
-		gc.TFLOAT64<<16 | gc.TUINT32,
-		gc.TFLOAT32<<16 | gc.TUINT64,
-		gc.TFLOAT64<<16 | gc.TUINT64:
-		bignodes()
-
-		var r1 gc.Node
-		gc.Regalloc(&r1, gc.Types[ft], f)
-		gmove(f, &r1)
-		if tt == gc.TUINT64 {
-			gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], nil)
-			gmove(&bigf, &r2)
-			gins(ppc64.AFCMPU, &r1, &r2)
-			p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TFLOAT64]), nil, +1)
-			gins(ppc64.AFSUB, &r2, &r1)
-			gc.Patch(p1, gc.Pc)
-			gc.Regfree(&r2)
-		}
-
-		gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], nil)
-		var r3 gc.Node
-		gc.Regalloc(&r3, gc.Types[gc.TINT64], t)
-		gins(ppc64.AFCTIDZ, &r1, &r2)
-		p1 := gins(ppc64.AFMOVD, &r2, nil)
-		p1.To.Type = obj.TYPE_MEM
-		p1.To.Reg = ppc64.REGSP
-		p1.To.Offset = -8
-		p1 = gins(ppc64.AMOVD, nil, &r3)
-		p1.From.Type = obj.TYPE_MEM
-		p1.From.Reg = ppc64.REGSP
-		p1.From.Offset = -8
-		gc.Regfree(&r2)
-		gc.Regfree(&r1)
-		if tt == gc.TUINT64 {
-			p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TFLOAT64]), nil, +1) // use CR0 here again
-			gc.Nodreg(&r1, gc.Types[gc.TINT64], ppc64.REGTMP)
-			gins(ppc64.AMOVD, &bigi, &r1)
-			gins(ppc64.AADD, &r1, &r3)
-			gc.Patch(p1, gc.Pc)
-		}
-
-		gmove(&r3, t)
-		gc.Regfree(&r3)
-		return
-
-	/*
-	 * integer to float
-	 */
-	case gc.TINT32<<16 | gc.TFLOAT32,
-		gc.TINT32<<16 | gc.TFLOAT64,
-		gc.TINT64<<16 | gc.TFLOAT32,
-		gc.TINT64<<16 | gc.TFLOAT64,
-		gc.TINT16<<16 | gc.TFLOAT32,
-		gc.TINT16<<16 | gc.TFLOAT64,
-		gc.TINT8<<16 | gc.TFLOAT32,
-		gc.TINT8<<16 | gc.TFLOAT64,
-		gc.TUINT16<<16 | gc.TFLOAT32,
-		gc.TUINT16<<16 | gc.TFLOAT64,
-		gc.TUINT8<<16 | gc.TFLOAT32,
-		gc.TUINT8<<16 | gc.TFLOAT64,
-		gc.TUINT32<<16 | gc.TFLOAT32,
-		gc.TUINT32<<16 | gc.TFLOAT64,
-		gc.TUINT64<<16 | gc.TFLOAT32,
-		gc.TUINT64<<16 | gc.TFLOAT64:
-		bignodes()
-
-		// The algorithm is:
-		//	if small enough, use native int64 -> float64 conversion,
-		//	otherwise halve (x -> (x>>1)|(x&1)), convert, and double.
-		// Note: could use FCFIDU instead if target supports it.
-		var r1 gc.Node
-		gc.Regalloc(&r1, gc.Types[gc.TINT64], nil)
-		gmove(f, &r1)
-		if ft == gc.TUINT64 {
-			gc.Nodreg(&r2, gc.Types[gc.TUINT64], ppc64.REGTMP)
-			gmove(&bigi, &r2)
-			gins(ppc64.ACMPU, &r1, &r2)
-			p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1)
-			var r3 gc.Node
-			gc.Regalloc(&r3, gc.Types[gc.TUINT64], nil)
-			p2 := gins(ppc64.AANDCC, nil, &r3) // andi.
-			p2.Reg = r1.Reg
-			p2.From.Type = obj.TYPE_CONST
-			p2.From.Offset = 1
-			p3 := gins(ppc64.ASRD, nil, &r1)
-			p3.From.Type = obj.TYPE_CONST
-			p3.From.Offset = 1
-			gins(ppc64.AOR, &r3, &r1)
-			gc.Regfree(&r3)
-			gc.Patch(p1, gc.Pc)
-		}
-		gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], t)
-		p1 := gins(ppc64.AMOVD, &r1, nil)
-		p1.To.Type = obj.TYPE_MEM
-		p1.To.Reg = ppc64.REGSP
-		p1.To.Offset = -8
-		p1 = gins(ppc64.AFMOVD, nil, &r2)
-		p1.From.Type = obj.TYPE_MEM
-		p1.From.Reg = ppc64.REGSP
-		p1.From.Offset = -8
-		gins(ppc64.AFCFID, &r2, &r2)
-		gc.Regfree(&r1)
-		if ft == gc.TUINT64 {
-			p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1) // use CR0 here again
-			gc.Nodreg(&r1, gc.Types[gc.TFLOAT64], ppc64.FREGTWO)
-			gins(ppc64.AFMUL, &r1, &r2)
-			gc.Patch(p1, gc.Pc)
-		}
-		gmove(&r2, t)
-		gc.Regfree(&r2)
-		return
-
-		/*
-		 * float to float
-		 */
-	case gc.TFLOAT32<<16 | gc.TFLOAT32:
-		a = ppc64.AFMOVS
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT64:
-		a = ppc64.AFMOVD
-
-	case gc.TFLOAT32<<16 | gc.TFLOAT64:
-		a = ppc64.AFMOVS
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT32:
-		a = ppc64.AFRSP
-		goto rdst
-	}
-
-	gins(a, f, t)
-	return
-
-	// requires register destination
-rdst:
-	{
-		gc.Regalloc(&r1, t.Type, t)
-
-		gins(a, f, &r1)
-		gmove(&r1, t)
-		gc.Regfree(&r1)
-		return
-	}
-
-	// requires register intermediate
-hard:
-	gc.Regalloc(&r1, cvt, t)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-}
-
-// gins is called by the front end.
-// It synthesizes some multiple-instruction sequences
-// so the front end can stay simpler.
-func gins(as obj.As, f, t *gc.Node) *obj.Prog {
-	if as >= obj.A_ARCHSPECIFIC {
-		if x, ok := f.IntLiteral(); ok {
-			ginscon(as, x, t)
-			return nil // caller must not use
-		}
-	}
-	if as == ppc64.ACMP || as == ppc64.ACMPU {
-		if x, ok := t.IntLiteral(); ok {
-			ginscon2(as, f, x)
-			return nil // caller must not use
-		}
-	}
-	return rawgins(as, f, t)
-}
-
-/*
- * generate one instruction:
- *	as f, t
- */
-func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
-	// TODO(austin): Add self-move test like in 6g (but be careful
-	// of truncation moves)
-
-	p := gc.Prog(as)
-	gc.Naddr(&p.From, f)
-	gc.Naddr(&p.To, t)
-
-	switch as {
-	case obj.ACALL:
-		if p.To.Type == obj.TYPE_REG && p.To.Reg != ppc64.REG_CTR {
-			// Allow front end to emit CALL REG, and rewrite into MOV REG, CTR; CALL CTR.
-			if gc.Ctxt.Flag_shared {
-				// Make sure function pointer is in R12 as well when
-				// compiling Go into PIC.
-				// TODO(mwhudson): it would obviously be better to
-				// change the register allocation to put the value in
-				// R12 already, but I don't know how to do that.
-				q := gc.Prog(as)
-				q.As = ppc64.AMOVD
-				q.From = p.To
-				q.To.Type = obj.TYPE_REG
-				q.To.Reg = ppc64.REG_R12
-			}
-			pp := gc.Prog(as)
-			pp.From = p.From
-			pp.To.Type = obj.TYPE_REG
-			pp.To.Reg = ppc64.REG_CTR
-
-			p.As = ppc64.AMOVD
-			p.From = p.To
-			p.To.Type = obj.TYPE_REG
-			p.To.Reg = ppc64.REG_CTR
-
-			if gc.Ctxt.Flag_shared {
-				// When compiling Go into PIC, the function we just
-				// called via pointer might have been implemented in
-				// a separate module and so overwritten the TOC
-				// pointer in R2; reload it.
-				q := gc.Prog(ppc64.AMOVD)
-				q.From.Type = obj.TYPE_MEM
-				q.From.Offset = 24
-				q.From.Reg = ppc64.REGSP
-				q.To.Type = obj.TYPE_REG
-				q.To.Reg = ppc64.REG_R2
-			}
-
-			if gc.Debug['g'] != 0 {
-				fmt.Printf("%v\n", p)
-				fmt.Printf("%v\n", pp)
-			}
-
-			return pp
-		}
-
-	// Bad things the front end has done to us. Crash to find call stack.
-	case ppc64.AAND, ppc64.AMULLD:
-		if p.From.Type == obj.TYPE_CONST {
-			gc.Debug['h'] = 1
-			gc.Fatalf("bad inst: %v", p)
-		}
-	case ppc64.ACMP, ppc64.ACMPU:
-		if p.From.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_MEM {
-			gc.Debug['h'] = 1
-			gc.Fatalf("bad inst: %v", p)
-		}
-	}
-
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("%v\n", p)
-	}
-
-	w := int32(0)
-	switch as {
-	case ppc64.AMOVB,
-		ppc64.AMOVBU,
-		ppc64.AMOVBZ,
-		ppc64.AMOVBZU:
-		w = 1
-
-	case ppc64.AMOVH,
-		ppc64.AMOVHU,
-		ppc64.AMOVHZ,
-		ppc64.AMOVHZU:
-		w = 2
-
-	case ppc64.AMOVW,
-		ppc64.AMOVWU,
-		ppc64.AMOVWZ,
-		ppc64.AMOVWZU:
-		w = 4
-
-	case ppc64.AMOVD,
-		ppc64.AMOVDU:
-		if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_ADDR {
-			break
-		}
-		w = 8
-	}
-
-	if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Type != obj.TYPE_REG && p.To.Width > int64(w))) {
-		gc.Dump("f", f)
-		gc.Dump("t", t)
-		gc.Fatalf("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width)
-	}
-
-	return p
-}
-
-/*
- * return Axxx for Oxxx on type t.
- */
-func optoas(op gc.Op, t *gc.Type) obj.As {
-	if t == nil {
-		gc.Fatalf("optoas: t is nil")
-	}
-
-	// avoid constant conversions in switches below
-	const (
-		OMINUS_ = uint32(gc.OMINUS) << 16
-		OLSH_   = uint32(gc.OLSH) << 16
-		ORSH_   = uint32(gc.ORSH) << 16
-		OADD_   = uint32(gc.OADD) << 16
-		OSUB_   = uint32(gc.OSUB) << 16
-		OMUL_   = uint32(gc.OMUL) << 16
-		ODIV_   = uint32(gc.ODIV) << 16
-		OOR_    = uint32(gc.OOR) << 16
-		OAND_   = uint32(gc.OAND) << 16
-		OXOR_   = uint32(gc.OXOR) << 16
-		OEQ_    = uint32(gc.OEQ) << 16
-		ONE_    = uint32(gc.ONE) << 16
-		OLT_    = uint32(gc.OLT) << 16
-		OLE_    = uint32(gc.OLE) << 16
-		OGE_    = uint32(gc.OGE) << 16
-		OGT_    = uint32(gc.OGT) << 16
-		OCMP_   = uint32(gc.OCMP) << 16
-		OAS_    = uint32(gc.OAS) << 16
-		OHMUL_  = uint32(gc.OHMUL) << 16
-		OSQRT_  = uint32(gc.OSQRT) << 16
-	)
-
-	a := obj.AXXX
-	switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
-	default:
-		gc.Fatalf("optoas: no entry for op=%v type=%v", op, t)
-
-	case OEQ_ | gc.TBOOL,
-		OEQ_ | gc.TINT8,
-		OEQ_ | gc.TUINT8,
-		OEQ_ | gc.TINT16,
-		OEQ_ | gc.TUINT16,
-		OEQ_ | gc.TINT32,
-		OEQ_ | gc.TUINT32,
-		OEQ_ | gc.TINT64,
-		OEQ_ | gc.TUINT64,
-		OEQ_ | gc.TPTR32,
-		OEQ_ | gc.TPTR64,
-		OEQ_ | gc.TFLOAT32,
-		OEQ_ | gc.TFLOAT64:
-		a = ppc64.ABEQ
-
-	case ONE_ | gc.TBOOL,
-		ONE_ | gc.TINT8,
-		ONE_ | gc.TUINT8,
-		ONE_ | gc.TINT16,
-		ONE_ | gc.TUINT16,
-		ONE_ | gc.TINT32,
-		ONE_ | gc.TUINT32,
-		ONE_ | gc.TINT64,
-		ONE_ | gc.TUINT64,
-		ONE_ | gc.TPTR32,
-		ONE_ | gc.TPTR64,
-		ONE_ | gc.TFLOAT32,
-		ONE_ | gc.TFLOAT64:
-		a = ppc64.ABNE
-
-	case OLT_ | gc.TINT8, // ACMP
-		OLT_ | gc.TINT16,
-		OLT_ | gc.TINT32,
-		OLT_ | gc.TINT64,
-		OLT_ | gc.TUINT8,
-		// ACMPU
-		OLT_ | gc.TUINT16,
-		OLT_ | gc.TUINT32,
-		OLT_ | gc.TUINT64,
-		OLT_ | gc.TFLOAT32,
-		// AFCMPU
-		OLT_ | gc.TFLOAT64:
-		a = ppc64.ABLT
-
-	case OLE_ | gc.TINT8, // ACMP
-		OLE_ | gc.TINT16,
-		OLE_ | gc.TINT32,
-		OLE_ | gc.TINT64,
-		OLE_ | gc.TUINT8,
-		// ACMPU
-		OLE_ | gc.TUINT16,
-		OLE_ | gc.TUINT32,
-		OLE_ | gc.TUINT64:
-		// No OLE for floats, because it mishandles NaN.
-		// Front end must reverse comparison or use OLT and OEQ together.
-		a = ppc64.ABLE
-
-	case OGT_ | gc.TINT8,
-		OGT_ | gc.TINT16,
-		OGT_ | gc.TINT32,
-		OGT_ | gc.TINT64,
-		OGT_ | gc.TUINT8,
-		OGT_ | gc.TUINT16,
-		OGT_ | gc.TUINT32,
-		OGT_ | gc.TUINT64,
-		OGT_ | gc.TFLOAT32,
-		OGT_ | gc.TFLOAT64:
-		a = ppc64.ABGT
-
-	case OGE_ | gc.TINT8,
-		OGE_ | gc.TINT16,
-		OGE_ | gc.TINT32,
-		OGE_ | gc.TINT64,
-		OGE_ | gc.TUINT8,
-		OGE_ | gc.TUINT16,
-		OGE_ | gc.TUINT32,
-		OGE_ | gc.TUINT64:
-		// No OGE for floats, because it mishandles NaN.
-		// Front end must reverse comparison or use OLT and OEQ together.
-		a = ppc64.ABGE
-
-	case OCMP_ | gc.TBOOL,
-		OCMP_ | gc.TINT8,
-		OCMP_ | gc.TINT16,
-		OCMP_ | gc.TINT32,
-		OCMP_ | gc.TPTR32,
-		OCMP_ | gc.TINT64:
-		a = ppc64.ACMP
-
-	case OCMP_ | gc.TUINT8,
-		OCMP_ | gc.TUINT16,
-		OCMP_ | gc.TUINT32,
-		OCMP_ | gc.TUINT64,
-		OCMP_ | gc.TPTR64:
-		a = ppc64.ACMPU
-
-	case OCMP_ | gc.TFLOAT32,
-		OCMP_ | gc.TFLOAT64:
-		a = ppc64.AFCMPU
-
-	case OAS_ | gc.TBOOL,
-		OAS_ | gc.TINT8:
-		a = ppc64.AMOVB
-
-	case OAS_ | gc.TUINT8:
-		a = ppc64.AMOVBZ
-
-	case OAS_ | gc.TINT16:
-		a = ppc64.AMOVH
-
-	case OAS_ | gc.TUINT16:
-		a = ppc64.AMOVHZ
-
-	case OAS_ | gc.TINT32:
-		a = ppc64.AMOVW
-
-	case OAS_ | gc.TUINT32,
-		OAS_ | gc.TPTR32:
-		a = ppc64.AMOVWZ
-
-	case OAS_ | gc.TINT64,
-		OAS_ | gc.TUINT64,
-		OAS_ | gc.TPTR64:
-		a = ppc64.AMOVD
-
-	case OAS_ | gc.TFLOAT32:
-		a = ppc64.AFMOVS
-
-	case OAS_ | gc.TFLOAT64:
-		a = ppc64.AFMOVD
-
-	case OADD_ | gc.TINT8,
-		OADD_ | gc.TUINT8,
-		OADD_ | gc.TINT16,
-		OADD_ | gc.TUINT16,
-		OADD_ | gc.TINT32,
-		OADD_ | gc.TUINT32,
-		OADD_ | gc.TPTR32,
-		OADD_ | gc.TINT64,
-		OADD_ | gc.TUINT64,
-		OADD_ | gc.TPTR64:
-		a = ppc64.AADD
-
-	case OADD_ | gc.TFLOAT32:
-		a = ppc64.AFADDS
-
-	case OADD_ | gc.TFLOAT64:
-		a = ppc64.AFADD
-
-	case OSUB_ | gc.TINT8,
-		OSUB_ | gc.TUINT8,
-		OSUB_ | gc.TINT16,
-		OSUB_ | gc.TUINT16,
-		OSUB_ | gc.TINT32,
-		OSUB_ | gc.TUINT32,
-		OSUB_ | gc.TPTR32,
-		OSUB_ | gc.TINT64,
-		OSUB_ | gc.TUINT64,
-		OSUB_ | gc.TPTR64:
-		a = ppc64.ASUB
-
-	case OSUB_ | gc.TFLOAT32:
-		a = ppc64.AFSUBS
-
-	case OSUB_ | gc.TFLOAT64:
-		a = ppc64.AFSUB
-
-	case OMINUS_ | gc.TINT8,
-		OMINUS_ | gc.TUINT8,
-		OMINUS_ | gc.TINT16,
-		OMINUS_ | gc.TUINT16,
-		OMINUS_ | gc.TINT32,
-		OMINUS_ | gc.TUINT32,
-		OMINUS_ | gc.TPTR32,
-		OMINUS_ | gc.TINT64,
-		OMINUS_ | gc.TUINT64,
-		OMINUS_ | gc.TPTR64:
-		a = ppc64.ANEG
-
-	case OAND_ | gc.TINT8,
-		OAND_ | gc.TUINT8,
-		OAND_ | gc.TINT16,
-		OAND_ | gc.TUINT16,
-		OAND_ | gc.TINT32,
-		OAND_ | gc.TUINT32,
-		OAND_ | gc.TPTR32,
-		OAND_ | gc.TINT64,
-		OAND_ | gc.TUINT64,
-		OAND_ | gc.TPTR64:
-		a = ppc64.AAND
-
-	case OOR_ | gc.TINT8,
-		OOR_ | gc.TUINT8,
-		OOR_ | gc.TINT16,
-		OOR_ | gc.TUINT16,
-		OOR_ | gc.TINT32,
-		OOR_ | gc.TUINT32,
-		OOR_ | gc.TPTR32,
-		OOR_ | gc.TINT64,
-		OOR_ | gc.TUINT64,
-		OOR_ | gc.TPTR64:
-		a = ppc64.AOR
-
-	case OXOR_ | gc.TINT8,
-		OXOR_ | gc.TUINT8,
-		OXOR_ | gc.TINT16,
-		OXOR_ | gc.TUINT16,
-		OXOR_ | gc.TINT32,
-		OXOR_ | gc.TUINT32,
-		OXOR_ | gc.TPTR32,
-		OXOR_ | gc.TINT64,
-		OXOR_ | gc.TUINT64,
-		OXOR_ | gc.TPTR64:
-		a = ppc64.AXOR
-
-		// TODO(minux): handle rotates
-	//case CASE(OLROT, TINT8):
-	//case CASE(OLROT, TUINT8):
-	//case CASE(OLROT, TINT16):
-	//case CASE(OLROT, TUINT16):
-	//case CASE(OLROT, TINT32):
-	//case CASE(OLROT, TUINT32):
-	//case CASE(OLROT, TPTR32):
-	//case CASE(OLROT, TINT64):
-	//case CASE(OLROT, TUINT64):
-	//case CASE(OLROT, TPTR64):
-	//	a = 0//???; RLDC?
-	//	break;
-
-	case OLSH_ | gc.TINT8,
-		OLSH_ | gc.TUINT8,
-		OLSH_ | gc.TINT16,
-		OLSH_ | gc.TUINT16,
-		OLSH_ | gc.TINT32,
-		OLSH_ | gc.TUINT32,
-		OLSH_ | gc.TPTR32,
-		OLSH_ | gc.TINT64,
-		OLSH_ | gc.TUINT64,
-		OLSH_ | gc.TPTR64:
-		a = ppc64.ASLD
-
-	case ORSH_ | gc.TUINT8,
-		ORSH_ | gc.TUINT16,
-		ORSH_ | gc.TUINT32,
-		ORSH_ | gc.TPTR32,
-		ORSH_ | gc.TUINT64,
-		ORSH_ | gc.TPTR64:
-		a = ppc64.ASRD
-
-	case ORSH_ | gc.TINT8,
-		ORSH_ | gc.TINT16,
-		ORSH_ | gc.TINT32,
-		ORSH_ | gc.TINT64:
-		a = ppc64.ASRAD
-
-		// TODO(minux): handle rotates
-	//case CASE(ORROTC, TINT8):
-	//case CASE(ORROTC, TUINT8):
-	//case CASE(ORROTC, TINT16):
-	//case CASE(ORROTC, TUINT16):
-	//case CASE(ORROTC, TINT32):
-	//case CASE(ORROTC, TUINT32):
-	//case CASE(ORROTC, TINT64):
-	//case CASE(ORROTC, TUINT64):
-	//	a = 0//??? RLDC??
-	//	break;
-
-	case OHMUL_ | gc.TINT64:
-		a = ppc64.AMULHD
-
-	case OHMUL_ | gc.TUINT64,
-		OHMUL_ | gc.TPTR64:
-		a = ppc64.AMULHDU
-
-	case OMUL_ | gc.TINT8,
-		OMUL_ | gc.TINT16,
-		OMUL_ | gc.TINT32,
-		OMUL_ | gc.TINT64:
-		a = ppc64.AMULLD
-
-	case OMUL_ | gc.TUINT8,
-		OMUL_ | gc.TUINT16,
-		OMUL_ | gc.TUINT32,
-		OMUL_ | gc.TPTR32,
-		// don't use word multiply, the high 32-bit are undefined.
-		OMUL_ | gc.TUINT64,
-		OMUL_ | gc.TPTR64:
-		// for 64-bit multiplies, signedness doesn't matter.
-		a = ppc64.AMULLD
-
-	case OMUL_ | gc.TFLOAT32:
-		a = ppc64.AFMULS
-
-	case OMUL_ | gc.TFLOAT64:
-		a = ppc64.AFMUL
-
-	case ODIV_ | gc.TINT8,
-		ODIV_ | gc.TINT16,
-		ODIV_ | gc.TINT32,
-		ODIV_ | gc.TINT64:
-		a = ppc64.ADIVD
-
-	case ODIV_ | gc.TUINT8,
-		ODIV_ | gc.TUINT16,
-		ODIV_ | gc.TUINT32,
-		ODIV_ | gc.TPTR32,
-		ODIV_ | gc.TUINT64,
-		ODIV_ | gc.TPTR64:
-		a = ppc64.ADIVDU
-
-	case ODIV_ | gc.TFLOAT32:
-		a = ppc64.AFDIVS
-
-	case ODIV_ | gc.TFLOAT64:
-		a = ppc64.AFDIV
-
-	case OSQRT_ | gc.TFLOAT64:
-		a = ppc64.AFSQRT
-	}
-
-	return a
-}
-
-const (
-	ODynam   = 1 << 0
-	OAddable = 1 << 1
-)
-
-func xgen(n *gc.Node, a *gc.Node, o int) bool {
-	// TODO(minux)
-
-	return -1 != 0 /*TypeKind(100016)*/
-}
-
-func sudoclean() {
-	return
-}
-
-/*
- * generate code to compute address of n,
- * a reference to a (perhaps nested) field inside
- * an array or struct.
- * return 0 on failure, 1 on success.
- * on success, leaves usable address in a.
- *
- * caller is responsible for calling sudoclean
- * after successful sudoaddable,
- * to release the register used for a.
- */
-func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool {
-	// TODO(minux)
-
-	*a = obj.Addr{}
-	return false
-}
diff --git a/src/cmd/compile/internal/ppc64/peep.go b/src/cmd/compile/internal/ppc64/peep.go
deleted file mode 100644
index 6efe0b7..0000000
--- a/src/cmd/compile/internal/ppc64/peep.go
+++ /dev/null
@@ -1,1032 +0,0 @@
-// Derived from Inferno utils/6c/peep.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package ppc64
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/ppc64"
-	"fmt"
-)
-
-var gactive uint32
-
-func peep(firstp *obj.Prog) {
-	g := gc.Flowstart(firstp, nil)
-	if g == nil {
-		return
-	}
-	gactive = 0
-
-	var p *obj.Prog
-	var r *gc.Flow
-	var t obj.As
-loop1:
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		gc.Dumpit("loop1", g.Start, 0)
-	}
-
-	t = 0
-	for r = g.Start; r != nil; r = r.Link {
-		p = r.Prog
-
-		// TODO(austin) Handle smaller moves.  arm and amd64
-		// distinguish between moves that moves that *must*
-		// sign/zero extend and moves that don't care so they
-		// can eliminate moves that don't care without
-		// breaking moves that do care. This might let us
-		// simplify or remove the next peep loop, too.
-		if p.As == ppc64.AMOVD || p.As == ppc64.AFMOVD {
-			if regtyp(&p.To) {
-				// Try to eliminate reg->reg moves
-				if regtyp(&p.From) {
-					if p.From.Type == p.To.Type {
-						if copyprop(r) {
-							excise(r)
-							t++
-						} else if subprop(r) && copyprop(r) {
-							excise(r)
-							t++
-						}
-					}
-				}
-
-				// Convert uses to $0 to uses of R0 and
-				// propagate R0
-				if regzer(&p.From) {
-					if p.To.Type == obj.TYPE_REG {
-						p.From.Type = obj.TYPE_REG
-						p.From.Reg = ppc64.REGZERO
-						if copyprop(r) {
-							excise(r)
-							t++
-						} else if subprop(r) && copyprop(r) {
-							excise(r)
-							t++
-						}
-					}
-				}
-			}
-		}
-	}
-
-	if t != 0 {
-		goto loop1
-	}
-
-	/*
-	 * look for MOVB x,R; MOVB R,R (for small MOVs not handled above)
-	 */
-	var p1 *obj.Prog
-	var r1 *gc.Flow
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		default:
-			continue
-
-		case ppc64.AMOVH,
-			ppc64.AMOVHZ,
-			ppc64.AMOVB,
-			ppc64.AMOVBZ,
-			ppc64.AMOVW,
-			ppc64.AMOVWZ:
-			if p.To.Type != obj.TYPE_REG {
-				continue
-			}
-		}
-
-		r1 = r.Link
-		if r1 == nil {
-			continue
-		}
-		p1 = r1.Prog
-		if p1.As != p.As {
-			continue
-		}
-		if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
-			continue
-		}
-		if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg {
-			continue
-		}
-		excise(r1)
-	}
-
-	if gc.Debug['D'] > 1 {
-		goto ret /* allow following code improvement to be suppressed */
-	}
-
-	/*
-	 * look for OP x,y,R; CMP R, $0 -> OPCC x,y,R
-	 * when OP can set condition codes correctly
-	 */
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		case ppc64.ACMP,
-			ppc64.ACMPW: /* always safe? */
-			if !regzer(&p.To) {
-				continue
-			}
-			r1 = r.S1
-			if r1 == nil {
-				continue
-			}
-			switch r1.Prog.As {
-			default:
-				continue
-
-				/* the conditions can be complex and these are currently little used */
-			case ppc64.ABCL,
-				ppc64.ABC:
-				continue
-
-			case ppc64.ABEQ,
-				ppc64.ABGE,
-				ppc64.ABGT,
-				ppc64.ABLE,
-				ppc64.ABLT,
-				ppc64.ABNE,
-				ppc64.ABVC,
-				ppc64.ABVS:
-				break
-			}
-
-			r1 = r
-			for {
-				r1 = gc.Uniqp(r1)
-				if r1 == nil || r1.Prog.As != obj.ANOP {
-					break
-				}
-			}
-
-			if r1 == nil {
-				continue
-			}
-			p1 = r1.Prog
-			if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.From.Reg {
-				continue
-			}
-			switch p1.As {
-			/* irregular instructions */
-			case ppc64.ASUB,
-				ppc64.AADD,
-				ppc64.AXOR,
-				ppc64.AOR:
-				if p1.From.Type == obj.TYPE_CONST || p1.From.Type == obj.TYPE_ADDR {
-					continue
-				}
-			}
-
-			switch p1.As {
-			default:
-				continue
-
-			case ppc64.AMOVW,
-				ppc64.AMOVD:
-				if p1.From.Type != obj.TYPE_REG {
-					continue
-				}
-				continue
-
-			case ppc64.AANDCC,
-				ppc64.AANDNCC,
-				ppc64.AORCC,
-				ppc64.AORNCC,
-				ppc64.AXORCC,
-				ppc64.ASUBCC,
-				ppc64.ASUBECC,
-				ppc64.ASUBMECC,
-				ppc64.ASUBZECC,
-				ppc64.AADDCC,
-				ppc64.AADDCCC,
-				ppc64.AADDECC,
-				ppc64.AADDMECC,
-				ppc64.AADDZECC,
-				ppc64.ARLWMICC,
-				ppc64.ARLWNMCC,
-				/* don't deal with floating point instructions for now */
-				/*
-					case AFABS:
-					case AFADD:
-					case AFADDS:
-					case AFCTIW:
-					case AFCTIWZ:
-					case AFDIV:
-					case AFDIVS:
-					case AFMADD:
-					case AFMADDS:
-					case AFMOVD:
-					case AFMSUB:
-					case AFMSUBS:
-					case AFMUL:
-					case AFMULS:
-					case AFNABS:
-					case AFNEG:
-					case AFNMADD:
-					case AFNMADDS:
-					case AFNMSUB:
-					case AFNMSUBS:
-					case AFRSP:
-					case AFSUB:
-					case AFSUBS:
-					case ACNTLZW:
-					case AMTFSB0:
-					case AMTFSB1:
-				*/
-				ppc64.AADD,
-				ppc64.AADDV,
-				ppc64.AADDC,
-				ppc64.AADDCV,
-				ppc64.AADDME,
-				ppc64.AADDMEV,
-				ppc64.AADDE,
-				ppc64.AADDEV,
-				ppc64.AADDZE,
-				ppc64.AADDZEV,
-				ppc64.AAND,
-				ppc64.AANDN,
-				ppc64.ADIVW,
-				ppc64.ADIVWV,
-				ppc64.ADIVWU,
-				ppc64.ADIVWUV,
-				ppc64.ADIVD,
-				ppc64.ADIVDV,
-				ppc64.ADIVDU,
-				ppc64.ADIVDUV,
-				ppc64.AEQV,
-				ppc64.AEXTSB,
-				ppc64.AEXTSH,
-				ppc64.AEXTSW,
-				ppc64.AMULHW,
-				ppc64.AMULHWU,
-				ppc64.AMULLW,
-				ppc64.AMULLWV,
-				ppc64.AMULHD,
-				ppc64.AMULHDU,
-				ppc64.AMULLD,
-				ppc64.AMULLDV,
-				ppc64.ANAND,
-				ppc64.ANEG,
-				ppc64.ANEGV,
-				ppc64.ANOR,
-				ppc64.AOR,
-				ppc64.AORN,
-				ppc64.AREM,
-				ppc64.AREMV,
-				ppc64.AREMU,
-				ppc64.AREMUV,
-				ppc64.AREMD,
-				ppc64.AREMDV,
-				ppc64.AREMDU,
-				ppc64.AREMDUV,
-				ppc64.ARLWMI,
-				ppc64.ARLWNM,
-				ppc64.ASLW,
-				ppc64.ASRAW,
-				ppc64.ASRW,
-				ppc64.ASLD,
-				ppc64.ASRAD,
-				ppc64.ASRD,
-				ppc64.ASUB,
-				ppc64.ASUBV,
-				ppc64.ASUBC,
-				ppc64.ASUBCV,
-				ppc64.ASUBME,
-				ppc64.ASUBMEV,
-				ppc64.ASUBE,
-				ppc64.ASUBEV,
-				ppc64.ASUBZE,
-				ppc64.ASUBZEV,
-				ppc64.AXOR:
-				t = variant2as(p1.As, as2variant(p1.As)|V_CC)
-			}
-
-			if gc.Debug['D'] != 0 {
-				fmt.Printf("cmp %v; %v -> ", p1, p)
-			}
-			p1.As = t
-			if gc.Debug['D'] != 0 {
-				fmt.Printf("%v\n", p1)
-			}
-			excise(r)
-			continue
-		}
-	}
-
-ret:
-	gc.Flowend(g)
-}
-
-func excise(r *gc.Flow) {
-	p := r.Prog
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("%v ===delete===\n", p)
-	}
-	obj.Nopout(p)
-	gc.Ostats.Ndelmov++
-}
-
-// regzer returns true if a's value is 0 (a is R0 or $0)
-func regzer(a *obj.Addr) bool {
-	if a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_ADDR {
-		if a.Sym == nil && a.Reg == 0 {
-			if a.Offset == 0 {
-				return true
-			}
-		}
-	}
-	return a.Type == obj.TYPE_REG && a.Reg == ppc64.REGZERO
-}
-
-func regtyp(a *obj.Addr) bool {
-	// TODO(rsc): Floating point register exclusions?
-	return a.Type == obj.TYPE_REG && ppc64.REG_R0 <= a.Reg && a.Reg <= ppc64.REG_F31 && a.Reg != ppc64.REGZERO
-}
-
-/*
- * the idea is to substitute
- * one register for another
- * from one MOV to another
- *	MOV	a, R1
- *	ADD	b, R1	/ no use of R2
- *	MOV	R1, R2
- * would be converted to
- *	MOV	a, R2
- *	ADD	b, R2
- *	MOV	R2, R1
- * hopefully, then the former or latter MOV
- * will be eliminated by copy propagation.
- *
- * r0 (the argument, not the register) is the MOV at the end of the
- * above sequences.  This returns 1 if it modified any instructions.
- */
-func subprop(r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	if !regtyp(v1) {
-		return false
-	}
-	v2 := &p.To
-	if !regtyp(v2) {
-		return false
-	}
-	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
-		if gc.Uniqs(r) == nil {
-			break
-		}
-		p = r.Prog
-		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
-			continue
-		}
-		if p.Info.Flags&gc.Call != 0 {
-			return false
-		}
-
-		if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite {
-			if p.To.Type == v1.Type {
-				if p.To.Reg == v1.Reg {
-					copysub(&p.To, v1, v2, true)
-					if gc.Debug['P'] != 0 {
-						fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
-						if p.From.Type == v2.Type {
-							fmt.Printf(" excise")
-						}
-						fmt.Printf("\n")
-					}
-
-					for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
-						p = r.Prog
-						copysub(&p.From, v1, v2, true)
-						copysub1(p, v1, v2, true)
-						copysub(&p.To, v1, v2, true)
-						if gc.Debug['P'] != 0 {
-							fmt.Printf("%v\n", r.Prog)
-						}
-					}
-
-					v1.Reg, v2.Reg = v2.Reg, v1.Reg
-					if gc.Debug['P'] != 0 {
-						fmt.Printf("%v last\n", r.Prog)
-					}
-					return true
-				}
-			}
-		}
-
-		if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) {
-			break
-		}
-		if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) {
-			break
-		}
-	}
-
-	return false
-}
-
-/*
- * The idea is to remove redundant copies.
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	use v2	return fail (v1->v2 move must remain)
- *	-----------------
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	set v2	return success (caller can remove v1->v2 move)
- */
-func copyprop(r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	v2 := &p.To
-	if copyas(v1, v2) {
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("eliminating self-move: %v\n", r0.Prog)
-		}
-		return true
-	}
-
-	gactive++
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog)
-	}
-	return copy1(v1, v2, r0.S1, false)
-}
-
-// copy1 replaces uses of v2 with v1 starting at r and returns 1 if
-// all uses were rewritten.
-func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool {
-	if uint32(r.Active) == gactive {
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("act set; return 1\n")
-		}
-		return true
-	}
-
-	r.Active = int32(gactive)
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("copy1 replace %v with %v f=%v\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f)
-	}
-	for ; r != nil; r = r.S1 {
-		p := r.Prog
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("%v", p)
-		}
-		if !f && gc.Uniqp(r) == nil {
-			// Multiple predecessors; conservatively
-			// assume v1 was set on other path
-			f = true
-
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; merge; f=%v", f)
-			}
-		}
-
-		switch t := copyu(p, v2, nil); t {
-		case 2: /* rar, can't split */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
-			}
-			return false
-
-		case 3: /* set */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
-			}
-			return true
-
-		case 1, /* used, substitute */
-			4: /* use and set */
-			if f {
-				if gc.Debug['P'] == 0 {
-					return false
-				}
-				if t == 4 {
-					fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				} else {
-					fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				}
-				return false
-			}
-
-			if copyu(p, v2, v1) != 0 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; sub fail; return 0\n")
-				}
-				return false
-			}
-
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p)
-			}
-			if t == 4 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
-				}
-				return true
-			}
-		}
-
-		if !f {
-			t := copyu(p, v1, nil)
-			if t == 2 || t == 3 || t == 4 {
-				f = true
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f)
-				}
-			}
-		}
-
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("\n")
-		}
-		if r.S2 != nil {
-			if !copy1(v1, v2, r.S2, f) {
-				return false
-			}
-		}
-	}
-
-	return true
-}
-
-// If s==nil, copyu returns the set/use of v in p; otherwise, it
-// modifies p to replace reads of v with reads of s and returns 0 for
-// success or non-zero for failure.
-//
-// If s==nil, copy returns one of the following values:
-// 	1 if v only used
-//	2 if v is set and used in one address (read-alter-rewrite;
-// 	  can't substitute)
-//	3 if v is only set
-//	4 if v is set in one address and used in another (so addresses
-// 	  can be rewritten independently)
-//	0 otherwise (not touched)
-func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
-	if p.From3Type() != obj.TYPE_NONE {
-		// 9g never generates a from3
-		fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3))
-	}
-
-	switch p.As {
-	default:
-		fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As))
-		return 2
-
-	case obj.ANOP, /* read p->from, write p->to */
-		ppc64.AMOVH,
-		ppc64.AMOVHZ,
-		ppc64.AMOVB,
-		ppc64.AMOVBZ,
-		ppc64.AMOVW,
-		ppc64.AMOVWZ,
-		ppc64.AMOVD,
-		ppc64.ANEG,
-		ppc64.ANEGCC,
-		ppc64.AADDME,
-		ppc64.AADDMECC,
-		ppc64.AADDZE,
-		ppc64.AADDZECC,
-		ppc64.ASUBME,
-		ppc64.ASUBMECC,
-		ppc64.ASUBZE,
-		ppc64.ASUBZECC,
-		ppc64.AFCTIW,
-		ppc64.AFCTIWZ,
-		ppc64.AFCTID,
-		ppc64.AFCTIDZ,
-		ppc64.AFCFID,
-		ppc64.AFCFIDCC,
-		ppc64.AFCFIDU,
-		ppc64.AFCFIDUCC,
-		ppc64.AFMOVS,
-		ppc64.AFMOVD,
-		ppc64.AFRSP,
-		ppc64.AFNEG,
-		ppc64.AFNEGCC,
-		ppc64.AFSQRT:
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-
-			// Update only indirect uses of v in p->to
-			if !copyas(&p.To, v) {
-				if copysub(&p.To, v, s, true) {
-					return 1
-				}
-			}
-			return 0
-		}
-
-		if copyas(&p.To, v) {
-			// Fix up implicit from
-			if p.From.Type == obj.TYPE_NONE {
-				p.From = p.To
-			}
-			if copyau(&p.From, v) {
-				return 4
-			}
-			return 3
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau(&p.To, v) {
-			// p->to only indirectly uses v
-			return 1
-		}
-
-		return 0
-
-	case ppc64.AMOVBU, /* rar p->from, write p->to or read p->from, rar p->to */
-		ppc64.AMOVBZU,
-		ppc64.AMOVHU,
-		ppc64.AMOVHZU,
-		ppc64.AMOVWZU,
-		ppc64.AMOVDU:
-		if p.From.Type == obj.TYPE_MEM {
-			if copyas(&p.From, v) {
-				// No s!=nil check; need to fail
-				// anyway in that case
-				return 2
-			}
-
-			if s != nil {
-				if copysub(&p.To, v, s, true) {
-					return 1
-				}
-				return 0
-			}
-
-			if copyas(&p.To, v) {
-				return 3
-			}
-		} else if p.To.Type == obj.TYPE_MEM {
-			if copyas(&p.To, v) {
-				return 2
-			}
-			if s != nil {
-				if copysub(&p.From, v, s, true) {
-					return 1
-				}
-				return 0
-			}
-
-			if copyau(&p.From, v) {
-				return 1
-			}
-		} else {
-			fmt.Printf("copyu: bad %v\n", p)
-		}
-
-		return 0
-
-	case ppc64.ARLWMI, /* read p->from, read p->reg, rar p->to */
-		ppc64.ARLWMICC:
-		if copyas(&p.To, v) {
-			return 2
-		}
-		fallthrough
-
-		/* fall through */
-	case ppc64.AADD,
-		/* read p->from, read p->reg, write p->to */
-		ppc64.AADDC,
-		ppc64.AADDE,
-		ppc64.ASUB,
-		ppc64.ASLW,
-		ppc64.ASRW,
-		ppc64.ASRAW,
-		ppc64.ASLD,
-		ppc64.ASRD,
-		ppc64.ASRAD,
-		ppc64.AOR,
-		ppc64.AORCC,
-		ppc64.AORN,
-		ppc64.AORNCC,
-		ppc64.AAND,
-		ppc64.AANDCC,
-		ppc64.AANDN,
-		ppc64.AANDNCC,
-		ppc64.ANAND,
-		ppc64.ANANDCC,
-		ppc64.ANOR,
-		ppc64.ANORCC,
-		ppc64.AXOR,
-		ppc64.AMULHW,
-		ppc64.AMULHWU,
-		ppc64.AMULLW,
-		ppc64.AMULLD,
-		ppc64.ADIVW,
-		ppc64.ADIVD,
-		ppc64.ADIVWU,
-		ppc64.ADIVDU,
-		ppc64.AREM,
-		ppc64.AREMU,
-		ppc64.AREMD,
-		ppc64.AREMDU,
-		ppc64.ARLWNM,
-		ppc64.ARLWNMCC,
-		ppc64.AFADDS,
-		ppc64.AFADD,
-		ppc64.AFSUBS,
-		ppc64.AFSUB,
-		ppc64.AFMULS,
-		ppc64.AFMUL,
-		ppc64.AFDIVS,
-		ppc64.AFDIV:
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if copysub1(p, v, s, true) {
-				return 1
-			}
-
-			// Update only indirect uses of v in p->to
-			if !copyas(&p.To, v) {
-				if copysub(&p.To, v, s, true) {
-					return 1
-				}
-			}
-			return 0
-		}
-
-		if copyas(&p.To, v) {
-			if p.Reg == 0 {
-				// Fix up implicit reg (e.g., ADD
-				// R3,R4 -> ADD R3,R4,R4) so we can
-				// update reg and to separately.
-				p.Reg = p.To.Reg
-			}
-
-			if copyau(&p.From, v) {
-				return 4
-			}
-			if copyau1(p, v) {
-				return 4
-			}
-			return 3
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau1(p, v) {
-			return 1
-		}
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case ppc64.ABEQ,
-		ppc64.ABGT,
-		ppc64.ABGE,
-		ppc64.ABLT,
-		ppc64.ABLE,
-		ppc64.ABNE,
-		ppc64.ABVC,
-		ppc64.ABVS:
-		return 0
-
-	case obj.ACHECKNIL, /* read p->from */
-		ppc64.ACMP, /* read p->from, read p->to */
-		ppc64.ACMPU,
-		ppc64.ACMPW,
-		ppc64.ACMPWU,
-		ppc64.AFCMPO,
-		ppc64.AFCMPU:
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-		// 9g never generates a branch to a GPR (this isn't
-	// even a normal instruction; liblink turns it in to a
-	// mov and a branch).
-	case ppc64.ABR: /* read p->to */
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case obj.ARET: /* funny */
-		if s != nil {
-			return 0
-		}
-
-		// All registers die at this point, so claim
-		// everything is set (and not used).
-		return 3
-
-	case ppc64.ABL: /* funny */
-		if v.Type == obj.TYPE_REG {
-			// TODO(rsc): REG_R0 and REG_F0 used to be
-			// (when register numbers started at 0) exregoffset and exfregoffset,
-			// which are unset entirely.
-			// It's strange that this handles R0 and F0 differently from the other
-			// registers. Possible failure to optimize?
-			if ppc64.REG_R0 < v.Reg && v.Reg <= ppc64.REGEXT {
-				return 2
-			}
-			if v.Reg == ppc64.REGARG {
-				return 2
-			}
-			if ppc64.REG_F0 < v.Reg && v.Reg <= ppc64.FREGEXT {
-				return 2
-			}
-		}
-
-		if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
-			return 2
-		}
-
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-		if copyau(&p.To, v) {
-			return 4
-		}
-		return 3
-
-		// R0 is zero, used by DUFFZERO, cannot be substituted.
-	// R3 is ptr to memory, used and set, cannot be substituted.
-	case obj.ADUFFZERO:
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == 0 {
-				return 1
-			}
-			if v.Reg == 3 {
-				return 2
-			}
-		}
-
-		return 0
-
-		// R3, R4 are ptr to src, dst, used and set, cannot be substituted.
-	// R5 is scratch, set by DUFFCOPY, cannot be substituted.
-	case obj.ADUFFCOPY:
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == 3 || v.Reg == 4 {
-				return 2
-			}
-			if v.Reg == 5 {
-				return 3
-			}
-		}
-
-		return 0
-
-	case obj.ATEXT: /* funny */
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == ppc64.REGARG {
-				return 3
-			}
-		}
-		return 0
-
-	case obj.APCDATA,
-		obj.AFUNCDATA,
-		obj.AVARDEF,
-		obj.AVARKILL,
-		obj.AVARLIVE,
-		obj.AUSEFIELD:
-		return 0
-	}
-}
-
-// copyas returns true if a and v address the same register.
-//
-// If a is the from operand, this means this operation reads the
-// register in v. If a is the to operand, this means this operation
-// writes the register in v.
-func copyas(a *obj.Addr, v *obj.Addr) bool {
-	return regtyp(v) && a.Type == v.Type && a.Reg == v.Reg
-}
-
-// copyau returns true if a either directly or indirectly addresses the
-// same register as v.
-//
-// If a is the from operand, this means this operation reads the
-// register in v. If a is the to operand, this means the operation
-// either reads or writes the register in v (if !copyas(a, v), then
-// the operation reads the register in v).
-func copyau(a *obj.Addr, v *obj.Addr) bool {
-	if copyas(a, v) {
-		return true
-	}
-	if v.Type == obj.TYPE_REG {
-		if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) {
-			if v.Reg == a.Reg {
-				return true
-			}
-		}
-	}
-	return false
-}
-
-// copyau1 returns true if p->reg references the same register as v and v
-// is a direct reference.
-func copyau1(p *obj.Prog, v *obj.Addr) bool {
-	return regtyp(v) && v.Reg != 0 && p.Reg == v.Reg
-}
-
-// copysub replaces v with s in a if f==true or indicates it if could if f==false.
-// Returns true on failure to substitute (it always succeeds on ppc64).
-// TODO(dfc) remove unused return value and callers where f=false.
-func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool {
-	if f && copyau(a, v) {
-		a.Reg = s.Reg
-	}
-	return false
-}
-
-// copysub1 replaces v with s in p1->reg if f==true or indicates if it could if f==false.
-// Returns true on failure to substitute (it always succeeds on ppc64).
-// TODO(dfc) remove unused return value and callers where f=false.
-func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool {
-	if f && copyau1(p1, v) {
-		p1.Reg = s.Reg
-	}
-	return false
-}
-
-func sameaddr(a *obj.Addr, v *obj.Addr) bool {
-	if a.Type != v.Type {
-		return false
-	}
-	if regtyp(v) && a.Reg == v.Reg {
-		return true
-	}
-	if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM {
-		if v.Offset == a.Offset {
-			return true
-		}
-	}
-	return false
-}
-
-func smallindir(a *obj.Addr, reg *obj.Addr) bool {
-	return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096
-}
-
-func stackaddr(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_REG && a.Reg == ppc64.REGSP
-}
diff --git a/src/cmd/compile/internal/ppc64/prog.go b/src/cmd/compile/internal/ppc64/prog.go
index e2d81ae..59cbaa1 100644
--- a/src/cmd/compile/internal/ppc64/prog.go
+++ b/src/cmd/compile/internal/ppc64/prog.go
@@ -24,14 +24,13 @@ const (
 // size variants of an operation even if we just use a subset.
 //
 // The table is formatted for 8-space tabs.
-var progtable = [ppc64.ALAST & obj.AMask]obj.ProgInfo{
+var progtable = [ppc64.ALAST & obj.AMask]gc.ProgInfo{
 	obj.ATYPE:     {Flags: gc.Pseudo | gc.Skip},
 	obj.ATEXT:     {Flags: gc.Pseudo},
 	obj.AFUNCDATA: {Flags: gc.Pseudo},
 	obj.APCDATA:   {Flags: gc.Pseudo},
 	obj.AUNDEF:    {Flags: gc.Break},
 	obj.AUSEFIELD: {Flags: gc.OK},
-	obj.ACHECKNIL: {Flags: gc.LeftRead},
 	obj.AVARDEF:   {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARKILL:  {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARLIVE:  {Flags: gc.Pseudo | gc.LeftRead},
@@ -42,22 +41,36 @@ var progtable = [ppc64.ALAST & obj.AMask]obj.ProgInfo{
 
 	// Integer
 	ppc64.AADD & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.AADDC & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.ASUB & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.AADDME & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.ANEG & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.AAND & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.AANDN & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.AOR & obj.AMask:     {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.AORN & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.AXOR & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.AEQV & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.AMULLD & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.AMULLW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	ppc64.AMULHD & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	ppc64.AMULHDU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.AMULHD & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.AMULHDU & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.AMULHW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.AMULHWU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.ADIVD & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.ADIVDU & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.ADIVW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.ADIVWU & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.ASLD & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.ASRD & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.ASRAD & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.ASLW & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.ASRW & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.ASRAW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.ACMP & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead},
 	ppc64.ACMPU & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead},
+	ppc64.ACMPW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
+	ppc64.ACMPWU & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
 	ppc64.ATD & obj.AMask:     {Flags: gc.SizeQ | gc.RightRead},
 
 	// Floating point.
@@ -70,11 +83,13 @@ var progtable = [ppc64.ALAST & obj.AMask]obj.ProgInfo{
 	ppc64.AFDIV & obj.AMask:   {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.AFDIVS & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.AFCTIDZ & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	ppc64.AFCTIWZ & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.AFCFID & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.AFCFIDU & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	ppc64.AFCMPU & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RightRead},
 	ppc64.AFRSP & obj.AMask:   {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
 	ppc64.AFSQRT & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
+	ppc64.AFNEG & obj.AMask:   {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
 
 	// Moves
 	ppc64.AMOVB & obj.AMask:  {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
@@ -85,17 +100,23 @@ var progtable = [ppc64.ALAST & obj.AMask]obj.ProgInfo{
 	ppc64.AMOVHZ & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
 	ppc64.AMOVW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
 
+	ppc64.AISEL & obj.AMask: {Flags: gc.SizeQ | gc.RegRead | gc.From3Read | gc.RightWrite},
+
 	// there is no AMOVWU.
 	ppc64.AMOVWZU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv | gc.PostInc},
 	ppc64.AMOVWZ & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
 	ppc64.AMOVD & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
 	ppc64.AMOVDU & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move | gc.PostInc},
 	ppc64.AFMOVS & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	ppc64.AFMOVSX & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	ppc64.AFMOVSZ & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
 	ppc64.AFMOVD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
 
 	// Jumps
 	ppc64.ABR & obj.AMask:  {Flags: gc.Jump | gc.Break},
 	ppc64.ABL & obj.AMask:  {Flags: gc.Call},
+	ppc64.ABVS & obj.AMask: {Flags: gc.Cjmp},
+	ppc64.ABVC & obj.AMask: {Flags: gc.Cjmp},
 	ppc64.ABEQ & obj.AMask: {Flags: gc.Cjmp},
 	ppc64.ABNE & obj.AMask: {Flags: gc.Cjmp},
 	ppc64.ABGE & obj.AMask: {Flags: gc.Cjmp},
@@ -127,9 +148,8 @@ func initproginfo() {
 	}
 }
 
-func proginfo(p *obj.Prog) {
-	info := &p.Info
-	*info = progtable[p.As&obj.AMask]
+func proginfo(p *obj.Prog) gc.ProgInfo {
+	info := progtable[p.As&obj.AMask]
 	if info.Flags == 0 {
 		gc.Fatalf("proginfo: unknown instruction %v", p)
 	}
@@ -139,36 +159,12 @@ func proginfo(p *obj.Prog) {
 		info.Flags |= gc.RightRead /*CanRegRead |*/
 	}
 
-	if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 {
-		info.Regindex |= RtoB(int(p.From.Reg))
-		if info.Flags&gc.PostInc != 0 {
-			info.Regset |= RtoB(int(p.From.Reg))
-		}
-	}
-
-	if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 {
-		info.Regindex |= RtoB(int(p.To.Reg))
-		if info.Flags&gc.PostInc != 0 {
-			info.Regset |= RtoB(int(p.To.Reg))
-		}
-	}
-
 	if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) {
 		info.Flags &^= gc.LeftRead
 		info.Flags |= gc.LeftAddr
 	}
 
-	if p.As == obj.ADUFFZERO {
-		info.Reguse |= 1<<0 | RtoB(ppc64.REG_R3)
-		info.Regset |= RtoB(ppc64.REG_R3)
-	}
-
-	if p.As == obj.ADUFFCOPY {
-		// TODO(austin) Revisit when duffcopy is implemented
-		info.Reguse |= RtoB(ppc64.REG_R3) | RtoB(ppc64.REG_R4) | RtoB(ppc64.REG_R5)
-
-		info.Regset |= RtoB(ppc64.REG_R3) | RtoB(ppc64.REG_R4)
-	}
+	return info
 }
 
 // Instruction variants table, populated by initvariants via Main.
@@ -297,7 +293,7 @@ func as2variant(as obj.As) int {
 			return i
 		}
 	}
-	gc.Fatalf("as2variant: instruction %v is not a variant of itself", obj.Aconv(as&obj.AMask))
+	gc.Fatalf("as2variant: instruction %v is not a variant of itself", as&obj.AMask)
 	return 0
 }
 
diff --git a/src/cmd/compile/internal/ppc64/reg.go b/src/cmd/compile/internal/ppc64/reg.go
deleted file mode 100644
index 215c9b5..0000000
--- a/src/cmd/compile/internal/ppc64/reg.go
+++ /dev/null
@@ -1,168 +0,0 @@
-// Derived from Inferno utils/6c/reg.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package ppc64
-
-import "cmd/internal/obj/ppc64"
-import "cmd/compile/internal/gc"
-
-const (
-	NREGVAR = 64 /* 32 general + 32 floating */
-)
-
-var regname = []string{
-	".R0",
-	".R1",
-	".R2",
-	".R3",
-	".R4",
-	".R5",
-	".R6",
-	".R7",
-	".R8",
-	".R9",
-	".R10",
-	".R11",
-	".R12",
-	".R13",
-	".R14",
-	".R15",
-	".R16",
-	".R17",
-	".R18",
-	".R19",
-	".R20",
-	".R21",
-	".R22",
-	".R23",
-	".R24",
-	".R25",
-	".R26",
-	".R27",
-	".R28",
-	".R29",
-	".R30",
-	".R31",
-	".F0",
-	".F1",
-	".F2",
-	".F3",
-	".F4",
-	".F5",
-	".F6",
-	".F7",
-	".F8",
-	".F9",
-	".F10",
-	".F11",
-	".F12",
-	".F13",
-	".F14",
-	".F15",
-	".F16",
-	".F17",
-	".F18",
-	".F19",
-	".F20",
-	".F21",
-	".F22",
-	".F23",
-	".F24",
-	".F25",
-	".F26",
-	".F27",
-	".F28",
-	".F29",
-	".F30",
-	".F31",
-}
-
-func regnames(n *int) []string {
-	*n = NREGVAR
-	return regname
-}
-
-func excludedregs() uint64 {
-	// Exclude registers with fixed functions
-	regbits := 1<<0 | RtoB(ppc64.REGSP) | RtoB(ppc64.REGG) | RtoB(ppc64.REGTLS) | RtoB(ppc64.REGTMP)
-
-	if gc.Ctxt.Flag_shared {
-		// When compiling Go into PIC, R2 is reserved to be the TOC pointer
-		// and R12 so that calls via function pointer can stomp on it.
-		regbits |= RtoB(ppc64.REG_R2)
-		regbits |= RtoB(ppc64.REG_R12)
-	}
-	// Also exclude floating point registers with fixed constants
-	regbits |= RtoB(ppc64.REG_F27) | RtoB(ppc64.REG_F28) | RtoB(ppc64.REG_F29) | RtoB(ppc64.REG_F30) | RtoB(ppc64.REG_F31)
-
-	return regbits
-}
-
-func doregbits(r int) uint64 {
-	return 0
-}
-
-/*
- * track register variables including external registers:
- *	bit	reg
- *	0	R0
- *	1	R1
- *	...	...
- *	31	R31
- *	32+0	F0
- *	32+1	F1
- *	...	...
- *	32+31	F31
- */
-func RtoB(r int) uint64 {
-	if r > ppc64.REG_R0 && r <= ppc64.REG_R31 {
-		return 1 << uint(r-ppc64.REG_R0)
-	}
-	if r >= ppc64.REG_F0 && r <= ppc64.REG_F31 {
-		return 1 << uint(32+r-ppc64.REG_F0)
-	}
-	return 0
-}
-
-func BtoR(b uint64) int {
-	b &= 0xffffffff
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + ppc64.REG_R0
-}
-
-func BtoF(b uint64) int {
-	b >>= 32
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + ppc64.REG_F0
-}
diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go
new file mode 100644
index 0000000..8387692
--- /dev/null
+++ b/src/cmd/compile/internal/ppc64/ssa.go
@@ -0,0 +1,938 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ppc64
+
+import (
+	"cmd/compile/internal/gc"
+	"cmd/compile/internal/ssa"
+	"cmd/internal/obj"
+	"cmd/internal/obj/ppc64"
+	"math"
+)
+
+var condOps = map[ssa.Op]obj.As{
+	ssa.OpPPC64Equal:        ppc64.ABEQ,
+	ssa.OpPPC64NotEqual:     ppc64.ABNE,
+	ssa.OpPPC64LessThan:     ppc64.ABLT,
+	ssa.OpPPC64GreaterEqual: ppc64.ABGE,
+	ssa.OpPPC64GreaterThan:  ppc64.ABGT,
+	ssa.OpPPC64LessEqual:    ppc64.ABLE,
+
+	ssa.OpPPC64FLessThan:     ppc64.ABLT, // 1 branch for FCMP
+	ssa.OpPPC64FGreaterThan:  ppc64.ABGT, // 1 branch for FCMP
+	ssa.OpPPC64FLessEqual:    ppc64.ABLT, // 2 branches for FCMP <=, second is BEQ
+	ssa.OpPPC64FGreaterEqual: ppc64.ABGT, // 2 branches for FCMP >=, second is BEQ
+}
+
+// iselOp encodes mapping of comparison operations onto ISEL operands
+type iselOp struct {
+	cond        int64
+	valueIfCond int // if cond is true, the value to return (0 or 1)
+}
+
+// Input registers to ISEL used for comparison. Index 0 is zero, 1 is (will be) 1
+var iselRegs = [2]int16{ppc64.REG_R0, ppc64.REGTMP}
+
+var iselOps = map[ssa.Op]iselOp{
+	ssa.OpPPC64Equal:         iselOp{cond: ppc64.C_COND_EQ, valueIfCond: 1},
+	ssa.OpPPC64NotEqual:      iselOp{cond: ppc64.C_COND_EQ, valueIfCond: 0},
+	ssa.OpPPC64LessThan:      iselOp{cond: ppc64.C_COND_LT, valueIfCond: 1},
+	ssa.OpPPC64GreaterEqual:  iselOp{cond: ppc64.C_COND_LT, valueIfCond: 0},
+	ssa.OpPPC64GreaterThan:   iselOp{cond: ppc64.C_COND_GT, valueIfCond: 1},
+	ssa.OpPPC64LessEqual:     iselOp{cond: ppc64.C_COND_GT, valueIfCond: 0},
+	ssa.OpPPC64FLessThan:     iselOp{cond: ppc64.C_COND_LT, valueIfCond: 1},
+	ssa.OpPPC64FGreaterThan:  iselOp{cond: ppc64.C_COND_GT, valueIfCond: 1},
+	ssa.OpPPC64FLessEqual:    iselOp{cond: ppc64.C_COND_LT, valueIfCond: 1}, // 2 comparisons, 2nd is EQ
+	ssa.OpPPC64FGreaterEqual: iselOp{cond: ppc64.C_COND_GT, valueIfCond: 1}, // 2 comparisons, 2nd is EQ
+}
+
+// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
+func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
+	//	flive := b.FlagsLiveAtEnd
+	//	if b.Control != nil && b.Control.Type.IsFlags() {
+	//		flive = true
+	//	}
+	//	for i := len(b.Values) - 1; i >= 0; i-- {
+	//		v := b.Values[i]
+	//		if flive && (v.Op == v.Op == ssa.OpPPC64MOVDconst) {
+	//			// The "mark" is any non-nil Aux value.
+	//			v.Aux = v
+	//		}
+	//		if v.Type.IsFlags() {
+	//			flive = false
+	//		}
+	//		for _, a := range v.Args {
+	//			if a.Type.IsFlags() {
+	//				flive = true
+	//			}
+	//		}
+	//	}
+}
+
+// loadByType returns the load instruction of the given type.
+func loadByType(t ssa.Type) obj.As {
+	if t.IsFloat() {
+		switch t.Size() {
+		case 4:
+			return ppc64.AFMOVS
+		case 8:
+			return ppc64.AFMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			if t.IsSigned() {
+				return ppc64.AMOVB
+			} else {
+				return ppc64.AMOVBZ
+			}
+		case 2:
+			if t.IsSigned() {
+				return ppc64.AMOVH
+			} else {
+				return ppc64.AMOVHZ
+			}
+		case 4:
+			if t.IsSigned() {
+				return ppc64.AMOVW
+			} else {
+				return ppc64.AMOVWZ
+			}
+		case 8:
+			return ppc64.AMOVD
+		}
+	}
+	panic("bad load type")
+}
+
+// storeByType returns the store instruction of the given type.
+func storeByType(t ssa.Type) obj.As {
+	if t.IsFloat() {
+		switch t.Size() {
+		case 4:
+			return ppc64.AFMOVS
+		case 8:
+			return ppc64.AFMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			return ppc64.AMOVB
+		case 2:
+			return ppc64.AMOVH
+		case 4:
+			return ppc64.AMOVW
+		case 8:
+			return ppc64.AMOVD
+		}
+	}
+	panic("bad store type")
+}
+
+func ssaGenISEL(v *ssa.Value, cr int64, r1, r2 int16) {
+	r := v.Reg()
+	p := gc.Prog(ppc64.AISEL)
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = r
+	p.Reg = r1
+	p.From3 = &obj.Addr{Type: obj.TYPE_REG, Reg: r2}
+	p.From.Type = obj.TYPE_CONST
+	p.From.Offset = cr
+}
+
+func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+	s.SetLineno(v.Line)
+	switch v.Op {
+	case ssa.OpInitMem:
+		// memory arg needs no code
+	case ssa.OpArg:
+		// input args need no code
+	case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
+		// nothing to do
+
+	case ssa.OpCopy, ssa.OpPPC64MOVDconvert:
+		t := v.Type
+		if t.IsMemory() {
+			return
+		}
+		x := v.Args[0].Reg()
+		y := v.Reg()
+		if x != y {
+			rt := obj.TYPE_REG
+			op := ppc64.AMOVD
+
+			if t.IsFloat() {
+				op = ppc64.AFMOVD
+			}
+			p := gc.Prog(op)
+			p.From.Type = rt
+			p.From.Reg = x
+			p.To.Type = rt
+			p.To.Reg = y
+		}
+
+	case ssa.OpPPC64Xf2i64:
+		{
+			x := v.Args[0].Reg()
+			y := v.Reg()
+			p := gc.Prog(ppc64.AFMOVD)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = x
+			s.AddrScratch(&p.To)
+			p = gc.Prog(ppc64.AMOVD)
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = y
+			s.AddrScratch(&p.From)
+		}
+	case ssa.OpPPC64Xi2f64:
+		{
+			x := v.Args[0].Reg()
+			y := v.Reg()
+			p := gc.Prog(ppc64.AMOVD)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = x
+			s.AddrScratch(&p.To)
+			p = gc.Prog(ppc64.AFMOVD)
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = y
+			s.AddrScratch(&p.From)
+		}
+
+	case ssa.OpPPC64LoweredGetClosurePtr:
+		// Closure pointer is R11 (already)
+		gc.CheckLoweredGetClosurePtr(v)
+
+	case ssa.OpLoadReg:
+		loadOp := loadByType(v.Type)
+		p := gc.Prog(loadOp)
+		gc.AddrAuto(&p.From, v.Args[0])
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+	case ssa.OpStoreReg:
+		storeOp := storeByType(v.Type)
+		p := gc.Prog(storeOp)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddrAuto(&p.To, v)
+
+	case ssa.OpPPC64DIVD:
+		// For now,
+		//
+		// cmp arg1, -1
+		// be  ahead
+		// v = arg0 / arg1
+		// b over
+		// ahead: v = - arg0
+		// over: nop
+		r := v.Reg()
+		r0 := v.Args[0].Reg()
+		r1 := v.Args[1].Reg()
+
+		p := gc.Prog(ppc64.ACMP)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r1
+		p.To.Type = obj.TYPE_CONST
+		p.To.Offset = -1
+
+		pbahead := gc.Prog(ppc64.ABEQ)
+		pbahead.To.Type = obj.TYPE_BRANCH
+
+		p = gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r1
+		p.Reg = r0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+
+		pbover := gc.Prog(obj.AJMP)
+		pbover.To.Type = obj.TYPE_BRANCH
+
+		p = gc.Prog(ppc64.ANEG)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r0
+		gc.Patch(pbahead, p)
+
+		p = gc.Prog(obj.ANOP)
+		gc.Patch(pbover, p)
+
+	case ssa.OpPPC64DIVW:
+		// word-width version of above
+		r := v.Reg()
+		r0 := v.Args[0].Reg()
+		r1 := v.Args[1].Reg()
+
+		p := gc.Prog(ppc64.ACMPW)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r1
+		p.To.Type = obj.TYPE_CONST
+		p.To.Offset = -1
+
+		pbahead := gc.Prog(ppc64.ABEQ)
+		pbahead.To.Type = obj.TYPE_BRANCH
+
+		p = gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r1
+		p.Reg = r0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+
+		pbover := gc.Prog(obj.AJMP)
+		pbover.To.Type = obj.TYPE_BRANCH
+
+		p = gc.Prog(ppc64.ANEG)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r0
+		gc.Patch(pbahead, p)
+
+		p = gc.Prog(obj.ANOP)
+		gc.Patch(pbover, p)
+
+	case ssa.OpPPC64ADD, ssa.OpPPC64FADD, ssa.OpPPC64FADDS, ssa.OpPPC64SUB, ssa.OpPPC64FSUB, ssa.OpPPC64FSUBS,
+		ssa.OpPPC64MULLD, ssa.OpPPC64MULLW, ssa.OpPPC64DIVDU, ssa.OpPPC64DIVWU,
+		ssa.OpPPC64SRAD, ssa.OpPPC64SRAW, ssa.OpPPC64SRD, ssa.OpPPC64SRW, ssa.OpPPC64SLD, ssa.OpPPC64SLW,
+		ssa.OpPPC64MULHD, ssa.OpPPC64MULHW, ssa.OpPPC64MULHDU, ssa.OpPPC64MULHWU,
+		ssa.OpPPC64FMUL, ssa.OpPPC64FMULS, ssa.OpPPC64FDIV, ssa.OpPPC64FDIVS,
+		ssa.OpPPC64AND, ssa.OpPPC64OR, ssa.OpPPC64ANDN, ssa.OpPPC64ORN, ssa.OpPPC64XOR, ssa.OpPPC64EQV:
+		r := v.Reg()
+		r1 := v.Args[0].Reg()
+		r2 := v.Args[1].Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r2
+		p.Reg = r1
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+
+	case ssa.OpPPC64MaskIfNotCarry:
+		r := v.Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = ppc64.REGZERO
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+
+	case ssa.OpPPC64ADDconstForCarry:
+		r1 := v.Args[0].Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.Reg = r1
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = ppc64.REGTMP // Ignored; this is for the carry effect.
+
+	case ssa.OpPPC64NEG, ssa.OpPPC64FNEG, ssa.OpPPC64FSQRT, ssa.OpPPC64FSQRTS, ssa.OpPPC64FCTIDZ, ssa.OpPPC64FCTIWZ, ssa.OpPPC64FCFID, ssa.OpPPC64FRSP:
+		r := v.Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+
+	case ssa.OpPPC64ADDconst, ssa.OpPPC64ANDconst, ssa.OpPPC64ORconst, ssa.OpPPC64XORconst,
+		ssa.OpPPC64SRADconst, ssa.OpPPC64SRAWconst, ssa.OpPPC64SRDconst, ssa.OpPPC64SRWconst, ssa.OpPPC64SLDconst, ssa.OpPPC64SLWconst:
+		p := gc.Prog(v.Op.Asm())
+		p.Reg = v.Args[0].Reg()
+
+		if v.Aux != nil {
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = gc.AuxOffset(v)
+		} else {
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = v.AuxInt
+		}
+
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+	case ssa.OpPPC64ANDCCconst:
+		p := gc.Prog(v.Op.Asm())
+		p.Reg = v.Args[0].Reg()
+
+		if v.Aux != nil {
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = gc.AuxOffset(v)
+		} else {
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = v.AuxInt
+		}
+
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = ppc64.REGTMP // discard result
+
+	case ssa.OpPPC64MOVDaddr:
+		p := gc.Prog(ppc64.AMOVD)
+		p.From.Type = obj.TYPE_ADDR
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+		var wantreg string
+		// Suspect comment, copied from ARM code
+		// MOVD $sym+off(base), R
+		// the assembler expands it as the following:
+		// - base is SP: add constant offset to SP
+		//               when constant is large, tmp register (R11) may be used
+		// - base is SB: load external address from constant pool (use relocation)
+		switch v.Aux.(type) {
+		default:
+			v.Fatalf("aux is of unknown type %T", v.Aux)
+		case *ssa.ExternSymbol:
+			wantreg = "SB"
+			gc.AddAux(&p.From, v)
+		case *ssa.ArgSymbol, *ssa.AutoSymbol:
+			wantreg = "SP"
+			gc.AddAux(&p.From, v)
+		case nil:
+			// No sym, just MOVD $off(SP), R
+			wantreg = "SP"
+			p.From.Reg = ppc64.REGSP
+			p.From.Offset = v.AuxInt
+		}
+		if reg := v.Args[0].RegName(); reg != wantreg {
+			v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
+		}
+
+	case ssa.OpPPC64MOVDconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+	case ssa.OpPPC64FMOVDconst, ssa.OpPPC64FMOVSconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_FCONST
+		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+	case ssa.OpPPC64FCMPU, ssa.OpPPC64CMP, ssa.OpPPC64CMPW, ssa.OpPPC64CMPU, ssa.OpPPC64CMPWU:
+		p := gc.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.Args[1].Reg()
+
+	case ssa.OpPPC64CMPconst, ssa.OpPPC64CMPUconst, ssa.OpPPC64CMPWconst, ssa.OpPPC64CMPWUconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_CONST
+		p.To.Offset = v.AuxInt
+
+	case ssa.OpPPC64MOVBreg, ssa.OpPPC64MOVBZreg, ssa.OpPPC64MOVHreg, ssa.OpPPC64MOVHZreg, ssa.OpPPC64MOVWreg, ssa.OpPPC64MOVWZreg:
+		// Shift in register to required size
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Reg = v.Reg()
+		p.To.Type = obj.TYPE_REG
+
+	case ssa.OpPPC64MOVDload, ssa.OpPPC64MOVWload, ssa.OpPPC64MOVHload, ssa.OpPPC64MOVWZload, ssa.OpPPC64MOVBZload, ssa.OpPPC64MOVHZload:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+	case ssa.OpPPC64FMOVDload, ssa.OpPPC64FMOVSload:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+	case ssa.OpPPC64MOVDstorezero, ssa.OpPPC64MOVWstorezero, ssa.OpPPC64MOVHstorezero, ssa.OpPPC64MOVBstorezero:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = ppc64.REGZERO
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+
+	case ssa.OpPPC64MOVDstore, ssa.OpPPC64MOVWstore, ssa.OpPPC64MOVHstore, ssa.OpPPC64MOVBstore:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpPPC64FMOVDstore, ssa.OpPPC64FMOVSstore:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+
+	case ssa.OpPPC64Equal,
+		ssa.OpPPC64NotEqual,
+		ssa.OpPPC64LessThan,
+		ssa.OpPPC64FLessThan,
+		ssa.OpPPC64LessEqual,
+		ssa.OpPPC64GreaterThan,
+		ssa.OpPPC64FGreaterThan,
+		ssa.OpPPC64GreaterEqual:
+
+		// On Power7 or later, can use isel instruction:
+		// for a < b, a > b, a = b:
+		//   rtmp := 1
+		//   isel rt,rtmp,r0,cond // rt is target in ppc asm
+
+		// for  a >= b, a <= b, a != b:
+		//   rtmp := 1
+		//   isel rt,0,rtmp,!cond // rt is target in ppc asm
+
+		if v.Block.Func.Config.OldArch {
+			p := gc.Prog(ppc64.AMOVD)
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = 1
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = v.Reg()
+
+			pb := gc.Prog(condOps[v.Op])
+			pb.To.Type = obj.TYPE_BRANCH
+
+			p = gc.Prog(ppc64.AMOVD)
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = 0
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = v.Reg()
+
+			p = gc.Prog(obj.ANOP)
+			gc.Patch(pb, p)
+			break
+		}
+		// Modern PPC uses ISEL
+		p := gc.Prog(ppc64.AMOVD)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = 1
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = iselRegs[1]
+		iop := iselOps[v.Op]
+		ssaGenISEL(v, iop.cond, iselRegs[iop.valueIfCond], iselRegs[1-iop.valueIfCond])
+
+	case ssa.OpPPC64FLessEqual, // These include a second branch for EQ -- dealing with NaN prevents REL= to !REL conversion
+		ssa.OpPPC64FGreaterEqual:
+
+		if v.Block.Func.Config.OldArch {
+			p := gc.Prog(ppc64.AMOVW)
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = 1
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = v.Reg()
+
+			pb0 := gc.Prog(condOps[v.Op])
+			pb0.To.Type = obj.TYPE_BRANCH
+			pb1 := gc.Prog(ppc64.ABEQ)
+			pb1.To.Type = obj.TYPE_BRANCH
+
+			p = gc.Prog(ppc64.AMOVW)
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = 0
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = v.Reg()
+
+			p = gc.Prog(obj.ANOP)
+			gc.Patch(pb0, p)
+			gc.Patch(pb1, p)
+			break
+		}
+		// Modern PPC uses ISEL
+		p := gc.Prog(ppc64.AMOVD)
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = 1
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = iselRegs[1]
+		iop := iselOps[v.Op]
+		ssaGenISEL(v, iop.cond, iselRegs[iop.valueIfCond], iselRegs[1-iop.valueIfCond])
+		ssaGenISEL(v, ppc64.C_COND_EQ, iselRegs[1], v.Reg())
+
+	case ssa.OpPPC64LoweredZero:
+		// Similar to how this is done on ARM,
+		// except that PPC MOVDU x,off(y) is *(y+off) = x; y=y+off
+		// not store-and-increment.
+		// Therefore R3 should be dest-align
+		// and arg1 should be dest+size-align
+		// HOWEVER, the input dest address cannot be dest-align because
+		// that does not necessarily address valid memory and it's not
+		// known how that might be optimized.  Therefore, correct it in
+		// in the expansion:
+		//
+		// ADD    -8,R3,R3
+		// MOVDU  R0, 8(R3)
+		// CMP	  R3, Rarg1
+		// BL	  -2(PC)
+		// arg1 is the address of the last element to zero
+		// auxint is alignment
+		var sz int64
+		var movu obj.As
+		switch {
+		case v.AuxInt%8 == 0:
+			sz = 8
+			movu = ppc64.AMOVDU
+		case v.AuxInt%4 == 0:
+			sz = 4
+			movu = ppc64.AMOVWZU // MOVWU instruction not implemented
+		case v.AuxInt%2 == 0:
+			sz = 2
+			movu = ppc64.AMOVHU
+		default:
+			sz = 1
+			movu = ppc64.AMOVBU
+		}
+
+		p := gc.Prog(ppc64.AADD)
+		p.Reg = v.Args[0].Reg()
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = -sz
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Args[0].Reg()
+
+		p = gc.Prog(movu)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = ppc64.REG_R0
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		p.To.Offset = sz
+
+		p2 := gc.Prog(ppc64.ACMPU)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = v.Args[0].Reg()
+		p2.To.Reg = v.Args[1].Reg()
+		p2.To.Type = obj.TYPE_REG
+
+		p3 := gc.Prog(ppc64.ABLT)
+		p3.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p3, p)
+
+	case ssa.OpPPC64LoweredMove:
+		// Similar to how this is done on ARM,
+		// except that PPC MOVDU x,off(y) is *(y+off) = x; y=y+off,
+		// not store-and-increment.
+		// Inputs must be valid pointers to memory,
+		// so adjust arg0 and arg1 as part of the expansion.
+		// arg2 should be src+size-align,
+		//
+		// ADD    -8,R3,R3
+		// ADD    -8,R4,R4
+		// MOVDU	8(R4), Rtmp
+		// MOVDU 	Rtmp, 8(R3)
+		// CMP	R4, Rarg2
+		// BL	-3(PC)
+		// arg2 is the address of the last element of src
+		// auxint is alignment
+		var sz int64
+		var movu obj.As
+		switch {
+		case v.AuxInt%8 == 0:
+			sz = 8
+			movu = ppc64.AMOVDU
+		case v.AuxInt%4 == 0:
+			sz = 4
+			movu = ppc64.AMOVWZU // MOVWU instruction not implemented
+		case v.AuxInt%2 == 0:
+			sz = 2
+			movu = ppc64.AMOVHU
+		default:
+			sz = 1
+			movu = ppc64.AMOVBU
+		}
+
+		p := gc.Prog(ppc64.AADD)
+		p.Reg = v.Args[0].Reg()
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = -sz
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Args[0].Reg()
+
+		p = gc.Prog(ppc64.AADD)
+		p.Reg = v.Args[1].Reg()
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = -sz
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Args[1].Reg()
+
+		p = gc.Prog(movu)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[1].Reg()
+		p.From.Offset = sz
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = ppc64.REGTMP
+
+		p2 := gc.Prog(movu)
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = ppc64.REGTMP
+		p2.To.Type = obj.TYPE_MEM
+		p2.To.Reg = v.Args[0].Reg()
+		p2.To.Offset = sz
+
+		p3 := gc.Prog(ppc64.ACMPU)
+		p3.From.Reg = v.Args[1].Reg()
+		p3.From.Type = obj.TYPE_REG
+		p3.To.Reg = v.Args[2].Reg()
+		p3.To.Type = obj.TYPE_REG
+
+		p4 := gc.Prog(ppc64.ABLT)
+		p4.To.Type = obj.TYPE_BRANCH
+		gc.Patch(p4, p)
+
+	case ssa.OpPPC64CALLstatic:
+		if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
+			// Deferred calls will appear to be returning to
+			// the CALL deferreturn(SB) that we are about to emit.
+			// However, the stack trace code will show the line
+			// of the instruction byte before the return PC.
+			// To avoid that being an unrelated instruction,
+			// insert two actual hardware NOPs that will have the right line number.
+			// This is different from obj.ANOP, which is a virtual no-op
+			// that doesn't make it into the instruction stream.
+			// PPC64 is unusual because TWO nops are required
+			// (see gc/cgen.go, gc/plive.go -- copy of comment below)
+			//
+			// On ppc64, when compiling Go into position
+			// independent code on ppc64le we insert an
+			// instruction to reload the TOC pointer from the
+			// stack as well. See the long comment near
+			// jmpdefer in runtime/asm_ppc64.s for why.
+			// If the MOVD is not needed, insert a hardware NOP
+			// so that the same number of instructions are used
+			// on ppc64 in both shared and non-shared modes.
+			ginsnop()
+			if gc.Ctxt.Flag_shared {
+				p := gc.Prog(ppc64.AMOVD)
+				p.From.Type = obj.TYPE_MEM
+				p.From.Offset = 24
+				p.From.Reg = ppc64.REGSP
+				p.To.Type = obj.TYPE_REG
+				p.To.Reg = ppc64.REG_R2
+			} else {
+				ginsnop()
+			}
+		}
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym))
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+
+	case ssa.OpPPC64CALLclosure, ssa.OpPPC64CALLinter:
+		p := gc.Prog(ppc64.AMOVD)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = ppc64.REG_CTR
+
+		if gc.Ctxt.Flag_shared && p.From.Reg != ppc64.REG_R12 {
+			// Make sure function pointer is in R12 as well when
+			// compiling Go into PIC.
+			// TODO(mwhudson): it would obviously be better to
+			// change the register allocation to put the value in
+			// R12 already, but I don't know how to do that.
+			// TODO: We have the technology now to implement TODO above.
+			q := gc.Prog(ppc64.AMOVD)
+			q.From = p.From
+			q.To.Type = obj.TYPE_REG
+			q.To.Reg = ppc64.REG_R12
+		}
+
+		pp := gc.Prog(obj.ACALL)
+		pp.To.Type = obj.TYPE_REG
+		pp.To.Reg = ppc64.REG_CTR
+
+		if gc.Ctxt.Flag_shared {
+			// When compiling Go into PIC, the function we just
+			// called via pointer might have been implemented in
+			// a separate module and so overwritten the TOC
+			// pointer in R2; reload it.
+			q := gc.Prog(ppc64.AMOVD)
+			q.From.Type = obj.TYPE_MEM
+			q.From.Offset = 24
+			q.From.Reg = ppc64.REGSP
+			q.To.Type = obj.TYPE_REG
+			q.To.Reg = ppc64.REG_R2
+		}
+
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+
+	case ssa.OpPPC64CALLdefer:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Deferproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpPPC64CALLgo:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Newproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpVarDef:
+		gc.Gvardef(v.Aux.(*gc.Node))
+	case ssa.OpVarKill:
+		gc.Gvarkill(v.Aux.(*gc.Node))
+	case ssa.OpVarLive:
+		gc.Gvarlive(v.Aux.(*gc.Node))
+	case ssa.OpKeepAlive:
+		gc.KeepAlive(v)
+	case ssa.OpPhi:
+		gc.CheckLoweredPhi(v)
+
+	case ssa.OpPPC64LoweredNilCheck:
+		// Issue a load which will fault if arg is nil.
+		p := gc.Prog(ppc64.AMOVBZ)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = ppc64.REGTMP
+		if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
+			gc.Warnl(v.Line, "generated nil check")
+		}
+
+	case ssa.OpPPC64InvertFlags:
+		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
+	case ssa.OpPPC64FlagEQ, ssa.OpPPC64FlagLT, ssa.OpPPC64FlagGT:
+		v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
+
+	default:
+		v.Fatalf("genValue not implemented: %s", v.LongString())
+	}
+}
+
+var blockJump = [...]struct {
+	asm, invasm     obj.As
+	asmeq, invasmun bool
+}{
+	ssa.BlockPPC64EQ: {ppc64.ABEQ, ppc64.ABNE, false, false},
+	ssa.BlockPPC64NE: {ppc64.ABNE, ppc64.ABEQ, false, false},
+
+	ssa.BlockPPC64LT: {ppc64.ABLT, ppc64.ABGE, false, false},
+	ssa.BlockPPC64GE: {ppc64.ABGE, ppc64.ABLT, false, false},
+	ssa.BlockPPC64LE: {ppc64.ABLE, ppc64.ABGT, false, false},
+	ssa.BlockPPC64GT: {ppc64.ABGT, ppc64.ABLE, false, false},
+
+	// TODO: need to work FP comparisons into block jumps
+	ssa.BlockPPC64FLT: {ppc64.ABLT, ppc64.ABGE, false, false},
+	ssa.BlockPPC64FGE: {ppc64.ABGT, ppc64.ABLT, true, true}, // GE = GT or EQ; !GE = LT or UN
+	ssa.BlockPPC64FLE: {ppc64.ABLT, ppc64.ABGT, true, true}, // LE = LT or EQ; !LE = GT or UN
+	ssa.BlockPPC64FGT: {ppc64.ABGT, ppc64.ABLE, false, false},
+}
+
+func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+	s.SetLineno(b.Line)
+
+	switch b.Kind {
+
+	case ssa.BlockDefer:
+		// defer returns in R3:
+		// 0 if we should continue executing
+		// 1 if we should jump to deferreturn call
+		p := gc.Prog(ppc64.ACMP)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = ppc64.REG_R3
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = ppc64.REG_R0
+
+		p = gc.Prog(ppc64.ABNE)
+		p.To.Type = obj.TYPE_BRANCH
+		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+
+	case ssa.BlockPlain:
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+	case ssa.BlockExit:
+		gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
+	case ssa.BlockRet:
+		gc.Prog(obj.ARET)
+	case ssa.BlockRetJmp:
+		p := gc.Prog(obj.AJMP)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
+
+	case ssa.BlockPPC64EQ, ssa.BlockPPC64NE,
+		ssa.BlockPPC64LT, ssa.BlockPPC64GE,
+		ssa.BlockPPC64LE, ssa.BlockPPC64GT,
+		ssa.BlockPPC64FLT, ssa.BlockPPC64FGE,
+		ssa.BlockPPC64FLE, ssa.BlockPPC64FGT:
+		jmp := blockJump[b.Kind]
+		likely := b.Likely
+		var p *obj.Prog
+		switch next {
+		case b.Succs[0].Block():
+			p = gc.Prog(jmp.invasm)
+			likely *= -1
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+			if jmp.invasmun {
+				// TODO: The second branch is probably predict-not-taken since it is for FP unordered
+				q := gc.Prog(ppc64.ABVS)
+				q.To.Type = obj.TYPE_BRANCH
+				s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
+			}
+		case b.Succs[1].Block():
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+			if jmp.asmeq {
+				q := gc.Prog(ppc64.ABEQ)
+				q.To.Type = obj.TYPE_BRANCH
+				s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[0].Block()})
+			}
+		default:
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+			if jmp.asmeq {
+				q := gc.Prog(ppc64.ABEQ)
+				q.To.Type = obj.TYPE_BRANCH
+				s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[0].Block()})
+			}
+			q := gc.Prog(obj.AJMP)
+			q.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
+		}
+
+		// liblink reorders the instruction stream as it sees fit.
+		// Pass along what we know so liblink can make use of it.
+		// TODO: Once we've fully switched to SSA,
+		// make liblink leave our output alone.
+		//switch likely {
+		//case ssa.BranchUnlikely:
+		//	p.From.Type = obj.TYPE_CONST
+		//	p.From.Offset = 0
+		//case ssa.BranchLikely:
+		//	p.From.Type = obj.TYPE_CONST
+		//	p.From.Offset = 1
+		//}
+
+	default:
+		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
+	}
+}
diff --git a/src/cmd/compile/internal/s390x/cgen.go b/src/cmd/compile/internal/s390x/cgen.go
deleted file mode 100644
index 28bb34e..0000000
--- a/src/cmd/compile/internal/s390x/cgen.go
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package s390x
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/s390x"
-)
-
-type direction int
-
-const (
-	_FORWARDS direction = iota
-	_BACKWARDS
-)
-
-// blockcopy copies w bytes from &n to &res
-func blockcopy(n, res *gc.Node, osrc, odst, w int64) {
-	var dst gc.Node
-	var src gc.Node
-	if n.Ullman >= res.Ullman {
-		gc.Agenr(n, &dst, res) // temporarily use dst
-		gc.Regalloc(&src, gc.Types[gc.Tptr], nil)
-		gins(s390x.AMOVD, &dst, &src)
-		if res.Op == gc.ONAME {
-			gc.Gvardef(res)
-		}
-		gc.Agen(res, &dst)
-	} else {
-		if res.Op == gc.ONAME {
-			gc.Gvardef(res)
-		}
-		gc.Agenr(res, &dst, res)
-		gc.Agenr(n, &src, nil)
-	}
-	defer gc.Regfree(&src)
-	defer gc.Regfree(&dst)
-
-	var tmp gc.Node
-	gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil)
-	defer gc.Regfree(&tmp)
-
-	offset := int64(0)
-	dir := _FORWARDS
-	if osrc < odst && odst < osrc+w {
-		// Reverse. Can't use MVC, fall back onto basic moves.
-		dir = _BACKWARDS
-		const copiesPerIter = 2
-		if w >= 8*copiesPerIter {
-			cnt := w - (w % (8 * copiesPerIter))
-			ginscon(s390x.AADD, w, &src)
-			ginscon(s390x.AADD, w, &dst)
-
-			var end gc.Node
-			gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
-			p := gins(s390x.ASUB, nil, &end)
-			p.From.Type = obj.TYPE_CONST
-			p.From.Offset = cnt
-			p.Reg = src.Reg
-
-			var label *obj.Prog
-			for i := 0; i < copiesPerIter; i++ {
-				offset := int64(-8 * (i + 1))
-				p := gins(s390x.AMOVD, &src, &tmp)
-				p.From.Type = obj.TYPE_MEM
-				p.From.Offset = offset
-				if i == 0 {
-					label = p
-				}
-				p = gins(s390x.AMOVD, &tmp, &dst)
-				p.To.Type = obj.TYPE_MEM
-				p.To.Offset = offset
-			}
-
-			ginscon(s390x.ASUB, 8*copiesPerIter, &src)
-			ginscon(s390x.ASUB, 8*copiesPerIter, &dst)
-			gins(s390x.ACMP, &src, &end)
-			gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), label)
-			gc.Regfree(&end)
-
-			w -= cnt
-		} else {
-			offset = w
-		}
-	}
-
-	if dir == _FORWARDS && w > 1024 {
-		// Loop over MVCs
-		cnt := w - (w % 256)
-
-		var end gc.Node
-		gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
-		add := gins(s390x.AADD, nil, &end)
-		add.From.Type = obj.TYPE_CONST
-		add.From.Offset = cnt
-		add.Reg = src.Reg
-
-		mvc := gins(s390x.AMVC, &src, &dst)
-		mvc.From.Type = obj.TYPE_MEM
-		mvc.From.Offset = 0
-		mvc.To.Type = obj.TYPE_MEM
-		mvc.To.Offset = 0
-		mvc.From3 = new(obj.Addr)
-		mvc.From3.Type = obj.TYPE_CONST
-		mvc.From3.Offset = 256
-
-		ginscon(s390x.AADD, 256, &src)
-		ginscon(s390x.AADD, 256, &dst)
-		gins(s390x.ACMP, &src, &end)
-		gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), mvc)
-		gc.Regfree(&end)
-
-		w -= cnt
-	}
-
-	for w > 0 {
-		cnt := w
-		// If in reverse we can only do 8, 4, 2 or 1 bytes at a time.
-		if dir == _BACKWARDS {
-			switch {
-			case cnt >= 8:
-				cnt = 8
-			case cnt >= 4:
-				cnt = 4
-			case cnt >= 2:
-				cnt = 2
-			}
-		} else if cnt > 256 {
-			cnt = 256
-		}
-
-		switch cnt {
-		case 8, 4, 2, 1:
-			op := s390x.AMOVB
-			switch cnt {
-			case 8:
-				op = s390x.AMOVD
-			case 4:
-				op = s390x.AMOVW
-			case 2:
-				op = s390x.AMOVH
-			}
-			load := gins(op, &src, &tmp)
-			load.From.Type = obj.TYPE_MEM
-			load.From.Offset = offset
-
-			store := gins(op, &tmp, &dst)
-			store.To.Type = obj.TYPE_MEM
-			store.To.Offset = offset
-
-			if dir == _BACKWARDS {
-				load.From.Offset -= cnt
-				store.To.Offset -= cnt
-			}
-
-		default:
-			p := gins(s390x.AMVC, &src, &dst)
-			p.From.Type = obj.TYPE_MEM
-			p.From.Offset = offset
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = offset
-			p.From3 = new(obj.Addr)
-			p.From3.Type = obj.TYPE_CONST
-			p.From3.Offset = cnt
-		}
-
-		switch dir {
-		case _FORWARDS:
-			offset += cnt
-		case _BACKWARDS:
-			offset -= cnt
-		}
-		w -= cnt
-	}
-}
diff --git a/src/cmd/compile/internal/s390x/galign.go b/src/cmd/compile/internal/s390x/galign.go
index d0d621e..91b9ed0 100644
--- a/src/cmd/compile/internal/s390x/galign.go
+++ b/src/cmd/compile/internal/s390x/galign.go
@@ -9,58 +9,15 @@ import (
 	"cmd/internal/obj/s390x"
 )
 
-func betypeinit() {
-	gc.Widthptr = 8
-	gc.Widthint = 8
-	gc.Widthreg = 8
-}
-
-func Main() {
+func Init() {
 	gc.Thearch.LinkArch = &s390x.Links390x
 	gc.Thearch.REGSP = s390x.REGSP
-	gc.Thearch.REGCTXT = s390x.REGCTXT
-	gc.Thearch.REGCALLX = s390x.REG_R3
-	gc.Thearch.REGCALLX2 = s390x.REG_R4
-	gc.Thearch.REGRETURN = s390x.REG_R3
-	gc.Thearch.REGMIN = s390x.REG_R0
-	gc.Thearch.REGMAX = s390x.REG_R15
-	gc.Thearch.FREGMIN = s390x.REG_F0
-	gc.Thearch.FREGMAX = s390x.REG_F15
 	gc.Thearch.MAXWIDTH = 1 << 50
-	gc.Thearch.ReservedRegs = resvd
 
-	gc.Thearch.Betypeinit = betypeinit
-	gc.Thearch.Cgen_hmul = cgen_hmul
-	gc.Thearch.Cgen_shift = cgen_shift
-	gc.Thearch.Clearfat = clearfat
 	gc.Thearch.Defframe = defframe
-	gc.Thearch.Dodiv = dodiv
-	gc.Thearch.Excise = excise
-	gc.Thearch.Expandchecks = expandchecks
-	gc.Thearch.Getg = getg
-	gc.Thearch.Gins = gins
-	gc.Thearch.Ginscmp = ginscmp
-	gc.Thearch.Ginscon = ginscon
-	gc.Thearch.Ginsnop = ginsnop
-	gc.Thearch.Gmove = gmove
-	gc.Thearch.Peep = peep
 	gc.Thearch.Proginfo = proginfo
-	gc.Thearch.Regtyp = isReg
-	gc.Thearch.Sameaddr = sameaddr
-	gc.Thearch.Smallindir = smallindir
-	gc.Thearch.Stackaddr = stackaddr
-	gc.Thearch.Blockcopy = blockcopy
-	gc.Thearch.Sudoaddable = sudoaddable
-	gc.Thearch.Sudoclean = sudoclean
-	gc.Thearch.Excludedregs = excludedregs
-	gc.Thearch.RtoB = RtoB
-	gc.Thearch.FtoB = RtoB
-	gc.Thearch.BtoR = BtoR
-	gc.Thearch.BtoF = BtoF
-	gc.Thearch.Optoas = optoas
-	gc.Thearch.Doregbits = doregbits
-	gc.Thearch.Regnames = regnames
 
-	gc.Main()
-	gc.Exit(0)
+	gc.Thearch.SSAMarkMoves = ssaMarkMoves
+	gc.Thearch.SSAGenValue = ssaGenValue
+	gc.Thearch.SSAGenBlock = ssaGenBlock
 }
diff --git a/src/cmd/compile/internal/s390x/ggen.go b/src/cmd/compile/internal/s390x/ggen.go
index 39885ba..15c6554 100644
--- a/src/cmd/compile/internal/s390x/ggen.go
+++ b/src/cmd/compile/internal/s390x/ggen.go
@@ -8,7 +8,6 @@ import (
 	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/s390x"
-	"fmt"
 )
 
 // clearLoopCutOff is the (somewhat arbitrary) value above which it is better
@@ -42,7 +41,7 @@ func defframe(ptxt *obj.Prog) {
 			gc.Fatalf("needzero class %d", n.Class)
 		}
 		if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
-			gc.Fatalf("var %v has size %d offset %d", gc.Nconv(n, gc.FmtLong), int(n.Type.Width), int(n.Xoffset))
+			gc.Fatalf("var %L has size %d offset %d", n, int(n.Type.Width), int(n.Xoffset))
 		}
 
 		if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) {
@@ -81,7 +80,7 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
 	// need to create a copy of the stack pointer that we can adjust.
 	// We also need to do this if we are going to loop.
 	if offset < 0 || offset > 4096-clearLoopCutoff || cnt > clearLoopCutoff {
-		p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, offset, obj.TYPE_REG, s390x.REGRT1, 0)
+		p = gc.Appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, offset, obj.TYPE_REG, s390x.REGRT1, 0)
 		p.Reg = int16(s390x.REGSP)
 		reg = s390x.REGRT1
 		offset = 0
@@ -91,16 +90,16 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
 	if cnt > clearLoopCutoff {
 		n := cnt - (cnt % 256)
 		end := int16(s390x.REGRT2)
-		p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, offset+n, obj.TYPE_REG, end, 0)
+		p = gc.Appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, offset+n, obj.TYPE_REG, end, 0)
 		p.Reg = reg
-		p = appendpp(p, s390x.AXC, obj.TYPE_MEM, reg, offset, obj.TYPE_MEM, reg, offset)
+		p = gc.Appendpp(p, s390x.AXC, obj.TYPE_MEM, reg, offset, obj.TYPE_MEM, reg, offset)
 		p.From3 = new(obj.Addr)
 		p.From3.Type = obj.TYPE_CONST
 		p.From3.Offset = 256
 		pl := p
-		p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, 256, obj.TYPE_REG, reg, 0)
-		p = appendpp(p, s390x.ACMP, obj.TYPE_REG, reg, 0, obj.TYPE_REG, end, 0)
-		p = appendpp(p, s390x.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
+		p = gc.Appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, 256, obj.TYPE_REG, reg, 0)
+		p = gc.Appendpp(p, s390x.ACMP, obj.TYPE_REG, reg, 0, obj.TYPE_REG, end, 0)
+		p = gc.Appendpp(p, s390x.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
 		gc.Patch(p, pl)
 
 		cnt -= n
@@ -127,11 +126,11 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
 			case 2:
 				ins = s390x.AMOVH
 			}
-			p = appendpp(p, ins, obj.TYPE_CONST, 0, 0, obj.TYPE_MEM, reg, offset)
+			p = gc.Appendpp(p, ins, obj.TYPE_CONST, 0, 0, obj.TYPE_MEM, reg, offset)
 
 		// Handle clears that would require multiple move instructions with XC.
 		default:
-			p = appendpp(p, s390x.AXC, obj.TYPE_MEM, reg, offset, obj.TYPE_MEM, reg, offset)
+			p = gc.Appendpp(p, s390x.AXC, obj.TYPE_MEM, reg, offset, obj.TYPE_MEM, reg, offset)
 			p.From3 = new(obj.Addr)
 			p.From3.Type = obj.TYPE_CONST
 			p.From3.Offset = n
@@ -144,434 +143,10 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
 	return p
 }
 
-func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
-	q := gc.Ctxt.NewProg()
-	gc.Clearp(q)
-	q.As = as
-	q.Lineno = p.Lineno
-	q.From.Type = ftype
-	q.From.Reg = freg
-	q.From.Offset = foffset
-	q.To.Type = ttype
-	q.To.Reg = treg
-	q.To.Offset = toffset
-	q.Link = p.Link
-	p.Link = q
-	return q
-}
-
 func ginsnop() {
-	var reg gc.Node
-	gc.Nodreg(&reg, gc.Types[gc.TINT], s390x.REG_R0)
-	gins(s390x.AOR, &reg, &reg)
-}
-
-var panicdiv *gc.Node
-
-/*
- * generate division.
- * generates one of:
- *	res = nl / nr
- *	res = nl % nr
- * according to op.
- */
-func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	// Have to be careful about handling
-	// most negative int divided by -1 correctly.
-	// The hardware will generate undefined result.
-	// Also need to explicitly trap on division on zero,
-	// the hardware will silently generate undefined result.
-	// DIVW will leave unpredicable result in higher 32-bit,
-	// so always use DIVD/DIVDU.
-	t := nl.Type
-
-	t0 := t
-	check := 0
-	if t.IsSigned() {
-		check = 1
-		if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<<uint64(t.Width*8-1)) {
-			check = 0
-		} else if gc.Isconst(nr, gc.CTINT) && nr.Int64() != -1 {
-			check = 0
-		}
-	}
-
-	if t.Width < 8 {
-		if t.IsSigned() {
-			t = gc.Types[gc.TINT64]
-		} else {
-			t = gc.Types[gc.TUINT64]
-		}
-		check = 0
-	}
-
-	a := optoas(gc.ODIV, t)
-
-	var tl gc.Node
-	gc.Regalloc(&tl, t0, nil)
-	var tr gc.Node
-	gc.Regalloc(&tr, t0, nil)
-	if nl.Ullman >= nr.Ullman {
-		gc.Cgen(nl, &tl)
-		gc.Cgen(nr, &tr)
-	} else {
-		gc.Cgen(nr, &tr)
-		gc.Cgen(nl, &tl)
-	}
-
-	if t != t0 {
-		// Convert
-		tl2 := tl
-
-		tr2 := tr
-		tl.Type = t
-		tr.Type = t
-		gmove(&tl2, &tl)
-		gmove(&tr2, &tr)
-	}
-
-	// Handle divide-by-zero panic.
-	p1 := gins(optoas(gc.OCMP, t), &tr, nil)
-
-	p1.To.Type = obj.TYPE_REG
-	p1.To.Reg = s390x.REGZERO
-	p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1)
-	if panicdiv == nil {
-		panicdiv = gc.Sysfunc("panicdivide")
-	}
-	gc.Ginscall(panicdiv, -1)
-	gc.Patch(p1, gc.Pc)
-
-	var p2 *obj.Prog
-	if check != 0 {
-		var nm1 gc.Node
-		gc.Nodconst(&nm1, t, -1)
-		gins(optoas(gc.OCMP, t), &tr, &nm1)
-		p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1)
-		if op == gc.ODIV {
-			// a / (-1) is -a.
-			gins(optoas(gc.OMINUS, t), nil, &tl)
-
-			gmove(&tl, res)
-		} else {
-			// a % (-1) is 0.
-			var nz gc.Node
-			gc.Nodconst(&nz, t, 0)
-
-			gmove(&nz, res)
-		}
-
-		p2 = gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-	}
-
-	p1 = gins(a, &tr, &tl)
-	if op == gc.ODIV {
-		gc.Regfree(&tr)
-		gmove(&tl, res)
-	} else {
-		// A%B = A-(A/B*B)
-		var tm gc.Node
-		gc.Regalloc(&tm, t, nil)
-
-		// patch div to use the 3 register form
-		// TODO(minux): add gins3?
-		p1.Reg = p1.To.Reg
-
-		p1.To.Reg = tm.Reg
-		gins(optoas(gc.OMUL, t), &tr, &tm)
-		gc.Regfree(&tr)
-		gins(optoas(gc.OSUB, t), &tm, &tl)
-		gc.Regfree(&tm)
-		gmove(&tl, res)
-	}
-
-	gc.Regfree(&tl)
-	if check != 0 {
-		gc.Patch(p2, gc.Pc)
-	}
-}
-
-/*
- * generate high multiply:
- *   res = (nl*nr) >> width
- */
-func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	// largest ullman on left.
-	if nl.Ullman < nr.Ullman {
-		nl, nr = nr, nl
-	}
-
-	t := nl.Type
-	w := int(t.Width) * 8
-	var n1 gc.Node
-	gc.Cgenr(nl, &n1, res)
-	var n2 gc.Node
-	gc.Cgenr(nr, &n2, nil)
-	switch gc.Simtype[t.Etype] {
-	case gc.TINT8,
-		gc.TINT16,
-		gc.TINT32:
-		gins(optoas(gc.OMUL, t), &n2, &n1)
-		p := gins(s390x.ASRAD, nil, &n1)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = int64(w)
-
-	case gc.TUINT8,
-		gc.TUINT16,
-		gc.TUINT32:
-		gins(optoas(gc.OMUL, t), &n2, &n1)
-		p := gins(s390x.ASRD, nil, &n1)
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = int64(w)
-
-	case gc.TINT64:
-		gins(s390x.AMULHD, &n2, &n1)
-
-	case gc.TUINT64:
-		gins(s390x.AMULHDU, &n2, &n1)
-
-	default:
-		gc.Fatalf("cgen_hmul %v", t)
-	}
-
-	gc.Cgen(&n1, res)
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-/*
- * generate shift according to op, one of:
- *	res = nl << nr
- *	res = nl >> nr
- */
-func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	a := optoas(op, nl.Type)
-
-	if nr.Op == gc.OLITERAL {
-		var n1 gc.Node
-		gc.Regalloc(&n1, nl.Type, res)
-		gc.Cgen(nl, &n1)
-		sc := uint64(nr.Int64())
-		if sc >= uint64(nl.Type.Width*8) {
-			// large shift gets 2 shifts by width-1
-			var n3 gc.Node
-			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
-
-			gins(a, &n3, &n1)
-			gins(a, &n3, &n1)
-		} else {
-			gins(a, nr, &n1)
-		}
-		gmove(&n1, res)
-		gc.Regfree(&n1)
-		return
-	}
-
-	if nl.Ullman >= gc.UINF {
-		var n4 gc.Node
-		gc.Tempname(&n4, nl.Type)
-		gc.Cgen(nl, &n4)
-		nl = &n4
-	}
-
-	if nr.Ullman >= gc.UINF {
-		var n5 gc.Node
-		gc.Tempname(&n5, nr.Type)
-		gc.Cgen(nr, &n5)
-		nr = &n5
-	}
-
-	// Allow either uint32 or uint64 as shift type,
-	// to avoid unnecessary conversion from uint32 to uint64
-	// just to do the comparison.
-	tcount := gc.Types[gc.Simtype[nr.Type.Etype]]
-
-	if tcount.Etype < gc.TUINT32 {
-		tcount = gc.Types[gc.TUINT32]
-	}
-
-	var n1 gc.Node
-	gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX
-	var n3 gc.Node
-	gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX
-
-	var n2 gc.Node
-	gc.Regalloc(&n2, nl.Type, res)
-
-	if nl.Ullman >= nr.Ullman {
-		gc.Cgen(nl, &n2)
-		gc.Cgen(nr, &n1)
-		gmove(&n1, &n3)
-	} else {
-		gc.Cgen(nr, &n1)
-		gmove(&n1, &n3)
-		gc.Cgen(nl, &n2)
-	}
-
-	gc.Regfree(&n3)
-
-	// test and fix up large shifts
-	if !bounded {
-		gc.Nodconst(&n3, tcount, nl.Type.Width*8)
-		gins(optoas(gc.OCMP, tcount), &n1, &n3)
-		p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, 1)
-		if op == gc.ORSH && nl.Type.IsSigned() {
-			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
-			gins(a, &n3, &n2)
-		} else {
-			gc.Nodconst(&n3, nl.Type, 0)
-			gmove(&n3, &n2)
-		}
-
-		gc.Patch(p1, gc.Pc)
-	}
-
-	gins(a, &n1, &n2)
-
-	gmove(&n2, res)
-
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-// clearfat clears (i.e. replaces with zeros) the value pointed to by nl.
-func clearfat(nl *gc.Node) {
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width)
-	}
-
-	// Avoid taking the address for simple enough types.
-	if gc.Componentgen(nil, nl) {
-		return
-	}
-
-	var dst gc.Node
-	gc.Regalloc(&dst, gc.Types[gc.Tptr], nil)
-	gc.Agen(nl, &dst)
-
-	var boff int64
-	w := nl.Type.Width
-	if w > clearLoopCutoff {
-		// Generate a loop clearing 256 bytes per iteration using XCs.
-		var end gc.Node
-		gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
-		p := gins(s390x.AMOVD, &dst, &end)
-		p.From.Type = obj.TYPE_ADDR
-		p.From.Offset = w - (w % 256)
-
-		p = gins(s390x.AXC, &dst, &dst)
-		p.From.Type = obj.TYPE_MEM
-		p.From.Offset = 0
-		p.To.Type = obj.TYPE_MEM
-		p.To.Offset = 0
-		p.From3 = new(obj.Addr)
-		p.From3.Offset = 256
-		p.From3.Type = obj.TYPE_CONST
-		pl := p
-
-		ginscon(s390x.AADD, 256, &dst)
-		gins(s390x.ACMP, &dst, &end)
-		gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), pl)
-		gc.Regfree(&end)
-		w = w % 256
-	}
-
-	// Generate instructions to clear the remaining memory.
-	for w > 0 {
-		n := w
-
-		// Can clear at most 256 bytes per instruction.
-		if n > 256 {
-			n = 256
-		}
-
-		switch n {
-		// Handle very small clears using moves.
-		case 8, 4, 2, 1:
-			ins := s390x.AMOVB
-			switch n {
-			case 8:
-				ins = s390x.AMOVD
-			case 4:
-				ins = s390x.AMOVW
-			case 2:
-				ins = s390x.AMOVH
-			}
-			p := gins(ins, nil, &dst)
-			p.From.Type = obj.TYPE_CONST
-			p.From.Offset = 0
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = boff
-
-		// Handle clears that would require multiple moves with a XC.
-		default:
-			p := gins(s390x.AXC, &dst, &dst)
-			p.From.Type = obj.TYPE_MEM
-			p.From.Offset = boff
-			p.To.Type = obj.TYPE_MEM
-			p.To.Offset = boff
-			p.From3 = new(obj.Addr)
-			p.From3.Offset = n
-			p.From3.Type = obj.TYPE_CONST
-		}
-
-		boff += n
-		w -= n
-	}
-
-	gc.Regfree(&dst)
-}
-
-// Called after regopt and peep have run.
-// Expand CHECKNIL pseudo-op into actual nil pointer check.
-func expandchecks(firstp *obj.Prog) {
-	for p := firstp; p != nil; p = p.Link {
-		if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 {
-			fmt.Printf("expandchecks: %v\n", p)
-		}
-		if p.As != obj.ACHECKNIL {
-			continue
-		}
-		if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers
-			gc.Warnl(p.Lineno, "generated nil check")
-		}
-		if p.From.Type != obj.TYPE_REG {
-			gc.Fatalf("invalid nil check %v\n", p)
-		}
-
-		// check is
-		//	CMPBNE arg, $0, 2(PC) [likely]
-		//	MOVD   R0, 0(R0)
-		p1 := gc.Ctxt.NewProg()
-
-		gc.Clearp(p1)
-		p1.Link = p.Link
-		p.Link = p1
-		p1.Lineno = p.Lineno
-		p1.Pc = 9999
-		p.As = s390x.ACMPBNE
-		p.From3 = new(obj.Addr)
-		p.From3.Type = obj.TYPE_CONST
-		p.From3.Offset = 0
-
-		p.To.Type = obj.TYPE_BRANCH
-		p.To.Val = p1.Link
-
-		// crash by write to memory address 0.
-		p1.As = s390x.AMOVD
-
-		p1.From.Type = obj.TYPE_REG
-		p1.From.Reg = s390x.REGZERO
-		p1.To.Type = obj.TYPE_MEM
-		p1.To.Reg = s390x.REGZERO
-		p1.To.Offset = 0
-	}
-}
-
-// res = runtime.getg()
-func getg(res *gc.Node) {
-	var n1 gc.Node
-	gc.Nodreg(&n1, res.Type, s390x.REGG)
-	gmove(&n1, res)
+	p := gc.Prog(s390x.AOR)
+	p.From.Type = obj.TYPE_REG
+	p.From.Reg = int16(s390x.REG_R0)
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = int16(s390x.REG_R0)
 }
diff --git a/src/cmd/compile/internal/s390x/gsubr.go b/src/cmd/compile/internal/s390x/gsubr.go
deleted file mode 100644
index 7760812..0000000
--- a/src/cmd/compile/internal/s390x/gsubr.go
+++ /dev/null
@@ -1,1110 +0,0 @@
-// Derived from Inferno utils/6c/txt.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package s390x
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/s390x"
-	"fmt"
-)
-
-var resvd = []int{
-	s390x.REGZERO, // R0
-	s390x.REGTMP,  // R10
-	s390x.REGTMP2, // R11
-	s390x.REGCTXT, // R12
-	s390x.REGG,    // R13
-	s390x.REG_LR,  // R14
-	s390x.REGSP,   // R15
-}
-
-// generate
-//	as $c, n
-func ginscon(as obj.As, c int64, n2 *gc.Node) {
-	var n1 gc.Node
-
-	gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
-
-	if as != s390x.AMOVD && (c < -s390x.BIG || c > s390x.BIG) || n2.Op != gc.OREGISTER {
-		// cannot have more than 16-bit of immediate in ADD, etc.
-		// instead, MOV into register first.
-		var ntmp gc.Node
-		gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil)
-
-		rawgins(s390x.AMOVD, &n1, &ntmp)
-		rawgins(as, &ntmp, n2)
-		gc.Regfree(&ntmp)
-		return
-	}
-
-	rawgins(as, &n1, n2)
-}
-
-// generate
-//	as n, $c (CMP/CMPU)
-func ginscon2(as obj.As, n2 *gc.Node, c int64) {
-	var n1 gc.Node
-
-	gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
-
-	switch as {
-	default:
-		gc.Fatalf("ginscon2")
-
-	case s390x.ACMP:
-		if -s390x.BIG <= c && c <= s390x.BIG {
-			rawgins(as, n2, &n1)
-			return
-		}
-
-	case s390x.ACMPU:
-		if 0 <= c && c <= 2*s390x.BIG {
-			rawgins(as, n2, &n1)
-			return
-		}
-	}
-
-	// MOV n1 into register first
-	var ntmp gc.Node
-	gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil)
-
-	rawgins(s390x.AMOVD, &n1, &ntmp)
-	rawgins(as, n2, &ntmp)
-	gc.Regfree(&ntmp)
-}
-
-func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
-	if t.IsInteger() && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL {
-		// Reverse comparison to place constant last.
-		op = gc.Brrev(op)
-		n1, n2 = n2, n1
-	}
-
-	var r1, r2, g1, g2 gc.Node
-	gc.Regalloc(&r1, t, n1)
-	gc.Regalloc(&g1, n1.Type, &r1)
-	gc.Cgen(n1, &g1)
-	gmove(&g1, &r1)
-	if t.IsInteger() && gc.Isconst(n2, gc.CTINT) {
-		ginscon2(optoas(gc.OCMP, t), &r1, n2.Int64())
-	} else {
-		gc.Regalloc(&r2, t, n2)
-		gc.Regalloc(&g2, n1.Type, &r2)
-		gc.Cgen(n2, &g2)
-		gmove(&g2, &r2)
-		rawgins(optoas(gc.OCMP, t), &r1, &r2)
-		gc.Regfree(&g2)
-		gc.Regfree(&r2)
-	}
-	gc.Regfree(&g1)
-	gc.Regfree(&r1)
-	return gc.Gbranch(optoas(op, t), nil, likely)
-}
-
-// gmvc tries to move f to t using a mvc instruction.
-// If successful it returns true, otherwise it returns false.
-func gmvc(f, t *gc.Node) bool {
-	ft := int(gc.Simsimtype(f.Type))
-	tt := int(gc.Simsimtype(t.Type))
-
-	if ft != tt {
-		return false
-	}
-
-	if f.Op != gc.OINDREG || t.Op != gc.OINDREG {
-		return false
-	}
-
-	if f.Xoffset < 0 || f.Xoffset >= 4096-8 {
-		return false
-	}
-
-	if t.Xoffset < 0 || t.Xoffset >= 4096-8 {
-		return false
-	}
-
-	var len int64
-	switch ft {
-	case gc.TUINT8, gc.TINT8, gc.TBOOL:
-		len = 1
-	case gc.TUINT16, gc.TINT16:
-		len = 2
-	case gc.TUINT32, gc.TINT32, gc.TFLOAT32:
-		len = 4
-	case gc.TUINT64, gc.TINT64, gc.TFLOAT64, gc.TPTR64:
-		len = 8
-	case gc.TUNSAFEPTR:
-		len = int64(gc.Widthptr)
-	default:
-		return false
-	}
-
-	p := gc.Prog(s390x.AMVC)
-	gc.Naddr(&p.From, f)
-	gc.Naddr(&p.To, t)
-	p.From3 = new(obj.Addr)
-	p.From3.Offset = len
-	p.From3.Type = obj.TYPE_CONST
-	return true
-}
-
-// generate move:
-//	t = f
-// hard part is conversions.
-func gmove(f *gc.Node, t *gc.Node) {
-	if gc.Debug['M'] != 0 {
-		fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, gc.FmtLong), gc.Nconv(t, gc.FmtLong))
-	}
-
-	ft := int(gc.Simsimtype(f.Type))
-	tt := int(gc.Simsimtype(t.Type))
-	cvt := t.Type
-
-	if gc.Iscomplex[ft] || gc.Iscomplex[tt] {
-		gc.Complexmove(f, t)
-		return
-	}
-
-	var a obj.As
-
-	// cannot have two memory operands
-	if gc.Ismem(f) && gc.Ismem(t) {
-		if gmvc(f, t) {
-			return
-		}
-		goto hard
-	}
-
-	// convert constant to desired type
-	if f.Op == gc.OLITERAL {
-		var con gc.Node
-		f.Convconst(&con, t.Type)
-		f = &con
-		ft = tt // so big switch will choose a simple mov
-
-		// some constants can't move directly to memory.
-		if gc.Ismem(t) {
-			// float constants come from memory.
-			if t.Type.IsFloat() {
-				goto hard
-			}
-
-			// all immediates are 16-bit sign-extended
-			// unless moving into a register.
-			if t.Type.IsInteger() {
-				if i := con.Int64(); int64(int16(i)) != i {
-					goto hard
-				}
-			}
-
-			// immediate moves to memory have a 12-bit unsigned displacement
-			if t.Xoffset < 0 || t.Xoffset >= 4096-8 {
-				goto hard
-			}
-		}
-	}
-
-	// a float-to-int or int-to-float conversion requires the source operand in a register
-	if gc.Ismem(f) && ((f.Type.IsFloat() && t.Type.IsInteger()) || (f.Type.IsInteger() && t.Type.IsFloat())) {
-		cvt = f.Type
-		goto hard
-	}
-
-	// a float32-to-float64 or float64-to-float32 conversion requires the source operand in a register
-	if gc.Ismem(f) && f.Type.IsFloat() && t.Type.IsFloat() && (ft != tt) {
-		cvt = f.Type
-		goto hard
-	}
-
-	// value -> value copy, only one memory operand.
-	// figure out the instruction to use.
-	// break out of switch for one-instruction gins.
-	// goto rdst for "destination must be register".
-	// goto hard for "convert to cvt type first".
-	// otherwise handle and return.
-	switch uint32(ft)<<16 | uint32(tt) {
-	default:
-		gc.Fatalf("gmove %v -> %v", gc.Tconv(f.Type, gc.FmtLong), gc.Tconv(t.Type, gc.FmtLong))
-
-	// integer copy and truncate
-	case gc.TINT8<<16 | gc.TINT8,
-		gc.TUINT8<<16 | gc.TINT8,
-		gc.TINT16<<16 | gc.TINT8,
-		gc.TUINT16<<16 | gc.TINT8,
-		gc.TINT32<<16 | gc.TINT8,
-		gc.TUINT32<<16 | gc.TINT8,
-		gc.TINT64<<16 | gc.TINT8,
-		gc.TUINT64<<16 | gc.TINT8:
-		a = s390x.AMOVB
-
-	case gc.TINT8<<16 | gc.TUINT8,
-		gc.TUINT8<<16 | gc.TUINT8,
-		gc.TINT16<<16 | gc.TUINT8,
-		gc.TUINT16<<16 | gc.TUINT8,
-		gc.TINT32<<16 | gc.TUINT8,
-		gc.TUINT32<<16 | gc.TUINT8,
-		gc.TINT64<<16 | gc.TUINT8,
-		gc.TUINT64<<16 | gc.TUINT8:
-		a = s390x.AMOVBZ
-
-	case gc.TINT16<<16 | gc.TINT16,
-		gc.TUINT16<<16 | gc.TINT16,
-		gc.TINT32<<16 | gc.TINT16,
-		gc.TUINT32<<16 | gc.TINT16,
-		gc.TINT64<<16 | gc.TINT16,
-		gc.TUINT64<<16 | gc.TINT16:
-		a = s390x.AMOVH
-
-	case gc.TINT16<<16 | gc.TUINT16,
-		gc.TUINT16<<16 | gc.TUINT16,
-		gc.TINT32<<16 | gc.TUINT16,
-		gc.TUINT32<<16 | gc.TUINT16,
-		gc.TINT64<<16 | gc.TUINT16,
-		gc.TUINT64<<16 | gc.TUINT16:
-		a = s390x.AMOVHZ
-
-	case gc.TINT32<<16 | gc.TINT32,
-		gc.TUINT32<<16 | gc.TINT32,
-		gc.TINT64<<16 | gc.TINT32,
-		gc.TUINT64<<16 | gc.TINT32:
-		a = s390x.AMOVW
-
-	case gc.TINT32<<16 | gc.TUINT32,
-		gc.TUINT32<<16 | gc.TUINT32,
-		gc.TINT64<<16 | gc.TUINT32,
-		gc.TUINT64<<16 | gc.TUINT32:
-		a = s390x.AMOVWZ
-
-	case gc.TINT64<<16 | gc.TINT64,
-		gc.TINT64<<16 | gc.TUINT64,
-		gc.TUINT64<<16 | gc.TINT64,
-		gc.TUINT64<<16 | gc.TUINT64:
-		a = s390x.AMOVD
-
-	// sign extend int8
-	case gc.TINT8<<16 | gc.TINT16,
-		gc.TINT8<<16 | gc.TUINT16,
-		gc.TINT8<<16 | gc.TINT32,
-		gc.TINT8<<16 | gc.TUINT32,
-		gc.TINT8<<16 | gc.TINT64,
-		gc.TINT8<<16 | gc.TUINT64:
-		a = s390x.AMOVB
-		goto rdst
-
-	// sign extend uint8
-	case gc.TUINT8<<16 | gc.TINT16,
-		gc.TUINT8<<16 | gc.TUINT16,
-		gc.TUINT8<<16 | gc.TINT32,
-		gc.TUINT8<<16 | gc.TUINT32,
-		gc.TUINT8<<16 | gc.TINT64,
-		gc.TUINT8<<16 | gc.TUINT64:
-		a = s390x.AMOVBZ
-		goto rdst
-
-	// sign extend int16
-	case gc.TINT16<<16 | gc.TINT32,
-		gc.TINT16<<16 | gc.TUINT32,
-		gc.TINT16<<16 | gc.TINT64,
-		gc.TINT16<<16 | gc.TUINT64:
-		a = s390x.AMOVH
-		goto rdst
-
-	// zero extend uint16
-	case gc.TUINT16<<16 | gc.TINT32,
-		gc.TUINT16<<16 | gc.TUINT32,
-		gc.TUINT16<<16 | gc.TINT64,
-		gc.TUINT16<<16 | gc.TUINT64:
-		a = s390x.AMOVHZ
-		goto rdst
-
-	// sign extend int32
-	case gc.TINT32<<16 | gc.TINT64,
-		gc.TINT32<<16 | gc.TUINT64:
-		a = s390x.AMOVW
-		goto rdst
-
-	// zero extend uint32
-	case gc.TUINT32<<16 | gc.TINT64,
-		gc.TUINT32<<16 | gc.TUINT64:
-		a = s390x.AMOVWZ
-		goto rdst
-
-	// float to integer
-	case gc.TFLOAT32<<16 | gc.TUINT8,
-		gc.TFLOAT32<<16 | gc.TUINT16:
-		cvt = gc.Types[gc.TUINT32]
-		goto hard
-
-	case gc.TFLOAT32<<16 | gc.TUINT32:
-		a = s390x.ACLFEBR
-		goto rdst
-
-	case gc.TFLOAT32<<16 | gc.TUINT64:
-		a = s390x.ACLGEBR
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TUINT8,
-		gc.TFLOAT64<<16 | gc.TUINT16:
-		cvt = gc.Types[gc.TUINT32]
-		goto hard
-
-	case gc.TFLOAT64<<16 | gc.TUINT32:
-		a = s390x.ACLFDBR
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TUINT64:
-		a = s390x.ACLGDBR
-		goto rdst
-
-	case gc.TFLOAT32<<16 | gc.TINT8,
-		gc.TFLOAT32<<16 | gc.TINT16:
-		cvt = gc.Types[gc.TINT32]
-		goto hard
-
-	case gc.TFLOAT32<<16 | gc.TINT32:
-		a = s390x.ACFEBRA
-		goto rdst
-
-	case gc.TFLOAT32<<16 | gc.TINT64:
-		a = s390x.ACGEBRA
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TINT8,
-		gc.TFLOAT64<<16 | gc.TINT16:
-		cvt = gc.Types[gc.TINT32]
-		goto hard
-
-	case gc.TFLOAT64<<16 | gc.TINT32:
-		a = s390x.ACFDBRA
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TINT64:
-		a = s390x.ACGDBRA
-		goto rdst
-
-	// integer to float
-	case gc.TUINT8<<16 | gc.TFLOAT32,
-		gc.TUINT16<<16 | gc.TFLOAT32:
-		cvt = gc.Types[gc.TUINT32]
-		goto hard
-
-	case gc.TUINT32<<16 | gc.TFLOAT32:
-		a = s390x.ACELFBR
-		goto rdst
-
-	case gc.TUINT64<<16 | gc.TFLOAT32:
-		a = s390x.ACELGBR
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TFLOAT64,
-		gc.TUINT16<<16 | gc.TFLOAT64:
-		cvt = gc.Types[gc.TUINT32]
-		goto hard
-
-	case gc.TUINT32<<16 | gc.TFLOAT64:
-		a = s390x.ACDLFBR
-		goto rdst
-
-	case gc.TUINT64<<16 | gc.TFLOAT64:
-		a = s390x.ACDLGBR
-		goto rdst
-
-	case gc.TINT8<<16 | gc.TFLOAT32,
-		gc.TINT16<<16 | gc.TFLOAT32:
-		cvt = gc.Types[gc.TINT32]
-		goto hard
-
-	case gc.TINT32<<16 | gc.TFLOAT32:
-		a = s390x.ACEFBRA
-		goto rdst
-
-	case gc.TINT64<<16 | gc.TFLOAT32:
-		a = s390x.ACEGBRA
-		goto rdst
-
-	case gc.TINT8<<16 | gc.TFLOAT64,
-		gc.TINT16<<16 | gc.TFLOAT64:
-		cvt = gc.Types[gc.TINT32]
-		goto hard
-
-	case gc.TINT32<<16 | gc.TFLOAT64:
-		a = s390x.ACDFBRA
-		goto rdst
-
-	case gc.TINT64<<16 | gc.TFLOAT64:
-		a = s390x.ACDGBRA
-		goto rdst
-
-	// float to float
-	case gc.TFLOAT32<<16 | gc.TFLOAT32:
-		a = s390x.AFMOVS
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT64:
-		a = s390x.AFMOVD
-
-	case gc.TFLOAT32<<16 | gc.TFLOAT64:
-		a = s390x.ALDEBR
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT32:
-		a = s390x.ALEDBR
-		goto rdst
-	}
-
-	gins(a, f, t)
-	return
-
-	// requires register destination
-rdst:
-	if t != nil && t.Op == gc.OREGISTER {
-		gins(a, f, t)
-		return
-	} else {
-		var r1 gc.Node
-		gc.Regalloc(&r1, t.Type, t)
-
-		gins(a, f, &r1)
-		gmove(&r1, t)
-		gc.Regfree(&r1)
-		return
-	}
-
-	// requires register intermediate
-hard:
-	var r1 gc.Node
-	gc.Regalloc(&r1, cvt, t)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-}
-
-func intLiteral(n *gc.Node) (x int64, ok bool) {
-	switch {
-	case n == nil:
-		return
-	case gc.Isconst(n, gc.CTINT):
-		return n.Int64(), true
-	case gc.Isconst(n, gc.CTBOOL):
-		return int64(obj.Bool2int(n.Bool())), true
-	}
-	return
-}
-
-// gins is called by the front end.
-// It synthesizes some multiple-instruction sequences
-// so the front end can stay simpler.
-func gins(as obj.As, f, t *gc.Node) *obj.Prog {
-	if t != nil {
-		if as >= obj.A_ARCHSPECIFIC {
-			if x, ok := intLiteral(f); ok {
-				ginscon(as, x, t)
-				return nil // caller must not use
-			}
-		}
-		if as == s390x.ACMP || as == s390x.ACMPU {
-			if x, ok := intLiteral(t); ok {
-				ginscon2(as, f, x)
-				return nil // caller must not use
-			}
-		}
-	}
-	return rawgins(as, f, t)
-}
-
-// generate one instruction:
-//	as f, t
-func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
-	// self move check
-	// TODO(mundaym): use sized math and extend to MOVB, MOVWZ etc.
-	switch as {
-	case s390x.AMOVD, s390x.AFMOVS, s390x.AFMOVD:
-		if f != nil && t != nil &&
-			f.Op == gc.OREGISTER && t.Op == gc.OREGISTER &&
-			f.Reg == t.Reg {
-			return nil
-		}
-	}
-
-	p := gc.Prog(as)
-	gc.Naddr(&p.From, f)
-	gc.Naddr(&p.To, t)
-
-	switch as {
-	// Bad things the front end has done to us. Crash to find call stack.
-	case s390x.ACMP, s390x.ACMPU:
-		if p.From.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_MEM {
-			gc.Debug['h'] = 1
-			gc.Fatalf("bad inst: %v", p)
-		}
-	}
-
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("%v\n", p)
-	}
-
-	w := int32(0)
-	switch as {
-	case s390x.AMOVB, s390x.AMOVBZ:
-		w = 1
-
-	case s390x.AMOVH, s390x.AMOVHZ:
-		w = 2
-
-	case s390x.AMOVW, s390x.AMOVWZ:
-		w = 4
-
-	case s390x.AMOVD:
-		if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_ADDR {
-			break
-		}
-		w = 8
-	}
-
-	if w != 0 && ((f != nil && p.From.Width < int64(w)) || (t != nil && p.To.Type != obj.TYPE_REG && p.To.Width > int64(w))) {
-		gc.Dump("f", f)
-		gc.Dump("t", t)
-		gc.Fatalf("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width)
-	}
-
-	return p
-}
-
-// optoas returns the Axxx equivalent of Oxxx for type t
-func optoas(op gc.Op, t *gc.Type) obj.As {
-	if t == nil {
-		gc.Fatalf("optoas: t is nil")
-	}
-
-	// avoid constant conversions in switches below
-	const (
-		OMINUS_ = uint32(gc.OMINUS) << 16
-		OLSH_   = uint32(gc.OLSH) << 16
-		ORSH_   = uint32(gc.ORSH) << 16
-		OADD_   = uint32(gc.OADD) << 16
-		OSUB_   = uint32(gc.OSUB) << 16
-		OMUL_   = uint32(gc.OMUL) << 16
-		ODIV_   = uint32(gc.ODIV) << 16
-		OOR_    = uint32(gc.OOR) << 16
-		OAND_   = uint32(gc.OAND) << 16
-		OXOR_   = uint32(gc.OXOR) << 16
-		OEQ_    = uint32(gc.OEQ) << 16
-		ONE_    = uint32(gc.ONE) << 16
-		OLT_    = uint32(gc.OLT) << 16
-		OLE_    = uint32(gc.OLE) << 16
-		OGE_    = uint32(gc.OGE) << 16
-		OGT_    = uint32(gc.OGT) << 16
-		OCMP_   = uint32(gc.OCMP) << 16
-		OAS_    = uint32(gc.OAS) << 16
-		OHMUL_  = uint32(gc.OHMUL) << 16
-		OSQRT_  = uint32(gc.OSQRT) << 16
-		OLROT_  = uint32(gc.OLROT) << 16
-	)
-
-	a := obj.AXXX
-	switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
-	default:
-		gc.Fatalf("optoas: no entry for op=%v type=%v", op, t)
-
-	case OEQ_ | gc.TBOOL,
-		OEQ_ | gc.TINT8,
-		OEQ_ | gc.TUINT8,
-		OEQ_ | gc.TINT16,
-		OEQ_ | gc.TUINT16,
-		OEQ_ | gc.TINT32,
-		OEQ_ | gc.TUINT32,
-		OEQ_ | gc.TINT64,
-		OEQ_ | gc.TUINT64,
-		OEQ_ | gc.TPTR32,
-		OEQ_ | gc.TPTR64,
-		OEQ_ | gc.TFLOAT32,
-		OEQ_ | gc.TFLOAT64:
-		a = s390x.ABEQ
-
-	case ONE_ | gc.TBOOL,
-		ONE_ | gc.TINT8,
-		ONE_ | gc.TUINT8,
-		ONE_ | gc.TINT16,
-		ONE_ | gc.TUINT16,
-		ONE_ | gc.TINT32,
-		ONE_ | gc.TUINT32,
-		ONE_ | gc.TINT64,
-		ONE_ | gc.TUINT64,
-		ONE_ | gc.TPTR32,
-		ONE_ | gc.TPTR64,
-		ONE_ | gc.TFLOAT32,
-		ONE_ | gc.TFLOAT64:
-		a = s390x.ABNE
-
-	case OLT_ | gc.TINT8, // ACMP
-		OLT_ | gc.TINT16,
-		OLT_ | gc.TINT32,
-		OLT_ | gc.TINT64,
-		OLT_ | gc.TUINT8,
-		// ACMPU
-		OLT_ | gc.TUINT16,
-		OLT_ | gc.TUINT32,
-		OLT_ | gc.TUINT64,
-		OLT_ | gc.TFLOAT32,
-		// AFCMPU
-		OLT_ | gc.TFLOAT64:
-		a = s390x.ABLT
-
-	case OLE_ | gc.TINT8, // ACMP
-		OLE_ | gc.TINT16,
-		OLE_ | gc.TINT32,
-		OLE_ | gc.TINT64,
-		OLE_ | gc.TUINT8,
-		// ACMPU
-		OLE_ | gc.TUINT16,
-		OLE_ | gc.TUINT32,
-		OLE_ | gc.TUINT64,
-		OLE_ | gc.TFLOAT32,
-		OLE_ | gc.TFLOAT64:
-		a = s390x.ABLE
-
-	case OGT_ | gc.TINT8,
-		OGT_ | gc.TINT16,
-		OGT_ | gc.TINT32,
-		OGT_ | gc.TINT64,
-		OGT_ | gc.TUINT8,
-		OGT_ | gc.TUINT16,
-		OGT_ | gc.TUINT32,
-		OGT_ | gc.TUINT64,
-		OGT_ | gc.TFLOAT32,
-		OGT_ | gc.TFLOAT64:
-		a = s390x.ABGT
-
-	case OGE_ | gc.TINT8,
-		OGE_ | gc.TINT16,
-		OGE_ | gc.TINT32,
-		OGE_ | gc.TINT64,
-		OGE_ | gc.TUINT8,
-		OGE_ | gc.TUINT16,
-		OGE_ | gc.TUINT32,
-		OGE_ | gc.TUINT64,
-		OGE_ | gc.TFLOAT32,
-		OGE_ | gc.TFLOAT64:
-		a = s390x.ABGE
-
-	case OCMP_ | gc.TBOOL,
-		OCMP_ | gc.TINT8,
-		OCMP_ | gc.TINT16,
-		OCMP_ | gc.TINT32,
-		OCMP_ | gc.TPTR32,
-		OCMP_ | gc.TINT64:
-		a = s390x.ACMP
-
-	case OCMP_ | gc.TUINT8,
-		OCMP_ | gc.TUINT16,
-		OCMP_ | gc.TUINT32,
-		OCMP_ | gc.TUINT64,
-		OCMP_ | gc.TPTR64:
-		a = s390x.ACMPU
-
-	case OCMP_ | gc.TFLOAT32:
-		a = s390x.ACEBR
-
-	case OCMP_ | gc.TFLOAT64:
-		a = s390x.AFCMPU
-
-	case OAS_ | gc.TBOOL,
-		OAS_ | gc.TINT8:
-		a = s390x.AMOVB
-
-	case OAS_ | gc.TUINT8:
-		a = s390x.AMOVBZ
-
-	case OAS_ | gc.TINT16:
-		a = s390x.AMOVH
-
-	case OAS_ | gc.TUINT16:
-		a = s390x.AMOVHZ
-
-	case OAS_ | gc.TINT32:
-		a = s390x.AMOVW
-
-	case OAS_ | gc.TUINT32,
-		OAS_ | gc.TPTR32:
-		a = s390x.AMOVWZ
-
-	case OAS_ | gc.TINT64,
-		OAS_ | gc.TUINT64,
-		OAS_ | gc.TPTR64:
-		a = s390x.AMOVD
-
-	case OAS_ | gc.TFLOAT32:
-		a = s390x.AFMOVS
-
-	case OAS_ | gc.TFLOAT64:
-		a = s390x.AFMOVD
-
-	case OADD_ | gc.TINT8,
-		OADD_ | gc.TUINT8,
-		OADD_ | gc.TINT16,
-		OADD_ | gc.TUINT16,
-		OADD_ | gc.TINT32,
-		OADD_ | gc.TUINT32,
-		OADD_ | gc.TPTR32,
-		OADD_ | gc.TINT64,
-		OADD_ | gc.TUINT64,
-		OADD_ | gc.TPTR64:
-		a = s390x.AADD
-
-	case OADD_ | gc.TFLOAT32:
-		a = s390x.AFADDS
-
-	case OADD_ | gc.TFLOAT64:
-		a = s390x.AFADD
-
-	case OSUB_ | gc.TINT8,
-		OSUB_ | gc.TUINT8,
-		OSUB_ | gc.TINT16,
-		OSUB_ | gc.TUINT16,
-		OSUB_ | gc.TINT32,
-		OSUB_ | gc.TUINT32,
-		OSUB_ | gc.TPTR32,
-		OSUB_ | gc.TINT64,
-		OSUB_ | gc.TUINT64,
-		OSUB_ | gc.TPTR64:
-		a = s390x.ASUB
-
-	case OSUB_ | gc.TFLOAT32:
-		a = s390x.AFSUBS
-
-	case OSUB_ | gc.TFLOAT64:
-		a = s390x.AFSUB
-
-	case OMINUS_ | gc.TINT8,
-		OMINUS_ | gc.TUINT8,
-		OMINUS_ | gc.TINT16,
-		OMINUS_ | gc.TUINT16,
-		OMINUS_ | gc.TINT32,
-		OMINUS_ | gc.TUINT32,
-		OMINUS_ | gc.TPTR32,
-		OMINUS_ | gc.TINT64,
-		OMINUS_ | gc.TUINT64,
-		OMINUS_ | gc.TPTR64:
-		a = s390x.ANEG
-
-	case OAND_ | gc.TINT8,
-		OAND_ | gc.TUINT8,
-		OAND_ | gc.TINT16,
-		OAND_ | gc.TUINT16,
-		OAND_ | gc.TINT32,
-		OAND_ | gc.TUINT32,
-		OAND_ | gc.TPTR32,
-		OAND_ | gc.TINT64,
-		OAND_ | gc.TUINT64,
-		OAND_ | gc.TPTR64:
-		a = s390x.AAND
-
-	case OOR_ | gc.TINT8,
-		OOR_ | gc.TUINT8,
-		OOR_ | gc.TINT16,
-		OOR_ | gc.TUINT16,
-		OOR_ | gc.TINT32,
-		OOR_ | gc.TUINT32,
-		OOR_ | gc.TPTR32,
-		OOR_ | gc.TINT64,
-		OOR_ | gc.TUINT64,
-		OOR_ | gc.TPTR64:
-		a = s390x.AOR
-
-	case OXOR_ | gc.TINT8,
-		OXOR_ | gc.TUINT8,
-		OXOR_ | gc.TINT16,
-		OXOR_ | gc.TUINT16,
-		OXOR_ | gc.TINT32,
-		OXOR_ | gc.TUINT32,
-		OXOR_ | gc.TPTR32,
-		OXOR_ | gc.TINT64,
-		OXOR_ | gc.TUINT64,
-		OXOR_ | gc.TPTR64:
-		a = s390x.AXOR
-
-	case OLSH_ | gc.TINT8,
-		OLSH_ | gc.TUINT8,
-		OLSH_ | gc.TINT16,
-		OLSH_ | gc.TUINT16,
-		OLSH_ | gc.TINT32,
-		OLSH_ | gc.TUINT32,
-		OLSH_ | gc.TPTR32,
-		OLSH_ | gc.TINT64,
-		OLSH_ | gc.TUINT64,
-		OLSH_ | gc.TPTR64:
-		a = s390x.ASLD
-
-	case ORSH_ | gc.TUINT8,
-		ORSH_ | gc.TUINT16,
-		ORSH_ | gc.TUINT32,
-		ORSH_ | gc.TPTR32,
-		ORSH_ | gc.TUINT64,
-		ORSH_ | gc.TPTR64:
-		a = s390x.ASRD
-
-	case ORSH_ | gc.TINT8,
-		ORSH_ | gc.TINT16,
-		ORSH_ | gc.TINT32,
-		ORSH_ | gc.TINT64:
-		a = s390x.ASRAD
-
-	case OHMUL_ | gc.TINT64:
-		a = s390x.AMULHD
-
-	case OHMUL_ | gc.TUINT64,
-		OHMUL_ | gc.TPTR64:
-		a = s390x.AMULHDU
-
-	case OMUL_ | gc.TINT8,
-		OMUL_ | gc.TINT16,
-		OMUL_ | gc.TINT32,
-		OMUL_ | gc.TINT64:
-		a = s390x.AMULLD
-
-	case OMUL_ | gc.TUINT8,
-		OMUL_ | gc.TUINT16,
-		OMUL_ | gc.TUINT32,
-		OMUL_ | gc.TPTR32,
-		// don't use word multiply, the high 32-bit are undefined.
-		OMUL_ | gc.TUINT64,
-		OMUL_ | gc.TPTR64:
-		// for 64-bit multiplies, signedness doesn't matter.
-		a = s390x.AMULLD
-
-	case OMUL_ | gc.TFLOAT32:
-		a = s390x.AFMULS
-
-	case OMUL_ | gc.TFLOAT64:
-		a = s390x.AFMUL
-
-	case ODIV_ | gc.TINT8,
-		ODIV_ | gc.TINT16,
-		ODIV_ | gc.TINT32,
-		ODIV_ | gc.TINT64:
-		a = s390x.ADIVD
-
-	case ODIV_ | gc.TUINT8,
-		ODIV_ | gc.TUINT16,
-		ODIV_ | gc.TUINT32,
-		ODIV_ | gc.TPTR32,
-		ODIV_ | gc.TUINT64,
-		ODIV_ | gc.TPTR64:
-		a = s390x.ADIVDU
-
-	case ODIV_ | gc.TFLOAT32:
-		a = s390x.AFDIVS
-
-	case ODIV_ | gc.TFLOAT64:
-		a = s390x.AFDIV
-
-	case OSQRT_ | gc.TFLOAT64:
-		a = s390x.AFSQRT
-
-	case OLROT_ | gc.TUINT32,
-		OLROT_ | gc.TPTR32,
-		OLROT_ | gc.TINT32:
-		a = s390x.ARLL
-
-	case OLROT_ | gc.TUINT64,
-		OLROT_ | gc.TPTR64,
-		OLROT_ | gc.TINT64:
-		a = s390x.ARLLG
-	}
-
-	return a
-}
-
-const (
-	ODynam   = 1 << 0
-	OAddable = 1 << 1
-)
-
-var clean [20]gc.Node
-
-var cleani int = 0
-
-func sudoclean() {
-	if clean[cleani-1].Op != gc.OEMPTY {
-		gc.Regfree(&clean[cleani-1])
-	}
-	if clean[cleani-2].Op != gc.OEMPTY {
-		gc.Regfree(&clean[cleani-2])
-	}
-	cleani -= 2
-}
-
-/*
- * generate code to compute address of n,
- * a reference to a (perhaps nested) field inside
- * an array or struct.
- * return 0 on failure, 1 on success.
- * on success, leaves usable address in a.
- *
- * caller is responsible for calling sudoclean
- * after successful sudoaddable,
- * to release the register used for a.
- */
-func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool {
-	if n.Type == nil {
-		return false
-	}
-
-	*a = obj.Addr{}
-
-	switch n.Op {
-	case gc.OLITERAL:
-		if !gc.Isconst(n, gc.CTINT) {
-			return false
-		}
-		v := n.Int64()
-		switch as {
-		default:
-			return false
-
-		// operations that can cope with a 32-bit immediate
-		// TODO(mundaym): logical operations can work on high bits
-		case s390x.AADD,
-			s390x.AADDC,
-			s390x.ASUB,
-			s390x.AMULLW,
-			s390x.AAND,
-			s390x.AOR,
-			s390x.AXOR,
-			s390x.ASLD,
-			s390x.ASLW,
-			s390x.ASRAW,
-			s390x.ASRAD,
-			s390x.ASRW,
-			s390x.ASRD,
-			s390x.AMOVB,
-			s390x.AMOVBZ,
-			s390x.AMOVH,
-			s390x.AMOVHZ,
-			s390x.AMOVW,
-			s390x.AMOVWZ,
-			s390x.AMOVD:
-			if int64(int32(v)) != v {
-				return false
-			}
-
-		// for comparisons avoid immediates unless they can
-		// fit into a int8/uint8
-		// this favours combined compare and branch instructions
-		case s390x.ACMP:
-			if int64(int8(v)) != v {
-				return false
-			}
-		case s390x.ACMPU:
-			if int64(uint8(v)) != v {
-				return false
-			}
-		}
-
-		cleani += 2
-		reg := &clean[cleani-1]
-		reg1 := &clean[cleani-2]
-		reg.Op = gc.OEMPTY
-		reg1.Op = gc.OEMPTY
-		gc.Naddr(a, n)
-		return true
-
-	case gc.ODOT,
-		gc.ODOTPTR:
-		cleani += 2
-		reg := &clean[cleani-1]
-		reg1 := &clean[cleani-2]
-		reg.Op = gc.OEMPTY
-		reg1.Op = gc.OEMPTY
-		var nn *gc.Node
-		var oary [10]int64
-		o := gc.Dotoffset(n, oary[:], &nn)
-		if nn == nil {
-			sudoclean()
-			return false
-		}
-
-		if nn.Addable && o == 1 && oary[0] >= 0 {
-			// directly addressable set of DOTs
-			n1 := *nn
-
-			n1.Type = n.Type
-			n1.Xoffset += oary[0]
-			// check that the offset fits into a 12-bit displacement
-			if n1.Xoffset < 0 || n1.Xoffset >= (1<<12)-8 {
-				sudoclean()
-				return false
-			}
-			gc.Naddr(a, &n1)
-			return true
-		}
-
-		gc.Regalloc(reg, gc.Types[gc.Tptr], nil)
-		n1 := *reg
-		n1.Op = gc.OINDREG
-		if oary[0] >= 0 {
-			gc.Agen(nn, reg)
-			n1.Xoffset = oary[0]
-		} else {
-			gc.Cgen(nn, reg)
-			gc.Cgen_checknil(reg)
-			n1.Xoffset = -(oary[0] + 1)
-		}
-
-		for i := 1; i < o; i++ {
-			if oary[i] >= 0 {
-				gc.Fatalf("can't happen")
-			}
-			gins(s390x.AMOVD, &n1, reg)
-			gc.Cgen_checknil(reg)
-			n1.Xoffset = -(oary[i] + 1)
-		}
-
-		a.Type = obj.TYPE_NONE
-		a.Index = 0
-		// check that the offset fits into a 12-bit displacement
-		if n1.Xoffset < 0 || n1.Xoffset >= (1<<12)-8 {
-			tmp := n1
-			tmp.Op = gc.OREGISTER
-			tmp.Type = gc.Types[gc.Tptr]
-			tmp.Xoffset = 0
-			gc.Cgen_checknil(&tmp)
-			ginscon(s390x.AADD, n1.Xoffset, &tmp)
-			n1.Xoffset = 0
-		}
-		gc.Naddr(a, &n1)
-		return true
-	}
-
-	return false
-}
diff --git a/src/cmd/compile/internal/s390x/peep.go b/src/cmd/compile/internal/s390x/peep.go
deleted file mode 100644
index cd6a8c5..0000000
--- a/src/cmd/compile/internal/s390x/peep.go
+++ /dev/null
@@ -1,1664 +0,0 @@
-// Derived from Inferno utils/6c/peep.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package s390x
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/s390x"
-	"fmt"
-)
-
-type usage int
-
-const (
-	_None          usage = iota // no usage found
-	_Read                       // only read from
-	_ReadWriteSame              // both read from and written to in a single operand
-	_Write                      // only written to
-	_ReadWriteDiff              // both read from and written to in different operands
-)
-
-var gactive uint32
-
-func peep(firstp *obj.Prog) {
-	g := gc.Flowstart(firstp, nil)
-	if g == nil {
-		return
-	}
-	gactive = 0
-
-	run := func(name string, pass func(r *gc.Flow) int) int {
-		n := pass(g.Start)
-		if gc.Debug['P'] != 0 {
-			fmt.Println(name, ":", n)
-		}
-		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-			gc.Dumpit(name, g.Start, 0)
-		}
-		return n
-	}
-
-	for {
-		n := 0
-		n += run("constant propagation", constantPropagation)
-		n += run("copy propagation", copyPropagation)
-		n += run("cast propagation", castPropagation)
-		n += run("remove load-hit-stores", removeLoadHitStores)
-		n += run("dead code elimination", deadCodeElimination)
-		if n == 0 {
-			break
-		}
-	}
-	run("fuse op moves", fuseOpMoves)
-	run("fuse clears", fuseClear)
-	run("load pipelining", loadPipelining)
-	run("fuse compare branch", fuseCompareBranch)
-	run("simplify ops", simplifyOps)
-	run("dead code elimination", deadCodeElimination)
-
-	// TODO(mundaym): load/store multiple aren't currently handled by copyu
-	// so this pass must be last.
-	run("fuse multiple", fuseMultiple)
-
-	gc.Flowend(g)
-}
-
-func pushback(r0 *gc.Flow) {
-	var r *gc.Flow
-
-	var b *gc.Flow
-	p0 := r0.Prog
-	for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) {
-		p := r.Prog
-		if p.As != obj.ANOP {
-			if !(isReg(&p.From) || isConst(&p.From)) || !isReg(&p.To) {
-				break
-			}
-			if copyu(p, &p0.To, nil) != _None || copyu(p0, &p.To, nil) != _None {
-				break
-			}
-		}
-
-		if p.As == obj.ACALL {
-			break
-		}
-		b = r
-	}
-
-	if b == nil {
-		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-			fmt.Printf("no pushback: %v\n", r0.Prog)
-			if r != nil {
-				fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil)
-			}
-		}
-
-		return
-	}
-
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("pushback\n")
-		for r := b; ; r = r.Link {
-			fmt.Printf("\t%v\n", r.Prog)
-			if r == r0 {
-				break
-			}
-		}
-	}
-
-	t := *r0.Prog
-	for r = gc.Uniqp(r0); ; r = gc.Uniqp(r) {
-		p0 = r.Link.Prog
-		p := r.Prog
-		p0.As = p.As
-		p0.Lineno = p.Lineno
-		p0.From = p.From
-		p0.To = p.To
-		p0.From3 = p.From3
-		p0.Reg = p.Reg
-		p0.RegTo2 = p.RegTo2
-		if r == b {
-			break
-		}
-	}
-
-	p0 = r.Prog
-	p0.As = t.As
-	p0.Lineno = t.Lineno
-	p0.From = t.From
-	p0.To = t.To
-	p0.From3 = t.From3
-	p0.Reg = t.Reg
-	p0.RegTo2 = t.RegTo2
-
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("\tafter\n")
-		for r := b; ; r = r.Link {
-			fmt.Printf("\t%v\n", r.Prog)
-			if r == r0 {
-				break
-			}
-		}
-	}
-}
-
-// excise replaces the given instruction with a NOP and clears
-// its operands.
-func excise(r *gc.Flow) {
-	p := r.Prog
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("%v ===delete===\n", p)
-	}
-	obj.Nopout(p)
-	gc.Ostats.Ndelmov++
-}
-
-// isZero returns true if a is either the constant 0 or the register
-// REGZERO.
-func isZero(a *obj.Addr) bool {
-	if a.Type == obj.TYPE_CONST && a.Offset == 0 {
-		return true
-	}
-	if a.Type == obj.TYPE_REG && a.Reg == s390x.REGZERO {
-		return true
-	}
-	return false
-}
-
-// isReg returns true if a is a general purpose or floating point
-// register (GPR or FPR).
-//
-// TODO(mundaym): currently this excludes REGZER0, but not other
-// special registers.
-func isReg(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_REG &&
-		s390x.REG_R0 <= a.Reg &&
-		a.Reg <= s390x.REG_F15 &&
-		a.Reg != s390x.REGZERO
-}
-
-// isGPR returns true if a is a general purpose register (GPR).
-// REGZERO is treated as a GPR.
-func isGPR(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_REG &&
-		s390x.REG_R0 <= a.Reg &&
-		a.Reg <= s390x.REG_R15
-}
-
-// isFPR returns true if a is a floating point register (FPR).
-func isFPR(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_REG &&
-		s390x.REG_F0 <= a.Reg &&
-		a.Reg <= s390x.REG_F15
-}
-
-// isConst returns true if a refers to a constant (integer or
-// floating point, not string currently).
-func isConst(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_FCONST
-}
-
-// isBDMem returns true if a refers to a memory location addressable by a
-// base register (B) and a displacement (D), such as:
-// 	x+8(R1)
-// and
-//	0(R10)
-// It returns false if the address contains an index register (X) such as:
-// 	16(R1)(R2*1)
-// or if a relocation is required.
-func isBDMem(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_MEM &&
-		a.Index == 0 &&
-		(a.Name == obj.NAME_NONE || a.Name == obj.NAME_AUTO || a.Name == obj.NAME_PARAM)
-}
-
-// the idea is to substitute
-// one register for another
-// from one MOV to another
-//	MOV	a, R1
-//	ADD	b, R1	/ no use of R2
-//	MOV	R1, R2
-// would be converted to
-//	MOV	a, R2
-//	ADD	b, R2
-//	MOV	R2, R1
-// hopefully, then the former or latter MOV
-// will be eliminated by copy propagation.
-//
-// r0 (the argument, not the register) is the MOV at the end of the
-// above sequences. subprop returns true if it modified any instructions.
-func subprop(r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	if !isReg(v1) {
-		return false
-	}
-	v2 := &p.To
-	if !isReg(v2) {
-		return false
-	}
-	cast := false
-	switch p.As {
-	case s390x.AMOVW, s390x.AMOVWZ,
-		s390x.AMOVH, s390x.AMOVHZ,
-		s390x.AMOVB, s390x.AMOVBZ:
-		cast = true
-	}
-	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
-		if gc.Uniqs(r) == nil {
-			break
-		}
-		p = r.Prog
-		switch copyu(p, v1, nil) {
-		case _Write, _ReadWriteDiff:
-			if p.As == obj.ACALL {
-				return false
-			}
-			if (!cast || p.As == r0.Prog.As) && p.To.Type == v1.Type && p.To.Reg == v1.Reg {
-				copysub(&p.To, v1, v2)
-				for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
-					p = r.Prog
-					copysub(&p.From, v1, v2)
-					copysub1(p, v1, v2)
-					copysub(&p.To, v1, v2)
-				}
-				v1.Reg, v2.Reg = v2.Reg, v1.Reg
-				return true
-			}
-			if cast {
-				return false
-			}
-		case _ReadWriteSame:
-			if cast {
-				return false
-			}
-		}
-		if copyu(p, v2, nil) != _None {
-			return false
-		}
-	}
-	return false
-}
-
-// The idea is to remove redundant copies.
-//     v1->v2  F=0
-//     (use v2 s/v2/v1/)*
-//     set v1  F=1
-//     use v2  return fail (v1->v2 move must remain)
-//     -----------------
-//     v1->v2  F=0
-//     (use v2 s/v2/v1/)*
-//     set v1  F=1
-//     set v2  return success (caller can remove v1->v2 move)
-func copyprop(r *gc.Flow) bool {
-	p := r.Prog
-
-	canSub := false
-	switch p.As {
-	case s390x.AFMOVS, s390x.AFMOVD, s390x.AMOVD:
-		canSub = true
-	default:
-		for rr := gc.Uniqp(r); rr != nil; rr = gc.Uniqp(rr) {
-			if gc.Uniqs(rr) == nil {
-				break
-			}
-			switch copyu(rr.Prog, &p.From, nil) {
-			case _Read, _None:
-				continue
-			}
-			// write
-			if rr.Prog.As == p.As {
-				canSub = true
-			}
-			break
-		}
-	}
-	if !canSub {
-		return false
-	}
-	if copyas(&p.From, &p.To) {
-		return true
-	}
-
-	gactive++
-	return copy1(&p.From, &p.To, r.S1, 0)
-}
-
-// copy1 replaces uses of v2 with v1 starting at r and returns true if
-// all uses were rewritten.
-func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool {
-	if uint32(r.Active) == gactive {
-		return true
-	}
-	r.Active = int32(gactive)
-	for ; r != nil; r = r.S1 {
-		p := r.Prog
-		if f == 0 && gc.Uniqp(r) == nil {
-			// Multiple predecessors; conservatively
-			// assume v1 was set on other path
-			f = 1
-		}
-		t := copyu(p, v2, nil)
-		switch t {
-		case _ReadWriteSame:
-			return false
-		case _Write:
-			return true
-		case _Read, _ReadWriteDiff:
-			if f != 0 {
-				return false
-			}
-			if copyu(p, v2, v1) != 0 {
-				return false
-			}
-			if t == _ReadWriteDiff {
-				return true
-			}
-		}
-		if f == 0 {
-			switch copyu(p, v1, nil) {
-			case _ReadWriteSame, _ReadWriteDiff, _Write:
-				f = 1
-			}
-		}
-		if r.S2 != nil {
-			if !copy1(v1, v2, r.S2, f) {
-				return false
-			}
-		}
-	}
-	return true
-}
-
-// If s==nil, copyu returns the set/use of v in p; otherwise, it
-// modifies p to replace reads of v with reads of s and returns 0 for
-// success or non-zero for failure.
-//
-// If s==nil, copy returns one of the following values:
-// 	_Read           if v only used
-//	_ReadWriteSame  if v is set and used in one address (read-alter-rewrite;
-// 	                can't substitute)
-//	_Write          if v is only set
-//	_ReadWriteDiff  if v is set in one address and used in another (so addresses
-// 	                can be rewritten independently)
-//	_None           otherwise (not touched)
-func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) usage {
-	if p.From3Type() != obj.TYPE_NONE && p.From3Type() != obj.TYPE_CONST {
-		// Currently we never generate a From3 with anything other than a constant in it.
-		fmt.Printf("copyu: From3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3))
-	}
-
-	switch p.As {
-	default:
-		fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As))
-		return _ReadWriteSame
-
-	case // read p.From, write p.To
-		s390x.AMOVH,
-		s390x.AMOVHZ,
-		s390x.AMOVB,
-		s390x.AMOVBZ,
-		s390x.AMOVW,
-		s390x.AMOVWZ,
-		s390x.AMOVD,
-		s390x.ANEG,
-		s390x.AADDME,
-		s390x.AADDZE,
-		s390x.ASUBME,
-		s390x.ASUBZE,
-		s390x.AFMOVS,
-		s390x.AFMOVD,
-		s390x.ALEDBR,
-		s390x.AFNEG,
-		s390x.ALDEBR,
-		s390x.ACLFEBR,
-		s390x.ACLGEBR,
-		s390x.ACLFDBR,
-		s390x.ACLGDBR,
-		s390x.ACFEBRA,
-		s390x.ACGEBRA,
-		s390x.ACFDBRA,
-		s390x.ACGDBRA,
-		s390x.ACELFBR,
-		s390x.ACELGBR,
-		s390x.ACDLFBR,
-		s390x.ACDLGBR,
-		s390x.ACEFBRA,
-		s390x.ACEGBRA,
-		s390x.ACDFBRA,
-		s390x.ACDGBRA,
-		s390x.AFSQRT:
-
-		if s != nil {
-			copysub(&p.From, v, s)
-
-			// Update only indirect uses of v in p.To
-			if !copyas(&p.To, v) {
-				copysub(&p.To, v, s)
-			}
-			return _None
-		}
-
-		if copyas(&p.To, v) {
-			// Fix up implicit from
-			if p.From.Type == obj.TYPE_NONE {
-				p.From = p.To
-			}
-			if copyau(&p.From, v) {
-				return _ReadWriteDiff
-			}
-			return _Write
-		}
-
-		if copyau(&p.From, v) {
-			return _Read
-		}
-		if copyau(&p.To, v) {
-			// p.To only indirectly uses v
-			return _Read
-		}
-
-		return _None
-
-	// read p.From, read p.Reg, write p.To
-	case s390x.AADD,
-		s390x.AADDC,
-		s390x.AADDE,
-		s390x.ASUB,
-		s390x.ASLW,
-		s390x.ASRW,
-		s390x.ASRAW,
-		s390x.ASLD,
-		s390x.ASRD,
-		s390x.ASRAD,
-		s390x.ARLL,
-		s390x.ARLLG,
-		s390x.AOR,
-		s390x.AORN,
-		s390x.AAND,
-		s390x.AANDN,
-		s390x.ANAND,
-		s390x.ANOR,
-		s390x.AXOR,
-		s390x.AMULLW,
-		s390x.AMULLD,
-		s390x.AMULHD,
-		s390x.AMULHDU,
-		s390x.ADIVW,
-		s390x.ADIVD,
-		s390x.ADIVWU,
-		s390x.ADIVDU,
-		s390x.AFADDS,
-		s390x.AFADD,
-		s390x.AFSUBS,
-		s390x.AFSUB,
-		s390x.AFMULS,
-		s390x.AFMUL,
-		s390x.AFDIVS,
-		s390x.AFDIV:
-		if s != nil {
-			copysub(&p.From, v, s)
-			copysub1(p, v, s)
-
-			// Update only indirect uses of v in p.To
-			if !copyas(&p.To, v) {
-				copysub(&p.To, v, s)
-			}
-		}
-
-		if copyas(&p.To, v) {
-			if p.Reg == 0 {
-				p.Reg = p.To.Reg
-			}
-			if copyau(&p.From, v) || copyau1(p, v) {
-				return _ReadWriteDiff
-			}
-			return _Write
-		}
-
-		if copyau(&p.From, v) {
-			return _Read
-		}
-		if copyau1(p, v) {
-			return _Read
-		}
-		if copyau(&p.To, v) {
-			return _Read
-		}
-		return _None
-
-	case s390x.ABEQ,
-		s390x.ABGT,
-		s390x.ABGE,
-		s390x.ABLT,
-		s390x.ABLE,
-		s390x.ABNE,
-		s390x.ABVC,
-		s390x.ABVS:
-		return _None
-
-	case obj.ACHECKNIL, // read p.From
-		s390x.ACMP, // read p.From, read p.To
-		s390x.ACMPU,
-		s390x.ACMPW,
-		s390x.ACMPWU,
-		s390x.AFCMPO,
-		s390x.AFCMPU,
-		s390x.ACEBR,
-		s390x.AMVC,
-		s390x.ACLC,
-		s390x.AXC,
-		s390x.AOC,
-		s390x.ANC:
-		if s != nil {
-			copysub(&p.From, v, s)
-			copysub(&p.To, v, s)
-			return _None
-		}
-
-		if copyau(&p.From, v) {
-			return _Read
-		}
-		if copyau(&p.To, v) {
-			return _Read
-		}
-		return _None
-
-	case s390x.ACMPBNE, s390x.ACMPBEQ,
-		s390x.ACMPBLT, s390x.ACMPBLE,
-		s390x.ACMPBGT, s390x.ACMPBGE,
-		s390x.ACMPUBNE, s390x.ACMPUBEQ,
-		s390x.ACMPUBLT, s390x.ACMPUBLE,
-		s390x.ACMPUBGT, s390x.ACMPUBGE:
-		if s != nil {
-			copysub(&p.From, v, s)
-			copysub1(p, v, s)
-			return _None
-		}
-		if copyau(&p.From, v) {
-			return _Read
-		}
-		if copyau1(p, v) {
-			return _Read
-		}
-		return _None
-
-	case s390x.ACLEAR:
-		if s != nil {
-			copysub(&p.To, v, s)
-			return _None
-		}
-		if copyau(&p.To, v) {
-			return _Read
-		}
-		return _None
-
-	// go never generates a branch to a GPR
-	// read p.To
-	case s390x.ABR:
-		if s != nil {
-			copysub(&p.To, v, s)
-			return _None
-		}
-
-		if copyau(&p.To, v) {
-			return _Read
-		}
-		return _None
-
-	case obj.ARET, obj.AUNDEF:
-		if s != nil {
-			return _None
-		}
-
-		// All registers die at this point, so claim
-		// everything is set (and not used).
-		return _Write
-
-	case s390x.ABL:
-		if v.Type == obj.TYPE_REG {
-			if s390x.REGARG != -1 && v.Reg == s390x.REGARG {
-				return _ReadWriteSame
-			}
-			if p.From.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
-				return _ReadWriteSame
-			}
-			if v.Reg == s390x.REGZERO {
-				// Deliberately inserted nops set R0.
-				return _ReadWriteSame
-			}
-			if v.Reg == s390x.REGCTXT {
-				// Context register for closures.
-				// TODO(mundaym): not sure if we need to exclude this.
-				return _ReadWriteSame
-			}
-		}
-		if s != nil {
-			copysub(&p.To, v, s)
-			return _None
-		}
-		if copyau(&p.To, v) {
-			return _ReadWriteDiff
-		}
-		return _Write
-
-	case obj.ATEXT:
-		if v.Type == obj.TYPE_REG {
-			if v.Reg == s390x.REGARG {
-				return _Write
-			}
-		}
-		return _None
-
-	case obj.APCDATA,
-		obj.AFUNCDATA,
-		obj.AVARDEF,
-		obj.AVARKILL,
-		obj.AVARLIVE,
-		obj.AUSEFIELD,
-		obj.ANOP:
-		return _None
-	}
-}
-
-// copyas returns 1 if a and v address the same register.
-//
-// If a is the from operand, this means this operation reads the
-// register in v.  If a is the to operand, this means this operation
-// writes the register in v.
-func copyas(a *obj.Addr, v *obj.Addr) bool {
-	if isReg(v) {
-		if a.Type == v.Type {
-			if a.Reg == v.Reg {
-				return true
-			}
-		}
-	}
-	return false
-}
-
-// copyau returns 1 if a either directly or indirectly addresses the
-// same register as v.
-//
-// If a is the from operand, this means this operation reads the
-// register in v.  If a is the to operand, this means the operation
-// either reads or writes the register in v (if !copyas(a, v), then
-// the operation reads the register in v).
-func copyau(a *obj.Addr, v *obj.Addr) bool {
-	if copyas(a, v) {
-		return true
-	}
-	if v.Type == obj.TYPE_REG {
-		if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) {
-			if v.Reg == a.Reg {
-				return true
-			}
-		}
-	}
-	return false
-}
-
-// copyau1 returns 1 if p.Reg references the same register as v and v
-// is a direct reference.
-func copyau1(p *obj.Prog, v *obj.Addr) bool {
-	if isReg(v) && v.Reg != 0 {
-		if p.Reg == v.Reg {
-			return true
-		}
-	}
-	return false
-}
-
-// copysub replaces v.Reg with s.Reg if a.Reg and v.Reg are direct
-// references to the same register.
-func copysub(a, v, s *obj.Addr) {
-	if copyau(a, v) {
-		a.Reg = s.Reg
-	}
-}
-
-// copysub1 replaces p.Reg with s.Reg if p.Reg and v.Reg are direct
-// references to the same register.
-func copysub1(p *obj.Prog, v, s *obj.Addr) {
-	if copyau1(p, v) {
-		p.Reg = s.Reg
-	}
-}
-
-func sameaddr(a *obj.Addr, v *obj.Addr) bool {
-	if a.Type != v.Type {
-		return false
-	}
-	if isReg(v) && a.Reg == v.Reg {
-		return true
-	}
-	if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM {
-		// TODO(mundaym): is the offset enough here? Node?
-		if v.Offset == a.Offset {
-			return true
-		}
-	}
-	return false
-}
-
-func smallindir(a *obj.Addr, reg *obj.Addr) bool {
-	return reg.Type == obj.TYPE_REG &&
-		a.Type == obj.TYPE_MEM &&
-		a.Reg == reg.Reg &&
-		0 <= a.Offset && a.Offset < 4096
-}
-
-func stackaddr(a *obj.Addr) bool {
-	// TODO(mundaym): the name implies this should check
-	// for TYPE_ADDR with a base register REGSP.
-	return a.Type == obj.TYPE_REG && a.Reg == s390x.REGSP
-}
-
-// isMove returns true if p is a move. Moves may imply
-// sign/zero extension.
-func isMove(p *obj.Prog) bool {
-	switch p.As {
-	case s390x.AMOVD,
-		s390x.AMOVW, s390x.AMOVWZ,
-		s390x.AMOVH, s390x.AMOVHZ,
-		s390x.AMOVB, s390x.AMOVBZ,
-		s390x.AFMOVD, s390x.AFMOVS:
-		return true
-	}
-	return false
-}
-
-// isLoad returns true if p is a move from memory to a register.
-func isLoad(p *obj.Prog) bool {
-	if !isMove(p) {
-		return false
-	}
-	if !(isGPR(&p.To) || isFPR(&p.To)) {
-		return false
-	}
-	if p.From.Type != obj.TYPE_MEM {
-		return false
-	}
-	return true
-}
-
-// isStore returns true if p is a move from a register to memory.
-func isStore(p *obj.Prog) bool {
-	if !isMove(p) {
-		return false
-	}
-	if !(isGPR(&p.From) || isFPR(&p.From) || isConst(&p.From)) {
-		return false
-	}
-	if p.To.Type != obj.TYPE_MEM {
-		return false
-	}
-	return true
-}
-
-// sameStackMem returns true if a and b are both memory operands
-// and address the same location which must reside on the stack.
-func sameStackMem(a, b *obj.Addr) bool {
-	if a.Type != obj.TYPE_MEM ||
-		b.Type != obj.TYPE_MEM ||
-		a.Name != b.Name ||
-		a.Sym != b.Sym ||
-		a.Node != b.Node ||
-		a.Reg != b.Reg ||
-		a.Index != b.Index ||
-		a.Offset != b.Offset {
-		return false
-	}
-	switch a.Name {
-	case obj.NAME_NONE:
-		return a.Reg == s390x.REGSP
-	case obj.NAME_PARAM, obj.NAME_AUTO:
-		// params and autos are always on the stack
-		return true
-	}
-	return false
-}
-
-// removeLoadHitStores trys to remove loads that take place
-// immediately after a store to the same location. Returns
-// true if load-hit-stores were removed.
-//
-// For example:
-// 	MOVD	R1, 0(R15)
-// 	MOVD	0(R15), R2
-// Would become:
-// 	MOVD	R1, 0(R15)
-// 	MOVD	R1, R2
-func removeLoadHitStores(r *gc.Flow) int {
-	n := 0
-	for ; r != nil; r = r.Link {
-		p := r.Prog
-		if !isStore(p) {
-			continue
-		}
-		for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) {
-			pp := rr.Prog
-			if gc.Uniqp(rr) == nil {
-				break
-			}
-			if pp.As == obj.ANOP {
-				continue
-			}
-			if isLoad(pp) && sameStackMem(&p.To, &pp.From) {
-				if size(p.As) >= size(pp.As) && isGPR(&p.From) == isGPR(&pp.To) {
-					pp.From = p.From
-				}
-			}
-			if !isMove(pp) || isStore(pp) {
-				break
-			}
-			if copyau(&p.From, &pp.To) {
-				break
-			}
-		}
-	}
-	return n
-}
-
-// size returns the width of the given move.
-func size(as obj.As) int {
-	switch as {
-	case s390x.AMOVD, s390x.AFMOVD:
-		return 8
-	case s390x.AMOVW, s390x.AMOVWZ, s390x.AFMOVS:
-		return 4
-	case s390x.AMOVH, s390x.AMOVHZ:
-		return 2
-	case s390x.AMOVB, s390x.AMOVBZ:
-		return 1
-	}
-	return -1
-}
-
-// castPropagation tries to eliminate unecessary casts.
-//
-// For example:
-// 	MOVHZ	R1, R2     // uint16
-//	MOVB	R2, 0(R15) // int8
-// Can be simplified to:
-//	MOVB	R1, 0(R15)
-func castPropagation(r *gc.Flow) int {
-	n := 0
-	for ; r != nil; r = r.Link {
-		p := r.Prog
-		if !isMove(p) || !isGPR(&p.To) {
-			continue
-		}
-
-		// r is a move with a destination register
-		var move *gc.Flow
-		for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) {
-			if gc.Uniqp(rr) == nil {
-				// branch target: leave alone
-				break
-			}
-			pp := rr.Prog
-			if isMove(pp) && copyas(&pp.From, &p.To) {
-				if pp.To.Type == obj.TYPE_MEM {
-					if p.From.Type == obj.TYPE_MEM ||
-						p.From.Type == obj.TYPE_ADDR {
-						break
-					}
-					if p.From.Type == obj.TYPE_CONST &&
-						int64(int16(p.From.Offset)) != p.From.Offset {
-						break
-					}
-				}
-				move = rr
-				break
-			}
-			if pp.As == obj.ANOP {
-				continue
-			}
-			break
-		}
-		if move == nil {
-			continue
-		}
-
-		// we have a move that reads from our destination reg, check if any future
-		// instructions also read from the reg
-		mp := move.Prog
-		if !copyas(&mp.From, &mp.To) {
-			safe := false
-			for rr := gc.Uniqs(move); rr != nil; rr = gc.Uniqs(rr) {
-				if gc.Uniqp(rr) == nil {
-					break
-				}
-				switch copyu(rr.Prog, &p.To, nil) {
-				case _None:
-					continue
-				case _Write:
-					safe = true
-				}
-				break
-			}
-			if !safe {
-				continue
-			}
-		}
-
-		// at this point we have something like:
-		// MOV* const/mem/reg, reg
-		// MOV* reg, reg/mem
-		// now check if this is a cast that cannot be forward propagated
-		execute := false
-		if p.As == mp.As || isZero(&p.From) || size(p.As) == size(mp.As) {
-			execute = true
-		} else if isGPR(&p.From) && size(p.As) >= size(mp.As) {
-			execute = true
-		}
-
-		if execute {
-			mp.From = p.From
-			excise(r)
-			n++
-		}
-	}
-	return n
-}
-
-// fuseClear merges memory clear operations.
-//
-// Looks for this pattern (sequence of clears):
-// 	MOVD	R0, n(R15)
-// 	MOVD	R0, n+8(R15)
-// 	MOVD	R0, n+16(R15)
-// Replaces with:
-//	CLEAR	$24, n(R15)
-func fuseClear(r *gc.Flow) int {
-	n := 0
-	var align int64
-	var clear *obj.Prog
-	for ; r != nil; r = r.Link {
-		// If there is a branch into the instruction stream then
-		// we can't fuse into previous instructions.
-		if gc.Uniqp(r) == nil {
-			clear = nil
-		}
-
-		p := r.Prog
-		if p.As == obj.ANOP {
-			continue
-		}
-		if p.As == s390x.AXC {
-			if p.From.Reg == p.To.Reg && p.From.Offset == p.To.Offset {
-				// TODO(mundaym): merge clears?
-				p.As = s390x.ACLEAR
-				p.From.Offset = p.From3.Offset
-				p.From3 = nil
-				p.From.Type = obj.TYPE_CONST
-				p.From.Reg = 0
-				clear = p
-			} else {
-				clear = nil
-			}
-			continue
-		}
-
-		// Is our source a constant zero?
-		if !isZero(&p.From) {
-			clear = nil
-			continue
-		}
-
-		// Are we moving to memory?
-		if p.To.Type != obj.TYPE_MEM ||
-			p.To.Index != 0 ||
-			p.To.Offset >= 4096 ||
-			!(p.To.Name == obj.NAME_NONE || p.To.Name == obj.NAME_AUTO || p.To.Name == obj.NAME_PARAM) {
-			clear = nil
-			continue
-		}
-
-		size := int64(0)
-		switch p.As {
-		default:
-			clear = nil
-			continue
-		case s390x.AMOVB, s390x.AMOVBZ:
-			size = 1
-		case s390x.AMOVH, s390x.AMOVHZ:
-			size = 2
-		case s390x.AMOVW, s390x.AMOVWZ:
-			size = 4
-		case s390x.AMOVD:
-			size = 8
-		}
-
-		// doubleword aligned clears should be kept doubleword
-		// aligned
-		if (size == 8 && align != 8) || (size != 8 && align == 8) {
-			clear = nil
-		}
-
-		if clear != nil &&
-			clear.To.Reg == p.To.Reg &&
-			clear.To.Name == p.To.Name &&
-			clear.To.Node == p.To.Node &&
-			clear.To.Sym == p.To.Sym {
-
-			min := clear.To.Offset
-			max := clear.To.Offset + clear.From.Offset
-
-			// previous clear is already clearing this region
-			if min <= p.To.Offset && max >= p.To.Offset+size {
-				excise(r)
-				n++
-				continue
-			}
-
-			// merge forwards
-			if max == p.To.Offset {
-				clear.From.Offset += size
-				excise(r)
-				n++
-				continue
-			}
-
-			// merge backwards
-			if min-size == p.To.Offset {
-				clear.From.Offset += size
-				clear.To.Offset -= size
-				excise(r)
-				n++
-				continue
-			}
-		}
-
-		// transform into clear
-		p.From.Type = obj.TYPE_CONST
-		p.From.Offset = size
-		p.From.Reg = 0
-		p.As = s390x.ACLEAR
-		clear = p
-		align = size
-	}
-	return n
-}
-
-// fuseMultiple merges memory loads and stores into load multiple and
-// store multiple operations.
-//
-// Looks for this pattern (sequence of loads or stores):
-// 	MOVD	R1, 0(R15)
-//	MOVD	R2, 8(R15)
-//	MOVD	R3, 16(R15)
-// Replaces with:
-//	STMG	R1, R3, 0(R15)
-func fuseMultiple(r *gc.Flow) int {
-	n := 0
-	var fused *obj.Prog
-	for ; r != nil; r = r.Link {
-		// If there is a branch into the instruction stream then
-		// we can't fuse into previous instructions.
-		if gc.Uniqp(r) == nil {
-			fused = nil
-		}
-
-		p := r.Prog
-
-		isStore := isGPR(&p.From) && isBDMem(&p.To)
-		isLoad := isGPR(&p.To) && isBDMem(&p.From)
-
-		// are we a candidate?
-		size := int64(0)
-		switch p.As {
-		default:
-			fused = nil
-			continue
-		case obj.ANOP:
-			// skip over nops
-			continue
-		case s390x.AMOVW, s390x.AMOVWZ:
-			size = 4
-			// TODO(mundaym): 32-bit load multiple is currently not supported
-			// as it requires sign/zero extension.
-			if !isStore {
-				fused = nil
-				continue
-			}
-		case s390x.AMOVD:
-			size = 8
-			if !isLoad && !isStore {
-				fused = nil
-				continue
-			}
-		}
-
-		// If we merge two loads/stores with different source/destination Nodes
-		// then we will lose a reference the second Node which means that the
-		// compiler might mark the Node as unused and free its slot on the stack.
-		// TODO(mundaym): allow this by adding a dummy reference to the Node.
-		if fused == nil ||
-			fused.From.Node != p.From.Node ||
-			fused.From.Type != p.From.Type ||
-			fused.To.Node != p.To.Node ||
-			fused.To.Type != p.To.Type {
-			fused = p
-			continue
-		}
-
-		// check two addresses
-		ca := func(a, b *obj.Addr, offset int64) bool {
-			return a.Reg == b.Reg && a.Offset+offset == b.Offset &&
-				a.Sym == b.Sym && a.Name == b.Name
-		}
-
-		switch fused.As {
-		default:
-			fused = p
-		case s390x.AMOVW, s390x.AMOVWZ:
-			if size == 4 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 4) {
-				fused.As = s390x.ASTMY
-				fused.Reg = p.From.Reg
-				excise(r)
-				n++
-			} else {
-				fused = p
-			}
-		case s390x.AMOVD:
-			if size == 8 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 8) {
-				fused.As = s390x.ASTMG
-				fused.Reg = p.From.Reg
-				excise(r)
-				n++
-			} else if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, 8) {
-				fused.As = s390x.ALMG
-				fused.Reg = fused.To.Reg
-				fused.To.Reg = p.To.Reg
-				excise(r)
-				n++
-			} else {
-				fused = p
-			}
-		case s390x.ASTMG, s390x.ASTMY:
-			if (fused.As == s390x.ASTMY && size != 4) ||
-				(fused.As == s390x.ASTMG && size != 8) {
-				fused = p
-				continue
-			}
-			offset := size * int64(fused.Reg-fused.From.Reg+1)
-			if fused.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, offset) {
-				fused.Reg = p.From.Reg
-				excise(r)
-				n++
-			} else {
-				fused = p
-			}
-		case s390x.ALMG:
-			offset := 8 * int64(fused.To.Reg-fused.Reg+1)
-			if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, offset) {
-				fused.To.Reg = p.To.Reg
-				excise(r)
-				n++
-			} else {
-				fused = p
-			}
-		}
-	}
-	return n
-}
-
-// simplifyOps looks for side-effect free ops that can be removed or
-// replaced with moves.
-//
-// For example:
-// 	XOR $0, R1 => NOP
-//	ADD $0, R1, R2 => MOVD R1, R2
-func simplifyOps(r *gc.Flow) int {
-	n := 0
-	for ; r != nil; r = r.Link {
-		p := r.Prog
-
-		// if the target is R0 then this is a required NOP
-		if isGPR(&p.To) && p.To.Reg == s390x.REGZERO {
-			continue
-		}
-
-		switch p.As {
-		case s390x.AADD, s390x.ASUB,
-			s390x.AOR, s390x.AXOR,
-			s390x.ASLW, s390x.ASRW, s390x.ASRAW,
-			s390x.ASLD, s390x.ASRD, s390x.ASRAD,
-			s390x.ARLL, s390x.ARLLG:
-			if isZero(&p.From) && isGPR(&p.To) {
-				if p.Reg == 0 || p.Reg == p.To.Reg {
-					excise(r)
-					n++
-				} else {
-					p.As = s390x.AMOVD
-					p.From.Type = obj.TYPE_REG
-					p.From.Reg = p.Reg
-					p.Reg = 0
-				}
-			}
-		case s390x.AMULLW, s390x.AAND:
-			if isZero(&p.From) && isGPR(&p.To) {
-				p.As = s390x.AMOVD
-				p.From.Type = obj.TYPE_REG
-				p.From.Reg = s390x.REGZERO
-				p.Reg = 0
-			}
-		}
-	}
-	return n
-}
-
-// fuseOpMoves looks for moves following 2-operand operations and trys to merge them into
-// a 3-operand operation.
-//
-// For example:
-//	ADD R1, R2
-//	MOVD R2, R3
-// might become
-//	ADD R1, R2, R3
-func fuseOpMoves(r *gc.Flow) int {
-	n := 0
-	for ; r != nil; r = r.Link {
-		p := r.Prog
-		switch p.As {
-		case s390x.AADD:
-		case s390x.ASUB:
-			if isConst(&p.From) && int64(int16(p.From.Offset)) != p.From.Offset {
-				continue
-			}
-		case s390x.ASLW,
-			s390x.ASRW,
-			s390x.ASRAW,
-			s390x.ASLD,
-			s390x.ASRD,
-			s390x.ASRAD,
-			s390x.ARLL,
-			s390x.ARLLG:
-			// ok - p.From will be a reg or a constant
-		case s390x.AOR,
-			s390x.AORN,
-			s390x.AAND,
-			s390x.AANDN,
-			s390x.ANAND,
-			s390x.ANOR,
-			s390x.AXOR,
-			s390x.AMULLW,
-			s390x.AMULLD:
-			if isConst(&p.From) {
-				// these instructions can either use 3 register form
-				// or have an immediate but not both
-				continue
-			}
-		default:
-			continue
-		}
-
-		if p.Reg != 0 && p.Reg != p.To.Reg {
-			continue
-		}
-
-		var move *gc.Flow
-		rr := gc.Uniqs(r)
-		for {
-			if rr == nil || gc.Uniqp(rr) == nil || rr == r {
-				break
-			}
-			pp := rr.Prog
-			switch copyu(pp, &p.To, nil) {
-			case _None:
-				rr = gc.Uniqs(rr)
-				continue
-			case _Read:
-				if move == nil && pp.As == s390x.AMOVD && isGPR(&pp.From) && isGPR(&pp.To) {
-					move = rr
-					rr = gc.Uniqs(rr)
-					continue
-				}
-			case _Write:
-				if move == nil {
-					// dead code
-					excise(r)
-					n++
-				} else {
-					for prev := gc.Uniqp(move); prev != r; prev = gc.Uniqp(prev) {
-						if copyu(prev.Prog, &move.Prog.To, nil) != 0 {
-							move = nil
-							break
-						}
-					}
-					if move == nil {
-						break
-					}
-					p.Reg, p.To.Reg = p.To.Reg, move.Prog.To.Reg
-					excise(move)
-					n++
-
-					// clean up
-					if p.From.Reg == p.To.Reg && isCommutative(p.As) {
-						p.From.Reg, p.Reg = p.Reg, 0
-					}
-					if p.To.Reg == p.Reg {
-						p.Reg = 0
-					}
-					// we could try again if p has become a 2-operand op
-					// but in testing nothing extra was extracted
-				}
-			}
-			break
-		}
-	}
-	return n
-}
-
-// isCommutative returns true if the order of input operands
-// does not affect the result. For example:
-//	x + y == y + x so ADD is commutative
-//	x ^ y == y ^ x so XOR is commutative
-func isCommutative(as obj.As) bool {
-	switch as {
-	case s390x.AADD,
-		s390x.AOR,
-		s390x.AAND,
-		s390x.AXOR,
-		s390x.AMULLW,
-		s390x.AMULLD:
-		return true
-	}
-	return false
-}
-
-// applyCast applies the cast implied by the given move
-// instruction to v and returns the result.
-func applyCast(cast obj.As, v int64) int64 {
-	switch cast {
-	case s390x.AMOVWZ:
-		return int64(uint32(v))
-	case s390x.AMOVHZ:
-		return int64(uint16(v))
-	case s390x.AMOVBZ:
-		return int64(uint8(v))
-	case s390x.AMOVW:
-		return int64(int32(v))
-	case s390x.AMOVH:
-		return int64(int16(v))
-	case s390x.AMOVB:
-		return int64(int8(v))
-	}
-	return v
-}
-
-// constantPropagation removes redundant constant copies.
-func constantPropagation(r *gc.Flow) int {
-	n := 0
-	// find MOV $con,R followed by
-	// another MOV $con,R without
-	// setting R in the interim
-	for ; r != nil; r = r.Link {
-		p := r.Prog
-		if isMove(p) {
-			if !isReg(&p.To) {
-				continue
-			}
-			if !isConst(&p.From) {
-				continue
-			}
-		} else {
-			continue
-		}
-
-		rr := r
-		for {
-			rr = gc.Uniqs(rr)
-			if rr == nil || rr == r {
-				break
-			}
-			if gc.Uniqp(rr) == nil {
-				break
-			}
-
-			pp := rr.Prog
-			t := copyu(pp, &p.To, nil)
-			switch t {
-			case _None:
-				continue
-			case _Read:
-				if !isGPR(&pp.From) || !isMove(pp) {
-					continue
-				}
-				if p.From.Type == obj.TYPE_CONST {
-					v := applyCast(p.As, p.From.Offset)
-					if isGPR(&pp.To) {
-						if int64(int32(v)) == v || ((v>>32)<<32) == v {
-							pp.From.Reg = 0
-							pp.From.Offset = v
-							pp.From.Type = obj.TYPE_CONST
-							n++
-						}
-					} else if int64(int16(v)) == v {
-						pp.From.Reg = 0
-						pp.From.Offset = v
-						pp.From.Type = obj.TYPE_CONST
-						n++
-					}
-				}
-				continue
-			case _Write:
-				if p.As != pp.As || p.From.Type != pp.From.Type {
-					break
-				}
-				if p.From.Type == obj.TYPE_CONST && p.From.Offset == pp.From.Offset {
-					excise(rr)
-					n++
-					continue
-				} else if p.From.Type == obj.TYPE_FCONST {
-					if p.From.Val.(float64) == pp.From.Val.(float64) {
-						excise(rr)
-						n++
-						continue
-					}
-				}
-			}
-			break
-		}
-	}
-	return n
-}
-
-// copyPropagation tries to eliminate register-to-register moves.
-func copyPropagation(r *gc.Flow) int {
-	n := 0
-	for ; r != nil; r = r.Link {
-		p := r.Prog
-		if isMove(p) && isReg(&p.To) {
-			// Convert uses to $0 to uses of R0 and
-			// propagate R0
-			if isGPR(&p.To) && isZero(&p.From) {
-				p.From.Type = obj.TYPE_REG
-				p.From.Reg = s390x.REGZERO
-			}
-
-			// Try to eliminate reg->reg moves
-			if isGPR(&p.From) || isFPR(&p.From) {
-				if copyprop(r) || (subprop(r) && copyprop(r)) {
-					excise(r)
-					n++
-				}
-			}
-		}
-	}
-	return n
-}
-
-// loadPipelining pushes any load from memory as early as possible.
-func loadPipelining(r *gc.Flow) int {
-	for ; r != nil; r = r.Link {
-		p := r.Prog
-		if isLoad(p) {
-			pushback(r)
-		}
-	}
-	return 0
-}
-
-// fuseCompareBranch finds comparisons followed by a branch and converts
-// them into a compare-and-branch instruction (which avoid setting the
-// condition code).
-func fuseCompareBranch(r *gc.Flow) int {
-	n := 0
-	for ; r != nil; r = r.Link {
-		p := r.Prog
-		r1 := gc.Uniqs(r)
-		if r1 == nil {
-			continue
-		}
-		p1 := r1.Prog
-
-		var ins obj.As
-		switch p.As {
-		case s390x.ACMP:
-			switch p1.As {
-			case s390x.ABCL, s390x.ABC:
-				continue
-			case s390x.ABEQ:
-				ins = s390x.ACMPBEQ
-			case s390x.ABGE:
-				ins = s390x.ACMPBGE
-			case s390x.ABGT:
-				ins = s390x.ACMPBGT
-			case s390x.ABLE:
-				ins = s390x.ACMPBLE
-			case s390x.ABLT:
-				ins = s390x.ACMPBLT
-			case s390x.ABNE:
-				ins = s390x.ACMPBNE
-			default:
-				continue
-			}
-
-		case s390x.ACMPU:
-			switch p1.As {
-			case s390x.ABCL, s390x.ABC:
-				continue
-			case s390x.ABEQ:
-				ins = s390x.ACMPUBEQ
-			case s390x.ABGE:
-				ins = s390x.ACMPUBGE
-			case s390x.ABGT:
-				ins = s390x.ACMPUBGT
-			case s390x.ABLE:
-				ins = s390x.ACMPUBLE
-			case s390x.ABLT:
-				ins = s390x.ACMPUBLT
-			case s390x.ABNE:
-				ins = s390x.ACMPUBNE
-			default:
-				continue
-			}
-
-		case s390x.ACMPW, s390x.ACMPWU:
-			continue
-
-		default:
-			continue
-		}
-
-		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-			fmt.Printf("cnb %v; %v  ", p, p1)
-		}
-
-		if p1.To.Sym != nil {
-			continue
-		}
-
-		if p.To.Type == obj.TYPE_REG {
-			p1.As = ins
-			p1.From = p.From
-			p1.Reg = p.To.Reg
-			p1.From3 = nil
-		} else if p.To.Type == obj.TYPE_CONST {
-			switch p.As {
-			case s390x.ACMP, s390x.ACMPW:
-				if (p.To.Offset < -(1 << 7)) || (p.To.Offset >= ((1 << 7) - 1)) {
-					continue
-				}
-			case s390x.ACMPU, s390x.ACMPWU:
-				if p.To.Offset >= (1 << 8) {
-					continue
-				}
-			default:
-			}
-			p1.As = ins
-			p1.From = p.From
-			p1.Reg = 0
-			p1.From3 = new(obj.Addr)
-			*(p1.From3) = p.To
-		} else {
-			continue
-		}
-
-		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-			fmt.Printf("%v\n", p1)
-		}
-		excise(r)
-		n++
-	}
-	return n
-}
-
-// deadCodeElimination removes writes to registers which are written
-// to again before they are next read.
-func deadCodeElimination(r *gc.Flow) int {
-	n := 0
-	for ; r != nil; r = r.Link {
-		p := r.Prog
-		// Currently there are no instructions which write to multiple
-		// registers in copyu. This check will need to change if there
-		// ever are.
-		if !(isGPR(&p.To) || isFPR(&p.To)) || copyu(p, &p.To, nil) != _Write {
-			continue
-		}
-		for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) {
-			t := copyu(rr.Prog, &p.To, nil)
-			if t == _None {
-				continue
-			}
-			if t == _Write {
-				excise(r)
-				n++
-			}
-			break
-		}
-	}
-	return n
-}
diff --git a/src/cmd/compile/internal/s390x/prog.go b/src/cmd/compile/internal/s390x/prog.go
index 306adf8..f356617 100644
--- a/src/cmd/compile/internal/s390x/prog.go
+++ b/src/cmd/compile/internal/s390x/prog.go
@@ -17,14 +17,13 @@ import (
 // Instructions not generated need not be listed.
 // As an exception to that rule, we typically write down all the
 // size variants of an operation even if we just use a subset.
-var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
+var progtable = [s390x.ALAST & obj.AMask]gc.ProgInfo{
 	obj.ATYPE & obj.AMask:     {Flags: gc.Pseudo | gc.Skip},
 	obj.ATEXT & obj.AMask:     {Flags: gc.Pseudo},
 	obj.AFUNCDATA & obj.AMask: {Flags: gc.Pseudo},
 	obj.APCDATA & obj.AMask:   {Flags: gc.Pseudo},
 	obj.AUNDEF & obj.AMask:    {Flags: gc.Break},
 	obj.AUSEFIELD & obj.AMask: {Flags: gc.OK},
-	obj.ACHECKNIL & obj.AMask: {Flags: gc.LeftRead},
 	obj.AVARDEF & obj.AMask:   {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARKILL & obj.AMask:  {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARLIVE & obj.AMask:  {Flags: gc.Pseudo | gc.LeftRead},
@@ -36,23 +35,42 @@ var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
 	// Integer
 	s390x.AADD & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.ASUB & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.ASUBE & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.AADDW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.ASUBW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.ANEG & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.ANEGW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.AAND & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.AANDW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.AOR & obj.AMask:     {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.AORW & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.AXOR & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.AXORW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.AMULLD & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.AMULLW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	s390x.AMULHD & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
-	s390x.AMULHDU & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.AMULHD & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.AMULHDU & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.ADIVD & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.ADIVDU & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.ADIVW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.ADIVWU & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.ASLD & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.ASLW & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.ASRD & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.ASRW & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.ASRAD & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.ASRAW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.ARLL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.ARLLG & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
 	s390x.ACMP & obj.AMask:    {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead},
 	s390x.ACMPU & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead},
+	s390x.ACMPW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
+	s390x.ACMPWU & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightRead},
+	s390x.AMODD & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.AMODDU & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.AMODW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.AMODWU & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RegRead | gc.RightWrite},
+	s390x.AFLOGR & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite},
 
 	// Floating point.
 	s390x.AFADD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RegRead | gc.RightWrite},
@@ -68,6 +86,8 @@ var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
 	s390x.ALEDBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
 	s390x.ALDEBR & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Conv},
 	s390x.AFSQRT & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
+	s390x.AFNEG & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite},
+	s390x.AFNEGS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite},
 
 	// Conversions
 	s390x.ACEFBRA & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Conv},
@@ -88,15 +108,24 @@ var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
 	s390x.ACLGDBR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Conv},
 
 	// Moves
-	s390x.AMOVB & obj.AMask:  {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	s390x.AMOVBZ & obj.AMask: {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	s390x.AMOVH & obj.AMask:  {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	s390x.AMOVHZ & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	s390x.AMOVW & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	s390x.AMOVWZ & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	s390x.AMOVD & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
-	s390x.AFMOVS & obj.AMask: {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
-	s390x.AFMOVD & obj.AMask: {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
+	s390x.AMOVB & obj.AMask:   {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	s390x.AMOVBZ & obj.AMask:  {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	s390x.AMOVH & obj.AMask:   {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	s390x.AMOVHZ & obj.AMask:  {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	s390x.AMOVW & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	s390x.AMOVWZ & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	s390x.AMOVD & obj.AMask:   {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	s390x.AMOVHBR & obj.AMask: {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	s390x.AMOVWBR & obj.AMask: {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	s390x.AMOVDBR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	s390x.AFMOVS & obj.AMask:  {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move | gc.Conv},
+	s390x.AFMOVD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
+	s390x.AMOVDEQ & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	s390x.AMOVDGE & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	s390x.AMOVDGT & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	s390x.AMOVDLE & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	s390x.AMOVDLT & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
+	s390x.AMOVDNE & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightWrite | gc.Move},
 
 	// Storage operations
 	s390x.AMVC & obj.AMask: {Flags: gc.LeftRead | gc.LeftAddr | gc.RightWrite | gc.RightAddr},
@@ -114,6 +143,8 @@ var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
 	s390x.ABLT & obj.AMask:     {Flags: gc.Cjmp},
 	s390x.ABGT & obj.AMask:     {Flags: gc.Cjmp},
 	s390x.ABLE & obj.AMask:     {Flags: gc.Cjmp},
+	s390x.ABLEU & obj.AMask:    {Flags: gc.Cjmp},
+	s390x.ABLTU & obj.AMask:    {Flags: gc.Cjmp},
 	s390x.ACMPBEQ & obj.AMask:  {Flags: gc.Cjmp},
 	s390x.ACMPBNE & obj.AMask:  {Flags: gc.Cjmp},
 	s390x.ACMPBGE & obj.AMask:  {Flags: gc.Cjmp},
@@ -127,6 +158,12 @@ var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
 	s390x.ACMPUBGT & obj.AMask: {Flags: gc.Cjmp},
 	s390x.ACMPUBLE & obj.AMask: {Flags: gc.Cjmp},
 
+	// Atomic
+	s390x.ACS & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.LeftWrite | gc.RegRead | gc.RightRead | gc.RightWrite},
+	s390x.ACSG & obj.AMask:  {Flags: gc.SizeQ | gc.LeftRead | gc.LeftWrite | gc.RegRead | gc.RightRead | gc.RightWrite},
+	s390x.ALAA & obj.AMask:  {Flags: gc.SizeL | gc.LeftRead | gc.RightRead | gc.RightWrite},
+	s390x.ALAAG & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightRead | gc.RightWrite},
+
 	// Macros
 	s390x.ACLEAR & obj.AMask: {Flags: gc.SizeQ | gc.LeftRead | gc.RightAddr | gc.RightWrite},
 
@@ -139,9 +176,8 @@ var progtable = [s390x.ALAST & obj.AMask]obj.ProgInfo{
 	obj.ARET & obj.AMask: {Flags: gc.Break},
 }
 
-func proginfo(p *obj.Prog) {
-	info := &p.Info
-	*info = progtable[p.As&obj.AMask]
+func proginfo(p *obj.Prog) gc.ProgInfo {
+	info := progtable[p.As&obj.AMask]
 	if info.Flags == 0 {
 		gc.Fatalf("proginfo: unknown instruction %v", p)
 	}
@@ -151,29 +187,10 @@ func proginfo(p *obj.Prog) {
 		info.Flags |= gc.RightRead /*CanRegRead |*/
 	}
 
-	if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 {
-		info.Regindex |= RtoB(int(p.From.Reg))
-	}
-
-	if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 {
-		info.Regindex |= RtoB(int(p.To.Reg))
-	}
-
 	if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) {
 		info.Flags &^= gc.LeftRead
 		info.Flags |= gc.LeftAddr
 	}
 
-	switch p.As {
-	// load multiple sets a range of registers
-	case s390x.ALMG, s390x.ALMY:
-		for r := p.Reg; r <= p.To.Reg; r++ {
-			info.Regset |= RtoB(int(r))
-		}
-	// store multiple reads a range of registers
-	case s390x.ASTMG, s390x.ASTMY:
-		for r := p.From.Reg; r <= p.Reg; r++ {
-			info.Reguse |= RtoB(int(r))
-		}
-	}
+	return info
 }
diff --git a/src/cmd/compile/internal/s390x/reg.go b/src/cmd/compile/internal/s390x/reg.go
deleted file mode 100644
index 4cb8a9d..0000000
--- a/src/cmd/compile/internal/s390x/reg.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// Derived from Inferno utils/6c/reg.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package s390x
-
-import "cmd/internal/obj/s390x"
-import "cmd/compile/internal/gc"
-
-const (
-	NREGVAR = 32 /* 16 general + 16 floating */
-)
-
-var regname = []string{
-	".R0",
-	".R1",
-	".R2",
-	".R3",
-	".R4",
-	".R5",
-	".R6",
-	".R7",
-	".R8",
-	".R9",
-	".R10",
-	".R11",
-	".R12",
-	".R13",
-	".R14",
-	".R15",
-	".F0",
-	".F1",
-	".F2",
-	".F3",
-	".F4",
-	".F5",
-	".F6",
-	".F7",
-	".F8",
-	".F9",
-	".F10",
-	".F11",
-	".F12",
-	".F13",
-	".F14",
-	".F15",
-}
-
-func regnames(n *int) []string {
-	*n = NREGVAR
-	return regname
-}
-
-func excludedregs() uint64 {
-	// Exclude registers with fixed functions
-	return RtoB(s390x.REG_R0) |
-		RtoB(s390x.REGSP) |
-		RtoB(s390x.REGG) |
-		RtoB(s390x.REGTMP) |
-		RtoB(s390x.REGTMP2) |
-		RtoB(s390x.REG_LR)
-}
-
-func doregbits(r int) uint64 {
-	return 0
-}
-
-/*
- * track register variables including external registers:
- *	bit	reg
- *	0	R0
- *	...	...
- *	15	R15
- *	16+0	F0
- *	16+1	F1
- *	...	...
- *	16+15	F15
- */
-func RtoB(r int) uint64 {
-	if r >= s390x.REG_R0 && r <= s390x.REG_R15 {
-		return 1 << uint(r-s390x.REG_R0)
-	}
-	if r >= s390x.REG_F0 && r <= s390x.REG_F15 {
-		return 1 << uint(16+r-s390x.REG_F0)
-	}
-	return 0
-}
-
-func BtoR(b uint64) int {
-	b &= 0xffff
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + s390x.REG_R0
-}
-
-func BtoF(b uint64) int {
-	b >>= 16
-	b &= 0xffff
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + s390x.REG_F0
-}
diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go
new file mode 100644
index 0000000..e2d3c28
--- /dev/null
+++ b/src/cmd/compile/internal/s390x/ssa.go
@@ -0,0 +1,862 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package s390x
+
+import (
+	"math"
+
+	"cmd/compile/internal/gc"
+	"cmd/compile/internal/ssa"
+	"cmd/internal/obj"
+	"cmd/internal/obj/s390x"
+)
+
+// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
+func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
+	flive := b.FlagsLiveAtEnd
+	if b.Control != nil && b.Control.Type.IsFlags() {
+		flive = true
+	}
+	for i := len(b.Values) - 1; i >= 0; i-- {
+		v := b.Values[i]
+		if flive && v.Op == ssa.OpS390XMOVDconst {
+			// The "mark" is any non-nil Aux value.
+			v.Aux = v
+		}
+		if v.Type.IsFlags() {
+			flive = false
+		}
+		for _, a := range v.Args {
+			if a.Type.IsFlags() {
+				flive = true
+			}
+		}
+	}
+}
+
+// loadByType returns the load instruction of the given type.
+func loadByType(t ssa.Type) obj.As {
+	if t.IsFloat() {
+		switch t.Size() {
+		case 4:
+			return s390x.AFMOVS
+		case 8:
+			return s390x.AFMOVD
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			if t.IsSigned() {
+				return s390x.AMOVB
+			} else {
+				return s390x.AMOVBZ
+			}
+		case 2:
+			if t.IsSigned() {
+				return s390x.AMOVH
+			} else {
+				return s390x.AMOVHZ
+			}
+		case 4:
+			if t.IsSigned() {
+				return s390x.AMOVW
+			} else {
+				return s390x.AMOVWZ
+			}
+		case 8:
+			return s390x.AMOVD
+		}
+	}
+	panic("bad load type")
+}
+
+// storeByType returns the store instruction of the given type.
+func storeByType(t ssa.Type) obj.As {
+	width := t.Size()
+	if t.IsFloat() {
+		switch width {
+		case 4:
+			return s390x.AFMOVS
+		case 8:
+			return s390x.AFMOVD
+		}
+	} else {
+		switch width {
+		case 1:
+			return s390x.AMOVB
+		case 2:
+			return s390x.AMOVH
+		case 4:
+			return s390x.AMOVW
+		case 8:
+			return s390x.AMOVD
+		}
+	}
+	panic("bad store type")
+}
+
+// moveByType returns the reg->reg move instruction of the given type.
+func moveByType(t ssa.Type) obj.As {
+	if t.IsFloat() {
+		return s390x.AFMOVD
+	} else {
+		switch t.Size() {
+		case 1:
+			if t.IsSigned() {
+				return s390x.AMOVB
+			} else {
+				return s390x.AMOVBZ
+			}
+		case 2:
+			if t.IsSigned() {
+				return s390x.AMOVH
+			} else {
+				return s390x.AMOVHZ
+			}
+		case 4:
+			if t.IsSigned() {
+				return s390x.AMOVW
+			} else {
+				return s390x.AMOVWZ
+			}
+		case 8:
+			return s390x.AMOVD
+		}
+	}
+	panic("bad load type")
+}
+
+// opregreg emits instructions for
+//     dest := dest(To) op src(From)
+// and also returns the created obj.Prog so it
+// may be further adjusted (offset, scale, etc).
+func opregreg(op obj.As, dest, src int16) *obj.Prog {
+	p := gc.Prog(op)
+	p.From.Type = obj.TYPE_REG
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = dest
+	p.From.Reg = src
+	return p
+}
+
+// opregregimm emits instructions for
+//	dest := src(From) op off
+// and also returns the created obj.Prog so it
+// may be further adjusted (offset, scale, etc).
+func opregregimm(op obj.As, dest, src int16, off int64) *obj.Prog {
+	p := gc.Prog(op)
+	p.From.Type = obj.TYPE_CONST
+	p.From.Offset = off
+	p.Reg = src
+	p.To.Reg = dest
+	p.To.Type = obj.TYPE_REG
+	return p
+}
+
+func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+	s.SetLineno(v.Line)
+	switch v.Op {
+	case ssa.OpS390XSLD, ssa.OpS390XSLW,
+		ssa.OpS390XSRD, ssa.OpS390XSRW,
+		ssa.OpS390XSRAD, ssa.OpS390XSRAW:
+		r := v.Reg()
+		r1 := v.Args[0].Reg()
+		r2 := v.Args[1].Reg()
+		if r2 == s390x.REG_R0 {
+			v.Fatalf("cannot use R0 as shift value %s", v.LongString())
+		}
+		p := opregreg(v.Op.Asm(), r, r2)
+		if r != r1 {
+			p.Reg = r1
+		}
+	case ssa.OpS390XADD, ssa.OpS390XADDW,
+		ssa.OpS390XSUB, ssa.OpS390XSUBW,
+		ssa.OpS390XAND, ssa.OpS390XANDW,
+		ssa.OpS390XOR, ssa.OpS390XORW,
+		ssa.OpS390XXOR, ssa.OpS390XXORW:
+		r := v.Reg()
+		r1 := v.Args[0].Reg()
+		r2 := v.Args[1].Reg()
+		p := opregreg(v.Op.Asm(), r, r2)
+		if r != r1 {
+			p.Reg = r1
+		}
+	// 2-address opcode arithmetic
+	case ssa.OpS390XMULLD, ssa.OpS390XMULLW,
+		ssa.OpS390XMULHD, ssa.OpS390XMULHDU,
+		ssa.OpS390XFADDS, ssa.OpS390XFADD, ssa.OpS390XFSUBS, ssa.OpS390XFSUB,
+		ssa.OpS390XFMULS, ssa.OpS390XFMUL, ssa.OpS390XFDIVS, ssa.OpS390XFDIV:
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		opregreg(v.Op.Asm(), r, v.Args[1].Reg())
+	case ssa.OpS390XDIVD, ssa.OpS390XDIVW,
+		ssa.OpS390XDIVDU, ssa.OpS390XDIVWU,
+		ssa.OpS390XMODD, ssa.OpS390XMODW,
+		ssa.OpS390XMODDU, ssa.OpS390XMODWU:
+
+		// TODO(mundaym): use the temp registers every time like x86 does with AX?
+		dividend := v.Args[0].Reg()
+		divisor := v.Args[1].Reg()
+
+		// CPU faults upon signed overflow, which occurs when most
+		// negative int is divided by -1.
+		var j *obj.Prog
+		if v.Op == ssa.OpS390XDIVD || v.Op == ssa.OpS390XDIVW ||
+			v.Op == ssa.OpS390XMODD || v.Op == ssa.OpS390XMODW {
+
+			var c *obj.Prog
+			c = gc.Prog(s390x.ACMP)
+			j = gc.Prog(s390x.ABEQ)
+
+			c.From.Type = obj.TYPE_REG
+			c.From.Reg = divisor
+			c.To.Type = obj.TYPE_CONST
+			c.To.Offset = -1
+
+			j.To.Type = obj.TYPE_BRANCH
+
+		}
+
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = divisor
+		p.Reg = 0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = dividend
+
+		// signed division, rest of the check for -1 case
+		if j != nil {
+			j2 := gc.Prog(s390x.ABR)
+			j2.To.Type = obj.TYPE_BRANCH
+
+			var n *obj.Prog
+			if v.Op == ssa.OpS390XDIVD || v.Op == ssa.OpS390XDIVW {
+				// n * -1 = -n
+				n = gc.Prog(s390x.ANEG)
+				n.To.Type = obj.TYPE_REG
+				n.To.Reg = dividend
+			} else {
+				// n % -1 == 0
+				n = gc.Prog(s390x.AXOR)
+				n.From.Type = obj.TYPE_REG
+				n.From.Reg = dividend
+				n.To.Type = obj.TYPE_REG
+				n.To.Reg = dividend
+			}
+
+			j.To.Val = n
+			j2.To.Val = s.Pc()
+		}
+	case ssa.OpS390XADDconst, ssa.OpS390XADDWconst:
+		opregregimm(v.Op.Asm(), v.Reg(), v.Args[0].Reg(), v.AuxInt)
+	case ssa.OpS390XMULLDconst, ssa.OpS390XMULLWconst,
+		ssa.OpS390XSUBconst, ssa.OpS390XSUBWconst,
+		ssa.OpS390XANDconst, ssa.OpS390XANDWconst,
+		ssa.OpS390XORconst, ssa.OpS390XORWconst,
+		ssa.OpS390XXORconst, ssa.OpS390XXORWconst:
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.OpS390XSLDconst, ssa.OpS390XSLWconst,
+		ssa.OpS390XSRDconst, ssa.OpS390XSRWconst,
+		ssa.OpS390XSRADconst, ssa.OpS390XSRAWconst,
+		ssa.OpS390XRLLGconst, ssa.OpS390XRLLconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		r := v.Reg()
+		r1 := v.Args[0].Reg()
+		if r != r1 {
+			p.Reg = r1
+		}
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.OpS390XSUBEcarrymask, ssa.OpS390XSUBEWcarrymask:
+		r := v.Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.OpS390XMOVDaddridx:
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
+		p := gc.Prog(s390x.AMOVD)
+		p.From.Scale = 1
+		if i == s390x.REGSP {
+			r, i = i, r
+		}
+		p.From.Type = obj.TYPE_ADDR
+		p.From.Reg = r
+		p.From.Index = i
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpS390XMOVDaddr:
+		p := gc.Prog(s390x.AMOVD)
+		p.From.Type = obj.TYPE_ADDR
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpS390XCMP, ssa.OpS390XCMPW, ssa.OpS390XCMPU, ssa.OpS390XCMPWU:
+		opregreg(v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg())
+	case ssa.OpS390XFCMPS, ssa.OpS390XFCMP:
+		opregreg(v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg())
+	case ssa.OpS390XCMPconst, ssa.OpS390XCMPWconst, ssa.OpS390XCMPUconst, ssa.OpS390XCMPWUconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_CONST
+		p.To.Offset = v.AuxInt
+	case ssa.OpS390XMOVDconst:
+		x := v.Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x
+	case ssa.OpS390XFMOVSconst, ssa.OpS390XFMOVDconst:
+		x := v.Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_FCONST
+		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x
+	case ssa.OpS390XADDWload, ssa.OpS390XADDload,
+		ssa.OpS390XMULLWload, ssa.OpS390XMULLDload,
+		ssa.OpS390XSUBWload, ssa.OpS390XSUBload,
+		ssa.OpS390XANDWload, ssa.OpS390XANDload,
+		ssa.OpS390XORWload, ssa.OpS390XORload,
+		ssa.OpS390XXORWload, ssa.OpS390XXORload:
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[1].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.OpS390XMOVDload,
+		ssa.OpS390XMOVWZload, ssa.OpS390XMOVHZload, ssa.OpS390XMOVBZload,
+		ssa.OpS390XMOVDBRload, ssa.OpS390XMOVWBRload, ssa.OpS390XMOVHBRload,
+		ssa.OpS390XMOVBload, ssa.OpS390XMOVHload, ssa.OpS390XMOVWload,
+		ssa.OpS390XFMOVSload, ssa.OpS390XFMOVDload:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpS390XMOVBZloadidx, ssa.OpS390XMOVHZloadidx, ssa.OpS390XMOVWZloadidx, ssa.OpS390XMOVDloadidx,
+		ssa.OpS390XMOVHBRloadidx, ssa.OpS390XMOVWBRloadidx, ssa.OpS390XMOVDBRloadidx,
+		ssa.OpS390XFMOVSloadidx, ssa.OpS390XFMOVDloadidx:
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
+		if i == s390x.REGSP {
+			r, i = i, r
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = r
+		p.From.Scale = 1
+		p.From.Index = i
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpS390XMOVBstore, ssa.OpS390XMOVHstore, ssa.OpS390XMOVWstore, ssa.OpS390XMOVDstore,
+		ssa.OpS390XMOVHBRstore, ssa.OpS390XMOVWBRstore, ssa.OpS390XMOVDBRstore,
+		ssa.OpS390XFMOVSstore, ssa.OpS390XFMOVDstore:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpS390XMOVBstoreidx, ssa.OpS390XMOVHstoreidx, ssa.OpS390XMOVWstoreidx, ssa.OpS390XMOVDstoreidx,
+		ssa.OpS390XMOVHBRstoreidx, ssa.OpS390XMOVWBRstoreidx, ssa.OpS390XMOVDBRstoreidx,
+		ssa.OpS390XFMOVSstoreidx, ssa.OpS390XFMOVDstoreidx:
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
+		if i == s390x.REGSP {
+			r, i = i, r
+		}
+		p := gc.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 = r
+		p.To.Scale = 1
+		p.To.Index = i
+		gc.AddAux(&p.To, v)
+	case ssa.OpS390XMOVDstoreconst, ssa.OpS390XMOVWstoreconst, ssa.OpS390XMOVHstoreconst, ssa.OpS390XMOVBstoreconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		sc := v.AuxValAndOff()
+		p.From.Offset = sc.Val()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux2(&p.To, v, sc.Off())
+	case ssa.OpS390XMOVBreg, ssa.OpS390XMOVHreg, ssa.OpS390XMOVWreg,
+		ssa.OpS390XMOVBZreg, ssa.OpS390XMOVHZreg, ssa.OpS390XMOVWZreg,
+		ssa.OpS390XCEFBRA, ssa.OpS390XCDFBRA, ssa.OpS390XCEGBRA, ssa.OpS390XCDGBRA,
+		ssa.OpS390XCFEBRA, ssa.OpS390XCFDBRA, ssa.OpS390XCGEBRA, ssa.OpS390XCGDBRA,
+		ssa.OpS390XLDEBR, ssa.OpS390XLEDBR,
+		ssa.OpS390XFNEG, ssa.OpS390XFNEGS:
+		opregreg(v.Op.Asm(), v.Reg(), v.Args[0].Reg())
+	case ssa.OpS390XCLEAR:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		sc := v.AuxValAndOff()
+		p.From.Offset = sc.Val()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux2(&p.To, v, sc.Off())
+	case ssa.OpCopy, ssa.OpS390XMOVDconvert:
+		if v.Type.IsMemory() {
+			return
+		}
+		x := v.Args[0].Reg()
+		y := v.Reg()
+		if x != y {
+			opregreg(moveByType(v.Type), y, x)
+		}
+	case ssa.OpLoadReg:
+		if v.Type.IsFlags() {
+			v.Fatalf("load flags not implemented: %v", v.LongString())
+			return
+		}
+		p := gc.Prog(loadByType(v.Type))
+		gc.AddrAuto(&p.From, v.Args[0])
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.OpStoreReg:
+		if v.Type.IsFlags() {
+			v.Fatalf("store flags not implemented: %v", v.LongString())
+			return
+		}
+		p := gc.Prog(storeByType(v.Type))
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddrAuto(&p.To, v)
+	case ssa.OpPhi:
+		gc.CheckLoweredPhi(v)
+	case ssa.OpInitMem:
+		// memory arg needs no code
+	case ssa.OpArg:
+		// input args need no code
+	case ssa.OpS390XLoweredGetClosurePtr:
+		// Closure pointer is R12 (already)
+		gc.CheckLoweredGetClosurePtr(v)
+	case ssa.OpS390XLoweredGetG:
+		r := v.Reg()
+		p := gc.Prog(s390x.AMOVD)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = s390x.REGG
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.OpS390XCALLstatic:
+		if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
+			// Deferred calls will appear to be returning to
+			// the CALL deferreturn(SB) that we are about to emit.
+			// However, the stack trace code will show the line
+			// of the instruction byte before the return PC.
+			// To avoid that being an unrelated instruction,
+			// insert an actual hardware NOP that will have the right line number.
+			// This is different from obj.ANOP, which is a virtual no-op
+			// that doesn't make it into the instruction stream.
+			ginsnop()
+		}
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym))
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpS390XCALLclosure:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpS390XCALLdefer:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Deferproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpS390XCALLgo:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Newproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpS390XCALLinter:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.OpS390XFLOGR, ssa.OpS390XNEG, ssa.OpS390XNEGW,
+		ssa.OpS390XMOVWBR, ssa.OpS390XMOVDBR:
+		p := gc.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.OpS390XNOT, ssa.OpS390XNOTW:
+		v.Fatalf("NOT/NOTW generated %s", v.LongString())
+	case ssa.OpS390XMOVDEQ, ssa.OpS390XMOVDNE,
+		ssa.OpS390XMOVDLT, ssa.OpS390XMOVDLE,
+		ssa.OpS390XMOVDGT, ssa.OpS390XMOVDGE,
+		ssa.OpS390XMOVDGTnoinv, ssa.OpS390XMOVDGEnoinv:
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.OpS390XFSQRT:
+		p := gc.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.OpSP, ssa.OpSB:
+		// nothing to do
+	case ssa.OpSelect0, ssa.OpSelect1:
+		// nothing to do
+	case ssa.OpVarDef:
+		gc.Gvardef(v.Aux.(*gc.Node))
+	case ssa.OpVarKill:
+		gc.Gvarkill(v.Aux.(*gc.Node))
+	case ssa.OpVarLive:
+		gc.Gvarlive(v.Aux.(*gc.Node))
+	case ssa.OpKeepAlive:
+		gc.KeepAlive(v)
+	case ssa.OpS390XInvertFlags:
+		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
+	case ssa.OpS390XFlagEQ, ssa.OpS390XFlagLT, ssa.OpS390XFlagGT:
+		v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
+	case ssa.OpS390XAddTupleFirst32, ssa.OpS390XAddTupleFirst64:
+		v.Fatalf("AddTupleFirst* should never make it to codegen %v", v.LongString())
+	case ssa.OpS390XLoweredNilCheck:
+		// Issue a load which will fault if the input is nil.
+		p := gc.Prog(s390x.AMOVBZ)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = s390x.REGTMP
+		if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
+			gc.Warnl(v.Line, "generated nil check")
+		}
+	case ssa.OpS390XMVC:
+		vo := v.AuxValAndOff()
+		p := gc.Prog(s390x.AMVC)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[1].Reg()
+		p.From.Offset = vo.Off()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		p.To.Offset = vo.Off()
+		p.From3 = new(obj.Addr)
+		p.From3.Type = obj.TYPE_CONST
+		p.From3.Offset = vo.Val()
+	case ssa.OpS390XSTMG2, ssa.OpS390XSTMG3, ssa.OpS390XSTMG4,
+		ssa.OpS390XSTM2, ssa.OpS390XSTM3, ssa.OpS390XSTM4:
+		for i := 2; i < len(v.Args)-1; i++ {
+			if v.Args[i].Reg() != v.Args[i-1].Reg()+1 {
+				v.Fatalf("invalid store multiple %s", v.LongString())
+			}
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.Reg = v.Args[len(v.Args)-2].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpS390XLoweredMove:
+		// Inputs must be valid pointers to memory,
+		// so adjust arg0 and arg1 as part of the expansion.
+		// arg2 should be src+size,
+		//
+		// mvc: MVC  $256, 0(R2), 0(R1)
+		//      MOVD $256(R1), R1
+		//      MOVD $256(R2), R2
+		//      CMP  R2, Rarg2
+		//      BNE  mvc
+		//      MVC  $rem, 0(R2), 0(R1) // if rem > 0
+		// arg2 is the last address to move in the loop + 256
+		mvc := gc.Prog(s390x.AMVC)
+		mvc.From.Type = obj.TYPE_MEM
+		mvc.From.Reg = v.Args[1].Reg()
+		mvc.To.Type = obj.TYPE_MEM
+		mvc.To.Reg = v.Args[0].Reg()
+		mvc.From3 = new(obj.Addr)
+		mvc.From3.Type = obj.TYPE_CONST
+		mvc.From3.Offset = 256
+
+		for i := 0; i < 2; i++ {
+			movd := gc.Prog(s390x.AMOVD)
+			movd.From.Type = obj.TYPE_ADDR
+			movd.From.Reg = v.Args[i].Reg()
+			movd.From.Offset = 256
+			movd.To.Type = obj.TYPE_REG
+			movd.To.Reg = v.Args[i].Reg()
+		}
+
+		cmpu := gc.Prog(s390x.ACMPU)
+		cmpu.From.Reg = v.Args[1].Reg()
+		cmpu.From.Type = obj.TYPE_REG
+		cmpu.To.Reg = v.Args[2].Reg()
+		cmpu.To.Type = obj.TYPE_REG
+
+		bne := gc.Prog(s390x.ABLT)
+		bne.To.Type = obj.TYPE_BRANCH
+		gc.Patch(bne, mvc)
+
+		if v.AuxInt > 0 {
+			mvc := gc.Prog(s390x.AMVC)
+			mvc.From.Type = obj.TYPE_MEM
+			mvc.From.Reg = v.Args[1].Reg()
+			mvc.To.Type = obj.TYPE_MEM
+			mvc.To.Reg = v.Args[0].Reg()
+			mvc.From3 = new(obj.Addr)
+			mvc.From3.Type = obj.TYPE_CONST
+			mvc.From3.Offset = v.AuxInt
+		}
+	case ssa.OpS390XLoweredZero:
+		// Input must be valid pointers to memory,
+		// so adjust arg0 as part of the expansion.
+		// arg1 should be src+size,
+		//
+		// clear: CLEAR $256, 0(R1)
+		//        MOVD  $256(R1), R1
+		//        CMP   R1, Rarg1
+		//        BNE   clear
+		//        CLEAR $rem, 0(R1) // if rem > 0
+		// arg1 is the last address to zero in the loop + 256
+		clear := gc.Prog(s390x.ACLEAR)
+		clear.From.Type = obj.TYPE_CONST
+		clear.From.Offset = 256
+		clear.To.Type = obj.TYPE_MEM
+		clear.To.Reg = v.Args[0].Reg()
+
+		movd := gc.Prog(s390x.AMOVD)
+		movd.From.Type = obj.TYPE_ADDR
+		movd.From.Reg = v.Args[0].Reg()
+		movd.From.Offset = 256
+		movd.To.Type = obj.TYPE_REG
+		movd.To.Reg = v.Args[0].Reg()
+
+		cmpu := gc.Prog(s390x.ACMPU)
+		cmpu.From.Reg = v.Args[0].Reg()
+		cmpu.From.Type = obj.TYPE_REG
+		cmpu.To.Reg = v.Args[1].Reg()
+		cmpu.To.Type = obj.TYPE_REG
+
+		bne := gc.Prog(s390x.ABLT)
+		bne.To.Type = obj.TYPE_BRANCH
+		gc.Patch(bne, clear)
+
+		if v.AuxInt > 0 {
+			clear := gc.Prog(s390x.ACLEAR)
+			clear.From.Type = obj.TYPE_CONST
+			clear.From.Offset = v.AuxInt
+			clear.To.Type = obj.TYPE_MEM
+			clear.To.Reg = v.Args[0].Reg()
+		}
+	case ssa.OpS390XMOVWZatomicload, ssa.OpS390XMOVDatomicload:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg0()
+	case ssa.OpS390XMOVWatomicstore, ssa.OpS390XMOVDatomicstore:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpS390XLAA, ssa.OpS390XLAAG:
+		p := gc.Prog(v.Op.Asm())
+		p.Reg = v.Reg0()
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.OpS390XLoweredAtomicCas32, ssa.OpS390XLoweredAtomicCas64:
+		// Convert the flags output of CS{,G} into a bool.
+		//    CS{,G} arg1, arg2, arg0
+		//    MOVD   $0, ret
+		//    BNE    2(PC)
+		//    MOVD   $1, ret
+		//    NOP (so the BNE has somewhere to land)
+
+		// CS{,G} arg1, arg2, arg0
+		cs := gc.Prog(v.Op.Asm())
+		cs.From.Type = obj.TYPE_REG
+		cs.From.Reg = v.Args[1].Reg() // old
+		cs.Reg = v.Args[2].Reg()      // new
+		cs.To.Type = obj.TYPE_MEM
+		cs.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&cs.To, v)
+
+		// MOVD $0, ret
+		movd := gc.Prog(s390x.AMOVD)
+		movd.From.Type = obj.TYPE_CONST
+		movd.From.Offset = 0
+		movd.To.Type = obj.TYPE_REG
+		movd.To.Reg = v.Reg0()
+
+		// BNE 2(PC)
+		bne := gc.Prog(s390x.ABNE)
+		bne.To.Type = obj.TYPE_BRANCH
+
+		// MOVD $1, ret
+		movd = gc.Prog(s390x.AMOVD)
+		movd.From.Type = obj.TYPE_CONST
+		movd.From.Offset = 1
+		movd.To.Type = obj.TYPE_REG
+		movd.To.Reg = v.Reg0()
+
+		// NOP (so the BNE has somewhere to land)
+		nop := gc.Prog(obj.ANOP)
+		gc.Patch(bne, nop)
+	case ssa.OpS390XLoweredAtomicExchange32, ssa.OpS390XLoweredAtomicExchange64:
+		// Loop until the CS{,G} succeeds.
+		//     MOV{WZ,D} arg0, ret
+		// cs: CS{,G}    ret, arg1, arg0
+		//     BNE       cs
+
+		// MOV{WZ,D} arg0, ret
+		load := gc.Prog(loadByType(v.Type.FieldType(0)))
+		load.From.Type = obj.TYPE_MEM
+		load.From.Reg = v.Args[0].Reg()
+		load.To.Type = obj.TYPE_REG
+		load.To.Reg = v.Reg0()
+		gc.AddAux(&load.From, v)
+
+		// CS{,G} ret, arg1, arg0
+		cs := gc.Prog(v.Op.Asm())
+		cs.From.Type = obj.TYPE_REG
+		cs.From.Reg = v.Reg0()   // old
+		cs.Reg = v.Args[1].Reg() // new
+		cs.To.Type = obj.TYPE_MEM
+		cs.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&cs.To, v)
+
+		// BNE cs
+		bne := gc.Prog(s390x.ABNE)
+		bne.To.Type = obj.TYPE_BRANCH
+		gc.Patch(bne, cs)
+	default:
+		v.Fatalf("genValue not implemented: %s", v.LongString())
+	}
+}
+
+var blockJump = [...]struct {
+	asm, invasm obj.As
+}{
+	ssa.BlockS390XEQ:  {s390x.ABEQ, s390x.ABNE},
+	ssa.BlockS390XNE:  {s390x.ABNE, s390x.ABEQ},
+	ssa.BlockS390XLT:  {s390x.ABLT, s390x.ABGE},
+	ssa.BlockS390XGE:  {s390x.ABGE, s390x.ABLT},
+	ssa.BlockS390XLE:  {s390x.ABLE, s390x.ABGT},
+	ssa.BlockS390XGT:  {s390x.ABGT, s390x.ABLE},
+	ssa.BlockS390XGTF: {s390x.ABGT, s390x.ABLEU},
+	ssa.BlockS390XGEF: {s390x.ABGE, s390x.ABLTU},
+}
+
+func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+	s.SetLineno(b.Line)
+
+	switch b.Kind {
+	case ssa.BlockPlain:
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(s390x.ABR)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+	case ssa.BlockDefer:
+		// defer returns in R3:
+		// 0 if we should continue executing
+		// 1 if we should jump to deferreturn call
+		p := gc.Prog(s390x.ACMPW)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = s390x.REG_R3
+		p.To.Type = obj.TYPE_CONST
+		p.To.Offset = 0
+		p = gc.Prog(s390x.ABNE)
+		p.To.Type = obj.TYPE_BRANCH
+		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(s390x.ABR)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+	case ssa.BlockExit:
+		gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
+	case ssa.BlockRet:
+		gc.Prog(obj.ARET)
+	case ssa.BlockRetJmp:
+		p := gc.Prog(s390x.ABR)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
+	case ssa.BlockS390XEQ, ssa.BlockS390XNE,
+		ssa.BlockS390XLT, ssa.BlockS390XGE,
+		ssa.BlockS390XLE, ssa.BlockS390XGT,
+		ssa.BlockS390XGEF, ssa.BlockS390XGTF:
+		jmp := blockJump[b.Kind]
+		likely := b.Likely
+		var p *obj.Prog
+		switch next {
+		case b.Succs[0].Block():
+			p = gc.Prog(jmp.invasm)
+			likely *= -1
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		case b.Succs[1].Block():
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		default:
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+			q := gc.Prog(s390x.ABR)
+			q.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
+		}
+	default:
+		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
+	}
+}
diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go
index 77f8306..e1c2f6d 100644
--- a/src/cmd/compile/internal/ssa/block.go
+++ b/src/cmd/compile/internal/ssa/block.go
@@ -89,13 +89,16 @@ type Edge struct {
 func (e Edge) Block() *Block {
 	return e.b
 }
+func (e Edge) Index() int {
+	return e.i
+}
 
 //     kind           control    successors
 //   ------------------------------------------
 //     Exit        return mem                []
 //    Plain               nil            [next]
 //       If   a boolean Value      [then, else]
-//     Call               mem  [nopanic, panic]  (control opcode should be OpCall or OpStaticCall)
+//    Defer               mem  [nopanic, panic]  (control opcode should be OpDeferCall)
 type BlockKind int8
 
 // short form print
@@ -144,6 +147,7 @@ func (b *Block) AddEdgeTo(c *Block) {
 	j := len(c.Preds)
 	b.Succs = append(b.Succs, Edge{c, j})
 	c.Preds = append(c.Preds, Edge{b, i})
+	b.Func.invalidateCFG()
 }
 
 // removePred removes the ith input edge from b.
@@ -159,6 +163,7 @@ func (b *Block) removePred(i int) {
 	}
 	b.Preds[n] = Edge{}
 	b.Preds = b.Preds[:n]
+	b.Func.invalidateCFG()
 }
 
 // removeSucc removes the ith output edge from b.
@@ -174,6 +179,7 @@ func (b *Block) removeSucc(i int) {
 	}
 	b.Succs[n] = Edge{}
 	b.Succs = b.Succs[:n]
+	b.Func.invalidateCFG()
 }
 
 func (b *Block) swapSuccessors() {
@@ -189,10 +195,9 @@ func (b *Block) swapSuccessors() {
 	b.Likely *= -1
 }
 
-func (b *Block) Logf(msg string, args ...interface{})           { b.Func.Logf(msg, args...) }
-func (b *Block) Log() bool                                      { return b.Func.Log() }
-func (b *Block) Fatalf(msg string, args ...interface{})         { b.Func.Fatalf(msg, args...) }
-func (b *Block) Unimplementedf(msg string, args ...interface{}) { b.Func.Unimplementedf(msg, args...) }
+func (b *Block) Logf(msg string, args ...interface{})   { b.Func.Logf(msg, args...) }
+func (b *Block) Log() bool                              { return b.Func.Log() }
+func (b *Block) Fatalf(msg string, args ...interface{}) { b.Func.Fatalf(msg, args...) }
 
 type BranchPrediction int8
 
diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go
index bfedd47..d78e915 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -80,16 +80,6 @@ func checkFunc(f *Func) {
 			if !b.Control.Type.IsBoolean() {
 				f.Fatalf("if block %s has non-bool control value %s", b, b.Control.LongString())
 			}
-		case BlockCall:
-			if len(b.Succs) != 1 {
-				f.Fatalf("call block %s len(Succs)==%d, want 1", b, len(b.Succs))
-			}
-			if b.Control == nil {
-				f.Fatalf("call block %s has no control value", b)
-			}
-			if !b.Control.Type.IsMemory() {
-				f.Fatalf("call block %s has non-memory control value %s", b, b.Control.LongString())
-			}
 		case BlockDefer:
 			if len(b.Succs) != 2 {
 				f.Fatalf("defer block %s len(Succs)==%d, want 2", b, len(b.Succs))
@@ -100,16 +90,6 @@ func checkFunc(f *Func) {
 			if !b.Control.Type.IsMemory() {
 				f.Fatalf("defer block %s has non-memory control value %s", b, b.Control.LongString())
 			}
-		case BlockCheck:
-			if len(b.Succs) != 1 {
-				f.Fatalf("check block %s len(Succs)==%d, want 1", b, len(b.Succs))
-			}
-			if b.Control == nil {
-				f.Fatalf("check block %s has no control value", b)
-			}
-			if !b.Control.Type.IsVoid() {
-				f.Fatalf("check block %s has non-void control value %s", b, b.Control.LongString())
-			}
 		case BlockFirst:
 			if len(b.Succs) != 2 {
 				f.Fatalf("plain/dead block %s len(Succs)==%d, want 2", b, len(b.Succs))
@@ -165,9 +145,11 @@ func checkFunc(f *Func) {
 				if !isExactFloat32(v) {
 					f.Fatalf("value %v has an AuxInt value that is not an exact float32", v)
 				}
+			case auxSizeAndAlign:
+				canHaveAuxInt = true
 			case auxString, auxSym:
 				canHaveAux = true
-			case auxSymOff, auxSymValAndOff:
+			case auxSymOff, auxSymValAndOff, auxSymSizeAndAlign:
 				canHaveAuxInt = true
 				canHaveAux = true
 			case auxSymInt32:
@@ -273,8 +255,7 @@ func checkFunc(f *Func) {
 	if f.RegAlloc == nil {
 		// Note: regalloc introduces non-dominating args.
 		// See TODO in regalloc.go.
-		idom := dominators(f)
-		sdom := newSparseTree(f, idom)
+		sdom := f.sdom()
 		for _, b := range f.Blocks {
 			for _, v := range b.Values {
 				for i, arg := range v.Args {
diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
index d8b0b0a..b9ec7eb 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -7,6 +7,7 @@ package ssa
 import (
 	"fmt"
 	"log"
+	"os"
 	"regexp"
 	"runtime"
 	"strings"
@@ -41,6 +42,9 @@ func Compile(f *Func) {
 	// Run all the passes
 	printFunc(f)
 	f.Config.HTML.WriteFunc("start", f)
+	if BuildDump != "" && BuildDump == f.Name {
+		f.dumpFile("build")
+	}
 	if checkEnabled {
 		checkFunc(f)
 	}
@@ -96,6 +100,10 @@ func Compile(f *Func) {
 				f.LogStat("TIME(ns):BYTES:ALLOCS", time, nBytes, nAllocs)
 			}
 		}
+		if p.dump != nil && p.dump[f.Name] {
+			// Dump function to appropriately named file
+			f.dumpFile(phaseName)
+		}
 		if checkEnabled {
 			checkFunc(f)
 		}
@@ -105,16 +113,48 @@ func Compile(f *Func) {
 	phaseName = ""
 }
 
+// TODO: should be a config field
+var dumpFileSeq int
+
+// dumpFile creates a file from the phase name and function name
+// Dumping is done to files to avoid buffering huge strings before
+// output.
+func (f *Func) dumpFile(phaseName string) {
+	dumpFileSeq++
+	fname := fmt.Sprintf("%s__%s_%d.dump", phaseName, f.Name, dumpFileSeq)
+	fname = strings.Replace(fname, " ", "_", -1)
+	fname = strings.Replace(fname, "/", "_", -1)
+	fname = strings.Replace(fname, ":", "_", -1)
+
+	fi, err := os.Create(fname)
+	if err != nil {
+		f.Config.Warnl(0, "Unable to create after-phase dump file %s", fname)
+		return
+	}
+
+	p := stringFuncPrinter{w: fi}
+	fprintFunc(p, f)
+	fi.Close()
+}
+
 type pass struct {
 	name     string
 	fn       func(*Func)
 	required bool
 	disabled bool
-	time     bool // report time to run pass
-	mem      bool // report mem stats to run pass
-	stats    int  // pass reports own "stats" (e.g., branches removed)
-	debug    int  // pass performs some debugging. =1 should be in error-testing-friendly Warnl format.
-	test     int  // pass-specific ad-hoc option, perhaps useful in development
+	time     bool            // report time to run pass
+	mem      bool            // report mem stats to run pass
+	stats    int             // pass reports own "stats" (e.g., branches removed)
+	debug    int             // pass performs some debugging. =1 should be in error-testing-friendly Warnl format.
+	test     int             // pass-specific ad-hoc option, perhaps useful in development
+	dump     map[string]bool // dump if function name matches
+}
+
+func (p *pass) addDump(s string) {
+	if p.dump == nil {
+		p.dump = make(map[string]bool)
+	}
+	p.dump[s] = true
 }
 
 // Run consistency checker between each phase
@@ -127,6 +167,7 @@ var IntrinsicsDisable bool
 var BuildDebug int
 var BuildTest int
 var BuildStats int
+var BuildDump string // name of function to dump after initial build of ssa
 
 // PhaseOption sets the specified flag in the specified ssa phase,
 // returning empty string if this was successful or a string explaining
@@ -146,7 +187,35 @@ var BuildStats int
 //
 // BOOT_GO_GCFLAGS=-d='ssa/~^.*scc$/off' GO_GCFLAGS='-d=ssa/~^.*scc$/off' ./make.bash
 //
-func PhaseOption(phase, flag string, val int) string {
+func PhaseOption(phase, flag string, val int, valString string) string {
+	if phase == "help" {
+		lastcr := 0
+		phasenames := "check, all, build, intrinsics"
+		for _, p := range passes {
+			pn := strings.Replace(p.name, " ", "_", -1)
+			if len(pn)+len(phasenames)-lastcr > 70 {
+				phasenames += "\n"
+				lastcr = len(phasenames)
+				phasenames += pn
+			} else {
+				phasenames += ", " + pn
+			}
+		}
+		return "" +
+			`GcFlag -d=ssa/<phase>/<flag>[=<value>]|[:<function_name>]
+<phase> is one of:
+` + phasenames + `
+<flag> is one of on, off, debug, mem, time, test, stats, dump
+<value> defaults to 1
+<function_name> is required for "dump", specifies name of function to dump after <phase>
+Except for dump, output is directed to standard out; dump appears in a file.
+Phase "all" supports flags "time", "mem", and "dump".
+Phases "intrinsics" supports flags "on", "off", and "debug".
+Interpretation of the "debug" value depends on the phase.
+Dump files are named <phase>__<function_name>_<seq>.dump.
+`
+	}
+
 	if phase == "check" && flag == "on" {
 		checkEnabled = val != 0
 		return ""
@@ -157,9 +226,18 @@ func PhaseOption(phase, flag string, val int) string {
 	}
 
 	alltime := false
+	allmem := false
+	alldump := false
 	if phase == "all" {
 		if flag == "time" {
 			alltime = val != 0
+		} else if flag == "mem" {
+			allmem = val != 0
+		} else if flag == "dump" {
+			alldump = val != 0
+			if alldump {
+				BuildDump = valString
+			}
 		} else {
 			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
 		}
@@ -186,6 +264,8 @@ func PhaseOption(phase, flag string, val int) string {
 			BuildTest = val
 		case "stats":
 			BuildStats = val
+		case "dump":
+			BuildDump = valString
 		default:
 			return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
 		}
@@ -205,6 +285,10 @@ func PhaseOption(phase, flag string, val int) string {
 	for i, p := range passes {
 		if phase == "all" {
 			p.time = alltime
+			p.mem = allmem
+			if alldump {
+				p.addDump(valString)
+			}
 			passes[i] = p
 			matchedOne = true
 		} else if p.name == phase || p.name == underphase || re != nil && re.MatchString(p.name) {
@@ -223,6 +307,8 @@ func PhaseOption(phase, flag string, val int) string {
 				p.stats = val
 			case "test":
 				p.test = val
+			case "dump":
+				p.addDump(valString)
 			default:
 				return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
 			}
@@ -250,7 +336,6 @@ var passes = [...]pass{
 	{name: "opt", fn: opt, required: true},               // TODO: split required rules and optimizing rules
 	{name: "zero arg cse", fn: zcse, required: true},     // required to merge OpSB values
 	{name: "opt deadcode", fn: deadcode, required: true}, // remove any blocks orphaned during opt
-	{name: "generic domtree", fn: domTree},
 	{name: "generic cse", fn: cse},
 	{name: "phiopt", fn: phiopt},
 	{name: "nilcheckelim", fn: nilcheckelim},
@@ -261,6 +346,7 @@ var passes = [...]pass{
 	{name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
 	{name: "generic deadcode", fn: deadcode},
 	{name: "check bce", fn: checkbce},
+	{name: "writebarrier", fn: writebarrier, required: true}, // expand write barrier ops
 	{name: "fuse", fn: fuse},
 	{name: "dse", fn: dse},
 	{name: "tighten", fn: tighten}, // move values closer to their uses
@@ -274,11 +360,13 @@ var passes = [...]pass{
 	{name: "late deadcode", fn: deadcode},
 	{name: "critical", fn: critical, required: true}, // remove critical edges
 	{name: "likelyadjust", fn: likelyadjust},
-	{name: "layout", fn: layout, required: true},       // schedule blocks
-	{name: "schedule", fn: schedule, required: true},   // schedule values
+	{name: "layout", fn: layout, required: true},     // schedule blocks
+	{name: "schedule", fn: schedule, required: true}, // schedule values
+	{name: "late nilcheck", fn: nilcheckelim2},
 	{name: "flagalloc", fn: flagalloc, required: true}, // allocate flags register
 	{name: "regalloc", fn: regalloc, required: true},   // allocate int & float registers + stack slots
-	{name: "trim", fn: trim},                           // remove empty blocks
+	{name: "stackframe", fn: stackframe, required: true},
+	{name: "trim", fn: trim}, // remove empty blocks
 }
 
 // Double-check phase ordering constraints.
@@ -307,12 +395,6 @@ var passOrder = [...]constraint{
 	{"opt", "nilcheckelim"},
 	// tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET
 	{"tighten", "lower"},
-	// cse, phiopt, nilcheckelim, prove and loopbce share idom.
-	{"generic domtree", "generic cse"},
-	{"generic domtree", "phiopt"},
-	{"generic domtree", "nilcheckelim"},
-	{"generic domtree", "prove"},
-	{"generic domtree", "loopbce"},
 	// tighten will be most effective when as many values have been removed as possible
 	{"generic deadcode", "tighten"},
 	{"generic cse", "tighten"},
@@ -329,10 +411,14 @@ var passOrder = [...]constraint{
 	// checkLower must run after lowering & subsequent dead code elim
 	{"lower", "checkLower"},
 	{"lowered deadcode", "checkLower"},
+	// late nilcheck needs instructions to be scheduled.
+	{"schedule", "late nilcheck"},
 	// flagalloc needs instructions to be scheduled.
 	{"schedule", "flagalloc"},
 	// regalloc needs flags to be allocated first.
 	{"flagalloc", "regalloc"},
+	// stackframe needs to know about spilled registers.
+	{"regalloc", "stackframe"},
 	// trim needs regalloc to be done first.
 	{"regalloc", "trim"},
 }
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index e8ab178..919386e 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -17,14 +17,27 @@ type Config struct {
 	arch            string                     // "amd64", etc.
 	IntSize         int64                      // 4 or 8
 	PtrSize         int64                      // 4 or 8
-	lowerBlock      func(*Block) bool          // lowering function
+	RegSize         int64                      // 4 or 8
+	lowerBlock      func(*Block, *Config) bool // lowering function
 	lowerValue      func(*Value, *Config) bool // lowering function
 	registers       []Register                 // machine registers
+	gpRegMask       regMask                    // general purpose integer register mask
+	fpRegMask       regMask                    // floating point register mask
+	specialRegMask  regMask                    // special register mask
+	FPReg           int8                       // register number of frame pointer, -1 if not used
+	LinkReg         int8                       // register number of link register if it is a general purpose register, -1 if not used
+	hasGReg         bool                       // has hardware g register
 	fe              Frontend                   // callbacks into compiler frontend
 	HTML            *HTMLWriter                // html writer, for debugging
 	ctxt            *obj.Link                  // Generic arch information
 	optimize        bool                       // Do optimization
 	noDuffDevice    bool                       // Don't use Duff's device
+	nacl            bool                       // GOOS=nacl
+	use387          bool                       // GO386=387
+	OldArch         bool                       // True for older versions of architecture, e.g. true for PPC64BE, false for PPC64LE
+	NeedsFpScratch  bool                       // No direct move between GP and FP register sets
+	BigEndian       bool                       //
+	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
 	sparsePhiCutoff uint64                     // Sparse phi location algorithm used above this #blocks*#variables score
 	curFunc         *Func
 
@@ -77,15 +90,12 @@ type Logger interface {
 	// Fatal reports a compiler error and exits.
 	Fatalf(line int32, msg string, args ...interface{})
 
-	// Unimplemented reports that the function cannot be compiled.
-	// It will be removed once SSA work is complete.
-	Unimplementedf(line int32, msg string, args ...interface{})
-
 	// Warnl writes compiler messages in the form expected by "errorcheck" tests
 	Warnl(line int32, fmt_ string, args ...interface{})
 
-	// Fowards the Debug_checknil flag from gc
+	// Fowards the Debug flags from gc
 	Debug_checknil() bool
+	Debug_wb() bool
 }
 
 type Frontend interface {
@@ -106,9 +116,18 @@ type Frontend interface {
 	SplitSlice(LocalSlot) (LocalSlot, LocalSlot, LocalSlot)
 	SplitComplex(LocalSlot) (LocalSlot, LocalSlot)
 	SplitStruct(LocalSlot, int) LocalSlot
+	SplitArray(LocalSlot) LocalSlot              // array must be length 1
+	SplitInt64(LocalSlot) (LocalSlot, LocalSlot) // returns (hi, lo)
 
 	// Line returns a string describing the given line number.
 	Line(int32) string
+
+	// AllocFrame assigns frame offsets to all live auto variables.
+	AllocFrame(f *Func)
+
+	// Syslook returns a symbol of the runtime function/variable with the
+	// given name.
+	Syslook(string) interface{} // returns *gc.Sym
 }
 
 // interface used to hold *gc.Node. We'd use *gc.Node directly but
@@ -125,32 +144,150 @@ func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config
 	case "amd64":
 		c.IntSize = 8
 		c.PtrSize = 8
+		c.RegSize = 8
 		c.lowerBlock = rewriteBlockAMD64
 		c.lowerValue = rewriteValueAMD64
 		c.registers = registersAMD64[:]
-	case "386":
+		c.gpRegMask = gpRegMaskAMD64
+		c.fpRegMask = fpRegMaskAMD64
+		c.FPReg = framepointerRegAMD64
+		c.LinkReg = linkRegAMD64
+		c.hasGReg = false
+	case "amd64p32":
 		c.IntSize = 4
 		c.PtrSize = 4
+		c.RegSize = 8
 		c.lowerBlock = rewriteBlockAMD64
-		c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support
+		c.lowerValue = rewriteValueAMD64
+		c.registers = registersAMD64[:]
+		c.gpRegMask = gpRegMaskAMD64
+		c.fpRegMask = fpRegMaskAMD64
+		c.FPReg = framepointerRegAMD64
+		c.LinkReg = linkRegAMD64
+		c.hasGReg = false
+		c.noDuffDevice = true
+	case "386":
+		c.IntSize = 4
+		c.PtrSize = 4
+		c.RegSize = 4
+		c.lowerBlock = rewriteBlock386
+		c.lowerValue = rewriteValue386
+		c.registers = registers386[:]
+		c.gpRegMask = gpRegMask386
+		c.fpRegMask = fpRegMask386
+		c.FPReg = framepointerReg386
+		c.LinkReg = linkReg386
+		c.hasGReg = false
 	case "arm":
 		c.IntSize = 4
 		c.PtrSize = 4
+		c.RegSize = 4
 		c.lowerBlock = rewriteBlockARM
 		c.lowerValue = rewriteValueARM
 		c.registers = registersARM[:]
+		c.gpRegMask = gpRegMaskARM
+		c.fpRegMask = fpRegMaskARM
+		c.FPReg = framepointerRegARM
+		c.LinkReg = linkRegARM
+		c.hasGReg = true
+	case "arm64":
+		c.IntSize = 8
+		c.PtrSize = 8
+		c.RegSize = 8
+		c.lowerBlock = rewriteBlockARM64
+		c.lowerValue = rewriteValueARM64
+		c.registers = registersARM64[:]
+		c.gpRegMask = gpRegMaskARM64
+		c.fpRegMask = fpRegMaskARM64
+		c.FPReg = framepointerRegARM64
+		c.LinkReg = linkRegARM64
+		c.hasGReg = true
+		c.noDuffDevice = obj.GOOS == "darwin" // darwin linker cannot handle BR26 reloc with non-zero addend
+	case "ppc64":
+		c.OldArch = true
+		c.BigEndian = true
+		fallthrough
+	case "ppc64le":
+		c.IntSize = 8
+		c.PtrSize = 8
+		c.RegSize = 8
+		c.lowerBlock = rewriteBlockPPC64
+		c.lowerValue = rewriteValuePPC64
+		c.registers = registersPPC64[:]
+		c.gpRegMask = gpRegMaskPPC64
+		c.fpRegMask = fpRegMaskPPC64
+		c.FPReg = framepointerRegPPC64
+		c.LinkReg = linkRegPPC64
+		c.noDuffDevice = true // TODO: Resolve PPC64 DuffDevice (has zero, but not copy)
+		c.NeedsFpScratch = true
+		c.hasGReg = true
+	case "mips64":
+		c.BigEndian = true
+		fallthrough
+	case "mips64le":
+		c.IntSize = 8
+		c.PtrSize = 8
+		c.RegSize = 8
+		c.lowerBlock = rewriteBlockMIPS64
+		c.lowerValue = rewriteValueMIPS64
+		c.registers = registersMIPS64[:]
+		c.gpRegMask = gpRegMaskMIPS64
+		c.fpRegMask = fpRegMaskMIPS64
+		c.specialRegMask = specialRegMaskMIPS64
+		c.FPReg = framepointerRegMIPS64
+		c.LinkReg = linkRegMIPS64
+		c.hasGReg = true
+	case "s390x":
+		c.IntSize = 8
+		c.PtrSize = 8
+		c.RegSize = 8
+		c.lowerBlock = rewriteBlockS390X
+		c.lowerValue = rewriteValueS390X
+		c.registers = registersS390X[:]
+		c.gpRegMask = gpRegMaskS390X
+		c.fpRegMask = fpRegMaskS390X
+		c.FPReg = framepointerRegS390X
+		c.LinkReg = linkRegS390X
+		c.hasGReg = true
+		c.noDuffDevice = true
+		c.BigEndian = true
+	case "mips":
+		c.BigEndian = true
+		fallthrough
+	case "mipsle":
+		c.IntSize = 4
+		c.PtrSize = 4
+		c.RegSize = 4
+		c.lowerBlock = rewriteBlockMIPS
+		c.lowerValue = rewriteValueMIPS
+		c.registers = registersMIPS[:]
+		c.gpRegMask = gpRegMaskMIPS
+		c.fpRegMask = fpRegMaskMIPS
+		c.specialRegMask = specialRegMaskMIPS
+		c.FPReg = framepointerRegMIPS
+		c.LinkReg = linkRegMIPS
+		c.hasGReg = true
+		c.noDuffDevice = true
 	default:
-		fe.Unimplementedf(0, "arch %s not implemented", arch)
+		fe.Fatalf(0, "arch %s not implemented", arch)
 	}
 	c.ctxt = ctxt
 	c.optimize = optimize
+	c.nacl = obj.GOOS == "nacl"
 
-	// Don't use Duff's device on Plan 9, because floating
+	// Don't use Duff's device on Plan 9 AMD64, because floating
 	// point operations are not allowed in note handler.
-	if obj.Getgoos() == "plan9" {
+	if obj.GOOS == "plan9" && arch == "amd64" {
 		c.noDuffDevice = true
 	}
 
+	if c.nacl {
+		c.noDuffDevice = true // Don't use Duff's device on NaCl
+
+		// runtime call clobber R12 on nacl
+		opcodeTable[OpARMUDIVrtcall].reg.clobbers |= 1 << 12 // R12
+	}
+
 	// Assign IDs to preallocated values/blocks.
 	for i := range c.values {
 		c.values[i].ID = ID(i)
@@ -180,8 +317,14 @@ func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config
 	return c
 }
 
+func (c *Config) Set387(b bool) {
+	c.NeedsFpScratch = b
+	c.use387 = b
+}
+
 func (c *Config) Frontend() Frontend      { return c.fe }
 func (c *Config) SparsePhiCutoff() uint64 { return c.sparsePhiCutoff }
+func (c *Config) Ctxt() *obj.Link         { return c.ctxt }
 
 // NewFunc returns a new, empty function object.
 // Caller must call f.Free() before calling NewFunc again.
@@ -198,11 +341,9 @@ func (c *Config) NewFunc() *Func {
 func (c *Config) Logf(msg string, args ...interface{})               { c.fe.Logf(msg, args...) }
 func (c *Config) Log() bool                                          { return c.fe.Log() }
 func (c *Config) Fatalf(line int32, msg string, args ...interface{}) { c.fe.Fatalf(line, msg, args...) }
-func (c *Config) Unimplementedf(line int32, msg string, args ...interface{}) {
-	c.fe.Unimplementedf(line, msg, args...)
-}
-func (c *Config) Warnl(line int32, msg string, args ...interface{}) { c.fe.Warnl(line, msg, args...) }
-func (c *Config) Debug_checknil() bool                              { return c.fe.Debug_checknil() }
+func (c *Config) Warnl(line int32, msg string, args ...interface{})  { c.fe.Warnl(line, msg, args...) }
+func (c *Config) Debug_checknil() bool                               { return c.fe.Debug_checknil() }
+func (c *Config) Debug_wb() bool                                     { return c.fe.Debug_wb() }
 
 func (c *Config) logDebugHashMatch(evname, name string) {
 	file := c.logfiles[evname]
diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go
index ad4e416..4e07c89 100644
--- a/src/cmd/compile/internal/ssa/cse.go
+++ b/src/cmd/compile/internal/ssa/cse.go
@@ -9,10 +9,6 @@ import (
 	"sort"
 )
 
-const (
-	cmpDepth = 4
-)
-
 // cse does common-subexpression elimination on the Function.
 // Values are just relinked, nothing is deleted. A subsequent deadcode
 // pass is required to actually remove duplicate expressions.
@@ -60,7 +56,8 @@ func cse(f *Func) {
 			valueEqClass[v.ID] = -v.ID
 		}
 	}
-	for i, e := range partition {
+	var pNum ID = 1
+	for _, e := range partition {
 		if f.pass.debug > 1 && len(e) > 500 {
 			fmt.Printf("CSE.large partition (%d): ", len(e))
 			for j := 0; j < 3; j++ {
@@ -70,20 +67,23 @@ func cse(f *Func) {
 		}
 
 		for _, v := range e {
-			valueEqClass[v.ID] = ID(i)
+			valueEqClass[v.ID] = pNum
 		}
 		if f.pass.debug > 2 && len(e) > 1 {
-			fmt.Printf("CSE.partition #%d:", i)
+			fmt.Printf("CSE.partition #%d:", pNum)
 			for _, v := range e {
 				fmt.Printf(" %s", v.String())
 			}
 			fmt.Printf("\n")
 		}
+		pNum++
 	}
 
-	// Find an equivalence class where some members of the class have
-	// non-equivalent arguments. Split the equivalence class appropriately.
-	// Repeat until we can't find any more splits.
+	// Split equivalence classes at points where they have
+	// non-equivalent arguments.  Repeat until we can't find any
+	// more splits.
+	var splitPoints []int
+	byArgClass := new(partitionByArgClass) // reuseable partitionByArgClass to reduce allocations
 	for {
 		changed := false
 
@@ -91,39 +91,53 @@ func cse(f *Func) {
 		// we process new additions as they arrive, avoiding O(n^2) behavior.
 		for i := 0; i < len(partition); i++ {
 			e := partition[i]
-			v := e[0]
-			// all values in this equiv class that are not equivalent to v get moved
-			// into another equiv class.
-			// To avoid allocating while building that equivalence class,
-			// move the values equivalent to v to the beginning of e
-			// and other values to the end of e.
-			allvals := e
-		eqloop:
-			for j := 1; j < len(e); {
-				w := e[j]
-				equivalent := true
-				for i := 0; i < len(v.Args); i++ {
-					if valueEqClass[v.Args[i].ID] != valueEqClass[w.Args[i].ID] {
-						equivalent = false
+
+			// Sort by eq class of arguments.
+			byArgClass.a = e
+			byArgClass.eqClass = valueEqClass
+			sort.Sort(byArgClass)
+
+			// Find split points.
+			splitPoints = append(splitPoints[:0], 0)
+			for j := 1; j < len(e); j++ {
+				v, w := e[j-1], e[j]
+				eqArgs := true
+				for k, a := range v.Args {
+					b := w.Args[k]
+					if valueEqClass[a.ID] != valueEqClass[b.ID] {
+						eqArgs = false
 						break
 					}
 				}
-				if !equivalent || v.Type.Compare(w.Type) != CMPeq {
-					// w is not equivalent to v.
-					// move it to the end and shrink e.
-					e[j], e[len(e)-1] = e[len(e)-1], e[j]
-					e = e[:len(e)-1]
-					valueEqClass[w.ID] = ID(len(partition))
-					changed = true
-					continue eqloop
+				if !eqArgs {
+					splitPoints = append(splitPoints, j)
 				}
-				// v and w are equivalent. Keep w in e.
-				j++
 			}
-			partition[i] = e
-			if len(e) < len(allvals) {
-				partition = append(partition, allvals[len(e):])
+			if len(splitPoints) == 1 {
+				continue // no splits, leave equivalence class alone.
+			}
+
+			// Move another equivalence class down in place of e.
+			partition[i] = partition[len(partition)-1]
+			partition = partition[:len(partition)-1]
+			i--
+
+			// Add new equivalence classes for the parts of e we found.
+			splitPoints = append(splitPoints, len(e))
+			for j := 0; j < len(splitPoints)-1; j++ {
+				f := e[splitPoints[j]:splitPoints[j+1]]
+				if len(f) == 1 {
+					// Don't add singletons.
+					valueEqClass[f[0].ID] = -f[0].ID
+					continue
+				}
+				for _, v := range f {
+					valueEqClass[v.ID] = pNum
+				}
+				pNum++
+				partition = append(partition, f)
 			}
+			changed = true
 		}
 
 		if !changed {
@@ -131,13 +145,16 @@ func cse(f *Func) {
 		}
 	}
 
-	// Dominator tree (f.sdom) is computed by the generic domtree pass.
+	sdom := f.sdom()
 
 	// Compute substitutions we would like to do. We substitute v for w
 	// if v and w are in the same equivalence class and v dominates w.
 	rewrite := make([]*Value, f.NumValues())
+	byDom := new(partitionByDom) // reusable partitionByDom to reduce allocs
 	for _, e := range partition {
-		sort.Sort(partitionByDom{e, f.sdom})
+		byDom.a = e
+		byDom.sdom = sdom
+		sort.Sort(byDom)
 		for i := 0; i < len(e)-1; i++ {
 			// e is sorted by domorder, so a maximal dominant element is first in the slice
 			v := e[i]
@@ -152,7 +169,7 @@ func cse(f *Func) {
 				if w == nil {
 					continue
 				}
-				if f.sdom.isAncestorEq(v.Block, w.Block) {
+				if sdom.isAncestorEq(v.Block, w.Block) {
 					rewrite[w.ID] = v
 					e[j] = nil
 				} else {
@@ -163,6 +180,43 @@ func cse(f *Func) {
 		}
 	}
 
+	// if we rewrite a tuple generator to a new one in a different block,
+	// copy its selectors to the new generator's block, so tuple generator
+	// and selectors stay together.
+	// be careful not to copy same selectors more than once (issue 16741).
+	copiedSelects := make(map[ID][]*Value)
+	for _, b := range f.Blocks {
+	out:
+		for _, v := range b.Values {
+			// New values are created when selectors are copied to
+			// a new block. We can safely ignore those new values,
+			// since they have already been copied (issue 17918).
+			if int(v.ID) >= len(rewrite) || rewrite[v.ID] != nil {
+				continue
+			}
+			if v.Op != OpSelect0 && v.Op != OpSelect1 {
+				continue
+			}
+			if !v.Args[0].Type.IsTuple() {
+				f.Fatalf("arg of tuple selector %s is not a tuple: %s", v.String(), v.Args[0].LongString())
+			}
+			t := rewrite[v.Args[0].ID]
+			if t != nil && t.Block != b {
+				// v.Args[0] is tuple generator, CSE'd into a different block as t, v is left behind
+				for _, c := range copiedSelects[t.ID] {
+					if v.Op == c.Op {
+						// an equivalent selector is already copied
+						rewrite[v.ID] = c
+						continue out
+					}
+				}
+				c := v.copyInto(t.Block)
+				rewrite[v.ID] = c
+				copiedSelects[t.ID] = append(copiedSelects[t.ID], c)
+			}
+		}
+	}
+
 	rewrites := int64(0)
 
 	// Apply substitutions
@@ -219,7 +273,7 @@ func partitionValues(a []*Value, auxIDs auxmap) []eqclass {
 		j := 1
 		for ; j < len(a); j++ {
 			w := a[j]
-			if cmpVal(v, w, auxIDs, cmpDepth) != CMPeq {
+			if cmpVal(v, w, auxIDs) != CMPeq {
 				break
 			}
 		}
@@ -240,7 +294,7 @@ func lt2Cmp(isLt bool) Cmp {
 
 type auxmap map[interface{}]int32
 
-func cmpVal(v, w *Value, auxIDs auxmap, depth int) Cmp {
+func cmpVal(v, w *Value, auxIDs auxmap) Cmp {
 	// Try to order these comparison by cost (cheaper first)
 	if v.Op != w.Op {
 		return lt2Cmp(v.Op < w.Op)
@@ -274,18 +328,6 @@ func cmpVal(v, w *Value, auxIDs auxmap, depth int) Cmp {
 		return lt2Cmp(auxIDs[v.Aux] < auxIDs[w.Aux])
 	}
 
-	if depth > 0 {
-		for i := range v.Args {
-			if v.Args[i] == w.Args[i] {
-				// skip comparing equal args
-				continue
-			}
-			if ac := cmpVal(v.Args[i], w.Args[i], auxIDs, depth-1); ac != CMPeq {
-				return ac
-			}
-		}
-	}
-
 	return CMPeq
 }
 
@@ -300,7 +342,7 @@ func (sv sortvalues) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] }
 func (sv sortvalues) Less(i, j int) bool {
 	v := sv.a[i]
 	w := sv.a[j]
-	if cmp := cmpVal(v, w, sv.auxIDs, cmpDepth); cmp != CMPeq {
+	if cmp := cmpVal(v, w, sv.auxIDs); cmp != CMPeq {
 		return cmp == CMPlt
 	}
 
@@ -320,3 +362,25 @@ func (sv partitionByDom) Less(i, j int) bool {
 	w := sv.a[j]
 	return sv.sdom.domorder(v.Block) < sv.sdom.domorder(w.Block)
 }
+
+type partitionByArgClass struct {
+	a       []*Value // array of values
+	eqClass []ID     // equivalence class IDs of values
+}
+
+func (sv partitionByArgClass) Len() int      { return len(sv.a) }
+func (sv partitionByArgClass) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] }
+func (sv partitionByArgClass) Less(i, j int) bool {
+	v := sv.a[i]
+	w := sv.a[j]
+	for i, a := range v.Args {
+		b := w.Args[i]
+		if sv.eqClass[a.ID] < sv.eqClass[b.ID] {
+			return true
+		}
+		if sv.eqClass[a.ID] > sv.eqClass[b.ID] {
+			return false
+		}
+	}
+	return false
+}
diff --git a/src/cmd/compile/internal/ssa/cse_test.go b/src/cmd/compile/internal/ssa/cse_test.go
index d5be2b5..905939f 100644
--- a/src/cmd/compile/internal/ssa/cse_test.go
+++ b/src/cmd/compile/internal/ssa/cse_test.go
@@ -44,7 +44,6 @@ func TestCSEAuxPartitionBug(t *testing.T) {
 			Exit("rstore")))
 
 	CheckFunc(fun.f)
-	domTree(fun.f)
 	cse(fun.f)
 	deadcode(fun.f)
 	CheckFunc(fun.f)
diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go
index 5ccf390..d75d2d5 100644
--- a/src/cmd/compile/internal/ssa/deadcode.go
+++ b/src/cmd/compile/internal/ssa/deadcode.go
@@ -54,6 +54,7 @@ func liveValues(f *Func, reachable []bool) []bool {
 	var q []*Value // stack-like worklist of unscanned values
 
 	// Starting set: all control values of reachable blocks are live.
+	// Calls are live (because callee can observe the memory state).
 	for _, b := range f.Blocks {
 		if !reachable[b.ID] {
 			continue
@@ -62,6 +63,17 @@ func liveValues(f *Func, reachable []bool) []bool {
 			live[v.ID] = true
 			q = append(q, v)
 		}
+		for _, v := range b.Values {
+			if opcodeTable[v.Op].call && !live[v.ID] {
+				live[v.ID] = true
+				q = append(q, v)
+			}
+			if v.Type.IsVoid() && !live[v.ID] {
+				// The only Void ops are nil checks.  We must keep these.
+				live[v.ID] = true
+				q = append(q, v)
+			}
+		}
 	}
 
 	// Compute transitive closure of live values.
diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go
index 59ac1b9..89ab17a 100644
--- a/src/cmd/compile/internal/ssa/deadstore.go
+++ b/src/cmd/compile/internal/ssa/deadstore.go
@@ -29,6 +29,10 @@ func dse(f *Func) {
 			}
 			if v.Type.IsMemory() {
 				stores = append(stores, v)
+				if v.Op == OpSelect1 {
+					// Use the args of the tuple-generating op.
+					v = v.Args[0]
+				}
 				for _, a := range v.Args {
 					if a.Block == b && a.Type.IsMemory() {
 						storeUse.add(a.ID)
@@ -80,7 +84,12 @@ func dse(f *Func) {
 			shadowed.clear()
 		}
 		if v.Op == OpStore || v.Op == OpZero {
-			sz := v.AuxInt
+			var sz int64
+			if v.Op == OpStore {
+				sz = v.AuxInt
+			} else { // OpZero
+				sz = SizeAndAlign(v.AuxInt).Size()
+			}
 			if shadowedSize := int64(shadowed.get(v.Args[0].ID)); shadowedSize != -1 && shadowedSize >= sz {
 				// Modify store into a copy
 				if v.Op == OpStore {
@@ -102,7 +111,7 @@ func dse(f *Func) {
 				if sz > 0x7fffffff { // work around sparseMap's int32 value type
 					sz = 0x7fffffff
 				}
-				shadowed.set(v.Args[0].ID, int32(sz))
+				shadowed.set(v.Args[0].ID, int32(sz), 0)
 			}
 		}
 		// walk to previous store
diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go
index 53116ba..b2ee2f0 100644
--- a/src/cmd/compile/internal/ssa/decompose.go
+++ b/src/cmd/compile/internal/ssa/decompose.go
@@ -25,6 +25,22 @@ func decomposeBuiltIn(f *Func) {
 	for _, name := range f.Names {
 		t := name.Type
 		switch {
+		case t.IsInteger() && t.Size() == 8 && f.Config.IntSize == 4:
+			var elemType Type
+			if t.IsSigned() {
+				elemType = f.Config.fe.TypeInt32()
+			} else {
+				elemType = f.Config.fe.TypeUInt32()
+			}
+			hiName, loName := f.Config.fe.SplitInt64(name)
+			newNames = append(newNames, hiName, loName)
+			for _, v := range f.NamedValues[name] {
+				hi := v.Block.NewValue1(v.Line, OpInt64Hi, elemType, v)
+				lo := v.Block.NewValue1(v.Line, OpInt64Lo, f.Config.fe.TypeUInt32(), v)
+				f.NamedValues[hiName] = append(f.NamedValues[hiName], hi)
+				f.NamedValues[loName] = append(f.NamedValues[loName], lo)
+			}
+			delete(f.NamedValues, name)
 		case t.IsComplex():
 			var elemType Type
 			if t.Size() == 16 {
@@ -78,8 +94,10 @@ func decomposeBuiltIn(f *Func) {
 				f.NamedValues[dataName] = append(f.NamedValues[dataName], data)
 			}
 			delete(f.NamedValues, name)
+		case t.IsFloat():
+			// floats are never decomposed, even ones bigger than IntSize
 		case t.Size() > f.Config.IntSize:
-			f.Unimplementedf("undecomposed named type %s %s", name, t)
+			f.Fatalf("undecomposed named type %v %v", name, t)
 		default:
 			newNames = append(newNames, name)
 		}
@@ -88,8 +106,13 @@ func decomposeBuiltIn(f *Func) {
 }
 
 func decomposeBuiltInPhi(v *Value) {
-	// TODO: decompose 64-bit ops on 32-bit archs?
 	switch {
+	case v.Type.IsInteger() && v.Type.Size() == 8 && v.Block.Func.Config.IntSize == 4:
+		if v.Block.Func.Config.arch == "amd64p32" {
+			// Even though ints are 32 bits, we have 64-bit ops.
+			break
+		}
+		decomposeInt64Phi(v)
 	case v.Type.IsComplex():
 		decomposeComplexPhi(v)
 	case v.Type.IsString():
@@ -98,8 +121,10 @@ func decomposeBuiltInPhi(v *Value) {
 		decomposeSlicePhi(v)
 	case v.Type.IsInterface():
 		decomposeInterfacePhi(v)
+	case v.Type.IsFloat():
+		// floats are never decomposed, even ones bigger than IntSize
 	case v.Type.Size() > v.Block.Func.Config.IntSize:
-		v.Unimplementedf("undecomposed type %s", v.Type)
+		v.Fatalf("undecomposed type %s", v.Type)
 	}
 }
 
@@ -138,6 +163,26 @@ func decomposeSlicePhi(v *Value) {
 	v.AddArg(cap)
 }
 
+func decomposeInt64Phi(v *Value) {
+	fe := v.Block.Func.Config.fe
+	var partType Type
+	if v.Type.IsSigned() {
+		partType = fe.TypeInt32()
+	} else {
+		partType = fe.TypeUInt32()
+	}
+
+	hi := v.Block.NewValue0(v.Line, OpPhi, partType)
+	lo := v.Block.NewValue0(v.Line, OpPhi, fe.TypeUInt32())
+	for _, a := range v.Args {
+		hi.AddArg(a.Block.NewValue1(v.Line, OpInt64Hi, partType, a))
+		lo.AddArg(a.Block.NewValue1(v.Line, OpInt64Lo, fe.TypeUInt32(), a))
+	}
+	v.reset(OpInt64Make)
+	v.AddArg(hi)
+	v.AddArg(lo)
+}
+
 func decomposeComplexPhi(v *Value) {
 	fe := v.Block.Func.Config.fe
 	var partType Type
@@ -208,6 +253,21 @@ func decomposeUser(f *Func) {
 			}
 			delete(f.NamedValues, name)
 			newNames = append(newNames, fnames...)
+		case t.IsArray():
+			if t.NumElem() == 0 {
+				// TODO(khr): Not sure what to do here.  Probably nothing.
+				// Names for empty arrays aren't important.
+				break
+			}
+			if t.NumElem() != 1 {
+				f.Fatalf("array not of size 1")
+			}
+			elemName := f.Config.fe.SplitArray(name)
+			for _, v := range f.NamedValues[name] {
+				e := v.Block.NewValue1I(v.Line, OpArraySelect, t.ElemType(), 0, v)
+				f.NamedValues[elemName] = append(f.NamedValues[elemName], e)
+			}
+
 		default:
 			f.Names[i] = name
 			i++
@@ -221,10 +281,13 @@ func decomposeUserPhi(v *Value) {
 	switch {
 	case v.Type.IsStruct():
 		decomposeStructPhi(v)
+	case v.Type.IsArray():
+		decomposeArrayPhi(v)
 	}
-	// TODO: Arrays of length 1?
 }
 
+// decomposeStructPhi replaces phi-of-struct with structmake(phi-for-each-field),
+// and then recursively decomposes the phis for each field.
 func decomposeStructPhi(v *Value) {
 	t := v.Type
 	n := t.NumFields()
@@ -242,10 +305,30 @@ func decomposeStructPhi(v *Value) {
 
 	// Recursively decompose phis for each field.
 	for _, f := range fields[:n] {
-		if f.Type.IsStruct() {
-			decomposeStructPhi(f)
-		}
+		decomposeUserPhi(f)
+	}
+}
+
+// decomposeArrayPhi replaces phi-of-array with arraymake(phi-of-array-element),
+// and then recursively decomposes the element phi.
+func decomposeArrayPhi(v *Value) {
+	t := v.Type
+	if t.NumElem() == 0 {
+		v.reset(OpArrayMake0)
+		return
+	}
+	if t.NumElem() != 1 {
+		v.Fatalf("SSAable array must have no more than 1 element")
 	}
+	elem := v.Block.NewValue0(v.Line, OpPhi, t.ElemType())
+	for _, a := range v.Args {
+		elem.AddArg(a.Block.NewValue1I(v.Line, OpArraySelect, t.ElemType(), 0, a))
+	}
+	v.reset(OpArrayMake1)
+	v.AddArg(elem)
+
+	// Recursively decompose elem phi.
+	decomposeUserPhi(elem)
 }
 
 // MaxStruct is the maximum number of fields a struct
diff --git a/src/cmd/compile/internal/ssa/dom.go b/src/cmd/compile/internal/ssa/dom.go
index 0c532c8..4790e33 100644
--- a/src/cmd/compile/internal/ssa/dom.go
+++ b/src/cmd/compile/internal/ssa/dom.go
@@ -247,7 +247,7 @@ func dominatorsSimple(f *Func) []*Block {
 	idom := make([]*Block, f.NumBlocks())
 
 	// Compute postorder walk
-	post := postorder(f)
+	post := f.postorder()
 
 	// Make map from block id to order index (for intersect call)
 	postnum := make([]int, f.NumBlocks())
@@ -306,9 +306,3 @@ func intersect(b, c *Block, postnum []int, idom []*Block) *Block {
 	}
 	return b
 }
-
-// build immediate dominators.
-func domTree(f *Func) {
-	f.idom = dominators(f)
-	f.sdom = newSparseTree(f, f.idom)
-}
diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go
index 27892a8..010c4d7 100644
--- a/src/cmd/compile/internal/ssa/export_test.go
+++ b/src/cmd/compile/internal/ssa/export_test.go
@@ -49,22 +49,34 @@ func (d DummyFrontend) SplitComplex(s LocalSlot) (LocalSlot, LocalSlot) {
 	}
 	return LocalSlot{s.N, d.TypeFloat32(), s.Off}, LocalSlot{s.N, d.TypeFloat32(), s.Off + 4}
 }
+func (d DummyFrontend) SplitInt64(s LocalSlot) (LocalSlot, LocalSlot) {
+	if s.Type.IsSigned() {
+		return LocalSlot{s.N, d.TypeInt32(), s.Off + 4}, LocalSlot{s.N, d.TypeUInt32(), s.Off}
+	}
+	return LocalSlot{s.N, d.TypeUInt32(), s.Off + 4}, LocalSlot{s.N, d.TypeUInt32(), s.Off}
+}
 func (d DummyFrontend) SplitStruct(s LocalSlot, i int) LocalSlot {
 	return LocalSlot{s.N, s.Type.FieldType(i), s.Off + s.Type.FieldOff(i)}
 }
+func (d DummyFrontend) SplitArray(s LocalSlot) LocalSlot {
+	return LocalSlot{s.N, s.Type.ElemType(), s.Off}
+}
 func (DummyFrontend) Line(line int32) string {
 	return "unknown.go:0"
 }
+func (DummyFrontend) AllocFrame(f *Func) {
+}
+func (DummyFrontend) Syslook(s string) interface{} {
+	return nil
+}
 
 func (d DummyFrontend) Logf(msg string, args ...interface{}) { d.t.Logf(msg, args...) }
 func (d DummyFrontend) Log() bool                            { return true }
 
 func (d DummyFrontend) Fatalf(line int32, msg string, args ...interface{}) { d.t.Fatalf(msg, args...) }
-func (d DummyFrontend) Unimplementedf(line int32, msg string, args ...interface{}) {
-	d.t.Fatalf(msg, args...)
-}
-func (d DummyFrontend) Warnl(line int32, msg string, args ...interface{}) { d.t.Logf(msg, args...) }
-func (d DummyFrontend) Debug_checknil() bool                              { return false }
+func (d DummyFrontend) Warnl(line int32, msg string, args ...interface{})  { d.t.Logf(msg, args...) }
+func (d DummyFrontend) Debug_checknil() bool                               { return false }
+func (d DummyFrontend) Debug_wb() bool                                     { return false }
 
 func (d DummyFrontend) TypeBool() Type    { return TypeBool }
 func (d DummyFrontend) TypeInt8() Type    { return TypeInt8 }
diff --git a/src/cmd/compile/internal/ssa/flagalloc.go b/src/cmd/compile/internal/ssa/flagalloc.go
index f6c457d..24b6a0e 100644
--- a/src/cmd/compile/internal/ssa/flagalloc.go
+++ b/src/cmd/compile/internal/ssa/flagalloc.go
@@ -4,8 +4,6 @@
 
 package ssa
 
-const flagRegMask = regMask(1) << 33 // TODO: arch-specific
-
 // flagalloc allocates the flag register among all the flag-generating
 // instructions. Flag values are recomputed if they need to be
 // spilled/restored.
@@ -13,14 +11,10 @@ func flagalloc(f *Func) {
 	// Compute the in-register flag value we want at the end of
 	// each block. This is basically a best-effort live variable
 	// analysis, so it can be much simpler than a full analysis.
-	// TODO: do we really need to keep flag values live across blocks?
-	// Could we force the flags register to be unused at basic block
-	// boundaries?  Then we wouldn't need this computation.
 	end := make([]*Value, f.NumBlocks())
+	po := f.postorder()
 	for n := 0; n < 2; n++ {
-		// Walk blocks backwards. Poor-man's postorder traversal.
-		for i := len(f.Blocks) - 1; i >= 0; i-- {
-			b := f.Blocks[i]
+		for _, b := range po {
 			// Walk values backwards to figure out what flag
 			// value we want in the flag register at the start
 			// of the block.
@@ -33,7 +27,7 @@ func flagalloc(f *Func) {
 				if v == flag {
 					flag = nil
 				}
-				if opcodeTable[v.Op].reg.clobbers&flagRegMask != 0 {
+				if v.clobbersFlags() {
 					flag = nil
 				}
 				for _, a := range v.Args {
@@ -97,7 +91,7 @@ func flagalloc(f *Func) {
 					continue
 				}
 				// Recalculate a
-				c := a.copyInto(b)
+				c := copyFlags(a, b)
 				// Update v.
 				v.SetArg(i, c)
 				// Remember the most-recently computed flag value.
@@ -105,7 +99,7 @@ func flagalloc(f *Func) {
 			}
 			// Issue v.
 			b.Values = append(b.Values, v)
-			if opcodeTable[v.Op].reg.clobbers&flagRegMask != 0 {
+			if v.clobbersFlags() {
 				flag = nil
 			}
 			if v.Type.IsFlags() {
@@ -121,7 +115,7 @@ func flagalloc(f *Func) {
 		if v := end[b.ID]; v != nil && v != flag {
 			// Need to reissue flag generator for use by
 			// subsequent blocks.
-			_ = v.copyInto(b)
+			copyFlags(v, b)
 			// Note: this flag generator is not properly linked up
 			// with the flag users. This breaks the SSA representation.
 			// We could fix up the users with another pass, but for now
@@ -135,3 +129,32 @@ func flagalloc(f *Func) {
 		b.FlagsLiveAtEnd = end[b.ID] != nil
 	}
 }
+
+func (v *Value) clobbersFlags() bool {
+	if opcodeTable[v.Op].clobberFlags {
+		return true
+	}
+	if v.Type.IsTuple() && (v.Type.FieldType(0).IsFlags() || v.Type.FieldType(1).IsFlags()) {
+		// This case handles the possibility where a flag value is generated but never used.
+		// In that case, there's no corresponding Select to overwrite the flags value,
+		// so we must consider flags clobbered by the tuple-generating instruction.
+		return true
+	}
+	return false
+}
+
+// copyFlags copies v (flag generator) into b, returns the copy.
+// If v's arg is also flags, copy recursively.
+func copyFlags(v *Value, b *Block) *Value {
+	flagsArgs := make(map[int]*Value)
+	for i, a := range v.Args {
+		if a.Type.IsFlags() || a.Type.IsTuple() {
+			flagsArgs[i] = copyFlags(a, b)
+		}
+	}
+	c := v.copyInto(b)
+	for i, a := range flagsArgs {
+		c.SetArg(i, a)
+	}
+	return c
+}
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index 1d60bb6..7b2097b 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -7,6 +7,7 @@ package ssa
 import (
 	"fmt"
 	"math"
+	"strings"
 )
 
 // A Func represents a Go func declaration (or function literal) and
@@ -36,8 +37,10 @@ type Func struct {
 	freeValues *Value // free Values linked by argstorage[0].  All other fields except ID are 0/nil.
 	freeBlocks *Block // free Blocks linked by succstorage[0].b.  All other fields except ID are 0/nil.
 
-	idom []*Block   // precomputed immediate dominators
-	sdom SparseTree // precomputed dominator tree
+	cachedPostorder []*Block   // cached postorder traversal
+	cachedIdom      []*Block   // cached immediate dominators
+	cachedSdom      SparseTree // cached dominator tree
+	cachedLoopnest  *loopnest  // cached loop nest information
 
 	constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type
 }
@@ -111,7 +114,7 @@ func (f *Func) LogStat(key string, args ...interface{}) {
 	}
 	n := "missing_pass"
 	if f.pass != nil {
-		n = f.pass.name
+		n = strings.Replace(f.pass.name, " ", "_", -1)
 	}
 	f.Config.Warnl(f.Entry.Line, "\t%s\t%s%s\t%s", n, key, value, f.Name)
 }
@@ -166,6 +169,7 @@ func (f *Func) NewBlock(kind BlockKind) *Block {
 	b.Succs = b.succstorage[:0]
 	b.Values = b.valstorage[:0]
 	f.Blocks = append(f.Blocks, b)
+	f.invalidateCFG()
 	return b
 }
 
@@ -315,6 +319,18 @@ func (b *Block) NewValue3I(line int32, op Op, t Type, auxint int64, arg0, arg1,
 	return v
 }
 
+// NewValue4 returns a new value in the block with four arguments and zero aux values.
+func (b *Block) NewValue4(line int32, op Op, t Type, arg0, arg1, arg2, arg3 *Value) *Value {
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = 0
+	v.Args = []*Value{arg0, arg1, arg2, arg3}
+	arg0.Uses++
+	arg1.Uses++
+	arg2.Uses++
+	arg3.Uses++
+	return v
+}
+
 // constVal returns a constant value for c.
 func (f *Func) constVal(line int32, op Op, t Type, c int64, setAux bool) *Value {
 	if f.constants == nil {
@@ -395,11 +411,11 @@ func (f *Func) ConstEmptyString(line int32, t Type) *Value {
 func (f *Func) Logf(msg string, args ...interface{})   { f.Config.Logf(msg, args...) }
 func (f *Func) Log() bool                              { return f.Config.Log() }
 func (f *Func) Fatalf(msg string, args ...interface{}) { f.Config.Fatalf(f.Entry.Line, msg, args...) }
-func (f *Func) Unimplementedf(msg string, args ...interface{}) {
-	f.Config.Unimplementedf(f.Entry.Line, msg, args...)
-}
 
 func (f *Func) Free() {
+	// Clear cached CFG info.
+	f.invalidateCFG()
+
 	// Clear values.
 	n := f.vid.num()
 	if n > len(f.Config.values) {
@@ -427,3 +443,45 @@ func (f *Func) Free() {
 	f.Config.curFunc = nil
 	*f = Func{} // just in case
 }
+
+// postorder returns the reachable blocks in f in a postorder traversal.
+func (f *Func) postorder() []*Block {
+	if f.cachedPostorder == nil {
+		f.cachedPostorder = postorder(f)
+	}
+	return f.cachedPostorder
+}
+
+// Idom returns a map from block ID to the immediate dominator of that block.
+// f.Entry.ID maps to nil. Unreachable blocks map to nil as well.
+func (f *Func) Idom() []*Block {
+	if f.cachedIdom == nil {
+		f.cachedIdom = dominators(f)
+	}
+	return f.cachedIdom
+}
+
+// sdom returns a sparse tree representing the dominator relationships
+// among the blocks of f.
+func (f *Func) sdom() SparseTree {
+	if f.cachedSdom == nil {
+		f.cachedSdom = newSparseTree(f, f.Idom())
+	}
+	return f.cachedSdom
+}
+
+// loopnest returns the loop nest information for f.
+func (f *Func) loopnest() *loopnest {
+	if f.cachedLoopnest == nil {
+		f.cachedLoopnest = loopnestfor(f)
+	}
+	return f.cachedLoopnest
+}
+
+// invalidateCFG tells f that its CFG has changed.
+func (f *Func) invalidateCFG() {
+	f.cachedPostorder = nil
+	f.cachedIdom = nil
+	f.cachedSdom = nil
+	f.cachedLoopnest = nil
+}
diff --git a/src/cmd/compile/internal/ssa/fuse.go b/src/cmd/compile/internal/ssa/fuse.go
index afb8bb2..d5940da 100644
--- a/src/cmd/compile/internal/ssa/fuse.go
+++ b/src/cmd/compile/internal/ssa/fuse.go
@@ -135,9 +135,11 @@ func fuseBlockPlain(b *Block) bool {
 		p := e.b
 		p.Succs[e.i] = Edge{c, i}
 	}
-	if f := b.Func; f.Entry == b {
+	f := b.Func
+	if f.Entry == b {
 		f.Entry = c
 	}
+	f.invalidateCFG()
 
 	// trash b, just in case
 	b.Kind = BlockInvalid
diff --git a/src/cmd/compile/internal/ssa/gen/386.rules b/src/cmd/compile/internal/ssa/gen/386.rules
new file mode 100644
index 0000000..a3f2ecb
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/386.rules
@@ -0,0 +1,1252 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Lowering arithmetic
+(AddPtr x y) -> (ADDL  x y)
+(Add32  x y) -> (ADDL  x y)
+(Add16  x y) -> (ADDL  x y)
+(Add8   x y) -> (ADDL  x y)
+(Add32F x y) -> (ADDSS x y)
+(Add64F x y) -> (ADDSD x y)
+
+(Add32carry x y) -> (ADDLcarry x y)
+(Add32withcarry x y c) -> (ADCL x y c)
+
+(SubPtr x y) -> (SUBL  x y)
+(Sub32  x y) -> (SUBL  x y)
+(Sub16  x y) -> (SUBL  x y)
+(Sub8   x y) -> (SUBL  x y)
+(Sub32F x y) -> (SUBSS x y)
+(Sub64F x y) -> (SUBSD x y)
+
+(Sub32carry x y) -> (SUBLcarry x y)
+(Sub32withcarry x y c) -> (SBBL x y c)
+
+(Mul32  x y) -> (MULL  x y)
+(Mul16  x y) -> (MULL  x y)
+(Mul8   x y) -> (MULL  x y)
+(Mul32F x y) -> (MULSS x y)
+(Mul64F x y) -> (MULSD x y)
+
+(Mul32uhilo x y) -> (MULLQU x y)
+
+(Div32F x y) -> (DIVSS x y)
+(Div64F x y) -> (DIVSD x y)
+
+(Div32  x y) -> (DIVL  x y)
+(Div32u x y) -> (DIVLU x y)
+(Div16  x y) -> (DIVW  x y)
+(Div16u x y) -> (DIVWU x y)
+(Div8   x y) -> (DIVW  (SignExt8to16 x) (SignExt8to16 y))
+(Div8u  x y) -> (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y))
+
+(Hmul32  x y) -> (HMULL  x y)
+(Hmul32u x y) -> (HMULLU x y)
+(Hmul16  x y) -> (HMULW  x y)
+(Hmul16u x y) -> (HMULWU x y)
+(Hmul8   x y) -> (HMULB  x y)
+(Hmul8u  x y) -> (HMULBU x y)
+
+(Mod32  x y) -> (MODL  x y)
+(Mod32u x y) -> (MODLU x y)
+(Mod16  x y) -> (MODW  x y)
+(Mod16u x y) -> (MODWU x y)
+(Mod8   x y) -> (MODW  (SignExt8to16 x) (SignExt8to16 y))
+(Mod8u  x y) -> (MODWU (ZeroExt8to16 x) (ZeroExt8to16 y))
+
+(And32 x y) -> (ANDL x y)
+(And16 x y) -> (ANDL x y)
+(And8  x y) -> (ANDL x y)
+
+(Or32 x y) -> (ORL x y)
+(Or16 x y) -> (ORL x y)
+(Or8  x y) -> (ORL x y)
+
+(Xor32 x y) -> (XORL x y)
+(Xor16 x y) -> (XORL x y)
+(Xor8  x y) -> (XORL x y)
+
+(Neg32  x) -> (NEGL x)
+(Neg16  x) -> (NEGL x)
+(Neg8   x) -> (NEGL x)
+(Neg32F x) && !config.use387 -> (PXOR x (MOVSSconst <config.Frontend().TypeFloat32()> [f2i(math.Copysign(0, -1))]))
+(Neg64F x) && !config.use387 -> (PXOR x (MOVSDconst <config.Frontend().TypeFloat64()> [f2i(math.Copysign(0, -1))]))
+(Neg32F x) && config.use387 -> (FCHS x)
+(Neg64F x) && config.use387 -> (FCHS x)
+
+(Com32 x) -> (NOTL x)
+(Com16 x) -> (NOTL x)
+(Com8  x) -> (NOTL x)
+
+// Lowering boolean ops
+(AndB x y) -> (ANDL x y)
+(OrB x y) -> (ORL x y)
+(Not x) -> (XORLconst [1] x)
+
+// Lowering pointer arithmetic
+(OffPtr [off] ptr) -> (ADDLconst [off] ptr)
+
+(Bswap32 x) -> (BSWAPL x)
+
+(Sqrt x) -> (SQRTSD x)
+
+// Lowering extension
+(SignExt8to16  x) -> (MOVBLSX x)
+(SignExt8to32  x) -> (MOVBLSX x)
+(SignExt16to32 x) -> (MOVWLSX x)
+
+(ZeroExt8to16  x) -> (MOVBLZX x)
+(ZeroExt8to32  x) -> (MOVBLZX x)
+(ZeroExt16to32 x) -> (MOVWLZX x)
+
+(Signmask x) -> (SARLconst x [31])
+(Zeromask <t> x) -> (XORLconst [-1] (SBBLcarrymask <t> (CMPLconst x [1])))
+(Slicemask <t> x) -> (XORLconst [-1] (SARLconst <t> (SUBLconst <t> x [1]) [31]))
+
+// Lowering truncation
+// Because we ignore high parts of registers, truncates are just copies.
+(Trunc16to8  x) -> x
+(Trunc32to8  x) -> x
+(Trunc32to16 x) -> x
+
+// Lowering float <-> int
+(Cvt32to32F x) -> (CVTSL2SS x)
+(Cvt32to64F x) -> (CVTSL2SD x)
+
+(Cvt32Fto32 x) -> (CVTTSS2SL x)
+(Cvt64Fto32 x) -> (CVTTSD2SL x)
+
+(Cvt32Fto64F x) -> (CVTSS2SD x)
+(Cvt64Fto32F x) -> (CVTSD2SS x)
+
+// Lowering shifts
+// Unsigned shifts need to return 0 if shift amount is >= width of shifted value.
+//   result = (arg << shift) & (shift >= argbits ? 0 : 0xffffffffffffffff)
+(Lsh32x32 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+(Lsh32x16 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+(Lsh32x8  <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+
+(Lsh16x32 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+(Lsh16x16 <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+(Lsh16x8  <t> x y) -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+
+(Lsh8x32 <t> x y)  -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+(Lsh8x16 <t> x y)  -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+(Lsh8x8  <t> x y)  -> (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+
+(Lrot32 <t> x [c]) -> (ROLLconst <t> [c&31] x)
+(Lrot16 <t> x [c]) -> (ROLWconst <t> [c&15] x)
+(Lrot8  <t> x [c]) -> (ROLBconst <t> [c&7] x)
+
+(Rsh32Ux32 <t> x y) -> (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+(Rsh32Ux16 <t> x y) -> (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+(Rsh32Ux8  <t> x y) -> (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+
+(Rsh16Ux32 <t> x y) -> (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPLconst y [16])))
+(Rsh16Ux16 <t> x y) -> (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPWconst y [16])))
+(Rsh16Ux8  <t> x y) -> (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPBconst y [16])))
+
+(Rsh8Ux32 <t> x y)  -> (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPLconst y [8])))
+(Rsh8Ux16 <t> x y)  -> (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPWconst y [8])))
+(Rsh8Ux8  <t> x y)  -> (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPBconst y [8])))
+
+// Signed right shift needs to return 0/-1 if shift amount is >= width of shifted value.
+// We implement this by setting the shift value to -1 (all ones) if the shift value is >= width.
+
+(Rsh32x32 <t> x y) -> (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [32])))))
+(Rsh32x16 <t> x y) -> (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [32])))))
+(Rsh32x8  <t> x y) -> (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [32])))))
+
+(Rsh16x32 <t> x y) -> (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [16])))))
+(Rsh16x16 <t> x y) -> (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [16])))))
+(Rsh16x8  <t> x y) -> (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [16])))))
+
+(Rsh8x32 <t> x y)  -> (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [8])))))
+(Rsh8x16 <t> x y)  -> (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [8])))))
+(Rsh8x8  <t> x y)  -> (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [8])))))
+
+// constant shifts
+// generic opt rewrites all constant shifts to shift by Const64
+(Lsh32x64 x (Const64 [c])) && uint64(c) < 32 -> (SHLLconst x [c])
+(Rsh32x64 x (Const64 [c])) && uint64(c) < 32 -> (SARLconst x [c])
+(Rsh32Ux64 x (Const64 [c])) && uint64(c) < 32 -> (SHRLconst x [c])
+(Lsh16x64 x (Const64 [c])) && uint64(c) < 16 -> (SHLLconst x [c])
+(Rsh16x64 x (Const64 [c])) && uint64(c) < 16 -> (SARWconst x [c])
+(Rsh16Ux64 x (Const64 [c])) && uint64(c) < 16 -> (SHRWconst x [c])
+(Lsh8x64 x (Const64 [c])) && uint64(c) < 8 -> (SHLLconst x [c])
+(Rsh8x64 x (Const64 [c])) && uint64(c) < 8 -> (SARBconst x [c])
+(Rsh8Ux64 x (Const64 [c])) && uint64(c) < 8 -> (SHRBconst x [c])
+
+// large constant shifts
+(Lsh32x64 _ (Const64 [c])) && uint64(c) >= 32 -> (Const32 [0])
+(Rsh32Ux64 _ (Const64 [c])) && uint64(c) >= 32 -> (Const32 [0])
+(Lsh16x64 _ (Const64 [c])) && uint64(c) >= 16 -> (Const16 [0])
+(Rsh16Ux64 _ (Const64 [c])) && uint64(c) >= 16 -> (Const16 [0])
+(Lsh8x64 _ (Const64 [c])) && uint64(c) >= 8 -> (Const8 [0])
+(Rsh8Ux64 _ (Const64 [c])) && uint64(c) >= 8 -> (Const8 [0])
+
+// large constant signed right shift, we leave the sign bit
+(Rsh32x64 x (Const64 [c])) && uint64(c) >= 32 -> (SARLconst x [31])
+(Rsh16x64 x (Const64 [c])) && uint64(c) >= 16 -> (SARWconst x [15])
+(Rsh8x64 x (Const64 [c])) && uint64(c) >= 8 -> (SARBconst x [7])
+
+// Lowering comparisons
+(Less32  x y) -> (SETL (CMPL x y))
+(Less16  x y) -> (SETL (CMPW x y))
+(Less8   x y) -> (SETL (CMPB x y))
+(Less32U x y) -> (SETB (CMPL x y))
+(Less16U x y) -> (SETB (CMPW x y))
+(Less8U  x y) -> (SETB (CMPB x y))
+// Use SETGF with reversed operands to dodge NaN case
+(Less64F x y) -> (SETGF (UCOMISD y x))
+(Less32F x y) -> (SETGF (UCOMISS y x))
+
+(Leq32  x y) -> (SETLE (CMPL x y))
+(Leq16  x y) -> (SETLE (CMPW x y))
+(Leq8   x y) -> (SETLE (CMPB x y))
+(Leq32U x y) -> (SETBE (CMPL x y))
+(Leq16U x y) -> (SETBE (CMPW x y))
+(Leq8U  x y) -> (SETBE (CMPB x y))
+// Use SETGEF with reversed operands to dodge NaN case
+(Leq64F x y) -> (SETGEF (UCOMISD y x))
+(Leq32F x y) -> (SETGEF (UCOMISS y x))
+
+(Greater32  x y) -> (SETG (CMPL x y))
+(Greater16  x y) -> (SETG (CMPW x y))
+(Greater8   x y) -> (SETG (CMPB x y))
+(Greater32U x y) -> (SETA (CMPL x y))
+(Greater16U x y) -> (SETA (CMPW x y))
+(Greater8U  x y) -> (SETA (CMPB x y))
+// Note Go assembler gets UCOMISx operand order wrong, but it is right here
+// Bug is accommodated at generation of assembly language.
+(Greater64F x y) -> (SETGF (UCOMISD x y))
+(Greater32F x y) -> (SETGF (UCOMISS x y))
+
+(Geq32  x y) -> (SETGE (CMPL x y))
+(Geq16  x y) -> (SETGE (CMPW x y))
+(Geq8   x y) -> (SETGE (CMPB x y))
+(Geq32U x y) -> (SETAE (CMPL x y))
+(Geq16U x y) -> (SETAE (CMPW x y))
+(Geq8U  x y) -> (SETAE (CMPB x y))
+// Note Go assembler gets UCOMISx operand order wrong, but it is right here
+// Bug is accommodated at generation of assembly language.
+(Geq64F x y) -> (SETGEF (UCOMISD x y))
+(Geq32F x y) -> (SETGEF (UCOMISS x y))
+
+(Eq32  x y) -> (SETEQ (CMPL x y))
+(Eq16  x y) -> (SETEQ (CMPW x y))
+(Eq8   x y) -> (SETEQ (CMPB x y))
+(EqB   x y) -> (SETEQ (CMPB x y))
+(EqPtr x y) -> (SETEQ (CMPL x y))
+(Eq64F x y) -> (SETEQF (UCOMISD x y))
+(Eq32F x y) -> (SETEQF (UCOMISS x y))
+
+(Neq32  x y) -> (SETNE (CMPL x y))
+(Neq16  x y) -> (SETNE (CMPW x y))
+(Neq8   x y) -> (SETNE (CMPB x y))
+(NeqB   x y) -> (SETNE (CMPB x y))
+(NeqPtr x y) -> (SETNE (CMPL x y))
+(Neq64F x y) -> (SETNEF (UCOMISD x y))
+(Neq32F x y) -> (SETNEF (UCOMISS x y))
+
+// Lowering loads
+(Load <t> ptr mem) && (is32BitInt(t) || isPtr(t)) -> (MOVLload ptr mem)
+(Load <t> ptr mem) && is16BitInt(t) -> (MOVWload ptr mem)
+(Load <t> ptr mem) && (t.IsBoolean() || is8BitInt(t)) -> (MOVBload ptr mem)
+(Load <t> ptr mem) && is32BitFloat(t) -> (MOVSSload ptr mem)
+(Load <t> ptr mem) && is64BitFloat(t) -> (MOVSDload ptr mem)
+
+// Lowering stores
+// These more-specific FP versions of Store pattern should come first.
+(Store [8] ptr val mem) && is64BitFloat(val.Type) -> (MOVSDstore ptr val mem)
+(Store [4] ptr val mem) && is32BitFloat(val.Type) -> (MOVSSstore ptr val mem)
+
+(Store [4] ptr val mem) -> (MOVLstore ptr val mem)
+(Store [2] ptr val mem) -> (MOVWstore ptr val mem)
+(Store [1] ptr val mem) -> (MOVBstore ptr val mem)
+
+// Lowering moves
+(Move [s] _ _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore dst (MOVBload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 -> (MOVWstore dst (MOVWload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 -> (MOVLstore dst (MOVLload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] dst (MOVBload [2] src mem)
+		(MOVWstore dst (MOVWload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 5 ->
+	(MOVBstore [4] dst (MOVBload [4] src mem)
+		(MOVLstore dst (MOVLload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 6 ->
+	(MOVWstore [4] dst (MOVWload [4] src mem)
+		(MOVLstore dst (MOVLload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 7 ->
+	(MOVLstore [3] dst (MOVLload [3] src mem)
+		(MOVLstore dst (MOVLload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 ->
+	(MOVLstore [4] dst (MOVLload [4] src mem)
+		(MOVLstore dst (MOVLload src mem) mem))
+
+// Adjust moves to be a multiple of 4 bytes.
+(Move [s] dst src mem)
+	&& SizeAndAlign(s).Size() > 8 && SizeAndAlign(s).Size()%4 != 0 ->
+	(Move [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%4]
+		(ADDLconst <dst.Type> dst [SizeAndAlign(s).Size()%4])
+		(ADDLconst <src.Type> src [SizeAndAlign(s).Size()%4])
+		(MOVLstore dst (MOVLload src mem) mem))
+
+// Medium copying uses a duff device.
+(Move [s] dst src mem)
+	&& SizeAndAlign(s).Size() > 8 && SizeAndAlign(s).Size() <= 4*128 && SizeAndAlign(s).Size()%4 == 0
+	&& !config.noDuffDevice ->
+	(DUFFCOPY [10*(128-SizeAndAlign(s).Size()/4)] dst src mem)
+// 10 and 128 are magic constants.  10 is the number of bytes to encode:
+//	MOVL	(SI), CX
+//	ADDL	$4, SI
+//	MOVL	CX, (DI)
+//	ADDL	$4, DI
+// and 128 is the number of such blocks. See src/runtime/duff_386.s:duffcopy.
+
+// Large copying uses REP MOVSL.
+(Move [s] dst src mem) && (SizeAndAlign(s).Size() > 4*128 || config.noDuffDevice) && SizeAndAlign(s).Size()%4 == 0 ->
+	(REPMOVSL dst src (MOVLconst [SizeAndAlign(s).Size()/4]) mem)
+
+// Lowering Zero instructions
+(Zero [s] _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstoreconst [0] destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 2 -> (MOVWstoreconst [0] destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 4 -> (MOVLstoreconst [0] destptr mem)
+
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstoreconst [makeValAndOff(0,2)] destptr
+		(MOVWstoreconst [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 5 ->
+	(MOVBstoreconst [makeValAndOff(0,4)] destptr
+		(MOVLstoreconst [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 6 ->
+	(MOVWstoreconst [makeValAndOff(0,4)] destptr
+		(MOVLstoreconst [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 7 ->
+	(MOVLstoreconst [makeValAndOff(0,3)] destptr
+		(MOVLstoreconst [0] destptr mem))
+
+// Strip off any fractional word zeroing.
+(Zero [s] destptr mem) && SizeAndAlign(s).Size()%4 != 0 && SizeAndAlign(s).Size() > 4 ->
+	(Zero [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%4] (ADDLconst destptr [SizeAndAlign(s).Size()%4])
+		(MOVLstoreconst [0] destptr mem))
+
+// Zero small numbers of words directly.
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 8 ->
+	(MOVLstoreconst [makeValAndOff(0,4)] destptr
+		(MOVLstoreconst [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 12 ->
+	(MOVLstoreconst [makeValAndOff(0,8)] destptr
+		(MOVLstoreconst [makeValAndOff(0,4)] destptr
+			(MOVLstoreconst [0] destptr mem)))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 16 ->
+	(MOVLstoreconst [makeValAndOff(0,12)] destptr
+		(MOVLstoreconst [makeValAndOff(0,8)] destptr
+			(MOVLstoreconst [makeValAndOff(0,4)] destptr
+				(MOVLstoreconst [0] destptr mem))))
+
+// Medium zeroing uses a duff device.
+(Zero [s] destptr mem)
+  && SizeAndAlign(s).Size() > 16
+  && SizeAndAlign(s).Size() <= 4*128
+  && SizeAndAlign(s).Size()%4 == 0
+  && !config.noDuffDevice ->
+	(DUFFZERO [1*(128-SizeAndAlign(s).Size()/4)] destptr (MOVLconst [0]) mem)
+// 1 and 128 are magic constants.  1 is the number of bytes to encode STOSL.
+// 128 is the number of STOSL instructions in duffzero.
+// See src/runtime/duff_386.s:duffzero.
+
+// Large zeroing uses REP STOSQ.
+(Zero [s] destptr mem)
+  && (SizeAndAlign(s).Size() > 4*128 || (config.noDuffDevice && SizeAndAlign(s).Size() > 16))
+  && SizeAndAlign(s).Size()%4 == 0 ->
+	(REPSTOSL destptr (MOVLconst [SizeAndAlign(s).Size()/4]) (MOVLconst [0]) mem)
+
+// Lowering constants
+(Const8   [val]) -> (MOVLconst [val])
+(Const16  [val]) -> (MOVLconst [val])
+(Const32  [val]) -> (MOVLconst [val])
+(Const32F [val]) -> (MOVSSconst [val])
+(Const64F [val]) -> (MOVSDconst [val])
+(ConstNil) -> (MOVLconst [0])
+(ConstBool [b]) -> (MOVLconst [b])
+
+// Lowering calls
+(StaticCall [argwid] {target} mem) -> (CALLstatic [argwid] {target} mem)
+(ClosureCall [argwid] entry closure mem) -> (CALLclosure [argwid] entry closure mem)
+(DeferCall [argwid] mem) -> (CALLdefer [argwid] mem)
+(GoCall [argwid] mem) -> (CALLgo [argwid] mem)
+(InterCall [argwid] entry mem) -> (CALLinter [argwid] entry mem)
+
+// Miscellaneous
+(Convert <t> x mem) -> (MOVLconvert <t> x mem)
+(IsNonNil p) -> (SETNE (TESTL p p))
+(IsInBounds idx len) -> (SETB (CMPL idx len))
+(IsSliceInBounds idx len) -> (SETBE (CMPL idx len))
+(NilCheck ptr mem) -> (LoweredNilCheck ptr mem)
+(GetG mem) -> (LoweredGetG mem)
+(GetClosurePtr) -> (LoweredGetClosurePtr)
+(Addr {sym} base) -> (LEAL {sym} base)
+
+// block rewrites
+(If (SETL  cmp) yes no) -> (LT  cmp yes no)
+(If (SETLE cmp) yes no) -> (LE  cmp yes no)
+(If (SETG  cmp) yes no) -> (GT  cmp yes no)
+(If (SETGE cmp) yes no) -> (GE  cmp yes no)
+(If (SETEQ cmp) yes no) -> (EQ  cmp yes no)
+(If (SETNE cmp) yes no) -> (NE  cmp yes no)
+(If (SETB  cmp) yes no) -> (ULT cmp yes no)
+(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)
+
+// Special case for floating point - LF/LEF not generated
+(If (SETGF  cmp) yes no) -> (UGT  cmp yes no)
+(If (SETGEF cmp) yes no) -> (UGE  cmp yes no)
+(If (SETEQF cmp) yes no) -> (EQF  cmp yes no)
+(If (SETNEF cmp) yes no) -> (NEF  cmp yes no)
+
+(If cond yes no) -> (NE (TESTB cond cond) yes no)
+
+// ***************************
+// Above: lowering rules
+// Below: optimizations
+// ***************************
+// TODO: Should the optimizations be a separate pass?
+
+// Fold boolean tests into blocks
+(NE (TESTB (SETL  cmp) (SETL  cmp)) yes no) -> (LT  cmp yes no)
+(NE (TESTB (SETLE cmp) (SETLE cmp)) yes no) -> (LE  cmp yes no)
+(NE (TESTB (SETG  cmp) (SETG  cmp)) yes no) -> (GT  cmp yes no)
+(NE (TESTB (SETGE cmp) (SETGE cmp)) yes no) -> (GE  cmp yes no)
+(NE (TESTB (SETEQ cmp) (SETEQ cmp)) yes no) -> (EQ  cmp yes no)
+(NE (TESTB (SETNE cmp) (SETNE cmp)) yes no) -> (NE  cmp yes no)
+(NE (TESTB (SETB  cmp) (SETB  cmp)) yes no) -> (ULT cmp yes no)
+(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)
+
+// Special case for floating point - LF/LEF not generated
+(NE (TESTB (SETGF  cmp) (SETGF  cmp)) yes no) -> (UGT  cmp yes no)
+(NE (TESTB (SETGEF cmp) (SETGEF cmp)) yes no) -> (UGE  cmp yes no)
+(NE (TESTB (SETEQF cmp) (SETEQF cmp)) yes no) -> (EQF  cmp yes no)
+(NE (TESTB (SETNEF cmp) (SETNEF cmp)) yes no) -> (NEF  cmp yes no)
+
+// fold constants into instructions
+(ADDL x (MOVLconst [c])) -> (ADDLconst [c] x)
+(ADDL (MOVLconst [c]) x) -> (ADDLconst [c] x)
+(ADDLcarry x (MOVLconst [c])) -> (ADDLconstcarry [c] x)
+(ADDLcarry (MOVLconst [c]) x) -> (ADDLconstcarry [c] x)
+(ADCL x (MOVLconst [c]) f) -> (ADCLconst [c] x f)
+(ADCL (MOVLconst [c]) x f) -> (ADCLconst [c] x f)
+
+(SUBL x (MOVLconst [c])) -> (SUBLconst x [c])
+(SUBL (MOVLconst [c]) x) -> (NEGL (SUBLconst <v.Type> x [c]))
+(SUBLcarry x (MOVLconst [c])) -> (SUBLconstcarry [c] x)
+(SBBL x (MOVLconst [c]) f) -> (SBBLconst [c] x f)
+
+(MULL x (MOVLconst [c])) -> (MULLconst [c] x)
+(MULL (MOVLconst [c]) x) -> (MULLconst [c] x)
+
+(ANDL x (MOVLconst [c])) -> (ANDLconst [c] x)
+(ANDL (MOVLconst [c]) x) -> (ANDLconst [c] x)
+
+(ANDLconst [c] (ANDLconst [d] x)) -> (ANDLconst [c & d] x)
+
+(XORLconst [c] (XORLconst [d] x)) -> (XORLconst [c ^ d] x)
+
+(MULLconst [c] (MULLconst [d] x)) -> (MULLconst [int64(int32(c * d))] x)
+
+(ORL x (MOVLconst [c])) -> (ORLconst [c] x)
+(ORL (MOVLconst [c]) x) -> (ORLconst [c] x)
+
+(XORL x (MOVLconst [c])) -> (XORLconst [c] x)
+(XORL (MOVLconst [c]) x) -> (XORLconst [c] x)
+
+(SHLL x (MOVLconst [c])) -> (SHLLconst [c&31] x)
+(SHLL x (MOVLconst [c])) -> (SHLLconst [c&31] x)
+
+(SHRL x (MOVLconst [c])) -> (SHRLconst [c&31] x)
+(SHRL x (MOVLconst [c])) -> (SHRLconst [c&31] x)
+
+(SHRW x (MOVLconst [c])) -> (SHRWconst [c&31] x)
+(SHRW x (MOVLconst [c])) -> (SHRWconst [c&31] x)
+
+(SHRB x (MOVLconst [c])) -> (SHRBconst [c&31] x)
+(SHRB x (MOVLconst [c])) -> (SHRBconst [c&31] x)
+
+(SARL x (MOVLconst [c])) -> (SARLconst [c&31] x)
+(SARL x (MOVLconst [c])) -> (SARLconst [c&31] x)
+
+(SARW x (MOVLconst [c])) -> (SARWconst [c&31] x)
+(SARW x (MOVLconst [c])) -> (SARWconst [c&31] x)
+
+(SARB x (MOVLconst [c])) -> (SARBconst [c&31] x)
+(SARB x (MOVLconst [c])) -> (SARBconst [c&31] x)
+
+(SARL x (ANDLconst [31] y)) -> (SARL x y)
+
+(SHLL x (ANDLconst [31] y)) -> (SHLL x y)
+
+(SHRL x (ANDLconst [31] y)) -> (SHRL x y)
+
+(ROLLconst [c] (ROLLconst [d] x)) -> (ROLLconst [(c+d)&31] x)
+(ROLWconst [c] (ROLWconst [d] x)) -> (ROLWconst [(c+d)&15] x)
+(ROLBconst [c] (ROLBconst [d] x)) -> (ROLBconst [(c+d)& 7] x)
+
+(ROLLconst [0] x) -> x
+(ROLWconst [0] x) -> x
+(ROLBconst [0] x) -> x
+
+// Note: the word and byte shifts keep the low 5 bits (not the low 4 or 3 bits)
+// because the x86 instructions are defined to use all 5 bits of the shift even
+// for the small shifts. I don't think we'll ever generate a weird shift (e.g.
+// (SHRW x (MOVLconst [24])), but just in case.
+
+(CMPL x (MOVLconst [c])) -> (CMPLconst x [c])
+(CMPL (MOVLconst [c]) x) -> (InvertFlags (CMPLconst x [c]))
+(CMPW x (MOVLconst [c])) -> (CMPWconst x [int64(int16(c))])
+(CMPW (MOVLconst [c]) x) -> (InvertFlags (CMPWconst x [int64(int16(c))]))
+(CMPB x (MOVLconst [c])) -> (CMPBconst x [int64(int8(c))])
+(CMPB (MOVLconst [c]) x) -> (InvertFlags (CMPBconst x [int64(int8(c))]))
+
+// strength reduction
+// Assumes that the following costs from https://gmplib.org/~tege/x86-timing.pdf:
+//    1 - addq, shlq, leaq, negq
+//    3 - imulq
+// This limits the rewrites to two instructions.
+// TODO: 27, 81
+(MULLconst [-1] x) -> (NEGL x)
+(MULLconst [0] _) -> (MOVLconst [0])
+(MULLconst [1] x) -> x
+(MULLconst [3] x) -> (LEAL2 x x)
+(MULLconst [5] x) -> (LEAL4 x x)
+(MULLconst [7] x) -> (LEAL8 (NEGL <v.Type> x) x)
+(MULLconst [9] x) -> (LEAL8 x x)
+(MULLconst [11] x) -> (LEAL2 x (LEAL4 <v.Type> x x))
+(MULLconst [13] x) -> (LEAL4 x (LEAL2 <v.Type> x x))
+(MULLconst [21] x) -> (LEAL4 x (LEAL4 <v.Type> x x))
+(MULLconst [25] x) -> (LEAL8 x (LEAL2 <v.Type> x x))
+(MULLconst [37] x) -> (LEAL4 x (LEAL8 <v.Type> x x))
+(MULLconst [41] x) -> (LEAL8 x (LEAL4 <v.Type> x x))
+(MULLconst [73] x) -> (LEAL8 x (LEAL8 <v.Type> x x))
+
+(MULLconst [c] x) && isPowerOfTwo(c) -> (SHLLconst [log2(c)] x)
+(MULLconst [c] x) && isPowerOfTwo(c+1) && c >= 15 -> (SUBL (SHLLconst <v.Type> [log2(c+1)] x) x)
+(MULLconst [c] x) && isPowerOfTwo(c-1) && c >= 17 -> (LEAL1 (SHLLconst <v.Type> [log2(c-1)] x) x)
+(MULLconst [c] x) && isPowerOfTwo(c-2) && c >= 34 -> (LEAL2 (SHLLconst <v.Type> [log2(c-2)] x) x)
+(MULLconst [c] x) && isPowerOfTwo(c-4) && c >= 68 -> (LEAL4 (SHLLconst <v.Type> [log2(c-4)] x) x)
+(MULLconst [c] x) && isPowerOfTwo(c-8) && c >= 136 -> (LEAL8 (SHLLconst <v.Type> [log2(c-8)] x) x)
+(MULLconst [c] x) && c%3 == 0 && isPowerOfTwo(c/3)-> (SHLLconst [log2(c/3)] (LEAL2 <v.Type> x x))
+(MULLconst [c] x) && c%5 == 0 && isPowerOfTwo(c/5)-> (SHLLconst [log2(c/5)] (LEAL4 <v.Type> x x))
+(MULLconst [c] x) && c%9 == 0 && isPowerOfTwo(c/9)-> (SHLLconst [log2(c/9)] (LEAL8 <v.Type> x x))
+
+// combine add/shift into LEAL
+(ADDL x (SHLLconst [3] y)) -> (LEAL8 x y)
+(ADDL x (SHLLconst [2] y)) -> (LEAL4 x y)
+(ADDL x (SHLLconst [1] y)) -> (LEAL2 x y)
+(ADDL x (ADDL y y)) -> (LEAL2 x y)
+(ADDL x (ADDL x y)) -> (LEAL2 y x)
+(ADDL x (ADDL y x)) -> (LEAL2 y x)
+
+// combine ADDL/ADDLconst into LEAL1
+(ADDLconst [c] (ADDL x y)) -> (LEAL1 [c] x y)
+(ADDL (ADDLconst [c] x) y) -> (LEAL1 [c] x y)
+(ADDL x (ADDLconst [c] y)) -> (LEAL1 [c] x y)
+
+// fold ADDL into LEAL
+(ADDLconst [c] (LEAL [d] {s} x)) && is32Bit(c+d) -> (LEAL [c+d] {s} x)
+(LEAL [c] {s} (ADDLconst [d] x)) && is32Bit(c+d) -> (LEAL [c+d] {s} x)
+(LEAL [c] {s} (ADDL x y)) && x.Op != OpSB && y.Op != OpSB -> (LEAL1 [c] {s} x y)
+(ADDL x (LEAL [c] {s} y)) && x.Op != OpSB && y.Op != OpSB -> (LEAL1 [c] {s} x y)
+(ADDL (LEAL [c] {s} x) y) && x.Op != OpSB && y.Op != OpSB -> (LEAL1 [c] {s} x y)
+
+// fold ADDLconst into LEALx
+(ADDLconst [c] (LEAL1 [d] {s} x y)) && is32Bit(c+d) -> (LEAL1 [c+d] {s} x y)
+(ADDLconst [c] (LEAL2 [d] {s} x y)) && is32Bit(c+d) -> (LEAL2 [c+d] {s} x y)
+(ADDLconst [c] (LEAL4 [d] {s} x y)) && is32Bit(c+d) -> (LEAL4 [c+d] {s} x y)
+(ADDLconst [c] (LEAL8 [d] {s} x y)) && is32Bit(c+d) -> (LEAL8 [c+d] {s} x y)
+(LEAL1 [c] {s} (ADDLconst [d] x) y) && is32Bit(c+d)   && x.Op != OpSB -> (LEAL1 [c+d] {s} x y)
+(LEAL1 [c] {s} x (ADDLconst [d] y)) && is32Bit(c+d)   && y.Op != OpSB -> (LEAL1 [c+d] {s} x y)
+(LEAL2 [c] {s} (ADDLconst [d] x) y) && is32Bit(c+d)   && x.Op != OpSB -> (LEAL2 [c+d] {s} x y)
+(LEAL2 [c] {s} x (ADDLconst [d] y)) && is32Bit(c+2*d) && y.Op != OpSB -> (LEAL2 [c+2*d] {s} x y)
+(LEAL4 [c] {s} (ADDLconst [d] x) y) && is32Bit(c+d)   && x.Op != OpSB -> (LEAL4 [c+d] {s} x y)
+(LEAL4 [c] {s} x (ADDLconst [d] y)) && is32Bit(c+4*d) && y.Op != OpSB -> (LEAL4 [c+4*d] {s} x y)
+(LEAL8 [c] {s} (ADDLconst [d] x) y) && is32Bit(c+d)   && x.Op != OpSB -> (LEAL8 [c+d] {s} x y)
+(LEAL8 [c] {s} x (ADDLconst [d] y)) && is32Bit(c+8*d) && y.Op != OpSB -> (LEAL8 [c+8*d] {s} x y)
+
+// fold shifts into LEALx
+(LEAL1 [c] {s} x (SHLLconst [1] y)) -> (LEAL2 [c] {s} x y)
+(LEAL1 [c] {s} (SHLLconst [1] x) y) -> (LEAL2 [c] {s} y x)
+(LEAL1 [c] {s} x (SHLLconst [2] y)) -> (LEAL4 [c] {s} x y)
+(LEAL1 [c] {s} (SHLLconst [2] x) y) -> (LEAL4 [c] {s} y x)
+(LEAL1 [c] {s} x (SHLLconst [3] y)) -> (LEAL8 [c] {s} x y)
+(LEAL1 [c] {s} (SHLLconst [3] x) y) -> (LEAL8 [c] {s} y x)
+
+(LEAL2 [c] {s} x (SHLLconst [1] y)) -> (LEAL4 [c] {s} x y)
+(LEAL2 [c] {s} x (SHLLconst [2] y)) -> (LEAL8 [c] {s} x y)
+(LEAL4 [c] {s} x (SHLLconst [1] y)) -> (LEAL8 [c] {s} x y)
+
+// reverse ordering of compare instruction
+(SETL (InvertFlags x)) -> (SETG x)
+(SETG (InvertFlags x)) -> (SETL x)
+(SETB (InvertFlags x)) -> (SETA x)
+(SETA (InvertFlags x)) -> (SETB x)
+(SETLE (InvertFlags x)) -> (SETGE x)
+(SETGE (InvertFlags x)) -> (SETLE x)
+(SETBE (InvertFlags x)) -> (SETAE x)
+(SETAE (InvertFlags x)) -> (SETBE x)
+(SETEQ (InvertFlags x)) -> (SETEQ x)
+(SETNE (InvertFlags x)) -> (SETNE x)
+
+// sign extended loads
+// Note: The combined instruction must end up in the same block
+// as the original load. If not, we end up making a value with
+// memory type live in two different blocks, which can lead to
+// multiple memory values alive simultaneously.
+// Make sure we don't combine these ops if the load has another use.
+// This prevents a single load from being split into multiple loads
+// which then might return different values.  See test/atomicload.go.
+(MOVBLSX x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBLSXload <v.Type> [off] {sym} ptr mem)
+(MOVBLZX x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
+(MOVWLSX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWLSXload <v.Type> [off] {sym} ptr mem)
+(MOVWLZX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
+
+(MOVBLZX x:(MOVBloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBloadidx1 <v.Type> [off] {sym} ptr idx mem)
+(MOVWLZX x:(MOVWloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx1 <v.Type> [off] {sym} ptr idx mem)
+(MOVWLZX x:(MOVWloadidx2 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx2 <v.Type> [off] {sym} ptr idx mem)
+
+// replace load from same location as preceding store with copy
+(MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+(MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+(MOVLload [off] {sym} ptr (MOVLstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+
+// Fold extensions and ANDs together.
+(MOVBLZX (ANDLconst [c] x)) -> (ANDLconst [c & 0xff] x)
+(MOVWLZX (ANDLconst [c] x)) -> (ANDLconst [c & 0xffff] x)
+(MOVBLSX (ANDLconst [c] x)) && c & 0x80 == 0 -> (ANDLconst [c & 0x7f] x)
+(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)
+
+// 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)
+
+// Fold constants into stores.
+(MOVLstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
+	(MOVLstoreconst [makeValAndOff(int64(int32(c)),off)] {sym} ptr mem)
+(MOVWstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
+	(MOVWstoreconst [makeValAndOff(int64(int16(c)),off)] {sym} ptr mem)
+(MOVBstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
+	(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)
+
+// 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.
+// Note: we turn off this merging for operations on globals when building
+// position-independent code (when Flag_shared is set).
+// PIC needs a spare register to load the PC into.  Having the LEAL be
+// 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)
+  && (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)
+
+(MOVBLSXload [off1] {sym1} (LEAL [off2] {sym2} base) 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)
+
+(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)
+  && (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)
+
+// 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)
+(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)
+(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)
+(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)
+(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)
+
+(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)
+
+(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)
+(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)
+
+// combine SHLL into indexed loads and stores
+(MOVWloadidx1 [c] {sym} ptr (SHLLconst [1] idx) mem) -> (MOVWloadidx2 [c] {sym} ptr idx mem)
+(MOVLloadidx1 [c] {sym} ptr (SHLLconst [2] idx) mem) -> (MOVLloadidx4 [c] {sym} ptr idx mem)
+(MOVWstoreidx1 [c] {sym} ptr (SHLLconst [1] idx) val mem) -> (MOVWstoreidx2 [c] {sym} ptr idx val mem)
+(MOVLstoreidx1 [c] {sym} ptr (SHLLconst [2] idx) val mem) -> (MOVLstoreidx4 [c] {sym} ptr idx val mem)
+(MOVWstoreconstidx1 [c] {sym} ptr (SHLLconst [1] idx) mem) -> (MOVWstoreconstidx2 [c] {sym} ptr idx mem)
+(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 [c+d] {sym} ptr idx mem)
+(MOVWloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVWloadidx1 [c+d] {sym} ptr idx mem)
+(MOVWloadidx2 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVWloadidx2 [c+d] {sym} ptr idx mem)
+(MOVLloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVLloadidx1 [c+d] {sym} ptr idx mem)
+(MOVLloadidx4 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVLloadidx4 [c+d] {sym} ptr idx mem)
+(MOVSSloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVSSloadidx1 [c+d] {sym} ptr idx mem)
+(MOVSSloadidx4 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVSSloadidx4 [c+d] {sym} ptr idx mem)
+(MOVSDloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVSDloadidx1 [c+d] {sym} ptr idx mem)
+(MOVSDloadidx8 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVSDloadidx8 [c+d] {sym} ptr idx mem)
+
+(MOVBstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVBstoreidx1 [c+d] {sym} ptr idx val mem)
+(MOVWstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVWstoreidx1 [c+d] {sym} ptr idx val mem)
+(MOVWstoreidx2 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVWstoreidx2 [c+d] {sym} ptr idx val mem)
+(MOVLstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVLstoreidx1 [c+d] {sym} ptr idx val mem)
+(MOVLstoreidx4 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVLstoreidx4 [c+d] {sym} ptr idx val mem)
+(MOVSSstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVSSstoreidx1 [c+d] {sym} ptr idx val mem)
+(MOVSSstoreidx4 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVSSstoreidx4 [c+d] {sym} ptr idx val mem)
+(MOVSDstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVSDstoreidx1 [c+d] {sym} ptr idx val mem)
+(MOVSDstoreidx8 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVSDstoreidx8 [c+d] {sym} ptr idx val mem)
+
+(MOVBloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVBloadidx1 [c+d] {sym} ptr idx mem)
+(MOVWloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVWloadidx1 [c+d] {sym} ptr idx mem)
+(MOVWloadidx2 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVWloadidx2 [c+2*d] {sym} ptr idx mem)
+(MOVLloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVLloadidx1 [c+d] {sym} ptr idx mem)
+(MOVLloadidx4 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVLloadidx4 [c+4*d] {sym} ptr idx mem)
+(MOVSSloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVSSloadidx1 [c+d] {sym} ptr idx mem)
+(MOVSSloadidx4 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVSSloadidx4 [c+4*d] {sym} ptr idx mem)
+(MOVSDloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVSDloadidx1 [c+d] {sym} ptr idx mem)
+(MOVSDloadidx8 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVSDloadidx8 [c+8*d] {sym} ptr idx mem)
+
+(MOVBstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVBstoreidx1 [c+d] {sym} ptr idx val mem)
+(MOVWstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVWstoreidx1 [c+d] {sym} ptr idx val mem)
+(MOVWstoreidx2 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVWstoreidx2 [c+2*d] {sym} ptr idx val mem)
+(MOVLstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVLstoreidx1 [c+d] {sym} ptr idx val mem)
+(MOVLstoreidx4 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVLstoreidx4 [c+4*d] {sym} ptr idx val mem)
+(MOVSSstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVSSstoreidx1 [c+d] {sym} ptr idx val mem)
+(MOVSSstoreidx4 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVSSstoreidx4 [c+4*d] {sym} ptr idx val mem)
+(MOVSDstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVSDstoreidx1 [c+d] {sym} ptr idx val mem)
+(MOVSDstoreidx8 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVSDstoreidx8 [c+8*d] {sym} ptr idx val 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)
+(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)
+(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)
+
+// fold LEALs together
+(LEAL [off1] {sym1} (LEAL [off2] {sym2} x)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+      (LEAL [off1+off2] {mergeSym(sym1,sym2)} x)
+
+// LEAL into LEAL1
+(LEAL1 [off1] {sym1} (LEAL [off2] {sym2} x) y) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB ->
+       (LEAL1 [off1+off2] {mergeSym(sym1,sym2)} x y)
+(LEAL1 [off1] {sym1} x (LEAL [off2] {sym2} y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && y.Op != OpSB ->
+       (LEAL1 [off1+off2] {mergeSym(sym1,sym2)} x y)
+
+// LEAL1 into LEAL
+(LEAL [off1] {sym1} (LEAL1 [off2] {sym2} x y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+       (LEAL1 [off1+off2] {mergeSym(sym1,sym2)} x y)
+
+// LEAL into LEAL[248]
+(LEAL2 [off1] {sym1} (LEAL [off2] {sym2} x) y) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB ->
+       (LEAL2 [off1+off2] {mergeSym(sym1,sym2)} x y)
+(LEAL4 [off1] {sym1} (LEAL [off2] {sym2} x) y) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB ->
+       (LEAL4 [off1+off2] {mergeSym(sym1,sym2)} x y)
+(LEAL8 [off1] {sym1} (LEAL [off2] {sym2} x) y) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB ->
+       (LEAL8 [off1+off2] {mergeSym(sym1,sym2)} x y)
+
+// LEAL[248] into LEAL
+(LEAL [off1] {sym1} (LEAL2 [off2] {sym2} x y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+      (LEAL2 [off1+off2] {mergeSym(sym1,sym2)} x y)
+(LEAL [off1] {sym1} (LEAL4 [off2] {sym2} x y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+      (LEAL4 [off1+off2] {mergeSym(sym1,sym2)} x y)
+(LEAL [off1] {sym1} (LEAL8 [off2] {sym2} x y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+      (LEAL8 [off1+off2] {mergeSym(sym1,sym2)} x y)
+
+// Absorb InvertFlags into branches.
+(LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
+(GT (InvertFlags cmp) yes no) -> (LT cmp yes no)
+(LE (InvertFlags cmp) yes no) -> (GE cmp yes no)
+(GE (InvertFlags cmp) yes no) -> (LE cmp yes no)
+(ULT (InvertFlags cmp) yes no) -> (UGT cmp yes no)
+(UGT (InvertFlags cmp) yes no) -> (ULT cmp yes no)
+(ULE (InvertFlags cmp) yes no) -> (UGE cmp yes no)
+(UGE (InvertFlags cmp) yes no) -> (ULE cmp yes no)
+(EQ (InvertFlags cmp) yes no) -> (EQ cmp yes no)
+(NE (InvertFlags cmp) yes no) -> (NE cmp yes no)
+
+// Constant comparisons.
+(CMPLconst (MOVLconst [x]) [y]) && int32(x)==int32(y) -> (FlagEQ)
+(CMPLconst (MOVLconst [x]) [y]) && int32(x)<int32(y) && uint32(x)<uint32(y) -> (FlagLT_ULT)
+(CMPLconst (MOVLconst [x]) [y]) && int32(x)<int32(y) && uint32(x)>uint32(y) -> (FlagLT_UGT)
+(CMPLconst (MOVLconst [x]) [y]) && int32(x)>int32(y) && uint32(x)<uint32(y) -> (FlagGT_ULT)
+(CMPLconst (MOVLconst [x]) [y]) && int32(x)>int32(y) && uint32(x)>uint32(y) -> (FlagGT_UGT)
+(CMPWconst (MOVLconst [x]) [y]) && int16(x)==int16(y) -> (FlagEQ)
+(CMPWconst (MOVLconst [x]) [y]) && int16(x)<int16(y) && uint16(x)<uint16(y) -> (FlagLT_ULT)
+(CMPWconst (MOVLconst [x]) [y]) && int16(x)<int16(y) && uint16(x)>uint16(y) -> (FlagLT_UGT)
+(CMPWconst (MOVLconst [x]) [y]) && int16(x)>int16(y) && uint16(x)<uint16(y) -> (FlagGT_ULT)
+(CMPWconst (MOVLconst [x]) [y]) && int16(x)>int16(y) && uint16(x)>uint16(y) -> (FlagGT_UGT)
+(CMPBconst (MOVLconst [x]) [y]) && int8(x)==int8(y) -> (FlagEQ)
+(CMPBconst (MOVLconst [x]) [y]) && int8(x)<int8(y) && uint8(x)<uint8(y) -> (FlagLT_ULT)
+(CMPBconst (MOVLconst [x]) [y]) && int8(x)<int8(y) && uint8(x)>uint8(y) -> (FlagLT_UGT)
+(CMPBconst (MOVLconst [x]) [y]) && int8(x)>int8(y) && uint8(x)<uint8(y) -> (FlagGT_ULT)
+(CMPBconst (MOVLconst [x]) [y]) && int8(x)>int8(y) && uint8(x)>uint8(y) -> (FlagGT_UGT)
+
+// Other known comparisons.
+(CMPLconst (SHRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n) -> (FlagLT_ULT)
+(CMPLconst (ANDLconst _ [m]) [n]) && 0 <= int32(m) && int32(m) < int32(n) -> (FlagLT_ULT)
+(CMPWconst (ANDLconst _ [m]) [n]) && 0 <= int16(m) && int16(m) < int16(n) -> (FlagLT_ULT)
+(CMPBconst (ANDLconst _ [m]) [n]) && 0 <= int8(m) && int8(m) < int8(n) -> (FlagLT_ULT)
+// TODO: DIVxU also.
+
+// Absorb flag constants into SBB ops.
+(SBBLcarrymask (FlagEQ)) -> (MOVLconst [0])
+(SBBLcarrymask (FlagLT_ULT)) -> (MOVLconst [-1])
+(SBBLcarrymask (FlagLT_UGT)) -> (MOVLconst [0])
+(SBBLcarrymask (FlagGT_ULT)) -> (MOVLconst [-1])
+(SBBLcarrymask (FlagGT_UGT)) -> (MOVLconst [0])
+
+// Absorb flag constants into branches.
+(EQ (FlagEQ) yes no) -> (First nil yes no)
+(EQ (FlagLT_ULT) yes no) -> (First nil no yes)
+(EQ (FlagLT_UGT) yes no) -> (First nil no yes)
+(EQ (FlagGT_ULT) yes no) -> (First nil no yes)
+(EQ (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(NE (FlagEQ) yes no) -> (First nil no yes)
+(NE (FlagLT_ULT) yes no) -> (First nil yes no)
+(NE (FlagLT_UGT) yes no) -> (First nil yes no)
+(NE (FlagGT_ULT) yes no) -> (First nil yes no)
+(NE (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(LT (FlagEQ) yes no) -> (First nil no yes)
+(LT (FlagLT_ULT) yes no) -> (First nil yes no)
+(LT (FlagLT_UGT) yes no) -> (First nil yes no)
+(LT (FlagGT_ULT) yes no) -> (First nil no yes)
+(LT (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(LE (FlagEQ) yes no) -> (First nil yes no)
+(LE (FlagLT_ULT) yes no) -> (First nil yes no)
+(LE (FlagLT_UGT) yes no) -> (First nil yes no)
+(LE (FlagGT_ULT) yes no) -> (First nil no yes)
+(LE (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(GT (FlagEQ) yes no) -> (First nil no yes)
+(GT (FlagLT_ULT) yes no) -> (First nil no yes)
+(GT (FlagLT_UGT) yes no) -> (First nil no yes)
+(GT (FlagGT_ULT) yes no) -> (First nil yes no)
+(GT (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(GE (FlagEQ) yes no) -> (First nil yes no)
+(GE (FlagLT_ULT) yes no) -> (First nil no yes)
+(GE (FlagLT_UGT) yes no) -> (First nil no yes)
+(GE (FlagGT_ULT) yes no) -> (First nil yes no)
+(GE (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(ULT (FlagEQ) yes no) -> (First nil no yes)
+(ULT (FlagLT_ULT) yes no) -> (First nil yes no)
+(ULT (FlagLT_UGT) yes no) -> (First nil no yes)
+(ULT (FlagGT_ULT) yes no) -> (First nil yes no)
+(ULT (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(ULE (FlagEQ) yes no) -> (First nil yes no)
+(ULE (FlagLT_ULT) yes no) -> (First nil yes no)
+(ULE (FlagLT_UGT) yes no) -> (First nil no yes)
+(ULE (FlagGT_ULT) yes no) -> (First nil yes no)
+(ULE (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(UGT (FlagEQ) yes no) -> (First nil no yes)
+(UGT (FlagLT_ULT) yes no) -> (First nil no yes)
+(UGT (FlagLT_UGT) yes no) -> (First nil yes no)
+(UGT (FlagGT_ULT) yes no) -> (First nil no yes)
+(UGT (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(UGE (FlagEQ) yes no) -> (First nil yes no)
+(UGE (FlagLT_ULT) yes no) -> (First nil no yes)
+(UGE (FlagLT_UGT) yes no) -> (First nil yes no)
+(UGE (FlagGT_ULT) yes no) -> (First nil no yes)
+(UGE (FlagGT_UGT) yes no) -> (First nil yes no)
+
+// Absorb flag constants into SETxx ops.
+(SETEQ (FlagEQ)) -> (MOVLconst [1])
+(SETEQ (FlagLT_ULT)) -> (MOVLconst [0])
+(SETEQ (FlagLT_UGT)) -> (MOVLconst [0])
+(SETEQ (FlagGT_ULT)) -> (MOVLconst [0])
+(SETEQ (FlagGT_UGT)) -> (MOVLconst [0])
+
+(SETNE (FlagEQ)) -> (MOVLconst [0])
+(SETNE (FlagLT_ULT)) -> (MOVLconst [1])
+(SETNE (FlagLT_UGT)) -> (MOVLconst [1])
+(SETNE (FlagGT_ULT)) -> (MOVLconst [1])
+(SETNE (FlagGT_UGT)) -> (MOVLconst [1])
+
+(SETL (FlagEQ)) -> (MOVLconst [0])
+(SETL (FlagLT_ULT)) -> (MOVLconst [1])
+(SETL (FlagLT_UGT)) -> (MOVLconst [1])
+(SETL (FlagGT_ULT)) -> (MOVLconst [0])
+(SETL (FlagGT_UGT)) -> (MOVLconst [0])
+
+(SETLE (FlagEQ)) -> (MOVLconst [1])
+(SETLE (FlagLT_ULT)) -> (MOVLconst [1])
+(SETLE (FlagLT_UGT)) -> (MOVLconst [1])
+(SETLE (FlagGT_ULT)) -> (MOVLconst [0])
+(SETLE (FlagGT_UGT)) -> (MOVLconst [0])
+
+(SETG (FlagEQ)) -> (MOVLconst [0])
+(SETG (FlagLT_ULT)) -> (MOVLconst [0])
+(SETG (FlagLT_UGT)) -> (MOVLconst [0])
+(SETG (FlagGT_ULT)) -> (MOVLconst [1])
+(SETG (FlagGT_UGT)) -> (MOVLconst [1])
+
+(SETGE (FlagEQ)) -> (MOVLconst [1])
+(SETGE (FlagLT_ULT)) -> (MOVLconst [0])
+(SETGE (FlagLT_UGT)) -> (MOVLconst [0])
+(SETGE (FlagGT_ULT)) -> (MOVLconst [1])
+(SETGE (FlagGT_UGT)) -> (MOVLconst [1])
+
+(SETB (FlagEQ)) -> (MOVLconst [0])
+(SETB (FlagLT_ULT)) -> (MOVLconst [1])
+(SETB (FlagLT_UGT)) -> (MOVLconst [0])
+(SETB (FlagGT_ULT)) -> (MOVLconst [1])
+(SETB (FlagGT_UGT)) -> (MOVLconst [0])
+
+(SETBE (FlagEQ)) -> (MOVLconst [1])
+(SETBE (FlagLT_ULT)) -> (MOVLconst [1])
+(SETBE (FlagLT_UGT)) -> (MOVLconst [0])
+(SETBE (FlagGT_ULT)) -> (MOVLconst [1])
+(SETBE (FlagGT_UGT)) -> (MOVLconst [0])
+
+(SETA (FlagEQ)) -> (MOVLconst [0])
+(SETA (FlagLT_ULT)) -> (MOVLconst [0])
+(SETA (FlagLT_UGT)) -> (MOVLconst [1])
+(SETA (FlagGT_ULT)) -> (MOVLconst [0])
+(SETA (FlagGT_UGT)) -> (MOVLconst [1])
+
+(SETAE (FlagEQ)) -> (MOVLconst [1])
+(SETAE (FlagLT_ULT)) -> (MOVLconst [0])
+(SETAE (FlagLT_UGT)) -> (MOVLconst [1])
+(SETAE (FlagGT_ULT)) -> (MOVLconst [0])
+(SETAE (FlagGT_UGT)) -> (MOVLconst [1])
+
+// Remove redundant *const ops
+(ADDLconst [c] x) && int32(c)==0 -> x
+(SUBLconst [c] x) && int32(c) == 0 -> x
+(ANDLconst [c] _) && int32(c)==0  -> (MOVLconst [0])
+(ANDLconst [c] x) && int32(c)==-1 -> x
+(ORLconst [c] x) && int32(c)==0   -> x
+(ORLconst [c] _) && int32(c)==-1  -> (MOVLconst [-1])
+(XORLconst [c] x) && int32(c)==0   -> x
+// TODO: since we got rid of the W/B versions, we might miss
+// things like (ANDLconst [0x100] x) which were formerly
+// (ANDBconst [0] x).  Probably doesn't happen very often.
+// If we cared, we might do:
+//  (ANDLconst <t> [c] x) && t.Size()==1 && int8(x)==0 -> (MOVLconst [0])
+
+// Convert constant subtracts to constant adds
+(SUBLconst [c] x) -> (ADDLconst [int64(int32(-c))] x)
+
+// generic constant folding
+// TODO: more of this
+(ADDLconst [c] (MOVLconst [d])) -> (MOVLconst [int64(int32(c+d))])
+(ADDLconst [c] (ADDLconst [d] x)) -> (ADDLconst [int64(int32(c+d))] x)
+(SARLconst [c] (MOVLconst [d])) -> (MOVLconst [d>>uint64(c)])
+(SARWconst [c] (MOVLconst [d])) -> (MOVLconst [d>>uint64(c)])
+(SARBconst [c] (MOVLconst [d])) -> (MOVLconst [d>>uint64(c)])
+(NEGL (MOVLconst [c])) -> (MOVLconst [int64(int32(-c))])
+(MULLconst [c] (MOVLconst [d])) -> (MOVLconst [int64(int32(c*d))])
+(ANDLconst [c] (MOVLconst [d])) -> (MOVLconst [c&d])
+(ORLconst [c] (MOVLconst [d])) -> (MOVLconst [c|d])
+(XORLconst [c] (MOVLconst [d])) -> (MOVLconst [c^d])
+(NOTL (MOVLconst [c])) -> (MOVLconst [^c])
+
+// generic simplifications
+// TODO: more of this
+(ADDL x (NEGL y)) -> (SUBL x y)
+(SUBL x x) -> (MOVLconst [0])
+(ANDL x x) -> x
+(ORL x x) -> x
+(XORL x x) -> (MOVLconst [0])
+
+// checking AND against 0.
+(CMPLconst (ANDL x y) [0]) -> (TESTL x y)
+(CMPWconst (ANDL x y) [0]) -> (TESTW x y)
+(CMPBconst (ANDL x y) [0]) -> (TESTB x y)
+(CMPLconst (ANDLconst [c] x) [0]) -> (TESTLconst [c] x)
+(CMPWconst (ANDLconst [c] x) [0]) -> (TESTWconst [int64(int16(c))] x)
+(CMPBconst (ANDLconst [c] x) [0]) -> (TESTBconst [int64(int8(c))] x)
+
+// TEST %reg,%reg is shorter than CMP
+(CMPLconst x [0]) -> (TESTL x x)
+(CMPWconst x [0]) -> (TESTW x x)
+(CMPBconst x [0]) -> (TESTB x x)
+
+// Combining byte loads into larger (unaligned) loads.
+// There are many ways these combinations could occur.  This is
+// designed to match the way encoding/binary.LittleEndian does it.
+(ORL                  x0:(MOVBload [i]   {s} p mem)
+    s0:(SHLLconst [8] x1:(MOVBload [i+1] {s} p mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && s0.Uses == 1
+  && mergePoint(b,x0,x1) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(s0)
+  -> @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem)
+
+(ORL o0:(ORL
+                       x0:(MOVWload [i]   {s} p mem)
+    s0:(SHLLconst [16] x1:(MOVBload [i+2] {s} p mem)))
+    s1:(SHLLconst [24] x2:(MOVBload [i+3] {s} p mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && o0.Uses == 1
+  && mergePoint(b,x0,x1,x2) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(o0)
+  -> @mergePoint(b,x0,x1,x2) (MOVLload [i] {s} p mem)
+
+(ORL                  x0:(MOVBloadidx1 [i]   {s} p idx mem)
+    s0:(SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && s0.Uses == 1
+  && mergePoint(b,x0,x1) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(s0)
+  -> @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i] {s} p idx mem)
+
+(ORL o0:(ORL
+                       x0:(MOVWloadidx1 [i]   {s} p idx mem)
+    s0:(SHLLconst [16] x1:(MOVBloadidx1 [i+2] {s} p idx mem)))
+    s1:(SHLLconst [24] x2:(MOVBloadidx1 [i+3] {s} p idx mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && o0.Uses == 1
+  && mergePoint(b,x0,x1,x2) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(o0)
+  -> @mergePoint(b,x0,x1,x2) (MOVLloadidx1 <v.Type> [i] {s} p idx mem)
+
+// Combine constant stores into larger (unaligned) stores.
+(MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {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)
+
+(MOVBstoreconstidx1 [c] {s} p i x:(MOVBstoreconstidx1 [a] {s} p i mem))
+  && x.Uses == 1
+  && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
+  && clobber(x)
+  -> (MOVWstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p i mem)
+(MOVWstoreconstidx1 [c] {s} p i x:(MOVWstoreconstidx1 [a] {s} p i mem))
+  && x.Uses == 1
+  && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
+  && clobber(x)
+  -> (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p i mem)
+
+(MOVWstoreconstidx2 [c] {s} p i x:(MOVWstoreconstidx2 [a] {s} p i mem))
+  && x.Uses == 1
+  && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
+  && clobber(x)
+  -> (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))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWstore [i-1] {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)
+  -> (MOVWstore [i-1] {s} p w0 mem)
+(MOVWstore [i] {s} p (SHRLconst [16] w) x:(MOVWstore [i-2] {s} p w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVLstore [i-2] {s} p w mem)
+(MOVWstore [i] {s} p (SHRLconst [j] w) x:(MOVWstore [i-2] {s} p w0:(SHRLconst [j-16] w) mem))
+  && x.Uses == 1
+  && 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))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWstoreidx1 [i-1] {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)
+  -> (MOVWstoreidx1 [i-1] {s} p idx w0 mem)
+(MOVWstoreidx1 [i] {s} p idx (SHRLconst [16] w) x:(MOVWstoreidx1 [i-2] {s} p idx w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVLstoreidx1 [i-2] {s} p idx w mem)
+(MOVWstoreidx1 [i] {s} p idx (SHRLconst [j] w) x:(MOVWstoreidx1 [i-2] {s} p idx w0:(SHRLconst [j-16] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVLstoreidx1 [i-2] {s} p idx w0 mem)
+
+(MOVWstoreidx2 [i] {s} p idx (SHRLconst [16] w) x:(MOVWstoreidx2 [i-2] {s} p idx w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVLstoreidx1 [i-2] {s} p (SHLLconst <idx.Type> [1] idx) w mem)
+(MOVWstoreidx2 [i] {s} p idx (SHRLconst [j] w) x:(MOVWstoreidx2 [i-2] {s} p idx w0:(SHRLconst [j-16] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVLstoreidx1 [i-2] {s} p (SHLLconst <idx.Type> [1] idx) w0 mem)
+
+// For PIC, break floating-point constant loading into two instructions so we have
+// a register to use for holding the address of the constant pool entry.
+(MOVSSconst [c]) && config.ctxt.Flag_shared -> (MOVSSconst2 (MOVSSconst1 [c]))
+(MOVSDconst [c]) && config.ctxt.Flag_shared -> (MOVSDconst2 (MOVSDconst1 [c]))
diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go
new file mode 100644
index 0000000..d09f497
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/386Ops.go
@@ -0,0 +1,506 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "strings"
+
+// Notes:
+//  - Integer types live in the low portion of registers. Upper portions are junk.
+//  - Boolean types use the low-order byte of a register. 0=false, 1=true.
+//    Upper bytes are junk.
+//  - Floating-point types live in the low natural slot of an sse2 register.
+//    Unused portions are junk.
+//  - We do not use AH,BH,CH,DH registers.
+//  - When doing sub-register operations, we try to write the whole
+//    destination register to avoid a partial-register write.
+//  - Unused portions of AuxInt (or the Val portion of ValAndOff) are
+//    filled by sign-extending the used portion.  Users of AuxInt which interpret
+//    AuxInt as unsigned (e.g. shifts) must be careful.
+
+// Suffixes encode the bit width of various instructions.
+// L (long word) = 32 bit
+// W (word)      = 16 bit
+// B (byte)      = 8 bit
+
+// copied from ../../x86/reg.go
+var regNames386 = []string{
+	"AX",
+	"CX",
+	"DX",
+	"BX",
+	"SP",
+	"BP",
+	"SI",
+	"DI",
+	"X0",
+	"X1",
+	"X2",
+	"X3",
+	"X4",
+	"X5",
+	"X6",
+	"X7",
+
+	// pseudo-registers
+	"SB",
+}
+
+// Notes on 387 support.
+//  - The 387 has a weird stack-register setup for floating-point registers.
+//    We use these registers when SSE registers are not available (when GO386=387).
+//  - We use the same register names (X0-X7) but they refer to the 387
+//    floating-point registers. That way, most of the SSA backend is unchanged.
+//  - The instruction generation pass maintains an SSE->387 register mapping.
+//    This mapping is updated whenever the FP stack is pushed or popped so that
+//    we can always find a given SSE register even when the TOS pointer has changed.
+//  - To facilitate the mapping from SSE to 387, we enforce that
+//    every basic block starts and ends with an empty floating-point stack.
+
+func init() {
+	// Make map from reg names to reg integers.
+	if len(regNames386) > 64 {
+		panic("too many registers")
+	}
+	num := map[string]int{}
+	for i, name := range regNames386 {
+		num[name] = i
+	}
+	buildReg := func(s string) regMask {
+		m := regMask(0)
+		for _, r := range strings.Split(s, " ") {
+			if n, ok := num[r]; ok {
+				m |= regMask(1) << uint(n)
+				continue
+			}
+			panic("register " + r + " not found")
+		}
+		return m
+	}
+
+	// Common individual register masks
+	var (
+		ax         = buildReg("AX")
+		cx         = buildReg("CX")
+		dx         = buildReg("DX")
+		gp         = buildReg("AX CX DX BX BP SI DI")
+		fp         = buildReg("X0 X1 X2 X3 X4 X5 X6 X7")
+		gpsp       = gp | buildReg("SP")
+		gpspsb     = gpsp | buildReg("SB")
+		callerSave = gp | fp
+	)
+	// Common slices of register masks
+	var (
+		gponly = []regMask{gp}
+		fponly = []regMask{fp}
+	)
+
+	// Common regInfo
+	var (
+		gp01      = regInfo{inputs: nil, outputs: gponly}
+		gp11      = regInfo{inputs: []regMask{gp}, outputs: gponly}
+		gp11sp    = regInfo{inputs: []regMask{gpsp}, outputs: gponly}
+		gp11sb    = regInfo{inputs: []regMask{gpspsb}, outputs: gponly}
+		gp21      = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
+		gp11carry = regInfo{inputs: []regMask{gp}, outputs: []regMask{gp, 0}}
+		gp21carry = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp, 0}}
+		gp1carry1 = regInfo{inputs: []regMask{gp}, outputs: gponly}
+		gp2carry1 = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
+		gp21sp    = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly}
+		gp21sb    = regInfo{inputs: []regMask{gpspsb, gpsp}, outputs: gponly}
+		gp21shift = regInfo{inputs: []regMask{gp, cx}, outputs: []regMask{gp}}
+		gp11div   = regInfo{inputs: []regMask{ax, gpsp &^ dx}, outputs: []regMask{ax}, clobbers: dx}
+		gp21hmul  = regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{dx}, clobbers: ax}
+		gp11mod   = regInfo{inputs: []regMask{ax, gpsp &^ dx}, outputs: []regMask{dx}, clobbers: ax}
+		gp21mul   = regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{dx, ax}}
+
+		gp2flags = regInfo{inputs: []regMask{gpsp, gpsp}}
+		gp1flags = regInfo{inputs: []regMask{gpsp}}
+		flagsgp  = regInfo{inputs: nil, outputs: gponly}
+
+		readflags = regInfo{inputs: nil, outputs: gponly}
+		flagsgpax = regInfo{inputs: nil, clobbers: ax, outputs: []regMask{gp &^ ax}}
+
+		gpload    = regInfo{inputs: []regMask{gpspsb, 0}, outputs: gponly}
+		gploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: gponly}
+
+		gpstore         = regInfo{inputs: []regMask{gpspsb, gpsp, 0}}
+		gpstoreconst    = regInfo{inputs: []regMask{gpspsb, 0}}
+		gpstoreidx      = regInfo{inputs: []regMask{gpspsb, gpsp, gpsp, 0}}
+		gpstoreconstidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}}
+
+		fp01     = regInfo{inputs: nil, outputs: fponly}
+		fp21     = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
+		fpgp     = regInfo{inputs: fponly, outputs: gponly}
+		gpfp     = regInfo{inputs: gponly, outputs: fponly}
+		fp11     = regInfo{inputs: fponly, outputs: fponly}
+		fp2flags = regInfo{inputs: []regMask{fp, fp}}
+
+		fpload    = regInfo{inputs: []regMask{gpspsb, 0}, outputs: fponly}
+		fploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: fponly}
+
+		fpstore    = regInfo{inputs: []regMask{gpspsb, fp, 0}}
+		fpstoreidx = regInfo{inputs: []regMask{gpspsb, gpsp, fp, 0}}
+	)
+
+	var _386ops = []opData{
+		// fp ops
+		{name: "ADDSS", argLength: 2, reg: fp21, asm: "ADDSS", commutative: true, resultInArg0: true, usesScratch: true}, // fp32 add
+		{name: "ADDSD", argLength: 2, reg: fp21, asm: "ADDSD", commutative: true, resultInArg0: true},                    // fp64 add
+		{name: "SUBSS", argLength: 2, reg: fp21, asm: "SUBSS", resultInArg0: true, usesScratch: true},                    // fp32 sub
+		{name: "SUBSD", argLength: 2, reg: fp21, asm: "SUBSD", resultInArg0: true},                                       // fp64 sub
+		{name: "MULSS", argLength: 2, reg: fp21, asm: "MULSS", commutative: true, resultInArg0: true, usesScratch: true}, // fp32 mul
+		{name: "MULSD", argLength: 2, reg: fp21, asm: "MULSD", commutative: true, resultInArg0: true},                    // fp64 mul
+		{name: "DIVSS", argLength: 2, reg: fp21, asm: "DIVSS", resultInArg0: true, usesScratch: true},                    // fp32 div
+		{name: "DIVSD", argLength: 2, reg: fp21, asm: "DIVSD", resultInArg0: true},                                       // fp64 div
+
+		{name: "MOVSSload", argLength: 2, reg: fpload, asm: "MOVSS", aux: "SymOff", faultOnNilArg0: true}, // fp32 load
+		{name: "MOVSDload", argLength: 2, reg: fpload, asm: "MOVSD", aux: "SymOff", faultOnNilArg0: true}, // fp64 load
+		{name: "MOVSSconst", reg: fp01, asm: "MOVSS", aux: "Float32", rematerializeable: true},            // fp32 constant
+		{name: "MOVSDconst", reg: fp01, asm: "MOVSD", aux: "Float64", rematerializeable: true},            // fp64 constant
+		{name: "MOVSSloadidx1", argLength: 3, reg: fploadidx, asm: "MOVSS", aux: "SymOff"},                // fp32 load indexed by i
+		{name: "MOVSSloadidx4", argLength: 3, reg: fploadidx, asm: "MOVSS", aux: "SymOff"},                // fp32 load indexed by 4*i
+		{name: "MOVSDloadidx1", argLength: 3, reg: fploadidx, asm: "MOVSD", aux: "SymOff"},                // fp64 load indexed by i
+		{name: "MOVSDloadidx8", argLength: 3, reg: fploadidx, asm: "MOVSD", aux: "SymOff"},                // fp64 load indexed by 8*i
+
+		{name: "MOVSSstore", argLength: 3, reg: fpstore, asm: "MOVSS", aux: "SymOff", faultOnNilArg0: true}, // fp32 store
+		{name: "MOVSDstore", argLength: 3, reg: fpstore, asm: "MOVSD", aux: "SymOff", faultOnNilArg0: true}, // fp64 store
+		{name: "MOVSSstoreidx1", argLength: 4, reg: fpstoreidx, asm: "MOVSS", aux: "SymOff"},                // fp32 indexed by i store
+		{name: "MOVSSstoreidx4", argLength: 4, reg: fpstoreidx, asm: "MOVSS", aux: "SymOff"},                // fp32 indexed by 4i store
+		{name: "MOVSDstoreidx1", argLength: 4, reg: fpstoreidx, asm: "MOVSD", aux: "SymOff"},                // fp64 indexed by i store
+		{name: "MOVSDstoreidx8", argLength: 4, reg: fpstoreidx, asm: "MOVSD", aux: "SymOff"},                // fp64 indexed by 8i store
+
+		// binary ops
+		{name: "ADDL", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true, clobberFlags: true},                // arg0 + arg1
+		{name: "ADDLconst", argLength: 1, reg: gp11sp, asm: "ADDL", aux: "Int32", typ: "UInt32", clobberFlags: true}, // arg0 + auxint
+
+		{name: "ADDLcarry", argLength: 2, reg: gp21carry, asm: "ADDL", commutative: true, resultInArg0: true},                // arg0 + arg1, generates <carry,result> pair
+		{name: "ADDLconstcarry", argLength: 1, reg: gp11carry, asm: "ADDL", aux: "Int32", resultInArg0: true},                // arg0 + auxint, generates <carry,result> pair
+		{name: "ADCL", argLength: 3, reg: gp2carry1, asm: "ADCL", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0+arg1+carry(arg2), where arg2 is flags
+		{name: "ADCLconst", argLength: 2, reg: gp1carry1, asm: "ADCL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0+auxint+carry(arg1), where arg1 is flags
+
+		{name: "SUBL", argLength: 2, reg: gp21, asm: "SUBL", resultInArg0: true, clobberFlags: true},                    // arg0 - arg1
+		{name: "SUBLconst", argLength: 1, reg: gp11, asm: "SUBL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 - auxint
+
+		{name: "SUBLcarry", argLength: 2, reg: gp21carry, asm: "SUBL", resultInArg0: true},                                   // arg0-arg1, generates <borrow,result> pair
+		{name: "SUBLconstcarry", argLength: 1, reg: gp11carry, asm: "SUBL", aux: "Int32", resultInArg0: true},                // arg0-auxint, generates <borrow,result> pair
+		{name: "SBBL", argLength: 3, reg: gp2carry1, asm: "SBBL", resultInArg0: true, clobberFlags: true},                    // arg0-arg1-borrow(arg2), where arg2 is flags
+		{name: "SBBLconst", argLength: 2, reg: gp1carry1, asm: "SBBL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0-auxint-borrow(arg1), where arg1 is flags
+
+		{name: "MULL", argLength: 2, reg: gp21, asm: "IMULL", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 * arg1
+		{name: "MULLconst", argLength: 1, reg: gp11, asm: "IMULL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 * auxint
+
+		{name: "HMULL", argLength: 2, reg: gp21hmul, asm: "IMULL", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULLU", argLength: 2, reg: gp21hmul, asm: "MULL", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULW", argLength: 2, reg: gp21hmul, asm: "IMULW", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULB", argLength: 2, reg: gp21hmul, asm: "IMULB", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULWU", argLength: 2, reg: gp21hmul, asm: "MULW", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULBU", argLength: 2, reg: gp21hmul, asm: "MULB", clobberFlags: true}, // (arg0 * arg1) >> width
+
+		{name: "MULLQU", argLength: 2, reg: gp21mul, asm: "MULL", clobberFlags: true}, // arg0 * arg1, high 32 in result[0], low 32 in result[1]
+
+		{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
+
+		{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: "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
+
+		{name: "ORL", argLength: 2, reg: gp21, asm: "ORL", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 | arg1
+		{name: "ORLconst", argLength: 1, reg: gp11, asm: "ORL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 | auxint
+
+		{name: "XORL", argLength: 2, reg: gp21, asm: "XORL", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 ^ arg1
+		{name: "XORLconst", argLength: 1, reg: gp11, asm: "XORL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 ^ auxint
+
+		{name: "CMPL", argLength: 2, reg: gp2flags, asm: "CMPL", typ: "Flags"},                    // arg0 compare to arg1
+		{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"},                    // arg0 compare to arg1
+		{name: "CMPB", argLength: 2, reg: gp2flags, asm: "CMPB", typ: "Flags"},                    // arg0 compare to arg1
+		{name: "CMPLconst", argLength: 1, reg: gp1flags, asm: "CMPL", typ: "Flags", aux: "Int32"}, // arg0 compare to auxint
+		{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", typ: "Flags", aux: "Int16"}, // arg0 compare to auxint
+		{name: "CMPBconst", argLength: 1, reg: gp1flags, asm: "CMPB", typ: "Flags", aux: "Int8"},  // arg0 compare to auxint
+
+		{name: "UCOMISS", argLength: 2, reg: fp2flags, asm: "UCOMISS", typ: "Flags", usesScratch: true}, // arg0 compare to arg1, f32
+		{name: "UCOMISD", argLength: 2, reg: fp2flags, asm: "UCOMISD", typ: "Flags", usesScratch: true}, // arg0 compare to arg1, f64
+
+		{name: "TESTL", argLength: 2, reg: gp2flags, asm: "TESTL", typ: "Flags"},                    // (arg0 & arg1) compare to 0
+		{name: "TESTW", argLength: 2, reg: gp2flags, asm: "TESTW", typ: "Flags"},                    // (arg0 & arg1) compare to 0
+		{name: "TESTB", argLength: 2, reg: gp2flags, asm: "TESTB", typ: "Flags"},                    // (arg0 & arg1) compare to 0
+		{name: "TESTLconst", argLength: 1, reg: gp1flags, asm: "TESTL", typ: "Flags", aux: "Int32"}, // (arg0 & auxint) compare to 0
+		{name: "TESTWconst", argLength: 1, reg: gp1flags, asm: "TESTW", typ: "Flags", aux: "Int16"}, // (arg0 & auxint) compare to 0
+		{name: "TESTBconst", argLength: 1, reg: gp1flags, asm: "TESTB", typ: "Flags", aux: "Int8"},  // (arg0 & auxint) compare to 0
+
+		{name: "SHLL", argLength: 2, reg: gp21shift, asm: "SHLL", resultInArg0: true, clobberFlags: true},               // arg0 << arg1, shift amount is mod 32
+		{name: "SHLLconst", argLength: 1, reg: gp11, asm: "SHLL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 << auxint, shift amount 0-31
+		// Note: x86 is weird, the 16 and 8 byte shifts still use all 5 bits of shift amount!
+
+		{name: "SHRL", argLength: 2, reg: gp21shift, asm: "SHRL", resultInArg0: true, clobberFlags: true},               // unsigned arg0 >> arg1, shift amount is mod 32
+		{name: "SHRW", argLength: 2, reg: gp21shift, asm: "SHRW", resultInArg0: true, clobberFlags: true},               // unsigned arg0 >> arg1, shift amount is mod 32
+		{name: "SHRB", argLength: 2, reg: gp21shift, asm: "SHRB", resultInArg0: true, clobberFlags: true},               // unsigned arg0 >> arg1, shift amount is mod 32
+		{name: "SHRLconst", argLength: 1, reg: gp11, asm: "SHRL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> auxint, shift amount 0-31
+		{name: "SHRWconst", argLength: 1, reg: gp11, asm: "SHRW", aux: "Int16", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> auxint, shift amount 0-31
+		{name: "SHRBconst", argLength: 1, reg: gp11, asm: "SHRB", aux: "Int8", resultInArg0: true, clobberFlags: true},  // unsigned arg0 >> auxint, shift amount 0-31
+
+		{name: "SARL", argLength: 2, reg: gp21shift, asm: "SARL", resultInArg0: true, clobberFlags: true},               // signed arg0 >> arg1, shift amount is mod 32
+		{name: "SARW", argLength: 2, reg: gp21shift, asm: "SARW", resultInArg0: true, clobberFlags: true},               // signed arg0 >> arg1, shift amount is mod 32
+		{name: "SARB", argLength: 2, reg: gp21shift, asm: "SARB", resultInArg0: true, clobberFlags: true},               // signed arg0 >> arg1, shift amount is mod 32
+		{name: "SARLconst", argLength: 1, reg: gp11, asm: "SARL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-31
+		{name: "SARWconst", argLength: 1, reg: gp11, asm: "SARW", aux: "Int16", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-31
+		{name: "SARBconst", argLength: 1, reg: gp11, asm: "SARB", aux: "Int8", resultInArg0: true, clobberFlags: true},  // signed arg0 >> auxint, shift amount 0-31
+
+		{name: "ROLLconst", argLength: 1, reg: gp11, asm: "ROLL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 rotate left auxint, rotate amount 0-31
+		{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
+
+		// unary ops
+		{name: "NEGL", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true, clobberFlags: true}, // -arg0
+
+		{name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true, clobberFlags: true}, // ^arg0
+
+		{name: "BSFL", argLength: 1, reg: gp11, asm: "BSFL", clobberFlags: true}, // arg0 # of low-order zeroes ; undef if zero
+		{name: "BSFW", argLength: 1, reg: gp11, asm: "BSFW", clobberFlags: true}, // arg0 # of low-order zeroes ; undef if zero
+
+		{name: "BSRL", argLength: 1, reg: gp11, asm: "BSRL", clobberFlags: true}, // arg0 # of high-order zeroes ; undef if zero
+		{name: "BSRW", argLength: 1, reg: gp11, asm: "BSRW", clobberFlags: true}, // arg0 # of high-order zeroes ; undef if zero
+
+		{name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true, clobberFlags: true}, // arg0 swap bytes
+
+		{name: "SQRTSD", argLength: 1, reg: fp11, asm: "SQRTSD"}, // sqrt(arg0)
+
+		{name: "SBBLcarrymask", argLength: 1, reg: flagsgp, asm: "SBBL"}, // (int32)(-1) if carry is set, 0 if carry is clear.
+		// Note: SBBW and SBBB are subsumed by SBBL
+
+		{name: "SETEQ", argLength: 1, reg: readflags, asm: "SETEQ"}, // extract == condition from arg0
+		{name: "SETNE", argLength: 1, reg: readflags, asm: "SETNE"}, // extract != condition from arg0
+		{name: "SETL", argLength: 1, reg: readflags, asm: "SETLT"},  // extract signed < condition from arg0
+		{name: "SETLE", argLength: 1, reg: readflags, asm: "SETLE"}, // extract signed <= condition from arg0
+		{name: "SETG", argLength: 1, reg: readflags, asm: "SETGT"},  // extract signed > condition from arg0
+		{name: "SETGE", argLength: 1, reg: readflags, asm: "SETGE"}, // extract signed >= condition from arg0
+		{name: "SETB", argLength: 1, reg: readflags, asm: "SETCS"},  // extract unsigned < condition from arg0
+		{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
+		// 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.
+		{name: "SETEQF", argLength: 1, reg: flagsgpax, asm: "SETEQ", clobberFlags: true}, // extract == condition from arg0
+		{name: "SETNEF", argLength: 1, reg: flagsgpax, asm: "SETNE", clobberFlags: true}, // extract != condition from arg0
+		{name: "SETORD", argLength: 1, reg: flagsgp, asm: "SETPC"},                       // extract "ordered" (No Nan present) condition from arg0
+		{name: "SETNAN", argLength: 1, reg: flagsgp, asm: "SETPS"},                       // extract "unordered" (Nan present) condition from arg0
+
+		{name: "SETGF", argLength: 1, reg: flagsgp, asm: "SETHI"},  // extract floating > condition from arg0
+		{name: "SETGEF", argLength: 1, reg: flagsgp, asm: "SETCC"}, // extract floating >= condition from arg0
+
+		{name: "MOVBLSX", argLength: 1, reg: gp11, asm: "MOVBLSX"}, // sign extend arg0 from int8 to int32
+		{name: "MOVBLZX", argLength: 1, reg: gp11, asm: "MOVBLZX"}, // zero extend arg0 from int8 to int32
+		{name: "MOVWLSX", argLength: 1, reg: gp11, asm: "MOVWLSX"}, // sign extend arg0 from int16 to int32
+		{name: "MOVWLZX", argLength: 1, reg: gp11, asm: "MOVWLZX"}, // zero extend arg0 from int16 to int32
+
+		{name: "MOVLconst", reg: gp01, asm: "MOVL", typ: "UInt32", aux: "Int32", rematerializeable: true}, // 32 low bits of auxint
+
+		{name: "CVTTSD2SL", argLength: 1, reg: fpgp, asm: "CVTTSD2SL", usesScratch: true}, // convert float64 to int32
+		{name: "CVTTSS2SL", argLength: 1, reg: fpgp, asm: "CVTTSS2SL", usesScratch: true}, // convert float32 to int32
+		{name: "CVTSL2SS", argLength: 1, reg: gpfp, asm: "CVTSL2SS", usesScratch: true},   // convert int32 to float32
+		{name: "CVTSL2SD", argLength: 1, reg: gpfp, asm: "CVTSL2SD", usesScratch: true},   // convert int32 to float64
+		{name: "CVTSD2SS", argLength: 1, reg: fp11, asm: "CVTSD2SS", usesScratch: true},   // convert float64 to float32
+		{name: "CVTSS2SD", argLength: 1, reg: fp11, asm: "CVTSS2SD"},                      // convert float32 to float64
+
+		{name: "PXOR", argLength: 2, reg: fp21, asm: "PXOR", commutative: true, resultInArg0: true}, // exclusive or, applied to X regs for float negation.
+
+		{name: "LEAL", argLength: 1, reg: gp11sb, aux: "SymOff", rematerializeable: true}, // arg0 + auxint + offset encoded in aux
+		{name: "LEAL1", argLength: 2, reg: gp21sb, aux: "SymOff"},                         // arg0 + arg1 + auxint + aux
+		{name: "LEAL2", argLength: 2, reg: gp21sb, aux: "SymOff"},                         // arg0 + 2*arg1 + auxint + aux
+		{name: "LEAL4", argLength: 2, reg: gp21sb, aux: "SymOff"},                         // arg0 + 4*arg1 + auxint + aux
+		{name: "LEAL8", argLength: 2, reg: gp21sb, aux: "SymOff"},                         // arg0 + 8*arg1 + auxint + aux
+		// Note: LEAL{1,2,4,8} must not have OpSB as either argument.
+
+		// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
+		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVBLZX", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true},  // load byte from arg0+auxint+aux. arg1=mem.  Zero extend.
+		{name: "MOVBLSXload", argLength: 2, reg: gpload, asm: "MOVBLSX", aux: "SymOff", faultOnNilArg0: true},             // ditto, sign extend to int32
+		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVWLZX", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true}, // load 2 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
+		{name: "MOVWLSXload", argLength: 2, reg: gpload, asm: "MOVWLSX", aux: "SymOff", faultOnNilArg0: true},             // ditto, sign extend to int32
+		{name: "MOVLload", argLength: 2, reg: gpload, asm: "MOVL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true},    // load 4 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
+		{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},     // store byte in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},     // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVLstore", argLength: 3, reg: gpstore, asm: "MOVL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},     // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
+
+		// indexed loads/stores
+		{name: "MOVBloadidx1", argLength: 3, reg: gploadidx, asm: "MOVBLZX", aux: "SymOff"}, // load a byte from arg0+arg1+auxint+aux. arg2=mem
+		{name: "MOVWloadidx1", argLength: 3, reg: gploadidx, asm: "MOVWLZX", aux: "SymOff"}, // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem
+		{name: "MOVWloadidx2", argLength: 3, reg: gploadidx, asm: "MOVWLZX", aux: "SymOff"}, // load 2 bytes from arg0+2*arg1+auxint+aux. arg2=mem
+		{name: "MOVLloadidx1", argLength: 3, reg: gploadidx, asm: "MOVL", aux: "SymOff"},    // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem
+		{name: "MOVLloadidx4", argLength: 3, reg: gploadidx, asm: "MOVL", aux: "SymOff"},    // load 4 bytes from arg0+4*arg1+auxint+aux. arg2=mem
+		// TODO: sign-extending indexed loads
+		{name: "MOVBstoreidx1", argLength: 4, reg: gpstoreidx, asm: "MOVB", aux: "SymOff"}, // store byte in arg2 to arg0+arg1+auxint+aux. arg3=mem
+		{name: "MOVWstoreidx1", argLength: 4, reg: gpstoreidx, asm: "MOVW", aux: "SymOff"}, // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
+		{name: "MOVWstoreidx2", argLength: 4, reg: gpstoreidx, asm: "MOVW", aux: "SymOff"}, // store 2 bytes in arg2 to arg0+2*arg1+auxint+aux. arg3=mem
+		{name: "MOVLstoreidx1", argLength: 4, reg: gpstoreidx, asm: "MOVL", aux: "SymOff"}, // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
+		{name: "MOVLstoreidx4", argLength: 4, reg: gpstoreidx, asm: "MOVL", aux: "SymOff"}, // store 4 bytes in arg2 to arg0+4*arg1+auxint+aux. arg3=mem
+		// TODO: add size-mismatched indexed loads, like MOVBstoreidx4.
+
+		// For storeconst ops, the AuxInt field encodes both
+		// the value to store and an address offset of the store.
+		// Cast AuxInt to a ValAndOff to extract Val and Off fields.
+		{name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux.  arg1=mem
+		{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true}, // store low 2 bytes of ...
+		{name: "MOVLstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVL", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true}, // store low 4 bytes of ...
+
+		{name: "MOVBstoreconstidx1", argLength: 3, reg: gpstoreconstidx, asm: "MOVB", aux: "SymValAndOff", typ: "Mem"}, // store low byte of ValAndOff(AuxInt).Val() to arg0+1*arg1+ValAndOff(AuxInt).Off()+aux.  arg2=mem
+		{name: "MOVWstoreconstidx1", argLength: 3, reg: gpstoreconstidx, asm: "MOVW", aux: "SymValAndOff", typ: "Mem"}, // store low 2 bytes of ... arg1 ...
+		{name: "MOVWstoreconstidx2", argLength: 3, reg: gpstoreconstidx, asm: "MOVW", aux: "SymValAndOff", typ: "Mem"}, // store low 2 bytes of ... 2*arg1 ...
+		{name: "MOVLstoreconstidx1", argLength: 3, reg: gpstoreconstidx, asm: "MOVL", aux: "SymValAndOff", typ: "Mem"}, // store low 4 bytes of ... arg1 ...
+		{name: "MOVLstoreconstidx4", argLength: 3, reg: gpstoreconstidx, asm: "MOVL", aux: "SymValAndOff", typ: "Mem"}, // store low 4 bytes of ... 4*arg1 ...
+
+		// arg0 = pointer to start of memory to zero
+		// arg1 = value to store (will always be zero)
+		// arg2 = mem
+		// auxint = offset into duffzero code to start executing
+		// returns mem
+		{
+			name:      "DUFFZERO",
+			aux:       "Int64",
+			argLength: 3,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("DI"), buildReg("AX")},
+				clobbers: buildReg("DI CX"),
+				// Note: CX is only clobbered when dynamic linking.
+			},
+		},
+
+		// arg0 = address of memory to zero
+		// arg1 = # of 4-byte words to zero
+		// arg2 = value to store (will always be zero)
+		// arg3 = mem
+		// returns mem
+		{
+			name:      "REPSTOSL",
+			argLength: 4,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("DI"), buildReg("CX"), buildReg("AX")},
+				clobbers: buildReg("DI CX"),
+			},
+		},
+
+		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true},                                             // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
+		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                               // call deferproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                  // call newproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                        // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
+
+		// arg0 = destination pointer
+		// arg1 = source pointer
+		// arg2 = mem
+		// auxint = offset from duffcopy symbol to call
+		// returns memory
+		{
+			name:      "DUFFCOPY",
+			aux:       "Int64",
+			argLength: 3,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("DI"), buildReg("SI")},
+				clobbers: buildReg("DI SI CX"), // uses CX as a temporary
+			},
+			clobberFlags: true,
+		},
+
+		// arg0 = destination pointer
+		// arg1 = source pointer
+		// arg2 = # of 8-byte words to copy
+		// arg3 = mem
+		// returns memory
+		{
+			name:      "REPMOVSL",
+			argLength: 4,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("DI"), buildReg("SI"), buildReg("CX")},
+				clobbers: buildReg("DI SI CX"),
+			},
+		},
+
+		// (InvertFlags (CMPL a b)) == (CMPL b a)
+		// So if we want (SETL (CMPL a b)) but we can't do that because a is a constant,
+		// then we do (SETL (InvertFlags (CMPL b a))) instead.
+		// Rewrites will convert this to (SETG (CMPL b a)).
+		// InvertFlags is a pseudo-op which can't appear in assembly output.
+		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
+
+		// Pseudo-ops
+		{name: "LoweredGetG", argLength: 1, reg: gp01}, // arg0=mem
+		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
+		// and sorts it to the very beginning of the block to prevent other
+		// use of DX (the closure pointer)
+		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("DX")}}},
+		//arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
+		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
+
+		// MOVLconvert converts between pointers and integers.
+		// We have a special op for this so as to not confuse GC
+		// (particularly stack maps).  It takes a memory arg so it
+		// gets correctly ordered with respect to GC safepoints.
+		// arg0=ptr/int arg1=mem, output=int/ptr
+		{name: "MOVLconvert", argLength: 2, reg: gp11, asm: "MOVL"},
+
+		// Constant flag values. For any comparison, there are 5 possible
+		// outcomes: the three from the signed total order (<,==,>) and the
+		// three from the unsigned total order. The == cases overlap.
+		// Note: there's a sixth "unordered" outcome for floating-point
+		// comparisons, but we don't use such a beast yet.
+		// These ops are for temporary use by rewrite rules. They
+		// cannot appear in the generated assembly.
+		{name: "FlagEQ"},     // equal
+		{name: "FlagLT_ULT"}, // signed < and unsigned <
+		{name: "FlagLT_UGT"}, // signed < and unsigned >
+		{name: "FlagGT_UGT"}, // signed > and unsigned <
+		{name: "FlagGT_ULT"}, // signed > and unsigned >
+
+		// Special op for -x on 387
+		{name: "FCHS", argLength: 1, reg: fp11},
+
+		// Special ops for PIC floating-point constants.
+		// MOVSXconst1 loads the address of the constant-pool entry into a register.
+		// MOVSXconst2 loads the constant from that address.
+		// MOVSXconst1 returns a pointer, but we type it as uint32 because it can never point to the Go heap.
+		{name: "MOVSSconst1", reg: gp01, typ: "UInt32", aux: "Float32"},
+		{name: "MOVSDconst1", reg: gp01, typ: "UInt32", aux: "Float64"},
+		{name: "MOVSSconst2", argLength: 1, reg: gpfp, asm: "MOVSS"},
+		{name: "MOVSDconst2", argLength: 1, reg: gpfp, asm: "MOVSD"},
+	}
+
+	var _386blocks = []blockData{
+		{name: "EQ"},
+		{name: "NE"},
+		{name: "LT"},
+		{name: "LE"},
+		{name: "GT"},
+		{name: "GE"},
+		{name: "ULT"},
+		{name: "ULE"},
+		{name: "UGT"},
+		{name: "UGE"},
+		{name: "EQF"},
+		{name: "NEF"},
+		{name: "ORD"}, // FP, ordered comparison (parity zero)
+		{name: "NAN"}, // FP, unordered comparison (parity one)
+	}
+
+	archs = append(archs, arch{
+		name:            "386",
+		pkg:             "cmd/internal/obj/x86",
+		genfile:         "../../x86/ssa.go",
+		ops:             _386ops,
+		blocks:          _386blocks,
+		regnames:        regNames386,
+		gpregmask:       gp,
+		fpregmask:       fp,
+		framepointerreg: int8(num["BP"]),
+		linkreg:         -1, // not used
+	})
+}
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules
index d27eff0..5b4649c 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64.rules
+++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules
@@ -4,7 +4,8 @@
 
 // Lowering arithmetic
 (Add64  x y) -> (ADDQ  x y)
-(AddPtr x y) -> (ADDQ  x y)
+(AddPtr x y) && config.PtrSize == 8 -> (ADDQ x y)
+(AddPtr x y) && config.PtrSize == 4 -> (ADDL x y)
 (Add32  x y) -> (ADDL  x y)
 (Add16  x y) -> (ADDL  x y)
 (Add8   x y) -> (ADDL  x y)
@@ -12,7 +13,8 @@
 (Add64F x y) -> (ADDSD x y)
 
 (Sub64  x y) -> (SUBQ  x y)
-(SubPtr x y) -> (SUBQ  x y)
+(SubPtr x y) && config.PtrSize == 8 -> (SUBQ x y)
+(SubPtr x y) && config.PtrSize == 4 -> (SUBL x y)
 (Sub32  x y) -> (SUBL  x y)
 (Sub16  x y) -> (SUBL  x y)
 (Sub8   x y) -> (SUBL  x y)
@@ -29,14 +31,14 @@
 (Div32F x y) -> (DIVSS x y)
 (Div64F x y) -> (DIVSD x y)
 
-(Div64  x y) -> (DIVQ  x y)
-(Div64u x y) -> (DIVQU x y)
-(Div32  x y) -> (DIVL  x y)
-(Div32u x y) -> (DIVLU x y)
-(Div16  x y) -> (DIVW  x y)
-(Div16u x y) -> (DIVWU x y)
-(Div8   x y) -> (DIVW  (SignExt8to16 x) (SignExt8to16 y))
-(Div8u  x y) -> (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y))
+(Div64  x y) -> (Select0 (DIVQ  x y))
+(Div64u x y) -> (Select0 (DIVQU x y))
+(Div32  x y) -> (Select0 (DIVL  x y))
+(Div32u x y) -> (Select0 (DIVLU x y))
+(Div16  x y) -> (Select0 (DIVW  x y))
+(Div16u x y) -> (Select0 (DIVWU x y))
+(Div8   x y) -> (Select0 (DIVW  (SignExt8to16 x) (SignExt8to16 y)))
+(Div8u  x y) -> (Select0 (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y)))
 
 (Hmul64  x y) -> (HMULQ  x y)
 (Hmul64u x y) -> (HMULQU x y)
@@ -47,16 +49,19 @@
 (Hmul8   x y) -> (HMULB  x y)
 (Hmul8u  x y) -> (HMULBU x y)
 
+(Mul64uhilo x y) -> (MULQU2 x y)
+(Div128u xhi xlo y) -> (DIVQU2 xhi xlo y)
+
 (Avg64u x y) -> (AVGQU x y)
 
-(Mod64  x y) -> (MODQ  x y)
-(Mod64u x y) -> (MODQU x y)
-(Mod32  x y) -> (MODL  x y)
-(Mod32u x y) -> (MODLU x y)
-(Mod16  x y) -> (MODW  x y)
-(Mod16u x y) -> (MODWU x y)
-(Mod8   x y) -> (MODW  (SignExt8to16 x) (SignExt8to16 y))
-(Mod8u  x y) -> (MODWU (ZeroExt8to16 x) (ZeroExt8to16 y))
+(Mod64  x y) -> (Select1 (DIVQ  x y))
+(Mod64u x y) -> (Select1 (DIVQU x y))
+(Mod32  x y) -> (Select1 (DIVL  x y))
+(Mod32u x y) -> (Select1 (DIVLU x y))
+(Mod16  x y) -> (Select1 (DIVW  x y))
+(Mod16u x y) -> (Select1 (DIVWU x y))
+(Mod8   x y) -> (Select1 (DIVW  (SignExt8to16 x) (SignExt8to16 y)))
+(Mod8u  x y) -> (Select1 (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y)))
 
 (And64 x y) -> (ANDQ x y)
 (And32 x y) -> (ANDL x y)
@@ -91,14 +96,13 @@
 (Not x) -> (XORLconst [1] x)
 
 // Lowering pointer arithmetic
-(OffPtr [off] ptr) && is32Bit(off) -> (ADDQconst [off] ptr)
-(OffPtr [off] ptr) -> (ADDQ (MOVQconst [off]) ptr)
+(OffPtr [off] ptr) && config.PtrSize == 8 && is32Bit(off) -> (ADDQconst [off] ptr)
+(OffPtr [off] ptr) && config.PtrSize == 8 -> (ADDQ (MOVQconst [off]) ptr)
+(OffPtr [off] ptr) && config.PtrSize == 4 -> (ADDLconst [off] ptr)
 
 // Lowering other arithmetic
-// TODO: CMPQconst 0 below is redundant because BSF sets Z but how to remove?
-(Ctz64 <t> x) -> (CMOVQEQconst (BSFQ <t> x) (CMPQconst x [0]) [64])
-(Ctz32 <t> x) -> (CMOVLEQconst (BSFL <t> x) (CMPLconst x [0]) [32])
-(Ctz16 <t> x) -> (CMOVWEQconst (BSFW <t> x) (CMPWconst x [0]) [16])
+(Ctz64 <t> x) -> (CMOVQEQ (Select0 <t> (BSFQ x)) (MOVQconst <t> [64]) (Select1 <TypeFlags> (BSFQ x)))
+(Ctz32 <t> x) -> (CMOVLEQ (Select0 <t> (BSFL x)) (MOVLconst <t> [32]) (Select1 <TypeFlags> (BSFL x)))
 
 (Bswap64 x) -> (BSWAPQ x)
 (Bswap32 x) -> (BSWAPL x)
@@ -121,6 +125,8 @@
 (ZeroExt16to64 x) -> (MOVWQZX x)
 (ZeroExt32to64 x) -> (MOVLQZX x)
 
+(Slicemask <t> x) -> (XORQconst [-1] (SARQconst <t> (SUBQconst <t> x [1]) [63]))
+
 // Lowering truncation
 // Because we ignore high parts of registers, truncates are just copies.
 (Trunc16to8  x) -> x
@@ -270,7 +276,8 @@
 (Eq16  x y) -> (SETEQ (CMPW x y))
 (Eq8   x y) -> (SETEQ (CMPB x y))
 (EqB   x y) -> (SETEQ (CMPB x y))
-(EqPtr x y) -> (SETEQ (CMPQ x y))
+(EqPtr x y) && config.PtrSize == 8 -> (SETEQ (CMPQ x y))
+(EqPtr x y) && config.PtrSize == 4 -> (SETEQ (CMPL x y))
 (Eq64F x y) -> (SETEQF (UCOMISD x y))
 (Eq32F x y) -> (SETEQF (UCOMISS x y))
 
@@ -279,13 +286,16 @@
 (Neq16  x y) -> (SETNE (CMPW x y))
 (Neq8   x y) -> (SETNE (CMPB x y))
 (NeqB   x y) -> (SETNE (CMPB x y))
-(NeqPtr x y) -> (SETNE (CMPQ x y))
+(NeqPtr x y) && config.PtrSize == 8 -> (SETNE (CMPQ x y))
+(NeqPtr x y) && config.PtrSize == 4 -> (SETNE (CMPL x y))
 (Neq64F x y) -> (SETNEF (UCOMISD x y))
 (Neq32F x y) -> (SETNEF (UCOMISS x y))
 
+(Int64Hi x) -> (SHRQconst [32] x) // needed for amd64p32
+
 // Lowering loads
-(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload ptr mem)
-(Load <t> ptr mem) && is32BitInt(t) -> (MOVLload ptr mem)
+(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t) && config.PtrSize == 8) -> (MOVQload ptr mem)
+(Load <t> ptr mem) && (is32BitInt(t) || isPtr(t) && config.PtrSize == 4) -> (MOVLload ptr mem)
 (Load <t> ptr mem) && is16BitInt(t) -> (MOVWload ptr mem)
 (Load <t> ptr mem) && (t.IsBoolean() || is8BitInt(t)) -> (MOVBload ptr mem)
 (Load <t> ptr mem) && is32BitFloat(t) -> (MOVSSload ptr mem)
@@ -302,39 +312,47 @@
 (Store [1] ptr val mem) -> (MOVBstore ptr val mem)
 
 // Lowering moves
-(Move [0] _ _ mem) -> mem
-(Move [1] dst src mem) -> (MOVBstore dst (MOVBload src mem) mem)
-(Move [2] dst src mem) -> (MOVWstore dst (MOVWload src mem) mem)
-(Move [4] dst src mem) -> (MOVLstore dst (MOVLload src mem) mem)
-(Move [8] dst src mem) -> (MOVQstore dst (MOVQload src mem) mem)
-(Move [16] dst src mem) -> (MOVOstore dst (MOVOload src mem) mem)
-(Move [3] dst src mem) ->
+(Move [s] _ _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore dst (MOVBload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 -> (MOVWstore dst (MOVWload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 -> (MOVLstore dst (MOVLload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 -> (MOVQstore dst (MOVQload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 16 -> (MOVOstore dst (MOVOload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 3 ->
 	(MOVBstore [2] dst (MOVBload [2] src mem)
 		(MOVWstore dst (MOVWload src mem) mem))
-(Move [5] dst src mem) ->
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 5 ->
 	(MOVBstore [4] dst (MOVBload [4] src mem)
 		(MOVLstore dst (MOVLload src mem) mem))
-(Move [6] dst src mem) ->
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 6 ->
 	(MOVWstore [4] dst (MOVWload [4] src mem)
 		(MOVLstore dst (MOVLload src mem) mem))
-(Move [7] dst src mem) ->
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 7 ->
 	(MOVLstore [3] dst (MOVLload [3] src mem)
 		(MOVLstore dst (MOVLload src mem) mem))
-(Move [size] dst src mem) && size > 8 && size < 16 ->
-	(MOVQstore [size-8] dst (MOVQload [size-8] src mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() > 8 && SizeAndAlign(s).Size() < 16 ->
+	(MOVQstore [SizeAndAlign(s).Size()-8] dst (MOVQload [SizeAndAlign(s).Size()-8] src mem)
 		(MOVQstore dst (MOVQload src mem) mem))
 
 // Adjust moves to be a multiple of 16 bytes.
-(Move [size] dst src mem) && size > 16 && size%16 != 0 && size%16 <= 8 ->
-	(Move [size-size%16] (ADDQconst <dst.Type> dst [size%16]) (ADDQconst <src.Type> src [size%16])
+(Move [s] dst src mem)
+	&& SizeAndAlign(s).Size() > 16 && SizeAndAlign(s).Size()%16 != 0 && SizeAndAlign(s).Size()%16 <= 8 ->
+	(Move [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%16]
+		(OffPtr <dst.Type> dst [SizeAndAlign(s).Size()%16])
+		(OffPtr <src.Type> src [SizeAndAlign(s).Size()%16])
 		(MOVQstore dst (MOVQload src mem) mem))
-(Move [size] dst src mem) && size > 16 && size%16 != 0 && size%16 > 8 ->
-	(Move [size-size%16] (ADDQconst <dst.Type> dst [size%16]) (ADDQconst <src.Type> src [size%16])
+(Move [s] dst src mem)
+	&& SizeAndAlign(s).Size() > 16 && SizeAndAlign(s).Size()%16 != 0 && SizeAndAlign(s).Size()%16 > 8 ->
+	(Move [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%16]
+		(OffPtr <dst.Type> dst [SizeAndAlign(s).Size()%16])
+		(OffPtr <src.Type> src [SizeAndAlign(s).Size()%16])
 		(MOVOstore dst (MOVOload src mem) mem))
 
 // Medium copying uses a duff device.
-(Move [size] dst src mem) && size >= 32 && size <= 16*64 && size%16 == 0 && !config.noDuffDevice ->
-	(DUFFCOPY [14*(64-size/16)] dst src mem)
+(Move [s] dst src mem)
+	&& SizeAndAlign(s).Size() >= 32 && SizeAndAlign(s).Size() <= 16*64 && SizeAndAlign(s).Size()%16 == 0
+	&& !config.noDuffDevice ->
+	(DUFFCOPY [14*(64-SizeAndAlign(s).Size()/16)] dst src mem)
 // 14 and 64 are magic constants.  14 is the number of bytes to encode:
 //	MOVUPS	(SI), X0
 //	ADDQ	$16, SI
@@ -343,57 +361,62 @@
 // and 64 is the number of such blocks. See src/runtime/duff_amd64.s:duffcopy.
 
 // Large copying uses REP MOVSQ.
-(Move [size] dst src mem) && (size > 16*64 || config.noDuffDevice) && size%8 == 0 ->
-	(REPMOVSQ dst src (MOVQconst [size/8]) mem)
+(Move [s] dst src mem) && (SizeAndAlign(s).Size() > 16*64 || config.noDuffDevice) && SizeAndAlign(s).Size()%8 == 0 ->
+	(REPMOVSQ dst src (MOVQconst [SizeAndAlign(s).Size()/8]) mem)
 
 // Lowering Zero instructions
-(Zero [0] _ mem) -> mem
-(Zero [1] destptr mem) -> (MOVBstoreconst [0] destptr mem)
-(Zero [2] destptr mem) -> (MOVWstoreconst [0] destptr mem)
-(Zero [4] destptr mem) -> (MOVLstoreconst [0] destptr mem)
-(Zero [8] destptr mem) -> (MOVQstoreconst [0] destptr mem)
+(Zero [s] _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstoreconst [0] destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 2 -> (MOVWstoreconst [0] destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 4 -> (MOVLstoreconst [0] destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 8 -> (MOVQstoreconst [0] destptr mem)
 
-(Zero [3] destptr mem) ->
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 3 ->
 	(MOVBstoreconst [makeValAndOff(0,2)] destptr
 		(MOVWstoreconst [0] destptr mem))
-(Zero [5] destptr mem) ->
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 5 ->
 	(MOVBstoreconst [makeValAndOff(0,4)] destptr
 		(MOVLstoreconst [0] destptr mem))
-(Zero [6] destptr mem) ->
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 6 ->
 	(MOVWstoreconst [makeValAndOff(0,4)] destptr
 		(MOVLstoreconst [0] destptr mem))
-(Zero [7] destptr mem) ->
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 7 ->
 	(MOVLstoreconst [makeValAndOff(0,3)] destptr
 		(MOVLstoreconst [0] destptr mem))
 
 // Strip off any fractional word zeroing.
-(Zero [size] destptr mem) && size%8 != 0 && size > 8 ->
-	(Zero [size-size%8] (ADDQconst destptr [size%8])
+(Zero [s] destptr mem) && SizeAndAlign(s).Size()%8 != 0 && SizeAndAlign(s).Size() > 8 ->
+	(Zero [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8] (OffPtr <destptr.Type> destptr [SizeAndAlign(s).Size()%8])
 		(MOVQstoreconst [0] destptr mem))
 
 // Zero small numbers of words directly.
-(Zero [16] destptr mem) ->
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 16 ->
 	(MOVQstoreconst [makeValAndOff(0,8)] destptr
 		(MOVQstoreconst [0] destptr mem))
-(Zero [24] destptr mem) ->
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 24 ->
 	(MOVQstoreconst [makeValAndOff(0,16)] destptr
 		(MOVQstoreconst [makeValAndOff(0,8)] destptr
 			(MOVQstoreconst [0] destptr mem)))
-(Zero [32] destptr mem) ->
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 32 ->
 	(MOVQstoreconst [makeValAndOff(0,24)] destptr
 		(MOVQstoreconst [makeValAndOff(0,16)] destptr
 			(MOVQstoreconst [makeValAndOff(0,8)] destptr
 				(MOVQstoreconst [0] destptr mem))))
 
 // Medium zeroing uses a duff device.
-(Zero [size] destptr mem) && size <= 1024 && size%8 == 0 && size%16 != 0 && !config.noDuffDevice ->
-	(Zero [size-8] (ADDQconst [8] destptr) (MOVQstore destptr (MOVQconst [0]) mem))
-(Zero [size] destptr mem) && size <= 1024 && size%16 == 0 && !config.noDuffDevice ->
-	(DUFFZERO [size] destptr (MOVOconst [0]) mem)
+(Zero [s] destptr mem)
+	&& SizeAndAlign(s).Size() <= 1024 && SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size()%16 != 0
+	&& !config.noDuffDevice ->
+	(Zero [SizeAndAlign(s).Size()-8] (OffPtr <destptr.Type> [8] destptr) (MOVQstore destptr (MOVQconst [0]) mem))
+(Zero [s] destptr mem)
+	&& SizeAndAlign(s).Size() <= 1024 && SizeAndAlign(s).Size()%16 == 0 && !config.noDuffDevice ->
+	(DUFFZERO [SizeAndAlign(s).Size()] destptr (MOVOconst [0]) mem)
 
 // Large zeroing uses REP STOSQ.
-(Zero [size] destptr mem) && (size > 1024 || (config.noDuffDevice && size > 32)) && size%8 == 0 ->
-	(REPSTOSQ destptr (MOVQconst [size/8]) (MOVQconst [0]) mem)
+(Zero [s] destptr mem)
+	&& (SizeAndAlign(s).Size() > 1024 || (config.noDuffDevice && SizeAndAlign(s).Size() > 32))
+	&& SizeAndAlign(s).Size()%8 == 0 ->
+	(REPSTOSQ destptr (MOVQconst [SizeAndAlign(s).Size()/8]) (MOVQconst [0]) mem)
 
 // Lowering constants
 (Const8   [val]) -> (MOVLconst [val])
@@ -402,7 +425,8 @@
 (Const64  [val]) -> (MOVQconst [val])
 (Const32F [val]) -> (MOVSSconst [val])
 (Const64F [val]) -> (MOVSDconst [val])
-(ConstNil) -> (MOVQconst [0])
+(ConstNil) && config.PtrSize == 8 -> (MOVQconst [0])
+(ConstNil) && config.PtrSize == 4 -> (MOVLconst [0])
 (ConstBool [b]) -> (MOVLconst [b])
 
 // Lowering calls
@@ -413,15 +437,17 @@
 (InterCall [argwid] entry mem) -> (CALLinter [argwid] entry mem)
 
 // Miscellaneous
-(Convert <t> x mem) -> (MOVQconvert <t> x mem)
-(IsNonNil p) -> (SETNE (TESTQ p p))
+(Convert <t> x mem) && config.PtrSize == 8 -> (MOVQconvert <t> x mem)
+(Convert <t> x mem) && config.PtrSize == 4 -> (MOVLconvert <t> x mem)
+(IsNonNil p) && config.PtrSize == 8 -> (SETNE (TESTQ p p))
+(IsNonNil p) && config.PtrSize == 4 -> (SETNE (TESTL p p))
 (IsInBounds idx len) -> (SETB (CMPQ idx len))
 (IsSliceInBounds idx len) -> (SETBE (CMPQ idx len))
 (NilCheck ptr mem) -> (LoweredNilCheck ptr mem)
 (GetG mem) -> (LoweredGetG mem)
 (GetClosurePtr) -> (LoweredGetClosurePtr)
-(Addr {sym} base) -> (LEAQ {sym} base)
-(ITab (Load ptr mem)) -> (MOVQload ptr mem)
+(Addr {sym} base) && config.PtrSize == 8 -> (LEAQ {sym} base)
+(Addr {sym} base) && config.PtrSize == 4 -> (LEAL {sym} base)
 
 // block rewrites
 (If (SETL  cmp) yes no) -> (LT  cmp yes no)
@@ -443,6 +469,39 @@
 
 (If cond yes no) -> (NE (TESTB cond cond) yes no)
 
+// Atomic loads.  Other than preserving their ordering with respect to other loads, nothing special here.
+(AtomicLoad32 ptr mem) -> (MOVLatomicload ptr mem)
+(AtomicLoad64 ptr mem) -> (MOVQatomicload ptr mem)
+(AtomicLoadPtr ptr mem) && config.PtrSize == 8 -> (MOVQatomicload ptr mem)
+(AtomicLoadPtr ptr mem) && config.PtrSize == 4 -> (MOVLatomicload ptr mem)
+
+// Atomic stores.  We use XCHG to prevent the hardware reordering a subsequent load.
+// TODO: most runtime uses of atomic stores don't need that property.  Use normal stores for those?
+(AtomicStore32 ptr val mem) -> (Select1 (XCHGL <MakeTuple(config.Frontend().TypeUInt32(),TypeMem)> val ptr mem))
+(AtomicStore64 ptr val mem) -> (Select1 (XCHGQ <MakeTuple(config.Frontend().TypeUInt64(),TypeMem)> val ptr mem))
+(AtomicStorePtrNoWB ptr val mem) && config.PtrSize == 8 -> (Select1 (XCHGQ <MakeTuple(config.Frontend().TypeBytePtr(),TypeMem)> val ptr mem))
+(AtomicStorePtrNoWB ptr val mem) && config.PtrSize == 4 -> (Select1 (XCHGL <MakeTuple(config.Frontend().TypeBytePtr(),TypeMem)> val ptr mem))
+
+// Atomic exchanges.
+(AtomicExchange32 ptr val mem) -> (XCHGL val ptr mem)
+(AtomicExchange64 ptr val mem) -> (XCHGQ val ptr mem)
+
+// Atomic adds.
+(AtomicAdd32 ptr val mem) -> (AddTupleFirst32 (XADDLlock val ptr mem) val)
+(AtomicAdd64 ptr val mem) -> (AddTupleFirst64 (XADDQlock val ptr mem) val)
+(Select0 <t> (AddTupleFirst32 tuple val)) -> (ADDL val (Select0 <t> tuple))
+(Select1     (AddTupleFirst32 tuple _  )) -> (Select1 tuple)
+(Select0 <t> (AddTupleFirst64 tuple val)) -> (ADDQ val (Select0 <t> tuple))
+(Select1     (AddTupleFirst64 tuple _  )) -> (Select1 tuple)
+
+// Atomic compare and swap.
+(AtomicCompareAndSwap32 ptr old new_ mem) -> (CMPXCHGLlock ptr old new_ mem)
+(AtomicCompareAndSwap64 ptr old new_ mem) -> (CMPXCHGQlock ptr old new_ mem)
+
+// Atomic memory updates.
+(AtomicAnd8 ptr val mem) -> (ANDBlock ptr val mem)
+(AtomicOr8 ptr val mem) -> (ORBlock ptr val mem)
+
 // ***************************
 // Above: lowering rules
 // Below: optimizations
@@ -495,6 +554,12 @@
 (ANDLconst [c] (ANDLconst [d] x)) -> (ANDLconst [c & d] x)
 (ANDQconst [c] (ANDQconst [d] x)) -> (ANDQconst [c & d] x)
 
+(XORLconst [c] (XORLconst [d] x)) -> (XORLconst [c ^ d] x)
+(XORQconst [c] (XORQconst [d] x)) -> (XORQconst [c ^ d] x)
+
+(MULLconst [c] (MULLconst [d] x)) -> (MULLconst [int64(int32(c * d))] x)
+(MULQconst [c] (MULQconst [d] x)) && is32Bit(c*d) -> (MULQconst [c * d] x)
+
 (ORQ x (MOVQconst [c])) && is32Bit(c) -> (ORQconst [c] x)
 (ORQ (MOVQconst [c]) x) && is32Bit(c) -> (ORQconst [c] x)
 (ORL x (MOVLconst [c])) -> (ORLconst [c] x)
@@ -544,6 +609,16 @@
 (SHRL x (ANDLconst [31] y)) -> (SHRL x y)
 (SHRQ x (ANDQconst [63] y)) -> (SHRQ x y)
 
+(ROLQconst [c] (ROLQconst [d] x)) -> (ROLQconst [(c+d)&63] x)
+(ROLLconst [c] (ROLLconst [d] x)) -> (ROLLconst [(c+d)&31] x)
+(ROLWconst [c] (ROLWconst [d] x)) -> (ROLWconst [(c+d)&15] x)
+(ROLBconst [c] (ROLBconst [d] x)) -> (ROLBconst [(c+d)& 7] x)
+
+(ROLQconst [0] x) -> x
+(ROLLconst [0] x) -> x
+(ROLWconst [0] x) -> x
+(ROLBconst [0] x) -> x
+
 // Note: the word and byte shifts keep the low 5 bits (not the low 4 or 3 bits)
 // because the x86 instructions are defined to use all 5 bits of the shift even
 // for the small shifts. I don't think we'll ever generate a weird shift (e.g.
@@ -558,7 +633,9 @@
 (CMPB x (MOVLconst [c])) -> (CMPBconst x [int64(int8(c))])
 (CMPB (MOVLconst [c]) x) -> (InvertFlags (CMPBconst x [int64(int8(c))]))
 
-// Using MOVBQZX instead of ANDQ is cheaper.
+// Using MOVZX instead of AND is cheaper.
+(ANDLconst [0xFF] x) -> (MOVBQZX x)
+(ANDLconst [0xFFFF] x) -> (MOVWQZX x)
 (ANDQconst [0xFF] x) -> (MOVBQZX x)
 (ANDQconst [0xFFFF] x) -> (MOVWQZX x)
 (ANDQconst [0xFFFFFFFF] x) -> (MOVLQZX x)
@@ -661,11 +738,23 @@
 // This prevents a single load from being split into multiple loads
 // which then might return different values.  See test/atomicload.go.
 (MOVBQSX x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
+(MOVBQSX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
+(MOVBQSX x:(MOVLload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
+(MOVBQSX x:(MOVQload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
 (MOVBQZX x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
+(MOVBQZX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
+(MOVBQZX x:(MOVLload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
+(MOVBQZX x:(MOVQload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
 (MOVWQSX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWQSXload <v.Type> [off] {sym} ptr mem)
+(MOVWQSX x:(MOVLload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWQSXload <v.Type> [off] {sym} ptr mem)
+(MOVWQSX x:(MOVQload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWQSXload <v.Type> [off] {sym} ptr mem)
 (MOVWQZX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
+(MOVWQZX x:(MOVLload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
+(MOVWQZX x:(MOVQload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
 (MOVLQSX x:(MOVLload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVLQSXload <v.Type> [off] {sym} ptr mem)
+(MOVLQSX x:(MOVQload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVLQSXload <v.Type> [off] {sym} ptr mem)
 (MOVLQZX x:(MOVLload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVLload <v.Type> [off] {sym} ptr mem)
+(MOVLQZX x:(MOVQload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVLload <v.Type> [off] {sym} ptr mem)
 
 (MOVBQZX x:(MOVBloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBloadidx1 <v.Type> [off] {sym} ptr idx mem)
 (MOVWQZX x:(MOVWloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx1 <v.Type> [off] {sym} ptr idx mem)
@@ -868,9 +957,13 @@
 (MOVWloadidx1 [c] {sym} ptr (SHLQconst [1] idx) mem) -> (MOVWloadidx2 [c] {sym} ptr idx mem)
 (MOVLloadidx1 [c] {sym} ptr (SHLQconst [2] idx) mem) -> (MOVLloadidx4 [c] {sym} ptr idx mem)
 (MOVQloadidx1 [c] {sym} ptr (SHLQconst [3] idx) mem) -> (MOVQloadidx8 [c] {sym} ptr idx mem)
+(MOVSSloadidx1 [c] {sym} ptr (SHLQconst [2] idx) mem) -> (MOVSSloadidx4 [c] {sym} ptr idx mem)
+(MOVSDloadidx1 [c] {sym} ptr (SHLQconst [3] idx) mem) -> (MOVSDloadidx8 [c] {sym} ptr idx mem)
 (MOVWstoreidx1 [c] {sym} ptr (SHLQconst [1] idx) val mem) -> (MOVWstoreidx2 [c] {sym} ptr idx val mem)
 (MOVLstoreidx1 [c] {sym} ptr (SHLQconst [2] idx) val mem) -> (MOVLstoreidx4 [c] {sym} ptr idx val mem)
 (MOVQstoreidx1 [c] {sym} ptr (SHLQconst [3] idx) val mem) -> (MOVQstoreidx8 [c] {sym} ptr idx val mem)
+(MOVSSstoreidx1 [c] {sym} ptr (SHLQconst [2] idx) val mem) -> (MOVSSstoreidx4 [c] {sym} ptr idx val mem)
+(MOVSDstoreidx1 [c] {sym} ptr (SHLQconst [3] idx) val mem) -> (MOVSDstoreidx8 [c] {sym} ptr idx val mem)
 (MOVWstoreconstidx1 [c] {sym} ptr (SHLQconst [1] idx) mem) -> (MOVWstoreconstidx2 [c] {sym} ptr idx mem)
 (MOVLstoreconstidx1 [c] {sym} ptr (SHLQconst [2] idx) mem) -> (MOVLstoreconstidx4 [c] {sym} ptr idx mem)
 (MOVQstoreconstidx1 [c] {sym} ptr (SHLQconst [3] idx) mem) -> (MOVQstoreconstidx8 [c] {sym} ptr idx mem)
@@ -1243,31 +1336,6 @@
 (CMPWconst x [0]) -> (TESTW x x)
 (CMPBconst x [0]) -> (TESTB x x)
 
-// Optimizing conditional moves
-(CMOVQEQconst x (InvertFlags y) [c]) -> (CMOVQNEconst x y [c])
-(CMOVLEQconst x (InvertFlags y) [c]) -> (CMOVLNEconst x y [c])
-(CMOVWEQconst x (InvertFlags y) [c]) -> (CMOVWNEconst x y [c])
-
-(CMOVQEQconst _ (FlagEQ) [c]) -> (Const64 [c])
-(CMOVLEQconst _ (FlagEQ) [c]) -> (Const32 [c])
-(CMOVWEQconst _ (FlagEQ) [c]) -> (Const16 [c])
-
-(CMOVQEQconst x (FlagLT_ULT)) -> x
-(CMOVLEQconst x (FlagLT_ULT)) -> x
-(CMOVWEQconst x (FlagLT_ULT)) -> x
-
-(CMOVQEQconst x (FlagLT_UGT)) -> x
-(CMOVLEQconst x (FlagLT_UGT)) -> x
-(CMOVWEQconst x (FlagLT_UGT)) -> x
-
-(CMOVQEQconst x (FlagGT_ULT)) -> x
-(CMOVLEQconst x (FlagGT_ULT)) -> x
-(CMOVWEQconst x (FlagGT_ULT)) -> x
-
-(CMOVQEQconst x (FlagGT_UGT)) -> x
-(CMOVLEQconst x (FlagGT_UGT)) -> x
-(CMOVWEQconst x (FlagGT_UGT)) -> x
-
 // Combining byte loads into larger (unaligned) loads.
 // There are many ways these combinations could occur.  This is
 // designed to match the way encoding/binary.LittleEndian does it.
@@ -1282,31 +1350,24 @@
   && clobber(s0)
   -> @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem)
 
-(ORL o0:(ORL o1:(ORL
-                       x0:(MOVBload [i]   {s} p mem)
-    s0:(SHLLconst [8]  x1:(MOVBload [i+1] {s} p mem)))
-    s1:(SHLLconst [16] x2:(MOVBload [i+2] {s} p mem)))
-    s2:(SHLLconst [24] x3:(MOVBload [i+3] {s} p mem)))
+(ORL o0:(ORL
+                       x0:(MOVWload [i]   {s} p mem)
+    s0:(SHLLconst [16] x1:(MOVBload [i+2] {s} p mem)))
+    s1:(SHLLconst [24] x2:(MOVBload [i+3] {s} p mem)))
   && x0.Uses == 1
   && x1.Uses == 1
   && x2.Uses == 1
-  && x3.Uses == 1
   && s0.Uses == 1
   && s1.Uses == 1
-  && s2.Uses == 1
   && o0.Uses == 1
-  && o1.Uses == 1
-  && mergePoint(b,x0,x1,x2,x3) != nil
+  && mergePoint(b,x0,x1,x2) != nil
   && clobber(x0)
   && clobber(x1)
   && clobber(x2)
-  && clobber(x3)
   && clobber(s0)
   && clobber(s1)
-  && clobber(s2)
   && clobber(o0)
-  && clobber(o1)
-  -> @mergePoint(b,x0,x1,x2,x3) (MOVLload [i] {s} p mem)
+  -> @mergePoint(b,x0,x1,x2) (MOVLload [i] {s} p mem)
 
 (ORQ o0:(ORQ o1:(ORQ o2:(ORQ o3:(ORQ o4:(ORQ o5:(ORQ
                        x0:(MOVBload [i]   {s} p mem)
@@ -1373,31 +1434,24 @@
   && clobber(s0)
   -> @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i] {s} p idx mem)
 
-(ORL o0:(ORL o1:(ORL
-                       x0:(MOVBloadidx1 [i]   {s} p idx mem)
-    s0:(SHLLconst [8]  x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
-    s1:(SHLLconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem)))
-    s2:(SHLLconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem)))
+(ORL o0:(ORL
+                       x0:(MOVWloadidx1 [i]   {s} p idx mem)
+    s0:(SHLLconst [16] x1:(MOVBloadidx1 [i+2] {s} p idx mem)))
+    s1:(SHLLconst [24] x2:(MOVBloadidx1 [i+3] {s} p idx mem)))
   && x0.Uses == 1
   && x1.Uses == 1
   && x2.Uses == 1
-  && x3.Uses == 1
   && s0.Uses == 1
   && s1.Uses == 1
-  && s2.Uses == 1
   && o0.Uses == 1
-  && o1.Uses == 1
-  && mergePoint(b,x0,x1,x2,x3) != nil
+  && mergePoint(b,x0,x1,x2) != nil
   && clobber(x0)
   && clobber(x1)
   && clobber(x2)
-  && clobber(x3)
   && clobber(s0)
   && clobber(s1)
-  && clobber(s2)
   && clobber(o0)
-  && clobber(o1)
-  -> @mergePoint(b,x0,x1,x2,x3) (MOVLloadidx1 <v.Type> [i] {s} p idx mem)
+  -> @mergePoint(b,x0,x1,x2) (MOVLloadidx1 <v.Type> [i] {s} p idx mem)
 
 (ORQ o0:(ORQ o1:(ORQ o2:(ORQ o3:(ORQ o4:(ORQ o5:(ORQ
                        x0:(MOVBloadidx1 [i]   {s} p idx mem)
@@ -1453,6 +1507,204 @@
   && clobber(o5)
   -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQloadidx1 <v.Type> [i] {s} p idx mem)
 
+// Combine byte loads + shifts into larger (unaligned) loads + bswap
+(ORL o1:(ORL o0:(ORL
+                       x0:(MOVBload [i] {s} p mem)
+    s0:(SHLLconst [8]  x1:(MOVBload [i-1] {s} p mem)))
+    s1:(SHLLconst [16] x2:(MOVBload [i-2] {s} p mem)))
+    s2:(SHLLconst [24] x3:(MOVBload [i-3] {s} p mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && x3.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && s2.Uses == 1
+  && o0.Uses == 1
+  && o1.Uses == 1
+  && mergePoint(b,x0,x1,x2,x3) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(x3)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(s2)
+  && clobber(o0)
+  && clobber(o1)
+  -> @mergePoint(b,x0,x1,x2,x3) (BSWAPL <v.Type> (MOVLload [i-3] {s} p mem))
+
+(ORL o1:(ORL o0:(ORL
+                       x0:(MOVBloadidx1 [i] {s} p idx mem)
+    s0:(SHLLconst [8]  x1:(MOVBloadidx1 [i-1] {s} p idx mem)))
+    s1:(SHLLconst [16] x2:(MOVBloadidx1 [i-2] {s} p idx mem)))
+    s2:(SHLLconst [24] x3:(MOVBloadidx1 [i-3] {s} p idx mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && x3.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && s2.Uses == 1
+  && o0.Uses == 1
+  && o1.Uses == 1
+  && mergePoint(b,x0,x1,x2,x3) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(x3)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(s2)
+  && clobber(o0)
+  && clobber(o1)
+  -> @mergePoint(b,x0,x1,x2,x3) (BSWAPL <v.Type> (MOVLloadidx1 <v.Type> [i-3] {s} p idx mem))
+
+(ORQ o5:(ORQ o4:(ORQ o3:(ORQ o2:(ORQ o1:(ORQ o0:(ORQ
+                       x0:(MOVBload [i] {s} p mem)
+    s0:(SHLQconst [8]  x1:(MOVBload [i-1] {s} p mem)))
+    s1:(SHLQconst [16] x2:(MOVBload [i-2] {s} p mem)))
+    s2:(SHLQconst [24] x3:(MOVBload [i-3] {s} p mem)))
+    s3:(SHLQconst [32] x4:(MOVBload [i-4] {s} p mem)))
+    s4:(SHLQconst [40] x5:(MOVBload [i-5] {s} p mem)))
+    s5:(SHLQconst [48] x6:(MOVBload [i-6] {s} p mem)))
+    s6:(SHLQconst [56] x7:(MOVBload [i-7] {s} p mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && x3.Uses == 1
+  && x4.Uses == 1
+  && x5.Uses == 1
+  && x6.Uses == 1
+  && x7.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && s2.Uses == 1
+  && s3.Uses == 1
+  && s4.Uses == 1
+  && s5.Uses == 1
+  && s6.Uses == 1
+  && o0.Uses == 1
+  && o1.Uses == 1
+  && o2.Uses == 1
+  && o3.Uses == 1
+  && o4.Uses == 1
+  && o5.Uses == 1
+  && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(x3)
+  && clobber(x4)
+  && clobber(x5)
+  && clobber(x6)
+  && clobber(x7)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(s2)
+  && clobber(s3)
+  && clobber(s4)
+  && clobber(s5)
+  && clobber(s6)
+  && clobber(o0)
+  && clobber(o1)
+  && clobber(o2)
+  && clobber(o3)
+  && clobber(o4)
+  && clobber(o5)
+  -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (BSWAPQ <v.Type> (MOVQload [i-7] {s} p mem))
+
+(ORQ o5:(ORQ o4:(ORQ o3:(ORQ o2:(ORQ o1:(ORQ o0:(ORQ
+                       x0:(MOVBloadidx1 [i] {s} p idx mem)
+    s0:(SHLQconst [8]  x1:(MOVBloadidx1 [i-1] {s} p idx mem)))
+    s1:(SHLQconst [16] x2:(MOVBloadidx1 [i-2] {s} p idx mem)))
+    s2:(SHLQconst [24] x3:(MOVBloadidx1 [i-3] {s} p idx mem)))
+    s3:(SHLQconst [32] x4:(MOVBloadidx1 [i-4] {s} p idx mem)))
+    s4:(SHLQconst [40] x5:(MOVBloadidx1 [i-5] {s} p idx mem)))
+    s5:(SHLQconst [48] x6:(MOVBloadidx1 [i-6] {s} p idx mem)))
+    s6:(SHLQconst [56] x7:(MOVBloadidx1 [i-7] {s} p idx mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && x3.Uses == 1
+  && x4.Uses == 1
+  && x5.Uses == 1
+  && x6.Uses == 1
+  && x7.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && s2.Uses == 1
+  && s3.Uses == 1
+  && s4.Uses == 1
+  && s5.Uses == 1
+  && s6.Uses == 1
+  && o0.Uses == 1
+  && o1.Uses == 1
+  && o2.Uses == 1
+  && o3.Uses == 1
+  && o4.Uses == 1
+  && o5.Uses == 1
+  && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(x3)
+  && clobber(x4)
+  && clobber(x5)
+  && clobber(x6)
+  && clobber(x7)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(s2)
+  && clobber(s3)
+  && clobber(s4)
+  && clobber(s5)
+  && clobber(s6)
+  && clobber(o0)
+  && clobber(o1)
+  && clobber(o2)
+  && clobber(o3)
+  && clobber(o4)
+  && clobber(o5)
+  -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (BSWAPQ <v.Type> (MOVQloadidx1 <v.Type> [i-7] {s} p idx mem))
+
+// Combine stores + shifts into bswap and larger (unaligned) stores
+(MOVBstore [i] {s} p w
+  x2:(MOVBstore [i-1] {s} p (SHRLconst [8] w)
+  x1:(MOVBstore [i-2] {s} p (SHRLconst [16] w)
+  x0:(MOVBstore [i-3] {s} p (SHRLconst [24] w) mem))))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  -> (MOVLstore [i-3] {s} p (BSWAPL <w.Type> w) mem)
+
+(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))))))))
+  && 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)
+  -> (MOVQstore [i-7] {s} p (BSWAPQ <w.Type> w) mem)
+
 // Combine constant stores into larger (unaligned) stores.
 (MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {s} p mem))
   && x.Uses == 1
@@ -1564,3 +1816,87 @@
   && x.Uses == 1
   && clobber(x)
   -> (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w0 mem)
+
+// amd64p32 rules
+// same as the rules above, but with 32 instead of 64 bit pointer arithmetic.
+// LEAQ,ADDQ -> LEAL,ADDL
+(ADDLconst [c] (LEAL [d] {s} x)) && is32Bit(c+d) -> (LEAL [c+d] {s} x)
+(LEAL [c] {s} (ADDLconst [d] x)) && is32Bit(c+d) -> (LEAL [c+d] {s} x)
+
+(MOVQload  [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) ->
+	(MOVQload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+(MOVLload  [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) ->
+	(MOVLload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+(MOVWload  [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) ->
+	(MOVWload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+(MOVBload  [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) ->
+	(MOVBload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+
+(MOVQstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) ->
+	(MOVQstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+(MOVLstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) ->
+	(MOVLstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+(MOVWstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) ->
+	(MOVWstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+(MOVBstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) ->
+	(MOVBstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+
+(MOVQstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) ->
+	(MOVQstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+(MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) ->
+	(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) ->
+	(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) ->
+	(MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+
+(MOVQload  [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVQload  [off1+off2] {sym} ptr mem)
+(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)
+(MOVQstore  [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVQstore  [off1+off2] {sym} ptr val 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)
+(MOVQstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) ->
+	(MOVQstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+(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)
+
+// Merge ADDQconst and LEAQ into atomic loads.
+(MOVQatomicload [off1] {sym} (ADDQconst [off2] ptr) mem) && is32Bit(off1+off2) ->
+	(MOVQatomicload [off1+off2] {sym} ptr mem)
+(MOVLatomicload [off1] {sym} (ADDQconst [off2] ptr) mem) && is32Bit(off1+off2) ->
+	(MOVLatomicload [off1+off2] {sym} ptr mem)
+(MOVQatomicload [off1] {sym1} (LEAQ [off2] {sym2} ptr) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVQatomicload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVLatomicload [off1] {sym1} (LEAQ [off2] {sym2} ptr) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVLatomicload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+
+// Merge ADDQconst and LEAQ into atomic stores.
+(XCHGQ [off1] {sym} val (ADDQconst [off2] ptr) mem) && is32Bit(off1+off2) ->
+	(XCHGQ [off1+off2] {sym} val ptr mem)
+(XCHGQ [off1] {sym1} val (LEAQ [off2] {sym2} ptr) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && ptr.Op != OpSB ->
+	(XCHGQ [off1+off2] {mergeSym(sym1,sym2)} val ptr mem)
+(XCHGL [off1] {sym} val (ADDQconst [off2] ptr) mem) && is32Bit(off1+off2) ->
+	(XCHGL [off1+off2] {sym} val ptr mem)
+(XCHGL [off1] {sym1} val (LEAQ [off2] {sym2} ptr) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && ptr.Op != OpSB ->
+	(XCHGL [off1+off2] {mergeSym(sym1,sym2)} val ptr mem)
+
+// Merge ADDQconst into atomic adds.
+// TODO: merging LEAQ doesn't work, assembler doesn't like the resulting instructions.
+(XADDQlock [off1] {sym} val (ADDQconst [off2] ptr) mem) && is32Bit(off1+off2) ->
+	(XADDQlock [off1+off2] {sym} val ptr mem)
+(XADDLlock [off1] {sym} val (ADDQconst [off2] ptr) mem) && is32Bit(off1+off2) ->
+	(XADDLlock [off1+off2] {sym} val ptr mem)
+
+// Merge ADDQconst into atomic compare and swaps.
+// TODO: merging LEAQ doesn't work, assembler doesn't like the resulting instructions.
+(CMPXCHGQlock [off1] {sym} (ADDQconst [off2] ptr) old new_ mem) && is32Bit(off1+off2) ->
+	(CMPXCHGQlock [off1+off2] {sym} ptr old new_ mem)
+(CMPXCHGLlock [off1] {sym} (ADDQconst [off2] ptr) old new_ mem) && is32Bit(off1+off2) ->
+	(CMPXCHGLlock [off1+off2] {sym} ptr old new_ mem)
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
index 43cc0eb..5a293d1 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
@@ -64,7 +64,6 @@ var regNamesAMD64 = []string{
 
 	// pseudo-registers
 	"SB",
-	"FLAGS",
 }
 
 func init() {
@@ -93,48 +92,39 @@ func init() {
 		ax         = buildReg("AX")
 		cx         = buildReg("CX")
 		dx         = buildReg("DX")
-		x15        = buildReg("X15")
 		gp         = buildReg("AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15")
 		fp         = buildReg("X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15")
 		gpsp       = gp | buildReg("SP")
 		gpspsb     = gpsp | buildReg("SB")
-		flags      = buildReg("FLAGS")
-		callerSave = gp | fp | flags
+		callerSave = gp | fp
 	)
 	// Common slices of register masks
 	var (
-		gponly    = []regMask{gp}
-		fponly    = []regMask{fp}
-		flagsonly = []regMask{flags}
+		gponly = []regMask{gp}
+		fponly = []regMask{fp}
 	)
 
 	// Common regInfo
 	var (
-		gp01      = regInfo{inputs: []regMask{}, outputs: gponly}
-		gp11      = regInfo{inputs: []regMask{gp}, outputs: gponly, clobbers: flags}
-		gp11sp    = regInfo{inputs: []regMask{gpsp}, outputs: gponly, clobbers: flags}
-		gp11nf    = regInfo{inputs: []regMask{gpsp}, outputs: gponly} // nf: no flags clobbered
+		gp01      = regInfo{inputs: nil, outputs: gponly}
+		gp11      = regInfo{inputs: []regMask{gp}, outputs: gponly}
+		gp11sp    = regInfo{inputs: []regMask{gpsp}, outputs: gponly}
 		gp11sb    = regInfo{inputs: []regMask{gpspsb}, outputs: gponly}
-		gp21      = regInfo{inputs: []regMask{gp, gp}, outputs: gponly, clobbers: flags}
-		gp21sp    = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly, clobbers: flags}
+		gp21      = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
+		gp21sp    = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly}
 		gp21sb    = regInfo{inputs: []regMask{gpspsb, gpsp}, outputs: gponly}
-		gp21shift = regInfo{inputs: []regMask{gp, cx}, outputs: []regMask{gp}, clobbers: flags}
-		gp11div   = regInfo{inputs: []regMask{ax, gpsp &^ dx}, outputs: []regMask{ax},
-			clobbers: dx | flags}
-		gp11hmul = regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{dx},
-			clobbers: ax | flags}
-		gp11mod = regInfo{inputs: []regMask{ax, gpsp &^ dx}, outputs: []regMask{dx},
-			clobbers: ax | flags}
+		gp21shift = regInfo{inputs: []regMask{gp, cx}, outputs: []regMask{gp}}
+		gp11div   = regInfo{inputs: []regMask{ax, gpsp &^ dx}, outputs: []regMask{ax, dx}}
+		gp21hmul  = regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{dx}, clobbers: ax}
 
-		gp2flags = regInfo{inputs: []regMask{gpsp, gpsp}, outputs: flagsonly}
-		gp1flags = regInfo{inputs: []regMask{gpsp}, outputs: flagsonly}
-		flagsgp  = regInfo{inputs: flagsonly, outputs: gponly}
+		gp2flags = regInfo{inputs: []regMask{gpsp, gpsp}}
+		gp1flags = regInfo{inputs: []regMask{gpsp}}
+		flagsgp  = regInfo{inputs: nil, outputs: gponly}
 
-		// for CMOVconst -- uses AX to hold constant temporary.
-		gp1flagsgp = regInfo{inputs: []regMask{gp &^ ax, flags}, clobbers: ax | flags, outputs: []regMask{gp &^ ax}}
+		gp11flags = regInfo{inputs: []regMask{gp}, outputs: []regMask{gp, 0}}
 
-		readflags = regInfo{inputs: flagsonly, outputs: gponly}
-		flagsgpax = regInfo{inputs: flagsonly, clobbers: ax | flags, outputs: []regMask{gp &^ ax}}
+		readflags = regInfo{inputs: nil, outputs: gponly}
+		flagsgpax = regInfo{inputs: nil, clobbers: ax, outputs: []regMask{gp &^ ax}}
 
 		gpload    = regInfo{inputs: []regMask{gpspsb, 0}, outputs: gponly}
 		gploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: gponly}
@@ -143,15 +133,15 @@ func init() {
 		gpstoreconst    = regInfo{inputs: []regMask{gpspsb, 0}}
 		gpstoreidx      = regInfo{inputs: []regMask{gpspsb, gpsp, gpsp, 0}}
 		gpstoreconstidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}}
+		gpstorexchg     = regInfo{inputs: []regMask{gp, gp, 0}, outputs: []regMask{gp}}
+		cmpxchg         = regInfo{inputs: []regMask{gp, ax, gp, 0}, outputs: []regMask{gp, 0}, clobbers: ax}
 
-		fp01    = regInfo{inputs: []regMask{}, outputs: fponly}
-		fp21    = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
-		fp21x15 = regInfo{inputs: []regMask{fp &^ x15, fp &^ x15},
-			clobbers: x15, outputs: []regMask{fp &^ x15}}
+		fp01     = regInfo{inputs: nil, outputs: fponly}
+		fp21     = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
 		fpgp     = regInfo{inputs: fponly, outputs: gponly}
 		gpfp     = regInfo{inputs: gponly, outputs: fponly}
 		fp11     = regInfo{inputs: fponly, outputs: fponly}
-		fp2flags = regInfo{inputs: []regMask{fp, fp}, outputs: flagsonly}
+		fp2flags = regInfo{inputs: []regMask{fp, fp}}
 
 		fpload    = regInfo{inputs: []regMask{gpspsb, 0}, outputs: fponly}
 		fploadidx = regInfo{inputs: []regMask{gpspsb, gpsp, 0}, outputs: fponly}
@@ -164,84 +154,80 @@ func init() {
 		// fp ops
 		{name: "ADDSS", argLength: 2, reg: fp21, asm: "ADDSS", commutative: true, resultInArg0: true}, // fp32 add
 		{name: "ADDSD", argLength: 2, reg: fp21, asm: "ADDSD", commutative: true, resultInArg0: true}, // fp64 add
-		{name: "SUBSS", argLength: 2, reg: fp21x15, asm: "SUBSS", resultInArg0: true},                 // fp32 sub
-		{name: "SUBSD", argLength: 2, reg: fp21x15, asm: "SUBSD", resultInArg0: true},                 // fp64 sub
+		{name: "SUBSS", argLength: 2, reg: fp21, asm: "SUBSS", resultInArg0: true},                    // fp32 sub
+		{name: "SUBSD", argLength: 2, reg: fp21, asm: "SUBSD", resultInArg0: true},                    // fp64 sub
 		{name: "MULSS", argLength: 2, reg: fp21, asm: "MULSS", commutative: true, resultInArg0: true}, // fp32 mul
 		{name: "MULSD", argLength: 2, reg: fp21, asm: "MULSD", commutative: true, resultInArg0: true}, // fp64 mul
-		{name: "DIVSS", argLength: 2, reg: fp21x15, asm: "DIVSS", resultInArg0: true},                 // fp32 div
-		{name: "DIVSD", argLength: 2, reg: fp21x15, asm: "DIVSD", resultInArg0: true},                 // fp64 div
-
-		{name: "MOVSSload", argLength: 2, reg: fpload, asm: "MOVSS", aux: "SymOff"},            // fp32 load
-		{name: "MOVSDload", argLength: 2, reg: fpload, asm: "MOVSD", aux: "SymOff"},            // fp64 load
-		{name: "MOVSSconst", reg: fp01, asm: "MOVSS", aux: "Float32", rematerializeable: true}, // fp32 constant
-		{name: "MOVSDconst", reg: fp01, asm: "MOVSD", aux: "Float64", rematerializeable: true}, // fp64 constant
-		{name: "MOVSSloadidx1", argLength: 3, reg: fploadidx, asm: "MOVSS", aux: "SymOff"},     // fp32 load indexed by i
-		{name: "MOVSSloadidx4", argLength: 3, reg: fploadidx, asm: "MOVSS", aux: "SymOff"},     // fp32 load indexed by 4*i
-		{name: "MOVSDloadidx1", argLength: 3, reg: fploadidx, asm: "MOVSD", aux: "SymOff"},     // fp64 load indexed by i
-		{name: "MOVSDloadidx8", argLength: 3, reg: fploadidx, asm: "MOVSD", aux: "SymOff"},     // fp64 load indexed by 8*i
-
-		{name: "MOVSSstore", argLength: 3, reg: fpstore, asm: "MOVSS", aux: "SymOff"},        // fp32 store
-		{name: "MOVSDstore", argLength: 3, reg: fpstore, asm: "MOVSD", aux: "SymOff"},        // fp64 store
-		{name: "MOVSSstoreidx1", argLength: 4, reg: fpstoreidx, asm: "MOVSS", aux: "SymOff"}, // fp32 indexed by i store
-		{name: "MOVSSstoreidx4", argLength: 4, reg: fpstoreidx, asm: "MOVSS", aux: "SymOff"}, // fp32 indexed by 4i store
-		{name: "MOVSDstoreidx1", argLength: 4, reg: fpstoreidx, asm: "MOVSD", aux: "SymOff"}, // fp64 indexed by i store
-		{name: "MOVSDstoreidx8", argLength: 4, reg: fpstoreidx, asm: "MOVSD", aux: "SymOff"}, // fp64 indexed by 8i store
+		{name: "DIVSS", argLength: 2, reg: fp21, asm: "DIVSS", resultInArg0: true},                    // fp32 div
+		{name: "DIVSD", argLength: 2, reg: fp21, asm: "DIVSD", resultInArg0: true},                    // fp64 div
+
+		{name: "MOVSSload", argLength: 2, reg: fpload, asm: "MOVSS", aux: "SymOff", faultOnNilArg0: true}, // fp32 load
+		{name: "MOVSDload", argLength: 2, reg: fpload, asm: "MOVSD", aux: "SymOff", faultOnNilArg0: true}, // fp64 load
+		{name: "MOVSSconst", reg: fp01, asm: "MOVSS", aux: "Float32", rematerializeable: true},            // fp32 constant
+		{name: "MOVSDconst", reg: fp01, asm: "MOVSD", aux: "Float64", rematerializeable: true},            // fp64 constant
+		{name: "MOVSSloadidx1", argLength: 3, reg: fploadidx, asm: "MOVSS", aux: "SymOff"},                // fp32 load indexed by i
+		{name: "MOVSSloadidx4", argLength: 3, reg: fploadidx, asm: "MOVSS", aux: "SymOff"},                // fp32 load indexed by 4*i
+		{name: "MOVSDloadidx1", argLength: 3, reg: fploadidx, asm: "MOVSD", aux: "SymOff"},                // fp64 load indexed by i
+		{name: "MOVSDloadidx8", argLength: 3, reg: fploadidx, asm: "MOVSD", aux: "SymOff"},                // fp64 load indexed by 8*i
+
+		{name: "MOVSSstore", argLength: 3, reg: fpstore, asm: "MOVSS", aux: "SymOff", faultOnNilArg0: true}, // fp32 store
+		{name: "MOVSDstore", argLength: 3, reg: fpstore, asm: "MOVSD", aux: "SymOff", faultOnNilArg0: true}, // fp64 store
+		{name: "MOVSSstoreidx1", argLength: 4, reg: fpstoreidx, asm: "MOVSS", aux: "SymOff"},                // fp32 indexed by i store
+		{name: "MOVSSstoreidx4", argLength: 4, reg: fpstoreidx, asm: "MOVSS", aux: "SymOff"},                // fp32 indexed by 4i store
+		{name: "MOVSDstoreidx1", argLength: 4, reg: fpstoreidx, asm: "MOVSD", aux: "SymOff"},                // fp64 indexed by i store
+		{name: "MOVSDstoreidx8", argLength: 4, reg: fpstoreidx, asm: "MOVSD", aux: "SymOff"},                // fp64 indexed by 8i store
 
 		// binary ops
-		{name: "ADDQ", argLength: 2, reg: gp21sp, asm: "ADDQ", commutative: true},                // arg0 + arg1
-		{name: "ADDL", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true},                // arg0 + arg1
-		{name: "ADDQconst", argLength: 1, reg: gp11sp, asm: "ADDQ", aux: "Int64", typ: "UInt64"}, // arg0 + auxint
-		{name: "ADDLconst", argLength: 1, reg: gp11sp, asm: "ADDL", aux: "Int32"},                // arg0 + auxint
-
-		{name: "SUBQ", argLength: 2, reg: gp21, asm: "SUBQ", resultInArg0: true},                    // arg0 - arg1
-		{name: "SUBL", argLength: 2, reg: gp21, asm: "SUBL", resultInArg0: true},                    // arg0 - arg1
-		{name: "SUBQconst", argLength: 1, reg: gp11, asm: "SUBQ", aux: "Int64", resultInArg0: true}, // arg0 - auxint
-		{name: "SUBLconst", argLength: 1, reg: gp11, asm: "SUBL", aux: "Int32", resultInArg0: true}, // arg0 - auxint
-
-		{name: "MULQ", argLength: 2, reg: gp21, asm: "IMULQ", commutative: true, resultInArg0: true}, // arg0 * arg1
-		{name: "MULL", argLength: 2, reg: gp21, asm: "IMULL", commutative: true, resultInArg0: true}, // arg0 * arg1
-		{name: "MULQconst", argLength: 1, reg: gp11, asm: "IMULQ", aux: "Int64", resultInArg0: true}, // arg0 * auxint
-		{name: "MULLconst", argLength: 1, reg: gp11, asm: "IMULL", aux: "Int32", resultInArg0: true}, // arg0 * auxint
-
-		{name: "HMULQ", argLength: 2, reg: gp11hmul, asm: "IMULQ"}, // (arg0 * arg1) >> width
-		{name: "HMULL", argLength: 2, reg: gp11hmul, asm: "IMULL"}, // (arg0 * arg1) >> width
-		{name: "HMULW", argLength: 2, reg: gp11hmul, asm: "IMULW"}, // (arg0 * arg1) >> width
-		{name: "HMULB", argLength: 2, reg: gp11hmul, asm: "IMULB"}, // (arg0 * arg1) >> width
-		{name: "HMULQU", argLength: 2, reg: gp11hmul, asm: "MULQ"}, // (arg0 * arg1) >> width
-		{name: "HMULLU", argLength: 2, reg: gp11hmul, asm: "MULL"}, // (arg0 * arg1) >> width
-		{name: "HMULWU", argLength: 2, reg: gp11hmul, asm: "MULW"}, // (arg0 * arg1) >> width
-		{name: "HMULBU", argLength: 2, reg: gp11hmul, asm: "MULB"}, // (arg0 * arg1) >> width
-
-		{name: "AVGQU", argLength: 2, reg: gp21, commutative: true, resultInArg0: true}, // (arg0 + arg1) / 2 as unsigned, all 64 result bits
-
-		{name: "DIVQ", argLength: 2, reg: gp11div, asm: "IDIVQ"}, // arg0 / arg1
-		{name: "DIVL", argLength: 2, reg: gp11div, asm: "IDIVL"}, // arg0 / arg1
-		{name: "DIVW", argLength: 2, reg: gp11div, asm: "IDIVW"}, // arg0 / arg1
-		{name: "DIVQU", argLength: 2, reg: gp11div, asm: "DIVQ"}, // arg0 / arg1
-		{name: "DIVLU", argLength: 2, reg: gp11div, asm: "DIVL"}, // arg0 / arg1
-		{name: "DIVWU", argLength: 2, reg: gp11div, asm: "DIVW"}, // arg0 / arg1
-
-		{name: "MODQ", argLength: 2, reg: gp11mod, asm: "IDIVQ"}, // arg0 % arg1
-		{name: "MODL", argLength: 2, reg: gp11mod, asm: "IDIVL"}, // arg0 % arg1
-		{name: "MODW", argLength: 2, reg: gp11mod, asm: "IDIVW"}, // arg0 % arg1
-		{name: "MODQU", argLength: 2, reg: gp11mod, asm: "DIVQ"}, // arg0 % arg1
-		{name: "MODLU", argLength: 2, reg: gp11mod, asm: "DIVL"}, // arg0 % arg1
-		{name: "MODWU", argLength: 2, reg: gp11mod, asm: "DIVW"}, // arg0 % arg1
-
-		{name: "ANDQ", argLength: 2, reg: gp21, asm: "ANDQ", commutative: true, resultInArg0: true}, // arg0 & arg1
-		{name: "ANDL", argLength: 2, reg: gp21, asm: "ANDL", commutative: true, resultInArg0: true}, // arg0 & arg1
-		{name: "ANDQconst", argLength: 1, reg: gp11, asm: "ANDQ", aux: "Int64", resultInArg0: true}, // arg0 & auxint
-		{name: "ANDLconst", argLength: 1, reg: gp11, asm: "ANDL", aux: "Int32", resultInArg0: true}, // arg0 & auxint
-
-		{name: "ORQ", argLength: 2, reg: gp21, asm: "ORQ", commutative: true, resultInArg0: true}, // arg0 | arg1
-		{name: "ORL", argLength: 2, reg: gp21, asm: "ORL", commutative: true, resultInArg0: true}, // arg0 | arg1
-		{name: "ORQconst", argLength: 1, reg: gp11, asm: "ORQ", aux: "Int64", resultInArg0: true}, // arg0 | auxint
-		{name: "ORLconst", argLength: 1, reg: gp11, asm: "ORL", aux: "Int32", resultInArg0: true}, // arg0 | auxint
-
-		{name: "XORQ", argLength: 2, reg: gp21, asm: "XORQ", commutative: true, resultInArg0: true}, // arg0 ^ arg1
-		{name: "XORL", argLength: 2, reg: gp21, asm: "XORL", commutative: true, resultInArg0: true}, // arg0 ^ arg1
-		{name: "XORQconst", argLength: 1, reg: gp11, asm: "XORQ", aux: "Int64", resultInArg0: true}, // arg0 ^ auxint
-		{name: "XORLconst", argLength: 1, reg: gp11, asm: "XORL", aux: "Int32", resultInArg0: true}, // arg0 ^ auxint
+		{name: "ADDQ", argLength: 2, reg: gp21sp, asm: "ADDQ", commutative: true, clobberFlags: true},                // arg0 + arg1
+		{name: "ADDL", argLength: 2, reg: gp21sp, asm: "ADDL", commutative: true, clobberFlags: true},                // arg0 + arg1
+		{name: "ADDQconst", argLength: 1, reg: gp11sp, asm: "ADDQ", aux: "Int64", typ: "UInt64", clobberFlags: true}, // arg0 + auxint
+		{name: "ADDLconst", argLength: 1, reg: gp11sp, asm: "ADDL", aux: "Int32", clobberFlags: true},                // arg0 + auxint
+
+		{name: "SUBQ", argLength: 2, reg: gp21, asm: "SUBQ", resultInArg0: true, clobberFlags: true},                    // arg0 - arg1
+		{name: "SUBL", argLength: 2, reg: gp21, asm: "SUBL", resultInArg0: true, clobberFlags: true},                    // arg0 - arg1
+		{name: "SUBQconst", argLength: 1, reg: gp11, asm: "SUBQ", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 - auxint
+		{name: "SUBLconst", argLength: 1, reg: gp11, asm: "SUBL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 - auxint
+
+		{name: "MULQ", argLength: 2, reg: gp21, asm: "IMULQ", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 * arg1
+		{name: "MULL", argLength: 2, reg: gp21, asm: "IMULL", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 * arg1
+		{name: "MULQconst", argLength: 1, reg: gp11, asm: "IMULQ", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 * auxint
+		{name: "MULLconst", argLength: 1, reg: gp11, asm: "IMULL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 * auxint
+
+		{name: "HMULQ", argLength: 2, reg: gp21hmul, asm: "IMULQ", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULL", argLength: 2, reg: gp21hmul, asm: "IMULL", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULW", argLength: 2, reg: gp21hmul, asm: "IMULW", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULB", argLength: 2, reg: gp21hmul, asm: "IMULB", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULQU", argLength: 2, reg: gp21hmul, asm: "MULQ", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULLU", argLength: 2, reg: gp21hmul, asm: "MULL", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULWU", argLength: 2, reg: gp21hmul, asm: "MULW", clobberFlags: true}, // (arg0 * arg1) >> width
+		{name: "HMULBU", argLength: 2, reg: gp21hmul, asm: "MULB", clobberFlags: true}, // (arg0 * arg1) >> width
+
+		{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]
+		{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]
+
+		{name: "MULQU2", argLength: 2, reg: regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{dx, ax}}, asm: "MULQ", clobberFlags: true},     // arg0 * arg1, returns (hi, lo)
+		{name: "DIVQU2", argLength: 3, reg: regInfo{inputs: []regMask{dx, ax, gpsp}, outputs: []regMask{ax, dx}}, asm: "DIVQ", clobberFlags: true}, // arg0:arg1 / arg2 (128-bit divided by 64-bit), returns (q, r)
+
+		{name: "ANDQ", argLength: 2, reg: gp21, asm: "ANDQ", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 & arg1
+		{name: "ANDL", argLength: 2, reg: gp21, asm: "ANDL", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 & arg1
+		{name: "ANDQconst", argLength: 1, reg: gp11, asm: "ANDQ", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 & auxint
+		{name: "ANDLconst", argLength: 1, reg: gp11, asm: "ANDL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 & auxint
+
+		{name: "ORQ", argLength: 2, reg: gp21, asm: "ORQ", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 | arg1
+		{name: "ORL", argLength: 2, reg: gp21, asm: "ORL", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 | arg1
+		{name: "ORQconst", argLength: 1, reg: gp11, asm: "ORQ", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 | auxint
+		{name: "ORLconst", argLength: 1, reg: gp11, asm: "ORL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 | auxint
+
+		{name: "XORQ", argLength: 2, reg: gp21, asm: "XORQ", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 ^ arg1
+		{name: "XORL", argLength: 2, reg: gp21, asm: "XORL", commutative: true, resultInArg0: true, clobberFlags: true}, // arg0 ^ arg1
+		{name: "XORQconst", argLength: 1, reg: gp11, asm: "XORQ", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 ^ auxint
+		{name: "XORLconst", argLength: 1, reg: gp11, asm: "XORL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 ^ auxint
 
 		{name: "CMPQ", argLength: 2, reg: gp2flags, asm: "CMPQ", typ: "Flags"},                    // arg0 compare to arg1
 		{name: "CMPL", argLength: 2, reg: gp2flags, asm: "CMPL", typ: "Flags"},                    // arg0 compare to arg1
@@ -264,60 +250,55 @@ func init() {
 		{name: "TESTWconst", argLength: 1, reg: gp1flags, asm: "TESTW", typ: "Flags", aux: "Int16"}, // (arg0 & auxint) compare to 0
 		{name: "TESTBconst", argLength: 1, reg: gp1flags, asm: "TESTB", typ: "Flags", aux: "Int8"},  // (arg0 & auxint) compare to 0
 
-		{name: "SHLQ", argLength: 2, reg: gp21shift, asm: "SHLQ", resultInArg0: true},               // arg0 << arg1, shift amount is mod 64
-		{name: "SHLL", argLength: 2, reg: gp21shift, asm: "SHLL", resultInArg0: true},               // arg0 << arg1, shift amount is mod 32
-		{name: "SHLQconst", argLength: 1, reg: gp11, asm: "SHLQ", aux: "Int64", resultInArg0: true}, // arg0 << auxint, shift amount 0-63
-		{name: "SHLLconst", argLength: 1, reg: gp11, asm: "SHLL", aux: "Int32", resultInArg0: true}, // arg0 << auxint, shift amount 0-31
+		{name: "SHLQ", argLength: 2, reg: gp21shift, asm: "SHLQ", resultInArg0: true, clobberFlags: true},               // arg0 << arg1, shift amount is mod 64
+		{name: "SHLL", argLength: 2, reg: gp21shift, asm: "SHLL", resultInArg0: true, clobberFlags: true},               // arg0 << arg1, shift amount is mod 32
+		{name: "SHLQconst", argLength: 1, reg: gp11, asm: "SHLQ", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 << auxint, shift amount 0-63
+		{name: "SHLLconst", argLength: 1, reg: gp11, asm: "SHLL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 << auxint, shift amount 0-31
 		// Note: x86 is weird, the 16 and 8 byte shifts still use all 5 bits of shift amount!
 
-		{name: "SHRQ", argLength: 2, reg: gp21shift, asm: "SHRQ", resultInArg0: true},               // unsigned arg0 >> arg1, shift amount is mod 64
-		{name: "SHRL", argLength: 2, reg: gp21shift, asm: "SHRL", resultInArg0: true},               // unsigned arg0 >> arg1, shift amount is mod 32
-		{name: "SHRW", argLength: 2, reg: gp21shift, asm: "SHRW", resultInArg0: true},               // unsigned arg0 >> arg1, shift amount is mod 32
-		{name: "SHRB", argLength: 2, reg: gp21shift, asm: "SHRB", resultInArg0: true},               // unsigned arg0 >> arg1, shift amount is mod 32
-		{name: "SHRQconst", argLength: 1, reg: gp11, asm: "SHRQ", aux: "Int64", resultInArg0: true}, // unsigned arg0 >> auxint, shift amount 0-63
-		{name: "SHRLconst", argLength: 1, reg: gp11, asm: "SHRL", aux: "Int32", resultInArg0: true}, // unsigned arg0 >> auxint, shift amount 0-31
-		{name: "SHRWconst", argLength: 1, reg: gp11, asm: "SHRW", aux: "Int16", resultInArg0: true}, // unsigned arg0 >> auxint, shift amount 0-31
-		{name: "SHRBconst", argLength: 1, reg: gp11, asm: "SHRB", aux: "Int8", resultInArg0: true},  // unsigned arg0 >> auxint, shift amount 0-31
-
-		{name: "SARQ", argLength: 2, reg: gp21shift, asm: "SARQ", resultInArg0: true},               // signed arg0 >> arg1, shift amount is mod 64
-		{name: "SARL", argLength: 2, reg: gp21shift, asm: "SARL", resultInArg0: true},               // signed arg0 >> arg1, shift amount is mod 32
-		{name: "SARW", argLength: 2, reg: gp21shift, asm: "SARW", resultInArg0: true},               // signed arg0 >> arg1, shift amount is mod 32
-		{name: "SARB", argLength: 2, reg: gp21shift, asm: "SARB", resultInArg0: true},               // signed arg0 >> arg1, shift amount is mod 32
-		{name: "SARQconst", argLength: 1, reg: gp11, asm: "SARQ", aux: "Int64", resultInArg0: true}, // signed arg0 >> auxint, shift amount 0-63
-		{name: "SARLconst", argLength: 1, reg: gp11, asm: "SARL", aux: "Int32", resultInArg0: true}, // signed arg0 >> auxint, shift amount 0-31
-		{name: "SARWconst", argLength: 1, reg: gp11, asm: "SARW", aux: "Int16", resultInArg0: true}, // signed arg0 >> auxint, shift amount 0-31
-		{name: "SARBconst", argLength: 1, reg: gp11, asm: "SARB", aux: "Int8", resultInArg0: true},  // signed arg0 >> auxint, shift amount 0-31
-
-		{name: "ROLQconst", argLength: 1, reg: gp11, asm: "ROLQ", aux: "Int64", resultInArg0: true}, // arg0 rotate left auxint, rotate amount 0-63
-		{name: "ROLLconst", argLength: 1, reg: gp11, asm: "ROLL", aux: "Int32", resultInArg0: true}, // arg0 rotate left auxint, rotate amount 0-31
-		{name: "ROLWconst", argLength: 1, reg: gp11, asm: "ROLW", aux: "Int16", resultInArg0: true}, // arg0 rotate left auxint, rotate amount 0-15
-		{name: "ROLBconst", argLength: 1, reg: gp11, asm: "ROLB", aux: "Int8", resultInArg0: true},  // arg0 rotate left auxint, rotate amount 0-7
+		{name: "SHRQ", argLength: 2, reg: gp21shift, asm: "SHRQ", resultInArg0: true, clobberFlags: true},               // unsigned arg0 >> arg1, shift amount is mod 64
+		{name: "SHRL", argLength: 2, reg: gp21shift, asm: "SHRL", resultInArg0: true, clobberFlags: true},               // unsigned arg0 >> arg1, shift amount is mod 32
+		{name: "SHRW", argLength: 2, reg: gp21shift, asm: "SHRW", resultInArg0: true, clobberFlags: true},               // unsigned arg0 >> arg1, shift amount is mod 32
+		{name: "SHRB", argLength: 2, reg: gp21shift, asm: "SHRB", resultInArg0: true, clobberFlags: true},               // unsigned arg0 >> arg1, shift amount is mod 32
+		{name: "SHRQconst", argLength: 1, reg: gp11, asm: "SHRQ", aux: "Int64", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> auxint, shift amount 0-63
+		{name: "SHRLconst", argLength: 1, reg: gp11, asm: "SHRL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> auxint, shift amount 0-31
+		{name: "SHRWconst", argLength: 1, reg: gp11, asm: "SHRW", aux: "Int16", resultInArg0: true, clobberFlags: true}, // unsigned arg0 >> auxint, shift amount 0-31
+		{name: "SHRBconst", argLength: 1, reg: gp11, asm: "SHRB", aux: "Int8", resultInArg0: true, clobberFlags: true},  // unsigned arg0 >> auxint, shift amount 0-31
+
+		{name: "SARQ", argLength: 2, reg: gp21shift, asm: "SARQ", resultInArg0: true, clobberFlags: true},               // signed arg0 >> arg1, shift amount is mod 64
+		{name: "SARL", argLength: 2, reg: gp21shift, asm: "SARL", resultInArg0: true, clobberFlags: true},               // signed arg0 >> arg1, shift amount is mod 32
+		{name: "SARW", argLength: 2, reg: gp21shift, asm: "SARW", resultInArg0: true, clobberFlags: true},               // signed arg0 >> arg1, shift amount is mod 32
+		{name: "SARB", argLength: 2, reg: gp21shift, asm: "SARB", resultInArg0: true, clobberFlags: true},               // signed arg0 >> arg1, shift amount is mod 32
+		{name: "SARQconst", argLength: 1, reg: gp11, asm: "SARQ", aux: "Int64", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
+		{name: "SARLconst", argLength: 1, reg: gp11, asm: "SARL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-31
+		{name: "SARWconst", argLength: 1, reg: gp11, asm: "SARW", aux: "Int16", resultInArg0: true, clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-31
+		{name: "SARBconst", argLength: 1, reg: gp11, asm: "SARB", aux: "Int8", resultInArg0: true, clobberFlags: true},  // signed arg0 >> auxint, shift amount 0-31
+
+		{name: "ROLQconst", argLength: 1, reg: gp11, asm: "ROLQ", aux: "Int64", resultInArg0: true, clobberFlags: true}, // arg0 rotate left auxint, rotate amount 0-63
+		{name: "ROLLconst", argLength: 1, reg: gp11, asm: "ROLL", aux: "Int32", resultInArg0: true, clobberFlags: true}, // arg0 rotate left auxint, rotate amount 0-31
+		{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
 
 		// unary ops
-		{name: "NEGQ", argLength: 1, reg: gp11, asm: "NEGQ", resultInArg0: true}, // -arg0
-		{name: "NEGL", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true}, // -arg0
+		{name: "NEGQ", argLength: 1, reg: gp11, asm: "NEGQ", resultInArg0: true, clobberFlags: true}, // -arg0
+		{name: "NEGL", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true, clobberFlags: true}, // -arg0
 
-		{name: "NOTQ", argLength: 1, reg: gp11, asm: "NOTQ", resultInArg0: true}, // ^arg0
-		{name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true}, // ^arg0
+		{name: "NOTQ", argLength: 1, reg: gp11, asm: "NOTQ", resultInArg0: true, clobberFlags: true}, // ^arg0
+		{name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true, clobberFlags: true}, // ^arg0
 
-		{name: "BSFQ", argLength: 1, reg: gp11, asm: "BSFQ"}, // arg0 # of low-order zeroes ; undef if zero
-		{name: "BSFL", argLength: 1, reg: gp11, asm: "BSFL"}, // arg0 # of low-order zeroes ; undef if zero
-		{name: "BSFW", argLength: 1, reg: gp11, asm: "BSFW"}, // arg0 # of low-order zeroes ; undef if zero
-
-		{name: "BSRQ", argLength: 1, reg: gp11, asm: "BSRQ"}, // arg0 # of high-order zeroes ; undef if zero
-		{name: "BSRL", argLength: 1, reg: gp11, asm: "BSRL"}, // arg0 # of high-order zeroes ; undef if zero
-		{name: "BSRW", argLength: 1, reg: gp11, asm: "BSRW"}, // arg0 # of high-order zeroes ; undef if zero
+		// BSF{L,Q} returns a tuple [result, flags]
+		// result is undefined if the input is zero.
+		// flags are set to "equal" if the input is zero, "not equal" otherwise.
+		{name: "BSFQ", argLength: 1, reg: gp11flags, asm: "BSFQ", typ: "(UInt64,Flags)"}, // # of low-order zeroes in 64-bit arg
+		{name: "BSFL", argLength: 1, reg: gp11flags, asm: "BSFL", typ: "(UInt32,Flags)"}, // # of low-order zeroes in 32-bit arg
 
 		// Note ASM for ops moves whole register
-		{name: "CMOVQEQconst", argLength: 2, reg: gp1flagsgp, asm: "CMOVQEQ", typ: "UInt64", aux: "Int64", resultInArg0: true}, // replace arg0 w/ constant if Z set
-		{name: "CMOVLEQconst", argLength: 2, reg: gp1flagsgp, asm: "CMOVLEQ", typ: "UInt32", aux: "Int32", resultInArg0: true}, // replace arg0 w/ constant if Z set
-		{name: "CMOVWEQconst", argLength: 2, reg: gp1flagsgp, asm: "CMOVLEQ", typ: "UInt16", aux: "Int16", resultInArg0: true}, // replace arg0 w/ constant if Z set
-		{name: "CMOVQNEconst", argLength: 2, reg: gp1flagsgp, asm: "CMOVQNE", typ: "UInt64", aux: "Int64", resultInArg0: true}, // replace arg0 w/ constant if Z not set
-		{name: "CMOVLNEconst", argLength: 2, reg: gp1flagsgp, asm: "CMOVLNE", typ: "UInt32", aux: "Int32", resultInArg0: true}, // replace arg0 w/ constant if Z not set
-		{name: "CMOVWNEconst", argLength: 2, reg: gp1flagsgp, asm: "CMOVLNE", typ: "UInt16", aux: "Int16", resultInArg0: true}, // replace arg0 w/ constant if Z not set
+		//
+		{name: "CMOVQEQ", argLength: 3, reg: gp21, asm: "CMOVQEQ", resultInArg0: true}, // if arg2 encodes "equal" return arg1 else arg0
+		{name: "CMOVLEQ", argLength: 3, reg: gp21, asm: "CMOVLEQ", resultInArg0: true}, // if arg2 encodes "equal" return arg1 else arg0
 
-		{name: "BSWAPQ", argLength: 1, reg: gp11, asm: "BSWAPQ", resultInArg0: true}, // arg0 swap bytes
-		{name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true}, // arg0 swap bytes
+		{name: "BSWAPQ", argLength: 1, reg: gp11, asm: "BSWAPQ", resultInArg0: true, clobberFlags: true}, // arg0 swap bytes
+		{name: "BSWAPL", argLength: 1, reg: gp11, asm: "BSWAPL", resultInArg0: true, clobberFlags: true}, // arg0 swap bytes
 
 		{name: "SQRTSD", argLength: 1, reg: fp11, asm: "SQRTSD"}, // sqrt(arg0)
 
@@ -338,20 +319,20 @@ func init() {
 		// 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.
-		{name: "SETEQF", argLength: 1, reg: flagsgpax, asm: "SETEQ"}, // extract == condition from arg0
-		{name: "SETNEF", argLength: 1, reg: flagsgpax, asm: "SETNE"}, // extract != condition from arg0
-		{name: "SETORD", argLength: 1, reg: flagsgp, asm: "SETPC"},   // extract "ordered" (No Nan present) condition from arg0
-		{name: "SETNAN", argLength: 1, reg: flagsgp, asm: "SETPS"},   // extract "unordered" (Nan present) condition from arg0
+		{name: "SETEQF", argLength: 1, reg: flagsgpax, asm: "SETEQ", clobberFlags: true}, // extract == condition from arg0
+		{name: "SETNEF", argLength: 1, reg: flagsgpax, asm: "SETNE", clobberFlags: true}, // extract != condition from arg0
+		{name: "SETORD", argLength: 1, reg: flagsgp, asm: "SETPC"},                       // extract "ordered" (No Nan present) condition from arg0
+		{name: "SETNAN", argLength: 1, reg: flagsgp, asm: "SETPS"},                       // extract "unordered" (Nan present) condition from arg0
 
 		{name: "SETGF", argLength: 1, reg: flagsgp, asm: "SETHI"},  // extract floating > condition from arg0
 		{name: "SETGEF", argLength: 1, reg: flagsgp, asm: "SETCC"}, // extract floating >= condition from arg0
 
-		{name: "MOVBQSX", argLength: 1, reg: gp11nf, asm: "MOVBQSX"}, // sign extend arg0 from int8 to int64
-		{name: "MOVBQZX", argLength: 1, reg: gp11nf, asm: "MOVBQZX"}, // zero extend arg0 from int8 to int64
-		{name: "MOVWQSX", argLength: 1, reg: gp11nf, asm: "MOVWQSX"}, // sign extend arg0 from int16 to int64
-		{name: "MOVWQZX", argLength: 1, reg: gp11nf, asm: "MOVWQZX"}, // zero extend arg0 from int16 to int64
-		{name: "MOVLQSX", argLength: 1, reg: gp11nf, asm: "MOVLQSX"}, // sign extend arg0 from int32 to int64
-		{name: "MOVLQZX", argLength: 1, reg: gp11nf, asm: "MOVLQZX"}, // zero extend arg0 from int32 to int64
+		{name: "MOVBQSX", argLength: 1, reg: gp11, asm: "MOVBQSX"}, // sign extend arg0 from int8 to int64
+		{name: "MOVBQZX", argLength: 1, reg: gp11, asm: "MOVBLZX"}, // zero extend arg0 from int8 to int64
+		{name: "MOVWQSX", argLength: 1, reg: gp11, asm: "MOVWQSX"}, // sign extend arg0 from int16 to int64
+		{name: "MOVWQZX", argLength: 1, reg: gp11, asm: "MOVWLZX"}, // zero extend arg0 from int16 to int64
+		{name: "MOVLQSX", argLength: 1, reg: gp11, asm: "MOVLQSX"}, // sign extend arg0 from int32 to int64
+		{name: "MOVLQZX", argLength: 1, reg: gp11, asm: "MOVL"},    // zero extend arg0 from int32 to int64
 
 		{name: "MOVLconst", reg: gp01, asm: "MOVL", typ: "UInt32", aux: "Int32", rematerializeable: true}, // 32 low bits of auxint
 		{name: "MOVQconst", reg: gp01, asm: "MOVQ", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
@@ -369,27 +350,29 @@ func init() {
 
 		{name: "PXOR", argLength: 2, reg: fp21, asm: "PXOR", commutative: true, resultInArg0: true}, // exclusive or, applied to X regs for float negation.
 
-		{name: "LEAQ", argLength: 1, reg: gp11sb, aux: "SymOff", rematerializeable: true}, // arg0 + auxint + offset encoded in aux
-		{name: "LEAQ1", argLength: 2, reg: gp21sb, aux: "SymOff"},                         // arg0 + arg1 + auxint + aux
-		{name: "LEAQ2", argLength: 2, reg: gp21sb, aux: "SymOff"},                         // arg0 + 2*arg1 + auxint + aux
-		{name: "LEAQ4", argLength: 2, reg: gp21sb, aux: "SymOff"},                         // arg0 + 4*arg1 + auxint + aux
-		{name: "LEAQ8", argLength: 2, reg: gp21sb, aux: "SymOff"},                         // arg0 + 8*arg1 + auxint + aux
+		{name: "LEAQ", argLength: 1, reg: gp11sb, asm: "LEAQ", aux: "SymOff", rematerializeable: true}, // arg0 + auxint + offset encoded in aux
+		{name: "LEAQ1", argLength: 2, reg: gp21sb, aux: "SymOff"},                                      // arg0 + arg1 + auxint + aux
+		{name: "LEAQ2", argLength: 2, reg: gp21sb, aux: "SymOff"},                                      // arg0 + 2*arg1 + auxint + aux
+		{name: "LEAQ4", argLength: 2, reg: gp21sb, aux: "SymOff"},                                      // arg0 + 4*arg1 + auxint + aux
+		{name: "LEAQ8", argLength: 2, reg: gp21sb, aux: "SymOff"},                                      // arg0 + 8*arg1 + auxint + aux
 		// Note: LEAQ{1,2,4,8} must not have OpSB as either argument.
 
+		{name: "LEAL", argLength: 1, reg: gp11sb, asm: "LEAL", aux: "SymOff", rematerializeable: true}, // arg0 + auxint + offset encoded in aux
+
 		// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
-		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVBLZX", aux: "SymOff", typ: "UInt8"},  // load byte from arg0+auxint+aux. arg1=mem.  Zero extend.
-		{name: "MOVBQSXload", argLength: 2, reg: gpload, asm: "MOVBQSX", aux: "SymOff"},             // ditto, sign extend to int64
-		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVWLZX", aux: "SymOff", typ: "UInt16"}, // load 2 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
-		{name: "MOVWQSXload", argLength: 2, reg: gpload, asm: "MOVWQSX", aux: "SymOff"},             // ditto, sign extend to int64
-		{name: "MOVLload", argLength: 2, reg: gpload, asm: "MOVL", aux: "SymOff", typ: "UInt32"},    // load 4 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
-		{name: "MOVLQSXload", argLength: 2, reg: gpload, asm: "MOVLQSX", aux: "SymOff"},             // ditto, sign extend to int64
-		{name: "MOVQload", argLength: 2, reg: gpload, asm: "MOVQ", aux: "SymOff", typ: "UInt64"},    // load 8 bytes from arg0+auxint+aux. arg1=mem
-		{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem"},     // store byte in arg1 to arg0+auxint+aux. arg2=mem
-		{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem"},     // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
-		{name: "MOVLstore", argLength: 3, reg: gpstore, asm: "MOVL", aux: "SymOff", typ: "Mem"},     // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
-		{name: "MOVQstore", argLength: 3, reg: gpstore, asm: "MOVQ", aux: "SymOff", typ: "Mem"},     // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
-		{name: "MOVOload", argLength: 2, reg: fpload, asm: "MOVUPS", aux: "SymOff", typ: "Int128"},  // load 16 bytes from arg0+auxint+aux. arg1=mem
-		{name: "MOVOstore", argLength: 3, reg: fpstore, asm: "MOVUPS", aux: "SymOff", typ: "Mem"},   // store 16 bytes in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVBLZX", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true},  // load byte from arg0+auxint+aux. arg1=mem.  Zero extend.
+		{name: "MOVBQSXload", argLength: 2, reg: gpload, asm: "MOVBQSX", aux: "SymOff", faultOnNilArg0: true},             // ditto, sign extend to int64
+		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVWLZX", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true}, // load 2 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
+		{name: "MOVWQSXload", argLength: 2, reg: gpload, asm: "MOVWQSX", aux: "SymOff", faultOnNilArg0: true},             // ditto, sign extend to int64
+		{name: "MOVLload", argLength: 2, reg: gpload, asm: "MOVL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true},    // load 4 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
+		{name: "MOVLQSXload", argLength: 2, reg: gpload, asm: "MOVLQSX", aux: "SymOff", faultOnNilArg0: true},             // ditto, sign extend to int64
+		{name: "MOVQload", argLength: 2, reg: gpload, asm: "MOVQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true},    // load 8 bytes from arg0+auxint+aux. arg1=mem
+		{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},     // store byte in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},     // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVLstore", argLength: 3, reg: gpstore, asm: "MOVL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},     // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVQstore", argLength: 3, reg: gpstore, asm: "MOVQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},     // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVOload", argLength: 2, reg: fpload, asm: "MOVUPS", aux: "SymOff", typ: "Int128", faultOnNilArg0: true},  // load 16 bytes from arg0+auxint+aux. arg1=mem
+		{name: "MOVOstore", argLength: 3, reg: fpstore, asm: "MOVUPS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},   // store 16 bytes in arg1 to arg0+auxint+aux. arg2=mem
 
 		// indexed loads/stores
 		{name: "MOVBloadidx1", argLength: 3, reg: gploadidx, asm: "MOVBLZX", aux: "SymOff"}, // load a byte from arg0+arg1+auxint+aux. arg2=mem
@@ -412,10 +395,10 @@ func init() {
 		// For storeconst ops, the AuxInt field encodes both
 		// the value to store and an address offset of the store.
 		// Cast AuxInt to a ValAndOff to extract Val and Off fields.
-		{name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem"}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux.  arg1=mem
-		{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem"}, // store low 2 bytes of ...
-		{name: "MOVLstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVL", aux: "SymValAndOff", typ: "Mem"}, // store low 4 bytes of ...
-		{name: "MOVQstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVQ", aux: "SymValAndOff", typ: "Mem"}, // store 8 bytes of ...
+		{name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux.  arg1=mem
+		{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true}, // store low 2 bytes of ...
+		{name: "MOVLstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVL", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true}, // store low 4 bytes of ...
+		{name: "MOVQstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVQ", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of ...
 
 		{name: "MOVBstoreconstidx1", argLength: 3, reg: gpstoreconstidx, asm: "MOVB", aux: "SymValAndOff", typ: "Mem"}, // store low byte of ValAndOff(AuxInt).Val() to arg0+1*arg1+ValAndOff(AuxInt).Off()+aux.  arg2=mem
 		{name: "MOVWstoreconstidx1", argLength: 3, reg: gpstoreconstidx, asm: "MOVW", aux: "SymValAndOff", typ: "Mem"}, // store low 2 bytes of ... arg1 ...
@@ -436,8 +419,9 @@ func init() {
 			argLength: 3,
 			reg: regInfo{
 				inputs:   []regMask{buildReg("DI"), buildReg("X0")},
-				clobbers: buildReg("DI FLAGS"),
+				clobbers: buildReg("DI"),
 			},
+			clobberFlags: true,
 		},
 		{name: "MOVOconst", reg: regInfo{nil, 0, []regMask{fp}}, typ: "Int128", aux: "Int128", rematerializeable: true},
 
@@ -455,11 +439,11 @@ func init() {
 			},
 		},
 
-		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff"},                                // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
-		{name: "CALLclosure", argLength: 3, reg: regInfo{[]regMask{gpsp, buildReg("DX"), 0}, callerSave, nil}, aux: "Int64"}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
-		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64"},                                  // call deferproc.  arg0=mem, auxint=argsize, returns mem
-		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64"},                                     // call newproc.  arg0=mem, auxint=argsize, returns mem
-		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64"},           // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
+		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true},                                             // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
+		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                               // call deferproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                  // call newproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                        // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
 
 		// arg0 = destination pointer
 		// arg1 = source pointer
@@ -472,8 +456,9 @@ func init() {
 			argLength: 3,
 			reg: regInfo{
 				inputs:   []regMask{buildReg("DI"), buildReg("SI")},
-				clobbers: buildReg("DI SI X0 FLAGS"), // uses X0 as a temporary
+				clobbers: buildReg("DI SI X0"), // uses X0 as a temporary
 			},
+			clobberFlags: true,
 		},
 
 		// arg0 = destination pointer
@@ -504,14 +489,15 @@ func init() {
 		// use of DX (the closure pointer)
 		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("DX")}}},
 		//arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
-		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpsp}, clobbers: flags}},
+		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
 
 		// MOVQconvert converts between pointers and integers.
 		// We have a special op for this so as to not confuse GC
 		// (particularly stack maps).  It takes a memory arg so it
 		// gets correctly ordered with respect to GC safepoints.
 		// arg0=ptr/int arg1=mem, output=int/ptr
-		{name: "MOVQconvert", argLength: 2, reg: gp11nf, asm: "MOVQ"},
+		{name: "MOVQconvert", argLength: 2, reg: gp11, asm: "MOVQ"},
+		{name: "MOVLconvert", argLength: 2, reg: gp11, asm: "MOVL"}, // amd64p32 equivalent
 
 		// Constant flag values. For any comparison, there are 5 possible
 		// outcomes: the three from the signed total order (<,==,>) and the
@@ -525,6 +511,54 @@ func init() {
 		{name: "FlagLT_UGT"}, // signed < and unsigned >
 		{name: "FlagGT_UGT"}, // signed > and unsigned <
 		{name: "FlagGT_ULT"}, // signed > and unsigned >
+
+		// Atomic loads.  These are just normal loads but return <value,memory> tuples
+		// so they can be properly ordered with other loads.
+		// load from arg0+auxint+aux.  arg1=mem.
+		{name: "MOVLatomicload", argLength: 2, reg: gpload, asm: "MOVL", aux: "SymOff", faultOnNilArg0: true},
+		{name: "MOVQatomicload", argLength: 2, reg: gpload, asm: "MOVQ", aux: "SymOff", faultOnNilArg0: true},
+
+		// Atomic stores and exchanges.  Stores use XCHG to get the right memory ordering semantics.
+		// store arg0 to arg1+auxint+aux, arg2=mem.
+		// These ops return a tuple of <old contents of *(arg1+auxint+aux), memory>.
+		// Note: arg0 and arg1 are backwards compared to MOVLstore (to facilitate resultInArg0)!
+		{name: "XCHGL", argLength: 3, reg: gpstorexchg, asm: "XCHGL", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true},
+		{name: "XCHGQ", argLength: 3, reg: gpstorexchg, asm: "XCHGQ", aux: "SymOff", resultInArg0: true, faultOnNilArg1: true},
+
+		// Atomic adds.
+		// *(arg1+auxint+aux) += arg0.  arg2=mem.
+		// Returns a tuple of <old contents of *(arg1+auxint+aux), memory>.
+		// Note: arg0 and arg1 are backwards compared to MOVLstore (to facilitate resultInArg0)!
+		{name: "XADDLlock", argLength: 3, reg: gpstorexchg, asm: "XADDL", typ: "(UInt32,Mem)", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},
+		{name: "XADDQlock", argLength: 3, reg: gpstorexchg, asm: "XADDQ", typ: "(UInt64,Mem)", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},
+		{name: "AddTupleFirst32", argLength: 2}, // arg0=tuple <x,y>.  Returns <x+arg1,y>.
+		{name: "AddTupleFirst64", argLength: 2}, // arg0=tuple <x,y>.  Returns <x+arg1,y>.
+
+		// Compare and swap.
+		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
+		// if *(arg0+auxint+aux) == arg1 {
+		//   *(arg0+auxint+aux) = arg2
+		//   return (true, memory)
+		// } else {
+		//   return (false, memory)
+		// }
+		// Note that these instructions also return the old value in AX, but we ignore it.
+		// TODO: have these return flags instead of bool.  The current system generates:
+		//    CMPXCHGQ ...
+		//    SETEQ AX
+		//    CMPB  AX, $0
+		//    JNE ...
+		// instead of just
+		//    CMPXCHGQ ...
+		//    JEQ ...
+		// but we can't do that because memory-using ops can't generate flags yet
+		// (flagalloc wants to move flag-generating instructions around).
+		{name: "CMPXCHGLlock", argLength: 4, reg: cmpxchg, asm: "CMPXCHGL", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
+		{name: "CMPXCHGQlock", argLength: 4, reg: cmpxchg, asm: "CMPXCHGQ", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
+
+		// Atomic memory updates.
+		{name: "ANDBlock", argLength: 3, reg: gpstore, asm: "ANDB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true}, // *(arg0+auxint+aux) &= arg1
+		{name: "ORBlock", argLength: 3, reg: gpstore, asm: "ORB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},   // *(arg0+auxint+aux) |= arg1
 	}
 
 	var AMD64blocks = []blockData{
@@ -545,11 +579,15 @@ func init() {
 	}
 
 	archs = append(archs, arch{
-		name:     "AMD64",
-		pkg:      "cmd/internal/obj/x86",
-		genfile:  "../../amd64/ssa.go",
-		ops:      AMD64ops,
-		blocks:   AMD64blocks,
-		regnames: regNamesAMD64,
+		name:            "AMD64",
+		pkg:             "cmd/internal/obj/x86",
+		genfile:         "../../amd64/ssa.go",
+		ops:             AMD64ops,
+		blocks:          AMD64blocks,
+		regnames:        regNamesAMD64,
+		gpregmask:       gp,
+		fpregmask:       fp,
+		framepointerreg: int8(num["BP"]),
+		linkreg:         -1, // not used
 	})
 }
diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules
index 273500f..bea9d6c 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM.rules
@@ -2,31 +2,1244 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+(AddPtr x y) -> (ADD x y)
 (Add32 x y) -> (ADD x y)
+(Add16 x y) -> (ADD x y)
+(Add8 x y) -> (ADD x y)
+(Add32F x y) -> (ADDF x y)
+(Add64F x y) -> (ADDD x y)
 
+(Add32carry x y) -> (ADDS x y)
+(Add32withcarry x y c) -> (ADC x y c)
+
+(SubPtr x y) -> (SUB x y)
+(Sub32 x y) -> (SUB x y)
+(Sub16 x y) -> (SUB x y)
+(Sub8 x y) -> (SUB x y)
+(Sub32F x y) -> (SUBF x y)
+(Sub64F x y) -> (SUBD x y)
+
+(Sub32carry x y) -> (SUBS x y)
+(Sub32withcarry x y c) -> (SBC x y c)
+
+(Mul32 x y) -> (MUL x y)
+(Mul16 x y) -> (MUL x y)
+(Mul8 x y) -> (MUL x y)
+(Mul32F x y) -> (MULF x y)
+(Mul64F x y) -> (MULD x y)
+
+(Hmul32 x y) -> (HMUL x y)
+(Hmul32u x y) -> (HMULU x y)
+(Hmul16 x y) -> (SRAconst (MUL <config.fe.TypeInt32()> (SignExt16to32 x) (SignExt16to32 y)) [16])
+(Hmul16u x y) -> (SRLconst (MUL <config.fe.TypeUInt32()> (ZeroExt16to32 x) (ZeroExt16to32 y)) [16])
+(Hmul8 x y) -> (SRAconst (MUL <config.fe.TypeInt16()> (SignExt8to32 x) (SignExt8to32 y)) [8])
+(Hmul8u x y) -> (SRLconst (MUL <config.fe.TypeUInt16()> (ZeroExt8to32 x) (ZeroExt8to32 y)) [8])
+
+(Mul32uhilo x y) -> (MULLU x y)
+
+(Div32 x y) ->
+	(SUB (XOR <config.fe.TypeUInt32()>                                                                  // negate the result if one operand is negative
+		(Select0 <config.fe.TypeUInt32()> (UDIVrtcall
+			(SUB <config.fe.TypeUInt32()> (XOR x <config.fe.TypeUInt32()> (Signmask x)) (Signmask x))   // negate x if negative
+			(SUB <config.fe.TypeUInt32()> (XOR y <config.fe.TypeUInt32()> (Signmask y)) (Signmask y)))) // negate y if negative
+		(Signmask (XOR <config.fe.TypeUInt32()> x y))) (Signmask (XOR <config.fe.TypeUInt32()> x y)))
+(Div32u x y) -> (Select0 <config.fe.TypeUInt32()> (UDIVrtcall x y))
+(Div16 x y) -> (Div32 (SignExt16to32 x) (SignExt16to32 y))
+(Div16u x y) -> (Div32u (ZeroExt16to32 x) (ZeroExt16to32 y))
+(Div8 x y) -> (Div32 (SignExt8to32 x) (SignExt8to32 y))
+(Div8u x y) -> (Div32u (ZeroExt8to32 x) (ZeroExt8to32 y))
+(Div32F x y) -> (DIVF x y)
+(Div64F x y) -> (DIVD x y)
+
+(Mod32 x y) ->
+	(SUB (XOR <config.fe.TypeUInt32()>                                                                  // negate the result if x is negative
+		(Select1 <config.fe.TypeUInt32()> (UDIVrtcall
+			(SUB <config.fe.TypeUInt32()> (XOR <config.fe.TypeUInt32()> x (Signmask x)) (Signmask x))   // negate x if negative
+			(SUB <config.fe.TypeUInt32()> (XOR <config.fe.TypeUInt32()> y (Signmask y)) (Signmask y)))) // negate y if negative
+		(Signmask x)) (Signmask x))
+(Mod32u x y) -> (Select1 <config.fe.TypeUInt32()> (UDIVrtcall x y))
+(Mod16 x y) -> (Mod32 (SignExt16to32 x) (SignExt16to32 y))
+(Mod16u x y) -> (Mod32u (ZeroExt16to32 x) (ZeroExt16to32 y))
+(Mod8 x y) -> (Mod32 (SignExt8to32 x) (SignExt8to32 y))
+(Mod8u x y) -> (Mod32u (ZeroExt8to32 x) (ZeroExt8to32 y))
+
+(And32 x y) -> (AND x y)
+(And16 x y) -> (AND x y)
+(And8 x y) -> (AND x y)
+
+(Or32 x y) -> (OR x y)
+(Or16 x y) -> (OR x y)
+(Or8 x y) -> (OR x y)
+
+(Xor32 x y) -> (XOR x y)
+(Xor16 x y) -> (XOR x y)
+(Xor8 x y) -> (XOR x y)
+
+// unary ops
+(Neg32 x) -> (RSBconst [0] x)
+(Neg16 x) -> (RSBconst [0] x)
+(Neg8 x) -> (RSBconst [0] x)
+(Neg32F x) -> (NEGF x)
+(Neg64F x) -> (NEGD x)
+
+(Com32 x) -> (MVN x)
+(Com16 x) -> (MVN x)
+(Com8 x) -> (MVN x)
+
+(Sqrt x) -> (SQRTD x)
+
+// count trailing zero
+// 32 - CLZ(x&-x - 1)
+(Ctz32 <t> x) -> (RSBconst [32] (CLZ <t> (SUBconst <t> (AND <t> x (RSBconst <t> [0] x)) [1])))
+
+// byte swap
+// let (a, b, c, d) be the bytes of x from high to low
+// t1 = x right rotate 16 bits -- (c,   d,   a,   b  )
+// t2 = x ^ t1                 -- (a^c, b^d, a^c, b^d)
+// t3 = t2 &^ 0xff0000         -- (a^c, 0,   a^c, b^d)
+// t4 = t3 >> 8                -- (0,   a^c, 0,   a^c)
+// t5 = x right rotate 8 bits  -- (d,   a,   b,   c  )
+// result = t4 ^ t5            -- (d,   c,   b,   a  )
+// using shifted ops this can be done in 4 instructions.
+(Bswap32 <t> x) ->
+	(XOR <t>
+		(SRLconst <t> (BICconst <t> (XOR <t> x (SRRconst <t> [16] x)) [0xff0000]) [8])
+		(SRRconst <t> x [8]))
+
+// boolean ops -- booleans are represented with 0=false, 1=true
+(AndB x y) -> (AND x y)
+(OrB x y) -> (OR x y)
+(EqB x y) -> (XORconst [1] (XOR <config.fe.TypeBool()> x y))
+(NeqB x y) -> (XOR x y)
+(Not x) -> (XORconst [1] x)
+
+// shifts
+// hardware instruction uses only the low byte of the shift
+// we compare to 256 to ensure Go semantics for large shifts
+(Lsh32x32 x y) -> (CMOVWHSconst (SLL <x.Type> x y) (CMPconst [256] y) [0])
+(Lsh32x16 x y) -> (CMOVWHSconst (SLL <x.Type> x (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+(Lsh32x8  x y) -> (SLL x (ZeroExt8to32 y))
+
+(Lsh16x32 x y) -> (CMOVWHSconst (SLL <x.Type> x y) (CMPconst [256] y) [0])
+(Lsh16x16 x y) -> (CMOVWHSconst (SLL <x.Type> x (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+(Lsh16x8  x y) -> (SLL x (ZeroExt8to32 y))
+
+(Lsh8x32 x y) -> (CMOVWHSconst (SLL <x.Type> x y) (CMPconst [256] y) [0])
+(Lsh8x16 x y) -> (CMOVWHSconst (SLL <x.Type> x (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+(Lsh8x8  x y) -> (SLL x (ZeroExt8to32 y))
+
+(Rsh32Ux32 x y) -> (CMOVWHSconst (SRL <x.Type> x y) (CMPconst [256] y) [0])
+(Rsh32Ux16 x y) -> (CMOVWHSconst (SRL <x.Type> x (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+(Rsh32Ux8  x y) -> (SRL x (ZeroExt8to32 y))
+
+(Rsh16Ux32 x y) -> (CMOVWHSconst (SRL <x.Type> (ZeroExt16to32 x) y) (CMPconst [256] y) [0])
+(Rsh16Ux16 x y) -> (CMOVWHSconst (SRL <x.Type> (ZeroExt16to32 x) (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+(Rsh16Ux8  x y) -> (SRL (ZeroExt16to32 x) (ZeroExt8to32 y))
+
+(Rsh8Ux32 x y) -> (CMOVWHSconst (SRL <x.Type> (ZeroExt8to32 x) y) (CMPconst [256] y) [0])
+(Rsh8Ux16 x y) -> (CMOVWHSconst (SRL <x.Type> (ZeroExt8to32 x) (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+(Rsh8Ux8  x y) -> (SRL (ZeroExt8to32 x) (ZeroExt8to32 y))
+
+(Rsh32x32 x y) -> (SRAcond x y (CMPconst [256] y))
+(Rsh32x16 x y) -> (SRAcond x (ZeroExt16to32 y) (CMPconst [256] (ZeroExt16to32 y)))
+(Rsh32x8  x y) -> (SRA x (ZeroExt8to32 y))
+
+(Rsh16x32 x y) -> (SRAcond (SignExt16to32 x) y (CMPconst [256] y))
+(Rsh16x16 x y) -> (SRAcond (SignExt16to32 x) (ZeroExt16to32 y) (CMPconst [256] (ZeroExt16to32 y)))
+(Rsh16x8  x y) -> (SRA (SignExt16to32 x) (ZeroExt8to32 y))
+
+(Rsh8x32 x y) -> (SRAcond (SignExt8to32 x) y (CMPconst [256] y))
+(Rsh8x16 x y) -> (SRAcond (SignExt8to32 x) (ZeroExt16to32 y) (CMPconst [256] (ZeroExt16to32 y)))
+(Rsh8x8  x y) -> (SRA (SignExt8to32 x) (ZeroExt8to32 y))
+
+// constant shifts
+// generic opt rewrites all constant shifts to shift by Const64
+(Lsh32x64 x (Const64 [c])) && uint64(c) < 32 -> (SLLconst x [c])
+(Rsh32x64 x (Const64 [c])) && uint64(c) < 32 -> (SRAconst x [c])
+(Rsh32Ux64 x (Const64 [c])) && uint64(c) < 32 -> (SRLconst x [c])
+(Lsh16x64 x (Const64 [c])) && uint64(c) < 16 -> (SLLconst x [c])
+(Rsh16x64 x (Const64 [c])) && uint64(c) < 16 -> (SRAconst (SLLconst <config.fe.TypeUInt32()> x [16]) [c+16])
+(Rsh16Ux64 x (Const64 [c])) && uint64(c) < 16 -> (SRLconst (SLLconst <config.fe.TypeUInt32()> x [16]) [c+16])
+(Lsh8x64 x (Const64 [c])) && uint64(c) < 8 -> (SLLconst x [c])
+(Rsh8x64 x (Const64 [c])) && uint64(c) < 8 -> (SRAconst (SLLconst <config.fe.TypeUInt32()> x [24]) [c+24])
+(Rsh8Ux64 x (Const64 [c])) && uint64(c) < 8 -> (SRLconst (SLLconst <config.fe.TypeUInt32()> x [24]) [c+24])
+
+// large constant shifts
+(Lsh32x64 _ (Const64 [c])) && uint64(c) >= 32 -> (Const32 [0])
+(Rsh32Ux64 _ (Const64 [c])) && uint64(c) >= 32 -> (Const32 [0])
+(Lsh16x64 _ (Const64 [c])) && uint64(c) >= 16 -> (Const16 [0])
+(Rsh16Ux64 _ (Const64 [c])) && uint64(c) >= 16 -> (Const16 [0])
+(Lsh8x64 _ (Const64 [c])) && uint64(c) >= 8 -> (Const8 [0])
+(Rsh8Ux64 _ (Const64 [c])) && uint64(c) >= 8 -> (Const8 [0])
+
+// large constant signed right shift, we leave the sign bit
+(Rsh32x64 x (Const64 [c])) && uint64(c) >= 32 -> (SRAconst x [31])
+(Rsh16x64 x (Const64 [c])) && uint64(c) >= 16 -> (SRAconst (SLLconst <config.fe.TypeUInt32()> x [16]) [31])
+(Rsh8x64 x (Const64 [c])) && uint64(c) >= 8 -> (SRAconst (SLLconst <config.fe.TypeUInt32()> x [24]) [31])
+
+(Lrot32 x [c]) -> (SRRconst x [32-c&31])
+(Lrot16 <t> x [c]) -> (OR (SLLconst <t> x [c&15]) (SRLconst <t> x [16-c&15]))
+(Lrot8 <t> x [c]) -> (OR (SLLconst <t> x [c&7]) (SRLconst <t> x [8-c&7]))
+
+// constants
+(Const8 [val]) -> (MOVWconst [val])
+(Const16 [val]) -> (MOVWconst [val])
 (Const32 [val]) -> (MOVWconst [val])
+(Const32F [val]) -> (MOVFconst [val])
+(Const64F [val]) -> (MOVDconst [val])
+(ConstNil) -> (MOVWconst [0])
+(ConstBool [b]) -> (MOVWconst [b])
+
+// truncations
+// Because we ignore high parts of registers, truncates are just copies.
+(Trunc16to8 x) -> x
+(Trunc32to8 x) -> x
+(Trunc32to16 x) -> x
+
+// Zero-/Sign-extensions
+(ZeroExt8to16 x) -> (MOVBUreg x)
+(ZeroExt8to32 x) -> (MOVBUreg x)
+(ZeroExt16to32 x) -> (MOVHUreg x)
 
+(SignExt8to16 x) -> (MOVBreg x)
+(SignExt8to32 x) -> (MOVBreg x)
+(SignExt16to32 x) -> (MOVHreg x)
+
+(Signmask x) -> (SRAconst x [31])
+(Zeromask x) -> (SRAconst (RSBshiftRL <config.fe.TypeInt32()> x x [1]) [31]) // sign bit of uint32(x)>>1 - x
+(Slicemask <t> x) -> (MVN (SRAconst <t> (SUBconst <t> x [1]) [31]))
+
+// float <-> int conversion
+(Cvt32to32F x) -> (MOVWF x)
+(Cvt32to64F x) -> (MOVWD x)
+(Cvt32Uto32F x) -> (MOVWUF x)
+(Cvt32Uto64F x) -> (MOVWUD x)
+(Cvt32Fto32 x) -> (MOVFW x)
+(Cvt64Fto32 x) -> (MOVDW x)
+(Cvt32Fto32U x) -> (MOVFWU x)
+(Cvt64Fto32U x) -> (MOVDWU x)
+(Cvt32Fto64F x) -> (MOVFD x)
+(Cvt64Fto32F x) -> (MOVDF x)
+
+// comparisons
+(Eq8 x y)  -> (Equal (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Eq16 x y) -> (Equal (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Eq32 x y) -> (Equal (CMP x y))
+(EqPtr x y) -> (Equal (CMP x y))
+(Eq32F x y) -> (Equal (CMPF x y))
+(Eq64F x y) -> (Equal (CMPD x y))
+
+(Neq8 x y)  -> (NotEqual (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Neq16 x y) -> (NotEqual (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Neq32 x y) -> (NotEqual (CMP x y))
+(NeqPtr x y) -> (NotEqual (CMP x y))
+(Neq32F x y) -> (NotEqual (CMPF x y))
+(Neq64F x y) -> (NotEqual (CMPD x y))
+
+(Less8 x y)  -> (LessThan (CMP (SignExt8to32 x) (SignExt8to32 y)))
+(Less16 x y) -> (LessThan (CMP (SignExt16to32 x) (SignExt16to32 y)))
 (Less32 x y) -> (LessThan (CMP x y))
+(Less32F x y) -> (GreaterThan (CMPF y x)) // reverse operands to work around NaN
+(Less64F x y) -> (GreaterThan (CMPD y x)) // reverse operands to work around NaN
+
+(Less8U x y)  -> (LessThanU (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Less16U x y) -> (LessThanU (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Less32U x y) -> (LessThanU (CMP x y))
+
+(Leq8 x y)  -> (LessEqual (CMP (SignExt8to32 x) (SignExt8to32 y)))
+(Leq16 x y) -> (LessEqual (CMP (SignExt16to32 x) (SignExt16to32 y)))
+(Leq32 x y) -> (LessEqual (CMP x y))
+(Leq32F x y) -> (GreaterEqual (CMPF y x)) // reverse operands to work around NaN
+(Leq64F x y) -> (GreaterEqual (CMPD y x)) // reverse operands to work around NaN
+
+(Leq8U x y)  -> (LessEqualU (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Leq16U x y) -> (LessEqualU (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Leq32U x y) -> (LessEqualU (CMP x y))
+
+(Greater8 x y)  -> (GreaterThan (CMP (SignExt8to32 x) (SignExt8to32 y)))
+(Greater16 x y) -> (GreaterThan (CMP (SignExt16to32 x) (SignExt16to32 y)))
+(Greater32 x y) -> (GreaterThan (CMP x y))
+(Greater32F x y) -> (GreaterThan (CMPF x y))
+(Greater64F x y) -> (GreaterThan (CMPD x y))
+
+(Greater8U x y)  -> (GreaterThanU (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Greater16U x y) -> (GreaterThanU (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Greater32U x y) -> (GreaterThanU (CMP x y))
+
+(Geq8 x y)  -> (GreaterEqual (CMP (SignExt8to32 x) (SignExt8to32 y)))
+(Geq16 x y) -> (GreaterEqual (CMP (SignExt16to32 x) (SignExt16to32 y)))
+(Geq32 x y) -> (GreaterEqual (CMP x y))
+(Geq32F x y) -> (GreaterEqual (CMPF x y))
+(Geq64F x y) -> (GreaterEqual (CMPD x y))
+
+(Geq8U x y)  -> (GreaterEqualU (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Geq16U x y) -> (GreaterEqualU (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Geq32U x y) -> (GreaterEqualU (CMP x y))
+
+(OffPtr [off] ptr:(SP)) -> (MOVWaddr [off] ptr)
+(OffPtr [off] ptr) -> (ADDconst [off] ptr)
+
+(Addr {sym} base) -> (MOVWaddr {sym} base)
+
+// loads
+(Load <t> ptr mem) && t.IsBoolean() -> (MOVBUload ptr mem)
+(Load <t> ptr mem) && (is8BitInt(t) && isSigned(t)) -> (MOVBload ptr mem)
+(Load <t> ptr mem) && (is8BitInt(t) && !isSigned(t)) -> (MOVBUload ptr mem)
+(Load <t> ptr mem) && (is16BitInt(t) && isSigned(t)) -> (MOVHload ptr mem)
+(Load <t> ptr mem) && (is16BitInt(t) && !isSigned(t)) -> (MOVHUload ptr mem)
+(Load <t> ptr mem) && (is32BitInt(t) || isPtr(t)) -> (MOVWload ptr mem)
+(Load <t> ptr mem) && is32BitFloat(t) -> (MOVFload ptr mem)
+(Load <t> ptr mem) && is64BitFloat(t) -> (MOVDload ptr mem)
+
+// stores
+(Store [1] ptr val mem) -> (MOVBstore ptr val mem)
+(Store [2] ptr val mem) -> (MOVHstore ptr val mem)
+(Store [4] ptr val mem) && !is32BitFloat(val.Type) -> (MOVWstore ptr val mem)
+(Store [4] ptr val mem) && is32BitFloat(val.Type) -> (MOVFstore ptr val mem)
+(Store [8] ptr val mem) && is64BitFloat(val.Type) -> (MOVDstore ptr val mem)
 
-(OffPtr [off] ptr) -> (ADD (MOVWconst <config.Frontend().TypeInt32()> [off]) ptr)
+// zero instructions
+(Zero [s] _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore ptr (MOVWconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore ptr (MOVWconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 2 ->
+	(MOVBstore [1] ptr (MOVWconst [0])
+		(MOVBstore [0] ptr (MOVWconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore ptr (MOVWconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [2] ptr (MOVWconst [0])
+		(MOVHstore [0] ptr (MOVWconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 ->
+	(MOVBstore [3] ptr (MOVWconst [0])
+		(MOVBstore [2] ptr (MOVWconst [0])
+			(MOVBstore [1] ptr (MOVWconst [0])
+				(MOVBstore [0] ptr (MOVWconst [0]) mem))))
 
-(Addr {sym} base) -> (ADDconst {sym} base)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] ptr (MOVWconst [0])
+		(MOVBstore [1] ptr (MOVWconst [0])
+			(MOVBstore [0] ptr (MOVWconst [0]) mem)))
 
-(Load <t> ptr mem) && is32BitInt(t) -> (MOVWload ptr mem)
-(Store [4] ptr val mem) -> (MOVWstore ptr val mem)
+// Medium zeroing uses a duff device
+// 4 and 128 are magic constants, see runtime/mkduff.go
+(Zero [s] ptr mem)
+	&& SizeAndAlign(s).Size()%4 == 0 && SizeAndAlign(s).Size() > 4 && SizeAndAlign(s).Size() <= 512
+	&& SizeAndAlign(s).Align()%4 == 0 && !config.noDuffDevice ->
+	(DUFFZERO [4 * (128 - int64(SizeAndAlign(s).Size()/4))] ptr (MOVWconst [0]) mem)
 
+// Large zeroing uses a loop
+(Zero [s] ptr mem)
+	&& (SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%4 != 0 ->
+	(LoweredZero [SizeAndAlign(s).Align()]
+		ptr
+		(ADDconst <ptr.Type> ptr [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)])
+		(MOVWconst [0])
+		mem)
+
+// moves
+(Move [s] _ _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore dst (MOVBUload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore dst (MOVHUload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 ->
+	(MOVBstore [1] dst (MOVBUload [1] src mem)
+		(MOVBstore dst (MOVBUload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore dst (MOVWload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [2] dst (MOVHUload [2] src mem)
+		(MOVHstore dst (MOVHUload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 ->
+	(MOVBstore [3] dst (MOVBUload [3] src mem)
+		(MOVBstore [2] dst (MOVBUload [2] src mem)
+			(MOVBstore [1] dst (MOVBUload [1] src mem)
+				(MOVBstore dst (MOVBUload src mem) mem))))
+
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] dst (MOVBUload [2] src mem)
+		(MOVBstore [1] dst (MOVBUload [1] src mem)
+			(MOVBstore dst (MOVBUload src mem) mem)))
+
+// Medium move uses a duff device
+// 8 and 128 are magic constants, see runtime/mkduff.go
+(Move [s] dst src mem)
+	&& SizeAndAlign(s).Size()%4 == 0 && SizeAndAlign(s).Size() > 4 && SizeAndAlign(s).Size() <= 512
+	&& SizeAndAlign(s).Align()%4 == 0 && !config.noDuffDevice ->
+	(DUFFCOPY [8 * (128 - int64(SizeAndAlign(s).Size()/4))] dst src mem)
+
+// Large move uses a loop
+(Move [s] dst src mem)
+	&& (SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%4 != 0 ->
+	(LoweredMove [SizeAndAlign(s).Align()]
+		dst
+		src
+		(ADDconst <src.Type> src [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)])
+		mem)
+
+// calls
 (StaticCall [argwid] {target} mem) -> (CALLstatic [argwid] {target} mem)
+(ClosureCall [argwid] entry closure mem) -> (CALLclosure [argwid] entry closure mem)
+(DeferCall [argwid] mem) -> (CALLdefer [argwid] mem)
+(GoCall [argwid] mem) -> (CALLgo [argwid] mem)
+(InterCall [argwid] entry mem) -> (CALLinter [argwid] entry mem)
+
+// checks
+(NilCheck ptr mem) -> (LoweredNilCheck ptr mem)
+(IsNonNil ptr) -> (NotEqual (CMPconst [0] ptr))
+(IsInBounds idx len) -> (LessThanU (CMP idx len))
+(IsSliceInBounds idx len) -> (LessEqualU (CMP idx len))
 
-// Absorb LessThan into blocks.
+// pseudo-ops
+(GetClosurePtr) -> (LoweredGetClosurePtr)
+(Convert x mem) -> (MOVWconvert x mem)
+
+// Absorb pseudo-ops into blocks.
+(If (Equal cc) yes no) -> (EQ cc yes no)
+(If (NotEqual cc) yes no) -> (NE cc yes no)
 (If (LessThan cc) yes no) -> (LT cc yes no)
+(If (LessThanU cc) yes no) -> (ULT cc yes no)
+(If (LessEqual cc) yes no) -> (LE cc yes no)
+(If (LessEqualU cc) yes no) -> (ULE cc yes no)
+(If (GreaterThan cc) yes no) -> (GT cc yes no)
+(If (GreaterThanU cc) yes no) -> (UGT cc yes no)
+(If (GreaterEqual cc) yes no) -> (GE cc yes no)
+(If (GreaterEqualU cc) yes no) -> (UGE cc yes no)
 
+(If cond yes no) -> (NE (CMPconst [0] cond) yes no)
 
+// Absorb boolean tests into block
+(NE (CMPconst [0] (Equal cc)) yes no) -> (EQ cc yes no)
+(NE (CMPconst [0] (NotEqual cc)) yes no) -> (NE cc yes no)
+(NE (CMPconst [0] (LessThan cc)) yes no) -> (LT cc yes no)
+(NE (CMPconst [0] (LessThanU cc)) yes no) -> (ULT cc yes no)
+(NE (CMPconst [0] (LessEqual cc)) yes no) -> (LE cc yes no)
+(NE (CMPconst [0] (LessEqualU cc)) yes no) -> (ULE cc yes no)
+(NE (CMPconst [0] (GreaterThan cc)) yes no) -> (GT cc yes no)
+(NE (CMPconst [0] (GreaterThanU cc)) yes no) -> (UGT cc yes no)
+(NE (CMPconst [0] (GreaterEqual cc)) yes no) -> (GE cc yes no)
+(NE (CMPconst [0] (GreaterEqualU cc)) yes no) -> (UGE cc yes no)
 
 // Optimizations
 
+// fold offset into address
+(ADDconst [off1] (MOVWaddr [off2] {sym} ptr)) -> (MOVWaddr [off1+off2] {sym} ptr)
+
+// fold address into load/store
+(MOVBload [off1] {sym} (ADDconst [off2] ptr) mem) -> (MOVBload [off1+off2] {sym} ptr mem)
+(MOVBUload [off1] {sym} (ADDconst [off2] ptr) mem) -> (MOVBUload [off1+off2] {sym} ptr mem)
+(MOVHload [off1] {sym} (ADDconst [off2] ptr) mem) -> (MOVHload [off1+off2] {sym} ptr mem)
+(MOVHUload [off1] {sym} (ADDconst [off2] ptr) mem) -> (MOVHUload [off1+off2] {sym} ptr mem)
+(MOVWload [off1] {sym} (ADDconst [off2] ptr) mem) -> (MOVWload [off1+off2] {sym} ptr mem)
+(MOVFload [off1] {sym} (ADDconst [off2] ptr) mem) -> (MOVFload [off1+off2] {sym} ptr mem)
+(MOVDload [off1] {sym} (ADDconst [off2] ptr) mem) -> (MOVDload [off1+off2] {sym} ptr mem)
+
+(MOVBstore [off1] {sym} (ADDconst [off2] ptr) val mem) -> (MOVBstore [off1+off2] {sym} ptr val mem)
+(MOVHstore [off1] {sym} (ADDconst [off2] ptr) val mem) -> (MOVHstore [off1+off2] {sym} ptr val mem)
+(MOVWstore [off1] {sym} (ADDconst [off2] ptr) val mem) -> (MOVWstore [off1+off2] {sym} ptr val mem)
+(MOVFstore [off1] {sym} (ADDconst [off2] ptr) val mem) -> (MOVFstore [off1+off2] {sym} ptr val mem)
+(MOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem) -> (MOVDstore [off1+off2] {sym} ptr val mem)
+
+(MOVBload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVBload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVBUload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVBUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHUload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVFload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVFload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVDload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+
+(MOVBstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVHstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVWstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVFstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVFstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVDstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+
+// replace load from same location as preceding store with copy
+(MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type) -> x
+(MOVBUload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type) -> x
+(MOVHload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type) -> x
+(MOVHUload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type) -> x
+(MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+(MOVFload [off] {sym} ptr (MOVFstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+(MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+
+(MOVWloadidx ptr idx (MOVWstoreidx ptr2 idx x _)) && isSamePtr(ptr, ptr2) -> x
+(MOVWloadshiftLL ptr idx [c] (MOVWstoreshiftLL ptr2 idx [d] x _)) && c==d && isSamePtr(ptr, ptr2) -> x
+(MOVWloadshiftRL ptr idx [c] (MOVWstoreshiftRL ptr2 idx [d] x _)) && c==d && isSamePtr(ptr, ptr2) -> x
+(MOVWloadshiftRA ptr idx [c] (MOVWstoreshiftRA ptr2 idx [d] x _)) && c==d && isSamePtr(ptr, ptr2) -> x
+
+// fold constant into arithmatic ops
 (ADD (MOVWconst [c]) x) -> (ADDconst [c] x)
 (ADD x (MOVWconst [c])) -> (ADDconst [c] x)
-(MOVWload [off1] {sym1} (ADDconst [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
-  (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
-(MOVWstore [off1] {sym1} (ADDconst [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
-  (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(SUB (MOVWconst [c]) x) -> (RSBconst [c] x)
+(SUB x (MOVWconst [c])) -> (SUBconst [c] x)
+(RSB (MOVWconst [c]) x) -> (SUBconst [c] x)
+(RSB x (MOVWconst [c])) -> (RSBconst [c] x)
+
+(ADDS (MOVWconst [c]) x) -> (ADDSconst [c] x)
+(ADDS x (MOVWconst [c])) -> (ADDSconst [c] x)
+(SUBS (MOVWconst [c]) x) -> (RSBSconst [c] x)
+(SUBS x (MOVWconst [c])) -> (SUBSconst [c] x)
+
+(ADC (MOVWconst [c]) x flags) -> (ADCconst [c] x flags)
+(ADC x (MOVWconst [c]) flags) -> (ADCconst [c] x flags)
+(SBC (MOVWconst [c]) x flags) -> (RSCconst [c] x flags)
+(SBC x (MOVWconst [c]) flags) -> (SBCconst [c] x flags)
+
+(AND (MOVWconst [c]) x) -> (ANDconst [c] x)
+(AND x (MOVWconst [c])) -> (ANDconst [c] x)
+(OR (MOVWconst [c]) x) -> (ORconst [c] x)
+(OR x (MOVWconst [c])) -> (ORconst [c] x)
+(XOR (MOVWconst [c]) x) -> (XORconst [c] x)
+(XOR x (MOVWconst [c])) -> (XORconst [c] x)
+(BIC x (MOVWconst [c])) -> (BICconst [c] x)
+
+(SLL x (MOVWconst [c])) -> (SLLconst x [c&31]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=32)
+(SRL x (MOVWconst [c])) -> (SRLconst x [c&31])
+(SRA x (MOVWconst [c])) -> (SRAconst x [c&31])
+
+(CMP x (MOVWconst [c])) -> (CMPconst [c] x)
+(CMP (MOVWconst [c]) x) -> (InvertFlags (CMPconst [c] x))
+
+// don't extend after proper load
+// MOVWreg instruction is not emitted if src and dst registers are same, but it ensures the type.
+(MOVBreg x:(MOVBload _ _)) -> (MOVWreg x)
+(MOVBUreg x:(MOVBUload _ _)) -> (MOVWreg x)
+(MOVHreg x:(MOVBload _ _)) -> (MOVWreg x)
+(MOVHreg x:(MOVBUload _ _)) -> (MOVWreg x)
+(MOVHreg x:(MOVHload _ _)) -> (MOVWreg x)
+(MOVHUreg x:(MOVBUload _ _)) -> (MOVWreg x)
+(MOVHUreg x:(MOVHUload _ _)) -> (MOVWreg x)
+
+// fold extensions and ANDs together
+(MOVBUreg (ANDconst [c] x)) -> (ANDconst [c&0xff] x)
+(MOVHUreg (ANDconst [c] x)) -> (ANDconst [c&0xffff] x)
+(MOVBreg (ANDconst [c] x)) && c & 0x80 == 0 -> (ANDconst [c&0x7f] x)
+(MOVHreg (ANDconst [c] x)) && c & 0x8000 == 0 -> (ANDconst [c&0x7fff] x)
+
+// fold double extensions
+(MOVBreg x:(MOVBreg _)) -> (MOVWreg x)
+(MOVBUreg x:(MOVBUreg _)) -> (MOVWreg x)
+(MOVHreg x:(MOVBreg _)) -> (MOVWreg x)
+(MOVHreg x:(MOVBUreg _)) -> (MOVWreg x)
+(MOVHreg x:(MOVHreg _)) -> (MOVWreg x)
+(MOVHUreg x:(MOVBUreg _)) -> (MOVWreg x)
+(MOVHUreg x:(MOVHUreg _)) -> (MOVWreg x)
+
+// don't extend before store
+(MOVBstore [off] {sym} ptr (MOVBreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVBUreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVHreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVHUreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHUreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+
+// if a register move has only 1 use, just use the same register without emitting instruction
+// MOVWnop doesn't emit instruction, only for ensuring the type.
+(MOVWreg x) && x.Uses == 1 -> (MOVWnop x)
+
+// mul by constant
+(MUL x (MOVWconst [c])) && int32(c) == -1 -> (RSBconst [0] x)
+(MUL _ (MOVWconst [0])) -> (MOVWconst [0])
+(MUL x (MOVWconst [1])) -> x
+(MUL x (MOVWconst [c])) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x)
+(MUL x (MOVWconst [c])) && isPowerOfTwo(c-1) && int32(c) >= 3 -> (ADDshiftLL x x [log2(c-1)])
+(MUL x (MOVWconst [c])) && isPowerOfTwo(c+1) && int32(c) >= 7 -> (RSBshiftLL x x [log2(c+1)])
+(MUL x (MOVWconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+(MUL x (MOVWconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+(MUL x (MOVWconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (SLLconst [log2(c/7)] (RSBshiftLL <x.Type> x x [3]))
+(MUL x (MOVWconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+
+(MUL (MOVWconst [c]) x) && int32(c) == -1 -> (RSBconst [0] x)
+(MUL (MOVWconst [0]) _) -> (MOVWconst [0])
+(MUL (MOVWconst [1]) x) -> x
+(MUL (MOVWconst [c]) x) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x)
+(MUL (MOVWconst [c]) x) && isPowerOfTwo(c-1) && int32(c) >= 3 -> (ADDshiftLL x x [log2(c-1)])
+(MUL (MOVWconst [c]) x) && isPowerOfTwo(c+1) && int32(c) >= 7 -> (RSBshiftLL x x [log2(c+1)])
+(MUL (MOVWconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+(MUL (MOVWconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+(MUL (MOVWconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (SLLconst [log2(c/7)] (RSBshiftLL <x.Type> x x [3]))
+(MUL (MOVWconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+
+(MULA x (MOVWconst [c]) a) && int32(c) == -1 -> (SUB a x)
+(MULA _ (MOVWconst [0]) a) -> a
+(MULA x (MOVWconst [1]) a) -> (ADD x a)
+(MULA x (MOVWconst [c]) a) && isPowerOfTwo(c) -> (ADD (SLLconst <x.Type> [log2(c)] x) a)
+(MULA x (MOVWconst [c]) a) && isPowerOfTwo(c-1) && int32(c) >= 3 -> (ADD (ADDshiftLL <x.Type> x x [log2(c-1)]) a)
+(MULA x (MOVWconst [c]) a) && isPowerOfTwo(c+1) && int32(c) >= 7 -> (ADD (RSBshiftLL <x.Type> x x [log2(c+1)]) a)
+(MULA x (MOVWconst [c]) a) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (ADD (SLLconst <x.Type> [log2(c/3)] (ADDshiftLL <x.Type> x x [1])) a)
+(MULA x (MOVWconst [c]) a) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (ADD (SLLconst <x.Type> [log2(c/5)] (ADDshiftLL <x.Type> x x [2])) a)
+(MULA x (MOVWconst [c]) a) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (ADD (SLLconst <x.Type> [log2(c/7)] (RSBshiftLL <x.Type> x x [3])) a)
+(MULA x (MOVWconst [c]) a) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (ADD (SLLconst <x.Type> [log2(c/9)] (ADDshiftLL <x.Type> x x [3])) a)
+
+(MULA (MOVWconst [c]) x a) && int32(c) == -1 -> (SUB a x)
+(MULA (MOVWconst [0]) _ a) -> a
+(MULA (MOVWconst [1]) x a) -> (ADD x a)
+(MULA (MOVWconst [c]) x a) && isPowerOfTwo(c) -> (ADD (SLLconst <x.Type> [log2(c)] x) a)
+(MULA (MOVWconst [c]) x a) && isPowerOfTwo(c-1) && int32(c) >= 3 -> (ADD (ADDshiftLL <x.Type> x x [log2(c-1)]) a)
+(MULA (MOVWconst [c]) x a) && isPowerOfTwo(c+1) && int32(c) >= 7 -> (ADD (RSBshiftLL <x.Type> x x [log2(c+1)]) a)
+(MULA (MOVWconst [c]) x a) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (ADD (SLLconst <x.Type> [log2(c/3)] (ADDshiftLL <x.Type> x x [1])) a)
+(MULA (MOVWconst [c]) x a) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (ADD (SLLconst <x.Type> [log2(c/5)] (ADDshiftLL <x.Type> x x [2])) a)
+(MULA (MOVWconst [c]) x a) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (ADD (SLLconst <x.Type> [log2(c/7)] (RSBshiftLL <x.Type> x x [3])) a)
+(MULA (MOVWconst [c]) x a) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (ADD (SLLconst <x.Type> [log2(c/9)] (ADDshiftLL <x.Type> x x [3])) a)
+
+// div by constant
+(Select0 (UDIVrtcall x (MOVWconst [1]))) -> x
+(Select1 (UDIVrtcall _ (MOVWconst [1]))) -> (MOVWconst [0])
+(Select0 (UDIVrtcall x (MOVWconst [c]))) && isPowerOfTwo(c) -> (SRLconst [log2(c)] x)
+(Select1 (UDIVrtcall x (MOVWconst [c]))) && isPowerOfTwo(c) -> (ANDconst [c-1] x)
+
+// constant comparisons
+(CMPconst (MOVWconst [x]) [y]) && int32(x)==int32(y) -> (FlagEQ)
+(CMPconst (MOVWconst [x]) [y]) && int32(x)<int32(y) && uint32(x)<uint32(y) -> (FlagLT_ULT)
+(CMPconst (MOVWconst [x]) [y]) && int32(x)<int32(y) && uint32(x)>uint32(y) -> (FlagLT_UGT)
+(CMPconst (MOVWconst [x]) [y]) && int32(x)>int32(y) && uint32(x)<uint32(y) -> (FlagGT_ULT)
+(CMPconst (MOVWconst [x]) [y]) && int32(x)>int32(y) && uint32(x)>uint32(y) -> (FlagGT_UGT)
+
+// other known comparisons
+(CMPconst (MOVBUreg _) [c]) && 0xff < c -> (FlagLT_ULT)
+(CMPconst (MOVHUreg _) [c]) && 0xffff < c -> (FlagLT_ULT)
+(CMPconst (ANDconst _ [m]) [n]) && 0 <= int32(m) && int32(m) < int32(n) -> (FlagLT_ULT)
+(CMPconst (SRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1<<uint32(32-c)) <= uint32(n) -> (FlagLT_ULT)
+
+// absorb flag constants into branches
+(EQ (FlagEQ) yes no) -> (First nil yes no)
+(EQ (FlagLT_ULT) yes no) -> (First nil no yes)
+(EQ (FlagLT_UGT) yes no) -> (First nil no yes)
+(EQ (FlagGT_ULT) yes no) -> (First nil no yes)
+(EQ (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(NE (FlagEQ) yes no) -> (First nil no yes)
+(NE (FlagLT_ULT) yes no) -> (First nil yes no)
+(NE (FlagLT_UGT) yes no) -> (First nil yes no)
+(NE (FlagGT_ULT) yes no) -> (First nil yes no)
+(NE (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(LT (FlagEQ) yes no) -> (First nil no yes)
+(LT (FlagLT_ULT) yes no) -> (First nil yes no)
+(LT (FlagLT_UGT) yes no) -> (First nil yes no)
+(LT (FlagGT_ULT) yes no) -> (First nil no yes)
+(LT (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(LE (FlagEQ) yes no) -> (First nil yes no)
+(LE (FlagLT_ULT) yes no) -> (First nil yes no)
+(LE (FlagLT_UGT) yes no) -> (First nil yes no)
+(LE (FlagGT_ULT) yes no) -> (First nil no yes)
+(LE (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(GT (FlagEQ) yes no) -> (First nil no yes)
+(GT (FlagLT_ULT) yes no) -> (First nil no yes)
+(GT (FlagLT_UGT) yes no) -> (First nil no yes)
+(GT (FlagGT_ULT) yes no) -> (First nil yes no)
+(GT (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(GE (FlagEQ) yes no) -> (First nil yes no)
+(GE (FlagLT_ULT) yes no) -> (First nil no yes)
+(GE (FlagLT_UGT) yes no) -> (First nil no yes)
+(GE (FlagGT_ULT) yes no) -> (First nil yes no)
+(GE (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(ULT (FlagEQ) yes no) -> (First nil no yes)
+(ULT (FlagLT_ULT) yes no) -> (First nil yes no)
+(ULT (FlagLT_UGT) yes no) -> (First nil no yes)
+(ULT (FlagGT_ULT) yes no) -> (First nil yes no)
+(ULT (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(ULE (FlagEQ) yes no) -> (First nil yes no)
+(ULE (FlagLT_ULT) yes no) -> (First nil yes no)
+(ULE (FlagLT_UGT) yes no) -> (First nil no yes)
+(ULE (FlagGT_ULT) yes no) -> (First nil yes no)
+(ULE (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(UGT (FlagEQ) yes no) -> (First nil no yes)
+(UGT (FlagLT_ULT) yes no) -> (First nil no yes)
+(UGT (FlagLT_UGT) yes no) -> (First nil yes no)
+(UGT (FlagGT_ULT) yes no) -> (First nil no yes)
+(UGT (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(UGE (FlagEQ) yes no) -> (First nil yes no)
+(UGE (FlagLT_ULT) yes no) -> (First nil no yes)
+(UGE (FlagLT_UGT) yes no) -> (First nil yes no)
+(UGE (FlagGT_ULT) yes no) -> (First nil no yes)
+(UGE (FlagGT_UGT) yes no) -> (First nil yes no)
+
+// absorb InvertFlags into branches
+(LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
+(GT (InvertFlags cmp) yes no) -> (LT cmp yes no)
+(LE (InvertFlags cmp) yes no) -> (GE cmp yes no)
+(GE (InvertFlags cmp) yes no) -> (LE cmp yes no)
+(ULT (InvertFlags cmp) yes no) -> (UGT cmp yes no)
+(UGT (InvertFlags cmp) yes no) -> (ULT cmp yes no)
+(ULE (InvertFlags cmp) yes no) -> (UGE cmp yes no)
+(UGE (InvertFlags cmp) yes no) -> (ULE cmp yes no)
+(EQ (InvertFlags cmp) yes no) -> (EQ cmp yes no)
+(NE (InvertFlags cmp) yes no) -> (NE cmp yes no)
+
+// absorb flag constants into boolean values
+(Equal (FlagEQ)) -> (MOVWconst [1])
+(Equal (FlagLT_ULT)) -> (MOVWconst [0])
+(Equal (FlagLT_UGT)) -> (MOVWconst [0])
+(Equal (FlagGT_ULT)) -> (MOVWconst [0])
+(Equal (FlagGT_UGT)) -> (MOVWconst [0])
+
+(NotEqual (FlagEQ)) -> (MOVWconst [0])
+(NotEqual (FlagLT_ULT)) -> (MOVWconst [1])
+(NotEqual (FlagLT_UGT)) -> (MOVWconst [1])
+(NotEqual (FlagGT_ULT)) -> (MOVWconst [1])
+(NotEqual (FlagGT_UGT)) -> (MOVWconst [1])
+
+(LessThan (FlagEQ)) -> (MOVWconst [0])
+(LessThan (FlagLT_ULT)) -> (MOVWconst [1])
+(LessThan (FlagLT_UGT)) -> (MOVWconst [1])
+(LessThan (FlagGT_ULT)) -> (MOVWconst [0])
+(LessThan (FlagGT_UGT)) -> (MOVWconst [0])
+
+(LessThanU (FlagEQ)) -> (MOVWconst [0])
+(LessThanU (FlagLT_ULT)) -> (MOVWconst [1])
+(LessThanU (FlagLT_UGT)) -> (MOVWconst [0])
+(LessThanU (FlagGT_ULT)) -> (MOVWconst [1])
+(LessThanU (FlagGT_UGT)) -> (MOVWconst [0])
+
+(LessEqual (FlagEQ)) -> (MOVWconst [1])
+(LessEqual (FlagLT_ULT)) -> (MOVWconst [1])
+(LessEqual (FlagLT_UGT)) -> (MOVWconst [1])
+(LessEqual (FlagGT_ULT)) -> (MOVWconst [0])
+(LessEqual (FlagGT_UGT)) -> (MOVWconst [0])
+
+(LessEqualU (FlagEQ)) -> (MOVWconst [1])
+(LessEqualU (FlagLT_ULT)) -> (MOVWconst [1])
+(LessEqualU (FlagLT_UGT)) -> (MOVWconst [0])
+(LessEqualU (FlagGT_ULT)) -> (MOVWconst [1])
+(LessEqualU (FlagGT_UGT)) -> (MOVWconst [0])
+
+(GreaterThan (FlagEQ)) -> (MOVWconst [0])
+(GreaterThan (FlagLT_ULT)) -> (MOVWconst [0])
+(GreaterThan (FlagLT_UGT)) -> (MOVWconst [0])
+(GreaterThan (FlagGT_ULT)) -> (MOVWconst [1])
+(GreaterThan (FlagGT_UGT)) -> (MOVWconst [1])
+
+(GreaterThanU (FlagEQ)) -> (MOVWconst [0])
+(GreaterThanU (FlagLT_ULT)) -> (MOVWconst [0])
+(GreaterThanU (FlagLT_UGT)) -> (MOVWconst [1])
+(GreaterThanU (FlagGT_ULT)) -> (MOVWconst [0])
+(GreaterThanU (FlagGT_UGT)) -> (MOVWconst [1])
+
+(GreaterEqual (FlagEQ)) -> (MOVWconst [1])
+(GreaterEqual (FlagLT_ULT)) -> (MOVWconst [0])
+(GreaterEqual (FlagLT_UGT)) -> (MOVWconst [0])
+(GreaterEqual (FlagGT_ULT)) -> (MOVWconst [1])
+(GreaterEqual (FlagGT_UGT)) -> (MOVWconst [1])
+
+(GreaterEqualU (FlagEQ)) -> (MOVWconst [1])
+(GreaterEqualU (FlagLT_ULT)) -> (MOVWconst [0])
+(GreaterEqualU (FlagLT_UGT)) -> (MOVWconst [1])
+(GreaterEqualU (FlagGT_ULT)) -> (MOVWconst [0])
+(GreaterEqualU (FlagGT_UGT)) -> (MOVWconst [1])
+
+// absorb InvertFlags into boolean values
+(Equal (InvertFlags x)) -> (Equal x)
+(NotEqual (InvertFlags x)) -> (NotEqual x)
+(LessThan (InvertFlags x)) -> (GreaterThan x)
+(LessThanU (InvertFlags x)) -> (GreaterThanU x)
+(GreaterThan (InvertFlags x)) -> (LessThan x)
+(GreaterThanU (InvertFlags x)) -> (LessThanU x)
+(LessEqual (InvertFlags x)) -> (GreaterEqual x)
+(LessEqualU (InvertFlags x)) -> (GreaterEqualU x)
+(GreaterEqual (InvertFlags x)) -> (LessEqual x)
+(GreaterEqualU (InvertFlags x)) -> (LessEqualU x)
+
+// absorb flag constants into conditional instructions
+(CMOVWLSconst _ (FlagEQ) [c]) -> (MOVWconst [c])
+(CMOVWLSconst _ (FlagLT_ULT) [c]) -> (MOVWconst [c])
+(CMOVWLSconst x (FlagLT_UGT)) -> x
+(CMOVWLSconst _ (FlagGT_ULT) [c]) -> (MOVWconst [c])
+(CMOVWLSconst x (FlagGT_UGT)) -> x
+
+(CMOVWHSconst _ (FlagEQ) [c]) -> (MOVWconst [c])
+(CMOVWHSconst x (FlagLT_ULT)) -> x
+(CMOVWHSconst _ (FlagLT_UGT) [c]) -> (MOVWconst [c])
+(CMOVWHSconst x (FlagGT_ULT)) -> x
+(CMOVWHSconst _ (FlagGT_UGT) [c]) -> (MOVWconst [c])
+
+(CMOVWLSconst x (InvertFlags flags) [c]) -> (CMOVWHSconst x flags [c])
+(CMOVWHSconst x (InvertFlags flags) [c]) -> (CMOVWLSconst x flags [c])
+
+(SRAcond x _ (FlagEQ)) -> (SRAconst x [31])
+(SRAcond x y (FlagLT_ULT)) -> (SRA x y)
+(SRAcond x _ (FlagLT_UGT)) -> (SRAconst x [31])
+(SRAcond x y (FlagGT_ULT)) -> (SRA x y)
+(SRAcond x _ (FlagGT_UGT)) -> (SRAconst x [31])
+
+// remove redundant *const ops
+(ADDconst [0] x) -> x
+(SUBconst [0] x) -> x
+(ANDconst [0] _) -> (MOVWconst [0])
+(ANDconst [c] x) && int32(c)==-1 -> x
+(ORconst [0] x) -> x
+(ORconst [c] _) && int32(c)==-1 -> (MOVWconst [-1])
+(XORconst [0] x) -> x
+(BICconst [0] x) -> x
+(BICconst [c] _) && int32(c)==-1 -> (MOVWconst [0])
+
+// generic constant folding
+(ADDconst [c] (MOVWconst [d])) -> (MOVWconst [int64(int32(c+d))])
+(ADDconst [c] (ADDconst [d] x)) -> (ADDconst [int64(int32(c+d))] x)
+(ADDconst [c] (SUBconst [d] x)) -> (ADDconst [int64(int32(c-d))] x)
+(ADDconst [c] (RSBconst [d] x)) -> (RSBconst [int64(int32(c+d))] x)
+(ADCconst [c] (ADDconst [d] x) flags) -> (ADCconst [int64(int32(c+d))] x flags)
+(ADCconst [c] (SUBconst [d] x) flags) -> (ADCconst [int64(int32(c-d))] x flags)
+(SUBconst [c] (MOVWconst [d])) -> (MOVWconst [int64(int32(d-c))])
+(SUBconst [c] (SUBconst [d] x)) -> (ADDconst [int64(int32(-c-d))] x)
+(SUBconst [c] (ADDconst [d] x)) -> (ADDconst [int64(int32(-c+d))] x)
+(SUBconst [c] (RSBconst [d] x)) -> (RSBconst [int64(int32(-c+d))] x)
+(SBCconst [c] (ADDconst [d] x) flags) -> (SBCconst [int64(int32(c-d))] x flags)
+(SBCconst [c] (SUBconst [d] x) flags) -> (SBCconst [int64(int32(c+d))] x flags)
+(RSBconst [c] (MOVWconst [d])) -> (MOVWconst [int64(int32(c-d))])
+(RSBconst [c] (RSBconst [d] x)) -> (ADDconst [int64(int32(c-d))] x)
+(RSBconst [c] (ADDconst [d] x)) -> (RSBconst [int64(int32(c-d))] x)
+(RSBconst [c] (SUBconst [d] x)) -> (RSBconst [int64(int32(c+d))] x)
+(RSCconst [c] (ADDconst [d] x) flags) -> (RSCconst [int64(int32(c-d))] x flags)
+(RSCconst [c] (SUBconst [d] x) flags) -> (RSCconst [int64(int32(c+d))] x flags)
+(SLLconst [c] (MOVWconst [d])) -> (MOVWconst [int64(uint32(d)<<uint64(c))])
+(SRLconst [c] (MOVWconst [d])) -> (MOVWconst [int64(uint32(d)>>uint64(c))])
+(SRAconst [c] (MOVWconst [d])) -> (MOVWconst [int64(int32(d)>>uint64(c))])
+(MUL (MOVWconst [c]) (MOVWconst [d])) -> (MOVWconst [int64(int32(c*d))])
+(MULA (MOVWconst [c]) (MOVWconst [d]) a) -> (ADDconst [int64(int32(c*d))] a)
+(Select0 (UDIVrtcall (MOVWconst [c]) (MOVWconst [d]))) -> (MOVWconst [int64(uint32(c)/uint32(d))])
+(Select1 (UDIVrtcall (MOVWconst [c]) (MOVWconst [d]))) -> (MOVWconst [int64(uint32(c)%uint32(d))])
+(ANDconst [c] (MOVWconst [d])) -> (MOVWconst [c&d])
+(ANDconst [c] (ANDconst [d] x)) -> (ANDconst [c&d] x)
+(ORconst [c] (MOVWconst [d])) -> (MOVWconst [c|d])
+(ORconst [c] (ORconst [d] x)) -> (ORconst [c|d] x)
+(XORconst [c] (MOVWconst [d])) -> (MOVWconst [c^d])
+(XORconst [c] (XORconst [d] x)) -> (XORconst [c^d] x)
+(BICconst [c] (MOVWconst [d])) -> (MOVWconst [d&^c])
+(MVN (MOVWconst [c])) -> (MOVWconst [^c])
+(MOVBreg (MOVWconst [c])) -> (MOVWconst [int64(int8(c))])
+(MOVBUreg (MOVWconst [c])) -> (MOVWconst [int64(uint8(c))])
+(MOVHreg (MOVWconst [c])) -> (MOVWconst [int64(int16(c))])
+(MOVHUreg (MOVWconst [c])) -> (MOVWconst [int64(uint16(c))])
+(MOVWreg (MOVWconst [c])) -> (MOVWconst [c])
+
+// absorb shifts into ops
+(ADD x (SLLconst [c] y)) -> (ADDshiftLL x y [c])
+(ADD (SLLconst [c] y) x) -> (ADDshiftLL x y [c])
+(ADD x (SRLconst [c] y)) -> (ADDshiftRL x y [c])
+(ADD (SRLconst [c] y) x) -> (ADDshiftRL x y [c])
+(ADD x (SRAconst [c] y)) -> (ADDshiftRA x y [c])
+(ADD (SRAconst [c] y) x) -> (ADDshiftRA x y [c])
+(ADD x (SLL y z)) -> (ADDshiftLLreg x y z)
+(ADD (SLL y z) x) -> (ADDshiftLLreg x y z)
+(ADD x (SRL y z)) -> (ADDshiftRLreg x y z)
+(ADD (SRL y z) x) -> (ADDshiftRLreg x y z)
+(ADD x (SRA y z)) -> (ADDshiftRAreg x y z)
+(ADD (SRA y z) x) -> (ADDshiftRAreg x y z)
+(ADC x (SLLconst [c] y) flags) -> (ADCshiftLL x y [c] flags)
+(ADC (SLLconst [c] y) x flags) -> (ADCshiftLL x y [c] flags)
+(ADC x (SRLconst [c] y) flags) -> (ADCshiftRL x y [c] flags)
+(ADC (SRLconst [c] y) x flags) -> (ADCshiftRL x y [c] flags)
+(ADC x (SRAconst [c] y) flags) -> (ADCshiftRA x y [c] flags)
+(ADC (SRAconst [c] y) x flags) -> (ADCshiftRA x y [c] flags)
+(ADC x (SLL y z) flags) -> (ADCshiftLLreg x y z flags)
+(ADC (SLL y z) x flags) -> (ADCshiftLLreg x y z flags)
+(ADC x (SRL y z) flags) -> (ADCshiftRLreg x y z flags)
+(ADC (SRL y z) x flags) -> (ADCshiftRLreg x y z flags)
+(ADC x (SRA y z) flags) -> (ADCshiftRAreg x y z flags)
+(ADC (SRA y z) x flags) -> (ADCshiftRAreg x y z flags)
+(ADDS x (SLLconst [c] y)) -> (ADDSshiftLL x y [c])
+(ADDS (SLLconst [c] y) x) -> (ADDSshiftLL x y [c])
+(ADDS x (SRLconst [c] y)) -> (ADDSshiftRL x y [c])
+(ADDS (SRLconst [c] y) x) -> (ADDSshiftRL x y [c])
+(ADDS x (SRAconst [c] y)) -> (ADDSshiftRA x y [c])
+(ADDS (SRAconst [c] y) x) -> (ADDSshiftRA x y [c])
+(ADDS x (SLL y z)) -> (ADDSshiftLLreg x y z)
+(ADDS (SLL y z) x) -> (ADDSshiftLLreg x y z)
+(ADDS x (SRL y z)) -> (ADDSshiftRLreg x y z)
+(ADDS (SRL y z) x) -> (ADDSshiftRLreg x y z)
+(ADDS x (SRA y z)) -> (ADDSshiftRAreg x y z)
+(ADDS (SRA y z) x) -> (ADDSshiftRAreg x y z)
+(SUB x (SLLconst [c] y)) -> (SUBshiftLL x y [c])
+(SUB (SLLconst [c] y) x) -> (RSBshiftLL x y [c])
+(SUB x (SRLconst [c] y)) -> (SUBshiftRL x y [c])
+(SUB (SRLconst [c] y) x) -> (RSBshiftRL x y [c])
+(SUB x (SRAconst [c] y)) -> (SUBshiftRA x y [c])
+(SUB (SRAconst [c] y) x) -> (RSBshiftRA x y [c])
+(SUB x (SLL y z)) -> (SUBshiftLLreg x y z)
+(SUB (SLL y z) x) -> (RSBshiftLLreg x y z)
+(SUB x (SRL y z)) -> (SUBshiftRLreg x y z)
+(SUB (SRL y z) x) -> (RSBshiftRLreg x y z)
+(SUB x (SRA y z)) -> (SUBshiftRAreg x y z)
+(SUB (SRA y z) x) -> (RSBshiftRAreg x y z)
+(SBC x (SLLconst [c] y) flags) -> (SBCshiftLL x y [c] flags)
+(SBC (SLLconst [c] y) x flags) -> (RSCshiftLL x y [c] flags)
+(SBC x (SRLconst [c] y) flags) -> (SBCshiftRL x y [c] flags)
+(SBC (SRLconst [c] y) x flags) -> (RSCshiftRL x y [c] flags)
+(SBC x (SRAconst [c] y) flags) -> (SBCshiftRA x y [c] flags)
+(SBC (SRAconst [c] y) x flags) -> (RSCshiftRA x y [c] flags)
+(SBC x (SLL y z) flags) -> (SBCshiftLLreg x y z flags)
+(SBC (SLL y z) x flags) -> (RSCshiftLLreg x y z flags)
+(SBC x (SRL y z) flags) -> (SBCshiftRLreg x y z flags)
+(SBC (SRL y z) x flags) -> (RSCshiftRLreg x y z flags)
+(SBC x (SRA y z) flags) -> (SBCshiftRAreg x y z flags)
+(SBC (SRA y z) x flags) -> (RSCshiftRAreg x y z flags)
+(SUBS x (SLLconst [c] y)) -> (SUBSshiftLL x y [c])
+(SUBS (SLLconst [c] y) x) -> (RSBSshiftLL x y [c])
+(SUBS x (SRLconst [c] y)) -> (SUBSshiftRL x y [c])
+(SUBS (SRLconst [c] y) x) -> (RSBSshiftRL x y [c])
+(SUBS x (SRAconst [c] y)) -> (SUBSshiftRA x y [c])
+(SUBS (SRAconst [c] y) x) -> (RSBSshiftRA x y [c])
+(SUBS x (SLL y z)) -> (SUBSshiftLLreg x y z)
+(SUBS (SLL y z) x) -> (RSBSshiftLLreg x y z)
+(SUBS x (SRL y z)) -> (SUBSshiftRLreg x y z)
+(SUBS (SRL y z) x) -> (RSBSshiftRLreg x y z)
+(SUBS x (SRA y z)) -> (SUBSshiftRAreg x y z)
+(SUBS (SRA y z) x) -> (RSBSshiftRAreg x y z)
+(RSB x (SLLconst [c] y)) -> (RSBshiftLL x y [c])
+(RSB (SLLconst [c] y) x) -> (SUBshiftLL x y [c])
+(RSB x (SRLconst [c] y)) -> (RSBshiftRL x y [c])
+(RSB (SRLconst [c] y) x) -> (SUBshiftRL x y [c])
+(RSB x (SRAconst [c] y)) -> (RSBshiftRA x y [c])
+(RSB (SRAconst [c] y) x) -> (SUBshiftRA x y [c])
+(RSB x (SLL y z)) -> (RSBshiftLLreg x y z)
+(RSB (SLL y z) x) -> (SUBshiftLLreg x y z)
+(RSB x (SRL y z)) -> (RSBshiftRLreg x y z)
+(RSB (SRL y z) x) -> (SUBshiftRLreg x y z)
+(RSB x (SRA y z)) -> (RSBshiftRAreg x y z)
+(RSB (SRA y z) x) -> (SUBshiftRAreg x y z)
+(AND x (SLLconst [c] y)) -> (ANDshiftLL x y [c])
+(AND (SLLconst [c] y) x) -> (ANDshiftLL x y [c])
+(AND x (SRLconst [c] y)) -> (ANDshiftRL x y [c])
+(AND (SRLconst [c] y) x) -> (ANDshiftRL x y [c])
+(AND x (SRAconst [c] y)) -> (ANDshiftRA x y [c])
+(AND (SRAconst [c] y) x) -> (ANDshiftRA x y [c])
+(AND x (SLL y z)) -> (ANDshiftLLreg x y z)
+(AND (SLL y z) x) -> (ANDshiftLLreg x y z)
+(AND x (SRL y z)) -> (ANDshiftRLreg x y z)
+(AND (SRL y z) x) -> (ANDshiftRLreg x y z)
+(AND x (SRA y z)) -> (ANDshiftRAreg x y z)
+(AND (SRA y z) x) -> (ANDshiftRAreg x y z)
+(OR x (SLLconst [c] y)) -> (ORshiftLL x y [c])
+(OR (SLLconst [c] y) x) -> (ORshiftLL x y [c])
+(OR x (SRLconst [c] y)) -> (ORshiftRL x y [c])
+(OR (SRLconst [c] y) x) -> (ORshiftRL x y [c])
+(OR x (SRAconst [c] y)) -> (ORshiftRA x y [c])
+(OR (SRAconst [c] y) x) -> (ORshiftRA x y [c])
+(OR x (SLL y z)) -> (ORshiftLLreg x y z)
+(OR (SLL y z) x) -> (ORshiftLLreg x y z)
+(OR x (SRL y z)) -> (ORshiftRLreg x y z)
+(OR (SRL y z) x) -> (ORshiftRLreg x y z)
+(OR x (SRA y z)) -> (ORshiftRAreg x y z)
+(OR (SRA y z) x) -> (ORshiftRAreg x y z)
+(XOR x (SLLconst [c] y)) -> (XORshiftLL x y [c])
+(XOR (SLLconst [c] y) x) -> (XORshiftLL x y [c])
+(XOR x (SRLconst [c] y)) -> (XORshiftRL x y [c])
+(XOR (SRLconst [c] y) x) -> (XORshiftRL x y [c])
+(XOR x (SRAconst [c] y)) -> (XORshiftRA x y [c])
+(XOR (SRAconst [c] y) x) -> (XORshiftRA x y [c])
+(XOR x (SRRconst [c] y)) -> (XORshiftRR x y [c])
+(XOR (SRRconst [c] y) x) -> (XORshiftRR x y [c])
+(XOR x (SLL y z)) -> (XORshiftLLreg x y z)
+(XOR (SLL y z) x) -> (XORshiftLLreg x y z)
+(XOR x (SRL y z)) -> (XORshiftRLreg x y z)
+(XOR (SRL y z) x) -> (XORshiftRLreg x y z)
+(XOR x (SRA y z)) -> (XORshiftRAreg x y z)
+(XOR (SRA y z) x) -> (XORshiftRAreg x y z)
+(BIC x (SLLconst [c] y)) -> (BICshiftLL x y [c])
+(BIC x (SRLconst [c] y)) -> (BICshiftRL x y [c])
+(BIC x (SRAconst [c] y)) -> (BICshiftRA x y [c])
+(BIC x (SLL y z)) -> (BICshiftLLreg x y z)
+(BIC x (SRL y z)) -> (BICshiftRLreg x y z)
+(BIC x (SRA y z)) -> (BICshiftRAreg x y z)
+(MVN (SLLconst [c] x)) -> (MVNshiftLL x [c])
+(MVN (SRLconst [c] x)) -> (MVNshiftRL x [c])
+(MVN (SRAconst [c] x)) -> (MVNshiftRA x [c])
+(MVN (SLL x y)) -> (MVNshiftLLreg x y)
+(MVN (SRL x y)) -> (MVNshiftRLreg x y)
+(MVN (SRA x y)) -> (MVNshiftRAreg x y)
+
+(CMP x (SLLconst [c] y)) -> (CMPshiftLL x y [c])
+(CMP (SLLconst [c] y) x) -> (InvertFlags (CMPshiftLL x y [c]))
+(CMP x (SRLconst [c] y)) -> (CMPshiftRL x y [c])
+(CMP (SRLconst [c] y) x) -> (InvertFlags (CMPshiftRL x y [c]))
+(CMP x (SRAconst [c] y)) -> (CMPshiftRA x y [c])
+(CMP (SRAconst [c] y) x) -> (InvertFlags (CMPshiftRA x y [c]))
+(CMP x (SLL y z)) -> (CMPshiftLLreg x y z)
+(CMP (SLL y z) x) -> (InvertFlags (CMPshiftLLreg x y z))
+(CMP x (SRL y z)) -> (CMPshiftRLreg x y z)
+(CMP (SRL y z) x) -> (InvertFlags (CMPshiftRLreg x y z))
+(CMP x (SRA y z)) -> (CMPshiftRAreg x y z)
+(CMP (SRA y z) x) -> (InvertFlags (CMPshiftRAreg x y z))
+
+// prefer *const ops to *shift ops
+(ADDshiftLL (MOVWconst [c]) x [d]) -> (ADDconst [c] (SLLconst <x.Type> x [d]))
+(ADDshiftRL (MOVWconst [c]) x [d]) -> (ADDconst [c] (SRLconst <x.Type> x [d]))
+(ADDshiftRA (MOVWconst [c]) x [d]) -> (ADDconst [c] (SRAconst <x.Type> x [d]))
+(ADCshiftLL (MOVWconst [c]) x [d] flags) -> (ADCconst [c] (SLLconst <x.Type> x [d]) flags)
+(ADCshiftRL (MOVWconst [c]) x [d] flags) -> (ADCconst [c] (SRLconst <x.Type> x [d]) flags)
+(ADCshiftRA (MOVWconst [c]) x [d] flags) -> (ADCconst [c] (SRAconst <x.Type> x [d]) flags)
+(ADDSshiftLL (MOVWconst [c]) x [d]) -> (ADDSconst [c] (SLLconst <x.Type> x [d]))
+(ADDSshiftRL (MOVWconst [c]) x [d]) -> (ADDSconst [c] (SRLconst <x.Type> x [d]))
+(ADDSshiftRA (MOVWconst [c]) x [d]) -> (ADDSconst [c] (SRAconst <x.Type> x [d]))
+(SUBshiftLL (MOVWconst [c]) x [d]) -> (RSBconst [c] (SLLconst <x.Type> x [d]))
+(SUBshiftRL (MOVWconst [c]) x [d]) -> (RSBconst [c] (SRLconst <x.Type> x [d]))
+(SUBshiftRA (MOVWconst [c]) x [d]) -> (RSBconst [c] (SRAconst <x.Type> x [d]))
+(SBCshiftLL (MOVWconst [c]) x [d] flags) -> (RSCconst [c] (SLLconst <x.Type> x [d]) flags)
+(SBCshiftRL (MOVWconst [c]) x [d] flags) -> (RSCconst [c] (SRLconst <x.Type> x [d]) flags)
+(SBCshiftRA (MOVWconst [c]) x [d] flags) -> (RSCconst [c] (SRAconst <x.Type> x [d]) flags)
+(SUBSshiftLL (MOVWconst [c]) x [d]) -> (RSBSconst [c] (SLLconst <x.Type> x [d]))
+(SUBSshiftRL (MOVWconst [c]) x [d]) -> (RSBSconst [c] (SRLconst <x.Type> x [d]))
+(SUBSshiftRA (MOVWconst [c]) x [d]) -> (RSBSconst [c] (SRAconst <x.Type> x [d]))
+(RSBshiftLL (MOVWconst [c]) x [d]) -> (SUBconst [c] (SLLconst <x.Type> x [d]))
+(RSBshiftRL (MOVWconst [c]) x [d]) -> (SUBconst [c] (SRLconst <x.Type> x [d]))
+(RSBshiftRA (MOVWconst [c]) x [d]) -> (SUBconst [c] (SRAconst <x.Type> x [d]))
+(RSCshiftLL (MOVWconst [c]) x [d] flags) -> (SBCconst [c] (SLLconst <x.Type> x [d]) flags)
+(RSCshiftRL (MOVWconst [c]) x [d] flags) -> (SBCconst [c] (SRLconst <x.Type> x [d]) flags)
+(RSCshiftRA (MOVWconst [c]) x [d] flags) -> (SBCconst [c] (SRAconst <x.Type> x [d]) flags)
+(RSBSshiftLL (MOVWconst [c]) x [d]) -> (SUBSconst [c] (SLLconst <x.Type> x [d]))
+(RSBSshiftRL (MOVWconst [c]) x [d]) -> (SUBSconst [c] (SRLconst <x.Type> x [d]))
+(RSBSshiftRA (MOVWconst [c]) x [d]) -> (SUBSconst [c] (SRAconst <x.Type> x [d]))
+(ANDshiftLL (MOVWconst [c]) x [d]) -> (ANDconst [c] (SLLconst <x.Type> x [d]))
+(ANDshiftRL (MOVWconst [c]) x [d]) -> (ANDconst [c] (SRLconst <x.Type> x [d]))
+(ANDshiftRA (MOVWconst [c]) x [d]) -> (ANDconst [c] (SRAconst <x.Type> x [d]))
+(ORshiftLL (MOVWconst [c]) x [d]) -> (ORconst [c] (SLLconst <x.Type> x [d]))
+(ORshiftRL (MOVWconst [c]) x [d]) -> (ORconst [c] (SRLconst <x.Type> x [d]))
+(ORshiftRA (MOVWconst [c]) x [d]) -> (ORconst [c] (SRAconst <x.Type> x [d]))
+(XORshiftLL (MOVWconst [c]) x [d]) -> (XORconst [c] (SLLconst <x.Type> x [d]))
+(XORshiftRL (MOVWconst [c]) x [d]) -> (XORconst [c] (SRLconst <x.Type> x [d]))
+(XORshiftRA (MOVWconst [c]) x [d]) -> (XORconst [c] (SRAconst <x.Type> x [d]))
+(XORshiftRR (MOVWconst [c]) x [d]) -> (XORconst [c] (SRRconst <x.Type> x [d]))
+(CMPshiftLL (MOVWconst [c]) x [d]) -> (InvertFlags (CMPconst [c] (SLLconst <x.Type> x [d])))
+(CMPshiftRL (MOVWconst [c]) x [d]) -> (InvertFlags (CMPconst [c] (SRLconst <x.Type> x [d])))
+(CMPshiftRA (MOVWconst [c]) x [d]) -> (InvertFlags (CMPconst [c] (SRAconst <x.Type> x [d])))
+
+(ADDshiftLLreg (MOVWconst [c]) x y) -> (ADDconst [c] (SLL <x.Type> x y))
+(ADDshiftRLreg (MOVWconst [c]) x y) -> (ADDconst [c] (SRL <x.Type> x y))
+(ADDshiftRAreg (MOVWconst [c]) x y) -> (ADDconst [c] (SRA <x.Type> x y))
+(ADCshiftLLreg (MOVWconst [c]) x y flags) -> (ADCconst [c] (SLL <x.Type> x y) flags)
+(ADCshiftRLreg (MOVWconst [c]) x y flags) -> (ADCconst [c] (SRL <x.Type> x y) flags)
+(ADCshiftRAreg (MOVWconst [c]) x y flags) -> (ADCconst [c] (SRA <x.Type> x y) flags)
+(ADDSshiftLLreg (MOVWconst [c]) x y) -> (ADDSconst [c] (SLL <x.Type> x y))
+(ADDSshiftRLreg (MOVWconst [c]) x y) -> (ADDSconst [c] (SRL <x.Type> x y))
+(ADDSshiftRAreg (MOVWconst [c]) x y) -> (ADDSconst [c] (SRA <x.Type> x y))
+(SUBshiftLLreg (MOVWconst [c]) x y) -> (RSBconst [c] (SLL <x.Type> x y))
+(SUBshiftRLreg (MOVWconst [c]) x y) -> (RSBconst [c] (SRL <x.Type> x y))
+(SUBshiftRAreg (MOVWconst [c]) x y) -> (RSBconst [c] (SRA <x.Type> x y))
+(SBCshiftLLreg (MOVWconst [c]) x y flags) -> (RSCconst [c] (SLL <x.Type> x y) flags)
+(SBCshiftRLreg (MOVWconst [c]) x y flags) -> (RSCconst [c] (SRL <x.Type> x y) flags)
+(SBCshiftRAreg (MOVWconst [c]) x y flags) -> (RSCconst [c] (SRA <x.Type> x y) flags)
+(SUBSshiftLLreg (MOVWconst [c]) x y) -> (RSBSconst [c] (SLL <x.Type> x y))
+(SUBSshiftRLreg (MOVWconst [c]) x y) -> (RSBSconst [c] (SRL <x.Type> x y))
+(SUBSshiftRAreg (MOVWconst [c]) x y) -> (RSBSconst [c] (SRA <x.Type> x y))
+(RSBshiftLLreg (MOVWconst [c]) x y) -> (SUBconst [c] (SLL <x.Type> x y))
+(RSBshiftRLreg (MOVWconst [c]) x y) -> (SUBconst [c] (SRL <x.Type> x y))
+(RSBshiftRAreg (MOVWconst [c]) x y) -> (SUBconst [c] (SRA <x.Type> x y))
+(RSCshiftLLreg (MOVWconst [c]) x y flags) -> (SBCconst [c] (SLL <x.Type> x y) flags)
+(RSCshiftRLreg (MOVWconst [c]) x y flags) -> (SBCconst [c] (SRL <x.Type> x y) flags)
+(RSCshiftRAreg (MOVWconst [c]) x y flags) -> (SBCconst [c] (SRA <x.Type> x y) flags)
+(RSBSshiftLLreg (MOVWconst [c]) x y) -> (SUBSconst [c] (SLL <x.Type> x y))
+(RSBSshiftRLreg (MOVWconst [c]) x y) -> (SUBSconst [c] (SRL <x.Type> x y))
+(RSBSshiftRAreg (MOVWconst [c]) x y) -> (SUBSconst [c] (SRA <x.Type> x y))
+(ANDshiftLLreg (MOVWconst [c]) x y) -> (ANDconst [c] (SLL <x.Type> x y))
+(ANDshiftRLreg (MOVWconst [c]) x y) -> (ANDconst [c] (SRL <x.Type> x y))
+(ANDshiftRAreg (MOVWconst [c]) x y) -> (ANDconst [c] (SRA <x.Type> x y))
+(ORshiftLLreg (MOVWconst [c]) x y) -> (ORconst [c] (SLL <x.Type> x y))
+(ORshiftRLreg (MOVWconst [c]) x y) -> (ORconst [c] (SRL <x.Type> x y))
+(ORshiftRAreg (MOVWconst [c]) x y) -> (ORconst [c] (SRA <x.Type> x y))
+(XORshiftLLreg (MOVWconst [c]) x y) -> (XORconst [c] (SLL <x.Type> x y))
+(XORshiftRLreg (MOVWconst [c]) x y) -> (XORconst [c] (SRL <x.Type> x y))
+(XORshiftRAreg (MOVWconst [c]) x y) -> (XORconst [c] (SRA <x.Type> x y))
+(CMPshiftLLreg (MOVWconst [c]) x y) -> (InvertFlags (CMPconst [c] (SLL <x.Type> x y)))
+(CMPshiftRLreg (MOVWconst [c]) x y) -> (InvertFlags (CMPconst [c] (SRL <x.Type> x y)))
+(CMPshiftRAreg (MOVWconst [c]) x y) -> (InvertFlags (CMPconst [c] (SRA <x.Type> x y)))
+
+// constant folding in *shift ops
+(ADDshiftLL x (MOVWconst [c]) [d]) -> (ADDconst x [int64(uint32(c)<<uint64(d))])
+(ADDshiftRL x (MOVWconst [c]) [d]) -> (ADDconst x [int64(uint32(c)>>uint64(d))])
+(ADDshiftRA x (MOVWconst [c]) [d]) -> (ADDconst x [int64(int32(c)>>uint64(d))])
+(ADCshiftLL x (MOVWconst [c]) [d] flags) -> (ADCconst x [int64(uint32(c)<<uint64(d))] flags)
+(ADCshiftRL x (MOVWconst [c]) [d] flags) -> (ADCconst x [int64(uint32(c)>>uint64(d))] flags)
+(ADCshiftRA x (MOVWconst [c]) [d] flags) -> (ADCconst x [int64(int32(c)>>uint64(d))] flags)
+(ADDSshiftLL x (MOVWconst [c]) [d]) -> (ADDSconst x [int64(uint32(c)<<uint64(d))])
+(ADDSshiftRL x (MOVWconst [c]) [d]) -> (ADDSconst x [int64(uint32(c)>>uint64(d))])
+(ADDSshiftRA x (MOVWconst [c]) [d]) -> (ADDSconst x [int64(int32(c)>>uint64(d))])
+(SUBshiftLL x (MOVWconst [c]) [d]) -> (SUBconst x [int64(uint32(c)<<uint64(d))])
+(SUBshiftRL x (MOVWconst [c]) [d]) -> (SUBconst x [int64(uint32(c)>>uint64(d))])
+(SUBshiftRA x (MOVWconst [c]) [d]) -> (SUBconst x [int64(int32(c)>>uint64(d))])
+(SBCshiftLL x (MOVWconst [c]) [d] flags) -> (SBCconst x [int64(uint32(c)<<uint64(d))] flags)
+(SBCshiftRL x (MOVWconst [c]) [d] flags) -> (SBCconst x [int64(uint32(c)>>uint64(d))] flags)
+(SBCshiftRA x (MOVWconst [c]) [d] flags) -> (SBCconst x [int64(int32(c)>>uint64(d))] flags)
+(SUBSshiftLL x (MOVWconst [c]) [d]) -> (SUBSconst x [int64(uint32(c)<<uint64(d))])
+(SUBSshiftRL x (MOVWconst [c]) [d]) -> (SUBSconst x [int64(uint32(c)>>uint64(d))])
+(SUBSshiftRA x (MOVWconst [c]) [d]) -> (SUBSconst x [int64(int32(c)>>uint64(d))])
+(RSBshiftLL x (MOVWconst [c]) [d]) -> (RSBconst x [int64(uint32(c)<<uint64(d))])
+(RSBshiftRL x (MOVWconst [c]) [d]) -> (RSBconst x [int64(uint32(c)>>uint64(d))])
+(RSBshiftRA x (MOVWconst [c]) [d]) -> (RSBconst x [int64(int32(c)>>uint64(d))])
+(RSCshiftLL x (MOVWconst [c]) [d] flags) -> (RSCconst x [int64(uint32(c)<<uint64(d))] flags)
+(RSCshiftRL x (MOVWconst [c]) [d] flags) -> (RSCconst x [int64(uint32(c)>>uint64(d))] flags)
+(RSCshiftRA x (MOVWconst [c]) [d] flags) -> (RSCconst x [int64(int32(c)>>uint64(d))] flags)
+(RSBSshiftLL x (MOVWconst [c]) [d]) -> (RSBSconst x [int64(uint32(c)<<uint64(d))])
+(RSBSshiftRL x (MOVWconst [c]) [d]) -> (RSBSconst x [int64(uint32(c)>>uint64(d))])
+(RSBSshiftRA x (MOVWconst [c]) [d]) -> (RSBSconst x [int64(int32(c)>>uint64(d))])
+(ANDshiftLL x (MOVWconst [c]) [d]) -> (ANDconst x [int64(uint32(c)<<uint64(d))])
+(ANDshiftRL x (MOVWconst [c]) [d]) -> (ANDconst x [int64(uint32(c)>>uint64(d))])
+(ANDshiftRA x (MOVWconst [c]) [d]) -> (ANDconst x [int64(int32(c)>>uint64(d))])
+(ORshiftLL x (MOVWconst [c]) [d]) -> (ORconst x [int64(uint32(c)<<uint64(d))])
+(ORshiftRL x (MOVWconst [c]) [d]) -> (ORconst x [int64(uint32(c)>>uint64(d))])
+(ORshiftRA x (MOVWconst [c]) [d]) -> (ORconst x [int64(int32(c)>>uint64(d))])
+(XORshiftLL x (MOVWconst [c]) [d]) -> (XORconst x [int64(uint32(c)<<uint64(d))])
+(XORshiftRL x (MOVWconst [c]) [d]) -> (XORconst x [int64(uint32(c)>>uint64(d))])
+(XORshiftRA x (MOVWconst [c]) [d]) -> (XORconst x [int64(int32(c)>>uint64(d))])
+(XORshiftRR x (MOVWconst [c]) [d]) -> (XORconst x [int64(uint32(c)>>uint64(d)|uint32(c)<<uint64(32-d))])
+(BICshiftLL x (MOVWconst [c]) [d]) -> (BICconst x [int64(uint32(c)<<uint64(d))])
+(BICshiftRL x (MOVWconst [c]) [d]) -> (BICconst x [int64(uint32(c)>>uint64(d))])
+(BICshiftRA x (MOVWconst [c]) [d]) -> (BICconst x [int64(int32(c)>>uint64(d))])
+(MVNshiftLL (MOVWconst [c]) [d]) -> (MOVWconst [^int64(uint32(c)<<uint64(d))])
+(MVNshiftRL (MOVWconst [c]) [d]) -> (MOVWconst [^int64(uint32(c)>>uint64(d))])
+(MVNshiftRA (MOVWconst [c]) [d]) -> (MOVWconst [^int64(int32(c)>>uint64(d))])
+(CMPshiftLL x (MOVWconst [c]) [d]) -> (CMPconst x [int64(uint32(c)<<uint64(d))])
+(CMPshiftRL x (MOVWconst [c]) [d]) -> (CMPconst x [int64(uint32(c)>>uint64(d))])
+(CMPshiftRA x (MOVWconst [c]) [d]) -> (CMPconst x [int64(int32(c)>>uint64(d))])
+
+(ADDshiftLLreg x y (MOVWconst [c])) -> (ADDshiftLL x y [c])
+(ADDshiftRLreg x y (MOVWconst [c])) -> (ADDshiftRL x y [c])
+(ADDshiftRAreg x y (MOVWconst [c])) -> (ADDshiftRA x y [c])
+(ADCshiftLLreg x y (MOVWconst [c]) flags) -> (ADCshiftLL x y [c] flags)
+(ADCshiftRLreg x y (MOVWconst [c]) flags) -> (ADCshiftRL x y [c] flags)
+(ADCshiftRAreg x y (MOVWconst [c]) flags) -> (ADCshiftRA x y [c] flags)
+(ADDSshiftLLreg x y (MOVWconst [c])) -> (ADDSshiftLL x y [c])
+(ADDSshiftRLreg x y (MOVWconst [c])) -> (ADDSshiftRL x y [c])
+(ADDSshiftRAreg x y (MOVWconst [c])) -> (ADDSshiftRA x y [c])
+(SUBshiftLLreg x y (MOVWconst [c])) -> (SUBshiftLL x y [c])
+(SUBshiftRLreg x y (MOVWconst [c])) -> (SUBshiftRL x y [c])
+(SUBshiftRAreg x y (MOVWconst [c])) -> (SUBshiftRA x y [c])
+(SBCshiftLLreg x y (MOVWconst [c]) flags) -> (SBCshiftLL x y [c] flags)
+(SBCshiftRLreg x y (MOVWconst [c]) flags) -> (SBCshiftRL x y [c] flags)
+(SBCshiftRAreg x y (MOVWconst [c]) flags) -> (SBCshiftRA x y [c] flags)
+(SUBSshiftLLreg x y (MOVWconst [c])) -> (SUBSshiftLL x y [c])
+(SUBSshiftRLreg x y (MOVWconst [c])) -> (SUBSshiftRL x y [c])
+(SUBSshiftRAreg x y (MOVWconst [c])) -> (SUBSshiftRA x y [c])
+(RSBshiftLLreg x y (MOVWconst [c])) -> (RSBshiftLL x y [c])
+(RSBshiftRLreg x y (MOVWconst [c])) -> (RSBshiftRL x y [c])
+(RSBshiftRAreg x y (MOVWconst [c])) -> (RSBshiftRA x y [c])
+(RSCshiftLLreg x y (MOVWconst [c]) flags) -> (RSCshiftLL x y [c] flags)
+(RSCshiftRLreg x y (MOVWconst [c]) flags) -> (RSCshiftRL x y [c] flags)
+(RSCshiftRAreg x y (MOVWconst [c]) flags) -> (RSCshiftRA x y [c] flags)
+(RSBSshiftLLreg x y (MOVWconst [c])) -> (RSBSshiftLL x y [c])
+(RSBSshiftRLreg x y (MOVWconst [c])) -> (RSBSshiftRL x y [c])
+(RSBSshiftRAreg x y (MOVWconst [c])) -> (RSBSshiftRA x y [c])
+(ANDshiftLLreg x y (MOVWconst [c])) -> (ANDshiftLL x y [c])
+(ANDshiftRLreg x y (MOVWconst [c])) -> (ANDshiftRL x y [c])
+(ANDshiftRAreg x y (MOVWconst [c])) -> (ANDshiftRA x y [c])
+(ORshiftLLreg x y (MOVWconst [c])) -> (ORshiftLL x y [c])
+(ORshiftRLreg x y (MOVWconst [c])) -> (ORshiftRL x y [c])
+(ORshiftRAreg x y (MOVWconst [c])) -> (ORshiftRA x y [c])
+(XORshiftLLreg x y (MOVWconst [c])) -> (XORshiftLL x y [c])
+(XORshiftRLreg x y (MOVWconst [c])) -> (XORshiftRL x y [c])
+(XORshiftRAreg x y (MOVWconst [c])) -> (XORshiftRA x y [c])
+(BICshiftLLreg x y (MOVWconst [c])) -> (BICshiftLL x y [c])
+(BICshiftRLreg x y (MOVWconst [c])) -> (BICshiftRL x y [c])
+(BICshiftRAreg x y (MOVWconst [c])) -> (BICshiftRA x y [c])
+(MVNshiftLLreg x (MOVWconst [c])) -> (MVNshiftLL x [c])
+(MVNshiftRLreg x (MOVWconst [c])) -> (MVNshiftRL x [c])
+(MVNshiftRAreg x (MOVWconst [c])) -> (MVNshiftRA x [c])
+(CMPshiftLLreg x y (MOVWconst [c])) -> (CMPshiftLL x y [c])
+(CMPshiftRLreg x y (MOVWconst [c])) -> (CMPshiftRL x y [c])
+(CMPshiftRAreg x y (MOVWconst [c])) -> (CMPshiftRA x y [c])
+
+// use indexed loads and stores
+(MOVWload [0] {sym} (ADD ptr idx) mem) && sym == nil && !config.nacl -> (MOVWloadidx ptr idx mem)
+(MOVWstore [0] {sym} (ADD ptr idx) val mem) && sym == nil && !config.nacl -> (MOVWstoreidx ptr idx val mem)
+(MOVWload [0] {sym} (ADDshiftLL ptr idx [c]) mem) && sym == nil && !config.nacl -> (MOVWloadshiftLL ptr idx [c] mem)
+(MOVWload [0] {sym} (ADDshiftRL ptr idx [c]) mem) && sym == nil && !config.nacl -> (MOVWloadshiftRL ptr idx [c] mem)
+(MOVWload [0] {sym} (ADDshiftRA ptr idx [c]) mem) && sym == nil && !config.nacl -> (MOVWloadshiftRA ptr idx [c] mem)
+(MOVWstore [0] {sym} (ADDshiftLL ptr idx [c]) val mem) && sym == nil && !config.nacl -> (MOVWstoreshiftLL ptr idx [c] val mem)
+(MOVWstore [0] {sym} (ADDshiftRL ptr idx [c]) val mem) && sym == nil && !config.nacl -> (MOVWstoreshiftRL ptr idx [c] val mem)
+(MOVWstore [0] {sym} (ADDshiftRA ptr idx [c]) val mem) && sym == nil && !config.nacl -> (MOVWstoreshiftRA ptr idx [c] val mem)
+
+// constant folding in indexed loads and stores
+(MOVWloadidx ptr (MOVWconst [c]) mem) -> (MOVWload [c] ptr mem)
+(MOVWloadidx (MOVWconst [c]) ptr mem) -> (MOVWload [c] ptr mem)
+
+(MOVWstoreidx ptr (MOVWconst [c]) val mem) -> (MOVWstore [c] ptr val mem)
+(MOVWstoreidx (MOVWconst [c]) ptr val mem) -> (MOVWstore [c] ptr val mem)
+
+(MOVWloadidx ptr (SLLconst idx [c]) mem) -> (MOVWloadshiftLL ptr idx [c] mem)
+(MOVWloadidx (SLLconst idx [c]) ptr mem) -> (MOVWloadshiftLL ptr idx [c] mem)
+(MOVWloadidx ptr (SRLconst idx [c]) mem) -> (MOVWloadshiftRL ptr idx [c] mem)
+(MOVWloadidx (SRLconst idx [c]) ptr mem) -> (MOVWloadshiftRL ptr idx [c] mem)
+(MOVWloadidx ptr (SRAconst idx [c]) mem) -> (MOVWloadshiftRA ptr idx [c] mem)
+(MOVWloadidx (SRAconst idx [c]) ptr mem) -> (MOVWloadshiftRA ptr idx [c] mem)
+
+(MOVWstoreidx ptr (SLLconst idx [c]) val mem) -> (MOVWstoreshiftLL ptr idx [c] val mem)
+(MOVWstoreidx (SLLconst idx [c]) ptr val mem) -> (MOVWstoreshiftLL ptr idx [c] val mem)
+(MOVWstoreidx ptr (SRLconst idx [c]) val mem) -> (MOVWstoreshiftRL ptr idx [c] val mem)
+(MOVWstoreidx (SRLconst idx [c]) ptr val mem) -> (MOVWstoreshiftRL ptr idx [c] val mem)
+(MOVWstoreidx ptr (SRAconst idx [c]) val mem) -> (MOVWstoreshiftRA ptr idx [c] val mem)
+(MOVWstoreidx (SRAconst idx [c]) ptr val mem) -> (MOVWstoreshiftRA ptr idx [c] val mem)
+
+(MOVWloadshiftLL ptr (MOVWconst [c]) [d] mem) -> (MOVWload [int64(uint32(c)<<uint64(d))] ptr mem)
+(MOVWloadshiftRL ptr (MOVWconst [c]) [d] mem) -> (MOVWload [int64(uint32(c)>>uint64(d))] ptr mem)
+(MOVWloadshiftRA ptr (MOVWconst [c]) [d] mem) -> (MOVWload [int64(int32(c)>>uint64(d))] ptr mem)
+
+(MOVWstoreshiftLL ptr (MOVWconst [c]) [d] val mem) -> (MOVWstore [int64(uint32(c)<<uint64(d))] ptr val mem)
+(MOVWstoreshiftRL ptr (MOVWconst [c]) [d] val mem) -> (MOVWstore [int64(uint32(c)>>uint64(d))] ptr val mem)
+(MOVWstoreshiftRA ptr (MOVWconst [c]) [d] val mem) -> (MOVWstore [int64(int32(c)>>uint64(d))] ptr val mem)
+
+// generic simplifications
+(ADD x (RSBconst [0] y)) -> (SUB x y)
+(ADD (RSBconst [0] y) x) -> (SUB x y)
+(SUB x x) -> (MOVWconst [0])
+(RSB x x) -> (MOVWconst [0])
+(AND x x) -> x
+(OR x x) -> x
+(XOR x x) -> (MOVWconst [0])
+(BIC x x) -> (MOVWconst [0])
+
+(ADD (MUL x y) a) -> (MULA x y a)
+(ADD a (MUL x y)) -> (MULA x y a)
+
+(AND x (MVN y)) -> (BIC x y)
+(AND (MVN y) x) -> (BIC x y)
+
+// simplification with *shift ops
+(SUBshiftLL x (SLLconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(SUBshiftRL x (SRLconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(SUBshiftRA x (SRAconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(RSBshiftLL x (SLLconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(RSBshiftRL x (SRLconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(RSBshiftRA x (SRAconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d -> y
+(ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d -> y
+(ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d -> y
+(ORshiftLL x y:(SLLconst x [c]) [d]) && c==d -> y
+(ORshiftRL x y:(SRLconst x [c]) [d]) && c==d -> y
+(ORshiftRA x y:(SRAconst x [c]) [d]) && c==d -> y
+(XORshiftLL x (SLLconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(XORshiftRL x (SRLconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(XORshiftRA x (SRAconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(BICshiftLL x (SLLconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(BICshiftRL x (SRLconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(BICshiftRA x (SRAconst x [c]) [d]) && c==d -> (MOVWconst [0])
+(AND x (MVNshiftLL y [c])) -> (BICshiftLL x y [c])
+(AND (MVNshiftLL y [c]) x) -> (BICshiftLL x y [c])
+(AND x (MVNshiftRL y [c])) -> (BICshiftRL x y [c])
+(AND (MVNshiftRL y [c]) x) -> (BICshiftRL x y [c])
+(AND x (MVNshiftRA y [c])) -> (BICshiftRA x y [c])
+(AND (MVNshiftRA y [c]) x) -> (BICshiftRA x y [c])
+
+// floating point optimizations
+(CMPF x (MOVFconst [0])) -> (CMPF0 x)
+(CMPD x (MOVDconst [0])) -> (CMPD0 x)
diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules
new file mode 100644
index 0000000..c36b6f7
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules
@@ -0,0 +1,1302 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+(AddPtr x y) -> (ADD x y)
+(Add64 x y) -> (ADD x y)
+(Add32 x y) -> (ADD x y)
+(Add16 x y) -> (ADD x y)
+(Add8 x y) -> (ADD x y)
+(Add32F x y) -> (FADDS x y)
+(Add64F x y) -> (FADDD x y)
+
+(SubPtr x y) -> (SUB x y)
+(Sub64 x y) -> (SUB x y)
+(Sub32 x y) -> (SUB x y)
+(Sub16 x y) -> (SUB x y)
+(Sub8 x y) -> (SUB x y)
+(Sub32F x y) -> (FSUBS x y)
+(Sub64F x y) -> (FSUBD x y)
+
+(Mul64 x y) -> (MUL x y)
+(Mul32 x y) -> (MULW x y)
+(Mul16 x y) -> (MULW x y)
+(Mul8 x y) -> (MULW x y)
+(Mul32F x y) -> (FMULS x y)
+(Mul64F x y) -> (FMULD x y)
+
+(Hmul64 x y) -> (MULH x y)
+(Hmul64u x y) -> (UMULH x y)
+(Hmul32 x y) -> (SRAconst (MULL <config.fe.TypeInt64()> x y) [32])
+(Hmul32u x y) -> (SRAconst (UMULL <config.fe.TypeUInt64()> x y) [32])
+(Hmul16 x y) -> (SRAconst (MULW <config.fe.TypeInt32()> (SignExt16to32 x) (SignExt16to32 y)) [16])
+(Hmul16u x y) -> (SRLconst (MUL <config.fe.TypeUInt32()> (ZeroExt16to32 x) (ZeroExt16to32 y)) [16])
+(Hmul8 x y) -> (SRAconst (MULW <config.fe.TypeInt16()> (SignExt8to32 x) (SignExt8to32 y)) [8])
+(Hmul8u x y) -> (SRLconst (MUL <config.fe.TypeUInt16()> (ZeroExt8to32 x) (ZeroExt8to32 y)) [8])
+
+(Div64 x y) -> (DIV x y)
+(Div64u x y) -> (UDIV x y)
+(Div32 x y) -> (DIVW x y)
+(Div32u x y) -> (UDIVW x y)
+(Div16 x y) -> (DIVW (SignExt16to32 x) (SignExt16to32 y))
+(Div16u x y) -> (UDIVW (ZeroExt16to32 x) (ZeroExt16to32 y))
+(Div8 x y) -> (DIVW (SignExt8to32 x) (SignExt8to32 y))
+(Div8u x y) -> (UDIVW (ZeroExt8to32 x) (ZeroExt8to32 y))
+(Div32F x y) -> (FDIVS x y)
+(Div64F x y) -> (FDIVD x y)
+
+(Mod64 x y) -> (MOD x y)
+(Mod64u x y) -> (UMOD x y)
+(Mod32 x y) -> (MODW x y)
+(Mod32u x y) -> (UMODW x y)
+(Mod16 x y) -> (MODW (SignExt16to32 x) (SignExt16to32 y))
+(Mod16u x y) -> (UMODW (ZeroExt16to32 x) (ZeroExt16to32 y))
+(Mod8 x y) -> (MODW (SignExt8to32 x) (SignExt8to32 y))
+(Mod8u x y) -> (UMODW (ZeroExt8to32 x) (ZeroExt8to32 y))
+
+(Avg64u <t> x y) -> (ADD (ADD <t> (SRLconst <t> x [1]) (SRLconst <t> y [1])) (AND <t> (AND <t> x y) (MOVDconst [1])))
+
+(And64 x y) -> (AND x y)
+(And32 x y) -> (AND x y)
+(And16 x y) -> (AND x y)
+(And8 x y) -> (AND x y)
+
+(Or64 x y) -> (OR x y)
+(Or32 x y) -> (OR x y)
+(Or16 x y) -> (OR x y)
+(Or8 x y) -> (OR x y)
+
+(Xor64 x y) -> (XOR x y)
+(Xor32 x y) -> (XOR x y)
+(Xor16 x y) -> (XOR x y)
+(Xor8 x y) -> (XOR x y)
+
+// unary ops
+(Neg64 x) -> (NEG x)
+(Neg32 x) -> (NEG x)
+(Neg16 x) -> (NEG x)
+(Neg8 x) -> (NEG x)
+(Neg32F x) -> (FNEGS x)
+(Neg64F x) -> (FNEGD x)
+
+(Com64 x) -> (MVN x)
+(Com32 x) -> (MVN x)
+(Com16 x) -> (MVN x)
+(Com8 x) -> (MVN x)
+
+(Sqrt x) -> (FSQRTD x)
+
+(Ctz64 <t> x) -> (CLZ (RBIT <t> x))
+(Ctz32 <t> x) -> (CLZW (RBITW <t> x))
+
+(Bswap64 x) -> (REV x)
+(Bswap32 x) -> (REVW x)
+
+// boolean ops -- booleans are represented with 0=false, 1=true
+(AndB x y) -> (AND x y)
+(OrB x y) -> (OR x y)
+(EqB x y) -> (XOR (MOVDconst [1]) (XOR <config.fe.TypeBool()> x y))
+(NeqB x y) -> (XOR x y)
+(Not x) -> (XOR (MOVDconst [1]) x)
+
+// constant shifts
+(Lsh64x64  x (MOVDconst [c])) && uint64(c) < 64 -> (SLLconst x [c])
+(Rsh64x64  x (MOVDconst [c])) && uint64(c) < 64 -> (SRAconst x [c])
+(Rsh64Ux64 x (MOVDconst [c])) && uint64(c) < 64 -> (SRLconst x [c])
+(Lsh32x64  x (MOVDconst [c])) && uint64(c) < 32 -> (SLLconst x [c])
+(Rsh32x64  x (MOVDconst [c])) && uint64(c) < 32 -> (SRAconst (SignExt32to64 x) [c])
+(Rsh32Ux64 x (MOVDconst [c])) && uint64(c) < 32 -> (SRLconst (ZeroExt32to64 x) [c])
+(Lsh16x64  x (MOVDconst [c])) && uint64(c) < 16 -> (SLLconst x [c])
+(Rsh16x64  x (MOVDconst [c])) && uint64(c) < 16 -> (SRAconst (SignExt16to64 x) [c])
+(Rsh16Ux64 x (MOVDconst [c])) && uint64(c) < 16 -> (SRLconst (ZeroExt16to64 x) [c])
+(Lsh8x64   x (MOVDconst [c])) && uint64(c) < 8  -> (SLLconst x [c])
+(Rsh8x64   x (MOVDconst [c])) && uint64(c) < 8  -> (SRAconst (SignExt8to64  x) [c])
+(Rsh8Ux64  x (MOVDconst [c])) && uint64(c) < 8  -> (SRLconst (ZeroExt8to64  x) [c])
+
+// large constant shifts
+(Lsh64x64  _ (MOVDconst [c])) && uint64(c) >= 64 -> (MOVDconst [0])
+(Rsh64Ux64 _ (MOVDconst [c])) && uint64(c) >= 64 -> (MOVDconst [0])
+(Lsh32x64  _ (MOVDconst [c])) && uint64(c) >= 32 -> (MOVDconst [0])
+(Rsh32Ux64 _ (MOVDconst [c])) && uint64(c) >= 32 -> (MOVDconst [0])
+(Lsh16x64  _ (MOVDconst [c])) && uint64(c) >= 16 -> (MOVDconst [0])
+(Rsh16Ux64 _ (MOVDconst [c])) && uint64(c) >= 16 -> (MOVDconst [0])
+(Lsh8x64   _ (MOVDconst [c])) && uint64(c) >= 8  -> (MOVDconst [0])
+(Rsh8Ux64  _ (MOVDconst [c])) && uint64(c) >= 8  -> (MOVDconst [0])
+
+// large constant signed right shift, we leave the sign bit
+(Rsh64x64 x (MOVDconst [c])) && uint64(c) >= 64 -> (SRAconst x [63])
+(Rsh32x64 x (MOVDconst [c])) && uint64(c) >= 32 -> (SRAconst (SignExt32to64 x) [63])
+(Rsh16x64 x (MOVDconst [c])) && uint64(c) >= 16 -> (SRAconst (SignExt16to64 x) [63])
+(Rsh8x64  x (MOVDconst [c])) && uint64(c) >= 8  -> (SRAconst (SignExt8to64  x) [63])
+
+// shifts
+// hardware instruction uses only the low 6 bits of the shift
+// we compare to 64 to ensure Go semantics for large shifts
+(Lsh64x64 <t> x y) -> (CSELULT (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
+(Lsh64x32 <t> x y) -> (CSELULT (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+(Lsh64x16 <t> x y) -> (CSELULT (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+(Lsh64x8  <t> x y) -> (CSELULT (SLL <t> x (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+
+(Lsh32x64 <t> x y) -> (CSELULT (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
+(Lsh32x32 <t> x y) -> (CSELULT (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+(Lsh32x16 <t> x y) -> (CSELULT (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+(Lsh32x8  <t> x y) -> (CSELULT (SLL <t> x (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+
+(Lsh16x64 <t> x y) -> (CSELULT (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
+(Lsh16x32 <t> x y) -> (CSELULT (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+(Lsh16x16 <t> x y) -> (CSELULT (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+(Lsh16x8  <t> x y) -> (CSELULT (SLL <t> x (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+
+(Lsh8x64 <t> x y) -> (CSELULT (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
+(Lsh8x32 <t> x y) -> (CSELULT (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+(Lsh8x16 <t> x y) -> (CSELULT (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+(Lsh8x8  <t> x y) -> (CSELULT (SLL <t> x (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+
+(Rsh64Ux64 <t> x y) -> (CSELULT (SRL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
+(Rsh64Ux32 <t> x y) -> (CSELULT (SRL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+(Rsh64Ux16 <t> x y) -> (CSELULT (SRL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+(Rsh64Ux8  <t> x y) -> (CSELULT (SRL <t> x (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+
+(Rsh32Ux64 <t> x y) -> (CSELULT (SRL <t> (ZeroExt32to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
+(Rsh32Ux32 <t> x y) -> (CSELULT (SRL <t> (ZeroExt32to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+(Rsh32Ux16 <t> x y) -> (CSELULT (SRL <t> (ZeroExt32to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+(Rsh32Ux8  <t> x y) -> (CSELULT (SRL <t> (ZeroExt32to64 x) (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+
+(Rsh16Ux64 <t> x y) -> (CSELULT (SRL <t> (ZeroExt16to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
+(Rsh16Ux32 <t> x y) -> (CSELULT (SRL <t> (ZeroExt16to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+(Rsh16Ux16 <t> x y) -> (CSELULT (SRL <t> (ZeroExt16to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+(Rsh16Ux8  <t> x y) -> (CSELULT (SRL <t> (ZeroExt16to64 x) (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+
+(Rsh8Ux64 <t> x y) -> (CSELULT (SRL <t> (ZeroExt8to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
+(Rsh8Ux32 <t> x y) -> (CSELULT (SRL <t> (ZeroExt8to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+(Rsh8Ux16 <t> x y) -> (CSELULT (SRL <t> (ZeroExt8to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+(Rsh8Ux8  <t> x y) -> (CSELULT (SRL <t> (ZeroExt8to64 x) (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+
+(Rsh64x64 x y) -> (SRA x (CSELULT <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
+(Rsh64x32 x y) -> (SRA x (CSELULT <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
+(Rsh64x16 x y) -> (SRA x (CSELULT <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
+(Rsh64x8  x y) -> (SRA x (CSELULT <y.Type> (ZeroExt8to64  y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64  y))))
+
+(Rsh32x64 x y) -> (SRA (SignExt32to64 x) (CSELULT <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
+(Rsh32x32 x y) -> (SRA (SignExt32to64 x) (CSELULT <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
+(Rsh32x16 x y) -> (SRA (SignExt32to64 x) (CSELULT <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
+(Rsh32x8  x y) -> (SRA (SignExt32to64 x) (CSELULT <y.Type> (ZeroExt8to64  y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64  y))))
+
+(Rsh16x64 x y) -> (SRA (SignExt16to64 x) (CSELULT <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
+(Rsh16x32 x y) -> (SRA (SignExt16to64 x) (CSELULT <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
+(Rsh16x16 x y) -> (SRA (SignExt16to64 x) (CSELULT <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
+(Rsh16x8  x y) -> (SRA (SignExt16to64 x) (CSELULT <y.Type> (ZeroExt8to64  y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64  y))))
+
+(Rsh8x64 x y) -> (SRA (SignExt8to64 x) (CSELULT <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
+(Rsh8x32 x y) -> (SRA (SignExt8to64 x) (CSELULT <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
+(Rsh8x16 x y) -> (SRA (SignExt8to64 x) (CSELULT <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
+(Rsh8x8  x y) -> (SRA (SignExt8to64 x) (CSELULT <y.Type> (ZeroExt8to64  y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64  y))))
+
+(Lrot64 x [c]) -> (RORconst  x [64-c&63])
+(Lrot32 x [c]) -> (RORWconst x [32-c&31])
+(Lrot16 <t> x [c]) -> (OR (SLLconst <t> x [c&15]) (SRLconst <t> (ZeroExt16to64 x) [16-c&15]))
+(Lrot8  <t> x [c]) -> (OR (SLLconst <t> x [c&7])  (SRLconst <t> (ZeroExt8to64  x) [8-c&7]))
+
+// constants
+(Const64 [val]) -> (MOVDconst [val])
+(Const32 [val]) -> (MOVDconst [val])
+(Const16 [val]) -> (MOVDconst [val])
+(Const8 [val]) -> (MOVDconst [val])
+(Const32F [val]) -> (FMOVSconst [val])
+(Const64F [val]) -> (FMOVDconst [val])
+(ConstNil) -> (MOVDconst [0])
+(ConstBool [b]) -> (MOVDconst [b])
+
+(Slicemask <t> x) -> (MVN (SRAconst <t> (SUBconst <t> x [1]) [63]))
+
+// truncations
+// Because we ignore high parts of registers, truncates are just copies.
+(Trunc16to8 x) -> x
+(Trunc32to8 x) -> x
+(Trunc32to16 x) -> x
+(Trunc64to8 x) -> x
+(Trunc64to16 x) -> x
+(Trunc64to32 x) -> x
+
+// Zero-/Sign-extensions
+(ZeroExt8to16 x) -> (MOVBUreg x)
+(ZeroExt8to32 x) -> (MOVBUreg x)
+(ZeroExt16to32 x) -> (MOVHUreg x)
+(ZeroExt8to64 x) -> (MOVBUreg x)
+(ZeroExt16to64 x) -> (MOVHUreg x)
+(ZeroExt32to64 x) -> (MOVWUreg x)
+
+(SignExt8to16 x) -> (MOVBreg x)
+(SignExt8to32 x) -> (MOVBreg x)
+(SignExt16to32 x) -> (MOVHreg x)
+(SignExt8to64 x) -> (MOVBreg x)
+(SignExt16to64 x) -> (MOVHreg x)
+(SignExt32to64 x) -> (MOVWreg x)
+
+// float <-> int conversion
+(Cvt32to32F x) -> (SCVTFWS x)
+(Cvt32to64F x) -> (SCVTFWD x)
+(Cvt64to32F x) -> (SCVTFS x)
+(Cvt64to64F x) -> (SCVTFD x)
+(Cvt32Uto32F x) -> (UCVTFWS x)
+(Cvt32Uto64F x) -> (UCVTFWD x)
+(Cvt64Uto32F x) -> (UCVTFS x)
+(Cvt64Uto64F x) -> (UCVTFD x)
+(Cvt32Fto32 x) -> (FCVTZSSW x)
+(Cvt64Fto32 x) -> (FCVTZSDW x)
+(Cvt32Fto64 x) -> (FCVTZSS x)
+(Cvt64Fto64 x) -> (FCVTZSD x)
+(Cvt32Fto32U x) -> (FCVTZUSW x)
+(Cvt64Fto32U x) -> (FCVTZUDW x)
+(Cvt32Fto64U x) -> (FCVTZUS x)
+(Cvt64Fto64U x) -> (FCVTZUD x)
+(Cvt32Fto64F x) -> (FCVTSD x)
+(Cvt64Fto32F x) -> (FCVTDS x)
+
+// comparisons
+(Eq8 x y)  -> (Equal (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Eq16 x y) -> (Equal (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Eq32 x y) -> (Equal (CMPW x y))
+(Eq64 x y) -> (Equal (CMP x y))
+(EqPtr x y) -> (Equal (CMP x y))
+(Eq32F x y) -> (Equal (FCMPS x y))
+(Eq64F x y) -> (Equal (FCMPD x y))
+
+(Neq8 x y)  -> (NotEqual (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Neq16 x y) -> (NotEqual (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Neq32 x y) -> (NotEqual (CMPW x y))
+(Neq64 x y) -> (NotEqual (CMP x y))
+(NeqPtr x y) -> (NotEqual (CMP x y))
+(Neq32F x y) -> (NotEqual (FCMPS x y))
+(Neq64F x y) -> (NotEqual (FCMPD x y))
+
+(Less8 x y)  -> (LessThan (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+(Less16 x y) -> (LessThan (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+(Less32 x y) -> (LessThan (CMPW x y))
+(Less64 x y) -> (LessThan (CMP x y))
+(Less32F x y) -> (GreaterThan (FCMPS y x)) // reverse operands to work around NaN
+(Less64F x y) -> (GreaterThan (FCMPD y x)) // reverse operands to work around NaN
+
+(Less8U x y)  -> (LessThanU (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Less16U x y) -> (LessThanU (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Less32U x y) -> (LessThanU (CMPW x y))
+(Less64U x y) -> (LessThanU (CMP x y))
+
+(Leq8 x y)  -> (LessEqual (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+(Leq16 x y) -> (LessEqual (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+(Leq32 x y) -> (LessEqual (CMPW x y))
+(Leq64 x y) -> (LessEqual (CMP x y))
+(Leq32F x y) -> (GreaterEqual (FCMPS y x)) // reverse operands to work around NaN
+(Leq64F x y) -> (GreaterEqual (FCMPD y x)) // reverse operands to work around NaN
+
+(Leq8U x y)  -> (LessEqualU (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Leq16U x y) -> (LessEqualU (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Leq32U x y) -> (LessEqualU (CMPW x y))
+(Leq64U x y) -> (LessEqualU (CMP x y))
+
+(Greater8 x y)  -> (GreaterThan (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+(Greater16 x y) -> (GreaterThan (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+(Greater32 x y) -> (GreaterThan (CMPW x y))
+(Greater64 x y) -> (GreaterThan (CMP x y))
+(Greater32F x y) -> (GreaterThan (FCMPS x y))
+(Greater64F x y) -> (GreaterThan (FCMPD x y))
+
+(Greater8U x y)  -> (GreaterThanU (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Greater16U x y) -> (GreaterThanU (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Greater32U x y) -> (GreaterThanU (CMPW x y))
+(Greater64U x y) -> (GreaterThanU (CMP x y))
+
+(Geq8 x y)  -> (GreaterEqual (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+(Geq16 x y) -> (GreaterEqual (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+(Geq32 x y) -> (GreaterEqual (CMPW x y))
+(Geq64 x y) -> (GreaterEqual (CMP x y))
+(Geq32F x y) -> (GreaterEqual (FCMPS x y))
+(Geq64F x y) -> (GreaterEqual (FCMPD x y))
+
+(Geq8U x y)  -> (GreaterEqualU (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Geq16U x y) -> (GreaterEqualU (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Geq32U x y) -> (GreaterEqualU (CMPW x y))
+(Geq64U x y) -> (GreaterEqualU (CMP x y))
+
+(OffPtr [off] ptr:(SP)) -> (MOVDaddr [off] ptr)
+(OffPtr [off] ptr) -> (ADDconst [off] ptr)
+
+(Addr {sym} base) -> (MOVDaddr {sym} base)
+
+// loads
+(Load <t> ptr mem) && t.IsBoolean() -> (MOVBUload ptr mem)
+(Load <t> ptr mem) && (is8BitInt(t) && isSigned(t)) -> (MOVBload ptr mem)
+(Load <t> ptr mem) && (is8BitInt(t) && !isSigned(t)) -> (MOVBUload ptr mem)
+(Load <t> ptr mem) && (is16BitInt(t) && isSigned(t)) -> (MOVHload ptr mem)
+(Load <t> ptr mem) && (is16BitInt(t) && !isSigned(t)) -> (MOVHUload ptr mem)
+(Load <t> ptr mem) && (is32BitInt(t) && isSigned(t)) -> (MOVWload ptr mem)
+(Load <t> ptr mem) && (is32BitInt(t) && !isSigned(t)) -> (MOVWUload ptr mem)
+(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVDload ptr mem)
+(Load <t> ptr mem) && is32BitFloat(t) -> (FMOVSload ptr mem)
+(Load <t> ptr mem) && is64BitFloat(t) -> (FMOVDload ptr mem)
+
+// stores
+(Store [1] ptr val mem) -> (MOVBstore ptr val mem)
+(Store [2] ptr val mem) -> (MOVHstore ptr val mem)
+(Store [4] ptr val mem) && !is32BitFloat(val.Type) -> (MOVWstore ptr val mem)
+(Store [8] ptr val mem) && !is64BitFloat(val.Type) -> (MOVDstore ptr val mem)
+(Store [4] ptr val mem) && is32BitFloat(val.Type) -> (FMOVSstore ptr val mem)
+(Store [8] ptr val mem) && is64BitFloat(val.Type) -> (FMOVDstore ptr val mem)
+
+// zeroing
+(Zero [s] _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore ptr (MOVDconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 2 -> (MOVHstore ptr (MOVDconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 -> (MOVWstore ptr (MOVDconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 8 -> (MOVDstore ptr (MOVDconst [0]) mem)
+
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] ptr (MOVDconst [0])
+		(MOVHstore ptr (MOVDconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 5 ->
+	(MOVBstore [4] ptr (MOVDconst [0])
+		(MOVWstore ptr (MOVDconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 6 ->
+	(MOVHstore [4] ptr (MOVDconst [0])
+		(MOVWstore ptr (MOVDconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 7 ->
+	(MOVBstore [6] ptr (MOVDconst [0])
+		(MOVHstore [4] ptr (MOVDconst [0])
+			(MOVWstore ptr (MOVDconst [0]) mem)))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 12 ->
+	(MOVWstore [8] ptr (MOVDconst [0])
+		(MOVDstore ptr (MOVDconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 16 ->
+	(MOVDstore [8] ptr (MOVDconst [0])
+		(MOVDstore ptr (MOVDconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 24 ->
+	(MOVDstore [16] ptr (MOVDconst [0])
+		(MOVDstore [8] ptr (MOVDconst [0])
+			(MOVDstore ptr (MOVDconst [0]) mem)))
+
+// strip off fractional word zeroing
+(Zero [s] ptr mem) && SizeAndAlign(s).Size()%8 != 0 && SizeAndAlign(s).Size() > 8 ->
+	(Zero [MakeSizeAndAlign(SizeAndAlign(s).Size()%8, 1).Int64()]
+		(OffPtr <ptr.Type> ptr [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8])
+		(Zero [MakeSizeAndAlign(SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8, 1).Int64()] ptr mem))
+
+// medium zeroing uses a duff device
+// 4, 8, and 128 are magic constants, see runtime/mkduff.go
+(Zero [s] ptr mem)
+	&& SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size() <= 8*128
+	&& !config.noDuffDevice ->
+	(DUFFZERO [4 * (128 - int64(SizeAndAlign(s).Size()/8))] ptr mem)
+
+// large zeroing uses a loop
+(Zero [s] ptr mem)
+	&& SizeAndAlign(s).Size()%8 == 0 && (SizeAndAlign(s).Size() > 8*128 || config.noDuffDevice) ->
+	(LoweredZero
+		ptr
+		(ADDconst <ptr.Type> [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)] ptr)
+		mem)
+
+// moves
+(Move [s] _ _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore dst (MOVBUload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 -> (MOVHstore dst (MOVHUload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 -> (MOVWstore dst (MOVWUload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 -> (MOVDstore dst (MOVDload src mem) mem)
+
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] dst (MOVBUload [2] src mem)
+		(MOVHstore dst (MOVHUload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 5 ->
+	(MOVBstore [4] dst (MOVBUload [4] src mem)
+		(MOVWstore dst (MOVWUload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 6 ->
+	(MOVHstore [4] dst (MOVHUload [4] src mem)
+		(MOVWstore dst (MOVWUload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 7 ->
+	(MOVBstore [6] dst (MOVBUload [6] src mem)
+		(MOVHstore [4] dst (MOVHUload [4] src mem)
+			(MOVWstore dst (MOVWUload src mem) mem)))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 12 ->
+	(MOVWstore [8] dst (MOVWUload [8] src mem)
+		(MOVDstore dst (MOVDload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 16 ->
+	(MOVDstore [8] dst (MOVDload [8] src mem)
+		(MOVDstore dst (MOVDload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 24 ->
+	(MOVDstore [16] dst (MOVDload [16] src mem)
+		(MOVDstore [8] dst (MOVDload [8] src mem)
+			(MOVDstore dst (MOVDload src mem) mem)))
+
+// strip off fractional word move
+(Move [s] dst src mem) && SizeAndAlign(s).Size()%8 != 0 && SizeAndAlign(s).Size() > 8 ->
+	(Move [MakeSizeAndAlign(SizeAndAlign(s).Size()%8, 1).Int64()]
+		(OffPtr <dst.Type> dst [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8])
+		(OffPtr <src.Type> src [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8])
+		(Move [MakeSizeAndAlign(SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8, 1).Int64()] dst src mem))
+
+// medium move uses a duff device
+// 8 and 128 are magic constants, see runtime/mkduff.go
+(Move [s] dst src mem)
+	&& SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size() <= 8*128
+	&& !config.noDuffDevice ->
+	(DUFFCOPY [8 * (128 - int64(SizeAndAlign(s).Size()/8))] dst src mem)
+
+// large move uses a loop
+(Move [s] dst src mem)
+	&& SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size()%8 == 0 ->
+	(LoweredMove
+		dst
+		src
+		(ADDconst <src.Type> src [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)])
+		mem)
+
+// calls
+(StaticCall [argwid] {target} mem) -> (CALLstatic [argwid] {target} mem)
+(ClosureCall [argwid] entry closure mem) -> (CALLclosure [argwid] entry closure mem)
+(DeferCall [argwid] mem) -> (CALLdefer [argwid] mem)
+(GoCall [argwid] mem) -> (CALLgo [argwid] mem)
+(InterCall [argwid] entry mem) -> (CALLinter [argwid] entry mem)
+
+// checks
+(NilCheck ptr mem) -> (LoweredNilCheck ptr mem)
+(IsNonNil ptr) -> (NotEqual (CMPconst [0] ptr))
+(IsInBounds idx len) -> (LessThanU (CMP idx len))
+(IsSliceInBounds idx len) -> (LessEqualU (CMP idx len))
+
+// pseudo-ops
+(GetClosurePtr) -> (LoweredGetClosurePtr)
+(Convert x mem) -> (MOVDconvert x mem)
+
+// Absorb pseudo-ops into blocks.
+(If (Equal cc) yes no) -> (EQ cc yes no)
+(If (NotEqual cc) yes no) -> (NE cc yes no)
+(If (LessThan cc) yes no) -> (LT cc yes no)
+(If (LessThanU cc) yes no) -> (ULT cc yes no)
+(If (LessEqual cc) yes no) -> (LE cc yes no)
+(If (LessEqualU cc) yes no) -> (ULE cc yes no)
+(If (GreaterThan cc) yes no) -> (GT cc yes no)
+(If (GreaterThanU cc) yes no) -> (UGT cc yes no)
+(If (GreaterEqual cc) yes no) -> (GE cc yes no)
+(If (GreaterEqualU cc) yes no) -> (UGE cc yes no)
+
+(If cond yes no) -> (NZ cond yes no)
+
+// atomic intrinsics
+// Note: these ops do not accept offset.
+(AtomicLoad32  ptr mem) -> (LDARW ptr mem)
+(AtomicLoad64  ptr mem) -> (LDAR  ptr mem)
+(AtomicLoadPtr ptr mem) -> (LDAR  ptr mem)
+
+(AtomicStore32      ptr val mem) -> (STLRW ptr val mem)
+(AtomicStore64      ptr val mem) -> (STLR  ptr val mem)
+(AtomicStorePtrNoWB ptr val mem) -> (STLR  ptr val mem)
+
+(AtomicExchange32 ptr val mem) -> (LoweredAtomicExchange32 ptr val mem)
+(AtomicExchange64 ptr val mem) -> (LoweredAtomicExchange64 ptr val mem)
+
+(AtomicAdd32 ptr val mem) -> (LoweredAtomicAdd32 ptr val mem)
+(AtomicAdd64 ptr val mem) -> (LoweredAtomicAdd64 ptr val mem)
+
+(AtomicCompareAndSwap32 ptr old new_ mem) -> (LoweredAtomicCas32 ptr old new_ mem)
+(AtomicCompareAndSwap64 ptr old new_ mem) -> (LoweredAtomicCas64 ptr old new_ mem)
+
+(AtomicAnd8 ptr val mem) -> (LoweredAtomicAnd8 ptr val mem)
+(AtomicOr8  ptr val mem) -> (LoweredAtomicOr8  ptr val mem)
+
+// Optimizations
+
+// Absorb boolean tests into block
+(NZ (Equal cc) yes no) -> (EQ cc yes no)
+(NZ (NotEqual cc) yes no) -> (NE cc yes no)
+(NZ (LessThan cc) yes no) -> (LT cc yes no)
+(NZ (LessThanU cc) yes no) -> (ULT cc yes no)
+(NZ (LessEqual cc) yes no) -> (LE cc yes no)
+(NZ (LessEqualU cc) yes no) -> (ULE cc yes no)
+(NZ (GreaterThan cc) yes no) -> (GT cc yes no)
+(NZ (GreaterThanU cc) yes no) -> (UGT cc yes no)
+(NZ (GreaterEqual cc) yes no) -> (GE cc yes no)
+(NZ (GreaterEqualU cc) yes no) -> (UGE cc yes no)
+
+(EQ (CMPconst [0] x) yes no) -> (Z x yes no)
+(NE (CMPconst [0] x) yes no) -> (NZ x yes no)
+(EQ (CMPWconst [0] x) yes no) -> (ZW x yes no)
+(NE (CMPWconst [0] x) yes no) -> (NZW x yes no)
+
+// fold offset into address
+(ADDconst [off1] (MOVDaddr [off2] {sym} ptr)) -> (MOVDaddr [off1+off2] {sym} ptr)
+
+// fold address into load/store
+// only small offset (between -256 and 256) or offset that is a multiple of data size
+// can be encoded in the instructions
+// since this rewriting takes place before stack allocation, the offset to SP is unknown,
+// so don't do it for args and locals with unaligned offset
+(MOVBload [off1] {sym} (ADDconst [off2] ptr) mem) -> (MOVBload [off1+off2] {sym} ptr mem)
+(MOVBUload [off1] {sym} (ADDconst [off2] ptr) mem) -> (MOVBUload [off1+off2] {sym} ptr mem)
+(MOVHload [off1] {sym} (ADDconst [off2] ptr) mem)
+	&& (off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVHload [off1+off2] {sym} ptr mem)
+(MOVHUload [off1] {sym} (ADDconst [off2] ptr) mem)
+	&& (off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVHUload [off1+off2] {sym} ptr mem)
+(MOVWload [off1] {sym} (ADDconst [off2] ptr) mem)
+	&& (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVWload [off1+off2] {sym} ptr mem)
+(MOVWUload [off1] {sym} (ADDconst [off2] ptr) mem)
+	&& (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVWUload [off1+off2] {sym} ptr mem)
+(MOVDload [off1] {sym} (ADDconst [off2] ptr) mem)
+	&& (off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVDload [off1+off2] {sym} ptr mem)
+(FMOVSload [off1] {sym} (ADDconst [off2] ptr) mem)
+	&& (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(FMOVSload [off1+off2] {sym} ptr mem)
+(FMOVDload [off1] {sym} (ADDconst [off2] ptr) mem)
+	&& (off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(FMOVDload [off1+off2] {sym} ptr mem)
+
+(MOVBstore [off1] {sym} (ADDconst [off2] ptr) val mem) -> (MOVBstore [off1+off2] {sym} ptr val mem)
+(MOVHstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	&& (off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVHstore [off1+off2] {sym} ptr val mem)
+(MOVWstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	&& (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVWstore [off1+off2] {sym} ptr val mem)
+(MOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	&& (off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVDstore [off1+off2] {sym} ptr val mem)
+(FMOVSstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	&& (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(FMOVSstore [off1+off2] {sym} ptr val mem)
+(FMOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	&& (off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(FMOVDstore [off1+off2] {sym} ptr val mem)
+(MOVBstorezero [off1] {sym} (ADDconst [off2] ptr) mem) -> (MOVBstorezero [off1+off2] {sym} ptr mem)
+(MOVHstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
+	&& (off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVHstorezero [off1+off2] {sym} ptr mem)
+(MOVWstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
+	&& (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVWstorezero [off1+off2] {sym} ptr mem)
+(MOVDstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
+	&& (off1+off2)%2==8 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym) ->
+	(MOVDstorezero [off1+off2] {sym} ptr mem)
+
+(MOVBload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVBload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVBUload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVBUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHUload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWUload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVWUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(FMOVSload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(FMOVSload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(FMOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(FMOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+
+(MOVBstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVHstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVWstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(FMOVSstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(FMOVSstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(FMOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(FMOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVBstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVDstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2)
+	&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1)) ->
+	(MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+
+// store zero
+(MOVBstore [off] {sym} ptr (MOVDconst [0]) mem) -> (MOVBstorezero [off] {sym} ptr mem)
+(MOVHstore [off] {sym} ptr (MOVDconst [0]) mem) -> (MOVHstorezero [off] {sym} ptr mem)
+(MOVWstore [off] {sym} ptr (MOVDconst [0]) mem) -> (MOVWstorezero [off] {sym} ptr mem)
+(MOVDstore [off] {sym} ptr (MOVDconst [0]) mem) -> (MOVDstorezero [off] {sym} ptr mem)
+
+// replace load from same location as preceding store with copy
+// these seem to have bad interaction with other rules, resulting in slower code
+//(MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+//(MOVBUload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+//(MOVHload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+//(MOVHUload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+//(MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+//(MOVWUload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+//(MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+//(FMOVSload [off] {sym} ptr (FMOVSstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+//(FMOVDload [off] {sym} ptr (FMOVDstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+
+(MOVBload [off] {sym} ptr (MOVBstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDconst [0])
+(MOVBUload [off] {sym} ptr (MOVBstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDconst [0])
+(MOVHload [off] {sym} ptr (MOVHstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDconst [0])
+(MOVHUload [off] {sym} ptr (MOVHstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDconst [0])
+(MOVWload [off] {sym} ptr (MOVWstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDconst [0])
+(MOVWUload [off] {sym} ptr (MOVWstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDconst [0])
+(MOVDload [off] {sym} ptr (MOVDstorezero [off2] {sym2} ptr2 _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVDconst [0])
+
+// don't extend after proper load
+(MOVBreg x:(MOVBload _ _)) -> (MOVDreg x)
+(MOVBUreg x:(MOVBUload _ _)) -> (MOVDreg x)
+(MOVHreg x:(MOVBload _ _)) -> (MOVDreg x)
+(MOVHreg x:(MOVBUload _ _)) -> (MOVDreg x)
+(MOVHreg x:(MOVHload _ _)) -> (MOVDreg x)
+(MOVHUreg x:(MOVBUload _ _)) -> (MOVDreg x)
+(MOVHUreg x:(MOVHUload _ _)) -> (MOVDreg x)
+(MOVWreg x:(MOVBload _ _)) -> (MOVDreg x)
+(MOVWreg x:(MOVBUload _ _)) -> (MOVDreg x)
+(MOVWreg x:(MOVHload _ _)) -> (MOVDreg x)
+(MOVWreg x:(MOVHUload _ _)) -> (MOVDreg x)
+(MOVWreg x:(MOVWload _ _)) -> (MOVDreg x)
+(MOVWUreg x:(MOVBUload _ _)) -> (MOVDreg x)
+(MOVWUreg x:(MOVHUload _ _)) -> (MOVDreg x)
+(MOVWUreg x:(MOVWUload _ _)) -> (MOVDreg x)
+
+// fold double extensions
+(MOVBreg x:(MOVBreg _)) -> (MOVDreg x)
+(MOVBUreg x:(MOVBUreg _)) -> (MOVDreg x)
+(MOVHreg x:(MOVBreg _)) -> (MOVDreg x)
+(MOVHreg x:(MOVBUreg _)) -> (MOVDreg x)
+(MOVHreg x:(MOVHreg _)) -> (MOVDreg x)
+(MOVHUreg x:(MOVBUreg _)) -> (MOVDreg x)
+(MOVHUreg x:(MOVHUreg _)) -> (MOVDreg x)
+(MOVWreg x:(MOVBreg _)) -> (MOVDreg x)
+(MOVWreg x:(MOVBUreg _)) -> (MOVDreg x)
+(MOVWreg x:(MOVHreg _)) -> (MOVDreg x)
+(MOVWreg x:(MOVHreg _)) -> (MOVDreg x)
+(MOVWreg x:(MOVWreg _)) -> (MOVDreg x)
+(MOVWUreg x:(MOVBUreg _)) -> (MOVDreg x)
+(MOVWUreg x:(MOVHUreg _)) -> (MOVDreg x)
+(MOVWUreg x:(MOVWUreg _)) -> (MOVDreg x)
+
+// don't extend before store
+(MOVBstore [off] {sym} ptr (MOVBreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVBUreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVHreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVHUreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVWUreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHUreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVWUreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVWstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
+(MOVWstore [off] {sym} ptr (MOVWUreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
+
+// if a register move has only 1 use, just use the same register without emitting instruction
+// MOVDnop doesn't emit instruction, only for ensuring the type.
+(MOVDreg x) && x.Uses == 1 -> (MOVDnop x)
+
+// fold constant into arithmatic ops
+(ADD (MOVDconst [c]) x) -> (ADDconst [c] x)
+(ADD x (MOVDconst [c])) -> (ADDconst [c] x)
+(SUB x (MOVDconst [c])) -> (SUBconst [c] x)
+(AND (MOVDconst [c]) x) -> (ANDconst [c] x)
+(AND x (MOVDconst [c])) -> (ANDconst [c] x)
+(OR  (MOVDconst [c]) x) -> (ORconst  [c] x)
+(OR  x (MOVDconst [c])) -> (ORconst  [c] x)
+(XOR (MOVDconst [c]) x) -> (XORconst [c] x)
+(XOR x (MOVDconst [c])) -> (XORconst [c] x)
+(BIC x (MOVDconst [c])) -> (BICconst [c] x)
+
+(SLL x (MOVDconst [c])) -> (SLLconst x [c&63]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=64)
+(SRL x (MOVDconst [c])) -> (SRLconst x [c&63])
+(SRA x (MOVDconst [c])) -> (SRAconst x [c&63])
+
+(CMP x (MOVDconst [c])) -> (CMPconst [c] x)
+(CMP (MOVDconst [c]) x) -> (InvertFlags (CMPconst [c] x))
+(CMPW x (MOVDconst [c])) -> (CMPWconst [int64(int32(c))] x)
+(CMPW (MOVDconst [c]) x) -> (InvertFlags (CMPWconst [int64(int32(c))] x))
+
+// mul by constant
+(MUL x (MOVDconst [-1])) -> (NEG x)
+(MUL _ (MOVDconst [0])) -> (MOVDconst [0])
+(MUL x (MOVDconst [1])) -> x
+(MUL x (MOVDconst [c])) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x)
+(MUL x (MOVDconst [c])) && isPowerOfTwo(c-1) && c >= 3 -> (ADDshiftLL x x [log2(c-1)])
+(MUL x (MOVDconst [c])) && isPowerOfTwo(c+1) && c >= 7 -> (ADDshiftLL (NEG <x.Type> x) x [log2(c+1)])
+(MUL x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) -> (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+(MUL x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) -> (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+(MUL x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) -> (SLLconst [log2(c/7)] (ADDshiftLL <x.Type> (NEG <x.Type> x) x [3]))
+(MUL x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) -> (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+
+(MUL (MOVDconst [-1]) x) -> (NEG x)
+(MUL (MOVDconst [0]) _) -> (MOVDconst [0])
+(MUL (MOVDconst [1]) x) -> x
+(MUL (MOVDconst [c]) x) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x)
+(MUL (MOVDconst [c]) x) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x)
+(MUL (MOVDconst [c]) x) && isPowerOfTwo(c-1) && c >= 3 -> (ADDshiftLL x x [log2(c-1)])
+(MUL (MOVDconst [c]) x) && isPowerOfTwo(c+1) && c >= 7 -> (ADDshiftLL (NEG <x.Type> x) x [log2(c+1)])
+(MUL (MOVDconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) -> (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+(MUL (MOVDconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) -> (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+(MUL (MOVDconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) -> (SLLconst [log2(c/7)] (ADDshiftLL <x.Type> (NEG <x.Type> x) x [3]))
+(MUL (MOVDconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) -> (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+
+(MULW x (MOVDconst [c])) && int32(c)==-1 -> (NEG x)
+(MULW _ (MOVDconst [c])) && int32(c)==0 -> (MOVDconst [0])
+(MULW x (MOVDconst [c])) && int32(c)==1 -> x
+(MULW x (MOVDconst [c])) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x)
+(MULW x (MOVDconst [c])) && isPowerOfTwo(c-1) && int32(c) >= 3 -> (ADDshiftLL x x [log2(c-1)])
+(MULW x (MOVDconst [c])) && isPowerOfTwo(c+1) && int32(c) >= 7 -> (ADDshiftLL (NEG <x.Type> x) x [log2(c+1)])
+(MULW x (MOVDconst [c])) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+(MULW x (MOVDconst [c])) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+(MULW x (MOVDconst [c])) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (SLLconst [log2(c/7)] (ADDshiftLL <x.Type> (NEG <x.Type> x) x [3]))
+(MULW x (MOVDconst [c])) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+
+(MULW (MOVDconst [c]) x) && int32(c)==-1 -> (NEG x)
+(MULW (MOVDconst [c]) _) && int32(c)==0 -> (MOVDconst [0])
+(MULW (MOVDconst [c]) x) && int32(c)==1 -> x
+(MULW (MOVDconst [c]) x) && isPowerOfTwo(c) -> (SLLconst [log2(c)] x)
+(MULW (MOVDconst [c]) x) && isPowerOfTwo(c-1) && int32(c) >= 3 -> (ADDshiftLL x x [log2(c-1)])
+(MULW (MOVDconst [c]) x) && isPowerOfTwo(c+1) && int32(c) >= 7 -> (ADDshiftLL (NEG <x.Type> x) x [log2(c+1)])
+(MULW (MOVDconst [c]) x) && c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c) -> (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+(MULW (MOVDconst [c]) x) && c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c) -> (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+(MULW (MOVDconst [c]) x) && c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c) -> (SLLconst [log2(c/7)] (ADDshiftLL <x.Type> (NEG <x.Type> x) x [3]))
+(MULW (MOVDconst [c]) x) && c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c) -> (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+
+// div by constant
+(UDIV x (MOVDconst [1])) -> x
+(UDIV x (MOVDconst [c])) && isPowerOfTwo(c) -> (SRLconst [log2(c)] x)
+(UDIVW x (MOVDconst [c])) && uint32(c)==1 -> x
+(UDIVW x (MOVDconst [c])) && isPowerOfTwo(c) && is32Bit(c) -> (SRLconst [log2(c)] x)
+(UMOD _ (MOVDconst [1])) -> (MOVDconst [0])
+(UMOD x (MOVDconst [c])) && isPowerOfTwo(c) -> (ANDconst [c-1] x)
+(UMODW _ (MOVDconst [c])) && uint32(c)==1 -> (MOVDconst [0])
+(UMODW x (MOVDconst [c])) && isPowerOfTwo(c) && is32Bit(c) -> (ANDconst [c-1] x)
+
+// generic simplifications
+(ADD x (NEG y)) -> (SUB x y)
+(ADD (NEG y) x) -> (SUB x y)
+(SUB x x) -> (MOVDconst [0])
+(AND x x) -> x
+(OR  x x) -> x
+(XOR x x) -> (MOVDconst [0])
+(BIC x x) -> (MOVDconst [0])
+(AND x (MVN y)) -> (BIC x y)
+(CSELULT x (MOVDconst [0]) flag) -> (CSELULT0 x flag)
+
+// remove redundant *const ops
+(ADDconst [0]  x) -> x
+(SUBconst [0]  x) -> x
+(ANDconst [0]  _) -> (MOVDconst [0])
+(ANDconst [-1] x) -> x
+(ORconst  [0]  x) -> x
+(ORconst  [-1] _) -> (MOVDconst [-1])
+(XORconst [0]  x) -> x
+(XORconst [-1] x) -> (MVN x)
+(BICconst [0]  x) -> x
+(BICconst [-1] _) -> (MOVDconst [0])
+
+// generic constant folding
+(ADDconst [c] (MOVDconst [d]))  -> (MOVDconst [c+d])
+(ADDconst [c] (ADDconst [d] x)) -> (ADDconst [c+d] x)
+(ADDconst [c] (SUBconst [d] x)) -> (ADDconst [c-d] x)
+(SUBconst [c] (MOVDconst [d]))  -> (MOVDconst [d-c])
+(SUBconst [c] (SUBconst [d] x)) -> (ADDconst [-c-d] x)
+(SUBconst [c] (ADDconst [d] x)) -> (ADDconst [-c+d] x)
+(SLLconst [c] (MOVDconst [d]))  -> (MOVDconst [int64(d)<<uint64(c)])
+(SRLconst [c] (MOVDconst [d]))  -> (MOVDconst [int64(uint64(d)>>uint64(c))])
+(SRAconst [c] (MOVDconst [d]))  -> (MOVDconst [int64(d)>>uint64(c)])
+(MUL   (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [c*d])
+(MULW  (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(int32(c)*int32(d))])
+(DIV   (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(c)/int64(d)])
+(UDIV  (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(uint64(c)/uint64(d))])
+(DIVW  (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(int32(c)/int32(d))])
+(UDIVW (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(uint32(c)/uint32(d))])
+(MOD   (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(c)%int64(d)])
+(UMOD  (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(uint64(c)%uint64(d))])
+(MODW  (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(int32(c)%int32(d))])
+(UMODW (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [int64(uint32(c)%uint32(d))])
+(ANDconst [c] (MOVDconst [d]))  -> (MOVDconst [c&d])
+(ANDconst [c] (ANDconst [d] x)) -> (ANDconst [c&d] x)
+(ORconst  [c] (MOVDconst [d]))  -> (MOVDconst [c|d])
+(ORconst  [c] (ORconst [d] x))  -> (ORconst [c|d] x)
+(XORconst [c] (MOVDconst [d]))  -> (MOVDconst [c^d])
+(XORconst [c] (XORconst [d] x)) -> (XORconst [c^d] x)
+(BICconst [c] (MOVDconst [d]))  -> (MOVDconst [d&^c])
+(MVN (MOVDconst [c])) -> (MOVDconst [^c])
+(NEG (MOVDconst [c])) -> (MOVDconst [-c])
+(MOVBreg  (MOVDconst [c])) -> (MOVDconst [int64(int8(c))])
+(MOVBUreg (MOVDconst [c])) -> (MOVDconst [int64(uint8(c))])
+(MOVHreg  (MOVDconst [c])) -> (MOVDconst [int64(int16(c))])
+(MOVHUreg (MOVDconst [c])) -> (MOVDconst [int64(uint16(c))])
+(MOVWreg  (MOVDconst [c])) -> (MOVDconst [int64(int32(c))])
+(MOVWUreg (MOVDconst [c])) -> (MOVDconst [int64(uint32(c))])
+(MOVDreg  (MOVDconst [c])) -> (MOVDconst [c])
+
+// constant comparisons
+(CMPconst  (MOVDconst [x]) [y]) && x==y -> (FlagEQ)
+(CMPconst  (MOVDconst [x]) [y]) && int64(x)<int64(y) && uint64(x)<uint64(y) -> (FlagLT_ULT)
+(CMPconst  (MOVDconst [x]) [y]) && int64(x)<int64(y) && uint64(x)>uint64(y) -> (FlagLT_UGT)
+(CMPconst  (MOVDconst [x]) [y]) && int64(x)>int64(y) && uint64(x)<uint64(y) -> (FlagGT_ULT)
+(CMPconst  (MOVDconst [x]) [y]) && int64(x)>int64(y) && uint64(x)>uint64(y) -> (FlagGT_UGT)
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)==int32(y) -> (FlagEQ)
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)<int32(y) && uint32(x)<uint32(y) -> (FlagLT_ULT)
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)<int32(y) && uint32(x)>uint32(y) -> (FlagLT_UGT)
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)>int32(y) && uint32(x)<uint32(y) -> (FlagGT_ULT)
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)>int32(y) && uint32(x)>uint32(y) -> (FlagGT_UGT)
+
+// other known comparisons
+(CMPconst (MOVBUreg _) [c]) && 0xff < c -> (FlagLT_ULT)
+(CMPconst (MOVHUreg _) [c]) && 0xffff < c -> (FlagLT_ULT)
+(CMPconst (MOVWUreg _) [c]) && 0xffffffff < c -> (FlagLT_ULT)
+(CMPconst (ANDconst _ [m]) [n]) && 0 <= m && m < n -> (FlagLT_ULT)
+(CMPconst (SRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 63 && (1<<uint64(64-c)) <= uint64(n) -> (FlagLT_ULT)
+(CMPWconst (MOVBUreg _) [c]) && 0xff < int32(c) -> (FlagLT_ULT)
+(CMPWconst (MOVHUreg _) [c]) && 0xffff < int32(c) -> (FlagLT_ULT)
+
+// absorb flag constants into branches
+(EQ (FlagEQ) yes no) -> (First nil yes no)
+(EQ (FlagLT_ULT) yes no) -> (First nil no yes)
+(EQ (FlagLT_UGT) yes no) -> (First nil no yes)
+(EQ (FlagGT_ULT) yes no) -> (First nil no yes)
+(EQ (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(NE (FlagEQ) yes no) -> (First nil no yes)
+(NE (FlagLT_ULT) yes no) -> (First nil yes no)
+(NE (FlagLT_UGT) yes no) -> (First nil yes no)
+(NE (FlagGT_ULT) yes no) -> (First nil yes no)
+(NE (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(LT (FlagEQ) yes no) -> (First nil no yes)
+(LT (FlagLT_ULT) yes no) -> (First nil yes no)
+(LT (FlagLT_UGT) yes no) -> (First nil yes no)
+(LT (FlagGT_ULT) yes no) -> (First nil no yes)
+(LT (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(LE (FlagEQ) yes no) -> (First nil yes no)
+(LE (FlagLT_ULT) yes no) -> (First nil yes no)
+(LE (FlagLT_UGT) yes no) -> (First nil yes no)
+(LE (FlagGT_ULT) yes no) -> (First nil no yes)
+(LE (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(GT (FlagEQ) yes no) -> (First nil no yes)
+(GT (FlagLT_ULT) yes no) -> (First nil no yes)
+(GT (FlagLT_UGT) yes no) -> (First nil no yes)
+(GT (FlagGT_ULT) yes no) -> (First nil yes no)
+(GT (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(GE (FlagEQ) yes no) -> (First nil yes no)
+(GE (FlagLT_ULT) yes no) -> (First nil no yes)
+(GE (FlagLT_UGT) yes no) -> (First nil no yes)
+(GE (FlagGT_ULT) yes no) -> (First nil yes no)
+(GE (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(ULT (FlagEQ) yes no) -> (First nil no yes)
+(ULT (FlagLT_ULT) yes no) -> (First nil yes no)
+(ULT (FlagLT_UGT) yes no) -> (First nil no yes)
+(ULT (FlagGT_ULT) yes no) -> (First nil yes no)
+(ULT (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(ULE (FlagEQ) yes no) -> (First nil yes no)
+(ULE (FlagLT_ULT) yes no) -> (First nil yes no)
+(ULE (FlagLT_UGT) yes no) -> (First nil no yes)
+(ULE (FlagGT_ULT) yes no) -> (First nil yes no)
+(ULE (FlagGT_UGT) yes no) -> (First nil no yes)
+
+(UGT (FlagEQ) yes no) -> (First nil no yes)
+(UGT (FlagLT_ULT) yes no) -> (First nil no yes)
+(UGT (FlagLT_UGT) yes no) -> (First nil yes no)
+(UGT (FlagGT_ULT) yes no) -> (First nil no yes)
+(UGT (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(UGE (FlagEQ) yes no) -> (First nil yes no)
+(UGE (FlagLT_ULT) yes no) -> (First nil no yes)
+(UGE (FlagLT_UGT) yes no) -> (First nil yes no)
+(UGE (FlagGT_ULT) yes no) -> (First nil no yes)
+(UGE (FlagGT_UGT) yes no) -> (First nil yes no)
+
+(Z (MOVDconst [0]) yes no) -> (First nil yes no)
+(Z (MOVDconst [c]) yes no) && c != 0 -> (First nil no yes)
+(NZ (MOVDconst [0]) yes no) -> (First nil no yes)
+(NZ (MOVDconst [c]) yes no) && c != 0 -> (First nil yes no)
+(ZW (MOVDconst [c]) yes no) && int32(c) == 0 -> (First nil yes no)
+(ZW (MOVDconst [c]) yes no) && int32(c) != 0 -> (First nil no yes)
+(NZW (MOVDconst [c]) yes no) && int32(c) == 0 -> (First nil no yes)
+(NZW (MOVDconst [c]) yes no) && int32(c) != 0 -> (First nil yes no)
+
+// absorb InvertFlags into branches
+(LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
+(GT (InvertFlags cmp) yes no) -> (LT cmp yes no)
+(LE (InvertFlags cmp) yes no) -> (GE cmp yes no)
+(GE (InvertFlags cmp) yes no) -> (LE cmp yes no)
+(ULT (InvertFlags cmp) yes no) -> (UGT cmp yes no)
+(UGT (InvertFlags cmp) yes no) -> (ULT cmp yes no)
+(ULE (InvertFlags cmp) yes no) -> (UGE cmp yes no)
+(UGE (InvertFlags cmp) yes no) -> (ULE cmp yes no)
+(EQ (InvertFlags cmp) yes no) -> (EQ cmp yes no)
+(NE (InvertFlags cmp) yes no) -> (NE cmp yes no)
+
+// absorb flag constants into boolean values
+(Equal (FlagEQ)) -> (MOVDconst [1])
+(Equal (FlagLT_ULT)) -> (MOVDconst [0])
+(Equal (FlagLT_UGT)) -> (MOVDconst [0])
+(Equal (FlagGT_ULT)) -> (MOVDconst [0])
+(Equal (FlagGT_UGT)) -> (MOVDconst [0])
+
+(NotEqual (FlagEQ)) -> (MOVDconst [0])
+(NotEqual (FlagLT_ULT)) -> (MOVDconst [1])
+(NotEqual (FlagLT_UGT)) -> (MOVDconst [1])
+(NotEqual (FlagGT_ULT)) -> (MOVDconst [1])
+(NotEqual (FlagGT_UGT)) -> (MOVDconst [1])
+
+(LessThan (FlagEQ)) -> (MOVDconst [0])
+(LessThan (FlagLT_ULT)) -> (MOVDconst [1])
+(LessThan (FlagLT_UGT)) -> (MOVDconst [1])
+(LessThan (FlagGT_ULT)) -> (MOVDconst [0])
+(LessThan (FlagGT_UGT)) -> (MOVDconst [0])
+
+(LessThanU (FlagEQ)) -> (MOVDconst [0])
+(LessThanU (FlagLT_ULT)) -> (MOVDconst [1])
+(LessThanU (FlagLT_UGT)) -> (MOVDconst [0])
+(LessThanU (FlagGT_ULT)) -> (MOVDconst [1])
+(LessThanU (FlagGT_UGT)) -> (MOVDconst [0])
+
+(LessEqual (FlagEQ)) -> (MOVDconst [1])
+(LessEqual (FlagLT_ULT)) -> (MOVDconst [1])
+(LessEqual (FlagLT_UGT)) -> (MOVDconst [1])
+(LessEqual (FlagGT_ULT)) -> (MOVDconst [0])
+(LessEqual (FlagGT_UGT)) -> (MOVDconst [0])
+
+(LessEqualU (FlagEQ)) -> (MOVDconst [1])
+(LessEqualU (FlagLT_ULT)) -> (MOVDconst [1])
+(LessEqualU (FlagLT_UGT)) -> (MOVDconst [0])
+(LessEqualU (FlagGT_ULT)) -> (MOVDconst [1])
+(LessEqualU (FlagGT_UGT)) -> (MOVDconst [0])
+
+(GreaterThan (FlagEQ)) -> (MOVDconst [0])
+(GreaterThan (FlagLT_ULT)) -> (MOVDconst [0])
+(GreaterThan (FlagLT_UGT)) -> (MOVDconst [0])
+(GreaterThan (FlagGT_ULT)) -> (MOVDconst [1])
+(GreaterThan (FlagGT_UGT)) -> (MOVDconst [1])
+
+(GreaterThanU (FlagEQ)) -> (MOVDconst [0])
+(GreaterThanU (FlagLT_ULT)) -> (MOVDconst [0])
+(GreaterThanU (FlagLT_UGT)) -> (MOVDconst [1])
+(GreaterThanU (FlagGT_ULT)) -> (MOVDconst [0])
+(GreaterThanU (FlagGT_UGT)) -> (MOVDconst [1])
+
+(GreaterEqual (FlagEQ)) -> (MOVDconst [1])
+(GreaterEqual (FlagLT_ULT)) -> (MOVDconst [0])
+(GreaterEqual (FlagLT_UGT)) -> (MOVDconst [0])
+(GreaterEqual (FlagGT_ULT)) -> (MOVDconst [1])
+(GreaterEqual (FlagGT_UGT)) -> (MOVDconst [1])
+
+(GreaterEqualU (FlagEQ)) -> (MOVDconst [1])
+(GreaterEqualU (FlagLT_ULT)) -> (MOVDconst [0])
+(GreaterEqualU (FlagLT_UGT)) -> (MOVDconst [1])
+(GreaterEqualU (FlagGT_ULT)) -> (MOVDconst [0])
+(GreaterEqualU (FlagGT_UGT)) -> (MOVDconst [1])
+
+// absorb InvertFlags into boolean values
+(Equal (InvertFlags x)) -> (Equal x)
+(NotEqual (InvertFlags x)) -> (NotEqual x)
+(LessThan (InvertFlags x)) -> (GreaterThan x)
+(LessThanU (InvertFlags x)) -> (GreaterThanU x)
+(GreaterThan (InvertFlags x)) -> (LessThan x)
+(GreaterThanU (InvertFlags x)) -> (LessThanU x)
+(LessEqual (InvertFlags x)) -> (GreaterEqual x)
+(LessEqualU (InvertFlags x)) -> (GreaterEqualU x)
+(GreaterEqual (InvertFlags x)) -> (LessEqual x)
+(GreaterEqualU (InvertFlags x)) -> (LessEqualU x)
+
+// absorb flag constants into conditional instructions
+(CSELULT _ y (FlagEQ)) -> y
+(CSELULT x _ (FlagLT_ULT)) -> x
+(CSELULT _ y (FlagLT_UGT)) -> y
+(CSELULT x _ (FlagGT_ULT)) -> x
+(CSELULT _ y (FlagGT_UGT)) -> y
+(CSELULT0 _ (FlagEQ)) -> (MOVDconst [0])
+(CSELULT0 x (FlagLT_ULT)) -> x
+(CSELULT0 _ (FlagLT_UGT)) -> (MOVDconst [0])
+(CSELULT0 x (FlagGT_ULT)) -> x
+(CSELULT0 _ (FlagGT_UGT)) -> (MOVDconst [0])
+
+// absorb shifts into ops
+(ADD x (SLLconst [c] y)) -> (ADDshiftLL x y [c])
+(ADD (SLLconst [c] y) x) -> (ADDshiftLL x y [c])
+(ADD x (SRLconst [c] y)) -> (ADDshiftRL x y [c])
+(ADD (SRLconst [c] y) x) -> (ADDshiftRL x y [c])
+(ADD x (SRAconst [c] y)) -> (ADDshiftRA x y [c])
+(ADD (SRAconst [c] y) x) -> (ADDshiftRA x y [c])
+(SUB x (SLLconst [c] y)) -> (SUBshiftLL x y [c])
+(SUB x (SRLconst [c] y)) -> (SUBshiftRL x y [c])
+(SUB x (SRAconst [c] y)) -> (SUBshiftRA x y [c])
+(AND x (SLLconst [c] y)) -> (ANDshiftLL x y [c])
+(AND (SLLconst [c] y) x) -> (ANDshiftLL x y [c])
+(AND x (SRLconst [c] y)) -> (ANDshiftRL x y [c])
+(AND (SRLconst [c] y) x) -> (ANDshiftRL x y [c])
+(AND x (SRAconst [c] y)) -> (ANDshiftRA x y [c])
+(AND (SRAconst [c] y) x) -> (ANDshiftRA x y [c])
+(OR  x s:(SLLconst [c] y)) && s.Uses == 1 && clobber(s) -> (ORshiftLL  x y [c]) // useful for combined load
+(OR  s:(SLLconst [c] y) x) && s.Uses == 1 && clobber(s) -> (ORshiftLL  x y [c])
+(OR  x (SLLconst [c] y)) -> (ORshiftLL  x y [c])
+(OR  (SLLconst [c] y) x) -> (ORshiftLL  x y [c])
+(OR  x (SRLconst [c] y)) -> (ORshiftRL  x y [c])
+(OR  (SRLconst [c] y) x) -> (ORshiftRL  x y [c])
+(OR  x (SRAconst [c] y)) -> (ORshiftRA  x y [c])
+(OR  (SRAconst [c] y) x) -> (ORshiftRA  x y [c])
+(XOR x (SLLconst [c] y)) -> (XORshiftLL x y [c])
+(XOR (SLLconst [c] y) x) -> (XORshiftLL x y [c])
+(XOR x (SRLconst [c] y)) -> (XORshiftRL x y [c])
+(XOR (SRLconst [c] y) x) -> (XORshiftRL x y [c])
+(XOR x (SRAconst [c] y)) -> (XORshiftRA x y [c])
+(XOR (SRAconst [c] y) x) -> (XORshiftRA x y [c])
+(BIC x (SLLconst [c] y)) -> (BICshiftLL x y [c])
+(BIC x (SRLconst [c] y)) -> (BICshiftRL x y [c])
+(BIC x (SRAconst [c] y)) -> (BICshiftRA x y [c])
+(CMP x (SLLconst [c] y)) -> (CMPshiftLL x y [c])
+(CMP (SLLconst [c] y) x) -> (InvertFlags (CMPshiftLL x y [c]))
+(CMP x (SRLconst [c] y)) -> (CMPshiftRL x y [c])
+(CMP (SRLconst [c] y) x) -> (InvertFlags (CMPshiftRL x y [c]))
+(CMP x (SRAconst [c] y)) -> (CMPshiftRA x y [c])
+(CMP (SRAconst [c] y) x) -> (InvertFlags (CMPshiftRA x y [c]))
+
+// prefer *const ops to *shift ops
+(ADDshiftLL (MOVDconst [c]) x [d]) -> (ADDconst [c] (SLLconst <x.Type> x [d]))
+(ADDshiftRL (MOVDconst [c]) x [d]) -> (ADDconst [c] (SRLconst <x.Type> x [d]))
+(ADDshiftRA (MOVDconst [c]) x [d]) -> (ADDconst [c] (SRAconst <x.Type> x [d]))
+(ANDshiftLL (MOVDconst [c]) x [d]) -> (ANDconst [c] (SLLconst <x.Type> x [d]))
+(ANDshiftRL (MOVDconst [c]) x [d]) -> (ANDconst [c] (SRLconst <x.Type> x [d]))
+(ANDshiftRA (MOVDconst [c]) x [d]) -> (ANDconst [c] (SRAconst <x.Type> x [d]))
+(ORshiftLL  (MOVDconst [c]) x [d]) -> (ORconst  [c] (SLLconst <x.Type> x [d]))
+(ORshiftRL  (MOVDconst [c]) x [d]) -> (ORconst  [c] (SRLconst <x.Type> x [d]))
+(ORshiftRA  (MOVDconst [c]) x [d]) -> (ORconst  [c] (SRAconst <x.Type> x [d]))
+(XORshiftLL (MOVDconst [c]) x [d]) -> (XORconst [c] (SLLconst <x.Type> x [d]))
+(XORshiftRL (MOVDconst [c]) x [d]) -> (XORconst [c] (SRLconst <x.Type> x [d]))
+(XORshiftRA (MOVDconst [c]) x [d]) -> (XORconst [c] (SRAconst <x.Type> x [d]))
+(CMPshiftLL (MOVDconst [c]) x [d]) -> (InvertFlags (CMPconst [c] (SLLconst <x.Type> x [d])))
+(CMPshiftRL (MOVDconst [c]) x [d]) -> (InvertFlags (CMPconst [c] (SRLconst <x.Type> x [d])))
+(CMPshiftRA (MOVDconst [c]) x [d]) -> (InvertFlags (CMPconst [c] (SRAconst <x.Type> x [d])))
+
+// constant folding in *shift ops
+(ADDshiftLL x (MOVDconst [c]) [d]) -> (ADDconst x [int64(uint64(c)<<uint64(d))])
+(ADDshiftRL x (MOVDconst [c]) [d]) -> (ADDconst x [int64(uint64(c)>>uint64(d))])
+(ADDshiftRA x (MOVDconst [c]) [d]) -> (ADDconst x [int64(int64(c)>>uint64(d))])
+(SUBshiftLL x (MOVDconst [c]) [d]) -> (SUBconst x [int64(uint64(c)<<uint64(d))])
+(SUBshiftRL x (MOVDconst [c]) [d]) -> (SUBconst x [int64(uint64(c)>>uint64(d))])
+(SUBshiftRA x (MOVDconst [c]) [d]) -> (SUBconst x [int64(int64(c)>>uint64(d))])
+(ANDshiftLL x (MOVDconst [c]) [d]) -> (ANDconst x [int64(uint64(c)<<uint64(d))])
+(ANDshiftRL x (MOVDconst [c]) [d]) -> (ANDconst x [int64(uint64(c)>>uint64(d))])
+(ANDshiftRA x (MOVDconst [c]) [d]) -> (ANDconst x [int64(int64(c)>>uint64(d))])
+(ORshiftLL  x (MOVDconst [c]) [d]) -> (ORconst  x [int64(uint64(c)<<uint64(d))])
+(ORshiftRL  x (MOVDconst [c]) [d]) -> (ORconst  x [int64(uint64(c)>>uint64(d))])
+(ORshiftRA  x (MOVDconst [c]) [d]) -> (ORconst  x [int64(int64(c)>>uint64(d))])
+(XORshiftLL x (MOVDconst [c]) [d]) -> (XORconst x [int64(uint64(c)<<uint64(d))])
+(XORshiftRL x (MOVDconst [c]) [d]) -> (XORconst x [int64(uint64(c)>>uint64(d))])
+(XORshiftRA x (MOVDconst [c]) [d]) -> (XORconst x [int64(int64(c)>>uint64(d))])
+(BICshiftLL x (MOVDconst [c]) [d]) -> (BICconst x [int64(uint64(c)<<uint64(d))])
+(BICshiftRL x (MOVDconst [c]) [d]) -> (BICconst x [int64(uint64(c)>>uint64(d))])
+(BICshiftRA x (MOVDconst [c]) [d]) -> (BICconst x [int64(int64(c)>>uint64(d))])
+(CMPshiftLL x (MOVDconst [c]) [d]) -> (CMPconst x [int64(uint64(c)<<uint64(d))])
+(CMPshiftRL x (MOVDconst [c]) [d]) -> (CMPconst x [int64(uint64(c)>>uint64(d))])
+(CMPshiftRA x (MOVDconst [c]) [d]) -> (CMPconst x [int64(int64(c)>>uint64(d))])
+
+// simplification with *shift ops
+(SUBshiftLL x (SLLconst x [c]) [d]) && c==d -> (MOVDconst [0])
+(SUBshiftRL x (SRLconst x [c]) [d]) && c==d -> (MOVDconst [0])
+(SUBshiftRA x (SRAconst x [c]) [d]) && c==d -> (MOVDconst [0])
+(ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d -> y
+(ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d -> y
+(ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d -> y
+(ORshiftLL  x y:(SLLconst x [c]) [d]) && c==d -> y
+(ORshiftRL  x y:(SRLconst x [c]) [d]) && c==d -> y
+(ORshiftRA  x y:(SRAconst x [c]) [d]) && c==d -> y
+(XORshiftLL x (SLLconst x [c]) [d]) && c==d -> (MOVDconst [0])
+(XORshiftRL x (SRLconst x [c]) [d]) && c==d -> (MOVDconst [0])
+(XORshiftRA x (SRAconst x [c]) [d]) && c==d -> (MOVDconst [0])
+(BICshiftLL x (SLLconst x [c]) [d]) && c==d -> (MOVDconst [0])
+(BICshiftRL x (SRLconst x [c]) [d]) && c==d -> (MOVDconst [0])
+(BICshiftRA x (SRAconst x [c]) [d]) && c==d -> (MOVDconst [0])
+
+// do combined loads
+// little endian loads
+// b[0] | b[1]<<8 -> load 16-bit
+(ORshiftLL <t> [8]
+	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem))
+	y1:(MOVDnop x1:(MOVBUload [i+1] {s} p mem)))
+	&& x0.Uses == 1 && x1.Uses == 1
+	&& y0.Uses == 1 && y1.Uses == 1
+	&& mergePoint(b,x0,x1) != nil
+	&& clobber(x0) && clobber(x1)
+	&& clobber(y0) && clobber(y1)
+	-> @mergePoint(b,x0,x1) (MOVHUload <t> {s} (OffPtr <p.Type> [i] p) mem)
+
+// b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24 -> load 32-bit
+(ORshiftLL <t> [24] o0:(ORshiftLL [16]
+	            x0:(MOVHUload [i]   {s} p mem)
+	y1:(MOVDnop x1:(MOVBUload [i+2] {s} p mem)))
+	y2:(MOVDnop x2:(MOVBUload [i+3] {s} p mem)))
+	&& x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1
+	&& y1.Uses == 1 && y2.Uses == 1
+	&& o0.Uses == 1
+	&& mergePoint(b,x0,x1,x2) != nil
+	&& clobber(x0) && clobber(x1) && clobber(x2)
+	&& clobber(y1) && clobber(y2)
+	&& clobber(o0)
+	-> @mergePoint(b,x0,x1,x2) (MOVWUload <t> {s} (OffPtr <p.Type> [i] p) mem)
+
+// b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24 | b[4]<<32 | b[5]<<40 | b[6]<<48 | b[7]<<56 -> load 64-bit
+(ORshiftLL <t> [56] o0:(ORshiftLL [48] o1:(ORshiftLL [40] o2:(ORshiftLL [32]
+	            x0:(MOVWUload [i]   {s} p mem)
+	y1:(MOVDnop x1:(MOVBUload [i+4] {s} p mem)))
+	y2:(MOVDnop x2:(MOVBUload [i+5] {s} p mem)))
+	y3:(MOVDnop x3:(MOVBUload [i+6] {s} p mem)))
+	y4:(MOVDnop x4:(MOVBUload [i+7] {s} p mem)))
+	&& x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1
+	&& y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && y4.Uses == 1
+	&& o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1
+	&& mergePoint(b,x0,x1,x2,x3,x4) != nil
+	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4)
+	&& clobber(y1) && clobber(y2) && clobber(y3) && clobber(y4)
+	&& clobber(o0) && clobber(o1) && clobber(o2)
+	-> @mergePoint(b,x0,x1,x2,x3,x4) (MOVDload <t> {s} (OffPtr <p.Type> [i] p) mem)
+
+// b[3]<<24 | b[2]<<16 | b[1]<<8 | b[0] -> load 32-bit
+(OR <t> o0:(ORshiftLL [8] o1:(ORshiftLL [16] s0:(SLLconst [24]
+	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem)))
+	y1:(MOVDnop x1:(MOVBUload [i-1] {s} p mem)))
+	y2:(MOVDnop x2:(MOVBUload [i-2] {s} p mem)))
+	y3:(MOVDnop x3:(MOVBUload [i-3] {s} p mem)))
+	&& x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1
+	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1
+	&& o0.Uses == 1 && o1.Uses == 1 && s0.Uses == 1
+	&& mergePoint(b,x0,x1,x2,x3) != nil
+	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3)
+	&& clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3)
+	&& clobber(o0) && clobber(o1) && clobber(s0)
+	-> @mergePoint(b,x0,x1,x2,x3) (MOVWUload <t> {s} (OffPtr <p.Type> [i-3] p) mem)
+
+// b[7]<<56 | b[6]<<48 | b[5]<<40 | b[4]<<32 | b[3]<<24 | b[2]<<16 | b[1]<<8 | b[0] -> load 64-bit, reverse
+(OR <t> o0:(ORshiftLL [8] o1:(ORshiftLL [16] o2:(ORshiftLL [24] o3:(ORshiftLL [32] o4:(ORshiftLL [40] o5:(ORshiftLL [48] s0:(SLLconst [56]
+	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem)))
+	y1:(MOVDnop x1:(MOVBUload [i-1] {s} p mem)))
+	y2:(MOVDnop x2:(MOVBUload [i-2] {s} p mem)))
+	y3:(MOVDnop x3:(MOVBUload [i-3] {s} p mem)))
+	y4:(MOVDnop x4:(MOVBUload [i-4] {s} p mem)))
+	y5:(MOVDnop x5:(MOVBUload [i-5] {s} p mem)))
+	y6:(MOVDnop x6:(MOVBUload [i-6] {s} p mem)))
+	y7:(MOVDnop x7:(MOVBUload [i-7] {s} p mem)))
+	&& x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1
+	&& x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1
+	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1
+	&& y4.Uses == 1 && y5.Uses == 1 && y6.Uses == 1 && y7.Uses == 1
+	&& o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1
+	&& o4.Uses == 1 && o5.Uses == 1 && s0.Uses == 1
+	&& mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3)
+	&& clobber(x4) && clobber(x5) && clobber(x6) && clobber(x7)
+	&& clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3)
+	&& clobber(y4) && clobber(y5) && clobber(y6) && clobber(y7)
+	&& clobber(o0) && clobber(o1) && clobber(o2) && clobber(o3)
+	&& clobber(o4) && clobber(o5) && clobber(s0)
+	-> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (REV <t> (MOVDload <t> {s} (OffPtr <p.Type> [i-7] p) mem))
+
+// big endian loads
+// b[1] | b[0]<<8 -> load 16-bit, reverse
+(ORshiftLL <t> [8]
+	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem))
+	y1:(MOVDnop x1:(MOVBUload [i-1] {s} p mem)))
+	&& ((i-1)%2 == 0 || i-1<256 && i-1>-256 && !isArg(s) && !isAuto(s))
+	&& x0.Uses == 1 && x1.Uses == 1
+	&& y0.Uses == 1 && y1.Uses == 1
+	&& mergePoint(b,x0,x1) != nil
+	&& clobber(x0) && clobber(x1)
+	&& clobber(y0) && clobber(y1)
+	-> @mergePoint(b,x0,x1) (REV16W <t> (MOVHUload <t> [i-1] {s} p mem))
+
+// b[3] | b[2]<<8 | b[1]<<16 | b[0]<<24 -> load 32-bit, reverse
+(ORshiftLL <t> [24] o0:(ORshiftLL [16]
+	y0:(REV16W  x0:(MOVHUload [i]   {s} p mem))
+	y1:(MOVDnop x1:(MOVBUload [i-1] {s} p mem)))
+	y2:(MOVDnop x2:(MOVBUload [i-2] {s} p mem)))
+	&& x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1
+	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1
+	&& o0.Uses == 1
+	&& mergePoint(b,x0,x1,x2) != nil
+	&& clobber(x0) && clobber(x1) && clobber(x2)
+	&& clobber(y0) && clobber(y1) && clobber(y2)
+	&& clobber(o0)
+	-> @mergePoint(b,x0,x1,x2) (REVW <t> (MOVWUload <t> {s} (OffPtr <p.Type> [i-2] p) mem))
+
+// b[7] | b[6]<<8 | b[5]<<16 | b[4]<<24 | b[3]<<32 | b[2]<<40 | b[1]<<48 | b[0]<<56 -> load 64-bit, reverse
+(ORshiftLL <t> [56] o0:(ORshiftLL [48] o1:(ORshiftLL [40] o2:(ORshiftLL [32]
+	y0:(REVW    x0:(MOVWUload [i]   {s} p mem))
+	y1:(MOVDnop x1:(MOVBUload [i-1] {s} p mem)))
+	y2:(MOVDnop x2:(MOVBUload [i-2] {s} p mem)))
+	y3:(MOVDnop x3:(MOVBUload [i-3] {s} p mem)))
+	y4:(MOVDnop x4:(MOVBUload [i-4] {s} p mem)))
+	&& x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1
+	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && y4.Uses == 1
+	&& o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1
+	&& mergePoint(b,x0,x1,x2,x3,x4) != nil
+	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4)
+	&& clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3) && clobber(y4)
+	&& clobber(o0) && clobber(o1) && clobber(o2)
+	-> @mergePoint(b,x0,x1,x2,x3,x4) (REV <t> (MOVDload <t> {s} (OffPtr <p.Type> [i-4] p) mem))
+
+// b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3] -> load 32-bit, reverse
+(OR <t> o0:(ORshiftLL [8] o1:(ORshiftLL [16] s0:(SLLconst [24]
+	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem)))
+	y1:(MOVDnop x1:(MOVBUload [i+1] {s} p mem)))
+	y2:(MOVDnop x2:(MOVBUload [i+2] {s} p mem)))
+	y3:(MOVDnop x3:(MOVBUload [i+3] {s} p mem)))
+	&& x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1
+	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1
+	&& o0.Uses == 1 && o1.Uses == 1 && s0.Uses == 1
+	&& mergePoint(b,x0,x1,x2,x3) != nil
+	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3)
+	&& clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3)
+	&& clobber(o0) && clobber(o1) && clobber(s0)
+	-> @mergePoint(b,x0,x1,x2,x3) (REVW <t> (MOVWUload <t> {s} (OffPtr <p.Type> [i] p) mem))
+
+// b[0]<<56 | b[1]<<48 | b[2]<<40 | b[3]<<32 | b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7] -> load 64-bit, reverse
+(OR <t> o0:(ORshiftLL [8] o1:(ORshiftLL [16] o2:(ORshiftLL [24] o3:(ORshiftLL [32] o4:(ORshiftLL [40] o5:(ORshiftLL [48] s0:(SLLconst [56]
+	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem)))
+	y1:(MOVDnop x1:(MOVBUload [i+1] {s} p mem)))
+	y2:(MOVDnop x2:(MOVBUload [i+2] {s} p mem)))
+	y3:(MOVDnop x3:(MOVBUload [i+3] {s} p mem)))
+	y4:(MOVDnop x4:(MOVBUload [i+4] {s} p mem)))
+	y5:(MOVDnop x5:(MOVBUload [i+5] {s} p mem)))
+	y6:(MOVDnop x6:(MOVBUload [i+6] {s} p mem)))
+	y7:(MOVDnop x7:(MOVBUload [i+7] {s} p mem)))
+	&& x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1
+	&& x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1
+	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1
+	&& y4.Uses == 1 && y5.Uses == 1 && y6.Uses == 1 && y7.Uses == 1
+	&& o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1
+	&& o4.Uses == 1 && o5.Uses == 1 && s0.Uses == 1
+	&& mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3)
+	&& clobber(x4) && clobber(x5) && clobber(x6) && clobber(x7)
+	&& clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3)
+	&& clobber(y4) && clobber(y5) && clobber(y6) && clobber(y7)
+	&& clobber(o0) && clobber(o1) && clobber(o2) && clobber(o3)
+	&& clobber(o4) && clobber(o5) && clobber(s0)
+	-> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (REV <t> (MOVDload <t> {s} (OffPtr <p.Type> [i] p) mem))
diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
new file mode 100644
index 0000000..dce61e3
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
@@ -0,0 +1,535 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "strings"
+
+// Notes:
+//  - Integer types live in the low portion of registers. Upper portions are junk.
+//  - Boolean types use the low-order byte of a register. 0=false, 1=true.
+//    Upper bytes are junk.
+//  - *const instructions may use a constant larger than the instuction can encode.
+//    In this case the assembler expands to multiple instructions and uses tmp
+//    register (R27).
+
+// Suffixes encode the bit width of various instructions.
+// D (double word) = 64 bit
+// W (word)        = 32 bit
+// H (half word)   = 16 bit
+// HU              = 16 bit unsigned
+// B (byte)        = 8 bit
+// BU              = 8 bit unsigned
+// S (single)      = 32 bit float
+// D (double)      = 64 bit float
+
+// Note: registers not used in regalloc are not included in this list,
+// so that regmask stays within int64
+// Be careful when hand coding regmasks.
+var regNamesARM64 = []string{
+	"R0",
+	"R1",
+	"R2",
+	"R3",
+	"R4",
+	"R5",
+	"R6",
+	"R7",
+	"R8",
+	"R9",
+	"R10",
+	"R11",
+	"R12",
+	"R13",
+	"R14",
+	"R15",
+	"R16",
+	"R17",
+	"R18", // platform register, not used
+	"R19",
+	"R20",
+	"R21",
+	"R22",
+	"R23",
+	"R24",
+	"R25",
+	"R26",
+	// R27 = REGTMP not used in regalloc
+	"g",   // aka R28
+	"R29", // frame pointer, not used
+	"R30", // aka REGLINK
+	"SP",  // aka R31
+
+	"F0",
+	"F1",
+	"F2",
+	"F3",
+	"F4",
+	"F5",
+	"F6",
+	"F7",
+	"F8",
+	"F9",
+	"F10",
+	"F11",
+	"F12",
+	"F13",
+	"F14",
+	"F15",
+	"F16",
+	"F17",
+	"F18",
+	"F19",
+	"F20",
+	"F21",
+	"F22",
+	"F23",
+	"F24",
+	"F25",
+	"F26",
+	"F27",
+	"F28",
+	"F29",
+	"F30",
+	"F31",
+
+	// pseudo-registers
+	"SB",
+}
+
+func init() {
+	// Make map from reg names to reg integers.
+	if len(regNamesARM64) > 64 {
+		panic("too many registers")
+	}
+	num := map[string]int{}
+	for i, name := range regNamesARM64 {
+		num[name] = i
+	}
+	buildReg := func(s string) regMask {
+		m := regMask(0)
+		for _, r := range strings.Split(s, " ") {
+			if n, ok := num[r]; ok {
+				m |= regMask(1) << uint(n)
+				continue
+			}
+			panic("register " + r + " not found")
+		}
+		return m
+	}
+
+	// Common individual register masks
+	var (
+		gp         = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30")
+		gpg        = gp | buildReg("g")
+		gpsp       = gp | buildReg("SP")
+		gpspg      = gpg | buildReg("SP")
+		gpspsbg    = gpspg | buildReg("SB")
+		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31")
+		callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
+	)
+	// Common regInfo
+	var (
+		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
+		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
+		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
+		gp1flags  = regInfo{inputs: []regMask{gpg}}
+		gp1flags1 = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
+		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
+		gp2flags  = regInfo{inputs: []regMask{gpg, gpg}}
+		gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
+		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
+		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
+		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
+		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
+		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
+		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
+		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
+		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
+		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
+		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
+		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
+		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
+		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
+		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
+	)
+	ops := []opData{
+		// binary ops
+		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true},     // arg0 + arg1
+		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int64"},   // arg0 + auxInt
+		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB"},                        // arg0 - arg1
+		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int64"},     // arg0 - auxInt
+		{name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true},     // arg0 * arg1
+		{name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true},   // arg0 * arg1, 32-bit
+		{name: "MULH", argLength: 2, reg: gp21, asm: "SMULH", commutative: true},  // (arg0 * arg1) >> 64, signed
+		{name: "UMULH", argLength: 2, reg: gp21, asm: "UMULH", commutative: true}, // (arg0 * arg1) >> 64, unsigned
+		{name: "MULL", argLength: 2, reg: gp21, asm: "SMULL", commutative: true},  // arg0 * arg1, signed, 32-bit mult results in 64-bit
+		{name: "UMULL", argLength: 2, reg: gp21, asm: "UMULL", commutative: true}, // arg0 * arg1, unsigned, 32-bit mult results in 64-bit
+		{name: "DIV", argLength: 2, reg: gp21, asm: "SDIV"},                       // arg0 / arg1, signed
+		{name: "UDIV", argLength: 2, reg: gp21, asm: "UDIV"},                      // arg0 / arg1, unsighed
+		{name: "DIVW", argLength: 2, reg: gp21, asm: "SDIVW"},                     // arg0 / arg1, signed, 32 bit
+		{name: "UDIVW", argLength: 2, reg: gp21, asm: "UDIVW"},                    // arg0 / arg1, unsighed, 32 bit
+		{name: "MOD", argLength: 2, reg: gp21, asm: "REM"},                        // arg0 % arg1, signed
+		{name: "UMOD", argLength: 2, reg: gp21, asm: "UREM"},                      // arg0 % arg1, unsigned
+		{name: "MODW", argLength: 2, reg: gp21, asm: "REMW"},                      // arg0 % arg1, signed, 32 bit
+		{name: "UMODW", argLength: 2, reg: gp21, asm: "UREMW"},                    // arg0 % arg1, unsigned, 32 bit
+
+		{name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0 + arg1
+		{name: "FADDD", argLength: 2, reg: fp21, asm: "FADDD", commutative: true}, // arg0 + arg1
+		{name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"},                    // arg0 - arg1
+		{name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD"},                    // arg0 - arg1
+		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true}, // arg0 * arg1
+		{name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true}, // arg0 * arg1
+		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS"},                    // arg0 / arg1
+		{name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD"},                    // arg0 / arg1
+
+		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1
+		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"}, // arg0 & auxInt
+		{name: "OR", argLength: 2, reg: gp21, asm: "ORR", commutative: true},  // arg0 | arg1
+		{name: "ORconst", argLength: 1, reg: gp11, asm: "ORR", aux: "Int64"},  // arg0 | auxInt
+		{name: "XOR", argLength: 2, reg: gp21, asm: "EOR", commutative: true}, // arg0 ^ arg1
+		{name: "XORconst", argLength: 1, reg: gp11, asm: "EOR", aux: "Int64"}, // arg0 ^ auxInt
+		{name: "BIC", argLength: 2, reg: gp21, asm: "BIC"},                    // arg0 &^ arg1
+		{name: "BICconst", argLength: 1, reg: gp11, asm: "BIC", aux: "Int64"}, // arg0 &^ auxInt
+
+		// unary ops
+		{name: "MVN", argLength: 1, reg: gp11, asm: "MVN"},       // ^arg0
+		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG"},       // -arg0
+		{name: "FNEGS", argLength: 1, reg: fp11, asm: "FNEGS"},   // -arg0, float32
+		{name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD"},   // -arg0, float64
+		{name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD"}, // sqrt(arg0), float64
+		{name: "REV", argLength: 1, reg: gp11, asm: "REV"},       // byte reverse, 64-bit
+		{name: "REVW", argLength: 1, reg: gp11, asm: "REVW"},     // byte reverse, 32-bit
+		{name: "REV16W", argLength: 1, reg: gp11, asm: "REV16W"}, // byte reverse in each 16-bit halfword, 32-bit
+		{name: "RBIT", argLength: 1, reg: gp11, asm: "RBIT"},     // bit reverse, 64-bit
+		{name: "RBITW", argLength: 1, reg: gp11, asm: "RBITW"},   // bit reverse, 32-bit
+		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},       // count leading zero, 64-bit
+		{name: "CLZW", argLength: 1, reg: gp11, asm: "CLZW"},     // count leading zero, 32-bit
+
+		// shifts
+		{name: "SLL", argLength: 2, reg: gp21, asm: "LSL"},                      // arg0 << arg1, shift amount is mod 64
+		{name: "SLLconst", argLength: 1, reg: gp11, asm: "LSL", aux: "Int64"},   // arg0 << auxInt
+		{name: "SRL", argLength: 2, reg: gp21, asm: "LSR"},                      // arg0 >> arg1, unsigned, shift amount is mod 64
+		{name: "SRLconst", argLength: 1, reg: gp11, asm: "LSR", aux: "Int64"},   // arg0 >> auxInt, unsigned
+		{name: "SRA", argLength: 2, reg: gp21, asm: "ASR"},                      // arg0 >> arg1, signed, shift amount is mod 64
+		{name: "SRAconst", argLength: 1, reg: gp11, asm: "ASR", aux: "Int64"},   // arg0 >> auxInt, signed
+		{name: "RORconst", argLength: 1, reg: gp11, asm: "ROR", aux: "Int64"},   // arg0 right rotate by auxInt bits
+		{name: "RORWconst", argLength: 1, reg: gp11, asm: "RORW", aux: "Int64"}, // uint32(arg0) right rotate by auxInt bits
+
+		// comparisons
+		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},                      // arg0 compare to arg1
+		{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int64", typ: "Flags"},   // arg0 compare to auxInt
+		{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"},                    // arg0 compare to arg1, 32 bit
+		{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt, 32 bit
+		{name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags"},                      // arg0 compare to -arg1
+		{name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int64", typ: "Flags"},   // arg0 compare to -auxInt
+		{name: "CMNW", argLength: 2, reg: gp2flags, asm: "CMNW", typ: "Flags"},                    // arg0 compare to -arg1, 32 bit
+		{name: "CMNWconst", argLength: 1, reg: gp1flags, asm: "CMNW", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt, 32 bit
+		{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "FCMPS", typ: "Flags"},                  // arg0 compare to arg1, float32
+		{name: "FCMPD", argLength: 2, reg: fp2flags, asm: "FCMPD", typ: "Flags"},                  // arg0 compare to arg1, float64
+
+		// shifted ops
+		{name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1<<auxInt
+		{name: "ADDshiftRL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1>>auxInt, unsigned shift
+		{name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int64"},                   // arg0 + arg1>>auxInt, signed shift
+		{name: "SUBshiftLL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1<<auxInt
+		{name: "SUBshiftRL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1>>auxInt, unsigned shift
+		{name: "SUBshiftRA", argLength: 2, reg: gp21, asm: "SUB", aux: "Int64"},                   // arg0 - arg1>>auxInt, signed shift
+		{name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1<<auxInt)
+		{name: "ANDshiftRL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1>>auxInt), unsigned shift
+		{name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"},                   // arg0 & (arg1>>auxInt), signed shift
+		{name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1<<auxInt
+		{name: "ORshiftRL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1>>auxInt, unsigned shift
+		{name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"},                    // arg0 | arg1>>auxInt, signed shift
+		{name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1<<auxInt
+		{name: "XORshiftRL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1>>auxInt, unsigned shift
+		{name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"},                   // arg0 ^ arg1>>auxInt, signed shift
+		{name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1<<auxInt)
+		{name: "BICshiftRL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1>>auxInt), unsigned shift
+		{name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"},                   // arg0 &^ (arg1>>auxInt), signed shift
+		{name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1<<auxInt
+		{name: "CMPshiftRL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, unsigned shift
+		{name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift
+
+		// moves
+		{name: "MOVDconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVD", typ: "UInt64", rematerializeable: true},      // 32 low bits of auxint
+		{name: "FMOVSconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVS", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
+		{name: "FMOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
+
+		{name: "MOVDaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVD", rematerializeable: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
+
+		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true},      // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true},   // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
+
+		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true},   // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true},   // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true},   // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVDstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true},   // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "FMOVSstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVS", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "FMOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVD", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+
+		{name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true}, // store 1 byte of zero to arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true}, // store 2 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVDstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of zero to arg0 + auxInt + aux.  ar12=mem.
+
+		// conversions
+		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
+		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
+		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
+		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
+		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
+		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
+		{name: "MOVDreg", argLength: 1, reg: gp11, asm: "MOVD"},   // move from arg0
+
+		{name: "MOVDnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
+
+		{name: "SCVTFWS", argLength: 1, reg: gpfp, asm: "SCVTFWS"},   // int32 -> float32
+		{name: "SCVTFWD", argLength: 1, reg: gpfp, asm: "SCVTFWD"},   // int32 -> float64
+		{name: "UCVTFWS", argLength: 1, reg: gpfp, asm: "UCVTFWS"},   // uint32 -> float32
+		{name: "UCVTFWD", argLength: 1, reg: gpfp, asm: "UCVTFWD"},   // uint32 -> float64
+		{name: "SCVTFS", argLength: 1, reg: gpfp, asm: "SCVTFS"},     // int64 -> float32
+		{name: "SCVTFD", argLength: 1, reg: gpfp, asm: "SCVTFD"},     // int64 -> float64
+		{name: "UCVTFS", argLength: 1, reg: gpfp, asm: "UCVTFS"},     // uint64 -> float32
+		{name: "UCVTFD", argLength: 1, reg: gpfp, asm: "UCVTFD"},     // uint64 -> float64
+		{name: "FCVTZSSW", argLength: 1, reg: fpgp, asm: "FCVTZSSW"}, // float32 -> int32
+		{name: "FCVTZSDW", argLength: 1, reg: fpgp, asm: "FCVTZSDW"}, // float64 -> int32
+		{name: "FCVTZUSW", argLength: 1, reg: fpgp, asm: "FCVTZUSW"}, // float32 -> uint32
+		{name: "FCVTZUDW", argLength: 1, reg: fpgp, asm: "FCVTZUDW"}, // float64 -> uint32
+		{name: "FCVTZSS", argLength: 1, reg: fpgp, asm: "FCVTZSS"},   // float32 -> int64
+		{name: "FCVTZSD", argLength: 1, reg: fpgp, asm: "FCVTZSD"},   // float64 -> int64
+		{name: "FCVTZUS", argLength: 1, reg: fpgp, asm: "FCVTZUS"},   // float32 -> uint64
+		{name: "FCVTZUD", argLength: 1, reg: fpgp, asm: "FCVTZUD"},   // float64 -> uint64
+		{name: "FCVTSD", argLength: 1, reg: fp11, asm: "FCVTSD"},     // float32 -> float64
+		{name: "FCVTDS", argLength: 1, reg: fp11, asm: "FCVTDS"},     // float64 -> float32
+
+		// conditional instructions
+		{name: "CSELULT", argLength: 3, reg: gp2flags1, asm: "CSEL"},  // returns arg0 if flags indicates unsigned LT, arg1 otherwise, arg2=flags
+		{name: "CSELULT0", argLength: 2, reg: gp1flags1, asm: "CSEL"}, // returns arg0 if flags indicates unsigned LT, 0 otherwise, arg1=flags
+
+		// function calls
+		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true},                                              // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
+		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                // call deferproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                   // call newproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
+
+		// pseudo-ops
+		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
+
+		{name: "Equal", argLength: 1, reg: readflags},         // bool, true flags encode x==y false otherwise.
+		{name: "NotEqual", argLength: 1, reg: readflags},      // bool, true flags encode x!=y false otherwise.
+		{name: "LessThan", argLength: 1, reg: readflags},      // bool, true flags encode signed x<y false otherwise.
+		{name: "LessEqual", argLength: 1, reg: readflags},     // bool, true flags encode signed x<=y false otherwise.
+		{name: "GreaterThan", argLength: 1, reg: readflags},   // bool, true flags encode signed x>y false otherwise.
+		{name: "GreaterEqual", argLength: 1, reg: readflags},  // bool, true flags encode signed x>=y false otherwise.
+		{name: "LessThanU", argLength: 1, reg: readflags},     // bool, true flags encode unsigned x<y false otherwise.
+		{name: "LessEqualU", argLength: 1, reg: readflags},    // bool, true flags encode unsigned x<=y false otherwise.
+		{name: "GreaterThanU", argLength: 1, reg: readflags},  // bool, true flags encode unsigned x>y false otherwise.
+		{name: "GreaterEqualU", argLength: 1, reg: readflags}, // bool, true flags encode unsigned x>=y false otherwise.
+
+		// duffzero
+		// arg0 = address of memory to zero
+		// arg1 = mem
+		// auxint = offset into duffzero code to start executing
+		// returns mem
+		// R16 aka arm64.REGRT1 changed as side effect
+		{
+			name:      "DUFFZERO",
+			aux:       "Int64",
+			argLength: 2,
+			reg: regInfo{
+				inputs:   []regMask{gp},
+				clobbers: buildReg("R16 R30"),
+			},
+			faultOnNilArg0: true,
+		},
+
+		// large zeroing
+		// arg0 = address of memory to zero (in R16 aka arm64.REGRT1, changed as side effect)
+		// arg1 = address of the last element to zero
+		// arg2 = mem
+		// returns mem
+		//	MOVD.P	ZR, 8(R16)
+		//	CMP	Rarg1, R16
+		//	BLE	-2(PC)
+		// Note: the-end-of-the-memory may be not a valid pointer. it's a problem if it is spilled.
+		// the-end-of-the-memory - 8 is with the area to zero, ok to spill.
+		{
+			name:      "LoweredZero",
+			argLength: 3,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R16"), gp},
+				clobbers: buildReg("R16"),
+			},
+			clobberFlags:   true,
+			faultOnNilArg0: true,
+		},
+
+		// duffcopy
+		// arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
+		// arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
+		// arg2 = mem
+		// auxint = offset into duffcopy code to start executing
+		// returns mem
+		// R16, R17 changed as side effect
+		{
+			name:      "DUFFCOPY",
+			aux:       "Int64",
+			argLength: 3,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R17"), buildReg("R16")},
+				clobbers: buildReg("R16 R17 R30"),
+			},
+			faultOnNilArg0: true,
+			faultOnNilArg1: true,
+		},
+
+		// large move
+		// arg0 = address of dst memory (in R17 aka arm64.REGRT2, changed as side effect)
+		// arg1 = address of src memory (in R16 aka arm64.REGRT1, changed as side effect)
+		// arg2 = address of the last element of src
+		// arg3 = mem
+		// returns mem
+		//	MOVD.P	8(R16), Rtmp
+		//	MOVD.P	Rtmp, 8(R17)
+		//	CMP	Rarg2, R16
+		//	BLE	-3(PC)
+		// Note: the-end-of-src may be not a valid pointer. it's a problem if it is spilled.
+		// the-end-of-src - 8 is within the area to copy, ok to spill.
+		{
+			name:      "LoweredMove",
+			argLength: 4,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R17"), buildReg("R16"), gp},
+				clobbers: buildReg("R16 R17"),
+			},
+			clobberFlags:   true,
+			faultOnNilArg0: true,
+			faultOnNilArg1: true,
+		},
+
+		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
+		// and sorts it to the very beginning of the block to prevent other
+		// use of R26 (arm64.REGCTXT, the closure pointer)
+		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R26")}}},
+
+		// MOVDconvert converts between pointers and integers.
+		// We have a special op for this so as to not confuse GC
+		// (particularly stack maps).  It takes a memory arg so it
+		// gets correctly ordered with respect to GC safepoints.
+		// arg0=ptr/int arg1=mem, output=int/ptr
+		{name: "MOVDconvert", argLength: 2, reg: gp11, asm: "MOVD"},
+
+		// Constant flag values. For any comparison, there are 5 possible
+		// outcomes: the three from the signed total order (<,==,>) and the
+		// three from the unsigned total order. The == cases overlap.
+		// Note: there's a sixth "unordered" outcome for floating-point
+		// comparisons, but we don't use such a beast yet.
+		// These ops are for temporary use by rewrite rules. They
+		// cannot appear in the generated assembly.
+		{name: "FlagEQ"},     // equal
+		{name: "FlagLT_ULT"}, // signed < and unsigned <
+		{name: "FlagLT_UGT"}, // signed < and unsigned >
+		{name: "FlagGT_UGT"}, // signed > and unsigned <
+		{name: "FlagGT_ULT"}, // signed > and unsigned >
+
+		// (InvertFlags (CMP a b)) == (CMP b a)
+		// InvertFlags is a pseudo-op which can't appear in assembly output.
+		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
+
+		// atomic loads.
+		// load from arg0. arg1=mem. auxint must be zero.
+		// returns <value,memory> so they can be properly ordered with other loads.
+		{name: "LDAR", argLength: 2, reg: gpload, asm: "LDAR", faultOnNilArg0: true},
+		{name: "LDARW", argLength: 2, reg: gpload, asm: "LDARW", faultOnNilArg0: true},
+
+		// atomic stores.
+		// store arg1 to arg0. arg2=mem. returns memory. auxint must be zero.
+		{name: "STLR", argLength: 3, reg: gpstore, asm: "STLR", faultOnNilArg0: true},
+		{name: "STLRW", argLength: 3, reg: gpstore, asm: "STLRW", faultOnNilArg0: true},
+
+		// atomic exchange.
+		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>. auxint must be zero.
+		// LDAXR	(Rarg0), Rout
+		// STLXR	Rarg1, (Rarg0), Rtmp
+		// CBNZ		Rtmp, -2(PC)
+		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
+		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
+
+		// atomic add.
+		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>. auxint must be zero.
+		// LDAXR	(Rarg0), Rout
+		// ADD		Rarg1, Rout
+		// STLXR	Rout, (Rarg0), Rtmp
+		// CBNZ		Rtmp, -3(PC)
+		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
+		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
+
+		// atomic compare and swap.
+		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
+		// if *arg0 == arg1 {
+		//   *arg0 = arg2
+		//   return (true, memory)
+		// } else {
+		//   return (false, memory)
+		// }
+		// LDAXR	(Rarg0), Rtmp
+		// CMP		Rarg1, Rtmp
+		// BNE		3(PC)
+		// STLXR	Rarg2, (Rarg0), Rtmp
+		// CBNZ		Rtmp, -4(PC)
+		// CSET		EQ, Rout
+		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true},
+		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true},
+
+		// atomic and/or.
+		// *arg0 &= (|=) arg1. arg2=mem. returns memory. auxint must be zero.
+		// LDAXRB	(Rarg0), Rtmp
+		// AND/OR	Rarg1, Rtmp
+		// STLXRB	Rtmp, (Rarg0), Rtmp
+		// CBNZ		Rtmp, -3(PC)
+		{name: "LoweredAtomicAnd8", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true},
+		{name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "ORR", faultOnNilArg0: true},
+	}
+
+	blocks := []blockData{
+		{name: "EQ"},
+		{name: "NE"},
+		{name: "LT"},
+		{name: "LE"},
+		{name: "GT"},
+		{name: "GE"},
+		{name: "ULT"},
+		{name: "ULE"},
+		{name: "UGT"},
+		{name: "UGE"},
+		{name: "Z"},   // Control == 0 (take a register instead of flags)
+		{name: "NZ"},  // Control != 0
+		{name: "ZW"},  // Control == 0, 32-bit
+		{name: "NZW"}, // Control != 0, 32-bit
+	}
+
+	archs = append(archs, arch{
+		name:            "ARM64",
+		pkg:             "cmd/internal/obj/arm64",
+		genfile:         "../../arm64/ssa.go",
+		ops:             ops,
+		blocks:          blocks,
+		regnames:        regNamesARM64,
+		gpregmask:       gp,
+		fpregmask:       fp,
+		framepointerreg: -1, // not used
+		linkreg:         int8(num["R30"]),
+	})
+}
diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go
index 23e8f63..5bf3c00 100644
--- a/src/cmd/compile/internal/ssa/gen/ARMOps.go
+++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go
@@ -6,32 +6,501 @@
 
 package main
 
+import "strings"
+
+// Notes:
+//  - Integer types live in the low portion of registers. Upper portions are junk.
+//  - Boolean types use the low-order byte of a register. 0=false, 1=true.
+//    Upper bytes are junk.
+//  - *const instructions may use a constant larger than the instuction can encode.
+//    In this case the assembler expands to multiple instructions and uses tmp
+//    register (R11).
+
+// Suffixes encode the bit width of various instructions.
+// W (word)      = 32 bit
+// H (half word) = 16 bit
+// HU            = 16 bit unsigned
+// B (byte)      = 8 bit
+// BU            = 8 bit unsigned
+// F (float)     = 32 bit float
+// D (double)    = 64 bit float
+
+var regNamesARM = []string{
+	"R0",
+	"R1",
+	"R2",
+	"R3",
+	"R4",
+	"R5",
+	"R6",
+	"R7",
+	"R8",
+	"R9",
+	"g",   // aka R10
+	"R11", // tmp
+	"R12",
+	"SP",  // aka R13
+	"R14", // link
+	"R15", // pc
+
+	"F0",
+	"F1",
+	"F2",
+	"F3",
+	"F4",
+	"F5",
+	"F6",
+	"F7",
+	"F8",
+	"F9",
+	"F10",
+	"F11",
+	"F12",
+	"F13",
+	"F14",
+	"F15", // tmp
+
+	// pseudo-registers
+	"SB",
+}
+
 func init() {
+	// Make map from reg names to reg integers.
+	if len(regNamesARM) > 64 {
+		panic("too many registers")
+	}
+	num := map[string]int{}
+	for i, name := range regNamesARM {
+		num[name] = i
+	}
+	buildReg := func(s string) regMask {
+		m := regMask(0)
+		for _, r := range strings.Split(s, " ") {
+			if n, ok := num[r]; ok {
+				m |= regMask(1) << uint(n)
+				continue
+			}
+			panic("register " + r + " not found")
+		}
+		return m
+	}
+
+	// Common individual register masks
+	var (
+		gp         = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14")
+		gpg        = gp | buildReg("g")
+		gpsp       = gp | buildReg("SP")
+		gpspg      = gpg | buildReg("SP")
+		gpspsbg    = gpspg | buildReg("SB")
+		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15")
+		callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
+	)
+	// Common regInfo
 	var (
-		gp01       = regInfo{inputs: []regMask{}, outputs: []regMask{31}}
-		gp11       = regInfo{inputs: []regMask{31}, outputs: []regMask{31}}
-		gp21       = regInfo{inputs: []regMask{31, 31}, outputs: []regMask{31}}
-		gp2flags   = regInfo{inputs: []regMask{31, 31}, outputs: []regMask{32}}
-		gpload     = regInfo{inputs: []regMask{31}, outputs: []regMask{31}}
-		gpstore    = regInfo{inputs: []regMask{31, 31}, outputs: []regMask{}}
-		flagsgp    = regInfo{inputs: []regMask{32}, outputs: []regMask{31}}
-		callerSave = regMask(15)
+		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
+		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
+		gp11carry = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp, 0}}
+		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
+		gp1flags  = regInfo{inputs: []regMask{gpg}}
+		gp1flags1 = regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}
+		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
+		gp21carry = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp, 0}}
+		gp2flags  = regInfo{inputs: []regMask{gpg, gpg}}
+		gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
+		gp22      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp, gp}}
+		gp31      = regInfo{inputs: []regMask{gp, gp, gp}, outputs: []regMask{gp}}
+		gp31carry = regInfo{inputs: []regMask{gp, gp, gp}, outputs: []regMask{gp, 0}}
+		gp3flags  = regInfo{inputs: []regMask{gp, gp, gp}}
+		gp3flags1 = regInfo{inputs: []regMask{gp, gp, gp}, outputs: []regMask{gp}}
+		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
+		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
+		gp2load   = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
+		gp2store  = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}}
+		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
+		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
+		fp1flags  = regInfo{inputs: []regMask{fp}}
+		fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
+		gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
+		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
+		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
+		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
+		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
+		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
 	)
 	ops := []opData{
-		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true},  // arg0 + arg1
-		{name: "ADDconst", argLength: 1, reg: gp11, asm: "ADD", aux: "SymOff"}, // arg0 + auxInt + aux.(*gc.Sym)
+		// binary ops
+		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true},     // arg0 + arg1
+		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int32"},   // arg0 + auxInt
+		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB"},                        // arg0 - arg1
+		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int32"},     // arg0 - auxInt
+		{name: "RSB", argLength: 2, reg: gp21, asm: "RSB"},                        // arg1 - arg0
+		{name: "RSBconst", argLength: 1, reg: gp11, asm: "RSB", aux: "Int32"},     // auxInt - arg0
+		{name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true},     // arg0 * arg1
+		{name: "HMUL", argLength: 2, reg: gp21, asm: "MULL", commutative: true},   // (arg0 * arg1) >> 32, signed
+		{name: "HMULU", argLength: 2, reg: gp21, asm: "MULLU", commutative: true}, // (arg0 * arg1) >> 32, unsigned
+
+		// udiv runtime call for soft division
+		// output0 = arg0/arg1, output1 = arg0%arg1
+		// see ../../../../../runtime/vlop_arm.s
+		{
+			name:      "UDIVrtcall",
+			argLength: 2,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R1"), buildReg("R0")},
+				outputs:  []regMask{buildReg("R0"), buildReg("R1")},
+				clobbers: buildReg("R2 R3 R14"), // also clobbers R12 on NaCl (modified in ../config.go)
+			},
+			clobberFlags: true,
+			typ:          "(UInt32,UInt32)",
+		},
+
+		{name: "ADDS", argLength: 2, reg: gp21carry, asm: "ADD", commutative: true}, // arg0 + arg1, set carry flag
+		{name: "ADDSconst", argLength: 1, reg: gp11carry, asm: "ADD", aux: "Int32"}, // arg0 + auxInt, set carry flag
+		{name: "ADC", argLength: 3, reg: gp2flags1, asm: "ADC", commutative: true},  // arg0 + arg1 + carry, arg2=flags
+		{name: "ADCconst", argLength: 2, reg: gp1flags1, asm: "ADC", aux: "Int32"},  // arg0 + auxInt + carry, arg1=flags
+		{name: "SUBS", argLength: 2, reg: gp21carry, asm: "SUB"},                    // arg0 - arg1, set carry flag
+		{name: "SUBSconst", argLength: 1, reg: gp11carry, asm: "SUB", aux: "Int32"}, // arg0 - auxInt, set carry flag
+		{name: "RSBSconst", argLength: 1, reg: gp11carry, asm: "RSB", aux: "Int32"}, // auxInt - arg0, set carry flag
+		{name: "SBC", argLength: 3, reg: gp2flags1, asm: "SBC"},                     // arg0 - arg1 - carry, arg2=flags
+		{name: "SBCconst", argLength: 2, reg: gp1flags1, asm: "SBC", aux: "Int32"},  // arg0 - auxInt - carry, arg1=flags
+		{name: "RSCconst", argLength: 2, reg: gp1flags1, asm: "RSC", aux: "Int32"},  // auxInt - arg0 - carry, arg1=flags
+
+		{name: "MULLU", argLength: 2, reg: gp22, asm: "MULLU", commutative: true}, // arg0 * arg1, high 32 bits in out0, low 32 bits in out1
+		{name: "MULA", argLength: 3, reg: gp31, asm: "MULA"},                      // arg0 * arg1 + arg2
+
+		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
+		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
+		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
+		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
+		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
+		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
+		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
+		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
+
+		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1
+		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32"}, // arg0 & auxInt
+		{name: "OR", argLength: 2, reg: gp21, asm: "ORR", commutative: true},  // arg0 | arg1
+		{name: "ORconst", argLength: 1, reg: gp11, asm: "ORR", aux: "Int32"},  // arg0 | auxInt
+		{name: "XOR", argLength: 2, reg: gp21, asm: "EOR", commutative: true}, // arg0 ^ arg1
+		{name: "XORconst", argLength: 1, reg: gp11, asm: "EOR", aux: "Int32"}, // arg0 ^ auxInt
+		{name: "BIC", argLength: 2, reg: gp21, asm: "BIC"},                    // arg0 &^ arg1
+		{name: "BICconst", argLength: 1, reg: gp11, asm: "BIC", aux: "Int32"}, // arg0 &^ auxInt
+
+		// unary ops
+		{name: "MVN", argLength: 1, reg: gp11, asm: "MVN"}, // ^arg0
+
+		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
+		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
+		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
+
+		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"}, // count leading zero
+
+		// shifts
+		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"},                    // arg0 << arg1, shift amount is mod 256
+		{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt
+		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"},                    // arg0 >> arg1, unsigned, shift amount is mod 256
+		{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned
+		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"},                    // arg0 >> arg1, signed, shift amount is mod 256
+		{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed
+		{name: "SRRconst", argLength: 1, reg: gp11, aux: "Int32"},             // arg0 right rotate by auxInt bits
+
+		{name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1<<auxInt
+		{name: "ADDshiftRL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1>>auxInt, unsigned shift
+		{name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1>>auxInt, signed shift
+		{name: "SUBshiftLL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int32"}, // arg0 - arg1<<auxInt
+		{name: "SUBshiftRL", argLength: 2, reg: gp21, asm: "SUB", aux: "Int32"}, // arg0 - arg1>>auxInt, unsigned shift
+		{name: "SUBshiftRA", argLength: 2, reg: gp21, asm: "SUB", aux: "Int32"}, // arg0 - arg1>>auxInt, signed shift
+		{name: "RSBshiftLL", argLength: 2, reg: gp21, asm: "RSB", aux: "Int32"}, // arg1<<auxInt - arg0
+		{name: "RSBshiftRL", argLength: 2, reg: gp21, asm: "RSB", aux: "Int32"}, // arg1>>auxInt - arg0, unsigned shift
+		{name: "RSBshiftRA", argLength: 2, reg: gp21, asm: "RSB", aux: "Int32"}, // arg1>>auxInt - arg0, signed shift
+		{name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int32"}, // arg0 & (arg1<<auxInt)
+		{name: "ANDshiftRL", argLength: 2, reg: gp21, asm: "AND", aux: "Int32"}, // arg0 & (arg1>>auxInt), unsigned shift
+		{name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int32"}, // arg0 & (arg1>>auxInt), signed shift
+		{name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int32"},  // arg0 | arg1<<auxInt
+		{name: "ORshiftRL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int32"},  // arg0 | arg1>>auxInt, unsigned shift
+		{name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int32"},  // arg0 | arg1>>auxInt, signed shift
+		{name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int32"}, // arg0 ^ arg1<<auxInt
+		{name: "XORshiftRL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int32"}, // arg0 ^ arg1>>auxInt, unsigned shift
+		{name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int32"}, // arg0 ^ arg1>>auxInt, signed shift
+		{name: "XORshiftRR", argLength: 2, reg: gp21, asm: "EOR", aux: "Int32"}, // arg0 ^ (arg1 right rotate by auxInt)
+		{name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int32"}, // arg0 &^ (arg1<<auxInt)
+		{name: "BICshiftRL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int32"}, // arg0 &^ (arg1>>auxInt), unsigned shift
+		{name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int32"}, // arg0 &^ (arg1>>auxInt), signed shift
+		{name: "MVNshiftLL", argLength: 1, reg: gp11, asm: "MVN", aux: "Int32"}, // ^(arg0<<auxInt)
+		{name: "MVNshiftRL", argLength: 1, reg: gp11, asm: "MVN", aux: "Int32"}, // ^(arg0>>auxInt), unsigned shift
+		{name: "MVNshiftRA", argLength: 1, reg: gp11, asm: "MVN", aux: "Int32"}, // ^(arg0>>auxInt), signed shift
+
+		{name: "ADCshiftLL", argLength: 3, reg: gp2flags1, asm: "ADC", aux: "Int32"}, // arg0 + arg1<<auxInt + carry, arg2=flags
+		{name: "ADCshiftRL", argLength: 3, reg: gp2flags1, asm: "ADC", aux: "Int32"}, // arg0 + arg1>>auxInt + carry, unsigned shift, arg2=flags
+		{name: "ADCshiftRA", argLength: 3, reg: gp2flags1, asm: "ADC", aux: "Int32"}, // arg0 + arg1>>auxInt + carry, signed shift, arg2=flags
+		{name: "SBCshiftLL", argLength: 3, reg: gp2flags1, asm: "SBC", aux: "Int32"}, // arg0 - arg1<<auxInt - carry, arg2=flags
+		{name: "SBCshiftRL", argLength: 3, reg: gp2flags1, asm: "SBC", aux: "Int32"}, // arg0 - arg1>>auxInt - carry, unsigned shift, arg2=flags
+		{name: "SBCshiftRA", argLength: 3, reg: gp2flags1, asm: "SBC", aux: "Int32"}, // arg0 - arg1>>auxInt - carry, signed shift, arg2=flags
+		{name: "RSCshiftLL", argLength: 3, reg: gp2flags1, asm: "RSC", aux: "Int32"}, // arg1<<auxInt - arg0 - carry, arg2=flags
+		{name: "RSCshiftRL", argLength: 3, reg: gp2flags1, asm: "RSC", aux: "Int32"}, // arg1>>auxInt - arg0 - carry, unsigned shift, arg2=flags
+		{name: "RSCshiftRA", argLength: 3, reg: gp2flags1, asm: "RSC", aux: "Int32"}, // arg1>>auxInt - arg0 - carry, signed shift, arg2=flags
+
+		{name: "ADDSshiftLL", argLength: 2, reg: gp21carry, asm: "ADD", aux: "Int32"}, // arg0 + arg1<<auxInt, set carry flag
+		{name: "ADDSshiftRL", argLength: 2, reg: gp21carry, asm: "ADD", aux: "Int32"}, // arg0 + arg1>>auxInt, unsigned shift, set carry flag
+		{name: "ADDSshiftRA", argLength: 2, reg: gp21carry, asm: "ADD", aux: "Int32"}, // arg0 + arg1>>auxInt, signed shift, set carry flag
+		{name: "SUBSshiftLL", argLength: 2, reg: gp21carry, asm: "SUB", aux: "Int32"}, // arg0 - arg1<<auxInt, set carry flag
+		{name: "SUBSshiftRL", argLength: 2, reg: gp21carry, asm: "SUB", aux: "Int32"}, // arg0 - arg1>>auxInt, unsigned shift, set carry flag
+		{name: "SUBSshiftRA", argLength: 2, reg: gp21carry, asm: "SUB", aux: "Int32"}, // arg0 - arg1>>auxInt, signed shift, set carry flag
+		{name: "RSBSshiftLL", argLength: 2, reg: gp21carry, asm: "RSB", aux: "Int32"}, // arg1<<auxInt - arg0, set carry flag
+		{name: "RSBSshiftRL", argLength: 2, reg: gp21carry, asm: "RSB", aux: "Int32"}, // arg1>>auxInt - arg0, unsigned shift, set carry flag
+		{name: "RSBSshiftRA", argLength: 2, reg: gp21carry, asm: "RSB", aux: "Int32"}, // arg1>>auxInt - arg0, signed shift, set carry flag
 
-		{name: "MOVWconst", argLength: 0, reg: gp01, aux: "Int32", asm: "MOVW", rematerializeable: true}, // 32 low bits of auxint
+		{name: "ADDshiftLLreg", argLength: 3, reg: gp31, asm: "ADD"}, // arg0 + arg1<<arg2
+		{name: "ADDshiftRLreg", argLength: 3, reg: gp31, asm: "ADD"}, // arg0 + arg1>>arg2, unsigned shift
+		{name: "ADDshiftRAreg", argLength: 3, reg: gp31, asm: "ADD"}, // arg0 + arg1>>arg2, signed shift
+		{name: "SUBshiftLLreg", argLength: 3, reg: gp31, asm: "SUB"}, // arg0 - arg1<<arg2
+		{name: "SUBshiftRLreg", argLength: 3, reg: gp31, asm: "SUB"}, // arg0 - arg1>>arg2, unsigned shift
+		{name: "SUBshiftRAreg", argLength: 3, reg: gp31, asm: "SUB"}, // arg0 - arg1>>arg2, signed shift
+		{name: "RSBshiftLLreg", argLength: 3, reg: gp31, asm: "RSB"}, // arg1<<arg2 - arg0
+		{name: "RSBshiftRLreg", argLength: 3, reg: gp31, asm: "RSB"}, // arg1>>arg2 - arg0, unsigned shift
+		{name: "RSBshiftRAreg", argLength: 3, reg: gp31, asm: "RSB"}, // arg1>>arg2 - arg0, signed shift
+		{name: "ANDshiftLLreg", argLength: 3, reg: gp31, asm: "AND"}, // arg0 & (arg1<<arg2)
+		{name: "ANDshiftRLreg", argLength: 3, reg: gp31, asm: "AND"}, // arg0 & (arg1>>arg2), unsigned shift
+		{name: "ANDshiftRAreg", argLength: 3, reg: gp31, asm: "AND"}, // arg0 & (arg1>>arg2), signed shift
+		{name: "ORshiftLLreg", argLength: 3, reg: gp31, asm: "ORR"},  // arg0 | arg1<<arg2
+		{name: "ORshiftRLreg", argLength: 3, reg: gp31, asm: "ORR"},  // arg0 | arg1>>arg2, unsigned shift
+		{name: "ORshiftRAreg", argLength: 3, reg: gp31, asm: "ORR"},  // arg0 | arg1>>arg2, signed shift
+		{name: "XORshiftLLreg", argLength: 3, reg: gp31, asm: "EOR"}, // arg0 ^ arg1<<arg2
+		{name: "XORshiftRLreg", argLength: 3, reg: gp31, asm: "EOR"}, // arg0 ^ arg1>>arg2, unsigned shift
+		{name: "XORshiftRAreg", argLength: 3, reg: gp31, asm: "EOR"}, // arg0 ^ arg1>>arg2, signed shift
+		{name: "BICshiftLLreg", argLength: 3, reg: gp31, asm: "BIC"}, // arg0 &^ (arg1<<arg2)
+		{name: "BICshiftRLreg", argLength: 3, reg: gp31, asm: "BIC"}, // arg0 &^ (arg1>>arg2), unsigned shift
+		{name: "BICshiftRAreg", argLength: 3, reg: gp31, asm: "BIC"}, // arg0 &^ (arg1>>arg2), signed shift
+		{name: "MVNshiftLLreg", argLength: 2, reg: gp21, asm: "MVN"}, // ^(arg0<<arg1)
+		{name: "MVNshiftRLreg", argLength: 2, reg: gp21, asm: "MVN"}, // ^(arg0>>arg1), unsigned shift
+		{name: "MVNshiftRAreg", argLength: 2, reg: gp21, asm: "MVN"}, // ^(arg0>>arg1), signed shift
 
-		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1
+		{name: "ADCshiftLLreg", argLength: 4, reg: gp3flags1, asm: "ADC"}, // arg0 + arg1<<arg2 + carry, arg3=flags
+		{name: "ADCshiftRLreg", argLength: 4, reg: gp3flags1, asm: "ADC"}, // arg0 + arg1>>arg2 + carry, unsigned shift, arg3=flags
+		{name: "ADCshiftRAreg", argLength: 4, reg: gp3flags1, asm: "ADC"}, // arg0 + arg1>>arg2 + carry, signed shift, arg3=flags
+		{name: "SBCshiftLLreg", argLength: 4, reg: gp3flags1, asm: "SBC"}, // arg0 - arg1<<arg2 - carry, arg3=flags
+		{name: "SBCshiftRLreg", argLength: 4, reg: gp3flags1, asm: "SBC"}, // arg0 - arg1>>arg2 - carry, unsigned shift, arg3=flags
+		{name: "SBCshiftRAreg", argLength: 4, reg: gp3flags1, asm: "SBC"}, // arg0 - arg1>>arg2 - carry, signed shift, arg3=flags
+		{name: "RSCshiftLLreg", argLength: 4, reg: gp3flags1, asm: "RSC"}, // arg1<<arg2 - arg0 - carry, arg3=flags
+		{name: "RSCshiftRLreg", argLength: 4, reg: gp3flags1, asm: "RSC"}, // arg1>>arg2 - arg0 - carry, unsigned shift, arg3=flags
+		{name: "RSCshiftRAreg", argLength: 4, reg: gp3flags1, asm: "RSC"}, // arg1>>arg2 - arg0 - carry, signed shift, arg3=flags
 
-		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW"},   // load from arg0 + auxInt + aux.  arg1=mem.
-		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW"}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "ADDSshiftLLreg", argLength: 3, reg: gp31carry, asm: "ADD"}, // arg0 + arg1<<arg2, set carry flag
+		{name: "ADDSshiftRLreg", argLength: 3, reg: gp31carry, asm: "ADD"}, // arg0 + arg1>>arg2, unsigned shift, set carry flag
+		{name: "ADDSshiftRAreg", argLength: 3, reg: gp31carry, asm: "ADD"}, // arg0 + arg1>>arg2, signed shift, set carry flag
+		{name: "SUBSshiftLLreg", argLength: 3, reg: gp31carry, asm: "SUB"}, // arg0 - arg1<<arg2, set carry flag
+		{name: "SUBSshiftRLreg", argLength: 3, reg: gp31carry, asm: "SUB"}, // arg0 - arg1>>arg2, unsigned shift, set carry flag
+		{name: "SUBSshiftRAreg", argLength: 3, reg: gp31carry, asm: "SUB"}, // arg0 - arg1>>arg2, signed shift, set carry flag
+		{name: "RSBSshiftLLreg", argLength: 3, reg: gp31carry, asm: "RSB"}, // arg1<<arg2 - arg0, set carry flag
+		{name: "RSBSshiftRLreg", argLength: 3, reg: gp31carry, asm: "RSB"}, // arg1>>arg2 - arg0, unsigned shift, set carry flag
+		{name: "RSBSshiftRAreg", argLength: 3, reg: gp31carry, asm: "RSB"}, // arg1>>arg2 - arg0, signed shift, set carry flag
 
-		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff"}, // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
+		// comparisons
+		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},                    // arg0 compare to arg1
+		{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", aux: "Int32", typ: "Flags"}, // arg0 compare to auxInt
+		{name: "CMN", argLength: 2, reg: gp2flags, asm: "CMN", typ: "Flags"},                    // arg0 compare to -arg1
+		{name: "CMNconst", argLength: 1, reg: gp1flags, asm: "CMN", aux: "Int32", typ: "Flags"}, // arg0 compare to -auxInt
+		{name: "TST", argLength: 2, reg: gp2flags, asm: "TST", typ: "Flags", commutative: true}, // arg0 & arg1 compare to 0
+		{name: "TSTconst", argLength: 1, reg: gp1flags, asm: "TST", aux: "Int32", typ: "Flags"}, // arg0 & auxInt compare to 0
+		{name: "TEQ", argLength: 2, reg: gp2flags, asm: "TEQ", typ: "Flags", commutative: true}, // arg0 ^ arg1 compare to 0
+		{name: "TEQconst", argLength: 1, reg: gp1flags, asm: "TEQ", aux: "Int32", typ: "Flags"}, // arg0 ^ auxInt compare to 0
+		{name: "CMPF", argLength: 2, reg: fp2flags, asm: "CMPF", typ: "Flags"},                  // arg0 compare to arg1, float32
+		{name: "CMPD", argLength: 2, reg: fp2flags, asm: "CMPD", typ: "Flags"},                  // arg0 compare to arg1, float64
+
+		{name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int32", typ: "Flags"}, // arg0 compare to arg1<<auxInt
+		{name: "CMPshiftRL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int32", typ: "Flags"}, // arg0 compare to arg1>>auxInt, unsigned shift
+		{name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int32", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift
+
+		{name: "CMPshiftLLreg", argLength: 3, reg: gp3flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1<<arg2
+		{name: "CMPshiftRLreg", argLength: 3, reg: gp3flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1>>arg2, unsigned shift
+		{name: "CMPshiftRAreg", argLength: 3, reg: gp3flags, asm: "CMP", typ: "Flags"}, // arg0 compare to arg1>>arg2, signed shift
+
+		{name: "CMPF0", argLength: 1, reg: fp1flags, asm: "CMPF", typ: "Flags"}, // arg0 compare to 0, float32
+		{name: "CMPD0", argLength: 1, reg: fp1flags, asm: "CMPD", typ: "Flags"}, // arg0 compare to 0, float64
+
+		// moves
+		{name: "MOVWconst", argLength: 0, reg: gp01, aux: "Int32", asm: "MOVW", typ: "UInt32", rematerializeable: true},    // 32 low bits of auxint
+		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
+		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
+
+		{name: "MOVWaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVW", rematerializeable: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
+
+		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "UInt32", faultOnNilArg0: true},   // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+
+		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true}, // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true}, // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+
+		{name: "MOVWloadidx", argLength: 3, reg: gp2load, asm: "MOVW"},                   // load from arg0 + arg1. arg2=mem
+		{name: "MOVWloadshiftLL", argLength: 3, reg: gp2load, asm: "MOVW", aux: "Int32"}, // load from arg0 + arg1<<auxInt. arg2=mem
+		{name: "MOVWloadshiftRL", argLength: 3, reg: gp2load, asm: "MOVW", aux: "Int32"}, // load from arg0 + arg1>>auxInt, unsigned shift. arg2=mem
+		{name: "MOVWloadshiftRA", argLength: 3, reg: gp2load, asm: "MOVW", aux: "Int32"}, // load from arg0 + arg1>>auxInt, signed shift. arg2=mem
+
+		{name: "MOVWstoreidx", argLength: 4, reg: gp2store, asm: "MOVW"},                   // store arg2 to arg0 + arg1. arg3=mem
+		{name: "MOVWstoreshiftLL", argLength: 4, reg: gp2store, asm: "MOVW", aux: "Int32"}, // store arg2 to arg0 + arg1<<auxInt. arg3=mem
+		{name: "MOVWstoreshiftRL", argLength: 4, reg: gp2store, asm: "MOVW", aux: "Int32"}, // store arg2 to arg0 + arg1>>auxInt, unsigned shift. arg3=mem
+		{name: "MOVWstoreshiftRA", argLength: 4, reg: gp2store, asm: "MOVW", aux: "Int32"}, // store arg2 to arg0 + arg1>>auxInt, signed shift. arg3=mem
+
+		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVBS"},  // move from arg0, sign-extended from byte
+		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
+		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVHS"},  // move from arg0, sign-extended from half
+		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
+		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0
+
+		{name: "MOVWnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
+
+		{name: "MOVWF", argLength: 1, reg: gpfp, asm: "MOVWF"},  // int32 -> float32
+		{name: "MOVWD", argLength: 1, reg: gpfp, asm: "MOVWD"},  // int32 -> float64
+		{name: "MOVWUF", argLength: 1, reg: gpfp, asm: "MOVWF"}, // uint32 -> float32, set U bit in the instruction
+		{name: "MOVWUD", argLength: 1, reg: gpfp, asm: "MOVWD"}, // uint32 -> float64, set U bit in the instruction
+		{name: "MOVFW", argLength: 1, reg: fpgp, asm: "MOVFW"},  // float32 -> int32
+		{name: "MOVDW", argLength: 1, reg: fpgp, asm: "MOVDW"},  // float64 -> int32
+		{name: "MOVFWU", argLength: 1, reg: fpgp, asm: "MOVFW"}, // float32 -> uint32, set U bit in the instruction
+		{name: "MOVDWU", argLength: 1, reg: fpgp, asm: "MOVDW"}, // float64 -> uint32, set U bit in the instruction
+		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},  // float32 -> float64
+		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},  // float64 -> float32
+
+		// conditional instructions, for lowering shifts
+		{name: "CMOVWHSconst", argLength: 2, reg: gp1flags1, asm: "MOVW", aux: "Int32", resultInArg0: true}, // replace arg0 w/ const if flags indicates HS, arg1=flags
+		{name: "CMOVWLSconst", argLength: 2, reg: gp1flags1, asm: "MOVW", aux: "Int32", resultInArg0: true}, // replace arg0 w/ const if flags indicates LS, arg1=flags
+		{name: "SRAcond", argLength: 3, reg: gp2flags1, asm: "SRA"},                                         // arg0 >> 31 if flags indicates HS, arg0 >> arg1 otherwise, signed shift, arg2=flags
+
+		// function calls
+		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true},                                             // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
+		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                               // call deferproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                  // call newproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                        // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
 
 		// pseudo-ops
-		{name: "LessThan", argLength: 1, reg: flagsgp}, // bool, 1 flags encode x<y 0 otherwise.
+		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
+
+		{name: "Equal", argLength: 1, reg: readflags},         // bool, true flags encode x==y false otherwise.
+		{name: "NotEqual", argLength: 1, reg: readflags},      // bool, true flags encode x!=y false otherwise.
+		{name: "LessThan", argLength: 1, reg: readflags},      // bool, true flags encode signed x<y false otherwise.
+		{name: "LessEqual", argLength: 1, reg: readflags},     // bool, true flags encode signed x<=y false otherwise.
+		{name: "GreaterThan", argLength: 1, reg: readflags},   // bool, true flags encode signed x>y false otherwise.
+		{name: "GreaterEqual", argLength: 1, reg: readflags},  // bool, true flags encode signed x>=y false otherwise.
+		{name: "LessThanU", argLength: 1, reg: readflags},     // bool, true flags encode unsigned x<y false otherwise.
+		{name: "LessEqualU", argLength: 1, reg: readflags},    // bool, true flags encode unsigned x<=y false otherwise.
+		{name: "GreaterThanU", argLength: 1, reg: readflags},  // bool, true flags encode unsigned x>y false otherwise.
+		{name: "GreaterEqualU", argLength: 1, reg: readflags}, // bool, true flags encode unsigned x>=y false otherwise.
+
+		// duffzero (must be 4-byte aligned)
+		// arg0 = address of memory to zero (in R1, changed as side effect)
+		// arg1 = value to store (always zero)
+		// arg2 = mem
+		// auxint = offset into duffzero code to start executing
+		// returns mem
+		{
+			name:      "DUFFZERO",
+			aux:       "Int64",
+			argLength: 3,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R1"), buildReg("R0")},
+				clobbers: buildReg("R1 R14"),
+			},
+			faultOnNilArg0: true,
+		},
+
+		// duffcopy (must be 4-byte aligned)
+		// arg0 = address of dst memory (in R2, changed as side effect)
+		// arg1 = address of src memory (in R1, changed as side effect)
+		// arg2 = mem
+		// auxint = offset into duffcopy code to start executing
+		// returns mem
+		{
+			name:      "DUFFCOPY",
+			aux:       "Int64",
+			argLength: 3,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R2"), buildReg("R1")},
+				clobbers: buildReg("R0 R1 R2 R14"),
+			},
+			faultOnNilArg0: true,
+			faultOnNilArg1: true,
+		},
+
+		// large or unaligned zeroing
+		// arg0 = address of memory to zero (in R1, changed as side effect)
+		// arg1 = address of the last element to zero
+		// arg2 = value to store (always zero)
+		// arg3 = mem
+		// returns mem
+		//	MOVW.P	Rarg2, 4(R1)
+		//	CMP	R1, Rarg1
+		//	BLE	-2(PC)
+		{
+			name:      "LoweredZero",
+			aux:       "Int64",
+			argLength: 4,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R1"), gp, gp},
+				clobbers: buildReg("R1"),
+			},
+			clobberFlags:   true,
+			faultOnNilArg0: true,
+		},
+
+		// large or unaligned move
+		// arg0 = address of dst memory (in R2, changed as side effect)
+		// arg1 = address of src memory (in R1, changed as side effect)
+		// arg2 = address of the last element of src
+		// arg3 = mem
+		// returns mem
+		//	MOVW.P	4(R1), Rtmp
+		//	MOVW.P	Rtmp, 4(R2)
+		//	CMP	R1, Rarg2
+		//	BLE	-3(PC)
+		{
+			name:      "LoweredMove",
+			aux:       "Int64",
+			argLength: 4,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
+				clobbers: buildReg("R1 R2"),
+			},
+			clobberFlags:   true,
+			faultOnNilArg0: true,
+			faultOnNilArg1: true,
+		},
+
+		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
+		// and sorts it to the very beginning of the block to prevent other
+		// use of R7 (arm.REGCTXT, the closure pointer)
+		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R7")}}},
+
+		// MOVWconvert converts between pointers and integers.
+		// We have a special op for this so as to not confuse GC
+		// (particularly stack maps).  It takes a memory arg so it
+		// gets correctly ordered with respect to GC safepoints.
+		// arg0=ptr/int arg1=mem, output=int/ptr
+		{name: "MOVWconvert", argLength: 2, reg: gp11, asm: "MOVW"},
+
+		// Constant flag values. For any comparison, there are 5 possible
+		// outcomes: the three from the signed total order (<,==,>) and the
+		// three from the unsigned total order. The == cases overlap.
+		// Note: there's a sixth "unordered" outcome for floating-point
+		// comparisons, but we don't use such a beast yet.
+		// These ops are for temporary use by rewrite rules. They
+		// cannot appear in the generated assembly.
+		{name: "FlagEQ"},     // equal
+		{name: "FlagLT_ULT"}, // signed < and unsigned <
+		{name: "FlagLT_UGT"}, // signed < and unsigned >
+		{name: "FlagGT_UGT"}, // signed > and unsigned <
+		{name: "FlagGT_ULT"}, // signed > and unsigned >
+
+		// (InvertFlags (CMP a b)) == (CMP b a)
+		// InvertFlags is a pseudo-op which can't appear in assembly output.
+		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
 	}
 
 	blocks := []blockData{
@@ -47,22 +516,16 @@ func init() {
 		{name: "UGE"},
 	}
 
-	regNames := []string{
-		"R0",
-		"R1",
-		"R2",
-		"R3",
-		"SP",
-		"FLAGS",
-		"SB",
-	}
-
 	archs = append(archs, arch{
-		name:     "ARM",
-		pkg:      "cmd/internal/obj/arm",
-		genfile:  "../../arm/ssa.go",
-		ops:      ops,
-		blocks:   blocks,
-		regnames: regNames,
+		name:            "ARM",
+		pkg:             "cmd/internal/obj/arm",
+		genfile:         "../../arm/ssa.go",
+		ops:             ops,
+		blocks:          blocks,
+		regnames:        regNamesARM,
+		gpregmask:       gp,
+		fpregmask:       fp,
+		framepointerreg: -1, // not used
+		linkreg:         int8(num["R14"]),
 	})
 }
diff --git a/src/cmd/compile/internal/ssa/gen/MIPS.rules b/src/cmd/compile/internal/ssa/gen/MIPS.rules
new file mode 100644
index 0000000..008f1b1
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/MIPS.rules
@@ -0,0 +1,739 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+(AddPtr x y) -> (ADD x y)
+(Add32 x y) -> (ADD x y)
+(Add16 x y) -> (ADD x y)
+(Add8 x y) -> (ADD x y)
+(Add32F x y) -> (ADDF x y)
+(Add64F x y) -> (ADDD x y)
+
+(Select0 (Add32carry <t> x y)) -> (ADD <t.FieldType(0)> x y)
+(Select1 (Add32carry <t> x y)) -> (SGTU <config.fe.TypeBool()> x (ADD <t.FieldType(0)> x y))
+(Add32withcarry <t> x y c) -> (ADD c (ADD <t> x y))
+
+(SubPtr x y) -> (SUB x y)
+(Sub32 x y) -> (SUB x y)
+(Sub16 x y) -> (SUB x y)
+(Sub8 x y) -> (SUB x y)
+(Sub32F x y) -> (SUBF x y)
+(Sub64F x y) -> (SUBD x y)
+
+(Select0 (Sub32carry <t> x y)) -> (SUB <t.FieldType(0)> x y)
+(Select1 (Sub32carry <t> x y)) -> (SGTU <config.fe.TypeBool()> (SUB <t.FieldType(0)> x y) x)
+(Sub32withcarry <t> x y c) -> (SUB (SUB <t> x y) c)
+
+(Mul32 x y) -> (MUL x y)
+(Mul16 x y) -> (MUL x y)
+(Mul8 x y) -> (MUL x y)
+(Mul32F x y) -> (MULF x y)
+(Mul64F x y) -> (MULD x y)
+
+(Hmul32 x y) -> (Select0 (MULT x y))
+(Hmul32u x y) -> (Select0 (MULTU x y))
+(Hmul16 x y) -> (SRAconst (MUL <config.fe.TypeInt32()> (SignExt16to32 x) (SignExt16to32 y)) [16])
+(Hmul16u x y) -> (SRLconst (MUL <config.fe.TypeUInt32()> (ZeroExt16to32 x) (ZeroExt16to32 y)) [16])
+(Hmul8 x y) -> (SRAconst  (MUL <config.fe.TypeInt32()> (SignExt8to32 x) (SignExt8to32 y)) [8])
+(Hmul8u x y) -> (SRLconst (MUL <config.fe.TypeUInt32()> (ZeroExt8to32 x) (ZeroExt8to32 y)) [8])
+
+(Mul32uhilo x y) -> (MULTU x y)
+
+(Div32 x y) -> (Select1 (DIV x y))
+(Div32u x y) -> (Select1 (DIVU x y))
+(Div16 x y) -> (Select1 (DIV (SignExt16to32 x) (SignExt16to32 y)))
+(Div16u x y) -> (Select1 (DIVU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Div8 x y) -> (Select1 (DIV (SignExt8to32 x) (SignExt8to32 y)))
+(Div8u x y) -> (Select1 (DIVU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Div32F x y) -> (DIVF x y)
+(Div64F x y) -> (DIVD x y)
+
+(Mod32 x y) -> (Select0 (DIV x y))
+(Mod32u x y) -> (Select0 (DIVU x y))
+(Mod16 x y) -> (Select0 (DIV (SignExt16to32 x) (SignExt16to32 y)))
+(Mod16u x y) -> (Select0 (DIVU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Mod8 x y) -> (Select0 (DIV (SignExt8to32 x) (SignExt8to32 y)))
+(Mod8u x y) -> (Select0 (DIVU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+
+(And32 x y) -> (AND x y)
+(And16 x y) -> (AND x y)
+(And8 x y) -> (AND x y)
+
+(Or32 x y) -> (OR x y)
+(Or16 x y) -> (OR x y)
+(Or8 x y) -> (OR x y)
+
+(Xor32 x y) -> (XOR x y)
+(Xor16 x y) -> (XOR x y)
+(Xor8 x y) -> (XOR x y)
+
+// constant shifts
+// generic opt rewrites all constant shifts to shift by Const64
+(Lsh32x64 x (Const64 [c])) && uint32(c) < 32 -> (SLLconst x [c])
+(Rsh32x64 x (Const64 [c])) && uint32(c) < 32 -> (SRAconst x [c])
+(Rsh32Ux64 x (Const64 [c])) && uint32(c) < 32 -> (SRLconst x [c])
+(Lsh16x64 x (Const64 [c])) && uint32(c) < 16 -> (SLLconst x [c])
+(Rsh16x64 x (Const64 [c])) && uint32(c) < 16 -> (SRAconst (SLLconst <config.fe.TypeUInt32()> x [16]) [c+16])
+(Rsh16Ux64 x (Const64 [c])) && uint32(c) < 16 -> (SRLconst (SLLconst <config.fe.TypeUInt32()> x [16]) [c+16])
+(Lsh8x64 x (Const64 [c])) && uint32(c) < 8 -> (SLLconst x [c])
+(Rsh8x64 x (Const64 [c])) && uint32(c) < 8 -> (SRAconst (SLLconst <config.fe.TypeUInt32()> x [24]) [c+24])
+(Rsh8Ux64 x (Const64 [c])) && uint32(c) < 8 -> (SRLconst (SLLconst <config.fe.TypeUInt32()> x [24]) [c+24])
+
+// large constant shifts
+(Lsh32x64 _ (Const64 [c])) && uint32(c) >= 32 -> (MOVWconst [0])
+(Rsh32Ux64 _ (Const64 [c])) && uint32(c) >= 32 -> (MOVWconst [0])
+(Lsh16x64 _ (Const64 [c])) && uint32(c) >= 16 -> (MOVWconst [0])
+(Rsh16Ux64 _ (Const64 [c])) && uint32(c) >= 16 -> (MOVWconst [0])
+(Lsh8x64 _ (Const64 [c])) && uint32(c) >= 8 -> (MOVWconst [0])
+(Rsh8Ux64 _ (Const64 [c])) && uint32(c) >= 8 -> (MOVWconst [0])
+
+// large constant signed right shift, we leave the sign bit
+(Rsh32x64 x (Const64 [c])) && uint32(c) >= 32 -> (SRAconst x [31])
+(Rsh16x64 x (Const64 [c])) && uint32(c) >= 16 -> (SRAconst (SLLconst <config.fe.TypeUInt32()> x [16]) [31])
+(Rsh8x64 x (Const64 [c])) && uint32(c) >= 8 -> (SRAconst (SLLconst <config.fe.TypeUInt32()> x [24]) [31])
+
+// shifts
+// hardware instruction uses only the low 5 bits of the shift
+// we compare to 32 to ensure Go semantics for large shifts
+(Lsh32x32 <t> x y) -> (CMOVZ (SLL <t> x y) (MOVWconst [0]) (SGTUconst [32] y))
+(Lsh32x16 <t> x y) -> (CMOVZ (SLL <t> x (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+(Lsh32x8 <t> x y) -> (CMOVZ (SLL <t> x (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+
+(Lsh16x32 <t> x y) -> (CMOVZ (SLL <t> x y) (MOVWconst [0]) (SGTUconst [32] y))
+(Lsh16x16 <t> x y) -> (CMOVZ (SLL <t> x (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+(Lsh16x8 <t> x y) -> (CMOVZ (SLL <t> x (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+
+(Lsh8x32 <t> x y) -> (CMOVZ (SLL <t> x y) (MOVWconst [0]) (SGTUconst [32] y))
+(Lsh8x16 <t> x y) -> (CMOVZ (SLL <t> x (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+(Lsh8x8 <t> x y) -> (CMOVZ (SLL <t> x (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+
+(Rsh32Ux32 <t> x y) -> (CMOVZ (SRL <t> x y) (MOVWconst [0]) (SGTUconst [32] y))
+(Rsh32Ux16 <t> x y) -> (CMOVZ (SRL <t> x (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+(Rsh32Ux8 <t> x y) -> (CMOVZ (SRL <t> x (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+
+(Rsh16Ux32 <t> x y) -> (CMOVZ (SRL <t> (ZeroExt16to32 x) y) (MOVWconst [0]) (SGTUconst [32] y))
+(Rsh16Ux16 <t> x y) -> (CMOVZ (SRL <t> (ZeroExt16to32 x) (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+(Rsh16Ux8 <t> x y) -> (CMOVZ (SRL <t> (ZeroExt16to32 x) (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+
+(Rsh8Ux32 <t> x y) -> (CMOVZ (SRL <t> (ZeroExt8to32 x) y) (MOVWconst [0]) (SGTUconst [32] y))
+(Rsh8Ux16 <t> x y) -> (CMOVZ (SRL <t> (ZeroExt8to32 x) (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+(Rsh8Ux8 <t> x y) -> (CMOVZ (SRL <t> (ZeroExt8to32 x) (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+
+(Rsh32x32 x y) -> (SRA x ( CMOVZ <config.fe.TypeUInt32()> y (MOVWconst [-1]) (SGTUconst [32] y)))
+(Rsh32x16 x y) -> (SRA x ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y))))
+(Rsh32x8 x y) -> (SRA x ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y))))
+
+(Rsh16x32 x y) -> (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> y (MOVWconst [-1]) (SGTUconst [32] y)))
+(Rsh16x16 x y) -> (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y))))
+(Rsh16x8 x y) -> (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y))))
+
+(Rsh8x32 x y) -> (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> y (MOVWconst [-1]) (SGTUconst [32] y)))
+(Rsh8x16 x y) -> (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y))))
+(Rsh8x8 x y) -> (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y))))
+
+// unary ops
+(Neg32 x) -> (NEG x)
+(Neg16 x) -> (NEG x)
+(Neg8 x) -> (NEG x)
+(Neg32F x) -> (NEGF x)
+(Neg64F x) -> (NEGD x)
+
+(Com32 x) -> (NORconst [0] x)
+(Com16 x) -> (NORconst [0] x)
+(Com8 x) -> (NORconst [0] x)
+
+(Sqrt x) -> (SQRTD x)
+
+// count trailing zero
+// 32 - CLZ(x&-x - 1)
+(Ctz32 <t> x) -> (SUB (MOVWconst [32]) (CLZ <t> (SUBconst <t> [1] (AND <t> x (NEG <t> x)))))
+
+// boolean ops -- booleans are represented with 0=false, 1=true
+(AndB x y) -> (AND x y)
+(OrB x y) -> (OR x y)
+(EqB x y) -> (XORconst [1] (XOR <config.fe.TypeBool()> x y))
+(NeqB x y) -> (XOR x y)
+(Not x) -> (XORconst [1] x)
+
+// constants
+(Const32 [val]) -> (MOVWconst [val])
+(Const16 [val]) -> (MOVWconst [val])
+(Const8 [val]) -> (MOVWconst [val])
+(Const32F [val]) -> (MOVFconst [val])
+(Const64F [val]) -> (MOVDconst [val])
+(ConstNil) -> (MOVWconst [0])
+(ConstBool [b]) -> (MOVWconst [b])
+
+// truncations
+// Because we ignore high parts of registers, truncates are just copies.
+(Trunc16to8 x) -> x
+(Trunc32to8 x) -> x
+(Trunc32to16 x) -> x
+
+// Zero-/Sign-extensions
+(ZeroExt8to16 x) -> (MOVBUreg x)
+(ZeroExt8to32 x) -> (MOVBUreg x)
+(ZeroExt16to32 x) -> (MOVHUreg x)
+
+(SignExt8to16 x) -> (MOVBreg x)
+(SignExt8to32 x) -> (MOVBreg x)
+(SignExt16to32 x) -> (MOVHreg x)
+
+(Signmask x) -> (SRAconst x [31])
+(Zeromask x) -> (NEG (SGTU x (MOVWconst [0])))
+(Slicemask x) -> (NEG (SGT x (MOVWconst [0])))
+
+// float <-> int conversion
+(Cvt32to32F x) -> (MOVWF x)
+(Cvt32to64F x) -> (MOVWD x)
+(Cvt32Fto32 x) -> (TRUNCFW x)
+(Cvt64Fto32 x) -> (TRUNCDW x)
+(Cvt32Fto64F x) -> (MOVFD x)
+(Cvt64Fto32F x) -> (MOVDF x)
+
+// comparisons
+(Eq8 x y)  -> (SGTUconst [1] (XOR (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Eq16 x y) -> (SGTUconst [1] (XOR (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Eq32 x y) -> (SGTUconst [1] (XOR x y))
+(EqPtr x y) -> (SGTUconst [1] (XOR x y))
+(Eq32F x y) -> (FPFlagTrue (CMPEQF x y))
+(Eq64F x y) -> (FPFlagTrue (CMPEQD x y))
+
+(Neq8 x y)  -> (SGTU (XOR (ZeroExt8to32 x) (ZeroExt8to32 y)) (MOVWconst [0]))
+(Neq16 x y) -> (SGTU (XOR (ZeroExt16to32 x) (ZeroExt16to32 y)) (MOVWconst [0]))
+(Neq32 x y) -> (SGTU (XOR x y) (MOVWconst [0]))
+(NeqPtr x y) -> (SGTU (XOR x y) (MOVWconst [0]))
+(Neq32F x y) -> (FPFlagFalse (CMPEQF x y))
+(Neq64F x y) -> (FPFlagFalse (CMPEQD x y))
+
+(Less8 x y)  -> (SGT (SignExt8to32 y) (SignExt8to32 x))
+(Less16 x y) -> (SGT (SignExt16to32 y) (SignExt16to32 x))
+(Less32 x y) -> (SGT y x)
+(Less32F x y) -> (FPFlagTrue (CMPGTF y x)) // reverse operands to work around NaN
+(Less64F x y) -> (FPFlagTrue (CMPGTD y x)) // reverse operands to work around NaN
+
+(Less8U x y)  -> (SGTU (ZeroExt8to32 y) (ZeroExt8to32 x))
+(Less16U x y) -> (SGTU (ZeroExt16to32 y) (ZeroExt16to32 x))
+(Less32U x y) -> (SGTU y x)
+
+(Leq8 x y)  -> (XORconst [1] (SGT (SignExt8to32 x) (SignExt8to32 y)))
+(Leq16 x y) -> (XORconst [1] (SGT (SignExt16to32 x) (SignExt16to32 y)))
+(Leq32 x y) -> (XORconst [1] (SGT x y))
+(Leq32F x y) -> (FPFlagTrue (CMPGEF y x)) // reverse operands to work around NaN
+(Leq64F x y) -> (FPFlagTrue (CMPGED y x)) // reverse operands to work around NaN
+
+(Leq8U x y)  -> (XORconst [1] (SGTU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Leq16U x y) -> (XORconst [1] (SGTU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Leq32U x y) -> (XORconst [1] (SGTU x y))
+
+(Greater8 x y)  -> (SGT (SignExt8to32 x) (SignExt8to32 y))
+(Greater16 x y) -> (SGT (SignExt16to32 x) (SignExt16to32 y))
+(Greater32 x y) -> (SGT x y)
+(Greater32F x y) -> (FPFlagTrue (CMPGTF x y))
+(Greater64F x y) -> (FPFlagTrue (CMPGTD x y))
+
+(Greater8U x y)  -> (SGTU (ZeroExt8to32 x) (ZeroExt8to32 y))
+(Greater16U x y) -> (SGTU (ZeroExt16to32 x) (ZeroExt16to32 y))
+(Greater32U x y) -> (SGTU x y)
+
+(Geq8 x y)  -> (XORconst [1] (SGT (SignExt8to32 y) (SignExt8to32 x)))
+(Geq16 x y) -> (XORconst [1] (SGT (SignExt16to32 y) (SignExt16to32 x)))
+(Geq32 x y) -> (XORconst [1] (SGT y x))
+(Geq32F x y) -> (FPFlagTrue (CMPGEF x y))
+(Geq64F x y) -> (FPFlagTrue (CMPGED x y))
+
+(Geq8U x y)  -> (XORconst [1] (SGTU (ZeroExt8to32 y) (ZeroExt8to32 x)))
+(Geq16U x y) -> (XORconst [1] (SGTU (ZeroExt16to32 y) (ZeroExt16to32 x)))
+(Geq32U x y) -> (XORconst [1] (SGTU y x))
+
+(OffPtr [off] ptr:(SP)) -> (MOVWaddr [off] ptr)
+(OffPtr [off] ptr) -> (ADDconst [off] ptr)
+
+(Addr {sym} base) -> (MOVWaddr {sym} base)
+
+// loads
+(Load <t> ptr mem) && t.IsBoolean() -> (MOVBUload ptr mem)
+(Load <t> ptr mem) && (is8BitInt(t) && isSigned(t)) -> (MOVBload ptr mem)
+(Load <t> ptr mem) && (is8BitInt(t) && !isSigned(t)) -> (MOVBUload ptr mem)
+(Load <t> ptr mem) && (is16BitInt(t) && isSigned(t)) -> (MOVHload ptr mem)
+(Load <t> ptr mem) && (is16BitInt(t) && !isSigned(t)) -> (MOVHUload ptr mem)
+(Load <t> ptr mem) && (is32BitInt(t) || isPtr(t)) -> (MOVWload ptr mem)
+(Load <t> ptr mem) && is32BitFloat(t) -> (MOVFload ptr mem)
+(Load <t> ptr mem) && is64BitFloat(t) -> (MOVDload ptr mem)
+
+// stores
+(Store [1] ptr val mem) -> (MOVBstore ptr val mem)
+(Store [2] ptr val mem) -> (MOVHstore ptr val mem)
+(Store [4] ptr val mem) && !is32BitFloat(val.Type) -> (MOVWstore ptr val mem)
+(Store [8] ptr val mem) && !is64BitFloat(val.Type) -> (MOVWstore ptr val mem)
+(Store [4] ptr val mem) && is32BitFloat(val.Type) -> (MOVFstore ptr val mem)
+(Store [8] ptr val mem) && is64BitFloat(val.Type) -> (MOVDstore ptr val mem)
+
+// zero instructions
+(Zero [s] _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore ptr (MOVWconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore ptr (MOVWconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 2 ->
+	(MOVBstore [1] ptr (MOVWconst [0])
+		(MOVBstore [0] ptr (MOVWconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore ptr (MOVWconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [2] ptr (MOVWconst [0])
+		(MOVHstore [0] ptr (MOVWconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 ->
+	(MOVBstore [3] ptr (MOVWconst [0])
+		(MOVBstore [2] ptr (MOVWconst [0])
+			(MOVBstore [1] ptr (MOVWconst [0])
+				(MOVBstore [0] ptr (MOVWconst [0]) mem))))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] ptr (MOVWconst [0])
+		(MOVBstore [1] ptr (MOVWconst [0])
+			(MOVBstore [0] ptr (MOVWconst [0]) mem)))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [4] ptr (MOVWconst [0])
+		(MOVHstore [2] ptr (MOVWconst [0])
+			(MOVHstore [0] ptr (MOVWconst [0]) mem)))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0 ->
+		(MOVWstore [4] ptr (MOVWconst [0])
+			(MOVWstore [0] ptr (MOVWconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore [8] ptr (MOVWconst [0])
+		(MOVWstore [4] ptr (MOVWconst [0])
+			(MOVWstore [0] ptr (MOVWconst [0]) mem)))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore [12] ptr (MOVWconst [0])
+		(MOVWstore [8] ptr (MOVWconst [0])
+			(MOVWstore [4] ptr (MOVWconst [0])
+				(MOVWstore [0] ptr (MOVWconst [0]) mem))))
+
+// large or unaligned zeroing uses a loop
+(Zero [s] ptr mem)
+	&& (SizeAndAlign(s).Size() > 16  || SizeAndAlign(s).Align()%4 != 0) ->
+	(LoweredZero [SizeAndAlign(s).Align()]
+		ptr
+		(ADDconst <ptr.Type> ptr [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)])
+		mem)
+
+// moves
+(Move [s] _ _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore dst (MOVBUload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore dst (MOVHUload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 ->
+	(MOVBstore [1] dst (MOVBUload [1] src mem)
+		(MOVBstore dst (MOVBUload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore dst (MOVWload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [2] dst (MOVHUload [2] src mem)
+		(MOVHstore dst (MOVHUload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 ->
+	(MOVBstore [3] dst (MOVBUload [3] src mem)
+		(MOVBstore [2] dst (MOVBUload [2] src mem)
+			(MOVBstore [1] dst (MOVBUload [1] src mem)
+				(MOVBstore dst (MOVBUload src mem) mem))))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] dst (MOVBUload [2] src mem)
+		(MOVBstore [1] dst (MOVBUload [1] src mem)
+			(MOVBstore dst (MOVBUload src mem) mem)))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore [4] dst (MOVWload [4] src mem)
+		(MOVWstore dst (MOVWload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [6] dst (MOVHload [6] src mem)
+		(MOVHstore [4] dst (MOVHload [4] src mem)
+			(MOVHstore [2] dst (MOVHload [2] src mem)
+				(MOVHstore dst (MOVHload src mem) mem))))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [4] dst (MOVHload [4] src mem)
+		(MOVHstore [2] dst (MOVHload [2] src mem)
+			(MOVHstore dst (MOVHload src mem) mem)))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore [8] dst (MOVWload [8] src mem)
+		(MOVWstore [4] dst (MOVWload [4] src mem)
+			(MOVWstore dst (MOVWload src mem) mem)))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore [12] dst (MOVWload [12] src mem)
+		(MOVWstore [8] dst (MOVWload [8] src mem)
+			(MOVWstore [4] dst (MOVWload [4] src mem)
+				(MOVWstore dst (MOVWload src mem) mem))))
+
+
+// large or unaligned move uses a loop
+(Move [s] dst src mem)
+	&& (SizeAndAlign(s).Size() > 16 || SizeAndAlign(s).Align()%4 != 0) ->
+	(LoweredMove [SizeAndAlign(s).Align()]
+		dst
+		src
+		(ADDconst <src.Type> src [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)])
+		mem)
+
+// calls
+(StaticCall [argwid] {target} mem) -> (CALLstatic [argwid] {target} mem)
+(ClosureCall [argwid] entry closure mem) -> (CALLclosure [argwid] entry closure mem)
+(DeferCall [argwid] mem) -> (CALLdefer [argwid] mem)
+(GoCall [argwid] mem) -> (CALLgo [argwid] mem)
+(InterCall [argwid] entry mem) -> (CALLinter [argwid] entry mem)
+
+// atomic intrinsics
+(AtomicLoad32  ptr mem) -> (LoweredAtomicLoad ptr mem)
+(AtomicLoadPtr ptr mem) -> (LoweredAtomicLoad  ptr mem)
+
+(AtomicStore32      ptr val mem) -> (LoweredAtomicStore ptr val mem)
+(AtomicStorePtrNoWB ptr val mem) -> (LoweredAtomicStore  ptr val mem)
+
+(AtomicExchange32 ptr val mem) -> (LoweredAtomicExchange ptr val mem)
+(AtomicAdd32 ptr val mem) -> (LoweredAtomicAdd ptr val mem)
+
+(AtomicCompareAndSwap32 ptr old new_ mem) -> (LoweredAtomicCas ptr old new_ mem)
+
+// AtomicOr8(ptr,val) -> LoweredAtomicOr(ptr&^3,uint32(val) << ((ptr & 3) * 8))
+(AtomicOr8 ptr val mem) && !config.BigEndian ->
+	(LoweredAtomicOr (AND <config.fe.TypeUInt32().PtrTo()> (MOVWconst [^3]) ptr)
+		(SLL <config.fe.TypeUInt32()> (ZeroExt8to32 val)
+			(SLLconst <config.fe.TypeUInt32()> [3]
+				(ANDconst <config.fe.TypeUInt32()> [3] ptr))) mem)
+
+// AtomicAnd8(ptr,val) -> LoweredAtomicAnd(ptr&^3,(uint32(val) << ((ptr & 3) * 8)) | ^(uint32(0xFF) << ((ptr & 3) * 8))))
+(AtomicAnd8  ptr val mem) && !config.BigEndian ->
+	(LoweredAtomicAnd (AND <config.fe.TypeUInt32().PtrTo()> (MOVWconst [^3]) ptr)
+		(OR <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()> (ZeroExt8to32 val)
+			(SLLconst <config.fe.TypeUInt32()> [3]
+				(ANDconst  <config.fe.TypeUInt32()> [3] ptr)))
+		(NORconst [0] <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()>
+			(MOVWconst [0xff]) (SLLconst <config.fe.TypeUInt32()> [3]
+				(ANDconst <config.fe.TypeUInt32()> [3]
+					(XORconst <config.fe.TypeUInt32()> [3] ptr)))))) mem)
+
+// AtomicOr8(ptr,val) -> LoweredAtomicOr(ptr&^3,uint32(val) << (((ptr^3) & 3) * 8))
+(AtomicOr8 ptr val mem) && config.BigEndian ->
+	(LoweredAtomicOr (AND <config.fe.TypeUInt32().PtrTo()> (MOVWconst [^3]) ptr)
+		(SLL <config.fe.TypeUInt32()> (ZeroExt8to32 val)
+			(SLLconst <config.fe.TypeUInt32()> [3]
+				(ANDconst <config.fe.TypeUInt32()> [3]
+					(XORconst <config.fe.TypeUInt32()> [3] ptr)))) mem)
+
+// AtomicAnd8(ptr,val) -> LoweredAtomicAnd(ptr&^3,(uint32(val) << (((ptr^3) & 3) * 8)) | ^(uint32(0xFF) << (((ptr^3) & 3) * 8))))
+(AtomicAnd8  ptr val mem) && config.BigEndian ->
+	(LoweredAtomicAnd (AND <config.fe.TypeUInt32().PtrTo()> (MOVWconst [^3]) ptr)
+		(OR <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()> (ZeroExt8to32 val)
+			(SLLconst <config.fe.TypeUInt32()> [3]
+				(ANDconst  <config.fe.TypeUInt32()> [3]
+					(XORconst <config.fe.TypeUInt32()> [3] ptr))))
+		(NORconst [0] <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()>
+			(MOVWconst [0xff]) (SLLconst <config.fe.TypeUInt32()> [3]
+				(ANDconst <config.fe.TypeUInt32()> [3]
+					(XORconst <config.fe.TypeUInt32()> [3] ptr)))))) mem)
+
+
+// checks
+(NilCheck ptr mem) -> (LoweredNilCheck ptr mem)
+(IsNonNil ptr) -> (SGTU ptr (MOVWconst [0]))
+(IsInBounds idx len) -> (SGTU len idx)
+(IsSliceInBounds idx len) -> (XORconst [1] (SGTU idx len))
+
+// pseudo-ops
+(GetClosurePtr) -> (LoweredGetClosurePtr)
+(Convert x mem) -> (MOVWconvert x mem)
+
+(If cond yes no) -> (NE cond yes no)
+
+
+// Optimizations
+
+// Absorb boolean tests into block
+(NE (FPFlagTrue cmp) yes no) -> (FPT cmp yes no)
+(NE (FPFlagFalse cmp) yes no) -> (FPF cmp yes no)
+(EQ (FPFlagTrue cmp) yes no) -> (FPF cmp yes no)
+(EQ (FPFlagFalse cmp) yes no) -> (FPT cmp yes no)
+(NE (XORconst [1] cmp:(SGT _ _)) yes no) -> (EQ cmp yes no)
+(NE (XORconst [1] cmp:(SGTU _ _)) yes no) -> (EQ cmp yes no)
+(NE (XORconst [1] cmp:(SGTconst _)) yes no) -> (EQ cmp yes no)
+(NE (XORconst [1] cmp:(SGTUconst _)) yes no) -> (EQ cmp yes no)
+(NE (XORconst [1] cmp:(SGTzero _)) yes no) -> (EQ cmp yes no)
+(NE (XORconst [1] cmp:(SGTUzero _)) yes no) -> (EQ cmp yes no)
+(EQ (XORconst [1] cmp:(SGT _ _)) yes no) -> (NE cmp yes no)
+(EQ (XORconst [1] cmp:(SGTU _ _)) yes no) -> (NE cmp yes no)
+(EQ (XORconst [1] cmp:(SGTconst _)) yes no) -> (NE cmp yes no)
+(EQ (XORconst [1] cmp:(SGTUconst _)) yes no) -> (NE cmp yes no)
+(EQ (XORconst [1] cmp:(SGTzero _)) yes no) -> (NE cmp yes no)
+(EQ (XORconst [1] cmp:(SGTUzero _)) yes no) -> (NE cmp yes no)
+(NE (SGTUconst [1] x) yes no) -> (EQ x yes no)
+(EQ (SGTUconst [1] x) yes no) -> (NE x yes no)
+(NE (SGTUzero x) yes no) -> (NE x yes no)
+(EQ (SGTUzero x) yes no) -> (EQ x yes no)
+(NE (SGTconst [0] x) yes no) -> (LTZ x yes no)
+(EQ (SGTconst [0] x) yes no) -> (GEZ x yes no)
+(NE (SGTzero x) yes no) -> (GTZ x yes no)
+(EQ (SGTzero x) yes no) -> (LEZ x yes no)
+
+// fold offset into address
+(ADDconst [off1] (MOVWaddr [off2] {sym} ptr)) -> (MOVWaddr [off1+off2] {sym} ptr)
+
+// fold address into load/store
+(MOVBload  [off1] {sym} x:(ADDconst [off2] ptr) mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVBload  [off1+off2] {sym} ptr mem)
+(MOVBUload [off1] {sym} x:(ADDconst [off2] ptr) mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVBUload [off1+off2] {sym} ptr mem)
+(MOVHload  [off1] {sym} x:(ADDconst [off2] ptr) mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVHload  [off1+off2] {sym} ptr mem)
+(MOVHUload [off1] {sym} x:(ADDconst [off2] ptr) mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVHUload [off1+off2] {sym} ptr mem)
+(MOVWload  [off1] {sym} x:(ADDconst [off2] ptr) mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVWload  [off1+off2] {sym} ptr mem)
+(MOVFload  [off1] {sym} x:(ADDconst [off2] ptr) mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVFload  [off1+off2] {sym} ptr mem)
+(MOVDload  [off1] {sym} x:(ADDconst [off2] ptr) mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVDload  [off1+off2] {sym} ptr mem)
+
+(MOVBstore [off1] {sym} x:(ADDconst [off2] ptr) val mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVBstore [off1+off2] {sym} ptr val mem)
+(MOVHstore [off1] {sym} x:(ADDconst [off2] ptr) val mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVHstore [off1+off2] {sym} ptr val mem)
+(MOVWstore [off1] {sym} x:(ADDconst [off2] ptr) val mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVWstore [off1+off2] {sym} ptr val mem)
+(MOVFstore [off1] {sym} x:(ADDconst [off2] ptr) val mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVFstore [off1+off2] {sym} ptr val mem)
+(MOVDstore [off1] {sym} x:(ADDconst [off2] ptr) val mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVDstore [off1+off2] {sym} ptr val mem)
+
+(MOVBstorezero [off1] {sym} x:(ADDconst [off2] ptr) mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVBstorezero [off1+off2] {sym} ptr mem)
+(MOVHstorezero [off1] {sym} x:(ADDconst [off2] ptr) mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVHstorezero [off1+off2] {sym} ptr mem)
+(MOVWstorezero [off1] {sym} x:(ADDconst [off2] ptr) mem) && (is16Bit(off1+off2) || x.Uses == 1) -> (MOVWstorezero [off1+off2] {sym} ptr mem)
+
+(MOVBload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVBload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVBUload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVBUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHUload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVFload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVFload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVDload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+
+(MOVBstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVHstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVWstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVFstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVFstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVDstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+	(MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVBstorezero [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHstorezero [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWstorezero [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+	(MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+
+// replace load from same location as preceding store with copy
+(MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type) -> x
+(MOVBUload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type) -> x
+(MOVHload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type) -> x
+(MOVHUload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type) -> x
+(MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+(MOVFload [off] {sym} ptr (MOVFstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+(MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+
+// store zero
+(MOVBstore [off] {sym} ptr (MOVWconst [0]) mem) -> (MOVBstorezero [off] {sym} ptr mem)
+(MOVHstore [off] {sym} ptr (MOVWconst [0]) mem) -> (MOVHstorezero [off] {sym} ptr mem)
+(MOVWstore [off] {sym} ptr (MOVWconst [0]) mem) -> (MOVWstorezero [off] {sym} ptr mem)
+
+// don't extend after proper load
+(MOVBreg x:(MOVBload _ _)) -> (MOVWreg x)
+(MOVBUreg x:(MOVBUload _ _)) -> (MOVWreg x)
+(MOVHreg x:(MOVBload _ _)) -> (MOVWreg x)
+(MOVHreg x:(MOVBUload _ _)) -> (MOVWreg x)
+(MOVHreg x:(MOVHload _ _)) -> (MOVWreg x)
+(MOVHUreg x:(MOVBUload _ _)) -> (MOVWreg x)
+(MOVHUreg x:(MOVHUload _ _)) -> (MOVWreg x)
+
+// fold double extensions
+(MOVBreg x:(MOVBreg _)) -> (MOVWreg x)
+(MOVBUreg x:(MOVBUreg _)) -> (MOVWreg x)
+(MOVHreg x:(MOVBreg _)) -> (MOVWreg x)
+(MOVHreg x:(MOVBUreg _)) -> (MOVWreg x)
+(MOVHreg x:(MOVHreg _)) -> (MOVWreg x)
+(MOVHUreg x:(MOVBUreg _)) -> (MOVWreg x)
+(MOVHUreg x:(MOVHUreg _)) -> (MOVWreg x)
+
+// sign extended loads
+// Note: The combined instruction must end up in the same block
+// as the original load. If not, we end up making a value with
+// memory type live in two different blocks, which can lead to
+// multiple memory values alive simultaneously.
+// Make sure we don't combine these ops if the load has another use.
+// This prevents a single load from being split into multiple loads
+// which then might return different values.  See test/atomicload.go.
+(MOVBreg <t> x:(MOVBUload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <t> [off] {sym} ptr mem)
+(MOVBUreg <t> x:(MOVBload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBUload <t> [off] {sym} ptr mem)
+(MOVHreg <t> x:(MOVHUload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHload <t> [off] {sym} ptr mem)
+(MOVHUreg <t> x:(MOVHload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHUload <t> [off] {sym} ptr mem)
+
+// fold extensions and ANDs together
+(MOVBUreg (ANDconst [c] x)) -> (ANDconst [c&0xff] x)
+(MOVHUreg (ANDconst [c] x)) -> (ANDconst [c&0xffff] x)
+(MOVBreg (ANDconst [c] x)) && c & 0x80 == 0 -> (ANDconst [c&0x7f] x)
+(MOVHreg (ANDconst [c] x)) && c & 0x8000 == 0 -> (ANDconst [c&0x7fff] x)
+
+// don't extend before store
+(MOVBstore [off] {sym} ptr (MOVBreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVBUreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVHreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVHUreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHUreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVWstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
+
+// if a register move has only 1 use, just use the same register without emitting instruction
+// MOVWnop doesn't emit instruction, only for ensuring the type.
+(MOVWreg x) && x.Uses == 1 -> (MOVWnop x)
+
+// fold constant into arithmatic ops
+(ADD (MOVWconst [c]) x) -> (ADDconst [c] x)
+(ADD x (MOVWconst [c])) -> (ADDconst [c] x)
+(SUB x (MOVWconst [c])) -> (SUBconst [c] x)
+(AND (MOVWconst [c]) x) -> (ANDconst [c] x)
+(AND x (MOVWconst [c])) -> (ANDconst [c] x)
+(OR  (MOVWconst [c]) x) -> (ORconst  [c] x)
+(OR  x (MOVWconst [c])) -> (ORconst  [c] x)
+(XOR (MOVWconst [c]) x) -> (XORconst [c] x)
+(XOR x (MOVWconst [c])) -> (XORconst [c] x)
+(NOR (MOVWconst [c]) x) -> (NORconst [c] x)
+(NOR x (MOVWconst [c])) -> (NORconst [c] x)
+
+(SLL _ (MOVWconst [c])) && uint32(c)>=32 -> (MOVWconst [0])
+(SRL _ (MOVWconst [c])) && uint32(c)>=32 -> (MOVWconst [0])
+(SRA x (MOVWconst [c])) && uint32(c)>=32 -> (SRAconst x [31])
+(SLL x (MOVWconst [c])) -> (SLLconst x [c])
+(SRL x (MOVWconst [c])) -> (SRLconst x [c])
+(SRA x (MOVWconst [c])) -> (SRAconst x [c])
+
+(SGT  (MOVWconst [c]) x) -> (SGTconst  [c] x)
+(SGTU (MOVWconst [c]) x) -> (SGTUconst [c] x)
+(SGT x (MOVWconst [0])) -> (SGTzero x)
+(SGTU x (MOVWconst [0])) -> (SGTUzero x)
+
+// mul with constant
+(Select1 (MULTU x (MOVWconst [c]))) && x.Op != OpMIPSMOVWconst-> (Select1 (MULTU (MOVWconst [c]) x ))
+(Select0 (MULTU x (MOVWconst [c]))) && x.Op != OpMIPSMOVWconst-> (Select0 (MULTU (MOVWconst [c]) x ))
+
+(Select1 (MULTU (MOVWconst [0]) _ )) -> (MOVWconst [0])
+(Select0 (MULTU (MOVWconst [0]) _ )) -> (MOVWconst [0])
+(Select1 (MULTU (MOVWconst [1]) x )) -> x
+(Select0 (MULTU (MOVWconst [1]) _ )) -> (MOVWconst [0])
+(Select1 (MULTU (MOVWconst [-1]) x )) -> (NEG <x.Type> x)
+(Select0 (MULTU (MOVWconst [-1]) x )) -> (CMOVZ (ADDconst <x.Type> [-1] x) (MOVWconst [0]) x)
+(Select1 (MULTU (MOVWconst [c]) x )) && isPowerOfTwo(int64(uint32(c))) -> (SLLconst [log2(int64(uint32(c)))] x)
+(Select0 (MULTU (MOVWconst [c]) x )) && isPowerOfTwo(int64(uint32(c))) -> (SRLconst [32-log2(int64(uint32(c)))] x)
+
+(MUL (MOVWconst [0]) _ ) -> (MOVWconst [0])
+(MUL (MOVWconst [1]) x ) -> x
+(MUL (MOVWconst [-1]) x ) -> (NEG x)
+(MUL (MOVWconst [c]) x ) && isPowerOfTwo(int64(uint32(c))) -> (SLLconst [log2(int64(uint32(c)))] x)
+
+// generic simplifications
+(ADD x (NEG y)) -> (SUB x y)
+(ADD (NEG y) x) -> (SUB x y)
+(SUB x x) -> (MOVWconst [0])
+(SUB (MOVWconst [0]) x) -> (NEG x)
+(AND x x) -> x
+(OR  x x) -> x
+(XOR x x) -> (MOVWconst [0])
+
+// miscellaneous patterns generated by dec64
+(AND (SGTUconst [1] x) (SGTUconst [1] y)) ->  (SGTUconst [1] (OR <x.Type> x y))
+(OR (SGTUzero x) (SGTUzero y)) ->  (SGTUzero (OR <x.Type> x y))
+
+// remove redundant *const ops
+(ADDconst [0]  x) -> x
+(SUBconst [0]  x) -> x
+(ANDconst [0]  _) -> (MOVWconst [0])
+(ANDconst [-1] x) -> x
+(ORconst  [0]  x) -> x
+(ORconst  [-1] _) -> (MOVWconst [-1])
+(XORconst [0]  x) -> x
+(XORconst [-1] x) -> (NORconst [0] x)
+
+// generic constant folding
+(ADDconst [c] (MOVWconst [d]))  -> (MOVWconst [int64(int32(c+d))])
+(ADDconst [c] (ADDconst [d] x)) -> (ADDconst [int64(int32(c+d))] x)
+(ADDconst [c] (SUBconst [d] x)) -> (ADDconst [int64(int32(c-d))] x)
+(SUBconst [c] (MOVWconst [d]))  -> (MOVWconst [int64(int32(d-c))])
+(SUBconst [c] (SUBconst [d] x)) -> (ADDconst [int64(int32(-c-d))] x)
+(SUBconst [c] (ADDconst [d] x)) -> (ADDconst [int64(int32(-c+d))] x)
+(SLLconst [c] (MOVWconst [d]))  -> (MOVWconst [int64(int32(uint32(d)<<uint32(c)))])
+(SRLconst [c] (MOVWconst [d]))  -> (MOVWconst [int64(uint32(d)>>uint32(c))])
+(SRAconst [c] (MOVWconst [d]))  -> (MOVWconst [int64(int32(d)>>uint32(c))])
+(MUL (MOVWconst [c]) (MOVWconst [d])) -> (MOVWconst [int64(int32(c)*int32(d))])
+(Select1 (MULTU  (MOVWconst [c]) (MOVWconst [d]))) -> (MOVWconst [int64(int32(uint32(c)*uint32(d)))])
+(Select0 (MULTU  (MOVWconst [c]) (MOVWconst [d]))) -> (MOVWconst [(c*d)>>32])
+(Select1 (DIV  (MOVWconst [c]) (MOVWconst [d]))) -> (MOVWconst [int64(int32(c)/int32(d))])
+(Select1 (DIVU (MOVWconst [c]) (MOVWconst [d]))) -> (MOVWconst [int64(int32(uint32(c)/uint32(d)))])
+(Select0 (DIV  (MOVWconst [c]) (MOVWconst [d]))) -> (MOVWconst [int64(int32(c)%int32(d))])
+(Select0 (DIVU (MOVWconst [c]) (MOVWconst [d]))) -> (MOVWconst [int64(int32(uint32(c)%uint32(d)))])
+(ANDconst [c] (MOVWconst [d])) -> (MOVWconst [c&d])
+(ANDconst [c] (ANDconst [d] x)) -> (ANDconst [c&d] x)
+(ORconst [c] (MOVWconst [d])) -> (MOVWconst [c|d])
+(ORconst [c] (ORconst [d] x)) -> (ORconst [c|d] x)
+(XORconst [c] (MOVWconst [d])) -> (MOVWconst [c^d])
+(XORconst [c] (XORconst [d] x)) -> (XORconst [c^d] x)
+(NORconst [c] (MOVWconst [d])) -> (MOVWconst [^(c|d)])
+(NEG (MOVWconst [c])) -> (MOVWconst [int64(int32(-c))])
+(MOVBreg  (MOVWconst [c])) -> (MOVWconst [int64(int8(c))])
+(MOVBUreg (MOVWconst [c])) -> (MOVWconst [int64(uint8(c))])
+(MOVHreg  (MOVWconst [c])) -> (MOVWconst [int64(int16(c))])
+(MOVHUreg (MOVWconst [c])) -> (MOVWconst [int64(uint16(c))])
+(MOVWreg  (MOVWconst [c])) -> (MOVWconst [c])
+
+// constant comparisons
+(SGTconst [c] (MOVWconst [d])) && int32(c) > int32(d) -> (MOVWconst [1])
+(SGTconst [c] (MOVWconst [d])) && int32(c) <= int32(d) -> (MOVWconst [0])
+(SGTUconst [c] (MOVWconst [d])) && uint32(c)>uint32(d) -> (MOVWconst [1])
+(SGTUconst [c] (MOVWconst [d])) && uint32(c)<=uint32(d) -> (MOVWconst [0])
+(SGTzero (MOVWconst [d])) && int32(d) > 0 -> (MOVWconst [1])
+(SGTzero (MOVWconst [d])) && int32(d) <= 0 -> (MOVWconst [0])
+(SGTUzero (MOVWconst [d])) && uint32(d) != 0 -> (MOVWconst [1])
+(SGTUzero (MOVWconst [d])) && uint32(d) == 0 -> (MOVWconst [0])
+
+// other known comparisons
+(SGTconst [c] (MOVBreg _)) && 0x7f < int32(c) -> (MOVWconst [1])
+(SGTconst [c] (MOVBreg _)) && int32(c) <= -0x80 -> (MOVWconst [0])
+(SGTconst [c] (MOVBUreg _)) && 0xff < int32(c) -> (MOVWconst [1])
+(SGTconst [c] (MOVBUreg _)) && int32(c) < 0 -> (MOVWconst [0])
+(SGTUconst [c] (MOVBUreg _)) && 0xff < uint32(c) -> (MOVWconst [1])
+(SGTconst [c] (MOVHreg _)) && 0x7fff < int32(c) -> (MOVWconst [1])
+(SGTconst [c] (MOVHreg _)) && int32(c) <= -0x8000 -> (MOVWconst [0])
+(SGTconst [c] (MOVHUreg _)) && 0xffff < int32(c) -> (MOVWconst [1])
+(SGTconst [c] (MOVHUreg _)) && int32(c) < 0 -> (MOVWconst [0])
+(SGTUconst [c] (MOVHUreg _)) && 0xffff < uint32(c) -> (MOVWconst [1])
+(SGTconst [c] (ANDconst [m] _)) && 0 <= int32(m) && int32(m) < int32(c) -> (MOVWconst [1])
+(SGTUconst [c] (ANDconst [m] _)) && uint32(m) < uint32(c) -> (MOVWconst [1])
+(SGTconst [c] (SRLconst _ [d])) && 0 <= int32(c) && uint32(d) <= 31 && 1<<(32-uint32(d)) <= int32(c) -> (MOVWconst [1])
+(SGTUconst [c] (SRLconst _ [d])) && uint32(d) <= 31 && 1<<(32-uint32(d)) <= uint32(c) -> (MOVWconst [1])
+
+// absorb constants into branches
+(EQ  (MOVWconst [0]) yes no) -> (First nil yes no)
+(EQ  (MOVWconst [c]) yes no) && c != 0 -> (First nil no yes)
+(NE  (MOVWconst [0]) yes no) -> (First nil no yes)
+(NE  (MOVWconst [c]) yes no) && c != 0 -> (First nil yes no)
+(LTZ (MOVWconst [c]) yes no) && int32(c) <  0 -> (First nil yes no)
+(LTZ (MOVWconst [c]) yes no) && int32(c) >= 0 -> (First nil no yes)
+(LEZ (MOVWconst [c]) yes no) && int32(c) <= 0 -> (First nil yes no)
+(LEZ (MOVWconst [c]) yes no) && int32(c) >  0 -> (First nil no yes)
+(GTZ (MOVWconst [c]) yes no) && int32(c) >  0 -> (First nil yes no)
+(GTZ (MOVWconst [c]) yes no) && int32(c) <= 0 -> (First nil no yes)
+(GEZ (MOVWconst [c]) yes no) && int32(c) >= 0 -> (First nil yes no)
+(GEZ (MOVWconst [c]) yes no) && int32(c) <  0 -> (First nil no yes)
+
+// conditional move
+(CMOVZ _ b (MOVWconst [0])) -> b
+(CMOVZ a _ (MOVWconst [c])) && c!=0-> a
+(CMOVZzero _ (MOVWconst [0])) -> (MOVWconst [0])
+(CMOVZzero a (MOVWconst [c])) && c!=0-> a
+(CMOVZ a (MOVWconst [0]) c) -> (CMOVZzero a c)
+
+// atomic
+(LoweredAtomicStore ptr (MOVWconst [0]) mem) -> (LoweredAtomicStorezero ptr mem)
+(LoweredAtomicAdd ptr (MOVWconst [c]) mem) && is16Bit(c)-> (LoweredAtomicAddconst [c] ptr mem)
+
diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64.rules b/src/cmd/compile/internal/ssa/gen/MIPS64.rules
new file mode 100644
index 0000000..7a496be
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/MIPS64.rules
@@ -0,0 +1,708 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+(AddPtr x y) -> (ADDV x y)
+(Add64 x y) -> (ADDV x y)
+(Add32 x y) -> (ADDV x y)
+(Add16 x y) -> (ADDV x y)
+(Add8 x y) -> (ADDV x y)
+(Add32F x y) -> (ADDF x y)
+(Add64F x y) -> (ADDD x y)
+
+(SubPtr x y) -> (SUBV x y)
+(Sub64 x y) -> (SUBV x y)
+(Sub32 x y) -> (SUBV x y)
+(Sub16 x y) -> (SUBV x y)
+(Sub8 x y) -> (SUBV x y)
+(Sub32F x y) -> (SUBF x y)
+(Sub64F x y) -> (SUBD x y)
+
+(Mul64 x y) -> (Select1 (MULVU x y))
+(Mul32 x y) -> (Select1 (MULVU x y))
+(Mul16 x y) -> (Select1 (MULVU x y))
+(Mul8 x y) -> (Select1 (MULVU x y))
+(Mul32F x y) -> (MULF x y)
+(Mul64F x y) -> (MULD x y)
+
+(Hmul64 x y) -> (Select0 (MULV x y))
+(Hmul64u x y) -> (Select0 (MULVU x y))
+(Hmul32 x y) -> (SRAVconst (Select1 <config.fe.TypeInt64()> (MULV (SignExt32to64 x) (SignExt32to64 y))) [32])
+(Hmul32u x y) -> (SRLVconst (Select1 <config.fe.TypeUInt64()> (MULVU (ZeroExt32to64 x) (ZeroExt32to64 y))) [32])
+(Hmul16 x y) -> (SRAVconst (Select1 <config.fe.TypeInt32()> (MULV (SignExt16to64 x) (SignExt16to64 y))) [16])
+(Hmul16u x y) -> (SRLVconst (Select1 <config.fe.TypeUInt32()> (MULVU (ZeroExt16to64 x) (ZeroExt16to64 y))) [16])
+(Hmul8 x y) -> (SRAVconst (Select1 <config.fe.TypeInt16()> (MULV (SignExt8to64 x) (SignExt8to64 y))) [8])
+(Hmul8u x y) -> (SRLVconst (Select1 <config.fe.TypeUInt16()> (MULVU (ZeroExt8to64 x) (ZeroExt8to64 y))) [8])
+
+(Div64 x y) -> (Select1 (DIVV x y))
+(Div64u x y) -> (Select1 (DIVVU x y))
+(Div32 x y) -> (Select1 (DIVV (SignExt32to64 x) (SignExt32to64 y)))
+(Div32u x y) -> (Select1 (DIVVU (ZeroExt32to64 x) (ZeroExt32to64 y)))
+(Div16 x y) -> (Select1 (DIVV (SignExt16to64 x) (SignExt16to64 y)))
+(Div16u x y) -> (Select1 (DIVVU (ZeroExt16to64 x) (ZeroExt16to64 y)))
+(Div8 x y) -> (Select1 (DIVV (SignExt8to64 x) (SignExt8to64 y)))
+(Div8u x y) -> (Select1 (DIVVU (ZeroExt8to64 x) (ZeroExt8to64 y)))
+(Div32F x y) -> (DIVF x y)
+(Div64F x y) -> (DIVD x y)
+
+(Mod64 x y) -> (Select0 (DIVV x y))
+(Mod64u x y) -> (Select0 (DIVVU x y))
+(Mod32 x y) -> (Select0 (DIVV (SignExt32to64 x) (SignExt32to64 y)))
+(Mod32u x y) -> (Select0 (DIVVU (ZeroExt32to64 x) (ZeroExt32to64 y)))
+(Mod16 x y) -> (Select0 (DIVV (SignExt16to64 x) (SignExt16to64 y)))
+(Mod16u x y) -> (Select0 (DIVVU (ZeroExt16to64 x) (ZeroExt16to64 y)))
+(Mod8 x y) -> (Select0 (DIVV (SignExt8to64 x) (SignExt8to64 y)))
+(Mod8u x y) -> (Select0 (DIVVU (ZeroExt8to64 x) (ZeroExt8to64 y)))
+
+(Avg64u <t> x y) -> (ADDV (ADDV <t> (SRLVconst <t> x [1]) (SRLVconst <t> y [1])) (AND <t> (AND <t> x y) (MOVVconst [1])))
+
+(And64 x y) -> (AND x y)
+(And32 x y) -> (AND x y)
+(And16 x y) -> (AND x y)
+(And8 x y) -> (AND x y)
+
+(Or64 x y) -> (OR x y)
+(Or32 x y) -> (OR x y)
+(Or16 x y) -> (OR x y)
+(Or8 x y) -> (OR x y)
+
+(Xor64 x y) -> (XOR x y)
+(Xor32 x y) -> (XOR x y)
+(Xor16 x y) -> (XOR x y)
+(Xor8 x y) -> (XOR x y)
+
+// shifts
+// hardware instruction uses only the low 6 bits of the shift
+// we compare to 64 to ensure Go semantics for large shifts
+(Lsh64x64 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SLLV <t> x y))
+(Lsh64x32 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SLLV <t> x (ZeroExt32to64 y)))
+(Lsh64x16 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SLLV <t> x (ZeroExt16to64 y)))
+(Lsh64x8  <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SLLV <t> x (ZeroExt8to64  y)))
+
+(Lsh32x64 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SLLV <t> x y))
+(Lsh32x32 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SLLV <t> x (ZeroExt32to64 y)))
+(Lsh32x16 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SLLV <t> x (ZeroExt16to64 y)))
+(Lsh32x8  <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SLLV <t> x (ZeroExt8to64  y)))
+
+(Lsh16x64 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SLLV <t> x y))
+(Lsh16x32 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SLLV <t> x (ZeroExt32to64 y)))
+(Lsh16x16 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SLLV <t> x (ZeroExt16to64 y)))
+(Lsh16x8  <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SLLV <t> x (ZeroExt8to64  y)))
+
+(Lsh8x64 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SLLV <t> x y))
+(Lsh8x32 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SLLV <t> x (ZeroExt32to64 y)))
+(Lsh8x16 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SLLV <t> x (ZeroExt16to64 y)))
+(Lsh8x8  <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SLLV <t> x (ZeroExt8to64  y)))
+
+(Rsh64Ux64 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SRLV <t> x y))
+(Rsh64Ux32 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SRLV <t> x (ZeroExt32to64 y)))
+(Rsh64Ux16 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SRLV <t> x (ZeroExt16to64 y)))
+(Rsh64Ux8  <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SRLV <t> x (ZeroExt8to64  y)))
+
+(Rsh32Ux64 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SRLV <t> (ZeroExt32to64 x) y))
+(Rsh32Ux32 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SRLV <t> (ZeroExt32to64 x) (ZeroExt32to64 y)))
+(Rsh32Ux16 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SRLV <t> (ZeroExt32to64 x) (ZeroExt16to64 y)))
+(Rsh32Ux8  <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SRLV <t> (ZeroExt32to64 x) (ZeroExt8to64  y)))
+
+(Rsh16Ux64 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SRLV <t> (ZeroExt16to64 x) y))
+(Rsh16Ux32 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SRLV <t> (ZeroExt16to64 x) (ZeroExt32to64 y)))
+(Rsh16Ux16 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SRLV <t> (ZeroExt16to64 x) (ZeroExt16to64 y)))
+(Rsh16Ux8  <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SRLV <t> (ZeroExt16to64 x) (ZeroExt8to64  y)))
+
+(Rsh8Ux64 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SRLV <t> (ZeroExt8to64 x) y))
+(Rsh8Ux32 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SRLV <t> (ZeroExt8to64 x) (ZeroExt32to64 y)))
+(Rsh8Ux16 <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SRLV <t> (ZeroExt8to64 x) (ZeroExt16to64 y)))
+(Rsh8Ux8  <t> x y) -> (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SRLV <t> (ZeroExt8to64 x) (ZeroExt8to64  y)))
+
+(Rsh64x64 <t> x y) -> (SRAV x (OR <t> (NEGV <t> (SGTU y (Const64 <config.fe.TypeUInt64()> [63]))) y))
+(Rsh64x32 <t> x y) -> (SRAV x (OR <t> (NEGV <t> (SGTU (ZeroExt32to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt32to64 y)))
+(Rsh64x16 <t> x y) -> (SRAV x (OR <t> (NEGV <t> (SGTU (ZeroExt16to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt16to64 y)))
+(Rsh64x8  <t> x y) -> (SRAV x (OR <t> (NEGV <t> (SGTU (ZeroExt8to64  y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt8to64  y)))
+
+(Rsh32x64 <t> x y) -> (SRAV (SignExt32to64 x) (OR <t> (NEGV <t> (SGTU y (Const64 <config.fe.TypeUInt64()> [63]))) y))
+(Rsh32x32 <t> x y) -> (SRAV (SignExt32to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt32to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt32to64 y)))
+(Rsh32x16 <t> x y) -> (SRAV (SignExt32to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt16to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt16to64 y)))
+(Rsh32x8  <t> x y) -> (SRAV (SignExt32to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt8to64  y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt8to64  y)))
+
+(Rsh16x64 <t> x y) -> (SRAV (SignExt16to64 x) (OR <t> (NEGV <t> (SGTU y (Const64 <config.fe.TypeUInt64()> [63]))) y))
+(Rsh16x32 <t> x y) -> (SRAV (SignExt16to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt32to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt32to64 y)))
+(Rsh16x16 <t> x y) -> (SRAV (SignExt16to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt16to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt16to64 y)))
+(Rsh16x8  <t> x y) -> (SRAV (SignExt16to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt8to64  y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt8to64  y)))
+
+(Rsh8x64 <t> x y) -> (SRAV (SignExt8to64 x) (OR <t> (NEGV <t> (SGTU y (Const64 <config.fe.TypeUInt64()> [63]))) y))
+(Rsh8x32 <t> x y) -> (SRAV (SignExt8to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt32to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt32to64 y)))
+(Rsh8x16 <t> x y) -> (SRAV (SignExt8to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt16to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt16to64 y)))
+(Rsh8x8  <t> x y) -> (SRAV (SignExt8to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt8to64  y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt8to64  y)))
+
+// unary ops
+(Neg64 x) -> (NEGV x)
+(Neg32 x) -> (NEGV x)
+(Neg16 x) -> (NEGV x)
+(Neg8 x) -> (NEGV x)
+(Neg32F x) -> (NEGF x)
+(Neg64F x) -> (NEGD x)
+
+(Com64 x) -> (NOR (MOVVconst [0]) x)
+(Com32 x) -> (NOR (MOVVconst [0]) x)
+(Com16 x) -> (NOR (MOVVconst [0]) x)
+(Com8 x) -> (NOR (MOVVconst [0]) x)
+
+// boolean ops -- booleans are represented with 0=false, 1=true
+(AndB x y) -> (AND x y)
+(OrB x y) -> (OR x y)
+(EqB x y) -> (XOR (MOVVconst [1]) (XOR <config.fe.TypeBool()> x y))
+(NeqB x y) -> (XOR x y)
+(Not x) -> (XORconst [1] x)
+
+// constants
+(Const64 [val]) -> (MOVVconst [val])
+(Const32 [val]) -> (MOVVconst [val])
+(Const16 [val]) -> (MOVVconst [val])
+(Const8 [val]) -> (MOVVconst [val])
+(Const32F [val]) -> (MOVFconst [val])
+(Const64F [val]) -> (MOVDconst [val])
+(ConstNil) -> (MOVVconst [0])
+(ConstBool [b]) -> (MOVVconst [b])
+
+(Slicemask <t> x) -> (NORconst [0] (SRAVconst <t> (SUBVconst <t> x [1]) [63]))
+
+// truncations
+// Because we ignore high parts of registers, truncates are just copies.
+(Trunc16to8 x) -> x
+(Trunc32to8 x) -> x
+(Trunc32to16 x) -> x
+(Trunc64to8 x) -> x
+(Trunc64to16 x) -> x
+(Trunc64to32 x) -> x
+
+// Zero-/Sign-extensions
+(ZeroExt8to16 x) -> (MOVBUreg x)
+(ZeroExt8to32 x) -> (MOVBUreg x)
+(ZeroExt16to32 x) -> (MOVHUreg x)
+(ZeroExt8to64 x) -> (MOVBUreg x)
+(ZeroExt16to64 x) -> (MOVHUreg x)
+(ZeroExt32to64 x) -> (MOVWUreg x)
+
+(SignExt8to16 x) -> (MOVBreg x)
+(SignExt8to32 x) -> (MOVBreg x)
+(SignExt16to32 x) -> (MOVHreg x)
+(SignExt8to64 x) -> (MOVBreg x)
+(SignExt16to64 x) -> (MOVHreg x)
+(SignExt32to64 x) -> (MOVWreg x)
+
+// float <-> int conversion
+(Cvt32to32F x) -> (MOVWF x)
+(Cvt32to64F x) -> (MOVWD x)
+(Cvt64to32F x) -> (MOVVF x)
+(Cvt64to64F x) -> (MOVVD x)
+(Cvt32Fto32 x) -> (TRUNCFW x)
+(Cvt64Fto32 x) -> (TRUNCDW x)
+(Cvt32Fto64 x) -> (TRUNCFV x)
+(Cvt64Fto64 x) -> (TRUNCDV x)
+(Cvt32Fto64F x) -> (MOVFD x)
+(Cvt64Fto32F x) -> (MOVDF x)
+
+// comparisons
+(Eq8 x y)  -> (SGTU (MOVVconst [1]) (XOR (ZeroExt8to64 x) (ZeroExt8to64 y)))
+(Eq16 x y) -> (SGTU (MOVVconst [1]) (XOR (ZeroExt16to64 x) (ZeroExt16to64 y)))
+(Eq32 x y) -> (SGTU (MOVVconst [1]) (XOR (ZeroExt32to64 x) (ZeroExt32to64 y)))
+(Eq64 x y) -> (SGTU (MOVVconst [1]) (XOR x y))
+(EqPtr x y) -> (SGTU (MOVVconst [1]) (XOR x y))
+(Eq32F x y) -> (FPFlagTrue (CMPEQF x y))
+(Eq64F x y) -> (FPFlagTrue (CMPEQD x y))
+
+(Neq8 x y)  -> (SGTU (XOR (ZeroExt8to64 x) (ZeroExt8to64 y)) (MOVVconst [0]))
+(Neq16 x y) -> (SGTU (XOR (ZeroExt16to32 x) (ZeroExt16to64 y)) (MOVVconst [0]))
+(Neq32 x y) -> (SGTU (XOR (ZeroExt32to64 x) (ZeroExt32to64 y)) (MOVVconst [0]))
+(Neq64 x y) -> (SGTU (XOR x y) (MOVVconst [0]))
+(NeqPtr x y) -> (SGTU (XOR x y) (MOVVconst [0]))
+(Neq32F x y) -> (FPFlagFalse (CMPEQF x y))
+(Neq64F x y) -> (FPFlagFalse (CMPEQD x y))
+
+(Less8 x y)  -> (SGT (SignExt8to64 y) (SignExt8to64 x))
+(Less16 x y) -> (SGT (SignExt16to64 y) (SignExt16to64 x))
+(Less32 x y) -> (SGT (SignExt32to64 y) (SignExt32to64 x))
+(Less64 x y) -> (SGT y x)
+(Less32F x y) -> (FPFlagTrue (CMPGTF y x)) // reverse operands to work around NaN
+(Less64F x y) -> (FPFlagTrue (CMPGTD y x)) // reverse operands to work around NaN
+
+(Less8U x y)  -> (SGTU (ZeroExt8to64 y) (ZeroExt8to64 x))
+(Less16U x y) -> (SGTU (ZeroExt16to64 y) (ZeroExt16to64 x))
+(Less32U x y) -> (SGTU (ZeroExt32to64 y) (ZeroExt32to64 x))
+(Less64U x y) -> (SGTU y x)
+
+(Leq8 x y)  -> (XOR (MOVVconst [1]) (SGT (SignExt8to64 x) (SignExt8to64 y)))
+(Leq16 x y) -> (XOR (MOVVconst [1]) (SGT (SignExt16to64 x) (SignExt16to64 y)))
+(Leq32 x y) -> (XOR (MOVVconst [1]) (SGT (SignExt32to64 x) (SignExt32to64 y)))
+(Leq64 x y) -> (XOR (MOVVconst [1]) (SGT x y))
+(Leq32F x y) -> (FPFlagTrue (CMPGEF y x)) // reverse operands to work around NaN
+(Leq64F x y) -> (FPFlagTrue (CMPGED y x)) // reverse operands to work around NaN
+
+(Leq8U x y)  -> (XOR (MOVVconst [1]) (SGTU (ZeroExt8to64 x) (ZeroExt8to64 y)))
+(Leq16U x y) -> (XOR (MOVVconst [1]) (SGTU (ZeroExt16to64 x) (ZeroExt16to64 y)))
+(Leq32U x y) -> (XOR (MOVVconst [1]) (SGTU (ZeroExt32to64 x) (ZeroExt32to64 y)))
+(Leq64U x y) -> (XOR (MOVVconst [1]) (SGTU x y))
+
+(Greater8 x y)  -> (SGT (SignExt8to64 x) (SignExt8to64 y))
+(Greater16 x y) -> (SGT (SignExt16to64 x) (SignExt16to64 y))
+(Greater32 x y) -> (SGT (SignExt32to64 x) (SignExt32to64 y))
+(Greater64 x y) -> (SGT x y)
+(Greater32F x y) -> (FPFlagTrue (CMPGTF x y))
+(Greater64F x y) -> (FPFlagTrue (CMPGTD x y))
+
+(Greater8U x y)  -> (SGTU (ZeroExt8to64 x) (ZeroExt8to64 y))
+(Greater16U x y) -> (SGTU (ZeroExt16to64 x) (ZeroExt16to64 y))
+(Greater32U x y) -> (SGTU (ZeroExt32to64 x) (ZeroExt32to64 y))
+(Greater64U x y) -> (SGTU x y)
+
+(Geq8 x y)  -> (XOR (MOVVconst [1]) (SGT (SignExt8to64 y) (SignExt8to64 x)))
+(Geq16 x y) -> (XOR (MOVVconst [1]) (SGT (SignExt16to64 y) (SignExt16to64 x)))
+(Geq32 x y) -> (XOR (MOVVconst [1]) (SGT (SignExt32to64 y) (SignExt32to64 x)))
+(Geq64 x y) -> (XOR (MOVVconst [1]) (SGT y x))
+(Geq32F x y) -> (FPFlagTrue (CMPGEF x y))
+(Geq64F x y) -> (FPFlagTrue (CMPGED x y))
+
+(Geq8U x y)  -> (XOR (MOVVconst [1]) (SGTU (ZeroExt8to64 y) (ZeroExt8to64 x)))
+(Geq16U x y) -> (XOR (MOVVconst [1]) (SGTU (ZeroExt16to64 y) (ZeroExt16to64 x)))
+(Geq32U x y) -> (XOR (MOVVconst [1]) (SGTU (ZeroExt32to64 y) (ZeroExt32to64 x)))
+(Geq64U x y) -> (XOR (MOVVconst [1]) (SGTU y x))
+
+(OffPtr [off] ptr:(SP)) -> (MOVVaddr [off] ptr)
+(OffPtr [off] ptr) -> (ADDVconst [off] ptr)
+
+(Addr {sym} base) -> (MOVVaddr {sym} base)
+
+// loads
+(Load <t> ptr mem) && t.IsBoolean() -> (MOVBUload ptr mem)
+(Load <t> ptr mem) && (is8BitInt(t) && isSigned(t)) -> (MOVBload ptr mem)
+(Load <t> ptr mem) && (is8BitInt(t) && !isSigned(t)) -> (MOVBUload ptr mem)
+(Load <t> ptr mem) && (is16BitInt(t) && isSigned(t)) -> (MOVHload ptr mem)
+(Load <t> ptr mem) && (is16BitInt(t) && !isSigned(t)) -> (MOVHUload ptr mem)
+(Load <t> ptr mem) && (is32BitInt(t) && isSigned(t)) -> (MOVWload ptr mem)
+(Load <t> ptr mem) && (is32BitInt(t) && !isSigned(t)) -> (MOVWUload ptr mem)
+(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVVload ptr mem)
+(Load <t> ptr mem) && is32BitFloat(t) -> (MOVFload ptr mem)
+(Load <t> ptr mem) && is64BitFloat(t) -> (MOVDload ptr mem)
+
+// stores
+(Store [1] ptr val mem) -> (MOVBstore ptr val mem)
+(Store [2] ptr val mem) -> (MOVHstore ptr val mem)
+(Store [4] ptr val mem) && !is32BitFloat(val.Type) -> (MOVWstore ptr val mem)
+(Store [8] ptr val mem) && !is64BitFloat(val.Type) -> (MOVVstore ptr val mem)
+(Store [4] ptr val mem) && is32BitFloat(val.Type) -> (MOVFstore ptr val mem)
+(Store [8] ptr val mem) && is64BitFloat(val.Type) -> (MOVDstore ptr val mem)
+
+// zeroing
+(Zero [s] _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore ptr (MOVVconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore ptr (MOVVconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 2 ->
+	(MOVBstore [1] ptr (MOVVconst [0])
+		(MOVBstore [0] ptr (MOVVconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore ptr (MOVVconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [2] ptr (MOVVconst [0])
+		(MOVHstore [0] ptr (MOVVconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 ->
+	(MOVBstore [3] ptr (MOVVconst [0])
+		(MOVBstore [2] ptr (MOVVconst [0])
+			(MOVBstore [1] ptr (MOVVconst [0])
+				(MOVBstore [0] ptr (MOVVconst [0]) mem))))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVVstore ptr (MOVVconst [0]) mem)
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore [4] ptr (MOVVconst [0])
+		(MOVWstore [0] ptr (MOVVconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 4 ->
+	(MOVHstore [6] ptr (MOVVconst [0])
+		(MOVHstore [4] ptr (MOVVconst [0])
+			(MOVHstore [2] ptr (MOVVconst [0])
+				(MOVHstore [0] ptr (MOVVconst [0]) mem))))
+
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] ptr (MOVVconst [0])
+		(MOVBstore [1] ptr (MOVVconst [0])
+			(MOVBstore [0] ptr (MOVVconst [0]) mem)))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [4] ptr (MOVVconst [0])
+		(MOVHstore [2] ptr (MOVVconst [0])
+			(MOVHstore [0] ptr (MOVVconst [0]) mem)))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore [8] ptr (MOVVconst [0])
+		(MOVWstore [4] ptr (MOVVconst [0])
+			(MOVWstore [0] ptr (MOVVconst [0]) mem)))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVVstore [8] ptr (MOVVconst [0])
+		(MOVVstore [0] ptr (MOVVconst [0]) mem))
+(Zero [s] ptr mem) && SizeAndAlign(s).Size() == 24 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVVstore [16] ptr (MOVVconst [0])
+		(MOVVstore [8] ptr (MOVVconst [0])
+			(MOVVstore [0] ptr (MOVVconst [0]) mem)))
+
+// medium zeroing uses a duff device
+// 8, and 128 are magic constants, see runtime/mkduff.go
+(Zero [s] ptr mem)
+	&& SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size() <= 8*128
+	&& SizeAndAlign(s).Align()%8 == 0 && !config.noDuffDevice ->
+	(DUFFZERO [8 * (128 - int64(SizeAndAlign(s).Size()/8))] ptr mem)
+
+// large or unaligned zeroing uses a loop
+(Zero [s] ptr mem)
+	&& (SizeAndAlign(s).Size() > 8*128 || config.noDuffDevice) || SizeAndAlign(s).Align()%8 != 0 ->
+	(LoweredZero [SizeAndAlign(s).Align()]
+		ptr
+		(ADDVconst <ptr.Type> ptr [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)])
+		mem)
+
+// moves
+(Move [s] _ _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore dst (MOVBload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore dst (MOVHload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 ->
+	(MOVBstore [1] dst (MOVBload [1] src mem)
+		(MOVBstore dst (MOVBload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore dst (MOVWload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [2] dst (MOVHload [2] src mem)
+		(MOVHstore dst (MOVHload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 ->
+	(MOVBstore [3] dst (MOVBload [3] src mem)
+		(MOVBstore [2] dst (MOVBload [2] src mem)
+			(MOVBstore [1] dst (MOVBload [1] src mem)
+				(MOVBstore dst (MOVBload src mem) mem))))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVVstore dst (MOVVload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore [4] dst (MOVWload [4] src mem)
+		(MOVWstore dst (MOVWload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [6] dst (MOVHload [6] src mem)
+		(MOVHstore [4] dst (MOVHload [4] src mem)
+			(MOVHstore [2] dst (MOVHload [2] src mem)
+				(MOVHstore dst (MOVHload src mem) mem))))
+
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] dst (MOVBload [2] src mem)
+		(MOVBstore [1] dst (MOVBload [1] src mem)
+			(MOVBstore dst (MOVBload src mem) mem)))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [4] dst (MOVHload [4] src mem)
+		(MOVHstore [2] dst (MOVHload [2] src mem)
+			(MOVHstore dst (MOVHload src mem) mem)))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore [8] dst (MOVWload [8] src mem)
+		(MOVWstore [4] dst (MOVWload [4] src mem)
+			(MOVWstore dst (MOVWload src mem) mem)))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVVstore [8] dst (MOVVload [8] src mem)
+		(MOVVstore dst (MOVVload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 24 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVVstore [16] dst (MOVVload [16] src mem)
+		(MOVVstore [8] dst (MOVVload [8] src mem)
+			(MOVVstore dst (MOVVload src mem) mem)))
+
+// large or unaligned move uses a loop
+(Move [s] dst src mem)
+	&& SizeAndAlign(s).Size() > 24 || SizeAndAlign(s).Align()%8 != 0 ->
+	(LoweredMove [SizeAndAlign(s).Align()]
+		dst
+		src
+		(ADDVconst <src.Type> src [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)])
+		mem)
+
+// calls
+(StaticCall [argwid] {target} mem) -> (CALLstatic [argwid] {target} mem)
+(ClosureCall [argwid] entry closure mem) -> (CALLclosure [argwid] entry closure mem)
+(DeferCall [argwid] mem) -> (CALLdefer [argwid] mem)
+(GoCall [argwid] mem) -> (CALLgo [argwid] mem)
+(InterCall [argwid] entry mem) -> (CALLinter [argwid] entry mem)
+
+// checks
+(NilCheck ptr mem) -> (LoweredNilCheck ptr mem)
+(IsNonNil ptr) -> (SGTU ptr (MOVVconst [0]))
+(IsInBounds idx len) -> (SGTU len idx)
+(IsSliceInBounds idx len) -> (XOR (MOVVconst [1]) (SGTU idx len))
+
+// pseudo-ops
+(GetClosurePtr) -> (LoweredGetClosurePtr)
+(Convert x mem) -> (MOVVconvert x mem)
+
+(If cond yes no) -> (NE cond yes no)
+
+// Optimizations
+
+// Absorb boolean tests into block
+(NE (FPFlagTrue cmp) yes no) -> (FPT cmp yes no)
+(NE (FPFlagFalse cmp) yes no) -> (FPF cmp yes no)
+(EQ (FPFlagTrue cmp) yes no) -> (FPF cmp yes no)
+(EQ (FPFlagFalse cmp) yes no) -> (FPT cmp yes no)
+(NE (XORconst [1] cmp:(SGT _ _)) yes no) -> (EQ cmp yes no)
+(NE (XORconst [1] cmp:(SGTU _ _)) yes no) -> (EQ cmp yes no)
+(NE (XORconst [1] cmp:(SGTconst _)) yes no) -> (EQ cmp yes no)
+(NE (XORconst [1] cmp:(SGTUconst _)) yes no) -> (EQ cmp yes no)
+(EQ (XORconst [1] cmp:(SGT _ _)) yes no) -> (NE cmp yes no)
+(EQ (XORconst [1] cmp:(SGTU _ _)) yes no) -> (NE cmp yes no)
+(EQ (XORconst [1] cmp:(SGTconst _)) yes no) -> (NE cmp yes no)
+(EQ (XORconst [1] cmp:(SGTUconst _)) yes no) -> (NE cmp yes no)
+(NE (SGTUconst [1] x) yes no) -> (EQ x yes no)
+(EQ (SGTUconst [1] x) yes no) -> (NE x yes no)
+(NE (SGTU x (MOVVconst [0])) yes no) -> (NE x yes no)
+(EQ (SGTU x (MOVVconst [0])) yes no) -> (EQ x yes no)
+(NE (SGTconst [0] x) yes no) -> (LTZ x yes no)
+(EQ (SGTconst [0] x) yes no) -> (GEZ x yes no)
+(NE (SGT x (MOVVconst [0])) yes no) -> (GTZ x yes no)
+(EQ (SGT x (MOVVconst [0])) yes no) -> (LEZ x yes no)
+
+// fold offset into address
+(ADDVconst [off1] (MOVVaddr [off2] {sym} ptr)) -> (MOVVaddr [off1+off2] {sym} ptr)
+
+// fold address into load/store
+(MOVBload  [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVBload  [off1+off2] {sym} ptr mem)
+(MOVBUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVBUload [off1+off2] {sym} ptr mem)
+(MOVHload  [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVHload  [off1+off2] {sym} ptr mem)
+(MOVHUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVHUload [off1+off2] {sym} ptr mem)
+(MOVWload  [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVWload  [off1+off2] {sym} ptr mem)
+(MOVWUload [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVWUload [off1+off2] {sym} ptr mem)
+(MOVVload  [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVVload  [off1+off2] {sym} ptr mem)
+(MOVFload  [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVFload  [off1+off2] {sym} ptr mem)
+(MOVDload  [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVDload  [off1+off2] {sym} ptr mem)
+
+(MOVBstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVBstore [off1+off2] {sym} ptr val mem)
+(MOVHstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVHstore [off1+off2] {sym} ptr val mem)
+(MOVWstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVWstore [off1+off2] {sym} ptr val mem)
+(MOVVstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVVstore [off1+off2] {sym} ptr val mem)
+(MOVFstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVFstore [off1+off2] {sym} ptr val mem)
+(MOVDstore [off1] {sym} (ADDVconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOVDstore [off1+off2] {sym} ptr val mem)
+(MOVBstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVBstorezero [off1+off2] {sym} ptr mem)
+(MOVHstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVHstorezero [off1+off2] {sym} ptr mem)
+(MOVWstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVWstorezero [off1+off2] {sym} ptr mem)
+(MOVVstorezero [off1] {sym} (ADDVconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOVVstorezero [off1+off2] {sym} ptr mem)
+
+(MOVBload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVBload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVBUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVBUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVWUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVVload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVVload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVFload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVFload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVDload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+
+(MOVBstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVHstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVWstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVVstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVVstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVFstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVFstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVDstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVBstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVVstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(off1+off2) ->
+	(MOVVstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+
+// store zero
+(MOVBstore [off] {sym} ptr (MOVVconst [0]) mem) -> (MOVBstorezero [off] {sym} ptr mem)
+(MOVHstore [off] {sym} ptr (MOVVconst [0]) mem) -> (MOVHstorezero [off] {sym} ptr mem)
+(MOVWstore [off] {sym} ptr (MOVVconst [0]) mem) -> (MOVWstorezero [off] {sym} ptr mem)
+(MOVVstore [off] {sym} ptr (MOVVconst [0]) mem) -> (MOVVstorezero [off] {sym} ptr mem)
+
+// don't extend after proper load
+(MOVBreg x:(MOVBload _ _)) -> (MOVVreg x)
+(MOVBUreg x:(MOVBUload _ _)) -> (MOVVreg x)
+(MOVHreg x:(MOVBload _ _)) -> (MOVVreg x)
+(MOVHreg x:(MOVBUload _ _)) -> (MOVVreg x)
+(MOVHreg x:(MOVHload _ _)) -> (MOVVreg x)
+(MOVHUreg x:(MOVBUload _ _)) -> (MOVVreg x)
+(MOVHUreg x:(MOVHUload _ _)) -> (MOVVreg x)
+(MOVWreg x:(MOVBload _ _)) -> (MOVVreg x)
+(MOVWreg x:(MOVBUload _ _)) -> (MOVVreg x)
+(MOVWreg x:(MOVHload _ _)) -> (MOVVreg x)
+(MOVWreg x:(MOVHUload _ _)) -> (MOVVreg x)
+(MOVWreg x:(MOVWload _ _)) -> (MOVVreg x)
+(MOVWUreg x:(MOVBUload _ _)) -> (MOVVreg x)
+(MOVWUreg x:(MOVHUload _ _)) -> (MOVVreg x)
+(MOVWUreg x:(MOVWUload _ _)) -> (MOVVreg x)
+
+// fold double extensions
+(MOVBreg x:(MOVBreg _)) -> (MOVVreg x)
+(MOVBUreg x:(MOVBUreg _)) -> (MOVVreg x)
+(MOVHreg x:(MOVBreg _)) -> (MOVVreg x)
+(MOVHreg x:(MOVBUreg _)) -> (MOVVreg x)
+(MOVHreg x:(MOVHreg _)) -> (MOVVreg x)
+(MOVHUreg x:(MOVBUreg _)) -> (MOVVreg x)
+(MOVHUreg x:(MOVHUreg _)) -> (MOVVreg x)
+(MOVWreg x:(MOVBreg _)) -> (MOVVreg x)
+(MOVWreg x:(MOVBUreg _)) -> (MOVVreg x)
+(MOVWreg x:(MOVHreg _)) -> (MOVVreg x)
+(MOVWreg x:(MOVHreg _)) -> (MOVVreg x)
+(MOVWreg x:(MOVWreg _)) -> (MOVVreg x)
+(MOVWUreg x:(MOVBUreg _)) -> (MOVVreg x)
+(MOVWUreg x:(MOVHUreg _)) -> (MOVVreg x)
+(MOVWUreg x:(MOVWUreg _)) -> (MOVVreg x)
+
+// don't extend before store
+(MOVBstore [off] {sym} ptr (MOVBreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVBUreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVHreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVHUreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVWUreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHUreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVWUreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVWstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
+(MOVWstore [off] {sym} ptr (MOVWUreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
+
+// if a register move has only 1 use, just use the same register without emitting instruction
+// MOVVnop doesn't emit instruction, only for ensuring the type.
+(MOVVreg x) && x.Uses == 1 -> (MOVVnop x)
+
+// fold constant into arithmatic ops
+(ADDV (MOVVconst [c]) x) && is32Bit(c) -> (ADDVconst [c] x)
+(ADDV x (MOVVconst [c])) && is32Bit(c) -> (ADDVconst [c] x)
+(SUBV x (MOVVconst [c])) && is32Bit(c) -> (SUBVconst [c] x)
+(AND (MOVVconst [c]) x) && is32Bit(c) -> (ANDconst [c] x)
+(AND x (MOVVconst [c])) && is32Bit(c) -> (ANDconst [c] x)
+(OR  (MOVVconst [c]) x) && is32Bit(c) -> (ORconst  [c] x)
+(OR  x (MOVVconst [c])) && is32Bit(c) -> (ORconst  [c] x)
+(XOR (MOVVconst [c]) x) && is32Bit(c) -> (XORconst [c] x)
+(XOR x (MOVVconst [c])) && is32Bit(c) -> (XORconst [c] x)
+(NOR (MOVVconst [c]) x) && is32Bit(c) -> (NORconst [c] x)
+(NOR x (MOVVconst [c])) && is32Bit(c) -> (NORconst [c] x)
+
+(SLLV _ (MOVVconst [c])) && uint64(c)>=64 -> (MOVVconst [0])
+(SRLV _ (MOVVconst [c])) && uint64(c)>=64 -> (MOVVconst [0])
+(SRAV x (MOVVconst [c])) && uint64(c)>=64 -> (SRAVconst x [63])
+(SLLV x (MOVVconst [c])) -> (SLLVconst x [c])
+(SRLV x (MOVVconst [c])) -> (SRLVconst x [c])
+(SRAV x (MOVVconst [c])) -> (SRAVconst x [c])
+
+(SGT  (MOVVconst [c]) x) && is32Bit(c) -> (SGTconst  [c] x)
+(SGTU (MOVVconst [c]) x) && is32Bit(c) -> (SGTUconst [c] x)
+
+// mul by constant
+(Select1 (MULVU x (MOVVconst [-1]))) -> (NEGV x)
+(Select1 (MULVU _ (MOVVconst [0]))) -> (MOVVconst [0])
+(Select1 (MULVU x (MOVVconst [1]))) -> x
+(Select1 (MULVU x (MOVVconst [c]))) && isPowerOfTwo(c) -> (SLLVconst [log2(c)] x)
+
+(Select1 (MULVU (MOVVconst [-1]) x)) -> (NEGV x)
+(Select1 (MULVU (MOVVconst [0]) _)) -> (MOVVconst [0])
+(Select1 (MULVU (MOVVconst [1]) x)) -> x
+(Select1 (MULVU (MOVVconst [c]) x)) && isPowerOfTwo(c) -> (SLLVconst [log2(c)] x)
+
+// div by constant
+(Select1 (DIVVU x (MOVVconst [1]))) -> x
+(Select1 (DIVVU x (MOVVconst [c]))) && isPowerOfTwo(c) -> (SRLVconst [log2(c)] x)
+(Select0 (DIVVU _ (MOVVconst [1]))) -> (MOVVconst [0])                       // mod
+(Select0 (DIVVU x (MOVVconst [c]))) && isPowerOfTwo(c) -> (ANDconst [c-1] x) // mod
+
+// generic simplifications
+(ADDV x (NEGV y)) -> (SUBV x y)
+(ADDV (NEGV y) x) -> (SUBV x y)
+(SUBV x x) -> (MOVVconst [0])
+(SUBV (MOVVconst [0]) x) -> (NEGV x)
+(AND x x) -> x
+(OR  x x) -> x
+(XOR x x) -> (MOVVconst [0])
+
+// remove redundant *const ops
+(ADDVconst [0]  x) -> x
+(SUBVconst [0]  x) -> x
+(ANDconst [0]  _) -> (MOVVconst [0])
+(ANDconst [-1] x) -> x
+(ORconst  [0]  x) -> x
+(ORconst  [-1] _) -> (MOVVconst [-1])
+(XORconst [0]  x) -> x
+(XORconst [-1] x) -> (NORconst [0] x)
+
+// generic constant folding
+(ADDVconst [c] (MOVVconst [d]))  -> (MOVVconst [c+d])
+(ADDVconst [c] (ADDVconst [d] x)) && is32Bit(c+d) -> (ADDVconst [c+d] x)
+(ADDVconst [c] (SUBVconst [d] x)) && is32Bit(c-d) -> (ADDVconst [c-d] x)
+(SUBVconst [c] (MOVVconst [d]))  -> (MOVVconst [d-c])
+(SUBVconst [c] (SUBVconst [d] x)) && is32Bit(-c-d) -> (ADDVconst [-c-d] x)
+(SUBVconst [c] (ADDVconst [d] x)) && is32Bit(-c+d) -> (ADDVconst [-c+d] x)
+(SLLVconst [c] (MOVVconst [d]))  -> (MOVVconst [int64(d)<<uint64(c)])
+(SRLVconst [c] (MOVVconst [d]))  -> (MOVVconst [int64(uint64(d)>>uint64(c))])
+(SRAVconst [c] (MOVVconst [d]))  -> (MOVVconst [int64(d)>>uint64(c)])
+(Select1 (MULVU (MOVVconst [c]) (MOVVconst [d]))) -> (MOVVconst [c*d])
+(Select1 (DIVV  (MOVVconst [c]) (MOVVconst [d]))) -> (MOVVconst [int64(c)/int64(d)])
+(Select1 (DIVVU (MOVVconst [c]) (MOVVconst [d]))) -> (MOVVconst [int64(uint64(c)/uint64(d))])
+(Select0 (DIVV  (MOVVconst [c]) (MOVVconst [d]))) -> (MOVVconst [int64(c)%int64(d)])   // mod
+(Select0 (DIVVU (MOVVconst [c]) (MOVVconst [d]))) -> (MOVVconst [int64(uint64(c)%uint64(d))]) // mod
+(ANDconst [c] (MOVVconst [d])) -> (MOVVconst [c&d])
+(ANDconst [c] (ANDconst [d] x)) -> (ANDconst [c&d] x)
+(ORconst [c] (MOVVconst [d])) -> (MOVVconst [c|d])
+(ORconst [c] (ORconst [d] x)) && is32Bit(c|d) -> (ORconst [c|d] x)
+(XORconst [c] (MOVVconst [d])) -> (MOVVconst [c^d])
+(XORconst [c] (XORconst [d] x)) && is32Bit(c^d) -> (XORconst [c^d] x)
+(NORconst [c] (MOVVconst [d])) -> (MOVVconst [^(c|d)])
+(NEGV (MOVVconst [c])) -> (MOVVconst [-c])
+(MOVBreg  (MOVVconst [c])) -> (MOVVconst [int64(int8(c))])
+(MOVBUreg (MOVVconst [c])) -> (MOVVconst [int64(uint8(c))])
+(MOVHreg  (MOVVconst [c])) -> (MOVVconst [int64(int16(c))])
+(MOVHUreg (MOVVconst [c])) -> (MOVVconst [int64(uint16(c))])
+(MOVWreg  (MOVVconst [c])) -> (MOVVconst [int64(int32(c))])
+(MOVWUreg (MOVVconst [c])) -> (MOVVconst [int64(uint32(c))])
+(MOVVreg  (MOVVconst [c])) -> (MOVVconst [c])
+
+// constant comparisons
+(SGTconst [c] (MOVVconst [d])) && int64(c)>int64(d) -> (MOVVconst [1])
+(SGTconst [c] (MOVVconst [d])) && int64(c)<=int64(d) -> (MOVVconst [0])
+(SGTUconst [c] (MOVVconst [d])) && uint64(c)>uint64(d) -> (MOVVconst [1])
+(SGTUconst [c] (MOVVconst [d])) && uint64(c)<=uint64(d) -> (MOVVconst [0])
+
+// other known comparisons
+(SGTconst [c] (MOVBreg _)) && 0x7f < int64(c) -> (MOVVconst [1])
+(SGTconst [c] (MOVBreg _)) && int64(c) <= -0x80 -> (MOVVconst [0])
+(SGTconst [c] (MOVBUreg _)) && 0xff < int64(c) -> (MOVVconst [1])
+(SGTconst [c] (MOVBUreg _)) && int64(c) < 0 -> (MOVVconst [0])
+(SGTUconst [c] (MOVBUreg _)) && 0xff < uint64(c) -> (MOVVconst [1])
+(SGTconst [c] (MOVHreg _)) && 0x7fff < int64(c) -> (MOVVconst [1])
+(SGTconst [c] (MOVHreg _)) && int64(c) <= -0x8000 -> (MOVVconst [0])
+(SGTconst [c] (MOVHUreg _)) && 0xffff < int64(c) -> (MOVVconst [1])
+(SGTconst [c] (MOVHUreg _)) && int64(c) < 0 -> (MOVVconst [0])
+(SGTUconst [c] (MOVHUreg _)) && 0xffff < uint64(c) -> (MOVVconst [1])
+(SGTconst [c] (MOVWUreg _)) && int64(c) < 0 -> (MOVVconst [0])
+(SGTconst [c] (ANDconst [m] _)) && 0 <= m && m < c -> (MOVVconst [1])
+(SGTUconst [c] (ANDconst [m] _)) && uint64(m) < uint64(c) -> (MOVVconst [1])
+(SGTconst [c] (SRLVconst _ [d])) && 0 <= c && 0 < d && d <= 63 && 1<<uint64(64-d) <= c -> (MOVVconst [1])
+(SGTUconst [c] (SRLVconst _ [d])) && 0 < d && d <= 63 && 1<<uint64(64-d) <= uint64(c) -> (MOVVconst [1])
+
+// absorb constants into branches
+(EQ  (MOVVconst [0]) yes no) -> (First nil yes no)
+(EQ  (MOVVconst [c]) yes no) && c != 0 -> (First nil no yes)
+(NE  (MOVVconst [0]) yes no) -> (First nil no yes)
+(NE  (MOVVconst [c]) yes no) && c != 0 -> (First nil yes no)
+(LTZ (MOVVconst [c]) yes no) && c <  0 -> (First nil yes no)
+(LTZ (MOVVconst [c]) yes no) && c >= 0 -> (First nil no yes)
+(LEZ (MOVVconst [c]) yes no) && c <= 0 -> (First nil yes no)
+(LEZ (MOVVconst [c]) yes no) && c >  0 -> (First nil no yes)
+(GTZ (MOVVconst [c]) yes no) && c >  0 -> (First nil yes no)
+(GTZ (MOVVconst [c]) yes no) && c <= 0 -> (First nil no yes)
+(GEZ (MOVVconst [c]) yes no) && c >= 0 -> (First nil yes no)
+(GEZ (MOVVconst [c]) yes no) && c <  0 -> (First nil no yes)
diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
new file mode 100644
index 0000000..d7d7fec
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
@@ -0,0 +1,381 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "strings"
+
+// Notes:
+//  - Integer types live in the low portion of registers. Upper portions are junk.
+//  - Boolean types use the low-order byte of a register. 0=false, 1=true.
+//    Upper bytes are junk.
+//  - *const instructions may use a constant larger than the instuction can encode.
+//    In this case the assembler expands to multiple instructions and uses tmp
+//    register (R23).
+
+// Suffixes encode the bit width of various instructions.
+// V (vlong)     = 64 bit
+// WU (word)     = 32 bit unsigned
+// W (word)      = 32 bit
+// H (half word) = 16 bit
+// HU            = 16 bit unsigned
+// B (byte)      = 8 bit
+// BU            = 8 bit unsigned
+// F (float)     = 32 bit float
+// D (double)    = 64 bit float
+
+// Note: registers not used in regalloc are not included in this list,
+// so that regmask stays within int64
+// Be careful when hand coding regmasks.
+var regNamesMIPS64 = []string{
+	"R0", // constant 0
+	"R1",
+	"R2",
+	"R3",
+	"R4",
+	"R5",
+	"R6",
+	"R7",
+	"R8",
+	"R9",
+	"R10",
+	"R11",
+	"R12",
+	"R13",
+	"R14",
+	"R15",
+	"R16",
+	"R17",
+	"R18",
+	"R19",
+	"R20",
+	"R21",
+	"R22",
+	// R23 = REGTMP not used in regalloc
+	"R24",
+	"R25",
+	// R26 reserved by kernel
+	// R27 reserved by kernel
+	// R28 = REGSB not used in regalloc
+	"SP",  // aka R29
+	"g",   // aka R30
+	"R31", // aka REGLINK
+
+	"F0",
+	"F1",
+	"F2",
+	"F3",
+	"F4",
+	"F5",
+	"F6",
+	"F7",
+	"F8",
+	"F9",
+	"F10",
+	"F11",
+	"F12",
+	"F13",
+	"F14",
+	"F15",
+	"F16",
+	"F17",
+	"F18",
+	"F19",
+	"F20",
+	"F21",
+	"F22",
+	"F23",
+	"F24",
+	"F25",
+	"F26",
+	"F27",
+	"F28",
+	"F29",
+	"F30",
+	"F31",
+
+	"HI", // high bits of multiplication
+	"LO", // low bits of multiplication
+
+	// pseudo-registers
+	"SB",
+}
+
+func init() {
+	// Make map from reg names to reg integers.
+	if len(regNamesMIPS64) > 64 {
+		panic("too many registers")
+	}
+	num := map[string]int{}
+	for i, name := range regNamesMIPS64 {
+		num[name] = i
+	}
+	buildReg := func(s string) regMask {
+		m := regMask(0)
+		for _, r := range strings.Split(s, " ") {
+			if n, ok := num[r]; ok {
+				m |= regMask(1) << uint(n)
+				continue
+			}
+			panic("register " + r + " not found")
+		}
+		return m
+	}
+
+	// Common individual register masks
+	var (
+		gp         = buildReg("R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31")
+		gpg        = gp | buildReg("g")
+		gpsp       = gp | buildReg("SP")
+		gpspg      = gpg | buildReg("SP")
+		gpspsbg    = gpspg | buildReg("SB")
+		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31")
+		lo         = buildReg("LO")
+		hi         = buildReg("HI")
+		callerSave = gp | fp | lo | hi | buildReg("g") // runtime.setg (and anything calling it) may clobber g
+	)
+	// Common regInfo
+	var (
+		gp01     = regInfo{inputs: nil, outputs: []regMask{gp}}
+		gp11     = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
+		gp11sp   = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
+		gp21     = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
+		gp2hilo  = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}}
+		gpload   = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
+		gpstore  = regInfo{inputs: []regMask{gpspsbg, gpg}}
+		gpstore0 = regInfo{inputs: []regMask{gpspsbg}}
+		fp01     = regInfo{inputs: nil, outputs: []regMask{fp}}
+		fp11     = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
+		//fp1flags  = regInfo{inputs: []regMask{fp}}
+		//fpgp      = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
+		//gpfp      = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
+		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
+		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
+		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
+		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
+		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
+	)
+	ops := []opData{
+		// binary ops
+		{name: "ADDV", argLength: 2, reg: gp21, asm: "ADDVU", commutative: true},                             // arg0 + arg1
+		{name: "ADDVconst", argLength: 1, reg: gp11sp, asm: "ADDVU", aux: "Int64"},                           // arg0 + auxInt
+		{name: "SUBV", argLength: 2, reg: gp21, asm: "SUBVU"},                                                // arg0 - arg1
+		{name: "SUBVconst", argLength: 1, reg: gp11, asm: "SUBVU", aux: "Int64"},                             // arg0 - auxInt
+		{name: "MULV", argLength: 2, reg: gp2hilo, asm: "MULV", commutative: true, typ: "(Int64,Int64)"},     // arg0 * arg1, signed, results hi,lo
+		{name: "MULVU", argLength: 2, reg: gp2hilo, asm: "MULVU", commutative: true, typ: "(UInt64,UInt64)"}, // arg0 * arg1, unsigned, results hi,lo
+		{name: "DIVV", argLength: 2, reg: gp2hilo, asm: "DIVV", typ: "(Int64,Int64)"},                        // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
+		{name: "DIVVU", argLength: 2, reg: gp2hilo, asm: "DIVVU", typ: "(UInt64,UInt64)"},                    // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
+
+		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
+		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
+		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
+		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
+		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
+		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
+		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
+		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
+
+		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
+		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64"},                // arg0 & auxInt
+		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
+		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"},                  // arg0 | auxInt
+		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt64"}, // arg0 ^ arg1
+		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", typ: "UInt64"}, // arg0 ^ auxInt
+		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
+		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int64"},                // ^(arg0 | auxInt)
+
+		{name: "NEGV", argLength: 1, reg: gp11},              // -arg0
+		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"}, // -arg0, float32
+		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"}, // -arg0, float64
+
+		// shifts
+		{name: "SLLV", argLength: 2, reg: gp21, asm: "SLLV"},                    // arg0 << arg1, shift amount is mod 64
+		{name: "SLLVconst", argLength: 1, reg: gp11, asm: "SLLV", aux: "Int64"}, // arg0 << auxInt
+		{name: "SRLV", argLength: 2, reg: gp21, asm: "SRLV"},                    // arg0 >> arg1, unsigned, shift amount is mod 64
+		{name: "SRLVconst", argLength: 1, reg: gp11, asm: "SRLV", aux: "Int64"}, // arg0 >> auxInt, unsigned
+		{name: "SRAV", argLength: 2, reg: gp21, asm: "SRAV"},                    // arg0 >> arg1, signed, shift amount is mod 64
+		{name: "SRAVconst", argLength: 1, reg: gp11, asm: "SRAV", aux: "Int64"}, // arg0 >> auxInt, signed
+
+		// comparisons
+		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
+		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int64", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
+		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
+		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int64", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
+
+		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
+		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
+		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
+		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
+		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
+		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
+
+		// moves
+		{name: "MOVVconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVV", typ: "UInt64", rematerializeable: true},    // auxint
+		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
+		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
+
+		{name: "MOVVaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVV", rematerializeable: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
+
+		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVVload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVV", typ: "UInt64", faultOnNilArg0: true},   // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+
+		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true}, // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true}, // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVVstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+
+		{name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true}, // store 1 byte of zero to arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true}, // store 2 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVVstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVV", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of zero to arg0 + auxInt + aux.  ar12=mem.
+
+		// conversions
+		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
+		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
+		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
+		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
+		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
+		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
+		{name: "MOVVreg", argLength: 1, reg: gp11, asm: "MOVV"},   // move from arg0
+
+		{name: "MOVVnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
+
+		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
+		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
+		{name: "MOVVF", argLength: 1, reg: fp11, asm: "MOVVF"},     // int64 -> float32
+		{name: "MOVVD", argLength: 1, reg: fp11, asm: "MOVVD"},     // int64 -> float64
+		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
+		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
+		{name: "TRUNCFV", argLength: 1, reg: fp11, asm: "TRUNCFV"}, // float32 -> int64
+		{name: "TRUNCDV", argLength: 1, reg: fp11, asm: "TRUNCDV"}, // float64 -> int64
+		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
+		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
+
+		// function calls
+		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true},                                              // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
+		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                // call deferproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                   // call newproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
+
+		// duffzero
+		// arg0 = address of memory to zero
+		// arg1 = mem
+		// auxint = offset into duffzero code to start executing
+		// returns mem
+		// R1 aka mips.REGRT1 changed as side effect
+		{
+			name:      "DUFFZERO",
+			aux:       "Int64",
+			argLength: 2,
+			reg: regInfo{
+				inputs:   []regMask{gp},
+				clobbers: buildReg("R1 R31"),
+			},
+			faultOnNilArg0: true,
+		},
+
+		// large or unaligned zeroing
+		// arg0 = address of memory to zero (in R1, changed as side effect)
+		// arg1 = address of the last element to zero
+		// arg2 = mem
+		// auxint = alignment
+		// returns mem
+		//	SUBV	$8, R1
+		//	MOVV	R0, 8(R1)
+		//	ADDV	$8, R1
+		//	BNE	Rarg1, R1, -2(PC)
+		{
+			name:      "LoweredZero",
+			aux:       "Int64",
+			argLength: 3,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R1"), gp},
+				clobbers: buildReg("R1"),
+			},
+			clobberFlags:   true,
+			faultOnNilArg0: true,
+		},
+
+		// large or unaligned move
+		// arg0 = address of dst memory (in R2, changed as side effect)
+		// arg1 = address of src memory (in R1, changed as side effect)
+		// arg2 = address of the last element of src
+		// arg3 = mem
+		// auxint = alignment
+		// returns mem
+		//	SUBV	$8, R1
+		//	MOVV	8(R1), Rtmp
+		//	MOVV	Rtmp, (R2)
+		//	ADDV	$8, R1
+		//	ADDV	$8, R2
+		//	BNE	Rarg2, R1, -4(PC)
+		{
+			name:      "LoweredMove",
+			aux:       "Int64",
+			argLength: 4,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
+				clobbers: buildReg("R1 R2"),
+			},
+			clobberFlags:   true,
+			faultOnNilArg0: true,
+			faultOnNilArg1: true,
+		},
+
+		// pseudo-ops
+		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
+
+		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
+		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
+
+		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
+		// and sorts it to the very beginning of the block to prevent other
+		// use of R22 (mips.REGCTXT, the closure pointer)
+		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}},
+
+		// MOVDconvert converts between pointers and integers.
+		// We have a special op for this so as to not confuse GC
+		// (particularly stack maps).  It takes a memory arg so it
+		// gets correctly ordered with respect to GC safepoints.
+		// arg0=ptr/int arg1=mem, output=int/ptr
+		{name: "MOVVconvert", argLength: 2, reg: gp11, asm: "MOVV"},
+	}
+
+	blocks := []blockData{
+		{name: "EQ"},
+		{name: "NE"},
+		{name: "LTZ"}, // < 0
+		{name: "LEZ"}, // <= 0
+		{name: "GTZ"}, // > 0
+		{name: "GEZ"}, // >= 0
+		{name: "FPT"}, // FP flag is true
+		{name: "FPF"}, // FP flag is false
+	}
+
+	archs = append(archs, arch{
+		name:            "MIPS64",
+		pkg:             "cmd/internal/obj/mips",
+		genfile:         "../../mips64/ssa.go",
+		ops:             ops,
+		blocks:          blocks,
+		regnames:        regNamesMIPS64,
+		gpregmask:       gp,
+		fpregmask:       fp,
+		specialregmask:  hi | lo,
+		framepointerreg: -1, // not used
+		linkreg:         int8(num["R31"]),
+	})
+}
diff --git a/src/cmd/compile/internal/ssa/gen/MIPSOps.go b/src/cmd/compile/internal/ssa/gen/MIPSOps.go
new file mode 100644
index 0000000..c803c49
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/MIPSOps.go
@@ -0,0 +1,413 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "strings"
+
+// Notes:
+//  - Integer types live in the low portion of registers. Upper portions are junk.
+//  - Boolean types use the low-order byte of a register. 0=false, 1=true.
+//    Upper bytes are junk.
+//  - Unused portions of AuxInt are filled by sign-extending the used portion.
+//  - *const instructions may use a constant larger than the instuction can encode.
+//    In this case the assembler expands to multiple instructions and uses tmp
+//    register (R23).
+
+// Suffixes encode the bit width of various instructions.
+// W (word)      = 32 bit
+// H (half word) = 16 bit
+// HU            = 16 bit unsigned
+// B (byte)      = 8 bit
+// BU            = 8 bit unsigned
+// F (float)     = 32 bit float
+// D (double)    = 64 bit float
+
+// Note: registers not used in regalloc are not included in this list,
+// so that regmask stays within int64
+// Be careful when hand coding regmasks.
+var regNamesMIPS = []string{
+	"R0", // constant 0
+	"R1",
+	"R2",
+	"R3",
+	"R4",
+	"R5",
+	"R6",
+	"R7",
+	"R8",
+	"R9",
+	"R10",
+	"R11",
+	"R12",
+	"R13",
+	"R14",
+	"R15",
+	"R16",
+	"R17",
+	"R18",
+	"R19",
+	"R20",
+	"R21",
+	"R22",
+	//REGTMP
+	"R24",
+	"R25",
+	// R26 reserved by kernel
+	// R27 reserved by kernel
+	"R28",
+	"SP",  // aka R29
+	"g",   // aka R30
+	"R31", // REGLINK
+
+	// odd FP registers contain high parts of 64-bit FP values
+	"F0",
+	"F2",
+	"F4",
+	"F6",
+	"F8",
+	"F10",
+	"F12",
+	"F14",
+	"F16",
+	"F18",
+	"F20",
+	"F22",
+	"F24",
+	"F26",
+	"F28",
+	"F30",
+
+	"HI", // high bits of multiplication
+	"LO", // low bits of multiplication
+
+	// pseudo-registers
+	"SB",
+}
+
+func init() {
+	// Make map from reg names to reg integers.
+	if len(regNamesMIPS) > 64 {
+		panic("too many registers")
+	}
+	num := map[string]int{}
+	for i, name := range regNamesMIPS {
+		num[name] = i
+	}
+	buildReg := func(s string) regMask {
+		m := regMask(0)
+		for _, r := range strings.Split(s, " ") {
+			if n, ok := num[r]; ok {
+				m |= regMask(1) << uint(n)
+				continue
+			}
+			panic("register " + r + " not found")
+		}
+		return m
+	}
+
+	// Common individual register masks
+	var (
+		gp         = buildReg("R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31")
+		gpg        = gp | buildReg("g")
+		gpsp       = gp | buildReg("SP")
+		gpspg      = gpg | buildReg("SP")
+		gpspsbg    = gpspg | buildReg("SB")
+		fp         = buildReg("F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30")
+		lo         = buildReg("LO")
+		hi         = buildReg("HI")
+		callerSave = gp | fp | lo | hi | buildReg("g") // runtime.setg (and anything calling it) may clobber g
+	)
+	// Common regInfo
+	var (
+		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
+		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
+		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
+		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
+		gp31      = regInfo{inputs: []regMask{gp, gp, gp}, outputs: []regMask{gp}}
+		gp2hilo   = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}}
+		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
+		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
+		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
+		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
+		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
+		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
+		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
+		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
+		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
+		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
+		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
+		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
+	)
+	ops := []opData{
+		{name: "ADD", argLength: 2, reg: gp21, asm: "ADDU", commutative: true},                                                                           // arg0 + arg1
+		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADDU", aux: "Int32"},                                                                         // arg0 + auxInt
+		{name: "SUB", argLength: 2, reg: gp21, asm: "SUBU"},                                                                                              // arg0 - arg1
+		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUBU", aux: "Int32"},                                                                           // arg0 - auxInt
+		{name: "MUL", argLength: 2, reg: regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}, clobbers: hi | lo}, asm: "MUL", commutative: true}, // arg0 * arg1
+		{name: "MULT", argLength: 2, reg: gp2hilo, asm: "MUL", commutative: true, typ: "(Int32,Int32)"},                                                  // arg0 * arg1, signed, results hi,lo
+		{name: "MULTU", argLength: 2, reg: gp2hilo, asm: "MULU", commutative: true, typ: "(UInt32,UInt32)"},                                              // arg0 * arg1, unsigned, results hi,lo
+		{name: "DIV", argLength: 2, reg: gp2hilo, asm: "DIV", typ: "(Int32,Int32)"},                                                                      // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
+		{name: "DIVU", argLength: 2, reg: gp2hilo, asm: "DIVU", typ: "(UInt32,UInt32)"},                                                                  // arg0 / arg1, signed, results hi=arg0%arg1,lo=arg0/arg1
+
+		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0 + arg1
+		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0 + arg1
+		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0 - arg1
+		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0 - arg1
+		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0 * arg1
+		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0 * arg1
+		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0 / arg1
+		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0 / arg1
+
+		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0 & arg1
+		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32"},                // arg0 & auxInt
+		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
+		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int32"},                  // arg0 | auxInt
+		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt32"}, // arg0 ^ arg1
+		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int32", typ: "UInt32"}, // arg0 ^ auxInt
+		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^(arg0 | arg1)
+		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int32"},                // ^(arg0 | auxInt)
+
+		{name: "NEG", argLength: 1, reg: gp11},                 // -arg0
+		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0, float32
+		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0, float64
+		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt(arg0), float64
+
+		// shifts
+		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"},                    // arg0 << arg1, shift amount is mod 32
+		{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt
+		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"},                    // arg0 >> arg1, unsigned, shift amount is mod 32
+		{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned
+		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"},                    // arg0 >> arg1, signed, shift amount is mod 32
+		{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed
+
+		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},
+
+		// comparisons
+		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 1 if arg0 > arg1 (signed), 0 otherwise
+		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int32", typ: "Bool"},   // 1 if auxInt > arg0 (signed), 0 otherwise
+		{name: "SGTzero", argLength: 1, reg: gp11, asm: "SGT", typ: "Bool"},                  // 1 if arg0 > 0 (signed), 0 otherwise
+		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 1 if arg0 > arg1 (unsigned), 0 otherwise
+		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int32", typ: "Bool"}, // 1 if auxInt > arg0 (unsigned), 0 otherwise
+		{name: "SGTUzero", argLength: 1, reg: gp11, asm: "SGTU", typ: "Bool"},                // 1 if arg0 > 0 (unsigned), 0 otherwise
+
+		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // flags=true if arg0 = arg1, float32
+		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // flags=true if arg0 = arg1, float64
+		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // flags=true if arg0 >= arg1, float32
+		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // flags=true if arg0 >= arg1, float64
+		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // flags=true if arg0 > arg1, float32
+		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // flags=true if arg0 > arg1, float64
+
+		// moves
+		{name: "MOVWconst", argLength: 0, reg: gp01, aux: "Int32", asm: "MOVW", typ: "UInt32", rematerializeable: true},    // auxint
+		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float32", asm: "MOVF", typ: "Float32", rematerializeable: true}, // auxint as 64-bit float, convert to 32-bit float
+		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // auxint as 64-bit float
+
+		{name: "MOVWaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVW", rematerializeable: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
+
+		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true},     // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true},    // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true}, // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "UInt32", faultOnNilArg0: true},   // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true},  // load from arg0 + auxInt + aux.  arg1=mem.
+
+		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true}, // store 1 byte of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true}, // store 2 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+		{name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true}, // store 8 bytes of arg1 to arg0 + auxInt + aux.  arg2=mem.
+
+		{name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true}, // store 1 byte of zero to arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true}, // store 2 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
+		{name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true}, // store 4 bytes of zero to arg0 + auxInt + aux.  arg1=mem.
+
+		// conversions
+		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
+		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
+		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
+		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
+		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0
+
+		{name: "MOVWnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // nop, return arg0 in same register
+
+		// conditional move on zero (returns arg1 if arg2 is 0, otherwise arg0)
+		// order of parameters is reversed so we can use resultInArg0 (OpCMOVZ result arg1 arg2-> CMOVZ arg2reg, arg1reg, resultReg)
+		{name: "CMOVZ", argLength: 3, reg: gp31, asm: "CMOVZ", resultInArg0: true},
+		{name: "CMOVZzero", argLength: 2, reg: regInfo{inputs: []regMask{gp, gpg}, outputs: []regMask{gp}}, asm: "CMOVZ", resultInArg0: true},
+
+		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32 -> float32
+		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32 -> float64
+		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32 -> int32
+		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // float64 -> int32
+		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // float32 -> float64
+		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // float64 -> float32
+
+		// function calls
+		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true},                                              // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "Int32", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
+		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int32", clobberFlags: true, call: true},                                                // call deferproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int32", clobberFlags: true, call: true},                                                   // call newproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int32", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
+
+		// atomic ops
+
+		// load from arg0. arg1=mem.
+		// returns <value,memory> so they can be properly ordered with other loads.
+		// SYNC
+		// MOVW	(Rarg0), Rout
+		// SYNC
+		{name: "LoweredAtomicLoad", argLength: 2, reg: gpload, faultOnNilArg0: true},
+
+		// store arg1 to arg0. arg2=mem. returns memory.
+		// SYNC
+		// MOVW	Rarg1, (Rarg0)
+		// SYNC
+		{name: "LoweredAtomicStore", argLength: 3, reg: gpstore, faultOnNilArg0: true},
+		{name: "LoweredAtomicStorezero", argLength: 2, reg: gpstore0, faultOnNilArg0: true},
+
+		// atomic exchange.
+		// store arg1 to arg0. arg2=mem. returns <old content of *arg0, memory>.
+		// SYNC
+		// LL	(Rarg0), Rout
+		// MOVW Rarg1, Rtmp
+		// SC	Rtmp, (Rarg0)
+		// BEQ	Rtmp, -3(PC)
+		// SYNC
+		{name: "LoweredAtomicExchange", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
+
+		// atomic add.
+		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
+		// SYNC
+		// LL	(Rarg0), Rout
+		// ADDU Rarg1, Rout, Rtmp
+		// SC	Rtmp, (Rarg0)
+		// BEQ	Rtmp, -3(PC)
+		// SYNC
+		// ADDU Rarg1, Rout
+		{name: "LoweredAtomicAdd", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true},
+		{name: "LoweredAtomicAddconst", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true},
+
+		// atomic compare and swap.
+		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
+		// if *arg0 == arg1 {
+		//   *arg0 = arg2
+		//   return (true, memory)
+		// } else {
+		//   return (false, memory)
+		// }
+		// SYNC
+		// MOVW $0, Rout
+		// LL	(Rarg0), Rtmp
+		// BNE	Rtmp, Rarg1, 4(PC)
+		// MOVW Rarg2, Rout
+		// SC	Rout, (Rarg0)
+		// BEQ	Rout, -4(PC)
+		// SYNC
+		{name: "LoweredAtomicCas", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true},
+
+		// atomic and/or.
+		// *arg0 &= (|=) arg1. arg2=mem. returns memory.
+		// SYNC
+		// LL	(Rarg0), Rtmp
+		// AND	Rarg1, Rtmp
+		// SC	Rtmp, (Rarg0)
+		// BEQ	Rtmp, -3(PC)
+		// SYNC
+		{name: "LoweredAtomicAnd", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true},
+		{name: "LoweredAtomicOr", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true},
+
+		// large or unaligned zeroing
+		// arg0 = address of memory to zero (in R1, changed as side effect)
+		// arg1 = address of the last element to zero
+		// arg2 = mem
+		// auxint = alignment
+		// returns mem
+		//	SUBU	$4, R1
+		//	MOVW	R0, 4(R1)
+		//	ADDU	$4, R1
+		//	BNE	Rarg1, R1, -2(PC)
+		{
+			name:      "LoweredZero",
+			aux:       "Int32",
+			argLength: 3,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R1"), gp},
+				clobbers: buildReg("R1"),
+			},
+			faultOnNilArg0: true,
+		},
+
+		// large or unaligned move
+		// arg0 = address of dst memory (in R2, changed as side effect)
+		// arg1 = address of src memory (in R1, changed as side effect)
+		// arg2 = address of the last element of src
+		// arg3 = mem
+		// auxint = alignment
+		// returns mem
+		//	SUBU	$4, R1
+		//	MOVW	4(R1), Rtmp
+		//	MOVW	Rtmp, (R2)
+		//	ADDU	$4, R1
+		//	ADDU	$4, R2
+		//	BNE	Rarg2, R1, -4(PC)
+		{
+			name:      "LoweredMove",
+			aux:       "Int32",
+			argLength: 4,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
+				clobbers: buildReg("R1 R2"),
+			},
+			faultOnNilArg0: true,
+			faultOnNilArg1: true,
+		},
+
+		// pseudo-ops
+		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil.  arg1=mem.
+
+		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool, true if FP flag is true
+		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool, true if FP flag is false
+
+		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
+		// and sorts it to the very beginning of the block to prevent other
+		// use of R22 (mips.REGCTXT, the closure pointer)
+		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}},
+
+		// MOVWconvert converts between pointers and integers.
+		// We have a special op for this so as to not confuse GC
+		// (particularly stack maps).  It takes a memory arg so it
+		// gets correctly ordered with respect to GC safepoints.
+		// arg0=ptr/int arg1=mem, output=int/ptr
+		{name: "MOVWconvert", argLength: 2, reg: gp11, asm: "MOVW"},
+	}
+
+	blocks := []blockData{
+		{name: "EQ"},
+		{name: "NE"},
+		{name: "LTZ"}, // < 0
+		{name: "LEZ"}, // <= 0
+		{name: "GTZ"}, // > 0
+		{name: "GEZ"}, // >= 0
+		{name: "FPT"}, // FP flag is true
+		{name: "FPF"}, // FP flag is false
+	}
+
+	archs = append(archs, arch{
+		name:            "MIPS",
+		pkg:             "cmd/internal/obj/mips",
+		genfile:         "../../mips/ssa.go",
+		ops:             ops,
+		blocks:          blocks,
+		regnames:        regNamesMIPS,
+		gpregmask:       gp,
+		fpregmask:       fp,
+		specialregmask:  hi | lo,
+		framepointerreg: -1, // not used
+		linkreg:         int8(num["R31"]),
+	})
+}
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules
new file mode 100644
index 0000000..0e0f1f9
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules
@@ -0,0 +1,832 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Lowering arithmetic
+(Add64  x y) -> (ADD  x y)
+(AddPtr x y) -> (ADD  x y)
+(Add32  x y) -> (ADD x y)
+(Add16  x y) -> (ADD x y)
+(Add8   x y) -> (ADD x y)
+(Add64F x y) -> (FADD x y)
+(Add32F x y) -> (FADDS x y)
+
+(Sub64  x y) -> (SUB  x y)
+(SubPtr x y) -> (SUB  x y)
+(Sub32  x y) -> (SUB x y)
+(Sub16  x y) -> (SUB x y)
+(Sub8   x y) -> (SUB x y)
+(Sub32F x y) -> (FSUBS x y)
+(Sub64F x y) -> (FSUB x y)
+
+(Mod16 x y) -> (Mod32 (SignExt16to32 x) (SignExt16to32 y))
+(Mod16u x y) -> (Mod32u (ZeroExt16to32 x) (ZeroExt16to32 y))
+(Mod8 x y) -> (Mod32 (SignExt8to32 x) (SignExt8to32 y))
+(Mod8u x y) -> (Mod32u (ZeroExt8to32 x) (ZeroExt8to32 y))
+(Mod64 x y) -> (SUB x (MULLD y (DIVD x y)))
+(Mod64u x y) -> (SUB x (MULLD y (DIVDU x y)))
+(Mod32 x y) -> (SUB x (MULLW y (DIVW x y)))
+(Mod32u x y) -> (SUB x (MULLW y (DIVWU x y)))
+
+(Avg64u <t> x y) -> (ADD (ADD <t> (SRD <t> x (MOVDconst <t> [1])) (SRD <t> y (MOVDconst <t> [1]))) (ANDconst <t> (AND <t> x y) [1]))
+
+(Mul64  x y) -> (MULLD  x y)
+(Mul32  x y) -> (MULLW  x y)
+(Mul16  x y) -> (MULLW x y)
+(Mul8   x y) -> (MULLW x y)
+
+(Div64  x y) -> (DIVD  x y)
+(Div64u x y) -> (DIVDU x y)
+(Div32  x y) -> (DIVW  x y)
+(Div32u x y) -> (DIVWU x y)
+(Div16  x y) -> (DIVW  (SignExt16to32 x) (SignExt16to32 y))
+(Div16u x y) -> (DIVWU (ZeroExt16to32 x) (ZeroExt16to32 y))
+(Div8   x y) -> (DIVW  (SignExt8to32 x) (SignExt8to32 y))
+(Div8u  x y) -> (DIVWU (ZeroExt8to32 x) (ZeroExt8to32 y))
+
+(Hmul64  x y) -> (MULHD  x y)
+(Hmul64u  x y) -> (MULHDU x y)
+(Hmul32  x y) -> (MULHW  x y)
+(Hmul32u  x y) -> (MULHWU x y)
+(Hmul16 x y) -> (SRAWconst (MULLW <config.fe.TypeInt32()> (SignExt16to32 x) (SignExt16to32 y)) [16])
+(Hmul16u x y) -> (SRWconst (MULLW <config.fe.TypeUInt32()> (ZeroExt16to32 x) (ZeroExt16to32 y)) [16])
+(Hmul8 x y) -> (SRAWconst (MULLW <config.fe.TypeInt16()> (SignExt8to32 x) (SignExt8to32 y)) [8])
+(Hmul8u x y) -> (SRWconst (MULLW <config.fe.TypeUInt16()> (ZeroExt8to32 x) (ZeroExt8to32 y)) [8])
+
+(Mul32F x y) -> (FMULS x y)
+(Mul64F x y) -> (FMUL x y)
+
+(Div32F x y) -> (FDIVS x y)
+(Div64F x y) -> (FDIV x y)
+
+// Lowering float <-> int
+(Cvt32to32F x) -> (FRSP (FCFID (Xi2f64 (SignExt32to64 x))))
+(Cvt32to64F x) -> (FCFID (Xi2f64 (SignExt32to64 x)))
+(Cvt64to32F x) -> (FRSP (FCFID (Xi2f64 x)))
+(Cvt64to64F x) -> (FCFID (Xi2f64 x))
+
+(Cvt32Fto32 x) -> (Xf2i64 (FCTIWZ x))
+(Cvt32Fto64 x) -> (Xf2i64 (FCTIDZ x))
+(Cvt64Fto32 x) -> (Xf2i64 (FCTIWZ x))
+(Cvt64Fto64 x) -> (Xf2i64 (FCTIDZ x))
+
+(Cvt32Fto64F x) -> x // Note x will have the wrong type for patterns dependent on Float32/Float64
+(Cvt64Fto32F x) -> (FRSP x)
+
+(Sqrt x) -> (FSQRT x)
+
+// Lowering constants
+(Const8   [val]) -> (MOVDconst [val])
+(Const16  [val]) -> (MOVDconst [val])
+(Const32  [val]) -> (MOVDconst [val])
+(Const64  [val]) -> (MOVDconst [val])
+(Const32F [val]) -> (FMOVSconst [val])
+(Const64F [val]) -> (FMOVDconst [val])
+(ConstNil) -> (MOVDconst [0])
+(ConstBool [b]) -> (MOVDconst [b])
+
+(Lsh64x64  x (Const64 [c])) && uint64(c) < 64 -> (SLDconst x [c])
+(Rsh64x64  x (Const64 [c])) && uint64(c) < 64 -> (SRADconst x [c])
+(Rsh64Ux64 x (Const64 [c])) && uint64(c) < 64 -> (SRDconst x [c])
+(Lsh32x64  x (Const64 [c])) && uint64(c) < 32 -> (SLWconst x [c])
+(Rsh32x64  x (Const64 [c])) && uint64(c) < 32 -> (SRAWconst x [c])
+(Rsh32Ux64 x (Const64 [c])) && uint64(c) < 32 -> (SRWconst x [c])
+(Lsh16x64  x (Const64 [c])) && uint64(c) < 16 -> (SLWconst x [c])
+(Rsh16x64  x (Const64 [c])) && uint64(c) < 16 -> (SRAWconst (SignExt16to32 x) [c])
+(Rsh16Ux64 x (Const64 [c])) && uint64(c) < 16 -> (SRWconst (ZeroExt16to32 x) [c])
+(Lsh8x64   x (Const64 [c])) && uint64(c) < 8  -> (SLWconst x [c])
+(Rsh8x64   x (Const64 [c])) && uint64(c) < 8  -> (SRAWconst (SignExt8to32  x) [c])
+(Rsh8Ux64  x (Const64 [c])) && uint64(c) < 8  -> (SRWconst (ZeroExt8to32  x) [c])
+
+(Lsh64x32  x (Const64 [c])) && uint32(c) < 64 -> (SLDconst x [c])
+(Rsh64x32  x (Const64 [c])) && uint32(c) < 64 -> (SRADconst x [c])
+(Rsh64Ux32 x (Const64 [c])) && uint32(c) < 64 -> (SRDconst x [c])
+(Lsh32x32  x (Const64 [c])) && uint32(c) < 32 -> (SLWconst x [c])
+(Rsh32x32  x (Const64 [c])) && uint32(c) < 32 -> (SRAWconst x [c])
+(Rsh32Ux32 x (Const64 [c])) && uint32(c) < 32 -> (SRWconst x [c])
+(Lsh16x32  x (Const64 [c])) && uint32(c) < 16 -> (SLWconst x [c])
+(Rsh16x32  x (Const64 [c])) && uint32(c) < 16 -> (SRAWconst (SignExt16to32 x) [c])
+(Rsh16Ux32 x (Const64 [c])) && uint32(c) < 16 -> (SRWconst (ZeroExt16to32 x) [c])
+(Lsh8x32   x (Const64 [c])) && uint32(c) < 8  -> (SLWconst x [c])
+(Rsh8x32   x (Const64 [c])) && uint32(c) < 8  -> (SRAWconst (SignExt8to32  x) [c])
+(Rsh8Ux32  x (Const64 [c])) && uint32(c) < 8  -> (SRWconst (ZeroExt8to32  x) [c])
+
+// large constant shifts
+(Lsh64x64  _ (Const64 [c])) && uint64(c) >= 64 -> (MOVDconst [0])
+(Rsh64Ux64 _ (Const64 [c])) && uint64(c) >= 64 -> (MOVDconst [0])
+(Lsh32x64  _ (Const64 [c])) && uint64(c) >= 32 -> (MOVDconst [0])
+(Rsh32Ux64 _ (Const64 [c])) && uint64(c) >= 32 -> (MOVDconst [0])
+(Lsh16x64  _ (Const64 [c])) && uint64(c) >= 16 -> (MOVDconst [0])
+(Rsh16Ux64 _ (Const64 [c])) && uint64(c) >= 16 -> (MOVDconst [0])
+(Lsh8x64   _ (Const64 [c])) && uint64(c) >= 8  -> (MOVDconst [0])
+(Rsh8Ux64  _ (Const64 [c])) && uint64(c) >= 8  -> (MOVDconst [0])
+
+// large constant signed right shift, we leave the sign bit
+(Rsh64x64 x (Const64 [c])) && uint64(c) >= 64 -> (SRADconst x [63])
+(Rsh32x64 x (Const64 [c])) && uint64(c) >= 32 -> (SRAWconst x [63])
+(Rsh16x64 x (Const64 [c])) && uint64(c) >= 16 -> (SRAWconst (SignExt16to32 x) [63])
+(Rsh8x64  x (Const64 [c])) && uint64(c) >= 8  -> (SRAWconst (SignExt8to32  x) [63])
+
+// constant shifts
+(Lsh64x64  x (MOVDconst [c])) && uint64(c) < 64 -> (SLDconst x [c])
+(Rsh64x64  x (MOVDconst [c])) && uint64(c) < 64 -> (SRADconst x [c])
+(Rsh64Ux64 x (MOVDconst [c])) && uint64(c) < 64 -> (SRDconst x [c])
+(Lsh32x64  x (MOVDconst [c])) && uint64(c) < 32 -> (SLWconst x [c])
+(Rsh32x64  x (MOVDconst [c])) && uint64(c) < 32 -> (SRAWconst x [c])
+(Rsh32Ux64 x (MOVDconst [c])) && uint64(c) < 32 -> (SRWconst x [c])
+(Lsh16x64  x (MOVDconst [c])) && uint64(c) < 16 -> (SLWconst x [c])
+(Rsh16x64  x (MOVDconst [c])) && uint64(c) < 16 -> (SRAWconst (SignExt16to32 x) [c])
+(Rsh16Ux64 x (MOVDconst [c])) && uint64(c) < 16 -> (SRWconst (ZeroExt16to32 x) [c])
+(Lsh8x64   x (MOVDconst [c])) && uint64(c) < 8  -> (SLWconst x [c])
+(Rsh8x64   x (MOVDconst [c])) && uint64(c) < 8  -> (SRAWconst (SignExt8to32  x) [c])
+(Rsh8Ux64  x (MOVDconst [c])) && uint64(c) < 8  -> (SRWconst (ZeroExt8to32  x) [c])
+
+(Lsh64x32  x (MOVDconst [c])) && uint32(c) < 64 -> (SLDconst x [c])
+(Rsh64x32  x (MOVDconst [c])) && uint32(c) < 64 -> (SRADconst x [c])
+(Rsh64Ux32 x (MOVDconst [c])) && uint32(c) < 64 -> (SRDconst x [c])
+(Lsh32x32  x (MOVDconst [c])) && uint32(c) < 32 -> (SLWconst x [c])
+(Rsh32x32  x (MOVDconst [c])) && uint32(c) < 32 -> (SRAWconst x [c])
+(Rsh32Ux32 x (MOVDconst [c])) && uint32(c) < 32 -> (SRWconst x [c])
+(Lsh16x32  x (MOVDconst [c])) && uint32(c) < 16 -> (SLWconst x [c])
+(Rsh16x32  x (MOVDconst [c])) && uint32(c) < 16 -> (SRAWconst (SignExt16to32 x) [c])
+(Rsh16Ux32 x (MOVDconst [c])) && uint32(c) < 16 -> (SRWconst (ZeroExt16to32 x) [c])
+(Lsh8x32   x (MOVDconst [c])) && uint32(c) < 8  -> (SLWconst x [c])
+(Rsh8x32   x (MOVDconst [c])) && uint32(c) < 8  -> (SRAWconst (SignExt8to32  x) [c])
+(Rsh8Ux32  x (MOVDconst [c])) && uint32(c) < 8  -> (SRWconst (ZeroExt8to32  x) [c])
+
+(Rsh64x64 x y)  -> (SRAD x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] y))))
+(Rsh64Ux64 x y) -> (SRD  x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] y))))
+(Lsh64x64 x y)  -> (SLD  x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] y))))
+
+(Rsh32x64 x y)  -> (SRAW x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] y))))
+(Rsh32Ux64 x y) -> (SRW  x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] y))))
+(Lsh32x64 x y)  -> (SLW  x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] y))))
+
+(Rsh16x64 x y)  -> (SRAW (SignExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] y))))
+(Rsh16Ux64 x y) -> (SRW  (ZeroExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] y))))
+(Lsh16x64 x y)  -> (SLW  x                 (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] y))))
+
+(Rsh8x64 x y)  -> (SRAW (SignExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] y))))
+(Rsh8Ux64 x y) -> (SRW  (ZeroExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] y))))
+(Lsh8x64 x y)  -> (SLW  x                (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] y))))
+
+
+(Rsh64x32 x y)  -> (SRAD x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt32to64 y)))))
+(Rsh64Ux32 x y) -> (SRD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt32to64 y)))))
+(Lsh64x32 x y)  -> (SLD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt32to64 y)))))
+
+(Rsh32x32 x y)  -> (SRAW x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt32to64 y)))))
+(Rsh32Ux32 x y) -> (SRW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt32to64 y)))))
+(Lsh32x32 x y)  -> (SLW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt32to64 y)))))
+
+(Rsh16x32 x y)  -> (SRAW (SignExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt32to64 y)))))
+(Rsh16Ux32 x y) -> (SRW  (ZeroExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt32to64 y)))))
+(Lsh16x32 x y)  -> (SLW  x                 (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt32to64 y)))))
+
+(Rsh8x32 x y)  -> (SRAW (SignExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt32to64 y)))))
+(Rsh8Ux32 x y) -> (SRW  (ZeroExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt32to64 y)))))
+(Lsh8x32 x y)  -> (SLW  x                (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt32to64 y)))))
+
+
+(Rsh64x16 x y)  -> (SRAD x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt16to64 y)))))
+(Rsh64Ux16 x y) -> (SRD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt16to64 y)))))
+(Lsh64x16 x y)  -> (SLD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt16to64 y)))))
+
+(Rsh32x16 x y)  -> (SRAW x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt16to64 y)))))
+(Rsh32Ux16 x y) -> (SRW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt16to64 y)))))
+(Lsh32x16 x y)  -> (SLW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt16to64 y)))))
+
+(Rsh16x16 x y)  -> (SRAW (SignExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt16to64 y)))))
+(Rsh16Ux16 x y) -> (SRW  (ZeroExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt16to64 y)))))
+(Lsh16x16 x y)  -> (SLW  x                 (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt16to64 y)))))
+
+(Rsh8x16 x y)  -> (SRAW (SignExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt16to64 y)))))
+(Rsh8Ux16 x y) -> (SRW  (ZeroExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt16to64 y)))))
+(Lsh8x16 x y)  -> (SLW  x                (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt16to64 y)))))
+
+
+(Rsh64x8 x y)  -> (SRAD x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt8to64 y)))))
+(Rsh64Ux8 x y) -> (SRD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt8to64 y)))))
+(Lsh64x8 x y)  -> (SLD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt8to64 y)))))
+
+(Rsh32x8 x y)  -> (SRAW x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt8to64 y)))))
+(Rsh32Ux8 x y) -> (SRW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt8to64 y)))))
+(Lsh32x8 x y)  -> (SLW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt8to64 y)))))
+
+(Rsh16x8 x y)  -> (SRAW (SignExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt8to64 y)))))
+(Rsh16Ux8 x y) -> (SRW  (ZeroExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt8to64 y)))))
+(Lsh16x8 x y)  -> (SLW  x                 (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt8to64 y)))))
+
+(Rsh8x8 x y)  -> (SRAW (SignExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt8to64 y)))))
+(Rsh8Ux8 x y) -> (SRW  (ZeroExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt8to64 y)))))
+(Lsh8x8 x y)  -> (SLW  x                (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt8to64 y)))))
+
+// Cleaning up shift ops when input is masked
+(MaskIfNotCarry (ADDconstForCarry [c] (ANDconst [d] _))) && c < 0 && d > 0 && c + d < 0 -> (MOVDconst [-1])
+(ORN x (MOVDconst [-1])) -> x
+
+// Potentially useful optimizing rewrites.
+// (ADDconstForCarry [k] c), k < 0 && (c < 0 || k+c >= 0) -> CarrySet
+// (ADDconstForCarry [k] c), K < 0 && (c >= 0 && k+c < 0) -> CarryClear
+// (MaskIfNotCarry CarrySet) -> 0
+// (MaskIfNotCarry CarrySet) -> -1
+
+(Addr {sym} base) -> (MOVDaddr {sym} base)
+// (Addr {sym} base) -> (ADDconst {sym} base)
+(OffPtr [off] ptr) -> (ADD (MOVDconst <config.Frontend().TypeInt64()> [off]) ptr)
+
+(And64 x y) -> (AND x y)
+(And32 x y) -> (AND x y)
+(And16 x y) -> (AND x y)
+(And8  x y) -> (AND x y)
+
+(Or64 x y) -> (OR x y)
+(Or32 x y) -> (OR x y)
+(Or16 x y) -> (OR x y)
+(Or8  x y) -> (OR x y)
+
+(Xor64 x y) -> (XOR x y)
+(Xor32 x y) -> (XOR x y)
+(Xor16 x y) -> (XOR x y)
+(Xor8  x y) -> (XOR x y)
+
+(Neg64F x) -> (FNEG x)
+(Neg32F x) -> (FNEG x)
+(Neg64  x) -> (NEG x)
+(Neg32  x) -> (NEG x)
+(Neg16  x) -> (NEG x)
+(Neg8   x) -> (NEG x)
+
+(Com64 x) -> (XORconst [-1] x)
+(Com32 x) -> (XORconst [-1] x)
+(Com16 x) -> (XORconst [-1] x)
+(Com8  x) -> (XORconst [-1] x)
+
+// Lowering boolean ops
+(AndB x y) -> (AND x y)
+(OrB x y) -> (OR x y)
+(Not x) -> (XORconst [1] x)
+
+// Use ANDN for AND x NOT y
+(AND x (XORconst [-1] y)) -> (ANDN x y)
+
+// Lowering comparisons
+(EqB x y)  -> (ANDconst [1] (EQV x y))
+// Sign extension dependence on operand sign sets up for sign/zero-extension elision later
+(Eq8 x y) && isSigned(x.Type) && isSigned(y.Type) -> (Equal (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+(Eq16 x y) && isSigned(x.Type) && isSigned(y.Type) -> (Equal (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+(Eq8 x y)  -> (Equal (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Eq16 x y) -> (Equal (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Eq32 x y) -> (Equal (CMPW x y))
+(Eq64 x y) -> (Equal (CMP x y))
+(Eq32F x y) -> (Equal (FCMPU x y))
+(Eq64F x y) -> (Equal (FCMPU x y))
+(EqPtr x y) -> (Equal (CMP x y))
+
+(NeqB x y)  -> (XOR x y)
+// Like Eq8 and Eq16, prefer sign extension likely to enable later elision.
+(Neq8 x y) && isSigned(x.Type) && isSigned(y.Type) -> (NotEqual (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+(Neq16 x y) && isSigned(x.Type) && isSigned(y.Type) -> (NotEqual (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+(Neq8 x y)  -> (NotEqual (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Neq16 x y) -> (NotEqual (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Neq32 x y) -> (NotEqual (CMPW x y))
+(Neq64 x y) -> (NotEqual (CMP x y))
+(Neq32F x y) -> (NotEqual (FCMPU x y))
+(Neq64F x y) -> (NotEqual (FCMPU x y))
+(NeqPtr x y) -> (NotEqual (CMP x y))
+
+(Less8 x y)  -> (LessThan (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+(Less16 x y) -> (LessThan (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+(Less32 x y) -> (LessThan (CMPW x y))
+(Less64 x y) -> (LessThan (CMP x y))
+(Less32F x y) -> (FLessThan (FCMPU x y))
+(Less64F x y) -> (FLessThan (FCMPU x y))
+
+(Less8U x y)  -> (LessThan (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Less16U x y) -> (LessThan (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Less32U x y) -> (LessThan (CMPWU x y))
+(Less64U x y) -> (LessThan (CMPU x y))
+
+(Leq8 x y)  -> (LessEqual (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+(Leq16 x y) -> (LessEqual (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+(Leq32 x y) -> (LessEqual (CMPW x y))
+(Leq64 x y) -> (LessEqual (CMP x y))
+(Leq32F x y) -> (FLessEqual (FCMPU x y))
+(Leq64F x y) -> (FLessEqual (FCMPU x y))
+
+(Leq8U x y)  -> (LessEqual (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Leq16U x y) -> (LessEqual (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Leq32U x y) -> (LessEqual (CMPWU x y))
+(Leq64U x y) -> (LessEqual (CMPU x y))
+
+(Greater8 x y)  -> (GreaterThan (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+(Greater16 x y) -> (GreaterThan (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+(Greater32 x y) -> (GreaterThan (CMPW x y))
+(Greater64 x y) -> (GreaterThan (CMP x y))
+(Greater32F x y) -> (FGreaterThan (FCMPU x y))
+(Greater64F x y) -> (FGreaterThan (FCMPU x y))
+
+(Greater8U x y)  -> (GreaterThan (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Greater16U x y) -> (GreaterThan (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Greater32U x y) -> (GreaterThan (CMPWU x y))
+(Greater64U x y) -> (GreaterThan (CMPU x y))
+
+(Geq8 x y)  -> (GreaterEqual (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+(Geq16 x y) -> (GreaterEqual (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+(Geq32 x y) -> (GreaterEqual (CMPW x y))
+(Geq64 x y) -> (GreaterEqual (CMP x y))
+(Geq32F x y) -> (FGreaterEqual (FCMPU x y))
+(Geq64F x y) -> (FGreaterEqual (FCMPU x y))
+
+(Geq8U x y)  -> (GreaterEqual (CMPU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+(Geq16U x y) -> (GreaterEqual (CMPU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+(Geq32U x y) -> (GreaterEqual (CMPU x y))
+(Geq64U x y) -> (GreaterEqual (CMPU x y))
+
+// Absorb pseudo-ops into blocks.
+(If (Equal cc) yes no) -> (EQ cc yes no)
+(If (NotEqual cc) yes no) -> (NE cc yes no)
+(If (LessThan cc) yes no) -> (LT cc yes no)
+(If (LessEqual cc) yes no) -> (LE cc yes no)
+(If (GreaterThan cc) yes no) -> (GT cc yes no)
+(If (GreaterEqual cc) yes no) -> (GE cc yes no)
+(If (FLessThan cc) yes no) -> (FLT cc yes no)
+(If (FLessEqual cc) yes no) -> (FLE cc yes no)
+(If (FGreaterThan cc) yes no) -> (FGT cc yes no)
+(If (FGreaterEqual cc) yes no) -> (FGE cc yes no)
+
+(If cond yes no) -> (NE (CMPWconst [0] cond) yes no)
+
+// Absorb boolean tests into block
+(NE (CMPWconst [0] (Equal cc)) yes no) -> (EQ cc yes no)
+(NE (CMPWconst [0] (NotEqual cc)) yes no) -> (NE cc yes no)
+(NE (CMPWconst [0] (LessThan cc)) yes no) -> (LT cc yes no)
+(NE (CMPWconst [0] (LessEqual cc)) yes no) -> (LE cc yes no)
+(NE (CMPWconst [0] (GreaterThan cc)) yes no) -> (GT cc yes no)
+(NE (CMPWconst [0] (GreaterEqual cc)) yes no) -> (GE cc yes no)
+(NE (CMPWconst [0] (FLessThan cc)) yes no) -> (FLT cc yes no)
+(NE (CMPWconst [0] (FLessEqual cc)) yes no) -> (FLE cc yes no)
+(NE (CMPWconst [0] (FGreaterThan cc)) yes no) -> (FGT cc yes no)
+(NE (CMPWconst [0] (FGreaterEqual cc)) yes no) -> (FGE cc yes no)
+
+// Elide compares of bit tests // TODO need to make both CC and result of ANDCC available.
+(EQ (CMPconst [0] (ANDconst [c] x)) yes no) -> (EQ (ANDCCconst [c] x) yes no)
+(NE (CMPconst [0] (ANDconst [c] x)) yes no) -> (NE (ANDCCconst [c] x) yes no)
+(EQ (CMPWconst [0] (ANDconst [c] x)) yes no) -> (EQ (ANDCCconst [c] x) yes no)
+(NE (CMPWconst [0] (ANDconst [c] x)) yes no) -> (NE (ANDCCconst [c] x) yes no)
+
+// absorb flag constants into branches
+(EQ (FlagEQ) yes no) -> (First nil yes no)
+(EQ (FlagLT) yes no) -> (First nil no yes)
+(EQ (FlagGT) yes no) -> (First nil no yes)
+
+(NE (FlagEQ) yes no) -> (First nil no yes)
+(NE (FlagLT) yes no) -> (First nil yes no)
+(NE (FlagGT) yes no) -> (First nil yes no)
+
+(LT (FlagEQ) yes no) -> (First nil no yes)
+(LT (FlagLT) yes no) -> (First nil yes no)
+(LT (FlagGT) yes no) -> (First nil no yes)
+
+(LE (FlagEQ) yes no) -> (First nil yes no)
+(LE (FlagLT) yes no) -> (First nil yes no)
+(LE (FlagGT) yes no) -> (First nil no yes)
+
+(GT (FlagEQ) yes no) -> (First nil no yes)
+(GT (FlagLT) yes no) -> (First nil no yes)
+(GT (FlagGT) yes no) -> (First nil yes no)
+
+(GE (FlagEQ) yes no) -> (First nil yes no)
+(GE (FlagLT) yes no) -> (First nil no yes)
+(GE (FlagGT) yes no) -> (First nil yes no)
+
+// absorb InvertFlags into branches
+(LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
+(GT (InvertFlags cmp) yes no) -> (LT cmp yes no)
+(LE (InvertFlags cmp) yes no) -> (GE cmp yes no)
+(GE (InvertFlags cmp) yes no) -> (LE cmp yes no)
+(EQ (InvertFlags cmp) yes no) -> (EQ cmp yes no)
+(NE (InvertFlags cmp) yes no) -> (NE cmp yes no)
+
+// constant comparisons
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)==int32(y) -> (FlagEQ)
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)<int32(y)  -> (FlagLT)
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)>int32(y)  -> (FlagGT)
+
+(CMPconst (MOVDconst [x]) [y]) && int64(x)==int64(y) -> (FlagEQ)
+(CMPconst (MOVDconst [x]) [y]) && int64(x)<int64(y)  -> (FlagLT)
+(CMPconst (MOVDconst [x]) [y]) && int64(x)>int64(y)  -> (FlagGT)
+
+(CMPWUconst (MOVDconst [x]) [y]) && int32(x)==int32(y)  -> (FlagEQ)
+(CMPWUconst (MOVDconst [x]) [y]) && uint32(x)<uint32(y) -> (FlagLT)
+(CMPWUconst (MOVDconst [x]) [y]) && uint32(x)>uint32(y) -> (FlagGT)
+
+(CMPUconst (MOVDconst [x]) [y]) && int64(x)==int64(y)  -> (FlagEQ)
+(CMPUconst (MOVDconst [x]) [y]) && uint64(x)<uint64(y) -> (FlagLT)
+(CMPUconst (MOVDconst [x]) [y]) && uint64(x)>uint64(y) -> (FlagGT)
+
+// other known comparisons
+//(CMPconst (MOVBUreg _) [c]) && 0xff < c -> (FlagLT)
+//(CMPconst (MOVHUreg _) [c]) && 0xffff < c -> (FlagLT)
+//(CMPconst (ANDconst _ [m]) [n]) && 0 <= int32(m) && int32(m) < int32(n) -> (FlagLT)
+//(CMPconst (SRLconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1<<uint32(32-c)) <= uint32(n) -> (FlagLT)
+
+// absorb flag constants into boolean values
+(Equal (FlagEQ)) -> (MOVDconst [1])
+(Equal (FlagLT)) -> (MOVDconst [0])
+(Equal (FlagGT)) -> (MOVDconst [0])
+
+(NotEqual (FlagEQ)) -> (MOVDconst [0])
+(NotEqual (FlagLT)) -> (MOVDconst [1])
+(NotEqual (FlagGT)) -> (MOVDconst [1])
+
+(LessThan (FlagEQ)) -> (MOVDconst [0])
+(LessThan (FlagLT)) -> (MOVDconst [1])
+(LessThan (FlagGT)) -> (MOVDconst [0])
+
+(LessEqual (FlagEQ)) -> (MOVDconst [1])
+(LessEqual (FlagLT)) -> (MOVDconst [1])
+(LessEqual (FlagGT)) -> (MOVDconst [0])
+
+(GreaterThan (FlagEQ)) -> (MOVDconst [0])
+(GreaterThan (FlagLT)) -> (MOVDconst [0])
+(GreaterThan (FlagGT)) -> (MOVDconst [1])
+
+(GreaterEqual (FlagEQ)) -> (MOVDconst [1])
+(GreaterEqual (FlagLT)) -> (MOVDconst [0])
+(GreaterEqual (FlagGT)) -> (MOVDconst [1])
+
+// absorb InvertFlags into boolean values
+(Equal (InvertFlags x)) -> (Equal x)
+(NotEqual (InvertFlags x)) -> (NotEqual x)
+(LessThan (InvertFlags x)) -> (GreaterThan x)
+(GreaterThan (InvertFlags x)) -> (LessThan x)
+(LessEqual (InvertFlags x)) -> (GreaterEqual x)
+(GreaterEqual (InvertFlags x)) -> (LessEqual x)
+
+// Lowering loads
+(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVDload ptr mem)
+(Load <t> ptr mem) && is32BitInt(t) && isSigned(t) -> (MOVWload ptr mem)
+(Load <t> ptr mem) && is32BitInt(t) && !isSigned(t) -> (MOVWZload ptr mem)
+(Load <t> ptr mem) && is16BitInt(t) && isSigned(t) -> (MOVHload ptr mem)
+(Load <t> ptr mem) && is16BitInt(t) && !isSigned(t) -> (MOVHZload ptr mem)
+(Load <t> ptr mem) && t.IsBoolean() -> (MOVBZload ptr mem)
+(Load <t> ptr mem) && is8BitInt(t) && isSigned(t) -> (MOVBreg (MOVBZload ptr mem)) // PPC has no signed-byte load.
+(Load <t> ptr mem) && is8BitInt(t) && !isSigned(t) -> (MOVBZload ptr mem)
+
+(Load <t> ptr mem) && is32BitFloat(t) -> (FMOVSload ptr mem)
+(Load <t> ptr mem) && is64BitFloat(t) -> (FMOVDload ptr mem)
+
+(Store [8] ptr val mem) && is64BitFloat(val.Type) -> (FMOVDstore ptr val mem)
+(Store [8] ptr val mem) && is32BitFloat(val.Type) -> (FMOVDstore ptr val mem) // glitch from (Cvt32Fto64F x) -> x -- type is wrong
+(Store [4] ptr val mem) && is32BitFloat(val.Type) -> (FMOVSstore ptr val mem)
+(Store [8] ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVDstore ptr val mem)
+(Store [4] ptr val mem) && is32BitInt(val.Type) -> (MOVWstore ptr val mem)
+(Store [2] ptr val mem) -> (MOVHstore ptr val mem)
+(Store [1] ptr val mem) -> (MOVBstore ptr val mem)
+
+(Zero [s] _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstorezero destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstorezero destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 2 ->
+	(MOVBstorezero [1] destptr
+		(MOVBstorezero [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstorezero destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstorezero [2] destptr
+		(MOVHstorezero [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 4 ->
+	(MOVBstorezero [3] destptr
+		(MOVBstorezero [2] destptr
+			(MOVBstorezero [1] destptr
+				(MOVBstorezero [0] destptr mem))))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVDstorezero [0] destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstorezero [4] destptr
+		(MOVWstorezero [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstorezero [6] destptr
+		(MOVHstorezero [4] destptr
+			(MOVHstorezero [2] destptr
+				(MOVHstorezero [0] destptr mem))))
+
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstorezero [2] destptr
+		(MOVBstorezero [1] destptr
+			(MOVBstorezero [0] destptr mem)))
+
+// Zero small numbers of words directly.
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVDstorezero [8] destptr
+                (MOVDstorezero [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 24 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVDstorezero [16] destptr
+		(MOVDstorezero [8] destptr
+			(MOVDstorezero [0] destptr mem)))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 32 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVDstorezero [24] destptr
+		(MOVDstorezero [16] destptr
+			(MOVDstorezero [8] destptr
+				(MOVDstorezero [0] destptr mem))))
+
+// Large zeroing uses a loop
+(Zero [s] ptr mem)
+	&& (SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%8 != 0 ->
+	(LoweredZero [SizeAndAlign(s).Align()]
+		ptr
+		(ADDconst <ptr.Type> ptr [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)])
+		mem)
+
+// moves
+(Move [s] _ _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore dst (MOVBZload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore dst (MOVHZload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 ->
+	(MOVBstore [1] dst (MOVBZload [1] src mem)
+		(MOVBstore dst (MOVBZload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore dst (MOVWload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0 ->
+	(MOVHstore [2] dst (MOVHZload [2] src mem)
+		(MOVHstore dst (MOVHZload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 ->
+	(MOVBstore [3] dst (MOVBZload [3] src mem)
+		(MOVBstore [2] dst (MOVBZload [2] src mem)
+			(MOVBstore [1] dst (MOVBZload [1] src mem)
+				(MOVBstore dst (MOVBZload src mem) mem))))
+
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0 ->
+	(MOVDstore dst (MOVDload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0 ->
+	(MOVWstore [4] dst (MOVWZload [4] src mem)
+		(MOVWstore dst (MOVWZload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0->
+	(MOVHstore [6] dst (MOVHZload [6] src mem)
+		(MOVHstore [4] dst (MOVHZload [4] src mem)
+			(MOVHstore [2] dst (MOVHZload [2] src mem)
+				(MOVHstore dst (MOVHZload src mem) mem))))
+
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] dst (MOVBZload [2] src mem)
+		(MOVBstore [1] dst (MOVBZload [1] src mem)
+			(MOVBstore dst (MOVBZload src mem) mem)))
+
+// Large move uses a loop
+(Move [s] dst src mem)
+	&& (SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%8 != 0 ->
+	(LoweredMove [SizeAndAlign(s).Align()]
+		dst
+		src
+		(ADDconst <src.Type> src [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)])
+		mem)
+
+// Calls
+// Lowering calls
+(StaticCall [argwid] {target} mem) -> (CALLstatic [argwid] {target} mem)
+(ClosureCall [argwid] entry closure mem) -> (CALLclosure [argwid] entry closure mem)
+(DeferCall [argwid] mem) -> (CALLdefer [argwid] mem)
+(GoCall [argwid] mem) -> (CALLgo [argwid] mem)
+(InterCall [argwid] entry mem) -> (CALLinter [argwid] entry mem)
+
+// Miscellaneous
+(Convert <t> x mem) -> (MOVDconvert <t> x mem)
+(GetClosurePtr) -> (LoweredGetClosurePtr)
+(IsNonNil ptr) -> (NotEqual (CMPconst [0] ptr))
+(IsInBounds idx len) -> (LessThan (CMPU idx len))
+(IsSliceInBounds idx len) -> (LessEqual (CMPU idx len))
+(NilCheck ptr mem) -> (LoweredNilCheck ptr mem)
+
+// Optimizations
+// Note that PPC "logical" immediates come in 0:15 and 16:31 unsigned immediate forms,
+// so ORconst, XORconst easily expand into a pair.
+
+// Include very-large constants in the const-const case.
+(AND (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [c&d])
+(OR (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [c|d])
+(XOR (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [c^d])
+
+// Discover consts
+(AND x (MOVDconst [c])) && isU16Bit(c) -> (ANDconst [c] x)
+(XOR x (MOVDconst [c])) && isU32Bit(c) -> (XORconst [c] x)
+(OR x (MOVDconst [c])) && isU32Bit(c) -> (ORconst [c] x)
+(AND (MOVDconst [c]) x) && isU16Bit(c) -> (ANDconst [c] x)
+(XOR (MOVDconst [c]) x) && isU32Bit(c) -> (XORconst [c] x)
+(OR (MOVDconst [c]) x) && isU32Bit(c) -> (ORconst [c] x)
+
+// Simplify consts
+(ANDconst [c] (ANDconst [d] x)) -> (ANDconst [c&d] x)
+(ORconst [c] (ORconst [d] x)) -> (ORconst [c|d] x)
+(XORconst [c] (XORconst [d] x)) -> (XORconst [c^d] x)
+(ANDconst [-1] x) -> x
+(ANDconst [0] _) -> (MOVDconst [0])
+(XORconst [0] x) -> x
+(ORconst [-1] _) -> (MOVDconst [-1])
+(ORconst [0] x) -> x
+
+// zero-extend of small and -> small and
+(MOVBZreg y:(ANDconst [c] _)) && uint64(c) <= 0xFF -> y
+(MOVHZreg y:(ANDconst [c] _)) && uint64(c) <= 0xFFFF -> y
+(MOVWZreg y:(ANDconst [c] _)) && uint64(c) <= 0xFFFFFFFF -> y
+(MOVWZreg y:(AND (MOVDconst [c]) _)) && uint64(c) <= 0xFFFFFFFF -> y
+
+// sign extend of small-positive and -> small-positive-and
+(MOVBreg y:(ANDconst [c] _)) && uint64(c) <= 0x7F -> y
+(MOVHreg y:(ANDconst [c] _)) && uint64(c) <= 0x7FFF -> y
+(MOVWreg y:(ANDconst [c] _)) && uint64(c) <= 0xFFFF -> y // 0xFFFF is largest immediate constant, when regarded as 32-bit is > 0
+(MOVWreg y:(AND (MOVDconst [c]) _)) && uint64(c) <= 0x7FFFFFFF -> y
+
+// small and of zero-extend -> either zero-extend or small and
+  // degenerate-and
+(ANDconst [c] y:(MOVBZreg _)) && c&0xFF == 0xFF -> y
+(ANDconst [c] y:(MOVHZreg _))  && c&0xFFFF == 0xFFFF -> y
+(ANDconst [c] y:(MOVWZreg _))  && c&0xFFFFFFFF == 0xFFFFFFFF -> y
+  // normal case
+(ANDconst [c] (MOVBZreg x)) -> (ANDconst [c&0xFF] x)
+(ANDconst [c] (MOVHZreg x)) -> (ANDconst [c&0xFFFF] x)
+(ANDconst [c] (MOVWZreg x)) -> (ANDconst [c&0xFFFFFFFF] x)
+
+// Various redundant zero/sign extension combinations.
+(MOVBZreg y:(MOVBZreg _)) -> y  // repeat
+(MOVBreg y:(MOVBreg _)) -> y // repeat
+(MOVBreg (MOVBZreg x)) -> (MOVBreg x)
+(MOVBZreg (MOVBreg x)) -> (MOVBZreg x)
+
+// H - there are more combinations than these
+
+(MOVHZreg y:(MOVHZreg _)) -> y // repeat
+(MOVHZreg y:(MOVBZreg _)) -> y // wide of narrow
+
+(MOVHreg y:(MOVHreg _)) -> y // repeat
+(MOVHreg y:(MOVBreg _)) -> y // wide of narrow
+
+(MOVHreg y:(MOVHZreg x)) -> (MOVHreg x)
+(MOVHZreg y:(MOVHreg x)) -> (MOVHZreg x)
+
+// W - there are more combinations than these
+
+(MOVWZreg y:(MOVWZreg _)) -> y // repeat
+(MOVWZreg y:(MOVHZreg _)) -> y // wide of narrow
+(MOVWZreg y:(MOVBZreg _)) -> y // wide of narrow
+
+(MOVWreg y:(MOVWreg _)) -> y // repeat
+(MOVWreg y:(MOVHreg _)) -> y // wide of narrow
+(MOVWreg y:(MOVBreg _)) -> y // wide of narrow
+
+(MOVWreg y:(MOVWZreg x)) -> (MOVWreg x)
+(MOVWZreg y:(MOVWreg x)) -> (MOVWZreg x)
+
+// Arithmetic constant ops
+
+(ADD (MOVDconst [c]) x) && is32Bit(c) -> (ADDconst [c] x)
+(ADD x (MOVDconst [c])) && is32Bit(c) -> (ADDconst [c] x)
+(ADDconst [c] (ADDconst [d] x)) && is32Bit(c+d) -> (ADDconst [c+d] x)
+(ADDconst [0] x) -> x
+(SUB x (MOVDconst [c])) && is32Bit(-c) -> (ADDconst [-c] x)
+// TODO deal with subtract-from-const
+
+(ADDconst [c] (MOVDaddr [d] {sym} x)) -> (MOVDaddr [c+d] {sym} x)
+
+// Fold offsets for stores.
+(MOVDstore [off1] {sym} (ADDconst [off2] x) val mem) && is16Bit(off1+off2) -> (MOVDstore [off1+off2] {sym} x val mem)
+(MOVWstore [off1] {sym} (ADDconst [off2] x) val mem) && is16Bit(off1+off2) -> (MOVWstore [off1+off2] {sym} x val mem)
+(MOVHstore [off1] {sym} (ADDconst [off2] x) val mem) && is16Bit(off1+off2) -> (MOVHstore [off1+off2] {sym} x val mem)
+(MOVBstore [off1] {sym} (ADDconst [off2] x) val mem) && is16Bit(off1+off2) -> (MOVBstore [off1+off2] {sym} x val mem)
+
+(FMOVSstore [off1] {sym} (ADDconst [off2] ptr) val mem) && is16Bit(off1+off2) -> (FMOVSstore [off1+off2] {sym} ptr val mem)
+(FMOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem) && is16Bit(off1+off2) -> (FMOVDstore [off1+off2] {sym} ptr val mem)
+
+(MOVBstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+        (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVHstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+        (MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVWstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+        (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(MOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+        (MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+
+(FMOVSstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+        (FMOVSstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+(FMOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem) && canMergeSym(sym1,sym2) ->
+        (FMOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+
+(MOVBZload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+        (MOVBZload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+        (MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVHZload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+        (MOVHZload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+        (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVWZload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+        (MOVWZload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(MOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+        (MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(FMOVSload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+        (FMOVSload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+(FMOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) ->
+        (FMOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+
+// Fold offsets for loads.
+(FMOVSload [off1] {sym} (ADDconst [off2] ptr) mem) && is16Bit(off1+off2) -> (FMOVSload [off1+off2] {sym} ptr mem)
+(FMOVDload [off1] {sym} (ADDconst [off2] ptr) mem) && is16Bit(off1+off2) -> (FMOVDload [off1+off2] {sym} ptr mem)
+
+(MOVDload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(off1+off2) -> (MOVDload [off1+off2] {sym} x mem)
+(MOVWload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(off1+off2) -> (MOVWload [off1+off2] {sym} x mem)
+(MOVWZload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(off1+off2) -> (MOVWZload [off1+off2] {sym} x mem)
+(MOVHload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(off1+off2) -> (MOVHload [off1+off2] {sym} x mem)
+(MOVHZload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(off1+off2) -> (MOVHZload [off1+off2] {sym} x mem)
+(MOVBZload [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(off1+off2) -> (MOVBZload [off1+off2] {sym} x mem)
+
+// Store of zero -> storezero
+(MOVDstore [off] {sym} ptr (MOVDconst [c]) mem) && c == 0 -> (MOVDstorezero [off] {sym} ptr mem)
+(MOVWstore [off] {sym} ptr (MOVDconst [c]) mem) && c == 0 -> (MOVWstorezero [off] {sym} ptr mem)
+(MOVHstore [off] {sym} ptr (MOVDconst [c]) mem) && c == 0 -> (MOVHstorezero [off] {sym} ptr mem)
+(MOVBstore [off] {sym} ptr (MOVDconst [c]) mem) && c == 0 -> (MOVBstorezero [off] {sym} ptr mem)
+
+// Fold offsets for storezero
+(MOVDstorezero [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(off1+off2) ->
+    (MOVDstorezero [off1+off2] {sym} x mem)
+(MOVWstorezero [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(off1+off2) ->
+    (MOVWstorezero [off1+off2] {sym} x mem)
+(MOVHstorezero [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(off1+off2) ->
+    (MOVHstorezero [off1+off2] {sym} x mem)
+(MOVBstorezero [off1] {sym} (ADDconst [off2] x) mem) && is16Bit(off1+off2) ->
+    (MOVBstorezero [off1+off2] {sym} x mem)
+
+// Fold symbols into storezero
+(MOVDstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} x) mem) && canMergeSym(sym1,sym2) ->
+    (MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem)
+(MOVWstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} x) mem) && canMergeSym(sym1,sym2) ->
+    (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem)
+(MOVHstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} x) mem) && canMergeSym(sym1,sym2) ->
+    (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem)
+(MOVBstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} x) mem) && canMergeSym(sym1,sym2) ->
+    (MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem)
+
+// Lowering extension
+// Note: we always extend to 64 bits even though some ops don't need that many result bits.
+(SignExt8to16  x) -> (MOVBreg x)
+(SignExt8to32  x) -> (MOVBreg x)
+(SignExt8to64  x) -> (MOVBreg x)
+(SignExt16to32 x) -> (MOVHreg x)
+(SignExt16to64 x) -> (MOVHreg x)
+(SignExt32to64 x) -> (MOVWreg x)
+
+(ZeroExt8to16  x) -> (MOVBZreg x)
+(ZeroExt8to32  x) -> (MOVBZreg x)
+(ZeroExt8to64  x) -> (MOVBZreg x)
+(ZeroExt16to32 x) -> (MOVHZreg x)
+(ZeroExt16to64 x) -> (MOVHZreg x)
+(ZeroExt32to64 x) -> (MOVWZreg x)
+
+(Trunc16to8  x) -> (MOVBreg x)
+(Trunc32to8  x) -> (MOVBreg x)
+(Trunc32to16 x) -> (MOVHreg x)
+(Trunc64to8  x) -> (MOVBreg x)
+(Trunc64to16 x) -> (MOVHreg x)
+(Trunc64to32 x) -> (MOVWreg x)
+
+(Slicemask <t> x) -> (XORconst [-1] (SRADconst <t> (ADDconst <t> x [-1]) [63]))
+
+// Note that MOV??reg returns a 64-bit int, x is not necessarily that wide
+// This may interact with other patterns in the future. (Compare with arm64)
+(MOVBZreg x:(MOVBZload _ _))  -> x
+(MOVHZreg x:(MOVHZload _ _))  -> x
+(MOVHreg x:(MOVHload _ _))  -> x
+
+(MOVBZreg (MOVDconst [c]))  -> (MOVDconst [int64(uint8(c))])
+(MOVBreg (MOVDconst [c]))  -> (MOVDconst [int64(int8(c))])
+(MOVHZreg (MOVDconst [c]))  -> (MOVDconst [int64(uint16(c))])
+(MOVHreg (MOVDconst [c]))  -> (MOVDconst [int64(int16(c))])
+
+// Lose widening ops fed to to stores
+(MOVBstore [off] {sym} ptr (MOVBreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVBZreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHZreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVWstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
+(MOVWstore [off] {sym} ptr (MOVWZreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
+
+// Lose W-widening ops fed to compare-W
+(CMPW x (MOVWreg y)) -> (CMPW x y)
+(CMPW (MOVWreg x) y) -> (CMPW x y)
+(CMPWU x (MOVWZreg y)) -> (CMPWU x y)
+(CMPWU (MOVWZreg x) y) -> (CMPWU x y)
+
+(CMP x (MOVDconst [c])) && is16Bit(c) -> (CMPconst x [c])
+(CMP (MOVDconst [c]) y) && is16Bit(c) -> (InvertFlags (CMPconst y [c]))
+(CMPW x (MOVDconst [c])) && is16Bit(c) -> (CMPWconst x [c])
+(CMPW (MOVDconst [c]) y) && is16Bit(c) -> (InvertFlags (CMPWconst y [c]))
+
+(CMPU x (MOVDconst [c])) && isU16Bit(c) -> (CMPUconst x [c])
+(CMPU (MOVDconst [c]) y) && isU16Bit(c) -> (InvertFlags (CMPUconst y [c]))
+(CMPWU x (MOVDconst [c])) && isU16Bit(c) -> (CMPWUconst x [c])
+(CMPWU (MOVDconst [c]) y) && isU16Bit(c) -> (InvertFlags (CMPWUconst y [c]))
+
+// A particular pattern seen in cgo code:
+(AND (MOVDconst [c]) x:(MOVBZload _ _)) -> (ANDconst [c&0xFF] x)
+(AND x:(MOVBZload _ _) (MOVDconst [c])) -> (ANDconst [c&0xFF] x)
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
new file mode 100644
index 0000000..d7a1363
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
@@ -0,0 +1,398 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "strings"
+
+// Notes:
+//  - Less-than-64-bit integer types live in the low portion of registers.
+//    For now, the upper portion is junk; sign/zero-extension might be optimized in the future, but not yet.
+//  - Boolean types are zero or 1; stored in a byte, but loaded with AMOVBZ so the upper bytes of a register are zero.
+//  - *const instructions may use a constant larger than the instuction can encode.
+//    In this case the assembler expands to multiple instructions and uses tmp
+//    register (R31).
+
+var regNamesPPC64 = []string{
+	"R0", // REGZERO, not used, but simplifies counting in regalloc
+	"SP", // REGSP
+	"SB", // REGSB
+	"R3",
+	"R4",
+	"R5",
+	"R6",
+	"R7",
+	"R8",
+	"R9",
+	"R10",
+	"R11", // REGCTXT for closures
+	"R12",
+	"R13", // REGTLS
+	"R14",
+	"R15",
+	"R16",
+	"R17",
+	"R18",
+	"R19",
+	"R20",
+	"R21",
+	"R22",
+	"R23",
+	"R24",
+	"R25",
+	"R26",
+	"R27",
+	"R28",
+	"R29",
+	"g",   // REGG.  Using name "g" and setting Config.hasGReg makes it "just happen".
+	"R31", // REGTMP
+
+	"F0",
+	"F1",
+	"F2",
+	"F3",
+	"F4",
+	"F5",
+	"F6",
+	"F7",
+	"F8",
+	"F9",
+	"F10",
+	"F11",
+	"F12",
+	"F13",
+	"F14",
+	"F15",
+	"F16",
+	"F17",
+	"F18",
+	"F19",
+	"F20",
+	"F21",
+	"F22",
+	"F23",
+	"F24",
+	"F25",
+	"F26",
+	"F27",
+	"F28",
+	"F29",
+	"F30",
+	"F31",
+
+	// "CR0",
+	// "CR1",
+	// "CR2",
+	// "CR3",
+	// "CR4",
+	// "CR5",
+	// "CR6",
+	// "CR7",
+
+	// "CR",
+	// "XER",
+	// "LR",
+	// "CTR",
+}
+
+func init() {
+	// Make map from reg names to reg integers.
+	if len(regNamesPPC64) > 64 {
+		panic("too many registers")
+	}
+	num := map[string]int{}
+	for i, name := range regNamesPPC64 {
+		num[name] = i
+	}
+	buildReg := func(s string) regMask {
+		m := regMask(0)
+		for _, r := range strings.Split(s, " ") {
+			if n, ok := num[r]; ok {
+				m |= regMask(1) << uint(n)
+				continue
+			}
+			panic("register " + r + " not found")
+		}
+		return m
+	}
+
+	var (
+		gp = buildReg("R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29")
+		fp = buildReg("F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26")
+		sp = buildReg("SP")
+		sb = buildReg("SB")
+		gr = buildReg("g")
+		// cr  = buildReg("CR")
+		// ctr = buildReg("CTR")
+		// lr  = buildReg("LR")
+		tmp  = buildReg("R31")
+		ctxt = buildReg("R11")
+		// tls = buildReg("R13")
+		gp01        = regInfo{inputs: nil, outputs: []regMask{gp}}
+		gp11        = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}
+		gp21        = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp}}
+		gp1cr       = regInfo{inputs: []regMask{gp | sp | sb}}
+		gp2cr       = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}}
+		crgp        = regInfo{inputs: nil, outputs: []regMask{gp}}
+		gpload      = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}
+		gpstore     = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}}
+		gpstorezero = regInfo{inputs: []regMask{gp | sp | sb}} // ppc64.REGZERO is reserved zero value
+		fp01        = regInfo{inputs: nil, outputs: []regMask{fp}}
+		fp11        = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
+		fpgp        = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
+		gpfp        = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
+		fp21        = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
+		fp2cr       = regInfo{inputs: []regMask{fp, fp}}
+		fpload      = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{fp}}
+		fpstore     = regInfo{inputs: []regMask{gp | sp | sb, fp}}
+		callerSave  = regMask(gp | fp | gr)
+	)
+	ops := []opData{
+		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true},     // arg0 + arg1
+		{name: "ADDconst", argLength: 1, reg: gp11, asm: "ADD", aux: "SymOff"},    // arg0 + auxInt + aux.(*gc.Sym)
+		{name: "FADD", argLength: 2, reg: fp21, asm: "FADD", commutative: true},   // arg0+arg1
+		{name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true}, // arg0+arg1
+		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB"},                        // arg0-arg1
+		{name: "FSUB", argLength: 2, reg: fp21, asm: "FSUB"},                      // arg0-arg1
+		{name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"},                    // arg0-arg1
+
+		{name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true}, // arg0*arg1 (signed 64-bit)
+		{name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true}, // arg0*arg1 (signed 32-bit)
+
+		{name: "MULHD", argLength: 2, reg: gp21, asm: "MULHD", commutative: true},   // (arg0 * arg1) >> 64, signed
+		{name: "MULHW", argLength: 2, reg: gp21, asm: "MULHW", commutative: true},   // (arg0 * arg1) >> 32, signed
+		{name: "MULHDU", argLength: 2, reg: gp21, asm: "MULHDU", commutative: true}, // (arg0 * arg1) >> 64, unsigned
+		{name: "MULHWU", argLength: 2, reg: gp21, asm: "MULHWU", commutative: true}, // (arg0 * arg1) >> 32, unsigned
+
+		{name: "FMUL", argLength: 2, reg: fp21, asm: "FMUL", commutative: true},   // arg0*arg1
+		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true}, // arg0*arg1
+
+		{name: "SRAD", argLength: 2, reg: gp21, asm: "SRAD"}, // arg0 >>a arg1, 64 bits (all sign if arg1 & 64 != 0)
+		{name: "SRAW", argLength: 2, reg: gp21, asm: "SRAW"}, // arg0 >>a arg1, 32 bits (all sign if arg1 & 32 != 0)
+		{name: "SRD", argLength: 2, reg: gp21, asm: "SRD"},   // arg0 >> arg1, 64 bits  (0 if arg1 & 64 != 0)
+		{name: "SRW", argLength: 2, reg: gp21, asm: "SRW"},   // arg0 >> arg1, 32 bits  (0 if arg1 & 32 != 0)
+		{name: "SLD", argLength: 2, reg: gp21, asm: "SLD"},   // arg0 << arg1, 64 bits  (0 if arg1 & 64 != 0)
+		{name: "SLW", argLength: 2, reg: gp21, asm: "SLW"},   // arg0 << arg1, 32 bits  (0 if arg1 & 32 != 0)
+
+		{name: "ADDconstForCarry", argLength: 1, reg: regInfo{inputs: []regMask{gp | sp | sb}, clobbers: tmp}, aux: "Int16", asm: "ADDC", typ: "Flags"}, // _, carry := arg0 + aux
+		{name: "MaskIfNotCarry", argLength: 1, reg: crgp, asm: "ADDME", typ: "Int64"},                                                                   // carry - 1 (if carry then 0 else -1)
+
+		{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int64"}, // arg0 >>a aux, 64 bits
+		{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int64"}, // arg0 >>a aux, 32 bits
+		{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int64"},   // arg0 >> aux, 64 bits
+		{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int64"},   // arg0 >> aux, 32 bits
+		{name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int64"},   // arg0 << aux, 64 bits
+		{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int64"},   // arg0 << aux, 32 bits
+
+		{name: "FDIV", argLength: 2, reg: fp21, asm: "FDIV"},   // arg0/arg1
+		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS"}, // arg0/arg1
+
+		{name: "DIVD", argLength: 2, reg: gp21, asm: "DIVD", typ: "Int64"},   // arg0/arg1 (signed 64-bit)
+		{name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", typ: "Int32"},   // arg0/arg1 (signed 32-bit)
+		{name: "DIVDU", argLength: 2, reg: gp21, asm: "DIVDU", typ: "Int64"}, // arg0/arg1 (unsigned 64-bit)
+		{name: "DIVWU", argLength: 2, reg: gp21, asm: "DIVWU", typ: "Int32"}, // arg0/arg1 (unsigned 32-bit)
+
+		// MOD is implemented as rem := arg0 - (arg0/arg1) * arg1
+
+		// Conversions are all float-to-float register operations.  "Integer" refers to encoding in the FP register.
+		{name: "FCTIDZ", argLength: 1, reg: fp11, asm: "FCTIDZ", typ: "Float64"}, // convert float to 64-bit int round towards zero
+		{name: "FCTIWZ", argLength: 1, reg: fp11, asm: "FCTIWZ", typ: "Float64"}, // convert float to 32-bit int round towards zero
+		{name: "FCFID", argLength: 1, reg: fp11, asm: "FCFID", typ: "Float64"},   // convert 64-bit integer to float
+		{name: "FRSP", argLength: 1, reg: fp11, asm: "FRSP", typ: "Float64"},     // round float to 32-bit value
+
+		// Movement between float and integer registers with no change in bits; accomplished with stores+loads on PPC.
+		// Because the 32-bit load-literal-bits instructions have impoverished addressability, always widen the
+		// data instead and use FMOVDload and FMOVDstore instead (this will also dodge endianess issues).
+		// There are optimizations that should apply -- (Xi2f64 (MOVWload (not-ADD-ptr+offset) ) ) could use
+		// the word-load instructions.  (Xi2f64 (MOVDload ptr )) can be (FMOVDload ptr)
+
+		{name: "Xf2i64", argLength: 1, reg: fpgp, typ: "Int64", usesScratch: true},   // move 64 bits of F register into G register
+		{name: "Xi2f64", argLength: 1, reg: gpfp, typ: "Float64", usesScratch: true}, // move 64 bits of G register into F register
+
+		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},               // arg0&arg1
+		{name: "ANDN", argLength: 2, reg: gp21, asm: "ANDN"},                                // arg0&^arg1
+		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                 // arg0|arg1
+		{name: "ORN", argLength: 2, reg: gp21, asm: "ORN"},                                  // arg0|^arg1
+		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", typ: "Int64", commutative: true}, // arg0^arg1
+		{name: "EQV", argLength: 2, reg: gp21, asm: "EQV", typ: "Int64", commutative: true}, // arg0^^arg1
+		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG"},                                  // -arg0 (integer)
+		{name: "FNEG", argLength: 1, reg: fp11, asm: "FNEG"},                                // -arg0 (floating point)
+		{name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"},                              // sqrt(arg0) (floating point)
+		{name: "FSQRTS", argLength: 1, reg: fp11, asm: "FSQRTS"},                            // sqrt(arg0) (floating point, single precision)
+
+		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"},                                                                                     // arg0|aux
+		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64"},                                                                                   // arg0^aux
+		{name: "ANDconst", argLength: 1, reg: regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}, asm: "ANDCC", aux: "Int64", clobberFlags: true}, // arg0&aux // and-immediate sets CC on PPC, always.
+		{name: "ANDCCconst", argLength: 1, reg: regInfo{inputs: []regMask{gp | sp | sb}}, asm: "ANDCC", aux: "Int64", typ: "Flags"},                             // arg0&aux == 0 // and-immediate sets CC on PPC, always.
+
+		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB", typ: "Int64"},                                            // sign extend int8 to int64
+		{name: "MOVBZreg", argLength: 1, reg: gp11, asm: "MOVBZ", typ: "Int64"},                                          // zero extend uint8 to uint64
+		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH", typ: "Int64"},                                            // sign extend int16 to int64
+		{name: "MOVHZreg", argLength: 1, reg: gp11, asm: "MOVHZ", typ: "Int64"},                                          // zero extend uint16 to uint64
+		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW", typ: "Int64"},                                            // sign extend int32 to int64
+		{name: "MOVWZreg", argLength: 1, reg: gp11, asm: "MOVWZ", typ: "Int64"},                                          // zero extend uint32 to uint64
+		{name: "MOVBZload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true},  // zero extend uint8 to uint64
+		{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", typ: "Int16", faultOnNilArg0: true},    // sign extend int16 to int64
+		{name: "MOVHZload", argLength: 2, reg: gpload, asm: "MOVHZ", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true}, // zero extend uint16 to uint64
+		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", typ: "Int32", faultOnNilArg0: true},    // sign extend int32 to int64
+		{name: "MOVWZload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true}, // zero extend uint32 to uint64
+		{name: "MOVDload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", typ: "Int64", faultOnNilArg0: true},
+
+		{name: "FMOVDload", argLength: 2, reg: fpload, asm: "FMOVD", aux: "SymOff", typ: "Float64", faultOnNilArg0: true},
+		{name: "FMOVSload", argLength: 2, reg: fpload, asm: "FMOVS", aux: "SymOff", typ: "Float32", faultOnNilArg0: true},
+		{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},
+		{name: "MOVHstore", argLength: 3, reg: gpstore, asm: "MOVH", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},
+		{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},
+		{name: "MOVDstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},
+		{name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "FMOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},
+		{name: "FMOVSstore", argLength: 3, reg: fpstore, asm: "FMOVS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},
+
+		{name: "MOVBstorezero", argLength: 2, reg: gpstorezero, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // store zero byte to arg0+aux.  arg1=mem
+		{name: "MOVHstorezero", argLength: 2, reg: gpstorezero, asm: "MOVH", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // store zero 2 bytes to ...
+		{name: "MOVWstorezero", argLength: 2, reg: gpstorezero, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // store zero 4 bytes to ...
+		{name: "MOVDstorezero", argLength: 2, reg: gpstorezero, asm: "MOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // store zero 8 bytes to ...
+
+		{name: "MOVDaddr", argLength: 1, reg: regInfo{inputs: []regMask{sp | sb}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVD", rematerializeable: true}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
+
+		{name: "MOVDconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVD", typ: "Int64", rematerializeable: true}, //
+		{name: "FMOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVD", rematerializeable: true},           //
+		{name: "FMOVSconst", argLength: 0, reg: fp01, aux: "Float32", asm: "FMOVS", rematerializeable: true},           //
+		{name: "FCMPU", argLength: 2, reg: fp2cr, asm: "FCMPU", typ: "Flags"},
+
+		{name: "CMP", argLength: 2, reg: gp2cr, asm: "CMP", typ: "Flags"},     // arg0 compare to arg1
+		{name: "CMPU", argLength: 2, reg: gp2cr, asm: "CMPU", typ: "Flags"},   // arg0 compare to arg1
+		{name: "CMPW", argLength: 2, reg: gp2cr, asm: "CMPW", typ: "Flags"},   // arg0 compare to arg1
+		{name: "CMPWU", argLength: 2, reg: gp2cr, asm: "CMPWU", typ: "Flags"}, // arg0 compare to arg1
+		{name: "CMPconst", argLength: 1, reg: gp1cr, asm: "CMP", aux: "Int64", typ: "Flags"},
+		{name: "CMPUconst", argLength: 1, reg: gp1cr, asm: "CMPU", aux: "Int64", typ: "Flags"},
+		{name: "CMPWconst", argLength: 1, reg: gp1cr, asm: "CMPW", aux: "Int32", typ: "Flags"},
+		{name: "CMPWUconst", argLength: 1, reg: gp1cr, asm: "CMPWU", aux: "Int32", typ: "Flags"},
+
+		// pseudo-ops
+		{name: "Equal", argLength: 1, reg: crgp},         // bool, true flags encode x==y false otherwise.
+		{name: "NotEqual", argLength: 1, reg: crgp},      // bool, true flags encode x!=y false otherwise.
+		{name: "LessThan", argLength: 1, reg: crgp},      // bool, true flags encode  x<y false otherwise.
+		{name: "FLessThan", argLength: 1, reg: crgp},     // bool, true flags encode  x<y false otherwise.
+		{name: "LessEqual", argLength: 1, reg: crgp},     // bool, true flags encode  x<=y false otherwise.
+		{name: "FLessEqual", argLength: 1, reg: crgp},    // bool, true flags encode  x<=y false otherwise; PPC <= === !> which is wrong for NaN
+		{name: "GreaterThan", argLength: 1, reg: crgp},   // bool, true flags encode  x>y false otherwise.
+		{name: "FGreaterThan", argLength: 1, reg: crgp},  // bool, true flags encode  x>y false otherwise.
+		{name: "GreaterEqual", argLength: 1, reg: crgp},  // bool, true flags encode  x>=y false otherwise.
+		{name: "FGreaterEqual", argLength: 1, reg: crgp}, // bool, true flags encode  x>=y false otherwise.; PPC >= === !< which is wrong for NaN
+
+		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
+		// and sorts it to the very beginning of the block to prevent other
+		// use of the closure pointer.
+		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{ctxt}}},
+
+		//arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
+		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gp | sp | sb}, clobbers: tmp}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
+
+		// Convert pointer to integer, takes a memory operand for ordering.
+		{name: "MOVDconvert", argLength: 2, reg: gp11, asm: "MOVD"},
+
+		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true},                                      // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gp | sp, ctxt, 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
+		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                        // call deferproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                           // call newproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                 // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
+
+		// large or unaligned zeroing
+		// arg0 = address of memory to zero (in R3, changed as side effect)
+		// arg1 = address of the last element to zero
+		// arg2 = mem
+		// returns mem
+		//  ADD -8,R3,R3 // intermediate value not valid GC ptr, cannot expose to opt+GC
+		//	MOVDU	R0, 8(R3)
+		//	CMP	R3, Rarg1
+		//	BLE	-2(PC)
+		{
+			name:      "LoweredZero",
+			aux:       "Int64",
+			argLength: 3,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R3"), gp},
+				clobbers: buildReg("R3"),
+			},
+			clobberFlags:   true,
+			typ:            "Mem",
+			faultOnNilArg0: true,
+		},
+
+		// large or unaligned move
+		// arg0 = address of dst memory (in R3, changed as side effect)
+		// arg1 = address of src memory (in R4, changed as side effect)
+		// arg2 = address of the last element of src
+		// arg3 = mem
+		// returns mem
+		//  ADD -8,R3,R3 // intermediate value not valid GC ptr, cannot expose to opt+GC
+		//  ADD -8,R4,R4 // intermediate value not valid GC ptr, cannot expose to opt+GC
+		//	MOVDU	8(R4), Rtmp
+		//	MOVDU	Rtmp, 8(R3)
+		//	CMP	R4, Rarg2
+		//	BLT	-3(PC)
+		{
+			name:      "LoweredMove",
+			aux:       "Int64",
+			argLength: 4,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R3"), buildReg("R4"), gp},
+				clobbers: buildReg("R3 R4"),
+			},
+			clobberFlags:   true,
+			typ:            "Mem",
+			faultOnNilArg0: true,
+			faultOnNilArg1: true,
+		},
+
+		// (InvertFlags (CMP a b)) == (CMP b a)
+		// So if we want (LessThan (CMP a b)) but we can't do that because a is a constant,
+		// then we do (LessThan (InvertFlags (CMP b a))) instead.
+		// Rewrites will convert this to (GreaterThan (CMP b a)).
+		// InvertFlags is a pseudo-op which can't appear in assembly output.
+		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
+
+		// Constant flag values. For any comparison, there are 3 possible
+		// outcomes: either the three from the signed total order (<,==,>)
+		// or the three from the unsigned total order, depending on which
+		// comparison operation was used (CMP or CMPU -- PPC is different from
+		// the other architectures, which have a single comparison producing
+		// both signed and unsigned comparison results.)
+
+		// These ops are for temporary use by rewrite rules. They
+		// cannot appear in the generated assembly.
+		{name: "FlagEQ"}, // equal
+		{name: "FlagLT"}, // signed < or unsigned <
+		{name: "FlagGT"}, // signed > or unsigned >
+
+	}
+
+	blocks := []blockData{
+		{name: "EQ"},
+		{name: "NE"},
+		{name: "LT"},
+		{name: "LE"},
+		{name: "GT"},
+		{name: "GE"},
+		{name: "FLT"},
+		{name: "FLE"},
+		{name: "FGT"},
+		{name: "FGE"},
+	}
+
+	archs = append(archs, arch{
+		name:            "PPC64",
+		pkg:             "cmd/internal/obj/ppc64",
+		genfile:         "../../ppc64/ssa.go",
+		ops:             ops,
+		blocks:          blocks,
+		regnames:        regNamesPPC64,
+		gpregmask:       gp,
+		fpregmask:       fp,
+		framepointerreg: int8(num["SP"]),
+		linkreg:         -1, // not used
+	})
+}
diff --git a/src/cmd/compile/internal/ssa/gen/S390X.rules b/src/cmd/compile/internal/ssa/gen/S390X.rules
new file mode 100644
index 0000000..3e0533a
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/S390X.rules
@@ -0,0 +1,1649 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Lowering arithmetic
+(Add64  x y) -> (ADD  x y)
+(AddPtr x y) -> (ADD  x y)
+(Add32  x y) -> (ADDW  x y)
+(Add16  x y) -> (ADDW  x y)
+(Add8   x y) -> (ADDW  x y)
+(Add32F x y) -> (FADDS x y)
+(Add64F x y) -> (FADD x y)
+
+(Sub64  x y) -> (SUB  x y)
+(SubPtr x y) -> (SUB  x y)
+(Sub32  x y) -> (SUBW  x y)
+(Sub16  x y) -> (SUBW  x y)
+(Sub8   x y) -> (SUBW  x y)
+(Sub32F x y) -> (FSUBS x y)
+(Sub64F x y) -> (FSUB x y)
+
+(Mul64  x y) -> (MULLD  x y)
+(Mul32  x y) -> (MULLW  x y)
+(Mul16  x y) -> (MULLW  x y)
+(Mul8   x y) -> (MULLW  x y)
+(Mul32F x y) -> (FMULS x y)
+(Mul64F x y) -> (FMUL x y)
+
+(Div32F x y) -> (FDIVS x y)
+(Div64F x y) -> (FDIV x y)
+
+(Div64  x y) -> (DIVD  x y)
+(Div64u x y) -> (DIVDU x y)
+// DIVW/DIVWU has a 64-bit dividend and a 32-bit divisor,
+// so a sign/zero extension of the dividend is required.
+(Div32  x y) -> (DIVW  (MOVWreg x) y)
+(Div32u x y) -> (DIVWU (MOVWZreg x) y)
+(Div16  x y) -> (DIVW  (MOVHreg x) (MOVHreg y))
+(Div16u x y) -> (DIVWU (MOVHZreg x) (MOVHZreg y))
+(Div8   x y) -> (DIVW  (MOVBreg x) (MOVBreg y))
+(Div8u  x y) -> (DIVWU (MOVBZreg x) (MOVBZreg y))
+
+(Hmul64  x y) -> (MULHD  x y)
+(Hmul64u x y) -> (MULHDU x y)
+(Hmul32  x y) -> (SRDconst [32] (MULLD (MOVWreg x) (MOVWreg y)))
+(Hmul32u x y) -> (SRDconst [32] (MULLD (MOVWZreg x) (MOVWZreg y)))
+(Hmul16  x y) -> (SRDconst [16] (MULLW (MOVHreg x) (MOVHreg y)))
+(Hmul16u x y) -> (SRDconst [16] (MULLW (MOVHZreg x) (MOVHZreg y)))
+(Hmul8   x y) -> (SRDconst [8] (MULLW (MOVBreg x) (MOVBreg y)))
+(Hmul8u  x y) -> (SRDconst [8] (MULLW (MOVBZreg x) (MOVBZreg y)))
+
+(Mod64  x y) -> (MODD  x y)
+(Mod64u x y) -> (MODDU x y)
+// MODW/MODWU has a 64-bit dividend and a 32-bit divisor,
+// so a sign/zero extension of the dividend is required.
+(Mod32  x y) -> (MODW  (MOVWreg x) y)
+(Mod32u x y) -> (MODWU (MOVWZreg x) y)
+(Mod16  x y) -> (MODW  (MOVHreg x) (MOVHreg y))
+(Mod16u x y) -> (MODWU (MOVHZreg x) (MOVHZreg y))
+(Mod8   x y) -> (MODW  (MOVBreg x) (MOVBreg y))
+(Mod8u  x y) -> (MODWU (MOVBZreg x) (MOVBZreg y))
+
+(Avg64u <t> x y) -> (ADD (ADD <t> (SRDconst <t> x [1]) (SRDconst <t> y [1])) (ANDconst <t> (AND <t> x y) [1]))
+
+(And64 x y) -> (AND x y)
+(And32 x y) -> (ANDW x y)
+(And16 x y) -> (ANDW x y)
+(And8  x y) -> (ANDW x y)
+
+(Or64 x y) -> (OR x y)
+(Or32 x y) -> (ORW x y)
+(Or16 x y) -> (ORW x y)
+(Or8  x y) -> (ORW x y)
+
+(Xor64 x y) -> (XOR x y)
+(Xor32 x y) -> (XORW x y)
+(Xor16 x y) -> (XORW x y)
+(Xor8  x y) -> (XORW x y)
+
+(Neg64  x) -> (NEG x)
+(Neg32  x) -> (NEGW x)
+(Neg16  x) -> (NEGW (MOVHreg x))
+(Neg8   x) -> (NEGW (MOVBreg x))
+(Neg32F x) -> (FNEGS x)
+(Neg64F x) -> (FNEG x)
+
+(Com64 x) -> (NOT x)
+(Com32 x) -> (NOTW x)
+(Com16 x) -> (NOTW x)
+(Com8  x) -> (NOTW x)
+(NOT x) && true -> (XOR (MOVDconst [-1]) x)
+(NOTW x) && true -> (XORWconst [-1] x)
+
+// Lowering boolean ops
+(AndB x y) -> (ANDW x y)
+(OrB x y) -> (ORW x y)
+(Not x) -> (XORWconst [1] x)
+
+// Lowering pointer arithmetic
+(OffPtr [off] ptr:(SP)) -> (MOVDaddr [off] ptr)
+(OffPtr [off] ptr) && is32Bit(off) -> (ADDconst [off] ptr)
+(OffPtr [off] ptr) -> (ADD (MOVDconst [off]) ptr)
+
+// Ctz(x) = 64 - findLeftmostOne((x-1)&^x)
+(Ctz64 <t> x) -> (SUB (MOVDconst [64]) (FLOGR (AND <t> (SUBconst <t> [1] x) (NOT <t> x))))
+(Ctz32 <t> x) -> (SUB (MOVDconst [64]) (FLOGR (MOVWZreg (ANDW <t> (SUBWconst <t> [1] x) (NOTW <t> x)))))
+
+(Bswap64 x) -> (MOVDBR x)
+(Bswap32 x) -> (MOVWBR x)
+
+(Sqrt x) -> (FSQRT x)
+
+// Atomic loads.
+(AtomicLoad32 ptr mem) -> (MOVWZatomicload ptr mem)
+(AtomicLoad64 ptr mem) -> (MOVDatomicload ptr mem)
+(AtomicLoadPtr ptr mem) -> (MOVDatomicload ptr mem)
+
+// Atomic stores.
+(AtomicStore32 ptr val mem) -> (MOVWatomicstore ptr val mem)
+(AtomicStore64 ptr val mem) -> (MOVDatomicstore ptr val mem)
+(AtomicStorePtrNoWB ptr val mem) -> (MOVDatomicstore ptr val mem)
+
+// Atomic adds.
+(AtomicAdd32 ptr val mem) -> (AddTupleFirst32 (LAA ptr val mem) val)
+(AtomicAdd64 ptr val mem) -> (AddTupleFirst64 (LAAG ptr val mem) val)
+(Select0 <t> (AddTupleFirst32 tuple val)) -> (ADDW val (Select0 <t> tuple))
+(Select1     (AddTupleFirst32 tuple _  )) -> (Select1 tuple)
+(Select0 <t> (AddTupleFirst64 tuple val)) -> (ADD val (Select0 <t> tuple))
+(Select1     (AddTupleFirst64 tuple _  )) -> (Select1 tuple)
+
+// Atomic exchanges.
+(AtomicExchange32 ptr val mem) -> (LoweredAtomicExchange32 ptr val mem)
+(AtomicExchange64 ptr val mem) -> (LoweredAtomicExchange64 ptr val mem)
+
+// Atomic compare and swap.
+(AtomicCompareAndSwap32 ptr old new_ mem) -> (LoweredAtomicCas32 ptr old new_ mem)
+(AtomicCompareAndSwap64 ptr old new_ mem) -> (LoweredAtomicCas64 ptr old new_ mem)
+
+// Lowering extension
+// Note: we always extend to 64 bits even though some ops don't need that many result bits.
+(SignExt8to16  x) -> (MOVBreg x)
+(SignExt8to32  x) -> (MOVBreg x)
+(SignExt8to64  x) -> (MOVBreg x)
+(SignExt16to32 x) -> (MOVHreg x)
+(SignExt16to64 x) -> (MOVHreg x)
+(SignExt32to64 x) -> (MOVWreg x)
+
+(ZeroExt8to16  x) -> (MOVBZreg x)
+(ZeroExt8to32  x) -> (MOVBZreg x)
+(ZeroExt8to64  x) -> (MOVBZreg x)
+(ZeroExt16to32 x) -> (MOVHZreg x)
+(ZeroExt16to64 x) -> (MOVHZreg x)
+(ZeroExt32to64 x) -> (MOVWZreg x)
+
+(Slicemask <t> x) -> (XOR (MOVDconst [-1]) (SRADconst <t> (SUBconst <t> x [1]) [63]))
+
+// Lowering truncation
+// Because we ignore high parts of registers, truncates are just copies.
+(Trunc16to8  x) -> x
+(Trunc32to8  x) -> x
+(Trunc32to16 x) -> x
+(Trunc64to8  x) -> x
+(Trunc64to16 x) -> x
+(Trunc64to32 x) -> x
+
+// Lowering float <-> int
+(Cvt32to32F x) -> (CEFBRA x)
+(Cvt32to64F x) -> (CDFBRA x)
+(Cvt64to32F x) -> (CEGBRA x)
+(Cvt64to64F x) -> (CDGBRA x)
+
+(Cvt32Fto32 x) -> (CFEBRA x)
+(Cvt32Fto64 x) -> (CGEBRA x)
+(Cvt64Fto32 x) -> (CFDBRA x)
+(Cvt64Fto64 x) -> (CGDBRA x)
+
+(Cvt32Fto64F x) -> (LDEBR x)
+(Cvt64Fto32F x) -> (LEDBR x)
+
+// Lowering shifts
+// Unsigned shifts need to return 0 if shift amount is >= width of shifted value.
+//   result = (arg << shift) & (shift >= argbits ? 0 : 0xffffffffffffffff)
+(Lsh64x64 <t> x y) -> (AND (SLD <t> x y) (SUBEcarrymask <t> (CMPUconst y [63])))
+(Lsh64x32 <t> x y) -> (AND (SLD <t> x y) (SUBEcarrymask <t> (CMPWUconst y [63])))
+(Lsh64x16 <t> x y) -> (AND (SLD <t> x y) (SUBEcarrymask <t> (CMPWUconst (MOVHZreg y) [63])))
+(Lsh64x8  <t> x y) -> (AND (SLD <t> x y) (SUBEcarrymask <t> (CMPWUconst (MOVBZreg y) [63])))
+
+(Lsh32x64 <t> x y) -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPUconst y [31])))
+(Lsh32x32 <t> x y) -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst y [31])))
+(Lsh32x16 <t> x y) -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [31])))
+(Lsh32x8  <t> x y) -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [31])))
+
+(Lsh16x64 <t> x y) -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPUconst y [31])))
+(Lsh16x32 <t> x y) -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst y [31])))
+(Lsh16x16 <t> x y) -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [31])))
+(Lsh16x8  <t> x y) -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [31])))
+
+(Lsh8x64 <t> x y)  -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPUconst y [31])))
+(Lsh8x32 <t> x y)  -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst y [31])))
+(Lsh8x16 <t> x y)  -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [31])))
+(Lsh8x8  <t> x y)  -> (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [31])))
+
+(Lrot64 <t> x [c]) -> (RLLGconst <t> [c&63] x)
+(Lrot32 <t> x [c]) -> (RLLconst <t> [c&31] x)
+
+(Rsh64Ux64 <t> x y) -> (AND (SRD <t> x y) (SUBEcarrymask <t> (CMPUconst y [63])))
+(Rsh64Ux32 <t> x y) -> (AND (SRD <t> x y) (SUBEcarrymask <t> (CMPWUconst y [63])))
+(Rsh64Ux16 <t> x y) -> (AND (SRD <t> x y) (SUBEcarrymask <t> (CMPWUconst (MOVHZreg y) [63])))
+(Rsh64Ux8  <t> x y) -> (AND (SRD <t> x y) (SUBEcarrymask <t> (CMPWUconst (MOVBZreg y) [63])))
+
+(Rsh32Ux64 <t> x y) -> (ANDW (SRW <t> x y) (SUBEWcarrymask <t> (CMPUconst y [31])))
+(Rsh32Ux32 <t> x y) -> (ANDW (SRW <t> x y) (SUBEWcarrymask <t> (CMPWUconst y [31])))
+(Rsh32Ux16 <t> x y) -> (ANDW (SRW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [31])))
+(Rsh32Ux8  <t> x y) -> (ANDW (SRW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [31])))
+
+(Rsh16Ux64 <t> x y) -> (ANDW (SRW <t> (MOVHZreg x) y) (SUBEWcarrymask <t> (CMPUconst y [15])))
+(Rsh16Ux32 <t> x y) -> (ANDW (SRW <t> (MOVHZreg x) y) (SUBEWcarrymask <t> (CMPWUconst y [15])))
+(Rsh16Ux16 <t> x y) -> (ANDW (SRW <t> (MOVHZreg x) y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [15])))
+(Rsh16Ux8  <t> x y) -> (ANDW (SRW <t> (MOVHZreg x) y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [15])))
+
+(Rsh8Ux64 <t> x y)  -> (ANDW (SRW <t> (MOVBZreg x) y) (SUBEWcarrymask <t> (CMPUconst y [7])))
+(Rsh8Ux32 <t> x y)  -> (ANDW (SRW <t> (MOVBZreg x) y) (SUBEWcarrymask <t> (CMPWUconst y [7])))
+(Rsh8Ux16 <t> x y)  -> (ANDW (SRW <t> (MOVBZreg x) y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [7])))
+(Rsh8Ux8  <t> x y)  -> (ANDW (SRW <t> (MOVBZreg x) y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [7])))
+
+// Signed right shift needs to return 0/-1 if shift amount is >= width of shifted value.
+// We implement this by setting the shift value to -1 (all ones) if the shift value is >= width.
+(Rsh64x64 <t> x y) -> (SRAD <t> x (OR <y.Type> y (NOT <y.Type> (SUBEcarrymask <y.Type> (CMPUconst y [63])))))
+(Rsh64x32 <t> x y) -> (SRAD <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst y [63])))))
+(Rsh64x16 <t> x y) -> (SRAD <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVHZreg y) [63])))))
+(Rsh64x8  <t> x y) -> (SRAD <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVBZreg y) [63])))))
+
+(Rsh32x64 <t> x y) -> (SRAW <t> x (OR <y.Type> y (NOT <y.Type> (SUBEcarrymask <y.Type> (CMPUconst y [31])))))
+(Rsh32x32 <t> x y) -> (SRAW <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst y [31])))))
+(Rsh32x16 <t> x y) -> (SRAW <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVHZreg y) [31])))))
+(Rsh32x8  <t> x y) -> (SRAW <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVBZreg y) [31])))))
+
+(Rsh16x64 <t> x y) -> (SRAW <t> (MOVHreg x) (OR <y.Type> y (NOT <y.Type> (SUBEcarrymask <y.Type> (CMPUconst y [15])))))
+(Rsh16x32 <t> x y) -> (SRAW <t> (MOVHreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst y [15])))))
+(Rsh16x16 <t> x y) -> (SRAW <t> (MOVHreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVHZreg y) [15])))))
+(Rsh16x8  <t> x y) -> (SRAW <t> (MOVHreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVBZreg y) [15])))))
+
+(Rsh8x64 <t> x y)  -> (SRAW <t> (MOVBreg x) (OR <y.Type> y (NOT <y.Type> (SUBEcarrymask <y.Type> (CMPUconst y [7])))))
+(Rsh8x32 <t> x y)  -> (SRAW <t> (MOVBreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst y [7])))))
+(Rsh8x16 <t> x y)  -> (SRAW <t> (MOVBreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVHZreg y) [7])))))
+(Rsh8x8  <t> x y)  -> (SRAW <t> (MOVBreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVBZreg y) [7])))))
+
+// Lowering comparisons
+(Less64  x y) -> (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+(Less32  x y) -> (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+(Less16  x y) -> (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+(Less8   x y) -> (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+(Less64U x y) -> (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPU x y))
+(Less32U x y) -> (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPWU x y))
+(Less16U x y) -> (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVHZreg x) (MOVHZreg y)))
+(Less8U  x y) -> (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVBZreg x) (MOVBZreg y)))
+// Use SETG with reversed operands to dodge NaN case.
+(Less64F x y) -> (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMP y x))
+(Less32F x y) -> (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMPS y x))
+
+(Leq64  x y) -> (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+(Leq32  x y) -> (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+(Leq16  x y) -> (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+(Leq8   x y) -> (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+(Leq64U x y) -> (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPU x y))
+(Leq32U x y) -> (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPWU x y))
+(Leq16U x y) -> (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVHZreg x) (MOVHZreg y)))
+(Leq8U  x y) -> (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVBZreg x) (MOVBZreg y)))
+// Use SETGE with reversed operands to dodge NaN case.
+(Leq64F x y) -> (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMP y x))
+(Leq32F x y) -> (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMPS y x))
+
+(Greater64  x y) -> (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+(Greater32  x y) -> (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+(Greater16  x y) -> (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+(Greater8   x y) -> (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+(Greater64U x y) -> (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMPU x y))
+(Greater32U x y) -> (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMPWU x y))
+(Greater16U x y) -> (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVHZreg x) (MOVHZreg y)))
+(Greater8U  x y) -> (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVBZreg x) (MOVBZreg y)))
+(Greater64F x y) -> (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMP x y))
+(Greater32F x y) -> (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMPS x y))
+
+(Geq64  x y) -> (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+(Geq32  x y) -> (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+(Geq16  x y) -> (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+(Geq8   x y) -> (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+(Geq64U x y) -> (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMPU x y))
+(Geq32U x y) -> (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMPWU x y))
+(Geq16U x y) -> (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVHZreg x) (MOVHZreg y)))
+(Geq8U  x y) -> (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVBZreg x) (MOVBZreg y)))
+(Geq64F x y) -> (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMP x y))
+(Geq32F x y) -> (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMPS x y))
+
+(Eq64  x y) -> (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+(Eq32  x y) -> (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+(Eq16  x y) -> (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+(Eq8   x y) -> (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+(EqB   x y) -> (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+(EqPtr x y) -> (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+(Eq64F x y) -> (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (FCMP x y))
+(Eq32F x y) -> (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (FCMPS x y))
+
+(Neq64  x y) -> (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+(Neq32  x y) -> (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+(Neq16  x y) -> (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+(Neq8   x y) -> (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+(NeqB   x y) -> (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+(NeqPtr x y) -> (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+(Neq64F x y) -> (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (FCMP x y))
+(Neq32F x y) -> (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (FCMPS x y))
+
+// Lowering loads
+(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVDload ptr mem)
+(Load <t> ptr mem) && is32BitInt(t) -> (MOVWZload ptr mem)
+(Load <t> ptr mem) && is16BitInt(t) -> (MOVHZload ptr mem)
+(Load <t> ptr mem) && (t.IsBoolean() || is8BitInt(t)) -> (MOVBZload ptr mem)
+(Load <t> ptr mem) && is32BitFloat(t) -> (FMOVSload ptr mem)
+(Load <t> ptr mem) && is64BitFloat(t) -> (FMOVDload ptr mem)
+
+// Lowering stores
+// These more-specific FP versions of Store pattern should come first.
+(Store [8] ptr val mem) && is64BitFloat(val.Type) -> (FMOVDstore ptr val mem)
+(Store [4] ptr val mem) && is32BitFloat(val.Type) -> (FMOVSstore ptr val mem)
+
+(Store [8] ptr val mem) -> (MOVDstore ptr val mem)
+(Store [4] ptr val mem) -> (MOVWstore ptr val mem)
+(Store [2] ptr val mem) -> (MOVHstore ptr val mem)
+(Store [1] ptr val mem) -> (MOVBstore ptr val mem)
+
+// Lowering moves
+
+// Load and store for small copies.
+(Move [s] _ _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstore dst (MOVBZload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 2 -> (MOVHstore dst (MOVHZload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 4 -> (MOVWstore dst (MOVWZload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 8 -> (MOVDstore dst (MOVDload src mem) mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 16 ->
+	(MOVDstore [8] dst (MOVDload [8] src mem)
+		(MOVDstore dst (MOVDload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 24 ->
+        (MOVDstore [16] dst (MOVDload [16] src mem)
+	        (MOVDstore [8] dst (MOVDload [8] src mem)
+                (MOVDstore dst (MOVDload src mem) mem)))
+(Move [s] dst src mem)  && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstore [2] dst (MOVBZload [2] src mem)
+		(MOVHstore dst (MOVHZload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 5 ->
+	(MOVBstore [4] dst (MOVBZload [4] src mem)
+		(MOVWstore dst (MOVWZload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 6 ->
+	(MOVHstore [4] dst (MOVHZload [4] src mem)
+		(MOVWstore dst (MOVWZload src mem) mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() == 7 ->
+	(MOVBstore [6] dst (MOVBZload [6] src mem)
+		(MOVHstore [4] dst (MOVHZload [4] src mem)
+			(MOVWstore dst (MOVWZload src mem) mem)))
+
+// MVC for other moves. Use up to 4 instructions (sizes up to 1024 bytes).
+(Move [s] dst src mem) && SizeAndAlign(s).Size() > 0 && SizeAndAlign(s).Size() <= 256 ->
+	(MVC [makeValAndOff(SizeAndAlign(s).Size(), 0)] dst src mem)
+(Move [s] dst src mem) && SizeAndAlign(s).Size() > 256 && SizeAndAlign(s).Size() <= 512 ->
+	(MVC [makeValAndOff(SizeAndAlign(s).Size()-256, 256)] dst src (MVC [makeValAndOff(256, 0)] dst src mem))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() > 512 && SizeAndAlign(s).Size() <= 768 ->
+	(MVC [makeValAndOff(SizeAndAlign(s).Size()-512, 512)] dst src (MVC [makeValAndOff(256, 256)] dst src (MVC [makeValAndOff(256, 0)] dst src mem)))
+(Move [s] dst src mem) && SizeAndAlign(s).Size() > 768 && SizeAndAlign(s).Size() <= 1024 ->
+	(MVC [makeValAndOff(SizeAndAlign(s).Size()-768, 768)] dst src (MVC [makeValAndOff(256, 512)] dst src (MVC [makeValAndOff(256, 256)] dst src (MVC [makeValAndOff(256, 0)] dst src mem))))
+
+// Move more than 1024 bytes using a loop.
+(Move [s] dst src mem) && SizeAndAlign(s).Size() > 1024 ->
+	(LoweredMove [SizeAndAlign(s).Size()%256] dst src (ADDconst <src.Type> src [(SizeAndAlign(s).Size()/256)*256]) mem)
+
+// Lowering Zero instructions
+(Zero [s] _ mem) && SizeAndAlign(s).Size() == 0 -> mem
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 1 -> (MOVBstoreconst [0] destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 2 -> (MOVHstoreconst [0] destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 4 -> (MOVWstoreconst [0] destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 8 -> (MOVDstoreconst [0] destptr mem)
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 3 ->
+	(MOVBstoreconst [makeValAndOff(0,2)] destptr
+		(MOVHstoreconst [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 5 ->
+	(MOVBstoreconst [makeValAndOff(0,4)] destptr
+		(MOVWstoreconst [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 6 ->
+	(MOVHstoreconst [makeValAndOff(0,4)] destptr
+		(MOVWstoreconst [0] destptr mem))
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() == 7 ->
+	(MOVWstoreconst [makeValAndOff(0,3)] destptr
+		(MOVWstoreconst [0] destptr mem))
+
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() > 0 && SizeAndAlign(s).Size() <= 1024 ->
+	(CLEAR [makeValAndOff(SizeAndAlign(s).Size(), 0)] destptr mem)
+
+// Move more than 1024 bytes using a loop.
+(Zero [s] destptr mem) && SizeAndAlign(s).Size() > 1024 ->
+	(LoweredZero [SizeAndAlign(s).Size()%256] destptr (ADDconst <destptr.Type> destptr [(SizeAndAlign(s).Size()/256)*256]) mem)
+
+// Lowering constants
+(Const8   [val]) -> (MOVDconst [val])
+(Const16  [val]) -> (MOVDconst [val])
+(Const32  [val]) -> (MOVDconst [val])
+(Const64  [val]) -> (MOVDconst [val])
+(Const32F [val]) -> (FMOVSconst [val])
+(Const64F [val]) -> (FMOVDconst [val])
+(ConstNil) -> (MOVDconst [0])
+(ConstBool [b]) -> (MOVDconst [b])
+
+// Lowering calls
+(StaticCall [argwid] {target} mem) -> (CALLstatic [argwid] {target} mem)
+(ClosureCall [argwid] entry closure mem) -> (CALLclosure [argwid] entry closure mem)
+(DeferCall [argwid] mem) -> (CALLdefer [argwid] mem)
+(GoCall [argwid] mem) -> (CALLgo [argwid] mem)
+(InterCall [argwid] entry mem) -> (CALLinter [argwid] entry mem)
+
+// Miscellaneous
+(Convert <t> x mem) -> (MOVDconvert <t> x mem)
+(IsNonNil p) -> (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMPconst p [0]))
+(IsInBounds idx len) -> (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPU idx len))
+(IsSliceInBounds idx len) -> (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPU idx len))
+(NilCheck ptr mem) -> (LoweredNilCheck ptr mem)
+(GetG mem) -> (LoweredGetG mem)
+(GetClosurePtr) -> (LoweredGetClosurePtr)
+(Addr {sym} base) -> (MOVDaddr {sym} base)
+(ITab (Load ptr mem)) -> (MOVDload ptr mem)
+
+// block rewrites
+(If (MOVDLT (MOVDconst [0]) (MOVDconst [1]) cmp) yes no) -> (LT cmp yes no)
+(If (MOVDLE (MOVDconst [0]) (MOVDconst [1]) cmp) yes no) -> (LE cmp yes no)
+(If (MOVDGT (MOVDconst [0]) (MOVDconst [1]) cmp) yes no) -> (GT cmp yes no)
+(If (MOVDGE (MOVDconst [0]) (MOVDconst [1]) cmp) yes no) -> (GE cmp yes no)
+(If (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) cmp) yes no) -> (EQ cmp yes no)
+(If (MOVDNE (MOVDconst [0]) (MOVDconst [1]) cmp) yes no) -> (NE cmp yes no)
+
+// Special case for floating point - LF/LEF not generated.
+(If (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) cmp) yes no) -> (GTF cmp yes no)
+(If (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) cmp) yes no) -> (GEF cmp yes no)
+
+(If cond yes no) -> (NE (CMPWconst [0] (MOVBZreg cond)) yes no)
+
+// ***************************
+// Above: lowering rules
+// Below: optimizations
+// ***************************
+// TODO: Should the optimizations be a separate pass?
+
+// Fold sign extensions into conditional moves of constants.
+// Designed to remove the MOVBZreg inserted by the If lowering.
+(MOVBZreg x:(MOVDLT (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
+(MOVBZreg x:(MOVDLE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
+(MOVBZreg x:(MOVDGT (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
+(MOVBZreg x:(MOVDGE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
+(MOVBZreg x:(MOVDEQ (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
+(MOVBZreg x:(MOVDNE (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
+(MOVBZreg x:(MOVDGTnoinv (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
+(MOVBZreg x:(MOVDGEnoinv (MOVDconst [c]) (MOVDconst [d]) _)) && int64(uint8(c)) == c && int64(uint8(d)) == d -> x
+
+// Fold boolean tests into blocks.
+(NE (CMPWconst [0] (MOVDLT (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no) -> (LT cmp yes no)
+(NE (CMPWconst [0] (MOVDLE (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no) -> (LE cmp yes no)
+(NE (CMPWconst [0] (MOVDGT (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no) -> (GT cmp yes no)
+(NE (CMPWconst [0] (MOVDGE (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no) -> (GE cmp yes no)
+(NE (CMPWconst [0] (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no) -> (EQ cmp yes no)
+(NE (CMPWconst [0] (MOVDNE (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no) -> (NE cmp yes no)
+(NE (CMPWconst [0] (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no) -> (GTF cmp yes no)
+(NE (CMPWconst [0] (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no) -> (GEF cmp yes no)
+
+// Fold constants into instructions.
+(ADD x (MOVDconst [c])) && is32Bit(c) -> (ADDconst [c] x)
+(ADD (MOVDconst [c]) x) && is32Bit(c) -> (ADDconst [c] x)
+(ADDW x (MOVDconst [c])) -> (ADDWconst [c] x)
+(ADDW (MOVDconst [c]) x) -> (ADDWconst [c] x)
+
+(SUB x (MOVDconst [c])) && is32Bit(c) -> (SUBconst x [c])
+(SUB (MOVDconst [c]) x) && is32Bit(c) -> (NEG (SUBconst <v.Type> x [c]))
+(SUBW x (MOVDconst [c])) -> (SUBWconst x [c])
+(SUBW (MOVDconst [c]) x) -> (NEGW (SUBWconst <v.Type> x [c]))
+
+(MULLD x (MOVDconst [c])) && is32Bit(c) -> (MULLDconst [c] x)
+(MULLD (MOVDconst [c]) x) && is32Bit(c) -> (MULLDconst [c] x)
+(MULLW x (MOVDconst [c])) -> (MULLWconst [c] x)
+(MULLW (MOVDconst [c]) x) -> (MULLWconst [c] x)
+
+// NILF instructions leave the high 32 bits unchanged which is
+// equivalent to the leftmost 32 bits being set.
+// TODO(mundaym): modify the assembler to accept 64-bit values
+// and use isU32Bit(^c).
+(AND x (MOVDconst [c])) && is32Bit(c) && c < 0 -> (ANDconst [c] x)
+(AND (MOVDconst [c]) x) && is32Bit(c) && c < 0 -> (ANDconst [c] x)
+(ANDW x (MOVDconst [c])) -> (ANDWconst [c] x)
+(ANDW (MOVDconst [c]) x) -> (ANDWconst [c] x)
+
+(ANDWconst [c] (ANDWconst [d] x)) -> (ANDWconst [c & d] x)
+(ANDconst [c] (ANDconst [d] x)) -> (ANDconst [c & d] x)
+
+(OR x (MOVDconst [c])) && isU32Bit(c) -> (ORconst [c] x)
+(OR (MOVDconst [c]) x) && isU32Bit(c) -> (ORconst [c] x)
+(ORW x (MOVDconst [c])) -> (ORWconst [c] x)
+(ORW (MOVDconst [c]) x) -> (ORWconst [c] x)
+
+(XOR x (MOVDconst [c])) && isU32Bit(c) -> (XORconst [c] x)
+(XOR (MOVDconst [c]) x) && isU32Bit(c) -> (XORconst [c] x)
+(XORW x (MOVDconst [c])) -> (XORWconst [c] x)
+(XORW (MOVDconst [c]) x) -> (XORWconst [c] x)
+
+(SLD x (MOVDconst [c])) -> (SLDconst [c&63] x)
+(SLW x (MOVDconst [c])) -> (SLWconst [c&63] x)
+(SRD x (MOVDconst [c])) -> (SRDconst [c&63] x)
+(SRW x (MOVDconst [c])) -> (SRWconst [c&63] x)
+(SRAD x (MOVDconst [c])) -> (SRADconst [c&63] x)
+(SRAW x (MOVDconst [c])) -> (SRAWconst [c&63] x)
+
+(SRAW x (ANDWconst [63] y)) -> (SRAW x y)
+(SRAD x (ANDconst [63] y)) -> (SRAD x y)
+(SLW x (ANDWconst [63] y)) -> (SLW x y)
+(SLD x (ANDconst [63] y)) -> (SLD x y)
+(SRW x (ANDWconst [63] y)) -> (SRW x y)
+(SRD x (ANDconst [63] y)) -> (SRD x y)
+
+(CMP x (MOVDconst [c])) && is32Bit(c) -> (CMPconst x [c])
+(CMP (MOVDconst [c]) x) && is32Bit(c) -> (InvertFlags (CMPconst x [c]))
+(CMPW x (MOVDconst [c])) -> (CMPWconst x [c])
+(CMPW (MOVDconst [c]) x) -> (InvertFlags (CMPWconst x [c]))
+(CMPU x (MOVDconst [c])) && is32Bit(c) -> (CMPUconst x [int64(uint32(c))])
+(CMPU (MOVDconst [c]) x) && is32Bit(c) -> (InvertFlags (CMPUconst x [int64(uint32(c))]))
+(CMPWU x (MOVDconst [c])) -> (CMPWUconst x [int64(uint32(c))])
+(CMPWU (MOVDconst [c]) x) -> (InvertFlags (CMPWUconst x [int64(uint32(c))]))
+
+// Using MOV{W,H,B}Zreg instead of AND is cheaper.
+(AND (MOVDconst [0xFF]) x) -> (MOVBZreg x)
+(AND x (MOVDconst [0xFF])) -> (MOVBZreg x)
+(AND (MOVDconst [0xFFFF]) x) -> (MOVHZreg x)
+(AND x (MOVDconst [0xFFFF])) -> (MOVHZreg x)
+(AND (MOVDconst [0xFFFFFFFF]) x) -> (MOVWZreg x)
+(AND x (MOVDconst [0xFFFFFFFF])) -> (MOVWZreg x)
+(ANDWconst [0xFF] x) -> (MOVBZreg x)
+(ANDWconst [0xFFFF] x) -> (MOVHZreg x)
+
+// strength reduction
+(MULLDconst [-1] x) -> (NEG x)
+(MULLDconst [0] _) -> (MOVDconst [0])
+(MULLDconst [1] x) -> x
+(MULLDconst [c] x) && isPowerOfTwo(c) -> (SLDconst [log2(c)] x)
+(MULLDconst [c] x) && isPowerOfTwo(c+1) && c >= 15 -> (SUB (SLDconst <v.Type> [log2(c+1)] x) x)
+(MULLDconst [c] x) && isPowerOfTwo(c-1) && c >= 17 -> (ADD (SLDconst <v.Type> [log2(c-1)] x) x)
+
+(MULLWconst [-1] x) -> (NEGW x)
+(MULLWconst [0] _) -> (MOVDconst [0])
+(MULLWconst [1] x) -> x
+(MULLWconst [c] x) && isPowerOfTwo(c) -> (SLWconst [log2(c)] x)
+(MULLWconst [c] x) && isPowerOfTwo(c+1) && c >= 15 -> (SUBW (SLWconst <v.Type> [log2(c+1)] x) x)
+(MULLWconst [c] x) && isPowerOfTwo(c-1) && c >= 17 -> (ADDW (SLWconst <v.Type> [log2(c-1)] x) x)
+
+// Fold ADD into MOVDaddr. Odd offsets from SB shouldn't be folded (LARL can't handle them).
+(ADDconst [c] (MOVDaddr [d] {s} x:(SB))) && ((c+d)&1 == 0) && is32Bit(c+d) -> (MOVDaddr [c+d] {s} x)
+(ADDconst [c] (MOVDaddr [d] {s} x)) && x.Op != OpSB && is20Bit(c+d) -> (MOVDaddr [c+d] {s} x)
+(ADD x (MOVDaddr [c] {s} y)) && x.Op != OpSB && y.Op != OpSB -> (MOVDaddridx [c] {s} x y)
+(ADD (MOVDaddr [c] {s} x) y) && x.Op != OpSB && y.Op != OpSB -> (MOVDaddridx [c] {s} x y)
+
+// fold ADDconst into MOVDaddrx
+(ADDconst [c] (MOVDaddridx [d] {s} x y)) && is20Bit(c+d) -> (MOVDaddridx [c+d] {s} x y)
+(MOVDaddridx [c] {s} (ADDconst [d] x) y) && is20Bit(c+d) && x.Op != OpSB -> (MOVDaddridx [c+d] {s} x y)
+(MOVDaddridx [c] {s} x (ADDconst [d] y)) && is20Bit(c+d) && y.Op != OpSB -> (MOVDaddridx [c+d] {s} x y)
+
+// reverse ordering of compare instruction
+(MOVDLT x y (InvertFlags cmp)) -> (MOVDGT x y cmp)
+(MOVDGT x y (InvertFlags cmp)) -> (MOVDLT x y cmp)
+(MOVDLE x y (InvertFlags cmp)) -> (MOVDGE x y cmp)
+(MOVDGE x y (InvertFlags cmp)) -> (MOVDLE x y cmp)
+(MOVDEQ x y (InvertFlags cmp)) -> (MOVDEQ x y cmp)
+(MOVDNE x y (InvertFlags cmp)) -> (MOVDNE x y cmp)
+
+// don't extend after proper load
+(MOVBreg x:(MOVBload _ _)) -> x
+(MOVBZreg x:(MOVBZload _ _)) -> x
+(MOVHreg x:(MOVBload _ _)) -> x
+(MOVHreg x:(MOVBZload _ _)) -> x
+(MOVHreg x:(MOVHload _ _)) -> x
+(MOVHZreg x:(MOVBZload _ _)) -> x
+(MOVHZreg x:(MOVHZload _ _)) -> x
+(MOVWreg x:(MOVBload _ _)) -> x
+(MOVWreg x:(MOVBZload _ _)) -> x
+(MOVWreg x:(MOVHload _ _)) -> x
+(MOVWreg x:(MOVHZload _ _)) -> x
+(MOVWreg x:(MOVWload _ _)) -> x
+(MOVWZreg x:(MOVBZload _ _)) -> x
+(MOVWZreg x:(MOVHZload _ _)) -> x
+(MOVWZreg x:(MOVWZload _ _)) -> x
+
+// don't extend if argument is already extended
+(MOVBreg x:(Arg <t>)) && is8BitInt(t) && isSigned(t) -> x
+(MOVBZreg x:(Arg <t>)) && is8BitInt(t) && !isSigned(t) -> x
+(MOVHreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t)) && isSigned(t) -> x
+(MOVHZreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t)) && !isSigned(t) -> x
+(MOVWreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && isSigned(t) -> x
+(MOVWZreg x:(Arg <t>)) && (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && !isSigned(t) -> x
+
+// fold double extensions
+(MOVBreg x:(MOVBreg _)) -> x
+(MOVBZreg x:(MOVBZreg _)) -> x
+(MOVHreg x:(MOVBreg _)) -> x
+(MOVHreg x:(MOVBZreg _)) -> x
+(MOVHreg x:(MOVHreg _)) -> x
+(MOVHZreg x:(MOVBZreg _)) -> x
+(MOVHZreg x:(MOVHZreg _)) -> x
+(MOVWreg x:(MOVBreg _)) -> x
+(MOVWreg x:(MOVBZreg _)) -> x
+(MOVWreg x:(MOVHreg _)) -> x
+(MOVWreg x:(MOVHreg _)) -> x
+(MOVWreg x:(MOVWreg _)) -> x
+(MOVWZreg x:(MOVBZreg _)) -> x
+(MOVWZreg x:(MOVHZreg _)) -> x
+(MOVWZreg x:(MOVWZreg _)) -> x
+
+// fold extensions into constants
+(MOVBreg (MOVDconst [c])) -> (MOVDconst [int64(int8(c))])
+(MOVBZreg (MOVDconst [c])) -> (MOVDconst [int64(uint8(c))])
+(MOVHreg (MOVDconst [c])) -> (MOVDconst [int64(int16(c))])
+(MOVHZreg (MOVDconst [c])) -> (MOVDconst [int64(uint16(c))])
+(MOVWreg (MOVDconst [c])) -> (MOVDconst [int64(int32(c))])
+(MOVWZreg (MOVDconst [c])) -> (MOVDconst [int64(uint32(c))])
+
+// sign extended loads
+// Note: The combined instruction must end up in the same block
+// as the original load. If not, we end up making a value with
+// memory type live in two different blocks, which can lead to
+// multiple memory values alive simultaneously.
+// Make sure we don't combine these ops if the load has another use.
+// This prevents a single load from being split into multiple loads
+// which then might return different values.  See test/atomicload.go.
+(MOVBreg x:(MOVBZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
+(MOVBZreg x:(MOVBZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBZload <v.Type> [off] {sym} ptr mem)
+(MOVHreg x:(MOVHZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHload <v.Type> [off] {sym} ptr mem)
+(MOVHZreg x:(MOVHZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHZload <v.Type> [off] {sym} ptr mem)
+(MOVWreg x:(MOVWZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
+(MOVWZreg x:(MOVWZload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZload <v.Type> [off] {sym} ptr mem)
+
+(MOVBZreg x:(MOVBZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBZloadidx <v.Type> [off] {sym} ptr idx mem)
+(MOVHZreg x:(MOVHZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVHZloadidx <v.Type> [off] {sym} ptr idx mem)
+(MOVWZreg x:(MOVWZloadidx [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWZloadidx <v.Type> [off] {sym} ptr idx mem)
+
+// replace load from same location as preceding store with copy
+(MOVBZload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+(MOVHZload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+(MOVWZload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+(MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> x
+
+// Don't extend before storing
+(MOVWstore [off] {sym} ptr (MOVWreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVBreg x) mem) -> (MOVBstore [off] {sym} ptr x mem)
+(MOVWstore [off] {sym} ptr (MOVWZreg x) mem) -> (MOVWstore [off] {sym} ptr x mem)
+(MOVHstore [off] {sym} ptr (MOVHZreg x) mem) -> (MOVHstore [off] {sym} ptr x mem)
+(MOVBstore [off] {sym} ptr (MOVBZreg 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 ADDconst get eliminated, we still have to compute the ADDconst and we now
+// have potentially two live values (ptr and (ADDconst [off] ptr)) instead of one.
+// Nevertheless, let's do it!
+(MOVDload   [off1] {sym} (ADDconst [off2] ptr) mem) && is20Bit(off1+off2) -> (MOVDload  [off1+off2] {sym} ptr mem)
+(MOVWload   [off1] {sym} (ADDconst [off2] ptr) mem) && is20Bit(off1+off2) -> (MOVWload  [off1+off2] {sym} ptr mem)
+(MOVHload   [off1] {sym} (ADDconst [off2] ptr) mem) && is20Bit(off1+off2) -> (MOVHload  [off1+off2] {sym} ptr mem)
+(MOVBload   [off1] {sym} (ADDconst [off2] ptr) mem) && is20Bit(off1+off2) -> (MOVBload  [off1+off2] {sym} ptr mem)
+(MOVWZload  [off1] {sym} (ADDconst [off2] ptr) mem) && is20Bit(off1+off2) -> (MOVWZload [off1+off2] {sym} ptr mem)
+(MOVHZload  [off1] {sym} (ADDconst [off2] ptr) mem) && is20Bit(off1+off2) -> (MOVHZload [off1+off2] {sym} ptr mem)
+(MOVBZload  [off1] {sym} (ADDconst [off2] ptr) mem) && is20Bit(off1+off2) -> (MOVBZload [off1+off2] {sym} ptr mem)
+(FMOVSload  [off1] {sym} (ADDconst [off2] ptr) mem) && is20Bit(off1+off2) -> (FMOVSload [off1+off2] {sym} ptr mem)
+(FMOVDload  [off1] {sym} (ADDconst [off2] ptr) mem) && is20Bit(off1+off2) -> (FMOVDload [off1+off2] {sym} ptr mem)
+
+(MOVDstore  [off1] {sym} (ADDconst [off2] ptr) val mem) && is20Bit(off1+off2) -> (MOVDstore  [off1+off2] {sym} ptr val mem)
+(MOVWstore  [off1] {sym} (ADDconst [off2] ptr) val mem) && is20Bit(off1+off2) -> (MOVWstore  [off1+off2] {sym} ptr val mem)
+(MOVHstore  [off1] {sym} (ADDconst [off2] ptr) val mem) && is20Bit(off1+off2) -> (MOVHstore  [off1+off2] {sym} ptr val mem)
+(MOVBstore  [off1] {sym} (ADDconst [off2] ptr) val mem) && is20Bit(off1+off2) -> (MOVBstore  [off1+off2] {sym} ptr val mem)
+(FMOVSstore [off1] {sym} (ADDconst [off2] ptr) val mem) && is20Bit(off1+off2) -> (FMOVSstore [off1+off2] {sym} ptr val mem)
+(FMOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem) && is20Bit(off1+off2) -> (FMOVDstore [off1+off2] {sym} ptr val mem)
+
+// Fold constants into stores.
+(MOVDstore [off] {sym} ptr (MOVDconst [c]) mem) && validValAndOff(c,off) && int64(int16(c)) == c && ptr.Op != OpSB ->
+	(MOVDstoreconst [makeValAndOff(c,off)] {sym} ptr mem)
+(MOVWstore [off] {sym} ptr (MOVDconst [c]) mem) && validOff(off) && int64(int16(c)) == c && ptr.Op != OpSB ->
+	(MOVWstoreconst [makeValAndOff(int64(int32(c)),off)] {sym} ptr mem)
+(MOVHstore [off] {sym} ptr (MOVDconst [c]) mem) && validOff(off) && ptr.Op != OpSB ->
+	(MOVHstoreconst [makeValAndOff(int64(int16(c)),off)] {sym} ptr mem)
+(MOVBstore [off] {sym} ptr (MOVDconst [c]) mem) && validOff(off) && ptr.Op != OpSB ->
+	(MOVBstoreconst [makeValAndOff(int64(int8(c)),off)] {sym} ptr mem)
+
+// Fold address offsets into constant stores.
+(MOVDstoreconst [sc] {s} (ADDconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) ->
+	(MOVDstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+(MOVWstoreconst [sc] {s} (ADDconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) ->
+	(MOVWstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+(MOVHstoreconst [sc] {s} (ADDconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) ->
+	(MOVHstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+(MOVBstoreconst [sc] {s} (ADDconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) ->
+	(MOVBstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+
+// We need to fold MOVDaddr into the MOVx ops so that the live variable analysis knows
+// what variables are being read/written by the ops.
+(MOVDload  [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVDload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+(MOVWZload  [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVWZload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+(MOVHZload  [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVHZload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+(MOVBZload  [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVBZload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+(FMOVSload [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(FMOVSload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+(FMOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(FMOVDload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+
+(MOVBload [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+(MOVHload [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVHload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+(MOVWload [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+
+(MOVDstore  [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVDstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+(MOVWstore  [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVWstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+(MOVHstore  [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVHstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+(MOVBstore  [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVBstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+(FMOVSstore [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(FMOVSstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+(FMOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(FMOVDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+
+(MOVDstoreconst [sc] {sym1} (MOVDaddr [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) ->
+	(MOVDstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+(MOVWstoreconst [sc] {sym1} (MOVDaddr [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) ->
+	(MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+(MOVHstoreconst [sc] {sym1} (MOVDaddr [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) ->
+	(MOVHstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+(MOVBstoreconst [sc] {sym1} (MOVDaddr [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) ->
+	(MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+
+// generating indexed loads and stores
+(MOVBZload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVBZloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+(MOVHZload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVHZloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+(MOVWZload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVWZloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+(MOVDload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVDloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+(FMOVSload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(FMOVSloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+(FMOVDload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(FMOVDloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+
+(MOVBstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVBstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+(MOVHstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVHstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+(MOVWstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVWstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+(MOVDstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(MOVDstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+(FMOVSstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(FMOVSstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+(FMOVDstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
+	(FMOVDstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+
+(MOVBZload [off] {sym} (ADD ptr idx) mem) && ptr.Op != OpSB -> (MOVBZloadidx [off] {sym} ptr idx mem)
+(MOVHZload [off] {sym} (ADD ptr idx) mem) && ptr.Op != OpSB -> (MOVHZloadidx [off] {sym} ptr idx mem)
+(MOVWZload [off] {sym} (ADD ptr idx) mem) && ptr.Op != OpSB -> (MOVWZloadidx [off] {sym} ptr idx mem)
+(MOVDload [off] {sym} (ADD ptr idx) mem) && ptr.Op != OpSB -> (MOVDloadidx [off] {sym} ptr idx mem)
+(FMOVSload [off] {sym} (ADD ptr idx) mem) && ptr.Op != OpSB -> (FMOVSloadidx [off] {sym} ptr idx mem)
+(FMOVDload [off] {sym} (ADD ptr idx) mem) && ptr.Op != OpSB -> (FMOVDloadidx [off] {sym} ptr idx mem)
+(MOVBstore [off] {sym} (ADD ptr idx) val mem) && ptr.Op != OpSB -> (MOVBstoreidx [off] {sym} ptr idx val mem)
+(MOVHstore [off] {sym} (ADD ptr idx) val mem) && ptr.Op != OpSB -> (MOVHstoreidx [off] {sym} ptr idx val mem)
+(MOVWstore [off] {sym} (ADD ptr idx) val mem) && ptr.Op != OpSB -> (MOVWstoreidx [off] {sym} ptr idx val mem)
+(MOVDstore [off] {sym} (ADD ptr idx) val mem) && ptr.Op != OpSB -> (MOVDstoreidx [off] {sym} ptr idx val mem)
+(FMOVSstore [off] {sym} (ADD ptr idx) val mem) && ptr.Op != OpSB -> (FMOVSstoreidx [off] {sym} ptr idx val mem)
+(FMOVDstore [off] {sym} (ADD ptr idx) val mem) && ptr.Op != OpSB -> (FMOVDstoreidx [off] {sym} ptr idx val mem)
+
+// combine ADD into indexed loads and stores
+(MOVBZloadidx [c] {sym} (ADDconst [d] ptr) idx mem) -> (MOVBZloadidx [c+d] {sym} ptr idx mem)
+(MOVHZloadidx [c] {sym} (ADDconst [d] ptr) idx mem) -> (MOVHZloadidx [c+d] {sym} ptr idx mem)
+(MOVWZloadidx [c] {sym} (ADDconst [d] ptr) idx mem) -> (MOVWZloadidx [c+d] {sym} ptr idx mem)
+(MOVDloadidx [c] {sym} (ADDconst [d] ptr) idx mem) -> (MOVDloadidx [c+d] {sym} ptr idx mem)
+(FMOVSloadidx [c] {sym} (ADDconst [d] ptr) idx mem) -> (FMOVSloadidx [c+d] {sym} ptr idx mem)
+(FMOVDloadidx [c] {sym} (ADDconst [d] ptr) idx mem) -> (FMOVDloadidx [c+d] {sym} ptr idx mem)
+
+(MOVBstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem) -> (MOVBstoreidx [c+d] {sym} ptr idx val mem)
+(MOVHstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem) -> (MOVHstoreidx [c+d] {sym} ptr idx val mem)
+(MOVWstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem) -> (MOVWstoreidx [c+d] {sym} ptr idx val mem)
+(MOVDstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem) -> (MOVDstoreidx [c+d] {sym} ptr idx val mem)
+(FMOVSstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem) -> (FMOVSstoreidx [c+d] {sym} ptr idx val mem)
+(FMOVDstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem) -> (FMOVDstoreidx [c+d] {sym} ptr idx val mem)
+
+(MOVBZloadidx [c] {sym} ptr (ADDconst [d] idx) mem) -> (MOVBZloadidx [c+d] {sym} ptr idx mem)
+(MOVHZloadidx [c] {sym} ptr (ADDconst [d] idx) mem) -> (MOVHZloadidx [c+d] {sym} ptr idx mem)
+(MOVWZloadidx [c] {sym} ptr (ADDconst [d] idx) mem) -> (MOVWZloadidx [c+d] {sym} ptr idx mem)
+(MOVDloadidx [c] {sym} ptr (ADDconst [d] idx) mem) -> (MOVDloadidx [c+d] {sym} ptr idx mem)
+(FMOVSloadidx [c] {sym} ptr (ADDconst [d] idx) mem) -> (FMOVSloadidx [c+d] {sym} ptr idx mem)
+(FMOVDloadidx [c] {sym} ptr (ADDconst [d] idx) mem) -> (FMOVDloadidx [c+d] {sym} ptr idx mem)
+
+(MOVBstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem) -> (MOVBstoreidx [c+d] {sym} ptr idx val mem)
+(MOVHstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem) -> (MOVHstoreidx [c+d] {sym} ptr idx val mem)
+(MOVWstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem) -> (MOVWstoreidx [c+d] {sym} ptr idx val mem)
+(MOVDstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem) -> (MOVDstoreidx [c+d] {sym} ptr idx val mem)
+(FMOVSstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem) -> (FMOVSstoreidx [c+d] {sym} ptr idx val mem)
+(FMOVDstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem) -> (FMOVDstoreidx [c+d] {sym} ptr idx val mem)
+
+// MOVDaddr into MOVDaddridx
+(MOVDaddridx [off1] {sym1} (MOVDaddr [off2] {sym2} x) y) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB ->
+       (MOVDaddridx [off1+off2] {mergeSym(sym1,sym2)} x y)
+(MOVDaddridx [off1] {sym1} x (MOVDaddr [off2] {sym2} y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && y.Op != OpSB ->
+       (MOVDaddridx [off1+off2] {mergeSym(sym1,sym2)} x y)
+
+// Absorb InvertFlags into branches.
+(LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
+(GT (InvertFlags cmp) yes no) -> (LT cmp yes no)
+(LE (InvertFlags cmp) yes no) -> (GE cmp yes no)
+(GE (InvertFlags cmp) yes no) -> (LE cmp yes no)
+(EQ (InvertFlags cmp) yes no) -> (EQ cmp yes no)
+(NE (InvertFlags cmp) yes no) -> (NE cmp yes no)
+
+// Constant comparisons.
+(CMPconst (MOVDconst [x]) [y]) && x==y -> (FlagEQ)
+(CMPconst (MOVDconst [x]) [y]) && x<y -> (FlagLT)
+(CMPconst (MOVDconst [x]) [y]) && x>y -> (FlagGT)
+(CMPUconst (MOVDconst [x]) [y]) && uint64(x)==uint64(y) -> (FlagEQ)
+(CMPUconst (MOVDconst [x]) [y]) && uint64(x)<uint64(y) -> (FlagLT)
+(CMPUconst (MOVDconst [x]) [y]) && uint64(x)>uint64(y) -> (FlagGT)
+
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)==int32(y) -> (FlagEQ)
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)<int32(y) -> (FlagLT)
+(CMPWconst (MOVDconst [x]) [y]) && int32(x)>int32(y) -> (FlagGT)
+(CMPWUconst (MOVDconst [x]) [y]) && uint32(x)==uint32(y) -> (FlagEQ)
+(CMPWUconst (MOVDconst [x]) [y]) && uint32(x)<uint32(y) -> (FlagLT)
+(CMPWUconst (MOVDconst [x]) [y]) && uint32(x)>uint32(y) -> (FlagGT)
+
+// Other known comparisons.
+(CMPconst (MOVBZreg _) [c]) && 0xFF < c -> (FlagLT)
+(CMPconst (MOVHZreg _) [c]) && 0xFFFF < c -> (FlagLT)
+(CMPconst (MOVWZreg _) [c]) && 0xFFFFFFFF < c -> (FlagLT)
+(CMPWconst (SRWconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n) -> (FlagLT)
+(CMPconst (SRDconst _ [c]) [n]) && 0 <= n && 0 < c && c <= 64 && (1<<uint64(64-c)) <= uint64(n) -> (FlagLT)
+(CMPconst (ANDconst _ [m]) [n]) && 0 <= m && m < n -> (FlagLT)
+(CMPWconst (ANDWconst _ [m]) [n]) && 0 <= int32(m) && int32(m) < int32(n) -> (FlagLT)
+
+// Absorb flag constants into SBB ops.
+(SUBEcarrymask (FlagEQ)) -> (MOVDconst [-1])
+(SUBEcarrymask (FlagLT)) -> (MOVDconst [-1])
+(SUBEcarrymask (FlagGT)) -> (MOVDconst [0])
+(SUBEWcarrymask (FlagEQ)) -> (MOVDconst [-1])
+(SUBEWcarrymask (FlagLT)) -> (MOVDconst [-1])
+(SUBEWcarrymask (FlagGT)) -> (MOVDconst [0])
+
+// Absorb flag constants into branches.
+(EQ (FlagEQ) yes no) -> (First nil yes no)
+(EQ (FlagLT) yes no) -> (First nil no yes)
+(EQ (FlagGT) yes no) -> (First nil no yes)
+
+(NE (FlagEQ) yes no) -> (First nil no yes)
+(NE (FlagLT) yes no) -> (First nil yes no)
+(NE (FlagGT) yes no) -> (First nil yes no)
+
+(LT (FlagEQ) yes no) -> (First nil no yes)
+(LT (FlagLT) yes no) -> (First nil yes no)
+(LT (FlagGT) yes no) -> (First nil no yes)
+
+(LE (FlagEQ) yes no) -> (First nil yes no)
+(LE (FlagLT) yes no) -> (First nil yes no)
+(LE (FlagGT) yes no) -> (First nil no yes)
+
+(GT (FlagEQ) yes no) -> (First nil no yes)
+(GT (FlagLT) yes no) -> (First nil no yes)
+(GT (FlagGT) yes no) -> (First nil yes no)
+
+(GE (FlagEQ) yes no) -> (First nil yes no)
+(GE (FlagLT) yes no) -> (First nil no yes)
+(GE (FlagGT) yes no) -> (First nil yes no)
+
+// Absorb flag constants into SETxx ops.
+(MOVDEQ _ x (FlagEQ)) -> x
+(MOVDEQ y _ (FlagLT)) -> y
+(MOVDEQ y _ (FlagGT)) -> y
+
+(MOVDNE _ y (FlagEQ)) -> y
+(MOVDNE x _ (FlagLT)) -> x
+(MOVDNE x _ (FlagGT)) -> x
+
+(MOVDLT y _ (FlagEQ)) -> y
+(MOVDLT _ x (FlagLT)) -> x
+(MOVDLT y _ (FlagGT)) -> y
+
+(MOVDLE _ x (FlagEQ)) -> x
+(MOVDLE _ x (FlagLT)) -> x
+(MOVDLE y _ (FlagGT)) -> y
+
+(MOVDGT y _ (FlagEQ)) -> y
+(MOVDGT y _ (FlagLT)) -> y
+(MOVDGT _ x (FlagGT)) -> x
+
+(MOVDGE _ x (FlagEQ)) -> x
+(MOVDGE y _ (FlagLT)) -> y
+(MOVDGE _ x (FlagGT)) -> x
+
+// Remove redundant *const ops
+(ADDconst [0] x) -> x
+(ADDWconst [c] x) && int32(c)==0 -> x
+(SUBconst [0] x) -> x
+(SUBWconst [c] x) && int32(c) == 0 -> x
+(ANDconst [0] _)                 -> (MOVDconst [0])
+(ANDWconst [c] _) && int32(c)==0  -> (MOVDconst [0])
+(ANDconst [-1] x)                -> x
+(ANDWconst [c] x) && int32(c)==-1 -> x
+(ORconst [0] x)                  -> x
+(ORWconst [c] x) && int32(c)==0   -> x
+(ORconst [-1] _)                 -> (MOVDconst [-1])
+(ORWconst [c] _) && int32(c)==-1  -> (MOVDconst [-1])
+(XORconst [0] x)                  -> x
+(XORWconst [c] x) && int32(c)==0   -> x
+
+// Convert constant subtracts to constant adds.
+(SUBconst [c] x) && c != -(1<<31) -> (ADDconst [-c] x)
+(SUBWconst [c] x) -> (ADDWconst [int64(int32(-c))] x)
+
+// generic constant folding
+// TODO: more of this
+(ADDconst [c] (MOVDconst [d])) -> (MOVDconst [c+d])
+(ADDWconst [c] (MOVDconst [d])) -> (MOVDconst [int64(int32(c+d))])
+(ADDconst [c] (ADDconst [d] x)) && is32Bit(c+d) -> (ADDconst [c+d] x)
+(ADDWconst [c] (ADDWconst [d] x)) -> (ADDWconst [int64(int32(c+d))] x)
+(SUBconst (MOVDconst [d]) [c]) -> (MOVDconst [d-c])
+(SUBconst (SUBconst x [d]) [c]) && is32Bit(-c-d) -> (ADDconst [-c-d] x)
+(SRADconst [c] (MOVDconst [d])) -> (MOVDconst [d>>uint64(c)])
+(SRAWconst [c] (MOVDconst [d])) -> (MOVDconst [d>>uint64(c)])
+(NEG (MOVDconst [c])) -> (MOVDconst [-c])
+(NEGW (MOVDconst [c])) -> (MOVDconst [int64(int32(-c))])
+(MULLDconst [c] (MOVDconst [d])) -> (MOVDconst [c*d])
+(MULLWconst [c] (MOVDconst [d])) -> (MOVDconst [int64(int32(c*d))])
+(AND (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [c&d])
+(ANDconst [c] (MOVDconst [d])) -> (MOVDconst [c&d])
+(ANDWconst [c] (MOVDconst [d])) -> (MOVDconst [c&d])
+(OR (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [c|d])
+(ORconst [c] (MOVDconst [d])) -> (MOVDconst [c|d])
+(ORWconst [c] (MOVDconst [d])) -> (MOVDconst [c|d])
+(XOR (MOVDconst [c]) (MOVDconst [d])) -> (MOVDconst [c^d])
+(XORconst [c] (MOVDconst [d])) -> (MOVDconst [c^d])
+(XORWconst [c] (MOVDconst [d])) -> (MOVDconst [c^d])
+
+// generic simplifications
+// TODO: more of this
+(ADD x (NEG y)) -> (SUB x y)
+(ADDW x (NEGW y)) -> (SUBW x y)
+(SUB x x) -> (MOVDconst [0])
+(SUBW x x) -> (MOVDconst [0])
+(AND x x) -> x
+(ANDW x x) -> x
+(OR x x) -> x
+(ORW x x) -> x
+(XOR x x) -> (MOVDconst [0])
+(XORW x x) -> (MOVDconst [0])
+
+// Fold memory operations into operations.
+// Exclude global data (SB) because these instructions cannot handle relative addresses.
+// TODO(mundaym): use LARL in the assembler to handle SB?
+// TODO(mundaym): indexed versions of these?
+(ADD <t> x g:(MOVDload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ADDload <t> [off] {sym} x ptr mem)
+(ADD <t> g:(MOVDload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ADDload <t> [off] {sym} x ptr mem)
+(ADDW <t> x g:(MOVWload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ADDWload <t> [off] {sym} x ptr mem)
+(ADDW <t> g:(MOVWload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ADDWload <t> [off] {sym} x ptr mem)
+(ADDW <t> x g:(MOVWZload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ADDWload <t> [off] {sym} x ptr mem)
+(ADDW <t> g:(MOVWZload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ADDWload <t> [off] {sym} x ptr mem)
+(MULLD <t> x g:(MOVDload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (MULLDload <t> [off] {sym} x ptr mem)
+(MULLD <t> g:(MOVDload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (MULLDload <t> [off] {sym} x ptr mem)
+(MULLW <t> x g:(MOVWload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (MULLWload <t> [off] {sym} x ptr mem)
+(MULLW <t> g:(MOVWload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (MULLWload <t> [off] {sym} x ptr mem)
+(MULLW <t> x g:(MOVWZload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (MULLWload <t> [off] {sym} x ptr mem)
+(MULLW <t> g:(MOVWZload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (MULLWload <t> [off] {sym} x ptr mem)
+(SUB <t> x g:(MOVDload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (SUBload <t> [off] {sym} x ptr mem)
+(SUBW <t> x g:(MOVWload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (SUBWload <t> [off] {sym} x ptr mem)
+(SUBW <t> x g:(MOVWZload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (SUBWload <t> [off] {sym} x ptr mem)
+(AND <t> x g:(MOVDload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ANDload <t> [off] {sym} x ptr mem)
+(AND <t> g:(MOVDload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ANDload <t> [off] {sym} x ptr mem)
+(ANDW <t> x g:(MOVWload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ANDWload <t> [off] {sym} x ptr mem)
+(ANDW <t> g:(MOVWload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ANDWload <t> [off] {sym} x ptr mem)
+(ANDW <t> x g:(MOVWZload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ANDWload <t> [off] {sym} x ptr mem)
+(ANDW <t> g:(MOVWZload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ANDWload <t> [off] {sym} x ptr mem)
+(OR <t> x g:(MOVDload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ORload <t> [off] {sym} x ptr mem)
+(OR <t> g:(MOVDload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ORload <t> [off] {sym} x ptr mem)
+(ORW <t> x g:(MOVWload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ORWload <t> [off] {sym} x ptr mem)
+(ORW <t> g:(MOVWload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ORWload <t> [off] {sym} x ptr mem)
+(ORW <t> x g:(MOVWZload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ORWload <t> [off] {sym} x ptr mem)
+(ORW <t> g:(MOVWZload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (ORWload <t> [off] {sym} x ptr mem)
+(XOR <t> x g:(MOVDload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (XORload <t> [off] {sym} x ptr mem)
+(XOR <t> g:(MOVDload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (XORload <t> [off] {sym} x ptr mem)
+(XORW <t> x g:(MOVWload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (XORWload <t> [off] {sym} x ptr mem)
+(XORW <t> g:(MOVWload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (XORWload <t> [off] {sym} x ptr mem)
+(XORW <t> x g:(MOVWZload [off] {sym} ptr mem)) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (XORWload <t> [off] {sym} x ptr mem)
+(XORW <t> g:(MOVWZload [off] {sym} ptr mem) x) && g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	-> (XORWload <t> [off] {sym} x ptr mem)
+
+// Combine constant stores into larger (unaligned) stores.
+// It doesn't work to global data (based on SB),
+// because STGRL doesn't support unaligned address
+(MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {s} p mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
+  && clobber(x)
+  -> (MOVHstoreconst [makeValAndOff(ValAndOff(c).Val()&0xff | ValAndOff(a).Val()<<8, ValAndOff(a).Off())] {s} p mem)
+(MOVHstoreconst [c] {s} p x:(MOVHstoreconst [a] {s} p mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
+  && clobber(x)
+  -> (MOVWstoreconst [makeValAndOff(ValAndOff(c).Val()&0xffff | ValAndOff(a).Val()<<16, ValAndOff(a).Off())] {s} p mem)
+(MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
+  && clobber(x)
+  -> (MOVDstore [ValAndOff(a).Off()] {s} p (MOVDconst [ValAndOff(c).Val()&0xffffffff | ValAndOff(a).Val()<<32]) mem)
+
+// Combine stores into larger (unaligned) stores.
+// It doesn't work on global data (based on SB) because stores with relative addressing
+// require that the memory operand be aligned.
+(MOVBstore [i] {s} p w x:(MOVBstore [i-1] {s} p (SRDconst [8] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHstore [i-1] {s} p w mem)
+(MOVBstore [i] {s} p w0:(SRDconst [j] w) x:(MOVBstore [i-1] {s} p (SRDconst [j+8] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHstore [i-1] {s} p w0 mem)
+(MOVBstore [i] {s} p w x:(MOVBstore [i-1] {s} p (SRWconst [8] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHstore [i-1] {s} p w mem)
+(MOVBstore [i] {s} p w0:(SRWconst [j] w) x:(MOVBstore [i-1] {s} p (SRWconst [j+8] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHstore [i-1] {s} p w0 mem)
+(MOVHstore [i] {s} p w x:(MOVHstore [i-2] {s} p (SRDconst [16] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWstore [i-2] {s} p w mem)
+(MOVHstore [i] {s} p w0:(SRDconst [j] w) x:(MOVHstore [i-2] {s} p (SRDconst [j+16] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWstore [i-2] {s} p w0 mem)
+(MOVHstore [i] {s} p w x:(MOVHstore [i-2] {s} p (SRWconst [16] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWstore [i-2] {s} p w mem)
+(MOVHstore [i] {s} p w0:(SRWconst [j] w) x:(MOVHstore [i-2] {s} p (SRWconst [j+16] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWstore [i-2] {s} p w0 mem)
+(MOVWstore [i] {s} p (SRDconst [32] w) x:(MOVWstore [i-4] {s} p w mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVDstore [i-4] {s} p w mem)
+(MOVWstore [i] {s} p w0:(SRDconst [j] w) x:(MOVWstore [i-4] {s} p (SRDconst [j+32] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVDstore [i-4] {s} p w0 mem)
+
+(MOVBstoreidx [i] {s} p idx w x:(MOVBstoreidx [i-1] {s} p idx (SRDconst [8] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHstoreidx [i-1] {s} p idx w mem)
+(MOVBstoreidx [i] {s} p idx w0:(SRDconst [j] w) x:(MOVBstoreidx [i-1] {s} p idx (SRDconst [j+8] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHstoreidx [i-1] {s} p idx w0 mem)
+(MOVBstoreidx [i] {s} p idx w x:(MOVBstoreidx [i-1] {s} p idx (SRWconst [8] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHstoreidx [i-1] {s} p idx w mem)
+(MOVBstoreidx [i] {s} p idx w0:(SRWconst [j] w) x:(MOVBstoreidx [i-1] {s} p idx (SRWconst [j+8] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHstoreidx [i-1] {s} p idx w0 mem)
+(MOVHstoreidx [i] {s} p idx w x:(MOVHstoreidx [i-2] {s} p idx (SRDconst [16] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWstoreidx [i-2] {s} p idx w mem)
+(MOVHstoreidx [i] {s} p idx w0:(SRDconst [j] w) x:(MOVHstoreidx [i-2] {s} p idx (SRDconst [j+16] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWstoreidx [i-2] {s} p idx w0 mem)
+(MOVHstoreidx [i] {s} p idx w x:(MOVHstoreidx [i-2] {s} p idx (SRWconst [16] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWstoreidx [i-2] {s} p idx w mem)
+(MOVHstoreidx [i] {s} p idx w0:(SRWconst [j] w) x:(MOVHstoreidx [i-2] {s} p idx (SRWconst [j+16] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWstoreidx [i-2] {s} p idx w0 mem)
+(MOVWstoreidx [i] {s} p idx w x:(MOVWstoreidx [i-4] {s} p idx (SRDconst [32] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVDstoreidx [i-4] {s} p idx w mem)
+(MOVWstoreidx [i] {s} p idx w0:(SRDconst [j] w) x:(MOVWstoreidx [i-4] {s} p idx (SRDconst [j+32] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVDstoreidx [i-4] {s} p idx w0 mem)
+
+// Combine stores into larger (unaligned) stores with the bytes reversed (little endian).
+// Store-with-bytes-reversed instructions do not support relative memory addresses,
+// so these stores can't operate on global data (SB).
+(MOVBstore [i] {s} p (SRDconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHBRstore [i-1] {s} p w mem)
+(MOVBstore [i] {s} p (SRDconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SRDconst [j-8] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHBRstore [i-1] {s} p w0 mem)
+(MOVBstore [i] {s} p (SRWconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHBRstore [i-1] {s} p w mem)
+(MOVBstore [i] {s} p (SRWconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SRWconst [j-8] w) mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHBRstore [i-1] {s} p w0 mem)
+(MOVHBRstore [i] {s} p (SRDconst [16] w) x:(MOVHBRstore [i-2] {s} p w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWBRstore [i-2] {s} p w mem)
+(MOVHBRstore [i] {s} p (SRDconst [j] w) x:(MOVHBRstore [i-2] {s} p w0:(SRDconst [j-16] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWBRstore [i-2] {s} p w0 mem)
+(MOVHBRstore [i] {s} p (SRWconst [16] w) x:(MOVHBRstore [i-2] {s} p w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWBRstore [i-2] {s} p w mem)
+(MOVHBRstore [i] {s} p (SRWconst [j] w) x:(MOVHBRstore [i-2] {s} p w0:(SRWconst [j-16] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWBRstore [i-2] {s} p w0 mem)
+(MOVWBRstore [i] {s} p (SRDconst [32] w) x:(MOVWBRstore [i-4] {s} p w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVDBRstore [i-4] {s} p w mem)
+(MOVWBRstore [i] {s} p (SRDconst [j] w) x:(MOVWBRstore [i-4] {s} p w0:(SRDconst [j-32] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVDBRstore [i-4] {s} p w0 mem)
+
+(MOVBstoreidx [i] {s} p idx (SRDconst [8] w) x:(MOVBstoreidx [i-1] {s} p idx w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHBRstoreidx [i-1] {s} p idx w mem)
+(MOVBstoreidx [i] {s} p idx (SRDconst [j] w) x:(MOVBstoreidx [i-1] {s} p idx w0:(SRDconst [j-8] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHBRstoreidx [i-1] {s} p idx w0 mem)
+(MOVBstoreidx [i] {s} p idx (SRWconst [8] w) x:(MOVBstoreidx [i-1] {s} p idx w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHBRstoreidx [i-1] {s} p idx w mem)
+(MOVBstoreidx [i] {s} p idx (SRWconst [j] w) x:(MOVBstoreidx [i-1] {s} p idx w0:(SRWconst [j-8] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVHBRstoreidx [i-1] {s} p idx w0 mem)
+(MOVHBRstoreidx [i] {s} p idx (SRDconst [16] w) x:(MOVHBRstoreidx [i-2] {s} p idx w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWBRstoreidx [i-2] {s} p idx w mem)
+(MOVHBRstoreidx [i] {s} p idx (SRDconst [j] w) x:(MOVHBRstoreidx [i-2] {s} p idx w0:(SRDconst [j-16] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWBRstoreidx [i-2] {s} p idx w0 mem)
+(MOVHBRstoreidx [i] {s} p idx (SRWconst [16] w) x:(MOVHBRstoreidx [i-2] {s} p idx w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWBRstoreidx [i-2] {s} p idx w mem)
+(MOVHBRstoreidx [i] {s} p idx (SRWconst [j] w) x:(MOVHBRstoreidx [i-2] {s} p idx w0:(SRWconst [j-16] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVWBRstoreidx [i-2] {s} p idx w0 mem)
+(MOVWBRstoreidx [i] {s} p idx (SRDconst [32] w) x:(MOVWBRstoreidx [i-4] {s} p idx w mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVDBRstoreidx [i-4] {s} p idx w mem)
+(MOVWBRstoreidx [i] {s} p idx (SRDconst [j] w) x:(MOVWBRstoreidx [i-4] {s} p idx w0:(SRDconst [j-32] w) mem))
+  && x.Uses == 1
+  && clobber(x)
+  -> (MOVDBRstoreidx [i-4] {s} p idx w0 mem)
+
+// Combining byte loads into larger (unaligned) loads.
+
+// Little endian loads.
+
+// b[0] | b[1]<<8 -> load 16-bit, reverse bytes
+(ORW                 x0:(MOVBZload [i]   {s} p mem)
+    s0:(SLWconst [8] x1:(MOVBZload [i+1] {s} p mem)))
+  && p.Op != OpSB
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && s0.Uses == 1
+  && mergePoint(b,x0,x1) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(s0)
+  -> @mergePoint(b,x0,x1) (MOVHZreg (MOVHBRload [i] {s} p mem))
+
+// b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24 -> load 32-bit, reverse bytes
+(ORW o0:(ORW z0:(MOVHZreg x0:(MOVHBRload [i] {s} p mem))
+    s0:(SLWconst [16] x1:(MOVBZload [i+2] {s} p mem)))
+    s1:(SLWconst [24] x2:(MOVBZload [i+3] {s} p mem)))
+  && p.Op != OpSB
+  && z0.Uses == 1
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && o0.Uses == 1
+  && mergePoint(b,x0,x1,x2) != nil
+  && clobber(z0)
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(o0)
+  -> @mergePoint(b,x0,x1,x2) (MOVWBRload [i] {s} p mem)
+
+// b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24 | b[4]<<32 | b[5]<<40 | b[6]<<48 | b[7]<<56 -> load 64-bit, reverse bytes
+(OR o0:(OR o1:(OR o2:(OR o3:(OR o4:(OR o5:(OR
+                      x0:(MOVBZload [i]   {s} p mem)
+    s0:(SLDconst [8]  x1:(MOVBZload [i+1] {s} p mem)))
+    s1:(SLDconst [16] x2:(MOVBZload [i+2] {s} p mem)))
+    s2:(SLDconst [24] x3:(MOVBZload [i+3] {s} p mem)))
+    s3:(SLDconst [32] x4:(MOVBZload [i+4] {s} p mem)))
+    s4:(SLDconst [40] x5:(MOVBZload [i+5] {s} p mem)))
+    s5:(SLDconst [48] x6:(MOVBZload [i+6] {s} p mem)))
+    s6:(SLDconst [56] x7:(MOVBZload [i+7] {s} p mem)))
+  && p.Op != OpSB
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && x3.Uses == 1
+  && x4.Uses == 1
+  && x5.Uses == 1
+  && x6.Uses == 1
+  && x7.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && s2.Uses == 1
+  && s3.Uses == 1
+  && s4.Uses == 1
+  && s5.Uses == 1
+  && s6.Uses == 1
+  && o0.Uses == 1
+  && o1.Uses == 1
+  && o2.Uses == 1
+  && o3.Uses == 1
+  && o4.Uses == 1
+  && o5.Uses == 1
+  && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(x3)
+  && clobber(x4)
+  && clobber(x5)
+  && clobber(x6)
+  && clobber(x7)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(s2)
+  && clobber(s3)
+  && clobber(s4)
+  && clobber(s5)
+  && clobber(s6)
+  && clobber(o0)
+  && clobber(o1)
+  && clobber(o2)
+  && clobber(o3)
+  && clobber(o4)
+  && clobber(o5)
+  -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVDBRload [i] {s} p mem)
+
+// b[0] | b[1]<<8 -> load 16-bit, reverse bytes
+(ORW                 x0:(MOVBZloadidx [i]   {s} p idx mem)
+    s0:(SLWconst [8] x1:(MOVBZloadidx [i+1] {s} p idx mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && s0.Uses == 1
+  && mergePoint(b,x0,x1) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(s0)
+  -> @mergePoint(b,x0,x1) (MOVHZreg (MOVHBRloadidx <v.Type> [i] {s} p idx mem))
+
+// b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24 -> load 32-bit, reverse bytes
+(ORW o0:(ORW z0:(MOVHZreg x0:(MOVHBRloadidx [i] {s} p idx mem))
+    s0:(SLWconst [16] x1:(MOVBZloadidx [i+2] {s} p idx mem)))
+    s1:(SLWconst [24] x2:(MOVBZloadidx [i+3] {s} p idx mem)))
+  && z0.Uses == 1
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && o0.Uses == 1
+  && mergePoint(b,x0,x1,x2) != nil
+  && clobber(z0)
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(o0)
+  -> @mergePoint(b,x0,x1,x2) (MOVWZreg (MOVWBRloadidx <v.Type> [i] {s} p idx mem))
+
+// b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24 | b[4]<<32 | b[5]<<40 | b[6]<<48 | b[7]<<56 -> load 64-bit, reverse bytes
+(OR o0:(OR o1:(OR o2:(OR o3:(OR o4:(OR o5:(OR
+                      x0:(MOVBZloadidx [i]   {s} p idx mem)
+    s0:(SLDconst [8]  x1:(MOVBZloadidx [i+1] {s} p idx mem)))
+    s1:(SLDconst [16] x2:(MOVBZloadidx [i+2] {s} p idx mem)))
+    s2:(SLDconst [24] x3:(MOVBZloadidx [i+3] {s} p idx mem)))
+    s3:(SLDconst [32] x4:(MOVBZloadidx [i+4] {s} p idx mem)))
+    s4:(SLDconst [40] x5:(MOVBZloadidx [i+5] {s} p idx mem)))
+    s5:(SLDconst [48] x6:(MOVBZloadidx [i+6] {s} p idx mem)))
+    s6:(SLDconst [56] x7:(MOVBZloadidx [i+7] {s} p idx mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && x3.Uses == 1
+  && x4.Uses == 1
+  && x5.Uses == 1
+  && x6.Uses == 1
+  && x7.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && s2.Uses == 1
+  && s3.Uses == 1
+  && s4.Uses == 1
+  && s5.Uses == 1
+  && s6.Uses == 1
+  && o0.Uses == 1
+  && o1.Uses == 1
+  && o2.Uses == 1
+  && o3.Uses == 1
+  && o4.Uses == 1
+  && o5.Uses == 1
+  && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(x3)
+  && clobber(x4)
+  && clobber(x5)
+  && clobber(x6)
+  && clobber(x7)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(s2)
+  && clobber(s3)
+  && clobber(s4)
+  && clobber(s5)
+  && clobber(s6)
+  && clobber(o0)
+  && clobber(o1)
+  && clobber(o2)
+  && clobber(o3)
+  && clobber(o4)
+  && clobber(o5)
+  -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVDBRloadidx <v.Type> [i] {s} p idx mem)
+
+// Big endian loads.
+
+// b[1] | b[0]<<8 -> load 16-bit
+(ORW                  x0:(MOVBZload [i]   {s} p mem)
+    s0:(SLWconst [8] x1:(MOVBZload [i-1] {s} p mem)))
+  && p.Op != OpSB
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && s0.Uses == 1
+  && mergePoint(b,x0,x1) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(s0)
+  -> @mergePoint(b,x0,x1) (MOVHZload [i-1] {s} p mem)
+
+// b[3] | b[2]<<8 | b[1]<<16 | b[0]<<24 -> load 32-bit
+(ORW o0:(ORW x0:(MOVHZload [i] {s} p mem)
+    s0:(SLWconst [16] x1:(MOVBZload [i-1] {s} p mem)))
+    s1:(SLWconst [24] x2:(MOVBZload [i-2] {s} p mem)))
+  && p.Op != OpSB
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && o0.Uses == 1
+  && mergePoint(b,x0,x1,x2) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(o0)
+  -> @mergePoint(b,x0,x1,x2) (MOVWZload [i-2] {s} p mem)
+
+// b[7] | b[6]<<8 | b[5]<<16 | b[4]<<24 | b[3]<<32 | b[2]<<40 | b[1]<<48 | b[0]<<56 -> load 64-bit
+(OR o0:(OR o1:(OR o2:(OR o3:(OR o4:(OR o5:(OR
+                      x0:(MOVBZload [i]   {s} p mem)
+    s0:(SLDconst [8]  x1:(MOVBZload [i-1] {s} p mem)))
+    s1:(SLDconst [16] x2:(MOVBZload [i-2] {s} p mem)))
+    s2:(SLDconst [24] x3:(MOVBZload [i-3] {s} p mem)))
+    s3:(SLDconst [32] x4:(MOVBZload [i-4] {s} p mem)))
+    s4:(SLDconst [40] x5:(MOVBZload [i-5] {s} p mem)))
+    s5:(SLDconst [48] x6:(MOVBZload [i-6] {s} p mem)))
+    s6:(SLDconst [56] x7:(MOVBZload [i-7] {s} p mem)))
+  && p.Op != OpSB
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && x3.Uses == 1
+  && x4.Uses == 1
+  && x5.Uses == 1
+  && x6.Uses == 1
+  && x7.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && s2.Uses == 1
+  && s3.Uses == 1
+  && s4.Uses == 1
+  && s5.Uses == 1
+  && s6.Uses == 1
+  && o0.Uses == 1
+  && o1.Uses == 1
+  && o2.Uses == 1
+  && o3.Uses == 1
+  && o4.Uses == 1
+  && o5.Uses == 1
+  && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(x3)
+  && clobber(x4)
+  && clobber(x5)
+  && clobber(x6)
+  && clobber(x7)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(s2)
+  && clobber(s3)
+  && clobber(s4)
+  && clobber(s5)
+  && clobber(s6)
+  && clobber(o0)
+  && clobber(o1)
+  && clobber(o2)
+  && clobber(o3)
+  && clobber(o4)
+  && clobber(o5)
+  -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVDload [i-7] {s} p mem)
+
+// b[1] | b[0]<<8 -> load 16-bit
+(ORW                 x0:(MOVBZloadidx [i]   {s} p idx mem)
+    s0:(SLWconst [8] x1:(MOVBZloadidx [i-1] {s} p idx mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && s0.Uses == 1
+  && mergePoint(b,x0,x1) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(s0)
+  -> @mergePoint(b,x0,x1) (MOVHZloadidx <v.Type> [i-1] {s} p idx mem)
+
+// b[3] | b[2]<<8 | b[1]<<16 | b[0]<<24 -> load 32-bit
+(ORW o0:(ORW x0:(MOVHZloadidx [i] {s} p idx mem)
+    s0:(SLWconst [16] x1:(MOVBZloadidx [i-1] {s} p idx mem)))
+    s1:(SLWconst [24] x2:(MOVBZloadidx [i-2] {s} p idx mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && o0.Uses == 1
+  && mergePoint(b,x0,x1,x2) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(o0)
+  -> @mergePoint(b,x0,x1,x2) (MOVWZloadidx <v.Type> [i-2] {s} p idx mem)
+
+// b[7] | b[6]<<8 | b[5]<<16 | b[4]<<24 | b[3]<<32 | b[2]<<40 | b[1]<<48 | b[0]<<56 -> load 64-bit
+(OR o0:(OR o1:(OR o2:(OR o3:(OR o4:(OR o5:(OR
+                      x0:(MOVBZloadidx [i]   {s} p idx mem)
+    s0:(SLDconst [8]  x1:(MOVBZloadidx [i-1] {s} p idx mem)))
+    s1:(SLDconst [16] x2:(MOVBZloadidx [i-2] {s} p idx mem)))
+    s2:(SLDconst [24] x3:(MOVBZloadidx [i-3] {s} p idx mem)))
+    s3:(SLDconst [32] x4:(MOVBZloadidx [i-4] {s} p idx mem)))
+    s4:(SLDconst [40] x5:(MOVBZloadidx [i-5] {s} p idx mem)))
+    s5:(SLDconst [48] x6:(MOVBZloadidx [i-6] {s} p idx mem)))
+    s6:(SLDconst [56] x7:(MOVBZloadidx [i-7] {s} p idx mem)))
+  && x0.Uses == 1
+  && x1.Uses == 1
+  && x2.Uses == 1
+  && x3.Uses == 1
+  && x4.Uses == 1
+  && x5.Uses == 1
+  && x6.Uses == 1
+  && x7.Uses == 1
+  && s0.Uses == 1
+  && s1.Uses == 1
+  && s2.Uses == 1
+  && s3.Uses == 1
+  && s4.Uses == 1
+  && s5.Uses == 1
+  && s6.Uses == 1
+  && o0.Uses == 1
+  && o1.Uses == 1
+  && o2.Uses == 1
+  && o3.Uses == 1
+  && o4.Uses == 1
+  && o5.Uses == 1
+  && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil
+  && clobber(x0)
+  && clobber(x1)
+  && clobber(x2)
+  && clobber(x3)
+  && clobber(x4)
+  && clobber(x5)
+  && clobber(x6)
+  && clobber(x7)
+  && clobber(s0)
+  && clobber(s1)
+  && clobber(s2)
+  && clobber(s3)
+  && clobber(s4)
+  && clobber(s5)
+  && clobber(s6)
+  && clobber(o0)
+  && clobber(o1)
+  && clobber(o2)
+  && clobber(o3)
+  && clobber(o4)
+  && clobber(o5)
+  -> @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVDloadidx <v.Type> [i-7] {s} p idx mem)
+
+// Combine stores into store multiples.
+// 32-bit
+(MOVWstore [i] {s} p w1 x:(MOVWstore [i-4] {s} p w0 mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && is20Bit(i-4)
+  && clobber(x)
+  -> (STM2 [i-4] {s} p w0 w1 mem)
+(MOVWstore [i] {s} p w2 x:(STM2 [i-8] {s} p w0 w1 mem))
+  && x.Uses == 1
+  && is20Bit(i-8)
+  && clobber(x)
+  -> (STM3 [i-8] {s} p w0 w1 w2 mem)
+(MOVWstore [i] {s} p w3 x:(STM3 [i-12] {s} p w0 w1 w2 mem))
+  && x.Uses == 1
+  && is20Bit(i-12)
+  && clobber(x)
+  -> (STM4 [i-12] {s} p w0 w1 w2 w3 mem)
+(STM2 [i] {s} p w2 w3 x:(STM2 [i-8] {s} p w0 w1 mem))
+  && x.Uses == 1
+  && is20Bit(i-8)
+  && clobber(x)
+  -> (STM4 [i-8] {s} p w0 w1 w2 w3 mem)
+// 64-bit
+(MOVDstore [i] {s} p w1 x:(MOVDstore [i-8] {s} p w0 mem))
+  && p.Op != OpSB
+  && x.Uses == 1
+  && is20Bit(i-8)
+  && clobber(x)
+  -> (STMG2 [i-8] {s} p w0 w1 mem)
+(MOVDstore [i] {s} p w2 x:(STMG2 [i-16] {s} p w0 w1 mem))
+  && x.Uses == 1
+  && is20Bit(i-16)
+  && clobber(x)
+  -> (STMG3 [i-16] {s} p w0 w1 w2 mem)
+(MOVDstore [i] {s} p w3 x:(STMG3 [i-24] {s} p w0 w1 w2 mem))
+  && x.Uses == 1
+  && is20Bit(i-24)
+  && clobber(x)
+  -> (STMG4 [i-24] {s} p w0 w1 w2 w3 mem)
+(STMG2 [i] {s} p w2 w3 x:(STMG2 [i-16] {s} p w0 w1 mem))
+  && x.Uses == 1
+  && is20Bit(i-16)
+  && clobber(x)
+  -> (STMG4 [i-16] {s} p w0 w1 w2 w3 mem)
+
+// Convert 32-bit store multiples into 64-bit stores.
+(STM2 [i] {s} p (SRDconst [32] x) x mem) -> (MOVDstore [i] {s} p x mem)
diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go
new file mode 100644
index 0000000..7a25c26
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go
@@ -0,0 +1,623 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+import "strings"
+
+// Notes:
+//  - Integer types live in the low portion of registers. Upper portions are junk.
+//  - Boolean types use the low-order byte of a register. 0=false, 1=true.
+//    Upper bytes are junk.
+//  - When doing sub-register operations, we try to write the whole
+//    destination register to avoid a partial-register write.
+//  - Unused portions of AuxInt (or the Val portion of ValAndOff) are
+//    filled by sign-extending the used portion.  Users of AuxInt which interpret
+//    AuxInt as unsigned (e.g. shifts) must be careful.
+
+// Suffixes encode the bit width of various instructions.
+// D (double word) = 64 bit (frequently omitted)
+// W (word)        = 32 bit
+// H (half word)   = 16 bit
+// B (byte)        = 8 bit
+
+// copied from ../../s390x/reg.go
+var regNamesS390X = []string{
+	"R0",
+	"R1",
+	"R2",
+	"R3",
+	"R4",
+	"R5",
+	"R6",
+	"R7",
+	"R8",
+	"R9",
+	"R10",
+	"R11",
+	"R12",
+	"g", // R13
+	"R14",
+	"SP", // R15
+	"F0",
+	"F1",
+	"F2",
+	"F3",
+	"F4",
+	"F5",
+	"F6",
+	"F7",
+	"F8",
+	"F9",
+	"F10",
+	"F11",
+	"F12",
+	"F13",
+	"F14",
+	"F15",
+
+	//pseudo-registers
+	"SB",
+}
+
+func init() {
+	// Make map from reg names to reg integers.
+	if len(regNamesS390X) > 64 {
+		panic("too many registers")
+	}
+	num := map[string]int{}
+	for i, name := range regNamesS390X {
+		num[name] = i
+	}
+	buildReg := func(s string) regMask {
+		m := regMask(0)
+		for _, r := range strings.Split(s, " ") {
+			if n, ok := num[r]; ok {
+				m |= regMask(1) << uint(n)
+				continue
+			}
+			panic("register " + r + " not found")
+		}
+		return m
+	}
+
+	// Common individual register masks
+	var (
+		sp = buildReg("SP")
+		sb = buildReg("SB")
+		r0 = buildReg("R0")
+
+		// R10 and R11 are reserved by the assembler.
+		gp   = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14")
+		gpsp = gp | sp
+
+		// R0 is considered to contain the value 0 in address calculations.
+		ptr     = gp &^ r0
+		ptrsp   = ptr | sp
+		ptrspsb = ptrsp | sb
+
+		fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15")
+		callerSave = gp | fp
+	)
+	// Common slices of register masks
+	var (
+		gponly = []regMask{gp}
+		fponly = []regMask{fp}
+	)
+
+	// Common regInfo
+	var (
+		gp01   = regInfo{inputs: []regMask{}, outputs: gponly}
+		gp11   = regInfo{inputs: []regMask{gp}, outputs: gponly}
+		gp11sp = regInfo{inputs: []regMask{gpsp}, outputs: gponly}
+		gp21   = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
+		gp21sp = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly}
+
+		// R0 evaluates to 0 when used as the number of bits to shift
+		// so we need to exclude it from that operand.
+		sh21 = regInfo{inputs: []regMask{gp, ptr}, outputs: gponly}
+
+		addr    = regInfo{inputs: []regMask{sp | sb}, outputs: gponly}
+		addridx = regInfo{inputs: []regMask{sp | sb, ptrsp}, outputs: gponly}
+
+		gp2flags  = regInfo{inputs: []regMask{gpsp, gpsp}}
+		gp1flags  = regInfo{inputs: []regMask{gpsp}}
+		flagsgp   = regInfo{outputs: gponly}
+		gp2flags1 = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
+
+		gpload       = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: gponly}
+		gploadidx    = regInfo{inputs: []regMask{ptrspsb, ptrsp, 0}, outputs: gponly}
+		gpopload     = regInfo{inputs: []regMask{gp, ptrsp, 0}, outputs: gponly}
+		gpstore      = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}}
+		gpstoreconst = regInfo{inputs: []regMask{ptrspsb, 0}}
+		gpstoreidx   = regInfo{inputs: []regMask{ptrsp, ptrsp, gpsp, 0}}
+		gpstorebr    = regInfo{inputs: []regMask{ptrsp, gpsp, 0}}
+		gpstorelaa   = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}, outputs: gponly}
+
+		gpmvc = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}}
+
+		fp01        = regInfo{inputs: []regMask{}, outputs: fponly}
+		fp21        = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
+		fp21clobber = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
+		fpgp        = regInfo{inputs: fponly, outputs: gponly}
+		gpfp        = regInfo{inputs: gponly, outputs: fponly}
+		fp11        = regInfo{inputs: fponly, outputs: fponly}
+		fp11clobber = regInfo{inputs: fponly, outputs: fponly}
+		fp2flags    = regInfo{inputs: []regMask{fp, fp}}
+
+		fpload    = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: fponly}
+		fploadidx = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}, outputs: fponly}
+
+		fpstore    = regInfo{inputs: []regMask{ptrspsb, fp, 0}}
+		fpstoreidx = regInfo{inputs: []regMask{ptrsp, ptrsp, fp, 0}}
+
+		// LoweredAtomicCas may overwrite arg1, so force it to R0 for now.
+		cas = regInfo{inputs: []regMask{ptrsp, r0, gpsp, 0}, outputs: []regMask{gp, 0}, clobbers: r0}
+
+		// LoweredAtomicExchange overwrites the output before executing
+		// CS{,G}, so the output register must not be the same as the
+		// input register. For now we just force the output register to
+		// R0.
+		exchange = regInfo{inputs: []regMask{ptrsp, gpsp &^ r0, 0}, outputs: []regMask{r0, 0}}
+	)
+
+	var S390Xops = []opData{
+		// fp ops
+		{name: "FADDS", argLength: 2, reg: fp21clobber, asm: "FADDS", commutative: true, resultInArg0: true, clobberFlags: true}, // fp32 add
+		{name: "FADD", argLength: 2, reg: fp21clobber, asm: "FADD", commutative: true, resultInArg0: true, clobberFlags: true},   // fp64 add
+		{name: "FSUBS", argLength: 2, reg: fp21clobber, asm: "FSUBS", resultInArg0: true, clobberFlags: true},                    // fp32 sub
+		{name: "FSUB", argLength: 2, reg: fp21clobber, asm: "FSUB", resultInArg0: true, clobberFlags: true},                      // fp64 sub
+		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true, resultInArg0: true},                            // fp32 mul
+		{name: "FMUL", argLength: 2, reg: fp21, asm: "FMUL", commutative: true, resultInArg0: true},                              // fp64 mul
+		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS", resultInArg0: true},                                               // fp32 div
+		{name: "FDIV", argLength: 2, reg: fp21, asm: "FDIV", resultInArg0: true},                                                 // fp64 div
+		{name: "FNEGS", argLength: 1, reg: fp11clobber, asm: "FNEGS", clobberFlags: true},                                        // fp32 neg
+		{name: "FNEG", argLength: 1, reg: fp11clobber, asm: "FNEG", clobberFlags: true},                                          // fp64 neg
+
+		{name: "FMOVSload", argLength: 2, reg: fpload, asm: "FMOVS", aux: "SymOff", faultOnNilArg0: true}, // fp32 load
+		{name: "FMOVDload", argLength: 2, reg: fpload, asm: "FMOVD", aux: "SymOff", faultOnNilArg0: true}, // fp64 load
+		{name: "FMOVSconst", reg: fp01, asm: "FMOVS", aux: "Float32", rematerializeable: true},            // fp32 constant
+		{name: "FMOVDconst", reg: fp01, asm: "FMOVD", aux: "Float64", rematerializeable: true},            // fp64 constant
+		{name: "FMOVSloadidx", argLength: 3, reg: fploadidx, asm: "FMOVS", aux: "SymOff"},                 // fp32 load indexed by i
+		{name: "FMOVDloadidx", argLength: 3, reg: fploadidx, asm: "FMOVD", aux: "SymOff"},                 // fp64 load indexed by i
+
+		{name: "FMOVSstore", argLength: 3, reg: fpstore, asm: "FMOVS", aux: "SymOff", faultOnNilArg0: true}, // fp32 store
+		{name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "FMOVD", aux: "SymOff", faultOnNilArg0: true}, // fp64 store
+		{name: "FMOVSstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVS", aux: "SymOff"},                 // fp32 indexed by i store
+		{name: "FMOVDstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVD", aux: "SymOff"},                 // fp64 indexed by i store
+
+		// binary ops
+		{name: "ADD", argLength: 2, reg: gp21sp, asm: "ADD", commutative: true, clobberFlags: true},                                               // arg0 + arg1
+		{name: "ADDW", argLength: 2, reg: gp21sp, asm: "ADDW", commutative: true, clobberFlags: true},                                             // arg0 + arg1
+		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int64", typ: "UInt64", clobberFlags: true},                                // arg0 + auxint
+		{name: "ADDWconst", argLength: 1, reg: gp11sp, asm: "ADDW", aux: "Int32", clobberFlags: true},                                             // arg0 + auxint
+		{name: "ADDload", argLength: 3, reg: gpopload, asm: "ADD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},   // arg0 + *arg1. arg2=mem
+		{name: "ADDWload", argLength: 3, reg: gpopload, asm: "ADDW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 + *arg1. arg2=mem
+
+		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB", clobberFlags: true},                                                                    // arg0 - arg1
+		{name: "SUBW", argLength: 2, reg: gp21, asm: "SUBW", clobberFlags: true},                                                                  // arg0 - arg1
+		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int64", resultInArg0: true, clobberFlags: true},                             // arg0 - auxint
+		{name: "SUBWconst", argLength: 1, reg: gp11, asm: "SUBW", aux: "Int32", resultInArg0: true, clobberFlags: true},                           // arg0 - auxint
+		{name: "SUBload", argLength: 3, reg: gpopload, asm: "SUB", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},   // arg0 - *arg1. arg2=mem
+		{name: "SUBWload", argLength: 3, reg: gpopload, asm: "SUBW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 - *arg1. arg2=mem
+
+		{name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true},             // arg0 * arg1
+		{name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true, resultInArg0: true, clobberFlags: true},             // arg0 * arg1
+		{name: "MULLDconst", argLength: 1, reg: gp11, asm: "MULLD", aux: "Int64", typ: "Int64", resultInArg0: true, clobberFlags: true},             // arg0 * auxint
+		{name: "MULLWconst", argLength: 1, reg: gp11, asm: "MULLW", aux: "Int32", typ: "Int32", resultInArg0: true, clobberFlags: true},             // arg0 * auxint
+		{name: "MULLDload", argLength: 3, reg: gpopload, asm: "MULLD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 * *arg1. arg2=mem
+		{name: "MULLWload", argLength: 3, reg: gpopload, asm: "MULLW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 * *arg1. arg2=mem
+
+		{name: "MULHD", argLength: 2, reg: gp21, asm: "MULHD", typ: "Int64", resultInArg0: true, clobberFlags: true},   // (arg0 * arg1) >> width
+		{name: "MULHDU", argLength: 2, reg: gp21, asm: "MULHDU", typ: "Int64", resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width
+
+		{name: "DIVD", argLength: 2, reg: gp21, asm: "DIVD", resultInArg0: true, clobberFlags: true},   // arg0 / arg1
+		{name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", resultInArg0: true, clobberFlags: true},   // arg0 / arg1
+		{name: "DIVDU", argLength: 2, reg: gp21, asm: "DIVDU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
+		{name: "DIVWU", argLength: 2, reg: gp21, asm: "DIVWU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
+
+		{name: "MODD", argLength: 2, reg: gp21, asm: "MODD", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
+		{name: "MODW", argLength: 2, reg: gp21, asm: "MODW", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
+
+		{name: "MODDU", argLength: 2, reg: gp21, asm: "MODDU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
+		{name: "MODWU", argLength: 2, reg: gp21, asm: "MODWU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
+
+		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true, clobberFlags: true},                                                 // arg0 & arg1
+		{name: "ANDW", argLength: 2, reg: gp21, asm: "ANDW", commutative: true, clobberFlags: true},                                               // arg0 & arg1
+		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64", resultInArg0: true, clobberFlags: true},                             // arg0 & auxint
+		{name: "ANDWconst", argLength: 1, reg: gp11, asm: "ANDW", aux: "Int32", resultInArg0: true, clobberFlags: true},                           // arg0 & auxint
+		{name: "ANDload", argLength: 3, reg: gpopload, asm: "AND", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},   // arg0 & *arg1. arg2=mem
+		{name: "ANDWload", argLength: 3, reg: gpopload, asm: "ANDW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 & *arg1. arg2=mem
+
+		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true, clobberFlags: true},                                                 // arg0 | arg1
+		{name: "ORW", argLength: 2, reg: gp21, asm: "ORW", commutative: true, clobberFlags: true},                                               // arg0 | arg1
+		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64", resultInArg0: true, clobberFlags: true},                             // arg0 | auxint
+		{name: "ORWconst", argLength: 1, reg: gp11, asm: "ORW", aux: "Int32", resultInArg0: true, clobberFlags: true},                           // arg0 | auxint
+		{name: "ORload", argLength: 3, reg: gpopload, asm: "OR", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},   // arg0 | *arg1. arg2=mem
+		{name: "ORWload", argLength: 3, reg: gpopload, asm: "ORW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 | *arg1. arg2=mem
+
+		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, clobberFlags: true},                                                 // arg0 ^ arg1
+		{name: "XORW", argLength: 2, reg: gp21, asm: "XORW", commutative: true, clobberFlags: true},                                               // arg0 ^ arg1
+		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", resultInArg0: true, clobberFlags: true},                             // arg0 ^ auxint
+		{name: "XORWconst", argLength: 1, reg: gp11, asm: "XORW", aux: "Int32", resultInArg0: true, clobberFlags: true},                           // arg0 ^ auxint
+		{name: "XORload", argLength: 3, reg: gpopload, asm: "XOR", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true},   // arg0 ^ *arg1. arg2=mem
+		{name: "XORWload", argLength: 3, reg: gpopload, asm: "XORW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true}, // arg0 ^ *arg1. arg2=mem
+
+		{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},   // arg0 compare to arg1
+		{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"}, // arg0 compare to arg1
+
+		{name: "CMPU", argLength: 2, reg: gp2flags, asm: "CMPU", typ: "Flags"},   // arg0 compare to arg1
+		{name: "CMPWU", argLength: 2, reg: gp2flags, asm: "CMPWU", typ: "Flags"}, // arg0 compare to arg1
+
+		{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", typ: "Flags", aux: "Int64"},     // arg0 compare to auxint
+		{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", typ: "Flags", aux: "Int32"},   // arg0 compare to auxint
+		{name: "CMPUconst", argLength: 1, reg: gp1flags, asm: "CMPU", typ: "Flags", aux: "Int64"},   // arg0 compare to auxint
+		{name: "CMPWUconst", argLength: 1, reg: gp1flags, asm: "CMPWU", typ: "Flags", aux: "Int32"}, // arg0 compare to auxint
+
+		{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "CEBR", typ: "Flags"}, // arg0 compare to arg1, f32
+		{name: "FCMP", argLength: 2, reg: fp2flags, asm: "FCMPU", typ: "Flags"}, // arg0 compare to arg1, f64
+
+		{name: "SLD", argLength: 2, reg: sh21, asm: "SLD"},                    // arg0 << arg1, shift amount is mod 64
+		{name: "SLW", argLength: 2, reg: sh21, asm: "SLW"},                    // arg0 << arg1, shift amount is mod 32
+		{name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int64"}, // arg0 << auxint, shift amount 0-63
+		{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int32"}, // arg0 << auxint, shift amount 0-31
+
+		{name: "SRD", argLength: 2, reg: sh21, asm: "SRD"},                    // unsigned arg0 >> arg1, shift amount is mod 64
+		{name: "SRW", argLength: 2, reg: sh21, asm: "SRW"},                    // unsigned arg0 >> arg1, shift amount is mod 32
+		{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int64"}, // unsigned arg0 >> auxint, shift amount 0-63
+		{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int32"}, // unsigned arg0 >> auxint, shift amount 0-31
+
+		// Arithmetic shifts clobber flags.
+		{name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true},                    // signed arg0 >> arg1, shift amount is mod 64
+		{name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true},                    // signed arg0 >> arg1, shift amount is mod 32
+		{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int64", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
+		{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int32", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-31
+
+		{name: "RLLGconst", argLength: 1, reg: gp11, asm: "RLLG", aux: "Int64"}, // arg0 rotate left auxint, rotate amount 0-63
+		{name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "Int32"},   // arg0 rotate left auxint, rotate amount 0-31
+
+		// unary ops
+		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG", clobberFlags: true},   // -arg0
+		{name: "NEGW", argLength: 1, reg: gp11, asm: "NEGW", clobberFlags: true}, // -arg0
+
+		{name: "NOT", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true},  // ^arg0
+		{name: "NOTW", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true}, // ^arg0
+
+		{name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"}, // sqrt(arg0)
+
+		{name: "SUBEcarrymask", argLength: 1, reg: flagsgp, asm: "SUBE"},  // (int64)(-1) if carry is set, 0 if carry is clear.
+		{name: "SUBEWcarrymask", argLength: 1, reg: flagsgp, asm: "SUBE"}, // (int32)(-1) if carry is set, 0 if carry is clear.
+		// Note: 32-bits subtraction is not implemented in S390X. Temporarily use SUBE (64-bits).
+
+		{name: "MOVDEQ", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDEQ"}, // extract == condition from arg0
+		{name: "MOVDNE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDNE"}, // extract != condition from arg0
+		{name: "MOVDLT", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDLT"}, // extract signed < condition from arg0
+		{name: "MOVDLE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDLE"}, // extract signed <= condition from arg0
+		{name: "MOVDGT", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGT"}, // extract signed > condition from arg0
+		{name: "MOVDGE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGE"}, // extract signed >= condition from arg0
+
+		// Different rules for floating point conditions because
+		// any comparison involving a NaN is always false and thus
+		// the patterns for inverting conditions cannot be used.
+		{name: "MOVDGTnoinv", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGT"}, // extract floating > condition from arg0
+		{name: "MOVDGEnoinv", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGE"}, // extract floating >= condition from arg0
+
+		{name: "MOVBreg", argLength: 1, reg: gp11sp, asm: "MOVB", typ: "Int64"},    // sign extend arg0 from int8 to int64
+		{name: "MOVBZreg", argLength: 1, reg: gp11sp, asm: "MOVBZ", typ: "UInt64"}, // zero extend arg0 from int8 to int64
+		{name: "MOVHreg", argLength: 1, reg: gp11sp, asm: "MOVH", typ: "Int64"},    // sign extend arg0 from int16 to int64
+		{name: "MOVHZreg", argLength: 1, reg: gp11sp, asm: "MOVHZ", typ: "UInt64"}, // zero extend arg0 from int16 to int64
+		{name: "MOVWreg", argLength: 1, reg: gp11sp, asm: "MOVW", typ: "Int64"},    // sign extend arg0 from int32 to int64
+		{name: "MOVWZreg", argLength: 1, reg: gp11sp, asm: "MOVWZ", typ: "UInt64"}, // zero extend arg0 from int32 to int64
+
+		{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
+
+		{name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA"}, // convert float64 to int32
+		{name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA"}, // convert float64 to int64
+		{name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA"}, // convert float32 to int32
+		{name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA"}, // convert float32 to int64
+		{name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA"}, // convert int32 to float32
+		{name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA"}, // convert int32 to float64
+		{name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA"}, // convert int64 to float32
+		{name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA"}, // convert int64 to float64
+		{name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"},   // convert float64 to float32
+		{name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"},   // convert float32 to float64
+
+		{name: "MOVDaddr", argLength: 1, reg: addr, aux: "SymOff", rematerializeable: true, clobberFlags: true}, // arg0 + auxint + offset encoded in aux
+		{name: "MOVDaddridx", argLength: 2, reg: addridx, aux: "SymOff", clobberFlags: true},                    // arg0 + arg1 + auxint + aux
+
+		// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
+		{name: "MOVBZload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", typ: "UInt8", clobberFlags: true, faultOnNilArg0: true},  // load byte from arg0+auxint+aux. arg1=mem.  Zero extend.
+		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},                  // ditto, sign extend to int64
+		{name: "MOVHZload", argLength: 2, reg: gpload, asm: "MOVHZ", aux: "SymOff", typ: "UInt16", clobberFlags: true, faultOnNilArg0: true}, // load 2 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
+		{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},                  // ditto, sign extend to int64
+		{name: "MOVWZload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", typ: "UInt32", clobberFlags: true, faultOnNilArg0: true}, // load 4 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
+		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},                  // ditto, sign extend to int64
+		{name: "MOVDload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", typ: "UInt64", clobberFlags: true, faultOnNilArg0: true},   // load 8 bytes from arg0+auxint+aux. arg1=mem
+
+		{name: "MOVWBR", argLength: 1, reg: gp11, asm: "MOVWBR"}, // arg0 swap bytes
+		{name: "MOVDBR", argLength: 1, reg: gp11, asm: "MOVDBR"}, // arg0 swap bytes
+
+		{name: "MOVHBRload", argLength: 2, reg: gpload, asm: "MOVHBR", aux: "SymOff", typ: "UInt16", clobberFlags: true, faultOnNilArg0: true}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
+		{name: "MOVWBRload", argLength: 2, reg: gpload, asm: "MOVWBR", aux: "SymOff", typ: "UInt32", clobberFlags: true, faultOnNilArg0: true}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
+		{name: "MOVDBRload", argLength: 2, reg: gpload, asm: "MOVDBR", aux: "SymOff", typ: "UInt64", clobberFlags: true, faultOnNilArg0: true}, // load 8 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
+
+		{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},       // store byte in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVHstore", argLength: 3, reg: gpstore, asm: "MOVH", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},       // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},       // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVDstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},       // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
+		{name: "MOVHBRstore", argLength: 3, reg: gpstorebr, asm: "MOVHBR", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
+		{name: "MOVWBRstore", argLength: 3, reg: gpstorebr, asm: "MOVWBR", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
+		{name: "MOVDBRstore", argLength: 3, reg: gpstorebr, asm: "MOVDBR", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
+
+		{name: "MVC", argLength: 3, reg: gpmvc, asm: "MVC", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, faultOnNilArg1: true}, // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size,off
+
+		// indexed loads/stores
+		// TODO(mundaym): add sign-extended indexed loads
+		{name: "MOVBZloadidx", argLength: 3, reg: gploadidx, asm: "MOVBZ", aux: "SymOff", clobberFlags: true},     // load a byte from arg0+arg1+auxint+aux. arg2=mem
+		{name: "MOVHZloadidx", argLength: 3, reg: gploadidx, asm: "MOVHZ", aux: "SymOff", clobberFlags: true},     // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem
+		{name: "MOVWZloadidx", argLength: 3, reg: gploadidx, asm: "MOVWZ", aux: "SymOff", clobberFlags: true},     // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem
+		{name: "MOVDloadidx", argLength: 3, reg: gploadidx, asm: "MOVD", aux: "SymOff", clobberFlags: true},       // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem
+		{name: "MOVHBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVHBR", aux: "SymOff", clobberFlags: true},   // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
+		{name: "MOVWBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVWBR", aux: "SymOff", clobberFlags: true},   // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
+		{name: "MOVDBRloadidx", argLength: 3, reg: gploadidx, asm: "MOVDBR", aux: "SymOff", clobberFlags: true},   // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
+		{name: "MOVBstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVB", aux: "SymOff", clobberFlags: true},     // store byte in arg2 to arg0+arg1+auxint+aux. arg3=mem
+		{name: "MOVHstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVH", aux: "SymOff", clobberFlags: true},     // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
+		{name: "MOVWstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVW", aux: "SymOff", clobberFlags: true},     // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
+		{name: "MOVDstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVD", aux: "SymOff", clobberFlags: true},     // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
+		{name: "MOVHBRstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVHBR", aux: "SymOff", clobberFlags: true}, // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
+		{name: "MOVWBRstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVWBR", aux: "SymOff", clobberFlags: true}, // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
+		{name: "MOVDBRstoreidx", argLength: 4, reg: gpstoreidx, asm: "MOVDBR", aux: "SymOff", clobberFlags: true}, // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
+
+		// For storeconst ops, the AuxInt field encodes both
+		// the value to store and an address offset of the store.
+		// Cast AuxInt to a ValAndOff to extract Val and Off fields.
+		{name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux.  arg1=mem
+		{name: "MOVHstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVH", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store low 2 bytes of ...
+		{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store low 4 bytes of ...
+		{name: "MOVDstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVD", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true}, // store 8 bytes of ...
+
+		{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
+
+		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "SymOff", clobberFlags: true, call: true},                                               // call static function aux.(*gc.Sym).  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
+		{name: "CALLdefer", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                 // call deferproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLgo", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                                                    // call newproc.  arg0=mem, auxint=argsize, returns mem
+		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
+
+		// (InvertFlags (CMP a b)) == (CMP b a)
+		// InvertFlags is a pseudo-op which can't appear in assembly output.
+		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
+
+		// Pseudo-ops
+		{name: "LoweredGetG", argLength: 1, reg: gp01}, // arg0=mem
+		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
+		// and sorts it to the very beginning of the block to prevent other
+		// use of R12 (the closure pointer)
+		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R12")}}},
+		// arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
+		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{ptrsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
+
+		// MOVDconvert converts between pointers and integers.
+		// We have a special op for this so as to not confuse GC
+		// (particularly stack maps). It takes a memory arg so it
+		// gets correctly ordered with respect to GC safepoints.
+		// arg0=ptr/int arg1=mem, output=int/ptr
+		{name: "MOVDconvert", argLength: 2, reg: gp11sp, asm: "MOVD"},
+
+		// Constant flag values. For any comparison, there are 5 possible
+		// outcomes: the three from the signed total order (<,==,>) and the
+		// three from the unsigned total order. The == cases overlap.
+		// Note: there's a sixth "unordered" outcome for floating-point
+		// comparisons, but we don't use such a beast yet.
+		// These ops are for temporary use by rewrite rules. They
+		// cannot appear in the generated assembly.
+		{name: "FlagEQ"}, // equal
+		{name: "FlagLT"}, // <
+		{name: "FlagGT"}, // >
+
+		// Atomic loads. These are just normal loads but return <value,memory> tuples
+		// so they can be properly ordered with other loads.
+		// load from arg0+auxint+aux.  arg1=mem.
+		{name: "MOVWZatomicload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", faultOnNilArg0: true},
+		{name: "MOVDatomicload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", faultOnNilArg0: true},
+
+		// Atomic stores. These are just normal stores.
+		// store arg1 to arg0+auxint+aux. arg2=mem.
+		{name: "MOVWatomicstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
+		{name: "MOVDatomicstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true},
+
+		// Atomic adds.
+		// *(arg0+auxint+aux) += arg1.  arg2=mem.
+		// Returns a tuple of <old contents of *(arg0+auxint+aux), memory>.
+		{name: "LAA", argLength: 3, reg: gpstorelaa, asm: "LAA", typ: "(UInt32,Mem)", aux: "SymOff", faultOnNilArg0: true},
+		{name: "LAAG", argLength: 3, reg: gpstorelaa, asm: "LAAG", typ: "(UInt64,Mem)", aux: "SymOff", faultOnNilArg0: true},
+		{name: "AddTupleFirst32", argLength: 2}, // arg0=tuple <x,y>.  Returns <x+arg1,y>.
+		{name: "AddTupleFirst64", argLength: 2}, // arg0=tuple <x,y>.  Returns <x+arg1,y>.
+
+		// Compare and swap.
+		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
+		// if *(arg0+auxint+aux) == arg1 {
+		//   *(arg0+auxint+aux) = arg2
+		//   return (true, memory)
+		// } else {
+		//   return (false, memory)
+		// }
+		// Note that these instructions also return the old value in arg1, but we ignore it.
+		// TODO: have these return flags instead of bool.  The current system generates:
+		//    CS ...
+		//    MOVD  $0, ret
+		//    BNE   2(PC)
+		//    MOVD  $1, ret
+		//    CMPW  ret, $0
+		//    BNE ...
+		// instead of just
+		//    CS ...
+		//    BEQ ...
+		// but we can't do that because memory-using ops can't generate flags yet
+		// (flagalloc wants to move flag-generating instructions around).
+		{name: "LoweredAtomicCas32", argLength: 4, reg: cas, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
+		{name: "LoweredAtomicCas64", argLength: 4, reg: cas, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
+
+		// Lowered atomic swaps, emulated using compare-and-swap.
+		// store arg1 to arg0+auxint+aux, arg2=mem.
+		{name: "LoweredAtomicExchange32", argLength: 3, reg: exchange, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
+		{name: "LoweredAtomicExchange64", argLength: 3, reg: exchange, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true},
+
+		// find leftmost one
+		{
+			name:         "FLOGR",
+			argLength:    1,
+			reg:          regInfo{inputs: gponly, outputs: []regMask{buildReg("R0")}, clobbers: buildReg("R1")},
+			asm:          "FLOGR",
+			typ:          "UInt64",
+			clobberFlags: true,
+		},
+
+		// store multiple
+		{
+			name:           "STMG2",
+			argLength:      4,
+			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
+			aux:            "SymOff",
+			typ:            "Mem",
+			asm:            "STMG",
+			faultOnNilArg0: true,
+		},
+		{
+			name:           "STMG3",
+			argLength:      5,
+			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
+			aux:            "SymOff",
+			typ:            "Mem",
+			asm:            "STMG",
+			faultOnNilArg0: true,
+		},
+		{
+			name:      "STMG4",
+			argLength: 6,
+			reg: regInfo{inputs: []regMask{
+				ptrsp,
+				buildReg("R1"),
+				buildReg("R2"),
+				buildReg("R3"),
+				buildReg("R4"),
+				0,
+			}},
+			aux:            "SymOff",
+			typ:            "Mem",
+			asm:            "STMG",
+			faultOnNilArg0: true,
+		},
+		{
+			name:           "STM2",
+			argLength:      4,
+			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
+			aux:            "SymOff",
+			typ:            "Mem",
+			asm:            "STMY",
+			faultOnNilArg0: true,
+		},
+		{
+			name:           "STM3",
+			argLength:      5,
+			reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
+			aux:            "SymOff",
+			typ:            "Mem",
+			asm:            "STMY",
+			faultOnNilArg0: true,
+		},
+		{
+			name:      "STM4",
+			argLength: 6,
+			reg: regInfo{inputs: []regMask{
+				ptrsp,
+				buildReg("R1"),
+				buildReg("R2"),
+				buildReg("R3"),
+				buildReg("R4"),
+				0,
+			}},
+			aux:            "SymOff",
+			typ:            "Mem",
+			asm:            "STMY",
+			faultOnNilArg0: true,
+		},
+
+		// large move
+		// auxint = remaining bytes after loop (rem)
+		// arg0 = address of dst memory (in R1, changed as a side effect)
+		// arg1 = address of src memory (in R2, changed as a side effect)
+		// arg2 = pointer to last address to move in loop + 256
+		// arg3 = mem
+		// returns mem
+		//
+		// mvc: MVC  $256, 0(R2), 0(R1)
+		//      MOVD $256(R1), R1
+		//      MOVD $256(R2), R2
+		//      CMP  R2, Rarg2
+		//      BNE  mvc
+		//	MVC  $rem, 0(R2), 0(R1) // if rem > 0
+		{
+			name:      "LoweredMove",
+			aux:       "Int64",
+			argLength: 4,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R1"), buildReg("R2"), gpsp},
+				clobbers: buildReg("R1 R2"),
+			},
+			clobberFlags: true,
+			typ:          "Mem",
+		},
+
+		// large clear
+		// auxint = remaining bytes after loop (rem)
+		// arg0 = address of dst memory (in R1, changed as a side effect)
+		// arg1 = pointer to last address to zero in loop + 256
+		// arg2 = mem
+		// returns mem
+		//
+		// clear: CLEAR $256, 0(R1)
+		//        MOVD  $256(R1), R1
+		//        CMP   R1, Rarg2
+		//        BNE   clear
+		//	  CLEAR $rem, 0(R1) // if rem > 0
+		{
+			name:      "LoweredZero",
+			aux:       "Int64",
+			argLength: 3,
+			reg: regInfo{
+				inputs:   []regMask{buildReg("R1"), gpsp},
+				clobbers: buildReg("R1"),
+			},
+			clobberFlags: true,
+			typ:          "Mem",
+		},
+	}
+
+	var S390Xblocks = []blockData{
+		{name: "EQ"},
+		{name: "NE"},
+		{name: "LT"},
+		{name: "LE"},
+		{name: "GT"},
+		{name: "GE"},
+		{name: "GTF"}, // FP comparison
+		{name: "GEF"}, // FP comparison
+	}
+
+	archs = append(archs, arch{
+		name:            "S390X",
+		pkg:             "cmd/internal/obj/s390x",
+		genfile:         "../../s390x/ssa.go",
+		ops:             S390Xops,
+		blocks:          S390Xblocks,
+		regnames:        regNamesS390X,
+		gpregmask:       gp,
+		fpregmask:       fp,
+		framepointerreg: -1, // not used
+		linkreg:         int8(num["R14"]),
+	})
+}
diff --git a/src/cmd/compile/internal/ssa/gen/dec64.rules b/src/cmd/compile/internal/ssa/gen/dec64.rules
new file mode 100644
index 0000000..961ffc7
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/dec64.rules
@@ -0,0 +1,447 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains rules to decompose [u]int64 types on 32-bit
+// architectures. These rules work together with the decomposeBuiltIn
+// pass which handles phis of these types.
+
+(Int64Hi (Int64Make hi _)) -> hi
+(Int64Lo (Int64Make _ lo)) -> lo
+
+
+(Load <t> ptr mem) && is64BitInt(t) && !config.BigEndian && t.IsSigned() ->
+	(Int64Make
+		(Load <config.fe.TypeInt32()> (OffPtr <config.fe.TypeInt32().PtrTo()> [4] ptr) mem)
+		(Load <config.fe.TypeUInt32()> ptr mem))
+
+(Load <t> ptr mem) && is64BitInt(t) && !config.BigEndian && !t.IsSigned() ->
+	(Int64Make
+		(Load <config.fe.TypeUInt32()> (OffPtr <config.fe.TypeUInt32().PtrTo()> [4] ptr) mem)
+		(Load <config.fe.TypeUInt32()> ptr mem))
+
+(Load <t> ptr mem) && is64BitInt(t) && config.BigEndian && t.IsSigned() ->
+	(Int64Make
+		(Load <config.fe.TypeInt32()> ptr mem)
+		(Load <config.fe.TypeUInt32()> (OffPtr <config.fe.TypeUInt32().PtrTo()> [4] ptr) mem))
+
+(Load <t> ptr mem) && is64BitInt(t) && config.BigEndian && !t.IsSigned() ->
+	(Int64Make
+		(Load <config.fe.TypeUInt32()> ptr mem)
+		(Load <config.fe.TypeUInt32()> (OffPtr <config.fe.TypeUInt32().PtrTo()> [4] ptr) mem))
+
+(Store [8] dst (Int64Make hi lo) mem) && !config.BigEndian ->
+	(Store [4]
+		(OffPtr <hi.Type.PtrTo()> [4] dst)
+		hi
+		(Store [4] dst lo mem))
+
+(Store [8] dst (Int64Make hi lo) mem) && config.BigEndian ->
+	(Store [4]
+		(OffPtr <lo.Type.PtrTo()> [4] dst)
+		lo
+		(Store [4] dst hi mem))
+
+(Arg {n} [off]) && is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned() ->
+  (Int64Make
+    (Arg <config.fe.TypeInt32()> {n} [off+4])
+    (Arg <config.fe.TypeUInt32()> {n} [off]))
+(Arg {n} [off]) && is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned() ->
+  (Int64Make
+    (Arg <config.fe.TypeUInt32()> {n} [off+4])
+    (Arg <config.fe.TypeUInt32()> {n} [off]))
+
+(Arg {n} [off]) && is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned() ->
+  (Int64Make
+    (Arg <config.fe.TypeInt32()> {n} [off])
+    (Arg <config.fe.TypeUInt32()> {n} [off+4]))
+(Arg {n} [off]) && is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned() ->
+  (Int64Make
+    (Arg <config.fe.TypeUInt32()> {n} [off])
+    (Arg <config.fe.TypeUInt32()> {n} [off+4]))
+
+(Add64 x y) ->
+	(Int64Make
+		(Add32withcarry <config.fe.TypeInt32()>
+			(Int64Hi x)
+			(Int64Hi y)
+			(Select1 <TypeFlags> (Add32carry (Int64Lo x) (Int64Lo y))))
+		(Select0 <config.fe.TypeUInt32()> (Add32carry (Int64Lo x) (Int64Lo y))))
+
+(Sub64 x y) ->
+	(Int64Make
+		(Sub32withcarry <config.fe.TypeInt32()>
+			(Int64Hi x)
+			(Int64Hi y)
+			(Select1 <TypeFlags> (Sub32carry (Int64Lo x) (Int64Lo y))))
+		(Select0 <config.fe.TypeUInt32()> (Sub32carry (Int64Lo x) (Int64Lo y))))
+
+(Mul64 x y) ->
+	(Int64Make
+		(Add32 <config.fe.TypeUInt32()>
+			(Mul32 <config.fe.TypeUInt32()> (Int64Lo x) (Int64Hi y))
+			(Add32 <config.fe.TypeUInt32()>
+				(Mul32 <config.fe.TypeUInt32()> (Int64Hi x) (Int64Lo y))
+				(Select0 <config.fe.TypeUInt32()> (Mul32uhilo (Int64Lo x) (Int64Lo y)))))
+		(Select1 <config.fe.TypeUInt32()> (Mul32uhilo (Int64Lo x) (Int64Lo y))))
+
+(And64 x y) ->
+	(Int64Make
+		(And32 <config.fe.TypeUInt32()> (Int64Hi x) (Int64Hi y))
+		(And32 <config.fe.TypeUInt32()> (Int64Lo x) (Int64Lo y)))
+
+(Or64 x y) ->
+	(Int64Make
+		(Or32 <config.fe.TypeUInt32()> (Int64Hi x) (Int64Hi y))
+		(Or32 <config.fe.TypeUInt32()> (Int64Lo x) (Int64Lo y)))
+
+(Xor64 x y) ->
+	(Int64Make
+		(Xor32 <config.fe.TypeUInt32()> (Int64Hi x) (Int64Hi y))
+		(Xor32 <config.fe.TypeUInt32()> (Int64Lo x) (Int64Lo y)))
+
+(Neg64 <t> x) -> (Sub64 (Const64 <t> [0]) x)
+
+(Com64 x) ->
+	(Int64Make
+		(Com32 <config.fe.TypeUInt32()> (Int64Hi x))
+		(Com32 <config.fe.TypeUInt32()> (Int64Lo x)))
+
+(Ctz64 x) ->
+	(Int64Make
+		(Const32 <config.fe.TypeUInt32()> [0])
+		(Add32 <config.fe.TypeUInt32()>
+			(Ctz32 <config.fe.TypeUInt32()> (Int64Lo x))
+			(And32 <config.fe.TypeUInt32()>
+				(Com32 <config.fe.TypeUInt32()> (Zeromask (Int64Lo x)))
+				(Ctz32 <config.fe.TypeUInt32()> (Int64Hi x)))))
+
+(Bswap64 x) ->
+	(Int64Make
+		(Bswap32 <config.fe.TypeUInt32()> (Int64Lo x))
+		(Bswap32 <config.fe.TypeUInt32()> (Int64Hi x)))
+
+(SignExt32to64 x) -> (Int64Make (Signmask x) x)
+(SignExt16to64 x) -> (SignExt32to64 (SignExt16to32 x))
+(SignExt8to64 x) -> (SignExt32to64 (SignExt8to32 x))
+
+(ZeroExt32to64 x) -> (Int64Make (Const32 <config.fe.TypeUInt32()> [0]) x)
+(ZeroExt16to64 x) -> (ZeroExt32to64 (ZeroExt16to32 x))
+(ZeroExt8to64 x) -> (ZeroExt32to64 (ZeroExt8to32 x))
+
+(Trunc64to32 (Int64Make _ lo)) -> lo
+(Trunc64to16 (Int64Make _ lo)) -> (Trunc32to16 lo)
+(Trunc64to8 (Int64Make _ lo)) -> (Trunc32to8 lo)
+
+(Lsh32x64 _ (Int64Make (Const32 [c]) _)) && c != 0 -> (Const32 [0])
+(Rsh32x64 x (Int64Make (Const32 [c]) _)) && c != 0 -> (Signmask x)
+(Rsh32Ux64 _ (Int64Make (Const32 [c]) _)) && c != 0 -> (Const32 [0])
+(Lsh16x64 _ (Int64Make (Const32 [c]) _)) && c != 0 -> (Const32 [0])
+(Rsh16x64 x (Int64Make (Const32 [c]) _)) && c != 0 -> (Signmask (SignExt16to32 x))
+(Rsh16Ux64 _ (Int64Make (Const32 [c]) _)) && c != 0 -> (Const32 [0])
+(Lsh8x64 _ (Int64Make (Const32 [c]) _)) && c != 0 -> (Const32 [0])
+(Rsh8x64 x (Int64Make (Const32 [c]) _)) && c != 0 -> (Signmask (SignExt8to32 x))
+(Rsh8Ux64 _ (Int64Make (Const32 [c]) _)) && c != 0 -> (Const32 [0])
+
+(Lsh32x64 x (Int64Make (Const32 [0]) lo)) -> (Lsh32x32 x lo)
+(Rsh32x64 x (Int64Make (Const32 [0]) lo)) -> (Rsh32x32 x lo)
+(Rsh32Ux64 x (Int64Make (Const32 [0]) lo)) -> (Rsh32Ux32 x lo)
+(Lsh16x64 x (Int64Make (Const32 [0]) lo)) -> (Lsh16x32 x lo)
+(Rsh16x64 x (Int64Make (Const32 [0]) lo)) -> (Rsh16x32 x lo)
+(Rsh16Ux64 x (Int64Make (Const32 [0]) lo)) -> (Rsh16Ux32 x lo)
+(Lsh8x64 x (Int64Make (Const32 [0]) lo)) -> (Lsh8x32 x lo)
+(Rsh8x64 x (Int64Make (Const32 [0]) lo)) -> (Rsh8x32 x lo)
+(Rsh8Ux64 x (Int64Make (Const32 [0]) lo)) -> (Rsh8Ux32 x lo)
+
+(Lsh64x64 _ (Int64Make (Const32 [c]) _)) && c != 0 -> (Const64 [0])
+(Rsh64x64 x (Int64Make (Const32 [c]) _)) && c != 0 -> (Int64Make (Signmask (Int64Hi x)) (Signmask (Int64Hi x)))
+(Rsh64Ux64 _ (Int64Make (Const32 [c]) _)) && c != 0 -> (Const64 [0])
+
+(Lsh64x64 x (Int64Make (Const32 [0]) lo)) -> (Lsh64x32 x lo)
+(Rsh64x64 x (Int64Make (Const32 [0]) lo)) -> (Rsh64x32 x lo)
+(Rsh64Ux64 x (Int64Make (Const32 [0]) lo)) -> (Rsh64Ux32 x lo)
+
+// turn x64 non-constant shifts to x32 shifts
+// if high 32-bit of the shift is nonzero, make a huge shift
+(Lsh64x64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Lsh64x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Rsh64x64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Rsh64x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Rsh64Ux64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Rsh64Ux32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Lsh32x64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Lsh32x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Rsh32x64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Rsh32x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Rsh32Ux64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Rsh32Ux32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Lsh16x64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Lsh16x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Rsh16x64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Rsh16x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Rsh16Ux64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Rsh16Ux32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Lsh8x64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Lsh8x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Rsh8x64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Rsh8x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+(Rsh8Ux64 x (Int64Make hi lo)) && hi.Op != OpConst32 ->
+	(Rsh8Ux32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+
+// 64x left shift
+// result.hi = hi<<s | lo>>(32-s) | lo<<(s-32) // >> is unsigned, large shifts result 0
+// result.lo = lo<<s
+(Lsh64x32 (Int64Make hi lo) s) ->
+	(Int64Make
+		(Or32 <config.fe.TypeUInt32()>
+			(Or32 <config.fe.TypeUInt32()>
+				(Lsh32x32 <config.fe.TypeUInt32()> hi s)
+				(Rsh32Ux32 <config.fe.TypeUInt32()>
+					lo
+					(Sub32 <config.fe.TypeUInt32()> (Const32 <config.fe.TypeUInt32()> [32]) s)))
+			(Lsh32x32 <config.fe.TypeUInt32()>
+				lo
+				(Sub32 <config.fe.TypeUInt32()> s (Const32 <config.fe.TypeUInt32()> [32]))))
+		(Lsh32x32 <config.fe.TypeUInt32()> lo s))
+(Lsh64x16 (Int64Make hi lo) s) ->
+	(Int64Make
+		(Or32 <config.fe.TypeUInt32()>
+			(Or32 <config.fe.TypeUInt32()>
+				(Lsh32x16 <config.fe.TypeUInt32()> hi s)
+				(Rsh32Ux16 <config.fe.TypeUInt32()>
+					lo
+					(Sub16 <config.fe.TypeUInt16()> (Const16 <config.fe.TypeUInt16()> [32]) s)))
+			(Lsh32x16 <config.fe.TypeUInt32()>
+				lo
+				(Sub16 <config.fe.TypeUInt16()> s (Const16 <config.fe.TypeUInt16()> [32]))))
+		(Lsh32x16 <config.fe.TypeUInt32()> lo s))
+(Lsh64x8 (Int64Make hi lo) s) ->
+	(Int64Make
+		(Or32 <config.fe.TypeUInt32()>
+			(Or32 <config.fe.TypeUInt32()>
+				(Lsh32x8 <config.fe.TypeUInt32()> hi s)
+				(Rsh32Ux8 <config.fe.TypeUInt32()>
+					lo
+					(Sub8 <config.fe.TypeUInt8()> (Const8 <config.fe.TypeUInt8()> [32]) s)))
+			(Lsh32x8 <config.fe.TypeUInt32()>
+				lo
+				(Sub8 <config.fe.TypeUInt8()> s (Const8 <config.fe.TypeUInt8()> [32]))))
+		(Lsh32x8 <config.fe.TypeUInt32()> lo s))
+
+// 64x unsigned right shift
+// result.hi = hi>>s
+// result.lo = lo>>s | hi<<(32-s) | hi>>(s-32) // >> is unsigned, large shifts result 0
+(Rsh64Ux32 (Int64Make hi lo) s) ->
+	(Int64Make
+		(Rsh32Ux32 <config.fe.TypeUInt32()> hi s)
+		(Or32 <config.fe.TypeUInt32()>
+			(Or32 <config.fe.TypeUInt32()>
+				(Rsh32Ux32 <config.fe.TypeUInt32()> lo s)
+				(Lsh32x32 <config.fe.TypeUInt32()>
+					hi
+					(Sub32 <config.fe.TypeUInt32()> (Const32 <config.fe.TypeUInt32()> [32]) s)))
+			(Rsh32Ux32 <config.fe.TypeUInt32()>
+				hi
+				(Sub32 <config.fe.TypeUInt32()> s (Const32 <config.fe.TypeUInt32()> [32])))))
+(Rsh64Ux16 (Int64Make hi lo) s) ->
+	(Int64Make
+		(Rsh32Ux16 <config.fe.TypeUInt32()> hi s)
+		(Or32 <config.fe.TypeUInt32()>
+			(Or32 <config.fe.TypeUInt32()>
+				(Rsh32Ux16 <config.fe.TypeUInt32()> lo s)
+				(Lsh32x16 <config.fe.TypeUInt32()>
+					hi
+					(Sub16 <config.fe.TypeUInt16()> (Const16 <config.fe.TypeUInt16()> [32]) s)))
+			(Rsh32Ux16 <config.fe.TypeUInt32()>
+				hi
+				(Sub16 <config.fe.TypeUInt16()> s (Const16 <config.fe.TypeUInt16()> [32])))))
+(Rsh64Ux8 (Int64Make hi lo) s) ->
+	(Int64Make
+		(Rsh32Ux8 <config.fe.TypeUInt32()> hi s)
+		(Or32 <config.fe.TypeUInt32()>
+			(Or32 <config.fe.TypeUInt32()>
+				(Rsh32Ux8 <config.fe.TypeUInt32()> lo s)
+				(Lsh32x8 <config.fe.TypeUInt32()>
+					hi
+					(Sub8 <config.fe.TypeUInt8()> (Const8 <config.fe.TypeUInt8()> [32]) s)))
+			(Rsh32Ux8 <config.fe.TypeUInt32()>
+				hi
+				(Sub8 <config.fe.TypeUInt8()> s (Const8 <config.fe.TypeUInt8()> [32])))))
+
+// 64x signed right shift
+// result.hi = hi>>s
+// result.lo = lo>>s | hi<<(32-s) | (hi>>(s-32))&zeromask(s>>5) // hi>>(s-32) is signed, large shifts result 0/-1
+(Rsh64x32 (Int64Make hi lo) s) ->
+	(Int64Make
+		(Rsh32x32 <config.fe.TypeUInt32()> hi s)
+		(Or32 <config.fe.TypeUInt32()>
+			(Or32 <config.fe.TypeUInt32()>
+				(Rsh32Ux32 <config.fe.TypeUInt32()> lo s)
+				(Lsh32x32 <config.fe.TypeUInt32()>
+					hi
+					(Sub32 <config.fe.TypeUInt32()> (Const32 <config.fe.TypeUInt32()> [32]) s)))
+			(And32 <config.fe.TypeUInt32()>
+				(Rsh32x32 <config.fe.TypeUInt32()>
+					hi
+					(Sub32 <config.fe.TypeUInt32()> s (Const32 <config.fe.TypeUInt32()> [32])))
+				(Zeromask
+					(Rsh32Ux32 <config.fe.TypeUInt32()> s (Const32 <config.fe.TypeUInt32()> [5]))))))
+(Rsh64x16 (Int64Make hi lo) s) ->
+	(Int64Make
+		(Rsh32x16 <config.fe.TypeUInt32()> hi s)
+		(Or32 <config.fe.TypeUInt32()>
+			(Or32 <config.fe.TypeUInt32()>
+				(Rsh32Ux16 <config.fe.TypeUInt32()> lo s)
+				(Lsh32x16 <config.fe.TypeUInt32()>
+					hi
+					(Sub16 <config.fe.TypeUInt16()> (Const16 <config.fe.TypeUInt16()> [32]) s)))
+			(And32 <config.fe.TypeUInt32()>
+				(Rsh32x16 <config.fe.TypeUInt32()>
+					hi
+					(Sub16 <config.fe.TypeUInt16()> s (Const16 <config.fe.TypeUInt16()> [32])))
+				(Zeromask
+					(ZeroExt16to32
+						(Rsh16Ux32 <config.fe.TypeUInt16()> s (Const32 <config.fe.TypeUInt32()> [5])))))))
+(Rsh64x8 (Int64Make hi lo) s) ->
+	(Int64Make
+		(Rsh32x8 <config.fe.TypeUInt32()> hi s)
+		(Or32 <config.fe.TypeUInt32()>
+			(Or32 <config.fe.TypeUInt32()>
+				(Rsh32Ux8 <config.fe.TypeUInt32()> lo s)
+				(Lsh32x8 <config.fe.TypeUInt32()>
+					hi
+					(Sub8 <config.fe.TypeUInt8()> (Const8 <config.fe.TypeUInt8()> [32]) s)))
+			(And32 <config.fe.TypeUInt32()>
+				(Rsh32x8 <config.fe.TypeUInt32()>
+					hi
+					(Sub8 <config.fe.TypeUInt8()> s (Const8 <config.fe.TypeUInt8()> [32])))
+				(Zeromask
+					(ZeroExt8to32
+						(Rsh8Ux32 <config.fe.TypeUInt8()> s (Const32 <config.fe.TypeUInt32()> [5])))))))
+
+// 64xConst32 shifts
+// we probably do not need them -- lateopt may take care of them just fine
+//(Lsh64x32 _ (Const32 [c])) && uint32(c) >= 64 -> (Const64 [0])
+//(Rsh64x32 x (Const32 [c])) && uint32(c) >= 64 -> (Int64Make (Signmask (Int64Hi x)) (Signmask (Int64Hi x)))
+//(Rsh64Ux32 _ (Const32 [c])) && uint32(c) >= 64 -> (Const64 [0])
+//
+//(Lsh64x32 x (Const32 [c])) && c < 64 && c > 32 ->
+//	(Int64Make
+//		(Lsh32x32 <config.fe.TypeUInt32()> (Int64Lo x) (Const32 <config.fe.TypeUInt32()> [c-32]))
+//		(Const32 <config.fe.TypeUInt32()> [0]))
+//(Rsh64x32 x (Const32 [c])) && c < 64 && c > 32 ->
+//	(Int64Make
+//		(Signmask (Int64Hi x))
+//		(Rsh32x32 <config.fe.TypeInt32()> (Int64Hi x) (Const32 <config.fe.TypeUInt32()> [c-32])))
+//(Rsh64Ux32 x (Const32 [c])) && c < 64 && c > 32 ->
+//	(Int64Make
+//		(Const32 <config.fe.TypeUInt32()> [0])
+//		(Rsh32Ux32 <config.fe.TypeUInt32()> (Int64Hi x) (Const32 <config.fe.TypeUInt32()> [c-32])))
+//
+//(Lsh64x32 x (Const32 [32])) -> (Int64Make (Int64Lo x) (Const32 <config.fe.TypeUInt32()> [0]))
+//(Rsh64x32 x (Const32 [32])) -> (Int64Make (Signmask (Int64Hi x)) (Int64Hi x))
+//(Rsh64Ux32 x (Const32 [32])) -> (Int64Make (Const32 <config.fe.TypeUInt32()> [0]) (Int64Hi x))
+//
+//(Lsh64x32 x (Const32 [c])) && c < 32 && c > 0 ->
+//	(Int64Make
+//		(Or32 <config.fe.TypeUInt32()>
+//			(Lsh32x32 <config.fe.TypeUInt32()> (Int64Hi x) (Const32 <config.fe.TypeUInt32()> [c]))
+//			(Rsh32Ux32 <config.fe.TypeUInt32()> (Int64Lo x) (Const32 <config.fe.TypeUInt32()> [32-c])))
+//		(Lsh32x32 <config.fe.TypeUInt32()> (Int64Lo x) (Const32 <config.fe.TypeUInt32()> [c])))
+//(Rsh64x32 x (Const32 [c])) && c < 32 && c > 0 ->
+//	(Int64Make
+//		(Rsh32x32 <config.fe.TypeInt32()> (Int64Hi x) (Const32 <config.fe.TypeUInt32()> [c]))
+//		(Or32 <config.fe.TypeUInt32()>
+//			(Rsh32Ux32 <config.fe.TypeUInt32()> (Int64Lo x) (Const32 <config.fe.TypeUInt32()> [c]))
+//			(Lsh32x32 <config.fe.TypeUInt32()> (Int64Hi x) (Const32 <config.fe.TypeUInt32()> [32-c]))))
+//(Rsh64Ux32 x (Const32 [c])) && c < 32 && c > 0 ->
+//	(Int64Make
+//		(Rsh32Ux32 <config.fe.TypeUInt32()> (Int64Hi x) (Const32 <config.fe.TypeUInt32()> [c]))
+//		(Or32 <config.fe.TypeUInt32()>
+//			(Rsh32Ux32 <config.fe.TypeUInt32()> (Int64Lo x) (Const32 <config.fe.TypeUInt32()> [c]))
+//			(Lsh32x32 <config.fe.TypeUInt32()> (Int64Hi x) (Const32 <config.fe.TypeUInt32()> [32-c]))))
+//
+//(Lsh64x32 x (Const32 [0])) -> x
+//(Rsh64x32 x (Const32 [0])) -> x
+//(Rsh64Ux32 x (Const32 [0])) -> x
+
+(Lrot64 (Int64Make hi lo) [c]) && c <= 32 ->
+	(Int64Make
+		(Or32 <config.fe.TypeUInt32()>
+			(Lsh32x32 <config.fe.TypeUInt32()> hi (Const32 <config.fe.TypeUInt32()> [c]))
+			(Rsh32Ux32 <config.fe.TypeUInt32()> lo (Const32 <config.fe.TypeUInt32()> [32-c])))
+		(Or32 <config.fe.TypeUInt32()>
+			(Lsh32x32 <config.fe.TypeUInt32()> lo (Const32 <config.fe.TypeUInt32()> [c]))
+			(Rsh32Ux32 <config.fe.TypeUInt32()> hi (Const32 <config.fe.TypeUInt32()> [32-c]))))
+(Lrot64 (Int64Make hi lo) [c]) && c > 32 -> (Lrot64 (Int64Make lo hi) [c-32])
+
+(Const64 <t> [c]) && t.IsSigned() ->
+	(Int64Make (Const32 <config.fe.TypeInt32()> [c>>32]) (Const32 <config.fe.TypeUInt32()> [int64(int32(c))]))
+(Const64 <t> [c]) && !t.IsSigned() ->
+	(Int64Make (Const32 <config.fe.TypeUInt32()> [c>>32]) (Const32 <config.fe.TypeUInt32()> [int64(int32(c))]))
+
+(Eq64 x y) ->
+	(AndB
+		(Eq32 (Int64Hi x) (Int64Hi y))
+		(Eq32 (Int64Lo x) (Int64Lo y)))
+
+(Neq64 x y) ->
+	(OrB
+		(Neq32 (Int64Hi x) (Int64Hi y))
+		(Neq32 (Int64Lo x) (Int64Lo y)))
+
+(Less64U x y) ->
+	(OrB
+		(Less32U (Int64Hi x) (Int64Hi y))
+		(AndB
+			(Eq32 (Int64Hi x) (Int64Hi y))
+			(Less32U (Int64Lo x) (Int64Lo y))))
+
+(Leq64U x y) ->
+	(OrB
+		(Less32U (Int64Hi x) (Int64Hi y))
+		(AndB
+			(Eq32 (Int64Hi x) (Int64Hi y))
+			(Leq32U (Int64Lo x) (Int64Lo y))))
+
+(Greater64U x y) ->
+	(OrB
+		(Greater32U (Int64Hi x) (Int64Hi y))
+		(AndB
+			(Eq32 (Int64Hi x) (Int64Hi y))
+			(Greater32U (Int64Lo x) (Int64Lo y))))
+
+(Geq64U x y) ->
+	(OrB
+		(Greater32U (Int64Hi x) (Int64Hi y))
+		(AndB
+			(Eq32 (Int64Hi x) (Int64Hi y))
+			(Geq32U (Int64Lo x) (Int64Lo y))))
+
+(Less64 x y) ->
+	(OrB
+		(Less32 (Int64Hi x) (Int64Hi y))
+		(AndB
+			(Eq32 (Int64Hi x) (Int64Hi y))
+			(Less32U (Int64Lo x) (Int64Lo y))))
+
+(Leq64 x y) ->
+	(OrB
+		(Less32 (Int64Hi x) (Int64Hi y))
+		(AndB
+			(Eq32 (Int64Hi x) (Int64Hi y))
+			(Leq32U (Int64Lo x) (Int64Lo y))))
+
+(Greater64 x y) ->
+	(OrB
+		(Greater32 (Int64Hi x) (Int64Hi y))
+		(AndB
+			(Eq32 (Int64Hi x) (Int64Hi y))
+			(Greater32U (Int64Lo x) (Int64Lo y))))
+
+(Geq64 x y) ->
+	(OrB
+		(Greater32 (Int64Hi x) (Int64Hi y))
+		(AndB
+			(Eq32 (Int64Hi x) (Int64Hi y))
+			(Geq32U (Int64Lo x) (Int64Lo y))))
diff --git a/src/cmd/compile/internal/ssa/gen/dec64Ops.go b/src/cmd/compile/internal/ssa/gen/dec64Ops.go
new file mode 100644
index 0000000..8c5883b
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/gen/dec64Ops.go
@@ -0,0 +1,20 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package main
+
+var dec64Ops = []opData{}
+
+var dec64Blocks = []blockData{}
+
+func init() {
+	archs = append(archs, arch{
+		name:    "dec64",
+		ops:     dec64Ops,
+		blocks:  dec64Blocks,
+		generic: true,
+	})
+}
diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules
index f5d1c98..0137b10 100644
--- a/src/cmd/compile/internal/ssa/gen/generic.rules
+++ b/src/cmd/compile/internal/ssa/gen/generic.rules
@@ -2,6 +2,19 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// Simplifications that apply to all backend architectures. As an example, this
+// Go source code
+//
+// y := 0 * x
+//
+// can be translated into y := 0 without losing any information, which saves a
+// pointless multiplication instruction. Other .rules files in this directory
+// (for example AMD64.rules) contain rules specific to the architecture in the
+// filename. The rules here apply to every architecture.
+//
+// The code for parsing this file lives in rulegen.go; this file generates
+// ssa/rewritegeneric.go.
+
 // values are specified using the following format:
 // (op <type> [auxint] {aux} arg0 arg1 ...)
 // the type, aux, and auxint fields are optional
@@ -34,6 +47,27 @@
 (Cvt64Fto32F (Const64F [c])) -> (Const32F [f2i(float64(i2f32(c)))])
 (Cvt32Fto64F (Const32F [c])) -> (Const64F [c]) // c is already a 64 bit float
 
+(Trunc16to8  (ZeroExt8to16  x)) -> x
+(Trunc32to8  (ZeroExt8to32  x)) -> x
+(Trunc32to16 (ZeroExt8to32  x)) -> (ZeroExt8to16  x)
+(Trunc32to16 (ZeroExt16to32 x)) -> x
+(Trunc64to8  (ZeroExt8to64  x)) -> x
+(Trunc64to16 (ZeroExt8to64  x)) -> (ZeroExt8to16  x)
+(Trunc64to16 (ZeroExt16to64 x)) -> x
+(Trunc64to32 (ZeroExt8to64  x)) -> (ZeroExt8to32  x)
+(Trunc64to32 (ZeroExt16to64 x)) -> (ZeroExt16to32 x)
+(Trunc64to32 (ZeroExt32to64 x)) -> x
+(Trunc16to8  (SignExt8to16  x)) -> x
+(Trunc32to8  (SignExt8to32  x)) -> x
+(Trunc32to16 (SignExt8to32  x)) -> (SignExt8to16  x)
+(Trunc32to16 (SignExt16to32 x)) -> x
+(Trunc64to8  (SignExt8to64  x)) -> x
+(Trunc64to16 (SignExt8to64  x)) -> (SignExt8to16  x)
+(Trunc64to16 (SignExt16to64 x)) -> x
+(Trunc64to32 (SignExt8to64  x)) -> (SignExt8to32  x)
+(Trunc64to32 (SignExt16to64 x)) -> (SignExt16to32 x)
+(Trunc64to32 (SignExt32to64 x)) -> x
+
 // const negation is currently handled by frontend
 //(Neg8 (Const8 [c])) -> (Const8 [-c])
 //(Neg16 (Const16 [c])) -> (Const16 [-c])
@@ -46,7 +80,7 @@
 (Add16  (Const16 [c])  (Const16 [d]))  -> (Const16 [int64(int16(c+d))])
 (Add32  (Const32 [c])  (Const32 [d]))  -> (Const32 [int64(int32(c+d))])
 (Add64  (Const64 [c])  (Const64 [d]))  -> (Const64 [c+d])
-(Add32F (Const32F [c]) (Const32F [d])) -> 
+(Add32F (Const32F [c]) (Const32F [d])) ->
         (Const32F [f2i(float64(i2f32(c) + i2f32(d)))]) // ensure we combine the operands with 32 bit precision
 (Add64F (Const64F [c]) (Const64F [d])) -> (Const64F [f2i(i2f(c) + i2f(d))])
 (AddPtr <t> x (Const64 [c])) -> (OffPtr <t> x [c])
@@ -55,7 +89,7 @@
 (Sub16  (Const16 [c]) (Const16 [d]))   -> (Const16 [int64(int16(c-d))])
 (Sub32  (Const32 [c]) (Const32 [d]))   -> (Const32 [int64(int32(c-d))])
 (Sub64  (Const64 [c]) (Const64 [d]))   -> (Const64 [c-d])
-(Sub32F (Const32F [c]) (Const32F [d])) -> 
+(Sub32F (Const32F [c]) (Const32F [d])) ->
         (Const32F [f2i(float64(i2f32(c) - i2f32(d)))])
 (Sub64F (Const64F [c]) (Const64F [d])) -> (Const64F [f2i(i2f(c) - i2f(d))])
 
@@ -63,10 +97,16 @@
 (Mul16  (Const16 [c])  (Const16 [d]))  -> (Const16 [int64(int16(c*d))])
 (Mul32  (Const32 [c])  (Const32 [d]))  -> (Const32 [int64(int32(c*d))])
 (Mul64  (Const64 [c])  (Const64 [d]))  -> (Const64 [c*d])
-(Mul32F (Const32F [c]) (Const32F [d])) -> 
+(Mul32F (Const32F [c]) (Const32F [d])) ->
         (Const32F [f2i(float64(i2f32(c) * i2f32(d)))])
 (Mul64F (Const64F [c]) (Const64F [d])) -> (Const64F [f2i(i2f(c) * i2f(d))])
 
+// Convert x * -1 to -x. The front-end catches some but not all of these.
+(Mul8  (Const8  [-1]) x) -> (Neg8  x)
+(Mul16 (Const16 [-1]) x) -> (Neg16 x)
+(Mul32 (Const32 [-1]) x) -> (Neg32 x)
+(Mul64 (Const64 [-1]) x) -> (Neg64 x)
+
 (Mod8  (Const8  [c]) (Const8  [d])) && d != 0 -> (Const8  [int64(int8(c % d))])
 (Mod16 (Const16 [c]) (Const16 [d])) && d != 0 -> (Const16 [int64(int16(c % d))])
 (Mod32 (Const32 [c]) (Const32 [d])) && d != 0 -> (Const32 [int64(int32(c % d))])
@@ -181,6 +221,59 @@
 (Xor16 x (Const16 <t> [c])) && x.Op != OpConst16 -> (Xor16 (Const16 <t> [c]) x)
 (Xor8  x (Const8  <t> [c])) && x.Op != OpConst8  -> (Xor8  (Const8  <t> [c]) x)
 
+// fold negation into comparison operators
+(Not (Eq64 x y)) -> (Neq64 x y)
+(Not (Eq32 x y)) -> (Neq32 x y)
+(Not (Eq16 x y)) -> (Neq16 x y)
+(Not (Eq8  x y)) -> (Neq8  x y)
+(Not (EqB  x y)) -> (NeqB  x y)
+
+(Not (Neq64 x y)) -> (Eq64 x y)
+(Not (Neq32 x y)) -> (Eq32 x y)
+(Not (Neq16 x y)) -> (Eq16 x y)
+(Not (Neq8  x y)) -> (Eq8  x y)
+(Not (NeqB  x y)) -> (EqB  x y)
+
+(Not (Greater64 x y)) -> (Leq64 x y)
+(Not (Greater32 x y)) -> (Leq32 x y)
+(Not (Greater16 x y)) -> (Leq16 x y)
+(Not (Greater8  x y)) -> (Leq8  x y)
+
+(Not (Greater64U x y)) -> (Leq64U x y)
+(Not (Greater32U x y)) -> (Leq32U x y)
+(Not (Greater16U x y)) -> (Leq16U x y)
+(Not (Greater8U  x y)) -> (Leq8U  x y)
+
+(Not (Geq64 x y)) -> (Less64 x y)
+(Not (Geq32 x y)) -> (Less32 x y)
+(Not (Geq16 x y)) -> (Less16 x y)
+(Not (Geq8  x y)) -> (Less8  x y)
+
+(Not (Geq64U x y)) -> (Less64U x y)
+(Not (Geq32U x y)) -> (Less32U x y)
+(Not (Geq16U x y)) -> (Less16U x y)
+(Not (Geq8U  x y)) -> (Less8U  x y)
+
+(Not (Less64 x y)) -> (Geq64 x y)
+(Not (Less32 x y)) -> (Geq32 x y)
+(Not (Less16 x y)) -> (Geq16 x y)
+(Not (Less8  x y)) -> (Geq8  x y)
+
+(Not (Less64U x y)) -> (Geq64U x y)
+(Not (Less32U x y)) -> (Geq32U x y)
+(Not (Less16U x y)) -> (Geq16U x y)
+(Not (Less8U  x y)) -> (Geq8U  x y)
+
+(Not (Leq64 x y)) -> (Greater64 x y)
+(Not (Leq32 x y)) -> (Greater32 x y)
+(Not (Leq16 x y)) -> (Greater16 x y)
+(Not (Leq8  x y)) -> (Greater8 x y)
+
+(Not (Leq64U x y)) -> (Greater64U x y)
+(Not (Leq32U x y)) -> (Greater32U x y)
+(Not (Leq16U x y)) -> (Greater16U x y)
+(Not (Leq8U  x y)) -> (Greater8U  x y)
+
 // Distribute multiplication c * (d+x) -> c*d + c*x. Useful for:
 // a[i].b = ...; a[i+1].b = ...
 (Mul64 (Const64 <t> [c]) (Add64 <t> (Const64 <t> [d]) x)) ->
@@ -509,6 +602,25 @@
 (Trunc32to16 (And32 (Const32 [y]) x)) && y&0xFFFF == 0xFFFF -> (Trunc32to16 x)
 (Trunc16to8  (And16 (Const16 [y]) x)) && y&0xFF == 0xFF -> (Trunc16to8 x)
 
+(ZeroExt8to64  (Trunc64to8  x:(Rsh64Ux64 _ (Const64 [s])))) && s >= 56 -> x
+(ZeroExt16to64 (Trunc64to16 x:(Rsh64Ux64 _ (Const64 [s])))) && s >= 48 -> x
+(ZeroExt32to64 (Trunc64to32 x:(Rsh64Ux64 _ (Const64 [s])))) && s >= 32 -> x
+(ZeroExt8to32  (Trunc32to8  x:(Rsh32Ux64 _ (Const64 [s])))) && s >= 24 -> x
+(ZeroExt16to32 (Trunc32to16 x:(Rsh32Ux64 _ (Const64 [s])))) && s >= 16 -> x
+(ZeroExt8to16  (Trunc16to8  x:(Rsh16Ux64 _ (Const64 [s])))) && s >= 8 -> x
+
+(SignExt8to64  (Trunc64to8  x:(Rsh64x64 _ (Const64 [s])))) && s >= 56 -> x
+(SignExt16to64 (Trunc64to16 x:(Rsh64x64 _ (Const64 [s])))) && s >= 48 -> x
+(SignExt32to64 (Trunc64to32 x:(Rsh64x64 _ (Const64 [s])))) && s >= 32 -> x
+(SignExt8to32  (Trunc32to8  x:(Rsh32x64 _ (Const64 [s])))) && s >= 24 -> x
+(SignExt16to32 (Trunc32to16 x:(Rsh32x64 _ (Const64 [s])))) && s >= 16 -> x
+(SignExt8to16  (Trunc16to8  x:(Rsh16x64 _ (Const64 [s])))) && s >= 8 -> x
+
+(Slicemask (Const32 [x])) && x > 0 -> (Const32 [-1])
+(Slicemask (Const32 [0]))          -> (Const32 [0])
+(Slicemask (Const64 [x])) && x > 0 -> (Const64 [-1])
+(Slicemask (Const64 [0]))          -> (Const64 [0])
+
 // Rewrite AND of consts as shifts if possible, slightly faster for 64 bit operands
 // leading zeros can be shifted left, then right
 (And64 <t> (Const64 [y]) x) && nlz(y) + nto(y) == 64 && nto(y) >= 32
@@ -556,7 +668,6 @@
 
 // indexing operations
 // Note: bounds check has already been done
-(ArrayIndex <t> [0] x:(Load ptr mem)) -> @x.Block (Load <t> ptr mem)
 (PtrIndex <t> ptr idx) && config.PtrSize == 4 -> (AddPtr ptr (Mul32 <config.fe.TypeInt()> idx (Const32 <config.fe.TypeInt()> [t.ElemType().Size()])))
 (PtrIndex <t> ptr idx) && config.PtrSize == 8 -> (AddPtr ptr (Mul64 <config.fe.TypeInt()> idx (Const64 <config.fe.TypeInt()> [t.ElemType().Size()])))
 
@@ -624,9 +735,33 @@
         f1
         (Store [t.FieldType(0).Size()] dst f0 mem))))
 
+// Putting struct{*byte} and similar into direct interfaces.
+(IMake typ (StructMake1 val)) -> (IMake typ val)
+(StructSelect [0] x:(IData _)) -> x
+
 // un-SSAable values use mem->mem copies
-(Store [size] dst (Load <t> src mem) mem) && !config.fe.CanSSA(t) -> (Move [size] dst src mem)
-(Store [size] dst (Load <t> src mem) (VarDef {x} mem)) && !config.fe.CanSSA(t) -> (Move [size] dst src (VarDef {x} mem))
+(Store [size] dst (Load <t> src mem) mem) && !config.fe.CanSSA(t) ->
+	(Move [MakeSizeAndAlign(size, t.Alignment()).Int64()] dst src mem)
+(Store [size] dst (Load <t> src mem) (VarDef {x} mem)) && !config.fe.CanSSA(t) ->
+	(Move [MakeSizeAndAlign(size, t.Alignment()).Int64()] dst src (VarDef {x} mem))
+
+// array ops
+(ArraySelect (ArrayMake1 x)) -> x
+
+(Load <t> _ _) && t.IsArray() && t.NumElem() == 0 ->
+  (ArrayMake0)
+
+(Load <t> ptr mem) && t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t) ->
+  (ArrayMake1 (Load <t.ElemType()> ptr mem))
+
+(Store _ (ArrayMake0) mem) -> mem
+(Store [size] dst (ArrayMake1 e) mem) -> (Store [size] dst e mem)
+
+(ArraySelect [0] (Load ptr mem)) -> (Load ptr mem)
+
+// Putting [1]{*byte} and similar into direct interfaces.
+(IMake typ (ArrayMake1 val)) -> (IMake typ val)
+(ArraySelect [0] x:(IData _)) -> x
 
 // string ops
 // Decomposing StringMake and lowering of StringPtr and StringLen
@@ -654,6 +789,8 @@
 // a more comprehensive set.
 (SliceLen (SliceMake _ (Const64 <t> [c]) _)) -> (Const64 <t> [c])
 (SliceCap (SliceMake _ _ (Const64 <t> [c]))) -> (Const64 <t> [c])
+(SliceLen (SliceMake _ (Const32 <t> [c]) _)) -> (Const32 <t> [c])
+(SliceCap (SliceMake _ _ (Const32 <t> [c]))) -> (Const32 <t> [c])
 (SlicePtr (SliceMake (SlicePtr x) _ _)) -> (SlicePtr x)
 (SliceLen (SliceMake _ (SliceLen x) _)) -> (SliceLen x)
 (SliceCap (SliceMake _ _ (SliceCap x))) -> (SliceCap x)
@@ -675,7 +812,7 @@
     (ConstNil <config.fe.TypeBytePtr()>)
     (ConstNil <config.fe.TypeBytePtr()>))
 
-(Check (NilCheck (GetG _) _) next) -> (Plain nil next)
+(NilCheck (GetG mem) mem) -> mem
 
 (If (Not cond) yes no) -> (If cond no yes)
 (If (ConstBool [c]) yes no) && c == 1 -> (First nil yes no)
@@ -734,6 +871,11 @@
     (Arg <t.FieldType(2)> {n} [off+t.FieldOff(2)])
     (Arg <t.FieldType(3)> {n} [off+t.FieldOff(3)]))
 
+(Arg <t>) && t.IsArray() && t.NumElem() == 0 ->
+  (ArrayMake0)
+(Arg <t> {n} [off]) && t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t) ->
+  (ArrayMake1 (Arg <t.ElemType()> {n} [off]))
+
 // strength reduction of divide by a constant.
 // Note: frontend does <=32 bits. We only need to do 64 bits here.
 // TODO: Do them all here?
@@ -832,3 +974,46 @@
   -> (Sub64 x (Mul64 <t> (Div64  <t> x (Const64 <t> [c])) (Const64 <t> [c])))
 (Mod64u <t> x (Const64 [c])) && x.Op != OpConst64 && umagic64ok(c)
   -> (Sub64 x (Mul64 <t> (Div64u <t> x (Const64 <t> [c])) (Const64 <t> [c])))
+
+// floating point optimizations
+(Add32F x (Const32F [0])) -> x
+(Add32F (Const32F [0]) x) -> x
+(Add64F x (Const64F [0])) -> x
+(Add64F (Const64F [0]) x) -> x
+(Sub32F x (Const32F [0])) -> x
+(Sub64F x (Const64F [0])) -> x
+(Mul32F x (Const32F [f2i(1)])) -> x
+(Mul32F (Const32F [f2i(1)]) x) -> x
+(Mul64F x (Const64F [f2i(1)])) -> x
+(Mul64F (Const64F [f2i(1)]) x) -> x
+(Mul32F x (Const32F [f2i(-1)])) -> (Neg32F x)
+(Mul32F (Const32F [f2i(-1)]) x) -> (Neg32F x)
+(Mul64F x (Const64F [f2i(-1)])) -> (Neg64F x)
+(Mul64F (Const64F [f2i(-1)]) x) -> (Neg64F x)
+(Div32F x (Const32F [f2i(1)])) -> x
+(Div64F x (Const64F [f2i(1)])) -> x
+(Div32F x (Const32F [f2i(-1)])) -> (Neg32F x)
+(Div64F x (Const64F [f2i(-1)])) -> (Neg32F x)
+
+(Sqrt (Const64F [c])) -> (Const64F [f2i(math.Sqrt(i2f(c)))])
+
+// recognize runtime.newobject and don't Zero/Nilcheck it
+(Zero (Load (OffPtr [c] (SP)) mem) mem)
+        && mem.Op == OpStaticCall
+	&& isSameSym(mem.Aux, "runtime.newobject")
+	&& c == config.ctxt.FixedFrameSize() + config.PtrSize // offset of return value
+	-> mem
+// nil checks just need to rewrite to something useless.
+// they will be deadcode eliminated soon afterwards.
+(NilCheck (Load (OffPtr [c] (SP)) mem) mem)
+	&& mem.Op == OpStaticCall
+	&& isSameSym(mem.Aux, "runtime.newobject")
+	&& c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value
+	&& warnRule(config.Debug_checknil() && int(v.Line) > 1, v, "removed nil check")
+	-> (Invalid)
+(NilCheck (OffPtr (Load (OffPtr [c] (SP)) mem)) mem)
+	&& mem.Op == OpStaticCall
+	&& isSameSym(mem.Aux, "runtime.newobject")
+	&& c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value
+	&& warnRule(config.Debug_checknil() && int(v.Line) > 1, v, "removed nil check")
+	-> (Invalid)
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index 8388ea8..fe93e52 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -58,6 +58,9 @@ var genericOps = []opData{
 	{name: "Hmul64", argLength: 2},
 	{name: "Hmul64u", argLength: 2},
 
+	{name: "Mul32uhilo", argLength: 2, typ: "(UInt32,UInt32)"}, // arg0 * arg1, returns (hi, lo)
+	{name: "Mul64uhilo", argLength: 2, typ: "(UInt64,UInt64)"}, // arg0 * arg1, returns (hi, lo)
+
 	// Weird special instruction for strength reduction of divides.
 	{name: "Avg64u", argLength: 2}, // (uint64(arg0) + uint64(arg1)) / 2, correct to all 64 bits.
 
@@ -69,6 +72,7 @@ var genericOps = []opData{
 	{name: "Div32u", argLength: 2},
 	{name: "Div64", argLength: 2},
 	{name: "Div64u", argLength: 2},
+	{name: "Div128u", argLength: 3}, // arg0:arg1 / arg2 (128-bit divided by 64-bit), returns (q, r)
 
 	{name: "Mod8", argLength: 2},  // arg0 % arg1, signed
 	{name: "Mod8u", argLength: 2}, // arg0 % arg1, unsigned
@@ -173,76 +177,76 @@ var genericOps = []opData{
 	{name: "Lrot64", argLength: 1, aux: "Int64"},
 
 	// 2-input comparisons
-	{name: "Eq8", argLength: 2, commutative: true}, // arg0 == arg1
-	{name: "Eq16", argLength: 2, commutative: true},
-	{name: "Eq32", argLength: 2, commutative: true},
-	{name: "Eq64", argLength: 2, commutative: true},
-	{name: "EqPtr", argLength: 2, commutative: true},
-	{name: "EqInter", argLength: 2}, // arg0 or arg1 is nil; other cases handled by frontend
-	{name: "EqSlice", argLength: 2}, // arg0 or arg1 is nil; other cases handled by frontend
-	{name: "Eq32F", argLength: 2},
-	{name: "Eq64F", argLength: 2},
-
-	{name: "Neq8", argLength: 2, commutative: true}, // arg0 != arg1
-	{name: "Neq16", argLength: 2, commutative: true},
-	{name: "Neq32", argLength: 2, commutative: true},
-	{name: "Neq64", argLength: 2, commutative: true},
-	{name: "NeqPtr", argLength: 2, commutative: true},
-	{name: "NeqInter", argLength: 2}, // arg0 or arg1 is nil; other cases handled by frontend
-	{name: "NeqSlice", argLength: 2}, // arg0 or arg1 is nil; other cases handled by frontend
-	{name: "Neq32F", argLength: 2},
+	{name: "Eq8", argLength: 2, commutative: true, typ: "Bool"}, // arg0 == arg1
+	{name: "Eq16", argLength: 2, commutative: true, typ: "Bool"},
+	{name: "Eq32", argLength: 2, commutative: true, typ: "Bool"},
+	{name: "Eq64", argLength: 2, commutative: true, typ: "Bool"},
+	{name: "EqPtr", argLength: 2, commutative: true, typ: "Bool"},
+	{name: "EqInter", argLength: 2, typ: "Bool"}, // arg0 or arg1 is nil; other cases handled by frontend
+	{name: "EqSlice", argLength: 2, typ: "Bool"}, // arg0 or arg1 is nil; other cases handled by frontend
+	{name: "Eq32F", argLength: 2, typ: "Bool"},
+	{name: "Eq64F", argLength: 2, typ: "Bool"},
+
+	{name: "Neq8", argLength: 2, commutative: true, typ: "Bool"}, // arg0 != arg1
+	{name: "Neq16", argLength: 2, commutative: true, typ: "Bool"},
+	{name: "Neq32", argLength: 2, commutative: true, typ: "Bool"},
+	{name: "Neq64", argLength: 2, commutative: true, typ: "Bool"},
+	{name: "NeqPtr", argLength: 2, commutative: true, typ: "Bool"},
+	{name: "NeqInter", argLength: 2, typ: "Bool"}, // arg0 or arg1 is nil; other cases handled by frontend
+	{name: "NeqSlice", argLength: 2, typ: "Bool"}, // arg0 or arg1 is nil; other cases handled by frontend
+	{name: "Neq32F", argLength: 2, typ: "Bool"},
 	{name: "Neq64F", argLength: 2},
 
-	{name: "Less8", argLength: 2},  // arg0 < arg1, signed
-	{name: "Less8U", argLength: 2}, // arg0 < arg1, unsigned
-	{name: "Less16", argLength: 2},
-	{name: "Less16U", argLength: 2},
-	{name: "Less32", argLength: 2},
-	{name: "Less32U", argLength: 2},
-	{name: "Less64", argLength: 2},
-	{name: "Less64U", argLength: 2},
-	{name: "Less32F", argLength: 2},
-	{name: "Less64F", argLength: 2},
-
-	{name: "Leq8", argLength: 2},  // arg0 <= arg1, signed
-	{name: "Leq8U", argLength: 2}, // arg0 <= arg1, unsigned
-	{name: "Leq16", argLength: 2},
-	{name: "Leq16U", argLength: 2},
-	{name: "Leq32", argLength: 2},
-	{name: "Leq32U", argLength: 2},
-	{name: "Leq64", argLength: 2},
-	{name: "Leq64U", argLength: 2},
-	{name: "Leq32F", argLength: 2},
-	{name: "Leq64F", argLength: 2},
-
-	{name: "Greater8", argLength: 2},  // arg0 > arg1, signed
-	{name: "Greater8U", argLength: 2}, // arg0 > arg1, unsigned
-	{name: "Greater16", argLength: 2},
-	{name: "Greater16U", argLength: 2},
-	{name: "Greater32", argLength: 2},
-	{name: "Greater32U", argLength: 2},
-	{name: "Greater64", argLength: 2},
-	{name: "Greater64U", argLength: 2},
-	{name: "Greater32F", argLength: 2},
-	{name: "Greater64F", argLength: 2},
-
-	{name: "Geq8", argLength: 2},  // arg0 <= arg1, signed
-	{name: "Geq8U", argLength: 2}, // arg0 <= arg1, unsigned
-	{name: "Geq16", argLength: 2},
-	{name: "Geq16U", argLength: 2},
-	{name: "Geq32", argLength: 2},
-	{name: "Geq32U", argLength: 2},
-	{name: "Geq64", argLength: 2},
-	{name: "Geq64U", argLength: 2},
-	{name: "Geq32F", argLength: 2},
-	{name: "Geq64F", argLength: 2},
+	{name: "Less8", argLength: 2, typ: "Bool"},  // arg0 < arg1, signed
+	{name: "Less8U", argLength: 2, typ: "Bool"}, // arg0 < arg1, unsigned
+	{name: "Less16", argLength: 2, typ: "Bool"},
+	{name: "Less16U", argLength: 2, typ: "Bool"},
+	{name: "Less32", argLength: 2, typ: "Bool"},
+	{name: "Less32U", argLength: 2, typ: "Bool"},
+	{name: "Less64", argLength: 2, typ: "Bool"},
+	{name: "Less64U", argLength: 2, typ: "Bool"},
+	{name: "Less32F", argLength: 2, typ: "Bool"},
+	{name: "Less64F", argLength: 2, typ: "Bool"},
+
+	{name: "Leq8", argLength: 2, typ: "Bool"},  // arg0 <= arg1, signed
+	{name: "Leq8U", argLength: 2, typ: "Bool"}, // arg0 <= arg1, unsigned
+	{name: "Leq16", argLength: 2, typ: "Bool"},
+	{name: "Leq16U", argLength: 2, typ: "Bool"},
+	{name: "Leq32", argLength: 2, typ: "Bool"},
+	{name: "Leq32U", argLength: 2, typ: "Bool"},
+	{name: "Leq64", argLength: 2, typ: "Bool"},
+	{name: "Leq64U", argLength: 2, typ: "Bool"},
+	{name: "Leq32F", argLength: 2, typ: "Bool"},
+	{name: "Leq64F", argLength: 2, typ: "Bool"},
+
+	{name: "Greater8", argLength: 2, typ: "Bool"},  // arg0 > arg1, signed
+	{name: "Greater8U", argLength: 2, typ: "Bool"}, // arg0 > arg1, unsigned
+	{name: "Greater16", argLength: 2, typ: "Bool"},
+	{name: "Greater16U", argLength: 2, typ: "Bool"},
+	{name: "Greater32", argLength: 2, typ: "Bool"},
+	{name: "Greater32U", argLength: 2, typ: "Bool"},
+	{name: "Greater64", argLength: 2, typ: "Bool"},
+	{name: "Greater64U", argLength: 2, typ: "Bool"},
+	{name: "Greater32F", argLength: 2, typ: "Bool"},
+	{name: "Greater64F", argLength: 2, typ: "Bool"},
+
+	{name: "Geq8", argLength: 2, typ: "Bool"},  // arg0 <= arg1, signed
+	{name: "Geq8U", argLength: 2, typ: "Bool"}, // arg0 <= arg1, unsigned
+	{name: "Geq16", argLength: 2, typ: "Bool"},
+	{name: "Geq16U", argLength: 2, typ: "Bool"},
+	{name: "Geq32", argLength: 2, typ: "Bool"},
+	{name: "Geq32U", argLength: 2, typ: "Bool"},
+	{name: "Geq64", argLength: 2, typ: "Bool"},
+	{name: "Geq64U", argLength: 2, typ: "Bool"},
+	{name: "Geq32F", argLength: 2, typ: "Bool"},
+	{name: "Geq64F", argLength: 2, typ: "Bool"},
 
 	// boolean ops
-	{name: "AndB", argLength: 2}, // arg0 && arg1 (not shortcircuited)
-	{name: "OrB", argLength: 2},  // arg0 || arg1 (not shortcircuited)
-	{name: "EqB", argLength: 2},  // arg0 == arg1
-	{name: "NeqB", argLength: 2}, // arg0 != arg1
-	{name: "Not", argLength: 1},  // !arg0, boolean
+	{name: "AndB", argLength: 2, typ: "Bool"}, // arg0 && arg1 (not shortcircuited)
+	{name: "OrB", argLength: 2, typ: "Bool"},  // arg0 || arg1 (not shortcircuited)
+	{name: "EqB", argLength: 2, typ: "Bool"},  // arg0 == arg1
+	{name: "NeqB", argLength: 2, typ: "Bool"}, // arg0 != arg1
+	{name: "Not", argLength: 1, typ: "Bool"},  // !arg0, boolean
 
 	// 1-input ops
 	{name: "Neg8", argLength: 1}, // -arg0
@@ -257,14 +261,9 @@ var genericOps = []opData{
 	{name: "Com32", argLength: 1},
 	{name: "Com64", argLength: 1},
 
-	{name: "Ctz16", argLength: 1}, // Count trailing (low  order) zeroes (returns 0-16)
-	{name: "Ctz32", argLength: 1}, // Count trailing zeroes (returns 0-32)
+	{name: "Ctz32", argLength: 1}, // Count trailing (low  order) zeroes (returns 0-32)
 	{name: "Ctz64", argLength: 1}, // Count trailing zeroes (returns 0-64)
 
-	{name: "Clz16", argLength: 1}, // Count leading (high order) zeroes (returns 0-16)
-	{name: "Clz32", argLength: 1}, // Count leading zeroes (returns 0-32)
-	{name: "Clz64", argLength: 1}, // Count leading zeroes (returns 0-64)
-
 	{name: "Bswap32", argLength: 1}, // Swap bytes
 	{name: "Bswap64", argLength: 1}, // Swap bytes
 
@@ -308,35 +307,43 @@ var genericOps = []opData{
 	{name: "SP"},                 // stack pointer
 	{name: "SB", typ: "Uintptr"}, // static base pointer (a.k.a. globals pointer)
 	{name: "Func", aux: "Sym"},   // entry address of a function
+	{name: "Invalid"},            // unused value
 
 	// Memory operations
-	{name: "Load", argLength: 2},                            // Load from arg0.  arg1=memory
-	{name: "Store", argLength: 3, typ: "Mem", aux: "Int64"}, // Store arg1 to arg0.  arg2=memory, auxint=size.  Returns memory.
-	{name: "Move", argLength: 3, aux: "Int64"},              // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size.  Returns memory.
-	{name: "Zero", argLength: 2, aux: "Int64"},              // arg0=destptr, arg1=mem, auxint=size. Returns memory.
+	{name: "Load", argLength: 2},                                  // Load from arg0.  arg1=memory
+	{name: "Store", argLength: 3, typ: "Mem", aux: "Int64"},       // Store arg1 to arg0.  arg2=memory, auxint=size.  Returns memory.
+	{name: "Move", argLength: 3, typ: "Mem", aux: "SizeAndAlign"}, // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size+alignment.  Returns memory.
+	{name: "Zero", argLength: 2, typ: "Mem", aux: "SizeAndAlign"}, // arg0=destptr, arg1=mem, auxint=size+alignment. Returns memory.
+
+	// Memory operations with write barriers.
+	// Expand to runtime calls. Write barrier will be removed if write on stack.
+	{name: "StoreWB", argLength: 3, typ: "Mem", aux: "Int64"},                  // Store arg1 to arg0. arg2=memory, auxint=size.  Returns memory.
+	{name: "MoveWB", argLength: 3, typ: "Mem", aux: "SymSizeAndAlign"},         // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size+alignment, aux=symbol-of-type (for typedmemmove).  Returns memory.
+	{name: "MoveWBVolatile", argLength: 3, typ: "Mem", aux: "SymSizeAndAlign"}, // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size+alignment, aux=symbol-of-type (for typedmemmove).  Returns memory. Src is volatile, i.e. needs to move to a temp space before calling typedmemmove.
+	{name: "ZeroWB", argLength: 2, typ: "Mem", aux: "SymSizeAndAlign"},         // arg0=destptr, arg1=mem, auxint=size+alignment, aux=symbol-of-type. Returns memory.
 
 	// Function calls. Arguments to the call have already been written to the stack.
 	// Return values appear on the stack. The method receiver, if any, is treated
 	// as a phantom first argument.
-	{name: "ClosureCall", argLength: 3, aux: "Int64"}, // arg0=code pointer, arg1=context ptr, arg2=memory.  auxint=arg size.  Returns memory.
-	{name: "StaticCall", argLength: 1, aux: "SymOff"}, // call function aux.(*gc.Sym), arg0=memory.  auxint=arg size.  Returns memory.
-	{name: "DeferCall", argLength: 1, aux: "Int64"},   // defer call.  arg0=memory, auxint=arg size.  Returns memory.
-	{name: "GoCall", argLength: 1, aux: "Int64"},      // go call.  arg0=memory, auxint=arg size.  Returns memory.
-	{name: "InterCall", argLength: 2, aux: "Int64"},   // interface call.  arg0=code pointer, arg1=memory, auxint=arg size.  Returns memory.
+	{name: "ClosureCall", argLength: 3, aux: "Int64", call: true}, // arg0=code pointer, arg1=context ptr, arg2=memory.  auxint=arg size.  Returns memory.
+	{name: "StaticCall", argLength: 1, aux: "SymOff", call: true}, // call function aux.(*gc.Sym), arg0=memory.  auxint=arg size.  Returns memory.
+	{name: "DeferCall", argLength: 1, aux: "Int64", call: true},   // defer call.  arg0=memory, auxint=arg size.  Returns memory.
+	{name: "GoCall", argLength: 1, aux: "Int64", call: true},      // go call.  arg0=memory, auxint=arg size.  Returns memory.
+	{name: "InterCall", argLength: 2, aux: "Int64", call: true},   // interface call.  arg0=code pointer, arg1=memory, auxint=arg size.  Returns memory.
 
 	// Conversions: signed extensions, zero (unsigned) extensions, truncations
 	{name: "SignExt8to16", argLength: 1, typ: "Int16"},
-	{name: "SignExt8to32", argLength: 1},
-	{name: "SignExt8to64", argLength: 1},
-	{name: "SignExt16to32", argLength: 1},
-	{name: "SignExt16to64", argLength: 1},
-	{name: "SignExt32to64", argLength: 1},
+	{name: "SignExt8to32", argLength: 1, typ: "Int32"},
+	{name: "SignExt8to64", argLength: 1, typ: "Int64"},
+	{name: "SignExt16to32", argLength: 1, typ: "Int32"},
+	{name: "SignExt16to64", argLength: 1, typ: "Int64"},
+	{name: "SignExt32to64", argLength: 1, typ: "Int64"},
 	{name: "ZeroExt8to16", argLength: 1, typ: "UInt16"},
-	{name: "ZeroExt8to32", argLength: 1},
-	{name: "ZeroExt8to64", argLength: 1},
-	{name: "ZeroExt16to32", argLength: 1},
-	{name: "ZeroExt16to64", argLength: 1},
-	{name: "ZeroExt32to64", argLength: 1},
+	{name: "ZeroExt8to32", argLength: 1, typ: "UInt32"},
+	{name: "ZeroExt8to64", argLength: 1, typ: "UInt64"},
+	{name: "ZeroExt16to32", argLength: 1, typ: "UInt32"},
+	{name: "ZeroExt16to64", argLength: 1, typ: "UInt64"},
+	{name: "ZeroExt32to64", argLength: 1, typ: "UInt64"},
 	{name: "Trunc16to8", argLength: 1},
 	{name: "Trunc32to8", argLength: 1},
 	{name: "Trunc32to16", argLength: 1},
@@ -359,16 +366,15 @@ var genericOps = []opData{
 	{name: "IsNonNil", argLength: 1, typ: "Bool"},        // arg0 != nil
 	{name: "IsInBounds", argLength: 2, typ: "Bool"},      // 0 <= arg0 < arg1. arg1 is guaranteed >= 0.
 	{name: "IsSliceInBounds", argLength: 2, typ: "Bool"}, // 0 <= arg0 <= arg1. arg1 is guaranteed >= 0.
-	{name: "NilCheck", argLength: 2, typ: "Void"},        // arg0=ptr, arg1=mem. Panics if arg0 is nil, returns void.
+	{name: "NilCheck", argLength: 2, typ: "Void"},        // arg0=ptr, arg1=mem. Panics if arg0 is nil. Returns void.
 
 	// Pseudo-ops
 	{name: "GetG", argLength: 1}, // runtime.getg() (read g pointer). arg0=mem
 	{name: "GetClosurePtr"},      // get closure pointer from dedicated register
 
 	// Indexing operations
-	{name: "ArrayIndex", aux: "Int64", argLength: 1}, // arg0=array, auxint=index. Returns a[i]
-	{name: "PtrIndex", argLength: 2},                 // arg0=ptr, arg1=index. Computes ptr+sizeof(*v.type)*index, where index is extended to ptrwidth type
-	{name: "OffPtr", argLength: 1, aux: "Int64"},     // arg0 + auxint (arg0 and result are pointers)
+	{name: "PtrIndex", argLength: 2},             // arg0=ptr, arg1=index. Computes ptr+sizeof(*v.type)*index, where index is extended to ptrwidth type
+	{name: "OffPtr", argLength: 1, aux: "Int64"}, // arg0 + auxint (arg0 and result are pointers)
 
 	// Slices
 	{name: "SliceMake", argLength: 3},                // arg0=ptr, arg1=len, arg2=cap
@@ -399,6 +405,11 @@ var genericOps = []opData{
 	{name: "StructMake4", argLength: 4},                // arg0..3=field0..3.  Returns struct.
 	{name: "StructSelect", argLength: 1, aux: "Int64"}, // arg0=struct, auxint=field index.  Returns the auxint'th field.
 
+	// Arrays
+	{name: "ArrayMake0"},                              // Returns array with 0 elements
+	{name: "ArrayMake1", argLength: 1},                // Returns array with 1 element
+	{name: "ArraySelect", argLength: 1, aux: "Int64"}, // arg0=array, auxint=index. Returns a[i].
+
 	// Spill&restore ops for the register allocator. These are
 	// semantically identical to OpCopy; they do not take/return
 	// stores like regular memory ops do. We can get away without memory
@@ -416,6 +427,53 @@ var genericOps = []opData{
 	{name: "VarKill", argLength: 1, aux: "Sym"},            // aux is a *gc.Node of a variable that is known to be dead.  arg0=mem, returns mem
 	{name: "VarLive", argLength: 1, aux: "Sym"},            // aux is a *gc.Node of a variable that must be kept live.  arg0=mem, returns mem
 	{name: "KeepAlive", argLength: 2, typ: "Mem"},          // arg[0] is a value that must be kept alive until this mark.  arg[1]=mem, returns mem
+
+	// Ops for breaking 64-bit operations on 32-bit architectures
+	{name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi, arg1=lo
+	{name: "Int64Hi", argLength: 1, typ: "UInt32"},   // high 32-bit of arg0
+	{name: "Int64Lo", argLength: 1, typ: "UInt32"},   // low 32-bit of arg0
+
+	{name: "Add32carry", argLength: 2, commutative: true, typ: "(UInt32,Flags)"}, // arg0 + arg1, returns (value, carry)
+	{name: "Add32withcarry", argLength: 3, commutative: true},                    // arg0 + arg1 + arg2, arg2=carry (0 or 1)
+
+	{name: "Sub32carry", argLength: 2, typ: "(UInt32,Flags)"}, // arg0 - arg1, returns (value, carry)
+	{name: "Sub32withcarry", argLength: 3},                    // arg0 - arg1 - arg2, arg2=carry (0 or 1)
+
+	{name: "Signmask", argLength: 1, typ: "Int32"},  // 0 if arg0 >= 0, -1 if arg0 < 0
+	{name: "Zeromask", argLength: 1, typ: "UInt32"}, // 0 if arg0 == 0, 0xffffffff if arg0 != 0
+	{name: "Slicemask", argLength: 1},               // 0 if arg0 == 0, -1 if arg0 > 0, undef if arg0<0. Type is native int size.
+
+	{name: "Cvt32Uto32F", argLength: 1}, // uint32 -> float32, only used on 32-bit arch
+	{name: "Cvt32Uto64F", argLength: 1}, // uint32 -> float64, only used on 32-bit arch
+	{name: "Cvt32Fto32U", argLength: 1}, // float32 -> uint32, only used on 32-bit arch
+	{name: "Cvt64Fto32U", argLength: 1}, // float64 -> uint32, only used on 32-bit arch
+	{name: "Cvt64Uto32F", argLength: 1}, // uint64 -> float32, only used on archs that has the instruction
+	{name: "Cvt64Uto64F", argLength: 1}, // uint64 -> float64, only used on archs that has the instruction
+	{name: "Cvt32Fto64U", argLength: 1}, // float32 -> uint64, only used on archs that has the instruction
+	{name: "Cvt64Fto64U", argLength: 1}, // float64 -> uint64, only used on archs that has the instruction
+
+	// pseudo-ops for breaking Tuple
+	{name: "Select0", argLength: 1}, // the first component of a tuple
+	{name: "Select1", argLength: 1}, // the second component of a tuple
+
+	// Atomic operations used for semantically inlining runtime/internal/atomic.
+	// Atomic loads return a new memory so that the loads are properly ordered
+	// with respect to other loads and stores.
+	// TODO: use for sync/atomic at some point.
+	{name: "AtomicLoad32", argLength: 2, typ: "(UInt32,Mem)"},         // Load from arg0.  arg1=memory.  Returns loaded value and new memory.
+	{name: "AtomicLoad64", argLength: 2, typ: "(UInt64,Mem)"},         // Load from arg0.  arg1=memory.  Returns loaded value and new memory.
+	{name: "AtomicLoadPtr", argLength: 2, typ: "(BytePtr,Mem)"},       // Load from arg0.  arg1=memory.  Returns loaded value and new memory.
+	{name: "AtomicStore32", argLength: 3, typ: "Mem"},                 // Store arg1 to *arg0.  arg2=memory.  Returns memory.
+	{name: "AtomicStore64", argLength: 3, typ: "Mem"},                 // Store arg1 to *arg0.  arg2=memory.  Returns memory.
+	{name: "AtomicStorePtrNoWB", argLength: 3, typ: "Mem"},            // Store arg1 to *arg0.  arg2=memory.  Returns memory.
+	{name: "AtomicExchange32", argLength: 3, typ: "(UInt32,Mem)"},     // Store arg1 to *arg0.  arg2=memory.  Returns old contents of *arg0 and new memory.
+	{name: "AtomicExchange64", argLength: 3, typ: "(UInt64,Mem)"},     // Store arg1 to *arg0.  arg2=memory.  Returns old contents of *arg0 and new memory.
+	{name: "AtomicAdd32", argLength: 3, typ: "(UInt32,Mem)"},          // Do *arg0 += arg1.  arg2=memory.  Returns sum and new memory.
+	{name: "AtomicAdd64", argLength: 3, typ: "(UInt64,Mem)"},          // Do *arg0 += arg1.  arg2=memory.  Returns sum and new memory.
+	{name: "AtomicCompareAndSwap32", argLength: 4, typ: "(Bool,Mem)"}, // if *arg0==arg1, then set *arg0=arg2.  Returns true iff store happens and new memory.
+	{name: "AtomicCompareAndSwap64", argLength: 4, typ: "(Bool,Mem)"}, // if *arg0==arg1, then set *arg0=arg2.  Returns true iff store happens and new memory.
+	{name: "AtomicAnd8", argLength: 3, typ: "Mem"},                    // *arg0 &= arg1.  arg2=memory.  Returns memory.
+	{name: "AtomicOr8", argLength: 3, typ: "Mem"},                     // *arg0 |= arg1.  arg2=memory.  Returns memory.
 }
 
 //     kind           control    successors       implicit exit
@@ -432,9 +490,7 @@ var genericOps = []opData{
 var genericBlocks = []blockData{
 	{name: "Plain"},  // a single successor
 	{name: "If"},     // 2 successors, if control goto Succs[0] else goto Succs[1]
-	{name: "Call"},   // 1 successor, control is call op (of memory type)
 	{name: "Defer"},  // 2 successors, Succs[0]=defer queued, Succs[1]=defer recovered. control is call op (of memory type)
-	{name: "Check"},  // 1 successor, control is nilcheck op (of void type)
 	{name: "Ret"},    // no successors, control value is memory result
 	{name: "RetJmp"}, // no successors, jumps to b.Aux.(*gc.Sym)
 	{name: "Exit"},   // no successors, control value generates a panic
diff --git a/src/cmd/compile/internal/ssa/gen/main.go b/src/cmd/compile/internal/ssa/gen/main.go
index 2aec4a3..41199f7 100644
--- a/src/cmd/compile/internal/ssa/gen/main.go
+++ b/src/cmd/compile/internal/ssa/gen/main.go
@@ -21,13 +21,18 @@ import (
 )
 
 type arch struct {
-	name     string
-	pkg      string // obj package to import for this arch.
-	genfile  string // source file containing opcode code generation.
-	ops      []opData
-	blocks   []blockData
-	regnames []string
-	generic  bool
+	name            string
+	pkg             string // obj package to import for this arch.
+	genfile         string // source file containing opcode code generation.
+	ops             []opData
+	blocks          []blockData
+	regnames        []string
+	gpregmask       regMask
+	fpregmask       regMask
+	specialregmask  regMask
+	framepointerreg int8
+	linkreg         int8
+	generic         bool
 }
 
 type opData struct {
@@ -38,8 +43,15 @@ type opData struct {
 	aux               string
 	rematerializeable bool
 	argLength         int32 // number of arguments, if -1, then this operation has a variable number of arguments
-	commutative       bool  // this operation is commutative (e.g. addition)
-	resultInArg0      bool  // v and v.Args[0] must be allocated to the same register
+	commutative       bool  // this operation is commutative on its first 2 arguments (e.g. addition)
+	resultInArg0      bool  // (first, if a tuple) output of v and v.Args[0] must be allocated to the same register
+	resultNotInArgs   bool  // outputs must not be allocated to the same registers as inputs
+	clobberFlags      bool  // this op clobbers flags register
+	call              bool  // is a function call
+	nilCheck          bool  // this op is a nil check on arg0
+	faultOnNilArg0    bool  // this op will fault if arg0 is nil (and aux encodes a small offset)
+	faultOnNilArg1    bool  // this op will fault if arg1 is nil (and aux encodes a small offset)
+	usesScratch       bool  // this op requires scratch memory space
 }
 
 type blockData struct {
@@ -73,6 +85,7 @@ var archs []arch
 
 func main() {
 	flag.Parse()
+	sort.Sort(ArchsByName(archs))
 	genOp()
 	genLower()
 }
@@ -118,10 +131,13 @@ func genOp() {
 
 	// generate Op* declarations
 	fmt.Fprintln(w, "const (")
-	fmt.Fprintln(w, "OpInvalid Op = iota")
+	fmt.Fprintln(w, "OpInvalid Op = iota") // make sure OpInvalid is 0.
 	for _, a := range archs {
 		fmt.Fprintln(w)
 		for _, v := range a.ops {
+			if v.name == "Invalid" {
+				continue
+			}
 			fmt.Fprintf(w, "Op%s%s\n", a.Name(), v.name)
 		}
 	}
@@ -135,6 +151,9 @@ func genOp() {
 
 		pkg := path.Base(a.pkg)
 		for _, v := range a.ops {
+			if v.name == "Invalid" {
+				continue
+			}
 			fmt.Fprintln(w, "{")
 			fmt.Fprintf(w, "name:\"%s\",\n", v.name)
 
@@ -156,12 +175,39 @@ func genOp() {
 			if v.resultInArg0 {
 				fmt.Fprintln(w, "resultInArg0: true,")
 				if v.reg.inputs[0] != v.reg.outputs[0] {
-					log.Fatalf("input[0] and output registers must be equal for %s", v.name)
+					log.Fatalf("input[0] and output[0] must use the same registers for %s", v.name)
 				}
 				if v.commutative && v.reg.inputs[1] != v.reg.outputs[0] {
-					log.Fatalf("input[1] and output registers must be equal for %s", v.name)
+					log.Fatalf("input[1] and output[0] must use the same registers for %s", v.name)
+				}
+			}
+			if v.resultNotInArgs {
+				fmt.Fprintln(w, "resultNotInArgs: true,")
+			}
+			if v.clobberFlags {
+				fmt.Fprintln(w, "clobberFlags: true,")
+			}
+			if v.call {
+				fmt.Fprintln(w, "call: true,")
+			}
+			if v.nilCheck {
+				fmt.Fprintln(w, "nilCheck: true,")
+			}
+			if v.faultOnNilArg0 {
+				fmt.Fprintln(w, "faultOnNilArg0: true,")
+				if v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" {
+					log.Fatalf("faultOnNilArg0 with aux %s not allowed", v.aux)
+				}
+			}
+			if v.faultOnNilArg1 {
+				fmt.Fprintln(w, "faultOnNilArg1: true,")
+				if v.aux != "SymOff" && v.aux != "SymValAndOff" && v.aux != "Int64" && v.aux != "Int32" && v.aux != "" {
+					log.Fatalf("faultOnNilArg1 with aux %s not allowed", v.aux)
 				}
 			}
+			if v.usesScratch {
+				fmt.Fprintln(w, "usesScratch: true,")
+			}
 			if a.name == "generic" {
 				fmt.Fprintln(w, "generic:true,")
 				fmt.Fprintln(w, "},") // close op
@@ -191,14 +237,22 @@ func genOp() {
 				}
 				fmt.Fprintln(w, "},")
 			}
+
 			if v.reg.clobbers > 0 {
 				fmt.Fprintf(w, "clobbers: %d,%s\n", v.reg.clobbers, a.regMaskComment(v.reg.clobbers))
 			}
+
 			// reg outputs
-			if len(v.reg.outputs) > 0 {
-				fmt.Fprintln(w, "outputs: []regMask{")
-				for _, r := range v.reg.outputs {
-					fmt.Fprintf(w, "%d,%s\n", r, a.regMaskComment(r))
+			s = s[:0]
+			for i, r := range v.reg.outputs {
+				s = append(s, intPair{countRegs(r), i})
+			}
+			if len(s) > 0 {
+				sort.Sort(byKey(s))
+				fmt.Fprintln(w, "outputs: []outputInfo{")
+				for _, p := range s {
+					r := v.reg.outputs[p.val]
+					fmt.Fprintf(w, "{%d,%d},%s\n", p.val, r, a.regMaskComment(r))
 				}
 				fmt.Fprintln(w, "},")
 			}
@@ -213,6 +267,8 @@ func genOp() {
 	// generate op string method
 	fmt.Fprintln(w, "func (o Op) String() string {return opcodeTable[o].name }")
 
+	fmt.Fprintln(w, "func (o Op) UsesScratch() bool { return opcodeTable[o].usesScratch }")
+
 	// generate registers
 	for _, a := range archs {
 		if a.generic {
@@ -220,9 +276,27 @@ func genOp() {
 		}
 		fmt.Fprintf(w, "var registers%s = [...]Register {\n", a.name)
 		for i, r := range a.regnames {
-			fmt.Fprintf(w, "  {%d, \"%s\"},\n", i, r)
+			pkg := a.pkg[len("cmd/internal/obj/"):]
+			var objname string // name in cmd/internal/obj/$ARCH
+			switch r {
+			case "SB":
+				// SB isn't a real register.  cmd/internal/obj expects 0 in this case.
+				objname = "0"
+			case "SP":
+				objname = pkg + ".REGSP"
+			case "g":
+				objname = pkg + ".REGG"
+			default:
+				objname = pkg + ".REG_" + r
+			}
+			fmt.Fprintf(w, "  {%d, %s, \"%s\"},\n", i, objname, r)
 		}
 		fmt.Fprintln(w, "}")
+		fmt.Fprintf(w, "var gpRegMask%s = regMask(%d)\n", a.name, a.gpregmask)
+		fmt.Fprintf(w, "var fpRegMask%s = regMask(%d)\n", a.name, a.fpregmask)
+		fmt.Fprintf(w, "var specialRegMask%s = regMask(%d)\n", a.name, a.specialregmask)
+		fmt.Fprintf(w, "var framepointerReg%s = int8(%d)\n", a.name, a.framepointerreg)
+		fmt.Fprintf(w, "var linkReg%s = int8(%d)\n", a.name, a.linkreg)
 	}
 
 	// gofmt result
@@ -298,3 +372,9 @@ type byKey []intPair
 func (a byKey) Len() int           { return len(a) }
 func (a byKey) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 func (a byKey) Less(i, j int) bool { return a[i].key < a[j].key }
+
+type ArchsByName []arch
+
+func (x ArchsByName) Len() int           { return len(x) }
+func (x ArchsByName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
+func (x ArchsByName) Less(i, j int) bool { return x[i].name < x[j].name }
diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go
index 0fc5749..f255f6b 100644
--- a/src/cmd/compile/internal/ssa/gen/rulegen.go
+++ b/src/cmd/compile/internal/ssa/gen/rulegen.go
@@ -117,15 +117,17 @@ func genRules(arch arch) {
 		if unbalanced(rule) {
 			continue
 		}
-		op := strings.Split(rule, " ")[0][1:]
-		if op[len(op)-1] == ')' {
-			op = op[:len(op)-1] // rule has only opcode, e.g. (ConstNil) -> ...
-		}
+
 		loc := fmt.Sprintf("%s.rules:%d", arch.name, ruleLineno)
-		if isBlock(op, arch) {
-			blockrules[op] = append(blockrules[op], Rule{rule: rule, loc: loc})
+		r := Rule{rule: rule, loc: loc}
+		if rawop := strings.Split(rule, " ")[0][1:]; isBlock(rawop, arch) {
+			blockrules[rawop] = append(blockrules[rawop], r)
 		} else {
-			oprules[op] = append(oprules[op], Rule{rule: rule, loc: loc})
+			// Do fancier value op matching.
+			match, _, _ := r.parse()
+			op, oparch, _, _, _, _ := parseValue(match, arch, loc)
+			opname := fmt.Sprintf("Op%s%s", oparch, op.name)
+			oprules[opname] = append(oprules[opname], r)
 		}
 		rule = ""
 		ruleLineno = 0
@@ -157,8 +159,8 @@ func genRules(arch arch) {
 	fmt.Fprintf(w, "func rewriteValue%s(v *Value, config *Config) bool {\n", arch.name)
 	fmt.Fprintf(w, "switch v.Op {\n")
 	for _, op := range ops {
-		fmt.Fprintf(w, "case %s:\n", opName(op, arch))
-		fmt.Fprintf(w, "return rewriteValue%s_%s(v, config)\n", arch.name, opName(op, arch))
+		fmt.Fprintf(w, "case %s:\n", op)
+		fmt.Fprintf(w, "return rewriteValue%s_%s(v, config)\n", arch.name, op)
 	}
 	fmt.Fprintf(w, "}\n")
 	fmt.Fprintf(w, "return false\n")
@@ -167,7 +169,7 @@ func genRules(arch arch) {
 	// Generate a routine per op. Note that we don't make one giant routine
 	// because it is too big for some compilers.
 	for _, op := range ops {
-		fmt.Fprintf(w, "func rewriteValue%s_%s(v *Value, config *Config) bool {\n", arch.name, opName(op, arch))
+		fmt.Fprintf(w, "func rewriteValue%s_%s(v *Value, config *Config) bool {\n", arch.name, op)
 		fmt.Fprintln(w, "b := v.Block")
 		fmt.Fprintln(w, "_ = b")
 		var canFail bool
@@ -207,7 +209,7 @@ func genRules(arch arch) {
 
 	// Generate block rewrite function. There are only a few block types
 	// so we can make this one function with a switch.
-	fmt.Fprintf(w, "func rewriteBlock%s(b *Block) bool {\n", arch.name)
+	fmt.Fprintf(w, "func rewriteBlock%s(b *Block, config *Config) bool {\n", arch.name)
 	fmt.Fprintf(w, "switch b.Kind {\n")
 	ops = nil
 	for op := range blockrules {
@@ -232,6 +234,7 @@ func genRules(arch arch) {
 				if strings.Contains(s[1], "(") {
 					genMatch0(w, arch, s[1], "v", map[string]struct{}{}, false, rule.loc)
 				} else {
+					fmt.Fprintf(w, "_ = v\n") // in case we don't use v
 					fmt.Fprintf(w, "%s := b.Control\n", s[1])
 				}
 			}
@@ -334,141 +337,108 @@ func genMatch0(w io.Writer, arch arch, match, v string, m map[string]struct{}, t
 	}
 	canFail := false
 
-	// split body up into regions. Split by spaces/tabs, except those
-	// contained in () or {}.
-	s := split(match[1 : len(match)-1]) // remove parens, then split
-
-	// Find op record
-	var op opData
-	for _, x := range genericOps {
-		if x.name == s[0] {
-			op = x
-			break
-		}
-	}
-	for _, x := range arch.ops {
-		if x.name == s[0] {
-			op = x
-			break
-		}
-	}
-	if op.name == "" {
-		log.Fatalf("%s: unknown op %s", loc, s[0])
-	}
+	op, oparch, typ, auxint, aux, args := parseValue(match, arch, loc)
 
 	// check op
 	if !top {
-		fmt.Fprintf(w, "if %s.Op != %s {\nbreak\n}\n", v, opName(s[0], arch))
+		fmt.Fprintf(w, "if %s.Op != Op%s%s {\nbreak\n}\n", v, oparch, op.name)
 		canFail = true
 	}
 
-	// check type/aux/args
-	argnum := 0
-	for _, a := range s[1:] {
-		if a[0] == '<' {
-			// type restriction
-			t := a[1 : len(a)-1] // remove <>
-			if !isVariable(t) {
-				// code. We must match the results of this code.
-				fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, t)
+	if typ != "" {
+		if !isVariable(typ) {
+			// code. We must match the results of this code.
+			fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, typ)
+			canFail = true
+		} else {
+			// variable
+			if _, ok := m[typ]; ok {
+				// must match previous variable
+				fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, typ)
 				canFail = true
 			} else {
-				// variable
-				if _, ok := m[t]; ok {
-					// must match previous variable
-					fmt.Fprintf(w, "if %s.Type != %s {\nbreak\n}\n", v, t)
-					canFail = true
-				} else {
-					m[t] = struct{}{}
-					fmt.Fprintf(w, "%s := %s.Type\n", t, v)
-				}
+				m[typ] = struct{}{}
+				fmt.Fprintf(w, "%s := %s.Type\n", typ, v)
 			}
-		} else if a[0] == '[' {
-			// auxint restriction
-			switch op.aux {
-			case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32":
-			default:
-				log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux)
-			}
-			x := a[1 : len(a)-1] // remove []
-			if !isVariable(x) {
-				// code
-				fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, x)
+		}
+	}
+
+	if auxint != "" {
+		if !isVariable(auxint) {
+			// code
+			fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, auxint)
+			canFail = true
+		} else {
+			// variable
+			if _, ok := m[auxint]; ok {
+				fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, auxint)
 				canFail = true
 			} else {
-				// variable
-				if _, ok := m[x]; ok {
-					fmt.Fprintf(w, "if %s.AuxInt != %s {\nbreak\n}\n", v, x)
-					canFail = true
-				} else {
-					m[x] = struct{}{}
-					fmt.Fprintf(w, "%s := %s.AuxInt\n", x, v)
-				}
-			}
-		} else if a[0] == '{' {
-			// aux restriction
-			switch op.aux {
-			case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32":
-			default:
-				log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux)
+				m[auxint] = struct{}{}
+				fmt.Fprintf(w, "%s := %s.AuxInt\n", auxint, v)
 			}
-			x := a[1 : len(a)-1] // remove {}
-			if !isVariable(x) {
-				// code
-				fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, x)
+		}
+	}
+
+	if aux != "" {
+
+		if !isVariable(aux) {
+			// code
+			fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, aux)
+			canFail = true
+		} else {
+			// variable
+			if _, ok := m[aux]; ok {
+				fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, aux)
 				canFail = true
 			} else {
-				// variable
-				if _, ok := m[x]; ok {
-					fmt.Fprintf(w, "if %s.Aux != %s {\nbreak\n}\n", v, x)
-					canFail = true
-				} else {
-					m[x] = struct{}{}
-					fmt.Fprintf(w, "%s := %s.Aux\n", x, v)
-				}
+				m[aux] = struct{}{}
+				fmt.Fprintf(w, "%s := %s.Aux\n", aux, v)
 			}
-		} else if a == "_" {
-			argnum++
-		} else if !strings.Contains(a, "(") {
+		}
+	}
+
+	for i, arg := range args {
+		if arg == "_" {
+			continue
+		}
+		if !strings.Contains(arg, "(") {
 			// leaf variable
-			if _, ok := m[a]; ok {
+			if _, ok := m[arg]; ok {
 				// variable already has a definition. Check whether
 				// the old definition and the new definition match.
 				// For example, (add x x).  Equality is just pointer equality
 				// on Values (so cse is important to do before lowering).
-				fmt.Fprintf(w, "if %s != %s.Args[%d] {\nbreak\n}\n", a, v, argnum)
+				fmt.Fprintf(w, "if %s != %s.Args[%d] {\nbreak\n}\n", arg, v, i)
 				canFail = true
 			} else {
 				// remember that this variable references the given value
-				m[a] = struct{}{}
-				fmt.Fprintf(w, "%s := %s.Args[%d]\n", a, v, argnum)
+				m[arg] = struct{}{}
+				fmt.Fprintf(w, "%s := %s.Args[%d]\n", arg, v, i)
 			}
-			argnum++
+			continue
+		}
+		// compound sexpr
+		var argname string
+		colon := strings.Index(arg, ":")
+		openparen := strings.Index(arg, "(")
+		if colon >= 0 && openparen >= 0 && colon < openparen {
+			// rule-specified name
+			argname = arg[:colon]
+			arg = arg[colon+1:]
 		} else {
-			// compound sexpr
-			var argname string
-			colon := strings.Index(a, ":")
-			openparen := strings.Index(a, "(")
-			if colon >= 0 && openparen >= 0 && colon < openparen {
-				// rule-specified name
-				argname = a[:colon]
-				a = a[colon+1:]
-			} else {
-				// autogenerated name
-				argname = fmt.Sprintf("%s_%d", v, argnum)
-			}
-			fmt.Fprintf(w, "%s := %s.Args[%d]\n", argname, v, argnum)
-			if genMatch0(w, arch, a, argname, m, false, loc) {
-				canFail = true
-			}
-			argnum++
+			// autogenerated name
+			argname = fmt.Sprintf("%s_%d", v, i)
+		}
+		fmt.Fprintf(w, "%s := %s.Args[%d]\n", argname, v, i)
+		if genMatch0(w, arch, arg, argname, m, false, loc) {
+			canFail = true
 		}
 	}
+
 	if op.argLength == -1 {
-		fmt.Fprintf(w, "if len(%s.Args) != %d {\nbreak\n}\n", v, argnum)
+		fmt.Fprintf(w, "if len(%s.Args) != %d {\nbreak\n}\n", v, len(args))
 		canFail = true
-	} else if int(op.argLength) != argnum {
-		log.Fatalf("%s: op %s should have %d args, has %d", loc, op.name, op.argLength, argnum)
 	}
 	return canFail
 }
@@ -500,105 +470,44 @@ func genResult0(w io.Writer, arch arch, result string, alloc *int, top, move boo
 		return result
 	}
 
-	s := split(result[1 : len(result)-1]) // remove parens, then split
-
-	// Find op record
-	var op opData
-	for _, x := range genericOps {
-		if x.name == s[0] {
-			op = x
-			break
-		}
-	}
-	for _, x := range arch.ops {
-		if x.name == s[0] {
-			op = x
-			break
-		}
-	}
-	if op.name == "" {
-		log.Fatalf("%s: unknown op %s", loc, s[0])
-	}
+	op, oparch, typ, auxint, aux, args := parseValue(result, arch, loc)
 
 	// Find the type of the variable.
-	var opType string
-	var typeOverride bool
-	for _, a := range s[1:] {
-		if a[0] == '<' {
-			// type restriction
-			opType = a[1 : len(a)-1] // remove <>
-			typeOverride = true
-			break
-		}
-	}
-	if opType == "" {
-		// find default type, if any
-		for _, op := range arch.ops {
-			if op.name == s[0] && op.typ != "" {
-				opType = typeName(op.typ)
-				break
-			}
-		}
-	}
-	if opType == "" {
-		for _, op := range genericOps {
-			if op.name == s[0] && op.typ != "" {
-				opType = typeName(op.typ)
-				break
-			}
-		}
+	typeOverride := typ != ""
+	if typ == "" && op.typ != "" {
+		typ = typeName(op.typ)
 	}
+
 	var v string
 	if top && !move {
 		v = "v"
-		fmt.Fprintf(w, "v.reset(%s)\n", opName(s[0], arch))
+		fmt.Fprintf(w, "v.reset(Op%s%s)\n", oparch, op.name)
 		if typeOverride {
-			fmt.Fprintf(w, "v.Type = %s\n", opType)
+			fmt.Fprintf(w, "v.Type = %s\n", typ)
 		}
 	} else {
-		if opType == "" {
-			log.Fatalf("sub-expression %s (op=%s) must have a type", result, s[0])
+		if typ == "" {
+			log.Fatalf("sub-expression %s (op=Op%s%s) must have a type", result, oparch, op.name)
 		}
 		v = fmt.Sprintf("v%d", *alloc)
 		*alloc++
-		fmt.Fprintf(w, "%s := b.NewValue0(v.Line, %s, %s)\n", v, opName(s[0], arch), opType)
+		fmt.Fprintf(w, "%s := b.NewValue0(v.Line, Op%s%s, %s)\n", v, oparch, op.name, typ)
 		if move && top {
 			// Rewrite original into a copy
 			fmt.Fprintf(w, "v.reset(OpCopy)\n")
 			fmt.Fprintf(w, "v.AddArg(%s)\n", v)
 		}
 	}
-	argnum := 0
-	for _, a := range s[1:] {
-		if a[0] == '<' {
-			// type restriction, handled above
-		} else if a[0] == '[' {
-			// auxint restriction
-			switch op.aux {
-			case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32":
-			default:
-				log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux)
-			}
-			x := a[1 : len(a)-1] // remove []
-			fmt.Fprintf(w, "%s.AuxInt = %s\n", v, x)
-		} else if a[0] == '{' {
-			// aux restriction
-			switch op.aux {
-			case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32":
-			default:
-				log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux)
-			}
-			x := a[1 : len(a)-1] // remove {}
-			fmt.Fprintf(w, "%s.Aux = %s\n", v, x)
-		} else {
-			// regular argument (sexpr or variable)
-			x := genResult0(w, arch, a, alloc, false, move, loc)
-			fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x)
-			argnum++
-		}
+
+	if auxint != "" {
+		fmt.Fprintf(w, "%s.AuxInt = %s\n", v, auxint)
 	}
-	if op.argLength != -1 && int(op.argLength) != argnum {
-		log.Fatalf("%s: op %s should have %d args, has %d", loc, op.name, op.argLength, argnum)
+	if aux != "" {
+		fmt.Fprintf(w, "%s.Aux = %s\n", v, aux)
+	}
+	for _, arg := range args {
+		x := genResult0(w, arch, arg, alloc, false, move, loc)
+		fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x)
 	}
 
 	return v
@@ -666,16 +575,102 @@ func isBlock(name string, arch arch) bool {
 	return false
 }
 
-// opName converts from an op name specified in a rule file to an Op enum.
-// if the name matches a generic op, returns "Op" plus the specified name.
-// Otherwise, returns "Op" plus arch name plus op name.
-func opName(name string, arch arch) string {
-	for _, op := range genericOps {
-		if op.name == name {
-			return "Op" + name
+// parseValue parses a parenthesized value from a rule.
+// The value can be from the match or the result side.
+// It returns the op and unparsed strings for typ, auxint, and aux restrictions and for all args.
+// oparch is the architecture that op is located in, or "" for generic.
+func parseValue(val string, arch arch, loc string) (op opData, oparch string, typ string, auxint string, aux string, args []string) {
+	val = val[1 : len(val)-1] // remove ()
+
+	// Split val up into regions.
+	// Split by spaces/tabs, except those contained in (), {}, [], or <>.
+	s := split(val)
+
+	// Extract restrictions and args.
+	for _, a := range s[1:] {
+		switch a[0] {
+		case '<':
+			typ = a[1 : len(a)-1] // remove <>
+		case '[':
+			auxint = a[1 : len(a)-1] // remove []
+		case '{':
+			aux = a[1 : len(a)-1] // remove {}
+		default:
+			args = append(args, a)
+		}
+	}
+
+	// Resolve the op.
+
+	// match reports whether x is a good op to select.
+	// If strict is true, rule generation might succeed.
+	// If strict is false, rule generation has failed,
+	// but we're trying to generate a useful error.
+	// Doing strict=true then strict=false allows
+	// precise op matching while retaining good error messages.
+	match := func(x opData, strict bool, archname string) bool {
+		if x.name != s[0] {
+			return false
+		}
+		if x.argLength != -1 && int(x.argLength) != len(args) {
+			if strict {
+				return false
+			} else {
+				log.Printf("%s: op %s (%s) should have %d args, has %d", loc, s[0], archname, x.argLength, len(args))
+			}
+		}
+		return true
+	}
+
+	for _, x := range genericOps {
+		if match(x, true, "generic") {
+			op = x
+			break
+		}
+	}
+	if arch.name != "generic" {
+		for _, x := range arch.ops {
+			if match(x, true, arch.name) {
+				if op.name != "" {
+					log.Fatalf("%s: matches for op %s found in both generic and %s", loc, op.name, arch.name)
+				}
+				op = x
+				oparch = arch.name
+				break
+			}
 		}
 	}
-	return "Op" + arch.name + name
+
+	if op.name == "" {
+		// Failed to find the op.
+		// Run through everything again with strict=false
+		// to generate useful diagnosic messages before failing.
+		for _, x := range genericOps {
+			match(x, false, "generic")
+		}
+		for _, x := range arch.ops {
+			match(x, false, arch.name)
+		}
+		log.Fatalf("%s: unknown op %s", loc, s)
+	}
+
+	// Sanity check aux, auxint.
+	if auxint != "" {
+		switch op.aux {
+		case "Bool", "Int8", "Int16", "Int32", "Int64", "Int128", "Float32", "Float64", "SymOff", "SymValAndOff", "SymInt32", "SizeAndAlign":
+		default:
+			log.Fatalf("%s: op %s %s can't have auxint", loc, op.name, op.aux)
+		}
+	}
+	if aux != "" {
+		switch op.aux {
+		case "String", "Sym", "SymOff", "SymValAndOff", "SymInt32":
+		default:
+			log.Fatalf("%s: op %s %s can't have aux", loc, op.name, op.aux)
+		}
+	}
+
+	return
 }
 
 func blockName(name string, arch arch) string {
@@ -689,6 +684,13 @@ func blockName(name string, arch arch) string {
 
 // typeName returns the string to use to generate a type.
 func typeName(typ string) string {
+	if typ[0] == '(' {
+		ts := strings.Split(typ[1:len(typ)-1], ",")
+		if len(ts) != 2 {
+			panic("Tuple expect 2 arguments")
+		}
+		return "MakeTuple(" + typeName(ts[0]) + ", " + typeName(ts[1]) + ")"
+	}
 	switch typ {
 	case "Flags", "Mem", "Void", "Int128":
 		return "Type" + typ
diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go
index 7432a07..316fd2a 100644
--- a/src/cmd/compile/internal/ssa/html.go
+++ b/src/cmd/compile/internal/ssa/html.go
@@ -274,9 +274,10 @@ function toggle_visibility(id) {
 <div id="help">
 
 <p>
-Click on a value or block to toggle highlighting of that value/block and its uses.
-Values and blocks are highlighted by ID, which may vary across passes.
-(TODO: Fix this.)
+Click on a value or block to toggle highlighting of that value/block
+and its uses.  (Values and blocks are highlighted by ID, and IDs of
+dead items may be reused, so not all highlights necessarily correspond
+to the clicked item.)
 </p>
 
 <p>
@@ -341,7 +342,8 @@ func (v *Value) HTML() string {
 	// TODO: Using the value ID as the class ignores the fact
 	// that value IDs get recycled and that some values
 	// are transmuted into other values.
-	return fmt.Sprintf("<span class=\"%[1]s ssa-value\">%[1]s</span>", v.String())
+	s := v.String()
+	return fmt.Sprintf("<span class=\"%s ssa-value\">%s</span>", s, s)
 }
 
 func (v *Value) LongHTML() string {
@@ -359,7 +361,7 @@ func (v *Value) LongHTML() string {
 	}
 	r := v.Block.Func.RegAlloc
 	if int(v.ID) < len(r) && r[v.ID] != nil {
-		s += " : " + r[v.ID].Name()
+		s += " : " + html.EscapeString(r[v.ID].Name())
 	}
 	s += "</span>"
 	return s
@@ -369,7 +371,8 @@ func (b *Block) HTML() string {
 	// TODO: Using the value ID as the class ignores the fact
 	// that value IDs get recycled and that some values
 	// are transmuted into other values.
-	return fmt.Sprintf("<span class=\"%[1]s ssa-block\">%[1]s</span>", html.EscapeString(b.String()))
+	s := html.EscapeString(b.String())
+	return fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", s, s)
 }
 
 func (b *Block) LongHTML() string {
diff --git a/src/cmd/compile/internal/ssa/lca.go b/src/cmd/compile/internal/ssa/lca.go
new file mode 100644
index 0000000..b9731fa
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/lca.go
@@ -0,0 +1,123 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssa
+
+// Code to compute lowest common ancestors in the dominator tree.
+// https://en.wikipedia.org/wiki/Lowest_common_ancestor
+// https://en.wikipedia.org/wiki/Range_minimum_query#Solution_using_constant_time_and_linearithmic_space
+
+// lcaRange is a data structure that can compute lowest common ancestor queries
+// in O(n lg n) precomputed space and O(1) time per query.
+type lcaRange struct {
+	// Additional information about each block (indexed by block ID).
+	blocks []lcaRangeBlock
+
+	// Data structure for range minimum queries.
+	// rangeMin[k][i] contains the ID of the minimum depth block
+	// in the Euler tour from positions i to i+1<<k-1, inclusive.
+	rangeMin [][]ID
+}
+
+type lcaRangeBlock struct {
+	b          *Block
+	parent     ID    // parent in dominator tree.  0 = no parent (entry or unreachable)
+	firstChild ID    // first child in dominator tree
+	sibling    ID    // next child of parent
+	pos        int32 // an index in the Euler tour where this block appears (any one of its occurrences)
+	depth      int32 // depth in dominator tree (root=0, its children=1, etc.)
+}
+
+func makeLCArange(f *Func) *lcaRange {
+	dom := f.Idom()
+
+	// Build tree
+	blocks := make([]lcaRangeBlock, f.NumBlocks())
+	for _, b := range f.Blocks {
+		blocks[b.ID].b = b
+		if dom[b.ID] == nil {
+			continue // entry or unreachable
+		}
+		parent := dom[b.ID].ID
+		blocks[b.ID].parent = parent
+		blocks[b.ID].sibling = blocks[parent].firstChild
+		blocks[parent].firstChild = b.ID
+	}
+
+	// Compute euler tour ordering.
+	// Each reachable block will appear #children+1 times in the tour.
+	tour := make([]ID, 0, f.NumBlocks()*2-1)
+	type queueEntry struct {
+		bid ID // block to work on
+		cid ID // child we're already working on (0 = haven't started yet)
+	}
+	q := []queueEntry{{f.Entry.ID, 0}}
+	for len(q) > 0 {
+		n := len(q) - 1
+		bid := q[n].bid
+		cid := q[n].cid
+		q = q[:n]
+
+		// Add block to tour.
+		blocks[bid].pos = int32(len(tour))
+		tour = append(tour, bid)
+
+		// Proceed down next child edge (if any).
+		if cid == 0 {
+			// This is our first visit to b. Set its depth.
+			blocks[bid].depth = blocks[blocks[bid].parent].depth + 1
+			// Then explore its first child.
+			cid = blocks[bid].firstChild
+		} else {
+			// We've seen b before. Explore the next child.
+			cid = blocks[cid].sibling
+		}
+		if cid != 0 {
+			q = append(q, queueEntry{bid, cid}, queueEntry{cid, 0})
+		}
+	}
+
+	// Compute fast range-minimum query data structure
+	var rangeMin [][]ID
+	rangeMin = append(rangeMin, tour) // 1-size windows are just the tour itself.
+	for logS, s := 1, 2; s < len(tour); logS, s = logS+1, s*2 {
+		r := make([]ID, len(tour)-s+1)
+		for i := 0; i < len(tour)-s+1; i++ {
+			bid := rangeMin[logS-1][i]
+			bid2 := rangeMin[logS-1][i+s/2]
+			if blocks[bid2].depth < blocks[bid].depth {
+				bid = bid2
+			}
+			r[i] = bid
+		}
+		rangeMin = append(rangeMin, r)
+	}
+
+	return &lcaRange{blocks: blocks, rangeMin: rangeMin}
+}
+
+// find returns the lowest common ancestor of a and b.
+func (lca *lcaRange) find(a, b *Block) *Block {
+	if a == b {
+		return a
+	}
+	// Find the positions of a and bin the Euler tour.
+	p1 := lca.blocks[a.ID].pos
+	p2 := lca.blocks[b.ID].pos
+	if p1 > p2 {
+		p1, p2 = p2, p1
+	}
+
+	// The lowest common ancestor is the minimum depth block
+	// on the tour from p1 to p2.  We've precomputed minimum
+	// depth blocks for powers-of-two subsequences of the tour.
+	// Combine the right two precomputed values to get the answer.
+	logS := uint(log2(int64(p2 - p1)))
+	bid1 := lca.rangeMin[logS][p1]
+	bid2 := lca.rangeMin[logS][p2-1<<logS+1]
+	if lca.blocks[bid1].depth < lca.blocks[bid2].depth {
+		return lca.blocks[bid1].b
+	}
+	return lca.blocks[bid2].b
+}
diff --git a/src/cmd/compile/internal/ssa/lca_test.go b/src/cmd/compile/internal/ssa/lca_test.go
new file mode 100644
index 0000000..beb33e0
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/lca_test.go
@@ -0,0 +1,103 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssa
+
+import "testing"
+
+type lca interface {
+	find(a, b *Block) *Block
+}
+
+func lcaEqual(f *Func, lca1, lca2 lca) bool {
+	for _, b := range f.Blocks {
+		for _, c := range f.Blocks {
+			if lca1.find(b, c) != lca2.find(b, c) {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+func testLCAgen(t *testing.T, bg blockGen, size int) {
+	c := NewConfig("amd64", DummyFrontend{t}, nil, true)
+	fun := Fun(c, "entry", bg(size)...)
+	CheckFunc(fun.f)
+	if size == 4 {
+		t.Logf(fun.f.String())
+	}
+	lca1 := makeLCArange(fun.f)
+	lca2 := makeLCAeasy(fun.f)
+	for _, b := range fun.f.Blocks {
+		for _, c := range fun.f.Blocks {
+			l1 := lca1.find(b, c)
+			l2 := lca2.find(b, c)
+			if l1 != l2 {
+				t.Errorf("lca(%s,%s)=%s, want %s", b, c, l1, l2)
+			}
+		}
+	}
+}
+
+func TestLCALinear(t *testing.T) {
+	testLCAgen(t, genLinear, 10)
+	testLCAgen(t, genLinear, 100)
+}
+
+func TestLCAFwdBack(t *testing.T) {
+	testLCAgen(t, genFwdBack, 10)
+	testLCAgen(t, genFwdBack, 100)
+}
+
+func TestLCAManyPred(t *testing.T) {
+	testLCAgen(t, genManyPred, 10)
+	testLCAgen(t, genManyPred, 100)
+}
+
+func TestLCAMaxPred(t *testing.T) {
+	testLCAgen(t, genMaxPred, 10)
+	testLCAgen(t, genMaxPred, 100)
+}
+
+func TestLCAMaxPredValue(t *testing.T) {
+	testLCAgen(t, genMaxPredValue, 10)
+	testLCAgen(t, genMaxPredValue, 100)
+}
+
+// Simple implementation of LCA to compare against.
+type lcaEasy struct {
+	parent []*Block
+}
+
+func makeLCAeasy(f *Func) *lcaEasy {
+	return &lcaEasy{parent: dominators(f)}
+}
+
+func (lca *lcaEasy) find(a, b *Block) *Block {
+	da := lca.depth(a)
+	db := lca.depth(b)
+	for da > db {
+		da--
+		a = lca.parent[a.ID]
+	}
+	for da < db {
+		db--
+		b = lca.parent[b.ID]
+	}
+	for a != b {
+		a = lca.parent[a.ID]
+		b = lca.parent[b.ID]
+	}
+	return a
+}
+
+func (lca *lcaEasy) depth(b *Block) int {
+	n := 0
+	for b != nil {
+		b = lca.parent[b.ID]
+		n++
+	}
+	return n
+}
diff --git a/src/cmd/compile/internal/ssa/likelyadjust.go b/src/cmd/compile/internal/ssa/likelyadjust.go
index cb2d82f..38a5e81 100644
--- a/src/cmd/compile/internal/ssa/likelyadjust.go
+++ b/src/cmd/compile/internal/ssa/likelyadjust.go
@@ -28,7 +28,7 @@ type loop struct {
 	isInner bool  // True if never discovered to contain a loop
 
 	// register allocation uses this.
-	containsCall bool // if any block in this loop or any loop it contains is a BlockCall or BlockDefer
+	containsCall bool // if any block in this loop or any loop it contains has a call
 }
 
 // outerinner records that outer contains inner
@@ -50,8 +50,15 @@ func (l *loop) setContainsCall() {
 
 }
 func (l *loop) checkContainsCall(bb *Block) {
-	if bb.Kind == BlockCall || bb.Kind == BlockDefer {
+	if bb.Kind == BlockDefer {
 		l.setContainsCall()
+		return
+	}
+	for _, v := range bb.Values {
+		if opcodeTable[v.Op].call {
+			l.setContainsCall()
+			return
+		}
 	}
 }
 
@@ -113,8 +120,8 @@ func likelyadjust(f *Func) {
 	certain := make([]int8, f.NumBlocks()) // In the long run, all outcomes are at least this bad. Mainly for Exit
 	local := make([]int8, f.NumBlocks())   // for our immediate predecessors.
 
-	nest := loopnestfor(f)
-	po := nest.po
+	po := f.postorder()
+	nest := f.loopnest()
 	b2l := nest.b2l
 
 	for _, b := range po {
@@ -132,7 +139,7 @@ func likelyadjust(f *Func) {
 			// Calls. TODO not all calls are equal, names give useful clues.
 			// Any name-based heuristics are only relative to other calls,
 			// and less influential than inferences from loop structure.
-		case BlockCall, BlockDefer:
+		case BlockDefer:
 			local[b.ID] = blCALL
 			certain[b.ID] = max8(blCALL, certain[b.Succs[0].b.ID])
 
@@ -210,6 +217,13 @@ func likelyadjust(f *Func) {
 					}
 				}
 			}
+			// Look for calls in the block.  If there is one, make this block unlikely.
+			for _, v := range b.Values {
+				if opcodeTable[v.Op].call {
+					local[b.ID] = blCALL
+					certain[b.ID] = max8(blCALL, certain[b.Succs[0].b.ID])
+				}
+			}
 		}
 		if f.pass.debug > 2 {
 			f.Config.Warnl(b.Line, "BP: Block %s, local=%s, certain=%s", b, bllikelies[local[b.ID]-blMin], bllikelies[certain[b.ID]-blMin])
@@ -246,9 +260,8 @@ func (l *loop) nearestOuterLoop(sdom SparseTree, b *Block) *loop {
 }
 
 func loopnestfor(f *Func) *loopnest {
-	po := postorder(f)
-	dom := dominators(f)
-	sdom := newSparseTree(f, dom)
+	po := f.postorder()
+	sdom := f.sdom()
 	b2l := make([]*loop, f.NumBlocks())
 	loops := make([]*loop, 0)
 
diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go
index 85f5255..f9eaedf 100644
--- a/src/cmd/compile/internal/ssa/location.go
+++ b/src/cmd/compile/internal/ssa/location.go
@@ -14,8 +14,9 @@ type Location interface {
 // A Register is a machine register, like %rax.
 // They are numbered densely from 0 (for each architecture).
 type Register struct {
-	Num  int32
-	name string
+	num    int32
+	objNum int16 // register number from cmd/internal/obj/$ARCH
+	name   string
 }
 
 func (r *Register) Name() string {
@@ -32,7 +33,20 @@ type LocalSlot struct {
 
 func (s LocalSlot) Name() string {
 	if s.Off == 0 {
-		return fmt.Sprintf("%s[%s]", s.N, s.Type)
+		return fmt.Sprintf("%v[%v]", s.N, s.Type)
 	}
-	return fmt.Sprintf("%s+%d[%s]", s.N, s.Off, s.Type)
+	return fmt.Sprintf("%v+%d[%v]", s.N, s.Off, s.Type)
+}
+
+type LocPair [2]Location
+
+func (t LocPair) Name() string {
+	n0, n1 := "nil", "nil"
+	if t[0] != nil {
+		n0 = t[0].Name()
+	}
+	if t[1] != nil {
+		n1 = t[1].Name()
+	}
+	return fmt.Sprintf("<%s,%s>", n0, n1)
 }
diff --git a/src/cmd/compile/internal/ssa/loopbce.go b/src/cmd/compile/internal/ssa/loopbce.go
index e94781b..14d8834 100644
--- a/src/cmd/compile/internal/ssa/loopbce.go
+++ b/src/cmd/compile/internal/ssa/loopbce.go
@@ -33,6 +33,7 @@ type indVar struct {
 // TODO: handle 32 bit operations
 func findIndVar(f *Func) []indVar {
 	var iv []indVar
+	sdom := f.sdom()
 
 nextb:
 	for _, b := range f.Blocks {
@@ -110,7 +111,7 @@ nextb:
 
 		// Second condition: b.Succs[entry] dominates nxt so that
 		// nxt is computed when inc < max, meaning nxt <= max.
-		if !f.sdom.isAncestorEq(b.Succs[entry].b, nxt.Block) {
+		if !sdom.isAncestorEq(b.Succs[entry].b, nxt.Block) {
 			// inc+ind can only be reached through the branch that enters the loop.
 			continue
 		}
@@ -172,6 +173,7 @@ func loopbce(f *Func) {
 
 // removesBoundsChecks remove IsInBounds and IsSliceInBounds based on the induction variables.
 func removeBoundsChecks(f *Func, m map[*Value]indVar) {
+	sdom := f.sdom()
 	for _, b := range f.Blocks {
 		if b.Kind != BlockIf {
 			continue
@@ -200,7 +202,7 @@ func removeBoundsChecks(f *Func, m map[*Value]indVar) {
 				goto skip1
 			}
 
-			if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
+			if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
 				if v.Args[1] == iv.max {
 					if f.pass.debug > 0 {
 						f.Config.Warnl(b.Line, "Found redundant %s", v.Op)
@@ -227,7 +229,7 @@ func removeBoundsChecks(f *Func, m map[*Value]indVar) {
 				goto skip2
 			}
 
-			if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
+			if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
 				if v.Args[1].Op == OpSliceCap && iv.max.Op == OpSliceLen && v.Args[1].Args[0] == iv.max.Args[0] {
 					if f.pass.debug > 0 {
 						f.Config.Warnl(b.Line, "Found redundant %s (len promoted to cap)", v.Op)
@@ -248,7 +250,7 @@ func removeBoundsChecks(f *Func, m map[*Value]indVar) {
 			}
 
 			// ind + add >= 0 <-> min + add >= 0 <-> min >= -add
-			if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) {
+			if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) {
 				if !v.Args[1].isGenericIntConst() || !iv.max.isGenericIntConst() {
 					goto skip3
 				}
diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go
index e271ed4..e7c2629 100644
--- a/src/cmd/compile/internal/ssa/lower.go
+++ b/src/cmd/compile/internal/ssa/lower.go
@@ -21,14 +21,19 @@ func checkLower(f *Func) {
 				continue // lowered
 			}
 			switch v.Op {
-			case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive, OpKeepAlive:
+			case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive, OpKeepAlive, OpSelect0, OpSelect1:
 				continue // ok not to lower
+			case OpGetG:
+				if f.Config.hasGReg {
+					// has hardware g register, regalloc takes care of it
+					continue // ok not to lower
+				}
 			}
-			s := "not lowered: " + v.Op.String() + " " + v.Type.SimpleString()
+			s := "not lowered: " + v.String() + ", " + v.Op.String() + " " + v.Type.SimpleString()
 			for _, a := range v.Args {
 				s += " " + a.Type.SimpleString()
 			}
-			f.Unimplementedf("%s", s)
+			f.Fatalf("%s", s)
 		}
 	}
 }
diff --git a/src/cmd/compile/internal/ssa/nilcheck.go b/src/cmd/compile/internal/ssa/nilcheck.go
index 00cb75e..eb2d297 100644
--- a/src/cmd/compile/internal/ssa/nilcheck.go
+++ b/src/cmd/compile/internal/ssa/nilcheck.go
@@ -5,19 +5,12 @@
 package ssa
 
 // nilcheckelim eliminates unnecessary nil checks.
+// runs on machine-independent code.
 func nilcheckelim(f *Func) {
 	// A nil check is redundant if the same nil check was successful in a
 	// dominating block. The efficacy of this pass depends heavily on the
 	// efficacy of the cse pass.
-	idom := f.idom
-	domTree := make([][]*Block, f.NumBlocks())
-
-	// Create a block ID -> [dominees] mapping
-	for _, b := range f.Blocks {
-		if dom := idom[b.ID]; dom != nil {
-			domTree[dom.ID] = append(domTree[dom.ID], b)
-		}
-	}
+	sdom := f.sdom()
 
 	// TODO: Eliminate more nil checks.
 	// We can recursively remove any chain of fixed offset calculations,
@@ -26,14 +19,13 @@ func nilcheckelim(f *Func) {
 
 	type walkState int
 	const (
-		Work   walkState = iota // clear nil check if we should and traverse to dominees regardless
-		RecPtr                  // record the pointer as being nil checked
-		ClearPtr
+		Work     walkState = iota // process nil checks and traverse to dominees
+		ClearPtr                  // forget the fact that ptr is nil
 	)
 
 	type bp struct {
-		block *Block // block, or nil in RecPtr/ClearPtr state
-		ptr   *Value // if non-nil, ptr that is to be set/cleared in RecPtr/ClearPtr state
+		block *Block // block, or nil in ClearPtr state
+		ptr   *Value // if non-nil, ptr that is to be cleared in ClearPtr state
 		op    walkState
 	}
 
@@ -76,54 +68,62 @@ func nilcheckelim(f *Func) {
 
 		switch node.op {
 		case Work:
-			checked := checkedptr(node.block) // ptr being checked for nil/non-nil
-			nonnil := nonnilptr(node.block)   // ptr that is non-nil due to this blocks pred
-
-			if checked != nil {
-				// already have a nilcheck in the dominator path, or this block is a success
-				// block for the same value it is checking
-				if nonNilValues[checked.ID] || checked == nonnil {
-					// Eliminate the nil check.
-					// The deadcode pass will remove vestigial values,
-					// and the fuse pass will join this block with its successor.
-
-					// Logging in the style of the former compiler -- and omit line 1,
-					// which is usually in generated code.
-					if f.Config.Debug_checknil() && node.block.Control.Line > 1 {
-						f.Config.Warnl(node.block.Control.Line, "removed nil check")
+			b := node.block
+
+			// First, see if we're dominated by an explicit nil check.
+			if len(b.Preds) == 1 {
+				p := b.Preds[0].b
+				if p.Kind == BlockIf && p.Control.Op == OpIsNonNil && p.Succs[0].b == b {
+					ptr := p.Control.Args[0]
+					if !nonNilValues[ptr.ID] {
+						nonNilValues[ptr.ID] = true
+						work = append(work, bp{op: ClearPtr, ptr: ptr})
 					}
+				}
+			}
 
-					switch node.block.Kind {
-					case BlockIf:
-						node.block.Kind = BlockFirst
-						node.block.SetControl(nil)
-					case BlockCheck:
-						node.block.Kind = BlockPlain
-						node.block.SetControl(nil)
-					default:
-						f.Fatalf("bad block kind in nilcheck %s", node.block.Kind)
+			// Next, process values in the block.
+			i := 0
+			for _, v := range b.Values {
+				b.Values[i] = v
+				i++
+				switch v.Op {
+				case OpIsNonNil:
+					ptr := v.Args[0]
+					if nonNilValues[ptr.ID] {
+						// This is a redundant explicit nil check.
+						v.reset(OpConstBool)
+						v.AuxInt = 1 // true
 					}
+				case OpNilCheck:
+					ptr := v.Args[0]
+					if nonNilValues[ptr.ID] {
+						// This is a redundant implicit nil check.
+						// Logging in the style of the former compiler -- and omit line 1,
+						// which is usually in generated code.
+						if f.Config.Debug_checknil() && v.Line > 1 {
+							f.Config.Warnl(v.Line, "removed nil check")
+						}
+						v.reset(OpUnknown)
+						i--
+						continue
+					}
+					// Record the fact that we know ptr is non nil, and remember to
+					// undo that information when this dominator subtree is done.
+					nonNilValues[ptr.ID] = true
+					work = append(work, bp{op: ClearPtr, ptr: ptr})
 				}
 			}
-
-			if nonnil != nil && !nonNilValues[nonnil.ID] {
-				// this is a new nilcheck so add a ClearPtr node to clear the
-				// ptr from the map of nil checks once we traverse
-				// back up the tree
-				work = append(work, bp{op: ClearPtr, ptr: nonnil})
+			for j := i; j < len(b.Values); j++ {
+				b.Values[j] = nil
 			}
+			b.Values = b.Values[:i]
 
-			// add all dominated blocks to the work list
-			for _, w := range domTree[node.block.ID] {
-				work = append(work, bp{block: w})
+			// Add all dominated blocks to the work list.
+			for w := sdom[node.block.ID].child; w != nil; w = sdom[w.ID].sibling {
+				work = append(work, bp{op: Work, block: w})
 			}
 
-			if nonnil != nil && !nonNilValues[nonnil.ID] {
-				work = append(work, bp{op: RecPtr, ptr: nonnil})
-			}
-		case RecPtr:
-			nonNilValues[node.ptr.ID] = true
-			continue
 		case ClearPtr:
 			nonNilValues[node.ptr.ID] = false
 			continue
@@ -131,31 +131,88 @@ func nilcheckelim(f *Func) {
 	}
 }
 
-// checkedptr returns the Value, if any,
-// that is used in a nil check in b's Control op.
-func checkedptr(b *Block) *Value {
-	if b.Kind == BlockCheck {
-		return b.Control.Args[0]
-	}
-	if b.Kind == BlockIf && b.Control.Op == OpIsNonNil {
-		return b.Control.Args[0]
-	}
-	return nil
-}
+// All platforms are guaranteed to fault if we load/store to anything smaller than this address.
+const minZeroPage = 4096
 
-// nonnilptr returns the Value, if any,
-// that is non-nil due to b being the successor block
-// of an OpIsNonNil or OpNilCheck block for the value and having a single
-// predecessor.
-func nonnilptr(b *Block) *Value {
-	if len(b.Preds) == 1 {
-		bp := b.Preds[0].b
-		if bp.Kind == BlockCheck {
-			return bp.Control.Args[0]
+// nilcheckelim2 eliminates unnecessary nil checks.
+// Runs after lowering and scheduling.
+func nilcheckelim2(f *Func) {
+	unnecessary := f.newSparseSet(f.NumValues())
+	defer f.retSparseSet(unnecessary)
+	for _, b := range f.Blocks {
+		// Walk the block backwards. Find instructions that will fault if their
+		// input pointer is nil. Remove nil checks on those pointers, as the
+		// faulting instruction effectively does the nil check for free.
+		unnecessary.clear()
+		for i := len(b.Values) - 1; i >= 0; i-- {
+			v := b.Values[i]
+			if opcodeTable[v.Op].nilCheck && unnecessary.contains(v.Args[0].ID) {
+				if f.Config.Debug_checknil() && int(v.Line) > 1 {
+					f.Config.Warnl(v.Line, "removed nil check")
+				}
+				v.reset(OpUnknown)
+				continue
+			}
+			if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
+				if v.Op == OpVarDef || v.Op == OpVarKill || v.Op == OpVarLive {
+					// These ops don't really change memory.
+					continue
+				}
+				// This op changes memory.  Any faulting instruction after v that
+				// we've recorded in the unnecessary map is now obsolete.
+				unnecessary.clear()
+			}
+
+			// Find any pointers that this op is guaranteed to fault on if nil.
+			var ptrstore [2]*Value
+			ptrs := ptrstore[:0]
+			if opcodeTable[v.Op].faultOnNilArg0 {
+				ptrs = append(ptrs, v.Args[0])
+			}
+			if opcodeTable[v.Op].faultOnNilArg1 {
+				ptrs = append(ptrs, v.Args[1])
+			}
+			for _, ptr := range ptrs {
+				// Check to make sure the offset is small.
+				switch opcodeTable[v.Op].auxType {
+				case auxSymOff:
+					if v.Aux != nil || v.AuxInt < 0 || v.AuxInt >= minZeroPage {
+						continue
+					}
+				case auxSymValAndOff:
+					off := ValAndOff(v.AuxInt).Off()
+					if v.Aux != nil || off < 0 || off >= minZeroPage {
+						continue
+					}
+				case auxInt32:
+					// Mips uses this auxType for atomic add constant. It does not affect the effective address.
+				case auxInt64:
+					// ARM uses this auxType for duffcopy/duffzero/alignment info.
+					// It does not affect the effective address.
+				case auxNone:
+					// offset is zero.
+				default:
+					v.Fatalf("can't handle aux %s (type %d) yet\n", v.auxString(), int(opcodeTable[v.Op].auxType))
+				}
+				// This instruction is guaranteed to fault if ptr is nil.
+				// Any previous nil check op is unnecessary.
+				unnecessary.add(ptr.ID)
+			}
 		}
-		if bp.Kind == BlockIf && bp.Control.Op == OpIsNonNil && bp.Succs[0].b == b {
-			return bp.Control.Args[0]
+		// Remove values we've clobbered with OpUnknown.
+		i := 0
+		for _, v := range b.Values {
+			if v.Op != OpUnknown {
+				b.Values[i] = v
+				i++
+			}
+		}
+		for j := i; j < len(b.Values); j++ {
+			b.Values[j] = nil
 		}
+		b.Values = b.Values[:i]
+
+		// TODO: if b.Kind == BlockPlain, start the analysis in the subsequent block to find
+		// more unnecessary nil checks.  Would fix test/nilptr3_ssa.go:157.
 	}
-	return nil
 }
diff --git a/src/cmd/compile/internal/ssa/nilcheck_test.go b/src/cmd/compile/internal/ssa/nilcheck_test.go
index af6cbe8..f12c68c 100644
--- a/src/cmd/compile/internal/ssa/nilcheck_test.go
+++ b/src/cmd/compile/internal/ssa/nilcheck_test.go
@@ -49,7 +49,6 @@ func benchmarkNilCheckDeep(b *testing.B, depth int) {
 	b.ReportAllocs()
 
 	for i := 0; i < b.N; i++ {
-		domTree(fun.f)
 		nilcheckelim(fun.f)
 	}
 }
@@ -84,7 +83,6 @@ func TestNilcheckSimple(t *testing.T) {
 			Exit("mem")))
 
 	CheckFunc(fun.f)
-	domTree(fun.f)
 	nilcheckelim(fun.f)
 
 	// clean up the removed nil check
@@ -122,7 +120,6 @@ func TestNilcheckDomOrder(t *testing.T) {
 			Goto("exit")))
 
 	CheckFunc(fun.f)
-	domTree(fun.f)
 	nilcheckelim(fun.f)
 
 	// clean up the removed nil check
@@ -156,7 +153,6 @@ func TestNilcheckAddr(t *testing.T) {
 			Exit("mem")))
 
 	CheckFunc(fun.f)
-	domTree(fun.f)
 	nilcheckelim(fun.f)
 
 	// clean up the removed nil check
@@ -191,7 +187,6 @@ func TestNilcheckAddPtr(t *testing.T) {
 			Exit("mem")))
 
 	CheckFunc(fun.f)
-	domTree(fun.f)
 	nilcheckelim(fun.f)
 
 	// clean up the removed nil check
@@ -236,7 +231,6 @@ func TestNilcheckPhi(t *testing.T) {
 			Exit("mem")))
 
 	CheckFunc(fun.f)
-	domTree(fun.f)
 	nilcheckelim(fun.f)
 
 	// clean up the removed nil check
@@ -278,7 +272,6 @@ func TestNilcheckKeepRemove(t *testing.T) {
 			Exit("mem")))
 
 	CheckFunc(fun.f)
-	domTree(fun.f)
 	nilcheckelim(fun.f)
 
 	// clean up the removed nil check
@@ -326,7 +319,6 @@ func TestNilcheckInFalseBranch(t *testing.T) {
 			Exit("mem")))
 
 	CheckFunc(fun.f)
-	domTree(fun.f)
 	nilcheckelim(fun.f)
 
 	// clean up the removed nil check
@@ -378,7 +370,6 @@ func TestNilcheckUser(t *testing.T) {
 	CheckFunc(fun.f)
 	// we need the opt here to rewrite the user nilcheck
 	opt(fun.f)
-	domTree(fun.f)
 	nilcheckelim(fun.f)
 
 	// clean up the removed nil check
@@ -423,7 +414,6 @@ func TestNilcheckBug(t *testing.T) {
 	CheckFunc(fun.f)
 	// we need the opt here to rewrite the user nilcheck
 	opt(fun.f)
-	domTree(fun.f)
 	nilcheckelim(fun.f)
 
 	// clean up the removed nil check
diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go
index cadbc7c..315d720 100644
--- a/src/cmd/compile/internal/ssa/op.go
+++ b/src/cmd/compile/internal/ssa/op.go
@@ -26,7 +26,14 @@ type opInfo struct {
 	generic           bool // this is a generic (arch-independent) opcode
 	rematerializeable bool // this op is rematerializeable
 	commutative       bool // this operation is commutative (e.g. addition)
-	resultInArg0      bool // v and v.Args[0] must be allocated to the same register
+	resultInArg0      bool // (first, if a tuple) output of v and v.Args[0] must be allocated to the same register
+	resultNotInArgs   bool // outputs must not be allocated to the same registers as inputs
+	clobberFlags      bool // this op clobbers flags register
+	call              bool // is a function call
+	nilCheck          bool // this op is a nil check on arg0
+	faultOnNilArg0    bool // this op will fault if arg0 is nil (and aux encodes a small offset)
+	faultOnNilArg1    bool // this op will fault if arg1 is nil (and aux encodes a small offset)
+	usesScratch       bool // this op requires scratch memory space
 }
 
 type inputInfo struct {
@@ -34,28 +41,35 @@ type inputInfo struct {
 	regs regMask // allowed input registers
 }
 
+type outputInfo struct {
+	idx  int     // index in output tuple
+	regs regMask // allowed output registers
+}
+
 type regInfo struct {
 	inputs   []inputInfo // ordered in register allocation order
 	clobbers regMask
-	outputs  []regMask // NOTE: values can only have 1 output for now.
+	outputs  []outputInfo // ordered in register allocation order
 }
 
 type auxType int8
 
 const (
-	auxNone         auxType = iota
-	auxBool                 // auxInt is 0/1 for false/true
-	auxInt8                 // auxInt is an 8-bit integer
-	auxInt16                // auxInt is a 16-bit integer
-	auxInt32                // auxInt is a 32-bit integer
-	auxInt64                // auxInt is a 64-bit integer
-	auxInt128               // auxInt represents a 128-bit integer.  Always 0.
-	auxFloat32              // auxInt is a float32 (encoded with math.Float64bits)
-	auxFloat64              // auxInt is a float64 (encoded with math.Float64bits)
-	auxString               // aux is a string
-	auxSym                  // aux is a symbol
-	auxSymOff               // aux is a symbol, auxInt is an offset
-	auxSymValAndOff         // aux is a symbol, auxInt is a ValAndOff
+	auxNone            auxType = iota
+	auxBool                    // auxInt is 0/1 for false/true
+	auxInt8                    // auxInt is an 8-bit integer
+	auxInt16                   // auxInt is a 16-bit integer
+	auxInt32                   // auxInt is a 32-bit integer
+	auxInt64                   // auxInt is a 64-bit integer
+	auxInt128                  // auxInt represents a 128-bit integer.  Always 0.
+	auxFloat32                 // auxInt is a float32 (encoded with math.Float64bits)
+	auxFloat64                 // auxInt is a float64 (encoded with math.Float64bits)
+	auxSizeAndAlign            // auxInt is a SizeAndAlign
+	auxString                  // aux is a string
+	auxSym                     // aux is a symbol
+	auxSymOff                  // aux is a symbol, auxInt is an offset
+	auxSymValAndOff            // aux is a symbol, auxInt is a ValAndOff
+	auxSymSizeAndAlign         // aux is a symbol, auxInt is a SizeAndAlign
 
 	auxSymInt32 // aux is a symbol, auxInt is a 32-bit integer
 )
@@ -124,3 +138,31 @@ func (x ValAndOff) add(off int64) int64 {
 	}
 	return makeValAndOff(x.Val(), x.Off()+off)
 }
+
+// SizeAndAlign holds both the size and the alignment of a type,
+// used in Zero and Move ops.
+// The high 8 bits hold the alignment.
+// The low 56 bits hold the size.
+type SizeAndAlign int64
+
+func (x SizeAndAlign) Size() int64 {
+	return int64(x) & (1<<56 - 1)
+}
+func (x SizeAndAlign) Align() int64 {
+	return int64(uint64(x) >> 56)
+}
+func (x SizeAndAlign) Int64() int64 {
+	return int64(x)
+}
+func (x SizeAndAlign) String() string {
+	return fmt.Sprintf("size=%d,align=%d", x.Size(), x.Align())
+}
+func MakeSizeAndAlign(size, align int64) SizeAndAlign {
+	if size&^(1<<56-1) != 0 {
+		panic("size too big in SizeAndAlign")
+	}
+	if align >= 1<<8 {
+		panic("alignment too big in SizeAndAlign")
+	}
+	return SizeAndAlign(size | align<<56)
+}
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index 383f1ae..a63c5b9 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -6,12 +6,31 @@ package ssa
 import (
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm"
+	"cmd/internal/obj/arm64"
+	"cmd/internal/obj/mips"
+	"cmd/internal/obj/ppc64"
+	"cmd/internal/obj/s390x"
 	"cmd/internal/obj/x86"
 )
 
 const (
 	BlockInvalid BlockKind = iota
 
+	Block386EQ
+	Block386NE
+	Block386LT
+	Block386LE
+	Block386GT
+	Block386GE
+	Block386ULT
+	Block386ULE
+	Block386UGT
+	Block386UGE
+	Block386EQF
+	Block386NEF
+	Block386ORD
+	Block386NAN
+
 	BlockAMD64EQ
 	BlockAMD64NE
 	BlockAMD64LT
@@ -38,11 +57,62 @@ const (
 	BlockARMUGT
 	BlockARMUGE
 
+	BlockARM64EQ
+	BlockARM64NE
+	BlockARM64LT
+	BlockARM64LE
+	BlockARM64GT
+	BlockARM64GE
+	BlockARM64ULT
+	BlockARM64ULE
+	BlockARM64UGT
+	BlockARM64UGE
+	BlockARM64Z
+	BlockARM64NZ
+	BlockARM64ZW
+	BlockARM64NZW
+
+	BlockMIPSEQ
+	BlockMIPSNE
+	BlockMIPSLTZ
+	BlockMIPSLEZ
+	BlockMIPSGTZ
+	BlockMIPSGEZ
+	BlockMIPSFPT
+	BlockMIPSFPF
+
+	BlockMIPS64EQ
+	BlockMIPS64NE
+	BlockMIPS64LTZ
+	BlockMIPS64LEZ
+	BlockMIPS64GTZ
+	BlockMIPS64GEZ
+	BlockMIPS64FPT
+	BlockMIPS64FPF
+
+	BlockPPC64EQ
+	BlockPPC64NE
+	BlockPPC64LT
+	BlockPPC64LE
+	BlockPPC64GT
+	BlockPPC64GE
+	BlockPPC64FLT
+	BlockPPC64FLE
+	BlockPPC64FGT
+	BlockPPC64FGE
+
+	BlockS390XEQ
+	BlockS390XNE
+	BlockS390XLT
+	BlockS390XLE
+	BlockS390XGT
+	BlockS390XGE
+	BlockS390XGTF
+	BlockS390XGEF
+
 	BlockPlain
 	BlockIf
-	BlockCall
 	BlockDefer
-	BlockCheck
 	BlockRet
 	BlockRetJmp
 	BlockExit
@@ -52,6 +122,21 @@ const (
 var blockString = [...]string{
 	BlockInvalid: "BlockInvalid",
 
+	Block386EQ:  "EQ",
+	Block386NE:  "NE",
+	Block386LT:  "LT",
+	Block386LE:  "LE",
+	Block386GT:  "GT",
+	Block386GE:  "GE",
+	Block386ULT: "ULT",
+	Block386ULE: "ULE",
+	Block386UGT: "UGT",
+	Block386UGE: "UGE",
+	Block386EQF: "EQF",
+	Block386NEF: "NEF",
+	Block386ORD: "ORD",
+	Block386NAN: "NAN",
+
 	BlockAMD64EQ:  "EQ",
 	BlockAMD64NE:  "NE",
 	BlockAMD64LT:  "LT",
@@ -78,11 +163,62 @@ var blockString = [...]string{
 	BlockARMUGT: "UGT",
 	BlockARMUGE: "UGE",
 
+	BlockARM64EQ:  "EQ",
+	BlockARM64NE:  "NE",
+	BlockARM64LT:  "LT",
+	BlockARM64LE:  "LE",
+	BlockARM64GT:  "GT",
+	BlockARM64GE:  "GE",
+	BlockARM64ULT: "ULT",
+	BlockARM64ULE: "ULE",
+	BlockARM64UGT: "UGT",
+	BlockARM64UGE: "UGE",
+	BlockARM64Z:   "Z",
+	BlockARM64NZ:  "NZ",
+	BlockARM64ZW:  "ZW",
+	BlockARM64NZW: "NZW",
+
+	BlockMIPSEQ:  "EQ",
+	BlockMIPSNE:  "NE",
+	BlockMIPSLTZ: "LTZ",
+	BlockMIPSLEZ: "LEZ",
+	BlockMIPSGTZ: "GTZ",
+	BlockMIPSGEZ: "GEZ",
+	BlockMIPSFPT: "FPT",
+	BlockMIPSFPF: "FPF",
+
+	BlockMIPS64EQ:  "EQ",
+	BlockMIPS64NE:  "NE",
+	BlockMIPS64LTZ: "LTZ",
+	BlockMIPS64LEZ: "LEZ",
+	BlockMIPS64GTZ: "GTZ",
+	BlockMIPS64GEZ: "GEZ",
+	BlockMIPS64FPT: "FPT",
+	BlockMIPS64FPF: "FPF",
+
+	BlockPPC64EQ:  "EQ",
+	BlockPPC64NE:  "NE",
+	BlockPPC64LT:  "LT",
+	BlockPPC64LE:  "LE",
+	BlockPPC64GT:  "GT",
+	BlockPPC64GE:  "GE",
+	BlockPPC64FLT: "FLT",
+	BlockPPC64FLE: "FLE",
+	BlockPPC64FGT: "FGT",
+	BlockPPC64FGE: "FGE",
+
+	BlockS390XEQ:  "EQ",
+	BlockS390XNE:  "NE",
+	BlockS390XLT:  "LT",
+	BlockS390XLE:  "LE",
+	BlockS390XGT:  "GT",
+	BlockS390XGE:  "GE",
+	BlockS390XGTF: "GTF",
+	BlockS390XGEF: "GEF",
+
 	BlockPlain:  "Plain",
 	BlockIf:     "If",
-	BlockCall:   "Call",
 	BlockDefer:  "Defer",
-	BlockCheck:  "Check",
 	BlockRet:    "Ret",
 	BlockRetJmp: "RetJmp",
 	BlockExit:   "Exit",
@@ -94,6 +230,187 @@ func (k BlockKind) String() string { return blockString[k] }
 const (
 	OpInvalid Op = iota
 
+	Op386ADDSS
+	Op386ADDSD
+	Op386SUBSS
+	Op386SUBSD
+	Op386MULSS
+	Op386MULSD
+	Op386DIVSS
+	Op386DIVSD
+	Op386MOVSSload
+	Op386MOVSDload
+	Op386MOVSSconst
+	Op386MOVSDconst
+	Op386MOVSSloadidx1
+	Op386MOVSSloadidx4
+	Op386MOVSDloadidx1
+	Op386MOVSDloadidx8
+	Op386MOVSSstore
+	Op386MOVSDstore
+	Op386MOVSSstoreidx1
+	Op386MOVSSstoreidx4
+	Op386MOVSDstoreidx1
+	Op386MOVSDstoreidx8
+	Op386ADDL
+	Op386ADDLconst
+	Op386ADDLcarry
+	Op386ADDLconstcarry
+	Op386ADCL
+	Op386ADCLconst
+	Op386SUBL
+	Op386SUBLconst
+	Op386SUBLcarry
+	Op386SUBLconstcarry
+	Op386SBBL
+	Op386SBBLconst
+	Op386MULL
+	Op386MULLconst
+	Op386HMULL
+	Op386HMULLU
+	Op386HMULW
+	Op386HMULB
+	Op386HMULWU
+	Op386HMULBU
+	Op386MULLQU
+	Op386DIVL
+	Op386DIVW
+	Op386DIVLU
+	Op386DIVWU
+	Op386MODL
+	Op386MODW
+	Op386MODLU
+	Op386MODWU
+	Op386ANDL
+	Op386ANDLconst
+	Op386ORL
+	Op386ORLconst
+	Op386XORL
+	Op386XORLconst
+	Op386CMPL
+	Op386CMPW
+	Op386CMPB
+	Op386CMPLconst
+	Op386CMPWconst
+	Op386CMPBconst
+	Op386UCOMISS
+	Op386UCOMISD
+	Op386TESTL
+	Op386TESTW
+	Op386TESTB
+	Op386TESTLconst
+	Op386TESTWconst
+	Op386TESTBconst
+	Op386SHLL
+	Op386SHLLconst
+	Op386SHRL
+	Op386SHRW
+	Op386SHRB
+	Op386SHRLconst
+	Op386SHRWconst
+	Op386SHRBconst
+	Op386SARL
+	Op386SARW
+	Op386SARB
+	Op386SARLconst
+	Op386SARWconst
+	Op386SARBconst
+	Op386ROLLconst
+	Op386ROLWconst
+	Op386ROLBconst
+	Op386NEGL
+	Op386NOTL
+	Op386BSFL
+	Op386BSFW
+	Op386BSRL
+	Op386BSRW
+	Op386BSWAPL
+	Op386SQRTSD
+	Op386SBBLcarrymask
+	Op386SETEQ
+	Op386SETNE
+	Op386SETL
+	Op386SETLE
+	Op386SETG
+	Op386SETGE
+	Op386SETB
+	Op386SETBE
+	Op386SETA
+	Op386SETAE
+	Op386SETEQF
+	Op386SETNEF
+	Op386SETORD
+	Op386SETNAN
+	Op386SETGF
+	Op386SETGEF
+	Op386MOVBLSX
+	Op386MOVBLZX
+	Op386MOVWLSX
+	Op386MOVWLZX
+	Op386MOVLconst
+	Op386CVTTSD2SL
+	Op386CVTTSS2SL
+	Op386CVTSL2SS
+	Op386CVTSL2SD
+	Op386CVTSD2SS
+	Op386CVTSS2SD
+	Op386PXOR
+	Op386LEAL
+	Op386LEAL1
+	Op386LEAL2
+	Op386LEAL4
+	Op386LEAL8
+	Op386MOVBload
+	Op386MOVBLSXload
+	Op386MOVWload
+	Op386MOVWLSXload
+	Op386MOVLload
+	Op386MOVBstore
+	Op386MOVWstore
+	Op386MOVLstore
+	Op386MOVBloadidx1
+	Op386MOVWloadidx1
+	Op386MOVWloadidx2
+	Op386MOVLloadidx1
+	Op386MOVLloadidx4
+	Op386MOVBstoreidx1
+	Op386MOVWstoreidx1
+	Op386MOVWstoreidx2
+	Op386MOVLstoreidx1
+	Op386MOVLstoreidx4
+	Op386MOVBstoreconst
+	Op386MOVWstoreconst
+	Op386MOVLstoreconst
+	Op386MOVBstoreconstidx1
+	Op386MOVWstoreconstidx1
+	Op386MOVWstoreconstidx2
+	Op386MOVLstoreconstidx1
+	Op386MOVLstoreconstidx4
+	Op386DUFFZERO
+	Op386REPSTOSL
+	Op386CALLstatic
+	Op386CALLclosure
+	Op386CALLdefer
+	Op386CALLgo
+	Op386CALLinter
+	Op386DUFFCOPY
+	Op386REPMOVSL
+	Op386InvertFlags
+	Op386LoweredGetG
+	Op386LoweredGetClosurePtr
+	Op386LoweredNilCheck
+	Op386MOVLconvert
+	Op386FlagEQ
+	Op386FlagLT_ULT
+	Op386FlagLT_UGT
+	Op386FlagGT_UGT
+	Op386FlagGT_ULT
+	Op386FCHS
+	Op386MOVSSconst1
+	Op386MOVSDconst1
+	Op386MOVSSconst2
+	Op386MOVSDconst2
+
 	OpAMD64ADDSS
 	OpAMD64ADDSD
 	OpAMD64SUBSS
@@ -143,12 +460,8 @@ const (
 	OpAMD64DIVQU
 	OpAMD64DIVLU
 	OpAMD64DIVWU
-	OpAMD64MODQ
-	OpAMD64MODL
-	OpAMD64MODW
-	OpAMD64MODQU
-	OpAMD64MODLU
-	OpAMD64MODWU
+	OpAMD64MULQU2
+	OpAMD64DIVQU2
 	OpAMD64ANDQ
 	OpAMD64ANDL
 	OpAMD64ANDQconst
@@ -209,16 +522,8 @@ const (
 	OpAMD64NOTL
 	OpAMD64BSFQ
 	OpAMD64BSFL
-	OpAMD64BSFW
-	OpAMD64BSRQ
-	OpAMD64BSRL
-	OpAMD64BSRW
-	OpAMD64CMOVQEQconst
-	OpAMD64CMOVLEQconst
-	OpAMD64CMOVWEQconst
-	OpAMD64CMOVQNEconst
-	OpAMD64CMOVLNEconst
-	OpAMD64CMOVWNEconst
+	OpAMD64CMOVQEQ
+	OpAMD64CMOVLEQ
 	OpAMD64BSWAPQ
 	OpAMD64BSWAPL
 	OpAMD64SQRTSD
@@ -264,6 +569,7 @@ const (
 	OpAMD64LEAQ2
 	OpAMD64LEAQ4
 	OpAMD64LEAQ8
+	OpAMD64LEAL
 	OpAMD64MOVBload
 	OpAMD64MOVBQSXload
 	OpAMD64MOVWload
@@ -317,20 +623,942 @@ const (
 	OpAMD64LoweredGetClosurePtr
 	OpAMD64LoweredNilCheck
 	OpAMD64MOVQconvert
+	OpAMD64MOVLconvert
 	OpAMD64FlagEQ
 	OpAMD64FlagLT_ULT
 	OpAMD64FlagLT_UGT
 	OpAMD64FlagGT_UGT
 	OpAMD64FlagGT_ULT
+	OpAMD64MOVLatomicload
+	OpAMD64MOVQatomicload
+	OpAMD64XCHGL
+	OpAMD64XCHGQ
+	OpAMD64XADDLlock
+	OpAMD64XADDQlock
+	OpAMD64AddTupleFirst32
+	OpAMD64AddTupleFirst64
+	OpAMD64CMPXCHGLlock
+	OpAMD64CMPXCHGQlock
+	OpAMD64ANDBlock
+	OpAMD64ORBlock
 
 	OpARMADD
 	OpARMADDconst
-	OpARMMOVWconst
+	OpARMSUB
+	OpARMSUBconst
+	OpARMRSB
+	OpARMRSBconst
+	OpARMMUL
+	OpARMHMUL
+	OpARMHMULU
+	OpARMUDIVrtcall
+	OpARMADDS
+	OpARMADDSconst
+	OpARMADC
+	OpARMADCconst
+	OpARMSUBS
+	OpARMSUBSconst
+	OpARMRSBSconst
+	OpARMSBC
+	OpARMSBCconst
+	OpARMRSCconst
+	OpARMMULLU
+	OpARMMULA
+	OpARMADDF
+	OpARMADDD
+	OpARMSUBF
+	OpARMSUBD
+	OpARMMULF
+	OpARMMULD
+	OpARMDIVF
+	OpARMDIVD
+	OpARMAND
+	OpARMANDconst
+	OpARMOR
+	OpARMORconst
+	OpARMXOR
+	OpARMXORconst
+	OpARMBIC
+	OpARMBICconst
+	OpARMMVN
+	OpARMNEGF
+	OpARMNEGD
+	OpARMSQRTD
+	OpARMCLZ
+	OpARMSLL
+	OpARMSLLconst
+	OpARMSRL
+	OpARMSRLconst
+	OpARMSRA
+	OpARMSRAconst
+	OpARMSRRconst
+	OpARMADDshiftLL
+	OpARMADDshiftRL
+	OpARMADDshiftRA
+	OpARMSUBshiftLL
+	OpARMSUBshiftRL
+	OpARMSUBshiftRA
+	OpARMRSBshiftLL
+	OpARMRSBshiftRL
+	OpARMRSBshiftRA
+	OpARMANDshiftLL
+	OpARMANDshiftRL
+	OpARMANDshiftRA
+	OpARMORshiftLL
+	OpARMORshiftRL
+	OpARMORshiftRA
+	OpARMXORshiftLL
+	OpARMXORshiftRL
+	OpARMXORshiftRA
+	OpARMXORshiftRR
+	OpARMBICshiftLL
+	OpARMBICshiftRL
+	OpARMBICshiftRA
+	OpARMMVNshiftLL
+	OpARMMVNshiftRL
+	OpARMMVNshiftRA
+	OpARMADCshiftLL
+	OpARMADCshiftRL
+	OpARMADCshiftRA
+	OpARMSBCshiftLL
+	OpARMSBCshiftRL
+	OpARMSBCshiftRA
+	OpARMRSCshiftLL
+	OpARMRSCshiftRL
+	OpARMRSCshiftRA
+	OpARMADDSshiftLL
+	OpARMADDSshiftRL
+	OpARMADDSshiftRA
+	OpARMSUBSshiftLL
+	OpARMSUBSshiftRL
+	OpARMSUBSshiftRA
+	OpARMRSBSshiftLL
+	OpARMRSBSshiftRL
+	OpARMRSBSshiftRA
+	OpARMADDshiftLLreg
+	OpARMADDshiftRLreg
+	OpARMADDshiftRAreg
+	OpARMSUBshiftLLreg
+	OpARMSUBshiftRLreg
+	OpARMSUBshiftRAreg
+	OpARMRSBshiftLLreg
+	OpARMRSBshiftRLreg
+	OpARMRSBshiftRAreg
+	OpARMANDshiftLLreg
+	OpARMANDshiftRLreg
+	OpARMANDshiftRAreg
+	OpARMORshiftLLreg
+	OpARMORshiftRLreg
+	OpARMORshiftRAreg
+	OpARMXORshiftLLreg
+	OpARMXORshiftRLreg
+	OpARMXORshiftRAreg
+	OpARMBICshiftLLreg
+	OpARMBICshiftRLreg
+	OpARMBICshiftRAreg
+	OpARMMVNshiftLLreg
+	OpARMMVNshiftRLreg
+	OpARMMVNshiftRAreg
+	OpARMADCshiftLLreg
+	OpARMADCshiftRLreg
+	OpARMADCshiftRAreg
+	OpARMSBCshiftLLreg
+	OpARMSBCshiftRLreg
+	OpARMSBCshiftRAreg
+	OpARMRSCshiftLLreg
+	OpARMRSCshiftRLreg
+	OpARMRSCshiftRAreg
+	OpARMADDSshiftLLreg
+	OpARMADDSshiftRLreg
+	OpARMADDSshiftRAreg
+	OpARMSUBSshiftLLreg
+	OpARMSUBSshiftRLreg
+	OpARMSUBSshiftRAreg
+	OpARMRSBSshiftLLreg
+	OpARMRSBSshiftRLreg
+	OpARMRSBSshiftRAreg
 	OpARMCMP
+	OpARMCMPconst
+	OpARMCMN
+	OpARMCMNconst
+	OpARMTST
+	OpARMTSTconst
+	OpARMTEQ
+	OpARMTEQconst
+	OpARMCMPF
+	OpARMCMPD
+	OpARMCMPshiftLL
+	OpARMCMPshiftRL
+	OpARMCMPshiftRA
+	OpARMCMPshiftLLreg
+	OpARMCMPshiftRLreg
+	OpARMCMPshiftRAreg
+	OpARMCMPF0
+	OpARMCMPD0
+	OpARMMOVWconst
+	OpARMMOVFconst
+	OpARMMOVDconst
+	OpARMMOVWaddr
+	OpARMMOVBload
+	OpARMMOVBUload
+	OpARMMOVHload
+	OpARMMOVHUload
 	OpARMMOVWload
+	OpARMMOVFload
+	OpARMMOVDload
+	OpARMMOVBstore
+	OpARMMOVHstore
 	OpARMMOVWstore
+	OpARMMOVFstore
+	OpARMMOVDstore
+	OpARMMOVWloadidx
+	OpARMMOVWloadshiftLL
+	OpARMMOVWloadshiftRL
+	OpARMMOVWloadshiftRA
+	OpARMMOVWstoreidx
+	OpARMMOVWstoreshiftLL
+	OpARMMOVWstoreshiftRL
+	OpARMMOVWstoreshiftRA
+	OpARMMOVBreg
+	OpARMMOVBUreg
+	OpARMMOVHreg
+	OpARMMOVHUreg
+	OpARMMOVWreg
+	OpARMMOVWnop
+	OpARMMOVWF
+	OpARMMOVWD
+	OpARMMOVWUF
+	OpARMMOVWUD
+	OpARMMOVFW
+	OpARMMOVDW
+	OpARMMOVFWU
+	OpARMMOVDWU
+	OpARMMOVFD
+	OpARMMOVDF
+	OpARMCMOVWHSconst
+	OpARMCMOVWLSconst
+	OpARMSRAcond
 	OpARMCALLstatic
+	OpARMCALLclosure
+	OpARMCALLdefer
+	OpARMCALLgo
+	OpARMCALLinter
+	OpARMLoweredNilCheck
+	OpARMEqual
+	OpARMNotEqual
 	OpARMLessThan
+	OpARMLessEqual
+	OpARMGreaterThan
+	OpARMGreaterEqual
+	OpARMLessThanU
+	OpARMLessEqualU
+	OpARMGreaterThanU
+	OpARMGreaterEqualU
+	OpARMDUFFZERO
+	OpARMDUFFCOPY
+	OpARMLoweredZero
+	OpARMLoweredMove
+	OpARMLoweredGetClosurePtr
+	OpARMMOVWconvert
+	OpARMFlagEQ
+	OpARMFlagLT_ULT
+	OpARMFlagLT_UGT
+	OpARMFlagGT_UGT
+	OpARMFlagGT_ULT
+	OpARMInvertFlags
+
+	OpARM64ADD
+	OpARM64ADDconst
+	OpARM64SUB
+	OpARM64SUBconst
+	OpARM64MUL
+	OpARM64MULW
+	OpARM64MULH
+	OpARM64UMULH
+	OpARM64MULL
+	OpARM64UMULL
+	OpARM64DIV
+	OpARM64UDIV
+	OpARM64DIVW
+	OpARM64UDIVW
+	OpARM64MOD
+	OpARM64UMOD
+	OpARM64MODW
+	OpARM64UMODW
+	OpARM64FADDS
+	OpARM64FADDD
+	OpARM64FSUBS
+	OpARM64FSUBD
+	OpARM64FMULS
+	OpARM64FMULD
+	OpARM64FDIVS
+	OpARM64FDIVD
+	OpARM64AND
+	OpARM64ANDconst
+	OpARM64OR
+	OpARM64ORconst
+	OpARM64XOR
+	OpARM64XORconst
+	OpARM64BIC
+	OpARM64BICconst
+	OpARM64MVN
+	OpARM64NEG
+	OpARM64FNEGS
+	OpARM64FNEGD
+	OpARM64FSQRTD
+	OpARM64REV
+	OpARM64REVW
+	OpARM64REV16W
+	OpARM64RBIT
+	OpARM64RBITW
+	OpARM64CLZ
+	OpARM64CLZW
+	OpARM64SLL
+	OpARM64SLLconst
+	OpARM64SRL
+	OpARM64SRLconst
+	OpARM64SRA
+	OpARM64SRAconst
+	OpARM64RORconst
+	OpARM64RORWconst
+	OpARM64CMP
+	OpARM64CMPconst
+	OpARM64CMPW
+	OpARM64CMPWconst
+	OpARM64CMN
+	OpARM64CMNconst
+	OpARM64CMNW
+	OpARM64CMNWconst
+	OpARM64FCMPS
+	OpARM64FCMPD
+	OpARM64ADDshiftLL
+	OpARM64ADDshiftRL
+	OpARM64ADDshiftRA
+	OpARM64SUBshiftLL
+	OpARM64SUBshiftRL
+	OpARM64SUBshiftRA
+	OpARM64ANDshiftLL
+	OpARM64ANDshiftRL
+	OpARM64ANDshiftRA
+	OpARM64ORshiftLL
+	OpARM64ORshiftRL
+	OpARM64ORshiftRA
+	OpARM64XORshiftLL
+	OpARM64XORshiftRL
+	OpARM64XORshiftRA
+	OpARM64BICshiftLL
+	OpARM64BICshiftRL
+	OpARM64BICshiftRA
+	OpARM64CMPshiftLL
+	OpARM64CMPshiftRL
+	OpARM64CMPshiftRA
+	OpARM64MOVDconst
+	OpARM64FMOVSconst
+	OpARM64FMOVDconst
+	OpARM64MOVDaddr
+	OpARM64MOVBload
+	OpARM64MOVBUload
+	OpARM64MOVHload
+	OpARM64MOVHUload
+	OpARM64MOVWload
+	OpARM64MOVWUload
+	OpARM64MOVDload
+	OpARM64FMOVSload
+	OpARM64FMOVDload
+	OpARM64MOVBstore
+	OpARM64MOVHstore
+	OpARM64MOVWstore
+	OpARM64MOVDstore
+	OpARM64FMOVSstore
+	OpARM64FMOVDstore
+	OpARM64MOVBstorezero
+	OpARM64MOVHstorezero
+	OpARM64MOVWstorezero
+	OpARM64MOVDstorezero
+	OpARM64MOVBreg
+	OpARM64MOVBUreg
+	OpARM64MOVHreg
+	OpARM64MOVHUreg
+	OpARM64MOVWreg
+	OpARM64MOVWUreg
+	OpARM64MOVDreg
+	OpARM64MOVDnop
+	OpARM64SCVTFWS
+	OpARM64SCVTFWD
+	OpARM64UCVTFWS
+	OpARM64UCVTFWD
+	OpARM64SCVTFS
+	OpARM64SCVTFD
+	OpARM64UCVTFS
+	OpARM64UCVTFD
+	OpARM64FCVTZSSW
+	OpARM64FCVTZSDW
+	OpARM64FCVTZUSW
+	OpARM64FCVTZUDW
+	OpARM64FCVTZSS
+	OpARM64FCVTZSD
+	OpARM64FCVTZUS
+	OpARM64FCVTZUD
+	OpARM64FCVTSD
+	OpARM64FCVTDS
+	OpARM64CSELULT
+	OpARM64CSELULT0
+	OpARM64CALLstatic
+	OpARM64CALLclosure
+	OpARM64CALLdefer
+	OpARM64CALLgo
+	OpARM64CALLinter
+	OpARM64LoweredNilCheck
+	OpARM64Equal
+	OpARM64NotEqual
+	OpARM64LessThan
+	OpARM64LessEqual
+	OpARM64GreaterThan
+	OpARM64GreaterEqual
+	OpARM64LessThanU
+	OpARM64LessEqualU
+	OpARM64GreaterThanU
+	OpARM64GreaterEqualU
+	OpARM64DUFFZERO
+	OpARM64LoweredZero
+	OpARM64DUFFCOPY
+	OpARM64LoweredMove
+	OpARM64LoweredGetClosurePtr
+	OpARM64MOVDconvert
+	OpARM64FlagEQ
+	OpARM64FlagLT_ULT
+	OpARM64FlagLT_UGT
+	OpARM64FlagGT_UGT
+	OpARM64FlagGT_ULT
+	OpARM64InvertFlags
+	OpARM64LDAR
+	OpARM64LDARW
+	OpARM64STLR
+	OpARM64STLRW
+	OpARM64LoweredAtomicExchange64
+	OpARM64LoweredAtomicExchange32
+	OpARM64LoweredAtomicAdd64
+	OpARM64LoweredAtomicAdd32
+	OpARM64LoweredAtomicCas64
+	OpARM64LoweredAtomicCas32
+	OpARM64LoweredAtomicAnd8
+	OpARM64LoweredAtomicOr8
+
+	OpMIPSADD
+	OpMIPSADDconst
+	OpMIPSSUB
+	OpMIPSSUBconst
+	OpMIPSMUL
+	OpMIPSMULT
+	OpMIPSMULTU
+	OpMIPSDIV
+	OpMIPSDIVU
+	OpMIPSADDF
+	OpMIPSADDD
+	OpMIPSSUBF
+	OpMIPSSUBD
+	OpMIPSMULF
+	OpMIPSMULD
+	OpMIPSDIVF
+	OpMIPSDIVD
+	OpMIPSAND
+	OpMIPSANDconst
+	OpMIPSOR
+	OpMIPSORconst
+	OpMIPSXOR
+	OpMIPSXORconst
+	OpMIPSNOR
+	OpMIPSNORconst
+	OpMIPSNEG
+	OpMIPSNEGF
+	OpMIPSNEGD
+	OpMIPSSQRTD
+	OpMIPSSLL
+	OpMIPSSLLconst
+	OpMIPSSRL
+	OpMIPSSRLconst
+	OpMIPSSRA
+	OpMIPSSRAconst
+	OpMIPSCLZ
+	OpMIPSSGT
+	OpMIPSSGTconst
+	OpMIPSSGTzero
+	OpMIPSSGTU
+	OpMIPSSGTUconst
+	OpMIPSSGTUzero
+	OpMIPSCMPEQF
+	OpMIPSCMPEQD
+	OpMIPSCMPGEF
+	OpMIPSCMPGED
+	OpMIPSCMPGTF
+	OpMIPSCMPGTD
+	OpMIPSMOVWconst
+	OpMIPSMOVFconst
+	OpMIPSMOVDconst
+	OpMIPSMOVWaddr
+	OpMIPSMOVBload
+	OpMIPSMOVBUload
+	OpMIPSMOVHload
+	OpMIPSMOVHUload
+	OpMIPSMOVWload
+	OpMIPSMOVFload
+	OpMIPSMOVDload
+	OpMIPSMOVBstore
+	OpMIPSMOVHstore
+	OpMIPSMOVWstore
+	OpMIPSMOVFstore
+	OpMIPSMOVDstore
+	OpMIPSMOVBstorezero
+	OpMIPSMOVHstorezero
+	OpMIPSMOVWstorezero
+	OpMIPSMOVBreg
+	OpMIPSMOVBUreg
+	OpMIPSMOVHreg
+	OpMIPSMOVHUreg
+	OpMIPSMOVWreg
+	OpMIPSMOVWnop
+	OpMIPSCMOVZ
+	OpMIPSCMOVZzero
+	OpMIPSMOVWF
+	OpMIPSMOVWD
+	OpMIPSTRUNCFW
+	OpMIPSTRUNCDW
+	OpMIPSMOVFD
+	OpMIPSMOVDF
+	OpMIPSCALLstatic
+	OpMIPSCALLclosure
+	OpMIPSCALLdefer
+	OpMIPSCALLgo
+	OpMIPSCALLinter
+	OpMIPSLoweredAtomicLoad
+	OpMIPSLoweredAtomicStore
+	OpMIPSLoweredAtomicStorezero
+	OpMIPSLoweredAtomicExchange
+	OpMIPSLoweredAtomicAdd
+	OpMIPSLoweredAtomicAddconst
+	OpMIPSLoweredAtomicCas
+	OpMIPSLoweredAtomicAnd
+	OpMIPSLoweredAtomicOr
+	OpMIPSLoweredZero
+	OpMIPSLoweredMove
+	OpMIPSLoweredNilCheck
+	OpMIPSFPFlagTrue
+	OpMIPSFPFlagFalse
+	OpMIPSLoweredGetClosurePtr
+	OpMIPSMOVWconvert
+
+	OpMIPS64ADDV
+	OpMIPS64ADDVconst
+	OpMIPS64SUBV
+	OpMIPS64SUBVconst
+	OpMIPS64MULV
+	OpMIPS64MULVU
+	OpMIPS64DIVV
+	OpMIPS64DIVVU
+	OpMIPS64ADDF
+	OpMIPS64ADDD
+	OpMIPS64SUBF
+	OpMIPS64SUBD
+	OpMIPS64MULF
+	OpMIPS64MULD
+	OpMIPS64DIVF
+	OpMIPS64DIVD
+	OpMIPS64AND
+	OpMIPS64ANDconst
+	OpMIPS64OR
+	OpMIPS64ORconst
+	OpMIPS64XOR
+	OpMIPS64XORconst
+	OpMIPS64NOR
+	OpMIPS64NORconst
+	OpMIPS64NEGV
+	OpMIPS64NEGF
+	OpMIPS64NEGD
+	OpMIPS64SLLV
+	OpMIPS64SLLVconst
+	OpMIPS64SRLV
+	OpMIPS64SRLVconst
+	OpMIPS64SRAV
+	OpMIPS64SRAVconst
+	OpMIPS64SGT
+	OpMIPS64SGTconst
+	OpMIPS64SGTU
+	OpMIPS64SGTUconst
+	OpMIPS64CMPEQF
+	OpMIPS64CMPEQD
+	OpMIPS64CMPGEF
+	OpMIPS64CMPGED
+	OpMIPS64CMPGTF
+	OpMIPS64CMPGTD
+	OpMIPS64MOVVconst
+	OpMIPS64MOVFconst
+	OpMIPS64MOVDconst
+	OpMIPS64MOVVaddr
+	OpMIPS64MOVBload
+	OpMIPS64MOVBUload
+	OpMIPS64MOVHload
+	OpMIPS64MOVHUload
+	OpMIPS64MOVWload
+	OpMIPS64MOVWUload
+	OpMIPS64MOVVload
+	OpMIPS64MOVFload
+	OpMIPS64MOVDload
+	OpMIPS64MOVBstore
+	OpMIPS64MOVHstore
+	OpMIPS64MOVWstore
+	OpMIPS64MOVVstore
+	OpMIPS64MOVFstore
+	OpMIPS64MOVDstore
+	OpMIPS64MOVBstorezero
+	OpMIPS64MOVHstorezero
+	OpMIPS64MOVWstorezero
+	OpMIPS64MOVVstorezero
+	OpMIPS64MOVBreg
+	OpMIPS64MOVBUreg
+	OpMIPS64MOVHreg
+	OpMIPS64MOVHUreg
+	OpMIPS64MOVWreg
+	OpMIPS64MOVWUreg
+	OpMIPS64MOVVreg
+	OpMIPS64MOVVnop
+	OpMIPS64MOVWF
+	OpMIPS64MOVWD
+	OpMIPS64MOVVF
+	OpMIPS64MOVVD
+	OpMIPS64TRUNCFW
+	OpMIPS64TRUNCDW
+	OpMIPS64TRUNCFV
+	OpMIPS64TRUNCDV
+	OpMIPS64MOVFD
+	OpMIPS64MOVDF
+	OpMIPS64CALLstatic
+	OpMIPS64CALLclosure
+	OpMIPS64CALLdefer
+	OpMIPS64CALLgo
+	OpMIPS64CALLinter
+	OpMIPS64DUFFZERO
+	OpMIPS64LoweredZero
+	OpMIPS64LoweredMove
+	OpMIPS64LoweredNilCheck
+	OpMIPS64FPFlagTrue
+	OpMIPS64FPFlagFalse
+	OpMIPS64LoweredGetClosurePtr
+	OpMIPS64MOVVconvert
+
+	OpPPC64ADD
+	OpPPC64ADDconst
+	OpPPC64FADD
+	OpPPC64FADDS
+	OpPPC64SUB
+	OpPPC64FSUB
+	OpPPC64FSUBS
+	OpPPC64MULLD
+	OpPPC64MULLW
+	OpPPC64MULHD
+	OpPPC64MULHW
+	OpPPC64MULHDU
+	OpPPC64MULHWU
+	OpPPC64FMUL
+	OpPPC64FMULS
+	OpPPC64SRAD
+	OpPPC64SRAW
+	OpPPC64SRD
+	OpPPC64SRW
+	OpPPC64SLD
+	OpPPC64SLW
+	OpPPC64ADDconstForCarry
+	OpPPC64MaskIfNotCarry
+	OpPPC64SRADconst
+	OpPPC64SRAWconst
+	OpPPC64SRDconst
+	OpPPC64SRWconst
+	OpPPC64SLDconst
+	OpPPC64SLWconst
+	OpPPC64FDIV
+	OpPPC64FDIVS
+	OpPPC64DIVD
+	OpPPC64DIVW
+	OpPPC64DIVDU
+	OpPPC64DIVWU
+	OpPPC64FCTIDZ
+	OpPPC64FCTIWZ
+	OpPPC64FCFID
+	OpPPC64FRSP
+	OpPPC64Xf2i64
+	OpPPC64Xi2f64
+	OpPPC64AND
+	OpPPC64ANDN
+	OpPPC64OR
+	OpPPC64ORN
+	OpPPC64XOR
+	OpPPC64EQV
+	OpPPC64NEG
+	OpPPC64FNEG
+	OpPPC64FSQRT
+	OpPPC64FSQRTS
+	OpPPC64ORconst
+	OpPPC64XORconst
+	OpPPC64ANDconst
+	OpPPC64ANDCCconst
+	OpPPC64MOVBreg
+	OpPPC64MOVBZreg
+	OpPPC64MOVHreg
+	OpPPC64MOVHZreg
+	OpPPC64MOVWreg
+	OpPPC64MOVWZreg
+	OpPPC64MOVBZload
+	OpPPC64MOVHload
+	OpPPC64MOVHZload
+	OpPPC64MOVWload
+	OpPPC64MOVWZload
+	OpPPC64MOVDload
+	OpPPC64FMOVDload
+	OpPPC64FMOVSload
+	OpPPC64MOVBstore
+	OpPPC64MOVHstore
+	OpPPC64MOVWstore
+	OpPPC64MOVDstore
+	OpPPC64FMOVDstore
+	OpPPC64FMOVSstore
+	OpPPC64MOVBstorezero
+	OpPPC64MOVHstorezero
+	OpPPC64MOVWstorezero
+	OpPPC64MOVDstorezero
+	OpPPC64MOVDaddr
+	OpPPC64MOVDconst
+	OpPPC64FMOVDconst
+	OpPPC64FMOVSconst
+	OpPPC64FCMPU
+	OpPPC64CMP
+	OpPPC64CMPU
+	OpPPC64CMPW
+	OpPPC64CMPWU
+	OpPPC64CMPconst
+	OpPPC64CMPUconst
+	OpPPC64CMPWconst
+	OpPPC64CMPWUconst
+	OpPPC64Equal
+	OpPPC64NotEqual
+	OpPPC64LessThan
+	OpPPC64FLessThan
+	OpPPC64LessEqual
+	OpPPC64FLessEqual
+	OpPPC64GreaterThan
+	OpPPC64FGreaterThan
+	OpPPC64GreaterEqual
+	OpPPC64FGreaterEqual
+	OpPPC64LoweredGetClosurePtr
+	OpPPC64LoweredNilCheck
+	OpPPC64MOVDconvert
+	OpPPC64CALLstatic
+	OpPPC64CALLclosure
+	OpPPC64CALLdefer
+	OpPPC64CALLgo
+	OpPPC64CALLinter
+	OpPPC64LoweredZero
+	OpPPC64LoweredMove
+	OpPPC64InvertFlags
+	OpPPC64FlagEQ
+	OpPPC64FlagLT
+	OpPPC64FlagGT
+
+	OpS390XFADDS
+	OpS390XFADD
+	OpS390XFSUBS
+	OpS390XFSUB
+	OpS390XFMULS
+	OpS390XFMUL
+	OpS390XFDIVS
+	OpS390XFDIV
+	OpS390XFNEGS
+	OpS390XFNEG
+	OpS390XFMOVSload
+	OpS390XFMOVDload
+	OpS390XFMOVSconst
+	OpS390XFMOVDconst
+	OpS390XFMOVSloadidx
+	OpS390XFMOVDloadidx
+	OpS390XFMOVSstore
+	OpS390XFMOVDstore
+	OpS390XFMOVSstoreidx
+	OpS390XFMOVDstoreidx
+	OpS390XADD
+	OpS390XADDW
+	OpS390XADDconst
+	OpS390XADDWconst
+	OpS390XADDload
+	OpS390XADDWload
+	OpS390XSUB
+	OpS390XSUBW
+	OpS390XSUBconst
+	OpS390XSUBWconst
+	OpS390XSUBload
+	OpS390XSUBWload
+	OpS390XMULLD
+	OpS390XMULLW
+	OpS390XMULLDconst
+	OpS390XMULLWconst
+	OpS390XMULLDload
+	OpS390XMULLWload
+	OpS390XMULHD
+	OpS390XMULHDU
+	OpS390XDIVD
+	OpS390XDIVW
+	OpS390XDIVDU
+	OpS390XDIVWU
+	OpS390XMODD
+	OpS390XMODW
+	OpS390XMODDU
+	OpS390XMODWU
+	OpS390XAND
+	OpS390XANDW
+	OpS390XANDconst
+	OpS390XANDWconst
+	OpS390XANDload
+	OpS390XANDWload
+	OpS390XOR
+	OpS390XORW
+	OpS390XORconst
+	OpS390XORWconst
+	OpS390XORload
+	OpS390XORWload
+	OpS390XXOR
+	OpS390XXORW
+	OpS390XXORconst
+	OpS390XXORWconst
+	OpS390XXORload
+	OpS390XXORWload
+	OpS390XCMP
+	OpS390XCMPW
+	OpS390XCMPU
+	OpS390XCMPWU
+	OpS390XCMPconst
+	OpS390XCMPWconst
+	OpS390XCMPUconst
+	OpS390XCMPWUconst
+	OpS390XFCMPS
+	OpS390XFCMP
+	OpS390XSLD
+	OpS390XSLW
+	OpS390XSLDconst
+	OpS390XSLWconst
+	OpS390XSRD
+	OpS390XSRW
+	OpS390XSRDconst
+	OpS390XSRWconst
+	OpS390XSRAD
+	OpS390XSRAW
+	OpS390XSRADconst
+	OpS390XSRAWconst
+	OpS390XRLLGconst
+	OpS390XRLLconst
+	OpS390XNEG
+	OpS390XNEGW
+	OpS390XNOT
+	OpS390XNOTW
+	OpS390XFSQRT
+	OpS390XSUBEcarrymask
+	OpS390XSUBEWcarrymask
+	OpS390XMOVDEQ
+	OpS390XMOVDNE
+	OpS390XMOVDLT
+	OpS390XMOVDLE
+	OpS390XMOVDGT
+	OpS390XMOVDGE
+	OpS390XMOVDGTnoinv
+	OpS390XMOVDGEnoinv
+	OpS390XMOVBreg
+	OpS390XMOVBZreg
+	OpS390XMOVHreg
+	OpS390XMOVHZreg
+	OpS390XMOVWreg
+	OpS390XMOVWZreg
+	OpS390XMOVDconst
+	OpS390XCFDBRA
+	OpS390XCGDBRA
+	OpS390XCFEBRA
+	OpS390XCGEBRA
+	OpS390XCEFBRA
+	OpS390XCDFBRA
+	OpS390XCEGBRA
+	OpS390XCDGBRA
+	OpS390XLEDBR
+	OpS390XLDEBR
+	OpS390XMOVDaddr
+	OpS390XMOVDaddridx
+	OpS390XMOVBZload
+	OpS390XMOVBload
+	OpS390XMOVHZload
+	OpS390XMOVHload
+	OpS390XMOVWZload
+	OpS390XMOVWload
+	OpS390XMOVDload
+	OpS390XMOVWBR
+	OpS390XMOVDBR
+	OpS390XMOVHBRload
+	OpS390XMOVWBRload
+	OpS390XMOVDBRload
+	OpS390XMOVBstore
+	OpS390XMOVHstore
+	OpS390XMOVWstore
+	OpS390XMOVDstore
+	OpS390XMOVHBRstore
+	OpS390XMOVWBRstore
+	OpS390XMOVDBRstore
+	OpS390XMVC
+	OpS390XMOVBZloadidx
+	OpS390XMOVHZloadidx
+	OpS390XMOVWZloadidx
+	OpS390XMOVDloadidx
+	OpS390XMOVHBRloadidx
+	OpS390XMOVWBRloadidx
+	OpS390XMOVDBRloadidx
+	OpS390XMOVBstoreidx
+	OpS390XMOVHstoreidx
+	OpS390XMOVWstoreidx
+	OpS390XMOVDstoreidx
+	OpS390XMOVHBRstoreidx
+	OpS390XMOVWBRstoreidx
+	OpS390XMOVDBRstoreidx
+	OpS390XMOVBstoreconst
+	OpS390XMOVHstoreconst
+	OpS390XMOVWstoreconst
+	OpS390XMOVDstoreconst
+	OpS390XCLEAR
+	OpS390XCALLstatic
+	OpS390XCALLclosure
+	OpS390XCALLdefer
+	OpS390XCALLgo
+	OpS390XCALLinter
+	OpS390XInvertFlags
+	OpS390XLoweredGetG
+	OpS390XLoweredGetClosurePtr
+	OpS390XLoweredNilCheck
+	OpS390XMOVDconvert
+	OpS390XFlagEQ
+	OpS390XFlagLT
+	OpS390XFlagGT
+	OpS390XMOVWZatomicload
+	OpS390XMOVDatomicload
+	OpS390XMOVWatomicstore
+	OpS390XMOVDatomicstore
+	OpS390XLAA
+	OpS390XLAAG
+	OpS390XAddTupleFirst32
+	OpS390XAddTupleFirst64
+	OpS390XLoweredAtomicCas32
+	OpS390XLoweredAtomicCas64
+	OpS390XLoweredAtomicExchange32
+	OpS390XLoweredAtomicExchange64
+	OpS390XFLOGR
+	OpS390XSTMG2
+	OpS390XSTMG3
+	OpS390XSTMG4
+	OpS390XSTM2
+	OpS390XSTM3
+	OpS390XSTM4
+	OpS390XLoweredMove
+	OpS390XLoweredZero
 
 	OpAdd8
 	OpAdd16
@@ -362,6 +1590,8 @@ const (
 	OpHmul32u
 	OpHmul64
 	OpHmul64u
+	OpMul32uhilo
+	OpMul64uhilo
 	OpAvg64u
 	OpDiv8
 	OpDiv8u
@@ -371,6 +1601,7 @@ const (
 	OpDiv32u
 	OpDiv64
 	OpDiv64u
+	OpDiv128u
 	OpMod8
 	OpMod8u
 	OpMod16
@@ -516,12 +1747,8 @@ const (
 	OpCom16
 	OpCom32
 	OpCom64
-	OpCtz16
 	OpCtz32
 	OpCtz64
-	OpClz16
-	OpClz32
-	OpClz64
 	OpBswap32
 	OpBswap64
 	OpSqrt
@@ -549,6 +1776,10 @@ const (
 	OpStore
 	OpMove
 	OpZero
+	OpStoreWB
+	OpMoveWB
+	OpMoveWBVolatile
+	OpZeroWB
 	OpClosureCall
 	OpStaticCall
 	OpDeferCall
@@ -588,7 +1819,6 @@ const (
 	OpNilCheck
 	OpGetG
 	OpGetClosurePtr
-	OpArrayIndex
 	OpPtrIndex
 	OpOffPtr
 	OpSliceMake
@@ -610,6 +1840,9 @@ const (
 	OpStructMake3
 	OpStructMake4
 	OpStructSelect
+	OpArrayMake0
+	OpArrayMake1
+	OpArraySelect
 	OpStoreReg
 	OpLoadReg
 	OpFwdRef
@@ -618,6 +1851,40 @@ const (
 	OpVarKill
 	OpVarLive
 	OpKeepAlive
+	OpInt64Make
+	OpInt64Hi
+	OpInt64Lo
+	OpAdd32carry
+	OpAdd32withcarry
+	OpSub32carry
+	OpSub32withcarry
+	OpSignmask
+	OpZeromask
+	OpSlicemask
+	OpCvt32Uto32F
+	OpCvt32Uto64F
+	OpCvt32Fto32U
+	OpCvt64Fto32U
+	OpCvt64Uto32F
+	OpCvt64Uto64F
+	OpCvt32Fto64U
+	OpCvt64Fto64U
+	OpSelect0
+	OpSelect1
+	OpAtomicLoad32
+	OpAtomicLoad64
+	OpAtomicLoadPtr
+	OpAtomicStore32
+	OpAtomicStore64
+	OpAtomicStorePtrNoWB
+	OpAtomicExchange32
+	OpAtomicExchange64
+	OpAtomicAdd32
+	OpAtomicAdd64
+	OpAtomicCompareAndSwap32
+	OpAtomicCompareAndSwap64
+	OpAtomicAnd8
+	OpAtomicOr8
 )
 
 var opcodeTable = [...]opInfo{
@@ -628,14 +1895,15 @@ var opcodeTable = [...]opInfo{
 		argLen:       2,
 		commutative:  true,
 		resultInArg0: true,
+		usesScratch:  true,
 		asm:          x86.AADDSS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -647,11 +1915,11 @@ var opcodeTable = [...]opInfo{
 		asm:          x86.AADDSD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -659,15 +1927,15 @@ var opcodeTable = [...]opInfo{
 		name:         "SUBSS",
 		argLen:       2,
 		resultInArg0: true,
+		usesScratch:  true,
 		asm:          x86.ASUBSS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
-				{1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
-			clobbers: 2147483648, // X15
-			outputs: []regMask{
-				2147418112, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -678,12 +1946,11 @@ var opcodeTable = [...]opInfo{
 		asm:          x86.ASUBSD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
-				{1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
-			clobbers: 2147483648, // X15
-			outputs: []regMask{
-				2147418112, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -692,14 +1959,15 @@ var opcodeTable = [...]opInfo{
 		argLen:       2,
 		commutative:  true,
 		resultInArg0: true,
+		usesScratch:  true,
 		asm:          x86.AMULSS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -711,11 +1979,11 @@ var opcodeTable = [...]opInfo{
 		asm:          x86.AMULSD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -723,15 +1991,15 @@ var opcodeTable = [...]opInfo{
 		name:         "DIVSS",
 		argLen:       2,
 		resultInArg0: true,
+		usesScratch:  true,
 		asm:          x86.ADIVSS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
-				{1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
-			clobbers: 2147483648, // X15
-			outputs: []regMask{
-				2147418112, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -742,40 +2010,41 @@ var opcodeTable = [...]opInfo{
 		asm:          x86.ADIVSD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
-				{1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
-			clobbers: 2147483648, // X15
-			outputs: []regMask{
-				2147418112, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
 	{
-		name:    "MOVSSload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     x86.AMOVSS,
+		name:           "MOVSSload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVSS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
 	{
-		name:    "MOVSDload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     x86.AMOVSD,
+		name:           "MOVSDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVSD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -786,8 +2055,8 @@ var opcodeTable = [...]opInfo{
 		rematerializeable: true,
 		asm:               x86.AMOVSS,
 		reg: regInfo{
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -798,8 +2067,8 @@ var opcodeTable = [...]opInfo{
 		rematerializeable: true,
 		asm:               x86.AMOVSD,
 		reg: regInfo{
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -810,11 +2079,11 @@ var opcodeTable = [...]opInfo{
 		asm:     x86.AMOVSS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 255},   // AX CX DX BX SP BP SI DI
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -825,11 +2094,11 @@ var opcodeTable = [...]opInfo{
 		asm:     x86.AMOVSS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 255},   // AX CX DX BX SP BP SI DI
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -840,11 +2109,11 @@ var opcodeTable = [...]opInfo{
 		asm:     x86.AMOVSD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 255},   // AX CX DX BX SP BP SI DI
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
@@ -855,35 +2124,37 @@ var opcodeTable = [...]opInfo{
 		asm:     x86.AMOVSD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 255},   // AX CX DX BX SP BP SI DI
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
 			},
 		},
 	},
 	{
-		name:    "MOVSSstore",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVSS,
+		name:           "MOVSSstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVSS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
 		},
 	},
 	{
-		name:    "MOVSDstore",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVSD,
+		name:           "MOVSDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVSD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
 		},
 	},
@@ -894,9 +2165,9 @@ var opcodeTable = [...]opInfo{
 		asm:     x86.AMOVSS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 255},   // AX CX DX BX SP BP SI DI
+				{2, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
 		},
 	},
@@ -907,9 +2178,9 @@ var opcodeTable = [...]opInfo{
 		asm:     x86.AMOVSS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 255},   // AX CX DX BX SP BP SI DI
+				{2, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
 		},
 	},
@@ -920,9 +2191,9 @@ var opcodeTable = [...]opInfo{
 		asm:     x86.AMOVSD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 255},   // AX CX DX BX SP BP SI DI
+				{2, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
 		},
 	},
@@ -933,119 +2204,122 @@ var opcodeTable = [...]opInfo{
 		asm:     x86.AMOVSD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 255},   // AX CX DX BX SP BP SI DI
+				{2, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
 			},
 		},
 	},
 	{
-		name:        "ADDQ",
-		argLen:      2,
-		commutative: true,
-		asm:         x86.AADDQ,
+		name:         "ADDL",
+		argLen:       2,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          x86.AADDL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 239}, // AX CX DX BX BP SI DI
+				{0, 255}, // AX CX DX BX SP BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:        "ADDL",
-		argLen:      2,
-		commutative: true,
-		asm:         x86.AADDL,
+		name:         "ADDLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.AADDL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 255}, // AX CX DX BX SP BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:    "ADDQconst",
-		auxType: auxInt64,
-		argLen:  1,
-		asm:     x86.AADDQ,
+		name:         "ADDLcarry",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		asm:          x86.AADDL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
+				{1, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:    "ADDLconst",
-		auxType: auxInt32,
-		argLen:  1,
-		asm:     x86.AADDL,
+		name:         "ADDLconstcarry",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		asm:          x86.AADDL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:         "SUBQ",
-		argLen:       2,
+		name:         "ADCL",
+		argLen:       3,
+		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.ASUBQ,
+		clobberFlags: true,
+		asm:          x86.AADCL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
+				{1, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:         "SUBL",
+		name:         "ADCLconst",
+		auxType:      auxInt32,
 		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASUBL,
+		clobberFlags: true,
+		asm:          x86.AADCL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:         "SUBQconst",
-		auxType:      auxInt64,
-		argLen:       1,
+		name:         "SUBL",
+		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASUBQ,
+		clobberFlags: true,
+		asm:          x86.ASUBL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
+				{1, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
@@ -1054,2676 +2328,17049 @@ var opcodeTable = [...]opInfo{
 		auxType:      auxInt32,
 		argLen:       1,
 		resultInArg0: true,
+		clobberFlags: true,
 		asm:          x86.ASUBL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:         "MULQ",
+		name:         "SUBLcarry",
 		argLen:       2,
-		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.AIMULQ,
+		asm:          x86.ASUBL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
+				{1, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:         "MULL",
-		argLen:       2,
-		commutative:  true,
+		name:         "SUBLconstcarry",
+		auxType:      auxInt32,
+		argLen:       1,
 		resultInArg0: true,
-		asm:          x86.AIMULL,
+		asm:          x86.ASUBL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:         "MULQconst",
-		auxType:      auxInt64,
-		argLen:       1,
+		name:         "SBBL",
+		argLen:       3,
 		resultInArg0: true,
-		asm:          x86.AIMULQ,
+		clobberFlags: true,
+		asm:          x86.ASBBL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
+				{1, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:         "MULLconst",
+		name:         "SBBLconst",
 		auxType:      auxInt32,
-		argLen:       1,
+		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.AIMULL,
+		clobberFlags: true,
+		asm:          x86.ASBBL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:   "HMULQ",
-		argLen: 2,
-		asm:    x86.AIMULQ,
+		name:         "MULL",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AIMULL,
 		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
+				{0, 239}, // AX CX DX BX BP SI DI
+				{1, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:   "HMULL",
-		argLen: 2,
-		asm:    x86.AIMULL,
+		name:         "MULLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AIMULL,
 		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
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:   "HMULW",
-		argLen: 2,
-		asm:    x86.AIMULW,
+		name:         "HMULL",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIMULL,
 		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
+				{0, 1},   // AX
+				{1, 255}, // AX CX DX BX SP BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
 			},
 		},
 	},
 	{
-		name:   "HMULB",
-		argLen: 2,
-		asm:    x86.AIMULB,
+		name:         "HMULLU",
+		argLen:       2,
+		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
+				{0, 1},   // AX
+				{1, 255}, // AX CX DX BX SP BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
 			},
 		},
 	},
 	{
-		name:   "HMULQU",
-		argLen: 2,
-		asm:    x86.AMULQ,
+		name:         "HMULW",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIMULW,
 		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
+				{0, 1},   // AX
+				{1, 255}, // AX CX DX BX SP BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
 			},
 		},
 	},
 	{
-		name:   "HMULLU",
-		argLen: 2,
-		asm:    x86.AMULL,
+		name:         "HMULB",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIMULB,
 		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
+				{0, 1},   // AX
+				{1, 255}, // AX CX DX BX SP BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
 			},
 		},
 	},
 	{
-		name:   "HMULWU",
-		argLen: 2,
-		asm:    x86.AMULW,
+		name:         "HMULWU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AMULW,
 		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
+				{0, 1},   // AX
+				{1, 255}, // AX CX DX BX SP BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
 			},
 		},
 	},
 	{
-		name:   "HMULBU",
-		argLen: 2,
-		asm:    x86.AMULB,
+		name:         "HMULBU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AMULB,
 		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
+				{0, 1},   // AX
+				{1, 255}, // AX CX DX BX SP BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
 			},
 		},
 	},
 	{
-		name:         "AVGQU",
+		name:         "MULLQU",
 		argLen:       2,
-		commutative:  true,
-		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AMULL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 1},   // AX
+				{1, 255}, // AX CX DX BX SP BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4}, // DX
+				{1, 1}, // AX
 			},
 		},
 	},
 	{
-		name:   "DIVQ",
-		argLen: 2,
-		asm:    x86.AIDIVQ,
+		name:         "DIVL",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIDIVL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 1},   // AX
+				{1, 251}, // AX CX BX SP BP SI DI
 			},
-			clobbers: 8589934596, // DX FLAGS
-			outputs: []regMask{
-				1, // AX
+			clobbers: 4, // DX
+			outputs: []outputInfo{
+				{0, 1}, // AX
 			},
 		},
 	},
 	{
-		name:   "DIVL",
-		argLen: 2,
-		asm:    x86.AIDIVL,
+		name:         "DIVW",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIDIVW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 1},   // AX
+				{1, 251}, // AX CX BX SP BP SI DI
 			},
-			clobbers: 8589934596, // DX FLAGS
-			outputs: []regMask{
-				1, // AX
+			clobbers: 4, // DX
+			outputs: []outputInfo{
+				{0, 1}, // AX
 			},
 		},
 	},
 	{
-		name:   "DIVW",
-		argLen: 2,
-		asm:    x86.AIDIVW,
+		name:         "DIVLU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.ADIVL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 1},   // AX
+				{1, 251}, // AX CX BX SP BP SI DI
 			},
-			clobbers: 8589934596, // DX FLAGS
-			outputs: []regMask{
-				1, // AX
+			clobbers: 4, // DX
+			outputs: []outputInfo{
+				{0, 1}, // AX
 			},
 		},
 	},
 	{
-		name:   "DIVQU",
-		argLen: 2,
-		asm:    x86.ADIVQ,
+		name:         "DIVWU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.ADIVW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 1},   // AX
+				{1, 251}, // AX CX BX SP BP SI DI
 			},
-			clobbers: 8589934596, // DX FLAGS
-			outputs: []regMask{
-				1, // AX
+			clobbers: 4, // DX
+			outputs: []outputInfo{
+				{0, 1}, // AX
 			},
 		},
 	},
 	{
-		name:   "DIVLU",
-		argLen: 2,
-		asm:    x86.ADIVL,
+		name:         "MODL",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIDIVL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 1},   // AX
+				{1, 251}, // AX CX BX SP BP SI DI
 			},
-			clobbers: 8589934596, // DX FLAGS
-			outputs: []regMask{
-				1, // AX
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
 			},
 		},
 	},
 	{
-		name:   "DIVWU",
-		argLen: 2,
-		asm:    x86.ADIVW,
+		name:         "MODW",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIDIVW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 1},   // AX
+				{1, 251}, // AX CX BX SP BP SI DI
 			},
-			clobbers: 8589934596, // DX FLAGS
-			outputs: []regMask{
-				1, // AX
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
 			},
 		},
 	},
 	{
-		name:   "MODQ",
-		argLen: 2,
-		asm:    x86.AIDIVQ,
+		name:         "MODLU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.ADIVL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 1},   // AX
+				{1, 251}, // AX CX BX SP BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
 			},
 		},
 	},
 	{
-		name:   "MODL",
-		argLen: 2,
-		asm:    x86.AIDIVL,
+		name:         "MODWU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.ADIVW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 1},   // AX
+				{1, 251}, // AX CX BX SP BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
 			},
 		},
 	},
 	{
-		name:   "MODW",
-		argLen: 2,
-		asm:    x86.AIDIVW,
+		name:         "ANDL",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AANDL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
+				{1, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:   "MODQU",
-		argLen: 2,
-		asm:    x86.ADIVQ,
+		name:         "ANDLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AANDL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:   "MODLU",
-		argLen: 2,
-		asm:    x86.ADIVL,
+		name:         "ORL",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AORL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
+				{1, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:   "MODWU",
-		argLen: 2,
-		asm:    x86.ADIVW,
+		name:         "ORLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AORL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 1},     // AX
-				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				4, // DX
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:         "ANDQ",
+		name:         "XORL",
 		argLen:       2,
 		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.AANDQ,
+		clobberFlags: true,
+		asm:          x86.AXORL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 239}, // AX CX DX BX BP SI DI
+				{1, 239}, // AX CX DX BX BP SI DI
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
 			},
 		},
 	},
 	{
-		name:         "ANDL",
-		argLen:       2,
-		commutative:  true,
+		name:         "XORLconst",
+		auxType:      auxInt32,
+		argLen:       1,
 		resultInArg0: true,
-		asm:          x86.AANDL,
+		clobberFlags: true,
+		asm:          x86.AXORL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "CMPL",
+		argLen: 2,
+		asm:    x86.ACMPL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+				{1, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:   "CMPW",
+		argLen: 2,
+		asm:    x86.ACMPW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+				{1, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:   "CMPB",
+		argLen: 2,
+		asm:    x86.ACMPB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+				{1, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:    "CMPLconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     x86.ACMPL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:    "CMPWconst",
+		auxType: auxInt16,
+		argLen:  1,
+		asm:     x86.ACMPW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:    "CMPBconst",
+		auxType: auxInt8,
+		argLen:  1,
+		asm:     x86.ACMPB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:        "UCOMISS",
+		argLen:      2,
+		usesScratch: true,
+		asm:         x86.AUCOMISS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+	{
+		name:        "UCOMISD",
+		argLen:      2,
+		usesScratch: true,
+		asm:         x86.AUCOMISD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+	{
+		name:   "TESTL",
+		argLen: 2,
+		asm:    x86.ATESTL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+				{1, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:   "TESTW",
+		argLen: 2,
+		asm:    x86.ATESTW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+				{1, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:   "TESTB",
+		argLen: 2,
+		asm:    x86.ATESTB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+				{1, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:    "TESTLconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     x86.ATESTL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:    "TESTWconst",
+		auxType: auxInt16,
+		argLen:  1,
+		asm:     x86.ATESTW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:    "TESTBconst",
+		auxType: auxInt8,
+		argLen:  1,
+		asm:     x86.ATESTB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SHLL",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHLL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},   // CX
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SHLLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHLL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SHRL",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},   // CX
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SHRW",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},   // CX
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SHRB",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},   // CX
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SHRLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SHRWconst",
+		auxType:      auxInt16,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SHRBconst",
+		auxType:      auxInt8,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SARL",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},   // CX
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SARW",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},   // CX
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SARB",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},   // CX
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SARLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SARWconst",
+		auxType:      auxInt16,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SARBconst",
+		auxType:      auxInt8,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "ROLLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AROLL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "ROLWconst",
+		auxType:      auxInt16,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AROLW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "ROLBconst",
+		auxType:      auxInt8,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AROLB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "NEGL",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ANEGL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "NOTL",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ANOTL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "BSFL",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.ABSFL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "BSFW",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.ABSFW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "BSRL",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.ABSRL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "BSRW",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.ABSRW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "BSWAPL",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ABSWAPL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SQRTSD",
+		argLen: 1,
+		asm:    x86.ASQRTSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+	{
+		name:   "SBBLcarrymask",
+		argLen: 1,
+		asm:    x86.ASBBL,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETEQ",
+		argLen: 1,
+		asm:    x86.ASETEQ,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETNE",
+		argLen: 1,
+		asm:    x86.ASETNE,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETL",
+		argLen: 1,
+		asm:    x86.ASETLT,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETLE",
+		argLen: 1,
+		asm:    x86.ASETLE,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETG",
+		argLen: 1,
+		asm:    x86.ASETGT,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETGE",
+		argLen: 1,
+		asm:    x86.ASETGE,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETB",
+		argLen: 1,
+		asm:    x86.ASETCS,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETBE",
+		argLen: 1,
+		asm:    x86.ASETLS,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETA",
+		argLen: 1,
+		asm:    x86.ASETHI,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETAE",
+		argLen: 1,
+		asm:    x86.ASETCC,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SETEQF",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.ASETEQ,
+		reg: regInfo{
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 238}, // CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:         "SETNEF",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.ASETNE,
+		reg: regInfo{
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 238}, // CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETORD",
+		argLen: 1,
+		asm:    x86.ASETPC,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETNAN",
+		argLen: 1,
+		asm:    x86.ASETPS,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETGF",
+		argLen: 1,
+		asm:    x86.ASETHI,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "SETGEF",
+		argLen: 1,
+		asm:    x86.ASETCC,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "MOVBLSX",
+		argLen: 1,
+		asm:    x86.AMOVBLSX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "MOVBLZX",
+		argLen: 1,
+		asm:    x86.AMOVBLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "MOVWLSX",
+		argLen: 1,
+		asm:    x86.AMOVWLSX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "MOVWLZX",
+		argLen: 1,
+		asm:    x86.AMOVWLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:              "MOVLconst",
+		auxType:           auxInt32,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               x86.AMOVL,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:        "CVTTSD2SL",
+		argLen:      1,
+		usesScratch: true,
+		asm:         x86.ACVTTSD2SL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:        "CVTTSS2SL",
+		argLen:      1,
+		usesScratch: true,
+		asm:         x86.ACVTTSS2SL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:        "CVTSL2SS",
+		argLen:      1,
+		usesScratch: true,
+		asm:         x86.ACVTSL2SS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+	{
+		name:        "CVTSL2SD",
+		argLen:      1,
+		usesScratch: true,
+		asm:         x86.ACVTSL2SD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+	{
+		name:        "CVTSD2SS",
+		argLen:      1,
+		usesScratch: true,
+		asm:         x86.ACVTSD2SS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+	{
+		name:   "CVTSS2SD",
+		argLen: 1,
+		asm:    x86.ACVTSS2SD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+	{
+		name:         "PXOR",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		asm:          x86.APXOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+				{1, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+	{
+		name:              "LEAL",
+		auxType:           auxSymOff,
+		argLen:            1,
+		rematerializeable: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:    "LEAL1",
+		auxType: auxSymOff,
+		argLen:  2,
+		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
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:    "LEAL2",
+		auxType: auxSymOff,
+		argLen:  2,
+		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
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:    "LEAL4",
+		auxType: auxSymOff,
+		argLen:  2,
+		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
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:    "LEAL8",
+		auxType: auxSymOff,
+		argLen:  2,
+		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
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:           "MOVBload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVBLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:           "MOVBLSXload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVBLSX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:           "MOVWload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVWLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:           "MOVWLSXload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVWLSX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:           "MOVLload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:           "MOVBstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVB,
+		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:           "MOVWstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVW,
+		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:           "MOVLstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVL,
+		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,
+		argLen:  3,
+		asm:     x86.AMOVBLZX,
+		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
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:    "MOVWloadidx1",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVWLZX,
+		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
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:    "MOVWloadidx2",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVWLZX,
+		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
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:    "MOVLloadidx1",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVL,
+		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
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:    "MOVLloadidx4",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVL,
+		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
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:    "MOVBstoreidx1",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVB,
+		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:    "MOVWstoreidx1",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVW,
+		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:    "MOVWstoreidx2",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVW,
+		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:    "MOVLstoreidx1",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVL,
+		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:    "MOVLstoreidx4",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVL,
+		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:           "MOVBstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
+			},
+		},
+	},
+	{
+		name:           "MOVWstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
+			},
+		},
+	},
+	{
+		name:           "MOVLstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65791}, // AX CX DX BX SP BP SI DI SB
+			},
+		},
+	},
+	{
+		name:    "MOVBstoreconstidx1",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVB,
+		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:    "MOVWstoreconstidx1",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVW,
+		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:    "MOVWstoreconstidx2",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVW,
+		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:    "MOVLstoreconstidx1",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVL,
+		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:    "MOVLstoreconstidx4",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVL,
+		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:    "DUFFZERO",
+		auxType: auxInt64,
+		argLen:  3,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 128}, // DI
+				{1, 1},   // AX
+			},
+			clobbers: 130, // CX DI
+		},
+	},
+	{
+		name:   "REPSTOSL",
+		argLen: 4,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 128}, // DI
+				{1, 2},   // CX
+				{2, 1},   // AX
+			},
+			clobbers: 130, // CX DI
+		},
+	},
+	{
+		name:         "CALLstatic",
+		auxType:      auxSymOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7
+		},
+	},
+	{
+		name:         "CALLclosure",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 4},   // DX
+				{0, 255}, // AX CX DX BX SP BP SI DI
+			},
+			clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7
+		},
+	},
+	{
+		name:         "CALLdefer",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7
+		},
+	},
+	{
+		name:         "CALLgo",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7
+		},
+	},
+	{
+		name:         "CALLinter",
+		auxType:      auxInt64,
+		argLen:       2,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7
+		},
+	},
+	{
+		name:         "DUFFCOPY",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 128}, // DI
+				{1, 64},  // SI
+			},
+			clobbers: 194, // CX SI DI
+		},
+	},
+	{
+		name:   "REPMOVSL",
+		argLen: 4,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 128}, // DI
+				{1, 64},  // SI
+				{2, 2},   // CX
+			},
+			clobbers: 194, // CX SI DI
+		},
+	},
+	{
+		name:   "InvertFlags",
+		argLen: 1,
+		reg:    regInfo{},
+	},
+	{
+		name:   "LoweredGetG",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "LoweredGetClosurePtr",
+		argLen: 0,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4}, // DX
+			},
+		},
+	},
+	{
+		name:           "LoweredNilCheck",
+		argLen:         2,
+		clobberFlags:   true,
+		nilCheck:       true,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 255}, // AX CX DX BX SP BP SI DI
+			},
+		},
+	},
+	{
+		name:   "MOVLconvert",
+		argLen: 2,
+		asm:    x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "FlagEQ",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagLT_ULT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagLT_UGT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagGT_UGT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagGT_ULT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FCHS",
+		argLen: 1,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+	{
+		name:    "MOVSSconst1",
+		auxType: auxFloat32,
+		argLen:  0,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:    "MOVSDconst1",
+		auxType: auxFloat64,
+		argLen:  0,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+		},
+	},
+	{
+		name:   "MOVSSconst2",
+		argLen: 1,
+		asm:    x86.AMOVSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+	{
+		name:   "MOVSDconst2",
+		argLen: 1,
+		asm:    x86.AMOVSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 239}, // AX CX DX BX BP SI DI
+			},
+			outputs: []outputInfo{
+				{0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
+			},
+		},
+	},
+
+	{
+		name:         "ADDSS",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		asm:          x86.AADDSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:         "ADDSD",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		asm:          x86.AADDSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:         "SUBSS",
+		argLen:       2,
+		resultInArg0: true,
+		asm:          x86.ASUBSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:         "SUBSD",
+		argLen:       2,
+		resultInArg0: true,
+		asm:          x86.ASUBSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:         "MULSS",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		asm:          x86.AMULSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:         "MULSD",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		asm:          x86.AMULSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:         "DIVSS",
+		argLen:       2,
+		resultInArg0: true,
+		asm:          x86.ADIVSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:         "DIVSD",
+		argLen:       2,
+		resultInArg0: true,
+		asm:          x86.ADIVSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:           "MOVSSload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:           "MOVSDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:              "MOVSSconst",
+		auxType:           auxFloat32,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               x86.AMOVSS,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:              "MOVSDconst",
+		auxType:           auxFloat64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               x86.AMOVSD,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:    "MOVSSloadidx1",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:    "MOVSSloadidx4",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:    "MOVSDloadidx1",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:    "MOVSDloadidx8",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:           "MOVSSstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:           "MOVSDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVSSstoreidx1",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVSSstoreidx4",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVSDstoreidx1",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVSDstoreidx8",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:         "ADDQ",
+		argLen:       2,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          x86.AADDQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ADDL",
+		argLen:       2,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          x86.AADDL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ADDQconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.AADDQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ADDLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.AADDL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SUBQ",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASUBQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SUBL",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASUBL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SUBQconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASUBQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SUBLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASUBL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "MULQ",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AIMULQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "MULL",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AIMULL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "MULQconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AIMULQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "MULLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AIMULL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "HMULQ",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIMULQ,
+		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: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "HMULL",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIMULL,
+		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: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "HMULW",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIMULW,
+		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: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "HMULB",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIMULB,
+		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: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "HMULQU",
+		argLen:       2,
+		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: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "HMULLU",
+		argLen:       2,
+		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: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "HMULWU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AMULW,
+		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: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "HMULBU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AMULB,
+		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: 1, // AX
+			outputs: []outputInfo{
+				{0, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "AVGQU",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "DIVQ",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIDIVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1},     // AX
+				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 1}, // AX
+				{1, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "DIVL",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIDIVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1},     // AX
+				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 1}, // AX
+				{1, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "DIVW",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.AIDIVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1},     // AX
+				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 1}, // AX
+				{1, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "DIVQU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.ADIVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1},     // AX
+				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 1}, // AX
+				{1, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "DIVLU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.ADIVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1},     // AX
+				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 1}, // AX
+				{1, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "DIVWU",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          x86.ADIVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1},     // AX
+				{1, 65531}, // AX CX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 1}, // AX
+				{1, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "MULQU2",
+		argLen:       2,
+		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
+			},
+			outputs: []outputInfo{
+				{0, 4}, // DX
+				{1, 1}, // AX
+			},
+		},
+	},
+	{
+		name:         "DIVQU2",
+		argLen:       3,
+		clobberFlags: true,
+		asm:          x86.ADIVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4},     // DX
+				{1, 1},     // AX
+				{2, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 1}, // AX
+				{1, 4}, // DX
+			},
+		},
+	},
+	{
+		name:         "ANDQ",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AANDQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ANDL",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AANDL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ANDQconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AANDQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ANDLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AANDL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ORQ",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AORQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ORL",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AORL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ORQconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AORQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ORLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AORL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "XORQ",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AXORQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "XORL",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AXORL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "XORQconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AXORQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "XORLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AXORL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "CMPQ",
+		argLen: 2,
+		asm:    x86.ACMPQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "CMPL",
+		argLen: 2,
+		asm:    x86.ACMPL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "CMPW",
+		argLen: 2,
+		asm:    x86.ACMPW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "CMPB",
+		argLen: 2,
+		asm:    x86.ACMPB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "CMPQconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     x86.ACMPQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "CMPLconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     x86.ACMPL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "CMPWconst",
+		auxType: auxInt16,
+		argLen:  1,
+		asm:     x86.ACMPW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "CMPBconst",
+		auxType: auxInt8,
+		argLen:  1,
+		asm:     x86.ACMPB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "UCOMISS",
+		argLen: 2,
+		asm:    x86.AUCOMISS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:   "UCOMISD",
+		argLen: 2,
+		asm:    x86.AUCOMISD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:   "TESTQ",
+		argLen: 2,
+		asm:    x86.ATESTQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "TESTL",
+		argLen: 2,
+		asm:    x86.ATESTL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "TESTW",
+		argLen: 2,
+		asm:    x86.ATESTW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "TESTB",
+		argLen: 2,
+		asm:    x86.ATESTB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "TESTQconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     x86.ATESTQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "TESTLconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     x86.ATESTL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "TESTWconst",
+		auxType: auxInt16,
+		argLen:  1,
+		asm:     x86.ATESTW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "TESTBconst",
+		auxType: auxInt8,
+		argLen:  1,
+		asm:     x86.ATESTB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHLQ",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHLQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // CX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHLL",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHLL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // CX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHLQconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHLQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHLLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHLL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHRQ",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // CX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHRL",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // CX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHRW",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // CX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHRB",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // CX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHRQconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHRLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHRWconst",
+		auxType:      auxInt16,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SHRBconst",
+		auxType:      auxInt8,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASHRB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SARQ",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // CX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SARL",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // CX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SARW",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // CX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SARB",
+		argLen:       2,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // CX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SARQconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SARLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SARWconst",
+		auxType:      auxInt16,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SARBconst",
+		auxType:      auxInt8,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ASARB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ROLQconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AROLQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ROLLconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AROLL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ROLWconst",
+		auxType:      auxInt16,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AROLW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "ROLBconst",
+		auxType:      auxInt8,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.AROLB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "NEGQ",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ANEGQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "NEGL",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ANEGL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "NOTQ",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ANOTQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "NOTL",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ANOTL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "BSFQ",
+		argLen: 1,
+		asm:    x86.ABSFQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "BSFL",
+		argLen: 1,
+		asm:    x86.ABSFL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "CMOVQEQ",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          x86.ACMOVQEQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "CMOVLEQ",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          x86.ACMOVLEQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "BSWAPQ",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ABSWAPQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "BSWAPL",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          x86.ABSWAPL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SQRTSD",
+		argLen: 1,
+		asm:    x86.ASQRTSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:   "SBBQcarrymask",
+		argLen: 1,
+		asm:    x86.ASBBQ,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SBBLcarrymask",
+		argLen: 1,
+		asm:    x86.ASBBL,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETEQ",
+		argLen: 1,
+		asm:    x86.ASETEQ,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETNE",
+		argLen: 1,
+		asm:    x86.ASETNE,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETL",
+		argLen: 1,
+		asm:    x86.ASETLT,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETLE",
+		argLen: 1,
+		asm:    x86.ASETLE,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETG",
+		argLen: 1,
+		asm:    x86.ASETGT,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETGE",
+		argLen: 1,
+		asm:    x86.ASETGE,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETB",
+		argLen: 1,
+		asm:    x86.ASETCS,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETBE",
+		argLen: 1,
+		asm:    x86.ASETLS,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETA",
+		argLen: 1,
+		asm:    x86.ASETHI,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETAE",
+		argLen: 1,
+		asm:    x86.ASETCC,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SETEQF",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.ASETEQ,
+		reg: regInfo{
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:         "SETNEF",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          x86.ASETNE,
+		reg: regInfo{
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{0, 65518}, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETORD",
+		argLen: 1,
+		asm:    x86.ASETPC,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETNAN",
+		argLen: 1,
+		asm:    x86.ASETPS,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETGF",
+		argLen: 1,
+		asm:    x86.ASETHI,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "SETGEF",
+		argLen: 1,
+		asm:    x86.ASETCC,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "MOVBQSX",
+		argLen: 1,
+		asm:    x86.AMOVBQSX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "MOVBQZX",
+		argLen: 1,
+		asm:    x86.AMOVBLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "MOVWQSX",
+		argLen: 1,
+		asm:    x86.AMOVWQSX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "MOVWQZX",
+		argLen: 1,
+		asm:    x86.AMOVWLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "MOVLQSX",
+		argLen: 1,
+		asm:    x86.AMOVLQSX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "MOVLQZX",
+		argLen: 1,
+		asm:    x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:              "MOVLconst",
+		auxType:           auxInt32,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               x86.AMOVL,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:              "MOVQconst",
+		auxType:           auxInt64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               x86.AMOVQ,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "CVTTSD2SL",
+		argLen: 1,
+		asm:    x86.ACVTTSD2SL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "CVTTSD2SQ",
+		argLen: 1,
+		asm:    x86.ACVTTSD2SQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "CVTTSS2SL",
+		argLen: 1,
+		asm:    x86.ACVTTSS2SL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "CVTTSS2SQ",
+		argLen: 1,
+		asm:    x86.ACVTTSS2SQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "CVTSL2SS",
+		argLen: 1,
+		asm:    x86.ACVTSL2SS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:   "CVTSL2SD",
+		argLen: 1,
+		asm:    x86.ACVTSL2SD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:   "CVTSQ2SS",
+		argLen: 1,
+		asm:    x86.ACVTSQ2SS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:   "CVTSQ2SD",
+		argLen: 1,
+		asm:    x86.ACVTSQ2SD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:   "CVTSD2SS",
+		argLen: 1,
+		asm:    x86.ACVTSD2SS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:   "CVTSS2SD",
+		argLen: 1,
+		asm:    x86.ACVTSS2SD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:         "PXOR",
+		argLen:       2,
+		commutative:  true,
+		resultInArg0: true,
+		asm:          x86.APXOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:              "LEAQ",
+		auxType:           auxSymOff,
+		argLen:            1,
+		rematerializeable: true,
+		asm:               x86.ALEAQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "LEAQ1",
+		auxType: auxSymOff,
+		argLen:  2,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "LEAQ2",
+		auxType: auxSymOff,
+		argLen:  2,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "LEAQ4",
+		auxType: auxSymOff,
+		argLen:  2,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "LEAQ8",
+		auxType: auxSymOff,
+		argLen:  2,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:              "LEAL",
+		auxType:           auxSymOff,
+		argLen:            1,
+		rematerializeable: true,
+		asm:               x86.ALEAL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "MOVBload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVBLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "MOVBQSXload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVBQSX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "MOVWload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVWLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "MOVWQSXload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVWQSX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "MOVLload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "MOVLQSXload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVLQSX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "MOVQload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "MOVBstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:           "MOVWstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:           "MOVLstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:           "MOVQstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:           "MOVOload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVUPS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:           "MOVOstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVUPS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVBloadidx1",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVBLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "MOVWloadidx1",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVWLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "MOVWloadidx2",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVWLZX,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "MOVLloadidx1",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "MOVLloadidx4",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "MOVQloadidx1",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "MOVQloadidx8",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:    "MOVBstoreidx1",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVWstoreidx1",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVWstoreidx2",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVLstoreidx1",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVLstoreidx4",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVQstoreidx1",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVQstoreidx8",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:           "MOVBstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:           "MOVWstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:           "MOVLstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:           "MOVQstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVBstoreconstidx1",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVWstoreconstidx1",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVWstoreconstidx2",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVLstoreconstidx1",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVLstoreconstidx4",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVQstoreconstidx1",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:    "MOVQstoreconstidx8",
+		auxType: auxSymValAndOff,
+		argLen:  3,
+		asm:     x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:         "DUFFZERO",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 128},   // DI
+				{1, 65536}, // X0
+			},
+			clobbers: 128, // DI
+		},
+	},
+	{
+		name:              "MOVOconst",
+		auxType:           auxInt128,
+		argLen:            0,
+		rematerializeable: true,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			},
+		},
+	},
+	{
+		name:   "REPSTOSQ",
+		argLen: 4,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 128}, // DI
+				{1, 2},   // CX
+				{2, 1},   // AX
+			},
+			clobbers: 130, // CX DI
+		},
+	},
+	{
+		name:         "CALLstatic",
+		auxType:      auxSymOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4294967279, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+		},
+	},
+	{
+		name:         "CALLclosure",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 4},     // DX
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			clobbers: 4294967279, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+		},
+	},
+	{
+		name:         "CALLdefer",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4294967279, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+		},
+	},
+	{
+		name:         "CALLgo",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4294967279, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+		},
+	},
+	{
+		name:         "CALLinter",
+		auxType:      auxInt64,
+		argLen:       2,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			clobbers: 4294967279, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+		},
+	},
+	{
+		name:         "DUFFCOPY",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 128}, // DI
+				{1, 64},  // SI
+			},
+			clobbers: 65728, // SI DI X0
+		},
+	},
+	{
+		name:   "REPMOVSQ",
+		argLen: 4,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 128}, // DI
+				{1, 64},  // SI
+				{2, 2},   // CX
+			},
+			clobbers: 194, // CX SI DI
+		},
+	},
+	{
+		name:   "InvertFlags",
+		argLen: 1,
+		reg:    regInfo{},
+	},
+	{
+		name:   "LoweredGetG",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "LoweredGetClosurePtr",
+		argLen: 0,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4}, // DX
+			},
+		},
+	},
+	{
+		name:           "LoweredNilCheck",
+		argLen:         2,
+		clobberFlags:   true,
+		nilCheck:       true,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "MOVQconvert",
+		argLen: 2,
+		asm:    x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "MOVLconvert",
+		argLen: 2,
+		asm:    x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "FlagEQ",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagLT_ULT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagLT_UGT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagGT_UGT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagGT_ULT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:           "MOVLatomicload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "MOVQatomicload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            x86.AMOVQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "XCHGL",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		faultOnNilArg1: true,
+		asm:            x86.AXCHGL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "XCHGQ",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		faultOnNilArg1: true,
+		asm:            x86.AXCHGQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "XADDLlock",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            x86.AXADDL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "XADDQlock",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            x86.AXADDQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			outputs: []outputInfo{
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:   "AddTupleFirst32",
+		argLen: 2,
+		reg:    regInfo{},
+	},
+	{
+		name:   "AddTupleFirst64",
+		argLen: 2,
+		reg:    regInfo{},
+	},
+	{
+		name:           "CMPXCHGLlock",
+		auxType:        auxSymOff,
+		argLen:         4,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            x86.ACMPXCHGL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 1},     // AX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "CMPXCHGQlock",
+		auxType:        auxSymOff,
+		argLen:         4,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            x86.ACMPXCHGQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 1},     // AX
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{2, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+			clobbers: 1, // AX
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			},
+		},
+	},
+	{
+		name:           "ANDBlock",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            x86.AANDB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+	{
+		name:           "ORBlock",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            x86.AORB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+			},
+		},
+	},
+
+	{
+		name:        "ADD",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADDconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 30719}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SUB",
+		argLen: 2,
+		asm:    arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SUBconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "RSB",
+		argLen: 2,
+		asm:    arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSBconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:        "MUL",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AMUL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:        "HMUL",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AMULL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:        "HMULU",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AMULLU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:         "UDIVrtcall",
+		argLen:       2,
+		clobberFlags: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 2}, // R1
+				{1, 1}, // R0
+			},
+			clobbers: 16396, // R2 R3 R14
+			outputs: []outputInfo{
+				{0, 1}, // R0
+				{1, 2}, // R1
+			},
+		},
+	},
+	{
+		name:        "ADDS",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADDSconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:        "ADC",
+		argLen:      3,
+		commutative: true,
+		asm:         arm.AADC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADCconst",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AADC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SUBS",
+		argLen: 2,
+		asm:    arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SUBSconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSBSconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SBC",
+		argLen: 3,
+		asm:    arm.ASBC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SBCconst",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ASBC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSCconst",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ARSC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:        "MULLU",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AMULLU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MULA",
+		argLen: 3,
+		asm:    arm.AMULA,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:        "ADDF",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AADDF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:        "ADDD",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AADDD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "SUBF",
+		argLen: 2,
+		asm:    arm.ASUBF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "SUBD",
+		argLen: 2,
+		asm:    arm.ASUBD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:        "MULF",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AMULF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:        "MULD",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AMULD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "DIVF",
+		argLen: 2,
+		asm:    arm.ADIVF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "DIVD",
+		argLen: 2,
+		asm:    arm.ADIVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:        "AND",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ANDconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:        "OR",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ORconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:        "XOR",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "XORconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "BIC",
+		argLen: 2,
+		asm:    arm.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "BICconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MVN",
+		argLen: 1,
+		asm:    arm.AMVN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "NEGF",
+		argLen: 1,
+		asm:    arm.ANEGF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "NEGD",
+		argLen: 1,
+		asm:    arm.ANEGD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "SQRTD",
+		argLen: 1,
+		asm:    arm.ASQRTD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "CLZ",
+		argLen: 1,
+		asm:    arm.ACLZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SLL",
+		argLen: 2,
+		asm:    arm.ASLL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SLLconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ASLL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SRL",
+		argLen: 2,
+		asm:    arm.ASRL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SRLconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ASRL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SRA",
+		argLen: 2,
+		asm:    arm.ASRA,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SRAconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ASRA,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SRRconst",
+		auxType: auxInt32,
+		argLen:  1,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADDshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADDshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADDshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SUBshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SUBshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SUBshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSBshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSBshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSBshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ANDshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ANDshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ANDshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ORshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ORshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ORshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "XORshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "XORshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "XORshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "XORshiftRR",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "BICshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "BICshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "BICshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "MVNshiftLL",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.AMVN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "MVNshiftRL",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.AMVN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "MVNshiftRA",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.AMVN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADCshiftLL",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.AADC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADCshiftRL",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.AADC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADCshiftRA",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.AADC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SBCshiftLL",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.ASBC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SBCshiftRL",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.ASBC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SBCshiftRA",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.ASBC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSCshiftLL",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.ARSC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSCshiftRL",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.ARSC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSCshiftRA",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.ARSC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADDSshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADDSshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "ADDSshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SUBSshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SUBSshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "SUBSshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSBSshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSBSshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "RSBSshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ADDshiftLLreg",
+		argLen: 3,
+		asm:    arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ADDshiftRLreg",
+		argLen: 3,
+		asm:    arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ADDshiftRAreg",
+		argLen: 3,
+		asm:    arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SUBshiftLLreg",
+		argLen: 3,
+		asm:    arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SUBshiftRLreg",
+		argLen: 3,
+		asm:    arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SUBshiftRAreg",
+		argLen: 3,
+		asm:    arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "RSBshiftLLreg",
+		argLen: 3,
+		asm:    arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "RSBshiftRLreg",
+		argLen: 3,
+		asm:    arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "RSBshiftRAreg",
+		argLen: 3,
+		asm:    arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ANDshiftLLreg",
+		argLen: 3,
+		asm:    arm.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ANDshiftRLreg",
+		argLen: 3,
+		asm:    arm.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ANDshiftRAreg",
+		argLen: 3,
+		asm:    arm.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ORshiftLLreg",
+		argLen: 3,
+		asm:    arm.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ORshiftRLreg",
+		argLen: 3,
+		asm:    arm.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ORshiftRAreg",
+		argLen: 3,
+		asm:    arm.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "XORshiftLLreg",
+		argLen: 3,
+		asm:    arm.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "XORshiftRLreg",
+		argLen: 3,
+		asm:    arm.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "XORshiftRAreg",
+		argLen: 3,
+		asm:    arm.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "BICshiftLLreg",
+		argLen: 3,
+		asm:    arm.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "BICshiftRLreg",
+		argLen: 3,
+		asm:    arm.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "BICshiftRAreg",
+		argLen: 3,
+		asm:    arm.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MVNshiftLLreg",
+		argLen: 2,
+		asm:    arm.AMVN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MVNshiftRLreg",
+		argLen: 2,
+		asm:    arm.AMVN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MVNshiftRAreg",
+		argLen: 2,
+		asm:    arm.AMVN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ADCshiftLLreg",
+		argLen: 4,
+		asm:    arm.AADC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ADCshiftRLreg",
+		argLen: 4,
+		asm:    arm.AADC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ADCshiftRAreg",
+		argLen: 4,
+		asm:    arm.AADC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SBCshiftLLreg",
+		argLen: 4,
+		asm:    arm.ASBC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SBCshiftRLreg",
+		argLen: 4,
+		asm:    arm.ASBC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SBCshiftRAreg",
+		argLen: 4,
+		asm:    arm.ASBC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "RSCshiftLLreg",
+		argLen: 4,
+		asm:    arm.ARSC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "RSCshiftRLreg",
+		argLen: 4,
+		asm:    arm.ARSC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "RSCshiftRAreg",
+		argLen: 4,
+		asm:    arm.ARSC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ADDSshiftLLreg",
+		argLen: 3,
+		asm:    arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ADDSshiftRLreg",
+		argLen: 3,
+		asm:    arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "ADDSshiftRAreg",
+		argLen: 3,
+		asm:    arm.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SUBSshiftLLreg",
+		argLen: 3,
+		asm:    arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SUBSshiftRLreg",
+		argLen: 3,
+		asm:    arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SUBSshiftRAreg",
+		argLen: 3,
+		asm:    arm.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "RSBSshiftLLreg",
+		argLen: 3,
+		asm:    arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "RSBSshiftRLreg",
+		argLen: 3,
+		asm:    arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "RSBSshiftRAreg",
+		argLen: 3,
+		asm:    arm.ARSB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "CMP",
+		argLen: 2,
+		asm:    arm.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:    "CMPconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:   "CMN",
+		argLen: 2,
+		asm:    arm.ACMN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:    "CMNconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ACMN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:        "TST",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.ATST,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:    "TSTconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ATST,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:        "TEQ",
+		argLen:      2,
+		commutative: true,
+		asm:         arm.ATEQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:    "TEQconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm.ATEQ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:   "CMPF",
+		argLen: 2,
+		asm:    arm.ACMPF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "CMPD",
+		argLen: 2,
+		asm:    arm.ACMPD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:    "CMPshiftLL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:    "CMPshiftRL",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:    "CMPshiftRA",
+		auxType: auxInt32,
+		argLen:  2,
+		asm:     arm.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{1, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:   "CMPshiftLLreg",
+		argLen: 3,
+		asm:    arm.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "CMPshiftRLreg",
+		argLen: 3,
+		asm:    arm.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "CMPshiftRAreg",
+		argLen: 3,
+		asm:    arm.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "CMPF0",
+		argLen: 1,
+		asm:    arm.ACMPF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "CMPD0",
+		argLen: 1,
+		asm:    arm.ACMPD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:              "MOVWconst",
+		auxType:           auxInt32,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               arm.AMOVW,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:              "MOVFconst",
+		auxType:           auxFloat64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               arm.AMOVF,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:              "MOVDconst",
+		auxType:           auxFloat64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               arm.AMOVD,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:              "MOVWaddr",
+		auxType:           auxSymOff,
+		argLen:            1,
+		rematerializeable: true,
+		asm:               arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294975488}, // SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "MOVBload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "MOVBUload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVBU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "MOVHload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "MOVHUload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVHU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "MOVWload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "MOVFload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:           "MOVDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:           "MOVBstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+		},
+	},
+	{
+		name:           "MOVHstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+		},
+	},
+	{
+		name:           "MOVWstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+		},
+	},
+	{
+		name:           "MOVFstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:           "MOVDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "MOVWloadidx",
+		argLen: 3,
+		asm:    arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "MOVWloadshiftLL",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "MOVWloadshiftRL",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:    "MOVWloadshiftRA",
+		auxType: auxInt32,
+		argLen:  3,
+		asm:     arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MOVWstoreidx",
+		argLen: 4,
+		asm:    arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{2, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+		},
+	},
+	{
+		name:    "MOVWstoreshiftLL",
+		auxType: auxInt32,
+		argLen:  4,
+		asm:     arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{2, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+		},
+	},
+	{
+		name:    "MOVWstoreshiftRL",
+		auxType: auxInt32,
+		argLen:  4,
+		asm:     arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{2, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+		},
+	},
+	{
+		name:    "MOVWstoreshiftRA",
+		auxType: auxInt32,
+		argLen:  4,
+		asm:     arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{2, 22527},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+				{0, 4294998015}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 SP R14 SB
+			},
+		},
+	},
+	{
+		name:   "MOVBreg",
+		argLen: 1,
+		asm:    arm.AMOVBS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MOVBUreg",
+		argLen: 1,
+		asm:    arm.AMOVBU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MOVHreg",
+		argLen: 1,
+		asm:    arm.AMOVHS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MOVHUreg",
+		argLen: 1,
+		asm:    arm.AMOVHU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MOVWreg",
+		argLen: 1,
+		asm:    arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:         "MOVWnop",
+		argLen:       1,
+		resultInArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MOVWF",
+		argLen: 1,
+		asm:    arm.AMOVWF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "MOVWD",
+		argLen: 1,
+		asm:    arm.AMOVWD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "MOVWUF",
+		argLen: 1,
+		asm:    arm.AMOVWF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "MOVWUD",
+		argLen: 1,
+		asm:    arm.AMOVWD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "MOVFW",
+		argLen: 1,
+		asm:    arm.AMOVFW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MOVDW",
+		argLen: 1,
+		asm:    arm.AMOVDW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MOVFWU",
+		argLen: 1,
+		asm:    arm.AMOVFW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MOVDWU",
+		argLen: 1,
+		asm:    arm.AMOVDW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "MOVFD",
+		argLen: 1,
+		asm:    arm.AMOVFD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:   "MOVDF",
+		argLen: 1,
+		asm:    arm.AMOVDF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:         "CMOVWHSconst",
+		auxType:      auxInt32,
+		argLen:       2,
+		resultInArg0: true,
+		asm:          arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:         "CMOVWLSconst",
+		auxType:      auxInt32,
+		argLen:       2,
+		resultInArg0: true,
+		asm:          arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "SRAcond",
+		argLen: 3,
+		asm:    arm.ASRA,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:         "CALLstatic",
+		auxType:      auxSymOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+		},
+	},
+	{
+		name:         "CALLclosure",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 128},   // R7
+				{0, 29695}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 SP R14
+			},
+			clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+		},
+	},
+	{
+		name:         "CALLdefer",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+		},
+	},
+	{
+		name:         "CALLgo",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+		},
+	},
+	{
+		name:         "CALLinter",
+		auxType:      auxInt64,
+		argLen:       2,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+		},
+	},
+	{
+		name:           "LoweredNilCheck",
+		argLen:         2,
+		nilCheck:       true,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+		},
+	},
+	{
+		name:   "Equal",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "NotEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "LessThan",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "LessEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "GreaterThan",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "GreaterEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "LessThanU",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "LessEqualU",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "GreaterThanU",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "GreaterEqualU",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "DUFFZERO",
+		auxType:        auxInt64,
+		argLen:         3,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 2}, // R1
+				{1, 1}, // R0
+			},
+			clobbers: 16386, // R1 R14
+		},
+	},
+	{
+		name:           "DUFFCOPY",
+		auxType:        auxInt64,
+		argLen:         3,
+		faultOnNilArg0: true,
+		faultOnNilArg1: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4}, // R2
+				{1, 2}, // R1
+			},
+			clobbers: 16391, // R0 R1 R2 R14
+		},
+	},
+	{
+		name:           "LoweredZero",
+		auxType:        auxInt64,
+		argLen:         4,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 2},     // R1
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			clobbers: 2, // R1
+		},
+	},
+	{
+		name:           "LoweredMove",
+		auxType:        auxInt64,
+		argLen:         4,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		faultOnNilArg1: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4},     // R2
+				{1, 2},     // R1
+				{2, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			clobbers: 6, // R1 R2
+		},
+	},
+	{
+		name:   "LoweredGetClosurePtr",
+		argLen: 0,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 128}, // R7
+			},
+		},
+	},
+	{
+		name:   "MOVWconvert",
+		argLen: 2,
+		asm:    arm.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 22527}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "FlagEQ",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagLT_ULT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagLT_UGT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagGT_UGT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagGT_ULT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "InvertFlags",
+		argLen: 1,
+		reg:    regInfo{},
+	},
+
+	{
+		name:        "ADD",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ADDconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1878786047}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "SUB",
+		argLen: 2,
+		asm:    arm64.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "SUBconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:        "MUL",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AMUL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:        "MULW",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AMULW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:        "MULH",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.ASMULH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:        "UMULH",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AUMULH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:        "MULL",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.ASMULL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:        "UMULL",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AUMULL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "DIV",
+		argLen: 2,
+		asm:    arm64.ASDIV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "UDIV",
+		argLen: 2,
+		asm:    arm64.AUDIV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "DIVW",
+		argLen: 2,
+		asm:    arm64.ASDIVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "UDIVW",
+		argLen: 2,
+		asm:    arm64.AUDIVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "MOD",
+		argLen: 2,
+		asm:    arm64.AREM,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "UMOD",
+		argLen: 2,
+		asm:    arm64.AUREM,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "MODW",
+		argLen: 2,
+		asm:    arm64.AREMW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "UMODW",
+		argLen: 2,
+		asm:    arm64.AUREMW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:        "FADDS",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AFADDS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:        "FADDD",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AFADDD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "FSUBS",
+		argLen: 2,
+		asm:    arm64.AFSUBS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "FSUBD",
+		argLen: 2,
+		asm:    arm64.AFSUBD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:        "FMULS",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AFMULS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:        "FMULD",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AFMULD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "FDIVS",
+		argLen: 2,
+		asm:    arm64.AFDIVS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "FDIVD",
+		argLen: 2,
+		asm:    arm64.AFDIVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:        "AND",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ANDconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:        "OR",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ORconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:        "XOR",
+		argLen:      2,
+		commutative: true,
+		asm:         arm64.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "XORconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "BIC",
+		argLen: 2,
+		asm:    arm64.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "BICconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "MVN",
+		argLen: 1,
+		asm:    arm64.AMVN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "NEG",
+		argLen: 1,
+		asm:    arm64.ANEG,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "FNEGS",
+		argLen: 1,
+		asm:    arm64.AFNEGS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "FNEGD",
+		argLen: 1,
+		asm:    arm64.AFNEGD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "FSQRTD",
+		argLen: 1,
+		asm:    arm64.AFSQRTD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "REV",
+		argLen: 1,
+		asm:    arm64.AREV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "REVW",
+		argLen: 1,
+		asm:    arm64.AREVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "REV16W",
+		argLen: 1,
+		asm:    arm64.AREV16W,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "RBIT",
+		argLen: 1,
+		asm:    arm64.ARBIT,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "RBITW",
+		argLen: 1,
+		asm:    arm64.ARBITW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "CLZ",
+		argLen: 1,
+		asm:    arm64.ACLZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "CLZW",
+		argLen: 1,
+		asm:    arm64.ACLZW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "SLL",
+		argLen: 2,
+		asm:    arm64.ALSL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "SLLconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.ALSL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "SRL",
+		argLen: 2,
+		asm:    arm64.ALSR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "SRLconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.ALSR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "SRA",
+		argLen: 2,
+		asm:    arm64.AASR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "SRAconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.AASR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "RORconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.AROR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "RORWconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.ARORW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "CMP",
+		argLen: 2,
+		asm:    arm64.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:    "CMPconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:   "CMPW",
+		argLen: 2,
+		asm:    arm64.ACMPW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:    "CMPWconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm64.ACMPW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:   "CMN",
+		argLen: 2,
+		asm:    arm64.ACMN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:    "CMNconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     arm64.ACMN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:   "CMNW",
+		argLen: 2,
+		asm:    arm64.ACMNW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:    "CMNWconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     arm64.ACMNW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:   "FCMPS",
+		argLen: 2,
+		asm:    arm64.AFCMPS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "FCMPD",
+		argLen: 2,
+		asm:    arm64.AFCMPD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:    "ADDshiftLL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ADDshiftRL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ADDshiftRA",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "SUBshiftLL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "SUBshiftRL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "SUBshiftRA",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ANDshiftLL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ANDshiftRL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ANDshiftRA",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ORshiftLL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ORshiftRL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "ORshiftRA",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "XORshiftLL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "XORshiftRL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "XORshiftRA",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.AEOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "BICshiftLL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "BICshiftRL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "BICshiftRA",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ABIC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:    "CMPshiftLL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:    "CMPshiftRL",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:    "CMPshiftRA",
+		auxType: auxInt64,
+		argLen:  2,
+		asm:     arm64.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:              "MOVDconst",
+		auxType:           auxInt64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               arm64.AMOVD,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:              "FMOVSconst",
+		auxType:           auxFloat64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               arm64.AFMOVS,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:              "FMOVDconst",
+		auxType:           auxFloat64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               arm64.AFMOVD,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:              "MOVDaddr",
+		auxType:           auxSymOff,
+		argLen:            1,
+		rematerializeable: true,
+		asm:               arm64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372037928517632}, // SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "MOVBload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "MOVBUload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVBU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "MOVHload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "MOVHUload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVHU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "MOVWload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "MOVWUload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVWU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "MOVDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "FMOVSload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AFMOVS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:           "FMOVDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AFMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:           "MOVBstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:           "MOVHstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:           "MOVWstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:           "MOVDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:           "FMOVSstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm64.AFMOVS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:           "FMOVDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm64.AFMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+				{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:           "MOVBstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:           "MOVHstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:           "MOVWstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:           "MOVDstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:   "MOVBreg",
+		argLen: 1,
+		asm:    arm64.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "MOVBUreg",
+		argLen: 1,
+		asm:    arm64.AMOVBU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "MOVHreg",
+		argLen: 1,
+		asm:    arm64.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "MOVHUreg",
+		argLen: 1,
+		asm:    arm64.AMOVHU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "MOVWreg",
+		argLen: 1,
+		asm:    arm64.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "MOVWUreg",
+		argLen: 1,
+		asm:    arm64.AMOVWU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "MOVDreg",
+		argLen: 1,
+		asm:    arm64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:         "MOVDnop",
+		argLen:       1,
+		resultInArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "SCVTFWS",
+		argLen: 1,
+		asm:    arm64.ASCVTFWS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "SCVTFWD",
+		argLen: 1,
+		asm:    arm64.ASCVTFWD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "UCVTFWS",
+		argLen: 1,
+		asm:    arm64.AUCVTFWS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "UCVTFWD",
+		argLen: 1,
+		asm:    arm64.AUCVTFWD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "SCVTFS",
+		argLen: 1,
+		asm:    arm64.ASCVTFS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "SCVTFD",
+		argLen: 1,
+		asm:    arm64.ASCVTFD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "UCVTFS",
+		argLen: 1,
+		asm:    arm64.AUCVTFS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "UCVTFD",
+		argLen: 1,
+		asm:    arm64.AUCVTFD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "FCVTZSSW",
+		argLen: 1,
+		asm:    arm64.AFCVTZSSW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "FCVTZSDW",
+		argLen: 1,
+		asm:    arm64.AFCVTZSDW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "FCVTZUSW",
+		argLen: 1,
+		asm:    arm64.AFCVTZUSW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "FCVTZUDW",
+		argLen: 1,
+		asm:    arm64.AFCVTZUDW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "FCVTZSS",
+		argLen: 1,
+		asm:    arm64.AFCVTZSS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "FCVTZSD",
+		argLen: 1,
+		asm:    arm64.AFCVTZSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "FCVTZUS",
+		argLen: 1,
+		asm:    arm64.AFCVTZUS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "FCVTZUD",
+		argLen: 1,
+		asm:    arm64.AFCVTZUD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "FCVTSD",
+		argLen: 1,
+		asm:    arm64.AFCVTSD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "FCVTDS",
+		argLen: 1,
+		asm:    arm64.AFCVTDS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "CSELULT",
+		argLen: 3,
+		asm:    arm64.ACSEL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+				{1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "CSELULT0",
+		argLen: 2,
+		asm:    arm64.ACSEL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:         "CALLstatic",
+		auxType:      auxSymOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+		},
+	},
+	{
+		name:         "CALLclosure",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 67108864},   // R26
+				{0, 1744568319}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 SP
+			},
+			clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+		},
+	},
+	{
+		name:         "CALLdefer",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+		},
+	},
+	{
+		name:         "CALLgo",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+		},
+	},
+	{
+		name:         "CALLinter",
+		auxType:      auxInt64,
+		argLen:       2,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+		},
+	},
+	{
+		name:           "LoweredNilCheck",
+		argLen:         2,
+		nilCheck:       true,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+		},
+	},
+	{
+		name:   "Equal",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "NotEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "LessThan",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "LessEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "GreaterThan",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "GreaterEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "LessThanU",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "LessEqualU",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "GreaterThanU",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "GreaterEqualU",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "DUFFZERO",
+		auxType:        auxInt64,
+		argLen:         2,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			clobbers: 536936448, // R16 R30
+		},
+	},
+	{
+		name:           "LoweredZero",
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 65536},     // R16
+				{1, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			clobbers: 65536, // R16
+		},
+	},
+	{
+		name:           "DUFFCOPY",
+		auxType:        auxInt64,
+		argLen:         3,
+		faultOnNilArg0: true,
+		faultOnNilArg1: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 131072}, // R17
+				{1, 65536},  // R16
+			},
+			clobbers: 537067520, // R16 R17 R30
+		},
+	},
+	{
+		name:           "LoweredMove",
+		argLen:         4,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		faultOnNilArg1: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 131072},    // R17
+				{1, 65536},     // R16
+				{2, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+			clobbers: 196608, // R16 R17
+		},
+	},
+	{
+		name:   "LoweredGetClosurePtr",
+		argLen: 0,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 67108864}, // R26
+			},
+		},
+	},
+	{
+		name:   "MOVDconvert",
+		argLen: 2,
+		asm:    arm64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:   "FlagEQ",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagLT_ULT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagLT_UGT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagGT_UGT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagGT_ULT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "InvertFlags",
+		argLen: 1,
+		reg:    regInfo{},
+	},
+	{
+		name:           "LDAR",
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.ALDAR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "LDARW",
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            arm64.ALDARW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "STLR",
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm64.ASTLR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:           "STLRW",
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm64.ASTLRW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:            "LoweredAtomicExchange64",
+		argLen:          3,
+		resultNotInArgs: true,
+		faultOnNilArg0:  true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:            "LoweredAtomicExchange32",
+		argLen:          3,
+		resultNotInArgs: true,
+		faultOnNilArg0:  true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:            "LoweredAtomicAdd64",
+		argLen:          3,
+		resultNotInArgs: true,
+		faultOnNilArg0:  true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:            "LoweredAtomicAdd32",
+		argLen:          3,
+		resultNotInArgs: true,
+		faultOnNilArg0:  true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:            "LoweredAtomicCas64",
+		argLen:          4,
+		resultNotInArgs: true,
+		clobberFlags:    true,
+		faultOnNilArg0:  true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{2, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:            "LoweredAtomicCas32",
+		argLen:          4,
+		resultNotInArgs: true,
+		clobberFlags:    true,
+		faultOnNilArg0:  true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{2, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30
+			},
+		},
+	},
+	{
+		name:           "LoweredAtomicAnd8",
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm64.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+	{
+		name:           "LoweredAtomicOr8",
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            arm64.AORR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 805044223},           // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30
+				{0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB
+			},
+		},
+	},
+
+	{
+		name:        "ADD",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AADDU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "ADDconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.AADDU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 536870910}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "SUB",
+		argLen: 2,
+		asm:    mips.ASUBU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "SUBconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.ASUBU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:        "MUL",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AMUL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			clobbers: 105553116266496, // HI LO
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:        "MULT",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AMUL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 35184372088832}, // HI
+				{1, 70368744177664}, // LO
+			},
+		},
+	},
+	{
+		name:        "MULTU",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AMULU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 35184372088832}, // HI
+				{1, 70368744177664}, // LO
+			},
+		},
+	},
+	{
+		name:   "DIV",
+		argLen: 2,
+		asm:    mips.ADIV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 35184372088832}, // HI
+				{1, 70368744177664}, // LO
+			},
+		},
+	},
+	{
+		name:   "DIVU",
+		argLen: 2,
+		asm:    mips.ADIVU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 35184372088832}, // HI
+				{1, 70368744177664}, // LO
+			},
+		},
+	},
+	{
+		name:        "ADDF",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AADDF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:        "ADDD",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AADDD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "SUBF",
+		argLen: 2,
+		asm:    mips.ASUBF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "SUBD",
+		argLen: 2,
+		asm:    mips.ASUBD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:        "MULF",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AMULF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:        "MULD",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AMULD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "DIVF",
+		argLen: 2,
+		asm:    mips.ADIVF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "DIVD",
+		argLen: 2,
+		asm:    mips.ADIVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:        "AND",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "ANDconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:        "OR",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "ORconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.AOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:        "XOR",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AXOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "XORconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.AXOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:        "NOR",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.ANOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "NORconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.ANOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "NEG",
+		argLen: 1,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "NEGF",
+		argLen: 1,
+		asm:    mips.ANEGF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "NEGD",
+		argLen: 1,
+		asm:    mips.ANEGD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "SQRTD",
+		argLen: 1,
+		asm:    mips.ASQRTD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "SLL",
+		argLen: 2,
+		asm:    mips.ASLL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "SLLconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.ASLL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "SRL",
+		argLen: 2,
+		asm:    mips.ASRL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "SRLconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.ASRL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "SRA",
+		argLen: 2,
+		asm:    mips.ASRA,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "SRAconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.ASRA,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "CLZ",
+		argLen: 1,
+		asm:    mips.ACLZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "SGT",
+		argLen: 2,
+		asm:    mips.ASGT,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "SGTconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.ASGT,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "SGTzero",
+		argLen: 1,
+		asm:    mips.ASGT,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "SGTU",
+		argLen: 2,
+		asm:    mips.ASGTU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:    "SGTUconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     mips.ASGTU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "SGTUzero",
+		argLen: 1,
+		asm:    mips.ASGTU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "CMPEQF",
+		argLen: 2,
+		asm:    mips.ACMPEQF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "CMPEQD",
+		argLen: 2,
+		asm:    mips.ACMPEQD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "CMPGEF",
+		argLen: 2,
+		asm:    mips.ACMPGEF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "CMPGED",
+		argLen: 2,
+		asm:    mips.ACMPGED,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "CMPGTF",
+		argLen: 2,
+		asm:    mips.ACMPGTF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "CMPGTD",
+		argLen: 2,
+		asm:    mips.ACMPGTD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{1, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:              "MOVWconst",
+		auxType:           auxInt32,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               mips.AMOVW,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:              "MOVFconst",
+		auxType:           auxFloat32,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               mips.AMOVF,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:              "MOVDconst",
+		auxType:           auxFloat64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               mips.AMOVD,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:              "MOVWaddr",
+		auxType:           auxSymOff,
+		argLen:            1,
+		rematerializeable: true,
+		asm:               mips.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140737555464192}, // SP SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:           "MOVBload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:           "MOVBUload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVBU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:           "MOVHload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:           "MOVHUload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVHU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:           "MOVWload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:           "MOVFload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:           "MOVDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:           "MOVBstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 469762046},       // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVHstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 469762046},       // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVWstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 469762046},       // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVFstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 35183835217920},  // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 35183835217920},  // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVBstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVHstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVWstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:   "MOVBreg",
+		argLen: 1,
+		asm:    mips.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "MOVBUreg",
+		argLen: 1,
+		asm:    mips.AMOVBU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "MOVHreg",
+		argLen: 1,
+		asm:    mips.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "MOVHUreg",
+		argLen: 1,
+		asm:    mips.AMOVHU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "MOVWreg",
+		argLen: 1,
+		asm:    mips.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:         "MOVWnop",
+		argLen:       1,
+		resultInArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:         "CMOVZ",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          mips.ACMOVZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+				{1, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+				{2, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:         "CMOVZzero",
+		argLen:       2,
+		resultInArg0: true,
+		asm:          mips.ACMOVZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+				{1, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "MOVWF",
+		argLen: 1,
+		asm:    mips.AMOVWF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "MOVWD",
+		argLen: 1,
+		asm:    mips.AMOVWD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "TRUNCFW",
+		argLen: 1,
+		asm:    mips.ATRUNCFW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "TRUNCDW",
+		argLen: 1,
+		asm:    mips.ATRUNCDW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "MOVFD",
+		argLen: 1,
+		asm:    mips.AMOVFD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:   "MOVDF",
+		argLen: 1,
+		asm:    mips.AMOVDF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+			outputs: []outputInfo{
+				{0, 35183835217920}, // F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30
+			},
+		},
+	},
+	{
+		name:         "CALLstatic",
+		auxType:      auxSymOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO
+		},
+	},
+	{
+		name:         "CALLclosure",
+		auxType:      auxInt32,
+		argLen:       3,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 4194304},   // R22
+				{0, 402653182}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP R31
+			},
+			clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO
+		},
+	},
+	{
+		name:         "CALLdefer",
+		auxType:      auxInt32,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO
+		},
+	},
+	{
+		name:         "CALLgo",
+		auxType:      auxInt32,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO
+		},
+	},
+	{
+		name:         "CALLinter",
+		auxType:      auxInt32,
+		argLen:       2,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+			clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO
+		},
+	},
+	{
+		name:           "LoweredAtomicLoad",
+		argLen:         2,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:           "LoweredAtomicStore",
+		argLen:         3,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 469762046},       // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "LoweredAtomicStorezero",
+		argLen:         2,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:            "LoweredAtomicExchange",
+		argLen:          3,
+		resultNotInArgs: true,
+		faultOnNilArg0:  true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 469762046},       // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:            "LoweredAtomicAdd",
+		argLen:          3,
+		resultNotInArgs: true,
+		faultOnNilArg0:  true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 469762046},       // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:            "LoweredAtomicAddconst",
+		auxType:         auxInt32,
+		argLen:          2,
+		resultNotInArgs: true,
+		faultOnNilArg0:  true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:            "LoweredAtomicCas",
+		argLen:          4,
+		resultNotInArgs: true,
+		faultOnNilArg0:  true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 469762046},       // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{2, 469762046},       // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:           "LoweredAtomicAnd",
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 469762046},       // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "LoweredAtomicOr",
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 469762046},       // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+				{0, 140738025226238}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "LoweredZero",
+		auxType:        auxInt32,
+		argLen:         3,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 2},         // R1
+				{1, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+			clobbers: 2, // R1
+		},
+	},
+	{
+		name:           "LoweredMove",
+		auxType:        auxInt32,
+		argLen:         4,
+		faultOnNilArg0: true,
+		faultOnNilArg1: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4},         // R2
+				{1, 2},         // R1
+				{2, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+			clobbers: 6, // R1 R2
+		},
+	},
+	{
+		name:           "LoweredNilCheck",
+		argLen:         2,
+		nilCheck:       true,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+		},
+	},
+	{
+		name:   "FPFlagTrue",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "FPFlagFalse",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+	{
+		name:   "LoweredGetClosurePtr",
+		argLen: 0,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4194304}, // R22
+			},
+		},
+	},
+	{
+		name:   "MOVWconvert",
+		argLen: 2,
+		asm:    mips.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 469762046}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31
+			},
+			outputs: []outputInfo{
+				{0, 335544318}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31
+			},
+		},
+	},
+
+	{
+		name:        "ADDV",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AADDVU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "ADDVconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.AADDVU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 268435454}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "SUBV",
+		argLen: 2,
+		asm:    mips.ASUBVU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "SUBVconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.ASUBVU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:        "MULV",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AMULV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504606846976}, // HI
+				{1, 2305843009213693952}, // LO
+			},
+		},
+	},
+	{
+		name:        "MULVU",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AMULVU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504606846976}, // HI
+				{1, 2305843009213693952}, // LO
+			},
+		},
+	},
+	{
+		name:   "DIVV",
+		argLen: 2,
+		asm:    mips.ADIVV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504606846976}, // HI
+				{1, 2305843009213693952}, // LO
+			},
+		},
+	},
+	{
+		name:   "DIVVU",
+		argLen: 2,
+		asm:    mips.ADIVVU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504606846976}, // HI
+				{1, 2305843009213693952}, // LO
+			},
+		},
+	},
+	{
+		name:        "ADDF",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AADDF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:        "ADDD",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AADDD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "SUBF",
+		argLen: 2,
+		asm:    mips.ASUBF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "SUBD",
+		argLen: 2,
+		asm:    mips.ASUBD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:        "MULF",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AMULF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:        "MULD",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AMULD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "DIVF",
+		argLen: 2,
+		asm:    mips.ADIVF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "DIVD",
+		argLen: 2,
+		asm:    mips.ADIVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:        "AND",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "ANDconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:        "OR",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "ORconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.AOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:        "XOR",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.AXOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "XORconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.AXOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:        "NOR",
+		argLen:      2,
+		commutative: true,
+		asm:         mips.ANOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "NORconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.ANOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "NEGV",
+		argLen: 1,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "NEGF",
+		argLen: 1,
+		asm:    mips.ANEGF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "NEGD",
+		argLen: 1,
+		asm:    mips.ANEGD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "SLLV",
+		argLen: 2,
+		asm:    mips.ASLLV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "SLLVconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.ASLLV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "SRLV",
+		argLen: 2,
+		asm:    mips.ASRLV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "SRLVconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.ASRLV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "SRAV",
+		argLen: 2,
+		asm:    mips.ASRAV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "SRAVconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.ASRAV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "SGT",
+		argLen: 2,
+		asm:    mips.ASGT,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "SGTconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.ASGT,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "SGTU",
+		argLen: 2,
+		asm:    mips.ASGTU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{1, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:    "SGTUconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     mips.ASGTU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "CMPEQF",
+		argLen: 2,
+		asm:    mips.ACMPEQF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "CMPEQD",
+		argLen: 2,
+		asm:    mips.ACMPEQD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "CMPGEF",
+		argLen: 2,
+		asm:    mips.ACMPGEF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "CMPGED",
+		argLen: 2,
+		asm:    mips.ACMPGED,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "CMPGTF",
+		argLen: 2,
+		asm:    mips.ACMPGTF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "CMPGTD",
+		argLen: 2,
+		asm:    mips.ACMPGTD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:              "MOVVconst",
+		auxType:           auxInt64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               mips.AMOVV,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:              "MOVFconst",
+		auxType:           auxFloat64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               mips.AMOVF,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:              "MOVDconst",
+		auxType:           auxFloat64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               mips.AMOVD,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:              "MOVVaddr",
+		auxType:           auxSymOff,
+		argLen:            1,
+		rematerializeable: true,
+		asm:               mips.AMOVV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018460942336}, // SP SB
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:           "MOVBload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:           "MOVBUload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVBU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:           "MOVHload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:           "MOVHUload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVHU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:           "MOVWload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:           "MOVWUload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVWU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:           "MOVVload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:           "MOVFload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:           "MOVDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:           "MOVBstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 234881022},           // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVHstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 234881022},           // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVWstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 234881022},           // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVVstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 234881022},           // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVFstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:           "MOVDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+				{1, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:           "MOVBstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVHstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVWstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:           "MOVVstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            mips.AMOVV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4611686018695823358}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP g R31 SB
+			},
+		},
+	},
+	{
+		name:   "MOVBreg",
+		argLen: 1,
+		asm:    mips.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "MOVBUreg",
+		argLen: 1,
+		asm:    mips.AMOVBU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "MOVHreg",
+		argLen: 1,
+		asm:    mips.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "MOVHUreg",
+		argLen: 1,
+		asm:    mips.AMOVHU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "MOVWreg",
+		argLen: 1,
+		asm:    mips.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "MOVWUreg",
+		argLen: 1,
+		asm:    mips.AMOVWU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "MOVVreg",
+		argLen: 1,
+		asm:    mips.AMOVV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:         "MOVVnop",
+		argLen:       1,
+		resultInArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "MOVWF",
+		argLen: 1,
+		asm:    mips.AMOVWF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "MOVWD",
+		argLen: 1,
+		asm:    mips.AMOVWD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "MOVVF",
+		argLen: 1,
+		asm:    mips.AMOVVF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "MOVVD",
+		argLen: 1,
+		asm:    mips.AMOVVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "TRUNCFW",
+		argLen: 1,
+		asm:    mips.ATRUNCFW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "TRUNCDW",
+		argLen: 1,
+		asm:    mips.ATRUNCDW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "TRUNCFV",
+		argLen: 1,
+		asm:    mips.ATRUNCFV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "TRUNCDV",
+		argLen: 1,
+		asm:    mips.ATRUNCDV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "MOVFD",
+		argLen: 1,
+		asm:    mips.AMOVFD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:   "MOVDF",
+		argLen: 1,
+		asm:    mips.AMOVDF,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+			outputs: []outputInfo{
+				{0, 1152921504338411520}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+			},
+		},
+	},
+	{
+		name:         "CALLstatic",
+		auxType:      auxSymOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO
+		},
+	},
+	{
+		name:         "CALLclosure",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 4194304},   // R22
+				{0, 201326590}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 SP R31
+			},
+			clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO
+		},
+	},
+	{
+		name:         "CALLdefer",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO
+		},
+	},
+	{
+		name:         "CALLgo",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO
+		},
+	},
+	{
+		name:         "CALLinter",
+		auxType:      auxInt64,
+		argLen:       2,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+			clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO
+		},
+	},
+	{
+		name:           "DUFFZERO",
+		auxType:        auxInt64,
+		argLen:         2,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+			clobbers: 134217730, // R1 R31
+		},
+	},
+	{
+		name:           "LoweredZero",
+		auxType:        auxInt64,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 2},         // R1
+				{1, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+			clobbers: 2, // R1
+		},
+	},
+	{
+		name:           "LoweredMove",
+		auxType:        auxInt64,
+		argLen:         4,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		faultOnNilArg1: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4},         // R2
+				{1, 2},         // R1
+				{2, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+			clobbers: 6, // R1 R2
+		},
+	},
+	{
+		name:           "LoweredNilCheck",
+		argLen:         2,
+		nilCheck:       true,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+		},
+	},
+	{
+		name:   "FPFlagTrue",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "FPFlagFalse",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+	{
+		name:   "LoweredGetClosurePtr",
+		argLen: 0,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4194304}, // R22
+			},
+		},
+	},
+	{
+		name:   "MOVVconvert",
+		argLen: 2,
+		asm:    mips.AMOVV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 234881022}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31
+			},
+			outputs: []outputInfo{
+				{0, 167772158}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R31
+			},
+		},
+	},
+
+	{
+		name:        "ADD",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "ADDconst",
+		auxType: auxSymOff,
+		argLen:  1,
+		asm:     ppc64.AADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "FADD",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AFADD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:        "FADDS",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AFADDS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "SUB",
+		argLen: 2,
+		asm:    ppc64.ASUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "FSUB",
+		argLen: 2,
+		asm:    ppc64.AFSUB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "FSUBS",
+		argLen: 2,
+		asm:    ppc64.AFSUBS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:        "MULLD",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AMULLD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "MULLW",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AMULLW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "MULHD",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AMULHD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "MULHW",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AMULHW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "MULHDU",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AMULHDU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "MULHWU",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AMULHWU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "FMUL",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AFMUL,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:        "FMULS",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AFMULS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "SRAD",
+		argLen: 2,
+		asm:    ppc64.ASRAD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "SRAW",
+		argLen: 2,
+		asm:    ppc64.ASRAW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "SRD",
+		argLen: 2,
+		asm:    ppc64.ASRD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "SRW",
+		argLen: 2,
+		asm:    ppc64.ASRW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "SLD",
+		argLen: 2,
+		asm:    ppc64.ASLD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "SLW",
+		argLen: 2,
+		asm:    ppc64.ASLW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "ADDconstForCarry",
+		auxType: auxInt16,
+		argLen:  1,
+		asm:     ppc64.AADDC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			clobbers: 2147483648, // R31
+		},
+	},
+	{
+		name:   "MaskIfNotCarry",
+		argLen: 1,
+		asm:    ppc64.AADDME,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "SRADconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.ASRAD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "SRAWconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.ASRAW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "SRDconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.ASRD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "SRWconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.ASRW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "SLDconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.ASLD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "SLWconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.ASLW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "FDIV",
+		argLen: 2,
+		asm:    ppc64.AFDIV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "FDIVS",
+		argLen: 2,
+		asm:    ppc64.AFDIVS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "DIVD",
+		argLen: 2,
+		asm:    ppc64.ADIVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "DIVW",
+		argLen: 2,
+		asm:    ppc64.ADIVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "DIVDU",
+		argLen: 2,
+		asm:    ppc64.ADIVDU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "DIVWU",
+		argLen: 2,
+		asm:    ppc64.ADIVWU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "FCTIDZ",
+		argLen: 1,
+		asm:    ppc64.AFCTIDZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "FCTIWZ",
+		argLen: 1,
+		asm:    ppc64.AFCTIWZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "FCFID",
+		argLen: 1,
+		asm:    ppc64.AFCFID,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "FRSP",
+		argLen: 1,
+		asm:    ppc64.AFRSP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:        "Xf2i64",
+		argLen:      1,
+		usesScratch: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "Xi2f64",
+		argLen:      1,
+		usesScratch: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:        "AND",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AAND,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "ANDN",
+		argLen: 2,
+		asm:    ppc64.AANDN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "OR",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "ORN",
+		argLen: 2,
+		asm:    ppc64.AORN,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "XOR",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AXOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:        "EQV",
+		argLen:      2,
+		commutative: true,
+		asm:         ppc64.AEQV,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "NEG",
+		argLen: 1,
+		asm:    ppc64.ANEG,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "FNEG",
+		argLen: 1,
+		asm:    ppc64.AFNEG,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "FSQRT",
+		argLen: 1,
+		asm:    ppc64.AFSQRT,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "FSQRTS",
+		argLen: 1,
+		asm:    ppc64.AFSQRTS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:    "ORconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.AOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "XORconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.AXOR,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:         "ANDconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		asm:          ppc64.AANDCC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "ANDCCconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.AANDCC,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "MOVBreg",
+		argLen: 1,
+		asm:    ppc64.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "MOVBZreg",
+		argLen: 1,
+		asm:    ppc64.AMOVBZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "MOVHreg",
+		argLen: 1,
+		asm:    ppc64.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "MOVHZreg",
+		argLen: 1,
+		asm:    ppc64.AMOVHZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "MOVWreg",
+		argLen: 1,
+		asm:    ppc64.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "MOVWZreg",
+		argLen: 1,
+		asm:    ppc64.AMOVWZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVBZload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVBZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVHload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVHZload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVHZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVWload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVWZload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVWZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "FMOVDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AFMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:           "FMOVSload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AFMOVS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:           "MOVBstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVHstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVWstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "FMOVDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            ppc64.AFMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{0, 1073733630},         // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "FMOVSstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            ppc64.AFMOVS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{0, 1073733630},         // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVBstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVB,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVHstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVH,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVWstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:           "MOVDstorezero",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            ppc64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:              "MOVDaddr",
+		auxType:           auxSymOff,
+		argLen:            1,
+		rematerializeable: true,
+		asm:               ppc64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 6}, // SP SB
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:              "MOVDconst",
+		auxType:           auxInt64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               ppc64.AMOVD,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:              "FMOVDconst",
+		auxType:           auxFloat64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               ppc64.AFMOVD,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:              "FMOVSconst",
+		auxType:           auxFloat32,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               ppc64.AFMOVS,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "FCMPU",
+		argLen: 2,
+		asm:    ppc64.AFCMPU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+				{1, 576460743713488896}, // F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+			},
+		},
+	},
+	{
+		name:   "CMP",
+		argLen: 2,
+		asm:    ppc64.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "CMPU",
+		argLen: 2,
+		asm:    ppc64.ACMPU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "CMPW",
+		argLen: 2,
+		asm:    ppc64.ACMPW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "CMPWU",
+		argLen: 2,
+		asm:    ppc64.ACMPWU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+				{1, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "CMPconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.ACMP,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "CMPUconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     ppc64.ACMPU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "CMPWconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     ppc64.ACMPW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:    "CMPWUconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     ppc64.ACMPWU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "Equal",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "NotEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "LessThan",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "FLessThan",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "LessEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "FLessEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "GreaterThan",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "FGreaterThan",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "GreaterEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "FGreaterEqual",
+		argLen: 1,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:   "LoweredGetClosurePtr",
+		argLen: 0,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 2048}, // R11
+			},
+		},
+	},
+	{
+		name:           "LoweredNilCheck",
+		argLen:         2,
+		clobberFlags:   true,
+		nilCheck:       true,
+		faultOnNilArg0: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			clobbers: 2147483648, // R31
+		},
+	},
+	{
+		name:   "MOVDconvert",
+		argLen: 2,
+		asm:    ppc64.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733630}, // SP SB R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			outputs: []outputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+		},
+	},
+	{
+		name:         "CALLstatic",
+		auxType:      auxSymOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+		},
+	},
+	{
+		name:         "CALLclosure",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2048},       // R11
+				{0, 1073733626}, // SP R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+		},
+	},
+	{
+		name:         "CALLdefer",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+		},
+	},
+	{
+		name:         "CALLgo",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+		},
+	},
+	{
+		name:         "CALLinter",
+		auxType:      auxInt64,
+		argLen:       2,
+		clobberFlags: true,
+		call:         true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
+			},
+			clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
+		},
+	},
+	{
+		name:           "LoweredZero",
+		auxType:        auxInt64,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 8},          // R3
+				{1, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			clobbers: 8, // R3
+		},
+	},
+	{
+		name:           "LoweredMove",
+		auxType:        auxInt64,
+		argLen:         4,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		faultOnNilArg1: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 8},          // R3
+				{1, 16},         // R4
+				{2, 1073733624}, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29
 			},
+			clobbers: 24, // R3 R4
 		},
 	},
 	{
-		name:         "ANDQconst",
-		auxType:      auxInt64,
-		argLen:       1,
+		name:   "InvertFlags",
+		argLen: 1,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagEQ",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagLT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+	{
+		name:   "FlagGT",
+		argLen: 0,
+		reg:    regInfo{},
+	},
+
+	{
+		name:         "FADDS",
+		argLen:       2,
+		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.AANDQ,
+		clobberFlags: true,
+		asm:          s390x.AFADDS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:         "ANDLconst",
-		auxType:      auxInt32,
-		argLen:       1,
+		name:         "FADD",
+		argLen:       2,
+		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.AANDL,
+		clobberFlags: true,
+		asm:          s390x.AFADD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:         "ORQ",
+		name:         "FSUBS",
 		argLen:       2,
-		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.AORQ,
+		clobberFlags: true,
+		asm:          s390x.AFSUBS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:         "ORL",
+		name:         "FSUB",
 		argLen:       2,
-		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.AORL,
+		clobberFlags: true,
+		asm:          s390x.AFSUB,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:         "ORQconst",
-		auxType:      auxInt64,
-		argLen:       1,
+		name:         "FMULS",
+		argLen:       2,
+		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.AORQ,
+		asm:          s390x.AFMULS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:         "ORLconst",
-		auxType:      auxInt32,
-		argLen:       1,
+		name:         "FMUL",
+		argLen:       2,
+		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.AORL,
+		asm:          s390x.AFMUL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:         "XORQ",
+		name:         "FDIVS",
 		argLen:       2,
-		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.AXORQ,
+		asm:          s390x.AFDIVS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:         "XORL",
+		name:         "FDIV",
 		argLen:       2,
-		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.AXORL,
+		asm:          s390x.AFDIV,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:         "XORQconst",
-		auxType:      auxInt64,
+		name:         "FNEGS",
 		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.AXORQ,
+		clobberFlags: true,
+		asm:          s390x.AFNEGS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:         "XORLconst",
-		auxType:      auxInt32,
+		name:         "FNEG",
 		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.AXORL,
+		clobberFlags: true,
+		asm:          s390x.AFNEG,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:   "CMPQ",
-		argLen: 2,
-		asm:    x86.ACMPQ,
+		name:           "FMOVSload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            s390x.AFMOVS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:   "CMPL",
-		argLen: 2,
-		asm:    x86.ACMPL,
+		name:           "FMOVDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            s390x.AFMOVD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:   "CMPW",
-		argLen: 2,
-		asm:    x86.ACMPW,
+		name:              "FMOVSconst",
+		auxType:           auxFloat32,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               s390x.AFMOVS,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:              "FMOVDconst",
+		auxType:           auxFloat64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               s390x.AFMOVD,
+		reg: regInfo{
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+			},
+		},
+	},
+	{
+		name:    "FMOVSloadidx",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     s390x.AFMOVS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:   "CMPB",
-		argLen: 2,
-		asm:    x86.ACMPB,
+		name:    "FMOVDloadidx",
+		auxType: auxSymOff,
+		argLen:  3,
+		asm:     s390x.AFMOVD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:    "CMPQconst",
-		auxType: auxInt64,
-		argLen:  1,
-		asm:     x86.ACMPQ,
+		name:           "FMOVSstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            s390x.AFMOVS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+		},
+	},
+	{
+		name:           "FMOVDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            s390x.AFMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:    "CMPLconst",
-		auxType: auxInt32,
-		argLen:  1,
-		asm:     x86.ACMPL,
+		name:    "FMOVSstoreidx",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     s390x.AFMOVS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+		},
+	},
+	{
+		name:    "FMOVDstoreidx",
+		auxType: auxSymOff,
+		argLen:  4,
+		asm:     s390x.AFMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:    "CMPWconst",
-		auxType: auxInt16,
-		argLen:  1,
-		asm:     x86.ACMPW,
+		name:         "ADD",
+		argLen:       2,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          s390x.AADD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "CMPBconst",
-		auxType: auxInt8,
-		argLen:  1,
-		asm:     x86.ACMPB,
+		name:         "ADDW",
+		argLen:       2,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          s390x.AADDW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "UCOMISS",
-		argLen: 2,
-		asm:    x86.AUCOMISS,
+		name:         "ADDconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		asm:          s390x.AADD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "UCOMISD",
-		argLen: 2,
-		asm:    x86.AUCOMISD,
+		name:         "ADDWconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		clobberFlags: true,
+		asm:          s390x.AADDW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "TESTQ",
-		argLen: 2,
-		asm:    x86.ATESTQ,
+		name:           "ADDload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.AADD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "TESTL",
-		argLen: 2,
-		asm:    x86.ATESTL,
+		name:           "ADDWload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.AADDW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "TESTW",
-		argLen: 2,
-		asm:    x86.ATESTW,
+		name:         "SUB",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          s390x.ASUB,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "TESTB",
-		argLen: 2,
-		asm:    x86.ATESTB,
+		name:         "SUBW",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          s390x.ASUBW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{1, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "TESTQconst",
-		auxType: auxInt64,
-		argLen:  1,
-		asm:     x86.ATESTQ,
+		name:         "SUBconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          s390x.ASUB,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "TESTLconst",
-		auxType: auxInt32,
-		argLen:  1,
-		asm:     x86.ATESTL,
+		name:         "SUBWconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          s390x.ASUBW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "TESTWconst",
-		auxType: auxInt16,
-		argLen:  1,
-		asm:     x86.ATESTW,
+		name:           "SUBload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.ASUB,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "TESTBconst",
-		auxType: auxInt8,
-		argLen:  1,
-		asm:     x86.ATESTB,
+		name:           "SUBWload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.ASUBW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				8589934592, // FLAGS
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHLQ",
+		name:         "MULLD",
 		argLen:       2,
+		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.ASHLQ,
+		clobberFlags: true,
+		asm:          s390x.AMULLD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 2},     // CX
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHLL",
+		name:         "MULLW",
 		argLen:       2,
+		commutative:  true,
 		resultInArg0: true,
-		asm:          x86.ASHLL,
+		clobberFlags: true,
+		asm:          s390x.AMULLW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 2},     // CX
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHLQconst",
+		name:         "MULLDconst",
 		auxType:      auxInt64,
 		argLen:       1,
 		resultInArg0: true,
-		asm:          x86.ASHLQ,
+		clobberFlags: true,
+		asm:          s390x.AMULLD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:         "MULLWconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          s390x.AMULLW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "MULLDload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.AMULLD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHLLconst",
-		auxType:      auxInt32,
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.ASHLL,
+		name:           "MULLWload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.AMULLW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHRQ",
+		name:         "MULHD",
 		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASHRQ,
+		clobberFlags: true,
+		asm:          s390x.AMULHD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 2},     // CX
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHRL",
+		name:         "MULHDU",
 		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASHRL,
+		clobberFlags: true,
+		asm:          s390x.AMULHDU,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 2},     // CX
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHRW",
+		name:         "DIVD",
 		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASHRW,
+		clobberFlags: true,
+		asm:          s390x.ADIVD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 2},     // CX
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHRB",
+		name:         "DIVW",
 		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASHRB,
+		clobberFlags: true,
+		asm:          s390x.ADIVW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 2},     // CX
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHRQconst",
-		auxType:      auxInt64,
-		argLen:       1,
+		name:         "DIVDU",
+		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASHRQ,
+		clobberFlags: true,
+		asm:          s390x.ADIVDU,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHRLconst",
-		auxType:      auxInt32,
-		argLen:       1,
+		name:         "DIVWU",
+		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASHRL,
+		clobberFlags: true,
+		asm:          s390x.ADIVWU,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHRWconst",
-		auxType:      auxInt16,
-		argLen:       1,
+		name:         "MODD",
+		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASHRW,
+		clobberFlags: true,
+		asm:          s390x.AMODD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SHRBconst",
-		auxType:      auxInt8,
-		argLen:       1,
+		name:         "MODW",
+		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASHRB,
+		clobberFlags: true,
+		asm:          s390x.AMODW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SARQ",
+		name:         "MODDU",
 		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASARQ,
+		clobberFlags: true,
+		asm:          s390x.AMODDU,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 2},     // CX
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SARL",
+		name:         "MODWU",
 		argLen:       2,
 		resultInArg0: true,
-		asm:          x86.ASARL,
+		clobberFlags: true,
+		asm:          s390x.AMODWU,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 2},     // CX
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SARW",
+		name:         "AND",
 		argLen:       2,
-		resultInArg0: true,
-		asm:          x86.ASARW,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          s390x.AAND,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 2},     // CX
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SARB",
+		name:         "ANDW",
 		argLen:       2,
-		resultInArg0: true,
-		asm:          x86.ASARB,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          s390x.AANDW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 2},     // CX
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SARQconst",
+		name:         "ANDconst",
 		auxType:      auxInt64,
 		argLen:       1,
 		resultInArg0: true,
-		asm:          x86.ASARQ,
+		clobberFlags: true,
+		asm:          s390x.AAND,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SARLconst",
+		name:         "ANDWconst",
 		auxType:      auxInt32,
 		argLen:       1,
 		resultInArg0: true,
-		asm:          x86.ASARL,
+		clobberFlags: true,
+		asm:          s390x.AANDW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SARWconst",
-		auxType:      auxInt16,
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.ASARW,
+		name:           "ANDload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.AAND,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "SARBconst",
-		auxType:      auxInt8,
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.ASARB,
+		name:           "ANDWload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.AANDW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "ROLQconst",
-		auxType:      auxInt64,
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.AROLQ,
+		name:         "OR",
+		argLen:       2,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          s390x.AOR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "ROLLconst",
-		auxType:      auxInt32,
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.AROLL,
+		name:         "ORW",
+		argLen:       2,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          s390x.AORW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "ROLWconst",
-		auxType:      auxInt16,
+		name:         "ORconst",
+		auxType:      auxInt64,
 		argLen:       1,
 		resultInArg0: true,
-		asm:          x86.AROLW,
+		clobberFlags: true,
+		asm:          s390x.AOR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "ROLBconst",
-		auxType:      auxInt8,
+		name:         "ORWconst",
+		auxType:      auxInt32,
 		argLen:       1,
 		resultInArg0: true,
-		asm:          x86.AROLB,
+		clobberFlags: true,
+		asm:          s390x.AORW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "NEGQ",
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.ANEGQ,
+		name:           "ORload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.AOR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "NEGL",
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.ANEGL,
+		name:           "ORWload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.AORW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "NOTQ",
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.ANOTQ,
+		name:         "XOR",
+		argLen:       2,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          s390x.AXOR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "NOTL",
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.ANOTL,
+		name:         "XORW",
+		argLen:       2,
+		commutative:  true,
+		clobberFlags: true,
+		asm:          s390x.AXORW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "BSFQ",
-		argLen: 1,
-		asm:    x86.ABSFQ,
+		name:         "XORconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          s390x.AXOR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "BSFL",
-		argLen: 1,
-		asm:    x86.ABSFL,
+		name:         "XORWconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
+		asm:          s390x.AXORW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "BSFW",
-		argLen: 1,
-		asm:    x86.ABSFW,
+		name:           "XORload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.AXOR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "BSRQ",
-		argLen: 1,
-		asm:    x86.ABSRQ,
+		name:           "XORWload",
+		auxType:        auxSymOff,
+		argLen:         3,
+		resultInArg0:   true,
+		clobberFlags:   true,
+		faultOnNilArg1: true,
+		asm:            s390x.AXORW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "BSRL",
-		argLen: 1,
-		asm:    x86.ABSRL,
+		name:   "CMP",
+		argLen: 2,
+		asm:    s390x.ACMP,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:   "BSRW",
-		argLen: 1,
-		asm:    x86.ABSRW,
+		name:   "CMPW",
+		argLen: 2,
+		asm:    s390x.ACMPW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:         "CMOVQEQconst",
-		auxType:      auxInt64,
-		argLen:       2,
-		resultInArg0: true,
-		asm:          x86.ACMOVQEQ,
+		name:   "CMPU",
+		argLen: 2,
+		asm:    s390x.ACMPU,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 8589934592}, // FLAGS
-				{0, 65518},      // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				65518, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:         "CMOVLEQconst",
-		auxType:      auxInt32,
-		argLen:       2,
-		resultInArg0: true,
-		asm:          x86.ACMOVLEQ,
+		name:   "CMPWU",
+		argLen: 2,
+		asm:    s390x.ACMPWU,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 8589934592}, // FLAGS
-				{0, 65518},      // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				65518, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:         "CMOVWEQconst",
-		auxType:      auxInt16,
-		argLen:       2,
-		resultInArg0: true,
-		asm:          x86.ACMOVLEQ,
+		name:    "CMPconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     s390x.ACMP,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 8589934592}, // FLAGS
-				{0, 65518},      // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				65518, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:         "CMOVQNEconst",
-		auxType:      auxInt64,
-		argLen:       2,
-		resultInArg0: true,
-		asm:          x86.ACMOVQNE,
+		name:    "CMPWconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     s390x.ACMPW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 8589934592}, // FLAGS
-				{0, 65518},      // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				65518, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:         "CMOVLNEconst",
-		auxType:      auxInt32,
-		argLen:       2,
-		resultInArg0: true,
-		asm:          x86.ACMOVLNE,
+		name:    "CMPUconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     s390x.ACMPU,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 8589934592}, // FLAGS
-				{0, 65518},      // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				65518, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:         "CMOVWNEconst",
-		auxType:      auxInt16,
-		argLen:       2,
-		resultInArg0: true,
-		asm:          x86.ACMOVLNE,
+		name:    "CMPWUconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     s390x.ACMPWU,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 8589934592}, // FLAGS
-				{0, 65518},      // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				65518, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:         "BSWAPQ",
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.ABSWAPQ,
+		name:   "FCMPS",
+		argLen: 2,
+		asm:    s390x.ACEBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+		},
+	},
+	{
+		name:   "FCMP",
+		argLen: 2,
+		asm:    s390x.AFCMPU,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+				{1, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:         "BSWAPL",
-		argLen:       1,
-		resultInArg0: true,
-		asm:          x86.ABSWAPL,
+		name:   "SLD",
+		argLen: 2,
+		asm:    s390x.ASLD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{1, 21502}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934592, // FLAGS
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SQRTSD",
-		argLen: 1,
-		asm:    x86.ASQRTSD,
+		name:   "SLW",
+		argLen: 2,
+		asm:    s390x.ASLW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{1, 21502}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SBBQcarrymask",
-		argLen: 1,
-		asm:    x86.ASBBQ,
+		name:    "SLDconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     s390x.ASLD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SBBLcarrymask",
-		argLen: 1,
-		asm:    x86.ASBBL,
+		name:    "SLWconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     s390x.ASLW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETEQ",
-		argLen: 1,
-		asm:    x86.ASETEQ,
+		name:   "SRD",
+		argLen: 2,
+		asm:    s390x.ASRD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{1, 21502}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETNE",
-		argLen: 1,
-		asm:    x86.ASETNE,
+		name:   "SRW",
+		argLen: 2,
+		asm:    s390x.ASRW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{1, 21502}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETL",
-		argLen: 1,
-		asm:    x86.ASETLT,
+		name:    "SRDconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     s390x.ASRD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETLE",
-		argLen: 1,
-		asm:    x86.ASETLE,
+		name:    "SRWconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     s390x.ASRW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETG",
-		argLen: 1,
-		asm:    x86.ASETGT,
+		name:         "SRAD",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          s390x.ASRAD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{1, 21502}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETGE",
-		argLen: 1,
-		asm:    x86.ASETGE,
+		name:         "SRAW",
+		argLen:       2,
+		clobberFlags: true,
+		asm:          s390x.ASRAW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{1, 21502}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETB",
-		argLen: 1,
-		asm:    x86.ASETCS,
+		name:         "SRADconst",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		asm:          s390x.ASRAD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETBE",
-		argLen: 1,
-		asm:    x86.ASETLS,
+		name:         "SRAWconst",
+		auxType:      auxInt32,
+		argLen:       1,
+		clobberFlags: true,
+		asm:          s390x.ASRAW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETA",
-		argLen: 1,
-		asm:    x86.ASETHI,
+		name:    "RLLGconst",
+		auxType: auxInt64,
+		argLen:  1,
+		asm:     s390x.ARLLG,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETAE",
-		argLen: 1,
-		asm:    x86.ASETCC,
+		name:    "RLLconst",
+		auxType: auxInt32,
+		argLen:  1,
+		asm:     s390x.ARLL,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETEQF",
-		argLen: 1,
-		asm:    x86.ASETEQ,
+		name:         "NEG",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          s390x.ANEG,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				65518, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETNEF",
-		argLen: 1,
-		asm:    x86.ASETNE,
+		name:         "NEGW",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          s390x.ANEGW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 8589934593, // AX FLAGS
-			outputs: []regMask{
-				65518, // CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETORD",
-		argLen: 1,
-		asm:    x86.ASETPC,
+		name:         "NOT",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETNAN",
-		argLen: 1,
-		asm:    x86.ASETPS,
+		name:         "NOTW",
+		argLen:       1,
+		resultInArg0: true,
+		clobberFlags: true,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "SETGF",
+		name:   "FSQRT",
 		argLen: 1,
-		asm:    x86.ASETHI,
+		asm:    s390x.AFSQRT,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:   "SETGEF",
+		name:   "SUBEcarrymask",
 		argLen: 1,
-		asm:    x86.ASETCC,
+		asm:    s390x.ASUBE,
 		reg: regInfo{
-			inputs: []inputInfo{
-				{0, 8589934592}, // FLAGS
-			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "MOVBQSX",
+		name:   "SUBEWcarrymask",
 		argLen: 1,
-		asm:    x86.AMOVBQSX,
+		asm:    s390x.ASUBE,
 		reg: regInfo{
-			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "MOVBQZX",
-		argLen: 1,
-		asm:    x86.AMOVBQZX,
+		name:         "MOVDEQ",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          s390x.AMOVDEQ,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "MOVWQSX",
-		argLen: 1,
-		asm:    x86.AMOVWQSX,
+		name:         "MOVDNE",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          s390x.AMOVDNE,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "MOVWQZX",
-		argLen: 1,
-		asm:    x86.AMOVWQZX,
+		name:         "MOVDLT",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          s390x.AMOVDLT,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "MOVLQSX",
-		argLen: 1,
-		asm:    x86.AMOVLQSX,
+		name:         "MOVDLE",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          s390x.AMOVDLE,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "MOVLQZX",
-		argLen: 1,
-		asm:    x86.AMOVLQZX,
+		name:         "MOVDGT",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          s390x.AMOVDGT,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:              "MOVLconst",
-		auxType:           auxInt32,
-		argLen:            0,
-		rematerializeable: true,
-		asm:               x86.AMOVL,
+		name:         "MOVDGE",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          s390x.AMOVDGE,
 		reg: regInfo{
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:              "MOVQconst",
-		auxType:           auxInt64,
-		argLen:            0,
-		rematerializeable: true,
-		asm:               x86.AMOVQ,
+		name:         "MOVDGTnoinv",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          s390x.AMOVDGT,
 		reg: regInfo{
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "CVTTSD2SL",
-		argLen: 1,
-		asm:    x86.ACVTTSD2SL,
+		name:         "MOVDGEnoinv",
+		argLen:       3,
+		resultInArg0: true,
+		asm:          s390x.AMOVDGE,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+				{1, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "CVTTSD2SQ",
+		name:   "MOVBreg",
 		argLen: 1,
-		asm:    x86.ACVTTSD2SQ,
+		asm:    s390x.AMOVB,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "CVTTSS2SL",
+		name:   "MOVBZreg",
 		argLen: 1,
-		asm:    x86.ACVTTSS2SL,
+		asm:    s390x.AMOVBZ,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "CVTTSS2SQ",
+		name:   "MOVHreg",
 		argLen: 1,
-		asm:    x86.ACVTTSS2SQ,
+		asm:    s390x.AMOVH,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "CVTSL2SS",
+		name:   "MOVHZreg",
 		argLen: 1,
-		asm:    x86.ACVTSL2SS,
+		asm:    s390x.AMOVHZ,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "CVTSL2SD",
+		name:   "MOVWreg",
 		argLen: 1,
-		asm:    x86.ACVTSL2SD,
+		asm:    s390x.AMOVW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "CVTSQ2SS",
+		name:   "MOVWZreg",
 		argLen: 1,
-		asm:    x86.ACVTSQ2SS,
+		asm:    s390x.AMOVWZ,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "CVTSQ2SD",
-		argLen: 1,
-		asm:    x86.ACVTSQ2SD,
+		name:              "MOVDconst",
+		auxType:           auxInt64,
+		argLen:            0,
+		rematerializeable: true,
+		asm:               s390x.AMOVD,
 		reg: regInfo{
-			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "CVTSD2SS",
+		name:   "CFDBRA",
 		argLen: 1,
-		asm:    x86.ACVTSD2SS,
+		asm:    s390x.ACFDBRA,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:   "CVTSS2SD",
+		name:   "CGDBRA",
 		argLen: 1,
-		asm:    x86.ACVTSS2SD,
+		asm:    s390x.ACGDBRA,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:         "PXOR",
-		argLen:       2,
-		commutative:  true,
-		resultInArg0: true,
-		asm:          x86.APXOR,
+		name:   "CFEBRA",
+		argLen: 1,
+		asm:    s390x.ACFEBRA,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:              "LEAQ",
-		auxType:           auxSymOff,
-		argLen:            1,
-		rematerializeable: true,
+		name:   "CGEBRA",
+		argLen: 1,
+		asm:    s390x.ACGEBRA,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "LEAQ1",
-		auxType: auxSymOff,
-		argLen:  2,
+		name:   "CEFBRA",
+		argLen: 1,
+		asm:    s390x.ACEFBRA,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:    "LEAQ2",
-		auxType: auxSymOff,
-		argLen:  2,
+		name:   "CDFBRA",
+		argLen: 1,
+		asm:    s390x.ACDFBRA,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:    "LEAQ4",
-		auxType: auxSymOff,
-		argLen:  2,
+		name:   "CEGBRA",
+		argLen: 1,
+		asm:    s390x.ACEGBRA,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:    "LEAQ8",
-		auxType: auxSymOff,
-		argLen:  2,
+		name:   "CDGBRA",
+		argLen: 1,
+		asm:    s390x.ACDGBRA,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:    "MOVBload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     x86.AMOVBLZX,
+		name:   "LEDBR",
+		argLen: 1,
+		asm:    s390x.ALEDBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:    "MOVBQSXload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     x86.AMOVBQSX,
+		name:   "LDEBR",
+		argLen: 1,
+		asm:    s390x.ALDEBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 4294901760}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 			},
 		},
 	},
 	{
-		name:    "MOVWload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     x86.AMOVWLZX,
+		name:              "MOVDaddr",
+		auxType:           auxSymOff,
+		argLen:            1,
+		rematerializeable: true,
+		clobberFlags:      true,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295000064}, // SP SB
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVWQSXload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     x86.AMOVWQSX,
+		name:         "MOVDaddridx",
+		auxType:      auxSymOff,
+		argLen:       2,
+		clobberFlags: true,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295000064}, // SP SB
+				{1, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVLload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     x86.AMOVL,
+		name:           "MOVBZload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVBZ,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVLQSXload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     x86.AMOVLQSX,
+		name:           "MOVBload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVB,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVQload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     x86.AMOVQ,
+		name:           "MOVHZload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVHZ,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVBstore",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVB,
+		name:           "MOVHload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVH,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVWstore",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVW,
+		name:           "MOVWZload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVWZ,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVLstore",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVL,
+		name:           "MOVWload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVQstore",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVQ,
+		name:           "MOVDload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVOload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     x86.AMOVUPS,
+		name:   "MOVWBR",
+		argLen: 1,
+		asm:    s390x.AMOVWBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVOstore",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVUPS,
+		name:   "MOVDBR",
+		argLen: 1,
+		asm:    s390x.AMOVDBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 4294901760}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVBloadidx1",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVBLZX,
+		name:           "MOVHBRload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVHBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVWloadidx1",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVWLZX,
+		name:           "MOVWBRload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVWBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVWloadidx2",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVWLZX,
+		name:           "MOVDBRload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVDBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVLloadidx1",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVL,
+		name:           "MOVBstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVB,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
-			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+				{1, 54271},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVLloadidx4",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVL,
+		name:           "MOVHstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVH,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
-			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+				{1, 54271},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVQloadidx1",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVQ,
+		name:           "MOVWstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
-			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+				{1, 54271},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVQloadidx8",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     x86.AMOVQ,
+		name:           "MOVDstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
-			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+				{1, 54271},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVBstoreidx1",
-		auxType: auxSymOff,
-		argLen:  4,
-		asm:     x86.AMOVB,
+		name:           "MOVHBRstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVHBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVWstoreidx1",
-		auxType: auxSymOff,
-		argLen:  4,
-		asm:     x86.AMOVW,
+		name:           "MOVWBRstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVWBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVWstoreidx2",
-		auxType: auxSymOff,
-		argLen:  4,
-		asm:     x86.AMOVW,
+		name:           "MOVDBRstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVDBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVLstoreidx1",
-		auxType: auxSymOff,
-		argLen:  4,
-		asm:     x86.AMOVL,
+		name:           "MVC",
+		auxType:        auxSymValAndOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		faultOnNilArg1: true,
+		asm:            s390x.AMVC,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVLstoreidx4",
-		auxType: auxSymOff,
-		argLen:  4,
-		asm:     x86.AMOVL,
+		name:         "MOVBZloadidx",
+		auxType:      auxSymOff,
+		argLen:       3,
+		clobberFlags: true,
+		asm:          s390x.AMOVBZ,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVQstoreidx1",
-		auxType: auxSymOff,
-		argLen:  4,
-		asm:     x86.AMOVQ,
+		name:         "MOVHZloadidx",
+		auxType:      auxSymOff,
+		argLen:       3,
+		clobberFlags: true,
+		asm:          s390x.AMOVHZ,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVQstoreidx8",
-		auxType: auxSymOff,
-		argLen:  4,
-		asm:     x86.AMOVQ,
+		name:         "MOVWZloadidx",
+		auxType:      auxSymOff,
+		argLen:       3,
+		clobberFlags: true,
+		asm:          s390x.AMOVWZ,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{2, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVBstoreconst",
-		auxType: auxSymValAndOff,
-		argLen:  2,
-		asm:     x86.AMOVB,
+		name:         "MOVDloadidx",
+		auxType:      auxSymOff,
+		argLen:       3,
+		clobberFlags: true,
+		asm:          s390x.AMOVD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVWstoreconst",
-		auxType: auxSymValAndOff,
-		argLen:  2,
-		asm:     x86.AMOVW,
+		name:         "MOVHBRloadidx",
+		auxType:      auxSymOff,
+		argLen:       3,
+		clobberFlags: true,
+		asm:          s390x.AMOVHBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVLstoreconst",
-		auxType: auxSymValAndOff,
-		argLen:  2,
-		asm:     x86.AMOVL,
+		name:         "MOVWBRloadidx",
+		auxType:      auxSymOff,
+		argLen:       3,
+		clobberFlags: true,
+		asm:          s390x.AMOVWBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVQstoreconst",
-		auxType: auxSymValAndOff,
-		argLen:  2,
-		asm:     x86.AMOVQ,
+		name:         "MOVDBRloadidx",
+		auxType:      auxSymOff,
+		argLen:       3,
+		clobberFlags: true,
+		asm:          s390x.AMOVDBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{1, 54270},      // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
 	{
-		name:    "MOVBstoreconstidx1",
-		auxType: auxSymValAndOff,
-		argLen:  3,
-		asm:     x86.AMOVB,
+		name:         "MOVBstoreidx",
+		auxType:      auxSymOff,
+		argLen:       4,
+		clobberFlags: true,
+		asm:          s390x.AMOVB,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVWstoreconstidx1",
-		auxType: auxSymValAndOff,
-		argLen:  3,
-		asm:     x86.AMOVW,
+		name:         "MOVHstoreidx",
+		auxType:      auxSymOff,
+		argLen:       4,
+		clobberFlags: true,
+		asm:          s390x.AMOVH,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVWstoreconstidx2",
-		auxType: auxSymValAndOff,
-		argLen:  3,
-		asm:     x86.AMOVW,
+		name:         "MOVWstoreidx",
+		auxType:      auxSymOff,
+		argLen:       4,
+		clobberFlags: true,
+		asm:          s390x.AMOVW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVLstoreconstidx1",
-		auxType: auxSymValAndOff,
-		argLen:  3,
-		asm:     x86.AMOVL,
+		name:         "MOVDstoreidx",
+		auxType:      auxSymOff,
+		argLen:       4,
+		clobberFlags: true,
+		asm:          s390x.AMOVD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVLstoreconstidx4",
-		auxType: auxSymValAndOff,
-		argLen:  3,
-		asm:     x86.AMOVL,
+		name:         "MOVHBRstoreidx",
+		auxType:      auxSymOff,
+		argLen:       4,
+		clobberFlags: true,
+		asm:          s390x.AMOVHBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVQstoreconstidx1",
-		auxType: auxSymValAndOff,
-		argLen:  3,
-		asm:     x86.AMOVQ,
+		name:         "MOVWBRstoreidx",
+		auxType:      auxSymOff,
+		argLen:       4,
+		clobberFlags: true,
+		asm:          s390x.AMOVWBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVQstoreconstidx8",
-		auxType: auxSymValAndOff,
-		argLen:  3,
-		asm:     x86.AMOVQ,
+		name:         "MOVDBRstoreidx",
+		auxType:      auxSymOff,
+		argLen:       4,
+		clobberFlags: true,
+		asm:          s390x.AMOVDBR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 65535},      // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-				{0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 SB
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "DUFFZERO",
-		auxType: auxInt64,
-		argLen:  3,
+		name:           "MOVBstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVB,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 128},   // DI
-				{1, 65536}, // X0
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
-			clobbers: 8589934720, // DI FLAGS
 		},
 	},
 	{
-		name:              "MOVOconst",
-		auxType:           auxInt128,
-		argLen:            0,
-		rematerializeable: true,
+		name:           "MOVHstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVH,
 		reg: regInfo{
-			outputs: []regMask{
-				4294901760, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15
+			inputs: []inputInfo{
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
 		},
 	},
 	{
-		name:   "REPSTOSQ",
-		argLen: 4,
+		name:           "MOVWstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVW,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 128}, // DI
-				{1, 2},   // CX
-				{2, 1},   // AX
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
 			},
-			clobbers: 130, // CX DI
 		},
 	},
 	{
-		name:    "CALLstatic",
-		auxType: auxSymOff,
-		argLen:  1,
+		name:           "MOVDstoreconst",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVD,
 		reg: regInfo{
-			clobbers: 12884901871, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 FLAGS
+			inputs: []inputInfo{
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
 		},
 	},
 	{
-		name:    "CALLclosure",
-		auxType: auxInt64,
-		argLen:  3,
+		name:           "CLEAR",
+		auxType:        auxSymValAndOff,
+		argLen:         2,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.ACLEAR,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{1, 4},     // DX
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 21502}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 12884901871, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 FLAGS
 		},
 	},
 	{
-		name:    "CALLdefer",
-		auxType: auxInt64,
-		argLen:  1,
+		name:         "CALLstatic",
+		auxType:      auxSymOff,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
 		reg: regInfo{
-			clobbers: 12884901871, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 FLAGS
+			clobbers: 4294923263, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 		},
 	},
 	{
-		name:    "CALLgo",
-		auxType: auxInt64,
-		argLen:  1,
+		name:         "CALLclosure",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		call:         true,
 		reg: regInfo{
-			clobbers: 12884901871, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 FLAGS
+			inputs: []inputInfo{
+				{1, 4096},  // R12
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+			},
+			clobbers: 4294923263, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 		},
 	},
 	{
-		name:    "CALLinter",
-		auxType: auxInt64,
-		argLen:  2,
+		name:         "CALLdefer",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
 		reg: regInfo{
-			inputs: []inputInfo{
-				{0, 65519}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
-			},
-			clobbers: 12884901871, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 FLAGS
+			clobbers: 4294923263, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 		},
 	},
 	{
-		name:    "DUFFCOPY",
-		auxType: auxInt64,
-		argLen:  3,
+		name:         "CALLgo",
+		auxType:      auxInt64,
+		argLen:       1,
+		clobberFlags: true,
+		call:         true,
 		reg: regInfo{
-			inputs: []inputInfo{
-				{0, 128}, // DI
-				{1, 64},  // SI
-			},
-			clobbers: 8590000320, // SI DI X0 FLAGS
+			clobbers: 4294923263, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 		},
 	},
 	{
-		name:   "REPMOVSQ",
-		argLen: 4,
+		name:         "CALLinter",
+		auxType:      auxInt64,
+		argLen:       2,
+		clobberFlags: true,
+		call:         true,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 128}, // DI
-				{1, 64},  // SI
-				{2, 2},   // CX
+				{0, 21502}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
-			clobbers: 194, // CX SI DI
+			clobbers: 4294923263, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
 		},
 	},
 	{
@@ -3735,8 +19382,8 @@ var opcodeTable = [...]opInfo{
 		name:   "LoweredGetG",
 		argLen: 1,
 		reg: regInfo{
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
@@ -3744,31 +19391,33 @@ var opcodeTable = [...]opInfo{
 		name:   "LoweredGetClosurePtr",
 		argLen: 0,
 		reg: regInfo{
-			outputs: []regMask{
-				4, // DX
+			outputs: []outputInfo{
+				{0, 4096}, // R12
 			},
 		},
 	},
 	{
-		name:   "LoweredNilCheck",
-		argLen: 2,
+		name:           "LoweredNilCheck",
+		argLen:         2,
+		clobberFlags:   true,
+		nilCheck:       true,
+		faultOnNilArg0: true,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			clobbers: 8589934592, // FLAGS
 		},
 	},
 	{
-		name:   "MOVQconvert",
+		name:   "MOVDconvert",
 		argLen: 2,
-		asm:    x86.AMOVQ,
+		asm:    s390x.AMOVD,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 65535}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+				{0, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				65519, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
 			},
 		},
 	},
@@ -3778,125 +19427,321 @@ var opcodeTable = [...]opInfo{
 		reg:    regInfo{},
 	},
 	{
-		name:   "FlagLT_ULT",
+		name:   "FlagLT",
 		argLen: 0,
 		reg:    regInfo{},
 	},
 	{
-		name:   "FlagLT_UGT",
+		name:   "FlagGT",
 		argLen: 0,
 		reg:    regInfo{},
 	},
 	{
-		name:   "FlagGT_UGT",
-		argLen: 0,
+		name:           "MOVWZatomicload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVWZ,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "MOVDatomicload",
+		auxType:        auxSymOff,
+		argLen:         2,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "MOVWatomicstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVW,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+				{1, 54271},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+			},
+		},
+	},
+	{
+		name:           "MOVDatomicstore",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.AMOVD,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+				{1, 54271},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+			},
+		},
+	},
+	{
+		name:           "LAA",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            s390x.ALAA,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+				{1, 54271},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "LAAG",
+		auxType:        auxSymOff,
+		argLen:         3,
+		faultOnNilArg0: true,
+		asm:            s390x.ALAAG,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 4295021566}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP SB
+				{1, 54271},      // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+			},
+			outputs: []outputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:   "AddTupleFirst32",
+		argLen: 2,
 		reg:    regInfo{},
 	},
 	{
-		name:   "FlagGT_ULT",
-		argLen: 0,
+		name:   "AddTupleFirst64",
+		argLen: 2,
 		reg:    regInfo{},
 	},
-
 	{
-		name:        "ADD",
-		argLen:      2,
-		commutative: true,
-		asm:         arm.AADD,
+		name:           "LoweredAtomicCas32",
+		auxType:        auxSymOff,
+		argLen:         4,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.ACS,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 1},     // R0
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+			},
+			clobbers: 1, // R0
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "LoweredAtomicCas64",
+		auxType:        auxSymOff,
+		argLen:         4,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.ACSG,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 1},     // R0
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{2, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+			},
+			clobbers: 1, // R0
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+		},
+	},
+	{
+		name:           "LoweredAtomicExchange32",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.ACS,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 31}, // R0 R1 R2 R3 SP
-				{1, 31}, // R0 R1 R2 R3 SP
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				31, // R0 R1 R2 R3 SP
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 1}, // R0
 			},
 		},
 	},
 	{
-		name:    "ADDconst",
-		auxType: auxSymOff,
-		argLen:  1,
-		asm:     arm.AADD,
+		name:           "LoweredAtomicExchange64",
+		auxType:        auxSymOff,
+		argLen:         3,
+		clobberFlags:   true,
+		faultOnNilArg0: true,
+		asm:            s390x.ACSG,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 31}, // R0 R1 R2 R3 SP
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+				{1, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				31, // R0 R1 R2 R3 SP
+			outputs: []outputInfo{
+				{1, 0},
+				{0, 1}, // R0
 			},
 		},
 	},
 	{
-		name:              "MOVWconst",
-		auxType:           auxInt32,
-		argLen:            0,
-		rematerializeable: true,
-		asm:               arm.AMOVW,
+		name:         "FLOGR",
+		argLen:       1,
+		clobberFlags: true,
+		asm:          s390x.AFLOGR,
 		reg: regInfo{
-			outputs: []regMask{
-				31, // R0 R1 R2 R3 SP
+			inputs: []inputInfo{
+				{0, 21503}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14
+			},
+			clobbers: 2, // R1
+			outputs: []outputInfo{
+				{0, 1}, // R0
 			},
 		},
 	},
 	{
-		name:   "CMP",
-		argLen: 2,
-		asm:    arm.ACMP,
+		name:           "STMG2",
+		auxType:        auxSymOff,
+		argLen:         4,
+		faultOnNilArg0: true,
+		asm:            s390x.ASTMG,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 31}, // R0 R1 R2 R3 SP
-				{1, 31}, // R0 R1 R2 R3 SP
+				{1, 2},     // R1
+				{2, 4},     // R2
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				32, // FLAGS
+		},
+	},
+	{
+		name:           "STMG3",
+		auxType:        auxSymOff,
+		argLen:         5,
+		faultOnNilArg0: true,
+		asm:            s390x.ASTMG,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // R1
+				{2, 4},     // R2
+				{3, 8},     // R3
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVWload",
-		auxType: auxSymOff,
-		argLen:  2,
-		asm:     arm.AMOVW,
+		name:           "STMG4",
+		auxType:        auxSymOff,
+		argLen:         6,
+		faultOnNilArg0: true,
+		asm:            s390x.ASTMG,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 31}, // R0 R1 R2 R3 SP
+				{1, 2},     // R1
+				{2, 4},     // R2
+				{3, 8},     // R3
+				{4, 16},    // R4
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				31, // R0 R1 R2 R3 SP
+		},
+	},
+	{
+		name:           "STM2",
+		auxType:        auxSymOff,
+		argLen:         4,
+		faultOnNilArg0: true,
+		asm:            s390x.ASTMY,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{1, 2},     // R1
+				{2, 4},     // R2
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "MOVWstore",
-		auxType: auxSymOff,
-		argLen:  3,
-		asm:     arm.AMOVW,
+		name:           "STM3",
+		auxType:        auxSymOff,
+		argLen:         5,
+		faultOnNilArg0: true,
+		asm:            s390x.ASTMY,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 31}, // R0 R1 R2 R3 SP
-				{1, 31}, // R0 R1 R2 R3 SP
+				{1, 2},     // R1
+				{2, 4},     // R2
+				{3, 8},     // R3
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
 		},
 	},
 	{
-		name:    "CALLstatic",
-		auxType: auxSymOff,
-		argLen:  1,
+		name:           "STM4",
+		auxType:        auxSymOff,
+		argLen:         6,
+		faultOnNilArg0: true,
+		asm:            s390x.ASTMY,
 		reg: regInfo{
-			clobbers: 15, // R0 R1 R2 R3
+			inputs: []inputInfo{
+				{1, 2},     // R1
+				{2, 4},     // R2
+				{3, 8},     // R3
+				{4, 16},    // R4
+				{0, 54270}, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
+			},
 		},
 	},
 	{
-		name:   "LessThan",
-		argLen: 1,
+		name:         "LoweredMove",
+		auxType:      auxInt64,
+		argLen:       4,
+		clobberFlags: true,
 		reg: regInfo{
 			inputs: []inputInfo{
-				{0, 32}, // FLAGS
+				{0, 2},     // R1
+				{1, 4},     // R2
+				{2, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
-			outputs: []regMask{
-				31, // R0 R1 R2 R3 SP
+			clobbers: 6, // R1 R2
+		},
+	},
+	{
+		name:         "LoweredZero",
+		auxType:      auxInt64,
+		argLen:       3,
+		clobberFlags: true,
+		reg: regInfo{
+			inputs: []inputInfo{
+				{0, 2},     // R1
+				{1, 54271}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R12 R14 SP
 			},
+			clobbers: 2, // R1
 		},
 	},
 
@@ -4059,6 +19904,16 @@ var opcodeTable = [...]opInfo{
 		generic: true,
 	},
 	{
+		name:    "Mul32uhilo",
+		argLen:  2,
+		generic: true,
+	},
+	{
+		name:    "Mul64uhilo",
+		argLen:  2,
+		generic: true,
+	},
+	{
 		name:    "Avg64u",
 		argLen:  2,
 		generic: true,
@@ -4104,6 +19959,11 @@ var opcodeTable = [...]opInfo{
 		generic: true,
 	},
 	{
+		name:    "Div128u",
+		argLen:  3,
+		generic: true,
+	},
+	{
 		name:    "Mod8",
 		argLen:  2,
 		generic: true,
@@ -4855,11 +20715,6 @@ var opcodeTable = [...]opInfo{
 		generic: true,
 	},
 	{
-		name:    "Ctz16",
-		argLen:  1,
-		generic: true,
-	},
-	{
 		name:    "Ctz32",
 		argLen:  1,
 		generic: true,
@@ -4870,21 +20725,6 @@ var opcodeTable = [...]opInfo{
 		generic: true,
 	},
 	{
-		name:    "Clz16",
-		argLen:  1,
-		generic: true,
-	},
-	{
-		name:    "Clz32",
-		argLen:  1,
-		generic: true,
-	},
-	{
-		name:    "Clz64",
-		argLen:  1,
-		generic: true,
-	},
-	{
 		name:    "Bswap32",
 		argLen:  1,
 		generic: true,
@@ -5023,13 +20863,37 @@ var opcodeTable = [...]opInfo{
 	},
 	{
 		name:    "Move",
-		auxType: auxInt64,
+		auxType: auxSizeAndAlign,
 		argLen:  3,
 		generic: true,
 	},
 	{
 		name:    "Zero",
+		auxType: auxSizeAndAlign,
+		argLen:  2,
+		generic: true,
+	},
+	{
+		name:    "StoreWB",
 		auxType: auxInt64,
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "MoveWB",
+		auxType: auxSymSizeAndAlign,
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "MoveWBVolatile",
+		auxType: auxSymSizeAndAlign,
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "ZeroWB",
+		auxType: auxSymSizeAndAlign,
 		argLen:  2,
 		generic: true,
 	},
@@ -5037,30 +20901,35 @@ var opcodeTable = [...]opInfo{
 		name:    "ClosureCall",
 		auxType: auxInt64,
 		argLen:  3,
+		call:    true,
 		generic: true,
 	},
 	{
 		name:    "StaticCall",
 		auxType: auxSymOff,
 		argLen:  1,
+		call:    true,
 		generic: true,
 	},
 	{
 		name:    "DeferCall",
 		auxType: auxInt64,
 		argLen:  1,
+		call:    true,
 		generic: true,
 	},
 	{
 		name:    "GoCall",
 		auxType: auxInt64,
 		argLen:  1,
+		call:    true,
 		generic: true,
 	},
 	{
 		name:    "InterCall",
 		auxType: auxInt64,
 		argLen:  2,
+		call:    true,
 		generic: true,
 	},
 	{
@@ -5234,12 +21103,6 @@ var opcodeTable = [...]opInfo{
 		generic: true,
 	},
 	{
-		name:    "ArrayIndex",
-		auxType: auxInt64,
-		argLen:  1,
-		generic: true,
-	},
-	{
 		name:    "PtrIndex",
 		argLen:  2,
 		generic: true,
@@ -5347,6 +21210,22 @@ var opcodeTable = [...]opInfo{
 		generic: true,
 	},
 	{
+		name:    "ArrayMake0",
+		argLen:  0,
+		generic: true,
+	},
+	{
+		name:    "ArrayMake1",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "ArraySelect",
+		auxType: auxInt64,
+		argLen:  1,
+		generic: true,
+	},
+	{
 		name:    "StoreReg",
 		argLen:  1,
 		generic: true,
@@ -5390,53 +21269,592 @@ var opcodeTable = [...]opInfo{
 		argLen:  2,
 		generic: true,
 	},
+	{
+		name:    "Int64Make",
+		argLen:  2,
+		generic: true,
+	},
+	{
+		name:    "Int64Hi",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Int64Lo",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:        "Add32carry",
+		argLen:      2,
+		commutative: true,
+		generic:     true,
+	},
+	{
+		name:        "Add32withcarry",
+		argLen:      3,
+		commutative: true,
+		generic:     true,
+	},
+	{
+		name:    "Sub32carry",
+		argLen:  2,
+		generic: true,
+	},
+	{
+		name:    "Sub32withcarry",
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "Signmask",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Zeromask",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Slicemask",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Cvt32Uto32F",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Cvt32Uto64F",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Cvt32Fto32U",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Cvt64Fto32U",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Cvt64Uto32F",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Cvt64Uto64F",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Cvt32Fto64U",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Cvt64Fto64U",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Select0",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "Select1",
+		argLen:  1,
+		generic: true,
+	},
+	{
+		name:    "AtomicLoad32",
+		argLen:  2,
+		generic: true,
+	},
+	{
+		name:    "AtomicLoad64",
+		argLen:  2,
+		generic: true,
+	},
+	{
+		name:    "AtomicLoadPtr",
+		argLen:  2,
+		generic: true,
+	},
+	{
+		name:    "AtomicStore32",
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "AtomicStore64",
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "AtomicStorePtrNoWB",
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "AtomicExchange32",
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "AtomicExchange64",
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "AtomicAdd32",
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "AtomicAdd64",
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "AtomicCompareAndSwap32",
+		argLen:  4,
+		generic: true,
+	},
+	{
+		name:    "AtomicCompareAndSwap64",
+		argLen:  4,
+		generic: true,
+	},
+	{
+		name:    "AtomicAnd8",
+		argLen:  3,
+		generic: true,
+	},
+	{
+		name:    "AtomicOr8",
+		argLen:  3,
+		generic: true,
+	},
 }
 
-func (o Op) Asm() obj.As    { return opcodeTable[o].asm }
-func (o Op) String() string { return opcodeTable[o].name }
+func (o Op) Asm() obj.As       { return opcodeTable[o].asm }
+func (o Op) String() string    { return opcodeTable[o].name }
+func (o Op) UsesScratch() bool { return opcodeTable[o].usesScratch }
 
+var registers386 = [...]Register{
+	{0, x86.REG_AX, "AX"},
+	{1, x86.REG_CX, "CX"},
+	{2, x86.REG_DX, "DX"},
+	{3, x86.REG_BX, "BX"},
+	{4, x86.REGSP, "SP"},
+	{5, x86.REG_BP, "BP"},
+	{6, x86.REG_SI, "SI"},
+	{7, x86.REG_DI, "DI"},
+	{8, x86.REG_X0, "X0"},
+	{9, x86.REG_X1, "X1"},
+	{10, x86.REG_X2, "X2"},
+	{11, x86.REG_X3, "X3"},
+	{12, x86.REG_X4, "X4"},
+	{13, x86.REG_X5, "X5"},
+	{14, x86.REG_X6, "X6"},
+	{15, x86.REG_X7, "X7"},
+	{16, 0, "SB"},
+}
+var gpRegMask386 = regMask(239)
+var fpRegMask386 = regMask(65280)
+var specialRegMask386 = regMask(0)
+var framepointerReg386 = int8(5)
+var linkReg386 = int8(-1)
 var registersAMD64 = [...]Register{
-	{0, "AX"},
-	{1, "CX"},
-	{2, "DX"},
-	{3, "BX"},
-	{4, "SP"},
-	{5, "BP"},
-	{6, "SI"},
-	{7, "DI"},
-	{8, "R8"},
-	{9, "R9"},
-	{10, "R10"},
-	{11, "R11"},
-	{12, "R12"},
-	{13, "R13"},
-	{14, "R14"},
-	{15, "R15"},
-	{16, "X0"},
-	{17, "X1"},
-	{18, "X2"},
-	{19, "X3"},
-	{20, "X4"},
-	{21, "X5"},
-	{22, "X6"},
-	{23, "X7"},
-	{24, "X8"},
-	{25, "X9"},
-	{26, "X10"},
-	{27, "X11"},
-	{28, "X12"},
-	{29, "X13"},
-	{30, "X14"},
-	{31, "X15"},
-	{32, "SB"},
-	{33, "FLAGS"},
+	{0, x86.REG_AX, "AX"},
+	{1, x86.REG_CX, "CX"},
+	{2, x86.REG_DX, "DX"},
+	{3, x86.REG_BX, "BX"},
+	{4, x86.REGSP, "SP"},
+	{5, x86.REG_BP, "BP"},
+	{6, x86.REG_SI, "SI"},
+	{7, x86.REG_DI, "DI"},
+	{8, x86.REG_R8, "R8"},
+	{9, x86.REG_R9, "R9"},
+	{10, x86.REG_R10, "R10"},
+	{11, x86.REG_R11, "R11"},
+	{12, x86.REG_R12, "R12"},
+	{13, x86.REG_R13, "R13"},
+	{14, x86.REG_R14, "R14"},
+	{15, x86.REG_R15, "R15"},
+	{16, x86.REG_X0, "X0"},
+	{17, x86.REG_X1, "X1"},
+	{18, x86.REG_X2, "X2"},
+	{19, x86.REG_X3, "X3"},
+	{20, x86.REG_X4, "X4"},
+	{21, x86.REG_X5, "X5"},
+	{22, x86.REG_X6, "X6"},
+	{23, x86.REG_X7, "X7"},
+	{24, x86.REG_X8, "X8"},
+	{25, x86.REG_X9, "X9"},
+	{26, x86.REG_X10, "X10"},
+	{27, x86.REG_X11, "X11"},
+	{28, x86.REG_X12, "X12"},
+	{29, x86.REG_X13, "X13"},
+	{30, x86.REG_X14, "X14"},
+	{31, x86.REG_X15, "X15"},
+	{32, 0, "SB"},
 }
+var gpRegMaskAMD64 = regMask(65519)
+var fpRegMaskAMD64 = regMask(4294901760)
+var specialRegMaskAMD64 = regMask(0)
+var framepointerRegAMD64 = int8(5)
+var linkRegAMD64 = int8(-1)
 var registersARM = [...]Register{
-	{0, "R0"},
-	{1, "R1"},
-	{2, "R2"},
-	{3, "R3"},
-	{4, "SP"},
-	{5, "FLAGS"},
-	{6, "SB"},
+	{0, arm.REG_R0, "R0"},
+	{1, arm.REG_R1, "R1"},
+	{2, arm.REG_R2, "R2"},
+	{3, arm.REG_R3, "R3"},
+	{4, arm.REG_R4, "R4"},
+	{5, arm.REG_R5, "R5"},
+	{6, arm.REG_R6, "R6"},
+	{7, arm.REG_R7, "R7"},
+	{8, arm.REG_R8, "R8"},
+	{9, arm.REG_R9, "R9"},
+	{10, arm.REGG, "g"},
+	{11, arm.REG_R11, "R11"},
+	{12, arm.REG_R12, "R12"},
+	{13, arm.REGSP, "SP"},
+	{14, arm.REG_R14, "R14"},
+	{15, arm.REG_R15, "R15"},
+	{16, arm.REG_F0, "F0"},
+	{17, arm.REG_F1, "F1"},
+	{18, arm.REG_F2, "F2"},
+	{19, arm.REG_F3, "F3"},
+	{20, arm.REG_F4, "F4"},
+	{21, arm.REG_F5, "F5"},
+	{22, arm.REG_F6, "F6"},
+	{23, arm.REG_F7, "F7"},
+	{24, arm.REG_F8, "F8"},
+	{25, arm.REG_F9, "F9"},
+	{26, arm.REG_F10, "F10"},
+	{27, arm.REG_F11, "F11"},
+	{28, arm.REG_F12, "F12"},
+	{29, arm.REG_F13, "F13"},
+	{30, arm.REG_F14, "F14"},
+	{31, arm.REG_F15, "F15"},
+	{32, 0, "SB"},
+}
+var gpRegMaskARM = regMask(21503)
+var fpRegMaskARM = regMask(4294901760)
+var specialRegMaskARM = regMask(0)
+var framepointerRegARM = int8(-1)
+var linkRegARM = int8(14)
+var registersARM64 = [...]Register{
+	{0, arm64.REG_R0, "R0"},
+	{1, arm64.REG_R1, "R1"},
+	{2, arm64.REG_R2, "R2"},
+	{3, arm64.REG_R3, "R3"},
+	{4, arm64.REG_R4, "R4"},
+	{5, arm64.REG_R5, "R5"},
+	{6, arm64.REG_R6, "R6"},
+	{7, arm64.REG_R7, "R7"},
+	{8, arm64.REG_R8, "R8"},
+	{9, arm64.REG_R9, "R9"},
+	{10, arm64.REG_R10, "R10"},
+	{11, arm64.REG_R11, "R11"},
+	{12, arm64.REG_R12, "R12"},
+	{13, arm64.REG_R13, "R13"},
+	{14, arm64.REG_R14, "R14"},
+	{15, arm64.REG_R15, "R15"},
+	{16, arm64.REG_R16, "R16"},
+	{17, arm64.REG_R17, "R17"},
+	{18, arm64.REG_R18, "R18"},
+	{19, arm64.REG_R19, "R19"},
+	{20, arm64.REG_R20, "R20"},
+	{21, arm64.REG_R21, "R21"},
+	{22, arm64.REG_R22, "R22"},
+	{23, arm64.REG_R23, "R23"},
+	{24, arm64.REG_R24, "R24"},
+	{25, arm64.REG_R25, "R25"},
+	{26, arm64.REG_R26, "R26"},
+	{27, arm64.REGG, "g"},
+	{28, arm64.REG_R29, "R29"},
+	{29, arm64.REG_R30, "R30"},
+	{30, arm64.REGSP, "SP"},
+	{31, arm64.REG_F0, "F0"},
+	{32, arm64.REG_F1, "F1"},
+	{33, arm64.REG_F2, "F2"},
+	{34, arm64.REG_F3, "F3"},
+	{35, arm64.REG_F4, "F4"},
+	{36, arm64.REG_F5, "F5"},
+	{37, arm64.REG_F6, "F6"},
+	{38, arm64.REG_F7, "F7"},
+	{39, arm64.REG_F8, "F8"},
+	{40, arm64.REG_F9, "F9"},
+	{41, arm64.REG_F10, "F10"},
+	{42, arm64.REG_F11, "F11"},
+	{43, arm64.REG_F12, "F12"},
+	{44, arm64.REG_F13, "F13"},
+	{45, arm64.REG_F14, "F14"},
+	{46, arm64.REG_F15, "F15"},
+	{47, arm64.REG_F16, "F16"},
+	{48, arm64.REG_F17, "F17"},
+	{49, arm64.REG_F18, "F18"},
+	{50, arm64.REG_F19, "F19"},
+	{51, arm64.REG_F20, "F20"},
+	{52, arm64.REG_F21, "F21"},
+	{53, arm64.REG_F22, "F22"},
+	{54, arm64.REG_F23, "F23"},
+	{55, arm64.REG_F24, "F24"},
+	{56, arm64.REG_F25, "F25"},
+	{57, arm64.REG_F26, "F26"},
+	{58, arm64.REG_F27, "F27"},
+	{59, arm64.REG_F28, "F28"},
+	{60, arm64.REG_F29, "F29"},
+	{61, arm64.REG_F30, "F30"},
+	{62, arm64.REG_F31, "F31"},
+	{63, 0, "SB"},
+}
+var gpRegMaskARM64 = regMask(670826495)
+var fpRegMaskARM64 = regMask(9223372034707292160)
+var specialRegMaskARM64 = regMask(0)
+var framepointerRegARM64 = int8(-1)
+var linkRegARM64 = int8(29)
+var registersMIPS = [...]Register{
+	{0, mips.REG_R0, "R0"},
+	{1, mips.REG_R1, "R1"},
+	{2, mips.REG_R2, "R2"},
+	{3, mips.REG_R3, "R3"},
+	{4, mips.REG_R4, "R4"},
+	{5, mips.REG_R5, "R5"},
+	{6, mips.REG_R6, "R6"},
+	{7, mips.REG_R7, "R7"},
+	{8, mips.REG_R8, "R8"},
+	{9, mips.REG_R9, "R9"},
+	{10, mips.REG_R10, "R10"},
+	{11, mips.REG_R11, "R11"},
+	{12, mips.REG_R12, "R12"},
+	{13, mips.REG_R13, "R13"},
+	{14, mips.REG_R14, "R14"},
+	{15, mips.REG_R15, "R15"},
+	{16, mips.REG_R16, "R16"},
+	{17, mips.REG_R17, "R17"},
+	{18, mips.REG_R18, "R18"},
+	{19, mips.REG_R19, "R19"},
+	{20, mips.REG_R20, "R20"},
+	{21, mips.REG_R21, "R21"},
+	{22, mips.REG_R22, "R22"},
+	{23, mips.REG_R24, "R24"},
+	{24, mips.REG_R25, "R25"},
+	{25, mips.REG_R28, "R28"},
+	{26, mips.REGSP, "SP"},
+	{27, mips.REGG, "g"},
+	{28, mips.REG_R31, "R31"},
+	{29, mips.REG_F0, "F0"},
+	{30, mips.REG_F2, "F2"},
+	{31, mips.REG_F4, "F4"},
+	{32, mips.REG_F6, "F6"},
+	{33, mips.REG_F8, "F8"},
+	{34, mips.REG_F10, "F10"},
+	{35, mips.REG_F12, "F12"},
+	{36, mips.REG_F14, "F14"},
+	{37, mips.REG_F16, "F16"},
+	{38, mips.REG_F18, "F18"},
+	{39, mips.REG_F20, "F20"},
+	{40, mips.REG_F22, "F22"},
+	{41, mips.REG_F24, "F24"},
+	{42, mips.REG_F26, "F26"},
+	{43, mips.REG_F28, "F28"},
+	{44, mips.REG_F30, "F30"},
+	{45, mips.REG_HI, "HI"},
+	{46, mips.REG_LO, "LO"},
+	{47, 0, "SB"},
+}
+var gpRegMaskMIPS = regMask(335544318)
+var fpRegMaskMIPS = regMask(35183835217920)
+var specialRegMaskMIPS = regMask(105553116266496)
+var framepointerRegMIPS = int8(-1)
+var linkRegMIPS = int8(28)
+var registersMIPS64 = [...]Register{
+	{0, mips.REG_R0, "R0"},
+	{1, mips.REG_R1, "R1"},
+	{2, mips.REG_R2, "R2"},
+	{3, mips.REG_R3, "R3"},
+	{4, mips.REG_R4, "R4"},
+	{5, mips.REG_R5, "R5"},
+	{6, mips.REG_R6, "R6"},
+	{7, mips.REG_R7, "R7"},
+	{8, mips.REG_R8, "R8"},
+	{9, mips.REG_R9, "R9"},
+	{10, mips.REG_R10, "R10"},
+	{11, mips.REG_R11, "R11"},
+	{12, mips.REG_R12, "R12"},
+	{13, mips.REG_R13, "R13"},
+	{14, mips.REG_R14, "R14"},
+	{15, mips.REG_R15, "R15"},
+	{16, mips.REG_R16, "R16"},
+	{17, mips.REG_R17, "R17"},
+	{18, mips.REG_R18, "R18"},
+	{19, mips.REG_R19, "R19"},
+	{20, mips.REG_R20, "R20"},
+	{21, mips.REG_R21, "R21"},
+	{22, mips.REG_R22, "R22"},
+	{23, mips.REG_R24, "R24"},
+	{24, mips.REG_R25, "R25"},
+	{25, mips.REGSP, "SP"},
+	{26, mips.REGG, "g"},
+	{27, mips.REG_R31, "R31"},
+	{28, mips.REG_F0, "F0"},
+	{29, mips.REG_F1, "F1"},
+	{30, mips.REG_F2, "F2"},
+	{31, mips.REG_F3, "F3"},
+	{32, mips.REG_F4, "F4"},
+	{33, mips.REG_F5, "F5"},
+	{34, mips.REG_F6, "F6"},
+	{35, mips.REG_F7, "F7"},
+	{36, mips.REG_F8, "F8"},
+	{37, mips.REG_F9, "F9"},
+	{38, mips.REG_F10, "F10"},
+	{39, mips.REG_F11, "F11"},
+	{40, mips.REG_F12, "F12"},
+	{41, mips.REG_F13, "F13"},
+	{42, mips.REG_F14, "F14"},
+	{43, mips.REG_F15, "F15"},
+	{44, mips.REG_F16, "F16"},
+	{45, mips.REG_F17, "F17"},
+	{46, mips.REG_F18, "F18"},
+	{47, mips.REG_F19, "F19"},
+	{48, mips.REG_F20, "F20"},
+	{49, mips.REG_F21, "F21"},
+	{50, mips.REG_F22, "F22"},
+	{51, mips.REG_F23, "F23"},
+	{52, mips.REG_F24, "F24"},
+	{53, mips.REG_F25, "F25"},
+	{54, mips.REG_F26, "F26"},
+	{55, mips.REG_F27, "F27"},
+	{56, mips.REG_F28, "F28"},
+	{57, mips.REG_F29, "F29"},
+	{58, mips.REG_F30, "F30"},
+	{59, mips.REG_F31, "F31"},
+	{60, mips.REG_HI, "HI"},
+	{61, mips.REG_LO, "LO"},
+	{62, 0, "SB"},
+}
+var gpRegMaskMIPS64 = regMask(167772158)
+var fpRegMaskMIPS64 = regMask(1152921504338411520)
+var specialRegMaskMIPS64 = regMask(3458764513820540928)
+var framepointerRegMIPS64 = int8(-1)
+var linkRegMIPS64 = int8(27)
+var registersPPC64 = [...]Register{
+	{0, ppc64.REG_R0, "R0"},
+	{1, ppc64.REGSP, "SP"},
+	{2, 0, "SB"},
+	{3, ppc64.REG_R3, "R3"},
+	{4, ppc64.REG_R4, "R4"},
+	{5, ppc64.REG_R5, "R5"},
+	{6, ppc64.REG_R6, "R6"},
+	{7, ppc64.REG_R7, "R7"},
+	{8, ppc64.REG_R8, "R8"},
+	{9, ppc64.REG_R9, "R9"},
+	{10, ppc64.REG_R10, "R10"},
+	{11, ppc64.REG_R11, "R11"},
+	{12, ppc64.REG_R12, "R12"},
+	{13, ppc64.REG_R13, "R13"},
+	{14, ppc64.REG_R14, "R14"},
+	{15, ppc64.REG_R15, "R15"},
+	{16, ppc64.REG_R16, "R16"},
+	{17, ppc64.REG_R17, "R17"},
+	{18, ppc64.REG_R18, "R18"},
+	{19, ppc64.REG_R19, "R19"},
+	{20, ppc64.REG_R20, "R20"},
+	{21, ppc64.REG_R21, "R21"},
+	{22, ppc64.REG_R22, "R22"},
+	{23, ppc64.REG_R23, "R23"},
+	{24, ppc64.REG_R24, "R24"},
+	{25, ppc64.REG_R25, "R25"},
+	{26, ppc64.REG_R26, "R26"},
+	{27, ppc64.REG_R27, "R27"},
+	{28, ppc64.REG_R28, "R28"},
+	{29, ppc64.REG_R29, "R29"},
+	{30, ppc64.REGG, "g"},
+	{31, ppc64.REG_R31, "R31"},
+	{32, ppc64.REG_F0, "F0"},
+	{33, ppc64.REG_F1, "F1"},
+	{34, ppc64.REG_F2, "F2"},
+	{35, ppc64.REG_F3, "F3"},
+	{36, ppc64.REG_F4, "F4"},
+	{37, ppc64.REG_F5, "F5"},
+	{38, ppc64.REG_F6, "F6"},
+	{39, ppc64.REG_F7, "F7"},
+	{40, ppc64.REG_F8, "F8"},
+	{41, ppc64.REG_F9, "F9"},
+	{42, ppc64.REG_F10, "F10"},
+	{43, ppc64.REG_F11, "F11"},
+	{44, ppc64.REG_F12, "F12"},
+	{45, ppc64.REG_F13, "F13"},
+	{46, ppc64.REG_F14, "F14"},
+	{47, ppc64.REG_F15, "F15"},
+	{48, ppc64.REG_F16, "F16"},
+	{49, ppc64.REG_F17, "F17"},
+	{50, ppc64.REG_F18, "F18"},
+	{51, ppc64.REG_F19, "F19"},
+	{52, ppc64.REG_F20, "F20"},
+	{53, ppc64.REG_F21, "F21"},
+	{54, ppc64.REG_F22, "F22"},
+	{55, ppc64.REG_F23, "F23"},
+	{56, ppc64.REG_F24, "F24"},
+	{57, ppc64.REG_F25, "F25"},
+	{58, ppc64.REG_F26, "F26"},
+	{59, ppc64.REG_F27, "F27"},
+	{60, ppc64.REG_F28, "F28"},
+	{61, ppc64.REG_F29, "F29"},
+	{62, ppc64.REG_F30, "F30"},
+	{63, ppc64.REG_F31, "F31"},
+}
+var gpRegMaskPPC64 = regMask(1073733624)
+var fpRegMaskPPC64 = regMask(576460743713488896)
+var specialRegMaskPPC64 = regMask(0)
+var framepointerRegPPC64 = int8(1)
+var linkRegPPC64 = int8(-1)
+var registersS390X = [...]Register{
+	{0, s390x.REG_R0, "R0"},
+	{1, s390x.REG_R1, "R1"},
+	{2, s390x.REG_R2, "R2"},
+	{3, s390x.REG_R3, "R3"},
+	{4, s390x.REG_R4, "R4"},
+	{5, s390x.REG_R5, "R5"},
+	{6, s390x.REG_R6, "R6"},
+	{7, s390x.REG_R7, "R7"},
+	{8, s390x.REG_R8, "R8"},
+	{9, s390x.REG_R9, "R9"},
+	{10, s390x.REG_R10, "R10"},
+	{11, s390x.REG_R11, "R11"},
+	{12, s390x.REG_R12, "R12"},
+	{13, s390x.REGG, "g"},
+	{14, s390x.REG_R14, "R14"},
+	{15, s390x.REGSP, "SP"},
+	{16, s390x.REG_F0, "F0"},
+	{17, s390x.REG_F1, "F1"},
+	{18, s390x.REG_F2, "F2"},
+	{19, s390x.REG_F3, "F3"},
+	{20, s390x.REG_F4, "F4"},
+	{21, s390x.REG_F5, "F5"},
+	{22, s390x.REG_F6, "F6"},
+	{23, s390x.REG_F7, "F7"},
+	{24, s390x.REG_F8, "F8"},
+	{25, s390x.REG_F9, "F9"},
+	{26, s390x.REG_F10, "F10"},
+	{27, s390x.REG_F11, "F11"},
+	{28, s390x.REG_F12, "F12"},
+	{29, s390x.REG_F13, "F13"},
+	{30, s390x.REG_F14, "F14"},
+	{31, s390x.REG_F15, "F15"},
+	{32, 0, "SB"},
 }
+var gpRegMaskS390X = regMask(21503)
+var fpRegMaskS390X = regMask(4294901760)
+var specialRegMaskS390X = regMask(0)
+var framepointerRegS390X = int8(-1)
+var linkRegS390X = int8(14)
diff --git a/src/cmd/compile/internal/ssa/opt.go b/src/cmd/compile/internal/ssa/opt.go
index caf8cca..f211488 100644
--- a/src/cmd/compile/internal/ssa/opt.go
+++ b/src/cmd/compile/internal/ssa/opt.go
@@ -11,4 +11,7 @@ func opt(f *Func) {
 
 func dec(f *Func) {
 	applyRewrite(f, rewriteBlockdec, rewriteValuedec)
+	if f.Config.IntSize == 4 && f.Config.arch != "amd64p32" {
+		applyRewrite(f, rewriteBlockdec64, rewriteValuedec64)
+	}
 }
diff --git a/src/cmd/compile/internal/ssa/passbm_test.go b/src/cmd/compile/internal/ssa/passbm_test.go
index 87069ab..e4bb0b8 100644
--- a/src/cmd/compile/internal/ssa/passbm_test.go
+++ b/src/cmd/compile/internal/ssa/passbm_test.go
@@ -35,7 +35,6 @@ func benchFnPass(b *testing.B, fn passFunc, size int, bg blockGen) {
 	b.ReportAllocs()
 	c := NewConfig("amd64", DummyFrontend{b}, nil, true)
 	fun := Fun(c, "entry", bg(size)...)
-	domTree(fun.f)
 	CheckFunc(fun.f)
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
@@ -51,7 +50,6 @@ func benchFnBlock(b *testing.B, fn passFunc, bg blockGen) {
 	b.ReportAllocs()
 	c := NewConfig("amd64", DummyFrontend{b}, nil, true)
 	fun := Fun(c, "entry", bg(b.N)...)
-	domTree(fun.f)
 	CheckFunc(fun.f)
 	b.ResetTimer()
 	for i := 0; i < passCount; i++ {
diff --git a/src/cmd/compile/internal/ssa/phiopt.go b/src/cmd/compile/internal/ssa/phiopt.go
index d6272b4..3e9f195 100644
--- a/src/cmd/compile/internal/ssa/phiopt.go
+++ b/src/cmd/compile/internal/ssa/phiopt.go
@@ -24,6 +24,7 @@ package ssa
 //
 // In this case we can replace x with a copy of b.
 func phiopt(f *Func) {
+	sdom := f.sdom()
 	for _, b := range f.Blocks {
 		if len(b.Preds) != 2 || len(b.Values) == 0 {
 			// TODO: handle more than 2 predecessors, e.g. a || b || c.
@@ -57,7 +58,16 @@ func phiopt(f *Func) {
 		}
 
 		for _, v := range b.Values {
-			if v.Op != OpPhi || !v.Type.IsBoolean() {
+			if v.Op != OpPhi {
+				continue
+			}
+
+			// Look for conversions from bool to 0/1.
+			if v.Type.IsInteger() {
+				phioptint(v, b0, reverse)
+			}
+
+			if !v.Type.IsBoolean() {
 				continue
 			}
 
@@ -83,7 +93,7 @@ func phiopt(f *Func) {
 			// value is always computed. This guarantees that the side effects
 			// of value are not seen if a is false.
 			if v.Args[reverse].Op == OpConstBool && v.Args[reverse].AuxInt == 1 {
-				if tmp := v.Args[1-reverse]; f.sdom.isAncestorEq(tmp.Block, b) {
+				if tmp := v.Args[1-reverse]; sdom.isAncestorEq(tmp.Block, b) {
 					v.reset(OpOrB)
 					v.SetArgs2(b0.Control, tmp)
 					if f.pass.debug > 0 {
@@ -99,7 +109,7 @@ func phiopt(f *Func) {
 			// value is always computed. This guarantees that the side effects
 			// of value are not seen if a is false.
 			if v.Args[1-reverse].Op == OpConstBool && v.Args[1-reverse].AuxInt == 0 {
-				if tmp := v.Args[reverse]; f.sdom.isAncestorEq(tmp.Block, b) {
+				if tmp := v.Args[reverse]; sdom.isAncestorEq(tmp.Block, b) {
 					v.reset(OpAndB)
 					v.SetArgs2(b0.Control, tmp)
 					if f.pass.debug > 0 {
@@ -110,5 +120,55 @@ func phiopt(f *Func) {
 			}
 		}
 	}
+}
+
+func phioptint(v *Value, b0 *Block, reverse int) {
+	a0 := v.Args[0]
+	a1 := v.Args[1]
+	if a0.Op != a1.Op {
+		return
+	}
+
+	switch a0.Op {
+	case OpConst8, OpConst16, OpConst32, OpConst64:
+	default:
+		return
+	}
 
+	negate := false
+	switch {
+	case a0.AuxInt == 0 && a1.AuxInt == 1:
+		negate = true
+	case a0.AuxInt == 1 && a1.AuxInt == 0:
+	default:
+		return
+	}
+
+	if reverse == 1 {
+		negate = !negate
+	}
+
+	switch v.Type.Size() {
+	case 1:
+		v.reset(OpCopy)
+	case 2:
+		v.reset(OpZeroExt8to16)
+	case 4:
+		v.reset(OpZeroExt8to32)
+	case 8:
+		v.reset(OpZeroExt8to64)
+	default:
+		v.Fatalf("bad int size %d", v.Type.Size())
+	}
+
+	a := b0.Control
+	if negate {
+		a = v.Block.NewValue1(v.Line, OpNot, a.Type, a)
+	}
+	v.AddArg(a)
+
+	f := b0.Func
+	if f.pass.debug > 0 {
+		f.Config.Warnl(v.Block.Line, "converted OpPhi bool -> int%d", v.Type.Size()*8)
+	}
 }
diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go
index 4416fa2..1925a61 100644
--- a/src/cmd/compile/internal/ssa/prove.go
+++ b/src/cmd/compile/internal/ssa/prove.go
@@ -4,7 +4,10 @@
 
 package ssa
 
-import "math"
+import (
+	"fmt"
+	"math"
+)
 
 type branch int
 
@@ -74,6 +77,10 @@ type limit struct {
 	umin, umax uint64 // umin <= value <= umax, unsigned
 }
 
+func (l limit) String() string {
+	return fmt.Sprintf("sm,SM,um,UM=%d,%d,%d,%d", l.min, l.max, l.umin, l.umax)
+}
+
 var noLimit = limit{math.MinInt64, math.MaxInt64, 0, math.MaxUint64}
 
 // a limitFact is a limit known for a particular value.
@@ -191,7 +198,7 @@ func (ft *factsTable) get(v, w *Value, d domain) relation {
 
 // update updates the set of relations between v and w in domain d
 // restricting it to r.
-func (ft *factsTable) update(v, w *Value, d domain, r relation) {
+func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {
 	if lessByID(w, v) {
 		v, w = w, v
 		r = reverseBits[r]
@@ -293,6 +300,9 @@ func (ft *factsTable) update(v, w *Value, d domain, r relation) {
 		}
 		ft.limitStack = append(ft.limitStack, limitFact{v.ID, old})
 		ft.limits[v.ID] = lim
+		if v.Block.Func.pass.debug > 2 {
+			v.Block.Func.Config.Warnl(parent.Line, "parent=%s, new limits %s %s %s", parent, v, w, lim.String())
+		}
 	}
 }
 
@@ -463,24 +473,26 @@ func prove(f *Func) {
 	})
 
 	ft := newFactsTable()
+	idom := f.Idom()
+	sdom := f.sdom()
 
 	// DFS on the dominator tree.
 	for len(work) > 0 {
 		node := work[len(work)-1]
 		work = work[:len(work)-1]
-		parent := f.idom[node.block.ID]
-		branch := getBranch(f.sdom, parent, node.block)
+		parent := idom[node.block.ID]
+		branch := getBranch(sdom, parent, node.block)
 
 		switch node.state {
 		case descend:
 			if branch != unknown {
 				ft.checkpoint()
 				c := parent.Control
-				updateRestrictions(ft, boolean, nil, c, lt|gt, branch)
+				updateRestrictions(parent, ft, boolean, nil, c, lt|gt, branch)
 				if tr, has := domainRelationTable[parent.Control.Op]; has {
 					// When we branched from parent we learned a new set of
 					// restrictions. Update the factsTable accordingly.
-					updateRestrictions(ft, tr.d, c.Args[0], c.Args[1], tr.r, branch)
+					updateRestrictions(parent, ft, tr.d, c.Args[0], c.Args[1], tr.r, branch)
 				}
 			}
 
@@ -488,7 +500,7 @@ func prove(f *Func) {
 				block: node.block,
 				state: simplify,
 			})
-			for s := f.sdom.Child(node.block); s != nil; s = f.sdom.Sibling(s) {
+			for s := sdom.Child(node.block); s != nil; s = sdom.Sibling(s) {
 				work = append(work, bp{
 					block: s,
 					state: descend,
@@ -536,7 +548,7 @@ func getBranch(sdom SparseTree, p *Block, b *Block) branch {
 
 // updateRestrictions updates restrictions from the immediate
 // dominating block (p) using r. r is adjusted according to the branch taken.
-func updateRestrictions(ft *factsTable, t domain, v, w *Value, r relation, branch branch) {
+func updateRestrictions(parent *Block, ft *factsTable, t domain, v, w *Value, r relation, branch branch) {
 	if t == 0 || branch == unknown {
 		// Trivial case: nothing to do, or branch unknown.
 		// Shoult not happen, but just in case.
@@ -548,7 +560,7 @@ func updateRestrictions(ft *factsTable, t domain, v, w *Value, r relation, branc
 	}
 	for i := domain(1); i <= t; i <<= 1 {
 		if t&i != 0 {
-			ft.update(v, w, i, r)
+			ft.update(parent, v, w, i, r)
 		}
 	}
 }
@@ -556,6 +568,44 @@ func updateRestrictions(ft *factsTable, t domain, v, w *Value, r relation, branc
 // simplifyBlock simplifies block known the restrictions in ft.
 // Returns which branch must always be taken.
 func simplifyBlock(ft *factsTable, b *Block) branch {
+	for _, v := range b.Values {
+		if v.Op != OpSlicemask {
+			continue
+		}
+		add := v.Args[0]
+		if add.Op != OpAdd64 && add.Op != OpAdd32 {
+			continue
+		}
+		// Note that the arg of slicemask was originally a sub, but
+		// was rewritten to an add by generic.rules (if the thing
+		// being subtracted was a constant).
+		x := add.Args[0]
+		y := add.Args[1]
+		if x.Op == OpConst64 || x.Op == OpConst32 {
+			x, y = y, x
+		}
+		if y.Op != OpConst64 && y.Op != OpConst32 {
+			continue
+		}
+		// slicemask(x + y)
+		// if x is larger than -y (y is negative), then slicemask is -1.
+		lim, ok := ft.limits[x.ID]
+		if !ok {
+			continue
+		}
+		if lim.umin > uint64(-y.AuxInt) {
+			if v.Args[0].Op == OpAdd64 {
+				v.reset(OpConst64)
+			} else {
+				v.reset(OpConst32)
+			}
+			if b.Func.pass.debug > 0 {
+				b.Func.Config.Warnl(v.Line, "Proved slicemask not needed")
+			}
+			v.AuxInt = -1
+		}
+	}
+
 	if b.Kind != BlockIf {
 		return unknown
 	}
@@ -564,13 +614,21 @@ func simplifyBlock(ft *factsTable, b *Block) branch {
 	m := ft.get(nil, b.Control, boolean)
 	if m == lt|gt {
 		if b.Func.pass.debug > 0 {
-			b.Func.Config.Warnl(b.Line, "Proved boolean %s", b.Control.Op)
+			if b.Func.pass.debug > 1 {
+				b.Func.Config.Warnl(b.Line, "Proved boolean %s (%s)", b.Control.Op, b.Control)
+			} else {
+				b.Func.Config.Warnl(b.Line, "Proved boolean %s", b.Control.Op)
+			}
 		}
 		return positive
 	}
 	if m == eq {
 		if b.Func.pass.debug > 0 {
-			b.Func.Config.Warnl(b.Line, "Disproved boolean %s", b.Control.Op)
+			if b.Func.pass.debug > 1 {
+				b.Func.Config.Warnl(b.Line, "Disproved boolean %s (%s)", b.Control.Op, b.Control)
+			} else {
+				b.Func.Config.Warnl(b.Line, "Disproved boolean %s", b.Control.Op)
+			}
 		}
 		return negative
 	}
@@ -597,13 +655,21 @@ func simplifyBlock(ft *factsTable, b *Block) branch {
 		m := ft.get(a0, a1, d)
 		if m != 0 && tr.r&m == m {
 			if b.Func.pass.debug > 0 {
-				b.Func.Config.Warnl(b.Line, "Proved %s", c.Op)
+				if b.Func.pass.debug > 1 {
+					b.Func.Config.Warnl(b.Line, "Proved %s (%s)", c.Op, c)
+				} else {
+					b.Func.Config.Warnl(b.Line, "Proved %s", c.Op)
+				}
 			}
 			return positive
 		}
 		if m != 0 && ((lt|eq|gt)^tr.r)&m == m {
 			if b.Func.pass.debug > 0 {
-				b.Func.Config.Warnl(b.Line, "Disproved %s", c.Op)
+				if b.Func.pass.debug > 1 {
+					b.Func.Config.Warnl(b.Line, "Disproved %s (%s)", c.Op, c)
+				} else {
+					b.Func.Config.Warnl(b.Line, "Disproved %s", c.Op)
+				}
 			}
 			return negative
 		}
@@ -618,7 +684,11 @@ func simplifyBlock(ft *factsTable, b *Block) branch {
 		m := ft.get(a0, a1, signed)
 		if m != 0 && tr.r&m == m {
 			if b.Func.pass.debug > 0 {
-				b.Func.Config.Warnl(b.Line, "Proved non-negative bounds %s", c.Op)
+				if b.Func.pass.debug > 1 {
+					b.Func.Config.Warnl(b.Line, "Proved non-negative bounds %s (%s)", c.Op, c)
+				} else {
+					b.Func.Config.Warnl(b.Line, "Proved non-negative bounds %s", c.Op)
+				}
 			}
 			return positive
 		}
@@ -633,6 +703,9 @@ func isNonNegative(v *Value) bool {
 	case OpConst64:
 		return v.AuxInt >= 0
 
+	case OpConst32:
+		return int32(v.AuxInt) >= 0
+
 	case OpStringLen, OpSliceLen, OpSliceCap,
 		OpZeroExt8to64, OpZeroExt16to64, OpZeroExt32to64:
 		return true
diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go
index 1eecd49..2b66982 100644
--- a/src/cmd/compile/internal/ssa/regalloc.go
+++ b/src/cmd/compile/internal/ssa/regalloc.go
@@ -106,6 +106,7 @@
 package ssa
 
 import (
+	"cmd/internal/obj"
 	"fmt"
 	"unsafe"
 )
@@ -180,6 +181,7 @@ func pickReg(r regMask) register {
 
 type use struct {
 	dist int32 // distance from start of the block to a use of a value
+	line int32 // line number of the use
 	next *use  // linked list of uses of a value in nondecreasing dist order
 }
 
@@ -206,6 +208,7 @@ type regAllocState struct {
 	numRegs     register
 	SPReg       register
 	SBReg       register
+	GReg        register
 	allocatable regMask
 
 	// for each block, its primary predecessor.
@@ -240,6 +243,9 @@ type regAllocState struct {
 	// mask of registers currently in use
 	used regMask
 
+	// mask of registers used in the current instruction
+	tmpused regMask
+
 	// current block we're working on
 	curBlock *Block
 
@@ -257,6 +263,10 @@ type regAllocState struct {
 	// spillLive[blockid] is the set of live spills at the end of each block
 	spillLive [][]ID
 
+	// a set of copies we generated to move things around, and
+	// whether it is used in shuffle. Unused copies will be deleted.
+	copies map[*Value]bool
+
 	loopnest *loopnest
 }
 
@@ -276,8 +286,9 @@ type endReg struct {
 }
 
 type startReg struct {
-	r   register
-	vid ID // pre-regalloc value needed in this register
+	r    register
+	vid  ID    // pre-regalloc value needed in this register
+	line int32 // line number of use of this register
 }
 
 // freeReg frees up register r. Any current user of r is kicked out.
@@ -332,14 +343,14 @@ func (s *regAllocState) assignReg(r register, v *Value, c *Value) {
 	s.f.setHome(c, &s.registers[r])
 }
 
-// allocReg chooses a register for v from the set of registers in mask.
+// allocReg chooses a register from the set of registers in mask.
 // If there is no unused register, a Value will be kicked out of
 // a register to make room.
-func (s *regAllocState) allocReg(v *Value, mask regMask) register {
+func (s *regAllocState) allocReg(mask regMask, v *Value) register {
 	mask &= s.allocatable
 	mask &^= s.nospill
 	if mask == 0 {
-		s.f.Fatalf("no register available")
+		s.f.Fatalf("no register available for %s", v)
 	}
 
 	// Pick an unused register if one is available.
@@ -372,7 +383,22 @@ func (s *regAllocState) allocReg(v *Value, mask regMask) register {
 		}
 	}
 	if maxuse == -1 {
-		s.f.Unimplementedf("couldn't find register to spill")
+		s.f.Fatalf("couldn't find register to spill")
+	}
+
+	// Try to move it around before kicking out, if there is a free register.
+	// We generate a Copy and record it. It will be deleted if never used.
+	v2 := s.regs[r].v
+	m := s.compatRegs(v2.Type) &^ s.used &^ s.tmpused &^ (regMask(1) << r)
+	if m != 0 && !s.values[v2.ID].rematerializeable && countRegs(s.values[v2.ID].regs) == 1 {
+		r2 := pickReg(m)
+		c := s.curBlock.NewValue1(v2.Line, OpCopy, v2.Type, s.regs[r].c)
+		s.copies[c] = false
+		if s.f.pass.debug > regDebug {
+			fmt.Printf("copy %s to %s : %s\n", v2, c, s.registers[r2].Name())
+		}
+		s.setOrig(c, v2)
+		s.assignReg(r2, v2, c)
 	}
 	s.freeReg(r)
 	return r
@@ -400,7 +426,7 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line
 	}
 
 	// Allocate a register.
-	r := s.allocReg(v, mask)
+	r := s.allocReg(mask, v)
 
 	// Allocate v to the new register.
 	var c *Value
@@ -435,43 +461,128 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line
 	return c
 }
 
+// isLeaf reports whether f performs any calls.
+func isLeaf(f *Func) bool {
+	for _, b := range f.Blocks {
+		for _, v := range b.Values {
+			if opcodeTable[v.Op].call {
+				return false
+			}
+		}
+	}
+	return true
+}
+
 func (s *regAllocState) init(f *Func) {
 	s.f = f
 	s.registers = f.Config.registers
-	s.numRegs = register(len(s.registers))
-	if s.numRegs > noRegister || s.numRegs > register(unsafe.Sizeof(regMask(0))*8) {
-		panic("too many registers")
+	if nr := len(s.registers); nr == 0 || nr > int(noRegister) || nr > int(unsafe.Sizeof(regMask(0))*8) {
+		s.f.Fatalf("bad number of registers: %d", nr)
+	} else {
+		s.numRegs = register(nr)
 	}
+	// Locate SP, SB, and g registers.
+	s.SPReg = noRegister
+	s.SBReg = noRegister
+	s.GReg = noRegister
 	for r := register(0); r < s.numRegs; r++ {
-		if s.registers[r].Name() == "SP" {
+		switch s.registers[r].Name() {
+		case "SP":
 			s.SPReg = r
-		}
-		if s.registers[r].Name() == "SB" {
+		case "SB":
 			s.SBReg = r
+		case "g":
+			s.GReg = r
+		}
+	}
+	// Make sure we found all required registers.
+	switch noRegister {
+	case s.SPReg:
+		s.f.Fatalf("no SP register found")
+	case s.SBReg:
+		s.f.Fatalf("no SB register found")
+	case s.GReg:
+		if f.Config.hasGReg {
+			s.f.Fatalf("no g register found")
 		}
 	}
 
 	// Figure out which registers we're allowed to use.
-	s.allocatable = regMask(1)<<s.numRegs - 1
+	s.allocatable = s.f.Config.gpRegMask | s.f.Config.fpRegMask | s.f.Config.specialRegMask
 	s.allocatable &^= 1 << s.SPReg
 	s.allocatable &^= 1 << s.SBReg
-	if s.f.Config.ctxt.Framepointer_enabled {
-		s.allocatable &^= 1 << 5 // BP
+	if s.f.Config.hasGReg {
+		s.allocatable &^= 1 << s.GReg
+	}
+	if s.f.Config.ctxt.Framepointer_enabled && s.f.Config.FPReg >= 0 {
+		s.allocatable &^= 1 << uint(s.f.Config.FPReg)
+	}
+	if s.f.Config.ctxt.Flag_shared {
+		switch s.f.Config.arch {
+		case "ppc64le": // R2 already reserved.
+			s.allocatable &^= 1 << 12 // R12
+		}
+	}
+	if s.f.Config.LinkReg != -1 {
+		if isLeaf(f) {
+			// Leaf functions don't save/restore the link register.
+			s.allocatable &^= 1 << uint(s.f.Config.LinkReg)
+		}
+		if s.f.Config.arch == "arm" && obj.GOARM == 5 {
+			// On ARMv5 we insert softfloat calls at each FP instruction.
+			// This clobbers LR almost everywhere. Disable allocating LR
+			// on ARMv5.
+			s.allocatable &^= 1 << uint(s.f.Config.LinkReg)
+		}
 	}
 	if s.f.Config.ctxt.Flag_dynlink {
-		s.allocatable &^= 1 << 15 // R15
+		switch s.f.Config.arch {
+		case "amd64":
+			s.allocatable &^= 1 << 15 // R15
+		case "arm":
+			s.allocatable &^= 1 << 9 // R9
+		case "ppc64le": // R2 already reserved.
+			s.allocatable &^= 1 << 12 // R12
+		case "arm64":
+			// nothing to do?
+		case "386":
+			// nothing to do.
+			// Note that for Flag_shared (position independent code)
+			// we do need to be careful, but that carefulness is hidden
+			// in the rewrite rules so we always have a free register
+			// available for global load/stores. See gen/386.rules (search for Flag_shared).
+		case "s390x":
+			// nothing to do, R10 & R11 already reserved
+		default:
+			s.f.Config.fe.Fatalf(0, "arch %s not implemented", s.f.Config.arch)
+		}
+	}
+	if s.f.Config.nacl {
+		switch s.f.Config.arch {
+		case "arm":
+			s.allocatable &^= 1 << 9 // R9 is "thread pointer" on nacl/arm
+		case "amd64p32":
+			s.allocatable &^= 1 << 5  // BP - reserved for nacl
+			s.allocatable &^= 1 << 15 // R15 - reserved for nacl
+		}
+	}
+	if s.f.Config.use387 {
+		s.allocatable &^= 1 << 15 // X7 disallowed (one 387 register is used as scratch space during SSE->387 generation in ../x86/387.go)
 	}
 
 	s.regs = make([]regState, s.numRegs)
 	s.values = make([]valState, f.NumValues())
 	s.orig = make([]*Value, f.NumValues())
+	s.copies = make(map[*Value]bool)
 	for _, b := range f.Blocks {
 		for _, v := range b.Values {
-			if !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() {
+			if !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() && !v.Type.IsTuple() {
 				s.values[v.ID].needReg = true
 				s.values[v.ID].rematerializeable = v.rematerializeable()
 				s.orig[v.ID] = v
 			}
+			// Note: needReg is false for values returning Tuple types.
+			// Instead, we mark the corresponding Selects as needReg.
 		}
 	}
 	s.computeLive()
@@ -506,7 +617,7 @@ func (s *regAllocState) init(f *Func) {
 
 // Adds a use record for id at distance dist from the start of the block.
 // All calls to addUse must happen with nonincreasing dist.
-func (s *regAllocState) addUse(id ID, dist int32) {
+func (s *regAllocState) addUse(id ID, dist int32, line int32) {
 	r := s.freeUseRecords
 	if r != nil {
 		s.freeUseRecords = r.next
@@ -514,6 +625,7 @@ func (s *regAllocState) addUse(id ID, dist int32) {
 		r = &use{}
 	}
 	r.dist = dist
+	r.line = line
 	r.next = s.values[id].uses
 	s.values[id].uses = r
 	if r.next != nil && dist > r.next.dist {
@@ -563,10 +675,13 @@ func (s *regAllocState) setState(regs []endReg) {
 // compatRegs returns the set of registers which can store a type t.
 func (s *regAllocState) compatRegs(t Type) regMask {
 	var m regMask
+	if t.IsTuple() || t.IsFlags() {
+		return 0
+	}
 	if t.IsFloat() || t == TypeInt128 {
-		m = 0xffff << 16 // X0-X15
+		m = s.f.Config.fpRegMask
 	} else {
-		m = 0xffff << 0 // AX-R15
+		m = s.f.Config.gpRegMask
 	}
 	return m & s.allocatable
 }
@@ -639,16 +754,12 @@ func (s *regAllocState) regalloc(f *Func) {
 		// Initialize liveSet and uses fields for this block.
 		// Walk backwards through the block doing liveness analysis.
 		liveSet.clear()
-		d := int32(len(b.Values))
-		if b.Kind == BlockCall || b.Kind == BlockDefer {
-			d += unlikelyDistance
-		}
 		for _, e := range s.live[b.ID] {
-			s.addUse(e.ID, d+e.dist) // pseudo-uses from beyond end of block
+			s.addUse(e.ID, int32(len(b.Values))+e.dist, e.line) // pseudo-uses from beyond end of block
 			liveSet.add(e.ID)
 		}
 		if v := b.Control; v != nil && s.values[v.ID].needReg {
-			s.addUse(v.ID, int32(len(b.Values))) // psuedo-use by control value
+			s.addUse(v.ID, int32(len(b.Values)), b.Line) // psuedo-use by control value
 			liveSet.add(v.ID)
 		}
 		for i := len(b.Values) - 1; i >= 0; i-- {
@@ -664,7 +775,7 @@ func (s *regAllocState) regalloc(f *Func) {
 				if !s.values[a.ID].needReg {
 					continue
 				}
-				s.addUse(a.ID, int32(i))
+				s.addUse(a.ID, int32(i), v.Line)
 				liveSet.add(a.ID)
 			}
 		}
@@ -753,10 +864,11 @@ func (s *regAllocState) regalloc(f *Func) {
 					continue
 				}
 				a := v.Args[idx]
-				m := s.values[a.ID].regs &^ phiUsed
+				// Some instructions target not-allocatable registers.
+				// They're not suitable for further (phi-function) allocation.
+				m := s.values[a.ID].regs &^ phiUsed & s.allocatable
 				if m != 0 {
 					r := pickReg(m)
-					s.freeReg(r)
 					phiUsed |= regMask(1) << r
 					phiRegs = append(phiRegs, r)
 				} else {
@@ -765,7 +877,7 @@ func (s *regAllocState) regalloc(f *Func) {
 			}
 
 			// Second pass - deallocate any phi inputs which are now dead.
-			for _, v := range phis {
+			for i, v := range phis {
 				if !s.values[v.ID].needReg {
 					continue
 				}
@@ -774,6 +886,31 @@ func (s *regAllocState) regalloc(f *Func) {
 					// Input is dead beyond the phi, deallocate
 					// anywhere else it might live.
 					s.freeRegs(s.values[a.ID].regs)
+				} else {
+					// Input is still live.
+					// Try to move it around before kicking out, if there is a free register.
+					// We generate a Copy in the predecessor block and record it. It will be
+					// deleted if never used.
+					r := phiRegs[i]
+					if r == noRegister {
+						continue
+					}
+					// Pick a free register. At this point some registers used in the predecessor
+					// block may have been deallocated. Those are the ones used for Phis. Exclude
+					// them (and they are not going to be helpful anyway).
+					m := s.compatRegs(a.Type) &^ s.used &^ phiUsed
+					if m != 0 && !s.values[a.ID].rematerializeable && countRegs(s.values[a.ID].regs) == 1 {
+						r2 := pickReg(m)
+						c := p.NewValue1(a.Line, OpCopy, a.Type, s.regs[r].c)
+						s.copies[c] = false
+						if s.f.pass.debug > regDebug {
+							fmt.Printf("copy %s to %s : %s\n", a, c, s.registers[r2].Name())
+						}
+						s.setOrig(c, a)
+						s.assignReg(r2, a, c)
+						s.endRegs[p.ID] = append(s.endRegs[p.ID], endReg{r2, a, c})
+					}
+					s.freeReg(r)
 				}
 			}
 
@@ -786,6 +923,9 @@ func (s *regAllocState) regalloc(f *Func) {
 				if phiRegs[i] != noRegister {
 					continue
 				}
+				if s.f.Config.use387 && v.Type.IsFloat() {
+					continue // 387 can't handle floats in registers between blocks
+				}
 				m := s.compatRegs(v.Type) &^ phiUsed &^ s.used
 				if m != 0 {
 					r := pickReg(m)
@@ -833,7 +973,7 @@ func (s *regAllocState) regalloc(f *Func) {
 					// specially during merge edge processing.
 					continue
 				}
-				regList = append(regList, startReg{r, v.ID})
+				regList = append(regList, startReg{r, v.ID, s.values[v.ID].uses.line})
 			}
 			s.startRegs[b.ID] = regList
 
@@ -878,7 +1018,7 @@ func (s *regAllocState) regalloc(f *Func) {
 				if !ok {
 					continue
 				}
-				desired.add(v.Args[pidx].ID, register(rp.Num))
+				desired.add(v.Args[pidx].ID, register(rp.num))
 			}
 		}
 		// Walk values backwards computing desired register info.
@@ -915,6 +1055,7 @@ func (s *regAllocState) regalloc(f *Func) {
 			if s.f.pass.debug > regDebug {
 				fmt.Printf("  processing %s\n", v.LongString())
 			}
+			regspec := opcodeTable[v.Op].reg
 			if v.Op == OpPhi {
 				f.Fatalf("phi %s not at start of block", v)
 			}
@@ -930,6 +1071,28 @@ func (s *regAllocState) regalloc(f *Func) {
 				s.advanceUses(v)
 				continue
 			}
+			if v.Op == OpSelect0 || v.Op == OpSelect1 {
+				if s.values[v.ID].needReg {
+					var i = 0
+					if v.Op == OpSelect1 {
+						i = 1
+					}
+					s.assignReg(register(s.f.getHome(v.Args[0].ID).(LocPair)[i].(*Register).num), v, v)
+				}
+				b.Values = append(b.Values, v)
+				s.advanceUses(v)
+				goto issueSpill
+			}
+			if v.Op == OpGetG && s.f.Config.hasGReg {
+				// use hardware g register
+				if s.regs[s.GReg].v != nil {
+					s.freeReg(s.GReg) // kick out the old value
+				}
+				s.assignReg(s.GReg, v, v)
+				b.Values = append(b.Values, v)
+				s.advanceUses(v)
+				goto issueSpill
+			}
 			if v.Op == OpArg {
 				// Args are "pre-spilled" values. We don't allocate
 				// any register here. We just set up the spill pointer to
@@ -957,7 +1120,6 @@ func (s *regAllocState) regalloc(f *Func) {
 				b.Values = append(b.Values, v)
 				continue
 			}
-			regspec := opcodeTable[v.Op].reg
 			if len(regspec.inputs) == 0 && len(regspec.outputs) == 0 {
 				// No register allocation required (or none specified yet)
 				s.freeRegs(regspec.clobbers)
@@ -1002,10 +1164,6 @@ func (s *regAllocState) regalloc(f *Func) {
 			args = append(args[:0], v.Args...)
 			for _, i := range regspec.inputs {
 				mask := i.regs
-				if mask == flagRegMask {
-					// TODO: remove flag input from regspec.inputs.
-					continue
-				}
 				if mask&s.values[args[i.idx].ID].regs == 0 {
 					// Need a new register for the input.
 					mask &= s.allocatable
@@ -1037,6 +1195,10 @@ func (s *regAllocState) regalloc(f *Func) {
 					// arg0 is dead.  We can clobber its register.
 					goto ok
 				}
+				if s.values[v.Args[0].ID].rematerializeable {
+					// We can rematerialize the input, don't worry about clobbering it.
+					goto ok
+				}
 				if countRegs(s.values[v.Args[0].ID].regs) >= 2 {
 					// we have at least 2 copies of arg0.  We can afford to clobber one.
 					goto ok
@@ -1046,6 +1208,10 @@ func (s *regAllocState) regalloc(f *Func) {
 						args[0], args[1] = args[1], args[0]
 						goto ok
 					}
+					if s.values[v.Args[1].ID].rematerializeable {
+						args[0], args[1] = args[1], args[0]
+						goto ok
+					}
 					if countRegs(s.values[v.Args[1].ID].regs) >= 2 {
 						args[0], args[1] = args[1], args[0]
 						goto ok
@@ -1080,7 +1246,8 @@ func (s *regAllocState) regalloc(f *Func) {
 				for _, r := range dinfo[idx].in[0] {
 					if r != noRegister && m>>r&1 != 0 {
 						m = regMask(1) << r
-						s.allocValToReg(v.Args[0], m, true, v.Line)
+						c := s.allocValToReg(v.Args[0], m, true, v.Line)
+						s.copies[c] = false
 						// Note: no update to args[0] so the instruction will
 						// use the original copy.
 						goto ok
@@ -1090,7 +1257,8 @@ func (s *regAllocState) regalloc(f *Func) {
 					for _, r := range dinfo[idx].in[1] {
 						if r != noRegister && m>>r&1 != 0 {
 							m = regMask(1) << r
-							s.allocValToReg(v.Args[1], m, true, v.Line)
+							c := s.allocValToReg(v.Args[1], m, true, v.Line)
+							s.copies[c] = false
 							args[0], args[1] = args[1], args[0]
 							goto ok
 						}
@@ -1101,65 +1269,102 @@ func (s *regAllocState) regalloc(f *Func) {
 					m &^= desired.avoid
 				}
 				// Save input 0 to a new register so we can clobber it.
-				s.allocValToReg(v.Args[0], m, true, v.Line)
-			ok:
+				c := s.allocValToReg(v.Args[0], m, true, v.Line)
+				s.copies[c] = false
 			}
 
+		ok:
 			// Now that all args are in regs, we're ready to issue the value itself.
 			// Before we pick a register for the output value, allow input registers
 			// to be deallocated. We do this here so that the output can use the
 			// same register as a dying input.
-			s.nospill = 0
-			s.advanceUses(v) // frees any registers holding args that are no longer live
+			if !opcodeTable[v.Op].resultNotInArgs {
+				s.tmpused = s.nospill
+				s.nospill = 0
+				s.advanceUses(v) // frees any registers holding args that are no longer live
+			}
 
 			// Dump any registers which will be clobbered
 			s.freeRegs(regspec.clobbers)
-
-			// Pick register for output.
-			if s.values[v.ID].needReg {
-				mask := regspec.outputs[0] & s.allocatable
-				if opcodeTable[v.Op].resultInArg0 {
-					if !opcodeTable[v.Op].commutative {
-						// Output must use the same register as input 0.
-						r := register(s.f.getHome(args[0].ID).(*Register).Num)
-						mask = regMask(1) << r
-					} else {
-						// Output must use the same register as input 0 or 1.
-						r0 := register(s.f.getHome(args[0].ID).(*Register).Num)
-						r1 := register(s.f.getHome(args[1].ID).(*Register).Num)
-						// Check r0 and r1 for desired output register.
-						found := false
-						for _, r := range dinfo[idx].out {
-							if (r == r0 || r == r1) && (mask&^s.used)>>r&1 != 0 {
-								mask = regMask(1) << r
-								found = true
-								if r == r1 {
-									args[0], args[1] = args[1], args[0]
+			s.tmpused |= regspec.clobbers
+
+			// Pick registers for outputs.
+			{
+				outRegs := [2]register{noRegister, noRegister}
+				var used regMask
+				for _, out := range regspec.outputs {
+					mask := out.regs & s.allocatable &^ used
+					if mask == 0 {
+						continue
+					}
+					if opcodeTable[v.Op].resultInArg0 && out.idx == 0 {
+						if !opcodeTable[v.Op].commutative {
+							// Output must use the same register as input 0.
+							r := register(s.f.getHome(args[0].ID).(*Register).num)
+							mask = regMask(1) << r
+						} else {
+							// Output must use the same register as input 0 or 1.
+							r0 := register(s.f.getHome(args[0].ID).(*Register).num)
+							r1 := register(s.f.getHome(args[1].ID).(*Register).num)
+							// Check r0 and r1 for desired output register.
+							found := false
+							for _, r := range dinfo[idx].out {
+								if (r == r0 || r == r1) && (mask&^s.used)>>r&1 != 0 {
+									mask = regMask(1) << r
+									found = true
+									if r == r1 {
+										args[0], args[1] = args[1], args[0]
+									}
+									break
 								}
-								break
+							}
+							if !found {
+								// Neither are desired, pick r0.
+								mask = regMask(1) << r0
 							}
 						}
-						if !found {
-							// Neither are desired, pick r0.
-							mask = regMask(1) << r0
+					}
+					for _, r := range dinfo[idx].out {
+						if r != noRegister && (mask&^s.used)>>r&1 != 0 {
+							// Desired register is allowed and unused.
+							mask = regMask(1) << r
+							break
 						}
 					}
-				}
-				for _, r := range dinfo[idx].out {
-					if r != noRegister && (mask&^s.used)>>r&1 != 0 {
-						// Desired register is allowed and unused.
-						mask = regMask(1) << r
-						break
+					// Avoid registers we're saving for other values.
+					if mask&^desired.avoid != 0 {
+						mask &^= desired.avoid
+					}
+					r := s.allocReg(mask, v)
+					outRegs[out.idx] = r
+					used |= regMask(1) << r
+					s.tmpused |= regMask(1) << r
+				}
+				// Record register choices
+				if v.Type.IsTuple() {
+					var outLocs LocPair
+					if r := outRegs[0]; r != noRegister {
+						outLocs[0] = &s.registers[r]
+					}
+					if r := outRegs[1]; r != noRegister {
+						outLocs[1] = &s.registers[r]
+					}
+					s.f.setHome(v, outLocs)
+					// Note that subsequent SelectX instructions will do the assignReg calls.
+				} else {
+					if r := outRegs[0]; r != noRegister {
+						s.assignReg(r, v, v)
 					}
 				}
-				// Avoid registers we're saving for other values.
-				if mask&^desired.avoid != 0 {
-					mask &^= desired.avoid
-				}
-				r := s.allocReg(v, mask)
-				s.assignReg(r, v, v)
 			}
 
+			// deallocate dead args, if we have not done so
+			if opcodeTable[v.Op].resultNotInArgs {
+				s.nospill = 0
+				s.advanceUses(v) // frees any registers holding args that are no longer live
+			}
+			s.tmpused = 0
+
 			// Issue the Value itself.
 			for i, a := range args {
 				v.SetArg(i, a) // use register version of arguments
@@ -1176,6 +1381,7 @@ func (s *regAllocState) regalloc(f *Func) {
 			//     f()
 			// }
 			// It would be good to have both spill and restore inside the IF.
+		issueSpill:
 			if s.values[v.ID].needReg {
 				spill := b.NewValue1(v.Line, OpStoreReg, v.Type, v)
 				s.setOrig(spill, v)
@@ -1194,9 +1400,14 @@ func (s *regAllocState) regalloc(f *Func) {
 			if s.f.pass.debug > regDebug {
 				fmt.Printf("  processing control %s\n", v.LongString())
 			}
-			// TODO: regspec for block control values, instead of using
-			// register set from the control op's output.
-			s.allocValToReg(v, opcodeTable[v.Op].reg.outputs[0], false, b.Line)
+			// We assume that a control input can be passed in any
+			// type-compatible register. If this turns out not to be true,
+			// we'll need to introduce a regspec for a block's control value.
+			b.Control = s.allocValToReg(v, s.compatRegs(v.Type), false, b.Line)
+			if b.Control != v {
+				v.Uses--
+				b.Control.Uses++
+			}
 			// Remove this use from the uses list.
 			vi := &s.values[v.ID]
 			u := vi.uses
@@ -1208,6 +1419,11 @@ func (s *regAllocState) regalloc(f *Func) {
 			s.freeUseRecords = u
 		}
 
+		// Spill any values that can't live across basic block boundaries.
+		if s.f.Config.use387 {
+			s.freeRegs(s.f.Config.fpRegMask)
+		}
+
 		// If we are approaching a merge point and we are the primary
 		// predecessor of it, find live values that we use soon after
 		// the merge point and promote them to registers now.
@@ -1230,7 +1446,13 @@ func (s *regAllocState) regalloc(f *Func) {
 				if vi.regs != 0 {
 					continue
 				}
+				if vi.rematerializeable {
+					continue
+				}
 				v := s.orig[vid]
+				if s.f.Config.use387 && v.Type.IsFloat() {
+					continue // 387 can't handle floats in registers between blocks
+				}
 				m := s.compatRegs(v.Type) &^ s.used
 				if m&^desired.avoid != 0 {
 					m &^= desired.avoid
@@ -1263,8 +1485,7 @@ func (s *regAllocState) regalloc(f *Func) {
 		}
 		s.endRegs[b.ID] = regList
 
-		// Check. TODO: remove
-		{
+		if checkEnabled {
 			liveSet.clear()
 			for _, x := range s.live[b.ID] {
 				liveSet.add(x.ID)
@@ -1387,7 +1608,7 @@ func (s *regAllocState) regalloc(f *Func) {
 	for i := range s.values {
 		vi := s.values[i]
 		if vi.spillUsed {
-			if s.f.pass.debug > logSpills {
+			if s.f.pass.debug > logSpills && vi.spill.Op != OpArg {
 				s.f.Config.Warnl(vi.spill.Line, "spilled value at %v remains", vi.spill)
 			}
 			continue
@@ -1545,6 +1766,39 @@ sinking:
 		}
 	}
 
+	// Erase any copies we never used.
+	// Also, an unused copy might be the only use of another copy,
+	// so continue erasing until we reach a fixed point.
+	for {
+		progress := false
+		for c, used := range s.copies {
+			if !used && c.Uses == 0 {
+				if s.f.pass.debug > regDebug {
+					fmt.Printf("delete copied value %s\n", c.LongString())
+				}
+				c.Args[0].Uses--
+				f.freeValue(c)
+				delete(s.copies, c)
+				progress = true
+			}
+		}
+		if !progress {
+			break
+		}
+	}
+
+	for _, b := range f.Blocks {
+		i := 0
+		for _, v := range b.Values {
+			if v.Op == OpInvalid {
+				continue
+			}
+			b.Values[i] = v
+			i++
+		}
+		b.Values = b.Values[:i]
+	}
+
 	if f.pass.stats > 0 {
 		f.LogStat("spills_info",
 			nSpills, "spills", nSpillsInner, "inner_spills_remaining", nSpillsSunk, "inner_spills_sunk", nSpillsSunkUnused, "inner_spills_unused", nSpillsNotSunkLateUse, "inner_spills_shuffled", nSpillsChanged, "inner_spills_changed")
@@ -1627,12 +1881,14 @@ type contentRecord struct {
 	vid   ID     // pre-regalloc value
 	c     *Value // cached value
 	final bool   // this is a satisfied destination
+	line  int32  // line number of use of the value
 }
 
 type dstRecord struct {
 	loc    Location // register or stack slot
 	vid    ID       // pre-regalloc value it should contain
 	splice **Value  // place to store reference to the generating instruction
+	line   int32    // line number of use of this location
 }
 
 // setup initializes the edge state for shuffling.
@@ -1655,19 +1911,19 @@ func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive
 
 	// Live registers can be sources.
 	for _, x := range srcReg {
-		e.set(&e.s.registers[x.r], x.v.ID, x.c, false)
+		e.set(&e.s.registers[x.r], x.v.ID, x.c, false, 0) // don't care the line number of the source
 	}
 	// So can all of the spill locations.
 	for _, spillID := range stacklive {
 		v := e.s.orig[spillID]
 		spill := e.s.values[v.ID].spill
-		e.set(e.s.f.getHome(spillID), v.ID, spill, false)
+		e.set(e.s.f.getHome(spillID), v.ID, spill, false, 0) // don't care the line number of the source
 	}
 
 	// Figure out all the destinations we need.
 	dsts := e.destinations[:0]
 	for _, x := range dstReg {
-		dsts = append(dsts, dstRecord{&e.s.registers[x.r], x.vid, nil})
+		dsts = append(dsts, dstRecord{&e.s.registers[x.r], x.vid, nil, x.line})
 	}
 	// Phis need their args to end up in a specific location.
 	for _, v := range e.b.Values {
@@ -1678,7 +1934,7 @@ func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive
 		if loc == nil {
 			continue
 		}
-		dsts = append(dsts, dstRecord{loc, v.Args[idx].ID, &v.Args[idx]})
+		dsts = append(dsts, dstRecord{loc, v.Args[idx].ID, &v.Args[idx], v.Line})
 	}
 	e.destinations = dsts
 
@@ -1703,7 +1959,7 @@ func (e *edgeState) process() {
 	for len(dsts) > 0 {
 		i := 0
 		for _, d := range dsts {
-			if !e.processDest(d.loc, d.vid, d.splice) {
+			if !e.processDest(d.loc, d.vid, d.splice, d.line) {
 				// Failed - save for next iteration.
 				dsts[i] = d
 				i++
@@ -1741,7 +1997,8 @@ func (e *edgeState) process() {
 		// Copy any cycle location to a temp register. This duplicates
 		// one of the cycle entries, allowing the just duplicated value
 		// to be overwritten and the cycle to proceed.
-		loc := dsts[0].loc
+		d := dsts[0]
+		loc := d.loc
 		vid := e.contents[loc].vid
 		c := e.contents[loc].c
 		r := e.findRegFor(c.Type)
@@ -1749,30 +2006,37 @@ func (e *edgeState) process() {
 			fmt.Printf("breaking cycle with v%d in %s:%s\n", vid, loc.Name(), c)
 		}
 		if _, isReg := loc.(*Register); isReg {
-			c = e.p.NewValue1(c.Line, OpCopy, c.Type, c)
+			c = e.p.NewValue1(d.line, OpCopy, c.Type, c)
 		} else {
 			e.s.lateSpillUse(vid)
-			c = e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
+			c = e.p.NewValue1(d.line, OpLoadReg, c.Type, c)
 		}
-		e.set(r, vid, c, false)
+		e.set(r, vid, c, false, d.line)
 	}
 }
 
 // processDest generates code to put value vid into location loc. Returns true
 // if progress was made.
-func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool {
+func (e *edgeState) processDest(loc Location, vid ID, splice **Value, line int32) bool {
 	occupant := e.contents[loc]
 	if occupant.vid == vid {
 		// Value is already in the correct place.
-		e.contents[loc] = contentRecord{vid, occupant.c, true}
+		e.contents[loc] = contentRecord{vid, occupant.c, true, line}
 		if splice != nil {
 			(*splice).Uses--
 			*splice = occupant.c
 			occupant.c.Uses++
+			if occupant.c.Op == OpStoreReg {
+				e.s.lateSpillUse(vid)
+			}
 		}
 		// Note: if splice==nil then c will appear dead. This is
 		// non-SSA formed code, so be careful after this pass not to run
 		// deadcode elimination.
+		if _, ok := e.s.copies[occupant.c]; ok {
+			// The copy at occupant.c was used to avoid spill.
+			e.s.copies[occupant.c] = true
+		}
 		return true
 	}
 
@@ -1813,7 +2077,7 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool {
 	var x *Value
 	if c == nil {
 		if !e.s.values[vid].rematerializeable {
-			e.s.f.Fatalf("can't find source for %s->%s: v%d\n", e.p, e.b, vid)
+			e.s.f.Fatalf("can't find source for %s->%s: %s\n", e.p, e.b, v.LongString())
 		}
 		if dstReg {
 			x = v.copyInto(e.p)
@@ -1823,25 +2087,25 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool {
 			e.erase(loc) // see pre-clobber comment below
 			r := e.findRegFor(v.Type)
 			x = v.copyInto(e.p)
-			e.set(r, vid, x, false)
+			e.set(r, vid, x, false, line)
 			// Make sure we spill with the size of the slot, not the
 			// size of x (which might be wider due to our dropping
 			// of narrowing conversions).
-			x = e.p.NewValue1(x.Line, OpStoreReg, loc.(LocalSlot).Type, x)
+			x = e.p.NewValue1(line, OpStoreReg, loc.(LocalSlot).Type, x)
 		}
 	} else {
 		// Emit move from src to dst.
 		_, srcReg := src.(*Register)
 		if srcReg {
 			if dstReg {
-				x = e.p.NewValue1(c.Line, OpCopy, c.Type, c)
+				x = e.p.NewValue1(line, OpCopy, c.Type, c)
 			} else {
-				x = e.p.NewValue1(c.Line, OpStoreReg, loc.(LocalSlot).Type, c)
+				x = e.p.NewValue1(line, OpStoreReg, loc.(LocalSlot).Type, c)
 			}
 		} else {
 			if dstReg {
 				e.s.lateSpillUse(vid)
-				x = e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
+				x = e.p.NewValue1(line, OpLoadReg, c.Type, c)
 			} else {
 				// mem->mem. Use temp register.
 
@@ -1859,13 +2123,13 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool {
 
 				r := e.findRegFor(c.Type)
 				e.s.lateSpillUse(vid)
-				t := e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
-				e.set(r, vid, t, false)
-				x = e.p.NewValue1(c.Line, OpStoreReg, loc.(LocalSlot).Type, t)
+				t := e.p.NewValue1(line, OpLoadReg, c.Type, c)
+				e.set(r, vid, t, false, line)
+				x = e.p.NewValue1(line, OpStoreReg, loc.(LocalSlot).Type, t)
 			}
 		}
 	}
-	e.set(loc, vid, x, true)
+	e.set(loc, vid, x, true, line)
 	if splice != nil {
 		(*splice).Uses--
 		*splice = x
@@ -1875,10 +2139,10 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool {
 }
 
 // set changes the contents of location loc to hold the given value and its cached representative.
-func (e *edgeState) set(loc Location, vid ID, c *Value, final bool) {
+func (e *edgeState) set(loc Location, vid ID, c *Value, final bool, line int32) {
 	e.s.f.setHome(c, loc)
 	e.erase(loc)
-	e.contents[loc] = contentRecord{vid, c, final}
+	e.contents[loc] = contentRecord{vid, c, final, line}
 	a := e.cache[vid]
 	if len(a) == 0 {
 		e.cachedVals = append(e.cachedVals, vid)
@@ -1886,16 +2150,16 @@ func (e *edgeState) set(loc Location, vid ID, c *Value, final bool) {
 	a = append(a, c)
 	e.cache[vid] = a
 	if r, ok := loc.(*Register); ok {
-		e.usedRegs |= regMask(1) << uint(r.Num)
+		e.usedRegs |= regMask(1) << uint(r.num)
 		if final {
-			e.finalRegs |= regMask(1) << uint(r.Num)
+			e.finalRegs |= regMask(1) << uint(r.num)
 		}
 		if len(a) == 1 {
-			e.uniqueRegs |= regMask(1) << uint(r.Num)
+			e.uniqueRegs |= regMask(1) << uint(r.num)
 		}
 		if len(a) == 2 {
 			if t, ok := e.s.f.getHome(a[0].ID).(*Register); ok {
-				e.uniqueRegs &^= regMask(1) << uint(t.Num)
+				e.uniqueRegs &^= regMask(1) << uint(t.num)
 			}
 		}
 	}
@@ -1917,7 +2181,7 @@ func (e *edgeState) erase(loc Location) {
 		// Add a destination to move this value back into place.
 		// Make sure it gets added to the tail of the destination queue
 		// so we make progress on other moves first.
-		e.extra = append(e.extra, dstRecord{loc, cr.vid, nil})
+		e.extra = append(e.extra, dstRecord{loc, cr.vid, nil, cr.line})
 	}
 
 	// Remove c from the list of cached values.
@@ -1935,14 +2199,14 @@ func (e *edgeState) erase(loc Location) {
 
 	// Update register masks.
 	if r, ok := loc.(*Register); ok {
-		e.usedRegs &^= regMask(1) << uint(r.Num)
+		e.usedRegs &^= regMask(1) << uint(r.num)
 		if cr.final {
-			e.finalRegs &^= regMask(1) << uint(r.Num)
+			e.finalRegs &^= regMask(1) << uint(r.num)
 		}
 	}
 	if len(a) == 1 {
 		if r, ok := e.s.f.getHome(a[0].ID).(*Register); ok {
-			e.uniqueRegs |= regMask(1) << uint(r.Num)
+			e.uniqueRegs |= regMask(1) << uint(r.num)
 		}
 	}
 }
@@ -1985,9 +2249,9 @@ func (e *edgeState) findRegFor(typ Type) Location {
 	for _, vid := range e.cachedVals {
 		a := e.cache[vid]
 		for _, c := range a {
-			if r, ok := e.s.f.getHome(c.ID).(*Register); ok && m>>uint(r.Num)&1 != 0 {
+			if r, ok := e.s.f.getHome(c.ID).(*Register); ok && m>>uint(r.num)&1 != 0 {
 				x := e.p.NewValue1(c.Line, OpStoreReg, c.Type, c)
-				e.set(t, vid, x, false)
+				e.set(t, vid, x, false, c.Line)
 				if e.s.f.pass.debug > regDebug {
 					fmt.Printf("  SPILL %s->%s %s\n", r.Name(), t.Name(), x.LongString())
 				}
@@ -2010,6 +2274,8 @@ func (e *edgeState) findRegFor(typ Type) Location {
 	return nil
 }
 
+// rematerializeable reports whether the register allocator should recompute
+// a value instead of spilling/restoring it.
 func (v *Value) rematerializeable() bool {
 	if !opcodeTable[v.Op].rematerializeable {
 		return false
@@ -2026,6 +2292,7 @@ func (v *Value) rematerializeable() bool {
 type liveInfo struct {
 	ID   ID    // ID of value
 	dist int32 // # of instructions before next use
+	line int32 // line number of next use
 }
 
 // dblock contains information about desired & avoid registers at the end of a block.
@@ -2063,8 +2330,8 @@ func (s *regAllocState) computeLive() {
 	// Walk the dominator tree from end to beginning, just once, treating SCC
 	// components as single blocks, duplicated calculated liveness information
 	// out to all of them.
-	s.loopnest = loopnestfor(f)
-	po := s.loopnest.po
+	po := f.postorder()
+	s.loopnest = f.loopnest()
 	for {
 		changed := false
 
@@ -2073,19 +2340,13 @@ func (s *regAllocState) computeLive() {
 			// Add len(b.Values) to adjust from end-of-block distance
 			// to beginning-of-block distance.
 			live.clear()
-			d := int32(len(b.Values))
-			if b.Kind == BlockCall || b.Kind == BlockDefer {
-				// Because we keep no values in registers across a call,
-				// make every use past a call appear very far away.
-				d += unlikelyDistance
-			}
 			for _, e := range s.live[b.ID] {
-				live.set(e.ID, e.dist+d)
+				live.set(e.ID, e.dist+int32(len(b.Values)), e.line)
 			}
 
 			// Mark control value as live
 			if b.Control != nil && s.values[b.Control.ID].needReg {
-				live.set(b.Control.ID, int32(len(b.Values)))
+				live.set(b.Control.ID, int32(len(b.Values)), b.Line)
 			}
 
 			// Propagate backwards to the start of the block
@@ -2099,9 +2360,15 @@ func (s *regAllocState) computeLive() {
 					phis = append(phis, v)
 					continue
 				}
+				if opcodeTable[v.Op].call {
+					c := live.contents()
+					for i := range c {
+						c[i].val += unlikelyDistance
+					}
+				}
 				for _, a := range v.Args {
 					if s.values[a.ID].needReg {
-						live.set(a.ID, int32(i))
+						live.set(a.ID, int32(i), v.Line)
 					}
 				}
 			}
@@ -2160,7 +2427,7 @@ func (s *regAllocState) computeLive() {
 				// Start t off with the previously known live values at the end of p.
 				t.clear()
 				for _, e := range s.live[p.ID] {
-					t.set(e.ID, e.dist)
+					t.set(e.ID, e.dist, e.line)
 				}
 				update := false
 
@@ -2169,7 +2436,7 @@ func (s *regAllocState) computeLive() {
 					d := e.val + delta
 					if !t.contains(e.key) || d < t.get(e.key) {
 						update = true
-						t.set(e.key, d)
+						t.set(e.key, d, e.aux)
 					}
 				}
 				// Also add the correct arg from the saved phi values.
@@ -2179,7 +2446,7 @@ func (s *regAllocState) computeLive() {
 					id := v.Args[i].ID
 					if s.values[id].needReg && (!t.contains(id) || delta < t.get(id)) {
 						update = true
-						t.set(id, delta)
+						t.set(id, delta, v.Line)
 					}
 				}
 
@@ -2192,7 +2459,7 @@ func (s *regAllocState) computeLive() {
 					l = make([]liveInfo, 0, t.size())
 				}
 				for _, e := range t.contents() {
-					l = append(l, liveInfo{e.key, e.val})
+					l = append(l, liveInfo{e.key, e.val, e.aux})
 				}
 				s.live[p.ID] = l
 				changed = true
diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go
index 61d4234..1f9a90f 100644
--- a/src/cmd/compile/internal/ssa/rewrite.go
+++ b/src/cmd/compile/internal/ssa/rewrite.go
@@ -11,7 +11,7 @@ import (
 	"path/filepath"
 )
 
-func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) {
+func applyRewrite(f *Func, rb func(*Block, *Config) bool, rv func(*Value, *Config) bool) {
 	// repeat rewrites until we find no more rewrites
 	var curb *Block
 	var curv *Value
@@ -34,7 +34,7 @@ func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool)
 				}
 			}
 			curb = b
-			if rb(b) {
+			if rb(b, config) {
 				change = true
 			}
 			curb = nil
@@ -149,6 +149,134 @@ func canMergeSym(x, y interface{}) bool {
 	return x == nil || y == nil
 }
 
+// canMergeLoad reports whether the load can be merged into target without
+// invalidating the schedule.
+func canMergeLoad(target, load *Value) bool {
+	if target.Block.ID != load.Block.ID {
+		// If the load is in a different block do not merge it.
+		return false
+	}
+	mem := load.Args[len(load.Args)-1]
+
+	// We need the load's memory arg to still be alive at target. That
+	// can't be the case if one of target's args depends on a memory
+	// state that is a successor of load's memory arg.
+	//
+	// For example, it would be invalid to merge load into target in
+	// the following situation because newmem has killed oldmem
+	// before target is reached:
+	//     load = read ... oldmem
+	//   newmem = write ... oldmem
+	//     arg0 = read ... newmem
+	//   target = add arg0 load
+	//
+	// If the argument comes from a different block then we can exclude
+	// it immediately because it must dominate load (which is in the
+	// same block as target).
+	var args []*Value
+	for _, a := range target.Args {
+		if a != load && a.Block.ID == target.Block.ID {
+			args = append(args, a)
+		}
+	}
+
+	// memPreds contains memory states known to be predecessors of load's
+	// memory state. It is lazily initialized.
+	var memPreds map[*Value]bool
+search:
+	for i := 0; len(args) > 0; i++ {
+		const limit = 100
+		if i >= limit {
+			// Give up if we have done a lot of iterations.
+			return false
+		}
+		v := args[len(args)-1]
+		args = args[:len(args)-1]
+		if target.Block.ID != v.Block.ID {
+			// Since target and load are in the same block
+			// we can stop searching when we leave the block.
+			continue search
+		}
+		if v.Op == OpPhi {
+			// A Phi implies we have reached the top of the block.
+			continue search
+		}
+		if v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
+			// We could handle this situation however it is likely
+			// to be very rare.
+			return false
+		}
+		if v.Type.IsMemory() {
+			if memPreds == nil {
+				// Initialise a map containing memory states
+				// known to be predecessors of load's memory
+				// state.
+				memPreds = make(map[*Value]bool)
+				m := mem
+				const limit = 50
+				for i := 0; i < limit; i++ {
+					if m.Op == OpPhi {
+						break
+					}
+					if m.Block.ID != target.Block.ID {
+						break
+					}
+					if !m.Type.IsMemory() {
+						break
+					}
+					memPreds[m] = true
+					if len(m.Args) == 0 {
+						break
+					}
+					m = m.Args[len(m.Args)-1]
+				}
+			}
+
+			// We can merge if v is a predecessor of mem.
+			//
+			// For example, we can merge load into target in the
+			// following scenario:
+			//      x = read ... v
+			//    mem = write ... v
+			//   load = read ... mem
+			// target = add x load
+			if memPreds[v] {
+				continue search
+			}
+			return false
+		}
+		if len(v.Args) > 0 && v.Args[len(v.Args)-1] == mem {
+			// If v takes mem as an input then we know mem
+			// is valid at this point.
+			continue search
+		}
+		for _, a := range v.Args {
+			if target.Block.ID == a.Block.ID {
+				args = append(args, a)
+			}
+		}
+	}
+	return true
+}
+
+// isArg returns whether s is an arg symbol
+func isArg(s interface{}) bool {
+	_, ok := s.(*ArgSymbol)
+	return ok
+}
+
+// isAuto returns whether s is an auto symbol
+func isAuto(s interface{}) bool {
+	_, ok := s.(*AutoSymbol)
+	return ok
+}
+
+// isSameSym returns whether sym is the same as the given named symbol
+func isSameSym(sym interface{}, name string) bool {
+	s, ok := sym.(fmt.Stringer)
+	return ok && s.String() == name
+}
+
 // nlz returns the number of leading zeros.
 func nlz(x int64) int64 {
 	// log2(0) == 1, so nlz(0) == 64
@@ -171,6 +299,7 @@ func nto(x int64) int64 {
 }
 
 // log2 returns logarithm in base of uint64(n), with log2(0) = -1.
+// Rounds down.
 func log2(n int64) (l int64) {
 	l = -1
 	x := uint64(n)
@@ -205,6 +334,26 @@ func is32Bit(n int64) bool {
 	return n == int64(int32(n))
 }
 
+// is16Bit reports whether n can be represented as a signed 16 bit integer.
+func is16Bit(n int64) bool {
+	return n == int64(int16(n))
+}
+
+// isU16Bit reports whether n can be represented as an unsigned 16 bit integer.
+func isU16Bit(n int64) bool {
+	return n == int64(uint16(n))
+}
+
+// isU32Bit reports whether n can be represented as an unsigned 32 bit integer.
+func isU32Bit(n int64) bool {
+	return n == int64(uint32(n))
+}
+
+// is20Bit reports whether n can be represented as a signed 20 bit integer.
+func is20Bit(n int64) bool {
+	return -(1<<19) <= n && n < (1<<19)
+}
+
 // b2i translates a boolean value to 0 or 1 for assigning to auxInt.
 func b2i(b bool) int64 {
 	if b {
@@ -254,6 +403,19 @@ func isSamePtr(p1, p2 *Value) bool {
 	return false
 }
 
+// moveSize returns the number of bytes an aligned MOV instruction moves
+func moveSize(align int64, c *Config) int64 {
+	switch {
+	case align%8 == 0 && c.IntSize == 8:
+		return 8
+	case align%4 == 0:
+		return 4
+	case align%2 == 0:
+		return 2
+	}
+	return 1
+}
+
 // mergePoint finds a block among a's blocks which dominates b and is itself
 // dominated by all of a's blocks. Returns nil if it can't find one.
 // Might return nil even if one does exist.
@@ -314,6 +476,24 @@ func clobber(v *Value) bool {
 	return true
 }
 
+// noteRule is an easy way to track if a rule is matched when writing
+// new ones.  Make the rule of interest also conditional on
+//     noteRule("note to self: rule of interest matched")
+// and that message will print when the rule matches.
+func noteRule(s string) bool {
+	fmt.Println(s)
+	return true
+}
+
+// warnRule generates a compiler debug output with string s when
+// cond is true and the rule is fired.
+func warnRule(cond bool, v *Value, s string) bool {
+	if cond {
+		v.Block.Func.Config.Warnl(v.Line, s)
+	}
+	return true
+}
+
 // logRule logs the use of the rule s. This will only be enabled if
 // rewrite rules were generated with the -log option, see gen/rulegen.go.
 func logRule(s string) {
diff --git a/src/cmd/compile/internal/ssa/rewrite386.go b/src/cmd/compile/internal/ssa/rewrite386.go
new file mode 100644
index 0000000..741886d
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/rewrite386.go
@@ -0,0 +1,14787 @@
+// autogenerated from gen/386.rules: do not edit!
+// generated with: cd gen; go run *.go
+
+package ssa
+
+import "math"
+
+var _ = math.MinInt8 // in case not otherwise used
+func rewriteValue386(v *Value, config *Config) bool {
+	switch v.Op {
+	case Op386ADCL:
+		return rewriteValue386_Op386ADCL(v, config)
+	case Op386ADDL:
+		return rewriteValue386_Op386ADDL(v, config)
+	case Op386ADDLcarry:
+		return rewriteValue386_Op386ADDLcarry(v, config)
+	case Op386ADDLconst:
+		return rewriteValue386_Op386ADDLconst(v, config)
+	case Op386ANDL:
+		return rewriteValue386_Op386ANDL(v, config)
+	case Op386ANDLconst:
+		return rewriteValue386_Op386ANDLconst(v, config)
+	case Op386CMPB:
+		return rewriteValue386_Op386CMPB(v, config)
+	case Op386CMPBconst:
+		return rewriteValue386_Op386CMPBconst(v, config)
+	case Op386CMPL:
+		return rewriteValue386_Op386CMPL(v, config)
+	case Op386CMPLconst:
+		return rewriteValue386_Op386CMPLconst(v, config)
+	case Op386CMPW:
+		return rewriteValue386_Op386CMPW(v, config)
+	case Op386CMPWconst:
+		return rewriteValue386_Op386CMPWconst(v, config)
+	case Op386LEAL:
+		return rewriteValue386_Op386LEAL(v, config)
+	case Op386LEAL1:
+		return rewriteValue386_Op386LEAL1(v, config)
+	case Op386LEAL2:
+		return rewriteValue386_Op386LEAL2(v, config)
+	case Op386LEAL4:
+		return rewriteValue386_Op386LEAL4(v, config)
+	case Op386LEAL8:
+		return rewriteValue386_Op386LEAL8(v, config)
+	case Op386MOVBLSX:
+		return rewriteValue386_Op386MOVBLSX(v, config)
+	case Op386MOVBLSXload:
+		return rewriteValue386_Op386MOVBLSXload(v, config)
+	case Op386MOVBLZX:
+		return rewriteValue386_Op386MOVBLZX(v, config)
+	case Op386MOVBload:
+		return rewriteValue386_Op386MOVBload(v, config)
+	case Op386MOVBloadidx1:
+		return rewriteValue386_Op386MOVBloadidx1(v, config)
+	case Op386MOVBstore:
+		return rewriteValue386_Op386MOVBstore(v, config)
+	case Op386MOVBstoreconst:
+		return rewriteValue386_Op386MOVBstoreconst(v, config)
+	case Op386MOVBstoreconstidx1:
+		return rewriteValue386_Op386MOVBstoreconstidx1(v, config)
+	case Op386MOVBstoreidx1:
+		return rewriteValue386_Op386MOVBstoreidx1(v, config)
+	case Op386MOVLload:
+		return rewriteValue386_Op386MOVLload(v, config)
+	case Op386MOVLloadidx1:
+		return rewriteValue386_Op386MOVLloadidx1(v, config)
+	case Op386MOVLloadidx4:
+		return rewriteValue386_Op386MOVLloadidx4(v, config)
+	case Op386MOVLstore:
+		return rewriteValue386_Op386MOVLstore(v, config)
+	case Op386MOVLstoreconst:
+		return rewriteValue386_Op386MOVLstoreconst(v, config)
+	case Op386MOVLstoreconstidx1:
+		return rewriteValue386_Op386MOVLstoreconstidx1(v, config)
+	case Op386MOVLstoreconstidx4:
+		return rewriteValue386_Op386MOVLstoreconstidx4(v, config)
+	case Op386MOVLstoreidx1:
+		return rewriteValue386_Op386MOVLstoreidx1(v, config)
+	case Op386MOVLstoreidx4:
+		return rewriteValue386_Op386MOVLstoreidx4(v, config)
+	case Op386MOVSDconst:
+		return rewriteValue386_Op386MOVSDconst(v, config)
+	case Op386MOVSDload:
+		return rewriteValue386_Op386MOVSDload(v, config)
+	case Op386MOVSDloadidx1:
+		return rewriteValue386_Op386MOVSDloadidx1(v, config)
+	case Op386MOVSDloadidx8:
+		return rewriteValue386_Op386MOVSDloadidx8(v, config)
+	case Op386MOVSDstore:
+		return rewriteValue386_Op386MOVSDstore(v, config)
+	case Op386MOVSDstoreidx1:
+		return rewriteValue386_Op386MOVSDstoreidx1(v, config)
+	case Op386MOVSDstoreidx8:
+		return rewriteValue386_Op386MOVSDstoreidx8(v, config)
+	case Op386MOVSSconst:
+		return rewriteValue386_Op386MOVSSconst(v, config)
+	case Op386MOVSSload:
+		return rewriteValue386_Op386MOVSSload(v, config)
+	case Op386MOVSSloadidx1:
+		return rewriteValue386_Op386MOVSSloadidx1(v, config)
+	case Op386MOVSSloadidx4:
+		return rewriteValue386_Op386MOVSSloadidx4(v, config)
+	case Op386MOVSSstore:
+		return rewriteValue386_Op386MOVSSstore(v, config)
+	case Op386MOVSSstoreidx1:
+		return rewriteValue386_Op386MOVSSstoreidx1(v, config)
+	case Op386MOVSSstoreidx4:
+		return rewriteValue386_Op386MOVSSstoreidx4(v, config)
+	case Op386MOVWLSX:
+		return rewriteValue386_Op386MOVWLSX(v, config)
+	case Op386MOVWLSXload:
+		return rewriteValue386_Op386MOVWLSXload(v, config)
+	case Op386MOVWLZX:
+		return rewriteValue386_Op386MOVWLZX(v, config)
+	case Op386MOVWload:
+		return rewriteValue386_Op386MOVWload(v, config)
+	case Op386MOVWloadidx1:
+		return rewriteValue386_Op386MOVWloadidx1(v, config)
+	case Op386MOVWloadidx2:
+		return rewriteValue386_Op386MOVWloadidx2(v, config)
+	case Op386MOVWstore:
+		return rewriteValue386_Op386MOVWstore(v, config)
+	case Op386MOVWstoreconst:
+		return rewriteValue386_Op386MOVWstoreconst(v, config)
+	case Op386MOVWstoreconstidx1:
+		return rewriteValue386_Op386MOVWstoreconstidx1(v, config)
+	case Op386MOVWstoreconstidx2:
+		return rewriteValue386_Op386MOVWstoreconstidx2(v, config)
+	case Op386MOVWstoreidx1:
+		return rewriteValue386_Op386MOVWstoreidx1(v, config)
+	case Op386MOVWstoreidx2:
+		return rewriteValue386_Op386MOVWstoreidx2(v, config)
+	case Op386MULL:
+		return rewriteValue386_Op386MULL(v, config)
+	case Op386MULLconst:
+		return rewriteValue386_Op386MULLconst(v, config)
+	case Op386NEGL:
+		return rewriteValue386_Op386NEGL(v, config)
+	case Op386NOTL:
+		return rewriteValue386_Op386NOTL(v, config)
+	case Op386ORL:
+		return rewriteValue386_Op386ORL(v, config)
+	case Op386ORLconst:
+		return rewriteValue386_Op386ORLconst(v, config)
+	case Op386ROLBconst:
+		return rewriteValue386_Op386ROLBconst(v, config)
+	case Op386ROLLconst:
+		return rewriteValue386_Op386ROLLconst(v, config)
+	case Op386ROLWconst:
+		return rewriteValue386_Op386ROLWconst(v, config)
+	case Op386SARB:
+		return rewriteValue386_Op386SARB(v, config)
+	case Op386SARBconst:
+		return rewriteValue386_Op386SARBconst(v, config)
+	case Op386SARL:
+		return rewriteValue386_Op386SARL(v, config)
+	case Op386SARLconst:
+		return rewriteValue386_Op386SARLconst(v, config)
+	case Op386SARW:
+		return rewriteValue386_Op386SARW(v, config)
+	case Op386SARWconst:
+		return rewriteValue386_Op386SARWconst(v, config)
+	case Op386SBBL:
+		return rewriteValue386_Op386SBBL(v, config)
+	case Op386SBBLcarrymask:
+		return rewriteValue386_Op386SBBLcarrymask(v, config)
+	case Op386SETA:
+		return rewriteValue386_Op386SETA(v, config)
+	case Op386SETAE:
+		return rewriteValue386_Op386SETAE(v, config)
+	case Op386SETB:
+		return rewriteValue386_Op386SETB(v, config)
+	case Op386SETBE:
+		return rewriteValue386_Op386SETBE(v, config)
+	case Op386SETEQ:
+		return rewriteValue386_Op386SETEQ(v, config)
+	case Op386SETG:
+		return rewriteValue386_Op386SETG(v, config)
+	case Op386SETGE:
+		return rewriteValue386_Op386SETGE(v, config)
+	case Op386SETL:
+		return rewriteValue386_Op386SETL(v, config)
+	case Op386SETLE:
+		return rewriteValue386_Op386SETLE(v, config)
+	case Op386SETNE:
+		return rewriteValue386_Op386SETNE(v, config)
+	case Op386SHLL:
+		return rewriteValue386_Op386SHLL(v, config)
+	case Op386SHRB:
+		return rewriteValue386_Op386SHRB(v, config)
+	case Op386SHRL:
+		return rewriteValue386_Op386SHRL(v, config)
+	case Op386SHRW:
+		return rewriteValue386_Op386SHRW(v, config)
+	case Op386SUBL:
+		return rewriteValue386_Op386SUBL(v, config)
+	case Op386SUBLcarry:
+		return rewriteValue386_Op386SUBLcarry(v, config)
+	case Op386SUBLconst:
+		return rewriteValue386_Op386SUBLconst(v, config)
+	case Op386XORL:
+		return rewriteValue386_Op386XORL(v, config)
+	case Op386XORLconst:
+		return rewriteValue386_Op386XORLconst(v, config)
+	case OpAdd16:
+		return rewriteValue386_OpAdd16(v, config)
+	case OpAdd32:
+		return rewriteValue386_OpAdd32(v, config)
+	case OpAdd32F:
+		return rewriteValue386_OpAdd32F(v, config)
+	case OpAdd32carry:
+		return rewriteValue386_OpAdd32carry(v, config)
+	case OpAdd32withcarry:
+		return rewriteValue386_OpAdd32withcarry(v, config)
+	case OpAdd64F:
+		return rewriteValue386_OpAdd64F(v, config)
+	case OpAdd8:
+		return rewriteValue386_OpAdd8(v, config)
+	case OpAddPtr:
+		return rewriteValue386_OpAddPtr(v, config)
+	case OpAddr:
+		return rewriteValue386_OpAddr(v, config)
+	case OpAnd16:
+		return rewriteValue386_OpAnd16(v, config)
+	case OpAnd32:
+		return rewriteValue386_OpAnd32(v, config)
+	case OpAnd8:
+		return rewriteValue386_OpAnd8(v, config)
+	case OpAndB:
+		return rewriteValue386_OpAndB(v, config)
+	case OpBswap32:
+		return rewriteValue386_OpBswap32(v, config)
+	case OpClosureCall:
+		return rewriteValue386_OpClosureCall(v, config)
+	case OpCom16:
+		return rewriteValue386_OpCom16(v, config)
+	case OpCom32:
+		return rewriteValue386_OpCom32(v, config)
+	case OpCom8:
+		return rewriteValue386_OpCom8(v, config)
+	case OpConst16:
+		return rewriteValue386_OpConst16(v, config)
+	case OpConst32:
+		return rewriteValue386_OpConst32(v, config)
+	case OpConst32F:
+		return rewriteValue386_OpConst32F(v, config)
+	case OpConst64F:
+		return rewriteValue386_OpConst64F(v, config)
+	case OpConst8:
+		return rewriteValue386_OpConst8(v, config)
+	case OpConstBool:
+		return rewriteValue386_OpConstBool(v, config)
+	case OpConstNil:
+		return rewriteValue386_OpConstNil(v, config)
+	case OpConvert:
+		return rewriteValue386_OpConvert(v, config)
+	case OpCvt32Fto32:
+		return rewriteValue386_OpCvt32Fto32(v, config)
+	case OpCvt32Fto64F:
+		return rewriteValue386_OpCvt32Fto64F(v, config)
+	case OpCvt32to32F:
+		return rewriteValue386_OpCvt32to32F(v, config)
+	case OpCvt32to64F:
+		return rewriteValue386_OpCvt32to64F(v, config)
+	case OpCvt64Fto32:
+		return rewriteValue386_OpCvt64Fto32(v, config)
+	case OpCvt64Fto32F:
+		return rewriteValue386_OpCvt64Fto32F(v, config)
+	case OpDeferCall:
+		return rewriteValue386_OpDeferCall(v, config)
+	case OpDiv16:
+		return rewriteValue386_OpDiv16(v, config)
+	case OpDiv16u:
+		return rewriteValue386_OpDiv16u(v, config)
+	case OpDiv32:
+		return rewriteValue386_OpDiv32(v, config)
+	case OpDiv32F:
+		return rewriteValue386_OpDiv32F(v, config)
+	case OpDiv32u:
+		return rewriteValue386_OpDiv32u(v, config)
+	case OpDiv64F:
+		return rewriteValue386_OpDiv64F(v, config)
+	case OpDiv8:
+		return rewriteValue386_OpDiv8(v, config)
+	case OpDiv8u:
+		return rewriteValue386_OpDiv8u(v, config)
+	case OpEq16:
+		return rewriteValue386_OpEq16(v, config)
+	case OpEq32:
+		return rewriteValue386_OpEq32(v, config)
+	case OpEq32F:
+		return rewriteValue386_OpEq32F(v, config)
+	case OpEq64F:
+		return rewriteValue386_OpEq64F(v, config)
+	case OpEq8:
+		return rewriteValue386_OpEq8(v, config)
+	case OpEqB:
+		return rewriteValue386_OpEqB(v, config)
+	case OpEqPtr:
+		return rewriteValue386_OpEqPtr(v, config)
+	case OpGeq16:
+		return rewriteValue386_OpGeq16(v, config)
+	case OpGeq16U:
+		return rewriteValue386_OpGeq16U(v, config)
+	case OpGeq32:
+		return rewriteValue386_OpGeq32(v, config)
+	case OpGeq32F:
+		return rewriteValue386_OpGeq32F(v, config)
+	case OpGeq32U:
+		return rewriteValue386_OpGeq32U(v, config)
+	case OpGeq64F:
+		return rewriteValue386_OpGeq64F(v, config)
+	case OpGeq8:
+		return rewriteValue386_OpGeq8(v, config)
+	case OpGeq8U:
+		return rewriteValue386_OpGeq8U(v, config)
+	case OpGetClosurePtr:
+		return rewriteValue386_OpGetClosurePtr(v, config)
+	case OpGetG:
+		return rewriteValue386_OpGetG(v, config)
+	case OpGoCall:
+		return rewriteValue386_OpGoCall(v, config)
+	case OpGreater16:
+		return rewriteValue386_OpGreater16(v, config)
+	case OpGreater16U:
+		return rewriteValue386_OpGreater16U(v, config)
+	case OpGreater32:
+		return rewriteValue386_OpGreater32(v, config)
+	case OpGreater32F:
+		return rewriteValue386_OpGreater32F(v, config)
+	case OpGreater32U:
+		return rewriteValue386_OpGreater32U(v, config)
+	case OpGreater64F:
+		return rewriteValue386_OpGreater64F(v, config)
+	case OpGreater8:
+		return rewriteValue386_OpGreater8(v, config)
+	case OpGreater8U:
+		return rewriteValue386_OpGreater8U(v, config)
+	case OpHmul16:
+		return rewriteValue386_OpHmul16(v, config)
+	case OpHmul16u:
+		return rewriteValue386_OpHmul16u(v, config)
+	case OpHmul32:
+		return rewriteValue386_OpHmul32(v, config)
+	case OpHmul32u:
+		return rewriteValue386_OpHmul32u(v, config)
+	case OpHmul8:
+		return rewriteValue386_OpHmul8(v, config)
+	case OpHmul8u:
+		return rewriteValue386_OpHmul8u(v, config)
+	case OpInterCall:
+		return rewriteValue386_OpInterCall(v, config)
+	case OpIsInBounds:
+		return rewriteValue386_OpIsInBounds(v, config)
+	case OpIsNonNil:
+		return rewriteValue386_OpIsNonNil(v, config)
+	case OpIsSliceInBounds:
+		return rewriteValue386_OpIsSliceInBounds(v, config)
+	case OpLeq16:
+		return rewriteValue386_OpLeq16(v, config)
+	case OpLeq16U:
+		return rewriteValue386_OpLeq16U(v, config)
+	case OpLeq32:
+		return rewriteValue386_OpLeq32(v, config)
+	case OpLeq32F:
+		return rewriteValue386_OpLeq32F(v, config)
+	case OpLeq32U:
+		return rewriteValue386_OpLeq32U(v, config)
+	case OpLeq64F:
+		return rewriteValue386_OpLeq64F(v, config)
+	case OpLeq8:
+		return rewriteValue386_OpLeq8(v, config)
+	case OpLeq8U:
+		return rewriteValue386_OpLeq8U(v, config)
+	case OpLess16:
+		return rewriteValue386_OpLess16(v, config)
+	case OpLess16U:
+		return rewriteValue386_OpLess16U(v, config)
+	case OpLess32:
+		return rewriteValue386_OpLess32(v, config)
+	case OpLess32F:
+		return rewriteValue386_OpLess32F(v, config)
+	case OpLess32U:
+		return rewriteValue386_OpLess32U(v, config)
+	case OpLess64F:
+		return rewriteValue386_OpLess64F(v, config)
+	case OpLess8:
+		return rewriteValue386_OpLess8(v, config)
+	case OpLess8U:
+		return rewriteValue386_OpLess8U(v, config)
+	case OpLoad:
+		return rewriteValue386_OpLoad(v, config)
+	case OpLrot16:
+		return rewriteValue386_OpLrot16(v, config)
+	case OpLrot32:
+		return rewriteValue386_OpLrot32(v, config)
+	case OpLrot8:
+		return rewriteValue386_OpLrot8(v, config)
+	case OpLsh16x16:
+		return rewriteValue386_OpLsh16x16(v, config)
+	case OpLsh16x32:
+		return rewriteValue386_OpLsh16x32(v, config)
+	case OpLsh16x64:
+		return rewriteValue386_OpLsh16x64(v, config)
+	case OpLsh16x8:
+		return rewriteValue386_OpLsh16x8(v, config)
+	case OpLsh32x16:
+		return rewriteValue386_OpLsh32x16(v, config)
+	case OpLsh32x32:
+		return rewriteValue386_OpLsh32x32(v, config)
+	case OpLsh32x64:
+		return rewriteValue386_OpLsh32x64(v, config)
+	case OpLsh32x8:
+		return rewriteValue386_OpLsh32x8(v, config)
+	case OpLsh8x16:
+		return rewriteValue386_OpLsh8x16(v, config)
+	case OpLsh8x32:
+		return rewriteValue386_OpLsh8x32(v, config)
+	case OpLsh8x64:
+		return rewriteValue386_OpLsh8x64(v, config)
+	case OpLsh8x8:
+		return rewriteValue386_OpLsh8x8(v, config)
+	case OpMod16:
+		return rewriteValue386_OpMod16(v, config)
+	case OpMod16u:
+		return rewriteValue386_OpMod16u(v, config)
+	case OpMod32:
+		return rewriteValue386_OpMod32(v, config)
+	case OpMod32u:
+		return rewriteValue386_OpMod32u(v, config)
+	case OpMod8:
+		return rewriteValue386_OpMod8(v, config)
+	case OpMod8u:
+		return rewriteValue386_OpMod8u(v, config)
+	case OpMove:
+		return rewriteValue386_OpMove(v, config)
+	case OpMul16:
+		return rewriteValue386_OpMul16(v, config)
+	case OpMul32:
+		return rewriteValue386_OpMul32(v, config)
+	case OpMul32F:
+		return rewriteValue386_OpMul32F(v, config)
+	case OpMul32uhilo:
+		return rewriteValue386_OpMul32uhilo(v, config)
+	case OpMul64F:
+		return rewriteValue386_OpMul64F(v, config)
+	case OpMul8:
+		return rewriteValue386_OpMul8(v, config)
+	case OpNeg16:
+		return rewriteValue386_OpNeg16(v, config)
+	case OpNeg32:
+		return rewriteValue386_OpNeg32(v, config)
+	case OpNeg32F:
+		return rewriteValue386_OpNeg32F(v, config)
+	case OpNeg64F:
+		return rewriteValue386_OpNeg64F(v, config)
+	case OpNeg8:
+		return rewriteValue386_OpNeg8(v, config)
+	case OpNeq16:
+		return rewriteValue386_OpNeq16(v, config)
+	case OpNeq32:
+		return rewriteValue386_OpNeq32(v, config)
+	case OpNeq32F:
+		return rewriteValue386_OpNeq32F(v, config)
+	case OpNeq64F:
+		return rewriteValue386_OpNeq64F(v, config)
+	case OpNeq8:
+		return rewriteValue386_OpNeq8(v, config)
+	case OpNeqB:
+		return rewriteValue386_OpNeqB(v, config)
+	case OpNeqPtr:
+		return rewriteValue386_OpNeqPtr(v, config)
+	case OpNilCheck:
+		return rewriteValue386_OpNilCheck(v, config)
+	case OpNot:
+		return rewriteValue386_OpNot(v, config)
+	case OpOffPtr:
+		return rewriteValue386_OpOffPtr(v, config)
+	case OpOr16:
+		return rewriteValue386_OpOr16(v, config)
+	case OpOr32:
+		return rewriteValue386_OpOr32(v, config)
+	case OpOr8:
+		return rewriteValue386_OpOr8(v, config)
+	case OpOrB:
+		return rewriteValue386_OpOrB(v, config)
+	case OpRsh16Ux16:
+		return rewriteValue386_OpRsh16Ux16(v, config)
+	case OpRsh16Ux32:
+		return rewriteValue386_OpRsh16Ux32(v, config)
+	case OpRsh16Ux64:
+		return rewriteValue386_OpRsh16Ux64(v, config)
+	case OpRsh16Ux8:
+		return rewriteValue386_OpRsh16Ux8(v, config)
+	case OpRsh16x16:
+		return rewriteValue386_OpRsh16x16(v, config)
+	case OpRsh16x32:
+		return rewriteValue386_OpRsh16x32(v, config)
+	case OpRsh16x64:
+		return rewriteValue386_OpRsh16x64(v, config)
+	case OpRsh16x8:
+		return rewriteValue386_OpRsh16x8(v, config)
+	case OpRsh32Ux16:
+		return rewriteValue386_OpRsh32Ux16(v, config)
+	case OpRsh32Ux32:
+		return rewriteValue386_OpRsh32Ux32(v, config)
+	case OpRsh32Ux64:
+		return rewriteValue386_OpRsh32Ux64(v, config)
+	case OpRsh32Ux8:
+		return rewriteValue386_OpRsh32Ux8(v, config)
+	case OpRsh32x16:
+		return rewriteValue386_OpRsh32x16(v, config)
+	case OpRsh32x32:
+		return rewriteValue386_OpRsh32x32(v, config)
+	case OpRsh32x64:
+		return rewriteValue386_OpRsh32x64(v, config)
+	case OpRsh32x8:
+		return rewriteValue386_OpRsh32x8(v, config)
+	case OpRsh8Ux16:
+		return rewriteValue386_OpRsh8Ux16(v, config)
+	case OpRsh8Ux32:
+		return rewriteValue386_OpRsh8Ux32(v, config)
+	case OpRsh8Ux64:
+		return rewriteValue386_OpRsh8Ux64(v, config)
+	case OpRsh8Ux8:
+		return rewriteValue386_OpRsh8Ux8(v, config)
+	case OpRsh8x16:
+		return rewriteValue386_OpRsh8x16(v, config)
+	case OpRsh8x32:
+		return rewriteValue386_OpRsh8x32(v, config)
+	case OpRsh8x64:
+		return rewriteValue386_OpRsh8x64(v, config)
+	case OpRsh8x8:
+		return rewriteValue386_OpRsh8x8(v, config)
+	case OpSignExt16to32:
+		return rewriteValue386_OpSignExt16to32(v, config)
+	case OpSignExt8to16:
+		return rewriteValue386_OpSignExt8to16(v, config)
+	case OpSignExt8to32:
+		return rewriteValue386_OpSignExt8to32(v, config)
+	case OpSignmask:
+		return rewriteValue386_OpSignmask(v, config)
+	case OpSlicemask:
+		return rewriteValue386_OpSlicemask(v, config)
+	case OpSqrt:
+		return rewriteValue386_OpSqrt(v, config)
+	case OpStaticCall:
+		return rewriteValue386_OpStaticCall(v, config)
+	case OpStore:
+		return rewriteValue386_OpStore(v, config)
+	case OpSub16:
+		return rewriteValue386_OpSub16(v, config)
+	case OpSub32:
+		return rewriteValue386_OpSub32(v, config)
+	case OpSub32F:
+		return rewriteValue386_OpSub32F(v, config)
+	case OpSub32carry:
+		return rewriteValue386_OpSub32carry(v, config)
+	case OpSub32withcarry:
+		return rewriteValue386_OpSub32withcarry(v, config)
+	case OpSub64F:
+		return rewriteValue386_OpSub64F(v, config)
+	case OpSub8:
+		return rewriteValue386_OpSub8(v, config)
+	case OpSubPtr:
+		return rewriteValue386_OpSubPtr(v, config)
+	case OpTrunc16to8:
+		return rewriteValue386_OpTrunc16to8(v, config)
+	case OpTrunc32to16:
+		return rewriteValue386_OpTrunc32to16(v, config)
+	case OpTrunc32to8:
+		return rewriteValue386_OpTrunc32to8(v, config)
+	case OpXor16:
+		return rewriteValue386_OpXor16(v, config)
+	case OpXor32:
+		return rewriteValue386_OpXor32(v, config)
+	case OpXor8:
+		return rewriteValue386_OpXor8(v, config)
+	case OpZero:
+		return rewriteValue386_OpZero(v, config)
+	case OpZeroExt16to32:
+		return rewriteValue386_OpZeroExt16to32(v, config)
+	case OpZeroExt8to16:
+		return rewriteValue386_OpZeroExt8to16(v, config)
+	case OpZeroExt8to32:
+		return rewriteValue386_OpZeroExt8to32(v, config)
+	case OpZeromask:
+		return rewriteValue386_OpZeromask(v, config)
+	}
+	return false
+}
+func rewriteValue386_Op386ADCL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADCL x (MOVLconst [c]) f)
+	// cond:
+	// result: (ADCLconst [c] x f)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		f := v.Args[2]
+		v.reset(Op386ADCLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(f)
+		return true
+	}
+	// match: (ADCL (MOVLconst [c]) x f)
+	// cond:
+	// result: (ADCLconst [c] x f)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		f := v.Args[2]
+		v.reset(Op386ADCLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(f)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386ADDL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDL x (MOVLconst [c]))
+	// cond:
+	// result: (ADDLconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386ADDLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDL (MOVLconst [c]) x)
+	// cond:
+	// result: (ADDLconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(Op386ADDLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDL x (SHLLconst [3] y))
+	// cond:
+	// result: (LEAL8 x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 3 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386LEAL8)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDL x (SHLLconst [2] y))
+	// cond:
+	// result: (LEAL4 x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 2 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386LEAL4)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDL x (SHLLconst [1] y))
+	// cond:
+	// result: (LEAL2 x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386LEAL2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDL x (ADDL y y))
+	// cond:
+	// result: (LEAL2 x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDL {
+			break
+		}
+		y := v_1.Args[0]
+		if y != v_1.Args[1] {
+			break
+		}
+		v.reset(Op386LEAL2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDL x (ADDL x y))
+	// cond:
+	// result: (LEAL2 y x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDL {
+			break
+		}
+		if x != v_1.Args[0] {
+			break
+		}
+		y := v_1.Args[1]
+		v.reset(Op386LEAL2)
+		v.AddArg(y)
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDL x (ADDL y x))
+	// cond:
+	// result: (LEAL2 y x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDL {
+			break
+		}
+		y := v_1.Args[0]
+		if x != v_1.Args[1] {
+			break
+		}
+		v.reset(Op386LEAL2)
+		v.AddArg(y)
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDL (ADDLconst [c] x) y)
+	// cond:
+	// result: (LEAL1 [c] x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		y := v.Args[1]
+		v.reset(Op386LEAL1)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDL x (ADDLconst [c] y))
+	// cond:
+	// result: (LEAL1 [c] x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(Op386LEAL1)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDL x (LEAL [c] {s} y))
+	// cond: x.Op != OpSB && y.Op != OpSB
+	// result: (LEAL1 [c] {s} x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386LEAL {
+			break
+		}
+		c := v_1.AuxInt
+		s := v_1.Aux
+		y := v_1.Args[0]
+		if !(x.Op != OpSB && y.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL1)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDL (LEAL [c] {s} x) y)
+	// cond: x.Op != OpSB && y.Op != OpSB
+	// result: (LEAL1 [c] {s} x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		c := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(x.Op != OpSB && y.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL1)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDL x (NEGL y))
+	// cond:
+	// result: (SUBL x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386NEGL {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386SUBL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386ADDLcarry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDLcarry x (MOVLconst [c]))
+	// cond:
+	// result: (ADDLconstcarry [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386ADDLconstcarry)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDLcarry (MOVLconst [c]) x)
+	// cond:
+	// result: (ADDLconstcarry [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(Op386ADDLconstcarry)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386ADDLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDLconst [c] (ADDL x y))
+	// cond:
+	// result: (LEAL1 [c] x y)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(Op386LEAL1)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDLconst [c] (LEAL [d] {s} x))
+	// cond: is32Bit(c+d)
+	// result: (LEAL [c+d] {s} x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		d := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		if !(is32Bit(c + d)) {
+			break
+		}
+		v.reset(Op386LEAL)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDLconst [c] (LEAL1 [d] {s} x y))
+	// cond: is32Bit(c+d)
+	// result: (LEAL1 [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		d := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(c + d)) {
+			break
+		}
+		v.reset(Op386LEAL1)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDLconst [c] (LEAL2 [d] {s} x y))
+	// cond: is32Bit(c+d)
+	// result: (LEAL2 [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL2 {
+			break
+		}
+		d := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(c + d)) {
+			break
+		}
+		v.reset(Op386LEAL2)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDLconst [c] (LEAL4 [d] {s} x y))
+	// cond: is32Bit(c+d)
+	// result: (LEAL4 [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL4 {
+			break
+		}
+		d := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(c + d)) {
+			break
+		}
+		v.reset(Op386LEAL4)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDLconst [c] (LEAL8 [d] {s} x y))
+	// cond: is32Bit(c+d)
+	// result: (LEAL8 [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL8 {
+			break
+		}
+		d := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(c + d)) {
+			break
+		}
+		v.reset(Op386LEAL8)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDLconst [c] x)
+	// cond: int32(c)==0
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDLconst [c] (MOVLconst [d]))
+	// cond:
+	// result: (MOVLconst [int64(int32(c+d))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = int64(int32(c + d))
+		return true
+	}
+	// match: (ADDLconst [c] (ADDLconst [d] x))
+	// cond:
+	// result: (ADDLconst [int64(int32(c+d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386ADDLconst)
+		v.AuxInt = int64(int32(c + d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386ANDL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDL x (MOVLconst [c]))
+	// cond:
+	// result: (ANDLconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386ANDLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDL (MOVLconst [c]) x)
+	// cond:
+	// result: (ANDLconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(Op386ANDLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDL x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386ANDLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDLconst [c] (ANDLconst [d] x))
+	// cond:
+	// result: (ANDLconst [c & d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386ANDLconst)
+		v.AuxInt = c & d
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDLconst [c] _)
+	// cond: int32(c)==0
+	// result: (MOVLconst [0])
+	for {
+		c := v.AuxInt
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (ANDLconst [c] x)
+	// cond: int32(c)==-1
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDLconst [c] (MOVLconst [d]))
+	// cond:
+	// result: (MOVLconst [c&d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = c & d
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386CMPB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPB x (MOVLconst [c]))
+	// cond:
+	// result: (CMPBconst x [int64(int8(c))])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386CMPBconst)
+		v.AuxInt = int64(int8(c))
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPB (MOVLconst [c]) x)
+	// cond:
+	// result: (InvertFlags (CMPBconst x [int64(int8(c))]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(Op386InvertFlags)
+		v0 := b.NewValue0(v.Line, Op386CMPBconst, TypeFlags)
+		v0.AuxInt = int64(int8(c))
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386CMPBconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPBconst (MOVLconst [x]) [y])
+	// cond: int8(x)==int8(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int8(x) == int8(y)) {
+			break
+		}
+		v.reset(Op386FlagEQ)
+		return true
+	}
+	// match: (CMPBconst (MOVLconst [x]) [y])
+	// cond: int8(x)<int8(y) && uint8(x)<uint8(y)
+	// result: (FlagLT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int8(x) < int8(y) && uint8(x) < uint8(y)) {
+			break
+		}
+		v.reset(Op386FlagLT_ULT)
+		return true
+	}
+	// match: (CMPBconst (MOVLconst [x]) [y])
+	// cond: int8(x)<int8(y) && uint8(x)>uint8(y)
+	// result: (FlagLT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int8(x) < int8(y) && uint8(x) > uint8(y)) {
+			break
+		}
+		v.reset(Op386FlagLT_UGT)
+		return true
+	}
+	// match: (CMPBconst (MOVLconst [x]) [y])
+	// cond: int8(x)>int8(y) && uint8(x)<uint8(y)
+	// result: (FlagGT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int8(x) > int8(y) && uint8(x) < uint8(y)) {
+			break
+		}
+		v.reset(Op386FlagGT_ULT)
+		return true
+	}
+	// match: (CMPBconst (MOVLconst [x]) [y])
+	// cond: int8(x)>int8(y) && uint8(x)>uint8(y)
+	// result: (FlagGT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int8(x) > int8(y) && uint8(x) > uint8(y)) {
+			break
+		}
+		v.reset(Op386FlagGT_UGT)
+		return true
+	}
+	// match: (CMPBconst (ANDLconst _ [m]) [n])
+	// cond: 0 <= int8(m) && int8(m) < int8(n)
+	// result: (FlagLT_ULT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(0 <= int8(m) && int8(m) < int8(n)) {
+			break
+		}
+		v.reset(Op386FlagLT_ULT)
+		return true
+	}
+	// match: (CMPBconst (ANDL x y) [0])
+	// cond:
+	// result: (TESTB x y)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDL {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(Op386TESTB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMPBconst (ANDLconst [c] x) [0])
+	// cond:
+	// result: (TESTBconst [int64(int8(c))] x)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386TESTBconst)
+		v.AuxInt = int64(int8(c))
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPBconst x [0])
+	// cond:
+	// result: (TESTB x x)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386TESTB)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386CMPL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPL x (MOVLconst [c]))
+	// cond:
+	// result: (CMPLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386CMPLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPL (MOVLconst [c]) x)
+	// cond:
+	// result: (InvertFlags (CMPLconst x [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(Op386InvertFlags)
+		v0 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386CMPLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPLconst (MOVLconst [x]) [y])
+	// cond: int32(x)==int32(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) == int32(y)) {
+			break
+		}
+		v.reset(Op386FlagEQ)
+		return true
+	}
+	// match: (CMPLconst (MOVLconst [x]) [y])
+	// cond: int32(x)<int32(y) && uint32(x)<uint32(y)
+	// result: (FlagLT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) < int32(y) && uint32(x) < uint32(y)) {
+			break
+		}
+		v.reset(Op386FlagLT_ULT)
+		return true
+	}
+	// match: (CMPLconst (MOVLconst [x]) [y])
+	// cond: int32(x)<int32(y) && uint32(x)>uint32(y)
+	// result: (FlagLT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) < int32(y) && uint32(x) > uint32(y)) {
+			break
+		}
+		v.reset(Op386FlagLT_UGT)
+		return true
+	}
+	// match: (CMPLconst (MOVLconst [x]) [y])
+	// cond: int32(x)>int32(y) && uint32(x)<uint32(y)
+	// result: (FlagGT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) > int32(y) && uint32(x) < uint32(y)) {
+			break
+		}
+		v.reset(Op386FlagGT_ULT)
+		return true
+	}
+	// match: (CMPLconst (MOVLconst [x]) [y])
+	// cond: int32(x)>int32(y) && uint32(x)>uint32(y)
+	// result: (FlagGT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) > int32(y) && uint32(x) > uint32(y)) {
+			break
+		}
+		v.reset(Op386FlagGT_UGT)
+		return true
+	}
+	// match: (CMPLconst (SHRLconst _ [c]) [n])
+	// cond: 0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n)
+	// result: (FlagLT_ULT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386SHRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		if !(0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n)) {
+			break
+		}
+		v.reset(Op386FlagLT_ULT)
+		return true
+	}
+	// match: (CMPLconst (ANDLconst _ [m]) [n])
+	// cond: 0 <= int32(m) && int32(m) < int32(n)
+	// result: (FlagLT_ULT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(0 <= int32(m) && int32(m) < int32(n)) {
+			break
+		}
+		v.reset(Op386FlagLT_ULT)
+		return true
+	}
+	// match: (CMPLconst (ANDL x y) [0])
+	// cond:
+	// result: (TESTL x y)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDL {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(Op386TESTL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMPLconst (ANDLconst [c] x) [0])
+	// cond:
+	// result: (TESTLconst [c] x)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386TESTLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPLconst x [0])
+	// cond:
+	// result: (TESTL x x)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386TESTL)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386CMPW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPW x (MOVLconst [c]))
+	// cond:
+	// result: (CMPWconst x [int64(int16(c))])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386CMPWconst)
+		v.AuxInt = int64(int16(c))
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPW (MOVLconst [c]) x)
+	// cond:
+	// result: (InvertFlags (CMPWconst x [int64(int16(c))]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(Op386InvertFlags)
+		v0 := b.NewValue0(v.Line, Op386CMPWconst, TypeFlags)
+		v0.AuxInt = int64(int16(c))
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386CMPWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPWconst (MOVLconst [x]) [y])
+	// cond: int16(x)==int16(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int16(x) == int16(y)) {
+			break
+		}
+		v.reset(Op386FlagEQ)
+		return true
+	}
+	// match: (CMPWconst (MOVLconst [x]) [y])
+	// cond: int16(x)<int16(y) && uint16(x)<uint16(y)
+	// result: (FlagLT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int16(x) < int16(y) && uint16(x) < uint16(y)) {
+			break
+		}
+		v.reset(Op386FlagLT_ULT)
+		return true
+	}
+	// match: (CMPWconst (MOVLconst [x]) [y])
+	// cond: int16(x)<int16(y) && uint16(x)>uint16(y)
+	// result: (FlagLT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int16(x) < int16(y) && uint16(x) > uint16(y)) {
+			break
+		}
+		v.reset(Op386FlagLT_UGT)
+		return true
+	}
+	// match: (CMPWconst (MOVLconst [x]) [y])
+	// cond: int16(x)>int16(y) && uint16(x)<uint16(y)
+	// result: (FlagGT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int16(x) > int16(y) && uint16(x) < uint16(y)) {
+			break
+		}
+		v.reset(Op386FlagGT_ULT)
+		return true
+	}
+	// match: (CMPWconst (MOVLconst [x]) [y])
+	// cond: int16(x)>int16(y) && uint16(x)>uint16(y)
+	// result: (FlagGT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int16(x) > int16(y) && uint16(x) > uint16(y)) {
+			break
+		}
+		v.reset(Op386FlagGT_UGT)
+		return true
+	}
+	// match: (CMPWconst (ANDLconst _ [m]) [n])
+	// cond: 0 <= int16(m) && int16(m) < int16(n)
+	// result: (FlagLT_ULT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(0 <= int16(m) && int16(m) < int16(n)) {
+			break
+		}
+		v.reset(Op386FlagLT_ULT)
+		return true
+	}
+	// match: (CMPWconst (ANDL x y) [0])
+	// cond:
+	// result: (TESTW x y)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDL {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(Op386TESTW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMPWconst (ANDLconst [c] x) [0])
+	// cond:
+	// result: (TESTWconst [int64(int16(c))] x)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386TESTWconst)
+		v.AuxInt = int64(int16(c))
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPWconst x [0])
+	// cond:
+	// result: (TESTW x x)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386TESTW)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386LEAL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LEAL [c] {s} (ADDLconst [d] x))
+	// cond: is32Bit(c+d)
+	// result: (LEAL [c+d] {s} x)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(c + d)) {
+			break
+		}
+		v.reset(Op386LEAL)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		return true
+	}
+	// match: (LEAL [c] {s} (ADDL x y))
+	// cond: x.Op != OpSB && y.Op != OpSB
+	// result: (LEAL1 [c] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(x.Op != OpSB && y.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL1)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL [off1] {sym1} (LEAL [off2] {sym2} x))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (LEAL [off1+off2] {mergeSym(sym1,sym2)} x)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386LEAL)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		return true
+	}
+	// match: (LEAL [off1] {sym1} (LEAL1 [off2] {sym2} x y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (LEAL1 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386LEAL1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL [off1] {sym1} (LEAL2 [off2] {sym2} x y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (LEAL2 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL2 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386LEAL2)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL [off1] {sym1} (LEAL4 [off2] {sym2} x y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (LEAL4 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL4 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386LEAL4)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL [off1] {sym1} (LEAL8 [off2] {sym2} x y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (LEAL8 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL8 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386LEAL8)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386LEAL1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LEAL1 [c] {s} (ADDLconst [d] x) y)
+	// cond: is32Bit(c+d)   && x.Op != OpSB
+	// result: (LEAL1 [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(c+d) && x.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL1)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL1 [c] {s} x (ADDLconst [d] y))
+	// cond: is32Bit(c+d)   && y.Op != OpSB
+	// result: (LEAL1 [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		y := v_1.Args[0]
+		if !(is32Bit(c+d) && y.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL1)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL1 [c] {s} x (SHLLconst [1] y))
+	// cond:
+	// result: (LEAL2 [c] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386LEAL2)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL1 [c] {s} (SHLLconst [1] x) y)
+	// cond:
+	// result: (LEAL2 [c] {s} y x)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386SHLLconst {
+			break
+		}
+		if v_0.AuxInt != 1 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v.Args[1]
+		v.reset(Op386LEAL2)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(y)
+		v.AddArg(x)
+		return true
+	}
+	// match: (LEAL1 [c] {s} x (SHLLconst [2] y))
+	// cond:
+	// result: (LEAL4 [c] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 2 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386LEAL4)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL1 [c] {s} (SHLLconst [2] x) y)
+	// cond:
+	// result: (LEAL4 [c] {s} y x)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386SHLLconst {
+			break
+		}
+		if v_0.AuxInt != 2 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v.Args[1]
+		v.reset(Op386LEAL4)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(y)
+		v.AddArg(x)
+		return true
+	}
+	// match: (LEAL1 [c] {s} x (SHLLconst [3] y))
+	// cond:
+	// result: (LEAL8 [c] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 3 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386LEAL8)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL1 [c] {s} (SHLLconst [3] x) y)
+	// cond:
+	// result: (LEAL8 [c] {s} y x)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386SHLLconst {
+			break
+		}
+		if v_0.AuxInt != 3 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v.Args[1]
+		v.reset(Op386LEAL8)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(y)
+		v.AddArg(x)
+		return true
+	}
+	// match: (LEAL1 [off1] {sym1} (LEAL [off2] {sym2} x) y)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
+	// result: (LEAL1 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL1 [off1] {sym1} x (LEAL [off2] {sym2} y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && y.Op != OpSB
+	// result: (LEAL1 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386LEAL {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		y := v_1.Args[0]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && y.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386LEAL2(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LEAL2 [c] {s} (ADDLconst [d] x) y)
+	// cond: is32Bit(c+d)   && x.Op != OpSB
+	// result: (LEAL2 [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(c+d) && x.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL2)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL2 [c] {s} x (ADDLconst [d] y))
+	// cond: is32Bit(c+2*d) && y.Op != OpSB
+	// result: (LEAL2 [c+2*d] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		y := v_1.Args[0]
+		if !(is32Bit(c+2*d) && y.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL2)
+		v.AuxInt = c + 2*d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL2 [c] {s} x (SHLLconst [1] y))
+	// cond:
+	// result: (LEAL4 [c] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386LEAL4)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL2 [c] {s} x (SHLLconst [2] y))
+	// cond:
+	// result: (LEAL8 [c] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 2 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386LEAL8)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL2 [off1] {sym1} (LEAL [off2] {sym2} x) y)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
+	// result: (LEAL2 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL2)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386LEAL4(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LEAL4 [c] {s} (ADDLconst [d] x) y)
+	// cond: is32Bit(c+d)   && x.Op != OpSB
+	// result: (LEAL4 [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(c+d) && x.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL4)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL4 [c] {s} x (ADDLconst [d] y))
+	// cond: is32Bit(c+4*d) && y.Op != OpSB
+	// result: (LEAL4 [c+4*d] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		y := v_1.Args[0]
+		if !(is32Bit(c+4*d) && y.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL4)
+		v.AuxInt = c + 4*d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL4 [c] {s} x (SHLLconst [1] y))
+	// cond:
+	// result: (LEAL8 [c] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386LEAL8)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL4 [off1] {sym1} (LEAL [off2] {sym2} x) y)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
+	// result: (LEAL4 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL4)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386LEAL8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LEAL8 [c] {s} (ADDLconst [d] x) y)
+	// cond: is32Bit(c+d)   && x.Op != OpSB
+	// result: (LEAL8 [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(c+d) && x.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL8)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL8 [c] {s} x (ADDLconst [d] y))
+	// cond: is32Bit(c+8*d) && y.Op != OpSB
+	// result: (LEAL8 [c+8*d] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		y := v_1.Args[0]
+		if !(is32Bit(c+8*d) && y.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL8)
+		v.AuxInt = c + 8*d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (LEAL8 [off1] {sym1} (LEAL [off2] {sym2} x) y)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
+	// result: (LEAL8 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+			break
+		}
+		v.reset(Op386LEAL8)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVBLSX(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBLSX x:(MOVBload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBLSXload <v.Type> [off] {sym} ptr mem)
+	for {
+		x := v.Args[0]
+		if x.Op != Op386MOVBload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, Op386MOVBLSXload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVBLSX (ANDLconst [c] x))
+	// cond: c & 0x80 == 0
+	// result: (ANDLconst [c & 0x7f] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(c&0x80 == 0) {
+			break
+		}
+		v.reset(Op386ANDLconst)
+		v.AuxInt = c & 0x7f
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVBLSXload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBLSXload [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVBLSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVBLSXload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVBLZX(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBLZX x:(MOVBload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
+	for {
+		x := v.Args[0]
+		if x.Op != Op386MOVBload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, Op386MOVBload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVBLZX x:(MOVBloadidx1 [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBloadidx1 <v.Type> [off] {sym} ptr idx mem)
+	for {
+		x := v.Args[0]
+		if x.Op != Op386MOVBloadidx1 {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, Op386MOVBloadidx1, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVBLZX (ANDLconst [c] x))
+	// cond:
+	// result: (ANDLconst [c & 0xff] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386ANDLconst)
+		v.AuxInt = c & 0xff
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVBload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVBstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBload  [off1] {sym} (ADDLconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVBload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(Op386MOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload  [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVBload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVBloadidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload [off] {sym} (ADDL ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVBloadidx1 [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(Op386MOVBloadidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVBloadidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVBloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVBloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem)
+	// cond:
+	// result: (MOVBloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVBloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVBstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstore [off] {sym} ptr (MOVBLSX x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVBLSX {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBLZX x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVBLZX {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore  [off1] {sym} (ADDLconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVBstore  [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(Op386MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVLconst [c]) mem)
+	// cond: validOff(off)
+	// result: (MOVBstoreconst [makeValAndOff(int64(int8(c)),off)] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(validOff(off)) {
+			break
+		}
+		v.reset(Op386MOVBstoreconst)
+		v.AuxInt = makeValAndOff(int64(int8(c)), off)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVBstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVBstoreidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} (ADDL ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVBstoreidx1 [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(Op386MOVBstoreidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [i] {s} p (SHRLconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstore [i-1] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHRLconst {
+			break
+		}
+		if v_1.AuxInt != 8 {
+			break
+		}
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != Op386MOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if w != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVWstore)
+		v.AuxInt = i - 1
+		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)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHRLconst {
+			break
+		}
+		j := v_1.AuxInt
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != Op386MOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		if w0.Op != Op386SHRLconst {
+			break
+		}
+		if w0.AuxInt != j-8 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVWstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVBstoreconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	for {
+		sc := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(Op386MOVBstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)   && (ptr.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+	for {
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) && (ptr.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVBstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreconst [x] {sym1} (LEAL1 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVBstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVBstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreconst [x] {sym} (ADDL ptr idx) mem)
+	// cond:
+	// result: (MOVBstoreconstidx1 [x] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		v.reset(Op386MOVBstoreconstidx1)
+		v.AuxInt = x
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {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 {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		x := v.Args[1]
+		if x.Op != Op386MOVBstoreconst {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		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(Op386MOVWstoreconst)
+		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
+	}
+	return false
+}
+func rewriteValue386_Op386MOVBstoreconstidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstoreconstidx1 [x] {sym} (ADDLconst [c] ptr) idx mem)
+	// cond:
+	// result: (MOVBstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVBstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreconstidx1 [x] {sym} ptr (ADDLconst [c] idx) mem)
+	// cond:
+	// result: (MOVBstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVBstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreconstidx1 [c] {s} p i x:(MOVBstoreconstidx1 [a] {s} p i mem))
+	// cond: x.Uses == 1   && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVWstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p i mem)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		i := v.Args[1]
+		x := v.Args[2]
+		if x.Op != Op386MOVBstoreconstidx1 {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if i != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && ValAndOff(a).Off()+1 == ValAndOff(c).Off() && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVWstoreconstidx1)
+		v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xff|ValAndOff(c).Val()<<8, ValAndOff(a).Off())
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(i)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVBstoreidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVBstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVBstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem)
+	// cond:
+	// result: (MOVBstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVBstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx1 [i] {s} p idx (SHRLconst [8] w) x:(MOVBstoreidx1 [i-1] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstoreidx1 [i-1] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != Op386SHRLconst {
+			break
+		}
+		if v_2.AuxInt != 8 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != Op386MOVBstoreidx1 {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVWstoreidx1)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx1 [i] {s} p idx (SHRLconst [j] w) x:(MOVBstoreidx1 [i-1] {s} p idx w0:(SHRLconst [j-8] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstoreidx1 [i-1] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != Op386SHRLconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != Op386MOVBstoreidx1 {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != Op386SHRLconst {
+			break
+		}
+		if w0.AuxInt != j-8 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVWstoreidx1)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVLload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVLload [off] {sym} ptr (MOVLstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVLload  [off1] {sym} (ADDLconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVLload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(Op386MOVLload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLload  [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVLload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVLload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLload [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVLloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVLloadidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLload [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVLloadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL4 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVLloadidx4)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLload [off] {sym} (ADDL ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVLloadidx1 [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(Op386MOVLloadidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVLloadidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVLloadidx1 [c] {sym} ptr (SHLLconst [2] idx) mem)
+	// cond:
+	// result: (MOVLloadidx4 [c] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 2 {
+			break
+		}
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVLloadidx4)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVLloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVLloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem)
+	// cond:
+	// result: (MOVLloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVLloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVLloadidx4(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVLloadidx4 [c] {sym} (ADDLconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVLloadidx4 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVLloadidx4)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLloadidx4 [c] {sym} ptr (ADDLconst [d] idx) mem)
+	// cond:
+	// result: (MOVLloadidx4 [c+4*d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVLloadidx4)
+		v.AuxInt = c + 4*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVLstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVLstore  [off1] {sym} (ADDLconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVLstore  [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(Op386MOVLstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstore [off] {sym} ptr (MOVLconst [c]) mem)
+	// cond: validOff(off)
+	// result: (MOVLstoreconst [makeValAndOff(int64(int32(c)),off)] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(validOff(off)) {
+			break
+		}
+		v.reset(Op386MOVLstoreconst)
+		v.AuxInt = makeValAndOff(int64(int32(c)), off)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVLstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVLstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstore [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVLstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVLstoreidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstore [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVLstoreidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL4 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVLstoreidx4)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstore [off] {sym} (ADDL ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVLstoreidx1 [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(Op386MOVLstoreidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVLstoreconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVLstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVLstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	for {
+		sc := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(Op386MOVLstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)   && (ptr.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVLstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+	for {
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) && (ptr.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVLstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstoreconst [x] {sym1} (LEAL1 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVLstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVLstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstoreconst [x] {sym1} (LEAL4 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVLstoreconstidx4 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL4 {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVLstoreconstidx4)
+		v.AuxInt = ValAndOff(x).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstoreconst [x] {sym} (ADDL ptr idx) mem)
+	// cond:
+	// result: (MOVLstoreconstidx1 [x] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		v.reset(Op386MOVLstoreconstidx1)
+		v.AuxInt = x
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVLstoreconstidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVLstoreconstidx1 [c] {sym} ptr (SHLLconst [2] idx) mem)
+	// cond:
+	// result: (MOVLstoreconstidx4 [c] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 2 {
+			break
+		}
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVLstoreconstidx4)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstoreconstidx1 [x] {sym} (ADDLconst [c] ptr) idx mem)
+	// cond:
+	// result: (MOVLstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVLstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstoreconstidx1 [x] {sym} ptr (ADDLconst [c] idx) mem)
+	// cond:
+	// result: (MOVLstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVLstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVLstoreconstidx4(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVLstoreconstidx4 [x] {sym} (ADDLconst [c] ptr) idx mem)
+	// cond:
+	// result: (MOVLstoreconstidx4 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVLstoreconstidx4)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstoreconstidx4 [x] {sym} ptr (ADDLconst [c] idx) mem)
+	// cond:
+	// result: (MOVLstoreconstidx4 [ValAndOff(x).add(4*c)] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVLstoreconstidx4)
+		v.AuxInt = ValAndOff(x).add(4 * c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVLstoreidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVLstoreidx1 [c] {sym} ptr (SHLLconst [2] idx) val mem)
+	// cond:
+	// result: (MOVLstoreidx4 [c] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 2 {
+			break
+		}
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVLstoreidx4)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVLstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVLstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem)
+	// cond:
+	// result: (MOVLstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVLstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVLstoreidx4(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVLstoreidx4 [c] {sym} (ADDLconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVLstoreidx4 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVLstoreidx4)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVLstoreidx4 [c] {sym} ptr (ADDLconst [d] idx) val mem)
+	// cond:
+	// result: (MOVLstoreidx4 [c+4*d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVLstoreidx4)
+		v.AuxInt = c + 4*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSDconst [c])
+	// cond: config.ctxt.Flag_shared
+	// result: (MOVSDconst2 (MOVSDconst1 [c]))
+	for {
+		c := v.AuxInt
+		if !(config.ctxt.Flag_shared) {
+			break
+		}
+		v.reset(Op386MOVSDconst2)
+		v0 := b.NewValue0(v.Line, Op386MOVSDconst1, config.fe.TypeUInt32())
+		v0.AuxInt = c
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSDload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSDload [off1] {sym} (ADDLconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVSDload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(Op386MOVSDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDload [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVSDload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVSDload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDload [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSDloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVSDloadidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDload [off1] {sym1} (LEAL8 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSDloadidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL8 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVSDloadidx8)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDload [off] {sym} (ADDL ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVSDloadidx1 [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(Op386MOVSDloadidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSDloadidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSDloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVSDloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVSDloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem)
+	// cond:
+	// result: (MOVSDloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVSDloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSDloadidx8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSDloadidx8 [c] {sym} (ADDLconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVSDloadidx8 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVSDloadidx8)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDloadidx8 [c] {sym} ptr (ADDLconst [d] idx) mem)
+	// cond:
+	// result: (MOVSDloadidx8 [c+8*d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVSDloadidx8)
+		v.AuxInt = c + 8*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSDstore [off1] {sym} (ADDLconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVSDstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(Op386MOVSDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVSDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVSDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstore [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSDstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVSDstoreidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstore [off1] {sym1} (LEAL8 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSDstoreidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL8 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVSDstoreidx8)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstore [off] {sym} (ADDL ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVSDstoreidx1 [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(Op386MOVSDstoreidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSDstoreidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSDstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVSDstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVSDstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem)
+	// cond:
+	// result: (MOVSDstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVSDstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSDstoreidx8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSDstoreidx8 [c] {sym} (ADDLconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVSDstoreidx8 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVSDstoreidx8)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstoreidx8 [c] {sym} ptr (ADDLconst [d] idx) val mem)
+	// cond:
+	// result: (MOVSDstoreidx8 [c+8*d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVSDstoreidx8)
+		v.AuxInt = c + 8*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSSconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSSconst [c])
+	// cond: config.ctxt.Flag_shared
+	// result: (MOVSSconst2 (MOVSSconst1 [c]))
+	for {
+		c := v.AuxInt
+		if !(config.ctxt.Flag_shared) {
+			break
+		}
+		v.reset(Op386MOVSSconst2)
+		v0 := b.NewValue0(v.Line, Op386MOVSSconst1, config.fe.TypeUInt32())
+		v0.AuxInt = c
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSSload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSSload [off1] {sym} (ADDLconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVSSload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(Op386MOVSSload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSload [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVSSload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVSSload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSload [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSSloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVSSloadidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSload [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSSloadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL4 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVSSloadidx4)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSload [off] {sym} (ADDL ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVSSloadidx1 [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(Op386MOVSSloadidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSSloadidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSSloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVSSloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVSSloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem)
+	// cond:
+	// result: (MOVSSloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVSSloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSSloadidx4(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSSloadidx4 [c] {sym} (ADDLconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVSSloadidx4 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVSSloadidx4)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSloadidx4 [c] {sym} ptr (ADDLconst [d] idx) mem)
+	// cond:
+	// result: (MOVSSloadidx4 [c+4*d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVSSloadidx4)
+		v.AuxInt = c + 4*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSSstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSSstore [off1] {sym} (ADDLconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVSSstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(Op386MOVSSstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVSSstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVSSstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSstore [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSSstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVSSstoreidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSstore [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSSstoreidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL4 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVSSstoreidx4)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSstore [off] {sym} (ADDL ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVSSstoreidx1 [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(Op386MOVSSstoreidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSSstoreidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSSstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVSSstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVSSstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem)
+	// cond:
+	// result: (MOVSSstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVSSstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVSSstoreidx4(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSSstoreidx4 [c] {sym} (ADDLconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVSSstoreidx4 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVSSstoreidx4)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSstoreidx4 [c] {sym} ptr (ADDLconst [d] idx) val mem)
+	// cond:
+	// result: (MOVSSstoreidx4 [c+4*d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVSSstoreidx4)
+		v.AuxInt = c + 4*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWLSX(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWLSX x:(MOVWload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWLSXload <v.Type> [off] {sym} ptr mem)
+	for {
+		x := v.Args[0]
+		if x.Op != Op386MOVWload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, Op386MOVWLSXload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVWLSX (ANDLconst [c] x))
+	// cond: c & 0x8000 == 0
+	// result: (ANDLconst [c & 0x7fff] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(c&0x8000 == 0) {
+			break
+		}
+		v.reset(Op386ANDLconst)
+		v.AuxInt = c & 0x7fff
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWLSXload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWLSXload [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVWLSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVWLSXload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWLZX(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWLZX x:(MOVWload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
+	for {
+		x := v.Args[0]
+		if x.Op != Op386MOVWload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, Op386MOVWload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVWLZX x:(MOVWloadidx1 [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWloadidx1 <v.Type> [off] {sym} ptr idx mem)
+	for {
+		x := v.Args[0]
+		if x.Op != Op386MOVWloadidx1 {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, Op386MOVWloadidx1, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVWLZX x:(MOVWloadidx2 [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWloadidx2 <v.Type> [off] {sym} ptr idx mem)
+	for {
+		x := v.Args[0]
+		if x.Op != Op386MOVWloadidx2 {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, Op386MOVWloadidx2, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVWLZX (ANDLconst [c] x))
+	// cond:
+	// result: (ANDLconst [c & 0xffff] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386ANDLconst)
+		v.AuxInt = c & 0xffff
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVWstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWload  [off1] {sym} (ADDLconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVWload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(Op386MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload  [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVWload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVWloadidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off1] {sym1} (LEAL2 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWloadidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL2 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVWloadidx2)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off] {sym} (ADDL ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVWloadidx1 [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(Op386MOVWloadidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWloadidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWloadidx1 [c] {sym} ptr (SHLLconst [1] idx) mem)
+	// cond:
+	// result: (MOVWloadidx2 [c] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVWloadidx2)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWloadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVWloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVWloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWloadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem)
+	// cond:
+	// result: (MOVWloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVWloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWloadidx2(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWloadidx2 [c] {sym} (ADDLconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVWloadidx2 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVWloadidx2)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWloadidx2 [c] {sym} ptr (ADDLconst [d] idx) mem)
+	// cond:
+	// result: (MOVWloadidx2 [c+2*d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVWloadidx2)
+		v.AuxInt = c + 2*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstore [off] {sym} ptr (MOVWLSX x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVWLSX {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWLZX x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVWLZX {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore  [off1] {sym} (ADDLconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVWstore  [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(Op386MOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVLconst [c]) mem)
+	// cond: validOff(off)
+	// result: (MOVWstoreconst [makeValAndOff(int64(int16(c)),off)] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(validOff(off)) {
+			break
+		}
+		v.reset(Op386MOVWstoreconst)
+		v.AuxInt = makeValAndOff(int64(int16(c)), off)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)   && (base.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVWstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVWstoreidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off1] {sym1} (LEAL2 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWstoreidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL2 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVWstoreidx2)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} (ADDL ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVWstoreidx1 [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(Op386MOVWstoreidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [i] {s} p (SHRLconst [16] w) x:(MOVWstore [i-2] {s} p w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVLstore [i-2] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHRLconst {
+			break
+		}
+		if v_1.AuxInt != 16 {
+			break
+		}
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != Op386MOVWstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if w != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVLstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// 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)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHRLconst {
+			break
+		}
+		j := v_1.AuxInt
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != Op386MOVWstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		if w0.Op != Op386SHRLconst {
+			break
+		}
+		if w0.AuxInt != j-16 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVLstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWstoreconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	for {
+		sc := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(Op386MOVWstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)   && (ptr.Op != OpSB || !config.ctxt.Flag_shared)
+	// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+	for {
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) && (ptr.Op != OpSB || !config.ctxt.Flag_shared)) {
+			break
+		}
+		v.reset(Op386MOVWstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconst [x] {sym1} (LEAL1 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVWstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL1 {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVWstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconst [x] {sym1} (LEAL2 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVWstoreconstidx2 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386LEAL2 {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(Op386MOVWstoreconstidx2)
+		v.AuxInt = ValAndOff(x).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconst [x] {sym} (ADDL ptr idx) mem)
+	// cond:
+	// result: (MOVWstoreconstidx1 [x] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDL {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		v.reset(Op386MOVWstoreconstidx1)
+		v.AuxInt = x
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {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 {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		x := v.Args[1]
+		if x.Op != Op386MOVWstoreconst {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		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(Op386MOVLstoreconst)
+		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
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWstoreconstidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreconstidx1 [c] {sym} ptr (SHLLconst [1] idx) mem)
+	// cond:
+	// result: (MOVWstoreconstidx2 [c] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVWstoreconstidx2)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconstidx1 [x] {sym} (ADDLconst [c] ptr) idx mem)
+	// cond:
+	// result: (MOVWstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVWstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconstidx1 [x] {sym} ptr (ADDLconst [c] idx) mem)
+	// cond:
+	// result: (MOVWstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVWstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconstidx1 [c] {s} p i x:(MOVWstoreconstidx1 [a] {s} p i mem))
+	// cond: x.Uses == 1   && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p i mem)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		i := v.Args[1]
+		x := v.Args[2]
+		if x.Op != Op386MOVWstoreconstidx1 {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if i != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVLstoreconstidx1)
+		v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xffff|ValAndOff(c).Val()<<16, ValAndOff(a).Off())
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(i)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWstoreconstidx2(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreconstidx2 [x] {sym} (ADDLconst [c] ptr) idx mem)
+	// cond:
+	// result: (MOVWstoreconstidx2 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVWstoreconstidx2)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconstidx2 [x] {sym} ptr (ADDLconst [c] idx) mem)
+	// cond:
+	// result: (MOVWstoreconstidx2 [ValAndOff(x).add(2*c)] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(Op386MOVWstoreconstidx2)
+		v.AuxInt = ValAndOff(x).add(2 * c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconstidx2 [c] {s} p i x:(MOVWstoreconstidx2 [a] {s} p i mem))
+	// cond: x.Uses == 1   && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p (SHLLconst <i.Type> [1] i) mem)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		i := v.Args[1]
+		x := v.Args[2]
+		if x.Op != Op386MOVWstoreconstidx2 {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if i != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVLstoreconstidx1)
+		v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xffff|ValAndOff(c).Val()<<16, ValAndOff(a).Off())
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, Op386SHLLconst, i.Type)
+		v0.AuxInt = 1
+		v0.AddArg(i)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWstoreidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreidx1 [c] {sym} ptr (SHLLconst [1] idx) val mem)
+	// cond:
+	// result: (MOVWstoreidx2 [c] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386SHLLconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVWstoreidx2)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVWstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVWstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem)
+	// cond:
+	// result: (MOVWstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVWstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx1 [i] {s} p idx (SHRLconst [16] w) x:(MOVWstoreidx1 [i-2] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVLstoreidx1 [i-2] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != Op386SHRLconst {
+			break
+		}
+		if v_2.AuxInt != 16 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != Op386MOVWstoreidx1 {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVLstoreidx1)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx1 [i] {s} p idx (SHRLconst [j] w) x:(MOVWstoreidx1 [i-2] {s} p idx w0:(SHRLconst [j-16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVLstoreidx1 [i-2] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != Op386SHRLconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != Op386MOVWstoreidx1 {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != Op386SHRLconst {
+			break
+		}
+		if w0.AuxInt != j-16 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVLstoreidx1)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MOVWstoreidx2(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreidx2 [c] {sym} (ADDLconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVWstoreidx2 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ADDLconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVWstoreidx2)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx2 [c] {sym} ptr (ADDLconst [d] idx) val mem)
+	// cond:
+	// result: (MOVWstoreidx2 [c+2*d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ADDLconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(Op386MOVWstoreidx2)
+		v.AuxInt = c + 2*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx2 [i] {s} p idx (SHRLconst [16] w) x:(MOVWstoreidx2 [i-2] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVLstoreidx1 [i-2] {s} p (SHLLconst <idx.Type> [1] idx) w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != Op386SHRLconst {
+			break
+		}
+		if v_2.AuxInt != 16 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != Op386MOVWstoreidx2 {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVLstoreidx1)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, Op386SHLLconst, idx.Type)
+		v0.AuxInt = 1
+		v0.AddArg(idx)
+		v.AddArg(v0)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx2 [i] {s} p idx (SHRLconst [j] w) x:(MOVWstoreidx2 [i-2] {s} p idx w0:(SHRLconst [j-16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVLstoreidx1 [i-2] {s} p (SHLLconst <idx.Type> [1] idx) w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != Op386SHRLconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != Op386MOVWstoreidx2 {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != Op386SHRLconst {
+			break
+		}
+		if w0.AuxInt != j-16 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(Op386MOVLstoreidx1)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, Op386SHLLconst, idx.Type)
+		v0.AuxInt = 1
+		v0.AddArg(idx)
+		v.AddArg(v0)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MULL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MULL x (MOVLconst [c]))
+	// cond:
+	// result: (MULLconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386MULLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULL (MOVLconst [c]) x)
+	// cond:
+	// result: (MULLconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(Op386MULLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386MULLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MULLconst [c] (MULLconst [d] x))
+	// cond:
+	// result: (MULLconst [int64(int32(c * d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MULLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386MULLconst)
+		v.AuxInt = int64(int32(c * d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [-1] x)
+	// cond:
+	// result: (NEGL x)
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386NEGL)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [0] _)
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (MULLconst [1] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [3] x)
+	// cond:
+	// result: (LEAL2 x x)
+	for {
+		if v.AuxInt != 3 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL2)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [5] x)
+	// cond:
+	// result: (LEAL4 x x)
+	for {
+		if v.AuxInt != 5 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL4)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [7] x)
+	// cond:
+	// result: (LEAL8 (NEGL <v.Type> x) x)
+	for {
+		if v.AuxInt != 7 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL8)
+		v0 := b.NewValue0(v.Line, Op386NEGL, v.Type)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [9] x)
+	// cond:
+	// result: (LEAL8 x x)
+	for {
+		if v.AuxInt != 9 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL8)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [11] x)
+	// cond:
+	// result: (LEAL2 x (LEAL4 <v.Type> x x))
+	for {
+		if v.AuxInt != 11 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL2)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386LEAL4, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULLconst [13] x)
+	// cond:
+	// result: (LEAL4 x (LEAL2 <v.Type> x x))
+	for {
+		if v.AuxInt != 13 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL4)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386LEAL2, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULLconst [21] x)
+	// cond:
+	// result: (LEAL4 x (LEAL4 <v.Type> x x))
+	for {
+		if v.AuxInt != 21 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL4)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386LEAL4, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULLconst [25] x)
+	// cond:
+	// result: (LEAL8 x (LEAL2 <v.Type> x x))
+	for {
+		if v.AuxInt != 25 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL8)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386LEAL2, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULLconst [37] x)
+	// cond:
+	// result: (LEAL4 x (LEAL8 <v.Type> x x))
+	for {
+		if v.AuxInt != 37 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL4)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386LEAL8, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULLconst [41] x)
+	// cond:
+	// result: (LEAL8 x (LEAL4 <v.Type> x x))
+	for {
+		if v.AuxInt != 41 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL8)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386LEAL4, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULLconst [73] x)
+	// cond:
+	// result: (LEAL8 x (LEAL8 <v.Type> x x))
+	for {
+		if v.AuxInt != 73 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(Op386LEAL8)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386LEAL8, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULLconst [c] x)
+	// cond: isPowerOfTwo(c)
+	// result: (SHLLconst [log2(c)] x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(Op386SHLLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [c] x)
+	// cond: isPowerOfTwo(c+1) && c >= 15
+	// result: (SUBL (SHLLconst <v.Type> [log2(c+1)] x) x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c+1) && c >= 15) {
+			break
+		}
+		v.reset(Op386SUBL)
+		v0 := b.NewValue0(v.Line, Op386SHLLconst, v.Type)
+		v0.AuxInt = log2(c + 1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [c] x)
+	// cond: isPowerOfTwo(c-1) && c >= 17
+	// result: (LEAL1 (SHLLconst <v.Type> [log2(c-1)] x) x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c-1) && c >= 17) {
+			break
+		}
+		v.reset(Op386LEAL1)
+		v0 := b.NewValue0(v.Line, Op386SHLLconst, v.Type)
+		v0.AuxInt = log2(c - 1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [c] x)
+	// cond: isPowerOfTwo(c-2) && c >= 34
+	// result: (LEAL2 (SHLLconst <v.Type> [log2(c-2)] x) x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c-2) && c >= 34) {
+			break
+		}
+		v.reset(Op386LEAL2)
+		v0 := b.NewValue0(v.Line, Op386SHLLconst, v.Type)
+		v0.AuxInt = log2(c - 2)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [c] x)
+	// cond: isPowerOfTwo(c-4) && c >= 68
+	// result: (LEAL4 (SHLLconst <v.Type> [log2(c-4)] x) x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c-4) && c >= 68) {
+			break
+		}
+		v.reset(Op386LEAL4)
+		v0 := b.NewValue0(v.Line, Op386SHLLconst, v.Type)
+		v0.AuxInt = log2(c - 4)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [c] x)
+	// cond: isPowerOfTwo(c-8) && c >= 136
+	// result: (LEAL8 (SHLLconst <v.Type> [log2(c-8)] x) x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c-8) && c >= 136) {
+			break
+		}
+		v.reset(Op386LEAL8)
+		v0 := b.NewValue0(v.Line, Op386SHLLconst, v.Type)
+		v0.AuxInt = log2(c - 8)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLconst [c] x)
+	// cond: c%3 == 0 && isPowerOfTwo(c/3)
+	// result: (SHLLconst [log2(c/3)] (LEAL2 <v.Type> x x))
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(c%3 == 0 && isPowerOfTwo(c/3)) {
+			break
+		}
+		v.reset(Op386SHLLconst)
+		v.AuxInt = log2(c / 3)
+		v0 := b.NewValue0(v.Line, Op386LEAL2, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULLconst [c] x)
+	// cond: c%5 == 0 && isPowerOfTwo(c/5)
+	// result: (SHLLconst [log2(c/5)] (LEAL4 <v.Type> x x))
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(c%5 == 0 && isPowerOfTwo(c/5)) {
+			break
+		}
+		v.reset(Op386SHLLconst)
+		v.AuxInt = log2(c / 5)
+		v0 := b.NewValue0(v.Line, Op386LEAL4, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULLconst [c] x)
+	// cond: c%9 == 0 && isPowerOfTwo(c/9)
+	// result: (SHLLconst [log2(c/9)] (LEAL8 <v.Type> x x))
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(c%9 == 0 && isPowerOfTwo(c/9)) {
+			break
+		}
+		v.reset(Op386SHLLconst)
+		v.AuxInt = log2(c / 9)
+		v0 := b.NewValue0(v.Line, Op386LEAL8, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULLconst [c] (MOVLconst [d]))
+	// cond:
+	// result: (MOVLconst [int64(int32(c*d))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = int64(int32(c * d))
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386NEGL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NEGL (MOVLconst [c]))
+	// cond:
+	// result: (MOVLconst [int64(int32(-c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = int64(int32(-c))
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386NOTL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NOTL (MOVLconst [c]))
+	// cond:
+	// result: (MOVLconst [^c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = ^c
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386ORL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORL x (MOVLconst [c]))
+	// cond:
+	// result: (ORLconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386ORLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORL (MOVLconst [c]) x)
+	// cond:
+	// result: (ORLconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(Op386ORLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORL x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORL                  x0:(MOVBload [i]   {s} p mem)     s0:(SHLLconst [8] x1:(MOVBload [i+1] {s} p mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && s0.Uses == 1   && mergePoint(b,x0,x1) != nil   && clobber(x0)   && clobber(x1)   && clobber(s0)
+	// result: @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem)
+	for {
+		x0 := v.Args[0]
+		if x0.Op != Op386MOVBload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := v.Args[1]
+		if s0.Op != Op386SHLLconst {
+			break
+		}
+		if s0.AuxInt != 8 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != Op386MOVBload {
+			break
+		}
+		if x1.AuxInt != i+1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1)
+		v0 := b.NewValue0(v.Line, Op386MOVWload, config.fe.TypeUInt16())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (ORL o0:(ORL                        x0:(MOVWload [i]   {s} p mem)     s0:(SHLLconst [16] x1:(MOVBload [i+2] {s} p mem)))     s1:(SHLLconst [24] x2:(MOVBload [i+3] {s} p mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && o0.Uses == 1   && mergePoint(b,x0,x1,x2) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(s0)   && clobber(s1)   && clobber(o0)
+	// result: @mergePoint(b,x0,x1,x2) (MOVLload [i] {s} p mem)
+	for {
+		o0 := v.Args[0]
+		if o0.Op != Op386ORL {
+			break
+		}
+		x0 := o0.Args[0]
+		if x0.Op != Op386MOVWload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := o0.Args[1]
+		if s0.Op != Op386SHLLconst {
+			break
+		}
+		if s0.AuxInt != 16 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != Op386MOVBload {
+			break
+		}
+		if x1.AuxInt != i+2 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		s1 := v.Args[1]
+		if s1.Op != Op386SHLLconst {
+			break
+		}
+		if s1.AuxInt != 24 {
+			break
+		}
+		x2 := s1.Args[0]
+		if x2.Op != Op386MOVBload {
+			break
+		}
+		if x2.AuxInt != i+3 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && o0.Uses == 1 && mergePoint(b, x0, x1, x2) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(s0) && clobber(s1) && clobber(o0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2)
+		v0 := b.NewValue0(v.Line, Op386MOVLload, config.fe.TypeUInt32())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (ORL                  x0:(MOVBloadidx1 [i]   {s} p idx mem)     s0:(SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && s0.Uses == 1   && mergePoint(b,x0,x1) != nil   && clobber(x0)   && clobber(x1)   && clobber(s0)
+	// result: @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i] {s} p idx mem)
+	for {
+		x0 := v.Args[0]
+		if x0.Op != Op386MOVBloadidx1 {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := v.Args[1]
+		if s0.Op != Op386SHLLconst {
+			break
+		}
+		if s0.AuxInt != 8 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != Op386MOVBloadidx1 {
+			break
+		}
+		if x1.AuxInt != i+1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if idx != x1.Args[1] {
+			break
+		}
+		if mem != x1.Args[2] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1)
+		v0 := b.NewValue0(v.Line, Op386MOVWloadidx1, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (ORL o0:(ORL                        x0:(MOVWloadidx1 [i]   {s} p idx mem)     s0:(SHLLconst [16] x1:(MOVBloadidx1 [i+2] {s} p idx mem)))     s1:(SHLLconst [24] x2:(MOVBloadidx1 [i+3] {s} p idx mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && o0.Uses == 1   && mergePoint(b,x0,x1,x2) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(s0)   && clobber(s1)   && clobber(o0)
+	// result: @mergePoint(b,x0,x1,x2) (MOVLloadidx1 <v.Type> [i] {s} p idx mem)
+	for {
+		o0 := v.Args[0]
+		if o0.Op != Op386ORL {
+			break
+		}
+		x0 := o0.Args[0]
+		if x0.Op != Op386MOVWloadidx1 {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := o0.Args[1]
+		if s0.Op != Op386SHLLconst {
+			break
+		}
+		if s0.AuxInt != 16 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != Op386MOVBloadidx1 {
+			break
+		}
+		if x1.AuxInt != i+2 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if idx != x1.Args[1] {
+			break
+		}
+		if mem != x1.Args[2] {
+			break
+		}
+		s1 := v.Args[1]
+		if s1.Op != Op386SHLLconst {
+			break
+		}
+		if s1.AuxInt != 24 {
+			break
+		}
+		x2 := s1.Args[0]
+		if x2.Op != Op386MOVBloadidx1 {
+			break
+		}
+		if x2.AuxInt != i+3 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if idx != x2.Args[1] {
+			break
+		}
+		if mem != x2.Args[2] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && o0.Uses == 1 && mergePoint(b, x0, x1, x2) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(s0) && clobber(s1) && clobber(o0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2)
+		v0 := b.NewValue0(v.Line, Op386MOVLloadidx1, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386ORLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORLconst [c] x)
+	// cond: int32(c)==0
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORLconst [c] _)
+	// cond: int32(c)==-1
+	// result: (MOVLconst [-1])
+	for {
+		c := v.AuxInt
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (ORLconst [c] (MOVLconst [d]))
+	// cond:
+	// result: (MOVLconst [c|d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = c | d
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386ROLBconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ROLBconst [c] (ROLBconst [d] x))
+	// cond:
+	// result: (ROLBconst [(c+d)& 7] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ROLBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386ROLBconst)
+		v.AuxInt = (c + d) & 7
+		v.AddArg(x)
+		return true
+	}
+	// match: (ROLBconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386ROLLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ROLLconst [c] (ROLLconst [d] x))
+	// cond:
+	// result: (ROLLconst [(c+d)&31] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ROLLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386ROLLconst)
+		v.AuxInt = (c + d) & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (ROLLconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386ROLWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ROLWconst [c] (ROLWconst [d] x))
+	// cond:
+	// result: (ROLWconst [(c+d)&15] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386ROLWconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386ROLWconst)
+		v.AuxInt = (c + d) & 15
+		v.AddArg(x)
+		return true
+	}
+	// match: (ROLWconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SARB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARB x (MOVLconst [c]))
+	// cond:
+	// result: (SARBconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SARBconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SARB x (MOVLconst [c]))
+	// cond:
+	// result: (SARBconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SARBconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SARBconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARBconst [c] (MOVLconst [d]))
+	// cond:
+	// result: (MOVLconst [d>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = d >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SARL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARL x (MOVLconst [c]))
+	// cond:
+	// result: (SARLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SARLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SARL x (MOVLconst [c]))
+	// cond:
+	// result: (SARLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SARLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SARL x (ANDLconst [31] y))
+	// cond:
+	// result: (SARL x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ANDLconst {
+			break
+		}
+		if v_1.AuxInt != 31 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386SARL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SARLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARLconst [c] (MOVLconst [d]))
+	// cond:
+	// result: (MOVLconst [d>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = d >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SARW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARW x (MOVLconst [c]))
+	// cond:
+	// result: (SARWconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SARWconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SARW x (MOVLconst [c]))
+	// cond:
+	// result: (SARWconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SARWconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SARWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARWconst [c] (MOVLconst [d]))
+	// cond:
+	// result: (MOVLconst [d>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = d >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SBBL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBBL x (MOVLconst [c]) f)
+	// cond:
+	// result: (SBBLconst [c] x f)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		f := v.Args[2]
+		v.reset(Op386SBBLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(f)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SBBLcarrymask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBBLcarrymask (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SBBLcarrymask (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (SBBLcarrymask (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SBBLcarrymask (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (SBBLcarrymask (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SETA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETA (InvertFlags x))
+	// cond:
+	// result: (SETB x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(Op386SETB)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETA (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETA (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETA (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETA (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETA (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SETAE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETAE (InvertFlags x))
+	// cond:
+	// result: (SETBE x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(Op386SETBE)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETAE (FlagEQ))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETAE (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETAE (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETAE (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETAE (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SETB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETB (InvertFlags x))
+	// cond:
+	// result: (SETA x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(Op386SETA)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETB (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETB (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETB (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETB (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETB (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SETBE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETBE (InvertFlags x))
+	// cond:
+	// result: (SETAE x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(Op386SETAE)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETBE (FlagEQ))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETBE (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETBE (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETBE (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETBE (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SETEQ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETEQ (InvertFlags x))
+	// cond:
+	// result: (SETEQ x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(Op386SETEQ)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETEQ (FlagEQ))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETEQ (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETEQ (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETEQ (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETEQ (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SETG(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETG (InvertFlags x))
+	// cond:
+	// result: (SETL x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(Op386SETL)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETG (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETG (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETG (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETG (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETG (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SETGE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETGE (InvertFlags x))
+	// cond:
+	// result: (SETLE x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(Op386SETLE)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETGE (FlagEQ))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETGE (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETGE (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETGE (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETGE (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SETL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETL (InvertFlags x))
+	// cond:
+	// result: (SETG x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(Op386SETG)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETL (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETL (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETL (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETL (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETL (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SETLE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETLE (InvertFlags x))
+	// cond:
+	// result: (SETGE x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(Op386SETGE)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETLE (FlagEQ))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETLE (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETLE (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETLE (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETLE (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SETNE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETNE (InvertFlags x))
+	// cond:
+	// result: (SETNE x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(Op386SETNE)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETNE (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagEQ {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETNE (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETNE (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagLT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETNE (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_ULT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETNE (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386FlagGT_UGT {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SHLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SHLL x (MOVLconst [c]))
+	// cond:
+	// result: (SHLLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SHLLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHLL x (MOVLconst [c]))
+	// cond:
+	// result: (SHLLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SHLLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHLL x (ANDLconst [31] y))
+	// cond:
+	// result: (SHLL x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ANDLconst {
+			break
+		}
+		if v_1.AuxInt != 31 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386SHLL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SHRB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SHRB x (MOVLconst [c]))
+	// cond:
+	// result: (SHRBconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SHRBconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHRB x (MOVLconst [c]))
+	// cond:
+	// result: (SHRBconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SHRBconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SHRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SHRL x (MOVLconst [c]))
+	// cond:
+	// result: (SHRLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SHRLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHRL x (MOVLconst [c]))
+	// cond:
+	// result: (SHRLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SHRLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHRL x (ANDLconst [31] y))
+	// cond:
+	// result: (SHRL x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386ANDLconst {
+			break
+		}
+		if v_1.AuxInt != 31 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(Op386SHRL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SHRW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SHRW x (MOVLconst [c]))
+	// cond:
+	// result: (SHRWconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SHRWconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHRW x (MOVLconst [c]))
+	// cond:
+	// result: (SHRWconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SHRWconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SUBL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBL x (MOVLconst [c]))
+	// cond:
+	// result: (SUBLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SUBLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBL (MOVLconst [c]) x)
+	// cond:
+	// result: (NEGL (SUBLconst <v.Type> x [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(Op386NEGL)
+		v0 := b.NewValue0(v.Line, Op386SUBLconst, v.Type)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBL x x)
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SUBLcarry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBLcarry x (MOVLconst [c]))
+	// cond:
+	// result: (SUBLconstcarry [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386SUBLconstcarry)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386SUBLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBLconst [c] x)
+	// cond: int32(c) == 0
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBLconst [c] x)
+	// cond:
+	// result: (ADDLconst [int64(int32(-c))] x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(Op386ADDLconst)
+		v.AuxInt = int64(int32(-c))
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_Op386XORL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORL x (MOVLconst [c]))
+	// cond:
+	// result: (XORLconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != Op386MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(Op386XORLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORL (MOVLconst [c]) x)
+	// cond:
+	// result: (XORLconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(Op386XORLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORL x x)
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_Op386XORLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORLconst [c] (XORLconst [d] x))
+	// cond:
+	// result: (XORLconst [c ^ d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386XORLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(Op386XORLconst)
+		v.AuxInt = c ^ d
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORLconst [c] x)
+	// cond: int32(c)==0
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORLconst [c] (MOVLconst [d]))
+	// cond:
+	// result: (MOVLconst [c^d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != Op386MOVLconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpAdd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add16  x y)
+	// cond:
+	// result: (ADDL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ADDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32  x y)
+	// cond:
+	// result: (ADDL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ADDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpAdd32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32F x y)
+	// cond:
+	// result: (ADDSS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ADDSS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpAdd32carry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32carry x y)
+	// cond:
+	// result: (ADDLcarry x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ADDLcarry)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpAdd32withcarry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32withcarry x y c)
+	// cond:
+	// result: (ADCL x y c)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		c := v.Args[2]
+		v.reset(Op386ADCL)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(c)
+		return true
+	}
+}
+func rewriteValue386_OpAdd64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64F x y)
+	// cond:
+	// result: (ADDSD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ADDSD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpAdd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add8   x y)
+	// cond:
+	// result: (ADDL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ADDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpAddPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AddPtr x y)
+	// cond:
+	// result: (ADDL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ADDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpAddr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Addr {sym} base)
+	// cond:
+	// result: (LEAL {sym} base)
+	for {
+		sym := v.Aux
+		base := v.Args[0]
+		v.reset(Op386LEAL)
+		v.Aux = sym
+		v.AddArg(base)
+		return true
+	}
+}
+func rewriteValue386_OpAnd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And16 x y)
+	// cond:
+	// result: (ANDL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpAnd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And32 x y)
+	// cond:
+	// result: (ANDL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And8  x y)
+	// cond:
+	// result: (ANDL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpAndB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AndB x y)
+	// cond:
+	// result: (ANDL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpBswap32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Bswap32 x)
+	// cond:
+	// result: (BSWAPL x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386BSWAPL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpClosureCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ClosureCall [argwid] entry closure mem)
+	// cond:
+	// result: (CALLclosure [argwid] entry closure mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		closure := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386CALLclosure)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(closure)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValue386_OpCom16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com16 x)
+	// cond:
+	// result: (NOTL x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386NOTL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpCom32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com32 x)
+	// cond:
+	// result: (NOTL x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386NOTL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpCom8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com8  x)
+	// cond:
+	// result: (NOTL x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386NOTL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpConst16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const16  [val])
+	// cond:
+	// result: (MOVLconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValue386_OpConst32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32  [val])
+	// cond:
+	// result: (MOVLconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValue386_OpConst32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32F [val])
+	// cond:
+	// result: (MOVSSconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(Op386MOVSSconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValue386_OpConst64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64F [val])
+	// cond:
+	// result: (MOVSDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(Op386MOVSDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValue386_OpConst8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const8   [val])
+	// cond:
+	// result: (MOVLconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValue386_OpConstBool(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstBool [b])
+	// cond:
+	// result: (MOVLconst [b])
+	for {
+		b := v.AuxInt
+		v.reset(Op386MOVLconst)
+		v.AuxInt = b
+		return true
+	}
+}
+func rewriteValue386_OpConstNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstNil)
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v.reset(Op386MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+}
+func rewriteValue386_OpConvert(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Convert <t> x mem)
+	// cond:
+	// result: (MOVLconvert <t> x mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		mem := v.Args[1]
+		v.reset(Op386MOVLconvert)
+		v.Type = t
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValue386_OpCvt32Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto32 x)
+	// cond:
+	// result: (CVTTSS2SL x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386CVTTSS2SL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpCvt32Fto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64F x)
+	// cond:
+	// result: (CVTSS2SD x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386CVTSS2SD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpCvt32to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to32F x)
+	// cond:
+	// result: (CVTSL2SS x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386CVTSL2SS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpCvt32to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to64F x)
+	// cond:
+	// result: (CVTSL2SD x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386CVTSL2SD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpCvt64Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32 x)
+	// cond:
+	// result: (CVTTSD2SL x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386CVTTSD2SL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpCvt64Fto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32F x)
+	// cond:
+	// result: (CVTSD2SS x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386CVTSD2SS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpDeferCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (DeferCall [argwid] mem)
+	// cond:
+	// result: (CALLdefer [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(Op386CALLdefer)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValue386_OpDiv16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16  x y)
+	// cond:
+	// result: (DIVW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386DIVW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpDiv16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16u x y)
+	// cond:
+	// result: (DIVWU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386DIVWU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpDiv32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32  x y)
+	// cond:
+	// result: (DIVL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386DIVL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpDiv32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32F x y)
+	// cond:
+	// result: (DIVSS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386DIVSS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpDiv32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32u x y)
+	// cond:
+	// result: (DIVLU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386DIVLU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpDiv64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64F x y)
+	// cond:
+	// result: (DIVSD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386DIVSD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpDiv8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8   x y)
+	// cond:
+	// result: (DIVW  (SignExt8to16 x) (SignExt8to16 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386DIVW)
+		v0 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpDiv8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8u  x y)
+	// cond:
+	// result: (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386DIVWU)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpEq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq16  x y)
+	// cond:
+	// result: (SETEQ (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETEQ)
+		v0 := b.NewValue0(v.Line, Op386CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpEq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32  x y)
+	// cond:
+	// result: (SETEQ (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETEQ)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpEq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32F x y)
+	// cond:
+	// result: (SETEQF (UCOMISS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETEQF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpEq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64F x y)
+	// cond:
+	// result: (SETEQF (UCOMISD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETEQF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpEq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq8   x y)
+	// cond:
+	// result: (SETEQ (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETEQ)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpEqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqB   x y)
+	// cond:
+	// result: (SETEQ (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETEQ)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpEqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqPtr x y)
+	// cond:
+	// result: (SETEQ (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETEQ)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16  x y)
+	// cond:
+	// result: (SETGE (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGE)
+		v0 := b.NewValue0(v.Line, Op386CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16U x y)
+	// cond:
+	// result: (SETAE (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETAE)
+		v0 := b.NewValue0(v.Line, Op386CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32  x y)
+	// cond:
+	// result: (SETGE (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGE)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32F x y)
+	// cond:
+	// result: (SETGEF (UCOMISS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGEF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32U x y)
+	// cond:
+	// result: (SETAE (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETAE)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64F x y)
+	// cond:
+	// result: (SETGEF (UCOMISD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGEF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8   x y)
+	// cond:
+	// result: (SETGE (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGE)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8U  x y)
+	// cond:
+	// result: (SETAE (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETAE)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGetClosurePtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetClosurePtr)
+	// cond:
+	// result: (LoweredGetClosurePtr)
+	for {
+		v.reset(Op386LoweredGetClosurePtr)
+		return true
+	}
+}
+func rewriteValue386_OpGetG(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetG mem)
+	// cond:
+	// result: (LoweredGetG mem)
+	for {
+		mem := v.Args[0]
+		v.reset(Op386LoweredGetG)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValue386_OpGoCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GoCall [argwid] mem)
+	// cond:
+	// result: (CALLgo [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(Op386CALLgo)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValue386_OpGreater16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16  x y)
+	// cond:
+	// result: (SETG (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETG)
+		v0 := b.NewValue0(v.Line, Op386CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGreater16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16U x y)
+	// cond:
+	// result: (SETA (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETA)
+		v0 := b.NewValue0(v.Line, Op386CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGreater32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32  x y)
+	// cond:
+	// result: (SETG (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETG)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGreater32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32F x y)
+	// cond:
+	// result: (SETGF (UCOMISS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGreater32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32U x y)
+	// cond:
+	// result: (SETA (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETA)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGreater64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64F x y)
+	// cond:
+	// result: (SETGF (UCOMISD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGreater8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8   x y)
+	// cond:
+	// result: (SETG (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETG)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpGreater8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8U  x y)
+	// cond:
+	// result: (SETA (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETA)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpHmul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16  x y)
+	// cond:
+	// result: (HMULW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386HMULW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpHmul16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16u x y)
+	// cond:
+	// result: (HMULWU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386HMULWU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpHmul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32  x y)
+	// cond:
+	// result: (HMULL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386HMULL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpHmul32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32u x y)
+	// cond:
+	// result: (HMULLU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386HMULLU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpHmul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8   x y)
+	// cond:
+	// result: (HMULB  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386HMULB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpHmul8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8u  x y)
+	// cond:
+	// result: (HMULBU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386HMULBU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpInterCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (InterCall [argwid] entry mem)
+	// cond:
+	// result: (CALLinter [argwid] entry mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		mem := v.Args[1]
+		v.reset(Op386CALLinter)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValue386_OpIsInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsInBounds idx len)
+	// cond:
+	// result: (SETB (CMPL idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(Op386SETB)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpIsNonNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsNonNil p)
+	// cond:
+	// result: (SETNE (TESTL p p))
+	for {
+		p := v.Args[0]
+		v.reset(Op386SETNE)
+		v0 := b.NewValue0(v.Line, Op386TESTL, TypeFlags)
+		v0.AddArg(p)
+		v0.AddArg(p)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpIsSliceInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsSliceInBounds idx len)
+	// cond:
+	// result: (SETBE (CMPL idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(Op386SETBE)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16  x y)
+	// cond:
+	// result: (SETLE (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETLE)
+		v0 := b.NewValue0(v.Line, Op386CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16U x y)
+	// cond:
+	// result: (SETBE (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETBE)
+		v0 := b.NewValue0(v.Line, Op386CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32  x y)
+	// cond:
+	// result: (SETLE (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETLE)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32F x y)
+	// cond:
+	// result: (SETGEF (UCOMISS y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGEF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISS, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32U x y)
+	// cond:
+	// result: (SETBE (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETBE)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64F x y)
+	// cond:
+	// result: (SETGEF (UCOMISD y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGEF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISD, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8   x y)
+	// cond:
+	// result: (SETLE (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETLE)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8U  x y)
+	// cond:
+	// result: (SETBE (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETBE)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLess16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16  x y)
+	// cond:
+	// result: (SETL (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETL)
+		v0 := b.NewValue0(v.Line, Op386CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLess16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16U x y)
+	// cond:
+	// result: (SETB (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETB)
+		v0 := b.NewValue0(v.Line, Op386CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLess32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32  x y)
+	// cond:
+	// result: (SETL (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETL)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLess32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32F x y)
+	// cond:
+	// result: (SETGF (UCOMISS y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISS, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLess32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32U x y)
+	// cond:
+	// result: (SETB (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETB)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLess64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64F x y)
+	// cond:
+	// result: (SETGF (UCOMISD y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETGF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISD, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLess8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8   x y)
+	// cond:
+	// result: (SETL (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETL)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLess8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8U  x y)
+	// cond:
+	// result: (SETB (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETB)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpLoad(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Load <t> ptr mem)
+	// cond: (is32BitInt(t) || isPtr(t))
+	// result: (MOVLload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t) || isPtr(t)) {
+			break
+		}
+		v.reset(Op386MOVLload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is16BitInt(t)
+	// result: (MOVWload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t)) {
+			break
+		}
+		v.reset(Op386MOVWload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (t.IsBoolean() || is8BitInt(t))
+	// result: (MOVBload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(t.IsBoolean() || is8BitInt(t)) {
+			break
+		}
+		v.reset(Op386MOVBload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitFloat(t)
+	// result: (MOVSSload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitFloat(t)) {
+			break
+		}
+		v.reset(Op386MOVSSload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitFloat(t)
+	// result: (MOVSDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitFloat(t)) {
+			break
+		}
+		v.reset(Op386MOVSDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpLrot16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot16 <t> x [c])
+	// cond:
+	// result: (ROLWconst <t> [c&15] x)
+	for {
+		t := v.Type
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(Op386ROLWconst)
+		v.Type = t
+		v.AuxInt = c & 15
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpLrot32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot32 <t> x [c])
+	// cond:
+	// result: (ROLLconst <t> [c&31] x)
+	for {
+		t := v.Type
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(Op386ROLLconst)
+		v.Type = t
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpLrot8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot8  <t> x [c])
+	// cond:
+	// result: (ROLBconst <t> [c&7] x)
+	for {
+		t := v.Type
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(Op386ROLBconst)
+		v.Type = t
+		v.AuxInt = c & 7
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpLsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x16 <t> x y)
+	// cond:
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPWconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpLsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x32 <t> x y)
+	// cond:
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpLsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x64 x (Const64 [c]))
+	// cond: uint64(c) < 16
+	// result: (SHLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(Op386SHLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh16x64 _ (Const64 [c]))
+	// cond: uint64(c) >= 16
+	// result: (Const16 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpConst16)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpLsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x8  <t> x y)
+	// cond:
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPBconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpLsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x16 <t> x y)
+	// cond:
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPWconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpLsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x32 <t> x y)
+	// cond:
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpLsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x64 x (Const64 [c]))
+	// cond: uint64(c) < 32
+	// result: (SHLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(Op386SHLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh32x64 _ (Const64 [c]))
+	// cond: uint64(c) >= 32
+	// result: (Const32 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpLsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x8  <t> x y)
+	// cond:
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPBconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpLsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x16 <t> x y)
+	// cond:
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPWconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpLsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x32 <t> x y)
+	// cond:
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpLsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x64 x (Const64 [c]))
+	// cond: uint64(c) < 8
+	// result: (SHLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(Op386SHLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh8x64 _ (Const64 [c]))
+	// cond: uint64(c) >= 8
+	// result: (Const8 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpConst8)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpLsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x8  <t> x y)
+	// cond:
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPBconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpMod16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16  x y)
+	// cond:
+	// result: (MODW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MODW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpMod16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16u x y)
+	// cond:
+	// result: (MODWU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MODWU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpMod32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32  x y)
+	// cond:
+	// result: (MODL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MODL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpMod32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32u x y)
+	// cond:
+	// result: (MODLU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MODLU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpMod8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8   x y)
+	// cond:
+	// result: (MODW  (SignExt8to16 x) (SignExt8to16 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MODW)
+		v0 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpMod8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8u  x y)
+	// cond:
+	// result: (MODWU (ZeroExt8to16 x) (ZeroExt8to16 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MODWU)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpMove(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Move [s] _ _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore dst (MOVBload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(Op386MOVBstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, Op386MOVBload, config.fe.TypeUInt8())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVWstore dst (MOVWload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(Op386MOVWstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, Op386MOVWload, config.fe.TypeUInt16())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVLstore dst (MOVLload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(Op386MOVLstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, Op386MOVLload, config.fe.TypeUInt32())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] dst (MOVBload [2] src mem) 		(MOVWstore dst (MOVWload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(Op386MOVBstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, Op386MOVBload, config.fe.TypeUInt8())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386MOVWstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, Op386MOVWload, config.fe.TypeUInt16())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 5
+	// result: (MOVBstore [4] dst (MOVBload [4] src mem) 		(MOVLstore dst (MOVLload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 5) {
+			break
+		}
+		v.reset(Op386MOVBstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, Op386MOVBload, config.fe.TypeUInt8())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386MOVLstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, Op386MOVLload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 6
+	// result: (MOVWstore [4] dst (MOVWload [4] src mem) 		(MOVLstore dst (MOVLload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 6) {
+			break
+		}
+		v.reset(Op386MOVWstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, Op386MOVWload, config.fe.TypeUInt16())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386MOVLstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, Op386MOVLload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 7
+	// result: (MOVLstore [3] dst (MOVLload [3] src mem) 		(MOVLstore dst (MOVLload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 7) {
+			break
+		}
+		v.reset(Op386MOVLstore)
+		v.AuxInt = 3
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, Op386MOVLload, config.fe.TypeUInt32())
+		v0.AuxInt = 3
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386MOVLstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, Op386MOVLload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8
+	// result: (MOVLstore [4] dst (MOVLload [4] src mem) 		(MOVLstore dst (MOVLload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8) {
+			break
+		}
+		v.reset(Op386MOVLstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, Op386MOVLload, config.fe.TypeUInt32())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386MOVLstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, Op386MOVLload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 8 && SizeAndAlign(s).Size()%4 != 0
+	// result: (Move [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%4] 		(ADDLconst <dst.Type> dst [SizeAndAlign(s).Size()%4]) 		(ADDLconst <src.Type> src [SizeAndAlign(s).Size()%4]) 		(MOVLstore dst (MOVLload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 8 && SizeAndAlign(s).Size()%4 != 0) {
+			break
+		}
+		v.reset(OpMove)
+		v.AuxInt = SizeAndAlign(s).Size() - SizeAndAlign(s).Size()%4
+		v0 := b.NewValue0(v.Line, Op386ADDLconst, dst.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() % 4
+		v0.AddArg(dst)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386ADDLconst, src.Type)
+		v1.AuxInt = SizeAndAlign(s).Size() % 4
+		v1.AddArg(src)
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, Op386MOVLstore, TypeMem)
+		v2.AddArg(dst)
+		v3 := b.NewValue0(v.Line, Op386MOVLload, config.fe.TypeUInt32())
+		v3.AddArg(src)
+		v3.AddArg(mem)
+		v2.AddArg(v3)
+		v2.AddArg(mem)
+		v.AddArg(v2)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 8 && SizeAndAlign(s).Size() <= 4*128 && SizeAndAlign(s).Size()%4 == 0 	&& !config.noDuffDevice
+	// result: (DUFFCOPY [10*(128-SizeAndAlign(s).Size()/4)] dst src mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 8 && SizeAndAlign(s).Size() <= 4*128 && SizeAndAlign(s).Size()%4 == 0 && !config.noDuffDevice) {
+			break
+		}
+		v.reset(Op386DUFFCOPY)
+		v.AuxInt = 10 * (128 - SizeAndAlign(s).Size()/4)
+		v.AddArg(dst)
+		v.AddArg(src)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: (SizeAndAlign(s).Size() > 4*128 || config.noDuffDevice) && SizeAndAlign(s).Size()%4 == 0
+	// result: (REPMOVSL dst src (MOVLconst [SizeAndAlign(s).Size()/4]) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !((SizeAndAlign(s).Size() > 4*128 || config.noDuffDevice) && SizeAndAlign(s).Size()%4 == 0) {
+			break
+		}
+		v.reset(Op386REPMOVSL)
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, Op386MOVLconst, config.fe.TypeUInt32())
+		v0.AuxInt = SizeAndAlign(s).Size() / 4
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpMul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul16  x y)
+	// cond:
+	// result: (MULL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MULL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpMul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32  x y)
+	// cond:
+	// result: (MULL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MULL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpMul32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32F x y)
+	// cond:
+	// result: (MULSS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MULSS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpMul32uhilo(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32uhilo x y)
+	// cond:
+	// result: (MULLQU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MULLQU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpMul64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64F x y)
+	// cond:
+	// result: (MULSD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MULSD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpMul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul8   x y)
+	// cond:
+	// result: (MULL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386MULL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpNeg16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg16  x)
+	// cond:
+	// result: (NEGL x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386NEGL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpNeg32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32  x)
+	// cond:
+	// result: (NEGL x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386NEGL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpNeg32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32F x)
+	// cond: !config.use387
+	// result: (PXOR x (MOVSSconst <config.Frontend().TypeFloat32()> [f2i(math.Copysign(0, -1))]))
+	for {
+		x := v.Args[0]
+		if !(!config.use387) {
+			break
+		}
+		v.reset(Op386PXOR)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386MOVSSconst, config.Frontend().TypeFloat32())
+		v0.AuxInt = f2i(math.Copysign(0, -1))
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Neg32F x)
+	// cond: config.use387
+	// result: (FCHS x)
+	for {
+		x := v.Args[0]
+		if !(config.use387) {
+			break
+		}
+		v.reset(Op386FCHS)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpNeg64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64F x)
+	// cond: !config.use387
+	// result: (PXOR x (MOVSDconst <config.Frontend().TypeFloat64()> [f2i(math.Copysign(0, -1))]))
+	for {
+		x := v.Args[0]
+		if !(!config.use387) {
+			break
+		}
+		v.reset(Op386PXOR)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386MOVSDconst, config.Frontend().TypeFloat64())
+		v0.AuxInt = f2i(math.Copysign(0, -1))
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Neg64F x)
+	// cond: config.use387
+	// result: (FCHS x)
+	for {
+		x := v.Args[0]
+		if !(config.use387) {
+			break
+		}
+		v.reset(Op386FCHS)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpNeg8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg8   x)
+	// cond:
+	// result: (NEGL x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386NEGL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpNeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq16  x y)
+	// cond:
+	// result: (SETNE (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETNE)
+		v0 := b.NewValue0(v.Line, Op386CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpNeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32  x y)
+	// cond:
+	// result: (SETNE (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETNE)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpNeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32F x y)
+	// cond:
+	// result: (SETNEF (UCOMISS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETNEF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpNeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64F x y)
+	// cond:
+	// result: (SETNEF (UCOMISD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETNEF)
+		v0 := b.NewValue0(v.Line, Op386UCOMISD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpNeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq8   x y)
+	// cond:
+	// result: (SETNE (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETNE)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpNeqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqB   x y)
+	// cond:
+	// result: (SETNE (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETNE)
+		v0 := b.NewValue0(v.Line, Op386CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpNeqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqPtr x y)
+	// cond:
+	// result: (SETNE (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SETNE)
+		v0 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpNilCheck(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NilCheck ptr mem)
+	// cond:
+	// result: (LoweredNilCheck ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(Op386LoweredNilCheck)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValue386_OpNot(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Not x)
+	// cond:
+	// result: (XORLconst [1] x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386XORLconst)
+		v.AuxInt = 1
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpOffPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OffPtr [off] ptr)
+	// cond:
+	// result: (ADDLconst [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		v.reset(Op386ADDLconst)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+}
+func rewriteValue386_OpOr16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or16 x y)
+	// cond:
+	// result: (ORL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ORL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpOr32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or32 x y)
+	// cond:
+	// result: (ORL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ORL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or8  x y)
+	// cond:
+	// result: (ORL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ORL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpOrB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OrB x y)
+	// cond:
+	// result: (ORL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ORL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpRsh16Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux16 <t> x y)
+	// cond:
+	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPWconst y [16])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPWconst, TypeFlags)
+		v2.AuxInt = 16
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpRsh16Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux32 <t> x y)
+	// cond:
+	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPLconst y [16])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v2.AuxInt = 16
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpRsh16Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux64 x (Const64 [c]))
+	// cond: uint64(c) < 16
+	// result: (SHRWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(Op386SHRWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh16Ux64 _ (Const64 [c]))
+	// cond: uint64(c) >= 16
+	// result: (Const16 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpConst16)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpRsh16Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux8  <t> x y)
+	// cond:
+	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPBconst y [16])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPBconst, TypeFlags)
+		v2.AuxInt = 16
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpRsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x16 <t> x y)
+	// cond:
+	// result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [16])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SARW)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, Op386NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, Op386SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, Op386CMPWconst, TypeFlags)
+		v3.AuxInt = 16
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpRsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x32 <t> x y)
+	// cond:
+	// result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [16])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SARW)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, Op386NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, Op386SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v3.AuxInt = 16
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpRsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x64 x (Const64 [c]))
+	// cond: uint64(c) < 16
+	// result: (SARWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(Op386SARWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh16x64 x (Const64 [c]))
+	// cond: uint64(c) >= 16
+	// result: (SARWconst x [15])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(Op386SARWconst)
+		v.AuxInt = 15
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpRsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x8  <t> x y)
+	// cond:
+	// result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [16])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SARW)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, Op386NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, Op386SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, Op386CMPBconst, TypeFlags)
+		v3.AuxInt = 16
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpRsh32Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux16 <t> x y)
+	// cond:
+	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHRL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPWconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpRsh32Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux32 <t> x y)
+	// cond:
+	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHRL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpRsh32Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux64 x (Const64 [c]))
+	// cond: uint64(c) < 32
+	// result: (SHRLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(Op386SHRLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32Ux64 _ (Const64 [c]))
+	// cond: uint64(c) >= 32
+	// result: (Const32 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpRsh32Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux8  <t> x y)
+	// cond:
+	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHRL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPBconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpRsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x16 <t> x y)
+	// cond:
+	// result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [32])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SARL)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, Op386NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, Op386SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, Op386CMPWconst, TypeFlags)
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpRsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x32 <t> x y)
+	// cond:
+	// result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [32])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SARL)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, Op386NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, Op386SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpRsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x64 x (Const64 [c]))
+	// cond: uint64(c) < 32
+	// result: (SARLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(Op386SARLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32x64 x (Const64 [c]))
+	// cond: uint64(c) >= 32
+	// result: (SARLconst x [31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(Op386SARLconst)
+		v.AuxInt = 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpRsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x8  <t> x y)
+	// cond:
+	// result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [32])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SARL)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, Op386NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, Op386SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, Op386CMPBconst, TypeFlags)
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpRsh8Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux16 <t> x y)
+	// cond:
+	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPWconst y [8])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHRB, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPWconst, TypeFlags)
+		v2.AuxInt = 8
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpRsh8Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux32 <t> x y)
+	// cond:
+	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPLconst y [8])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHRB, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v2.AuxInt = 8
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpRsh8Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux64 x (Const64 [c]))
+	// cond: uint64(c) < 8
+	// result: (SHRBconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(Op386SHRBconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh8Ux64 _ (Const64 [c]))
+	// cond: uint64(c) >= 8
+	// result: (Const8 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpConst8)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpRsh8Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux8  <t> x y)
+	// cond:
+	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPBconst y [8])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386ANDL)
+		v0 := b.NewValue0(v.Line, Op386SHRB, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, Op386CMPBconst, TypeFlags)
+		v2.AuxInt = 8
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValue386_OpRsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x16 <t> x y)
+	// cond:
+	// result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [8])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SARB)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, Op386NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, Op386SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, Op386CMPWconst, TypeFlags)
+		v3.AuxInt = 8
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpRsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x32 <t> x y)
+	// cond:
+	// result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [8])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SARB)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, Op386NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, Op386SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v3.AuxInt = 8
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpRsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x64 x (Const64 [c]))
+	// cond: uint64(c) < 8
+	// result: (SARBconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(Op386SARBconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh8x64 x (Const64 [c]))
+	// cond: uint64(c) >= 8
+	// result: (SARBconst x [7])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(Op386SARBconst)
+		v.AuxInt = 7
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpRsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x8  <t> x y)
+	// cond:
+	// result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [8])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SARB)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, Op386ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, Op386NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, Op386SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, Op386CMPBconst, TypeFlags)
+		v3.AuxInt = 8
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpSignExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to32 x)
+	// cond:
+	// result: (MOVWLSX x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386MOVWLSX)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpSignExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to16  x)
+	// cond:
+	// result: (MOVBLSX x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386MOVBLSX)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpSignExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to32  x)
+	// cond:
+	// result: (MOVBLSX x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386MOVBLSX)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpSignmask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Signmask x)
+	// cond:
+	// result: (SARLconst x [31])
+	for {
+		x := v.Args[0]
+		v.reset(Op386SARLconst)
+		v.AuxInt = 31
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpSlicemask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Slicemask <t> x)
+	// cond:
+	// result: (XORLconst [-1] (SARLconst <t> (SUBLconst <t> x [1]) [31]))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(Op386XORLconst)
+		v.AuxInt = -1
+		v0 := b.NewValue0(v.Line, Op386SARLconst, t)
+		v0.AuxInt = 31
+		v1 := b.NewValue0(v.Line, Op386SUBLconst, t)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValue386_OpSqrt(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sqrt x)
+	// cond:
+	// result: (SQRTSD x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386SQRTSD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpStaticCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (StaticCall [argwid] {target} mem)
+	// cond:
+	// result: (CALLstatic [argwid] {target} mem)
+	for {
+		argwid := v.AuxInt
+		target := v.Aux
+		mem := v.Args[0]
+		v.reset(Op386CALLstatic)
+		v.AuxInt = argwid
+		v.Aux = target
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValue386_OpStore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Store [8] ptr val mem)
+	// cond: is64BitFloat(val.Type)
+	// result: (MOVSDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is64BitFloat(val.Type)) {
+			break
+		}
+		v.reset(Op386MOVSDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: is32BitFloat(val.Type)
+	// result: (MOVSSstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(Op386MOVSSstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond:
+	// result: (MOVLstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVLstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [2] ptr val mem)
+	// cond:
+	// result: (MOVWstore ptr val mem)
+	for {
+		if v.AuxInt != 2 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVWstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [1] ptr val mem)
+	// cond:
+	// result: (MOVBstore ptr val mem)
+	for {
+		if v.AuxInt != 1 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(Op386MOVBstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpSub16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub16  x y)
+	// cond:
+	// result: (SUBL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SUBL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpSub32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32  x y)
+	// cond:
+	// result: (SUBL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SUBL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpSub32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32F x y)
+	// cond:
+	// result: (SUBSS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SUBSS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpSub32carry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32carry x y)
+	// cond:
+	// result: (SUBLcarry x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SUBLcarry)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpSub32withcarry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32withcarry x y c)
+	// cond:
+	// result: (SBBL x y c)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		c := v.Args[2]
+		v.reset(Op386SBBL)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(c)
+		return true
+	}
+}
+func rewriteValue386_OpSub64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64F x y)
+	// cond:
+	// result: (SUBSD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SUBSD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpSub8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub8   x y)
+	// cond:
+	// result: (SUBL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SUBL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpSubPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SubPtr x y)
+	// cond:
+	// result: (SUBL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386SUBL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpTrunc16to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc16to8  x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpTrunc32to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to16 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpTrunc32to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to8  x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpXor16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor16 x y)
+	// cond:
+	// result: (XORL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386XORL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpXor32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor32 x y)
+	// cond:
+	// result: (XORL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386XORL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpXor8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor8  x y)
+	// cond:
+	// result: (XORL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(Op386XORL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValue386_OpZero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zero [s] _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstoreconst [0] destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(Op386MOVBstoreconst)
+		v.AuxInt = 0
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVWstoreconst [0] destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(Op386MOVWstoreconst)
+		v.AuxInt = 0
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVLstoreconst [0] destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(Op386MOVLstoreconst)
+		v.AuxInt = 0
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstoreconst [makeValAndOff(0,2)] destptr 		(MOVWstoreconst [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(Op386MOVBstoreconst)
+		v.AuxInt = makeValAndOff(0, 2)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, Op386MOVWstoreconst, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 5
+	// result: (MOVBstoreconst [makeValAndOff(0,4)] destptr 		(MOVLstoreconst [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 5) {
+			break
+		}
+		v.reset(Op386MOVBstoreconst)
+		v.AuxInt = makeValAndOff(0, 4)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, Op386MOVLstoreconst, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 6
+	// result: (MOVWstoreconst [makeValAndOff(0,4)] destptr 		(MOVLstoreconst [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 6) {
+			break
+		}
+		v.reset(Op386MOVWstoreconst)
+		v.AuxInt = makeValAndOff(0, 4)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, Op386MOVLstoreconst, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 7
+	// result: (MOVLstoreconst [makeValAndOff(0,3)] destptr 		(MOVLstoreconst [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 7) {
+			break
+		}
+		v.reset(Op386MOVLstoreconst)
+		v.AuxInt = makeValAndOff(0, 3)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, Op386MOVLstoreconst, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size()%4 != 0 && SizeAndAlign(s).Size() > 4
+	// result: (Zero [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%4] (ADDLconst destptr [SizeAndAlign(s).Size()%4]) 		(MOVLstoreconst [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size()%4 != 0 && SizeAndAlign(s).Size() > 4) {
+			break
+		}
+		v.reset(OpZero)
+		v.AuxInt = SizeAndAlign(s).Size() - SizeAndAlign(s).Size()%4
+		v0 := b.NewValue0(v.Line, Op386ADDLconst, config.fe.TypeUInt32())
+		v0.AuxInt = SizeAndAlign(s).Size() % 4
+		v0.AddArg(destptr)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386MOVLstoreconst, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(destptr)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 8
+	// result: (MOVLstoreconst [makeValAndOff(0,4)] destptr 		(MOVLstoreconst [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 8) {
+			break
+		}
+		v.reset(Op386MOVLstoreconst)
+		v.AuxInt = makeValAndOff(0, 4)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, Op386MOVLstoreconst, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 12
+	// result: (MOVLstoreconst [makeValAndOff(0,8)] destptr 		(MOVLstoreconst [makeValAndOff(0,4)] destptr 			(MOVLstoreconst [0] destptr mem)))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 12) {
+			break
+		}
+		v.reset(Op386MOVLstoreconst)
+		v.AuxInt = makeValAndOff(0, 8)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, Op386MOVLstoreconst, TypeMem)
+		v0.AuxInt = makeValAndOff(0, 4)
+		v0.AddArg(destptr)
+		v1 := b.NewValue0(v.Line, Op386MOVLstoreconst, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(destptr)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 16
+	// result: (MOVLstoreconst [makeValAndOff(0,12)] destptr 		(MOVLstoreconst [makeValAndOff(0,8)] destptr 			(MOVLstoreconst [makeValAndOff(0,4)] destptr 				(MOVLstoreconst [0] destptr mem))))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 16) {
+			break
+		}
+		v.reset(Op386MOVLstoreconst)
+		v.AuxInt = makeValAndOff(0, 12)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, Op386MOVLstoreconst, TypeMem)
+		v0.AuxInt = makeValAndOff(0, 8)
+		v0.AddArg(destptr)
+		v1 := b.NewValue0(v.Line, Op386MOVLstoreconst, TypeMem)
+		v1.AuxInt = makeValAndOff(0, 4)
+		v1.AddArg(destptr)
+		v2 := b.NewValue0(v.Line, Op386MOVLstoreconst, TypeMem)
+		v2.AuxInt = 0
+		v2.AddArg(destptr)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() > 16   && SizeAndAlign(s).Size() <= 4*128   && SizeAndAlign(s).Size()%4 == 0   && !config.noDuffDevice
+	// result: (DUFFZERO [1*(128-SizeAndAlign(s).Size()/4)] destptr (MOVLconst [0]) mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() > 16 && SizeAndAlign(s).Size() <= 4*128 && SizeAndAlign(s).Size()%4 == 0 && !config.noDuffDevice) {
+			break
+		}
+		v.reset(Op386DUFFZERO)
+		v.AuxInt = 1 * (128 - SizeAndAlign(s).Size()/4)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, Op386MOVLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: (SizeAndAlign(s).Size() > 4*128 || (config.noDuffDevice && SizeAndAlign(s).Size() > 16))   && SizeAndAlign(s).Size()%4 == 0
+	// result: (REPSTOSL destptr (MOVLconst [SizeAndAlign(s).Size()/4]) (MOVLconst [0]) mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !((SizeAndAlign(s).Size() > 4*128 || (config.noDuffDevice && SizeAndAlign(s).Size() > 16)) && SizeAndAlign(s).Size()%4 == 0) {
+			break
+		}
+		v.reset(Op386REPSTOSL)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, Op386MOVLconst, config.fe.TypeUInt32())
+		v0.AuxInt = SizeAndAlign(s).Size() / 4
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, Op386MOVLconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValue386_OpZeroExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to32 x)
+	// cond:
+	// result: (MOVWLZX x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386MOVWLZX)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpZeroExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to16  x)
+	// cond:
+	// result: (MOVBLZX x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386MOVBLZX)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpZeroExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to32  x)
+	// cond:
+	// result: (MOVBLZX x)
+	for {
+		x := v.Args[0]
+		v.reset(Op386MOVBLZX)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValue386_OpZeromask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zeromask <t> x)
+	// cond:
+	// result: (XORLconst [-1] (SBBLcarrymask <t> (CMPLconst x [1])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(Op386XORLconst)
+		v.AuxInt = -1
+		v0 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
+		v1 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteBlock386(b *Block, config *Config) bool {
+	switch b.Kind {
+	case Block386EQ:
+		// match: (EQ (InvertFlags cmp) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386EQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case Block386GE:
+		// match: (GE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (LE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386LE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	case Block386GT:
+		// match: (GT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (LT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386LT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockIf:
+		// match: (If (SETL  cmp) yes no)
+		// cond:
+		// result: (LT  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETL {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386LT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETLE cmp) yes no)
+		// cond:
+		// result: (LE  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETLE {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386LE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETG  cmp) yes no)
+		// cond:
+		// result: (GT  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETG {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386GT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETGE cmp) yes no)
+		// cond:
+		// result: (GE  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETGE {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386GE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETEQ cmp) yes no)
+		// cond:
+		// result: (EQ  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETEQ {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386EQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETNE cmp) yes no)
+		// cond:
+		// result: (NE  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETNE {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386NE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETB  cmp) yes no)
+		// cond:
+		// result: (ULT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETB {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386ULT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETBE cmp) yes no)
+		// cond:
+		// result: (ULE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETBE {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386ULE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETA  cmp) yes no)
+		// cond:
+		// result: (UGT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETA {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386UGT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETAE cmp) yes no)
+		// cond:
+		// result: (UGE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETAE {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386UGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETGF  cmp) yes no)
+		// cond:
+		// result: (UGT  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETGF {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386UGT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETGEF cmp) yes no)
+		// cond:
+		// result: (UGE  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETGEF {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386UGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETEQF cmp) yes no)
+		// cond:
+		// result: (EQF  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETEQF {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386EQF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (SETNEF cmp) yes no)
+		// cond:
+		// result: (NEF  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386SETNEF {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386NEF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If cond yes no)
+		// cond:
+		// result: (NE (TESTB cond cond) yes no)
+		for {
+			v := b.Control
+			_ = v
+			cond := b.Control
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386NE
+			v0 := b.NewValue0(v.Line, Op386TESTB, TypeFlags)
+			v0.AddArg(cond)
+			v0.AddArg(cond)
+			b.SetControl(v0)
+			_ = yes
+			_ = no
+			return true
+		}
+	case Block386LE:
+		// match: (LE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (GE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386GE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case Block386LT:
+		// match: (LT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (GT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386GT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case Block386NE:
+		// match: (NE (TESTB (SETL  cmp) (SETL  cmp)) yes no)
+		// cond:
+		// result: (LT  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETL {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETL {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386LT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETLE cmp) (SETLE cmp)) yes no)
+		// cond:
+		// result: (LE  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETLE {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETLE {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386LE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETG  cmp) (SETG  cmp)) yes no)
+		// cond:
+		// result: (GT  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETG {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETG {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386GT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETGE cmp) (SETGE cmp)) yes no)
+		// cond:
+		// result: (GE  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETGE {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETGE {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386GE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETEQ cmp) (SETEQ cmp)) yes no)
+		// cond:
+		// result: (EQ  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETEQ {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETEQ {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386EQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETNE cmp) (SETNE cmp)) yes no)
+		// cond:
+		// result: (NE  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETNE {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETNE {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386NE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETB  cmp) (SETB  cmp)) yes no)
+		// cond:
+		// result: (ULT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETB {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETB {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386ULT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETBE cmp) (SETBE cmp)) yes no)
+		// cond:
+		// result: (ULE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETBE {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETBE {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386ULE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETA  cmp) (SETA  cmp)) yes no)
+		// cond:
+		// result: (UGT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETA {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETA {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386UGT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETAE cmp) (SETAE cmp)) yes no)
+		// cond:
+		// result: (UGE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETAE {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETAE {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386UGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETGF  cmp) (SETGF  cmp)) yes no)
+		// cond:
+		// result: (UGT  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETGF {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETGF {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386UGT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETGEF cmp) (SETGEF cmp)) yes no)
+		// cond:
+		// result: (UGE  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETGEF {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETGEF {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386UGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETEQF cmp) (SETEQF cmp)) yes no)
+		// cond:
+		// result: (EQF  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETEQF {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETEQF {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386EQF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (TESTB (SETNEF cmp) (SETNEF cmp)) yes no)
+		// cond:
+		// result: (NEF  cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386TESTB {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != Op386SETNEF {
+				break
+			}
+			cmp := v_0.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != Op386SETNEF {
+				break
+			}
+			if cmp != v_1.Args[0] {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386NEF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386NE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (NE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	case Block386UGE:
+		// match: (UGE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (ULE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386ULE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	case Block386UGT:
+		// match: (UGT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (ULT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386ULT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	case Block386ULE:
+		// match: (ULE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (UGE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386UGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case Block386ULT:
+		// match: (ULT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (UGT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = Block386UGT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != Op386FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go
index a2b9e15..1257ec6 100644
--- a/src/cmd/compile/internal/ssa/rewriteAMD64.go
+++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go
@@ -24,6 +24,262 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpAMD64ANDQ(v, config)
 	case OpAMD64ANDQconst:
 		return rewriteValueAMD64_OpAMD64ANDQconst(v, config)
+	case OpAMD64CMPB:
+		return rewriteValueAMD64_OpAMD64CMPB(v, config)
+	case OpAMD64CMPBconst:
+		return rewriteValueAMD64_OpAMD64CMPBconst(v, config)
+	case OpAMD64CMPL:
+		return rewriteValueAMD64_OpAMD64CMPL(v, config)
+	case OpAMD64CMPLconst:
+		return rewriteValueAMD64_OpAMD64CMPLconst(v, config)
+	case OpAMD64CMPQ:
+		return rewriteValueAMD64_OpAMD64CMPQ(v, config)
+	case OpAMD64CMPQconst:
+		return rewriteValueAMD64_OpAMD64CMPQconst(v, config)
+	case OpAMD64CMPW:
+		return rewriteValueAMD64_OpAMD64CMPW(v, config)
+	case OpAMD64CMPWconst:
+		return rewriteValueAMD64_OpAMD64CMPWconst(v, config)
+	case OpAMD64CMPXCHGLlock:
+		return rewriteValueAMD64_OpAMD64CMPXCHGLlock(v, config)
+	case OpAMD64CMPXCHGQlock:
+		return rewriteValueAMD64_OpAMD64CMPXCHGQlock(v, config)
+	case OpAMD64LEAL:
+		return rewriteValueAMD64_OpAMD64LEAL(v, config)
+	case OpAMD64LEAQ:
+		return rewriteValueAMD64_OpAMD64LEAQ(v, config)
+	case OpAMD64LEAQ1:
+		return rewriteValueAMD64_OpAMD64LEAQ1(v, config)
+	case OpAMD64LEAQ2:
+		return rewriteValueAMD64_OpAMD64LEAQ2(v, config)
+	case OpAMD64LEAQ4:
+		return rewriteValueAMD64_OpAMD64LEAQ4(v, config)
+	case OpAMD64LEAQ8:
+		return rewriteValueAMD64_OpAMD64LEAQ8(v, config)
+	case OpAMD64MOVBQSX:
+		return rewriteValueAMD64_OpAMD64MOVBQSX(v, config)
+	case OpAMD64MOVBQSXload:
+		return rewriteValueAMD64_OpAMD64MOVBQSXload(v, config)
+	case OpAMD64MOVBQZX:
+		return rewriteValueAMD64_OpAMD64MOVBQZX(v, config)
+	case OpAMD64MOVBload:
+		return rewriteValueAMD64_OpAMD64MOVBload(v, config)
+	case OpAMD64MOVBloadidx1:
+		return rewriteValueAMD64_OpAMD64MOVBloadidx1(v, config)
+	case OpAMD64MOVBstore:
+		return rewriteValueAMD64_OpAMD64MOVBstore(v, config)
+	case OpAMD64MOVBstoreconst:
+		return rewriteValueAMD64_OpAMD64MOVBstoreconst(v, config)
+	case OpAMD64MOVBstoreconstidx1:
+		return rewriteValueAMD64_OpAMD64MOVBstoreconstidx1(v, config)
+	case OpAMD64MOVBstoreidx1:
+		return rewriteValueAMD64_OpAMD64MOVBstoreidx1(v, config)
+	case OpAMD64MOVLQSX:
+		return rewriteValueAMD64_OpAMD64MOVLQSX(v, config)
+	case OpAMD64MOVLQSXload:
+		return rewriteValueAMD64_OpAMD64MOVLQSXload(v, config)
+	case OpAMD64MOVLQZX:
+		return rewriteValueAMD64_OpAMD64MOVLQZX(v, config)
+	case OpAMD64MOVLatomicload:
+		return rewriteValueAMD64_OpAMD64MOVLatomicload(v, config)
+	case OpAMD64MOVLload:
+		return rewriteValueAMD64_OpAMD64MOVLload(v, config)
+	case OpAMD64MOVLloadidx1:
+		return rewriteValueAMD64_OpAMD64MOVLloadidx1(v, config)
+	case OpAMD64MOVLloadidx4:
+		return rewriteValueAMD64_OpAMD64MOVLloadidx4(v, config)
+	case OpAMD64MOVLstore:
+		return rewriteValueAMD64_OpAMD64MOVLstore(v, config)
+	case OpAMD64MOVLstoreconst:
+		return rewriteValueAMD64_OpAMD64MOVLstoreconst(v, config)
+	case OpAMD64MOVLstoreconstidx1:
+		return rewriteValueAMD64_OpAMD64MOVLstoreconstidx1(v, config)
+	case OpAMD64MOVLstoreconstidx4:
+		return rewriteValueAMD64_OpAMD64MOVLstoreconstidx4(v, config)
+	case OpAMD64MOVLstoreidx1:
+		return rewriteValueAMD64_OpAMD64MOVLstoreidx1(v, config)
+	case OpAMD64MOVLstoreidx4:
+		return rewriteValueAMD64_OpAMD64MOVLstoreidx4(v, config)
+	case OpAMD64MOVOload:
+		return rewriteValueAMD64_OpAMD64MOVOload(v, config)
+	case OpAMD64MOVOstore:
+		return rewriteValueAMD64_OpAMD64MOVOstore(v, config)
+	case OpAMD64MOVQatomicload:
+		return rewriteValueAMD64_OpAMD64MOVQatomicload(v, config)
+	case OpAMD64MOVQload:
+		return rewriteValueAMD64_OpAMD64MOVQload(v, config)
+	case OpAMD64MOVQloadidx1:
+		return rewriteValueAMD64_OpAMD64MOVQloadidx1(v, config)
+	case OpAMD64MOVQloadidx8:
+		return rewriteValueAMD64_OpAMD64MOVQloadidx8(v, config)
+	case OpAMD64MOVQstore:
+		return rewriteValueAMD64_OpAMD64MOVQstore(v, config)
+	case OpAMD64MOVQstoreconst:
+		return rewriteValueAMD64_OpAMD64MOVQstoreconst(v, config)
+	case OpAMD64MOVQstoreconstidx1:
+		return rewriteValueAMD64_OpAMD64MOVQstoreconstidx1(v, config)
+	case OpAMD64MOVQstoreconstidx8:
+		return rewriteValueAMD64_OpAMD64MOVQstoreconstidx8(v, config)
+	case OpAMD64MOVQstoreidx1:
+		return rewriteValueAMD64_OpAMD64MOVQstoreidx1(v, config)
+	case OpAMD64MOVQstoreidx8:
+		return rewriteValueAMD64_OpAMD64MOVQstoreidx8(v, config)
+	case OpAMD64MOVSDload:
+		return rewriteValueAMD64_OpAMD64MOVSDload(v, config)
+	case OpAMD64MOVSDloadidx1:
+		return rewriteValueAMD64_OpAMD64MOVSDloadidx1(v, config)
+	case OpAMD64MOVSDloadidx8:
+		return rewriteValueAMD64_OpAMD64MOVSDloadidx8(v, config)
+	case OpAMD64MOVSDstore:
+		return rewriteValueAMD64_OpAMD64MOVSDstore(v, config)
+	case OpAMD64MOVSDstoreidx1:
+		return rewriteValueAMD64_OpAMD64MOVSDstoreidx1(v, config)
+	case OpAMD64MOVSDstoreidx8:
+		return rewriteValueAMD64_OpAMD64MOVSDstoreidx8(v, config)
+	case OpAMD64MOVSSload:
+		return rewriteValueAMD64_OpAMD64MOVSSload(v, config)
+	case OpAMD64MOVSSloadidx1:
+		return rewriteValueAMD64_OpAMD64MOVSSloadidx1(v, config)
+	case OpAMD64MOVSSloadidx4:
+		return rewriteValueAMD64_OpAMD64MOVSSloadidx4(v, config)
+	case OpAMD64MOVSSstore:
+		return rewriteValueAMD64_OpAMD64MOVSSstore(v, config)
+	case OpAMD64MOVSSstoreidx1:
+		return rewriteValueAMD64_OpAMD64MOVSSstoreidx1(v, config)
+	case OpAMD64MOVSSstoreidx4:
+		return rewriteValueAMD64_OpAMD64MOVSSstoreidx4(v, config)
+	case OpAMD64MOVWQSX:
+		return rewriteValueAMD64_OpAMD64MOVWQSX(v, config)
+	case OpAMD64MOVWQSXload:
+		return rewriteValueAMD64_OpAMD64MOVWQSXload(v, config)
+	case OpAMD64MOVWQZX:
+		return rewriteValueAMD64_OpAMD64MOVWQZX(v, config)
+	case OpAMD64MOVWload:
+		return rewriteValueAMD64_OpAMD64MOVWload(v, config)
+	case OpAMD64MOVWloadidx1:
+		return rewriteValueAMD64_OpAMD64MOVWloadidx1(v, config)
+	case OpAMD64MOVWloadidx2:
+		return rewriteValueAMD64_OpAMD64MOVWloadidx2(v, config)
+	case OpAMD64MOVWstore:
+		return rewriteValueAMD64_OpAMD64MOVWstore(v, config)
+	case OpAMD64MOVWstoreconst:
+		return rewriteValueAMD64_OpAMD64MOVWstoreconst(v, config)
+	case OpAMD64MOVWstoreconstidx1:
+		return rewriteValueAMD64_OpAMD64MOVWstoreconstidx1(v, config)
+	case OpAMD64MOVWstoreconstidx2:
+		return rewriteValueAMD64_OpAMD64MOVWstoreconstidx2(v, config)
+	case OpAMD64MOVWstoreidx1:
+		return rewriteValueAMD64_OpAMD64MOVWstoreidx1(v, config)
+	case OpAMD64MOVWstoreidx2:
+		return rewriteValueAMD64_OpAMD64MOVWstoreidx2(v, config)
+	case OpAMD64MULL:
+		return rewriteValueAMD64_OpAMD64MULL(v, config)
+	case OpAMD64MULLconst:
+		return rewriteValueAMD64_OpAMD64MULLconst(v, config)
+	case OpAMD64MULQ:
+		return rewriteValueAMD64_OpAMD64MULQ(v, config)
+	case OpAMD64MULQconst:
+		return rewriteValueAMD64_OpAMD64MULQconst(v, config)
+	case OpAMD64NEGL:
+		return rewriteValueAMD64_OpAMD64NEGL(v, config)
+	case OpAMD64NEGQ:
+		return rewriteValueAMD64_OpAMD64NEGQ(v, config)
+	case OpAMD64NOTL:
+		return rewriteValueAMD64_OpAMD64NOTL(v, config)
+	case OpAMD64NOTQ:
+		return rewriteValueAMD64_OpAMD64NOTQ(v, config)
+	case OpAMD64ORL:
+		return rewriteValueAMD64_OpAMD64ORL(v, config)
+	case OpAMD64ORLconst:
+		return rewriteValueAMD64_OpAMD64ORLconst(v, config)
+	case OpAMD64ORQ:
+		return rewriteValueAMD64_OpAMD64ORQ(v, config)
+	case OpAMD64ORQconst:
+		return rewriteValueAMD64_OpAMD64ORQconst(v, config)
+	case OpAMD64ROLBconst:
+		return rewriteValueAMD64_OpAMD64ROLBconst(v, config)
+	case OpAMD64ROLLconst:
+		return rewriteValueAMD64_OpAMD64ROLLconst(v, config)
+	case OpAMD64ROLQconst:
+		return rewriteValueAMD64_OpAMD64ROLQconst(v, config)
+	case OpAMD64ROLWconst:
+		return rewriteValueAMD64_OpAMD64ROLWconst(v, config)
+	case OpAMD64SARB:
+		return rewriteValueAMD64_OpAMD64SARB(v, config)
+	case OpAMD64SARBconst:
+		return rewriteValueAMD64_OpAMD64SARBconst(v, config)
+	case OpAMD64SARL:
+		return rewriteValueAMD64_OpAMD64SARL(v, config)
+	case OpAMD64SARLconst:
+		return rewriteValueAMD64_OpAMD64SARLconst(v, config)
+	case OpAMD64SARQ:
+		return rewriteValueAMD64_OpAMD64SARQ(v, config)
+	case OpAMD64SARQconst:
+		return rewriteValueAMD64_OpAMD64SARQconst(v, config)
+	case OpAMD64SARW:
+		return rewriteValueAMD64_OpAMD64SARW(v, config)
+	case OpAMD64SARWconst:
+		return rewriteValueAMD64_OpAMD64SARWconst(v, config)
+	case OpAMD64SBBLcarrymask:
+		return rewriteValueAMD64_OpAMD64SBBLcarrymask(v, config)
+	case OpAMD64SBBQcarrymask:
+		return rewriteValueAMD64_OpAMD64SBBQcarrymask(v, config)
+	case OpAMD64SETA:
+		return rewriteValueAMD64_OpAMD64SETA(v, config)
+	case OpAMD64SETAE:
+		return rewriteValueAMD64_OpAMD64SETAE(v, config)
+	case OpAMD64SETB:
+		return rewriteValueAMD64_OpAMD64SETB(v, config)
+	case OpAMD64SETBE:
+		return rewriteValueAMD64_OpAMD64SETBE(v, config)
+	case OpAMD64SETEQ:
+		return rewriteValueAMD64_OpAMD64SETEQ(v, config)
+	case OpAMD64SETG:
+		return rewriteValueAMD64_OpAMD64SETG(v, config)
+	case OpAMD64SETGE:
+		return rewriteValueAMD64_OpAMD64SETGE(v, config)
+	case OpAMD64SETL:
+		return rewriteValueAMD64_OpAMD64SETL(v, config)
+	case OpAMD64SETLE:
+		return rewriteValueAMD64_OpAMD64SETLE(v, config)
+	case OpAMD64SETNE:
+		return rewriteValueAMD64_OpAMD64SETNE(v, config)
+	case OpAMD64SHLL:
+		return rewriteValueAMD64_OpAMD64SHLL(v, config)
+	case OpAMD64SHLQ:
+		return rewriteValueAMD64_OpAMD64SHLQ(v, config)
+	case OpAMD64SHRB:
+		return rewriteValueAMD64_OpAMD64SHRB(v, config)
+	case OpAMD64SHRL:
+		return rewriteValueAMD64_OpAMD64SHRL(v, config)
+	case OpAMD64SHRQ:
+		return rewriteValueAMD64_OpAMD64SHRQ(v, config)
+	case OpAMD64SHRW:
+		return rewriteValueAMD64_OpAMD64SHRW(v, config)
+	case OpAMD64SUBL:
+		return rewriteValueAMD64_OpAMD64SUBL(v, config)
+	case OpAMD64SUBLconst:
+		return rewriteValueAMD64_OpAMD64SUBLconst(v, config)
+	case OpAMD64SUBQ:
+		return rewriteValueAMD64_OpAMD64SUBQ(v, config)
+	case OpAMD64SUBQconst:
+		return rewriteValueAMD64_OpAMD64SUBQconst(v, config)
+	case OpAMD64XADDLlock:
+		return rewriteValueAMD64_OpAMD64XADDLlock(v, config)
+	case OpAMD64XADDQlock:
+		return rewriteValueAMD64_OpAMD64XADDQlock(v, config)
+	case OpAMD64XCHGL:
+		return rewriteValueAMD64_OpAMD64XCHGL(v, config)
+	case OpAMD64XCHGQ:
+		return rewriteValueAMD64_OpAMD64XCHGQ(v, config)
+	case OpAMD64XORL:
+		return rewriteValueAMD64_OpAMD64XORL(v, config)
+	case OpAMD64XORLconst:
+		return rewriteValueAMD64_OpAMD64XORLconst(v, config)
+	case OpAMD64XORQ:
+		return rewriteValueAMD64_OpAMD64XORQ(v, config)
+	case OpAMD64XORQconst:
+		return rewriteValueAMD64_OpAMD64XORQconst(v, config)
 	case OpAdd16:
 		return rewriteValueAMD64_OpAdd16(v, config)
 	case OpAdd32:
@@ -50,34 +306,40 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpAnd8(v, config)
 	case OpAndB:
 		return rewriteValueAMD64_OpAndB(v, config)
+	case OpAtomicAdd32:
+		return rewriteValueAMD64_OpAtomicAdd32(v, config)
+	case OpAtomicAdd64:
+		return rewriteValueAMD64_OpAtomicAdd64(v, config)
+	case OpAtomicAnd8:
+		return rewriteValueAMD64_OpAtomicAnd8(v, config)
+	case OpAtomicCompareAndSwap32:
+		return rewriteValueAMD64_OpAtomicCompareAndSwap32(v, config)
+	case OpAtomicCompareAndSwap64:
+		return rewriteValueAMD64_OpAtomicCompareAndSwap64(v, config)
+	case OpAtomicExchange32:
+		return rewriteValueAMD64_OpAtomicExchange32(v, config)
+	case OpAtomicExchange64:
+		return rewriteValueAMD64_OpAtomicExchange64(v, config)
+	case OpAtomicLoad32:
+		return rewriteValueAMD64_OpAtomicLoad32(v, config)
+	case OpAtomicLoad64:
+		return rewriteValueAMD64_OpAtomicLoad64(v, config)
+	case OpAtomicLoadPtr:
+		return rewriteValueAMD64_OpAtomicLoadPtr(v, config)
+	case OpAtomicOr8:
+		return rewriteValueAMD64_OpAtomicOr8(v, config)
+	case OpAtomicStore32:
+		return rewriteValueAMD64_OpAtomicStore32(v, config)
+	case OpAtomicStore64:
+		return rewriteValueAMD64_OpAtomicStore64(v, config)
+	case OpAtomicStorePtrNoWB:
+		return rewriteValueAMD64_OpAtomicStorePtrNoWB(v, config)
 	case OpAvg64u:
 		return rewriteValueAMD64_OpAvg64u(v, config)
 	case OpBswap32:
 		return rewriteValueAMD64_OpBswap32(v, config)
 	case OpBswap64:
 		return rewriteValueAMD64_OpBswap64(v, config)
-	case OpAMD64CMOVLEQconst:
-		return rewriteValueAMD64_OpAMD64CMOVLEQconst(v, config)
-	case OpAMD64CMOVQEQconst:
-		return rewriteValueAMD64_OpAMD64CMOVQEQconst(v, config)
-	case OpAMD64CMOVWEQconst:
-		return rewriteValueAMD64_OpAMD64CMOVWEQconst(v, config)
-	case OpAMD64CMPB:
-		return rewriteValueAMD64_OpAMD64CMPB(v, config)
-	case OpAMD64CMPBconst:
-		return rewriteValueAMD64_OpAMD64CMPBconst(v, config)
-	case OpAMD64CMPL:
-		return rewriteValueAMD64_OpAMD64CMPL(v, config)
-	case OpAMD64CMPLconst:
-		return rewriteValueAMD64_OpAMD64CMPLconst(v, config)
-	case OpAMD64CMPQ:
-		return rewriteValueAMD64_OpAMD64CMPQ(v, config)
-	case OpAMD64CMPQconst:
-		return rewriteValueAMD64_OpAMD64CMPQconst(v, config)
-	case OpAMD64CMPW:
-		return rewriteValueAMD64_OpAMD64CMPW(v, config)
-	case OpAMD64CMPWconst:
-		return rewriteValueAMD64_OpAMD64CMPWconst(v, config)
 	case OpClosureCall:
 		return rewriteValueAMD64_OpClosureCall(v, config)
 	case OpCom16:
@@ -106,8 +368,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpConstNil(v, config)
 	case OpConvert:
 		return rewriteValueAMD64_OpConvert(v, config)
-	case OpCtz16:
-		return rewriteValueAMD64_OpCtz16(v, config)
 	case OpCtz32:
 		return rewriteValueAMD64_OpCtz32(v, config)
 	case OpCtz64:
@@ -134,6 +394,8 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpCvt64to64F(v, config)
 	case OpDeferCall:
 		return rewriteValueAMD64_OpDeferCall(v, config)
+	case OpDiv128u:
+		return rewriteValueAMD64_OpDiv128u(v, config)
 	case OpDiv16:
 		return rewriteValueAMD64_OpDiv16(v, config)
 	case OpDiv16u:
@@ -232,8 +494,8 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpHmul8(v, config)
 	case OpHmul8u:
 		return rewriteValueAMD64_OpHmul8u(v, config)
-	case OpITab:
-		return rewriteValueAMD64_OpITab(v, config)
+	case OpInt64Hi:
+		return rewriteValueAMD64_OpInt64Hi(v, config)
 	case OpInterCall:
 		return rewriteValueAMD64_OpInterCall(v, config)
 	case OpIsInBounds:
@@ -242,16 +504,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpIsNonNil(v, config)
 	case OpIsSliceInBounds:
 		return rewriteValueAMD64_OpIsSliceInBounds(v, config)
-	case OpAMD64LEAQ:
-		return rewriteValueAMD64_OpAMD64LEAQ(v, config)
-	case OpAMD64LEAQ1:
-		return rewriteValueAMD64_OpAMD64LEAQ1(v, config)
-	case OpAMD64LEAQ2:
-		return rewriteValueAMD64_OpAMD64LEAQ2(v, config)
-	case OpAMD64LEAQ4:
-		return rewriteValueAMD64_OpAMD64LEAQ4(v, config)
-	case OpAMD64LEAQ8:
-		return rewriteValueAMD64_OpAMD64LEAQ8(v, config)
 	case OpLeq16:
 		return rewriteValueAMD64_OpLeq16(v, config)
 	case OpLeq16U:
@@ -334,126 +586,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpLsh8x64(v, config)
 	case OpLsh8x8:
 		return rewriteValueAMD64_OpLsh8x8(v, config)
-	case OpAMD64MOVBQSX:
-		return rewriteValueAMD64_OpAMD64MOVBQSX(v, config)
-	case OpAMD64MOVBQSXload:
-		return rewriteValueAMD64_OpAMD64MOVBQSXload(v, config)
-	case OpAMD64MOVBQZX:
-		return rewriteValueAMD64_OpAMD64MOVBQZX(v, config)
-	case OpAMD64MOVBload:
-		return rewriteValueAMD64_OpAMD64MOVBload(v, config)
-	case OpAMD64MOVBloadidx1:
-		return rewriteValueAMD64_OpAMD64MOVBloadidx1(v, config)
-	case OpAMD64MOVBstore:
-		return rewriteValueAMD64_OpAMD64MOVBstore(v, config)
-	case OpAMD64MOVBstoreconst:
-		return rewriteValueAMD64_OpAMD64MOVBstoreconst(v, config)
-	case OpAMD64MOVBstoreconstidx1:
-		return rewriteValueAMD64_OpAMD64MOVBstoreconstidx1(v, config)
-	case OpAMD64MOVBstoreidx1:
-		return rewriteValueAMD64_OpAMD64MOVBstoreidx1(v, config)
-	case OpAMD64MOVLQSX:
-		return rewriteValueAMD64_OpAMD64MOVLQSX(v, config)
-	case OpAMD64MOVLQSXload:
-		return rewriteValueAMD64_OpAMD64MOVLQSXload(v, config)
-	case OpAMD64MOVLQZX:
-		return rewriteValueAMD64_OpAMD64MOVLQZX(v, config)
-	case OpAMD64MOVLload:
-		return rewriteValueAMD64_OpAMD64MOVLload(v, config)
-	case OpAMD64MOVLloadidx1:
-		return rewriteValueAMD64_OpAMD64MOVLloadidx1(v, config)
-	case OpAMD64MOVLloadidx4:
-		return rewriteValueAMD64_OpAMD64MOVLloadidx4(v, config)
-	case OpAMD64MOVLstore:
-		return rewriteValueAMD64_OpAMD64MOVLstore(v, config)
-	case OpAMD64MOVLstoreconst:
-		return rewriteValueAMD64_OpAMD64MOVLstoreconst(v, config)
-	case OpAMD64MOVLstoreconstidx1:
-		return rewriteValueAMD64_OpAMD64MOVLstoreconstidx1(v, config)
-	case OpAMD64MOVLstoreconstidx4:
-		return rewriteValueAMD64_OpAMD64MOVLstoreconstidx4(v, config)
-	case OpAMD64MOVLstoreidx1:
-		return rewriteValueAMD64_OpAMD64MOVLstoreidx1(v, config)
-	case OpAMD64MOVLstoreidx4:
-		return rewriteValueAMD64_OpAMD64MOVLstoreidx4(v, config)
-	case OpAMD64MOVOload:
-		return rewriteValueAMD64_OpAMD64MOVOload(v, config)
-	case OpAMD64MOVOstore:
-		return rewriteValueAMD64_OpAMD64MOVOstore(v, config)
-	case OpAMD64MOVQload:
-		return rewriteValueAMD64_OpAMD64MOVQload(v, config)
-	case OpAMD64MOVQloadidx1:
-		return rewriteValueAMD64_OpAMD64MOVQloadidx1(v, config)
-	case OpAMD64MOVQloadidx8:
-		return rewriteValueAMD64_OpAMD64MOVQloadidx8(v, config)
-	case OpAMD64MOVQstore:
-		return rewriteValueAMD64_OpAMD64MOVQstore(v, config)
-	case OpAMD64MOVQstoreconst:
-		return rewriteValueAMD64_OpAMD64MOVQstoreconst(v, config)
-	case OpAMD64MOVQstoreconstidx1:
-		return rewriteValueAMD64_OpAMD64MOVQstoreconstidx1(v, config)
-	case OpAMD64MOVQstoreconstidx8:
-		return rewriteValueAMD64_OpAMD64MOVQstoreconstidx8(v, config)
-	case OpAMD64MOVQstoreidx1:
-		return rewriteValueAMD64_OpAMD64MOVQstoreidx1(v, config)
-	case OpAMD64MOVQstoreidx8:
-		return rewriteValueAMD64_OpAMD64MOVQstoreidx8(v, config)
-	case OpAMD64MOVSDload:
-		return rewriteValueAMD64_OpAMD64MOVSDload(v, config)
-	case OpAMD64MOVSDloadidx1:
-		return rewriteValueAMD64_OpAMD64MOVSDloadidx1(v, config)
-	case OpAMD64MOVSDloadidx8:
-		return rewriteValueAMD64_OpAMD64MOVSDloadidx8(v, config)
-	case OpAMD64MOVSDstore:
-		return rewriteValueAMD64_OpAMD64MOVSDstore(v, config)
-	case OpAMD64MOVSDstoreidx1:
-		return rewriteValueAMD64_OpAMD64MOVSDstoreidx1(v, config)
-	case OpAMD64MOVSDstoreidx8:
-		return rewriteValueAMD64_OpAMD64MOVSDstoreidx8(v, config)
-	case OpAMD64MOVSSload:
-		return rewriteValueAMD64_OpAMD64MOVSSload(v, config)
-	case OpAMD64MOVSSloadidx1:
-		return rewriteValueAMD64_OpAMD64MOVSSloadidx1(v, config)
-	case OpAMD64MOVSSloadidx4:
-		return rewriteValueAMD64_OpAMD64MOVSSloadidx4(v, config)
-	case OpAMD64MOVSSstore:
-		return rewriteValueAMD64_OpAMD64MOVSSstore(v, config)
-	case OpAMD64MOVSSstoreidx1:
-		return rewriteValueAMD64_OpAMD64MOVSSstoreidx1(v, config)
-	case OpAMD64MOVSSstoreidx4:
-		return rewriteValueAMD64_OpAMD64MOVSSstoreidx4(v, config)
-	case OpAMD64MOVWQSX:
-		return rewriteValueAMD64_OpAMD64MOVWQSX(v, config)
-	case OpAMD64MOVWQSXload:
-		return rewriteValueAMD64_OpAMD64MOVWQSXload(v, config)
-	case OpAMD64MOVWQZX:
-		return rewriteValueAMD64_OpAMD64MOVWQZX(v, config)
-	case OpAMD64MOVWload:
-		return rewriteValueAMD64_OpAMD64MOVWload(v, config)
-	case OpAMD64MOVWloadidx1:
-		return rewriteValueAMD64_OpAMD64MOVWloadidx1(v, config)
-	case OpAMD64MOVWloadidx2:
-		return rewriteValueAMD64_OpAMD64MOVWloadidx2(v, config)
-	case OpAMD64MOVWstore:
-		return rewriteValueAMD64_OpAMD64MOVWstore(v, config)
-	case OpAMD64MOVWstoreconst:
-		return rewriteValueAMD64_OpAMD64MOVWstoreconst(v, config)
-	case OpAMD64MOVWstoreconstidx1:
-		return rewriteValueAMD64_OpAMD64MOVWstoreconstidx1(v, config)
-	case OpAMD64MOVWstoreconstidx2:
-		return rewriteValueAMD64_OpAMD64MOVWstoreconstidx2(v, config)
-	case OpAMD64MOVWstoreidx1:
-		return rewriteValueAMD64_OpAMD64MOVWstoreidx1(v, config)
-	case OpAMD64MOVWstoreidx2:
-		return rewriteValueAMD64_OpAMD64MOVWstoreidx2(v, config)
-	case OpAMD64MULL:
-		return rewriteValueAMD64_OpAMD64MULL(v, config)
-	case OpAMD64MULLconst:
-		return rewriteValueAMD64_OpAMD64MULLconst(v, config)
-	case OpAMD64MULQ:
-		return rewriteValueAMD64_OpAMD64MULQ(v, config)
-	case OpAMD64MULQconst:
-		return rewriteValueAMD64_OpAMD64MULQconst(v, config)
 	case OpMod16:
 		return rewriteValueAMD64_OpMod16(v, config)
 	case OpMod16u:
@@ -482,16 +614,10 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpMul64(v, config)
 	case OpMul64F:
 		return rewriteValueAMD64_OpMul64F(v, config)
+	case OpMul64uhilo:
+		return rewriteValueAMD64_OpMul64uhilo(v, config)
 	case OpMul8:
 		return rewriteValueAMD64_OpMul8(v, config)
-	case OpAMD64NEGL:
-		return rewriteValueAMD64_OpAMD64NEGL(v, config)
-	case OpAMD64NEGQ:
-		return rewriteValueAMD64_OpAMD64NEGQ(v, config)
-	case OpAMD64NOTL:
-		return rewriteValueAMD64_OpAMD64NOTL(v, config)
-	case OpAMD64NOTQ:
-		return rewriteValueAMD64_OpAMD64NOTQ(v, config)
 	case OpNeg16:
 		return rewriteValueAMD64_OpNeg16(v, config)
 	case OpNeg32:
@@ -524,14 +650,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpNilCheck(v, config)
 	case OpNot:
 		return rewriteValueAMD64_OpNot(v, config)
-	case OpAMD64ORL:
-		return rewriteValueAMD64_OpAMD64ORL(v, config)
-	case OpAMD64ORLconst:
-		return rewriteValueAMD64_OpAMD64ORLconst(v, config)
-	case OpAMD64ORQ:
-		return rewriteValueAMD64_OpAMD64ORQ(v, config)
-	case OpAMD64ORQconst:
-		return rewriteValueAMD64_OpAMD64ORQconst(v, config)
 	case OpOffPtr:
 		return rewriteValueAMD64_OpOffPtr(v, config)
 	case OpOr16:
@@ -608,66 +726,10 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpRsh8x64(v, config)
 	case OpRsh8x8:
 		return rewriteValueAMD64_OpRsh8x8(v, config)
-	case OpAMD64SARB:
-		return rewriteValueAMD64_OpAMD64SARB(v, config)
-	case OpAMD64SARBconst:
-		return rewriteValueAMD64_OpAMD64SARBconst(v, config)
-	case OpAMD64SARL:
-		return rewriteValueAMD64_OpAMD64SARL(v, config)
-	case OpAMD64SARLconst:
-		return rewriteValueAMD64_OpAMD64SARLconst(v, config)
-	case OpAMD64SARQ:
-		return rewriteValueAMD64_OpAMD64SARQ(v, config)
-	case OpAMD64SARQconst:
-		return rewriteValueAMD64_OpAMD64SARQconst(v, config)
-	case OpAMD64SARW:
-		return rewriteValueAMD64_OpAMD64SARW(v, config)
-	case OpAMD64SARWconst:
-		return rewriteValueAMD64_OpAMD64SARWconst(v, config)
-	case OpAMD64SBBLcarrymask:
-		return rewriteValueAMD64_OpAMD64SBBLcarrymask(v, config)
-	case OpAMD64SBBQcarrymask:
-		return rewriteValueAMD64_OpAMD64SBBQcarrymask(v, config)
-	case OpAMD64SETA:
-		return rewriteValueAMD64_OpAMD64SETA(v, config)
-	case OpAMD64SETAE:
-		return rewriteValueAMD64_OpAMD64SETAE(v, config)
-	case OpAMD64SETB:
-		return rewriteValueAMD64_OpAMD64SETB(v, config)
-	case OpAMD64SETBE:
-		return rewriteValueAMD64_OpAMD64SETBE(v, config)
-	case OpAMD64SETEQ:
-		return rewriteValueAMD64_OpAMD64SETEQ(v, config)
-	case OpAMD64SETG:
-		return rewriteValueAMD64_OpAMD64SETG(v, config)
-	case OpAMD64SETGE:
-		return rewriteValueAMD64_OpAMD64SETGE(v, config)
-	case OpAMD64SETL:
-		return rewriteValueAMD64_OpAMD64SETL(v, config)
-	case OpAMD64SETLE:
-		return rewriteValueAMD64_OpAMD64SETLE(v, config)
-	case OpAMD64SETNE:
-		return rewriteValueAMD64_OpAMD64SETNE(v, config)
-	case OpAMD64SHLL:
-		return rewriteValueAMD64_OpAMD64SHLL(v, config)
-	case OpAMD64SHLQ:
-		return rewriteValueAMD64_OpAMD64SHLQ(v, config)
-	case OpAMD64SHRB:
-		return rewriteValueAMD64_OpAMD64SHRB(v, config)
-	case OpAMD64SHRL:
-		return rewriteValueAMD64_OpAMD64SHRL(v, config)
-	case OpAMD64SHRQ:
-		return rewriteValueAMD64_OpAMD64SHRQ(v, config)
-	case OpAMD64SHRW:
-		return rewriteValueAMD64_OpAMD64SHRW(v, config)
-	case OpAMD64SUBL:
-		return rewriteValueAMD64_OpAMD64SUBL(v, config)
-	case OpAMD64SUBLconst:
-		return rewriteValueAMD64_OpAMD64SUBLconst(v, config)
-	case OpAMD64SUBQ:
-		return rewriteValueAMD64_OpAMD64SUBQ(v, config)
-	case OpAMD64SUBQconst:
-		return rewriteValueAMD64_OpAMD64SUBQconst(v, config)
+	case OpSelect0:
+		return rewriteValueAMD64_OpSelect0(v, config)
+	case OpSelect1:
+		return rewriteValueAMD64_OpSelect1(v, config)
 	case OpSignExt16to32:
 		return rewriteValueAMD64_OpSignExt16to32(v, config)
 	case OpSignExt16to64:
@@ -680,6 +742,8 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpSignExt8to32(v, config)
 	case OpSignExt8to64:
 		return rewriteValueAMD64_OpSignExt8to64(v, config)
+	case OpSlicemask:
+		return rewriteValueAMD64_OpSlicemask(v, config)
 	case OpSqrt:
 		return rewriteValueAMD64_OpSqrt(v, config)
 	case OpStaticCall:
@@ -712,14 +776,6 @@ func rewriteValueAMD64(v *Value, config *Config) bool {
 		return rewriteValueAMD64_OpTrunc64to32(v, config)
 	case OpTrunc64to8:
 		return rewriteValueAMD64_OpTrunc64to8(v, config)
-	case OpAMD64XORL:
-		return rewriteValueAMD64_OpAMD64XORL(v, config)
-	case OpAMD64XORLconst:
-		return rewriteValueAMD64_OpAMD64XORLconst(v, config)
-	case OpAMD64XORQ:
-		return rewriteValueAMD64_OpAMD64XORQ(v, config)
-	case OpAMD64XORQconst:
-		return rewriteValueAMD64_OpAMD64XORQconst(v, config)
 	case OpXor16:
 		return rewriteValueAMD64_OpXor16(v, config)
 	case OpXor32:
@@ -842,6 +898,27 @@ func rewriteValueAMD64_OpAMD64ADDLconst(v *Value, config *Config) bool {
 		v.AddArg(x)
 		return true
 	}
+	// match: (ADDLconst [c] (LEAL [d] {s} x))
+	// cond: is32Bit(c+d)
+	// result: (LEAL [c+d] {s} x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAL {
+			break
+		}
+		d := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		if !(is32Bit(c + d)) {
+			break
+		}
+		v.reset(OpAMD64LEAL)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		return true
+	}
 	return false
 }
 func rewriteValueAMD64_OpAMD64ADDQ(v *Value, config *Config) bool {
@@ -1334,6 +1411,30 @@ func rewriteValueAMD64_OpAMD64ANDLconst(v *Value, config *Config) bool {
 		v.AddArg(x)
 		return true
 	}
+	// match: (ANDLconst [0xFF] x)
+	// cond:
+	// result: (MOVBQZX x)
+	for {
+		if v.AuxInt != 0xFF {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpAMD64MOVBQZX)
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDLconst [0xFFFF] x)
+	// cond:
+	// result: (MOVWQZX x)
+	for {
+		if v.AuxInt != 0xFFFF {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpAMD64MOVWQZX)
+		v.AddArg(x)
+		return true
+	}
 	// match: (ANDLconst [c] _)
 	// cond: int32(c)==0
 	// result: (MOVLconst [0])
@@ -1525,2674 +1626,3832 @@ func rewriteValueAMD64_OpAMD64ANDQconst(v *Value, config *Config) bool {
 	}
 	return false
 }
-func rewriteValueAMD64_OpAdd16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64CMPB(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Add16  x y)
+	// match: (CMPB x (MOVLconst [c]))
 	// cond:
-	// result: (ADDL  x y)
+	// result: (CMPBconst x [int64(int8(c))])
 	for {
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ADDL)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpAdd32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Add32  x y)
-	// cond:
-	// result: (ADDL  x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ADDL)
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64CMPBconst)
+		v.AuxInt = int64(int8(c))
 		v.AddArg(x)
-		v.AddArg(y)
 		return true
 	}
-}
-func rewriteValueAMD64_OpAdd32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Add32F x y)
+	// match: (CMPB (MOVLconst [c]) x)
 	// cond:
-	// result: (ADDSS x y)
+	// result: (InvertFlags (CMPBconst x [int64(int8(c))]))
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ADDSS)
-		v.AddArg(x)
-		v.AddArg(y)
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpAMD64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v0.AuxInt = int64(int8(c))
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpAdd64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Add64  x y)
-	// cond:
-	// result: (ADDQ  x y)
+	// match: (CMPBconst (MOVLconst [x]) [y])
+	// cond: int8(x)==int8(y)
+	// result: (FlagEQ)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ADDQ)
-		v.AddArg(x)
-		v.AddArg(y)
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int8(x) == int8(y)) {
+			break
+		}
+		v.reset(OpAMD64FlagEQ)
 		return true
 	}
-}
-func rewriteValueAMD64_OpAdd64F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Add64F x y)
-	// cond:
-	// result: (ADDSD x y)
+	// match: (CMPBconst (MOVLconst [x]) [y])
+	// cond: int8(x)<int8(y) && uint8(x)<uint8(y)
+	// result: (FlagLT_ULT)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ADDSD)
-		v.AddArg(x)
-		v.AddArg(y)
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int8(x) < int8(y) && uint8(x) < uint8(y)) {
+			break
+		}
+		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-}
-func rewriteValueAMD64_OpAdd8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Add8   x y)
-	// cond:
-	// result: (ADDL  x y)
+	// match: (CMPBconst (MOVLconst [x]) [y])
+	// cond: int8(x)<int8(y) && uint8(x)>uint8(y)
+	// result: (FlagLT_UGT)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ADDL)
-		v.AddArg(x)
-		v.AddArg(y)
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int8(x) < int8(y) && uint8(x) > uint8(y)) {
+			break
+		}
+		v.reset(OpAMD64FlagLT_UGT)
 		return true
 	}
-}
-func rewriteValueAMD64_OpAddPtr(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (AddPtr x y)
-	// cond:
-	// result: (ADDQ  x y)
+	// match: (CMPBconst (MOVLconst [x]) [y])
+	// cond: int8(x)>int8(y) && uint8(x)<uint8(y)
+	// result: (FlagGT_ULT)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ADDQ)
-		v.AddArg(x)
-		v.AddArg(y)
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int8(x) > int8(y) && uint8(x) < uint8(y)) {
+			break
+		}
+		v.reset(OpAMD64FlagGT_ULT)
 		return true
 	}
-}
-func rewriteValueAMD64_OpAddr(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Addr {sym} base)
-	// cond:
-	// result: (LEAQ {sym} base)
+	// match: (CMPBconst (MOVLconst [x]) [y])
+	// cond: int8(x)>int8(y) && uint8(x)>uint8(y)
+	// result: (FlagGT_UGT)
 	for {
-		sym := v.Aux
-		base := v.Args[0]
-		v.reset(OpAMD64LEAQ)
-		v.Aux = sym
-		v.AddArg(base)
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int8(x) > int8(y) && uint8(x) > uint8(y)) {
+			break
+		}
+		v.reset(OpAMD64FlagGT_UGT)
 		return true
 	}
-}
-func rewriteValueAMD64_OpAnd16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (And16 x y)
-	// cond:
-	// result: (ANDL x y)
+	// match: (CMPBconst (ANDLconst _ [m]) [n])
+	// cond: 0 <= int8(m) && int8(m) < int8(n)
+	// result: (FlagLT_ULT)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v.AddArg(x)
-		v.AddArg(y)
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDLconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(0 <= int8(m) && int8(m) < int8(n)) {
+			break
+		}
+		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-}
-func rewriteValueAMD64_OpAnd32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (And32 x y)
+	// match: (CMPBconst (ANDL x y) [0])
 	// cond:
-	// result: (ANDL x y)
+	// result: (TESTB x y)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
+		if v.AuxInt != 0 {
+			break
+		}
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDL {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpAMD64TESTB)
 		v.AddArg(x)
 		v.AddArg(y)
 		return true
 	}
-}
-func rewriteValueAMD64_OpAnd64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (And64 x y)
+	// match: (CMPBconst (ANDLconst [c] x) [0])
 	// cond:
-	// result: (ANDQ x y)
+	// result: (TESTBconst [int64(int8(c))] x)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDQ)
+		if v.AuxInt != 0 {
+			break
+		}
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64TESTBconst)
+		v.AuxInt = int64(int8(c))
 		v.AddArg(x)
-		v.AddArg(y)
 		return true
 	}
-}
-func rewriteValueAMD64_OpAnd8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (And8  x y)
+	// match: (CMPBconst x [0])
 	// cond:
-	// result: (ANDL x y)
+	// result: (TESTB x x)
 	for {
+		if v.AuxInt != 0 {
+			break
+		}
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
+		v.reset(OpAMD64TESTB)
+		v.AddArg(x)
 		v.AddArg(x)
-		v.AddArg(y)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpAndB(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64CMPL(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (AndB x y)
+	// match: (CMPL x (MOVLconst [c]))
 	// cond:
-	// result: (ANDL x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpAvg64u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Avg64u x y)
-	// cond:
-	// result: (AVGQU x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64AVGQU)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpBswap32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Bswap32 x)
-	// cond:
-	// result: (BSWAPL x)
+	// result: (CMPLconst x [c])
 	for {
 		x := v.Args[0]
-		v.reset(OpAMD64BSWAPL)
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64CMPLconst)
+		v.AuxInt = c
 		v.AddArg(x)
 		return true
 	}
-}
-func rewriteValueAMD64_OpBswap64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Bswap64 x)
+	// match: (CMPL (MOVLconst [c]) x)
 	// cond:
-	// result: (BSWAPQ x)
+	// result: (InvertFlags (CMPLconst x [c]))
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64BSWAPQ)
-		v.AddArg(x)
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpAMD64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpAMD64CMOVLEQconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64CMPLconst(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (CMOVLEQconst x (InvertFlags y) [c])
-	// cond:
-	// result: (CMOVLNEconst x y [c])
+	// match: (CMPLconst (MOVLconst [x]) [y])
+	// cond: int32(x)==int32(y)
+	// result: (FlagEQ)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64InvertFlags {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
-		y := v_1.Args[0]
-		c := v.AuxInt
-		v.reset(OpAMD64CMOVLNEconst)
-		v.AddArg(x)
-		v.AddArg(y)
-		v.AuxInt = c
-		return true
-	}
-	// match: (CMOVLEQconst _ (FlagEQ) [c])
-	// cond:
-	// result: (Const32 [c])
-	for {
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagEQ {
+		x := v_0.AuxInt
+		if !(int32(x) == int32(y)) {
 			break
 		}
-		c := v.AuxInt
-		v.reset(OpConst32)
-		v.AuxInt = c
+		v.reset(OpAMD64FlagEQ)
 		return true
 	}
-	// match: (CMOVLEQconst x (FlagLT_ULT))
-	// cond:
-	// result: x
+	// match: (CMPLconst (MOVLconst [x]) [y])
+	// cond: int32(x)<int32(y) && uint32(x)<uint32(y)
+	// result: (FlagLT_ULT)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagLT_ULT {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (CMOVLEQconst x (FlagLT_UGT))
-	// cond:
-	// result: x
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagLT_UGT {
+		x := v_0.AuxInt
+		if !(int32(x) < int32(y) && uint32(x) < uint32(y)) {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
+		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-	// match: (CMOVLEQconst x (FlagGT_ULT))
-	// cond:
-	// result: x
+	// match: (CMPLconst (MOVLconst [x]) [y])
+	// cond: int32(x)<int32(y) && uint32(x)>uint32(y)
+	// result: (FlagLT_UGT)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagGT_ULT {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
+		x := v_0.AuxInt
+		if !(int32(x) < int32(y) && uint32(x) > uint32(y)) {
+			break
+		}
+		v.reset(OpAMD64FlagLT_UGT)
 		return true
 	}
-	// match: (CMOVLEQconst x (FlagGT_UGT))
-	// cond:
-	// result: x
+	// match: (CMPLconst (MOVLconst [x]) [y])
+	// cond: int32(x)>int32(y) && uint32(x)<uint32(y)
+	// result: (FlagGT_ULT)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagGT_UGT {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
+		x := v_0.AuxInt
+		if !(int32(x) > int32(y) && uint32(x) < uint32(y)) {
+			break
+		}
+		v.reset(OpAMD64FlagGT_ULT)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64CMOVQEQconst(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (CMOVQEQconst x (InvertFlags y) [c])
-	// cond:
-	// result: (CMOVQNEconst x y [c])
+	// match: (CMPLconst (MOVLconst [x]) [y])
+	// cond: int32(x)>int32(y) && uint32(x)>uint32(y)
+	// result: (FlagGT_UGT)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64InvertFlags {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
-		y := v_1.Args[0]
-		c := v.AuxInt
-		v.reset(OpAMD64CMOVQNEconst)
-		v.AddArg(x)
-		v.AddArg(y)
-		v.AuxInt = c
+		x := v_0.AuxInt
+		if !(int32(x) > int32(y) && uint32(x) > uint32(y)) {
+			break
+		}
+		v.reset(OpAMD64FlagGT_UGT)
 		return true
 	}
-	// match: (CMOVQEQconst _ (FlagEQ) [c])
-	// cond:
-	// result: (Const64 [c])
+	// match: (CMPLconst (SHRLconst _ [c]) [n])
+	// cond: 0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n)
+	// result: (FlagLT_ULT)
 	for {
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagEQ {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64SHRLconst {
 			break
 		}
-		c := v.AuxInt
-		v.reset(OpConst64)
-		v.AuxInt = c
+		c := v_0.AuxInt
+		if !(0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n)) {
+			break
+		}
+		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-	// match: (CMOVQEQconst x (FlagLT_ULT))
-	// cond:
-	// result: x
+	// match: (CMPLconst (ANDLconst _ [m]) [n])
+	// cond: 0 <= int32(m) && int32(m) < int32(n)
+	// result: (FlagLT_ULT)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagLT_ULT {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDLconst {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
+		m := v_0.AuxInt
+		if !(0 <= int32(m) && int32(m) < int32(n)) {
+			break
+		}
+		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-	// match: (CMOVQEQconst x (FlagLT_UGT))
+	// match: (CMPLconst (ANDL x y) [0])
 	// cond:
-	// result: x
+	// result: (TESTL x y)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagLT_UGT {
+		if v.AuxInt != 0 {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDL {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpAMD64TESTL)
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMOVQEQconst x (FlagGT_ULT))
+	// match: (CMPLconst (ANDLconst [c] x) [0])
 	// cond:
-	// result: x
+	// result: (TESTLconst [c] x)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagGT_ULT {
+		if v.AuxInt != 0 {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64TESTLconst)
+		v.AuxInt = c
 		v.AddArg(x)
 		return true
 	}
-	// match: (CMOVQEQconst x (FlagGT_UGT))
+	// match: (CMPLconst x [0])
 	// cond:
-	// result: x
+	// result: (TESTL x x)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagGT_UGT {
+		if v.AuxInt != 0 {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
+		x := v.Args[0]
+		v.reset(OpAMD64TESTL)
+		v.AddArg(x)
 		v.AddArg(x)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64CMOVWEQconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64CMPQ(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (CMOVWEQconst x (InvertFlags y) [c])
-	// cond:
-	// result: (CMOVWNEconst x y [c])
+	// match: (CMPQ x (MOVQconst [c]))
+	// cond: is32Bit(c)
+	// result: (CMPQconst x [c])
 	for {
 		x := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64InvertFlags {
+		if v_1.Op != OpAMD64MOVQconst {
 			break
 		}
-		y := v_1.Args[0]
-		c := v.AuxInt
-		v.reset(OpAMD64CMOVWNEconst)
-		v.AddArg(x)
-		v.AddArg(y)
-		v.AuxInt = c
-		return true
-	}
-	// match: (CMOVWEQconst _ (FlagEQ) [c])
-	// cond:
-	// result: (Const16 [c])
-	for {
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagEQ {
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
 			break
 		}
-		c := v.AuxInt
-		v.reset(OpConst16)
+		v.reset(OpAMD64CMPQconst)
 		v.AuxInt = c
-		return true
-	}
-	// match: (CMOVWEQconst x (FlagLT_ULT))
-	// cond:
-	// result: x
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (CMOVWEQconst x (FlagLT_UGT))
-	// cond:
-	// result: x
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagLT_UGT {
-			break
-		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (CMOVWEQconst x (FlagGT_ULT))
-	// cond:
-	// result: x
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagGT_ULT {
-			break
-		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (CMOVWEQconst x (FlagGT_UGT))
-	// cond:
-	// result: x
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64FlagGT_UGT {
-			break
-		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64CMPB(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (CMPB x (MOVLconst [c]))
-	// cond:
-	// result: (CMPBconst x [int64(int8(c))])
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64CMPBconst)
 		v.AddArg(x)
-		v.AuxInt = int64(int8(c))
 		return true
 	}
-	// match: (CMPB (MOVLconst [c]) x)
-	// cond:
-	// result: (InvertFlags (CMPBconst x [int64(int8(c))]))
+	// match: (CMPQ (MOVQconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (InvertFlags (CMPQconst x [c]))
 	for {
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v_0.Op != OpAMD64MOVQconst {
 			break
 		}
 		c := v_0.AuxInt
 		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
 		v.reset(OpAMD64InvertFlags)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v0.AuxInt = c
 		v0.AddArg(x)
-		v0.AuxInt = int64(int8(c))
 		v.AddArg(v0)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64CMPBconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64CMPQconst(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (CMPBconst (MOVLconst [x]) [y])
-	// cond: int8(x)==int8(y)
+	// match: (CMPQconst (MOVQconst [x]) [y])
+	// cond: x==y
 	// result: (FlagEQ)
 	for {
+		y := v.AuxInt
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v_0.Op != OpAMD64MOVQconst {
 			break
 		}
 		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int8(x) == int8(y)) {
+		if !(x == y) {
 			break
 		}
 		v.reset(OpAMD64FlagEQ)
 		return true
 	}
-	// match: (CMPBconst (MOVLconst [x]) [y])
-	// cond: int8(x)<int8(y) && uint8(x)<uint8(y)
+	// match: (CMPQconst (MOVQconst [x]) [y])
+	// cond: x<y && uint64(x)<uint64(y)
 	// result: (FlagLT_ULT)
 	for {
+		y := v.AuxInt
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v_0.Op != OpAMD64MOVQconst {
 			break
 		}
 		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int8(x) < int8(y) && uint8(x) < uint8(y)) {
+		if !(x < y && uint64(x) < uint64(y)) {
 			break
 		}
 		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-	// match: (CMPBconst (MOVLconst [x]) [y])
-	// cond: int8(x)<int8(y) && uint8(x)>uint8(y)
+	// match: (CMPQconst (MOVQconst [x]) [y])
+	// cond: x<y && uint64(x)>uint64(y)
 	// result: (FlagLT_UGT)
 	for {
+		y := v.AuxInt
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v_0.Op != OpAMD64MOVQconst {
 			break
 		}
 		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int8(x) < int8(y) && uint8(x) > uint8(y)) {
+		if !(x < y && uint64(x) > uint64(y)) {
 			break
 		}
 		v.reset(OpAMD64FlagLT_UGT)
 		return true
 	}
-	// match: (CMPBconst (MOVLconst [x]) [y])
-	// cond: int8(x)>int8(y) && uint8(x)<uint8(y)
+	// match: (CMPQconst (MOVQconst [x]) [y])
+	// cond: x>y && uint64(x)<uint64(y)
 	// result: (FlagGT_ULT)
 	for {
+		y := v.AuxInt
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v_0.Op != OpAMD64MOVQconst {
 			break
 		}
 		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int8(x) > int8(y) && uint8(x) < uint8(y)) {
+		if !(x > y && uint64(x) < uint64(y)) {
 			break
 		}
 		v.reset(OpAMD64FlagGT_ULT)
 		return true
 	}
-	// match: (CMPBconst (MOVLconst [x]) [y])
-	// cond: int8(x)>int8(y) && uint8(x)>uint8(y)
+	// match: (CMPQconst (MOVQconst [x]) [y])
+	// cond: x>y && uint64(x)>uint64(y)
 	// result: (FlagGT_UGT)
 	for {
+		y := v.AuxInt
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v_0.Op != OpAMD64MOVQconst {
 			break
 		}
 		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int8(x) > int8(y) && uint8(x) > uint8(y)) {
+		if !(x > y && uint64(x) > uint64(y)) {
 			break
 		}
 		v.reset(OpAMD64FlagGT_UGT)
 		return true
 	}
-	// match: (CMPBconst (ANDLconst _ [m]) [n])
-	// cond: 0 <= int8(m) && int8(m) < int8(n)
+	// match: (CMPQconst (MOVBQZX _) [c])
+	// cond: 0xFF < c
 	// result: (FlagLT_ULT)
 	for {
+		c := v.AuxInt
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDLconst {
+		if v_0.Op != OpAMD64MOVBQZX {
 			break
 		}
-		m := v_0.AuxInt
-		n := v.AuxInt
-		if !(0 <= int8(m) && int8(m) < int8(n)) {
+		if !(0xFF < c) {
 			break
 		}
 		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-	// match: (CMPBconst (ANDL x y) [0])
-	// cond:
-	// result: (TESTB x y)
+	// match: (CMPQconst (MOVWQZX _) [c])
+	// cond: 0xFFFF < c
+	// result: (FlagLT_ULT)
 	for {
+		c := v.AuxInt
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDL {
+		if v_0.Op != OpAMD64MOVWQZX {
 			break
 		}
-		x := v_0.Args[0]
-		y := v_0.Args[1]
-		if v.AuxInt != 0 {
+		if !(0xFFFF < c) {
 			break
 		}
-		v.reset(OpAMD64TESTB)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-	// match: (CMPBconst (ANDLconst [c] x) [0])
-	// cond:
-	// result: (TESTBconst [int64(int8(c))] x)
+	// match: (CMPQconst (MOVLQZX _) [c])
+	// cond: 0xFFFFFFFF < c
+	// result: (FlagLT_ULT)
 	for {
+		c := v.AuxInt
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDLconst {
+		if v_0.Op != OpAMD64MOVLQZX {
+			break
+		}
+		if !(0xFFFFFFFF < c) {
+			break
+		}
+		v.reset(OpAMD64FlagLT_ULT)
+		return true
+	}
+	// match: (CMPQconst (SHRQconst _ [c]) [n])
+	// cond: 0 <= n && 0 < c && c <= 64 && (1<<uint64(64-c)) <= uint64(n)
+	// result: (FlagLT_ULT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64SHRQconst {
 			break
 		}
 		c := v_0.AuxInt
-		x := v_0.Args[0]
-		if v.AuxInt != 0 {
+		if !(0 <= n && 0 < c && c <= 64 && (1<<uint64(64-c)) <= uint64(n)) {
 			break
 		}
-		v.reset(OpAMD64TESTBconst)
-		v.AuxInt = int64(int8(c))
-		v.AddArg(x)
+		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-	// match: (CMPBconst x [0])
-	// cond:
-	// result: (TESTB x x)
+	// match: (CMPQconst (ANDQconst _ [m]) [n])
+	// cond: 0 <= m && m < n
+	// result: (FlagLT_ULT)
 	for {
-		x := v.Args[0]
-		if v.AuxInt != 0 {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDQconst {
 			break
 		}
-		v.reset(OpAMD64TESTB)
-		v.AddArg(x)
-		v.AddArg(x)
+		m := v_0.AuxInt
+		if !(0 <= m && m < n) {
+			break
+		}
+		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64CMPL(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (CMPL x (MOVLconst [c]))
+	// match: (CMPQconst (ANDQ x y) [0])
 	// cond:
-	// result: (CMPLconst x [c])
+	// result: (TESTQ x y)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
+		if v.AuxInt != 0 {
 			break
 		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64CMPLconst)
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDQ {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpAMD64TESTQ)
 		v.AddArg(x)
-		v.AuxInt = c
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPL (MOVLconst [c]) x)
+	// match: (CMPQconst (ANDQconst [c] x) [0])
 	// cond:
-	// result: (InvertFlags (CMPLconst x [c]))
+	// result: (TESTQconst [c] x)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v.AuxInt != 0 {
+			break
+		}
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDQconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64TESTQconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPQconst x [0])
+	// cond:
+	// result: (TESTQ x x)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpAMD64TESTQ)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64CMPW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPW x (MOVLconst [c]))
+	// cond:
+	// result: (CMPWconst x [int64(int16(c))])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64CMPWconst)
+		v.AuxInt = int64(int16(c))
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPW (MOVLconst [c]) x)
+	// cond:
+	// result: (InvertFlags (CMPWconst x [int64(int16(c))]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
 		c := v_0.AuxInt
 		x := v.Args[1]
 		v.reset(OpAMD64InvertFlags)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v0.AuxInt = int64(int16(c))
 		v0.AddArg(x)
-		v0.AuxInt = c
 		v.AddArg(v0)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64CMPLconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (CMPLconst (MOVLconst [x]) [y])
-	// cond: int32(x)==int32(y)
+	// match: (CMPWconst (MOVLconst [x]) [y])
+	// cond: int16(x)==int16(y)
 	// result: (FlagEQ)
 	for {
+		y := v.AuxInt
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
 		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int32(x) == int32(y)) {
+		if !(int16(x) == int16(y)) {
 			break
 		}
 		v.reset(OpAMD64FlagEQ)
 		return true
 	}
-	// match: (CMPLconst (MOVLconst [x]) [y])
-	// cond: int32(x)<int32(y) && uint32(x)<uint32(y)
+	// match: (CMPWconst (MOVLconst [x]) [y])
+	// cond: int16(x)<int16(y) && uint16(x)<uint16(y)
 	// result: (FlagLT_ULT)
 	for {
+		y := v.AuxInt
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
 		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int32(x) < int32(y) && uint32(x) < uint32(y)) {
+		if !(int16(x) < int16(y) && uint16(x) < uint16(y)) {
 			break
 		}
 		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-	// match: (CMPLconst (MOVLconst [x]) [y])
-	// cond: int32(x)<int32(y) && uint32(x)>uint32(y)
+	// match: (CMPWconst (MOVLconst [x]) [y])
+	// cond: int16(x)<int16(y) && uint16(x)>uint16(y)
 	// result: (FlagLT_UGT)
 	for {
+		y := v.AuxInt
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
 		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int32(x) < int32(y) && uint32(x) > uint32(y)) {
+		if !(int16(x) < int16(y) && uint16(x) > uint16(y)) {
 			break
 		}
 		v.reset(OpAMD64FlagLT_UGT)
 		return true
 	}
-	// match: (CMPLconst (MOVLconst [x]) [y])
-	// cond: int32(x)>int32(y) && uint32(x)<uint32(y)
+	// match: (CMPWconst (MOVLconst [x]) [y])
+	// cond: int16(x)>int16(y) && uint16(x)<uint16(y)
 	// result: (FlagGT_ULT)
 	for {
+		y := v.AuxInt
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
 		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int32(x) > int32(y) && uint32(x) < uint32(y)) {
+		if !(int16(x) > int16(y) && uint16(x) < uint16(y)) {
 			break
 		}
 		v.reset(OpAMD64FlagGT_ULT)
 		return true
 	}
-	// match: (CMPLconst (MOVLconst [x]) [y])
-	// cond: int32(x)>int32(y) && uint32(x)>uint32(y)
+	// match: (CMPWconst (MOVLconst [x]) [y])
+	// cond: int16(x)>int16(y) && uint16(x)>uint16(y)
 	// result: (FlagGT_UGT)
 	for {
+		y := v.AuxInt
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
 		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int32(x) > int32(y) && uint32(x) > uint32(y)) {
+		if !(int16(x) > int16(y) && uint16(x) > uint16(y)) {
 			break
 		}
 		v.reset(OpAMD64FlagGT_UGT)
 		return true
 	}
-	// match: (CMPLconst (SHRLconst _ [c]) [n])
-	// cond: 0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n)
+	// match: (CMPWconst (ANDLconst _ [m]) [n])
+	// cond: 0 <= int16(m) && int16(m) < int16(n)
 	// result: (FlagLT_ULT)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64SHRLconst {
-			break
-		}
-		c := v_0.AuxInt
 		n := v.AuxInt
-		if !(0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n)) {
-			break
-		}
-		v.reset(OpAMD64FlagLT_ULT)
-		return true
-	}
-	// match: (CMPLconst (ANDLconst _ [m]) [n])
-	// cond: 0 <= int32(m) && int32(m) < int32(n)
-	// result: (FlagLT_ULT)
-	for {
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ANDLconst {
 			break
 		}
 		m := v_0.AuxInt
-		n := v.AuxInt
-		if !(0 <= int32(m) && int32(m) < int32(n)) {
+		if !(0 <= int16(m) && int16(m) < int16(n)) {
 			break
 		}
 		v.reset(OpAMD64FlagLT_ULT)
 		return true
 	}
-	// match: (CMPLconst (ANDL x y) [0])
+	// match: (CMPWconst (ANDL x y) [0])
 	// cond:
-	// result: (TESTL x y)
+	// result: (TESTW x y)
 	for {
+		if v.AuxInt != 0 {
+			break
+		}
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ANDL {
 			break
 		}
 		x := v_0.Args[0]
 		y := v_0.Args[1]
-		if v.AuxInt != 0 {
-			break
-		}
-		v.reset(OpAMD64TESTL)
+		v.reset(OpAMD64TESTW)
 		v.AddArg(x)
 		v.AddArg(y)
 		return true
 	}
-	// match: (CMPLconst (ANDLconst [c] x) [0])
+	// match: (CMPWconst (ANDLconst [c] x) [0])
 	// cond:
-	// result: (TESTLconst [c] x)
+	// result: (TESTWconst [int64(int16(c))] x)
 	for {
+		if v.AuxInt != 0 {
+			break
+		}
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ANDLconst {
 			break
 		}
 		c := v_0.AuxInt
 		x := v_0.Args[0]
-		if v.AuxInt != 0 {
-			break
-		}
-		v.reset(OpAMD64TESTLconst)
-		v.AuxInt = c
+		v.reset(OpAMD64TESTWconst)
+		v.AuxInt = int64(int16(c))
 		v.AddArg(x)
 		return true
 	}
-	// match: (CMPLconst x [0])
+	// match: (CMPWconst x [0])
 	// cond:
-	// result: (TESTL x x)
+	// result: (TESTW x x)
 	for {
-		x := v.Args[0]
 		if v.AuxInt != 0 {
 			break
 		}
-		v.reset(OpAMD64TESTL)
+		x := v.Args[0]
+		v.reset(OpAMD64TESTW)
 		v.AddArg(x)
 		v.AddArg(x)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64CMPQ(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64CMPXCHGLlock(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (CMPQ x (MOVQconst [c]))
-	// cond: is32Bit(c)
-	// result: (CMPQconst x [c])
+	// match: (CMPXCHGLlock [off1] {sym} (ADDQconst [off2] ptr) old new_ mem)
+	// cond: is32Bit(off1+off2)
+	// result: (CMPXCHGLlock [off1+off2] {sym} ptr old new_ mem)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		c := v_1.AuxInt
-		if !(is32Bit(c)) {
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		old := v.Args[1]
+		new_ := v.Args[2]
+		mem := v.Args[3]
+		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		v.reset(OpAMD64CMPQconst)
-		v.AddArg(x)
-		v.AuxInt = c
+		v.reset(OpAMD64CMPXCHGLlock)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(old)
+		v.AddArg(new_)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (CMPQ (MOVQconst [c]) x)
-	// cond: is32Bit(c)
-	// result: (InvertFlags (CMPQconst x [c]))
+	return false
+}
+func rewriteValueAMD64_OpAMD64CMPXCHGQlock(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPXCHGQlock [off1] {sym} (ADDQconst [off2] ptr) old new_ mem)
+	// cond: is32Bit(off1+off2)
+	// result: (CMPXCHGQlock [off1+off2] {sym} ptr old new_ mem)
 	for {
+		off1 := v.AuxInt
+		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		c := v_0.AuxInt
-		x := v.Args[1]
-		if !(is32Bit(c)) {
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		old := v.Args[1]
+		new_ := v.Args[2]
+		mem := v.Args[3]
+		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		v.reset(OpAMD64InvertFlags)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v0.AddArg(x)
-		v0.AuxInt = c
-		v.AddArg(v0)
+		v.reset(OpAMD64CMPXCHGQlock)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(old)
+		v.AddArg(new_)
+		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64CMPQconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64LEAL(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (CMPQconst (MOVQconst [x]) [y])
-	// cond: x==y
-	// result: (FlagEQ)
+	// match: (LEAL [c] {s} (ADDLconst [d] x))
+	// cond: is32Bit(c+d)
+	// result: (LEAL [c+d] {s} x)
 	for {
+		c := v.AuxInt
+		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
+		if v_0.Op != OpAMD64ADDLconst {
 			break
 		}
-		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(x == y) {
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(c + d)) {
 			break
 		}
-		v.reset(OpAMD64FlagEQ)
+		v.reset(OpAMD64LEAL)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
 		return true
 	}
-	// match: (CMPQconst (MOVQconst [x]) [y])
-	// cond: x<y && uint64(x)<uint64(y)
-	// result: (FlagLT_ULT)
+	return false
+}
+func rewriteValueAMD64_OpAMD64LEAQ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LEAQ [c] {s} (ADDQconst [d] x))
+	// cond: is32Bit(c+d)
+	// result: (LEAQ [c+d] {s} x)
 	for {
+		c := v.AuxInt
+		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(x < y && uint64(x) < uint64(y)) {
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(c + d)) {
 			break
 		}
-		v.reset(OpAMD64FlagLT_ULT)
+		v.reset(OpAMD64LEAQ)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
 		return true
 	}
-	// match: (CMPQconst (MOVQconst [x]) [y])
-	// cond: x<y && uint64(x)>uint64(y)
-	// result: (FlagLT_UGT)
-	for {
+	// match: (LEAQ [c] {s} (ADDQ x y))
+	// cond: x.Op != OpSB && y.Op != OpSB
+	// result: (LEAQ1 [c] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
+		if v_0.Op != OpAMD64ADDQ {
 			break
 		}
-		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(x < y && uint64(x) > uint64(y)) {
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(x.Op != OpSB && y.Op != OpSB) {
 			break
 		}
-		v.reset(OpAMD64FlagLT_UGT)
+		v.reset(OpAMD64LEAQ1)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPQconst (MOVQconst [x]) [y])
-	// cond: x>y && uint64(x)<uint64(y)
-	// result: (FlagGT_ULT)
+	// match: (LEAQ [off1] {sym1} (LEAQ [off2] {sym2} x))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (LEAQ [off1+off2] {mergeSym(sym1,sym2)} x)
 	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
+		if v_0.Op != OpAMD64LEAQ {
 			break
 		}
-		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(x > y && uint64(x) < uint64(y)) {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64FlagGT_ULT)
+		v.reset(OpAMD64LEAQ)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
 		return true
 	}
-	// match: (CMPQconst (MOVQconst [x]) [y])
-	// cond: x>y && uint64(x)>uint64(y)
-	// result: (FlagGT_UGT)
+	// match: (LEAQ [off1] {sym1} (LEAQ1 [off2] {sym2} x y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (LEAQ1 [off1+off2] {mergeSym(sym1,sym2)} x y)
 	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
+		if v_0.Op != OpAMD64LEAQ1 {
 			break
 		}
-		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(x > y && uint64(x) > uint64(y)) {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64FlagGT_UGT)
+		v.reset(OpAMD64LEAQ1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPQconst (MOVBQZX _) [c])
-	// cond: 0xFF < c
-	// result: (FlagLT_ULT)
+	// match: (LEAQ [off1] {sym1} (LEAQ2 [off2] {sym2} x y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (LEAQ2 [off1+off2] {mergeSym(sym1,sym2)} x y)
 	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVBQZX {
+		if v_0.Op != OpAMD64LEAQ2 {
 			break
 		}
-		c := v.AuxInt
-		if !(0xFF < c) {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64FlagLT_ULT)
+		v.reset(OpAMD64LEAQ2)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPQconst (MOVWQZX _) [c])
-	// cond: 0xFFFF < c
-	// result: (FlagLT_ULT)
+	// match: (LEAQ [off1] {sym1} (LEAQ4 [off2] {sym2} x y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (LEAQ4 [off1+off2] {mergeSym(sym1,sym2)} x y)
 	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVWQZX {
+		if v_0.Op != OpAMD64LEAQ4 {
 			break
 		}
-		c := v.AuxInt
-		if !(0xFFFF < c) {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64FlagLT_ULT)
+		v.reset(OpAMD64LEAQ4)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPQconst (MOVLQZX _) [c])
-	// cond: 0xFFFFFFFF < c
-	// result: (FlagLT_ULT)
+	// match: (LEAQ [off1] {sym1} (LEAQ8 [off2] {sym2} x y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (LEAQ8 [off1+off2] {mergeSym(sym1,sym2)} x y)
 	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLQZX {
+		if v_0.Op != OpAMD64LEAQ8 {
 			break
 		}
-		c := v.AuxInt
-		if !(0xFFFFFFFF < c) {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64FlagLT_ULT)
+		v.reset(OpAMD64LEAQ8)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPQconst (SHRQconst _ [c]) [n])
-	// cond: 0 <= n && 0 < c && c <= 64 && (1<<uint64(64-c)) <= uint64(n)
-	// result: (FlagLT_ULT)
+	return false
+}
+func rewriteValueAMD64_OpAMD64LEAQ1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LEAQ1 [c] {s} (ADDQconst [d] x) y)
+	// cond: is32Bit(c+d)   && x.Op != OpSB
+	// result: (LEAQ1 [c+d] {s} x y)
 	for {
+		c := v.AuxInt
+		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64SHRQconst {
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		c := v_0.AuxInt
-		n := v.AuxInt
-		if !(0 <= n && 0 < c && c <= 64 && (1<<uint64(64-c)) <= uint64(n)) {
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(c+d) && x.Op != OpSB) {
 			break
 		}
-		v.reset(OpAMD64FlagLT_ULT)
+		v.reset(OpAMD64LEAQ1)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPQconst (ANDQconst _ [m]) [n])
-	// cond: 0 <= m && m < n
-	// result: (FlagLT_ULT)
+	// match: (LEAQ1 [c] {s} x (ADDQconst [d] y))
+	// cond: is32Bit(c+d)   && y.Op != OpSB
+	// result: (LEAQ1 [c+d] {s} x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDQconst {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		m := v_0.AuxInt
-		n := v.AuxInt
-		if !(0 <= m && m < n) {
+		d := v_1.AuxInt
+		y := v_1.Args[0]
+		if !(is32Bit(c+d) && y.Op != OpSB) {
 			break
 		}
-		v.reset(OpAMD64FlagLT_ULT)
+		v.reset(OpAMD64LEAQ1)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPQconst (ANDQ x y) [0])
+	// match: (LEAQ1 [c] {s} x (SHLQconst [1] y))
 	// cond:
-	// result: (TESTQ x y)
+	// result: (LEAQ2 [c] {s} x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDQ {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		x := v_0.Args[0]
-		y := v_0.Args[1]
-		if v.AuxInt != 0 {
+		if v_1.AuxInt != 1 {
 			break
 		}
-		v.reset(OpAMD64TESTQ)
+		y := v_1.Args[0]
+		v.reset(OpAMD64LEAQ2)
+		v.AuxInt = c
+		v.Aux = s
 		v.AddArg(x)
 		v.AddArg(y)
 		return true
 	}
-	// match: (CMPQconst (ANDQconst [c] x) [0])
+	// match: (LEAQ1 [c] {s} (SHLQconst [1] x) y)
 	// cond:
-	// result: (TESTQconst [c] x)
+	// result: (LEAQ2 [c] {s} y x)
 	for {
+		c := v.AuxInt
+		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDQconst {
+		if v_0.Op != OpAMD64SHLQconst {
 			break
 		}
-		c := v_0.AuxInt
-		x := v_0.Args[0]
-		if v.AuxInt != 0 {
+		if v_0.AuxInt != 1 {
 			break
 		}
-		v.reset(OpAMD64TESTQconst)
+		x := v_0.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64LEAQ2)
 		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(y)
 		v.AddArg(x)
 		return true
 	}
-	// match: (CMPQconst x [0])
+	// match: (LEAQ1 [c] {s} x (SHLQconst [2] y))
 	// cond:
-	// result: (TESTQ x x)
+	// result: (LEAQ4 [c] {s} x y)
 	for {
+		c := v.AuxInt
+		s := v.Aux
 		x := v.Args[0]
-		if v.AuxInt != 0 {
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		v.reset(OpAMD64TESTQ)
-		v.AddArg(x)
+		if v_1.AuxInt != 2 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpAMD64LEAQ4)
+		v.AuxInt = c
+		v.Aux = s
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64CMPW(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (CMPW x (MOVLconst [c]))
+	// match: (LEAQ1 [c] {s} (SHLQconst [2] x) y)
 	// cond:
-	// result: (CMPWconst x [int64(int16(c))])
+	// result: (LEAQ4 [c] {s} y x)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64SHLQconst {
 			break
 		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64CMPWconst)
-		v.AddArg(x)
-		v.AuxInt = int64(int16(c))
-		return true
-	}
-	// match: (CMPW (MOVLconst [c]) x)
-	// cond:
-	// result: (InvertFlags (CMPWconst x [int64(int16(c))]))
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v_0.AuxInt != 2 {
 			break
 		}
-		c := v_0.AuxInt
-		x := v.Args[1]
-		v.reset(OpAMD64InvertFlags)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v0.AddArg(x)
-		v0.AuxInt = int64(int16(c))
-		v.AddArg(v0)
+		x := v_0.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64LEAQ4)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(y)
+		v.AddArg(x)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64CMPWconst(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (CMPWconst (MOVLconst [x]) [y])
-	// cond: int16(x)==int16(y)
-	// result: (FlagEQ)
+	// match: (LEAQ1 [c] {s} x (SHLQconst [3] y))
+	// cond:
+	// result: (LEAQ8 [c] {s} x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int16(x) == int16(y)) {
+		if v_1.AuxInt != 3 {
 			break
 		}
-		v.reset(OpAMD64FlagEQ)
+		y := v_1.Args[0]
+		v.reset(OpAMD64LEAQ8)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPWconst (MOVLconst [x]) [y])
-	// cond: int16(x)<int16(y) && uint16(x)<uint16(y)
-	// result: (FlagLT_ULT)
+	// match: (LEAQ1 [c] {s} (SHLQconst [3] x) y)
+	// cond:
+	// result: (LEAQ8 [c] {s} y x)
 	for {
+		c := v.AuxInt
+		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v_0.Op != OpAMD64SHLQconst {
 			break
 		}
-		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int16(x) < int16(y) && uint16(x) < uint16(y)) {
+		if v_0.AuxInt != 3 {
 			break
 		}
-		v.reset(OpAMD64FlagLT_ULT)
+		x := v_0.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64LEAQ8)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(y)
+		v.AddArg(x)
 		return true
 	}
-	// match: (CMPWconst (MOVLconst [x]) [y])
-	// cond: int16(x)<int16(y) && uint16(x)>uint16(y)
-	// result: (FlagLT_UGT)
+	// match: (LEAQ1 [off1] {sym1} (LEAQ [off2] {sym2} x) y)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
+	// result: (LEAQ1 [off1+off2] {mergeSym(sym1,sym2)} x y)
 	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v_0.Op != OpAMD64LEAQ {
 			break
 		}
-		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int16(x) < int16(y) && uint16(x) > uint16(y)) {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
 			break
 		}
-		v.reset(OpAMD64FlagLT_UGT)
+		v.reset(OpAMD64LEAQ1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPWconst (MOVLconst [x]) [y])
-	// cond: int16(x)>int16(y) && uint16(x)<uint16(y)
-	// result: (FlagGT_ULT)
+	// match: (LEAQ1 [off1] {sym1} x (LEAQ [off2] {sym2} y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && y.Op != OpSB
+	// result: (LEAQ1 [off1+off2] {mergeSym(sym1,sym2)} x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64LEAQ {
 			break
 		}
-		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int16(x) > int16(y) && uint16(x) < uint16(y)) {
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		y := v_1.Args[0]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && y.Op != OpSB) {
 			break
 		}
-		v.reset(OpAMD64FlagGT_ULT)
+		v.reset(OpAMD64LEAQ1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPWconst (MOVLconst [x]) [y])
-	// cond: int16(x)>int16(y) && uint16(x)>uint16(y)
-	// result: (FlagGT_UGT)
+	return false
+}
+func rewriteValueAMD64_OpAMD64LEAQ2(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LEAQ2 [c] {s} (ADDQconst [d] x) y)
+	// cond: is32Bit(c+d)   && x.Op != OpSB
+	// result: (LEAQ2 [c+d] {s} x y)
 	for {
+		c := v.AuxInt
+		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		x := v_0.AuxInt
-		y := v.AuxInt
-		if !(int16(x) > int16(y) && uint16(x) > uint16(y)) {
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(c+d) && x.Op != OpSB) {
 			break
 		}
-		v.reset(OpAMD64FlagGT_UGT)
+		v.reset(OpAMD64LEAQ2)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPWconst (ANDLconst _ [m]) [n])
-	// cond: 0 <= int16(m) && int16(m) < int16(n)
-	// result: (FlagLT_ULT)
+	// match: (LEAQ2 [c] {s} x (ADDQconst [d] y))
+	// cond: is32Bit(c+2*d) && y.Op != OpSB
+	// result: (LEAQ2 [c+2*d] {s} x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDLconst {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		m := v_0.AuxInt
-		n := v.AuxInt
-		if !(0 <= int16(m) && int16(m) < int16(n)) {
+		d := v_1.AuxInt
+		y := v_1.Args[0]
+		if !(is32Bit(c+2*d) && y.Op != OpSB) {
 			break
 		}
-		v.reset(OpAMD64FlagLT_ULT)
+		v.reset(OpAMD64LEAQ2)
+		v.AuxInt = c + 2*d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPWconst (ANDL x y) [0])
+	// match: (LEAQ2 [c] {s} x (SHLQconst [1] y))
 	// cond:
-	// result: (TESTW x y)
+	// result: (LEAQ4 [c] {s} x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDL {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		x := v_0.Args[0]
-		y := v_0.Args[1]
-		if v.AuxInt != 0 {
+		if v_1.AuxInt != 1 {
 			break
 		}
-		v.reset(OpAMD64TESTW)
+		y := v_1.Args[0]
+		v.reset(OpAMD64LEAQ4)
+		v.AuxInt = c
+		v.Aux = s
 		v.AddArg(x)
 		v.AddArg(y)
 		return true
 	}
-	// match: (CMPWconst (ANDLconst [c] x) [0])
+	// match: (LEAQ2 [c] {s} x (SHLQconst [2] y))
 	// cond:
-	// result: (TESTWconst [int64(int16(c))] x)
+	// result: (LEAQ8 [c] {s} x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDLconst {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		c := v_0.AuxInt
-		x := v_0.Args[0]
-		if v.AuxInt != 0 {
+		if v_1.AuxInt != 2 {
 			break
 		}
-		v.reset(OpAMD64TESTWconst)
-		v.AuxInt = int64(int16(c))
+		y := v_1.Args[0]
+		v.reset(OpAMD64LEAQ8)
+		v.AuxInt = c
+		v.Aux = s
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (CMPWconst x [0])
-	// cond:
-	// result: (TESTW x x)
+	// match: (LEAQ2 [off1] {sym1} (LEAQ [off2] {sym2} x) y)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
+	// result: (LEAQ2 [off1+off2] {mergeSym(sym1,sym2)} x y)
 	for {
-		x := v.Args[0]
-		if v.AuxInt != 0 {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
 			break
 		}
-		v.reset(OpAMD64TESTW)
-		v.AddArg(x)
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64LEAQ2)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpClosureCall(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64LEAQ4(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (ClosureCall [argwid] entry closure mem)
-	// cond:
-	// result: (CALLclosure [argwid] entry closure mem)
-	for {
-		argwid := v.AuxInt
-		entry := v.Args[0]
-		closure := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64CALLclosure)
-		v.AuxInt = argwid
-		v.AddArg(entry)
-		v.AddArg(closure)
-		v.AddArg(mem)
-		return true
-	}
-}
-func rewriteValueAMD64_OpCom16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Com16 x)
-	// cond:
-	// result: (NOTL x)
+	// match: (LEAQ4 [c] {s} (ADDQconst [d] x) y)
+	// cond: is32Bit(c+d)   && x.Op != OpSB
+	// result: (LEAQ4 [c+d] {s} x y)
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64NOTL)
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(c+d) && x.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64LEAQ4)
+		v.AuxInt = c + d
+		v.Aux = s
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCom32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Com32 x)
-	// cond:
-	// result: (NOTL x)
+	// match: (LEAQ4 [c] {s} x (ADDQconst [d] y))
+	// cond: is32Bit(c+4*d) && y.Op != OpSB
+	// result: (LEAQ4 [c+4*d] {s} x y)
 	for {
+		c := v.AuxInt
+		s := v.Aux
 		x := v.Args[0]
-		v.reset(OpAMD64NOTL)
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_1.AuxInt
+		y := v_1.Args[0]
+		if !(is32Bit(c+4*d) && y.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64LEAQ4)
+		v.AuxInt = c + 4*d
+		v.Aux = s
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCom64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Com64 x)
+	// match: (LEAQ4 [c] {s} x (SHLQconst [1] y))
 	// cond:
-	// result: (NOTQ x)
+	// result: (LEAQ8 [c] {s} x y)
 	for {
+		c := v.AuxInt
+		s := v.Aux
 		x := v.Args[0]
-		v.reset(OpAMD64NOTQ)
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpAMD64LEAQ8)
+		v.AuxInt = c
+		v.Aux = s
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCom8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Com8  x)
-	// cond:
-	// result: (NOTL x)
+	// match: (LEAQ4 [off1] {sym1} (LEAQ [off2] {sym2} x) y)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
+	// result: (LEAQ4 [off1+off2] {mergeSym(sym1,sym2)} x y)
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64NOTL)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64LEAQ4)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpConst16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64LEAQ8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Const16  [val])
-	// cond:
-	// result: (MOVLconst [val])
+	// match: (LEAQ8 [c] {s} (ADDQconst [d] x) y)
+	// cond: is32Bit(c+d)   && x.Op != OpSB
+	// result: (LEAQ8 [c+d] {s} x y)
 	for {
-		val := v.AuxInt
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = val
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(c+d) && x.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64LEAQ8)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-}
-func rewriteValueAMD64_OpConst32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Const32  [val])
-	// cond:
-	// result: (MOVLconst [val])
+	// match: (LEAQ8 [c] {s} x (ADDQconst [d] y))
+	// cond: is32Bit(c+8*d) && y.Op != OpSB
+	// result: (LEAQ8 [c+8*d] {s} x y)
 	for {
-		val := v.AuxInt
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = val
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_1.AuxInt
+		y := v_1.Args[0]
+		if !(is32Bit(c+8*d) && y.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64LEAQ8)
+		v.AuxInt = c + 8*d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-}
-func rewriteValueAMD64_OpConst32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Const32F [val])
-	// cond:
-	// result: (MOVSSconst [val])
+	// match: (LEAQ8 [off1] {sym1} (LEAQ [off2] {sym2} x) y)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
+	// result: (LEAQ8 [off1+off2] {mergeSym(sym1,sym2)} x y)
 	for {
-		val := v.AuxInt
-		v.reset(OpAMD64MOVSSconst)
-		v.AuxInt = val
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64LEAQ8)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpConst64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVBQSX(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Const64  [val])
-	// cond:
-	// result: (MOVQconst [val])
+	// match: (MOVBQSX x:(MOVBload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
 	for {
-		val := v.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = val
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVBload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBQSXload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpConst64F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Const64F [val])
-	// cond:
-	// result: (MOVSDconst [val])
+	// match: (MOVBQSX x:(MOVWload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
 	for {
-		val := v.AuxInt
-		v.reset(OpAMD64MOVSDconst)
-		v.AuxInt = val
-		return true
-	}
-}
-func rewriteValueAMD64_OpConst8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Const8   [val])
-	// cond:
-	// result: (MOVLconst [val])
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVWload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBQSXload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVBQSX x:(MOVLload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
 	for {
-		val := v.AuxInt
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = val
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVLload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBQSXload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpConstBool(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (ConstBool [b])
-	// cond:
-	// result: (MOVLconst [b])
+	// match: (MOVBQSX x:(MOVQload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
 	for {
-		b := v.AuxInt
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = b
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVQload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBQSXload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpConstNil(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (ConstNil)
-	// cond:
-	// result: (MOVQconst [0])
+	// match: (MOVBQSX (ANDLconst [c] x))
+	// cond: c & 0x80 == 0
+	// result: (ANDLconst [c & 0x7f] x)
 	for {
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = 0
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(c&0x80 == 0) {
+			break
+		}
+		v.reset(OpAMD64ANDLconst)
+		v.AuxInt = c & 0x7f
+		v.AddArg(x)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpConvert(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVBQSXload(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Convert <t> x mem)
-	// cond:
-	// result: (MOVQconvert <t> x mem)
+	// match: (MOVBQSXload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBQSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
 		mem := v.Args[1]
-		v.reset(OpAMD64MOVQconvert)
-		v.Type = t
-		v.AddArg(x)
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBQSXload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
 		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpCtz16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVBQZX(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Ctz16 <t> x)
-	// cond:
-	// result: (CMOVWEQconst (BSFW <t> x) (CMPWconst x [0]) [16])
+	// match: (MOVBQZX x:(MOVBload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
 	for {
-		t := v.Type
 		x := v.Args[0]
-		v.reset(OpAMD64CMOVWEQconst)
-		v0 := b.NewValue0(v.Line, OpAMD64BSFW, t)
-		v0.AddArg(x)
+		if x.Op != OpAMD64MOVBload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, v.Type)
+		v.reset(OpCopy)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v1.AddArg(x)
-		v1.AuxInt = 0
-		v.AddArg(v1)
-		v.AuxInt = 16
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCtz32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Ctz32 <t> x)
-	// cond:
-	// result: (CMOVLEQconst (BSFL <t> x) (CMPLconst x [0]) [32])
+	// match: (MOVBQZX x:(MOVWload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
 	for {
-		t := v.Type
 		x := v.Args[0]
-		v.reset(OpAMD64CMOVLEQconst)
-		v0 := b.NewValue0(v.Line, OpAMD64BSFL, t)
-		v0.AddArg(x)
+		if x.Op != OpAMD64MOVWload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, v.Type)
+		v.reset(OpCopy)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v1.AddArg(x)
-		v1.AuxInt = 0
-		v.AddArg(v1)
-		v.AuxInt = 32
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCtz64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Ctz64 <t> x)
-	// cond:
-	// result: (CMOVQEQconst (BSFQ <t> x) (CMPQconst x [0]) [64])
+	// match: (MOVBQZX x:(MOVLload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
 	for {
-		t := v.Type
 		x := v.Args[0]
-		v.reset(OpAMD64CMOVQEQconst)
-		v0 := b.NewValue0(v.Line, OpAMD64BSFQ, t)
-		v0.AddArg(x)
+		if x.Op != OpAMD64MOVLload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, v.Type)
+		v.reset(OpCopy)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v1.AddArg(x)
-		v1.AuxInt = 0
-		v.AddArg(v1)
-		v.AuxInt = 64
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCvt32Fto32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Cvt32Fto32 x)
-	// cond:
-	// result: (CVTTSS2SL x)
+	// match: (MOVBQZX x:(MOVQload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
 	for {
 		x := v.Args[0]
-		v.reset(OpAMD64CVTTSS2SL)
-		v.AddArg(x)
+		if x.Op != OpAMD64MOVQload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCvt32Fto64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Cvt32Fto64 x)
-	// cond:
-	// result: (CVTTSS2SQ x)
+	// match: (MOVBQZX x:(MOVBloadidx1 [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBloadidx1 <v.Type> [off] {sym} ptr idx mem)
 	for {
 		x := v.Args[0]
-		v.reset(OpAMD64CVTTSS2SQ)
-		v.AddArg(x)
+		if x.Op != OpAMD64MOVBloadidx1 {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBloadidx1, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCvt32Fto64F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Cvt32Fto64F x)
+	// match: (MOVBQZX (ANDLconst [c] x))
 	// cond:
-	// result: (CVTSS2SD x)
+	// result: (ANDLconst [c & 0xff] x)
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64CVTSS2SD)
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64ANDLconst)
+		v.AuxInt = c & 0xff
 		v.AddArg(x)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpCvt32to32F(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVBload(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Cvt32to32F x)
-	// cond:
-	// result: (CVTSL2SS x)
+	// match: (MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64CVTSL2SS)
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVBstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
 		v.AddArg(x)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCvt32to64F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Cvt32to64F x)
-	// cond:
-	// result: (CVTSL2SD x)
+	// match: (MOVBload  [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVBload  [off1+off2] {sym} ptr mem)
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64CVTSL2SD)
-		v.AddArg(x)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCvt64Fto32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Cvt64Fto32 x)
-	// cond:
-	// result: (CVTTSD2SL x)
+	// match: (MOVBload  [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64CVTTSD2SL)
-		v.AddArg(x)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCvt64Fto32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Cvt64Fto32F x)
-	// cond:
-	// result: (CVTSD2SS x)
+	// match: (MOVBload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64CVTSD2SS)
-		v.AddArg(x)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBloadidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCvt64Fto64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Cvt64Fto64 x)
-	// cond:
-	// result: (CVTTSD2SQ x)
+	// match: (MOVBload [off] {sym} (ADDQ ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVBloadidx1 [off] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64CVTTSD2SQ)
-		v.AddArg(x)
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQ {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64MOVBloadidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpCvt64to32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Cvt64to32F x)
-	// cond:
-	// result: (CVTSQ2SS x)
+	// match: (MOVBload  [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVBload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64CVTSQ2SS)
-		v.AddArg(x)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload  [off1] {sym} (ADDLconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVBload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpCvt64to64F(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVBloadidx1(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Cvt64to64F x)
+	// match: (MOVBloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
 	// cond:
-	// result: (CVTSQ2SD x)
+	// result: (MOVBloadidx1 [c+d] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		v.reset(OpAMD64CVTSQ2SD)
-		v.AddArg(x)
-		return true
-	}
-}
-func rewriteValueAMD64_OpDeferCall(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (DeferCall [argwid] mem)
-	// cond:
-	// result: (CALLdefer [argwid] mem)
-	for {
-		argwid := v.AuxInt
-		mem := v.Args[0]
-		v.reset(OpAMD64CALLdefer)
-		v.AuxInt = argwid
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVBloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpDiv16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Div16  x y)
+	// match: (MOVBloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
 	// cond:
-	// result: (DIVW  x y)
+	// result: (MOVBloadidx1 [c+d] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64DIVW)
-		v.AddArg(x)
-		v.AddArg(y)
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVBloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpDiv16u(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVBstore(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Div16u x y)
+	// match: (MOVBstore [off] {sym} ptr (MOVBQSX x) mem)
 	// cond:
-	// result: (DIVWU x y)
+	// result: (MOVBstore [off] {sym} ptr x mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64DIVWU)
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVBQSX {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
 		v.AddArg(x)
-		v.AddArg(y)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpDiv32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Div32  x y)
+	// match: (MOVBstore [off] {sym} ptr (MOVBQZX x) mem)
 	// cond:
-	// result: (DIVL  x y)
+	// result: (MOVBstore [off] {sym} ptr x mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64DIVL)
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVBQZX {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
 		v.AddArg(x)
-		v.AddArg(y)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpDiv32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Div32F x y)
-	// cond:
-	// result: (DIVSS x y)
+	// match: (MOVBstore  [off1] {sym} (ADDQconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVBstore  [off1+off2] {sym} ptr val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64DIVSS)
-		v.AddArg(x)
-		v.AddArg(y)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpDiv32u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Div32u x y)
-	// cond:
-	// result: (DIVLU x y)
+	// match: (MOVBstore [off] {sym} ptr (MOVLconst [c]) mem)
+	// cond: validOff(off)
+	// result: (MOVBstoreconst [makeValAndOff(int64(int8(c)),off)] {sym} ptr mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64DIVLU)
-		v.AddArg(x)
-		v.AddArg(y)
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			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
 	}
-}
-func rewriteValueAMD64_OpDiv64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Div64  x y)
-	// cond:
-	// result: (DIVQ  x y)
+	// 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)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64DIVQ)
-		v.AddArg(x)
-		v.AddArg(y)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpDiv64F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Div64F x y)
-	// cond:
-	// result: (DIVSD x y)
+	// match: (MOVBstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64DIVSD)
-		v.AddArg(x)
-		v.AddArg(y)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBstoreidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpDiv64u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Div64u x y)
-	// cond:
-	// result: (DIVQU x y)
+	// match: (MOVBstore [off] {sym} (ADDQ ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVBstoreidx1 [off] {sym} ptr idx val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64DIVQU)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpDiv8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Div8   x y)
-	// cond:
-	// result: (DIVW  (SignExt8to16 x) (SignExt8to16 y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64DIVW)
-		v0 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
-		v0.AddArg(x)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
-		v1.AddArg(y)
-		v.AddArg(v1)
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQ {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64MOVBstoreidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpDiv8u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Div8u  x y)
-	// cond:
-	// result: (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y))
+	// match: (MOVBstore [i] {s} p w   x2:(MOVBstore [i-1] {s} p (SHRLconst [8] w)   x1:(MOVBstore [i-2] {s} p (SHRLconst [16] w)   x0:(MOVBstore [i-3] {s} p (SHRLconst [24] w) mem))))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && clobber(x0)   && clobber(x1)   && clobber(x2)
+	// result: (MOVLstore [i-3] {s} p (BSWAPL <w.Type> w) mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64DIVWU)
-		v0 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
-		v0.AddArg(x)
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w := v.Args[1]
+		x2 := v.Args[2]
+		if x2.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x2.AuxInt != i-1 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		x2_1 := x2.Args[1]
+		if x2_1.Op != OpAMD64SHRLconst {
+			break
+		}
+		if x2_1.AuxInt != 8 {
+			break
+		}
+		if w != x2_1.Args[0] {
+			break
+		}
+		x1 := x2.Args[2]
+		if x1.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x1.AuxInt != i-2 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		x1_1 := x1.Args[1]
+		if x1_1.Op != OpAMD64SHRLconst {
+			break
+		}
+		if x1_1.AuxInt != 16 {
+			break
+		}
+		if w != x1_1.Args[0] {
+			break
+		}
+		x0 := x1.Args[2]
+		if x0.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x0.AuxInt != i-3 {
+			break
+		}
+		if x0.Aux != s {
+			break
+		}
+		if p != x0.Args[0] {
+			break
+		}
+		x0_1 := x0.Args[1]
+		if x0_1.Op != OpAMD64SHRLconst {
+			break
+		}
+		if x0_1.AuxInt != 24 {
+			break
+		}
+		if w != x0_1.Args[0] {
+			break
+		}
+		mem := x0.Args[2]
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && clobber(x0) && clobber(x1) && clobber(x2)) {
+			break
+		}
+		v.reset(OpAMD64MOVLstore)
+		v.AuxInt = i - 3
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, OpAMD64BSWAPL, w.Type)
+		v0.AddArg(w)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
-		v1.AddArg(y)
-		v.AddArg(v1)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpEq16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Eq16  x y)
-	// cond:
-	// result: (SETEQ (CMPW x y))
+	// 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)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETEQ)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w := v.Args[1]
+		x6 := v.Args[2]
+		if x6.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x6.AuxInt != i-1 {
+			break
+		}
+		if x6.Aux != s {
+			break
+		}
+		if p != x6.Args[0] {
+			break
+		}
+		x6_1 := x6.Args[1]
+		if x6_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		if x6_1.AuxInt != 8 {
+			break
+		}
+		if w != x6_1.Args[0] {
+			break
+		}
+		x5 := x6.Args[2]
+		if x5.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x5.AuxInt != i-2 {
+			break
+		}
+		if x5.Aux != s {
+			break
+		}
+		if p != x5.Args[0] {
+			break
+		}
+		x5_1 := x5.Args[1]
+		if x5_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		if x5_1.AuxInt != 16 {
+			break
+		}
+		if w != x5_1.Args[0] {
+			break
+		}
+		x4 := x5.Args[2]
+		if x4.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x4.AuxInt != i-3 {
+			break
+		}
+		if x4.Aux != s {
+			break
+		}
+		if p != x4.Args[0] {
+			break
+		}
+		x4_1 := x4.Args[1]
+		if x4_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		if x4_1.AuxInt != 24 {
+			break
+		}
+		if w != x4_1.Args[0] {
+			break
+		}
+		x3 := x4.Args[2]
+		if x3.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x3.AuxInt != i-4 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		x3_1 := x3.Args[1]
+		if x3_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		if x3_1.AuxInt != 32 {
+			break
+		}
+		if w != x3_1.Args[0] {
+			break
+		}
+		x2 := x3.Args[2]
+		if x2.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x2.AuxInt != i-5 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		x2_1 := x2.Args[1]
+		if x2_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		if x2_1.AuxInt != 40 {
+			break
+		}
+		if w != x2_1.Args[0] {
+			break
+		}
+		x1 := x2.Args[2]
+		if x1.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x1.AuxInt != i-6 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		x1_1 := x1.Args[1]
+		if x1_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		if x1_1.AuxInt != 48 {
+			break
+		}
+		if w != x1_1.Args[0] {
+			break
+		}
+		x0 := x1.Args[2]
+		if x0.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x0.AuxInt != i-7 {
+			break
+		}
+		if x0.Aux != s {
+			break
+		}
+		if p != x0.Args[0] {
+			break
+		}
+		x0_1 := x0.Args[1]
+		if x0_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		if x0_1.AuxInt != 56 {
+			break
+		}
+		if w != x0_1.Args[0] {
+			break
+		}
+		mem := x0.Args[2]
+		if !(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)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstore)
+		v.AuxInt = i - 7
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, OpAMD64BSWAPQ, w.Type)
+		v0.AddArg(w)
 		v.AddArg(v0)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpEq32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Eq32  x y)
-	// cond:
-	// result: (SETEQ (CMPL x y))
+	// match: (MOVBstore [i] {s} p (SHRQconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstore [i-1] {s} p w mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETEQ)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpEq32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Eq32F x y)
-	// cond:
-	// result: (SETEQF (UCOMISS x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETEQF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		if v_1.AuxInt != 8 {
+			break
+		}
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if w != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVWstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpEq64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Eq64  x y)
-	// cond:
-	// result: (SETEQ (CMPQ x y))
+	// match: (MOVBstore [i] {s} p (SHRQconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SHRQconst [j-8] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstore [i-1] {s} p w0 mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETEQ)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		j := v_1.AuxInt
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		if w0.Op != OpAMD64SHRQconst {
+			break
+		}
+		if w0.AuxInt != j-8 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVWstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpEq64F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Eq64F x y)
-	// cond:
-	// result: (SETEQF (UCOMISD x y))
+	// match: (MOVBstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVBstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETEQF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpEq8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Eq8   x y)
-	// cond:
-	// result: (SETEQ (CMPB x y))
+	// match: (MOVBstore  [off1] {sym} (ADDLconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVBstore  [off1+off2] {sym} ptr val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETEQ)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpEqB(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVBstoreconst(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (EqB   x y)
-	// cond:
-	// result: (SETEQ (CMPB x y))
+	// match: (MOVBstoreconst [sc] {s} (ADDQconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETEQ)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		sc := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpAMD64MOVBstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpEqPtr(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (EqPtr x y)
-	// cond:
-	// result: (SETEQ (CMPQ x y))
+	// match: (MOVBstoreconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
+	// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETEQ)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpAMD64MOVBstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGeq16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Geq16  x y)
-	// cond:
-	// result: (SETGE (CMPW x y))
+	// match: (MOVBstoreconst [x] {sym1} (LEAQ1 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVBstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		x := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVBstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGeq16U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Geq16U x y)
+	// match: (MOVBstoreconst [x] {sym} (ADDQ ptr idx) mem)
 	// cond:
-	// result: (SETAE (CMPW x y))
+	// result: (MOVBstoreconstidx1 [x] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETAE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQ {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		v.reset(OpAMD64MOVBstoreconstidx1)
+		v.AuxInt = x
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGeq32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Geq32  x y)
-	// cond:
-	// result: (SETGE (CMPL x y))
+	// match: (MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {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 {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		x := v.Args[1]
+		if x.Op != OpAMD64MOVBstoreconst {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		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
 	}
-}
-func rewriteValueAMD64_OpGeq32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Geq32F x y)
-	// cond:
-	// result: (SETGEF (UCOMISS x y))
+	// 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)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGEF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAL {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpAMD64MOVBstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
+	// match: (MOVBstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	for {
+		sc := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDLconst {
+			break
+		}
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpAMD64MOVBstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
 }
-func rewriteValueAMD64_OpGeq32U(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVBstoreconstidx1(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Geq32U x y)
+	// match: (MOVBstoreconstidx1 [x] {sym} (ADDQconst [c] ptr) idx mem)
 	// cond:
-	// result: (SETAE (CMPL x y))
+	// result: (MOVBstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETAE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVBstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGeq64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Geq64  x y)
+	// match: (MOVBstoreconstidx1 [x] {sym} ptr (ADDQconst [c] idx) mem)
 	// cond:
-	// result: (SETGE (CMPQ x y))
+	// result: (MOVBstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		x := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVBstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreconstidx1 [c] {s} p i x:(MOVBstoreconstidx1 [a] {s} p i mem))
+	// cond: x.Uses == 1   && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVWstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p i mem)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		i := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVBstoreconstidx1 {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if i != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && ValAndOff(a).Off()+1 == ValAndOff(c).Off() && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVWstoreconstidx1)
+		v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xff|ValAndOff(c).Val()<<8, ValAndOff(a).Off())
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(i)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpGeq64F(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVBstoreidx1(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Geq64F x y)
+	// match: (MOVBstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
 	// cond:
-	// result: (SETGEF (UCOMISD x y))
+	// result: (MOVBstoreidx1 [c+d] {sym} ptr idx val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGEF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVBstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGeq64U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Geq64U x y)
+	// match: (MOVBstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
 	// cond:
-	// result: (SETAE (CMPQ x y))
+	// result: (MOVBstoreidx1 [c+d] {sym} ptr idx val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETAE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVBstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGeq8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Geq8   x y)
-	// cond:
-	// result: (SETGE (CMPB x y))
+	// match: (MOVBstoreidx1 [i] {s} p idx (SHRQconst [8] w) x:(MOVBstoreidx1 [i-1] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstoreidx1 [i-1] {s} p idx w mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpAMD64SHRQconst {
+			break
+		}
+		if v_2.AuxInt != 8 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpAMD64MOVBstoreidx1 {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVWstoreidx1)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVBstoreidx1 [i-1] {s} p idx w0:(SHRQconst [j-8] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstoreidx1 [i-1] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpAMD64SHRQconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpAMD64MOVBstoreidx1 {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != OpAMD64SHRQconst {
+			break
+		}
+		if w0.AuxInt != j-8 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVWstoreidx1)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpGeq8U(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLQSX(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Geq8U  x y)
-	// cond:
-	// result: (SETAE (CMPB x y))
+	// match: (MOVLQSX x:(MOVLload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVLQSXload <v.Type> [off] {sym} ptr mem)
 	for {
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETAE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
+		if x.Op != OpAMD64MOVLload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVLQSXload, v.Type)
+		v.reset(OpCopy)
 		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGetClosurePtr(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (GetClosurePtr)
-	// cond:
-	// result: (LoweredGetClosurePtr)
+	// match: (MOVLQSX x:(MOVQload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVLQSXload <v.Type> [off] {sym} ptr mem)
 	for {
-		v.reset(OpAMD64LoweredGetClosurePtr)
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVQload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVLQSXload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGetG(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (GetG mem)
-	// cond:
-	// result: (LoweredGetG mem)
+	// match: (MOVLQSX (ANDLconst [c] x))
+	// cond: c & 0x80000000 == 0
+	// result: (ANDLconst [c & 0x7fffffff] x)
 	for {
-		mem := v.Args[0]
-		v.reset(OpAMD64LoweredGetG)
-		v.AddArg(mem)
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(c&0x80000000 == 0) {
+			break
+		}
+		v.reset(OpAMD64ANDLconst)
+		v.AuxInt = c & 0x7fffffff
+		v.AddArg(x)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpGoCall(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLQSXload(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (GoCall [argwid] mem)
-	// cond:
-	// result: (CALLgo [argwid] mem)
+	// match: (MOVLQSXload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVLQSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		argwid := v.AuxInt
-		mem := v.Args[0]
-		v.reset(OpAMD64CALLgo)
-		v.AuxInt = argwid
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVLQSXload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
 		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpGreater16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Greater16  x y)
-	// cond:
-	// result: (SETG (CMPW x y))
+	// match: (MOVLQZX x:(MOVLload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVLload <v.Type> [off] {sym} ptr mem)
 	for {
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETG)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
+		if x.Op != OpAMD64MOVLload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVLload, v.Type)
+		v.reset(OpCopy)
 		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGreater16U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Greater16U x y)
-	// cond:
-	// result: (SETA (CMPW x y))
+	// match: (MOVLQZX x:(MOVQload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVLload <v.Type> [off] {sym} ptr mem)
 	for {
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETA)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
+		if x.Op != OpAMD64MOVQload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVLload, v.Type)
+		v.reset(OpCopy)
 		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGreater32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Greater32  x y)
-	// cond:
-	// result: (SETG (CMPL x y))
+	// match: (MOVLQZX x:(MOVLloadidx1 [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVLloadidx1 <v.Type> [off] {sym} ptr idx mem)
 	for {
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETG)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
+		if x.Op != OpAMD64MOVLloadidx1 {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVLloadidx1, v.Type)
+		v.reset(OpCopy)
 		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGreater32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Greater32F x y)
-	// cond:
-	// result: (SETGF (UCOMISS x y))
+	// match: (MOVLQZX x:(MOVLloadidx4 [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVLloadidx4 <v.Type> [off] {sym} ptr idx mem)
 	for {
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
+		if x.Op != OpAMD64MOVLloadidx4 {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVLloadidx4, v.Type)
+		v.reset(OpCopy)
 		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGreater32U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Greater32U x y)
+	// match: (MOVLQZX (ANDLconst [c] x))
 	// cond:
-	// result: (SETA (CMPL x y))
+	// result: (ANDLconst [c] x)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETA)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64ANDLconst)
+		v.AuxInt = c
+		v.AddArg(x)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpGreater64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLatomicload(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Greater64  x y)
-	// cond:
-	// result: (SETG (CMPQ x y))
+	// match: (MOVLatomicload [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVLatomicload [off1+off2] {sym} ptr mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETG)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVLatomicload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGreater64F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Greater64F x y)
-	// cond:
-	// result: (SETGF (UCOMISD x y))
+	// match: (MOVLatomicload [off1] {sym1} (LEAQ [off2] {sym2} ptr) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVLatomicload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVLatomicload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpGreater64U(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLload(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Greater64U x y)
-	// cond:
-	// result: (SETA (CMPQ x y))
+	// match: (MOVLload [off] {sym} ptr (MOVLstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETA)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpGreater8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Greater8   x y)
-	// cond:
-	// result: (SETG (CMPB x y))
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVLload  [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVLload  [off1+off2] {sym} ptr mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETG)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVLload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpGreater8U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Greater8U  x y)
-	// cond:
-	// result: (SETA (CMPB x y))
+	// match: (MOVLload  [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVLload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETA)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVLload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpHmul16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Hmul16  x y)
-	// cond:
-	// result: (HMULW  x y)
+	// match: (MOVLload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVLloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64HMULW)
-		v.AddArg(x)
-		v.AddArg(y)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVLloadidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpHmul16u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Hmul16u x y)
-	// cond:
-	// result: (HMULWU x y)
+	// match: (MOVLload [off1] {sym1} (LEAQ4 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVLloadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64HMULWU)
-		v.AddArg(x)
-		v.AddArg(y)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ4 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVLloadidx4)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpHmul32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Hmul32  x y)
-	// cond:
-	// result: (HMULL  x y)
+	// match: (MOVLload [off] {sym} (ADDQ ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVLloadidx1 [off] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64HMULL)
-		v.AddArg(x)
-		v.AddArg(y)
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQ {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64MOVLloadidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpHmul32u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Hmul32u x y)
-	// cond:
-	// result: (HMULLU x y)
+	// match: (MOVLload  [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVLload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64HMULLU)
-		v.AddArg(x)
-		v.AddArg(y)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVLload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpHmul64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Hmul64  x y)
-	// cond:
-	// result: (HMULQ  x y)
+	// match: (MOVLload  [off1] {sym} (ADDLconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVLload  [off1+off2] {sym} ptr mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64HMULQ)
-		v.AddArg(x)
-		v.AddArg(y)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVLload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpHmul64u(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLloadidx1(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Hmul64u x y)
+	// match: (MOVLloadidx1 [c] {sym} ptr (SHLQconst [2] idx) mem)
 	// cond:
-	// result: (HMULQU x y)
+	// result: (MOVLloadidx4 [c] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64HMULQU)
-		v.AddArg(x)
-		v.AddArg(y)
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
+			break
+		}
+		if v_1.AuxInt != 2 {
+			break
+		}
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLloadidx4)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpHmul8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Hmul8   x y)
+	// match: (MOVLloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
 	// cond:
-	// result: (HMULB  x y)
+	// result: (MOVLloadidx1 [c+d] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64HMULB)
-		v.AddArg(x)
-		v.AddArg(y)
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpHmul8u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Hmul8u  x y)
+	// match: (MOVLloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
 	// cond:
-	// result: (HMULBU x y)
+	// result: (MOVLloadidx1 [c+d] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64HMULBU)
-		v.AddArg(x)
-		v.AddArg(y)
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpITab(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLloadidx4(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (ITab (Load ptr mem))
+	// match: (MOVLloadidx4 [c] {sym} (ADDQconst [d] ptr) idx mem)
 	// cond:
-	// result: (MOVQload ptr mem)
+	// result: (MOVLloadidx4 [c+d] {sym} ptr idx mem)
 	for {
+		c := v.AuxInt
+		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpLoad {
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
+		d := v_0.AuxInt
 		ptr := v_0.Args[0]
-		mem := v_0.Args[1]
-		v.reset(OpAMD64MOVQload)
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLloadidx4)
+		v.AuxInt = c + d
+		v.Aux = sym
 		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpInterCall(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (InterCall [argwid] entry mem)
+	// match: (MOVLloadidx4 [c] {sym} ptr (ADDQconst [d] idx) mem)
 	// cond:
-	// result: (CALLinter [argwid] entry mem)
+	// result: (MOVLloadidx4 [c+4*d] {sym} ptr idx mem)
 	for {
-		argwid := v.AuxInt
-		entry := v.Args[0]
-		mem := v.Args[1]
-		v.reset(OpAMD64CALLinter)
-		v.AuxInt = argwid
-		v.AddArg(entry)
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLloadidx4)
+		v.AuxInt = c + 4*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpIsInBounds(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (IsInBounds idx len)
-	// cond:
-	// result: (SETB (CMPQ idx len))
-	for {
-		idx := v.Args[0]
-		len := v.Args[1]
-		v.reset(OpAMD64SETB)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(idx)
-		v0.AddArg(len)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpIsNonNil(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (IsNonNil p)
+	// match: (MOVLstore [off] {sym} ptr (MOVLQSX x) mem)
 	// cond:
-	// result: (SETNE (TESTQ p p))
+	// result: (MOVLstore [off] {sym} ptr x mem)
 	for {
-		p := v.Args[0]
-		v.reset(OpAMD64SETNE)
-		v0 := b.NewValue0(v.Line, OpAMD64TESTQ, TypeFlags)
-		v0.AddArg(p)
-		v0.AddArg(p)
-		v.AddArg(v0)
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLQSX {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpIsSliceInBounds(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (IsSliceInBounds idx len)
+	// match: (MOVLstore [off] {sym} ptr (MOVLQZX x) mem)
 	// cond:
-	// result: (SETBE (CMPQ idx len))
+	// result: (MOVLstore [off] {sym} ptr x mem)
 	for {
-		idx := v.Args[0]
-		len := v.Args[1]
-		v.reset(OpAMD64SETBE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(idx)
-		v0.AddArg(len)
-		v.AddArg(v0)
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLQZX {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpAMD64LEAQ(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (LEAQ [c] {s} (ADDQconst [d] x))
-	// cond: is32Bit(c+d)
-	// result: (LEAQ [c+d] {s} x)
+	// match: (MOVLstore  [off1] {sym} (ADDQconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVLstore  [off1+off2] {sym} ptr val mem)
 	for {
-		c := v.AuxInt
-		s := v.Aux
+		off1 := v.AuxInt
+		sym := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		d := v_0.AuxInt
-		x := v_0.Args[0]
-		if !(is32Bit(c + d)) {
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ)
-		v.AuxInt = c + d
-		v.Aux = s
-		v.AddArg(x)
+		v.reset(OpAMD64MOVLstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ [c] {s} (ADDQ x y))
-	// cond: x.Op != OpSB && y.Op != OpSB
-	// result: (LEAQ1 [c] {s} x y)
+	// match: (MOVLstore [off] {sym} ptr (MOVLconst [c]) mem)
+	// cond: validOff(off)
+	// result: (MOVLstoreconst [makeValAndOff(int64(int32(c)),off)] {sym} ptr mem)
 	for {
-		c := v.AuxInt
-		s := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
 			break
 		}
-		x := v_0.Args[0]
-		y := v_0.Args[1]
-		if !(x.Op != OpSB && y.Op != OpSB) {
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(validOff(off)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ1)
-		v.AuxInt = c
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
+		v.reset(OpAMD64MOVLstoreconst)
+		v.AuxInt = makeValAndOff(int64(int32(c)), off)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ [off1] {sym1} (LEAQ [off2] {sym2} x))
+	// match: (MOVLstore  [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
 	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (LEAQ [off1+off2] {mergeSym(sym1,sym2)} x)
+	// result: (MOVLstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
@@ -4202,19 +5461,23 @@ func rewriteValueAMD64_OpAMD64LEAQ(v *Value, config *Config) bool {
 		}
 		off2 := v_0.AuxInt
 		sym2 := v_0.Aux
-		x := v_0.Args[0]
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ)
+		v.reset(OpAMD64MOVLstore)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(x)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ [off1] {sym1} (LEAQ1 [off2] {sym2} x y))
+	// match: (MOVLstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
 	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (LEAQ1 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	// result: (MOVLstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
@@ -4224,45 +5487,25 @@ func rewriteValueAMD64_OpAMD64LEAQ(v *Value, config *Config) bool {
 		}
 		off2 := v_0.AuxInt
 		sym2 := v_0.Aux
-		x := v_0.Args[0]
-		y := v_0.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
-			break
-		}
-		v.reset(OpAMD64LEAQ1)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-	// match: (LEAQ [off1] {sym1} (LEAQ2 [off2] {sym2} x y))
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (LEAQ2 [off1+off2] {mergeSym(sym1,sym2)} x y)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ2 {
-			break
-		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		x := v_0.Args[0]
-		y := v_0.Args[1]
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ2)
+		v.reset(OpAMD64MOVLstoreidx1)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ [off1] {sym1} (LEAQ4 [off2] {sym2} x y))
+	// match: (MOVLstore [off1] {sym1} (LEAQ4 [off2] {sym2} ptr idx) val mem)
 	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (LEAQ4 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	// result: (MOVLstoreidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
@@ -4272,1768 +5515,1864 @@ func rewriteValueAMD64_OpAMD64LEAQ(v *Value, config *Config) bool {
 		}
 		off2 := v_0.AuxInt
 		sym2 := v_0.Aux
-		x := v_0.Args[0]
-		y := v_0.Args[1]
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ4)
+		v.reset(OpAMD64MOVLstoreidx4)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ [off1] {sym1} (LEAQ8 [off2] {sym2} x y))
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (LEAQ8 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	// match: (MOVLstore [off] {sym} (ADDQ ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVLstoreidx1 [off] {sym} ptr idx val mem)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
+		off := v.AuxInt
+		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ8 {
+		if v_0.Op != OpAMD64ADDQ {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		x := v_0.Args[0]
-		y := v_0.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
 			break
 		}
-		v.reset(OpAMD64LEAQ8)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.reset(OpAMD64MOVLstoreidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64LEAQ1(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (LEAQ1 [c] {s} (ADDQconst [d] x) y)
-	// cond: is32Bit(c+d)   && x.Op != OpSB
-	// result: (LEAQ1 [c+d] {s} x y)
+	// 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)
 	for {
-		c := v.AuxInt
+		i := v.AuxInt
 		s := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHRQconst {
 			break
 		}
-		d := v_0.AuxInt
-		x := v_0.Args[0]
-		y := v.Args[1]
-		if !(is32Bit(c+d) && x.Op != OpSB) {
+		if v_1.AuxInt != 32 {
 			break
 		}
-		v.reset(OpAMD64LEAQ1)
-		v.AuxInt = c + d
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-	// match: (LEAQ1 [c] {s} x (ADDQconst [d] y))
-	// cond: is32Bit(c+d)   && y.Op != OpSB
-	// result: (LEAQ1 [c+d] {s} x y)
-	for {
-		c := v.AuxInt
-		s := v.Aux
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVLstore {
 			break
 		}
-		d := v_1.AuxInt
-		y := v_1.Args[0]
-		if !(is32Bit(c+d) && y.Op != OpSB) {
+		if x.AuxInt != i-4 {
 			break
 		}
-		v.reset(OpAMD64LEAQ1)
-		v.AuxInt = c + d
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-	// match: (LEAQ1 [c] {s} x (SHLQconst [1] y))
-	// cond:
-	// result: (LEAQ2 [c] {s} x y)
-	for {
-		c := v.AuxInt
-		s := v.Aux
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		if x.Aux != s {
 			break
 		}
-		if v_1.AuxInt != 1 {
+		if p != x.Args[0] {
 			break
 		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64LEAQ2)
-		v.AuxInt = c
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-	// match: (LEAQ1 [c] {s} (SHLQconst [1] x) y)
-	// cond:
-	// result: (LEAQ2 [c] {s} y x)
-	for {
-		c := v.AuxInt
-		s := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64SHLQconst {
+		if w != x.Args[1] {
 			break
 		}
-		if v_0.AuxInt != 1 {
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
 			break
 		}
-		x := v_0.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64LEAQ2)
-		v.AuxInt = c
+		v.reset(OpAMD64MOVQstore)
+		v.AuxInt = i - 4
 		v.Aux = s
-		v.AddArg(y)
-		v.AddArg(x)
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ1 [c] {s} x (SHLQconst [2] y))
-	// cond:
-	// result: (LEAQ4 [c] {s} x y)
+	// 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)
 	for {
-		c := v.AuxInt
+		i := v.AuxInt
 		s := v.Aux
-		x := v.Args[0]
+		p := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		if v_1.Op != OpAMD64SHRQconst {
 			break
 		}
-		if v_1.AuxInt != 2 {
+		j := v_1.AuxInt
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVLstore {
 			break
 		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64LEAQ4)
-		v.AuxInt = c
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-	// match: (LEAQ1 [c] {s} (SHLQconst [2] x) y)
-	// cond:
-	// result: (LEAQ4 [c] {s} y x)
-	for {
-		c := v.AuxInt
-		s := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64SHLQconst {
+		if x.AuxInt != i-4 {
 			break
 		}
-		if v_0.AuxInt != 2 {
+		if x.Aux != s {
 			break
 		}
-		x := v_0.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64LEAQ4)
-		v.AuxInt = c
-		v.Aux = s
-		v.AddArg(y)
-		v.AddArg(x)
-		return true
-	}
-	// match: (LEAQ1 [c] {s} x (SHLQconst [3] y))
-	// cond:
-	// result: (LEAQ8 [c] {s} x y)
-	for {
-		c := v.AuxInt
-		s := v.Aux
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		if p != x.Args[0] {
 			break
 		}
-		if v_1.AuxInt != 3 {
+		w0 := x.Args[1]
+		if w0.Op != OpAMD64SHRQconst {
 			break
 		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64LEAQ8)
-		v.AuxInt = c
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-	// match: (LEAQ1 [c] {s} (SHLQconst [3] x) y)
-	// cond:
-	// result: (LEAQ8 [c] {s} y x)
-	for {
-		c := v.AuxInt
-		s := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64SHLQconst {
+		if w0.AuxInt != j-32 {
 			break
 		}
-		if v_0.AuxInt != 3 {
+		if w != w0.Args[0] {
 			break
 		}
-		x := v_0.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64LEAQ8)
-		v.AuxInt = c
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstore)
+		v.AuxInt = i - 4
 		v.Aux = s
-		v.AddArg(y)
-		v.AddArg(x)
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ1 [off1] {sym1} (LEAQ [off2] {sym2} x) y)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
-	// result: (LEAQ1 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	// match: (MOVLstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVLstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		if v_0.Op != OpAMD64LEAL {
 			break
 		}
 		off2 := v_0.AuxInt
 		sym2 := v_0.Aux
-		x := v_0.Args[0]
-		y := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ1)
+		v.reset(OpAMD64MOVLstore)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ1 [off1] {sym1} x (LEAQ [off2] {sym2} y))
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && y.Op != OpSB
-	// result: (LEAQ1 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	// match: (MOVLstore  [off1] {sym} (ADDLconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVLstore  [off1+off2] {sym} ptr val mem)
 	for {
 		off1 := v.AuxInt
-		sym1 := v.Aux
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64LEAQ {
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDLconst {
 			break
 		}
-		off2 := v_1.AuxInt
-		sym2 := v_1.Aux
-		y := v_1.Args[0]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && y.Op != OpSB) {
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ1)
+		v.reset(OpAMD64MOVLstore)
 		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64LEAQ2(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (LEAQ2 [c] {s} (ADDQconst [d] x) y)
-	// cond: is32Bit(c+d)   && x.Op != OpSB
-	// result: (LEAQ2 [c+d] {s} x y)
+	// match: (MOVLstoreconst [sc] {s} (ADDQconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVLstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
 	for {
-		c := v.AuxInt
+		sc := v.AuxInt
 		s := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		d := v_0.AuxInt
-		x := v_0.Args[0]
-		y := v.Args[1]
-		if !(is32Bit(c+d) && x.Op != OpSB) {
-			break
-		}
-		v.reset(OpAMD64LEAQ2)
-		v.AuxInt = c + d
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-	// match: (LEAQ2 [c] {s} x (ADDQconst [d] y))
-	// cond: is32Bit(c+2*d) && y.Op != OpSB
-	// result: (LEAQ2 [c+2*d] {s} x y)
-	for {
-		c := v.AuxInt
-		s := v.Aux
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
-			break
-		}
-		d := v_1.AuxInt
-		y := v_1.Args[0]
-		if !(is32Bit(c+2*d) && y.Op != OpSB) {
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ2)
-		v.AuxInt = c + 2*d
+		v.reset(OpAMD64MOVLstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
 		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ2 [c] {s} x (SHLQconst [1] y))
-	// cond:
-	// result: (LEAQ4 [c] {s} x y)
+	// match: (MOVLstoreconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
+	// result: (MOVLstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
 	for {
-		c := v.AuxInt
-		s := v.Aux
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
 			break
 		}
-		if v_1.AuxInt != 1 {
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
 			break
 		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64LEAQ4)
-		v.AuxInt = c
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
+		v.reset(OpAMD64MOVLstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ2 [c] {s} x (SHLQconst [2] y))
-	// cond:
-	// result: (LEAQ8 [c] {s} x y)
+	// match: (MOVLstoreconst [x] {sym1} (LEAQ1 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVLstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		c := v.AuxInt
-		s := v.Aux
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		x := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
 			break
 		}
-		if v_1.AuxInt != 2 {
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
 			break
 		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64LEAQ8)
-		v.AuxInt = c
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
+		v.reset(OpAMD64MOVLstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ2 [off1] {sym1} (LEAQ [off2] {sym2} x) y)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
-	// result: (LEAQ2 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	// match: (MOVLstoreconst [x] {sym1} (LEAQ4 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVLstoreconstidx4 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		off1 := v.AuxInt
+		x := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		if v_0.Op != OpAMD64LEAQ4 {
 			break
 		}
-		off2 := v_0.AuxInt
+		off := v_0.AuxInt
 		sym2 := v_0.Aux
-		x := v_0.Args[0]
-		y := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ2)
-		v.AuxInt = off1 + off2
+		v.reset(OpAMD64MOVLstoreconstidx4)
+		v.AuxInt = ValAndOff(x).add(off)
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64LEAQ4(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (LEAQ4 [c] {s} (ADDQconst [d] x) y)
-	// cond: is32Bit(c+d)   && x.Op != OpSB
-	// result: (LEAQ4 [c+d] {s} x y)
-	for {
-		c := v.AuxInt
-		s := v.Aux
+	// match: (MOVLstoreconst [x] {sym} (ADDQ ptr idx) mem)
+	// cond:
+	// result: (MOVLstoreconstidx1 [x] {sym} ptr idx mem)
+	for {
+		x := v.AuxInt
+		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
-			break
-		}
-		d := v_0.AuxInt
-		x := v_0.Args[0]
-		y := v.Args[1]
-		if !(is32Bit(c+d) && x.Op != OpSB) {
+		if v_0.Op != OpAMD64ADDQ {
 			break
 		}
-		v.reset(OpAMD64LEAQ4)
-		v.AuxInt = c + d
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		v.reset(OpAMD64MOVLstoreconstidx1)
+		v.AuxInt = x
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ4 [c] {s} x (ADDQconst [d] y))
-	// cond: is32Bit(c+4*d) && y.Op != OpSB
-	// result: (LEAQ4 [c+4*d] {s} x y)
+	// match: (MOVLstoreconst [c] {s} p x:(MOVLstoreconst [a] {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 {
 		c := v.AuxInt
 		s := v.Aux
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		p := v.Args[0]
+		x := v.Args[1]
+		if x.Op != OpAMD64MOVLstoreconst {
 			break
 		}
-		d := v_1.AuxInt
-		y := v_1.Args[0]
-		if !(is32Bit(c+4*d) && y.Op != OpSB) {
+		a := x.AuxInt
+		if x.Aux != s {
 			break
 		}
-		v.reset(OpAMD64LEAQ4)
-		v.AuxInt = c + 4*d
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-	// match: (LEAQ4 [c] {s} x (SHLQconst [1] y))
-	// cond:
-	// result: (LEAQ8 [c] {s} x y)
-	for {
-		c := v.AuxInt
-		s := v.Aux
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		if p != x.Args[0] {
 			break
 		}
-		if v_1.AuxInt != 1 {
+		mem := x.Args[1]
+		if !(x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
 			break
 		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64LEAQ8)
-		v.AuxInt = c
+		v.reset(OpAMD64MOVQstore)
+		v.AuxInt = ValAndOff(a).Off()
 		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
+		v0.AuxInt = ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32
+		v.AddArg(v0)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ4 [off1] {sym1} (LEAQ [off2] {sym2} x) y)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
-	// result: (LEAQ4 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	// 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)
 	for {
-		off1 := v.AuxInt
+		sc := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		if v_0.Op != OpAMD64LEAL {
 			break
 		}
-		off2 := v_0.AuxInt
+		off := v_0.AuxInt
 		sym2 := v_0.Aux
-		x := v_0.Args[0]
-		y := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ4)
-		v.AuxInt = off1 + off2
+		v.reset(OpAMD64MOVLstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64LEAQ8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (LEAQ8 [c] {s} (ADDQconst [d] x) y)
-	// cond: is32Bit(c+d)   && x.Op != OpSB
-	// result: (LEAQ8 [c+d] {s} x y)
+	// match: (MOVLstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVLstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
 	for {
-		c := v.AuxInt
+		sc := v.AuxInt
 		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v_0.Op != OpAMD64ADDLconst {
 			break
 		}
-		d := v_0.AuxInt
-		x := v_0.Args[0]
-		y := v.Args[1]
-		if !(is32Bit(c+d) && x.Op != OpSB) {
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
 			break
 		}
-		v.reset(OpAMD64LEAQ8)
-		v.AuxInt = c + d
+		v.reset(OpAMD64MOVLstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
 		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ8 [c] {s} x (ADDQconst [d] y))
-	// cond: is32Bit(c+8*d) && y.Op != OpSB
-	// result: (LEAQ8 [c+8*d] {s} x y)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVLstoreconstidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVLstoreconstidx1 [c] {sym} ptr (SHLQconst [2] idx) mem)
+	// cond:
+	// result: (MOVLstoreconstidx4 [c] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
-		s := v.Aux
-		x := v.Args[0]
+		sym := v.Aux
+		ptr := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		d := v_1.AuxInt
-		y := v_1.Args[0]
-		if !(is32Bit(c+8*d) && y.Op != OpSB) {
+		if v_1.AuxInt != 2 {
 			break
 		}
-		v.reset(OpAMD64LEAQ8)
-		v.AuxInt = c + 8*d
-		v.Aux = s
-		v.AddArg(x)
-		v.AddArg(y)
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLstoreconstidx4)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (LEAQ8 [off1] {sym1} (LEAQ [off2] {sym2} x) y)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
-	// result: (LEAQ8 [off1+off2] {mergeSym(sym1,sym2)} x y)
+	// match: (MOVLstoreconstidx1 [x] {sym} (ADDQconst [c] ptr) idx mem)
+	// cond:
+	// result: (MOVLstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
+		x := v.AuxInt
+		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
-			break
-		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		x := v_0.Args[0]
-		y := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		v.reset(OpAMD64LEAQ8)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(x)
-		v.AddArg(y)
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpLeq16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Leq16  x y)
+	// match: (MOVLstoreconstidx1 [x] {sym} ptr (ADDQconst [c] idx) mem)
 	// cond:
-	// result: (SETLE (CMPW x y))
+	// result: (MOVLstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETLE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		x := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLeq16U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Leq16U x y)
-	// cond:
-	// result: (SETBE (CMPW x y))
+	// match: (MOVLstoreconstidx1 [c] {s} p i x:(MOVLstoreconstidx1 [a] {s} p i mem))
+	// cond: x.Uses == 1   && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVQstoreidx1 [ValAndOff(a).Off()] {s} p i (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETBE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpLeq32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Leq32  x y)
-	// cond:
-	// result: (SETLE (CMPL x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETLE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		i := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVLstoreconstidx1 {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if i != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstoreidx1)
+		v.AuxInt = ValAndOff(a).Off()
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(i)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
+		v0.AuxInt = ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32
 		v.AddArg(v0)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpLeq32F(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLstoreconstidx4(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Leq32F x y)
+	// match: (MOVLstoreconstidx4 [x] {sym} (ADDQconst [c] ptr) idx mem)
 	// cond:
-	// result: (SETGEF (UCOMISS y x))
+	// result: (MOVLstoreconstidx4 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGEF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
-		v0.AddArg(y)
-		v0.AddArg(x)
-		v.AddArg(v0)
+		x := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLstoreconstidx4)
+		v.AuxInt = ValAndOff(x).add(c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLeq32U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Leq32U x y)
+	// match: (MOVLstoreconstidx4 [x] {sym} ptr (ADDQconst [c] idx) mem)
 	// cond:
-	// result: (SETBE (CMPL x y))
+	// result: (MOVLstoreconstidx4 [ValAndOff(x).add(4*c)] {sym} ptr idx mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETBE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		x := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVLstoreconstidx4)
+		v.AuxInt = ValAndOff(x).add(4 * c)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLeq64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Leq64  x y)
-	// cond:
-	// result: (SETLE (CMPQ x y))
+	// match: (MOVLstoreconstidx4 [c] {s} p i x:(MOVLstoreconstidx4 [a] {s} p i mem))
+	// cond: x.Uses == 1   && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVQstoreidx1 [ValAndOff(a).Off()] {s} p (SHLQconst <i.Type> [2] i) (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETLE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		i := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVLstoreconstidx4 {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if i != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstoreidx1)
+		v.AuxInt = ValAndOff(a).Off()
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, i.Type)
+		v0.AuxInt = 2
+		v0.AddArg(i)
 		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
+		v1.AuxInt = ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32
+		v.AddArg(v1)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpLeq64F(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLstoreidx1(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Leq64F x y)
+	// match: (MOVLstoreidx1 [c] {sym} ptr (SHLQconst [2] idx) val mem)
 	// cond:
-	// result: (SETGEF (UCOMISD y x))
+	// result: (MOVLstoreidx4 [c] {sym} ptr idx val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGEF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
-		v0.AddArg(y)
-		v0.AddArg(x)
-		v.AddArg(v0)
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
+			break
+		}
+		if v_1.AuxInt != 2 {
+			break
+		}
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVLstoreidx4)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLeq64U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Leq64U x y)
+	// match: (MOVLstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
 	// cond:
-	// result: (SETBE (CMPQ x y))
+	// result: (MOVLstoreidx1 [c+d] {sym} ptr idx val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETBE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVLstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLeq8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Leq8   x y)
+	// match: (MOVLstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
 	// cond:
-	// result: (SETLE (CMPB x y))
+	// result: (MOVLstoreidx1 [c+d] {sym} ptr idx val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETLE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVLstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLeq8U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Leq8U  x y)
-	// cond:
-	// result: (SETBE (CMPB x y))
+	// match: (MOVLstoreidx1 [i] {s} p idx (SHRQconst [32] w) x:(MOVLstoreidx1 [i-4] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVQstoreidx1 [i-4] {s} p idx w mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETBE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpAMD64SHRQconst {
+			break
+		}
+		if v_2.AuxInt != 32 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpAMD64MOVLstoreidx1 {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstoreidx1)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLess16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Less16  x y)
-	// cond:
-	// result: (SETL (CMPW x y))
+	// match: (MOVLstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVLstoreidx1 [i-4] {s} p idx w0:(SHRQconst [j-32] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVQstoreidx1 [i-4] {s} p idx w0 mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETL)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpAMD64SHRQconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpAMD64MOVLstoreidx1 {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != OpAMD64SHRQconst {
+			break
+		}
+		if w0.AuxInt != j-32 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstoreidx1)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpLess16U(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVLstoreidx4(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Less16U x y)
+	// match: (MOVLstoreidx4 [c] {sym} (ADDQconst [d] ptr) idx val mem)
 	// cond:
-	// result: (SETB (CMPW x y))
+	// result: (MOVLstoreidx4 [c+d] {sym} ptr idx val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETB)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVLstoreidx4)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLess32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Less32  x y)
+	// match: (MOVLstoreidx4 [c] {sym} ptr (ADDQconst [d] idx) val mem)
 	// cond:
-	// result: (SETL (CMPL x y))
+	// result: (MOVLstoreidx4 [c+4*d] {sym} ptr idx val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETL)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVLstoreidx4)
+		v.AuxInt = c + 4*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLess32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Less32F x y)
-	// cond:
-	// result: (SETGF (UCOMISS y x))
+	// match: (MOVLstoreidx4 [i] {s} p idx (SHRQconst [32] w) x:(MOVLstoreidx4 [i-4] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
-		v0.AddArg(y)
-		v0.AddArg(x)
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpAMD64SHRQconst {
+			break
+		}
+		if v_2.AuxInt != 32 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpAMD64MOVLstoreidx4 {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstoreidx1)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
+		v0.AuxInt = 2
+		v0.AddArg(idx)
 		v.AddArg(v0)
+		v.AddArg(w)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLess32U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Less32U x y)
-	// cond:
-	// result: (SETB (CMPL x y))
+	// match: (MOVLstoreidx4 [i] {s} p idx (SHRQconst [j] w) x:(MOVLstoreidx4 [i-4] {s} p idx w0:(SHRQconst [j-32] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w0 mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETB)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpLess64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Less64  x y)
-	// cond:
-	// result: (SETL (CMPQ x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETL)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpAMD64SHRQconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpAMD64MOVLstoreidx4 {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != OpAMD64SHRQconst {
+			break
+		}
+		if w0.AuxInt != j-32 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstoreidx1)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
+		v0.AuxInt = 2
+		v0.AddArg(idx)
 		v.AddArg(v0)
+		v.AddArg(w0)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpLess64F(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVOload(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Less64F x y)
-	// cond:
-	// result: (SETGF (UCOMISD y x))
+	// match: (MOVOload  [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVOload  [off1+off2] {sym} ptr mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETGF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
-		v0.AddArg(y)
-		v0.AddArg(x)
-		v.AddArg(v0)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVOload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLess64U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Less64U x y)
-	// cond:
-	// result: (SETB (CMPQ x y))
+	// match: (MOVOload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVOload [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETB)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVOload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpLess8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVOstore(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Less8   x y)
-	// cond:
-	// result: (SETL (CMPB x y))
+	// match: (MOVOstore  [off1] {sym} (ADDQconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVOstore  [off1+off2] {sym} ptr val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETL)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVOstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLess8U(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Less8U  x y)
-	// cond:
-	// result: (SETB (CMPB x y))
+	// match: (MOVOstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVOstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
 	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETB)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVOstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpLoad(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVQatomicload(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Load <t> ptr mem)
-	// cond: (is64BitInt(t) || isPtr(t))
-	// result: (MOVQload ptr mem)
+	// match: (MOVQatomicload [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVQatomicload [off1+off2] {sym} ptr mem)
 	for {
-		t := v.Type
-		ptr := v.Args[0]
-		mem := v.Args[1]
-		if !(is64BitInt(t) || isPtr(t)) {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		v.reset(OpAMD64MOVQload)
-		v.AddArg(ptr)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (Load <t> ptr mem)
-	// cond: is32BitInt(t)
-	// result: (MOVLload ptr mem)
-	for {
-		t := v.Type
-		ptr := v.Args[0]
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
 		mem := v.Args[1]
-		if !(is32BitInt(t)) {
+		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		v.reset(OpAMD64MOVLload)
+		v.reset(OpAMD64MOVQatomicload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (Load <t> ptr mem)
-	// cond: is16BitInt(t)
-	// result: (MOVWload ptr mem)
+	// match: (MOVQatomicload [off1] {sym1} (LEAQ [off2] {sym2} ptr) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVQatomicload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
 	for {
-		t := v.Type
-		ptr := v.Args[0]
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
 		mem := v.Args[1]
-		if !(is16BitInt(t)) {
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVWload)
+		v.reset(OpAMD64MOVQatomicload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (Load <t> ptr mem)
-	// cond: (t.IsBoolean() || is8BitInt(t))
-	// result: (MOVBload ptr mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVQload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVQload [off] {sym} ptr (MOVQstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
 	for {
-		t := v.Type
+		off := v.AuxInt
+		sym := v.Aux
 		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVQload  [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVQload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
 		mem := v.Args[1]
-		if !(t.IsBoolean() || is8BitInt(t)) {
+		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		v.reset(OpAMD64MOVBload)
+		v.reset(OpAMD64MOVQload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (Load <t> ptr mem)
-	// cond: is32BitFloat(t)
-	// result: (MOVSSload ptr mem)
+	// match: (MOVQload  [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVQload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		t := v.Type
-		ptr := v.Args[0]
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
 		mem := v.Args[1]
-		if !(is32BitFloat(t)) {
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVSSload)
+		v.reset(OpAMD64MOVQload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVQload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVQloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVQloadidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (Load <t> ptr mem)
-	// cond: is64BitFloat(t)
-	// result: (MOVSDload ptr mem)
+	// match: (MOVQload [off1] {sym1} (LEAQ8 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVQloadidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		t := v.Type
-		ptr := v.Args[0]
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ8 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
 		mem := v.Args[1]
-		if !(is64BitFloat(t)) {
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVSDload)
+		v.reset(OpAMD64MOVQloadidx8)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpLrot16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lrot16 <t> x [c])
-	// cond:
-	// result: (ROLWconst <t> [c&15] x)
+	// match: (MOVQload [off] {sym} (ADDQ ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVQloadidx1 [off] {sym} ptr idx mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		c := v.AuxInt
-		v.reset(OpAMD64ROLWconst)
-		v.Type = t
-		v.AuxInt = c & 15
-		v.AddArg(x)
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQ {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64MOVQloadidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLrot32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lrot32 <t> x [c])
-	// cond:
-	// result: (ROLLconst <t> [c&31] x)
+	// match: (MOVQload  [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVQload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		c := v.AuxInt
-		v.reset(OpAMD64ROLLconst)
-		v.Type = t
-		v.AuxInt = c & 31
-		v.AddArg(x)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVQload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLrot64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lrot64 <t> x [c])
-	// cond:
-	// result: (ROLQconst <t> [c&63] x)
+	// match: (MOVQload  [off1] {sym} (ADDLconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVQload  [off1+off2] {sym} ptr mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		c := v.AuxInt
-		v.reset(OpAMD64ROLQconst)
-		v.Type = t
-		v.AuxInt = c & 63
-		v.AddArg(x)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVQload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpLrot8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVQloadidx1(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Lrot8  <t> x [c])
+	// match: (MOVQloadidx1 [c] {sym} ptr (SHLQconst [3] idx) mem)
 	// cond:
-	// result: (ROLBconst <t> [c&7] x)
+	// result: (MOVQloadidx8 [c] {sym} ptr idx mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
 		c := v.AuxInt
-		v.reset(OpAMD64ROLBconst)
-		v.Type = t
-		v.AuxInt = c & 7
-		v.AddArg(x)
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
+			break
+		}
+		if v_1.AuxInt != 3 {
+			break
+		}
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVQloadidx8)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLsh16x16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh16x16 <t> x y)
+	// match: (MOVQloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
 	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+	// result: (MOVQloadidx1 [c+d] {sym} ptr idx mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVQloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLsh16x32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh16x32 <t> x y)
+	// match: (MOVQloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
 	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+	// result: (MOVQloadidx1 [c+d] {sym} ptr idx mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVQloadidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpLsh16x64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVQloadidx8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Lsh16x64 <t> x y)
+	// match: (MOVQloadidx8 [c] {sym} (ADDQconst [d] ptr) idx mem)
 	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
+	// result: (MOVQloadidx8 [c+d] {sym} ptr idx mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVQloadidx8)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLsh16x8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh16x8  <t> x y)
+	// match: (MOVQloadidx8 [c] {sym} ptr (ADDQconst [d] idx) mem)
 	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+	// result: (MOVQloadidx8 [c+8*d] {sym} ptr idx mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVQloadidx8)
+		v.AuxInt = c + 8*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpLsh32x16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVQstore(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Lsh32x16 <t> x y)
-	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+	// match: (MOVQstore  [off1] {sym} (ADDQconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVQstore  [off1+off2] {sym} ptr val mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLsh32x32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh32x32 <t> x y)
-	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+	// match: (MOVQstore [off] {sym} ptr (MOVQconst [c]) mem)
+	// cond: validValAndOff(c,off)
+	// result: (MOVQstoreconst [makeValAndOff(c,off)] {sym} ptr mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(validValAndOff(c, off)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstoreconst)
+		v.AuxInt = makeValAndOff(c, off)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLsh32x64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh32x64 <t> x y)
-	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
+	// match: (MOVQstore  [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVQstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
-		return true
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
 	}
-}
-func rewriteValueAMD64_OpLsh32x8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh32x8  <t> x y)
-	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+	// match: (MOVQstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVQstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstoreidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLsh64x16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh64x16 <t> x y)
-	// cond:
-	// result: (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPWconst y [64])))
+	// match: (MOVQstore [off1] {sym1} (LEAQ8 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVQstoreidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDQ)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQ, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 64
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ8 {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstoreidx8)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLsh64x32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh64x32 <t> x y)
-	// cond:
-	// result: (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPLconst y [64])))
+	// match: (MOVQstore [off] {sym} (ADDQ ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVQstoreidx1 [off] {sym} ptr idx val mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDQ)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQ, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 64
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQ {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64MOVQstoreidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLsh64x64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh64x64 <t> x y)
-	// cond:
-	// result: (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPQconst y [64])))
+	// match: (MOVQstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVQstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDQ)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQ, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 64
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-}
-func rewriteValueAMD64_OpLsh64x8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh64x8  <t> x y)
-	// cond:
-	// result: (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPBconst y [64])))
+	// match: (MOVQstore  [off1] {sym} (ADDLconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVQstore  [off1+off2] {sym} ptr val mem)
 	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDQ)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQ, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 64
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDLconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVQstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
+	return false
 }
-func rewriteValueAMD64_OpLsh8x16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVQstoreconst(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Lsh8x16 <t> x y)
-	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
-	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
-		return true
-	}
-}
-func rewriteValueAMD64_OpLsh8x32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh8x32 <t> x y)
-	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
-	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
-		return true
-	}
-}
-func rewriteValueAMD64_OpLsh8x64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh8x64 <t> x y)
-	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
-	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
-		return true
-	}
-}
-func rewriteValueAMD64_OpLsh8x8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Lsh8x8  <t> x y)
-	// cond:
-	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
-	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
-		return true
-	}
-}
-func rewriteValueAMD64_OpAMD64MOVBQSX(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVBQSX x:(MOVBload [off] {sym} ptr mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVBQSXload <v.Type> [off] {sym} ptr mem)
-	for {
-		x := v.Args[0]
-		if x.Op != OpAMD64MOVBload {
-			break
-		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		mem := x.Args[1]
-		if !(x.Uses == 1 && clobber(x)) {
-			break
-		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVBQSXload, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(mem)
-		return true
-	}
-	// match: (MOVBQSX (ANDLconst [c] x))
-	// cond: c & 0x80 == 0
-	// result: (ANDLconst [c & 0x7f] x)
+	// match: (MOVQstoreconst [sc] {s} (ADDQconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVQstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
 	for {
+		sc := v.AuxInt
+		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDLconst {
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		c := v_0.AuxInt
-		x := v_0.Args[0]
-		if !(c&0x80 == 0) {
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
 			break
 		}
-		v.reset(OpAMD64ANDLconst)
-		v.AuxInt = c & 0x7f
-		v.AddArg(x)
+		v.reset(OpAMD64MOVQstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVBQSXload(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVBQSXload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVBQSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	// match: (MOVQstoreconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
+	// result: (MOVQstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
 	for {
-		off1 := v.AuxInt
+		sc := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64LEAQ {
 			break
 		}
-		off2 := v_0.AuxInt
+		off := v_0.AuxInt
 		sym2 := v_0.Aux
-		base := v_0.Args[0]
+		ptr := v_0.Args[0]
 		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
 			break
 		}
-		v.reset(OpAMD64MOVBQSXload)
-		v.AuxInt = off1 + off2
+		v.reset(OpAMD64MOVQstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
+		v.AddArg(ptr)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVBQZX(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVBQZX x:(MOVBload [off] {sym} ptr mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
+	// match: (MOVQstoreconst [x] {sym1} (LEAQ1 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVQstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		x := v.Args[0]
-		if x.Op != OpAMD64MOVBload {
+		x := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
 			break
 		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		mem := x.Args[1]
-		if !(x.Uses == 1 && clobber(x)) {
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
 			break
 		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(mem)
+		v.reset(OpAMD64MOVQstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBQZX x:(MOVBloadidx1 [off] {sym} ptr idx mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVBloadidx1 <v.Type> [off] {sym} ptr idx mem)
+	// match: (MOVQstoreconst [x] {sym1} (LEAQ8 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVQstoreconstidx8 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		x := v.Args[0]
-		if x.Op != OpAMD64MOVBloadidx1 {
+		x := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ8 {
 			break
 		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		idx := x.Args[1]
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
 			break
 		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVBloadidx1, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(idx)
-		v0.AddArg(mem)
+		v.reset(OpAMD64MOVQstoreconstidx8)
+		v.AuxInt = ValAndOff(x).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBQZX (ANDLconst [c] x))
+	// match: (MOVQstoreconst [x] {sym} (ADDQ ptr idx) mem)
 	// cond:
-	// result: (ANDLconst [c & 0xff] x)
+	// result: (MOVQstoreconstidx1 [x] {sym} ptr idx mem)
 	for {
+		x := v.AuxInt
+		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDLconst {
+		if v_0.Op != OpAMD64ADDQ {
 			break
 		}
-		c := v_0.AuxInt
-		x := v_0.Args[0]
-		v.reset(OpAMD64ANDLconst)
-		v.AuxInt = c & 0xff
-		v.AddArg(x)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVBload(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _))
-	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
-	// result: x
-	for {
-		off := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVBstore {
-			break
-		}
-		off2 := v_1.AuxInt
-		sym2 := v_1.Aux
-		ptr2 := v_1.Args[0]
-		x := v_1.Args[1]
-		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
-			break
-		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (MOVBload  [off1] {sym} (ADDQconst [off2] ptr) mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVBload  [off1+off2] {sym} ptr mem)
-	for {
-		off1 := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
-			break
-		}
-		off2 := v_0.AuxInt
 		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
 		mem := v.Args[1]
-		if !(is32Bit(off1 + off2)) {
-			break
-		}
-		v.reset(OpAMD64MOVBload)
-		v.AuxInt = off1 + off2
+		v.reset(OpAMD64MOVQstoreconstidx1)
+		v.AuxInt = x
 		v.Aux = sym
 		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBload  [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVBload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	// match: (MOVQstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
+	// result: (MOVQstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
 	for {
-		off1 := v.AuxInt
+		sc := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		if v_0.Op != OpAMD64LEAL {
 			break
 		}
-		off2 := v_0.AuxInt
+		off := v_0.AuxInt
 		sym2 := v_0.Aux
-		base := v_0.Args[0]
+		ptr := v_0.Args[0]
 		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
 			break
 		}
-		v.reset(OpAMD64MOVBload)
-		v.AuxInt = off1 + off2
+		v.reset(OpAMD64MOVQstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
+		v.AddArg(ptr)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVBloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	// match: (MOVQstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVQstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
+		sc := v.AuxInt
+		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
+		if v_0.Op != OpAMD64ADDLconst {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
+		off := v_0.AuxInt
 		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
 		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if !(ValAndOff(sc).canAdd(off)) {
 			break
 		}
-		v.reset(OpAMD64MOVBloadidx1)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
+		v.reset(OpAMD64MOVQstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
 		v.AddArg(ptr)
-		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBload [off] {sym} (ADDQ ptr idx) mem)
-	// cond: ptr.Op != OpSB
-	// result: (MOVBloadidx1 [off] {sym} ptr idx mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVQstoreconstidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVQstoreconstidx1 [c] {sym} ptr (SHLQconst [3] idx) mem)
+	// cond:
+	// result: (MOVQstoreconstidx8 [c] {sym} ptr idx mem)
 	for {
-		off := v.AuxInt
+		c := v.AuxInt
 		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(ptr.Op != OpSB) {
+		if v_1.AuxInt != 3 {
 			break
 		}
-		v.reset(OpAMD64MOVBloadidx1)
-		v.AuxInt = off
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVQstoreconstidx8)
+		v.AuxInt = c
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVBloadidx1(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVBloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
+	// match: (MOVQstoreconstidx1 [x] {sym} (ADDQconst [c] ptr) idx mem)
 	// cond:
-	// result: (MOVBloadidx1 [c+d] {sym} ptr idx mem)
+	// result: (MOVQstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		c := v.AuxInt
+		x := v.AuxInt
 		sym := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		d := v_0.AuxInt
+		c := v_0.AuxInt
 		ptr := v_0.Args[0]
 		idx := v.Args[1]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVBloadidx1)
-		v.AuxInt = c + d
+		v.reset(OpAMD64MOVQstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
+	// match: (MOVQstoreconstidx1 [x] {sym} ptr (ADDQconst [c] idx) mem)
 	// cond:
-	// result: (MOVBloadidx1 [c+d] {sym} ptr idx mem)
+	// result: (MOVQstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		c := v.AuxInt
+		x := v.AuxInt
 		sym := v.Aux
 		ptr := v.Args[0]
 		v_1 := v.Args[1]
 		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		d := v_1.AuxInt
+		c := v_1.AuxInt
 		idx := v_1.Args[0]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVBloadidx1)
-		v.AuxInt = c + d
+		v.reset(OpAMD64MOVQstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
@@ -6042,172 +7381,154 @@ func rewriteValueAMD64_OpAMD64MOVBloadidx1(v *Value, config *Config) bool {
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVBstore(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVQstoreconstidx8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVBstore [off] {sym} ptr (MOVBQSX x) mem)
+	// match: (MOVQstoreconstidx8 [x] {sym} (ADDQconst [c] ptr) idx mem)
 	// cond:
-	// result: (MOVBstore [off] {sym} ptr x mem)
+	// result: (MOVQstoreconstidx8 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		off := v.AuxInt
+		x := v.AuxInt
 		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVBQSX {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		x := v_1.Args[0]
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVBstore)
-		v.AuxInt = off
+		v.reset(OpAMD64MOVQstoreconstidx8)
+		v.AuxInt = ValAndOff(x).add(c)
 		v.Aux = sym
 		v.AddArg(ptr)
-		v.AddArg(x)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstore [off] {sym} ptr (MOVBQZX x) mem)
+	// match: (MOVQstoreconstidx8 [x] {sym} ptr (ADDQconst [c] idx) mem)
 	// cond:
-	// result: (MOVBstore [off] {sym} ptr x mem)
+	// result: (MOVQstoreconstidx8 [ValAndOff(x).add(8*c)] {sym} ptr idx mem)
 	for {
-		off := v.AuxInt
+		x := v.AuxInt
 		sym := v.Aux
 		ptr := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVBQZX {
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		x := v_1.Args[0]
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVBstore)
-		v.AuxInt = off
+		v.reset(OpAMD64MOVQstoreconstidx8)
+		v.AuxInt = ValAndOff(x).add(8 * c)
 		v.Aux = sym
 		v.AddArg(ptr)
-		v.AddArg(x)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstore  [off1] {sym} (ADDQconst [off2] ptr) val mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVBstore  [off1+off2] {sym} ptr val mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVQstoreidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVQstoreidx1 [c] {sym} ptr (SHLQconst [3] idx) val mem)
+	// cond:
+	// result: (MOVQstoreidx8 [c] {sym} ptr idx val mem)
 	for {
-		off1 := v.AuxInt
+		c := v.AuxInt
 		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		off2 := v_0.AuxInt
-		ptr := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1 + off2)) {
+		if v_1.AuxInt != 3 {
 			break
 		}
-		v.reset(OpAMD64MOVBstore)
-		v.AuxInt = off1 + off2
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVQstoreidx8)
+		v.AuxInt = c
 		v.Aux = sym
 		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstore [off] {sym} ptr (MOVLconst [c]) mem)
-	// cond: validOff(off)
-	// result: (MOVBstoreconst [makeValAndOff(int64(int8(c)),off)] {sym} ptr mem)
+	// match: (MOVQstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVQstoreidx1 [c+d] {sym} ptr idx val mem)
 	for {
-		off := v.AuxInt
+		c := v.AuxInt
 		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		mem := v.Args[2]
-		if !(validOff(off)) {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		v.reset(OpAMD64MOVBstoreconst)
-		v.AuxInt = makeValAndOff(int64(int8(c)), off)
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVQstoreidx1)
+		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
 		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)
+	// match: (MOVQstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// cond:
+	// result: (MOVQstoreidx1 [c+d] {sym} ptr idx val mem)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
-			break
-		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		base := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
-			break
-		}
-		v.reset(OpAMD64MOVBstore)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVBstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVBstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
-			break
-		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		v.reset(OpAMD64MOVBstoreidx1)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVQstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstore [off] {sym} (ADDQ ptr idx) val mem)
-	// cond: ptr.Op != OpSB
-	// result: (MOVBstoreidx1 [off] {sym} ptr idx val mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVQstoreidx8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVQstoreidx8 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVQstoreidx8 [c+d] {sym} ptr idx val mem)
 	for {
-		off := v.AuxInt
+		c := v.AuxInt
 		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
+		d := v_0.AuxInt
 		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(ptr.Op != OpSB) {
-			break
-		}
-		v.reset(OpAMD64MOVBstoreidx1)
-		v.AuxInt = off
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVQstoreidx8)
+		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
@@ -6215,316 +7536,239 @@ func rewriteValueAMD64_OpAMD64MOVBstore(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstore [i] {s} p (SHRQconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVWstore [i-1] {s} p w mem)
-	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHRQconst {
-			break
-		}
-		if v_1.AuxInt != 8 {
-			break
-		}
-		w := v_1.Args[0]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVBstore {
-			break
-		}
-		if x.AuxInt != i-1 {
-			break
-		}
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
-			break
-		}
-		if w != x.Args[1] {
-			break
-		}
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
-			break
-		}
-		v.reset(OpAMD64MOVWstore)
-		v.AuxInt = i - 1
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(w)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVBstore [i] {s} p (SHRQconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SHRQconst [j-8] w) mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVWstore [i-1] {s} p w0 mem)
+	// match: (MOVQstoreidx8 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// cond:
+	// result: (MOVQstoreidx8 [c+8*d] {sym} ptr idx val mem)
 	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHRQconst {
-			break
-		}
-		j := v_1.AuxInt
-		w := v_1.Args[0]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVBstore {
-			break
-		}
-		if x.AuxInt != i-1 {
-			break
-		}
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
-			break
-		}
-		w0 := x.Args[1]
-		if w0.Op != OpAMD64SHRQconst {
-			break
-		}
-		if w0.AuxInt != j-8 {
-			break
-		}
-		if w != w0.Args[0] {
-			break
-		}
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		v.reset(OpAMD64MOVWstore)
-		v.AuxInt = i - 1
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(w0)
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVQstoreidx8)
+		v.AuxInt = c + 8*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVBstoreconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVSDload(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVBstoreconst [sc] {s} (ADDQconst [off] ptr) mem)
-	// cond: ValAndOff(sc).canAdd(off)
-	// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	// match: (MOVSDload [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVSDload [off1+off2] {sym} ptr mem)
 	for {
-		sc := v.AuxInt
-		s := v.Aux
+		off1 := v.AuxInt
+		sym := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		off := v_0.AuxInt
+		off2 := v_0.AuxInt
 		ptr := v_0.Args[0]
 		mem := v.Args[1]
-		if !(ValAndOff(sc).canAdd(off)) {
+		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		v.reset(OpAMD64MOVBstoreconst)
-		v.AuxInt = ValAndOff(sc).add(off)
-		v.Aux = s
+		v.reset(OpAMD64MOVSDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstoreconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem)
-	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
-	// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+	// match: (MOVSDload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSDload [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		sc := v.AuxInt
+		off1 := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64LEAQ {
 			break
 		}
-		off := v_0.AuxInt
+		off2 := v_0.AuxInt
 		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
+		base := v_0.Args[0]
 		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVBstoreconst)
-		v.AuxInt = ValAndOff(sc).add(off)
+		v.reset(OpAMD64MOVSDload)
+		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
+		v.AddArg(base)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstoreconst [x] {sym1} (LEAQ1 [off] {sym2} ptr idx) mem)
-	// cond: canMergeSym(sym1, sym2)
-	// result: (MOVBstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
+	// match: (MOVSDload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSDloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		x := v.AuxInt
+		off1 := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64LEAQ1 {
 			break
 		}
-		off := v_0.AuxInt
+		off2 := v_0.AuxInt
 		sym2 := v_0.Aux
 		ptr := v_0.Args[0]
 		idx := v_0.Args[1]
 		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2)) {
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVBstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(off)
+		v.reset(OpAMD64MOVSDloadidx1)
+		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstoreconst [x] {sym} (ADDQ ptr idx) mem)
-	// cond:
-	// result: (MOVBstoreconstidx1 [x] {sym} ptr idx mem)
+	// match: (MOVSDload [off1] {sym1} (LEAQ8 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSDloadidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		x := v.AuxInt
-		sym := v.Aux
+		off1 := v.AuxInt
+		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
+		if v_0.Op != OpAMD64LEAQ8 {
 			break
 		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
 		ptr := v_0.Args[0]
 		idx := v_0.Args[1]
 		mem := v.Args[1]
-		v.reset(OpAMD64MOVBstoreconstidx1)
-		v.AuxInt = x
-		v.Aux = sym
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVSDloadidx8)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {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)
+	// match: (MOVSDload [off] {sym} (ADDQ ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVSDloadidx1 [off] {sym} ptr idx mem)
 	for {
-		c := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		x := v.Args[1]
-		if x.Op != OpAMD64MOVBstoreconst {
-			break
-		}
-		a := x.AuxInt
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQ {
 			break
 		}
-		mem := x.Args[1]
-		if !(x.Uses == 1 && ValAndOff(a).Off()+1 == ValAndOff(c).Off() && clobber(x)) {
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
 			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.reset(OpAMD64MOVSDloadidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVBstoreconstidx1(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVSDloadidx1(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVBstoreconstidx1 [x] {sym} (ADDQconst [c] ptr) idx mem)
+	// match: (MOVSDloadidx1 [c] {sym} ptr (SHLQconst [3] idx) mem)
 	// cond:
-	// result: (MOVBstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	// result: (MOVSDloadidx8 [c] {sym} ptr idx mem)
 	for {
-		x := v.AuxInt
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
+			break
+		}
+		if v_1.AuxInt != 3 {
+			break
+		}
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVSDloadidx8)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVSDloadidx1 [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
 		sym := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		c := v_0.AuxInt
+		d := v_0.AuxInt
 		ptr := v_0.Args[0]
 		idx := v.Args[1]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVBstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(c)
+		v.reset(OpAMD64MOVSDloadidx1)
+		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstoreconstidx1 [x] {sym} ptr (ADDQconst [c] idx) mem)
+	// match: (MOVSDloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
 	// cond:
-	// result: (MOVBstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	// result: (MOVSDloadidx1 [c+d] {sym} ptr idx mem)
 	for {
-		x := v.AuxInt
+		c := v.AuxInt
 		sym := v.Aux
 		ptr := v.Args[0]
 		v_1 := v.Args[1]
 		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		c := v_1.AuxInt
+		d := v_1.AuxInt
 		idx := v_1.Args[0]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVBstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(c)
+		v.reset(OpAMD64MOVSDloadidx1)
+		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstoreconstidx1 [c] {s} p i x:(MOVBstoreconstidx1 [a] {s} p i mem))
-	// cond: x.Uses == 1   && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()   && clobber(x)
-	// result: (MOVWstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p i mem)
-	for {
-		c := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		i := v.Args[1]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVBstoreconstidx1 {
-			break
-		}
-		a := x.AuxInt
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
-			break
-		}
-		if i != x.Args[1] {
-			break
-		}
-		mem := x.Args[2]
-		if !(x.Uses == 1 && ValAndOff(a).Off()+1 == ValAndOff(c).Off() && clobber(x)) {
-			break
-		}
-		v.reset(OpAMD64MOVWstoreconstidx1)
-		v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xff|ValAndOff(c).Val()<<8, ValAndOff(a).Off())
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(i)
-		v.AddArg(mem)
-		return true
-	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVBstoreidx1(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVSDloadidx8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVBstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// match: (MOVSDloadidx8 [c] {sym} (ADDQconst [d] ptr) idx mem)
 	// cond:
-	// result: (MOVBstoreidx1 [c+d] {sym} ptr idx val mem)
+	// result: (MOVSDloadidx8 [c+d] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -6535,20 +7779,18 @@ func rewriteValueAMD64_OpAMD64MOVBstoreidx1(v *Value, config *Config) bool {
 		d := v_0.AuxInt
 		ptr := v_0.Args[0]
 		idx := v.Args[1]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVBstoreidx1)
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVSDloadidx8)
 		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
-		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// match: (MOVSDloadidx8 [c] {sym} ptr (ADDQconst [d] idx) mem)
 	// cond:
-	// result: (MOVBstoreidx1 [c+d] {sym} ptr idx val mem)
+	// result: (MOVSDloadidx8 [c+8*d] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -6559,174 +7801,316 @@ func rewriteValueAMD64_OpAMD64MOVBstoreidx1(v *Value, config *Config) bool {
 		}
 		d := v_1.AuxInt
 		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVBstoreidx1)
-		v.AuxInt = c + d
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVSDloadidx8)
+		v.AuxInt = c + 8*d
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
-		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstoreidx1 [i] {s} p idx (SHRQconst [8] w) x:(MOVBstoreidx1 [i-1] {s} p idx w mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVWstoreidx1 [i-1] {s} p idx w mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVSDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSDstore [off1] {sym} (ADDQconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVSDstore [off1+off2] {sym} ptr val mem)
 	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		idx := v.Args[1]
-		v_2 := v.Args[2]
-		if v_2.Op != OpAMD64SHRQconst {
-			break
-		}
-		if v_2.AuxInt != 8 {
-			break
-		}
-		w := v_2.Args[0]
-		x := v.Args[3]
-		if x.Op != OpAMD64MOVBstoreidx1 {
-			break
-		}
-		if x.AuxInt != i-1 {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		if x.Aux != s {
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		if p != x.Args[0] {
+		v.reset(OpAMD64MOVSDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
 			break
 		}
-		if idx != x.Args[1] {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		if w != x.Args[2] {
+		v.reset(OpAMD64MOVSDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSDstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
 			break
 		}
-		mem := x.Args[3]
-		if !(x.Uses == 1 && clobber(x)) {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVWstoreidx1)
-		v.AuxInt = i - 1
-		v.Aux = s
-		v.AddArg(p)
+		v.reset(OpAMD64MOVSDstoreidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
 		v.AddArg(idx)
-		v.AddArg(w)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVBstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVBstoreidx1 [i-1] {s} p idx w0:(SHRQconst [j-8] w) mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVWstoreidx1 [i-1] {s} p idx w0 mem)
+	// match: (MOVSDstore [off1] {sym1} (LEAQ8 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSDstoreidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
 	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		idx := v.Args[1]
-		v_2 := v.Args[2]
-		if v_2.Op != OpAMD64SHRQconst {
-			break
-		}
-		j := v_2.AuxInt
-		w := v_2.Args[0]
-		x := v.Args[3]
-		if x.Op != OpAMD64MOVBstoreidx1 {
-			break
-		}
-		if x.AuxInt != i-1 {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ8 {
 			break
 		}
-		if x.Aux != s {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		if p != x.Args[0] {
+		v.reset(OpAMD64MOVSDstoreidx8)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstore [off] {sym} (ADDQ ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVSDstoreidx1 [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQ {
 			break
 		}
-		if idx != x.Args[1] {
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
 			break
 		}
-		w0 := x.Args[2]
-		if w0.Op != OpAMD64SHRQconst {
+		v.reset(OpAMD64MOVSDstoreidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVSDstoreidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSDstoreidx1 [c] {sym} ptr (SHLQconst [3] idx) val mem)
+	// cond:
+	// result: (MOVSDstoreidx8 [c] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		if w0.AuxInt != j-8 {
+		if v_1.AuxInt != 3 {
 			break
 		}
-		if w != w0.Args[0] {
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVSDstoreidx8)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVSDstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		mem := x.Args[3]
-		if !(x.Uses == 1 && clobber(x)) {
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVSDstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// cond:
+	// result: (MOVSDstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		v.reset(OpAMD64MOVWstoreidx1)
-		v.AuxInt = i - 1
-		v.Aux = s
-		v.AddArg(p)
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVSDstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
 		v.AddArg(idx)
-		v.AddArg(w0)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVLQSX(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVSDstoreidx8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVLQSX x:(MOVLload [off] {sym} ptr mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVLQSXload <v.Type> [off] {sym} ptr mem)
+	// match: (MOVSDstoreidx8 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVSDstoreidx8 [c+d] {sym} ptr idx val mem)
 	for {
-		x := v.Args[0]
-		if x.Op != OpAMD64MOVLload {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		mem := x.Args[1]
-		if !(x.Uses == 1 && clobber(x)) {
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVSDstoreidx8)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSDstoreidx8 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// cond:
+	// result: (MOVSDstoreidx8 [c+8*d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVLQSXload, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(mem)
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVSDstoreidx8)
+		v.AuxInt = c + 8*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLQSX (ANDLconst [c] x))
-	// cond: c & 0x80000000 == 0
-	// result: (ANDLconst [c & 0x7fffffff] x)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVSSload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSSload [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVSSload [off1+off2] {sym} ptr mem)
 	for {
+		off1 := v.AuxInt
+		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDLconst {
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		c := v_0.AuxInt
-		x := v_0.Args[0]
-		if !(c&0x80000000 == 0) {
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		v.reset(OpAMD64ANDLconst)
-		v.AuxInt = c & 0x7fffffff
-		v.AddArg(x)
+		v.reset(OpAMD64MOVSSload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVLQSXload(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVLQSXload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// match: (MOVSSload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
 	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVLQSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	// result: (MOVSSload [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
@@ -6741,195 +8125,21 @@ func rewriteValueAMD64_OpAMD64MOVLQSXload(v *Value, config *Config) bool {
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVLQSXload)
+		v.reset(OpAMD64MOVSSload)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(base)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVLQZX x:(MOVLload [off] {sym} ptr mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVLload <v.Type> [off] {sym} ptr mem)
+	// match: (MOVSSload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSSloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		x := v.Args[0]
-		if x.Op != OpAMD64MOVLload {
-			break
-		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		mem := x.Args[1]
-		if !(x.Uses == 1 && clobber(x)) {
-			break
-		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVLload, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(mem)
-		return true
-	}
-	// match: (MOVLQZX x:(MOVLloadidx1 [off] {sym} ptr idx mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVLloadidx1 <v.Type> [off] {sym} ptr idx mem)
-	for {
-		x := v.Args[0]
-		if x.Op != OpAMD64MOVLloadidx1 {
-			break
-		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		idx := x.Args[1]
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
-			break
-		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVLloadidx1, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(idx)
-		v0.AddArg(mem)
-		return true
-	}
-	// match: (MOVLQZX x:(MOVLloadidx4 [off] {sym} ptr idx mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVLloadidx4 <v.Type> [off] {sym} ptr idx mem)
-	for {
-		x := v.Args[0]
-		if x.Op != OpAMD64MOVLloadidx4 {
-			break
-		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		idx := x.Args[1]
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
-			break
-		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVLloadidx4, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(idx)
-		v0.AddArg(mem)
-		return true
-	}
-	// match: (MOVLQZX (ANDLconst [c] x))
-	// cond:
-	// result: (ANDLconst [c] x)
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDLconst {
-			break
-		}
-		c := v_0.AuxInt
-		x := v_0.Args[0]
-		v.reset(OpAMD64ANDLconst)
-		v.AuxInt = c
-		v.AddArg(x)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVLload(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVLload [off] {sym} ptr (MOVLstore [off2] {sym2} ptr2 x _))
-	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
-	// result: x
-	for {
-		off := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLstore {
-			break
-		}
-		off2 := v_1.AuxInt
-		sym2 := v_1.Aux
-		ptr2 := v_1.Args[0]
-		x := v_1.Args[1]
-		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
-			break
-		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (MOVLload  [off1] {sym} (ADDQconst [off2] ptr) mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVLload  [off1+off2] {sym} ptr mem)
-	for {
-		off1 := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
-			break
-		}
-		off2 := v_0.AuxInt
-		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(is32Bit(off1 + off2)) {
-			break
-		}
-		v.reset(OpAMD64MOVLload)
-		v.AuxInt = off1 + off2
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVLload  [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVLload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
-			break
-		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		base := v_0.Args[0]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
-			break
-		}
-		v.reset(OpAMD64MOVLload)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVLload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVLloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
 			break
 		}
 		off2 := v_0.AuxInt
@@ -6940,7 +8150,7 @@ func rewriteValueAMD64_OpAMD64MOVLload(v *Value, config *Config) bool {
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVLloadidx1)
+		v.reset(OpAMD64MOVSSloadidx1)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
@@ -6948,9 +8158,9 @@ func rewriteValueAMD64_OpAMD64MOVLload(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLload [off1] {sym1} (LEAQ4 [off2] {sym2} ptr idx) mem)
+	// match: (MOVSSload [off1] {sym1} (LEAQ4 [off2] {sym2} ptr idx) mem)
 	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVLloadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	// result: (MOVSSloadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
@@ -6966,7 +8176,7 @@ func rewriteValueAMD64_OpAMD64MOVLload(v *Value, config *Config) bool {
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVLloadidx4)
+		v.reset(OpAMD64MOVSSloadidx4)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
@@ -6974,9 +8184,9 @@ func rewriteValueAMD64_OpAMD64MOVLload(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLload [off] {sym} (ADDQ ptr idx) mem)
+	// match: (MOVSSload [off] {sym} (ADDQ ptr idx) mem)
 	// cond: ptr.Op != OpSB
-	// result: (MOVLloadidx1 [off] {sym} ptr idx mem)
+	// result: (MOVSSloadidx1 [off] {sym} ptr idx mem)
 	for {
 		off := v.AuxInt
 		sym := v.Aux
@@ -6990,7 +8200,7 @@ func rewriteValueAMD64_OpAMD64MOVLload(v *Value, config *Config) bool {
 		if !(ptr.Op != OpSB) {
 			break
 		}
-		v.reset(OpAMD64MOVLloadidx1)
+		v.reset(OpAMD64MOVSSloadidx1)
 		v.AuxInt = off
 		v.Aux = sym
 		v.AddArg(ptr)
@@ -7000,12 +8210,12 @@ func rewriteValueAMD64_OpAMD64MOVLload(v *Value, config *Config) bool {
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVLloadidx1(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVSSloadidx1(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVLloadidx1 [c] {sym} ptr (SHLQconst [2] idx) mem)
+	// match: (MOVSSloadidx1 [c] {sym} ptr (SHLQconst [2] idx) mem)
 	// cond:
-	// result: (MOVLloadidx4 [c] {sym} ptr idx mem)
+	// result: (MOVSSloadidx4 [c] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -7019,7 +8229,7 @@ func rewriteValueAMD64_OpAMD64MOVLloadidx1(v *Value, config *Config) bool {
 		}
 		idx := v_1.Args[0]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVLloadidx4)
+		v.reset(OpAMD64MOVSSloadidx4)
 		v.AuxInt = c
 		v.Aux = sym
 		v.AddArg(ptr)
@@ -7027,9 +8237,9 @@ func rewriteValueAMD64_OpAMD64MOVLloadidx1(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
+	// match: (MOVSSloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
 	// cond:
-	// result: (MOVLloadidx1 [c+d] {sym} ptr idx mem)
+	// result: (MOVSSloadidx1 [c+d] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -7041,7 +8251,7 @@ func rewriteValueAMD64_OpAMD64MOVLloadidx1(v *Value, config *Config) bool {
 		ptr := v_0.Args[0]
 		idx := v.Args[1]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVLloadidx1)
+		v.reset(OpAMD64MOVSSloadidx1)
 		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
@@ -7049,9 +8259,9 @@ func rewriteValueAMD64_OpAMD64MOVLloadidx1(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
+	// match: (MOVSSloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
 	// cond:
-	// result: (MOVLloadidx1 [c+d] {sym} ptr idx mem)
+	// result: (MOVSSloadidx1 [c+d] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -7063,7 +8273,7 @@ func rewriteValueAMD64_OpAMD64MOVLloadidx1(v *Value, config *Config) bool {
 		d := v_1.AuxInt
 		idx := v_1.Args[0]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVLloadidx1)
+		v.reset(OpAMD64MOVSSloadidx1)
 		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
@@ -7073,12 +8283,12 @@ func rewriteValueAMD64_OpAMD64MOVLloadidx1(v *Value, config *Config) bool {
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVLloadidx4(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVSSloadidx4(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVLloadidx4 [c] {sym} (ADDQconst [d] ptr) idx mem)
+	// match: (MOVSSloadidx4 [c] {sym} (ADDQconst [d] ptr) idx mem)
 	// cond:
-	// result: (MOVLloadidx4 [c+d] {sym} ptr idx mem)
+	// result: (MOVSSloadidx4 [c+d] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -7090,7 +8300,7 @@ func rewriteValueAMD64_OpAMD64MOVLloadidx4(v *Value, config *Config) bool {
 		ptr := v_0.Args[0]
 		idx := v.Args[1]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVLloadidx4)
+		v.reset(OpAMD64MOVSSloadidx4)
 		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
@@ -7098,9 +8308,9 @@ func rewriteValueAMD64_OpAMD64MOVLloadidx4(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLloadidx4 [c] {sym} ptr (ADDQconst [d] idx) mem)
+	// match: (MOVSSloadidx4 [c] {sym} ptr (ADDQconst [d] idx) mem)
 	// cond:
-	// result: (MOVLloadidx4 [c+4*d] {sym} ptr idx mem)
+	// result: (MOVSSloadidx4 [c+4*d] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -7112,7 +8322,7 @@ func rewriteValueAMD64_OpAMD64MOVLloadidx4(v *Value, config *Config) bool {
 		d := v_1.AuxInt
 		idx := v_1.Args[0]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVLloadidx4)
+		v.reset(OpAMD64MOVSSloadidx4)
 		v.AuxInt = c + 4*d
 		v.Aux = sym
 		v.AddArg(ptr)
@@ -7122,102 +8332,37 @@ func rewriteValueAMD64_OpAMD64MOVLloadidx4(v *Value, config *Config) bool {
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVSSstore(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVLstore [off] {sym} ptr (MOVLQSX x) mem)
-	// cond:
-	// result: (MOVLstore [off] {sym} ptr x mem)
+	// match: (MOVSSstore [off1] {sym} (ADDQconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVSSstore [off1+off2] {sym} ptr val mem)
 	for {
-		off := v.AuxInt
+		off1 := v.AuxInt
 		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLQSX {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		x := v_1.Args[0]
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVLstore)
-		v.AuxInt = off
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVSSstore)
+		v.AuxInt = off1 + off2
 		v.Aux = sym
 		v.AddArg(ptr)
-		v.AddArg(x)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstore [off] {sym} ptr (MOVLQZX x) mem)
-	// cond:
-	// result: (MOVLstore [off] {sym} ptr x mem)
-	for {
-		off := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLQZX {
-			break
-		}
-		x := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVLstore)
-		v.AuxInt = off
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(x)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVLstore  [off1] {sym} (ADDQconst [off2] ptr) val mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVLstore  [off1+off2] {sym} ptr val mem)
-	for {
-		off1 := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
-			break
-		}
-		off2 := v_0.AuxInt
-		ptr := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1 + off2)) {
-			break
-		}
-		v.reset(OpAMD64MOVLstore)
-		v.AuxInt = off1 + off2
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVLstore [off] {sym} ptr (MOVLconst [c]) mem)
-	// cond: validOff(off)
-	// result: (MOVLstoreconst [makeValAndOff(int64(int32(c)),off)] {sym} ptr mem)
-	for {
-		off := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			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)
+	// match: (MOVSSstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVSSstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
@@ -7233,7 +8378,7 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVLstore)
+		v.reset(OpAMD64MOVSSstore)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(base)
@@ -7241,9 +8386,9 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
+	// match: (MOVSSstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
 	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVLstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	// result: (MOVSSstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
@@ -7260,7 +8405,7 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVLstoreidx1)
+		v.reset(OpAMD64MOVSSstoreidx1)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
@@ -7269,9 +8414,9 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstore [off1] {sym1} (LEAQ4 [off2] {sym2} ptr idx) val mem)
+	// match: (MOVSSstore [off1] {sym1} (LEAQ4 [off2] {sym2} ptr idx) val mem)
 	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVLstoreidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	// result: (MOVSSstoreidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
@@ -7288,7 +8433,7 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVLstoreidx4)
+		v.reset(OpAMD64MOVSSstoreidx4)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
@@ -7297,9 +8442,9 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstore [off] {sym} (ADDQ ptr idx) val mem)
+	// match: (MOVSSstore [off] {sym} (ADDQ ptr idx) val mem)
 	// cond: ptr.Op != OpSB
-	// result: (MOVLstoreidx1 [off] {sym} ptr idx val mem)
+	// result: (MOVSSstoreidx1 [off] {sym} ptr idx val mem)
 	for {
 		off := v.AuxInt
 		sym := v.Aux
@@ -7314,7 +8459,7 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
 		if !(ptr.Op != OpSB) {
 			break
 		}
-		v.reset(OpAMD64MOVLstoreidx1)
+		v.reset(OpAMD64MOVSSstoreidx1)
 		v.AuxInt = off
 		v.Aux = sym
 		v.AddArg(ptr)
@@ -7323,641 +8468,644 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// 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)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVSSstoreidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVSSstoreidx1 [c] {sym} ptr (SHLQconst [2] idx) val mem)
+	// cond:
+	// result: (MOVSSstoreidx4 [c] {sym} ptr idx val mem)
 	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHRQconst {
-			break
-		}
-		if v_1.AuxInt != 32 {
-			break
-		}
-		w := v_1.Args[0]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVLstore {
-			break
-		}
-		if x.AuxInt != i-4 {
-			break
-		}
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		if w != x.Args[1] {
+		if v_1.AuxInt != 2 {
 			break
 		}
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVSSstoreidx4)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVSSstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVSSstoreidx1 [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		v.reset(OpAMD64MOVQstore)
-		v.AuxInt = i - 4
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(w)
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVSSstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// 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)
+	// match: (MOVSSstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// cond:
+	// result: (MOVSSstoreidx1 [c+d] {sym} ptr idx val mem)
 	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHRQconst {
-			break
-		}
-		j := v_1.AuxInt
-		w := v_1.Args[0]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVLstore {
-			break
-		}
-		if x.AuxInt != i-4 {
-			break
-		}
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
-			break
-		}
-		w0 := x.Args[1]
-		if w0.Op != OpAMD64SHRQconst {
-			break
-		}
-		if w0.AuxInt != j-32 {
-			break
-		}
-		if w != w0.Args[0] {
-			break
-		}
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		v.reset(OpAMD64MOVQstore)
-		v.AuxInt = i - 4
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(w0)
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVSSstoreidx1)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVSSstoreidx4(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVLstoreconst [sc] {s} (ADDQconst [off] ptr) mem)
-	// cond: ValAndOff(sc).canAdd(off)
-	// result: (MOVLstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	// match: (MOVSSstoreidx4 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVSSstoreidx4 [c+d] {sym} ptr idx val mem)
 	for {
-		sc := v.AuxInt
-		s := v.Aux
+		c := v.AuxInt
+		sym := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		off := v_0.AuxInt
+		d := v_0.AuxInt
 		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(ValAndOff(sc).canAdd(off)) {
-			break
-		}
-		v.reset(OpAMD64MOVLstoreconst)
-		v.AuxInt = ValAndOff(sc).add(off)
-		v.Aux = s
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVSSstoreidx4)
+		v.AuxInt = c + d
+		v.Aux = sym
 		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem)
-	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
-	// result: (MOVLstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+	// match: (MOVSSstoreidx4 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// cond:
+	// result: (MOVSSstoreidx4 [c+4*d] {sym} ptr idx val mem)
 	for {
-		sc := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
-			break
-		}
-		off := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		v.reset(OpAMD64MOVLstoreconst)
-		v.AuxInt = ValAndOff(sc).add(off)
-		v.Aux = mergeSym(sym1, sym2)
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVSSstoreidx4)
+		v.AuxInt = c + 4*d
+		v.Aux = sym
 		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreconst [x] {sym1} (LEAQ1 [off] {sym2} ptr idx) mem)
-	// cond: canMergeSym(sym1, sym2)
-	// result: (MOVLstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVWQSX(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWQSX x:(MOVWload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWQSXload <v.Type> [off] {sym} ptr mem)
 	for {
-		x := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVWload {
 			break
 		}
-		off := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2)) {
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
 			break
 		}
-		v.reset(OpAMD64MOVLstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(off)
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWQSXload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreconst [x] {sym1} (LEAQ4 [off] {sym2} ptr idx) mem)
-	// cond: canMergeSym(sym1, sym2)
-	// result: (MOVLstoreconstidx4 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
+	// match: (MOVWQSX x:(MOVLload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWQSXload <v.Type> [off] {sym} ptr mem)
 	for {
-		x := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ4 {
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVLload {
 			break
 		}
-		off := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2)) {
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
 			break
 		}
-		v.reset(OpAMD64MOVLstoreconstidx4)
-		v.AuxInt = ValAndOff(x).add(off)
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWQSXload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreconst [x] {sym} (ADDQ ptr idx) mem)
-	// cond:
-	// result: (MOVLstoreconstidx1 [x] {sym} ptr idx mem)
+	// match: (MOVWQSX x:(MOVQload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWQSXload <v.Type> [off] {sym} ptr mem)
 	for {
-		x := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVQload {
 			break
 		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		v.reset(OpAMD64MOVLstoreconstidx1)
-		v.AuxInt = x
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWQSXload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreconst [c] {s} p x:(MOVLstoreconst [a] {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)
+	// match: (MOVWQSX (ANDLconst [c] x))
+	// cond: c & 0x8000 == 0
+	// result: (ANDLconst [c & 0x7fff] x)
 	for {
-		c := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		x := v.Args[1]
-		if x.Op != OpAMD64MOVLstoreconst {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDLconst {
 			break
 		}
-		a := x.AuxInt
-		if x.Aux != s {
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(c&0x8000 == 0) {
 			break
 		}
-		if p != x.Args[0] {
+		v.reset(OpAMD64ANDLconst)
+		v.AuxInt = c & 0x7fff
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVWQSXload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWQSXload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWQSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
 			break
 		}
-		mem := x.Args[1]
-		if !(x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVQstore)
-		v.AuxInt = ValAndOff(a).Off()
-		v.Aux = s
-		v.AddArg(p)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
-		v0.AuxInt = ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32
-		v.AddArg(v0)
+		v.reset(OpAMD64MOVWQSXload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
 		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVLstoreconstidx1(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVWQZX(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVLstoreconstidx1 [c] {sym} ptr (SHLQconst [2] idx) mem)
-	// cond:
-	// result: (MOVLstoreconstidx4 [c] {sym} ptr idx mem)
+	// match: (MOVWQZX x:(MOVWload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVWload {
 			break
 		}
-		if v_1.AuxInt != 2 {
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
 			break
 		}
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVLstoreconstidx4)
-		v.AuxInt = c
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreconstidx1 [x] {sym} (ADDQconst [c] ptr) idx mem)
-	// cond:
-	// result: (MOVLstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	// match: (MOVWQZX x:(MOVLload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
 	for {
-		x := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVLload {
 			break
 		}
-		c := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVLstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(c)
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVLstoreconstidx1 [x] {sym} ptr (ADDQconst [c] idx) mem)
-	// cond:
-	// result: (MOVLstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
 			break
 		}
-		c := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVLstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(c)
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreconstidx1 [c] {s} p i x:(MOVLstoreconstidx1 [a] {s} p i mem))
-	// cond: x.Uses == 1   && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()   && clobber(x)
-	// result: (MOVQstoreidx1 [ValAndOff(a).Off()] {s} p i (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
+	// match: (MOVWQZX x:(MOVQload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
 	for {
-		c := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		i := v.Args[1]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVLstoreconstidx1 {
-			break
-		}
-		a := x.AuxInt
-		if x.Aux != s {
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVQload {
 			break
 		}
-		if p != x.Args[0] {
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
 			break
 		}
-		if i != x.Args[1] {
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVWQZX x:(MOVWloadidx1 [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWloadidx1 <v.Type> [off] {sym} ptr idx mem)
+	for {
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVWloadidx1 {
 			break
 		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
 		mem := x.Args[2]
-		if !(x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
+		if !(x.Uses == 1 && clobber(x)) {
 			break
 		}
-		v.reset(OpAMD64MOVQstoreidx1)
-		v.AuxInt = ValAndOff(a).Off()
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(i)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
-		v0.AuxInt = ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWloadidx1, v.Type)
+		v.reset(OpCopy)
 		v.AddArg(v0)
-		v.AddArg(mem)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVLstoreconstidx4(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVLstoreconstidx4 [x] {sym} (ADDQconst [c] ptr) idx mem)
-	// cond:
-	// result: (MOVLstoreconstidx4 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	// match: (MOVWQZX x:(MOVWloadidx2 [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWloadidx2 <v.Type> [off] {sym} ptr idx mem)
 	for {
-		x := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		x := v.Args[0]
+		if x.Op != OpAMD64MOVWloadidx2 {
 			break
 		}
-		c := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVLstoreconstidx4)
-		v.AuxInt = ValAndOff(x).add(c)
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVLstoreconstidx4 [x] {sym} ptr (ADDQconst [c] idx) mem)
-	// cond:
-	// result: (MOVLstoreconstidx4 [ValAndOff(x).add(4*c)] {sym} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
 			break
 		}
-		c := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVLstoreconstidx4)
-		v.AuxInt = ValAndOff(x).add(4 * c)
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWloadidx2, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreconstidx4 [c] {s} p i x:(MOVLstoreconstidx4 [a] {s} p i mem))
-	// cond: x.Uses == 1   && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()   && clobber(x)
-	// result: (MOVQstoreidx1 [ValAndOff(a).Off()] {s} p (SHLQconst <i.Type> [2] i) (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
+	// match: (MOVWQZX (ANDLconst [c] x))
+	// cond:
+	// result: (ANDLconst [c & 0xffff] x)
 	for {
-		c := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		i := v.Args[1]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVLstoreconstidx4 {
-			break
-		}
-		a := x.AuxInt
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
-			break
-		}
-		if i != x.Args[1] {
-			break
-		}
-		mem := x.Args[2]
-		if !(x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ANDLconst {
 			break
 		}
-		v.reset(OpAMD64MOVQstoreidx1)
-		v.AuxInt = ValAndOff(a).Off()
-		v.Aux = s
-		v.AddArg(p)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, i.Type)
-		v0.AuxInt = 2
-		v0.AddArg(i)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
-		v1.AuxInt = ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32
-		v.AddArg(v1)
-		v.AddArg(mem)
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64ANDLconst)
+		v.AuxInt = c & 0xffff
+		v.AddArg(x)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVLstoreidx1(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVWload(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVLstoreidx1 [c] {sym} ptr (SHLQconst [2] idx) val mem)
-	// cond:
-	// result: (MOVLstoreidx4 [c] {sym} ptr idx val mem)
+	// match: (MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
 	for {
-		c := v.AuxInt
+		off := v.AuxInt
 		sym := v.Aux
 		ptr := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		if v_1.Op != OpAMD64MOVWstore {
 			break
 		}
-		if v_1.AuxInt != 2 {
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
 			break
 		}
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVLstoreidx4)
-		v.AuxInt = c
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVLstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
-	// cond:
-	// result: (MOVLstoreidx1 [c+d] {sym} ptr idx val mem)
+	// match: (MOVWload  [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVWload  [off1+off2] {sym} ptr mem)
 	for {
-		c := v.AuxInt
+		off1 := v.AuxInt
 		sym := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		d := v_0.AuxInt
+		off2 := v_0.AuxInt
 		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVLstoreidx1)
-		v.AuxInt = c + d
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64MOVWload)
+		v.AuxInt = off1 + off2
 		v.Aux = sym
 		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
-	// cond:
-	// result: (MOVLstoreidx1 [c+d] {sym} ptr idx val mem)
+	// match: (MOVWload  [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVLstoreidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreidx1 [i] {s} p idx (SHRQconst [32] w) x:(MOVLstoreidx1 [i-4] {s} p idx w mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVQstoreidx1 [i-4] {s} p idx w mem)
+	// match: (MOVWload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		idx := v.Args[1]
-		v_2 := v.Args[2]
-		if v_2.Op != OpAMD64SHRQconst {
-			break
-		}
-		if v_2.AuxInt != 32 {
-			break
-		}
-		w := v_2.Args[0]
-		x := v.Args[3]
-		if x.Op != OpAMD64MOVLstoreidx1 {
-			break
-		}
-		if x.AuxInt != i-4 {
-			break
-		}
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
-			break
-		}
-		if idx != x.Args[1] {
-			break
-		}
-		if w != x.Args[2] {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ1 {
 			break
 		}
-		mem := x.Args[3]
-		if !(x.Uses == 1 && clobber(x)) {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVQstoreidx1)
-		v.AuxInt = i - 4
-		v.Aux = s
-		v.AddArg(p)
+		v.reset(OpAMD64MOVWloadidx1)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
 		v.AddArg(idx)
-		v.AddArg(w)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVLstoreidx1 [i-4] {s} p idx w0:(SHRQconst [j-32] w) mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVQstoreidx1 [i-4] {s} p idx w0 mem)
+	// match: (MOVWload [off1] {sym1} (LEAQ2 [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWloadidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		idx := v.Args[1]
-		v_2 := v.Args[2]
-		if v_2.Op != OpAMD64SHRQconst {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAQ2 {
 			break
 		}
-		j := v_2.AuxInt
-		w := v_2.Args[0]
-		x := v.Args[3]
-		if x.Op != OpAMD64MOVLstoreidx1 {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		if x.AuxInt != i-4 {
+		v.reset(OpAMD64MOVWloadidx2)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off] {sym} (ADDQ ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVWloadidx1 [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQ {
 			break
 		}
-		if x.Aux != s {
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
 			break
 		}
-		if p != x.Args[0] {
+		v.reset(OpAMD64MOVWloadidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload  [off1] {sym1} (LEAL [off2] {sym2} base) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVWload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAL {
 			break
 		}
-		if idx != x.Args[1] {
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
 			break
 		}
-		w0 := x.Args[2]
-		if w0.Op != OpAMD64SHRQconst {
+		v.reset(OpAMD64MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload  [off1] {sym} (ADDLconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVWload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDLconst {
 			break
 		}
-		if w0.AuxInt != j-32 {
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		if w != w0.Args[0] {
+		v.reset(OpAMD64MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVWloadidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWloadidx1 [c] {sym} ptr (SHLQconst [1] idx) mem)
+	// cond:
+	// result: (MOVWloadidx2 [c] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		mem := x.Args[3]
-		if !(x.Uses == 1 && clobber(x)) {
+		if v_1.AuxInt != 1 {
 			break
 		}
-		v.reset(OpAMD64MOVQstoreidx1)
-		v.AuxInt = i - 4
-		v.Aux = s
-		v.AddArg(p)
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVWloadidx2)
+		v.AuxInt = c
+		v.Aux = sym
+		v.AddArg(ptr)
 		v.AddArg(idx)
-		v.AddArg(w0)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVLstoreidx4(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVLstoreidx4 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// match: (MOVWloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
 	// cond:
-	// result: (MOVLstoreidx4 [c+d] {sym} ptr idx val mem)
+	// result: (MOVWloadidx1 [c+d] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -7968,20 +9116,18 @@ func rewriteValueAMD64_OpAMD64MOVLstoreidx4(v *Value, config *Config) bool {
 		d := v_0.AuxInt
 		ptr := v_0.Args[0]
 		idx := v.Args[1]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVLstoreidx4)
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVWloadidx1)
 		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
-		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreidx4 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// match: (MOVWloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
 	// cond:
-	// result: (MOVLstoreidx4 [c+4*d] {sym} ptr idx val mem)
+	// result: (MOVWloadidx1 [c+d] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -7992,132 +9138,114 @@ func rewriteValueAMD64_OpAMD64MOVLstoreidx4(v *Value, config *Config) bool {
 		}
 		d := v_1.AuxInt
 		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVLstoreidx4)
-		v.AuxInt = c + 4*d
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVWloadidx1)
+		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
-		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreidx4 [i] {s} p idx (SHRQconst [32] w) x:(MOVLstoreidx4 [i-4] {s} p idx w mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVWloadidx2(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWloadidx2 [c] {sym} (ADDQconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVWloadidx2 [c+d] {sym} ptr idx mem)
 	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		idx := v.Args[1]
-		v_2 := v.Args[2]
-		if v_2.Op != OpAMD64SHRQconst {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		if v_2.AuxInt != 32 {
-			break
-		}
-		w := v_2.Args[0]
-		x := v.Args[3]
-		if x.Op != OpAMD64MOVLstoreidx4 {
-			break
-		}
-		if x.AuxInt != i-4 {
-			break
-		}
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
-			break
-		}
-		if idx != x.Args[1] {
-			break
-		}
-		if w != x.Args[2] {
-			break
-		}
-		mem := x.Args[3]
-		if !(x.Uses == 1 && clobber(x)) {
-			break
-		}
-		v.reset(OpAMD64MOVQstoreidx1)
-		v.AuxInt = i - 4
-		v.Aux = s
-		v.AddArg(p)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
-		v0.AuxInt = 2
-		v0.AddArg(idx)
-		v.AddArg(v0)
-		v.AddArg(w)
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVWloadidx2)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVLstoreidx4 [i] {s} p idx (SHRQconst [j] w) x:(MOVLstoreidx4 [i-4] {s} p idx w0:(SHRQconst [j-32] w) mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w0 mem)
+	// match: (MOVWloadidx2 [c] {sym} ptr (ADDQconst [d] idx) mem)
+	// cond:
+	// result: (MOVWloadidx2 [c+2*d] {sym} ptr idx mem)
 	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		idx := v.Args[1]
-		v_2 := v.Args[2]
-		if v_2.Op != OpAMD64SHRQconst {
-			break
-		}
-		j := v_2.AuxInt
-		w := v_2.Args[0]
-		x := v.Args[3]
-		if x.Op != OpAMD64MOVLstoreidx4 {
-			break
-		}
-		if x.AuxInt != i-4 {
-			break
-		}
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
-			break
-		}
-		if idx != x.Args[1] {
-			break
-		}
-		w0 := x.Args[2]
-		if w0.Op != OpAMD64SHRQconst {
-			break
-		}
-		if w0.AuxInt != j-32 {
-			break
-		}
-		if w != w0.Args[0] {
-			break
-		}
-		mem := x.Args[3]
-		if !(x.Uses == 1 && clobber(x)) {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		v.reset(OpAMD64MOVQstoreidx1)
-		v.AuxInt = i - 4
-		v.Aux = s
-		v.AddArg(p)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
-		v0.AuxInt = 2
-		v0.AddArg(idx)
-		v.AddArg(v0)
-		v.AddArg(w0)
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVWloadidx2)
+		v.AuxInt = c + 2*d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVOload(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVWstore(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVOload  [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// match: (MOVWstore [off] {sym} ptr (MOVWQSX x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVWQSX {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWQZX x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVWQZX {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpAMD64MOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore  [off1] {sym} (ADDQconst [off2] ptr) val mem)
 	// cond: is32Bit(off1+off2)
-	// result: (MOVOload  [off1+off2] {sym} ptr mem)
+	// result: (MOVWstore  [off1+off2] {sym} ptr val mem)
 	for {
 		off1 := v.AuxInt
 		sym := v.Aux
@@ -8127,20 +9255,45 @@ func rewriteValueAMD64_OpAMD64MOVOload(v *Value, config *Config) bool {
 		}
 		off2 := v_0.AuxInt
 		ptr := v_0.Args[0]
-		mem := v.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
 		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		v.reset(OpAMD64MOVOload)
+		v.reset(OpAMD64MOVWstore)
 		v.AuxInt = off1 + off2
 		v.Aux = sym
 		v.AddArg(ptr)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVOload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
+	// match: (MOVWstore [off] {sym} ptr (MOVLconst [c]) mem)
+	// cond: validOff(off)
+	// result: (MOVWstoreconst [makeValAndOff(int64(int16(c)),off)] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			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: (MOVOload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	// result: (MOVWstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
@@ -8151,205 +9304,352 @@ func rewriteValueAMD64_OpAMD64MOVOload(v *Value, config *Config) bool {
 		off2 := v_0.AuxInt
 		sym2 := v_0.Aux
 		base := v_0.Args[0]
-		mem := v.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVOload)
+		v.reset(OpAMD64MOVWstore)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(base)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVOstore(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVOstore  [off1] {sym} (ADDQconst [off2] ptr) val mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVOstore  [off1+off2] {sym} ptr val mem)
+	// match: (MOVWstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
 	for {
 		off1 := v.AuxInt
-		sym := v.Aux
+		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v_0.Op != OpAMD64LEAQ1 {
 			break
 		}
 		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
 		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
 		val := v.Args[1]
 		mem := v.Args[2]
-		if !(is32Bit(off1 + off2)) {
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVOstore)
+		v.reset(OpAMD64MOVWstoreidx1)
 		v.AuxInt = off1 + off2
-		v.Aux = sym
+		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVOstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
+	// match: (MOVWstore [off1] {sym1} (LEAQ2 [off2] {sym2} ptr idx) val mem)
 	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVOstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	// result: (MOVWstoreidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
 	for {
 		off1 := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		if v_0.Op != OpAMD64LEAQ2 {
 			break
 		}
 		off2 := v_0.AuxInt
 		sym2 := v_0.Aux
-		base := v_0.Args[0]
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
 		val := v.Args[1]
 		mem := v.Args[2]
 		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVOstore)
+		v.reset(OpAMD64MOVWstoreidx2)
 		v.AuxInt = off1 + off2
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
+		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVQload(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVQload [off] {sym} ptr (MOVQstore [off2] {sym2} ptr2 x _))
-	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
-	// result: x
+	// match: (MOVWstore [off] {sym} (ADDQ ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVWstoreidx1 [off] {sym} ptr idx val mem)
 	for {
 		off := v.AuxInt
 		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQstore {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQ {
 			break
 		}
-		off2 := v_1.AuxInt
-		sym2 := v_1.Aux
-		ptr2 := v_1.Args[0]
-		x := v_1.Args[1]
-		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
+		v.reset(OpAMD64MOVWstoreidx1)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQload  [off1] {sym} (ADDQconst [off2] ptr) mem)
+	// 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)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		if v_1.AuxInt != 16 {
+			break
+		}
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVWstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if w != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVLstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [i] {s} p (SHRQconst [j] w) x:(MOVWstore [i-2] {s} p w0:(SHRQconst [j-16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVLstore [i-2] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64SHRQconst {
+			break
+		}
+		j := v_1.AuxInt
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVWstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		if w0.Op != OpAMD64SHRQconst {
+			break
+		}
+		if w0.AuxInt != j-16 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVLstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore  [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVWstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAL {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpAMD64MOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore  [off1] {sym} (ADDLconst [off2] ptr) val mem)
 	// cond: is32Bit(off1+off2)
-	// result: (MOVQload  [off1+off2] {sym} ptr mem)
+	// result: (MOVWstore  [off1+off2] {sym} ptr val mem)
 	for {
 		off1 := v.AuxInt
 		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v_0.Op != OpAMD64ADDLconst {
 			break
 		}
 		off2 := v_0.AuxInt
 		ptr := v_0.Args[0]
-		mem := v.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
 		if !(is32Bit(off1 + off2)) {
 			break
 		}
-		v.reset(OpAMD64MOVQload)
+		v.reset(OpAMD64MOVWstore)
 		v.AuxInt = off1 + off2
 		v.Aux = sym
 		v.AddArg(ptr)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQload  [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVQload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVWstoreconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreconst [sc] {s} (ADDQconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
 	for {
-		off1 := v.AuxInt
+		sc := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ADDQconst {
+			break
+		}
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpAMD64MOVWstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
+	// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+	for {
+		sc := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64LEAQ {
 			break
 		}
-		off2 := v_0.AuxInt
+		off := v_0.AuxInt
 		sym2 := v_0.Aux
-		base := v_0.Args[0]
+		ptr := v_0.Args[0]
 		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
 			break
 		}
-		v.reset(OpAMD64MOVQload)
-		v.AuxInt = off1 + off2
+		v.reset(OpAMD64MOVWstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
 		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
+		v.AddArg(ptr)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVQloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	// match: (MOVWstoreconst [x] {sym1} (LEAQ1 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVWstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		off1 := v.AuxInt
+		x := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64LEAQ1 {
 			break
 		}
-		off2 := v_0.AuxInt
+		off := v_0.AuxInt
 		sym2 := v_0.Aux
 		ptr := v_0.Args[0]
 		idx := v_0.Args[1]
 		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if !(canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVQloadidx1)
-		v.AuxInt = off1 + off2
+		v.reset(OpAMD64MOVWstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(off)
 		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQload [off1] {sym1} (LEAQ8 [off2] {sym2} ptr idx) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVQloadidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	// match: (MOVWstoreconst [x] {sym1} (LEAQ2 [off] {sym2} ptr idx) mem)
+	// cond: canMergeSym(sym1, sym2)
+	// result: (MOVWstoreconstidx2 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
 	for {
-		off1 := v.AuxInt
+		x := v.AuxInt
 		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ8 {
+		if v_0.Op != OpAMD64LEAQ2 {
 			break
 		}
-		off2 := v_0.AuxInt
+		off := v_0.AuxInt
 		sym2 := v_0.Aux
 		ptr := v_0.Args[0]
 		idx := v_0.Args[1]
 		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if !(canMergeSym(sym1, sym2)) {
 			break
 		}
-		v.reset(OpAMD64MOVQloadidx8)
-		v.AuxInt = off1 + off2
+		v.reset(OpAMD64MOVWstoreconstidx2)
+		v.AuxInt = ValAndOff(x).add(off)
 		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQload [off] {sym} (ADDQ ptr idx) mem)
-	// cond: ptr.Op != OpSB
-	// result: (MOVQloadidx1 [off] {sym} ptr idx mem)
+	// match: (MOVWstoreconst [x] {sym} (ADDQ ptr idx) mem)
+	// cond:
+	// result: (MOVWstoreconstidx1 [x] {sym} ptr idx mem)
 	for {
-		off := v.AuxInt
+		x := v.AuxInt
 		sym := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQ {
@@ -8358,433 +9658,291 @@ func rewriteValueAMD64_OpAMD64MOVQload(v *Value, config *Config) bool {
 		ptr := v_0.Args[0]
 		idx := v_0.Args[1]
 		mem := v.Args[1]
-		if !(ptr.Op != OpSB) {
-			break
-		}
-		v.reset(OpAMD64MOVQloadidx1)
-		v.AuxInt = off
+		v.reset(OpAMD64MOVWstoreconstidx1)
+		v.AuxInt = x
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVQloadidx1(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVQloadidx1 [c] {sym} ptr (SHLQconst [3] idx) mem)
-	// cond:
-	// result: (MOVQloadidx8 [c] {sym} ptr idx mem)
+	// match: (MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {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 {
 		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
-			break
-		}
-		if v_1.AuxInt != 3 {
+		s := v.Aux
+		p := v.Args[0]
+		x := v.Args[1]
+		if x.Op != OpAMD64MOVWstoreconst {
 			break
 		}
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVQloadidx8)
-		v.AuxInt = c
-		v.Aux = sym
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		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)
+	for {
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64LEAL {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpAMD64MOVWstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
 		v.AddArg(ptr)
-		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
-	// cond:
-	// result: (MOVQloadidx1 [c+d] {sym} ptr idx mem)
+	// match: (MOVWstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
 	for {
-		c := v.AuxInt
-		sym := v.Aux
+		sc := v.AuxInt
+		s := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v_0.Op != OpAMD64ADDLconst {
 			break
 		}
-		d := v_0.AuxInt
+		off := v_0.AuxInt
 		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVQloadidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpAMD64MOVWstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
 		v.AddArg(ptr)
-		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MOVWstoreconstidx1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreconstidx1 [c] {sym} ptr (SHLQconst [1] idx) mem)
 	// cond:
-	// result: (MOVQloadidx1 [c+d] {sym} ptr idx mem)
+	// result: (MOVWstoreconstidx2 [c] {sym} ptr idx mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
 		ptr := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if v_1.Op != OpAMD64SHLQconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
 			break
 		}
-		d := v_1.AuxInt
 		idx := v_1.Args[0]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVQloadidx1)
-		v.AuxInt = c + d
+		v.reset(OpAMD64MOVWstoreconstidx2)
+		v.AuxInt = c
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVQloadidx8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVQloadidx8 [c] {sym} (ADDQconst [d] ptr) idx mem)
+	// match: (MOVWstoreconstidx1 [x] {sym} (ADDQconst [c] ptr) idx mem)
 	// cond:
-	// result: (MOVQloadidx8 [c+d] {sym} ptr idx mem)
+	// result: (MOVWstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		c := v.AuxInt
+		x := v.AuxInt
 		sym := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		d := v_0.AuxInt
+		c := v_0.AuxInt
 		ptr := v_0.Args[0]
 		idx := v.Args[1]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVQloadidx8)
-		v.AuxInt = c + d
+		v.reset(OpAMD64MOVWstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQloadidx8 [c] {sym} ptr (ADDQconst [d] idx) mem)
+	// match: (MOVWstoreconstidx1 [x] {sym} ptr (ADDQconst [c] idx) mem)
 	// cond:
-	// result: (MOVQloadidx8 [c+8*d] {sym} ptr idx mem)
+	// result: (MOVWstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		c := v.AuxInt
+		x := v.AuxInt
 		sym := v.Aux
 		ptr := v.Args[0]
 		v_1 := v.Args[1]
 		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		d := v_1.AuxInt
+		c := v_1.AuxInt
 		idx := v_1.Args[0]
 		mem := v.Args[2]
-		v.reset(OpAMD64MOVQloadidx8)
-		v.AuxInt = c + 8*d
+		v.reset(OpAMD64MOVWstoreconstidx1)
+		v.AuxInt = ValAndOff(x).add(c)
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
+	// match: (MOVWstoreconstidx1 [c] {s} p i x:(MOVWstoreconstidx1 [a] {s} p i mem))
+	// cond: x.Uses == 1   && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p i mem)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		i := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVWstoreconstidx1 {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if i != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVLstoreconstidx1)
+		v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xffff|ValAndOff(c).Val()<<16, ValAndOff(a).Off())
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(i)
+		v.AddArg(mem)
+		return true
+	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVQstore(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVWstoreconstidx2(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVQstore  [off1] {sym} (ADDQconst [off2] ptr) val mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVQstore  [off1+off2] {sym} ptr val mem)
+	// match: (MOVWstoreconstidx2 [x] {sym} (ADDQconst [c] ptr) idx mem)
+	// cond:
+	// result: (MOVWstoreconstidx2 [ValAndOff(x).add(c)] {sym} ptr idx mem)
 	for {
-		off1 := v.AuxInt
+		x := v.AuxInt
 		sym := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		off2 := v_0.AuxInt
+		c := v_0.AuxInt
 		ptr := v_0.Args[0]
-		val := v.Args[1]
+		idx := v.Args[1]
 		mem := v.Args[2]
-		if !(is32Bit(off1 + off2)) {
-			break
-		}
-		v.reset(OpAMD64MOVQstore)
-		v.AuxInt = off1 + off2
+		v.reset(OpAMD64MOVWstoreconstidx2)
+		v.AuxInt = ValAndOff(x).add(c)
 		v.Aux = sym
 		v.AddArg(ptr)
-		v.AddArg(val)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQstore [off] {sym} ptr (MOVQconst [c]) mem)
-	// cond: validValAndOff(c,off)
-	// result: (MOVQstoreconst [makeValAndOff(c,off)] {sym} ptr mem)
+	// match: (MOVWstoreconstidx2 [x] {sym} ptr (ADDQconst [c] idx) mem)
+	// cond:
+	// result: (MOVWstoreconstidx2 [ValAndOff(x).add(2*c)] {sym} ptr idx mem)
 	for {
-		off := v.AuxInt
+		x := v.AuxInt
 		sym := v.Aux
 		ptr := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
+		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
 		c := v_1.AuxInt
+		idx := v_1.Args[0]
 		mem := v.Args[2]
-		if !(validValAndOff(c, off)) {
-			break
-		}
-		v.reset(OpAMD64MOVQstoreconst)
-		v.AuxInt = makeValAndOff(c, off)
+		v.reset(OpAMD64MOVWstoreconstidx2)
+		v.AuxInt = ValAndOff(x).add(2 * c)
 		v.Aux = sym
 		v.AddArg(ptr)
+		v.AddArg(idx)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQstore  [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVQstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	// match: (MOVWstoreconstidx2 [c] {s} p i x:(MOVWstoreconstidx2 [a] {s} p i mem))
+	// cond: x.Uses == 1   && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p (SHLQconst <i.Type> [1] i) mem)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		i := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpAMD64MOVWstoreconstidx2 {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		base := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		a := x.AuxInt
+		if x.Aux != s {
 			break
 		}
-		v.reset(OpAMD64MOVQstore)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVQstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVQstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
+		if p != x.Args[0] {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if i != x.Args[1] {
 			break
 		}
-		v.reset(OpAMD64MOVQstoreidx1)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVQstore [off1] {sym1} (LEAQ8 [off2] {sym2} ptr idx) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVQstoreidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ8 {
-			break
-		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
-			break
-		}
-		v.reset(OpAMD64MOVQstoreidx8)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVQstore [off] {sym} (ADDQ ptr idx) val mem)
-	// cond: ptr.Op != OpSB
-	// result: (MOVQstoreidx1 [off] {sym} ptr idx val mem)
-	for {
-		off := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
-			break
-		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(ptr.Op != OpSB) {
-			break
-		}
-		v.reset(OpAMD64MOVQstoreidx1)
-		v.AuxInt = off
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVQstoreconst(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVQstoreconst [sc] {s} (ADDQconst [off] ptr) mem)
-	// cond: ValAndOff(sc).canAdd(off)
-	// result: (MOVQstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
-	for {
-		sc := v.AuxInt
-		s := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
-			break
-		}
-		off := v_0.AuxInt
-		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(ValAndOff(sc).canAdd(off)) {
+		mem := x.Args[2]
+		if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
 			break
 		}
-		v.reset(OpAMD64MOVQstoreconst)
-		v.AuxInt = ValAndOff(sc).add(off)
+		v.reset(OpAMD64MOVLstoreconstidx1)
+		v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xffff|ValAndOff(c).Val()<<16, ValAndOff(a).Off())
 		v.Aux = s
-		v.AddArg(ptr)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVQstoreconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem)
-	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
-	// result: (MOVQstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
-	for {
-		sc := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
-			break
-		}
-		off := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
-			break
-		}
-		v.reset(OpAMD64MOVQstoreconst)
-		v.AuxInt = ValAndOff(sc).add(off)
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVQstoreconst [x] {sym1} (LEAQ1 [off] {sym2} ptr idx) mem)
-	// cond: canMergeSym(sym1, sym2)
-	// result: (MOVQstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
-			break
-		}
-		off := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2)) {
-			break
-		}
-		v.reset(OpAMD64MOVQstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(off)
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVQstoreconst [x] {sym1} (LEAQ8 [off] {sym2} ptr idx) mem)
-	// cond: canMergeSym(sym1, sym2)
-	// result: (MOVQstoreconstidx8 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ8 {
-			break
-		}
-		off := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2)) {
-			break
-		}
-		v.reset(OpAMD64MOVQstoreconstidx8)
-		v.AuxInt = ValAndOff(x).add(off)
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVQstoreconst [x] {sym} (ADDQ ptr idx) mem)
-	// cond:
-	// result: (MOVQstoreconstidx1 [x] {sym} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
-			break
-		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		v.reset(OpAMD64MOVQstoreconstidx1)
-		v.AuxInt = x
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, i.Type)
+		v0.AuxInt = 1
+		v0.AddArg(i)
+		v.AddArg(v0)
 		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVQstoreconstidx1(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVWstoreidx1(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVQstoreconstidx1 [c] {sym} ptr (SHLQconst [3] idx) mem)
+	// match: (MOVWstoreidx1 [c] {sym} ptr (SHLQconst [1] idx) val mem)
 	// cond:
-	// result: (MOVQstoreconstidx8 [c] {sym} ptr idx mem)
+	// result: (MOVWstoreidx2 [c] {sym} ptr idx val mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -8793,199 +9951,178 @@ func rewriteValueAMD64_OpAMD64MOVQstoreconstidx1(v *Value, config *Config) bool
 		if v_1.Op != OpAMD64SHLQconst {
 			break
 		}
-		if v_1.AuxInt != 3 {
+		if v_1.AuxInt != 1 {
 			break
 		}
 		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVQstoreconstidx8)
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVWstoreidx2)
 		v.AuxInt = c
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQstoreconstidx1 [x] {sym} (ADDQconst [c] ptr) idx mem)
+	// match: (MOVWstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
 	// cond:
-	// result: (MOVQstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	// result: (MOVWstoreidx1 [c+d] {sym} ptr idx val mem)
 	for {
-		x := v.AuxInt
+		c := v.AuxInt
 		sym := v.Aux
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64ADDQconst {
 			break
 		}
-		c := v_0.AuxInt
+		d := v_0.AuxInt
 		ptr := v_0.Args[0]
 		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVQstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(c)
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVWstoreidx1)
+		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQstoreconstidx1 [x] {sym} ptr (ADDQconst [c] idx) mem)
+	// match: (MOVWstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
 	// cond:
-	// result: (MOVQstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	// result: (MOVWstoreidx1 [c+d] {sym} ptr idx val mem)
 	for {
-		x := v.AuxInt
+		c := v.AuxInt
 		sym := v.Aux
 		ptr := v.Args[0]
 		v_1 := v.Args[1]
 		if v_1.Op != OpAMD64ADDQconst {
 			break
 		}
-		c := v_1.AuxInt
+		d := v_1.AuxInt
 		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVQstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(c)
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64MOVWstoreidx1)
+		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
+		v.AddArg(val)
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVQstoreconstidx8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVQstoreconstidx8 [x] {sym} (ADDQconst [c] ptr) idx mem)
-	// cond:
-	// result: (MOVQstoreconstidx8 [ValAndOff(x).add(c)] {sym} ptr idx mem)
+	// match: (MOVWstoreidx1 [i] {s} p idx (SHRQconst [16] w) x:(MOVWstoreidx1 [i-2] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVLstoreidx1 [i-2] {s} p idx w mem)
 	for {
-		x := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpAMD64SHRQconst {
 			break
 		}
-		c := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVQstoreconstidx8)
-		v.AuxInt = ValAndOff(x).add(c)
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVQstoreconstidx8 [x] {sym} ptr (ADDQconst [c] idx) mem)
-	// cond:
-	// result: (MOVQstoreconstidx8 [ValAndOff(x).add(8*c)] {sym} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if v_2.AuxInt != 16 {
 			break
 		}
-		c := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVQstoreconstidx8)
-		v.AuxInt = ValAndOff(x).add(8 * c)
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVQstoreidx1(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVQstoreidx1 [c] {sym} ptr (SHLQconst [3] idx) val mem)
-	// cond:
-	// result: (MOVQstoreidx8 [c] {sym} ptr idx val mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpAMD64MOVWstoreidx1 {
 			break
 		}
-		if v_1.AuxInt != 3 {
+		if x.AuxInt != i-2 {
 			break
 		}
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVQstoreidx8)
-		v.AuxInt = c
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVQstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
-	// cond:
-	// result: (MOVQstoreidx1 [c+d] {sym} ptr idx val mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if x.Aux != s {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVQstoreidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVLstoreidx1)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
 		v.AddArg(idx)
-		v.AddArg(val)
+		v.AddArg(w)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
-	// cond:
-	// result: (MOVQstoreidx1 [c+d] {sym} ptr idx val mem)
+	// match: (MOVWstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVWstoreidx1 [i-2] {s} p idx w0:(SHRQconst [j-16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVLstoreidx1 [i-2] {s} p idx w0 mem)
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpAMD64SHRQconst {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVQstoreidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpAMD64MOVWstoreidx1 {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != OpAMD64SHRQconst {
+			break
+		}
+		if w0.AuxInt != j-16 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVLstoreidx1)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
 		v.AddArg(idx)
-		v.AddArg(val)
+		v.AddArg(w0)
 		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVQstoreidx8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MOVWstoreidx2(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVQstoreidx8 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// match: (MOVWstoreidx2 [c] {sym} (ADDQconst [d] ptr) idx val mem)
 	// cond:
-	// result: (MOVQstoreidx8 [c+d] {sym} ptr idx val mem)
+	// result: (MOVWstoreidx2 [c+d] {sym} ptr idx val mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -8998,7 +10135,7 @@ func rewriteValueAMD64_OpAMD64MOVQstoreidx8(v *Value, config *Config) bool {
 		idx := v.Args[1]
 		val := v.Args[2]
 		mem := v.Args[3]
-		v.reset(OpAMD64MOVQstoreidx8)
+		v.reset(OpAMD64MOVWstoreidx2)
 		v.AuxInt = c + d
 		v.Aux = sym
 		v.AddArg(ptr)
@@ -9007,9 +10144,9 @@ func rewriteValueAMD64_OpAMD64MOVQstoreidx8(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVQstoreidx8 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// match: (MOVWstoreidx2 [c] {sym} ptr (ADDQconst [d] idx) val mem)
 	// cond:
-	// result: (MOVQstoreidx8 [c+8*d] {sym} ptr idx val mem)
+	// result: (MOVWstoreidx2 [c+2*d] {sym} ptr idx val mem)
 	for {
 		c := v.AuxInt
 		sym := v.Aux
@@ -9022,8 +10159,8 @@ func rewriteValueAMD64_OpAMD64MOVQstoreidx8(v *Value, config *Config) bool {
 		idx := v_1.Args[0]
 		val := v.Args[2]
 		mem := v.Args[3]
-		v.reset(OpAMD64MOVQstoreidx8)
-		v.AuxInt = c + 8*d
+		v.reset(OpAMD64MOVWstoreidx2)
+		v.AuxInt = c + 2*d
 		v.Aux = sym
 		v.AddArg(ptr)
 		v.AddArg(idx)
@@ -9031,2419 +10168,1246 @@ func rewriteValueAMD64_OpAMD64MOVQstoreidx8(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVSDload(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVSDload [off1] {sym} (ADDQconst [off2] ptr) mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVSDload [off1+off2] {sym} ptr mem)
+	// match: (MOVWstoreidx2 [i] {s} p idx (SHRQconst [16] w) x:(MOVWstoreidx2 [i-2] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVLstoreidx1 [i-2] {s} p (SHLQconst <idx.Type> [1] idx) w mem)
 	for {
-		off1 := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpAMD64SHRQconst {
 			break
 		}
-		off2 := v_0.AuxInt
-		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(is32Bit(off1 + off2)) {
+		if v_2.AuxInt != 16 {
 			break
 		}
-		v.reset(OpAMD64MOVSDload)
-		v.AuxInt = off1 + off2
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVSDload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSDload [off1+off2] {mergeSym(sym1,sym2)} base mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpAMD64MOVWstoreidx2 {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		base := v_0.Args[0]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if x.AuxInt != i-2 {
 			break
 		}
-		v.reset(OpAMD64MOVSDload)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVSDload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSDloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
+		if x.Aux != s {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if p != x.Args[0] {
 			break
 		}
-		v.reset(OpAMD64MOVSDloadidx1)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVSDload [off1] {sym1} (LEAQ8 [off2] {sym2} ptr idx) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSDloadidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ8 {
+		if idx != x.Args[1] {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if w != x.Args[2] {
 			break
 		}
-		v.reset(OpAMD64MOVSDloadidx8)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVLstoreidx1)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
+		v0.AuxInt = 1
+		v0.AddArg(idx)
+		v.AddArg(v0)
+		v.AddArg(w)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (MOVSDload [off] {sym} (ADDQ ptr idx) mem)
-	// cond: ptr.Op != OpSB
-	// result: (MOVSDloadidx1 [off] {sym} ptr idx mem)
+	// match: (MOVWstoreidx2 [i] {s} p idx (SHRQconst [j] w) x:(MOVWstoreidx2 [i-2] {s} p idx w0:(SHRQconst [j-16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVLstoreidx1 [i-2] {s} p (SHLQconst <idx.Type> [1] idx) w0 mem)
 	for {
-		off := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpAMD64SHRQconst {
 			break
 		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(ptr.Op != OpSB) {
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpAMD64MOVWstoreidx2 {
 			break
 		}
-		v.reset(OpAMD64MOVSDloadidx1)
-		v.AuxInt = off
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != OpAMD64SHRQconst {
+			break
+		}
+		if w0.AuxInt != j-16 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpAMD64MOVLstoreidx1)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
+		v0.AuxInt = 1
+		v0.AddArg(idx)
+		v.AddArg(v0)
+		v.AddArg(w0)
 		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVSDloadidx1(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MULL(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVSDloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
+	// match: (MULL x (MOVLconst [c]))
 	// cond:
-	// result: (MOVSDloadidx1 [c+d] {sym} ptr idx mem)
+	// result: (MULLconst [c] x)
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVSDloadidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		c := v_1.AuxInt
+		v.reset(OpAMD64MULLconst)
+		v.AuxInt = c
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSDloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
+	// match: (MULL (MOVLconst [c]) x)
 	// cond:
-	// result: (MOVSDloadidx1 [c+d] {sym} ptr idx mem)
+	// result: (MULLconst [c] x)
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVSDloadidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpAMD64MULLconst)
+		v.AuxInt = c
+		v.AddArg(x)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVSDloadidx8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MULLconst(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVSDloadidx8 [c] {sym} (ADDQconst [d] ptr) idx mem)
+	// match: (MULLconst [c] (MULLconst [d] x))
 	// cond:
-	// result: (MOVSDloadidx8 [c+d] {sym} ptr idx mem)
+	// result: (MULLconst [int64(int32(c * d))] x)
 	for {
 		c := v.AuxInt
-		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v_0.Op != OpAMD64MULLconst {
 			break
 		}
 		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVSDloadidx8)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		x := v_0.Args[0]
+		v.reset(OpAMD64MULLconst)
+		v.AuxInt = int64(int32(c * d))
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSDloadidx8 [c] {sym} ptr (ADDQconst [d] idx) mem)
+	// match: (MULLconst [c] (MOVLconst [d]))
 	// cond:
-	// result: (MOVSDloadidx8 [c+8*d] {sym} ptr idx mem)
+	// result: (MOVLconst [int64(int32(c*d))])
 	for {
 		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVSDloadidx8)
-		v.AuxInt = c + 8*d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		d := v_0.AuxInt
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = int64(int32(c * d))
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVSDstore(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64MULQ(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVSDstore [off1] {sym} (ADDQconst [off2] ptr) val mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVSDstore [off1+off2] {sym} ptr val mem)
+	// match: (MULQ x (MOVQconst [c]))
+	// cond: is32Bit(c)
+	// result: (MULQconst [c] x)
 	for {
-		off1 := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
 			break
 		}
-		off2 := v_0.AuxInt
-		ptr := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1 + off2)) {
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
 			break
 		}
-		v.reset(OpAMD64MOVSDstore)
-		v.AuxInt = off1 + off2
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpAMD64MULQconst)
+		v.AuxInt = c
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSDstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	// match: (MULQ (MOVQconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (MULQconst [c] x)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		if v_0.Op != OpAMD64MOVQconst {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		base := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
 			break
 		}
-		v.reset(OpAMD64MOVSDstore)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpAMD64MULQconst)
+		v.AuxInt = c
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSDstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSDstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64MULQconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MULQconst [c] (MULQconst [d] x))
+	// cond: is32Bit(c*d)
+	// result: (MULQconst [c * d] x)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
+		c := v.AuxInt
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
+		if v_0.Op != OpAMD64MULQconst {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(c * d)) {
 			break
 		}
-		v.reset(OpAMD64MOVSDstoreidx1)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpAMD64MULQconst)
+		v.AuxInt = c * d
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSDstore [off1] {sym1} (LEAQ8 [off2] {sym2} ptr idx) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSDstoreidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	// match: (MULQconst [-1] x)
+	// cond:
+	// result: (NEGQ x)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ8 {
-			break
-		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if v.AuxInt != -1 {
 			break
 		}
-		v.reset(OpAMD64MOVSDstoreidx8)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64NEGQ)
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSDstore [off] {sym} (ADDQ ptr idx) val mem)
-	// cond: ptr.Op != OpSB
-	// result: (MOVSDstoreidx1 [off] {sym} ptr idx val mem)
+	// match: (MULQconst [0] _)
+	// cond:
+	// result: (MOVQconst [0])
 	for {
-		off := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
-			break
-		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(ptr.Op != OpSB) {
+		if v.AuxInt != 0 {
 			break
 		}
-		v.reset(OpAMD64MOVSDstoreidx1)
-		v.AuxInt = off
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = 0
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVSDstoreidx1(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVSDstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// match: (MULQconst [1] x)
 	// cond:
-	// result: (MOVSDstoreidx1 [c+d] {sym} ptr idx val mem)
+	// result: x
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v.AuxInt != 1 {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVSDstoreidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSDstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// match: (MULQconst [3] x)
 	// cond:
-	// result: (MOVSDstoreidx1 [c+d] {sym} ptr idx val mem)
+	// result: (LEAQ2 x x)
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if v.AuxInt != 3 {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVSDstoreidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ2)
+		v.AddArg(x)
+		v.AddArg(x)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVSDstoreidx8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVSDstoreidx8 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// match: (MULQconst [5] x)
 	// cond:
-	// result: (MOVSDstoreidx8 [c+d] {sym} ptr idx val mem)
+	// result: (LEAQ4 x x)
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v.AuxInt != 5 {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVSDstoreidx8)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ4)
+		v.AddArg(x)
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSDstoreidx8 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	// match: (MULQconst [7] x)
 	// cond:
-	// result: (MOVSDstoreidx8 [c+8*d] {sym} ptr idx val mem)
+	// result: (LEAQ8 (NEGQ <v.Type> x) x)
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if v.AuxInt != 7 {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVSDstoreidx8)
-		v.AuxInt = c + 8*d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ8)
+		v0 := b.NewValue0(v.Line, OpAMD64NEGQ, v.Type)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVSSload(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVSSload [off1] {sym} (ADDQconst [off2] ptr) mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVSSload [off1+off2] {sym} ptr mem)
+	// match: (MULQconst [9] x)
+	// cond:
+	// result: (LEAQ8 x x)
 	for {
-		off1 := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
-			break
-		}
-		off2 := v_0.AuxInt
-		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(is32Bit(off1 + off2)) {
+		if v.AuxInt != 9 {
 			break
 		}
-		v.reset(OpAMD64MOVSSload)
-		v.AuxInt = off1 + off2
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ8)
+		v.AddArg(x)
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSSload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSSload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	// match: (MULQconst [11] x)
+	// cond:
+	// result: (LEAQ2 x (LEAQ4 <v.Type> x x))
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
-			break
-		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		base := v_0.Args[0]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if v.AuxInt != 11 {
 			break
 		}
-		v.reset(OpAMD64MOVSSload)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ2)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64LEAQ4, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (MOVSSload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSSloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
-			break
-		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+	// match: (MULQconst [13] x)
+	// cond:
+	// result: (LEAQ4 x (LEAQ2 <v.Type> x x))
+	for {
+		if v.AuxInt != 13 {
 			break
 		}
-		v.reset(OpAMD64MOVSSloadidx1)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ4)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64LEAQ2, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (MOVSSload [off1] {sym1} (LEAQ4 [off2] {sym2} ptr idx) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSSloadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	// match: (MULQconst [21] x)
+	// cond:
+	// result: (LEAQ4 x (LEAQ4 <v.Type> x x))
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ4 {
-			break
-		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if v.AuxInt != 21 {
 			break
 		}
-		v.reset(OpAMD64MOVSSloadidx4)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ4)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64LEAQ4, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (MOVSSload [off] {sym} (ADDQ ptr idx) mem)
-	// cond: ptr.Op != OpSB
-	// result: (MOVSSloadidx1 [off] {sym} ptr idx mem)
+	// match: (MULQconst [25] x)
+	// cond:
+	// result: (LEAQ8 x (LEAQ2 <v.Type> x x))
 	for {
-		off := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
-			break
-		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(ptr.Op != OpSB) {
+		if v.AuxInt != 25 {
 			break
 		}
-		v.reset(OpAMD64MOVSSloadidx1)
-		v.AuxInt = off
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ8)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64LEAQ2, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVSSloadidx1(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVSSloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
+	// match: (MULQconst [37] x)
 	// cond:
-	// result: (MOVSSloadidx1 [c+d] {sym} ptr idx mem)
+	// result: (LEAQ4 x (LEAQ8 <v.Type> x x))
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v.AuxInt != 37 {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVSSloadidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ4)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64LEAQ8, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (MOVSSloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
+	// match: (MULQconst [41] x)
 	// cond:
-	// result: (MOVSSloadidx1 [c+d] {sym} ptr idx mem)
+	// result: (LEAQ8 x (LEAQ4 <v.Type> x x))
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if v.AuxInt != 41 {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVSSloadidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ8)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64LEAQ4, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVSSloadidx4(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVSSloadidx4 [c] {sym} (ADDQconst [d] ptr) idx mem)
+	// match: (MULQconst [73] x)
 	// cond:
-	// result: (MOVSSloadidx4 [c+d] {sym} ptr idx mem)
+	// result: (LEAQ8 x (LEAQ8 <v.Type> x x))
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v.AuxInt != 73 {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVSSloadidx4)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		x := v.Args[0]
+		v.reset(OpAMD64LEAQ8)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64LEAQ8, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (MOVSSloadidx4 [c] {sym} ptr (ADDQconst [d] idx) mem)
-	// cond:
-	// result: (MOVSSloadidx4 [c+4*d] {sym} ptr idx mem)
+	// match: (MULQconst [c] x)
+	// cond: isPowerOfTwo(c)
+	// result: (SHLQconst [log2(c)] x)
 	for {
 		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		x := v.Args[0]
+		if !(isPowerOfTwo(c)) {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVSSloadidx4)
-		v.AuxInt = c + 4*d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		v.reset(OpAMD64SHLQconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVSSstore(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVSSstore [off1] {sym} (ADDQconst [off2] ptr) val mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVSSstore [off1+off2] {sym} ptr val mem)
+	// match: (MULQconst [c] x)
+	// cond: isPowerOfTwo(c+1) && c >= 15
+	// result: (SUBQ (SHLQconst <v.Type> [log2(c+1)] x) x)
 	for {
-		off1 := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
-			break
-		}
-		off2 := v_0.AuxInt
-		ptr := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1 + off2)) {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c+1) && c >= 15) {
 			break
 		}
-		v.reset(OpAMD64MOVSSstore)
-		v.AuxInt = off1 + off2
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpAMD64SUBQ)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, v.Type)
+		v0.AuxInt = log2(c + 1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSSstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSSstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	// match: (MULQconst [c] x)
+	// cond: isPowerOfTwo(c-1) && c >= 17
+	// result: (LEAQ1 (SHLQconst <v.Type> [log2(c-1)] x) x)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c-1) && c >= 17) {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		base := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
-			break
-		}
-		v.reset(OpAMD64MOVSSstore)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpAMD64LEAQ1)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, v.Type)
+		v0.AuxInt = log2(c - 1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSSstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSSstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	// match: (MULQconst [c] x)
+	// cond: isPowerOfTwo(c-2) && c >= 34
+	// result: (LEAQ2 (SHLQconst <v.Type> [log2(c-2)] x) x)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c-2) && c >= 34) {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		v.reset(OpAMD64LEAQ2)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, v.Type)
+		v0.AuxInt = log2(c - 2)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULQconst [c] x)
+	// cond: isPowerOfTwo(c-4) && c >= 68
+	// result: (LEAQ4 (SHLQconst <v.Type> [log2(c-4)] x) x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c-4) && c >= 68) {
 			break
 		}
-		v.reset(OpAMD64MOVSSstoreidx1)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpAMD64LEAQ4)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, v.Type)
+		v0.AuxInt = log2(c - 4)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVSSstore [off1] {sym1} (LEAQ4 [off2] {sym2} ptr idx) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVSSstoreidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	// match: (MULQconst [c] x)
+	// cond: isPowerOfTwo(c-8) && c >= 136
+	// result: (LEAQ8 (SHLQconst <v.Type> [log2(c-8)] x) x)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ4 {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c-8) && c >= 136) {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		v.reset(OpAMD64LEAQ8)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, v.Type)
+		v0.AuxInt = log2(c - 8)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULQconst [c] x)
+	// cond: c%3 == 0 && isPowerOfTwo(c/3)
+	// result: (SHLQconst [log2(c/3)] (LEAQ2 <v.Type> x x))
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(c%3 == 0 && isPowerOfTwo(c/3)) {
 			break
 		}
-		v.reset(OpAMD64MOVSSstoreidx4)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpAMD64SHLQconst)
+		v.AuxInt = log2(c / 3)
+		v0 := b.NewValue0(v.Line, OpAMD64LEAQ2, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (MOVSSstore [off] {sym} (ADDQ ptr idx) val mem)
-	// cond: ptr.Op != OpSB
-	// result: (MOVSSstoreidx1 [off] {sym} ptr idx val mem)
+	// match: (MULQconst [c] x)
+	// cond: c%5 == 0 && isPowerOfTwo(c/5)
+	// result: (SHLQconst [log2(c/5)] (LEAQ4 <v.Type> x x))
 	for {
-		off := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(c%5 == 0 && isPowerOfTwo(c/5)) {
 			break
 		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(ptr.Op != OpSB) {
+		v.reset(OpAMD64SHLQconst)
+		v.AuxInt = log2(c / 5)
+		v0 := b.NewValue0(v.Line, OpAMD64LEAQ4, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULQconst [c] x)
+	// cond: c%9 == 0 && isPowerOfTwo(c/9)
+	// result: (SHLQconst [log2(c/9)] (LEAQ8 <v.Type> x x))
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(c%9 == 0 && isPowerOfTwo(c/9)) {
 			break
 		}
-		v.reset(OpAMD64MOVSSstoreidx1)
-		v.AuxInt = off
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpAMD64SHLQconst)
+		v.AuxInt = log2(c / 9)
+		v0 := b.NewValue0(v.Line, OpAMD64LEAQ8, v.Type)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVSSstoreidx1(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVSSstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// match: (MULQconst [c] (MOVQconst [d]))
 	// cond:
-	// result: (MOVSSstoreidx1 [c+d] {sym} ptr idx val mem)
+	// result: (MOVQconst [c*d])
 	for {
 		c := v.AuxInt
-		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v_0.Op != OpAMD64MOVQconst {
 			break
 		}
 		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVSSstoreidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = c * d
 		return true
 	}
-	// match: (MOVSSstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64NEGL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NEGL (MOVLconst [c]))
 	// cond:
-	// result: (MOVSSstoreidx1 [c+d] {sym} ptr idx val mem)
+	// result: (MOVLconst [int64(int32(-c))])
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVSSstoreidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		c := v_0.AuxInt
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = int64(int32(-c))
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVSSstoreidx4(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64NEGQ(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVSSstoreidx4 [c] {sym} (ADDQconst [d] ptr) idx val mem)
+	// match: (NEGQ (MOVQconst [c]))
 	// cond:
-	// result: (MOVSSstoreidx4 [c+d] {sym} ptr idx val mem)
+	// result: (MOVQconst [-c])
 	for {
-		c := v.AuxInt
-		sym := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if v_0.Op != OpAMD64MOVQconst {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVSSstoreidx4)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		c := v_0.AuxInt
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = -c
 		return true
 	}
-	// match: (MOVSSstoreidx4 [c] {sym} ptr (ADDQconst [d] idx) val mem)
+	return false
+}
+func rewriteValueAMD64_OpAMD64NOTL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NOTL (MOVLconst [c]))
 	// cond:
-	// result: (MOVSSstoreidx4 [c+4*d] {sym} ptr idx val mem)
+	// result: (MOVLconst [^c])
 	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVSSstoreidx4)
-		v.AuxInt = c + 4*d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		c := v_0.AuxInt
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = ^c
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVWQSX(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64NOTQ(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVWQSX x:(MOVWload [off] {sym} ptr mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVWQSXload <v.Type> [off] {sym} ptr mem)
-	for {
-		x := v.Args[0]
-		if x.Op != OpAMD64MOVWload {
-			break
-		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		mem := x.Args[1]
-		if !(x.Uses == 1 && clobber(x)) {
-			break
-		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVWQSXload, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(mem)
-		return true
-	}
-	// match: (MOVWQSX (ANDLconst [c] x))
-	// cond: c & 0x8000 == 0
-	// result: (ANDLconst [c & 0x7fff] x)
+	// match: (NOTQ (MOVQconst [c]))
+	// cond:
+	// result: (MOVQconst [^c])
 	for {
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDLconst {
+		if v_0.Op != OpAMD64MOVQconst {
 			break
 		}
 		c := v_0.AuxInt
-		x := v_0.Args[0]
-		if !(c&0x8000 == 0) {
-			break
-		}
-		v.reset(OpAMD64ANDLconst)
-		v.AuxInt = c & 0x7fff
-		v.AddArg(x)
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = ^c
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVWQSXload(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVWQSXload [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVWQSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	// match: (ORL x (MOVLconst [c]))
+	// cond:
+	// result: (ORLconst [c] x)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		base := v_0.Args[0]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		c := v_1.AuxInt
+		v.reset(OpAMD64ORLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORL (MOVLconst [c]) x)
+	// cond:
+	// result: (ORLconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
 			break
 		}
-		v.reset(OpAMD64MOVWQSXload)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
-		v.AddArg(mem)
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpAMD64ORLconst)
+		v.AuxInt = c
+		v.AddArg(x)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVWQZX(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVWQZX x:(MOVWload [off] {sym} ptr mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
+	// match: (ORL x x)
+	// cond:
+	// result: x
 	for {
 		x := v.Args[0]
-		if x.Op != OpAMD64MOVWload {
-			break
-		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		mem := x.Args[1]
-		if !(x.Uses == 1 && clobber(x)) {
+		if x != v.Args[1] {
 			break
 		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVWload, v.Type)
 		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(mem)
+		v.Type = x.Type
+		v.AddArg(x)
 		return true
 	}
-	// match: (MOVWQZX x:(MOVWloadidx1 [off] {sym} ptr idx mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVWloadidx1 <v.Type> [off] {sym} ptr idx mem)
+	// match: (ORL                  x0:(MOVBload [i]   {s} p mem)     s0:(SHLLconst [8] x1:(MOVBload [i+1] {s} p mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && s0.Uses == 1   && mergePoint(b,x0,x1) != nil   && clobber(x0)   && clobber(x1)   && clobber(s0)
+	// result: @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem)
 	for {
-		x := v.Args[0]
-		if x.Op != OpAMD64MOVWloadidx1 {
+		x0 := v.Args[0]
+		if x0.Op != OpAMD64MOVBload {
 			break
 		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		idx := x.Args[1]
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := v.Args[1]
+		if s0.Op != OpAMD64SHLLconst {
 			break
 		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVWloadidx1, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(idx)
-		v0.AddArg(mem)
-		return true
-	}
-	// match: (MOVWQZX x:(MOVWloadidx2 [off] {sym} ptr idx mem))
-	// cond: x.Uses == 1 && clobber(x)
-	// result: @x.Block (MOVWloadidx2 <v.Type> [off] {sym} ptr idx mem)
-	for {
-		x := v.Args[0]
-		if x.Op != OpAMD64MOVWloadidx2 {
+		if s0.AuxInt != 8 {
 			break
 		}
-		off := x.AuxInt
-		sym := x.Aux
-		ptr := x.Args[0]
-		idx := x.Args[1]
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
+		x1 := s0.Args[0]
+		if x1.Op != OpAMD64MOVBload {
 			break
 		}
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpAMD64MOVWloadidx2, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = off
-		v0.Aux = sym
-		v0.AddArg(ptr)
-		v0.AddArg(idx)
-		v0.AddArg(mem)
-		return true
-	}
-	// match: (MOVWQZX (ANDLconst [c] x))
-	// cond:
-	// result: (ANDLconst [c & 0xffff] x)
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ANDLconst {
+		if x1.AuxInt != i+1 {
 			break
 		}
-		c := v_0.AuxInt
-		x := v_0.Args[0]
-		v.reset(OpAMD64ANDLconst)
-		v.AuxInt = c & 0xffff
-		v.AddArg(x)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVWload(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _))
-	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
-	// result: x
-	for {
-		off := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVWstore {
+		if x1.Aux != s {
 			break
 		}
-		off2 := v_1.AuxInt
-		sym2 := v_1.Aux
-		ptr2 := v_1.Args[0]
-		x := v_1.Args[1]
-		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
 			break
 		}
+		b = mergePoint(b, x0, x1)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWload, config.fe.TypeUInt16())
 		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(mem)
 		return true
 	}
-	// match: (MOVWload  [off1] {sym} (ADDQconst [off2] ptr) mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVWload  [off1+off2] {sym} ptr mem)
+	// match: (ORL o0:(ORL                        x0:(MOVWload [i]   {s} p mem)     s0:(SHLLconst [16] x1:(MOVBload [i+2] {s} p mem)))     s1:(SHLLconst [24] x2:(MOVBload [i+3] {s} p mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && o0.Uses == 1   && mergePoint(b,x0,x1,x2) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(s0)   && clobber(s1)   && clobber(o0)
+	// result: @mergePoint(b,x0,x1,x2) (MOVLload [i] {s} p mem)
 	for {
-		off1 := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		o0 := v.Args[0]
+		if o0.Op != OpAMD64ORL {
 			break
 		}
-		off2 := v_0.AuxInt
-		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(is32Bit(off1 + off2)) {
+		x0 := o0.Args[0]
+		if x0.Op != OpAMD64MOVWload {
 			break
 		}
-		v.reset(OpAMD64MOVWload)
-		v.AuxInt = off1 + off2
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWload  [off1] {sym1} (LEAQ [off2] {sym2} base) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVWload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := o0.Args[1]
+		if s0.Op != OpAMD64SHLLconst {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		base := v_0.Args[0]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if s0.AuxInt != 16 {
 			break
 		}
-		v.reset(OpAMD64MOVWload)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWload [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVWloadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
+		x1 := s0.Args[0]
+		if x1.Op != OpAMD64MOVBload {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if x1.AuxInt != i+2 {
 			break
 		}
-		v.reset(OpAMD64MOVWloadidx1)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWload [off1] {sym1} (LEAQ2 [off2] {sym2} ptr idx) mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVWloadidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ2 {
+		if x1.Aux != s {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if p != x1.Args[0] {
 			break
 		}
-		v.reset(OpAMD64MOVWloadidx2)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWload [off] {sym} (ADDQ ptr idx) mem)
-	// cond: ptr.Op != OpSB
-	// result: (MOVWloadidx1 [off] {sym} ptr idx mem)
-	for {
-		off := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
+		if mem != x1.Args[1] {
 			break
 		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(ptr.Op != OpSB) {
+		s1 := v.Args[1]
+		if s1.Op != OpAMD64SHLLconst {
 			break
 		}
-		v.reset(OpAMD64MOVWloadidx1)
-		v.AuxInt = off
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVWloadidx1(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVWloadidx1 [c] {sym} ptr (SHLQconst [1] idx) mem)
-	// cond:
-	// result: (MOVWloadidx2 [c] {sym} ptr idx mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		if s1.AuxInt != 24 {
 			break
 		}
-		if v_1.AuxInt != 1 {
+		x2 := s1.Args[0]
+		if x2.Op != OpAMD64MOVBload {
 			break
 		}
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWloadidx2)
-		v.AuxInt = c
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWloadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem)
-	// cond:
-	// result: (MOVWloadidx1 [c+d] {sym} ptr idx mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if x2.AuxInt != i+3 {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWloadidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWloadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem)
-	// cond:
-	// result: (MOVWloadidx1 [c+d] {sym} ptr idx mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if x2.Aux != s {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWloadidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVWloadidx2(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVWloadidx2 [c] {sym} (ADDQconst [d] ptr) idx mem)
-	// cond:
-	// result: (MOVWloadidx2 [c+d] {sym} ptr idx mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if p != x2.Args[0] {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWloadidx2)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWloadidx2 [c] {sym} ptr (ADDQconst [d] idx) mem)
-	// cond:
-	// result: (MOVWloadidx2 [c+2*d] {sym} ptr idx mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if mem != x2.Args[1] {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWloadidx2)
-		v.AuxInt = c + 2*d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVWstore(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVWstore [off] {sym} ptr (MOVWQSX x) mem)
-	// cond:
-	// result: (MOVWstore [off] {sym} ptr x mem)
-	for {
-		off := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVWQSX {
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && o0.Uses == 1 && mergePoint(b, x0, x1, x2) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(s0) && clobber(s1) && clobber(o0)) {
 			break
 		}
-		x := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWstore)
-		v.AuxInt = off
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(x)
-		v.AddArg(mem)
+		b = mergePoint(b, x0, x1, x2)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(mem)
 		return true
 	}
-	// match: (MOVWstore [off] {sym} ptr (MOVWQZX x) mem)
-	// cond:
-	// result: (MOVWstore [off] {sym} ptr x mem)
+	// match: (ORL                  x0:(MOVBloadidx1 [i]   {s} p idx mem)     s0:(SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && s0.Uses == 1   && mergePoint(b,x0,x1) != nil   && clobber(x0)   && clobber(x1)   && clobber(s0)
+	// result: @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i] {s} p idx mem)
 	for {
-		off := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVWQZX {
+		x0 := v.Args[0]
+		if x0.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		x := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWstore)
-		v.AuxInt = off
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(x)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstore  [off1] {sym} (ADDQconst [off2] ptr) val mem)
-	// cond: is32Bit(off1+off2)
-	// result: (MOVWstore  [off1+off2] {sym} ptr val mem)
-	for {
-		off1 := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
-			break
-		}
-		off2 := v_0.AuxInt
-		ptr := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1 + off2)) {
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := v.Args[1]
+		if s0.Op != OpAMD64SHLLconst {
 			break
 		}
-		v.reset(OpAMD64MOVWstore)
-		v.AuxInt = off1 + off2
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstore [off] {sym} ptr (MOVLconst [c]) mem)
-	// cond: validOff(off)
-	// result: (MOVWstoreconst [makeValAndOff(int64(int16(c)),off)] {sym} ptr mem)
-	for {
-		off := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
+		if s0.AuxInt != 8 {
 			break
 		}
-		c := v_1.AuxInt
-		mem := v.Args[2]
-		if !(validOff(off)) {
+		x1 := s0.Args[0]
+		if x1.Op != OpAMD64MOVBloadidx1 {
 			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)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		if x1.AuxInt != i+1 {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		base := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if x1.Aux != s {
 			break
 		}
-		v.reset(OpAMD64MOVWstore)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(base)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstore [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVWstoreidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
+		if p != x1.Args[0] {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if idx != x1.Args[1] {
 			break
 		}
-		v.reset(OpAMD64MOVWstoreidx1)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstore [off1] {sym1} (LEAQ2 [off2] {sym2} ptr idx) val mem)
-	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
-	// result: (MOVWstoreidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
-	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ2 {
+		if mem != x1.Args[2] {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+		if !(x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
 			break
 		}
-		v.reset(OpAMD64MOVWstoreidx2)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
+		b = mergePoint(b, x0, x1)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWloadidx1, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
 		return true
 	}
-	// match: (MOVWstore [off] {sym} (ADDQ ptr idx) val mem)
-	// cond: ptr.Op != OpSB
-	// result: (MOVWstoreidx1 [off] {sym} ptr idx val mem)
+	// match: (ORL o0:(ORL                        x0:(MOVWloadidx1 [i]   {s} p idx mem)     s0:(SHLLconst [16] x1:(MOVBloadidx1 [i+2] {s} p idx mem)))     s1:(SHLLconst [24] x2:(MOVBloadidx1 [i+3] {s} p idx mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && o0.Uses == 1   && mergePoint(b,x0,x1,x2) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(s0)   && clobber(s1)   && clobber(o0)
+	// result: @mergePoint(b,x0,x1,x2) (MOVLloadidx1 <v.Type> [i] {s} p idx mem)
 	for {
-		off := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
+		o0 := v.Args[0]
+		if o0.Op != OpAMD64ORL {
 			break
 		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(ptr.Op != OpSB) {
+		x0 := o0.Args[0]
+		if x0.Op != OpAMD64MOVWloadidx1 {
 			break
 		}
-		v.reset(OpAMD64MOVWstoreidx1)
-		v.AuxInt = off
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// 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)
-	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHRQconst {
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := o0.Args[1]
+		if s0.Op != OpAMD64SHLLconst {
 			break
 		}
-		if v_1.AuxInt != 16 {
+		if s0.AuxInt != 16 {
 			break
 		}
-		w := v_1.Args[0]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVWstore {
+		x1 := s0.Args[0]
+		if x1.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x.AuxInt != i-2 {
+		if x1.AuxInt != i+2 {
 			break
 		}
-		if x.Aux != s {
+		if x1.Aux != s {
 			break
 		}
-		if p != x.Args[0] {
+		if p != x1.Args[0] {
 			break
 		}
-		if w != x.Args[1] {
+		if idx != x1.Args[1] {
 			break
 		}
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
+		if mem != x1.Args[2] {
 			break
 		}
-		v.reset(OpAMD64MOVLstore)
-		v.AuxInt = i - 2
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(w)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstore [i] {s} p (SHRQconst [j] w) x:(MOVWstore [i-2] {s} p w0:(SHRQconst [j-16] w) mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVLstore [i-2] {s} p w0 mem)
-	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHRQconst {
+		s1 := v.Args[1]
+		if s1.Op != OpAMD64SHLLconst {
 			break
 		}
-		j := v_1.AuxInt
-		w := v_1.Args[0]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVWstore {
+		if s1.AuxInt != 24 {
 			break
 		}
-		if x.AuxInt != i-2 {
+		x2 := s1.Args[0]
+		if x2.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x.Aux != s {
+		if x2.AuxInt != i+3 {
 			break
 		}
-		if p != x.Args[0] {
+		if x2.Aux != s {
 			break
 		}
-		w0 := x.Args[1]
-		if w0.Op != OpAMD64SHRQconst {
+		if p != x2.Args[0] {
 			break
 		}
-		if w0.AuxInt != j-16 {
+		if idx != x2.Args[1] {
 			break
 		}
-		if w != w0.Args[0] {
+		if mem != x2.Args[2] {
 			break
 		}
-		mem := x.Args[2]
-		if !(x.Uses == 1 && clobber(x)) {
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && o0.Uses == 1 && mergePoint(b, x0, x1, x2) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(s0) && clobber(s1) && clobber(o0)) {
 			break
 		}
-		v.reset(OpAMD64MOVLstore)
-		v.AuxInt = i - 2
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(w0)
-		v.AddArg(mem)
+		b = mergePoint(b, x0, x1, x2)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVLloadidx1, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVWstoreconst(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVWstoreconst [sc] {s} (ADDQconst [off] ptr) mem)
-	// cond: ValAndOff(sc).canAdd(off)
-	// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	// match: (ORL o1:(ORL o0:(ORL                        x0:(MOVBload [i] {s} p mem)     s0:(SHLLconst [8]  x1:(MOVBload [i-1] {s} p mem)))     s1:(SHLLconst [16] x2:(MOVBload [i-2] {s} p mem)))     s2:(SHLLconst [24] x3:(MOVBload [i-3] {s} p mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && mergePoint(b,x0,x1,x2,x3) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(x3)   && clobber(s0)   && clobber(s1)   && clobber(s2)   && clobber(o0)   && clobber(o1)
+	// result: @mergePoint(b,x0,x1,x2,x3) (BSWAPL <v.Type> (MOVLload [i-3] {s} p mem))
 	for {
-		sc := v.AuxInt
-		s := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		o1 := v.Args[0]
+		if o1.Op != OpAMD64ORL {
 			break
 		}
-		off := v_0.AuxInt
-		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(ValAndOff(sc).canAdd(off)) {
+		o0 := o1.Args[0]
+		if o0.Op != OpAMD64ORL {
 			break
 		}
-		v.reset(OpAMD64MOVWstoreconst)
-		v.AuxInt = ValAndOff(sc).add(off)
-		v.Aux = s
-		v.AddArg(ptr)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem)
-	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
-	// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
-	for {
-		sc := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ {
+		x0 := o0.Args[0]
+		if x0.Op != OpAMD64MOVBload {
 			break
 		}
-		off := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := o0.Args[1]
+		if s0.Op != OpAMD64SHLLconst {
 			break
 		}
-		v.reset(OpAMD64MOVWstoreconst)
-		v.AuxInt = ValAndOff(sc).add(off)
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreconst [x] {sym1} (LEAQ1 [off] {sym2} ptr idx) mem)
-	// cond: canMergeSym(sym1, sym2)
-	// result: (MOVWstoreconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ1 {
+		if s0.AuxInt != 8 {
 			break
 		}
-		off := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2)) {
+		x1 := s0.Args[0]
+		if x1.Op != OpAMD64MOVBload {
 			break
 		}
-		v.reset(OpAMD64MOVWstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(off)
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreconst [x] {sym1} (LEAQ2 [off] {sym2} ptr idx) mem)
-	// cond: canMergeSym(sym1, sym2)
-	// result: (MOVWstoreconstidx2 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym1 := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64LEAQ2 {
+		if x1.AuxInt != i-1 {
 			break
 		}
-		off := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2)) {
+		if x1.Aux != s {
 			break
 		}
-		v.reset(OpAMD64MOVWstoreconstidx2)
-		v.AuxInt = ValAndOff(x).add(off)
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreconst [x] {sym} (ADDQ ptr idx) mem)
-	// cond:
-	// result: (MOVWstoreconstidx1 [x] {sym} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQ {
+		if p != x1.Args[0] {
 			break
 		}
-		ptr := v_0.Args[0]
-		idx := v_0.Args[1]
-		mem := v.Args[1]
-		v.reset(OpAMD64MOVWstoreconstidx1)
-		v.AuxInt = x
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {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 {
-		c := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		x := v.Args[1]
-		if x.Op != OpAMD64MOVWstoreconst {
+		if mem != x1.Args[1] {
 			break
 		}
-		a := x.AuxInt
-		if x.Aux != s {
+		s1 := o1.Args[1]
+		if s1.Op != OpAMD64SHLLconst {
 			break
 		}
-		if p != x.Args[0] {
+		if s1.AuxInt != 16 {
 			break
 		}
-		mem := x.Args[1]
-		if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
+		x2 := s1.Args[0]
+		if x2.Op != OpAMD64MOVBload {
 			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
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVWstoreconstidx1(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVWstoreconstidx1 [c] {sym} ptr (SHLQconst [1] idx) mem)
-	// cond:
-	// result: (MOVWstoreconstidx2 [c] {sym} ptr idx mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		if x2.AuxInt != i-2 {
 			break
 		}
-		if v_1.AuxInt != 1 {
+		if x2.Aux != s {
 			break
 		}
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWstoreconstidx2)
-		v.AuxInt = c
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreconstidx1 [x] {sym} (ADDQconst [c] ptr) idx mem)
-	// cond:
-	// result: (MOVWstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if p != x2.Args[0] {
 			break
 		}
-		c := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(c)
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreconstidx1 [x] {sym} ptr (ADDQconst [c] idx) mem)
-	// cond:
-	// result: (MOVWstoreconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if mem != x2.Args[1] {
 			break
 		}
-		c := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWstoreconstidx1)
-		v.AuxInt = ValAndOff(x).add(c)
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreconstidx1 [c] {s} p i x:(MOVWstoreconstidx1 [a] {s} p i mem))
-	// cond: x.Uses == 1   && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()   && clobber(x)
-	// result: (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p i mem)
-	for {
-		c := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		i := v.Args[1]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVWstoreconstidx1 {
+		s2 := v.Args[1]
+		if s2.Op != OpAMD64SHLLconst {
 			break
 		}
-		a := x.AuxInt
-		if x.Aux != s {
+		if s2.AuxInt != 24 {
 			break
 		}
-		if p != x.Args[0] {
+		x3 := s2.Args[0]
+		if x3.Op != OpAMD64MOVBload {
 			break
 		}
-		if i != x.Args[1] {
+		if x3.AuxInt != i-3 {
 			break
 		}
-		mem := x.Args[2]
-		if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
+		if x3.Aux != s {
 			break
 		}
-		v.reset(OpAMD64MOVLstoreconstidx1)
-		v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xffff|ValAndOff(c).Val()<<16, ValAndOff(a).Off())
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(i)
-		v.AddArg(mem)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVWstoreconstidx2(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVWstoreconstidx2 [x] {sym} (ADDQconst [c] ptr) idx mem)
-	// cond:
-	// result: (MOVWstoreconstidx2 [ValAndOff(x).add(c)] {sym} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if p != x3.Args[0] {
 			break
 		}
-		c := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWstoreconstidx2)
-		v.AuxInt = ValAndOff(x).add(c)
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreconstidx2 [x] {sym} ptr (ADDQconst [c] idx) mem)
-	// cond:
-	// result: (MOVWstoreconstidx2 [ValAndOff(x).add(2*c)] {sym} ptr idx mem)
-	for {
-		x := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if mem != x3.Args[1] {
 			break
 		}
-		c := v_1.AuxInt
-		idx := v_1.Args[0]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWstoreconstidx2)
-		v.AuxInt = ValAndOff(x).add(2 * c)
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(mem)
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(o0) && clobber(o1)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3)
+		v0 := b.NewValue0(v.Line, OpAMD64BSWAPL, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
+		v1.AuxInt = i - 3
+		v1.Aux = s
+		v1.AddArg(p)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
 		return true
 	}
-	// match: (MOVWstoreconstidx2 [c] {s} p i x:(MOVWstoreconstidx2 [a] {s} p i mem))
-	// cond: x.Uses == 1   && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()   && clobber(x)
-	// result: (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p (SHLQconst <i.Type> [1] i) mem)
+	// match: (ORL o1:(ORL o0:(ORL                        x0:(MOVBloadidx1 [i] {s} p idx mem)     s0:(SHLLconst [8]  x1:(MOVBloadidx1 [i-1] {s} p idx mem)))     s1:(SHLLconst [16] x2:(MOVBloadidx1 [i-2] {s} p idx mem)))     s2:(SHLLconst [24] x3:(MOVBloadidx1 [i-3] {s} p idx mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && mergePoint(b,x0,x1,x2,x3) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(x3)   && clobber(s0)   && clobber(s1)   && clobber(s2)   && clobber(o0)   && clobber(o1)
+	// result: @mergePoint(b,x0,x1,x2,x3) (BSWAPL <v.Type> (MOVLloadidx1 <v.Type> [i-3] {s} p idx mem))
 	for {
-		c := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		i := v.Args[1]
-		x := v.Args[2]
-		if x.Op != OpAMD64MOVWstoreconstidx2 {
+		o1 := v.Args[0]
+		if o1.Op != OpAMD64ORL {
 			break
 		}
-		a := x.AuxInt
-		if x.Aux != s {
+		o0 := o1.Args[0]
+		if o0.Op != OpAMD64ORL {
 			break
 		}
-		if p != x.Args[0] {
+		x0 := o0.Args[0]
+		if x0.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if i != x.Args[1] {
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := o0.Args[1]
+		if s0.Op != OpAMD64SHLLconst {
 			break
 		}
-		mem := x.Args[2]
-		if !(x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
+		if s0.AuxInt != 8 {
 			break
 		}
-		v.reset(OpAMD64MOVLstoreconstidx1)
-		v.AuxInt = makeValAndOff(ValAndOff(a).Val()&0xffff|ValAndOff(c).Val()<<16, ValAndOff(a).Off())
-		v.Aux = s
-		v.AddArg(p)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, i.Type)
-		v0.AuxInt = 1
-		v0.AddArg(i)
-		v.AddArg(v0)
-		v.AddArg(mem)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MOVWstoreidx1(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVWstoreidx1 [c] {sym} ptr (SHLQconst [1] idx) val mem)
-	// cond:
-	// result: (MOVWstoreidx2 [c] {sym} ptr idx val mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64SHLQconst {
+		x1 := s0.Args[0]
+		if x1.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if v_1.AuxInt != 1 {
+		if x1.AuxInt != i-1 {
 			break
 		}
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVWstoreidx2)
-		v.AuxInt = c
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem)
-	// cond:
-	// result: (MOVWstoreidx1 [c+d] {sym} ptr idx val mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		if x1.Aux != s {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVWstoreidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem)
-	// cond:
-	// result: (MOVWstoreidx1 [c+d] {sym} ptr idx val mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
+		if p != x1.Args[0] {
 			break
 		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVWstoreidx1)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreidx1 [i] {s} p idx (SHRQconst [16] w) x:(MOVWstoreidx1 [i-2] {s} p idx w mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVLstoreidx1 [i-2] {s} p idx w mem)
-	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		idx := v.Args[1]
-		v_2 := v.Args[2]
-		if v_2.Op != OpAMD64SHRQconst {
+		if idx != x1.Args[1] {
 			break
 		}
-		if v_2.AuxInt != 16 {
+		if mem != x1.Args[2] {
 			break
 		}
-		w := v_2.Args[0]
-		x := v.Args[3]
-		if x.Op != OpAMD64MOVWstoreidx1 {
+		s1 := o1.Args[1]
+		if s1.Op != OpAMD64SHLLconst {
 			break
 		}
-		if x.AuxInt != i-2 {
+		if s1.AuxInt != 16 {
 			break
 		}
-		if x.Aux != s {
+		x2 := s1.Args[0]
+		if x2.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if p != x.Args[0] {
+		if x2.AuxInt != i-2 {
 			break
 		}
-		if idx != x.Args[1] {
+		if x2.Aux != s {
 			break
 		}
-		if w != x.Args[2] {
+		if p != x2.Args[0] {
 			break
 		}
-		mem := x.Args[3]
-		if !(x.Uses == 1 && clobber(x)) {
+		if idx != x2.Args[1] {
 			break
 		}
-		v.reset(OpAMD64MOVLstoreidx1)
-		v.AuxInt = i - 2
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(idx)
-		v.AddArg(w)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVWstoreidx1 [i-2] {s} p idx w0:(SHRQconst [j-16] w) mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVLstoreidx1 [i-2] {s} p idx w0 mem)
-	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		idx := v.Args[1]
-		v_2 := v.Args[2]
-		if v_2.Op != OpAMD64SHRQconst {
+		if mem != x2.Args[2] {
 			break
 		}
-		j := v_2.AuxInt
-		w := v_2.Args[0]
-		x := v.Args[3]
-		if x.Op != OpAMD64MOVWstoreidx1 {
+		s2 := v.Args[1]
+		if s2.Op != OpAMD64SHLLconst {
 			break
 		}
-		if x.AuxInt != i-2 {
+		if s2.AuxInt != 24 {
 			break
 		}
-		if x.Aux != s {
+		x3 := s2.Args[0]
+		if x3.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if p != x.Args[0] {
+		if x3.AuxInt != i-3 {
 			break
 		}
-		if idx != x.Args[1] {
+		if x3.Aux != s {
 			break
 		}
-		w0 := x.Args[2]
-		if w0.Op != OpAMD64SHRQconst {
+		if p != x3.Args[0] {
 			break
 		}
-		if w0.AuxInt != j-16 {
+		if idx != x3.Args[1] {
 			break
 		}
-		if w != w0.Args[0] {
+		if mem != x3.Args[2] {
 			break
 		}
-		mem := x.Args[3]
-		if !(x.Uses == 1 && clobber(x)) {
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(o0) && clobber(o1)) {
 			break
 		}
-		v.reset(OpAMD64MOVLstoreidx1)
-		v.AuxInt = i - 2
-		v.Aux = s
-		v.AddArg(p)
-		v.AddArg(idx)
-		v.AddArg(w0)
-		v.AddArg(mem)
+		b = mergePoint(b, x0, x1, x2, x3)
+		v0 := b.NewValue0(v.Line, OpAMD64BSWAPL, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64MOVLloadidx1, v.Type)
+		v1.AuxInt = i - 3
+		v1.Aux = s
+		v1.AddArg(p)
+		v1.AddArg(idx)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MOVWstoreidx2(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64ORLconst(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVWstoreidx2 [c] {sym} (ADDQconst [d] ptr) idx val mem)
-	// cond:
-	// result: (MOVWstoreidx2 [c+d] {sym} ptr idx val mem)
+	// match: (ORLconst [c] x)
+	// cond: int32(c)==0
+	// result: x
 	for {
 		c := v.AuxInt
-		sym := v.Aux
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64ADDQconst {
+		x := v.Args[0]
+		if !(int32(c) == 0) {
 			break
 		}
-		d := v_0.AuxInt
-		ptr := v_0.Args[0]
-		idx := v.Args[1]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVWstoreidx2)
-		v.AuxInt = c + d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreidx2 [c] {sym} ptr (ADDQconst [d] idx) val mem)
-	// cond:
-	// result: (MOVWstoreidx2 [c+2*d] {sym} ptr idx val mem)
-	for {
-		c := v.AuxInt
-		sym := v.Aux
-		ptr := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ADDQconst {
-			break
-		}
-		d := v_1.AuxInt
-		idx := v_1.Args[0]
-		val := v.Args[2]
-		mem := v.Args[3]
-		v.reset(OpAMD64MOVWstoreidx2)
-		v.AuxInt = c + 2*d
-		v.Aux = sym
-		v.AddArg(ptr)
-		v.AddArg(idx)
-		v.AddArg(val)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreidx2 [i] {s} p idx (SHRQconst [16] w) x:(MOVWstoreidx2 [i-2] {s} p idx w mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVLstoreidx1 [i-2] {s} p (SHLQconst <idx.Type> [1] idx) w mem)
-	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		idx := v.Args[1]
-		v_2 := v.Args[2]
-		if v_2.Op != OpAMD64SHRQconst {
-			break
-		}
-		if v_2.AuxInt != 16 {
-			break
-		}
-		w := v_2.Args[0]
-		x := v.Args[3]
-		if x.Op != OpAMD64MOVWstoreidx2 {
-			break
-		}
-		if x.AuxInt != i-2 {
-			break
-		}
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
-			break
-		}
-		if idx != x.Args[1] {
-			break
-		}
-		if w != x.Args[2] {
-			break
-		}
-		mem := x.Args[3]
-		if !(x.Uses == 1 && clobber(x)) {
-			break
-		}
-		v.reset(OpAMD64MOVLstoreidx1)
-		v.AuxInt = i - 2
-		v.Aux = s
-		v.AddArg(p)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
-		v0.AuxInt = 1
-		v0.AddArg(idx)
-		v.AddArg(v0)
-		v.AddArg(w)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (MOVWstoreidx2 [i] {s} p idx (SHRQconst [j] w) x:(MOVWstoreidx2 [i-2] {s} p idx w0:(SHRQconst [j-16] w) mem))
-	// cond: x.Uses == 1   && clobber(x)
-	// result: (MOVLstoreidx1 [i-2] {s} p (SHLQconst <idx.Type> [1] idx) w0 mem)
-	for {
-		i := v.AuxInt
-		s := v.Aux
-		p := v.Args[0]
-		idx := v.Args[1]
-		v_2 := v.Args[2]
-		if v_2.Op != OpAMD64SHRQconst {
-			break
-		}
-		j := v_2.AuxInt
-		w := v_2.Args[0]
-		x := v.Args[3]
-		if x.Op != OpAMD64MOVWstoreidx2 {
-			break
-		}
-		if x.AuxInt != i-2 {
-			break
-		}
-		if x.Aux != s {
-			break
-		}
-		if p != x.Args[0] {
-			break
-		}
-		if idx != x.Args[1] {
-			break
-		}
-		w0 := x.Args[2]
-		if w0.Op != OpAMD64SHRQconst {
-			break
-		}
-		if w0.AuxInt != j-16 {
-			break
-		}
-		if w != w0.Args[0] {
-			break
-		}
-		mem := x.Args[3]
-		if !(x.Uses == 1 && clobber(x)) {
-			break
-		}
-		v.reset(OpAMD64MOVLstoreidx1)
-		v.AuxInt = i - 2
-		v.Aux = s
-		v.AddArg(p)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, idx.Type)
-		v0.AuxInt = 1
-		v0.AddArg(idx)
-		v.AddArg(v0)
-		v.AddArg(w0)
-		v.AddArg(mem)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MULL(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MULL x (MOVLconst [c]))
-	// cond:
-	// result: (MULLconst [c] x)
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64MULLconst)
-		v.AuxInt = c
+		v.reset(OpCopy)
+		v.Type = x.Type
 		v.AddArg(x)
 		return true
 	}
-	// match: (MULL (MOVLconst [c]) x)
-	// cond:
-	// result: (MULLconst [c] x)
+	// match: (ORLconst [c] _)
+	// cond: int32(c)==-1
+	// result: (MOVLconst [-1])
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		c := v.AuxInt
+		if !(int32(c) == -1) {
 			break
 		}
-		c := v_0.AuxInt
-		x := v.Args[1]
-		v.reset(OpAMD64MULLconst)
-		v.AuxInt = c
-		v.AddArg(x)
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = -1
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MULLconst(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MULLconst [c] (MOVLconst [d]))
+	// match: (ORLconst [c] (MOVLconst [d]))
 	// cond:
-	// result: (MOVLconst [int64(int32(c*d))])
+	// result: (MOVLconst [c|d])
 	for {
 		c := v.AuxInt
 		v_0 := v.Args[0]
@@ -11452,17 +11416,17 @@ func rewriteValueAMD64_OpAMD64MULLconst(v *Value, config *Config) bool {
 		}
 		d := v_0.AuxInt
 		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = int64(int32(c * d))
+		v.AuxInt = c | d
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64MULQ(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MULQ x (MOVQconst [c]))
+	// match: (ORQ x (MOVQconst [c]))
 	// cond: is32Bit(c)
-	// result: (MULQconst [c] x)
+	// result: (ORQconst [c] x)
 	for {
 		x := v.Args[0]
 		v_1 := v.Args[1]
@@ -11473,14 +11437,14 @@ func rewriteValueAMD64_OpAMD64MULQ(v *Value, config *Config) bool {
 		if !(is32Bit(c)) {
 			break
 		}
-		v.reset(OpAMD64MULQconst)
+		v.reset(OpAMD64ORQconst)
 		v.AuxInt = c
 		v.AddArg(x)
 		return true
 	}
-	// match: (MULQ (MOVQconst [c]) x)
+	// match: (ORQ (MOVQconst [c]) x)
 	// cond: is32Bit(c)
-	// result: (MULQconst [c] x)
+	// result: (ORQconst [c] x)
 	for {
 		v_0 := v.Args[0]
 		if v_0.Op != OpAMD64MOVQconst {
@@ -11491,1517 +11455,458 @@ func rewriteValueAMD64_OpAMD64MULQ(v *Value, config *Config) bool {
 		if !(is32Bit(c)) {
 			break
 		}
-		v.reset(OpAMD64MULQconst)
+		v.reset(OpAMD64ORQconst)
 		v.AuxInt = c
 		v.AddArg(x)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64MULQconst(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MULQconst [-1] x)
+	// match: (ORQ x x)
 	// cond:
-	// result: (NEGQ x)
+	// result: x
 	for {
-		if v.AuxInt != -1 {
+		x := v.Args[0]
+		if x != v.Args[1] {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64NEGQ)
+		v.reset(OpCopy)
+		v.Type = x.Type
 		v.AddArg(x)
 		return true
 	}
-	// match: (MULQconst [0] _)
-	// cond:
-	// result: (MOVQconst [0])
+	// match: (ORQ o0:(ORQ o1:(ORQ o2:(ORQ o3:(ORQ o4:(ORQ o5:(ORQ                        x0:(MOVBload [i]   {s} p mem)     s0:(SHLQconst [8]  x1:(MOVBload [i+1] {s} p mem)))     s1:(SHLQconst [16] x2:(MOVBload [i+2] {s} p mem)))     s2:(SHLQconst [24] x3:(MOVBload [i+3] {s} p mem)))     s3:(SHLQconst [32] x4:(MOVBload [i+4] {s} p mem)))     s4:(SHLQconst [40] x5:(MOVBload [i+5] {s} p mem)))     s5:(SHLQconst [48] x6:(MOVBload [i+6] {s} p mem)))     s6:(SHLQconst [56] x7:(MOVBload [i+7] {s} [...]
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && x4.Uses == 1   && x5.Uses == 1   && x6.Uses == 1   && x7.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && s3.Uses == 1   && s4.Uses == 1   && s5.Uses == 1   && s6.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && o2.Uses == 1   && o3.Uses == 1   && o4.Uses == 1   && o5.Uses == 1   && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clo [...]
+	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQload [i] {s} p mem)
 	for {
-		if v.AuxInt != 0 {
+		o0 := v.Args[0]
+		if o0.Op != OpAMD64ORQ {
 			break
 		}
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = 0
-		return true
-	}
-	// match: (MULQconst [1] x)
-	// cond:
-	// result: x
-	for {
-		if v.AuxInt != 1 {
+		o1 := o0.Args[0]
+		if o1.Op != OpAMD64ORQ {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [3] x)
-	// cond:
-	// result: (LEAQ2 x x)
-	for {
-		if v.AuxInt != 3 {
+		o2 := o1.Args[0]
+		if o2.Op != OpAMD64ORQ {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ2)
-		v.AddArg(x)
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [5] x)
-	// cond:
-	// result: (LEAQ4 x x)
-	for {
-		if v.AuxInt != 5 {
+		o3 := o2.Args[0]
+		if o3.Op != OpAMD64ORQ {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ4)
-		v.AddArg(x)
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [7] x)
-	// cond:
-	// result: (LEAQ8 (NEGQ <v.Type> x) x)
-	for {
-		if v.AuxInt != 7 {
+		o4 := o3.Args[0]
+		if o4.Op != OpAMD64ORQ {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ8)
-		v0 := b.NewValue0(v.Line, OpAMD64NEGQ, v.Type)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [9] x)
-	// cond:
-	// result: (LEAQ8 x x)
-	for {
-		if v.AuxInt != 9 {
+		o5 := o4.Args[0]
+		if o5.Op != OpAMD64ORQ {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ8)
-		v.AddArg(x)
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [11] x)
-	// cond:
-	// result: (LEAQ2 x (LEAQ4 <v.Type> x x))
-	for {
-		if v.AuxInt != 11 {
+		x0 := o5.Args[0]
+		if x0.Op != OpAMD64MOVBload {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ2)
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64LEAQ4, v.Type)
-		v0.AddArg(x)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		return true
-	}
-	// match: (MULQconst [13] x)
-	// cond:
-	// result: (LEAQ4 x (LEAQ2 <v.Type> x x))
-	for {
-		if v.AuxInt != 13 {
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := o5.Args[1]
+		if s0.Op != OpAMD64SHLQconst {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ4)
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64LEAQ2, v.Type)
-		v0.AddArg(x)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		return true
-	}
-	// match: (MULQconst [21] x)
-	// cond:
-	// result: (LEAQ4 x (LEAQ4 <v.Type> x x))
-	for {
-		if v.AuxInt != 21 {
+		if s0.AuxInt != 8 {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ4)
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64LEAQ4, v.Type)
-		v0.AddArg(x)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		return true
-	}
-	// match: (MULQconst [25] x)
-	// cond:
-	// result: (LEAQ8 x (LEAQ2 <v.Type> x x))
-	for {
-		if v.AuxInt != 25 {
+		x1 := s0.Args[0]
+		if x1.Op != OpAMD64MOVBload {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ8)
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64LEAQ2, v.Type)
-		v0.AddArg(x)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		return true
-	}
-	// match: (MULQconst [37] x)
-	// cond:
-	// result: (LEAQ4 x (LEAQ8 <v.Type> x x))
-	for {
-		if v.AuxInt != 37 {
+		if x1.AuxInt != i+1 {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ4)
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64LEAQ8, v.Type)
-		v0.AddArg(x)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		return true
-	}
-	// match: (MULQconst [41] x)
-	// cond:
-	// result: (LEAQ8 x (LEAQ4 <v.Type> x x))
-	for {
-		if v.AuxInt != 41 {
+		if x1.Aux != s {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ8)
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64LEAQ4, v.Type)
-		v0.AddArg(x)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		return true
-	}
-	// match: (MULQconst [73] x)
-	// cond:
-	// result: (LEAQ8 x (LEAQ8 <v.Type> x x))
-	for {
-		if v.AuxInt != 73 {
+		if p != x1.Args[0] {
 			break
 		}
-		x := v.Args[0]
-		v.reset(OpAMD64LEAQ8)
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64LEAQ8, v.Type)
-		v0.AddArg(x)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		return true
-	}
-	// match: (MULQconst [c] x)
-	// cond: isPowerOfTwo(c)
-	// result: (SHLQconst [log2(c)] x)
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(isPowerOfTwo(c)) {
+		if mem != x1.Args[1] {
 			break
 		}
-		v.reset(OpAMD64SHLQconst)
-		v.AuxInt = log2(c)
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [c] x)
-	// cond: isPowerOfTwo(c+1) && c >= 15
-	// result: (SUBQ (SHLQconst <v.Type> [log2(c+1)] x) x)
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(isPowerOfTwo(c+1) && c >= 15) {
+		s1 := o4.Args[1]
+		if s1.Op != OpAMD64SHLQconst {
 			break
 		}
-		v.reset(OpAMD64SUBQ)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, v.Type)
-		v0.AuxInt = log2(c + 1)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [c] x)
-	// cond: isPowerOfTwo(c-1) && c >= 17
-	// result: (LEAQ1 (SHLQconst <v.Type> [log2(c-1)] x) x)
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(isPowerOfTwo(c-1) && c >= 17) {
+		if s1.AuxInt != 16 {
 			break
 		}
-		v.reset(OpAMD64LEAQ1)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, v.Type)
-		v0.AuxInt = log2(c - 1)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [c] x)
-	// cond: isPowerOfTwo(c-2) && c >= 34
-	// result: (LEAQ2 (SHLQconst <v.Type> [log2(c-2)] x) x)
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(isPowerOfTwo(c-2) && c >= 34) {
+		x2 := s1.Args[0]
+		if x2.Op != OpAMD64MOVBload {
 			break
 		}
-		v.reset(OpAMD64LEAQ2)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, v.Type)
-		v0.AuxInt = log2(c - 2)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [c] x)
-	// cond: isPowerOfTwo(c-4) && c >= 68
-	// result: (LEAQ4 (SHLQconst <v.Type> [log2(c-4)] x) x)
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(isPowerOfTwo(c-4) && c >= 68) {
+		if x2.AuxInt != i+2 {
 			break
 		}
-		v.reset(OpAMD64LEAQ4)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, v.Type)
-		v0.AuxInt = log2(c - 4)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [c] x)
-	// cond: isPowerOfTwo(c-8) && c >= 136
-	// result: (LEAQ8 (SHLQconst <v.Type> [log2(c-8)] x) x)
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(isPowerOfTwo(c-8) && c >= 136) {
+		if x2.Aux != s {
 			break
 		}
-		v.reset(OpAMD64LEAQ8)
-		v0 := b.NewValue0(v.Line, OpAMD64SHLQconst, v.Type)
-		v0.AuxInt = log2(c - 8)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		v.AddArg(x)
-		return true
-	}
-	// match: (MULQconst [c] x)
-	// cond: c%3 == 0 && isPowerOfTwo(c/3)
-	// result: (SHLQconst [log2(c/3)] (LEAQ2 <v.Type> x x))
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(c%3 == 0 && isPowerOfTwo(c/3)) {
+		if p != x2.Args[0] {
 			break
 		}
-		v.reset(OpAMD64SHLQconst)
-		v.AuxInt = log2(c / 3)
-		v0 := b.NewValue0(v.Line, OpAMD64LEAQ2, v.Type)
-		v0.AddArg(x)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		return true
-	}
-	// match: (MULQconst [c] x)
-	// cond: c%5 == 0 && isPowerOfTwo(c/5)
-	// result: (SHLQconst [log2(c/5)] (LEAQ4 <v.Type> x x))
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(c%5 == 0 && isPowerOfTwo(c/5)) {
+		if mem != x2.Args[1] {
 			break
 		}
-		v.reset(OpAMD64SHLQconst)
-		v.AuxInt = log2(c / 5)
-		v0 := b.NewValue0(v.Line, OpAMD64LEAQ4, v.Type)
-		v0.AddArg(x)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		return true
-	}
-	// match: (MULQconst [c] x)
-	// cond: c%9 == 0 && isPowerOfTwo(c/9)
-	// result: (SHLQconst [log2(c/9)] (LEAQ8 <v.Type> x x))
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(c%9 == 0 && isPowerOfTwo(c/9)) {
+		s2 := o3.Args[1]
+		if s2.Op != OpAMD64SHLQconst {
 			break
 		}
-		v.reset(OpAMD64SHLQconst)
-		v.AuxInt = log2(c / 9)
-		v0 := b.NewValue0(v.Line, OpAMD64LEAQ8, v.Type)
-		v0.AddArg(x)
-		v0.AddArg(x)
-		v.AddArg(v0)
-		return true
-	}
-	// match: (MULQconst [c] (MOVQconst [d]))
-	// cond:
-	// result: (MOVQconst [c*d])
-	for {
-		c := v.AuxInt
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
+		if s2.AuxInt != 24 {
 			break
 		}
-		d := v_0.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = c * d
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpMod16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mod16  x y)
-	// cond:
-	// result: (MODW  x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MODW)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMod16u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mod16u x y)
-	// cond:
-	// result: (MODWU x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MODWU)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMod32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mod32  x y)
-	// cond:
-	// result: (MODL  x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MODL)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMod32u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mod32u x y)
-	// cond:
-	// result: (MODLU x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MODLU)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMod64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mod64  x y)
-	// cond:
-	// result: (MODQ  x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MODQ)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMod64u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mod64u x y)
-	// cond:
-	// result: (MODQU x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MODQU)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMod8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mod8   x y)
-	// cond:
-	// result: (MODW  (SignExt8to16 x) (SignExt8to16 y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MODW)
-		v0 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
-		v0.AddArg(x)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
-		v1.AddArg(y)
-		v.AddArg(v1)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMod8u(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mod8u  x y)
-	// cond:
-	// result: (MODWU (ZeroExt8to16 x) (ZeroExt8to16 y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MODWU)
-		v0 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
-		v0.AddArg(x)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
-		v1.AddArg(y)
-		v.AddArg(v1)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMove(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Move [0] _ _ mem)
-	// cond:
-	// result: mem
-	for {
-		if v.AuxInt != 0 {
+		x3 := s2.Args[0]
+		if x3.Op != OpAMD64MOVBload {
 			break
 		}
-		mem := v.Args[2]
-		v.reset(OpCopy)
-		v.Type = mem.Type
-		v.AddArg(mem)
-		return true
-	}
-	// match: (Move [1] dst src mem)
-	// cond:
-	// result: (MOVBstore dst (MOVBload src mem) mem)
-	for {
-		if v.AuxInt != 1 {
+		if x3.AuxInt != i+3 {
 			break
 		}
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVBstore)
-		v.AddArg(dst)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, config.fe.TypeUInt8())
-		v0.AddArg(src)
-		v0.AddArg(mem)
-		v.AddArg(v0)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (Move [2] dst src mem)
-	// cond:
-	// result: (MOVWstore dst (MOVWload src mem) mem)
-	for {
-		if v.AuxInt != 2 {
+		if x3.Aux != s {
 			break
 		}
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWstore)
-		v.AddArg(dst)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVWload, config.fe.TypeUInt16())
-		v0.AddArg(src)
-		v0.AddArg(mem)
-		v.AddArg(v0)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (Move [4] dst src mem)
-	// cond:
-	// result: (MOVLstore dst (MOVLload src mem) mem)
-	for {
-		if v.AuxInt != 4 {
+		if p != x3.Args[0] {
 			break
 		}
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVLstore)
-		v.AddArg(dst)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
-		v0.AddArg(src)
-		v0.AddArg(mem)
-		v.AddArg(v0)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (Move [8] dst src mem)
-	// cond:
-	// result: (MOVQstore dst (MOVQload src mem) mem)
-	for {
-		if v.AuxInt != 8 {
+		if mem != x3.Args[1] {
 			break
 		}
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVQstore)
-		v.AddArg(dst)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVQload, config.fe.TypeUInt64())
-		v0.AddArg(src)
-		v0.AddArg(mem)
-		v.AddArg(v0)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (Move [16] dst src mem)
-	// cond:
-	// result: (MOVOstore dst (MOVOload src mem) mem)
-	for {
-		if v.AuxInt != 16 {
+		s3 := o2.Args[1]
+		if s3.Op != OpAMD64SHLQconst {
 			break
 		}
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVOstore)
-		v.AddArg(dst)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVOload, TypeInt128)
-		v0.AddArg(src)
-		v0.AddArg(mem)
-		v.AddArg(v0)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (Move [3] dst src mem)
-	// cond:
-	// result: (MOVBstore [2] dst (MOVBload [2] src mem) 		(MOVWstore dst (MOVWload src mem) mem))
-	for {
-		if v.AuxInt != 3 {
+		if s3.AuxInt != 32 {
 			break
 		}
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVBstore)
-		v.AuxInt = 2
-		v.AddArg(dst)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, config.fe.TypeUInt8())
-		v0.AuxInt = 2
-		v0.AddArg(src)
-		v0.AddArg(mem)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64MOVWstore, TypeMem)
-		v1.AddArg(dst)
-		v2 := b.NewValue0(v.Line, OpAMD64MOVWload, config.fe.TypeUInt16())
-		v2.AddArg(src)
-		v2.AddArg(mem)
-		v1.AddArg(v2)
-		v1.AddArg(mem)
-		v.AddArg(v1)
-		return true
-	}
-	// match: (Move [5] dst src mem)
-	// cond:
-	// result: (MOVBstore [4] dst (MOVBload [4] src mem) 		(MOVLstore dst (MOVLload src mem) mem))
-	for {
-		if v.AuxInt != 5 {
+		x4 := s3.Args[0]
+		if x4.Op != OpAMD64MOVBload {
 			break
 		}
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVBstore)
-		v.AuxInt = 4
-		v.AddArg(dst)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, config.fe.TypeUInt8())
-		v0.AuxInt = 4
-		v0.AddArg(src)
-		v0.AddArg(mem)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64MOVLstore, TypeMem)
-		v1.AddArg(dst)
-		v2 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
-		v2.AddArg(src)
-		v2.AddArg(mem)
-		v1.AddArg(v2)
-		v1.AddArg(mem)
-		v.AddArg(v1)
-		return true
-	}
-	// match: (Move [6] dst src mem)
-	// cond:
-	// result: (MOVWstore [4] dst (MOVWload [4] src mem) 		(MOVLstore dst (MOVLload src mem) mem))
-	for {
-		if v.AuxInt != 6 {
+		if x4.AuxInt != i+4 {
 			break
 		}
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVWstore)
-		v.AuxInt = 4
-		v.AddArg(dst)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVWload, config.fe.TypeUInt16())
-		v0.AuxInt = 4
-		v0.AddArg(src)
-		v0.AddArg(mem)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64MOVLstore, TypeMem)
-		v1.AddArg(dst)
-		v2 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
-		v2.AddArg(src)
-		v2.AddArg(mem)
-		v1.AddArg(v2)
-		v1.AddArg(mem)
-		v.AddArg(v1)
-		return true
-	}
-	// match: (Move [7] dst src mem)
-	// cond:
-	// result: (MOVLstore [3] dst (MOVLload [3] src mem) 		(MOVLstore dst (MOVLload src mem) mem))
-	for {
-		if v.AuxInt != 7 {
+		if x4.Aux != s {
 			break
 		}
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpAMD64MOVLstore)
-		v.AuxInt = 3
-		v.AddArg(dst)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
-		v0.AuxInt = 3
-		v0.AddArg(src)
-		v0.AddArg(mem)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64MOVLstore, TypeMem)
-		v1.AddArg(dst)
-		v2 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
-		v2.AddArg(src)
-		v2.AddArg(mem)
-		v1.AddArg(v2)
-		v1.AddArg(mem)
-		v.AddArg(v1)
-		return true
-	}
-	// match: (Move [size] dst src mem)
-	// cond: size > 8 && size < 16
-	// result: (MOVQstore [size-8] dst (MOVQload [size-8] src mem) 		(MOVQstore dst (MOVQload src mem) mem))
-	for {
-		size := v.AuxInt
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		if !(size > 8 && size < 16) {
+		if p != x4.Args[0] {
 			break
 		}
-		v.reset(OpAMD64MOVQstore)
-		v.AuxInt = size - 8
-		v.AddArg(dst)
+		if mem != x4.Args[1] {
+			break
+		}
+		s4 := o1.Args[1]
+		if s4.Op != OpAMD64SHLQconst {
+			break
+		}
+		if s4.AuxInt != 40 {
+			break
+		}
+		x5 := s4.Args[0]
+		if x5.Op != OpAMD64MOVBload {
+			break
+		}
+		if x5.AuxInt != i+5 {
+			break
+		}
+		if x5.Aux != s {
+			break
+		}
+		if p != x5.Args[0] {
+			break
+		}
+		if mem != x5.Args[1] {
+			break
+		}
+		s5 := o0.Args[1]
+		if s5.Op != OpAMD64SHLQconst {
+			break
+		}
+		if s5.AuxInt != 48 {
+			break
+		}
+		x6 := s5.Args[0]
+		if x6.Op != OpAMD64MOVBload {
+			break
+		}
+		if x6.AuxInt != i+6 {
+			break
+		}
+		if x6.Aux != s {
+			break
+		}
+		if p != x6.Args[0] {
+			break
+		}
+		if mem != x6.Args[1] {
+			break
+		}
+		s6 := v.Args[1]
+		if s6.Op != OpAMD64SHLQconst {
+			break
+		}
+		if s6.AuxInt != 56 {
+			break
+		}
+		x7 := s6.Args[0]
+		if x7.Op != OpAMD64MOVBload {
+			break
+		}
+		if x7.AuxInt != i+7 {
+			break
+		}
+		if x7.Aux != s {
+			break
+		}
+		if p != x7.Args[0] {
+			break
+		}
+		if mem != x7.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clo [...]
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
 		v0 := b.NewValue0(v.Line, OpAMD64MOVQload, config.fe.TypeUInt64())
-		v0.AuxInt = size - 8
-		v0.AddArg(src)
-		v0.AddArg(mem)
+		v.reset(OpCopy)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64MOVQstore, TypeMem)
-		v1.AddArg(dst)
-		v2 := b.NewValue0(v.Line, OpAMD64MOVQload, config.fe.TypeUInt64())
-		v2.AddArg(src)
-		v2.AddArg(mem)
-		v1.AddArg(v2)
-		v1.AddArg(mem)
-		v.AddArg(v1)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(mem)
 		return true
 	}
-	// match: (Move [size] dst src mem)
-	// cond: size > 16 && size%16 != 0 && size%16 <= 8
-	// result: (Move [size-size%16] (ADDQconst <dst.Type> dst [size%16]) (ADDQconst <src.Type> src [size%16]) 		(MOVQstore dst (MOVQload src mem) mem))
+	// match: (ORQ o0:(ORQ o1:(ORQ o2:(ORQ o3:(ORQ o4:(ORQ o5:(ORQ                        x0:(MOVBloadidx1 [i]   {s} p idx mem)     s0:(SHLQconst [8]  x1:(MOVBloadidx1 [i+1] {s} p idx mem)))     s1:(SHLQconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem)))     s2:(SHLQconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem)))     s3:(SHLQconst [32] x4:(MOVBloadidx1 [i+4] {s} p idx mem)))     s4:(SHLQconst [40] x5:(MOVBloadidx1 [i+5] {s} p idx mem)))     s5:(SHLQconst [48] x6:(MOVBloadidx1 [i+6] {s} p  [...]
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && x4.Uses == 1   && x5.Uses == 1   && x6.Uses == 1   && x7.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && s3.Uses == 1   && s4.Uses == 1   && s5.Uses == 1   && s6.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && o2.Uses == 1   && o3.Uses == 1   && o4.Uses == 1   && o5.Uses == 1   && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clo [...]
+	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQloadidx1 <v.Type> [i] {s} p idx mem)
 	for {
-		size := v.AuxInt
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		if !(size > 16 && size%16 != 0 && size%16 <= 8) {
+		o0 := v.Args[0]
+		if o0.Op != OpAMD64ORQ {
 			break
 		}
-		v.reset(OpMove)
-		v.AuxInt = size - size%16
-		v0 := b.NewValue0(v.Line, OpAMD64ADDQconst, dst.Type)
-		v0.AddArg(dst)
-		v0.AuxInt = size % 16
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64ADDQconst, src.Type)
-		v1.AddArg(src)
-		v1.AuxInt = size % 16
-		v.AddArg(v1)
-		v2 := b.NewValue0(v.Line, OpAMD64MOVQstore, TypeMem)
-		v2.AddArg(dst)
-		v3 := b.NewValue0(v.Line, OpAMD64MOVQload, config.fe.TypeUInt64())
-		v3.AddArg(src)
-		v3.AddArg(mem)
-		v2.AddArg(v3)
-		v2.AddArg(mem)
-		v.AddArg(v2)
-		return true
-	}
-	// match: (Move [size] dst src mem)
-	// cond: size > 16 && size%16 != 0 && size%16 > 8
-	// result: (Move [size-size%16] (ADDQconst <dst.Type> dst [size%16]) (ADDQconst <src.Type> src [size%16]) 		(MOVOstore dst (MOVOload src mem) mem))
-	for {
-		size := v.AuxInt
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		if !(size > 16 && size%16 != 0 && size%16 > 8) {
+		o1 := o0.Args[0]
+		if o1.Op != OpAMD64ORQ {
 			break
 		}
-		v.reset(OpMove)
-		v.AuxInt = size - size%16
-		v0 := b.NewValue0(v.Line, OpAMD64ADDQconst, dst.Type)
-		v0.AddArg(dst)
-		v0.AuxInt = size % 16
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64ADDQconst, src.Type)
-		v1.AddArg(src)
-		v1.AuxInt = size % 16
-		v.AddArg(v1)
-		v2 := b.NewValue0(v.Line, OpAMD64MOVOstore, TypeMem)
-		v2.AddArg(dst)
-		v3 := b.NewValue0(v.Line, OpAMD64MOVOload, TypeInt128)
-		v3.AddArg(src)
-		v3.AddArg(mem)
-		v2.AddArg(v3)
-		v2.AddArg(mem)
-		v.AddArg(v2)
-		return true
-	}
-	// match: (Move [size] dst src mem)
-	// cond: size >= 32 && size <= 16*64 && size%16 == 0 && !config.noDuffDevice
-	// result: (DUFFCOPY [14*(64-size/16)] dst src mem)
-	for {
-		size := v.AuxInt
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		if !(size >= 32 && size <= 16*64 && size%16 == 0 && !config.noDuffDevice) {
+		o2 := o1.Args[0]
+		if o2.Op != OpAMD64ORQ {
 			break
 		}
-		v.reset(OpAMD64DUFFCOPY)
-		v.AuxInt = 14 * (64 - size/16)
-		v.AddArg(dst)
-		v.AddArg(src)
-		v.AddArg(mem)
-		return true
-	}
-	// match: (Move [size] dst src mem)
-	// cond: (size > 16*64 || config.noDuffDevice) && size%8 == 0
-	// result: (REPMOVSQ dst src (MOVQconst [size/8]) mem)
-	for {
-		size := v.AuxInt
-		dst := v.Args[0]
-		src := v.Args[1]
-		mem := v.Args[2]
-		if !((size > 16*64 || config.noDuffDevice) && size%8 == 0) {
+		o3 := o2.Args[0]
+		if o3.Op != OpAMD64ORQ {
 			break
 		}
-		v.reset(OpAMD64REPMOVSQ)
-		v.AddArg(dst)
-		v.AddArg(src)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
-		v0.AuxInt = size / 8
-		v.AddArg(v0)
-		v.AddArg(mem)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpMul16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mul16  x y)
-	// cond:
-	// result: (MULL  x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MULL)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMul32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mul32  x y)
-	// cond:
-	// result: (MULL  x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MULL)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMul32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mul32F x y)
-	// cond:
-	// result: (MULSS x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MULSS)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMul64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mul64  x y)
-	// cond:
-	// result: (MULQ  x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MULQ)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMul64F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mul64F x y)
-	// cond:
-	// result: (MULSD x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MULSD)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpMul8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Mul8   x y)
-	// cond:
-	// result: (MULL  x y)
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64MULL)
-		v.AddArg(x)
-		v.AddArg(y)
-		return true
-	}
-}
-func rewriteValueAMD64_OpAMD64NEGL(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (NEGL (MOVLconst [c]))
-	// cond:
-	// result: (MOVLconst [int64(int32(-c))])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		o4 := o3.Args[0]
+		if o4.Op != OpAMD64ORQ {
 			break
 		}
-		c := v_0.AuxInt
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = int64(int32(-c))
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64NEGQ(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (NEGQ (MOVQconst [c]))
-	// cond:
-	// result: (MOVQconst [-c])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
+		o5 := o4.Args[0]
+		if o5.Op != OpAMD64ORQ {
 			break
 		}
-		c := v_0.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = -c
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64NOTL(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (NOTL (MOVLconst [c]))
-	// cond:
-	// result: (MOVLconst [^c])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		x0 := o5.Args[0]
+		if x0.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		c := v_0.AuxInt
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = ^c
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64NOTQ(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (NOTQ (MOVQconst [c]))
-	// cond:
-	// result: (MOVQconst [^c])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := o5.Args[1]
+		if s0.Op != OpAMD64SHLQconst {
 			break
 		}
-		c := v_0.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = ^c
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpNeg16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neg16  x)
-	// cond:
-	// result: (NEGL x)
-	for {
-		x := v.Args[0]
-		v.reset(OpAMD64NEGL)
-		v.AddArg(x)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeg32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neg32  x)
-	// cond:
-	// result: (NEGL x)
-	for {
-		x := v.Args[0]
-		v.reset(OpAMD64NEGL)
-		v.AddArg(x)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeg32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neg32F x)
-	// cond:
-	// result: (PXOR x (MOVSSconst <config.Frontend().TypeFloat32()> [f2i(math.Copysign(0, -1))]))
-	for {
-		x := v.Args[0]
-		v.reset(OpAMD64PXOR)
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVSSconst, config.Frontend().TypeFloat32())
-		v0.AuxInt = f2i(math.Copysign(0, -1))
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeg64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neg64  x)
-	// cond:
-	// result: (NEGQ x)
-	for {
-		x := v.Args[0]
-		v.reset(OpAMD64NEGQ)
-		v.AddArg(x)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeg64F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neg64F x)
-	// cond:
-	// result: (PXOR x (MOVSDconst <config.Frontend().TypeFloat64()> [f2i(math.Copysign(0, -1))]))
-	for {
-		x := v.Args[0]
-		v.reset(OpAMD64PXOR)
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVSDconst, config.Frontend().TypeFloat64())
-		v0.AuxInt = f2i(math.Copysign(0, -1))
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeg8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neg8   x)
-	// cond:
-	// result: (NEGL x)
-	for {
-		x := v.Args[0]
-		v.reset(OpAMD64NEGL)
-		v.AddArg(x)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeq16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neq16  x y)
-	// cond:
-	// result: (SETNE (CMPW x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETNE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeq32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neq32  x y)
-	// cond:
-	// result: (SETNE (CMPL x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETNE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeq32F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neq32F x y)
-	// cond:
-	// result: (SETNEF (UCOMISS x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETNEF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeq64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neq64  x y)
-	// cond:
-	// result: (SETNE (CMPQ x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETNE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeq64F(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neq64F x y)
-	// cond:
-	// result: (SETNEF (UCOMISD x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETNEF)
-		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeq8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Neq8   x y)
-	// cond:
-	// result: (SETNE (CMPB x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETNE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeqB(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (NeqB   x y)
-	// cond:
-	// result: (SETNE (CMPB x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETNE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNeqPtr(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (NeqPtr x y)
-	// cond:
-	// result: (SETNE (CMPQ x y))
-	for {
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SETNE)
-		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNilCheck(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (NilCheck ptr mem)
-	// cond:
-	// result: (LoweredNilCheck ptr mem)
-	for {
-		ptr := v.Args[0]
-		mem := v.Args[1]
-		v.reset(OpAMD64LoweredNilCheck)
-		v.AddArg(ptr)
-		v.AddArg(mem)
-		return true
-	}
-}
-func rewriteValueAMD64_OpNot(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Not x)
-	// cond:
-	// result: (XORLconst [1] x)
-	for {
-		x := v.Args[0]
-		v.reset(OpAMD64XORLconst)
-		v.AuxInt = 1
-		v.AddArg(x)
-		return true
-	}
-}
-func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (ORL x (MOVLconst [c]))
-	// cond:
-	// result: (ORLconst [c] x)
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
+		if s0.AuxInt != 8 {
 			break
 		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64ORLconst)
-		v.AuxInt = c
-		v.AddArg(x)
-		return true
-	}
-	// match: (ORL (MOVLconst [c]) x)
-	// cond:
-	// result: (ORLconst [c] x)
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
+		x1 := s0.Args[0]
+		if x1.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		c := v_0.AuxInt
-		x := v.Args[1]
-		v.reset(OpAMD64ORLconst)
-		v.AuxInt = c
-		v.AddArg(x)
-		return true
-	}
-	// match: (ORL x x)
-	// cond:
-	// result: x
-	for {
-		x := v.Args[0]
-		if x != v.Args[1] {
+		if x1.AuxInt != i+1 {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (ORL                  x0:(MOVBload [i]   {s} p mem)     s0:(SHLLconst [8] x1:(MOVBload [i+1] {s} p mem)))
-	// cond: x0.Uses == 1   && x1.Uses == 1   && s0.Uses == 1   && mergePoint(b,x0,x1) != nil   && clobber(x0)   && clobber(x1)   && clobber(s0)
-	// result: @mergePoint(b,x0,x1) (MOVWload [i] {s} p mem)
-	for {
-		x0 := v.Args[0]
-		if x0.Op != OpAMD64MOVBload {
+		if x1.Aux != s {
 			break
 		}
-		i := x0.AuxInt
-		s := x0.Aux
-		p := x0.Args[0]
-		mem := x0.Args[1]
-		s0 := v.Args[1]
-		if s0.Op != OpAMD64SHLLconst {
+		if p != x1.Args[0] {
 			break
 		}
-		if s0.AuxInt != 8 {
+		if idx != x1.Args[1] {
 			break
 		}
-		x1 := s0.Args[0]
-		if x1.Op != OpAMD64MOVBload {
+		if mem != x1.Args[2] {
 			break
 		}
-		if x1.AuxInt != i+1 {
+		s1 := o4.Args[1]
+		if s1.Op != OpAMD64SHLQconst {
 			break
 		}
-		if x1.Aux != s {
+		if s1.AuxInt != 16 {
 			break
 		}
-		if p != x1.Args[0] {
+		x2 := s1.Args[0]
+		if x2.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if mem != x1.Args[1] {
+		if x2.AuxInt != i+2 {
 			break
 		}
-		if !(x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
+		if x2.Aux != s {
 			break
 		}
-		b = mergePoint(b, x0, x1)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVWload, config.fe.TypeUInt16())
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = i
-		v0.Aux = s
-		v0.AddArg(p)
-		v0.AddArg(mem)
-		return true
-	}
-	// match: (ORL o0:(ORL o1:(ORL                        x0:(MOVBload [i]   {s} p mem)     s0:(SHLLconst [8]  x1:(MOVBload [i+1] {s} p mem)))     s1:(SHLLconst [16] x2:(MOVBload [i+2] {s} p mem)))     s2:(SHLLconst [24] x3:(MOVBload [i+3] {s} p mem)))
-	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && mergePoint(b,x0,x1,x2,x3) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(x3)   && clobber(s0)   && clobber(s1)   && clobber(s2)   && clobber(o0)   && clobber(o1)
-	// result: @mergePoint(b,x0,x1,x2,x3) (MOVLload [i] {s} p mem)
-	for {
-		o0 := v.Args[0]
-		if o0.Op != OpAMD64ORL {
+		if p != x2.Args[0] {
 			break
 		}
-		o1 := o0.Args[0]
-		if o1.Op != OpAMD64ORL {
+		if idx != x2.Args[1] {
 			break
 		}
-		x0 := o1.Args[0]
-		if x0.Op != OpAMD64MOVBload {
+		if mem != x2.Args[2] {
 			break
 		}
-		i := x0.AuxInt
-		s := x0.Aux
-		p := x0.Args[0]
-		mem := x0.Args[1]
-		s0 := o1.Args[1]
-		if s0.Op != OpAMD64SHLLconst {
+		s2 := o3.Args[1]
+		if s2.Op != OpAMD64SHLQconst {
 			break
 		}
-		if s0.AuxInt != 8 {
+		if s2.AuxInt != 24 {
 			break
 		}
-		x1 := s0.Args[0]
-		if x1.Op != OpAMD64MOVBload {
+		x3 := s2.Args[0]
+		if x3.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x1.AuxInt != i+1 {
+		if x3.AuxInt != i+3 {
 			break
 		}
-		if x1.Aux != s {
+		if x3.Aux != s {
 			break
 		}
-		if p != x1.Args[0] {
+		if p != x3.Args[0] {
 			break
 		}
-		if mem != x1.Args[1] {
+		if idx != x3.Args[1] {
 			break
 		}
-		s1 := o0.Args[1]
-		if s1.Op != OpAMD64SHLLconst {
+		if mem != x3.Args[2] {
 			break
 		}
-		if s1.AuxInt != 16 {
+		s3 := o2.Args[1]
+		if s3.Op != OpAMD64SHLQconst {
 			break
 		}
-		x2 := s1.Args[0]
-		if x2.Op != OpAMD64MOVBload {
+		if s3.AuxInt != 32 {
 			break
 		}
-		if x2.AuxInt != i+2 {
+		x4 := s3.Args[0]
+		if x4.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x2.Aux != s {
+		if x4.AuxInt != i+4 {
 			break
 		}
-		if p != x2.Args[0] {
+		if x4.Aux != s {
 			break
 		}
-		if mem != x2.Args[1] {
+		if p != x4.Args[0] {
 			break
 		}
-		s2 := v.Args[1]
-		if s2.Op != OpAMD64SHLLconst {
+		if idx != x4.Args[1] {
 			break
 		}
-		if s2.AuxInt != 24 {
+		if mem != x4.Args[2] {
 			break
 		}
-		x3 := s2.Args[0]
-		if x3.Op != OpAMD64MOVBload {
+		s4 := o1.Args[1]
+		if s4.Op != OpAMD64SHLQconst {
 			break
 		}
-		if x3.AuxInt != i+3 {
+		if s4.AuxInt != 40 {
 			break
 		}
-		if x3.Aux != s {
+		x5 := s4.Args[0]
+		if x5.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if p != x3.Args[0] {
+		if x5.AuxInt != i+5 {
 			break
 		}
-		if mem != x3.Args[1] {
+		if x5.Aux != s {
 			break
 		}
-		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(o0) && clobber(o1)) {
+		if p != x5.Args[0] {
 			break
 		}
-		b = mergePoint(b, x0, x1, x2, x3)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = i
-		v0.Aux = s
-		v0.AddArg(p)
-		v0.AddArg(mem)
-		return true
-	}
-	// match: (ORL                  x0:(MOVBloadidx1 [i]   {s} p idx mem)     s0:(SHLLconst [8] x1:(MOVBloadidx1 [i+1] {s} p idx mem)))
-	// cond: x0.Uses == 1   && x1.Uses == 1   && s0.Uses == 1   && mergePoint(b,x0,x1) != nil   && clobber(x0)   && clobber(x1)   && clobber(s0)
-	// result: @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i] {s} p idx mem)
-	for {
-		x0 := v.Args[0]
-		if x0.Op != OpAMD64MOVBloadidx1 {
+		if idx != x5.Args[1] {
 			break
 		}
-		i := x0.AuxInt
-		s := x0.Aux
-		p := x0.Args[0]
-		idx := x0.Args[1]
-		mem := x0.Args[2]
-		s0 := v.Args[1]
-		if s0.Op != OpAMD64SHLLconst {
+		if mem != x5.Args[2] {
 			break
 		}
-		if s0.AuxInt != 8 {
+		s5 := o0.Args[1]
+		if s5.Op != OpAMD64SHLQconst {
 			break
 		}
-		x1 := s0.Args[0]
-		if x1.Op != OpAMD64MOVBloadidx1 {
+		if s5.AuxInt != 48 {
 			break
 		}
-		if x1.AuxInt != i+1 {
+		x6 := s5.Args[0]
+		if x6.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x1.Aux != s {
+		if x6.AuxInt != i+6 {
 			break
 		}
-		if p != x1.Args[0] {
+		if x6.Aux != s {
 			break
 		}
-		if idx != x1.Args[1] {
+		if p != x6.Args[0] {
 			break
 		}
-		if mem != x1.Args[2] {
+		if idx != x6.Args[1] {
 			break
 		}
-		if !(x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
+		if mem != x6.Args[2] {
 			break
 		}
-		b = mergePoint(b, x0, x1)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVWloadidx1, v.Type)
+		s6 := v.Args[1]
+		if s6.Op != OpAMD64SHLQconst {
+			break
+		}
+		if s6.AuxInt != 56 {
+			break
+		}
+		x7 := s6.Args[0]
+		if x7.Op != OpAMD64MOVBloadidx1 {
+			break
+		}
+		if x7.AuxInt != i+7 {
+			break
+		}
+		if x7.Aux != s {
+			break
+		}
+		if p != x7.Args[0] {
+			break
+		}
+		if idx != x7.Args[1] {
+			break
+		}
+		if mem != x7.Args[2] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clo [...]
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVQloadidx1, v.Type)
 		v.reset(OpCopy)
 		v.AddArg(v0)
 		v0.AuxInt = i
@@ -13011,39 +11916,54 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
 		v0.AddArg(mem)
 		return true
 	}
-	// match: (ORL o0:(ORL o1:(ORL                        x0:(MOVBloadidx1 [i]   {s} p idx mem)     s0:(SHLLconst [8]  x1:(MOVBloadidx1 [i+1] {s} p idx mem)))     s1:(SHLLconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem)))     s2:(SHLLconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem)))
-	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && mergePoint(b,x0,x1,x2,x3) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(x3)   && clobber(s0)   && clobber(s1)   && clobber(s2)   && clobber(o0)   && clobber(o1)
-	// result: @mergePoint(b,x0,x1,x2,x3) (MOVLloadidx1 <v.Type> [i] {s} p idx mem)
+	// match: (ORQ o5:(ORQ o4:(ORQ o3:(ORQ o2:(ORQ o1:(ORQ o0:(ORQ                        x0:(MOVBload [i] {s} p mem)     s0:(SHLQconst [8]  x1:(MOVBload [i-1] {s} p mem)))     s1:(SHLQconst [16] x2:(MOVBload [i-2] {s} p mem)))     s2:(SHLQconst [24] x3:(MOVBload [i-3] {s} p mem)))     s3:(SHLQconst [32] x4:(MOVBload [i-4] {s} p mem)))     s4:(SHLQconst [40] x5:(MOVBload [i-5] {s} p mem)))     s5:(SHLQconst [48] x6:(MOVBload [i-6] {s} p mem)))     s6:(SHLQconst [56] x7:(MOVBload [i-7] {s} p mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && x4.Uses == 1   && x5.Uses == 1   && x6.Uses == 1   && x7.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && s3.Uses == 1   && s4.Uses == 1   && s5.Uses == 1   && s6.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && o2.Uses == 1   && o3.Uses == 1   && o4.Uses == 1   && o5.Uses == 1   && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clo [...]
+	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (BSWAPQ <v.Type> (MOVQload [i-7] {s} p mem))
 	for {
-		o0 := v.Args[0]
-		if o0.Op != OpAMD64ORL {
+		o5 := v.Args[0]
+		if o5.Op != OpAMD64ORQ {
 			break
 		}
-		o1 := o0.Args[0]
-		if o1.Op != OpAMD64ORL {
+		o4 := o5.Args[0]
+		if o4.Op != OpAMD64ORQ {
 			break
 		}
-		x0 := o1.Args[0]
-		if x0.Op != OpAMD64MOVBloadidx1 {
+		o3 := o4.Args[0]
+		if o3.Op != OpAMD64ORQ {
+			break
+		}
+		o2 := o3.Args[0]
+		if o2.Op != OpAMD64ORQ {
+			break
+		}
+		o1 := o2.Args[0]
+		if o1.Op != OpAMD64ORQ {
+			break
+		}
+		o0 := o1.Args[0]
+		if o0.Op != OpAMD64ORQ {
+			break
+		}
+		x0 := o0.Args[0]
+		if x0.Op != OpAMD64MOVBload {
 			break
 		}
 		i := x0.AuxInt
 		s := x0.Aux
 		p := x0.Args[0]
-		idx := x0.Args[1]
-		mem := x0.Args[2]
-		s0 := o1.Args[1]
-		if s0.Op != OpAMD64SHLLconst {
+		mem := x0.Args[1]
+		s0 := o0.Args[1]
+		if s0.Op != OpAMD64SHLQconst {
 			break
 		}
 		if s0.AuxInt != 8 {
 			break
 		}
 		x1 := s0.Args[0]
-		if x1.Op != OpAMD64MOVBloadidx1 {
+		if x1.Op != OpAMD64MOVBload {
 			break
 		}
-		if x1.AuxInt != i+1 {
+		if x1.AuxInt != i-1 {
 			break
 		}
 		if x1.Aux != s {
@@ -13052,24 +11972,21 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
 		if p != x1.Args[0] {
 			break
 		}
-		if idx != x1.Args[1] {
-			break
-		}
-		if mem != x1.Args[2] {
+		if mem != x1.Args[1] {
 			break
 		}
-		s1 := o0.Args[1]
-		if s1.Op != OpAMD64SHLLconst {
+		s1 := o1.Args[1]
+		if s1.Op != OpAMD64SHLQconst {
 			break
 		}
 		if s1.AuxInt != 16 {
 			break
 		}
 		x2 := s1.Args[0]
-		if x2.Op != OpAMD64MOVBloadidx1 {
+		if x2.Op != OpAMD64MOVBload {
 			break
 		}
-		if x2.AuxInt != i+2 {
+		if x2.AuxInt != i-2 {
 			break
 		}
 		if x2.Aux != s {
@@ -13078,24 +11995,21 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
 		if p != x2.Args[0] {
 			break
 		}
-		if idx != x2.Args[1] {
-			break
-		}
-		if mem != x2.Args[2] {
+		if mem != x2.Args[1] {
 			break
 		}
-		s2 := v.Args[1]
-		if s2.Op != OpAMD64SHLLconst {
+		s2 := o2.Args[1]
+		if s2.Op != OpAMD64SHLQconst {
 			break
 		}
 		if s2.AuxInt != 24 {
 			break
 		}
 		x3 := s2.Args[0]
-		if x3.Op != OpAMD64MOVBloadidx1 {
+		if x3.Op != OpAMD64MOVBload {
 			break
 		}
-		if x3.AuxInt != i+3 {
+		if x3.AuxInt != i-3 {
 			break
 		}
 		if x3.Aux != s {
@@ -13104,242 +12018,21 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value, config *Config) bool {
 		if p != x3.Args[0] {
 			break
 		}
-		if idx != x3.Args[1] {
+		if mem != x3.Args[1] {
 			break
 		}
-		if mem != x3.Args[2] {
+		s3 := o3.Args[1]
+		if s3.Op != OpAMD64SHLQconst {
 			break
 		}
-		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(s0) && clobber(s1) && clobber(s2) && clobber(o0) && clobber(o1)) {
+		if s3.AuxInt != 32 {
 			break
 		}
-		b = mergePoint(b, x0, x1, x2, x3)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVLloadidx1, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = i
-		v0.Aux = s
-		v0.AddArg(p)
-		v0.AddArg(idx)
-		v0.AddArg(mem)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64ORLconst(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (ORLconst [c] x)
-	// cond: int32(c)==0
-	// result: x
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(int32(c) == 0) {
+		x4 := s3.Args[0]
+		if x4.Op != OpAMD64MOVBload {
 			break
 		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (ORLconst [c] _)
-	// cond: int32(c)==-1
-	// result: (MOVLconst [-1])
-	for {
-		c := v.AuxInt
-		if !(int32(c) == -1) {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = -1
-		return true
-	}
-	// match: (ORLconst [c] (MOVLconst [d]))
-	// cond:
-	// result: (MOVLconst [c|d])
-	for {
-		c := v.AuxInt
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
-			break
-		}
-		d := v_0.AuxInt
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = c | d
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (ORQ x (MOVQconst [c]))
-	// cond: is32Bit(c)
-	// result: (ORQconst [c] x)
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		if !(is32Bit(c)) {
-			break
-		}
-		v.reset(OpAMD64ORQconst)
-		v.AuxInt = c
-		v.AddArg(x)
-		return true
-	}
-	// match: (ORQ (MOVQconst [c]) x)
-	// cond: is32Bit(c)
-	// result: (ORQconst [c] x)
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_0.AuxInt
-		x := v.Args[1]
-		if !(is32Bit(c)) {
-			break
-		}
-		v.reset(OpAMD64ORQconst)
-		v.AuxInt = c
-		v.AddArg(x)
-		return true
-	}
-	// match: (ORQ x x)
-	// cond:
-	// result: x
-	for {
-		x := v.Args[0]
-		if x != v.Args[1] {
-			break
-		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (ORQ o0:(ORQ o1:(ORQ o2:(ORQ o3:(ORQ o4:(ORQ o5:(ORQ                        x0:(MOVBload [i]   {s} p mem)     s0:(SHLQconst [8]  x1:(MOVBload [i+1] {s} p mem)))     s1:(SHLQconst [16] x2:(MOVBload [i+2] {s} p mem)))     s2:(SHLQconst [24] x3:(MOVBload [i+3] {s} p mem)))     s3:(SHLQconst [32] x4:(MOVBload [i+4] {s} p mem)))     s4:(SHLQconst [40] x5:(MOVBload [i+5] {s} p mem)))     s5:(SHLQconst [48] x6:(MOVBload [i+6] {s} p mem)))     s6:(SHLQconst [56] x7:(MOVBload [i+7] {s} [...]
-	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && x4.Uses == 1   && x5.Uses == 1   && x6.Uses == 1   && x7.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && s3.Uses == 1   && s4.Uses == 1   && s5.Uses == 1   && s6.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && o2.Uses == 1   && o3.Uses == 1   && o4.Uses == 1   && o5.Uses == 1   && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clo [...]
-	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQload [i] {s} p mem)
-	for {
-		o0 := v.Args[0]
-		if o0.Op != OpAMD64ORQ {
-			break
-		}
-		o1 := o0.Args[0]
-		if o1.Op != OpAMD64ORQ {
-			break
-		}
-		o2 := o1.Args[0]
-		if o2.Op != OpAMD64ORQ {
-			break
-		}
-		o3 := o2.Args[0]
-		if o3.Op != OpAMD64ORQ {
-			break
-		}
-		o4 := o3.Args[0]
-		if o4.Op != OpAMD64ORQ {
-			break
-		}
-		o5 := o4.Args[0]
-		if o5.Op != OpAMD64ORQ {
-			break
-		}
-		x0 := o5.Args[0]
-		if x0.Op != OpAMD64MOVBload {
-			break
-		}
-		i := x0.AuxInt
-		s := x0.Aux
-		p := x0.Args[0]
-		mem := x0.Args[1]
-		s0 := o5.Args[1]
-		if s0.Op != OpAMD64SHLQconst {
-			break
-		}
-		if s0.AuxInt != 8 {
-			break
-		}
-		x1 := s0.Args[0]
-		if x1.Op != OpAMD64MOVBload {
-			break
-		}
-		if x1.AuxInt != i+1 {
-			break
-		}
-		if x1.Aux != s {
-			break
-		}
-		if p != x1.Args[0] {
-			break
-		}
-		if mem != x1.Args[1] {
-			break
-		}
-		s1 := o4.Args[1]
-		if s1.Op != OpAMD64SHLQconst {
-			break
-		}
-		if s1.AuxInt != 16 {
-			break
-		}
-		x2 := s1.Args[0]
-		if x2.Op != OpAMD64MOVBload {
-			break
-		}
-		if x2.AuxInt != i+2 {
-			break
-		}
-		if x2.Aux != s {
-			break
-		}
-		if p != x2.Args[0] {
-			break
-		}
-		if mem != x2.Args[1] {
-			break
-		}
-		s2 := o3.Args[1]
-		if s2.Op != OpAMD64SHLQconst {
-			break
-		}
-		if s2.AuxInt != 24 {
-			break
-		}
-		x3 := s2.Args[0]
-		if x3.Op != OpAMD64MOVBload {
-			break
-		}
-		if x3.AuxInt != i+3 {
-			break
-		}
-		if x3.Aux != s {
-			break
-		}
-		if p != x3.Args[0] {
-			break
-		}
-		if mem != x3.Args[1] {
-			break
-		}
-		s3 := o2.Args[1]
-		if s3.Op != OpAMD64SHLQconst {
-			break
-		}
-		if s3.AuxInt != 32 {
-			break
-		}
-		x4 := s3.Args[0]
-		if x4.Op != OpAMD64MOVBload {
-			break
-		}
-		if x4.AuxInt != i+4 {
+		if x4.AuxInt != i-4 {
 			break
 		}
 		if x4.Aux != s {
@@ -13351,7 +12044,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if mem != x4.Args[1] {
 			break
 		}
-		s4 := o1.Args[1]
+		s4 := o4.Args[1]
 		if s4.Op != OpAMD64SHLQconst {
 			break
 		}
@@ -13362,7 +12055,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if x5.Op != OpAMD64MOVBload {
 			break
 		}
-		if x5.AuxInt != i+5 {
+		if x5.AuxInt != i-5 {
 			break
 		}
 		if x5.Aux != s {
@@ -13374,7 +12067,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if mem != x5.Args[1] {
 			break
 		}
-		s5 := o0.Args[1]
+		s5 := o5.Args[1]
 		if s5.Op != OpAMD64SHLQconst {
 			break
 		}
@@ -13385,7 +12078,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if x6.Op != OpAMD64MOVBload {
 			break
 		}
-		if x6.AuxInt != i+6 {
+		if x6.AuxInt != i-6 {
 			break
 		}
 		if x6.Aux != s {
@@ -13408,7 +12101,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if x7.Op != OpAMD64MOVBload {
 			break
 		}
-		if x7.AuxInt != i+7 {
+		if x7.AuxInt != i-7 {
 			break
 		}
 		if x7.Aux != s {
@@ -13424,44 +12117,46 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 			break
 		}
 		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVQload, config.fe.TypeUInt64())
+		v0 := b.NewValue0(v.Line, OpAMD64BSWAPQ, v.Type)
 		v.reset(OpCopy)
 		v.AddArg(v0)
-		v0.AuxInt = i
-		v0.Aux = s
-		v0.AddArg(p)
-		v0.AddArg(mem)
+		v1 := b.NewValue0(v.Line, OpAMD64MOVQload, config.fe.TypeUInt64())
+		v1.AuxInt = i - 7
+		v1.Aux = s
+		v1.AddArg(p)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
 		return true
 	}
-	// match: (ORQ o0:(ORQ o1:(ORQ o2:(ORQ o3:(ORQ o4:(ORQ o5:(ORQ                        x0:(MOVBloadidx1 [i]   {s} p idx mem)     s0:(SHLQconst [8]  x1:(MOVBloadidx1 [i+1] {s} p idx mem)))     s1:(SHLQconst [16] x2:(MOVBloadidx1 [i+2] {s} p idx mem)))     s2:(SHLQconst [24] x3:(MOVBloadidx1 [i+3] {s} p idx mem)))     s3:(SHLQconst [32] x4:(MOVBloadidx1 [i+4] {s} p idx mem)))     s4:(SHLQconst [40] x5:(MOVBloadidx1 [i+5] {s} p idx mem)))     s5:(SHLQconst [48] x6:(MOVBloadidx1 [i+6] {s} p  [...]
+	// match: (ORQ o5:(ORQ o4:(ORQ o3:(ORQ o2:(ORQ o1:(ORQ o0:(ORQ                        x0:(MOVBloadidx1 [i] {s} p idx mem)     s0:(SHLQconst [8]  x1:(MOVBloadidx1 [i-1] {s} p idx mem)))     s1:(SHLQconst [16] x2:(MOVBloadidx1 [i-2] {s} p idx mem)))     s2:(SHLQconst [24] x3:(MOVBloadidx1 [i-3] {s} p idx mem)))     s3:(SHLQconst [32] x4:(MOVBloadidx1 [i-4] {s} p idx mem)))     s4:(SHLQconst [40] x5:(MOVBloadidx1 [i-5] {s} p idx mem)))     s5:(SHLQconst [48] x6:(MOVBloadidx1 [i-6] {s} p id [...]
 	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && x4.Uses == 1   && x5.Uses == 1   && x6.Uses == 1   && x7.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && s3.Uses == 1   && s4.Uses == 1   && s5.Uses == 1   && s6.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && o2.Uses == 1   && o3.Uses == 1   && o4.Uses == 1   && o5.Uses == 1   && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clo [...]
-	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVQloadidx1 <v.Type> [i] {s} p idx mem)
+	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (BSWAPQ <v.Type> (MOVQloadidx1 <v.Type> [i-7] {s} p idx mem))
 	for {
-		o0 := v.Args[0]
-		if o0.Op != OpAMD64ORQ {
+		o5 := v.Args[0]
+		if o5.Op != OpAMD64ORQ {
 			break
 		}
-		o1 := o0.Args[0]
-		if o1.Op != OpAMD64ORQ {
+		o4 := o5.Args[0]
+		if o4.Op != OpAMD64ORQ {
 			break
 		}
-		o2 := o1.Args[0]
-		if o2.Op != OpAMD64ORQ {
+		o3 := o4.Args[0]
+		if o3.Op != OpAMD64ORQ {
 			break
 		}
-		o3 := o2.Args[0]
-		if o3.Op != OpAMD64ORQ {
+		o2 := o3.Args[0]
+		if o2.Op != OpAMD64ORQ {
 			break
 		}
-		o4 := o3.Args[0]
-		if o4.Op != OpAMD64ORQ {
+		o1 := o2.Args[0]
+		if o1.Op != OpAMD64ORQ {
 			break
 		}
-		o5 := o4.Args[0]
-		if o5.Op != OpAMD64ORQ {
+		o0 := o1.Args[0]
+		if o0.Op != OpAMD64ORQ {
 			break
 		}
-		x0 := o5.Args[0]
+		x0 := o0.Args[0]
 		if x0.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
@@ -13470,7 +12165,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		p := x0.Args[0]
 		idx := x0.Args[1]
 		mem := x0.Args[2]
-		s0 := o5.Args[1]
+		s0 := o0.Args[1]
 		if s0.Op != OpAMD64SHLQconst {
 			break
 		}
@@ -13481,7 +12176,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if x1.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x1.AuxInt != i+1 {
+		if x1.AuxInt != i-1 {
 			break
 		}
 		if x1.Aux != s {
@@ -13496,7 +12191,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if mem != x1.Args[2] {
 			break
 		}
-		s1 := o4.Args[1]
+		s1 := o1.Args[1]
 		if s1.Op != OpAMD64SHLQconst {
 			break
 		}
@@ -13507,7 +12202,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if x2.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x2.AuxInt != i+2 {
+		if x2.AuxInt != i-2 {
 			break
 		}
 		if x2.Aux != s {
@@ -13522,7 +12217,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if mem != x2.Args[2] {
 			break
 		}
-		s2 := o3.Args[1]
+		s2 := o2.Args[1]
 		if s2.Op != OpAMD64SHLQconst {
 			break
 		}
@@ -13533,7 +12228,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if x3.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x3.AuxInt != i+3 {
+		if x3.AuxInt != i-3 {
 			break
 		}
 		if x3.Aux != s {
@@ -13548,7 +12243,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if mem != x3.Args[2] {
 			break
 		}
-		s3 := o2.Args[1]
+		s3 := o3.Args[1]
 		if s3.Op != OpAMD64SHLQconst {
 			break
 		}
@@ -13559,7 +12254,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if x4.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x4.AuxInt != i+4 {
+		if x4.AuxInt != i-4 {
 			break
 		}
 		if x4.Aux != s {
@@ -13574,7 +12269,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if mem != x4.Args[2] {
 			break
 		}
-		s4 := o1.Args[1]
+		s4 := o4.Args[1]
 		if s4.Op != OpAMD64SHLQconst {
 			break
 		}
@@ -13585,7 +12280,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if x5.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x5.AuxInt != i+5 {
+		if x5.AuxInt != i-5 {
 			break
 		}
 		if x5.Aux != s {
@@ -13600,7 +12295,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if mem != x5.Args[2] {
 			break
 		}
-		s5 := o0.Args[1]
+		s5 := o5.Args[1]
 		if s5.Op != OpAMD64SHLQconst {
 			break
 		}
@@ -13611,7 +12306,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if x6.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x6.AuxInt != i+6 {
+		if x6.AuxInt != i-6 {
 			break
 		}
 		if x6.Aux != s {
@@ -13637,7 +12332,7 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if x7.Op != OpAMD64MOVBloadidx1 {
 			break
 		}
-		if x7.AuxInt != i+7 {
+		if x7.AuxInt != i-7 {
 			break
 		}
 		if x7.Aux != s {
@@ -13652,2625 +12347,6920 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value, config *Config) bool {
 		if mem != x7.Args[2] {
 			break
 		}
-		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clo [...]
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clo [...]
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
+		v0 := b.NewValue0(v.Line, OpAMD64BSWAPQ, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64MOVQloadidx1, v.Type)
+		v1.AuxInt = i - 7
+		v1.Aux = s
+		v1.AddArg(p)
+		v1.AddArg(idx)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64ORQconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORQconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORQconst [-1] _)
+	// cond:
+	// result: (MOVQconst [-1])
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (ORQconst [c] (MOVQconst [d]))
+	// cond:
+	// result: (MOVQconst [c|d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVQconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = c | d
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64ROLBconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ROLBconst [c] (ROLBconst [d] x))
+	// cond:
+	// result: (ROLBconst [(c+d)& 7] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ROLBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64ROLBconst)
+		v.AuxInt = (c + d) & 7
+		v.AddArg(x)
+		return true
+	}
+	// match: (ROLBconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64ROLLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ROLLconst [c] (ROLLconst [d] x))
+	// cond:
+	// result: (ROLLconst [(c+d)&31] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ROLLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64ROLLconst)
+		v.AuxInt = (c + d) & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (ROLLconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64ROLQconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ROLQconst [c] (ROLQconst [d] x))
+	// cond:
+	// result: (ROLQconst [(c+d)&63] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ROLQconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64ROLQconst)
+		v.AuxInt = (c + d) & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (ROLQconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64ROLWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ROLWconst [c] (ROLWconst [d] x))
+	// cond:
+	// result: (ROLWconst [(c+d)&15] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64ROLWconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64ROLWconst)
+		v.AuxInt = (c + d) & 15
+		v.AddArg(x)
+		return true
+	}
+	// match: (ROLWconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SARB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARB x (MOVQconst [c]))
+	// cond:
+	// result: (SARBconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SARBconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SARB x (MOVLconst [c]))
+	// cond:
+	// result: (SARBconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SARBconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SARBconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARBconst [c] (MOVQconst [d]))
+	// cond:
+	// result: (MOVQconst [d>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVQconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = d >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SARL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARL x (MOVQconst [c]))
+	// cond:
+	// result: (SARLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SARLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SARL x (MOVLconst [c]))
+	// cond:
+	// result: (SARLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SARLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SARL x (ANDLconst [31] y))
+	// cond:
+	// result: (SARL x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ANDLconst {
+			break
+		}
+		if v_1.AuxInt != 31 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpAMD64SARL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SARLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARLconst [c] (MOVQconst [d]))
+	// cond:
+	// result: (MOVQconst [d>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVQconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = d >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SARQ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARQ x (MOVQconst [c]))
+	// cond:
+	// result: (SARQconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SARQconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SARQ x (MOVLconst [c]))
+	// cond:
+	// result: (SARQconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SARQconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SARQ x (ANDQconst [63] y))
+	// cond:
+	// result: (SARQ x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ANDQconst {
+			break
+		}
+		if v_1.AuxInt != 63 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpAMD64SARQ)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SARQconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARQconst [c] (MOVQconst [d]))
+	// cond:
+	// result: (MOVQconst [d>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVQconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = d >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SARW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARW x (MOVQconst [c]))
+	// cond:
+	// result: (SARWconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SARWconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SARW x (MOVLconst [c]))
+	// cond:
+	// result: (SARWconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SARWconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SARWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SARWconst [c] (MOVQconst [d]))
+	// cond:
+	// result: (MOVQconst [d>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVQconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = d >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SBBLcarrymask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBBLcarrymask (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SBBLcarrymask (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (SBBLcarrymask (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SBBLcarrymask (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (SBBLcarrymask (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SBBQcarrymask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBBQcarrymask (FlagEQ))
+	// cond:
+	// result: (MOVQconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SBBQcarrymask (FlagLT_ULT))
+	// cond:
+	// result: (MOVQconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (SBBQcarrymask (FlagLT_UGT))
+	// cond:
+	// result: (MOVQconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SBBQcarrymask (FlagGT_ULT))
+	// cond:
+	// result: (MOVQconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (SBBQcarrymask (FlagGT_UGT))
+	// cond:
+	// result: (MOVQconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SETA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETA (InvertFlags x))
+	// cond:
+	// result: (SETB x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpAMD64SETB)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETA (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETA (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETA (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETA (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETA (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SETAE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETAE (InvertFlags x))
+	// cond:
+	// result: (SETBE x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpAMD64SETBE)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETAE (FlagEQ))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETAE (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETAE (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETAE (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETAE (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SETB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETB (InvertFlags x))
+	// cond:
+	// result: (SETA x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpAMD64SETA)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETB (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETB (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETB (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETB (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETB (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SETBE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETBE (InvertFlags x))
+	// cond:
+	// result: (SETAE x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpAMD64SETAE)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETBE (FlagEQ))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETBE (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETBE (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETBE (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETBE (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SETEQ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETEQ (InvertFlags x))
+	// cond:
+	// result: (SETEQ x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpAMD64SETEQ)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETEQ (FlagEQ))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETEQ (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETEQ (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETEQ (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETEQ (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SETG(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETG (InvertFlags x))
+	// cond:
+	// result: (SETL x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpAMD64SETL)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETG (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETG (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETG (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETG (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETG (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SETGE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETGE (InvertFlags x))
+	// cond:
+	// result: (SETLE x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpAMD64SETLE)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETGE (FlagEQ))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETGE (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETGE (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETGE (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETGE (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SETL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETL (InvertFlags x))
+	// cond:
+	// result: (SETG x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpAMD64SETG)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETL (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETL (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETL (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETL (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETL (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SETLE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETLE (InvertFlags x))
+	// cond:
+	// result: (SETGE x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpAMD64SETGE)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETLE (FlagEQ))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETLE (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETLE (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETLE (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETLE (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SETNE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SETNE (InvertFlags x))
+	// cond:
+	// result: (SETNE x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpAMD64SETNE)
+		v.AddArg(x)
+		return true
+	}
+	// match: (SETNE (FlagEQ))
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagEQ {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SETNE (FlagLT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETNE (FlagLT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagLT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETNE (FlagGT_ULT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_ULT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SETNE (FlagGT_UGT))
+	// cond:
+	// result: (MOVLconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64FlagGT_UGT {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SHLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SHLL x (MOVQconst [c]))
+	// cond:
+	// result: (SHLLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHLLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHLL x (MOVLconst [c]))
+	// cond:
+	// result: (SHLLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHLLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHLL x (ANDLconst [31] y))
+	// cond:
+	// result: (SHLL x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ANDLconst {
+			break
+		}
+		if v_1.AuxInt != 31 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpAMD64SHLL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SHLQ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SHLQ x (MOVQconst [c]))
+	// cond:
+	// result: (SHLQconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHLQconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHLQ x (MOVLconst [c]))
+	// cond:
+	// result: (SHLQconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHLQconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHLQ x (ANDQconst [63] y))
+	// cond:
+	// result: (SHLQ x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ANDQconst {
+			break
+		}
+		if v_1.AuxInt != 63 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpAMD64SHLQ)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SHRB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SHRB x (MOVQconst [c]))
+	// cond:
+	// result: (SHRBconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHRBconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHRB x (MOVLconst [c]))
+	// cond:
+	// result: (SHRBconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHRBconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SHRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SHRL x (MOVQconst [c]))
+	// cond:
+	// result: (SHRLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHRLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHRL x (MOVLconst [c]))
+	// cond:
+	// result: (SHRLconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHRLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHRL x (ANDLconst [31] y))
+	// cond:
+	// result: (SHRL x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ANDLconst {
+			break
+		}
+		if v_1.AuxInt != 31 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpAMD64SHRL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SHRQ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SHRQ x (MOVQconst [c]))
+	// cond:
+	// result: (SHRQconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHRQconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHRQ x (MOVLconst [c]))
+	// cond:
+	// result: (SHRQconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHRQconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHRQ x (ANDQconst [63] y))
+	// cond:
+	// result: (SHRQ x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ANDQconst {
+			break
+		}
+		if v_1.AuxInt != 63 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpAMD64SHRQ)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SHRW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SHRW x (MOVQconst [c]))
+	// cond:
+	// result: (SHRWconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHRWconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SHRW x (MOVLconst [c]))
+	// cond:
+	// result: (SHRWconst [c&31] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SHRWconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SUBL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBL x (MOVLconst [c]))
+	// cond:
+	// result: (SUBLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64SUBLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBL (MOVLconst [c]) x)
+	// cond:
+	// result: (NEGL (SUBLconst <v.Type> x [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpAMD64NEGL)
+		v0 := b.NewValue0(v.Line, OpAMD64SUBLconst, v.Type)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBL x x)
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SUBLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBLconst [c] x)
+	// cond: int32(c) == 0
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBLconst [c] x)
+	// cond:
+	// result: (ADDLconst [int64(int32(-c))] x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpAMD64ADDLconst)
+		v.AuxInt = int64(int32(-c))
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAMD64SUBQ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBQ x (MOVQconst [c]))
+	// cond: is32Bit(c)
+	// result: (SUBQconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpAMD64SUBQconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBQ (MOVQconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (NEGQ (SUBQconst <v.Type> x [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpAMD64NEGQ)
+		v0 := b.NewValue0(v.Line, OpAMD64SUBQconst, v.Type)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBQ x x)
+	// cond:
+	// result: (MOVQconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64SUBQconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBQconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBQconst [c] x)
+	// cond: c != -(1<<31)
+	// result: (ADDQconst [-c] x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(c != -(1 << 31)) {
+			break
+		}
+		v.reset(OpAMD64ADDQconst)
+		v.AuxInt = -c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBQconst (MOVQconst [d]) [c])
+	// cond:
+	// result: (MOVQconst [d-c])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVQconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = d - c
+		return true
+	}
+	// match: (SUBQconst (SUBQconst x [d]) [c])
+	// cond: is32Bit(-c-d)
+	// result: (ADDQconst [-c-d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64SUBQconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(-c - d)) {
+			break
+		}
+		v.reset(OpAMD64ADDQconst)
+		v.AuxInt = -c - d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64XADDLlock(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XADDLlock [off1] {sym} val (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (XADDLlock [off1+off2] {sym} val ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		val := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_1.AuxInt
+		ptr := v_1.Args[0]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64XADDLlock)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(val)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64XADDQlock(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XADDQlock [off1] {sym} val (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (XADDQlock [off1+off2] {sym} val ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		val := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_1.AuxInt
+		ptr := v_1.Args[0]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64XADDQlock)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(val)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64XCHGL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XCHGL [off1] {sym} val (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (XCHGL [off1+off2] {sym} val ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		val := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_1.AuxInt
+		ptr := v_1.Args[0]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64XCHGL)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(val)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (XCHGL [off1] {sym1} val (LEAQ [off2] {sym2} ptr) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && ptr.Op != OpSB
+	// result: (XCHGL [off1+off2] {mergeSym(sym1,sym2)} val ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		val := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr := v_1.Args[0]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64XCHGL)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(val)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64XCHGQ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XCHGQ [off1] {sym} val (ADDQconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (XCHGQ [off1+off2] {sym} val ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		val := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64ADDQconst {
+			break
+		}
+		off2 := v_1.AuxInt
+		ptr := v_1.Args[0]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpAMD64XCHGQ)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(val)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (XCHGQ [off1] {sym1} val (LEAQ [off2] {sym2} ptr) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && ptr.Op != OpSB
+	// result: (XCHGQ [off1+off2] {mergeSym(sym1,sym2)} val ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		val := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64LEAQ {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr := v_1.Args[0]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpAMD64XCHGQ)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(val)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64XORL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORL x (MOVLconst [c]))
+	// cond:
+	// result: (XORLconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpAMD64XORLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORL (MOVLconst [c]) x)
+	// cond:
+	// result: (XORLconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpAMD64XORLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORL x x)
+	// cond:
+	// result: (MOVLconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64XORLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORLconst [c] (XORLconst [d] x))
+	// cond:
+	// result: (XORLconst [c ^ d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64XORLconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64XORLconst)
+		v.AuxInt = c ^ d
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORLconst [c] x)
+	// cond: int32(c)==0
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORLconst [c] (MOVLconst [d]))
+	// cond:
+	// result: (MOVLconst [c^d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVLconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64XORQ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORQ x (MOVQconst [c]))
+	// cond: is32Bit(c)
+	// result: (XORQconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpAMD64XORQconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORQ (MOVQconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (XORQconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVQconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpAMD64XORQconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORQ x x)
+	// cond:
+	// result: (MOVQconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAMD64XORQconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORQconst [c] (XORQconst [d] x))
+	// cond:
+	// result: (XORQconst [c ^ d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64XORQconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpAMD64XORQconst)
+		v.AuxInt = c ^ d
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORQconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORQconst [c] (MOVQconst [d]))
+	// cond:
+	// result: (MOVQconst [c^d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64MOVQconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAdd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add16  x y)
+	// cond:
+	// result: (ADDL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ADDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32  x y)
+	// cond:
+	// result: (ADDL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ADDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAdd32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32F x y)
+	// cond:
+	// result: (ADDSS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ADDSS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAdd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64  x y)
+	// cond:
+	// result: (ADDQ  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ADDQ)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAdd64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64F x y)
+	// cond:
+	// result: (ADDSD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ADDSD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAdd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add8   x y)
+	// cond:
+	// result: (ADDL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ADDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAddPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AddPtr x y)
+	// cond: config.PtrSize == 8
+	// result: (ADDQ x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(config.PtrSize == 8) {
+			break
+		}
+		v.reset(OpAMD64ADDQ)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AddPtr x y)
+	// cond: config.PtrSize == 4
+	// result: (ADDL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(config.PtrSize == 4) {
+			break
+		}
+		v.reset(OpAMD64ADDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAddr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Addr {sym} base)
+	// cond: config.PtrSize == 8
+	// result: (LEAQ {sym} base)
+	for {
+		sym := v.Aux
+		base := v.Args[0]
+		if !(config.PtrSize == 8) {
+			break
+		}
+		v.reset(OpAMD64LEAQ)
+		v.Aux = sym
+		v.AddArg(base)
+		return true
+	}
+	// match: (Addr {sym} base)
+	// cond: config.PtrSize == 4
+	// result: (LEAL {sym} base)
+	for {
+		sym := v.Aux
+		base := v.Args[0]
+		if !(config.PtrSize == 4) {
+			break
+		}
+		v.reset(OpAMD64LEAL)
+		v.Aux = sym
+		v.AddArg(base)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAnd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And16 x y)
+	// cond:
+	// result: (ANDL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAnd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And32 x y)
+	// cond:
+	// result: (ANDL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAnd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And64 x y)
+	// cond:
+	// result: (ANDQ x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDQ)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And8  x y)
+	// cond:
+	// result: (ANDL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAndB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AndB x y)
+	// cond:
+	// result: (ANDL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicAdd32 ptr val mem)
+	// cond:
+	// result: (AddTupleFirst32 (XADDLlock val ptr mem) val)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64AddTupleFirst32)
+		v0 := b.NewValue0(v.Line, OpAMD64XADDLlock, MakeTuple(config.fe.TypeUInt32(), TypeMem))
+		v0.AddArg(val)
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(val)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicAdd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicAdd64 ptr val mem)
+	// cond:
+	// result: (AddTupleFirst64 (XADDQlock val ptr mem) val)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64AddTupleFirst64)
+		v0 := b.NewValue0(v.Line, OpAMD64XADDQlock, MakeTuple(config.fe.TypeUInt64(), TypeMem))
+		v0.AddArg(val)
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(val)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicAnd8 ptr val mem)
+	// cond:
+	// result: (ANDBlock ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64ANDBlock)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicCompareAndSwap32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicCompareAndSwap32 ptr old new_ mem)
+	// cond:
+	// result: (CMPXCHGLlock ptr old new_ mem)
+	for {
+		ptr := v.Args[0]
+		old := v.Args[1]
+		new_ := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64CMPXCHGLlock)
+		v.AddArg(ptr)
+		v.AddArg(old)
+		v.AddArg(new_)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicCompareAndSwap64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicCompareAndSwap64 ptr old new_ mem)
+	// cond:
+	// result: (CMPXCHGQlock ptr old new_ mem)
+	for {
+		ptr := v.Args[0]
+		old := v.Args[1]
+		new_ := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpAMD64CMPXCHGQlock)
+		v.AddArg(ptr)
+		v.AddArg(old)
+		v.AddArg(new_)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicExchange32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicExchange32 ptr val mem)
+	// cond:
+	// result: (XCHGL val ptr mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64XCHGL)
+		v.AddArg(val)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicExchange64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicExchange64 ptr val mem)
+	// cond:
+	// result: (XCHGQ val ptr mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64XCHGQ)
+		v.AddArg(val)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicLoad32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoad32 ptr mem)
+	// cond:
+	// result: (MOVLatomicload ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpAMD64MOVLatomicload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicLoad64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoad64 ptr mem)
+	// cond:
+	// result: (MOVQatomicload ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpAMD64MOVQatomicload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicLoadPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoadPtr ptr mem)
+	// cond: config.PtrSize == 8
+	// result: (MOVQatomicload ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(config.PtrSize == 8) {
+			break
+		}
+		v.reset(OpAMD64MOVQatomicload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (AtomicLoadPtr ptr mem)
+	// cond: config.PtrSize == 4
+	// result: (MOVLatomicload ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(config.PtrSize == 4) {
+			break
+		}
+		v.reset(OpAMD64MOVLatomicload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAtomicOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicOr8 ptr val mem)
+	// cond:
+	// result: (ORBlock ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64ORBlock)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicStore32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStore32 ptr val mem)
+	// cond:
+	// result: (Select1 (XCHGL <MakeTuple(config.Frontend().TypeUInt32(),TypeMem)> val ptr mem))
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64XCHGL, MakeTuple(config.Frontend().TypeUInt32(), TypeMem))
+		v0.AddArg(val)
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicStore64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStore64 ptr val mem)
+	// cond:
+	// result: (Select1 (XCHGQ <MakeTuple(config.Frontend().TypeUInt64(),TypeMem)> val ptr mem))
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64XCHGQ, MakeTuple(config.Frontend().TypeUInt64(), TypeMem))
+		v0.AddArg(val)
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpAtomicStorePtrNoWB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStorePtrNoWB ptr val mem)
+	// cond: config.PtrSize == 8
+	// result: (Select1 (XCHGQ <MakeTuple(config.Frontend().TypeBytePtr(),TypeMem)> val ptr mem))
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(config.PtrSize == 8) {
+			break
+		}
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64XCHGQ, MakeTuple(config.Frontend().TypeBytePtr(), TypeMem))
+		v0.AddArg(val)
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (AtomicStorePtrNoWB ptr val mem)
+	// cond: config.PtrSize == 4
+	// result: (Select1 (XCHGL <MakeTuple(config.Frontend().TypeBytePtr(),TypeMem)> val ptr mem))
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(config.PtrSize == 4) {
+			break
+		}
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64XCHGL, MakeTuple(config.Frontend().TypeBytePtr(), TypeMem))
+		v0.AddArg(val)
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpAvg64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Avg64u x y)
+	// cond:
+	// result: (AVGQU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64AVGQU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpBswap32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Bswap32 x)
+	// cond:
+	// result: (BSWAPL x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64BSWAPL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpBswap64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Bswap64 x)
+	// cond:
+	// result: (BSWAPQ x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64BSWAPQ)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpClosureCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ClosureCall [argwid] entry closure mem)
+	// cond:
+	// result: (CALLclosure [argwid] entry closure mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		closure := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpAMD64CALLclosure)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(closure)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCom16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com16 x)
+	// cond:
+	// result: (NOTL x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64NOTL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCom32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com32 x)
+	// cond:
+	// result: (NOTL x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64NOTL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCom64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com64 x)
+	// cond:
+	// result: (NOTQ x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64NOTQ)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCom8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com8  x)
+	// cond:
+	// result: (NOTL x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64NOTL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpConst16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const16  [val])
+	// cond:
+	// result: (MOVLconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueAMD64_OpConst32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32  [val])
+	// cond:
+	// result: (MOVLconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueAMD64_OpConst32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32F [val])
+	// cond:
+	// result: (MOVSSconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpAMD64MOVSSconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueAMD64_OpConst64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64  [val])
+	// cond:
+	// result: (MOVQconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueAMD64_OpConst64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64F [val])
+	// cond:
+	// result: (MOVSDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpAMD64MOVSDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueAMD64_OpConst8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const8   [val])
+	// cond:
+	// result: (MOVLconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueAMD64_OpConstBool(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstBool [b])
+	// cond:
+	// result: (MOVLconst [b])
+	for {
+		b := v.AuxInt
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = b
+		return true
+	}
+}
+func rewriteValueAMD64_OpConstNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstNil)
+	// cond: config.PtrSize == 8
+	// result: (MOVQconst [0])
+	for {
+		if !(config.PtrSize == 8) {
+			break
+		}
+		v.reset(OpAMD64MOVQconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (ConstNil)
+	// cond: config.PtrSize == 4
+	// result: (MOVLconst [0])
+	for {
+		if !(config.PtrSize == 4) {
+			break
+		}
+		v.reset(OpAMD64MOVLconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpConvert(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Convert <t> x mem)
+	// cond: config.PtrSize == 8
+	// result: (MOVQconvert <t> x mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		mem := v.Args[1]
+		if !(config.PtrSize == 8) {
+			break
+		}
+		v.reset(OpAMD64MOVQconvert)
+		v.Type = t
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Convert <t> x mem)
+	// cond: config.PtrSize == 4
+	// result: (MOVLconvert <t> x mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		mem := v.Args[1]
+		if !(config.PtrSize == 4) {
+			break
+		}
+		v.reset(OpAMD64MOVLconvert)
+		v.Type = t
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpCtz32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Ctz32 <t> x)
+	// cond:
+	// result: (CMOVLEQ (Select0 <t> (BSFL x)) (MOVLconst <t> [32]) (Select1 <TypeFlags> (BSFL x)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpAMD64CMOVLEQ)
+		v0 := b.NewValue0(v.Line, OpSelect0, t)
+		v1 := b.NewValue0(v.Line, OpAMD64BSFL, MakeTuple(config.fe.TypeUInt32(), TypeFlags))
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpAMD64MOVLconst, t)
+		v2.AuxInt = 32
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSelect1, TypeFlags)
+		v4 := b.NewValue0(v.Line, OpAMD64BSFL, MakeTuple(config.fe.TypeUInt32(), TypeFlags))
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCtz64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Ctz64 <t> x)
+	// cond:
+	// result: (CMOVQEQ (Select0 <t> (BSFQ x)) (MOVQconst <t> [64]) (Select1 <TypeFlags> (BSFQ x)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpAMD64CMOVQEQ)
+		v0 := b.NewValue0(v.Line, OpSelect0, t)
+		v1 := b.NewValue0(v.Line, OpAMD64BSFQ, MakeTuple(config.fe.TypeUInt64(), TypeFlags))
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpAMD64MOVQconst, t)
+		v2.AuxInt = 64
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSelect1, TypeFlags)
+		v4 := b.NewValue0(v.Line, OpAMD64BSFQ, MakeTuple(config.fe.TypeUInt64(), TypeFlags))
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCvt32Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto32 x)
+	// cond:
+	// result: (CVTTSS2SL x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64CVTTSS2SL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCvt32Fto64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64 x)
+	// cond:
+	// result: (CVTTSS2SQ x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64CVTTSS2SQ)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCvt32Fto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64F x)
+	// cond:
+	// result: (CVTSS2SD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64CVTSS2SD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCvt32to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to32F x)
+	// cond:
+	// result: (CVTSL2SS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64CVTSL2SS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCvt32to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to64F x)
+	// cond:
+	// result: (CVTSL2SD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64CVTSL2SD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCvt64Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32 x)
+	// cond:
+	// result: (CVTTSD2SL x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64CVTTSD2SL)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCvt64Fto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32F x)
+	// cond:
+	// result: (CVTSD2SS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64CVTSD2SS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCvt64Fto64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto64 x)
+	// cond:
+	// result: (CVTTSD2SQ x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64CVTTSD2SQ)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCvt64to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64to32F x)
+	// cond:
+	// result: (CVTSQ2SS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64CVTSQ2SS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpCvt64to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64to64F x)
+	// cond:
+	// result: (CVTSQ2SD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64CVTSQ2SD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDeferCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (DeferCall [argwid] mem)
+	// cond:
+	// result: (CALLdefer [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpAMD64CALLdefer)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv128u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div128u xhi xlo y)
+	// cond:
+	// result: (DIVQU2 xhi xlo y)
+	for {
+		xhi := v.Args[0]
+		xlo := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpAMD64DIVQU2)
+		v.AddArg(xhi)
+		v.AddArg(xlo)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16  x y)
+	// cond:
+	// result: (Select0 (DIVW  x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVW, MakeTuple(config.fe.TypeInt16(), config.fe.TypeInt16()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16u x y)
+	// cond:
+	// result: (Select0 (DIVWU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVWU, MakeTuple(config.fe.TypeUInt16(), config.fe.TypeUInt16()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32  x y)
+	// cond:
+	// result: (Select0 (DIVL  x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVL, MakeTuple(config.fe.TypeInt32(), config.fe.TypeInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32F x y)
+	// cond:
+	// result: (DIVSS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64DIVSS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32u x y)
+	// cond:
+	// result: (Select0 (DIVLU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVLU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64  x y)
+	// cond:
+	// result: (Select0 (DIVQ  x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVQ, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64F x y)
+	// cond:
+	// result: (DIVSD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64DIVSD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64u x y)
+	// cond:
+	// result: (Select0 (DIVQU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVQU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8   x y)
+	// cond:
+	// result: (Select0 (DIVW  (SignExt8to16 x) (SignExt8to16 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVW, MakeTuple(config.fe.TypeInt16(), config.fe.TypeInt16()))
+		v1 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpDiv8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8u  x y)
+	// cond:
+	// result: (Select0 (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVWU, MakeTuple(config.fe.TypeUInt16(), config.fe.TypeUInt16()))
+		v1 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpEq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq16  x y)
+	// cond:
+	// result: (SETEQ (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETEQ)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpEq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32  x y)
+	// cond:
+	// result: (SETEQ (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETEQ)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpEq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32F x y)
+	// cond:
+	// result: (SETEQF (UCOMISS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETEQF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpEq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64  x y)
+	// cond:
+	// result: (SETEQ (CMPQ x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETEQ)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpEq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64F x y)
+	// cond:
+	// result: (SETEQF (UCOMISD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETEQF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpEq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq8   x y)
+	// cond:
+	// result: (SETEQ (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETEQ)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpEqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqB   x y)
+	// cond:
+	// result: (SETEQ (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETEQ)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpEqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqPtr x y)
+	// cond: config.PtrSize == 8
+	// result: (SETEQ (CMPQ x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(config.PtrSize == 8) {
+			break
+		}
+		v.reset(OpAMD64SETEQ)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (EqPtr x y)
+	// cond: config.PtrSize == 4
+	// result: (SETEQ (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(config.PtrSize == 4) {
+			break
+		}
+		v.reset(OpAMD64SETEQ)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueAMD64_OpGeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16  x y)
+	// cond:
+	// result: (SETGE (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETGE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16U x y)
+	// cond:
+	// result: (SETAE (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETAE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32  x y)
+	// cond:
+	// result: (SETGE (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETGE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32F x y)
+	// cond:
+	// result: (SETGEF (UCOMISS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETGEF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32U x y)
+	// cond:
+	// result: (SETAE (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETAE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64  x y)
+	// cond:
+	// result: (SETGE (CMPQ x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETGE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64F x y)
+	// cond:
+	// result: (SETGEF (UCOMISD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETGEF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64U x y)
+	// cond:
+	// result: (SETAE (CMPQ x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETAE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8   x y)
+	// cond:
+	// result: (SETGE (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETGE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8U  x y)
+	// cond:
+	// result: (SETAE (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETAE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGetClosurePtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetClosurePtr)
+	// cond:
+	// result: (LoweredGetClosurePtr)
+	for {
+		v.reset(OpAMD64LoweredGetClosurePtr)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGetG(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetG mem)
+	// cond:
+	// result: (LoweredGetG mem)
+	for {
+		mem := v.Args[0]
+		v.reset(OpAMD64LoweredGetG)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGoCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GoCall [argwid] mem)
+	// cond:
+	// result: (CALLgo [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpAMD64CALLgo)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGreater16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16  x y)
+	// cond:
+	// result: (SETG (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETG)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGreater16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16U x y)
+	// cond:
+	// result: (SETA (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETA)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGreater32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32  x y)
+	// cond:
+	// result: (SETG (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETG)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGreater32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32F x y)
+	// cond:
+	// result: (SETGF (UCOMISS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETGF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGreater32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32U x y)
+	// cond:
+	// result: (SETA (CMPL x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETA)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGreater64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64  x y)
+	// cond:
+	// result: (SETG (CMPQ x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETG)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGreater64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64F x y)
+	// cond:
+	// result: (SETGF (UCOMISD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETGF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGreater64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64U x y)
+	// cond:
+	// result: (SETA (CMPQ x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETA)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGreater8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8   x y)
+	// cond:
+	// result: (SETG (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETG)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpGreater8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8U  x y)
+	// cond:
+	// result: (SETA (CMPB x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETA)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpHmul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16  x y)
+	// cond:
+	// result: (HMULW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64HMULW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpHmul16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16u x y)
+	// cond:
+	// result: (HMULWU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64HMULWU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpHmul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32  x y)
+	// cond:
+	// result: (HMULL  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64HMULL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpHmul32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32u x y)
+	// cond:
+	// result: (HMULLU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64HMULLU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpHmul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul64  x y)
+	// cond:
+	// result: (HMULQ  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64HMULQ)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpHmul64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul64u x y)
+	// cond:
+	// result: (HMULQU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64HMULQU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpHmul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8   x y)
+	// cond:
+	// result: (HMULB  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64HMULB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpHmul8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8u  x y)
+	// cond:
+	// result: (HMULBU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64HMULBU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueAMD64_OpInt64Hi(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Int64Hi x)
+	// cond:
+	// result: (SHRQconst [32] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpAMD64SHRQconst)
+		v.AuxInt = 32
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpInterCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (InterCall [argwid] entry mem)
+	// cond:
+	// result: (CALLinter [argwid] entry mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpAMD64CALLinter)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueAMD64_OpIsInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsInBounds idx len)
+	// cond:
+	// result: (SETB (CMPQ idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpAMD64SETB)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueAMD64_OpIsNonNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsNonNil p)
+	// cond: config.PtrSize == 8
+	// result: (SETNE (TESTQ p p))
+	for {
+		p := v.Args[0]
+		if !(config.PtrSize == 8) {
+			break
+		}
+		v.reset(OpAMD64SETNE)
+		v0 := b.NewValue0(v.Line, OpAMD64TESTQ, TypeFlags)
+		v0.AddArg(p)
+		v0.AddArg(p)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (IsNonNil p)
+	// cond: config.PtrSize == 4
+	// result: (SETNE (TESTL p p))
+	for {
+		p := v.Args[0]
+		if !(config.PtrSize == 4) {
 			break
 		}
-		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVQloadidx1, v.Type)
-		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AuxInt = i
-		v0.Aux = s
+		v.reset(OpAMD64SETNE)
+		v0 := b.NewValue0(v.Line, OpAMD64TESTL, TypeFlags)
 		v0.AddArg(p)
-		v0.AddArg(idx)
-		v0.AddArg(mem)
+		v0.AddArg(p)
+		v.AddArg(v0)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64ORQconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpIsSliceInBounds(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (ORQconst [0] x)
+	// match: (IsSliceInBounds idx len)
 	// cond:
-	// result: x
+	// result: (SETBE (CMPQ idx len))
 	for {
-		if v.AuxInt != 0 {
-			break
-		}
-		x := v.Args[0]
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpAMD64SETBE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (ORQconst [-1] _)
+}
+func rewriteValueAMD64_OpLeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16  x y)
 	// cond:
-	// result: (MOVQconst [-1])
+	// result: (SETLE (CMPW x y))
 	for {
-		if v.AuxInt != -1 {
-			break
-		}
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = -1
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETLE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (ORQconst [c] (MOVQconst [d]))
+}
+func rewriteValueAMD64_OpLeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16U x y)
 	// cond:
-	// result: (MOVQconst [c|d])
+	// result: (SETBE (CMPW x y))
 	for {
-		c := v.AuxInt
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
-			break
-		}
-		d := v_0.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = c | d
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETBE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpOffPtr(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLeq32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (OffPtr [off] ptr)
-	// cond: is32Bit(off)
-	// result: (ADDQconst [off] ptr)
+	// match: (Leq32  x y)
+	// cond:
+	// result: (SETLE (CMPL x y))
 	for {
-		off := v.AuxInt
-		ptr := v.Args[0]
-		if !(is32Bit(off)) {
-			break
-		}
-		v.reset(OpAMD64ADDQconst)
-		v.AuxInt = off
-		v.AddArg(ptr)
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETLE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (OffPtr [off] ptr)
+}
+func rewriteValueAMD64_OpLeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32F x y)
 	// cond:
-	// result: (ADDQ (MOVQconst [off]) ptr)
+	// result: (SETGEF (UCOMISS y x))
 	for {
-		off := v.AuxInt
-		ptr := v.Args[0]
-		v.reset(OpAMD64ADDQ)
-		v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
-		v0.AuxInt = off
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETGEF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
 		v.AddArg(v0)
-		v.AddArg(ptr)
 		return true
 	}
 }
-func rewriteValueAMD64_OpOr16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLeq32U(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Or16 x y)
+	// match: (Leq32U x y)
 	// cond:
-	// result: (ORL x y)
+	// result: (SETBE (CMPL x y))
 	for {
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ORL)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.reset(OpAMD64SETBE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpOr32(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLeq64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Or32 x y)
+	// match: (Leq64  x y)
 	// cond:
-	// result: (ORL x y)
+	// result: (SETLE (CMPQ x y))
 	for {
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ORL)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.reset(OpAMD64SETLE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpOr64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLeq64F(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Or64 x y)
+	// match: (Leq64F x y)
 	// cond:
-	// result: (ORQ x y)
+	// result: (SETGEF (UCOMISD y x))
 	for {
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ORQ)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.reset(OpAMD64SETGEF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpOr8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLeq64U(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Or8  x y)
+	// match: (Leq64U x y)
 	// cond:
-	// result: (ORL x y)
+	// result: (SETBE (CMPQ x y))
 	for {
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ORL)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.reset(OpAMD64SETBE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpOrB(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLeq8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (OrB x y)
+	// match: (Leq8   x y)
 	// cond:
-	// result: (ORL x y)
+	// result: (SETLE (CMPB x y))
 	for {
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ORL)
-		v.AddArg(x)
-		v.AddArg(y)
+		v.reset(OpAMD64SETLE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh16Ux16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLeq8U(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh16Ux16 <t> x y)
+	// match: (Leq8U  x y)
 	// cond:
-	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPWconst y [16])))
+	// result: (SETBE (CMPB x y))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
+		v.reset(OpAMD64SETBE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 16
-		v1.AddArg(v2)
-		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh16Ux32(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLess16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh16Ux32 <t> x y)
+	// match: (Less16  x y)
 	// cond:
-	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPLconst y [16])))
+	// result: (SETL (CMPW x y))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
+		v.reset(OpAMD64SETL)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 16
-		v1.AddArg(v2)
-		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh16Ux64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLess16U(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh16Ux64 <t> x y)
+	// match: (Less16U x y)
 	// cond:
-	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPQconst y [16])))
+	// result: (SETB (CMPW x y))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
+		v.reset(OpAMD64SETB)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 16
-		v1.AddArg(v2)
-		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh16Ux8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLess32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh16Ux8  <t> x y)
+	// match: (Less32  x y)
 	// cond:
-	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPBconst y [16])))
+	// result: (SETL (CMPL x y))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
+		v.reset(OpAMD64SETL)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 16
-		v1.AddArg(v2)
-		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh16x16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLess32F(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh16x16 <t> x y)
+	// match: (Less32F x y)
 	// cond:
-	// result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [16])))))
+	// result: (SETGF (UCOMISS y x))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64SARW)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v.reset(OpAMD64SETGF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
 		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 16
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
+		v0.AddArg(x)
 		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh16x32(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLess32U(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh16x32 <t> x y)
+	// match: (Less32U x y)
 	// cond:
-	// result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [16])))))
+	// result: (SETB (CMPL x y))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64SARW)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v.reset(OpAMD64SETB)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
 		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 16
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
 		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh16x64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLess64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh16x64 <t> x y)
+	// match: (Less64  x y)
 	// cond:
-	// result: (SARW <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [16])))))
+	// result: (SETL (CMPQ x y))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64SARW)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORQ, y.Type)
+		v.reset(OpAMD64SETL)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
 		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTQ, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 16
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
 		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh16x8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLess64F(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh16x8  <t> x y)
+	// match: (Less64F x y)
 	// cond:
-	// result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [16])))))
+	// result: (SETGF (UCOMISD y x))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64SARW)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v.reset(OpAMD64SETGF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
 		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 16
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
+		v0.AddArg(x)
 		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh32Ux16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLess64U(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh32Ux16 <t> x y)
+	// match: (Less64U x y)
 	// cond:
-	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
+	// result: (SETB (CMPQ x y))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRL, t)
+		v.reset(OpAMD64SETB)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh32Ux32(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLess8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh32Ux32 <t> x y)
+	// match: (Less8   x y)
 	// cond:
-	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
+	// result: (SETL (CMPB x y))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRL, t)
+		v.reset(OpAMD64SETL)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh32Ux64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLess8U(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh32Ux64 <t> x y)
+	// match: (Less8U  x y)
 	// cond:
-	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
+	// result: (SETB (CMPB x y))
 	for {
-		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRL, t)
+		v.reset(OpAMD64SETB)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh32Ux8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLoad(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh32Ux8  <t> x y)
-	// cond:
-	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
+	// match: (Load <t> ptr mem)
+	// cond: (is64BitInt(t) || isPtr(t) && config.PtrSize == 8)
+	// result: (MOVQload ptr mem)
 	for {
 		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRL, t)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
-		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
-		v2.AddArg(y)
-		v2.AuxInt = 32
-		v1.AddArg(v2)
-		v.AddArg(v1)
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitInt(t) || isPtr(t) && config.PtrSize == 8) {
+			break
+		}
+		v.reset(OpAMD64MOVQload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is32BitInt(t) || isPtr(t) && config.PtrSize == 4)
+	// result: (MOVLload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t) || isPtr(t) && config.PtrSize == 4) {
+			break
+		}
+		v.reset(OpAMD64MOVLload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is16BitInt(t)
+	// result: (MOVWload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t)) {
+			break
+		}
+		v.reset(OpAMD64MOVWload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (t.IsBoolean() || is8BitInt(t))
+	// result: (MOVBload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(t.IsBoolean() || is8BitInt(t)) {
+			break
+		}
+		v.reset(OpAMD64MOVBload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitFloat(t)
+	// result: (MOVSSload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitFloat(t)) {
+			break
+		}
+		v.reset(OpAMD64MOVSSload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitFloat(t)
+	// result: (MOVSDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitFloat(t)) {
+			break
+		}
+		v.reset(OpAMD64MOVSDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
 }
-func rewriteValueAMD64_OpRsh32x16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLrot16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh32x16 <t> x y)
+	// match: (Lrot16 <t> x [c])
 	// cond:
-	// result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [32])))))
+	// result: (ROLWconst <t> [c&15] x)
 	for {
 		t := v.Type
+		c := v.AuxInt
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SARL)
+		v.reset(OpAMD64ROLWconst)
 		v.Type = t
+		v.AuxInt = c & 15
 		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
-		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 32
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
-		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh32x32(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLrot32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh32x32 <t> x y)
+	// match: (Lrot32 <t> x [c])
 	// cond:
-	// result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [32])))))
+	// result: (ROLLconst <t> [c&31] x)
 	for {
 		t := v.Type
+		c := v.AuxInt
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SARL)
+		v.reset(OpAMD64ROLLconst)
 		v.Type = t
+		v.AuxInt = c & 31
 		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
-		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 32
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
-		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh32x64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLrot64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh32x64 <t> x y)
+	// match: (Lrot64 <t> x [c])
 	// cond:
-	// result: (SARL <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [32])))))
+	// result: (ROLQconst <t> [c&63] x)
 	for {
 		t := v.Type
+		c := v.AuxInt
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SARL)
+		v.reset(OpAMD64ROLQconst)
 		v.Type = t
+		v.AuxInt = c & 63
 		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORQ, y.Type)
-		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTQ, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 32
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
-		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh32x8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLrot8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh32x8  <t> x y)
+	// match: (Lrot8  <t> x [c])
 	// cond:
-	// result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [32])))))
+	// result: (ROLBconst <t> [c&7] x)
 	for {
 		t := v.Type
+		c := v.AuxInt
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SARL)
+		v.reset(OpAMD64ROLBconst)
 		v.Type = t
+		v.AuxInt = c & 7
 		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
-		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 32
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
-		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh64Ux16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh16x16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh64Ux16 <t> x y)
+	// match: (Lsh16x16 <t> x y)
 	// cond:
-	// result: (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPWconst y [64])))
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDQ)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRQ, t)
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
 		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v2.AuxInt = 32
 		v2.AddArg(y)
-		v2.AuxInt = 64
 		v1.AddArg(v2)
 		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh64Ux32(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh16x32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh64Ux32 <t> x y)
+	// match: (Lsh16x32 <t> x y)
 	// cond:
-	// result: (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPLconst y [64])))
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDQ)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRQ, t)
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
 		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v2.AuxInt = 32
 		v2.AddArg(y)
-		v2.AuxInt = 64
 		v1.AddArg(v2)
 		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh64Ux64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh16x64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh64Ux64 <t> x y)
+	// match: (Lsh16x64 <t> x y)
 	// cond:
-	// result: (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPQconst y [64])))
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDQ)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRQ, t)
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
 		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v2.AuxInt = 32
 		v2.AddArg(y)
-		v2.AuxInt = 64
 		v1.AddArg(v2)
 		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh64Ux8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh16x8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh64Ux8  <t> x y)
+	// match: (Lsh16x8  <t> x y)
 	// cond:
-	// result: (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPBconst y [64])))
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64ANDQ)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRQ, t)
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
-		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
 		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v2.AuxInt = 32
 		v2.AddArg(y)
-		v2.AuxInt = 64
 		v1.AddArg(v2)
 		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh64x16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Rsh64x16 <t> x y)
-	// cond:
-	// result: (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [64])))))
-	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SARQ)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
-		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 64
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpRsh64x32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Rsh64x32 <t> x y)
-	// cond:
-	// result: (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [64])))))
-	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SARQ)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
-		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 64
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpRsh64x64(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Rsh64x64 <t> x y)
-	// cond:
-	// result: (SARQ <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [64])))))
-	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SARQ)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORQ, y.Type)
-		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTQ, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 64
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpRsh64x8(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Rsh64x8  <t> x y)
-	// cond:
-	// result: (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [64])))))
-	for {
-		t := v.Type
-		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpAMD64SARQ)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
-		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 64
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
-		v.AddArg(v0)
-		return true
-	}
-}
-func rewriteValueAMD64_OpRsh8Ux16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh32x16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh8Ux16 <t> x y)
+	// match: (Lsh32x16 <t> x y)
 	// cond:
-	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPWconst y [8])))
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
 		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
 		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v2.AuxInt = 32
 		v2.AddArg(y)
-		v2.AuxInt = 8
 		v1.AddArg(v2)
 		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh8Ux32(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh32x32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh8Ux32 <t> x y)
+	// match: (Lsh32x32 <t> x y)
 	// cond:
-	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPLconst y [8])))
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
 		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
 		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v2.AuxInt = 32
 		v2.AddArg(y)
-		v2.AuxInt = 8
 		v1.AddArg(v2)
 		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh8Ux64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh32x64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh8Ux64 <t> x y)
+	// match: (Lsh32x64 <t> x y)
 	// cond:
-	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPQconst y [8])))
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
 		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
 		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v2.AuxInt = 32
 		v2.AddArg(y)
-		v2.AuxInt = 8
 		v1.AddArg(v2)
 		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh8Ux8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh32x8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh8Ux8  <t> x y)
+	// match: (Lsh32x8  <t> x y)
 	// cond:
-	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPBconst y [8])))
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
 		v.reset(OpAMD64ANDL)
-		v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
 		v0.AddArg(x)
 		v0.AddArg(y)
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
 		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v2.AuxInt = 32
 		v2.AddArg(y)
-		v2.AuxInt = 8
 		v1.AddArg(v2)
 		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh8x16(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh64x16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh8x16 <t> x y)
+	// match: (Lsh64x16 <t> x y)
 	// cond:
-	// result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [8])))))
+	// result: (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPWconst y [64])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64SARB)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v.reset(OpAMD64ANDQ)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQ, t)
+		v0.AddArg(x)
 		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 8
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
 		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh8x32(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh64x32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh8x32 <t> x y)
+	// match: (Lsh64x32 <t> x y)
 	// cond:
-	// result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [8])))))
+	// result: (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPLconst y [64])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64SARB)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v.reset(OpAMD64ANDQ)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQ, t)
+		v0.AddArg(x)
 		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 8
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
 		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh8x64(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh64x64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh8x64 <t> x y)
+	// match: (Lsh64x64 <t> x y)
 	// cond:
-	// result: (SARB <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [8])))))
+	// result: (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPQconst y [64])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64SARB)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORQ, y.Type)
+		v.reset(OpAMD64ANDQ)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQ, t)
+		v0.AddArg(x)
 		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTQ, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 8
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
 		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpRsh8x8(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh64x8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Rsh8x8  <t> x y)
+	// match: (Lsh64x8  <t> x y)
 	// cond:
-	// result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [8])))))
+	// result: (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPBconst y [64])))
 	for {
 		t := v.Type
 		x := v.Args[0]
 		y := v.Args[1]
-		v.reset(OpAMD64SARB)
-		v.Type = t
-		v.AddArg(x)
-		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v.reset(OpAMD64ANDQ)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLQ, t)
+		v0.AddArg(x)
 		v0.AddArg(y)
-		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
-		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
-		v3 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
-		v3.AddArg(y)
-		v3.AuxInt = 8
-		v2.AddArg(v3)
-		v1.AddArg(v2)
-		v0.AddArg(v1)
 		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
 }
-func rewriteValueAMD64_OpAMD64SARB(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (SARB x (MOVQconst [c]))
-	// cond:
-	// result: (SARBconst [c&31] x)
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SARBconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
-		return true
-	}
-	// match: (SARB x (MOVLconst [c]))
-	// cond:
-	// result: (SARBconst [c&31] x)
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SARBconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64SARBconst(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (SARBconst [c] (MOVQconst [d]))
-	// cond:
-	// result: (MOVQconst [d>>uint64(c)])
-	for {
-		c := v.AuxInt
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
-			break
-		}
-		d := v_0.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = d >> uint64(c)
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64SARL(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh8x16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SARL x (MOVQconst [c]))
-	// cond:
-	// result: (SARLconst [c&31] x)
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SARLconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
-		return true
-	}
-	// match: (SARL x (MOVLconst [c]))
-	// cond:
-	// result: (SARLconst [c&31] x)
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SARLconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
-		return true
-	}
-	// match: (SARL x (ANDLconst [31] y))
+	// match: (Lsh8x16 <t> x y)
 	// cond:
-	// result: (SARL x y)
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ANDLconst {
-			break
-		}
-		if v_1.AuxInt != 31 {
-			break
-		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64SARL)
-		v.AddArg(x)
-		v.AddArg(y)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SARLconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh8x32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SARLconst [c] (MOVQconst [d]))
+	// match: (Lsh8x32 <t> x y)
 	// cond:
-	// result: (MOVQconst [d>>uint64(c)])
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
 	for {
-		c := v.AuxInt
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
-			break
-		}
-		d := v_0.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = d >> uint64(c)
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SARQ(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpLsh8x64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SARQ x (MOVQconst [c]))
+	// match: (Lsh8x64 <t> x y)
 	// cond:
-	// result: (SARQconst [c&63] x)
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SARQconst)
-		v.AuxInt = c & 63
-		v.AddArg(x)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SARQ x (MOVLconst [c]))
+}
+func rewriteValueAMD64_OpLsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x8  <t> x y)
 	// cond:
-	// result: (SARQconst [c&63] x)
+	// result: (ANDL (SHLL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SARQconst)
-		v.AuxInt = c & 63
-		v.AddArg(x)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SARQ x (ANDQconst [63] y))
+}
+func rewriteValueAMD64_OpMod16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16  x y)
 	// cond:
-	// result: (SARQ x y)
+	// result: (Select1 (DIVW  x y))
 	for {
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ANDQconst {
-			break
-		}
-		if v_1.AuxInt != 63 {
-			break
-		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64SARQ)
-		v.AddArg(x)
-		v.AddArg(y)
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVW, MakeTuple(config.fe.TypeInt16(), config.fe.TypeInt16()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SARQconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpMod16u(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SARQconst [c] (MOVQconst [d]))
+	// match: (Mod16u x y)
 	// cond:
-	// result: (MOVQconst [d>>uint64(c)])
+	// result: (Select1 (DIVWU x y))
 	for {
-		c := v.AuxInt
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
-			break
-		}
-		d := v_0.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = d >> uint64(c)
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVWU, MakeTuple(config.fe.TypeUInt16(), config.fe.TypeUInt16()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SARW(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpMod32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SARW x (MOVQconst [c]))
+	// match: (Mod32  x y)
 	// cond:
-	// result: (SARWconst [c&31] x)
+	// result: (Select1 (DIVL  x y))
 	for {
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SARWconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVL, MakeTuple(config.fe.TypeInt32(), config.fe.TypeInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SARW x (MOVLconst [c]))
+}
+func rewriteValueAMD64_OpMod32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32u x y)
 	// cond:
-	// result: (SARWconst [c&31] x)
+	// result: (Select1 (DIVLU x y))
 	for {
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SARWconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVLU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SARWconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpMod64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SARWconst [c] (MOVQconst [d]))
+	// match: (Mod64  x y)
 	// cond:
-	// result: (MOVQconst [d>>uint64(c)])
+	// result: (Select1 (DIVQ  x y))
 	for {
-		c := v.AuxInt
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
-			break
-		}
-		d := v_0.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = d >> uint64(c)
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVQ, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SBBLcarrymask(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpMod64u(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SBBLcarrymask (FlagEQ))
-	// cond:
-	// result: (MOVLconst [0])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
-		return true
-	}
-	// match: (SBBLcarrymask (FlagLT_ULT))
-	// cond:
-	// result: (MOVLconst [-1])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = -1
-		return true
-	}
-	// match: (SBBLcarrymask (FlagLT_UGT))
-	// cond:
-	// result: (MOVLconst [0])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
-		return true
-	}
-	// match: (SBBLcarrymask (FlagGT_ULT))
+	// match: (Mod64u x y)
 	// cond:
-	// result: (MOVLconst [-1])
+	// result: (Select1 (DIVQU x y))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = -1
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVQU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SBBLcarrymask (FlagGT_UGT))
+}
+func rewriteValueAMD64_OpMod8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8   x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (Select1 (DIVW  (SignExt8to16 x) (SignExt8to16 y)))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVW, MakeTuple(config.fe.TypeInt16(), config.fe.TypeInt16()))
+		v1 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to16, config.fe.TypeInt16())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SBBQcarrymask(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpMod8u(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SBBQcarrymask (FlagEQ))
-	// cond:
-	// result: (MOVQconst [0])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
-			break
-		}
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = 0
-		return true
-	}
-	// match: (SBBQcarrymask (FlagLT_ULT))
+	// match: (Mod8u  x y)
 	// cond:
-	// result: (MOVQconst [-1])
+	// result: (Select1 (DIVWU (ZeroExt8to16 x) (ZeroExt8to16 y)))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = -1
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpAMD64DIVWU, MakeTuple(config.fe.TypeUInt16(), config.fe.TypeUInt16()))
+		v1 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to16, config.fe.TypeUInt16())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SBBQcarrymask (FlagLT_UGT))
-	// cond:
-	// result: (MOVQconst [0])
+}
+func rewriteValueAMD64_OpMove(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Move [s] _ _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
+		s := v.AuxInt
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 0) {
 			break
 		}
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = 0
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
 		return true
 	}
-	// match: (SBBQcarrymask (FlagGT_ULT))
-	// cond:
-	// result: (MOVQconst [-1])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore dst (MOVBload src mem) mem)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 1) {
 			break
 		}
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = -1
+		v.reset(OpAMD64MOVBstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, config.fe.TypeUInt8())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (SBBQcarrymask (FlagGT_UGT))
-	// cond:
-	// result: (MOVQconst [0])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVWstore dst (MOVWload src mem) mem)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2) {
 			break
 		}
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = 0
+		v.reset(OpAMD64MOVWstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWload, config.fe.TypeUInt16())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64SETA(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (SETA (InvertFlags x))
-	// cond:
-	// result: (SETB x)
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVLstore dst (MOVLload src mem) mem)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64InvertFlags {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4) {
 			break
 		}
-		x := v_0.Args[0]
-		v.reset(OpAMD64SETB)
-		v.AddArg(x)
+		v.reset(OpAMD64MOVLstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (SETA (FlagEQ))
-	// cond:
-	// result: (MOVLconst [0])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8
+	// result: (MOVQstore dst (MOVQload src mem) mem)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		v.reset(OpAMD64MOVQstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVQload, config.fe.TypeUInt64())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (SETA (FlagLT_ULT))
-	// cond:
-	// result: (MOVLconst [0])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 16
+	// result: (MOVOstore dst (MOVOload src mem) mem)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 16) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		v.reset(OpAMD64MOVOstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVOload, TypeInt128)
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (SETA (FlagLT_UGT))
-	// cond:
-	// result: (MOVLconst [1])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] dst (MOVBload [2] src mem) 		(MOVWstore dst (MOVWload src mem) mem))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 3) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		v.reset(OpAMD64MOVBstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, config.fe.TypeUInt8())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64MOVWstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpAMD64MOVWload, config.fe.TypeUInt16())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SETA (FlagGT_ULT))
-	// cond:
-	// result: (MOVLconst [0])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 5
+	// result: (MOVBstore [4] dst (MOVBload [4] src mem) 		(MOVLstore dst (MOVLload src mem) mem))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 5) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		v.reset(OpAMD64MOVBstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVBload, config.fe.TypeUInt8())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64MOVLstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SETA (FlagGT_UGT))
-	// cond:
-	// result: (MOVLconst [1])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 6
+	// result: (MOVWstore [4] dst (MOVWload [4] src mem) 		(MOVLstore dst (MOVLload src mem) mem))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 6) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		v.reset(OpAMD64MOVWstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVWload, config.fe.TypeUInt16())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64MOVLstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
 		return true
 	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64SETAE(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (SETAE (InvertFlags x))
-	// cond:
-	// result: (SETBE x)
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 7
+	// result: (MOVLstore [3] dst (MOVLload [3] src mem) 		(MOVLstore dst (MOVLload src mem) mem))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64InvertFlags {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 7) {
 			break
 		}
-		x := v_0.Args[0]
-		v.reset(OpAMD64SETBE)
-		v.AddArg(x)
+		v.reset(OpAMD64MOVLstore)
+		v.AuxInt = 3
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
+		v0.AuxInt = 3
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64MOVLstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpAMD64MOVLload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SETAE (FlagEQ))
-	// cond:
-	// result: (MOVLconst [1])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 8 && SizeAndAlign(s).Size() < 16
+	// result: (MOVQstore [SizeAndAlign(s).Size()-8] dst (MOVQload [SizeAndAlign(s).Size()-8] src mem) 		(MOVQstore dst (MOVQload src mem) mem))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 8 && SizeAndAlign(s).Size() < 16) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		v.reset(OpAMD64MOVQstore)
+		v.AuxInt = SizeAndAlign(s).Size() - 8
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVQload, config.fe.TypeUInt64())
+		v0.AuxInt = SizeAndAlign(s).Size() - 8
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64MOVQstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpAMD64MOVQload, config.fe.TypeUInt64())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SETAE (FlagLT_ULT))
-	// cond:
-	// result: (MOVLconst [0])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 16 && SizeAndAlign(s).Size()%16 != 0 && SizeAndAlign(s).Size()%16 <= 8
+	// result: (Move [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%16] 		(OffPtr <dst.Type> dst [SizeAndAlign(s).Size()%16]) 		(OffPtr <src.Type> src [SizeAndAlign(s).Size()%16]) 		(MOVQstore dst (MOVQload src mem) mem))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 16 && SizeAndAlign(s).Size()%16 != 0 && SizeAndAlign(s).Size()%16 <= 8) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		v.reset(OpMove)
+		v.AuxInt = SizeAndAlign(s).Size() - SizeAndAlign(s).Size()%16
+		v0 := b.NewValue0(v.Line, OpOffPtr, dst.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() % 16
+		v0.AddArg(dst)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpOffPtr, src.Type)
+		v1.AuxInt = SizeAndAlign(s).Size() % 16
+		v1.AddArg(src)
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpAMD64MOVQstore, TypeMem)
+		v2.AddArg(dst)
+		v3 := b.NewValue0(v.Line, OpAMD64MOVQload, config.fe.TypeUInt64())
+		v3.AddArg(src)
+		v3.AddArg(mem)
+		v2.AddArg(v3)
+		v2.AddArg(mem)
+		v.AddArg(v2)
 		return true
 	}
-	// match: (SETAE (FlagLT_UGT))
-	// cond:
-	// result: (MOVLconst [1])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 16 && SizeAndAlign(s).Size()%16 != 0 && SizeAndAlign(s).Size()%16 > 8
+	// result: (Move [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%16] 		(OffPtr <dst.Type> dst [SizeAndAlign(s).Size()%16]) 		(OffPtr <src.Type> src [SizeAndAlign(s).Size()%16]) 		(MOVOstore dst (MOVOload src mem) mem))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 16 && SizeAndAlign(s).Size()%16 != 0 && SizeAndAlign(s).Size()%16 > 8) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		v.reset(OpMove)
+		v.AuxInt = SizeAndAlign(s).Size() - SizeAndAlign(s).Size()%16
+		v0 := b.NewValue0(v.Line, OpOffPtr, dst.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() % 16
+		v0.AddArg(dst)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpOffPtr, src.Type)
+		v1.AuxInt = SizeAndAlign(s).Size() % 16
+		v1.AddArg(src)
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpAMD64MOVOstore, TypeMem)
+		v2.AddArg(dst)
+		v3 := b.NewValue0(v.Line, OpAMD64MOVOload, TypeInt128)
+		v3.AddArg(src)
+		v3.AddArg(mem)
+		v2.AddArg(v3)
+		v2.AddArg(mem)
+		v.AddArg(v2)
 		return true
 	}
-	// match: (SETAE (FlagGT_ULT))
-	// cond:
-	// result: (MOVLconst [0])
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() >= 32 && SizeAndAlign(s).Size() <= 16*64 && SizeAndAlign(s).Size()%16 == 0 	&& !config.noDuffDevice
+	// result: (DUFFCOPY [14*(64-SizeAndAlign(s).Size()/16)] dst src mem)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() >= 32 && SizeAndAlign(s).Size() <= 16*64 && SizeAndAlign(s).Size()%16 == 0 && !config.noDuffDevice) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		v.reset(OpAMD64DUFFCOPY)
+		v.AuxInt = 14 * (64 - SizeAndAlign(s).Size()/16)
+		v.AddArg(dst)
+		v.AddArg(src)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (SETAE (FlagGT_UGT))
-	// cond:
-	// result: (MOVLconst [1])
+	// match: (Move [s] dst src mem)
+	// cond: (SizeAndAlign(s).Size() > 16*64 || config.noDuffDevice) && SizeAndAlign(s).Size()%8 == 0
+	// result: (REPMOVSQ dst src (MOVQconst [SizeAndAlign(s).Size()/8]) mem)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !((SizeAndAlign(s).Size() > 16*64 || config.noDuffDevice) && SizeAndAlign(s).Size()%8 == 0) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		v.reset(OpAMD64REPMOVSQ)
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
+		v0.AuxInt = SizeAndAlign(s).Size() / 8
+		v.AddArg(v0)
+		v.AddArg(mem)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64SETB(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpMul16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SETB (InvertFlags x))
+	// match: (Mul16  x y)
 	// cond:
-	// result: (SETA x)
+	// result: (MULL  x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64InvertFlags {
-			break
-		}
-		x := v_0.Args[0]
-		v.reset(OpAMD64SETA)
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64MULL)
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (SETB (FlagEQ))
-	// cond:
-	// result: (MOVLconst [0])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
-		return true
-	}
-	// match: (SETB (FlagLT_ULT))
-	// cond:
-	// result: (MOVLconst [1])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
-		return true
-	}
-	// match: (SETB (FlagLT_UGT))
+}
+func rewriteValueAMD64_OpMul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32  x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (MULL  x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64MULL)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (SETB (FlagGT_ULT))
+}
+func rewriteValueAMD64_OpMul32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32F x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (MULSS x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64MULSS)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (SETB (FlagGT_UGT))
+}
+func rewriteValueAMD64_OpMul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64  x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (MULQ  x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64MULQ)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SETBE(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpMul64F(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SETBE (InvertFlags x))
+	// match: (Mul64F x y)
 	// cond:
-	// result: (SETAE x)
+	// result: (MULSD x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64InvertFlags {
-			break
-		}
-		x := v_0.Args[0]
-		v.reset(OpAMD64SETAE)
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64MULSD)
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (SETBE (FlagEQ))
+}
+func rewriteValueAMD64_OpMul64uhilo(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64uhilo x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (MULQU2 x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64MULQU2)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (SETBE (FlagLT_ULT))
+}
+func rewriteValueAMD64_OpMul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul8   x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (MULL  x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64MULL)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (SETBE (FlagLT_UGT))
+}
+func rewriteValueAMD64_OpNeg16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg16  x)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (NEGL x)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		v.reset(OpAMD64NEGL)
+		v.AddArg(x)
 		return true
 	}
-	// match: (SETBE (FlagGT_ULT))
+}
+func rewriteValueAMD64_OpNeg32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32  x)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (NEGL x)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		x := v.Args[0]
+		v.reset(OpAMD64NEGL)
+		v.AddArg(x)
 		return true
 	}
-	// match: (SETBE (FlagGT_UGT))
+}
+func rewriteValueAMD64_OpNeg32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32F x)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (PXOR x (MOVSSconst <config.Frontend().TypeFloat32()> [f2i(math.Copysign(0, -1))]))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		v.reset(OpAMD64PXOR)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVSSconst, config.Frontend().TypeFloat32())
+		v0.AuxInt = f2i(math.Copysign(0, -1))
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SETEQ(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpNeg64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SETEQ (InvertFlags x))
+	// match: (Neg64  x)
 	// cond:
-	// result: (SETEQ x)
+	// result: (NEGQ x)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64InvertFlags {
-			break
-		}
-		x := v_0.Args[0]
-		v.reset(OpAMD64SETEQ)
+		x := v.Args[0]
+		v.reset(OpAMD64NEGQ)
 		v.AddArg(x)
 		return true
 	}
-	// match: (SETEQ (FlagEQ))
+}
+func rewriteValueAMD64_OpNeg64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64F x)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (PXOR x (MOVSDconst <config.Frontend().TypeFloat64()> [f2i(math.Copysign(0, -1))]))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		x := v.Args[0]
+		v.reset(OpAMD64PXOR)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVSDconst, config.Frontend().TypeFloat64())
+		v0.AuxInt = f2i(math.Copysign(0, -1))
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETEQ (FlagLT_ULT))
+}
+func rewriteValueAMD64_OpNeg8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg8   x)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (NEGL x)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		v.reset(OpAMD64NEGL)
+		v.AddArg(x)
 		return true
 	}
-	// match: (SETEQ (FlagLT_UGT))
+}
+func rewriteValueAMD64_OpNeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq16  x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (SETNE (CMPW x y))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETNE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETEQ (FlagGT_ULT))
+}
+func rewriteValueAMD64_OpNeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32  x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (SETNE (CMPL x y))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETNE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETEQ (FlagGT_UGT))
+}
+func rewriteValueAMD64_OpNeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32F x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (SETNEF (UCOMISS x y))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETNEF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SETG(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpNeq64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SETG (InvertFlags x))
+	// match: (Neq64  x y)
 	// cond:
-	// result: (SETL x)
+	// result: (SETNE (CMPQ x y))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64InvertFlags {
-			break
-		}
-		x := v_0.Args[0]
-		v.reset(OpAMD64SETL)
-		v.AddArg(x)
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETNE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETG (FlagEQ))
+}
+func rewriteValueAMD64_OpNeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64F x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (SETNEF (UCOMISD x y))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETNEF)
+		v0 := b.NewValue0(v.Line, OpAMD64UCOMISD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETG (FlagLT_ULT))
+}
+func rewriteValueAMD64_OpNeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq8   x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (SETNE (CMPB x y))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETNE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETG (FlagLT_UGT))
+}
+func rewriteValueAMD64_OpNeqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqB   x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (SETNE (CMPB x y))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SETNE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPB, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETG (FlagGT_ULT))
-	// cond:
-	// result: (MOVLconst [1])
+}
+func rewriteValueAMD64_OpNeqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqPtr x y)
+	// cond: config.PtrSize == 8
+	// result: (SETNE (CMPQ x y))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(config.PtrSize == 8) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		v.reset(OpAMD64SETNE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPQ, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETG (FlagGT_UGT))
-	// cond:
-	// result: (MOVLconst [1])
+	// match: (NeqPtr x y)
+	// cond: config.PtrSize == 4
+	// result: (SETNE (CMPL x y))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(config.PtrSize == 4) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		v.reset(OpAMD64SETNE)
+		v0 := b.NewValue0(v.Line, OpAMD64CMPL, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64SETGE(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpNilCheck(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SETGE (InvertFlags x))
+	// match: (NilCheck ptr mem)
 	// cond:
-	// result: (SETLE x)
+	// result: (LoweredNilCheck ptr mem)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64InvertFlags {
-			break
-		}
-		x := v_0.Args[0]
-		v.reset(OpAMD64SETLE)
-		v.AddArg(x)
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpAMD64LoweredNilCheck)
+		v.AddArg(ptr)
+		v.AddArg(mem)
 		return true
 	}
-	// match: (SETGE (FlagEQ))
+}
+func rewriteValueAMD64_OpNot(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Not x)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (XORLconst [1] x)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
+		x := v.Args[0]
+		v.reset(OpAMD64XORLconst)
 		v.AuxInt = 1
+		v.AddArg(x)
 		return true
 	}
-	// match: (SETGE (FlagLT_ULT))
-	// cond:
-	// result: (MOVLconst [0])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
-		return true
-	}
-	// match: (SETGE (FlagLT_UGT))
-	// cond:
-	// result: (MOVLconst [0])
+}
+func rewriteValueAMD64_OpOffPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OffPtr [off] ptr)
+	// cond: config.PtrSize == 8 && is32Bit(off)
+	// result: (ADDQconst [off] ptr)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		if !(config.PtrSize == 8 && is32Bit(off)) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		v.reset(OpAMD64ADDQconst)
+		v.AuxInt = off
+		v.AddArg(ptr)
 		return true
 	}
-	// match: (SETGE (FlagGT_ULT))
-	// cond:
-	// result: (MOVLconst [1])
+	// match: (OffPtr [off] ptr)
+	// cond: config.PtrSize == 8
+	// result: (ADDQ (MOVQconst [off]) ptr)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		if !(config.PtrSize == 8) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		v.reset(OpAMD64ADDQ)
+		v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
+		v0.AuxInt = off
+		v.AddArg(v0)
+		v.AddArg(ptr)
 		return true
 	}
-	// match: (SETGE (FlagGT_UGT))
-	// cond:
-	// result: (MOVLconst [1])
+	// match: (OffPtr [off] ptr)
+	// cond: config.PtrSize == 4
+	// result: (ADDLconst [off] ptr)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		if !(config.PtrSize == 4) {
 			break
 		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		v.reset(OpAMD64ADDLconst)
+		v.AuxInt = off
+		v.AddArg(ptr)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64SETL(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpOr16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SETL (InvertFlags x))
+	// match: (Or16 x y)
 	// cond:
-	// result: (SETG x)
+	// result: (ORL x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64InvertFlags {
-			break
-		}
-		x := v_0.Args[0]
-		v.reset(OpAMD64SETG)
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ORL)
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (SETL (FlagEQ))
-	// cond:
-	// result: (MOVLconst [0])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
-		return true
-	}
-	// match: (SETL (FlagLT_ULT))
-	// cond:
-	// result: (MOVLconst [1])
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
-		return true
-	}
-	// match: (SETL (FlagLT_UGT))
+}
+func rewriteValueAMD64_OpOr32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or32 x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (ORL x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ORL)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (SETL (FlagGT_ULT))
+}
+func rewriteValueAMD64_OpOr64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or64 x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (ORQ x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ORQ)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (SETL (FlagGT_UGT))
+}
+func rewriteValueAMD64_OpOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or8  x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (ORL x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ORL)
+		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SETLE(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpOrB(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SETLE (InvertFlags x))
+	// match: (OrB x y)
 	// cond:
-	// result: (SETGE x)
+	// result: (ORL x y)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64InvertFlags {
-			break
-		}
-		x := v_0.Args[0]
-		v.reset(OpAMD64SETGE)
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ORL)
 		v.AddArg(x)
+		v.AddArg(y)
 		return true
 	}
-	// match: (SETLE (FlagEQ))
+}
+func rewriteValueAMD64_OpRsh16Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux16 <t> x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPWconst y [16])))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v2.AuxInt = 16
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SETLE (FlagLT_ULT))
+}
+func rewriteValueAMD64_OpRsh16Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux32 <t> x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPLconst y [16])))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v2.AuxInt = 16
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SETLE (FlagLT_UGT))
+}
+func rewriteValueAMD64_OpRsh16Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux64 <t> x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPQconst y [16])))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v2.AuxInt = 16
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SETLE (FlagGT_ULT))
+}
+func rewriteValueAMD64_OpRsh16Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux8  <t> x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (ANDL (SHRW <t> x y) (SBBLcarrymask <t> (CMPBconst y [16])))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v2.AuxInt = 16
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SETLE (FlagGT_UGT))
+}
+func rewriteValueAMD64_OpRsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x16 <t> x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [16])))))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SARW)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v3.AuxInt = 16
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SETNE(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpRsh16x32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SETNE (InvertFlags x))
+	// match: (Rsh16x32 <t> x y)
 	// cond:
-	// result: (SETNE x)
+	// result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [16])))))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64InvertFlags {
-			break
-		}
-		x := v_0.Args[0]
-		v.reset(OpAMD64SETNE)
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SARW)
+		v.Type = t
 		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v3.AuxInt = 16
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETNE (FlagEQ))
+}
+func rewriteValueAMD64_OpRsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x64 <t> x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (SARW <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [16])))))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagEQ {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SARW)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORQ, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTQ, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v3.AuxInt = 16
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETNE (FlagLT_ULT))
+}
+func rewriteValueAMD64_OpRsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x8  <t> x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (SARW <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [16])))))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64SARW)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v3.AuxInt = 16
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SETNE (FlagLT_UGT))
+}
+func rewriteValueAMD64_OpRsh32Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux16 <t> x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPWconst y [32])))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagLT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SETNE (FlagGT_ULT))
+}
+func rewriteValueAMD64_OpRsh32Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux32 <t> x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPLconst y [32])))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_ULT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SETNE (FlagGT_UGT))
+}
+func rewriteValueAMD64_OpRsh32Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux64 <t> x y)
 	// cond:
-	// result: (MOVLconst [1])
+	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPQconst y [32])))
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64FlagGT_UGT {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 1
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SHLL(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpRsh32Ux8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SHLL x (MOVQconst [c]))
+	// match: (Rsh32Ux8  <t> x y)
 	// cond:
-	// result: (SHLLconst [c&31] x)
+	// result: (ANDL (SHRL <t> x y) (SBBLcarrymask <t> (CMPBconst y [32])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHLLconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SHLL x (MOVLconst [c]))
+}
+func rewriteValueAMD64_OpRsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x16 <t> x y)
 	// cond:
-	// result: (SHLLconst [c&31] x)
+	// result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [32])))))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHLLconst)
-		v.AuxInt = c & 31
+		y := v.Args[1]
+		v.reset(OpAMD64SARL)
+		v.Type = t
 		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SHLL x (ANDLconst [31] y))
+}
+func rewriteValueAMD64_OpRsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x32 <t> x y)
 	// cond:
-	// result: (SHLL x y)
+	// result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [32])))))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ANDLconst {
-			break
-		}
-		if v_1.AuxInt != 31 {
-			break
-		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64SHLL)
+		y := v.Args[1]
+		v.reset(OpAMD64SARL)
+		v.Type = t
 		v.AddArg(x)
-		v.AddArg(y)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SHLQ(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpRsh32x64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SHLQ x (MOVQconst [c]))
+	// match: (Rsh32x64 <t> x y)
 	// cond:
-	// result: (SHLQconst [c&63] x)
+	// result: (SARL <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [32])))))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHLQconst)
-		v.AuxInt = c & 63
+		y := v.Args[1]
+		v.reset(OpAMD64SARL)
+		v.Type = t
 		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORQ, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTQ, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SHLQ x (MOVLconst [c]))
+}
+func rewriteValueAMD64_OpRsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x8  <t> x y)
 	// cond:
-	// result: (SHLQconst [c&63] x)
+	// result: (SARL <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [32])))))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHLQconst)
-		v.AuxInt = c & 63
+		y := v.Args[1]
+		v.reset(OpAMD64SARL)
+		v.Type = t
 		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SHLQ x (ANDQconst [63] y))
+}
+func rewriteValueAMD64_OpRsh64Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux16 <t> x y)
 	// cond:
-	// result: (SHLQ x y)
+	// result: (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPWconst y [64])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ANDQconst {
-			break
-		}
-		if v_1.AuxInt != 63 {
-			break
-		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64SHLQ)
-		v.AddArg(x)
-		v.AddArg(y)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDQ)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRQ, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SHRB(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpRsh64Ux32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SHRB x (MOVQconst [c]))
+	// match: (Rsh64Ux32 <t> x y)
 	// cond:
-	// result: (SHRBconst [c&31] x)
+	// result: (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPLconst y [64])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHRBconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDQ)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRQ, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SHRB x (MOVLconst [c]))
+}
+func rewriteValueAMD64_OpRsh64Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux64 <t> x y)
 	// cond:
-	// result: (SHRBconst [c&31] x)
+	// result: (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPQconst y [64])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHRBconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDQ)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRQ, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SHRL(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpRsh64Ux8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SHRL x (MOVQconst [c]))
+	// match: (Rsh64Ux8  <t> x y)
 	// cond:
-	// result: (SHRLconst [c&31] x)
+	// result: (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPBconst y [64])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHRLconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDQ)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRQ, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SHRL x (MOVLconst [c]))
+}
+func rewriteValueAMD64_OpRsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x16 <t> x y)
 	// cond:
-	// result: (SHRLconst [c&31] x)
+	// result: (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [64])))))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHRLconst)
-		v.AuxInt = c & 31
+		y := v.Args[1]
+		v.reset(OpAMD64SARQ)
+		v.Type = t
 		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v3.AuxInt = 64
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SHRL x (ANDLconst [31] y))
+}
+func rewriteValueAMD64_OpRsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x32 <t> x y)
 	// cond:
-	// result: (SHRL x y)
+	// result: (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [64])))))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ANDLconst {
-			break
-		}
-		if v_1.AuxInt != 31 {
-			break
-		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64SHRL)
+		y := v.Args[1]
+		v.reset(OpAMD64SARQ)
+		v.Type = t
 		v.AddArg(x)
-		v.AddArg(y)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v3.AuxInt = 64
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SHRQ(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpRsh64x64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SHRQ x (MOVQconst [c]))
+	// match: (Rsh64x64 <t> x y)
 	// cond:
-	// result: (SHRQconst [c&63] x)
+	// result: (SARQ <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [64])))))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHRQconst)
-		v.AuxInt = c & 63
+		y := v.Args[1]
+		v.reset(OpAMD64SARQ)
+		v.Type = t
 		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORQ, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTQ, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v3.AuxInt = 64
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SHRQ x (MOVLconst [c]))
+}
+func rewriteValueAMD64_OpRsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x8  <t> x y)
 	// cond:
-	// result: (SHRQconst [c&63] x)
+	// result: (SARQ <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [64])))))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHRQconst)
-		v.AuxInt = c & 63
+		y := v.Args[1]
+		v.reset(OpAMD64SARQ)
+		v.Type = t
 		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v3.AuxInt = 64
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SHRQ x (ANDQconst [63] y))
+}
+func rewriteValueAMD64_OpRsh8Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux16 <t> x y)
 	// cond:
-	// result: (SHRQ x y)
+	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPWconst y [8])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64ANDQconst {
-			break
-		}
-		if v_1.AuxInt != 63 {
-			break
-		}
-		y := v_1.Args[0]
-		v.reset(OpAMD64SHRQ)
-		v.AddArg(x)
-		v.AddArg(y)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v2.AuxInt = 8
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SHRW(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpRsh8Ux32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SHRW x (MOVQconst [c]))
+	// match: (Rsh8Ux32 <t> x y)
 	// cond:
-	// result: (SHRWconst [c&31] x)
+	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPLconst y [8])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHRWconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v2.AuxInt = 8
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SHRW x (MOVLconst [c]))
+}
+func rewriteValueAMD64_OpRsh8Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux64 <t> x y)
 	// cond:
-	// result: (SHRWconst [c&31] x)
+	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPQconst y [8])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SHRWconst)
-		v.AuxInt = c & 31
-		v.AddArg(x)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v2.AuxInt = 8
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SUBL(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpRsh8Ux8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SUBL x (MOVLconst [c]))
+	// match: (Rsh8Ux8  <t> x y)
 	// cond:
-	// result: (SUBLconst x [c])
+	// result: (ANDL (SHRB <t> x y) (SBBLcarrymask <t> (CMPBconst y [8])))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64SUBLconst)
-		v.AddArg(x)
-		v.AuxInt = c
-		return true
-	}
-	// match: (SUBL (MOVLconst [c]) x)
-	// cond:
-	// result: (NEGL (SUBLconst <v.Type> x [c]))
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_0.AuxInt
-		x := v.Args[1]
-		v.reset(OpAMD64NEGL)
-		v0 := b.NewValue0(v.Line, OpAMD64SUBLconst, v.Type)
+		y := v.Args[1]
+		v.reset(OpAMD64ANDL)
+		v0 := b.NewValue0(v.Line, OpAMD64SHRB, t)
 		v0.AddArg(x)
-		v0.AuxInt = c
+		v0.AddArg(y)
 		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v2.AuxInt = 8
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
 		return true
 	}
-	// match: (SUBL x x)
+}
+func rewriteValueAMD64_OpRsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x16 <t> x y)
 	// cond:
-	// result: (MOVLconst [0])
+	// result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPWconst y [8])))))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		if x != v.Args[1] {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
+		y := v.Args[1]
+		v.reset(OpAMD64SARB)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPWconst, TypeFlags)
+		v3.AuxInt = 8
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	return false
 }
-func rewriteValueAMD64_OpAMD64SUBLconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpRsh8x32(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SUBLconst [c] x)
-	// cond: int32(c) == 0
-	// result: x
+	// match: (Rsh8x32 <t> x y)
+	// cond:
+	// result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPLconst y [8])))))
 	for {
-		c := v.AuxInt
+		t := v.Type
 		x := v.Args[0]
-		if !(int32(c) == 0) {
-			break
-		}
-		v.reset(OpCopy)
-		v.Type = x.Type
+		y := v.Args[1]
+		v.reset(OpAMD64SARB)
+		v.Type = t
 		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPLconst, TypeFlags)
+		v3.AuxInt = 8
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SUBLconst [c] x)
+}
+func rewriteValueAMD64_OpRsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x64 <t> x y)
 	// cond:
-	// result: (ADDLconst [int64(int32(-c))] x)
+	// result: (SARB <t> x (ORQ <y.Type> y (NOTQ <y.Type> (SBBQcarrymask <y.Type> (CMPQconst y [8])))))
 	for {
-		c := v.AuxInt
+		t := v.Type
 		x := v.Args[0]
-		v.reset(OpAMD64ADDLconst)
-		v.AuxInt = int64(int32(-c))
+		y := v.Args[1]
+		v.reset(OpAMD64SARB)
+		v.Type = t
 		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpAMD64ORQ, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTQ, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBQcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPQconst, TypeFlags)
+		v3.AuxInt = 8
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
 }
-func rewriteValueAMD64_OpAMD64SUBQ(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpRsh8x8(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SUBQ x (MOVQconst [c]))
-	// cond: is32Bit(c)
-	// result: (SUBQconst x [c])
+	// match: (Rsh8x8  <t> x y)
+	// cond:
+	// result: (SARB <t> x (ORL <y.Type> y (NOTL <y.Type> (SBBLcarrymask <y.Type> (CMPBconst y [8])))))
 	for {
+		t := v.Type
 		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		if !(is32Bit(c)) {
-			break
-		}
-		v.reset(OpAMD64SUBQconst)
+		y := v.Args[1]
+		v.reset(OpAMD64SARB)
+		v.Type = t
 		v.AddArg(x)
-		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpAMD64ORL, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpAMD64NOTL, y.Type)
+		v2 := b.NewValue0(v.Line, OpAMD64SBBLcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpAMD64CMPBconst, TypeFlags)
+		v3.AuxInt = 8
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
-	// match: (SUBQ (MOVQconst [c]) x)
-	// cond: is32Bit(c)
-	// result: (NEGQ (SUBQconst <v.Type> x [c]))
+}
+func rewriteValueAMD64_OpSelect0(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Select0 <t> (AddTupleFirst32 tuple val))
+	// cond:
+	// result: (ADDL val (Select0 <t> tuple))
 	for {
+		t := v.Type
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_0.AuxInt
-		x := v.Args[1]
-		if !(is32Bit(c)) {
+		if v_0.Op != OpAMD64AddTupleFirst32 {
 			break
 		}
-		v.reset(OpAMD64NEGQ)
-		v0 := b.NewValue0(v.Line, OpAMD64SUBQconst, v.Type)
-		v0.AddArg(x)
-		v0.AuxInt = c
+		tuple := v_0.Args[0]
+		val := v_0.Args[1]
+		v.reset(OpAMD64ADDL)
+		v.AddArg(val)
+		v0 := b.NewValue0(v.Line, OpSelect0, t)
+		v0.AddArg(tuple)
 		v.AddArg(v0)
 		return true
 	}
-	// match: (SUBQ x x)
+	// match: (Select0 <t> (AddTupleFirst64 tuple val))
 	// cond:
-	// result: (MOVQconst [0])
+	// result: (ADDQ val (Select0 <t> tuple))
 	for {
-		x := v.Args[0]
-		if x != v.Args[1] {
+		t := v.Type
+		v_0 := v.Args[0]
+		if v_0.Op != OpAMD64AddTupleFirst64 {
 			break
 		}
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = 0
+		tuple := v_0.Args[0]
+		val := v_0.Args[1]
+		v.reset(OpAMD64ADDQ)
+		v.AddArg(val)
+		v0 := b.NewValue0(v.Line, OpSelect0, t)
+		v0.AddArg(tuple)
+		v.AddArg(v0)
 		return true
 	}
 	return false
 }
-func rewriteValueAMD64_OpAMD64SUBQconst(v *Value, config *Config) bool {
+func rewriteValueAMD64_OpSelect1(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SUBQconst [0] x)
-	// cond:
-	// result: x
-	for {
-		if v.AuxInt != 0 {
-			break
-		}
-		x := v.Args[0]
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (SUBQconst [c] x)
-	// cond: c != -(1<<31)
-	// result: (ADDQconst [-c] x)
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(c != -(1 << 31)) {
-			break
-		}
-		v.reset(OpAMD64ADDQconst)
-		v.AuxInt = -c
-		v.AddArg(x)
-		return true
-	}
-	// match: (SUBQconst (MOVQconst [d]) [c])
+	// match: (Select1     (AddTupleFirst32 tuple _  ))
 	// cond:
-	// result: (MOVQconst [d-c])
+	// result: (Select1 tuple)
 	for {
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
+		if v_0.Op != OpAMD64AddTupleFirst32 {
 			break
 		}
-		d := v_0.AuxInt
-		c := v.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = d - c
+		tuple := v_0.Args[0]
+		v.reset(OpSelect1)
+		v.AddArg(tuple)
 		return true
 	}
-	// match: (SUBQconst (SUBQconst x [d]) [c])
-	// cond: is32Bit(-c-d)
-	// result: (ADDQconst [-c-d] x)
+	// match: (Select1     (AddTupleFirst64 tuple _  ))
+	// cond:
+	// result: (Select1 tuple)
 	for {
 		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64SUBQconst {
-			break
-		}
-		x := v_0.Args[0]
-		d := v_0.AuxInt
-		c := v.AuxInt
-		if !(is32Bit(-c - d)) {
+		if v_0.Op != OpAMD64AddTupleFirst64 {
 			break
 		}
-		v.reset(OpAMD64ADDQconst)
-		v.AuxInt = -c - d
-		v.AddArg(x)
+		tuple := v_0.Args[0]
+		v.reset(OpSelect1)
+		v.AddArg(tuple)
 		return true
 	}
 	return false
@@ -16348,8 +19338,29 @@ func rewriteValueAMD64_OpSignExt8to64(v *Value, config *Config) bool {
 	// result: (MOVBQSX x)
 	for {
 		x := v.Args[0]
-		v.reset(OpAMD64MOVBQSX)
-		v.AddArg(x)
+		v.reset(OpAMD64MOVBQSX)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueAMD64_OpSlicemask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Slicemask <t> x)
+	// cond:
+	// result: (XORQconst [-1] (SARQconst <t> (SUBQconst <t> x [1]) [63]))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpAMD64XORQconst)
+		v.AuxInt = -1
+		v0 := b.NewValue0(v.Line, OpAMD64SARQconst, t)
+		v0.AuxInt = 63
+		v1 := b.NewValue0(v.Line, OpAMD64SUBQconst, t)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
 		return true
 	}
 }
@@ -16584,16 +19595,34 @@ func rewriteValueAMD64_OpSubPtr(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
 	// match: (SubPtr x y)
-	// cond:
-	// result: (SUBQ  x y)
+	// cond: config.PtrSize == 8
+	// result: (SUBQ x y)
 	for {
 		x := v.Args[0]
 		y := v.Args[1]
+		if !(config.PtrSize == 8) {
+			break
+		}
 		v.reset(OpAMD64SUBQ)
 		v.AddArg(x)
 		v.AddArg(y)
 		return true
 	}
+	// match: (SubPtr x y)
+	// cond: config.PtrSize == 4
+	// result: (SUBL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(config.PtrSize == 4) {
+			break
+		}
+		v.reset(OpAMD64SUBL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
 }
 func rewriteValueAMD64_OpTrunc16to8(v *Value, config *Config) bool {
 	b := v.Block
@@ -16679,171 +19708,6 @@ func rewriteValueAMD64_OpTrunc64to8(v *Value, config *Config) bool {
 		return true
 	}
 }
-func rewriteValueAMD64_OpAMD64XORL(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (XORL x (MOVLconst [c]))
-	// cond:
-	// result: (XORLconst [c] x)
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_1.AuxInt
-		v.reset(OpAMD64XORLconst)
-		v.AuxInt = c
-		v.AddArg(x)
-		return true
-	}
-	// match: (XORL (MOVLconst [c]) x)
-	// cond:
-	// result: (XORLconst [c] x)
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
-			break
-		}
-		c := v_0.AuxInt
-		x := v.Args[1]
-		v.reset(OpAMD64XORLconst)
-		v.AuxInt = c
-		v.AddArg(x)
-		return true
-	}
-	// match: (XORL x x)
-	// cond:
-	// result: (MOVLconst [0])
-	for {
-		x := v.Args[0]
-		if x != v.Args[1] {
-			break
-		}
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = 0
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64XORLconst(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (XORLconst [c] x)
-	// cond: int32(c)==0
-	// result: x
-	for {
-		c := v.AuxInt
-		x := v.Args[0]
-		if !(int32(c) == 0) {
-			break
-		}
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (XORLconst [c] (MOVLconst [d]))
-	// cond:
-	// result: (MOVLconst [c^d])
-	for {
-		c := v.AuxInt
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVLconst {
-			break
-		}
-		d := v_0.AuxInt
-		v.reset(OpAMD64MOVLconst)
-		v.AuxInt = c ^ d
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64XORQ(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (XORQ x (MOVQconst [c]))
-	// cond: is32Bit(c)
-	// result: (XORQconst [c] x)
-	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_1.AuxInt
-		if !(is32Bit(c)) {
-			break
-		}
-		v.reset(OpAMD64XORQconst)
-		v.AuxInt = c
-		v.AddArg(x)
-		return true
-	}
-	// match: (XORQ (MOVQconst [c]) x)
-	// cond: is32Bit(c)
-	// result: (XORQconst [c] x)
-	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
-			break
-		}
-		c := v_0.AuxInt
-		x := v.Args[1]
-		if !(is32Bit(c)) {
-			break
-		}
-		v.reset(OpAMD64XORQconst)
-		v.AuxInt = c
-		v.AddArg(x)
-		return true
-	}
-	// match: (XORQ x x)
-	// cond:
-	// result: (MOVQconst [0])
-	for {
-		x := v.Args[0]
-		if x != v.Args[1] {
-			break
-		}
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = 0
-		return true
-	}
-	return false
-}
-func rewriteValueAMD64_OpAMD64XORQconst(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (XORQconst [0] x)
-	// cond:
-	// result: x
-	for {
-		if v.AuxInt != 0 {
-			break
-		}
-		x := v.Args[0]
-		v.reset(OpCopy)
-		v.Type = x.Type
-		v.AddArg(x)
-		return true
-	}
-	// match: (XORQconst [c] (MOVQconst [d]))
-	// cond:
-	// result: (MOVQconst [c^d])
-	for {
-		c := v.AuxInt
-		v_0 := v.Args[0]
-		if v_0.Op != OpAMD64MOVQconst {
-			break
-		}
-		d := v_0.AuxInt
-		v.reset(OpAMD64MOVQconst)
-		v.AuxInt = c ^ d
-		return true
-	}
-	return false
-}
 func rewriteValueAMD64_OpXor16(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
@@ -16907,88 +19771,94 @@ func rewriteValueAMD64_OpXor8(v *Value, config *Config) bool {
 func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Zero [0] _ mem)
-	// cond:
+	// match: (Zero [s] _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
 	// result: mem
 	for {
-		if v.AuxInt != 0 {
+		s := v.AuxInt
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 0) {
 			break
 		}
-		mem := v.Args[1]
 		v.reset(OpCopy)
 		v.Type = mem.Type
 		v.AddArg(mem)
 		return true
 	}
-	// match: (Zero [1] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 1
 	// result: (MOVBstoreconst [0] destptr mem)
 	for {
-		if v.AuxInt != 1 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
 		v.reset(OpAMD64MOVBstoreconst)
 		v.AuxInt = 0
 		v.AddArg(destptr)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (Zero [2] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 2
 	// result: (MOVWstoreconst [0] destptr mem)
 	for {
-		if v.AuxInt != 2 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
 		v.reset(OpAMD64MOVWstoreconst)
 		v.AuxInt = 0
 		v.AddArg(destptr)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (Zero [4] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 4
 	// result: (MOVLstoreconst [0] destptr mem)
 	for {
-		if v.AuxInt != 4 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
 		v.reset(OpAMD64MOVLstoreconst)
 		v.AuxInt = 0
 		v.AddArg(destptr)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (Zero [8] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 8
 	// result: (MOVQstoreconst [0] destptr mem)
 	for {
-		if v.AuxInt != 8 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 8) {
+			break
+		}
 		v.reset(OpAMD64MOVQstoreconst)
 		v.AuxInt = 0
 		v.AddArg(destptr)
 		v.AddArg(mem)
 		return true
 	}
-	// match: (Zero [3] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 3
 	// result: (MOVBstoreconst [makeValAndOff(0,2)] destptr 		(MOVWstoreconst [0] destptr mem))
 	for {
-		if v.AuxInt != 3 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
 		v.reset(OpAMD64MOVBstoreconst)
 		v.AuxInt = makeValAndOff(0, 2)
 		v.AddArg(destptr)
@@ -16999,15 +19869,16 @@ func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 		v.AddArg(v0)
 		return true
 	}
-	// match: (Zero [5] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 5
 	// result: (MOVBstoreconst [makeValAndOff(0,4)] destptr 		(MOVLstoreconst [0] destptr mem))
 	for {
-		if v.AuxInt != 5 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 5) {
+			break
+		}
 		v.reset(OpAMD64MOVBstoreconst)
 		v.AuxInt = makeValAndOff(0, 4)
 		v.AddArg(destptr)
@@ -17018,15 +19889,16 @@ func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 		v.AddArg(v0)
 		return true
 	}
-	// match: (Zero [6] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 6
 	// result: (MOVWstoreconst [makeValAndOff(0,4)] destptr 		(MOVLstoreconst [0] destptr mem))
 	for {
-		if v.AuxInt != 6 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 6) {
+			break
+		}
 		v.reset(OpAMD64MOVWstoreconst)
 		v.AuxInt = makeValAndOff(0, 4)
 		v.AddArg(destptr)
@@ -17037,15 +19909,16 @@ func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 		v.AddArg(v0)
 		return true
 	}
-	// match: (Zero [7] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 7
 	// result: (MOVLstoreconst [makeValAndOff(0,3)] destptr 		(MOVLstoreconst [0] destptr mem))
 	for {
-		if v.AuxInt != 7 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 7) {
+			break
+		}
 		v.reset(OpAMD64MOVLstoreconst)
 		v.AuxInt = makeValAndOff(0, 3)
 		v.AddArg(destptr)
@@ -17056,21 +19929,21 @@ func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 		v.AddArg(v0)
 		return true
 	}
-	// match: (Zero [size] destptr mem)
-	// cond: size%8 != 0 && size > 8
-	// result: (Zero [size-size%8] (ADDQconst destptr [size%8]) 		(MOVQstoreconst [0] destptr mem))
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size()%8 != 0 && SizeAndAlign(s).Size() > 8
+	// result: (Zero [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8] (OffPtr <destptr.Type> destptr [SizeAndAlign(s).Size()%8]) 		(MOVQstoreconst [0] destptr mem))
 	for {
-		size := v.AuxInt
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
-		if !(size%8 != 0 && size > 8) {
+		if !(SizeAndAlign(s).Size()%8 != 0 && SizeAndAlign(s).Size() > 8) {
 			break
 		}
 		v.reset(OpZero)
-		v.AuxInt = size - size%8
-		v0 := b.NewValue0(v.Line, OpAMD64ADDQconst, config.fe.TypeUInt64())
+		v.AuxInt = SizeAndAlign(s).Size() - SizeAndAlign(s).Size()%8
+		v0 := b.NewValue0(v.Line, OpOffPtr, destptr.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() % 8
 		v0.AddArg(destptr)
-		v0.AuxInt = size % 8
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpAMD64MOVQstoreconst, TypeMem)
 		v1.AuxInt = 0
@@ -17079,15 +19952,16 @@ func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 		v.AddArg(v1)
 		return true
 	}
-	// match: (Zero [16] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 16
 	// result: (MOVQstoreconst [makeValAndOff(0,8)] destptr 		(MOVQstoreconst [0] destptr mem))
 	for {
-		if v.AuxInt != 16 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 16) {
+			break
+		}
 		v.reset(OpAMD64MOVQstoreconst)
 		v.AuxInt = makeValAndOff(0, 8)
 		v.AddArg(destptr)
@@ -17098,15 +19972,16 @@ func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 		v.AddArg(v0)
 		return true
 	}
-	// match: (Zero [24] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 24
 	// result: (MOVQstoreconst [makeValAndOff(0,16)] destptr 		(MOVQstoreconst [makeValAndOff(0,8)] destptr 			(MOVQstoreconst [0] destptr mem)))
 	for {
-		if v.AuxInt != 24 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 24) {
+			break
+		}
 		v.reset(OpAMD64MOVQstoreconst)
 		v.AuxInt = makeValAndOff(0, 16)
 		v.AddArg(destptr)
@@ -17121,15 +19996,16 @@ func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 		v.AddArg(v0)
 		return true
 	}
-	// match: (Zero [32] destptr mem)
-	// cond:
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 32
 	// result: (MOVQstoreconst [makeValAndOff(0,24)] destptr 		(MOVQstoreconst [makeValAndOff(0,16)] destptr 			(MOVQstoreconst [makeValAndOff(0,8)] destptr 				(MOVQstoreconst [0] destptr mem))))
 	for {
-		if v.AuxInt != 32 {
-			break
-		}
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 32) {
+			break
+		}
 		v.reset(OpAMD64MOVQstoreconst)
 		v.AuxInt = makeValAndOff(0, 24)
 		v.AddArg(destptr)
@@ -17148,19 +20024,19 @@ func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 		v.AddArg(v0)
 		return true
 	}
-	// match: (Zero [size] destptr mem)
-	// cond: size <= 1024 && size%8 == 0 && size%16 != 0 && !config.noDuffDevice
-	// result: (Zero [size-8] (ADDQconst [8] destptr) (MOVQstore destptr (MOVQconst [0]) mem))
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() <= 1024 && SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size()%16 != 0 	&& !config.noDuffDevice
+	// result: (Zero [SizeAndAlign(s).Size()-8] (OffPtr <destptr.Type> [8] destptr) (MOVQstore destptr (MOVQconst [0]) mem))
 	for {
-		size := v.AuxInt
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
-		if !(size <= 1024 && size%8 == 0 && size%16 != 0 && !config.noDuffDevice) {
+		if !(SizeAndAlign(s).Size() <= 1024 && SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size()%16 != 0 && !config.noDuffDevice) {
 			break
 		}
 		v.reset(OpZero)
-		v.AuxInt = size - 8
-		v0 := b.NewValue0(v.Line, OpAMD64ADDQconst, config.fe.TypeUInt64())
+		v.AuxInt = SizeAndAlign(s).Size() - 8
+		v0 := b.NewValue0(v.Line, OpOffPtr, destptr.Type)
 		v0.AuxInt = 8
 		v0.AddArg(destptr)
 		v.AddArg(v0)
@@ -17173,18 +20049,18 @@ func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 		v.AddArg(v1)
 		return true
 	}
-	// match: (Zero [size] destptr mem)
-	// cond: size <= 1024 && size%16 == 0 && !config.noDuffDevice
-	// result: (DUFFZERO [size] destptr (MOVOconst [0]) mem)
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() <= 1024 && SizeAndAlign(s).Size()%16 == 0 && !config.noDuffDevice
+	// result: (DUFFZERO [SizeAndAlign(s).Size()] destptr (MOVOconst [0]) mem)
 	for {
-		size := v.AuxInt
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
-		if !(size <= 1024 && size%16 == 0 && !config.noDuffDevice) {
+		if !(SizeAndAlign(s).Size() <= 1024 && SizeAndAlign(s).Size()%16 == 0 && !config.noDuffDevice) {
 			break
 		}
 		v.reset(OpAMD64DUFFZERO)
-		v.AuxInt = size
+		v.AuxInt = SizeAndAlign(s).Size()
 		v.AddArg(destptr)
 		v0 := b.NewValue0(v.Line, OpAMD64MOVOconst, TypeInt128)
 		v0.AuxInt = 0
@@ -17192,20 +20068,20 @@ func rewriteValueAMD64_OpZero(v *Value, config *Config) bool {
 		v.AddArg(mem)
 		return true
 	}
-	// match: (Zero [size] destptr mem)
-	// cond: (size > 1024 || (config.noDuffDevice && size > 32)) && size%8 == 0
-	// result: (REPSTOSQ destptr (MOVQconst [size/8]) (MOVQconst [0]) mem)
+	// match: (Zero [s] destptr mem)
+	// cond: (SizeAndAlign(s).Size() > 1024 || (config.noDuffDevice && SizeAndAlign(s).Size() > 32)) 	&& SizeAndAlign(s).Size()%8 == 0
+	// result: (REPSTOSQ destptr (MOVQconst [SizeAndAlign(s).Size()/8]) (MOVQconst [0]) mem)
 	for {
-		size := v.AuxInt
+		s := v.AuxInt
 		destptr := v.Args[0]
 		mem := v.Args[1]
-		if !((size > 1024 || (config.noDuffDevice && size > 32)) && size%8 == 0) {
+		if !((SizeAndAlign(s).Size() > 1024 || (config.noDuffDevice && SizeAndAlign(s).Size() > 32)) && SizeAndAlign(s).Size()%8 == 0) {
 			break
 		}
 		v.reset(OpAMD64REPSTOSQ)
 		v.AddArg(destptr)
 		v0 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
-		v0.AuxInt = size / 8
+		v0.AuxInt = SizeAndAlign(s).Size() / 8
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpAMD64MOVQconst, config.fe.TypeUInt64())
 		v1.AuxInt = 0
@@ -17293,7 +20169,7 @@ func rewriteValueAMD64_OpZeroExt8to64(v *Value, config *Config) bool {
 		return true
 	}
 }
-func rewriteBlockAMD64(b *Block) bool {
+func rewriteBlockAMD64(b *Block, config *Config) bool {
 	switch b.Kind {
 	case BlockAMD64EQ:
 		// match: (EQ (InvertFlags cmp) yes no)
@@ -17842,6 +20718,7 @@ func rewriteBlockAMD64(b *Block) bool {
 		// result: (NE (TESTB cond cond) yes no)
 		for {
 			v := b.Control
+			_ = v
 			cond := b.Control
 			yes := b.Succs[0]
 			no := b.Succs[1]
diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go
index b57283e..0f8a77f 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM.go
@@ -8,37 +8,737 @@ import "math"
 var _ = math.MinInt8 // in case not otherwise used
 func rewriteValueARM(v *Value, config *Config) bool {
 	switch v.Op {
+	case OpARMADC:
+		return rewriteValueARM_OpARMADC(v, config)
+	case OpARMADCconst:
+		return rewriteValueARM_OpARMADCconst(v, config)
+	case OpARMADCshiftLL:
+		return rewriteValueARM_OpARMADCshiftLL(v, config)
+	case OpARMADCshiftLLreg:
+		return rewriteValueARM_OpARMADCshiftLLreg(v, config)
+	case OpARMADCshiftRA:
+		return rewriteValueARM_OpARMADCshiftRA(v, config)
+	case OpARMADCshiftRAreg:
+		return rewriteValueARM_OpARMADCshiftRAreg(v, config)
+	case OpARMADCshiftRL:
+		return rewriteValueARM_OpARMADCshiftRL(v, config)
+	case OpARMADCshiftRLreg:
+		return rewriteValueARM_OpARMADCshiftRLreg(v, config)
 	case OpARMADD:
 		return rewriteValueARM_OpARMADD(v, config)
+	case OpARMADDS:
+		return rewriteValueARM_OpARMADDS(v, config)
+	case OpARMADDSshiftLL:
+		return rewriteValueARM_OpARMADDSshiftLL(v, config)
+	case OpARMADDSshiftLLreg:
+		return rewriteValueARM_OpARMADDSshiftLLreg(v, config)
+	case OpARMADDSshiftRA:
+		return rewriteValueARM_OpARMADDSshiftRA(v, config)
+	case OpARMADDSshiftRAreg:
+		return rewriteValueARM_OpARMADDSshiftRAreg(v, config)
+	case OpARMADDSshiftRL:
+		return rewriteValueARM_OpARMADDSshiftRL(v, config)
+	case OpARMADDSshiftRLreg:
+		return rewriteValueARM_OpARMADDSshiftRLreg(v, config)
+	case OpARMADDconst:
+		return rewriteValueARM_OpARMADDconst(v, config)
+	case OpARMADDshiftLL:
+		return rewriteValueARM_OpARMADDshiftLL(v, config)
+	case OpARMADDshiftLLreg:
+		return rewriteValueARM_OpARMADDshiftLLreg(v, config)
+	case OpARMADDshiftRA:
+		return rewriteValueARM_OpARMADDshiftRA(v, config)
+	case OpARMADDshiftRAreg:
+		return rewriteValueARM_OpARMADDshiftRAreg(v, config)
+	case OpARMADDshiftRL:
+		return rewriteValueARM_OpARMADDshiftRL(v, config)
+	case OpARMADDshiftRLreg:
+		return rewriteValueARM_OpARMADDshiftRLreg(v, config)
+	case OpARMAND:
+		return rewriteValueARM_OpARMAND(v, config)
+	case OpARMANDconst:
+		return rewriteValueARM_OpARMANDconst(v, config)
+	case OpARMANDshiftLL:
+		return rewriteValueARM_OpARMANDshiftLL(v, config)
+	case OpARMANDshiftLLreg:
+		return rewriteValueARM_OpARMANDshiftLLreg(v, config)
+	case OpARMANDshiftRA:
+		return rewriteValueARM_OpARMANDshiftRA(v, config)
+	case OpARMANDshiftRAreg:
+		return rewriteValueARM_OpARMANDshiftRAreg(v, config)
+	case OpARMANDshiftRL:
+		return rewriteValueARM_OpARMANDshiftRL(v, config)
+	case OpARMANDshiftRLreg:
+		return rewriteValueARM_OpARMANDshiftRLreg(v, config)
+	case OpARMBIC:
+		return rewriteValueARM_OpARMBIC(v, config)
+	case OpARMBICconst:
+		return rewriteValueARM_OpARMBICconst(v, config)
+	case OpARMBICshiftLL:
+		return rewriteValueARM_OpARMBICshiftLL(v, config)
+	case OpARMBICshiftLLreg:
+		return rewriteValueARM_OpARMBICshiftLLreg(v, config)
+	case OpARMBICshiftRA:
+		return rewriteValueARM_OpARMBICshiftRA(v, config)
+	case OpARMBICshiftRAreg:
+		return rewriteValueARM_OpARMBICshiftRAreg(v, config)
+	case OpARMBICshiftRL:
+		return rewriteValueARM_OpARMBICshiftRL(v, config)
+	case OpARMBICshiftRLreg:
+		return rewriteValueARM_OpARMBICshiftRLreg(v, config)
+	case OpARMCMOVWHSconst:
+		return rewriteValueARM_OpARMCMOVWHSconst(v, config)
+	case OpARMCMOVWLSconst:
+		return rewriteValueARM_OpARMCMOVWLSconst(v, config)
+	case OpARMCMP:
+		return rewriteValueARM_OpARMCMP(v, config)
+	case OpARMCMPD:
+		return rewriteValueARM_OpARMCMPD(v, config)
+	case OpARMCMPF:
+		return rewriteValueARM_OpARMCMPF(v, config)
+	case OpARMCMPconst:
+		return rewriteValueARM_OpARMCMPconst(v, config)
+	case OpARMCMPshiftLL:
+		return rewriteValueARM_OpARMCMPshiftLL(v, config)
+	case OpARMCMPshiftLLreg:
+		return rewriteValueARM_OpARMCMPshiftLLreg(v, config)
+	case OpARMCMPshiftRA:
+		return rewriteValueARM_OpARMCMPshiftRA(v, config)
+	case OpARMCMPshiftRAreg:
+		return rewriteValueARM_OpARMCMPshiftRAreg(v, config)
+	case OpARMCMPshiftRL:
+		return rewriteValueARM_OpARMCMPshiftRL(v, config)
+	case OpARMCMPshiftRLreg:
+		return rewriteValueARM_OpARMCMPshiftRLreg(v, config)
+	case OpARMEqual:
+		return rewriteValueARM_OpARMEqual(v, config)
+	case OpARMGreaterEqual:
+		return rewriteValueARM_OpARMGreaterEqual(v, config)
+	case OpARMGreaterEqualU:
+		return rewriteValueARM_OpARMGreaterEqualU(v, config)
+	case OpARMGreaterThan:
+		return rewriteValueARM_OpARMGreaterThan(v, config)
+	case OpARMGreaterThanU:
+		return rewriteValueARM_OpARMGreaterThanU(v, config)
+	case OpARMLessEqual:
+		return rewriteValueARM_OpARMLessEqual(v, config)
+	case OpARMLessEqualU:
+		return rewriteValueARM_OpARMLessEqualU(v, config)
+	case OpARMLessThan:
+		return rewriteValueARM_OpARMLessThan(v, config)
+	case OpARMLessThanU:
+		return rewriteValueARM_OpARMLessThanU(v, config)
+	case OpARMMOVBUload:
+		return rewriteValueARM_OpARMMOVBUload(v, config)
+	case OpARMMOVBUreg:
+		return rewriteValueARM_OpARMMOVBUreg(v, config)
+	case OpARMMOVBload:
+		return rewriteValueARM_OpARMMOVBload(v, config)
+	case OpARMMOVBreg:
+		return rewriteValueARM_OpARMMOVBreg(v, config)
+	case OpARMMOVBstore:
+		return rewriteValueARM_OpARMMOVBstore(v, config)
+	case OpARMMOVDload:
+		return rewriteValueARM_OpARMMOVDload(v, config)
+	case OpARMMOVDstore:
+		return rewriteValueARM_OpARMMOVDstore(v, config)
+	case OpARMMOVFload:
+		return rewriteValueARM_OpARMMOVFload(v, config)
+	case OpARMMOVFstore:
+		return rewriteValueARM_OpARMMOVFstore(v, config)
+	case OpARMMOVHUload:
+		return rewriteValueARM_OpARMMOVHUload(v, config)
+	case OpARMMOVHUreg:
+		return rewriteValueARM_OpARMMOVHUreg(v, config)
+	case OpARMMOVHload:
+		return rewriteValueARM_OpARMMOVHload(v, config)
+	case OpARMMOVHreg:
+		return rewriteValueARM_OpARMMOVHreg(v, config)
+	case OpARMMOVHstore:
+		return rewriteValueARM_OpARMMOVHstore(v, config)
+	case OpARMMOVWload:
+		return rewriteValueARM_OpARMMOVWload(v, config)
+	case OpARMMOVWloadidx:
+		return rewriteValueARM_OpARMMOVWloadidx(v, config)
+	case OpARMMOVWloadshiftLL:
+		return rewriteValueARM_OpARMMOVWloadshiftLL(v, config)
+	case OpARMMOVWloadshiftRA:
+		return rewriteValueARM_OpARMMOVWloadshiftRA(v, config)
+	case OpARMMOVWloadshiftRL:
+		return rewriteValueARM_OpARMMOVWloadshiftRL(v, config)
+	case OpARMMOVWreg:
+		return rewriteValueARM_OpARMMOVWreg(v, config)
+	case OpARMMOVWstore:
+		return rewriteValueARM_OpARMMOVWstore(v, config)
+	case OpARMMOVWstoreidx:
+		return rewriteValueARM_OpARMMOVWstoreidx(v, config)
+	case OpARMMOVWstoreshiftLL:
+		return rewriteValueARM_OpARMMOVWstoreshiftLL(v, config)
+	case OpARMMOVWstoreshiftRA:
+		return rewriteValueARM_OpARMMOVWstoreshiftRA(v, config)
+	case OpARMMOVWstoreshiftRL:
+		return rewriteValueARM_OpARMMOVWstoreshiftRL(v, config)
+	case OpARMMUL:
+		return rewriteValueARM_OpARMMUL(v, config)
+	case OpARMMULA:
+		return rewriteValueARM_OpARMMULA(v, config)
+	case OpARMMVN:
+		return rewriteValueARM_OpARMMVN(v, config)
+	case OpARMMVNshiftLL:
+		return rewriteValueARM_OpARMMVNshiftLL(v, config)
+	case OpARMMVNshiftLLreg:
+		return rewriteValueARM_OpARMMVNshiftLLreg(v, config)
+	case OpARMMVNshiftRA:
+		return rewriteValueARM_OpARMMVNshiftRA(v, config)
+	case OpARMMVNshiftRAreg:
+		return rewriteValueARM_OpARMMVNshiftRAreg(v, config)
+	case OpARMMVNshiftRL:
+		return rewriteValueARM_OpARMMVNshiftRL(v, config)
+	case OpARMMVNshiftRLreg:
+		return rewriteValueARM_OpARMMVNshiftRLreg(v, config)
+	case OpARMNotEqual:
+		return rewriteValueARM_OpARMNotEqual(v, config)
+	case OpARMOR:
+		return rewriteValueARM_OpARMOR(v, config)
+	case OpARMORconst:
+		return rewriteValueARM_OpARMORconst(v, config)
+	case OpARMORshiftLL:
+		return rewriteValueARM_OpARMORshiftLL(v, config)
+	case OpARMORshiftLLreg:
+		return rewriteValueARM_OpARMORshiftLLreg(v, config)
+	case OpARMORshiftRA:
+		return rewriteValueARM_OpARMORshiftRA(v, config)
+	case OpARMORshiftRAreg:
+		return rewriteValueARM_OpARMORshiftRAreg(v, config)
+	case OpARMORshiftRL:
+		return rewriteValueARM_OpARMORshiftRL(v, config)
+	case OpARMORshiftRLreg:
+		return rewriteValueARM_OpARMORshiftRLreg(v, config)
+	case OpARMRSB:
+		return rewriteValueARM_OpARMRSB(v, config)
+	case OpARMRSBSshiftLL:
+		return rewriteValueARM_OpARMRSBSshiftLL(v, config)
+	case OpARMRSBSshiftLLreg:
+		return rewriteValueARM_OpARMRSBSshiftLLreg(v, config)
+	case OpARMRSBSshiftRA:
+		return rewriteValueARM_OpARMRSBSshiftRA(v, config)
+	case OpARMRSBSshiftRAreg:
+		return rewriteValueARM_OpARMRSBSshiftRAreg(v, config)
+	case OpARMRSBSshiftRL:
+		return rewriteValueARM_OpARMRSBSshiftRL(v, config)
+	case OpARMRSBSshiftRLreg:
+		return rewriteValueARM_OpARMRSBSshiftRLreg(v, config)
+	case OpARMRSBconst:
+		return rewriteValueARM_OpARMRSBconst(v, config)
+	case OpARMRSBshiftLL:
+		return rewriteValueARM_OpARMRSBshiftLL(v, config)
+	case OpARMRSBshiftLLreg:
+		return rewriteValueARM_OpARMRSBshiftLLreg(v, config)
+	case OpARMRSBshiftRA:
+		return rewriteValueARM_OpARMRSBshiftRA(v, config)
+	case OpARMRSBshiftRAreg:
+		return rewriteValueARM_OpARMRSBshiftRAreg(v, config)
+	case OpARMRSBshiftRL:
+		return rewriteValueARM_OpARMRSBshiftRL(v, config)
+	case OpARMRSBshiftRLreg:
+		return rewriteValueARM_OpARMRSBshiftRLreg(v, config)
+	case OpARMRSCconst:
+		return rewriteValueARM_OpARMRSCconst(v, config)
+	case OpARMRSCshiftLL:
+		return rewriteValueARM_OpARMRSCshiftLL(v, config)
+	case OpARMRSCshiftLLreg:
+		return rewriteValueARM_OpARMRSCshiftLLreg(v, config)
+	case OpARMRSCshiftRA:
+		return rewriteValueARM_OpARMRSCshiftRA(v, config)
+	case OpARMRSCshiftRAreg:
+		return rewriteValueARM_OpARMRSCshiftRAreg(v, config)
+	case OpARMRSCshiftRL:
+		return rewriteValueARM_OpARMRSCshiftRL(v, config)
+	case OpARMRSCshiftRLreg:
+		return rewriteValueARM_OpARMRSCshiftRLreg(v, config)
+	case OpARMSBC:
+		return rewriteValueARM_OpARMSBC(v, config)
+	case OpARMSBCconst:
+		return rewriteValueARM_OpARMSBCconst(v, config)
+	case OpARMSBCshiftLL:
+		return rewriteValueARM_OpARMSBCshiftLL(v, config)
+	case OpARMSBCshiftLLreg:
+		return rewriteValueARM_OpARMSBCshiftLLreg(v, config)
+	case OpARMSBCshiftRA:
+		return rewriteValueARM_OpARMSBCshiftRA(v, config)
+	case OpARMSBCshiftRAreg:
+		return rewriteValueARM_OpARMSBCshiftRAreg(v, config)
+	case OpARMSBCshiftRL:
+		return rewriteValueARM_OpARMSBCshiftRL(v, config)
+	case OpARMSBCshiftRLreg:
+		return rewriteValueARM_OpARMSBCshiftRLreg(v, config)
+	case OpARMSLL:
+		return rewriteValueARM_OpARMSLL(v, config)
+	case OpARMSLLconst:
+		return rewriteValueARM_OpARMSLLconst(v, config)
+	case OpARMSRA:
+		return rewriteValueARM_OpARMSRA(v, config)
+	case OpARMSRAcond:
+		return rewriteValueARM_OpARMSRAcond(v, config)
+	case OpARMSRAconst:
+		return rewriteValueARM_OpARMSRAconst(v, config)
+	case OpARMSRL:
+		return rewriteValueARM_OpARMSRL(v, config)
+	case OpARMSRLconst:
+		return rewriteValueARM_OpARMSRLconst(v, config)
+	case OpARMSUB:
+		return rewriteValueARM_OpARMSUB(v, config)
+	case OpARMSUBS:
+		return rewriteValueARM_OpARMSUBS(v, config)
+	case OpARMSUBSshiftLL:
+		return rewriteValueARM_OpARMSUBSshiftLL(v, config)
+	case OpARMSUBSshiftLLreg:
+		return rewriteValueARM_OpARMSUBSshiftLLreg(v, config)
+	case OpARMSUBSshiftRA:
+		return rewriteValueARM_OpARMSUBSshiftRA(v, config)
+	case OpARMSUBSshiftRAreg:
+		return rewriteValueARM_OpARMSUBSshiftRAreg(v, config)
+	case OpARMSUBSshiftRL:
+		return rewriteValueARM_OpARMSUBSshiftRL(v, config)
+	case OpARMSUBSshiftRLreg:
+		return rewriteValueARM_OpARMSUBSshiftRLreg(v, config)
+	case OpARMSUBconst:
+		return rewriteValueARM_OpARMSUBconst(v, config)
+	case OpARMSUBshiftLL:
+		return rewriteValueARM_OpARMSUBshiftLL(v, config)
+	case OpARMSUBshiftLLreg:
+		return rewriteValueARM_OpARMSUBshiftLLreg(v, config)
+	case OpARMSUBshiftRA:
+		return rewriteValueARM_OpARMSUBshiftRA(v, config)
+	case OpARMSUBshiftRAreg:
+		return rewriteValueARM_OpARMSUBshiftRAreg(v, config)
+	case OpARMSUBshiftRL:
+		return rewriteValueARM_OpARMSUBshiftRL(v, config)
+	case OpARMSUBshiftRLreg:
+		return rewriteValueARM_OpARMSUBshiftRLreg(v, config)
+	case OpARMXOR:
+		return rewriteValueARM_OpARMXOR(v, config)
+	case OpARMXORconst:
+		return rewriteValueARM_OpARMXORconst(v, config)
+	case OpARMXORshiftLL:
+		return rewriteValueARM_OpARMXORshiftLL(v, config)
+	case OpARMXORshiftLLreg:
+		return rewriteValueARM_OpARMXORshiftLLreg(v, config)
+	case OpARMXORshiftRA:
+		return rewriteValueARM_OpARMXORshiftRA(v, config)
+	case OpARMXORshiftRAreg:
+		return rewriteValueARM_OpARMXORshiftRAreg(v, config)
+	case OpARMXORshiftRL:
+		return rewriteValueARM_OpARMXORshiftRL(v, config)
+	case OpARMXORshiftRLreg:
+		return rewriteValueARM_OpARMXORshiftRLreg(v, config)
+	case OpARMXORshiftRR:
+		return rewriteValueARM_OpARMXORshiftRR(v, config)
+	case OpAdd16:
+		return rewriteValueARM_OpAdd16(v, config)
 	case OpAdd32:
 		return rewriteValueARM_OpAdd32(v, config)
+	case OpAdd32F:
+		return rewriteValueARM_OpAdd32F(v, config)
+	case OpAdd32carry:
+		return rewriteValueARM_OpAdd32carry(v, config)
+	case OpAdd32withcarry:
+		return rewriteValueARM_OpAdd32withcarry(v, config)
+	case OpAdd64F:
+		return rewriteValueARM_OpAdd64F(v, config)
+	case OpAdd8:
+		return rewriteValueARM_OpAdd8(v, config)
+	case OpAddPtr:
+		return rewriteValueARM_OpAddPtr(v, config)
 	case OpAddr:
 		return rewriteValueARM_OpAddr(v, config)
+	case OpAnd16:
+		return rewriteValueARM_OpAnd16(v, config)
+	case OpAnd32:
+		return rewriteValueARM_OpAnd32(v, config)
+	case OpAnd8:
+		return rewriteValueARM_OpAnd8(v, config)
+	case OpAndB:
+		return rewriteValueARM_OpAndB(v, config)
+	case OpBswap32:
+		return rewriteValueARM_OpBswap32(v, config)
+	case OpClosureCall:
+		return rewriteValueARM_OpClosureCall(v, config)
+	case OpCom16:
+		return rewriteValueARM_OpCom16(v, config)
+	case OpCom32:
+		return rewriteValueARM_OpCom32(v, config)
+	case OpCom8:
+		return rewriteValueARM_OpCom8(v, config)
+	case OpConst16:
+		return rewriteValueARM_OpConst16(v, config)
 	case OpConst32:
 		return rewriteValueARM_OpConst32(v, config)
+	case OpConst32F:
+		return rewriteValueARM_OpConst32F(v, config)
+	case OpConst64F:
+		return rewriteValueARM_OpConst64F(v, config)
+	case OpConst8:
+		return rewriteValueARM_OpConst8(v, config)
+	case OpConstBool:
+		return rewriteValueARM_OpConstBool(v, config)
+	case OpConstNil:
+		return rewriteValueARM_OpConstNil(v, config)
+	case OpConvert:
+		return rewriteValueARM_OpConvert(v, config)
+	case OpCtz32:
+		return rewriteValueARM_OpCtz32(v, config)
+	case OpCvt32Fto32:
+		return rewriteValueARM_OpCvt32Fto32(v, config)
+	case OpCvt32Fto32U:
+		return rewriteValueARM_OpCvt32Fto32U(v, config)
+	case OpCvt32Fto64F:
+		return rewriteValueARM_OpCvt32Fto64F(v, config)
+	case OpCvt32Uto32F:
+		return rewriteValueARM_OpCvt32Uto32F(v, config)
+	case OpCvt32Uto64F:
+		return rewriteValueARM_OpCvt32Uto64F(v, config)
+	case OpCvt32to32F:
+		return rewriteValueARM_OpCvt32to32F(v, config)
+	case OpCvt32to64F:
+		return rewriteValueARM_OpCvt32to64F(v, config)
+	case OpCvt64Fto32:
+		return rewriteValueARM_OpCvt64Fto32(v, config)
+	case OpCvt64Fto32F:
+		return rewriteValueARM_OpCvt64Fto32F(v, config)
+	case OpCvt64Fto32U:
+		return rewriteValueARM_OpCvt64Fto32U(v, config)
+	case OpDeferCall:
+		return rewriteValueARM_OpDeferCall(v, config)
+	case OpDiv16:
+		return rewriteValueARM_OpDiv16(v, config)
+	case OpDiv16u:
+		return rewriteValueARM_OpDiv16u(v, config)
+	case OpDiv32:
+		return rewriteValueARM_OpDiv32(v, config)
+	case OpDiv32F:
+		return rewriteValueARM_OpDiv32F(v, config)
+	case OpDiv32u:
+		return rewriteValueARM_OpDiv32u(v, config)
+	case OpDiv64F:
+		return rewriteValueARM_OpDiv64F(v, config)
+	case OpDiv8:
+		return rewriteValueARM_OpDiv8(v, config)
+	case OpDiv8u:
+		return rewriteValueARM_OpDiv8u(v, config)
+	case OpEq16:
+		return rewriteValueARM_OpEq16(v, config)
+	case OpEq32:
+		return rewriteValueARM_OpEq32(v, config)
+	case OpEq32F:
+		return rewriteValueARM_OpEq32F(v, config)
+	case OpEq64F:
+		return rewriteValueARM_OpEq64F(v, config)
+	case OpEq8:
+		return rewriteValueARM_OpEq8(v, config)
+	case OpEqB:
+		return rewriteValueARM_OpEqB(v, config)
+	case OpEqPtr:
+		return rewriteValueARM_OpEqPtr(v, config)
+	case OpGeq16:
+		return rewriteValueARM_OpGeq16(v, config)
+	case OpGeq16U:
+		return rewriteValueARM_OpGeq16U(v, config)
+	case OpGeq32:
+		return rewriteValueARM_OpGeq32(v, config)
+	case OpGeq32F:
+		return rewriteValueARM_OpGeq32F(v, config)
+	case OpGeq32U:
+		return rewriteValueARM_OpGeq32U(v, config)
+	case OpGeq64F:
+		return rewriteValueARM_OpGeq64F(v, config)
+	case OpGeq8:
+		return rewriteValueARM_OpGeq8(v, config)
+	case OpGeq8U:
+		return rewriteValueARM_OpGeq8U(v, config)
+	case OpGetClosurePtr:
+		return rewriteValueARM_OpGetClosurePtr(v, config)
+	case OpGoCall:
+		return rewriteValueARM_OpGoCall(v, config)
+	case OpGreater16:
+		return rewriteValueARM_OpGreater16(v, config)
+	case OpGreater16U:
+		return rewriteValueARM_OpGreater16U(v, config)
+	case OpGreater32:
+		return rewriteValueARM_OpGreater32(v, config)
+	case OpGreater32F:
+		return rewriteValueARM_OpGreater32F(v, config)
+	case OpGreater32U:
+		return rewriteValueARM_OpGreater32U(v, config)
+	case OpGreater64F:
+		return rewriteValueARM_OpGreater64F(v, config)
+	case OpGreater8:
+		return rewriteValueARM_OpGreater8(v, config)
+	case OpGreater8U:
+		return rewriteValueARM_OpGreater8U(v, config)
+	case OpHmul16:
+		return rewriteValueARM_OpHmul16(v, config)
+	case OpHmul16u:
+		return rewriteValueARM_OpHmul16u(v, config)
+	case OpHmul32:
+		return rewriteValueARM_OpHmul32(v, config)
+	case OpHmul32u:
+		return rewriteValueARM_OpHmul32u(v, config)
+	case OpHmul8:
+		return rewriteValueARM_OpHmul8(v, config)
+	case OpHmul8u:
+		return rewriteValueARM_OpHmul8u(v, config)
+	case OpInterCall:
+		return rewriteValueARM_OpInterCall(v, config)
+	case OpIsInBounds:
+		return rewriteValueARM_OpIsInBounds(v, config)
+	case OpIsNonNil:
+		return rewriteValueARM_OpIsNonNil(v, config)
+	case OpIsSliceInBounds:
+		return rewriteValueARM_OpIsSliceInBounds(v, config)
+	case OpLeq16:
+		return rewriteValueARM_OpLeq16(v, config)
+	case OpLeq16U:
+		return rewriteValueARM_OpLeq16U(v, config)
+	case OpLeq32:
+		return rewriteValueARM_OpLeq32(v, config)
+	case OpLeq32F:
+		return rewriteValueARM_OpLeq32F(v, config)
+	case OpLeq32U:
+		return rewriteValueARM_OpLeq32U(v, config)
+	case OpLeq64F:
+		return rewriteValueARM_OpLeq64F(v, config)
+	case OpLeq8:
+		return rewriteValueARM_OpLeq8(v, config)
+	case OpLeq8U:
+		return rewriteValueARM_OpLeq8U(v, config)
+	case OpLess16:
+		return rewriteValueARM_OpLess16(v, config)
+	case OpLess16U:
+		return rewriteValueARM_OpLess16U(v, config)
 	case OpLess32:
 		return rewriteValueARM_OpLess32(v, config)
+	case OpLess32F:
+		return rewriteValueARM_OpLess32F(v, config)
+	case OpLess32U:
+		return rewriteValueARM_OpLess32U(v, config)
+	case OpLess64F:
+		return rewriteValueARM_OpLess64F(v, config)
+	case OpLess8:
+		return rewriteValueARM_OpLess8(v, config)
+	case OpLess8U:
+		return rewriteValueARM_OpLess8U(v, config)
 	case OpLoad:
 		return rewriteValueARM_OpLoad(v, config)
-	case OpARMMOVWload:
-		return rewriteValueARM_OpARMMOVWload(v, config)
-	case OpARMMOVWstore:
-		return rewriteValueARM_OpARMMOVWstore(v, config)
+	case OpLrot16:
+		return rewriteValueARM_OpLrot16(v, config)
+	case OpLrot32:
+		return rewriteValueARM_OpLrot32(v, config)
+	case OpLrot8:
+		return rewriteValueARM_OpLrot8(v, config)
+	case OpLsh16x16:
+		return rewriteValueARM_OpLsh16x16(v, config)
+	case OpLsh16x32:
+		return rewriteValueARM_OpLsh16x32(v, config)
+	case OpLsh16x64:
+		return rewriteValueARM_OpLsh16x64(v, config)
+	case OpLsh16x8:
+		return rewriteValueARM_OpLsh16x8(v, config)
+	case OpLsh32x16:
+		return rewriteValueARM_OpLsh32x16(v, config)
+	case OpLsh32x32:
+		return rewriteValueARM_OpLsh32x32(v, config)
+	case OpLsh32x64:
+		return rewriteValueARM_OpLsh32x64(v, config)
+	case OpLsh32x8:
+		return rewriteValueARM_OpLsh32x8(v, config)
+	case OpLsh8x16:
+		return rewriteValueARM_OpLsh8x16(v, config)
+	case OpLsh8x32:
+		return rewriteValueARM_OpLsh8x32(v, config)
+	case OpLsh8x64:
+		return rewriteValueARM_OpLsh8x64(v, config)
+	case OpLsh8x8:
+		return rewriteValueARM_OpLsh8x8(v, config)
+	case OpMod16:
+		return rewriteValueARM_OpMod16(v, config)
+	case OpMod16u:
+		return rewriteValueARM_OpMod16u(v, config)
+	case OpMod32:
+		return rewriteValueARM_OpMod32(v, config)
+	case OpMod32u:
+		return rewriteValueARM_OpMod32u(v, config)
+	case OpMod8:
+		return rewriteValueARM_OpMod8(v, config)
+	case OpMod8u:
+		return rewriteValueARM_OpMod8u(v, config)
+	case OpMove:
+		return rewriteValueARM_OpMove(v, config)
+	case OpMul16:
+		return rewriteValueARM_OpMul16(v, config)
+	case OpMul32:
+		return rewriteValueARM_OpMul32(v, config)
+	case OpMul32F:
+		return rewriteValueARM_OpMul32F(v, config)
+	case OpMul32uhilo:
+		return rewriteValueARM_OpMul32uhilo(v, config)
+	case OpMul64F:
+		return rewriteValueARM_OpMul64F(v, config)
+	case OpMul8:
+		return rewriteValueARM_OpMul8(v, config)
+	case OpNeg16:
+		return rewriteValueARM_OpNeg16(v, config)
+	case OpNeg32:
+		return rewriteValueARM_OpNeg32(v, config)
+	case OpNeg32F:
+		return rewriteValueARM_OpNeg32F(v, config)
+	case OpNeg64F:
+		return rewriteValueARM_OpNeg64F(v, config)
+	case OpNeg8:
+		return rewriteValueARM_OpNeg8(v, config)
+	case OpNeq16:
+		return rewriteValueARM_OpNeq16(v, config)
+	case OpNeq32:
+		return rewriteValueARM_OpNeq32(v, config)
+	case OpNeq32F:
+		return rewriteValueARM_OpNeq32F(v, config)
+	case OpNeq64F:
+		return rewriteValueARM_OpNeq64F(v, config)
+	case OpNeq8:
+		return rewriteValueARM_OpNeq8(v, config)
+	case OpNeqB:
+		return rewriteValueARM_OpNeqB(v, config)
+	case OpNeqPtr:
+		return rewriteValueARM_OpNeqPtr(v, config)
+	case OpNilCheck:
+		return rewriteValueARM_OpNilCheck(v, config)
+	case OpNot:
+		return rewriteValueARM_OpNot(v, config)
 	case OpOffPtr:
 		return rewriteValueARM_OpOffPtr(v, config)
+	case OpOr16:
+		return rewriteValueARM_OpOr16(v, config)
+	case OpOr32:
+		return rewriteValueARM_OpOr32(v, config)
+	case OpOr8:
+		return rewriteValueARM_OpOr8(v, config)
+	case OpOrB:
+		return rewriteValueARM_OpOrB(v, config)
+	case OpRsh16Ux16:
+		return rewriteValueARM_OpRsh16Ux16(v, config)
+	case OpRsh16Ux32:
+		return rewriteValueARM_OpRsh16Ux32(v, config)
+	case OpRsh16Ux64:
+		return rewriteValueARM_OpRsh16Ux64(v, config)
+	case OpRsh16Ux8:
+		return rewriteValueARM_OpRsh16Ux8(v, config)
+	case OpRsh16x16:
+		return rewriteValueARM_OpRsh16x16(v, config)
+	case OpRsh16x32:
+		return rewriteValueARM_OpRsh16x32(v, config)
+	case OpRsh16x64:
+		return rewriteValueARM_OpRsh16x64(v, config)
+	case OpRsh16x8:
+		return rewriteValueARM_OpRsh16x8(v, config)
+	case OpRsh32Ux16:
+		return rewriteValueARM_OpRsh32Ux16(v, config)
+	case OpRsh32Ux32:
+		return rewriteValueARM_OpRsh32Ux32(v, config)
+	case OpRsh32Ux64:
+		return rewriteValueARM_OpRsh32Ux64(v, config)
+	case OpRsh32Ux8:
+		return rewriteValueARM_OpRsh32Ux8(v, config)
+	case OpRsh32x16:
+		return rewriteValueARM_OpRsh32x16(v, config)
+	case OpRsh32x32:
+		return rewriteValueARM_OpRsh32x32(v, config)
+	case OpRsh32x64:
+		return rewriteValueARM_OpRsh32x64(v, config)
+	case OpRsh32x8:
+		return rewriteValueARM_OpRsh32x8(v, config)
+	case OpRsh8Ux16:
+		return rewriteValueARM_OpRsh8Ux16(v, config)
+	case OpRsh8Ux32:
+		return rewriteValueARM_OpRsh8Ux32(v, config)
+	case OpRsh8Ux64:
+		return rewriteValueARM_OpRsh8Ux64(v, config)
+	case OpRsh8Ux8:
+		return rewriteValueARM_OpRsh8Ux8(v, config)
+	case OpRsh8x16:
+		return rewriteValueARM_OpRsh8x16(v, config)
+	case OpRsh8x32:
+		return rewriteValueARM_OpRsh8x32(v, config)
+	case OpRsh8x64:
+		return rewriteValueARM_OpRsh8x64(v, config)
+	case OpRsh8x8:
+		return rewriteValueARM_OpRsh8x8(v, config)
+	case OpSelect0:
+		return rewriteValueARM_OpSelect0(v, config)
+	case OpSelect1:
+		return rewriteValueARM_OpSelect1(v, config)
+	case OpSignExt16to32:
+		return rewriteValueARM_OpSignExt16to32(v, config)
+	case OpSignExt8to16:
+		return rewriteValueARM_OpSignExt8to16(v, config)
+	case OpSignExt8to32:
+		return rewriteValueARM_OpSignExt8to32(v, config)
+	case OpSignmask:
+		return rewriteValueARM_OpSignmask(v, config)
+	case OpSlicemask:
+		return rewriteValueARM_OpSlicemask(v, config)
+	case OpSqrt:
+		return rewriteValueARM_OpSqrt(v, config)
 	case OpStaticCall:
 		return rewriteValueARM_OpStaticCall(v, config)
 	case OpStore:
 		return rewriteValueARM_OpStore(v, config)
+	case OpSub16:
+		return rewriteValueARM_OpSub16(v, config)
+	case OpSub32:
+		return rewriteValueARM_OpSub32(v, config)
+	case OpSub32F:
+		return rewriteValueARM_OpSub32F(v, config)
+	case OpSub32carry:
+		return rewriteValueARM_OpSub32carry(v, config)
+	case OpSub32withcarry:
+		return rewriteValueARM_OpSub32withcarry(v, config)
+	case OpSub64F:
+		return rewriteValueARM_OpSub64F(v, config)
+	case OpSub8:
+		return rewriteValueARM_OpSub8(v, config)
+	case OpSubPtr:
+		return rewriteValueARM_OpSubPtr(v, config)
+	case OpTrunc16to8:
+		return rewriteValueARM_OpTrunc16to8(v, config)
+	case OpTrunc32to16:
+		return rewriteValueARM_OpTrunc32to16(v, config)
+	case OpTrunc32to8:
+		return rewriteValueARM_OpTrunc32to8(v, config)
+	case OpXor16:
+		return rewriteValueARM_OpXor16(v, config)
+	case OpXor32:
+		return rewriteValueARM_OpXor32(v, config)
+	case OpXor8:
+		return rewriteValueARM_OpXor8(v, config)
+	case OpZero:
+		return rewriteValueARM_OpZero(v, config)
+	case OpZeroExt16to32:
+		return rewriteValueARM_OpZeroExt16to32(v, config)
+	case OpZeroExt8to16:
+		return rewriteValueARM_OpZeroExt8to16(v, config)
+	case OpZeroExt8to32:
+		return rewriteValueARM_OpZeroExt8to32(v, config)
+	case OpZeromask:
+		return rewriteValueARM_OpZeromask(v, config)
 	}
 	return false
 }
-func rewriteValueARM_OpARMADD(v *Value, config *Config) bool {
+func rewriteValueARM_OpARMADC(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (ADD (MOVWconst [c]) x)
+	// match: (ADC (MOVWconst [c]) x flags)
 	// cond:
-	// result: (ADDconst [c] x)
+	// result: (ADCconst [c] x flags)
 	for {
 		v_0 := v.Args[0]
 		if v_0.Op != OpARMMOVWconst {
@@ -46,14 +746,16 @@ func rewriteValueARM_OpARMADD(v *Value, config *Config) bool {
 		}
 		c := v_0.AuxInt
 		x := v.Args[1]
-		v.reset(OpARMADDconst)
+		flags := v.Args[2]
+		v.reset(OpARMADCconst)
 		v.AuxInt = c
 		v.AddArg(x)
+		v.AddArg(flags)
 		return true
 	}
-	// match: (ADD x (MOVWconst [c]))
+	// match: (ADC x (MOVWconst [c]) flags)
 	// cond:
-	// result: (ADDconst [c] x)
+	// result: (ADCconst [c] x flags)
 	for {
 		x := v.Args[0]
 		v_1 := v.Args[1]
@@ -61,224 +763,17857 @@ func rewriteValueARM_OpARMADD(v *Value, config *Config) bool {
 			break
 		}
 		c := v_1.AuxInt
-		v.reset(OpARMADDconst)
+		flags := v.Args[2]
+		v.reset(OpARMADCconst)
 		v.AuxInt = c
 		v.AddArg(x)
+		v.AddArg(flags)
 		return true
 	}
-	return false
-}
-func rewriteValueARM_OpAdd32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Add32 x y)
+	// match: (ADC x (SLLconst [c] y) flags)
 	// cond:
-	// result: (ADD x y)
+	// result: (ADCshiftLL x y [c] flags)
 	for {
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpARMADD)
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftLL)
+		v.AuxInt = c
 		v.AddArg(x)
 		v.AddArg(y)
+		v.AddArg(flags)
 		return true
 	}
-}
-func rewriteValueARM_OpAddr(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Addr {sym} base)
+	// match: (ADC (SLLconst [c] y) x flags)
 	// cond:
-	// result: (ADDconst {sym} base)
+	// result: (ADCshiftLL x y [c] flags)
 	for {
-		sym := v.Aux
-		base := v.Args[0]
-		v.reset(OpARMADDconst)
-		v.Aux = sym
-		v.AddArg(base)
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
 		return true
 	}
-}
-func rewriteValueARM_OpConst32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Const32 [val])
+	// match: (ADC x (SRLconst [c] y) flags)
 	// cond:
-	// result: (MOVWconst [val])
+	// result: (ADCshiftRL x y [c] flags)
 	for {
-		val := v.AuxInt
-		v.reset(OpARMMOVWconst)
-		v.AuxInt = val
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
 		return true
 	}
-}
-func rewriteValueARM_OpLess32(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Less32 x y)
+	// match: (ADC (SRLconst [c] y) x flags)
 	// cond:
-	// result: (LessThan (CMP x y))
+	// result: (ADCshiftRL x y [c] flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADC x (SRAconst [c] y) flags)
+	// cond:
+	// result: (ADCshiftRA x y [c] flags)
 	for {
 		x := v.Args[0]
-		y := v.Args[1]
-		v.reset(OpARMLessThan)
-		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
-		v0.AddArg(x)
-		v0.AddArg(y)
-		v.AddArg(v0)
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
 		return true
 	}
-}
-func rewriteValueARM_OpLoad(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Load <t> ptr mem)
-	// cond: is32BitInt(t)
-	// result: (MOVWload ptr mem)
+	// match: (ADC (SRAconst [c] y) x flags)
+	// cond:
+	// result: (ADCshiftRA x y [c] flags)
 	for {
-		t := v.Type
-		ptr := v.Args[0]
-		mem := v.Args[1]
-		if !(is32BitInt(t)) {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
 			break
 		}
-		v.reset(OpARMMOVWload)
-		v.AddArg(ptr)
-		v.AddArg(mem)
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
 		return true
 	}
-	return false
-}
-func rewriteValueARM_OpARMMOVWload(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (MOVWload [off1] {sym1} (ADDconst [off2] {sym2} ptr) mem)
-	// cond: canMergeSym(sym1,sym2)
-	// result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	// match: (ADC x (SLL y z) flags)
+	// cond:
+	// result: (ADCshiftLLreg x y z flags)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADC (SLL y z) x flags)
+	// cond:
+	// result: (ADCshiftLLreg x y z flags)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
 		v_0 := v.Args[0]
-		if v_0.Op != OpARMADDconst {
+		if v_0.Op != OpARMSLL {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		mem := v.Args[1]
-		if !(canMergeSym(sym1, sym2)) {
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADC x (SRL y z) flags)
+	// cond:
+	// result: (ADCshiftRLreg x y z flags)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
 			break
 		}
-		v.reset(OpARMMOVWload)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(mem)
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADC (SRL y z) x flags)
+	// cond:
+	// result: (ADCshiftRLreg x y z flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADC x (SRA y z) flags)
+	// cond:
+	// result: (ADCshiftRAreg x y z flags)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADC (SRA y z) x flags)
+	// cond:
+	// result: (ADCshiftRAreg x y z flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
 		return true
 	}
 	return false
 }
-func rewriteValueARM_OpARMMOVWstore(v *Value, config *Config) bool {
+func rewriteValueARM_OpARMADCconst(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (MOVWstore [off1] {sym1} (ADDconst [off2] {sym2} ptr) val mem)
-	// cond: canMergeSym(sym1,sym2)
-	// result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	// match: (ADCconst [c] (ADDconst [d] x) flags)
+	// cond:
+	// result: (ADCconst [int64(int32(c+d))] x flags)
 	for {
-		off1 := v.AuxInt
-		sym1 := v.Aux
+		c := v.AuxInt
 		v_0 := v.Args[0]
 		if v_0.Op != OpARMADDconst {
 			break
 		}
-		off2 := v_0.AuxInt
-		sym2 := v_0.Aux
-		ptr := v_0.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		if !(canMergeSym(sym1, sym2)) {
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		flags := v.Args[1]
+		v.reset(OpARMADCconst)
+		v.AuxInt = int64(int32(c + d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADCconst [c] (SUBconst [d] x) flags)
+	// cond:
+	// result: (ADCconst [int64(int32(c-d))] x flags)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSUBconst {
 			break
 		}
-		v.reset(OpARMMOVWstore)
-		v.AuxInt = off1 + off2
-		v.Aux = mergeSym(sym1, sym2)
-		v.AddArg(ptr)
-		v.AddArg(val)
-		v.AddArg(mem)
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		flags := v.Args[1]
+		v.reset(OpARMADCconst)
+		v.AuxInt = int64(int32(c - d))
+		v.AddArg(x)
+		v.AddArg(flags)
 		return true
 	}
 	return false
 }
-func rewriteValueARM_OpOffPtr(v *Value, config *Config) bool {
+func rewriteValueARM_OpARMADCshiftLL(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (OffPtr [off] ptr)
+	// match: (ADCshiftLL (MOVWconst [c]) x [d] flags)
 	// cond:
-	// result: (ADD (MOVWconst <config.Frontend().TypeInt32()> [off]) ptr)
+	// result: (ADCconst [c] (SLLconst <x.Type> x [d]) flags)
 	for {
-		off := v.AuxInt
-		ptr := v.Args[0]
-		v.reset(OpARMADD)
-		v0 := b.NewValue0(v.Line, OpARMMOVWconst, config.Frontend().TypeInt32())
-		v0.AuxInt = off
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
 		v.AddArg(v0)
-		v.AddArg(ptr)
+		v.AddArg(flags)
 		return true
 	}
-}
-func rewriteValueARM_OpStaticCall(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (StaticCall [argwid] {target} mem)
+	// match: (ADCshiftLL x (MOVWconst [c]) [d] flags)
 	// cond:
-	// result: (CALLstatic [argwid] {target} mem)
+	// result: (ADCconst x [int64(uint32(c)<<uint64(d))] flags)
 	for {
-		argwid := v.AuxInt
-		target := v.Aux
-		mem := v.Args[0]
-		v.reset(OpARMCALLstatic)
-		v.AuxInt = argwid
-		v.Aux = target
-		v.AddArg(mem)
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		flags := v.Args[2]
+		v.reset(OpARMADCconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		v.AddArg(flags)
 		return true
 	}
+	return false
 }
-func rewriteValueARM_OpStore(v *Value, config *Config) bool {
+func rewriteValueARM_OpARMADCshiftLLreg(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (Store [4] ptr val mem)
+	// match: (ADCshiftLLreg (MOVWconst [c]) x y flags)
 	// cond:
-	// result: (MOVWstore ptr val mem)
+	// result: (ADCconst [c] (SLL <x.Type> x y) flags)
 	for {
-		if v.AuxInt != 4 {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
 			break
 		}
-		ptr := v.Args[0]
-		val := v.Args[1]
-		mem := v.Args[2]
-		v.reset(OpARMMOVWstore)
-		v.AddArg(ptr)
-		v.AddArg(val)
-		v.AddArg(mem)
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		flags := v.Args[3]
+		v.reset(OpARMADCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADCshiftLLreg x y (MOVWconst [c]) flags)
+	// cond:
+	// result: (ADCshiftLL x y [c] flags)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		flags := v.Args[3]
+		v.reset(OpARMADCshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
 		return true
 	}
 	return false
 }
-func rewriteBlockARM(b *Block) bool {
-	switch b.Kind {
-	case BlockIf:
-		// match: (If (LessThan cc) yes no)
+func rewriteValueARM_OpARMADCshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADCshiftRA (MOVWconst [c]) x [d] flags)
+	// cond:
+	// result: (ADCconst [c] (SRAconst <x.Type> x [d]) flags)
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADCshiftRA x (MOVWconst [c]) [d] flags)
+	// cond:
+	// result: (ADCconst x [int64(int32(c)>>uint64(d))] flags)
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		flags := v.Args[2]
+		v.reset(OpARMADCconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADCshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADCshiftRAreg (MOVWconst [c]) x y flags)
+	// cond:
+	// result: (ADCconst [c] (SRA <x.Type> x y) flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		flags := v.Args[3]
+		v.reset(OpARMADCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADCshiftRAreg x y (MOVWconst [c]) flags)
+	// cond:
+	// result: (ADCshiftRA x y [c] flags)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		flags := v.Args[3]
+		v.reset(OpARMADCshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADCshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADCshiftRL (MOVWconst [c]) x [d] flags)
+	// cond:
+	// result: (ADCconst [c] (SRLconst <x.Type> x [d]) flags)
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMADCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADCshiftRL x (MOVWconst [c]) [d] flags)
+	// cond:
+	// result: (ADCconst x [int64(uint32(c)>>uint64(d))] flags)
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		flags := v.Args[2]
+		v.reset(OpARMADCconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADCshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADCshiftRLreg (MOVWconst [c]) x y flags)
+	// cond:
+	// result: (ADCconst [c] (SRL <x.Type> x y) flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		flags := v.Args[3]
+		v.reset(OpARMADCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (ADCshiftRLreg x y (MOVWconst [c]) flags)
+	// cond:
+	// result: (ADCshiftRL x y [c] flags)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		flags := v.Args[3]
+		v.reset(OpARMADCshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADD (MOVWconst [c]) x)
+	// cond:
+	// result: (ADDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMADDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADD x (MOVWconst [c]))
+	// cond:
+	// result: (ADDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMADDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADD x (SLLconst [c] y))
+	// cond:
+	// result: (ADDshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMADDshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (SLLconst [c] y) x)
+	// cond:
+	// result: (ADDshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMADDshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD x (SRLconst [c] y))
+	// cond:
+	// result: (ADDshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMADDshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (SRLconst [c] y) x)
+	// cond:
+	// result: (ADDshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMADDshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD x (SRAconst [c] y))
+	// cond:
+	// result: (ADDshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMADDshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (SRAconst [c] y) x)
+	// cond:
+	// result: (ADDshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMADDshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD x (SLL y z))
+	// cond:
+	// result: (ADDshiftLLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMADDshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADD (SLL y z) x)
+	// cond:
+	// result: (ADDshiftLLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMADDshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADD x (SRL y z))
+	// cond:
+	// result: (ADDshiftRLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMADDshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADD (SRL y z) x)
+	// cond:
+	// result: (ADDshiftRLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMADDshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADD x (SRA y z))
+	// cond:
+	// result: (ADDshiftRAreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMADDshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADD (SRA y z) x)
+	// cond:
+	// result: (ADDshiftRAreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMADDshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADD x (RSBconst [0] y))
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMRSBconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpARMSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (RSBconst [0] y) x)
+	// cond:
+	// result: (SUB x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMRSBconst {
+			break
+		}
+		if v_0.AuxInt != 0 {
+			break
+		}
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (MUL x y) a)
+	// cond:
+	// result: (MULA x y a)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMUL {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		a := v.Args[1]
+		v.reset(OpARMMULA)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(a)
+		return true
+	}
+	// match: (ADD a (MUL x y))
+	// cond:
+	// result: (MULA x y a)
+	for {
+		a := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMUL {
+			break
+		}
+		x := v_1.Args[0]
+		y := v_1.Args[1]
+		v.reset(OpARMMULA)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(a)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDS(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDS (MOVWconst [c]) x)
+	// cond:
+	// result: (ADDSconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMADDSconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDS x (MOVWconst [c]))
+	// cond:
+	// result: (ADDSconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMADDSconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDS x (SLLconst [c] y))
+	// cond:
+	// result: (ADDSshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMADDSshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDS (SLLconst [c] y) x)
+	// cond:
+	// result: (ADDSshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMADDSshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDS x (SRLconst [c] y))
+	// cond:
+	// result: (ADDSshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMADDSshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDS (SRLconst [c] y) x)
+	// cond:
+	// result: (ADDSshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMADDSshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDS x (SRAconst [c] y))
+	// cond:
+	// result: (ADDSshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMADDSshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDS (SRAconst [c] y) x)
+	// cond:
+	// result: (ADDSshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMADDSshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDS x (SLL y z))
+	// cond:
+	// result: (ADDSshiftLLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMADDSshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADDS (SLL y z) x)
+	// cond:
+	// result: (ADDSshiftLLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMADDSshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADDS x (SRL y z))
+	// cond:
+	// result: (ADDSshiftRLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMADDSshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADDS (SRL y z) x)
+	// cond:
+	// result: (ADDSshiftRLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMADDSshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADDS x (SRA y z))
+	// cond:
+	// result: (ADDSshiftRAreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMADDSshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (ADDS (SRA y z) x)
+	// cond:
+	// result: (ADDSshiftRAreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMADDSshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDSshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDSshiftLL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ADDSconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMADDSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDSshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ADDSconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMADDSconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDSshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDSshiftLLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ADDSconst [c] (SLL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMADDSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDSshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ADDSshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMADDSshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDSshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDSshiftRA (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ADDSconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMADDSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDSshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ADDSconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMADDSconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDSshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDSshiftRAreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ADDSconst [c] (SRA <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMADDSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDSshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ADDSshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMADDSshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDSshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDSshiftRL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ADDSconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMADDSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDSshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ADDSconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMADDSconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDSshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDSshiftRLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ADDSconst [c] (SRL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMADDSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDSshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ADDSshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMADDSshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDconst [off1] (MOVWaddr [off2] {sym} ptr))
+	// cond:
+	// result: (MOVWaddr [off1+off2] {sym} ptr)
+	for {
+		off1 := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym := v_0.Aux
+		ptr := v_0.Args[0]
+		v.reset(OpARMMOVWaddr)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		return true
+	}
+	// match: (ADDconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(int32(c+d))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(int32(c + d))
+		return true
+	}
+	// match: (ADDconst [c] (ADDconst [d] x))
+	// cond:
+	// result: (ADDconst [int64(int32(c+d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMADDconst)
+		v.AuxInt = int64(int32(c + d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (SUBconst [d] x))
+	// cond:
+	// result: (ADDconst [int64(int32(c-d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSUBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMADDconst)
+		v.AuxInt = int64(int32(c - d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (RSBconst [d] x))
+	// cond:
+	// result: (RSBconst [int64(int32(c+d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMRSBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = int64(int32(c + d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDshiftLL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ADDconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMADDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ADDconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMADDconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDshiftLLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ADDconst [c] (SLL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMADDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ADDshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMADDshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDshiftRA (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ADDconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMADDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ADDconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMADDconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDshiftRAreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ADDconst [c] (SRA <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMADDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ADDshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMADDshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDshiftRL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ADDconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMADDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ADDconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMADDconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMADDshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDshiftRLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ADDconst [c] (SRL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMADDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ADDshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMADDshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMAND(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AND (MOVWconst [c]) x)
+	// cond:
+	// result: (ANDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x (MOVWconst [c]))
+	// cond:
+	// result: (ANDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x (SLLconst [c] y))
+	// cond:
+	// result: (ANDshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMANDshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (SLLconst [c] y) x)
+	// cond:
+	// result: (ANDshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMANDshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND x (SRLconst [c] y))
+	// cond:
+	// result: (ANDshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMANDshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (SRLconst [c] y) x)
+	// cond:
+	// result: (ANDshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMANDshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND x (SRAconst [c] y))
+	// cond:
+	// result: (ANDshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMANDshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (SRAconst [c] y) x)
+	// cond:
+	// result: (ANDshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMANDshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND x (SLL y z))
+	// cond:
+	// result: (ANDshiftLLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMANDshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (AND (SLL y z) x)
+	// cond:
+	// result: (ANDshiftLLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMANDshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (AND x (SRL y z))
+	// cond:
+	// result: (ANDshiftRLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMANDshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (AND (SRL y z) x)
+	// cond:
+	// result: (ANDshiftRLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMANDshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (AND x (SRA y z))
+	// cond:
+	// result: (ANDshiftRAreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMANDshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (AND (SRA y z) x)
+	// cond:
+	// result: (ANDshiftRAreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMANDshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (AND x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x (MVN y))
+	// cond:
+	// result: (BIC x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMVN {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpARMBIC)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (MVN y) x)
+	// cond:
+	// result: (BIC x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMVN {
+			break
+		}
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMBIC)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND x (MVNshiftLL y [c]))
+	// cond:
+	// result: (BICshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMVNshiftLL {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMBICshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (MVNshiftLL y [c]) x)
+	// cond:
+	// result: (BICshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMVNshiftLL {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMBICshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND x (MVNshiftRL y [c]))
+	// cond:
+	// result: (BICshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMVNshiftRL {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMBICshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (MVNshiftRL y [c]) x)
+	// cond:
+	// result: (BICshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMVNshiftRL {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMBICshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND x (MVNshiftRA y [c]))
+	// cond:
+	// result: (BICshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMVNshiftRA {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMBICshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (MVNshiftRA y [c]) x)
+	// cond:
+	// result: (BICshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMVNshiftRA {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMBICshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMANDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDconst [0] _)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (ANDconst [c] x)
+	// cond: int32(c)==-1
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [c&d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = c & d
+		return true
+	}
+	// match: (ANDconst [c] (ANDconst [d] x))
+	// cond:
+	// result: (ANDconst [c&d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMANDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMANDconst)
+		v.AuxInt = c & d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMANDshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDshiftLL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ANDconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMANDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ANDshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ANDconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMANDconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDshiftLL x y:(SLLconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARMSLLconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMANDshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDshiftLLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ANDconst [c] (SLL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMANDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ANDshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ANDshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMANDshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMANDshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDshiftRA (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ANDconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMANDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ANDshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ANDconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMANDconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDshiftRA x y:(SRAconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARMSRAconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMANDshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDshiftRAreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ANDconst [c] (SRA <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMANDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ANDshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ANDshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMANDshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMANDshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDshiftRL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ANDconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMANDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ANDshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ANDconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMANDconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDshiftRL x y:(SRLconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARMSRLconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMANDshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDshiftRLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ANDconst [c] (SRL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMANDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ANDshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ANDshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMANDshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMBIC(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BIC x (MOVWconst [c]))
+	// cond:
+	// result: (BICconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMBICconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (BIC x (SLLconst [c] y))
+	// cond:
+	// result: (BICshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMBICshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (BIC x (SRLconst [c] y))
+	// cond:
+	// result: (BICshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMBICshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (BIC x (SRAconst [c] y))
+	// cond:
+	// result: (BICshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMBICshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (BIC x (SLL y z))
+	// cond:
+	// result: (BICshiftLLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMBICshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (BIC x (SRL y z))
+	// cond:
+	// result: (BICshiftRLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMBICshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (BIC x (SRA y z))
+	// cond:
+	// result: (BICshiftRAreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMBICshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (BIC x x)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMBICconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (BICconst [c] _)
+	// cond: int32(c)==-1
+	// result: (MOVWconst [0])
+	for {
+		c := v.AuxInt
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (BICconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [d&^c])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = d &^ c
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMBICshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (BICconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMBICconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (BICshiftLL x (SLLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMBICshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (BICshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMBICshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMBICshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (BICconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMBICconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (BICshiftRA x (SRAconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMBICshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (BICshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMBICshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMBICshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (BICconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMBICconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (BICshiftRL x (SRLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMBICshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (BICshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMBICshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMOVWHSconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMOVWHSconst _ (FlagEQ) [c])
+	// cond:
+	// result: (MOVWconst [c])
+	for {
+		c := v.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = c
+		return true
+	}
+	// match: (CMOVWHSconst x (FlagLT_ULT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMOVWHSconst _ (FlagLT_UGT) [c])
+	// cond:
+	// result: (MOVWconst [c])
+	for {
+		c := v.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = c
+		return true
+	}
+	// match: (CMOVWHSconst x (FlagGT_ULT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMOVWHSconst _ (FlagGT_UGT) [c])
+	// cond:
+	// result: (MOVWconst [c])
+	for {
+		c := v.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = c
+		return true
+	}
+	// match: (CMOVWHSconst x (InvertFlags flags) [c])
+	// cond:
+	// result: (CMOVWLSconst x flags [c])
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMInvertFlags {
+			break
+		}
+		flags := v_1.Args[0]
+		v.reset(OpARMCMOVWLSconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMOVWLSconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMOVWLSconst _ (FlagEQ) [c])
+	// cond:
+	// result: (MOVWconst [c])
+	for {
+		c := v.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = c
+		return true
+	}
+	// match: (CMOVWLSconst _ (FlagLT_ULT) [c])
+	// cond:
+	// result: (MOVWconst [c])
+	for {
+		c := v.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = c
+		return true
+	}
+	// match: (CMOVWLSconst x (FlagLT_UGT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMOVWLSconst _ (FlagGT_ULT) [c])
+	// cond:
+	// result: (MOVWconst [c])
+	for {
+		c := v.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = c
+		return true
+	}
+	// match: (CMOVWLSconst x (FlagGT_UGT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMOVWLSconst x (InvertFlags flags) [c])
+	// cond:
+	// result: (CMOVWHSconst x flags [c])
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMInvertFlags {
+			break
+		}
+		flags := v_1.Args[0]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMP(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMP x (MOVWconst [c]))
+	// cond:
+	// result: (CMPconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMCMPconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMP (MOVWconst [c]) x)
+	// cond:
+	// result: (InvertFlags (CMPconst [c] x))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMP x (SLLconst [c] y))
+	// cond:
+	// result: (CMPshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMCMPshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMP (SLLconst [c] y) x)
+	// cond:
+	// result: (InvertFlags (CMPshiftLL x y [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPshiftLL, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMP x (SRLconst [c] y))
+	// cond:
+	// result: (CMPshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMCMPshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMP (SRLconst [c] y) x)
+	// cond:
+	// result: (InvertFlags (CMPshiftRL x y [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPshiftRL, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMP x (SRAconst [c] y))
+	// cond:
+	// result: (CMPshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMCMPshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMP (SRAconst [c] y) x)
+	// cond:
+	// result: (InvertFlags (CMPshiftRA x y [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPshiftRA, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMP x (SLL y z))
+	// cond:
+	// result: (CMPshiftLLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMCMPshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (CMP (SLL y z) x)
+	// cond:
+	// result: (InvertFlags (CMPshiftLLreg x y z))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPshiftLLreg, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v0.AddArg(z)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMP x (SRL y z))
+	// cond:
+	// result: (CMPshiftRLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMCMPshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (CMP (SRL y z) x)
+	// cond:
+	// result: (InvertFlags (CMPshiftRLreg x y z))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPshiftRLreg, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v0.AddArg(z)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMP x (SRA y z))
+	// cond:
+	// result: (CMPshiftRAreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMCMPshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (CMP (SRA y z) x)
+	// cond:
+	// result: (InvertFlags (CMPshiftRAreg x y z))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPshiftRAreg, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v0.AddArg(z)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMPD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPD x (MOVDconst [0]))
+	// cond:
+	// result: (CMPD0 x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVDconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpARMCMPD0)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMPF(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPF x (MOVFconst [0]))
+	// cond:
+	// result: (CMPF0 x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVFconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpARMCMPF0)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMPconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPconst (MOVWconst [x]) [y])
+	// cond: int32(x)==int32(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) == int32(y)) {
+			break
+		}
+		v.reset(OpARMFlagEQ)
+		return true
+	}
+	// match: (CMPconst (MOVWconst [x]) [y])
+	// cond: int32(x)<int32(y) && uint32(x)<uint32(y)
+	// result: (FlagLT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) < int32(y) && uint32(x) < uint32(y)) {
+			break
+		}
+		v.reset(OpARMFlagLT_ULT)
+		return true
+	}
+	// match: (CMPconst (MOVWconst [x]) [y])
+	// cond: int32(x)<int32(y) && uint32(x)>uint32(y)
+	// result: (FlagLT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) < int32(y) && uint32(x) > uint32(y)) {
+			break
+		}
+		v.reset(OpARMFlagLT_UGT)
+		return true
+	}
+	// match: (CMPconst (MOVWconst [x]) [y])
+	// cond: int32(x)>int32(y) && uint32(x)<uint32(y)
+	// result: (FlagGT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) > int32(y) && uint32(x) < uint32(y)) {
+			break
+		}
+		v.reset(OpARMFlagGT_ULT)
+		return true
+	}
+	// match: (CMPconst (MOVWconst [x]) [y])
+	// cond: int32(x)>int32(y) && uint32(x)>uint32(y)
+	// result: (FlagGT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) > int32(y) && uint32(x) > uint32(y)) {
+			break
+		}
+		v.reset(OpARMFlagGT_UGT)
+		return true
+	}
+	// match: (CMPconst (MOVBUreg _) [c])
+	// cond: 0xff < c
+	// result: (FlagLT_ULT)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVBUreg {
+			break
+		}
+		if !(0xff < c) {
+			break
+		}
+		v.reset(OpARMFlagLT_ULT)
+		return true
+	}
+	// match: (CMPconst (MOVHUreg _) [c])
+	// cond: 0xffff < c
+	// result: (FlagLT_ULT)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVHUreg {
+			break
+		}
+		if !(0xffff < c) {
+			break
+		}
+		v.reset(OpARMFlagLT_ULT)
+		return true
+	}
+	// match: (CMPconst (ANDconst _ [m]) [n])
+	// cond: 0 <= int32(m) && int32(m) < int32(n)
+	// result: (FlagLT_ULT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMANDconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(0 <= int32(m) && int32(m) < int32(n)) {
+			break
+		}
+		v.reset(OpARMFlagLT_ULT)
+		return true
+	}
+	// match: (CMPconst (SRLconst _ [c]) [n])
+	// cond: 0 <= n && 0 < c && c <= 32 && (1<<uint32(32-c)) <= uint32(n)
+	// result: (FlagLT_ULT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		if !(0 <= n && 0 < c && c <= 32 && (1<<uint32(32-c)) <= uint32(n)) {
+			break
+		}
+		v.reset(OpARMFlagLT_ULT)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMPshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPshiftLL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (InvertFlags (CMPconst [c] (SLLconst <x.Type> x [d])))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v0.AuxInt = c
+		v1 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v1.AuxInt = d
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMPshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (CMPconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMCMPconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMPshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPshiftLLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (InvertFlags (CMPconst [c] (SLL <x.Type> x y)))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v0.AuxInt = c
+		v1 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMPshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (CMPshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMCMPshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMPshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPshiftRA (MOVWconst [c]) x [d])
+	// cond:
+	// result: (InvertFlags (CMPconst [c] (SRAconst <x.Type> x [d])))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v0.AuxInt = c
+		v1 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v1.AuxInt = d
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMPshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (CMPconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMCMPconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMPshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPshiftRAreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (InvertFlags (CMPconst [c] (SRA <x.Type> x y)))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v0.AuxInt = c
+		v1 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMPshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (CMPshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMCMPshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMPshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPshiftRL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (InvertFlags (CMPconst [c] (SRLconst <x.Type> x [d])))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v0.AuxInt = c
+		v1 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v1.AuxInt = d
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMPshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (CMPconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMCMPconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMCMPshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPshiftRLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (InvertFlags (CMPconst [c] (SRL <x.Type> x y)))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMInvertFlags)
+		v0 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v0.AuxInt = c
+		v1 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMPshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (CMPshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMCMPshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMEqual(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Equal (FlagEQ))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (Equal (FlagLT_ULT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Equal (FlagLT_UGT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Equal (FlagGT_ULT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Equal (FlagGT_UGT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Equal (InvertFlags x))
+	// cond:
+	// result: (Equal x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMInvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARMEqual)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMGreaterEqual(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GreaterEqual (FlagEQ))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqual (FlagLT_ULT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterEqual (FlagLT_UGT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterEqual (FlagGT_ULT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqual (FlagGT_UGT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqual (InvertFlags x))
+	// cond:
+	// result: (LessEqual x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMInvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARMLessEqual)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMGreaterEqualU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GreaterEqualU (FlagEQ))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqualU (FlagLT_ULT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterEqualU (FlagLT_UGT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqualU (FlagGT_ULT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterEqualU (FlagGT_UGT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqualU (InvertFlags x))
+	// cond:
+	// result: (LessEqualU x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMInvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARMLessEqualU)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMGreaterThan(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GreaterThan (FlagEQ))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThan (FlagLT_ULT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThan (FlagLT_UGT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThan (FlagGT_ULT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterThan (FlagGT_UGT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterThan (InvertFlags x))
+	// cond:
+	// result: (LessThan x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMInvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARMLessThan)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMGreaterThanU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GreaterThanU (FlagEQ))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThanU (FlagLT_ULT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThanU (FlagLT_UGT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterThanU (FlagGT_ULT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThanU (FlagGT_UGT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterThanU (InvertFlags x))
+	// cond:
+	// result: (LessThanU x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMInvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARMLessThanU)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMLessEqual(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LessEqual (FlagEQ))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqual (FlagLT_ULT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqual (FlagLT_UGT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqual (FlagGT_ULT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessEqual (FlagGT_UGT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessEqual (InvertFlags x))
+	// cond:
+	// result: (GreaterEqual x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMInvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARMGreaterEqual)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMLessEqualU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LessEqualU (FlagEQ))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqualU (FlagLT_ULT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqualU (FlagLT_UGT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessEqualU (FlagGT_ULT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqualU (FlagGT_UGT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessEqualU (InvertFlags x))
+	// cond:
+	// result: (GreaterEqualU x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMInvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARMGreaterEqualU)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMLessThan(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LessThan (FlagEQ))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThan (FlagLT_ULT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessThan (FlagLT_UGT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessThan (FlagGT_ULT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThan (FlagGT_UGT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThan (InvertFlags x))
+	// cond:
+	// result: (GreaterThan x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMInvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARMGreaterThan)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMLessThanU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LessThanU (FlagEQ))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThanU (FlagLT_ULT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessThanU (FlagLT_UGT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThanU (FlagGT_ULT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessThanU (FlagGT_UGT))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThanU (InvertFlags x))
+	// cond:
+	// result: (GreaterThanU x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMInvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARMGreaterThanU)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVBUload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBUload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond:
+	// result: (MOVBUload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARMMOVBUload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBUload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVBUload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBUload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVBstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVBUreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBUreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVBUload {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBUreg (ANDconst [c] x))
+	// cond:
+	// result: (ANDconst [c&0xff] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMANDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMANDconst)
+		v.AuxInt = c & 0xff
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBUreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVBUreg {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBUreg (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [int64(uint8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(uint8(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVBload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond:
+	// result: (MOVBload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARMMOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVBstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVBreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBreg x:(MOVBload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVBload {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg (ANDconst [c] x))
+	// cond: c & 0x80 == 0
+	// result: (ANDconst [c&0x7f] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMANDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(c&0x80 == 0) {
+			break
+		}
+		v.reset(OpARMANDconst)
+		v.AuxInt = c & 0x7f
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg x:(MOVBreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVBreg {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [int64(int8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(int8(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVBstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond:
+	// result: (MOVBstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVBreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBUreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVBUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVHreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVHreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVHUreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVHUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVDload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond:
+	// result: (MOVDload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARMMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVDstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond:
+	// result: (MOVDstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVFload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVFload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond:
+	// result: (MOVFload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARMMOVFload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVFload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVFload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVFload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVFload [off] {sym} ptr (MOVFstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVFstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVFstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVFstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond:
+	// result: (MOVFstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVFstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVFstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVFstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVFstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVHUload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHUload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond:
+	// result: (MOVHUload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARMMOVHUload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHUload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVHUload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHUload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVHstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVHUreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHUreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVBUload {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVHUload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVHUload {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg (ANDconst [c] x))
+	// cond:
+	// result: (ANDconst [c&0xffff] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMANDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMANDconst)
+		v.AuxInt = c & 0xffff
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVBUreg {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVHUreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVHUreg {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [int64(uint16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(uint16(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVHload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond:
+	// result: (MOVHload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARMMOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVHstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVHreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHreg x:(MOVBload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVBload {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVBUload {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVHload {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg (ANDconst [c] x))
+	// cond: c & 0x8000 == 0
+	// result: (ANDconst [c&0x7fff] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMANDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(c&0x8000 == 0) {
+			break
+		}
+		v.reset(OpARMANDconst)
+		v.AuxInt = c & 0x7fff
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVBreg {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVBUreg {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARMMOVHreg {
+			break
+		}
+		v.reset(OpARMMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [int64(int16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(int16(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVHstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond:
+	// result: (MOVHstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVHreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARMMOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHUreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVHUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARMMOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond:
+	// result: (MOVWload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARMMOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWload [0] {sym} (ADD ptr idx) mem)
+	// cond: sym == nil && !config.nacl
+	// result: (MOVWloadidx ptr idx mem)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(sym == nil && !config.nacl) {
+			break
+		}
+		v.reset(OpARMMOVWloadidx)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [0] {sym} (ADDshiftLL ptr idx [c]) mem)
+	// cond: sym == nil && !config.nacl
+	// result: (MOVWloadshiftLL ptr idx [c] mem)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDshiftLL {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(sym == nil && !config.nacl) {
+			break
+		}
+		v.reset(OpARMMOVWloadshiftLL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [0] {sym} (ADDshiftRL ptr idx [c]) mem)
+	// cond: sym == nil && !config.nacl
+	// result: (MOVWloadshiftRL ptr idx [c] mem)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDshiftRL {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(sym == nil && !config.nacl) {
+			break
+		}
+		v.reset(OpARMMOVWloadshiftRL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [0] {sym} (ADDshiftRA ptr idx [c]) mem)
+	// cond: sym == nil && !config.nacl
+	// result: (MOVWloadshiftRA ptr idx [c] mem)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDshiftRA {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(sym == nil && !config.nacl) {
+			break
+		}
+		v.reset(OpARMMOVWloadshiftRA)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWloadidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWloadidx ptr idx (MOVWstoreidx ptr2 idx x _))
+	// cond: isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		ptr := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWstoreidx {
+			break
+		}
+		ptr2 := v_2.Args[0]
+		if idx != v_2.Args[1] {
+			break
+		}
+		x := v_2.Args[2]
+		if !(isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWloadidx ptr (MOVWconst [c]) mem)
+	// cond:
+	// result: (MOVWload [c] ptr mem)
+	for {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		v.reset(OpARMMOVWload)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWloadidx (MOVWconst [c]) ptr mem)
+	// cond:
+	// result: (MOVWload [c] ptr mem)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVWload)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWloadidx ptr (SLLconst idx [c]) mem)
+	// cond:
+	// result: (MOVWloadshiftLL ptr idx [c] mem)
+	for {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARMMOVWloadshiftLL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWloadidx (SLLconst idx [c]) ptr mem)
+	// cond:
+	// result: (MOVWloadshiftLL ptr idx [c] mem)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		idx := v_0.Args[0]
+		ptr := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVWloadshiftLL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWloadidx ptr (SRLconst idx [c]) mem)
+	// cond:
+	// result: (MOVWloadshiftRL ptr idx [c] mem)
+	for {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARMMOVWloadshiftRL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWloadidx (SRLconst idx [c]) ptr mem)
+	// cond:
+	// result: (MOVWloadshiftRL ptr idx [c] mem)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		idx := v_0.Args[0]
+		ptr := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVWloadshiftRL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWloadidx ptr (SRAconst idx [c]) mem)
+	// cond:
+	// result: (MOVWloadshiftRA ptr idx [c] mem)
+	for {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARMMOVWloadshiftRA)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWloadidx (SRAconst idx [c]) ptr mem)
+	// cond:
+	// result: (MOVWloadshiftRA ptr idx [c] mem)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		idx := v_0.Args[0]
+		ptr := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVWloadshiftRA)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWloadshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWloadshiftLL ptr idx [c] (MOVWstoreshiftLL ptr2 idx [d] x _))
+	// cond: c==d && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		c := v.AuxInt
+		ptr := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWstoreshiftLL {
+			break
+		}
+		d := v_2.AuxInt
+		ptr2 := v_2.Args[0]
+		if idx != v_2.Args[1] {
+			break
+		}
+		x := v_2.Args[2]
+		if !(c == d && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWloadshiftLL ptr (MOVWconst [c]) [d] mem)
+	// cond:
+	// result: (MOVWload [int64(uint32(c)<<uint64(d))] ptr mem)
+	for {
+		d := v.AuxInt
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		v.reset(OpARMMOVWload)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWloadshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWloadshiftRA ptr idx [c] (MOVWstoreshiftRA ptr2 idx [d] x _))
+	// cond: c==d && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		c := v.AuxInt
+		ptr := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWstoreshiftRA {
+			break
+		}
+		d := v_2.AuxInt
+		ptr2 := v_2.Args[0]
+		if idx != v_2.Args[1] {
+			break
+		}
+		x := v_2.Args[2]
+		if !(c == d && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWloadshiftRA ptr (MOVWconst [c]) [d] mem)
+	// cond:
+	// result: (MOVWload [int64(int32(c)>>uint64(d))] ptr mem)
+	for {
+		d := v.AuxInt
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		v.reset(OpARMMOVWload)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWloadshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWloadshiftRL ptr idx [c] (MOVWstoreshiftRL ptr2 idx [d] x _))
+	// cond: c==d && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		c := v.AuxInt
+		ptr := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWstoreshiftRL {
+			break
+		}
+		d := v_2.AuxInt
+		ptr2 := v_2.Args[0]
+		if idx != v_2.Args[1] {
+			break
+		}
+		x := v_2.Args[2]
+		if !(c == d && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWloadshiftRL ptr (MOVWconst [c]) [d] mem)
+	// cond:
+	// result: (MOVWload [int64(uint32(c)>>uint64(d))] ptr mem)
+	for {
+		d := v.AuxInt
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		v.reset(OpARMMOVWload)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWreg x)
+	// cond: x.Uses == 1
+	// result: (MOVWnop x)
+	for {
+		x := v.Args[0]
+		if !(x.Uses == 1) {
+			break
+		}
+		v.reset(OpARMMOVWnop)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = c
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond:
+	// result: (MOVWstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARMMOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [0] {sym} (ADD ptr idx) val mem)
+	// cond: sym == nil && !config.nacl
+	// result: (MOVWstoreidx ptr idx val mem)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(sym == nil && !config.nacl) {
+			break
+		}
+		v.reset(OpARMMOVWstoreidx)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [0] {sym} (ADDshiftLL ptr idx [c]) val mem)
+	// cond: sym == nil && !config.nacl
+	// result: (MOVWstoreshiftLL ptr idx [c] val mem)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDshiftLL {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(sym == nil && !config.nacl) {
+			break
+		}
+		v.reset(OpARMMOVWstoreshiftLL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [0] {sym} (ADDshiftRL ptr idx [c]) val mem)
+	// cond: sym == nil && !config.nacl
+	// result: (MOVWstoreshiftRL ptr idx [c] val mem)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDshiftRL {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(sym == nil && !config.nacl) {
+			break
+		}
+		v.reset(OpARMMOVWstoreshiftRL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [0] {sym} (ADDshiftRA ptr idx [c]) val mem)
+	// cond: sym == nil && !config.nacl
+	// result: (MOVWstoreshiftRA ptr idx [c] val mem)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDshiftRA {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(sym == nil && !config.nacl) {
+			break
+		}
+		v.reset(OpARMMOVWstoreshiftRA)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWstoreidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreidx ptr (MOVWconst [c]) val mem)
+	// cond:
+	// result: (MOVWstore [c] ptr val mem)
+	for {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstore)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx (MOVWconst [c]) ptr val mem)
+	// cond:
+	// result: (MOVWstore [c] ptr val mem)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		ptr := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstore)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx ptr (SLLconst idx [c]) val mem)
+	// cond:
+	// result: (MOVWstoreshiftLL ptr idx [c] val mem)
+	for {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstoreshiftLL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx (SLLconst idx [c]) ptr val mem)
+	// cond:
+	// result: (MOVWstoreshiftLL ptr idx [c] val mem)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		idx := v_0.Args[0]
+		ptr := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstoreshiftLL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx ptr (SRLconst idx [c]) val mem)
+	// cond:
+	// result: (MOVWstoreshiftRL ptr idx [c] val mem)
+	for {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstoreshiftRL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx (SRLconst idx [c]) ptr val mem)
+	// cond:
+	// result: (MOVWstoreshiftRL ptr idx [c] val mem)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		idx := v_0.Args[0]
+		ptr := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstoreshiftRL)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx ptr (SRAconst idx [c]) val mem)
+	// cond:
+	// result: (MOVWstoreshiftRA ptr idx [c] val mem)
+	for {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstoreshiftRA)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx (SRAconst idx [c]) ptr val mem)
+	// cond:
+	// result: (MOVWstoreshiftRA ptr idx [c] val mem)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		idx := v_0.Args[0]
+		ptr := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstoreshiftRA)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWstoreshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreshiftLL ptr (MOVWconst [c]) [d] val mem)
+	// cond:
+	// result: (MOVWstore [int64(uint32(c)<<uint64(d))] ptr val mem)
+	for {
+		d := v.AuxInt
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstore)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWstoreshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreshiftRA ptr (MOVWconst [c]) [d] val mem)
+	// cond:
+	// result: (MOVWstore [int64(int32(c)>>uint64(d))] ptr val mem)
+	for {
+		d := v.AuxInt
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstore)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMOVWstoreshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreshiftRL ptr (MOVWconst [c]) [d] val mem)
+	// cond:
+	// result: (MOVWstore [int64(uint32(c)>>uint64(d))] ptr val mem)
+	for {
+		d := v.AuxInt
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARMMOVWstore)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMUL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MUL x (MOVWconst [c]))
+	// cond: int32(c) == -1
+	// result: (RSBconst [0] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpARMRSBconst)
+		v.AuxInt = 0
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL _ (MOVWconst [0]))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (MUL x (MOVWconst [1]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL x (MOVWconst [c]))
+	// cond: isPowerOfTwo(c)
+	// result: (SLLconst [log2(c)] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL x (MOVWconst [c]))
+	// cond: isPowerOfTwo(c-1) && int32(c) >= 3
+	// result: (ADDshiftLL x x [log2(c-1)])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c-1) && int32(c) >= 3) {
+			break
+		}
+		v.reset(OpARMADDshiftLL)
+		v.AuxInt = log2(c - 1)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL x (MOVWconst [c]))
+	// cond: isPowerOfTwo(c+1) && int32(c) >= 7
+	// result: (RSBshiftLL x x [log2(c+1)])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c+1) && int32(c) >= 7) {
+			break
+		}
+		v.reset(OpARMRSBshiftLL)
+		v.AuxInt = log2(c + 1)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL x (MOVWconst [c]))
+	// cond: c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)
+	// result: (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = log2(c / 3)
+		v0 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v0.AuxInt = 1
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL x (MOVWconst [c]))
+	// cond: c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)
+	// result: (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = log2(c / 5)
+		v0 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v0.AuxInt = 2
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL x (MOVWconst [c]))
+	// cond: c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)
+	// result: (SLLconst [log2(c/7)] (RSBshiftLL <x.Type> x x [3]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = log2(c / 7)
+		v0 := b.NewValue0(v.Line, OpARMRSBshiftLL, x.Type)
+		v0.AuxInt = 3
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL x (MOVWconst [c]))
+	// cond: c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)
+	// result: (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = log2(c / 9)
+		v0 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v0.AuxInt = 3
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) x)
+	// cond: int32(c) == -1
+	// result: (RSBconst [0] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpARMRSBconst)
+		v.AuxInt = 0
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVWconst [0]) _)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		if v_0.AuxInt != 0 {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (MUL (MOVWconst [1]) x)
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		if v_0.AuxInt != 1 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) x)
+	// cond: isPowerOfTwo(c)
+	// result: (SLLconst [log2(c)] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) x)
+	// cond: isPowerOfTwo(c-1) && int32(c) >= 3
+	// result: (ADDshiftLL x x [log2(c-1)])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(c-1) && int32(c) >= 3) {
+			break
+		}
+		v.reset(OpARMADDshiftLL)
+		v.AuxInt = log2(c - 1)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) x)
+	// cond: isPowerOfTwo(c+1) && int32(c) >= 7
+	// result: (RSBshiftLL x x [log2(c+1)])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(c+1) && int32(c) >= 7) {
+			break
+		}
+		v.reset(OpARMRSBshiftLL)
+		v.AuxInt = log2(c + 1)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) x)
+	// cond: c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)
+	// result: (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = log2(c / 3)
+		v0 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v0.AuxInt = 1
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) x)
+	// cond: c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)
+	// result: (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = log2(c / 5)
+		v0 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v0.AuxInt = 2
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) x)
+	// cond: c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)
+	// result: (SLLconst [log2(c/7)] (RSBshiftLL <x.Type> x x [3]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = log2(c / 7)
+		v0 := b.NewValue0(v.Line, OpARMRSBshiftLL, x.Type)
+		v0.AuxInt = 3
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) x)
+	// cond: c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)
+	// result: (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = log2(c / 9)
+		v0 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v0.AuxInt = 3
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(int32(c*d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(int32(c * d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMULA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MULA x (MOVWconst [c]) a)
+	// cond: int32(c) == -1
+	// result: (SUB a x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		a := v.Args[2]
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpARMSUB)
+		v.AddArg(a)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULA _ (MOVWconst [0]) a)
+	// cond:
+	// result: a
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		a := v.Args[2]
+		v.reset(OpCopy)
+		v.Type = a.Type
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA x (MOVWconst [1]) a)
+	// cond:
+	// result: (ADD x a)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		a := v.Args[2]
+		v.reset(OpARMADD)
+		v.AddArg(x)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA x (MOVWconst [c]) a)
+	// cond: isPowerOfTwo(c)
+	// result: (ADD (SLLconst <x.Type> [log2(c)] x) a)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		a := v.Args[2]
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = log2(c)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA x (MOVWconst [c]) a)
+	// cond: isPowerOfTwo(c-1) && int32(c) >= 3
+	// result: (ADD (ADDshiftLL <x.Type> x x [log2(c-1)]) a)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		a := v.Args[2]
+		if !(isPowerOfTwo(c-1) && int32(c) >= 3) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v0.AuxInt = log2(c - 1)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA x (MOVWconst [c]) a)
+	// cond: isPowerOfTwo(c+1) && int32(c) >= 7
+	// result: (ADD (RSBshiftLL <x.Type> x x [log2(c+1)]) a)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		a := v.Args[2]
+		if !(isPowerOfTwo(c+1) && int32(c) >= 7) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMRSBshiftLL, x.Type)
+		v0.AuxInt = log2(c + 1)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA x (MOVWconst [c]) a)
+	// cond: c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)
+	// result: (ADD (SLLconst <x.Type> [log2(c/3)] (ADDshiftLL <x.Type> x x [1])) a)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		a := v.Args[2]
+		if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = log2(c / 3)
+		v1 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA x (MOVWconst [c]) a)
+	// cond: c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)
+	// result: (ADD (SLLconst <x.Type> [log2(c/5)] (ADDshiftLL <x.Type> x x [2])) a)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		a := v.Args[2]
+		if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = log2(c / 5)
+		v1 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v1.AuxInt = 2
+		v1.AddArg(x)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA x (MOVWconst [c]) a)
+	// cond: c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)
+	// result: (ADD (SLLconst <x.Type> [log2(c/7)] (RSBshiftLL <x.Type> x x [3])) a)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		a := v.Args[2]
+		if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = log2(c / 7)
+		v1 := b.NewValue0(v.Line, OpARMRSBshiftLL, x.Type)
+		v1.AuxInt = 3
+		v1.AddArg(x)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA x (MOVWconst [c]) a)
+	// cond: c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)
+	// result: (ADD (SLLconst <x.Type> [log2(c/9)] (ADDshiftLL <x.Type> x x [3])) a)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		a := v.Args[2]
+		if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = log2(c / 9)
+		v1 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v1.AuxInt = 3
+		v1.AddArg(x)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA (MOVWconst [c]) x a)
+	// cond: int32(c) == -1
+	// result: (SUB a x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		a := v.Args[2]
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpARMSUB)
+		v.AddArg(a)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULA (MOVWconst [0]) _ a)
+	// cond:
+	// result: a
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		if v_0.AuxInt != 0 {
+			break
+		}
+		a := v.Args[2]
+		v.reset(OpCopy)
+		v.Type = a.Type
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA (MOVWconst [1]) x a)
+	// cond:
+	// result: (ADD x a)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		if v_0.AuxInt != 1 {
+			break
+		}
+		x := v.Args[1]
+		a := v.Args[2]
+		v.reset(OpARMADD)
+		v.AddArg(x)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA (MOVWconst [c]) x a)
+	// cond: isPowerOfTwo(c)
+	// result: (ADD (SLLconst <x.Type> [log2(c)] x) a)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		a := v.Args[2]
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = log2(c)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA (MOVWconst [c]) x a)
+	// cond: isPowerOfTwo(c-1) && int32(c) >= 3
+	// result: (ADD (ADDshiftLL <x.Type> x x [log2(c-1)]) a)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		a := v.Args[2]
+		if !(isPowerOfTwo(c-1) && int32(c) >= 3) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v0.AuxInt = log2(c - 1)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA (MOVWconst [c]) x a)
+	// cond: isPowerOfTwo(c+1) && int32(c) >= 7
+	// result: (ADD (RSBshiftLL <x.Type> x x [log2(c+1)]) a)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		a := v.Args[2]
+		if !(isPowerOfTwo(c+1) && int32(c) >= 7) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMRSBshiftLL, x.Type)
+		v0.AuxInt = log2(c + 1)
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA (MOVWconst [c]) x a)
+	// cond: c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)
+	// result: (ADD (SLLconst <x.Type> [log2(c/3)] (ADDshiftLL <x.Type> x x [1])) a)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		a := v.Args[2]
+		if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = log2(c / 3)
+		v1 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA (MOVWconst [c]) x a)
+	// cond: c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)
+	// result: (ADD (SLLconst <x.Type> [log2(c/5)] (ADDshiftLL <x.Type> x x [2])) a)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		a := v.Args[2]
+		if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = log2(c / 5)
+		v1 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v1.AuxInt = 2
+		v1.AddArg(x)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA (MOVWconst [c]) x a)
+	// cond: c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)
+	// result: (ADD (SLLconst <x.Type> [log2(c/7)] (RSBshiftLL <x.Type> x x [3])) a)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		a := v.Args[2]
+		if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = log2(c / 7)
+		v1 := b.NewValue0(v.Line, OpARMRSBshiftLL, x.Type)
+		v1.AuxInt = 3
+		v1.AddArg(x)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA (MOVWconst [c]) x a)
+	// cond: c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)
+	// result: (ADD (SLLconst <x.Type> [log2(c/9)] (ADDshiftLL <x.Type> x x [3])) a)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		a := v.Args[2]
+		if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARMADD)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = log2(c / 9)
+		v1 := b.NewValue0(v.Line, OpARMADDshiftLL, x.Type)
+		v1.AuxInt = 3
+		v1.AddArg(x)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v.AddArg(a)
+		return true
+	}
+	// match: (MULA (MOVWconst [c]) (MOVWconst [d]) a)
+	// cond:
+	// result: (ADDconst [int64(int32(c*d))] a)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_1.AuxInt
+		a := v.Args[2]
+		v.reset(OpARMADDconst)
+		v.AuxInt = int64(int32(c * d))
+		v.AddArg(a)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMVN(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MVN (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [^c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = ^c
+		return true
+	}
+	// match: (MVN (SLLconst [c] x))
+	// cond:
+	// result: (MVNshiftLL x [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMMVNshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (MVN (SRLconst [c] x))
+	// cond:
+	// result: (MVNshiftRL x [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMMVNshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (MVN (SRAconst [c] x))
+	// cond:
+	// result: (MVNshiftRA x [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMMVNshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (MVN (SLL x y))
+	// cond:
+	// result: (MVNshiftLLreg x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpARMMVNshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (MVN (SRL x y))
+	// cond:
+	// result: (MVNshiftRLreg x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpARMMVNshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (MVN (SRA x y))
+	// cond:
+	// result: (MVNshiftRAreg x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpARMMVNshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMVNshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MVNshiftLL (MOVWconst [c]) [d])
+	// cond:
+	// result: (MOVWconst [^int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = ^int64(uint32(c) << uint64(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMVNshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MVNshiftLLreg x (MOVWconst [c]))
+	// cond:
+	// result: (MVNshiftLL x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMMVNshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMVNshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MVNshiftRA (MOVWconst [c]) [d])
+	// cond:
+	// result: (MOVWconst [^int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = ^int64(int32(c) >> uint64(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMVNshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MVNshiftRAreg x (MOVWconst [c]))
+	// cond:
+	// result: (MVNshiftRA x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMMVNshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMVNshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MVNshiftRL (MOVWconst [c]) [d])
+	// cond:
+	// result: (MOVWconst [^int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = ^int64(uint32(c) >> uint64(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMMVNshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MVNshiftRLreg x (MOVWconst [c]))
+	// cond:
+	// result: (MVNshiftRL x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMMVNshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMNotEqual(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NotEqual (FlagEQ))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (NotEqual (FlagLT_ULT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (NotEqual (FlagLT_UGT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (NotEqual (FlagGT_ULT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (NotEqual (FlagGT_UGT))
+	// cond:
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (NotEqual (InvertFlags x))
+	// cond:
+	// result: (NotEqual x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMInvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARMNotEqual)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OR (MOVWconst [c]) x)
+	// cond:
+	// result: (ORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR x (MOVWconst [c]))
+	// cond:
+	// result: (ORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR x (SLLconst [c] y))
+	// cond:
+	// result: (ORshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR (SLLconst [c] y) x)
+	// cond:
+	// result: (ORshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR x (SRLconst [c] y))
+	// cond:
+	// result: (ORshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMORshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR (SRLconst [c] y) x)
+	// cond:
+	// result: (ORshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMORshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR x (SRAconst [c] y))
+	// cond:
+	// result: (ORshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMORshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR (SRAconst [c] y) x)
+	// cond:
+	// result: (ORshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMORshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR x (SLL y z))
+	// cond:
+	// result: (ORshiftLLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMORshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (OR (SLL y z) x)
+	// cond:
+	// result: (ORshiftLLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMORshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (OR x (SRL y z))
+	// cond:
+	// result: (ORshiftRLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMORshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (OR (SRL y z) x)
+	// cond:
+	// result: (ORshiftRLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMORshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (OR x (SRA y z))
+	// cond:
+	// result: (ORshiftRAreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMORshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (OR (SRA y z) x)
+	// cond:
+	// result: (ORshiftRAreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMORshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (OR x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORconst [c] _)
+	// cond: int32(c)==-1
+	// result: (MOVWconst [-1])
+	for {
+		c := v.AuxInt
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (ORconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [c|d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = c | d
+		return true
+	}
+	// match: (ORconst [c] (ORconst [d] x))
+	// cond:
+	// result: (ORconst [c|d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMORconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMORconst)
+		v.AuxInt = c | d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMORshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORshiftLL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ORconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ORshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ORconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMORconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORshiftLL x y:(SLLconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARMSLLconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMORshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORshiftLLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ORconst [c] (SLL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ORshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ORshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMORshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORshiftRA (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ORconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ORshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ORconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMORconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORshiftRA x y:(SRAconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARMSRAconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMORshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORshiftRAreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ORconst [c] (SRA <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ORshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ORshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMORshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMORshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORshiftRL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (ORconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ORshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (ORconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMORconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORshiftRL x y:(SRLconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARMSRLconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMORshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORshiftRLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (ORconst [c] (SRL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ORshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (ORshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMORshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSB (MOVWconst [c]) x)
+	// cond:
+	// result: (SUBconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMSUBconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (RSB x (MOVWconst [c]))
+	// cond:
+	// result: (RSBconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMRSBconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (RSB x (SLLconst [c] y))
+	// cond:
+	// result: (RSBshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMRSBshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (RSB (SLLconst [c] y) x)
+	// cond:
+	// result: (SUBshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMSUBshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (RSB x (SRLconst [c] y))
+	// cond:
+	// result: (RSBshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMRSBshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (RSB (SRLconst [c] y) x)
+	// cond:
+	// result: (SUBshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMSUBshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (RSB x (SRAconst [c] y))
+	// cond:
+	// result: (RSBshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMRSBshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (RSB (SRAconst [c] y) x)
+	// cond:
+	// result: (SUBshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMSUBshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (RSB x (SLL y z))
+	// cond:
+	// result: (RSBshiftLLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMRSBshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (RSB (SLL y z) x)
+	// cond:
+	// result: (SUBshiftLLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMSUBshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (RSB x (SRL y z))
+	// cond:
+	// result: (RSBshiftRLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMRSBshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (RSB (SRL y z) x)
+	// cond:
+	// result: (SUBshiftRLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMSUBshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (RSB x (SRA y z))
+	// cond:
+	// result: (RSBshiftRAreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMRSBshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (RSB (SRA y z) x)
+	// cond:
+	// result: (SUBshiftRAreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMSUBshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (RSB x x)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBSshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBSshiftLL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (SUBSconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMSUBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBSshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (RSBSconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMRSBSconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBSshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBSshiftLLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (SUBSconst [c] (SLL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMSUBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBSshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (RSBSshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMRSBSshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBSshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBSshiftRA (MOVWconst [c]) x [d])
+	// cond:
+	// result: (SUBSconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMSUBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBSshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (RSBSconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMRSBSconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBSshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBSshiftRAreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (SUBSconst [c] (SRA <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMSUBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBSshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (RSBSshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMRSBSshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBSshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBSshiftRL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (SUBSconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMSUBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBSshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (RSBSconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMRSBSconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBSshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBSshiftRLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (SUBSconst [c] (SRL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMSUBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBSshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (RSBSshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMRSBSshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(int32(c-d))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(int32(c - d))
+		return true
+	}
+	// match: (RSBconst [c] (RSBconst [d] x))
+	// cond:
+	// result: (ADDconst [int64(int32(c-d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMRSBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMADDconst)
+		v.AuxInt = int64(int32(c - d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (RSBconst [c] (ADDconst [d] x))
+	// cond:
+	// result: (RSBconst [int64(int32(c-d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = int64(int32(c - d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (RSBconst [c] (SUBconst [d] x))
+	// cond:
+	// result: (RSBconst [int64(int32(c+d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSUBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = int64(int32(c + d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBshiftLL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (SUBconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMSUBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (RSBconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMRSBconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (RSBshiftLL x (SLLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBshiftLLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (SUBconst [c] (SLL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMSUBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (RSBshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMRSBshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBshiftRA (MOVWconst [c]) x [d])
+	// cond:
+	// result: (SUBconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMSUBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (RSBconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMRSBconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (RSBshiftRA x (SRAconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBshiftRAreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (SUBconst [c] (SRA <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMSUBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (RSBshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMRSBshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBshiftRL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (SUBconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMSUBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (RSBconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMRSBconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (RSBshiftRL x (SRLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSBshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSBshiftRLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (SUBconst [c] (SRL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMSUBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (RSBshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (RSBshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMRSBshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSCconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSCconst [c] (ADDconst [d] x) flags)
+	// cond:
+	// result: (RSCconst [int64(int32(c-d))] x flags)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		flags := v.Args[1]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = int64(int32(c - d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (RSCconst [c] (SUBconst [d] x) flags)
+	// cond:
+	// result: (RSCconst [int64(int32(c+d))] x flags)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSUBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		flags := v.Args[1]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = int64(int32(c + d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSCshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSCshiftLL (MOVWconst [c]) x [d] flags)
+	// cond:
+	// result: (SBCconst [c] (SLLconst <x.Type> x [d]) flags)
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (RSCshiftLL x (MOVWconst [c]) [d] flags)
+	// cond:
+	// result: (RSCconst x [int64(uint32(c)<<uint64(d))] flags)
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		flags := v.Args[2]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSCshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSCshiftLLreg (MOVWconst [c]) x y flags)
+	// cond:
+	// result: (SBCconst [c] (SLL <x.Type> x y) flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		flags := v.Args[3]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (RSCshiftLLreg x y (MOVWconst [c]) flags)
+	// cond:
+	// result: (RSCshiftLL x y [c] flags)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		flags := v.Args[3]
+		v.reset(OpARMRSCshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSCshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSCshiftRA (MOVWconst [c]) x [d] flags)
+	// cond:
+	// result: (SBCconst [c] (SRAconst <x.Type> x [d]) flags)
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (RSCshiftRA x (MOVWconst [c]) [d] flags)
+	// cond:
+	// result: (RSCconst x [int64(int32(c)>>uint64(d))] flags)
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		flags := v.Args[2]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSCshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSCshiftRAreg (MOVWconst [c]) x y flags)
+	// cond:
+	// result: (SBCconst [c] (SRA <x.Type> x y) flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		flags := v.Args[3]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (RSCshiftRAreg x y (MOVWconst [c]) flags)
+	// cond:
+	// result: (RSCshiftRA x y [c] flags)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		flags := v.Args[3]
+		v.reset(OpARMRSCshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSCshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSCshiftRL (MOVWconst [c]) x [d] flags)
+	// cond:
+	// result: (SBCconst [c] (SRLconst <x.Type> x [d]) flags)
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (RSCshiftRL x (MOVWconst [c]) [d] flags)
+	// cond:
+	// result: (RSCconst x [int64(uint32(c)>>uint64(d))] flags)
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		flags := v.Args[2]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMRSCshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (RSCshiftRLreg (MOVWconst [c]) x y flags)
+	// cond:
+	// result: (SBCconst [c] (SRL <x.Type> x y) flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		flags := v.Args[3]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (RSCshiftRLreg x y (MOVWconst [c]) flags)
+	// cond:
+	// result: (RSCshiftRL x y [c] flags)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		flags := v.Args[3]
+		v.reset(OpARMRSCshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSBC(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBC (MOVWconst [c]) x flags)
+	// cond:
+	// result: (RSCconst [c] x flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC x (MOVWconst [c]) flags)
+	// cond:
+	// result: (SBCconst [c] x flags)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		flags := v.Args[2]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC x (SLLconst [c] y) flags)
+	// cond:
+	// result: (SBCshiftLL x y [c] flags)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		flags := v.Args[2]
+		v.reset(OpARMSBCshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC (SLLconst [c] y) x flags)
+	// cond:
+	// result: (RSCshiftLL x y [c] flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMRSCshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC x (SRLconst [c] y) flags)
+	// cond:
+	// result: (SBCshiftRL x y [c] flags)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		flags := v.Args[2]
+		v.reset(OpARMSBCshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC (SRLconst [c] y) x flags)
+	// cond:
+	// result: (RSCshiftRL x y [c] flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMRSCshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC x (SRAconst [c] y) flags)
+	// cond:
+	// result: (SBCshiftRA x y [c] flags)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		flags := v.Args[2]
+		v.reset(OpARMSBCshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC (SRAconst [c] y) x flags)
+	// cond:
+	// result: (RSCshiftRA x y [c] flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMRSCshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC x (SLL y z) flags)
+	// cond:
+	// result: (SBCshiftLLreg x y z flags)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMSBCshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC (SLL y z) x flags)
+	// cond:
+	// result: (RSCshiftLLreg x y z flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMRSCshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC x (SRL y z) flags)
+	// cond:
+	// result: (SBCshiftRLreg x y z flags)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMSBCshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC (SRL y z) x flags)
+	// cond:
+	// result: (RSCshiftRLreg x y z flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMRSCshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC x (SRA y z) flags)
+	// cond:
+	// result: (SBCshiftRAreg x y z flags)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMSBCshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBC (SRA y z) x flags)
+	// cond:
+	// result: (RSCshiftRAreg x y z flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMRSCshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSBCconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBCconst [c] (ADDconst [d] x) flags)
+	// cond:
+	// result: (SBCconst [int64(int32(c-d))] x flags)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		flags := v.Args[1]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = int64(int32(c - d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBCconst [c] (SUBconst [d] x) flags)
+	// cond:
+	// result: (SBCconst [int64(int32(c+d))] x flags)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSUBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		flags := v.Args[1]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = int64(int32(c + d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSBCshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBCshiftLL (MOVWconst [c]) x [d] flags)
+	// cond:
+	// result: (RSCconst [c] (SLLconst <x.Type> x [d]) flags)
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBCshiftLL x (MOVWconst [c]) [d] flags)
+	// cond:
+	// result: (SBCconst x [int64(uint32(c)<<uint64(d))] flags)
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		flags := v.Args[2]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSBCshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBCshiftLLreg (MOVWconst [c]) x y flags)
+	// cond:
+	// result: (RSCconst [c] (SLL <x.Type> x y) flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		flags := v.Args[3]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBCshiftLLreg x y (MOVWconst [c]) flags)
+	// cond:
+	// result: (SBCshiftLL x y [c] flags)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		flags := v.Args[3]
+		v.reset(OpARMSBCshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSBCshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBCshiftRA (MOVWconst [c]) x [d] flags)
+	// cond:
+	// result: (RSCconst [c] (SRAconst <x.Type> x [d]) flags)
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBCshiftRA x (MOVWconst [c]) [d] flags)
+	// cond:
+	// result: (SBCconst x [int64(int32(c)>>uint64(d))] flags)
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		flags := v.Args[2]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSBCshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBCshiftRAreg (MOVWconst [c]) x y flags)
+	// cond:
+	// result: (RSCconst [c] (SRA <x.Type> x y) flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		flags := v.Args[3]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBCshiftRAreg x y (MOVWconst [c]) flags)
+	// cond:
+	// result: (SBCshiftRA x y [c] flags)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		flags := v.Args[3]
+		v.reset(OpARMSBCshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSBCshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBCshiftRL (MOVWconst [c]) x [d] flags)
+	// cond:
+	// result: (RSCconst [c] (SRLconst <x.Type> x [d]) flags)
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		flags := v.Args[2]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBCshiftRL x (MOVWconst [c]) [d] flags)
+	// cond:
+	// result: (SBCconst x [int64(uint32(c)>>uint64(d))] flags)
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		flags := v.Args[2]
+		v.reset(OpARMSBCconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSBCshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SBCshiftRLreg (MOVWconst [c]) x y flags)
+	// cond:
+	// result: (RSCconst [c] (SRL <x.Type> x y) flags)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		flags := v.Args[3]
+		v.reset(OpARMRSCconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(flags)
+		return true
+	}
+	// match: (SBCshiftRLreg x y (MOVWconst [c]) flags)
+	// cond:
+	// result: (SBCshiftRL x y [c] flags)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		flags := v.Args[3]
+		v.reset(OpARMSBCshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(flags)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SLL x (MOVWconst [c]))
+	// cond:
+	// result: (SLLconst x [c&31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSLLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSLLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SLLconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(uint32(d)<<uint64(c))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(uint32(d) << uint64(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRA x (MOVWconst [c]))
+	// cond:
+	// result: (SRAconst x [c&31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSRAconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSRAcond(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRAcond x _ (FlagEQ))
+	// cond:
+	// result: (SRAconst x [31])
+	for {
+		x := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMFlagEQ {
+			break
+		}
+		v.reset(OpARMSRAconst)
+		v.AuxInt = 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRAcond x y (FlagLT_ULT))
+	// cond:
+	// result: (SRA x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMFlagLT_ULT {
+			break
+		}
+		v.reset(OpARMSRA)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SRAcond x _ (FlagLT_UGT))
+	// cond:
+	// result: (SRAconst x [31])
+	for {
+		x := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMFlagLT_UGT {
+			break
+		}
+		v.reset(OpARMSRAconst)
+		v.AuxInt = 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRAcond x y (FlagGT_ULT))
+	// cond:
+	// result: (SRA x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMFlagGT_ULT {
+			break
+		}
+		v.reset(OpARMSRA)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SRAcond x _ (FlagGT_UGT))
+	// cond:
+	// result: (SRAconst x [31])
+	for {
+		x := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMFlagGT_UGT {
+			break
+		}
+		v.reset(OpARMSRAconst)
+		v.AuxInt = 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSRAconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRAconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(int32(d)>>uint64(c))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(int32(d) >> uint64(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRL x (MOVWconst [c]))
+	// cond:
+	// result: (SRLconst x [c&31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSRLconst)
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSRLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRLconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(uint32(d)>>uint64(c))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(uint32(d) >> uint64(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUB (MOVWconst [c]) x)
+	// cond:
+	// result: (RSBconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUB x (MOVWconst [c]))
+	// cond:
+	// result: (SUBconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSUBconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUB x (SLLconst [c] y))
+	// cond:
+	// result: (SUBshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMSUBshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUB (SLLconst [c] y) x)
+	// cond:
+	// result: (RSBshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMRSBshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUB x (SRLconst [c] y))
+	// cond:
+	// result: (SUBshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMSUBshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUB (SRLconst [c] y) x)
+	// cond:
+	// result: (RSBshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMRSBshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUB x (SRAconst [c] y))
+	// cond:
+	// result: (SUBshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMSUBshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUB (SRAconst [c] y) x)
+	// cond:
+	// result: (RSBshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMRSBshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUB x (SLL y z))
+	// cond:
+	// result: (SUBshiftLLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMSUBshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUB (SLL y z) x)
+	// cond:
+	// result: (RSBshiftLLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMRSBshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUB x (SRL y z))
+	// cond:
+	// result: (SUBshiftRLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMSUBshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUB (SRL y z) x)
+	// cond:
+	// result: (RSBshiftRLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMRSBshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUB x (SRA y z))
+	// cond:
+	// result: (SUBshiftRAreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMSUBshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUB (SRA y z) x)
+	// cond:
+	// result: (RSBshiftRAreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMRSBshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUB x x)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBS(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBS (MOVWconst [c]) x)
+	// cond:
+	// result: (RSBSconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMRSBSconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBS x (MOVWconst [c]))
+	// cond:
+	// result: (SUBSconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSUBSconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBS x (SLLconst [c] y))
+	// cond:
+	// result: (SUBSshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMSUBSshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUBS (SLLconst [c] y) x)
+	// cond:
+	// result: (RSBSshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMRSBSshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUBS x (SRLconst [c] y))
+	// cond:
+	// result: (SUBSshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMSUBSshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUBS (SRLconst [c] y) x)
+	// cond:
+	// result: (RSBSshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMRSBSshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUBS x (SRAconst [c] y))
+	// cond:
+	// result: (SUBSshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMSUBSshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUBS (SRAconst [c] y) x)
+	// cond:
+	// result: (RSBSshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMRSBSshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUBS x (SLL y z))
+	// cond:
+	// result: (SUBSshiftLLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMSUBSshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUBS (SLL y z) x)
+	// cond:
+	// result: (RSBSshiftLLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMRSBSshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUBS x (SRL y z))
+	// cond:
+	// result: (SUBSshiftRLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMSUBSshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUBS (SRL y z) x)
+	// cond:
+	// result: (RSBSshiftRLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMRSBSshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUBS x (SRA y z))
+	// cond:
+	// result: (SUBSshiftRAreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMSUBSshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (SUBS (SRA y z) x)
+	// cond:
+	// result: (RSBSshiftRAreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMRSBSshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBSshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBSshiftLL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (RSBSconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMRSBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBSshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (SUBSconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSUBSconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBSshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBSshiftLLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (RSBSconst [c] (SLL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMRSBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBSshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (SUBSshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMSUBSshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBSshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBSshiftRA (MOVWconst [c]) x [d])
+	// cond:
+	// result: (RSBSconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMRSBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBSshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (SUBSconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSUBSconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBSshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBSshiftRAreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (RSBSconst [c] (SRA <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMRSBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBSshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (SUBSshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMSUBSshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBSshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBSshiftRL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (RSBSconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMRSBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBSshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (SUBSconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSUBSconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBSshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBSshiftRLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (RSBSconst [c] (SRL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMRSBSconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBSshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (SUBSshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMSUBSshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(int32(d-c))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(int32(d - c))
+		return true
+	}
+	// match: (SUBconst [c] (SUBconst [d] x))
+	// cond:
+	// result: (ADDconst [int64(int32(-c-d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSUBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMADDconst)
+		v.AuxInt = int64(int32(-c - d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBconst [c] (ADDconst [d] x))
+	// cond:
+	// result: (ADDconst [int64(int32(-c+d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMADDconst)
+		v.AuxInt = int64(int32(-c + d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBconst [c] (RSBconst [d] x))
+	// cond:
+	// result: (RSBconst [int64(int32(-c+d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMRSBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = int64(int32(-c + d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBshiftLL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (RSBconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (SUBconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSUBconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBshiftLL x (SLLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBshiftLLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (RSBconst [c] (SLL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (SUBshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMSUBshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBshiftRA (MOVWconst [c]) x [d])
+	// cond:
+	// result: (RSBconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (SUBconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSUBconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBshiftRA x (SRAconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBshiftRAreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (RSBconst [c] (SRA <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (SUBshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMSUBshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBshiftRL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (RSBconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (SUBconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMSUBconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBshiftRL x (SRLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMSUBshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBshiftRLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (RSBconst [c] (SRL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (SUBshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMSUBshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMXOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XOR (MOVWconst [c]) x)
+	// cond:
+	// result: (XORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMXORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR x (MOVWconst [c]))
+	// cond:
+	// result: (XORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMXORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR x (SLLconst [c] y))
+	// cond:
+	// result: (XORshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMXORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR (SLLconst [c] y) x)
+	// cond:
+	// result: (XORshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMXORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR x (SRLconst [c] y))
+	// cond:
+	// result: (XORshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMXORshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR (SRLconst [c] y) x)
+	// cond:
+	// result: (XORshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMXORshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR x (SRAconst [c] y))
+	// cond:
+	// result: (XORshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMXORshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR (SRAconst [c] y) x)
+	// cond:
+	// result: (XORshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMXORshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR x (SRRconst [c] y))
+	// cond:
+	// result: (XORshiftRR x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRRconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARMXORshiftRR)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR (SRRconst [c] y) x)
+	// cond:
+	// result: (XORshiftRR x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRRconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARMXORshiftRR)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR x (SLL y z))
+	// cond:
+	// result: (XORshiftLLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMXORshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (XOR (SLL y z) x)
+	// cond:
+	// result: (XORshiftLLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSLL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMXORshiftLLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (XOR x (SRL y z))
+	// cond:
+	// result: (XORshiftRLreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRL {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMXORshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (XOR (SRL y z) x)
+	// cond:
+	// result: (XORshiftRLreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRL {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMXORshiftRLreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (XOR x (SRA y z))
+	// cond:
+	// result: (XORshiftRAreg x y z)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRA {
+			break
+		}
+		y := v_1.Args[0]
+		z := v_1.Args[1]
+		v.reset(OpARMXORshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (XOR (SRA y z) x)
+	// cond:
+	// result: (XORshiftRAreg x y z)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMSRA {
+			break
+		}
+		y := v_0.Args[0]
+		z := v_0.Args[1]
+		x := v.Args[1]
+		v.reset(OpARMXORshiftRAreg)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(z)
+		return true
+	}
+	// match: (XOR x x)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMXORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [c^d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	// match: (XORconst [c] (XORconst [d] x))
+	// cond:
+	// result: (XORconst [c^d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMXORconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARMXORconst)
+		v.AuxInt = c ^ d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMXORshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORshiftLL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (XORconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMXORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftLL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (XORconst x [int64(uint32(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMXORconst)
+		v.AuxInt = int64(uint32(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORshiftLL x (SLLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMXORshiftLLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORshiftLLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (XORconst [c] (SLL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMXORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftLLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (XORshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMXORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMXORshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORshiftRA (MOVWconst [c]) x [d])
+	// cond:
+	// result: (XORconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMXORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftRA x (MOVWconst [c]) [d])
+	// cond:
+	// result: (XORconst x [int64(int32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMXORconst)
+		v.AuxInt = int64(int32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORshiftRA x (SRAconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMXORshiftRAreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORshiftRAreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (XORconst [c] (SRA <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMXORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRA, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftRAreg x y (MOVWconst [c]))
+	// cond:
+	// result: (XORshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMXORshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMXORshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORshiftRL (MOVWconst [c]) x [d])
+	// cond:
+	// result: (XORconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMXORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftRL x (MOVWconst [c]) [d])
+	// cond:
+	// result: (XORconst x [int64(uint32(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMXORconst)
+		v.AuxInt = int64(uint32(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORshiftRL x (SRLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVWconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMSRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMXORshiftRLreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORshiftRLreg (MOVWconst [c]) x y)
+	// cond:
+	// result: (XORconst [c] (SRL <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		y := v.Args[2]
+		v.reset(OpARMXORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftRLreg x y (MOVWconst [c]))
+	// cond:
+	// result: (XORshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		v.reset(OpARMXORshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpARMXORshiftRR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORshiftRR (MOVWconst [c]) x [d])
+	// cond:
+	// result: (XORconst [c] (SRRconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARMXORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARMSRRconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftRR x (MOVWconst [c]) [d])
+	// cond:
+	// result: (XORconst x [int64(uint32(c)>>uint64(d)|uint32(c)<<uint64(32-d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARMXORconst)
+		v.AuxInt = int64(uint32(c)>>uint64(d) | uint32(c)<<uint64(32-d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpAdd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add16 x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32 x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpAdd32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32F x y)
+	// cond:
+	// result: (ADDF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMADDF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpAdd32carry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32carry x y)
+	// cond:
+	// result: (ADDS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMADDS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpAdd32withcarry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32withcarry x y c)
+	// cond:
+	// result: (ADC x y c)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		c := v.Args[2]
+		v.reset(OpARMADC)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(c)
+		return true
+	}
+}
+func rewriteValueARM_OpAdd64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64F x y)
+	// cond:
+	// result: (ADDD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMADDD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpAdd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add8 x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpAddPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AddPtr x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpAddr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Addr {sym} base)
+	// cond:
+	// result: (MOVWaddr {sym} base)
+	for {
+		sym := v.Aux
+		base := v.Args[0]
+		v.reset(OpARMMOVWaddr)
+		v.Aux = sym
+		v.AddArg(base)
+		return true
+	}
+}
+func rewriteValueARM_OpAnd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And16 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMAND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpAnd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And32 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMAND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And8 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMAND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpAndB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AndB x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMAND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpBswap32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Bswap32 <t> x)
+	// cond:
+	// result: (XOR <t> 		(SRLconst <t> (BICconst <t> (XOR <t> x (SRRconst <t> [16] x)) [0xff0000]) [8]) 		(SRRconst <t> x [8]))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpARMXOR)
+		v.Type = t
+		v0 := b.NewValue0(v.Line, OpARMSRLconst, t)
+		v0.AuxInt = 8
+		v1 := b.NewValue0(v.Line, OpARMBICconst, t)
+		v1.AuxInt = 0xff0000
+		v2 := b.NewValue0(v.Line, OpARMXOR, t)
+		v2.AddArg(x)
+		v3 := b.NewValue0(v.Line, OpARMSRRconst, t)
+		v3.AuxInt = 16
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpARMSRRconst, t)
+		v4.AuxInt = 8
+		v4.AddArg(x)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueARM_OpClosureCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ClosureCall [argwid] entry closure mem)
+	// cond:
+	// result: (CALLclosure [argwid] entry closure mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		closure := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMCALLclosure)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(closure)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM_OpCom16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com16 x)
+	// cond:
+	// result: (MVN x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMVN)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCom32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com32 x)
+	// cond:
+	// result: (MVN x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMVN)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCom8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com8 x)
+	// cond:
+	// result: (MVN x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMVN)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpConst16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const16 [val])
+	// cond:
+	// result: (MOVWconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM_OpConst32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32 [val])
+	// cond:
+	// result: (MOVWconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM_OpConst32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32F [val])
+	// cond:
+	// result: (MOVFconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARMMOVFconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM_OpConst64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64F [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARMMOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM_OpConst8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const8 [val])
+	// cond:
+	// result: (MOVWconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM_OpConstBool(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstBool [b])
+	// cond:
+	// result: (MOVWconst [b])
+	for {
+		b := v.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = b
+		return true
+	}
+}
+func rewriteValueARM_OpConstNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstNil)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+}
+func rewriteValueARM_OpConvert(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Convert x mem)
+	// cond:
+	// result: (MOVWconvert x mem)
+	for {
+		x := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARMMOVWconvert)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM_OpCtz32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Ctz32 <t> x)
+	// cond:
+	// result: (RSBconst [32] (CLZ <t> (SUBconst <t> (AND <t> x (RSBconst <t> [0] x)) [1])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = 32
+		v0 := b.NewValue0(v.Line, OpARMCLZ, t)
+		v1 := b.NewValue0(v.Line, OpARMSUBconst, t)
+		v1.AuxInt = 1
+		v2 := b.NewValue0(v.Line, OpARMAND, t)
+		v2.AddArg(x)
+		v3 := b.NewValue0(v.Line, OpARMRSBconst, t)
+		v3.AuxInt = 0
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpCvt32Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto32 x)
+	// cond:
+	// result: (MOVFW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVFW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCvt32Fto32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto32U x)
+	// cond:
+	// result: (MOVFWU x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVFWU)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCvt32Fto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64F x)
+	// cond:
+	// result: (MOVFD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVFD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCvt32Uto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Uto32F x)
+	// cond:
+	// result: (MOVWUF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVWUF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCvt32Uto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Uto64F x)
+	// cond:
+	// result: (MOVWUD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVWUD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCvt32to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to32F x)
+	// cond:
+	// result: (MOVWF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVWF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCvt32to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to64F x)
+	// cond:
+	// result: (MOVWD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVWD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCvt64Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32 x)
+	// cond:
+	// result: (MOVDW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVDW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCvt64Fto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32F x)
+	// cond:
+	// result: (MOVDF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVDF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpCvt64Fto32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32U x)
+	// cond:
+	// result: (MOVDWU x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVDWU)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpDeferCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (DeferCall [argwid] mem)
+	// cond:
+	// result: (CALLdefer [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpARMCALLdefer)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM_OpDiv16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16 x y)
+	// cond:
+	// result: (Div32 (SignExt16to32 x) (SignExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpDiv32)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpDiv16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16u x y)
+	// cond:
+	// result: (Div32u (ZeroExt16to32 x) (ZeroExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpDiv32u)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpDiv32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32 x y)
+	// cond:
+	// result: (SUB (XOR <config.fe.TypeUInt32()> 		(Select0 <config.fe.TypeUInt32()> (UDIVrtcall 			(SUB <config.fe.TypeUInt32()> (XOR x <config.fe.TypeUInt32()> (Signmask x)) (Signmask x)) 			(SUB <config.fe.TypeUInt32()> (XOR y <config.fe.TypeUInt32()> (Signmask y)) (Signmask y)))) 		(Signmask (XOR <config.fe.TypeUInt32()> x y))) (Signmask (XOR <config.fe.TypeUInt32()> x y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSUB)
+		v0 := b.NewValue0(v.Line, OpARMXOR, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpSelect0, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpARMUDIVrtcall, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v3 := b.NewValue0(v.Line, OpARMSUB, config.fe.TypeUInt32())
+		v4 := b.NewValue0(v.Line, OpARMXOR, config.fe.TypeUInt32())
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v3.AddArg(v4)
+		v6 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v6.AddArg(x)
+		v3.AddArg(v6)
+		v2.AddArg(v3)
+		v7 := b.NewValue0(v.Line, OpARMSUB, config.fe.TypeUInt32())
+		v8 := b.NewValue0(v.Line, OpARMXOR, config.fe.TypeUInt32())
+		v8.AddArg(y)
+		v9 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v9.AddArg(y)
+		v8.AddArg(v9)
+		v7.AddArg(v8)
+		v10 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v10.AddArg(y)
+		v7.AddArg(v10)
+		v2.AddArg(v7)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v11 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v12 := b.NewValue0(v.Line, OpARMXOR, config.fe.TypeUInt32())
+		v12.AddArg(x)
+		v12.AddArg(y)
+		v11.AddArg(v12)
+		v0.AddArg(v11)
+		v.AddArg(v0)
+		v13 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v14 := b.NewValue0(v.Line, OpARMXOR, config.fe.TypeUInt32())
+		v14.AddArg(x)
+		v14.AddArg(y)
+		v13.AddArg(v14)
+		v.AddArg(v13)
+		return true
+	}
+}
+func rewriteValueARM_OpDiv32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32F x y)
+	// cond:
+	// result: (DIVF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMDIVF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpDiv32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32u x y)
+	// cond:
+	// result: (Select0 <config.fe.TypeUInt32()> (UDIVrtcall x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v.Type = config.fe.TypeUInt32()
+		v0 := b.NewValue0(v.Line, OpARMUDIVrtcall, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpDiv64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64F x y)
+	// cond:
+	// result: (DIVD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMDIVD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpDiv8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8 x y)
+	// cond:
+	// result: (Div32 (SignExt8to32 x) (SignExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpDiv32)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpDiv8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8u x y)
+	// cond:
+	// result: (Div32u (ZeroExt8to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpDiv32u)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpEq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq16 x y)
+	// cond:
+	// result: (Equal (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpEq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32 x y)
+	// cond:
+	// result: (Equal (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpEq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32F x y)
+	// cond:
+	// result: (Equal (CMPF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMPF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpEq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64F x y)
+	// cond:
+	// result: (Equal (CMPD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMPD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpEq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq8 x y)
+	// cond:
+	// result: (Equal (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpEqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqB x y)
+	// cond:
+	// result: (XORconst [1] (XOR <config.fe.TypeBool()> x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpARMXOR, config.fe.TypeBool())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpEqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqPtr x y)
+	// cond:
+	// result: (Equal (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16 x y)
+	// cond:
+	// result: (GreaterEqual (CMP (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16U x y)
+	// cond:
+	// result: (GreaterEqualU (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterEqualU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32 x y)
+	// cond:
+	// result: (GreaterEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32F x y)
+	// cond:
+	// result: (GreaterEqual (CMPF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMPF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32U x y)
+	// cond:
+	// result: (GreaterEqualU (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterEqualU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64F x y)
+	// cond:
+	// result: (GreaterEqual (CMPD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMPD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8 x y)
+	// cond:
+	// result: (GreaterEqual (CMP (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8U x y)
+	// cond:
+	// result: (GreaterEqualU (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterEqualU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGetClosurePtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetClosurePtr)
+	// cond:
+	// result: (LoweredGetClosurePtr)
+	for {
+		v.reset(OpARMLoweredGetClosurePtr)
+		return true
+	}
+}
+func rewriteValueARM_OpGoCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GoCall [argwid] mem)
+	// cond:
+	// result: (CALLgo [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpARMCALLgo)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM_OpGreater16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16 x y)
+	// cond:
+	// result: (GreaterThan (CMP (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterThan)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGreater16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16U x y)
+	// cond:
+	// result: (GreaterThanU (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterThanU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGreater32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32 x y)
+	// cond:
+	// result: (GreaterThan (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterThan)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGreater32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32F x y)
+	// cond:
+	// result: (GreaterThan (CMPF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterThan)
+		v0 := b.NewValue0(v.Line, OpARMCMPF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGreater32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32U x y)
+	// cond:
+	// result: (GreaterThanU (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterThanU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGreater64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64F x y)
+	// cond:
+	// result: (GreaterThan (CMPD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterThan)
+		v0 := b.NewValue0(v.Line, OpARMCMPD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGreater8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8 x y)
+	// cond:
+	// result: (GreaterThan (CMP (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterThan)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpGreater8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8U x y)
+	// cond:
+	// result: (GreaterThanU (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterThanU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpHmul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16 x y)
+	// cond:
+	// result: (SRAconst (MUL <config.fe.TypeInt32()> (SignExt16to32 x) (SignExt16to32 y)) [16])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRAconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpARMMUL, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpHmul16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16u x y)
+	// cond:
+	// result: (SRLconst (MUL <config.fe.TypeUInt32()> (ZeroExt16to32 x) (ZeroExt16to32 y)) [16])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRLconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpARMMUL, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpHmul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32 x y)
+	// cond:
+	// result: (HMUL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMHMUL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpHmul32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32u x y)
+	// cond:
+	// result: (HMULU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMHMULU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpHmul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8 x y)
+	// cond:
+	// result: (SRAconst (MUL <config.fe.TypeInt16()> (SignExt8to32 x) (SignExt8to32 y)) [8])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRAconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpARMMUL, config.fe.TypeInt16())
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpHmul8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8u x y)
+	// cond:
+	// result: (SRLconst (MUL <config.fe.TypeUInt16()> (ZeroExt8to32 x) (ZeroExt8to32 y)) [8])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRLconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpARMMUL, config.fe.TypeUInt16())
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpInterCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (InterCall [argwid] entry mem)
+	// cond:
+	// result: (CALLinter [argwid] entry mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARMCALLinter)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM_OpIsInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsInBounds idx len)
+	// cond:
+	// result: (LessThanU (CMP idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpARMLessThanU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpIsNonNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsNonNil ptr)
+	// cond:
+	// result: (NotEqual (CMPconst [0] ptr))
+	for {
+		ptr := v.Args[0]
+		v.reset(OpARMNotEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v0.AuxInt = 0
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpIsSliceInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsSliceInBounds idx len)
+	// cond:
+	// result: (LessEqualU (CMP idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpARMLessEqualU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16 x y)
+	// cond:
+	// result: (LessEqual (CMP (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16U x y)
+	// cond:
+	// result: (LessEqualU (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessEqualU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32 x y)
+	// cond:
+	// result: (LessEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32F x y)
+	// cond:
+	// result: (GreaterEqual (CMPF y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMPF, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32U x y)
+	// cond:
+	// result: (LessEqualU (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessEqualU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64F x y)
+	// cond:
+	// result: (GreaterEqual (CMPD y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMPD, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8 x y)
+	// cond:
+	// result: (LessEqual (CMP (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8U x y)
+	// cond:
+	// result: (LessEqualU (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessEqualU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLess16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16 x y)
+	// cond:
+	// result: (LessThan (CMP (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessThan)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLess16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16U x y)
+	// cond:
+	// result: (LessThanU (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessThanU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLess32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32 x y)
+	// cond:
+	// result: (LessThan (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessThan)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLess32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32F x y)
+	// cond:
+	// result: (GreaterThan (CMPF y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterThan)
+		v0 := b.NewValue0(v.Line, OpARMCMPF, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLess32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32U x y)
+	// cond:
+	// result: (LessThanU (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessThanU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLess64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64F x y)
+	// cond:
+	// result: (GreaterThan (CMPD y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMGreaterThan)
+		v0 := b.NewValue0(v.Line, OpARMCMPD, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLess8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8 x y)
+	// cond:
+	// result: (LessThan (CMP (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessThan)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLess8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8U x y)
+	// cond:
+	// result: (LessThanU (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMLessThanU)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLoad(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Load <t> ptr mem)
+	// cond: t.IsBoolean()
+	// result: (MOVBUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(t.IsBoolean()) {
+			break
+		}
+		v.reset(OpARMMOVBUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is8BitInt(t) && isSigned(t))
+	// result: (MOVBload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is8BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpARMMOVBload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is8BitInt(t) && !isSigned(t))
+	// result: (MOVBUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is8BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpARMMOVBUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is16BitInt(t) && isSigned(t))
+	// result: (MOVHload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpARMMOVHload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is16BitInt(t) && !isSigned(t))
+	// result: (MOVHUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpARMMOVHUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is32BitInt(t) || isPtr(t))
+	// result: (MOVWload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t) || isPtr(t)) {
+			break
+		}
+		v.reset(OpARMMOVWload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitFloat(t)
+	// result: (MOVFload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitFloat(t)) {
+			break
+		}
+		v.reset(OpARMMOVFload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitFloat(t)
+	// result: (MOVDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitFloat(t)) {
+			break
+		}
+		v.reset(OpARMMOVDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpLrot16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot16 <t> x [c])
+	// cond:
+	// result: (OR (SLLconst <t> x [c&15]) (SRLconst <t> x [16-c&15]))
+	for {
+		t := v.Type
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpARMOR)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, t)
+		v0.AuxInt = c & 15
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMSRLconst, t)
+		v1.AuxInt = 16 - c&15
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpLrot32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot32 x [c])
+	// cond:
+	// result: (SRRconst x [32-c&31])
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpARMSRRconst)
+		v.AuxInt = 32 - c&31
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpLrot8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot8 <t> x [c])
+	// cond:
+	// result: (OR (SLLconst <t> x [c&7]) (SRLconst <t> x [8-c&7]))
+	for {
+		t := v.Type
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpARMOR)
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, t)
+		v0.AuxInt = c & 7
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMSRLconst, t)
+		v1.AuxInt = 8 - c&7
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpLsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x16 x y)
+	// cond:
+	// result: (CMOVWHSconst (SLL <x.Type> x (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v2.AuxInt = 256
+		v3 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM_OpLsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x32 x y)
+	// cond:
+	// result: (CMOVWHSconst (SLL <x.Type> x y) (CMPconst [256] y) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v1.AuxInt = 256
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpLsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x64 x (Const64 [c]))
+	// cond: uint64(c) < 16
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh16x64 _ (Const64 [c]))
+	// cond: uint64(c) >= 16
+	// result: (Const16 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpConst16)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpLsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x8  x y)
+	// cond:
+	// result: (SLL x (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSLL)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x16 x y)
+	// cond:
+	// result: (CMOVWHSconst (SLL <x.Type> x (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v2.AuxInt = 256
+		v3 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM_OpLsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x32 x y)
+	// cond:
+	// result: (CMOVWHSconst (SLL <x.Type> x y) (CMPconst [256] y) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v1.AuxInt = 256
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpLsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x64 x (Const64 [c]))
+	// cond: uint64(c) < 32
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh32x64 _ (Const64 [c]))
+	// cond: uint64(c) >= 32
+	// result: (Const32 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpLsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x8  x y)
+	// cond:
+	// result: (SLL x (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSLL)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpLsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x16 x y)
+	// cond:
+	// result: (CMOVWHSconst (SLL <x.Type> x (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v2.AuxInt = 256
+		v3 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM_OpLsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x32 x y)
+	// cond:
+	// result: (CMOVWHSconst (SLL <x.Type> x y) (CMPconst [256] y) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSLL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v1.AuxInt = 256
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpLsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x64 x (Const64 [c]))
+	// cond: uint64(c) < 8
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpARMSLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh8x64 _ (Const64 [c]))
+	// cond: uint64(c) >= 8
+	// result: (Const8 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpConst8)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpLsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x8  x y)
+	// cond:
+	// result: (SLL x (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSLL)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpMod16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16 x y)
+	// cond:
+	// result: (Mod32 (SignExt16to32 x) (SignExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMod32)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpMod16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16u x y)
+	// cond:
+	// result: (Mod32u (ZeroExt16to32 x) (ZeroExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMod32u)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpMod32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32 x y)
+	// cond:
+	// result: (SUB (XOR <config.fe.TypeUInt32()> 		(Select1 <config.fe.TypeUInt32()> (UDIVrtcall 			(SUB <config.fe.TypeUInt32()> (XOR <config.fe.TypeUInt32()> x (Signmask x)) (Signmask x)) 			(SUB <config.fe.TypeUInt32()> (XOR <config.fe.TypeUInt32()> y (Signmask y)) (Signmask y)))) 		(Signmask x)) (Signmask x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSUB)
+		v0 := b.NewValue0(v.Line, OpARMXOR, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpSelect1, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpARMUDIVrtcall, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v3 := b.NewValue0(v.Line, OpARMSUB, config.fe.TypeUInt32())
+		v4 := b.NewValue0(v.Line, OpARMXOR, config.fe.TypeUInt32())
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v3.AddArg(v4)
+		v6 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v6.AddArg(x)
+		v3.AddArg(v6)
+		v2.AddArg(v3)
+		v7 := b.NewValue0(v.Line, OpARMSUB, config.fe.TypeUInt32())
+		v8 := b.NewValue0(v.Line, OpARMXOR, config.fe.TypeUInt32())
+		v8.AddArg(y)
+		v9 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v9.AddArg(y)
+		v8.AddArg(v9)
+		v7.AddArg(v8)
+		v10 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v10.AddArg(y)
+		v7.AddArg(v10)
+		v2.AddArg(v7)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v11 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v11.AddArg(x)
+		v0.AddArg(v11)
+		v.AddArg(v0)
+		v12 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v12.AddArg(x)
+		v.AddArg(v12)
+		return true
+	}
+}
+func rewriteValueARM_OpMod32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32u x y)
+	// cond:
+	// result: (Select1 <config.fe.TypeUInt32()> (UDIVrtcall x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v.Type = config.fe.TypeUInt32()
+		v0 := b.NewValue0(v.Line, OpARMUDIVrtcall, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpMod8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8 x y)
+	// cond:
+	// result: (Mod32 (SignExt8to32 x) (SignExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMod32)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpMod8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8u x y)
+	// cond:
+	// result: (Mod32u (ZeroExt8to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMod32u)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpMove(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Move [s] _ _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore dst (MOVBUload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpARMMOVBstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARMMOVBUload, config.fe.TypeUInt8())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore dst (MOVHUload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpARMMOVHstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARMMOVHUload, config.fe.TypeUInt16())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVBstore [1] dst (MOVBUload [1] src mem) 		(MOVBstore dst (MOVBUload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = 1
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARMMOVBUload, config.fe.TypeUInt8())
+		v0.AuxInt = 1
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARMMOVBUload, config.fe.TypeUInt8())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore dst (MOVWload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpARMMOVWstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARMMOVWload, config.fe.TypeUInt32())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [2] dst (MOVHUload [2] src mem) 		(MOVHstore dst (MOVHUload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpARMMOVHstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARMMOVHUload, config.fe.TypeUInt16())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMMOVHstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARMMOVHUload, config.fe.TypeUInt16())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVBstore [3] dst (MOVBUload [3] src mem) 		(MOVBstore [2] dst (MOVBUload [2] src mem) 			(MOVBstore [1] dst (MOVBUload [1] src mem) 				(MOVBstore dst (MOVBUload src mem) mem))))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = 3
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARMMOVBUload, config.fe.TypeUInt8())
+		v0.AuxInt = 3
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARMMOVBUload, config.fe.TypeUInt8())
+		v2.AuxInt = 2
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v3.AuxInt = 1
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpARMMOVBUload, config.fe.TypeUInt8())
+		v4.AuxInt = 1
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v5.AddArg(dst)
+		v6 := b.NewValue0(v.Line, OpARMMOVBUload, config.fe.TypeUInt8())
+		v6.AddArg(src)
+		v6.AddArg(mem)
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] dst (MOVBUload [2] src mem) 		(MOVBstore [1] dst (MOVBUload [1] src mem) 			(MOVBstore dst (MOVBUload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARMMOVBUload, config.fe.TypeUInt8())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v1.AuxInt = 1
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARMMOVBUload, config.fe.TypeUInt8())
+		v2.AuxInt = 1
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpARMMOVBUload, config.fe.TypeUInt8())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size()%4 == 0 && SizeAndAlign(s).Size() > 4 && SizeAndAlign(s).Size() <= 512 	&& SizeAndAlign(s).Align()%4 == 0 && !config.noDuffDevice
+	// result: (DUFFCOPY [8 * (128 - int64(SizeAndAlign(s).Size()/4))] dst src mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size()%4 == 0 && SizeAndAlign(s).Size() > 4 && SizeAndAlign(s).Size() <= 512 && SizeAndAlign(s).Align()%4 == 0 && !config.noDuffDevice) {
+			break
+		}
+		v.reset(OpARMDUFFCOPY)
+		v.AuxInt = 8 * (128 - int64(SizeAndAlign(s).Size()/4))
+		v.AddArg(dst)
+		v.AddArg(src)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: (SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%4 != 0
+	// result: (LoweredMove [SizeAndAlign(s).Align()] 		dst 		src 		(ADDconst <src.Type> src [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)]) 		mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !((SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%4 != 0) {
+			break
+		}
+		v.reset(OpARMLoweredMove)
+		v.AuxInt = SizeAndAlign(s).Align()
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, OpARMADDconst, src.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - moveSize(SizeAndAlign(s).Align(), config)
+		v0.AddArg(src)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpMul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul16 x y)
+	// cond:
+	// result: (MUL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMMUL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpMul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32 x y)
+	// cond:
+	// result: (MUL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMMUL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpMul32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32F x y)
+	// cond:
+	// result: (MULF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMMULF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpMul32uhilo(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32uhilo x y)
+	// cond:
+	// result: (MULLU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMMULLU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpMul64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64F x y)
+	// cond:
+	// result: (MULD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMMULD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpMul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul8 x y)
+	// cond:
+	// result: (MUL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMMUL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpNeg16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg16 x)
+	// cond:
+	// result: (RSBconst [0] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = 0
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpNeg32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32 x)
+	// cond:
+	// result: (RSBconst [0] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = 0
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpNeg32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32F x)
+	// cond:
+	// result: (NEGF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMNEGF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpNeg64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64F x)
+	// cond:
+	// result: (NEGD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMNEGD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpNeg8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg8 x)
+	// cond:
+	// result: (RSBconst [0] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMRSBconst)
+		v.AuxInt = 0
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpNeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq16 x y)
+	// cond:
+	// result: (NotEqual (CMP (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMNotEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpNeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32 x y)
+	// cond:
+	// result: (NotEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMNotEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpNeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32F x y)
+	// cond:
+	// result: (NotEqual (CMPF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMNotEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMPF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpNeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64F x y)
+	// cond:
+	// result: (NotEqual (CMPD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMNotEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMPD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpNeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq8 x y)
+	// cond:
+	// result: (NotEqual (CMP (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMNotEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpNeqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqB x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMXOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpNeqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqPtr x y)
+	// cond:
+	// result: (NotEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMNotEqual)
+		v0 := b.NewValue0(v.Line, OpARMCMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpNilCheck(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NilCheck ptr mem)
+	// cond:
+	// result: (LoweredNilCheck ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARMLoweredNilCheck)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM_OpNot(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Not x)
+	// cond:
+	// result: (XORconst [1] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMXORconst)
+		v.AuxInt = 1
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpOffPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OffPtr [off] ptr:(SP))
+	// cond:
+	// result: (MOVWaddr [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		if ptr.Op != OpSP {
+			break
+		}
+		v.reset(OpARMMOVWaddr)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+	// match: (OffPtr [off] ptr)
+	// cond:
+	// result: (ADDconst [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		v.reset(OpARMADDconst)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+}
+func rewriteValueARM_OpOr16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or16 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpOr32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or32 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or8 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpOrB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OrB x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh16Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux16 x y)
+	// cond:
+	// result: (CMOVWHSconst (SRL <x.Type> (ZeroExt16to32 x) (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v3.AuxInt = 256
+		v4 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh16Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux32 x y)
+	// cond:
+	// result: (CMOVWHSconst (SRL <x.Type> (ZeroExt16to32 x) y) (CMPconst [256] y) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v2.AuxInt = 256
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh16Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux64 x (Const64 [c]))
+	// cond: uint64(c) < 16
+	// result: (SRLconst (SLLconst <config.fe.TypeUInt32()> x [16]) [c+16])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpARMSRLconst)
+		v.AuxInt = c + 16
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 16
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16Ux64 _ (Const64 [c]))
+	// cond: uint64(c) >= 16
+	// result: (Const16 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpConst16)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpRsh16Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux8  x y)
+	// cond:
+	// result: (SRL (ZeroExt16to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRL)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x16 x y)
+	// cond:
+	// result: (SRAcond (SignExt16to32 x) (ZeroExt16to32 y) (CMPconst [256] (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRAcond)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v2.AuxInt = 256
+		v3 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x32 x y)
+	// cond:
+	// result: (SRAcond (SignExt16to32 x) y (CMPconst [256] y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRAcond)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v1.AuxInt = 256
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x64 x (Const64 [c]))
+	// cond: uint64(c) < 16
+	// result: (SRAconst (SLLconst <config.fe.TypeUInt32()> x [16]) [c+16])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpARMSRAconst)
+		v.AuxInt = c + 16
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 16
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16x64 x (Const64 [c]))
+	// cond: uint64(c) >= 16
+	// result: (SRAconst (SLLconst <config.fe.TypeUInt32()> x [16]) [31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpARMSRAconst)
+		v.AuxInt = 31
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 16
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpRsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x8  x y)
+	// cond:
+	// result: (SRA (SignExt16to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh32Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux16 x y)
+	// cond:
+	// result: (CMOVWHSconst (SRL <x.Type> x (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v2.AuxInt = 256
+		v3 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh32Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux32 x y)
+	// cond:
+	// result: (CMOVWHSconst (SRL <x.Type> x y) (CMPconst [256] y) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v1.AuxInt = 256
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh32Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux64 x (Const64 [c]))
+	// cond: uint64(c) < 32
+	// result: (SRLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpARMSRLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32Ux64 _ (Const64 [c]))
+	// cond: uint64(c) >= 32
+	// result: (Const32 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpRsh32Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux8  x y)
+	// cond:
+	// result: (SRL x (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRL)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x16 x y)
+	// cond:
+	// result: (SRAcond x (ZeroExt16to32 y) (CMPconst [256] (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRAcond)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v1.AuxInt = 256
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x32 x y)
+	// cond:
+	// result: (SRAcond x y (CMPconst [256] y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRAcond)
+		v.AddArg(x)
+		v.AddArg(y)
+		v0 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v0.AuxInt = 256
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x64 x (Const64 [c]))
+	// cond: uint64(c) < 32
+	// result: (SRAconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpARMSRAconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32x64 x (Const64 [c]))
+	// cond: uint64(c) >= 32
+	// result: (SRAconst x [31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpARMSRAconst)
+		v.AuxInt = 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpRsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x8  x y)
+	// cond:
+	// result: (SRA x (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRA)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh8Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux16 x y)
+	// cond:
+	// result: (CMOVWHSconst (SRL <x.Type> (ZeroExt8to32 x) (ZeroExt16to32 y)) (CMPconst [256] (ZeroExt16to32 y)) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v3.AuxInt = 256
+		v4 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh8Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux32 x y)
+	// cond:
+	// result: (CMOVWHSconst (SRL <x.Type> (ZeroExt8to32 x) y) (CMPconst [256] y) [0])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMCMOVWHSconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpARMSRL, x.Type)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v2.AuxInt = 256
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh8Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux64 x (Const64 [c]))
+	// cond: uint64(c) < 8
+	// result: (SRLconst (SLLconst <config.fe.TypeUInt32()> x [24]) [c+24])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpARMSRLconst)
+		v.AuxInt = c + 24
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 24
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8Ux64 _ (Const64 [c]))
+	// cond: uint64(c) >= 8
+	// result: (Const8 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpConst8)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpRsh8Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux8  x y)
+	// cond:
+	// result: (SRL (ZeroExt8to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRL)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x16 x y)
+	// cond:
+	// result: (SRAcond (SignExt8to32 x) (ZeroExt16to32 y) (CMPconst [256] (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRAcond)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v2.AuxInt = 256
+		v3 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x32 x y)
+	// cond:
+	// result: (SRAcond (SignExt8to32 x) y (CMPconst [256] y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRAcond)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+		v1.AuxInt = 256
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpRsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x64 x (Const64 [c]))
+	// cond: uint64(c) < 8
+	// result: (SRAconst (SLLconst <config.fe.TypeUInt32()> x [24]) [c+24])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpARMSRAconst)
+		v.AuxInt = c + 24
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 24
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8x64 x (Const64 [c]))
+	// cond: uint64(c) >= 8
+	// result: (SRAconst (SLLconst <config.fe.TypeUInt32()> x [24]) [31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpARMSRAconst)
+		v.AuxInt = 31
+		v0 := b.NewValue0(v.Line, OpARMSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 24
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpRsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x8  x y)
+	// cond:
+	// result: (SRA (SignExt8to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSRA)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM_OpSelect0(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Select0 (UDIVrtcall x (MOVWconst [1])))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMUDIVrtcall {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpARMMOVWconst {
+			break
+		}
+		if v_0_1.AuxInt != 1 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select0 (UDIVrtcall x (MOVWconst [c])))
+	// cond: isPowerOfTwo(c)
+	// result: (SRLconst [log2(c)] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMUDIVrtcall {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0_1.AuxInt
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARMSRLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select0 (UDIVrtcall (MOVWconst [c]) (MOVWconst [d])))
+	// cond:
+	// result: (MOVWconst [int64(uint32(c)/uint32(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMUDIVrtcall {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(uint32(c) / uint32(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpSelect1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Select1 (UDIVrtcall _ (MOVWconst [1])))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMUDIVrtcall {
+			break
+		}
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpARMMOVWconst {
+			break
+		}
+		if v_0_1.AuxInt != 1 {
+			break
+		}
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Select1 (UDIVrtcall x (MOVWconst [c])))
+	// cond: isPowerOfTwo(c)
+	// result: (ANDconst [c-1] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMUDIVrtcall {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0_1.AuxInt
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARMANDconst)
+		v.AuxInt = c - 1
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (UDIVrtcall (MOVWconst [c]) (MOVWconst [d])))
+	// cond:
+	// result: (MOVWconst [int64(uint32(c)%uint32(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARMUDIVrtcall {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpARMMOVWconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpARMMOVWconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpARMMOVWconst)
+		v.AuxInt = int64(uint32(c) % uint32(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpSignExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to32 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpSignExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to16 x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpSignExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to32 x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpSignmask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Signmask x)
+	// cond:
+	// result: (SRAconst x [31])
+	for {
+		x := v.Args[0]
+		v.reset(OpARMSRAconst)
+		v.AuxInt = 31
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpSlicemask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Slicemask <t> x)
+	// cond:
+	// result: (MVN (SRAconst <t> (SUBconst <t> x [1]) [31]))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpARMMVN)
+		v0 := b.NewValue0(v.Line, OpARMSRAconst, t)
+		v0.AuxInt = 31
+		v1 := b.NewValue0(v.Line, OpARMSUBconst, t)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM_OpSqrt(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sqrt x)
+	// cond:
+	// result: (SQRTD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMSQRTD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpStaticCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (StaticCall [argwid] {target} mem)
+	// cond:
+	// result: (CALLstatic [argwid] {target} mem)
+	for {
+		argwid := v.AuxInt
+		target := v.Aux
+		mem := v.Args[0]
+		v.reset(OpARMCALLstatic)
+		v.AuxInt = argwid
+		v.Aux = target
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM_OpStore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Store [1] ptr val mem)
+	// cond:
+	// result: (MOVBstore ptr val mem)
+	for {
+		if v.AuxInt != 1 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVBstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [2] ptr val mem)
+	// cond:
+	// result: (MOVHstore ptr val mem)
+	for {
+		if v.AuxInt != 2 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARMMOVHstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: !is32BitFloat(val.Type)
+	// result: (MOVWstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(!is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpARMMOVWstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: is32BitFloat(val.Type)
+	// result: (MOVFstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpARMMOVFstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [8] ptr val mem)
+	// cond: is64BitFloat(val.Type)
+	// result: (MOVDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is64BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpARMMOVDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpSub16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub16 x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpSub32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32 x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpSub32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32F x y)
+	// cond:
+	// result: (SUBF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSUBF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpSub32carry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32carry x y)
+	// cond:
+	// result: (SUBS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSUBS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpSub32withcarry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32withcarry x y c)
+	// cond:
+	// result: (SBC x y c)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		c := v.Args[2]
+		v.reset(OpARMSBC)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(c)
+		return true
+	}
+}
+func rewriteValueARM_OpSub64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64F x y)
+	// cond:
+	// result: (SUBD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSUBD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpSub8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub8 x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpSubPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SubPtr x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpTrunc16to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc16to8 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpTrunc32to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to16 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpTrunc32to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to8 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpXor16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor16 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMXOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpXor32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor32 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMXOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpXor8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor8 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARMXOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM_OpZero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zero [s] _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore ptr (MOVWconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpARMMOVBstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore ptr (MOVWconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpARMMOVHstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVBstore [1] ptr (MOVWconst [0]) 		(MOVBstore [0] ptr (MOVWconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = 1
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore ptr (MOVWconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpARMMOVWstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [2] ptr (MOVWconst [0]) 		(MOVHstore [0] ptr (MOVWconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpARMMOVHstore)
+		v.AuxInt = 2
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMMOVHstore, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVBstore [3] ptr (MOVWconst [0]) 		(MOVBstore [2] ptr (MOVWconst [0]) 			(MOVBstore [1] ptr (MOVWconst [0]) 				(MOVBstore [0] ptr (MOVWconst [0]) mem))))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = 3
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v3.AuxInt = 1
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v5.AuxInt = 0
+		v5.AddArg(ptr)
+		v6 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v6.AuxInt = 0
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] ptr (MOVWconst [0]) 		(MOVBstore [1] ptr (MOVWconst [0]) 			(MOVBstore [0] ptr (MOVWconst [0]) mem)))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpARMMOVBstore)
+		v.AuxInt = 2
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v1.AuxInt = 1
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARMMOVBstore, TypeMem)
+		v3.AuxInt = 0
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size()%4 == 0 && SizeAndAlign(s).Size() > 4 && SizeAndAlign(s).Size() <= 512 	&& SizeAndAlign(s).Align()%4 == 0 && !config.noDuffDevice
+	// result: (DUFFZERO [4 * (128 - int64(SizeAndAlign(s).Size()/4))] ptr (MOVWconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size()%4 == 0 && SizeAndAlign(s).Size() > 4 && SizeAndAlign(s).Size() <= 512 && SizeAndAlign(s).Align()%4 == 0 && !config.noDuffDevice) {
+			break
+		}
+		v.reset(OpARMDUFFZERO)
+		v.AuxInt = 4 * (128 - int64(SizeAndAlign(s).Size()/4))
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: (SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%4 != 0
+	// result: (LoweredZero [SizeAndAlign(s).Align()] 		ptr 		(ADDconst <ptr.Type> ptr [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)]) 		(MOVWconst [0]) 		mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !((SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%4 != 0) {
+			break
+		}
+		v.reset(OpARMLoweredZero)
+		v.AuxInt = SizeAndAlign(s).Align()
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARMADDconst, ptr.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - moveSize(SizeAndAlign(s).Align(), config)
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARMMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM_OpZeroExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to32 x)
+	// cond:
+	// result: (MOVHUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVHUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpZeroExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to16 x)
+	// cond:
+	// result: (MOVBUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVBUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpZeroExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to32 x)
+	// cond:
+	// result: (MOVBUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARMMOVBUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM_OpZeromask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zeromask x)
+	// cond:
+	// result: (SRAconst (RSBshiftRL <config.fe.TypeInt32()> x x [1]) [31])
+	for {
+		x := v.Args[0]
+		v.reset(OpARMSRAconst)
+		v.AuxInt = 31
+		v0 := b.NewValue0(v.Line, OpARMRSBshiftRL, config.fe.TypeInt32())
+		v0.AuxInt = 1
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteBlockARM(b *Block, config *Config) bool {
+	switch b.Kind {
+	case BlockARMEQ:
+		// match: (EQ (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (InvertFlags cmp) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMEQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARMGE:
+		// match: (GE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (LE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMLE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARMGT:
+		// match: (GT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (LT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMLT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockIf:
+		// match: (If (Equal cc) yes no)
+		// cond:
+		// result: (EQ cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMEQ
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (NotEqual cc) yes no)
+		// cond:
+		// result: (NE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMNotEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMNE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (LessThan cc) yes no)
+		// cond:
+		// result: (LT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMLessThan {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMLT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (LessThanU cc) yes no)
+		// cond:
+		// result: (ULT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMLessThanU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMULT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (LessEqual cc) yes no)
+		// cond:
+		// result: (LE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMLessEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMLE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (LessEqualU cc) yes no)
+		// cond:
+		// result: (ULE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMLessEqualU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMULE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (GreaterThan cc) yes no)
+		// cond:
+		// result: (GT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMGreaterThan {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMGT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (GreaterThanU cc) yes no)
+		// cond:
+		// result: (UGT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMGreaterThanU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMUGT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (GreaterEqual cc) yes no)
+		// cond:
+		// result: (GE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMGreaterEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMGE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (GreaterEqualU cc) yes no)
+		// cond:
+		// result: (UGE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMGreaterEqualU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMUGE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If cond yes no)
+		// cond:
+		// result: (NE (CMPconst [0] cond) yes no)
+		for {
+			v := b.Control
+			_ = v
+			cond := b.Control
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMNE
+			v0 := b.NewValue0(v.Line, OpARMCMPconst, TypeFlags)
+			v0.AuxInt = 0
+			v0.AddArg(cond)
+			b.SetControl(v0)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARMLE:
+		// match: (LE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (GE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARMLT:
+		// match: (LT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (GT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMGT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARMNE:
+		// match: (NE (CMPconst [0] (Equal cc)) yes no)
+		// cond:
+		// result: (EQ cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMCMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpARMEqual {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMEQ
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPconst [0] (NotEqual cc)) yes no)
+		// cond:
+		// result: (NE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMCMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpARMNotEqual {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMNE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPconst [0] (LessThan cc)) yes no)
+		// cond:
+		// result: (LT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMCMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpARMLessThan {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMLT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPconst [0] (LessThanU cc)) yes no)
+		// cond:
+		// result: (ULT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMCMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpARMLessThanU {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMULT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPconst [0] (LessEqual cc)) yes no)
+		// cond:
+		// result: (LE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMCMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpARMLessEqual {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMLE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPconst [0] (LessEqualU cc)) yes no)
+		// cond:
+		// result: (ULE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMCMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpARMLessEqualU {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMULE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPconst [0] (GreaterThan cc)) yes no)
+		// cond:
+		// result: (GT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMCMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpARMGreaterThan {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMGT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPconst [0] (GreaterThanU cc)) yes no)
+		// cond:
+		// result: (UGT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMCMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpARMGreaterThanU {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMUGT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPconst [0] (GreaterEqual cc)) yes no)
+		// cond:
+		// result: (GE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMCMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpARMGreaterEqual {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMGE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPconst [0] (GreaterEqualU cc)) yes no)
+		// cond:
+		// result: (UGE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMCMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpARMGreaterEqualU {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMUGE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (NE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMNE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARMUGE:
+		// match: (UGE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGE (FlagLT_ULT) yes no)
 		// cond:
-		// result: (LT cc yes no)
+		// result: (First nil no yes)
 		for {
 			v := b.Control
-			if v.Op != OpARMLessThan {
+			if v.Op != OpARMFlagLT_ULT {
 				break
 			}
-			cc := v.Args[0]
 			yes := b.Succs[0]
 			no := b.Succs[1]
-			b.Kind = BlockARMLT
-			b.SetControl(cc)
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (ULE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMULE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARMUGT:
+		// match: (UGT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (ULT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMULT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARMULE:
+		// match: (ULE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (UGE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMUGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARMULT:
+		// match: (ULT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARMFlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (UGT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARMInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARMUGT
+			b.SetControl(cmp)
 			_ = yes
 			_ = no
 			return true
diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go
new file mode 100644
index 0000000..dd5aa28
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/rewriteARM64.go
@@ -0,0 +1,16703 @@
+// autogenerated from gen/ARM64.rules: do not edit!
+// generated with: cd gen; go run *.go
+
+package ssa
+
+import "math"
+
+var _ = math.MinInt8 // in case not otherwise used
+func rewriteValueARM64(v *Value, config *Config) bool {
+	switch v.Op {
+	case OpARM64ADD:
+		return rewriteValueARM64_OpARM64ADD(v, config)
+	case OpARM64ADDconst:
+		return rewriteValueARM64_OpARM64ADDconst(v, config)
+	case OpARM64ADDshiftLL:
+		return rewriteValueARM64_OpARM64ADDshiftLL(v, config)
+	case OpARM64ADDshiftRA:
+		return rewriteValueARM64_OpARM64ADDshiftRA(v, config)
+	case OpARM64ADDshiftRL:
+		return rewriteValueARM64_OpARM64ADDshiftRL(v, config)
+	case OpARM64AND:
+		return rewriteValueARM64_OpARM64AND(v, config)
+	case OpARM64ANDconst:
+		return rewriteValueARM64_OpARM64ANDconst(v, config)
+	case OpARM64ANDshiftLL:
+		return rewriteValueARM64_OpARM64ANDshiftLL(v, config)
+	case OpARM64ANDshiftRA:
+		return rewriteValueARM64_OpARM64ANDshiftRA(v, config)
+	case OpARM64ANDshiftRL:
+		return rewriteValueARM64_OpARM64ANDshiftRL(v, config)
+	case OpARM64BIC:
+		return rewriteValueARM64_OpARM64BIC(v, config)
+	case OpARM64BICconst:
+		return rewriteValueARM64_OpARM64BICconst(v, config)
+	case OpARM64BICshiftLL:
+		return rewriteValueARM64_OpARM64BICshiftLL(v, config)
+	case OpARM64BICshiftRA:
+		return rewriteValueARM64_OpARM64BICshiftRA(v, config)
+	case OpARM64BICshiftRL:
+		return rewriteValueARM64_OpARM64BICshiftRL(v, config)
+	case OpARM64CMP:
+		return rewriteValueARM64_OpARM64CMP(v, config)
+	case OpARM64CMPW:
+		return rewriteValueARM64_OpARM64CMPW(v, config)
+	case OpARM64CMPWconst:
+		return rewriteValueARM64_OpARM64CMPWconst(v, config)
+	case OpARM64CMPconst:
+		return rewriteValueARM64_OpARM64CMPconst(v, config)
+	case OpARM64CMPshiftLL:
+		return rewriteValueARM64_OpARM64CMPshiftLL(v, config)
+	case OpARM64CMPshiftRA:
+		return rewriteValueARM64_OpARM64CMPshiftRA(v, config)
+	case OpARM64CMPshiftRL:
+		return rewriteValueARM64_OpARM64CMPshiftRL(v, config)
+	case OpARM64CSELULT:
+		return rewriteValueARM64_OpARM64CSELULT(v, config)
+	case OpARM64CSELULT0:
+		return rewriteValueARM64_OpARM64CSELULT0(v, config)
+	case OpARM64DIV:
+		return rewriteValueARM64_OpARM64DIV(v, config)
+	case OpARM64DIVW:
+		return rewriteValueARM64_OpARM64DIVW(v, config)
+	case OpARM64Equal:
+		return rewriteValueARM64_OpARM64Equal(v, config)
+	case OpARM64FMOVDload:
+		return rewriteValueARM64_OpARM64FMOVDload(v, config)
+	case OpARM64FMOVDstore:
+		return rewriteValueARM64_OpARM64FMOVDstore(v, config)
+	case OpARM64FMOVSload:
+		return rewriteValueARM64_OpARM64FMOVSload(v, config)
+	case OpARM64FMOVSstore:
+		return rewriteValueARM64_OpARM64FMOVSstore(v, config)
+	case OpARM64GreaterEqual:
+		return rewriteValueARM64_OpARM64GreaterEqual(v, config)
+	case OpARM64GreaterEqualU:
+		return rewriteValueARM64_OpARM64GreaterEqualU(v, config)
+	case OpARM64GreaterThan:
+		return rewriteValueARM64_OpARM64GreaterThan(v, config)
+	case OpARM64GreaterThanU:
+		return rewriteValueARM64_OpARM64GreaterThanU(v, config)
+	case OpARM64LessEqual:
+		return rewriteValueARM64_OpARM64LessEqual(v, config)
+	case OpARM64LessEqualU:
+		return rewriteValueARM64_OpARM64LessEqualU(v, config)
+	case OpARM64LessThan:
+		return rewriteValueARM64_OpARM64LessThan(v, config)
+	case OpARM64LessThanU:
+		return rewriteValueARM64_OpARM64LessThanU(v, config)
+	case OpARM64MOD:
+		return rewriteValueARM64_OpARM64MOD(v, config)
+	case OpARM64MODW:
+		return rewriteValueARM64_OpARM64MODW(v, config)
+	case OpARM64MOVBUload:
+		return rewriteValueARM64_OpARM64MOVBUload(v, config)
+	case OpARM64MOVBUreg:
+		return rewriteValueARM64_OpARM64MOVBUreg(v, config)
+	case OpARM64MOVBload:
+		return rewriteValueARM64_OpARM64MOVBload(v, config)
+	case OpARM64MOVBreg:
+		return rewriteValueARM64_OpARM64MOVBreg(v, config)
+	case OpARM64MOVBstore:
+		return rewriteValueARM64_OpARM64MOVBstore(v, config)
+	case OpARM64MOVBstorezero:
+		return rewriteValueARM64_OpARM64MOVBstorezero(v, config)
+	case OpARM64MOVDload:
+		return rewriteValueARM64_OpARM64MOVDload(v, config)
+	case OpARM64MOVDreg:
+		return rewriteValueARM64_OpARM64MOVDreg(v, config)
+	case OpARM64MOVDstore:
+		return rewriteValueARM64_OpARM64MOVDstore(v, config)
+	case OpARM64MOVDstorezero:
+		return rewriteValueARM64_OpARM64MOVDstorezero(v, config)
+	case OpARM64MOVHUload:
+		return rewriteValueARM64_OpARM64MOVHUload(v, config)
+	case OpARM64MOVHUreg:
+		return rewriteValueARM64_OpARM64MOVHUreg(v, config)
+	case OpARM64MOVHload:
+		return rewriteValueARM64_OpARM64MOVHload(v, config)
+	case OpARM64MOVHreg:
+		return rewriteValueARM64_OpARM64MOVHreg(v, config)
+	case OpARM64MOVHstore:
+		return rewriteValueARM64_OpARM64MOVHstore(v, config)
+	case OpARM64MOVHstorezero:
+		return rewriteValueARM64_OpARM64MOVHstorezero(v, config)
+	case OpARM64MOVWUload:
+		return rewriteValueARM64_OpARM64MOVWUload(v, config)
+	case OpARM64MOVWUreg:
+		return rewriteValueARM64_OpARM64MOVWUreg(v, config)
+	case OpARM64MOVWload:
+		return rewriteValueARM64_OpARM64MOVWload(v, config)
+	case OpARM64MOVWreg:
+		return rewriteValueARM64_OpARM64MOVWreg(v, config)
+	case OpARM64MOVWstore:
+		return rewriteValueARM64_OpARM64MOVWstore(v, config)
+	case OpARM64MOVWstorezero:
+		return rewriteValueARM64_OpARM64MOVWstorezero(v, config)
+	case OpARM64MUL:
+		return rewriteValueARM64_OpARM64MUL(v, config)
+	case OpARM64MULW:
+		return rewriteValueARM64_OpARM64MULW(v, config)
+	case OpARM64MVN:
+		return rewriteValueARM64_OpARM64MVN(v, config)
+	case OpARM64NEG:
+		return rewriteValueARM64_OpARM64NEG(v, config)
+	case OpARM64NotEqual:
+		return rewriteValueARM64_OpARM64NotEqual(v, config)
+	case OpARM64OR:
+		return rewriteValueARM64_OpARM64OR(v, config)
+	case OpARM64ORconst:
+		return rewriteValueARM64_OpARM64ORconst(v, config)
+	case OpARM64ORshiftLL:
+		return rewriteValueARM64_OpARM64ORshiftLL(v, config)
+	case OpARM64ORshiftRA:
+		return rewriteValueARM64_OpARM64ORshiftRA(v, config)
+	case OpARM64ORshiftRL:
+		return rewriteValueARM64_OpARM64ORshiftRL(v, config)
+	case OpARM64SLL:
+		return rewriteValueARM64_OpARM64SLL(v, config)
+	case OpARM64SLLconst:
+		return rewriteValueARM64_OpARM64SLLconst(v, config)
+	case OpARM64SRA:
+		return rewriteValueARM64_OpARM64SRA(v, config)
+	case OpARM64SRAconst:
+		return rewriteValueARM64_OpARM64SRAconst(v, config)
+	case OpARM64SRL:
+		return rewriteValueARM64_OpARM64SRL(v, config)
+	case OpARM64SRLconst:
+		return rewriteValueARM64_OpARM64SRLconst(v, config)
+	case OpARM64SUB:
+		return rewriteValueARM64_OpARM64SUB(v, config)
+	case OpARM64SUBconst:
+		return rewriteValueARM64_OpARM64SUBconst(v, config)
+	case OpARM64SUBshiftLL:
+		return rewriteValueARM64_OpARM64SUBshiftLL(v, config)
+	case OpARM64SUBshiftRA:
+		return rewriteValueARM64_OpARM64SUBshiftRA(v, config)
+	case OpARM64SUBshiftRL:
+		return rewriteValueARM64_OpARM64SUBshiftRL(v, config)
+	case OpARM64UDIV:
+		return rewriteValueARM64_OpARM64UDIV(v, config)
+	case OpARM64UDIVW:
+		return rewriteValueARM64_OpARM64UDIVW(v, config)
+	case OpARM64UMOD:
+		return rewriteValueARM64_OpARM64UMOD(v, config)
+	case OpARM64UMODW:
+		return rewriteValueARM64_OpARM64UMODW(v, config)
+	case OpARM64XOR:
+		return rewriteValueARM64_OpARM64XOR(v, config)
+	case OpARM64XORconst:
+		return rewriteValueARM64_OpARM64XORconst(v, config)
+	case OpARM64XORshiftLL:
+		return rewriteValueARM64_OpARM64XORshiftLL(v, config)
+	case OpARM64XORshiftRA:
+		return rewriteValueARM64_OpARM64XORshiftRA(v, config)
+	case OpARM64XORshiftRL:
+		return rewriteValueARM64_OpARM64XORshiftRL(v, config)
+	case OpAdd16:
+		return rewriteValueARM64_OpAdd16(v, config)
+	case OpAdd32:
+		return rewriteValueARM64_OpAdd32(v, config)
+	case OpAdd32F:
+		return rewriteValueARM64_OpAdd32F(v, config)
+	case OpAdd64:
+		return rewriteValueARM64_OpAdd64(v, config)
+	case OpAdd64F:
+		return rewriteValueARM64_OpAdd64F(v, config)
+	case OpAdd8:
+		return rewriteValueARM64_OpAdd8(v, config)
+	case OpAddPtr:
+		return rewriteValueARM64_OpAddPtr(v, config)
+	case OpAddr:
+		return rewriteValueARM64_OpAddr(v, config)
+	case OpAnd16:
+		return rewriteValueARM64_OpAnd16(v, config)
+	case OpAnd32:
+		return rewriteValueARM64_OpAnd32(v, config)
+	case OpAnd64:
+		return rewriteValueARM64_OpAnd64(v, config)
+	case OpAnd8:
+		return rewriteValueARM64_OpAnd8(v, config)
+	case OpAndB:
+		return rewriteValueARM64_OpAndB(v, config)
+	case OpAtomicAdd32:
+		return rewriteValueARM64_OpAtomicAdd32(v, config)
+	case OpAtomicAdd64:
+		return rewriteValueARM64_OpAtomicAdd64(v, config)
+	case OpAtomicAnd8:
+		return rewriteValueARM64_OpAtomicAnd8(v, config)
+	case OpAtomicCompareAndSwap32:
+		return rewriteValueARM64_OpAtomicCompareAndSwap32(v, config)
+	case OpAtomicCompareAndSwap64:
+		return rewriteValueARM64_OpAtomicCompareAndSwap64(v, config)
+	case OpAtomicExchange32:
+		return rewriteValueARM64_OpAtomicExchange32(v, config)
+	case OpAtomicExchange64:
+		return rewriteValueARM64_OpAtomicExchange64(v, config)
+	case OpAtomicLoad32:
+		return rewriteValueARM64_OpAtomicLoad32(v, config)
+	case OpAtomicLoad64:
+		return rewriteValueARM64_OpAtomicLoad64(v, config)
+	case OpAtomicLoadPtr:
+		return rewriteValueARM64_OpAtomicLoadPtr(v, config)
+	case OpAtomicOr8:
+		return rewriteValueARM64_OpAtomicOr8(v, config)
+	case OpAtomicStore32:
+		return rewriteValueARM64_OpAtomicStore32(v, config)
+	case OpAtomicStore64:
+		return rewriteValueARM64_OpAtomicStore64(v, config)
+	case OpAtomicStorePtrNoWB:
+		return rewriteValueARM64_OpAtomicStorePtrNoWB(v, config)
+	case OpAvg64u:
+		return rewriteValueARM64_OpAvg64u(v, config)
+	case OpBswap32:
+		return rewriteValueARM64_OpBswap32(v, config)
+	case OpBswap64:
+		return rewriteValueARM64_OpBswap64(v, config)
+	case OpClosureCall:
+		return rewriteValueARM64_OpClosureCall(v, config)
+	case OpCom16:
+		return rewriteValueARM64_OpCom16(v, config)
+	case OpCom32:
+		return rewriteValueARM64_OpCom32(v, config)
+	case OpCom64:
+		return rewriteValueARM64_OpCom64(v, config)
+	case OpCom8:
+		return rewriteValueARM64_OpCom8(v, config)
+	case OpConst16:
+		return rewriteValueARM64_OpConst16(v, config)
+	case OpConst32:
+		return rewriteValueARM64_OpConst32(v, config)
+	case OpConst32F:
+		return rewriteValueARM64_OpConst32F(v, config)
+	case OpConst64:
+		return rewriteValueARM64_OpConst64(v, config)
+	case OpConst64F:
+		return rewriteValueARM64_OpConst64F(v, config)
+	case OpConst8:
+		return rewriteValueARM64_OpConst8(v, config)
+	case OpConstBool:
+		return rewriteValueARM64_OpConstBool(v, config)
+	case OpConstNil:
+		return rewriteValueARM64_OpConstNil(v, config)
+	case OpConvert:
+		return rewriteValueARM64_OpConvert(v, config)
+	case OpCtz32:
+		return rewriteValueARM64_OpCtz32(v, config)
+	case OpCtz64:
+		return rewriteValueARM64_OpCtz64(v, config)
+	case OpCvt32Fto32:
+		return rewriteValueARM64_OpCvt32Fto32(v, config)
+	case OpCvt32Fto32U:
+		return rewriteValueARM64_OpCvt32Fto32U(v, config)
+	case OpCvt32Fto64:
+		return rewriteValueARM64_OpCvt32Fto64(v, config)
+	case OpCvt32Fto64F:
+		return rewriteValueARM64_OpCvt32Fto64F(v, config)
+	case OpCvt32Fto64U:
+		return rewriteValueARM64_OpCvt32Fto64U(v, config)
+	case OpCvt32Uto32F:
+		return rewriteValueARM64_OpCvt32Uto32F(v, config)
+	case OpCvt32Uto64F:
+		return rewriteValueARM64_OpCvt32Uto64F(v, config)
+	case OpCvt32to32F:
+		return rewriteValueARM64_OpCvt32to32F(v, config)
+	case OpCvt32to64F:
+		return rewriteValueARM64_OpCvt32to64F(v, config)
+	case OpCvt64Fto32:
+		return rewriteValueARM64_OpCvt64Fto32(v, config)
+	case OpCvt64Fto32F:
+		return rewriteValueARM64_OpCvt64Fto32F(v, config)
+	case OpCvt64Fto32U:
+		return rewriteValueARM64_OpCvt64Fto32U(v, config)
+	case OpCvt64Fto64:
+		return rewriteValueARM64_OpCvt64Fto64(v, config)
+	case OpCvt64Fto64U:
+		return rewriteValueARM64_OpCvt64Fto64U(v, config)
+	case OpCvt64Uto32F:
+		return rewriteValueARM64_OpCvt64Uto32F(v, config)
+	case OpCvt64Uto64F:
+		return rewriteValueARM64_OpCvt64Uto64F(v, config)
+	case OpCvt64to32F:
+		return rewriteValueARM64_OpCvt64to32F(v, config)
+	case OpCvt64to64F:
+		return rewriteValueARM64_OpCvt64to64F(v, config)
+	case OpDeferCall:
+		return rewriteValueARM64_OpDeferCall(v, config)
+	case OpDiv16:
+		return rewriteValueARM64_OpDiv16(v, config)
+	case OpDiv16u:
+		return rewriteValueARM64_OpDiv16u(v, config)
+	case OpDiv32:
+		return rewriteValueARM64_OpDiv32(v, config)
+	case OpDiv32F:
+		return rewriteValueARM64_OpDiv32F(v, config)
+	case OpDiv32u:
+		return rewriteValueARM64_OpDiv32u(v, config)
+	case OpDiv64:
+		return rewriteValueARM64_OpDiv64(v, config)
+	case OpDiv64F:
+		return rewriteValueARM64_OpDiv64F(v, config)
+	case OpDiv64u:
+		return rewriteValueARM64_OpDiv64u(v, config)
+	case OpDiv8:
+		return rewriteValueARM64_OpDiv8(v, config)
+	case OpDiv8u:
+		return rewriteValueARM64_OpDiv8u(v, config)
+	case OpEq16:
+		return rewriteValueARM64_OpEq16(v, config)
+	case OpEq32:
+		return rewriteValueARM64_OpEq32(v, config)
+	case OpEq32F:
+		return rewriteValueARM64_OpEq32F(v, config)
+	case OpEq64:
+		return rewriteValueARM64_OpEq64(v, config)
+	case OpEq64F:
+		return rewriteValueARM64_OpEq64F(v, config)
+	case OpEq8:
+		return rewriteValueARM64_OpEq8(v, config)
+	case OpEqB:
+		return rewriteValueARM64_OpEqB(v, config)
+	case OpEqPtr:
+		return rewriteValueARM64_OpEqPtr(v, config)
+	case OpGeq16:
+		return rewriteValueARM64_OpGeq16(v, config)
+	case OpGeq16U:
+		return rewriteValueARM64_OpGeq16U(v, config)
+	case OpGeq32:
+		return rewriteValueARM64_OpGeq32(v, config)
+	case OpGeq32F:
+		return rewriteValueARM64_OpGeq32F(v, config)
+	case OpGeq32U:
+		return rewriteValueARM64_OpGeq32U(v, config)
+	case OpGeq64:
+		return rewriteValueARM64_OpGeq64(v, config)
+	case OpGeq64F:
+		return rewriteValueARM64_OpGeq64F(v, config)
+	case OpGeq64U:
+		return rewriteValueARM64_OpGeq64U(v, config)
+	case OpGeq8:
+		return rewriteValueARM64_OpGeq8(v, config)
+	case OpGeq8U:
+		return rewriteValueARM64_OpGeq8U(v, config)
+	case OpGetClosurePtr:
+		return rewriteValueARM64_OpGetClosurePtr(v, config)
+	case OpGoCall:
+		return rewriteValueARM64_OpGoCall(v, config)
+	case OpGreater16:
+		return rewriteValueARM64_OpGreater16(v, config)
+	case OpGreater16U:
+		return rewriteValueARM64_OpGreater16U(v, config)
+	case OpGreater32:
+		return rewriteValueARM64_OpGreater32(v, config)
+	case OpGreater32F:
+		return rewriteValueARM64_OpGreater32F(v, config)
+	case OpGreater32U:
+		return rewriteValueARM64_OpGreater32U(v, config)
+	case OpGreater64:
+		return rewriteValueARM64_OpGreater64(v, config)
+	case OpGreater64F:
+		return rewriteValueARM64_OpGreater64F(v, config)
+	case OpGreater64U:
+		return rewriteValueARM64_OpGreater64U(v, config)
+	case OpGreater8:
+		return rewriteValueARM64_OpGreater8(v, config)
+	case OpGreater8U:
+		return rewriteValueARM64_OpGreater8U(v, config)
+	case OpHmul16:
+		return rewriteValueARM64_OpHmul16(v, config)
+	case OpHmul16u:
+		return rewriteValueARM64_OpHmul16u(v, config)
+	case OpHmul32:
+		return rewriteValueARM64_OpHmul32(v, config)
+	case OpHmul32u:
+		return rewriteValueARM64_OpHmul32u(v, config)
+	case OpHmul64:
+		return rewriteValueARM64_OpHmul64(v, config)
+	case OpHmul64u:
+		return rewriteValueARM64_OpHmul64u(v, config)
+	case OpHmul8:
+		return rewriteValueARM64_OpHmul8(v, config)
+	case OpHmul8u:
+		return rewriteValueARM64_OpHmul8u(v, config)
+	case OpInterCall:
+		return rewriteValueARM64_OpInterCall(v, config)
+	case OpIsInBounds:
+		return rewriteValueARM64_OpIsInBounds(v, config)
+	case OpIsNonNil:
+		return rewriteValueARM64_OpIsNonNil(v, config)
+	case OpIsSliceInBounds:
+		return rewriteValueARM64_OpIsSliceInBounds(v, config)
+	case OpLeq16:
+		return rewriteValueARM64_OpLeq16(v, config)
+	case OpLeq16U:
+		return rewriteValueARM64_OpLeq16U(v, config)
+	case OpLeq32:
+		return rewriteValueARM64_OpLeq32(v, config)
+	case OpLeq32F:
+		return rewriteValueARM64_OpLeq32F(v, config)
+	case OpLeq32U:
+		return rewriteValueARM64_OpLeq32U(v, config)
+	case OpLeq64:
+		return rewriteValueARM64_OpLeq64(v, config)
+	case OpLeq64F:
+		return rewriteValueARM64_OpLeq64F(v, config)
+	case OpLeq64U:
+		return rewriteValueARM64_OpLeq64U(v, config)
+	case OpLeq8:
+		return rewriteValueARM64_OpLeq8(v, config)
+	case OpLeq8U:
+		return rewriteValueARM64_OpLeq8U(v, config)
+	case OpLess16:
+		return rewriteValueARM64_OpLess16(v, config)
+	case OpLess16U:
+		return rewriteValueARM64_OpLess16U(v, config)
+	case OpLess32:
+		return rewriteValueARM64_OpLess32(v, config)
+	case OpLess32F:
+		return rewriteValueARM64_OpLess32F(v, config)
+	case OpLess32U:
+		return rewriteValueARM64_OpLess32U(v, config)
+	case OpLess64:
+		return rewriteValueARM64_OpLess64(v, config)
+	case OpLess64F:
+		return rewriteValueARM64_OpLess64F(v, config)
+	case OpLess64U:
+		return rewriteValueARM64_OpLess64U(v, config)
+	case OpLess8:
+		return rewriteValueARM64_OpLess8(v, config)
+	case OpLess8U:
+		return rewriteValueARM64_OpLess8U(v, config)
+	case OpLoad:
+		return rewriteValueARM64_OpLoad(v, config)
+	case OpLrot16:
+		return rewriteValueARM64_OpLrot16(v, config)
+	case OpLrot32:
+		return rewriteValueARM64_OpLrot32(v, config)
+	case OpLrot64:
+		return rewriteValueARM64_OpLrot64(v, config)
+	case OpLrot8:
+		return rewriteValueARM64_OpLrot8(v, config)
+	case OpLsh16x16:
+		return rewriteValueARM64_OpLsh16x16(v, config)
+	case OpLsh16x32:
+		return rewriteValueARM64_OpLsh16x32(v, config)
+	case OpLsh16x64:
+		return rewriteValueARM64_OpLsh16x64(v, config)
+	case OpLsh16x8:
+		return rewriteValueARM64_OpLsh16x8(v, config)
+	case OpLsh32x16:
+		return rewriteValueARM64_OpLsh32x16(v, config)
+	case OpLsh32x32:
+		return rewriteValueARM64_OpLsh32x32(v, config)
+	case OpLsh32x64:
+		return rewriteValueARM64_OpLsh32x64(v, config)
+	case OpLsh32x8:
+		return rewriteValueARM64_OpLsh32x8(v, config)
+	case OpLsh64x16:
+		return rewriteValueARM64_OpLsh64x16(v, config)
+	case OpLsh64x32:
+		return rewriteValueARM64_OpLsh64x32(v, config)
+	case OpLsh64x64:
+		return rewriteValueARM64_OpLsh64x64(v, config)
+	case OpLsh64x8:
+		return rewriteValueARM64_OpLsh64x8(v, config)
+	case OpLsh8x16:
+		return rewriteValueARM64_OpLsh8x16(v, config)
+	case OpLsh8x32:
+		return rewriteValueARM64_OpLsh8x32(v, config)
+	case OpLsh8x64:
+		return rewriteValueARM64_OpLsh8x64(v, config)
+	case OpLsh8x8:
+		return rewriteValueARM64_OpLsh8x8(v, config)
+	case OpMod16:
+		return rewriteValueARM64_OpMod16(v, config)
+	case OpMod16u:
+		return rewriteValueARM64_OpMod16u(v, config)
+	case OpMod32:
+		return rewriteValueARM64_OpMod32(v, config)
+	case OpMod32u:
+		return rewriteValueARM64_OpMod32u(v, config)
+	case OpMod64:
+		return rewriteValueARM64_OpMod64(v, config)
+	case OpMod64u:
+		return rewriteValueARM64_OpMod64u(v, config)
+	case OpMod8:
+		return rewriteValueARM64_OpMod8(v, config)
+	case OpMod8u:
+		return rewriteValueARM64_OpMod8u(v, config)
+	case OpMove:
+		return rewriteValueARM64_OpMove(v, config)
+	case OpMul16:
+		return rewriteValueARM64_OpMul16(v, config)
+	case OpMul32:
+		return rewriteValueARM64_OpMul32(v, config)
+	case OpMul32F:
+		return rewriteValueARM64_OpMul32F(v, config)
+	case OpMul64:
+		return rewriteValueARM64_OpMul64(v, config)
+	case OpMul64F:
+		return rewriteValueARM64_OpMul64F(v, config)
+	case OpMul8:
+		return rewriteValueARM64_OpMul8(v, config)
+	case OpNeg16:
+		return rewriteValueARM64_OpNeg16(v, config)
+	case OpNeg32:
+		return rewriteValueARM64_OpNeg32(v, config)
+	case OpNeg32F:
+		return rewriteValueARM64_OpNeg32F(v, config)
+	case OpNeg64:
+		return rewriteValueARM64_OpNeg64(v, config)
+	case OpNeg64F:
+		return rewriteValueARM64_OpNeg64F(v, config)
+	case OpNeg8:
+		return rewriteValueARM64_OpNeg8(v, config)
+	case OpNeq16:
+		return rewriteValueARM64_OpNeq16(v, config)
+	case OpNeq32:
+		return rewriteValueARM64_OpNeq32(v, config)
+	case OpNeq32F:
+		return rewriteValueARM64_OpNeq32F(v, config)
+	case OpNeq64:
+		return rewriteValueARM64_OpNeq64(v, config)
+	case OpNeq64F:
+		return rewriteValueARM64_OpNeq64F(v, config)
+	case OpNeq8:
+		return rewriteValueARM64_OpNeq8(v, config)
+	case OpNeqB:
+		return rewriteValueARM64_OpNeqB(v, config)
+	case OpNeqPtr:
+		return rewriteValueARM64_OpNeqPtr(v, config)
+	case OpNilCheck:
+		return rewriteValueARM64_OpNilCheck(v, config)
+	case OpNot:
+		return rewriteValueARM64_OpNot(v, config)
+	case OpOffPtr:
+		return rewriteValueARM64_OpOffPtr(v, config)
+	case OpOr16:
+		return rewriteValueARM64_OpOr16(v, config)
+	case OpOr32:
+		return rewriteValueARM64_OpOr32(v, config)
+	case OpOr64:
+		return rewriteValueARM64_OpOr64(v, config)
+	case OpOr8:
+		return rewriteValueARM64_OpOr8(v, config)
+	case OpOrB:
+		return rewriteValueARM64_OpOrB(v, config)
+	case OpRsh16Ux16:
+		return rewriteValueARM64_OpRsh16Ux16(v, config)
+	case OpRsh16Ux32:
+		return rewriteValueARM64_OpRsh16Ux32(v, config)
+	case OpRsh16Ux64:
+		return rewriteValueARM64_OpRsh16Ux64(v, config)
+	case OpRsh16Ux8:
+		return rewriteValueARM64_OpRsh16Ux8(v, config)
+	case OpRsh16x16:
+		return rewriteValueARM64_OpRsh16x16(v, config)
+	case OpRsh16x32:
+		return rewriteValueARM64_OpRsh16x32(v, config)
+	case OpRsh16x64:
+		return rewriteValueARM64_OpRsh16x64(v, config)
+	case OpRsh16x8:
+		return rewriteValueARM64_OpRsh16x8(v, config)
+	case OpRsh32Ux16:
+		return rewriteValueARM64_OpRsh32Ux16(v, config)
+	case OpRsh32Ux32:
+		return rewriteValueARM64_OpRsh32Ux32(v, config)
+	case OpRsh32Ux64:
+		return rewriteValueARM64_OpRsh32Ux64(v, config)
+	case OpRsh32Ux8:
+		return rewriteValueARM64_OpRsh32Ux8(v, config)
+	case OpRsh32x16:
+		return rewriteValueARM64_OpRsh32x16(v, config)
+	case OpRsh32x32:
+		return rewriteValueARM64_OpRsh32x32(v, config)
+	case OpRsh32x64:
+		return rewriteValueARM64_OpRsh32x64(v, config)
+	case OpRsh32x8:
+		return rewriteValueARM64_OpRsh32x8(v, config)
+	case OpRsh64Ux16:
+		return rewriteValueARM64_OpRsh64Ux16(v, config)
+	case OpRsh64Ux32:
+		return rewriteValueARM64_OpRsh64Ux32(v, config)
+	case OpRsh64Ux64:
+		return rewriteValueARM64_OpRsh64Ux64(v, config)
+	case OpRsh64Ux8:
+		return rewriteValueARM64_OpRsh64Ux8(v, config)
+	case OpRsh64x16:
+		return rewriteValueARM64_OpRsh64x16(v, config)
+	case OpRsh64x32:
+		return rewriteValueARM64_OpRsh64x32(v, config)
+	case OpRsh64x64:
+		return rewriteValueARM64_OpRsh64x64(v, config)
+	case OpRsh64x8:
+		return rewriteValueARM64_OpRsh64x8(v, config)
+	case OpRsh8Ux16:
+		return rewriteValueARM64_OpRsh8Ux16(v, config)
+	case OpRsh8Ux32:
+		return rewriteValueARM64_OpRsh8Ux32(v, config)
+	case OpRsh8Ux64:
+		return rewriteValueARM64_OpRsh8Ux64(v, config)
+	case OpRsh8Ux8:
+		return rewriteValueARM64_OpRsh8Ux8(v, config)
+	case OpRsh8x16:
+		return rewriteValueARM64_OpRsh8x16(v, config)
+	case OpRsh8x32:
+		return rewriteValueARM64_OpRsh8x32(v, config)
+	case OpRsh8x64:
+		return rewriteValueARM64_OpRsh8x64(v, config)
+	case OpRsh8x8:
+		return rewriteValueARM64_OpRsh8x8(v, config)
+	case OpSignExt16to32:
+		return rewriteValueARM64_OpSignExt16to32(v, config)
+	case OpSignExt16to64:
+		return rewriteValueARM64_OpSignExt16to64(v, config)
+	case OpSignExt32to64:
+		return rewriteValueARM64_OpSignExt32to64(v, config)
+	case OpSignExt8to16:
+		return rewriteValueARM64_OpSignExt8to16(v, config)
+	case OpSignExt8to32:
+		return rewriteValueARM64_OpSignExt8to32(v, config)
+	case OpSignExt8to64:
+		return rewriteValueARM64_OpSignExt8to64(v, config)
+	case OpSlicemask:
+		return rewriteValueARM64_OpSlicemask(v, config)
+	case OpSqrt:
+		return rewriteValueARM64_OpSqrt(v, config)
+	case OpStaticCall:
+		return rewriteValueARM64_OpStaticCall(v, config)
+	case OpStore:
+		return rewriteValueARM64_OpStore(v, config)
+	case OpSub16:
+		return rewriteValueARM64_OpSub16(v, config)
+	case OpSub32:
+		return rewriteValueARM64_OpSub32(v, config)
+	case OpSub32F:
+		return rewriteValueARM64_OpSub32F(v, config)
+	case OpSub64:
+		return rewriteValueARM64_OpSub64(v, config)
+	case OpSub64F:
+		return rewriteValueARM64_OpSub64F(v, config)
+	case OpSub8:
+		return rewriteValueARM64_OpSub8(v, config)
+	case OpSubPtr:
+		return rewriteValueARM64_OpSubPtr(v, config)
+	case OpTrunc16to8:
+		return rewriteValueARM64_OpTrunc16to8(v, config)
+	case OpTrunc32to16:
+		return rewriteValueARM64_OpTrunc32to16(v, config)
+	case OpTrunc32to8:
+		return rewriteValueARM64_OpTrunc32to8(v, config)
+	case OpTrunc64to16:
+		return rewriteValueARM64_OpTrunc64to16(v, config)
+	case OpTrunc64to32:
+		return rewriteValueARM64_OpTrunc64to32(v, config)
+	case OpTrunc64to8:
+		return rewriteValueARM64_OpTrunc64to8(v, config)
+	case OpXor16:
+		return rewriteValueARM64_OpXor16(v, config)
+	case OpXor32:
+		return rewriteValueARM64_OpXor32(v, config)
+	case OpXor64:
+		return rewriteValueARM64_OpXor64(v, config)
+	case OpXor8:
+		return rewriteValueARM64_OpXor8(v, config)
+	case OpZero:
+		return rewriteValueARM64_OpZero(v, config)
+	case OpZeroExt16to32:
+		return rewriteValueARM64_OpZeroExt16to32(v, config)
+	case OpZeroExt16to64:
+		return rewriteValueARM64_OpZeroExt16to64(v, config)
+	case OpZeroExt32to64:
+		return rewriteValueARM64_OpZeroExt32to64(v, config)
+	case OpZeroExt8to16:
+		return rewriteValueARM64_OpZeroExt8to16(v, config)
+	case OpZeroExt8to32:
+		return rewriteValueARM64_OpZeroExt8to32(v, config)
+	case OpZeroExt8to64:
+		return rewriteValueARM64_OpZeroExt8to64(v, config)
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ADD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADD (MOVDconst [c]) x)
+	// cond:
+	// result: (ADDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADD x (MOVDconst [c]))
+	// cond:
+	// result: (ADDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADD x (NEG y))
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64NEG {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpARM64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (NEG y) x)
+	// cond:
+	// result: (SUB x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64NEG {
+			break
+		}
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD x (SLLconst [c] y))
+	// cond:
+	// result: (ADDshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64ADDshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (SLLconst [c] y) x)
+	// cond:
+	// result: (ADDshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64ADDshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD x (SRLconst [c] y))
+	// cond:
+	// result: (ADDshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64ADDshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (SRLconst [c] y) x)
+	// cond:
+	// result: (ADDshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64ADDshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD x (SRAconst [c] y))
+	// cond:
+	// result: (ADDshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64ADDshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (SRAconst [c] y) x)
+	// cond:
+	// result: (ADDshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64ADDshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ADDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDconst [off1] (MOVDaddr [off2] {sym} ptr))
+	// cond:
+	// result: (MOVDaddr [off1+off2] {sym} ptr)
+	for {
+		off1 := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym := v_0.Aux
+		ptr := v_0.Args[0]
+		v.reset(OpARM64MOVDaddr)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		return true
+	}
+	// match: (ADDconst [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c+d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = c + d
+		return true
+	}
+	// match: (ADDconst [c] (ADDconst [d] x))
+	// cond:
+	// result: (ADDconst [c+d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = c + d
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (SUBconst [d] x))
+	// cond:
+	// result: (ADDconst [c-d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SUBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = c - d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ADDshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDshiftLL (MOVDconst [c]) x [d])
+	// cond:
+	// result: (ADDconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDshiftLL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (ADDconst x [int64(uint64(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = int64(uint64(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ADDshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDshiftRA (MOVDconst [c]) x [d])
+	// cond:
+	// result: (ADDconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDshiftRA x (MOVDconst [c]) [d])
+	// cond:
+	// result: (ADDconst x [int64(int64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = int64(int64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ADDshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDshiftRL (MOVDconst [c]) x [d])
+	// cond:
+	// result: (ADDconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ADDshiftRL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (ADDconst x [int64(uint64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = int64(uint64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64AND(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AND (MOVDconst [c]) x)
+	// cond:
+	// result: (ANDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x (MOVDconst [c]))
+	// cond:
+	// result: (ANDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x (MVN y))
+	// cond:
+	// result: (BIC x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MVN {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpARM64BIC)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND x (SLLconst [c] y))
+	// cond:
+	// result: (ANDshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64ANDshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (SLLconst [c] y) x)
+	// cond:
+	// result: (ANDshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64ANDshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND x (SRLconst [c] y))
+	// cond:
+	// result: (ANDshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64ANDshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (SRLconst [c] y) x)
+	// cond:
+	// result: (ANDshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64ANDshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND x (SRAconst [c] y))
+	// cond:
+	// result: (ANDshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64ANDshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (SRAconst [c] y) x)
+	// cond:
+	// result: (ANDshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64ANDshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ANDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDconst [0]  _)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (ANDconst [-1] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c&d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = c & d
+		return true
+	}
+	// match: (ANDconst [c] (ANDconst [d] x))
+	// cond:
+	// result: (ANDconst [c&d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ANDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = c & d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ANDshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDshiftLL (MOVDconst [c]) x [d])
+	// cond:
+	// result: (ANDconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ANDshiftLL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (ANDconst x [int64(uint64(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = int64(uint64(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDshiftLL x y:(SLLconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARM64SLLconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ANDshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDshiftRA (MOVDconst [c]) x [d])
+	// cond:
+	// result: (ANDconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ANDshiftRA x (MOVDconst [c]) [d])
+	// cond:
+	// result: (ANDconst x [int64(int64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = int64(int64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDshiftRA x y:(SRAconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARM64SRAconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ANDshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDshiftRL (MOVDconst [c]) x [d])
+	// cond:
+	// result: (ANDconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ANDshiftRL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (ANDconst x [int64(uint64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = int64(uint64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDshiftRL x y:(SRLconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARM64SRLconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64BIC(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BIC x (MOVDconst [c]))
+	// cond:
+	// result: (BICconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64BICconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (BIC x x)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (BIC x (SLLconst [c] y))
+	// cond:
+	// result: (BICshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64BICshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (BIC x (SRLconst [c] y))
+	// cond:
+	// result: (BICshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64BICshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (BIC x (SRAconst [c] y))
+	// cond:
+	// result: (BICshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64BICshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64BICconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICconst [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (BICconst [-1] _)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (BICconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [d&^c])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = d &^ c
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64BICshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICshiftLL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (BICconst x [int64(uint64(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64BICconst)
+		v.AuxInt = int64(uint64(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (BICshiftLL x (SLLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVDconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64BICshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICshiftRA x (MOVDconst [c]) [d])
+	// cond:
+	// result: (BICconst x [int64(int64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64BICconst)
+		v.AuxInt = int64(int64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (BICshiftRA x (SRAconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVDconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64BICshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (BICshiftRL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (BICconst x [int64(uint64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64BICconst)
+		v.AuxInt = int64(uint64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (BICshiftRL x (SRLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVDconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64CMP(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMP x (MOVDconst [c]))
+	// cond:
+	// result: (CMPconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64CMPconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMP (MOVDconst [c]) x)
+	// cond:
+	// result: (InvertFlags (CMPconst [c] x))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMP x (SLLconst [c] y))
+	// cond:
+	// result: (CMPshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64CMPshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMP (SLLconst [c] y) x)
+	// cond:
+	// result: (InvertFlags (CMPshiftLL x y [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpARM64CMPshiftLL, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMP x (SRLconst [c] y))
+	// cond:
+	// result: (CMPshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64CMPshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMP (SRLconst [c] y) x)
+	// cond:
+	// result: (InvertFlags (CMPshiftRL x y [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpARM64CMPshiftRL, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMP x (SRAconst [c] y))
+	// cond:
+	// result: (CMPshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64CMPshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMP (SRAconst [c] y) x)
+	// cond:
+	// result: (InvertFlags (CMPshiftRA x y [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpARM64CMPshiftRA, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64CMPW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPW x (MOVDconst [c]))
+	// cond:
+	// result: (CMPWconst [int64(int32(c))] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64CMPWconst)
+		v.AuxInt = int64(int32(c))
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPW (MOVDconst [c]) x)
+	// cond:
+	// result: (InvertFlags (CMPWconst [int64(int32(c))] x))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpARM64CMPWconst, TypeFlags)
+		v0.AuxInt = int64(int32(c))
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64CMPWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)==int32(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) == int32(y)) {
+			break
+		}
+		v.reset(OpARM64FlagEQ)
+		return true
+	}
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)<int32(y) && uint32(x)<uint32(y)
+	// result: (FlagLT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) < int32(y) && uint32(x) < uint32(y)) {
+			break
+		}
+		v.reset(OpARM64FlagLT_ULT)
+		return true
+	}
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)<int32(y) && uint32(x)>uint32(y)
+	// result: (FlagLT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) < int32(y) && uint32(x) > uint32(y)) {
+			break
+		}
+		v.reset(OpARM64FlagLT_UGT)
+		return true
+	}
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)>int32(y) && uint32(x)<uint32(y)
+	// result: (FlagGT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) > int32(y) && uint32(x) < uint32(y)) {
+			break
+		}
+		v.reset(OpARM64FlagGT_ULT)
+		return true
+	}
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)>int32(y) && uint32(x)>uint32(y)
+	// result: (FlagGT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) > int32(y) && uint32(x) > uint32(y)) {
+			break
+		}
+		v.reset(OpARM64FlagGT_UGT)
+		return true
+	}
+	// match: (CMPWconst (MOVBUreg _) [c])
+	// cond: 0xff < int32(c)
+	// result: (FlagLT_ULT)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVBUreg {
+			break
+		}
+		if !(0xff < int32(c)) {
+			break
+		}
+		v.reset(OpARM64FlagLT_ULT)
+		return true
+	}
+	// match: (CMPWconst (MOVHUreg _) [c])
+	// cond: 0xffff < int32(c)
+	// result: (FlagLT_ULT)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVHUreg {
+			break
+		}
+		if !(0xffff < int32(c)) {
+			break
+		}
+		v.reset(OpARM64FlagLT_ULT)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64CMPconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPconst  (MOVDconst [x]) [y])
+	// cond: x==y
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(x == y) {
+			break
+		}
+		v.reset(OpARM64FlagEQ)
+		return true
+	}
+	// match: (CMPconst  (MOVDconst [x]) [y])
+	// cond: int64(x)<int64(y) && uint64(x)<uint64(y)
+	// result: (FlagLT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int64(x) < int64(y) && uint64(x) < uint64(y)) {
+			break
+		}
+		v.reset(OpARM64FlagLT_ULT)
+		return true
+	}
+	// match: (CMPconst  (MOVDconst [x]) [y])
+	// cond: int64(x)<int64(y) && uint64(x)>uint64(y)
+	// result: (FlagLT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int64(x) < int64(y) && uint64(x) > uint64(y)) {
+			break
+		}
+		v.reset(OpARM64FlagLT_UGT)
+		return true
+	}
+	// match: (CMPconst  (MOVDconst [x]) [y])
+	// cond: int64(x)>int64(y) && uint64(x)<uint64(y)
+	// result: (FlagGT_ULT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int64(x) > int64(y) && uint64(x) < uint64(y)) {
+			break
+		}
+		v.reset(OpARM64FlagGT_ULT)
+		return true
+	}
+	// match: (CMPconst  (MOVDconst [x]) [y])
+	// cond: int64(x)>int64(y) && uint64(x)>uint64(y)
+	// result: (FlagGT_UGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int64(x) > int64(y) && uint64(x) > uint64(y)) {
+			break
+		}
+		v.reset(OpARM64FlagGT_UGT)
+		return true
+	}
+	// match: (CMPconst (MOVBUreg _) [c])
+	// cond: 0xff < c
+	// result: (FlagLT_ULT)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVBUreg {
+			break
+		}
+		if !(0xff < c) {
+			break
+		}
+		v.reset(OpARM64FlagLT_ULT)
+		return true
+	}
+	// match: (CMPconst (MOVHUreg _) [c])
+	// cond: 0xffff < c
+	// result: (FlagLT_ULT)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVHUreg {
+			break
+		}
+		if !(0xffff < c) {
+			break
+		}
+		v.reset(OpARM64FlagLT_ULT)
+		return true
+	}
+	// match: (CMPconst (MOVWUreg _) [c])
+	// cond: 0xffffffff < c
+	// result: (FlagLT_ULT)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVWUreg {
+			break
+		}
+		if !(0xffffffff < c) {
+			break
+		}
+		v.reset(OpARM64FlagLT_ULT)
+		return true
+	}
+	// match: (CMPconst (ANDconst _ [m]) [n])
+	// cond: 0 <= m && m < n
+	// result: (FlagLT_ULT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ANDconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(0 <= m && m < n) {
+			break
+		}
+		v.reset(OpARM64FlagLT_ULT)
+		return true
+	}
+	// match: (CMPconst (SRLconst _ [c]) [n])
+	// cond: 0 <= n && 0 < c && c <= 63 && (1<<uint64(64-c)) <= uint64(n)
+	// result: (FlagLT_ULT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		if !(0 <= n && 0 < c && c <= 63 && (1<<uint64(64-c)) <= uint64(n)) {
+			break
+		}
+		v.reset(OpARM64FlagLT_ULT)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64CMPshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPshiftLL (MOVDconst [c]) x [d])
+	// cond:
+	// result: (InvertFlags (CMPconst [c] (SLLconst <x.Type> x [d])))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v0.AuxInt = c
+		v1 := b.NewValue0(v.Line, OpARM64SLLconst, x.Type)
+		v1.AuxInt = d
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMPshiftLL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (CMPconst x [int64(uint64(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64CMPconst)
+		v.AuxInt = int64(uint64(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64CMPshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPshiftRA (MOVDconst [c]) x [d])
+	// cond:
+	// result: (InvertFlags (CMPconst [c] (SRAconst <x.Type> x [d])))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v0.AuxInt = c
+		v1 := b.NewValue0(v.Line, OpARM64SRAconst, x.Type)
+		v1.AuxInt = d
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMPshiftRA x (MOVDconst [c]) [d])
+	// cond:
+	// result: (CMPconst x [int64(int64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64CMPconst)
+		v.AuxInt = int64(int64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64CMPshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPshiftRL (MOVDconst [c]) x [d])
+	// cond:
+	// result: (InvertFlags (CMPconst [c] (SRLconst <x.Type> x [d])))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v0.AuxInt = c
+		v1 := b.NewValue0(v.Line, OpARM64SRLconst, x.Type)
+		v1.AuxInt = d
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (CMPshiftRL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (CMPconst x [int64(uint64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64CMPconst)
+		v.AuxInt = int64(uint64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64CSELULT(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CSELULT x (MOVDconst [0]) flag)
+	// cond:
+	// result: (CSELULT0 x flag)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		flag := v.Args[2]
+		v.reset(OpARM64CSELULT0)
+		v.AddArg(x)
+		v.AddArg(flag)
+		return true
+	}
+	// match: (CSELULT _ y (FlagEQ))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (CSELULT x _ (FlagLT_ULT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (CSELULT _ y (FlagLT_UGT))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (CSELULT x _ (FlagGT_ULT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (CSELULT _ y (FlagGT_UGT))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64CSELULT0(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CSELULT0 _ (FlagEQ))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (CSELULT0 x (FlagLT_ULT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (CSELULT0 _ (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (CSELULT0 x (FlagGT_ULT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (CSELULT0 _ (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64DIV(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (DIV   (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(c)/int64(d)])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(c) / int64(d)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64DIVW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (DIVW  (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(int32(c)/int32(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(int32(c) / int32(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64Equal(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Equal (FlagEQ))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (Equal (FlagLT_ULT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Equal (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Equal (FlagGT_ULT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Equal (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Equal (InvertFlags x))
+	// cond:
+	// result: (Equal x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64Equal)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64FMOVDload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVDload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: (off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (FMOVDload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64FMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (FMOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64FMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64FMOVDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: (off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (FMOVDstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64FMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (FMOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64FMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64FMOVSload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVSload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (FMOVSload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64FMOVSload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (FMOVSload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64FMOVSload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64FMOVSstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVSstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (FMOVSstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64FMOVSstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (FMOVSstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64FMOVSstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64GreaterEqual(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GreaterEqual (FlagEQ))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqual (FlagLT_ULT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterEqual (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterEqual (FlagGT_ULT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqual (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqual (InvertFlags x))
+	// cond:
+	// result: (LessEqual x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64LessEqual)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64GreaterEqualU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GreaterEqualU (FlagEQ))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqualU (FlagLT_ULT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterEqualU (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqualU (FlagGT_ULT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterEqualU (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqualU (InvertFlags x))
+	// cond:
+	// result: (LessEqualU x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64LessEqualU)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64GreaterThan(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GreaterThan (FlagEQ))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThan (FlagLT_ULT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThan (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThan (FlagGT_ULT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterThan (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterThan (InvertFlags x))
+	// cond:
+	// result: (LessThan x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64LessThan)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64GreaterThanU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GreaterThanU (FlagEQ))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThanU (FlagLT_ULT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThanU (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterThanU (FlagGT_ULT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThanU (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterThanU (InvertFlags x))
+	// cond:
+	// result: (LessThanU x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64LessThanU)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64LessEqual(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LessEqual (FlagEQ))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqual (FlagLT_ULT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqual (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqual (FlagGT_ULT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessEqual (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessEqual (InvertFlags x))
+	// cond:
+	// result: (GreaterEqual x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64GreaterEqual)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64LessEqualU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LessEqualU (FlagEQ))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqualU (FlagLT_ULT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqualU (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessEqualU (FlagGT_ULT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqualU (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessEqualU (InvertFlags x))
+	// cond:
+	// result: (GreaterEqualU x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64GreaterEqualU)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64LessThan(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LessThan (FlagEQ))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThan (FlagLT_ULT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessThan (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessThan (FlagGT_ULT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThan (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThan (InvertFlags x))
+	// cond:
+	// result: (GreaterThan x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64GreaterThan)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64LessThanU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LessThanU (FlagEQ))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThanU (FlagLT_ULT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessThanU (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThanU (FlagGT_ULT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessThanU (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThanU (InvertFlags x))
+	// cond:
+	// result: (GreaterThanU x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64GreaterThanU)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOD   (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(c)%int64(d)])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(c) % int64(d)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MODW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MODW  (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(int32(c)%int32(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(int32(c) % int32(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVBUload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBUload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond:
+	// result: (MOVBUload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARM64MOVBUload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBUload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARM64MOVBUload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBUload [off] {sym} ptr (MOVBstorezero [off2] {sym2} ptr2 _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: (MOVDconst [0])
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVBstorezero {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVBUreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBUreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBUload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBUreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBUreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBUreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(uint8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(uint8(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVBload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond:
+	// result: (MOVBload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARM64MOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARM64MOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload [off] {sym} ptr (MOVBstorezero [off2] {sym2} ptr2 _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: (MOVDconst [0])
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVBstorezero {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVBreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBreg x:(MOVBload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg x:(MOVBreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg  (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(int8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(int8(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVBstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond:
+	// result: (MOVBstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVDconst [0]) mem)
+	// cond:
+	// result: (MOVBstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpARM64MOVBstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVBreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBUreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVBUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVHreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVHreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVHUreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVHUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVWUreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVWUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVBstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond:
+	// result: (MOVBstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARM64MOVBstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpARM64MOVBstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVDload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: (off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVDload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload [off] {sym} ptr (MOVDstorezero [off2] {sym2} ptr2 _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: (MOVDconst [0])
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDstorezero {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVDreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDreg x)
+	// cond: x.Uses == 1
+	// result: (MOVDnop x)
+	for {
+		x := v.Args[0]
+		if !(x.Uses == 1) {
+			break
+		}
+		v.reset(OpARM64MOVDnop)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVDreg  (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = c
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: (off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVDstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [off] {sym} ptr (MOVDconst [0]) mem)
+	// cond:
+	// result: (MOVDstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpARM64MOVDstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVDstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: (off1+off2)%2==8 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVDstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !((off1+off2)%2 == 8 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVDstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%8==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%8 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVDstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVHUload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHUload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: (off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVHUload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVHUload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHUload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVHUload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHUload [off] {sym} ptr (MOVHstorezero [off2] {sym2} ptr2 _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: (MOVDconst [0])
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVHstorezero {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVHUreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHUreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBUload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVHUload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVHUload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBUreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVHUreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVHUreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(uint16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(uint16(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVHload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: (off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVHload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHload [off] {sym} ptr (MOVHstorezero [off2] {sym2} ptr2 _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: (MOVDconst [0])
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVHstorezero {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVHreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHreg x:(MOVBload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBUload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVHload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBUreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVHreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg  (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(int16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(int16(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVHstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: (off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVHstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVDconst [0]) mem)
+	// cond:
+	// result: (MOVHstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpARM64MOVHstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVHreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHUreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVHUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVWUreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVWUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVHstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: (off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVHstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVHstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%2==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%2 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVHstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVWUload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWUload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVWUload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVWUload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWUload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVWUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVWUload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWUload [off] {sym} ptr (MOVWstorezero [off2] {sym2} ptr2 _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: (MOVDconst [0])
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVWstorezero {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVWUreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWUreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBUload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg x:(MOVHUload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVHUload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg x:(MOVWUload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVWUload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBUreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg x:(MOVHUreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVHUreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg x:(MOVWUreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVWUreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(uint32(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(uint32(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVWload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVWload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off] {sym} ptr (MOVWstorezero [off2] {sym2} ptr2 _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: (MOVDconst [0])
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVWstorezero {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVWreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWreg x:(MOVBload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBUload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVHload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHUload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVHUload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVWload _ _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVWload {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVBreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVBUreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVHreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVHreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVWreg _))
+	// cond:
+	// result: (MOVDreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpARM64MOVWreg {
+			break
+		}
+		v.reset(OpARM64MOVDreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg  (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(int32(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(int32(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVWstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVWstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVDconst [0]) mem)
+	// cond:
+	// result: (MOVWstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpARM64MOVWstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWUreg x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVWUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MOVWstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstorezero [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: (off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym) && !isAuto(sym)
+	// result: (MOVWstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym) && !isAuto(sym)) {
+			break
+		}
+		v.reset(OpARM64MOVWstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) 	&& ((off1+off2)%4==0 || off1+off2<256 && off1+off2>-256 && !isArg(sym1) && !isAuto(sym1))
+	// result: (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ((off1+off2)%4 == 0 || off1+off2 < 256 && off1+off2 > -256 && !isArg(sym1) && !isAuto(sym1))) {
+			break
+		}
+		v.reset(OpARM64MOVWstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MUL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MUL x (MOVDconst [-1]))
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != -1 {
+			break
+		}
+		v.reset(OpARM64NEG)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL _ (MOVDconst [0]))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (MUL x (MOVDconst [1]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL x (MOVDconst [c]))
+	// cond: isPowerOfTwo(c)
+	// result: (SLLconst [log2(c)] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL x (MOVDconst [c]))
+	// cond: isPowerOfTwo(c-1) && c >= 3
+	// result: (ADDshiftLL x x [log2(c-1)])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c-1) && c >= 3) {
+			break
+		}
+		v.reset(OpARM64ADDshiftLL)
+		v.AuxInt = log2(c - 1)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL x (MOVDconst [c]))
+	// cond: isPowerOfTwo(c+1) && c >= 7
+	// result: (ADDshiftLL (NEG <x.Type> x) x [log2(c+1)])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c+1) && c >= 7) {
+			break
+		}
+		v.reset(OpARM64ADDshiftLL)
+		v.AuxInt = log2(c + 1)
+		v0 := b.NewValue0(v.Line, OpARM64NEG, x.Type)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL x (MOVDconst [c]))
+	// cond: c%3 == 0 && isPowerOfTwo(c/3)
+	// result: (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%3 == 0 && isPowerOfTwo(c/3)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 3)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 1
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL x (MOVDconst [c]))
+	// cond: c%5 == 0 && isPowerOfTwo(c/5)
+	// result: (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%5 == 0 && isPowerOfTwo(c/5)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 5)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 2
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL x (MOVDconst [c]))
+	// cond: c%7 == 0 && isPowerOfTwo(c/7)
+	// result: (SLLconst [log2(c/7)] (ADDshiftLL <x.Type> (NEG <x.Type> x) x [3]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%7 == 0 && isPowerOfTwo(c/7)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 7)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 3
+		v1 := b.NewValue0(v.Line, OpARM64NEG, x.Type)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL x (MOVDconst [c]))
+	// cond: c%9 == 0 && isPowerOfTwo(c/9)
+	// result: (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%9 == 0 && isPowerOfTwo(c/9)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 9)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 3
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL (MOVDconst [-1]) x)
+	// cond:
+	// result: (NEG x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_0.AuxInt != -1 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpARM64NEG)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVDconst [0]) _)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_0.AuxInt != 0 {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (MUL (MOVDconst [1]) x)
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_0.AuxInt != 1 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVDconst [c]) x)
+	// cond: isPowerOfTwo(c)
+	// result: (SLLconst [log2(c)] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVDconst [c]) x)
+	// cond: isPowerOfTwo(c)
+	// result: (SLLconst [log2(c)] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVDconst [c]) x)
+	// cond: isPowerOfTwo(c-1) && c >= 3
+	// result: (ADDshiftLL x x [log2(c-1)])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(c-1) && c >= 3) {
+			break
+		}
+		v.reset(OpARM64ADDshiftLL)
+		v.AuxInt = log2(c - 1)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVDconst [c]) x)
+	// cond: isPowerOfTwo(c+1) && c >= 7
+	// result: (ADDshiftLL (NEG <x.Type> x) x [log2(c+1)])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(c+1) && c >= 7) {
+			break
+		}
+		v.reset(OpARM64ADDshiftLL)
+		v.AuxInt = log2(c + 1)
+		v0 := b.NewValue0(v.Line, OpARM64NEG, x.Type)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVDconst [c]) x)
+	// cond: c%3 == 0 && isPowerOfTwo(c/3)
+	// result: (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%3 == 0 && isPowerOfTwo(c/3)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 3)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 1
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL (MOVDconst [c]) x)
+	// cond: c%5 == 0 && isPowerOfTwo(c/5)
+	// result: (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%5 == 0 && isPowerOfTwo(c/5)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 5)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 2
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL (MOVDconst [c]) x)
+	// cond: c%7 == 0 && isPowerOfTwo(c/7)
+	// result: (SLLconst [log2(c/7)] (ADDshiftLL <x.Type> (NEG <x.Type> x) x [3]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%7 == 0 && isPowerOfTwo(c/7)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 7)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 3
+		v1 := b.NewValue0(v.Line, OpARM64NEG, x.Type)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL (MOVDconst [c]) x)
+	// cond: c%9 == 0 && isPowerOfTwo(c/9)
+	// result: (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%9 == 0 && isPowerOfTwo(c/9)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 9)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 3
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MUL   (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c*d])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = c * d
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MULW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MULW x (MOVDconst [c]))
+	// cond: int32(c)==-1
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpARM64NEG)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULW _ (MOVDconst [c]))
+	// cond: int32(c)==0
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (MULW x (MOVDconst [c]))
+	// cond: int32(c)==1
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(int32(c) == 1) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULW x (MOVDconst [c]))
+	// cond: isPowerOfTwo(c)
+	// result: (SLLconst [log2(c)] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULW x (MOVDconst [c]))
+	// cond: isPowerOfTwo(c-1) && int32(c) >= 3
+	// result: (ADDshiftLL x x [log2(c-1)])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c-1) && int32(c) >= 3) {
+			break
+		}
+		v.reset(OpARM64ADDshiftLL)
+		v.AuxInt = log2(c - 1)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULW x (MOVDconst [c]))
+	// cond: isPowerOfTwo(c+1) && int32(c) >= 7
+	// result: (ADDshiftLL (NEG <x.Type> x) x [log2(c+1)])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c+1) && int32(c) >= 7) {
+			break
+		}
+		v.reset(OpARM64ADDshiftLL)
+		v.AuxInt = log2(c + 1)
+		v0 := b.NewValue0(v.Line, OpARM64NEG, x.Type)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULW x (MOVDconst [c]))
+	// cond: c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)
+	// result: (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 3)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 1
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULW x (MOVDconst [c]))
+	// cond: c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)
+	// result: (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 5)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 2
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULW x (MOVDconst [c]))
+	// cond: c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)
+	// result: (SLLconst [log2(c/7)] (ADDshiftLL <x.Type> (NEG <x.Type> x) x [3]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 7)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 3
+		v1 := b.NewValue0(v.Line, OpARM64NEG, x.Type)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULW x (MOVDconst [c]))
+	// cond: c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)
+	// result: (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 9)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 3
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULW (MOVDconst [c]) x)
+	// cond: int32(c)==-1
+	// result: (NEG x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpARM64NEG)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULW (MOVDconst [c]) _)
+	// cond: int32(c)==0
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (MULW (MOVDconst [c]) x)
+	// cond: int32(c)==1
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(int32(c) == 1) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULW (MOVDconst [c]) x)
+	// cond: isPowerOfTwo(c)
+	// result: (SLLconst [log2(c)] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULW (MOVDconst [c]) x)
+	// cond: isPowerOfTwo(c-1) && int32(c) >= 3
+	// result: (ADDshiftLL x x [log2(c-1)])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(c-1) && int32(c) >= 3) {
+			break
+		}
+		v.reset(OpARM64ADDshiftLL)
+		v.AuxInt = log2(c - 1)
+		v.AddArg(x)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULW (MOVDconst [c]) x)
+	// cond: isPowerOfTwo(c+1) && int32(c) >= 7
+	// result: (ADDshiftLL (NEG <x.Type> x) x [log2(c+1)])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(c+1) && int32(c) >= 7) {
+			break
+		}
+		v.reset(OpARM64ADDshiftLL)
+		v.AuxInt = log2(c + 1)
+		v0 := b.NewValue0(v.Line, OpARM64NEG, x.Type)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULW (MOVDconst [c]) x)
+	// cond: c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)
+	// result: (SLLconst [log2(c/3)] (ADDshiftLL <x.Type> x x [1]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%3 == 0 && isPowerOfTwo(c/3) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 3)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 1
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULW (MOVDconst [c]) x)
+	// cond: c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)
+	// result: (SLLconst [log2(c/5)] (ADDshiftLL <x.Type> x x [2]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%5 == 0 && isPowerOfTwo(c/5) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 5)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 2
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULW (MOVDconst [c]) x)
+	// cond: c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)
+	// result: (SLLconst [log2(c/7)] (ADDshiftLL <x.Type> (NEG <x.Type> x) x [3]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%7 == 0 && isPowerOfTwo(c/7) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 7)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 3
+		v1 := b.NewValue0(v.Line, OpARM64NEG, x.Type)
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULW (MOVDconst [c]) x)
+	// cond: c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)
+	// result: (SLLconst [log2(c/9)] (ADDshiftLL <x.Type> x x [3]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(c%9 == 0 && isPowerOfTwo(c/9) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = log2(c / 9)
+		v0 := b.NewValue0(v.Line, OpARM64ADDshiftLL, x.Type)
+		v0.AuxInt = 3
+		v0.AddArg(x)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (MULW  (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(int32(c)*int32(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(int32(c) * int32(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64MVN(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MVN (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [^c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = ^c
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64NEG(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NEG (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [-c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = -c
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64NotEqual(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NotEqual (FlagEQ))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagEQ {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (NotEqual (FlagLT_ULT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (NotEqual (FlagLT_UGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagLT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (NotEqual (FlagGT_ULT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_ULT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (NotEqual (FlagGT_UGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64FlagGT_UGT {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (NotEqual (InvertFlags x))
+	// cond:
+	// result: (NotEqual x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpARM64NotEqual)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64OR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OR  (MOVDconst [c]) x)
+	// cond:
+	// result: (ORconst  [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR  x (MOVDconst [c]))
+	// cond:
+	// result: (ORconst  [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR  x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR  x s:(SLLconst [c] y))
+	// cond: s.Uses == 1 && clobber(s)
+	// result: (ORshiftLL  x y [c])
+	for {
+		x := v.Args[0]
+		s := v.Args[1]
+		if s.Op != OpARM64SLLconst {
+			break
+		}
+		c := s.AuxInt
+		y := s.Args[0]
+		if !(s.Uses == 1 && clobber(s)) {
+			break
+		}
+		v.reset(OpARM64ORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR  s:(SLLconst [c] y) x)
+	// cond: s.Uses == 1 && clobber(s)
+	// result: (ORshiftLL  x y [c])
+	for {
+		s := v.Args[0]
+		if s.Op != OpARM64SLLconst {
+			break
+		}
+		c := s.AuxInt
+		y := s.Args[0]
+		x := v.Args[1]
+		if !(s.Uses == 1 && clobber(s)) {
+			break
+		}
+		v.reset(OpARM64ORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR  x (SLLconst [c] y))
+	// cond:
+	// result: (ORshiftLL  x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64ORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR  (SLLconst [c] y) x)
+	// cond:
+	// result: (ORshiftLL  x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64ORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR  x (SRLconst [c] y))
+	// cond:
+	// result: (ORshiftRL  x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64ORshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR  (SRLconst [c] y) x)
+	// cond:
+	// result: (ORshiftRL  x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64ORshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR  x (SRAconst [c] y))
+	// cond:
+	// result: (ORshiftRA  x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64ORshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR  (SRAconst [c] y) x)
+	// cond:
+	// result: (ORshiftRA  x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64ORshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (OR <t> o0:(ORshiftLL [8] o1:(ORshiftLL [16] s0:(SLLconst [24] 	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem))) 	y1:(MOVDnop x1:(MOVBUload [i-1] {s} p mem))) 	y2:(MOVDnop x2:(MOVBUload [i-2] {s} p mem))) 	y3:(MOVDnop x3:(MOVBUload [i-3] {s} p mem)))
+	// cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 	&& o0.Uses == 1 && o1.Uses == 1 && s0.Uses == 1 	&& mergePoint(b,x0,x1,x2,x3) != nil 	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) 	&& clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3) 	&& clobber(o0) && clobber(o1) && clobber(s0)
+	// result: @mergePoint(b,x0,x1,x2,x3) (MOVWUload <t> {s} (OffPtr <p.Type> [i-3] p) mem)
+	for {
+		t := v.Type
+		o0 := v.Args[0]
+		if o0.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o0.AuxInt != 8 {
+			break
+		}
+		o1 := o0.Args[0]
+		if o1.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o1.AuxInt != 16 {
+			break
+		}
+		s0 := o1.Args[0]
+		if s0.Op != OpARM64SLLconst {
+			break
+		}
+		if s0.AuxInt != 24 {
+			break
+		}
+		y0 := s0.Args[0]
+		if y0.Op != OpARM64MOVDnop {
+			break
+		}
+		x0 := y0.Args[0]
+		if x0.Op != OpARM64MOVBUload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		y1 := o1.Args[1]
+		if y1.Op != OpARM64MOVDnop {
+			break
+		}
+		x1 := y1.Args[0]
+		if x1.Op != OpARM64MOVBUload {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		y2 := o0.Args[1]
+		if y2.Op != OpARM64MOVDnop {
+			break
+		}
+		x2 := y2.Args[0]
+		if x2.Op != OpARM64MOVBUload {
+			break
+		}
+		if x2.AuxInt != i-2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		y3 := v.Args[1]
+		if y3.Op != OpARM64MOVDnop {
+			break
+		}
+		x3 := y3.Args[0]
+		if x3.Op != OpARM64MOVBUload {
+			break
+		}
+		if x3.AuxInt != i-3 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		if mem != x3.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3) && clobber(o0) && clobber(o1) && clobber(s0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3)
+		v0 := b.NewValue0(v.Line, OpARM64MOVWUload, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.Aux = s
+		v1 := b.NewValue0(v.Line, OpOffPtr, p.Type)
+		v1.AuxInt = i - 3
+		v1.AddArg(p)
+		v0.AddArg(v1)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (OR <t> o0:(ORshiftLL [8] o1:(ORshiftLL [16] o2:(ORshiftLL [24] o3:(ORshiftLL [32] o4:(ORshiftLL [40] o5:(ORshiftLL [48] s0:(SLLconst [56] 	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem))) 	y1:(MOVDnop x1:(MOVBUload [i-1] {s} p mem))) 	y2:(MOVDnop x2:(MOVBUload [i-2] {s} p mem))) 	y3:(MOVDnop x3:(MOVBUload [i-3] {s} p mem))) 	y4:(MOVDnop x4:(MOVBUload [i-4] {s} p mem))) 	y5:(MOVDnop x5:(MOVBUload [i-5] {s} p mem))) 	y6:(MOVDnop x6:(MOVBUload [i-6] {s} p mem))) 	y7:(MOVDnop x7:(MOV [...]
+	// cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 	&& x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 	&& y4.Uses == 1 && y5.Uses == 1 && y6.Uses == 1 && y7.Uses == 1 	&& o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 	&& o4.Uses == 1 && o5.Uses == 1 && s0.Uses == 1 	&& mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil 	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) 	& [...]
+	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (REV <t> (MOVDload <t> {s} (OffPtr <p.Type> [i-7] p) mem))
+	for {
+		t := v.Type
+		o0 := v.Args[0]
+		if o0.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o0.AuxInt != 8 {
+			break
+		}
+		o1 := o0.Args[0]
+		if o1.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o1.AuxInt != 16 {
+			break
+		}
+		o2 := o1.Args[0]
+		if o2.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o2.AuxInt != 24 {
+			break
+		}
+		o3 := o2.Args[0]
+		if o3.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o3.AuxInt != 32 {
+			break
+		}
+		o4 := o3.Args[0]
+		if o4.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o4.AuxInt != 40 {
+			break
+		}
+		o5 := o4.Args[0]
+		if o5.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o5.AuxInt != 48 {
+			break
+		}
+		s0 := o5.Args[0]
+		if s0.Op != OpARM64SLLconst {
+			break
+		}
+		if s0.AuxInt != 56 {
+			break
+		}
+		y0 := s0.Args[0]
+		if y0.Op != OpARM64MOVDnop {
+			break
+		}
+		x0 := y0.Args[0]
+		if x0.Op != OpARM64MOVBUload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		y1 := o5.Args[1]
+		if y1.Op != OpARM64MOVDnop {
+			break
+		}
+		x1 := y1.Args[0]
+		if x1.Op != OpARM64MOVBUload {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		y2 := o4.Args[1]
+		if y2.Op != OpARM64MOVDnop {
+			break
+		}
+		x2 := y2.Args[0]
+		if x2.Op != OpARM64MOVBUload {
+			break
+		}
+		if x2.AuxInt != i-2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		y3 := o3.Args[1]
+		if y3.Op != OpARM64MOVDnop {
+			break
+		}
+		x3 := y3.Args[0]
+		if x3.Op != OpARM64MOVBUload {
+			break
+		}
+		if x3.AuxInt != i-3 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		if mem != x3.Args[1] {
+			break
+		}
+		y4 := o2.Args[1]
+		if y4.Op != OpARM64MOVDnop {
+			break
+		}
+		x4 := y4.Args[0]
+		if x4.Op != OpARM64MOVBUload {
+			break
+		}
+		if x4.AuxInt != i-4 {
+			break
+		}
+		if x4.Aux != s {
+			break
+		}
+		if p != x4.Args[0] {
+			break
+		}
+		if mem != x4.Args[1] {
+			break
+		}
+		y5 := o1.Args[1]
+		if y5.Op != OpARM64MOVDnop {
+			break
+		}
+		x5 := y5.Args[0]
+		if x5.Op != OpARM64MOVBUload {
+			break
+		}
+		if x5.AuxInt != i-5 {
+			break
+		}
+		if x5.Aux != s {
+			break
+		}
+		if p != x5.Args[0] {
+			break
+		}
+		if mem != x5.Args[1] {
+			break
+		}
+		y6 := o0.Args[1]
+		if y6.Op != OpARM64MOVDnop {
+			break
+		}
+		x6 := y6.Args[0]
+		if x6.Op != OpARM64MOVBUload {
+			break
+		}
+		if x6.AuxInt != i-6 {
+			break
+		}
+		if x6.Aux != s {
+			break
+		}
+		if p != x6.Args[0] {
+			break
+		}
+		if mem != x6.Args[1] {
+			break
+		}
+		y7 := v.Args[1]
+		if y7.Op != OpARM64MOVDnop {
+			break
+		}
+		x7 := y7.Args[0]
+		if x7.Op != OpARM64MOVBUload {
+			break
+		}
+		if x7.AuxInt != i-7 {
+			break
+		}
+		if x7.Aux != s {
+			break
+		}
+		if p != x7.Args[0] {
+			break
+		}
+		if mem != x7.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && y4.Uses == 1 && y5.Uses == 1 && y6.Uses == 1 && y7.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && c [...]
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
+		v0 := b.NewValue0(v.Line, OpARM64REV, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVDload, t)
+		v1.Aux = s
+		v2 := b.NewValue0(v.Line, OpOffPtr, p.Type)
+		v2.AuxInt = i - 7
+		v2.AddArg(p)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		return true
+	}
+	// match: (OR <t> o0:(ORshiftLL [8] o1:(ORshiftLL [16] s0:(SLLconst [24] 	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem))) 	y1:(MOVDnop x1:(MOVBUload [i+1] {s} p mem))) 	y2:(MOVDnop x2:(MOVBUload [i+2] {s} p mem))) 	y3:(MOVDnop x3:(MOVBUload [i+3] {s} p mem)))
+	// cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 	&& o0.Uses == 1 && o1.Uses == 1 && s0.Uses == 1 	&& mergePoint(b,x0,x1,x2,x3) != nil 	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) 	&& clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3) 	&& clobber(o0) && clobber(o1) && clobber(s0)
+	// result: @mergePoint(b,x0,x1,x2,x3) (REVW <t> (MOVWUload <t> {s} (OffPtr <p.Type> [i] p) mem))
+	for {
+		t := v.Type
+		o0 := v.Args[0]
+		if o0.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o0.AuxInt != 8 {
+			break
+		}
+		o1 := o0.Args[0]
+		if o1.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o1.AuxInt != 16 {
+			break
+		}
+		s0 := o1.Args[0]
+		if s0.Op != OpARM64SLLconst {
+			break
+		}
+		if s0.AuxInt != 24 {
+			break
+		}
+		y0 := s0.Args[0]
+		if y0.Op != OpARM64MOVDnop {
+			break
+		}
+		x0 := y0.Args[0]
+		if x0.Op != OpARM64MOVBUload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		y1 := o1.Args[1]
+		if y1.Op != OpARM64MOVDnop {
+			break
+		}
+		x1 := y1.Args[0]
+		if x1.Op != OpARM64MOVBUload {
+			break
+		}
+		if x1.AuxInt != i+1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		y2 := o0.Args[1]
+		if y2.Op != OpARM64MOVDnop {
+			break
+		}
+		x2 := y2.Args[0]
+		if x2.Op != OpARM64MOVBUload {
+			break
+		}
+		if x2.AuxInt != i+2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		y3 := v.Args[1]
+		if y3.Op != OpARM64MOVDnop {
+			break
+		}
+		x3 := y3.Args[0]
+		if x3.Op != OpARM64MOVBUload {
+			break
+		}
+		if x3.AuxInt != i+3 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		if mem != x3.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1, x2, x3) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3) && clobber(o0) && clobber(o1) && clobber(s0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3)
+		v0 := b.NewValue0(v.Line, OpARM64REVW, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVWUload, t)
+		v1.Aux = s
+		v2 := b.NewValue0(v.Line, OpOffPtr, p.Type)
+		v2.AuxInt = i
+		v2.AddArg(p)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		return true
+	}
+	// match: (OR <t> o0:(ORshiftLL [8] o1:(ORshiftLL [16] o2:(ORshiftLL [24] o3:(ORshiftLL [32] o4:(ORshiftLL [40] o5:(ORshiftLL [48] s0:(SLLconst [56] 	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem))) 	y1:(MOVDnop x1:(MOVBUload [i+1] {s} p mem))) 	y2:(MOVDnop x2:(MOVBUload [i+2] {s} p mem))) 	y3:(MOVDnop x3:(MOVBUload [i+3] {s} p mem))) 	y4:(MOVDnop x4:(MOVBUload [i+4] {s} p mem))) 	y5:(MOVDnop x5:(MOVBUload [i+5] {s} p mem))) 	y6:(MOVDnop x6:(MOVBUload [i+6] {s} p mem))) 	y7:(MOVDnop x7:(MOV [...]
+	// cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 	&& x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 	&& y4.Uses == 1 && y5.Uses == 1 && y6.Uses == 1 && y7.Uses == 1 	&& o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 	&& o4.Uses == 1 && o5.Uses == 1 && s0.Uses == 1 	&& mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil 	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) 	& [...]
+	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (REV <t> (MOVDload <t> {s} (OffPtr <p.Type> [i] p) mem))
+	for {
+		t := v.Type
+		o0 := v.Args[0]
+		if o0.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o0.AuxInt != 8 {
+			break
+		}
+		o1 := o0.Args[0]
+		if o1.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o1.AuxInt != 16 {
+			break
+		}
+		o2 := o1.Args[0]
+		if o2.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o2.AuxInt != 24 {
+			break
+		}
+		o3 := o2.Args[0]
+		if o3.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o3.AuxInt != 32 {
+			break
+		}
+		o4 := o3.Args[0]
+		if o4.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o4.AuxInt != 40 {
+			break
+		}
+		o5 := o4.Args[0]
+		if o5.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o5.AuxInt != 48 {
+			break
+		}
+		s0 := o5.Args[0]
+		if s0.Op != OpARM64SLLconst {
+			break
+		}
+		if s0.AuxInt != 56 {
+			break
+		}
+		y0 := s0.Args[0]
+		if y0.Op != OpARM64MOVDnop {
+			break
+		}
+		x0 := y0.Args[0]
+		if x0.Op != OpARM64MOVBUload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		y1 := o5.Args[1]
+		if y1.Op != OpARM64MOVDnop {
+			break
+		}
+		x1 := y1.Args[0]
+		if x1.Op != OpARM64MOVBUload {
+			break
+		}
+		if x1.AuxInt != i+1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		y2 := o4.Args[1]
+		if y2.Op != OpARM64MOVDnop {
+			break
+		}
+		x2 := y2.Args[0]
+		if x2.Op != OpARM64MOVBUload {
+			break
+		}
+		if x2.AuxInt != i+2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		y3 := o3.Args[1]
+		if y3.Op != OpARM64MOVDnop {
+			break
+		}
+		x3 := y3.Args[0]
+		if x3.Op != OpARM64MOVBUload {
+			break
+		}
+		if x3.AuxInt != i+3 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		if mem != x3.Args[1] {
+			break
+		}
+		y4 := o2.Args[1]
+		if y4.Op != OpARM64MOVDnop {
+			break
+		}
+		x4 := y4.Args[0]
+		if x4.Op != OpARM64MOVBUload {
+			break
+		}
+		if x4.AuxInt != i+4 {
+			break
+		}
+		if x4.Aux != s {
+			break
+		}
+		if p != x4.Args[0] {
+			break
+		}
+		if mem != x4.Args[1] {
+			break
+		}
+		y5 := o1.Args[1]
+		if y5.Op != OpARM64MOVDnop {
+			break
+		}
+		x5 := y5.Args[0]
+		if x5.Op != OpARM64MOVBUload {
+			break
+		}
+		if x5.AuxInt != i+5 {
+			break
+		}
+		if x5.Aux != s {
+			break
+		}
+		if p != x5.Args[0] {
+			break
+		}
+		if mem != x5.Args[1] {
+			break
+		}
+		y6 := o0.Args[1]
+		if y6.Op != OpARM64MOVDnop {
+			break
+		}
+		x6 := y6.Args[0]
+		if x6.Op != OpARM64MOVBUload {
+			break
+		}
+		if x6.AuxInt != i+6 {
+			break
+		}
+		if x6.Aux != s {
+			break
+		}
+		if p != x6.Args[0] {
+			break
+		}
+		if mem != x6.Args[1] {
+			break
+		}
+		y7 := v.Args[1]
+		if y7.Op != OpARM64MOVDnop {
+			break
+		}
+		x7 := y7.Args[0]
+		if x7.Op != OpARM64MOVBUload {
+			break
+		}
+		if x7.AuxInt != i+7 {
+			break
+		}
+		if x7.Aux != s {
+			break
+		}
+		if p != x7.Args[0] {
+			break
+		}
+		if mem != x7.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && y4.Uses == 1 && y5.Uses == 1 && y6.Uses == 1 && y7.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && c [...]
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
+		v0 := b.NewValue0(v.Line, OpARM64REV, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVDload, t)
+		v1.Aux = s
+		v2 := b.NewValue0(v.Line, OpOffPtr, p.Type)
+		v2.AuxInt = i
+		v2.AddArg(p)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORconst  [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORconst  [-1] _)
+	// cond:
+	// result: (MOVDconst [-1])
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (ORconst  [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c|d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = c | d
+		return true
+	}
+	// match: (ORconst  [c] (ORconst [d] x))
+	// cond:
+	// result: (ORconst [c|d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ORconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARM64ORconst)
+		v.AuxInt = c | d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ORshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORshiftLL  (MOVDconst [c]) x [d])
+	// cond:
+	// result: (ORconst  [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ORshiftLL  x (MOVDconst [c]) [d])
+	// cond:
+	// result: (ORconst  x [int64(uint64(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ORconst)
+		v.AuxInt = int64(uint64(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORshiftLL  x y:(SLLconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARM64SLLconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (ORshiftLL <t> [8] 	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem)) 	y1:(MOVDnop x1:(MOVBUload [i+1] {s} p mem)))
+	// cond: x0.Uses == 1 && x1.Uses == 1 	&& y0.Uses == 1 && y1.Uses == 1 	&& mergePoint(b,x0,x1) != nil 	&& clobber(x0) && clobber(x1) 	&& clobber(y0) && clobber(y1)
+	// result: @mergePoint(b,x0,x1) (MOVHUload <t> {s} (OffPtr <p.Type> [i] p) mem)
+	for {
+		t := v.Type
+		if v.AuxInt != 8 {
+			break
+		}
+		y0 := v.Args[0]
+		if y0.Op != OpARM64MOVDnop {
+			break
+		}
+		x0 := y0.Args[0]
+		if x0.Op != OpARM64MOVBUload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		y1 := v.Args[1]
+		if y1.Op != OpARM64MOVDnop {
+			break
+		}
+		x1 := y1.Args[0]
+		if x1.Op != OpARM64MOVBUload {
+			break
+		}
+		if x1.AuxInt != i+1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(y0) && clobber(y1)) {
+			break
+		}
+		b = mergePoint(b, x0, x1)
+		v0 := b.NewValue0(v.Line, OpARM64MOVHUload, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.Aux = s
+		v1 := b.NewValue0(v.Line, OpOffPtr, p.Type)
+		v1.AuxInt = i
+		v1.AddArg(p)
+		v0.AddArg(v1)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (ORshiftLL <t> [24] o0:(ORshiftLL [16] 	            x0:(MOVHUload [i]   {s} p mem) 	y1:(MOVDnop x1:(MOVBUload [i+2] {s} p mem))) 	y2:(MOVDnop x2:(MOVBUload [i+3] {s} p mem)))
+	// cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 	&& y1.Uses == 1 && y2.Uses == 1 	&& o0.Uses == 1 	&& mergePoint(b,x0,x1,x2) != nil 	&& clobber(x0) && clobber(x1) && clobber(x2) 	&& clobber(y1) && clobber(y2) 	&& clobber(o0)
+	// result: @mergePoint(b,x0,x1,x2) (MOVWUload <t> {s} (OffPtr <p.Type> [i] p) mem)
+	for {
+		t := v.Type
+		if v.AuxInt != 24 {
+			break
+		}
+		o0 := v.Args[0]
+		if o0.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o0.AuxInt != 16 {
+			break
+		}
+		x0 := o0.Args[0]
+		if x0.Op != OpARM64MOVHUload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		y1 := o0.Args[1]
+		if y1.Op != OpARM64MOVDnop {
+			break
+		}
+		x1 := y1.Args[0]
+		if x1.Op != OpARM64MOVBUload {
+			break
+		}
+		if x1.AuxInt != i+2 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		y2 := v.Args[1]
+		if y2.Op != OpARM64MOVDnop {
+			break
+		}
+		x2 := y2.Args[0]
+		if x2.Op != OpARM64MOVBUload {
+			break
+		}
+		if x2.AuxInt != i+3 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && o0.Uses == 1 && mergePoint(b, x0, x1, x2) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(y1) && clobber(y2) && clobber(o0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2)
+		v0 := b.NewValue0(v.Line, OpARM64MOVWUload, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.Aux = s
+		v1 := b.NewValue0(v.Line, OpOffPtr, p.Type)
+		v1.AuxInt = i
+		v1.AddArg(p)
+		v0.AddArg(v1)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (ORshiftLL <t> [56] o0:(ORshiftLL [48] o1:(ORshiftLL [40] o2:(ORshiftLL [32] 	            x0:(MOVWUload [i]   {s} p mem) 	y1:(MOVDnop x1:(MOVBUload [i+4] {s} p mem))) 	y2:(MOVDnop x2:(MOVBUload [i+5] {s} p mem))) 	y3:(MOVDnop x3:(MOVBUload [i+6] {s} p mem))) 	y4:(MOVDnop x4:(MOVBUload [i+7] {s} p mem)))
+	// cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 	&& y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && y4.Uses == 1 	&& o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 	&& mergePoint(b,x0,x1,x2,x3,x4) != nil 	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) 	&& clobber(y1) && clobber(y2) && clobber(y3) && clobber(y4) 	&& clobber(o0) && clobber(o1) && clobber(o2)
+	// result: @mergePoint(b,x0,x1,x2,x3,x4) (MOVDload <t> {s} (OffPtr <p.Type> [i] p) mem)
+	for {
+		t := v.Type
+		if v.AuxInt != 56 {
+			break
+		}
+		o0 := v.Args[0]
+		if o0.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o0.AuxInt != 48 {
+			break
+		}
+		o1 := o0.Args[0]
+		if o1.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o1.AuxInt != 40 {
+			break
+		}
+		o2 := o1.Args[0]
+		if o2.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o2.AuxInt != 32 {
+			break
+		}
+		x0 := o2.Args[0]
+		if x0.Op != OpARM64MOVWUload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		y1 := o2.Args[1]
+		if y1.Op != OpARM64MOVDnop {
+			break
+		}
+		x1 := y1.Args[0]
+		if x1.Op != OpARM64MOVBUload {
+			break
+		}
+		if x1.AuxInt != i+4 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		y2 := o1.Args[1]
+		if y2.Op != OpARM64MOVDnop {
+			break
+		}
+		x2 := y2.Args[0]
+		if x2.Op != OpARM64MOVBUload {
+			break
+		}
+		if x2.AuxInt != i+5 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		y3 := o0.Args[1]
+		if y3.Op != OpARM64MOVDnop {
+			break
+		}
+		x3 := y3.Args[0]
+		if x3.Op != OpARM64MOVBUload {
+			break
+		}
+		if x3.AuxInt != i+6 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		if mem != x3.Args[1] {
+			break
+		}
+		y4 := v.Args[1]
+		if y4.Op != OpARM64MOVDnop {
+			break
+		}
+		x4 := y4.Args[0]
+		if x4.Op != OpARM64MOVBUload {
+			break
+		}
+		if x4.AuxInt != i+7 {
+			break
+		}
+		if x4.Aux != s {
+			break
+		}
+		if p != x4.Args[0] {
+			break
+		}
+		if mem != x4.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && y4.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(y1) && clobber(y2) && clobber(y3) && clobber(y4) && clobber(o0) && clobber(o1) && clobber(o2)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDload, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.Aux = s
+		v1 := b.NewValue0(v.Line, OpOffPtr, p.Type)
+		v1.AuxInt = i
+		v1.AddArg(p)
+		v0.AddArg(v1)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (ORshiftLL <t> [8] 	y0:(MOVDnop x0:(MOVBUload [i]   {s} p mem)) 	y1:(MOVDnop x1:(MOVBUload [i-1] {s} p mem)))
+	// cond: ((i-1)%2 == 0 || i-1<256 && i-1>-256 && !isArg(s) && !isAuto(s)) 	&& x0.Uses == 1 && x1.Uses == 1 	&& y0.Uses == 1 && y1.Uses == 1 	&& mergePoint(b,x0,x1) != nil 	&& clobber(x0) && clobber(x1) 	&& clobber(y0) && clobber(y1)
+	// result: @mergePoint(b,x0,x1) (REV16W <t> (MOVHUload <t> [i-1] {s} p mem))
+	for {
+		t := v.Type
+		if v.AuxInt != 8 {
+			break
+		}
+		y0 := v.Args[0]
+		if y0.Op != OpARM64MOVDnop {
+			break
+		}
+		x0 := y0.Args[0]
+		if x0.Op != OpARM64MOVBUload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		y1 := v.Args[1]
+		if y1.Op != OpARM64MOVDnop {
+			break
+		}
+		x1 := y1.Args[0]
+		if x1.Op != OpARM64MOVBUload {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		if !(((i-1)%2 == 0 || i-1 < 256 && i-1 > -256 && !isArg(s) && !isAuto(s)) && x0.Uses == 1 && x1.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(y0) && clobber(y1)) {
+			break
+		}
+		b = mergePoint(b, x0, x1)
+		v0 := b.NewValue0(v.Line, OpARM64REV16W, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVHUload, t)
+		v1.AuxInt = i - 1
+		v1.Aux = s
+		v1.AddArg(p)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		return true
+	}
+	// match: (ORshiftLL <t> [24] o0:(ORshiftLL [16] 	y0:(REV16W  x0:(MOVHUload [i]   {s} p mem)) 	y1:(MOVDnop x1:(MOVBUload [i-1] {s} p mem))) 	y2:(MOVDnop x2:(MOVBUload [i-2] {s} p mem)))
+	// cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 	&& o0.Uses == 1 	&& mergePoint(b,x0,x1,x2) != nil 	&& clobber(x0) && clobber(x1) && clobber(x2) 	&& clobber(y0) && clobber(y1) && clobber(y2) 	&& clobber(o0)
+	// result: @mergePoint(b,x0,x1,x2) (REVW <t> (MOVWUload <t> {s} (OffPtr <p.Type> [i-2] p) mem))
+	for {
+		t := v.Type
+		if v.AuxInt != 24 {
+			break
+		}
+		o0 := v.Args[0]
+		if o0.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o0.AuxInt != 16 {
+			break
+		}
+		y0 := o0.Args[0]
+		if y0.Op != OpARM64REV16W {
+			break
+		}
+		x0 := y0.Args[0]
+		if x0.Op != OpARM64MOVHUload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		y1 := o0.Args[1]
+		if y1.Op != OpARM64MOVDnop {
+			break
+		}
+		x1 := y1.Args[0]
+		if x1.Op != OpARM64MOVBUload {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		y2 := v.Args[1]
+		if y2.Op != OpARM64MOVDnop {
+			break
+		}
+		x2 := y2.Args[0]
+		if x2.Op != OpARM64MOVBUload {
+			break
+		}
+		if x2.AuxInt != i-2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && o0.Uses == 1 && mergePoint(b, x0, x1, x2) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(y0) && clobber(y1) && clobber(y2) && clobber(o0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2)
+		v0 := b.NewValue0(v.Line, OpARM64REVW, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVWUload, t)
+		v1.Aux = s
+		v2 := b.NewValue0(v.Line, OpOffPtr, p.Type)
+		v2.AuxInt = i - 2
+		v2.AddArg(p)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		return true
+	}
+	// match: (ORshiftLL <t> [56] o0:(ORshiftLL [48] o1:(ORshiftLL [40] o2:(ORshiftLL [32] 	y0:(REVW    x0:(MOVWUload [i]   {s} p mem)) 	y1:(MOVDnop x1:(MOVBUload [i-1] {s} p mem))) 	y2:(MOVDnop x2:(MOVBUload [i-2] {s} p mem))) 	y3:(MOVDnop x3:(MOVBUload [i-3] {s} p mem))) 	y4:(MOVDnop x4:(MOVBUload [i-4] {s} p mem)))
+	// cond: x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 	&& y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && y4.Uses == 1 	&& o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 	&& mergePoint(b,x0,x1,x2,x3,x4) != nil 	&& clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) 	&& clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3) && clobber(y4) 	&& clobber(o0) && clobber(o1) && clobber(o2)
+	// result: @mergePoint(b,x0,x1,x2,x3,x4) (REV <t> (MOVDload <t> {s} (OffPtr <p.Type> [i-4] p) mem))
+	for {
+		t := v.Type
+		if v.AuxInt != 56 {
+			break
+		}
+		o0 := v.Args[0]
+		if o0.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o0.AuxInt != 48 {
+			break
+		}
+		o1 := o0.Args[0]
+		if o1.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o1.AuxInt != 40 {
+			break
+		}
+		o2 := o1.Args[0]
+		if o2.Op != OpARM64ORshiftLL {
+			break
+		}
+		if o2.AuxInt != 32 {
+			break
+		}
+		y0 := o2.Args[0]
+		if y0.Op != OpARM64REVW {
+			break
+		}
+		x0 := y0.Args[0]
+		if x0.Op != OpARM64MOVWUload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		y1 := o2.Args[1]
+		if y1.Op != OpARM64MOVDnop {
+			break
+		}
+		x1 := y1.Args[0]
+		if x1.Op != OpARM64MOVBUload {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		y2 := o1.Args[1]
+		if y2.Op != OpARM64MOVDnop {
+			break
+		}
+		x2 := y2.Args[0]
+		if x2.Op != OpARM64MOVBUload {
+			break
+		}
+		if x2.AuxInt != i-2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		y3 := o0.Args[1]
+		if y3.Op != OpARM64MOVDnop {
+			break
+		}
+		x3 := y3.Args[0]
+		if x3.Op != OpARM64MOVBUload {
+			break
+		}
+		if x3.AuxInt != i-3 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		if mem != x3.Args[1] {
+			break
+		}
+		y4 := v.Args[1]
+		if y4.Op != OpARM64MOVDnop {
+			break
+		}
+		x4 := y4.Args[0]
+		if x4.Op != OpARM64MOVBUload {
+			break
+		}
+		if x4.AuxInt != i-4 {
+			break
+		}
+		if x4.Aux != s {
+			break
+		}
+		if p != x4.Args[0] {
+			break
+		}
+		if mem != x4.Args[1] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && y0.Uses == 1 && y1.Uses == 1 && y2.Uses == 1 && y3.Uses == 1 && y4.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(y0) && clobber(y1) && clobber(y2) && clobber(y3) && clobber(y4) && clobber(o0) && clobber(o1) && clobber(o2)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4)
+		v0 := b.NewValue0(v.Line, OpARM64REV, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVDload, t)
+		v1.Aux = s
+		v2 := b.NewValue0(v.Line, OpOffPtr, p.Type)
+		v2.AuxInt = i - 4
+		v2.AddArg(p)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ORshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORshiftRA  (MOVDconst [c]) x [d])
+	// cond:
+	// result: (ORconst  [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ORshiftRA  x (MOVDconst [c]) [d])
+	// cond:
+	// result: (ORconst  x [int64(int64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ORconst)
+		v.AuxInt = int64(int64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORshiftRA  x y:(SRAconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARM64SRAconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64ORshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORshiftRL  (MOVDconst [c]) x [d])
+	// cond:
+	// result: (ORconst  [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64ORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (ORshiftRL  x (MOVDconst [c]) [d])
+	// cond:
+	// result: (ORconst  x [int64(uint64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64ORconst)
+		v.AuxInt = int64(uint64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORshiftRL  x y:(SRLconst x [c]) [d])
+	// cond: c==d
+	// result: y
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		y := v.Args[1]
+		if y.Op != OpARM64SRLconst {
+			break
+		}
+		c := y.AuxInt
+		if x != y.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SLL x (MOVDconst [c]))
+	// cond:
+	// result: (SLLconst x [c&63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SLLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SLLconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(d)<<uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(d) << uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRA x (MOVDconst [c]))
+	// cond:
+	// result: (SRAconst x [c&63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SRAconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRAconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(d)>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(d) >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRL x (MOVDconst [c]))
+	// cond:
+	// result: (SRLconst x [c&63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64SRLconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SRLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRLconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(uint64(d)>>uint64(c))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(uint64(d) >> uint64(c))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SUB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUB x (MOVDconst [c]))
+	// cond:
+	// result: (SUBconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64SUBconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUB x x)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SUB x (SLLconst [c] y))
+	// cond:
+	// result: (SUBshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64SUBshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUB x (SRLconst [c] y))
+	// cond:
+	// result: (SUBshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64SUBshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (SUB x (SRAconst [c] y))
+	// cond:
+	// result: (SUBshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64SUBshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SUBconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBconst [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [d-c])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = d - c
+		return true
+	}
+	// match: (SUBconst [c] (SUBconst [d] x))
+	// cond:
+	// result: (ADDconst [-c-d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SUBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = -c - d
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBconst [c] (ADDconst [d] x))
+	// cond:
+	// result: (ADDconst [-c+d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64ADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = -c + d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SUBshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBshiftLL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (SUBconst x [int64(uint64(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64SUBconst)
+		v.AuxInt = int64(uint64(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBshiftLL x (SLLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVDconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SUBshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBshiftRA x (MOVDconst [c]) [d])
+	// cond:
+	// result: (SUBconst x [int64(int64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64SUBconst)
+		v.AuxInt = int64(int64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBshiftRA x (SRAconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVDconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64SUBshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBshiftRL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (SUBconst x [int64(uint64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64SUBconst)
+		v.AuxInt = int64(uint64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBshiftRL x (SRLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVDconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64UDIV(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (UDIV x (MOVDconst [1]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (UDIV x (MOVDconst [c]))
+	// cond: isPowerOfTwo(c)
+	// result: (SRLconst [log2(c)] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARM64SRLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (UDIV  (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(uint64(c)/uint64(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(uint64(c) / uint64(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64UDIVW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (UDIVW x (MOVDconst [c]))
+	// cond: uint32(c)==1
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) == 1) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (UDIVW x (MOVDconst [c]))
+	// cond: isPowerOfTwo(c) && is32Bit(c)
+	// result: (SRLconst [log2(c)] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARM64SRLconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (UDIVW (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(uint32(c)/uint32(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(uint32(c) / uint32(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64UMOD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (UMOD _ (MOVDconst [1]))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (UMOD x (MOVDconst [c]))
+	// cond: isPowerOfTwo(c)
+	// result: (ANDconst [c-1] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = c - 1
+		v.AddArg(x)
+		return true
+	}
+	// match: (UMOD  (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(uint64(c)%uint64(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(uint64(c) % uint64(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64UMODW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (UMODW _ (MOVDconst [c]))
+	// cond: uint32(c)==1
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) == 1) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (UMODW x (MOVDconst [c]))
+	// cond: isPowerOfTwo(c) && is32Bit(c)
+	// result: (ANDconst [c-1] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isPowerOfTwo(c) && is32Bit(c)) {
+			break
+		}
+		v.reset(OpARM64ANDconst)
+		v.AuxInt = c - 1
+		v.AddArg(x)
+		return true
+	}
+	// match: (UMODW (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(uint32(c)%uint32(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = int64(uint32(c) % uint32(d))
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64XOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XOR (MOVDconst [c]) x)
+	// cond:
+	// result: (XORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64XORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR x (MOVDconst [c]))
+	// cond:
+	// result: (XORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64XORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR x x)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (XOR x (SLLconst [c] y))
+	// cond:
+	// result: (XORshiftLL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64XORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR (SLLconst [c] y) x)
+	// cond:
+	// result: (XORshiftLL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64XORshiftLL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR x (SRLconst [c] y))
+	// cond:
+	// result: (XORshiftRL x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64XORshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR (SRLconst [c] y) x)
+	// cond:
+	// result: (XORshiftRL x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64XORshiftRL)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR x (SRAconst [c] y))
+	// cond:
+	// result: (XORshiftRA x y [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		y := v_1.Args[0]
+		v.reset(OpARM64XORshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (XOR (SRAconst [c] y) x)
+	// cond:
+	// result: (XORshiftRA x y [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpARM64XORshiftRA)
+		v.AuxInt = c
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64XORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORconst [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORconst [-1] x)
+	// cond:
+	// result: (MVN x)
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpARM64MVN)
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c^d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	// match: (XORconst [c] (XORconst [d] x))
+	// cond:
+	// result: (XORconst [c^d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64XORconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpARM64XORconst)
+		v.AuxInt = c ^ d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64XORshiftLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORshiftLL (MOVDconst [c]) x [d])
+	// cond:
+	// result: (XORconst [c] (SLLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64XORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SLLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftLL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (XORconst x [int64(uint64(c)<<uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64XORconst)
+		v.AuxInt = int64(uint64(c) << uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORshiftLL x (SLLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVDconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SLLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64XORshiftRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORshiftRA (MOVDconst [c]) x [d])
+	// cond:
+	// result: (XORconst [c] (SRAconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64XORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SRAconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftRA x (MOVDconst [c]) [d])
+	// cond:
+	// result: (XORconst x [int64(int64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64XORconst)
+		v.AuxInt = int64(int64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORshiftRA x (SRAconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVDconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRAconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpARM64XORshiftRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORshiftRL (MOVDconst [c]) x [d])
+	// cond:
+	// result: (XORconst [c] (SRLconst <x.Type> x [d]))
+	for {
+		d := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpARM64XORconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpARM64SRLconst, x.Type)
+		v0.AuxInt = d
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (XORshiftRL x (MOVDconst [c]) [d])
+	// cond:
+	// result: (XORconst x [int64(uint64(c)>>uint64(d))])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpARM64XORconst)
+		v.AuxInt = int64(uint64(c) >> uint64(d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORshiftRL x (SRLconst x [c]) [d])
+	// cond: c==d
+	// result: (MOVDconst [0])
+	for {
+		d := v.AuxInt
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64SRLconst {
+			break
+		}
+		c := v_1.AuxInt
+		if x != v_1.Args[0] {
+			break
+		}
+		if !(c == d) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpAdd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add16 x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64ADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32 x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64ADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAdd32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32F x y)
+	// cond:
+	// result: (FADDS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64FADDS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAdd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64 x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64ADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAdd64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64F x y)
+	// cond:
+	// result: (FADDD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64FADDD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAdd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add8 x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64ADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAddPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AddPtr x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64ADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAddr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Addr {sym} base)
+	// cond:
+	// result: (MOVDaddr {sym} base)
+	for {
+		sym := v.Aux
+		base := v.Args[0]
+		v.reset(OpARM64MOVDaddr)
+		v.Aux = sym
+		v.AddArg(base)
+		return true
+	}
+}
+func rewriteValueARM64_OpAnd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And16 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAnd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And32 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAnd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And64 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And8 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAndB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AndB x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicAdd32 ptr val mem)
+	// cond:
+	// result: (LoweredAtomicAdd32 ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64LoweredAtomicAdd32)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicAdd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicAdd64 ptr val mem)
+	// cond:
+	// result: (LoweredAtomicAdd64 ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64LoweredAtomicAdd64)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicAnd8 ptr val mem)
+	// cond:
+	// result: (LoweredAtomicAnd8 ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64LoweredAtomicAnd8)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicCompareAndSwap32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicCompareAndSwap32 ptr old new_ mem)
+	// cond:
+	// result: (LoweredAtomicCas32 ptr old new_ mem)
+	for {
+		ptr := v.Args[0]
+		old := v.Args[1]
+		new_ := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARM64LoweredAtomicCas32)
+		v.AddArg(ptr)
+		v.AddArg(old)
+		v.AddArg(new_)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicCompareAndSwap64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicCompareAndSwap64 ptr old new_ mem)
+	// cond:
+	// result: (LoweredAtomicCas64 ptr old new_ mem)
+	for {
+		ptr := v.Args[0]
+		old := v.Args[1]
+		new_ := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpARM64LoweredAtomicCas64)
+		v.AddArg(ptr)
+		v.AddArg(old)
+		v.AddArg(new_)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicExchange32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicExchange32 ptr val mem)
+	// cond:
+	// result: (LoweredAtomicExchange32 ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64LoweredAtomicExchange32)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicExchange64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicExchange64 ptr val mem)
+	// cond:
+	// result: (LoweredAtomicExchange64 ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64LoweredAtomicExchange64)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicLoad32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoad32  ptr mem)
+	// cond:
+	// result: (LDARW ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARM64LDARW)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicLoad64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoad64  ptr mem)
+	// cond:
+	// result: (LDAR  ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARM64LDAR)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicLoadPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoadPtr ptr mem)
+	// cond:
+	// result: (LDAR  ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARM64LDAR)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicOr8  ptr val mem)
+	// cond:
+	// result: (LoweredAtomicOr8  ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64LoweredAtomicOr8)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicStore32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStore32      ptr val mem)
+	// cond:
+	// result: (STLRW ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64STLRW)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicStore64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStore64      ptr val mem)
+	// cond:
+	// result: (STLR  ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64STLR)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAtomicStorePtrNoWB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStorePtrNoWB ptr val mem)
+	// cond:
+	// result: (STLR  ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64STLR)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpAvg64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Avg64u <t> x y)
+	// cond:
+	// result: (ADD (ADD <t> (SRLconst <t> x [1]) (SRLconst <t> y [1])) (AND <t> (AND <t> x y) (MOVDconst [1])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64ADD)
+		v0 := b.NewValue0(v.Line, OpARM64ADD, t)
+		v1 := b.NewValue0(v.Line, OpARM64SRLconst, t)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpARM64SRLconst, t)
+		v2.AuxInt = 1
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpARM64AND, t)
+		v4 := b.NewValue0(v.Line, OpARM64AND, t)
+		v4.AddArg(x)
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v5.AuxInt = 1
+		v3.AddArg(v5)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpBswap32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Bswap32 x)
+	// cond:
+	// result: (REVW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64REVW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpBswap64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Bswap64 x)
+	// cond:
+	// result: (REV x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64REV)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpClosureCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ClosureCall [argwid] entry closure mem)
+	// cond:
+	// result: (CALLclosure [argwid] entry closure mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		closure := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64CALLclosure)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(closure)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpCom16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com16 x)
+	// cond:
+	// result: (MVN x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MVN)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCom32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com32 x)
+	// cond:
+	// result: (MVN x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MVN)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCom64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com64 x)
+	// cond:
+	// result: (MVN x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MVN)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCom8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com8 x)
+	// cond:
+	// result: (MVN x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MVN)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpConst16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const16 [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM64_OpConst32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32 [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM64_OpConst32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32F [val])
+	// cond:
+	// result: (FMOVSconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARM64FMOVSconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM64_OpConst64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64 [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM64_OpConst64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64F [val])
+	// cond:
+	// result: (FMOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARM64FMOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM64_OpConst8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const8 [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueARM64_OpConstBool(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstBool [b])
+	// cond:
+	// result: (MOVDconst [b])
+	for {
+		b := v.AuxInt
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = b
+		return true
+	}
+}
+func rewriteValueARM64_OpConstNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstNil)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+}
+func rewriteValueARM64_OpConvert(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Convert x mem)
+	// cond:
+	// result: (MOVDconvert x mem)
+	for {
+		x := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARM64MOVDconvert)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpCtz32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Ctz32 <t> x)
+	// cond:
+	// result: (CLZW (RBITW <t> x))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpARM64CLZW)
+		v0 := b.NewValue0(v.Line, OpARM64RBITW, t)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpCtz64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Ctz64 <t> x)
+	// cond:
+	// result: (CLZ (RBIT <t> x))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpARM64CLZ)
+		v0 := b.NewValue0(v.Line, OpARM64RBIT, t)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt32Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto32 x)
+	// cond:
+	// result: (FCVTZSSW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FCVTZSSW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt32Fto32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto32U x)
+	// cond:
+	// result: (FCVTZUSW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FCVTZUSW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt32Fto64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64 x)
+	// cond:
+	// result: (FCVTZSS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FCVTZSS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt32Fto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64F x)
+	// cond:
+	// result: (FCVTSD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FCVTSD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt32Fto64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64U x)
+	// cond:
+	// result: (FCVTZUS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FCVTZUS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt32Uto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Uto32F x)
+	// cond:
+	// result: (UCVTFWS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64UCVTFWS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt32Uto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Uto64F x)
+	// cond:
+	// result: (UCVTFWD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64UCVTFWD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt32to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to32F x)
+	// cond:
+	// result: (SCVTFWS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64SCVTFWS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt32to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to64F x)
+	// cond:
+	// result: (SCVTFWD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64SCVTFWD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt64Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32 x)
+	// cond:
+	// result: (FCVTZSDW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FCVTZSDW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt64Fto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32F x)
+	// cond:
+	// result: (FCVTDS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FCVTDS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt64Fto32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32U x)
+	// cond:
+	// result: (FCVTZUDW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FCVTZUDW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt64Fto64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto64 x)
+	// cond:
+	// result: (FCVTZSD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FCVTZSD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt64Fto64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto64U x)
+	// cond:
+	// result: (FCVTZUD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FCVTZUD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt64Uto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Uto32F x)
+	// cond:
+	// result: (UCVTFS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64UCVTFS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt64Uto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Uto64F x)
+	// cond:
+	// result: (UCVTFD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64UCVTFD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt64to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64to32F x)
+	// cond:
+	// result: (SCVTFS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64SCVTFS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpCvt64to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64to64F x)
+	// cond:
+	// result: (SCVTFD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64SCVTFD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpDeferCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (DeferCall [argwid] mem)
+	// cond:
+	// result: (CALLdefer [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpARM64CALLdefer)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpDiv16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16 x y)
+	// cond:
+	// result: (DIVW (SignExt16to32 x) (SignExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64DIVW)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpDiv16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16u x y)
+	// cond:
+	// result: (UDIVW (ZeroExt16to32 x) (ZeroExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64UDIVW)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpDiv32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32 x y)
+	// cond:
+	// result: (DIVW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64DIVW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpDiv32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32F x y)
+	// cond:
+	// result: (FDIVS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64FDIVS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpDiv32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32u x y)
+	// cond:
+	// result: (UDIVW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64UDIVW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpDiv64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64 x y)
+	// cond:
+	// result: (DIV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64DIV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpDiv64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64F x y)
+	// cond:
+	// result: (FDIVD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64FDIVD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpDiv64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64u x y)
+	// cond:
+	// result: (UDIV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64UDIV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpDiv8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8 x y)
+	// cond:
+	// result: (DIVW (SignExt8to32 x) (SignExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64DIVW)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpDiv8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8u x y)
+	// cond:
+	// result: (UDIVW (ZeroExt8to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64UDIVW)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpEq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq16 x y)
+	// cond:
+	// result: (Equal (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64Equal)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpEq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32 x y)
+	// cond:
+	// result: (Equal (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64Equal)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpEq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32F x y)
+	// cond:
+	// result: (Equal (FCMPS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64Equal)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpEq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64 x y)
+	// cond:
+	// result: (Equal (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64Equal)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpEq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64F x y)
+	// cond:
+	// result: (Equal (FCMPD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64Equal)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpEq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq8 x y)
+	// cond:
+	// result: (Equal (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64Equal)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpEqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqB x y)
+	// cond:
+	// result: (XOR (MOVDconst [1]) (XOR <config.fe.TypeBool()> x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64XOR)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64XOR, config.fe.TypeBool())
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpEqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqPtr x y)
+	// cond:
+	// result: (Equal (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64Equal)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16 x y)
+	// cond:
+	// result: (GreaterEqual (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16U x y)
+	// cond:
+	// result: (GreaterEqualU (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqualU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32 x y)
+	// cond:
+	// result: (GreaterEqual (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32F x y)
+	// cond:
+	// result: (GreaterEqual (FCMPS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32U x y)
+	// cond:
+	// result: (GreaterEqualU (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqualU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64 x y)
+	// cond:
+	// result: (GreaterEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64F x y)
+	// cond:
+	// result: (GreaterEqual (FCMPD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64U x y)
+	// cond:
+	// result: (GreaterEqualU (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqualU)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8 x y)
+	// cond:
+	// result: (GreaterEqual (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8U x y)
+	// cond:
+	// result: (GreaterEqualU (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqualU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGetClosurePtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetClosurePtr)
+	// cond:
+	// result: (LoweredGetClosurePtr)
+	for {
+		v.reset(OpARM64LoweredGetClosurePtr)
+		return true
+	}
+}
+func rewriteValueARM64_OpGoCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GoCall [argwid] mem)
+	// cond:
+	// result: (CALLgo [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpARM64CALLgo)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpGreater16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16 x y)
+	// cond:
+	// result: (GreaterThan (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGreater16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16U x y)
+	// cond:
+	// result: (GreaterThanU (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThanU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGreater32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32 x y)
+	// cond:
+	// result: (GreaterThan (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGreater32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32F x y)
+	// cond:
+	// result: (GreaterThan (FCMPS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGreater32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32U x y)
+	// cond:
+	// result: (GreaterThanU (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThanU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGreater64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64 x y)
+	// cond:
+	// result: (GreaterThan (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGreater64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64F x y)
+	// cond:
+	// result: (GreaterThan (FCMPD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGreater64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64U x y)
+	// cond:
+	// result: (GreaterThanU (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThanU)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGreater8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8 x y)
+	// cond:
+	// result: (GreaterThan (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpGreater8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8U x y)
+	// cond:
+	// result: (GreaterThanU (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThanU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpHmul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16 x y)
+	// cond:
+	// result: (SRAconst (MULW <config.fe.TypeInt32()> (SignExt16to32 x) (SignExt16to32 y)) [16])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpARM64MULW, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpHmul16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16u x y)
+	// cond:
+	// result: (SRLconst (MUL <config.fe.TypeUInt32()> (ZeroExt16to32 x) (ZeroExt16to32 y)) [16])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRLconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpARM64MUL, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpHmul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32 x y)
+	// cond:
+	// result: (SRAconst (MULL <config.fe.TypeInt64()> x y) [32])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = 32
+		v0 := b.NewValue0(v.Line, OpARM64MULL, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpHmul32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32u x y)
+	// cond:
+	// result: (SRAconst (UMULL <config.fe.TypeUInt64()> x y) [32])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = 32
+		v0 := b.NewValue0(v.Line, OpARM64UMULL, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpHmul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul64 x y)
+	// cond:
+	// result: (MULH x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64MULH)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpHmul64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul64u x y)
+	// cond:
+	// result: (UMULH x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64UMULH)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpHmul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8 x y)
+	// cond:
+	// result: (SRAconst (MULW <config.fe.TypeInt16()> (SignExt8to32 x) (SignExt8to32 y)) [8])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpARM64MULW, config.fe.TypeInt16())
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpHmul8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8u x y)
+	// cond:
+	// result: (SRLconst (MUL <config.fe.TypeUInt16()> (ZeroExt8to32 x) (ZeroExt8to32 y)) [8])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRLconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpARM64MUL, config.fe.TypeUInt16())
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpInterCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (InterCall [argwid] entry mem)
+	// cond:
+	// result: (CALLinter [argwid] entry mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARM64CALLinter)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpIsInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsInBounds idx len)
+	// cond:
+	// result: (LessThanU (CMP idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpARM64LessThanU)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpIsNonNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsNonNil ptr)
+	// cond:
+	// result: (NotEqual (CMPconst [0] ptr))
+	for {
+		ptr := v.Args[0]
+		v.reset(OpARM64NotEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v0.AuxInt = 0
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpIsSliceInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsSliceInBounds idx len)
+	// cond:
+	// result: (LessEqualU (CMP idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpARM64LessEqualU)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16 x y)
+	// cond:
+	// result: (LessEqual (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16U x y)
+	// cond:
+	// result: (LessEqualU (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessEqualU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32 x y)
+	// cond:
+	// result: (LessEqual (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32F x y)
+	// cond:
+	// result: (GreaterEqual (FCMPS y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPS, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32U x y)
+	// cond:
+	// result: (LessEqualU (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessEqualU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64 x y)
+	// cond:
+	// result: (LessEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64F x y)
+	// cond:
+	// result: (GreaterEqual (FCMPD y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPD, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64U x y)
+	// cond:
+	// result: (LessEqualU (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessEqualU)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8 x y)
+	// cond:
+	// result: (LessEqual (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8U x y)
+	// cond:
+	// result: (LessEqualU (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessEqualU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLess16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16 x y)
+	// cond:
+	// result: (LessThan (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessThan)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLess16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16U x y)
+	// cond:
+	// result: (LessThanU (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessThanU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLess32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32 x y)
+	// cond:
+	// result: (LessThan (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessThan)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLess32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32F x y)
+	// cond:
+	// result: (GreaterThan (FCMPS y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPS, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLess32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32U x y)
+	// cond:
+	// result: (LessThanU (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessThanU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLess64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64 x y)
+	// cond:
+	// result: (LessThan (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessThan)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLess64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64F x y)
+	// cond:
+	// result: (GreaterThan (FCMPD y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPD, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLess64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64U x y)
+	// cond:
+	// result: (LessThanU (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessThanU)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLess8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8 x y)
+	// cond:
+	// result: (LessThan (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessThan)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLess8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8U x y)
+	// cond:
+	// result: (LessThanU (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64LessThanU)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpLoad(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Load <t> ptr mem)
+	// cond: t.IsBoolean()
+	// result: (MOVBUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(t.IsBoolean()) {
+			break
+		}
+		v.reset(OpARM64MOVBUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is8BitInt(t) && isSigned(t))
+	// result: (MOVBload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is8BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpARM64MOVBload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is8BitInt(t) && !isSigned(t))
+	// result: (MOVBUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is8BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpARM64MOVBUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is16BitInt(t) && isSigned(t))
+	// result: (MOVHload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpARM64MOVHload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is16BitInt(t) && !isSigned(t))
+	// result: (MOVHUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpARM64MOVHUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is32BitInt(t) && isSigned(t))
+	// result: (MOVWload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpARM64MOVWload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is32BitInt(t) && !isSigned(t))
+	// result: (MOVWUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpARM64MOVWUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is64BitInt(t) || isPtr(t))
+	// result: (MOVDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitInt(t) || isPtr(t)) {
+			break
+		}
+		v.reset(OpARM64MOVDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitFloat(t)
+	// result: (FMOVSload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitFloat(t)) {
+			break
+		}
+		v.reset(OpARM64FMOVSload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitFloat(t)
+	// result: (FMOVDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitFloat(t)) {
+			break
+		}
+		v.reset(OpARM64FMOVDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpLrot16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot16 <t> x [c])
+	// cond:
+	// result: (OR (SLLconst <t> x [c&15]) (SRLconst <t> (ZeroExt16to64 x) [16-c&15]))
+	for {
+		t := v.Type
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpARM64OR)
+		v0 := b.NewValue0(v.Line, OpARM64SLLconst, t)
+		v0.AuxInt = c & 15
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64SRLconst, t)
+		v1.AuxInt = 16 - c&15
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpLrot32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot32 x [c])
+	// cond:
+	// result: (RORWconst x [32-c&31])
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpARM64RORWconst)
+		v.AuxInt = 32 - c&31
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpLrot64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot64 x [c])
+	// cond:
+	// result: (RORconst  x [64-c&63])
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpARM64RORconst)
+		v.AuxInt = 64 - c&63
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpLrot8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot8  <t> x [c])
+	// cond:
+	// result: (OR (SLLconst <t> x [c&7])  (SRLconst <t> (ZeroExt8to64  x) [8-c&7]))
+	for {
+		t := v.Type
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpARM64OR)
+		v0 := b.NewValue0(v.Line, OpARM64SLLconst, t)
+		v0.AuxInt = c & 7
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64SRLconst, t)
+		v1.AuxInt = 8 - c&7
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x16 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x32 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 16
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh16x64  _ (MOVDconst [c]))
+	// cond: uint64(c) >= 16
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh16x64 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpConst64, t)
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x8  <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x16 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x32 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 32
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh32x64  _ (MOVDconst [c]))
+	// cond: uint64(c) >= 32
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh32x64 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpConst64, t)
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x8  <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x16 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x32 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh64x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 64
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 64) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh64x64  _ (MOVDconst [c]))
+	// cond: uint64(c) >= 64
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 64) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh64x64 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpConst64, t)
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x8  <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x16 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x32 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x64   x (MOVDconst [c]))
+	// cond: uint64(c) < 8
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpARM64SLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh8x64   _ (MOVDconst [c]))
+	// cond: uint64(c) >= 8
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh8x64 <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpConst64, t)
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM64_OpLsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x8  <t> x y)
+	// cond:
+	// result: (CSELULT (SLL <t> x (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpMod16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16 x y)
+	// cond:
+	// result: (MODW (SignExt16to32 x) (SignExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64MODW)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpMod16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16u x y)
+	// cond:
+	// result: (UMODW (ZeroExt16to32 x) (ZeroExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64UMODW)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpMod32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32 x y)
+	// cond:
+	// result: (MODW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64MODW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpMod32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32u x y)
+	// cond:
+	// result: (UMODW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64UMODW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpMod64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod64 x y)
+	// cond:
+	// result: (MOD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64MOD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpMod64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod64u x y)
+	// cond:
+	// result: (UMOD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64UMOD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpMod8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8 x y)
+	// cond:
+	// result: (MODW (SignExt8to32 x) (SignExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64MODW)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpMod8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8u x y)
+	// cond:
+	// result: (UMODW (ZeroExt8to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64UMODW)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpMove(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Move [s] _ _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore dst (MOVBUload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpARM64MOVBstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVBUload, config.fe.TypeUInt8())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVHstore dst (MOVHUload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpARM64MOVHstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVHUload, config.fe.TypeUInt16())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVWstore dst (MOVWUload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpARM64MOVWstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVWUload, config.fe.TypeUInt32())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8
+	// result: (MOVDstore dst (MOVDload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8) {
+			break
+		}
+		v.reset(OpARM64MOVDstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDload, config.fe.TypeUInt64())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] dst (MOVBUload [2] src mem) 		(MOVHstore dst (MOVHUload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVBUload, config.fe.TypeUInt8())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVHstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARM64MOVHUload, config.fe.TypeUInt16())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 5
+	// result: (MOVBstore [4] dst (MOVBUload [4] src mem) 		(MOVWstore dst (MOVWUload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 5) {
+			break
+		}
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVBUload, config.fe.TypeUInt8())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVWstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARM64MOVWUload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 6
+	// result: (MOVHstore [4] dst (MOVHUload [4] src mem) 		(MOVWstore dst (MOVWUload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 6) {
+			break
+		}
+		v.reset(OpARM64MOVHstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVHUload, config.fe.TypeUInt16())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVWstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARM64MOVWUload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 7
+	// result: (MOVBstore [6] dst (MOVBUload [6] src mem) 		(MOVHstore [4] dst (MOVHUload [4] src mem) 			(MOVWstore dst (MOVWUload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 7) {
+			break
+		}
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = 6
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVBUload, config.fe.TypeUInt8())
+		v0.AuxInt = 6
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVHstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARM64MOVHUload, config.fe.TypeUInt16())
+		v2.AuxInt = 4
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64MOVWstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpARM64MOVWUload, config.fe.TypeUInt32())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 12
+	// result: (MOVWstore [8] dst (MOVWUload [8] src mem) 		(MOVDstore dst (MOVDload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 12) {
+			break
+		}
+		v.reset(OpARM64MOVWstore)
+		v.AuxInt = 8
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVWUload, config.fe.TypeUInt32())
+		v0.AuxInt = 8
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVDstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARM64MOVDload, config.fe.TypeUInt64())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 16
+	// result: (MOVDstore [8] dst (MOVDload [8] src mem) 		(MOVDstore dst (MOVDload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 16) {
+			break
+		}
+		v.reset(OpARM64MOVDstore)
+		v.AuxInt = 8
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDload, config.fe.TypeUInt64())
+		v0.AuxInt = 8
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVDstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARM64MOVDload, config.fe.TypeUInt64())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 24
+	// result: (MOVDstore [16] dst (MOVDload [16] src mem) 		(MOVDstore [8] dst (MOVDload [8] src mem) 			(MOVDstore dst (MOVDload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 24) {
+			break
+		}
+		v.reset(OpARM64MOVDstore)
+		v.AuxInt = 16
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDload, config.fe.TypeUInt64())
+		v0.AuxInt = 16
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVDstore, TypeMem)
+		v1.AuxInt = 8
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpARM64MOVDload, config.fe.TypeUInt64())
+		v2.AuxInt = 8
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64MOVDstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpARM64MOVDload, config.fe.TypeUInt64())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size()%8 != 0 && SizeAndAlign(s).Size() > 8
+	// result: (Move [MakeSizeAndAlign(SizeAndAlign(s).Size()%8, 1).Int64()] 		(OffPtr <dst.Type> dst [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8]) 		(OffPtr <src.Type> src [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8]) 		(Move [MakeSizeAndAlign(SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8, 1).Int64()] dst src mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size()%8 != 0 && SizeAndAlign(s).Size() > 8) {
+			break
+		}
+		v.reset(OpMove)
+		v.AuxInt = MakeSizeAndAlign(SizeAndAlign(s).Size()%8, 1).Int64()
+		v0 := b.NewValue0(v.Line, OpOffPtr, dst.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - SizeAndAlign(s).Size()%8
+		v0.AddArg(dst)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpOffPtr, src.Type)
+		v1.AuxInt = SizeAndAlign(s).Size() - SizeAndAlign(s).Size()%8
+		v1.AddArg(src)
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpMove, TypeMem)
+		v2.AuxInt = MakeSizeAndAlign(SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8, 1).Int64()
+		v2.AddArg(dst)
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v.AddArg(v2)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size() <= 8*128 	&& !config.noDuffDevice
+	// result: (DUFFCOPY [8 * (128 - int64(SizeAndAlign(s).Size()/8))] dst src mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size() <= 8*128 && !config.noDuffDevice) {
+			break
+		}
+		v.reset(OpARM64DUFFCOPY)
+		v.AuxInt = 8 * (128 - int64(SizeAndAlign(s).Size()/8))
+		v.AddArg(dst)
+		v.AddArg(src)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size()%8 == 0
+	// result: (LoweredMove 		dst 		src 		(ADDconst <src.Type> src [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)]) 		mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size()%8 == 0) {
+			break
+		}
+		v.reset(OpARM64LoweredMove)
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, OpARM64ADDconst, src.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - moveSize(SizeAndAlign(s).Align(), config)
+		v0.AddArg(src)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpMul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul16 x y)
+	// cond:
+	// result: (MULW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64MULW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpMul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32 x y)
+	// cond:
+	// result: (MULW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64MULW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpMul32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32F x y)
+	// cond:
+	// result: (FMULS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64FMULS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpMul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64 x y)
+	// cond:
+	// result: (MUL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64MUL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpMul64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64F x y)
+	// cond:
+	// result: (FMULD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64FMULD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpMul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul8 x y)
+	// cond:
+	// result: (MULW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64MULW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeg16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg16 x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64NEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeg32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32 x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64NEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeg32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32F x)
+	// cond:
+	// result: (FNEGS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FNEGS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeg64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64 x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64NEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeg64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64F x)
+	// cond:
+	// result: (FNEGD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FNEGD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeg8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg8 x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64NEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq16 x y)
+	// cond:
+	// result: (NotEqual (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64NotEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32 x y)
+	// cond:
+	// result: (NotEqual (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64NotEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32F x y)
+	// cond:
+	// result: (NotEqual (FCMPS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64NotEqual)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPS, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64 x y)
+	// cond:
+	// result: (NotEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64NotEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64F x y)
+	// cond:
+	// result: (NotEqual (FCMPD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64NotEqual)
+		v0 := b.NewValue0(v.Line, OpARM64FCMPD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq8 x y)
+	// cond:
+	// result: (NotEqual (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64NotEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqB x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpNeqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqPtr x y)
+	// cond:
+	// result: (NotEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64NotEqual)
+		v0 := b.NewValue0(v.Line, OpARM64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpNilCheck(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NilCheck ptr mem)
+	// cond:
+	// result: (LoweredNilCheck ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpARM64LoweredNilCheck)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpNot(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Not x)
+	// cond:
+	// result: (XOR (MOVDconst [1]) x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64XOR)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpOffPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OffPtr [off] ptr:(SP))
+	// cond:
+	// result: (MOVDaddr [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		if ptr.Op != OpSP {
+			break
+		}
+		v.reset(OpARM64MOVDaddr)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+	// match: (OffPtr [off] ptr)
+	// cond:
+	// result: (ADDconst [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		v.reset(OpARM64ADDconst)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+}
+func rewriteValueARM64_OpOr16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or16 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpOr32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or32 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpOr64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or64 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or8 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpOrB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OrB x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh16Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux16 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt16to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpConst64, t)
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh16Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux32 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt16to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpConst64, t)
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh16Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux64 x (MOVDconst [c]))
+	// cond: uint64(c) < 16
+	// result: (SRLconst (ZeroExt16to64 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpARM64SRLconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16Ux64 _ (MOVDconst [c]))
+	// cond: uint64(c) >= 16
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh16Ux64 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt16to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh16Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux8  <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt16to64 x) (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpConst64, t)
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x16 x y)
+	// cond:
+	// result: (SRA (SignExt16to64 x) (CSELULT <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v3.AuxInt = 63
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x32 x y)
+	// cond:
+	// result: (SRA (SignExt16to64 x) (CSELULT <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v3.AuxInt = 63
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 16
+	// result: (SRAconst (SignExt16to64 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16x64 x (MOVDconst [c]))
+	// cond: uint64(c) >= 16
+	// result: (SRAconst (SignExt16to64 x) [63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = 63
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16x64 x y)
+	// cond:
+	// result: (SRA (SignExt16to64 x) (CSELULT <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v2.AuxInt = 63
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x8  x y)
+	// cond:
+	// result: (SRA (SignExt16to64 x) (CSELULT <y.Type> (ZeroExt8to64  y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64  y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v3.AuxInt = 63
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh32Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux16 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt32to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpConst64, t)
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh32Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux32 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt32to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpConst64, t)
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh32Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux64 x (MOVDconst [c]))
+	// cond: uint64(c) < 32
+	// result: (SRLconst (ZeroExt32to64 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpARM64SRLconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh32Ux64 _ (MOVDconst [c]))
+	// cond: uint64(c) >= 32
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh32Ux64 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt32to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh32Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux8  <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt32to64 x) (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpConst64, t)
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x16 x y)
+	// cond:
+	// result: (SRA (SignExt32to64 x) (CSELULT <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v3.AuxInt = 63
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x32 x y)
+	// cond:
+	// result: (SRA (SignExt32to64 x) (CSELULT <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v3.AuxInt = 63
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 32
+	// result: (SRAconst (SignExt32to64 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh32x64 x (MOVDconst [c]))
+	// cond: uint64(c) >= 32
+	// result: (SRAconst (SignExt32to64 x) [63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = 63
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh32x64 x y)
+	// cond:
+	// result: (SRA (SignExt32to64 x) (CSELULT <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v2.AuxInt = 63
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x8  x y)
+	// cond:
+	// result: (SRA (SignExt32to64 x) (CSELULT <y.Type> (ZeroExt8to64  y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64  y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v3.AuxInt = 63
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh64Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux16 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> x (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh64Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux32 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> x (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh64Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux64 x (MOVDconst [c]))
+	// cond: uint64(c) < 64
+	// result: (SRLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 64) {
+			break
+		}
+		v.reset(OpARM64SRLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64Ux64 _ (MOVDconst [c]))
+	// cond: uint64(c) >= 64
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 64) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh64Ux64 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> x y) (Const64 <t> [0]) (CMPconst [64] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpConst64, t)
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh64Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux8  <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> x (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x16 x y)
+	// cond:
+	// result: (SRA x (CSELULT <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v2.AuxInt = 63
+		v0.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v0.AddArg(v3)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x32 x y)
+	// cond:
+	// result: (SRA x (CSELULT <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v2.AuxInt = 63
+		v0.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v0.AddArg(v3)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh64x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 64
+	// result: (SRAconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 64) {
+			break
+		}
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64x64 x (MOVDconst [c]))
+	// cond: uint64(c) >= 64
+	// result: (SRAconst x [63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 64) {
+			break
+		}
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64x64 x y)
+	// cond:
+	// result: (SRA x (CSELULT <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v1.AuxInt = 63
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v2.AuxInt = 64
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x8  x y)
+	// cond:
+	// result: (SRA x (CSELULT <y.Type> (ZeroExt8to64  y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64  y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v2.AuxInt = 63
+		v0.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v0.AddArg(v3)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh8Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux16 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt8to64 x) (ZeroExt16to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpConst64, t)
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh8Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux32 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt8to64 x) (ZeroExt32to64 y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpConst64, t)
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh8Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux64  x (MOVDconst [c]))
+	// cond: uint64(c) < 8
+	// result: (SRLconst (ZeroExt8to64  x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpARM64SRLconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8Ux64  _ (MOVDconst [c]))
+	// cond: uint64(c) >= 8
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpARM64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh8Ux64 <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt8to64 x) y) (Const64 <t> [0]) (CMPconst [64] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpConst64, t)
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh8Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux8  <t> x y)
+	// cond:
+	// result: (CSELULT (SRL <t> (ZeroExt8to64 x) (ZeroExt8to64  y)) (Const64 <t> [0]) (CMPconst [64] (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64CSELULT)
+		v0 := b.NewValue0(v.Line, OpARM64SRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpConst64, t)
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x16 x y)
+	// cond:
+	// result: (SRA (SignExt8to64 x) (CSELULT <y.Type> (ZeroExt16to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt16to64 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v3.AuxInt = 63
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x32 x y)
+	// cond:
+	// result: (SRA (SignExt8to64 x) (CSELULT <y.Type> (ZeroExt32to64 y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt32to64 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v3.AuxInt = 63
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x64   x (MOVDconst [c]))
+	// cond: uint64(c) < 8
+	// result: (SRAconst (SignExt8to64  x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8x64  x (MOVDconst [c]))
+	// cond: uint64(c) >= 8
+	// result: (SRAconst (SignExt8to64  x) [63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpARM64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpARM64SRAconst)
+		v.AuxInt = 63
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8x64 x y)
+	// cond:
+	// result: (SRA (SignExt8to64 x) (CSELULT <y.Type> y (Const64 <y.Type> [63]) (CMPconst [64] y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v2.AuxInt = 63
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v3.AuxInt = 64
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpRsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x8  x y)
+	// cond:
+	// result: (SRA (SignExt8to64 x) (CSELULT <y.Type> (ZeroExt8to64  y) (Const64 <y.Type> [63]) (CMPconst [64] (ZeroExt8to64  y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SRA)
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64CSELULT, y.Type)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpConst64, y.Type)
+		v3.AuxInt = 63
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpARM64CMPconst, TypeFlags)
+		v4.AuxInt = 64
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueARM64_OpSignExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to32 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpSignExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to64 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpSignExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt32to64 x)
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVWreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpSignExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to16 x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpSignExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to32 x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpSignExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to64 x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpSlicemask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Slicemask <t> x)
+	// cond:
+	// result: (MVN (SRAconst <t> (SUBconst <t> x [1]) [63]))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpARM64MVN)
+		v0 := b.NewValue0(v.Line, OpARM64SRAconst, t)
+		v0.AuxInt = 63
+		v1 := b.NewValue0(v.Line, OpARM64SUBconst, t)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueARM64_OpSqrt(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sqrt x)
+	// cond:
+	// result: (FSQRTD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64FSQRTD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpStaticCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (StaticCall [argwid] {target} mem)
+	// cond:
+	// result: (CALLstatic [argwid] {target} mem)
+	for {
+		argwid := v.AuxInt
+		target := v.Aux
+		mem := v.Args[0]
+		v.reset(OpARM64CALLstatic)
+		v.AuxInt = argwid
+		v.Aux = target
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueARM64_OpStore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Store [1] ptr val mem)
+	// cond:
+	// result: (MOVBstore ptr val mem)
+	for {
+		if v.AuxInt != 1 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVBstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [2] ptr val mem)
+	// cond:
+	// result: (MOVHstore ptr val mem)
+	for {
+		if v.AuxInt != 2 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpARM64MOVHstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: !is32BitFloat(val.Type)
+	// result: (MOVWstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(!is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpARM64MOVWstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [8] ptr val mem)
+	// cond: !is64BitFloat(val.Type)
+	// result: (MOVDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(!is64BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpARM64MOVDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: is32BitFloat(val.Type)
+	// result: (FMOVSstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpARM64FMOVSstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [8] ptr val mem)
+	// cond: is64BitFloat(val.Type)
+	// result: (FMOVDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is64BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpARM64FMOVDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpSub16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub16 x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpSub32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32 x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpSub32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32F x y)
+	// cond:
+	// result: (FSUBS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64FSUBS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpSub64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64 x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpSub64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64F x y)
+	// cond:
+	// result: (FSUBD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64FSUBD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpSub8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub8 x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpSubPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SubPtr x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpTrunc16to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc16to8 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpTrunc32to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to16 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpTrunc32to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to8 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpTrunc64to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to16 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpTrunc64to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to32 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpTrunc64to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to8 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpXor16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor16 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpXor32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor32 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpXor64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor64 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpXor8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor8 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpARM64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueARM64_OpZero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zero [s] _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore ptr (MOVDconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpARM64MOVBstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVHstore ptr (MOVDconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpARM64MOVHstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVWstore ptr (MOVDconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpARM64MOVWstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 8
+	// result: (MOVDstore ptr (MOVDconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 8) {
+			break
+		}
+		v.reset(OpARM64MOVDstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] ptr (MOVDconst [0]) 		(MOVHstore ptr (MOVDconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = 2
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVHstore, TypeMem)
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 5
+	// result: (MOVBstore [4] ptr (MOVDconst [0]) 		(MOVWstore ptr (MOVDconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 5) {
+			break
+		}
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = 4
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVWstore, TypeMem)
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 6
+	// result: (MOVHstore [4] ptr (MOVDconst [0]) 		(MOVWstore ptr (MOVDconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 6) {
+			break
+		}
+		v.reset(OpARM64MOVHstore)
+		v.AuxInt = 4
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVWstore, TypeMem)
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 7
+	// result: (MOVBstore [6] ptr (MOVDconst [0]) 		(MOVHstore [4] ptr (MOVDconst [0]) 			(MOVWstore ptr (MOVDconst [0]) mem)))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 7) {
+			break
+		}
+		v.reset(OpARM64MOVBstore)
+		v.AuxInt = 6
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVHstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64MOVWstore, TypeMem)
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 12
+	// result: (MOVWstore [8] ptr (MOVDconst [0]) 		(MOVDstore ptr (MOVDconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 12) {
+			break
+		}
+		v.reset(OpARM64MOVWstore)
+		v.AuxInt = 8
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVDstore, TypeMem)
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 16
+	// result: (MOVDstore [8] ptr (MOVDconst [0]) 		(MOVDstore ptr (MOVDconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 16) {
+			break
+		}
+		v.reset(OpARM64MOVDstore)
+		v.AuxInt = 8
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVDstore, TypeMem)
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 24
+	// result: (MOVDstore [16] ptr (MOVDconst [0]) 		(MOVDstore [8] ptr (MOVDconst [0]) 			(MOVDstore ptr (MOVDconst [0]) mem)))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 24) {
+			break
+		}
+		v.reset(OpARM64MOVDstore)
+		v.AuxInt = 16
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpARM64MOVDstore, TypeMem)
+		v1.AuxInt = 8
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpARM64MOVDstore, TypeMem)
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpARM64MOVDconst, config.fe.TypeUInt64())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size()%8 != 0 && SizeAndAlign(s).Size() > 8
+	// result: (Zero [MakeSizeAndAlign(SizeAndAlign(s).Size()%8, 1).Int64()] 		(OffPtr <ptr.Type> ptr [SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8]) 		(Zero [MakeSizeAndAlign(SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8, 1).Int64()] ptr mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size()%8 != 0 && SizeAndAlign(s).Size() > 8) {
+			break
+		}
+		v.reset(OpZero)
+		v.AuxInt = MakeSizeAndAlign(SizeAndAlign(s).Size()%8, 1).Int64()
+		v0 := b.NewValue0(v.Line, OpOffPtr, ptr.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - SizeAndAlign(s).Size()%8
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZero, TypeMem)
+		v1.AuxInt = MakeSizeAndAlign(SizeAndAlign(s).Size()-SizeAndAlign(s).Size()%8, 1).Int64()
+		v1.AddArg(ptr)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size() <= 8*128 	&& !config.noDuffDevice
+	// result: (DUFFZERO [4 * (128 - int64(SizeAndAlign(s).Size()/8))] ptr mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size() <= 8*128 && !config.noDuffDevice) {
+			break
+		}
+		v.reset(OpARM64DUFFZERO)
+		v.AuxInt = 4 * (128 - int64(SizeAndAlign(s).Size()/8))
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size()%8 == 0 && (SizeAndAlign(s).Size() > 8*128 || config.noDuffDevice)
+	// result: (LoweredZero 		ptr 		(ADDconst <ptr.Type> [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)] ptr) 		mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size()%8 == 0 && (SizeAndAlign(s).Size() > 8*128 || config.noDuffDevice)) {
+			break
+		}
+		v.reset(OpARM64LoweredZero)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpARM64ADDconst, ptr.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - moveSize(SizeAndAlign(s).Align(), config)
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueARM64_OpZeroExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to32 x)
+	// cond:
+	// result: (MOVHUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVHUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpZeroExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to64 x)
+	// cond:
+	// result: (MOVHUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVHUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpZeroExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt32to64 x)
+	// cond:
+	// result: (MOVWUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVWUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpZeroExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to16 x)
+	// cond:
+	// result: (MOVBUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVBUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpZeroExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to32 x)
+	// cond:
+	// result: (MOVBUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVBUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueARM64_OpZeroExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to64 x)
+	// cond:
+	// result: (MOVBUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpARM64MOVBUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteBlockARM64(b *Block, config *Config) bool {
+	switch b.Kind {
+	case BlockARM64EQ:
+		// match: (EQ (CMPconst [0] x) yes no)
+		// cond:
+		// result: (Z x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64CMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64Z
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (CMPWconst [0] x) yes no)
+		// cond:
+		// result: (ZW x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64ZW
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (InvertFlags cmp) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64EQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64GE:
+		// match: (GE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (LE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64LE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64GT:
+		// match: (GT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (LT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64LT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockIf:
+		// match: (If (Equal cc) yes no)
+		// cond:
+		// result: (EQ cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64Equal {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64EQ
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (NotEqual cc) yes no)
+		// cond:
+		// result: (NE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64NotEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64NE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (LessThan cc) yes no)
+		// cond:
+		// result: (LT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64LessThan {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64LT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (LessThanU cc) yes no)
+		// cond:
+		// result: (ULT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64LessThanU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64ULT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (LessEqual cc) yes no)
+		// cond:
+		// result: (LE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64LessEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64LE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (LessEqualU cc) yes no)
+		// cond:
+		// result: (ULE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64LessEqualU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64ULE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (GreaterThan cc) yes no)
+		// cond:
+		// result: (GT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64GreaterThan {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64GT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (GreaterThanU cc) yes no)
+		// cond:
+		// result: (UGT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64GreaterThanU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64UGT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (GreaterEqual cc) yes no)
+		// cond:
+		// result: (GE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64GreaterEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64GE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (GreaterEqualU cc) yes no)
+		// cond:
+		// result: (UGE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64GreaterEqualU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64UGE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If cond yes no)
+		// cond:
+		// result: (NZ cond yes no)
+		for {
+			v := b.Control
+			_ = v
+			cond := b.Control
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64NZ
+			b.SetControl(cond)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64LE:
+		// match: (LE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (GE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64GE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64LT:
+		// match: (LT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (GT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64GT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64NE:
+		// match: (NE (CMPconst [0] x) yes no)
+		// cond:
+		// result: (NZ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64CMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64NZ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] x) yes no)
+		// cond:
+		// result: (NZW x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64NZW
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (NE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64NE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64NZ:
+		// match: (NZ (Equal cc) yes no)
+		// cond:
+		// result: (EQ cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64Equal {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64EQ
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NZ (NotEqual cc) yes no)
+		// cond:
+		// result: (NE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64NotEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64NE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NZ (LessThan cc) yes no)
+		// cond:
+		// result: (LT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64LessThan {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64LT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NZ (LessThanU cc) yes no)
+		// cond:
+		// result: (ULT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64LessThanU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64ULT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NZ (LessEqual cc) yes no)
+		// cond:
+		// result: (LE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64LessEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64LE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NZ (LessEqualU cc) yes no)
+		// cond:
+		// result: (ULE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64LessEqualU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64ULE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NZ (GreaterThan cc) yes no)
+		// cond:
+		// result: (GT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64GreaterThan {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64GT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NZ (GreaterThanU cc) yes no)
+		// cond:
+		// result: (UGT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64GreaterThanU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64UGT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NZ (GreaterEqual cc) yes no)
+		// cond:
+		// result: (GE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64GreaterEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64GE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NZ (GreaterEqualU cc) yes no)
+		// cond:
+		// result: (UGE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64GreaterEqualU {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64UGE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NZ (MOVDconst [0]) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64MOVDconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (NZ (MOVDconst [c]) yes no)
+		// cond: c != 0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64MOVDconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c != 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64NZW:
+		// match: (NZW (MOVDconst [c]) yes no)
+		// cond: int32(c) == 0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64MOVDconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) == 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (NZW (MOVDconst [c]) yes no)
+		// cond: int32(c) != 0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64MOVDconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) != 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64UGE:
+		// match: (UGE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (ULE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64ULE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64UGT:
+		// match: (UGT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (UGT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (UGT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (ULT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64ULT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64ULE:
+		// match: (ULE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULE (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULE (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULE (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULE (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (UGE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64UGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64ULT:
+		// match: (ULT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULT (FlagLT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULT (FlagLT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagLT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULT (FlagGT_ULT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_ULT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ULT (FlagGT_UGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64FlagGT_UGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (ULT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (UGT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockARM64UGT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockARM64Z:
+		// match: (Z (MOVDconst [0]) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64MOVDconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (Z (MOVDconst [c]) yes no)
+		// cond: c != 0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64MOVDconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c != 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockARM64ZW:
+		// match: (ZW (MOVDconst [c]) yes no)
+		// cond: int32(c) == 0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpARM64MOVDconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) == 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (ZW (MOVDconst [c]) yes no)
+		// cond: int32(c) != 0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpARM64MOVDconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) != 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS.go b/src/cmd/compile/internal/ssa/rewriteMIPS.go
new file mode 100644
index 0000000..cbe9f1b
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/rewriteMIPS.go
@@ -0,0 +1,9831 @@
+// autogenerated from gen/MIPS.rules: do not edit!
+// generated with: cd gen; go run *.go
+
+package ssa
+
+import "math"
+
+var _ = math.MinInt8 // in case not otherwise used
+func rewriteValueMIPS(v *Value, config *Config) bool {
+	switch v.Op {
+	case OpAdd16:
+		return rewriteValueMIPS_OpAdd16(v, config)
+	case OpAdd32:
+		return rewriteValueMIPS_OpAdd32(v, config)
+	case OpAdd32F:
+		return rewriteValueMIPS_OpAdd32F(v, config)
+	case OpAdd32withcarry:
+		return rewriteValueMIPS_OpAdd32withcarry(v, config)
+	case OpAdd64F:
+		return rewriteValueMIPS_OpAdd64F(v, config)
+	case OpAdd8:
+		return rewriteValueMIPS_OpAdd8(v, config)
+	case OpAddPtr:
+		return rewriteValueMIPS_OpAddPtr(v, config)
+	case OpAddr:
+		return rewriteValueMIPS_OpAddr(v, config)
+	case OpAnd16:
+		return rewriteValueMIPS_OpAnd16(v, config)
+	case OpAnd32:
+		return rewriteValueMIPS_OpAnd32(v, config)
+	case OpAnd8:
+		return rewriteValueMIPS_OpAnd8(v, config)
+	case OpAndB:
+		return rewriteValueMIPS_OpAndB(v, config)
+	case OpAtomicAdd32:
+		return rewriteValueMIPS_OpAtomicAdd32(v, config)
+	case OpAtomicAnd8:
+		return rewriteValueMIPS_OpAtomicAnd8(v, config)
+	case OpAtomicCompareAndSwap32:
+		return rewriteValueMIPS_OpAtomicCompareAndSwap32(v, config)
+	case OpAtomicExchange32:
+		return rewriteValueMIPS_OpAtomicExchange32(v, config)
+	case OpAtomicLoad32:
+		return rewriteValueMIPS_OpAtomicLoad32(v, config)
+	case OpAtomicLoadPtr:
+		return rewriteValueMIPS_OpAtomicLoadPtr(v, config)
+	case OpAtomicOr8:
+		return rewriteValueMIPS_OpAtomicOr8(v, config)
+	case OpAtomicStore32:
+		return rewriteValueMIPS_OpAtomicStore32(v, config)
+	case OpAtomicStorePtrNoWB:
+		return rewriteValueMIPS_OpAtomicStorePtrNoWB(v, config)
+	case OpClosureCall:
+		return rewriteValueMIPS_OpClosureCall(v, config)
+	case OpCom16:
+		return rewriteValueMIPS_OpCom16(v, config)
+	case OpCom32:
+		return rewriteValueMIPS_OpCom32(v, config)
+	case OpCom8:
+		return rewriteValueMIPS_OpCom8(v, config)
+	case OpConst16:
+		return rewriteValueMIPS_OpConst16(v, config)
+	case OpConst32:
+		return rewriteValueMIPS_OpConst32(v, config)
+	case OpConst32F:
+		return rewriteValueMIPS_OpConst32F(v, config)
+	case OpConst64F:
+		return rewriteValueMIPS_OpConst64F(v, config)
+	case OpConst8:
+		return rewriteValueMIPS_OpConst8(v, config)
+	case OpConstBool:
+		return rewriteValueMIPS_OpConstBool(v, config)
+	case OpConstNil:
+		return rewriteValueMIPS_OpConstNil(v, config)
+	case OpConvert:
+		return rewriteValueMIPS_OpConvert(v, config)
+	case OpCtz32:
+		return rewriteValueMIPS_OpCtz32(v, config)
+	case OpCvt32Fto32:
+		return rewriteValueMIPS_OpCvt32Fto32(v, config)
+	case OpCvt32Fto64F:
+		return rewriteValueMIPS_OpCvt32Fto64F(v, config)
+	case OpCvt32to32F:
+		return rewriteValueMIPS_OpCvt32to32F(v, config)
+	case OpCvt32to64F:
+		return rewriteValueMIPS_OpCvt32to64F(v, config)
+	case OpCvt64Fto32:
+		return rewriteValueMIPS_OpCvt64Fto32(v, config)
+	case OpCvt64Fto32F:
+		return rewriteValueMIPS_OpCvt64Fto32F(v, config)
+	case OpDeferCall:
+		return rewriteValueMIPS_OpDeferCall(v, config)
+	case OpDiv16:
+		return rewriteValueMIPS_OpDiv16(v, config)
+	case OpDiv16u:
+		return rewriteValueMIPS_OpDiv16u(v, config)
+	case OpDiv32:
+		return rewriteValueMIPS_OpDiv32(v, config)
+	case OpDiv32F:
+		return rewriteValueMIPS_OpDiv32F(v, config)
+	case OpDiv32u:
+		return rewriteValueMIPS_OpDiv32u(v, config)
+	case OpDiv64F:
+		return rewriteValueMIPS_OpDiv64F(v, config)
+	case OpDiv8:
+		return rewriteValueMIPS_OpDiv8(v, config)
+	case OpDiv8u:
+		return rewriteValueMIPS_OpDiv8u(v, config)
+	case OpEq16:
+		return rewriteValueMIPS_OpEq16(v, config)
+	case OpEq32:
+		return rewriteValueMIPS_OpEq32(v, config)
+	case OpEq32F:
+		return rewriteValueMIPS_OpEq32F(v, config)
+	case OpEq64F:
+		return rewriteValueMIPS_OpEq64F(v, config)
+	case OpEq8:
+		return rewriteValueMIPS_OpEq8(v, config)
+	case OpEqB:
+		return rewriteValueMIPS_OpEqB(v, config)
+	case OpEqPtr:
+		return rewriteValueMIPS_OpEqPtr(v, config)
+	case OpGeq16:
+		return rewriteValueMIPS_OpGeq16(v, config)
+	case OpGeq16U:
+		return rewriteValueMIPS_OpGeq16U(v, config)
+	case OpGeq32:
+		return rewriteValueMIPS_OpGeq32(v, config)
+	case OpGeq32F:
+		return rewriteValueMIPS_OpGeq32F(v, config)
+	case OpGeq32U:
+		return rewriteValueMIPS_OpGeq32U(v, config)
+	case OpGeq64F:
+		return rewriteValueMIPS_OpGeq64F(v, config)
+	case OpGeq8:
+		return rewriteValueMIPS_OpGeq8(v, config)
+	case OpGeq8U:
+		return rewriteValueMIPS_OpGeq8U(v, config)
+	case OpGetClosurePtr:
+		return rewriteValueMIPS_OpGetClosurePtr(v, config)
+	case OpGoCall:
+		return rewriteValueMIPS_OpGoCall(v, config)
+	case OpGreater16:
+		return rewriteValueMIPS_OpGreater16(v, config)
+	case OpGreater16U:
+		return rewriteValueMIPS_OpGreater16U(v, config)
+	case OpGreater32:
+		return rewriteValueMIPS_OpGreater32(v, config)
+	case OpGreater32F:
+		return rewriteValueMIPS_OpGreater32F(v, config)
+	case OpGreater32U:
+		return rewriteValueMIPS_OpGreater32U(v, config)
+	case OpGreater64F:
+		return rewriteValueMIPS_OpGreater64F(v, config)
+	case OpGreater8:
+		return rewriteValueMIPS_OpGreater8(v, config)
+	case OpGreater8U:
+		return rewriteValueMIPS_OpGreater8U(v, config)
+	case OpHmul16:
+		return rewriteValueMIPS_OpHmul16(v, config)
+	case OpHmul16u:
+		return rewriteValueMIPS_OpHmul16u(v, config)
+	case OpHmul32:
+		return rewriteValueMIPS_OpHmul32(v, config)
+	case OpHmul32u:
+		return rewriteValueMIPS_OpHmul32u(v, config)
+	case OpHmul8:
+		return rewriteValueMIPS_OpHmul8(v, config)
+	case OpHmul8u:
+		return rewriteValueMIPS_OpHmul8u(v, config)
+	case OpInterCall:
+		return rewriteValueMIPS_OpInterCall(v, config)
+	case OpIsInBounds:
+		return rewriteValueMIPS_OpIsInBounds(v, config)
+	case OpIsNonNil:
+		return rewriteValueMIPS_OpIsNonNil(v, config)
+	case OpIsSliceInBounds:
+		return rewriteValueMIPS_OpIsSliceInBounds(v, config)
+	case OpLeq16:
+		return rewriteValueMIPS_OpLeq16(v, config)
+	case OpLeq16U:
+		return rewriteValueMIPS_OpLeq16U(v, config)
+	case OpLeq32:
+		return rewriteValueMIPS_OpLeq32(v, config)
+	case OpLeq32F:
+		return rewriteValueMIPS_OpLeq32F(v, config)
+	case OpLeq32U:
+		return rewriteValueMIPS_OpLeq32U(v, config)
+	case OpLeq64F:
+		return rewriteValueMIPS_OpLeq64F(v, config)
+	case OpLeq8:
+		return rewriteValueMIPS_OpLeq8(v, config)
+	case OpLeq8U:
+		return rewriteValueMIPS_OpLeq8U(v, config)
+	case OpLess16:
+		return rewriteValueMIPS_OpLess16(v, config)
+	case OpLess16U:
+		return rewriteValueMIPS_OpLess16U(v, config)
+	case OpLess32:
+		return rewriteValueMIPS_OpLess32(v, config)
+	case OpLess32F:
+		return rewriteValueMIPS_OpLess32F(v, config)
+	case OpLess32U:
+		return rewriteValueMIPS_OpLess32U(v, config)
+	case OpLess64F:
+		return rewriteValueMIPS_OpLess64F(v, config)
+	case OpLess8:
+		return rewriteValueMIPS_OpLess8(v, config)
+	case OpLess8U:
+		return rewriteValueMIPS_OpLess8U(v, config)
+	case OpLoad:
+		return rewriteValueMIPS_OpLoad(v, config)
+	case OpLsh16x16:
+		return rewriteValueMIPS_OpLsh16x16(v, config)
+	case OpLsh16x32:
+		return rewriteValueMIPS_OpLsh16x32(v, config)
+	case OpLsh16x64:
+		return rewriteValueMIPS_OpLsh16x64(v, config)
+	case OpLsh16x8:
+		return rewriteValueMIPS_OpLsh16x8(v, config)
+	case OpLsh32x16:
+		return rewriteValueMIPS_OpLsh32x16(v, config)
+	case OpLsh32x32:
+		return rewriteValueMIPS_OpLsh32x32(v, config)
+	case OpLsh32x64:
+		return rewriteValueMIPS_OpLsh32x64(v, config)
+	case OpLsh32x8:
+		return rewriteValueMIPS_OpLsh32x8(v, config)
+	case OpLsh8x16:
+		return rewriteValueMIPS_OpLsh8x16(v, config)
+	case OpLsh8x32:
+		return rewriteValueMIPS_OpLsh8x32(v, config)
+	case OpLsh8x64:
+		return rewriteValueMIPS_OpLsh8x64(v, config)
+	case OpLsh8x8:
+		return rewriteValueMIPS_OpLsh8x8(v, config)
+	case OpMIPSADD:
+		return rewriteValueMIPS_OpMIPSADD(v, config)
+	case OpMIPSADDconst:
+		return rewriteValueMIPS_OpMIPSADDconst(v, config)
+	case OpMIPSAND:
+		return rewriteValueMIPS_OpMIPSAND(v, config)
+	case OpMIPSANDconst:
+		return rewriteValueMIPS_OpMIPSANDconst(v, config)
+	case OpMIPSCMOVZ:
+		return rewriteValueMIPS_OpMIPSCMOVZ(v, config)
+	case OpMIPSCMOVZzero:
+		return rewriteValueMIPS_OpMIPSCMOVZzero(v, config)
+	case OpMIPSLoweredAtomicAdd:
+		return rewriteValueMIPS_OpMIPSLoweredAtomicAdd(v, config)
+	case OpMIPSLoweredAtomicStore:
+		return rewriteValueMIPS_OpMIPSLoweredAtomicStore(v, config)
+	case OpMIPSMOVBUload:
+		return rewriteValueMIPS_OpMIPSMOVBUload(v, config)
+	case OpMIPSMOVBUreg:
+		return rewriteValueMIPS_OpMIPSMOVBUreg(v, config)
+	case OpMIPSMOVBload:
+		return rewriteValueMIPS_OpMIPSMOVBload(v, config)
+	case OpMIPSMOVBreg:
+		return rewriteValueMIPS_OpMIPSMOVBreg(v, config)
+	case OpMIPSMOVBstore:
+		return rewriteValueMIPS_OpMIPSMOVBstore(v, config)
+	case OpMIPSMOVBstorezero:
+		return rewriteValueMIPS_OpMIPSMOVBstorezero(v, config)
+	case OpMIPSMOVDload:
+		return rewriteValueMIPS_OpMIPSMOVDload(v, config)
+	case OpMIPSMOVDstore:
+		return rewriteValueMIPS_OpMIPSMOVDstore(v, config)
+	case OpMIPSMOVFload:
+		return rewriteValueMIPS_OpMIPSMOVFload(v, config)
+	case OpMIPSMOVFstore:
+		return rewriteValueMIPS_OpMIPSMOVFstore(v, config)
+	case OpMIPSMOVHUload:
+		return rewriteValueMIPS_OpMIPSMOVHUload(v, config)
+	case OpMIPSMOVHUreg:
+		return rewriteValueMIPS_OpMIPSMOVHUreg(v, config)
+	case OpMIPSMOVHload:
+		return rewriteValueMIPS_OpMIPSMOVHload(v, config)
+	case OpMIPSMOVHreg:
+		return rewriteValueMIPS_OpMIPSMOVHreg(v, config)
+	case OpMIPSMOVHstore:
+		return rewriteValueMIPS_OpMIPSMOVHstore(v, config)
+	case OpMIPSMOVHstorezero:
+		return rewriteValueMIPS_OpMIPSMOVHstorezero(v, config)
+	case OpMIPSMOVWload:
+		return rewriteValueMIPS_OpMIPSMOVWload(v, config)
+	case OpMIPSMOVWreg:
+		return rewriteValueMIPS_OpMIPSMOVWreg(v, config)
+	case OpMIPSMOVWstore:
+		return rewriteValueMIPS_OpMIPSMOVWstore(v, config)
+	case OpMIPSMOVWstorezero:
+		return rewriteValueMIPS_OpMIPSMOVWstorezero(v, config)
+	case OpMIPSMUL:
+		return rewriteValueMIPS_OpMIPSMUL(v, config)
+	case OpMIPSNEG:
+		return rewriteValueMIPS_OpMIPSNEG(v, config)
+	case OpMIPSNOR:
+		return rewriteValueMIPS_OpMIPSNOR(v, config)
+	case OpMIPSNORconst:
+		return rewriteValueMIPS_OpMIPSNORconst(v, config)
+	case OpMIPSOR:
+		return rewriteValueMIPS_OpMIPSOR(v, config)
+	case OpMIPSORconst:
+		return rewriteValueMIPS_OpMIPSORconst(v, config)
+	case OpMIPSSGT:
+		return rewriteValueMIPS_OpMIPSSGT(v, config)
+	case OpMIPSSGTU:
+		return rewriteValueMIPS_OpMIPSSGTU(v, config)
+	case OpMIPSSGTUconst:
+		return rewriteValueMIPS_OpMIPSSGTUconst(v, config)
+	case OpMIPSSGTUzero:
+		return rewriteValueMIPS_OpMIPSSGTUzero(v, config)
+	case OpMIPSSGTconst:
+		return rewriteValueMIPS_OpMIPSSGTconst(v, config)
+	case OpMIPSSGTzero:
+		return rewriteValueMIPS_OpMIPSSGTzero(v, config)
+	case OpMIPSSLL:
+		return rewriteValueMIPS_OpMIPSSLL(v, config)
+	case OpMIPSSLLconst:
+		return rewriteValueMIPS_OpMIPSSLLconst(v, config)
+	case OpMIPSSRA:
+		return rewriteValueMIPS_OpMIPSSRA(v, config)
+	case OpMIPSSRAconst:
+		return rewriteValueMIPS_OpMIPSSRAconst(v, config)
+	case OpMIPSSRL:
+		return rewriteValueMIPS_OpMIPSSRL(v, config)
+	case OpMIPSSRLconst:
+		return rewriteValueMIPS_OpMIPSSRLconst(v, config)
+	case OpMIPSSUB:
+		return rewriteValueMIPS_OpMIPSSUB(v, config)
+	case OpMIPSSUBconst:
+		return rewriteValueMIPS_OpMIPSSUBconst(v, config)
+	case OpMIPSXOR:
+		return rewriteValueMIPS_OpMIPSXOR(v, config)
+	case OpMIPSXORconst:
+		return rewriteValueMIPS_OpMIPSXORconst(v, config)
+	case OpMod16:
+		return rewriteValueMIPS_OpMod16(v, config)
+	case OpMod16u:
+		return rewriteValueMIPS_OpMod16u(v, config)
+	case OpMod32:
+		return rewriteValueMIPS_OpMod32(v, config)
+	case OpMod32u:
+		return rewriteValueMIPS_OpMod32u(v, config)
+	case OpMod8:
+		return rewriteValueMIPS_OpMod8(v, config)
+	case OpMod8u:
+		return rewriteValueMIPS_OpMod8u(v, config)
+	case OpMove:
+		return rewriteValueMIPS_OpMove(v, config)
+	case OpMul16:
+		return rewriteValueMIPS_OpMul16(v, config)
+	case OpMul32:
+		return rewriteValueMIPS_OpMul32(v, config)
+	case OpMul32F:
+		return rewriteValueMIPS_OpMul32F(v, config)
+	case OpMul32uhilo:
+		return rewriteValueMIPS_OpMul32uhilo(v, config)
+	case OpMul64F:
+		return rewriteValueMIPS_OpMul64F(v, config)
+	case OpMul8:
+		return rewriteValueMIPS_OpMul8(v, config)
+	case OpNeg16:
+		return rewriteValueMIPS_OpNeg16(v, config)
+	case OpNeg32:
+		return rewriteValueMIPS_OpNeg32(v, config)
+	case OpNeg32F:
+		return rewriteValueMIPS_OpNeg32F(v, config)
+	case OpNeg64F:
+		return rewriteValueMIPS_OpNeg64F(v, config)
+	case OpNeg8:
+		return rewriteValueMIPS_OpNeg8(v, config)
+	case OpNeq16:
+		return rewriteValueMIPS_OpNeq16(v, config)
+	case OpNeq32:
+		return rewriteValueMIPS_OpNeq32(v, config)
+	case OpNeq32F:
+		return rewriteValueMIPS_OpNeq32F(v, config)
+	case OpNeq64F:
+		return rewriteValueMIPS_OpNeq64F(v, config)
+	case OpNeq8:
+		return rewriteValueMIPS_OpNeq8(v, config)
+	case OpNeqB:
+		return rewriteValueMIPS_OpNeqB(v, config)
+	case OpNeqPtr:
+		return rewriteValueMIPS_OpNeqPtr(v, config)
+	case OpNilCheck:
+		return rewriteValueMIPS_OpNilCheck(v, config)
+	case OpNot:
+		return rewriteValueMIPS_OpNot(v, config)
+	case OpOffPtr:
+		return rewriteValueMIPS_OpOffPtr(v, config)
+	case OpOr16:
+		return rewriteValueMIPS_OpOr16(v, config)
+	case OpOr32:
+		return rewriteValueMIPS_OpOr32(v, config)
+	case OpOr8:
+		return rewriteValueMIPS_OpOr8(v, config)
+	case OpOrB:
+		return rewriteValueMIPS_OpOrB(v, config)
+	case OpRsh16Ux16:
+		return rewriteValueMIPS_OpRsh16Ux16(v, config)
+	case OpRsh16Ux32:
+		return rewriteValueMIPS_OpRsh16Ux32(v, config)
+	case OpRsh16Ux64:
+		return rewriteValueMIPS_OpRsh16Ux64(v, config)
+	case OpRsh16Ux8:
+		return rewriteValueMIPS_OpRsh16Ux8(v, config)
+	case OpRsh16x16:
+		return rewriteValueMIPS_OpRsh16x16(v, config)
+	case OpRsh16x32:
+		return rewriteValueMIPS_OpRsh16x32(v, config)
+	case OpRsh16x64:
+		return rewriteValueMIPS_OpRsh16x64(v, config)
+	case OpRsh16x8:
+		return rewriteValueMIPS_OpRsh16x8(v, config)
+	case OpRsh32Ux16:
+		return rewriteValueMIPS_OpRsh32Ux16(v, config)
+	case OpRsh32Ux32:
+		return rewriteValueMIPS_OpRsh32Ux32(v, config)
+	case OpRsh32Ux64:
+		return rewriteValueMIPS_OpRsh32Ux64(v, config)
+	case OpRsh32Ux8:
+		return rewriteValueMIPS_OpRsh32Ux8(v, config)
+	case OpRsh32x16:
+		return rewriteValueMIPS_OpRsh32x16(v, config)
+	case OpRsh32x32:
+		return rewriteValueMIPS_OpRsh32x32(v, config)
+	case OpRsh32x64:
+		return rewriteValueMIPS_OpRsh32x64(v, config)
+	case OpRsh32x8:
+		return rewriteValueMIPS_OpRsh32x8(v, config)
+	case OpRsh8Ux16:
+		return rewriteValueMIPS_OpRsh8Ux16(v, config)
+	case OpRsh8Ux32:
+		return rewriteValueMIPS_OpRsh8Ux32(v, config)
+	case OpRsh8Ux64:
+		return rewriteValueMIPS_OpRsh8Ux64(v, config)
+	case OpRsh8Ux8:
+		return rewriteValueMIPS_OpRsh8Ux8(v, config)
+	case OpRsh8x16:
+		return rewriteValueMIPS_OpRsh8x16(v, config)
+	case OpRsh8x32:
+		return rewriteValueMIPS_OpRsh8x32(v, config)
+	case OpRsh8x64:
+		return rewriteValueMIPS_OpRsh8x64(v, config)
+	case OpRsh8x8:
+		return rewriteValueMIPS_OpRsh8x8(v, config)
+	case OpSelect0:
+		return rewriteValueMIPS_OpSelect0(v, config)
+	case OpSelect1:
+		return rewriteValueMIPS_OpSelect1(v, config)
+	case OpSignExt16to32:
+		return rewriteValueMIPS_OpSignExt16to32(v, config)
+	case OpSignExt8to16:
+		return rewriteValueMIPS_OpSignExt8to16(v, config)
+	case OpSignExt8to32:
+		return rewriteValueMIPS_OpSignExt8to32(v, config)
+	case OpSignmask:
+		return rewriteValueMIPS_OpSignmask(v, config)
+	case OpSlicemask:
+		return rewriteValueMIPS_OpSlicemask(v, config)
+	case OpSqrt:
+		return rewriteValueMIPS_OpSqrt(v, config)
+	case OpStaticCall:
+		return rewriteValueMIPS_OpStaticCall(v, config)
+	case OpStore:
+		return rewriteValueMIPS_OpStore(v, config)
+	case OpSub16:
+		return rewriteValueMIPS_OpSub16(v, config)
+	case OpSub32:
+		return rewriteValueMIPS_OpSub32(v, config)
+	case OpSub32F:
+		return rewriteValueMIPS_OpSub32F(v, config)
+	case OpSub32withcarry:
+		return rewriteValueMIPS_OpSub32withcarry(v, config)
+	case OpSub64F:
+		return rewriteValueMIPS_OpSub64F(v, config)
+	case OpSub8:
+		return rewriteValueMIPS_OpSub8(v, config)
+	case OpSubPtr:
+		return rewriteValueMIPS_OpSubPtr(v, config)
+	case OpTrunc16to8:
+		return rewriteValueMIPS_OpTrunc16to8(v, config)
+	case OpTrunc32to16:
+		return rewriteValueMIPS_OpTrunc32to16(v, config)
+	case OpTrunc32to8:
+		return rewriteValueMIPS_OpTrunc32to8(v, config)
+	case OpXor16:
+		return rewriteValueMIPS_OpXor16(v, config)
+	case OpXor32:
+		return rewriteValueMIPS_OpXor32(v, config)
+	case OpXor8:
+		return rewriteValueMIPS_OpXor8(v, config)
+	case OpZero:
+		return rewriteValueMIPS_OpZero(v, config)
+	case OpZeroExt16to32:
+		return rewriteValueMIPS_OpZeroExt16to32(v, config)
+	case OpZeroExt8to16:
+		return rewriteValueMIPS_OpZeroExt8to16(v, config)
+	case OpZeroExt8to32:
+		return rewriteValueMIPS_OpZeroExt8to32(v, config)
+	case OpZeromask:
+		return rewriteValueMIPS_OpZeromask(v, config)
+	}
+	return false
+}
+func rewriteValueMIPS_OpAdd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add16 x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32 x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAdd32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32F x y)
+	// cond:
+	// result: (ADDF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSADDF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAdd32withcarry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32withcarry <t> x y c)
+	// cond:
+	// result: (ADD c (ADD <t> x y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		c := v.Args[2]
+		v.reset(OpMIPSADD)
+		v.AddArg(c)
+		v0 := b.NewValue0(v.Line, OpMIPSADD, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAdd64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64F x y)
+	// cond:
+	// result: (ADDD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSADDD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAdd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add8 x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAddPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AddPtr x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAddr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Addr {sym} base)
+	// cond:
+	// result: (MOVWaddr {sym} base)
+	for {
+		sym := v.Aux
+		base := v.Args[0]
+		v.reset(OpMIPSMOVWaddr)
+		v.Aux = sym
+		v.AddArg(base)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAnd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And16 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSAND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAnd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And32 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSAND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And8 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSAND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAndB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AndB x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSAND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAtomicAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicAdd32 ptr val mem)
+	// cond:
+	// result: (LoweredAtomicAdd ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpMIPSLoweredAtomicAdd)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAtomicAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicAnd8  ptr val mem)
+	// cond: !config.BigEndian
+	// result: (LoweredAtomicAnd (AND <config.fe.TypeUInt32().PtrTo()> (MOVWconst [^3]) ptr) 		(OR <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()> (ZeroExt8to32 val) 			(SLLconst <config.fe.TypeUInt32()> [3] 				(ANDconst  <config.fe.TypeUInt32()> [3] ptr))) 		(NORconst [0] <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()> 			(MOVWconst [0xff]) (SLLconst <config.fe.TypeUInt32()> [3] 				(ANDconst <config.fe.TypeUInt32()> [3] 					(XORconst <config.fe.TypeUInt32()> [3] ptr)))))) mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(!config.BigEndian) {
+			break
+		}
+		v.reset(OpMIPSLoweredAtomicAnd)
+		v0 := b.NewValue0(v.Line, OpMIPSAND, config.fe.TypeUInt32().PtrTo())
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = ^3
+		v0.AddArg(v1)
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSOR, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpMIPSSLL, config.fe.TypeUInt32())
+		v4 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v4.AddArg(val)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v5.AuxInt = 3
+		v6 := b.NewValue0(v.Line, OpMIPSANDconst, config.fe.TypeUInt32())
+		v6.AuxInt = 3
+		v6.AddArg(ptr)
+		v5.AddArg(v6)
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v7 := b.NewValue0(v.Line, OpMIPSNORconst, config.fe.TypeUInt32())
+		v7.AuxInt = 0
+		v8 := b.NewValue0(v.Line, OpMIPSSLL, config.fe.TypeUInt32())
+		v9 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v9.AuxInt = 0xff
+		v8.AddArg(v9)
+		v10 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v10.AuxInt = 3
+		v11 := b.NewValue0(v.Line, OpMIPSANDconst, config.fe.TypeUInt32())
+		v11.AuxInt = 3
+		v12 := b.NewValue0(v.Line, OpMIPSXORconst, config.fe.TypeUInt32())
+		v12.AuxInt = 3
+		v12.AddArg(ptr)
+		v11.AddArg(v12)
+		v10.AddArg(v11)
+		v8.AddArg(v10)
+		v7.AddArg(v8)
+		v2.AddArg(v7)
+		v.AddArg(v2)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (AtomicAnd8  ptr val mem)
+	// cond: config.BigEndian
+	// result: (LoweredAtomicAnd (AND <config.fe.TypeUInt32().PtrTo()> (MOVWconst [^3]) ptr) 		(OR <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()> (ZeroExt8to32 val) 			(SLLconst <config.fe.TypeUInt32()> [3] 				(ANDconst  <config.fe.TypeUInt32()> [3] 					(XORconst <config.fe.TypeUInt32()> [3] ptr)))) 		(NORconst [0] <config.fe.TypeUInt32()> (SLL <config.fe.TypeUInt32()> 			(MOVWconst [0xff]) (SLLconst <config.fe.TypeUInt32()> [3] 				(ANDconst <config.fe.TypeUInt32()> [3] 					(XOR [...]
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(config.BigEndian) {
+			break
+		}
+		v.reset(OpMIPSLoweredAtomicAnd)
+		v0 := b.NewValue0(v.Line, OpMIPSAND, config.fe.TypeUInt32().PtrTo())
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = ^3
+		v0.AddArg(v1)
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSOR, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpMIPSSLL, config.fe.TypeUInt32())
+		v4 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v4.AddArg(val)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v5.AuxInt = 3
+		v6 := b.NewValue0(v.Line, OpMIPSANDconst, config.fe.TypeUInt32())
+		v6.AuxInt = 3
+		v7 := b.NewValue0(v.Line, OpMIPSXORconst, config.fe.TypeUInt32())
+		v7.AuxInt = 3
+		v7.AddArg(ptr)
+		v6.AddArg(v7)
+		v5.AddArg(v6)
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v8 := b.NewValue0(v.Line, OpMIPSNORconst, config.fe.TypeUInt32())
+		v8.AuxInt = 0
+		v9 := b.NewValue0(v.Line, OpMIPSSLL, config.fe.TypeUInt32())
+		v10 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v10.AuxInt = 0xff
+		v9.AddArg(v10)
+		v11 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v11.AuxInt = 3
+		v12 := b.NewValue0(v.Line, OpMIPSANDconst, config.fe.TypeUInt32())
+		v12.AuxInt = 3
+		v13 := b.NewValue0(v.Line, OpMIPSXORconst, config.fe.TypeUInt32())
+		v13.AuxInt = 3
+		v13.AddArg(ptr)
+		v12.AddArg(v13)
+		v11.AddArg(v12)
+		v9.AddArg(v11)
+		v8.AddArg(v9)
+		v2.AddArg(v8)
+		v.AddArg(v2)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpAtomicCompareAndSwap32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicCompareAndSwap32 ptr old new_ mem)
+	// cond:
+	// result: (LoweredAtomicCas ptr old new_ mem)
+	for {
+		ptr := v.Args[0]
+		old := v.Args[1]
+		new_ := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpMIPSLoweredAtomicCas)
+		v.AddArg(ptr)
+		v.AddArg(old)
+		v.AddArg(new_)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAtomicExchange32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicExchange32 ptr val mem)
+	// cond:
+	// result: (LoweredAtomicExchange ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpMIPSLoweredAtomicExchange)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAtomicLoad32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoad32  ptr mem)
+	// cond:
+	// result: (LoweredAtomicLoad ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpMIPSLoweredAtomicLoad)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAtomicLoadPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoadPtr ptr mem)
+	// cond:
+	// result: (LoweredAtomicLoad  ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpMIPSLoweredAtomicLoad)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAtomicOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicOr8 ptr val mem)
+	// cond: !config.BigEndian
+	// result: (LoweredAtomicOr (AND <config.fe.TypeUInt32().PtrTo()> (MOVWconst [^3]) ptr) 		(SLL <config.fe.TypeUInt32()> (ZeroExt8to32 val) 			(SLLconst <config.fe.TypeUInt32()> [3] 				(ANDconst <config.fe.TypeUInt32()> [3] ptr))) mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(!config.BigEndian) {
+			break
+		}
+		v.reset(OpMIPSLoweredAtomicOr)
+		v0 := b.NewValue0(v.Line, OpMIPSAND, config.fe.TypeUInt32().PtrTo())
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = ^3
+		v0.AddArg(v1)
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSSLL, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v3.AddArg(val)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v4.AuxInt = 3
+		v5 := b.NewValue0(v.Line, OpMIPSANDconst, config.fe.TypeUInt32())
+		v5.AuxInt = 3
+		v5.AddArg(ptr)
+		v4.AddArg(v5)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (AtomicOr8 ptr val mem)
+	// cond: config.BigEndian
+	// result: (LoweredAtomicOr (AND <config.fe.TypeUInt32().PtrTo()> (MOVWconst [^3]) ptr) 		(SLL <config.fe.TypeUInt32()> (ZeroExt8to32 val) 			(SLLconst <config.fe.TypeUInt32()> [3] 				(ANDconst <config.fe.TypeUInt32()> [3] 					(XORconst <config.fe.TypeUInt32()> [3] ptr)))) mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(config.BigEndian) {
+			break
+		}
+		v.reset(OpMIPSLoweredAtomicOr)
+		v0 := b.NewValue0(v.Line, OpMIPSAND, config.fe.TypeUInt32().PtrTo())
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = ^3
+		v0.AddArg(v1)
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSSLL, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v3.AddArg(val)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v4.AuxInt = 3
+		v5 := b.NewValue0(v.Line, OpMIPSANDconst, config.fe.TypeUInt32())
+		v5.AuxInt = 3
+		v6 := b.NewValue0(v.Line, OpMIPSXORconst, config.fe.TypeUInt32())
+		v6.AuxInt = 3
+		v6.AddArg(ptr)
+		v5.AddArg(v6)
+		v4.AddArg(v5)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpAtomicStore32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStore32      ptr val mem)
+	// cond:
+	// result: (LoweredAtomicStore ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpMIPSLoweredAtomicStore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpAtomicStorePtrNoWB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStorePtrNoWB ptr val mem)
+	// cond:
+	// result: (LoweredAtomicStore  ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpMIPSLoweredAtomicStore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpClosureCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ClosureCall [argwid] entry closure mem)
+	// cond:
+	// result: (CALLclosure [argwid] entry closure mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		closure := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpMIPSCALLclosure)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(closure)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpCom16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com16 x)
+	// cond:
+	// result: (NORconst [0] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSNORconst)
+		v.AuxInt = 0
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpCom32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com32 x)
+	// cond:
+	// result: (NORconst [0] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSNORconst)
+		v.AuxInt = 0
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpCom8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com8 x)
+	// cond:
+	// result: (NORconst [0] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSNORconst)
+		v.AuxInt = 0
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpConst16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const16 [val])
+	// cond:
+	// result: (MOVWconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS_OpConst32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32 [val])
+	// cond:
+	// result: (MOVWconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS_OpConst32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32F [val])
+	// cond:
+	// result: (MOVFconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPSMOVFconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS_OpConst64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64F [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPSMOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS_OpConst8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const8 [val])
+	// cond:
+	// result: (MOVWconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS_OpConstBool(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstBool [b])
+	// cond:
+	// result: (MOVWconst [b])
+	for {
+		b := v.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = b
+		return true
+	}
+}
+func rewriteValueMIPS_OpConstNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstNil)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+}
+func rewriteValueMIPS_OpConvert(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Convert x mem)
+	// cond:
+	// result: (MOVWconvert x mem)
+	for {
+		x := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpMIPSMOVWconvert)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpCtz32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Ctz32 <t> x)
+	// cond:
+	// result: (SUB (MOVWconst [32]) (CLZ <t> (SUBconst <t> [1] (AND <t> x (NEG <t> x)))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpMIPSSUB)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 32
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSCLZ, t)
+		v2 := b.NewValue0(v.Line, OpMIPSSUBconst, t)
+		v2.AuxInt = 1
+		v3 := b.NewValue0(v.Line, OpMIPSAND, t)
+		v3.AddArg(x)
+		v4 := b.NewValue0(v.Line, OpMIPSNEG, t)
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpCvt32Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto32 x)
+	// cond:
+	// result: (TRUNCFW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSTRUNCFW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpCvt32Fto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64F x)
+	// cond:
+	// result: (MOVFD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSMOVFD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpCvt32to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to32F x)
+	// cond:
+	// result: (MOVWF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSMOVWF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpCvt32to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to64F x)
+	// cond:
+	// result: (MOVWD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSMOVWD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpCvt64Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32 x)
+	// cond:
+	// result: (TRUNCDW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSTRUNCDW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpCvt64Fto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32F x)
+	// cond:
+	// result: (MOVDF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSMOVDF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpDeferCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (DeferCall [argwid] mem)
+	// cond:
+	// result: (CALLdefer [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpMIPSCALLdefer)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpDiv16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16 x y)
+	// cond:
+	// result: (Select1 (DIV (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPSDIV, MakeTuple(config.fe.TypeInt32(), config.fe.TypeInt32()))
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpDiv16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16u x y)
+	// cond:
+	// result: (Select1 (DIVU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPSDIVU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpDiv32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32 x y)
+	// cond:
+	// result: (Select1 (DIV x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPSDIV, MakeTuple(config.fe.TypeInt32(), config.fe.TypeInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpDiv32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32F x y)
+	// cond:
+	// result: (DIVF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSDIVF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpDiv32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32u x y)
+	// cond:
+	// result: (Select1 (DIVU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPSDIVU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpDiv64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64F x y)
+	// cond:
+	// result: (DIVD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSDIVD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpDiv8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8 x y)
+	// cond:
+	// result: (Select1 (DIV (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPSDIV, MakeTuple(config.fe.TypeInt32(), config.fe.TypeInt32()))
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpDiv8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8u x y)
+	// cond:
+	// result: (Select1 (DIVU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPSDIVU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpEq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq16 x y)
+	// cond:
+	// result: (SGTUconst [1] (XOR (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTUconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSXOR, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpEq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32 x y)
+	// cond:
+	// result: (SGTUconst [1] (XOR x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTUconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSXOR, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpEq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPEQF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPEQF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpEq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPEQD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPEQD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpEq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq8 x y)
+	// cond:
+	// result: (SGTUconst [1] (XOR (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTUconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSXOR, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpEqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqB x y)
+	// cond:
+	// result: (XORconst [1] (XOR <config.fe.TypeBool()> x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSXOR, config.fe.TypeBool())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpEqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqPtr x y)
+	// cond:
+	// result: (SGTUconst [1] (XOR x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTUconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSXOR, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16 x y)
+	// cond:
+	// result: (XORconst [1] (SGT (SignExt16to32 y) (SignExt16to32 x)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGT, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(x)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16U x y)
+	// cond:
+	// result: (XORconst [1] (SGTU (ZeroExt16to32 y) (ZeroExt16to32 x)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGTU, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(x)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32 x y)
+	// cond:
+	// result: (XORconst [1] (SGT y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGT, config.fe.TypeBool())
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGEF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPGEF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32U x y)
+	// cond:
+	// result: (XORconst [1] (SGTU y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGTU, config.fe.TypeBool())
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGED x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPGED, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8 x y)
+	// cond:
+	// result: (XORconst [1] (SGT (SignExt8to32 y) (SignExt8to32 x)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGT, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(x)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8U x y)
+	// cond:
+	// result: (XORconst [1] (SGTU (ZeroExt8to32 y) (ZeroExt8to32 x)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGTU, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(x)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGetClosurePtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetClosurePtr)
+	// cond:
+	// result: (LoweredGetClosurePtr)
+	for {
+		v.reset(OpMIPSLoweredGetClosurePtr)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGoCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GoCall [argwid] mem)
+	// cond:
+	// result: (CALLgo [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpMIPSCALLgo)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGreater16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16 x y)
+	// cond:
+	// result: (SGT (SignExt16to32 x) (SignExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGT)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGreater16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16U x y)
+	// cond:
+	// result: (SGTU (ZeroExt16to32 x) (ZeroExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGreater32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32 x y)
+	// cond:
+	// result: (SGT x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGT)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGreater32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGTF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPGTF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGreater32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32U x y)
+	// cond:
+	// result: (SGTU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGreater64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGTD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPGTD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGreater8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8 x y)
+	// cond:
+	// result: (SGT (SignExt8to32 x) (SignExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGT)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpGreater8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8U x y)
+	// cond:
+	// result: (SGTU (ZeroExt8to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpHmul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16 x y)
+	// cond:
+	// result: (SRAconst (MUL <config.fe.TypeInt32()> (SignExt16to32 x) (SignExt16to32 y)) [16])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpMIPSMUL, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpHmul16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16u x y)
+	// cond:
+	// result: (SRLconst (MUL <config.fe.TypeUInt32()> (ZeroExt16to32 x) (ZeroExt16to32 y)) [16])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRLconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpMIPSMUL, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpHmul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32 x y)
+	// cond:
+	// result: (Select0 (MULT x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPSMULT, MakeTuple(config.fe.TypeInt32(), config.fe.TypeInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpHmul32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32u x y)
+	// cond:
+	// result: (Select0 (MULTU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPSMULTU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpHmul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8 x y)
+	// cond:
+	// result: (SRAconst  (MUL <config.fe.TypeInt32()> (SignExt8to32 x) (SignExt8to32 y)) [8])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpMIPSMUL, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpHmul8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8u x y)
+	// cond:
+	// result: (SRLconst (MUL <config.fe.TypeUInt32()> (ZeroExt8to32 x) (ZeroExt8to32 y)) [8])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRLconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpMIPSMUL, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpInterCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (InterCall [argwid] entry mem)
+	// cond:
+	// result: (CALLinter [argwid] entry mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpMIPSCALLinter)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpIsInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsInBounds idx len)
+	// cond:
+	// result: (SGTU len idx)
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v.AddArg(len)
+		v.AddArg(idx)
+		return true
+	}
+}
+func rewriteValueMIPS_OpIsNonNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsNonNil ptr)
+	// cond:
+	// result: (SGTU ptr (MOVWconst [0]))
+	for {
+		ptr := v.Args[0]
+		v.reset(OpMIPSSGTU)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpIsSliceInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsSliceInBounds idx len)
+	// cond:
+	// result: (XORconst [1] (SGTU idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGTU, config.fe.TypeBool())
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16 x y)
+	// cond:
+	// result: (XORconst [1] (SGT (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGT, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16U x y)
+	// cond:
+	// result: (XORconst [1] (SGTU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGTU, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32 x y)
+	// cond:
+	// result: (XORconst [1] (SGT x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGT, config.fe.TypeBool())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGEF y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPGEF, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32U x y)
+	// cond:
+	// result: (XORconst [1] (SGTU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGTU, config.fe.TypeBool())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGED y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPGED, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8 x y)
+	// cond:
+	// result: (XORconst [1] (SGT (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGT, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8U x y)
+	// cond:
+	// result: (XORconst [1] (SGTU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSSGTU, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLess16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16 x y)
+	// cond:
+	// result: (SGT (SignExt16to32 y) (SignExt16to32 x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGT)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLess16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16U x y)
+	// cond:
+	// result: (SGTU (ZeroExt16to32 y) (ZeroExt16to32 x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLess32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32 x y)
+	// cond:
+	// result: (SGT y x)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGT)
+		v.AddArg(y)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLess32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGTF y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPGTF, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLess32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32U x y)
+	// cond:
+	// result: (SGTU y x)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v.AddArg(y)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLess64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGTD y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPGTD, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLess8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8 x y)
+	// cond:
+	// result: (SGT (SignExt8to32 y) (SignExt8to32 x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGT)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLess8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8U x y)
+	// cond:
+	// result: (SGTU (ZeroExt8to32 y) (ZeroExt8to32 x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLoad(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Load <t> ptr mem)
+	// cond: t.IsBoolean()
+	// result: (MOVBUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(t.IsBoolean()) {
+			break
+		}
+		v.reset(OpMIPSMOVBUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is8BitInt(t) && isSigned(t))
+	// result: (MOVBload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is8BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpMIPSMOVBload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is8BitInt(t) && !isSigned(t))
+	// result: (MOVBUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is8BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpMIPSMOVBUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is16BitInt(t) && isSigned(t))
+	// result: (MOVHload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpMIPSMOVHload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is16BitInt(t) && !isSigned(t))
+	// result: (MOVHUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpMIPSMOVHUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is32BitInt(t) || isPtr(t))
+	// result: (MOVWload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t) || isPtr(t)) {
+			break
+		}
+		v.reset(OpMIPSMOVWload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitFloat(t)
+	// result: (MOVFload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitFloat(t)) {
+			break
+		}
+		v.reset(OpMIPSMOVFload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitFloat(t)
+	// result: (MOVDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitFloat(t)) {
+			break
+		}
+		v.reset(OpMIPSMOVDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpLsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x16 <t> x y)
+	// cond:
+	// result: (CMOVZ (SLL <t> x (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v4 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x32 <t> x y)
+	// cond:
+	// result: (CMOVZ (SLL <t> x y) (MOVWconst [0]) (SGTUconst [32] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x64 x (Const64 [c]))
+	// cond: uint32(c) < 16
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 16) {
+			break
+		}
+		v.reset(OpMIPSSLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh16x64 _ (Const64 [c]))
+	// cond: uint32(c) >= 16
+	// result: (MOVWconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 16) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpLsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x8 <t> x y)
+	// cond:
+	// result: (CMOVZ (SLL <t> x (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v4 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x16 <t> x y)
+	// cond:
+	// result: (CMOVZ (SLL <t> x (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v4 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x32 <t> x y)
+	// cond:
+	// result: (CMOVZ (SLL <t> x y) (MOVWconst [0]) (SGTUconst [32] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x64 x (Const64 [c]))
+	// cond: uint32(c) < 32
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 32) {
+			break
+		}
+		v.reset(OpMIPSSLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh32x64 _ (Const64 [c]))
+	// cond: uint32(c) >= 32
+	// result: (MOVWconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 32) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpLsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x8 <t> x y)
+	// cond:
+	// result: (CMOVZ (SLL <t> x (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v4 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x16 <t> x y)
+	// cond:
+	// result: (CMOVZ (SLL <t> x (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v4 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x32 <t> x y)
+	// cond:
+	// result: (CMOVZ (SLL <t> x y) (MOVWconst [0]) (SGTUconst [32] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSLL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueMIPS_OpLsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x64 x (Const64 [c]))
+	// cond: uint32(c) < 8
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 8) {
+			break
+		}
+		v.reset(OpMIPSSLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh8x64 _ (Const64 [c]))
+	// cond: uint32(c) >= 8
+	// result: (MOVWconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 8) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpLsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x8 <t> x y)
+	// cond:
+	// result: (CMOVZ (SLL <t> x (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSLL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v4 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMIPSADD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADD (MOVWconst [c]) x)
+	// cond:
+	// result: (ADDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpMIPSADDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADD x (MOVWconst [c]))
+	// cond:
+	// result: (ADDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPSADDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADD x (NEG y))
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSNEG {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpMIPSSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (NEG y) x)
+	// cond:
+	// result: (SUB x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSNEG {
+			break
+		}
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpMIPSSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSADDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDconst [off1] (MOVWaddr [off2] {sym} ptr))
+	// cond:
+	// result: (MOVWaddr [off1+off2] {sym} ptr)
+	for {
+		off1 := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym := v_0.Aux
+		ptr := v_0.Args[0]
+		v.reset(OpMIPSMOVWaddr)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		return true
+	}
+	// match: (ADDconst [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(int32(c+d))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(c + d))
+		return true
+	}
+	// match: (ADDconst [c] (ADDconst [d] x))
+	// cond:
+	// result: (ADDconst [int64(int32(c+d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpMIPSADDconst)
+		v.AuxInt = int64(int32(c + d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (SUBconst [d] x))
+	// cond:
+	// result: (ADDconst [int64(int32(c-d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSSUBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpMIPSADDconst)
+		v.AuxInt = int64(int32(c - d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSAND(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AND (MOVWconst [c]) x)
+	// cond:
+	// result: (ANDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpMIPSANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x (MOVWconst [c]))
+	// cond:
+	// result: (ANDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPSANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND (SGTUconst [1] x) (SGTUconst [1] y))
+	// cond:
+	// result: (SGTUconst [1] (OR <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSSGTUconst {
+			break
+		}
+		if v_0.AuxInt != 1 {
+			break
+		}
+		x := v_0.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSSGTUconst {
+			break
+		}
+		if v_1.AuxInt != 1 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpMIPSSGTUconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpMIPSOR, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSANDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDconst [0]  _)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (ANDconst [-1] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [c&d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = c & d
+		return true
+	}
+	// match: (ANDconst [c] (ANDconst [d] x))
+	// cond:
+	// result: (ANDconst [c&d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSANDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpMIPSANDconst)
+		v.AuxInt = c & d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSCMOVZ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMOVZ _ b (MOVWconst [0]))
+	// cond:
+	// result: b
+	for {
+		b := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_2.AuxInt != 0 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = b.Type
+		v.AddArg(b)
+		return true
+	}
+	// match: (CMOVZ a _ (MOVWconst [c]))
+	// cond: c!=0
+	// result: a
+	for {
+		a := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_2.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = a.Type
+		v.AddArg(a)
+		return true
+	}
+	// match: (CMOVZ a (MOVWconst [0]) c)
+	// cond:
+	// result: (CMOVZzero a c)
+	for {
+		a := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		c := v.Args[2]
+		v.reset(OpMIPSCMOVZzero)
+		v.AddArg(a)
+		v.AddArg(c)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSCMOVZzero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMOVZzero _ (MOVWconst [0]))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (CMOVZzero a (MOVWconst [c]))
+	// cond: c!=0
+	// result: a
+	for {
+		a := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = a.Type
+		v.AddArg(a)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSLoweredAtomicAdd(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LoweredAtomicAdd ptr (MOVWconst [c]) mem)
+	// cond: is16Bit(c)
+	// result: (LoweredAtomicAddconst [c] ptr mem)
+	for {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(is16Bit(c)) {
+			break
+		}
+		v.reset(OpMIPSLoweredAtomicAddconst)
+		v.AuxInt = c
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSLoweredAtomicStore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LoweredAtomicStore ptr (MOVWconst [0]) mem)
+	// cond:
+	// result: (LoweredAtomicStorezero ptr mem)
+	for {
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpMIPSLoweredAtomicStorezero)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVBUload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBUload [off1] {sym} x:(ADDconst [off2] ptr) mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVBUload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVBUload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBUload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVBUload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBUload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVBstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVBUreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBUreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBUload {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBUreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBUreg {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBUreg <t> x:(MOVBload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBUload <t> [off] {sym} ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpMIPSMOVBUload, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVBUreg (ANDconst [c] x))
+	// cond:
+	// result: (ANDconst [c&0xff] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSANDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpMIPSANDconst)
+		v.AuxInt = c & 0xff
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBUreg (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [int64(uint8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(uint8(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVBload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBload  [off1] {sym} x:(ADDconst [off2] ptr) mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVBload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVBstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVBreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBreg x:(MOVBload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBload {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg x:(MOVBreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBreg {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg <t> x:(MOVBUload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBload <t> [off] {sym} ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBUload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpMIPSMOVBload, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVBreg (ANDconst [c] x))
+	// cond: c & 0x80 == 0
+	// result: (ANDconst [c&0x7f] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSANDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(c&0x80 == 0) {
+			break
+		}
+		v.reset(OpMIPSANDconst)
+		v.AuxInt = c & 0x7f
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg  (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [int64(int8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int8(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVBstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstore [off1] {sym} x:(ADDconst [off2] ptr) val mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVBstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVWconst [0]) mem)
+	// cond:
+	// result: (MOVBstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVBstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVBreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBUreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVBUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVHreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVHreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVHUreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVHUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVBstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstorezero [off1] {sym} x:(ADDconst [off2] ptr) mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVBstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVBstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstorezero [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVBstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVDload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDload  [off1] {sym} x:(ADDconst [off2] ptr) mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVDload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVDstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDstore [off1] {sym} x:(ADDconst [off2] ptr) val mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVDstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVFload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVFload  [off1] {sym} x:(ADDconst [off2] ptr) mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVFload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVFload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVFload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVFload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVFload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVFload [off] {sym} ptr (MOVFstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVFstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVFstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVFstore [off1] {sym} x:(ADDconst [off2] ptr) val mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVFstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVFstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVFstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVFstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVFstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVHUload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHUload [off1] {sym} x:(ADDconst [off2] ptr) mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVHUload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVHUload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHUload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVHUload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHUload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVHstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && !isSigned(x.Type)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVHUreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHUreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBUload {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVHUload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVHUload {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBUreg {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVHUreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVHUreg {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg <t> x:(MOVHload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVHUload <t> [off] {sym} ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVHload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpMIPSMOVHUload, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVHUreg (ANDconst [c] x))
+	// cond:
+	// result: (ANDconst [c&0xffff] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSANDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpMIPSANDconst)
+		v.AuxInt = c & 0xffff
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [int64(uint16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(uint16(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVHload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHload  [off1] {sym} x:(ADDconst [off2] ptr) mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVHload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVHstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) && isSigned(x.Type)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVHreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHreg x:(MOVBload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBload {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBUload {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHload _ _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVHload {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBreg {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVBUreg {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHreg _))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVHreg {
+			break
+		}
+		v.reset(OpMIPSMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg <t> x:(MOVHUload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVHload <t> [off] {sym} ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		if x.Op != OpMIPSMOVHUload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpMIPSMOVHload, t)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVHreg (ANDconst [c] x))
+	// cond: c & 0x8000 == 0
+	// result: (ANDconst [c&0x7fff] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSANDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(c&0x8000 == 0) {
+			break
+		}
+		v.reset(OpMIPSANDconst)
+		v.AuxInt = c & 0x7fff
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg  (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [int64(int16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int16(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVHstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstore [off1] {sym} x:(ADDconst [off2] ptr) val mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVHstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVWconst [0]) mem)
+	// cond:
+	// result: (MOVHstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVHstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVHreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHUreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVHUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVHstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstorezero [off1] {sym} x:(ADDconst [off2] ptr) mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVHstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVHstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstorezero [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVHstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVWload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWload  [off1] {sym} x:(ADDconst [off2] ptr) mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVWload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVWreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWreg x)
+	// cond: x.Uses == 1
+	// result: (MOVWnop x)
+	for {
+		x := v.Args[0]
+		if !(x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVWnop)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg  (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = c
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVWstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstore [off1] {sym} x:(ADDconst [off2] ptr) val mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVWstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWconst [0]) mem)
+	// cond:
+	// result: (MOVWstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVWstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMOVWstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstorezero [off1] {sym} x:(ADDconst [off2] ptr) mem)
+	// cond: (is16Bit(off1+off2) || x.Uses == 1)
+	// result: (MOVWstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		x := v.Args[0]
+		if x.Op != OpMIPSADDconst {
+			break
+		}
+		off2 := x.AuxInt
+		ptr := x.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1+off2) || x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVWstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstorezero [off1] {sym1} (MOVWaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpMIPSMOVWstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSMUL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MUL (MOVWconst [0]) _ )
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_0.AuxInt != 0 {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (MUL (MOVWconst [1]) x )
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_0.AuxInt != 1 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVWconst [-1]) x )
+	// cond:
+	// result: (NEG x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_0.AuxInt != -1 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpMIPSNEG)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) x )
+	// cond: isPowerOfTwo(int64(uint32(c)))
+	// result: (SLLconst [log2(int64(uint32(c)))] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isPowerOfTwo(int64(uint32(c)))) {
+			break
+		}
+		v.reset(OpMIPSSLLconst)
+		v.AuxInt = log2(int64(uint32(c)))
+		v.AddArg(x)
+		return true
+	}
+	// match: (MUL (MOVWconst [c]) (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(int32(c)*int32(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(c) * int32(d))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSNEG(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NEG (MOVWconst [c]))
+	// cond:
+	// result: (MOVWconst [int64(int32(-c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(-c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSNOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NOR (MOVWconst [c]) x)
+	// cond:
+	// result: (NORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpMIPSNORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (NOR x (MOVWconst [c]))
+	// cond:
+	// result: (NORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPSNORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSNORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NORconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [^(c|d)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = ^(c | d)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OR  (MOVWconst [c]) x)
+	// cond:
+	// result: (ORconst  [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpMIPSORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR  x (MOVWconst [c]))
+	// cond:
+	// result: (ORconst  [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPSORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR  x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR (SGTUzero x) (SGTUzero y))
+	// cond:
+	// result: (SGTUzero (OR <x.Type> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSSGTUzero {
+			break
+		}
+		x := v_0.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSSGTUzero {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpMIPSSGTUzero)
+		v0 := b.NewValue0(v.Line, OpMIPSOR, x.Type)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORconst  [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORconst  [-1] _)
+	// cond:
+	// result: (MOVWconst [-1])
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (ORconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [c|d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = c | d
+		return true
+	}
+	// match: (ORconst [c] (ORconst [d] x))
+	// cond:
+	// result: (ORconst [c|d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSORconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpMIPSORconst)
+		v.AuxInt = c | d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSGT(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SGT  (MOVWconst [c]) x)
+	// cond:
+	// result: (SGTconst  [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpMIPSSGTconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SGT x (MOVWconst [0]))
+	// cond:
+	// result: (SGTzero x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpMIPSSGTzero)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSGTU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SGTU (MOVWconst [c]) x)
+	// cond:
+	// result: (SGTUconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpMIPSSGTUconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SGTU x (MOVWconst [0]))
+	// cond:
+	// result: (SGTUzero x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpMIPSSGTUzero)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSGTUconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SGTUconst [c] (MOVWconst [d]))
+	// cond: uint32(c)>uint32(d)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(uint32(c) > uint32(d)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTUconst [c] (MOVWconst [d]))
+	// cond: uint32(c)<=uint32(d)
+	// result: (MOVWconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(uint32(c) <= uint32(d)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTUconst [c] (MOVBUreg _))
+	// cond: 0xff < uint32(c)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVBUreg {
+			break
+		}
+		if !(0xff < uint32(c)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTUconst [c] (MOVHUreg _))
+	// cond: 0xffff < uint32(c)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVHUreg {
+			break
+		}
+		if !(0xffff < uint32(c)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTUconst [c] (ANDconst [m] _))
+	// cond: uint32(m) < uint32(c)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSANDconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(uint32(m) < uint32(c)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTUconst [c] (SRLconst _ [d]))
+	// cond: uint32(d) <= 31 && 1<<(32-uint32(d)) <= uint32(c)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSSRLconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(uint32(d) <= 31 && 1<<(32-uint32(d)) <= uint32(c)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSGTUzero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SGTUzero (MOVWconst [d]))
+	// cond: uint32(d) != 0
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(uint32(d) != 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTUzero (MOVWconst [d]))
+	// cond: uint32(d) == 0
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(uint32(d) == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSGTconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SGTconst [c] (MOVWconst [d]))
+	// cond: int32(c) > int32(d)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(int32(c) > int32(d)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (MOVWconst [d]))
+	// cond: int32(c) <= int32(d)
+	// result: (MOVWconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(int32(c) <= int32(d)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (MOVBreg _))
+	// cond: 0x7f < int32(c)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVBreg {
+			break
+		}
+		if !(0x7f < int32(c)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (MOVBreg _))
+	// cond: int32(c) <= -0x80
+	// result: (MOVWconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVBreg {
+			break
+		}
+		if !(int32(c) <= -0x80) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (MOVBUreg _))
+	// cond: 0xff < int32(c)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVBUreg {
+			break
+		}
+		if !(0xff < int32(c)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (MOVBUreg _))
+	// cond: int32(c) < 0
+	// result: (MOVWconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVBUreg {
+			break
+		}
+		if !(int32(c) < 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (MOVHreg _))
+	// cond: 0x7fff < int32(c)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVHreg {
+			break
+		}
+		if !(0x7fff < int32(c)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (MOVHreg _))
+	// cond: int32(c) <= -0x8000
+	// result: (MOVWconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVHreg {
+			break
+		}
+		if !(int32(c) <= -0x8000) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (MOVHUreg _))
+	// cond: 0xffff < int32(c)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVHUreg {
+			break
+		}
+		if !(0xffff < int32(c)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (MOVHUreg _))
+	// cond: int32(c) < 0
+	// result: (MOVWconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVHUreg {
+			break
+		}
+		if !(int32(c) < 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (ANDconst [m] _))
+	// cond: 0 <= int32(m) && int32(m) < int32(c)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSANDconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(0 <= int32(m) && int32(m) < int32(c)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (SRLconst _ [d]))
+	// cond: 0 <= int32(c) && uint32(d) <= 31 && 1<<(32-uint32(d)) <= int32(c)
+	// result: (MOVWconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSSRLconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(0 <= int32(c) && uint32(d) <= 31 && 1<<(32-uint32(d)) <= int32(c)) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSGTzero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SGTzero (MOVWconst [d]))
+	// cond: int32(d) > 0
+	// result: (MOVWconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(int32(d) > 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTzero (MOVWconst [d]))
+	// cond: int32(d) <= 0
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(int32(d) <= 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSLL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SLL _ (MOVWconst [c]))
+	// cond: uint32(c)>=32
+	// result: (MOVWconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 32) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SLL x (MOVWconst [c]))
+	// cond:
+	// result: (SLLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPSSLLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSLLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SLLconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(int32(uint32(d)<<uint32(c)))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(uint32(d) << uint32(c)))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSRA(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRA x (MOVWconst [c]))
+	// cond: uint32(c)>=32
+	// result: (SRAconst x [31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 32) {
+			break
+		}
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = 31
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRA x (MOVWconst [c]))
+	// cond:
+	// result: (SRAconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSRAconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRAconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(int32(d)>>uint32(c))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(d) >> uint32(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSRL(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRL _ (MOVWconst [c]))
+	// cond: uint32(c)>=32
+	// result: (MOVWconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 32) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SRL x (MOVWconst [c]))
+	// cond:
+	// result: (SRLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPSSRLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSRLconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRLconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(uint32(d)>>uint32(c))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(uint32(d) >> uint32(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSUB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUB x (MOVWconst [c]))
+	// cond:
+	// result: (SUBconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPSSUBconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUB x x)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SUB (MOVWconst [0]) x)
+	// cond:
+	// result: (NEG x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_0.AuxInt != 0 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpMIPSNEG)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSSUBconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBconst [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [int64(int32(d-c))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(d - c))
+		return true
+	}
+	// match: (SUBconst [c] (SUBconst [d] x))
+	// cond:
+	// result: (ADDconst [int64(int32(-c-d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSSUBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpMIPSADDconst)
+		v.AuxInt = int64(int32(-c - d))
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBconst [c] (ADDconst [d] x))
+	// cond:
+	// result: (ADDconst [int64(int32(-c+d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpMIPSADDconst)
+		v.AuxInt = int64(int32(-c + d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSXOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XOR (MOVWconst [c]) x)
+	// cond:
+	// result: (XORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR x (MOVWconst [c]))
+	// cond:
+	// result: (XORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR x x)
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMIPSXORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORconst [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORconst [-1] x)
+	// cond:
+	// result: (NORconst [0] x)
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpMIPSNORconst)
+		v.AuxInt = 0
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORconst [c] (MOVWconst [d]))
+	// cond:
+	// result: (MOVWconst [c^d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	// match: (XORconst [c] (XORconst [d] x))
+	// cond:
+	// result: (XORconst [c^d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSXORconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = c ^ d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMod16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16 x y)
+	// cond:
+	// result: (Select0 (DIV (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPSDIV, MakeTuple(config.fe.TypeInt32(), config.fe.TypeInt32()))
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMod16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16u x y)
+	// cond:
+	// result: (Select0 (DIVU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPSDIVU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMod32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32 x y)
+	// cond:
+	// result: (Select0 (DIV x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPSDIV, MakeTuple(config.fe.TypeInt32(), config.fe.TypeInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMod32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32u x y)
+	// cond:
+	// result: (Select0 (DIVU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPSDIVU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMod8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8 x y)
+	// cond:
+	// result: (Select0 (DIV (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPSDIV, MakeTuple(config.fe.TypeInt32(), config.fe.TypeInt32()))
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMod8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8u x y)
+	// cond:
+	// result: (Select0 (DIVU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPSDIVU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMove(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Move [s] _ _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore dst (MOVBUload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVBstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVBUload, config.fe.TypeUInt8())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore dst (MOVHUload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVHstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVHUload, config.fe.TypeUInt16())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVBstore [1] dst (MOVBUload [1] src mem) 		(MOVBstore dst (MOVBUload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = 1
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVBUload, config.fe.TypeUInt8())
+		v0.AuxInt = 1
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVBUload, config.fe.TypeUInt8())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore dst (MOVWload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWload, config.fe.TypeUInt32())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [2] dst (MOVHUload [2] src mem) 		(MOVHstore dst (MOVHUload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVHstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVHUload, config.fe.TypeUInt16())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVHstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVHUload, config.fe.TypeUInt16())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVBstore [3] dst (MOVBUload [3] src mem) 		(MOVBstore [2] dst (MOVBUload [2] src mem) 			(MOVBstore [1] dst (MOVBUload [1] src mem) 				(MOVBstore dst (MOVBUload src mem) mem))))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = 3
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVBUload, config.fe.TypeUInt8())
+		v0.AuxInt = 3
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVBUload, config.fe.TypeUInt8())
+		v2.AuxInt = 2
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v3.AuxInt = 1
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVBUload, config.fe.TypeUInt8())
+		v4.AuxInt = 1
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v5.AddArg(dst)
+		v6 := b.NewValue0(v.Line, OpMIPSMOVBUload, config.fe.TypeUInt8())
+		v6.AddArg(src)
+		v6.AddArg(mem)
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] dst (MOVBUload [2] src mem) 		(MOVBstore [1] dst (MOVBUload [1] src mem) 			(MOVBstore dst (MOVBUload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVBUload, config.fe.TypeUInt8())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v1.AuxInt = 1
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVBUload, config.fe.TypeUInt8())
+		v2.AuxInt = 1
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVBUload, config.fe.TypeUInt8())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [4] dst (MOVWload [4] src mem) 		(MOVWstore dst (MOVWload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWload, config.fe.TypeUInt32())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [6] dst (MOVHload [6] src mem) 		(MOVHstore [4] dst (MOVHload [4] src mem) 			(MOVHstore [2] dst (MOVHload [2] src mem) 				(MOVHstore dst (MOVHload src mem) mem))))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVHstore)
+		v.AuxInt = 6
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVHload, config.fe.TypeInt16())
+		v0.AuxInt = 6
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVHstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVHload, config.fe.TypeInt16())
+		v2.AuxInt = 4
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVHstore, TypeMem)
+		v3.AuxInt = 2
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVHload, config.fe.TypeInt16())
+		v4.AuxInt = 2
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPSMOVHstore, TypeMem)
+		v5.AddArg(dst)
+		v6 := b.NewValue0(v.Line, OpMIPSMOVHload, config.fe.TypeInt16())
+		v6.AddArg(src)
+		v6.AddArg(mem)
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [4] dst (MOVHload [4] src mem) 		(MOVHstore [2] dst (MOVHload [2] src mem) 			(MOVHstore dst (MOVHload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVHstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVHload, config.fe.TypeInt16())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVHstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVHload, config.fe.TypeInt16())
+		v2.AuxInt = 2
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVHstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVHload, config.fe.TypeInt16())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [8] dst (MOVWload [8] src mem) 		(MOVWstore [4] dst (MOVWload [4] src mem) 			(MOVWstore dst (MOVWload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AuxInt = 8
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWload, config.fe.TypeUInt32())
+		v0.AuxInt = 8
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWload, config.fe.TypeUInt32())
+		v2.AuxInt = 4
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVWload, config.fe.TypeUInt32())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [12] dst (MOVWload [12] src mem) 		(MOVWstore [8] dst (MOVWload [8] src mem) 			(MOVWstore [4] dst (MOVWload [4] src mem) 				(MOVWstore dst (MOVWload src mem) mem))))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AuxInt = 12
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWload, config.fe.TypeUInt32())
+		v0.AuxInt = 12
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v1.AuxInt = 8
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWload, config.fe.TypeUInt32())
+		v2.AuxInt = 8
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v3.AuxInt = 4
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVWload, config.fe.TypeUInt32())
+		v4.AuxInt = 4
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v5.AddArg(dst)
+		v6 := b.NewValue0(v.Line, OpMIPSMOVWload, config.fe.TypeUInt32())
+		v6.AddArg(src)
+		v6.AddArg(mem)
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: (SizeAndAlign(s).Size() > 16 || SizeAndAlign(s).Align()%4 != 0)
+	// result: (LoweredMove [SizeAndAlign(s).Align()] 		dst 		src 		(ADDconst <src.Type> src [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)]) 		mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 16 || SizeAndAlign(s).Align()%4 != 0) {
+			break
+		}
+		v.reset(OpMIPSLoweredMove)
+		v.AuxInt = SizeAndAlign(s).Align()
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, OpMIPSADDconst, src.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - moveSize(SizeAndAlign(s).Align(), config)
+		v0.AddArg(src)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpMul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul16 x y)
+	// cond:
+	// result: (MUL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSMUL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32 x y)
+	// cond:
+	// result: (MUL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSMUL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMul32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32F x y)
+	// cond:
+	// result: (MULF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSMULF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMul32uhilo(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32uhilo x y)
+	// cond:
+	// result: (MULTU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSMULTU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMul64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64F x y)
+	// cond:
+	// result: (MULD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSMULD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpMul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul8 x y)
+	// cond:
+	// result: (MUL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSMUL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeg16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg16 x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSNEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeg32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32 x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSNEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeg32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32F x)
+	// cond:
+	// result: (NEGF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSNEGF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeg64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64F x)
+	// cond:
+	// result: (NEGD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSNEGD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeg8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg8 x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSNEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq16 x y)
+	// cond:
+	// result: (SGTU (XOR (ZeroExt16to32 x) (ZeroExt16to32 y)) (MOVWconst [0]))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v0 := b.NewValue0(v.Line, OpMIPSXOR, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32 x y)
+	// cond:
+	// result: (SGTU (XOR x y) (MOVWconst [0]))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v0 := b.NewValue0(v.Line, OpMIPSXOR, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32F x y)
+	// cond:
+	// result: (FPFlagFalse (CMPEQF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagFalse)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPEQF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64F x y)
+	// cond:
+	// result: (FPFlagFalse (CMPEQD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSFPFlagFalse)
+		v0 := b.NewValue0(v.Line, OpMIPSCMPEQD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq8 x y)
+	// cond:
+	// result: (SGTU (XOR (ZeroExt8to32 x) (ZeroExt8to32 y)) (MOVWconst [0]))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v0 := b.NewValue0(v.Line, OpMIPSXOR, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqB x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNeqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqPtr x y)
+	// cond:
+	// result: (SGTU (XOR x y) (MOVWconst [0]))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSGTU)
+		v0 := b.NewValue0(v.Line, OpMIPSXOR, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNilCheck(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NilCheck ptr mem)
+	// cond:
+	// result: (LoweredNilCheck ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpMIPSLoweredNilCheck)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpNot(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Not x)
+	// cond:
+	// result: (XORconst [1] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSXORconst)
+		v.AuxInt = 1
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpOffPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OffPtr [off] ptr:(SP))
+	// cond:
+	// result: (MOVWaddr [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		if ptr.Op != OpSP {
+			break
+		}
+		v.reset(OpMIPSMOVWaddr)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+	// match: (OffPtr [off] ptr)
+	// cond:
+	// result: (ADDconst [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		v.reset(OpMIPSADDconst)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+}
+func rewriteValueMIPS_OpOr16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or16 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpOr32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or32 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or8 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpOrB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OrB x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh16Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux16 <t> x y)
+	// cond:
+	// result: (CMOVZ (SRL <t> (ZeroExt16to32 x) (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v4.AuxInt = 32
+		v5 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh16Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux32 <t> x y)
+	// cond:
+	// result: (CMOVZ (SRL <t> (ZeroExt16to32 x) y) (MOVWconst [0]) (SGTUconst [32] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh16Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux64 x (Const64 [c]))
+	// cond: uint32(c) < 16
+	// result: (SRLconst (SLLconst <config.fe.TypeUInt32()> x [16]) [c+16])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 16) {
+			break
+		}
+		v.reset(OpMIPSSRLconst)
+		v.AuxInt = c + 16
+		v0 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 16
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16Ux64 _ (Const64 [c]))
+	// cond: uint32(c) >= 16
+	// result: (MOVWconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 16) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpRsh16Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux8 <t> x y)
+	// cond:
+	// result: (CMOVZ (SRL <t> (ZeroExt16to32 x) (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v4.AuxInt = 32
+		v5 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x16 x y)
+	// cond:
+	// result: (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSCMOVZ, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v3.AuxInt = -1
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v4.AuxInt = 32
+		v5 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x32 x y)
+	// cond:
+	// result: (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> y (MOVWconst [-1]) (SGTUconst [32] y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSCMOVZ, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = -1
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x64 x (Const64 [c]))
+	// cond: uint32(c) < 16
+	// result: (SRAconst (SLLconst <config.fe.TypeUInt32()> x [16]) [c+16])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 16) {
+			break
+		}
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = c + 16
+		v0 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 16
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16x64 x (Const64 [c]))
+	// cond: uint32(c) >= 16
+	// result: (SRAconst (SLLconst <config.fe.TypeUInt32()> x [16]) [31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 16) {
+			break
+		}
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = 31
+		v0 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 16
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpRsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x8 x y)
+	// cond:
+	// result: (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSCMOVZ, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v3.AuxInt = -1
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v4.AuxInt = 32
+		v5 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh32Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux16 <t> x y)
+	// cond:
+	// result: (CMOVZ (SRL <t> x (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSRL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v4 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh32Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux32 <t> x y)
+	// cond:
+	// result: (CMOVZ (SRL <t> x y) (MOVWconst [0]) (SGTUconst [32] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSRL, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh32Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux64 x (Const64 [c]))
+	// cond: uint32(c) < 32
+	// result: (SRLconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 32) {
+			break
+		}
+		v.reset(OpMIPSSRLconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32Ux64 _ (Const64 [c]))
+	// cond: uint32(c) >= 32
+	// result: (MOVWconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 32) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpRsh32Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux8 <t> x y)
+	// cond:
+	// result: (CMOVZ (SRL <t> x (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSRL, t)
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v4 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x16 x y)
+	// cond:
+	// result: (SRA x ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRA)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpMIPSCMOVZ, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = -1
+		v0.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v4 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v0.AddArg(v3)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x32 x y)
+	// cond:
+	// result: (SRA x ( CMOVZ <config.fe.TypeUInt32()> y (MOVWconst [-1]) (SGTUconst [32] y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRA)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpMIPSCMOVZ, config.fe.TypeUInt32())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = -1
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v2.AuxInt = 32
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x64 x (Const64 [c]))
+	// cond: uint32(c) < 32
+	// result: (SRAconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 32) {
+			break
+		}
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32x64 x (Const64 [c]))
+	// cond: uint32(c) >= 32
+	// result: (SRAconst x [31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 32) {
+			break
+		}
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = 31
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpRsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x8 x y)
+	// cond:
+	// result: (SRA x ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRA)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpMIPSCMOVZ, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = -1
+		v0.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v4 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v0.AddArg(v3)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh8Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux16 <t> x y)
+	// cond:
+	// result: (CMOVZ (SRL <t> (ZeroExt8to32 x) (ZeroExt16to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt16to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v4.AuxInt = 32
+		v5 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh8Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux32 <t> x y)
+	// cond:
+	// result: (CMOVZ (SRL <t> (ZeroExt8to32 x) y) (MOVWconst [0]) (SGTUconst [32] y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh8Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux64 x (Const64 [c]))
+	// cond: uint32(c) < 8
+	// result: (SRLconst (SLLconst <config.fe.TypeUInt32()> x [24]) [c+24])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 8) {
+			break
+		}
+		v.reset(OpMIPSSRLconst)
+		v.AuxInt = c + 24
+		v0 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 24
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8Ux64 _ (Const64 [c]))
+	// cond: uint32(c) >= 8
+	// result: (MOVWconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 8) {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpRsh8Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux8 <t> x y)
+	// cond:
+	// result: (CMOVZ (SRL <t> (ZeroExt8to32 x) (ZeroExt8to32 y) ) (MOVWconst [0]) (SGTUconst [32] (ZeroExt8to32 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSSRL, t)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v4.AuxInt = 32
+		v5 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x16 x y)
+	// cond:
+	// result: (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt16to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt16to32 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSCMOVZ, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v3.AuxInt = -1
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v4.AuxInt = 32
+		v5 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x32 x y)
+	// cond:
+	// result: (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> y (MOVWconst [-1]) (SGTUconst [32] y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSCMOVZ, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = -1
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v3.AuxInt = 32
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpRsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x64 x (Const64 [c]))
+	// cond: uint32(c) < 8
+	// result: (SRAconst (SLLconst <config.fe.TypeUInt32()> x [24]) [c+24])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 8) {
+			break
+		}
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = c + 24
+		v0 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 24
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8x64 x (Const64 [c]))
+	// cond: uint32(c) >= 8
+	// result: (SRAconst (SLLconst <config.fe.TypeUInt32()> x [24]) [31])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) >= 8) {
+			break
+		}
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = 31
+		v0 := b.NewValue0(v.Line, OpMIPSSLLconst, config.fe.TypeUInt32())
+		v0.AuxInt = 24
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpRsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x8 x y)
+	// cond:
+	// result: (SRA (SignExt16to32 x) ( CMOVZ <config.fe.TypeUInt32()> (ZeroExt8to32 y) (MOVWconst [-1]) (SGTUconst [32] (ZeroExt8to32 y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSRA)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSCMOVZ, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v3.AuxInt = -1
+		v1.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpMIPSSGTUconst, config.fe.TypeBool())
+		v4.AuxInt = 32
+		v5 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSelect0(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Select0 (Add32carry <t> x y))
+	// cond:
+	// result: (ADD <t.FieldType(0)> x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAdd32carry {
+			break
+		}
+		t := v_0.Type
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpMIPSADD)
+		v.Type = t.FieldType(0)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Select0 (Sub32carry <t> x y))
+	// cond:
+	// result: (SUB <t.FieldType(0)> x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSub32carry {
+			break
+		}
+		t := v_0.Type
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpMIPSSUB)
+		v.Type = t.FieldType(0)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Select0 (MULTU x (MOVWconst [c])))
+	// cond: x.Op != OpMIPSMOVWconst
+	// result: (Select0 (MULTU (MOVWconst [c]) x ))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0_1.AuxInt
+		if !(x.Op != OpMIPSMOVWconst) {
+			break
+		}
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPSMULTU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = c
+		v0.AddArg(v1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Select0 (MULTU (MOVWconst [0]) _ ))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_0_0.AuxInt != 0 {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Select0 (MULTU (MOVWconst [1]) _ ))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_0_0.AuxInt != 1 {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Select0 (MULTU (MOVWconst [-1]) x ))
+	// cond:
+	// result: (CMOVZ (ADDconst <x.Type> [-1] x) (MOVWconst [0]) x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_0_0.AuxInt != -1 {
+			break
+		}
+		x := v_0.Args[1]
+		v.reset(OpMIPSCMOVZ)
+		v0 := b.NewValue0(v.Line, OpMIPSADDconst, x.Type)
+		v0.AuxInt = -1
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select0 (MULTU (MOVWconst [c]) x ))
+	// cond: isPowerOfTwo(int64(uint32(c)))
+	// result: (SRLconst [32-log2(int64(uint32(c)))] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		x := v_0.Args[1]
+		if !(isPowerOfTwo(int64(uint32(c)))) {
+			break
+		}
+		v.reset(OpMIPSSRLconst)
+		v.AuxInt = 32 - log2(int64(uint32(c)))
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select0 (MULTU  (MOVWconst [c]) (MOVWconst [d])))
+	// cond:
+	// result: (MOVWconst [(c*d)>>32])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = (c * d) >> 32
+		return true
+	}
+	// match: (Select0 (DIV  (MOVWconst [c]) (MOVWconst [d])))
+	// cond:
+	// result: (MOVWconst [int64(int32(c)%int32(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSDIV {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(c) % int32(d))
+		return true
+	}
+	// match: (Select0 (DIVU (MOVWconst [c]) (MOVWconst [d])))
+	// cond:
+	// result: (MOVWconst [int64(int32(uint32(c)%uint32(d)))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSDIVU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(uint32(c) % uint32(d)))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpSelect1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Select1 (Add32carry <t> x y))
+	// cond:
+	// result: (SGTU <config.fe.TypeBool()> x (ADD <t.FieldType(0)> x y))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpAdd32carry {
+			break
+		}
+		t := v_0.Type
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpMIPSSGTU)
+		v.Type = config.fe.TypeBool()
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpMIPSADD, t.FieldType(0))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Select1 (Sub32carry <t> x y))
+	// cond:
+	// result: (SGTU <config.fe.TypeBool()> (SUB <t.FieldType(0)> x y) x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSub32carry {
+			break
+		}
+		t := v_0.Type
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpMIPSSGTU)
+		v.Type = config.fe.TypeBool()
+		v0 := b.NewValue0(v.Line, OpMIPSSUB, t.FieldType(0))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (MULTU x (MOVWconst [c])))
+	// cond: x.Op != OpMIPSMOVWconst
+	// result: (Select1 (MULTU (MOVWconst [c]) x ))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0_1.AuxInt
+		if !(x.Op != OpMIPSMOVWconst) {
+			break
+		}
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPSMULTU, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = c
+		v0.AddArg(v1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Select1 (MULTU (MOVWconst [0]) _ ))
+	// cond:
+	// result: (MOVWconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_0_0.AuxInt != 0 {
+			break
+		}
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Select1 (MULTU (MOVWconst [1]) x ))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_0_0.AuxInt != 1 {
+			break
+		}
+		x := v_0.Args[1]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (MULTU (MOVWconst [-1]) x ))
+	// cond:
+	// result: (NEG <x.Type> x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		if v_0_0.AuxInt != -1 {
+			break
+		}
+		x := v_0.Args[1]
+		v.reset(OpMIPSNEG)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (MULTU (MOVWconst [c]) x ))
+	// cond: isPowerOfTwo(int64(uint32(c)))
+	// result: (SLLconst [log2(int64(uint32(c)))] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		x := v_0.Args[1]
+		if !(isPowerOfTwo(int64(uint32(c)))) {
+			break
+		}
+		v.reset(OpMIPSSLLconst)
+		v.AuxInt = log2(int64(uint32(c)))
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (MULTU  (MOVWconst [c]) (MOVWconst [d])))
+	// cond:
+	// result: (MOVWconst [int64(int32(uint32(c)*uint32(d)))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSMULTU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(uint32(c) * uint32(d)))
+		return true
+	}
+	// match: (Select1 (DIV  (MOVWconst [c]) (MOVWconst [d])))
+	// cond:
+	// result: (MOVWconst [int64(int32(c)/int32(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSDIV {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(c) / int32(d))
+		return true
+	}
+	// match: (Select1 (DIVU (MOVWconst [c]) (MOVWconst [d])))
+	// cond:
+	// result: (MOVWconst [int64(int32(uint32(c)/uint32(d)))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPSDIVU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPSMOVWconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPSMOVWconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPSMOVWconst)
+		v.AuxInt = int64(int32(uint32(c) / uint32(d)))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpSignExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to32 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSMOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSignExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to16 x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSMOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSignExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to32 x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSMOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSignmask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Signmask x)
+	// cond:
+	// result: (SRAconst x [31])
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSSRAconst)
+		v.AuxInt = 31
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSlicemask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Slicemask x)
+	// cond:
+	// result: (NEG (SGT x (MOVWconst [0])))
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSNEG)
+		v0 := b.NewValue0(v.Line, OpMIPSSGT, config.fe.TypeBool())
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSqrt(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sqrt x)
+	// cond:
+	// result: (SQRTD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSSQRTD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpStaticCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (StaticCall [argwid] {target} mem)
+	// cond:
+	// result: (CALLstatic [argwid] {target} mem)
+	for {
+		argwid := v.AuxInt
+		target := v.Aux
+		mem := v.Args[0]
+		v.reset(OpMIPSCALLstatic)
+		v.AuxInt = argwid
+		v.Aux = target
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS_OpStore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Store [1] ptr val mem)
+	// cond:
+	// result: (MOVBstore ptr val mem)
+	for {
+		if v.AuxInt != 1 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVBstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [2] ptr val mem)
+	// cond:
+	// result: (MOVHstore ptr val mem)
+	for {
+		if v.AuxInt != 2 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpMIPSMOVHstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: !is32BitFloat(val.Type)
+	// result: (MOVWstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(!is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [8] ptr val mem)
+	// cond: !is64BitFloat(val.Type)
+	// result: (MOVWstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(!is64BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: is32BitFloat(val.Type)
+	// result: (MOVFstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpMIPSMOVFstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [8] ptr val mem)
+	// cond: is64BitFloat(val.Type)
+	// result: (MOVDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is64BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpMIPSMOVDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpSub16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub16 x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSub32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32 x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSub32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32F x y)
+	// cond:
+	// result: (SUBF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSUBF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSub32withcarry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32withcarry <t> x y c)
+	// cond:
+	// result: (SUB (SUB <t> x y) c)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		c := v.Args[2]
+		v.reset(OpMIPSSUB)
+		v0 := b.NewValue0(v.Line, OpMIPSSUB, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v.AddArg(c)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSub64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64F x y)
+	// cond:
+	// result: (SUBD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSUBD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSub8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub8 x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpSubPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SubPtr x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpTrunc16to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc16to8 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpTrunc32to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to16 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpTrunc32to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to8 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpXor16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor16 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpXor32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor32 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpXor8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor8 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPSXOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS_OpZero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zero [s] _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore ptr (MOVWconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpMIPSMOVBstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore ptr (MOVWconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVHstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVBstore [1] ptr (MOVWconst [0]) 		(MOVBstore [0] ptr (MOVWconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = 1
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore ptr (MOVWconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [2] ptr (MOVWconst [0]) 		(MOVHstore [0] ptr (MOVWconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVHstore)
+		v.AuxInt = 2
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVHstore, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVBstore [3] ptr (MOVWconst [0]) 		(MOVBstore [2] ptr (MOVWconst [0]) 			(MOVBstore [1] ptr (MOVWconst [0]) 				(MOVBstore [0] ptr (MOVWconst [0]) mem))))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = 3
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v3.AuxInt = 1
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v5.AuxInt = 0
+		v5.AddArg(ptr)
+		v6 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v6.AuxInt = 0
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] ptr (MOVWconst [0]) 		(MOVBstore [1] ptr (MOVWconst [0]) 			(MOVBstore [0] ptr (MOVWconst [0]) mem)))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpMIPSMOVBstore)
+		v.AuxInt = 2
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v1.AuxInt = 1
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVBstore, TypeMem)
+		v3.AuxInt = 0
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [4] ptr (MOVWconst [0]) 		(MOVHstore [2] ptr (MOVWconst [0]) 			(MOVHstore [0] ptr (MOVWconst [0]) mem)))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVHstore)
+		v.AuxInt = 4
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVHstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVHstore, TypeMem)
+		v3.AuxInt = 0
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [4] ptr (MOVWconst [0]) 			(MOVWstore [0] ptr (MOVWconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AuxInt = 4
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [8] ptr (MOVWconst [0]) 		(MOVWstore [4] ptr (MOVWconst [0]) 			(MOVWstore [0] ptr (MOVWconst [0]) mem)))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AuxInt = 8
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v3.AuxInt = 0
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [12] ptr (MOVWconst [0]) 		(MOVWstore [8] ptr (MOVWconst [0]) 			(MOVWstore [4] ptr (MOVWconst [0]) 				(MOVWstore [0] ptr (MOVWconst [0]) mem))))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPSMOVWstore)
+		v.AuxInt = 12
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v1.AuxInt = 8
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v3.AuxInt = 4
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPSMOVWstore, TypeMem)
+		v5.AuxInt = 0
+		v5.AddArg(ptr)
+		v6 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v6.AuxInt = 0
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: (SizeAndAlign(s).Size() > 16  || SizeAndAlign(s).Align()%4 != 0)
+	// result: (LoweredZero [SizeAndAlign(s).Align()] 		ptr 		(ADDconst <ptr.Type> ptr [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)]) 		mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() > 16 || SizeAndAlign(s).Align()%4 != 0) {
+			break
+		}
+		v.reset(OpMIPSLoweredZero)
+		v.AuxInt = SizeAndAlign(s).Align()
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPSADDconst, ptr.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - moveSize(SizeAndAlign(s).Align(), config)
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS_OpZeroExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to32 x)
+	// cond:
+	// result: (MOVHUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSMOVHUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpZeroExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to16 x)
+	// cond:
+	// result: (MOVBUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSMOVBUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpZeroExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to32 x)
+	// cond:
+	// result: (MOVBUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSMOVBUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS_OpZeromask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zeromask x)
+	// cond:
+	// result: (NEG (SGTU x (MOVWconst [0])))
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPSNEG)
+		v0 := b.NewValue0(v.Line, OpMIPSSGTU, config.fe.TypeBool())
+		v0.AddArg(x)
+		v1 := b.NewValue0(v.Line, OpMIPSMOVWconst, config.fe.TypeUInt32())
+		v1.AuxInt = 0
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteBlockMIPS(b *Block, config *Config) bool {
+	switch b.Kind {
+	case BlockMIPSEQ:
+		// match: (EQ (FPFlagTrue cmp) yes no)
+		// cond:
+		// result: (FPF cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSFPFlagTrue {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSFPF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FPFlagFalse cmp) yes no)
+		// cond:
+		// result: (FPT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSFPFlagFalse {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSFPT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (XORconst [1] cmp:(SGT _ _)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSNE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (XORconst [1] cmp:(SGTU _ _)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGTU {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSNE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (XORconst [1] cmp:(SGTconst _)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGTconst {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSNE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (XORconst [1] cmp:(SGTUconst _)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGTUconst {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSNE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (XORconst [1] cmp:(SGTzero _)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGTzero {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSNE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (XORconst [1] cmp:(SGTUzero _)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGTUzero {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSNE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (SGTUconst [1] x) yes no)
+		// cond:
+		// result: (NE x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSSGTUconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSNE
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (SGTUzero x) yes no)
+		// cond:
+		// result: (EQ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSSGTUzero {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSEQ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (SGTconst [0] x) yes no)
+		// cond:
+		// result: (GEZ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSSGTconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSGEZ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (SGTzero x) yes no)
+		// cond:
+		// result: (LEZ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSSGTzero {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSLEZ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ  (MOVWconst [0]) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ  (MOVWconst [c]) yes no)
+		// cond: c != 0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c != 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockMIPSGEZ:
+		// match: (GEZ (MOVWconst [c]) yes no)
+		// cond: int32(c) >= 0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) >= 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GEZ (MOVWconst [c]) yes no)
+		// cond: int32(c) <  0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) < 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockMIPSGTZ:
+		// match: (GTZ (MOVWconst [c]) yes no)
+		// cond: int32(c) >  0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) > 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GTZ (MOVWconst [c]) yes no)
+		// cond: int32(c) <= 0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) <= 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockIf:
+		// match: (If cond yes no)
+		// cond:
+		// result: (NE cond yes no)
+		for {
+			v := b.Control
+			_ = v
+			cond := b.Control
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSNE
+			b.SetControl(cond)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockMIPSLEZ:
+		// match: (LEZ (MOVWconst [c]) yes no)
+		// cond: int32(c) <= 0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) <= 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LEZ (MOVWconst [c]) yes no)
+		// cond: int32(c) >  0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) > 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockMIPSLTZ:
+		// match: (LTZ (MOVWconst [c]) yes no)
+		// cond: int32(c) <  0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) < 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LTZ (MOVWconst [c]) yes no)
+		// cond: int32(c) >= 0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(int32(c) >= 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockMIPSNE:
+		// match: (NE (FPFlagTrue cmp) yes no)
+		// cond:
+		// result: (FPT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSFPFlagTrue {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSFPT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FPFlagFalse cmp) yes no)
+		// cond:
+		// result: (FPF cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSFPFlagFalse {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSFPF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (XORconst [1] cmp:(SGT _ _)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSEQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (XORconst [1] cmp:(SGTU _ _)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGTU {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSEQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (XORconst [1] cmp:(SGTconst _)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGTconst {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSEQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (XORconst [1] cmp:(SGTUconst _)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGTUconst {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSEQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (XORconst [1] cmp:(SGTzero _)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGTzero {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSEQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (XORconst [1] cmp:(SGTUzero _)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSXORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPSSGTUzero {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSEQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (SGTUconst [1] x) yes no)
+		// cond:
+		// result: (EQ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSSGTUconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSEQ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (SGTUzero x) yes no)
+		// cond:
+		// result: (NE x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSSGTUzero {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSNE
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (SGTconst [0] x) yes no)
+		// cond:
+		// result: (LTZ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSSGTconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSLTZ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (SGTzero x) yes no)
+		// cond:
+		// result: (GTZ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSSGTzero {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPSGTZ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE  (MOVWconst [0]) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (NE  (MOVWconst [c]) yes no)
+		// cond: c != 0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPSMOVWconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c != 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS64.go b/src/cmd/compile/internal/ssa/rewriteMIPS64.go
new file mode 100644
index 0000000..76c6412
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/rewriteMIPS64.go
@@ -0,0 +1,10432 @@
+// autogenerated from gen/MIPS64.rules: do not edit!
+// generated with: cd gen; go run *.go
+
+package ssa
+
+import "math"
+
+var _ = math.MinInt8 // in case not otherwise used
+func rewriteValueMIPS64(v *Value, config *Config) bool {
+	switch v.Op {
+	case OpAdd16:
+		return rewriteValueMIPS64_OpAdd16(v, config)
+	case OpAdd32:
+		return rewriteValueMIPS64_OpAdd32(v, config)
+	case OpAdd32F:
+		return rewriteValueMIPS64_OpAdd32F(v, config)
+	case OpAdd64:
+		return rewriteValueMIPS64_OpAdd64(v, config)
+	case OpAdd64F:
+		return rewriteValueMIPS64_OpAdd64F(v, config)
+	case OpAdd8:
+		return rewriteValueMIPS64_OpAdd8(v, config)
+	case OpAddPtr:
+		return rewriteValueMIPS64_OpAddPtr(v, config)
+	case OpAddr:
+		return rewriteValueMIPS64_OpAddr(v, config)
+	case OpAnd16:
+		return rewriteValueMIPS64_OpAnd16(v, config)
+	case OpAnd32:
+		return rewriteValueMIPS64_OpAnd32(v, config)
+	case OpAnd64:
+		return rewriteValueMIPS64_OpAnd64(v, config)
+	case OpAnd8:
+		return rewriteValueMIPS64_OpAnd8(v, config)
+	case OpAndB:
+		return rewriteValueMIPS64_OpAndB(v, config)
+	case OpAvg64u:
+		return rewriteValueMIPS64_OpAvg64u(v, config)
+	case OpClosureCall:
+		return rewriteValueMIPS64_OpClosureCall(v, config)
+	case OpCom16:
+		return rewriteValueMIPS64_OpCom16(v, config)
+	case OpCom32:
+		return rewriteValueMIPS64_OpCom32(v, config)
+	case OpCom64:
+		return rewriteValueMIPS64_OpCom64(v, config)
+	case OpCom8:
+		return rewriteValueMIPS64_OpCom8(v, config)
+	case OpConst16:
+		return rewriteValueMIPS64_OpConst16(v, config)
+	case OpConst32:
+		return rewriteValueMIPS64_OpConst32(v, config)
+	case OpConst32F:
+		return rewriteValueMIPS64_OpConst32F(v, config)
+	case OpConst64:
+		return rewriteValueMIPS64_OpConst64(v, config)
+	case OpConst64F:
+		return rewriteValueMIPS64_OpConst64F(v, config)
+	case OpConst8:
+		return rewriteValueMIPS64_OpConst8(v, config)
+	case OpConstBool:
+		return rewriteValueMIPS64_OpConstBool(v, config)
+	case OpConstNil:
+		return rewriteValueMIPS64_OpConstNil(v, config)
+	case OpConvert:
+		return rewriteValueMIPS64_OpConvert(v, config)
+	case OpCvt32Fto32:
+		return rewriteValueMIPS64_OpCvt32Fto32(v, config)
+	case OpCvt32Fto64:
+		return rewriteValueMIPS64_OpCvt32Fto64(v, config)
+	case OpCvt32Fto64F:
+		return rewriteValueMIPS64_OpCvt32Fto64F(v, config)
+	case OpCvt32to32F:
+		return rewriteValueMIPS64_OpCvt32to32F(v, config)
+	case OpCvt32to64F:
+		return rewriteValueMIPS64_OpCvt32to64F(v, config)
+	case OpCvt64Fto32:
+		return rewriteValueMIPS64_OpCvt64Fto32(v, config)
+	case OpCvt64Fto32F:
+		return rewriteValueMIPS64_OpCvt64Fto32F(v, config)
+	case OpCvt64Fto64:
+		return rewriteValueMIPS64_OpCvt64Fto64(v, config)
+	case OpCvt64to32F:
+		return rewriteValueMIPS64_OpCvt64to32F(v, config)
+	case OpCvt64to64F:
+		return rewriteValueMIPS64_OpCvt64to64F(v, config)
+	case OpDeferCall:
+		return rewriteValueMIPS64_OpDeferCall(v, config)
+	case OpDiv16:
+		return rewriteValueMIPS64_OpDiv16(v, config)
+	case OpDiv16u:
+		return rewriteValueMIPS64_OpDiv16u(v, config)
+	case OpDiv32:
+		return rewriteValueMIPS64_OpDiv32(v, config)
+	case OpDiv32F:
+		return rewriteValueMIPS64_OpDiv32F(v, config)
+	case OpDiv32u:
+		return rewriteValueMIPS64_OpDiv32u(v, config)
+	case OpDiv64:
+		return rewriteValueMIPS64_OpDiv64(v, config)
+	case OpDiv64F:
+		return rewriteValueMIPS64_OpDiv64F(v, config)
+	case OpDiv64u:
+		return rewriteValueMIPS64_OpDiv64u(v, config)
+	case OpDiv8:
+		return rewriteValueMIPS64_OpDiv8(v, config)
+	case OpDiv8u:
+		return rewriteValueMIPS64_OpDiv8u(v, config)
+	case OpEq16:
+		return rewriteValueMIPS64_OpEq16(v, config)
+	case OpEq32:
+		return rewriteValueMIPS64_OpEq32(v, config)
+	case OpEq32F:
+		return rewriteValueMIPS64_OpEq32F(v, config)
+	case OpEq64:
+		return rewriteValueMIPS64_OpEq64(v, config)
+	case OpEq64F:
+		return rewriteValueMIPS64_OpEq64F(v, config)
+	case OpEq8:
+		return rewriteValueMIPS64_OpEq8(v, config)
+	case OpEqB:
+		return rewriteValueMIPS64_OpEqB(v, config)
+	case OpEqPtr:
+		return rewriteValueMIPS64_OpEqPtr(v, config)
+	case OpGeq16:
+		return rewriteValueMIPS64_OpGeq16(v, config)
+	case OpGeq16U:
+		return rewriteValueMIPS64_OpGeq16U(v, config)
+	case OpGeq32:
+		return rewriteValueMIPS64_OpGeq32(v, config)
+	case OpGeq32F:
+		return rewriteValueMIPS64_OpGeq32F(v, config)
+	case OpGeq32U:
+		return rewriteValueMIPS64_OpGeq32U(v, config)
+	case OpGeq64:
+		return rewriteValueMIPS64_OpGeq64(v, config)
+	case OpGeq64F:
+		return rewriteValueMIPS64_OpGeq64F(v, config)
+	case OpGeq64U:
+		return rewriteValueMIPS64_OpGeq64U(v, config)
+	case OpGeq8:
+		return rewriteValueMIPS64_OpGeq8(v, config)
+	case OpGeq8U:
+		return rewriteValueMIPS64_OpGeq8U(v, config)
+	case OpGetClosurePtr:
+		return rewriteValueMIPS64_OpGetClosurePtr(v, config)
+	case OpGoCall:
+		return rewriteValueMIPS64_OpGoCall(v, config)
+	case OpGreater16:
+		return rewriteValueMIPS64_OpGreater16(v, config)
+	case OpGreater16U:
+		return rewriteValueMIPS64_OpGreater16U(v, config)
+	case OpGreater32:
+		return rewriteValueMIPS64_OpGreater32(v, config)
+	case OpGreater32F:
+		return rewriteValueMIPS64_OpGreater32F(v, config)
+	case OpGreater32U:
+		return rewriteValueMIPS64_OpGreater32U(v, config)
+	case OpGreater64:
+		return rewriteValueMIPS64_OpGreater64(v, config)
+	case OpGreater64F:
+		return rewriteValueMIPS64_OpGreater64F(v, config)
+	case OpGreater64U:
+		return rewriteValueMIPS64_OpGreater64U(v, config)
+	case OpGreater8:
+		return rewriteValueMIPS64_OpGreater8(v, config)
+	case OpGreater8U:
+		return rewriteValueMIPS64_OpGreater8U(v, config)
+	case OpHmul16:
+		return rewriteValueMIPS64_OpHmul16(v, config)
+	case OpHmul16u:
+		return rewriteValueMIPS64_OpHmul16u(v, config)
+	case OpHmul32:
+		return rewriteValueMIPS64_OpHmul32(v, config)
+	case OpHmul32u:
+		return rewriteValueMIPS64_OpHmul32u(v, config)
+	case OpHmul64:
+		return rewriteValueMIPS64_OpHmul64(v, config)
+	case OpHmul64u:
+		return rewriteValueMIPS64_OpHmul64u(v, config)
+	case OpHmul8:
+		return rewriteValueMIPS64_OpHmul8(v, config)
+	case OpHmul8u:
+		return rewriteValueMIPS64_OpHmul8u(v, config)
+	case OpInterCall:
+		return rewriteValueMIPS64_OpInterCall(v, config)
+	case OpIsInBounds:
+		return rewriteValueMIPS64_OpIsInBounds(v, config)
+	case OpIsNonNil:
+		return rewriteValueMIPS64_OpIsNonNil(v, config)
+	case OpIsSliceInBounds:
+		return rewriteValueMIPS64_OpIsSliceInBounds(v, config)
+	case OpLeq16:
+		return rewriteValueMIPS64_OpLeq16(v, config)
+	case OpLeq16U:
+		return rewriteValueMIPS64_OpLeq16U(v, config)
+	case OpLeq32:
+		return rewriteValueMIPS64_OpLeq32(v, config)
+	case OpLeq32F:
+		return rewriteValueMIPS64_OpLeq32F(v, config)
+	case OpLeq32U:
+		return rewriteValueMIPS64_OpLeq32U(v, config)
+	case OpLeq64:
+		return rewriteValueMIPS64_OpLeq64(v, config)
+	case OpLeq64F:
+		return rewriteValueMIPS64_OpLeq64F(v, config)
+	case OpLeq64U:
+		return rewriteValueMIPS64_OpLeq64U(v, config)
+	case OpLeq8:
+		return rewriteValueMIPS64_OpLeq8(v, config)
+	case OpLeq8U:
+		return rewriteValueMIPS64_OpLeq8U(v, config)
+	case OpLess16:
+		return rewriteValueMIPS64_OpLess16(v, config)
+	case OpLess16U:
+		return rewriteValueMIPS64_OpLess16U(v, config)
+	case OpLess32:
+		return rewriteValueMIPS64_OpLess32(v, config)
+	case OpLess32F:
+		return rewriteValueMIPS64_OpLess32F(v, config)
+	case OpLess32U:
+		return rewriteValueMIPS64_OpLess32U(v, config)
+	case OpLess64:
+		return rewriteValueMIPS64_OpLess64(v, config)
+	case OpLess64F:
+		return rewriteValueMIPS64_OpLess64F(v, config)
+	case OpLess64U:
+		return rewriteValueMIPS64_OpLess64U(v, config)
+	case OpLess8:
+		return rewriteValueMIPS64_OpLess8(v, config)
+	case OpLess8U:
+		return rewriteValueMIPS64_OpLess8U(v, config)
+	case OpLoad:
+		return rewriteValueMIPS64_OpLoad(v, config)
+	case OpLsh16x16:
+		return rewriteValueMIPS64_OpLsh16x16(v, config)
+	case OpLsh16x32:
+		return rewriteValueMIPS64_OpLsh16x32(v, config)
+	case OpLsh16x64:
+		return rewriteValueMIPS64_OpLsh16x64(v, config)
+	case OpLsh16x8:
+		return rewriteValueMIPS64_OpLsh16x8(v, config)
+	case OpLsh32x16:
+		return rewriteValueMIPS64_OpLsh32x16(v, config)
+	case OpLsh32x32:
+		return rewriteValueMIPS64_OpLsh32x32(v, config)
+	case OpLsh32x64:
+		return rewriteValueMIPS64_OpLsh32x64(v, config)
+	case OpLsh32x8:
+		return rewriteValueMIPS64_OpLsh32x8(v, config)
+	case OpLsh64x16:
+		return rewriteValueMIPS64_OpLsh64x16(v, config)
+	case OpLsh64x32:
+		return rewriteValueMIPS64_OpLsh64x32(v, config)
+	case OpLsh64x64:
+		return rewriteValueMIPS64_OpLsh64x64(v, config)
+	case OpLsh64x8:
+		return rewriteValueMIPS64_OpLsh64x8(v, config)
+	case OpLsh8x16:
+		return rewriteValueMIPS64_OpLsh8x16(v, config)
+	case OpLsh8x32:
+		return rewriteValueMIPS64_OpLsh8x32(v, config)
+	case OpLsh8x64:
+		return rewriteValueMIPS64_OpLsh8x64(v, config)
+	case OpLsh8x8:
+		return rewriteValueMIPS64_OpLsh8x8(v, config)
+	case OpMIPS64ADDV:
+		return rewriteValueMIPS64_OpMIPS64ADDV(v, config)
+	case OpMIPS64ADDVconst:
+		return rewriteValueMIPS64_OpMIPS64ADDVconst(v, config)
+	case OpMIPS64AND:
+		return rewriteValueMIPS64_OpMIPS64AND(v, config)
+	case OpMIPS64ANDconst:
+		return rewriteValueMIPS64_OpMIPS64ANDconst(v, config)
+	case OpMIPS64MOVBUload:
+		return rewriteValueMIPS64_OpMIPS64MOVBUload(v, config)
+	case OpMIPS64MOVBUreg:
+		return rewriteValueMIPS64_OpMIPS64MOVBUreg(v, config)
+	case OpMIPS64MOVBload:
+		return rewriteValueMIPS64_OpMIPS64MOVBload(v, config)
+	case OpMIPS64MOVBreg:
+		return rewriteValueMIPS64_OpMIPS64MOVBreg(v, config)
+	case OpMIPS64MOVBstore:
+		return rewriteValueMIPS64_OpMIPS64MOVBstore(v, config)
+	case OpMIPS64MOVBstorezero:
+		return rewriteValueMIPS64_OpMIPS64MOVBstorezero(v, config)
+	case OpMIPS64MOVDload:
+		return rewriteValueMIPS64_OpMIPS64MOVDload(v, config)
+	case OpMIPS64MOVDstore:
+		return rewriteValueMIPS64_OpMIPS64MOVDstore(v, config)
+	case OpMIPS64MOVFload:
+		return rewriteValueMIPS64_OpMIPS64MOVFload(v, config)
+	case OpMIPS64MOVFstore:
+		return rewriteValueMIPS64_OpMIPS64MOVFstore(v, config)
+	case OpMIPS64MOVHUload:
+		return rewriteValueMIPS64_OpMIPS64MOVHUload(v, config)
+	case OpMIPS64MOVHUreg:
+		return rewriteValueMIPS64_OpMIPS64MOVHUreg(v, config)
+	case OpMIPS64MOVHload:
+		return rewriteValueMIPS64_OpMIPS64MOVHload(v, config)
+	case OpMIPS64MOVHreg:
+		return rewriteValueMIPS64_OpMIPS64MOVHreg(v, config)
+	case OpMIPS64MOVHstore:
+		return rewriteValueMIPS64_OpMIPS64MOVHstore(v, config)
+	case OpMIPS64MOVHstorezero:
+		return rewriteValueMIPS64_OpMIPS64MOVHstorezero(v, config)
+	case OpMIPS64MOVVload:
+		return rewriteValueMIPS64_OpMIPS64MOVVload(v, config)
+	case OpMIPS64MOVVreg:
+		return rewriteValueMIPS64_OpMIPS64MOVVreg(v, config)
+	case OpMIPS64MOVVstore:
+		return rewriteValueMIPS64_OpMIPS64MOVVstore(v, config)
+	case OpMIPS64MOVVstorezero:
+		return rewriteValueMIPS64_OpMIPS64MOVVstorezero(v, config)
+	case OpMIPS64MOVWUload:
+		return rewriteValueMIPS64_OpMIPS64MOVWUload(v, config)
+	case OpMIPS64MOVWUreg:
+		return rewriteValueMIPS64_OpMIPS64MOVWUreg(v, config)
+	case OpMIPS64MOVWload:
+		return rewriteValueMIPS64_OpMIPS64MOVWload(v, config)
+	case OpMIPS64MOVWreg:
+		return rewriteValueMIPS64_OpMIPS64MOVWreg(v, config)
+	case OpMIPS64MOVWstore:
+		return rewriteValueMIPS64_OpMIPS64MOVWstore(v, config)
+	case OpMIPS64MOVWstorezero:
+		return rewriteValueMIPS64_OpMIPS64MOVWstorezero(v, config)
+	case OpMIPS64NEGV:
+		return rewriteValueMIPS64_OpMIPS64NEGV(v, config)
+	case OpMIPS64NOR:
+		return rewriteValueMIPS64_OpMIPS64NOR(v, config)
+	case OpMIPS64NORconst:
+		return rewriteValueMIPS64_OpMIPS64NORconst(v, config)
+	case OpMIPS64OR:
+		return rewriteValueMIPS64_OpMIPS64OR(v, config)
+	case OpMIPS64ORconst:
+		return rewriteValueMIPS64_OpMIPS64ORconst(v, config)
+	case OpMIPS64SGT:
+		return rewriteValueMIPS64_OpMIPS64SGT(v, config)
+	case OpMIPS64SGTU:
+		return rewriteValueMIPS64_OpMIPS64SGTU(v, config)
+	case OpMIPS64SGTUconst:
+		return rewriteValueMIPS64_OpMIPS64SGTUconst(v, config)
+	case OpMIPS64SGTconst:
+		return rewriteValueMIPS64_OpMIPS64SGTconst(v, config)
+	case OpMIPS64SLLV:
+		return rewriteValueMIPS64_OpMIPS64SLLV(v, config)
+	case OpMIPS64SLLVconst:
+		return rewriteValueMIPS64_OpMIPS64SLLVconst(v, config)
+	case OpMIPS64SRAV:
+		return rewriteValueMIPS64_OpMIPS64SRAV(v, config)
+	case OpMIPS64SRAVconst:
+		return rewriteValueMIPS64_OpMIPS64SRAVconst(v, config)
+	case OpMIPS64SRLV:
+		return rewriteValueMIPS64_OpMIPS64SRLV(v, config)
+	case OpMIPS64SRLVconst:
+		return rewriteValueMIPS64_OpMIPS64SRLVconst(v, config)
+	case OpMIPS64SUBV:
+		return rewriteValueMIPS64_OpMIPS64SUBV(v, config)
+	case OpMIPS64SUBVconst:
+		return rewriteValueMIPS64_OpMIPS64SUBVconst(v, config)
+	case OpMIPS64XOR:
+		return rewriteValueMIPS64_OpMIPS64XOR(v, config)
+	case OpMIPS64XORconst:
+		return rewriteValueMIPS64_OpMIPS64XORconst(v, config)
+	case OpMod16:
+		return rewriteValueMIPS64_OpMod16(v, config)
+	case OpMod16u:
+		return rewriteValueMIPS64_OpMod16u(v, config)
+	case OpMod32:
+		return rewriteValueMIPS64_OpMod32(v, config)
+	case OpMod32u:
+		return rewriteValueMIPS64_OpMod32u(v, config)
+	case OpMod64:
+		return rewriteValueMIPS64_OpMod64(v, config)
+	case OpMod64u:
+		return rewriteValueMIPS64_OpMod64u(v, config)
+	case OpMod8:
+		return rewriteValueMIPS64_OpMod8(v, config)
+	case OpMod8u:
+		return rewriteValueMIPS64_OpMod8u(v, config)
+	case OpMove:
+		return rewriteValueMIPS64_OpMove(v, config)
+	case OpMul16:
+		return rewriteValueMIPS64_OpMul16(v, config)
+	case OpMul32:
+		return rewriteValueMIPS64_OpMul32(v, config)
+	case OpMul32F:
+		return rewriteValueMIPS64_OpMul32F(v, config)
+	case OpMul64:
+		return rewriteValueMIPS64_OpMul64(v, config)
+	case OpMul64F:
+		return rewriteValueMIPS64_OpMul64F(v, config)
+	case OpMul8:
+		return rewriteValueMIPS64_OpMul8(v, config)
+	case OpNeg16:
+		return rewriteValueMIPS64_OpNeg16(v, config)
+	case OpNeg32:
+		return rewriteValueMIPS64_OpNeg32(v, config)
+	case OpNeg32F:
+		return rewriteValueMIPS64_OpNeg32F(v, config)
+	case OpNeg64:
+		return rewriteValueMIPS64_OpNeg64(v, config)
+	case OpNeg64F:
+		return rewriteValueMIPS64_OpNeg64F(v, config)
+	case OpNeg8:
+		return rewriteValueMIPS64_OpNeg8(v, config)
+	case OpNeq16:
+		return rewriteValueMIPS64_OpNeq16(v, config)
+	case OpNeq32:
+		return rewriteValueMIPS64_OpNeq32(v, config)
+	case OpNeq32F:
+		return rewriteValueMIPS64_OpNeq32F(v, config)
+	case OpNeq64:
+		return rewriteValueMIPS64_OpNeq64(v, config)
+	case OpNeq64F:
+		return rewriteValueMIPS64_OpNeq64F(v, config)
+	case OpNeq8:
+		return rewriteValueMIPS64_OpNeq8(v, config)
+	case OpNeqB:
+		return rewriteValueMIPS64_OpNeqB(v, config)
+	case OpNeqPtr:
+		return rewriteValueMIPS64_OpNeqPtr(v, config)
+	case OpNilCheck:
+		return rewriteValueMIPS64_OpNilCheck(v, config)
+	case OpNot:
+		return rewriteValueMIPS64_OpNot(v, config)
+	case OpOffPtr:
+		return rewriteValueMIPS64_OpOffPtr(v, config)
+	case OpOr16:
+		return rewriteValueMIPS64_OpOr16(v, config)
+	case OpOr32:
+		return rewriteValueMIPS64_OpOr32(v, config)
+	case OpOr64:
+		return rewriteValueMIPS64_OpOr64(v, config)
+	case OpOr8:
+		return rewriteValueMIPS64_OpOr8(v, config)
+	case OpOrB:
+		return rewriteValueMIPS64_OpOrB(v, config)
+	case OpRsh16Ux16:
+		return rewriteValueMIPS64_OpRsh16Ux16(v, config)
+	case OpRsh16Ux32:
+		return rewriteValueMIPS64_OpRsh16Ux32(v, config)
+	case OpRsh16Ux64:
+		return rewriteValueMIPS64_OpRsh16Ux64(v, config)
+	case OpRsh16Ux8:
+		return rewriteValueMIPS64_OpRsh16Ux8(v, config)
+	case OpRsh16x16:
+		return rewriteValueMIPS64_OpRsh16x16(v, config)
+	case OpRsh16x32:
+		return rewriteValueMIPS64_OpRsh16x32(v, config)
+	case OpRsh16x64:
+		return rewriteValueMIPS64_OpRsh16x64(v, config)
+	case OpRsh16x8:
+		return rewriteValueMIPS64_OpRsh16x8(v, config)
+	case OpRsh32Ux16:
+		return rewriteValueMIPS64_OpRsh32Ux16(v, config)
+	case OpRsh32Ux32:
+		return rewriteValueMIPS64_OpRsh32Ux32(v, config)
+	case OpRsh32Ux64:
+		return rewriteValueMIPS64_OpRsh32Ux64(v, config)
+	case OpRsh32Ux8:
+		return rewriteValueMIPS64_OpRsh32Ux8(v, config)
+	case OpRsh32x16:
+		return rewriteValueMIPS64_OpRsh32x16(v, config)
+	case OpRsh32x32:
+		return rewriteValueMIPS64_OpRsh32x32(v, config)
+	case OpRsh32x64:
+		return rewriteValueMIPS64_OpRsh32x64(v, config)
+	case OpRsh32x8:
+		return rewriteValueMIPS64_OpRsh32x8(v, config)
+	case OpRsh64Ux16:
+		return rewriteValueMIPS64_OpRsh64Ux16(v, config)
+	case OpRsh64Ux32:
+		return rewriteValueMIPS64_OpRsh64Ux32(v, config)
+	case OpRsh64Ux64:
+		return rewriteValueMIPS64_OpRsh64Ux64(v, config)
+	case OpRsh64Ux8:
+		return rewriteValueMIPS64_OpRsh64Ux8(v, config)
+	case OpRsh64x16:
+		return rewriteValueMIPS64_OpRsh64x16(v, config)
+	case OpRsh64x32:
+		return rewriteValueMIPS64_OpRsh64x32(v, config)
+	case OpRsh64x64:
+		return rewriteValueMIPS64_OpRsh64x64(v, config)
+	case OpRsh64x8:
+		return rewriteValueMIPS64_OpRsh64x8(v, config)
+	case OpRsh8Ux16:
+		return rewriteValueMIPS64_OpRsh8Ux16(v, config)
+	case OpRsh8Ux32:
+		return rewriteValueMIPS64_OpRsh8Ux32(v, config)
+	case OpRsh8Ux64:
+		return rewriteValueMIPS64_OpRsh8Ux64(v, config)
+	case OpRsh8Ux8:
+		return rewriteValueMIPS64_OpRsh8Ux8(v, config)
+	case OpRsh8x16:
+		return rewriteValueMIPS64_OpRsh8x16(v, config)
+	case OpRsh8x32:
+		return rewriteValueMIPS64_OpRsh8x32(v, config)
+	case OpRsh8x64:
+		return rewriteValueMIPS64_OpRsh8x64(v, config)
+	case OpRsh8x8:
+		return rewriteValueMIPS64_OpRsh8x8(v, config)
+	case OpSelect0:
+		return rewriteValueMIPS64_OpSelect0(v, config)
+	case OpSelect1:
+		return rewriteValueMIPS64_OpSelect1(v, config)
+	case OpSignExt16to32:
+		return rewriteValueMIPS64_OpSignExt16to32(v, config)
+	case OpSignExt16to64:
+		return rewriteValueMIPS64_OpSignExt16to64(v, config)
+	case OpSignExt32to64:
+		return rewriteValueMIPS64_OpSignExt32to64(v, config)
+	case OpSignExt8to16:
+		return rewriteValueMIPS64_OpSignExt8to16(v, config)
+	case OpSignExt8to32:
+		return rewriteValueMIPS64_OpSignExt8to32(v, config)
+	case OpSignExt8to64:
+		return rewriteValueMIPS64_OpSignExt8to64(v, config)
+	case OpSlicemask:
+		return rewriteValueMIPS64_OpSlicemask(v, config)
+	case OpStaticCall:
+		return rewriteValueMIPS64_OpStaticCall(v, config)
+	case OpStore:
+		return rewriteValueMIPS64_OpStore(v, config)
+	case OpSub16:
+		return rewriteValueMIPS64_OpSub16(v, config)
+	case OpSub32:
+		return rewriteValueMIPS64_OpSub32(v, config)
+	case OpSub32F:
+		return rewriteValueMIPS64_OpSub32F(v, config)
+	case OpSub64:
+		return rewriteValueMIPS64_OpSub64(v, config)
+	case OpSub64F:
+		return rewriteValueMIPS64_OpSub64F(v, config)
+	case OpSub8:
+		return rewriteValueMIPS64_OpSub8(v, config)
+	case OpSubPtr:
+		return rewriteValueMIPS64_OpSubPtr(v, config)
+	case OpTrunc16to8:
+		return rewriteValueMIPS64_OpTrunc16to8(v, config)
+	case OpTrunc32to16:
+		return rewriteValueMIPS64_OpTrunc32to16(v, config)
+	case OpTrunc32to8:
+		return rewriteValueMIPS64_OpTrunc32to8(v, config)
+	case OpTrunc64to16:
+		return rewriteValueMIPS64_OpTrunc64to16(v, config)
+	case OpTrunc64to32:
+		return rewriteValueMIPS64_OpTrunc64to32(v, config)
+	case OpTrunc64to8:
+		return rewriteValueMIPS64_OpTrunc64to8(v, config)
+	case OpXor16:
+		return rewriteValueMIPS64_OpXor16(v, config)
+	case OpXor32:
+		return rewriteValueMIPS64_OpXor32(v, config)
+	case OpXor64:
+		return rewriteValueMIPS64_OpXor64(v, config)
+	case OpXor8:
+		return rewriteValueMIPS64_OpXor8(v, config)
+	case OpZero:
+		return rewriteValueMIPS64_OpZero(v, config)
+	case OpZeroExt16to32:
+		return rewriteValueMIPS64_OpZeroExt16to32(v, config)
+	case OpZeroExt16to64:
+		return rewriteValueMIPS64_OpZeroExt16to64(v, config)
+	case OpZeroExt32to64:
+		return rewriteValueMIPS64_OpZeroExt32to64(v, config)
+	case OpZeroExt8to16:
+		return rewriteValueMIPS64_OpZeroExt8to16(v, config)
+	case OpZeroExt8to32:
+		return rewriteValueMIPS64_OpZeroExt8to32(v, config)
+	case OpZeroExt8to64:
+		return rewriteValueMIPS64_OpZeroExt8to64(v, config)
+	}
+	return false
+}
+func rewriteValueMIPS64_OpAdd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add16 x y)
+	// cond:
+	// result: (ADDV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64ADDV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32 x y)
+	// cond:
+	// result: (ADDV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64ADDV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAdd32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32F x y)
+	// cond:
+	// result: (ADDF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64ADDF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAdd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64 x y)
+	// cond:
+	// result: (ADDV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64ADDV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAdd64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64F x y)
+	// cond:
+	// result: (ADDD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64ADDD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAdd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add8 x y)
+	// cond:
+	// result: (ADDV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64ADDV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAddPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AddPtr x y)
+	// cond:
+	// result: (ADDV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64ADDV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAddr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Addr {sym} base)
+	// cond:
+	// result: (MOVVaddr {sym} base)
+	for {
+		sym := v.Aux
+		base := v.Args[0]
+		v.reset(OpMIPS64MOVVaddr)
+		v.Aux = sym
+		v.AddArg(base)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAnd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And16 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAnd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And32 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAnd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And64 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And8 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAndB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AndB x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpAvg64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Avg64u <t> x y)
+	// cond:
+	// result: (ADDV (ADDV <t> (SRLVconst <t> x [1]) (SRLVconst <t> y [1])) (AND <t> (AND <t> x y) (MOVVconst [1])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64ADDV)
+		v0 := b.NewValue0(v.Line, OpMIPS64ADDV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SRLVconst, t)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpMIPS64SRLVconst, t)
+		v2.AuxInt = 1
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64AND, t)
+		v4 := b.NewValue0(v.Line, OpMIPS64AND, t)
+		v4.AddArg(x)
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v5.AuxInt = 1
+		v3.AddArg(v5)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpClosureCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ClosureCall [argwid] entry closure mem)
+	// cond:
+	// result: (CALLclosure [argwid] entry closure mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		closure := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpMIPS64CALLclosure)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(closure)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCom16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com16 x)
+	// cond:
+	// result: (NOR (MOVVconst [0]) x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64NOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCom32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com32 x)
+	// cond:
+	// result: (NOR (MOVVconst [0]) x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64NOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCom64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com64 x)
+	// cond:
+	// result: (NOR (MOVVconst [0]) x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64NOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCom8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com8 x)
+	// cond:
+	// result: (NOR (MOVVconst [0]) x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64NOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpConst16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const16 [val])
+	// cond:
+	// result: (MOVVconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS64_OpConst32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32 [val])
+	// cond:
+	// result: (MOVVconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS64_OpConst32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32F [val])
+	// cond:
+	// result: (MOVFconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPS64MOVFconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS64_OpConst64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64 [val])
+	// cond:
+	// result: (MOVVconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS64_OpConst64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64F [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPS64MOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS64_OpConst8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const8 [val])
+	// cond:
+	// result: (MOVVconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueMIPS64_OpConstBool(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstBool [b])
+	// cond:
+	// result: (MOVVconst [b])
+	for {
+		b := v.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = b
+		return true
+	}
+}
+func rewriteValueMIPS64_OpConstNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstNil)
+	// cond:
+	// result: (MOVVconst [0])
+	for {
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+}
+func rewriteValueMIPS64_OpConvert(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Convert x mem)
+	// cond:
+	// result: (MOVVconvert x mem)
+	for {
+		x := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpMIPS64MOVVconvert)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCvt32Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto32 x)
+	// cond:
+	// result: (TRUNCFW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64TRUNCFW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCvt32Fto64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64 x)
+	// cond:
+	// result: (TRUNCFV x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64TRUNCFV)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCvt32Fto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64F x)
+	// cond:
+	// result: (MOVFD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVFD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCvt32to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to32F x)
+	// cond:
+	// result: (MOVWF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVWF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCvt32to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to64F x)
+	// cond:
+	// result: (MOVWD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVWD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCvt64Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32 x)
+	// cond:
+	// result: (TRUNCDW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64TRUNCDW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCvt64Fto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32F x)
+	// cond:
+	// result: (MOVDF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVDF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCvt64Fto64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto64 x)
+	// cond:
+	// result: (TRUNCDV x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64TRUNCDV)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCvt64to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64to32F x)
+	// cond:
+	// result: (MOVVF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVVF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpCvt64to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64to64F x)
+	// cond:
+	// result: (MOVVD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVVD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDeferCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (DeferCall [argwid] mem)
+	// cond:
+	// result: (CALLdefer [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpMIPS64CALLdefer)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDiv16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16 x y)
+	// cond:
+	// result: (Select1 (DIVV (SignExt16to64 x) (SignExt16to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v1 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDiv16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16u x y)
+	// cond:
+	// result: (Select1 (DIVVU (ZeroExt16to64 x) (ZeroExt16to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDiv32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32 x y)
+	// cond:
+	// result: (Select1 (DIVV (SignExt32to64 x) (SignExt32to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v1 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDiv32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32F x y)
+	// cond:
+	// result: (DIVF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64DIVF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDiv32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32u x y)
+	// cond:
+	// result: (Select1 (DIVVU (ZeroExt32to64 x) (ZeroExt32to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDiv64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64 x y)
+	// cond:
+	// result: (Select1 (DIVV x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDiv64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64F x y)
+	// cond:
+	// result: (DIVD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64DIVD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDiv64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64u x y)
+	// cond:
+	// result: (Select1 (DIVVU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDiv8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8 x y)
+	// cond:
+	// result: (Select1 (DIVV (SignExt8to64 x) (SignExt8to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v1 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpDiv8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8u x y)
+	// cond:
+	// result: (Select1 (DIVVU (ZeroExt8to64 x) (ZeroExt8to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpEq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq16 x y)
+	// cond:
+	// result: (SGTU (MOVVconst [1]) (XOR (ZeroExt16to64 x) (ZeroExt16to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeUInt64())
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpEq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32 x y)
+	// cond:
+	// result: (SGTU (MOVVconst [1]) (XOR (ZeroExt32to64 x) (ZeroExt32to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeUInt64())
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpEq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPEQF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPEQF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpEq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64 x y)
+	// cond:
+	// result: (SGTU (MOVVconst [1]) (XOR x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpEq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPEQD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPEQD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpEq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq8 x y)
+	// cond:
+	// result: (SGTU (MOVVconst [1]) (XOR (ZeroExt8to64 x) (ZeroExt8to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeUInt64())
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpEqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqB x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (XOR <config.fe.TypeBool()> x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeBool())
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpEqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqPtr x y)
+	// cond:
+	// result: (SGTU (MOVVconst [1]) (XOR x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16 x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGT (SignExt16to64 y) (SignExt16to64 x)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGT, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16U x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGTU (ZeroExt16to64 y) (ZeroExt16to64 x)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32 x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGT (SignExt32to64 y) (SignExt32to64 x)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGT, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGEF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPGEF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32U x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGTU (ZeroExt32to64 y) (ZeroExt32to64 x)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64 x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGT y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGT, config.fe.TypeBool())
+		v1.AddArg(y)
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGED x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPGED, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64U x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGTU y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v1.AddArg(y)
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8 x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGT (SignExt8to64 y) (SignExt8to64 x)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGT, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8U x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGTU (ZeroExt8to64 y) (ZeroExt8to64 x)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGetClosurePtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetClosurePtr)
+	// cond:
+	// result: (LoweredGetClosurePtr)
+	for {
+		v.reset(OpMIPS64LoweredGetClosurePtr)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGoCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GoCall [argwid] mem)
+	// cond:
+	// result: (CALLgo [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpMIPS64CALLgo)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGreater16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16 x y)
+	// cond:
+	// result: (SGT (SignExt16to64 x) (SignExt16to64 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGT)
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGreater16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16U x y)
+	// cond:
+	// result: (SGTU (ZeroExt16to64 x) (ZeroExt16to64 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGreater32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32 x y)
+	// cond:
+	// result: (SGT (SignExt32to64 x) (SignExt32to64 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGT)
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGreater32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGTF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPGTF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGreater32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32U x y)
+	// cond:
+	// result: (SGTU (ZeroExt32to64 x) (ZeroExt32to64 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGreater64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64 x y)
+	// cond:
+	// result: (SGT x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGT)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGreater64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGTD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPGTD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGreater64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64U x y)
+	// cond:
+	// result: (SGTU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGreater8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8 x y)
+	// cond:
+	// result: (SGT (SignExt8to64 x) (SignExt8to64 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGT)
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpGreater8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8U x y)
+	// cond:
+	// result: (SGTU (ZeroExt8to64 x) (ZeroExt8to64 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpHmul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16 x y)
+	// cond:
+	// result: (SRAVconst (Select1 <config.fe.TypeInt32()> (MULV (SignExt16to64 x) (SignExt16to64 y))) [16])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAVconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpSelect1, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpMIPS64MULV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v2 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpHmul16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16u x y)
+	// cond:
+	// result: (SRLVconst (Select1 <config.fe.TypeUInt32()> (MULVU (ZeroExt16to64 x) (ZeroExt16to64 y))) [16])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRLVconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpSelect1, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpMIPS64MULVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpHmul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32 x y)
+	// cond:
+	// result: (SRAVconst (Select1 <config.fe.TypeInt64()> (MULV (SignExt32to64 x) (SignExt32to64 y))) [32])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAVconst)
+		v.AuxInt = 32
+		v0 := b.NewValue0(v.Line, OpSelect1, config.fe.TypeInt64())
+		v1 := b.NewValue0(v.Line, OpMIPS64MULV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v2 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpHmul32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32u x y)
+	// cond:
+	// result: (SRLVconst (Select1 <config.fe.TypeUInt64()> (MULVU (ZeroExt32to64 x) (ZeroExt32to64 y))) [32])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRLVconst)
+		v.AuxInt = 32
+		v0 := b.NewValue0(v.Line, OpSelect1, config.fe.TypeUInt64())
+		v1 := b.NewValue0(v.Line, OpMIPS64MULVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpHmul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul64 x y)
+	// cond:
+	// result: (Select0 (MULV x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPS64MULV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpHmul64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul64u x y)
+	// cond:
+	// result: (Select0 (MULVU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPS64MULVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpHmul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8 x y)
+	// cond:
+	// result: (SRAVconst (Select1 <config.fe.TypeInt16()> (MULV (SignExt8to64 x) (SignExt8to64 y))) [8])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAVconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpSelect1, config.fe.TypeInt16())
+		v1 := b.NewValue0(v.Line, OpMIPS64MULV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v2 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpHmul8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8u x y)
+	// cond:
+	// result: (SRLVconst (Select1 <config.fe.TypeUInt16()> (MULVU (ZeroExt8to64 x) (ZeroExt8to64 y))) [8])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRLVconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpSelect1, config.fe.TypeUInt16())
+		v1 := b.NewValue0(v.Line, OpMIPS64MULVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpInterCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (InterCall [argwid] entry mem)
+	// cond:
+	// result: (CALLinter [argwid] entry mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpMIPS64CALLinter)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpIsInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsInBounds idx len)
+	// cond:
+	// result: (SGTU len idx)
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v.AddArg(len)
+		v.AddArg(idx)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpIsNonNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsNonNil ptr)
+	// cond:
+	// result: (SGTU ptr (MOVVconst [0]))
+	for {
+		ptr := v.Args[0]
+		v.reset(OpMIPS64SGTU)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpIsSliceInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsSliceInBounds idx len)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGTU idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v1.AddArg(idx)
+		v1.AddArg(len)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16 x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGT (SignExt16to64 x) (SignExt16to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGT, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16U x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGTU (ZeroExt16to64 x) (ZeroExt16to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32 x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGT (SignExt32to64 x) (SignExt32to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGT, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGEF y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPGEF, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32U x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGTU (ZeroExt32to64 x) (ZeroExt32to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64 x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGT x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGT, config.fe.TypeBool())
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGED y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPGED, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64U x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGTU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8 x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGT (SignExt8to64 x) (SignExt8to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGT, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8U x y)
+	// cond:
+	// result: (XOR (MOVVconst [1]) (SGTU (ZeroExt8to64 x) (ZeroExt8to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLess16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16 x y)
+	// cond:
+	// result: (SGT (SignExt16to64 y) (SignExt16to64 x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGT)
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLess16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16U x y)
+	// cond:
+	// result: (SGTU (ZeroExt16to64 y) (ZeroExt16to64 x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLess32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32 x y)
+	// cond:
+	// result: (SGT (SignExt32to64 y) (SignExt32to64 x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGT)
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLess32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGTF y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPGTF, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLess32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32U x y)
+	// cond:
+	// result: (SGTU (ZeroExt32to64 y) (ZeroExt32to64 x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLess64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64 x y)
+	// cond:
+	// result: (SGT y x)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGT)
+		v.AddArg(y)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLess64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64F x y)
+	// cond:
+	// result: (FPFlagTrue (CMPGTD y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagTrue)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPGTD, TypeFlags)
+		v0.AddArg(y)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLess64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64U x y)
+	// cond:
+	// result: (SGTU y x)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v.AddArg(y)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLess8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8 x y)
+	// cond:
+	// result: (SGT (SignExt8to64 y) (SignExt8to64 x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGT)
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLess8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8U x y)
+	// cond:
+	// result: (SGTU (ZeroExt8to64 y) (ZeroExt8to64 x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLoad(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Load <t> ptr mem)
+	// cond: t.IsBoolean()
+	// result: (MOVBUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(t.IsBoolean()) {
+			break
+		}
+		v.reset(OpMIPS64MOVBUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is8BitInt(t) && isSigned(t))
+	// result: (MOVBload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is8BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpMIPS64MOVBload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is8BitInt(t) && !isSigned(t))
+	// result: (MOVBUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is8BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpMIPS64MOVBUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is16BitInt(t) && isSigned(t))
+	// result: (MOVHload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpMIPS64MOVHload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is16BitInt(t) && !isSigned(t))
+	// result: (MOVHUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpMIPS64MOVHUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is32BitInt(t) && isSigned(t))
+	// result: (MOVWload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is32BitInt(t) && !isSigned(t))
+	// result: (MOVWUload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWUload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (is64BitInt(t) || isPtr(t))
+	// result: (MOVVload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitInt(t) || isPtr(t)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitFloat(t)
+	// result: (MOVFload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitFloat(t)) {
+			break
+		}
+		v.reset(OpMIPS64MOVFload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitFloat(t)
+	// result: (MOVDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitFloat(t)) {
+			break
+		}
+		v.reset(OpMIPS64MOVDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpLsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x16 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SLLV <t> x (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x32 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SLLV <t> x (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x64 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SLLV <t> x y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v3.AddArg(x)
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x8  <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SLLV <t> x (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x16 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SLLV <t> x (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x32 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SLLV <t> x (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x64 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SLLV <t> x y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v3.AddArg(x)
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x8  <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SLLV <t> x (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x16 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SLLV <t> x (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x32 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SLLV <t> x (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh64x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x64 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SLLV <t> x y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v3.AddArg(x)
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x8  <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SLLV <t> x (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x16 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SLLV <t> x (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x32 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SLLV <t> x (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x64 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SLLV <t> x y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v3.AddArg(x)
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpLsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x8  <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SLLV <t> x (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SLLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMIPS64ADDV(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDV (MOVVconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (ADDVconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64ADDVconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDV x (MOVVconst [c]))
+	// cond: is32Bit(c)
+	// result: (ADDVconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64ADDVconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDV x (NEGV y))
+	// cond:
+	// result: (SUBV x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64NEGV {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpMIPS64SUBV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDV (NEGV y) x)
+	// cond:
+	// result: (SUBV x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64NEGV {
+			break
+		}
+		y := v_0.Args[0]
+		x := v.Args[1]
+		v.reset(OpMIPS64SUBV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64ADDVconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDVconst [off1] (MOVVaddr [off2] {sym} ptr))
+	// cond:
+	// result: (MOVVaddr [off1+off2] {sym} ptr)
+	for {
+		off1 := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym := v_0.Aux
+		ptr := v_0.Args[0]
+		v.reset(OpMIPS64MOVVaddr)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		return true
+	}
+	// match: (ADDVconst [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDVconst [c] (MOVVconst [d]))
+	// cond:
+	// result: (MOVVconst [c+d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = c + d
+		return true
+	}
+	// match: (ADDVconst [c] (ADDVconst [d] x))
+	// cond: is32Bit(c+d)
+	// result: (ADDVconst [c+d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(c + d)) {
+			break
+		}
+		v.reset(OpMIPS64ADDVconst)
+		v.AuxInt = c + d
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDVconst [c] (SUBVconst [d] x))
+	// cond: is32Bit(c-d)
+	// result: (ADDVconst [c-d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64SUBVconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(c - d)) {
+			break
+		}
+		v.reset(OpMIPS64ADDVconst)
+		v.AuxInt = c - d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64AND(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AND (MOVVconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (ANDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64ANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x (MOVVconst [c]))
+	// cond: is32Bit(c)
+	// result: (ANDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64ANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64ANDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDconst [0]  _)
+	// cond:
+	// result: (MOVVconst [0])
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (ANDconst [-1] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDconst [c] (MOVVconst [d]))
+	// cond:
+	// result: (MOVVconst [c&d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = c & d
+		return true
+	}
+	// match: (ANDconst [c] (ANDconst [d] x))
+	// cond:
+	// result: (ANDconst [c&d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ANDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpMIPS64ANDconst)
+		v.AuxInt = c & d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVBUload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBUload [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVBUload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVBUload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVBUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVBUload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVBUreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBUreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBUload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBUreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBUreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBUreg (MOVVconst [c]))
+	// cond:
+	// result: (MOVVconst [int64(uint8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(uint8(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVBload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBload  [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVBload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVBload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVBreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBreg x:(MOVBload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg x:(MOVBreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg  (MOVVconst [c]))
+	// cond:
+	// result: (MOVVconst [int64(int8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(int8(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVBstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVBstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVVconst [0]) mem)
+	// cond:
+	// result: (MOVBstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVBstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVBreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBUreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVBUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVHreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVHreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVHUreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVHUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVWUreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVWUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVBstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstorezero [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVBstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVDload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDload  [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVDload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVDstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVFload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVFload  [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVFload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVFload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVFload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVFload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVFload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVFstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVFstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVFstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVFstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVFstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVFstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVFstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVHUload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHUload [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVHUload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVHUload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVHUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVHUload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVHUreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHUreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBUload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVHUload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVHUload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBUreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg x:(MOVHUreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVHUreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHUreg (MOVVconst [c]))
+	// cond:
+	// result: (MOVVconst [int64(uint16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(uint16(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVHload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHload  [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVHload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVHreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHreg x:(MOVBload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBUload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVHload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBUreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVHreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg  (MOVVconst [c]))
+	// cond:
+	// result: (MOVVconst [int64(int16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(int16(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVHstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVHstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVVconst [0]) mem)
+	// cond:
+	// result: (MOVHstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVHstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVHreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHUreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVHUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVWUreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVWUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVHstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstorezero [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVHstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVVload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVVload  [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVVload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVVload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVVload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVVreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVVreg x)
+	// cond: x.Uses == 1
+	// result: (MOVVnop x)
+	for {
+		x := v.Args[0]
+		if !(x.Uses == 1) {
+			break
+		}
+		v.reset(OpMIPS64MOVVnop)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVVreg  (MOVVconst [c]))
+	// cond:
+	// result: (MOVVconst [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = c
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVVstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVVstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVVstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVVstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVVstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVVstore [off] {sym} ptr (MOVVconst [0]) mem)
+	// cond:
+	// result: (MOVVstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVVstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVVstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVVstorezero [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVVstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVVstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVVstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVWUload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWUload [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVWUload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWUload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWUload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVWUload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWUload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVWUreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWUreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBUload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg x:(MOVHUload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVHUload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg x:(MOVWUload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVWUload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBUreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg x:(MOVHUreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVHUreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg x:(MOVWUreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVWUreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWUreg (MOVVconst [c]))
+	// cond:
+	// result: (MOVVconst [int64(uint32(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(uint32(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVWload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWload  [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVWload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVWreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWreg x:(MOVBload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVBUload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBUload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVHload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHUload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVHUload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVWload _ _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVWload {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVBreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVBUreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVBUreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVHreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVHreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVWreg _))
+	// cond:
+	// result: (MOVVreg x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpMIPS64MOVWreg {
+			break
+		}
+		v.reset(OpMIPS64MOVVreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg  (MOVVconst [c]))
+	// cond:
+	// result: (MOVVconst [int64(int32(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(int32(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVWstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstore [off1] {sym} (ADDVconst [off2] ptr) val mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVWstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVVconst [0]) mem)
+	// cond:
+	// result: (MOVWstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVWstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWUreg x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVWUreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64MOVWstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstorezero [off1] {sym} (ADDVconst [off2] ptr) mem)
+	// cond: is32Bit(off1+off2)
+	// result: (MOVWstorezero [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstorezero [off1] {sym1} (MOVVaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2) && is32Bit(off1+off2)
+	// result: (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && is32Bit(off1+off2)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64NEGV(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NEGV (MOVVconst [c]))
+	// cond:
+	// result: (MOVVconst [-c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = -c
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64NOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NOR (MOVVconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (NORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64NORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (NOR x (MOVVconst [c]))
+	// cond: is32Bit(c)
+	// result: (NORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64NORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64NORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NORconst [c] (MOVVconst [d]))
+	// cond:
+	// result: (MOVVconst [^(c|d)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = ^(c | d)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64OR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OR  (MOVVconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (ORconst  [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64ORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR  x (MOVVconst [c]))
+	// cond: is32Bit(c)
+	// result: (ORconst  [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64ORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR  x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64ORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORconst  [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORconst  [-1] _)
+	// cond:
+	// result: (MOVVconst [-1])
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (ORconst [c] (MOVVconst [d]))
+	// cond:
+	// result: (MOVVconst [c|d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = c | d
+		return true
+	}
+	// match: (ORconst [c] (ORconst [d] x))
+	// cond: is32Bit(c|d)
+	// result: (ORconst [c|d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ORconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(c | d)) {
+			break
+		}
+		v.reset(OpMIPS64ORconst)
+		v.AuxInt = c | d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SGT(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SGT  (MOVVconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (SGTconst  [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64SGTconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SGTU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SGTU (MOVVconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (SGTUconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64SGTUconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SGTUconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SGTUconst [c] (MOVVconst [d]))
+	// cond: uint64(c)>uint64(d)
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(uint64(c) > uint64(d)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTUconst [c] (MOVVconst [d]))
+	// cond: uint64(c)<=uint64(d)
+	// result: (MOVVconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(uint64(c) <= uint64(d)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTUconst [c] (MOVBUreg _))
+	// cond: 0xff < uint64(c)
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVBUreg {
+			break
+		}
+		if !(0xff < uint64(c)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTUconst [c] (MOVHUreg _))
+	// cond: 0xffff < uint64(c)
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVHUreg {
+			break
+		}
+		if !(0xffff < uint64(c)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTUconst [c] (ANDconst [m] _))
+	// cond: uint64(m) < uint64(c)
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ANDconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(uint64(m) < uint64(c)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTUconst [c] (SRLVconst _ [d]))
+	// cond: 0 < d && d <= 63 && 1<<uint64(64-d) <= uint64(c)
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64SRLVconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(0 < d && d <= 63 && 1<<uint64(64-d) <= uint64(c)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SGTconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SGTconst [c] (MOVVconst [d]))
+	// cond: int64(c)>int64(d)
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(int64(c) > int64(d)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (MOVVconst [d]))
+	// cond: int64(c)<=int64(d)
+	// result: (MOVVconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(int64(c) <= int64(d)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (MOVBreg _))
+	// cond: 0x7f < int64(c)
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVBreg {
+			break
+		}
+		if !(0x7f < int64(c)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (MOVBreg _))
+	// cond: int64(c) <= -0x80
+	// result: (MOVVconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVBreg {
+			break
+		}
+		if !(int64(c) <= -0x80) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (MOVBUreg _))
+	// cond: 0xff < int64(c)
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVBUreg {
+			break
+		}
+		if !(0xff < int64(c)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (MOVBUreg _))
+	// cond: int64(c) < 0
+	// result: (MOVVconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVBUreg {
+			break
+		}
+		if !(int64(c) < 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (MOVHreg _))
+	// cond: 0x7fff < int64(c)
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVHreg {
+			break
+		}
+		if !(0x7fff < int64(c)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (MOVHreg _))
+	// cond: int64(c) <= -0x8000
+	// result: (MOVVconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVHreg {
+			break
+		}
+		if !(int64(c) <= -0x8000) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (MOVHUreg _))
+	// cond: 0xffff < int64(c)
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVHUreg {
+			break
+		}
+		if !(0xffff < int64(c)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (MOVHUreg _))
+	// cond: int64(c) < 0
+	// result: (MOVVconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVHUreg {
+			break
+		}
+		if !(int64(c) < 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (MOVWUreg _))
+	// cond: int64(c) < 0
+	// result: (MOVVconst [0])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVWUreg {
+			break
+		}
+		if !(int64(c) < 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SGTconst [c] (ANDconst [m] _))
+	// cond: 0 <= m && m < c
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ANDconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(0 <= m && m < c) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (SGTconst [c] (SRLVconst _ [d]))
+	// cond: 0 <= c && 0 < d && d <= 63 && 1<<uint64(64-d) <= c
+	// result: (MOVVconst [1])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64SRLVconst {
+			break
+		}
+		d := v_0.AuxInt
+		if !(0 <= c && 0 < d && d <= 63 && 1<<uint64(64-d) <= c) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 1
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SLLV(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SLLV _ (MOVVconst [c]))
+	// cond: uint64(c)>=64
+	// result: (MOVVconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 64) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SLLV x (MOVVconst [c]))
+	// cond:
+	// result: (SLLVconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPS64SLLVconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SLLVconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SLLVconst [c] (MOVVconst [d]))
+	// cond:
+	// result: (MOVVconst [int64(d)<<uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(d) << uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SRAV(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRAV x (MOVVconst [c]))
+	// cond: uint64(c)>=64
+	// result: (SRAVconst x [63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 64) {
+			break
+		}
+		v.reset(OpMIPS64SRAVconst)
+		v.AuxInt = 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRAV x (MOVVconst [c]))
+	// cond:
+	// result: (SRAVconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPS64SRAVconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SRAVconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRAVconst [c] (MOVVconst [d]))
+	// cond:
+	// result: (MOVVconst [int64(d)>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(d) >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SRLV(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRLV _ (MOVVconst [c]))
+	// cond: uint64(c)>=64
+	// result: (MOVVconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 64) {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SRLV x (MOVVconst [c]))
+	// cond:
+	// result: (SRLVconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpMIPS64SRLVconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SRLVconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRLVconst [c] (MOVVconst [d]))
+	// cond:
+	// result: (MOVVconst [int64(uint64(d)>>uint64(c))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(uint64(d) >> uint64(c))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SUBV(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBV x (MOVVconst [c]))
+	// cond: is32Bit(c)
+	// result: (SUBVconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64SUBVconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBV x x)
+	// cond:
+	// result: (MOVVconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SUBV (MOVVconst [0]) x)
+	// cond:
+	// result: (NEGV x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_0.AuxInt != 0 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpMIPS64NEGV)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64SUBVconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBVconst [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBVconst [c] (MOVVconst [d]))
+	// cond:
+	// result: (MOVVconst [d-c])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = d - c
+		return true
+	}
+	// match: (SUBVconst [c] (SUBVconst [d] x))
+	// cond: is32Bit(-c-d)
+	// result: (ADDVconst [-c-d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64SUBVconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(-c - d)) {
+			break
+		}
+		v.reset(OpMIPS64ADDVconst)
+		v.AuxInt = -c - d
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBVconst [c] (ADDVconst [d] x))
+	// cond: is32Bit(-c+d)
+	// result: (ADDVconst [-c+d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64ADDVconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(-c + d)) {
+			break
+		}
+		v.reset(OpMIPS64ADDVconst)
+		v.AuxInt = -c + d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64XOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XOR (MOVVconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (XORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64XORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR x (MOVVconst [c]))
+	// cond: is32Bit(c)
+	// result: (XORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpMIPS64XORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR x x)
+	// cond:
+	// result: (MOVVconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMIPS64XORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORconst [0]  x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORconst [-1] x)
+	// cond:
+	// result: (NORconst [0] x)
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpMIPS64NORconst)
+		v.AuxInt = 0
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORconst [c] (MOVVconst [d]))
+	// cond:
+	// result: (MOVVconst [c^d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	// match: (XORconst [c] (XORconst [d] x))
+	// cond: is32Bit(c^d)
+	// result: (XORconst [c^d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64XORconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(c ^ d)) {
+			break
+		}
+		v.reset(OpMIPS64XORconst)
+		v.AuxInt = c ^ d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMod16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16 x y)
+	// cond:
+	// result: (Select0 (DIVV (SignExt16to64 x) (SignExt16to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v1 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMod16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16u x y)
+	// cond:
+	// result: (Select0 (DIVVU (ZeroExt16to64 x) (ZeroExt16to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v1 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMod32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32 x y)
+	// cond:
+	// result: (Select0 (DIVV (SignExt32to64 x) (SignExt32to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v1 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMod32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32u x y)
+	// cond:
+	// result: (Select0 (DIVVU (ZeroExt32to64 x) (ZeroExt32to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMod64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod64 x y)
+	// cond:
+	// result: (Select0 (DIVV x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMod64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod64u x y)
+	// cond:
+	// result: (Select0 (DIVVU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMod8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8 x y)
+	// cond:
+	// result: (Select0 (DIVV (SignExt8to64 x) (SignExt8to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVV, MakeTuple(config.fe.TypeInt64(), config.fe.TypeInt64()))
+		v1 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMod8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8u x y)
+	// cond:
+	// result: (Select0 (DIVVU (ZeroExt8to64 x) (ZeroExt8to64 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect0)
+		v0 := b.NewValue0(v.Line, OpMIPS64DIVVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMove(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Move [s] _ _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore dst (MOVBload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVBload, config.fe.TypeInt8())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore dst (MOVHload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVHload, config.fe.TypeInt16())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVBstore [1] dst (MOVBload [1] src mem) 		(MOVBstore dst (MOVBload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = 1
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVBload, config.fe.TypeInt8())
+		v0.AuxInt = 1
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVBload, config.fe.TypeInt8())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore dst (MOVWload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVWload, config.fe.TypeInt32())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [2] dst (MOVHload [2] src mem) 		(MOVHstore dst (MOVHload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVHload, config.fe.TypeInt16())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVHload, config.fe.TypeInt16())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVBstore [3] dst (MOVBload [3] src mem) 		(MOVBstore [2] dst (MOVBload [2] src mem) 			(MOVBstore [1] dst (MOVBload [1] src mem) 				(MOVBstore dst (MOVBload src mem) mem))))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = 3
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVBload, config.fe.TypeInt8())
+		v0.AuxInt = 3
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVBload, config.fe.TypeInt8())
+		v2.AuxInt = 2
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v3.AuxInt = 1
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVBload, config.fe.TypeInt8())
+		v4.AuxInt = 1
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v5.AddArg(dst)
+		v6 := b.NewValue0(v.Line, OpMIPS64MOVBload, config.fe.TypeInt8())
+		v6.AddArg(src)
+		v6.AddArg(mem)
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVVstore dst (MOVVload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVload, config.fe.TypeUInt64())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [4] dst (MOVWload [4] src mem) 		(MOVWstore dst (MOVWload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVWload, config.fe.TypeInt32())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVWstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVWload, config.fe.TypeInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [6] dst (MOVHload [6] src mem) 		(MOVHstore [4] dst (MOVHload [4] src mem) 			(MOVHstore [2] dst (MOVHload [2] src mem) 				(MOVHstore dst (MOVHload src mem) mem))))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = 6
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVHload, config.fe.TypeInt16())
+		v0.AuxInt = 6
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVHload, config.fe.TypeInt16())
+		v2.AuxInt = 4
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v3.AuxInt = 2
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVHload, config.fe.TypeInt16())
+		v4.AuxInt = 2
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v5.AddArg(dst)
+		v6 := b.NewValue0(v.Line, OpMIPS64MOVHload, config.fe.TypeInt16())
+		v6.AddArg(src)
+		v6.AddArg(mem)
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] dst (MOVBload [2] src mem) 		(MOVBstore [1] dst (MOVBload [1] src mem) 			(MOVBstore dst (MOVBload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVBload, config.fe.TypeInt8())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v1.AuxInt = 1
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVBload, config.fe.TypeInt8())
+		v2.AuxInt = 1
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVBload, config.fe.TypeInt8())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [4] dst (MOVHload [4] src mem) 		(MOVHstore [2] dst (MOVHload [2] src mem) 			(MOVHstore dst (MOVHload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVHload, config.fe.TypeInt16())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVHload, config.fe.TypeInt16())
+		v2.AuxInt = 2
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVHload, config.fe.TypeInt16())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [8] dst (MOVWload [8] src mem) 		(MOVWstore [4] dst (MOVWload [4] src mem) 			(MOVWstore dst (MOVWload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstore)
+		v.AuxInt = 8
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVWload, config.fe.TypeInt32())
+		v0.AuxInt = 8
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVWstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVWload, config.fe.TypeInt32())
+		v2.AuxInt = 4
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVWstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVWload, config.fe.TypeInt32())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVVstore [8] dst (MOVVload [8] src mem) 		(MOVVstore dst (MOVVload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstore)
+		v.AuxInt = 8
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVload, config.fe.TypeUInt64())
+		v0.AuxInt = 8
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVVstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVload, config.fe.TypeUInt64())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 24 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVVstore [16] dst (MOVVload [16] src mem) 		(MOVVstore [8] dst (MOVVload [8] src mem) 			(MOVVstore dst (MOVVload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 24 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstore)
+		v.AuxInt = 16
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVload, config.fe.TypeUInt64())
+		v0.AuxInt = 16
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVVstore, TypeMem)
+		v1.AuxInt = 8
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVload, config.fe.TypeUInt64())
+		v2.AuxInt = 8
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVVstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVVload, config.fe.TypeUInt64())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 24 || SizeAndAlign(s).Align()%8 != 0
+	// result: (LoweredMove [SizeAndAlign(s).Align()] 		dst 		src 		(ADDVconst <src.Type> src [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)]) 		mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 24 || SizeAndAlign(s).Align()%8 != 0) {
+			break
+		}
+		v.reset(OpMIPS64LoweredMove)
+		v.AuxInt = SizeAndAlign(s).Align()
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, OpMIPS64ADDVconst, src.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - moveSize(SizeAndAlign(s).Align(), config)
+		v0.AddArg(src)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpMul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul16 x y)
+	// cond:
+	// result: (Select1 (MULVU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64MULVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32 x y)
+	// cond:
+	// result: (Select1 (MULVU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64MULVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMul32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32F x y)
+	// cond:
+	// result: (MULF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64MULF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64 x y)
+	// cond:
+	// result: (Select1 (MULVU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64MULVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMul64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64F x y)
+	// cond:
+	// result: (MULD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64MULD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpMul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul8 x y)
+	// cond:
+	// result: (Select1 (MULVU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpSelect1)
+		v0 := b.NewValue0(v.Line, OpMIPS64MULVU, MakeTuple(config.fe.TypeUInt64(), config.fe.TypeUInt64()))
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeg16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg16 x)
+	// cond:
+	// result: (NEGV x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64NEGV)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeg32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32 x)
+	// cond:
+	// result: (NEGV x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64NEGV)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeg32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32F x)
+	// cond:
+	// result: (NEGF x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64NEGF)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeg64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64 x)
+	// cond:
+	// result: (NEGV x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64NEGV)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeg64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64F x)
+	// cond:
+	// result: (NEGD x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64NEGD)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeg8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg8 x)
+	// cond:
+	// result: (NEGV x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64NEGV)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq16 x y)
+	// cond:
+	// result: (SGTU (XOR (ZeroExt16to32 x) (ZeroExt16to64 y)) (MOVVconst [0]))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeUInt64())
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32 x y)
+	// cond:
+	// result: (SGTU (XOR (ZeroExt32to64 x) (ZeroExt32to64 y)) (MOVVconst [0]))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeUInt64())
+		v1 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32F x y)
+	// cond:
+	// result: (FPFlagFalse (CMPEQF x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagFalse)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPEQF, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64 x y)
+	// cond:
+	// result: (SGTU (XOR x y) (MOVVconst [0]))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64F x y)
+	// cond:
+	// result: (FPFlagFalse (CMPEQD x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64FPFlagFalse)
+		v0 := b.NewValue0(v.Line, OpMIPS64CMPEQD, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq8 x y)
+	// cond:
+	// result: (SGTU (XOR (ZeroExt8to64 x) (ZeroExt8to64 y)) (MOVVconst [0]))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeUInt64())
+		v1 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v3.AuxInt = 0
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqB x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNeqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqPtr x y)
+	// cond:
+	// result: (SGTU (XOR x y) (MOVVconst [0]))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SGTU)
+		v0 := b.NewValue0(v.Line, OpMIPS64XOR, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v1.AuxInt = 0
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNilCheck(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NilCheck ptr mem)
+	// cond:
+	// result: (LoweredNilCheck ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpMIPS64LoweredNilCheck)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpNot(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Not x)
+	// cond:
+	// result: (XORconst [1] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64XORconst)
+		v.AuxInt = 1
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpOffPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OffPtr [off] ptr:(SP))
+	// cond:
+	// result: (MOVVaddr [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		if ptr.Op != OpSP {
+			break
+		}
+		v.reset(OpMIPS64MOVVaddr)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+	// match: (OffPtr [off] ptr)
+	// cond:
+	// result: (ADDVconst [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		v.reset(OpMIPS64ADDVconst)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpOr16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or16 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpOr32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or32 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpOr64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or64 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or8 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpOrB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OrB x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh16Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux16 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SRLV <t> (ZeroExt16to64 x) (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh16Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux32 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SRLV <t> (ZeroExt16to64 x) (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh16Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux64 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SRLV <t> (ZeroExt16to64 x) y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh16Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux8  <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SRLV <t> (ZeroExt16to64 x) (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x16 <t> x y)
+	// cond:
+	// result: (SRAV (SignExt16to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt16to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v5.AuxInt = 63
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v6 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v1.AddArg(v6)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x32 <t> x y)
+	// cond:
+	// result: (SRAV (SignExt16to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt32to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v5.AuxInt = 63
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v6 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v1.AddArg(v6)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x64 <t> x y)
+	// cond:
+	// result: (SRAV (SignExt16to64 x) (OR <t> (NEGV <t> (SGTU y (Const64 <config.fe.TypeUInt64()> [63]))) y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v3.AddArg(y)
+		v4 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v4.AuxInt = 63
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x8  <t> x y)
+	// cond:
+	// result: (SRAV (SignExt16to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt8to64  y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt16to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v5.AuxInt = 63
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v6 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v1.AddArg(v6)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh32Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux16 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SRLV <t> (ZeroExt32to64 x) (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh32Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux32 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SRLV <t> (ZeroExt32to64 x) (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh32Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux64 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SRLV <t> (ZeroExt32to64 x) y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh32Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux8  <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SRLV <t> (ZeroExt32to64 x) (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x16 <t> x y)
+	// cond:
+	// result: (SRAV (SignExt32to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt16to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v5.AuxInt = 63
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v6 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v1.AddArg(v6)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x32 <t> x y)
+	// cond:
+	// result: (SRAV (SignExt32to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt32to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v5.AuxInt = 63
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v6 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v1.AddArg(v6)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x64 <t> x y)
+	// cond:
+	// result: (SRAV (SignExt32to64 x) (OR <t> (NEGV <t> (SGTU y (Const64 <config.fe.TypeUInt64()> [63]))) y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v3.AddArg(y)
+		v4 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v4.AuxInt = 63
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x8  <t> x y)
+	// cond:
+	// result: (SRAV (SignExt32to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt8to64  y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v5.AuxInt = 63
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v6 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v1.AddArg(v6)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh64Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux16 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SRLV <t> x (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh64Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux32 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SRLV <t> x (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh64Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux64 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SRLV <t> x y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v3.AddArg(x)
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh64Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux8  <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SRLV <t> x (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v4.AddArg(x)
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x16 <t> x y)
+	// cond:
+	// result: (SRAV x (OR <t> (NEGV <t> (SGTU (ZeroExt16to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v4.AuxInt = 63
+		v2.AddArg(v4)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v5 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v0.AddArg(v5)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x32 <t> x y)
+	// cond:
+	// result: (SRAV x (OR <t> (NEGV <t> (SGTU (ZeroExt32to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v4.AuxInt = 63
+		v2.AddArg(v4)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v5 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v0.AddArg(v5)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh64x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x64 <t> x y)
+	// cond:
+	// result: (SRAV x (OR <t> (NEGV <t> (SGTU y (Const64 <config.fe.TypeUInt64()> [63]))) y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2.AddArg(y)
+		v3 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v3.AuxInt = 63
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x8  <t> x y)
+	// cond:
+	// result: (SRAV x (OR <t> (NEGV <t> (SGTU (ZeroExt8to64  y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v4.AuxInt = 63
+		v2.AddArg(v4)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v0.AddArg(v5)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh8Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux16 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt16to64 y))) (SRLV <t> (ZeroExt8to64 x) (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh8Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux32 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt32to64 y))) (SRLV <t> (ZeroExt8to64 x) (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh8Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux64 <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) y)) (SRLV <t> (ZeroExt8to64 x) y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v3.AddArg(y)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh8Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux8  <t> x y)
+	// cond:
+	// result: (AND (NEGV <t> (SGTU (Const64 <config.fe.TypeUInt64()> [64]) (ZeroExt8to64  y))) (SRLV <t> (ZeroExt8to64 x) (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64AND)
+		v0 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v1 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v2 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v2.AuxInt = 64
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v4 := b.NewValue0(v.Line, OpMIPS64SRLV, t)
+		v5 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v.AddArg(v4)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x16 <t> x y)
+	// cond:
+	// result: (SRAV (SignExt8to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt16to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt16to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v5.AuxInt = 63
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v6 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v1.AddArg(v6)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x32 <t> x y)
+	// cond:
+	// result: (SRAV (SignExt8to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt32to64 y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt32to64 y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v5.AuxInt = 63
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v6 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v1.AddArg(v6)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x64 <t> x y)
+	// cond:
+	// result: (SRAV (SignExt8to64 x) (OR <t> (NEGV <t> (SGTU y (Const64 <config.fe.TypeUInt64()> [63]))) y))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v3.AddArg(y)
+		v4 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v4.AuxInt = 63
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpRsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x8  <t> x y)
+	// cond:
+	// result: (SRAV (SignExt8to64 x) (OR <t> (NEGV <t> (SGTU (ZeroExt8to64  y) (Const64 <config.fe.TypeUInt64()> [63]))) (ZeroExt8to64  y)))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SRAV)
+		v0 := b.NewValue0(v.Line, OpSignExt8to64, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64OR, t)
+		v2 := b.NewValue0(v.Line, OpMIPS64NEGV, t)
+		v3 := b.NewValue0(v.Line, OpMIPS64SGTU, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpConst64, config.fe.TypeUInt64())
+		v5.AuxInt = 63
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v6 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v6.AddArg(y)
+		v1.AddArg(v6)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSelect0(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Select0 (DIVVU _ (MOVVconst [1])))
+	// cond:
+	// result: (MOVVconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64DIVVU {
+			break
+		}
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_0_1.AuxInt != 1 {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Select0 (DIVVU x (MOVVconst [c])))
+	// cond: isPowerOfTwo(c)
+	// result: (ANDconst [c-1] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64DIVVU {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0_1.AuxInt
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpMIPS64ANDconst)
+		v.AuxInt = c - 1
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select0 (DIVV  (MOVVconst [c]) (MOVVconst [d])))
+	// cond:
+	// result: (MOVVconst [int64(c)%int64(d)])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64DIVV {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(c) % int64(d)
+		return true
+	}
+	// match: (Select0 (DIVVU (MOVVconst [c]) (MOVVconst [d])))
+	// cond:
+	// result: (MOVVconst [int64(uint64(c)%uint64(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64DIVVU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(uint64(c) % uint64(d))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpSelect1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Select1 (MULVU x (MOVVconst [-1])))
+	// cond:
+	// result: (NEGV x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MULVU {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_0_1.AuxInt != -1 {
+			break
+		}
+		v.reset(OpMIPS64NEGV)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (MULVU _ (MOVVconst [0])))
+	// cond:
+	// result: (MOVVconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MULVU {
+			break
+		}
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_0_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Select1 (MULVU x (MOVVconst [1])))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MULVU {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_0_1.AuxInt != 1 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (MULVU x (MOVVconst [c])))
+	// cond: isPowerOfTwo(c)
+	// result: (SLLVconst [log2(c)] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MULVU {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0_1.AuxInt
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpMIPS64SLLVconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (MULVU (MOVVconst [-1]) x))
+	// cond:
+	// result: (NEGV x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MULVU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_0_0.AuxInt != -1 {
+			break
+		}
+		x := v_0.Args[1]
+		v.reset(OpMIPS64NEGV)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (MULVU (MOVVconst [0]) _))
+	// cond:
+	// result: (MOVVconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MULVU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_0_0.AuxInt != 0 {
+			break
+		}
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Select1 (MULVU (MOVVconst [1]) x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MULVU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_0_0.AuxInt != 1 {
+			break
+		}
+		x := v_0.Args[1]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (MULVU (MOVVconst [c]) x))
+	// cond: isPowerOfTwo(c)
+	// result: (SLLVconst [log2(c)] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MULVU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		x := v_0.Args[1]
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpMIPS64SLLVconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (DIVVU x (MOVVconst [1])))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64DIVVU {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		if v_0_1.AuxInt != 1 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (DIVVU x (MOVVconst [c])))
+	// cond: isPowerOfTwo(c)
+	// result: (SRLVconst [log2(c)] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64DIVVU {
+			break
+		}
+		x := v_0.Args[0]
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0_1.AuxInt
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpMIPS64SRLVconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Select1 (MULVU (MOVVconst [c]) (MOVVconst [d])))
+	// cond:
+	// result: (MOVVconst [c*d])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64MULVU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = c * d
+		return true
+	}
+	// match: (Select1 (DIVV  (MOVVconst [c]) (MOVVconst [d])))
+	// cond:
+	// result: (MOVVconst [int64(c)/int64(d)])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64DIVV {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(c) / int64(d)
+		return true
+	}
+	// match: (Select1 (DIVVU (MOVVconst [c]) (MOVVconst [d])))
+	// cond:
+	// result: (MOVVconst [int64(uint64(c)/uint64(d))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpMIPS64DIVVU {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpMIPS64MOVVconst {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpMIPS64MOVVconst {
+			break
+		}
+		d := v_0_1.AuxInt
+		v.reset(OpMIPS64MOVVconst)
+		v.AuxInt = int64(uint64(c) / uint64(d))
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpSignExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to32 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSignExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to64 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSignExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt32to64 x)
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVWreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSignExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to16 x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSignExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to32 x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSignExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to64 x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSlicemask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Slicemask <t> x)
+	// cond:
+	// result: (NORconst [0] (SRAVconst <t> (SUBVconst <t> x [1]) [63]))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpMIPS64NORconst)
+		v.AuxInt = 0
+		v0 := b.NewValue0(v.Line, OpMIPS64SRAVconst, t)
+		v0.AuxInt = 63
+		v1 := b.NewValue0(v.Line, OpMIPS64SUBVconst, t)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpStaticCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (StaticCall [argwid] {target} mem)
+	// cond:
+	// result: (CALLstatic [argwid] {target} mem)
+	for {
+		argwid := v.AuxInt
+		target := v.Aux
+		mem := v.Args[0]
+		v.reset(OpMIPS64CALLstatic)
+		v.AuxInt = argwid
+		v.Aux = target
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpStore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Store [1] ptr val mem)
+	// cond:
+	// result: (MOVBstore ptr val mem)
+	for {
+		if v.AuxInt != 1 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVBstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [2] ptr val mem)
+	// cond:
+	// result: (MOVHstore ptr val mem)
+	for {
+		if v.AuxInt != 2 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpMIPS64MOVHstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: !is32BitFloat(val.Type)
+	// result: (MOVWstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(!is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [8] ptr val mem)
+	// cond: !is64BitFloat(val.Type)
+	// result: (MOVVstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(!is64BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: is32BitFloat(val.Type)
+	// result: (MOVFstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpMIPS64MOVFstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [8] ptr val mem)
+	// cond: is64BitFloat(val.Type)
+	// result: (MOVDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is64BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpMIPS64MOVDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpSub16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub16 x y)
+	// cond:
+	// result: (SUBV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SUBV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSub32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32 x y)
+	// cond:
+	// result: (SUBV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SUBV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSub32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32F x y)
+	// cond:
+	// result: (SUBF x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SUBF)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSub64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64 x y)
+	// cond:
+	// result: (SUBV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SUBV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSub64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64F x y)
+	// cond:
+	// result: (SUBD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SUBD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSub8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub8 x y)
+	// cond:
+	// result: (SUBV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SUBV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpSubPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SubPtr x y)
+	// cond:
+	// result: (SUBV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64SUBV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpTrunc16to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc16to8 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpTrunc32to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to16 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpTrunc32to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to8 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpTrunc64to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to16 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpTrunc64to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to32 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpTrunc64to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to8 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpXor16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor16 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpXor32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor32 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpXor64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor64 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpXor8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor8 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMIPS64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpZero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zero [s] _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore ptr (MOVVconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore ptr (MOVVconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVBstore [1] ptr (MOVVconst [0]) 		(MOVBstore [0] ptr (MOVVconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = 1
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore ptr (MOVVconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [2] ptr (MOVVconst [0]) 		(MOVHstore [0] ptr (MOVVconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = 2
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVBstore [3] ptr (MOVVconst [0]) 		(MOVBstore [2] ptr (MOVVconst [0]) 			(MOVBstore [1] ptr (MOVVconst [0]) 				(MOVBstore [0] ptr (MOVVconst [0]) mem))))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = 3
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v3.AuxInt = 1
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v5.AuxInt = 0
+		v5.AddArg(ptr)
+		v6 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v6.AuxInt = 0
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVVstore ptr (MOVVconst [0]) mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstore)
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [4] ptr (MOVVconst [0]) 		(MOVWstore [0] ptr (MOVVconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstore)
+		v.AuxInt = 4
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVWstore, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVHstore [6] ptr (MOVVconst [0]) 		(MOVHstore [4] ptr (MOVVconst [0]) 			(MOVHstore [2] ptr (MOVVconst [0]) 				(MOVHstore [0] ptr (MOVVconst [0]) mem))))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = 6
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v3.AuxInt = 2
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v5.AuxInt = 0
+		v5.AddArg(ptr)
+		v6 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v6.AuxInt = 0
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] ptr (MOVVconst [0]) 		(MOVBstore [1] ptr (MOVVconst [0]) 			(MOVBstore [0] ptr (MOVVconst [0]) mem)))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpMIPS64MOVBstore)
+		v.AuxInt = 2
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v1.AuxInt = 1
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVBstore, TypeMem)
+		v3.AuxInt = 0
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [4] ptr (MOVVconst [0]) 		(MOVHstore [2] ptr (MOVVconst [0]) 			(MOVHstore [0] ptr (MOVVconst [0]) mem)))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 6 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVHstore)
+		v.AuxInt = 4
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVHstore, TypeMem)
+		v3.AuxInt = 0
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [8] ptr (MOVVconst [0]) 		(MOVWstore [4] ptr (MOVVconst [0]) 			(MOVWstore [0] ptr (MOVVconst [0]) mem)))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 12 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVWstore)
+		v.AuxInt = 8
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVWstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVWstore, TypeMem)
+		v3.AuxInt = 0
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVVstore [8] ptr (MOVVconst [0]) 		(MOVVstore [0] ptr (MOVVconst [0]) mem))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstore)
+		v.AuxInt = 8
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVVstore, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size() == 24 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVVstore [16] ptr (MOVVconst [0]) 		(MOVVstore [8] ptr (MOVVconst [0]) 			(MOVVstore [0] ptr (MOVVconst [0]) mem)))
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 24 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpMIPS64MOVVstore)
+		v.AuxInt = 16
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpMIPS64MOVVstore, TypeMem)
+		v1.AuxInt = 8
+		v1.AddArg(ptr)
+		v2 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v2.AuxInt = 0
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpMIPS64MOVVstore, TypeMem)
+		v3.AuxInt = 0
+		v3.AddArg(ptr)
+		v4 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
+		v4.AuxInt = 0
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size() <= 8*128 	&& SizeAndAlign(s).Align()%8 == 0 && !config.noDuffDevice
+	// result: (DUFFZERO [8 * (128 - int64(SizeAndAlign(s).Size()/8))] ptr mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size()%8 == 0 && SizeAndAlign(s).Size() > 24 && SizeAndAlign(s).Size() <= 8*128 && SizeAndAlign(s).Align()%8 == 0 && !config.noDuffDevice) {
+			break
+		}
+		v.reset(OpMIPS64DUFFZERO)
+		v.AuxInt = 8 * (128 - int64(SizeAndAlign(s).Size()/8))
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: (SizeAndAlign(s).Size() > 8*128 || config.noDuffDevice) || SizeAndAlign(s).Align()%8 != 0
+	// result: (LoweredZero [SizeAndAlign(s).Align()] 		ptr 		(ADDVconst <ptr.Type> ptr [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)]) 		mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !((SizeAndAlign(s).Size() > 8*128 || config.noDuffDevice) || SizeAndAlign(s).Align()%8 != 0) {
+			break
+		}
+		v.reset(OpMIPS64LoweredZero)
+		v.AuxInt = SizeAndAlign(s).Align()
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpMIPS64ADDVconst, ptr.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - moveSize(SizeAndAlign(s).Align(), config)
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueMIPS64_OpZeroExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to32 x)
+	// cond:
+	// result: (MOVHUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVHUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpZeroExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to64 x)
+	// cond:
+	// result: (MOVHUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVHUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpZeroExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt32to64 x)
+	// cond:
+	// result: (MOVWUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVWUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpZeroExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to16 x)
+	// cond:
+	// result: (MOVBUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVBUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpZeroExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to32 x)
+	// cond:
+	// result: (MOVBUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVBUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueMIPS64_OpZeroExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to64 x)
+	// cond:
+	// result: (MOVBUreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpMIPS64MOVBUreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteBlockMIPS64(b *Block, config *Config) bool {
+	switch b.Kind {
+	case BlockMIPS64EQ:
+		// match: (EQ (FPFlagTrue cmp) yes no)
+		// cond:
+		// result: (FPF cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64FPFlagTrue {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64FPF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FPFlagFalse cmp) yes no)
+		// cond:
+		// result: (FPT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64FPFlagFalse {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64FPT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (XORconst [1] cmp:(SGT _ _)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64XORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPS64SGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64NE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (XORconst [1] cmp:(SGTU _ _)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64XORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPS64SGTU {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64NE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (XORconst [1] cmp:(SGTconst _)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64XORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPS64SGTconst {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64NE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (XORconst [1] cmp:(SGTUconst _)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64XORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPS64SGTUconst {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64NE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (SGTUconst [1] x) yes no)
+		// cond:
+		// result: (NE x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64SGTUconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64NE
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (SGTU x (MOVVconst [0])) yes no)
+		// cond:
+		// result: (EQ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64SGTU {
+				break
+			}
+			x := v.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != OpMIPS64MOVVconst {
+				break
+			}
+			if v_1.AuxInt != 0 {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64EQ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (SGTconst [0] x) yes no)
+		// cond:
+		// result: (GEZ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64SGTconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64GEZ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (SGT x (MOVVconst [0])) yes no)
+		// cond:
+		// result: (LEZ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64SGT {
+				break
+			}
+			x := v.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != OpMIPS64MOVVconst {
+				break
+			}
+			if v_1.AuxInt != 0 {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64LEZ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ  (MOVVconst [0]) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ  (MOVVconst [c]) yes no)
+		// cond: c != 0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c != 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockMIPS64GEZ:
+		// match: (GEZ (MOVVconst [c]) yes no)
+		// cond: c >= 0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c >= 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GEZ (MOVVconst [c]) yes no)
+		// cond: c <  0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c < 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockMIPS64GTZ:
+		// match: (GTZ (MOVVconst [c]) yes no)
+		// cond: c >  0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c > 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GTZ (MOVVconst [c]) yes no)
+		// cond: c <= 0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c <= 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockIf:
+		// match: (If cond yes no)
+		// cond:
+		// result: (NE cond yes no)
+		for {
+			v := b.Control
+			_ = v
+			cond := b.Control
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64NE
+			b.SetControl(cond)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockMIPS64LEZ:
+		// match: (LEZ (MOVVconst [c]) yes no)
+		// cond: c <= 0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c <= 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LEZ (MOVVconst [c]) yes no)
+		// cond: c >  0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c > 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockMIPS64LTZ:
+		// match: (LTZ (MOVVconst [c]) yes no)
+		// cond: c <  0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c < 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LTZ (MOVVconst [c]) yes no)
+		// cond: c >= 0
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c >= 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockMIPS64NE:
+		// match: (NE (FPFlagTrue cmp) yes no)
+		// cond:
+		// result: (FPT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64FPFlagTrue {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64FPT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FPFlagFalse cmp) yes no)
+		// cond:
+		// result: (FPF cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64FPFlagFalse {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64FPF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (XORconst [1] cmp:(SGT _ _)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64XORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPS64SGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64EQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (XORconst [1] cmp:(SGTU _ _)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64XORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPS64SGTU {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64EQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (XORconst [1] cmp:(SGTconst _)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64XORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPS64SGTconst {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64EQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (XORconst [1] cmp:(SGTUconst _)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64XORconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[0]
+			if cmp.Op != OpMIPS64SGTUconst {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64EQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (SGTUconst [1] x) yes no)
+		// cond:
+		// result: (EQ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64SGTUconst {
+				break
+			}
+			if v.AuxInt != 1 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64EQ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (SGTU x (MOVVconst [0])) yes no)
+		// cond:
+		// result: (NE x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64SGTU {
+				break
+			}
+			x := v.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != OpMIPS64MOVVconst {
+				break
+			}
+			if v_1.AuxInt != 0 {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64NE
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (SGTconst [0] x) yes no)
+		// cond:
+		// result: (LTZ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64SGTconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			x := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64LTZ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (SGT x (MOVVconst [0])) yes no)
+		// cond:
+		// result: (GTZ x yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64SGT {
+				break
+			}
+			x := v.Args[0]
+			v_1 := v.Args[1]
+			if v_1.Op != OpMIPS64MOVVconst {
+				break
+			}
+			if v_1.AuxInt != 0 {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockMIPS64GTZ
+			b.SetControl(x)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE  (MOVVconst [0]) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (NE  (MOVVconst [c]) yes no)
+		// cond: c != 0
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpMIPS64MOVVconst {
+				break
+			}
+			c := v.AuxInt
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			if !(c != 0) {
+				break
+			}
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
new file mode 100644
index 0000000..8c8373b
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -0,0 +1,10848 @@
+// autogenerated from gen/PPC64.rules: do not edit!
+// generated with: cd gen; go run *.go
+
+package ssa
+
+import "math"
+
+var _ = math.MinInt8 // in case not otherwise used
+func rewriteValuePPC64(v *Value, config *Config) bool {
+	switch v.Op {
+	case OpAdd16:
+		return rewriteValuePPC64_OpAdd16(v, config)
+	case OpAdd32:
+		return rewriteValuePPC64_OpAdd32(v, config)
+	case OpAdd32F:
+		return rewriteValuePPC64_OpAdd32F(v, config)
+	case OpAdd64:
+		return rewriteValuePPC64_OpAdd64(v, config)
+	case OpAdd64F:
+		return rewriteValuePPC64_OpAdd64F(v, config)
+	case OpAdd8:
+		return rewriteValuePPC64_OpAdd8(v, config)
+	case OpAddPtr:
+		return rewriteValuePPC64_OpAddPtr(v, config)
+	case OpAddr:
+		return rewriteValuePPC64_OpAddr(v, config)
+	case OpAnd16:
+		return rewriteValuePPC64_OpAnd16(v, config)
+	case OpAnd32:
+		return rewriteValuePPC64_OpAnd32(v, config)
+	case OpAnd64:
+		return rewriteValuePPC64_OpAnd64(v, config)
+	case OpAnd8:
+		return rewriteValuePPC64_OpAnd8(v, config)
+	case OpAndB:
+		return rewriteValuePPC64_OpAndB(v, config)
+	case OpAvg64u:
+		return rewriteValuePPC64_OpAvg64u(v, config)
+	case OpClosureCall:
+		return rewriteValuePPC64_OpClosureCall(v, config)
+	case OpCom16:
+		return rewriteValuePPC64_OpCom16(v, config)
+	case OpCom32:
+		return rewriteValuePPC64_OpCom32(v, config)
+	case OpCom64:
+		return rewriteValuePPC64_OpCom64(v, config)
+	case OpCom8:
+		return rewriteValuePPC64_OpCom8(v, config)
+	case OpConst16:
+		return rewriteValuePPC64_OpConst16(v, config)
+	case OpConst32:
+		return rewriteValuePPC64_OpConst32(v, config)
+	case OpConst32F:
+		return rewriteValuePPC64_OpConst32F(v, config)
+	case OpConst64:
+		return rewriteValuePPC64_OpConst64(v, config)
+	case OpConst64F:
+		return rewriteValuePPC64_OpConst64F(v, config)
+	case OpConst8:
+		return rewriteValuePPC64_OpConst8(v, config)
+	case OpConstBool:
+		return rewriteValuePPC64_OpConstBool(v, config)
+	case OpConstNil:
+		return rewriteValuePPC64_OpConstNil(v, config)
+	case OpConvert:
+		return rewriteValuePPC64_OpConvert(v, config)
+	case OpCvt32Fto32:
+		return rewriteValuePPC64_OpCvt32Fto32(v, config)
+	case OpCvt32Fto64:
+		return rewriteValuePPC64_OpCvt32Fto64(v, config)
+	case OpCvt32Fto64F:
+		return rewriteValuePPC64_OpCvt32Fto64F(v, config)
+	case OpCvt32to32F:
+		return rewriteValuePPC64_OpCvt32to32F(v, config)
+	case OpCvt32to64F:
+		return rewriteValuePPC64_OpCvt32to64F(v, config)
+	case OpCvt64Fto32:
+		return rewriteValuePPC64_OpCvt64Fto32(v, config)
+	case OpCvt64Fto32F:
+		return rewriteValuePPC64_OpCvt64Fto32F(v, config)
+	case OpCvt64Fto64:
+		return rewriteValuePPC64_OpCvt64Fto64(v, config)
+	case OpCvt64to32F:
+		return rewriteValuePPC64_OpCvt64to32F(v, config)
+	case OpCvt64to64F:
+		return rewriteValuePPC64_OpCvt64to64F(v, config)
+	case OpDeferCall:
+		return rewriteValuePPC64_OpDeferCall(v, config)
+	case OpDiv16:
+		return rewriteValuePPC64_OpDiv16(v, config)
+	case OpDiv16u:
+		return rewriteValuePPC64_OpDiv16u(v, config)
+	case OpDiv32:
+		return rewriteValuePPC64_OpDiv32(v, config)
+	case OpDiv32F:
+		return rewriteValuePPC64_OpDiv32F(v, config)
+	case OpDiv32u:
+		return rewriteValuePPC64_OpDiv32u(v, config)
+	case OpDiv64:
+		return rewriteValuePPC64_OpDiv64(v, config)
+	case OpDiv64F:
+		return rewriteValuePPC64_OpDiv64F(v, config)
+	case OpDiv64u:
+		return rewriteValuePPC64_OpDiv64u(v, config)
+	case OpDiv8:
+		return rewriteValuePPC64_OpDiv8(v, config)
+	case OpDiv8u:
+		return rewriteValuePPC64_OpDiv8u(v, config)
+	case OpEq16:
+		return rewriteValuePPC64_OpEq16(v, config)
+	case OpEq32:
+		return rewriteValuePPC64_OpEq32(v, config)
+	case OpEq32F:
+		return rewriteValuePPC64_OpEq32F(v, config)
+	case OpEq64:
+		return rewriteValuePPC64_OpEq64(v, config)
+	case OpEq64F:
+		return rewriteValuePPC64_OpEq64F(v, config)
+	case OpEq8:
+		return rewriteValuePPC64_OpEq8(v, config)
+	case OpEqB:
+		return rewriteValuePPC64_OpEqB(v, config)
+	case OpEqPtr:
+		return rewriteValuePPC64_OpEqPtr(v, config)
+	case OpGeq16:
+		return rewriteValuePPC64_OpGeq16(v, config)
+	case OpGeq16U:
+		return rewriteValuePPC64_OpGeq16U(v, config)
+	case OpGeq32:
+		return rewriteValuePPC64_OpGeq32(v, config)
+	case OpGeq32F:
+		return rewriteValuePPC64_OpGeq32F(v, config)
+	case OpGeq32U:
+		return rewriteValuePPC64_OpGeq32U(v, config)
+	case OpGeq64:
+		return rewriteValuePPC64_OpGeq64(v, config)
+	case OpGeq64F:
+		return rewriteValuePPC64_OpGeq64F(v, config)
+	case OpGeq64U:
+		return rewriteValuePPC64_OpGeq64U(v, config)
+	case OpGeq8:
+		return rewriteValuePPC64_OpGeq8(v, config)
+	case OpGeq8U:
+		return rewriteValuePPC64_OpGeq8U(v, config)
+	case OpGetClosurePtr:
+		return rewriteValuePPC64_OpGetClosurePtr(v, config)
+	case OpGoCall:
+		return rewriteValuePPC64_OpGoCall(v, config)
+	case OpGreater16:
+		return rewriteValuePPC64_OpGreater16(v, config)
+	case OpGreater16U:
+		return rewriteValuePPC64_OpGreater16U(v, config)
+	case OpGreater32:
+		return rewriteValuePPC64_OpGreater32(v, config)
+	case OpGreater32F:
+		return rewriteValuePPC64_OpGreater32F(v, config)
+	case OpGreater32U:
+		return rewriteValuePPC64_OpGreater32U(v, config)
+	case OpGreater64:
+		return rewriteValuePPC64_OpGreater64(v, config)
+	case OpGreater64F:
+		return rewriteValuePPC64_OpGreater64F(v, config)
+	case OpGreater64U:
+		return rewriteValuePPC64_OpGreater64U(v, config)
+	case OpGreater8:
+		return rewriteValuePPC64_OpGreater8(v, config)
+	case OpGreater8U:
+		return rewriteValuePPC64_OpGreater8U(v, config)
+	case OpHmul16:
+		return rewriteValuePPC64_OpHmul16(v, config)
+	case OpHmul16u:
+		return rewriteValuePPC64_OpHmul16u(v, config)
+	case OpHmul32:
+		return rewriteValuePPC64_OpHmul32(v, config)
+	case OpHmul32u:
+		return rewriteValuePPC64_OpHmul32u(v, config)
+	case OpHmul64:
+		return rewriteValuePPC64_OpHmul64(v, config)
+	case OpHmul64u:
+		return rewriteValuePPC64_OpHmul64u(v, config)
+	case OpHmul8:
+		return rewriteValuePPC64_OpHmul8(v, config)
+	case OpHmul8u:
+		return rewriteValuePPC64_OpHmul8u(v, config)
+	case OpInterCall:
+		return rewriteValuePPC64_OpInterCall(v, config)
+	case OpIsInBounds:
+		return rewriteValuePPC64_OpIsInBounds(v, config)
+	case OpIsNonNil:
+		return rewriteValuePPC64_OpIsNonNil(v, config)
+	case OpIsSliceInBounds:
+		return rewriteValuePPC64_OpIsSliceInBounds(v, config)
+	case OpLeq16:
+		return rewriteValuePPC64_OpLeq16(v, config)
+	case OpLeq16U:
+		return rewriteValuePPC64_OpLeq16U(v, config)
+	case OpLeq32:
+		return rewriteValuePPC64_OpLeq32(v, config)
+	case OpLeq32F:
+		return rewriteValuePPC64_OpLeq32F(v, config)
+	case OpLeq32U:
+		return rewriteValuePPC64_OpLeq32U(v, config)
+	case OpLeq64:
+		return rewriteValuePPC64_OpLeq64(v, config)
+	case OpLeq64F:
+		return rewriteValuePPC64_OpLeq64F(v, config)
+	case OpLeq64U:
+		return rewriteValuePPC64_OpLeq64U(v, config)
+	case OpLeq8:
+		return rewriteValuePPC64_OpLeq8(v, config)
+	case OpLeq8U:
+		return rewriteValuePPC64_OpLeq8U(v, config)
+	case OpLess16:
+		return rewriteValuePPC64_OpLess16(v, config)
+	case OpLess16U:
+		return rewriteValuePPC64_OpLess16U(v, config)
+	case OpLess32:
+		return rewriteValuePPC64_OpLess32(v, config)
+	case OpLess32F:
+		return rewriteValuePPC64_OpLess32F(v, config)
+	case OpLess32U:
+		return rewriteValuePPC64_OpLess32U(v, config)
+	case OpLess64:
+		return rewriteValuePPC64_OpLess64(v, config)
+	case OpLess64F:
+		return rewriteValuePPC64_OpLess64F(v, config)
+	case OpLess64U:
+		return rewriteValuePPC64_OpLess64U(v, config)
+	case OpLess8:
+		return rewriteValuePPC64_OpLess8(v, config)
+	case OpLess8U:
+		return rewriteValuePPC64_OpLess8U(v, config)
+	case OpLoad:
+		return rewriteValuePPC64_OpLoad(v, config)
+	case OpLsh16x16:
+		return rewriteValuePPC64_OpLsh16x16(v, config)
+	case OpLsh16x32:
+		return rewriteValuePPC64_OpLsh16x32(v, config)
+	case OpLsh16x64:
+		return rewriteValuePPC64_OpLsh16x64(v, config)
+	case OpLsh16x8:
+		return rewriteValuePPC64_OpLsh16x8(v, config)
+	case OpLsh32x16:
+		return rewriteValuePPC64_OpLsh32x16(v, config)
+	case OpLsh32x32:
+		return rewriteValuePPC64_OpLsh32x32(v, config)
+	case OpLsh32x64:
+		return rewriteValuePPC64_OpLsh32x64(v, config)
+	case OpLsh32x8:
+		return rewriteValuePPC64_OpLsh32x8(v, config)
+	case OpLsh64x16:
+		return rewriteValuePPC64_OpLsh64x16(v, config)
+	case OpLsh64x32:
+		return rewriteValuePPC64_OpLsh64x32(v, config)
+	case OpLsh64x64:
+		return rewriteValuePPC64_OpLsh64x64(v, config)
+	case OpLsh64x8:
+		return rewriteValuePPC64_OpLsh64x8(v, config)
+	case OpLsh8x16:
+		return rewriteValuePPC64_OpLsh8x16(v, config)
+	case OpLsh8x32:
+		return rewriteValuePPC64_OpLsh8x32(v, config)
+	case OpLsh8x64:
+		return rewriteValuePPC64_OpLsh8x64(v, config)
+	case OpLsh8x8:
+		return rewriteValuePPC64_OpLsh8x8(v, config)
+	case OpMod16:
+		return rewriteValuePPC64_OpMod16(v, config)
+	case OpMod16u:
+		return rewriteValuePPC64_OpMod16u(v, config)
+	case OpMod32:
+		return rewriteValuePPC64_OpMod32(v, config)
+	case OpMod32u:
+		return rewriteValuePPC64_OpMod32u(v, config)
+	case OpMod64:
+		return rewriteValuePPC64_OpMod64(v, config)
+	case OpMod64u:
+		return rewriteValuePPC64_OpMod64u(v, config)
+	case OpMod8:
+		return rewriteValuePPC64_OpMod8(v, config)
+	case OpMod8u:
+		return rewriteValuePPC64_OpMod8u(v, config)
+	case OpMove:
+		return rewriteValuePPC64_OpMove(v, config)
+	case OpMul16:
+		return rewriteValuePPC64_OpMul16(v, config)
+	case OpMul32:
+		return rewriteValuePPC64_OpMul32(v, config)
+	case OpMul32F:
+		return rewriteValuePPC64_OpMul32F(v, config)
+	case OpMul64:
+		return rewriteValuePPC64_OpMul64(v, config)
+	case OpMul64F:
+		return rewriteValuePPC64_OpMul64F(v, config)
+	case OpMul8:
+		return rewriteValuePPC64_OpMul8(v, config)
+	case OpNeg16:
+		return rewriteValuePPC64_OpNeg16(v, config)
+	case OpNeg32:
+		return rewriteValuePPC64_OpNeg32(v, config)
+	case OpNeg32F:
+		return rewriteValuePPC64_OpNeg32F(v, config)
+	case OpNeg64:
+		return rewriteValuePPC64_OpNeg64(v, config)
+	case OpNeg64F:
+		return rewriteValuePPC64_OpNeg64F(v, config)
+	case OpNeg8:
+		return rewriteValuePPC64_OpNeg8(v, config)
+	case OpNeq16:
+		return rewriteValuePPC64_OpNeq16(v, config)
+	case OpNeq32:
+		return rewriteValuePPC64_OpNeq32(v, config)
+	case OpNeq32F:
+		return rewriteValuePPC64_OpNeq32F(v, config)
+	case OpNeq64:
+		return rewriteValuePPC64_OpNeq64(v, config)
+	case OpNeq64F:
+		return rewriteValuePPC64_OpNeq64F(v, config)
+	case OpNeq8:
+		return rewriteValuePPC64_OpNeq8(v, config)
+	case OpNeqB:
+		return rewriteValuePPC64_OpNeqB(v, config)
+	case OpNeqPtr:
+		return rewriteValuePPC64_OpNeqPtr(v, config)
+	case OpNilCheck:
+		return rewriteValuePPC64_OpNilCheck(v, config)
+	case OpNot:
+		return rewriteValuePPC64_OpNot(v, config)
+	case OpOffPtr:
+		return rewriteValuePPC64_OpOffPtr(v, config)
+	case OpOr16:
+		return rewriteValuePPC64_OpOr16(v, config)
+	case OpOr32:
+		return rewriteValuePPC64_OpOr32(v, config)
+	case OpOr64:
+		return rewriteValuePPC64_OpOr64(v, config)
+	case OpOr8:
+		return rewriteValuePPC64_OpOr8(v, config)
+	case OpOrB:
+		return rewriteValuePPC64_OpOrB(v, config)
+	case OpPPC64ADD:
+		return rewriteValuePPC64_OpPPC64ADD(v, config)
+	case OpPPC64ADDconst:
+		return rewriteValuePPC64_OpPPC64ADDconst(v, config)
+	case OpPPC64AND:
+		return rewriteValuePPC64_OpPPC64AND(v, config)
+	case OpPPC64ANDconst:
+		return rewriteValuePPC64_OpPPC64ANDconst(v, config)
+	case OpPPC64CMP:
+		return rewriteValuePPC64_OpPPC64CMP(v, config)
+	case OpPPC64CMPU:
+		return rewriteValuePPC64_OpPPC64CMPU(v, config)
+	case OpPPC64CMPUconst:
+		return rewriteValuePPC64_OpPPC64CMPUconst(v, config)
+	case OpPPC64CMPW:
+		return rewriteValuePPC64_OpPPC64CMPW(v, config)
+	case OpPPC64CMPWU:
+		return rewriteValuePPC64_OpPPC64CMPWU(v, config)
+	case OpPPC64CMPWUconst:
+		return rewriteValuePPC64_OpPPC64CMPWUconst(v, config)
+	case OpPPC64CMPWconst:
+		return rewriteValuePPC64_OpPPC64CMPWconst(v, config)
+	case OpPPC64CMPconst:
+		return rewriteValuePPC64_OpPPC64CMPconst(v, config)
+	case OpPPC64Equal:
+		return rewriteValuePPC64_OpPPC64Equal(v, config)
+	case OpPPC64FMOVDload:
+		return rewriteValuePPC64_OpPPC64FMOVDload(v, config)
+	case OpPPC64FMOVDstore:
+		return rewriteValuePPC64_OpPPC64FMOVDstore(v, config)
+	case OpPPC64FMOVSload:
+		return rewriteValuePPC64_OpPPC64FMOVSload(v, config)
+	case OpPPC64FMOVSstore:
+		return rewriteValuePPC64_OpPPC64FMOVSstore(v, config)
+	case OpPPC64GreaterEqual:
+		return rewriteValuePPC64_OpPPC64GreaterEqual(v, config)
+	case OpPPC64GreaterThan:
+		return rewriteValuePPC64_OpPPC64GreaterThan(v, config)
+	case OpPPC64LessEqual:
+		return rewriteValuePPC64_OpPPC64LessEqual(v, config)
+	case OpPPC64LessThan:
+		return rewriteValuePPC64_OpPPC64LessThan(v, config)
+	case OpPPC64MOVBZload:
+		return rewriteValuePPC64_OpPPC64MOVBZload(v, config)
+	case OpPPC64MOVBZreg:
+		return rewriteValuePPC64_OpPPC64MOVBZreg(v, config)
+	case OpPPC64MOVBreg:
+		return rewriteValuePPC64_OpPPC64MOVBreg(v, config)
+	case OpPPC64MOVBstore:
+		return rewriteValuePPC64_OpPPC64MOVBstore(v, config)
+	case OpPPC64MOVBstorezero:
+		return rewriteValuePPC64_OpPPC64MOVBstorezero(v, config)
+	case OpPPC64MOVDload:
+		return rewriteValuePPC64_OpPPC64MOVDload(v, config)
+	case OpPPC64MOVDstore:
+		return rewriteValuePPC64_OpPPC64MOVDstore(v, config)
+	case OpPPC64MOVDstorezero:
+		return rewriteValuePPC64_OpPPC64MOVDstorezero(v, config)
+	case OpPPC64MOVHZload:
+		return rewriteValuePPC64_OpPPC64MOVHZload(v, config)
+	case OpPPC64MOVHZreg:
+		return rewriteValuePPC64_OpPPC64MOVHZreg(v, config)
+	case OpPPC64MOVHload:
+		return rewriteValuePPC64_OpPPC64MOVHload(v, config)
+	case OpPPC64MOVHreg:
+		return rewriteValuePPC64_OpPPC64MOVHreg(v, config)
+	case OpPPC64MOVHstore:
+		return rewriteValuePPC64_OpPPC64MOVHstore(v, config)
+	case OpPPC64MOVHstorezero:
+		return rewriteValuePPC64_OpPPC64MOVHstorezero(v, config)
+	case OpPPC64MOVWZload:
+		return rewriteValuePPC64_OpPPC64MOVWZload(v, config)
+	case OpPPC64MOVWZreg:
+		return rewriteValuePPC64_OpPPC64MOVWZreg(v, config)
+	case OpPPC64MOVWload:
+		return rewriteValuePPC64_OpPPC64MOVWload(v, config)
+	case OpPPC64MOVWreg:
+		return rewriteValuePPC64_OpPPC64MOVWreg(v, config)
+	case OpPPC64MOVWstore:
+		return rewriteValuePPC64_OpPPC64MOVWstore(v, config)
+	case OpPPC64MOVWstorezero:
+		return rewriteValuePPC64_OpPPC64MOVWstorezero(v, config)
+	case OpPPC64MaskIfNotCarry:
+		return rewriteValuePPC64_OpPPC64MaskIfNotCarry(v, config)
+	case OpPPC64NotEqual:
+		return rewriteValuePPC64_OpPPC64NotEqual(v, config)
+	case OpPPC64OR:
+		return rewriteValuePPC64_OpPPC64OR(v, config)
+	case OpPPC64ORN:
+		return rewriteValuePPC64_OpPPC64ORN(v, config)
+	case OpPPC64ORconst:
+		return rewriteValuePPC64_OpPPC64ORconst(v, config)
+	case OpPPC64SUB:
+		return rewriteValuePPC64_OpPPC64SUB(v, config)
+	case OpPPC64XOR:
+		return rewriteValuePPC64_OpPPC64XOR(v, config)
+	case OpPPC64XORconst:
+		return rewriteValuePPC64_OpPPC64XORconst(v, config)
+	case OpRsh16Ux16:
+		return rewriteValuePPC64_OpRsh16Ux16(v, config)
+	case OpRsh16Ux32:
+		return rewriteValuePPC64_OpRsh16Ux32(v, config)
+	case OpRsh16Ux64:
+		return rewriteValuePPC64_OpRsh16Ux64(v, config)
+	case OpRsh16Ux8:
+		return rewriteValuePPC64_OpRsh16Ux8(v, config)
+	case OpRsh16x16:
+		return rewriteValuePPC64_OpRsh16x16(v, config)
+	case OpRsh16x32:
+		return rewriteValuePPC64_OpRsh16x32(v, config)
+	case OpRsh16x64:
+		return rewriteValuePPC64_OpRsh16x64(v, config)
+	case OpRsh16x8:
+		return rewriteValuePPC64_OpRsh16x8(v, config)
+	case OpRsh32Ux16:
+		return rewriteValuePPC64_OpRsh32Ux16(v, config)
+	case OpRsh32Ux32:
+		return rewriteValuePPC64_OpRsh32Ux32(v, config)
+	case OpRsh32Ux64:
+		return rewriteValuePPC64_OpRsh32Ux64(v, config)
+	case OpRsh32Ux8:
+		return rewriteValuePPC64_OpRsh32Ux8(v, config)
+	case OpRsh32x16:
+		return rewriteValuePPC64_OpRsh32x16(v, config)
+	case OpRsh32x32:
+		return rewriteValuePPC64_OpRsh32x32(v, config)
+	case OpRsh32x64:
+		return rewriteValuePPC64_OpRsh32x64(v, config)
+	case OpRsh32x8:
+		return rewriteValuePPC64_OpRsh32x8(v, config)
+	case OpRsh64Ux16:
+		return rewriteValuePPC64_OpRsh64Ux16(v, config)
+	case OpRsh64Ux32:
+		return rewriteValuePPC64_OpRsh64Ux32(v, config)
+	case OpRsh64Ux64:
+		return rewriteValuePPC64_OpRsh64Ux64(v, config)
+	case OpRsh64Ux8:
+		return rewriteValuePPC64_OpRsh64Ux8(v, config)
+	case OpRsh64x16:
+		return rewriteValuePPC64_OpRsh64x16(v, config)
+	case OpRsh64x32:
+		return rewriteValuePPC64_OpRsh64x32(v, config)
+	case OpRsh64x64:
+		return rewriteValuePPC64_OpRsh64x64(v, config)
+	case OpRsh64x8:
+		return rewriteValuePPC64_OpRsh64x8(v, config)
+	case OpRsh8Ux16:
+		return rewriteValuePPC64_OpRsh8Ux16(v, config)
+	case OpRsh8Ux32:
+		return rewriteValuePPC64_OpRsh8Ux32(v, config)
+	case OpRsh8Ux64:
+		return rewriteValuePPC64_OpRsh8Ux64(v, config)
+	case OpRsh8Ux8:
+		return rewriteValuePPC64_OpRsh8Ux8(v, config)
+	case OpRsh8x16:
+		return rewriteValuePPC64_OpRsh8x16(v, config)
+	case OpRsh8x32:
+		return rewriteValuePPC64_OpRsh8x32(v, config)
+	case OpRsh8x64:
+		return rewriteValuePPC64_OpRsh8x64(v, config)
+	case OpRsh8x8:
+		return rewriteValuePPC64_OpRsh8x8(v, config)
+	case OpSignExt16to32:
+		return rewriteValuePPC64_OpSignExt16to32(v, config)
+	case OpSignExt16to64:
+		return rewriteValuePPC64_OpSignExt16to64(v, config)
+	case OpSignExt32to64:
+		return rewriteValuePPC64_OpSignExt32to64(v, config)
+	case OpSignExt8to16:
+		return rewriteValuePPC64_OpSignExt8to16(v, config)
+	case OpSignExt8to32:
+		return rewriteValuePPC64_OpSignExt8to32(v, config)
+	case OpSignExt8to64:
+		return rewriteValuePPC64_OpSignExt8to64(v, config)
+	case OpSlicemask:
+		return rewriteValuePPC64_OpSlicemask(v, config)
+	case OpSqrt:
+		return rewriteValuePPC64_OpSqrt(v, config)
+	case OpStaticCall:
+		return rewriteValuePPC64_OpStaticCall(v, config)
+	case OpStore:
+		return rewriteValuePPC64_OpStore(v, config)
+	case OpSub16:
+		return rewriteValuePPC64_OpSub16(v, config)
+	case OpSub32:
+		return rewriteValuePPC64_OpSub32(v, config)
+	case OpSub32F:
+		return rewriteValuePPC64_OpSub32F(v, config)
+	case OpSub64:
+		return rewriteValuePPC64_OpSub64(v, config)
+	case OpSub64F:
+		return rewriteValuePPC64_OpSub64F(v, config)
+	case OpSub8:
+		return rewriteValuePPC64_OpSub8(v, config)
+	case OpSubPtr:
+		return rewriteValuePPC64_OpSubPtr(v, config)
+	case OpTrunc16to8:
+		return rewriteValuePPC64_OpTrunc16to8(v, config)
+	case OpTrunc32to16:
+		return rewriteValuePPC64_OpTrunc32to16(v, config)
+	case OpTrunc32to8:
+		return rewriteValuePPC64_OpTrunc32to8(v, config)
+	case OpTrunc64to16:
+		return rewriteValuePPC64_OpTrunc64to16(v, config)
+	case OpTrunc64to32:
+		return rewriteValuePPC64_OpTrunc64to32(v, config)
+	case OpTrunc64to8:
+		return rewriteValuePPC64_OpTrunc64to8(v, config)
+	case OpXor16:
+		return rewriteValuePPC64_OpXor16(v, config)
+	case OpXor32:
+		return rewriteValuePPC64_OpXor32(v, config)
+	case OpXor64:
+		return rewriteValuePPC64_OpXor64(v, config)
+	case OpXor8:
+		return rewriteValuePPC64_OpXor8(v, config)
+	case OpZero:
+		return rewriteValuePPC64_OpZero(v, config)
+	case OpZeroExt16to32:
+		return rewriteValuePPC64_OpZeroExt16to32(v, config)
+	case OpZeroExt16to64:
+		return rewriteValuePPC64_OpZeroExt16to64(v, config)
+	case OpZeroExt32to64:
+		return rewriteValuePPC64_OpZeroExt32to64(v, config)
+	case OpZeroExt8to16:
+		return rewriteValuePPC64_OpZeroExt8to16(v, config)
+	case OpZeroExt8to32:
+		return rewriteValuePPC64_OpZeroExt8to32(v, config)
+	case OpZeroExt8to64:
+		return rewriteValuePPC64_OpZeroExt8to64(v, config)
+	}
+	return false
+}
+func rewriteValuePPC64_OpAdd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add16  x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64ADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32  x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64ADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAdd32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32F x y)
+	// cond:
+	// result: (FADDS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FADDS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAdd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64  x y)
+	// cond:
+	// result: (ADD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64ADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAdd64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64F x y)
+	// cond:
+	// result: (FADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAdd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add8   x y)
+	// cond:
+	// result: (ADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64ADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAddPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AddPtr x y)
+	// cond:
+	// result: (ADD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64ADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAddr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Addr {sym} base)
+	// cond:
+	// result: (MOVDaddr {sym} base)
+	for {
+		sym := v.Aux
+		base := v.Args[0]
+		v.reset(OpPPC64MOVDaddr)
+		v.Aux = sym
+		v.AddArg(base)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAnd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And16 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAnd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And32 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAnd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And64 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And8  x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAndB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AndB x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64AND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpAvg64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Avg64u <t> x y)
+	// cond:
+	// result: (ADD (ADD <t> (SRD <t> x (MOVDconst <t> [1])) (SRD <t> y (MOVDconst <t> [1]))) (ANDconst <t> (AND <t> x y) [1]))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64ADD)
+		v0 := b.NewValue0(v.Line, OpPPC64ADD, t)
+		v1 := b.NewValue0(v.Line, OpPPC64SRD, t)
+		v1.AddArg(x)
+		v2 := b.NewValue0(v.Line, OpPPC64MOVDconst, t)
+		v2.AuxInt = 1
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v3 := b.NewValue0(v.Line, OpPPC64SRD, t)
+		v3.AddArg(y)
+		v4 := b.NewValue0(v.Line, OpPPC64MOVDconst, t)
+		v4.AuxInt = 1
+		v3.AddArg(v4)
+		v0.AddArg(v3)
+		v.AddArg(v0)
+		v5 := b.NewValue0(v.Line, OpPPC64ANDconst, t)
+		v5.AuxInt = 1
+		v6 := b.NewValue0(v.Line, OpPPC64AND, t)
+		v6.AddArg(x)
+		v6.AddArg(y)
+		v5.AddArg(v6)
+		v.AddArg(v5)
+		return true
+	}
+}
+func rewriteValuePPC64_OpClosureCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ClosureCall [argwid] entry closure mem)
+	// cond:
+	// result: (CALLclosure [argwid] entry closure mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		closure := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpPPC64CALLclosure)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(closure)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCom16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com16 x)
+	// cond:
+	// result: (XORconst [-1] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64XORconst)
+		v.AuxInt = -1
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCom32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com32 x)
+	// cond:
+	// result: (XORconst [-1] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64XORconst)
+		v.AuxInt = -1
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCom64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com64 x)
+	// cond:
+	// result: (XORconst [-1] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64XORconst)
+		v.AuxInt = -1
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCom8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com8  x)
+	// cond:
+	// result: (XORconst [-1] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64XORconst)
+		v.AuxInt = -1
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpConst16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const16  [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValuePPC64_OpConst32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32  [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValuePPC64_OpConst32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32F [val])
+	// cond:
+	// result: (FMOVSconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpPPC64FMOVSconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValuePPC64_OpConst64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64  [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValuePPC64_OpConst64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64F [val])
+	// cond:
+	// result: (FMOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpPPC64FMOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValuePPC64_OpConst8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const8   [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValuePPC64_OpConstBool(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstBool [b])
+	// cond:
+	// result: (MOVDconst [b])
+	for {
+		b := v.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = b
+		return true
+	}
+}
+func rewriteValuePPC64_OpConstNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstNil)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+}
+func rewriteValuePPC64_OpConvert(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Convert <t> x mem)
+	// cond:
+	// result: (MOVDconvert <t> x mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpPPC64MOVDconvert)
+		v.Type = t
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCvt32Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto32 x)
+	// cond:
+	// result: (Xf2i64 (FCTIWZ x))
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64Xf2i64)
+		v0 := b.NewValue0(v.Line, OpPPC64FCTIWZ, config.fe.TypeFloat64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCvt32Fto64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64 x)
+	// cond:
+	// result: (Xf2i64 (FCTIDZ x))
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64Xf2i64)
+		v0 := b.NewValue0(v.Line, OpPPC64FCTIDZ, config.fe.TypeFloat64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCvt32Fto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64F x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCvt32to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to32F x)
+	// cond:
+	// result: (FRSP (FCFID (Xi2f64 (SignExt32to64 x))))
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64FRSP)
+		v0 := b.NewValue0(v.Line, OpPPC64FCFID, config.fe.TypeFloat64())
+		v1 := b.NewValue0(v.Line, OpPPC64Xi2f64, config.fe.TypeFloat64())
+		v2 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCvt32to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to64F x)
+	// cond:
+	// result: (FCFID (Xi2f64 (SignExt32to64 x)))
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64FCFID)
+		v0 := b.NewValue0(v.Line, OpPPC64Xi2f64, config.fe.TypeFloat64())
+		v1 := b.NewValue0(v.Line, OpSignExt32to64, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCvt64Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32 x)
+	// cond:
+	// result: (Xf2i64 (FCTIWZ x))
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64Xf2i64)
+		v0 := b.NewValue0(v.Line, OpPPC64FCTIWZ, config.fe.TypeFloat64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCvt64Fto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32F x)
+	// cond:
+	// result: (FRSP x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64FRSP)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCvt64Fto64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto64 x)
+	// cond:
+	// result: (Xf2i64 (FCTIDZ x))
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64Xf2i64)
+		v0 := b.NewValue0(v.Line, OpPPC64FCTIDZ, config.fe.TypeFloat64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCvt64to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64to32F x)
+	// cond:
+	// result: (FRSP (FCFID (Xi2f64 x)))
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64FRSP)
+		v0 := b.NewValue0(v.Line, OpPPC64FCFID, config.fe.TypeFloat64())
+		v1 := b.NewValue0(v.Line, OpPPC64Xi2f64, config.fe.TypeFloat64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpCvt64to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64to64F x)
+	// cond:
+	// result: (FCFID (Xi2f64 x))
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64FCFID)
+		v0 := b.NewValue0(v.Line, OpPPC64Xi2f64, config.fe.TypeFloat64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDeferCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (DeferCall [argwid] mem)
+	// cond:
+	// result: (CALLdefer [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpPPC64CALLdefer)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDiv16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16  x y)
+	// cond:
+	// result: (DIVW  (SignExt16to32 x) (SignExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64DIVW)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDiv16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16u x y)
+	// cond:
+	// result: (DIVWU (ZeroExt16to32 x) (ZeroExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64DIVWU)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDiv32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32  x y)
+	// cond:
+	// result: (DIVW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64DIVW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDiv32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32F x y)
+	// cond:
+	// result: (FDIVS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FDIVS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDiv32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32u x y)
+	// cond:
+	// result: (DIVWU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64DIVWU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDiv64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64  x y)
+	// cond:
+	// result: (DIVD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64DIVD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDiv64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64F x y)
+	// cond:
+	// result: (FDIV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FDIV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDiv64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64u x y)
+	// cond:
+	// result: (DIVDU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64DIVDU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDiv8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8   x y)
+	// cond:
+	// result: (DIVW  (SignExt8to32 x) (SignExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64DIVW)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpDiv8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8u  x y)
+	// cond:
+	// result: (DIVWU (ZeroExt8to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64DIVWU)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpEq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq16 x y)
+	// cond: isSigned(x.Type) && isSigned(y.Type)
+	// result: (Equal (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(isSigned(x.Type) && isSigned(y.Type)) {
+			break
+		}
+		v.reset(OpPPC64Equal)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Eq16 x y)
+	// cond:
+	// result: (Equal (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64Equal)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpEq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32 x y)
+	// cond:
+	// result: (Equal (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64Equal)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpEq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32F x y)
+	// cond:
+	// result: (Equal (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64Equal)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpEq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64 x y)
+	// cond:
+	// result: (Equal (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64Equal)
+		v0 := b.NewValue0(v.Line, OpPPC64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpEq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64F x y)
+	// cond:
+	// result: (Equal (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64Equal)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpEq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq8 x y)
+	// cond: isSigned(x.Type) && isSigned(y.Type)
+	// result: (Equal (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(isSigned(x.Type) && isSigned(y.Type)) {
+			break
+		}
+		v.reset(OpPPC64Equal)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Eq8 x y)
+	// cond:
+	// result: (Equal (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64Equal)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpEqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqB x y)
+	// cond:
+	// result: (ANDconst [1] (EQV x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64ANDconst)
+		v.AuxInt = 1
+		v0 := b.NewValue0(v.Line, OpPPC64EQV, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpEqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqPtr x y)
+	// cond:
+	// result: (Equal (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64Equal)
+		v0 := b.NewValue0(v.Line, OpPPC64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16 x y)
+	// cond:
+	// result: (GreaterEqual (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16U x y)
+	// cond:
+	// result: (GreaterEqual (CMPU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32 x y)
+	// cond:
+	// result: (GreaterEqual (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32F x y)
+	// cond:
+	// result: (FGreaterEqual (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FGreaterEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32U x y)
+	// cond:
+	// result: (GreaterEqual (CMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64 x y)
+	// cond:
+	// result: (GreaterEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64F x y)
+	// cond:
+	// result: (FGreaterEqual (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FGreaterEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64U x y)
+	// cond:
+	// result: (GreaterEqual (CMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8 x y)
+	// cond:
+	// result: (GreaterEqual (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8U x y)
+	// cond:
+	// result: (GreaterEqual (CMPU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGetClosurePtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetClosurePtr)
+	// cond:
+	// result: (LoweredGetClosurePtr)
+	for {
+		v.reset(OpPPC64LoweredGetClosurePtr)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGoCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GoCall [argwid] mem)
+	// cond:
+	// result: (CALLgo [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpPPC64CALLgo)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGreater16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16 x y)
+	// cond:
+	// result: (GreaterThan (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGreater16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16U x y)
+	// cond:
+	// result: (GreaterThan (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGreater32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32 x y)
+	// cond:
+	// result: (GreaterThan (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGreater32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32F x y)
+	// cond:
+	// result: (FGreaterThan (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FGreaterThan)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGreater32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32U x y)
+	// cond:
+	// result: (GreaterThan (CMPWU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGreater64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64 x y)
+	// cond:
+	// result: (GreaterThan (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGreater64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64F x y)
+	// cond:
+	// result: (FGreaterThan (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FGreaterThan)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGreater64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64U x y)
+	// cond:
+	// result: (GreaterThan (CMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGreater8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8 x y)
+	// cond:
+	// result: (GreaterThan (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpGreater8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8U x y)
+	// cond:
+	// result: (GreaterThan (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64GreaterThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpHmul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16 x y)
+	// cond:
+	// result: (SRAWconst (MULLW <config.fe.TypeInt32()> (SignExt16to32 x) (SignExt16to32 y)) [16])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpPPC64MULLW, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpHmul16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16u x y)
+	// cond:
+	// result: (SRWconst (MULLW <config.fe.TypeUInt32()> (ZeroExt16to32 x) (ZeroExt16to32 y)) [16])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpPPC64MULLW, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpHmul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32  x y)
+	// cond:
+	// result: (MULHW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64MULHW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpHmul32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32u  x y)
+	// cond:
+	// result: (MULHWU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64MULHWU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpHmul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul64  x y)
+	// cond:
+	// result: (MULHD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64MULHD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpHmul64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul64u  x y)
+	// cond:
+	// result: (MULHDU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64MULHDU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpHmul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8 x y)
+	// cond:
+	// result: (SRAWconst (MULLW <config.fe.TypeInt16()> (SignExt8to32 x) (SignExt8to32 y)) [8])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpPPC64MULLW, config.fe.TypeInt16())
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpHmul8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8u x y)
+	// cond:
+	// result: (SRWconst (MULLW <config.fe.TypeUInt16()> (ZeroExt8to32 x) (ZeroExt8to32 y)) [8])
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpPPC64MULLW, config.fe.TypeUInt16())
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpInterCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (InterCall [argwid] entry mem)
+	// cond:
+	// result: (CALLinter [argwid] entry mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpPPC64CALLinter)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValuePPC64_OpIsInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsInBounds idx len)
+	// cond:
+	// result: (LessThan (CMPU idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpPPC64LessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpIsNonNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsNonNil ptr)
+	// cond:
+	// result: (NotEqual (CMPconst [0] ptr))
+	for {
+		ptr := v.Args[0]
+		v.reset(OpPPC64NotEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPconst, TypeFlags)
+		v0.AuxInt = 0
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpIsSliceInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsSliceInBounds idx len)
+	// cond:
+	// result: (LessEqual (CMPU idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpPPC64LessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+		v0.AddArg(idx)
+		v0.AddArg(len)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16 x y)
+	// cond:
+	// result: (LessEqual (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16U x y)
+	// cond:
+	// result: (LessEqual (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32 x y)
+	// cond:
+	// result: (LessEqual (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32F x y)
+	// cond:
+	// result: (FLessEqual (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FLessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32U x y)
+	// cond:
+	// result: (LessEqual (CMPWU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64 x y)
+	// cond:
+	// result: (LessEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64F x y)
+	// cond:
+	// result: (FLessEqual (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FLessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64U x y)
+	// cond:
+	// result: (LessEqual (CMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8 x y)
+	// cond:
+	// result: (LessEqual (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8U x y)
+	// cond:
+	// result: (LessEqual (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLess16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16 x y)
+	// cond:
+	// result: (LessThan (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLess16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16U x y)
+	// cond:
+	// result: (LessThan (CMPWU (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLess32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32 x y)
+	// cond:
+	// result: (LessThan (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLess32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32F x y)
+	// cond:
+	// result: (FLessThan (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FLessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLess32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32U x y)
+	// cond:
+	// result: (LessThan (CMPWU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLess64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64 x y)
+	// cond:
+	// result: (LessThan (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLess64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64F x y)
+	// cond:
+	// result: (FLessThan (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FLessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLess64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64U x y)
+	// cond:
+	// result: (LessThan (CMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLess8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8 x y)
+	// cond:
+	// result: (LessThan (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLess8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8U x y)
+	// cond:
+	// result: (LessThan (CMPWU (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64LessThan)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWU, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLoad(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Load <t> ptr mem)
+	// cond: (is64BitInt(t) || isPtr(t))
+	// result: (MOVDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitInt(t) || isPtr(t)) {
+			break
+		}
+		v.reset(OpPPC64MOVDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitInt(t) && isSigned(t)
+	// result: (MOVWload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpPPC64MOVWload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitInt(t) && !isSigned(t)
+	// result: (MOVWZload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpPPC64MOVWZload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is16BitInt(t) && isSigned(t)
+	// result: (MOVHload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpPPC64MOVHload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is16BitInt(t) && !isSigned(t)
+	// result: (MOVHZload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpPPC64MOVHZload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: t.IsBoolean()
+	// result: (MOVBZload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(t.IsBoolean()) {
+			break
+		}
+		v.reset(OpPPC64MOVBZload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is8BitInt(t) && isSigned(t)
+	// result: (MOVBreg (MOVBZload ptr mem))
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is8BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpPPC64MOVBreg)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is8BitInt(t) && !isSigned(t)
+	// result: (MOVBZload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is8BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpPPC64MOVBZload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitFloat(t)
+	// result: (FMOVSload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitFloat(t)) {
+			break
+		}
+		v.reset(OpPPC64FMOVSload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitFloat(t)
+	// result: (FMOVDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitFloat(t)) {
+			break
+		}
+		v.reset(OpPPC64FMOVDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpLsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x16 x y)
+	// cond:
+	// result: (SLW  x                 (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -16
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x32  x (Const64 [c]))
+	// cond: uint32(c) < 16
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh16x32  x (MOVDconst [c]))
+	// cond: uint32(c) < 16
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh16x32 x y)
+	// cond:
+	// result: (SLW  x                 (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -16
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x64  x (Const64 [c]))
+	// cond: uint64(c) < 16
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh16x64  _ (Const64 [c]))
+	// cond: uint64(c) >= 16
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh16x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 16
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh16x64 x y)
+	// cond:
+	// result: (SLW  x                 (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -16
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x8 x y)
+	// cond:
+	// result: (SLW  x                 (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -16
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x16 x y)
+	// cond:
+	// result: (SLW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x32  x (Const64 [c]))
+	// cond: uint32(c) < 32
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh32x32  x (MOVDconst [c]))
+	// cond: uint32(c) < 32
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh32x32 x y)
+	// cond:
+	// result: (SLW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x64  x (Const64 [c]))
+	// cond: uint64(c) < 32
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh32x64  _ (Const64 [c]))
+	// cond: uint64(c) >= 32
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh32x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 32
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh32x64 x y)
+	// cond:
+	// result: (SLW  x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x8 x y)
+	// cond:
+	// result: (SLW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x16 x y)
+	// cond:
+	// result: (SLD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x32  x (Const64 [c]))
+	// cond: uint32(c) < 64
+	// result: (SLDconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SLDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh64x32  x (MOVDconst [c]))
+	// cond: uint32(c) < 64
+	// result: (SLDconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SLDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh64x32 x y)
+	// cond:
+	// result: (SLD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh64x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x64  x (Const64 [c]))
+	// cond: uint64(c) < 64
+	// result: (SLDconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SLDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh64x64  _ (Const64 [c]))
+	// cond: uint64(c) >= 64
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 64) {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh64x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 64
+	// result: (SLDconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SLDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh64x64 x y)
+	// cond:
+	// result: (SLD  x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x8 x y)
+	// cond:
+	// result: (SLD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x16 x y)
+	// cond:
+	// result: (SLW  x                (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -8
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x32   x (Const64 [c]))
+	// cond: uint32(c) < 8
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh8x32   x (MOVDconst [c]))
+	// cond: uint32(c) < 8
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh8x32 x y)
+	// cond:
+	// result: (SLW  x                (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -8
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x64   x (Const64 [c]))
+	// cond: uint64(c) < 8
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh8x64   _ (Const64 [c]))
+	// cond: uint64(c) >= 8
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh8x64   x (MOVDconst [c]))
+	// cond: uint64(c) < 8
+	// result: (SLWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Lsh8x64 x y)
+	// cond:
+	// result: (SLW  x                (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -8
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpLsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x8 x y)
+	// cond:
+	// result: (SLW  x                (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SLW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -8
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMod16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16 x y)
+	// cond:
+	// result: (Mod32 (SignExt16to32 x) (SignExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMod32)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMod16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16u x y)
+	// cond:
+	// result: (Mod32u (ZeroExt16to32 x) (ZeroExt16to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMod32u)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMod32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32 x y)
+	// cond:
+	// result: (SUB x (MULLW y (DIVW x y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SUB)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64MULLW, config.fe.TypeInt32())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64DIVW, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMod32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32u x y)
+	// cond:
+	// result: (SUB x (MULLW y (DIVWU x y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SUB)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64MULLW, config.fe.TypeInt32())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64DIVWU, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMod64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod64 x y)
+	// cond:
+	// result: (SUB x (MULLD y (DIVD x y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SUB)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64MULLD, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64DIVD, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMod64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod64u x y)
+	// cond:
+	// result: (SUB x (MULLD y (DIVDU x y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SUB)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64MULLD, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64DIVDU, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v1.AddArg(y)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMod8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8 x y)
+	// cond:
+	// result: (Mod32 (SignExt8to32 x) (SignExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMod32)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMod8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8u x y)
+	// cond:
+	// result: (Mod32u (ZeroExt8to32 x) (ZeroExt8to32 y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpMod32u)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMove(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Move [s] _ _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore dst (MOVBZload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpPPC64MOVBstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore dst (MOVHZload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVHstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVHZload, config.fe.TypeUInt16())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVBstore [1] dst (MOVBZload [1] src mem) 		(MOVBstore dst (MOVBZload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpPPC64MOVBstore)
+		v.AuxInt = 1
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v0.AuxInt = 1
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVBstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore dst (MOVWload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVWstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVWload, config.fe.TypeInt32())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [2] dst (MOVHZload [2] src mem) 		(MOVHstore dst (MOVHZload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVHstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVHZload, config.fe.TypeUInt16())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVHstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpPPC64MOVHZload, config.fe.TypeUInt16())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVBstore [3] dst (MOVBZload [3] src mem) 		(MOVBstore [2] dst (MOVBZload [2] src mem) 			(MOVBstore [1] dst (MOVBZload [1] src mem) 				(MOVBstore dst (MOVBZload src mem) mem))))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpPPC64MOVBstore)
+		v.AuxInt = 3
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v0.AuxInt = 3
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVBstore, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v2.AuxInt = 2
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpPPC64MOVBstore, TypeMem)
+		v3.AuxInt = 1
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v4.AuxInt = 1
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpPPC64MOVBstore, TypeMem)
+		v5.AddArg(dst)
+		v6 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v6.AddArg(src)
+		v6.AddArg(mem)
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVDstore dst (MOVDload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVDstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVDload, config.fe.TypeInt64())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstore [4] dst (MOVWZload [4] src mem) 		(MOVWstore dst (MOVWZload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVWstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVWZload, config.fe.TypeUInt32())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVWstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpPPC64MOVWZload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstore [6] dst (MOVHZload [6] src mem) 		(MOVHstore [4] dst (MOVHZload [4] src mem) 			(MOVHstore [2] dst (MOVHZload [2] src mem) 				(MOVHstore dst (MOVHZload src mem) mem))))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVHstore)
+		v.AuxInt = 6
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVHZload, config.fe.TypeUInt16())
+		v0.AuxInt = 6
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVHstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpPPC64MOVHZload, config.fe.TypeUInt16())
+		v2.AuxInt = 4
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpPPC64MOVHstore, TypeMem)
+		v3.AuxInt = 2
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpPPC64MOVHZload, config.fe.TypeUInt16())
+		v4.AuxInt = 2
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpPPC64MOVHstore, TypeMem)
+		v5.AddArg(dst)
+		v6 := b.NewValue0(v.Line, OpPPC64MOVHZload, config.fe.TypeUInt16())
+		v6.AddArg(src)
+		v6.AddArg(mem)
+		v5.AddArg(v6)
+		v5.AddArg(mem)
+		v3.AddArg(v5)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] dst (MOVBZload [2] src mem) 		(MOVBstore [1] dst (MOVBZload [1] src mem) 			(MOVBstore dst (MOVBZload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpPPC64MOVBstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVBstore, TypeMem)
+		v1.AuxInt = 1
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v2.AuxInt = 1
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpPPC64MOVBstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpPPC64MOVBZload, config.fe.TypeUInt8())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: (SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%8 != 0
+	// result: (LoweredMove [SizeAndAlign(s).Align()] 		dst 		src 		(ADDconst <src.Type> src [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)]) 		mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !((SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%8 != 0) {
+			break
+		}
+		v.reset(OpPPC64LoweredMove)
+		v.AuxInt = SizeAndAlign(s).Align()
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, OpPPC64ADDconst, src.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - moveSize(SizeAndAlign(s).Align(), config)
+		v0.AddArg(src)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpMul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul16  x y)
+	// cond:
+	// result: (MULLW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64MULLW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32  x y)
+	// cond:
+	// result: (MULLW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64MULLW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMul32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32F x y)
+	// cond:
+	// result: (FMULS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FMULS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64  x y)
+	// cond:
+	// result: (MULLD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64MULLD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMul64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64F x y)
+	// cond:
+	// result: (FMUL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FMUL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpMul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul8   x y)
+	// cond:
+	// result: (MULLW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64MULLW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeg16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg16  x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64NEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeg32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32  x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64NEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeg32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32F x)
+	// cond:
+	// result: (FNEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64FNEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeg64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64  x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64NEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeg64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64F x)
+	// cond:
+	// result: (FNEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64FNEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeg8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg8   x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64NEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq16 x y)
+	// cond: isSigned(x.Type) && isSigned(y.Type)
+	// result: (NotEqual (CMPW (SignExt16to32 x) (SignExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(isSigned(x.Type) && isSigned(y.Type)) {
+			break
+		}
+		v.reset(OpPPC64NotEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Neq16 x y)
+	// cond:
+	// result: (NotEqual (CMPW (ZeroExt16to32 x) (ZeroExt16to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64NotEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32 x y)
+	// cond:
+	// result: (NotEqual (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64NotEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32F x y)
+	// cond:
+	// result: (NotEqual (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64NotEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64 x y)
+	// cond:
+	// result: (NotEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64NotEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64F x y)
+	// cond:
+	// result: (NotEqual (FCMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64NotEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64FCMPU, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq8 x y)
+	// cond: isSigned(x.Type) && isSigned(y.Type)
+	// result: (NotEqual (CMPW (SignExt8to32 x) (SignExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		if !(isSigned(x.Type) && isSigned(y.Type)) {
+			break
+		}
+		v.reset(OpPPC64NotEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Neq8 x y)
+	// cond:
+	// result: (NotEqual (CMPW (ZeroExt8to32 x) (ZeroExt8to32 y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64NotEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPW, TypeFlags)
+		v1 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqB x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNeqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqPtr x y)
+	// cond:
+	// result: (NotEqual (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64NotEqual)
+		v0 := b.NewValue0(v.Line, OpPPC64CMP, TypeFlags)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNilCheck(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NilCheck ptr mem)
+	// cond:
+	// result: (LoweredNilCheck ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpPPC64LoweredNilCheck)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValuePPC64_OpNot(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Not x)
+	// cond:
+	// result: (XORconst [1] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64XORconst)
+		v.AuxInt = 1
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpOffPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OffPtr [off] ptr)
+	// cond:
+	// result: (ADD (MOVDconst <config.Frontend().TypeInt64()> [off]) ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		v.reset(OpPPC64ADD)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVDconst, config.Frontend().TypeInt64())
+		v0.AuxInt = off
+		v.AddArg(v0)
+		v.AddArg(ptr)
+		return true
+	}
+}
+func rewriteValuePPC64_OpOr16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or16 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpOr32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or32 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpOr64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or64 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or8  x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpOrB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OrB x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64OR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpPPC64ADD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADD (MOVDconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (ADDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64ADDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADD x (MOVDconst [c]))
+	// cond: is32Bit(c)
+	// result: (ADDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64ADDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64ADDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDconst [c] (ADDconst [d] x))
+	// cond: is32Bit(c+d)
+	// result: (ADDconst [c+d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(c + d)) {
+			break
+		}
+		v.reset(OpPPC64ADDconst)
+		v.AuxInt = c + d
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (MOVDaddr [d] {sym} x))
+	// cond:
+	// result: (MOVDaddr [c+d] {sym} x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		d := v_0.AuxInt
+		sym := v_0.Aux
+		x := v_0.Args[0]
+		v.reset(OpPPC64MOVDaddr)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64AND(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AND x (XORconst [-1] y))
+	// cond:
+	// result: (ANDN x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64XORconst {
+			break
+		}
+		if v_1.AuxInt != -1 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpPPC64ANDN)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (AND (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c&d])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = c & d
+		return true
+	}
+	// match: (AND x (MOVDconst [c]))
+	// cond: isU16Bit(c)
+	// result: (ANDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isU16Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64ANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND (MOVDconst [c]) x)
+	// cond: isU16Bit(c)
+	// result: (ANDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isU16Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64ANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND (MOVDconst [c]) x:(MOVBZload _ _))
+	// cond:
+	// result: (ANDconst [c&0xFF] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if x.Op != OpPPC64MOVBZload {
+			break
+		}
+		v.reset(OpPPC64ANDconst)
+		v.AuxInt = c & 0xFF
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x:(MOVBZload _ _) (MOVDconst [c]))
+	// cond:
+	// result: (ANDconst [c&0xFF] x)
+	for {
+		x := v.Args[0]
+		if x.Op != OpPPC64MOVBZload {
+			break
+		}
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpPPC64ANDconst)
+		v.AuxInt = c & 0xFF
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64ANDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDconst [c] (ANDconst [d] x))
+	// cond:
+	// result: (ANDconst [c&d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ANDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpPPC64ANDconst)
+		v.AuxInt = c & d
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDconst [-1] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDconst [0] _)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (ANDconst [c] y:(MOVBZreg _))
+	// cond: c&0xFF == 0xFF
+	// result: y
+	for {
+		c := v.AuxInt
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVBZreg {
+			break
+		}
+		if !(c&0xFF == 0xFF) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (ANDconst [c] y:(MOVHZreg _))
+	// cond: c&0xFFFF == 0xFFFF
+	// result: y
+	for {
+		c := v.AuxInt
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVHZreg {
+			break
+		}
+		if !(c&0xFFFF == 0xFFFF) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (ANDconst [c] y:(MOVWZreg _))
+	// cond: c&0xFFFFFFFF == 0xFFFFFFFF
+	// result: y
+	for {
+		c := v.AuxInt
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVWZreg {
+			break
+		}
+		if !(c&0xFFFFFFFF == 0xFFFFFFFF) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (ANDconst [c] (MOVBZreg x))
+	// cond:
+	// result: (ANDconst [c&0xFF] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVBZreg {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64ANDconst)
+		v.AuxInt = c & 0xFF
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDconst [c] (MOVHZreg x))
+	// cond:
+	// result: (ANDconst [c&0xFFFF] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVHZreg {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64ANDconst)
+		v.AuxInt = c & 0xFFFF
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDconst [c] (MOVWZreg x))
+	// cond:
+	// result: (ANDconst [c&0xFFFFFFFF] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVWZreg {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64ANDconst)
+		v.AuxInt = c & 0xFFFFFFFF
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64CMP(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMP x (MOVDconst [c]))
+	// cond: is16Bit(c)
+	// result: (CMPconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is16Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64CMPconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMP (MOVDconst [c]) y)
+	// cond: is16Bit(c)
+	// result: (InvertFlags (CMPconst y [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v.Args[1]
+		if !(is16Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPconst, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64CMPU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPU x (MOVDconst [c]))
+	// cond: isU16Bit(c)
+	// result: (CMPUconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isU16Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64CMPUconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPU (MOVDconst [c]) y)
+	// cond: isU16Bit(c)
+	// result: (InvertFlags (CMPUconst y [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v.Args[1]
+		if !(isU16Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPUconst, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64CMPUconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPUconst (MOVDconst [x]) [y])
+	// cond: int64(x)==int64(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int64(x) == int64(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagEQ)
+		return true
+	}
+	// match: (CMPUconst (MOVDconst [x]) [y])
+	// cond: uint64(x)<uint64(y)
+	// result: (FlagLT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(uint64(x) < uint64(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagLT)
+		return true
+	}
+	// match: (CMPUconst (MOVDconst [x]) [y])
+	// cond: uint64(x)>uint64(y)
+	// result: (FlagGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(uint64(x) > uint64(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagGT)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64CMPW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPW x (MOVWreg y))
+	// cond:
+	// result: (CMPW x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVWreg {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpPPC64CMPW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMPW (MOVWreg x) y)
+	// cond:
+	// result: (CMPW x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVWreg {
+			break
+		}
+		x := v_0.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64CMPW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMPW x (MOVDconst [c]))
+	// cond: is16Bit(c)
+	// result: (CMPWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is16Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64CMPWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPW (MOVDconst [c]) y)
+	// cond: is16Bit(c)
+	// result: (InvertFlags (CMPWconst y [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v.Args[1]
+		if !(is16Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWconst, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64CMPWU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPWU x (MOVWZreg y))
+	// cond:
+	// result: (CMPWU x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVWZreg {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpPPC64CMPWU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMPWU (MOVWZreg x) y)
+	// cond:
+	// result: (CMPWU x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVWZreg {
+			break
+		}
+		x := v_0.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64CMPWU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (CMPWU x (MOVDconst [c]))
+	// cond: isU16Bit(c)
+	// result: (CMPWUconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isU16Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64CMPWUconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPWU (MOVDconst [c]) y)
+	// cond: isU16Bit(c)
+	// result: (InvertFlags (CMPWUconst y [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		y := v.Args[1]
+		if !(isU16Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64InvertFlags)
+		v0 := b.NewValue0(v.Line, OpPPC64CMPWUconst, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(y)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64CMPWUconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPWUconst (MOVDconst [x]) [y])
+	// cond: int32(x)==int32(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) == int32(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagEQ)
+		return true
+	}
+	// match: (CMPWUconst (MOVDconst [x]) [y])
+	// cond: uint32(x)<uint32(y)
+	// result: (FlagLT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(uint32(x) < uint32(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagLT)
+		return true
+	}
+	// match: (CMPWUconst (MOVDconst [x]) [y])
+	// cond: uint32(x)>uint32(y)
+	// result: (FlagGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(uint32(x) > uint32(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagGT)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64CMPWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)==int32(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) == int32(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagEQ)
+		return true
+	}
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)<int32(y)
+	// result: (FlagLT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) < int32(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagLT)
+		return true
+	}
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)>int32(y)
+	// result: (FlagGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) > int32(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagGT)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64CMPconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPconst (MOVDconst [x]) [y])
+	// cond: int64(x)==int64(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int64(x) == int64(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagEQ)
+		return true
+	}
+	// match: (CMPconst (MOVDconst [x]) [y])
+	// cond: int64(x)<int64(y)
+	// result: (FlagLT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int64(x) < int64(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagLT)
+		return true
+	}
+	// match: (CMPconst (MOVDconst [x]) [y])
+	// cond: int64(x)>int64(y)
+	// result: (FlagGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int64(x) > int64(y)) {
+			break
+		}
+		v.reset(OpPPC64FlagGT)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64Equal(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Equal (FlagEQ))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagEQ {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (Equal (FlagLT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagLT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Equal (FlagGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagGT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Equal (InvertFlags x))
+	// cond:
+	// result: (Equal x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64Equal)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64FMOVDload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (FMOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64FMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (FMOVDload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64FMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64FMOVDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: is16Bit(off1+off2)
+	// result: (FMOVDstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64FMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (FMOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64FMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64FMOVSload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVSload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (FMOVSload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64FMOVSload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSload [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (FMOVSload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64FMOVSload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64FMOVSstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVSstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: is16Bit(off1+off2)
+	// result: (FMOVSstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64FMOVSstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (FMOVSstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64FMOVSstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64GreaterEqual(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GreaterEqual (FlagEQ))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagEQ {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqual (FlagLT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagLT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterEqual (FlagGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagGT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterEqual (InvertFlags x))
+	// cond:
+	// result: (LessEqual x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64LessEqual)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64GreaterThan(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GreaterThan (FlagEQ))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagEQ {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThan (FlagLT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagLT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (GreaterThan (FlagGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagGT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (GreaterThan (InvertFlags x))
+	// cond:
+	// result: (LessThan x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64LessThan)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64LessEqual(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LessEqual (FlagEQ))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagEQ {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqual (FlagLT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagLT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessEqual (FlagGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagGT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessEqual (InvertFlags x))
+	// cond:
+	// result: (GreaterEqual x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64GreaterEqual)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64LessThan(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (LessThan (FlagEQ))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagEQ {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThan (FlagLT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagLT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (LessThan (FlagGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagGT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (LessThan (InvertFlags x))
+	// cond:
+	// result: (GreaterThan x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64GreaterThan)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVBZload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBZload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBZload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVBZload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBZload [off1] {sym} (ADDconst [off2] x) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVBZload [off1+off2] {sym} x mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVBZload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVBZreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBZreg y:(ANDconst [c] _))
+	// cond: uint64(c) <= 0xFF
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64ANDconst {
+			break
+		}
+		c := y.AuxInt
+		if !(uint64(c) <= 0xFF) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVBZreg y:(MOVBZreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVBZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVBZreg (MOVBreg x))
+	// cond:
+	// result: (MOVBZreg x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVBreg {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64MOVBZreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVBZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpPPC64MOVBZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(uint8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = int64(uint8(c))
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVBreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBreg y:(ANDconst [c] _))
+	// cond: uint64(c) <= 0x7F
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64ANDconst {
+			break
+		}
+		c := y.AuxInt
+		if !(uint64(c) <= 0x7F) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVBreg y:(MOVBreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVBreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVBreg (MOVBZreg x))
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVBZreg {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(int8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = int64(int8(c))
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVBstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstore [off1] {sym} (ADDconst [off2] x) val mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVBstore [off1+off2] {sym} x val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVDconst [c]) mem)
+	// cond: c == 0
+	// result: (MOVBstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(c == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVBstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVBreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpPPC64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBZreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVBZreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpPPC64MOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVBstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstorezero [off1] {sym} (ADDconst [off2] x) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVBstorezero [off1+off2] {sym} x mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVBstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} x) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVBstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVDload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVDload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload [off1] {sym} (ADDconst [off2] x) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVDload [off1+off2] {sym} x mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDstore [off1] {sym} (ADDconst [off2] x) val mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVDstore [off1+off2] {sym} x val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVDstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [off] {sym} ptr (MOVDconst [c]) mem)
+	// cond: c == 0
+	// result: (MOVDstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(c == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVDstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVDstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDstorezero [off1] {sym} (ADDconst [off2] x) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVDstorezero [off1+off2] {sym} x mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVDstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} x) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVDstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVHZload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHZload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHZload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVHZload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHZload [off1] {sym} (ADDconst [off2] x) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVHZload [off1+off2] {sym} x mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVHZload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVHZreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHZreg y:(ANDconst [c] _))
+	// cond: uint64(c) <= 0xFFFF
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64ANDconst {
+			break
+		}
+		c := y.AuxInt
+		if !(uint64(c) <= 0xFFFF) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVHZreg y:(MOVHZreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVHZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVHZreg y:(MOVBZreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVBZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVHZreg y:(MOVHreg x))
+	// cond:
+	// result: (MOVHZreg x)
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVHreg {
+			break
+		}
+		x := y.Args[0]
+		v.reset(OpPPC64MOVHZreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHZreg x:(MOVHZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpPPC64MOVHZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHZreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(uint16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = int64(uint16(c))
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVHload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHload [off1] {sym} (ADDconst [off2] x) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVHload [off1+off2] {sym} x mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVHreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHreg y:(ANDconst [c] _))
+	// cond: uint64(c) <= 0x7FFF
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64ANDconst {
+			break
+		}
+		c := y.AuxInt
+		if !(uint64(c) <= 0x7FFF) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVHreg y:(MOVHreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVHreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVHreg y:(MOVBreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVBreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVHreg y:(MOVHZreg x))
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVHZreg {
+			break
+		}
+		x := y.Args[0]
+		v.reset(OpPPC64MOVHreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpPPC64MOVHload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(int16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = int64(int16(c))
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVHstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstore [off1] {sym} (ADDconst [off2] x) val mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVHstore [off1+off2] {sym} x val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVDconst [c]) mem)
+	// cond: c == 0
+	// result: (MOVHstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(c == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVHstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVHreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpPPC64MOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHZreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVHZreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpPPC64MOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVHstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstorezero [off1] {sym} (ADDconst [off2] x) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVHstorezero [off1+off2] {sym} x mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVHstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} x) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVHstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVWZload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWZload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVWZload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVWZload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWZload [off1] {sym} (ADDconst [off2] x) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVWZload [off1+off2] {sym} x mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVWZload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVWZreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWZreg y:(ANDconst [c] _))
+	// cond: uint64(c) <= 0xFFFFFFFF
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64ANDconst {
+			break
+		}
+		c := y.AuxInt
+		if !(uint64(c) <= 0xFFFFFFFF) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVWZreg y:(AND (MOVDconst [c]) _))
+	// cond: uint64(c) <= 0xFFFFFFFF
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64AND {
+			break
+		}
+		y_0 := y.Args[0]
+		if y_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := y_0.AuxInt
+		if !(uint64(c) <= 0xFFFFFFFF) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVWZreg y:(MOVWZreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVWZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVWZreg y:(MOVHZreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVHZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVWZreg y:(MOVBZreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVBZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVWZreg y:(MOVWreg x))
+	// cond:
+	// result: (MOVWZreg x)
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVWreg {
+			break
+		}
+		x := y.Args[0]
+		v.reset(OpPPC64MOVWZreg)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVWload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWload [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off1] {sym} (ADDconst [off2] x) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVWload [off1+off2] {sym} x mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVWreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWreg y:(ANDconst [c] _))
+	// cond: uint64(c) <= 0xFFFF
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64ANDconst {
+			break
+		}
+		c := y.AuxInt
+		if !(uint64(c) <= 0xFFFF) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVWreg y:(AND (MOVDconst [c]) _))
+	// cond: uint64(c) <= 0x7FFFFFFF
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64AND {
+			break
+		}
+		y_0 := y.Args[0]
+		if y_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := y_0.AuxInt
+		if !(uint64(c) <= 0x7FFFFFFF) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVWreg y:(MOVWreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVWreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVWreg y:(MOVHreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVHreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVWreg y:(MOVBreg _))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVBreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVWreg y:(MOVWZreg x))
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		y := v.Args[0]
+		if y.Op != OpPPC64MOVWZreg {
+			break
+		}
+		x := y.Args[0]
+		v.reset(OpPPC64MOVWreg)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVWstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstore [off1] {sym} (ADDconst [off2] x) val mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVWstore [off1+off2] {sym} x val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off1] {sym1} (MOVDaddr [off2] {sym2} ptr) val mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVDconst [c]) mem)
+	// cond: c == 0
+	// result: (MOVWstorezero [off] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(c == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVWstorezero)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpPPC64MOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWZreg x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVWZreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpPPC64MOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MOVWstorezero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstorezero [off1] {sym} (ADDconst [off2] x) mem)
+	// cond: is16Bit(off1+off2)
+	// result: (MOVWstorezero [off1+off2] {sym} x mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is16Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpPPC64MOVWstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstorezero [off1] {sym1} (MOVDaddr [off2] {sym2} x) mem)
+	// cond: canMergeSym(sym1,sym2)
+	// result: (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} x mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpPPC64MOVWstorezero)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64MaskIfNotCarry(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MaskIfNotCarry (ADDconstForCarry [c] (ANDconst [d] _)))
+	// cond: c < 0 && d > 0 && c + d < 0
+	// result: (MOVDconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ADDconstForCarry {
+			break
+		}
+		c := v_0.AuxInt
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpPPC64ANDconst {
+			break
+		}
+		d := v_0_0.AuxInt
+		if !(c < 0 && d > 0 && c+d < 0) {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = -1
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64NotEqual(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NotEqual (FlagEQ))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagEQ {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (NotEqual (FlagLT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagLT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (NotEqual (FlagGT))
+	// cond:
+	// result: (MOVDconst [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64FlagGT {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 1
+		return true
+	}
+	// match: (NotEqual (InvertFlags x))
+	// cond:
+	// result: (NotEqual x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64InvertFlags {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpPPC64NotEqual)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64OR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OR (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c|d])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = c | d
+		return true
+	}
+	// match: (OR x (MOVDconst [c]))
+	// cond: isU32Bit(c)
+	// result: (ORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isU32Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64ORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR (MOVDconst [c]) x)
+	// cond: isU32Bit(c)
+	// result: (ORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isU32Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64ORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64ORN(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORN x (MOVDconst [-1]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		if v_1.AuxInt != -1 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64ORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORconst [c] (ORconst [d] x))
+	// cond:
+	// result: (ORconst [c|d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64ORconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpPPC64ORconst)
+		v.AuxInt = c | d
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORconst [-1] _)
+	// cond:
+	// result: (MOVDconst [-1])
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (ORconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64SUB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUB x (MOVDconst [c]))
+	// cond: is32Bit(-c)
+	// result: (ADDconst [-c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(-c)) {
+			break
+		}
+		v.reset(OpPPC64ADDconst)
+		v.AuxInt = -c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64XOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XOR (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c^d])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	// match: (XOR x (MOVDconst [c]))
+	// cond: isU32Bit(c)
+	// result: (XORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isU32Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64XORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR (MOVDconst [c]) x)
+	// cond: isU32Bit(c)
+	// result: (XORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isU32Bit(c)) {
+			break
+		}
+		v.reset(OpPPC64XORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpPPC64XORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORconst [c] (XORconst [d] x))
+	// cond:
+	// result: (XORconst [c^d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpPPC64XORconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpPPC64XORconst)
+		v.AuxInt = c ^ d
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpRsh16Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux16 x y)
+	// cond:
+	// result: (SRW  (ZeroExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -16
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh16Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux32 x (Const64 [c]))
+	// cond: uint32(c) < 16
+	// result: (SRWconst (ZeroExt16to32 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16Ux32 x (MOVDconst [c]))
+	// cond: uint32(c) < 16
+	// result: (SRWconst (ZeroExt16to32 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16Ux32 x y)
+	// cond:
+	// result: (SRW  (ZeroExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -16
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh16Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux64 x (Const64 [c]))
+	// cond: uint64(c) < 16
+	// result: (SRWconst (ZeroExt16to32 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16Ux64 _ (Const64 [c]))
+	// cond: uint64(c) >= 16
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh16Ux64 x (MOVDconst [c]))
+	// cond: uint64(c) < 16
+	// result: (SRWconst (ZeroExt16to32 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16Ux64 x y)
+	// cond:
+	// result: (SRW  (ZeroExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -16
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh16Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux8 x y)
+	// cond:
+	// result: (SRW  (ZeroExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -16
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x16 x y)
+	// cond:
+	// result: (SRAW (SignExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -16
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x32  x (Const64 [c]))
+	// cond: uint32(c) < 16
+	// result: (SRAWconst (SignExt16to32 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16x32  x (MOVDconst [c]))
+	// cond: uint32(c) < 16
+	// result: (SRAWconst (SignExt16to32 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16x32 x y)
+	// cond:
+	// result: (SRAW (SignExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -16
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x64  x (Const64 [c]))
+	// cond: uint64(c) < 16
+	// result: (SRAWconst (SignExt16to32 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16x64 x (Const64 [c]))
+	// cond: uint64(c) >= 16
+	// result: (SRAWconst (SignExt16to32 x) [63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 16) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = 63
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 16
+	// result: (SRAWconst (SignExt16to32 x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 16) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16x64 x y)
+	// cond:
+	// result: (SRAW (SignExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -16
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x8 x y)
+	// cond:
+	// result: (SRAW (SignExt16to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-16] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -16
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh32Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux16 x y)
+	// cond:
+	// result: (SRW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh32Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux32 x (Const64 [c]))
+	// cond: uint32(c) < 32
+	// result: (SRWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32Ux32 x (MOVDconst [c]))
+	// cond: uint32(c) < 32
+	// result: (SRWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32Ux32 x y)
+	// cond:
+	// result: (SRW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh32Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux64 x (Const64 [c]))
+	// cond: uint64(c) < 32
+	// result: (SRWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32Ux64 _ (Const64 [c]))
+	// cond: uint64(c) >= 32
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh32Ux64 x (MOVDconst [c]))
+	// cond: uint64(c) < 32
+	// result: (SRWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32Ux64 x y)
+	// cond:
+	// result: (SRW  x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh32Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux8 x y)
+	// cond:
+	// result: (SRW x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x16 x y)
+	// cond:
+	// result: (SRAW x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x32  x (Const64 [c]))
+	// cond: uint32(c) < 32
+	// result: (SRAWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32x32  x (MOVDconst [c]))
+	// cond: uint32(c) < 32
+	// result: (SRAWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32x32 x y)
+	// cond:
+	// result: (SRAW x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x64  x (Const64 [c]))
+	// cond: uint64(c) < 32
+	// result: (SRAWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32x64 x (Const64 [c]))
+	// cond: uint64(c) >= 32
+	// result: (SRAWconst x [63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 32) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 32
+	// result: (SRAWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 32) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32x64 x y)
+	// cond:
+	// result: (SRAW x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x8 x y)
+	// cond:
+	// result: (SRAW x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-32] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -32
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh64Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux16 x y)
+	// cond:
+	// result: (SRD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh64Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux32 x (Const64 [c]))
+	// cond: uint32(c) < 64
+	// result: (SRDconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SRDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64Ux32 x (MOVDconst [c]))
+	// cond: uint32(c) < 64
+	// result: (SRDconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SRDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64Ux32 x y)
+	// cond:
+	// result: (SRD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh64Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux64 x (Const64 [c]))
+	// cond: uint64(c) < 64
+	// result: (SRDconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SRDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64Ux64 _ (Const64 [c]))
+	// cond: uint64(c) >= 64
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 64) {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh64Ux64 x (MOVDconst [c]))
+	// cond: uint64(c) < 64
+	// result: (SRDconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SRDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64Ux64 x y)
+	// cond:
+	// result: (SRD  x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh64Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux8 x y)
+	// cond:
+	// result: (SRD x  (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x16 x y)
+	// cond:
+	// result: (SRAD x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v3 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x32  x (Const64 [c]))
+	// cond: uint32(c) < 64
+	// result: (SRADconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SRADconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64x32  x (MOVDconst [c]))
+	// cond: uint32(c) < 64
+	// result: (SRADconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SRADconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64x32 x y)
+	// cond:
+	// result: (SRAD x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v3 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh64x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x64  x (Const64 [c]))
+	// cond: uint64(c) < 64
+	// result: (SRADconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SRADconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64x64 x (Const64 [c]))
+	// cond: uint64(c) >= 64
+	// result: (SRADconst x [63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 64) {
+			break
+		}
+		v.reset(OpPPC64SRADconst)
+		v.AuxInt = 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64x64  x (MOVDconst [c]))
+	// cond: uint64(c) < 64
+	// result: (SRADconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 64) {
+			break
+		}
+		v.reset(OpPPC64SRADconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh64x64 x y)
+	// cond:
+	// result: (SRAD x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x8 x y)
+	// cond:
+	// result: (SRAD x (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-64] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAD)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v2 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v2.AuxInt = -64
+		v3 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh8Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux16 x y)
+	// cond:
+	// result: (SRW  (ZeroExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -8
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh8Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux32  x (Const64 [c]))
+	// cond: uint32(c) < 8
+	// result: (SRWconst (ZeroExt8to32  x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8Ux32  x (MOVDconst [c]))
+	// cond: uint32(c) < 8
+	// result: (SRWconst (ZeroExt8to32  x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8Ux32 x y)
+	// cond:
+	// result: (SRW  (ZeroExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -8
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh8Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux64  x (Const64 [c]))
+	// cond: uint64(c) < 8
+	// result: (SRWconst (ZeroExt8to32  x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8Ux64  _ (Const64 [c]))
+	// cond: uint64(c) >= 8
+	// result: (MOVDconst [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpPPC64MOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh8Ux64  x (MOVDconst [c]))
+	// cond: uint64(c) < 8
+	// result: (SRWconst (ZeroExt8to32  x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SRWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8Ux64 x y)
+	// cond:
+	// result: (SRW  (ZeroExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -8
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh8Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux8 x y)
+	// cond:
+	// result: (SRW  (ZeroExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRW)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -8
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x16 x y)
+	// cond:
+	// result: (SRAW (SignExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt16to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -8
+		v4 := b.NewValue0(v.Line, OpZeroExt16to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x32   x (Const64 [c]))
+	// cond: uint32(c) < 8
+	// result: (SRAWconst (SignExt8to32  x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8x32   x (MOVDconst [c]))
+	// cond: uint32(c) < 8
+	// result: (SRAWconst (SignExt8to32  x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint32(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8x32 x y)
+	// cond:
+	// result: (SRAW (SignExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt32to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -8
+		v4 := b.NewValue0(v.Line, OpZeroExt32to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x64   x (Const64 [c]))
+	// cond: uint64(c) < 8
+	// result: (SRAWconst (SignExt8to32  x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8x64  x (Const64 [c]))
+	// cond: uint64(c) >= 8
+	// result: (SRAWconst (SignExt8to32  x) [63])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64 {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) >= 8) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = 63
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8x64   x (MOVDconst [c]))
+	// cond: uint64(c) < 8
+	// result: (SRAWconst (SignExt8to32  x) [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpPPC64MOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(uint64(c) < 8) {
+			break
+		}
+		v.reset(OpPPC64SRAWconst)
+		v.AuxInt = c
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8x64 x y)
+	// cond:
+	// result: (SRAW (SignExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -8
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpRsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x8 x y)
+	// cond:
+	// result: (SRAW (SignExt8to32 x) (ORN y <config.fe.TypeInt64()> (MaskIfNotCarry (ADDconstForCarry [-8] (ZeroExt8to64 y)))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SRAW)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpPPC64ORN, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpPPC64MaskIfNotCarry, config.fe.TypeInt64())
+		v3 := b.NewValue0(v.Line, OpPPC64ADDconstForCarry, TypeFlags)
+		v3.AuxInt = -8
+		v4 := b.NewValue0(v.Line, OpZeroExt8to64, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSignExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to32 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSignExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to64 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSignExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt32to64 x)
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVWreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSignExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to16  x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSignExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to32  x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSignExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to64  x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSlicemask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Slicemask <t> x)
+	// cond:
+	// result: (XORconst [-1] (SRADconst <t> (ADDconst <t> x [-1]) [63]))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpPPC64XORconst)
+		v.AuxInt = -1
+		v0 := b.NewValue0(v.Line, OpPPC64SRADconst, t)
+		v0.AuxInt = 63
+		v1 := b.NewValue0(v.Line, OpPPC64ADDconst, t)
+		v1.AuxInt = -1
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSqrt(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sqrt x)
+	// cond:
+	// result: (FSQRT x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64FSQRT)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpStaticCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (StaticCall [argwid] {target} mem)
+	// cond:
+	// result: (CALLstatic [argwid] {target} mem)
+	for {
+		argwid := v.AuxInt
+		target := v.Aux
+		mem := v.Args[0]
+		v.reset(OpPPC64CALLstatic)
+		v.AuxInt = argwid
+		v.Aux = target
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValuePPC64_OpStore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Store [8] ptr val mem)
+	// cond: is64BitFloat(val.Type)
+	// result: (FMOVDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is64BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpPPC64FMOVDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [8] ptr val mem)
+	// cond: is32BitFloat(val.Type)
+	// result: (FMOVDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpPPC64FMOVDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: is32BitFloat(val.Type)
+	// result: (FMOVSstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpPPC64FMOVSstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [8] ptr val mem)
+	// cond: (is64BitInt(val.Type) || isPtr(val.Type))
+	// result: (MOVDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is64BitInt(val.Type) || isPtr(val.Type)) {
+			break
+		}
+		v.reset(OpPPC64MOVDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: is32BitInt(val.Type)
+	// result: (MOVWstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32BitInt(val.Type)) {
+			break
+		}
+		v.reset(OpPPC64MOVWstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [2] ptr val mem)
+	// cond:
+	// result: (MOVHstore ptr val mem)
+	for {
+		if v.AuxInt != 2 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpPPC64MOVHstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [1] ptr val mem)
+	// cond:
+	// result: (MOVBstore ptr val mem)
+	for {
+		if v.AuxInt != 1 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpPPC64MOVBstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpSub16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub16  x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSub32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32  x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSub32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32F x y)
+	// cond:
+	// result: (FSUBS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FSUBS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSub64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64  x y)
+	// cond:
+	// result: (SUB  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSub64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64F x y)
+	// cond:
+	// result: (FSUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64FSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSub8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub8   x y)
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpSubPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SubPtr x y)
+	// cond:
+	// result: (SUB  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64SUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpTrunc16to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc16to8  x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpTrunc32to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to16 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpTrunc32to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to8  x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpTrunc64to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to16 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpTrunc64to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to32 x)
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVWreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpTrunc64to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to8  x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpXor16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor16 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpXor32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor32 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpXor64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor64 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpXor8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor8  x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpPPC64XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValuePPC64_OpZero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zero [s] _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstorezero destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpPPC64MOVBstorezero)
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstorezero destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVHstorezero)
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVBstorezero [1] destptr 		(MOVBstorezero [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpPPC64MOVBstorezero)
+		v.AuxInt = 1
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVBstorezero, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstorezero destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVWstorezero)
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstorezero [2] destptr 		(MOVHstorezero [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVHstorezero)
+		v.AuxInt = 2
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVHstorezero, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVBstorezero [3] destptr 		(MOVBstorezero [2] destptr 			(MOVBstorezero [1] destptr 				(MOVBstorezero [0] destptr mem))))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpPPC64MOVBstorezero)
+		v.AuxInt = 3
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVBstorezero, TypeMem)
+		v0.AuxInt = 2
+		v0.AddArg(destptr)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVBstorezero, TypeMem)
+		v1.AuxInt = 1
+		v1.AddArg(destptr)
+		v2 := b.NewValue0(v.Line, OpPPC64MOVBstorezero, TypeMem)
+		v2.AuxInt = 0
+		v2.AddArg(destptr)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVDstorezero [0] destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVDstorezero)
+		v.AuxInt = 0
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0
+	// result: (MOVWstorezero [4] destptr 		(MOVWstorezero [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%4 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVWstorezero)
+		v.AuxInt = 4
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVWstorezero, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0
+	// result: (MOVHstorezero [6] destptr 		(MOVHstorezero [4] destptr 			(MOVHstorezero [2] destptr 				(MOVHstorezero [0] destptr mem))))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 8 && SizeAndAlign(s).Align()%2 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVHstorezero)
+		v.AuxInt = 6
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVHstorezero, TypeMem)
+		v0.AuxInt = 4
+		v0.AddArg(destptr)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVHstorezero, TypeMem)
+		v1.AuxInt = 2
+		v1.AddArg(destptr)
+		v2 := b.NewValue0(v.Line, OpPPC64MOVHstorezero, TypeMem)
+		v2.AuxInt = 0
+		v2.AddArg(destptr)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstorezero [2] destptr 		(MOVBstorezero [1] destptr 			(MOVBstorezero [0] destptr mem)))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpPPC64MOVBstorezero)
+		v.AuxInt = 2
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVBstorezero, TypeMem)
+		v0.AuxInt = 1
+		v0.AddArg(destptr)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVBstorezero, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(destptr)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVDstorezero [8] destptr                 (MOVDstorezero [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 16 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVDstorezero)
+		v.AuxInt = 8
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVDstorezero, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 24 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVDstorezero [16] destptr 		(MOVDstorezero [8] destptr 			(MOVDstorezero [0] destptr mem)))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 24 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVDstorezero)
+		v.AuxInt = 16
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVDstorezero, TypeMem)
+		v0.AuxInt = 8
+		v0.AddArg(destptr)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVDstorezero, TypeMem)
+		v1.AuxInt = 0
+		v1.AddArg(destptr)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 32 && SizeAndAlign(s).Align()%8 == 0
+	// result: (MOVDstorezero [24] destptr 		(MOVDstorezero [16] destptr 			(MOVDstorezero [8] destptr 				(MOVDstorezero [0] destptr mem))))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 32 && SizeAndAlign(s).Align()%8 == 0) {
+			break
+		}
+		v.reset(OpPPC64MOVDstorezero)
+		v.AuxInt = 24
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpPPC64MOVDstorezero, TypeMem)
+		v0.AuxInt = 16
+		v0.AddArg(destptr)
+		v1 := b.NewValue0(v.Line, OpPPC64MOVDstorezero, TypeMem)
+		v1.AuxInt = 8
+		v1.AddArg(destptr)
+		v2 := b.NewValue0(v.Line, OpPPC64MOVDstorezero, TypeMem)
+		v2.AuxInt = 0
+		v2.AddArg(destptr)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] ptr mem)
+	// cond: (SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%8 != 0
+	// result: (LoweredZero [SizeAndAlign(s).Align()] 		ptr 		(ADDconst <ptr.Type> ptr [SizeAndAlign(s).Size()-moveSize(SizeAndAlign(s).Align(), config)]) 		mem)
+	for {
+		s := v.AuxInt
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !((SizeAndAlign(s).Size() > 512 || config.noDuffDevice) || SizeAndAlign(s).Align()%8 != 0) {
+			break
+		}
+		v.reset(OpPPC64LoweredZero)
+		v.AuxInt = SizeAndAlign(s).Align()
+		v.AddArg(ptr)
+		v0 := b.NewValue0(v.Line, OpPPC64ADDconst, ptr.Type)
+		v0.AuxInt = SizeAndAlign(s).Size() - moveSize(SizeAndAlign(s).Align(), config)
+		v0.AddArg(ptr)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuePPC64_OpZeroExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to32 x)
+	// cond:
+	// result: (MOVHZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVHZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpZeroExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to64 x)
+	// cond:
+	// result: (MOVHZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVHZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpZeroExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt32to64 x)
+	// cond:
+	// result: (MOVWZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVWZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpZeroExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to16  x)
+	// cond:
+	// result: (MOVBZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVBZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpZeroExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to32  x)
+	// cond:
+	// result: (MOVBZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVBZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuePPC64_OpZeroExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to64  x)
+	// cond:
+	// result: (MOVBZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpPPC64MOVBZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteBlockPPC64(b *Block, config *Config) bool {
+	switch b.Kind {
+	case BlockPPC64EQ:
+		// match: (EQ (CMPconst [0] (ANDconst [c] x)) yes no)
+		// cond:
+		// result: (EQ (ANDCCconst [c] x) yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64ANDconst {
+				break
+			}
+			c := v_0.AuxInt
+			x := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64EQ
+			v0 := b.NewValue0(v.Line, OpPPC64ANDCCconst, TypeFlags)
+			v0.AuxInt = c
+			v0.AddArg(x)
+			b.SetControl(v0)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (CMPWconst [0] (ANDconst [c] x)) yes no)
+		// cond:
+		// result: (EQ (ANDCCconst [c] x) yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64ANDconst {
+				break
+			}
+			c := v_0.AuxInt
+			x := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64EQ
+			v0 := b.NewValue0(v.Line, OpPPC64ANDCCconst, TypeFlags)
+			v0.AuxInt = c
+			v0.AddArg(x)
+			b.SetControl(v0)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FlagLT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (InvertFlags cmp) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64EQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockPPC64GE:
+		// match: (GE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (FlagLT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GE (FlagGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (LE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64LE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockPPC64GT:
+		// match: (GT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagLT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (LT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64LT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockIf:
+		// match: (If (Equal cc) yes no)
+		// cond:
+		// result: (EQ cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64Equal {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64EQ
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (NotEqual cc) yes no)
+		// cond:
+		// result: (NE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64NotEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64NE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (LessThan cc) yes no)
+		// cond:
+		// result: (LT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64LessThan {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64LT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (LessEqual cc) yes no)
+		// cond:
+		// result: (LE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64LessEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64LE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (GreaterThan cc) yes no)
+		// cond:
+		// result: (GT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64GreaterThan {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64GT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (GreaterEqual cc) yes no)
+		// cond:
+		// result: (GE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64GreaterEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64GE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (FLessThan cc) yes no)
+		// cond:
+		// result: (FLT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FLessThan {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64FLT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (FLessEqual cc) yes no)
+		// cond:
+		// result: (FLE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FLessEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64FLE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (FGreaterThan cc) yes no)
+		// cond:
+		// result: (FGT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FGreaterThan {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64FGT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (FGreaterEqual cc) yes no)
+		// cond:
+		// result: (FGE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FGreaterEqual {
+				break
+			}
+			cc := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64FGE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If cond yes no)
+		// cond:
+		// result: (NE (CMPWconst [0] cond) yes no)
+		for {
+			v := b.Control
+			_ = v
+			cond := b.Control
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64NE
+			v0 := b.NewValue0(v.Line, OpPPC64CMPWconst, TypeFlags)
+			v0.AuxInt = 0
+			v0.AddArg(cond)
+			b.SetControl(v0)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockPPC64LE:
+		// match: (LE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagLT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (GE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64GE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockPPC64LT:
+		// match: (LT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (FlagLT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LT (FlagGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (GT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64GT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockPPC64NE:
+		// match: (NE (CMPWconst [0] (Equal cc)) yes no)
+		// cond:
+		// result: (EQ cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64Equal {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64EQ
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (NotEqual cc)) yes no)
+		// cond:
+		// result: (NE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64NotEqual {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64NE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (LessThan cc)) yes no)
+		// cond:
+		// result: (LT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64LessThan {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64LT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (LessEqual cc)) yes no)
+		// cond:
+		// result: (LE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64LessEqual {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64LE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (GreaterThan cc)) yes no)
+		// cond:
+		// result: (GT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64GreaterThan {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64GT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (GreaterEqual cc)) yes no)
+		// cond:
+		// result: (GE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64GreaterEqual {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64GE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (FLessThan cc)) yes no)
+		// cond:
+		// result: (FLT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64FLessThan {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64FLT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (FLessEqual cc)) yes no)
+		// cond:
+		// result: (FLE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64FLessEqual {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64FLE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (FGreaterThan cc)) yes no)
+		// cond:
+		// result: (FGT cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64FGreaterThan {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64FGT
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (FGreaterEqual cc)) yes no)
+		// cond:
+		// result: (FGE cc yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64FGreaterEqual {
+				break
+			}
+			cc := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64FGE
+			b.SetControl(cc)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPconst [0] (ANDconst [c] x)) yes no)
+		// cond:
+		// result: (NE (ANDCCconst [c] x) yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64ANDconst {
+				break
+			}
+			c := v_0.AuxInt
+			x := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64NE
+			v0 := b.NewValue0(v.Line, OpPPC64ANDCCconst, TypeFlags)
+			v0.AuxInt = c
+			v0.AddArg(x)
+			b.SetControl(v0)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (ANDconst [c] x)) yes no)
+		// cond:
+		// result: (NE (ANDCCconst [c] x) yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64CMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpPPC64ANDconst {
+				break
+			}
+			c := v_0.AuxInt
+			x := v_0.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64NE
+			v0 := b.NewValue0(v.Line, OpPPC64ANDCCconst, TypeFlags)
+			v0.AuxInt = c
+			v0.AddArg(x)
+			b.SetControl(v0)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (NE (FlagLT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64FlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpPPC64InvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockPPC64NE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/cmd/compile/internal/ssa/rewriteS390X.go b/src/cmd/compile/internal/ssa/rewriteS390X.go
new file mode 100644
index 0000000..7d023bc
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/rewriteS390X.go
@@ -0,0 +1,18694 @@
+// autogenerated from gen/S390X.rules: do not edit!
+// generated with: cd gen; go run *.go
+
+package ssa
+
+import "math"
+
+var _ = math.MinInt8 // in case not otherwise used
+func rewriteValueS390X(v *Value, config *Config) bool {
+	switch v.Op {
+	case OpAdd16:
+		return rewriteValueS390X_OpAdd16(v, config)
+	case OpAdd32:
+		return rewriteValueS390X_OpAdd32(v, config)
+	case OpAdd32F:
+		return rewriteValueS390X_OpAdd32F(v, config)
+	case OpAdd64:
+		return rewriteValueS390X_OpAdd64(v, config)
+	case OpAdd64F:
+		return rewriteValueS390X_OpAdd64F(v, config)
+	case OpAdd8:
+		return rewriteValueS390X_OpAdd8(v, config)
+	case OpAddPtr:
+		return rewriteValueS390X_OpAddPtr(v, config)
+	case OpAddr:
+		return rewriteValueS390X_OpAddr(v, config)
+	case OpAnd16:
+		return rewriteValueS390X_OpAnd16(v, config)
+	case OpAnd32:
+		return rewriteValueS390X_OpAnd32(v, config)
+	case OpAnd64:
+		return rewriteValueS390X_OpAnd64(v, config)
+	case OpAnd8:
+		return rewriteValueS390X_OpAnd8(v, config)
+	case OpAndB:
+		return rewriteValueS390X_OpAndB(v, config)
+	case OpAtomicAdd32:
+		return rewriteValueS390X_OpAtomicAdd32(v, config)
+	case OpAtomicAdd64:
+		return rewriteValueS390X_OpAtomicAdd64(v, config)
+	case OpAtomicCompareAndSwap32:
+		return rewriteValueS390X_OpAtomicCompareAndSwap32(v, config)
+	case OpAtomicCompareAndSwap64:
+		return rewriteValueS390X_OpAtomicCompareAndSwap64(v, config)
+	case OpAtomicExchange32:
+		return rewriteValueS390X_OpAtomicExchange32(v, config)
+	case OpAtomicExchange64:
+		return rewriteValueS390X_OpAtomicExchange64(v, config)
+	case OpAtomicLoad32:
+		return rewriteValueS390X_OpAtomicLoad32(v, config)
+	case OpAtomicLoad64:
+		return rewriteValueS390X_OpAtomicLoad64(v, config)
+	case OpAtomicLoadPtr:
+		return rewriteValueS390X_OpAtomicLoadPtr(v, config)
+	case OpAtomicStore32:
+		return rewriteValueS390X_OpAtomicStore32(v, config)
+	case OpAtomicStore64:
+		return rewriteValueS390X_OpAtomicStore64(v, config)
+	case OpAtomicStorePtrNoWB:
+		return rewriteValueS390X_OpAtomicStorePtrNoWB(v, config)
+	case OpAvg64u:
+		return rewriteValueS390X_OpAvg64u(v, config)
+	case OpBswap32:
+		return rewriteValueS390X_OpBswap32(v, config)
+	case OpBswap64:
+		return rewriteValueS390X_OpBswap64(v, config)
+	case OpClosureCall:
+		return rewriteValueS390X_OpClosureCall(v, config)
+	case OpCom16:
+		return rewriteValueS390X_OpCom16(v, config)
+	case OpCom32:
+		return rewriteValueS390X_OpCom32(v, config)
+	case OpCom64:
+		return rewriteValueS390X_OpCom64(v, config)
+	case OpCom8:
+		return rewriteValueS390X_OpCom8(v, config)
+	case OpConst16:
+		return rewriteValueS390X_OpConst16(v, config)
+	case OpConst32:
+		return rewriteValueS390X_OpConst32(v, config)
+	case OpConst32F:
+		return rewriteValueS390X_OpConst32F(v, config)
+	case OpConst64:
+		return rewriteValueS390X_OpConst64(v, config)
+	case OpConst64F:
+		return rewriteValueS390X_OpConst64F(v, config)
+	case OpConst8:
+		return rewriteValueS390X_OpConst8(v, config)
+	case OpConstBool:
+		return rewriteValueS390X_OpConstBool(v, config)
+	case OpConstNil:
+		return rewriteValueS390X_OpConstNil(v, config)
+	case OpConvert:
+		return rewriteValueS390X_OpConvert(v, config)
+	case OpCtz32:
+		return rewriteValueS390X_OpCtz32(v, config)
+	case OpCtz64:
+		return rewriteValueS390X_OpCtz64(v, config)
+	case OpCvt32Fto32:
+		return rewriteValueS390X_OpCvt32Fto32(v, config)
+	case OpCvt32Fto64:
+		return rewriteValueS390X_OpCvt32Fto64(v, config)
+	case OpCvt32Fto64F:
+		return rewriteValueS390X_OpCvt32Fto64F(v, config)
+	case OpCvt32to32F:
+		return rewriteValueS390X_OpCvt32to32F(v, config)
+	case OpCvt32to64F:
+		return rewriteValueS390X_OpCvt32to64F(v, config)
+	case OpCvt64Fto32:
+		return rewriteValueS390X_OpCvt64Fto32(v, config)
+	case OpCvt64Fto32F:
+		return rewriteValueS390X_OpCvt64Fto32F(v, config)
+	case OpCvt64Fto64:
+		return rewriteValueS390X_OpCvt64Fto64(v, config)
+	case OpCvt64to32F:
+		return rewriteValueS390X_OpCvt64to32F(v, config)
+	case OpCvt64to64F:
+		return rewriteValueS390X_OpCvt64to64F(v, config)
+	case OpDeferCall:
+		return rewriteValueS390X_OpDeferCall(v, config)
+	case OpDiv16:
+		return rewriteValueS390X_OpDiv16(v, config)
+	case OpDiv16u:
+		return rewriteValueS390X_OpDiv16u(v, config)
+	case OpDiv32:
+		return rewriteValueS390X_OpDiv32(v, config)
+	case OpDiv32F:
+		return rewriteValueS390X_OpDiv32F(v, config)
+	case OpDiv32u:
+		return rewriteValueS390X_OpDiv32u(v, config)
+	case OpDiv64:
+		return rewriteValueS390X_OpDiv64(v, config)
+	case OpDiv64F:
+		return rewriteValueS390X_OpDiv64F(v, config)
+	case OpDiv64u:
+		return rewriteValueS390X_OpDiv64u(v, config)
+	case OpDiv8:
+		return rewriteValueS390X_OpDiv8(v, config)
+	case OpDiv8u:
+		return rewriteValueS390X_OpDiv8u(v, config)
+	case OpEq16:
+		return rewriteValueS390X_OpEq16(v, config)
+	case OpEq32:
+		return rewriteValueS390X_OpEq32(v, config)
+	case OpEq32F:
+		return rewriteValueS390X_OpEq32F(v, config)
+	case OpEq64:
+		return rewriteValueS390X_OpEq64(v, config)
+	case OpEq64F:
+		return rewriteValueS390X_OpEq64F(v, config)
+	case OpEq8:
+		return rewriteValueS390X_OpEq8(v, config)
+	case OpEqB:
+		return rewriteValueS390X_OpEqB(v, config)
+	case OpEqPtr:
+		return rewriteValueS390X_OpEqPtr(v, config)
+	case OpGeq16:
+		return rewriteValueS390X_OpGeq16(v, config)
+	case OpGeq16U:
+		return rewriteValueS390X_OpGeq16U(v, config)
+	case OpGeq32:
+		return rewriteValueS390X_OpGeq32(v, config)
+	case OpGeq32F:
+		return rewriteValueS390X_OpGeq32F(v, config)
+	case OpGeq32U:
+		return rewriteValueS390X_OpGeq32U(v, config)
+	case OpGeq64:
+		return rewriteValueS390X_OpGeq64(v, config)
+	case OpGeq64F:
+		return rewriteValueS390X_OpGeq64F(v, config)
+	case OpGeq64U:
+		return rewriteValueS390X_OpGeq64U(v, config)
+	case OpGeq8:
+		return rewriteValueS390X_OpGeq8(v, config)
+	case OpGeq8U:
+		return rewriteValueS390X_OpGeq8U(v, config)
+	case OpGetClosurePtr:
+		return rewriteValueS390X_OpGetClosurePtr(v, config)
+	case OpGetG:
+		return rewriteValueS390X_OpGetG(v, config)
+	case OpGoCall:
+		return rewriteValueS390X_OpGoCall(v, config)
+	case OpGreater16:
+		return rewriteValueS390X_OpGreater16(v, config)
+	case OpGreater16U:
+		return rewriteValueS390X_OpGreater16U(v, config)
+	case OpGreater32:
+		return rewriteValueS390X_OpGreater32(v, config)
+	case OpGreater32F:
+		return rewriteValueS390X_OpGreater32F(v, config)
+	case OpGreater32U:
+		return rewriteValueS390X_OpGreater32U(v, config)
+	case OpGreater64:
+		return rewriteValueS390X_OpGreater64(v, config)
+	case OpGreater64F:
+		return rewriteValueS390X_OpGreater64F(v, config)
+	case OpGreater64U:
+		return rewriteValueS390X_OpGreater64U(v, config)
+	case OpGreater8:
+		return rewriteValueS390X_OpGreater8(v, config)
+	case OpGreater8U:
+		return rewriteValueS390X_OpGreater8U(v, config)
+	case OpHmul16:
+		return rewriteValueS390X_OpHmul16(v, config)
+	case OpHmul16u:
+		return rewriteValueS390X_OpHmul16u(v, config)
+	case OpHmul32:
+		return rewriteValueS390X_OpHmul32(v, config)
+	case OpHmul32u:
+		return rewriteValueS390X_OpHmul32u(v, config)
+	case OpHmul64:
+		return rewriteValueS390X_OpHmul64(v, config)
+	case OpHmul64u:
+		return rewriteValueS390X_OpHmul64u(v, config)
+	case OpHmul8:
+		return rewriteValueS390X_OpHmul8(v, config)
+	case OpHmul8u:
+		return rewriteValueS390X_OpHmul8u(v, config)
+	case OpITab:
+		return rewriteValueS390X_OpITab(v, config)
+	case OpInterCall:
+		return rewriteValueS390X_OpInterCall(v, config)
+	case OpIsInBounds:
+		return rewriteValueS390X_OpIsInBounds(v, config)
+	case OpIsNonNil:
+		return rewriteValueS390X_OpIsNonNil(v, config)
+	case OpIsSliceInBounds:
+		return rewriteValueS390X_OpIsSliceInBounds(v, config)
+	case OpLeq16:
+		return rewriteValueS390X_OpLeq16(v, config)
+	case OpLeq16U:
+		return rewriteValueS390X_OpLeq16U(v, config)
+	case OpLeq32:
+		return rewriteValueS390X_OpLeq32(v, config)
+	case OpLeq32F:
+		return rewriteValueS390X_OpLeq32F(v, config)
+	case OpLeq32U:
+		return rewriteValueS390X_OpLeq32U(v, config)
+	case OpLeq64:
+		return rewriteValueS390X_OpLeq64(v, config)
+	case OpLeq64F:
+		return rewriteValueS390X_OpLeq64F(v, config)
+	case OpLeq64U:
+		return rewriteValueS390X_OpLeq64U(v, config)
+	case OpLeq8:
+		return rewriteValueS390X_OpLeq8(v, config)
+	case OpLeq8U:
+		return rewriteValueS390X_OpLeq8U(v, config)
+	case OpLess16:
+		return rewriteValueS390X_OpLess16(v, config)
+	case OpLess16U:
+		return rewriteValueS390X_OpLess16U(v, config)
+	case OpLess32:
+		return rewriteValueS390X_OpLess32(v, config)
+	case OpLess32F:
+		return rewriteValueS390X_OpLess32F(v, config)
+	case OpLess32U:
+		return rewriteValueS390X_OpLess32U(v, config)
+	case OpLess64:
+		return rewriteValueS390X_OpLess64(v, config)
+	case OpLess64F:
+		return rewriteValueS390X_OpLess64F(v, config)
+	case OpLess64U:
+		return rewriteValueS390X_OpLess64U(v, config)
+	case OpLess8:
+		return rewriteValueS390X_OpLess8(v, config)
+	case OpLess8U:
+		return rewriteValueS390X_OpLess8U(v, config)
+	case OpLoad:
+		return rewriteValueS390X_OpLoad(v, config)
+	case OpLrot32:
+		return rewriteValueS390X_OpLrot32(v, config)
+	case OpLrot64:
+		return rewriteValueS390X_OpLrot64(v, config)
+	case OpLsh16x16:
+		return rewriteValueS390X_OpLsh16x16(v, config)
+	case OpLsh16x32:
+		return rewriteValueS390X_OpLsh16x32(v, config)
+	case OpLsh16x64:
+		return rewriteValueS390X_OpLsh16x64(v, config)
+	case OpLsh16x8:
+		return rewriteValueS390X_OpLsh16x8(v, config)
+	case OpLsh32x16:
+		return rewriteValueS390X_OpLsh32x16(v, config)
+	case OpLsh32x32:
+		return rewriteValueS390X_OpLsh32x32(v, config)
+	case OpLsh32x64:
+		return rewriteValueS390X_OpLsh32x64(v, config)
+	case OpLsh32x8:
+		return rewriteValueS390X_OpLsh32x8(v, config)
+	case OpLsh64x16:
+		return rewriteValueS390X_OpLsh64x16(v, config)
+	case OpLsh64x32:
+		return rewriteValueS390X_OpLsh64x32(v, config)
+	case OpLsh64x64:
+		return rewriteValueS390X_OpLsh64x64(v, config)
+	case OpLsh64x8:
+		return rewriteValueS390X_OpLsh64x8(v, config)
+	case OpLsh8x16:
+		return rewriteValueS390X_OpLsh8x16(v, config)
+	case OpLsh8x32:
+		return rewriteValueS390X_OpLsh8x32(v, config)
+	case OpLsh8x64:
+		return rewriteValueS390X_OpLsh8x64(v, config)
+	case OpLsh8x8:
+		return rewriteValueS390X_OpLsh8x8(v, config)
+	case OpMod16:
+		return rewriteValueS390X_OpMod16(v, config)
+	case OpMod16u:
+		return rewriteValueS390X_OpMod16u(v, config)
+	case OpMod32:
+		return rewriteValueS390X_OpMod32(v, config)
+	case OpMod32u:
+		return rewriteValueS390X_OpMod32u(v, config)
+	case OpMod64:
+		return rewriteValueS390X_OpMod64(v, config)
+	case OpMod64u:
+		return rewriteValueS390X_OpMod64u(v, config)
+	case OpMod8:
+		return rewriteValueS390X_OpMod8(v, config)
+	case OpMod8u:
+		return rewriteValueS390X_OpMod8u(v, config)
+	case OpMove:
+		return rewriteValueS390X_OpMove(v, config)
+	case OpMul16:
+		return rewriteValueS390X_OpMul16(v, config)
+	case OpMul32:
+		return rewriteValueS390X_OpMul32(v, config)
+	case OpMul32F:
+		return rewriteValueS390X_OpMul32F(v, config)
+	case OpMul64:
+		return rewriteValueS390X_OpMul64(v, config)
+	case OpMul64F:
+		return rewriteValueS390X_OpMul64F(v, config)
+	case OpMul8:
+		return rewriteValueS390X_OpMul8(v, config)
+	case OpNeg16:
+		return rewriteValueS390X_OpNeg16(v, config)
+	case OpNeg32:
+		return rewriteValueS390X_OpNeg32(v, config)
+	case OpNeg32F:
+		return rewriteValueS390X_OpNeg32F(v, config)
+	case OpNeg64:
+		return rewriteValueS390X_OpNeg64(v, config)
+	case OpNeg64F:
+		return rewriteValueS390X_OpNeg64F(v, config)
+	case OpNeg8:
+		return rewriteValueS390X_OpNeg8(v, config)
+	case OpNeq16:
+		return rewriteValueS390X_OpNeq16(v, config)
+	case OpNeq32:
+		return rewriteValueS390X_OpNeq32(v, config)
+	case OpNeq32F:
+		return rewriteValueS390X_OpNeq32F(v, config)
+	case OpNeq64:
+		return rewriteValueS390X_OpNeq64(v, config)
+	case OpNeq64F:
+		return rewriteValueS390X_OpNeq64F(v, config)
+	case OpNeq8:
+		return rewriteValueS390X_OpNeq8(v, config)
+	case OpNeqB:
+		return rewriteValueS390X_OpNeqB(v, config)
+	case OpNeqPtr:
+		return rewriteValueS390X_OpNeqPtr(v, config)
+	case OpNilCheck:
+		return rewriteValueS390X_OpNilCheck(v, config)
+	case OpNot:
+		return rewriteValueS390X_OpNot(v, config)
+	case OpOffPtr:
+		return rewriteValueS390X_OpOffPtr(v, config)
+	case OpOr16:
+		return rewriteValueS390X_OpOr16(v, config)
+	case OpOr32:
+		return rewriteValueS390X_OpOr32(v, config)
+	case OpOr64:
+		return rewriteValueS390X_OpOr64(v, config)
+	case OpOr8:
+		return rewriteValueS390X_OpOr8(v, config)
+	case OpOrB:
+		return rewriteValueS390X_OpOrB(v, config)
+	case OpRsh16Ux16:
+		return rewriteValueS390X_OpRsh16Ux16(v, config)
+	case OpRsh16Ux32:
+		return rewriteValueS390X_OpRsh16Ux32(v, config)
+	case OpRsh16Ux64:
+		return rewriteValueS390X_OpRsh16Ux64(v, config)
+	case OpRsh16Ux8:
+		return rewriteValueS390X_OpRsh16Ux8(v, config)
+	case OpRsh16x16:
+		return rewriteValueS390X_OpRsh16x16(v, config)
+	case OpRsh16x32:
+		return rewriteValueS390X_OpRsh16x32(v, config)
+	case OpRsh16x64:
+		return rewriteValueS390X_OpRsh16x64(v, config)
+	case OpRsh16x8:
+		return rewriteValueS390X_OpRsh16x8(v, config)
+	case OpRsh32Ux16:
+		return rewriteValueS390X_OpRsh32Ux16(v, config)
+	case OpRsh32Ux32:
+		return rewriteValueS390X_OpRsh32Ux32(v, config)
+	case OpRsh32Ux64:
+		return rewriteValueS390X_OpRsh32Ux64(v, config)
+	case OpRsh32Ux8:
+		return rewriteValueS390X_OpRsh32Ux8(v, config)
+	case OpRsh32x16:
+		return rewriteValueS390X_OpRsh32x16(v, config)
+	case OpRsh32x32:
+		return rewriteValueS390X_OpRsh32x32(v, config)
+	case OpRsh32x64:
+		return rewriteValueS390X_OpRsh32x64(v, config)
+	case OpRsh32x8:
+		return rewriteValueS390X_OpRsh32x8(v, config)
+	case OpRsh64Ux16:
+		return rewriteValueS390X_OpRsh64Ux16(v, config)
+	case OpRsh64Ux32:
+		return rewriteValueS390X_OpRsh64Ux32(v, config)
+	case OpRsh64Ux64:
+		return rewriteValueS390X_OpRsh64Ux64(v, config)
+	case OpRsh64Ux8:
+		return rewriteValueS390X_OpRsh64Ux8(v, config)
+	case OpRsh64x16:
+		return rewriteValueS390X_OpRsh64x16(v, config)
+	case OpRsh64x32:
+		return rewriteValueS390X_OpRsh64x32(v, config)
+	case OpRsh64x64:
+		return rewriteValueS390X_OpRsh64x64(v, config)
+	case OpRsh64x8:
+		return rewriteValueS390X_OpRsh64x8(v, config)
+	case OpRsh8Ux16:
+		return rewriteValueS390X_OpRsh8Ux16(v, config)
+	case OpRsh8Ux32:
+		return rewriteValueS390X_OpRsh8Ux32(v, config)
+	case OpRsh8Ux64:
+		return rewriteValueS390X_OpRsh8Ux64(v, config)
+	case OpRsh8Ux8:
+		return rewriteValueS390X_OpRsh8Ux8(v, config)
+	case OpRsh8x16:
+		return rewriteValueS390X_OpRsh8x16(v, config)
+	case OpRsh8x32:
+		return rewriteValueS390X_OpRsh8x32(v, config)
+	case OpRsh8x64:
+		return rewriteValueS390X_OpRsh8x64(v, config)
+	case OpRsh8x8:
+		return rewriteValueS390X_OpRsh8x8(v, config)
+	case OpS390XADD:
+		return rewriteValueS390X_OpS390XADD(v, config)
+	case OpS390XADDW:
+		return rewriteValueS390X_OpS390XADDW(v, config)
+	case OpS390XADDWconst:
+		return rewriteValueS390X_OpS390XADDWconst(v, config)
+	case OpS390XADDconst:
+		return rewriteValueS390X_OpS390XADDconst(v, config)
+	case OpS390XAND:
+		return rewriteValueS390X_OpS390XAND(v, config)
+	case OpS390XANDW:
+		return rewriteValueS390X_OpS390XANDW(v, config)
+	case OpS390XANDWconst:
+		return rewriteValueS390X_OpS390XANDWconst(v, config)
+	case OpS390XANDconst:
+		return rewriteValueS390X_OpS390XANDconst(v, config)
+	case OpS390XCMP:
+		return rewriteValueS390X_OpS390XCMP(v, config)
+	case OpS390XCMPU:
+		return rewriteValueS390X_OpS390XCMPU(v, config)
+	case OpS390XCMPUconst:
+		return rewriteValueS390X_OpS390XCMPUconst(v, config)
+	case OpS390XCMPW:
+		return rewriteValueS390X_OpS390XCMPW(v, config)
+	case OpS390XCMPWU:
+		return rewriteValueS390X_OpS390XCMPWU(v, config)
+	case OpS390XCMPWUconst:
+		return rewriteValueS390X_OpS390XCMPWUconst(v, config)
+	case OpS390XCMPWconst:
+		return rewriteValueS390X_OpS390XCMPWconst(v, config)
+	case OpS390XCMPconst:
+		return rewriteValueS390X_OpS390XCMPconst(v, config)
+	case OpS390XFMOVDload:
+		return rewriteValueS390X_OpS390XFMOVDload(v, config)
+	case OpS390XFMOVDloadidx:
+		return rewriteValueS390X_OpS390XFMOVDloadidx(v, config)
+	case OpS390XFMOVDstore:
+		return rewriteValueS390X_OpS390XFMOVDstore(v, config)
+	case OpS390XFMOVDstoreidx:
+		return rewriteValueS390X_OpS390XFMOVDstoreidx(v, config)
+	case OpS390XFMOVSload:
+		return rewriteValueS390X_OpS390XFMOVSload(v, config)
+	case OpS390XFMOVSloadidx:
+		return rewriteValueS390X_OpS390XFMOVSloadidx(v, config)
+	case OpS390XFMOVSstore:
+		return rewriteValueS390X_OpS390XFMOVSstore(v, config)
+	case OpS390XFMOVSstoreidx:
+		return rewriteValueS390X_OpS390XFMOVSstoreidx(v, config)
+	case OpS390XMOVBZload:
+		return rewriteValueS390X_OpS390XMOVBZload(v, config)
+	case OpS390XMOVBZloadidx:
+		return rewriteValueS390X_OpS390XMOVBZloadidx(v, config)
+	case OpS390XMOVBZreg:
+		return rewriteValueS390X_OpS390XMOVBZreg(v, config)
+	case OpS390XMOVBload:
+		return rewriteValueS390X_OpS390XMOVBload(v, config)
+	case OpS390XMOVBreg:
+		return rewriteValueS390X_OpS390XMOVBreg(v, config)
+	case OpS390XMOVBstore:
+		return rewriteValueS390X_OpS390XMOVBstore(v, config)
+	case OpS390XMOVBstoreconst:
+		return rewriteValueS390X_OpS390XMOVBstoreconst(v, config)
+	case OpS390XMOVBstoreidx:
+		return rewriteValueS390X_OpS390XMOVBstoreidx(v, config)
+	case OpS390XMOVDEQ:
+		return rewriteValueS390X_OpS390XMOVDEQ(v, config)
+	case OpS390XMOVDGE:
+		return rewriteValueS390X_OpS390XMOVDGE(v, config)
+	case OpS390XMOVDGT:
+		return rewriteValueS390X_OpS390XMOVDGT(v, config)
+	case OpS390XMOVDLE:
+		return rewriteValueS390X_OpS390XMOVDLE(v, config)
+	case OpS390XMOVDLT:
+		return rewriteValueS390X_OpS390XMOVDLT(v, config)
+	case OpS390XMOVDNE:
+		return rewriteValueS390X_OpS390XMOVDNE(v, config)
+	case OpS390XMOVDaddridx:
+		return rewriteValueS390X_OpS390XMOVDaddridx(v, config)
+	case OpS390XMOVDload:
+		return rewriteValueS390X_OpS390XMOVDload(v, config)
+	case OpS390XMOVDloadidx:
+		return rewriteValueS390X_OpS390XMOVDloadidx(v, config)
+	case OpS390XMOVDstore:
+		return rewriteValueS390X_OpS390XMOVDstore(v, config)
+	case OpS390XMOVDstoreconst:
+		return rewriteValueS390X_OpS390XMOVDstoreconst(v, config)
+	case OpS390XMOVDstoreidx:
+		return rewriteValueS390X_OpS390XMOVDstoreidx(v, config)
+	case OpS390XMOVHBRstore:
+		return rewriteValueS390X_OpS390XMOVHBRstore(v, config)
+	case OpS390XMOVHBRstoreidx:
+		return rewriteValueS390X_OpS390XMOVHBRstoreidx(v, config)
+	case OpS390XMOVHZload:
+		return rewriteValueS390X_OpS390XMOVHZload(v, config)
+	case OpS390XMOVHZloadidx:
+		return rewriteValueS390X_OpS390XMOVHZloadidx(v, config)
+	case OpS390XMOVHZreg:
+		return rewriteValueS390X_OpS390XMOVHZreg(v, config)
+	case OpS390XMOVHload:
+		return rewriteValueS390X_OpS390XMOVHload(v, config)
+	case OpS390XMOVHreg:
+		return rewriteValueS390X_OpS390XMOVHreg(v, config)
+	case OpS390XMOVHstore:
+		return rewriteValueS390X_OpS390XMOVHstore(v, config)
+	case OpS390XMOVHstoreconst:
+		return rewriteValueS390X_OpS390XMOVHstoreconst(v, config)
+	case OpS390XMOVHstoreidx:
+		return rewriteValueS390X_OpS390XMOVHstoreidx(v, config)
+	case OpS390XMOVWBRstore:
+		return rewriteValueS390X_OpS390XMOVWBRstore(v, config)
+	case OpS390XMOVWBRstoreidx:
+		return rewriteValueS390X_OpS390XMOVWBRstoreidx(v, config)
+	case OpS390XMOVWZload:
+		return rewriteValueS390X_OpS390XMOVWZload(v, config)
+	case OpS390XMOVWZloadidx:
+		return rewriteValueS390X_OpS390XMOVWZloadidx(v, config)
+	case OpS390XMOVWZreg:
+		return rewriteValueS390X_OpS390XMOVWZreg(v, config)
+	case OpS390XMOVWload:
+		return rewriteValueS390X_OpS390XMOVWload(v, config)
+	case OpS390XMOVWreg:
+		return rewriteValueS390X_OpS390XMOVWreg(v, config)
+	case OpS390XMOVWstore:
+		return rewriteValueS390X_OpS390XMOVWstore(v, config)
+	case OpS390XMOVWstoreconst:
+		return rewriteValueS390X_OpS390XMOVWstoreconst(v, config)
+	case OpS390XMOVWstoreidx:
+		return rewriteValueS390X_OpS390XMOVWstoreidx(v, config)
+	case OpS390XMULLD:
+		return rewriteValueS390X_OpS390XMULLD(v, config)
+	case OpS390XMULLDconst:
+		return rewriteValueS390X_OpS390XMULLDconst(v, config)
+	case OpS390XMULLW:
+		return rewriteValueS390X_OpS390XMULLW(v, config)
+	case OpS390XMULLWconst:
+		return rewriteValueS390X_OpS390XMULLWconst(v, config)
+	case OpS390XNEG:
+		return rewriteValueS390X_OpS390XNEG(v, config)
+	case OpS390XNEGW:
+		return rewriteValueS390X_OpS390XNEGW(v, config)
+	case OpS390XNOT:
+		return rewriteValueS390X_OpS390XNOT(v, config)
+	case OpS390XNOTW:
+		return rewriteValueS390X_OpS390XNOTW(v, config)
+	case OpS390XOR:
+		return rewriteValueS390X_OpS390XOR(v, config)
+	case OpS390XORW:
+		return rewriteValueS390X_OpS390XORW(v, config)
+	case OpS390XORWconst:
+		return rewriteValueS390X_OpS390XORWconst(v, config)
+	case OpS390XORconst:
+		return rewriteValueS390X_OpS390XORconst(v, config)
+	case OpS390XSLD:
+		return rewriteValueS390X_OpS390XSLD(v, config)
+	case OpS390XSLW:
+		return rewriteValueS390X_OpS390XSLW(v, config)
+	case OpS390XSRAD:
+		return rewriteValueS390X_OpS390XSRAD(v, config)
+	case OpS390XSRADconst:
+		return rewriteValueS390X_OpS390XSRADconst(v, config)
+	case OpS390XSRAW:
+		return rewriteValueS390X_OpS390XSRAW(v, config)
+	case OpS390XSRAWconst:
+		return rewriteValueS390X_OpS390XSRAWconst(v, config)
+	case OpS390XSRD:
+		return rewriteValueS390X_OpS390XSRD(v, config)
+	case OpS390XSRW:
+		return rewriteValueS390X_OpS390XSRW(v, config)
+	case OpS390XSTM2:
+		return rewriteValueS390X_OpS390XSTM2(v, config)
+	case OpS390XSTMG2:
+		return rewriteValueS390X_OpS390XSTMG2(v, config)
+	case OpS390XSUB:
+		return rewriteValueS390X_OpS390XSUB(v, config)
+	case OpS390XSUBEWcarrymask:
+		return rewriteValueS390X_OpS390XSUBEWcarrymask(v, config)
+	case OpS390XSUBEcarrymask:
+		return rewriteValueS390X_OpS390XSUBEcarrymask(v, config)
+	case OpS390XSUBW:
+		return rewriteValueS390X_OpS390XSUBW(v, config)
+	case OpS390XSUBWconst:
+		return rewriteValueS390X_OpS390XSUBWconst(v, config)
+	case OpS390XSUBconst:
+		return rewriteValueS390X_OpS390XSUBconst(v, config)
+	case OpS390XXOR:
+		return rewriteValueS390X_OpS390XXOR(v, config)
+	case OpS390XXORW:
+		return rewriteValueS390X_OpS390XXORW(v, config)
+	case OpS390XXORWconst:
+		return rewriteValueS390X_OpS390XXORWconst(v, config)
+	case OpS390XXORconst:
+		return rewriteValueS390X_OpS390XXORconst(v, config)
+	case OpSelect0:
+		return rewriteValueS390X_OpSelect0(v, config)
+	case OpSelect1:
+		return rewriteValueS390X_OpSelect1(v, config)
+	case OpSignExt16to32:
+		return rewriteValueS390X_OpSignExt16to32(v, config)
+	case OpSignExt16to64:
+		return rewriteValueS390X_OpSignExt16to64(v, config)
+	case OpSignExt32to64:
+		return rewriteValueS390X_OpSignExt32to64(v, config)
+	case OpSignExt8to16:
+		return rewriteValueS390X_OpSignExt8to16(v, config)
+	case OpSignExt8to32:
+		return rewriteValueS390X_OpSignExt8to32(v, config)
+	case OpSignExt8to64:
+		return rewriteValueS390X_OpSignExt8to64(v, config)
+	case OpSlicemask:
+		return rewriteValueS390X_OpSlicemask(v, config)
+	case OpSqrt:
+		return rewriteValueS390X_OpSqrt(v, config)
+	case OpStaticCall:
+		return rewriteValueS390X_OpStaticCall(v, config)
+	case OpStore:
+		return rewriteValueS390X_OpStore(v, config)
+	case OpSub16:
+		return rewriteValueS390X_OpSub16(v, config)
+	case OpSub32:
+		return rewriteValueS390X_OpSub32(v, config)
+	case OpSub32F:
+		return rewriteValueS390X_OpSub32F(v, config)
+	case OpSub64:
+		return rewriteValueS390X_OpSub64(v, config)
+	case OpSub64F:
+		return rewriteValueS390X_OpSub64F(v, config)
+	case OpSub8:
+		return rewriteValueS390X_OpSub8(v, config)
+	case OpSubPtr:
+		return rewriteValueS390X_OpSubPtr(v, config)
+	case OpTrunc16to8:
+		return rewriteValueS390X_OpTrunc16to8(v, config)
+	case OpTrunc32to16:
+		return rewriteValueS390X_OpTrunc32to16(v, config)
+	case OpTrunc32to8:
+		return rewriteValueS390X_OpTrunc32to8(v, config)
+	case OpTrunc64to16:
+		return rewriteValueS390X_OpTrunc64to16(v, config)
+	case OpTrunc64to32:
+		return rewriteValueS390X_OpTrunc64to32(v, config)
+	case OpTrunc64to8:
+		return rewriteValueS390X_OpTrunc64to8(v, config)
+	case OpXor16:
+		return rewriteValueS390X_OpXor16(v, config)
+	case OpXor32:
+		return rewriteValueS390X_OpXor32(v, config)
+	case OpXor64:
+		return rewriteValueS390X_OpXor64(v, config)
+	case OpXor8:
+		return rewriteValueS390X_OpXor8(v, config)
+	case OpZero:
+		return rewriteValueS390X_OpZero(v, config)
+	case OpZeroExt16to32:
+		return rewriteValueS390X_OpZeroExt16to32(v, config)
+	case OpZeroExt16to64:
+		return rewriteValueS390X_OpZeroExt16to64(v, config)
+	case OpZeroExt32to64:
+		return rewriteValueS390X_OpZeroExt32to64(v, config)
+	case OpZeroExt8to16:
+		return rewriteValueS390X_OpZeroExt8to16(v, config)
+	case OpZeroExt8to32:
+		return rewriteValueS390X_OpZeroExt8to32(v, config)
+	case OpZeroExt8to64:
+		return rewriteValueS390X_OpZeroExt8to64(v, config)
+	}
+	return false
+}
+func rewriteValueS390X_OpAdd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add16  x y)
+	// cond:
+	// result: (ADDW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XADDW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32  x y)
+	// cond:
+	// result: (ADDW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XADDW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAdd32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add32F x y)
+	// cond:
+	// result: (FADDS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XFADDS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAdd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64  x y)
+	// cond:
+	// result: (ADD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAdd64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64F x y)
+	// cond:
+	// result: (FADD x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XFADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAdd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add8   x y)
+	// cond:
+	// result: (ADDW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XADDW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAddPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AddPtr x y)
+	// cond:
+	// result: (ADD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XADD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAddr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Addr {sym} base)
+	// cond:
+	// result: (MOVDaddr {sym} base)
+	for {
+		sym := v.Aux
+		base := v.Args[0]
+		v.reset(OpS390XMOVDaddr)
+		v.Aux = sym
+		v.AddArg(base)
+		return true
+	}
+}
+func rewriteValueS390X_OpAnd16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And16 x y)
+	// cond:
+	// result: (ANDW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAnd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And32 x y)
+	// cond:
+	// result: (ANDW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAnd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And64 x y)
+	// cond:
+	// result: (AND x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XAND)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAnd8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And8  x y)
+	// cond:
+	// result: (ANDW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAndB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AndB x y)
+	// cond:
+	// result: (ANDW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicAdd32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicAdd32 ptr val mem)
+	// cond:
+	// result: (AddTupleFirst32 (LAA ptr val mem) val)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XAddTupleFirst32)
+		v0 := b.NewValue0(v.Line, OpS390XLAA, MakeTuple(config.fe.TypeUInt32(), TypeMem))
+		v0.AddArg(ptr)
+		v0.AddArg(val)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(val)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicAdd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicAdd64 ptr val mem)
+	// cond:
+	// result: (AddTupleFirst64 (LAAG ptr val mem) val)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XAddTupleFirst64)
+		v0 := b.NewValue0(v.Line, OpS390XLAAG, MakeTuple(config.fe.TypeUInt64(), TypeMem))
+		v0.AddArg(ptr)
+		v0.AddArg(val)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(val)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicCompareAndSwap32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicCompareAndSwap32 ptr old new_ mem)
+	// cond:
+	// result: (LoweredAtomicCas32 ptr old new_ mem)
+	for {
+		ptr := v.Args[0]
+		old := v.Args[1]
+		new_ := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XLoweredAtomicCas32)
+		v.AddArg(ptr)
+		v.AddArg(old)
+		v.AddArg(new_)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicCompareAndSwap64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicCompareAndSwap64 ptr old new_ mem)
+	// cond:
+	// result: (LoweredAtomicCas64 ptr old new_ mem)
+	for {
+		ptr := v.Args[0]
+		old := v.Args[1]
+		new_ := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XLoweredAtomicCas64)
+		v.AddArg(ptr)
+		v.AddArg(old)
+		v.AddArg(new_)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicExchange32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicExchange32 ptr val mem)
+	// cond:
+	// result: (LoweredAtomicExchange32 ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XLoweredAtomicExchange32)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicExchange64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicExchange64 ptr val mem)
+	// cond:
+	// result: (LoweredAtomicExchange64 ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XLoweredAtomicExchange64)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicLoad32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoad32 ptr mem)
+	// cond:
+	// result: (MOVWZatomicload ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpS390XMOVWZatomicload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicLoad64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoad64 ptr mem)
+	// cond:
+	// result: (MOVDatomicload ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpS390XMOVDatomicload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicLoadPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicLoadPtr ptr mem)
+	// cond:
+	// result: (MOVDatomicload ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpS390XMOVDatomicload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicStore32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStore32 ptr val mem)
+	// cond:
+	// result: (MOVWatomicstore ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVWatomicstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicStore64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStore64 ptr val mem)
+	// cond:
+	// result: (MOVDatomicstore ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVDatomicstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpAtomicStorePtrNoWB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AtomicStorePtrNoWB ptr val mem)
+	// cond:
+	// result: (MOVDatomicstore ptr val mem)
+	for {
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVDatomicstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpAvg64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Avg64u <t> x y)
+	// cond:
+	// result: (ADD (ADD <t> (SRDconst <t> x [1]) (SRDconst <t> y [1])) (ANDconst <t> (AND <t> x y) [1]))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XADD)
+		v0 := b.NewValue0(v.Line, OpS390XADD, t)
+		v1 := b.NewValue0(v.Line, OpS390XSRDconst, t)
+		v1.AuxInt = 1
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XSRDconst, t)
+		v2.AuxInt = 1
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpS390XANDconst, t)
+		v3.AuxInt = 1
+		v4 := b.NewValue0(v.Line, OpS390XAND, t)
+		v4.AddArg(x)
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValueS390X_OpBswap32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Bswap32 x)
+	// cond:
+	// result: (MOVWBR x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVWBR)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpBswap64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Bswap64 x)
+	// cond:
+	// result: (MOVDBR x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVDBR)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpClosureCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ClosureCall [argwid] entry closure mem)
+	// cond:
+	// result: (CALLclosure [argwid] entry closure mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		closure := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XCALLclosure)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(closure)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpCom16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com16 x)
+	// cond:
+	// result: (NOTW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XNOTW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCom32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com32 x)
+	// cond:
+	// result: (NOTW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XNOTW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCom64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com64 x)
+	// cond:
+	// result: (NOT x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XNOT)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCom8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com8  x)
+	// cond:
+	// result: (NOTW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XNOTW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpConst16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const16  [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueS390X_OpConst32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32  [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueS390X_OpConst32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const32F [val])
+	// cond:
+	// result: (FMOVSconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpS390XFMOVSconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueS390X_OpConst64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64  [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueS390X_OpConst64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64F [val])
+	// cond:
+	// result: (FMOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpS390XFMOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueS390X_OpConst8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const8   [val])
+	// cond:
+	// result: (MOVDconst [val])
+	for {
+		val := v.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = val
+		return true
+	}
+}
+func rewriteValueS390X_OpConstBool(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstBool [b])
+	// cond:
+	// result: (MOVDconst [b])
+	for {
+		b := v.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = b
+		return true
+	}
+}
+func rewriteValueS390X_OpConstNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ConstNil)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+}
+func rewriteValueS390X_OpConvert(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Convert <t> x mem)
+	// cond:
+	// result: (MOVDconvert <t> x mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpS390XMOVDconvert)
+		v.Type = t
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpCtz32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Ctz32 <t> x)
+	// cond:
+	// result: (SUB (MOVDconst [64]) (FLOGR (MOVWZreg (ANDW <t> (SUBWconst <t> [1] x) (NOTW <t> x)))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpS390XSUB)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 64
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XFLOGR, config.fe.TypeUInt64())
+		v2 := b.NewValue0(v.Line, OpS390XMOVWZreg, config.fe.TypeUInt64())
+		v3 := b.NewValue0(v.Line, OpS390XANDW, t)
+		v4 := b.NewValue0(v.Line, OpS390XSUBWconst, t)
+		v4.AuxInt = 1
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpS390XNOTW, t)
+		v5.AddArg(x)
+		v3.AddArg(v5)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpCtz64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Ctz64 <t> x)
+	// cond:
+	// result: (SUB (MOVDconst [64]) (FLOGR (AND <t> (SUBconst <t> [1] x) (NOT <t> x))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpS390XSUB)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 64
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XFLOGR, config.fe.TypeUInt64())
+		v2 := b.NewValue0(v.Line, OpS390XAND, t)
+		v3 := b.NewValue0(v.Line, OpS390XSUBconst, t)
+		v3.AuxInt = 1
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XNOT, t)
+		v4.AddArg(x)
+		v2.AddArg(v4)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpCvt32Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto32 x)
+	// cond:
+	// result: (CFEBRA x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XCFEBRA)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCvt32Fto64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64 x)
+	// cond:
+	// result: (CGEBRA x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XCGEBRA)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCvt32Fto64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32Fto64F x)
+	// cond:
+	// result: (LDEBR x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XLDEBR)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCvt32to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to32F x)
+	// cond:
+	// result: (CEFBRA x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XCEFBRA)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCvt32to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt32to64F x)
+	// cond:
+	// result: (CDFBRA x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XCDFBRA)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCvt64Fto32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32 x)
+	// cond:
+	// result: (CFDBRA x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XCFDBRA)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCvt64Fto32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto32F x)
+	// cond:
+	// result: (LEDBR x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XLEDBR)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCvt64Fto64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64Fto64 x)
+	// cond:
+	// result: (CGDBRA x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XCGDBRA)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCvt64to32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64to32F x)
+	// cond:
+	// result: (CEGBRA x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XCEGBRA)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpCvt64to64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Cvt64to64F x)
+	// cond:
+	// result: (CDGBRA x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XCDGBRA)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpDeferCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (DeferCall [argwid] mem)
+	// cond:
+	// result: (CALLdefer [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpS390XCALLdefer)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpDiv16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16  x y)
+	// cond:
+	// result: (DIVW  (MOVHreg x) (MOVHreg y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XDIVW)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpDiv16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div16u x y)
+	// cond:
+	// result: (DIVWU (MOVHZreg x) (MOVHZreg y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XDIVWU)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpDiv32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32  x y)
+	// cond:
+	// result: (DIVW  (MOVWreg x) y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XDIVW)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpDiv32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32F x y)
+	// cond:
+	// result: (FDIVS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XFDIVS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpDiv32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32u x y)
+	// cond:
+	// result: (DIVWU (MOVWZreg x) y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XDIVWU)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWZreg, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpDiv64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64  x y)
+	// cond:
+	// result: (DIVD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XDIVD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpDiv64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64F x y)
+	// cond:
+	// result: (FDIV x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XFDIV)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpDiv64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64u x y)
+	// cond:
+	// result: (DIVDU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XDIVDU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpDiv8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8   x y)
+	// cond:
+	// result: (DIVW  (MOVBreg x) (MOVBreg y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XDIVW)
+		v0 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpDiv8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div8u  x y)
+	// cond:
+	// result: (DIVWU (MOVBZreg x) (MOVBZreg y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XDIVWU)
+		v0 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpEq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq16  x y)
+	// cond:
+	// result: (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDEQ)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpEq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32  x y)
+	// cond:
+	// result: (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDEQ)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPW, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpEq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq32F x y)
+	// cond:
+	// result: (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (FCMPS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDEQ)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMPS, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpEq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64  x y)
+	// cond:
+	// result: (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDEQ)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpEq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64F x y)
+	// cond:
+	// result: (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (FCMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDEQ)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpEq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq8   x y)
+	// cond:
+	// result: (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDEQ)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpEqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqB   x y)
+	// cond:
+	// result: (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDEQ)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpEqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (EqPtr x y)
+	// cond:
+	// result: (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDEQ)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16  x y)
+	// cond:
+	// result: (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq16U x y)
+	// cond:
+	// result: (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVHZreg x) (MOVHZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32  x y)
+	// cond:
+	// result: (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPW, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32F x y)
+	// cond:
+	// result: (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMPS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGEnoinv)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMPS, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq32U x y)
+	// cond:
+	// result: (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMPWU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWU, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64  x y)
+	// cond:
+	// result: (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64F x y)
+	// cond:
+	// result: (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGEnoinv)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64U x y)
+	// cond:
+	// result: (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8   x y)
+	// cond:
+	// result: (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq8U  x y)
+	// cond:
+	// result: (MOVDGE (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVBZreg x) (MOVBZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGetClosurePtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetClosurePtr)
+	// cond:
+	// result: (LoweredGetClosurePtr)
+	for {
+		v.reset(OpS390XLoweredGetClosurePtr)
+		return true
+	}
+}
+func rewriteValueS390X_OpGetG(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GetG mem)
+	// cond:
+	// result: (LoweredGetG mem)
+	for {
+		mem := v.Args[0]
+		v.reset(OpS390XLoweredGetG)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpGoCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (GoCall [argwid] mem)
+	// cond:
+	// result: (CALLgo [argwid] mem)
+	for {
+		argwid := v.AuxInt
+		mem := v.Args[0]
+		v.reset(OpS390XCALLgo)
+		v.AuxInt = argwid
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpGreater16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16  x y)
+	// cond:
+	// result: (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGreater16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater16U x y)
+	// cond:
+	// result: (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVHZreg x) (MOVHZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGreater32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32  x y)
+	// cond:
+	// result: (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPW, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGreater32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32F x y)
+	// cond:
+	// result: (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMPS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGTnoinv)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMPS, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGreater32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater32U x y)
+	// cond:
+	// result: (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMPWU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWU, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGreater64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64  x y)
+	// cond:
+	// result: (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGreater64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64F x y)
+	// cond:
+	// result: (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGTnoinv)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGreater64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64U x y)
+	// cond:
+	// result: (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGreater8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8   x y)
+	// cond:
+	// result: (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpGreater8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater8U  x y)
+	// cond:
+	// result: (MOVDGT (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVBZreg x) (MOVBZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpHmul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16  x y)
+	// cond:
+	// result: (SRDconst [16] (MULLW (MOVHreg x) (MOVHreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRDconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpS390XMULLW, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpHmul16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul16u x y)
+	// cond:
+	// result: (SRDconst [16] (MULLW (MOVHZreg x) (MOVHZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRDconst)
+		v.AuxInt = 16
+		v0 := b.NewValue0(v.Line, OpS390XMULLW, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpHmul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32  x y)
+	// cond:
+	// result: (SRDconst [32] (MULLD (MOVWreg x) (MOVWreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRDconst)
+		v.AuxInt = 32
+		v0 := b.NewValue0(v.Line, OpS390XMULLD, config.fe.TypeInt64())
+		v1 := b.NewValue0(v.Line, OpS390XMOVWreg, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XMOVWreg, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpHmul32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul32u x y)
+	// cond:
+	// result: (SRDconst [32] (MULLD (MOVWZreg x) (MOVWZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRDconst)
+		v.AuxInt = 32
+		v0 := b.NewValue0(v.Line, OpS390XMULLD, config.fe.TypeInt64())
+		v1 := b.NewValue0(v.Line, OpS390XMOVWZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XMOVWZreg, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpHmul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul64  x y)
+	// cond:
+	// result: (MULHD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMULHD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpHmul64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul64u x y)
+	// cond:
+	// result: (MULHDU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMULHDU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpHmul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8   x y)
+	// cond:
+	// result: (SRDconst [8] (MULLW (MOVBreg x) (MOVBreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRDconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpS390XMULLW, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpHmul8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Hmul8u  x y)
+	// cond:
+	// result: (SRDconst [8] (MULLW (MOVBZreg x) (MOVBZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRDconst)
+		v.AuxInt = 8
+		v0 := b.NewValue0(v.Line, OpS390XMULLW, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpITab(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ITab (Load ptr mem))
+	// cond:
+	// result: (MOVDload ptr mem)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLoad {
+			break
+		}
+		ptr := v_0.Args[0]
+		mem := v_0.Args[1]
+		v.reset(OpS390XMOVDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpInterCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (InterCall [argwid] entry mem)
+	// cond:
+	// result: (CALLinter [argwid] entry mem)
+	for {
+		argwid := v.AuxInt
+		entry := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpS390XCALLinter)
+		v.AuxInt = argwid
+		v.AddArg(entry)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpIsInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsInBounds idx len)
+	// cond:
+	// result: (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPU idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpS390XMOVDLT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v2.AddArg(idx)
+		v2.AddArg(len)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpIsNonNil(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsNonNil p)
+	// cond:
+	// result: (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMPconst p [0]))
+	for {
+		p := v.Args[0]
+		v.reset(OpS390XMOVDNE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPconst, TypeFlags)
+		v2.AuxInt = 0
+		v2.AddArg(p)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpIsSliceInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsSliceInBounds idx len)
+	// cond:
+	// result: (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPU idx len))
+	for {
+		idx := v.Args[0]
+		len := v.Args[1]
+		v.reset(OpS390XMOVDLE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v2.AddArg(idx)
+		v2.AddArg(len)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16  x y)
+	// cond:
+	// result: (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLeq16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq16U x y)
+	// cond:
+	// result: (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVHZreg x) (MOVHZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32  x y)
+	// cond:
+	// result: (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPW, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32F x y)
+	// cond:
+	// result: (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMPS y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGEnoinv)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMPS, TypeFlags)
+		v2.AddArg(y)
+		v2.AddArg(x)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLeq32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq32U x y)
+	// cond:
+	// result: (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPWU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWU, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64  x y)
+	// cond:
+	// result: (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64F x y)
+	// cond:
+	// result: (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMP y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGEnoinv)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMP, TypeFlags)
+		v2.AddArg(y)
+		v2.AddArg(x)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64U x y)
+	// cond:
+	// result: (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8   x y)
+	// cond:
+	// result: (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLeq8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq8U  x y)
+	// cond:
+	// result: (MOVDLE (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVBZreg x) (MOVBZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLess16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16  x y)
+	// cond:
+	// result: (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLess16U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less16U x y)
+	// cond:
+	// result: (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVHZreg x) (MOVHZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLess32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32  x y)
+	// cond:
+	// result: (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPW, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLess32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32F x y)
+	// cond:
+	// result: (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMPS y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGTnoinv)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMPS, TypeFlags)
+		v2.AddArg(y)
+		v2.AddArg(x)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLess32U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less32U x y)
+	// cond:
+	// result: (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPWU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWU, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLess64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64  x y)
+	// cond:
+	// result: (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLess64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64F x y)
+	// cond:
+	// result: (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) (FCMP y x))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDGTnoinv)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMP, TypeFlags)
+		v2.AddArg(y)
+		v2.AddArg(x)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLess64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64U x y)
+	// cond:
+	// result: (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPU x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLess8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8   x y)
+	// cond:
+	// result: (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLess8U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less8U  x y)
+	// cond:
+	// result: (MOVDLT (MOVDconst [0]) (MOVDconst [1]) (CMPU (MOVBZreg x) (MOVBZreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDLT)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPU, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpLoad(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Load <t> ptr mem)
+	// cond: (is64BitInt(t) || isPtr(t))
+	// result: (MOVDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitInt(t) || isPtr(t)) {
+			break
+		}
+		v.reset(OpS390XMOVDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitInt(t)
+	// result: (MOVWZload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitInt(t)) {
+			break
+		}
+		v.reset(OpS390XMOVWZload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is16BitInt(t)
+	// result: (MOVHZload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is16BitInt(t)) {
+			break
+		}
+		v.reset(OpS390XMOVHZload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: (t.IsBoolean() || is8BitInt(t))
+	// result: (MOVBZload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(t.IsBoolean() || is8BitInt(t)) {
+			break
+		}
+		v.reset(OpS390XMOVBZload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is32BitFloat(t)
+	// result: (FMOVSload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is32BitFloat(t)) {
+			break
+		}
+		v.reset(OpS390XFMOVSload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitFloat(t)
+	// result: (FMOVDload ptr mem)
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitFloat(t)) {
+			break
+		}
+		v.reset(OpS390XFMOVDload)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpLrot32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot32 <t> x [c])
+	// cond:
+	// result: (RLLconst <t> [c&31] x)
+	for {
+		t := v.Type
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpS390XRLLconst)
+		v.Type = t
+		v.AuxInt = c & 31
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpLrot64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot64 <t> x [c])
+	// cond:
+	// result: (RLLGconst <t> [c&63] x)
+	for {
+		t := v.Type
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpS390XRLLGconst)
+		v.Type = t
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x16 <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v3 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x32 <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst y [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x64 <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPUconst y [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v2.AuxInt = 31
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x8  <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v3 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x16 <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v3 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x32 <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst y [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x64 <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPUconst y [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v2.AuxInt = 31
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x8  <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v3 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x16 <t> x y)
+	// cond:
+	// result: (AND (SLD <t> x y) (SUBEcarrymask <t> (CMPWUconst (MOVHZreg y) [63])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XAND)
+		v0 := b.NewValue0(v.Line, OpS390XSLD, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 63
+		v3 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x32 <t> x y)
+	// cond:
+	// result: (AND (SLD <t> x y) (SUBEcarrymask <t> (CMPWUconst y [63])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XAND)
+		v0 := b.NewValue0(v.Line, OpS390XSLD, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 63
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh64x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x64 <t> x y)
+	// cond:
+	// result: (AND (SLD <t> x y) (SUBEcarrymask <t> (CMPUconst y [63])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XAND)
+		v0 := b.NewValue0(v.Line, OpS390XSLD, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v2.AuxInt = 63
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x8  <t> x y)
+	// cond:
+	// result: (AND (SLD <t> x y) (SUBEcarrymask <t> (CMPWUconst (MOVBZreg y) [63])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XAND)
+		v0 := b.NewValue0(v.Line, OpS390XSLD, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 63
+		v3 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x16 <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v3 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x32 <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst y [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x64 <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPUconst y [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v2.AuxInt = 31
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpLsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x8  <t> x y)
+	// cond:
+	// result: (ANDW (SLW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v3 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpMod16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16  x y)
+	// cond:
+	// result: (MODW  (MOVHreg x) (MOVHreg y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMODW)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpMod16u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod16u x y)
+	// cond:
+	// result: (MODWU (MOVHZreg x) (MOVHZreg y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMODWU)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpMod32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32  x y)
+	// cond:
+	// result: (MODW  (MOVWreg x) y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMODW)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpMod32u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod32u x y)
+	// cond:
+	// result: (MODWU (MOVWZreg x) y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMODWU)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWZreg, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpMod64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod64  x y)
+	// cond:
+	// result: (MODD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMODD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpMod64u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod64u x y)
+	// cond:
+	// result: (MODDU x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMODDU)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpMod8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8   x y)
+	// cond:
+	// result: (MODW  (MOVBreg x) (MOVBreg y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMODW)
+		v0 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpMod8u(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mod8u  x y)
+	// cond:
+	// result: (MODWU (MOVBZreg x) (MOVBZreg y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMODWU)
+		v0 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v1.AddArg(y)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpMove(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Move [s] _ _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstore dst (MOVBZload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpS390XMOVBstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpS390XMOVBZload, config.fe.TypeUInt8())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVHstore dst (MOVHZload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpS390XMOVHstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHZload, config.fe.TypeUInt16())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVWstore dst (MOVWZload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpS390XMOVWstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWZload, config.fe.TypeUInt32())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 8
+	// result: (MOVDstore dst (MOVDload src mem) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 8) {
+			break
+		}
+		v.reset(OpS390XMOVDstore)
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDload, config.fe.TypeUInt64())
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 16
+	// result: (MOVDstore [8] dst (MOVDload [8] src mem) 		(MOVDstore dst (MOVDload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 16) {
+			break
+		}
+		v.reset(OpS390XMOVDstore)
+		v.AuxInt = 8
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDload, config.fe.TypeUInt64())
+		v0.AuxInt = 8
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpS390XMOVDload, config.fe.TypeUInt64())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 24
+	// result: (MOVDstore [16] dst (MOVDload [16] src mem) 	        (MOVDstore [8] dst (MOVDload [8] src mem)                 (MOVDstore dst (MOVDload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 24) {
+			break
+		}
+		v.reset(OpS390XMOVDstore)
+		v.AuxInt = 16
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDload, config.fe.TypeUInt64())
+		v0.AuxInt = 16
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDstore, TypeMem)
+		v1.AuxInt = 8
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpS390XMOVDload, config.fe.TypeUInt64())
+		v2.AuxInt = 8
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpS390XMOVDstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpS390XMOVDload, config.fe.TypeUInt64())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstore [2] dst (MOVBZload [2] src mem) 		(MOVHstore dst (MOVHZload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpS390XMOVBstore)
+		v.AuxInt = 2
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpS390XMOVBZload, config.fe.TypeUInt8())
+		v0.AuxInt = 2
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpS390XMOVHZload, config.fe.TypeUInt16())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 5
+	// result: (MOVBstore [4] dst (MOVBZload [4] src mem) 		(MOVWstore dst (MOVWZload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 5) {
+			break
+		}
+		v.reset(OpS390XMOVBstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpS390XMOVBZload, config.fe.TypeUInt8())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVWstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpS390XMOVWZload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 6
+	// result: (MOVHstore [4] dst (MOVHZload [4] src mem) 		(MOVWstore dst (MOVWZload src mem) mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 6) {
+			break
+		}
+		v.reset(OpS390XMOVHstore)
+		v.AuxInt = 4
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHZload, config.fe.TypeUInt16())
+		v0.AuxInt = 4
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVWstore, TypeMem)
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpS390XMOVWZload, config.fe.TypeUInt32())
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() == 7
+	// result: (MOVBstore [6] dst (MOVBZload [6] src mem) 		(MOVHstore [4] dst (MOVHZload [4] src mem) 			(MOVWstore dst (MOVWZload src mem) mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() == 7) {
+			break
+		}
+		v.reset(OpS390XMOVBstore)
+		v.AuxInt = 6
+		v.AddArg(dst)
+		v0 := b.NewValue0(v.Line, OpS390XMOVBZload, config.fe.TypeUInt8())
+		v0.AuxInt = 6
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHstore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(dst)
+		v2 := b.NewValue0(v.Line, OpS390XMOVHZload, config.fe.TypeUInt16())
+		v2.AuxInt = 4
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpS390XMOVWstore, TypeMem)
+		v3.AddArg(dst)
+		v4 := b.NewValue0(v.Line, OpS390XMOVWZload, config.fe.TypeUInt32())
+		v4.AddArg(src)
+		v4.AddArg(mem)
+		v3.AddArg(v4)
+		v3.AddArg(mem)
+		v1.AddArg(v3)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 0 && SizeAndAlign(s).Size() <= 256
+	// result: (MVC [makeValAndOff(SizeAndAlign(s).Size(), 0)] dst src mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 0 && SizeAndAlign(s).Size() <= 256) {
+			break
+		}
+		v.reset(OpS390XMVC)
+		v.AuxInt = makeValAndOff(SizeAndAlign(s).Size(), 0)
+		v.AddArg(dst)
+		v.AddArg(src)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 256 && SizeAndAlign(s).Size() <= 512
+	// result: (MVC [makeValAndOff(SizeAndAlign(s).Size()-256, 256)] dst src (MVC [makeValAndOff(256, 0)] dst src mem))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 256 && SizeAndAlign(s).Size() <= 512) {
+			break
+		}
+		v.reset(OpS390XMVC)
+		v.AuxInt = makeValAndOff(SizeAndAlign(s).Size()-256, 256)
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, OpS390XMVC, TypeMem)
+		v0.AuxInt = makeValAndOff(256, 0)
+		v0.AddArg(dst)
+		v0.AddArg(src)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 512 && SizeAndAlign(s).Size() <= 768
+	// result: (MVC [makeValAndOff(SizeAndAlign(s).Size()-512, 512)] dst src (MVC [makeValAndOff(256, 256)] dst src (MVC [makeValAndOff(256, 0)] dst src mem)))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 512 && SizeAndAlign(s).Size() <= 768) {
+			break
+		}
+		v.reset(OpS390XMVC)
+		v.AuxInt = makeValAndOff(SizeAndAlign(s).Size()-512, 512)
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, OpS390XMVC, TypeMem)
+		v0.AuxInt = makeValAndOff(256, 256)
+		v0.AddArg(dst)
+		v0.AddArg(src)
+		v1 := b.NewValue0(v.Line, OpS390XMVC, TypeMem)
+		v1.AuxInt = makeValAndOff(256, 0)
+		v1.AddArg(dst)
+		v1.AddArg(src)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 768 && SizeAndAlign(s).Size() <= 1024
+	// result: (MVC [makeValAndOff(SizeAndAlign(s).Size()-768, 768)] dst src (MVC [makeValAndOff(256, 512)] dst src (MVC [makeValAndOff(256, 256)] dst src (MVC [makeValAndOff(256, 0)] dst src mem))))
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 768 && SizeAndAlign(s).Size() <= 1024) {
+			break
+		}
+		v.reset(OpS390XMVC)
+		v.AuxInt = makeValAndOff(SizeAndAlign(s).Size()-768, 768)
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, OpS390XMVC, TypeMem)
+		v0.AuxInt = makeValAndOff(256, 512)
+		v0.AddArg(dst)
+		v0.AddArg(src)
+		v1 := b.NewValue0(v.Line, OpS390XMVC, TypeMem)
+		v1.AuxInt = makeValAndOff(256, 256)
+		v1.AddArg(dst)
+		v1.AddArg(src)
+		v2 := b.NewValue0(v.Line, OpS390XMVC, TypeMem)
+		v2.AuxInt = makeValAndOff(256, 0)
+		v2.AddArg(dst)
+		v2.AddArg(src)
+		v2.AddArg(mem)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Move [s] dst src mem)
+	// cond: SizeAndAlign(s).Size() > 1024
+	// result: (LoweredMove [SizeAndAlign(s).Size()%256] dst src (ADDconst <src.Type> src [(SizeAndAlign(s).Size()/256)*256]) mem)
+	for {
+		s := v.AuxInt
+		dst := v.Args[0]
+		src := v.Args[1]
+		mem := v.Args[2]
+		if !(SizeAndAlign(s).Size() > 1024) {
+			break
+		}
+		v.reset(OpS390XLoweredMove)
+		v.AuxInt = SizeAndAlign(s).Size() % 256
+		v.AddArg(dst)
+		v.AddArg(src)
+		v0 := b.NewValue0(v.Line, OpS390XADDconst, src.Type)
+		v0.AuxInt = (SizeAndAlign(s).Size() / 256) * 256
+		v0.AddArg(src)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpMul16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul16  x y)
+	// cond:
+	// result: (MULLW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMULLW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpMul32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32  x y)
+	// cond:
+	// result: (MULLW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMULLW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpMul32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul32F x y)
+	// cond:
+	// result: (FMULS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XFMULS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpMul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64  x y)
+	// cond:
+	// result: (MULLD  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMULLD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpMul64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64F x y)
+	// cond:
+	// result: (FMUL x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XFMUL)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpMul8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul8   x y)
+	// cond:
+	// result: (MULLW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMULLW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeg16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg16  x)
+	// cond:
+	// result: (NEGW (MOVHreg x))
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XNEGW)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeg32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32  x)
+	// cond:
+	// result: (NEGW x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XNEGW)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeg32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg32F x)
+	// cond:
+	// result: (FNEGS x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XFNEGS)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeg64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64  x)
+	// cond:
+	// result: (NEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XNEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeg64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64F x)
+	// cond:
+	// result: (FNEG x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XFNEG)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeg8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg8   x)
+	// cond:
+	// result: (NEGW (MOVBreg x))
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XNEGW)
+		v0 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeq16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq16  x y)
+	// cond:
+	// result: (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVHreg x) (MOVHreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDNE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeq32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32  x y)
+	// cond:
+	// result: (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMPW x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDNE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMPW, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeq32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq32F x y)
+	// cond:
+	// result: (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (FCMPS x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDNE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMPS, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64  x y)
+	// cond:
+	// result: (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDNE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeq64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64F x y)
+	// cond:
+	// result: (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (FCMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDNE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XFCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeq8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq8   x y)
+	// cond:
+	// result: (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDNE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeqB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqB   x y)
+	// cond:
+	// result: (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMP (MOVBreg x) (MOVBreg y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDNE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v3 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v4.AddArg(y)
+		v2.AddArg(v4)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpNeqPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NeqPtr x y)
+	// cond:
+	// result: (MOVDNE (MOVDconst [0]) (MOVDconst [1]) (CMP x y))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XMOVDNE)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v1.AuxInt = 1
+		v.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpS390XCMP, TypeFlags)
+		v2.AddArg(x)
+		v2.AddArg(y)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpNilCheck(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NilCheck ptr mem)
+	// cond:
+	// result: (LoweredNilCheck ptr mem)
+	for {
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		v.reset(OpS390XLoweredNilCheck)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpNot(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Not x)
+	// cond:
+	// result: (XORWconst [1] x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XXORWconst)
+		v.AuxInt = 1
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpOffPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OffPtr [off] ptr:(SP))
+	// cond:
+	// result: (MOVDaddr [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		if ptr.Op != OpSP {
+			break
+		}
+		v.reset(OpS390XMOVDaddr)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+	// match: (OffPtr [off] ptr)
+	// cond: is32Bit(off)
+	// result: (ADDconst [off] ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		if !(is32Bit(off)) {
+			break
+		}
+		v.reset(OpS390XADDconst)
+		v.AuxInt = off
+		v.AddArg(ptr)
+		return true
+	}
+	// match: (OffPtr [off] ptr)
+	// cond:
+	// result: (ADD (MOVDconst [off]) ptr)
+	for {
+		off := v.AuxInt
+		ptr := v.Args[0]
+		v.reset(OpS390XADD)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = off
+		v.AddArg(v0)
+		v.AddArg(ptr)
+		return true
+	}
+}
+func rewriteValueS390X_OpOr16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or16 x y)
+	// cond:
+	// result: (ORW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XORW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpOr32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or32 x y)
+	// cond:
+	// result: (ORW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XORW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpOr64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or64 x y)
+	// cond:
+	// result: (OR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpOr8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or8  x y)
+	// cond:
+	// result: (ORW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XORW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpOrB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OrB x y)
+	// cond:
+	// result: (ORW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XORW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh16Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux16 <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> (MOVHZreg x) y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [15])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 15
+		v4 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh16Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux32 <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> (MOVHZreg x) y) (SUBEWcarrymask <t> (CMPWUconst y [15])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 15
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh16Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux64 <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> (MOVHZreg x) y) (SUBEWcarrymask <t> (CMPUconst y [15])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v3 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v3.AuxInt = 15
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh16Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux8  <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> (MOVHZreg x) y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [15])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 15
+		v4 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh16x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x16 <t> x y)
+	// cond:
+	// result: (SRAW <t> (MOVHreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVHZreg y) [15])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v0 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v4 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v4.AuxInt = 15
+		v5 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh16x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x32 <t> x y)
+	// cond:
+	// result: (SRAW <t> (MOVHreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst y [15])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v0 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v4 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v4.AuxInt = 15
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x64 <t> x y)
+	// cond:
+	// result: (SRAW <t> (MOVHreg x) (OR <y.Type> y (NOT <y.Type> (SUBEcarrymask <y.Type> (CMPUconst y [15])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v0 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XOR, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpS390XNOT, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, y.Type)
+		v4 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v4.AuxInt = 15
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh16x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x8  <t> x y)
+	// cond:
+	// result: (SRAW <t> (MOVHreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVBZreg y) [15])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v0 := b.NewValue0(v.Line, OpS390XMOVHreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v4 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v4.AuxInt = 15
+		v5 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh32Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux16 <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v3 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh32Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux32 <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> x y) (SUBEWcarrymask <t> (CMPWUconst y [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh32Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux64 <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> x y) (SUBEWcarrymask <t> (CMPUconst y [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v2.AuxInt = 31
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh32Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux8  <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> x y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [31])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 31
+		v3 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh32x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x16 <t> x y)
+	// cond:
+	// result: (SRAW <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVHZreg y) [31])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 31
+		v4 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh32x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x32 <t> x y)
+	// cond:
+	// result: (SRAW <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst y [31])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 31
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x64 <t> x y)
+	// cond:
+	// result: (SRAW <t> x (OR <y.Type> y (NOT <y.Type> (SUBEcarrymask <y.Type> (CMPUconst y [31])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpS390XOR, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpS390XNOT, y.Type)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v3.AuxInt = 31
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh32x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x8  <t> x y)
+	// cond:
+	// result: (SRAW <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVBZreg y) [31])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 31
+		v4 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh64Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux16 <t> x y)
+	// cond:
+	// result: (AND (SRD <t> x y) (SUBEcarrymask <t> (CMPWUconst (MOVHZreg y) [63])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XAND)
+		v0 := b.NewValue0(v.Line, OpS390XSRD, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 63
+		v3 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh64Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux32 <t> x y)
+	// cond:
+	// result: (AND (SRD <t> x y) (SUBEcarrymask <t> (CMPWUconst y [63])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XAND)
+		v0 := b.NewValue0(v.Line, OpS390XSRD, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 63
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh64Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux64 <t> x y)
+	// cond:
+	// result: (AND (SRD <t> x y) (SUBEcarrymask <t> (CMPUconst y [63])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XAND)
+		v0 := b.NewValue0(v.Line, OpS390XSRD, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v2.AuxInt = 63
+		v2.AddArg(y)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh64Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux8  <t> x y)
+	// cond:
+	// result: (AND (SRD <t> x y) (SUBEcarrymask <t> (CMPWUconst (MOVBZreg y) [63])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XAND)
+		v0 := b.NewValue0(v.Line, OpS390XSRD, t)
+		v0.AddArg(x)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, t)
+		v2 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v2.AuxInt = 63
+		v3 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x16 <t> x y)
+	// cond:
+	// result: (SRAD <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVHZreg y) [63])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAD)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 63
+		v4 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x32 <t> x y)
+	// cond:
+	// result: (SRAD <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst y [63])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAD)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 63
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh64x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x64 <t> x y)
+	// cond:
+	// result: (SRAD <t> x (OR <y.Type> y (NOT <y.Type> (SUBEcarrymask <y.Type> (CMPUconst y [63])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAD)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpS390XOR, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpS390XNOT, y.Type)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v3.AuxInt = 63
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x8  <t> x y)
+	// cond:
+	// result: (SRAD <t> x (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVBZreg y) [63])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAD)
+		v.Type = t
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v0.AddArg(y)
+		v1 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 63
+		v4 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh8Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux16 <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> (MOVBZreg x) y) (SUBEWcarrymask <t> (CMPWUconst (MOVHZreg y) [7])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v1 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 7
+		v4 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh8Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux32 <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> (MOVBZreg x) y) (SUBEWcarrymask <t> (CMPWUconst y [7])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v1 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 7
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh8Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux64 <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> (MOVBZreg x) y) (SUBEWcarrymask <t> (CMPUconst y [7])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v1 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v3 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v3.AuxInt = 7
+		v3.AddArg(y)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh8Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux8  <t> x y)
+	// cond:
+	// result: (ANDW (SRW <t> (MOVBZreg x) y) (SUBEWcarrymask <t> (CMPWUconst (MOVBZreg y) [7])))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XANDW)
+		v0 := b.NewValue0(v.Line, OpS390XSRW, t)
+		v1 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v0.AddArg(y)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, t)
+		v3 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v3.AuxInt = 7
+		v4 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh8x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x16 <t> x y)
+	// cond:
+	// result: (SRAW <t> (MOVBreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVHZreg y) [7])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v0 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v4 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v4.AuxInt = 7
+		v5 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh8x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x32 <t> x y)
+	// cond:
+	// result: (SRAW <t> (MOVBreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst y [7])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v0 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v4 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v4.AuxInt = 7
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x64 <t> x y)
+	// cond:
+	// result: (SRAW <t> (MOVBreg x) (OR <y.Type> y (NOT <y.Type> (SUBEcarrymask <y.Type> (CMPUconst y [7])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v0 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XOR, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpS390XNOT, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XSUBEcarrymask, y.Type)
+		v4 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v4.AuxInt = 7
+		v4.AddArg(y)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpRsh8x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x8  <t> x y)
+	// cond:
+	// result: (SRAW <t> (MOVBreg x) (ORW <y.Type> y (NOTW <y.Type> (SUBEWcarrymask <y.Type> (CMPWUconst (MOVBZreg y) [7])))))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSRAW)
+		v.Type = t
+		v0 := b.NewValue0(v.Line, OpS390XMOVBreg, config.fe.TypeInt64())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XORW, y.Type)
+		v1.AddArg(y)
+		v2 := b.NewValue0(v.Line, OpS390XNOTW, y.Type)
+		v3 := b.NewValue0(v.Line, OpS390XSUBEWcarrymask, y.Type)
+		v4 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v4.AuxInt = 7
+		v5 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+		v5.AddArg(y)
+		v4.AddArg(v5)
+		v3.AddArg(v4)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpS390XADD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADD x (MOVDconst [c]))
+	// cond: is32Bit(c)
+	// result: (ADDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XADDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADD (MOVDconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (ADDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XADDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADD x (MOVDaddr [c] {s} y))
+	// cond: x.Op != OpSB && y.Op != OpSB
+	// result: (MOVDaddridx [c] {s} x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDaddr {
+			break
+		}
+		c := v_1.AuxInt
+		s := v_1.Aux
+		y := v_1.Args[0]
+		if !(x.Op != OpSB && y.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVDaddridx)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD (MOVDaddr [c] {s} x) y)
+	// cond: x.Op != OpSB && y.Op != OpSB
+	// result: (MOVDaddridx [c] {s} x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		c := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(x.Op != OpSB && y.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVDaddridx)
+		v.AuxInt = c
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD x (NEG y))
+	// cond:
+	// result: (SUB x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XNEG {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpS390XSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADD <t> x g:(MOVDload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ADDload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XADDload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ADD <t> g:(MOVDload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ADDload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XADDload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XADDW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDW x (MOVDconst [c]))
+	// cond:
+	// result: (ADDWconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XADDWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDW (MOVDconst [c]) x)
+	// cond:
+	// result: (ADDWconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpS390XADDWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDW x (NEGW y))
+	// cond:
+	// result: (SUBW x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XNEGW {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpS390XSUBW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDW <t> x g:(MOVWload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ADDWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XADDWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ADDW <t> g:(MOVWload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ADDWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XADDWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ADDW <t> x g:(MOVWZload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ADDWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XADDWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ADDW <t> g:(MOVWZload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ADDWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XADDWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XADDWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDWconst [c] x)
+	// cond: int32(c)==0
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDWconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(int32(c+d))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = int64(int32(c + d))
+		return true
+	}
+	// match: (ADDWconst [c] (ADDWconst [d] x))
+	// cond:
+	// result: (ADDWconst [int64(int32(c+d))] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDWconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpS390XADDWconst)
+		v.AuxInt = int64(int32(c + d))
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XADDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ADDconst [c] (MOVDaddr [d] {s} x:(SB)))
+	// cond: ((c+d)&1 == 0) && is32Bit(c+d)
+	// result: (MOVDaddr [c+d] {s} x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		d := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		if x.Op != OpSB {
+			break
+		}
+		if !(((c+d)&1 == 0) && is32Bit(c+d)) {
+			break
+		}
+		v.reset(OpS390XMOVDaddr)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (MOVDaddr [d] {s} x))
+	// cond: x.Op != OpSB && is20Bit(c+d)
+	// result: (MOVDaddr [c+d] {s} x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		d := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		if !(x.Op != OpSB && is20Bit(c+d)) {
+			break
+		}
+		v.reset(OpS390XMOVDaddr)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (MOVDaddridx [d] {s} x y))
+	// cond: is20Bit(c+d)
+	// result: (MOVDaddridx [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		d := v_0.AuxInt
+		s := v_0.Aux
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		if !(is20Bit(c + d)) {
+			break
+		}
+		v.reset(OpS390XMOVDaddridx)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (ADDconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ADDconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c+d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c + d
+		return true
+	}
+	// match: (ADDconst [c] (ADDconst [d] x))
+	// cond: is32Bit(c+d)
+	// result: (ADDconst [c+d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(c + d)) {
+			break
+		}
+		v.reset(OpS390XADDconst)
+		v.AuxInt = c + d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XAND(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (AND x (MOVDconst [c]))
+	// cond: is32Bit(c) && c < 0
+	// result: (ANDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c) && c < 0) {
+			break
+		}
+		v.reset(OpS390XANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND (MOVDconst [c]) x)
+	// cond: is32Bit(c) && c < 0
+	// result: (ANDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c) && c < 0) {
+			break
+		}
+		v.reset(OpS390XANDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND (MOVDconst [0xFF]) x)
+	// cond:
+	// result: (MOVBZreg x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		if v_0.AuxInt != 0xFF {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpS390XMOVBZreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x (MOVDconst [0xFF]))
+	// cond:
+	// result: (MOVBZreg x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		if v_1.AuxInt != 0xFF {
+			break
+		}
+		v.reset(OpS390XMOVBZreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND (MOVDconst [0xFFFF]) x)
+	// cond:
+	// result: (MOVHZreg x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		if v_0.AuxInt != 0xFFFF {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpS390XMOVHZreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x (MOVDconst [0xFFFF]))
+	// cond:
+	// result: (MOVHZreg x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		if v_1.AuxInt != 0xFFFF {
+			break
+		}
+		v.reset(OpS390XMOVHZreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND (MOVDconst [0xFFFFFFFF]) x)
+	// cond:
+	// result: (MOVWZreg x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		if v_0.AuxInt != 0xFFFFFFFF {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpS390XMOVWZreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND x (MOVDconst [0xFFFFFFFF]))
+	// cond:
+	// result: (MOVWZreg x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		if v_1.AuxInt != 0xFFFFFFFF {
+			break
+		}
+		v.reset(OpS390XMOVWZreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c&d])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c & d
+		return true
+	}
+	// match: (AND x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (AND <t> x g:(MOVDload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ANDload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XANDload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (AND <t> g:(MOVDload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ANDload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XANDload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XANDW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDW x (MOVDconst [c]))
+	// cond:
+	// result: (ANDWconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XANDWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDW (MOVDconst [c]) x)
+	// cond:
+	// result: (ANDWconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpS390XANDWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDW x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDW <t> x g:(MOVWload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ANDWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XANDWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ANDW <t> g:(MOVWload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ANDWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XANDWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ANDW <t> x g:(MOVWZload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ANDWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XANDWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ANDW <t> g:(MOVWZload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ANDWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XANDWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XANDWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDWconst [c] (ANDWconst [d] x))
+	// cond:
+	// result: (ANDWconst [c & d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XANDWconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpS390XANDWconst)
+		v.AuxInt = c & d
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDWconst [0xFF] x)
+	// cond:
+	// result: (MOVBZreg x)
+	for {
+		if v.AuxInt != 0xFF {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpS390XMOVBZreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDWconst [0xFFFF] x)
+	// cond:
+	// result: (MOVHZreg x)
+	for {
+		if v.AuxInt != 0xFFFF {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpS390XMOVHZreg)
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDWconst [c] _)
+	// cond: int32(c)==0
+	// result: (MOVDconst [0])
+	for {
+		c := v.AuxInt
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (ANDWconst [c] x)
+	// cond: int32(c)==-1
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDWconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c&d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c & d
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XANDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ANDconst [c] (ANDconst [d] x))
+	// cond:
+	// result: (ANDconst [c & d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XANDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		v.reset(OpS390XANDconst)
+		v.AuxInt = c & d
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDconst [0] _)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (ANDconst [-1] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ANDconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c&d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c & d
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XCMP(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMP x (MOVDconst [c]))
+	// cond: is32Bit(c)
+	// result: (CMPconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XCMPconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMP (MOVDconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (InvertFlags (CMPconst x [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XInvertFlags)
+		v0 := b.NewValue0(v.Line, OpS390XCMPconst, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XCMPU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPU x (MOVDconst [c]))
+	// cond: is32Bit(c)
+	// result: (CMPUconst x [int64(uint32(c))])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XCMPUconst)
+		v.AuxInt = int64(uint32(c))
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPU (MOVDconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (InvertFlags (CMPUconst x [int64(uint32(c))]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XInvertFlags)
+		v0 := b.NewValue0(v.Line, OpS390XCMPUconst, TypeFlags)
+		v0.AuxInt = int64(uint32(c))
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XCMPUconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPUconst (MOVDconst [x]) [y])
+	// cond: uint64(x)==uint64(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(uint64(x) == uint64(y)) {
+			break
+		}
+		v.reset(OpS390XFlagEQ)
+		return true
+	}
+	// match: (CMPUconst (MOVDconst [x]) [y])
+	// cond: uint64(x)<uint64(y)
+	// result: (FlagLT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(uint64(x) < uint64(y)) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	// match: (CMPUconst (MOVDconst [x]) [y])
+	// cond: uint64(x)>uint64(y)
+	// result: (FlagGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(uint64(x) > uint64(y)) {
+			break
+		}
+		v.reset(OpS390XFlagGT)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XCMPW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPW x (MOVDconst [c]))
+	// cond:
+	// result: (CMPWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XCMPWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPW (MOVDconst [c]) x)
+	// cond:
+	// result: (InvertFlags (CMPWconst x [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpS390XInvertFlags)
+		v0 := b.NewValue0(v.Line, OpS390XCMPWconst, TypeFlags)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XCMPWU(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPWU x (MOVDconst [c]))
+	// cond:
+	// result: (CMPWUconst x [int64(uint32(c))])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XCMPWUconst)
+		v.AuxInt = int64(uint32(c))
+		v.AddArg(x)
+		return true
+	}
+	// match: (CMPWU (MOVDconst [c]) x)
+	// cond:
+	// result: (InvertFlags (CMPWUconst x [int64(uint32(c))]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpS390XInvertFlags)
+		v0 := b.NewValue0(v.Line, OpS390XCMPWUconst, TypeFlags)
+		v0.AuxInt = int64(uint32(c))
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XCMPWUconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPWUconst (MOVDconst [x]) [y])
+	// cond: uint32(x)==uint32(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(uint32(x) == uint32(y)) {
+			break
+		}
+		v.reset(OpS390XFlagEQ)
+		return true
+	}
+	// match: (CMPWUconst (MOVDconst [x]) [y])
+	// cond: uint32(x)<uint32(y)
+	// result: (FlagLT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(uint32(x) < uint32(y)) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	// match: (CMPWUconst (MOVDconst [x]) [y])
+	// cond: uint32(x)>uint32(y)
+	// result: (FlagGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(uint32(x) > uint32(y)) {
+			break
+		}
+		v.reset(OpS390XFlagGT)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XCMPWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)==int32(y)
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) == int32(y)) {
+			break
+		}
+		v.reset(OpS390XFlagEQ)
+		return true
+	}
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)<int32(y)
+	// result: (FlagLT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) < int32(y)) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	// match: (CMPWconst (MOVDconst [x]) [y])
+	// cond: int32(x)>int32(y)
+	// result: (FlagGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(int32(x) > int32(y)) {
+			break
+		}
+		v.reset(OpS390XFlagGT)
+		return true
+	}
+	// match: (CMPWconst (SRWconst _ [c]) [n])
+	// cond: 0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n)
+	// result: (FlagLT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XSRWconst {
+			break
+		}
+		c := v_0.AuxInt
+		if !(0 <= n && 0 < c && c <= 32 && (1<<uint64(32-c)) <= uint64(n)) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	// match: (CMPWconst (ANDWconst _ [m]) [n])
+	// cond: 0 <= int32(m) && int32(m) < int32(n)
+	// result: (FlagLT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XANDWconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(0 <= int32(m) && int32(m) < int32(n)) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XCMPconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (CMPconst (MOVDconst [x]) [y])
+	// cond: x==y
+	// result: (FlagEQ)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(x == y) {
+			break
+		}
+		v.reset(OpS390XFlagEQ)
+		return true
+	}
+	// match: (CMPconst (MOVDconst [x]) [y])
+	// cond: x<y
+	// result: (FlagLT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(x < y) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	// match: (CMPconst (MOVDconst [x]) [y])
+	// cond: x>y
+	// result: (FlagGT)
+	for {
+		y := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		x := v_0.AuxInt
+		if !(x > y) {
+			break
+		}
+		v.reset(OpS390XFlagGT)
+		return true
+	}
+	// match: (CMPconst (MOVBZreg _) [c])
+	// cond: 0xFF < c
+	// result: (FlagLT)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVBZreg {
+			break
+		}
+		if !(0xFF < c) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	// match: (CMPconst (MOVHZreg _) [c])
+	// cond: 0xFFFF < c
+	// result: (FlagLT)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVHZreg {
+			break
+		}
+		if !(0xFFFF < c) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	// match: (CMPconst (MOVWZreg _) [c])
+	// cond: 0xFFFFFFFF < c
+	// result: (FlagLT)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVWZreg {
+			break
+		}
+		if !(0xFFFFFFFF < c) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	// match: (CMPconst (SRDconst _ [c]) [n])
+	// cond: 0 <= n && 0 < c && c <= 64 && (1<<uint64(64-c)) <= uint64(n)
+	// result: (FlagLT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XSRDconst {
+			break
+		}
+		c := v_0.AuxInt
+		if !(0 <= n && 0 < c && c <= 64 && (1<<uint64(64-c)) <= uint64(n)) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	// match: (CMPconst (ANDconst _ [m]) [n])
+	// cond: 0 <= m && m < n
+	// result: (FlagLT)
+	for {
+		n := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XANDconst {
+			break
+		}
+		m := v_0.AuxInt
+		if !(0 <= m && m < n) {
+			break
+		}
+		v.reset(OpS390XFlagLT)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XFMOVDload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVDload  [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is20Bit(off1+off2)
+	// result: (FMOVDload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XFMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDload [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (FMOVDload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XFMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (FMOVDloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XFMOVDloadidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDload [off] {sym} (ADD ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (FMOVDloadidx [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XFMOVDloadidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XFMOVDloadidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVDloadidx [c] {sym} (ADDconst [d] ptr) idx mem)
+	// cond:
+	// result: (FMOVDloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XFMOVDloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDloadidx [c] {sym} ptr (ADDconst [d] idx) mem)
+	// cond:
+	// result: (FMOVDloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XFMOVDloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XFMOVDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVDstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: is20Bit(off1+off2)
+	// result: (FMOVDstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XFMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDstore [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (FMOVDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XFMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (FMOVDstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XFMOVDstoreidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDstore [off] {sym} (ADD ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (FMOVDstoreidx [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XFMOVDstoreidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XFMOVDstoreidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVDstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem)
+	// cond:
+	// result: (FMOVDstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XFMOVDstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVDstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem)
+	// cond:
+	// result: (FMOVDstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XFMOVDstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XFMOVSload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVSload  [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is20Bit(off1+off2)
+	// result: (FMOVSload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XFMOVSload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSload [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (FMOVSload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XFMOVSload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (FMOVSloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XFMOVSloadidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSload [off] {sym} (ADD ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (FMOVSloadidx [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XFMOVSloadidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XFMOVSloadidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVSloadidx [c] {sym} (ADDconst [d] ptr) idx mem)
+	// cond:
+	// result: (FMOVSloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XFMOVSloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSloadidx [c] {sym} ptr (ADDconst [d] idx) mem)
+	// cond:
+	// result: (FMOVSloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XFMOVSloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XFMOVSstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVSstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: is20Bit(off1+off2)
+	// result: (FMOVSstore [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XFMOVSstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSstore [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (FMOVSstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XFMOVSstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (FMOVSstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XFMOVSstoreidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSstore [off] {sym} (ADD ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (FMOVSstoreidx [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XFMOVSstoreidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XFMOVSstoreidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (FMOVSstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem)
+	// cond:
+	// result: (FMOVSstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XFMOVSstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (FMOVSstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem)
+	// cond:
+	// result: (FMOVSstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XFMOVSstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVBZload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBZload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVBstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZload  [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVBZload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVBZload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBZload  [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBZload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVBZload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBZload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBZloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVBZloadidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBZload [off] {sym} (ADD ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVBZloadidx [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVBZloadidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVBZloadidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBZloadidx [c] {sym} (ADDconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVBZloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVBZloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBZloadidx [c] {sym} ptr (ADDconst [d] idx) mem)
+	// cond:
+	// result: (MOVBZloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVBZloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVBZreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBZreg x:(MOVDLT (MOVDconst [c]) (MOVDconst [d]) _))
+	// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVDLT {
+			break
+		}
+		x_0 := x.Args[0]
+		if x_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := x_0.AuxInt
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := x_1.AuxInt
+		if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVDLE (MOVDconst [c]) (MOVDconst [d]) _))
+	// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVDLE {
+			break
+		}
+		x_0 := x.Args[0]
+		if x_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := x_0.AuxInt
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := x_1.AuxInt
+		if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVDGT (MOVDconst [c]) (MOVDconst [d]) _))
+	// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVDGT {
+			break
+		}
+		x_0 := x.Args[0]
+		if x_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := x_0.AuxInt
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := x_1.AuxInt
+		if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVDGE (MOVDconst [c]) (MOVDconst [d]) _))
+	// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVDGE {
+			break
+		}
+		x_0 := x.Args[0]
+		if x_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := x_0.AuxInt
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := x_1.AuxInt
+		if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVDEQ (MOVDconst [c]) (MOVDconst [d]) _))
+	// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVDEQ {
+			break
+		}
+		x_0 := x.Args[0]
+		if x_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := x_0.AuxInt
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := x_1.AuxInt
+		if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVDNE (MOVDconst [c]) (MOVDconst [d]) _))
+	// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVDNE {
+			break
+		}
+		x_0 := x.Args[0]
+		if x_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := x_0.AuxInt
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := x_1.AuxInt
+		if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVDGTnoinv (MOVDconst [c]) (MOVDconst [d]) _))
+	// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVDGTnoinv {
+			break
+		}
+		x_0 := x.Args[0]
+		if x_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := x_0.AuxInt
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := x_1.AuxInt
+		if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVDGEnoinv (MOVDconst [c]) (MOVDconst [d]) _))
+	// cond: int64(uint8(c)) == c && int64(uint8(d)) == d
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVDGEnoinv {
+			break
+		}
+		x_0 := x.Args[0]
+		if x_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := x_0.AuxInt
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := x_1.AuxInt
+		if !(int64(uint8(c)) == c && int64(uint8(d)) == d) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVBZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(Arg <t>))
+	// cond: is8BitInt(t) && !isSigned(t)
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpArg {
+			break
+		}
+		t := x.Type
+		if !(is8BitInt(t) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVBZreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBZreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(uint8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = int64(uint8(c))
+		return true
+	}
+	// match: (MOVBZreg x:(MOVBZload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBZload <v.Type> [off] {sym} ptr mem)
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpS390XMOVBZload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVBZreg x:(MOVBZloadidx [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBZloadidx <v.Type> [off] {sym} ptr idx mem)
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpS390XMOVBZloadidx, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVBload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBload   [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVBload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBload [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVBload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVBreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBreg x:(MOVBload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg x:(Arg <t>))
+	// cond: is8BitInt(t) && isSigned(t)
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpArg {
+			break
+		}
+		t := x.Type
+		if !(is8BitInt(t) && isSigned(t)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg x:(MOVBreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVBreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(int8(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = int64(int8(c))
+		return true
+	}
+	// match: (MOVBreg x:(MOVBZload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVBload <v.Type> [off] {sym} ptr mem)
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpS390XMOVBload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVBstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstore [off] {sym} ptr (MOVBreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVBreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVBZreg x) mem)
+	// cond:
+	// result: (MOVBstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVBZreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVBstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore  [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVBstore  [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} ptr (MOVDconst [c]) mem)
+	// cond: validOff(off) && ptr.Op != OpSB
+	// result: (MOVBstoreconst [makeValAndOff(int64(int8(c)),off)] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(validOff(off) && ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVBstoreconst)
+		v.AuxInt = makeValAndOff(int64(int8(c)), off)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore  [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVBstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVBstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVBstoreidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [off] {sym} (ADD ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVBstoreidx [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVBstoreidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [i] {s} p w x:(MOVBstore [i-1] {s} p (SRDconst [8] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVHstore [i-1] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XSRDconst {
+			break
+		}
+		if x_1.AuxInt != 8 {
+			break
+		}
+		if w != x_1.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [i] {s} p w0:(SRDconst [j] w) x:(MOVBstore [i-1] {s} p (SRDconst [j+8] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVHstore [i-1] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w0 := v.Args[1]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		j := w0.AuxInt
+		w := w0.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XSRDconst {
+			break
+		}
+		if x_1.AuxInt != j+8 {
+			break
+		}
+		if w != x_1.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [i] {s} p w x:(MOVBstore [i-1] {s} p (SRWconst [8] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVHstore [i-1] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XSRWconst {
+			break
+		}
+		if x_1.AuxInt != 8 {
+			break
+		}
+		if w != x_1.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [i] {s} p w0:(SRWconst [j] w) x:(MOVBstore [i-1] {s} p (SRWconst [j+8] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVHstore [i-1] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w0 := v.Args[1]
+		if w0.Op != OpS390XSRWconst {
+			break
+		}
+		j := w0.AuxInt
+		w := w0.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XSRWconst {
+			break
+		}
+		if x_1.AuxInt != j+8 {
+			break
+		}
+		if w != x_1.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [i] {s} p (SRDconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVHBRstore [i-1] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRDconst {
+			break
+		}
+		if v_1.AuxInt != 8 {
+			break
+		}
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if w != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHBRstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [i] {s} p (SRDconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SRDconst [j-8] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVHBRstore [i-1] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRDconst {
+			break
+		}
+		j := v_1.AuxInt
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		if w0.AuxInt != j-8 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHBRstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [i] {s} p (SRWconst [8] w) x:(MOVBstore [i-1] {s} p w mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVHBRstore [i-1] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRWconst {
+			break
+		}
+		if v_1.AuxInt != 8 {
+			break
+		}
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if w != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHBRstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstore [i] {s} p (SRWconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SRWconst [j-8] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVHBRstore [i-1] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRWconst {
+			break
+		}
+		j := v_1.AuxInt
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVBstore {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		if w0.Op != OpS390XSRWconst {
+			break
+		}
+		if w0.AuxInt != j-8 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHBRstore)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVBstoreconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstoreconst [sc] {s} (ADDconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	for {
+		sc := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpS390XMOVBstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreconst [sc] {sym1} (MOVDaddr [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
+	// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+	for {
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpS390XMOVBstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {s} p mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVHstoreconst [makeValAndOff(ValAndOff(c).Val()&0xff | ValAndOff(a).Val()<<8, ValAndOff(a).Off())] {s} p mem)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		x := v.Args[1]
+		if x.Op != OpS390XMOVBstoreconst {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		mem := x.Args[1]
+		if !(p.Op != OpSB && x.Uses == 1 && ValAndOff(a).Off()+1 == ValAndOff(c).Off() && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreconst)
+		v.AuxInt = makeValAndOff(ValAndOff(c).Val()&0xff|ValAndOff(a).Val()<<8, ValAndOff(a).Off())
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVBstoreidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVBstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVBstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XMOVBstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem)
+	// cond:
+	// result: (MOVBstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XMOVBstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx [i] {s} p idx w x:(MOVBstoreidx [i-1] {s} p idx (SRDconst [8] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVHstoreidx [i-1] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		w := v.Args[2]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVBstoreidx {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		x_2 := x.Args[2]
+		if x_2.Op != OpS390XSRDconst {
+			break
+		}
+		if x_2.AuxInt != 8 {
+			break
+		}
+		if w != x_2.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreidx)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx [i] {s} p idx w0:(SRDconst [j] w) x:(MOVBstoreidx [i-1] {s} p idx (SRDconst [j+8] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVHstoreidx [i-1] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		w0 := v.Args[2]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		j := w0.AuxInt
+		w := w0.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVBstoreidx {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		x_2 := x.Args[2]
+		if x_2.Op != OpS390XSRDconst {
+			break
+		}
+		if x_2.AuxInt != j+8 {
+			break
+		}
+		if w != x_2.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreidx)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx [i] {s} p idx w x:(MOVBstoreidx [i-1] {s} p idx (SRWconst [8] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVHstoreidx [i-1] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		w := v.Args[2]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVBstoreidx {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		x_2 := x.Args[2]
+		if x_2.Op != OpS390XSRWconst {
+			break
+		}
+		if x_2.AuxInt != 8 {
+			break
+		}
+		if w != x_2.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreidx)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx [i] {s} p idx w0:(SRWconst [j] w) x:(MOVBstoreidx [i-1] {s} p idx (SRWconst [j+8] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVHstoreidx [i-1] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		w0 := v.Args[2]
+		if w0.Op != OpS390XSRWconst {
+			break
+		}
+		j := w0.AuxInt
+		w := w0.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVBstoreidx {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		x_2 := x.Args[2]
+		if x_2.Op != OpS390XSRWconst {
+			break
+		}
+		if x_2.AuxInt != j+8 {
+			break
+		}
+		if w != x_2.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreidx)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx [i] {s} p idx (SRDconst [8] w) x:(MOVBstoreidx [i-1] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVHBRstoreidx [i-1] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XSRDconst {
+			break
+		}
+		if v_2.AuxInt != 8 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVBstoreidx {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHBRstoreidx)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx [i] {s} p idx (SRDconst [j] w) x:(MOVBstoreidx [i-1] {s} p idx w0:(SRDconst [j-8] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVHBRstoreidx [i-1] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XSRDconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVBstoreidx {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		if w0.AuxInt != j-8 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHBRstoreidx)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx [i] {s} p idx (SRWconst [8] w) x:(MOVBstoreidx [i-1] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVHBRstoreidx [i-1] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XSRWconst {
+			break
+		}
+		if v_2.AuxInt != 8 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVBstoreidx {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHBRstoreidx)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVBstoreidx [i] {s} p idx (SRWconst [j] w) x:(MOVBstoreidx [i-1] {s} p idx w0:(SRWconst [j-8] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVHBRstoreidx [i-1] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XSRWconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVBstoreidx {
+			break
+		}
+		if x.AuxInt != i-1 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != OpS390XSRWconst {
+			break
+		}
+		if w0.AuxInt != j-8 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVHBRstoreidx)
+		v.AuxInt = i - 1
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDEQ(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDEQ x y (InvertFlags cmp))
+	// cond:
+	// result: (MOVDEQ x y cmp)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XInvertFlags {
+			break
+		}
+		cmp := v_2.Args[0]
+		v.reset(OpS390XMOVDEQ)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(cmp)
+		return true
+	}
+	// match: (MOVDEQ _ x (FlagEQ))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagEQ {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVDEQ y _ (FlagLT))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagLT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVDEQ y _ (FlagGT))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagGT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDGE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDGE x y (InvertFlags cmp))
+	// cond:
+	// result: (MOVDLE x y cmp)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XInvertFlags {
+			break
+		}
+		cmp := v_2.Args[0]
+		v.reset(OpS390XMOVDLE)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(cmp)
+		return true
+	}
+	// match: (MOVDGE _ x (FlagEQ))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagEQ {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVDGE y _ (FlagLT))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagLT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVDGE _ x (FlagGT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagGT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDGT(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDGT x y (InvertFlags cmp))
+	// cond:
+	// result: (MOVDLT x y cmp)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XInvertFlags {
+			break
+		}
+		cmp := v_2.Args[0]
+		v.reset(OpS390XMOVDLT)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(cmp)
+		return true
+	}
+	// match: (MOVDGT y _ (FlagEQ))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagEQ {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVDGT y _ (FlagLT))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagLT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVDGT _ x (FlagGT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagGT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDLE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDLE x y (InvertFlags cmp))
+	// cond:
+	// result: (MOVDGE x y cmp)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XInvertFlags {
+			break
+		}
+		cmp := v_2.Args[0]
+		v.reset(OpS390XMOVDGE)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(cmp)
+		return true
+	}
+	// match: (MOVDLE _ x (FlagEQ))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagEQ {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVDLE _ x (FlagLT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagLT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVDLE y _ (FlagGT))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagGT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDLT(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDLT x y (InvertFlags cmp))
+	// cond:
+	// result: (MOVDGT x y cmp)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XInvertFlags {
+			break
+		}
+		cmp := v_2.Args[0]
+		v.reset(OpS390XMOVDGT)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(cmp)
+		return true
+	}
+	// match: (MOVDLT y _ (FlagEQ))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagEQ {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVDLT _ x (FlagLT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagLT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVDLT y _ (FlagGT))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagGT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDNE(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDNE x y (InvertFlags cmp))
+	// cond:
+	// result: (MOVDNE x y cmp)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XInvertFlags {
+			break
+		}
+		cmp := v_2.Args[0]
+		v.reset(OpS390XMOVDNE)
+		v.AddArg(x)
+		v.AddArg(y)
+		v.AddArg(cmp)
+		return true
+	}
+	// match: (MOVDNE _ y (FlagEQ))
+	// cond:
+	// result: y
+	for {
+		y := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagEQ {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = y.Type
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVDNE x _ (FlagLT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagLT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVDNE x _ (FlagGT))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XFlagGT {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDaddridx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDaddridx [c] {s} (ADDconst [d] x) y)
+	// cond: is20Bit(c+d) && x.Op != OpSB
+	// result: (MOVDaddridx [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is20Bit(c+d) && x.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVDaddridx)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVDaddridx [c] {s} x (ADDconst [d] y))
+	// cond: is20Bit(c+d) && y.Op != OpSB
+	// result: (MOVDaddridx [c+d] {s} x y)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		y := v_1.Args[0]
+		if !(is20Bit(c+d) && y.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVDaddridx)
+		v.AuxInt = c + d
+		v.Aux = s
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVDaddridx [off1] {sym1} (MOVDaddr [off2] {sym2} x) y)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB
+	// result: (MOVDaddridx [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		x := v_0.Args[0]
+		y := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && x.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVDaddridx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (MOVDaddridx [off1] {sym1} x (MOVDaddr [off2] {sym2} y))
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && y.Op != OpSB
+	// result: (MOVDaddridx [off1+off2] {mergeSym(sym1,sym2)} x y)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		y := v_1.Args[0]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && y.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVDaddridx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDload [off] {sym} ptr (MOVDstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVDload   [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVDload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload  [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVDload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVDload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVDloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVDloadidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDload [off] {sym} (ADD ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVDloadidx [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVDloadidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDloadidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDloadidx [c] {sym} (ADDconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVDloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVDloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDloadidx [c] {sym} ptr (ADDconst [d] idx) mem)
+	// cond:
+	// result: (MOVDloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVDloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDstore  [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVDstore  [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [off] {sym} ptr (MOVDconst [c]) mem)
+	// cond: validValAndOff(c,off) && int64(int16(c)) == c && ptr.Op != OpSB
+	// result: (MOVDstoreconst [makeValAndOff(c,off)] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(validValAndOff(c, off) && int64(int16(c)) == c && ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVDstoreconst)
+		v.AuxInt = makeValAndOff(c, off)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore  [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVDstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVDstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVDstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVDstoreidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [off] {sym} (ADD ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVDstoreidx [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVDstoreidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [i] {s} p w1 x:(MOVDstore [i-8] {s} p w0 mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && is20Bit(i-8)   && clobber(x)
+	// result: (STMG2 [i-8] {s} p w0 w1 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w1 := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVDstore {
+			break
+		}
+		if x.AuxInt != i-8 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && is20Bit(i-8) && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XSTMG2)
+		v.AuxInt = i - 8
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(w1)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [i] {s} p w2 x:(STMG2 [i-16] {s} p w0 w1 mem))
+	// cond: x.Uses == 1   && is20Bit(i-16)   && clobber(x)
+	// result: (STMG3 [i-16] {s} p w0 w1 w2 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w2 := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpS390XSTMG2 {
+			break
+		}
+		if x.AuxInt != i-16 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		w1 := x.Args[2]
+		mem := x.Args[3]
+		if !(x.Uses == 1 && is20Bit(i-16) && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XSTMG3)
+		v.AuxInt = i - 16
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(w1)
+		v.AddArg(w2)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstore [i] {s} p w3 x:(STMG3 [i-24] {s} p w0 w1 w2 mem))
+	// cond: x.Uses == 1   && is20Bit(i-24)   && clobber(x)
+	// result: (STMG4 [i-24] {s} p w0 w1 w2 w3 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w3 := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpS390XSTMG3 {
+			break
+		}
+		if x.AuxInt != i-24 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		w1 := x.Args[2]
+		w2 := x.Args[3]
+		mem := x.Args[4]
+		if !(x.Uses == 1 && is20Bit(i-24) && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XSTMG4)
+		v.AuxInt = i - 24
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(w1)
+		v.AddArg(w2)
+		v.AddArg(w3)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDstoreconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDstoreconst [sc] {s} (ADDconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVDstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	for {
+		sc := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpS390XMOVDstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstoreconst [sc] {sym1} (MOVDaddr [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
+	// result: (MOVDstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+	for {
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpS390XMOVDstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVDstoreidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVDstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVDstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XMOVDstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVDstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem)
+	// cond:
+	// result: (MOVDstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XMOVDstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVHBRstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHBRstore [i] {s} p (SRDconst [16] w) x:(MOVHBRstore [i-2] {s} p w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWBRstore [i-2] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRDconst {
+			break
+		}
+		if v_1.AuxInt != 16 {
+			break
+		}
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVHBRstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if w != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWBRstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHBRstore [i] {s} p (SRDconst [j] w) x:(MOVHBRstore [i-2] {s} p w0:(SRDconst [j-16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWBRstore [i-2] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRDconst {
+			break
+		}
+		j := v_1.AuxInt
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVHBRstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		if w0.AuxInt != j-16 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWBRstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHBRstore [i] {s} p (SRWconst [16] w) x:(MOVHBRstore [i-2] {s} p w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWBRstore [i-2] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRWconst {
+			break
+		}
+		if v_1.AuxInt != 16 {
+			break
+		}
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVHBRstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if w != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWBRstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHBRstore [i] {s} p (SRWconst [j] w) x:(MOVHBRstore [i-2] {s} p w0:(SRWconst [j-16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWBRstore [i-2] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRWconst {
+			break
+		}
+		j := v_1.AuxInt
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVHBRstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		if w0.Op != OpS390XSRWconst {
+			break
+		}
+		if w0.AuxInt != j-16 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWBRstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVHBRstoreidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHBRstoreidx [i] {s} p idx (SRDconst [16] w) x:(MOVHBRstoreidx [i-2] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWBRstoreidx [i-2] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XSRDconst {
+			break
+		}
+		if v_2.AuxInt != 16 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVHBRstoreidx {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWBRstoreidx)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHBRstoreidx [i] {s} p idx (SRDconst [j] w) x:(MOVHBRstoreidx [i-2] {s} p idx w0:(SRDconst [j-16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWBRstoreidx [i-2] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XSRDconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVHBRstoreidx {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		if w0.AuxInt != j-16 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWBRstoreidx)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHBRstoreidx [i] {s} p idx (SRWconst [16] w) x:(MOVHBRstoreidx [i-2] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWBRstoreidx [i-2] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XSRWconst {
+			break
+		}
+		if v_2.AuxInt != 16 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVHBRstoreidx {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWBRstoreidx)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHBRstoreidx [i] {s} p idx (SRWconst [j] w) x:(MOVHBRstoreidx [i-2] {s} p idx w0:(SRWconst [j-16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWBRstoreidx [i-2] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XSRWconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVHBRstoreidx {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != OpS390XSRWconst {
+			break
+		}
+		if w0.AuxInt != j-16 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWBRstoreidx)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVHZload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHZload [off] {sym} ptr (MOVHstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVHstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHZload  [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVHZload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVHZload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHZload  [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVHZload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVHZload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHZload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVHZloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVHZloadidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHZload [off] {sym} (ADD ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVHZloadidx [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVHZloadidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVHZloadidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHZloadidx [c] {sym} (ADDconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVHZloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVHZloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHZloadidx [c] {sym} ptr (ADDconst [d] idx) mem)
+	// cond:
+	// result: (MOVHZloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVHZloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVHZreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHZreg x:(MOVBZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHZreg x:(MOVHZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHZreg x:(Arg <t>))
+	// cond: (is8BitInt(t) || is16BitInt(t)) && !isSigned(t)
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpArg {
+			break
+		}
+		t := x.Type
+		if !((is8BitInt(t) || is16BitInt(t)) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHZreg x:(MOVBZreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHZreg x:(MOVHZreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHZreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(uint16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = int64(uint16(c))
+		return true
+	}
+	// match: (MOVHZreg x:(MOVHZload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVHZload <v.Type> [off] {sym} ptr mem)
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHZload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpS390XMOVHZload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVHZreg x:(MOVHZloadidx [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVHZloadidx <v.Type> [off] {sym} ptr idx mem)
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHZloadidx {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpS390XMOVHZloadidx, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVHload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHload   [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVHload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHload [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVHload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVHload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVHreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHreg x:(MOVBload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(Arg <t>))
+	// cond: (is8BitInt(t) || is16BitInt(t)) && isSigned(t)
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpArg {
+			break
+		}
+		t := x.Type
+		if !((is8BitInt(t) || is16BitInt(t)) && isSigned(t)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVBZreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg x:(MOVHreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVHreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(int16(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = int64(int16(c))
+		return true
+	}
+	// match: (MOVHreg x:(MOVHZload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVHload <v.Type> [off] {sym} ptr mem)
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHZload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpS390XMOVHload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVHstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstore [off] {sym} ptr (MOVHreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVHreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVHZreg x) mem)
+	// cond:
+	// result: (MOVHstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVHZreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVHstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore  [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVHstore  [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} ptr (MOVDconst [c]) mem)
+	// cond: validOff(off) && ptr.Op != OpSB
+	// result: (MOVHstoreconst [makeValAndOff(int64(int16(c)),off)] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(validOff(off) && ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreconst)
+		v.AuxInt = makeValAndOff(int64(int16(c)), off)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore  [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVHstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVHstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVHstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [off] {sym} (ADD ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVHstoreidx [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [i] {s} p w x:(MOVHstore [i-2] {s} p (SRDconst [16] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVWstore [i-2] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVHstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XSRDconst {
+			break
+		}
+		if x_1.AuxInt != 16 {
+			break
+		}
+		if w != x_1.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [i] {s} p w0:(SRDconst [j] w) x:(MOVHstore [i-2] {s} p (SRDconst [j+16] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVWstore [i-2] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w0 := v.Args[1]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		j := w0.AuxInt
+		w := w0.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVHstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XSRDconst {
+			break
+		}
+		if x_1.AuxInt != j+16 {
+			break
+		}
+		if w != x_1.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [i] {s} p w x:(MOVHstore [i-2] {s} p (SRWconst [16] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVWstore [i-2] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVHstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XSRWconst {
+			break
+		}
+		if x_1.AuxInt != 16 {
+			break
+		}
+		if w != x_1.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstore [i] {s} p w0:(SRWconst [j] w) x:(MOVHstore [i-2] {s} p (SRWconst [j+16] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVWstore [i-2] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w0 := v.Args[1]
+		if w0.Op != OpS390XSRWconst {
+			break
+		}
+		j := w0.AuxInt
+		w := w0.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVHstore {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XSRWconst {
+			break
+		}
+		if x_1.AuxInt != j+16 {
+			break
+		}
+		if w != x_1.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWstore)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVHstoreconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstoreconst [sc] {s} (ADDconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVHstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	for {
+		sc := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstoreconst [sc] {sym1} (MOVDaddr [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
+	// result: (MOVHstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+	for {
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstoreconst [c] {s} p x:(MOVHstoreconst [a] {s} p mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVWstoreconst [makeValAndOff(ValAndOff(c).Val()&0xffff | ValAndOff(a).Val()<<16, ValAndOff(a).Off())] {s} p mem)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		x := v.Args[1]
+		if x.Op != OpS390XMOVHstoreconst {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		mem := x.Args[1]
+		if !(p.Op != OpSB && x.Uses == 1 && ValAndOff(a).Off()+2 == ValAndOff(c).Off() && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreconst)
+		v.AuxInt = makeValAndOff(ValAndOff(c).Val()&0xffff|ValAndOff(a).Val()<<16, ValAndOff(a).Off())
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVHstoreidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVHstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVHstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XMOVHstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem)
+	// cond:
+	// result: (MOVHstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XMOVHstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstoreidx [i] {s} p idx w x:(MOVHstoreidx [i-2] {s} p idx (SRDconst [16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstoreidx [i-2] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		w := v.Args[2]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVHstoreidx {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		x_2 := x.Args[2]
+		if x_2.Op != OpS390XSRDconst {
+			break
+		}
+		if x_2.AuxInt != 16 {
+			break
+		}
+		if w != x_2.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreidx)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstoreidx [i] {s} p idx w0:(SRDconst [j] w) x:(MOVHstoreidx [i-2] {s} p idx (SRDconst [j+16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstoreidx [i-2] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		w0 := v.Args[2]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		j := w0.AuxInt
+		w := w0.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVHstoreidx {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		x_2 := x.Args[2]
+		if x_2.Op != OpS390XSRDconst {
+			break
+		}
+		if x_2.AuxInt != j+16 {
+			break
+		}
+		if w != x_2.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreidx)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstoreidx [i] {s} p idx w x:(MOVHstoreidx [i-2] {s} p idx (SRWconst [16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstoreidx [i-2] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		w := v.Args[2]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVHstoreidx {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		x_2 := x.Args[2]
+		if x_2.Op != OpS390XSRWconst {
+			break
+		}
+		if x_2.AuxInt != 16 {
+			break
+		}
+		if w != x_2.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreidx)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVHstoreidx [i] {s} p idx w0:(SRWconst [j] w) x:(MOVHstoreidx [i-2] {s} p idx (SRWconst [j+16] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVWstoreidx [i-2] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		w0 := v.Args[2]
+		if w0.Op != OpS390XSRWconst {
+			break
+		}
+		j := w0.AuxInt
+		w := w0.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVHstoreidx {
+			break
+		}
+		if x.AuxInt != i-2 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		x_2 := x.Args[2]
+		if x_2.Op != OpS390XSRWconst {
+			break
+		}
+		if x_2.AuxInt != j+16 {
+			break
+		}
+		if w != x_2.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreidx)
+		v.AuxInt = i - 2
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVWBRstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWBRstore [i] {s} p (SRDconst [32] w) x:(MOVWBRstore [i-4] {s} p w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVDBRstore [i-4] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRDconst {
+			break
+		}
+		if v_1.AuxInt != 32 {
+			break
+		}
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVWBRstore {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if w != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVDBRstore)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWBRstore [i] {s} p (SRDconst [j] w) x:(MOVWBRstore [i-4] {s} p w0:(SRDconst [j-32] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVDBRstore [i-4] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRDconst {
+			break
+		}
+		j := v_1.AuxInt
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVWBRstore {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		if w0.AuxInt != j-32 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVDBRstore)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVWBRstoreidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWBRstoreidx [i] {s} p idx (SRDconst [32] w) x:(MOVWBRstoreidx [i-4] {s} p idx w mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVDBRstoreidx [i-4] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XSRDconst {
+			break
+		}
+		if v_2.AuxInt != 32 {
+			break
+		}
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVWBRstoreidx {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		if w != x.Args[2] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVDBRstoreidx)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWBRstoreidx [i] {s} p idx (SRDconst [j] w) x:(MOVWBRstoreidx [i-4] {s} p idx w0:(SRDconst [j-32] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVDBRstoreidx [i-4] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		v_2 := v.Args[2]
+		if v_2.Op != OpS390XSRDconst {
+			break
+		}
+		j := v_2.AuxInt
+		w := v_2.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVWBRstoreidx {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		w0 := x.Args[2]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		if w0.AuxInt != j-32 {
+			break
+		}
+		if w != w0.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVDBRstoreidx)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVWZload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWZload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _))
+	// cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)
+	// result: x
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVWstore {
+			break
+		}
+		off2 := v_1.AuxInt
+		sym2 := v_1.Aux
+		ptr2 := v_1.Args[0]
+		x := v_1.Args[1]
+		if !(sym == sym2 && off == off2 && isSamePtr(ptr, ptr2)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWZload  [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVWZload [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVWZload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWZload  [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWZload  [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVWZload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWZload [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWZloadidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVWZloadidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWZload [off] {sym} (ADD ptr idx) mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVWZloadidx [off] {sym} ptr idx mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		mem := v.Args[1]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVWZloadidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVWZloadidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWZloadidx [c] {sym} (ADDconst [d] ptr) idx mem)
+	// cond:
+	// result: (MOVWZloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVWZloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWZloadidx [c] {sym} ptr (ADDconst [d] idx) mem)
+	// cond:
+	// result: (MOVWZloadidx [c+d] {sym} ptr idx mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVWZloadidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVWZreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWZreg x:(MOVBZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWZreg x:(MOVHZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWZreg x:(MOVWZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVWZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWZreg x:(Arg <t>))
+	// cond: (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && !isSigned(t)
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpArg {
+			break
+		}
+		t := x.Type
+		if !((is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && !isSigned(t)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWZreg x:(MOVBZreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWZreg x:(MOVHZreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWZreg x:(MOVWZreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVWZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWZreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(uint32(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = int64(uint32(c))
+		return true
+	}
+	// match: (MOVWZreg x:(MOVWZload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWZload <v.Type> [off] {sym} ptr mem)
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVWZload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpS390XMOVWZload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (MOVWZreg x:(MOVWZloadidx [off] {sym} ptr idx mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWZloadidx <v.Type> [off] {sym} ptr idx mem)
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVWZloadidx {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		idx := x.Args[1]
+		mem := x.Args[2]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpS390XMOVWZloadidx, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVWload(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWload   [off1] {sym} (ADDconst [off2] ptr) mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVWload  [off1+off2] {sym} ptr mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWload [off1] {sym1} (MOVDaddr [off2] {sym2} base) mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		mem := v.Args[1]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVWload)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVWreg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWreg x:(MOVBload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVBZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHZload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHZload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVWload _ _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVWload {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(Arg <t>))
+	// cond: (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && isSigned(t)
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpArg {
+			break
+		}
+		t := x.Type
+		if !((is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && isSigned(t)) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVBreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVBZreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVBZreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVHreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVHreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg x:(MOVWreg _))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVWreg {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MOVWreg (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(int32(c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = int64(int32(c))
+		return true
+	}
+	// match: (MOVWreg x:(MOVWZload [off] {sym} ptr mem))
+	// cond: x.Uses == 1 && clobber(x)
+	// result: @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
+	for {
+		x := v.Args[0]
+		if x.Op != OpS390XMOVWZload {
+			break
+		}
+		off := x.AuxInt
+		sym := x.Aux
+		ptr := x.Args[0]
+		mem := x.Args[1]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		b = x.Block
+		v0 := b.NewValue0(v.Line, OpS390XMOVWload, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = off
+		v0.Aux = sym
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVWstore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstore [off] {sym} ptr (MOVWreg x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVWreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVWZreg x) mem)
+	// cond:
+	// result: (MOVWstore [off] {sym} ptr x mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVWZreg {
+			break
+		}
+		x := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVWstore)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore  [off1] {sym} (ADDconst [off2] ptr) val mem)
+	// cond: is20Bit(off1+off2)
+	// result: (MOVWstore  [off1+off2] {sym} ptr val mem)
+	for {
+		off1 := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off2 := v_0.AuxInt
+		ptr := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is20Bit(off1 + off2)) {
+			break
+		}
+		v.reset(OpS390XMOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} ptr (MOVDconst [c]) mem)
+	// cond: validOff(off) && int64(int16(c)) == c && ptr.Op != OpSB
+	// result: (MOVWstoreconst [makeValAndOff(int64(int32(c)),off)] {sym} ptr mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		mem := v.Args[2]
+		if !(validOff(off) && int64(int16(c)) == c && ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreconst)
+		v.AuxInt = makeValAndOff(int64(int32(c)), off)
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore  [off1] {sym1} (MOVDaddr [off2] {sym2} base) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWstore  [off1+off2] {mergeSym(sym1,sym2)} base val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		base := v_0.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVWstore)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(base)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off1] {sym1} (MOVDaddridx [off2] {sym2} ptr idx) val mem)
+	// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2)
+	// result: (MOVWstoreidx [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
+	for {
+		off1 := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddridx {
+			break
+		}
+		off2 := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreidx)
+		v.AuxInt = off1 + off2
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [off] {sym} (ADD ptr idx) val mem)
+	// cond: ptr.Op != OpSB
+	// result: (MOVWstoreidx [off] {sym} ptr idx val mem)
+	for {
+		off := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADD {
+			break
+		}
+		ptr := v_0.Args[0]
+		idx := v_0.Args[1]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(ptr.Op != OpSB) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreidx)
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [i] {s} p (SRDconst [32] w) x:(MOVWstore [i-4] {s} p w mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVDstore [i-4] {s} p w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRDconst {
+			break
+		}
+		if v_1.AuxInt != 32 {
+			break
+		}
+		w := v_1.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVWstore {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if w != x.Args[1] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVDstore)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [i] {s} p w0:(SRDconst [j] w) x:(MOVWstore [i-4] {s} p (SRDconst [j+32] w) mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && clobber(x)
+	// result: (MOVDstore [i-4] {s} p w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w0 := v.Args[1]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		j := w0.AuxInt
+		w := w0.Args[0]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVWstore {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpS390XSRDconst {
+			break
+		}
+		if x_1.AuxInt != j+32 {
+			break
+		}
+		if w != x_1.Args[0] {
+			break
+		}
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVDstore)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [i] {s} p w1 x:(MOVWstore [i-4] {s} p w0 mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && is20Bit(i-4)   && clobber(x)
+	// result: (STM2 [i-4] {s} p w0 w1 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w1 := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpS390XMOVWstore {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		mem := x.Args[2]
+		if !(p.Op != OpSB && x.Uses == 1 && is20Bit(i-4) && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XSTM2)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(w1)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [i] {s} p w2 x:(STM2 [i-8] {s} p w0 w1 mem))
+	// cond: x.Uses == 1   && is20Bit(i-8)   && clobber(x)
+	// result: (STM3 [i-8] {s} p w0 w1 w2 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w2 := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpS390XSTM2 {
+			break
+		}
+		if x.AuxInt != i-8 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		w1 := x.Args[2]
+		mem := x.Args[3]
+		if !(x.Uses == 1 && is20Bit(i-8) && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XSTM3)
+		v.AuxInt = i - 8
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(w1)
+		v.AddArg(w2)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstore [i] {s} p w3 x:(STM3 [i-12] {s} p w0 w1 w2 mem))
+	// cond: x.Uses == 1   && is20Bit(i-12)   && clobber(x)
+	// result: (STM4 [i-12] {s} p w0 w1 w2 w3 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w3 := v.Args[1]
+		x := v.Args[2]
+		if x.Op != OpS390XSTM3 {
+			break
+		}
+		if x.AuxInt != i-12 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		w1 := x.Args[2]
+		w2 := x.Args[3]
+		mem := x.Args[4]
+		if !(x.Uses == 1 && is20Bit(i-12) && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XSTM4)
+		v.AuxInt = i - 12
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(w1)
+		v.AddArg(w2)
+		v.AddArg(w3)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVWstoreconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreconst [sc] {s} (ADDconst [off] ptr) mem)
+	// cond: ValAndOff(sc).canAdd(off)
+	// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {s} ptr mem)
+	for {
+		sc := v.AuxInt
+		s := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		off := v_0.AuxInt
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = s
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconst [sc] {sym1} (MOVDaddr [off] {sym2} ptr) mem)
+	// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
+	// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
+	for {
+		sc := v.AuxInt
+		sym1 := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDaddr {
+			break
+		}
+		off := v_0.AuxInt
+		sym2 := v_0.Aux
+		ptr := v_0.Args[0]
+		mem := v.Args[1]
+		if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreconst)
+		v.AuxInt = ValAndOff(sc).add(off)
+		v.Aux = mergeSym(sym1, sym2)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem))
+	// cond: p.Op != OpSB   && x.Uses == 1   && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()   && clobber(x)
+	// result: (MOVDstore [ValAndOff(a).Off()] {s} p (MOVDconst [ValAndOff(c).Val()&0xffffffff | ValAndOff(a).Val()<<32]) mem)
+	for {
+		c := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		x := v.Args[1]
+		if x.Op != OpS390XMOVWstoreconst {
+			break
+		}
+		a := x.AuxInt
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		mem := x.Args[1]
+		if !(p.Op != OpSB && x.Uses == 1 && ValAndOff(a).Off()+4 == ValAndOff(c).Off() && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVDstore)
+		v.AuxInt = ValAndOff(a).Off()
+		v.Aux = s
+		v.AddArg(p)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = ValAndOff(c).Val()&0xffffffff | ValAndOff(a).Val()<<32
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMOVWstoreidx(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MOVWstoreidx [c] {sym} (ADDconst [d] ptr) idx val mem)
+	// cond:
+	// result: (MOVWstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XADDconst {
+			break
+		}
+		d := v_0.AuxInt
+		ptr := v_0.Args[0]
+		idx := v.Args[1]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XMOVWstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx [c] {sym} ptr (ADDconst [d] idx) val mem)
+	// cond:
+	// result: (MOVWstoreidx [c+d] {sym} ptr idx val mem)
+	for {
+		c := v.AuxInt
+		sym := v.Aux
+		ptr := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XADDconst {
+			break
+		}
+		d := v_1.AuxInt
+		idx := v_1.Args[0]
+		val := v.Args[2]
+		mem := v.Args[3]
+		v.reset(OpS390XMOVWstoreidx)
+		v.AuxInt = c + d
+		v.Aux = sym
+		v.AddArg(ptr)
+		v.AddArg(idx)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx [i] {s} p idx w x:(MOVWstoreidx [i-4] {s} p idx (SRDconst [32] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVDstoreidx [i-4] {s} p idx w mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		w := v.Args[2]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVWstoreidx {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		x_2 := x.Args[2]
+		if x_2.Op != OpS390XSRDconst {
+			break
+		}
+		if x_2.AuxInt != 32 {
+			break
+		}
+		if w != x_2.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVDstoreidx)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MOVWstoreidx [i] {s} p idx w0:(SRDconst [j] w) x:(MOVWstoreidx [i-4] {s} p idx (SRDconst [j+32] w) mem))
+	// cond: x.Uses == 1   && clobber(x)
+	// result: (MOVDstoreidx [i-4] {s} p idx w0 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		idx := v.Args[1]
+		w0 := v.Args[2]
+		if w0.Op != OpS390XSRDconst {
+			break
+		}
+		j := w0.AuxInt
+		w := w0.Args[0]
+		x := v.Args[3]
+		if x.Op != OpS390XMOVWstoreidx {
+			break
+		}
+		if x.AuxInt != i-4 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		if idx != x.Args[1] {
+			break
+		}
+		x_2 := x.Args[2]
+		if x_2.Op != OpS390XSRDconst {
+			break
+		}
+		if x_2.AuxInt != j+32 {
+			break
+		}
+		if w != x_2.Args[0] {
+			break
+		}
+		mem := x.Args[3]
+		if !(x.Uses == 1 && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XMOVDstoreidx)
+		v.AuxInt = i - 4
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(idx)
+		v.AddArg(w0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMULLD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MULLD x (MOVDconst [c]))
+	// cond: is32Bit(c)
+	// result: (MULLDconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XMULLDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLD (MOVDconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (MULLDconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XMULLDconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLD <t> x g:(MOVDload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (MULLDload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XMULLDload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MULLD <t> g:(MOVDload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (MULLDload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XMULLDload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMULLDconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MULLDconst [-1] x)
+	// cond:
+	// result: (NEG x)
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpS390XNEG)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLDconst [0] _)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (MULLDconst [1] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLDconst [c] x)
+	// cond: isPowerOfTwo(c)
+	// result: (SLDconst [log2(c)] x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpS390XSLDconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLDconst [c] x)
+	// cond: isPowerOfTwo(c+1) && c >= 15
+	// result: (SUB (SLDconst <v.Type> [log2(c+1)] x) x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c+1) && c >= 15) {
+			break
+		}
+		v.reset(OpS390XSUB)
+		v0 := b.NewValue0(v.Line, OpS390XSLDconst, v.Type)
+		v0.AuxInt = log2(c + 1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLDconst [c] x)
+	// cond: isPowerOfTwo(c-1) && c >= 17
+	// result: (ADD (SLDconst <v.Type> [log2(c-1)] x) x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c-1) && c >= 17) {
+			break
+		}
+		v.reset(OpS390XADD)
+		v0 := b.NewValue0(v.Line, OpS390XSLDconst, v.Type)
+		v0.AuxInt = log2(c - 1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLDconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c*d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c * d
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMULLW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MULLW x (MOVDconst [c]))
+	// cond:
+	// result: (MULLWconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XMULLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLW (MOVDconst [c]) x)
+	// cond:
+	// result: (MULLWconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpS390XMULLWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLW <t> x g:(MOVWload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (MULLWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XMULLWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MULLW <t> g:(MOVWload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (MULLWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XMULLWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MULLW <t> x g:(MOVWZload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (MULLWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XMULLWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (MULLW <t> g:(MOVWZload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (MULLWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XMULLWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XMULLWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (MULLWconst [-1] x)
+	// cond:
+	// result: (NEGW x)
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpS390XNEGW)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLWconst [0] _)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (MULLWconst [1] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 1 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLWconst [c] x)
+	// cond: isPowerOfTwo(c)
+	// result: (SLWconst [log2(c)] x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c)) {
+			break
+		}
+		v.reset(OpS390XSLWconst)
+		v.AuxInt = log2(c)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLWconst [c] x)
+	// cond: isPowerOfTwo(c+1) && c >= 15
+	// result: (SUBW (SLWconst <v.Type> [log2(c+1)] x) x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c+1) && c >= 15) {
+			break
+		}
+		v.reset(OpS390XSUBW)
+		v0 := b.NewValue0(v.Line, OpS390XSLWconst, v.Type)
+		v0.AuxInt = log2(c + 1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLWconst [c] x)
+	// cond: isPowerOfTwo(c-1) && c >= 17
+	// result: (ADDW (SLWconst <v.Type> [log2(c-1)] x) x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(isPowerOfTwo(c-1) && c >= 17) {
+			break
+		}
+		v.reset(OpS390XADDW)
+		v0 := b.NewValue0(v.Line, OpS390XSLWconst, v.Type)
+		v0.AuxInt = log2(c - 1)
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	// match: (MULLWconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [int64(int32(c*d))])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = int64(int32(c * d))
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XNEG(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NEG (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [-c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = -c
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XNEGW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NEGW (MOVDconst [c]))
+	// cond:
+	// result: (MOVDconst [int64(int32(-c))])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = int64(int32(-c))
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XNOT(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NOT x)
+	// cond: true
+	// result: (XOR (MOVDconst [-1]) x)
+	for {
+		x := v.Args[0]
+		if !(true) {
+			break
+		}
+		v.reset(OpS390XXOR)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = -1
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XNOTW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (NOTW x)
+	// cond: true
+	// result: (XORWconst [-1] x)
+	for {
+		x := v.Args[0]
+		if !(true) {
+			break
+		}
+		v.reset(OpS390XXORWconst)
+		v.AuxInt = -1
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OR x (MOVDconst [c]))
+	// cond: isU32Bit(c)
+	// result: (ORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isU32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR (MOVDconst [c]) x)
+	// cond: isU32Bit(c)
+	// result: (ORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isU32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c|d])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c | d
+		return true
+	}
+	// match: (OR x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (OR <t> x g:(MOVDload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ORload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XORload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (OR <t> g:(MOVDload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ORload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XORload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (OR o0:(OR o1:(OR o2:(OR o3:(OR o4:(OR o5:(OR                       x0:(MOVBZload [i]   {s} p mem)     s0:(SLDconst [8]  x1:(MOVBZload [i+1] {s} p mem)))     s1:(SLDconst [16] x2:(MOVBZload [i+2] {s} p mem)))     s2:(SLDconst [24] x3:(MOVBZload [i+3] {s} p mem)))     s3:(SLDconst [32] x4:(MOVBZload [i+4] {s} p mem)))     s4:(SLDconst [40] x5:(MOVBZload [i+5] {s} p mem)))     s5:(SLDconst [48] x6:(MOVBZload [i+6] {s} p mem)))     s6:(SLDconst [56] x7:(MOVBZload [i+7] {s} p mem)))
+	// cond: p.Op != OpSB   && x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && x4.Uses == 1   && x5.Uses == 1   && x6.Uses == 1   && x7.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && s3.Uses == 1   && s4.Uses == 1   && s5.Uses == 1   && s6.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && o2.Uses == 1   && o3.Uses == 1   && o4.Uses == 1   && o5.Uses == 1   && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil   && clobber(x0)   && clobber(x1)   && cl [...]
+	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVDBRload [i] {s} p mem)
+	for {
+		o0 := v.Args[0]
+		if o0.Op != OpS390XOR {
+			break
+		}
+		o1 := o0.Args[0]
+		if o1.Op != OpS390XOR {
+			break
+		}
+		o2 := o1.Args[0]
+		if o2.Op != OpS390XOR {
+			break
+		}
+		o3 := o2.Args[0]
+		if o3.Op != OpS390XOR {
+			break
+		}
+		o4 := o3.Args[0]
+		if o4.Op != OpS390XOR {
+			break
+		}
+		o5 := o4.Args[0]
+		if o5.Op != OpS390XOR {
+			break
+		}
+		x0 := o5.Args[0]
+		if x0.Op != OpS390XMOVBZload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := o5.Args[1]
+		if s0.Op != OpS390XSLDconst {
+			break
+		}
+		if s0.AuxInt != 8 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZload {
+			break
+		}
+		if x1.AuxInt != i+1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		s1 := o4.Args[1]
+		if s1.Op != OpS390XSLDconst {
+			break
+		}
+		if s1.AuxInt != 16 {
+			break
+		}
+		x2 := s1.Args[0]
+		if x2.Op != OpS390XMOVBZload {
+			break
+		}
+		if x2.AuxInt != i+2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		s2 := o3.Args[1]
+		if s2.Op != OpS390XSLDconst {
+			break
+		}
+		if s2.AuxInt != 24 {
+			break
+		}
+		x3 := s2.Args[0]
+		if x3.Op != OpS390XMOVBZload {
+			break
+		}
+		if x3.AuxInt != i+3 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		if mem != x3.Args[1] {
+			break
+		}
+		s3 := o2.Args[1]
+		if s3.Op != OpS390XSLDconst {
+			break
+		}
+		if s3.AuxInt != 32 {
+			break
+		}
+		x4 := s3.Args[0]
+		if x4.Op != OpS390XMOVBZload {
+			break
+		}
+		if x4.AuxInt != i+4 {
+			break
+		}
+		if x4.Aux != s {
+			break
+		}
+		if p != x4.Args[0] {
+			break
+		}
+		if mem != x4.Args[1] {
+			break
+		}
+		s4 := o1.Args[1]
+		if s4.Op != OpS390XSLDconst {
+			break
+		}
+		if s4.AuxInt != 40 {
+			break
+		}
+		x5 := s4.Args[0]
+		if x5.Op != OpS390XMOVBZload {
+			break
+		}
+		if x5.AuxInt != i+5 {
+			break
+		}
+		if x5.Aux != s {
+			break
+		}
+		if p != x5.Args[0] {
+			break
+		}
+		if mem != x5.Args[1] {
+			break
+		}
+		s5 := o0.Args[1]
+		if s5.Op != OpS390XSLDconst {
+			break
+		}
+		if s5.AuxInt != 48 {
+			break
+		}
+		x6 := s5.Args[0]
+		if x6.Op != OpS390XMOVBZload {
+			break
+		}
+		if x6.AuxInt != i+6 {
+			break
+		}
+		if x6.Aux != s {
+			break
+		}
+		if p != x6.Args[0] {
+			break
+		}
+		if mem != x6.Args[1] {
+			break
+		}
+		s6 := v.Args[1]
+		if s6.Op != OpS390XSLDconst {
+			break
+		}
+		if s6.AuxInt != 56 {
+			break
+		}
+		x7 := s6.Args[0]
+		if x7.Op != OpS390XMOVBZload {
+			break
+		}
+		if x7.AuxInt != i+7 {
+			break
+		}
+		if x7.Aux != s {
+			break
+		}
+		if p != x7.Args[0] {
+			break
+		}
+		if mem != x7.Args[1] {
+			break
+		}
+		if !(p.Op != OpSB && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && cl [...]
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDBRload, config.fe.TypeUInt64())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (OR o0:(OR o1:(OR o2:(OR o3:(OR o4:(OR o5:(OR                       x0:(MOVBZloadidx [i]   {s} p idx mem)     s0:(SLDconst [8]  x1:(MOVBZloadidx [i+1] {s} p idx mem)))     s1:(SLDconst [16] x2:(MOVBZloadidx [i+2] {s} p idx mem)))     s2:(SLDconst [24] x3:(MOVBZloadidx [i+3] {s} p idx mem)))     s3:(SLDconst [32] x4:(MOVBZloadidx [i+4] {s} p idx mem)))     s4:(SLDconst [40] x5:(MOVBZloadidx [i+5] {s} p idx mem)))     s5:(SLDconst [48] x6:(MOVBZloadidx [i+6] {s} p idx mem)))     [...]
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && x4.Uses == 1   && x5.Uses == 1   && x6.Uses == 1   && x7.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && s3.Uses == 1   && s4.Uses == 1   && s5.Uses == 1   && s6.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && o2.Uses == 1   && o3.Uses == 1   && o4.Uses == 1   && o5.Uses == 1   && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clo [...]
+	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVDBRloadidx <v.Type> [i] {s} p idx mem)
+	for {
+		o0 := v.Args[0]
+		if o0.Op != OpS390XOR {
+			break
+		}
+		o1 := o0.Args[0]
+		if o1.Op != OpS390XOR {
+			break
+		}
+		o2 := o1.Args[0]
+		if o2.Op != OpS390XOR {
+			break
+		}
+		o3 := o2.Args[0]
+		if o3.Op != OpS390XOR {
+			break
+		}
+		o4 := o3.Args[0]
+		if o4.Op != OpS390XOR {
+			break
+		}
+		o5 := o4.Args[0]
+		if o5.Op != OpS390XOR {
+			break
+		}
+		x0 := o5.Args[0]
+		if x0.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := o5.Args[1]
+		if s0.Op != OpS390XSLDconst {
+			break
+		}
+		if s0.AuxInt != 8 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x1.AuxInt != i+1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if idx != x1.Args[1] {
+			break
+		}
+		if mem != x1.Args[2] {
+			break
+		}
+		s1 := o4.Args[1]
+		if s1.Op != OpS390XSLDconst {
+			break
+		}
+		if s1.AuxInt != 16 {
+			break
+		}
+		x2 := s1.Args[0]
+		if x2.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x2.AuxInt != i+2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if idx != x2.Args[1] {
+			break
+		}
+		if mem != x2.Args[2] {
+			break
+		}
+		s2 := o3.Args[1]
+		if s2.Op != OpS390XSLDconst {
+			break
+		}
+		if s2.AuxInt != 24 {
+			break
+		}
+		x3 := s2.Args[0]
+		if x3.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x3.AuxInt != i+3 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		if idx != x3.Args[1] {
+			break
+		}
+		if mem != x3.Args[2] {
+			break
+		}
+		s3 := o2.Args[1]
+		if s3.Op != OpS390XSLDconst {
+			break
+		}
+		if s3.AuxInt != 32 {
+			break
+		}
+		x4 := s3.Args[0]
+		if x4.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x4.AuxInt != i+4 {
+			break
+		}
+		if x4.Aux != s {
+			break
+		}
+		if p != x4.Args[0] {
+			break
+		}
+		if idx != x4.Args[1] {
+			break
+		}
+		if mem != x4.Args[2] {
+			break
+		}
+		s4 := o1.Args[1]
+		if s4.Op != OpS390XSLDconst {
+			break
+		}
+		if s4.AuxInt != 40 {
+			break
+		}
+		x5 := s4.Args[0]
+		if x5.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x5.AuxInt != i+5 {
+			break
+		}
+		if x5.Aux != s {
+			break
+		}
+		if p != x5.Args[0] {
+			break
+		}
+		if idx != x5.Args[1] {
+			break
+		}
+		if mem != x5.Args[2] {
+			break
+		}
+		s5 := o0.Args[1]
+		if s5.Op != OpS390XSLDconst {
+			break
+		}
+		if s5.AuxInt != 48 {
+			break
+		}
+		x6 := s5.Args[0]
+		if x6.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x6.AuxInt != i+6 {
+			break
+		}
+		if x6.Aux != s {
+			break
+		}
+		if p != x6.Args[0] {
+			break
+		}
+		if idx != x6.Args[1] {
+			break
+		}
+		if mem != x6.Args[2] {
+			break
+		}
+		s6 := v.Args[1]
+		if s6.Op != OpS390XSLDconst {
+			break
+		}
+		if s6.AuxInt != 56 {
+			break
+		}
+		x7 := s6.Args[0]
+		if x7.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x7.AuxInt != i+7 {
+			break
+		}
+		if x7.Aux != s {
+			break
+		}
+		if p != x7.Args[0] {
+			break
+		}
+		if idx != x7.Args[1] {
+			break
+		}
+		if mem != x7.Args[2] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clo [...]
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDBRloadidx, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (OR o0:(OR o1:(OR o2:(OR o3:(OR o4:(OR o5:(OR                       x0:(MOVBZload [i]   {s} p mem)     s0:(SLDconst [8]  x1:(MOVBZload [i-1] {s} p mem)))     s1:(SLDconst [16] x2:(MOVBZload [i-2] {s} p mem)))     s2:(SLDconst [24] x3:(MOVBZload [i-3] {s} p mem)))     s3:(SLDconst [32] x4:(MOVBZload [i-4] {s} p mem)))     s4:(SLDconst [40] x5:(MOVBZload [i-5] {s} p mem)))     s5:(SLDconst [48] x6:(MOVBZload [i-6] {s} p mem)))     s6:(SLDconst [56] x7:(MOVBZload [i-7] {s} p mem)))
+	// cond: p.Op != OpSB   && x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && x4.Uses == 1   && x5.Uses == 1   && x6.Uses == 1   && x7.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && s3.Uses == 1   && s4.Uses == 1   && s5.Uses == 1   && s6.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && o2.Uses == 1   && o3.Uses == 1   && o4.Uses == 1   && o5.Uses == 1   && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil   && clobber(x0)   && clobber(x1)   && cl [...]
+	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVDload [i-7] {s} p mem)
+	for {
+		o0 := v.Args[0]
+		if o0.Op != OpS390XOR {
+			break
+		}
+		o1 := o0.Args[0]
+		if o1.Op != OpS390XOR {
+			break
+		}
+		o2 := o1.Args[0]
+		if o2.Op != OpS390XOR {
+			break
+		}
+		o3 := o2.Args[0]
+		if o3.Op != OpS390XOR {
+			break
+		}
+		o4 := o3.Args[0]
+		if o4.Op != OpS390XOR {
+			break
+		}
+		o5 := o4.Args[0]
+		if o5.Op != OpS390XOR {
+			break
+		}
+		x0 := o5.Args[0]
+		if x0.Op != OpS390XMOVBZload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := o5.Args[1]
+		if s0.Op != OpS390XSLDconst {
+			break
+		}
+		if s0.AuxInt != 8 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZload {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		s1 := o4.Args[1]
+		if s1.Op != OpS390XSLDconst {
+			break
+		}
+		if s1.AuxInt != 16 {
+			break
+		}
+		x2 := s1.Args[0]
+		if x2.Op != OpS390XMOVBZload {
+			break
+		}
+		if x2.AuxInt != i-2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		s2 := o3.Args[1]
+		if s2.Op != OpS390XSLDconst {
+			break
+		}
+		if s2.AuxInt != 24 {
+			break
+		}
+		x3 := s2.Args[0]
+		if x3.Op != OpS390XMOVBZload {
+			break
+		}
+		if x3.AuxInt != i-3 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		if mem != x3.Args[1] {
+			break
+		}
+		s3 := o2.Args[1]
+		if s3.Op != OpS390XSLDconst {
+			break
+		}
+		if s3.AuxInt != 32 {
+			break
+		}
+		x4 := s3.Args[0]
+		if x4.Op != OpS390XMOVBZload {
+			break
+		}
+		if x4.AuxInt != i-4 {
+			break
+		}
+		if x4.Aux != s {
+			break
+		}
+		if p != x4.Args[0] {
+			break
+		}
+		if mem != x4.Args[1] {
+			break
+		}
+		s4 := o1.Args[1]
+		if s4.Op != OpS390XSLDconst {
+			break
+		}
+		if s4.AuxInt != 40 {
+			break
+		}
+		x5 := s4.Args[0]
+		if x5.Op != OpS390XMOVBZload {
+			break
+		}
+		if x5.AuxInt != i-5 {
+			break
+		}
+		if x5.Aux != s {
+			break
+		}
+		if p != x5.Args[0] {
+			break
+		}
+		if mem != x5.Args[1] {
+			break
+		}
+		s5 := o0.Args[1]
+		if s5.Op != OpS390XSLDconst {
+			break
+		}
+		if s5.AuxInt != 48 {
+			break
+		}
+		x6 := s5.Args[0]
+		if x6.Op != OpS390XMOVBZload {
+			break
+		}
+		if x6.AuxInt != i-6 {
+			break
+		}
+		if x6.Aux != s {
+			break
+		}
+		if p != x6.Args[0] {
+			break
+		}
+		if mem != x6.Args[1] {
+			break
+		}
+		s6 := v.Args[1]
+		if s6.Op != OpS390XSLDconst {
+			break
+		}
+		if s6.AuxInt != 56 {
+			break
+		}
+		x7 := s6.Args[0]
+		if x7.Op != OpS390XMOVBZload {
+			break
+		}
+		if x7.AuxInt != i-7 {
+			break
+		}
+		if x7.Aux != s {
+			break
+		}
+		if p != x7.Args[0] {
+			break
+		}
+		if mem != x7.Args[1] {
+			break
+		}
+		if !(p.Op != OpSB && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && cl [...]
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDload, config.fe.TypeUInt64())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i - 7
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (OR o0:(OR o1:(OR o2:(OR o3:(OR o4:(OR o5:(OR                       x0:(MOVBZloadidx [i]   {s} p idx mem)     s0:(SLDconst [8]  x1:(MOVBZloadidx [i-1] {s} p idx mem)))     s1:(SLDconst [16] x2:(MOVBZloadidx [i-2] {s} p idx mem)))     s2:(SLDconst [24] x3:(MOVBZloadidx [i-3] {s} p idx mem)))     s3:(SLDconst [32] x4:(MOVBZloadidx [i-4] {s} p idx mem)))     s4:(SLDconst [40] x5:(MOVBZloadidx [i-5] {s} p idx mem)))     s5:(SLDconst [48] x6:(MOVBZloadidx [i-6] {s} p idx mem)))     [...]
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && x3.Uses == 1   && x4.Uses == 1   && x5.Uses == 1   && x6.Uses == 1   && x7.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && s2.Uses == 1   && s3.Uses == 1   && s4.Uses == 1   && s5.Uses == 1   && s6.Uses == 1   && o0.Uses == 1   && o1.Uses == 1   && o2.Uses == 1   && o3.Uses == 1   && o4.Uses == 1   && o5.Uses == 1   && mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clo [...]
+	// result: @mergePoint(b,x0,x1,x2,x3,x4,x5,x6,x7) (MOVDloadidx <v.Type> [i-7] {s} p idx mem)
+	for {
+		o0 := v.Args[0]
+		if o0.Op != OpS390XOR {
+			break
+		}
+		o1 := o0.Args[0]
+		if o1.Op != OpS390XOR {
+			break
+		}
+		o2 := o1.Args[0]
+		if o2.Op != OpS390XOR {
+			break
+		}
+		o3 := o2.Args[0]
+		if o3.Op != OpS390XOR {
+			break
+		}
+		o4 := o3.Args[0]
+		if o4.Op != OpS390XOR {
+			break
+		}
+		o5 := o4.Args[0]
+		if o5.Op != OpS390XOR {
+			break
+		}
+		x0 := o5.Args[0]
+		if x0.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := o5.Args[1]
+		if s0.Op != OpS390XSLDconst {
+			break
+		}
+		if s0.AuxInt != 8 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if idx != x1.Args[1] {
+			break
+		}
+		if mem != x1.Args[2] {
+			break
+		}
+		s1 := o4.Args[1]
+		if s1.Op != OpS390XSLDconst {
+			break
+		}
+		if s1.AuxInt != 16 {
+			break
+		}
+		x2 := s1.Args[0]
+		if x2.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x2.AuxInt != i-2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if idx != x2.Args[1] {
+			break
+		}
+		if mem != x2.Args[2] {
+			break
+		}
+		s2 := o3.Args[1]
+		if s2.Op != OpS390XSLDconst {
+			break
+		}
+		if s2.AuxInt != 24 {
+			break
+		}
+		x3 := s2.Args[0]
+		if x3.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x3.AuxInt != i-3 {
+			break
+		}
+		if x3.Aux != s {
+			break
+		}
+		if p != x3.Args[0] {
+			break
+		}
+		if idx != x3.Args[1] {
+			break
+		}
+		if mem != x3.Args[2] {
+			break
+		}
+		s3 := o2.Args[1]
+		if s3.Op != OpS390XSLDconst {
+			break
+		}
+		if s3.AuxInt != 32 {
+			break
+		}
+		x4 := s3.Args[0]
+		if x4.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x4.AuxInt != i-4 {
+			break
+		}
+		if x4.Aux != s {
+			break
+		}
+		if p != x4.Args[0] {
+			break
+		}
+		if idx != x4.Args[1] {
+			break
+		}
+		if mem != x4.Args[2] {
+			break
+		}
+		s4 := o1.Args[1]
+		if s4.Op != OpS390XSLDconst {
+			break
+		}
+		if s4.AuxInt != 40 {
+			break
+		}
+		x5 := s4.Args[0]
+		if x5.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x5.AuxInt != i-5 {
+			break
+		}
+		if x5.Aux != s {
+			break
+		}
+		if p != x5.Args[0] {
+			break
+		}
+		if idx != x5.Args[1] {
+			break
+		}
+		if mem != x5.Args[2] {
+			break
+		}
+		s5 := o0.Args[1]
+		if s5.Op != OpS390XSLDconst {
+			break
+		}
+		if s5.AuxInt != 48 {
+			break
+		}
+		x6 := s5.Args[0]
+		if x6.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x6.AuxInt != i-6 {
+			break
+		}
+		if x6.Aux != s {
+			break
+		}
+		if p != x6.Args[0] {
+			break
+		}
+		if idx != x6.Args[1] {
+			break
+		}
+		if mem != x6.Args[2] {
+			break
+		}
+		s6 := v.Args[1]
+		if s6.Op != OpS390XSLDconst {
+			break
+		}
+		if s6.AuxInt != 56 {
+			break
+		}
+		x7 := s6.Args[0]
+		if x7.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x7.AuxInt != i-7 {
+			break
+		}
+		if x7.Aux != s {
+			break
+		}
+		if p != x7.Args[0] {
+			break
+		}
+		if idx != x7.Args[1] {
+			break
+		}
+		if mem != x7.Args[2] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && x3.Uses == 1 && x4.Uses == 1 && x5.Uses == 1 && x6.Uses == 1 && x7.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && s4.Uses == 1 && s5.Uses == 1 && s6.Uses == 1 && o0.Uses == 1 && o1.Uses == 1 && o2.Uses == 1 && o3.Uses == 1 && o4.Uses == 1 && o5.Uses == 1 && mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(x3) && clobber(x4) && clobber(x5) && clo [...]
+			break
+		}
+		b = mergePoint(b, x0, x1, x2, x3, x4, x5, x6, x7)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDloadidx, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i - 7
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XORW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORW x (MOVDconst [c]))
+	// cond:
+	// result: (ORWconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XORWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORW (MOVDconst [c]) x)
+	// cond:
+	// result: (ORWconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpS390XORWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORW x x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORW <t> x g:(MOVWload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ORWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XORWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ORW <t> g:(MOVWload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ORWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XORWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ORW <t> x g:(MOVWZload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ORWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XORWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ORW <t> g:(MOVWZload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (ORWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XORWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ORW                 x0:(MOVBZload [i]   {s} p mem)     s0:(SLWconst [8] x1:(MOVBZload [i+1] {s} p mem)))
+	// cond: p.Op != OpSB   && x0.Uses == 1   && x1.Uses == 1   && s0.Uses == 1   && mergePoint(b,x0,x1) != nil   && clobber(x0)   && clobber(x1)   && clobber(s0)
+	// result: @mergePoint(b,x0,x1) (MOVHZreg (MOVHBRload [i] {s} p mem))
+	for {
+		x0 := v.Args[0]
+		if x0.Op != OpS390XMOVBZload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := v.Args[1]
+		if s0.Op != OpS390XSLWconst {
+			break
+		}
+		if s0.AuxInt != 8 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZload {
+			break
+		}
+		if x1.AuxInt != i+1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		if !(p.Op != OpSB && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHBRload, config.fe.TypeUInt16())
+		v1.AuxInt = i
+		v1.Aux = s
+		v1.AddArg(p)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		return true
+	}
+	// match: (ORW o0:(ORW z0:(MOVHZreg x0:(MOVHBRload [i] {s} p mem))     s0:(SLWconst [16] x1:(MOVBZload [i+2] {s} p mem)))     s1:(SLWconst [24] x2:(MOVBZload [i+3] {s} p mem)))
+	// cond: p.Op != OpSB   && z0.Uses == 1   && x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && o0.Uses == 1   && mergePoint(b,x0,x1,x2) != nil   && clobber(z0)   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(s0)   && clobber(s1)   && clobber(o0)
+	// result: @mergePoint(b,x0,x1,x2) (MOVWBRload [i] {s} p mem)
+	for {
+		o0 := v.Args[0]
+		if o0.Op != OpS390XORW {
+			break
+		}
+		z0 := o0.Args[0]
+		if z0.Op != OpS390XMOVHZreg {
+			break
+		}
+		x0 := z0.Args[0]
+		if x0.Op != OpS390XMOVHBRload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := o0.Args[1]
+		if s0.Op != OpS390XSLWconst {
+			break
+		}
+		if s0.AuxInt != 16 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZload {
+			break
+		}
+		if x1.AuxInt != i+2 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		s1 := v.Args[1]
+		if s1.Op != OpS390XSLWconst {
+			break
+		}
+		if s1.AuxInt != 24 {
+			break
+		}
+		x2 := s1.Args[0]
+		if x2.Op != OpS390XMOVBZload {
+			break
+		}
+		if x2.AuxInt != i+3 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		if !(p.Op != OpSB && z0.Uses == 1 && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && o0.Uses == 1 && mergePoint(b, x0, x1, x2) != nil && clobber(z0) && clobber(x0) && clobber(x1) && clobber(x2) && clobber(s0) && clobber(s1) && clobber(o0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWBRload, config.fe.TypeUInt32())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (ORW                 x0:(MOVBZloadidx [i]   {s} p idx mem)     s0:(SLWconst [8] x1:(MOVBZloadidx [i+1] {s} p idx mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && s0.Uses == 1   && mergePoint(b,x0,x1) != nil   && clobber(x0)   && clobber(x1)   && clobber(s0)
+	// result: @mergePoint(b,x0,x1) (MOVHZreg (MOVHBRloadidx <v.Type> [i] {s} p idx mem))
+	for {
+		x0 := v.Args[0]
+		if x0.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := v.Args[1]
+		if s0.Op != OpS390XSLWconst {
+			break
+		}
+		if s0.AuxInt != 8 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x1.AuxInt != i+1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if idx != x1.Args[1] {
+			break
+		}
+		if mem != x1.Args[2] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHZreg, config.fe.TypeUInt64())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVHBRloadidx, v.Type)
+		v1.AuxInt = i
+		v1.Aux = s
+		v1.AddArg(p)
+		v1.AddArg(idx)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		return true
+	}
+	// match: (ORW o0:(ORW z0:(MOVHZreg x0:(MOVHBRloadidx [i] {s} p idx mem))     s0:(SLWconst [16] x1:(MOVBZloadidx [i+2] {s} p idx mem)))     s1:(SLWconst [24] x2:(MOVBZloadidx [i+3] {s} p idx mem)))
+	// cond: z0.Uses == 1   && x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && o0.Uses == 1   && mergePoint(b,x0,x1,x2) != nil   && clobber(z0)   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(s0)   && clobber(s1)   && clobber(o0)
+	// result: @mergePoint(b,x0,x1,x2) (MOVWZreg (MOVWBRloadidx <v.Type> [i] {s} p idx mem))
+	for {
+		o0 := v.Args[0]
+		if o0.Op != OpS390XORW {
+			break
+		}
+		z0 := o0.Args[0]
+		if z0.Op != OpS390XMOVHZreg {
+			break
+		}
+		x0 := z0.Args[0]
+		if x0.Op != OpS390XMOVHBRloadidx {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := o0.Args[1]
+		if s0.Op != OpS390XSLWconst {
+			break
+		}
+		if s0.AuxInt != 16 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x1.AuxInt != i+2 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if idx != x1.Args[1] {
+			break
+		}
+		if mem != x1.Args[2] {
+			break
+		}
+		s1 := v.Args[1]
+		if s1.Op != OpS390XSLWconst {
+			break
+		}
+		if s1.AuxInt != 24 {
+			break
+		}
+		x2 := s1.Args[0]
+		if x2.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x2.AuxInt != i+3 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if idx != x2.Args[1] {
+			break
+		}
+		if mem != x2.Args[2] {
+			break
+		}
+		if !(z0.Uses == 1 && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && o0.Uses == 1 && mergePoint(b, x0, x1, x2) != nil && clobber(z0) && clobber(x0) && clobber(x1) && clobber(x2) && clobber(s0) && clobber(s1) && clobber(o0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWZreg, config.fe.TypeUInt64())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XMOVWBRloadidx, v.Type)
+		v1.AuxInt = i
+		v1.Aux = s
+		v1.AddArg(p)
+		v1.AddArg(idx)
+		v1.AddArg(mem)
+		v0.AddArg(v1)
+		return true
+	}
+	// match: (ORW                  x0:(MOVBZload [i]   {s} p mem)     s0:(SLWconst [8] x1:(MOVBZload [i-1] {s} p mem)))
+	// cond: p.Op != OpSB   && x0.Uses == 1   && x1.Uses == 1   && s0.Uses == 1   && mergePoint(b,x0,x1) != nil   && clobber(x0)   && clobber(x1)   && clobber(s0)
+	// result: @mergePoint(b,x0,x1) (MOVHZload [i-1] {s} p mem)
+	for {
+		x0 := v.Args[0]
+		if x0.Op != OpS390XMOVBZload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := v.Args[1]
+		if s0.Op != OpS390XSLWconst {
+			break
+		}
+		if s0.AuxInt != 8 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZload {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		if !(p.Op != OpSB && x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHZload, config.fe.TypeUInt16())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i - 1
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (ORW o0:(ORW x0:(MOVHZload [i] {s} p mem)     s0:(SLWconst [16] x1:(MOVBZload [i-1] {s} p mem)))     s1:(SLWconst [24] x2:(MOVBZload [i-2] {s} p mem)))
+	// cond: p.Op != OpSB   && x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && o0.Uses == 1   && mergePoint(b,x0,x1,x2) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(s0)   && clobber(s1)   && clobber(o0)
+	// result: @mergePoint(b,x0,x1,x2) (MOVWZload [i-2] {s} p mem)
+	for {
+		o0 := v.Args[0]
+		if o0.Op != OpS390XORW {
+			break
+		}
+		x0 := o0.Args[0]
+		if x0.Op != OpS390XMOVHZload {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		mem := x0.Args[1]
+		s0 := o0.Args[1]
+		if s0.Op != OpS390XSLWconst {
+			break
+		}
+		if s0.AuxInt != 16 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZload {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if mem != x1.Args[1] {
+			break
+		}
+		s1 := v.Args[1]
+		if s1.Op != OpS390XSLWconst {
+			break
+		}
+		if s1.AuxInt != 24 {
+			break
+		}
+		x2 := s1.Args[0]
+		if x2.Op != OpS390XMOVBZload {
+			break
+		}
+		if x2.AuxInt != i-2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if mem != x2.Args[1] {
+			break
+		}
+		if !(p.Op != OpSB && x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && o0.Uses == 1 && mergePoint(b, x0, x1, x2) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(s0) && clobber(s1) && clobber(o0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWZload, config.fe.TypeUInt32())
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i - 2
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (ORW                 x0:(MOVBZloadidx [i]   {s} p idx mem)     s0:(SLWconst [8] x1:(MOVBZloadidx [i-1] {s} p idx mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && s0.Uses == 1   && mergePoint(b,x0,x1) != nil   && clobber(x0)   && clobber(x1)   && clobber(s0)
+	// result: @mergePoint(b,x0,x1) (MOVHZloadidx <v.Type> [i-1] {s} p idx mem)
+	for {
+		x0 := v.Args[0]
+		if x0.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := v.Args[1]
+		if s0.Op != OpS390XSLWconst {
+			break
+		}
+		if s0.AuxInt != 8 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if idx != x1.Args[1] {
+			break
+		}
+		if mem != x1.Args[2] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && s0.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0) && clobber(x1) && clobber(s0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHZloadidx, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i - 1
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	// match: (ORW o0:(ORW x0:(MOVHZloadidx [i] {s} p idx mem)     s0:(SLWconst [16] x1:(MOVBZloadidx [i-1] {s} p idx mem)))     s1:(SLWconst [24] x2:(MOVBZloadidx [i-2] {s} p idx mem)))
+	// cond: x0.Uses == 1   && x1.Uses == 1   && x2.Uses == 1   && s0.Uses == 1   && s1.Uses == 1   && o0.Uses == 1   && mergePoint(b,x0,x1,x2) != nil   && clobber(x0)   && clobber(x1)   && clobber(x2)   && clobber(s0)   && clobber(s1)   && clobber(o0)
+	// result: @mergePoint(b,x0,x1,x2) (MOVWZloadidx <v.Type> [i-2] {s} p idx mem)
+	for {
+		o0 := v.Args[0]
+		if o0.Op != OpS390XORW {
+			break
+		}
+		x0 := o0.Args[0]
+		if x0.Op != OpS390XMOVHZloadidx {
+			break
+		}
+		i := x0.AuxInt
+		s := x0.Aux
+		p := x0.Args[0]
+		idx := x0.Args[1]
+		mem := x0.Args[2]
+		s0 := o0.Args[1]
+		if s0.Op != OpS390XSLWconst {
+			break
+		}
+		if s0.AuxInt != 16 {
+			break
+		}
+		x1 := s0.Args[0]
+		if x1.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x1.AuxInt != i-1 {
+			break
+		}
+		if x1.Aux != s {
+			break
+		}
+		if p != x1.Args[0] {
+			break
+		}
+		if idx != x1.Args[1] {
+			break
+		}
+		if mem != x1.Args[2] {
+			break
+		}
+		s1 := v.Args[1]
+		if s1.Op != OpS390XSLWconst {
+			break
+		}
+		if s1.AuxInt != 24 {
+			break
+		}
+		x2 := s1.Args[0]
+		if x2.Op != OpS390XMOVBZloadidx {
+			break
+		}
+		if x2.AuxInt != i-2 {
+			break
+		}
+		if x2.Aux != s {
+			break
+		}
+		if p != x2.Args[0] {
+			break
+		}
+		if idx != x2.Args[1] {
+			break
+		}
+		if mem != x2.Args[2] {
+			break
+		}
+		if !(x0.Uses == 1 && x1.Uses == 1 && x2.Uses == 1 && s0.Uses == 1 && s1.Uses == 1 && o0.Uses == 1 && mergePoint(b, x0, x1, x2) != nil && clobber(x0) && clobber(x1) && clobber(x2) && clobber(s0) && clobber(s1) && clobber(o0)) {
+			break
+		}
+		b = mergePoint(b, x0, x1, x2)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWZloadidx, v.Type)
+		v.reset(OpCopy)
+		v.AddArg(v0)
+		v0.AuxInt = i - 2
+		v0.Aux = s
+		v0.AddArg(p)
+		v0.AddArg(idx)
+		v0.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XORWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORWconst [c] x)
+	// cond: int32(c)==0
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORWconst [c] _)
+	// cond: int32(c)==-1
+	// result: (MOVDconst [-1])
+	for {
+		c := v.AuxInt
+		if !(int32(c) == -1) {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (ORWconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c|d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c | d
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ORconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ORconst [-1] _)
+	// cond:
+	// result: (MOVDconst [-1])
+	for {
+		if v.AuxInt != -1 {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (ORconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c|d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c | d
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSLD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SLD x (MOVDconst [c]))
+	// cond:
+	// result: (SLDconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XSLDconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SLD x (ANDconst [63] y))
+	// cond:
+	// result: (SLD x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XANDconst {
+			break
+		}
+		if v_1.AuxInt != 63 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpS390XSLD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSLW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SLW x (MOVDconst [c]))
+	// cond:
+	// result: (SLWconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XSLWconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SLW x (ANDWconst [63] y))
+	// cond:
+	// result: (SLW x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XANDWconst {
+			break
+		}
+		if v_1.AuxInt != 63 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpS390XSLW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSRAD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRAD x (MOVDconst [c]))
+	// cond:
+	// result: (SRADconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XSRADconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRAD x (ANDconst [63] y))
+	// cond:
+	// result: (SRAD x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XANDconst {
+			break
+		}
+		if v_1.AuxInt != 63 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpS390XSRAD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSRADconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRADconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [d>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = d >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSRAW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRAW x (MOVDconst [c]))
+	// cond:
+	// result: (SRAWconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XSRAWconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRAW x (ANDWconst [63] y))
+	// cond:
+	// result: (SRAW x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XANDWconst {
+			break
+		}
+		if v_1.AuxInt != 63 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpS390XSRAW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSRAWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRAWconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [d>>uint64(c)])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = d >> uint64(c)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSRD(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRD x (MOVDconst [c]))
+	// cond:
+	// result: (SRDconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XSRDconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRD x (ANDconst [63] y))
+	// cond:
+	// result: (SRD x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XANDconst {
+			break
+		}
+		if v_1.AuxInt != 63 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpS390XSRD)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSRW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SRW x (MOVDconst [c]))
+	// cond:
+	// result: (SRWconst [c&63] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XSRWconst)
+		v.AuxInt = c & 63
+		v.AddArg(x)
+		return true
+	}
+	// match: (SRW x (ANDWconst [63] y))
+	// cond:
+	// result: (SRW x y)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XANDWconst {
+			break
+		}
+		if v_1.AuxInt != 63 {
+			break
+		}
+		y := v_1.Args[0]
+		v.reset(OpS390XSRW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSTM2(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (STM2 [i] {s} p w2 w3 x:(STM2 [i-8] {s} p w0 w1 mem))
+	// cond: x.Uses == 1   && is20Bit(i-8)   && clobber(x)
+	// result: (STM4 [i-8] {s} p w0 w1 w2 w3 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w2 := v.Args[1]
+		w3 := v.Args[2]
+		x := v.Args[3]
+		if x.Op != OpS390XSTM2 {
+			break
+		}
+		if x.AuxInt != i-8 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		w1 := x.Args[2]
+		mem := x.Args[3]
+		if !(x.Uses == 1 && is20Bit(i-8) && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XSTM4)
+		v.AuxInt = i - 8
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(w1)
+		v.AddArg(w2)
+		v.AddArg(w3)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (STM2 [i] {s} p (SRDconst [32] x) x mem)
+	// cond:
+	// result: (MOVDstore [i] {s} p x mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XSRDconst {
+			break
+		}
+		if v_1.AuxInt != 32 {
+			break
+		}
+		x := v_1.Args[0]
+		if x != v.Args[2] {
+			break
+		}
+		mem := v.Args[3]
+		v.reset(OpS390XMOVDstore)
+		v.AuxInt = i
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(x)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSTMG2(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (STMG2 [i] {s} p w2 w3 x:(STMG2 [i-16] {s} p w0 w1 mem))
+	// cond: x.Uses == 1   && is20Bit(i-16)   && clobber(x)
+	// result: (STMG4 [i-16] {s} p w0 w1 w2 w3 mem)
+	for {
+		i := v.AuxInt
+		s := v.Aux
+		p := v.Args[0]
+		w2 := v.Args[1]
+		w3 := v.Args[2]
+		x := v.Args[3]
+		if x.Op != OpS390XSTMG2 {
+			break
+		}
+		if x.AuxInt != i-16 {
+			break
+		}
+		if x.Aux != s {
+			break
+		}
+		if p != x.Args[0] {
+			break
+		}
+		w0 := x.Args[1]
+		w1 := x.Args[2]
+		mem := x.Args[3]
+		if !(x.Uses == 1 && is20Bit(i-16) && clobber(x)) {
+			break
+		}
+		v.reset(OpS390XSTMG4)
+		v.AuxInt = i - 16
+		v.Aux = s
+		v.AddArg(p)
+		v.AddArg(w0)
+		v.AddArg(w1)
+		v.AddArg(w2)
+		v.AddArg(w3)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSUB(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUB x (MOVDconst [c]))
+	// cond: is32Bit(c)
+	// result: (SUBconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XSUBconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUB (MOVDconst [c]) x)
+	// cond: is32Bit(c)
+	// result: (NEG (SUBconst <v.Type> x [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(is32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XNEG)
+		v0 := b.NewValue0(v.Line, OpS390XSUBconst, v.Type)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUB x x)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SUB <t> x g:(MOVDload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (SUBload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XSUBload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSUBEWcarrymask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBEWcarrymask (FlagEQ))
+	// cond:
+	// result: (MOVDconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XFlagEQ {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (SUBEWcarrymask (FlagLT))
+	// cond:
+	// result: (MOVDconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XFlagLT {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (SUBEWcarrymask (FlagGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XFlagGT {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSUBEcarrymask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBEcarrymask (FlagEQ))
+	// cond:
+	// result: (MOVDconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XFlagEQ {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (SUBEcarrymask (FlagLT))
+	// cond:
+	// result: (MOVDconst [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XFlagLT {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (SUBEcarrymask (FlagGT))
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XFlagGT {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSUBW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBW x (MOVDconst [c]))
+	// cond:
+	// result: (SUBWconst x [c])
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XSUBWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBW (MOVDconst [c]) x)
+	// cond:
+	// result: (NEGW (SUBWconst <v.Type> x [c]))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpS390XNEGW)
+		v0 := b.NewValue0(v.Line, OpS390XSUBWconst, v.Type)
+		v0.AuxInt = c
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (SUBW x x)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (SUBW <t> x g:(MOVWload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (SUBWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XSUBWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (SUBW <t> x g:(MOVWZload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (SUBWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XSUBWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XSUBWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBWconst [c] x)
+	// cond: int32(c) == 0
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBWconst [c] x)
+	// cond:
+	// result: (ADDWconst [int64(int32(-c))] x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		v.reset(OpS390XADDWconst)
+		v.AuxInt = int64(int32(-c))
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpS390XSUBconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SUBconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBconst [c] x)
+	// cond: c != -(1<<31)
+	// result: (ADDconst [-c] x)
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(c != -(1 << 31)) {
+			break
+		}
+		v.reset(OpS390XADDconst)
+		v.AuxInt = -c
+		v.AddArg(x)
+		return true
+	}
+	// match: (SUBconst (MOVDconst [d]) [c])
+	// cond:
+	// result: (MOVDconst [d-c])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = d - c
+		return true
+	}
+	// match: (SUBconst (SUBconst x [d]) [c])
+	// cond: is32Bit(-c-d)
+	// result: (ADDconst [-c-d] x)
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XSUBconst {
+			break
+		}
+		d := v_0.AuxInt
+		x := v_0.Args[0]
+		if !(is32Bit(-c - d)) {
+			break
+		}
+		v.reset(OpS390XADDconst)
+		v.AuxInt = -c - d
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XXOR(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XOR x (MOVDconst [c]))
+	// cond: isU32Bit(c)
+	// result: (XORconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		if !(isU32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XXORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR (MOVDconst [c]) x)
+	// cond: isU32Bit(c)
+	// result: (XORconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		if !(isU32Bit(c)) {
+			break
+		}
+		v.reset(OpS390XXORconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XOR (MOVDconst [c]) (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c^d])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_1.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	// match: (XOR x x)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (XOR <t> x g:(MOVDload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (XORload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XXORload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (XOR <t> g:(MOVDload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (XORload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVDload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XXORload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XXORW(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORW x (MOVDconst [c]))
+	// cond:
+	// result: (XORWconst [c] x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_1.AuxInt
+		v.reset(OpS390XXORWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORW (MOVDconst [c]) x)
+	// cond:
+	// result: (XORWconst [c] x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		c := v_0.AuxInt
+		x := v.Args[1]
+		v.reset(OpS390XXORWconst)
+		v.AuxInt = c
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORW x x)
+	// cond:
+	// result: (MOVDconst [0])
+	for {
+		x := v.Args[0]
+		if x != v.Args[1] {
+			break
+		}
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (XORW <t> x g:(MOVWload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (XORWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XXORWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (XORW <t> g:(MOVWload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (XORWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVWload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XXORWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (XORW <t> x g:(MOVWZload [off] {sym} ptr mem))
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (XORWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		g := v.Args[1]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XXORWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (XORW <t> g:(MOVWZload [off] {sym} ptr mem) x)
+	// cond: g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)
+	// result: (XORWload <t> [off] {sym} x ptr mem)
+	for {
+		t := v.Type
+		g := v.Args[0]
+		if g.Op != OpS390XMOVWZload {
+			break
+		}
+		off := g.AuxInt
+		sym := g.Aux
+		ptr := g.Args[0]
+		mem := g.Args[1]
+		x := v.Args[1]
+		if !(g.Uses == 1 && ptr.Op != OpSB && is20Bit(off) && canMergeLoad(v, g) && clobber(g)) {
+			break
+		}
+		v.reset(OpS390XXORWload)
+		v.Type = t
+		v.AuxInt = off
+		v.Aux = sym
+		v.AddArg(x)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XXORWconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORWconst [c] x)
+	// cond: int32(c)==0
+	// result: x
+	for {
+		c := v.AuxInt
+		x := v.Args[0]
+		if !(int32(c) == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORWconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c^d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpS390XXORconst(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (XORconst [0] x)
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (XORconst [c] (MOVDconst [d]))
+	// cond:
+	// result: (MOVDconst [c^d])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XMOVDconst {
+			break
+		}
+		d := v_0.AuxInt
+		v.reset(OpS390XMOVDconst)
+		v.AuxInt = c ^ d
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpSelect0(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Select0 <t> (AddTupleFirst32 tuple val))
+	// cond:
+	// result: (ADDW val (Select0 <t> tuple))
+	for {
+		t := v.Type
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XAddTupleFirst32 {
+			break
+		}
+		tuple := v_0.Args[0]
+		val := v_0.Args[1]
+		v.reset(OpS390XADDW)
+		v.AddArg(val)
+		v0 := b.NewValue0(v.Line, OpSelect0, t)
+		v0.AddArg(tuple)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Select0 <t> (AddTupleFirst64 tuple val))
+	// cond:
+	// result: (ADD val (Select0 <t> tuple))
+	for {
+		t := v.Type
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XAddTupleFirst64 {
+			break
+		}
+		tuple := v_0.Args[0]
+		val := v_0.Args[1]
+		v.reset(OpS390XADD)
+		v.AddArg(val)
+		v0 := b.NewValue0(v.Line, OpSelect0, t)
+		v0.AddArg(tuple)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpSelect1(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Select1     (AddTupleFirst32 tuple _  ))
+	// cond:
+	// result: (Select1 tuple)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XAddTupleFirst32 {
+			break
+		}
+		tuple := v_0.Args[0]
+		v.reset(OpSelect1)
+		v.AddArg(tuple)
+		return true
+	}
+	// match: (Select1     (AddTupleFirst64 tuple _  ))
+	// cond:
+	// result: (Select1 tuple)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpS390XAddTupleFirst64 {
+			break
+		}
+		tuple := v_0.Args[0]
+		v.reset(OpSelect1)
+		v.AddArg(tuple)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpSignExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to32 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpSignExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to64 x)
+	// cond:
+	// result: (MOVHreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVHreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpSignExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt32to64 x)
+	// cond:
+	// result: (MOVWreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVWreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpSignExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to16  x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpSignExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to32  x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpSignExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to64  x)
+	// cond:
+	// result: (MOVBreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVBreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpSlicemask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Slicemask <t> x)
+	// cond:
+	// result: (XOR (MOVDconst [-1]) (SRADconst <t> (SUBconst <t> x [1]) [63]))
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpS390XXOR)
+		v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+		v0.AuxInt = -1
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpS390XSRADconst, t)
+		v1.AuxInt = 63
+		v2 := b.NewValue0(v.Line, OpS390XSUBconst, t)
+		v2.AuxInt = 1
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValueS390X_OpSqrt(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sqrt x)
+	// cond:
+	// result: (FSQRT x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XFSQRT)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpStaticCall(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (StaticCall [argwid] {target} mem)
+	// cond:
+	// result: (CALLstatic [argwid] {target} mem)
+	for {
+		argwid := v.AuxInt
+		target := v.Aux
+		mem := v.Args[0]
+		v.reset(OpS390XCALLstatic)
+		v.AuxInt = argwid
+		v.Aux = target
+		v.AddArg(mem)
+		return true
+	}
+}
+func rewriteValueS390X_OpStore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Store [8] ptr val mem)
+	// cond: is64BitFloat(val.Type)
+	// result: (FMOVDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is64BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpS390XFMOVDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond: is32BitFloat(val.Type)
+	// result: (FMOVSstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		if !(is32BitFloat(val.Type)) {
+			break
+		}
+		v.reset(OpS390XFMOVSstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [8] ptr val mem)
+	// cond:
+	// result: (MOVDstore ptr val mem)
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVDstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [4] ptr val mem)
+	// cond:
+	// result: (MOVWstore ptr val mem)
+	for {
+		if v.AuxInt != 4 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVWstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [2] ptr val mem)
+	// cond:
+	// result: (MOVHstore ptr val mem)
+	for {
+		if v.AuxInt != 2 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVHstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [1] ptr val mem)
+	// cond:
+	// result: (MOVBstore ptr val mem)
+	for {
+		if v.AuxInt != 1 {
+			break
+		}
+		ptr := v.Args[0]
+		val := v.Args[1]
+		mem := v.Args[2]
+		v.reset(OpS390XMOVBstore)
+		v.AddArg(ptr)
+		v.AddArg(val)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpSub16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub16  x y)
+	// cond:
+	// result: (SUBW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSUBW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpSub32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32  x y)
+	// cond:
+	// result: (SUBW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSUBW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpSub32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub32F x y)
+	// cond:
+	// result: (FSUBS x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XFSUBS)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpSub64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64  x y)
+	// cond:
+	// result: (SUB  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpSub64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64F x y)
+	// cond:
+	// result: (FSUB x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XFSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpSub8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub8   x y)
+	// cond:
+	// result: (SUBW  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSUBW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpSubPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SubPtr x y)
+	// cond:
+	// result: (SUB  x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XSUB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpTrunc16to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc16to8  x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpTrunc32to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to16 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpTrunc32to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc32to8  x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpTrunc64to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to16 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpTrunc64to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to32 x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpTrunc64to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to8  x)
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpXor16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor16 x y)
+	// cond:
+	// result: (XORW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XXORW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpXor32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor32 x y)
+	// cond:
+	// result: (XORW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XXORW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpXor64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor64 x y)
+	// cond:
+	// result: (XOR x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XXOR)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpXor8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor8  x y)
+	// cond:
+	// result: (XORW x y)
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpS390XXORW)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+}
+func rewriteValueS390X_OpZero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zero [s] _ mem)
+	// cond: SizeAndAlign(s).Size() == 0
+	// result: mem
+	for {
+		s := v.AuxInt
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 0) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 1
+	// result: (MOVBstoreconst [0] destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 1) {
+			break
+		}
+		v.reset(OpS390XMOVBstoreconst)
+		v.AuxInt = 0
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 2
+	// result: (MOVHstoreconst [0] destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 2) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreconst)
+		v.AuxInt = 0
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 4
+	// result: (MOVWstoreconst [0] destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 4) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreconst)
+		v.AuxInt = 0
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 8
+	// result: (MOVDstoreconst [0] destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 8) {
+			break
+		}
+		v.reset(OpS390XMOVDstoreconst)
+		v.AuxInt = 0
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 3
+	// result: (MOVBstoreconst [makeValAndOff(0,2)] destptr 		(MOVHstoreconst [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 3) {
+			break
+		}
+		v.reset(OpS390XMOVBstoreconst)
+		v.AuxInt = makeValAndOff(0, 2)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpS390XMOVHstoreconst, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 5
+	// result: (MOVBstoreconst [makeValAndOff(0,4)] destptr 		(MOVWstoreconst [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 5) {
+			break
+		}
+		v.reset(OpS390XMOVBstoreconst)
+		v.AuxInt = makeValAndOff(0, 4)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWstoreconst, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 6
+	// result: (MOVHstoreconst [makeValAndOff(0,4)] destptr 		(MOVWstoreconst [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 6) {
+			break
+		}
+		v.reset(OpS390XMOVHstoreconst)
+		v.AuxInt = makeValAndOff(0, 4)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWstoreconst, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() == 7
+	// result: (MOVWstoreconst [makeValAndOff(0,3)] destptr 		(MOVWstoreconst [0] destptr mem))
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() == 7) {
+			break
+		}
+		v.reset(OpS390XMOVWstoreconst)
+		v.AuxInt = makeValAndOff(0, 3)
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpS390XMOVWstoreconst, TypeMem)
+		v0.AuxInt = 0
+		v0.AddArg(destptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() > 0 && SizeAndAlign(s).Size() <= 1024
+	// result: (CLEAR [makeValAndOff(SizeAndAlign(s).Size(), 0)] destptr mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() > 0 && SizeAndAlign(s).Size() <= 1024) {
+			break
+		}
+		v.reset(OpS390XCLEAR)
+		v.AuxInt = makeValAndOff(SizeAndAlign(s).Size(), 0)
+		v.AddArg(destptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Zero [s] destptr mem)
+	// cond: SizeAndAlign(s).Size() > 1024
+	// result: (LoweredZero [SizeAndAlign(s).Size()%256] destptr (ADDconst <destptr.Type> destptr [(SizeAndAlign(s).Size()/256)*256]) mem)
+	for {
+		s := v.AuxInt
+		destptr := v.Args[0]
+		mem := v.Args[1]
+		if !(SizeAndAlign(s).Size() > 1024) {
+			break
+		}
+		v.reset(OpS390XLoweredZero)
+		v.AuxInt = SizeAndAlign(s).Size() % 256
+		v.AddArg(destptr)
+		v0 := b.NewValue0(v.Line, OpS390XADDconst, destptr.Type)
+		v0.AuxInt = (SizeAndAlign(s).Size() / 256) * 256
+		v0.AddArg(destptr)
+		v.AddArg(v0)
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValueS390X_OpZeroExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to32 x)
+	// cond:
+	// result: (MOVHZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVHZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpZeroExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to64 x)
+	// cond:
+	// result: (MOVHZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVHZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpZeroExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt32to64 x)
+	// cond:
+	// result: (MOVWZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVWZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpZeroExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to16  x)
+	// cond:
+	// result: (MOVBZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVBZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpZeroExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to32  x)
+	// cond:
+	// result: (MOVBZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVBZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValueS390X_OpZeroExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to64  x)
+	// cond:
+	// result: (MOVBZreg x)
+	for {
+		x := v.Args[0]
+		v.reset(OpS390XMOVBZreg)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteBlockS390X(b *Block, config *Config) bool {
+	switch b.Kind {
+	case BlockS390XEQ:
+		// match: (EQ (InvertFlags cmp) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XEQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (EQ (FlagLT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (EQ (FlagGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockS390XGE:
+		// match: (GE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (LE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XLE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GE (FlagLT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GE (FlagGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockS390XGT:
+		// match: (GT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (LT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XLT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (GT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagLT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (GT (FlagGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockIf:
+		// match: (If (MOVDLT (MOVDconst [0]) (MOVDconst [1]) cmp) yes no)
+		// cond:
+		// result: (LT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XMOVDLT {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0.AuxInt != 0 {
+				break
+			}
+			v_1 := v.Args[1]
+			if v_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_1.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XLT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (MOVDLE (MOVDconst [0]) (MOVDconst [1]) cmp) yes no)
+		// cond:
+		// result: (LE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XMOVDLE {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0.AuxInt != 0 {
+				break
+			}
+			v_1 := v.Args[1]
+			if v_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_1.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XLE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (MOVDGT (MOVDconst [0]) (MOVDconst [1]) cmp) yes no)
+		// cond:
+		// result: (GT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XMOVDGT {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0.AuxInt != 0 {
+				break
+			}
+			v_1 := v.Args[1]
+			if v_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_1.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XGT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (MOVDGE (MOVDconst [0]) (MOVDconst [1]) cmp) yes no)
+		// cond:
+		// result: (GE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XMOVDGE {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0.AuxInt != 0 {
+				break
+			}
+			v_1 := v.Args[1]
+			if v_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_1.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) cmp) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XMOVDEQ {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0.AuxInt != 0 {
+				break
+			}
+			v_1 := v.Args[1]
+			if v_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_1.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XEQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (MOVDNE (MOVDconst [0]) (MOVDconst [1]) cmp) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XMOVDNE {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0.AuxInt != 0 {
+				break
+			}
+			v_1 := v.Args[1]
+			if v_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_1.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XNE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) cmp) yes no)
+		// cond:
+		// result: (GTF cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XMOVDGTnoinv {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0.AuxInt != 0 {
+				break
+			}
+			v_1 := v.Args[1]
+			if v_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_1.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XGTF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) cmp) yes no)
+		// cond:
+		// result: (GEF cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XMOVDGEnoinv {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0.AuxInt != 0 {
+				break
+			}
+			v_1 := v.Args[1]
+			if v_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_1.AuxInt != 1 {
+				break
+			}
+			cmp := v.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XGEF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (If cond yes no)
+		// cond:
+		// result: (NE (CMPWconst [0] (MOVBZreg cond)) yes no)
+		for {
+			v := b.Control
+			_ = v
+			cond := b.Control
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XNE
+			v0 := b.NewValue0(v.Line, OpS390XCMPWconst, TypeFlags)
+			v0.AuxInt = 0
+			v1 := b.NewValue0(v.Line, OpS390XMOVBZreg, config.fe.TypeUInt64())
+			v1.AddArg(cond)
+			v0.AddArg(v1)
+			b.SetControl(v0)
+			_ = yes
+			_ = no
+			return true
+		}
+	case BlockS390XLE:
+		// match: (LE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (GE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagLT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LE (FlagGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockS390XLT:
+		// match: (LT (InvertFlags cmp) yes no)
+		// cond:
+		// result: (GT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XGT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LT (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (LT (FlagLT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (LT (FlagGT) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+	case BlockS390XNE:
+		// match: (NE (CMPWconst [0] (MOVDLT (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no)
+		// cond:
+		// result: (LT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XCMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDLT {
+				break
+			}
+			v_0_0 := v_0.Args[0]
+			if v_0_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_0.AuxInt != 0 {
+				break
+			}
+			v_0_1 := v_0.Args[1]
+			if v_0_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_1.AuxInt != 1 {
+				break
+			}
+			cmp := v_0.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XLT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (MOVDLE (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no)
+		// cond:
+		// result: (LE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XCMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDLE {
+				break
+			}
+			v_0_0 := v_0.Args[0]
+			if v_0_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_0.AuxInt != 0 {
+				break
+			}
+			v_0_1 := v_0.Args[1]
+			if v_0_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_1.AuxInt != 1 {
+				break
+			}
+			cmp := v_0.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XLE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (MOVDGT (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no)
+		// cond:
+		// result: (GT cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XCMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDGT {
+				break
+			}
+			v_0_0 := v_0.Args[0]
+			if v_0_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_0.AuxInt != 0 {
+				break
+			}
+			v_0_1 := v_0.Args[1]
+			if v_0_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_1.AuxInt != 1 {
+				break
+			}
+			cmp := v_0.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XGT
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (MOVDGE (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no)
+		// cond:
+		// result: (GE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XCMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDGE {
+				break
+			}
+			v_0_0 := v_0.Args[0]
+			if v_0_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_0.AuxInt != 0 {
+				break
+			}
+			v_0_1 := v_0.Args[1]
+			if v_0_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_1.AuxInt != 1 {
+				break
+			}
+			cmp := v_0.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XGE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (MOVDEQ (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no)
+		// cond:
+		// result: (EQ cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XCMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDEQ {
+				break
+			}
+			v_0_0 := v_0.Args[0]
+			if v_0_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_0.AuxInt != 0 {
+				break
+			}
+			v_0_1 := v_0.Args[1]
+			if v_0_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_1.AuxInt != 1 {
+				break
+			}
+			cmp := v_0.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XEQ
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (MOVDNE (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XCMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDNE {
+				break
+			}
+			v_0_0 := v_0.Args[0]
+			if v_0_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_0.AuxInt != 0 {
+				break
+			}
+			v_0_1 := v_0.Args[1]
+			if v_0_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_1.AuxInt != 1 {
+				break
+			}
+			cmp := v_0.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XNE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (MOVDGTnoinv (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no)
+		// cond:
+		// result: (GTF cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XCMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDGTnoinv {
+				break
+			}
+			v_0_0 := v_0.Args[0]
+			if v_0_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_0.AuxInt != 0 {
+				break
+			}
+			v_0_1 := v_0.Args[1]
+			if v_0_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_1.AuxInt != 1 {
+				break
+			}
+			cmp := v_0.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XGTF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (CMPWconst [0] (MOVDGEnoinv (MOVDconst [0]) (MOVDconst [1]) cmp)) yes no)
+		// cond:
+		// result: (GEF cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XCMPWconst {
+				break
+			}
+			if v.AuxInt != 0 {
+				break
+			}
+			v_0 := v.Args[0]
+			if v_0.Op != OpS390XMOVDGEnoinv {
+				break
+			}
+			v_0_0 := v_0.Args[0]
+			if v_0_0.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_0.AuxInt != 0 {
+				break
+			}
+			v_0_1 := v_0.Args[1]
+			if v_0_1.Op != OpS390XMOVDconst {
+				break
+			}
+			if v_0_1.AuxInt != 1 {
+				break
+			}
+			cmp := v_0.Args[2]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XGEF
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (InvertFlags cmp) yes no)
+		// cond:
+		// result: (NE cmp yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XInvertFlags {
+				break
+			}
+			cmp := v.Args[0]
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockS390XNE
+			b.SetControl(cmp)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagEQ) yes no)
+		// cond:
+		// result: (First nil no yes)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagEQ {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			b.swapSuccessors()
+			_ = no
+			_ = yes
+			return true
+		}
+		// match: (NE (FlagLT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagLT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+		// match: (NE (FlagGT) yes no)
+		// cond:
+		// result: (First nil yes no)
+		for {
+			v := b.Control
+			if v.Op != OpS390XFlagGT {
+				break
+			}
+			yes := b.Succs[0]
+			no := b.Succs[1]
+			b.Kind = BlockFirst
+			b.SetControl(nil)
+			_ = yes
+			_ = no
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/cmd/compile/internal/ssa/rewrite_test.go b/src/cmd/compile/internal/ssa/rewrite_test.go
index b786df8..7bd32ff 100644
--- a/src/cmd/compile/internal/ssa/rewrite_test.go
+++ b/src/cmd/compile/internal/ssa/rewrite_test.go
@@ -92,6 +92,9 @@ func TestLog2(t *testing.T) {
 		{1, 0},
 		{2, 1},
 		{4, 2},
+		{7, 2},
+		{8, 3},
+		{9, 3},
 		{1024, 10}}
 
 	for _, tc := range log2Tests {
diff --git a/src/cmd/compile/internal/ssa/rewritedec.go b/src/cmd/compile/internal/ssa/rewritedec.go
index c32d54e..fd52751 100644
--- a/src/cmd/compile/internal/ssa/rewritedec.go
+++ b/src/cmd/compile/internal/ssa/rewritedec.go
@@ -500,7 +500,7 @@ func rewriteValuedec_OpStringPtr(v *Value, config *Config) bool {
 	}
 	return false
 }
-func rewriteBlockdec(b *Block) bool {
+func rewriteBlockdec(b *Block, config *Config) bool {
 	switch b.Kind {
 	}
 	return false
diff --git a/src/cmd/compile/internal/ssa/rewritedec64.go b/src/cmd/compile/internal/ssa/rewritedec64.go
new file mode 100644
index 0000000..deca007
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/rewritedec64.go
@@ -0,0 +1,2720 @@
+// autogenerated from gen/dec64.rules: do not edit!
+// generated with: cd gen; go run *.go
+
+package ssa
+
+import "math"
+
+var _ = math.MinInt8 // in case not otherwise used
+func rewriteValuedec64(v *Value, config *Config) bool {
+	switch v.Op {
+	case OpAdd64:
+		return rewriteValuedec64_OpAdd64(v, config)
+	case OpAnd64:
+		return rewriteValuedec64_OpAnd64(v, config)
+	case OpArg:
+		return rewriteValuedec64_OpArg(v, config)
+	case OpBswap64:
+		return rewriteValuedec64_OpBswap64(v, config)
+	case OpCom64:
+		return rewriteValuedec64_OpCom64(v, config)
+	case OpConst64:
+		return rewriteValuedec64_OpConst64(v, config)
+	case OpCtz64:
+		return rewriteValuedec64_OpCtz64(v, config)
+	case OpEq64:
+		return rewriteValuedec64_OpEq64(v, config)
+	case OpGeq64:
+		return rewriteValuedec64_OpGeq64(v, config)
+	case OpGeq64U:
+		return rewriteValuedec64_OpGeq64U(v, config)
+	case OpGreater64:
+		return rewriteValuedec64_OpGreater64(v, config)
+	case OpGreater64U:
+		return rewriteValuedec64_OpGreater64U(v, config)
+	case OpInt64Hi:
+		return rewriteValuedec64_OpInt64Hi(v, config)
+	case OpInt64Lo:
+		return rewriteValuedec64_OpInt64Lo(v, config)
+	case OpLeq64:
+		return rewriteValuedec64_OpLeq64(v, config)
+	case OpLeq64U:
+		return rewriteValuedec64_OpLeq64U(v, config)
+	case OpLess64:
+		return rewriteValuedec64_OpLess64(v, config)
+	case OpLess64U:
+		return rewriteValuedec64_OpLess64U(v, config)
+	case OpLoad:
+		return rewriteValuedec64_OpLoad(v, config)
+	case OpLrot64:
+		return rewriteValuedec64_OpLrot64(v, config)
+	case OpLsh16x64:
+		return rewriteValuedec64_OpLsh16x64(v, config)
+	case OpLsh32x64:
+		return rewriteValuedec64_OpLsh32x64(v, config)
+	case OpLsh64x16:
+		return rewriteValuedec64_OpLsh64x16(v, config)
+	case OpLsh64x32:
+		return rewriteValuedec64_OpLsh64x32(v, config)
+	case OpLsh64x64:
+		return rewriteValuedec64_OpLsh64x64(v, config)
+	case OpLsh64x8:
+		return rewriteValuedec64_OpLsh64x8(v, config)
+	case OpLsh8x64:
+		return rewriteValuedec64_OpLsh8x64(v, config)
+	case OpMul64:
+		return rewriteValuedec64_OpMul64(v, config)
+	case OpNeg64:
+		return rewriteValuedec64_OpNeg64(v, config)
+	case OpNeq64:
+		return rewriteValuedec64_OpNeq64(v, config)
+	case OpOr64:
+		return rewriteValuedec64_OpOr64(v, config)
+	case OpRsh16Ux64:
+		return rewriteValuedec64_OpRsh16Ux64(v, config)
+	case OpRsh16x64:
+		return rewriteValuedec64_OpRsh16x64(v, config)
+	case OpRsh32Ux64:
+		return rewriteValuedec64_OpRsh32Ux64(v, config)
+	case OpRsh32x64:
+		return rewriteValuedec64_OpRsh32x64(v, config)
+	case OpRsh64Ux16:
+		return rewriteValuedec64_OpRsh64Ux16(v, config)
+	case OpRsh64Ux32:
+		return rewriteValuedec64_OpRsh64Ux32(v, config)
+	case OpRsh64Ux64:
+		return rewriteValuedec64_OpRsh64Ux64(v, config)
+	case OpRsh64Ux8:
+		return rewriteValuedec64_OpRsh64Ux8(v, config)
+	case OpRsh64x16:
+		return rewriteValuedec64_OpRsh64x16(v, config)
+	case OpRsh64x32:
+		return rewriteValuedec64_OpRsh64x32(v, config)
+	case OpRsh64x64:
+		return rewriteValuedec64_OpRsh64x64(v, config)
+	case OpRsh64x8:
+		return rewriteValuedec64_OpRsh64x8(v, config)
+	case OpRsh8Ux64:
+		return rewriteValuedec64_OpRsh8Ux64(v, config)
+	case OpRsh8x64:
+		return rewriteValuedec64_OpRsh8x64(v, config)
+	case OpSignExt16to64:
+		return rewriteValuedec64_OpSignExt16to64(v, config)
+	case OpSignExt32to64:
+		return rewriteValuedec64_OpSignExt32to64(v, config)
+	case OpSignExt8to64:
+		return rewriteValuedec64_OpSignExt8to64(v, config)
+	case OpStore:
+		return rewriteValuedec64_OpStore(v, config)
+	case OpSub64:
+		return rewriteValuedec64_OpSub64(v, config)
+	case OpTrunc64to16:
+		return rewriteValuedec64_OpTrunc64to16(v, config)
+	case OpTrunc64to32:
+		return rewriteValuedec64_OpTrunc64to32(v, config)
+	case OpTrunc64to8:
+		return rewriteValuedec64_OpTrunc64to8(v, config)
+	case OpXor64:
+		return rewriteValuedec64_OpXor64(v, config)
+	case OpZeroExt16to64:
+		return rewriteValuedec64_OpZeroExt16to64(v, config)
+	case OpZeroExt32to64:
+		return rewriteValuedec64_OpZeroExt32to64(v, config)
+	case OpZeroExt8to64:
+		return rewriteValuedec64_OpZeroExt8to64(v, config)
+	}
+	return false
+}
+func rewriteValuedec64_OpAdd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Add64 x y)
+	// cond:
+	// result: (Int64Make 		(Add32withcarry <config.fe.TypeInt32()> 			(Int64Hi x) 			(Int64Hi y) 			(Select1 <TypeFlags> (Add32carry (Int64Lo x) (Int64Lo y)))) 		(Select0 <config.fe.TypeUInt32()> (Add32carry (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpAdd32withcarry, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSelect1, TypeFlags)
+		v4 := b.NewValue0(v.Line, OpAdd32carry, MakeTuple(config.fe.TypeUInt32(), TypeFlags))
+		v5 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v3.AddArg(v4)
+		v0.AddArg(v3)
+		v.AddArg(v0)
+		v7 := b.NewValue0(v.Line, OpSelect0, config.fe.TypeUInt32())
+		v8 := b.NewValue0(v.Line, OpAdd32carry, MakeTuple(config.fe.TypeUInt32(), TypeFlags))
+		v9 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v9.AddArg(x)
+		v8.AddArg(v9)
+		v10 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v10.AddArg(y)
+		v8.AddArg(v10)
+		v7.AddArg(v8)
+		v.AddArg(v7)
+		return true
+	}
+}
+func rewriteValuedec64_OpAnd64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (And64 x y)
+	// cond:
+	// result: (Int64Make 		(And32 <config.fe.TypeUInt32()> (Int64Hi x) (Int64Hi y)) 		(And32 <config.fe.TypeUInt32()> (Int64Lo x) (Int64Lo y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpAnd32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpAnd32, config.fe.TypeUInt32())
+		v4 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v3.AddArg(v5)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpArg(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Arg {n} [off])
+	// cond: is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned()
+	// result: (Int64Make     (Arg <config.fe.TypeInt32()> {n} [off+4])     (Arg <config.fe.TypeUInt32()> {n} [off]))
+	for {
+		off := v.AuxInt
+		n := v.Aux
+		if !(is64BitInt(v.Type) && !config.BigEndian && v.Type.IsSigned()) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpArg, config.fe.TypeInt32())
+		v0.AuxInt = off + 4
+		v0.Aux = n
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpArg, config.fe.TypeUInt32())
+		v1.AuxInt = off
+		v1.Aux = n
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Arg {n} [off])
+	// cond: is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned()
+	// result: (Int64Make     (Arg <config.fe.TypeUInt32()> {n} [off+4])     (Arg <config.fe.TypeUInt32()> {n} [off]))
+	for {
+		off := v.AuxInt
+		n := v.Aux
+		if !(is64BitInt(v.Type) && !config.BigEndian && !v.Type.IsSigned()) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpArg, config.fe.TypeUInt32())
+		v0.AuxInt = off + 4
+		v0.Aux = n
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpArg, config.fe.TypeUInt32())
+		v1.AuxInt = off
+		v1.Aux = n
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Arg {n} [off])
+	// cond: is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned()
+	// result: (Int64Make     (Arg <config.fe.TypeInt32()> {n} [off])     (Arg <config.fe.TypeUInt32()> {n} [off+4]))
+	for {
+		off := v.AuxInt
+		n := v.Aux
+		if !(is64BitInt(v.Type) && config.BigEndian && v.Type.IsSigned()) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpArg, config.fe.TypeInt32())
+		v0.AuxInt = off
+		v0.Aux = n
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpArg, config.fe.TypeUInt32())
+		v1.AuxInt = off + 4
+		v1.Aux = n
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Arg {n} [off])
+	// cond: is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned()
+	// result: (Int64Make     (Arg <config.fe.TypeUInt32()> {n} [off])     (Arg <config.fe.TypeUInt32()> {n} [off+4]))
+	for {
+		off := v.AuxInt
+		n := v.Aux
+		if !(is64BitInt(v.Type) && config.BigEndian && !v.Type.IsSigned()) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpArg, config.fe.TypeUInt32())
+		v0.AuxInt = off
+		v0.Aux = n
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpArg, config.fe.TypeUInt32())
+		v1.AuxInt = off + 4
+		v1.Aux = n
+		v.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpBswap64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Bswap64 x)
+	// cond:
+	// result: (Int64Make 		(Bswap32 <config.fe.TypeUInt32()> (Int64Lo x)) 		(Bswap32 <config.fe.TypeUInt32()> (Int64Hi x)))
+	for {
+		x := v.Args[0]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpBswap32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpBswap32, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValuedec64_OpCom64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Com64 x)
+	// cond:
+	// result: (Int64Make 		(Com32 <config.fe.TypeUInt32()> (Int64Hi x)) 		(Com32 <config.fe.TypeUInt32()> (Int64Lo x)))
+	for {
+		x := v.Args[0]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpCom32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpCom32, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+}
+func rewriteValuedec64_OpConst64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Const64 <t> [c])
+	// cond: t.IsSigned()
+	// result: (Int64Make (Const32 <config.fe.TypeInt32()> [c>>32]) (Const32 <config.fe.TypeUInt32()> [int64(int32(c))]))
+	for {
+		t := v.Type
+		c := v.AuxInt
+		if !(t.IsSigned()) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpConst32, config.fe.TypeInt32())
+		v0.AuxInt = c >> 32
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v1.AuxInt = int64(int32(c))
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Const64 <t> [c])
+	// cond: !t.IsSigned()
+	// result: (Int64Make (Const32 <config.fe.TypeUInt32()> [c>>32]) (Const32 <config.fe.TypeUInt32()> [int64(int32(c))]))
+	for {
+		t := v.Type
+		c := v.AuxInt
+		if !(!t.IsSigned()) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v0.AuxInt = c >> 32
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v1.AuxInt = int64(int32(c))
+		v.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpCtz64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Ctz64 x)
+	// cond:
+	// result: (Int64Make 		(Const32 <config.fe.TypeUInt32()> [0]) 		(Add32 <config.fe.TypeUInt32()> 			(Ctz32 <config.fe.TypeUInt32()> (Int64Lo x)) 			(And32 <config.fe.TypeUInt32()> 				(Com32 <config.fe.TypeUInt32()> (Zeromask (Int64Lo x))) 				(Ctz32 <config.fe.TypeUInt32()> (Int64Hi x)))))
+	for {
+		x := v.Args[0]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpAdd32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpCtz32, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v1.AddArg(v2)
+		v4 := b.NewValue0(v.Line, OpAnd32, config.fe.TypeUInt32())
+		v5 := b.NewValue0(v.Line, OpCom32, config.fe.TypeUInt32())
+		v6 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v7 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v7.AddArg(x)
+		v6.AddArg(v7)
+		v5.AddArg(v6)
+		v4.AddArg(v5)
+		v8 := b.NewValue0(v.Line, OpCtz32, config.fe.TypeUInt32())
+		v9 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v9.AddArg(x)
+		v8.AddArg(v9)
+		v4.AddArg(v8)
+		v1.AddArg(v4)
+		v.AddArg(v1)
+		return true
+	}
+}
+func rewriteValuedec64_OpEq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Eq64 x y)
+	// cond:
+	// result: (AndB 		(Eq32 (Int64Hi x) (Int64Hi y)) 		(Eq32 (Int64Lo x) (Int64Lo y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpAndB)
+		v0 := b.NewValue0(v.Line, OpEq32, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpEq32, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v3.AddArg(v5)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpGeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64 x y)
+	// cond:
+	// result: (OrB 		(Greater32 (Int64Hi x) (Int64Hi y)) 		(AndB 			(Eq32 (Int64Hi x) (Int64Hi y)) 			(Geq32U (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpOrB)
+		v0 := b.NewValue0(v.Line, OpGreater32, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpAndB, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpEq32, config.fe.TypeBool())
+		v5 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v3.AddArg(v4)
+		v7 := b.NewValue0(v.Line, OpGeq32U, config.fe.TypeBool())
+		v8 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v8.AddArg(x)
+		v7.AddArg(v8)
+		v9 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v9.AddArg(y)
+		v7.AddArg(v9)
+		v3.AddArg(v7)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpGeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Geq64U x y)
+	// cond:
+	// result: (OrB 		(Greater32U (Int64Hi x) (Int64Hi y)) 		(AndB 			(Eq32 (Int64Hi x) (Int64Hi y)) 			(Geq32U (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpOrB)
+		v0 := b.NewValue0(v.Line, OpGreater32U, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpAndB, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpEq32, config.fe.TypeBool())
+		v5 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v3.AddArg(v4)
+		v7 := b.NewValue0(v.Line, OpGeq32U, config.fe.TypeBool())
+		v8 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v8.AddArg(x)
+		v7.AddArg(v8)
+		v9 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v9.AddArg(y)
+		v7.AddArg(v9)
+		v3.AddArg(v7)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpGreater64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64 x y)
+	// cond:
+	// result: (OrB 		(Greater32 (Int64Hi x) (Int64Hi y)) 		(AndB 			(Eq32 (Int64Hi x) (Int64Hi y)) 			(Greater32U (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpOrB)
+		v0 := b.NewValue0(v.Line, OpGreater32, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpAndB, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpEq32, config.fe.TypeBool())
+		v5 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v3.AddArg(v4)
+		v7 := b.NewValue0(v.Line, OpGreater32U, config.fe.TypeBool())
+		v8 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v8.AddArg(x)
+		v7.AddArg(v8)
+		v9 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v9.AddArg(y)
+		v7.AddArg(v9)
+		v3.AddArg(v7)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpGreater64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Greater64U x y)
+	// cond:
+	// result: (OrB 		(Greater32U (Int64Hi x) (Int64Hi y)) 		(AndB 			(Eq32 (Int64Hi x) (Int64Hi y)) 			(Greater32U (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpOrB)
+		v0 := b.NewValue0(v.Line, OpGreater32U, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpAndB, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpEq32, config.fe.TypeBool())
+		v5 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v3.AddArg(v4)
+		v7 := b.NewValue0(v.Line, OpGreater32U, config.fe.TypeBool())
+		v8 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v8.AddArg(x)
+		v7.AddArg(v8)
+		v9 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v9.AddArg(y)
+		v7.AddArg(v9)
+		v3.AddArg(v7)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpInt64Hi(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Int64Hi (Int64Make hi _))
+	// cond:
+	// result: hi
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = hi.Type
+		v.AddArg(hi)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpInt64Lo(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Int64Lo (Int64Make _ lo))
+	// cond:
+	// result: lo
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		lo := v_0.Args[1]
+		v.reset(OpCopy)
+		v.Type = lo.Type
+		v.AddArg(lo)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpLeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64 x y)
+	// cond:
+	// result: (OrB 		(Less32 (Int64Hi x) (Int64Hi y)) 		(AndB 			(Eq32 (Int64Hi x) (Int64Hi y)) 			(Leq32U (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpOrB)
+		v0 := b.NewValue0(v.Line, OpLess32, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpAndB, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpEq32, config.fe.TypeBool())
+		v5 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v3.AddArg(v4)
+		v7 := b.NewValue0(v.Line, OpLeq32U, config.fe.TypeBool())
+		v8 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v8.AddArg(x)
+		v7.AddArg(v8)
+		v9 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v9.AddArg(y)
+		v7.AddArg(v9)
+		v3.AddArg(v7)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpLeq64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Leq64U x y)
+	// cond:
+	// result: (OrB 		(Less32U (Int64Hi x) (Int64Hi y)) 		(AndB 			(Eq32 (Int64Hi x) (Int64Hi y)) 			(Leq32U (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpOrB)
+		v0 := b.NewValue0(v.Line, OpLess32U, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpAndB, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpEq32, config.fe.TypeBool())
+		v5 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v3.AddArg(v4)
+		v7 := b.NewValue0(v.Line, OpLeq32U, config.fe.TypeBool())
+		v8 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v8.AddArg(x)
+		v7.AddArg(v8)
+		v9 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v9.AddArg(y)
+		v7.AddArg(v9)
+		v3.AddArg(v7)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpLess64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64 x y)
+	// cond:
+	// result: (OrB 		(Less32 (Int64Hi x) (Int64Hi y)) 		(AndB 			(Eq32 (Int64Hi x) (Int64Hi y)) 			(Less32U (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpOrB)
+		v0 := b.NewValue0(v.Line, OpLess32, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpAndB, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpEq32, config.fe.TypeBool())
+		v5 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v3.AddArg(v4)
+		v7 := b.NewValue0(v.Line, OpLess32U, config.fe.TypeBool())
+		v8 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v8.AddArg(x)
+		v7.AddArg(v8)
+		v9 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v9.AddArg(y)
+		v7.AddArg(v9)
+		v3.AddArg(v7)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpLess64U(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Less64U x y)
+	// cond:
+	// result: (OrB 		(Less32U (Int64Hi x) (Int64Hi y)) 		(AndB 			(Eq32 (Int64Hi x) (Int64Hi y)) 			(Less32U (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpOrB)
+		v0 := b.NewValue0(v.Line, OpLess32U, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpAndB, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpEq32, config.fe.TypeBool())
+		v5 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v3.AddArg(v4)
+		v7 := b.NewValue0(v.Line, OpLess32U, config.fe.TypeBool())
+		v8 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v8.AddArg(x)
+		v7.AddArg(v8)
+		v9 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v9.AddArg(y)
+		v7.AddArg(v9)
+		v3.AddArg(v7)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpLoad(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Load <t> ptr mem)
+	// cond: is64BitInt(t) && !config.BigEndian && t.IsSigned()
+	// result: (Int64Make 		(Load <config.fe.TypeInt32()> (OffPtr <config.fe.TypeInt32().PtrTo()> [4] ptr) mem) 		(Load <config.fe.TypeUInt32()> ptr mem))
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitInt(t) && !config.BigEndian && t.IsSigned()) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpLoad, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpOffPtr, config.fe.TypeInt32().PtrTo())
+		v1.AuxInt = 4
+		v1.AddArg(ptr)
+		v0.AddArg(v1)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpLoad, config.fe.TypeUInt32())
+		v2.AddArg(ptr)
+		v2.AddArg(mem)
+		v.AddArg(v2)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitInt(t) && !config.BigEndian && !t.IsSigned()
+	// result: (Int64Make 		(Load <config.fe.TypeUInt32()> (OffPtr <config.fe.TypeUInt32().PtrTo()> [4] ptr) mem) 		(Load <config.fe.TypeUInt32()> ptr mem))
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitInt(t) && !config.BigEndian && !t.IsSigned()) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpLoad, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpOffPtr, config.fe.TypeUInt32().PtrTo())
+		v1.AuxInt = 4
+		v1.AddArg(ptr)
+		v0.AddArg(v1)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpLoad, config.fe.TypeUInt32())
+		v2.AddArg(ptr)
+		v2.AddArg(mem)
+		v.AddArg(v2)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitInt(t) && config.BigEndian && t.IsSigned()
+	// result: (Int64Make 		(Load <config.fe.TypeInt32()> ptr mem) 		(Load <config.fe.TypeUInt32()> (OffPtr <config.fe.TypeUInt32().PtrTo()> [4] ptr) mem))
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitInt(t) && config.BigEndian && t.IsSigned()) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpLoad, config.fe.TypeInt32())
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpLoad, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpOffPtr, config.fe.TypeUInt32().PtrTo())
+		v2.AuxInt = 4
+		v2.AddArg(ptr)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: is64BitInt(t) && config.BigEndian && !t.IsSigned()
+	// result: (Int64Make 		(Load <config.fe.TypeUInt32()> ptr mem) 		(Load <config.fe.TypeUInt32()> (OffPtr <config.fe.TypeUInt32().PtrTo()> [4] ptr) mem))
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(is64BitInt(t) && config.BigEndian && !t.IsSigned()) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpLoad, config.fe.TypeUInt32())
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpLoad, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpOffPtr, config.fe.TypeUInt32().PtrTo())
+		v2.AuxInt = 4
+		v2.AddArg(ptr)
+		v1.AddArg(v2)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpLrot64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lrot64 (Int64Make hi lo) [c])
+	// cond: c <= 32
+	// result: (Int64Make 		(Or32 <config.fe.TypeUInt32()> 			(Lsh32x32 <config.fe.TypeUInt32()> hi (Const32 <config.fe.TypeUInt32()> [c])) 			(Rsh32Ux32 <config.fe.TypeUInt32()> lo (Const32 <config.fe.TypeUInt32()> [32-c]))) 		(Or32 <config.fe.TypeUInt32()> 			(Lsh32x32 <config.fe.TypeUInt32()> lo (Const32 <config.fe.TypeUInt32()> [c])) 			(Rsh32Ux32 <config.fe.TypeUInt32()> hi (Const32 <config.fe.TypeUInt32()> [32-c]))))
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		if !(c <= 32) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpLsh32x32, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v2 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v2.AuxInt = c
+		v1.AddArg(v2)
+		v0.AddArg(v1)
+		v3 := b.NewValue0(v.Line, OpRsh32Ux32, config.fe.TypeUInt32())
+		v3.AddArg(lo)
+		v4 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v4.AuxInt = 32 - c
+		v3.AddArg(v4)
+		v0.AddArg(v3)
+		v.AddArg(v0)
+		v5 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v6 := b.NewValue0(v.Line, OpLsh32x32, config.fe.TypeUInt32())
+		v6.AddArg(lo)
+		v7 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v7.AuxInt = c
+		v6.AddArg(v7)
+		v5.AddArg(v6)
+		v8 := b.NewValue0(v.Line, OpRsh32Ux32, config.fe.TypeUInt32())
+		v8.AddArg(hi)
+		v9 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v9.AuxInt = 32 - c
+		v8.AddArg(v9)
+		v5.AddArg(v8)
+		v.AddArg(v5)
+		return true
+	}
+	// match: (Lrot64 (Int64Make hi lo) [c])
+	// cond: c > 32
+	// result: (Lrot64 (Int64Make lo hi) [c-32])
+	for {
+		c := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		if !(c > 32) {
+			break
+		}
+		v.reset(OpLrot64)
+		v.AuxInt = c - 32
+		v0 := b.NewValue0(v.Line, OpInt64Make, config.fe.TypeUInt64())
+		v0.AddArg(lo)
+		v0.AddArg(hi)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpLsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh16x64 _ (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Const32 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh16x64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Lsh16x32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpLsh16x32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Lsh16x64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Lsh16x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpLsh16x32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpLsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh32x64 _ (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Const32 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh32x64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Lsh32x32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpLsh32x32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Lsh32x64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Lsh32x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpLsh32x32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpLsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x16 (Int64Make hi lo) s)
+	// cond:
+	// result: (Int64Make 		(Or32 <config.fe.TypeUInt32()> 			(Or32 <config.fe.TypeUInt32()> 				(Lsh32x16 <config.fe.TypeUInt32()> hi s) 				(Rsh32Ux16 <config.fe.TypeUInt32()> 					lo 					(Sub16 <config.fe.TypeUInt16()> (Const16 <config.fe.TypeUInt16()> [32]) s))) 			(Lsh32x16 <config.fe.TypeUInt32()> 				lo 				(Sub16 <config.fe.TypeUInt16()> s (Const16 <config.fe.TypeUInt16()> [32])))) 		(Lsh32x16 <config.fe.TypeUInt32()> lo s))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		s := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpLsh32x16, config.fe.TypeUInt32())
+		v2.AddArg(hi)
+		v2.AddArg(s)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpRsh32Ux16, config.fe.TypeUInt32())
+		v3.AddArg(lo)
+		v4 := b.NewValue0(v.Line, OpSub16, config.fe.TypeUInt16())
+		v5 := b.NewValue0(v.Line, OpConst16, config.fe.TypeUInt16())
+		v5.AuxInt = 32
+		v4.AddArg(v5)
+		v4.AddArg(s)
+		v3.AddArg(v4)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v6 := b.NewValue0(v.Line, OpLsh32x16, config.fe.TypeUInt32())
+		v6.AddArg(lo)
+		v7 := b.NewValue0(v.Line, OpSub16, config.fe.TypeUInt16())
+		v7.AddArg(s)
+		v8 := b.NewValue0(v.Line, OpConst16, config.fe.TypeUInt16())
+		v8.AuxInt = 32
+		v7.AddArg(v8)
+		v6.AddArg(v7)
+		v0.AddArg(v6)
+		v.AddArg(v0)
+		v9 := b.NewValue0(v.Line, OpLsh32x16, config.fe.TypeUInt32())
+		v9.AddArg(lo)
+		v9.AddArg(s)
+		v.AddArg(v9)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpLsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x32 (Int64Make hi lo) s)
+	// cond:
+	// result: (Int64Make 		(Or32 <config.fe.TypeUInt32()> 			(Or32 <config.fe.TypeUInt32()> 				(Lsh32x32 <config.fe.TypeUInt32()> hi s) 				(Rsh32Ux32 <config.fe.TypeUInt32()> 					lo 					(Sub32 <config.fe.TypeUInt32()> (Const32 <config.fe.TypeUInt32()> [32]) s))) 			(Lsh32x32 <config.fe.TypeUInt32()> 				lo 				(Sub32 <config.fe.TypeUInt32()> s (Const32 <config.fe.TypeUInt32()> [32])))) 		(Lsh32x32 <config.fe.TypeUInt32()> lo s))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		s := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpLsh32x32, config.fe.TypeUInt32())
+		v2.AddArg(hi)
+		v2.AddArg(s)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpRsh32Ux32, config.fe.TypeUInt32())
+		v3.AddArg(lo)
+		v4 := b.NewValue0(v.Line, OpSub32, config.fe.TypeUInt32())
+		v5 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v5.AuxInt = 32
+		v4.AddArg(v5)
+		v4.AddArg(s)
+		v3.AddArg(v4)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v6 := b.NewValue0(v.Line, OpLsh32x32, config.fe.TypeUInt32())
+		v6.AddArg(lo)
+		v7 := b.NewValue0(v.Line, OpSub32, config.fe.TypeUInt32())
+		v7.AddArg(s)
+		v8 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v8.AuxInt = 32
+		v7.AddArg(v8)
+		v6.AddArg(v7)
+		v0.AddArg(v6)
+		v.AddArg(v0)
+		v9 := b.NewValue0(v.Line, OpLsh32x32, config.fe.TypeUInt32())
+		v9.AddArg(lo)
+		v9.AddArg(s)
+		v.AddArg(v9)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpLsh64x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x64 _ (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Const64 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpConst64)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh64x64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Lsh64x32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpLsh64x32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Lsh64x64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Lsh64x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpLsh64x32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpLsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh64x8 (Int64Make hi lo) s)
+	// cond:
+	// result: (Int64Make 		(Or32 <config.fe.TypeUInt32()> 			(Or32 <config.fe.TypeUInt32()> 				(Lsh32x8 <config.fe.TypeUInt32()> hi s) 				(Rsh32Ux8 <config.fe.TypeUInt32()> 					lo 					(Sub8 <config.fe.TypeUInt8()> (Const8 <config.fe.TypeUInt8()> [32]) s))) 			(Lsh32x8 <config.fe.TypeUInt32()> 				lo 				(Sub8 <config.fe.TypeUInt8()> s (Const8 <config.fe.TypeUInt8()> [32])))) 		(Lsh32x8 <config.fe.TypeUInt32()> lo s))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		s := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpLsh32x8, config.fe.TypeUInt32())
+		v2.AddArg(hi)
+		v2.AddArg(s)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpRsh32Ux8, config.fe.TypeUInt32())
+		v3.AddArg(lo)
+		v4 := b.NewValue0(v.Line, OpSub8, config.fe.TypeUInt8())
+		v5 := b.NewValue0(v.Line, OpConst8, config.fe.TypeUInt8())
+		v5.AuxInt = 32
+		v4.AddArg(v5)
+		v4.AddArg(s)
+		v3.AddArg(v4)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v6 := b.NewValue0(v.Line, OpLsh32x8, config.fe.TypeUInt32())
+		v6.AddArg(lo)
+		v7 := b.NewValue0(v.Line, OpSub8, config.fe.TypeUInt8())
+		v7.AddArg(s)
+		v8 := b.NewValue0(v.Line, OpConst8, config.fe.TypeUInt8())
+		v8.AuxInt = 32
+		v7.AddArg(v8)
+		v6.AddArg(v7)
+		v0.AddArg(v6)
+		v.AddArg(v0)
+		v9 := b.NewValue0(v.Line, OpLsh32x8, config.fe.TypeUInt32())
+		v9.AddArg(lo)
+		v9.AddArg(s)
+		v.AddArg(v9)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpLsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Lsh8x64 _ (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Const32 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Lsh8x64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Lsh8x32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpLsh8x32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Lsh8x64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Lsh8x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpLsh8x32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpMul64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Mul64 x y)
+	// cond:
+	// result: (Int64Make 		(Add32 <config.fe.TypeUInt32()> 			(Mul32 <config.fe.TypeUInt32()> (Int64Lo x) (Int64Hi y)) 			(Add32 <config.fe.TypeUInt32()> 				(Mul32 <config.fe.TypeUInt32()> (Int64Hi x) (Int64Lo y)) 				(Select0 <config.fe.TypeUInt32()> (Mul32uhilo (Int64Lo x) (Int64Lo y))))) 		(Select1 <config.fe.TypeUInt32()> (Mul32uhilo (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpAdd32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpMul32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v2.AddArg(x)
+		v1.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v3.AddArg(y)
+		v1.AddArg(v3)
+		v0.AddArg(v1)
+		v4 := b.NewValue0(v.Line, OpAdd32, config.fe.TypeUInt32())
+		v5 := b.NewValue0(v.Line, OpMul32, config.fe.TypeUInt32())
+		v6 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v6.AddArg(x)
+		v5.AddArg(v6)
+		v7 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v7.AddArg(y)
+		v5.AddArg(v7)
+		v4.AddArg(v5)
+		v8 := b.NewValue0(v.Line, OpSelect0, config.fe.TypeUInt32())
+		v9 := b.NewValue0(v.Line, OpMul32uhilo, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v10 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v10.AddArg(x)
+		v9.AddArg(v10)
+		v11 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v11.AddArg(y)
+		v9.AddArg(v11)
+		v8.AddArg(v9)
+		v4.AddArg(v8)
+		v0.AddArg(v4)
+		v.AddArg(v0)
+		v12 := b.NewValue0(v.Line, OpSelect1, config.fe.TypeUInt32())
+		v13 := b.NewValue0(v.Line, OpMul32uhilo, MakeTuple(config.fe.TypeUInt32(), config.fe.TypeUInt32()))
+		v14 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v14.AddArg(x)
+		v13.AddArg(v14)
+		v15 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v15.AddArg(y)
+		v13.AddArg(v15)
+		v12.AddArg(v13)
+		v.AddArg(v12)
+		return true
+	}
+}
+func rewriteValuedec64_OpNeg64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neg64 <t> x)
+	// cond:
+	// result: (Sub64 (Const64 <t> [0]) x)
+	for {
+		t := v.Type
+		x := v.Args[0]
+		v.reset(OpSub64)
+		v0 := b.NewValue0(v.Line, OpConst64, t)
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuedec64_OpNeq64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Neq64 x y)
+	// cond:
+	// result: (OrB 		(Neq32 (Int64Hi x) (Int64Hi y)) 		(Neq32 (Int64Lo x) (Int64Lo y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpOrB)
+		v0 := b.NewValue0(v.Line, OpNeq32, config.fe.TypeBool())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpNeq32, config.fe.TypeBool())
+		v4 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v3.AddArg(v5)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpOr64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or64 x y)
+	// cond:
+	// result: (Int64Make 		(Or32 <config.fe.TypeUInt32()> (Int64Hi x) (Int64Hi y)) 		(Or32 <config.fe.TypeUInt32()> (Int64Lo x) (Int64Lo y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v4 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v3.AddArg(v5)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpRsh16Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16Ux64 _ (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Const32 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh16Ux64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Rsh16Ux32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpRsh16Ux32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Rsh16Ux64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Rsh16Ux32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpRsh16Ux32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh16x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh16x64 x (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Signmask (SignExt16to32 x))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpSignmask)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh16x64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Rsh16x32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpRsh16x32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Rsh16x64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Rsh16x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpRsh16x32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh32Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32Ux64 _ (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Const32 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh32Ux64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Rsh32Ux32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpRsh32Ux32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Rsh32Ux64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Rsh32Ux32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpRsh32Ux32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh32x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh32x64 x (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Signmask x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpSignmask)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Rsh32x64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Rsh32x32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpRsh32x32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Rsh32x64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Rsh32x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpRsh32x32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh64Ux16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux16 (Int64Make hi lo) s)
+	// cond:
+	// result: (Int64Make 		(Rsh32Ux16 <config.fe.TypeUInt32()> hi s) 		(Or32 <config.fe.TypeUInt32()> 			(Or32 <config.fe.TypeUInt32()> 				(Rsh32Ux16 <config.fe.TypeUInt32()> lo s) 				(Lsh32x16 <config.fe.TypeUInt32()> 					hi 					(Sub16 <config.fe.TypeUInt16()> (Const16 <config.fe.TypeUInt16()> [32]) s))) 			(Rsh32Ux16 <config.fe.TypeUInt32()> 				hi 				(Sub16 <config.fe.TypeUInt16()> s (Const16 <config.fe.TypeUInt16()> [32])))))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		s := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpRsh32Ux16, config.fe.TypeUInt32())
+		v0.AddArg(hi)
+		v0.AddArg(s)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpRsh32Ux16, config.fe.TypeUInt32())
+		v3.AddArg(lo)
+		v3.AddArg(s)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpLsh32x16, config.fe.TypeUInt32())
+		v4.AddArg(hi)
+		v5 := b.NewValue0(v.Line, OpSub16, config.fe.TypeUInt16())
+		v6 := b.NewValue0(v.Line, OpConst16, config.fe.TypeUInt16())
+		v6.AuxInt = 32
+		v5.AddArg(v6)
+		v5.AddArg(s)
+		v4.AddArg(v5)
+		v2.AddArg(v4)
+		v1.AddArg(v2)
+		v7 := b.NewValue0(v.Line, OpRsh32Ux16, config.fe.TypeUInt32())
+		v7.AddArg(hi)
+		v8 := b.NewValue0(v.Line, OpSub16, config.fe.TypeUInt16())
+		v8.AddArg(s)
+		v9 := b.NewValue0(v.Line, OpConst16, config.fe.TypeUInt16())
+		v9.AuxInt = 32
+		v8.AddArg(v9)
+		v7.AddArg(v8)
+		v1.AddArg(v7)
+		v.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh64Ux32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux32 (Int64Make hi lo) s)
+	// cond:
+	// result: (Int64Make 		(Rsh32Ux32 <config.fe.TypeUInt32()> hi s) 		(Or32 <config.fe.TypeUInt32()> 			(Or32 <config.fe.TypeUInt32()> 				(Rsh32Ux32 <config.fe.TypeUInt32()> lo s) 				(Lsh32x32 <config.fe.TypeUInt32()> 					hi 					(Sub32 <config.fe.TypeUInt32()> (Const32 <config.fe.TypeUInt32()> [32]) s))) 			(Rsh32Ux32 <config.fe.TypeUInt32()> 				hi 				(Sub32 <config.fe.TypeUInt32()> s (Const32 <config.fe.TypeUInt32()> [32])))))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		s := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpRsh32Ux32, config.fe.TypeUInt32())
+		v0.AddArg(hi)
+		v0.AddArg(s)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpRsh32Ux32, config.fe.TypeUInt32())
+		v3.AddArg(lo)
+		v3.AddArg(s)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpLsh32x32, config.fe.TypeUInt32())
+		v4.AddArg(hi)
+		v5 := b.NewValue0(v.Line, OpSub32, config.fe.TypeUInt32())
+		v6 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v6.AuxInt = 32
+		v5.AddArg(v6)
+		v5.AddArg(s)
+		v4.AddArg(v5)
+		v2.AddArg(v4)
+		v1.AddArg(v2)
+		v7 := b.NewValue0(v.Line, OpRsh32Ux32, config.fe.TypeUInt32())
+		v7.AddArg(hi)
+		v8 := b.NewValue0(v.Line, OpSub32, config.fe.TypeUInt32())
+		v8.AddArg(s)
+		v9 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v9.AuxInt = 32
+		v8.AddArg(v9)
+		v7.AddArg(v8)
+		v1.AddArg(v7)
+		v.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh64Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux64 _ (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Const64 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpConst64)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh64Ux64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Rsh64Ux32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpRsh64Ux32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Rsh64Ux64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Rsh64Ux32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpRsh64Ux32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh64Ux8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64Ux8 (Int64Make hi lo) s)
+	// cond:
+	// result: (Int64Make 		(Rsh32Ux8 <config.fe.TypeUInt32()> hi s) 		(Or32 <config.fe.TypeUInt32()> 			(Or32 <config.fe.TypeUInt32()> 				(Rsh32Ux8 <config.fe.TypeUInt32()> lo s) 				(Lsh32x8 <config.fe.TypeUInt32()> 					hi 					(Sub8 <config.fe.TypeUInt8()> (Const8 <config.fe.TypeUInt8()> [32]) s))) 			(Rsh32Ux8 <config.fe.TypeUInt32()> 				hi 				(Sub8 <config.fe.TypeUInt8()> s (Const8 <config.fe.TypeUInt8()> [32])))))
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		s := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpRsh32Ux8, config.fe.TypeUInt32())
+		v0.AddArg(hi)
+		v0.AddArg(s)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpRsh32Ux8, config.fe.TypeUInt32())
+		v3.AddArg(lo)
+		v3.AddArg(s)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpLsh32x8, config.fe.TypeUInt32())
+		v4.AddArg(hi)
+		v5 := b.NewValue0(v.Line, OpSub8, config.fe.TypeUInt8())
+		v6 := b.NewValue0(v.Line, OpConst8, config.fe.TypeUInt8())
+		v6.AuxInt = 32
+		v5.AddArg(v6)
+		v5.AddArg(s)
+		v4.AddArg(v5)
+		v2.AddArg(v4)
+		v1.AddArg(v2)
+		v7 := b.NewValue0(v.Line, OpRsh32Ux8, config.fe.TypeUInt32())
+		v7.AddArg(hi)
+		v8 := b.NewValue0(v.Line, OpSub8, config.fe.TypeUInt8())
+		v8.AddArg(s)
+		v9 := b.NewValue0(v.Line, OpConst8, config.fe.TypeUInt8())
+		v9.AuxInt = 32
+		v8.AddArg(v9)
+		v7.AddArg(v8)
+		v1.AddArg(v7)
+		v.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh64x16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x16 (Int64Make hi lo) s)
+	// cond:
+	// result: (Int64Make 		(Rsh32x16 <config.fe.TypeUInt32()> hi s) 		(Or32 <config.fe.TypeUInt32()> 			(Or32 <config.fe.TypeUInt32()> 				(Rsh32Ux16 <config.fe.TypeUInt32()> lo s) 				(Lsh32x16 <config.fe.TypeUInt32()> 					hi 					(Sub16 <config.fe.TypeUInt16()> (Const16 <config.fe.TypeUInt16()> [32]) s))) 			(And32 <config.fe.TypeUInt32()> 				(Rsh32x16 <config.fe.TypeUInt32()> 					hi 					(Sub16 <config.fe.TypeUInt16()> s (Const16 <config.fe.TypeUInt16()> [32]))) 				(Zeromask 					(Z [...]
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		s := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpRsh32x16, config.fe.TypeUInt32())
+		v0.AddArg(hi)
+		v0.AddArg(s)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpRsh32Ux16, config.fe.TypeUInt32())
+		v3.AddArg(lo)
+		v3.AddArg(s)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpLsh32x16, config.fe.TypeUInt32())
+		v4.AddArg(hi)
+		v5 := b.NewValue0(v.Line, OpSub16, config.fe.TypeUInt16())
+		v6 := b.NewValue0(v.Line, OpConst16, config.fe.TypeUInt16())
+		v6.AuxInt = 32
+		v5.AddArg(v6)
+		v5.AddArg(s)
+		v4.AddArg(v5)
+		v2.AddArg(v4)
+		v1.AddArg(v2)
+		v7 := b.NewValue0(v.Line, OpAnd32, config.fe.TypeUInt32())
+		v8 := b.NewValue0(v.Line, OpRsh32x16, config.fe.TypeUInt32())
+		v8.AddArg(hi)
+		v9 := b.NewValue0(v.Line, OpSub16, config.fe.TypeUInt16())
+		v9.AddArg(s)
+		v10 := b.NewValue0(v.Line, OpConst16, config.fe.TypeUInt16())
+		v10.AuxInt = 32
+		v9.AddArg(v10)
+		v8.AddArg(v9)
+		v7.AddArg(v8)
+		v11 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v12 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v13 := b.NewValue0(v.Line, OpRsh16Ux32, config.fe.TypeUInt16())
+		v13.AddArg(s)
+		v14 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v14.AuxInt = 5
+		v13.AddArg(v14)
+		v12.AddArg(v13)
+		v11.AddArg(v12)
+		v7.AddArg(v11)
+		v1.AddArg(v7)
+		v.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh64x32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x32 (Int64Make hi lo) s)
+	// cond:
+	// result: (Int64Make 		(Rsh32x32 <config.fe.TypeUInt32()> hi s) 		(Or32 <config.fe.TypeUInt32()> 			(Or32 <config.fe.TypeUInt32()> 				(Rsh32Ux32 <config.fe.TypeUInt32()> lo s) 				(Lsh32x32 <config.fe.TypeUInt32()> 					hi 					(Sub32 <config.fe.TypeUInt32()> (Const32 <config.fe.TypeUInt32()> [32]) s))) 			(And32 <config.fe.TypeUInt32()> 				(Rsh32x32 <config.fe.TypeUInt32()> 					hi 					(Sub32 <config.fe.TypeUInt32()> s (Const32 <config.fe.TypeUInt32()> [32]))) 				(Zeromask 					(R [...]
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		s := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpRsh32x32, config.fe.TypeUInt32())
+		v0.AddArg(hi)
+		v0.AddArg(s)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpRsh32Ux32, config.fe.TypeUInt32())
+		v3.AddArg(lo)
+		v3.AddArg(s)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpLsh32x32, config.fe.TypeUInt32())
+		v4.AddArg(hi)
+		v5 := b.NewValue0(v.Line, OpSub32, config.fe.TypeUInt32())
+		v6 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v6.AuxInt = 32
+		v5.AddArg(v6)
+		v5.AddArg(s)
+		v4.AddArg(v5)
+		v2.AddArg(v4)
+		v1.AddArg(v2)
+		v7 := b.NewValue0(v.Line, OpAnd32, config.fe.TypeUInt32())
+		v8 := b.NewValue0(v.Line, OpRsh32x32, config.fe.TypeUInt32())
+		v8.AddArg(hi)
+		v9 := b.NewValue0(v.Line, OpSub32, config.fe.TypeUInt32())
+		v9.AddArg(s)
+		v10 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v10.AuxInt = 32
+		v9.AddArg(v10)
+		v8.AddArg(v9)
+		v7.AddArg(v8)
+		v11 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v12 := b.NewValue0(v.Line, OpRsh32Ux32, config.fe.TypeUInt32())
+		v12.AddArg(s)
+		v13 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v13.AuxInt = 5
+		v12.AddArg(v13)
+		v11.AddArg(v12)
+		v7.AddArg(v11)
+		v1.AddArg(v7)
+		v.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh64x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x64 x (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Int64Make (Signmask (Int64Hi x)) (Signmask (Int64Hi x)))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v.AddArg(v0)
+		v2 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v3 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v3.AddArg(x)
+		v2.AddArg(v3)
+		v.AddArg(v2)
+		return true
+	}
+	// match: (Rsh64x64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Rsh64x32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpRsh64x32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Rsh64x64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Rsh64x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpRsh64x32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh64x8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh64x8 (Int64Make hi lo) s)
+	// cond:
+	// result: (Int64Make 		(Rsh32x8 <config.fe.TypeUInt32()> hi s) 		(Or32 <config.fe.TypeUInt32()> 			(Or32 <config.fe.TypeUInt32()> 				(Rsh32Ux8 <config.fe.TypeUInt32()> lo s) 				(Lsh32x8 <config.fe.TypeUInt32()> 					hi 					(Sub8 <config.fe.TypeUInt8()> (Const8 <config.fe.TypeUInt8()> [32]) s))) 			(And32 <config.fe.TypeUInt32()> 				(Rsh32x8 <config.fe.TypeUInt32()> 					hi 					(Sub8 <config.fe.TypeUInt8()> s (Const8 <config.fe.TypeUInt8()> [32]))) 				(Zeromask 					(ZeroExt8to32  [...]
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		hi := v_0.Args[0]
+		lo := v_0.Args[1]
+		s := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpRsh32x8, config.fe.TypeUInt32())
+		v0.AddArg(hi)
+		v0.AddArg(s)
+		v.AddArg(v0)
+		v1 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v2 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v3 := b.NewValue0(v.Line, OpRsh32Ux8, config.fe.TypeUInt32())
+		v3.AddArg(lo)
+		v3.AddArg(s)
+		v2.AddArg(v3)
+		v4 := b.NewValue0(v.Line, OpLsh32x8, config.fe.TypeUInt32())
+		v4.AddArg(hi)
+		v5 := b.NewValue0(v.Line, OpSub8, config.fe.TypeUInt8())
+		v6 := b.NewValue0(v.Line, OpConst8, config.fe.TypeUInt8())
+		v6.AuxInt = 32
+		v5.AddArg(v6)
+		v5.AddArg(s)
+		v4.AddArg(v5)
+		v2.AddArg(v4)
+		v1.AddArg(v2)
+		v7 := b.NewValue0(v.Line, OpAnd32, config.fe.TypeUInt32())
+		v8 := b.NewValue0(v.Line, OpRsh32x8, config.fe.TypeUInt32())
+		v8.AddArg(hi)
+		v9 := b.NewValue0(v.Line, OpSub8, config.fe.TypeUInt8())
+		v9.AddArg(s)
+		v10 := b.NewValue0(v.Line, OpConst8, config.fe.TypeUInt8())
+		v10.AuxInt = 32
+		v9.AddArg(v10)
+		v8.AddArg(v9)
+		v7.AddArg(v8)
+		v11 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v12 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v13 := b.NewValue0(v.Line, OpRsh8Ux32, config.fe.TypeUInt8())
+		v13.AddArg(s)
+		v14 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v14.AuxInt = 5
+		v13.AddArg(v14)
+		v12.AddArg(v13)
+		v11.AddArg(v12)
+		v7.AddArg(v11)
+		v1.AddArg(v7)
+		v.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh8Ux64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8Ux64 _ (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Const32 [0])
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Rsh8Ux64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Rsh8Ux32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpRsh8Ux32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Rsh8Ux64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Rsh8Ux32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpRsh8Ux32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpRsh8x64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Rsh8x64 x (Int64Make (Const32 [c]) _))
+	// cond: c != 0
+	// result: (Signmask (SignExt8to32 x))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		c := v_1_0.AuxInt
+		if !(c != 0) {
+			break
+		}
+		v.reset(OpSignmask)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+	// match: (Rsh8x64 x (Int64Make (Const32 [0]) lo))
+	// cond:
+	// result: (Rsh8x32 x lo)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		v_1_0 := v_1.Args[0]
+		if v_1_0.Op != OpConst32 {
+			break
+		}
+		if v_1_0.AuxInt != 0 {
+			break
+		}
+		lo := v_1.Args[1]
+		v.reset(OpRsh8x32)
+		v.AddArg(x)
+		v.AddArg(lo)
+		return true
+	}
+	// match: (Rsh8x64 x (Int64Make hi lo))
+	// cond: hi.Op != OpConst32
+	// result: (Rsh8x32 x (Or32 <config.fe.TypeUInt32()> (Zeromask hi) lo))
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		if !(hi.Op != OpConst32) {
+			break
+		}
+		v.reset(OpRsh8x32)
+		v.AddArg(x)
+		v0 := b.NewValue0(v.Line, OpOr32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpZeromask, config.fe.TypeUInt32())
+		v1.AddArg(hi)
+		v0.AddArg(v1)
+		v0.AddArg(lo)
+		v.AddArg(v0)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpSignExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to64 x)
+	// cond:
+	// result: (SignExt32to64 (SignExt16to32 x))
+	for {
+		x := v.Args[0]
+		v.reset(OpSignExt32to64)
+		v0 := b.NewValue0(v.Line, OpSignExt16to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuedec64_OpSignExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt32to64 x)
+	// cond:
+	// result: (Int64Make (Signmask x) x)
+	for {
+		x := v.Args[0]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpSignmask, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuedec64_OpSignExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to64 x)
+	// cond:
+	// result: (SignExt32to64 (SignExt8to32 x))
+	for {
+		x := v.Args[0]
+		v.reset(OpSignExt32to64)
+		v0 := b.NewValue0(v.Line, OpSignExt8to32, config.fe.TypeInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuedec64_OpStore(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Store [8] dst (Int64Make hi lo) mem)
+	// cond: !config.BigEndian
+	// result: (Store [4] 		(OffPtr <hi.Type.PtrTo()> [4] dst) 		hi 		(Store [4] dst lo mem))
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		dst := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		mem := v.Args[2]
+		if !(!config.BigEndian) {
+			break
+		}
+		v.reset(OpStore)
+		v.AuxInt = 4
+		v0 := b.NewValue0(v.Line, OpOffPtr, hi.Type.PtrTo())
+		v0.AuxInt = 4
+		v0.AddArg(dst)
+		v.AddArg(v0)
+		v.AddArg(hi)
+		v1 := b.NewValue0(v.Line, OpStore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(dst)
+		v1.AddArg(lo)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	// match: (Store [8] dst (Int64Make hi lo) mem)
+	// cond: config.BigEndian
+	// result: (Store [4] 		(OffPtr <lo.Type.PtrTo()> [4] dst) 		lo 		(Store [4] dst hi mem))
+	for {
+		if v.AuxInt != 8 {
+			break
+		}
+		dst := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpInt64Make {
+			break
+		}
+		hi := v_1.Args[0]
+		lo := v_1.Args[1]
+		mem := v.Args[2]
+		if !(config.BigEndian) {
+			break
+		}
+		v.reset(OpStore)
+		v.AuxInt = 4
+		v0 := b.NewValue0(v.Line, OpOffPtr, lo.Type.PtrTo())
+		v0.AuxInt = 4
+		v0.AddArg(dst)
+		v.AddArg(v0)
+		v.AddArg(lo)
+		v1 := b.NewValue0(v.Line, OpStore, TypeMem)
+		v1.AuxInt = 4
+		v1.AddArg(dst)
+		v1.AddArg(hi)
+		v1.AddArg(mem)
+		v.AddArg(v1)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpSub64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Sub64 x y)
+	// cond:
+	// result: (Int64Make 		(Sub32withcarry <config.fe.TypeInt32()> 			(Int64Hi x) 			(Int64Hi y) 			(Select1 <TypeFlags> (Sub32carry (Int64Lo x) (Int64Lo y)))) 		(Select0 <config.fe.TypeUInt32()> (Sub32carry (Int64Lo x) (Int64Lo y))))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpSub32withcarry, config.fe.TypeInt32())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v3 := b.NewValue0(v.Line, OpSelect1, TypeFlags)
+		v4 := b.NewValue0(v.Line, OpSub32carry, MakeTuple(config.fe.TypeUInt32(), TypeFlags))
+		v5 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v5.AddArg(x)
+		v4.AddArg(v5)
+		v6 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v6.AddArg(y)
+		v4.AddArg(v6)
+		v3.AddArg(v4)
+		v0.AddArg(v3)
+		v.AddArg(v0)
+		v7 := b.NewValue0(v.Line, OpSelect0, config.fe.TypeUInt32())
+		v8 := b.NewValue0(v.Line, OpSub32carry, MakeTuple(config.fe.TypeUInt32(), TypeFlags))
+		v9 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v9.AddArg(x)
+		v8.AddArg(v9)
+		v10 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v10.AddArg(y)
+		v8.AddArg(v10)
+		v7.AddArg(v8)
+		v.AddArg(v7)
+		return true
+	}
+}
+func rewriteValuedec64_OpTrunc64to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to16 (Int64Make _ lo))
+	// cond:
+	// result: (Trunc32to16 lo)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		lo := v_0.Args[1]
+		v.reset(OpTrunc32to16)
+		v.AddArg(lo)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpTrunc64to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to32 (Int64Make _ lo))
+	// cond:
+	// result: lo
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		lo := v_0.Args[1]
+		v.reset(OpCopy)
+		v.Type = lo.Type
+		v.AddArg(lo)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpTrunc64to8(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Trunc64to8 (Int64Make _ lo))
+	// cond:
+	// result: (Trunc32to8 lo)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpInt64Make {
+			break
+		}
+		lo := v_0.Args[1]
+		v.reset(OpTrunc32to8)
+		v.AddArg(lo)
+		return true
+	}
+	return false
+}
+func rewriteValuedec64_OpXor64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Xor64 x y)
+	// cond:
+	// result: (Int64Make 		(Xor32 <config.fe.TypeUInt32()> (Int64Hi x) (Int64Hi y)) 		(Xor32 <config.fe.TypeUInt32()> (Int64Lo x) (Int64Lo y)))
+	for {
+		x := v.Args[0]
+		y := v.Args[1]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpXor32, config.fe.TypeUInt32())
+		v1 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v1.AddArg(x)
+		v0.AddArg(v1)
+		v2 := b.NewValue0(v.Line, OpInt64Hi, config.fe.TypeUInt32())
+		v2.AddArg(y)
+		v0.AddArg(v2)
+		v.AddArg(v0)
+		v3 := b.NewValue0(v.Line, OpXor32, config.fe.TypeUInt32())
+		v4 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v4.AddArg(x)
+		v3.AddArg(v4)
+		v5 := b.NewValue0(v.Line, OpInt64Lo, config.fe.TypeUInt32())
+		v5.AddArg(y)
+		v3.AddArg(v5)
+		v.AddArg(v3)
+		return true
+	}
+}
+func rewriteValuedec64_OpZeroExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to64 x)
+	// cond:
+	// result: (ZeroExt32to64 (ZeroExt16to32 x))
+	for {
+		x := v.Args[0]
+		v.reset(OpZeroExt32to64)
+		v0 := b.NewValue0(v.Line, OpZeroExt16to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteValuedec64_OpZeroExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt32to64 x)
+	// cond:
+	// result: (Int64Make (Const32 <config.fe.TypeUInt32()> [0]) x)
+	for {
+		x := v.Args[0]
+		v.reset(OpInt64Make)
+		v0 := b.NewValue0(v.Line, OpConst32, config.fe.TypeUInt32())
+		v0.AuxInt = 0
+		v.AddArg(v0)
+		v.AddArg(x)
+		return true
+	}
+}
+func rewriteValuedec64_OpZeroExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to64 x)
+	// cond:
+	// result: (ZeroExt32to64 (ZeroExt8to32 x))
+	for {
+		x := v.Args[0]
+		v.reset(OpZeroExt32to64)
+		v0 := b.NewValue0(v.Line, OpZeroExt8to32, config.fe.TypeUInt32())
+		v0.AddArg(x)
+		v.AddArg(v0)
+		return true
+	}
+}
+func rewriteBlockdec64(b *Block, config *Config) bool {
+	switch b.Kind {
+	}
+	return false
+}
diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go
index 9f08b3c..1a2eacc 100644
--- a/src/cmd/compile/internal/ssa/rewritegeneric.go
+++ b/src/cmd/compile/internal/ssa/rewritegeneric.go
@@ -32,8 +32,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
 		return rewriteValuegeneric_OpAnd8(v, config)
 	case OpArg:
 		return rewriteValuegeneric_OpArg(v, config)
-	case OpArrayIndex:
-		return rewriteValuegeneric_OpArrayIndex(v, config)
+	case OpArraySelect:
+		return rewriteValuegeneric_OpArraySelect(v, config)
 	case OpCom16:
 		return rewriteValuegeneric_OpCom16(v, config)
 	case OpCom32:
@@ -54,8 +54,12 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
 		return rewriteValuegeneric_OpCvt32Fto64F(v, config)
 	case OpCvt64Fto32F:
 		return rewriteValuegeneric_OpCvt64Fto32F(v, config)
+	case OpDiv32F:
+		return rewriteValuegeneric_OpDiv32F(v, config)
 	case OpDiv64:
 		return rewriteValuegeneric_OpDiv64(v, config)
+	case OpDiv64F:
+		return rewriteValuegeneric_OpDiv64F(v, config)
 	case OpDiv64u:
 		return rewriteValuegeneric_OpDiv64u(v, config)
 	case OpEq16:
@@ -106,6 +110,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
 		return rewriteValuegeneric_OpGreater8(v, config)
 	case OpGreater8U:
 		return rewriteValuegeneric_OpGreater8U(v, config)
+	case OpIMake:
+		return rewriteValuegeneric_OpIMake(v, config)
 	case OpIsInBounds:
 		return rewriteValuegeneric_OpIsInBounds(v, config)
 	case OpIsSliceInBounds:
@@ -228,6 +234,10 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
 		return rewriteValuegeneric_OpNeqPtr(v, config)
 	case OpNeqSlice:
 		return rewriteValuegeneric_OpNeqSlice(v, config)
+	case OpNilCheck:
+		return rewriteValuegeneric_OpNilCheck(v, config)
+	case OpNot:
+		return rewriteValuegeneric_OpNot(v, config)
 	case OpOffPtr:
 		return rewriteValuegeneric_OpOffPtr(v, config)
 	case OpOr16:
@@ -306,12 +316,28 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
 		return rewriteValuegeneric_OpRsh8x64(v, config)
 	case OpRsh8x8:
 		return rewriteValuegeneric_OpRsh8x8(v, config)
+	case OpSignExt16to32:
+		return rewriteValuegeneric_OpSignExt16to32(v, config)
+	case OpSignExt16to64:
+		return rewriteValuegeneric_OpSignExt16to64(v, config)
+	case OpSignExt32to64:
+		return rewriteValuegeneric_OpSignExt32to64(v, config)
+	case OpSignExt8to16:
+		return rewriteValuegeneric_OpSignExt8to16(v, config)
+	case OpSignExt8to32:
+		return rewriteValuegeneric_OpSignExt8to32(v, config)
+	case OpSignExt8to64:
+		return rewriteValuegeneric_OpSignExt8to64(v, config)
 	case OpSliceCap:
 		return rewriteValuegeneric_OpSliceCap(v, config)
 	case OpSliceLen:
 		return rewriteValuegeneric_OpSliceLen(v, config)
 	case OpSlicePtr:
 		return rewriteValuegeneric_OpSlicePtr(v, config)
+	case OpSlicemask:
+		return rewriteValuegeneric_OpSlicemask(v, config)
+	case OpSqrt:
+		return rewriteValuegeneric_OpSqrt(v, config)
 	case OpStore:
 		return rewriteValuegeneric_OpStore(v, config)
 	case OpStringLen:
@@ -352,6 +378,20 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
 		return rewriteValuegeneric_OpXor64(v, config)
 	case OpXor8:
 		return rewriteValuegeneric_OpXor8(v, config)
+	case OpZero:
+		return rewriteValuegeneric_OpZero(v, config)
+	case OpZeroExt16to32:
+		return rewriteValuegeneric_OpZeroExt16to32(v, config)
+	case OpZeroExt16to64:
+		return rewriteValuegeneric_OpZeroExt16to64(v, config)
+	case OpZeroExt32to64:
+		return rewriteValuegeneric_OpZeroExt32to64(v, config)
+	case OpZeroExt8to16:
+		return rewriteValuegeneric_OpZeroExt8to16(v, config)
+	case OpZeroExt8to32:
+		return rewriteValuegeneric_OpZeroExt8to32(v, config)
+	case OpZeroExt8to64:
+		return rewriteValuegeneric_OpZeroExt8to64(v, config)
 	}
 	return false
 }
@@ -498,6 +538,40 @@ func rewriteValuegeneric_OpAdd32F(v *Value, config *Config) bool {
 		v.AuxInt = f2i(float64(i2f32(c) + i2f32(d)))
 		return true
 	}
+	// match: (Add32F x (Const32F [0]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst32F {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Add32F (Const32F [0]) x)
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst32F {
+			break
+		}
+		if v_0.AuxInt != 0 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
 	return false
 }
 func rewriteValuegeneric_OpAdd64(v *Value, config *Config) bool {
@@ -582,6 +656,40 @@ func rewriteValuegeneric_OpAdd64F(v *Value, config *Config) bool {
 		v.AuxInt = f2i(i2f(c) + i2f(d))
 		return true
 	}
+	// match: (Add64F x (Const64F [0]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64F {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Add64F (Const64F [0]) x)
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst64F {
+			break
+		}
+		if v_0.AuxInt != 0 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
 	return false
 }
 func rewriteValuegeneric_OpAdd8(v *Value, config *Config) bool {
@@ -661,8 +769,8 @@ func rewriteValuegeneric_OpAddPtr(v *Value, config *Config) bool {
 		c := v_1.AuxInt
 		v.reset(OpOffPtr)
 		v.Type = t
-		v.AddArg(x)
 		v.AuxInt = c
+		v.AddArg(x)
 		return true
 	}
 	return false
@@ -1298,19 +1406,19 @@ func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
 	// cond: v.Type.IsString()
 	// result: (StringMake     (Arg <config.fe.TypeBytePtr()> {n} [off])     (Arg <config.fe.TypeInt()> {n} [off+config.PtrSize]))
 	for {
-		n := v.Aux
 		off := v.AuxInt
+		n := v.Aux
 		if !(v.Type.IsString()) {
 			break
 		}
 		v.reset(OpStringMake)
 		v0 := b.NewValue0(v.Line, OpArg, config.fe.TypeBytePtr())
-		v0.Aux = n
 		v0.AuxInt = off
+		v0.Aux = n
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpArg, config.fe.TypeInt())
-		v1.Aux = n
 		v1.AuxInt = off + config.PtrSize
+		v1.Aux = n
 		v.AddArg(v1)
 		return true
 	}
@@ -1318,23 +1426,23 @@ func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
 	// cond: v.Type.IsSlice()
 	// result: (SliceMake     (Arg <v.Type.ElemType().PtrTo()> {n} [off])     (Arg <config.fe.TypeInt()> {n} [off+config.PtrSize])     (Arg <config.fe.TypeInt()> {n} [off+2*config.PtrSize]))
 	for {
-		n := v.Aux
 		off := v.AuxInt
+		n := v.Aux
 		if !(v.Type.IsSlice()) {
 			break
 		}
 		v.reset(OpSliceMake)
 		v0 := b.NewValue0(v.Line, OpArg, v.Type.ElemType().PtrTo())
-		v0.Aux = n
 		v0.AuxInt = off
+		v0.Aux = n
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpArg, config.fe.TypeInt())
-		v1.Aux = n
 		v1.AuxInt = off + config.PtrSize
+		v1.Aux = n
 		v.AddArg(v1)
 		v2 := b.NewValue0(v.Line, OpArg, config.fe.TypeInt())
-		v2.Aux = n
 		v2.AuxInt = off + 2*config.PtrSize
+		v2.Aux = n
 		v.AddArg(v2)
 		return true
 	}
@@ -1342,19 +1450,19 @@ func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
 	// cond: v.Type.IsInterface()
 	// result: (IMake     (Arg <config.fe.TypeBytePtr()> {n} [off])     (Arg <config.fe.TypeBytePtr()> {n} [off+config.PtrSize]))
 	for {
-		n := v.Aux
 		off := v.AuxInt
+		n := v.Aux
 		if !(v.Type.IsInterface()) {
 			break
 		}
 		v.reset(OpIMake)
 		v0 := b.NewValue0(v.Line, OpArg, config.fe.TypeBytePtr())
-		v0.Aux = n
 		v0.AuxInt = off
+		v0.Aux = n
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpArg, config.fe.TypeBytePtr())
-		v1.Aux = n
 		v1.AuxInt = off + config.PtrSize
+		v1.Aux = n
 		v.AddArg(v1)
 		return true
 	}
@@ -1362,19 +1470,19 @@ func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
 	// cond: v.Type.IsComplex() && v.Type.Size() == 16
 	// result: (ComplexMake     (Arg <config.fe.TypeFloat64()> {n} [off])     (Arg <config.fe.TypeFloat64()> {n} [off+8]))
 	for {
-		n := v.Aux
 		off := v.AuxInt
+		n := v.Aux
 		if !(v.Type.IsComplex() && v.Type.Size() == 16) {
 			break
 		}
 		v.reset(OpComplexMake)
 		v0 := b.NewValue0(v.Line, OpArg, config.fe.TypeFloat64())
-		v0.Aux = n
 		v0.AuxInt = off
+		v0.Aux = n
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpArg, config.fe.TypeFloat64())
-		v1.Aux = n
 		v1.AuxInt = off + 8
+		v1.Aux = n
 		v.AddArg(v1)
 		return true
 	}
@@ -1382,19 +1490,19 @@ func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
 	// cond: v.Type.IsComplex() && v.Type.Size() == 8
 	// result: (ComplexMake     (Arg <config.fe.TypeFloat32()> {n} [off])     (Arg <config.fe.TypeFloat32()> {n} [off+4]))
 	for {
-		n := v.Aux
 		off := v.AuxInt
+		n := v.Aux
 		if !(v.Type.IsComplex() && v.Type.Size() == 8) {
 			break
 		}
 		v.reset(OpComplexMake)
 		v0 := b.NewValue0(v.Line, OpArg, config.fe.TypeFloat32())
-		v0.Aux = n
 		v0.AuxInt = off
+		v0.Aux = n
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpArg, config.fe.TypeFloat32())
-		v1.Aux = n
 		v1.AuxInt = off + 4
+		v1.Aux = n
 		v.AddArg(v1)
 		return true
 	}
@@ -1414,15 +1522,15 @@ func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
 	// result: (StructMake1     (Arg <t.FieldType(0)> {n} [off+t.FieldOff(0)]))
 	for {
 		t := v.Type
-		n := v.Aux
 		off := v.AuxInt
+		n := v.Aux
 		if !(t.IsStruct() && t.NumFields() == 1 && config.fe.CanSSA(t)) {
 			break
 		}
 		v.reset(OpStructMake1)
 		v0 := b.NewValue0(v.Line, OpArg, t.FieldType(0))
-		v0.Aux = n
 		v0.AuxInt = off + t.FieldOff(0)
+		v0.Aux = n
 		v.AddArg(v0)
 		return true
 	}
@@ -1431,19 +1539,19 @@ func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
 	// result: (StructMake2     (Arg <t.FieldType(0)> {n} [off+t.FieldOff(0)])     (Arg <t.FieldType(1)> {n} [off+t.FieldOff(1)]))
 	for {
 		t := v.Type
-		n := v.Aux
 		off := v.AuxInt
+		n := v.Aux
 		if !(t.IsStruct() && t.NumFields() == 2 && config.fe.CanSSA(t)) {
 			break
 		}
 		v.reset(OpStructMake2)
 		v0 := b.NewValue0(v.Line, OpArg, t.FieldType(0))
-		v0.Aux = n
 		v0.AuxInt = off + t.FieldOff(0)
+		v0.Aux = n
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpArg, t.FieldType(1))
-		v1.Aux = n
 		v1.AuxInt = off + t.FieldOff(1)
+		v1.Aux = n
 		v.AddArg(v1)
 		return true
 	}
@@ -1452,23 +1560,23 @@ func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
 	// result: (StructMake3     (Arg <t.FieldType(0)> {n} [off+t.FieldOff(0)])     (Arg <t.FieldType(1)> {n} [off+t.FieldOff(1)])     (Arg <t.FieldType(2)> {n} [off+t.FieldOff(2)]))
 	for {
 		t := v.Type
-		n := v.Aux
 		off := v.AuxInt
+		n := v.Aux
 		if !(t.IsStruct() && t.NumFields() == 3 && config.fe.CanSSA(t)) {
 			break
 		}
 		v.reset(OpStructMake3)
 		v0 := b.NewValue0(v.Line, OpArg, t.FieldType(0))
-		v0.Aux = n
 		v0.AuxInt = off + t.FieldOff(0)
+		v0.Aux = n
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpArg, t.FieldType(1))
-		v1.Aux = n
 		v1.AuxInt = off + t.FieldOff(1)
+		v1.Aux = n
 		v.AddArg(v1)
 		v2 := b.NewValue0(v.Line, OpArg, t.FieldType(2))
-		v2.Aux = n
 		v2.AuxInt = off + t.FieldOff(2)
+		v2.Aux = n
 		v.AddArg(v2)
 		return true
 	}
@@ -1477,55 +1585,109 @@ func rewriteValuegeneric_OpArg(v *Value, config *Config) bool {
 	// result: (StructMake4     (Arg <t.FieldType(0)> {n} [off+t.FieldOff(0)])     (Arg <t.FieldType(1)> {n} [off+t.FieldOff(1)])     (Arg <t.FieldType(2)> {n} [off+t.FieldOff(2)])     (Arg <t.FieldType(3)> {n} [off+t.FieldOff(3)]))
 	for {
 		t := v.Type
-		n := v.Aux
 		off := v.AuxInt
+		n := v.Aux
 		if !(t.IsStruct() && t.NumFields() == 4 && config.fe.CanSSA(t)) {
 			break
 		}
 		v.reset(OpStructMake4)
 		v0 := b.NewValue0(v.Line, OpArg, t.FieldType(0))
-		v0.Aux = n
 		v0.AuxInt = off + t.FieldOff(0)
+		v0.Aux = n
 		v.AddArg(v0)
 		v1 := b.NewValue0(v.Line, OpArg, t.FieldType(1))
-		v1.Aux = n
 		v1.AuxInt = off + t.FieldOff(1)
+		v1.Aux = n
 		v.AddArg(v1)
 		v2 := b.NewValue0(v.Line, OpArg, t.FieldType(2))
-		v2.Aux = n
 		v2.AuxInt = off + t.FieldOff(2)
+		v2.Aux = n
 		v.AddArg(v2)
 		v3 := b.NewValue0(v.Line, OpArg, t.FieldType(3))
-		v3.Aux = n
 		v3.AuxInt = off + t.FieldOff(3)
+		v3.Aux = n
 		v.AddArg(v3)
 		return true
 	}
+	// match: (Arg <t>)
+	// cond: t.IsArray() && t.NumElem() == 0
+	// result: (ArrayMake0)
+	for {
+		t := v.Type
+		if !(t.IsArray() && t.NumElem() == 0) {
+			break
+		}
+		v.reset(OpArrayMake0)
+		return true
+	}
+	// match: (Arg <t> {n} [off])
+	// cond: t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t)
+	// result: (ArrayMake1 (Arg <t.ElemType()> {n} [off]))
+	for {
+		t := v.Type
+		off := v.AuxInt
+		n := v.Aux
+		if !(t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t)) {
+			break
+		}
+		v.reset(OpArrayMake1)
+		v0 := b.NewValue0(v.Line, OpArg, t.ElemType())
+		v0.AuxInt = off
+		v0.Aux = n
+		v.AddArg(v0)
+		return true
+	}
 	return false
 }
-func rewriteValuegeneric_OpArrayIndex(v *Value, config *Config) bool {
+func rewriteValuegeneric_OpArraySelect(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (ArrayIndex <t> [0] x:(Load ptr mem))
+	// match: (ArraySelect (ArrayMake1 x))
 	// cond:
-	// result: @x.Block (Load <t> ptr mem)
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpArrayMake1 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (ArraySelect [0] (Load ptr mem))
+	// cond:
+	// result: (Load ptr mem)
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		v_0 := v.Args[0]
+		if v_0.Op != OpLoad {
+			break
+		}
+		ptr := v_0.Args[0]
+		mem := v_0.Args[1]
+		v.reset(OpLoad)
+		v.AddArg(ptr)
+		v.AddArg(mem)
+		return true
+	}
+	// match: (ArraySelect [0] x:(IData _))
+	// cond:
+	// result: x
 	for {
-		t := v.Type
 		if v.AuxInt != 0 {
 			break
 		}
 		x := v.Args[0]
-		if x.Op != OpLoad {
+		if x.Op != OpIData {
 			break
 		}
-		ptr := x.Args[0]
-		mem := x.Args[1]
-		b = x.Block
-		v0 := b.NewValue0(v.Line, OpLoad, t)
 		v.reset(OpCopy)
-		v.AddArg(v0)
-		v0.AddArg(ptr)
-		v0.AddArg(mem)
+		v.Type = x.Type
+		v.AddArg(x)
 		return true
 	}
 	return false
@@ -1842,6 +2004,44 @@ func rewriteValuegeneric_OpCvt64Fto32F(v *Value, config *Config) bool {
 	}
 	return false
 }
+func rewriteValuegeneric_OpDiv32F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div32F x (Const32F [f2i(1)]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst32F {
+			break
+		}
+		if v_1.AuxInt != f2i(1) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Div32F x (Const32F [f2i(-1)]))
+	// cond:
+	// result: (Neg32F x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst32F {
+			break
+		}
+		if v_1.AuxInt != f2i(-1) {
+			break
+		}
+		v.reset(OpNeg32F)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
 func rewriteValuegeneric_OpDiv64(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
@@ -1997,6 +2197,44 @@ func rewriteValuegeneric_OpDiv64(v *Value, config *Config) bool {
 	}
 	return false
 }
+func rewriteValuegeneric_OpDiv64F(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Div64F x (Const64F [f2i(1)]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64F {
+			break
+		}
+		if v_1.AuxInt != f2i(1) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Div64F x (Const64F [f2i(-1)]))
+	// cond:
+	// result: (Neg32F x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64F {
+			break
+		}
+		if v_1.AuxInt != f2i(-1) {
+			break
+		}
+		v.reset(OpNeg32F)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
 func rewriteValuegeneric_OpDiv64u(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
@@ -2919,22 +3157,57 @@ func rewriteValuegeneric_OpGreater8U(v *Value, config *Config) bool {
 	}
 	return false
 }
-func rewriteValuegeneric_OpIsInBounds(v *Value, config *Config) bool {
+func rewriteValuegeneric_OpIMake(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (IsInBounds (ZeroExt8to32  _) (Const32 [c]))
-	// cond: (1 << 8)  <= c
-	// result: (ConstBool [1])
+	// match: (IMake typ (StructMake1 val))
+	// cond:
+	// result: (IMake typ val)
 	for {
-		v_0 := v.Args[0]
-		if v_0.Op != OpZeroExt8to32 {
-			break
-		}
+		typ := v.Args[0]
 		v_1 := v.Args[1]
-		if v_1.Op != OpConst32 {
+		if v_1.Op != OpStructMake1 {
 			break
 		}
-		c := v_1.AuxInt
+		val := v_1.Args[0]
+		v.reset(OpIMake)
+		v.AddArg(typ)
+		v.AddArg(val)
+		return true
+	}
+	// match: (IMake typ (ArrayMake1 val))
+	// cond:
+	// result: (IMake typ val)
+	for {
+		typ := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpArrayMake1 {
+			break
+		}
+		val := v_1.Args[0]
+		v.reset(OpIMake)
+		v.AddArg(typ)
+		v.AddArg(val)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpIsInBounds(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (IsInBounds (ZeroExt8to32  _) (Const32 [c]))
+	// cond: (1 << 8)  <= c
+	// result: (ConstBool [1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt8to32 {
+			break
+		}
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst32 {
+			break
+		}
+		c := v_1.AuxInt
 		if !((1 << 8) <= c) {
 			break
 		}
@@ -3800,6 +4073,34 @@ func rewriteValuegeneric_OpLoad(v *Value, config *Config) bool {
 		v.AddArg(v5)
 		return true
 	}
+	// match: (Load <t> _ _)
+	// cond: t.IsArray() && t.NumElem() == 0
+	// result: (ArrayMake0)
+	for {
+		t := v.Type
+		if !(t.IsArray() && t.NumElem() == 0) {
+			break
+		}
+		v.reset(OpArrayMake0)
+		return true
+	}
+	// match: (Load <t> ptr mem)
+	// cond: t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t)
+	// result: (ArrayMake1 (Load <t.ElemType()> ptr mem))
+	for {
+		t := v.Type
+		ptr := v.Args[0]
+		mem := v.Args[1]
+		if !(t.IsArray() && t.NumElem() == 1 && config.fe.CanSSA(t)) {
+			break
+		}
+		v.reset(OpArrayMake1)
+		v0 := b.NewValue0(v.Line, OpLoad, t.ElemType())
+		v0.AddArg(ptr)
+		v0.AddArg(mem)
+		v.AddArg(v0)
+		return true
+	}
 	return false
 }
 func rewriteValuegeneric_OpLsh16x16(v *Value, config *Config) bool {
@@ -5122,6 +5423,22 @@ func rewriteValuegeneric_OpMul16(v *Value, config *Config) bool {
 		v.AuxInt = int64(int16(c * d))
 		return true
 	}
+	// match: (Mul16 (Const16 [-1]) x)
+	// cond:
+	// result: (Neg16 x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst16 {
+			break
+		}
+		if v_0.AuxInt != -1 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpNeg16)
+		v.AddArg(x)
+		return true
+	}
 	// match: (Mul16 x (Const16 <t> [c]))
 	// cond: x.Op != OpConst16
 	// result: (Mul16 (Const16 <t> [c]) x)
@@ -5181,6 +5498,22 @@ func rewriteValuegeneric_OpMul32(v *Value, config *Config) bool {
 		v.AuxInt = int64(int32(c * d))
 		return true
 	}
+	// match: (Mul32 (Const32 [-1]) x)
+	// cond:
+	// result: (Neg32 x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst32 {
+			break
+		}
+		if v_0.AuxInt != -1 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpNeg32)
+		v.AddArg(x)
+		return true
+	}
 	// match: (Mul32 x (Const32 <t> [c]))
 	// cond: x.Op != OpConst32
 	// result: (Mul32 (Const32 <t> [c]) x)
@@ -5278,6 +5611,72 @@ func rewriteValuegeneric_OpMul32F(v *Value, config *Config) bool {
 		v.AuxInt = f2i(float64(i2f32(c) * i2f32(d)))
 		return true
 	}
+	// match: (Mul32F x (Const32F [f2i(1)]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst32F {
+			break
+		}
+		if v_1.AuxInt != f2i(1) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Mul32F (Const32F [f2i(1)]) x)
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst32F {
+			break
+		}
+		if v_0.AuxInt != f2i(1) {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Mul32F x (Const32F [f2i(-1)]))
+	// cond:
+	// result: (Neg32F x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst32F {
+			break
+		}
+		if v_1.AuxInt != f2i(-1) {
+			break
+		}
+		v.reset(OpNeg32F)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Mul32F (Const32F [f2i(-1)]) x)
+	// cond:
+	// result: (Neg32F x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst32F {
+			break
+		}
+		if v_0.AuxInt != f2i(-1) {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpNeg32F)
+		v.AddArg(x)
+		return true
+	}
 	return false
 }
 func rewriteValuegeneric_OpMul64(v *Value, config *Config) bool {
@@ -5301,6 +5700,22 @@ func rewriteValuegeneric_OpMul64(v *Value, config *Config) bool {
 		v.AuxInt = c * d
 		return true
 	}
+	// match: (Mul64 (Const64 [-1]) x)
+	// cond:
+	// result: (Neg64 x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst64 {
+			break
+		}
+		if v_0.AuxInt != -1 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpNeg64)
+		v.AddArg(x)
+		return true
+	}
 	// match: (Mul64 x (Const64 <t> [c]))
 	// cond: x.Op != OpConst64
 	// result: (Mul64 (Const64 <t> [c]) x)
@@ -5398,6 +5813,72 @@ func rewriteValuegeneric_OpMul64F(v *Value, config *Config) bool {
 		v.AuxInt = f2i(i2f(c) * i2f(d))
 		return true
 	}
+	// match: (Mul64F x (Const64F [f2i(1)]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64F {
+			break
+		}
+		if v_1.AuxInt != f2i(1) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Mul64F (Const64F [f2i(1)]) x)
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst64F {
+			break
+		}
+		if v_0.AuxInt != f2i(1) {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Mul64F x (Const64F [f2i(-1)]))
+	// cond:
+	// result: (Neg64F x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64F {
+			break
+		}
+		if v_1.AuxInt != f2i(-1) {
+			break
+		}
+		v.reset(OpNeg64F)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Mul64F (Const64F [f2i(-1)]) x)
+	// cond:
+	// result: (Neg64F x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst64F {
+			break
+		}
+		if v_0.AuxInt != f2i(-1) {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpNeg64F)
+		v.AddArg(x)
+		return true
+	}
 	return false
 }
 func rewriteValuegeneric_OpMul8(v *Value, config *Config) bool {
@@ -5421,6 +5902,22 @@ func rewriteValuegeneric_OpMul8(v *Value, config *Config) bool {
 		v.AuxInt = int64(int8(c * d))
 		return true
 	}
+	// match: (Mul8  (Const8  [-1]) x)
+	// cond:
+	// result: (Neg8  x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst8 {
+			break
+		}
+		if v_0.AuxInt != -1 {
+			break
+		}
+		x := v.Args[1]
+		v.reset(OpNeg8)
+		v.AddArg(x)
+		return true
+	}
 	// match: (Mul8  x (Const8  <t> [c]))
 	// cond: x.Op != OpConst8
 	// result: (Mul8  (Const8  <t> [c]) x)
@@ -6008,64 +6505,779 @@ func rewriteValuegeneric_OpNeqSlice(v *Value, config *Config) bool {
 		return true
 	}
 }
-func rewriteValuegeneric_OpOffPtr(v *Value, config *Config) bool {
+func rewriteValuegeneric_OpNilCheck(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (OffPtr (OffPtr p [b]) [a])
+	// match: (NilCheck (GetG mem) mem)
 	// cond:
-	// result: (OffPtr p [a+b])
+	// result: mem
 	for {
 		v_0 := v.Args[0]
-		if v_0.Op != OpOffPtr {
-			break
-		}
-		p := v_0.Args[0]
-		b := v_0.AuxInt
-		a := v.AuxInt
-		v.reset(OpOffPtr)
-		v.AddArg(p)
-		v.AuxInt = a + b
-		return true
-	}
-	// match: (OffPtr p [0])
-	// cond: v.Type.Compare(p.Type) == CMPeq
-	// result: p
-	for {
-		p := v.Args[0]
-		if v.AuxInt != 0 {
+		if v_0.Op != OpGetG {
 			break
 		}
-		if !(v.Type.Compare(p.Type) == CMPeq) {
+		mem := v_0.Args[0]
+		if mem != v.Args[1] {
 			break
 		}
 		v.reset(OpCopy)
-		v.Type = p.Type
-		v.AddArg(p)
+		v.Type = mem.Type
+		v.AddArg(mem)
 		return true
 	}
-	return false
-}
-func rewriteValuegeneric_OpOr16(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (Or16 x (Const16 <t> [c]))
-	// cond: x.Op != OpConst16
-	// result: (Or16 (Const16 <t> [c]) x)
+	// match: (NilCheck (Load (OffPtr [c] (SP)) mem) mem)
+	// cond: mem.Op == OpStaticCall 	&& isSameSym(mem.Aux, "runtime.newobject") 	&& c == config.ctxt.FixedFrameSize() + config.RegSize 	&& warnRule(config.Debug_checknil() && int(v.Line) > 1, v, "removed nil check")
+	// result: (Invalid)
 	for {
-		x := v.Args[0]
-		v_1 := v.Args[1]
-		if v_1.Op != OpConst16 {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLoad {
 			break
 		}
-		t := v_1.Type
-		c := v_1.AuxInt
-		if !(x.Op != OpConst16) {
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpOffPtr {
 			break
 		}
-		v.reset(OpOr16)
-		v0 := b.NewValue0(v.Line, OpConst16, t)
-		v0.AuxInt = c
-		v.AddArg(v0)
+		c := v_0_0.AuxInt
+		v_0_0_0 := v_0_0.Args[0]
+		if v_0_0_0.Op != OpSP {
+			break
+		}
+		mem := v_0.Args[1]
+		if mem != v.Args[1] {
+			break
+		}
+		if !(mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(config.Debug_checknil() && int(v.Line) > 1, v, "removed nil check")) {
+			break
+		}
+		v.reset(OpInvalid)
+		return true
+	}
+	// match: (NilCheck (OffPtr (Load (OffPtr [c] (SP)) mem)) mem)
+	// cond: mem.Op == OpStaticCall 	&& isSameSym(mem.Aux, "runtime.newobject") 	&& c == config.ctxt.FixedFrameSize() + config.RegSize 	&& warnRule(config.Debug_checknil() && int(v.Line) > 1, v, "removed nil check")
+	// result: (Invalid)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpOffPtr {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpLoad {
+			break
+		}
+		v_0_0_0 := v_0_0.Args[0]
+		if v_0_0_0.Op != OpOffPtr {
+			break
+		}
+		c := v_0_0_0.AuxInt
+		v_0_0_0_0 := v_0_0_0.Args[0]
+		if v_0_0_0_0.Op != OpSP {
+			break
+		}
+		mem := v_0_0.Args[1]
+		if mem != v.Args[1] {
+			break
+		}
+		if !(mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(config.Debug_checknil() && int(v.Line) > 1, v, "removed nil check")) {
+			break
+		}
+		v.reset(OpInvalid)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpNot(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Not (Eq64 x y))
+	// cond:
+	// result: (Neq64 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpEq64 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpNeq64)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Eq32 x y))
+	// cond:
+	// result: (Neq32 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpEq32 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpNeq32)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Eq16 x y))
+	// cond:
+	// result: (Neq16 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpEq16 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpNeq16)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Eq8  x y))
+	// cond:
+	// result: (Neq8  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpEq8 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpNeq8)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (EqB  x y))
+	// cond:
+	// result: (NeqB  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpEqB {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpNeqB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Neq64 x y))
+	// cond:
+	// result: (Eq64 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpNeq64 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpEq64)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Neq32 x y))
+	// cond:
+	// result: (Eq32 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpNeq32 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpEq32)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Neq16 x y))
+	// cond:
+	// result: (Eq16 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpNeq16 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpEq16)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Neq8  x y))
+	// cond:
+	// result: (Eq8  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpNeq8 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpEq8)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (NeqB  x y))
+	// cond:
+	// result: (EqB  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpNeqB {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpEqB)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Greater64 x y))
+	// cond:
+	// result: (Leq64 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGreater64 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLeq64)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Greater32 x y))
+	// cond:
+	// result: (Leq32 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGreater32 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLeq32)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Greater16 x y))
+	// cond:
+	// result: (Leq16 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGreater16 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLeq16)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Greater8  x y))
+	// cond:
+	// result: (Leq8  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGreater8 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLeq8)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Greater64U x y))
+	// cond:
+	// result: (Leq64U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGreater64U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLeq64U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Greater32U x y))
+	// cond:
+	// result: (Leq32U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGreater32U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLeq32U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Greater16U x y))
+	// cond:
+	// result: (Leq16U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGreater16U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLeq16U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Greater8U  x y))
+	// cond:
+	// result: (Leq8U  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGreater8U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLeq8U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Geq64 x y))
+	// cond:
+	// result: (Less64 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGeq64 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLess64)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Geq32 x y))
+	// cond:
+	// result: (Less32 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGeq32 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLess32)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Geq16 x y))
+	// cond:
+	// result: (Less16 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGeq16 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLess16)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Geq8  x y))
+	// cond:
+	// result: (Less8  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGeq8 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLess8)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Geq64U x y))
+	// cond:
+	// result: (Less64U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGeq64U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLess64U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Geq32U x y))
+	// cond:
+	// result: (Less32U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGeq32U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLess32U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Geq16U x y))
+	// cond:
+	// result: (Less16U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGeq16U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLess16U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Geq8U  x y))
+	// cond:
+	// result: (Less8U  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpGeq8U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpLess8U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Less64 x y))
+	// cond:
+	// result: (Geq64 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLess64 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGeq64)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Less32 x y))
+	// cond:
+	// result: (Geq32 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLess32 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGeq32)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Less16 x y))
+	// cond:
+	// result: (Geq16 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLess16 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGeq16)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Less8  x y))
+	// cond:
+	// result: (Geq8  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLess8 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGeq8)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Less64U x y))
+	// cond:
+	// result: (Geq64U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLess64U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGeq64U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Less32U x y))
+	// cond:
+	// result: (Geq32U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLess32U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGeq32U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Less16U x y))
+	// cond:
+	// result: (Geq16U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLess16U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGeq16U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Less8U  x y))
+	// cond:
+	// result: (Geq8U  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLess8U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGeq8U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Leq64 x y))
+	// cond:
+	// result: (Greater64 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLeq64 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGreater64)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Leq32 x y))
+	// cond:
+	// result: (Greater32 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLeq32 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGreater32)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Leq16 x y))
+	// cond:
+	// result: (Greater16 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLeq16 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGreater16)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Leq8  x y))
+	// cond:
+	// result: (Greater8 x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLeq8 {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGreater8)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Leq64U x y))
+	// cond:
+	// result: (Greater64U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLeq64U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGreater64U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Leq32U x y))
+	// cond:
+	// result: (Greater32U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLeq32U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGreater32U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Leq16U x y))
+	// cond:
+	// result: (Greater16U x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLeq16U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGreater16U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	// match: (Not (Leq8U  x y))
+	// cond:
+	// result: (Greater8U  x y)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLeq8U {
+			break
+		}
+		x := v_0.Args[0]
+		y := v_0.Args[1]
+		v.reset(OpGreater8U)
+		v.AddArg(x)
+		v.AddArg(y)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpOffPtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (OffPtr (OffPtr p [b]) [a])
+	// cond:
+	// result: (OffPtr p [a+b])
+	for {
+		a := v.AuxInt
+		v_0 := v.Args[0]
+		if v_0.Op != OpOffPtr {
+			break
+		}
+		b := v_0.AuxInt
+		p := v_0.Args[0]
+		v.reset(OpOffPtr)
+		v.AuxInt = a + b
+		v.AddArg(p)
+		return true
+	}
+	// match: (OffPtr p [0])
+	// cond: v.Type.Compare(p.Type) == CMPeq
+	// result: p
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		p := v.Args[0]
+		if !(v.Type.Compare(p.Type) == CMPeq) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = p.Type
+		v.AddArg(p)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpOr16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Or16 x (Const16 <t> [c]))
+	// cond: x.Op != OpConst16
+	// result: (Or16 (Const16 <t> [c]) x)
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst16 {
+			break
+		}
+		t := v_1.Type
+		c := v_1.AuxInt
+		if !(x.Op != OpConst16) {
+			break
+		}
+		v.reset(OpOr16)
+		v0 := b.NewValue0(v.Line, OpConst16, t)
+		v0.AuxInt = c
+		v.AddArg(v0)
 		v.AddArg(x)
 		return true
 	}
@@ -8567,6 +9779,186 @@ func rewriteValuegeneric_OpRsh8x8(v *Value, config *Config) bool {
 	}
 	return false
 }
+func rewriteValuegeneric_OpSignExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to32 (Trunc32to16 x:(Rsh32x64 _ (Const64 [s]))))
+	// cond: s >= 16
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc32to16 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh32x64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 16) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpSignExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt16to64 (Trunc64to16 x:(Rsh64x64 _ (Const64 [s]))))
+	// cond: s >= 48
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc64to16 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh64x64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 48) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpSignExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt32to64 (Trunc64to32 x:(Rsh64x64 _ (Const64 [s]))))
+	// cond: s >= 32
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc64to32 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh64x64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 32) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpSignExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to16  (Trunc16to8  x:(Rsh16x64 _ (Const64 [s]))))
+	// cond: s >= 8
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc16to8 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh16x64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 8) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpSignExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to32  (Trunc32to8  x:(Rsh32x64 _ (Const64 [s]))))
+	// cond: s >= 24
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc32to8 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh32x64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 24) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpSignExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SignExt8to64  (Trunc64to8  x:(Rsh64x64 _ (Const64 [s]))))
+	// cond: s >= 56
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc64to8 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh64x64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 56) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
 func rewriteValuegeneric_OpSliceCap(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
@@ -8589,6 +9981,25 @@ func rewriteValuegeneric_OpSliceCap(v *Value, config *Config) bool {
 		v.AuxInt = c
 		return true
 	}
+	// match: (SliceCap (SliceMake _ _ (Const32 <t> [c])))
+	// cond:
+	// result: (Const32 <t> [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSliceMake {
+			break
+		}
+		v_0_2 := v_0.Args[2]
+		if v_0_2.Op != OpConst32 {
+			break
+		}
+		t := v_0_2.Type
+		c := v_0_2.AuxInt
+		v.reset(OpConst32)
+		v.Type = t
+		v.AuxInt = c
+		return true
+	}
 	// match: (SliceCap (SliceMake _ _ (SliceCap x)))
 	// cond:
 	// result: (SliceCap x)
@@ -8623,67 +10034,171 @@ func rewriteValuegeneric_OpSliceCap(v *Value, config *Config) bool {
 		v.AddArg(x)
 		return true
 	}
-	return false
-}
-func rewriteValuegeneric_OpSliceLen(v *Value, config *Config) bool {
-	b := v.Block
-	_ = b
-	// match: (SliceLen (SliceMake _ (Const64 <t> [c]) _))
-	// cond:
-	// result: (Const64 <t> [c])
+	return false
+}
+func rewriteValuegeneric_OpSliceLen(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SliceLen (SliceMake _ (Const64 <t> [c]) _))
+	// cond:
+	// result: (Const64 <t> [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSliceMake {
+			break
+		}
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpConst64 {
+			break
+		}
+		t := v_0_1.Type
+		c := v_0_1.AuxInt
+		v.reset(OpConst64)
+		v.Type = t
+		v.AuxInt = c
+		return true
+	}
+	// match: (SliceLen (SliceMake _ (Const32 <t> [c]) _))
+	// cond:
+	// result: (Const32 <t> [c])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSliceMake {
+			break
+		}
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpConst32 {
+			break
+		}
+		t := v_0_1.Type
+		c := v_0_1.AuxInt
+		v.reset(OpConst32)
+		v.Type = t
+		v.AuxInt = c
+		return true
+	}
+	// match: (SliceLen (SliceMake _ (SliceLen x) _))
+	// cond:
+	// result: (SliceLen x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSliceMake {
+			break
+		}
+		v_0_1 := v_0.Args[1]
+		if v_0_1.Op != OpSliceLen {
+			break
+		}
+		x := v_0_1.Args[0]
+		v.reset(OpSliceLen)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpSlicePtr(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (SlicePtr (SliceMake (SlicePtr x) _ _))
+	// cond:
+	// result: (SlicePtr x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSliceMake {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpSlicePtr {
+			break
+		}
+		x := v_0_0.Args[0]
+		v.reset(OpSlicePtr)
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpSlicemask(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Slicemask (Const32 [x]))
+	// cond: x > 0
+	// result: (Const32 [-1])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst32 {
+			break
+		}
+		x := v_0.AuxInt
+		if !(x > 0) {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = -1
+		return true
+	}
+	// match: (Slicemask (Const32 [0]))
+	// cond:
+	// result: (Const32 [0])
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpConst32 {
+			break
+		}
+		if v_0.AuxInt != 0 {
+			break
+		}
+		v.reset(OpConst32)
+		v.AuxInt = 0
+		return true
+	}
+	// match: (Slicemask (Const64 [x]))
+	// cond: x > 0
+	// result: (Const64 [-1])
 	for {
 		v_0 := v.Args[0]
-		if v_0.Op != OpSliceMake {
+		if v_0.Op != OpConst64 {
 			break
 		}
-		v_0_1 := v_0.Args[1]
-		if v_0_1.Op != OpConst64 {
+		x := v_0.AuxInt
+		if !(x > 0) {
 			break
 		}
-		t := v_0_1.Type
-		c := v_0_1.AuxInt
 		v.reset(OpConst64)
-		v.Type = t
-		v.AuxInt = c
+		v.AuxInt = -1
 		return true
 	}
-	// match: (SliceLen (SliceMake _ (SliceLen x) _))
+	// match: (Slicemask (Const64 [0]))
 	// cond:
-	// result: (SliceLen x)
+	// result: (Const64 [0])
 	for {
 		v_0 := v.Args[0]
-		if v_0.Op != OpSliceMake {
+		if v_0.Op != OpConst64 {
 			break
 		}
-		v_0_1 := v_0.Args[1]
-		if v_0_1.Op != OpSliceLen {
+		if v_0.AuxInt != 0 {
 			break
 		}
-		x := v_0_1.Args[0]
-		v.reset(OpSliceLen)
-		v.AddArg(x)
+		v.reset(OpConst64)
+		v.AuxInt = 0
 		return true
 	}
 	return false
 }
-func rewriteValuegeneric_OpSlicePtr(v *Value, config *Config) bool {
+func rewriteValuegeneric_OpSqrt(v *Value, config *Config) bool {
 	b := v.Block
 	_ = b
-	// match: (SlicePtr (SliceMake (SlicePtr x) _ _))
+	// match: (Sqrt (Const64F [c]))
 	// cond:
-	// result: (SlicePtr x)
+	// result: (Const64F [f2i(math.Sqrt(i2f(c)))])
 	for {
 		v_0 := v.Args[0]
-		if v_0.Op != OpSliceMake {
-			break
-		}
-		v_0_0 := v_0.Args[0]
-		if v_0_0.Op != OpSlicePtr {
+		if v_0.Op != OpConst64F {
 			break
 		}
-		x := v_0_0.Args[0]
-		v.reset(OpSlicePtr)
-		v.AddArg(x)
+		c := v_0.AuxInt
+		v.reset(OpConst64F)
+		v.AuxInt = f2i(math.Sqrt(i2f(c)))
 		return true
 	}
 	return false
@@ -8837,7 +10352,7 @@ func rewriteValuegeneric_OpStore(v *Value, config *Config) bool {
 	}
 	// match: (Store [size] dst (Load <t> src mem) mem)
 	// cond: !config.fe.CanSSA(t)
-	// result: (Move [size] dst src mem)
+	// result: (Move [MakeSizeAndAlign(size, t.Alignment()).Int64()] dst src mem)
 	for {
 		size := v.AuxInt
 		dst := v.Args[0]
@@ -8855,7 +10370,7 @@ func rewriteValuegeneric_OpStore(v *Value, config *Config) bool {
 			break
 		}
 		v.reset(OpMove)
-		v.AuxInt = size
+		v.AuxInt = MakeSizeAndAlign(size, t.Alignment()).Int64()
 		v.AddArg(dst)
 		v.AddArg(src)
 		v.AddArg(mem)
@@ -8863,7 +10378,7 @@ func rewriteValuegeneric_OpStore(v *Value, config *Config) bool {
 	}
 	// match: (Store [size] dst (Load <t> src mem) (VarDef {x} mem))
 	// cond: !config.fe.CanSSA(t)
-	// result: (Move [size] dst src (VarDef {x} mem))
+	// result: (Move [MakeSizeAndAlign(size, t.Alignment()).Int64()] dst src (VarDef {x} mem))
 	for {
 		size := v.AuxInt
 		dst := v.Args[0]
@@ -8886,7 +10401,7 @@ func rewriteValuegeneric_OpStore(v *Value, config *Config) bool {
 			break
 		}
 		v.reset(OpMove)
-		v.AuxInt = size
+		v.AuxInt = MakeSizeAndAlign(size, t.Alignment()).Int64()
 		v.AddArg(dst)
 		v.AddArg(src)
 		v0 := b.NewValue0(v.Line, OpVarDef, TypeMem)
@@ -8895,6 +10410,39 @@ func rewriteValuegeneric_OpStore(v *Value, config *Config) bool {
 		v.AddArg(v0)
 		return true
 	}
+	// match: (Store _ (ArrayMake0) mem)
+	// cond:
+	// result: mem
+	for {
+		v_1 := v.Args[1]
+		if v_1.Op != OpArrayMake0 {
+			break
+		}
+		mem := v.Args[2]
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	// match: (Store [size] dst (ArrayMake1 e) mem)
+	// cond:
+	// result: (Store [size] dst e mem)
+	for {
+		size := v.AuxInt
+		dst := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpArrayMake1 {
+			break
+		}
+		e := v_1.Args[0]
+		mem := v.Args[2]
+		v.reset(OpStore)
+		v.AuxInt = size
+		v.AddArg(dst)
+		v.AddArg(e)
+		v.AddArg(mem)
+		return true
+	}
 	return false
 }
 func rewriteValuegeneric_OpStringLen(v *Value, config *Config) bool {
@@ -9141,6 +10689,22 @@ func rewriteValuegeneric_OpStructSelect(v *Value, config *Config) bool {
 		v0.AddArg(mem)
 		return true
 	}
+	// match: (StructSelect [0] x:(IData _))
+	// cond:
+	// result: x
+	for {
+		if v.AuxInt != 0 {
+			break
+		}
+		x := v.Args[0]
+		if x.Op != OpIData {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
 	return false
 }
 func rewriteValuegeneric_OpSub16(v *Value, config *Config) bool {
@@ -9348,6 +10912,23 @@ func rewriteValuegeneric_OpSub32F(v *Value, config *Config) bool {
 		v.AuxInt = f2i(float64(i2f32(c) - i2f32(d)))
 		return true
 	}
+	// match: (Sub32F x (Const32F [0]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst32F {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
 	return false
 }
 func rewriteValuegeneric_OpSub64(v *Value, config *Config) bool {
@@ -9463,6 +11044,23 @@ func rewriteValuegeneric_OpSub64F(v *Value, config *Config) bool {
 		v.AuxInt = f2i(i2f(c) - i2f(d))
 		return true
 	}
+	// match: (Sub64F x (Const64F [0]))
+	// cond:
+	// result: x
+	for {
+		x := v.Args[0]
+		v_1 := v.Args[1]
+		if v_1.Op != OpConst64F {
+			break
+		}
+		if v_1.AuxInt != 0 {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
 	return false
 }
 func rewriteValuegeneric_OpSub8(v *Value, config *Config) bool {
@@ -9573,6 +11171,34 @@ func rewriteValuegeneric_OpTrunc16to8(v *Value, config *Config) bool {
 		v.AuxInt = int64(int8(c))
 		return true
 	}
+	// match: (Trunc16to8  (ZeroExt8to16  x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt8to16 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc16to8  (SignExt8to16  x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSignExt8to16 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
 	// match: (Trunc16to8  (And16 (Const16 [y]) x))
 	// cond: y&0xFF == 0xFF
 	// result: (Trunc16to8 x)
@@ -9612,6 +11238,60 @@ func rewriteValuegeneric_OpTrunc32to16(v *Value, config *Config) bool {
 		v.AuxInt = int64(int16(c))
 		return true
 	}
+	// match: (Trunc32to16 (ZeroExt8to32  x))
+	// cond:
+	// result: (ZeroExt8to16  x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt8to32 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpZeroExt8to16)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc32to16 (ZeroExt16to32 x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt16to32 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc32to16 (SignExt8to32  x))
+	// cond:
+	// result: (SignExt8to16  x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSignExt8to32 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpSignExt8to16)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc32to16 (SignExt16to32 x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSignExt16to32 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
 	// match: (Trunc32to16 (And32 (Const32 [y]) x))
 	// cond: y&0xFFFF == 0xFFFF
 	// result: (Trunc32to16 x)
@@ -9651,6 +11331,34 @@ func rewriteValuegeneric_OpTrunc32to8(v *Value, config *Config) bool {
 		v.AuxInt = int64(int8(c))
 		return true
 	}
+	// match: (Trunc32to8  (ZeroExt8to32  x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt8to32 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc32to8  (SignExt8to32  x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSignExt8to32 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
 	// match: (Trunc32to8  (And32 (Const32 [y]) x))
 	// cond: y&0xFF == 0xFF
 	// result: (Trunc32to8 x)
@@ -9685,9 +11393,63 @@ func rewriteValuegeneric_OpTrunc64to16(v *Value, config *Config) bool {
 		if v_0.Op != OpConst64 {
 			break
 		}
-		c := v_0.AuxInt
-		v.reset(OpConst16)
-		v.AuxInt = int64(int16(c))
+		c := v_0.AuxInt
+		v.reset(OpConst16)
+		v.AuxInt = int64(int16(c))
+		return true
+	}
+	// match: (Trunc64to16 (ZeroExt8to64  x))
+	// cond:
+	// result: (ZeroExt8to16  x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt8to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpZeroExt8to16)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc64to16 (ZeroExt16to64 x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt16to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc64to16 (SignExt8to64  x))
+	// cond:
+	// result: (SignExt8to16  x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSignExt8to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpSignExt8to16)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc64to16 (SignExt16to64 x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSignExt16to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
 		return true
 	}
 	// match: (Trunc64to16 (And64 (Const64 [y]) x))
@@ -9729,6 +11491,86 @@ func rewriteValuegeneric_OpTrunc64to32(v *Value, config *Config) bool {
 		v.AuxInt = int64(int32(c))
 		return true
 	}
+	// match: (Trunc64to32 (ZeroExt8to64  x))
+	// cond:
+	// result: (ZeroExt8to32  x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt8to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpZeroExt8to32)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc64to32 (ZeroExt16to64 x))
+	// cond:
+	// result: (ZeroExt16to32 x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt16to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpZeroExt16to32)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc64to32 (ZeroExt32to64 x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt32to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc64to32 (SignExt8to64  x))
+	// cond:
+	// result: (SignExt8to32  x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSignExt8to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpSignExt8to32)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc64to32 (SignExt16to64 x))
+	// cond:
+	// result: (SignExt16to32 x)
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSignExt16to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpSignExt16to32)
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc64to32 (SignExt32to64 x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSignExt32to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
 	// match: (Trunc64to32 (And64 (Const64 [y]) x))
 	// cond: y&0xFFFFFFFF == 0xFFFFFFFF
 	// result: (Trunc64to32 x)
@@ -9768,6 +11610,34 @@ func rewriteValuegeneric_OpTrunc64to8(v *Value, config *Config) bool {
 		v.AuxInt = int64(int8(c))
 		return true
 	}
+	// match: (Trunc64to8  (ZeroExt8to64  x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpZeroExt8to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	// match: (Trunc64to8  (SignExt8to64  x))
+	// cond:
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpSignExt8to64 {
+			break
+		}
+		x := v_0.Args[0]
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
 	// match: (Trunc64to8  (And64 (Const64 [y]) x))
 	// cond: y&0xFF == 0xFF
 	// result: (Trunc64to8 x)
@@ -10299,27 +12169,222 @@ func rewriteValuegeneric_OpXor8(v *Value, config *Config) bool {
 	}
 	return false
 }
-func rewriteBlockgeneric(b *Block) bool {
-	switch b.Kind {
-	case BlockCheck:
-		// match: (Check (NilCheck (GetG _) _) next)
-		// cond:
-		// result: (Plain nil next)
-		for {
-			v := b.Control
-			if v.Op != OpNilCheck {
-				break
-			}
-			v_0 := v.Args[0]
-			if v_0.Op != OpGetG {
-				break
-			}
-			next := b.Succs[0]
-			b.Kind = BlockPlain
-			b.SetControl(nil)
-			_ = next
-			return true
+func rewriteValuegeneric_OpZero(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (Zero (Load (OffPtr [c] (SP)) mem) mem)
+	// cond: mem.Op == OpStaticCall 	&& isSameSym(mem.Aux, "runtime.newobject") 	&& c == config.ctxt.FixedFrameSize() + config.PtrSize
+	// result: mem
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpLoad {
+			break
+		}
+		v_0_0 := v_0.Args[0]
+		if v_0_0.Op != OpOffPtr {
+			break
+		}
+		c := v_0_0.AuxInt
+		v_0_0_0 := v_0_0.Args[0]
+		if v_0_0_0.Op != OpSP {
+			break
+		}
+		mem := v_0.Args[1]
+		if mem != v.Args[1] {
+			break
+		}
+		if !(mem.Op == OpStaticCall && isSameSym(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.PtrSize) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = mem.Type
+		v.AddArg(mem)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpZeroExt16to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to32 (Trunc32to16 x:(Rsh32Ux64 _ (Const64 [s]))))
+	// cond: s >= 16
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc32to16 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh32Ux64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 16) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpZeroExt16to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt16to64 (Trunc64to16 x:(Rsh64Ux64 _ (Const64 [s]))))
+	// cond: s >= 48
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc64to16 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh64Ux64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 48) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpZeroExt32to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt32to64 (Trunc64to32 x:(Rsh64Ux64 _ (Const64 [s]))))
+	// cond: s >= 32
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc64to32 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh64Ux64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 32) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpZeroExt8to16(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to16  (Trunc16to8  x:(Rsh16Ux64 _ (Const64 [s]))))
+	// cond: s >= 8
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc16to8 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh16Ux64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 8) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpZeroExt8to32(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to32  (Trunc32to8  x:(Rsh32Ux64 _ (Const64 [s]))))
+	// cond: s >= 24
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc32to8 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh32Ux64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 24) {
+			break
+		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteValuegeneric_OpZeroExt8to64(v *Value, config *Config) bool {
+	b := v.Block
+	_ = b
+	// match: (ZeroExt8to64  (Trunc64to8  x:(Rsh64Ux64 _ (Const64 [s]))))
+	// cond: s >= 56
+	// result: x
+	for {
+		v_0 := v.Args[0]
+		if v_0.Op != OpTrunc64to8 {
+			break
+		}
+		x := v_0.Args[0]
+		if x.Op != OpRsh64Ux64 {
+			break
+		}
+		x_1 := x.Args[1]
+		if x_1.Op != OpConst64 {
+			break
+		}
+		s := x_1.AuxInt
+		if !(s >= 56) {
+			break
 		}
+		v.reset(OpCopy)
+		v.Type = x.Type
+		v.AddArg(x)
+		return true
+	}
+	return false
+}
+func rewriteBlockgeneric(b *Block, config *Config) bool {
+	switch b.Kind {
 	case BlockIf:
 		// match: (If (Not cond) yes no)
 		// cond:
diff --git a/src/cmd/compile/internal/ssa/schedule.go b/src/cmd/compile/internal/ssa/schedule.go
index 765f0c1..f2a89d8 100644
--- a/src/cmd/compile/internal/ssa/schedule.go
+++ b/src/cmd/compile/internal/ssa/schedule.go
@@ -8,6 +8,8 @@ import "container/heap"
 
 const (
 	ScorePhi = iota // towards top of block
+	ScoreNilCheck
+	ScoreReadTuple
 	ScoreVarDef
 	ScoreMemory
 	ScoreDefault
@@ -83,7 +85,10 @@ func schedule(f *Func) {
 		// Compute score. Larger numbers are scheduled closer to the end of the block.
 		for _, v := range b.Values {
 			switch {
-			case v.Op == OpAMD64LoweredGetClosurePtr:
+			case v.Op == OpAMD64LoweredGetClosurePtr || v.Op == OpPPC64LoweredGetClosurePtr ||
+				v.Op == OpARMLoweredGetClosurePtr || v.Op == OpARM64LoweredGetClosurePtr ||
+				v.Op == Op386LoweredGetClosurePtr || v.Op == OpMIPS64LoweredGetClosurePtr ||
+				v.Op == OpS390XLoweredGetClosurePtr || v.Op == OpMIPSLoweredGetClosurePtr:
 				// We also score GetLoweredClosurePtr as early as possible to ensure that the
 				// context register is not stomped. GetLoweredClosurePtr should only appear
 				// in the entry block where there are no phi functions, so there is no
@@ -92,6 +97,12 @@ func schedule(f *Func) {
 					f.Fatalf("LoweredGetClosurePtr appeared outside of entry block, b=%s", b.String())
 				}
 				score[v.ID] = ScorePhi
+			case v.Op == OpAMD64LoweredNilCheck || v.Op == OpPPC64LoweredNilCheck ||
+				v.Op == OpARMLoweredNilCheck || v.Op == OpARM64LoweredNilCheck ||
+				v.Op == Op386LoweredNilCheck || v.Op == OpMIPS64LoweredNilCheck ||
+				v.Op == OpS390XLoweredNilCheck || v.Op == OpMIPSLoweredNilCheck:
+				// Nil checks must come before loads from the same address.
+				score[v.ID] = ScoreNilCheck
 			case v.Op == OpPhi:
 				// We want all the phis first.
 				score[v.ID] = ScorePhi
@@ -103,7 +114,14 @@ func schedule(f *Func) {
 				// reduce register pressure. It also helps make sure
 				// VARDEF ops are scheduled before the corresponding LEA.
 				score[v.ID] = ScoreMemory
-			case v.Type.IsFlags():
+			case v.Op == OpSelect0 || v.Op == OpSelect1:
+				// Schedule the pseudo-op of reading part of a tuple
+				// immediately after the tuple-generating op, since
+				// this value is already live. This also removes its
+				// false dependency on the other part of the tuple.
+				// Also ensures tuple is never spilled.
+				score[v.ID] = ScoreReadTuple
+			case v.Type.IsFlags() || v.Type.IsTuple():
 				// Schedule flag register generation as late as possible.
 				// This makes sure that we only have one live flags
 				// value at a time.
@@ -120,9 +138,13 @@ func schedule(f *Func) {
 		// the calculated store chain is good only for this block.
 		for _, v := range b.Values {
 			if v.Op != OpPhi && v.Type.IsMemory() {
+				mem := v
+				if v.Op == OpSelect1 {
+					v = v.Args[0]
+				}
 				for _, w := range v.Args {
 					if w.Type.IsMemory() {
-						nextMem[w.ID] = v
+						nextMem[w.ID] = mem
 					}
 				}
 			}
@@ -188,6 +210,7 @@ func schedule(f *Func) {
 
 		// Schedule highest priority value, update use counts, repeat.
 		order = order[:0]
+		tuples := make(map[ID][]*Value)
 		for {
 			// Find highest priority schedulable value.
 			// Note that schedule is assembled backwards.
@@ -199,7 +222,31 @@ func schedule(f *Func) {
 			v := heap.Pop(priq).(*Value)
 
 			// Add it to the schedule.
-			order = append(order, v)
+			// Do not emit tuple-reading ops until we're ready to emit the tuple-generating op.
+			//TODO: maybe remove ReadTuple score above, if it does not help on performance
+			switch {
+			case v.Op == OpSelect0:
+				if tuples[v.Args[0].ID] == nil {
+					tuples[v.Args[0].ID] = make([]*Value, 2)
+				}
+				tuples[v.Args[0].ID][0] = v
+			case v.Op == OpSelect1:
+				if tuples[v.Args[0].ID] == nil {
+					tuples[v.Args[0].ID] = make([]*Value, 2)
+				}
+				tuples[v.Args[0].ID][1] = v
+			case v.Type.IsTuple() && tuples[v.ID] != nil:
+				if tuples[v.ID][1] != nil {
+					order = append(order, tuples[v.ID][1])
+				}
+				if tuples[v.ID][0] != nil {
+					order = append(order, tuples[v.ID][0])
+				}
+				delete(tuples, v.ID)
+				fallthrough
+			default:
+				order = append(order, v)
+			}
 
 			// Update use counts of arguments.
 			for _, w := range v.Args {
diff --git a/src/cmd/compile/internal/ssa/sparsemap.go b/src/cmd/compile/internal/ssa/sparsemap.go
index afb9f60..70c4f61 100644
--- a/src/cmd/compile/internal/ssa/sparsemap.go
+++ b/src/cmd/compile/internal/ssa/sparsemap.go
@@ -10,6 +10,7 @@ package ssa
 type sparseEntry struct {
 	key ID
 	val int32
+	aux int32
 }
 
 type sparseMap struct {
@@ -42,13 +43,14 @@ func (s *sparseMap) get(k ID) int32 {
 	return -1
 }
 
-func (s *sparseMap) set(k ID, v int32) {
+func (s *sparseMap) set(k ID, v, a int32) {
 	i := s.sparse[k]
 	if i < int32(len(s.dense)) && s.dense[i].key == k {
 		s.dense[i].val = v
+		s.dense[i].aux = a
 		return
 	}
-	s.dense = append(s.dense, sparseEntry{k, v})
+	s.dense = append(s.dense, sparseEntry{k, v, a})
 	s.sparse[k] = int32(len(s.dense)) - 1
 }
 
@@ -62,7 +64,7 @@ func (s *sparseMap) setBit(k ID, v uint) {
 		s.dense[i].val |= 1 << v
 		return
 	}
-	s.dense = append(s.dense, sparseEntry{k, 1 << v})
+	s.dense = append(s.dense, sparseEntry{k, 1 << v, 0})
 	s.sparse[k] = int32(len(s.dense)) - 1
 }
 
diff --git a/src/cmd/compile/internal/ssa/sparsetreemap.go b/src/cmd/compile/internal/ssa/sparsetreemap.go
index 3e6f296..d264675 100644
--- a/src/cmd/compile/internal/ssa/sparsetreemap.go
+++ b/src/cmd/compile/internal/ssa/sparsetreemap.go
@@ -57,7 +57,7 @@ type SparseTreeHelper struct {
 // NewSparseTreeHelper returns a SparseTreeHelper for use
 // in the gc package, for example in phi-function placement.
 func NewSparseTreeHelper(f *Func) *SparseTreeHelper {
-	dom := dominators(f)
+	dom := f.Idom()
 	ponums := make([]int32, f.NumBlocks())
 	po := postorderWithNumbering(f, ponums)
 	return makeSparseTreeHelper(newSparseTree(f, dom), dom, po, ponums)
diff --git a/src/cmd/compile/internal/ssa/stackalloc.go b/src/cmd/compile/internal/ssa/stackalloc.go
index 83f65d0..dc2fd7d 100644
--- a/src/cmd/compile/internal/ssa/stackalloc.go
+++ b/src/cmd/compile/internal/ssa/stackalloc.go
@@ -273,7 +273,7 @@ func (s *stackAllocState) computeLive(spillLive [][]ID) {
 	// Instead of iterating over f.Blocks, iterate over their postordering.
 	// Liveness information flows backward, so starting at the end
 	// increases the probability that we will stabilize quickly.
-	po := postorder(s.f)
+	po := s.f.postorder()
 	for {
 		changed := false
 		for _, b := range po {
diff --git a/src/cmd/compile/internal/ssa/stackframe.go b/src/cmd/compile/internal/ssa/stackframe.go
new file mode 100644
index 0000000..de32c60
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/stackframe.go
@@ -0,0 +1,10 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssa
+
+// stackframe calls back into the frontend to assign frame offsets.
+func stackframe(f *Func) {
+	f.Config.fe.AllocFrame(f)
+}
diff --git a/src/cmd/compile/internal/ssa/tighten.go b/src/cmd/compile/internal/ssa/tighten.go
index 2f7c309..6f19263 100644
--- a/src/cmd/compile/internal/ssa/tighten.go
+++ b/src/cmd/compile/internal/ssa/tighten.go
@@ -7,81 +7,135 @@ package ssa
 // tighten moves Values closer to the Blocks in which they are used.
 // This can reduce the amount of register spilling required,
 // if it doesn't also create more live values.
-// For now, it handles only the trivial case in which a
-// Value with one or fewer args is only used in a single Block,
-// and not in a phi value.
-// TODO: Do something smarter.
 // A Value can be moved to any block that
 // dominates all blocks in which it is used.
-// Figure out when that will be an improvement.
 func tighten(f *Func) {
-	// For each value, the number of blocks in which it is used.
-	uses := make([]int32, f.NumValues())
+	canMove := make([]bool, f.NumValues())
+	for _, b := range f.Blocks {
+		for _, v := range b.Values {
+			switch v.Op {
+			case OpPhi, OpGetClosurePtr, OpArg, OpSelect0, OpSelect1:
+				// Phis need to stay in their block.
+				// GetClosurePtr & Arg must stay in the entry block.
+				// Tuple selectors must stay with the tuple generator.
+				continue
+			}
+			if len(v.Args) > 0 && v.Args[len(v.Args)-1].Type.IsMemory() {
+				// We can't move values which have a memory arg - it might
+				// make two memory values live across a block boundary.
+				continue
+			}
+			// Count arguments which will need a register.
+			narg := 0
+			for _, a := range v.Args {
+				switch a.Op {
+				case OpConst8, OpConst16, OpConst32, OpConst64, OpAddr:
+					// Probably foldable into v, don't count as an argument needing a register.
+					// TODO: move tighten to a machine-dependent phase and use v.rematerializeable()?
+				default:
+					narg++
+				}
+			}
+			if narg >= 2 && !v.Type.IsBoolean() {
+				// Don't move values with more than one input, as that may
+				// increase register pressure.
+				// We make an exception for boolean-typed values, as they will
+				// likely be converted to flags, and we want flag generators
+				// moved next to uses (because we only have 1 flag register).
+				continue
+			}
+			canMove[v.ID] = true
+		}
+	}
 
-	// For each value, whether that value is ever an arg to a phi value.
-	phi := make([]bool, f.NumValues())
+	// Build data structure for fast least-common-ancestor queries.
+	lca := makeLCArange(f)
 
-	// For each value, one block in which that value is used.
-	home := make([]*Block, f.NumValues())
+	// For each moveable value, record the block that dominates all uses found so far.
+	target := make([]*Block, f.NumValues())
+
+	// Grab loop information.
+	// We use this to make sure we don't tighten a value into a (deeper) loop.
+	idom := f.Idom()
+	loops := f.loopnest()
+	loops.calculateDepths()
 
 	changed := true
 	for changed {
 		changed = false
 
-		// Reset uses
-		for i := range uses {
-			uses[i] = 0
+		// Reset target
+		for i := range target {
+			target[i] = nil
 		}
-		// No need to reset home; any relevant values will be written anew anyway.
-		// No need to reset phi; once used in a phi, always used in a phi.
 
+		// Compute target locations (for moveable values only).
+		// target location = the least common ancestor of all uses in the dominator tree.
 		for _, b := range f.Blocks {
 			for _, v := range b.Values {
-				for _, w := range v.Args {
+				for i, a := range v.Args {
+					if !canMove[a.ID] {
+						continue
+					}
+					use := b
 					if v.Op == OpPhi {
-						phi[w.ID] = true
+						use = b.Preds[i].b
+					}
+					if target[a.ID] == nil {
+						target[a.ID] = use
+					} else {
+						target[a.ID] = lca.find(target[a.ID], use)
 					}
-					uses[w.ID]++
-					home[w.ID] = b
 				}
 			}
-			if b.Control != nil {
-				uses[b.Control.ID]++
-				home[b.Control.ID] = b
+			if c := b.Control; c != nil {
+				if !canMove[c.ID] {
+					continue
+				}
+				if target[c.ID] == nil {
+					target[c.ID] = b
+				} else {
+					target[c.ID] = lca.find(target[c.ID], b)
+				}
 			}
 		}
 
+		// If the target location is inside a loop,
+		// move the target location up to just before the loop head.
 		for _, b := range f.Blocks {
-			for i := 0; i < len(b.Values); i++ {
-				v := b.Values[i]
-				if v.Op == OpPhi || v.Op == OpGetClosurePtr || v.Op == OpConvert || v.Op == OpArg {
-					// GetClosurePtr & Arg must stay in entry block.
-					// OpConvert must not float over call sites.
-					// TODO do we instead need a dependence edge of some sort for OpConvert?
-					// Would memory do the trick, or do we need something else that relates
-					// to safe point operations?
+			origloop := loops.b2l[b.ID]
+			for _, v := range b.Values {
+				t := target[v.ID]
+				if t == nil {
 					continue
 				}
-				if len(v.Args) > 0 && v.Args[len(v.Args)-1].Type.IsMemory() {
-					// We can't move values which have a memory arg - it might
-					// make two memory values live across a block boundary.
-					continue
+				targetloop := loops.b2l[t.ID]
+				for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) {
+					t = idom[targetloop.header.ID]
+					target[v.ID] = t
+					targetloop = loops.b2l[t.ID]
 				}
-				if uses[v.ID] == 1 && !phi[v.ID] && home[v.ID] != b && len(v.Args) < 2 {
-					// v is used in exactly one block, and it is not b.
-					// Furthermore, it takes at most one input,
-					// so moving it will not increase the
-					// number of live values anywhere.
-					// Move v to that block.
-					c := home[v.ID]
-					c.Values = append(c.Values, v)
-					v.Block = c
-					last := len(b.Values) - 1
-					b.Values[i] = b.Values[last]
-					b.Values[last] = nil
-					b.Values = b.Values[:last]
-					changed = true
+			}
+		}
+
+		// Move values to target locations.
+		for _, b := range f.Blocks {
+			for i := 0; i < len(b.Values); i++ {
+				v := b.Values[i]
+				t := target[v.ID]
+				if t == nil || t == b {
+					// v is not moveable, or is already in correct place.
+					continue
 				}
+				// Move v to the block which dominates its uses.
+				t.Values = append(t.Values, v)
+				v.Block = t
+				last := len(b.Values) - 1
+				b.Values[i] = b.Values[last]
+				b.Values[last] = nil
+				b.Values = b.Values[:last]
+				changed = true
+				i--
 			}
 		}
 	}
diff --git a/src/cmd/compile/internal/ssa/trim.go b/src/cmd/compile/internal/ssa/trim.go
index 8ffb459..09e80bd 100644
--- a/src/cmd/compile/internal/ssa/trim.go
+++ b/src/cmd/compile/internal/ssa/trim.go
@@ -9,24 +9,129 @@ package ssa
 func trim(f *Func) {
 	n := 0
 	for _, b := range f.Blocks {
-		if b.Kind != BlockPlain || len(b.Values) != 0 || len(b.Preds) != 1 {
+		if !trimmableBlock(b) {
 			f.Blocks[n] = b
 			n++
 			continue
 		}
-		// TODO: handle len(b.Preds)>1 case.
 
-		// Splice b out of the graph.
-		p := b.Preds[0].b
-		i := b.Preds[0].i
-		s := b.Succs[0].b
-		j := b.Succs[0].i
+		// Splice b out of the graph. NOTE: `mergePhi` depends on the
+		// order, in which the predecessors edges are merged here.
+		p, i := b.Preds[0].b, b.Preds[0].i
+		s, j := b.Succs[0].b, b.Succs[0].i
+		ns := len(s.Preds)
 		p.Succs[i] = Edge{s, j}
 		s.Preds[j] = Edge{p, i}
+
+		for _, e := range b.Preds[1:] {
+			p, i := e.b, e.i
+			p.Succs[i] = Edge{s, len(s.Preds)}
+			s.Preds = append(s.Preds, Edge{p, i})
+		}
+
+		// If `s` had more than one predecessor, update its phi-ops to
+		// account for the merge.
+		if ns > 1 {
+			for _, v := range s.Values {
+				if v.Op == OpPhi {
+					mergePhi(v, j, b)
+				}
+			}
+			// Remove the phi-ops from `b` if they were merged into the
+			// phi-ops of `s`.
+			k := 0
+			for _, v := range b.Values {
+				if v.Op == OpPhi {
+					if v.Uses == 0 {
+						v.resetArgs()
+						continue
+					}
+					// Pad the arguments of the remaining phi-ops, so
+					// they match the new predecessor count of `s`.
+					for len(v.Args) < len(s.Preds) {
+						v.AddArg(v.Args[0])
+					}
+				}
+				b.Values[k] = v
+				k++
+			}
+			b.Values = b.Values[:k]
+		}
+
+		// Merge the blocks' values.
+		for _, v := range b.Values {
+			v.Block = s
+		}
+		k := len(b.Values)
+		m := len(s.Values)
+		for i := 0; i < k; i++ {
+			s.Values = append(s.Values, nil)
+		}
+		copy(s.Values[k:], s.Values[:m])
+		copy(s.Values, b.Values)
+	}
+	if n < len(f.Blocks) {
+		f.invalidateCFG()
+		tail := f.Blocks[n:]
+		for i := range tail {
+			tail[i] = nil
+		}
+		f.Blocks = f.Blocks[:n]
+	}
+}
+
+// emptyBlock returns true if the block does not contain actual
+// instructions
+func emptyBlock(b *Block) bool {
+	for _, v := range b.Values {
+		if v.Op != OpPhi {
+			return false
+		}
+	}
+	return true
+}
+
+// trimmableBlock returns true if the block can be trimmed from the CFG,
+// subject to the following criteria:
+//  - it should not be the first block
+//  - it should be BlockPlain
+//  - it should not loop back to itself
+//  - it either is the single predecessor of the successor block or
+//    contains no actual instructions
+func trimmableBlock(b *Block) bool {
+	if b.Kind != BlockPlain || b == b.Func.Entry {
+		return false
 	}
-	tail := f.Blocks[n:]
-	for i := range tail {
-		tail[i] = nil
+	s := b.Succs[0].b
+	return s != b && (len(s.Preds) == 1 || emptyBlock(b))
+}
+
+// mergePhi adjusts the number of `v`s arguments to account for merge
+// of `b`, which was `i`th predecessor of the `v`s block. Returns
+// `v`.
+func mergePhi(v *Value, i int, b *Block) *Value {
+	u := v.Args[i]
+	if u.Block == b {
+		if u.Op != OpPhi {
+			b.Func.Fatalf("value %s is not a phi operation", u.LongString())
+		}
+		// If the original block contained u = φ(u0, u1, ..., un) and
+		// the current phi is
+		//    v = φ(v0, v1, ..., u, ..., vk)
+		// then the merged phi is
+		//    v = φ(v0, v1, ..., u0, ..., vk, u1, ..., un)
+		v.SetArg(i, u.Args[0])
+		v.AddArgs(u.Args[1:]...)
+	} else {
+		// If the original block contained u = φ(u0, u1, ..., un) and
+		// the current phi is
+		//    v = φ(v0, v1, ...,  vi, ..., vk)
+		// i.e. it does not use a value from the predecessor block,
+		// then the merged phi is
+		//    v = φ(v0, v1, ..., vk, vi, vi, ...)
+		for j := 1; j < len(b.Preds); j++ {
+			v.AddArg(v.Args[i])
+		}
 	}
-	f.Blocks = f.Blocks[:n]
+	return v
 }
diff --git a/src/cmd/compile/internal/ssa/type.go b/src/cmd/compile/internal/ssa/type.go
index 91a4efe..3ebee6a 100644
--- a/src/cmd/compile/internal/ssa/type.go
+++ b/src/cmd/compile/internal/ssa/type.go
@@ -27,12 +27,13 @@ type Type interface {
 	IsMemory() bool // special ssa-package-only types
 	IsFlags() bool
 	IsVoid() bool
+	IsTuple() bool
 
 	ElemType() Type // given []T or *T or [n]T, return T
 	PtrTo() Type    // given T, return *T
 
 	NumFields() int         // # of fields of a struct
-	FieldType(i int) Type   // type of ith field of the struct
+	FieldType(i int) Type   // type of ith field of the struct or ith part of a tuple
 	FieldOff(i int) int64   // offset of ith field of the struct
 	FieldName(i int) string // name of ith field of the struct
 
@@ -69,6 +70,7 @@ func (t *CompilerType) IsInterface() bool      { return false }
 func (t *CompilerType) IsMemory() bool         { return t.Memory }
 func (t *CompilerType) IsFlags() bool          { return t.Flags }
 func (t *CompilerType) IsVoid() bool           { return t.Void }
+func (t *CompilerType) IsTuple() bool          { return false }
 func (t *CompilerType) String() string         { return t.Name }
 func (t *CompilerType) SimpleString() string   { return t.Name }
 func (t *CompilerType) ElemType() Type         { panic("not implemented") }
@@ -79,6 +81,48 @@ func (t *CompilerType) FieldOff(i int) int64   { panic("not implemented") }
 func (t *CompilerType) FieldName(i int) string { panic("not implemented") }
 func (t *CompilerType) NumElem() int64         { panic("not implemented") }
 
+type TupleType struct {
+	first  Type
+	second Type
+	// Any tuple with a memory type must put that memory type second.
+}
+
+func (t *TupleType) Size() int64          { panic("not implemented") }
+func (t *TupleType) Alignment() int64     { panic("not implemented") }
+func (t *TupleType) IsBoolean() bool      { return false }
+func (t *TupleType) IsInteger() bool      { return false }
+func (t *TupleType) IsSigned() bool       { return false }
+func (t *TupleType) IsFloat() bool        { return false }
+func (t *TupleType) IsComplex() bool      { return false }
+func (t *TupleType) IsPtrShaped() bool    { return false }
+func (t *TupleType) IsString() bool       { return false }
+func (t *TupleType) IsSlice() bool        { return false }
+func (t *TupleType) IsArray() bool        { return false }
+func (t *TupleType) IsStruct() bool       { return false }
+func (t *TupleType) IsInterface() bool    { return false }
+func (t *TupleType) IsMemory() bool       { return false }
+func (t *TupleType) IsFlags() bool        { return false }
+func (t *TupleType) IsVoid() bool         { return false }
+func (t *TupleType) IsTuple() bool        { return true }
+func (t *TupleType) String() string       { return t.first.String() + "," + t.second.String() }
+func (t *TupleType) SimpleString() string { return "Tuple" }
+func (t *TupleType) ElemType() Type       { panic("not implemented") }
+func (t *TupleType) PtrTo() Type          { panic("not implemented") }
+func (t *TupleType) NumFields() int       { panic("not implemented") }
+func (t *TupleType) FieldType(i int) Type {
+	switch i {
+	case 0:
+		return t.first
+	case 1:
+		return t.second
+	default:
+		panic("bad tuple index")
+	}
+}
+func (t *TupleType) FieldOff(i int) int64   { panic("not implemented") }
+func (t *TupleType) FieldName(i int) string { panic("not implemented") }
+func (t *TupleType) NumElem() int64         { panic("not implemented") }
+
 // Cmp is a comparison between values a and b.
 // -1 if a < b
 //  0 if a == b
@@ -116,6 +160,25 @@ func (t *CompilerType) Compare(u Type) Cmp {
 	return CMPlt
 }
 
+func (t *TupleType) Compare(u Type) Cmp {
+	// ssa.TupleType is greater than ssa.CompilerType
+	if _, ok := u.(*CompilerType); ok {
+		return CMPgt
+	}
+	// ssa.TupleType is smaller than any other type
+	x, ok := u.(*TupleType)
+	if !ok {
+		return CMPlt
+	}
+	if t == x {
+		return CMPeq
+	}
+	if c := t.first.Compare(x.first); c != CMPeq {
+		return c
+	}
+	return t.second.Compare(x.second)
+}
+
 var (
 	TypeInvalid = &CompilerType{Name: "invalid"}
 	TypeMem     = &CompilerType{Name: "mem", Memory: true}
@@ -123,3 +186,7 @@ var (
 	TypeVoid    = &CompilerType{Name: "void", Void: true}
 	TypeInt128  = &CompilerType{Name: "int128", size: 16, Int128: true}
 )
+
+func MakeTuple(t0, t1 Type) *TupleType {
+	return &TupleType{first: t0, second: t1}
+}
diff --git a/src/cmd/compile/internal/ssa/type_test.go b/src/cmd/compile/internal/ssa/type_test.go
index 3b1a892..a76a065 100644
--- a/src/cmd/compile/internal/ssa/type_test.go
+++ b/src/cmd/compile/internal/ssa/type_test.go
@@ -39,6 +39,7 @@ func (t *TypeImpl) IsStruct() bool         { return t.struct_ }
 func (t *TypeImpl) IsInterface() bool      { return t.inter }
 func (t *TypeImpl) IsMemory() bool         { return false }
 func (t *TypeImpl) IsFlags() bool          { return false }
+func (t *TypeImpl) IsTuple() bool          { return false }
 func (t *TypeImpl) IsVoid() bool           { return false }
 func (t *TypeImpl) String() string         { return t.Name }
 func (t *TypeImpl) SimpleString() string   { return t.Name }
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index 867221b..489ed35 100644
--- a/src/cmd/compile/internal/ssa/value.go
+++ b/src/cmd/compile/internal/ssa/value.go
@@ -26,6 +26,7 @@ type Value struct {
 
 	// Auxiliary info for this value. The type of this information depends on the opcode and type.
 	// AuxInt is used for integer values, Aux is used for other values.
+	// Floats are stored in AuxInt using math.Float64bits(f).
 	AuxInt int64
 	Aux    interface{}
 
@@ -96,7 +97,7 @@ func (v *Value) AuxValAndOff() ValAndOff {
 
 // long form print.  v# = opcode <type> [aux] args [: reg]
 func (v *Value) LongString() string {
-	s := fmt.Sprintf("v%d = %s", v.ID, v.Op.String())
+	s := fmt.Sprintf("v%d = %s", v.ID, v.Op)
 	s += " <" + v.Type.String() + ">"
 	s += v.auxString()
 	for _, a := range v.Args {
@@ -125,18 +126,20 @@ func (v *Value) auxString() string {
 		return fmt.Sprintf(" [%d]", v.AuxInt32())
 	case auxInt64, auxInt128:
 		return fmt.Sprintf(" [%d]", v.AuxInt)
+	case auxSizeAndAlign:
+		return fmt.Sprintf(" [%s]", SizeAndAlign(v.AuxInt))
 	case auxFloat32, auxFloat64:
 		return fmt.Sprintf(" [%g]", v.AuxFloat())
 	case auxString:
 		return fmt.Sprintf(" {%q}", v.Aux)
 	case auxSym:
 		if v.Aux != nil {
-			return fmt.Sprintf(" {%s}", v.Aux)
+			return fmt.Sprintf(" {%v}", v.Aux)
 		}
 	case auxSymOff, auxSymInt32:
 		s := ""
 		if v.Aux != nil {
-			s = fmt.Sprintf(" {%s}", v.Aux)
+			s = fmt.Sprintf(" {%v}", v.Aux)
 		}
 		if v.AuxInt != 0 {
 			s += fmt.Sprintf(" [%v]", v.AuxInt)
@@ -145,9 +148,15 @@ func (v *Value) auxString() string {
 	case auxSymValAndOff:
 		s := ""
 		if v.Aux != nil {
-			s = fmt.Sprintf(" {%s}", v.Aux)
+			s = fmt.Sprintf(" {%v}", v.Aux)
 		}
 		return s + fmt.Sprintf(" [%s]", v.AuxValAndOff())
+	case auxSymSizeAndAlign:
+		s := ""
+		if v.Aux != nil {
+			s = fmt.Sprintf(" {%v}", v.Aux)
+		}
+		return s + fmt.Sprintf(" [%s]", SizeAndAlign(v.AuxInt))
 	}
 	return ""
 }
@@ -225,9 +234,6 @@ func (v *Value) Log() bool                            { return v.Block.Log() }
 func (v *Value) Fatalf(msg string, args ...interface{}) {
 	v.Block.Func.Config.Fatalf(v.Line, msg, args...)
 }
-func (v *Value) Unimplementedf(msg string, args ...interface{}) {
-	v.Block.Func.Config.Unimplementedf(v.Line, msg, args...)
-}
 
 // isGenericIntConst returns whether v is a generic integer constant.
 func (v *Value) isGenericIntConst() bool {
@@ -268,3 +274,38 @@ func (s *ArgSymbol) String() string {
 func (s *AutoSymbol) String() string {
 	return s.Node.String()
 }
+
+// Reg returns the register assigned to v, in cmd/internal/obj/$ARCH numbering.
+func (v *Value) Reg() int16 {
+	reg := v.Block.Func.RegAlloc[v.ID]
+	if reg == nil {
+		v.Fatalf("nil register for value: %s\n%s\n", v.LongString(), v.Block.Func)
+	}
+	return reg.(*Register).objNum
+}
+
+// Reg0 returns the register assigned to the first output of v, in cmd/internal/obj/$ARCH numbering.
+func (v *Value) Reg0() int16 {
+	reg := v.Block.Func.RegAlloc[v.ID].(LocPair)[0]
+	if reg == nil {
+		v.Fatalf("nil first register for value: %s\n%s\n", v.LongString(), v.Block.Func)
+	}
+	return reg.(*Register).objNum
+}
+
+// Reg1 returns the register assigned to the second output of v, in cmd/internal/obj/$ARCH numbering.
+func (v *Value) Reg1() int16 {
+	reg := v.Block.Func.RegAlloc[v.ID].(LocPair)[1]
+	if reg == nil {
+		v.Fatalf("nil second register for value: %s\n%s\n", v.LongString(), v.Block.Func)
+	}
+	return reg.(*Register).objNum
+}
+
+func (v *Value) RegName() string {
+	reg := v.Block.Func.RegAlloc[v.ID]
+	if reg == nil {
+		v.Fatalf("nil register for value: %s\n%s\n", v.LongString(), v.Block.Func)
+	}
+	return reg.(*Register).name
+}
diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go
new file mode 100644
index 0000000..b914154
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/writebarrier.go
@@ -0,0 +1,278 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssa
+
+import "fmt"
+
+// writebarrier expands write barrier ops (StoreWB, MoveWB, etc.) into
+// branches and runtime calls, like
+//
+// if writeBarrier.enabled {
+//   writebarrierptr(ptr, val)
+// } else {
+//   *ptr = val
+// }
+//
+// If ptr is an address of a stack slot, write barrier will be removed
+// and a normal store will be used.
+// A sequence of WB stores for many pointer fields of a single type will
+// be emitted together, with a single branch.
+//
+// Expanding WB ops introduces new control flows, and we would need to
+// split a block into two if there were values after WB ops, which would
+// require scheduling the values. To avoid this complexity, when building
+// SSA, we make sure that WB ops are always at the end of a block. We do
+// this before fuse as it may merge blocks. It also helps to reduce
+// number of blocks as fuse merges blocks introduced in this phase.
+func writebarrier(f *Func) {
+	var sb, sp, wbaddr *Value
+	var writebarrierptr, typedmemmove, typedmemclr interface{} // *gc.Sym
+	var storeWBs, others []*Value
+	var wbs *sparseSet
+	for _, b := range f.Blocks { // range loop is safe since the blocks we added contain no WB stores
+	valueLoop:
+		for i, v := range b.Values {
+			switch v.Op {
+			case OpStoreWB, OpMoveWB, OpMoveWBVolatile:
+				if IsStackAddr(v.Args[0]) {
+					switch v.Op {
+					case OpStoreWB:
+						v.Op = OpStore
+					case OpMoveWB, OpMoveWBVolatile:
+						v.Op = OpMove
+						v.Aux = nil
+					case OpZeroWB:
+						v.Op = OpZero
+						v.Aux = nil
+					}
+					continue
+				}
+
+				if wbaddr == nil {
+					// initalize global values for write barrier test and calls
+					// find SB and SP values in entry block
+					initln := f.Entry.Line
+					for _, v := range f.Entry.Values {
+						if v.Op == OpSB {
+							sb = v
+						}
+						if v.Op == OpSP {
+							sp = v
+						}
+					}
+					if sb == nil {
+						sb = f.Entry.NewValue0(initln, OpSB, f.Config.fe.TypeUintptr())
+					}
+					if sp == nil {
+						sp = f.Entry.NewValue0(initln, OpSP, f.Config.fe.TypeUintptr())
+					}
+					wbsym := &ExternSymbol{Typ: f.Config.fe.TypeBool(), Sym: f.Config.fe.Syslook("writeBarrier").(fmt.Stringer)}
+					wbaddr = f.Entry.NewValue1A(initln, OpAddr, f.Config.fe.TypeUInt32().PtrTo(), wbsym, sb)
+					writebarrierptr = f.Config.fe.Syslook("writebarrierptr")
+					typedmemmove = f.Config.fe.Syslook("typedmemmove")
+					typedmemclr = f.Config.fe.Syslook("typedmemclr")
+
+					wbs = f.newSparseSet(f.NumValues())
+					defer f.retSparseSet(wbs)
+				}
+
+				mem := v.Args[2]
+				line := v.Line
+
+				// there may be a sequence of WB stores in the current block. find them.
+				storeWBs = storeWBs[:0]
+				others = others[:0]
+				wbs.clear()
+				for _, w := range b.Values[i:] {
+					if w.Op == OpStoreWB || w.Op == OpMoveWB || w.Op == OpMoveWBVolatile || w.Op == OpZeroWB {
+						storeWBs = append(storeWBs, w)
+						wbs.add(w.ID)
+					} else {
+						others = append(others, w)
+					}
+				}
+
+				// make sure that no value in this block depends on WB stores
+				for _, w := range b.Values {
+					if w.Op == OpStoreWB || w.Op == OpMoveWB || w.Op == OpMoveWBVolatile || w.Op == OpZeroWB {
+						continue
+					}
+					for _, a := range w.Args {
+						if wbs.contains(a.ID) {
+							f.Fatalf("value %v depends on WB store %v in the same block %v", w, a, b)
+						}
+					}
+				}
+
+				b.Values = append(b.Values[:i], others...) // move WB ops out of this block
+
+				bThen := f.NewBlock(BlockPlain)
+				bElse := f.NewBlock(BlockPlain)
+				bEnd := f.NewBlock(b.Kind)
+				bThen.Line = line
+				bElse.Line = line
+				bEnd.Line = line
+
+				// set up control flow for end block
+				bEnd.SetControl(b.Control)
+				bEnd.Likely = b.Likely
+				for _, e := range b.Succs {
+					bEnd.Succs = append(bEnd.Succs, e)
+					e.b.Preds[e.i].b = bEnd
+				}
+
+				// set up control flow for write barrier test
+				// load word, test word, avoiding partial register write from load byte.
+				flag := b.NewValue2(line, OpLoad, f.Config.fe.TypeUInt32(), wbaddr, mem)
+				const0 := f.ConstInt32(line, f.Config.fe.TypeUInt32(), 0)
+				flag = b.NewValue2(line, OpNeq32, f.Config.fe.TypeBool(), flag, const0)
+				b.Kind = BlockIf
+				b.SetControl(flag)
+				b.Likely = BranchUnlikely
+				b.Succs = b.Succs[:0]
+				b.AddEdgeTo(bThen)
+				b.AddEdgeTo(bElse)
+				bThen.AddEdgeTo(bEnd)
+				bElse.AddEdgeTo(bEnd)
+
+				memThen := mem
+				memElse := mem
+				for _, w := range storeWBs {
+					var val *Value
+					ptr := w.Args[0]
+					siz := w.AuxInt
+					typ := w.Aux // only non-nil for MoveWB, MoveWBVolatile, ZeroWB
+
+					var op Op
+					var fn interface{} // *gc.Sym
+					switch w.Op {
+					case OpStoreWB:
+						op = OpStore
+						fn = writebarrierptr
+						val = w.Args[1]
+					case OpMoveWB, OpMoveWBVolatile:
+						op = OpMove
+						fn = typedmemmove
+						val = w.Args[1]
+					case OpZeroWB:
+						op = OpZero
+						fn = typedmemclr
+					}
+
+					// then block: emit write barrier call
+					memThen = wbcall(line, bThen, fn, typ, ptr, val, memThen, sp, sb, w.Op == OpMoveWBVolatile)
+
+					// else block: normal store
+					if op == OpZero {
+						memElse = bElse.NewValue2I(line, op, TypeMem, siz, ptr, memElse)
+					} else {
+						memElse = bElse.NewValue3I(line, op, TypeMem, siz, ptr, val, memElse)
+					}
+				}
+
+				// merge memory
+				// Splice memory Phi into the last memory of the original sequence,
+				// which may be used in subsequent blocks. Other memories in the
+				// sequence must be dead after this block since there can be only
+				// one memory live.
+				v = storeWBs[len(storeWBs)-1]
+				bEnd.Values = append(bEnd.Values, v)
+				v.Block = bEnd
+				v.reset(OpPhi)
+				v.Type = TypeMem
+				v.AddArg(memThen)
+				v.AddArg(memElse)
+				for _, w := range storeWBs[:len(storeWBs)-1] {
+					for _, a := range w.Args {
+						a.Uses--
+					}
+				}
+				for _, w := range storeWBs[:len(storeWBs)-1] {
+					f.freeValue(w)
+				}
+
+				if f.Config.fe.Debug_wb() {
+					f.Config.Warnl(line, "write barrier")
+				}
+
+				break valueLoop
+			}
+		}
+	}
+}
+
+// wbcall emits write barrier runtime call in b, returns memory.
+// if valIsVolatile, it moves val into temp space before making the call.
+func wbcall(line int32, b *Block, fn interface{}, typ interface{}, ptr, val, mem, sp, sb *Value, valIsVolatile bool) *Value {
+	config := b.Func.Config
+
+	var tmp GCNode
+	if valIsVolatile {
+		// Copy to temp location if the source is volatile (will be clobbered by
+		// a function call). Marshaling the args to typedmemmove might clobber the
+		// value we're trying to move.
+		t := val.Type.ElemType()
+		tmp = config.fe.Auto(t)
+		aux := &AutoSymbol{Typ: t, Node: tmp}
+		mem = b.NewValue1A(line, OpVarDef, TypeMem, tmp, mem)
+		tmpaddr := b.NewValue1A(line, OpAddr, t.PtrTo(), aux, sp)
+		siz := MakeSizeAndAlign(t.Size(), t.Alignment()).Int64()
+		mem = b.NewValue3I(line, OpMove, TypeMem, siz, tmpaddr, val, mem)
+		val = tmpaddr
+	}
+
+	// put arguments on stack
+	off := config.ctxt.FixedFrameSize()
+
+	if typ != nil { // for typedmemmove
+		taddr := b.NewValue1A(line, OpAddr, config.fe.TypeUintptr(), typ, sb)
+		off = round(off, taddr.Type.Alignment())
+		arg := b.NewValue1I(line, OpOffPtr, taddr.Type.PtrTo(), off, sp)
+		mem = b.NewValue3I(line, OpStore, TypeMem, ptr.Type.Size(), arg, taddr, mem)
+		off += taddr.Type.Size()
+	}
+
+	off = round(off, ptr.Type.Alignment())
+	arg := b.NewValue1I(line, OpOffPtr, ptr.Type.PtrTo(), off, sp)
+	mem = b.NewValue3I(line, OpStore, TypeMem, ptr.Type.Size(), arg, ptr, mem)
+	off += ptr.Type.Size()
+
+	if val != nil {
+		off = round(off, val.Type.Alignment())
+		arg = b.NewValue1I(line, OpOffPtr, val.Type.PtrTo(), off, sp)
+		mem = b.NewValue3I(line, OpStore, TypeMem, val.Type.Size(), arg, val, mem)
+		off += val.Type.Size()
+	}
+	off = round(off, config.PtrSize)
+
+	// issue call
+	mem = b.NewValue1A(line, OpStaticCall, TypeMem, fn, mem)
+	mem.AuxInt = off - config.ctxt.FixedFrameSize()
+
+	if valIsVolatile {
+		mem = b.NewValue1A(line, OpVarKill, TypeMem, tmp, mem) // mark temp dead
+	}
+
+	return mem
+}
+
+// round to a multiple of r, r is a power of 2
+func round(o int64, r int64) int64 {
+	return (o + r - 1) &^ (r - 1)
+}
+
+// IsStackAddr returns whether v is known to be an address of a stack slot
+func IsStackAddr(v *Value) bool {
+	for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy {
+		v = v.Args[0]
+	}
+	switch v.Op {
+	case OpSP:
+		return true
+	case OpAddr:
+		return v.Args[0].Op == OpSP
+	}
+	return false
+}
diff --git a/src/cmd/compile/internal/syntax/dumper.go b/src/cmd/compile/internal/syntax/dumper.go
new file mode 100644
index 0000000..bb369fc
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/dumper.go
@@ -0,0 +1,212 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements printing of syntax tree structures.
+
+package syntax
+
+import (
+	"fmt"
+	"io"
+	"reflect"
+	"unicode"
+	"unicode/utf8"
+)
+
+// Fdump dumps the structure of the syntax tree rooted at n to w.
+// It is intended for debugging purposes; no specific output format
+// is guaranteed.
+func Fdump(w io.Writer, n Node) (err error) {
+	p := dumper{
+		output: w,
+		ptrmap: make(map[Node]int),
+		last:   '\n', // force printing of line number on first line
+	}
+
+	defer func() {
+		if e := recover(); e != nil {
+			err = e.(localError).err // re-panics if it's not a localError
+		}
+	}()
+
+	if n == nil {
+		p.printf("nil\n")
+		return
+	}
+	p.dump(reflect.ValueOf(n), n)
+	p.printf("\n")
+
+	return
+}
+
+type dumper struct {
+	output io.Writer
+	ptrmap map[Node]int // node -> dump line number
+	indent int          // current indentation level
+	last   byte         // last byte processed by Write
+	line   int          // current line number
+}
+
+var indentBytes = []byte(".  ")
+
+func (p *dumper) Write(data []byte) (n int, err error) {
+	var m int
+	for i, b := range data {
+		// invariant: data[0:n] has been written
+		if b == '\n' {
+			m, err = p.output.Write(data[n : i+1])
+			n += m
+			if err != nil {
+				return
+			}
+		} else if p.last == '\n' {
+			p.line++
+			_, err = fmt.Fprintf(p.output, "%6d  ", p.line)
+			if err != nil {
+				return
+			}
+			for j := p.indent; j > 0; j-- {
+				_, err = p.output.Write(indentBytes)
+				if err != nil {
+					return
+				}
+			}
+		}
+		p.last = b
+	}
+	if len(data) > n {
+		m, err = p.output.Write(data[n:])
+		n += m
+	}
+	return
+}
+
+// localError wraps locally caught errors so we can distinguish
+// them from genuine panics which we don't want to return as errors.
+type localError struct {
+	err error
+}
+
+// printf is a convenience wrapper that takes care of print errors.
+func (p *dumper) printf(format string, args ...interface{}) {
+	if _, err := fmt.Fprintf(p, format, args...); err != nil {
+		panic(localError{err})
+	}
+}
+
+// dump prints the contents of x.
+// If x is the reflect.Value of a struct s, where &s
+// implements Node, then &s should be passed for n -
+// this permits printing of the unexported span and
+// comments fields of the embedded isNode field by
+// calling the Span() and Comment() instead of using
+// reflection.
+func (p *dumper) dump(x reflect.Value, n Node) {
+	switch x.Kind() {
+	case reflect.Interface:
+		if x.IsNil() {
+			p.printf("nil")
+			return
+		}
+		p.dump(x.Elem(), nil)
+
+	case reflect.Ptr:
+		if x.IsNil() {
+			p.printf("nil")
+			return
+		}
+
+		// special cases for identifiers w/o attached comments (common case)
+		if x, ok := x.Interface().(*Name); ok {
+			p.printf(x.Value)
+			return
+		}
+
+		p.printf("*")
+		// Fields may share type expressions, and declarations
+		// may share the same group - use ptrmap to keep track
+		// of nodes that have been printed already.
+		if ptr, ok := x.Interface().(Node); ok {
+			if line, exists := p.ptrmap[ptr]; exists {
+				p.printf("(Node @ %d)", line)
+				return
+			}
+			p.ptrmap[ptr] = p.line
+			n = ptr
+		}
+		p.dump(x.Elem(), n)
+
+	case reflect.Slice:
+		if x.IsNil() {
+			p.printf("nil")
+			return
+		}
+		p.printf("%s (%d entries) {", x.Type(), x.Len())
+		if x.Len() > 0 {
+			p.indent++
+			p.printf("\n")
+			for i, n := 0, x.Len(); i < n; i++ {
+				p.printf("%d: ", i)
+				p.dump(x.Index(i), nil)
+				p.printf("\n")
+			}
+			p.indent--
+		}
+		p.printf("}")
+
+	case reflect.Struct:
+		typ := x.Type()
+
+		// if span, ok := x.Interface().(lexical.Span); ok {
+		// 	p.printf("%s", &span)
+		// 	return
+		// }
+
+		p.printf("%s {", typ)
+		p.indent++
+
+		first := true
+		if n != nil {
+			p.printf("\n")
+			first = false
+			// p.printf("Span: %s\n", n.Span())
+			// if c := *n.Comments(); c != nil {
+			// 	p.printf("Comments: ")
+			// 	p.dump(reflect.ValueOf(c), nil) // a Comment is not a Node
+			// 	p.printf("\n")
+			// }
+		}
+
+		for i, n := 0, typ.NumField(); i < n; i++ {
+			// Exclude non-exported fields because their
+			// values cannot be accessed via reflection.
+			if name := typ.Field(i).Name; isExported(name) {
+				if first {
+					p.printf("\n")
+					first = false
+				}
+				p.printf("%s: ", name)
+				p.dump(x.Field(i), nil)
+				p.printf("\n")
+			}
+		}
+
+		p.indent--
+		p.printf("}")
+
+	default:
+		switch x := x.Interface().(type) {
+		case string:
+			// print strings in quotes
+			p.printf("%q", x)
+		default:
+			p.printf("%v", x)
+		}
+	}
+}
+
+func isExported(name string) bool {
+	ch, _ := utf8.DecodeRuneInString(name)
+	return unicode.IsUpper(ch)
+}
diff --git a/src/cmd/compile/internal/syntax/dumper_test.go b/src/cmd/compile/internal/syntax/dumper_test.go
new file mode 100644
index 0000000..2b20cbd
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/dumper_test.go
@@ -0,0 +1,22 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+	"os"
+	"testing"
+)
+
+func TestDump(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping test in short mode")
+	}
+
+	ast, err := ParseFile(*src, nil, nil, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	Fdump(os.Stdout, ast)
+}
diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go
new file mode 100644
index 0000000..fadba84
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/nodes.go
@@ -0,0 +1,452 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+// ----------------------------------------------------------------------------
+// Nodes
+
+type Node interface {
+	Line() uint32
+	aNode()
+	init(p *parser)
+}
+
+type node struct {
+	// commented out for now since not yet used
+	// doc  *Comment // nil means no comment(s) attached
+	pos  uint32
+	line uint32
+}
+
+func (*node) aNode() {}
+
+func (n *node) Line() uint32 {
+	return n.line
+}
+
+func (n *node) init(p *parser) {
+	n.pos = uint32(p.pos)
+	n.line = uint32(p.line)
+}
+
+// ----------------------------------------------------------------------------
+// Files
+
+// package PkgName; DeclList[0], DeclList[1], ...
+type File struct {
+	PkgName  *Name
+	DeclList []Decl
+	Lines    int
+	node
+}
+
+// ----------------------------------------------------------------------------
+// Declarations
+
+type (
+	Decl interface {
+		Node
+		aDecl()
+	}
+
+	//              Path
+	// LocalPkgName Path
+	ImportDecl struct {
+		LocalPkgName *Name // including "."; nil means no rename present
+		Path         *BasicLit
+		Group        *Group // nil means not part of a group
+		decl
+	}
+
+	// NameList
+	// NameList      = Values
+	// NameList Type = Values
+	ConstDecl struct {
+		NameList []*Name
+		Type     Expr   // nil means no type
+		Values   Expr   // nil means no values
+		Group    *Group // nil means not part of a group
+		decl
+	}
+
+	// Name Type
+	TypeDecl struct {
+		Name   *Name
+		Type   Expr
+		Group  *Group // nil means not part of a group
+		Pragma Pragma
+		decl
+	}
+
+	// NameList Type
+	// NameList Type = Values
+	// NameList      = Values
+	VarDecl struct {
+		NameList []*Name
+		Type     Expr   // nil means no type
+		Values   Expr   // nil means no values
+		Group    *Group // nil means not part of a group
+		decl
+	}
+
+	// func          Name Type { Body }
+	// func          Name Type
+	// func Receiver Name Type { Body }
+	// func Receiver Name Type
+	FuncDecl struct {
+		Attr    map[string]bool // go:attr map
+		Recv    *Field          // nil means regular function
+		Name    *Name
+		Type    *FuncType
+		Body    []Stmt // nil means no body (forward declaration)
+		Pragma  Pragma // TODO(mdempsky): Cleaner solution.
+		EndLine uint32 // TODO(mdempsky): Cleaner solution.
+		decl
+	}
+)
+
+type decl struct{ node }
+
+func (*decl) aDecl() {}
+
+// All declarations belonging to the same group point to the same Group node.
+type Group struct {
+	dummy int // not empty so we are guaranteed different Group instances
+}
+
+// ----------------------------------------------------------------------------
+// Expressions
+
+type (
+	Expr interface {
+		Node
+		aExpr()
+	}
+
+	// Value
+	Name struct {
+		Value string
+		expr
+	}
+
+	// Value
+	BasicLit struct {
+		Value string
+		Kind  LitKind
+		expr
+	}
+
+	// Type { ElemList[0], ElemList[1], ... }
+	CompositeLit struct {
+		Type     Expr // nil means no literal type
+		ElemList []Expr
+		NKeys    int    // number of elements with keys
+		EndLine  uint32 // TODO(mdempsky): Cleaner solution.
+		expr
+	}
+
+	// Key: Value
+	KeyValueExpr struct {
+		Key, Value Expr
+		expr
+	}
+
+	// func Type { Body }
+	FuncLit struct {
+		Type    *FuncType
+		Body    []Stmt
+		EndLine uint32 // TODO(mdempsky): Cleaner solution.
+		expr
+	}
+
+	// (X)
+	ParenExpr struct {
+		X Expr
+		expr
+	}
+
+	// X.Sel
+	SelectorExpr struct {
+		X   Expr
+		Sel *Name
+		expr
+	}
+
+	// X[Index]
+	IndexExpr struct {
+		X     Expr
+		Index Expr
+		expr
+	}
+
+	// X[Index[0] : Index[1] : Index[2]]
+	SliceExpr struct {
+		X     Expr
+		Index [3]Expr
+		// Full indicates whether this is a simple or full slice expression.
+		// In a valid AST, this is equivalent to Index[2] != nil.
+		// TODO(mdempsky): This is only needed to report the "3-index
+		// slice of string" error when Index[2] is missing.
+		Full bool
+		expr
+	}
+
+	// X.(Type)
+	AssertExpr struct {
+		X Expr
+		// TODO(gri) consider using Name{"..."} instead of nil (permits attaching of comments)
+		Type Expr
+		expr
+	}
+
+	Operation struct {
+		Op   Operator
+		X, Y Expr // Y == nil means unary expression
+		expr
+	}
+
+	// Fun(ArgList[0], ArgList[1], ...)
+	CallExpr struct {
+		Fun     Expr
+		ArgList []Expr
+		HasDots bool // last argument is followed by ...
+		expr
+	}
+
+	// ElemList[0], ElemList[1], ...
+	ListExpr struct {
+		ElemList []Expr
+		expr
+	}
+
+	// [Len]Elem
+	ArrayType struct {
+		// TODO(gri) consider using Name{"..."} instead of nil (permits attaching of comments)
+		Len  Expr // nil means Len is ...
+		Elem Expr
+		expr
+	}
+
+	// []Elem
+	SliceType struct {
+		Elem Expr
+		expr
+	}
+
+	// ...Elem
+	DotsType struct {
+		Elem Expr
+		expr
+	}
+
+	// struct { FieldList[0] TagList[0]; FieldList[1] TagList[1]; ... }
+	StructType struct {
+		FieldList []*Field
+		TagList   []*BasicLit // i >= len(TagList) || TagList[i] == nil means no tag for field i
+		expr
+	}
+
+	// Name Type
+	//      Type
+	Field struct {
+		Name *Name // nil means anonymous field/parameter (structs/parameters), or embedded interface (interfaces)
+		Type Expr  // field names declared in a list share the same Type (identical pointers)
+		node
+	}
+
+	// interface { MethodList[0]; MethodList[1]; ... }
+	InterfaceType struct {
+		MethodList []*Field
+		expr
+	}
+
+	FuncType struct {
+		ParamList  []*Field
+		ResultList []*Field
+		expr
+	}
+
+	// map[Key]Value
+	MapType struct {
+		Key   Expr
+		Value Expr
+		expr
+	}
+
+	//   chan Elem
+	// <-chan Elem
+	// chan<- Elem
+	ChanType struct {
+		Dir  ChanDir // 0 means no direction
+		Elem Expr
+		expr
+	}
+)
+
+type expr struct{ node }
+
+func (*expr) aExpr() {}
+
+type ChanDir uint
+
+const (
+	_ ChanDir = iota
+	SendOnly
+	RecvOnly
+)
+
+// ----------------------------------------------------------------------------
+// Statements
+
+type (
+	Stmt interface {
+		Node
+		aStmt()
+	}
+
+	SimpleStmt interface {
+		Stmt
+		aSimpleStmt()
+	}
+
+	EmptyStmt struct {
+		simpleStmt
+	}
+
+	LabeledStmt struct {
+		Label *Name
+		Stmt  Stmt
+		stmt
+	}
+
+	BlockStmt struct {
+		Body []Stmt
+		stmt
+	}
+
+	ExprStmt struct {
+		X Expr
+		simpleStmt
+	}
+
+	SendStmt struct {
+		Chan, Value Expr // Chan <- Value
+		simpleStmt
+	}
+
+	DeclStmt struct {
+		DeclList []Decl
+		stmt
+	}
+
+	AssignStmt struct {
+		Op       Operator // 0 means no operation
+		Lhs, Rhs Expr     // Rhs == ImplicitOne means Lhs++ (Op == Add) or Lhs-- (Op == Sub)
+		simpleStmt
+	}
+
+	BranchStmt struct {
+		Tok   token // Break, Continue, Fallthrough, or Goto
+		Label *Name
+		stmt
+	}
+
+	CallStmt struct {
+		Tok  token // Go or Defer
+		Call *CallExpr
+		stmt
+	}
+
+	ReturnStmt struct {
+		Results Expr // nil means no explicit return values
+		stmt
+	}
+
+	IfStmt struct {
+		Init SimpleStmt
+		Cond Expr
+		Then []Stmt
+		Else Stmt // either *IfStmt or *BlockStmt
+		stmt
+	}
+
+	ForStmt struct {
+		Init SimpleStmt // incl. *RangeClause
+		Cond Expr
+		Post SimpleStmt
+		Body []Stmt
+		stmt
+	}
+
+	SwitchStmt struct {
+		Init SimpleStmt
+		Tag  Expr
+		Body []*CaseClause
+		stmt
+	}
+
+	SelectStmt struct {
+		Body []*CommClause
+		stmt
+	}
+)
+
+type (
+	RangeClause struct {
+		Lhs Expr // nil means no Lhs = or Lhs :=
+		Def bool // means :=
+		X   Expr // range X
+		simpleStmt
+	}
+
+	TypeSwitchGuard struct {
+		// TODO(gri) consider using Name{"..."} instead of nil (permits attaching of comments)
+		Lhs *Name // nil means no Lhs :=
+		X   Expr  // X.(type)
+		expr
+	}
+
+	CaseClause struct {
+		Cases Expr // nil means default clause
+		Body  []Stmt
+		node
+	}
+
+	CommClause struct {
+		Comm SimpleStmt // send or receive stmt; nil means default clause
+		Body []Stmt
+		node
+	}
+)
+
+type stmt struct{ node }
+
+func (stmt) aStmt() {}
+
+type simpleStmt struct {
+	stmt
+}
+
+func (simpleStmt) aSimpleStmt() {}
+
+// ----------------------------------------------------------------------------
+// Comments
+
+// TODO(gri) Consider renaming to CommentPos, CommentPlacement, etc.
+//           Kind = Above doesn't make much sense.
+type CommentKind uint
+
+const (
+	Above CommentKind = iota
+	Below
+	Left
+	Right
+)
+
+type Comment struct {
+	Kind CommentKind
+	Text string
+	Next *Comment
+}
diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go
new file mode 100644
index 0000000..a2e307f
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/parser.go
@@ -0,0 +1,2143 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+	"fmt"
+	"io"
+	"strings"
+)
+
+const debug = false
+const trace = false
+
+// The old gc parser assigned line numbers very inconsistently depending
+// on when it happened to construct AST nodes. To make transitioning to the
+// new AST easier, we try to mimick the behavior as much as possible.
+const gcCompat = true
+
+type parser struct {
+	scanner
+
+	fnest  int    // function nesting level (for error handling)
+	xnest  int    // expression nesting level (for complit ambiguity resolution)
+	indent []byte // tracing support
+}
+
+type parserError string // for error recovery if no error handler was installed
+
+func (p *parser) init(src io.Reader, errh ErrorHandler, pragh PragmaHandler) {
+	p.scanner.init(src, errh, pragh)
+
+	p.fnest = 0
+	p.xnest = 0
+	p.indent = nil
+}
+
+func (p *parser) got(tok token) bool {
+	if p.tok == tok {
+		p.next()
+		return true
+	}
+	return false
+}
+
+func (p *parser) want(tok token) {
+	if !p.got(tok) {
+		p.syntax_error("expecting " + tok.String())
+		p.advance()
+	}
+}
+
+// ----------------------------------------------------------------------------
+// Error handling
+
+// syntax_error reports a syntax error at the current line.
+func (p *parser) syntax_error(msg string) {
+	p.syntax_error_at(p.pos, p.line, msg)
+}
+
+// Like syntax_error, but reports error at given line rather than current lexer line.
+func (p *parser) syntax_error_at(pos, line int, msg string) {
+	if trace {
+		defer p.trace("syntax_error (" + msg + ")")()
+	}
+
+	if p.tok == _EOF && p.first != nil {
+		return // avoid meaningless follow-up errors
+	}
+
+	// add punctuation etc. as needed to msg
+	switch {
+	case msg == "":
+		// nothing to do
+	case strings.HasPrefix(msg, "in"), strings.HasPrefix(msg, "at"), strings.HasPrefix(msg, "after"):
+		msg = " " + msg
+	case strings.HasPrefix(msg, "expecting"):
+		msg = ", " + msg
+	default:
+		// plain error - we don't care about current token
+		p.error_at(pos, line, "syntax error: "+msg)
+		return
+	}
+
+	// determine token string
+	var tok string
+	switch p.tok {
+	case _Name:
+		tok = p.lit
+	case _Literal:
+		tok = "literal " + p.lit
+	case _Operator:
+		tok = p.op.String()
+	case _AssignOp:
+		tok = p.op.String() + "="
+	case _IncOp:
+		tok = p.op.String()
+		tok += tok
+	default:
+		tok = tokstring(p.tok)
+	}
+
+	p.error_at(pos, line, "syntax error: unexpected "+tok+msg)
+}
+
+// The stopset contains keywords that start a statement.
+// They are good synchronization points in case of syntax
+// errors and (usually) shouldn't be skipped over.
+const stopset uint64 = 1<<_Break |
+	1<<_Const |
+	1<<_Continue |
+	1<<_Defer |
+	1<<_Fallthrough |
+	1<<_For |
+	1<<_Func |
+	1<<_Go |
+	1<<_Goto |
+	1<<_If |
+	1<<_Return |
+	1<<_Select |
+	1<<_Switch |
+	1<<_Type |
+	1<<_Var
+
+// Advance consumes tokens until it finds a token of the stopset or followlist.
+// The stopset is only considered if we are inside a function (p.fnest > 0).
+// The followlist is the list of valid tokens that can follow a production;
+// if it is empty, exactly one token is consumed to ensure progress.
+func (p *parser) advance(followlist ...token) {
+	if len(followlist) == 0 {
+		p.next()
+		return
+	}
+
+	// compute follow set
+	// TODO(gri) the args are constants - do as constant expressions?
+	var followset uint64 = 1 << _EOF // never skip over EOF
+	for _, tok := range followlist {
+		followset |= 1 << tok
+	}
+
+	for !(contains(followset, p.tok) || p.fnest > 0 && contains(stopset, p.tok)) {
+		p.next()
+	}
+}
+
+func tokstring(tok token) string {
+	switch tok {
+	case _EOF:
+		return "EOF"
+	case _Comma:
+		return "comma"
+	case _Semi:
+		return "semicolon or newline"
+	}
+	return tok.String()
+}
+
+// usage: defer p.trace(msg)()
+func (p *parser) trace(msg string) func() {
+	fmt.Printf("%5d: %s%s (\n", p.line, p.indent, msg)
+	const tab = ". "
+	p.indent = append(p.indent, tab...)
+	return func() {
+		p.indent = p.indent[:len(p.indent)-len(tab)]
+		if x := recover(); x != nil {
+			panic(x) // skip print_trace
+		}
+		fmt.Printf("%5d: %s)\n", p.line, p.indent)
+	}
+}
+
+// ----------------------------------------------------------------------------
+// Package files
+//
+// Parse methods are annotated with matching Go productions as appropriate.
+// The annotations are intended as guidelines only since a single Go grammar
+// rule may be covered by multiple parse methods and vice versa.
+
+// SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
+func (p *parser) file() *File {
+	if trace {
+		defer p.trace("file")()
+	}
+
+	f := new(File)
+	f.init(p)
+
+	// PackageClause
+	if !p.got(_Package) {
+		p.syntax_error("package statement must be first")
+		return nil
+	}
+	f.PkgName = p.name()
+	p.want(_Semi)
+
+	// don't bother continuing if package clause has errors
+	if p.first != nil {
+		return nil
+	}
+
+	// { ImportDecl ";" }
+	for p.got(_Import) {
+		f.DeclList = p.appendGroup(f.DeclList, p.importDecl)
+		p.want(_Semi)
+	}
+
+	// { TopLevelDecl ";" }
+	for p.tok != _EOF {
+		switch p.tok {
+		case _Const:
+			p.next()
+			f.DeclList = p.appendGroup(f.DeclList, p.constDecl)
+
+		case _Type:
+			p.next()
+			f.DeclList = p.appendGroup(f.DeclList, p.typeDecl)
+
+		case _Var:
+			p.next()
+			f.DeclList = p.appendGroup(f.DeclList, p.varDecl)
+
+		case _Func:
+			p.next()
+			f.DeclList = append(f.DeclList, p.funcDecl())
+
+		default:
+			if p.tok == _Lbrace && len(f.DeclList) > 0 && emptyFuncDecl(f.DeclList[len(f.DeclList)-1]) {
+				// opening { of function declaration on next line
+				p.syntax_error("unexpected semicolon or newline before {")
+			} else {
+				p.syntax_error("non-declaration statement outside function body")
+			}
+			p.advance(_Const, _Type, _Var, _Func)
+			continue
+		}
+
+		// Reset p.pragma BEFORE advancing to the next token (consuming ';')
+		// since comments before may set pragmas for the next function decl.
+		p.pragma = 0
+
+		if p.tok != _EOF && !p.got(_Semi) {
+			p.syntax_error("after top level declaration")
+			p.advance(_Const, _Type, _Var, _Func)
+		}
+	}
+	// p.tok == _EOF
+
+	f.Lines = p.source.line
+
+	return f
+}
+
+func emptyFuncDecl(dcl Decl) bool {
+	f, ok := dcl.(*FuncDecl)
+	return ok && f.Body == nil
+}
+
+// ----------------------------------------------------------------------------
+// Declarations
+
+// appendGroup(f) = f | "(" { f ";" } ")" .
+func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl {
+	if p.got(_Lparen) {
+		g := new(Group)
+		for p.tok != _EOF && p.tok != _Rparen {
+			list = append(list, f(g))
+			if !p.osemi(_Rparen) {
+				break
+			}
+		}
+		p.want(_Rparen)
+		return list
+	}
+
+	return append(list, f(nil))
+}
+
+func (p *parser) importDecl(group *Group) Decl {
+	if trace {
+		defer p.trace("importDecl")()
+	}
+
+	d := new(ImportDecl)
+	d.init(p)
+
+	switch p.tok {
+	case _Name:
+		d.LocalPkgName = p.name()
+	case _Dot:
+		n := new(Name)
+		n.init(p)
+		n.Value = "."
+		d.LocalPkgName = n
+		p.next()
+	}
+	if p.tok == _Literal && (gcCompat || p.kind == StringLit) {
+		d.Path = p.oliteral()
+	} else {
+		p.syntax_error("missing import path; require quoted string")
+		p.advance(_Semi, _Rparen)
+	}
+	d.Group = group
+
+	return d
+}
+
+// ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
+func (p *parser) constDecl(group *Group) Decl {
+	if trace {
+		defer p.trace("constDecl")()
+	}
+
+	d := new(ConstDecl)
+	d.init(p)
+
+	d.NameList = p.nameList(p.name())
+	if p.tok != _EOF && p.tok != _Semi && p.tok != _Rparen {
+		d.Type = p.tryType()
+		if p.got(_Assign) {
+			d.Values = p.exprList()
+		}
+	}
+	d.Group = group
+
+	return d
+}
+
+// TypeSpec = identifier Type .
+func (p *parser) typeDecl(group *Group) Decl {
+	if trace {
+		defer p.trace("typeDecl")()
+	}
+
+	d := new(TypeDecl)
+	d.init(p)
+
+	d.Name = p.name()
+	d.Type = p.tryType()
+	if d.Type == nil {
+		p.syntax_error("in type declaration")
+		p.advance(_Semi, _Rparen)
+	}
+	d.Group = group
+	d.Pragma = p.pragma
+
+	return d
+}
+
+// VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
+func (p *parser) varDecl(group *Group) Decl {
+	if trace {
+		defer p.trace("varDecl")()
+	}
+
+	d := new(VarDecl)
+	d.init(p)
+
+	d.NameList = p.nameList(p.name())
+	if p.got(_Assign) {
+		d.Values = p.exprList()
+	} else {
+		d.Type = p.type_()
+		if p.got(_Assign) {
+			d.Values = p.exprList()
+		}
+	}
+	d.Group = group
+	if gcCompat {
+		d.init(p)
+	}
+
+	return d
+}
+
+// FunctionDecl = "func" FunctionName ( Function | Signature ) .
+// FunctionName = identifier .
+// Function     = Signature FunctionBody .
+// MethodDecl   = "func" Receiver MethodName ( Function | Signature ) .
+// Receiver     = Parameters .
+func (p *parser) funcDecl() *FuncDecl {
+	if trace {
+		defer p.trace("funcDecl")()
+	}
+
+	f := new(FuncDecl)
+	f.init(p)
+
+	badRecv := false
+	if p.tok == _Lparen {
+		rcvr := p.paramList()
+		switch len(rcvr) {
+		case 0:
+			p.error("method has no receiver")
+			badRecv = true
+		case 1:
+			f.Recv = rcvr[0]
+		default:
+			p.error("method has multiple receivers")
+			badRecv = true
+		}
+	}
+
+	if p.tok != _Name {
+		p.syntax_error("expecting name or (")
+		p.advance(_Lbrace, _Semi)
+		return nil
+	}
+
+	// TODO(gri) check for regular functions only
+	// if name.Sym.Name == "init" {
+	// 	name = renameinit()
+	// 	if params != nil || result != nil {
+	// 		p.error("func init must have no arguments and no return values")
+	// 	}
+	// }
+
+	// if localpkg.Name == "main" && name.Name == "main" {
+	// 	if params != nil || result != nil {
+	// 		p.error("func main must have no arguments and no return values")
+	// 	}
+	// }
+
+	f.Name = p.name()
+	f.Type = p.funcType()
+	if gcCompat {
+		f.node = f.Type.node
+	}
+	f.Body = p.funcBody()
+
+	f.Pragma = p.pragma
+	f.EndLine = uint32(p.line)
+
+	// TODO(gri) deal with function properties
+	// if noescape && body != nil {
+	// 	p.error("can only use //go:noescape with external func implementations")
+	// }
+
+	if badRecv {
+		return nil // TODO(gri) better solution
+	}
+	return f
+}
+
+// ----------------------------------------------------------------------------
+// Expressions
+
+func (p *parser) expr() Expr {
+	if trace {
+		defer p.trace("expr")()
+	}
+
+	return p.binaryExpr(0)
+}
+
+// Expression = UnaryExpr | Expression binary_op Expression .
+func (p *parser) binaryExpr(prec int) Expr {
+	// don't trace binaryExpr - only leads to overly nested trace output
+
+	x := p.unaryExpr()
+	for (p.tok == _Operator || p.tok == _Star) && p.prec > prec {
+		t := new(Operation)
+		t.init(p)
+		t.Op = p.op
+		t.X = x
+		tprec := p.prec
+		p.next()
+		t.Y = p.binaryExpr(tprec)
+		if gcCompat {
+			t.init(p)
+		}
+		x = t
+	}
+	return x
+}
+
+// UnaryExpr = PrimaryExpr | unary_op UnaryExpr .
+func (p *parser) unaryExpr() Expr {
+	if trace {
+		defer p.trace("unaryExpr")()
+	}
+
+	switch p.tok {
+	case _Operator, _Star:
+		switch p.op {
+		case Mul, Add, Sub, Not, Xor:
+			x := new(Operation)
+			x.init(p)
+			x.Op = p.op
+			p.next()
+			x.X = p.unaryExpr()
+			if gcCompat {
+				x.init(p)
+			}
+			return x
+
+		case And:
+			p.next()
+			x := new(Operation)
+			x.init(p)
+			x.Op = And
+			// unaryExpr may have returned a parenthesized composite literal
+			// (see comment in operand) - remove parentheses if any
+			x.X = unparen(p.unaryExpr())
+			return x
+		}
+
+	case _Arrow:
+		// receive op (<-x) or receive-only channel (<-chan E)
+		p.next()
+
+		// If the next token is _Chan we still don't know if it is
+		// a channel (<-chan int) or a receive op (<-chan int(ch)).
+		// We only know once we have found the end of the unaryExpr.
+
+		x := p.unaryExpr()
+
+		// There are two cases:
+		//
+		//   <-chan...  => <-x is a channel type
+		//   <-x        => <-x is a receive operation
+		//
+		// In the first case, <- must be re-associated with
+		// the channel type parsed already:
+		//
+		//   <-(chan E)   =>  (<-chan E)
+		//   <-(chan<-E)  =>  (<-chan (<-E))
+
+		if _, ok := x.(*ChanType); ok {
+			// x is a channel type => re-associate <-
+			dir := SendOnly
+			t := x
+			for dir == SendOnly {
+				c, ok := t.(*ChanType)
+				if !ok {
+					break
+				}
+				dir = c.Dir
+				if dir == RecvOnly {
+					// t is type <-chan E but <-<-chan E is not permitted
+					// (report same error as for "type _ <-<-chan E")
+					p.syntax_error("unexpected <-, expecting chan")
+					// already progressed, no need to advance
+				}
+				c.Dir = RecvOnly
+				t = c.Elem
+			}
+			if dir == SendOnly {
+				// channel dir is <- but channel element E is not a channel
+				// (report same error as for "type _ <-chan<-E")
+				p.syntax_error(fmt.Sprintf("unexpected %s, expecting chan", String(t)))
+				// already progressed, no need to advance
+			}
+			return x
+		}
+
+		// x is not a channel type => we have a receive op
+		return &Operation{Op: Recv, X: x}
+	}
+
+	// TODO(mdempsky): We need parens here so we can report an
+	// error for "(x) := true". It should be possible to detect
+	// and reject that more efficiently though.
+	return p.pexpr(true)
+}
+
+// callStmt parses call-like statements that can be preceded by 'defer' and 'go'.
+func (p *parser) callStmt() *CallStmt {
+	if trace {
+		defer p.trace("callStmt")()
+	}
+
+	s := new(CallStmt)
+	s.init(p)
+	s.Tok = p.tok
+	p.next()
+
+	x := p.pexpr(p.tok == _Lparen) // keep_parens so we can report error below
+	switch x := x.(type) {
+	case *CallExpr:
+		s.Call = x
+		if gcCompat {
+			s.node = x.node
+		}
+	case *ParenExpr:
+		p.error(fmt.Sprintf("expression in %s must not be parenthesized", s.Tok))
+		// already progressed, no need to advance
+	default:
+		p.error(fmt.Sprintf("expression in %s must be function call", s.Tok))
+		// already progressed, no need to advance
+	}
+
+	return s // TODO(gri) should we return nil in case of failure?
+}
+
+// Operand     = Literal | OperandName | MethodExpr | "(" Expression ")" .
+// Literal     = BasicLit | CompositeLit | FunctionLit .
+// BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
+// OperandName = identifier | QualifiedIdent.
+func (p *parser) operand(keep_parens bool) Expr {
+	if trace {
+		defer p.trace("operand " + p.tok.String())()
+	}
+
+	switch p.tok {
+	case _Name:
+		return p.name()
+
+	case _Literal:
+		return p.oliteral()
+
+	case _Lparen:
+		p.next()
+		p.xnest++
+		x := p.expr() // expr_or_type
+		p.xnest--
+		p.want(_Rparen)
+
+		// Optimization: Record presence of ()'s only where needed
+		// for error reporting. Don't bother in other cases; it is
+		// just a waste of memory and time.
+
+		// Parentheses are not permitted on lhs of := .
+		// switch x.Op {
+		// case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW:
+		// 	keep_parens = true
+		// }
+
+		// Parentheses are not permitted around T in a composite
+		// literal T{}. If the next token is a {, assume x is a
+		// composite literal type T (it may not be, { could be
+		// the opening brace of a block, but we don't know yet).
+		if p.tok == _Lbrace {
+			keep_parens = true
+		}
+
+		// Parentheses are also not permitted around the expression
+		// in a go/defer statement. In that case, operand is called
+		// with keep_parens set.
+		if keep_parens {
+			x = &ParenExpr{X: x}
+		}
+		return x
+
+	case _Func:
+		p.next()
+		t := p.funcType()
+		if p.tok == _Lbrace {
+			p.fnest++
+			p.xnest++
+			f := new(FuncLit)
+			f.init(p)
+			f.Type = t
+			f.Body = p.funcBody()
+			f.EndLine = uint32(p.line)
+			p.xnest--
+			p.fnest--
+			return f
+		}
+		return t
+
+	case _Lbrack, _Chan, _Map, _Struct, _Interface:
+		return p.type_() // othertype
+
+	case _Lbrace:
+		// common case: p.header is missing simpleStmt before { in if, for, switch
+		p.syntax_error("missing operand")
+		// '{' will be consumed in pexpr - no need to consume it here
+		return nil
+
+	default:
+		p.syntax_error("expecting expression")
+		p.advance()
+		return nil
+	}
+
+	// Syntactically, composite literals are operands. Because a complit
+	// type may be a qualified identifier which is handled by pexpr
+	// (together with selector expressions), complits are parsed there
+	// as well (operand is only called from pexpr).
+}
+
+// PrimaryExpr =
+// 	Operand |
+// 	Conversion |
+// 	PrimaryExpr Selector |
+// 	PrimaryExpr Index |
+// 	PrimaryExpr Slice |
+// 	PrimaryExpr TypeAssertion |
+// 	PrimaryExpr Arguments .
+//
+// Selector       = "." identifier .
+// Index          = "[" Expression "]" .
+// Slice          = "[" ( [ Expression ] ":" [ Expression ] ) |
+//                      ( [ Expression ] ":" Expression ":" Expression )
+//                  "]" .
+// TypeAssertion  = "." "(" Type ")" .
+// Arguments      = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
+func (p *parser) pexpr(keep_parens bool) Expr {
+	if trace {
+		defer p.trace("pexpr")()
+	}
+
+	x := p.operand(keep_parens)
+
+loop:
+	for {
+		switch p.tok {
+		case _Dot:
+			p.next()
+			switch p.tok {
+			case _Name:
+				// pexpr '.' sym
+				t := new(SelectorExpr)
+				t.init(p)
+				t.X = x
+				t.Sel = p.name()
+				x = t
+
+			case _Lparen:
+				p.next()
+				if p.got(_Type) {
+					t := new(TypeSwitchGuard)
+					t.init(p)
+					t.X = x
+					x = t
+				} else {
+					t := new(AssertExpr)
+					t.init(p)
+					t.X = x
+					t.Type = p.expr()
+					x = t
+				}
+				p.want(_Rparen)
+
+			default:
+				p.syntax_error("expecting name or (")
+				p.advance(_Semi, _Rparen)
+			}
+			if gcCompat {
+				x.init(p)
+			}
+
+		case _Lbrack:
+			p.next()
+			p.xnest++
+
+			var i Expr
+			if p.tok != _Colon {
+				i = p.expr()
+				if p.got(_Rbrack) {
+					// x[i]
+					t := new(IndexExpr)
+					t.init(p)
+					t.X = x
+					t.Index = i
+					x = t
+					p.xnest--
+					break
+				}
+			}
+
+			// x[i:...
+			t := new(SliceExpr)
+			t.init(p)
+			t.X = x
+			t.Index[0] = i
+			p.want(_Colon)
+			if p.tok != _Colon && p.tok != _Rbrack {
+				// x[i:j...
+				t.Index[1] = p.expr()
+			}
+			if p.got(_Colon) {
+				t.Full = true
+				// x[i:j:...]
+				if t.Index[1] == nil {
+					p.error("middle index required in 3-index slice")
+				}
+				if p.tok != _Rbrack {
+					// x[i:j:k...
+					t.Index[2] = p.expr()
+				} else {
+					p.error("final index required in 3-index slice")
+				}
+			}
+			p.want(_Rbrack)
+
+			x = t
+			p.xnest--
+
+		case _Lparen:
+			x = p.call(x)
+
+		case _Lbrace:
+			// operand may have returned a parenthesized complit
+			// type; accept it but complain if we have a complit
+			t := unparen(x)
+			// determine if '{' belongs to a complit or a compound_stmt
+			complit_ok := false
+			switch t.(type) {
+			case *Name, *SelectorExpr:
+				if p.xnest >= 0 {
+					// x is considered a comptype
+					complit_ok = true
+				}
+			case *ArrayType, *SliceType, *StructType, *MapType:
+				// x is a comptype
+				complit_ok = true
+			}
+			if !complit_ok {
+				break loop
+			}
+			if t != x {
+				p.syntax_error("cannot parenthesize type in composite literal")
+				// already progressed, no need to advance
+			}
+			n := p.complitexpr()
+			n.Type = x
+			x = n
+
+		default:
+			break loop
+		}
+	}
+
+	return x
+}
+
+// Element = Expression | LiteralValue .
+func (p *parser) bare_complitexpr() Expr {
+	if trace {
+		defer p.trace("bare_complitexpr")()
+	}
+
+	if p.tok == _Lbrace {
+		// '{' start_complit braced_keyval_list '}'
+		return p.complitexpr()
+	}
+
+	return p.expr()
+}
+
+// LiteralValue = "{" [ ElementList [ "," ] ] "}" .
+func (p *parser) complitexpr() *CompositeLit {
+	if trace {
+		defer p.trace("complitexpr")()
+	}
+
+	x := new(CompositeLit)
+	x.init(p)
+
+	p.want(_Lbrace)
+	p.xnest++
+
+	for p.tok != _EOF && p.tok != _Rbrace {
+		// value
+		e := p.bare_complitexpr()
+		if p.got(_Colon) {
+			// key ':' value
+			l := new(KeyValueExpr)
+			l.init(p)
+			l.Key = e
+			l.Value = p.bare_complitexpr()
+			if gcCompat {
+				l.init(p)
+			}
+			e = l
+			x.NKeys++
+		}
+		x.ElemList = append(x.ElemList, e)
+		if !p.ocomma(_Rbrace) {
+			break
+		}
+	}
+
+	x.EndLine = uint32(p.line)
+	p.xnest--
+	p.want(_Rbrace)
+
+	return x
+}
+
+// ----------------------------------------------------------------------------
+// Types
+
+func (p *parser) type_() Expr {
+	if trace {
+		defer p.trace("type_")()
+	}
+
+	if typ := p.tryType(); typ != nil {
+		return typ
+	}
+
+	p.syntax_error("")
+	p.advance()
+	return nil
+}
+
+func indirect(typ Expr) Expr {
+	return &Operation{Op: Mul, X: typ}
+}
+
+// tryType is like type_ but it returns nil if there was no type
+// instead of reporting an error.
+//
+// Type     = TypeName | TypeLit | "(" Type ")" .
+// TypeName = identifier | QualifiedIdent .
+// TypeLit  = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
+// 	      SliceType | MapType | Channel_Type .
+func (p *parser) tryType() Expr {
+	if trace {
+		defer p.trace("tryType")()
+	}
+
+	switch p.tok {
+	case _Star:
+		// ptrtype
+		p.next()
+		return indirect(p.type_())
+
+	case _Arrow:
+		// recvchantype
+		p.next()
+		p.want(_Chan)
+		t := new(ChanType)
+		t.init(p)
+		t.Dir = RecvOnly
+		t.Elem = p.chanElem()
+		return t
+
+	case _Func:
+		// fntype
+		p.next()
+		return p.funcType()
+
+	case _Lbrack:
+		// '[' oexpr ']' ntype
+		// '[' _DotDotDot ']' ntype
+		p.next()
+		p.xnest++
+		if p.got(_Rbrack) {
+			// []T
+			p.xnest--
+			t := new(SliceType)
+			t.init(p)
+			t.Elem = p.type_()
+			return t
+		}
+
+		// [n]T
+		t := new(ArrayType)
+		t.init(p)
+		if !p.got(_DotDotDot) {
+			t.Len = p.expr()
+		}
+		p.want(_Rbrack)
+		p.xnest--
+		t.Elem = p.type_()
+		return t
+
+	case _Chan:
+		// _Chan non_recvchantype
+		// _Chan _Comm ntype
+		p.next()
+		t := new(ChanType)
+		t.init(p)
+		if p.got(_Arrow) {
+			t.Dir = SendOnly
+		}
+		t.Elem = p.chanElem()
+		return t
+
+	case _Map:
+		// _Map '[' ntype ']' ntype
+		p.next()
+		p.want(_Lbrack)
+		t := new(MapType)
+		t.init(p)
+		t.Key = p.type_()
+		p.want(_Rbrack)
+		t.Value = p.type_()
+		return t
+
+	case _Struct:
+		return p.structType()
+
+	case _Interface:
+		return p.interfaceType()
+
+	case _Name:
+		return p.dotname(p.name())
+
+	case _Lparen:
+		p.next()
+		t := p.type_()
+		p.want(_Rparen)
+		return t
+	}
+
+	return nil
+}
+
+func (p *parser) funcType() *FuncType {
+	if trace {
+		defer p.trace("funcType")()
+	}
+
+	typ := new(FuncType)
+	typ.init(p)
+	typ.ParamList = p.paramList()
+	typ.ResultList = p.funcResult()
+	if gcCompat {
+		typ.init(p)
+	}
+	return typ
+}
+
+func (p *parser) chanElem() Expr {
+	if trace {
+		defer p.trace("chanElem")()
+	}
+
+	if typ := p.tryType(); typ != nil {
+		return typ
+	}
+
+	p.syntax_error("missing channel element type")
+	// assume element type is simply absent - don't advance
+	return nil
+}
+
+func (p *parser) dotname(name *Name) Expr {
+	if trace {
+		defer p.trace("dotname")()
+	}
+
+	if p.got(_Dot) {
+		s := new(SelectorExpr)
+		s.init(p)
+		s.X = name
+		s.Sel = p.name()
+		return s
+	}
+	return name
+}
+
+// StructType = "struct" "{" { FieldDecl ";" } "}" .
+func (p *parser) structType() *StructType {
+	if trace {
+		defer p.trace("structType")()
+	}
+
+	typ := new(StructType)
+	typ.init(p)
+
+	p.want(_Struct)
+	p.want(_Lbrace)
+	for p.tok != _EOF && p.tok != _Rbrace {
+		p.fieldDecl(typ)
+		if !p.osemi(_Rbrace) {
+			break
+		}
+	}
+	if gcCompat {
+		typ.init(p)
+	}
+	p.want(_Rbrace)
+
+	return typ
+}
+
+// InterfaceType = "interface" "{" { MethodSpec ";" } "}" .
+func (p *parser) interfaceType() *InterfaceType {
+	if trace {
+		defer p.trace("interfaceType")()
+	}
+
+	typ := new(InterfaceType)
+	typ.init(p)
+
+	p.want(_Interface)
+	p.want(_Lbrace)
+	for p.tok != _EOF && p.tok != _Rbrace {
+		if m := p.methodDecl(); m != nil {
+			typ.MethodList = append(typ.MethodList, m)
+		}
+		if !p.osemi(_Rbrace) {
+			break
+		}
+	}
+	if gcCompat {
+		typ.init(p)
+	}
+	p.want(_Rbrace)
+
+	return typ
+}
+
+// FunctionBody = Block .
+func (p *parser) funcBody() []Stmt {
+	if trace {
+		defer p.trace("funcBody")()
+	}
+
+	if p.got(_Lbrace) {
+		p.fnest++
+		body := p.stmtList()
+		p.fnest--
+		p.want(_Rbrace)
+		if body == nil {
+			body = []Stmt{new(EmptyStmt)}
+		}
+		return body
+	}
+
+	return nil
+}
+
+// Result = Parameters | Type .
+func (p *parser) funcResult() []*Field {
+	if trace {
+		defer p.trace("funcResult")()
+	}
+
+	if p.tok == _Lparen {
+		return p.paramList()
+	}
+
+	if result := p.tryType(); result != nil {
+		f := new(Field)
+		f.init(p)
+		f.Type = result
+		return []*Field{f}
+	}
+
+	return nil
+}
+
+func (p *parser) addField(styp *StructType, name *Name, typ Expr, tag *BasicLit) {
+	if tag != nil {
+		for i := len(styp.FieldList) - len(styp.TagList); i > 0; i-- {
+			styp.TagList = append(styp.TagList, nil)
+		}
+		styp.TagList = append(styp.TagList, tag)
+	}
+
+	f := new(Field)
+	f.init(p)
+	f.Name = name
+	f.Type = typ
+	styp.FieldList = append(styp.FieldList, f)
+
+	if gcCompat && name != nil {
+		f.node = name.node
+	}
+
+	if debug && tag != nil && len(styp.FieldList) != len(styp.TagList) {
+		panic("inconsistent struct field list")
+	}
+}
+
+// FieldDecl      = (IdentifierList Type | AnonymousField) [ Tag ] .
+// AnonymousField = [ "*" ] TypeName .
+// Tag            = string_lit .
+func (p *parser) fieldDecl(styp *StructType) {
+	if trace {
+		defer p.trace("fieldDecl")()
+	}
+
+	var name *Name
+	switch p.tok {
+	case _Name:
+		name = p.name()
+		if p.tok == _Dot || p.tok == _Literal || p.tok == _Semi || p.tok == _Rbrace {
+			// embed oliteral
+			typ := p.qualifiedName(name)
+			tag := p.oliteral()
+			p.addField(styp, nil, typ, tag)
+			return
+		}
+
+		// new_name_list ntype oliteral
+		names := p.nameList(name)
+		typ := p.type_()
+		tag := p.oliteral()
+
+		for _, name := range names {
+			p.addField(styp, name, typ, tag)
+		}
+
+	case _Lparen:
+		p.next()
+		if p.tok == _Star {
+			// '(' '*' embed ')' oliteral
+			p.next()
+			typ := indirect(p.qualifiedName(nil))
+			p.want(_Rparen)
+			tag := p.oliteral()
+			p.addField(styp, nil, typ, tag)
+			p.error("cannot parenthesize embedded type")
+
+		} else {
+			// '(' embed ')' oliteral
+			typ := p.qualifiedName(nil)
+			p.want(_Rparen)
+			tag := p.oliteral()
+			p.addField(styp, nil, typ, tag)
+			p.error("cannot parenthesize embedded type")
+		}
+
+	case _Star:
+		p.next()
+		if p.got(_Lparen) {
+			// '*' '(' embed ')' oliteral
+			typ := indirect(p.qualifiedName(nil))
+			p.want(_Rparen)
+			tag := p.oliteral()
+			p.addField(styp, nil, typ, tag)
+			p.error("cannot parenthesize embedded type")
+
+		} else {
+			// '*' embed oliteral
+			typ := indirect(p.qualifiedName(nil))
+			tag := p.oliteral()
+			p.addField(styp, nil, typ, tag)
+		}
+
+	default:
+		p.syntax_error("expecting field name or embedded type")
+		p.advance(_Semi, _Rbrace)
+	}
+}
+
+func (p *parser) oliteral() *BasicLit {
+	if p.tok == _Literal {
+		b := new(BasicLit)
+		b.init(p)
+		b.Value = p.lit
+		b.Kind = p.kind
+		p.next()
+		return b
+	}
+	return nil
+}
+
+// MethodSpec        = MethodName Signature | InterfaceTypeName .
+// MethodName        = identifier .
+// InterfaceTypeName = TypeName .
+func (p *parser) methodDecl() *Field {
+	if trace {
+		defer p.trace("methodDecl")()
+	}
+
+	switch p.tok {
+	case _Name:
+		name := p.name()
+
+		// accept potential name list but complain
+		hasNameList := false
+		for p.got(_Comma) {
+			p.name()
+			hasNameList = true
+		}
+		if hasNameList {
+			p.syntax_error("name list not allowed in interface type")
+			// already progressed, no need to advance
+		}
+
+		f := new(Field)
+		f.init(p)
+		if p.tok != _Lparen {
+			// packname
+			f.Type = p.qualifiedName(name)
+			return f
+		}
+
+		f.Name = name
+		f.Type = p.funcType()
+		return f
+
+	case _Lparen:
+		p.next()
+		f := new(Field)
+		f.init(p)
+		f.Type = p.qualifiedName(nil)
+		p.want(_Rparen)
+		p.error("cannot parenthesize embedded type")
+		return f
+
+	default:
+		p.syntax_error("")
+		p.advance(_Semi, _Rbrace)
+		return nil
+	}
+}
+
+// ParameterDecl = [ IdentifierList ] [ "..." ] Type .
+func (p *parser) paramDecl() *Field {
+	if trace {
+		defer p.trace("paramDecl")()
+	}
+
+	f := new(Field)
+	f.init(p)
+
+	switch p.tok {
+	case _Name:
+		f.Name = p.name()
+		switch p.tok {
+		case _Name, _Star, _Arrow, _Func, _Lbrack, _Chan, _Map, _Struct, _Interface, _Lparen:
+			// sym name_or_type
+			f.Type = p.type_()
+
+		case _DotDotDot:
+			// sym dotdotdot
+			f.Type = p.dotsType()
+
+		case _Dot:
+			// name_or_type
+			// from dotname
+			f.Type = p.dotname(f.Name)
+			f.Name = nil
+		}
+
+	case _Arrow, _Star, _Func, _Lbrack, _Chan, _Map, _Struct, _Interface, _Lparen:
+		// name_or_type
+		f.Type = p.type_()
+
+	case _DotDotDot:
+		// dotdotdot
+		f.Type = p.dotsType()
+
+	default:
+		p.syntax_error("expecting )")
+		p.advance(_Comma, _Rparen)
+		return nil
+	}
+
+	return f
+}
+
+// ...Type
+func (p *parser) dotsType() *DotsType {
+	if trace {
+		defer p.trace("dotsType")()
+	}
+
+	t := new(DotsType)
+	t.init(p)
+
+	p.want(_DotDotDot)
+	t.Elem = p.tryType()
+	if t.Elem == nil {
+		p.error("final argument in variadic function missing type")
+	}
+
+	return t
+}
+
+// Parameters    = "(" [ ParameterList [ "," ] ] ")" .
+// ParameterList = ParameterDecl { "," ParameterDecl } .
+func (p *parser) paramList() (list []*Field) {
+	if trace {
+		defer p.trace("paramList")()
+	}
+
+	p.want(_Lparen)
+
+	var named int // number of parameters that have an explicit name and type
+	for p.tok != _EOF && p.tok != _Rparen {
+		if par := p.paramDecl(); par != nil {
+			if debug && par.Name == nil && par.Type == nil {
+				panic("parameter without name or type")
+			}
+			if par.Name != nil && par.Type != nil {
+				named++
+			}
+			list = append(list, par)
+		}
+		if !p.ocomma(_Rparen) {
+			break
+		}
+	}
+
+	// distribute parameter types
+	if named == 0 {
+		// all unnamed => found names are named types
+		for _, par := range list {
+			if typ := par.Name; typ != nil {
+				par.Type = typ
+				par.Name = nil
+			}
+		}
+	} else if named != len(list) {
+		// some named => all must be named
+		var typ Expr
+		for i := len(list) - 1; i >= 0; i-- {
+			if par := list[i]; par.Type != nil {
+				typ = par.Type
+				if par.Name == nil {
+					typ = nil // error
+				}
+			} else {
+				par.Type = typ
+			}
+			if typ == nil {
+				p.syntax_error("mixed named and unnamed function parameters")
+				break
+			}
+		}
+	}
+
+	p.want(_Rparen)
+	return
+}
+
+// ----------------------------------------------------------------------------
+// Statements
+
+// We represent x++, x-- as assignments x += ImplicitOne, x -= ImplicitOne.
+// ImplicitOne should not be used elsewhere.
+var ImplicitOne = &BasicLit{Value: "1"}
+
+// SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
+//
+// simpleStmt may return missing_stmt if labelOk is set.
+func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
+	if trace {
+		defer p.trace("simpleStmt")()
+	}
+
+	if rangeOk && p.got(_Range) {
+		// _Range expr
+		if debug && lhs != nil {
+			panic("invalid call of simpleStmt")
+		}
+		return p.rangeClause(nil, false)
+	}
+
+	if lhs == nil {
+		lhs = p.exprList()
+	}
+
+	if _, ok := lhs.(*ListExpr); !ok && p.tok != _Assign && p.tok != _Define {
+		// expr
+		switch p.tok {
+		case _AssignOp:
+			// lhs op= rhs
+			op := p.op
+			p.next()
+			return p.newAssignStmt(op, lhs, p.expr())
+
+		case _IncOp:
+			// lhs++ or lhs--
+			op := p.op
+			p.next()
+			return p.newAssignStmt(op, lhs, ImplicitOne)
+
+		case _Arrow:
+			// lhs <- rhs
+			p.next()
+			s := new(SendStmt)
+			s.init(p)
+			s.Chan = lhs
+			s.Value = p.expr()
+			if gcCompat {
+				s.init(p)
+			}
+			return s
+
+		default:
+			// expr
+			return &ExprStmt{X: lhs}
+		}
+	}
+
+	// expr_list
+	switch p.tok {
+	case _Assign:
+		p.next()
+
+		if rangeOk && p.got(_Range) {
+			// expr_list '=' _Range expr
+			return p.rangeClause(lhs, false)
+		}
+
+		// expr_list '=' expr_list
+		return p.newAssignStmt(0, lhs, p.exprList())
+
+	case _Define:
+		var n node
+		n.init(p)
+		p.next()
+
+		if rangeOk && p.got(_Range) {
+			// expr_list ':=' range expr
+			return p.rangeClause(lhs, true)
+		}
+
+		// expr_list ':=' expr_list
+		rhs := p.exprList()
+
+		if x, ok := rhs.(*TypeSwitchGuard); ok {
+			switch lhs := lhs.(type) {
+			case *Name:
+				x.Lhs = lhs
+			case *ListExpr:
+				p.error(fmt.Sprintf("argument count mismatch: %d = %d", len(lhs.ElemList), 1))
+			default:
+				// TODO(mdempsky): Have Expr types implement Stringer?
+				p.error(fmt.Sprintf("invalid variable name %s in type switch", lhs))
+			}
+			return &ExprStmt{X: x}
+		}
+
+		as := p.newAssignStmt(Def, lhs, rhs)
+		if gcCompat {
+			as.node = n
+		}
+		return as
+
+	default:
+		p.syntax_error("expecting := or = or comma")
+		p.advance(_Semi, _Rbrace)
+		return nil
+	}
+}
+
+func (p *parser) rangeClause(lhs Expr, def bool) *RangeClause {
+	r := new(RangeClause)
+	r.init(p)
+	r.Lhs = lhs
+	r.Def = def
+	r.X = p.expr()
+	if gcCompat {
+		r.init(p)
+	}
+	return r
+}
+
+func (p *parser) newAssignStmt(op Operator, lhs, rhs Expr) *AssignStmt {
+	a := new(AssignStmt)
+	a.init(p)
+	a.Op = op
+	a.Lhs = lhs
+	a.Rhs = rhs
+	return a
+}
+
+func (p *parser) labeledStmt(label *Name) Stmt {
+	if trace {
+		defer p.trace("labeledStmt")()
+	}
+
+	s := new(LabeledStmt)
+	s.init(p)
+	s.Label = label
+
+	p.want(_Colon)
+
+	if p.tok != _Rbrace && p.tok != _EOF {
+		s.Stmt = p.stmt()
+		if s.Stmt == missing_stmt {
+			// report error at line of ':' token
+			p.syntax_error_at(int(label.pos), int(label.line), "missing statement after label")
+			// we are already at the end of the labeled statement - no need to advance
+			return missing_stmt
+		}
+	}
+
+	return s
+}
+
+func (p *parser) blockStmt() *BlockStmt {
+	if trace {
+		defer p.trace("blockStmt")()
+	}
+
+	s := new(BlockStmt)
+	s.init(p)
+	p.want(_Lbrace)
+	s.Body = p.stmtList()
+	p.want(_Rbrace)
+
+	return s
+}
+
+func (p *parser) declStmt(f func(*Group) Decl) *DeclStmt {
+	if trace {
+		defer p.trace("declStmt")()
+	}
+
+	s := new(DeclStmt)
+	s.init(p)
+
+	p.next() // _Const, _Type, or _Var
+	s.DeclList = p.appendGroup(nil, f)
+
+	return s
+}
+
+func (p *parser) forStmt() Stmt {
+	if trace {
+		defer p.trace("forStmt")()
+	}
+
+	s := new(ForStmt)
+	s.init(p)
+
+	p.want(_For)
+	s.Init, s.Cond, s.Post = p.header(true)
+	if gcCompat {
+		s.init(p)
+	}
+	s.Body = p.stmtBody("for clause")
+
+	return s
+}
+
+// stmtBody parses if and for statement bodies.
+func (p *parser) stmtBody(context string) []Stmt {
+	if trace {
+		defer p.trace("stmtBody")()
+	}
+
+	if !p.got(_Lbrace) {
+		p.syntax_error("missing { after " + context)
+		p.advance(_Name, _Rbrace)
+	}
+
+	body := p.stmtList()
+	p.want(_Rbrace)
+
+	return body
+}
+
+func (p *parser) header(forStmt bool) (init SimpleStmt, cond Expr, post SimpleStmt) {
+	if p.tok == _Lbrace {
+		return
+	}
+
+	outer := p.xnest
+	p.xnest = -1
+
+	if p.tok != _Semi {
+		// accept potential varDecl but complain
+		if forStmt && p.got(_Var) {
+			p.error("var declaration not allowed in for initializer")
+		}
+		init = p.simpleStmt(nil, forStmt)
+		// If we have a range clause, we are done.
+		if _, ok := init.(*RangeClause); ok {
+			p.xnest = outer
+			return
+		}
+	}
+
+	var condStmt SimpleStmt
+	if p.got(_Semi) {
+		if forStmt {
+			if p.tok != _Semi {
+				condStmt = p.simpleStmt(nil, false)
+			}
+			p.want(_Semi)
+			if p.tok != _Lbrace {
+				post = p.simpleStmt(nil, false)
+			}
+		} else if p.tok != _Lbrace {
+			condStmt = p.simpleStmt(nil, false)
+		}
+	} else {
+		condStmt = init
+		init = nil
+	}
+
+	// unpack condStmt
+	switch s := condStmt.(type) {
+	case nil:
+		// nothing to do
+	case *ExprStmt:
+		cond = s.X
+	default:
+		p.error("invalid condition, tag, or type switch guard")
+	}
+
+	p.xnest = outer
+	return
+}
+
+func (p *parser) ifStmt() *IfStmt {
+	if trace {
+		defer p.trace("ifStmt")()
+	}
+
+	s := new(IfStmt)
+	s.init(p)
+
+	p.want(_If)
+	s.Init, s.Cond, _ = p.header(false)
+	if s.Cond == nil {
+		p.error("missing condition in if statement")
+	}
+
+	if gcCompat {
+		s.init(p)
+	}
+
+	s.Then = p.stmtBody("if clause")
+
+	if p.got(_Else) {
+		switch p.tok {
+		case _If:
+			s.Else = p.ifStmt()
+		case _Lbrace:
+			s.Else = p.blockStmt()
+		default:
+			p.error("else must be followed by if or statement block")
+			p.advance(_Name, _Rbrace)
+		}
+	}
+
+	return s
+}
+
+func (p *parser) switchStmt() *SwitchStmt {
+	if trace {
+		defer p.trace("switchStmt")()
+	}
+
+	p.want(_Switch)
+	s := new(SwitchStmt)
+	s.init(p)
+
+	s.Init, s.Tag, _ = p.header(false)
+
+	if !p.got(_Lbrace) {
+		p.syntax_error("missing { after switch clause")
+		p.advance(_Case, _Default, _Rbrace)
+	}
+	for p.tok != _EOF && p.tok != _Rbrace {
+		s.Body = append(s.Body, p.caseClause())
+	}
+	p.want(_Rbrace)
+
+	return s
+}
+
+func (p *parser) selectStmt() *SelectStmt {
+	if trace {
+		defer p.trace("selectStmt")()
+	}
+
+	p.want(_Select)
+	s := new(SelectStmt)
+	s.init(p)
+
+	if !p.got(_Lbrace) {
+		p.syntax_error("missing { after select clause")
+		p.advance(_Case, _Default, _Rbrace)
+	}
+	for p.tok != _EOF && p.tok != _Rbrace {
+		s.Body = append(s.Body, p.commClause())
+	}
+	p.want(_Rbrace)
+
+	return s
+}
+
+func (p *parser) caseClause() *CaseClause {
+	if trace {
+		defer p.trace("caseClause")()
+	}
+
+	c := new(CaseClause)
+	c.init(p)
+
+	switch p.tok {
+	case _Case:
+		p.next()
+		c.Cases = p.exprList()
+
+	case _Default:
+		p.next()
+
+	default:
+		p.syntax_error("expecting case or default or }")
+		p.advance(_Case, _Default, _Rbrace)
+	}
+
+	if gcCompat {
+		c.init(p)
+	}
+	p.want(_Colon)
+	c.Body = p.stmtList()
+
+	return c
+}
+
+func (p *parser) commClause() *CommClause {
+	if trace {
+		defer p.trace("commClause")()
+	}
+
+	c := new(CommClause)
+	c.init(p)
+
+	switch p.tok {
+	case _Case:
+		p.next()
+		c.Comm = p.simpleStmt(nil, false)
+
+		// The syntax restricts the possible simple statements here to:
+		//
+		//     lhs <- x (send statement)
+		//     <-x
+		//     lhs = <-x
+		//     lhs := <-x
+		//
+		// All these (and more) are recognized by simpleStmt and invalid
+		// syntax trees are flagged later, during type checking.
+		// TODO(gri) eventually may want to restrict valid syntax trees
+		// here.
+
+	case _Default:
+		p.next()
+
+	default:
+		p.syntax_error("expecting case or default or }")
+		p.advance(_Case, _Default, _Rbrace)
+	}
+
+	if gcCompat {
+		c.init(p)
+	}
+	p.want(_Colon)
+	c.Body = p.stmtList()
+
+	return c
+}
+
+// TODO(gri) find a better solution
+var missing_stmt Stmt = new(EmptyStmt) // = nod(OXXX, nil, nil)
+
+// Statement =
+// 	Declaration | LabeledStmt | SimpleStmt |
+// 	GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
+// 	FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
+// 	DeferStmt .
+//
+// stmt may return missing_stmt.
+func (p *parser) stmt() Stmt {
+	if trace {
+		defer p.trace("stmt " + p.tok.String())()
+	}
+
+	// Most statements (assignments) start with an identifier;
+	// look for it first before doing anything more expensive.
+	if p.tok == _Name {
+		lhs := p.exprList()
+		if label, ok := lhs.(*Name); ok && p.tok == _Colon {
+			return p.labeledStmt(label)
+		}
+		return p.simpleStmt(lhs, false)
+	}
+
+	switch p.tok {
+	case _Lbrace:
+		return p.blockStmt()
+
+	case _Var:
+		return p.declStmt(p.varDecl)
+
+	case _Const:
+		return p.declStmt(p.constDecl)
+
+	case _Type:
+		return p.declStmt(p.typeDecl)
+
+	case _Operator, _Star:
+		switch p.op {
+		case Add, Sub, Mul, And, Xor, Not:
+			return p.simpleStmt(nil, false) // unary operators
+		}
+
+	case _Literal, _Func, _Lparen, // operands
+		_Lbrack, _Struct, _Map, _Chan, _Interface, // composite types
+		_Arrow: // receive operator
+		return p.simpleStmt(nil, false)
+
+	case _For:
+		return p.forStmt()
+
+	case _Switch:
+		return p.switchStmt()
+
+	case _Select:
+		return p.selectStmt()
+
+	case _If:
+		return p.ifStmt()
+
+	case _Fallthrough:
+		p.next()
+		s := new(BranchStmt)
+		s.init(p)
+		s.Tok = _Fallthrough
+		return s
+		// // will be converted to OFALL
+		// stmt := nod(OXFALL, nil, nil)
+		// stmt.Xoffset = int64(block)
+		// return stmt
+
+	case _Break, _Continue:
+		tok := p.tok
+		p.next()
+		s := new(BranchStmt)
+		s.init(p)
+		s.Tok = tok
+		if p.tok == _Name {
+			s.Label = p.name()
+		}
+		return s
+
+	case _Go, _Defer:
+		return p.callStmt()
+
+	case _Goto:
+		p.next()
+		s := new(BranchStmt)
+		s.init(p)
+		s.Tok = _Goto
+		s.Label = p.name()
+		return s
+		// stmt := nod(OGOTO, p.new_name(p.name()), nil)
+		// stmt.Sym = dclstack // context, for goto restrictions
+		// return stmt
+
+	case _Return:
+		p.next()
+		s := new(ReturnStmt)
+		s.init(p)
+		if p.tok != _Semi && p.tok != _Rbrace {
+			s.Results = p.exprList()
+		}
+		if gcCompat {
+			s.init(p)
+		}
+		return s
+
+	case _Semi:
+		s := new(EmptyStmt)
+		s.init(p)
+		return s
+	}
+
+	return missing_stmt
+}
+
+// StatementList = { Statement ";" } .
+func (p *parser) stmtList() (l []Stmt) {
+	if trace {
+		defer p.trace("stmtList")()
+	}
+
+	for p.tok != _EOF && p.tok != _Rbrace && p.tok != _Case && p.tok != _Default {
+		s := p.stmt()
+		if s == missing_stmt {
+			break
+		}
+		l = append(l, s)
+		// customized version of osemi:
+		// ';' is optional before a closing ')' or '}'
+		if p.tok == _Rparen || p.tok == _Rbrace {
+			continue
+		}
+		if !p.got(_Semi) {
+			p.syntax_error("at end of statement")
+			p.advance(_Semi, _Rbrace)
+		}
+	}
+	return
+}
+
+// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
+func (p *parser) call(fun Expr) *CallExpr {
+	if trace {
+		defer p.trace("call")()
+	}
+
+	// call or conversion
+	// convtype '(' expr ocomma ')'
+	c := new(CallExpr)
+	c.init(p)
+	c.Fun = fun
+
+	p.want(_Lparen)
+	p.xnest++
+
+	for p.tok != _EOF && p.tok != _Rparen {
+		c.ArgList = append(c.ArgList, p.expr()) // expr_or_type
+		c.HasDots = p.got(_DotDotDot)
+		if !p.ocomma(_Rparen) || c.HasDots {
+			break
+		}
+	}
+
+	p.xnest--
+	if gcCompat {
+		c.init(p)
+	}
+	p.want(_Rparen)
+
+	return c
+}
+
+// ----------------------------------------------------------------------------
+// Common productions
+
+func (p *parser) name() *Name {
+	// no tracing to avoid overly verbose output
+
+	n := new(Name)
+	n.init(p)
+
+	if p.tok == _Name {
+		n.Value = p.lit
+		p.next()
+	} else {
+		n.Value = "_"
+		p.syntax_error("expecting name")
+		p.advance()
+	}
+
+	return n
+}
+
+// IdentifierList = identifier { "," identifier } .
+// The first name must be provided.
+func (p *parser) nameList(first *Name) []*Name {
+	if trace {
+		defer p.trace("nameList")()
+	}
+
+	if debug && first == nil {
+		panic("first name not provided")
+	}
+
+	l := []*Name{first}
+	for p.got(_Comma) {
+		l = append(l, p.name())
+	}
+
+	return l
+}
+
+// The first name may be provided, or nil.
+func (p *parser) qualifiedName(name *Name) Expr {
+	if trace {
+		defer p.trace("qualifiedName")()
+	}
+
+	switch {
+	case name != nil:
+		// name is provided
+	case p.tok == _Name:
+		name = p.name()
+	default:
+		name = new(Name)
+		name.init(p)
+		p.syntax_error("expecting name")
+		p.advance(_Dot, _Semi, _Rbrace)
+	}
+
+	return p.dotname(name)
+}
+
+// ExpressionList = Expression { "," Expression } .
+func (p *parser) exprList() Expr {
+	if trace {
+		defer p.trace("exprList")()
+	}
+
+	x := p.expr()
+	if p.got(_Comma) {
+		list := []Expr{x, p.expr()}
+		for p.got(_Comma) {
+			list = append(list, p.expr())
+		}
+		t := new(ListExpr)
+		t.init(p) // TODO(gri) what is the correct thing here?
+		t.ElemList = list
+		x = t
+	}
+	return x
+}
+
+// osemi parses an optional semicolon.
+func (p *parser) osemi(follow token) bool {
+	switch p.tok {
+	case _Semi:
+		p.next()
+		return true
+
+	case _Rparen, _Rbrace:
+		// semicolon is optional before ) or }
+		return true
+	}
+
+	p.syntax_error("expecting semicolon, newline, or " + tokstring(follow))
+	p.advance(follow)
+	return false
+}
+
+// ocomma parses an optional comma.
+func (p *parser) ocomma(follow token) bool {
+	switch p.tok {
+	case _Comma:
+		p.next()
+		return true
+
+	case _Rparen, _Rbrace:
+		// comma is optional before ) or }
+		return true
+	}
+
+	p.syntax_error("expecting comma or " + tokstring(follow))
+	p.advance(follow)
+	return false
+}
+
+// unparen removes all parentheses around an expression.
+func unparen(x Expr) Expr {
+	for {
+		p, ok := x.(*ParenExpr)
+		if !ok {
+			break
+		}
+		x = p.X
+	}
+	return x
+}
diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go
new file mode 100644
index 0000000..c4b43bf
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/parser_test.go
@@ -0,0 +1,184 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"sync"
+	"testing"
+	"time"
+)
+
+var fast = flag.Bool("fast", false, "parse package files in parallel")
+var src = flag.String("src", "parser.go", "source file to parse")
+var verify = flag.Bool("verify", false, "verify idempotent printing")
+
+func TestParse(t *testing.T) {
+	_, err := ParseFile(*src, nil, nil, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestStdLib(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping test in short mode")
+	}
+
+	var m1 runtime.MemStats
+	runtime.ReadMemStats(&m1)
+	start := time.Now()
+
+	type parseResult struct {
+		filename string
+		lines    int
+	}
+
+	results := make(chan parseResult)
+	go func() {
+		defer close(results)
+		for _, dir := range []string{
+			runtime.GOROOT(),
+		} {
+			walkDirs(t, dir, func(filename string) {
+				if debug {
+					fmt.Printf("parsing %s\n", filename)
+				}
+				ast, err := ParseFile(filename, nil, nil, 0)
+				if err != nil {
+					t.Error(err)
+					return
+				}
+				if *verify {
+					verifyPrint(filename, ast)
+				}
+				results <- parseResult{filename, ast.Lines}
+			})
+		}
+	}()
+
+	var count, lines int
+	for res := range results {
+		count++
+		lines += res.lines
+		if testing.Verbose() {
+			fmt.Printf("%5d  %s (%d lines)\n", count, res.filename, res.lines)
+		}
+	}
+
+	dt := time.Since(start)
+	var m2 runtime.MemStats
+	runtime.ReadMemStats(&m2)
+	dm := float64(m2.TotalAlloc-m1.TotalAlloc) / 1e6
+
+	fmt.Printf("parsed %d lines (%d files) in %v (%d lines/s)\n", lines, count, dt, int64(float64(lines)/dt.Seconds()))
+	fmt.Printf("allocated %.3fMb (%.3fMb/s)\n", dm, dm/dt.Seconds())
+}
+
+func walkDirs(t *testing.T, dir string, action func(string)) {
+	fis, err := ioutil.ReadDir(dir)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	var files, dirs []string
+	for _, fi := range fis {
+		if fi.Mode().IsRegular() {
+			if strings.HasSuffix(fi.Name(), ".go") {
+				path := filepath.Join(dir, fi.Name())
+				files = append(files, path)
+			}
+		} else if fi.IsDir() && fi.Name() != "testdata" {
+			path := filepath.Join(dir, fi.Name())
+			if !strings.HasSuffix(path, "/test") {
+				dirs = append(dirs, path)
+			}
+		}
+	}
+
+	if *fast {
+		var wg sync.WaitGroup
+		wg.Add(len(files))
+		for _, filename := range files {
+			go func(filename string) {
+				defer wg.Done()
+				action(filename)
+			}(filename)
+		}
+		wg.Wait()
+	} else {
+		for _, filename := range files {
+			action(filename)
+		}
+	}
+
+	for _, dir := range dirs {
+		walkDirs(t, dir, action)
+	}
+}
+
+func verifyPrint(filename string, ast1 *File) {
+	var buf1 bytes.Buffer
+	_, err := Fprint(&buf1, ast1, true)
+	if err != nil {
+		panic(err)
+	}
+
+	ast2, err := ParseBytes(buf1.Bytes(), nil, nil, 0)
+	if err != nil {
+		panic(err)
+	}
+
+	var buf2 bytes.Buffer
+	_, err = Fprint(&buf2, ast2, true)
+	if err != nil {
+		panic(err)
+	}
+
+	if bytes.Compare(buf1.Bytes(), buf2.Bytes()) != 0 {
+		fmt.Printf("--- %s ---\n", filename)
+		fmt.Printf("%s\n", buf1.Bytes())
+		fmt.Println()
+
+		fmt.Printf("--- %s ---\n", filename)
+		fmt.Printf("%s\n", buf2.Bytes())
+		fmt.Println()
+		panic("not equal")
+	}
+}
+
+func TestIssue17697(t *testing.T) {
+	_, err := ParseBytes(nil, nil, nil, 0) // return with parser error, don't panic
+	if err == nil {
+		t.Errorf("no error reported")
+	}
+}
+
+func TestParseFile(t *testing.T) {
+	_, err := ParseFile("", nil, nil, 0)
+	if err == nil {
+		t.Error("missing io error")
+	}
+
+	var first error
+	_, err = ParseFile("", func(err error) {
+		if first == nil {
+			first = err
+		}
+	}, nil, 0)
+	if err == nil || first == nil {
+		t.Error("missing io error")
+	}
+	if err != first {
+		t.Errorf("got %v; want first error %v", err, first)
+	}
+}
diff --git a/src/cmd/compile/internal/syntax/printer.go b/src/cmd/compile/internal/syntax/printer.go
new file mode 100644
index 0000000..0cacf1e
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/printer.go
@@ -0,0 +1,942 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements printing of syntax trees in source format.
+
+package syntax
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"strings"
+)
+
+// TODO(gri) Consider removing the linebreaks flag from this signature.
+// Its likely rarely used in common cases.
+
+func Fprint(w io.Writer, x Node, linebreaks bool) (n int, err error) {
+	p := printer{
+		output:     w,
+		linebreaks: linebreaks,
+	}
+
+	defer func() {
+		n = p.written
+		if e := recover(); e != nil {
+			err = e.(localError).err // re-panics if it's not a localError
+		}
+	}()
+
+	p.print(x)
+	p.flush(_EOF)
+
+	return
+}
+
+func String(n Node) string {
+	var buf bytes.Buffer
+	_, err := Fprint(&buf, n, false)
+	if err != nil {
+		panic(err) // TODO(gri) print something sensible into buf instead
+	}
+	return buf.String()
+}
+
+type ctrlSymbol int
+
+const (
+	none ctrlSymbol = iota
+	semi
+	blank
+	newline
+	indent
+	outdent
+	// comment
+	// eolComment
+)
+
+type whitespace struct {
+	last token
+	kind ctrlSymbol
+	//text string // comment text (possibly ""); valid if kind == comment
+}
+
+type printer struct {
+	output     io.Writer
+	written    int  // number of bytes written
+	linebreaks bool // print linebreaks instead of semis
+
+	indent  int // current indentation level
+	nlcount int // number of consecutive newlines
+
+	pending []whitespace // pending whitespace
+	lastTok token        // last token (after any pending semi) processed by print
+}
+
+// write is a thin wrapper around p.output.Write
+// that takes care of accounting and error handling.
+func (p *printer) write(data []byte) {
+	n, err := p.output.Write(data)
+	p.written += n
+	if err != nil {
+		panic(localError{err})
+	}
+}
+
+var (
+	tabBytes    = []byte("\t\t\t\t\t\t\t\t")
+	newlineByte = []byte("\n")
+	blankByte   = []byte(" ")
+)
+
+func (p *printer) writeBytes(data []byte) {
+	if len(data) == 0 {
+		panic("expected non-empty []byte")
+	}
+	if p.nlcount > 0 && p.indent > 0 {
+		// write indentation
+		n := p.indent
+		for n > len(tabBytes) {
+			p.write(tabBytes)
+			n -= len(tabBytes)
+		}
+		p.write(tabBytes[:n])
+	}
+	p.write(data)
+	p.nlcount = 0
+}
+
+func (p *printer) writeString(s string) {
+	p.writeBytes([]byte(s))
+}
+
+// If impliesSemi returns true for a non-blank line's final token tok,
+// a semicolon is automatically inserted. Vice versa, a semicolon may
+// be omitted in those cases.
+func impliesSemi(tok token) bool {
+	switch tok {
+	case _Name,
+		_Break, _Continue, _Fallthrough, _Return,
+		/*_Inc, _Dec,*/ _Rparen, _Rbrack, _Rbrace: // TODO(gri) fix this
+		return true
+	}
+	return false
+}
+
+// TODO(gri) provide table of []byte values for all tokens to avoid repeated string conversion
+
+func lineComment(text string) bool {
+	return strings.HasPrefix(text, "//")
+}
+
+func (p *printer) addWhitespace(kind ctrlSymbol, text string) {
+	p.pending = append(p.pending, whitespace{p.lastTok, kind /*text*/})
+	switch kind {
+	case semi:
+		p.lastTok = _Semi
+	case newline:
+		p.lastTok = 0
+		// TODO(gri) do we need to handle /*-style comments containing newlines here?
+	}
+}
+
+func (p *printer) flush(next token) {
+	// eliminate semis and redundant whitespace
+	sawNewline := next == _EOF
+	sawParen := next == _Rparen || next == _Rbrace
+	for i := len(p.pending) - 1; i >= 0; i-- {
+		switch p.pending[i].kind {
+		case semi:
+			k := semi
+			if sawParen {
+				sawParen = false
+				k = none // eliminate semi
+			} else if sawNewline && impliesSemi(p.pending[i].last) {
+				sawNewline = false
+				k = none // eliminate semi
+			}
+			p.pending[i].kind = k
+		case newline:
+			sawNewline = true
+		case blank, indent, outdent:
+			// nothing to do
+		// case comment:
+		// 	// A multi-line comment acts like a newline; and a ""
+		// 	// comment implies by definition at least one newline.
+		// 	if text := p.pending[i].text; strings.HasPrefix(text, "/*") && strings.ContainsRune(text, '\n') {
+		// 		sawNewline = true
+		// 	}
+		// case eolComment:
+		// 	// TODO(gri) act depending on sawNewline
+		default:
+			panic("unreachable")
+		}
+	}
+
+	// print pending
+	prev := none
+	for i := range p.pending {
+		switch p.pending[i].kind {
+		case none:
+			// nothing to do
+		case semi:
+			p.writeString(";")
+			p.nlcount = 0
+			prev = semi
+		case blank:
+			if prev != blank {
+				// at most one blank
+				p.writeBytes(blankByte)
+				p.nlcount = 0
+				prev = blank
+			}
+		case newline:
+			const maxEmptyLines = 1
+			if p.nlcount <= maxEmptyLines {
+				p.write(newlineByte)
+				p.nlcount++
+				prev = newline
+			}
+		case indent:
+			p.indent++
+		case outdent:
+			p.indent--
+			if p.indent < 0 {
+				panic("negative indentation")
+			}
+		// case comment:
+		// 	if text := p.pending[i].text; text != "" {
+		// 		p.writeString(text)
+		// 		p.nlcount = 0
+		// 		prev = comment
+		// 	}
+		// 	// TODO(gri) should check that line comments are always followed by newline
+		default:
+			panic("unreachable")
+		}
+	}
+
+	p.pending = p.pending[:0] // re-use underlying array
+}
+
+func mayCombine(prev token, next byte) (b bool) {
+	return // for now
+	// switch prev {
+	// case lexical.Int:
+	// 	b = next == '.' // 1.
+	// case lexical.Add:
+	// 	b = next == '+' // ++
+	// case lexical.Sub:
+	// 	b = next == '-' // --
+	// case lexical.Quo:
+	// 	b = next == '*' // /*
+	// case lexical.Lss:
+	// 	b = next == '-' || next == '<' // <- or <<
+	// case lexical.And:
+	// 	b = next == '&' || next == '^' // && or &^
+	// }
+	// return
+}
+
+func (p *printer) print(args ...interface{}) {
+	for i := 0; i < len(args); i++ {
+		switch x := args[i].(type) {
+		case nil:
+			// we should not reach here but don't crash
+
+		case Node:
+			p.printNode(x)
+
+		case token:
+			// _Name implies an immediately following string
+			// argument which is the actual value to print.
+			var s string
+			if x == _Name {
+				i++
+				if i >= len(args) {
+					panic("missing string argument after _Name")
+				}
+				s = args[i].(string)
+			} else {
+				s = x.String()
+			}
+
+			// TODO(gri) This check seems at the wrong place since it doesn't
+			//           take into account pending white space.
+			if mayCombine(p.lastTok, s[0]) {
+				panic("adjacent tokens combine without whitespace")
+			}
+
+			if x == _Semi {
+				// delay printing of semi
+				p.addWhitespace(semi, "")
+			} else {
+				p.flush(x)
+				p.writeString(s)
+				p.nlcount = 0
+				p.lastTok = x
+			}
+
+		case Operator:
+			if x != 0 {
+				p.flush(_Operator)
+				p.writeString(x.String())
+			}
+
+		case ctrlSymbol:
+			switch x {
+			case none, semi /*, comment*/ :
+				panic("unreachable")
+			case newline:
+				// TODO(gri) need to handle mandatory newlines after a //-style comment
+				if !p.linebreaks {
+					x = blank
+				}
+			}
+			p.addWhitespace(x, "")
+
+		// case *Comment: // comments are not Nodes
+		// 	p.addWhitespace(comment, x.Text)
+
+		default:
+			panic(fmt.Sprintf("unexpected argument %v (%T)", x, x))
+		}
+	}
+}
+
+func (p *printer) printNode(n Node) {
+	// ncom := *n.Comments()
+	// if ncom != nil {
+	// 	// TODO(gri) in general we cannot make assumptions about whether
+	// 	// a comment is a /*- or a //-style comment since the syntax
+	// 	// tree may have been manipulated. Need to make sure the correct
+	// 	// whitespace is emitted.
+	// 	for _, c := range ncom.Alone {
+	// 		p.print(c, newline)
+	// 	}
+	// 	for _, c := range ncom.Before {
+	// 		if c.Text == "" || lineComment(c.Text) {
+	// 			panic("unexpected empty line or //-style 'before' comment")
+	// 		}
+	// 		p.print(c, blank)
+	// 	}
+	// }
+
+	p.printRawNode(n)
+
+	// if ncom != nil && len(ncom.After) > 0 {
+	// 	for i, c := range ncom.After {
+	// 		if i+1 < len(ncom.After) {
+	// 			if c.Text == "" || lineComment(c.Text) {
+	// 				panic("unexpected empty line or //-style non-final 'after' comment")
+	// 			}
+	// 		}
+	// 		p.print(blank, c)
+	// 	}
+	// 	//p.print(newline)
+	// }
+}
+
+func (p *printer) printRawNode(n Node) {
+	switch n := n.(type) {
+	// expressions and types
+	case *Name:
+		p.print(_Name, n.Value) // _Name requires actual value following immediately
+
+	case *BasicLit:
+		p.print(_Name, n.Value) // _Name requires actual value following immediately
+
+	case *FuncLit:
+		p.print(n.Type, blank)
+		p.printBody(n.Body)
+
+	case *CompositeLit:
+		if n.Type != nil {
+			p.print(n.Type)
+		}
+		p.print(_Lbrace)
+		if n.NKeys > 0 && n.NKeys == len(n.ElemList) {
+			p.printExprLines(n.ElemList)
+		} else {
+			p.printExprList(n.ElemList)
+		}
+		p.print(_Rbrace)
+
+	case *ParenExpr:
+		p.print(_Lparen, n.X, _Rparen)
+
+	case *SelectorExpr:
+		p.print(n.X, _Dot, n.Sel)
+
+	case *IndexExpr:
+		p.print(n.X, _Lbrack, n.Index, _Rbrack)
+
+	case *SliceExpr:
+		p.print(n.X, _Lbrack)
+		if i := n.Index[0]; i != nil {
+			p.printNode(i)
+		}
+		p.print(_Colon)
+		if j := n.Index[1]; j != nil {
+			p.printNode(j)
+		}
+		if k := n.Index[2]; k != nil {
+			p.print(_Colon, k)
+		}
+		p.print(_Rbrack)
+
+	case *AssertExpr:
+		p.print(n.X, _Dot, _Lparen)
+		if n.Type != nil {
+			p.printNode(n.Type)
+		} else {
+			p.print(_Type)
+		}
+		p.print(_Rparen)
+
+	case *CallExpr:
+		p.print(n.Fun, _Lparen)
+		p.printExprList(n.ArgList)
+		if n.HasDots {
+			p.print(_DotDotDot)
+		}
+		p.print(_Rparen)
+
+	case *Operation:
+		if n.Y == nil {
+			// unary expr
+			p.print(n.Op)
+			// if n.Op == lexical.Range {
+			// 	p.print(blank)
+			// }
+			p.print(n.X)
+		} else {
+			// binary expr
+			// TODO(gri) eventually take precedence into account
+			// to control possibly missing parentheses
+			p.print(n.X, blank, n.Op, blank, n.Y)
+		}
+
+	case *KeyValueExpr:
+		p.print(n.Key, _Colon, blank, n.Value)
+
+	case *ListExpr:
+		p.printExprList(n.ElemList)
+
+	case *ArrayType:
+		var len interface{} = _DotDotDot
+		if n.Len != nil {
+			len = n.Len
+		}
+		p.print(_Lbrack, len, _Rbrack, n.Elem)
+
+	case *SliceType:
+		p.print(_Lbrack, _Rbrack, n.Elem)
+
+	case *DotsType:
+		p.print(_DotDotDot, n.Elem)
+
+	case *StructType:
+		p.print(_Struct)
+		if len(n.FieldList) > 0 && p.linebreaks {
+			p.print(blank)
+		}
+		p.print(_Lbrace)
+		if len(n.FieldList) > 0 {
+			p.print(newline, indent)
+			p.printFieldList(n.FieldList, n.TagList)
+			p.print(outdent, newline)
+		}
+		p.print(_Rbrace)
+
+	case *FuncType:
+		p.print(_Func)
+		p.printSignature(n)
+
+	case *InterfaceType:
+		p.print(_Interface)
+		if len(n.MethodList) > 0 && p.linebreaks {
+			p.print(blank)
+		}
+		p.print(_Lbrace)
+		if len(n.MethodList) > 0 {
+			p.print(newline, indent)
+			p.printMethodList(n.MethodList)
+			p.print(outdent, newline)
+		}
+		p.print(_Rbrace)
+
+	case *MapType:
+		p.print(_Map, _Lbrack, n.Key, _Rbrack, n.Value)
+
+	case *ChanType:
+		if n.Dir == RecvOnly {
+			p.print(_Arrow)
+		}
+		p.print(_Chan)
+		if n.Dir == SendOnly {
+			p.print(_Arrow)
+		}
+		p.print(blank, n.Elem)
+
+	// statements
+	case *DeclStmt:
+		p.printDecl(n.DeclList)
+
+	case *EmptyStmt:
+		// nothing to print
+
+	case *LabeledStmt:
+		p.print(outdent, n.Label, _Colon, indent, newline, n.Stmt)
+
+	case *ExprStmt:
+		p.print(n.X)
+
+	case *SendStmt:
+		p.print(n.Chan, blank, _Arrow, blank, n.Value)
+
+	case *AssignStmt:
+		p.print(n.Lhs)
+		if n.Rhs == ImplicitOne {
+			// TODO(gri) This is going to break the mayCombine
+			//           check once we enable that again.
+			p.print(n.Op, n.Op) // ++ or --
+		} else {
+			p.print(blank, n.Op, _Assign, blank)
+			p.print(n.Rhs)
+		}
+
+	case *CallStmt:
+		p.print(n.Tok, blank, n.Call)
+
+	case *ReturnStmt:
+		p.print(_Return)
+		if n.Results != nil {
+			p.print(blank, n.Results)
+		}
+
+	case *BranchStmt:
+		p.print(n.Tok)
+		if n.Label != nil {
+			p.print(blank, n.Label)
+		}
+
+	case *BlockStmt:
+		p.printBody(n.Body)
+
+	case *IfStmt:
+		p.print(_If, blank)
+		if n.Init != nil {
+			p.print(n.Init, _Semi, blank)
+		}
+		p.print(n.Cond, blank)
+		p.printBody(n.Then)
+		if n.Else != nil {
+			p.print(blank, _Else, blank, n.Else)
+		}
+
+	case *SwitchStmt:
+		p.print(_Switch, blank)
+		if n.Init != nil {
+			p.print(n.Init, _Semi, blank)
+		}
+		if n.Tag != nil {
+			p.print(n.Tag, blank)
+		}
+		p.printSwitchBody(n.Body)
+
+	case *TypeSwitchGuard:
+		if n.Lhs != nil {
+			p.print(n.Lhs, blank, _Define, blank)
+		}
+		p.print(n.X, _Dot, _Lparen, _Type, _Rparen)
+
+	case *SelectStmt:
+		p.print(_Select, blank) // for now
+		p.printSelectBody(n.Body)
+
+	case *RangeClause:
+		if n.Lhs != nil {
+			tok := _Assign
+			if n.Def {
+				tok = _Define
+			}
+			p.print(n.Lhs, blank, tok, blank)
+		}
+		p.print(_Range, blank, n.X)
+
+	case *ForStmt:
+		p.print(_For, blank)
+		if n.Init == nil && n.Post == nil {
+			if n.Cond != nil {
+				p.print(n.Cond, blank)
+			}
+		} else {
+			if n.Init != nil {
+				p.print(n.Init)
+				// TODO(gri) clean this up
+				if _, ok := n.Init.(*RangeClause); ok {
+					p.print(blank)
+					p.printBody(n.Body)
+					break
+				}
+			}
+			p.print(_Semi, blank)
+			if n.Cond != nil {
+				p.print(n.Cond)
+			}
+			p.print(_Semi, blank)
+			if n.Post != nil {
+				p.print(n.Post, blank)
+			}
+		}
+		p.printBody(n.Body)
+
+	case *ImportDecl:
+		if n.Group == nil {
+			p.print(_Import, blank)
+		}
+		if n.LocalPkgName != nil {
+			p.print(n.LocalPkgName, blank)
+		}
+		p.print(n.Path)
+
+	case *ConstDecl:
+		if n.Group == nil {
+			p.print(_Const, blank)
+		}
+		p.printNameList(n.NameList)
+		if n.Type != nil {
+			p.print(blank, n.Type)
+		}
+		if n.Values != nil {
+			p.print(blank, _Assign, blank, n.Values)
+		}
+
+	case *TypeDecl:
+		if n.Group == nil {
+			p.print(_Type, blank)
+		}
+		p.print(n.Name, blank, n.Type)
+
+	case *VarDecl:
+		if n.Group == nil {
+			p.print(_Var, blank)
+		}
+		p.printNameList(n.NameList)
+		if n.Type != nil {
+			p.print(blank, n.Type)
+		}
+		if n.Values != nil {
+			p.print(blank, _Assign, blank, n.Values)
+		}
+
+	case *FuncDecl:
+		p.print(_Func, blank)
+		if r := n.Recv; r != nil {
+			p.print(_Lparen)
+			if r.Name != nil {
+				p.print(r.Name, blank)
+			}
+			p.printNode(r.Type)
+			p.print(_Rparen, blank)
+		}
+		p.print(n.Name)
+		p.printSignature(n.Type)
+		if n.Body != nil {
+			p.print(blank)
+			p.printBody(n.Body)
+		}
+
+	case *printGroup:
+		p.print(n.Tok, blank, _Lparen)
+		if len(n.Decls) > 0 {
+			p.print(newline, indent)
+			for _, d := range n.Decls {
+				p.printNode(d)
+				p.print(_Semi, newline)
+			}
+			p.print(outdent)
+		}
+		p.print(_Rparen)
+
+	// files
+	case *File:
+		p.print(_Package, blank, n.PkgName)
+		if len(n.DeclList) > 0 {
+			p.print(_Semi, newline, newline)
+			p.printDeclList(n.DeclList)
+		}
+
+	default:
+		panic(fmt.Sprintf("syntax.Iterate: unexpected node type %T", n))
+	}
+}
+
+func (p *printer) printFields(fields []*Field, tags []*BasicLit, i, j int) {
+	if i+1 == j && fields[i].Name == nil {
+		// anonymous field
+		p.printNode(fields[i].Type)
+	} else {
+		for k, f := range fields[i:j] {
+			if k > 0 {
+				p.print(_Comma, blank)
+			}
+			p.printNode(f.Name)
+		}
+		p.print(blank)
+		p.printNode(fields[i].Type)
+	}
+	if i < len(tags) && tags[i] != nil {
+		p.print(blank)
+		p.printNode(tags[i])
+	}
+}
+
+func (p *printer) printFieldList(fields []*Field, tags []*BasicLit) {
+	i0 := 0
+	var typ Expr
+	for i, f := range fields {
+		if f.Name == nil || f.Type != typ {
+			if i0 < i {
+				p.printFields(fields, tags, i0, i)
+				p.print(_Semi, newline)
+				i0 = i
+			}
+			typ = f.Type
+		}
+	}
+	p.printFields(fields, tags, i0, len(fields))
+}
+
+func (p *printer) printMethodList(methods []*Field) {
+	for i, m := range methods {
+		if i > 0 {
+			p.print(_Semi, newline)
+		}
+		if m.Name != nil {
+			p.printNode(m.Name)
+			p.printSignature(m.Type.(*FuncType))
+		} else {
+			p.printNode(m.Type)
+		}
+	}
+}
+
+func (p *printer) printNameList(list []*Name) {
+	for i, x := range list {
+		if i > 0 {
+			p.print(_Comma, blank)
+		}
+		p.printNode(x)
+	}
+}
+
+func (p *printer) printExprList(list []Expr) {
+	for i, x := range list {
+		if i > 0 {
+			p.print(_Comma, blank)
+		}
+		p.printNode(x)
+	}
+}
+
+func (p *printer) printExprLines(list []Expr) {
+	if len(list) > 0 {
+		p.print(newline, indent)
+		for _, x := range list {
+			p.print(x, _Comma, newline)
+		}
+		p.print(outdent)
+	}
+}
+
+func groupFor(d Decl) (token, *Group) {
+	switch d := d.(type) {
+	case *ImportDecl:
+		return _Import, d.Group
+	case *ConstDecl:
+		return _Const, d.Group
+	case *TypeDecl:
+		return _Type, d.Group
+	case *VarDecl:
+		return _Var, d.Group
+	case *FuncDecl:
+		return _Func, nil
+	default:
+		panic("unreachable")
+	}
+}
+
+type printGroup struct {
+	node
+	Tok   token
+	Decls []Decl
+}
+
+func (p *printer) printDecl(list []Decl) {
+	tok, group := groupFor(list[0])
+
+	if group == nil {
+		if len(list) != 1 {
+			panic("unreachable")
+		}
+		p.printNode(list[0])
+		return
+	}
+
+	// if _, ok := list[0].(*EmptyDecl); ok {
+	// 	if len(list) != 1 {
+	// 		panic("unreachable")
+	// 	}
+	// 	// TODO(gri) if there are comments inside the empty
+	// 	// group, we may need to keep the list non-nil
+	// 	list = nil
+	// }
+
+	// printGroup is here for consistent comment handling
+	// (this is not yet used)
+	var pg printGroup
+	// *pg.Comments() = *group.Comments()
+	pg.Tok = tok
+	pg.Decls = list
+	p.printNode(&pg)
+}
+
+func (p *printer) printDeclList(list []Decl) {
+	i0 := 0
+	var tok token
+	var group *Group
+	for i, x := range list {
+		if s, g := groupFor(x); g == nil || g != group {
+			if i0 < i {
+				p.printDecl(list[i0:i])
+				p.print(_Semi, newline)
+				// print empty line between different declaration groups,
+				// different kinds of declarations, or between functions
+				if g != group || s != tok || s == _Func {
+					p.print(newline)
+				}
+				i0 = i
+			}
+			tok, group = s, g
+		}
+	}
+	p.printDecl(list[i0:])
+}
+
+func (p *printer) printSignature(sig *FuncType) {
+	p.printParameterList(sig.ParamList)
+	if list := sig.ResultList; list != nil {
+		p.print(blank)
+		if len(list) == 1 && list[0].Name == nil {
+			p.printNode(list[0].Type)
+		} else {
+			p.printParameterList(list)
+		}
+	}
+}
+
+func (p *printer) printParameterList(list []*Field) {
+	p.print(_Lparen)
+	if len(list) > 0 {
+		for i, f := range list {
+			if i > 0 {
+				p.print(_Comma, blank)
+			}
+			if f.Name != nil {
+				p.printNode(f.Name)
+				if i+1 < len(list) {
+					f1 := list[i+1]
+					if f1.Name != nil && f1.Type == f.Type {
+						continue // no need to print type
+					}
+				}
+				p.print(blank)
+			}
+			p.printNode(f.Type)
+		}
+	}
+	p.print(_Rparen)
+}
+
+func (p *printer) printStmtList(list []Stmt, braces bool) {
+	for i, x := range list {
+		p.print(x, _Semi)
+		if i+1 < len(list) {
+			p.print(newline)
+		} else if braces {
+			// Print an extra semicolon if the last statement is
+			// an empty statement and we are in a braced block
+			// because one semicolon is automatically removed.
+			if _, ok := x.(*EmptyStmt); ok {
+				p.print(x, _Semi)
+			}
+		}
+	}
+}
+
+func (p *printer) printBody(list []Stmt) {
+	p.print(_Lbrace)
+	if len(list) > 0 {
+		p.print(newline, indent)
+		p.printStmtList(list, true)
+		p.print(outdent, newline)
+	}
+	p.print(_Rbrace)
+}
+
+func (p *printer) printSwitchBody(list []*CaseClause) {
+	p.print(_Lbrace)
+	if len(list) > 0 {
+		p.print(newline)
+		for i, c := range list {
+			p.printCaseClause(c, i+1 == len(list))
+			p.print(newline)
+		}
+	}
+	p.print(_Rbrace)
+}
+
+func (p *printer) printSelectBody(list []*CommClause) {
+	p.print(_Lbrace)
+	if len(list) > 0 {
+		p.print(newline)
+		for i, c := range list {
+			p.printCommClause(c, i+1 == len(list))
+			p.print(newline)
+		}
+	}
+	p.print(_Rbrace)
+}
+
+func (p *printer) printCaseClause(c *CaseClause, braces bool) {
+	if c.Cases != nil {
+		p.print(_Case, blank, c.Cases)
+	} else {
+		p.print(_Default)
+	}
+	p.print(_Colon)
+	if len(c.Body) > 0 {
+		p.print(newline, indent)
+		p.printStmtList(c.Body, braces)
+		p.print(outdent)
+	}
+}
+
+func (p *printer) printCommClause(c *CommClause, braces bool) {
+	if c.Comm != nil {
+		p.print(_Case, blank)
+		p.print(c.Comm)
+	} else {
+		p.print(_Default)
+	}
+	p.print(_Colon)
+	if len(c.Body) > 0 {
+		p.print(newline, indent)
+		p.printStmtList(c.Body, braces)
+		p.print(outdent)
+	}
+}
diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go
new file mode 100644
index 0000000..5c0fc77
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/printer_test.go
@@ -0,0 +1,24 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+	"fmt"
+	"os"
+	"testing"
+)
+
+func TestPrint(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping test in short mode")
+	}
+
+	ast, err := ParseFile(*src, nil, nil, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	Fprint(os.Stdout, ast, true)
+	fmt.Println()
+}
diff --git a/src/cmd/compile/internal/syntax/scanner.go b/src/cmd/compile/internal/syntax/scanner.go
new file mode 100644
index 0000000..b84fcc5
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/scanner.go
@@ -0,0 +1,664 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+	"fmt"
+	"io"
+	"strings"
+	"unicode"
+	"unicode/utf8"
+)
+
+type scanner struct {
+	source
+	nlsemi bool // if set '\n' and EOF translate to ';'
+	pragma Pragma
+
+	// current token, valid after calling next()
+	pos, line int
+	tok       token
+	lit       string   // valid if tok is _Name or _Literal
+	kind      LitKind  // valid if tok is _Literal
+	op        Operator // valid if tok is _Operator, _AssignOp, or _IncOp
+	prec      int      // valid if tok is _Operator, _AssignOp, or _IncOp
+
+	pragh PragmaHandler
+}
+
+func (s *scanner) init(src io.Reader, errh ErrorHandler, pragh PragmaHandler) {
+	s.source.init(src, errh)
+	s.nlsemi = false
+	s.pragh = pragh
+}
+
+func (s *scanner) next() {
+	nlsemi := s.nlsemi
+	s.nlsemi = false
+
+redo:
+	// skip white space
+	c := s.getr()
+	for c == ' ' || c == '\t' || c == '\n' && !nlsemi || c == '\r' {
+		c = s.getr()
+	}
+
+	// token start
+	s.pos, s.line = s.source.pos0(), s.source.line0
+
+	if isLetter(c) || c >= utf8.RuneSelf && (unicode.IsLetter(c) || s.isCompatRune(c, true)) {
+		s.ident()
+		return
+	}
+
+	switch c {
+	case -1:
+		if nlsemi {
+			s.tok = _Semi
+			break
+		}
+		s.tok = _EOF
+
+	case '\n':
+		s.tok = _Semi
+
+	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+		s.number(c)
+
+	case '"':
+		s.stdString()
+
+	case '`':
+		s.rawString()
+
+	case '\'':
+		s.rune()
+
+	case '(':
+		s.tok = _Lparen
+
+	case '[':
+		s.tok = _Lbrack
+
+	case '{':
+		s.tok = _Lbrace
+
+	case ',':
+		s.tok = _Comma
+
+	case ';':
+		s.tok = _Semi
+
+	case ')':
+		s.nlsemi = true
+		s.tok = _Rparen
+
+	case ']':
+		s.nlsemi = true
+		s.tok = _Rbrack
+
+	case '}':
+		s.nlsemi = true
+		s.tok = _Rbrace
+
+	case ':':
+		if s.getr() == '=' {
+			s.tok = _Define
+			break
+		}
+		s.ungetr()
+		s.tok = _Colon
+
+	case '.':
+		c = s.getr()
+		if isDigit(c) {
+			s.ungetr()
+			s.source.r0-- // make sure '.' is part of literal (line cannot have changed)
+			s.number('.')
+			break
+		}
+		if c == '.' {
+			c = s.getr()
+			if c == '.' {
+				s.tok = _DotDotDot
+				break
+			}
+			s.ungetr()
+			s.source.r0-- // make next ungetr work (line cannot have changed)
+		}
+		s.ungetr()
+		s.tok = _Dot
+
+	case '+':
+		s.op, s.prec = Add, precAdd
+		c = s.getr()
+		if c != '+' {
+			goto assignop
+		}
+		s.nlsemi = true
+		s.tok = _IncOp
+
+	case '-':
+		s.op, s.prec = Sub, precAdd
+		c = s.getr()
+		if c != '-' {
+			goto assignop
+		}
+		s.nlsemi = true
+		s.tok = _IncOp
+
+	case '*':
+		s.op, s.prec = Mul, precMul
+		// don't goto assignop - want _Star token
+		if s.getr() == '=' {
+			s.tok = _AssignOp
+			break
+		}
+		s.ungetr()
+		s.tok = _Star
+
+	case '/':
+		c = s.getr()
+		if c == '/' {
+			s.lineComment()
+			goto redo
+		}
+		if c == '*' {
+			s.fullComment()
+			if s.source.line > s.line && nlsemi {
+				// A multi-line comment acts like a newline;
+				// it translates to a ';' if nlsemi is set.
+				s.tok = _Semi
+				break
+			}
+			goto redo
+		}
+		s.op, s.prec = Div, precMul
+		goto assignop
+
+	case '%':
+		s.op, s.prec = Rem, precMul
+		c = s.getr()
+		goto assignop
+
+	case '&':
+		c = s.getr()
+		if c == '&' {
+			s.op, s.prec = AndAnd, precAndAnd
+			s.tok = _Operator
+			break
+		}
+		s.op, s.prec = And, precMul
+		if c == '^' {
+			s.op = AndNot
+			c = s.getr()
+		}
+		goto assignop
+
+	case '|':
+		c = s.getr()
+		if c == '|' {
+			s.op, s.prec = OrOr, precOrOr
+			s.tok = _Operator
+			break
+		}
+		s.op, s.prec = Or, precAdd
+		goto assignop
+
+	case '~':
+		s.error("bitwise complement operator is ^")
+		fallthrough
+
+	case '^':
+		s.op, s.prec = Xor, precAdd
+		c = s.getr()
+		goto assignop
+
+	case '<':
+		c = s.getr()
+		if c == '=' {
+			s.op, s.prec = Leq, precCmp
+			s.tok = _Operator
+			break
+		}
+		if c == '<' {
+			s.op, s.prec = Shl, precMul
+			c = s.getr()
+			goto assignop
+		}
+		if c == '-' {
+			s.tok = _Arrow
+			break
+		}
+		s.ungetr()
+		s.op, s.prec = Lss, precCmp
+		s.tok = _Operator
+
+	case '>':
+		c = s.getr()
+		if c == '=' {
+			s.op, s.prec = Geq, precCmp
+			s.tok = _Operator
+			break
+		}
+		if c == '>' {
+			s.op, s.prec = Shr, precMul
+			c = s.getr()
+			goto assignop
+		}
+		s.ungetr()
+		s.op, s.prec = Gtr, precCmp
+		s.tok = _Operator
+
+	case '=':
+		if s.getr() == '=' {
+			s.op, s.prec = Eql, precCmp
+			s.tok = _Operator
+			break
+		}
+		s.ungetr()
+		s.tok = _Assign
+
+	case '!':
+		if s.getr() == '=' {
+			s.op, s.prec = Neq, precCmp
+			s.tok = _Operator
+			break
+		}
+		s.ungetr()
+		s.op, s.prec = Not, 0
+		s.tok = _Operator
+
+	default:
+		s.tok = 0
+		s.error(fmt.Sprintf("illegal character %#U", c))
+		goto redo
+	}
+
+	return
+
+assignop:
+	if c == '=' {
+		s.tok = _AssignOp
+		return
+	}
+	s.ungetr()
+	s.tok = _Operator
+}
+
+func isLetter(c rune) bool {
+	return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_'
+}
+
+func isDigit(c rune) bool {
+	return '0' <= c && c <= '9'
+}
+
+func (s *scanner) ident() {
+	s.startLit()
+
+	// accelerate common case (7bit ASCII)
+	c := s.getr()
+	for isLetter(c) || isDigit(c) {
+		c = s.getr()
+	}
+
+	// general case
+	if c >= utf8.RuneSelf {
+		for unicode.IsLetter(c) || c == '_' || unicode.IsDigit(c) || s.isCompatRune(c, false) {
+			c = s.getr()
+		}
+	}
+	s.ungetr()
+
+	lit := s.stopLit()
+
+	// possibly a keyword
+	if len(lit) >= 2 {
+		if tok := keywordMap[hash(lit)]; tok != 0 && tokstrings[tok] == string(lit) {
+			s.nlsemi = contains(1<<_Break|1<<_Continue|1<<_Fallthrough|1<<_Return, tok)
+			s.tok = tok
+			return
+		}
+	}
+
+	s.nlsemi = true
+	s.lit = string(lit)
+	s.tok = _Name
+}
+
+func (s *scanner) isCompatRune(c rune, start bool) bool {
+	if !gcCompat || c < utf8.RuneSelf {
+		return false
+	}
+	if start && unicode.IsNumber(c) {
+		s.error(fmt.Sprintf("identifier cannot begin with digit %#U", c))
+	} else {
+		s.error(fmt.Sprintf("invalid identifier character %#U", c))
+	}
+	return true
+}
+
+// hash is a perfect hash function for keywords.
+// It assumes that s has at least length 2.
+func hash(s []byte) uint {
+	return (uint(s[0])<<4 ^ uint(s[1]) + uint(len(s))) & uint(len(keywordMap)-1)
+}
+
+var keywordMap [1 << 6]token // size must be power of two
+
+func init() {
+	// populate keywordMap
+	for tok := _Break; tok <= _Var; tok++ {
+		h := hash([]byte(tokstrings[tok]))
+		if keywordMap[h] != 0 {
+			panic("imperfect hash")
+		}
+		keywordMap[h] = tok
+	}
+}
+
+func (s *scanner) number(c rune) {
+	s.startLit()
+
+	if c != '.' {
+		s.kind = IntLit // until proven otherwise
+		if c == '0' {
+			c = s.getr()
+			if c == 'x' || c == 'X' {
+				// hex
+				c = s.getr()
+				hasDigit := false
+				for isDigit(c) || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
+					c = s.getr()
+					hasDigit = true
+				}
+				if !hasDigit {
+					s.error("malformed hex constant")
+				}
+				goto done
+			}
+
+			// decimal 0, octal, or float
+			has8or9 := false
+			for isDigit(c) {
+				if c > '7' {
+					has8or9 = true
+				}
+				c = s.getr()
+			}
+			if c != '.' && c != 'e' && c != 'E' && c != 'i' {
+				// octal
+				if has8or9 {
+					s.error("malformed octal constant")
+				}
+				goto done
+			}
+
+		} else {
+			// decimal or float
+			for isDigit(c) {
+				c = s.getr()
+			}
+		}
+	}
+
+	// float
+	if c == '.' {
+		s.kind = FloatLit
+		c = s.getr()
+		for isDigit(c) {
+			c = s.getr()
+		}
+	}
+
+	// exponent
+	if c == 'e' || c == 'E' {
+		s.kind = FloatLit
+		c = s.getr()
+		if c == '-' || c == '+' {
+			c = s.getr()
+		}
+		if !isDigit(c) {
+			s.error("malformed floating-point constant exponent")
+		}
+		for isDigit(c) {
+			c = s.getr()
+		}
+	}
+
+	// complex
+	if c == 'i' {
+		s.kind = ImagLit
+		s.getr()
+	}
+
+done:
+	s.ungetr()
+	s.nlsemi = true
+	s.lit = string(s.stopLit())
+	s.tok = _Literal
+}
+
+func (s *scanner) stdString() {
+	s.startLit()
+
+	for {
+		r := s.getr()
+		if r == '"' {
+			break
+		}
+		if r == '\\' {
+			s.escape('"')
+			continue
+		}
+		if r == '\n' {
+			s.ungetr() // assume newline is not part of literal
+			s.error("newline in string")
+			break
+		}
+		if r < 0 {
+			s.error_at(s.pos, s.line, "string not terminated")
+			break
+		}
+	}
+
+	s.nlsemi = true
+	s.lit = string(s.stopLit())
+	s.kind = StringLit
+	s.tok = _Literal
+}
+
+func (s *scanner) rawString() {
+	s.startLit()
+
+	for {
+		r := s.getr()
+		if r == '`' {
+			break
+		}
+		if r < 0 {
+			s.error_at(s.pos, s.line, "string not terminated")
+			break
+		}
+	}
+	// We leave CRs in the string since they are part of the
+	// literal (even though they are not part of the literal
+	// value).
+
+	s.nlsemi = true
+	s.lit = string(s.stopLit())
+	s.kind = StringLit
+	s.tok = _Literal
+}
+
+func (s *scanner) rune() {
+	s.startLit()
+
+	r := s.getr()
+	ok := false
+	if r == '\'' {
+		s.error("empty character literal or unescaped ' in character literal")
+	} else if r == '\n' {
+		s.ungetr() // assume newline is not part of literal
+		s.error("newline in character literal")
+	} else {
+		ok = true
+		if r == '\\' {
+			ok = s.escape('\'')
+		}
+	}
+
+	r = s.getr()
+	if r != '\'' {
+		// only report error if we're ok so far
+		if ok {
+			s.error("missing '")
+		}
+		s.ungetr()
+	}
+
+	s.nlsemi = true
+	s.lit = string(s.stopLit())
+	s.kind = RuneLit
+	s.tok = _Literal
+}
+
+func (s *scanner) lineComment() {
+	// recognize pragmas
+	var prefix string
+	r := s.getr()
+	if s.pragh == nil {
+		goto skip
+	}
+
+	switch r {
+	case 'g':
+		prefix = "go:"
+	case 'l':
+		prefix = "line "
+	default:
+		goto skip
+	}
+
+	s.startLit()
+	for _, m := range prefix {
+		if r != m {
+			s.stopLit()
+			goto skip
+		}
+		r = s.getr()
+	}
+
+	for r >= 0 {
+		if r == '\n' {
+			s.ungetr()
+			break
+		}
+		r = s.getr()
+	}
+	s.pragma |= s.pragh(0, s.line, strings.TrimSuffix(string(s.stopLit()), "\r"))
+	return
+
+skip:
+	// consume line
+	for r != '\n' && r >= 0 {
+		r = s.getr()
+	}
+	s.ungetr() // don't consume '\n' - needed for nlsemi logic
+}
+
+func (s *scanner) fullComment() {
+	for {
+		r := s.getr()
+		for r == '*' {
+			r = s.getr()
+			if r == '/' {
+				return
+			}
+		}
+		if r < 0 {
+			s.error_at(s.pos, s.line, "comment not terminated")
+			return
+		}
+	}
+}
+
+func (s *scanner) escape(quote rune) bool {
+	var n int
+	var base, max uint32
+
+	c := s.getr()
+	switch c {
+	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
+		return true
+	case '0', '1', '2', '3', '4', '5', '6', '7':
+		n, base, max = 3, 8, 255
+	case 'x':
+		c = s.getr()
+		n, base, max = 2, 16, 255
+	case 'u':
+		c = s.getr()
+		n, base, max = 4, 16, unicode.MaxRune
+	case 'U':
+		c = s.getr()
+		n, base, max = 8, 16, unicode.MaxRune
+	default:
+		if c < 0 {
+			return true // complain in caller about EOF
+		}
+		s.error("unknown escape sequence")
+		return false
+	}
+
+	var x uint32
+	for i := n; i > 0; i-- {
+		d := base
+		switch {
+		case isDigit(c):
+			d = uint32(c) - '0'
+		case 'a' <= c && c <= 'f':
+			d = uint32(c) - ('a' - 10)
+		case 'A' <= c && c <= 'F':
+			d = uint32(c) - ('A' - 10)
+		}
+		if d >= base {
+			if c < 0 {
+				return true // complain in caller about EOF
+			}
+			if gcCompat {
+				name := "hex"
+				if base == 8 {
+					name = "octal"
+				}
+				s.error(fmt.Sprintf("non-%s character in escape sequence: %c", name, c))
+			} else {
+				if c != quote {
+					s.error(fmt.Sprintf("illegal character %#U in escape sequence", c))
+				} else {
+					s.error("escape sequence incomplete")
+				}
+			}
+			s.ungetr()
+			return false
+		}
+		// d < base
+		x = x*base + d
+		c = s.getr()
+	}
+	s.ungetr()
+
+	if x > max && base == 8 {
+		s.error(fmt.Sprintf("octal escape value > 255: %d", x))
+		return false
+	}
+
+	if x > max || 0xD800 <= x && x < 0xE000 /* surrogate range */ {
+		s.error("escape sequence is invalid Unicode code point")
+		return false
+	}
+
+	return true
+}
diff --git a/src/cmd/compile/internal/syntax/scanner_test.go b/src/cmd/compile/internal/syntax/scanner_test.go
new file mode 100644
index 0000000..0e81c4e
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/scanner_test.go
@@ -0,0 +1,355 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+	"fmt"
+	"os"
+	"testing"
+)
+
+func TestScanner(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping test in short mode")
+	}
+
+	src, err := os.Open("parser.go")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer src.Close()
+
+	var s scanner
+	s.init(src, nil, nil)
+	for {
+		s.next()
+		if s.tok == _EOF {
+			break
+		}
+		switch s.tok {
+		case _Name:
+			fmt.Println(s.line, s.tok, "=>", s.lit)
+		case _Operator:
+			fmt.Println(s.line, s.tok, "=>", s.op, s.prec)
+		default:
+			fmt.Println(s.line, s.tok)
+		}
+	}
+}
+
+func TestTokens(t *testing.T) {
+	// make source
+	var buf []byte
+	for i, s := range sampleTokens {
+		buf = append(buf, "\t\t\t\t"[:i&3]...)     // leading indentation
+		buf = append(buf, s.src...)                // token
+		buf = append(buf, "        "[:i&7]...)     // trailing spaces
+		buf = append(buf, "/* foo */ // bar\n"...) // comments
+	}
+
+	// scan source
+	var got scanner
+	got.init(&bytesReader{buf}, nil, nil)
+	got.next()
+	for i, want := range sampleTokens {
+		nlsemi := false
+
+		if got.line != i+1 {
+			t.Errorf("got line %d; want %d", got.line, i+1)
+		}
+
+		if got.tok != want.tok {
+			t.Errorf("got tok = %s; want %s", got.tok, want.tok)
+			continue
+		}
+
+		switch want.tok {
+		case _Name, _Literal:
+			if got.lit != want.src {
+				t.Errorf("got lit = %q; want %q", got.lit, want.src)
+				continue
+			}
+			nlsemi = true
+
+		case _Operator, _AssignOp, _IncOp:
+			if got.op != want.op {
+				t.Errorf("got op = %s; want %s", got.op, want.op)
+				continue
+			}
+			if got.prec != want.prec {
+				t.Errorf("got prec = %d; want %d", got.prec, want.prec)
+				continue
+			}
+			nlsemi = want.tok == _IncOp
+
+		case _Rparen, _Rbrack, _Rbrace, _Break, _Continue, _Fallthrough, _Return:
+			nlsemi = true
+		}
+
+		if nlsemi {
+			got.next()
+			if got.tok != _Semi {
+				t.Errorf("got tok = %s; want ;", got.tok)
+				continue
+			}
+		}
+
+		got.next()
+	}
+
+	if got.tok != _EOF {
+		t.Errorf("got %q; want _EOF", got.tok)
+	}
+}
+
+var sampleTokens = [...]struct {
+	tok  token
+	src  string
+	op   Operator
+	prec int
+}{
+	// name samples
+	{_Name, "x", 0, 0},
+	{_Name, "X123", 0, 0},
+	{_Name, "foo", 0, 0},
+	{_Name, "Foo123", 0, 0},
+	{_Name, "foo_bar", 0, 0},
+	{_Name, "_", 0, 0},
+	{_Name, "_foobar", 0, 0},
+	{_Name, "a۰۱۸", 0, 0},
+	{_Name, "foo६४", 0, 0},
+	{_Name, "bar9876", 0, 0},
+	{_Name, "ŝ", 0, 0},
+	{_Name, "ŝfoo", 0, 0},
+
+	// literal samples
+	{_Literal, "0", 0, 0},
+	{_Literal, "1", 0, 0},
+	{_Literal, "12345", 0, 0},
+	{_Literal, "123456789012345678890123456789012345678890", 0, 0},
+	{_Literal, "01234567", 0, 0},
+	{_Literal, "0x0", 0, 0},
+	{_Literal, "0xcafebabe", 0, 0},
+	{_Literal, "0.", 0, 0},
+	{_Literal, "0.e0", 0, 0},
+	{_Literal, "0.e-1", 0, 0},
+	{_Literal, "0.e+123", 0, 0},
+	{_Literal, ".0", 0, 0},
+	{_Literal, ".0E00", 0, 0},
+	{_Literal, ".0E-0123", 0, 0},
+	{_Literal, ".0E+12345678901234567890", 0, 0},
+	{_Literal, ".45e1", 0, 0},
+	{_Literal, "3.14159265", 0, 0},
+	{_Literal, "1e0", 0, 0},
+	{_Literal, "1e+100", 0, 0},
+	{_Literal, "1e-100", 0, 0},
+	{_Literal, "2.71828e-1000", 0, 0},
+	{_Literal, "0i", 0, 0},
+	{_Literal, "1i", 0, 0},
+	{_Literal, "012345678901234567889i", 0, 0},
+	{_Literal, "123456789012345678890i", 0, 0},
+	{_Literal, "0.i", 0, 0},
+	{_Literal, ".0i", 0, 0},
+	{_Literal, "3.14159265i", 0, 0},
+	{_Literal, "1e0i", 0, 0},
+	{_Literal, "1e+100i", 0, 0},
+	{_Literal, "1e-100i", 0, 0},
+	{_Literal, "2.71828e-1000i", 0, 0},
+	{_Literal, "'a'", 0, 0},
+	{_Literal, "'\\000'", 0, 0},
+	{_Literal, "'\\xFF'", 0, 0},
+	{_Literal, "'\\uff16'", 0, 0},
+	{_Literal, "'\\U0000ff16'", 0, 0},
+	{_Literal, "`foobar`", 0, 0},
+	{_Literal, "`foo\tbar`", 0, 0},
+	{_Literal, "`\r`", 0, 0},
+
+	// operators
+	{_Operator, "||", OrOr, precOrOr},
+
+	{_Operator, "&&", AndAnd, precAndAnd},
+
+	{_Operator, "==", Eql, precCmp},
+	{_Operator, "!=", Neq, precCmp},
+	{_Operator, "<", Lss, precCmp},
+	{_Operator, "<=", Leq, precCmp},
+	{_Operator, ">", Gtr, precCmp},
+	{_Operator, ">=", Geq, precCmp},
+
+	{_Operator, "+", Add, precAdd},
+	{_Operator, "-", Sub, precAdd},
+	{_Operator, "|", Or, precAdd},
+	{_Operator, "^", Xor, precAdd},
+
+	{_Star, "*", Mul, precMul},
+	{_Operator, "/", Div, precMul},
+	{_Operator, "%", Rem, precMul},
+	{_Operator, "&", And, precMul},
+	{_Operator, "&^", AndNot, precMul},
+	{_Operator, "<<", Shl, precMul},
+	{_Operator, ">>", Shr, precMul},
+
+	// assignment operations
+	{_AssignOp, "+=", Add, precAdd},
+	{_AssignOp, "-=", Sub, precAdd},
+	{_AssignOp, "|=", Or, precAdd},
+	{_AssignOp, "^=", Xor, precAdd},
+
+	{_AssignOp, "*=", Mul, precMul},
+	{_AssignOp, "/=", Div, precMul},
+	{_AssignOp, "%=", Rem, precMul},
+	{_AssignOp, "&=", And, precMul},
+	{_AssignOp, "&^=", AndNot, precMul},
+	{_AssignOp, "<<=", Shl, precMul},
+	{_AssignOp, ">>=", Shr, precMul},
+
+	// other operations
+	{_IncOp, "++", Add, precAdd},
+	{_IncOp, "--", Sub, precAdd},
+	{_Assign, "=", 0, 0},
+	{_Define, ":=", 0, 0},
+	{_Arrow, "<-", 0, 0},
+
+	// delimiters
+	{_Lparen, "(", 0, 0},
+	{_Lbrack, "[", 0, 0},
+	{_Lbrace, "{", 0, 0},
+	{_Rparen, ")", 0, 0},
+	{_Rbrack, "]", 0, 0},
+	{_Rbrace, "}", 0, 0},
+	{_Comma, ",", 0, 0},
+	{_Semi, ";", 0, 0},
+	{_Colon, ":", 0, 0},
+	{_Dot, ".", 0, 0},
+	{_DotDotDot, "...", 0, 0},
+
+	// keywords
+	{_Break, "break", 0, 0},
+	{_Case, "case", 0, 0},
+	{_Chan, "chan", 0, 0},
+	{_Const, "const", 0, 0},
+	{_Continue, "continue", 0, 0},
+	{_Default, "default", 0, 0},
+	{_Defer, "defer", 0, 0},
+	{_Else, "else", 0, 0},
+	{_Fallthrough, "fallthrough", 0, 0},
+	{_For, "for", 0, 0},
+	{_Func, "func", 0, 0},
+	{_Go, "go", 0, 0},
+	{_Goto, "goto", 0, 0},
+	{_If, "if", 0, 0},
+	{_Import, "import", 0, 0},
+	{_Interface, "interface", 0, 0},
+	{_Map, "map", 0, 0},
+	{_Package, "package", 0, 0},
+	{_Range, "range", 0, 0},
+	{_Return, "return", 0, 0},
+	{_Select, "select", 0, 0},
+	{_Struct, "struct", 0, 0},
+	{_Switch, "switch", 0, 0},
+	{_Type, "type", 0, 0},
+	{_Var, "var", 0, 0},
+}
+
+func TestScanErrors(t *testing.T) {
+	for _, test := range []struct {
+		src, msg  string
+		pos, line int
+	}{
+		// Note: Positions for lexical errors are the earliest position
+		// where the error is apparent, not the beginning of the respective
+		// token.
+
+		// rune-level errors
+		{"fo\x00o", "invalid NUL character", 2, 1},
+		{"foo\n\ufeff bar", "invalid BOM in the middle of the file", 4, 2},
+		{"foo\n\n\xff    ", "invalid UTF-8 encoding", 5, 3},
+
+		// token-level errors
+		{"x + ~y", "bitwise complement operator is ^", 4, 1},
+		{"foo$bar = 0", "illegal character U+0024 '$'", 3, 1},
+		{"const x = 0xyz", "malformed hex constant", 12, 1},
+		{"0123456789", "malformed octal constant", 10, 1},
+		{"0123456789. /* foobar", "comment not terminated", 12, 1},   // valid float constant
+		{"0123456789e0 /*\nfoobar", "comment not terminated", 13, 1}, // valid float constant
+		{"var a, b = 08, 07\n", "malformed octal constant", 13, 1},
+		{"(x + 1.0e+x)", "malformed floating-point constant exponent", 10, 1},
+
+		{`''`, "empty character literal or unescaped ' in character literal", 1, 1},
+		{"'\n", "newline in character literal", 1, 1},
+		{`'\`, "missing '", 2, 1},
+		{`'\'`, "missing '", 3, 1},
+		{`'\x`, "missing '", 3, 1},
+		{`'\x'`, "non-hex character in escape sequence: '", 3, 1},
+		{`'\y'`, "unknown escape sequence", 2, 1},
+		{`'\x0'`, "non-hex character in escape sequence: '", 4, 1},
+		{`'\00'`, "non-octal character in escape sequence: '", 4, 1},
+		{`'\377' /*`, "comment not terminated", 7, 1}, // valid octal escape
+		{`'\378`, "non-octal character in escape sequence: 8", 4, 1},
+		{`'\400'`, "octal escape value > 255: 256", 5, 1},
+		{`'xx`, "missing '", 2, 1},
+
+		{"\"\n", "newline in string", 1, 1},
+		{`"`, "string not terminated", 0, 1},
+		{`"foo`, "string not terminated", 0, 1},
+		{"`", "string not terminated", 0, 1},
+		{"`foo", "string not terminated", 0, 1},
+		{"/*/", "comment not terminated", 0, 1},
+		{"/*\n\nfoo", "comment not terminated", 0, 1},
+		{"/*\n\nfoo", "comment not terminated", 0, 1},
+		{`"\`, "string not terminated", 0, 1},
+		{`"\"`, "string not terminated", 0, 1},
+		{`"\x`, "string not terminated", 0, 1},
+		{`"\x"`, "non-hex character in escape sequence: \"", 3, 1},
+		{`"\y"`, "unknown escape sequence", 2, 1},
+		{`"\x0"`, "non-hex character in escape sequence: \"", 4, 1},
+		{`"\00"`, "non-octal character in escape sequence: \"", 4, 1},
+		{`"\377" /*`, "comment not terminated", 7, 1}, // valid octal escape
+		{`"\378"`, "non-octal character in escape sequence: 8", 4, 1},
+		{`"\400"`, "octal escape value > 255: 256", 5, 1},
+
+		{`s := "foo\z"`, "unknown escape sequence", 10, 1},
+		{`s := "foo\z00\nbar"`, "unknown escape sequence", 10, 1},
+		{`"\x`, "string not terminated", 0, 1},
+		{`"\x"`, "non-hex character in escape sequence: \"", 3, 1},
+		{`var s string = "\x"`, "non-hex character in escape sequence: \"", 18, 1},
+		{`return "\Uffffffff"`, "escape sequence is invalid Unicode code point", 18, 1},
+
+		// former problem cases
+		{"package p\n\n\xef", "invalid UTF-8 encoding", 11, 3},
+	} {
+		var s scanner
+		nerrors := 0
+		s.init(&bytesReader{[]byte(test.src)}, func(err error) {
+			nerrors++
+			// only check the first error
+			e := err.(Error) // we know it's an Error
+			if nerrors == 1 {
+				if e.Msg != test.msg {
+					t.Errorf("%q: got msg = %q; want %q", test.src, e.Msg, test.msg)
+				}
+				if e.Pos != test.pos {
+					t.Errorf("%q: got pos = %d; want %d", test.src, e.Pos, test.pos)
+				}
+				if e.Line != test.line {
+					t.Errorf("%q: got line = %d; want %d", test.src, e.Line, test.line)
+				}
+			} else if nerrors > 1 {
+				t.Errorf("%q: got unexpected %q at pos = %d, line = %d", test.src, e.Msg, e.Pos, e.Line)
+			}
+		}, nil)
+
+		for {
+			s.next()
+			if s.tok == _EOF {
+				break
+			}
+		}
+
+		if nerrors == 0 {
+			t.Errorf("%q: got no error; want %q", test.src, test.msg)
+		}
+	}
+}
diff --git a/src/cmd/compile/internal/syntax/source.go b/src/cmd/compile/internal/syntax/source.go
new file mode 100644
index 0000000..05a1196
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/source.go
@@ -0,0 +1,181 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+	"io"
+	"unicode/utf8"
+)
+
+// buf [...read...|...|...unread...|s|...free...]
+//         ^      ^   ^            ^
+//         |      |   |            |
+//        suf     r0  r            w
+
+type source struct {
+	src   io.Reader
+	errh  ErrorHandler
+	first error // first error encountered
+
+	// source buffer
+	buf         [4 << 10]byte
+	offs        int   // source offset of buf
+	r0, r, w    int   // previous/current read and write buf positions, excluding sentinel
+	line0, line int   // previous/current line
+	err         error // pending io error
+
+	// literal buffer
+	lit []byte // literal prefix
+	suf int    // literal suffix; suf >= 0 means we are scanning a literal
+}
+
+func (s *source) init(src io.Reader, errh ErrorHandler) {
+	s.src = src
+	s.errh = errh
+	s.first = nil
+
+	s.buf[0] = utf8.RuneSelf // terminate with sentinel
+	s.offs = 0
+	s.r0, s.r, s.w = 0, 0, 0
+	s.line0, s.line = 1, 1
+	s.err = nil
+
+	s.lit = s.lit[:0]
+	s.suf = -1
+}
+
+func (s *source) error(msg string) {
+	s.error_at(s.pos0(), s.line0, msg)
+}
+
+func (s *source) error_at(pos, line int, msg string) {
+	err := Error{pos, line, msg}
+	if s.first == nil {
+		s.first = err
+	}
+	if s.errh == nil {
+		panic(s.first)
+	}
+	s.errh(err)
+}
+
+// pos0 returns the byte position of the last character read.
+func (s *source) pos0() int {
+	return s.offs + s.r0
+}
+
+func (s *source) ungetr() {
+	s.r, s.line = s.r0, s.line0
+}
+
+func (s *source) getr() rune {
+redo:
+	s.r0, s.line0 = s.r, s.line
+
+	// We could avoid at least one test that is always taken in the
+	// for loop below by duplicating the common case code (ASCII)
+	// here since we always have at least the sentinel (utf8.RuneSelf)
+	// in the buffer. Measure and optimize if necessary.
+
+	// make sure we have at least one rune in buffer, or we are at EOF
+	for s.r+utf8.UTFMax > s.w && !utf8.FullRune(s.buf[s.r:s.w]) && s.err == nil && s.w-s.r < len(s.buf) {
+		s.fill() // s.w-s.r < len(s.buf) => buffer is not full
+	}
+
+	// common case: ASCII and enough bytes
+	// (invariant: s.buf[s.w] == utf8.RuneSelf)
+	if b := s.buf[s.r]; b < utf8.RuneSelf {
+		s.r++
+		if b == 0 {
+			s.error("invalid NUL character")
+			goto redo
+		}
+		if b == '\n' {
+			s.line++
+		}
+		return rune(b)
+	}
+
+	// EOF
+	if s.r == s.w {
+		if s.err != io.EOF {
+			s.error(s.err.Error())
+		}
+		return -1
+	}
+
+	// uncommon case: not ASCII
+	r, w := utf8.DecodeRune(s.buf[s.r:s.w])
+	s.r += w
+
+	if r == utf8.RuneError && w == 1 {
+		s.error("invalid UTF-8 encoding")
+		goto redo
+	}
+
+	// BOM's are only allowed as the first character in a file
+	const BOM = 0xfeff
+	if r == BOM {
+		if s.r0 > 0 { // s.r0 is always > 0 after 1st character (fill will set it to 1)
+			s.error("invalid BOM in the middle of the file")
+		}
+		goto redo
+	}
+
+	return r
+}
+
+func (s *source) fill() {
+	// Slide unread bytes to beginning but preserve last read char
+	// (for one ungetr call) plus one extra byte (for a 2nd ungetr
+	// call, only for ".." character sequence and float literals
+	// starting with ".").
+	if s.r0 > 1 {
+		// save literal prefix, if any
+		// (We see at most one ungetr call while reading
+		// a literal, so make sure s.r0 remains in buf.)
+		if s.suf >= 0 {
+			s.lit = append(s.lit, s.buf[s.suf:s.r0]...)
+			s.suf = 1 // == s.r0 after slide below
+		}
+		s.offs += s.r0 - 1
+		r := s.r - s.r0 + 1 // last read char plus one byte
+		s.w = r + copy(s.buf[r:], s.buf[s.r:s.w])
+		s.r = r
+		s.r0 = 1
+	}
+
+	// read more data: try a limited number of times
+	for i := 100; i > 0; i-- {
+		n, err := s.src.Read(s.buf[s.w : len(s.buf)-1]) // -1 to leave space for sentinel
+		if n < 0 {
+			panic("negative read") // incorrect underlying io.Reader implementation
+		}
+		s.w += n
+		if n > 0 || err != nil {
+			s.buf[s.w] = utf8.RuneSelf // sentinel
+			if err != nil {
+				s.err = err
+			}
+			return
+		}
+	}
+
+	s.err = io.ErrNoProgress
+}
+
+func (s *source) startLit() {
+	s.suf = s.r0
+	s.lit = s.lit[:0] // reuse lit
+}
+
+func (s *source) stopLit() []byte {
+	lit := s.buf[s.suf:s.r]
+	if len(s.lit) > 0 {
+		lit = append(s.lit, lit...)
+	}
+	s.suf = -1 // no pending literal
+	return lit
+}
diff --git a/src/cmd/compile/internal/syntax/syntax.go b/src/cmd/compile/internal/syntax/syntax.go
new file mode 100644
index 0000000..b1e56ee
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/syntax.go
@@ -0,0 +1,100 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+	"fmt"
+	"io"
+	"os"
+)
+
+// Mode describes the parser mode.
+type Mode uint
+
+// Error describes a syntax error. Error implements the error interface.
+type Error struct {
+	// TODO(gri) decide what we really need here
+	Pos  int // byte offset from file start
+	Line int // line (starting with 1)
+	Msg  string
+}
+
+func (err Error) Error() string {
+	return fmt.Sprintf("%d: %s", err.Line, err.Msg)
+}
+
+var _ error = Error{} // verify that Error implements error
+
+// An ErrorHandler is called for each error encountered reading a .go file.
+type ErrorHandler func(err error)
+
+// A Pragma value is a set of flags that augment a function or
+// type declaration. Callers may assign meaning to the flags as
+// appropriate.
+type Pragma uint16
+
+// A PragmaHandler is used to process //line and //go: directives as
+// they're scanned. The returned Pragma value will be unioned into the
+// next FuncDecl node.
+type PragmaHandler func(pos, line int, text string) Pragma
+
+// Parse parses a single Go source file from src and returns the corresponding
+// syntax tree. If there are syntax errors, Parse will return the first error
+// encountered.
+//
+// If errh != nil, it is called with each error encountered, and Parse will
+// process as much source as possible. If errh is nil, Parse will terminate
+// immediately upon encountering an error.
+//
+// If a PragmaHandler is provided, it is called with each pragma encountered.
+//
+// The Mode argument is currently ignored.
+func Parse(src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, err error) {
+	defer func() {
+		if p := recover(); p != nil {
+			var ok bool
+			if err, ok = p.(Error); ok {
+				return
+			}
+			panic(p)
+		}
+	}()
+
+	var p parser
+	p.init(src, errh, pragh)
+	p.next()
+	return p.file(), p.first
+}
+
+// ParseBytes behaves like Parse but it reads the source from the []byte slice provided.
+func ParseBytes(src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
+	return Parse(&bytesReader{src}, errh, pragh, mode)
+}
+
+type bytesReader struct {
+	data []byte
+}
+
+func (r *bytesReader) Read(p []byte) (int, error) {
+	if len(r.data) > 0 {
+		n := copy(p, r.data)
+		r.data = r.data[n:]
+		return n, nil
+	}
+	return 0, io.EOF
+}
+
+// ParseFile behaves like Parse but it reads the source from the named file.
+func ParseFile(filename string, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) {
+	src, err := os.Open(filename)
+	if err != nil {
+		if errh != nil {
+			errh(err)
+		}
+		return nil, err
+	}
+	defer src.Close()
+	return Parse(src, errh, pragh, mode)
+}
diff --git a/src/cmd/compile/internal/syntax/tokens.go b/src/cmd/compile/internal/syntax/tokens.go
new file mode 100644
index 0000000..bd0118a
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/tokens.go
@@ -0,0 +1,263 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import "fmt"
+
+type token uint
+
+const (
+	_ token = iota
+	_EOF
+
+	// names and literals
+	_Name
+	_Literal
+
+	// operators and operations
+	_Operator // excluding '*' (_Star)
+	_AssignOp
+	_IncOp
+	_Assign
+	_Define
+	_Arrow
+	_Star
+
+	// delimitors
+	_Lparen
+	_Lbrack
+	_Lbrace
+	_Rparen
+	_Rbrack
+	_Rbrace
+	_Comma
+	_Semi
+	_Colon
+	_Dot
+	_DotDotDot
+
+	// keywords
+	_Break
+	_Case
+	_Chan
+	_Const
+	_Continue
+	_Default
+	_Defer
+	_Else
+	_Fallthrough
+	_For
+	_Func
+	_Go
+	_Goto
+	_If
+	_Import
+	_Interface
+	_Map
+	_Package
+	_Range
+	_Return
+	_Select
+	_Struct
+	_Switch
+	_Type
+	_Var
+
+	tokenCount
+)
+
+const (
+	// for BranchStmt
+	Break       = _Break
+	Continue    = _Continue
+	Fallthrough = _Fallthrough
+	Goto        = _Goto
+
+	// for CallStmt
+	Go    = _Go
+	Defer = _Defer
+)
+
+var tokstrings = [...]string{
+	// source control
+	_EOF: "EOF",
+
+	// names and literals
+	_Name:    "name",
+	_Literal: "literal",
+
+	// operators and operations
+	_Operator: "op",
+	_AssignOp: "op=",
+	_IncOp:    "opop",
+	_Assign:   "=",
+	_Define:   ":=",
+	_Arrow:    "<-",
+	_Star:     "*",
+
+	// delimitors
+	_Lparen:    "(",
+	_Lbrack:    "[",
+	_Lbrace:    "{",
+	_Rparen:    ")",
+	_Rbrack:    "]",
+	_Rbrace:    "}",
+	_Comma:     ",",
+	_Semi:      ";",
+	_Colon:     ":",
+	_Dot:       ".",
+	_DotDotDot: "...",
+
+	// keywords
+	_Break:       "break",
+	_Case:        "case",
+	_Chan:        "chan",
+	_Const:       "const",
+	_Continue:    "continue",
+	_Default:     "default",
+	_Defer:       "defer",
+	_Else:        "else",
+	_Fallthrough: "fallthrough",
+	_For:         "for",
+	_Func:        "func",
+	_Go:          "go",
+	_Goto:        "goto",
+	_If:          "if",
+	_Import:      "import",
+	_Interface:   "interface",
+	_Map:         "map",
+	_Package:     "package",
+	_Range:       "range",
+	_Return:      "return",
+	_Select:      "select",
+	_Struct:      "struct",
+	_Switch:      "switch",
+	_Type:        "type",
+	_Var:         "var",
+}
+
+func (tok token) String() string {
+	var s string
+	if 0 <= tok && int(tok) < len(tokstrings) {
+		s = tokstrings[tok]
+	}
+	if s == "" {
+		s = fmt.Sprintf("<tok-%d>", tok)
+	}
+	return s
+}
+
+// Make sure we have at most 64 tokens so we can use them in a set.
+const _ uint64 = 1 << (tokenCount - 1)
+
+// contains reports whether tok is in tokset.
+func contains(tokset uint64, tok token) bool {
+	return tokset&(1<<tok) != 0
+}
+
+type LitKind uint
+
+const (
+	IntLit LitKind = iota
+	FloatLit
+	ImagLit
+	RuneLit
+	StringLit
+)
+
+type Operator uint
+
+const (
+	_    Operator = iota
+	Def           // :=
+	Not           // !
+	Recv          // <-
+
+	// precOrOr
+	OrOr // ||
+
+	// precAndAnd
+	AndAnd // &&
+
+	// precCmp
+	Eql // ==
+	Neq // !=
+	Lss // <
+	Leq // <=
+	Gtr // >
+	Geq // >=
+
+	// precAdd
+	Add // +
+	Sub // -
+	Or  // |
+	Xor // ^
+
+	// precMul
+	Mul    // *
+	Div    // /
+	Rem    // %
+	And    // &
+	AndNot // &^
+	Shl    // <<
+	Shr    // >>
+)
+
+var opstrings = [...]string{
+	// prec == 0
+	Def:  ":", // : in :=
+	Not:  "!",
+	Recv: "<-",
+
+	// precOrOr
+	OrOr: "||",
+
+	// precAndAnd
+	AndAnd: "&&",
+
+	// precCmp
+	Eql: "==",
+	Neq: "!=",
+	Lss: "<",
+	Leq: "<=",
+	Gtr: ">",
+	Geq: ">=",
+
+	// precAdd
+	Add: "+",
+	Sub: "-",
+	Or:  "|",
+	Xor: "^",
+
+	// precMul
+	Mul:    "*",
+	Div:    "/",
+	Rem:    "%",
+	And:    "&",
+	AndNot: "&^",
+	Shl:    "<<",
+	Shr:    ">>",
+}
+
+func (op Operator) String() string {
+	var s string
+	if 0 <= op && int(op) < len(opstrings) {
+		s = opstrings[op]
+	}
+	if s == "" {
+		s = fmt.Sprintf("<op-%d>", op)
+	}
+	return s
+}
+
+// Operator precedences
+const (
+	_ = iota
+	precOrOr
+	precAndAnd
+	precCmp
+	precAdd
+	precMul
+)
diff --git a/src/cmd/compile/internal/x86/387.go b/src/cmd/compile/internal/x86/387.go
new file mode 100644
index 0000000..248fec6
--- /dev/null
+++ b/src/cmd/compile/internal/x86/387.go
@@ -0,0 +1,357 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x86
+
+import (
+	"cmd/compile/internal/gc"
+	"cmd/compile/internal/ssa"
+	"cmd/internal/obj"
+	"cmd/internal/obj/x86"
+	"math"
+)
+
+// Generates code for v using 387 instructions.  Reports whether
+// the instruction was handled by this routine.
+func ssaGenValue387(s *gc.SSAGenState, v *ssa.Value) bool {
+	// The SSA compiler pretends that it has an SSE backend.
+	// If we don't have one of those, we need to translate
+	// all the SSE ops to equivalent 387 ops. That's what this
+	// function does.
+
+	switch v.Op {
+	case ssa.Op386MOVSSconst, ssa.Op386MOVSDconst:
+		p := gc.Prog(loadPush(v.Type))
+		p.From.Type = obj.TYPE_FCONST
+		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x86.REG_F0
+		popAndSave(s, v)
+		return true
+	case ssa.Op386MOVSSconst2, ssa.Op386MOVSDconst2:
+		p := gc.Prog(loadPush(v.Type))
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x86.REG_F0
+		popAndSave(s, v)
+		return true
+
+	case ssa.Op386MOVSSload, ssa.Op386MOVSDload, ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1, ssa.Op386MOVSSloadidx4, ssa.Op386MOVSDloadidx8:
+		p := gc.Prog(loadPush(v.Type))
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		switch v.Op {
+		case ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1:
+			p.From.Scale = 1
+			p.From.Index = v.Args[1].Reg()
+		case ssa.Op386MOVSSloadidx4:
+			p.From.Scale = 4
+			p.From.Index = v.Args[1].Reg()
+		case ssa.Op386MOVSDloadidx8:
+			p.From.Scale = 8
+			p.From.Index = v.Args[1].Reg()
+		}
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x86.REG_F0
+		popAndSave(s, v)
+		return true
+
+	case ssa.Op386MOVSSstore, ssa.Op386MOVSDstore:
+		// Push to-be-stored value on top of stack.
+		push(s, v.Args[1])
+
+		// Pop and store value.
+		var op obj.As
+		switch v.Op {
+		case ssa.Op386MOVSSstore:
+			op = x86.AFMOVFP
+		case ssa.Op386MOVSDstore:
+			op = x86.AFMOVDP
+		}
+		p := gc.Prog(op)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_F0
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+		return true
+
+	case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1, ssa.Op386MOVSSstoreidx4, ssa.Op386MOVSDstoreidx8:
+		push(s, v.Args[2])
+		var op obj.As
+		switch v.Op {
+		case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSSstoreidx4:
+			op = x86.AFMOVFP
+		case ssa.Op386MOVSDstoreidx1, ssa.Op386MOVSDstoreidx8:
+			op = x86.AFMOVDP
+		}
+		p := gc.Prog(op)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_F0
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+		switch v.Op {
+		case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1:
+			p.To.Scale = 1
+			p.To.Index = v.Args[1].Reg()
+		case ssa.Op386MOVSSstoreidx4:
+			p.To.Scale = 4
+			p.To.Index = v.Args[1].Reg()
+		case ssa.Op386MOVSDstoreidx8:
+			p.To.Scale = 8
+			p.To.Index = v.Args[1].Reg()
+		}
+		return true
+
+	case ssa.Op386ADDSS, ssa.Op386ADDSD, ssa.Op386SUBSS, ssa.Op386SUBSD,
+		ssa.Op386MULSS, ssa.Op386MULSD, ssa.Op386DIVSS, ssa.Op386DIVSD:
+		if v.Reg() != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+
+		// Push arg1 on top of stack
+		push(s, v.Args[1])
+
+		// Set precision if needed.  64 bits is the default.
+		switch v.Op {
+		case ssa.Op386ADDSS, ssa.Op386SUBSS, ssa.Op386MULSS, ssa.Op386DIVSS:
+			p := gc.Prog(x86.AFSTCW)
+			s.AddrScratch(&p.To)
+			p = gc.Prog(x86.AFLDCW)
+			p.From.Type = obj.TYPE_MEM
+			p.From.Name = obj.NAME_EXTERN
+			p.From.Sym = gc.Linksym(gc.Pkglookup("controlWord32", gc.Runtimepkg))
+		}
+
+		var op obj.As
+		switch v.Op {
+		case ssa.Op386ADDSS, ssa.Op386ADDSD:
+			op = x86.AFADDDP
+		case ssa.Op386SUBSS, ssa.Op386SUBSD:
+			op = x86.AFSUBDP
+		case ssa.Op386MULSS, ssa.Op386MULSD:
+			op = x86.AFMULDP
+		case ssa.Op386DIVSS, ssa.Op386DIVSD:
+			op = x86.AFDIVDP
+		}
+		p := gc.Prog(op)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_F0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = s.SSEto387[v.Reg()] + 1
+
+		// Restore precision if needed.
+		switch v.Op {
+		case ssa.Op386ADDSS, ssa.Op386SUBSS, ssa.Op386MULSS, ssa.Op386DIVSS:
+			p := gc.Prog(x86.AFLDCW)
+			s.AddrScratch(&p.From)
+		}
+
+		return true
+
+	case ssa.Op386UCOMISS, ssa.Op386UCOMISD:
+		push(s, v.Args[0])
+
+		// Compare.
+		p := gc.Prog(x86.AFUCOMP)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_F0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = s.SSEto387[v.Args[1].Reg()] + 1
+
+		// Save AX.
+		p = gc.Prog(x86.AMOVL)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_AX
+		s.AddrScratch(&p.To)
+
+		// Move status word into AX.
+		p = gc.Prog(x86.AFSTSW)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x86.REG_AX
+
+		// Then move the flags we need to the integer flags.
+		gc.Prog(x86.ASAHF)
+
+		// Restore AX.
+		p = gc.Prog(x86.AMOVL)
+		s.AddrScratch(&p.From)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x86.REG_AX
+
+		return true
+
+	case ssa.Op386SQRTSD:
+		push(s, v.Args[0])
+		gc.Prog(x86.AFSQRT)
+		popAndSave(s, v)
+		return true
+
+	case ssa.Op386FCHS:
+		push(s, v.Args[0])
+		gc.Prog(x86.AFCHS)
+		popAndSave(s, v)
+		return true
+
+	case ssa.Op386CVTSL2SS, ssa.Op386CVTSL2SD:
+		p := gc.Prog(x86.AMOVL)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		s.AddrScratch(&p.To)
+		p = gc.Prog(x86.AFMOVL)
+		s.AddrScratch(&p.From)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x86.REG_F0
+		popAndSave(s, v)
+		return true
+
+	case ssa.Op386CVTTSD2SL, ssa.Op386CVTTSS2SL:
+		push(s, v.Args[0])
+
+		// Save control word.
+		p := gc.Prog(x86.AFSTCW)
+		s.AddrScratch(&p.To)
+		p.To.Offset += 4
+
+		// Load control word which truncates (rounds towards zero).
+		p = gc.Prog(x86.AFLDCW)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Name = obj.NAME_EXTERN
+		p.From.Sym = gc.Linksym(gc.Pkglookup("controlWord64trunc", gc.Runtimepkg))
+
+		// Now do the conversion.
+		p = gc.Prog(x86.AFMOVLP)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_F0
+		s.AddrScratch(&p.To)
+		p = gc.Prog(x86.AMOVL)
+		s.AddrScratch(&p.From)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+		// Restore control word.
+		p = gc.Prog(x86.AFLDCW)
+		s.AddrScratch(&p.From)
+		p.From.Offset += 4
+		return true
+
+	case ssa.Op386CVTSS2SD:
+		// float32 -> float64 is a nop
+		push(s, v.Args[0])
+		popAndSave(s, v)
+		return true
+
+	case ssa.Op386CVTSD2SS:
+		// Round to nearest float32.
+		push(s, v.Args[0])
+		p := gc.Prog(x86.AFMOVFP)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_F0
+		s.AddrScratch(&p.To)
+		p = gc.Prog(x86.AFMOVF)
+		s.AddrScratch(&p.From)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x86.REG_F0
+		popAndSave(s, v)
+		return true
+
+	case ssa.OpLoadReg:
+		if !v.Type.IsFloat() {
+			return false
+		}
+		// Load+push the value we need.
+		p := gc.Prog(loadPush(v.Type))
+		gc.AddrAuto(&p.From, v.Args[0])
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x86.REG_F0
+		// Move the value to its assigned register.
+		popAndSave(s, v)
+		return true
+
+	case ssa.OpStoreReg:
+		if !v.Type.IsFloat() {
+			return false
+		}
+		push(s, v.Args[0])
+		var op obj.As
+		switch v.Type.Size() {
+		case 4:
+			op = x86.AFMOVFP
+		case 8:
+			op = x86.AFMOVDP
+		}
+		p := gc.Prog(op)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_F0
+		gc.AddrAuto(&p.To, v)
+		return true
+
+	case ssa.OpCopy:
+		if !v.Type.IsFloat() {
+			return false
+		}
+		push(s, v.Args[0])
+		popAndSave(s, v)
+		return true
+
+	case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLdefer, ssa.Op386CALLgo, ssa.Op386CALLinter:
+		flush387(s)  // Calls must empty the the FP stack.
+		return false // then issue the call as normal
+	}
+	return false
+}
+
+// push pushes v onto the floating-point stack.  v must be in a register.
+func push(s *gc.SSAGenState, v *ssa.Value) {
+	p := gc.Prog(x86.AFMOVD)
+	p.From.Type = obj.TYPE_REG
+	p.From.Reg = s.SSEto387[v.Reg()]
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = x86.REG_F0
+}
+
+// popAndSave pops a value off of the floating-point stack and stores
+// it in the reigster assigned to v.
+func popAndSave(s *gc.SSAGenState, v *ssa.Value) {
+	r := v.Reg()
+	if _, ok := s.SSEto387[r]; ok {
+		// Pop value, write to correct register.
+		p := gc.Prog(x86.AFMOVDP)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_F0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = s.SSEto387[v.Reg()] + 1
+	} else {
+		// Don't actually pop value. This 387 register is now the
+		// new home for the not-yet-assigned-a-home SSE register.
+		// Increase the register mapping of all other registers by one.
+		for rSSE, r387 := range s.SSEto387 {
+			s.SSEto387[rSSE] = r387 + 1
+		}
+		s.SSEto387[r] = x86.REG_F0
+	}
+}
+
+// loadPush returns the opcode for load+push of the given type.
+func loadPush(t ssa.Type) obj.As {
+	if t.Size() == 4 {
+		return x86.AFMOVF
+	}
+	return x86.AFMOVD
+}
+
+// flush387 removes all entries from the 387 floating-point stack.
+func flush387(s *gc.SSAGenState) {
+	for k := range s.SSEto387 {
+		p := gc.Prog(x86.AFMOVDP)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_F0
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x86.REG_F0
+		delete(s.SSEto387, k)
+	}
+}
diff --git a/src/cmd/compile/internal/x86/cgen.go b/src/cmd/compile/internal/x86/cgen.go
deleted file mode 100644
index 90a773d..0000000
--- a/src/cmd/compile/internal/x86/cgen.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package x86
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/x86"
-)
-
-/*
- * generate an addressable node in res, containing the value of n.
- * n is an array index, and might be any size; res width is <= 32-bit.
- * returns Prog* to patch to panic call.
- */
-func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog {
-	if !gc.Is64(n.Type) {
-		if n.Addable && (gc.Simtype[n.Etype] == gc.TUINT32 || gc.Simtype[n.Etype] == gc.TINT32) {
-			// nothing to do.
-			*res = *n
-		} else {
-			gc.Tempname(res, gc.Types[gc.TUINT32])
-			gc.Cgen(n, res)
-		}
-
-		return nil
-	}
-
-	var tmp gc.Node
-	gc.Tempname(&tmp, gc.Types[gc.TINT64])
-	gc.Cgen(n, &tmp)
-	var lo gc.Node
-	var hi gc.Node
-	split64(&tmp, &lo, &hi)
-	gc.Tempname(res, gc.Types[gc.TUINT32])
-	gmove(&lo, res)
-	if bounded {
-		splitclean()
-		return nil
-	}
-
-	var zero gc.Node
-	gc.Nodconst(&zero, gc.Types[gc.TINT32], 0)
-	gins(x86.ACMPL, &hi, &zero)
-	splitclean()
-	return gc.Gbranch(x86.AJNE, nil, +1)
-}
-
-func blockcopy(n, res *gc.Node, osrc, odst, w int64) {
-	var dst gc.Node
-	gc.Nodreg(&dst, gc.Types[gc.Tptr], x86.REG_DI)
-	var src gc.Node
-	gc.Nodreg(&src, gc.Types[gc.Tptr], x86.REG_SI)
-
-	var tsrc gc.Node
-	gc.Tempname(&tsrc, gc.Types[gc.Tptr])
-	var tdst gc.Node
-	gc.Tempname(&tdst, gc.Types[gc.Tptr])
-	if !n.Addable {
-		gc.Agen(n, &tsrc)
-	}
-	if !res.Addable {
-		gc.Agen(res, &tdst)
-	}
-	if n.Addable {
-		gc.Agen(n, &src)
-	} else {
-		gmove(&tsrc, &src)
-	}
-
-	if res.Op == gc.ONAME {
-		gc.Gvardef(res)
-	}
-
-	if res.Addable {
-		gc.Agen(res, &dst)
-	} else {
-		gmove(&tdst, &dst)
-	}
-
-	c := int32(w % 4) // bytes
-	q := int32(w / 4) // doublewords
-
-	// if we are copying forward on the stack and
-	// the src and dst overlap, then reverse direction
-	if osrc < odst && odst < osrc+w {
-		// reverse direction
-		gins(x86.ASTD, nil, nil) // set direction flag
-		if c > 0 {
-			gconreg(x86.AADDL, w-1, x86.REG_SI)
-			gconreg(x86.AADDL, w-1, x86.REG_DI)
-
-			gconreg(x86.AMOVL, int64(c), x86.REG_CX)
-			gins(x86.AREP, nil, nil)   // repeat
-			gins(x86.AMOVSB, nil, nil) // MOVB *(SI)-,*(DI)-
-		}
-
-		if q > 0 {
-			if c > 0 {
-				gconreg(x86.AADDL, -3, x86.REG_SI)
-				gconreg(x86.AADDL, -3, x86.REG_DI)
-			} else {
-				gconreg(x86.AADDL, w-4, x86.REG_SI)
-				gconreg(x86.AADDL, w-4, x86.REG_DI)
-			}
-
-			gconreg(x86.AMOVL, int64(q), x86.REG_CX)
-			gins(x86.AREP, nil, nil)   // repeat
-			gins(x86.AMOVSL, nil, nil) // MOVL *(SI)-,*(DI)-
-		}
-
-		// we leave with the flag clear
-		gins(x86.ACLD, nil, nil)
-	} else {
-		gins(x86.ACLD, nil, nil) // paranoia.  TODO(rsc): remove?
-
-		// normal direction
-		if q > 128 || (q >= 4 && gc.Nacl) {
-			gconreg(x86.AMOVL, int64(q), x86.REG_CX)
-			gins(x86.AREP, nil, nil)   // repeat
-			gins(x86.AMOVSL, nil, nil) // MOVL *(SI)+,*(DI)+
-		} else if q >= 4 {
-			p := gins(obj.ADUFFCOPY, nil, nil)
-			p.To.Type = obj.TYPE_ADDR
-			p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg))
-
-			// 10 and 128 = magic constants: see ../../runtime/asm_386.s
-			p.To.Offset = 10 * (128 - int64(q))
-		} else if !gc.Nacl && c == 0 {
-			var cx gc.Node
-			gc.Nodreg(&cx, gc.Types[gc.TINT32], x86.REG_CX)
-
-			// We don't need the MOVSL side-effect of updating SI and DI,
-			// and issuing a sequence of MOVLs directly is faster.
-			src.Op = gc.OINDREG
-
-			dst.Op = gc.OINDREG
-			for q > 0 {
-				gmove(&src, &cx) // MOVL x+(SI),CX
-				gmove(&cx, &dst) // MOVL CX,x+(DI)
-				src.Xoffset += 4
-				dst.Xoffset += 4
-				q--
-			}
-		} else {
-			for q > 0 {
-				gins(x86.AMOVSL, nil, nil) // MOVL *(SI)+,*(DI)+
-				q--
-			}
-		}
-
-		for c > 0 {
-			gins(x86.AMOVSB, nil, nil) // MOVB *(SI)+,*(DI)+
-			c--
-		}
-	}
-}
diff --git a/src/cmd/compile/internal/x86/cgen64.go b/src/cmd/compile/internal/x86/cgen64.go
deleted file mode 100644
index ea52d69..0000000
--- a/src/cmd/compile/internal/x86/cgen64.go
+++ /dev/null
@@ -1,598 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package x86
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/x86"
-)
-
-/*
- * attempt to generate 64-bit
- *	res = n
- * return 1 on success, 0 if op not handled.
- */
-func cgen64(n *gc.Node, res *gc.Node) {
-	if res.Op != gc.OINDREG && res.Op != gc.ONAME {
-		gc.Dump("n", n)
-		gc.Dump("res", res)
-		gc.Fatalf("cgen64 %v of %v", n.Op, res.Op)
-	}
-
-	switch n.Op {
-	default:
-		gc.Fatalf("cgen64 %v", n.Op)
-
-	case gc.OMINUS:
-		gc.Cgen(n.Left, res)
-		var hi1 gc.Node
-		var lo1 gc.Node
-		split64(res, &lo1, &hi1)
-		gins(x86.ANEGL, nil, &lo1)
-		gins(x86.AADCL, ncon(0), &hi1)
-		gins(x86.ANEGL, nil, &hi1)
-		splitclean()
-		return
-
-	case gc.OCOM:
-		gc.Cgen(n.Left, res)
-		var lo1 gc.Node
-		var hi1 gc.Node
-		split64(res, &lo1, &hi1)
-		gins(x86.ANOTL, nil, &lo1)
-		gins(x86.ANOTL, nil, &hi1)
-		splitclean()
-		return
-
-		// binary operators.
-	// common setup below.
-	case gc.OADD,
-		gc.OSUB,
-		gc.OMUL,
-		gc.OLROT,
-		gc.OLSH,
-		gc.ORSH,
-		gc.OAND,
-		gc.OOR,
-		gc.OXOR:
-		break
-	}
-
-	l := n.Left
-	r := n.Right
-	if !l.Addable {
-		var t1 gc.Node
-		gc.Tempname(&t1, l.Type)
-		gc.Cgen(l, &t1)
-		l = &t1
-	}
-
-	if r != nil && !r.Addable {
-		var t2 gc.Node
-		gc.Tempname(&t2, r.Type)
-		gc.Cgen(r, &t2)
-		r = &t2
-	}
-
-	var ax gc.Node
-	gc.Nodreg(&ax, gc.Types[gc.TINT32], x86.REG_AX)
-	var cx gc.Node
-	gc.Nodreg(&cx, gc.Types[gc.TINT32], x86.REG_CX)
-	var dx gc.Node
-	gc.Nodreg(&dx, gc.Types[gc.TINT32], x86.REG_DX)
-
-	// Setup for binary operation.
-	var hi1 gc.Node
-	var lo1 gc.Node
-	split64(l, &lo1, &hi1)
-
-	var lo2 gc.Node
-	var hi2 gc.Node
-	if gc.Is64(r.Type) {
-		split64(r, &lo2, &hi2)
-	}
-
-	// Do op. Leave result in DX:AX.
-	switch n.Op {
-	// TODO: Constants
-	case gc.OADD:
-		gins(x86.AMOVL, &lo1, &ax)
-
-		gins(x86.AMOVL, &hi1, &dx)
-		gins(x86.AADDL, &lo2, &ax)
-		gins(x86.AADCL, &hi2, &dx)
-
-		// TODO: Constants.
-	case gc.OSUB:
-		gins(x86.AMOVL, &lo1, &ax)
-
-		gins(x86.AMOVL, &hi1, &dx)
-		gins(x86.ASUBL, &lo2, &ax)
-		gins(x86.ASBBL, &hi2, &dx)
-
-	case gc.OMUL:
-		// let's call the next three EX, FX and GX
-		var ex, fx, gx gc.Node
-		gc.Regalloc(&ex, gc.Types[gc.TPTR32], nil)
-		gc.Regalloc(&fx, gc.Types[gc.TPTR32], nil)
-		gc.Regalloc(&gx, gc.Types[gc.TPTR32], nil)
-
-		// load args into DX:AX and EX:GX.
-		gins(x86.AMOVL, &lo1, &ax)
-
-		gins(x86.AMOVL, &hi1, &dx)
-		gins(x86.AMOVL, &lo2, &gx)
-		gins(x86.AMOVL, &hi2, &ex)
-
-		// if DX and EX are zero, use 32 x 32 -> 64 unsigned multiply.
-		gins(x86.AMOVL, &dx, &fx)
-
-		gins(x86.AORL, &ex, &fx)
-		p1 := gc.Gbranch(x86.AJNE, nil, 0)
-		gins(x86.AMULL, &gx, nil) // implicit &ax
-		p2 := gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-
-		// full 64x64 -> 64, from 32x32 -> 64.
-		gins(x86.AIMULL, &gx, &dx)
-
-		gins(x86.AMOVL, &ax, &fx)
-		gins(x86.AIMULL, &ex, &fx)
-		gins(x86.AADDL, &dx, &fx)
-		gins(x86.AMOVL, &gx, &dx)
-		gins(x86.AMULL, &dx, nil) // implicit &ax
-		gins(x86.AADDL, &fx, &dx)
-		gc.Patch(p2, gc.Pc)
-
-		gc.Regfree(&ex)
-		gc.Regfree(&fx)
-		gc.Regfree(&gx)
-
-	// We only rotate by a constant c in [0,64).
-	// if c >= 32:
-	//	lo, hi = hi, lo
-	//	c -= 32
-	// if c == 0:
-	//	no-op
-	// else:
-	//	t = hi
-	//	shld hi:lo, c
-	//	shld lo:t, c
-	case gc.OLROT:
-		v := uint64(r.Int64())
-
-		if v >= 32 {
-			// reverse during load to do the first 32 bits of rotate
-			v -= 32
-
-			gins(x86.AMOVL, &lo1, &dx)
-			gins(x86.AMOVL, &hi1, &ax)
-		} else {
-			gins(x86.AMOVL, &lo1, &ax)
-			gins(x86.AMOVL, &hi1, &dx)
-		}
-
-		if v == 0 {
-		} else // done
-		{
-			gins(x86.AMOVL, &dx, &cx)
-			p1 := gins(x86.ASHLL, ncon(uint32(v)), &dx)
-			p1.From.Index = x86.REG_AX // double-width shift
-			p1.From.Scale = 0
-			p1 = gins(x86.ASHLL, ncon(uint32(v)), &ax)
-			p1.From.Index = x86.REG_CX // double-width shift
-			p1.From.Scale = 0
-		}
-
-	case gc.OLSH:
-		if r.Op == gc.OLITERAL {
-			v := uint64(r.Int64())
-			if v >= 64 {
-				if gc.Is64(r.Type) {
-					splitclean()
-				}
-				splitclean()
-				split64(res, &lo2, &hi2)
-				gins(x86.AMOVL, ncon(0), &lo2)
-				gins(x86.AMOVL, ncon(0), &hi2)
-				splitclean()
-				return
-			}
-
-			if v >= 32 {
-				if gc.Is64(r.Type) {
-					splitclean()
-				}
-				split64(res, &lo2, &hi2)
-				gmove(&lo1, &hi2)
-				if v > 32 {
-					gins(x86.ASHLL, ncon(uint32(v-32)), &hi2)
-				}
-
-				gins(x86.AMOVL, ncon(0), &lo2)
-				splitclean()
-				splitclean()
-				return
-			}
-
-			// general shift
-			gins(x86.AMOVL, &lo1, &ax)
-
-			gins(x86.AMOVL, &hi1, &dx)
-			p1 := gins(x86.ASHLL, ncon(uint32(v)), &dx)
-			p1.From.Index = x86.REG_AX // double-width shift
-			p1.From.Scale = 0
-			gins(x86.ASHLL, ncon(uint32(v)), &ax)
-			break
-		}
-
-		// load value into DX:AX.
-		gins(x86.AMOVL, &lo1, &ax)
-
-		gins(x86.AMOVL, &hi1, &dx)
-
-		// load shift value into register.
-		// if high bits are set, zero value.
-		var p1 *obj.Prog
-
-		if gc.Is64(r.Type) {
-			gins(x86.ACMPL, &hi2, ncon(0))
-			p1 = gc.Gbranch(x86.AJNE, nil, +1)
-			gins(x86.AMOVL, &lo2, &cx)
-		} else {
-			cx.Type = gc.Types[gc.TUINT32]
-			gmove(r, &cx)
-		}
-
-		// if shift count is >=64, zero value
-		gins(x86.ACMPL, &cx, ncon(64))
-
-		p2 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
-		if p1 != nil {
-			gc.Patch(p1, gc.Pc)
-		}
-		gins(x86.AXORL, &dx, &dx)
-		gins(x86.AXORL, &ax, &ax)
-		gc.Patch(p2, gc.Pc)
-
-		// if shift count is >= 32, zero low.
-		gins(x86.ACMPL, &cx, ncon(32))
-
-		p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
-		gins(x86.AMOVL, &ax, &dx)
-		gins(x86.ASHLL, &cx, &dx) // SHLL only uses bottom 5 bits of count
-		gins(x86.AXORL, &ax, &ax)
-		p2 = gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-
-		// general shift
-		p1 = gins(x86.ASHLL, &cx, &dx)
-
-		p1.From.Index = x86.REG_AX // double-width shift
-		p1.From.Scale = 0
-		gins(x86.ASHLL, &cx, &ax)
-		gc.Patch(p2, gc.Pc)
-
-	case gc.ORSH:
-		if r.Op == gc.OLITERAL {
-			v := uint64(r.Int64())
-			if v >= 64 {
-				if gc.Is64(r.Type) {
-					splitclean()
-				}
-				splitclean()
-				split64(res, &lo2, &hi2)
-				if hi1.Type.Etype == gc.TINT32 {
-					gmove(&hi1, &lo2)
-					gins(x86.ASARL, ncon(31), &lo2)
-					gmove(&hi1, &hi2)
-					gins(x86.ASARL, ncon(31), &hi2)
-				} else {
-					gins(x86.AMOVL, ncon(0), &lo2)
-					gins(x86.AMOVL, ncon(0), &hi2)
-				}
-
-				splitclean()
-				return
-			}
-
-			if v >= 32 {
-				if gc.Is64(r.Type) {
-					splitclean()
-				}
-				split64(res, &lo2, &hi2)
-				gmove(&hi1, &lo2)
-				if v > 32 {
-					gins(optoas(gc.ORSH, hi1.Type), ncon(uint32(v-32)), &lo2)
-				}
-				if hi1.Type.Etype == gc.TINT32 {
-					gmove(&hi1, &hi2)
-					gins(x86.ASARL, ncon(31), &hi2)
-				} else {
-					gins(x86.AMOVL, ncon(0), &hi2)
-				}
-				splitclean()
-				splitclean()
-				return
-			}
-
-			// general shift
-			gins(x86.AMOVL, &lo1, &ax)
-
-			gins(x86.AMOVL, &hi1, &dx)
-			p1 := gins(x86.ASHRL, ncon(uint32(v)), &ax)
-			p1.From.Index = x86.REG_DX // double-width shift
-			p1.From.Scale = 0
-			gins(optoas(gc.ORSH, hi1.Type), ncon(uint32(v)), &dx)
-			break
-		}
-
-		// load value into DX:AX.
-		gins(x86.AMOVL, &lo1, &ax)
-
-		gins(x86.AMOVL, &hi1, &dx)
-
-		// load shift value into register.
-		// if high bits are set, zero value.
-		var p1 *obj.Prog
-
-		if gc.Is64(r.Type) {
-			gins(x86.ACMPL, &hi2, ncon(0))
-			p1 = gc.Gbranch(x86.AJNE, nil, +1)
-			gins(x86.AMOVL, &lo2, &cx)
-		} else {
-			cx.Type = gc.Types[gc.TUINT32]
-			gmove(r, &cx)
-		}
-
-		// if shift count is >=64, zero or sign-extend value
-		gins(x86.ACMPL, &cx, ncon(64))
-
-		p2 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
-		if p1 != nil {
-			gc.Patch(p1, gc.Pc)
-		}
-		if hi1.Type.Etype == gc.TINT32 {
-			gins(x86.ASARL, ncon(31), &dx)
-			gins(x86.AMOVL, &dx, &ax)
-		} else {
-			gins(x86.AXORL, &dx, &dx)
-			gins(x86.AXORL, &ax, &ax)
-		}
-
-		gc.Patch(p2, gc.Pc)
-
-		// if shift count is >= 32, sign-extend hi.
-		gins(x86.ACMPL, &cx, ncon(32))
-
-		p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
-		gins(x86.AMOVL, &dx, &ax)
-		if hi1.Type.Etype == gc.TINT32 {
-			gins(x86.ASARL, &cx, &ax) // SARL only uses bottom 5 bits of count
-			gins(x86.ASARL, ncon(31), &dx)
-		} else {
-			gins(x86.ASHRL, &cx, &ax)
-			gins(x86.AXORL, &dx, &dx)
-		}
-
-		p2 = gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-
-		// general shift
-		p1 = gins(x86.ASHRL, &cx, &ax)
-
-		p1.From.Index = x86.REG_DX // double-width shift
-		p1.From.Scale = 0
-		gins(optoas(gc.ORSH, hi1.Type), &cx, &dx)
-		gc.Patch(p2, gc.Pc)
-
-		// make constant the right side (it usually is anyway).
-	case gc.OXOR,
-		gc.OAND,
-		gc.OOR:
-		if lo1.Op == gc.OLITERAL {
-			nswap(&lo1, &lo2)
-			nswap(&hi1, &hi2)
-		}
-
-		if lo2.Op == gc.OLITERAL {
-			// special cases for constants.
-			lv := uint32(lo2.Int64())
-			hv := uint32(hi2.Int64())
-			splitclean() // right side
-			split64(res, &lo2, &hi2)
-			switch n.Op {
-			case gc.OXOR:
-				gmove(&lo1, &lo2)
-				gmove(&hi1, &hi2)
-				switch lv {
-				case 0:
-					break
-
-				case 0xffffffff:
-					gins(x86.ANOTL, nil, &lo2)
-
-				default:
-					gins(x86.AXORL, ncon(lv), &lo2)
-				}
-
-				switch hv {
-				case 0:
-					break
-
-				case 0xffffffff:
-					gins(x86.ANOTL, nil, &hi2)
-
-				default:
-					gins(x86.AXORL, ncon(hv), &hi2)
-				}
-
-			case gc.OAND:
-				switch lv {
-				case 0:
-					gins(x86.AMOVL, ncon(0), &lo2)
-
-				default:
-					gmove(&lo1, &lo2)
-					if lv != 0xffffffff {
-						gins(x86.AANDL, ncon(lv), &lo2)
-					}
-				}
-
-				switch hv {
-				case 0:
-					gins(x86.AMOVL, ncon(0), &hi2)
-
-				default:
-					gmove(&hi1, &hi2)
-					if hv != 0xffffffff {
-						gins(x86.AANDL, ncon(hv), &hi2)
-					}
-				}
-
-			case gc.OOR:
-				switch lv {
-				case 0:
-					gmove(&lo1, &lo2)
-
-				case 0xffffffff:
-					gins(x86.AMOVL, ncon(0xffffffff), &lo2)
-
-				default:
-					gmove(&lo1, &lo2)
-					gins(x86.AORL, ncon(lv), &lo2)
-				}
-
-				switch hv {
-				case 0:
-					gmove(&hi1, &hi2)
-
-				case 0xffffffff:
-					gins(x86.AMOVL, ncon(0xffffffff), &hi2)
-
-				default:
-					gmove(&hi1, &hi2)
-					gins(x86.AORL, ncon(hv), &hi2)
-				}
-			}
-
-			splitclean()
-			splitclean()
-			return
-		}
-
-		gins(x86.AMOVL, &lo1, &ax)
-		gins(x86.AMOVL, &hi1, &dx)
-		gins(optoas(n.Op, lo1.Type), &lo2, &ax)
-		gins(optoas(n.Op, lo1.Type), &hi2, &dx)
-	}
-
-	if gc.Is64(r.Type) {
-		splitclean()
-	}
-	splitclean()
-
-	split64(res, &lo1, &hi1)
-	gins(x86.AMOVL, &ax, &lo1)
-	gins(x86.AMOVL, &dx, &hi1)
-	splitclean()
-}
-
-/*
- * generate comparison of nl, nr, both 64-bit.
- * nl is memory; nr is constant or memory.
- */
-func cmp64(nl *gc.Node, nr *gc.Node, op gc.Op, likely int, to *obj.Prog) {
-	var lo1 gc.Node
-	var hi1 gc.Node
-	var lo2 gc.Node
-	var hi2 gc.Node
-	var rr gc.Node
-
-	split64(nl, &lo1, &hi1)
-	split64(nr, &lo2, &hi2)
-
-	// compare most significant word;
-	// if they differ, we're done.
-	t := hi1.Type
-
-	if nl.Op == gc.OLITERAL || nr.Op == gc.OLITERAL {
-		gins(x86.ACMPL, &hi1, &hi2)
-	} else {
-		gc.Regalloc(&rr, gc.Types[gc.TINT32], nil)
-		gins(x86.AMOVL, &hi1, &rr)
-		gins(x86.ACMPL, &rr, &hi2)
-		gc.Regfree(&rr)
-	}
-
-	var br *obj.Prog
-	switch op {
-	default:
-		gc.Fatalf("cmp64 %v %v", op, t)
-
-		// cmp hi
-	// jne L
-	// cmp lo
-	// jeq to
-	// L:
-	case gc.OEQ:
-		br = gc.Gbranch(x86.AJNE, nil, -likely)
-
-		// cmp hi
-	// jne to
-	// cmp lo
-	// jne to
-	case gc.ONE:
-		gc.Patch(gc.Gbranch(x86.AJNE, nil, likely), to)
-
-		// cmp hi
-	// jgt to
-	// jlt L
-	// cmp lo
-	// jge to (or jgt to)
-	// L:
-	case gc.OGE,
-		gc.OGT:
-		gc.Patch(gc.Gbranch(optoas(gc.OGT, t), nil, likely), to)
-
-		br = gc.Gbranch(optoas(gc.OLT, t), nil, -likely)
-
-		// cmp hi
-	// jlt to
-	// jgt L
-	// cmp lo
-	// jle to (or jlt to)
-	// L:
-	case gc.OLE,
-		gc.OLT:
-		gc.Patch(gc.Gbranch(optoas(gc.OLT, t), nil, likely), to)
-
-		br = gc.Gbranch(optoas(gc.OGT, t), nil, -likely)
-	}
-
-	// compare least significant word
-	t = lo1.Type
-
-	if nl.Op == gc.OLITERAL || nr.Op == gc.OLITERAL {
-		gins(x86.ACMPL, &lo1, &lo2)
-	} else {
-		gc.Regalloc(&rr, gc.Types[gc.TINT32], nil)
-		gins(x86.AMOVL, &lo1, &rr)
-		gins(x86.ACMPL, &rr, &lo2)
-		gc.Regfree(&rr)
-	}
-
-	// jump again
-	gc.Patch(gc.Gbranch(optoas(op, t), nil, likely), to)
-
-	// point first branch down here if appropriate
-	if br != nil {
-		gc.Patch(br, gc.Pc)
-	}
-
-	splitclean()
-	splitclean()
-}
diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go
index 738d887..edac6a0 100644
--- a/src/cmd/compile/internal/x86/galign.go
+++ b/src/cmd/compile/internal/x86/galign.go
@@ -12,71 +12,23 @@ import (
 	"os"
 )
 
-func betypeinit() {
-}
-
-func Main() {
+func Init() {
 	gc.Thearch.LinkArch = &x86.Link386
 	gc.Thearch.REGSP = x86.REGSP
-	gc.Thearch.REGCTXT = x86.REGCTXT
-	gc.Thearch.REGCALLX = x86.REG_BX
-	gc.Thearch.REGCALLX2 = x86.REG_AX
-	gc.Thearch.REGRETURN = x86.REG_AX
-	gc.Thearch.REGMIN = x86.REG_AX
-	gc.Thearch.REGMAX = x86.REG_DI
-	switch v := obj.Getgo386(); v {
+	switch v := obj.GO386; v {
 	case "387":
-		gc.Thearch.FREGMIN = x86.REG_F0
-		gc.Thearch.FREGMAX = x86.REG_F7
 		gc.Thearch.Use387 = true
 	case "sse2":
-		gc.Thearch.FREGMIN = x86.REG_X0
-		gc.Thearch.FREGMAX = x86.REG_X7
 	default:
 		fmt.Fprintf(os.Stderr, "unsupported setting GO386=%s\n", v)
 		gc.Exit(1)
 	}
 	gc.Thearch.MAXWIDTH = (1 << 32) - 1
-	gc.Thearch.ReservedRegs = resvd
 
-	gc.Thearch.Betypeinit = betypeinit
-	gc.Thearch.Bgen_float = bgen_float
-	gc.Thearch.Cgen64 = cgen64
-	gc.Thearch.Cgen_bmul = cgen_bmul
-	gc.Thearch.Cgen_float = cgen_float
-	gc.Thearch.Cgen_hmul = cgen_hmul
-	gc.Thearch.Cgen_shift = cgen_shift
-	gc.Thearch.Clearfat = clearfat
-	gc.Thearch.Cmp64 = cmp64
 	gc.Thearch.Defframe = defframe
-	gc.Thearch.Dodiv = cgen_div
-	gc.Thearch.Excise = excise
-	gc.Thearch.Expandchecks = expandchecks
-	gc.Thearch.Getg = getg
-	gc.Thearch.Gins = gins
-	gc.Thearch.Ginscmp = ginscmp
-	gc.Thearch.Ginscon = ginscon
-	gc.Thearch.Ginsnop = ginsnop
-	gc.Thearch.Gmove = gmove
-	gc.Thearch.Igenindex = igenindex
-	gc.Thearch.Peep = peep
 	gc.Thearch.Proginfo = proginfo
-	gc.Thearch.Regtyp = regtyp
-	gc.Thearch.Sameaddr = sameaddr
-	gc.Thearch.Smallindir = smallindir
-	gc.Thearch.Stackaddr = stackaddr
-	gc.Thearch.Blockcopy = blockcopy
-	gc.Thearch.Sudoaddable = sudoaddable
-	gc.Thearch.Sudoclean = sudoclean
-	gc.Thearch.Excludedregs = excludedregs
-	gc.Thearch.RtoB = RtoB
-	gc.Thearch.FtoB = FtoB
-	gc.Thearch.BtoR = BtoR
-	gc.Thearch.BtoF = BtoF
-	gc.Thearch.Optoas = optoas
-	gc.Thearch.Doregbits = doregbits
-	gc.Thearch.Regnames = regnames
 
-	gc.Main()
-	gc.Exit(0)
+	gc.Thearch.SSAMarkMoves = ssaMarkMoves
+	gc.Thearch.SSAGenValue = ssaGenValue
+	gc.Thearch.SSAGenBlock = ssaGenBlock
 }
diff --git a/src/cmd/compile/internal/x86/ggen.go b/src/cmd/compile/internal/x86/ggen.go
index 21d989c..25769b4 100644
--- a/src/cmd/compile/internal/x86/ggen.go
+++ b/src/cmd/compile/internal/x86/ggen.go
@@ -34,9 +34,9 @@ func defframe(ptxt *obj.Prog) {
 			gc.Fatalf("needzero class %d", n.Class)
 		}
 		if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
-			gc.Fatalf("var %v has size %d offset %d", gc.Nconv(n, gc.FmtLong), int(n.Type.Width), int(n.Xoffset))
+			gc.Fatalf("var %L has size %d offset %d", n, int(n.Type.Width), int(n.Xoffset))
 		}
-		if lo != hi && n.Xoffset+n.Type.Width == lo-int64(2*gc.Widthptr) {
+		if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthptr) {
 			// merge with range we already have
 			lo = n.Xoffset
 
@@ -62,858 +62,32 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32) *obj.Pr
 		return p
 	}
 	if *ax == 0 {
-		p = appendpp(p, x86.AMOVL, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
+		p = gc.Appendpp(p, x86.AMOVL, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0)
 		*ax = 1
 	}
 
 	if cnt <= int64(4*gc.Widthreg) {
 		for i := int64(0); i < cnt; i += int64(gc.Widthreg) {
-			p = appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+i)
+			p = gc.Appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+i)
 		}
 	} else if !gc.Nacl && cnt <= int64(128*gc.Widthreg) {
-		p = appendpp(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0)
-		p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, 1*(128-cnt/int64(gc.Widthreg)))
+		p = gc.Appendpp(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0)
+		p = gc.Appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, 1*(128-cnt/int64(gc.Widthreg)))
 		p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
 	} else {
-		p = appendpp(p, x86.AMOVL, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0)
-		p = appendpp(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0)
-		p = appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
-		p = appendpp(p, x86.ASTOSL, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
+		p = gc.Appendpp(p, x86.AMOVL, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0)
+		p = gc.Appendpp(p, x86.ALEAL, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0)
+		p = gc.Appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
+		p = gc.Appendpp(p, x86.ASTOSL, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
 	}
 
 	return p
 }
 
-func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int64, ttype obj.AddrType, treg int, toffset int64) *obj.Prog {
-	q := gc.Ctxt.NewProg()
-	gc.Clearp(q)
-	q.As = as
-	q.Lineno = p.Lineno
-	q.From.Type = ftype
-	q.From.Reg = int16(freg)
-	q.From.Offset = foffset
-	q.To.Type = ttype
-	q.To.Reg = int16(treg)
-	q.To.Offset = toffset
-	q.Link = p.Link
-	p.Link = q
-	return q
-}
-
-func clearfat(nl *gc.Node) {
-	/* clear a fat object */
-	if gc.Debug['g'] != 0 {
-		gc.Dump("\nclearfat", nl)
-	}
-
-	w := uint32(nl.Type.Width)
-
-	// Avoid taking the address for simple enough types.
-	if gc.Componentgen(nil, nl) {
-		return
-	}
-
-	c := w % 4 // bytes
-	q := w / 4 // quads
-
-	if q < 4 {
-		// Write sequence of MOV 0, off(base) instead of using STOSL.
-		// The hope is that although the code will be slightly longer,
-		// the MOVs will have no dependencies and pipeline better
-		// than the unrolled STOSL loop.
-		// NOTE: Must use agen, not igen, so that optimizer sees address
-		// being taken. We are not writing on field boundaries.
-		var n1 gc.Node
-		gc.Regalloc(&n1, gc.Types[gc.Tptr], nil)
-
-		gc.Agen(nl, &n1)
-		n1.Op = gc.OINDREG
-		var z gc.Node
-		gc.Nodconst(&z, gc.Types[gc.TUINT64], 0)
-		for ; q > 0; q-- {
-			n1.Type = z.Type
-			gins(x86.AMOVL, &z, &n1)
-			n1.Xoffset += 4
-		}
-
-		gc.Nodconst(&z, gc.Types[gc.TUINT8], 0)
-		for ; c > 0; c-- {
-			n1.Type = z.Type
-			gins(x86.AMOVB, &z, &n1)
-			n1.Xoffset++
-		}
-
-		gc.Regfree(&n1)
-		return
-	}
-
-	var n1 gc.Node
-	gc.Nodreg(&n1, gc.Types[gc.Tptr], x86.REG_DI)
-	gc.Agen(nl, &n1)
-	gconreg(x86.AMOVL, 0, x86.REG_AX)
-
-	if q > 128 || (q >= 4 && gc.Nacl) {
-		gconreg(x86.AMOVL, int64(q), x86.REG_CX)
-		gins(x86.AREP, nil, nil)   // repeat
-		gins(x86.ASTOSL, nil, nil) // STOL AL,*(DI)+
-	} else if q >= 4 {
-		p := gins(obj.ADUFFZERO, nil, nil)
-		p.To.Type = obj.TYPE_ADDR
-		p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
-
-		// 1 and 128 = magic constants: see ../../runtime/asm_386.s
-		p.To.Offset = 1 * (128 - int64(q))
-	} else {
-		for q > 0 {
-			gins(x86.ASTOSL, nil, nil) // STOL AL,*(DI)+
-			q--
-		}
-	}
-
-	for c > 0 {
-		gins(x86.ASTOSB, nil, nil) // STOB AL,*(DI)+
-		c--
-	}
-}
-
-var panicdiv *gc.Node
-
-/*
- * generate division.
- * caller must set:
- *	ax = allocated AX register
- *	dx = allocated DX register
- * generates one of:
- *	res = nl / nr
- *	res = nl % nr
- * according to op.
- */
-func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node, ax *gc.Node, dx *gc.Node) {
-	// Have to be careful about handling
-	// most negative int divided by -1 correctly.
-	// The hardware will trap.
-	// Also the byte divide instruction needs AH,
-	// which we otherwise don't have to deal with.
-	// Easiest way to avoid for int8, int16: use int32.
-	// For int32 and int64, use explicit test.
-	// Could use int64 hw for int32.
-	t := nl.Type
-
-	t0 := t
-	check := false
-	if t.IsSigned() {
-		check = true
-		if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -1<<uint64(t.Width*8-1) {
-			check = false
-		} else if gc.Isconst(nr, gc.CTINT) && nr.Int64() != -1 {
-			check = false
-		}
-	}
-
-	if t.Width < 4 {
-		if t.IsSigned() {
-			t = gc.Types[gc.TINT32]
-		} else {
-			t = gc.Types[gc.TUINT32]
-		}
-		check = false
-	}
-
-	var t1 gc.Node
-	gc.Tempname(&t1, t)
-	var t2 gc.Node
-	gc.Tempname(&t2, t)
-	if t0 != t {
-		var t3 gc.Node
-		gc.Tempname(&t3, t0)
-		var t4 gc.Node
-		gc.Tempname(&t4, t0)
-		gc.Cgen(nl, &t3)
-		gc.Cgen(nr, &t4)
-
-		// Convert.
-		gmove(&t3, &t1)
-
-		gmove(&t4, &t2)
-	} else {
-		gc.Cgen(nl, &t1)
-		gc.Cgen(nr, &t2)
-	}
-
-	var n1 gc.Node
-	if !gc.Samereg(ax, res) && !gc.Samereg(dx, res) {
-		gc.Regalloc(&n1, t, res)
-	} else {
-		gc.Regalloc(&n1, t, nil)
-	}
-	gmove(&t2, &n1)
-	gmove(&t1, ax)
-	var p2 *obj.Prog
-	var n4 gc.Node
-	if gc.Nacl {
-		// Native Client does not relay the divide-by-zero trap
-		// to the executing program, so we must insert a check
-		// for ourselves.
-		gc.Nodconst(&n4, t, 0)
-
-		gins(optoas(gc.OCMP, t), &n1, &n4)
-		p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1)
-		if panicdiv == nil {
-			panicdiv = gc.Sysfunc("panicdivide")
-		}
-		gc.Ginscall(panicdiv, -1)
-		gc.Patch(p1, gc.Pc)
-	}
-
-	if check {
-		gc.Nodconst(&n4, t, -1)
-		gins(optoas(gc.OCMP, t), &n1, &n4)
-		p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1)
-		if op == gc.ODIV {
-			// a / (-1) is -a.
-			gins(optoas(gc.OMINUS, t), nil, ax)
-
-			gmove(ax, res)
-		} else {
-			// a % (-1) is 0.
-			gc.Nodconst(&n4, t, 0)
-
-			gmove(&n4, res)
-		}
-
-		p2 = gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-	}
-
-	if !t.IsSigned() {
-		var nz gc.Node
-		gc.Nodconst(&nz, t, 0)
-		gmove(&nz, dx)
-	} else {
-		gins(optoas(gc.OEXTEND, t), nil, nil)
-	}
-	gins(optoas(op, t), &n1, nil)
-	gc.Regfree(&n1)
-
-	if op == gc.ODIV {
-		gmove(ax, res)
-	} else {
-		gmove(dx, res)
-	}
-	if check {
-		gc.Patch(p2, gc.Pc)
-	}
-}
-
-func savex(dr int, x *gc.Node, oldx *gc.Node, res *gc.Node, t *gc.Type) {
-	r := gc.GetReg(dr)
-	gc.Nodreg(x, gc.Types[gc.TINT32], dr)
-
-	// save current ax and dx if they are live
-	// and not the destination
-	*oldx = gc.Node{}
-
-	if r > 0 && !gc.Samereg(x, res) {
-		gc.Tempname(oldx, gc.Types[gc.TINT32])
-		gmove(x, oldx)
-	}
-
-	gc.Regalloc(x, t, x)
-}
-
-func restx(x *gc.Node, oldx *gc.Node) {
-	gc.Regfree(x)
-
-	if oldx.Op != 0 {
-		x.Type = gc.Types[gc.TINT32]
-		gmove(oldx, x)
-	}
-}
-
-/*
- * generate division according to op, one of:
- *	res = nl / nr
- *	res = nl % nr
- */
-func cgen_div(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	if gc.Is64(nl.Type) {
-		gc.Fatalf("cgen_div %v", nl.Type)
-	}
-
-	var t *gc.Type
-	if nl.Type.IsSigned() {
-		t = gc.Types[gc.TINT32]
-	} else {
-		t = gc.Types[gc.TUINT32]
-	}
-	var ax gc.Node
-	var oldax gc.Node
-	savex(x86.REG_AX, &ax, &oldax, res, t)
-	var olddx gc.Node
-	var dx gc.Node
-	savex(x86.REG_DX, &dx, &olddx, res, t)
-	dodiv(op, nl, nr, res, &ax, &dx)
-	restx(&dx, &olddx)
-	restx(&ax, &oldax)
-}
-
-/*
- * generate shift according to op, one of:
- *	res = nl << nr
- *	res = nl >> nr
- */
-func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	if nl.Type.Width > 4 {
-		gc.Fatalf("cgen_shift %v", nl.Type)
-	}
-
-	w := int(nl.Type.Width * 8)
-
-	a := optoas(op, nl.Type)
-
-	if nr.Op == gc.OLITERAL {
-		var n2 gc.Node
-		gc.Tempname(&n2, nl.Type)
-		gc.Cgen(nl, &n2)
-		var n1 gc.Node
-		gc.Regalloc(&n1, nl.Type, res)
-		gmove(&n2, &n1)
-		sc := uint64(nr.Int64())
-		if sc >= uint64(nl.Type.Width*8) {
-			// large shift gets 2 shifts by width-1
-			gins(a, ncon(uint32(w)-1), &n1)
-
-			gins(a, ncon(uint32(w)-1), &n1)
-		} else {
-			gins(a, nr, &n1)
-		}
-		gmove(&n1, res)
-		gc.Regfree(&n1)
-		return
-	}
-
-	var oldcx gc.Node
-	var cx gc.Node
-	gc.Nodreg(&cx, gc.Types[gc.TUINT32], x86.REG_CX)
-	if gc.GetReg(x86.REG_CX) > 1 && !gc.Samereg(&cx, res) {
-		gc.Tempname(&oldcx, gc.Types[gc.TUINT32])
-		gmove(&cx, &oldcx)
-	}
-
-	var n1 gc.Node
-	var nt gc.Node
-	if nr.Type.Width > 4 {
-		gc.Tempname(&nt, nr.Type)
-		n1 = nt
-	} else {
-		gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX)
-		gc.Regalloc(&n1, nr.Type, &n1) // to hold the shift type in CX
-	}
-
-	var n2 gc.Node
-	if gc.Samereg(&cx, res) {
-		gc.Regalloc(&n2, nl.Type, nil)
-	} else {
-		gc.Regalloc(&n2, nl.Type, res)
-	}
-	if nl.Ullman >= nr.Ullman {
-		gc.Cgen(nl, &n2)
-		gc.Cgen(nr, &n1)
-	} else {
-		gc.Cgen(nr, &n1)
-		gc.Cgen(nl, &n2)
-	}
-
-	// test and fix up large shifts
-	if bounded {
-		if nr.Type.Width > 4 {
-			// delayed reg alloc
-			gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX)
-
-			gc.Regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX
-			var lo gc.Node
-			var hi gc.Node
-			split64(&nt, &lo, &hi)
-			gmove(&lo, &n1)
-			splitclean()
-		}
-	} else {
-		var p1 *obj.Prog
-		if nr.Type.Width > 4 {
-			// delayed reg alloc
-			gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX)
-
-			gc.Regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX
-			var lo gc.Node
-			var hi gc.Node
-			split64(&nt, &lo, &hi)
-			gmove(&lo, &n1)
-			gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &hi, ncon(0))
-			p2 := gc.Gbranch(optoas(gc.ONE, gc.Types[gc.TUINT32]), nil, +1)
-			gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &n1, ncon(uint32(w)))
-			p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
-			splitclean()
-			gc.Patch(p2, gc.Pc)
-		} else {
-			gins(optoas(gc.OCMP, nr.Type), &n1, ncon(uint32(w)))
-			p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
-		}
-
-		if op == gc.ORSH && nl.Type.IsSigned() {
-			gins(a, ncon(uint32(w)-1), &n2)
-		} else {
-			gmove(ncon(0), &n2)
-		}
-
-		gc.Patch(p1, gc.Pc)
-	}
-
-	gins(a, &n1, &n2)
-
-	if oldcx.Op != 0 {
-		gmove(&oldcx, &cx)
-	}
-
-	gmove(&n2, res)
-
-	gc.Regfree(&n1)
-	gc.Regfree(&n2)
-}
-
-/*
- * generate byte multiply:
- *	res = nl * nr
- * there is no 2-operand byte multiply instruction so
- * we do a full-width multiplication and truncate afterwards.
- */
-func cgen_bmul(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) bool {
-	if optoas(op, nl.Type) != x86.AIMULB {
-		return false
-	}
-
-	// copy from byte to full registers
-	t := gc.Types[gc.TUINT32]
-
-	if nl.Type.IsSigned() {
-		t = gc.Types[gc.TINT32]
-	}
-
-	// largest ullman on left.
-	if nl.Ullman < nr.Ullman {
-		nl, nr = nr, nl
-	}
-
-	var nt gc.Node
-	gc.Tempname(&nt, nl.Type)
-	gc.Cgen(nl, &nt)
-	var n1 gc.Node
-	gc.Regalloc(&n1, t, res)
-	gc.Cgen(nr, &n1)
-	var n2 gc.Node
-	gc.Regalloc(&n2, t, nil)
-	gmove(&nt, &n2)
-	a := optoas(op, t)
-	gins(a, &n2, &n1)
-	gc.Regfree(&n2)
-	gmove(&n1, res)
-	gc.Regfree(&n1)
-
-	return true
-}
-
-/*
- * generate high multiply:
- *   res = (nl*nr) >> width
- */
-func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) {
-	var n1 gc.Node
-	var n2 gc.Node
-
-	t := nl.Type
-	a := optoas(gc.OHMUL, t)
-
-	// gen nl in n1.
-	gc.Tempname(&n1, t)
-	gc.Cgen(nl, &n1)
-
-	// gen nr in n2.
-	gc.Regalloc(&n2, t, res)
-	gc.Cgen(nr, &n2)
-
-	var ax, oldax, dx, olddx gc.Node
-	savex(x86.REG_AX, &ax, &oldax, res, gc.Types[gc.TUINT32])
-	savex(x86.REG_DX, &dx, &olddx, res, gc.Types[gc.TUINT32])
-
-	gmove(&n2, &ax)
-	gins(a, &n1, nil)
-	gc.Regfree(&n2)
-
-	if t.Width == 1 {
-		// byte multiply behaves differently.
-		var byteAH, byteDX gc.Node
-		gc.Nodreg(&byteAH, t, x86.REG_AH)
-		gc.Nodreg(&byteDX, t, x86.REG_DX)
-		gmove(&byteAH, &byteDX)
-	}
-
-	gmove(&dx, res)
-
-	restx(&ax, &oldax)
-	restx(&dx, &olddx)
-}
-
-/*
- * generate floating-point operation.
- */
-func cgen_float(n *gc.Node, res *gc.Node) {
-	nl := n.Left
-	switch n.Op {
-	case gc.OEQ,
-		gc.ONE,
-		gc.OLT,
-		gc.OLE,
-		gc.OGE:
-		p1 := gc.Gbranch(obj.AJMP, nil, 0)
-		p2 := gc.Pc
-		gmove(gc.Nodbool(true), res)
-		p3 := gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-		gc.Bgen(n, true, 0, p2)
-		gmove(gc.Nodbool(false), res)
-		gc.Patch(p3, gc.Pc)
-		return
-
-	case gc.OPLUS:
-		gc.Cgen(nl, res)
-		return
-
-	case gc.OCONV:
-		if gc.Eqtype(n.Type, nl.Type) || gc.Noconv(n.Type, nl.Type) {
-			gc.Cgen(nl, res)
-			return
-		}
-
-		var n2 gc.Node
-		gc.Tempname(&n2, n.Type)
-		var n1 gc.Node
-		gc.Mgen(nl, &n1, res)
-		gmove(&n1, &n2)
-		gmove(&n2, res)
-		gc.Mfree(&n1)
-		return
-	}
-
-	if gc.Thearch.Use387 {
-		cgen_float387(n, res)
-	} else {
-		cgen_floatsse(n, res)
-	}
-}
-
-// floating-point.  387 (not SSE2)
-func cgen_float387(n *gc.Node, res *gc.Node) {
-	var f0 gc.Node
-	var f1 gc.Node
-
-	nl := n.Left
-	nr := n.Right
-	gc.Nodreg(&f0, nl.Type, x86.REG_F0)
-	gc.Nodreg(&f1, n.Type, x86.REG_F0+1)
-	if nr != nil {
-		// binary
-		if nl.Ullman >= nr.Ullman {
-			gc.Cgen(nl, &f0)
-			if nr.Addable {
-				gins(foptoas(n.Op, n.Type, 0), nr, &f0)
-			} else {
-				gc.Cgen(nr, &f0)
-				gins(foptoas(n.Op, n.Type, Fpop), &f0, &f1)
-			}
-		} else {
-			gc.Cgen(nr, &f0)
-			if nl.Addable {
-				gins(foptoas(n.Op, n.Type, Frev), nl, &f0)
-			} else {
-				gc.Cgen(nl, &f0)
-				gins(foptoas(n.Op, n.Type, Frev|Fpop), &f0, &f1)
-			}
-		}
-
-		gmove(&f0, res)
-		return
-	}
-
-	// unary
-	gc.Cgen(nl, &f0)
-
-	if n.Op != gc.OCONV && n.Op != gc.OPLUS {
-		gins(foptoas(n.Op, n.Type, 0), nil, nil)
-	}
-	gmove(&f0, res)
-	return
-}
-
-func cgen_floatsse(n *gc.Node, res *gc.Node) {
-	var a obj.As
-
-	nl := n.Left
-	nr := n.Right
-	switch n.Op {
-	default:
-		gc.Dump("cgen_floatsse", n)
-		gc.Fatalf("cgen_floatsse %v", n.Op)
-		return
-
-	case gc.OMINUS,
-		gc.OCOM:
-		nr = gc.NegOne(n.Type)
-		a = foptoas(gc.OMUL, nl.Type, 0)
-		goto sbop
-
-		// symmetric binary
-	case gc.OADD,
-		gc.OMUL:
-		a = foptoas(n.Op, nl.Type, 0)
-
-		goto sbop
-
-		// asymmetric binary
-	case gc.OSUB,
-		gc.OMOD,
-		gc.ODIV:
-		a = foptoas(n.Op, nl.Type, 0)
-
-		goto abop
-	}
-
-sbop: // symmetric binary
-	if nl.Ullman < nr.Ullman || nl.Op == gc.OLITERAL {
-		nl, nr = nr, nl
-	}
-
-abop: // asymmetric binary
-	if nl.Ullman >= nr.Ullman {
-		var nt gc.Node
-		gc.Tempname(&nt, nl.Type)
-		gc.Cgen(nl, &nt)
-		var n2 gc.Node
-		gc.Mgen(nr, &n2, nil)
-		var n1 gc.Node
-		gc.Regalloc(&n1, nl.Type, res)
-		gmove(&nt, &n1)
-		gins(a, &n2, &n1)
-		gmove(&n1, res)
-		gc.Regfree(&n1)
-		gc.Mfree(&n2)
-	} else {
-		var n2 gc.Node
-		gc.Regalloc(&n2, nr.Type, res)
-		gc.Cgen(nr, &n2)
-		var n1 gc.Node
-		gc.Regalloc(&n1, nl.Type, nil)
-		gc.Cgen(nl, &n1)
-		gins(a, &n2, &n1)
-		gc.Regfree(&n2)
-		gmove(&n1, res)
-		gc.Regfree(&n1)
-	}
-
-	return
-}
-
-func bgen_float(n *gc.Node, wantTrue bool, likely int, to *obj.Prog) {
-	nl := n.Left
-	nr := n.Right
-	op := n.Op
-	if !wantTrue {
-		// brcom is not valid on floats when NaN is involved.
-		p1 := gc.Gbranch(obj.AJMP, nil, 0)
-		p2 := gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p1, gc.Pc)
-
-		// No need to avoid re-genning ninit.
-		bgen_float(n, true, -likely, p2)
-
-		gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to)
-		gc.Patch(p2, gc.Pc)
-		return
-	}
-
-	if gc.Thearch.Use387 {
-		op = gc.Brrev(op) // because the args are stacked
-		if op == gc.OGE || op == gc.OGT {
-			// only < and <= work right with NaN; reverse if needed
-			nl, nr = nr, nl
-			op = gc.Brrev(op)
-		}
-
-		var ax, n2, tmp gc.Node
-		gc.Nodreg(&tmp, nr.Type, x86.REG_F0)
-		gc.Nodreg(&n2, nr.Type, x86.REG_F0+1)
-		gc.Nodreg(&ax, gc.Types[gc.TUINT16], x86.REG_AX)
-		if gc.Simsimtype(nr.Type) == gc.TFLOAT64 {
-			if nl.Ullman > nr.Ullman {
-				gc.Cgen(nl, &tmp)
-				gc.Cgen(nr, &tmp)
-				gins(x86.AFXCHD, &tmp, &n2)
-			} else {
-				gc.Cgen(nr, &tmp)
-				gc.Cgen(nl, &tmp)
-			}
-			gins(x86.AFUCOMPP, &tmp, &n2)
-		} else {
-			// TODO(rsc): The moves back and forth to memory
-			// here are for truncating the value to 32 bits.
-			// This handles 32-bit comparison but presumably
-			// all the other ops have the same problem.
-			// We need to figure out what the right general
-			// solution is, besides telling people to use float64.
-			var t1 gc.Node
-			gc.Tempname(&t1, gc.Types[gc.TFLOAT32])
-
-			var t2 gc.Node
-			gc.Tempname(&t2, gc.Types[gc.TFLOAT32])
-			gc.Cgen(nr, &t1)
-			gc.Cgen(nl, &t2)
-			gmove(&t2, &tmp)
-			gins(x86.AFCOMFP, &t1, &tmp)
-		}
-		gins(x86.AFSTSW, nil, &ax)
-		gins(x86.ASAHF, nil, nil)
-	} else {
-		// Not 387
-		if !nl.Addable {
-			nl = gc.CgenTemp(nl)
-		}
-		if !nr.Addable {
-			nr = gc.CgenTemp(nr)
-		}
-
-		var n2 gc.Node
-		gc.Regalloc(&n2, nr.Type, nil)
-		gmove(nr, &n2)
-		nr = &n2
-
-		if nl.Op != gc.OREGISTER {
-			var n3 gc.Node
-			gc.Regalloc(&n3, nl.Type, nil)
-			gmove(nl, &n3)
-			nl = &n3
-		}
-
-		if op == gc.OGE || op == gc.OGT {
-			// only < and <= work right with NopN; reverse if needed
-			nl, nr = nr, nl
-			op = gc.Brrev(op)
-		}
-
-		gins(foptoas(gc.OCMP, nr.Type, 0), nl, nr)
-		if nl.Op == gc.OREGISTER {
-			gc.Regfree(nl)
-		}
-		gc.Regfree(nr)
-	}
-
-	switch op {
-	case gc.OEQ:
-		// neither NE nor P
-		p1 := gc.Gbranch(x86.AJNE, nil, -likely)
-		p2 := gc.Gbranch(x86.AJPS, nil, -likely)
-		gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to)
-		gc.Patch(p1, gc.Pc)
-		gc.Patch(p2, gc.Pc)
-	case gc.ONE:
-		// either NE or P
-		gc.Patch(gc.Gbranch(x86.AJNE, nil, likely), to)
-		gc.Patch(gc.Gbranch(x86.AJPS, nil, likely), to)
-	default:
-		gc.Patch(gc.Gbranch(optoas(op, nr.Type), nil, likely), to)
-	}
-}
-
-// Called after regopt and peep have run.
-// Expand CHECKNIL pseudo-op into actual nil pointer check.
-func expandchecks(firstp *obj.Prog) {
-	var p1 *obj.Prog
-	var p2 *obj.Prog
-
-	for p := firstp; p != nil; p = p.Link {
-		if p.As != obj.ACHECKNIL {
-			continue
-		}
-		if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers
-			gc.Warnl(p.Lineno, "generated nil check")
-		}
-
-		// check is
-		//	CMP arg, $0
-		//	JNE 2(PC) (likely)
-		//	MOV AX, 0
-		p1 = gc.Ctxt.NewProg()
-
-		p2 = gc.Ctxt.NewProg()
-		gc.Clearp(p1)
-		gc.Clearp(p2)
-		p1.Link = p2
-		p2.Link = p.Link
-		p.Link = p1
-		p1.Lineno = p.Lineno
-		p2.Lineno = p.Lineno
-		p1.Pc = 9999
-		p2.Pc = 9999
-		p.As = x86.ACMPL
-		p.To.Type = obj.TYPE_CONST
-		p.To.Offset = 0
-		p1.As = x86.AJNE
-		p1.From.Type = obj.TYPE_CONST
-		p1.From.Offset = 1 // likely
-		p1.To.Type = obj.TYPE_BRANCH
-		p1.To.Val = p2.Link
-
-		// crash by write to memory address 0.
-		// if possible, since we know arg is 0, use 0(arg),
-		// which will be shorter to encode than plain 0.
-		p2.As = x86.AMOVL
-
-		p2.From.Type = obj.TYPE_REG
-		p2.From.Reg = x86.REG_AX
-		if regtyp(&p.From) {
-			p2.To.Type = obj.TYPE_MEM
-			p2.To.Reg = p.From.Reg
-		} else {
-			p2.To.Type = obj.TYPE_MEM
-		}
-		p2.To.Offset = 0
-	}
-}
-
-// addr += index*width if possible.
-func addindex(index *gc.Node, width int64, addr *gc.Node) bool {
-	switch width {
-	case 1, 2, 4, 8:
-		p1 := gins(x86.ALEAL, index, addr)
-		p1.From.Type = obj.TYPE_MEM
-		p1.From.Scale = int16(width)
-		p1.From.Index = p1.From.Reg
-		p1.From.Reg = p1.To.Reg
-		return true
-	}
-	return false
-}
-
-// res = runtime.getg()
-func getg(res *gc.Node) {
-	var n1 gc.Node
-	gc.Regalloc(&n1, res.Type, res)
-	mov := optoas(gc.OAS, gc.Types[gc.Tptr])
-	p := gins(mov, nil, &n1)
+func ginsnop() {
+	p := gc.Prog(x86.AXCHGL)
 	p.From.Type = obj.TYPE_REG
-	p.From.Reg = x86.REG_TLS
-	p = gins(mov, nil, &n1)
-	p.From = p.To
-	p.From.Type = obj.TYPE_MEM
-	p.From.Index = x86.REG_TLS
-	p.From.Scale = 1
-	gmove(&n1, res)
-	gc.Regfree(&n1)
+	p.From.Reg = x86.REG_AX
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = x86.REG_AX
 }
diff --git a/src/cmd/compile/internal/x86/gsubr.go b/src/cmd/compile/internal/x86/gsubr.go
deleted file mode 100644
index 6406326..0000000
--- a/src/cmd/compile/internal/x86/gsubr.go
+++ /dev/null
@@ -1,1844 +0,0 @@
-// Derived from Inferno utils/8c/txt.c
-// http://code.google.com/p/inferno-os/source/browse/utils/8c/txt.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package x86
-
-import (
-	"cmd/compile/internal/big"
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/x86"
-	"fmt"
-)
-
-// TODO(rsc): Can make this bigger if we move
-// the text segment up higher in 8l for all GOOS.
-// At the same time, can raise StackBig in ../../runtime/stack.h.
-var unmappedzero uint32 = 4096
-
-// foptoas flags
-const (
-	Frev  = 1 << 0
-	Fpop  = 1 << 1
-	Fpop2 = 1 << 2
-)
-
-/*
- * return Axxx for Oxxx on type t.
- */
-func optoas(op gc.Op, t *gc.Type) obj.As {
-	if t == nil {
-		gc.Fatalf("optoas: t is nil")
-	}
-
-	// avoid constant conversions in switches below
-	const (
-		OMINUS_  = uint32(gc.OMINUS) << 16
-		OLSH_    = uint32(gc.OLSH) << 16
-		ORSH_    = uint32(gc.ORSH) << 16
-		OADD_    = uint32(gc.OADD) << 16
-		OSUB_    = uint32(gc.OSUB) << 16
-		OMUL_    = uint32(gc.OMUL) << 16
-		ODIV_    = uint32(gc.ODIV) << 16
-		OMOD_    = uint32(gc.OMOD) << 16
-		OOR_     = uint32(gc.OOR) << 16
-		OAND_    = uint32(gc.OAND) << 16
-		OXOR_    = uint32(gc.OXOR) << 16
-		OEQ_     = uint32(gc.OEQ) << 16
-		ONE_     = uint32(gc.ONE) << 16
-		OLT_     = uint32(gc.OLT) << 16
-		OLE_     = uint32(gc.OLE) << 16
-		OGE_     = uint32(gc.OGE) << 16
-		OGT_     = uint32(gc.OGT) << 16
-		OCMP_    = uint32(gc.OCMP) << 16
-		OAS_     = uint32(gc.OAS) << 16
-		OHMUL_   = uint32(gc.OHMUL) << 16
-		OADDR_   = uint32(gc.OADDR) << 16
-		OINC_    = uint32(gc.OINC) << 16
-		ODEC_    = uint32(gc.ODEC) << 16
-		OLROT_   = uint32(gc.OLROT) << 16
-		OEXTEND_ = uint32(gc.OEXTEND) << 16
-		OCOM_    = uint32(gc.OCOM) << 16
-	)
-
-	a := obj.AXXX
-	switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) {
-	default:
-		gc.Fatalf("optoas: no entry %v-%v", op, t)
-
-	case OADDR_ | gc.TPTR32:
-		a = x86.ALEAL
-
-	case OEQ_ | gc.TBOOL,
-		OEQ_ | gc.TINT8,
-		OEQ_ | gc.TUINT8,
-		OEQ_ | gc.TINT16,
-		OEQ_ | gc.TUINT16,
-		OEQ_ | gc.TINT32,
-		OEQ_ | gc.TUINT32,
-		OEQ_ | gc.TINT64,
-		OEQ_ | gc.TUINT64,
-		OEQ_ | gc.TPTR32,
-		OEQ_ | gc.TPTR64,
-		OEQ_ | gc.TFLOAT32,
-		OEQ_ | gc.TFLOAT64:
-		a = x86.AJEQ
-
-	case ONE_ | gc.TBOOL,
-		ONE_ | gc.TINT8,
-		ONE_ | gc.TUINT8,
-		ONE_ | gc.TINT16,
-		ONE_ | gc.TUINT16,
-		ONE_ | gc.TINT32,
-		ONE_ | gc.TUINT32,
-		ONE_ | gc.TINT64,
-		ONE_ | gc.TUINT64,
-		ONE_ | gc.TPTR32,
-		ONE_ | gc.TPTR64,
-		ONE_ | gc.TFLOAT32,
-		ONE_ | gc.TFLOAT64:
-		a = x86.AJNE
-
-	case OLT_ | gc.TINT8,
-		OLT_ | gc.TINT16,
-		OLT_ | gc.TINT32,
-		OLT_ | gc.TINT64:
-		a = x86.AJLT
-
-	case OLT_ | gc.TUINT8,
-		OLT_ | gc.TUINT16,
-		OLT_ | gc.TUINT32,
-		OLT_ | gc.TUINT64:
-		a = x86.AJCS
-
-	case OLE_ | gc.TINT8,
-		OLE_ | gc.TINT16,
-		OLE_ | gc.TINT32,
-		OLE_ | gc.TINT64:
-		a = x86.AJLE
-
-	case OLE_ | gc.TUINT8,
-		OLE_ | gc.TUINT16,
-		OLE_ | gc.TUINT32,
-		OLE_ | gc.TUINT64:
-		a = x86.AJLS
-
-	case OGT_ | gc.TINT8,
-		OGT_ | gc.TINT16,
-		OGT_ | gc.TINT32,
-		OGT_ | gc.TINT64:
-		a = x86.AJGT
-
-	case OGT_ | gc.TUINT8,
-		OGT_ | gc.TUINT16,
-		OGT_ | gc.TUINT32,
-		OGT_ | gc.TUINT64,
-		OLT_ | gc.TFLOAT32,
-		OLT_ | gc.TFLOAT64:
-		a = x86.AJHI
-
-	case OGE_ | gc.TINT8,
-		OGE_ | gc.TINT16,
-		OGE_ | gc.TINT32,
-		OGE_ | gc.TINT64:
-		a = x86.AJGE
-
-	case OGE_ | gc.TUINT8,
-		OGE_ | gc.TUINT16,
-		OGE_ | gc.TUINT32,
-		OGE_ | gc.TUINT64,
-		OLE_ | gc.TFLOAT32,
-		OLE_ | gc.TFLOAT64:
-		a = x86.AJCC
-
-	case OCMP_ | gc.TBOOL,
-		OCMP_ | gc.TINT8,
-		OCMP_ | gc.TUINT8:
-		a = x86.ACMPB
-
-	case OCMP_ | gc.TINT16,
-		OCMP_ | gc.TUINT16:
-		a = x86.ACMPW
-
-	case OCMP_ | gc.TINT32,
-		OCMP_ | gc.TUINT32,
-		OCMP_ | gc.TPTR32:
-		a = x86.ACMPL
-
-	case OAS_ | gc.TBOOL,
-		OAS_ | gc.TINT8,
-		OAS_ | gc.TUINT8:
-		a = x86.AMOVB
-
-	case OAS_ | gc.TINT16,
-		OAS_ | gc.TUINT16:
-		a = x86.AMOVW
-
-	case OAS_ | gc.TINT32,
-		OAS_ | gc.TUINT32,
-		OAS_ | gc.TPTR32:
-		a = x86.AMOVL
-
-	case OAS_ | gc.TFLOAT32:
-		a = x86.AMOVSS
-
-	case OAS_ | gc.TFLOAT64:
-		a = x86.AMOVSD
-
-	case OADD_ | gc.TINT8,
-		OADD_ | gc.TUINT8:
-		a = x86.AADDB
-
-	case OADD_ | gc.TINT16,
-		OADD_ | gc.TUINT16:
-		a = x86.AADDW
-
-	case OADD_ | gc.TINT32,
-		OADD_ | gc.TUINT32,
-		OADD_ | gc.TPTR32:
-		a = x86.AADDL
-
-	case OSUB_ | gc.TINT8,
-		OSUB_ | gc.TUINT8:
-		a = x86.ASUBB
-
-	case OSUB_ | gc.TINT16,
-		OSUB_ | gc.TUINT16:
-		a = x86.ASUBW
-
-	case OSUB_ | gc.TINT32,
-		OSUB_ | gc.TUINT32,
-		OSUB_ | gc.TPTR32:
-		a = x86.ASUBL
-
-	case OINC_ | gc.TINT8,
-		OINC_ | gc.TUINT8:
-		a = x86.AINCB
-
-	case OINC_ | gc.TINT16,
-		OINC_ | gc.TUINT16:
-		a = x86.AINCW
-
-	case OINC_ | gc.TINT32,
-		OINC_ | gc.TUINT32,
-		OINC_ | gc.TPTR32:
-		a = x86.AINCL
-
-	case ODEC_ | gc.TINT8,
-		ODEC_ | gc.TUINT8:
-		a = x86.ADECB
-
-	case ODEC_ | gc.TINT16,
-		ODEC_ | gc.TUINT16:
-		a = x86.ADECW
-
-	case ODEC_ | gc.TINT32,
-		ODEC_ | gc.TUINT32,
-		ODEC_ | gc.TPTR32:
-		a = x86.ADECL
-
-	case OCOM_ | gc.TINT8,
-		OCOM_ | gc.TUINT8:
-		a = x86.ANOTB
-
-	case OCOM_ | gc.TINT16,
-		OCOM_ | gc.TUINT16:
-		a = x86.ANOTW
-
-	case OCOM_ | gc.TINT32,
-		OCOM_ | gc.TUINT32,
-		OCOM_ | gc.TPTR32:
-		a = x86.ANOTL
-
-	case OMINUS_ | gc.TINT8,
-		OMINUS_ | gc.TUINT8:
-		a = x86.ANEGB
-
-	case OMINUS_ | gc.TINT16,
-		OMINUS_ | gc.TUINT16:
-		a = x86.ANEGW
-
-	case OMINUS_ | gc.TINT32,
-		OMINUS_ | gc.TUINT32,
-		OMINUS_ | gc.TPTR32:
-		a = x86.ANEGL
-
-	case OAND_ | gc.TINT8,
-		OAND_ | gc.TUINT8:
-		a = x86.AANDB
-
-	case OAND_ | gc.TINT16,
-		OAND_ | gc.TUINT16:
-		a = x86.AANDW
-
-	case OAND_ | gc.TINT32,
-		OAND_ | gc.TUINT32,
-		OAND_ | gc.TPTR32:
-		a = x86.AANDL
-
-	case OOR_ | gc.TINT8,
-		OOR_ | gc.TUINT8:
-		a = x86.AORB
-
-	case OOR_ | gc.TINT16,
-		OOR_ | gc.TUINT16:
-		a = x86.AORW
-
-	case OOR_ | gc.TINT32,
-		OOR_ | gc.TUINT32,
-		OOR_ | gc.TPTR32:
-		a = x86.AORL
-
-	case OXOR_ | gc.TINT8,
-		OXOR_ | gc.TUINT8:
-		a = x86.AXORB
-
-	case OXOR_ | gc.TINT16,
-		OXOR_ | gc.TUINT16:
-		a = x86.AXORW
-
-	case OXOR_ | gc.TINT32,
-		OXOR_ | gc.TUINT32,
-		OXOR_ | gc.TPTR32:
-		a = x86.AXORL
-
-	case OLROT_ | gc.TINT8,
-		OLROT_ | gc.TUINT8:
-		a = x86.AROLB
-
-	case OLROT_ | gc.TINT16,
-		OLROT_ | gc.TUINT16:
-		a = x86.AROLW
-
-	case OLROT_ | gc.TINT32,
-		OLROT_ | gc.TUINT32,
-		OLROT_ | gc.TPTR32:
-		a = x86.AROLL
-
-	case OLSH_ | gc.TINT8,
-		OLSH_ | gc.TUINT8:
-		a = x86.ASHLB
-
-	case OLSH_ | gc.TINT16,
-		OLSH_ | gc.TUINT16:
-		a = x86.ASHLW
-
-	case OLSH_ | gc.TINT32,
-		OLSH_ | gc.TUINT32,
-		OLSH_ | gc.TPTR32:
-		a = x86.ASHLL
-
-	case ORSH_ | gc.TUINT8:
-		a = x86.ASHRB
-
-	case ORSH_ | gc.TUINT16:
-		a = x86.ASHRW
-
-	case ORSH_ | gc.TUINT32,
-		ORSH_ | gc.TPTR32:
-		a = x86.ASHRL
-
-	case ORSH_ | gc.TINT8:
-		a = x86.ASARB
-
-	case ORSH_ | gc.TINT16:
-		a = x86.ASARW
-
-	case ORSH_ | gc.TINT32:
-		a = x86.ASARL
-
-	case OHMUL_ | gc.TINT8,
-		OMUL_ | gc.TINT8,
-		OMUL_ | gc.TUINT8:
-		a = x86.AIMULB
-
-	case OHMUL_ | gc.TINT16,
-		OMUL_ | gc.TINT16,
-		OMUL_ | gc.TUINT16:
-		a = x86.AIMULW
-
-	case OHMUL_ | gc.TINT32,
-		OMUL_ | gc.TINT32,
-		OMUL_ | gc.TUINT32,
-		OMUL_ | gc.TPTR32:
-		a = x86.AIMULL
-
-	case OHMUL_ | gc.TUINT8:
-		a = x86.AMULB
-
-	case OHMUL_ | gc.TUINT16:
-		a = x86.AMULW
-
-	case OHMUL_ | gc.TUINT32,
-		OHMUL_ | gc.TPTR32:
-		a = x86.AMULL
-
-	case ODIV_ | gc.TINT8,
-		OMOD_ | gc.TINT8:
-		a = x86.AIDIVB
-
-	case ODIV_ | gc.TUINT8,
-		OMOD_ | gc.TUINT8:
-		a = x86.ADIVB
-
-	case ODIV_ | gc.TINT16,
-		OMOD_ | gc.TINT16:
-		a = x86.AIDIVW
-
-	case ODIV_ | gc.TUINT16,
-		OMOD_ | gc.TUINT16:
-		a = x86.ADIVW
-
-	case ODIV_ | gc.TINT32,
-		OMOD_ | gc.TINT32:
-		a = x86.AIDIVL
-
-	case ODIV_ | gc.TUINT32,
-		ODIV_ | gc.TPTR32,
-		OMOD_ | gc.TUINT32,
-		OMOD_ | gc.TPTR32:
-		a = x86.ADIVL
-
-	case OEXTEND_ | gc.TINT16:
-		a = x86.ACWD
-
-	case OEXTEND_ | gc.TINT32:
-		a = x86.ACDQ
-	}
-
-	return a
-}
-
-func foptoas(op gc.Op, t *gc.Type, flg int) obj.As {
-	a := obj.AXXX
-	et := gc.Simtype[t.Etype]
-
-	// avoid constant conversions in switches below
-	const (
-		OCMP_   = uint32(gc.OCMP) << 16
-		OAS_    = uint32(gc.OAS) << 16
-		OADD_   = uint32(gc.OADD) << 16
-		OSUB_   = uint32(gc.OSUB) << 16
-		OMUL_   = uint32(gc.OMUL) << 16
-		ODIV_   = uint32(gc.ODIV) << 16
-		OMINUS_ = uint32(gc.OMINUS) << 16
-	)
-
-	if !gc.Thearch.Use387 {
-		switch uint32(op)<<16 | uint32(et) {
-		default:
-			gc.Fatalf("foptoas-sse: no entry %v-%v", op, t)
-
-		case OCMP_ | gc.TFLOAT32:
-			a = x86.AUCOMISS
-
-		case OCMP_ | gc.TFLOAT64:
-			a = x86.AUCOMISD
-
-		case OAS_ | gc.TFLOAT32:
-			a = x86.AMOVSS
-
-		case OAS_ | gc.TFLOAT64:
-			a = x86.AMOVSD
-
-		case OADD_ | gc.TFLOAT32:
-			a = x86.AADDSS
-
-		case OADD_ | gc.TFLOAT64:
-			a = x86.AADDSD
-
-		case OSUB_ | gc.TFLOAT32:
-			a = x86.ASUBSS
-
-		case OSUB_ | gc.TFLOAT64:
-			a = x86.ASUBSD
-
-		case OMUL_ | gc.TFLOAT32:
-			a = x86.AMULSS
-
-		case OMUL_ | gc.TFLOAT64:
-			a = x86.AMULSD
-
-		case ODIV_ | gc.TFLOAT32:
-			a = x86.ADIVSS
-
-		case ODIV_ | gc.TFLOAT64:
-			a = x86.ADIVSD
-		}
-
-		return a
-	}
-
-	// If we need Fpop, it means we're working on
-	// two different floating-point registers, not memory.
-	// There the instruction only has a float64 form.
-	if flg&Fpop != 0 {
-		et = gc.TFLOAT64
-	}
-
-	// clear Frev if unneeded
-	switch op {
-	case gc.OADD,
-		gc.OMUL:
-		flg &^= Frev
-	}
-
-	switch uint32(op)<<16 | (uint32(et)<<8 | uint32(flg)) {
-	case OADD_ | (gc.TFLOAT32<<8 | 0):
-		return x86.AFADDF
-
-	case OADD_ | (gc.TFLOAT64<<8 | 0):
-		return x86.AFADDD
-
-	case OADD_ | (gc.TFLOAT64<<8 | Fpop):
-		return x86.AFADDDP
-
-	case OSUB_ | (gc.TFLOAT32<<8 | 0):
-		return x86.AFSUBF
-
-	case OSUB_ | (gc.TFLOAT32<<8 | Frev):
-		return x86.AFSUBRF
-
-	case OSUB_ | (gc.TFLOAT64<<8 | 0):
-		return x86.AFSUBD
-
-	case OSUB_ | (gc.TFLOAT64<<8 | Frev):
-		return x86.AFSUBRD
-
-	case OSUB_ | (gc.TFLOAT64<<8 | Fpop):
-		return x86.AFSUBDP
-
-	case OSUB_ | (gc.TFLOAT64<<8 | (Fpop | Frev)):
-		return x86.AFSUBRDP
-
-	case OMUL_ | (gc.TFLOAT32<<8 | 0):
-		return x86.AFMULF
-
-	case OMUL_ | (gc.TFLOAT64<<8 | 0):
-		return x86.AFMULD
-
-	case OMUL_ | (gc.TFLOAT64<<8 | Fpop):
-		return x86.AFMULDP
-
-	case ODIV_ | (gc.TFLOAT32<<8 | 0):
-		return x86.AFDIVF
-
-	case ODIV_ | (gc.TFLOAT32<<8 | Frev):
-		return x86.AFDIVRF
-
-	case ODIV_ | (gc.TFLOAT64<<8 | 0):
-		return x86.AFDIVD
-
-	case ODIV_ | (gc.TFLOAT64<<8 | Frev):
-		return x86.AFDIVRD
-
-	case ODIV_ | (gc.TFLOAT64<<8 | Fpop):
-		return x86.AFDIVDP
-
-	case ODIV_ | (gc.TFLOAT64<<8 | (Fpop | Frev)):
-		return x86.AFDIVRDP
-
-	case OCMP_ | (gc.TFLOAT32<<8 | 0):
-		return x86.AFCOMF
-
-	case OCMP_ | (gc.TFLOAT32<<8 | Fpop):
-		return x86.AFCOMFP
-
-	case OCMP_ | (gc.TFLOAT64<<8 | 0):
-		return x86.AFCOMD
-
-	case OCMP_ | (gc.TFLOAT64<<8 | Fpop):
-		return x86.AFCOMDP
-
-	case OCMP_ | (gc.TFLOAT64<<8 | Fpop2):
-		return x86.AFCOMDPP
-
-	case OMINUS_ | (gc.TFLOAT32<<8 | 0):
-		return x86.AFCHS
-
-	case OMINUS_ | (gc.TFLOAT64<<8 | 0):
-		return x86.AFCHS
-	}
-
-	gc.Fatalf("foptoas %v %v %#x", op, t, flg)
-	return 0
-}
-
-var resvd = []int{
-	//	REG_DI,	// for movstring
-	//	REG_SI,	// for movstring
-
-	x86.REG_AX, // for divide
-	x86.REG_CX, // for shift
-	x86.REG_DX, // for divide, context
-	x86.REG_SP, // for stack
-}
-
-/*
- * generate
- *	as $c, reg
- */
-func gconreg(as obj.As, c int64, reg int) {
-	var n1 gc.Node
-	var n2 gc.Node
-
-	gc.Nodconst(&n1, gc.Types[gc.TINT64], c)
-	gc.Nodreg(&n2, gc.Types[gc.TINT64], reg)
-	gins(as, &n1, &n2)
-}
-
-/*
- * generate
- *	as $c, n
- */
-func ginscon(as obj.As, c int64, n2 *gc.Node) {
-	var n1 gc.Node
-	gc.Nodconst(&n1, gc.Types[gc.TINT32], c)
-	gins(as, &n1, n2)
-}
-
-func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
-	if t.IsInteger() || t.Etype == gc.Tptr {
-		if (n1.Op == gc.OLITERAL || n1.Op == gc.OADDR && n1.Left.Op == gc.ONAME) && n2.Op != gc.OLITERAL {
-			// Reverse comparison to place constant (including address constant) last.
-			op = gc.Brrev(op)
-			n1, n2 = n2, n1
-		}
-	}
-
-	// General case.
-	var r1, r2, g1, g2 gc.Node
-
-	// A special case to make write barriers more efficient.
-	// Comparing the first field of a named struct can be done directly.
-	base := n1
-	if n1.Op == gc.ODOT && n1.Left.Type.IsStruct() && n1.Left.Type.Field(0).Sym == n1.Sym {
-		base = n1.Left
-	}
-
-	if base.Op == gc.ONAME && base.Class != gc.PAUTOHEAP || n1.Op == gc.OINDREG {
-		r1 = *n1
-	} else {
-		gc.Regalloc(&r1, t, n1)
-		gc.Regalloc(&g1, n1.Type, &r1)
-		gc.Cgen(n1, &g1)
-		gmove(&g1, &r1)
-	}
-	if n2.Op == gc.OLITERAL && t.IsInteger() || n2.Op == gc.OADDR && n2.Left.Op == gc.ONAME && n2.Left.Class == gc.PEXTERN {
-		r2 = *n2
-	} else {
-		gc.Regalloc(&r2, t, n2)
-		gc.Regalloc(&g2, n1.Type, &r2)
-		gc.Cgen(n2, &g2)
-		gmove(&g2, &r2)
-	}
-	gins(optoas(gc.OCMP, t), &r1, &r2)
-	if r1.Op == gc.OREGISTER {
-		gc.Regfree(&g1)
-		gc.Regfree(&r1)
-	}
-	if r2.Op == gc.OREGISTER {
-		gc.Regfree(&g2)
-		gc.Regfree(&r2)
-	}
-	return gc.Gbranch(optoas(op, t), nil, likely)
-}
-
-/*
- * swap node contents
- */
-func nswap(a *gc.Node, b *gc.Node) {
-	t := *a
-	*a = *b
-	*b = t
-}
-
-/*
- * return constant i node.
- * overwritten by next call, but useful in calls to gins.
- */
-
-var ncon_n gc.Node
-
-func ncon(i uint32) *gc.Node {
-	if ncon_n.Type == nil {
-		gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0)
-	}
-	ncon_n.SetInt(int64(i))
-	return &ncon_n
-}
-
-var sclean [10]gc.Node
-
-var nsclean int
-
-/*
- * n is a 64-bit value.  fill in lo and hi to refer to its 32-bit halves.
- */
-func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
-	if !gc.Is64(n.Type) {
-		gc.Fatalf("split64 %v", n.Type)
-	}
-
-	if nsclean >= len(sclean) {
-		gc.Fatalf("split64 clean")
-	}
-	sclean[nsclean].Op = gc.OEMPTY
-	nsclean++
-	switch n.Op {
-	default:
-		switch n.Op {
-		default:
-			var n1 gc.Node
-			if !dotaddable(n, &n1) {
-				gc.Igen(n, &n1, nil)
-				sclean[nsclean-1] = n1
-			}
-
-			n = &n1
-
-		case gc.ONAME, gc.OINDREG:
-			// nothing
-		}
-
-		*lo = *n
-		*hi = *n
-		lo.Type = gc.Types[gc.TUINT32]
-		if n.Type.Etype == gc.TINT64 {
-			hi.Type = gc.Types[gc.TINT32]
-		} else {
-			hi.Type = gc.Types[gc.TUINT32]
-		}
-		hi.Xoffset += 4
-
-	case gc.OLITERAL:
-		var n1 gc.Node
-		n.Convconst(&n1, n.Type)
-		i := n1.Int64()
-		gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i)))
-		i >>= 32
-		if n.Type.Etype == gc.TINT64 {
-			gc.Nodconst(hi, gc.Types[gc.TINT32], int64(int32(i)))
-		} else {
-			gc.Nodconst(hi, gc.Types[gc.TUINT32], int64(uint32(i)))
-		}
-	}
-}
-
-func splitclean() {
-	if nsclean <= 0 {
-		gc.Fatalf("splitclean")
-	}
-	nsclean--
-	if sclean[nsclean].Op != gc.OEMPTY {
-		gc.Regfree(&sclean[nsclean])
-	}
-}
-
-// set up nodes representing fp constants
-var (
-	zerof        gc.Node
-	two63f       gc.Node
-	two64f       gc.Node
-	bignodes_did bool
-)
-
-func bignodes() {
-	if bignodes_did {
-		return
-	}
-	bignodes_did = true
-
-	gc.Nodconst(&zerof, gc.Types[gc.TINT64], 0)
-	zerof.Convconst(&zerof, gc.Types[gc.TFLOAT64])
-
-	var i big.Int
-	i.SetInt64(1)
-	i.Lsh(&i, 63)
-	var bigi gc.Node
-
-	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
-	bigi.SetBigInt(&i)
-	bigi.Convconst(&two63f, gc.Types[gc.TFLOAT64])
-
-	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
-	i.Lsh(&i, 1)
-	bigi.SetBigInt(&i)
-	bigi.Convconst(&two64f, gc.Types[gc.TFLOAT64])
-}
-
-func memname(n *gc.Node, t *gc.Type) {
-	gc.Tempname(n, t)
-	n.Sym = gc.Lookup("." + n.Sym.Name[1:]) // keep optimizer from registerizing
-	n.Orig.Sym = n.Sym
-}
-
-func gmove(f *gc.Node, t *gc.Node) {
-	if gc.Debug['M'] != 0 {
-		fmt.Printf("gmove %v -> %v\n", f, t)
-	}
-
-	ft := gc.Simsimtype(f.Type)
-	tt := gc.Simsimtype(t.Type)
-	cvt := t.Type
-
-	if gc.Iscomplex[ft] || gc.Iscomplex[tt] {
-		gc.Complexmove(f, t)
-		return
-	}
-
-	if gc.Isfloat[ft] || gc.Isfloat[tt] {
-		floatmove(f, t)
-		return
-	}
-
-	// cannot have two integer memory operands;
-	// except 64-bit, which always copies via registers anyway.
-	var r1 gc.Node
-	var a obj.As
-	if gc.Isint[ft] && gc.Isint[tt] && !gc.Is64(f.Type) && !gc.Is64(t.Type) && gc.Ismem(f) && gc.Ismem(t) {
-		goto hard
-	}
-
-	// convert constant to desired type
-	if f.Op == gc.OLITERAL {
-		var con gc.Node
-		f.Convconst(&con, t.Type)
-		f = &con
-		ft = gc.Simsimtype(con.Type)
-	}
-
-	// value -> value copy, only one memory operand.
-	// figure out the instruction to use.
-	// break out of switch for one-instruction gins.
-	// goto rdst for "destination must be register".
-	// goto hard for "convert to cvt type first".
-	// otherwise handle and return.
-
-	switch uint32(ft)<<16 | uint32(tt) {
-	default:
-		// should not happen
-		gc.Fatalf("gmove %v -> %v", f, t)
-		return
-
-		/*
-		 * integer copy and truncate
-		 */
-	case gc.TINT8<<16 | gc.TINT8, // same size
-		gc.TINT8<<16 | gc.TUINT8,
-		gc.TUINT8<<16 | gc.TINT8,
-		gc.TUINT8<<16 | gc.TUINT8:
-		a = x86.AMOVB
-
-	case gc.TINT16<<16 | gc.TINT8, // truncate
-		gc.TUINT16<<16 | gc.TINT8,
-		gc.TINT32<<16 | gc.TINT8,
-		gc.TUINT32<<16 | gc.TINT8,
-		gc.TINT16<<16 | gc.TUINT8,
-		gc.TUINT16<<16 | gc.TUINT8,
-		gc.TINT32<<16 | gc.TUINT8,
-		gc.TUINT32<<16 | gc.TUINT8:
-		a = x86.AMOVB
-
-		goto rsrc
-
-	case gc.TINT64<<16 | gc.TINT8, // truncate low word
-		gc.TUINT64<<16 | gc.TINT8,
-		gc.TINT64<<16 | gc.TUINT8,
-		gc.TUINT64<<16 | gc.TUINT8:
-		var flo gc.Node
-		var fhi gc.Node
-		split64(f, &flo, &fhi)
-
-		var r1 gc.Node
-		gc.Nodreg(&r1, t.Type, x86.REG_AX)
-		gmove(&flo, &r1)
-		gins(x86.AMOVB, &r1, t)
-		splitclean()
-		return
-
-	case gc.TINT16<<16 | gc.TINT16, // same size
-		gc.TINT16<<16 | gc.TUINT16,
-		gc.TUINT16<<16 | gc.TINT16,
-		gc.TUINT16<<16 | gc.TUINT16:
-		a = x86.AMOVW
-
-	case gc.TINT32<<16 | gc.TINT16, // truncate
-		gc.TUINT32<<16 | gc.TINT16,
-		gc.TINT32<<16 | gc.TUINT16,
-		gc.TUINT32<<16 | gc.TUINT16:
-		a = x86.AMOVW
-
-		goto rsrc
-
-	case gc.TINT64<<16 | gc.TINT16, // truncate low word
-		gc.TUINT64<<16 | gc.TINT16,
-		gc.TINT64<<16 | gc.TUINT16,
-		gc.TUINT64<<16 | gc.TUINT16:
-		var flo gc.Node
-		var fhi gc.Node
-		split64(f, &flo, &fhi)
-
-		var r1 gc.Node
-		gc.Nodreg(&r1, t.Type, x86.REG_AX)
-		gmove(&flo, &r1)
-		gins(x86.AMOVW, &r1, t)
-		splitclean()
-		return
-
-	case gc.TINT32<<16 | gc.TINT32, // same size
-		gc.TINT32<<16 | gc.TUINT32,
-		gc.TUINT32<<16 | gc.TINT32,
-		gc.TUINT32<<16 | gc.TUINT32:
-		a = x86.AMOVL
-
-	case gc.TINT64<<16 | gc.TINT32, // truncate
-		gc.TUINT64<<16 | gc.TINT32,
-		gc.TINT64<<16 | gc.TUINT32,
-		gc.TUINT64<<16 | gc.TUINT32:
-		var fhi gc.Node
-		var flo gc.Node
-		split64(f, &flo, &fhi)
-
-		var r1 gc.Node
-		gc.Nodreg(&r1, t.Type, x86.REG_AX)
-		gmove(&flo, &r1)
-		gins(x86.AMOVL, &r1, t)
-		splitclean()
-		return
-
-	case gc.TINT64<<16 | gc.TINT64, // same size
-		gc.TINT64<<16 | gc.TUINT64,
-		gc.TUINT64<<16 | gc.TINT64,
-		gc.TUINT64<<16 | gc.TUINT64:
-		var fhi gc.Node
-		var flo gc.Node
-		split64(f, &flo, &fhi)
-
-		var tlo gc.Node
-		var thi gc.Node
-		split64(t, &tlo, &thi)
-		if f.Op == gc.OLITERAL {
-			gins(x86.AMOVL, &flo, &tlo)
-			gins(x86.AMOVL, &fhi, &thi)
-		} else {
-			// Implementation of conversion-free x = y for int64 or uint64 x.
-			// This is generated by the code that copies small values out of closures,
-			// and that code has DX live, so avoid DX and just use AX twice.
-			var r1 gc.Node
-			gc.Nodreg(&r1, gc.Types[gc.TUINT32], x86.REG_AX)
-			gins(x86.AMOVL, &flo, &r1)
-			gins(x86.AMOVL, &r1, &tlo)
-			gins(x86.AMOVL, &fhi, &r1)
-			gins(x86.AMOVL, &r1, &thi)
-		}
-
-		splitclean()
-		splitclean()
-		return
-
-		/*
-		 * integer up-conversions
-		 */
-	case gc.TINT8<<16 | gc.TINT16, // sign extend int8
-		gc.TINT8<<16 | gc.TUINT16:
-		a = x86.AMOVBWSX
-
-		goto rdst
-
-	case gc.TINT8<<16 | gc.TINT32,
-		gc.TINT8<<16 | gc.TUINT32:
-		a = x86.AMOVBLSX
-		goto rdst
-
-	case gc.TINT8<<16 | gc.TINT64, // convert via int32
-		gc.TINT8<<16 | gc.TUINT64:
-		cvt = gc.Types[gc.TINT32]
-
-		goto hard
-
-	case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8
-		gc.TUINT8<<16 | gc.TUINT16:
-		a = x86.AMOVBWZX
-
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TINT32,
-		gc.TUINT8<<16 | gc.TUINT32:
-		a = x86.AMOVBLZX
-		goto rdst
-
-	case gc.TUINT8<<16 | gc.TINT64, // convert via uint32
-		gc.TUINT8<<16 | gc.TUINT64:
-		cvt = gc.Types[gc.TUINT32]
-
-		goto hard
-
-	case gc.TINT16<<16 | gc.TINT32, // sign extend int16
-		gc.TINT16<<16 | gc.TUINT32:
-		a = x86.AMOVWLSX
-
-		goto rdst
-
-	case gc.TINT16<<16 | gc.TINT64, // convert via int32
-		gc.TINT16<<16 | gc.TUINT64:
-		cvt = gc.Types[gc.TINT32]
-
-		goto hard
-
-	case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16
-		gc.TUINT16<<16 | gc.TUINT32:
-		a = x86.AMOVWLZX
-
-		goto rdst
-
-	case gc.TUINT16<<16 | gc.TINT64, // convert via uint32
-		gc.TUINT16<<16 | gc.TUINT64:
-		cvt = gc.Types[gc.TUINT32]
-
-		goto hard
-
-	case gc.TINT32<<16 | gc.TINT64, // sign extend int32
-		gc.TINT32<<16 | gc.TUINT64:
-		var thi gc.Node
-		var tlo gc.Node
-		split64(t, &tlo, &thi)
-
-		var flo gc.Node
-		gc.Nodreg(&flo, tlo.Type, x86.REG_AX)
-		var fhi gc.Node
-		gc.Nodreg(&fhi, thi.Type, x86.REG_DX)
-		gmove(f, &flo)
-		gins(x86.ACDQ, nil, nil)
-		gins(x86.AMOVL, &flo, &tlo)
-		gins(x86.AMOVL, &fhi, &thi)
-		splitclean()
-		return
-
-	case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32
-		gc.TUINT32<<16 | gc.TUINT64:
-		var tlo gc.Node
-		var thi gc.Node
-		split64(t, &tlo, &thi)
-
-		gmove(f, &tlo)
-		gins(x86.AMOVL, ncon(0), &thi)
-		splitclean()
-		return
-	}
-
-	gins(a, f, t)
-	return
-
-	// requires register source
-rsrc:
-	gc.Regalloc(&r1, f.Type, t)
-
-	gmove(f, &r1)
-	gins(a, &r1, t)
-	gc.Regfree(&r1)
-	return
-
-	// requires register destination
-rdst:
-	{
-		gc.Regalloc(&r1, t.Type, t)
-
-		gins(a, f, &r1)
-		gmove(&r1, t)
-		gc.Regfree(&r1)
-		return
-	}
-
-	// requires register intermediate
-hard:
-	gc.Regalloc(&r1, cvt, t)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-}
-
-func floatmove(f *gc.Node, t *gc.Node) {
-	var r1 gc.Node
-
-	ft := gc.Simsimtype(f.Type)
-	tt := gc.Simsimtype(t.Type)
-	cvt := t.Type
-
-	// cannot have two floating point memory operands.
-	if gc.Isfloat[ft] && gc.Isfloat[tt] && gc.Ismem(f) && gc.Ismem(t) {
-		goto hard
-	}
-
-	// convert constant to desired type
-	if f.Op == gc.OLITERAL {
-		var con gc.Node
-		f.Convconst(&con, t.Type)
-		f = &con
-		ft = gc.Simsimtype(con.Type)
-
-		// some constants can't move directly to memory.
-		if gc.Ismem(t) {
-			// float constants come from memory.
-			if gc.Isfloat[tt] {
-				goto hard
-			}
-		}
-	}
-
-	// value -> value copy, only one memory operand.
-	// figure out the instruction to use.
-	// break out of switch for one-instruction gins.
-	// goto rdst for "destination must be register".
-	// goto hard for "convert to cvt type first".
-	// otherwise handle and return.
-
-	switch uint32(ft)<<16 | uint32(tt) {
-	default:
-		if gc.Thearch.Use387 {
-			floatmove_387(f, t)
-		} else {
-			floatmove_sse(f, t)
-		}
-		return
-
-		// float to very long integer.
-	case gc.TFLOAT32<<16 | gc.TINT64,
-		gc.TFLOAT64<<16 | gc.TINT64:
-		if f.Op == gc.OREGISTER {
-			cvt = f.Type
-			goto hardmem
-		}
-
-		var r1 gc.Node
-		gc.Nodreg(&r1, gc.Types[ft], x86.REG_F0)
-		if ft == gc.TFLOAT32 {
-			gins(x86.AFMOVF, f, &r1)
-		} else {
-			gins(x86.AFMOVD, f, &r1)
-		}
-
-		// set round to zero mode during conversion
-		var t1 gc.Node
-		memname(&t1, gc.Types[gc.TUINT16])
-
-		var t2 gc.Node
-		memname(&t2, gc.Types[gc.TUINT16])
-		gins(x86.AFSTCW, nil, &t1)
-		gins(x86.AMOVW, ncon(0xf7f), &t2)
-		gins(x86.AFLDCW, &t2, nil)
-		if tt == gc.TINT16 {
-			gins(x86.AFMOVWP, &r1, t)
-		} else if tt == gc.TINT32 {
-			gins(x86.AFMOVLP, &r1, t)
-		} else {
-			gins(x86.AFMOVVP, &r1, t)
-		}
-		gins(x86.AFLDCW, &t1, nil)
-		return
-
-	case gc.TFLOAT32<<16 | gc.TUINT64,
-		gc.TFLOAT64<<16 | gc.TUINT64:
-		if !gc.Ismem(f) {
-			cvt = f.Type
-			goto hardmem
-		}
-
-		bignodes()
-		var f0 gc.Node
-		gc.Nodreg(&f0, gc.Types[ft], x86.REG_F0)
-		var f1 gc.Node
-		gc.Nodreg(&f1, gc.Types[ft], x86.REG_F0+1)
-		var ax gc.Node
-		gc.Nodreg(&ax, gc.Types[gc.TUINT16], x86.REG_AX)
-
-		if ft == gc.TFLOAT32 {
-			gins(x86.AFMOVF, f, &f0)
-		} else {
-			gins(x86.AFMOVD, f, &f0)
-		}
-
-		// if 0 > v { answer = 0 }
-		gins(x86.AFMOVD, &zerof, &f0)
-		gins(x86.AFUCOMP, &f0, &f1)
-		gins(x86.AFSTSW, nil, &ax)
-		gins(x86.ASAHF, nil, nil)
-		p1 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0)
-
-		// if 1<<64 <= v { answer = 0 too }
-		gins(x86.AFMOVD, &two64f, &f0)
-
-		gins(x86.AFUCOMP, &f0, &f1)
-		gins(x86.AFSTSW, nil, &ax)
-		gins(x86.ASAHF, nil, nil)
-		p2 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0)
-		gc.Patch(p1, gc.Pc)
-		gins(x86.AFMOVVP, &f0, t) // don't care about t, but will pop the stack
-		var thi gc.Node
-		var tlo gc.Node
-		split64(t, &tlo, &thi)
-		gins(x86.AMOVL, ncon(0), &tlo)
-		gins(x86.AMOVL, ncon(0), &thi)
-		splitclean()
-		p1 = gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p2, gc.Pc)
-
-		// in range; algorithm is:
-		//	if small enough, use native float64 -> int64 conversion.
-		//	otherwise, subtract 2^63, convert, and add it back.
-
-		// set round to zero mode during conversion
-		var t1 gc.Node
-		memname(&t1, gc.Types[gc.TUINT16])
-
-		var t2 gc.Node
-		memname(&t2, gc.Types[gc.TUINT16])
-		gins(x86.AFSTCW, nil, &t1)
-		gins(x86.AMOVW, ncon(0xf7f), &t2)
-		gins(x86.AFLDCW, &t2, nil)
-
-		// actual work
-		gins(x86.AFMOVD, &two63f, &f0)
-
-		gins(x86.AFUCOMP, &f0, &f1)
-		gins(x86.AFSTSW, nil, &ax)
-		gins(x86.ASAHF, nil, nil)
-		p2 = gc.Gbranch(optoas(gc.OLE, gc.Types[tt]), nil, 0)
-		gins(x86.AFMOVVP, &f0, t)
-		p3 := gc.Gbranch(obj.AJMP, nil, 0)
-		gc.Patch(p2, gc.Pc)
-		gins(x86.AFMOVD, &two63f, &f0)
-		gins(x86.AFSUBDP, &f0, &f1)
-		gins(x86.AFMOVVP, &f0, t)
-		split64(t, &tlo, &thi)
-		gins(x86.AXORL, ncon(0x80000000), &thi) // + 2^63
-		gc.Patch(p3, gc.Pc)
-		splitclean()
-
-		// restore rounding mode
-		gins(x86.AFLDCW, &t1, nil)
-
-		gc.Patch(p1, gc.Pc)
-		return
-
-		/*
-		 * integer to float
-		 */
-	case gc.TINT64<<16 | gc.TFLOAT32,
-		gc.TINT64<<16 | gc.TFLOAT64:
-		if t.Op == gc.OREGISTER {
-			goto hardmem
-		}
-		var f0 gc.Node
-		gc.Nodreg(&f0, t.Type, x86.REG_F0)
-		gins(x86.AFMOVV, f, &f0)
-		if tt == gc.TFLOAT32 {
-			gins(x86.AFMOVFP, &f0, t)
-		} else {
-			gins(x86.AFMOVDP, &f0, t)
-		}
-		return
-
-		// algorithm is:
-	//	if small enough, use native int64 -> float64 conversion.
-	//	otherwise, halve (rounding to odd?), convert, and double.
-	case gc.TUINT64<<16 | gc.TFLOAT32,
-		gc.TUINT64<<16 | gc.TFLOAT64:
-		var ax gc.Node
-		gc.Nodreg(&ax, gc.Types[gc.TUINT32], x86.REG_AX)
-
-		var dx gc.Node
-		gc.Nodreg(&dx, gc.Types[gc.TUINT32], x86.REG_DX)
-		var cx gc.Node
-		gc.Nodreg(&cx, gc.Types[gc.TUINT32], x86.REG_CX)
-		var t1 gc.Node
-		gc.Tempname(&t1, f.Type)
-		var tlo gc.Node
-		var thi gc.Node
-		split64(&t1, &tlo, &thi)
-		gmove(f, &t1)
-		gins(x86.ACMPL, &thi, ncon(0))
-		p1 := gc.Gbranch(x86.AJLT, nil, 0)
-
-		// native
-		var r1 gc.Node
-		gc.Nodreg(&r1, gc.Types[tt], x86.REG_F0)
-
-		gins(x86.AFMOVV, &t1, &r1)
-		if tt == gc.TFLOAT32 {
-			gins(x86.AFMOVFP, &r1, t)
-		} else {
-			gins(x86.AFMOVDP, &r1, t)
-		}
-		p2 := gc.Gbranch(obj.AJMP, nil, 0)
-
-		// simulated
-		gc.Patch(p1, gc.Pc)
-
-		gmove(&tlo, &ax)
-		gmove(&thi, &dx)
-		p1 = gins(x86.ASHRL, ncon(1), &ax)
-		p1.From.Index = x86.REG_DX // double-width shift DX -> AX
-		p1.From.Scale = 0
-		gins(x86.AMOVL, ncon(0), &cx)
-		gins(x86.ASETCC, nil, &cx)
-		gins(x86.AORL, &cx, &ax)
-		gins(x86.ASHRL, ncon(1), &dx)
-		gmove(&dx, &thi)
-		gmove(&ax, &tlo)
-		gc.Nodreg(&r1, gc.Types[tt], x86.REG_F0)
-		var r2 gc.Node
-		gc.Nodreg(&r2, gc.Types[tt], x86.REG_F0+1)
-		gins(x86.AFMOVV, &t1, &r1)
-		gins(x86.AFMOVD, &r1, &r1)
-		gins(x86.AFADDDP, &r1, &r2)
-		if tt == gc.TFLOAT32 {
-			gins(x86.AFMOVFP, &r1, t)
-		} else {
-			gins(x86.AFMOVDP, &r1, t)
-		}
-		gc.Patch(p2, gc.Pc)
-		splitclean()
-		return
-	}
-
-	// requires register intermediate
-hard:
-	gc.Regalloc(&r1, cvt, t)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-
-	// requires memory intermediate
-hardmem:
-	gc.Tempname(&r1, cvt)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	return
-}
-
-func floatmove_387(f *gc.Node, t *gc.Node) {
-	var r1 gc.Node
-	var a obj.As
-
-	ft := gc.Simsimtype(f.Type)
-	tt := gc.Simsimtype(t.Type)
-	cvt := t.Type
-
-	switch uint32(ft)<<16 | uint32(tt) {
-	default:
-		goto fatal
-
-		/*
-		* float to integer
-		 */
-	case gc.TFLOAT32<<16 | gc.TINT16,
-		gc.TFLOAT32<<16 | gc.TINT32,
-		gc.TFLOAT32<<16 | gc.TINT64,
-		gc.TFLOAT64<<16 | gc.TINT16,
-		gc.TFLOAT64<<16 | gc.TINT32,
-		gc.TFLOAT64<<16 | gc.TINT64:
-		if t.Op == gc.OREGISTER {
-			goto hardmem
-		}
-		var r1 gc.Node
-		gc.Nodreg(&r1, gc.Types[ft], x86.REG_F0)
-		if f.Op != gc.OREGISTER {
-			if ft == gc.TFLOAT32 {
-				gins(x86.AFMOVF, f, &r1)
-			} else {
-				gins(x86.AFMOVD, f, &r1)
-			}
-		}
-
-		// set round to zero mode during conversion
-		var t1 gc.Node
-		memname(&t1, gc.Types[gc.TUINT16])
-
-		var t2 gc.Node
-		memname(&t2, gc.Types[gc.TUINT16])
-		gins(x86.AFSTCW, nil, &t1)
-		gins(x86.AMOVW, ncon(0xf7f), &t2)
-		gins(x86.AFLDCW, &t2, nil)
-		if tt == gc.TINT16 {
-			gins(x86.AFMOVWP, &r1, t)
-		} else if tt == gc.TINT32 {
-			gins(x86.AFMOVLP, &r1, t)
-		} else {
-			gins(x86.AFMOVVP, &r1, t)
-		}
-		gins(x86.AFLDCW, &t1, nil)
-		return
-
-		// convert via int32.
-	case gc.TFLOAT32<<16 | gc.TINT8,
-		gc.TFLOAT32<<16 | gc.TUINT16,
-		gc.TFLOAT32<<16 | gc.TUINT8,
-		gc.TFLOAT64<<16 | gc.TINT8,
-		gc.TFLOAT64<<16 | gc.TUINT16,
-		gc.TFLOAT64<<16 | gc.TUINT8:
-		var t1 gc.Node
-		gc.Tempname(&t1, gc.Types[gc.TINT32])
-
-		gmove(f, &t1)
-		switch tt {
-		default:
-			gc.Fatalf("gmove %v", t)
-
-		case gc.TINT8:
-			gins(x86.ACMPL, &t1, ncon(-0x80&(1<<32-1)))
-			p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TINT32]), nil, -1)
-			gins(x86.ACMPL, &t1, ncon(0x7f))
-			p2 := gc.Gbranch(optoas(gc.OGT, gc.Types[gc.TINT32]), nil, -1)
-			p3 := gc.Gbranch(obj.AJMP, nil, 0)
-			gc.Patch(p1, gc.Pc)
-			gc.Patch(p2, gc.Pc)
-			gmove(ncon(-0x80&(1<<32-1)), &t1)
-			gc.Patch(p3, gc.Pc)
-			gmove(&t1, t)
-
-		case gc.TUINT8:
-			gins(x86.ATESTL, ncon(0xffffff00), &t1)
-			p1 := gc.Gbranch(x86.AJEQ, nil, +1)
-			gins(x86.AMOVL, ncon(0), &t1)
-			gc.Patch(p1, gc.Pc)
-			gmove(&t1, t)
-
-		case gc.TUINT16:
-			gins(x86.ATESTL, ncon(0xffff0000), &t1)
-			p1 := gc.Gbranch(x86.AJEQ, nil, +1)
-			gins(x86.AMOVL, ncon(0), &t1)
-			gc.Patch(p1, gc.Pc)
-			gmove(&t1, t)
-		}
-
-		return
-
-		// convert via int64.
-	case gc.TFLOAT32<<16 | gc.TUINT32,
-		gc.TFLOAT64<<16 | gc.TUINT32:
-		cvt = gc.Types[gc.TINT64]
-
-		goto hardmem
-
-		/*
-		 * integer to float
-		 */
-	case gc.TINT16<<16 | gc.TFLOAT32,
-		gc.TINT16<<16 | gc.TFLOAT64,
-		gc.TINT32<<16 | gc.TFLOAT32,
-		gc.TINT32<<16 | gc.TFLOAT64,
-		gc.TINT64<<16 | gc.TFLOAT32,
-		gc.TINT64<<16 | gc.TFLOAT64:
-		if t.Op != gc.OREGISTER {
-			goto hard
-		}
-		if f.Op == gc.OREGISTER {
-			cvt = f.Type
-			goto hardmem
-		}
-
-		switch ft {
-		case gc.TINT16:
-			a = x86.AFMOVW
-
-		case gc.TINT32:
-			a = x86.AFMOVL
-
-		default:
-			a = x86.AFMOVV
-		}
-
-		// convert via int32 memory
-	case gc.TINT8<<16 | gc.TFLOAT32,
-		gc.TINT8<<16 | gc.TFLOAT64,
-		gc.TUINT16<<16 | gc.TFLOAT32,
-		gc.TUINT16<<16 | gc.TFLOAT64,
-		gc.TUINT8<<16 | gc.TFLOAT32,
-		gc.TUINT8<<16 | gc.TFLOAT64:
-		cvt = gc.Types[gc.TINT32]
-
-		goto hardmem
-
-		// convert via int64 memory
-	case gc.TUINT32<<16 | gc.TFLOAT32,
-		gc.TUINT32<<16 | gc.TFLOAT64:
-		cvt = gc.Types[gc.TINT64]
-
-		goto hardmem
-
-		// The way the code generator uses floating-point
-	// registers, a move from F0 to F0 is intended as a no-op.
-	// On the x86, it's not: it pushes a second copy of F0
-	// on the floating point stack. So toss it away here.
-	// Also, F0 is the *only* register we ever evaluate
-	// into, so we should only see register/register as F0/F0.
-	/*
-	 * float to float
-	 */
-	case gc.TFLOAT32<<16 | gc.TFLOAT32,
-		gc.TFLOAT64<<16 | gc.TFLOAT64:
-		if gc.Ismem(f) && gc.Ismem(t) {
-			goto hard
-		}
-		if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER {
-			if f.Reg != x86.REG_F0 || t.Reg != x86.REG_F0 {
-				goto fatal
-			}
-			return
-		}
-
-		a = x86.AFMOVF
-		if ft == gc.TFLOAT64 {
-			a = x86.AFMOVD
-		}
-		if gc.Ismem(t) {
-			if f.Op != gc.OREGISTER || f.Reg != x86.REG_F0 {
-				gc.Fatalf("gmove %v", f)
-			}
-			a = x86.AFMOVFP
-			if ft == gc.TFLOAT64 {
-				a = x86.AFMOVDP
-			}
-		}
-
-	case gc.TFLOAT32<<16 | gc.TFLOAT64:
-		if gc.Ismem(f) && gc.Ismem(t) {
-			goto hard
-		}
-		if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER {
-			if f.Reg != x86.REG_F0 || t.Reg != x86.REG_F0 {
-				goto fatal
-			}
-			return
-		}
-
-		if f.Op == gc.OREGISTER {
-			gins(x86.AFMOVDP, f, t)
-		} else {
-			gins(x86.AFMOVF, f, t)
-		}
-		return
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT32:
-		if gc.Ismem(f) && gc.Ismem(t) {
-			goto hard
-		}
-		if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER {
-			var r1 gc.Node
-			gc.Tempname(&r1, gc.Types[gc.TFLOAT32])
-			gins(x86.AFMOVFP, f, &r1)
-			gins(x86.AFMOVF, &r1, t)
-			return
-		}
-
-		if f.Op == gc.OREGISTER {
-			gins(x86.AFMOVFP, f, t)
-		} else {
-			gins(x86.AFMOVD, f, t)
-		}
-		return
-	}
-
-	gins(a, f, t)
-	return
-
-	// requires register intermediate
-hard:
-	gc.Regalloc(&r1, cvt, t)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-
-	// requires memory intermediate
-hardmem:
-	gc.Tempname(&r1, cvt)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	return
-
-	// should not happen
-fatal:
-	gc.Fatalf("gmove %v -> %v", gc.Nconv(f, gc.FmtLong), gc.Nconv(t, gc.FmtLong))
-
-	return
-}
-
-func floatmove_sse(f *gc.Node, t *gc.Node) {
-	var r1 gc.Node
-	var cvt *gc.Type
-	var a obj.As
-
-	ft := gc.Simsimtype(f.Type)
-	tt := gc.Simsimtype(t.Type)
-
-	switch uint32(ft)<<16 | uint32(tt) {
-	// should not happen
-	default:
-		gc.Fatalf("gmove %v -> %v", f, t)
-
-		return
-
-		// convert via int32.
-	/*
-	* float to integer
-	 */
-	case gc.TFLOAT32<<16 | gc.TINT16,
-		gc.TFLOAT32<<16 | gc.TINT8,
-		gc.TFLOAT32<<16 | gc.TUINT16,
-		gc.TFLOAT32<<16 | gc.TUINT8,
-		gc.TFLOAT64<<16 | gc.TINT16,
-		gc.TFLOAT64<<16 | gc.TINT8,
-		gc.TFLOAT64<<16 | gc.TUINT16,
-		gc.TFLOAT64<<16 | gc.TUINT8:
-		cvt = gc.Types[gc.TINT32]
-
-		goto hard
-
-		// convert via int64.
-	case gc.TFLOAT32<<16 | gc.TUINT32,
-		gc.TFLOAT64<<16 | gc.TUINT32:
-		cvt = gc.Types[gc.TINT64]
-
-		goto hardmem
-
-	case gc.TFLOAT32<<16 | gc.TINT32:
-		a = x86.ACVTTSS2SL
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TINT32:
-		a = x86.ACVTTSD2SL
-		goto rdst
-
-		// convert via int32 memory
-	/*
-	 * integer to float
-	 */
-	case gc.TINT8<<16 | gc.TFLOAT32,
-		gc.TINT8<<16 | gc.TFLOAT64,
-		gc.TINT16<<16 | gc.TFLOAT32,
-		gc.TINT16<<16 | gc.TFLOAT64,
-		gc.TUINT16<<16 | gc.TFLOAT32,
-		gc.TUINT16<<16 | gc.TFLOAT64,
-		gc.TUINT8<<16 | gc.TFLOAT32,
-		gc.TUINT8<<16 | gc.TFLOAT64:
-		cvt = gc.Types[gc.TINT32]
-
-		goto hard
-
-		// convert via int64 memory
-	case gc.TUINT32<<16 | gc.TFLOAT32,
-		gc.TUINT32<<16 | gc.TFLOAT64:
-		cvt = gc.Types[gc.TINT64]
-
-		goto hardmem
-
-	case gc.TINT32<<16 | gc.TFLOAT32:
-		a = x86.ACVTSL2SS
-		goto rdst
-
-	case gc.TINT32<<16 | gc.TFLOAT64:
-		a = x86.ACVTSL2SD
-		goto rdst
-
-		/*
-		 * float to float
-		 */
-	case gc.TFLOAT32<<16 | gc.TFLOAT32:
-		a = x86.AMOVSS
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT64:
-		a = x86.AMOVSD
-
-	case gc.TFLOAT32<<16 | gc.TFLOAT64:
-		a = x86.ACVTSS2SD
-		goto rdst
-
-	case gc.TFLOAT64<<16 | gc.TFLOAT32:
-		a = x86.ACVTSD2SS
-		goto rdst
-	}
-
-	gins(a, f, t)
-	return
-
-	// requires register intermediate
-hard:
-	gc.Regalloc(&r1, cvt, t)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-
-	// requires memory intermediate
-hardmem:
-	gc.Tempname(&r1, cvt)
-
-	gmove(f, &r1)
-	gmove(&r1, t)
-	return
-
-	// requires register destination
-rdst:
-	gc.Regalloc(&r1, t.Type, t)
-
-	gins(a, f, &r1)
-	gmove(&r1, t)
-	gc.Regfree(&r1)
-	return
-}
-
-func samaddr(f *gc.Node, t *gc.Node) bool {
-	if f.Op != t.Op {
-		return false
-	}
-
-	switch f.Op {
-	case gc.OREGISTER:
-		if f.Reg != t.Reg {
-			break
-		}
-		return true
-	}
-
-	return false
-}
-
-/*
- * generate one instruction:
- *	as f, t
- */
-func gins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog {
-	if as == x86.AFMOVF && f != nil && f.Op == gc.OREGISTER && t != nil && t.Op == gc.OREGISTER {
-		gc.Fatalf("gins MOVF reg, reg")
-	}
-	if as == x86.ACVTSD2SS && f != nil && f.Op == gc.OLITERAL {
-		gc.Fatalf("gins CVTSD2SS const")
-	}
-	if as == x86.AMOVSD && t != nil && t.Op == gc.OREGISTER && t.Reg == x86.REG_F0 {
-		gc.Fatalf("gins MOVSD into F0")
-	}
-
-	if as == x86.AMOVL && f != nil && f.Op == gc.OADDR && f.Left.Op == gc.ONAME && f.Left.Class != gc.PEXTERN && f.Left.Class != gc.PFUNC {
-		// Turn MOVL $xxx(FP/SP) into LEAL xxx.
-		// These should be equivalent but most of the backend
-		// only expects to see LEAL, because that's what we had
-		// historically generated. Various hidden assumptions are baked in by now.
-		as = x86.ALEAL
-		f = f.Left
-	}
-
-	switch as {
-	case x86.AMOVB,
-		x86.AMOVW,
-		x86.AMOVL:
-		if f != nil && t != nil && samaddr(f, t) {
-			return nil
-		}
-
-	case x86.ALEAL:
-		if f != nil && gc.Isconst(f, gc.CTNIL) {
-			gc.Fatalf("gins LEAL nil %v", f.Type)
-		}
-	}
-
-	p := gc.Prog(as)
-	gc.Naddr(&p.From, f)
-	gc.Naddr(&p.To, t)
-
-	if gc.Debug['g'] != 0 {
-		fmt.Printf("%v\n", p)
-	}
-
-	w := 0
-	switch as {
-	case x86.AMOVB:
-		w = 1
-
-	case x86.AMOVW:
-		w = 2
-
-	case x86.AMOVL:
-		w = 4
-	}
-
-	if true && w != 0 && f != nil && (p.From.Width > int64(w) || p.To.Width > int64(w)) {
-		gc.Dump("bad width from:", f)
-		gc.Dump("bad width to:", t)
-		gc.Fatalf("bad width: %v (%d, %d)\n", p, p.From.Width, p.To.Width)
-	}
-
-	if p.To.Type == obj.TYPE_ADDR && w > 0 {
-		gc.Fatalf("bad use of addr: %v", p)
-	}
-
-	return p
-}
-
-func ginsnop() {
-	var reg gc.Node
-	gc.Nodreg(&reg, gc.Types[gc.TINT], x86.REG_AX)
-	gins(x86.AXCHGL, &reg, &reg)
-}
-
-func dotaddable(n *gc.Node, n1 *gc.Node) bool {
-	if n.Op != gc.ODOT {
-		return false
-	}
-
-	var oary [10]int64
-	var nn *gc.Node
-	o := gc.Dotoffset(n, oary[:], &nn)
-	if nn != nil && nn.Addable && o == 1 && oary[0] >= 0 {
-		*n1 = *nn
-		n1.Type = n.Type
-		n1.Xoffset += oary[0]
-		return true
-	}
-
-	return false
-}
-
-func sudoclean() {
-}
-
-func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool {
-	*a = obj.Addr{}
-	return false
-}
diff --git a/src/cmd/compile/internal/x86/peep.go b/src/cmd/compile/internal/x86/peep.go
deleted file mode 100644
index e70c10f..0000000
--- a/src/cmd/compile/internal/x86/peep.go
+++ /dev/null
@@ -1,807 +0,0 @@
-// Derived from Inferno utils/6c/peep.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package x86
-
-import (
-	"cmd/compile/internal/gc"
-	"cmd/internal/obj"
-	"cmd/internal/obj/x86"
-	"fmt"
-)
-
-const (
-	REGEXT      = 0
-	exregoffset = x86.REG_DI
-)
-
-var gactive uint32
-
-// do we need the carry bit
-func needc(p *obj.Prog) bool {
-	for p != nil {
-		if p.Info.Flags&gc.UseCarry != 0 {
-			return true
-		}
-		if p.Info.Flags&(gc.SetCarry|gc.KillCarry) != 0 {
-			return false
-		}
-		p = p.Link
-	}
-
-	return false
-}
-
-func rnops(r *gc.Flow) *gc.Flow {
-	if r != nil {
-		var p *obj.Prog
-		var r1 *gc.Flow
-		for {
-			p = r.Prog
-			if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE {
-				break
-			}
-			r1 = gc.Uniqs(r)
-			if r1 == nil {
-				break
-			}
-			r = r1
-		}
-	}
-
-	return r
-}
-
-func peep(firstp *obj.Prog) {
-	g := gc.Flowstart(firstp, nil)
-	if g == nil {
-		return
-	}
-	gactive = 0
-
-	// byte, word arithmetic elimination.
-	elimshortmov(g)
-
-	// constant propagation
-	// find MOV $con,R followed by
-	// another MOV $con,R without
-	// setting R in the interim
-	var p *obj.Prog
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		case x86.ALEAL:
-			if regtyp(&p.To) {
-				if p.From.Sym != nil {
-					if p.From.Index == x86.REG_NONE {
-						conprop(r)
-					}
-				}
-			}
-
-		case x86.AMOVB,
-			x86.AMOVW,
-			x86.AMOVL,
-			x86.AMOVSS,
-			x86.AMOVSD:
-			if regtyp(&p.To) {
-				if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST {
-					conprop(r)
-				}
-			}
-		}
-	}
-
-	var r1 *gc.Flow
-	var p1 *obj.Prog
-	var r *gc.Flow
-	var t int
-loop1:
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		gc.Dumpit("loop1", g.Start, 0)
-	}
-
-	t = 0
-	for r = g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		switch p.As {
-		case x86.AMOVL,
-			x86.AMOVSS,
-			x86.AMOVSD:
-			if regtyp(&p.To) {
-				if regtyp(&p.From) {
-					if copyprop(g, r) {
-						excise(r)
-						t++
-					} else if subprop(r) && copyprop(g, r) {
-						excise(r)
-						t++
-					}
-				}
-			}
-
-		case x86.AMOVBLZX,
-			x86.AMOVWLZX,
-			x86.AMOVBLSX,
-			x86.AMOVWLSX:
-			if regtyp(&p.To) {
-				r1 = rnops(gc.Uniqs(r))
-				if r1 != nil {
-					p1 = r1.Prog
-					if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg {
-						p1.As = x86.AMOVL
-						t++
-					}
-				}
-			}
-
-		case x86.AADDL,
-			x86.AADDW:
-			if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
-				break
-			}
-			if p.From.Offset == -1 {
-				if p.As == x86.AADDL {
-					p.As = x86.ADECL
-				} else {
-					p.As = x86.ADECW
-				}
-				p.From = obj.Addr{}
-				break
-			}
-
-			if p.From.Offset == 1 {
-				if p.As == x86.AADDL {
-					p.As = x86.AINCL
-				} else {
-					p.As = x86.AINCW
-				}
-				p.From = obj.Addr{}
-				break
-			}
-
-		case x86.ASUBL,
-			x86.ASUBW:
-			if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
-				break
-			}
-			if p.From.Offset == -1 {
-				if p.As == x86.ASUBL {
-					p.As = x86.AINCL
-				} else {
-					p.As = x86.AINCW
-				}
-				p.From = obj.Addr{}
-				break
-			}
-
-			if p.From.Offset == 1 {
-				if p.As == x86.ASUBL {
-					p.As = x86.ADECL
-				} else {
-					p.As = x86.ADECW
-				}
-				p.From = obj.Addr{}
-				break
-			}
-		}
-	}
-
-	if t != 0 {
-		goto loop1
-	}
-
-	// MOVSD removal.
-	// We never use packed registers, so a MOVSD between registers
-	// can be replaced by MOVAPD, which moves the pair of float64s
-	// instead of just the lower one. We only use the lower one, but
-	// the processor can do better if we do moves using both.
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		if p.As == x86.AMOVSD {
-			if regtyp(&p.From) {
-				if regtyp(&p.To) {
-					p.As = x86.AMOVAPD
-				}
-			}
-		}
-	}
-
-	gc.Flowend(g)
-}
-
-func excise(r *gc.Flow) {
-	p := r.Prog
-	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-		fmt.Printf("%v ===delete===\n", p)
-	}
-
-	obj.Nopout(p)
-
-	gc.Ostats.Ndelmov++
-}
-
-func regtyp(a *obj.Addr) bool {
-	if gc.Ctxt.Flag_shared && a.Type == obj.TYPE_REG && a.Reg == x86.REG_CX {
-		// don't propagate CX, it is used implicitly by PIC global references
-		return false
-	}
-	return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_DI || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X7)
-}
-
-// movb elimination.
-// movb is simulated by the linker
-// when a register other than ax, bx, cx, dx
-// is used, so rewrite to other instructions
-// when possible.  a movb into a register
-// can smash the entire 64-bit register without
-// causing any trouble.
-func elimshortmov(g *gc.Graph) {
-	var p *obj.Prog
-
-	for r := g.Start; r != nil; r = r.Link {
-		p = r.Prog
-		if regtyp(&p.To) {
-			switch p.As {
-			case x86.AINCB,
-				x86.AINCW:
-				p.As = x86.AINCL
-
-			case x86.ADECB,
-				x86.ADECW:
-				p.As = x86.ADECL
-
-			case x86.ANEGB,
-				x86.ANEGW:
-				p.As = x86.ANEGL
-
-			case x86.ANOTB,
-				x86.ANOTW:
-				p.As = x86.ANOTL
-			}
-
-			if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST {
-				// move or arithmetic into partial register.
-				// from another register or constant can be movl.
-				// we don't switch to 32-bit arithmetic if it can
-				// change how the carry bit is set (and the carry bit is needed).
-				switch p.As {
-				case x86.AMOVB,
-					x86.AMOVW:
-					p.As = x86.AMOVL
-
-				case x86.AADDB,
-					x86.AADDW:
-					if !needc(p.Link) {
-						p.As = x86.AADDL
-					}
-
-				case x86.ASUBB,
-					x86.ASUBW:
-					if !needc(p.Link) {
-						p.As = x86.ASUBL
-					}
-
-				case x86.AMULB,
-					x86.AMULW:
-					p.As = x86.AMULL
-
-				case x86.AIMULB,
-					x86.AIMULW:
-					p.As = x86.AIMULL
-
-				case x86.AANDB,
-					x86.AANDW:
-					p.As = x86.AANDL
-
-				case x86.AORB,
-					x86.AORW:
-					p.As = x86.AORL
-
-				case x86.AXORB,
-					x86.AXORW:
-					p.As = x86.AXORL
-
-				case x86.ASHLB,
-					x86.ASHLW:
-					p.As = x86.ASHLL
-				}
-			} else {
-				// explicit zero extension
-				switch p.As {
-				case x86.AMOVB:
-					p.As = x86.AMOVBLZX
-
-				case x86.AMOVW:
-					p.As = x86.AMOVWLZX
-				}
-			}
-		}
-	}
-}
-
-/*
- * the idea is to substitute
- * one register for another
- * from one MOV to another
- *	MOV	a, R0
- *	ADD	b, R0	/ no use of R1
- *	MOV	R0, R1
- * would be converted to
- *	MOV	a, R1
- *	ADD	b, R1
- *	MOV	R1, R0
- * hopefully, then the former or latter MOV
- * will be eliminated by copy propagation.
- */
-func subprop(r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	if !regtyp(v1) {
-		return false
-	}
-	v2 := &p.To
-	if !regtyp(v2) {
-		return false
-	}
-	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
-		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
-			fmt.Printf("\t? %v\n", r.Prog)
-		}
-		if gc.Uniqs(r) == nil {
-			break
-		}
-		p = r.Prog
-		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
-			continue
-		}
-		if p.Info.Flags&gc.Call != 0 {
-			return false
-		}
-
-		if p.Info.Reguse|p.Info.Regset != 0 {
-			return false
-		}
-
-		if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg {
-			copysub(&p.To, v1, v2, true)
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
-				if p.From.Type == v2.Type && p.From.Reg == v2.Reg {
-					fmt.Printf(" excise")
-				}
-				fmt.Printf("\n")
-			}
-
-			for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
-				p = r.Prog
-				copysub(&p.From, v1, v2, true)
-				copysub(&p.To, v1, v2, true)
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("%v\n", r.Prog)
-				}
-			}
-
-			t := int(v1.Reg)
-			v1.Reg = v2.Reg
-			v2.Reg = int16(t)
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("%v last\n", r.Prog)
-			}
-			return true
-		}
-
-		if copyau(&p.From, v2) || copyau(&p.To, v2) {
-			break
-		}
-		if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) {
-			break
-		}
-	}
-
-	return false
-}
-
-/*
- * The idea is to remove redundant copies.
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	use v2	return fail
- *	-----------------
- *	v1->v2	F=0
- *	(use v2	s/v2/v1/)*
- *	set v1	F=1
- *	set v2	return success
- */
-func copyprop(g *gc.Graph, r0 *gc.Flow) bool {
-	p := r0.Prog
-	v1 := &p.From
-	v2 := &p.To
-	if copyas(v1, v2) {
-		return true
-	}
-	gactive++
-	return copy1(v1, v2, r0.S1, false)
-}
-
-func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool {
-	if uint32(r.Active) == gactive {
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("act set; return 1\n")
-		}
-		return true
-	}
-
-	r.Active = int32(gactive)
-	if gc.Debug['P'] != 0 {
-		fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f)
-	}
-	for ; r != nil; r = r.S1 {
-		p := r.Prog
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("%v", p)
-		}
-		if !f && gc.Uniqp(r) == nil {
-			f = true
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; merge; f=%v", f)
-			}
-		}
-
-		switch t := copyu(p, v2, nil); t {
-		case 2: /* rar, can't split */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
-			}
-			return false
-
-		case 3: /* set */
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
-			}
-			return true
-
-		case 1, /* used, substitute */
-			4: /* use and set */
-			if f {
-				if gc.Debug['P'] == 0 {
-					return false
-				}
-				if t == 4 {
-					fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				} else {
-					fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
-				}
-				return false
-			}
-
-			if copyu(p, v2, v1) != 0 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; sub fail; return 0\n")
-				}
-				return false
-			}
-
-			if gc.Debug['P'] != 0 {
-				fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1))
-			}
-			if t == 4 {
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
-				}
-				return true
-			}
-		}
-
-		if !f {
-			t := copyu(p, v1, nil)
-			if t == 2 || t == 3 || t == 4 {
-				f = true
-				if gc.Debug['P'] != 0 {
-					fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f)
-				}
-			}
-		}
-
-		if gc.Debug['P'] != 0 {
-			fmt.Printf("\n")
-		}
-		if r.S2 != nil {
-			if !copy1(v1, v2, r.S2, f) {
-				return false
-			}
-		}
-	}
-	return true
-}
-
-/*
- * return
- * 1 if v only used (and substitute),
- * 2 if read-alter-rewrite
- * 3 if set
- * 4 if set and used
- * 0 otherwise (not touched)
- */
-func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
-	switch p.As {
-	case obj.AJMP:
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.To, v) {
-			return 1
-		}
-		return 0
-
-	case obj.ARET:
-		if s != nil {
-			return 1
-		}
-		return 3
-
-	case obj.ACALL:
-		if REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= REGEXT && v.Reg > exregoffset {
-			return 2
-		}
-		if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
-			return 2
-		}
-		if v.Type == p.From.Type && v.Reg == p.From.Reg {
-			return 2
-		}
-
-		if s != nil {
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-
-		if copyau(&p.To, v) {
-			return 4
-		}
-		return 3
-
-	case obj.ATEXT:
-		if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
-			return 3
-		}
-		return 0
-	}
-
-	if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
-		return 0
-	}
-
-	if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 {
-		return 2
-	}
-
-	if p.Info.Flags&gc.LeftAddr != 0 {
-		if copyas(&p.From, v) {
-			return 2
-		}
-	}
-
-	if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite {
-		if copyas(&p.To, v) {
-			return 2
-		}
-	}
-
-	if p.Info.Flags&gc.RightWrite != 0 {
-		if copyas(&p.To, v) {
-			if s != nil {
-				if copysub(&p.From, v, s, true) {
-					return 1
-				}
-				return 0
-			}
-			if copyau(&p.From, v) {
-				return 4
-			}
-			return 3
-		}
-	}
-
-	if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 {
-		if s != nil {
-			if copysub(&p.From, v, s, true) {
-				return 1
-			}
-			if copysub(&p.To, v, s, true) {
-				return 1
-			}
-			return 0
-		}
-		if copyau(&p.From, v) {
-			return 1
-		}
-		if copyau(&p.To, v) {
-			return 1
-		}
-	}
-	return 0
-}
-
-/*
- * direct reference,
- * could be set/use depending on
- * semantics
- */
-func copyas(a *obj.Addr, v *obj.Addr) bool {
-	if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_BL {
-		gc.Fatalf("use of byte register")
-	}
-	if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL {
-		gc.Fatalf("use of byte register")
-	}
-
-	if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
-		return false
-	}
-	if regtyp(v) {
-		return true
-	}
-	if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
-		if v.Offset == a.Offset {
-			return true
-		}
-	}
-	return false
-}
-
-func sameaddr(a *obj.Addr, v *obj.Addr) bool {
-	if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
-		return false
-	}
-	if regtyp(v) {
-		return true
-	}
-	if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
-		if v.Offset == a.Offset {
-			return true
-		}
-	}
-	return false
-}
-
-/*
- * either direct or indirect
- */
-func copyau(a *obj.Addr, v *obj.Addr) bool {
-	if copyas(a, v) {
-		return true
-	}
-	if regtyp(v) {
-		if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg {
-			return true
-		}
-		if a.Index == v.Reg {
-			return true
-		}
-	}
-
-	return false
-}
-
-// copysub substitute s for v in a.
-// copysub returns true on failure to substitute.
-// TODO(dfc) reverse this logic to return false on sunstitution failure.
-func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool {
-	if copyas(a, v) {
-		if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_DI || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X7 {
-			if f {
-				a.Reg = s.Reg
-			}
-		}
-		return false
-	}
-
-	if regtyp(v) {
-		if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg {
-			if (s.Reg == x86.REG_BP) && a.Index != x86.REG_NONE {
-				return true /* can't use BP-base with index */
-			}
-			if f {
-				a.Reg = s.Reg
-			}
-		}
-
-		if a.Index == v.Reg {
-			if f {
-				a.Index = s.Reg
-			}
-		}
-	}
-	return false
-}
-
-func conprop(r0 *gc.Flow) {
-	var p *obj.Prog
-
-	p0 := r0.Prog
-	v0 := &p0.To
-	r := r0
-
-loop:
-	r = gc.Uniqs(r)
-	if r == nil || r == r0 {
-		return
-	}
-	if gc.Uniqp(r) == nil {
-		return
-	}
-
-	p = r.Prog
-	switch copyu(p, v0, nil) {
-	case 0, // miss
-		1: // use
-		goto loop
-
-	case 2, // rar
-		4: // use and set
-		break
-
-	case 3: // set
-		if p.As == p0.As {
-			if p.From.Type == p0.From.Type {
-				if p.From.Reg == p0.From.Reg {
-					if p.From.Node == p0.From.Node {
-						if p.From.Offset == p0.From.Offset {
-							if p.From.Scale == p0.From.Scale {
-								if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) {
-									if p.From.Index == p0.From.Index {
-										excise(r)
-										goto loop
-									}
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-}
-
-func smallindir(a *obj.Addr, reg *obj.Addr) bool {
-	return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096
-}
-
-func stackaddr(a *obj.Addr) bool {
-	return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP
-}
diff --git a/src/cmd/compile/internal/x86/prog.go b/src/cmd/compile/internal/x86/prog.go
index f2b4a65..e46bdb7 100644
--- a/src/cmd/compile/internal/x86/prog.go
+++ b/src/cmd/compile/internal/x86/prog.go
@@ -10,13 +10,7 @@ import (
 	"cmd/internal/obj/x86"
 )
 
-var (
-	AX               = RtoB(x86.REG_AX)
-	BX               = RtoB(x86.REG_BX)
-	CX               = RtoB(x86.REG_CX)
-	DX               = RtoB(x86.REG_DX)
-	DI               = RtoB(x86.REG_DI)
-	SI               = RtoB(x86.REG_SI)
+const (
 	LeftRdwr  uint32 = gc.LeftRead | gc.LeftWrite
 	RightRdwr uint32 = gc.RightRead | gc.RightWrite
 )
@@ -30,14 +24,13 @@ var (
 // size variants of an operation even if we just use a subset.
 //
 // The table is formatted for 8-space tabs.
-var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
+var progtable = [x86.ALAST & obj.AMask]gc.ProgInfo{
 	obj.ATYPE:     {Flags: gc.Pseudo | gc.Skip},
 	obj.ATEXT:     {Flags: gc.Pseudo},
 	obj.AFUNCDATA: {Flags: gc.Pseudo},
 	obj.APCDATA:   {Flags: gc.Pseudo},
 	obj.AUNDEF:    {Flags: gc.Break},
 	obj.AUSEFIELD: {Flags: gc.OK},
-	obj.ACHECKNIL: {Flags: gc.LeftRead},
 	obj.AVARDEF:   {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARKILL:  {Flags: gc.Pseudo | gc.RightWrite},
 	obj.AVARLIVE:  {Flags: gc.Pseudo | gc.LeftRead},
@@ -56,8 +49,8 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.AANDL & obj.AMask:      {Flags: gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry},
 	x86.AANDW & obj.AMask:      {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry},
 	obj.ACALL:                  {Flags: gc.RightAddr | gc.Call | gc.KillCarry},
-	x86.ACDQ & obj.AMask:       {Flags: gc.OK, Reguse: AX, Regset: AX | DX},
-	x86.ACWD & obj.AMask:       {Flags: gc.OK, Reguse: AX, Regset: AX | DX},
+	x86.ACDQ & obj.AMask:       {Flags: gc.OK},
+	x86.ACWD & obj.AMask:       {Flags: gc.OK},
 	x86.ACLD & obj.AMask:       {Flags: gc.OK},
 	x86.ASTD & obj.AMask:       {Flags: gc.OK},
 	x86.ACMPB & obj.AMask:      {Flags: gc.SizeB | gc.LeftRead | gc.RightRead | gc.SetCarry},
@@ -76,9 +69,9 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ADECB & obj.AMask:      {Flags: gc.SizeB | RightRdwr},
 	x86.ADECL & obj.AMask:      {Flags: gc.SizeL | RightRdwr},
 	x86.ADECW & obj.AMask:      {Flags: gc.SizeW | RightRdwr},
-	x86.ADIVB & obj.AMask:      {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX},
-	x86.ADIVL & obj.AMask:      {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry, Reguse: AX | DX, Regset: AX | DX},
-	x86.ADIVW & obj.AMask:      {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry, Reguse: AX | DX, Regset: AX | DX},
+	x86.ADIVB & obj.AMask:      {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry},
+	x86.ADIVL & obj.AMask:      {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry},
+	x86.ADIVW & obj.AMask:      {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry},
 	x86.ADIVSD & obj.AMask:     {Flags: gc.SizeD | gc.LeftRead | RightRdwr},
 	x86.ADIVSS & obj.AMask:     {Flags: gc.SizeF | gc.LeftRead | RightRdwr},
 	x86.AFLDCW & obj.AMask:     {Flags: gc.SizeW | gc.LeftAddr},
@@ -131,10 +124,10 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.AFMULD & obj.AMask:   {Flags: gc.SizeD | gc.LeftAddr | RightRdwr},
 	x86.AFMULDP & obj.AMask:  {Flags: gc.SizeD | gc.LeftAddr | RightRdwr},
 	x86.AFMULF & obj.AMask:   {Flags: gc.SizeF | gc.LeftAddr | RightRdwr},
-	x86.AIDIVB & obj.AMask:   {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX},
-	x86.AIDIVL & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry, Reguse: AX | DX, Regset: AX | DX},
-	x86.AIDIVW & obj.AMask:   {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry, Reguse: AX | DX, Regset: AX | DX},
-	x86.AIMULB & obj.AMask:   {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX},
+	x86.AIDIVB & obj.AMask:   {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry},
+	x86.AIDIVL & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry},
+	x86.AIDIVW & obj.AMask:   {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry},
+	x86.AIMULB & obj.AMask:   {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry},
 	x86.AIMULL & obj.AMask:   {Flags: gc.SizeL | gc.LeftRead | gc.ImulAXDX | gc.SetCarry},
 	x86.AIMULW & obj.AMask:   {Flags: gc.SizeW | gc.LeftRead | gc.ImulAXDX | gc.SetCarry},
 	x86.AINCB & obj.AMask:    {Flags: gc.SizeB | RightRdwr},
@@ -168,18 +161,18 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.AMOVB & obj.AMask:    {Flags: gc.SizeB | gc.LeftRead | gc.RightWrite | gc.Move},
 	x86.AMOVL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.RightWrite | gc.Move},
 	x86.AMOVW & obj.AMask:    {Flags: gc.SizeW | gc.LeftRead | gc.RightWrite | gc.Move},
-	x86.AMOVSB & obj.AMask:   {Flags: gc.OK, Reguse: DI | SI, Regset: DI | SI},
-	x86.AMOVSL & obj.AMask:   {Flags: gc.OK, Reguse: DI | SI, Regset: DI | SI},
-	x86.AMOVSW & obj.AMask:   {Flags: gc.OK, Reguse: DI | SI, Regset: DI | SI},
-	obj.ADUFFCOPY:            {Flags: gc.OK, Reguse: DI | SI, Regset: DI | SI | CX},
+	x86.AMOVSB & obj.AMask:   {Flags: gc.OK},
+	x86.AMOVSL & obj.AMask:   {Flags: gc.OK},
+	x86.AMOVSW & obj.AMask:   {Flags: gc.OK},
+	obj.ADUFFCOPY:            {Flags: gc.OK},
 	x86.AMOVSD & obj.AMask:   {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
 	x86.AMOVSS & obj.AMask:   {Flags: gc.SizeF | gc.LeftRead | gc.RightWrite | gc.Move},
 
 	// We use MOVAPD as a faster synonym for MOVSD.
 	x86.AMOVAPD & obj.AMask:  {Flags: gc.SizeD | gc.LeftRead | gc.RightWrite | gc.Move},
-	x86.AMULB & obj.AMask:    {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX},
-	x86.AMULL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX | DX},
-	x86.AMULW & obj.AMask:    {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry, Reguse: AX, Regset: AX | DX},
+	x86.AMULB & obj.AMask:    {Flags: gc.SizeB | gc.LeftRead | gc.SetCarry},
+	x86.AMULL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | gc.SetCarry},
+	x86.AMULW & obj.AMask:    {Flags: gc.SizeW | gc.LeftRead | gc.SetCarry},
 	x86.AMULSD & obj.AMask:   {Flags: gc.SizeD | gc.LeftRead | RightRdwr},
 	x86.AMULSS & obj.AMask:   {Flags: gc.SizeF | gc.LeftRead | RightRdwr},
 	x86.ANEGB & obj.AMask:    {Flags: gc.SizeB | RightRdwr | gc.SetCarry},
@@ -200,8 +193,8 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ARCRB & obj.AMask:    {Flags: gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry},
 	x86.ARCRL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry},
 	x86.ARCRW & obj.AMask:    {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry | gc.UseCarry},
-	x86.AREP & obj.AMask:     {Flags: gc.OK, Reguse: CX, Regset: CX},
-	x86.AREPN & obj.AMask:    {Flags: gc.OK, Reguse: CX, Regset: CX},
+	x86.AREP & obj.AMask:     {Flags: gc.OK},
+	x86.AREPN & obj.AMask:    {Flags: gc.OK},
 	obj.ARET:                 {Flags: gc.Break | gc.KillCarry},
 	x86.AROLB & obj.AMask:    {Flags: gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
 	x86.AROLL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
@@ -209,7 +202,7 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ARORB & obj.AMask:    {Flags: gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
 	x86.ARORL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
 	x86.ARORW & obj.AMask:    {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
-	x86.ASAHF & obj.AMask:    {Flags: gc.OK, Reguse: AX, Regset: AX},
+	x86.ASAHF & obj.AMask:    {Flags: gc.OK},
 	x86.ASALB & obj.AMask:    {Flags: gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
 	x86.ASALL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
 	x86.ASALW & obj.AMask:    {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
@@ -241,10 +234,10 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.ASHRB & obj.AMask:    {Flags: gc.SizeB | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
 	x86.ASHRL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
 	x86.ASHRW & obj.AMask:    {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.ShiftCX | gc.SetCarry},
-	x86.ASTOSB & obj.AMask:   {Flags: gc.OK, Reguse: AX | DI, Regset: DI},
-	x86.ASTOSL & obj.AMask:   {Flags: gc.OK, Reguse: AX | DI, Regset: DI},
-	x86.ASTOSW & obj.AMask:   {Flags: gc.OK, Reguse: AX | DI, Regset: DI},
-	obj.ADUFFZERO:            {Flags: gc.OK, Reguse: AX | DI, Regset: DI},
+	x86.ASTOSB & obj.AMask:   {Flags: gc.OK},
+	x86.ASTOSL & obj.AMask:   {Flags: gc.OK},
+	x86.ASTOSW & obj.AMask:   {Flags: gc.OK},
+	obj.ADUFFZERO:            {Flags: gc.OK},
 	x86.ASUBB & obj.AMask:    {Flags: gc.SizeB | gc.LeftRead | RightRdwr | gc.SetCarry},
 	x86.ASUBL & obj.AMask:    {Flags: gc.SizeL | gc.LeftRead | RightRdwr | gc.SetCarry},
 	x86.ASUBW & obj.AMask:    {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry},
@@ -263,37 +256,15 @@ var progtable = [x86.ALAST & obj.AMask]obj.ProgInfo{
 	x86.AXORW & obj.AMask:    {Flags: gc.SizeW | gc.LeftRead | RightRdwr | gc.SetCarry},
 }
 
-func proginfo(p *obj.Prog) {
-	info := &p.Info
-	*info = progtable[p.As&obj.AMask]
+func proginfo(p *obj.Prog) gc.ProgInfo {
+	info := progtable[p.As&obj.AMask]
 	if info.Flags == 0 {
 		gc.Fatalf("unknown instruction %v", p)
 	}
 
-	if (info.Flags&gc.ShiftCX != 0) && p.From.Type != obj.TYPE_CONST {
-		info.Reguse |= CX
+	if info.Flags&gc.ImulAXDX != 0 && p.To.Type != obj.TYPE_NONE {
+		info.Flags |= RightRdwr
 	}
 
-	if info.Flags&gc.ImulAXDX != 0 {
-		if p.To.Type == obj.TYPE_NONE {
-			info.Reguse |= AX
-			info.Regset |= AX | DX
-		} else {
-			info.Flags |= RightRdwr
-		}
-	}
-
-	// Addressing makes some registers used.
-	if p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_NONE {
-		info.Regindex |= RtoB(int(p.From.Reg))
-	}
-	if p.From.Index != x86.REG_NONE {
-		info.Regindex |= RtoB(int(p.From.Index))
-	}
-	if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE {
-		info.Regindex |= RtoB(int(p.To.Reg))
-	}
-	if p.To.Index != x86.REG_NONE {
-		info.Regindex |= RtoB(int(p.To.Index))
-	}
+	return info
 }
diff --git a/src/cmd/compile/internal/x86/reg.go b/src/cmd/compile/internal/x86/reg.go
deleted file mode 100644
index cc94d72..0000000
--- a/src/cmd/compile/internal/x86/reg.go
+++ /dev/null
@@ -1,114 +0,0 @@
-// Derived from Inferno utils/6c/reg.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package x86
-
-import "cmd/internal/obj/x86"
-import "cmd/compile/internal/gc"
-
-const (
-	NREGVAR = 16 /* 8 integer + 8 floating */
-)
-
-var regname = []string{
-	".ax",
-	".cx",
-	".dx",
-	".bx",
-	".sp",
-	".bp",
-	".si",
-	".di",
-	".x0",
-	".x1",
-	".x2",
-	".x3",
-	".x4",
-	".x5",
-	".x6",
-	".x7",
-}
-
-func regnames(n *int) []string {
-	*n = NREGVAR
-	return regname
-}
-
-func excludedregs() uint64 {
-	if gc.Ctxt.Flag_shared {
-		return RtoB(x86.REG_SP) | RtoB(x86.REG_CX)
-	} else {
-		return RtoB(x86.REG_SP)
-	}
-}
-
-func doregbits(r int) uint64 {
-	b := uint64(0)
-	if r >= x86.REG_AX && r <= x86.REG_DI {
-		b |= RtoB(r)
-	} else if r >= x86.REG_AL && r <= x86.REG_BL {
-		b |= RtoB(r - x86.REG_AL + x86.REG_AX)
-	} else if r >= x86.REG_AH && r <= x86.REG_BH {
-		b |= RtoB(r - x86.REG_AH + x86.REG_AX)
-	} else if r >= x86.REG_X0 && r <= x86.REG_X0+7 {
-		b |= FtoB(r)
-	}
-	return b
-}
-
-func RtoB(r int) uint64 {
-	if r < x86.REG_AX || r > x86.REG_DI {
-		return 0
-	}
-	return 1 << uint(r-x86.REG_AX)
-}
-
-func BtoR(b uint64) int {
-	b &= 0xff
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) + x86.REG_AX
-}
-
-func FtoB(f int) uint64 {
-	if f < x86.REG_X0 || f > x86.REG_X7 {
-		return 0
-	}
-	return 1 << uint(f-x86.REG_X0+8)
-}
-
-func BtoF(b uint64) int {
-	b &= 0xFF00
-	if b == 0 {
-		return 0
-	}
-	return gc.Bitno(b) - 8 + x86.REG_X0
-}
diff --git a/src/cmd/compile/internal/x86/ssa.go b/src/cmd/compile/internal/x86/ssa.go
new file mode 100644
index 0000000..21be634
--- /dev/null
+++ b/src/cmd/compile/internal/x86/ssa.go
@@ -0,0 +1,918 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x86
+
+import (
+	"fmt"
+	"math"
+
+	"cmd/compile/internal/gc"
+	"cmd/compile/internal/ssa"
+	"cmd/internal/obj"
+	"cmd/internal/obj/x86"
+)
+
+// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
+func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {
+	flive := b.FlagsLiveAtEnd
+	if b.Control != nil && b.Control.Type.IsFlags() {
+		flive = true
+	}
+	for i := len(b.Values) - 1; i >= 0; i-- {
+		v := b.Values[i]
+		if flive && v.Op == ssa.Op386MOVLconst {
+			// The "mark" is any non-nil Aux value.
+			v.Aux = v
+		}
+		if v.Type.IsFlags() {
+			flive = false
+		}
+		for _, a := range v.Args {
+			if a.Type.IsFlags() {
+				flive = true
+			}
+		}
+	}
+}
+
+// loadByType returns the load instruction of the given type.
+func loadByType(t ssa.Type) obj.As {
+	// Avoid partial register write
+	if !t.IsFloat() && t.Size() <= 2 {
+		if t.Size() == 1 {
+			return x86.AMOVBLZX
+		} else {
+			return x86.AMOVWLZX
+		}
+	}
+	// Otherwise, there's no difference between load and store opcodes.
+	return storeByType(t)
+}
+
+// storeByType returns the store instruction of the given type.
+func storeByType(t ssa.Type) obj.As {
+	width := t.Size()
+	if t.IsFloat() {
+		switch width {
+		case 4:
+			return x86.AMOVSS
+		case 8:
+			return x86.AMOVSD
+		}
+	} else {
+		switch width {
+		case 1:
+			return x86.AMOVB
+		case 2:
+			return x86.AMOVW
+		case 4:
+			return x86.AMOVL
+		}
+	}
+	panic("bad store type")
+}
+
+// moveByType returns the reg->reg move instruction of the given type.
+func moveByType(t ssa.Type) obj.As {
+	if t.IsFloat() {
+		switch t.Size() {
+		case 4:
+			return x86.AMOVSS
+		case 8:
+			return x86.AMOVSD
+		default:
+			panic(fmt.Sprintf("bad float register width %d:%s", t.Size(), t))
+		}
+	} else {
+		switch t.Size() {
+		case 1:
+			// Avoids partial register write
+			return x86.AMOVL
+		case 2:
+			return x86.AMOVL
+		case 4:
+			return x86.AMOVL
+		default:
+			panic(fmt.Sprintf("bad int register width %d:%s", t.Size(), t))
+		}
+	}
+}
+
+// opregreg emits instructions for
+//     dest := dest(To) op src(From)
+// and also returns the created obj.Prog so it
+// may be further adjusted (offset, scale, etc).
+func opregreg(op obj.As, dest, src int16) *obj.Prog {
+	p := gc.Prog(op)
+	p.From.Type = obj.TYPE_REG
+	p.To.Type = obj.TYPE_REG
+	p.To.Reg = dest
+	p.From.Reg = src
+	return p
+}
+
+func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
+	s.SetLineno(v.Line)
+
+	if gc.Thearch.Use387 {
+		if ssaGenValue387(s, v) {
+			return // v was handled by 387 generation.
+		}
+	}
+
+	switch v.Op {
+	case ssa.Op386ADDL:
+		r := v.Reg()
+		r1 := v.Args[0].Reg()
+		r2 := v.Args[1].Reg()
+		switch {
+		case r == r1:
+			p := gc.Prog(v.Op.Asm())
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = r2
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = r
+		case r == r2:
+			p := gc.Prog(v.Op.Asm())
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = r1
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = r
+		default:
+			p := gc.Prog(x86.ALEAL)
+			p.From.Type = obj.TYPE_MEM
+			p.From.Reg = r1
+			p.From.Scale = 1
+			p.From.Index = r2
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = r
+		}
+
+	// 2-address opcode arithmetic
+	case ssa.Op386SUBL,
+		ssa.Op386MULL,
+		ssa.Op386ANDL,
+		ssa.Op386ORL,
+		ssa.Op386XORL,
+		ssa.Op386SHLL,
+		ssa.Op386SHRL, ssa.Op386SHRW, ssa.Op386SHRB,
+		ssa.Op386SARL, ssa.Op386SARW, ssa.Op386SARB,
+		ssa.Op386ADDSS, ssa.Op386ADDSD, ssa.Op386SUBSS, ssa.Op386SUBSD,
+		ssa.Op386MULSS, ssa.Op386MULSD, ssa.Op386DIVSS, ssa.Op386DIVSD,
+		ssa.Op386PXOR,
+		ssa.Op386ADCL,
+		ssa.Op386SBBL:
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		opregreg(v.Op.Asm(), r, v.Args[1].Reg())
+
+	case ssa.Op386ADDLcarry, ssa.Op386SUBLcarry:
+		// output 0 is carry/borrow, output 1 is the low 32 bits.
+		r := v.Reg0()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output[0] not in same register %s", v.LongString())
+		}
+		opregreg(v.Op.Asm(), r, v.Args[1].Reg())
+
+	case ssa.Op386ADDLconstcarry, ssa.Op386SUBLconstcarry:
+		// output 0 is carry/borrow, output 1 is the low 32 bits.
+		r := v.Reg0()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output[0] not in same register %s", v.LongString())
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+
+	case ssa.Op386DIVL, ssa.Op386DIVW,
+		ssa.Op386DIVLU, ssa.Op386DIVWU,
+		ssa.Op386MODL, ssa.Op386MODW,
+		ssa.Op386MODLU, ssa.Op386MODWU:
+
+		// Arg[0] is already in AX as it's the only register we allow
+		// and AX is the only output
+		x := v.Args[1].Reg()
+
+		// CPU faults upon signed overflow, which occurs when most
+		// negative int is divided by -1.
+		var j *obj.Prog
+		if v.Op == ssa.Op386DIVL || v.Op == ssa.Op386DIVW ||
+			v.Op == ssa.Op386MODL || v.Op == ssa.Op386MODW {
+
+			var c *obj.Prog
+			switch v.Op {
+			case ssa.Op386DIVL, ssa.Op386MODL:
+				c = gc.Prog(x86.ACMPL)
+				j = gc.Prog(x86.AJEQ)
+				gc.Prog(x86.ACDQ) //TODO: fix
+
+			case ssa.Op386DIVW, ssa.Op386MODW:
+				c = gc.Prog(x86.ACMPW)
+				j = gc.Prog(x86.AJEQ)
+				gc.Prog(x86.ACWD)
+			}
+			c.From.Type = obj.TYPE_REG
+			c.From.Reg = x
+			c.To.Type = obj.TYPE_CONST
+			c.To.Offset = -1
+
+			j.To.Type = obj.TYPE_BRANCH
+		}
+
+		// for unsigned ints, we sign extend by setting DX = 0
+		// signed ints were sign extended above
+		if v.Op == ssa.Op386DIVLU || v.Op == ssa.Op386MODLU ||
+			v.Op == ssa.Op386DIVWU || v.Op == ssa.Op386MODWU {
+			c := gc.Prog(x86.AXORL)
+			c.From.Type = obj.TYPE_REG
+			c.From.Reg = x86.REG_DX
+			c.To.Type = obj.TYPE_REG
+			c.To.Reg = x86.REG_DX
+		}
+
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x
+
+		// signed division, rest of the check for -1 case
+		if j != nil {
+			j2 := gc.Prog(obj.AJMP)
+			j2.To.Type = obj.TYPE_BRANCH
+
+			var n *obj.Prog
+			if v.Op == ssa.Op386DIVL || v.Op == ssa.Op386DIVW {
+				// n * -1 = -n
+				n = gc.Prog(x86.ANEGL)
+				n.To.Type = obj.TYPE_REG
+				n.To.Reg = x86.REG_AX
+			} else {
+				// n % -1 == 0
+				n = gc.Prog(x86.AXORL)
+				n.From.Type = obj.TYPE_REG
+				n.From.Reg = x86.REG_DX
+				n.To.Type = obj.TYPE_REG
+				n.To.Reg = x86.REG_DX
+			}
+
+			j.To.Val = n
+			j2.To.Val = s.Pc()
+		}
+
+	case ssa.Op386HMULL, ssa.Op386HMULW, ssa.Op386HMULB,
+		ssa.Op386HMULLU, ssa.Op386HMULWU, ssa.Op386HMULBU:
+		// the frontend rewrites constant division by 8/16/32 bit integers into
+		// HMUL by a constant
+		// SSA rewrites generate the 64 bit versions
+
+		// Arg[0] is already in AX as it's the only register we allow
+		// and DX is the only output we care about (the high bits)
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+
+		// IMULB puts the high portion in AH instead of DL,
+		// so move it to DL for consistency
+		if v.Type.Size() == 1 {
+			m := gc.Prog(x86.AMOVB)
+			m.From.Type = obj.TYPE_REG
+			m.From.Reg = x86.REG_AH
+			m.To.Type = obj.TYPE_REG
+			m.To.Reg = x86.REG_DX
+		}
+
+	case ssa.Op386MULLQU:
+		// AX * args[1], high 32 bits in DX (result[0]), low 32 bits in AX (result[1]).
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+
+	case ssa.Op386ADDLconst:
+		r := v.Reg()
+		a := v.Args[0].Reg()
+		if r == a {
+			if v.AuxInt == 1 {
+				p := gc.Prog(x86.AINCL)
+				p.To.Type = obj.TYPE_REG
+				p.To.Reg = r
+				return
+			}
+			if v.AuxInt == -1 {
+				p := gc.Prog(x86.ADECL)
+				p.To.Type = obj.TYPE_REG
+				p.To.Reg = r
+				return
+			}
+			p := gc.Prog(v.Op.Asm())
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = v.AuxInt
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = r
+			return
+		}
+		p := gc.Prog(x86.ALEAL)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = a
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+
+	case ssa.Op386MULLconst:
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+		// TODO: Teach doasm to compile the three-address multiply imul $c, r1, r2
+		// then we don't need to use resultInArg0 for these ops.
+		//p.From3 = new(obj.Addr)
+		//p.From3.Type = obj.TYPE_REG
+		//p.From3.Reg = v.Args[0].Reg()
+
+	case ssa.Op386SUBLconst,
+		ssa.Op386ADCLconst,
+		ssa.Op386SBBLconst,
+		ssa.Op386ANDLconst,
+		ssa.Op386ORLconst,
+		ssa.Op386XORLconst,
+		ssa.Op386SHLLconst,
+		ssa.Op386SHRLconst, ssa.Op386SHRWconst, ssa.Op386SHRBconst,
+		ssa.Op386SARLconst, ssa.Op386SARWconst, ssa.Op386SARBconst,
+		ssa.Op386ROLLconst, ssa.Op386ROLWconst, ssa.Op386ROLBconst:
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.Op386SBBLcarrymask:
+		r := v.Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = r
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.Op386LEAL1, ssa.Op386LEAL2, ssa.Op386LEAL4, ssa.Op386LEAL8:
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
+		p := gc.Prog(x86.ALEAL)
+		switch v.Op {
+		case ssa.Op386LEAL1:
+			p.From.Scale = 1
+			if i == x86.REG_SP {
+				r, i = i, r
+			}
+		case ssa.Op386LEAL2:
+			p.From.Scale = 2
+		case ssa.Op386LEAL4:
+			p.From.Scale = 4
+		case ssa.Op386LEAL8:
+			p.From.Scale = 8
+		}
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = r
+		p.From.Index = i
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.Op386LEAL:
+		p := gc.Prog(x86.ALEAL)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.Op386CMPL, ssa.Op386CMPW, ssa.Op386CMPB,
+		ssa.Op386TESTL, ssa.Op386TESTW, ssa.Op386TESTB:
+		opregreg(v.Op.Asm(), v.Args[1].Reg(), v.Args[0].Reg())
+	case ssa.Op386UCOMISS, ssa.Op386UCOMISD:
+		// Go assembler has swapped operands for UCOMISx relative to CMP,
+		// must account for that right here.
+		opregreg(v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg())
+	case ssa.Op386CMPLconst, ssa.Op386CMPWconst, ssa.Op386CMPBconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_CONST
+		p.To.Offset = v.AuxInt
+	case ssa.Op386TESTLconst, ssa.Op386TESTWconst, ssa.Op386TESTBconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Args[0].Reg()
+	case ssa.Op386MOVLconst:
+		x := v.Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		p.From.Offset = v.AuxInt
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x
+		// If flags are live at this instruction, suppress the
+		// MOV $0,AX -> XOR AX,AX optimization.
+		if v.Aux != nil {
+			p.Mark |= x86.PRESERVEFLAGS
+		}
+	case ssa.Op386MOVSSconst, ssa.Op386MOVSDconst:
+		x := v.Reg()
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_FCONST
+		p.From.Val = math.Float64frombits(uint64(v.AuxInt))
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x
+	case ssa.Op386MOVSSconst1, ssa.Op386MOVSDconst1:
+		var literal string
+		if v.Op == ssa.Op386MOVSDconst1 {
+			literal = fmt.Sprintf("$f64.%016x", uint64(v.AuxInt))
+		} else {
+			literal = fmt.Sprintf("$f32.%08x", math.Float32bits(float32(math.Float64frombits(uint64(v.AuxInt)))))
+		}
+		p := gc.Prog(x86.ALEAL)
+		p.From.Type = obj.TYPE_MEM
+		p.From.Name = obj.NAME_EXTERN
+		p.From.Sym = obj.Linklookup(gc.Ctxt, literal, 0)
+		p.From.Sym.Set(obj.AttrLocal, true)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.Op386MOVSSconst2, ssa.Op386MOVSDconst2:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+	case ssa.Op386MOVSSload, ssa.Op386MOVSDload, ssa.Op386MOVLload, ssa.Op386MOVWload, ssa.Op386MOVBload, ssa.Op386MOVBLSXload, ssa.Op386MOVWLSXload:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.Op386MOVSDloadidx8:
+		p := gc.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.Op386MOVLloadidx4, ssa.Op386MOVSSloadidx4:
+		p := gc.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.Op386MOVWloadidx2:
+		p := gc.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.Op386MOVBloadidx1, ssa.Op386MOVWloadidx1, ssa.Op386MOVLloadidx1, ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1:
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
+		if i == x86.REG_SP {
+			r, i = i, r
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = r
+		p.From.Scale = 1
+		p.From.Index = i
+		gc.AddAux(&p.From, v)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+	case ssa.Op386MOVSSstore, ssa.Op386MOVSDstore, ssa.Op386MOVLstore, ssa.Op386MOVWstore, ssa.Op386MOVBstore:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[1].Reg()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+	case ssa.Op386MOVSDstoreidx8:
+		p := gc.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.Op386MOVSSstoreidx4, ssa.Op386MOVLstoreidx4:
+		p := gc.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.Op386MOVWstoreidx2:
+		p := gc.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.Op386MOVBstoreidx1, ssa.Op386MOVWstoreidx1, ssa.Op386MOVLstoreidx1, ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1:
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
+		if i == x86.REG_SP {
+			r, i = i, r
+		}
+		p := gc.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 = r
+		p.To.Scale = 1
+		p.To.Index = i
+		gc.AddAux(&p.To, v)
+	case ssa.Op386MOVLstoreconst, ssa.Op386MOVWstoreconst, ssa.Op386MOVBstoreconst:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		sc := v.AuxValAndOff()
+		p.From.Offset = sc.Val()
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux2(&p.To, v, sc.Off())
+	case ssa.Op386MOVLstoreconstidx1, ssa.Op386MOVLstoreconstidx4, ssa.Op386MOVWstoreconstidx1, ssa.Op386MOVWstoreconstidx2, ssa.Op386MOVBstoreconstidx1:
+		p := gc.Prog(v.Op.Asm())
+		p.From.Type = obj.TYPE_CONST
+		sc := v.AuxValAndOff()
+		p.From.Offset = sc.Val()
+		r := v.Args[0].Reg()
+		i := v.Args[1].Reg()
+		switch v.Op {
+		case ssa.Op386MOVBstoreconstidx1, ssa.Op386MOVWstoreconstidx1, ssa.Op386MOVLstoreconstidx1:
+			p.To.Scale = 1
+			if i == x86.REG_SP {
+				r, i = i, r
+			}
+		case ssa.Op386MOVWstoreconstidx2:
+			p.To.Scale = 2
+		case ssa.Op386MOVLstoreconstidx4:
+			p.To.Scale = 4
+		}
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = r
+		p.To.Index = i
+		gc.AddAux2(&p.To, v, sc.Off())
+	case ssa.Op386MOVWLSX, ssa.Op386MOVBLSX, ssa.Op386MOVWLZX, ssa.Op386MOVBLZX,
+		ssa.Op386CVTSL2SS, ssa.Op386CVTSL2SD,
+		ssa.Op386CVTTSS2SL, ssa.Op386CVTTSD2SL,
+		ssa.Op386CVTSS2SD, ssa.Op386CVTSD2SS:
+		opregreg(v.Op.Asm(), v.Reg(), v.Args[0].Reg())
+	case ssa.Op386DUFFZERO:
+		p := gc.Prog(obj.ADUFFZERO)
+		p.To.Type = obj.TYPE_ADDR
+		p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
+		p.To.Offset = v.AuxInt
+	case ssa.Op386DUFFCOPY:
+		p := gc.Prog(obj.ADUFFCOPY)
+		p.To.Type = obj.TYPE_ADDR
+		p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg))
+		p.To.Offset = v.AuxInt
+
+	case ssa.OpCopy, ssa.Op386MOVLconvert: // TODO: use MOVLreg for reg->reg copies instead of OpCopy?
+		if v.Type.IsMemory() {
+			return
+		}
+		x := v.Args[0].Reg()
+		y := v.Reg()
+		if x != y {
+			opregreg(moveByType(v.Type), y, x)
+		}
+	case ssa.OpLoadReg:
+		if v.Type.IsFlags() {
+			v.Fatalf("load flags not implemented: %v", v.LongString())
+			return
+		}
+		p := gc.Prog(loadByType(v.Type))
+		gc.AddrAuto(&p.From, v.Args[0])
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+	case ssa.OpStoreReg:
+		if v.Type.IsFlags() {
+			v.Fatalf("store flags not implemented: %v", v.LongString())
+			return
+		}
+		p := gc.Prog(storeByType(v.Type))
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = v.Args[0].Reg()
+		gc.AddrAuto(&p.To, v)
+	case ssa.OpPhi:
+		gc.CheckLoweredPhi(v)
+	case ssa.OpInitMem:
+		// memory arg needs no code
+	case ssa.OpArg:
+		// input args need no code
+	case ssa.Op386LoweredGetClosurePtr:
+		// Closure pointer is DX.
+		gc.CheckLoweredGetClosurePtr(v)
+	case ssa.Op386LoweredGetG:
+		r := v.Reg()
+		// See the comments in cmd/internal/obj/x86/obj6.go
+		// near CanUse1InsnTLS for a detailed explanation of these instructions.
+		if x86.CanUse1InsnTLS(gc.Ctxt) {
+			// MOVL (TLS), r
+			p := gc.Prog(x86.AMOVL)
+			p.From.Type = obj.TYPE_MEM
+			p.From.Reg = x86.REG_TLS
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = r
+		} else {
+			// MOVL TLS, r
+			// MOVL (r)(TLS*1), r
+			p := gc.Prog(x86.AMOVL)
+			p.From.Type = obj.TYPE_REG
+			p.From.Reg = x86.REG_TLS
+			p.To.Type = obj.TYPE_REG
+			p.To.Reg = r
+			q := gc.Prog(x86.AMOVL)
+			q.From.Type = obj.TYPE_MEM
+			q.From.Reg = r
+			q.From.Index = x86.REG_TLS
+			q.From.Scale = 1
+			q.To.Type = obj.TYPE_REG
+			q.To.Reg = r
+		}
+	case ssa.Op386CALLstatic:
+		if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
+			// Deferred calls will appear to be returning to
+			// the CALL deferreturn(SB) that we are about to emit.
+			// However, the stack trace code will show the line
+			// of the instruction byte before the return PC.
+			// To avoid that being an unrelated instruction,
+			// insert an actual hardware NOP that will have the right line number.
+			// This is different from obj.ANOP, which is a virtual no-op
+			// that doesn't make it into the instruction stream.
+			ginsnop()
+		}
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym))
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.Op386CALLclosure:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.Op386CALLdefer:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Deferproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.Op386CALLgo:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(gc.Newproc.Sym)
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.Op386CALLinter:
+		p := gc.Prog(obj.ACALL)
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Args[0].Reg()
+		if gc.Maxarg < v.AuxInt {
+			gc.Maxarg = v.AuxInt
+		}
+	case ssa.Op386NEGL,
+		ssa.Op386BSWAPL,
+		ssa.Op386NOTL:
+		r := v.Reg()
+		if r != v.Args[0].Reg() {
+			v.Fatalf("input[0] and output not in same register %s", v.LongString())
+		}
+		p := gc.Prog(v.Op.Asm())
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = r
+	case ssa.Op386BSFL, ssa.Op386BSFW,
+		ssa.Op386BSRL, ssa.Op386BSRW,
+		ssa.Op386SQRTSD:
+		p := gc.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.OpSP, ssa.OpSB, ssa.OpSelect0, ssa.OpSelect1:
+		// nothing to do
+	case ssa.Op386SETEQ, ssa.Op386SETNE,
+		ssa.Op386SETL, ssa.Op386SETLE,
+		ssa.Op386SETG, ssa.Op386SETGE,
+		ssa.Op386SETGF, ssa.Op386SETGEF,
+		ssa.Op386SETB, ssa.Op386SETBE,
+		ssa.Op386SETORD, ssa.Op386SETNAN,
+		ssa.Op386SETA, ssa.Op386SETAE:
+		p := gc.Prog(v.Op.Asm())
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+
+	case ssa.Op386SETNEF:
+		p := gc.Prog(v.Op.Asm())
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+		q := gc.Prog(x86.ASETPS)
+		q.To.Type = obj.TYPE_REG
+		q.To.Reg = x86.REG_AX
+		opregreg(x86.AORL, v.Reg(), x86.REG_AX)
+
+	case ssa.Op386SETEQF:
+		p := gc.Prog(v.Op.Asm())
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = v.Reg()
+		q := gc.Prog(x86.ASETPC)
+		q.To.Type = obj.TYPE_REG
+		q.To.Reg = x86.REG_AX
+		opregreg(x86.AANDL, v.Reg(), x86.REG_AX)
+
+	case ssa.Op386InvertFlags:
+		v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
+	case ssa.Op386FlagEQ, ssa.Op386FlagLT_ULT, ssa.Op386FlagLT_UGT, ssa.Op386FlagGT_ULT, ssa.Op386FlagGT_UGT:
+		v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString())
+	case ssa.Op386REPSTOSL:
+		gc.Prog(x86.AREP)
+		gc.Prog(x86.ASTOSL)
+	case ssa.Op386REPMOVSL:
+		gc.Prog(x86.AREP)
+		gc.Prog(x86.AMOVSL)
+	case ssa.OpVarDef:
+		gc.Gvardef(v.Aux.(*gc.Node))
+	case ssa.OpVarKill:
+		gc.Gvarkill(v.Aux.(*gc.Node))
+	case ssa.OpVarLive:
+		gc.Gvarlive(v.Aux.(*gc.Node))
+	case ssa.OpKeepAlive:
+		gc.KeepAlive(v)
+	case ssa.Op386LoweredNilCheck:
+		// Issue a load which will fault if the input is nil.
+		// TODO: We currently use the 2-byte instruction TESTB AX, (reg).
+		// Should we use the 3-byte TESTB $0, (reg) instead?  It is larger
+		// but it doesn't have false dependency on AX.
+		// Or maybe allocate an output register and use MOVL (reg),reg2 ?
+		// That trades clobbering flags for clobbering a register.
+		p := gc.Prog(x86.ATESTB)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_AX
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = v.Args[0].Reg()
+		gc.AddAux(&p.To, v)
+		if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers
+			gc.Warnl(v.Line, "generated nil check")
+		}
+	case ssa.Op386FCHS:
+		v.Fatalf("FCHS in non-387 mode")
+	default:
+		v.Fatalf("genValue not implemented: %s", v.LongString())
+	}
+}
+
+var blockJump = [...]struct {
+	asm, invasm obj.As
+}{
+	ssa.Block386EQ:  {x86.AJEQ, x86.AJNE},
+	ssa.Block386NE:  {x86.AJNE, x86.AJEQ},
+	ssa.Block386LT:  {x86.AJLT, x86.AJGE},
+	ssa.Block386GE:  {x86.AJGE, x86.AJLT},
+	ssa.Block386LE:  {x86.AJLE, x86.AJGT},
+	ssa.Block386GT:  {x86.AJGT, x86.AJLE},
+	ssa.Block386ULT: {x86.AJCS, x86.AJCC},
+	ssa.Block386UGE: {x86.AJCC, x86.AJCS},
+	ssa.Block386UGT: {x86.AJHI, x86.AJLS},
+	ssa.Block386ULE: {x86.AJLS, x86.AJHI},
+	ssa.Block386ORD: {x86.AJPC, x86.AJPS},
+	ssa.Block386NAN: {x86.AJPS, x86.AJPC},
+}
+
+var eqfJumps = [2][2]gc.FloatingEQNEJump{
+	{{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPS, Index: 1}}, // next == b.Succs[0]
+	{{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPC, Index: 0}}, // next == b.Succs[1]
+}
+var nefJumps = [2][2]gc.FloatingEQNEJump{
+	{{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPC, Index: 1}}, // next == b.Succs[0]
+	{{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPS, Index: 0}}, // next == b.Succs[1]
+}
+
+func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
+	s.SetLineno(b.Line)
+
+	if gc.Thearch.Use387 {
+		// Empty the 387's FP stack before the block ends.
+		flush387(s)
+	}
+
+	switch b.Kind {
+	case ssa.BlockPlain:
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+	case ssa.BlockDefer:
+		// defer returns in rax:
+		// 0 if we should continue executing
+		// 1 if we should jump to deferreturn call
+		p := gc.Prog(x86.ATESTL)
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = x86.REG_AX
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = x86.REG_AX
+		p = gc.Prog(x86.AJNE)
+		p.To.Type = obj.TYPE_BRANCH
+		s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		if b.Succs[0].Block() != next {
+			p := gc.Prog(obj.AJMP)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		}
+	case ssa.BlockExit:
+		gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
+	case ssa.BlockRet:
+		gc.Prog(obj.ARET)
+	case ssa.BlockRetJmp:
+		p := gc.Prog(obj.AJMP)
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym))
+
+	case ssa.Block386EQF:
+		gc.SSAGenFPJump(s, b, next, &eqfJumps)
+
+	case ssa.Block386NEF:
+		gc.SSAGenFPJump(s, b, next, &nefJumps)
+
+	case ssa.Block386EQ, ssa.Block386NE,
+		ssa.Block386LT, ssa.Block386GE,
+		ssa.Block386LE, ssa.Block386GT,
+		ssa.Block386ULT, ssa.Block386UGT,
+		ssa.Block386ULE, ssa.Block386UGE:
+		jmp := blockJump[b.Kind]
+		likely := b.Likely
+		var p *obj.Prog
+		switch next {
+		case b.Succs[0].Block():
+			p = gc.Prog(jmp.invasm)
+			likely *= -1
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
+		case b.Succs[1].Block():
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+		default:
+			p = gc.Prog(jmp.asm)
+			p.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
+			q := gc.Prog(obj.AJMP)
+			q.To.Type = obj.TYPE_BRANCH
+			s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
+		}
+
+		// liblink reorders the instruction stream as it sees fit.
+		// Pass along what we know so liblink can make use of it.
+		// TODO: Once we've fully switched to SSA,
+		// make liblink leave our output alone.
+		switch likely {
+		case ssa.BranchUnlikely:
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = 0
+		case ssa.BranchLikely:
+			p.From.Type = obj.TYPE_CONST
+			p.From.Offset = 1
+		}
+
+	default:
+		b.Fatalf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString())
+	}
+}
diff --git a/src/cmd/compile/main.go b/src/cmd/compile/main.go
index 8b8161e..c3c0b6a 100644
--- a/src/cmd/compile/main.go
+++ b/src/cmd/compile/main.go
@@ -8,6 +8,8 @@ import (
 	"cmd/compile/internal/amd64"
 	"cmd/compile/internal/arm"
 	"cmd/compile/internal/arm64"
+	"cmd/compile/internal/gc"
+	"cmd/compile/internal/mips"
 	"cmd/compile/internal/mips64"
 	"cmd/compile/internal/ppc64"
 	"cmd/compile/internal/s390x"
@@ -23,23 +25,28 @@ func main() {
 	log.SetFlags(0)
 	log.SetPrefix("compile: ")
 
-	switch obj.Getgoarch() {
+	switch obj.GOARCH {
 	default:
-		fmt.Fprintf(os.Stderr, "compile: unknown architecture %q\n", obj.Getgoarch())
+		fmt.Fprintf(os.Stderr, "compile: unknown architecture %q\n", obj.GOARCH)
 		os.Exit(2)
 	case "386":
-		x86.Main()
+		x86.Init()
 	case "amd64", "amd64p32":
-		amd64.Main()
+		amd64.Init()
 	case "arm":
-		arm.Main()
+		arm.Init()
 	case "arm64":
-		arm64.Main()
+		arm64.Init()
+	case "mips", "mipsle":
+		mips.Init()
 	case "mips64", "mips64le":
-		mips64.Main()
+		mips64.Init()
 	case "ppc64", "ppc64le":
-		ppc64.Main()
+		ppc64.Init()
 	case "s390x":
-		s390x.Main()
+		s390x.Init()
 	}
+
+	gc.Main()
+	gc.Exit(0)
 }
diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go
index a9ed66e..b7d9125 100644
--- a/src/cmd/cover/cover.go
+++ b/src/cmd/cover/cover.go
@@ -168,13 +168,13 @@ func (f *File) Visit(node ast.Node) ast.Visitor {
 			case *ast.CaseClause: // switch
 				for _, n := range n.List {
 					clause := n.(*ast.CaseClause)
-					clause.Body = f.addCounters(clause.Pos(), clause.End(), clause.Body, false)
+					clause.Body = f.addCounters(clause.Colon+1, clause.End(), clause.Body, false)
 				}
 				return f
 			case *ast.CommClause: // select
 				for _, n := range n.List {
 					clause := n.(*ast.CommClause)
-					clause.Body = f.addCounters(clause.Pos(), clause.End(), clause.Body, false)
+					clause.Body = f.addCounters(clause.Colon+1, clause.End(), clause.Body, false)
 				}
 				return f
 			}
@@ -240,6 +240,18 @@ func (f *File) Visit(node ast.Node) ast.Visitor {
 			ast.Walk(f, n.Assign)
 			return nil
 		}
+	case *ast.CommentGroup:
+		var list []*ast.Comment
+		// Drop all but the //go: comments, some of which are semantically important.
+		// We drop all others because they can appear in places that cause our counters
+		// to appear in syntactically incorrect places. //go: appears at the beginning of
+		// the line and is syntactically safe.
+		for _, c := range n.List {
+			if strings.HasPrefix(c.Text, "//go:") && f.fset.Position(c.Slash).Column == 1 {
+				list = append(list, c)
+			}
+		}
+		n.List = list
 	}
 	return f
 }
@@ -348,7 +360,8 @@ func annotate(name string) {
 	if err != nil {
 		log.Fatalf("cover: %s: %s", name, err)
 	}
-	parsedFile.Comments = trimComments(parsedFile, fset)
+	// Remove comments. Or else they interfere with new AST.
+	parsedFile.Comments = nil
 
 	file := &File{
 		fset:    fset,
@@ -374,26 +387,6 @@ func annotate(name string) {
 	file.addVariables(fd)
 }
 
-// trimComments drops all but the //go: comments, some of which are semantically important.
-// We drop all others because they can appear in places that cause our counters
-// to appear in syntactically incorrect places. //go: appears at the beginning of
-// the line and is syntactically safe.
-func trimComments(file *ast.File, fset *token.FileSet) []*ast.CommentGroup {
-	var comments []*ast.CommentGroup
-	for _, group := range file.Comments {
-		var list []*ast.Comment
-		for _, comment := range group.List {
-			if strings.HasPrefix(comment.Text, "//go:") && fset.Position(comment.Slash).Column == 1 {
-				list = append(list, comment)
-			}
-		}
-		if list != nil {
-			comments = append(comments, &ast.CommentGroup{List: list})
-		}
-	}
-	return comments
-}
-
 func (f *File) print(w io.Writer) {
 	printer.Fprint(w, f.fset, f.astFile)
 }
@@ -488,10 +481,35 @@ func (f *File) addCounters(pos, blockEnd token.Pos, list []ast.Stmt, extendToClo
 		var last int
 		end := blockEnd
 		for last = 0; last < len(list); last++ {
-			end = f.statementBoundary(list[last])
-			if f.endsBasicSourceBlock(list[last]) {
-				extendToClosingBrace = false // Block is broken up now.
+			stmt := list[last]
+			end = f.statementBoundary(stmt)
+			if f.endsBasicSourceBlock(stmt) {
+				// If it is a labeled statement, we need to place a counter between
+				// the label and its statement because it may be the target of a goto
+				// and thus start a basic block. That is, given
+				//	foo: stmt
+				// we need to create
+				//	foo: ; stmt
+				// and mark the label as a block-terminating statement.
+				// The result will then be
+				//	foo: COUNTER[n]++; stmt
+				// However, we can't do this if the labeled statement is already
+				// a control statement, such as a labeled for.
+				if label, isLabel := stmt.(*ast.LabeledStmt); isLabel && !f.isControl(label.Stmt) {
+					newLabel := *label
+					newLabel.Stmt = &ast.EmptyStmt{
+						Semicolon: label.Stmt.Pos(),
+						Implicit:  true,
+					}
+					end = label.Pos() // Previous block ends before the label.
+					list[last] = &newLabel
+					// Open a gap and drop in the old statement, now without a label.
+					list = append(list, nil)
+					copy(list[last+1:], list[last:])
+					list[last+1] = label.Stmt
+				}
 				last++
+				extendToClosingBrace = false // Block is broken up now.
 				break
 			}
 		}
@@ -610,7 +628,7 @@ func (f *File) endsBasicSourceBlock(s ast.Stmt) bool {
 	case *ast.IfStmt:
 		return true
 	case *ast.LabeledStmt:
-		return f.endsBasicSourceBlock(s.Stmt)
+		return true // A goto may branch here, starting a new basic block.
 	case *ast.RangeStmt:
 		return true
 	case *ast.SwitchStmt:
@@ -634,6 +652,16 @@ func (f *File) endsBasicSourceBlock(s ast.Stmt) bool {
 	return found
 }
 
+// isControl reports whether s is a control statement that, if labeled, cannot be
+// separated from its label.
+func (f *File) isControl(s ast.Stmt) bool {
+	switch s.(type) {
+	case *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt, *ast.TypeSwitchStmt:
+		return true
+	}
+	return false
+}
+
 // funcLitFinder implements the ast.Visitor pattern to find the location of any
 // function literal in a subtree.
 type funcLitFinder token.Pos
diff --git a/src/cmd/cover/cover_test.go b/src/cmd/cover/cover_test.go
index 68e9e9f..50a7ce8 100644
--- a/src/cmd/cover/cover_test.go
+++ b/src/cmd/cover/cover_test.go
@@ -12,6 +12,7 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
+	"regexp"
 	"testing"
 )
 
@@ -62,14 +63,14 @@ func TestCover(t *testing.T) {
 	}
 
 	// go build -o testcover
-	cmd := exec.Command("go", "build", "-o", testcover)
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", testcover)
 	run(cmd, t)
 
 	// defer removal of testcover
 	defer os.Remove(testcover)
 
-	// ./testcover -mode=count -var=coverTest -o ./testdata/test_cover.go testdata/test_line.go
-	cmd = exec.Command(testcover, "-mode=count", "-var=coverTest", "-o", coverOutput, coverInput)
+	// ./testcover -mode=count -var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest -o ./testdata/test_cover.go testdata/test_line.go
+	cmd = exec.Command(testcover, "-mode=count", "-var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest", "-o", coverOutput, coverInput)
 	run(cmd, t)
 
 	// defer removal of ./testdata/test_cover.go
@@ -78,8 +79,22 @@ func TestCover(t *testing.T) {
 	}
 
 	// go run ./testdata/main.go ./testdata/test.go
-	cmd = exec.Command("go", "run", testMain, coverOutput)
+	cmd = exec.Command(testenv.GoToolPath(t), "run", testMain, coverOutput)
 	run(cmd, t)
+
+	file, err = ioutil.ReadFile(coverOutput)
+	if err != nil {
+		t.Fatal(err)
+	}
+	// compiler directive must appear right next to function declaration.
+	if got, err := regexp.MatchString(".*\n//go:nosplit\nfunc someFunction().*", string(file)); err != nil || !got {
+		t.Errorf("misplaced compiler directive: got=(%v, %v); want=(true; nil)", got, err)
+	}
+	// No other comments should be present in generated code.
+	c := ".*// This comment shouldn't appear in generated go code.*"
+	if got, err := regexp.MatchString(c, string(file)); err != nil || got {
+		t.Errorf("non compiler directive comment %q found. got=(%v, %v); want=(false; nil)", c, got, err)
+	}
 }
 
 func run(c *exec.Cmd, t *testing.T) {
diff --git a/src/cmd/cover/html.go b/src/cmd/cover/html.go
index d0ac447..04dc76f 100644
--- a/src/cmd/cover/html.go
+++ b/src/cmd/cover/html.go
@@ -7,15 +7,14 @@ package main
 import (
 	"bufio"
 	"bytes"
+	"cmd/internal/browser"
 	"fmt"
 	"html/template"
 	"io"
 	"io/ioutil"
 	"math"
 	"os"
-	"os/exec"
 	"path/filepath"
-	"runtime"
 )
 
 // htmlOutput reads the profile data from profile and generates an HTML
@@ -65,16 +64,19 @@ func htmlOutput(profile, outfile string) error {
 	} else {
 		out, err = os.Create(outfile)
 	}
+	if err != nil {
+		return err
+	}
 	err = htmlTemplate.Execute(out, d)
-	if err == nil {
-		err = out.Close()
+	if err2 := out.Close(); err == nil {
+		err = err2
 	}
 	if err != nil {
 		return err
 	}
 
 	if outfile == "" {
-		if !startBrowser("file://" + out.Name()) {
+		if !browser.Open("file://" + out.Name()) {
 			fmt.Fprintf(os.Stderr, "HTML output written to %s\n", out.Name())
 		}
 	}
@@ -133,23 +135,6 @@ func htmlGen(w io.Writer, src []byte, boundaries []Boundary) error {
 	return dst.Flush()
 }
 
-// startBrowser tries to open the URL in a browser
-// and reports whether it succeeds.
-func startBrowser(url string) bool {
-	// try to start the browser
-	var args []string
-	switch runtime.GOOS {
-	case "darwin":
-		args = []string{"open"}
-	case "windows":
-		args = []string{"cmd", "/c", "start"}
-	default:
-		args = []string{"xdg-open"}
-	}
-	cmd := exec.Command(args[0], append(args[1:], url)...)
-	return cmd.Start() == nil
-}
-
 // rgb returns an rgb value for the specified coverage value
 // between 0 (no coverage) and 10 (max coverage).
 func rgb(n int) string {
diff --git a/src/cmd/cover/profile.go b/src/cmd/cover/profile.go
index a03b5d5..5628b91 100644
--- a/src/cmd/cover/profile.go
+++ b/src/cmd/cover/profile.go
@@ -93,6 +93,29 @@ func ParseProfiles(fileName string) ([]*Profile, error) {
 	}
 	for _, p := range files {
 		sort.Sort(blocksByStart(p.Blocks))
+		// Merge samples from the same location.
+		j := 1
+		for i := 1; i < len(p.Blocks); i++ {
+			b := p.Blocks[i]
+			last := p.Blocks[j-1]
+			if b.StartLine == last.StartLine &&
+				b.StartCol == last.StartCol &&
+				b.EndLine == last.EndLine &&
+				b.EndCol == last.EndCol {
+				if b.NumStmt != last.NumStmt {
+					return nil, fmt.Errorf("inconsistent NumStmt: changed from %d to %d", last.NumStmt, b.NumStmt)
+				}
+				if mode == "set" {
+					p.Blocks[j-1].Count |= b.Count
+				} else {
+					p.Blocks[j-1].Count += b.Count
+				}
+				continue
+			}
+			p.Blocks[j] = b
+			j++
+		}
+		p.Blocks = p.Blocks[:j]
 	}
 	// Generate a sorted slice.
 	profiles := make([]*Profile, 0, len(files))
diff --git a/src/cmd/cover/testdata/main.go b/src/cmd/cover/testdata/main.go
index 6ed39c4..be74b4a 100644
--- a/src/cmd/cover/testdata/main.go
+++ b/src/cmd/cover/testdata/main.go
@@ -3,7 +3,8 @@
 // license that can be found in the LICENSE file.
 
 // Test runner for coverage test. This file is not coverage-annotated; test.go is.
-// It knows the coverage counter is called "coverTest".
+// It knows the coverage counter is called
+// "thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest".
 
 package main
 
@@ -24,6 +25,9 @@ type block struct {
 
 var counters = make(map[block]bool)
 
+// shorthand for the long counter variable.
+var coverTest = &thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest
+
 // check records the location and expected value for a counter.
 func check(line, count uint32) {
 	b := block{
diff --git a/src/cmd/cover/testdata/test.go b/src/cmd/cover/testdata/test.go
index c4c0e15..61b40ea 100644
--- a/src/cmd/cover/testdata/test.go
+++ b/src/cmd/cover/testdata/test.go
@@ -25,6 +25,7 @@ func testAll() {
 	testPanic()
 	testEmptySwitches()
 	testFunctionLiteral()
+	testGoto()
 }
 
 // The indexes of the counters in testPanic are known to main.go
@@ -245,4 +246,46 @@ func testFunctionLiteral() {
 		check(LINE, 2)
 	}) {
 	}
+
+	x := 2
+	switch x {
+	case func() int { check(LINE, 1); return 1 }():
+		check(LINE, 0)
+		panic("2=1")
+	case func() int { check(LINE, 1); return 2 }():
+		check(LINE, 1)
+	case func() int { check(LINE, 0); return 3 }():
+		check(LINE, 0)
+		panic("2=3")
+	}
+}
+
+func testGoto() {
+	for i := 0; i < 2; i++ {
+		if i == 0 {
+			goto Label
+		}
+		check(LINE, 1)
+	Label:
+		check(LINE, 2)
+	}
+	// Now test that we don't inject empty statements
+	// between a label and a loop.
+loop:
+	for {
+		check(LINE, 1)
+		break loop
+	}
+}
+
+// This comment shouldn't appear in generated go code.
+func haha() {
+	// Needed for cover to add counter increment here.
+	_ = 42
+}
+
+// Some someFunction.
+//
+//go:nosplit
+func someFunction() {
 }
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 9eb9caf..6fb7884 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -21,30 +21,31 @@ import (
 
 // The usual variables.
 var (
-	goarch           string
-	gobin            string
-	gohostarch       string
-	gohostos         string
-	goos             string
-	goarm            string
-	go386            string
-	goroot           string
-	goroot_final     string
-	goextlinkenabled string
-	gogcflags        string // For running built compiler
-	workdir          string
-	tooldir          string
-	oldgoos          string
-	oldgoarch        string
-	slash            string
-	exe              string
-	defaultcc        string
-	defaultcflags    string
-	defaultldflags   string
-	defaultcxxtarget string
-	defaultcctarget  string
-	rebuildall       bool
-	defaultclang     bool
+	goarch                 string
+	gobin                  string
+	gohostarch             string
+	gohostos               string
+	goos                   string
+	goarm                  string
+	go386                  string
+	goroot                 string
+	goroot_final           string
+	goextlinkenabled       string
+	gogcflags              string // For running built compiler
+	workdir                string
+	tooldir                string
+	oldgoos                string
+	oldgoarch              string
+	slash                  string
+	exe                    string
+	defaultcc              string
+	defaultcflags          string
+	defaultldflags         string
+	defaultcxxtarget       string
+	defaultcctarget        string
+	defaultpkgconfigtarget string
+	rebuildall             bool
+	defaultclang           bool
 
 	vflag int // verbosity
 )
@@ -56,6 +57,8 @@ var okgoarch = []string{
 	"amd64p32",
 	"arm",
 	"arm64",
+	"mips",
+	"mipsle",
 	"mips64",
 	"mips64le",
 	"ppc64",
@@ -208,6 +211,12 @@ func xinit() {
 	}
 	defaultcxxtarget = b
 
+	b = os.Getenv("PKG_CONFIG")
+	if b == "" {
+		b = "pkg-config"
+	}
+	defaultpkgconfigtarget = b
+
 	// For tools being invoked but also for os.ExpandEnv.
 	os.Setenv("GO386", go386)
 	os.Setenv("GOARCH", goarch)
@@ -1098,6 +1107,8 @@ var cgoEnabled = map[string]bool{
 	"linux/arm64":     true,
 	"linux/ppc64":     false,
 	"linux/ppc64le":   true,
+	"linux/mips":      false,
+	"linux/mipsle":    false,
 	"linux/mips64":    true,
 	"linux/mips64le":  true,
 	"linux/s390x":     true,
diff --git a/src/cmd/dist/buildgo.go b/src/cmd/dist/buildgo.go
index 3fab235..27976fb 100644
--- a/src/cmd/dist/buildgo.go
+++ b/src/cmd/dist/buildgo.go
@@ -7,6 +7,7 @@ package main
 import (
 	"bytes"
 	"fmt"
+	"os"
 	"sort"
 )
 
@@ -19,6 +20,7 @@ import (
 //	package main
 //	const defaultCC = <defaultcc>
 //	const defaultCXX = <defaultcxx>
+//	const defaultPkgConfig = <defaultpkgconfig>
 //
 // It is invoked to write cmd/go/zdefaultcc.go
 // but we also write cmd/cgo/zdefaultcc.go
@@ -29,8 +31,9 @@ func mkzdefaultcc(dir, file string) {
 			"package main\n"+
 			"\n"+
 			"const defaultCC = `%s`\n"+
-			"const defaultCXX = `%s`\n",
-		defaultcctarget, defaultcxxtarget)
+			"const defaultCXX = `%s`\n"+
+			"const defaultPkgConfig = `%s`\n",
+		defaultcctarget, defaultcxxtarget, defaultpkgconfigtarget)
 
 	writefile(out, file, writeSkipSame)
 
@@ -83,7 +86,8 @@ func mkzcgo(dir, file string) {
 			"\n"+
 			"package build\n"+
 			"\n"+
-			"var cgoEnabled = map[string]bool{\n")
+			"const defaultCGO_ENABLED = %q\n\n"+
+			"var cgoEnabled = map[string]bool{\n", os.Getenv("CGO_ENABLED"))
 	for _, plat := range list {
 		fmt.Fprintf(&buf, "\t%q: true,\n", plat)
 	}
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index a535316..b0b9b25 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -18,46 +18,62 @@ import (
 
 // bootstrapDirs is a list of directories holding code that must be
 // compiled with a Go 1.4 toolchain to produce the bootstrapTargets.
-// All directories in this list are relative to and must be below $GOROOT/src/cmd.
-// The list is assumed to have two kinds of entries: names without slashes,
-// which are commands, and entries beginning with internal/, which are
-// packages supporting the commands.
+// All directories in this list are relative to and must be below $GOROOT/src.
+//
+// The list has have two kinds of entries: names beginning with cmd/ with
+// no other slashes, which are commands, and other paths, which are packages
+// supporting the commands. Packages in the standard library can be listed
+// if a newer copy needs to be substituted for the Go 1.4 copy when used
+// by the command packages.
+// These will be imported during bootstrap as bootstrap/name, like bootstrap/math/big.
 var bootstrapDirs = []string{
-	"asm",
-	"asm/internal/arch",
-	"asm/internal/asm",
-	"asm/internal/flags",
-	"asm/internal/lex",
-	"compile",
-	"compile/internal/amd64",
-	"compile/internal/arm",
-	"compile/internal/arm64",
-	"compile/internal/big",
-	"compile/internal/gc",
-	"compile/internal/mips64",
-	"compile/internal/ppc64",
-	"compile/internal/ssa",
-	"compile/internal/x86",
-	"compile/internal/s390x",
-	"internal/bio",
-	"internal/gcprog",
-	"internal/obj",
-	"internal/obj/arm",
-	"internal/obj/arm64",
-	"internal/obj/mips",
-	"internal/obj/ppc64",
-	"internal/obj/s390x",
-	"internal/obj/x86",
-	"internal/sys",
-	"link",
-	"link/internal/amd64",
-	"link/internal/arm",
-	"link/internal/arm64",
-	"link/internal/ld",
-	"link/internal/mips64",
-	"link/internal/ppc64",
-	"link/internal/s390x",
-	"link/internal/x86",
+	"cmd/asm",
+	"cmd/asm/internal/arch",
+	"cmd/asm/internal/asm",
+	"cmd/asm/internal/flags",
+	"cmd/asm/internal/lex",
+	"cmd/compile",
+	"cmd/compile/internal/amd64",
+	"cmd/compile/internal/arm",
+	"cmd/compile/internal/arm64",
+	"cmd/compile/internal/gc",
+	"cmd/compile/internal/mips",
+	"cmd/compile/internal/mips64",
+	"cmd/compile/internal/ppc64",
+	"cmd/compile/internal/s390x",
+	"cmd/compile/internal/ssa",
+	"cmd/compile/internal/syntax",
+	"cmd/compile/internal/x86",
+	"cmd/internal/bio",
+	"cmd/internal/gcprog",
+	"cmd/internal/dwarf",
+	"cmd/internal/obj",
+	"cmd/internal/obj/arm",
+	"cmd/internal/obj/arm64",
+	"cmd/internal/obj/mips",
+	"cmd/internal/obj/ppc64",
+	"cmd/internal/obj/s390x",
+	"cmd/internal/obj/x86",
+	"cmd/internal/sys",
+	"cmd/link",
+	"cmd/link/internal/amd64",
+	"cmd/link/internal/arm",
+	"cmd/link/internal/arm64",
+	"cmd/link/internal/ld",
+	"cmd/link/internal/mips",
+	"cmd/link/internal/mips64",
+	"cmd/link/internal/ppc64",
+	"cmd/link/internal/s390x",
+	"cmd/link/internal/x86",
+	"debug/pe",
+	"math/big",
+}
+
+// File suffixes that use build tags introduced since Go 1.4.
+// These must not be copied into the bootstrap build directory.
+var ignoreSuffixes = []string{
+	"_arm64.s",
+	"_arm64.go",
 }
 
 func bootstrapBuildTools() {
@@ -81,10 +97,16 @@ func bootstrapBuildTools() {
 
 	// Copy source code into $GOROOT/pkg/bootstrap and rewrite import paths.
 	for _, dir := range bootstrapDirs {
-		src := pathf("%s/src/cmd/%s", goroot, dir)
+		src := pathf("%s/src/%s", goroot, dir)
 		dst := pathf("%s/%s", base, dir)
 		xmkdirall(dst)
+	Dir:
 		for _, name := range xreaddirfiles(src) {
+			for _, suf := range ignoreSuffixes {
+				if strings.HasSuffix(name, suf) {
+					continue Dir
+				}
+			}
 			srcFile := pathf("%s/%s", src, name)
 			text := readfile(srcFile)
 			text = bootstrapFixImports(text, srcFile)
@@ -119,10 +141,16 @@ func bootstrapBuildTools() {
 	// Run Go 1.4 to build binaries. Use -gcflags=-l to disable inlining to
 	// workaround bugs in Go 1.4's compiler. See discussion thread:
 	// https://groups.google.com/d/msg/golang-dev/Ss7mCKsvk8w/Gsq7VYI0AwAJ
-	run(workspace, ShowOutput|CheckExit, pathf("%s/bin/go", goroot_bootstrap), "install", "-gcflags=-l", "-v", "bootstrap/...")
+	// Use the math_big_pure_go build tag to disable the assembly in math/big
+	// which may contain unsupported instructions.
+	run(workspace, ShowOutput|CheckExit, pathf("%s/bin/go", goroot_bootstrap), "install", "-gcflags=-l", "-tags=math_big_pure_go", "-v", "bootstrap/cmd/...")
 
 	// Copy binaries into tool binary directory.
 	for _, name := range bootstrapDirs {
+		if !strings.HasPrefix(name, "cmd/") {
+			continue
+		}
+		name = name[len("cmd/"):]
 		if !strings.Contains(name, "/") {
 			copyfile(pathf("%s/%s%s", tooldir, name, exe), pathf("%s/bin/%s%s", workspace, name, exe), writeExec)
 		}
@@ -145,7 +173,14 @@ func bootstrapFixImports(text, srcFile string) string {
 		}
 		if strings.HasPrefix(line, `import "`) || strings.HasPrefix(line, `import . "`) ||
 			inBlock && (strings.HasPrefix(line, "\t\"") || strings.HasPrefix(line, "\t. \"")) {
-			lines[i] = strings.Replace(line, `"cmd/`, `"bootstrap/`, -1)
+			line = strings.Replace(line, `"cmd/`, `"bootstrap/cmd/`, -1)
+			for _, dir := range bootstrapDirs {
+				if strings.HasPrefix(dir, "cmd/") {
+					continue
+				}
+				line = strings.Replace(line, `"`+dir+`"`, `"bootstrap/`+dir+`"`, -1)
+			}
+			lines[i] = line
 		}
 	}
 
diff --git a/src/cmd/dist/deps.go b/src/cmd/dist/deps.go
index e8dd6cf..817484f 100644
--- a/src/cmd/dist/deps.go
+++ b/src/cmd/dist/deps.go
@@ -7,7 +7,7 @@ var builddeps = map[string][]string{
 	"bytes":                             {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "unicode", "unicode/utf8"},
 	"compress/flate":                    {"bufio", "bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
 	"compress/zlib":                     {"bufio", "bytes", "compress/flate", "errors", "fmt", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
-	"container/heap":                    {"runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort"},
+	"container/heap":                    {"errors", "internal/race", "math", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "unicode/utf8"},
 	"context":                           {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
 	"crypto":                            {"errors", "hash", "internal/race", "io", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"},
 	"crypto/sha1":                       {"crypto", "errors", "hash", "internal/race", "io", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"},
@@ -35,7 +35,7 @@ var builddeps = map[string][]string{
 	"internal/syscall/windows/registry": {"errors", "internal/race", "internal/syscall/windows/sysdll", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "unicode/utf16"},
 	"internal/syscall/windows/sysdll":   {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
 	"io":                      {"errors", "internal/race", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"},
-	"io/ioutil":               {"bytes", "errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
+	"io/ioutil":               {"bytes", "errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
 	"log":                     {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
 	"math":                    {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
 	"net/url":                 {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
@@ -43,14 +43,14 @@ var builddeps = map[string][]string{
 	"os/exec":                 {"bytes", "context", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
 	"os/signal":               {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "os", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
 	"path":                    {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"},
-	"path/filepath":           {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "os", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
+	"path/filepath":           {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
 	"reflect":                 {"errors", "internal/race", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"},
-	"regexp":                  {"bytes", "errors", "internal/race", "io", "math", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"},
-	"regexp/syntax":           {"bytes", "errors", "internal/race", "io", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"},
+	"regexp":                  {"bytes", "errors", "internal/race", "io", "math", "reflect", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"},
+	"regexp/syntax":           {"bytes", "errors", "internal/race", "io", "math", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"},
 	"runtime":                 {"runtime/internal/atomic", "runtime/internal/sys"},
 	"runtime/internal/atomic": {"runtime/internal/sys"},
 	"runtime/internal/sys":    {},
-	"sort":                    {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
+	"sort":                    {"errors", "internal/race", "math", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"},
 	"strconv":                 {"errors", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "unicode/utf8"},
 	"strings":                 {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "unicode", "unicode/utf8"},
 	"sync":                    {"internal/race", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync/atomic"},
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index e56d108..508863f 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -9,6 +9,7 @@ import (
 	"errors"
 	"flag"
 	"fmt"
+	"io/ioutil"
 	"log"
 	"os"
 	"os/exec"
@@ -328,6 +329,21 @@ func (t *tester) registerRaceBenchTest(pkg string) {
 }
 
 func (t *tester) registerTests() {
+	if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-vetall") {
+		// Run vet over std and cmd and call it quits.
+		t.tests = append(t.tests, distTest{
+			name:    "vet/all",
+			heading: "go vet std cmd",
+			fn: func(dt *distTest) error {
+				// This runs vet/all for the current platform.
+				// TODO: on a fast builder or builders, run over all platforms.
+				t.addCmd(dt, "src/cmd/vet/all", "go", "run", "main.go", "-all")
+				return nil
+			},
+		})
+		return
+	}
+
 	// Fast path to avoid the ~1 second of `go list std cmd` when
 	// the caller lists specific tests to run. (as the continuous
 	// build coordinator does).
@@ -351,7 +367,7 @@ func (t *tester) registerTests() {
 		if !t.race {
 			cmd.Args = append(cmd.Args, "cmd")
 		}
-		all, err := cmd.CombinedOutput()
+		all, err := cmd.Output()
 		if err != nil {
 			log.Fatalf("Error running go list std cmd: %v, %s", err, all)
 		}
@@ -361,7 +377,9 @@ func (t *tester) registerTests() {
 		}
 		if t.race {
 			for _, pkg := range pkgs {
-				t.registerRaceBenchTest(pkg)
+				if t.packageHasBenchmarks(pkg) {
+					t.registerRaceBenchTest(pkg)
+				}
 			}
 		}
 	}
@@ -391,9 +409,7 @@ func (t *tester) registerTests() {
 	// release on a system that does not have a C compiler
 	// installed and still build Go programs (that don't use cgo).
 	for _, pkg := range cgoPackages {
-
-		// Internal linking is not currently supported on Dragonfly.
-		if t.goos == "dragonfly" {
+		if !t.internalLink() {
 			break
 		}
 
@@ -402,11 +418,6 @@ func (t *tester) registerTests() {
 			break
 		}
 
-		// Darwin/Android ARM64 fails with internal linking.
-		if (t.goos == "darwin" || t.goos == "android") && t.goarch == "arm64" {
-			break
-		}
-
 		pkg := pkg
 		var run string
 		if pkg == "net" {
@@ -422,6 +433,18 @@ func (t *tester) registerTests() {
 		})
 	}
 
+	// Test internal linking of PIE binaries where it is supported.
+	if t.goos == "linux" && t.goarch == "amd64" {
+		t.tests = append(t.tests, distTest{
+			name:    "pie_internal",
+			heading: "internal linking of -buildmode=pie",
+			fn: func(dt *distTest) error {
+				t.addCmd(dt, "src", "go", "test", "reflect", "-short", "-buildmode=pie", "-ldflags=-linkmode=internal", t.timeout(60), t.tags(), t.runFlag(""))
+				return nil
+			},
+		})
+	}
+
 	// sync tests
 	t.tests = append(t.tests, distTest{
 		name:    "sync_cpu",
@@ -502,7 +525,7 @@ func (t *tester) registerTests() {
 			})
 		}
 		if t.supportedBuildmode("c-archive") {
-			t.registerHostTest("testcarchive", "misc/cgo/testcarchive", "carchive_test.go")
+			t.registerHostTest("testcarchive", "../misc/cgo/testcarchive", "misc/cgo/testcarchive", "carchive_test.go")
 		}
 		if t.supportedBuildmode("c-shared") {
 			t.registerTest("testcshared", "../misc/cgo/testcshared", "./test.bash")
@@ -510,6 +533,9 @@ func (t *tester) registerTests() {
 		if t.supportedBuildmode("shared") {
 			t.registerTest("testshared", "../misc/cgo/testshared", "go", "test")
 		}
+		if t.supportedBuildmode("plugin") {
+			t.registerTest("testplugin", "../misc/cgo/testplugin", "./test.bash")
+		}
 		if t.gohostos == "linux" && t.goarch == "amd64" {
 			t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go")
 		}
@@ -667,6 +693,31 @@ func (t *tester) extLink() bool {
 	return false
 }
 
+func (t *tester) internalLink() bool {
+	if t.gohostos == "dragonfly" {
+		// linkmode=internal fails on dragonfly since errno is a TLS relocation.
+		return false
+	}
+	if t.gohostarch == "ppc64le" {
+		// linkmode=internal fails on ppc64le because cmd/link doesn't
+		// handle the TOC correctly (issue 15409).
+		return false
+	}
+	if t.goos == "android" {
+		return false
+	}
+	if t.goos == "darwin" && (t.goarch == "arm" || t.goarch == "arm64") {
+		return false
+	}
+	// Internally linking cgo is incomplete on some architectures.
+	// https://golang.org/issue/10373
+	// https://golang.org/issue/14449
+	if t.goarch == "arm64" || t.goarch == "mips64" || t.goarch == "mips64le" {
+		return false
+	}
+	return true
+}
+
 func (t *tester) supportedBuildmode(mode string) bool {
 	pair := t.goos + "-" + t.goarch
 	switch mode {
@@ -694,32 +745,49 @@ func (t *tester) supportedBuildmode(mode string) bool {
 			return true
 		}
 		return false
+	case "plugin":
+		if os.Getenv("GO_BUILDER_NAME") == "linux-amd64-noopt" {
+			// Skip the plugin tests on noopt. They're
+			// causing build failures potentially
+			// obscuring other issues. This is hopefully a
+			// temporary workaround. See golang.org/issue/17937.
+			return false
+		}
+
+		// linux-arm64 is missing because it causes the external linker
+		// to crash, see https://golang.org/issue/17138
+		switch pair {
+		case "linux-386", "linux-amd64", "linux-arm",
+			"darwin-amd64":
+			return true
+		}
+		return false
 	default:
 		log.Fatalf("internal error: unknown buildmode %s", mode)
 		return false
 	}
 }
 
-func (t *tester) registerHostTest(name, dirBanner, pkg string) {
+func (t *tester) registerHostTest(name, heading, dir, pkg string) {
 	t.tests = append(t.tests, distTest{
 		name:    name,
-		heading: dirBanner,
+		heading: heading,
 		fn: func(dt *distTest) error {
 			t.runPending(dt)
-			return t.runHostTest(dirBanner, pkg)
+			return t.runHostTest(dir, pkg)
 		},
 	})
 }
 
-func (t *tester) runHostTest(dirBanner, pkg string) error {
+func (t *tester) runHostTest(dir, pkg string) error {
 	env := mergeEnvLists([]string{"GOARCH=" + t.gohostarch, "GOOS=" + t.gohostos}, os.Environ())
-	defer os.Remove(filepath.Join(t.goroot, dirBanner, "test.test"))
-	cmd := t.dirCmd(dirBanner, "go", "test", t.tags(), "-c", "-o", "test.test", pkg)
+	defer os.Remove(filepath.Join(t.goroot, dir, "test.test"))
+	cmd := t.dirCmd(dir, "go", "test", t.tags(), "-c", "-o", "test.test", pkg)
 	cmd.Env = env
 	if err := cmd.Run(); err != nil {
 		return err
 	}
-	return t.dirCmd(dirBanner, "./test.test").Run()
+	return t.dirCmd(dir, "./test.test").Run()
 }
 
 func (t *tester) cgoTest(dt *distTest) error {
@@ -728,10 +796,7 @@ func (t *tester) cgoTest(dt *distTest) error {
 	cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", t.tags(), "-ldflags", "-linkmode=auto", t.runFlag(""))
 	cmd.Env = env
 
-	if t.gohostos != "dragonfly" && t.gohostarch != "ppc64le" && t.goos != "android" && (t.goos != "darwin" || t.goarch != "arm") {
-		// linkmode=internal fails on dragonfly since errno is a TLS relocation.
-		// linkmode=internal fails on ppc64le because cmd/link doesn't
-		// handle the TOC correctly (issue 15409).
+	if t.internalLink() {
 		cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", "-ldflags", "-linkmode=internal", t.runFlag(""))
 		cmd.Env = env
 	}
@@ -752,7 +817,7 @@ func (t *tester) cgoTest(dt *distTest) error {
 	case "android-arm",
 		"dragonfly-386", "dragonfly-amd64",
 		"freebsd-386", "freebsd-amd64", "freebsd-arm",
-		"linux-386", "linux-amd64", "linux-arm", "linux-s390x",
+		"linux-386", "linux-amd64", "linux-arm", "linux-ppc64le", "linux-s390x",
 		"netbsd-386", "netbsd-amd64":
 
 		cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", "-ldflags", "-linkmode=external")
@@ -998,7 +1063,7 @@ func (t *tester) runFlag(rx string) string {
 func (t *tester) raceTest(dt *distTest) error {
 	t.addCmd(dt, "src", "go", "test", "-race", "-i", "runtime/race", "flag", "os/exec")
 	t.addCmd(dt, "src", "go", "test", "-race", t.runFlag("Output"), "runtime/race")
-	t.addCmd(dt, "src", "go", "test", "-race", "-short", t.runFlag("TestParse|TestEcho"), "flag", "os/exec")
+	t.addCmd(dt, "src", "go", "test", "-race", "-short", t.runFlag("TestParse|TestEcho|TestStdinCloseRace"), "flag", "os/exec")
 	// We don't want the following line, because it
 	// slows down all.bash (by 10 seconds on my laptop).
 	// The race builder should catch any error here, but doesn't.
@@ -1011,7 +1076,7 @@ func (t *tester) raceTest(dt *distTest) error {
 	}
 	if t.extLink() {
 		// Test with external linking; see issue 9133.
-		t.addCmd(dt, "src", "go", "test", "-race", "-short", "-ldflags=-linkmode=external", t.runFlag("TestParse|TestEcho"), "flag", "os/exec")
+		t.addCmd(dt, "src", "go", "test", "-race", "-short", "-ldflags=-linkmode=external", t.runFlag("TestParse|TestEcho|TestStdinCloseRace"), "flag", "os/exec")
 	}
 	return nil
 }
@@ -1073,3 +1138,38 @@ var cgoPackages = []string{
 	"net",
 	"os/user",
 }
+
+var funcBenchmark = []byte("\nfunc Benchmark")
+
+// packageHasBenchmarks reports whether pkg has benchmarks.
+// On any error, it conservatively returns true.
+//
+// This exists just to eliminate work on the builders, since compiling
+// a test in race mode just to discover it has no benchmarks costs a
+// second or two per package, and this function returns false for
+// about 100 packages.
+func (t *tester) packageHasBenchmarks(pkg string) bool {
+	pkgDir := filepath.Join(t.goroot, "src", pkg)
+	d, err := os.Open(pkgDir)
+	if err != nil {
+		return true // conservatively
+	}
+	defer d.Close()
+	names, err := d.Readdirnames(-1)
+	if err != nil {
+		return true // conservatively
+	}
+	for _, name := range names {
+		if !strings.HasSuffix(name, "_test.go") {
+			continue
+		}
+		slurp, err := ioutil.ReadFile(filepath.Join(pkgDir, name))
+		if err != nil {
+			return true // conservatively
+		}
+		if bytes.Contains(slurp, funcBenchmark) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/cmd/dist/util.go b/src/cmd/dist/util.go
index bbf3b75..e2f22df 100644
--- a/src/cmd/dist/util.go
+++ b/src/cmd/dist/util.go
@@ -445,6 +445,11 @@ func main() {
 			if elfIsLittleEndian(os.Args[0]) {
 				gohostarch = "mips64le"
 			}
+		case strings.Contains(out, "mips"):
+			gohostarch = "mips"
+			if elfIsLittleEndian(os.Args[0]) {
+				gohostarch = "mipsle"
+			}
 		case strings.Contains(out, "s390x"):
 			gohostarch = "s390x"
 		case gohostos == "darwin":
diff --git a/src/cmd/doc/dirs.go b/src/cmd/doc/dirs.go
index 2982eee..a4ef8d2 100644
--- a/src/cmd/doc/dirs.go
+++ b/src/cmd/doc/dirs.go
@@ -77,14 +77,14 @@ func (d *Dirs) bfsWalkRoot(root string) {
 		for _, dir := range this {
 			fd, err := os.Open(dir)
 			if err != nil {
-				log.Printf("error opening %s: %v", dir, err)
-				return // TODO? There may be entry before the error.
+				log.Print(err)
+				continue
 			}
 			entries, err := fd.Readdir(0)
 			fd.Close()
 			if err != nil {
-				log.Printf("error reading %s: %v", dir, err)
-				return // TODO? There may be entry before the error.
+				log.Print(err)
+				continue
 			}
 			hasGoFiles := false
 			for _, entry := range entries {
diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go
index 453a3d5..1c054fd 100644
--- a/src/cmd/doc/doc_test.go
+++ b/src/cmd/doc/doc_test.go
@@ -55,16 +55,22 @@ var tests = []test{
 		[]string{p},
 		[]string{
 			`Package comment`,
-			`const ExportedConstant = 1`,                            // Simple constant.
-			`const ConstOne = 1`,                                    // First entry in constant block.
-			`const ConstFive ...`,                                   // From block starting with unexported constant.
-			`var ExportedVariable = 1`,                              // Simple variable.
-			`var VarOne = 1`,                                        // First entry in variable block.
-			`func ExportedFunc\(a int\) bool`,                       // Function.
-			`func ReturnUnexported\(\) unexportedType`,              // Function with unexported return type.
-			`type ExportedType struct { ... }`,                      // Exported type.
-			`const ExportedTypedConstant ExportedType = iota`,       // Typed constant.
-			`const ExportedTypedConstant_unexported unexportedType`, // Typed constant, exported for unexported type.
+			`const ExportedConstant = 1`,                                   // Simple constant.
+			`const ConstOne = 1`,                                           // First entry in constant block.
+			`const ConstFive ...`,                                          // From block starting with unexported constant.
+			`var ExportedVariable = 1`,                                     // Simple variable.
+			`var VarOne = 1`,                                               // First entry in variable block.
+			`func ExportedFunc\(a int\) bool`,                              // Function.
+			`func ReturnUnexported\(\) unexportedType`,                     // Function with unexported return type.
+			`type ExportedType struct{ ... }`,                              // Exported type.
+			`const ExportedTypedConstant ExportedType = iota`,              // Typed constant.
+			`const ExportedTypedConstant_unexported unexportedType`,        // Typed constant, exported for unexported type.
+			`const ConstLeft2 uint64 ...`,                                  // Typed constant using unexported iota.
+			`const ConstGroup1 unexportedType = iota ...`,                  // Typed constant using unexported type.
+			`const ConstGroup4 ExportedType = ExportedType{}`,              // Typed constant using exported type.
+			`const MultiLineConst = ...`,                                   // Multi line constant.
+			`var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`,  // Multi line variable.
+			`func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function.
 		},
 		[]string{
 			`const internalConstant = 2`,        // No internal constants.
@@ -99,6 +105,7 @@ var tests = []test{
 			`Comment about exported constant`,  // No comment for simple constant.
 			`Comment about block of constants`, // No comment for constant block.
 			`Comment about internal function`,  // No comment for internal function.
+			`MultiLine(String|Method|Field)`,   // No data from multi line portions.
 		},
 	},
 
@@ -144,6 +151,30 @@ var tests = []test{
 		},
 		nil,
 	},
+	// Block of constants with carryover type from unexported field.
+	{
+		"block of constants with carryover type",
+		[]string{p, `ConstLeft2`},
+		[]string{
+			`ConstLeft2, constRight2 uint64`,
+			`constLeft3, ConstRight3`,
+			`ConstLeft4, ConstRight4`,
+		},
+		nil,
+	},
+	// Block of constants -u with carryover type from unexported field.
+	{
+		"block of constants with carryover type",
+		[]string{"-u", p, `ConstLeft2`},
+		[]string{
+			`_, _ uint64 = 2 \* iota, 1 << iota`,
+			`constLeft1, constRight1`,
+			`ConstLeft2, constRight2`,
+			`constLeft3, ConstRight3`,
+			`ConstLeft4, ConstRight4`,
+		},
+		nil,
+	},
 
 	// Single variable.
 	{
@@ -273,7 +304,7 @@ var tests = []test{
 
 	// Interface.
 	{
-		"type",
+		"interface type",
 		[]string{p, `ExportedInterface`},
 		[]string{
 			`Comment about exported interface`, // Include comment.
@@ -293,7 +324,7 @@ var tests = []test{
 	},
 	// Interface -u with unexported methods.
 	{
-		"type with unexported methods and -u",
+		"interface type with unexported methods and -u",
 		[]string{"-u", p, `ExportedInterface`},
 		[]string{
 			`Comment about exported interface`, // Include comment.
@@ -309,6 +340,19 @@ var tests = []test{
 		},
 	},
 
+	// Interface method.
+	{
+		"interface method",
+		[]string{p, `ExportedInterface.ExportedMethod`},
+		[]string{
+			`Comment before exported method.*\n.*ExportedMethod\(\)` +
+				`.*Comment on line with exported method`,
+		},
+		[]string{
+			`Comment about exported interface.`,
+		},
+	},
+
 	// Method.
 	{
 		"method",
diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index defddfd..daa6ed3 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -194,60 +194,176 @@ func (pkg *Package) emit(comment string, node ast.Node) {
 	}
 }
 
-var formatBuf bytes.Buffer // Reusable to avoid allocation.
-
-// formatNode is a helper function for printing.
-func (pkg *Package) formatNode(node ast.Node) []byte {
-	formatBuf.Reset()
-	format.Node(&formatBuf, pkg.fs, node)
-	return formatBuf.Bytes()
+// oneLineNode returns a one-line summary of the given input node.
+func (pkg *Package) oneLineNode(node ast.Node) string {
+	const maxDepth = 10
+	return pkg.oneLineNodeDepth(node, maxDepth)
 }
 
-// oneLineFunc prints a function declaration as a single line.
-func (pkg *Package) oneLineFunc(decl *ast.FuncDecl) {
-	decl.Doc = nil
-	decl.Body = nil
-	pkg.emit("", decl)
-}
+// oneLineNodeDepth returns a one-line summary of the given input node.
+// The depth specifies the maximum depth when traversing the AST.
+func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
+	const dotDotDot = "..."
+	if depth == 0 {
+		return dotDotDot
+	}
+	depth--
 
-// oneLineValueGenDecl prints a var or const declaration as a single line.
-func (pkg *Package) oneLineValueGenDecl(decl *ast.GenDecl) {
-	decl.Doc = nil
-	dotDotDot := ""
-	if len(decl.Specs) > 1 {
-		dotDotDot = " ..."
-	}
-	// Find the first relevant spec.
-	for i, spec := range decl.Specs {
-		valueSpec := spec.(*ast.ValueSpec) // Must succeed; we can't mix types in one genDecl.
-		if !isExported(valueSpec.Names[0].Name) {
-			continue
+	switch n := node.(type) {
+	case nil:
+		return ""
+
+	case *ast.GenDecl:
+		// Formats const and var declarations.
+		trailer := ""
+		if len(n.Specs) > 1 {
+			trailer = " " + dotDotDot
 		}
+
+		// Find the first relevant spec.
 		typ := ""
-		if valueSpec.Type != nil {
-			typ = fmt.Sprintf(" %s", pkg.formatNode(valueSpec.Type))
+		for i, spec := range n.Specs {
+			valueSpec := spec.(*ast.ValueSpec) // Must succeed; we can't mix types in one GenDecl.
+
+			// The type name may carry over from a previous specification in the
+			// case of constants and iota.
+			if valueSpec.Type != nil {
+				typ = fmt.Sprintf(" %s", pkg.oneLineNodeDepth(valueSpec.Type, depth))
+			} else if len(valueSpec.Values) > 0 {
+				typ = ""
+			}
+
+			if !isExported(valueSpec.Names[0].Name) {
+				continue
+			}
+			val := ""
+			if i < len(valueSpec.Values) && valueSpec.Values[i] != nil {
+				val = fmt.Sprintf(" = %s", pkg.oneLineNodeDepth(valueSpec.Values[i], depth))
+			}
+			return fmt.Sprintf("%s %s%s%s%s", n.Tok, valueSpec.Names[0], typ, val, trailer)
 		}
-		val := ""
-		if i < len(valueSpec.Values) && valueSpec.Values[i] != nil {
-			val = fmt.Sprintf(" = %s", pkg.formatNode(valueSpec.Values[i]))
+		return ""
+
+	case *ast.FuncDecl:
+		// Formats func declarations.
+		name := n.Name.Name
+		recv := pkg.oneLineNodeDepth(n.Recv, depth)
+		if len(recv) > 0 {
+			recv = "(" + recv + ") "
 		}
-		pkg.Printf("%s %s%s%s%s\n", decl.Tok, valueSpec.Names[0], typ, val, dotDotDot)
-		break
-	}
-}
+		fnc := pkg.oneLineNodeDepth(n.Type, depth)
+		if strings.Index(fnc, "func") == 0 {
+			fnc = fnc[4:]
+		}
+		return fmt.Sprintf("func %s%s%s", recv, name, fnc)
+
+	case *ast.TypeSpec:
+		return fmt.Sprintf("type %s %s", n.Name.Name, pkg.oneLineNodeDepth(n.Type, depth))
+
+	case *ast.FuncType:
+		var params []string
+		if n.Params != nil {
+			for _, field := range n.Params.List {
+				params = append(params, pkg.oneLineField(field, depth))
+			}
+		}
+		needParens := false
+		var results []string
+		if n.Results != nil {
+			needParens = needParens || len(n.Results.List) > 1
+			for _, field := range n.Results.List {
+				needParens = needParens || len(field.Names) > 0
+				results = append(results, pkg.oneLineField(field, depth))
+			}
+		}
+
+		param := strings.Join(params, ", ")
+		if len(results) == 0 {
+			return fmt.Sprintf("func(%s)", param)
+		}
+		result := strings.Join(results, ", ")
+		if !needParens {
+			return fmt.Sprintf("func(%s) %s", param, result)
+		}
+		return fmt.Sprintf("func(%s) (%s)", param, result)
 
-// oneLineTypeDecl prints a type declaration as a single line.
-func (pkg *Package) oneLineTypeDecl(spec *ast.TypeSpec) {
-	spec.Doc = nil
-	spec.Comment = nil
-	switch spec.Type.(type) {
-	case *ast.InterfaceType:
-		pkg.Printf("type %s interface { ... }\n", spec.Name)
 	case *ast.StructType:
-		pkg.Printf("type %s struct { ... }\n", spec.Name)
+		if n.Fields == nil || len(n.Fields.List) == 0 {
+			return "struct{}"
+		}
+		return "struct{ ... }"
+
+	case *ast.InterfaceType:
+		if n.Methods == nil || len(n.Methods.List) == 0 {
+			return "interface{}"
+		}
+		return "interface{ ... }"
+
+	case *ast.FieldList:
+		if n == nil || len(n.List) == 0 {
+			return ""
+		}
+		if len(n.List) == 1 {
+			return pkg.oneLineField(n.List[0], depth)
+		}
+		return dotDotDot
+
+	case *ast.FuncLit:
+		return pkg.oneLineNodeDepth(n.Type, depth) + " { ... }"
+
+	case *ast.CompositeLit:
+		typ := pkg.oneLineNodeDepth(n.Type, depth)
+		if len(n.Elts) == 0 {
+			return fmt.Sprintf("%s{}", typ)
+		}
+		return fmt.Sprintf("%s{ %s }", typ, dotDotDot)
+
+	case *ast.ArrayType:
+		length := pkg.oneLineNodeDepth(n.Len, depth)
+		element := pkg.oneLineNodeDepth(n.Elt, depth)
+		return fmt.Sprintf("[%s]%s", length, element)
+
+	case *ast.MapType:
+		key := pkg.oneLineNodeDepth(n.Key, depth)
+		value := pkg.oneLineNodeDepth(n.Value, depth)
+		return fmt.Sprintf("map[%s]%s", key, value)
+
+	case *ast.CallExpr:
+		fnc := pkg.oneLineNodeDepth(n.Fun, depth)
+		var args []string
+		for _, arg := range n.Args {
+			args = append(args, pkg.oneLineNodeDepth(arg, depth))
+		}
+		return fmt.Sprintf("%s(%s)", fnc, strings.Join(args, ", "))
+
+	case *ast.UnaryExpr:
+		return fmt.Sprintf("%s%s", n.Op, pkg.oneLineNodeDepth(n.X, depth))
+
+	case *ast.Ident:
+		return n.Name
+
 	default:
-		pkg.Printf("type %s %s\n", spec.Name, pkg.formatNode(spec.Type))
+		// As a fallback, use default formatter for all unknown node types.
+		buf := new(bytes.Buffer)
+		format.Node(buf, pkg.fs, node)
+		s := buf.String()
+		if strings.Contains(s, "\n") {
+			return dotDotDot
+		}
+		return s
+	}
+}
+
+// oneLineField returns a one-line summary of the field.
+func (pkg *Package) oneLineField(field *ast.Field, depth int) string {
+	var names []string
+	for _, name := range field.Names {
+		names = append(names, name.Name)
+	}
+	if len(names) == 0 {
+		return pkg.oneLineNodeDepth(field.Type, depth)
 	}
+	return strings.Join(names, ", ") + " " + pkg.oneLineNodeDepth(field.Type, depth)
 }
 
 // packageDoc prints the docs for the package (package doc plus one-liners of the rest).
@@ -266,8 +382,8 @@ func (pkg *Package) packageDoc() {
 	}
 
 	pkg.newlines(2) // Guarantee blank line before the components.
-	pkg.valueSummary(pkg.doc.Consts)
-	pkg.valueSummary(pkg.doc.Vars)
+	pkg.valueSummary(pkg.doc.Consts, false)
+	pkg.valueSummary(pkg.doc.Vars, false)
 	pkg.funcSummary(pkg.doc.Funcs, false)
 	pkg.typeSummary()
 	pkg.bugs()
@@ -302,9 +418,31 @@ func (pkg *Package) packageClause(checkUserPath bool) {
 }
 
 // valueSummary prints a one-line summary for each set of values and constants.
-func (pkg *Package) valueSummary(values []*doc.Value) {
+// If all the types in a constant or variable declaration belong to the same
+// type they can be printed by typeSummary, and so can be suppressed here.
+func (pkg *Package) valueSummary(values []*doc.Value, showGrouped bool) {
+	var isGrouped map[*doc.Value]bool
+	if !showGrouped {
+		isGrouped = make(map[*doc.Value]bool)
+		for _, typ := range pkg.doc.Types {
+			if !isExported(typ.Name) {
+				continue
+			}
+			for _, c := range typ.Consts {
+				isGrouped[c] = true
+			}
+			for _, v := range typ.Vars {
+				isGrouped[v] = true
+			}
+		}
+	}
+
 	for _, value := range values {
-		pkg.oneLineValueGenDecl(value.Decl)
+		if !isGrouped[value] {
+			if decl := pkg.oneLineNode(value.Decl); decl != "" {
+				pkg.Printf("%s\n", decl)
+			}
+		}
 	}
 }
 
@@ -316,19 +454,18 @@ func (pkg *Package) funcSummary(funcs []*doc.Func, showConstructors bool) {
 	if !showConstructors {
 		isConstructor = make(map[*doc.Func]bool)
 		for _, typ := range pkg.doc.Types {
-			for _, constructor := range typ.Funcs {
-				if isExported(typ.Name) {
-					isConstructor[constructor] = true
+			if isExported(typ.Name) {
+				for _, f := range typ.Funcs {
+					isConstructor[f] = true
 				}
 			}
 		}
 	}
 	for _, fun := range funcs {
-		decl := fun.Decl
 		// Exported functions only. The go/doc package does not include methods here.
 		if isExported(fun.Name) {
 			if !isConstructor[fun] {
-				pkg.oneLineFunc(decl)
+				pkg.Printf("%s\n", pkg.oneLineNode(fun.Decl))
 			}
 		}
 	}
@@ -340,12 +477,21 @@ func (pkg *Package) typeSummary() {
 		for _, spec := range typ.Decl.Specs {
 			typeSpec := spec.(*ast.TypeSpec) // Must succeed.
 			if isExported(typeSpec.Name.Name) {
-				pkg.oneLineTypeDecl(typeSpec)
-				// Now print the constructors.
+				pkg.Printf("%s\n", pkg.oneLineNode(typeSpec))
+				// Now print the consts, vars, and constructors.
+				for _, c := range typ.Consts {
+					if decl := pkg.oneLineNode(c.Decl); decl != "" {
+						pkg.Printf(indent+"%s\n", decl)
+					}
+				}
+				for _, v := range typ.Vars {
+					if decl := pkg.oneLineNode(v.Decl); decl != "" {
+						pkg.Printf(indent+"%s\n", decl)
+					}
+				}
 				for _, constructor := range typ.Funcs {
 					if isExported(constructor.Name) {
-						pkg.Printf(indent)
-						pkg.oneLineFunc(constructor.Decl)
+						pkg.Printf(indent+"%s\n", pkg.oneLineNode(constructor.Decl))
 					}
 				}
 			}
@@ -437,11 +583,29 @@ func (pkg *Package) symbolDoc(symbol string) bool {
 		// It's an unlikely scenario, probably not worth the trouble.
 		// TODO: Would be nice if go/doc did this for us.
 		specs := make([]ast.Spec, 0, len(value.Decl.Specs))
+		var typ ast.Expr
 		for _, spec := range value.Decl.Specs {
 			vspec := spec.(*ast.ValueSpec)
+
+			// The type name may carry over from a previous specification in the
+			// case of constants and iota.
+			if vspec.Type != nil {
+				typ = vspec.Type
+			}
+
 			for _, ident := range vspec.Names {
 				if isExported(ident.Name) {
+					if vspec.Type == nil && vspec.Values == nil && typ != nil {
+						// This a standalone identifier, as in the case of iota usage.
+						// Thus, assume the type comes from the previous type.
+						vspec.Type = &ast.Ident{
+							Name:    string(pkg.oneLineNode(typ)),
+							NamePos: vspec.End() - 1,
+						}
+					}
+
 					specs = append(specs, vspec)
+					typ = nil // Only inject type on first exported identifier
 					break
 				}
 			}
@@ -473,8 +637,8 @@ func (pkg *Package) symbolDoc(symbol string) bool {
 		if len(typ.Consts) > 0 || len(typ.Vars) > 0 || len(typ.Funcs) > 0 || len(typ.Methods) > 0 {
 			pkg.Printf("\n")
 		}
-		pkg.valueSummary(typ.Consts)
-		pkg.valueSummary(typ.Vars)
+		pkg.valueSummary(typ.Consts, true)
+		pkg.valueSummary(typ.Vars, true)
 		pkg.funcSummary(typ.Funcs, true)
 		pkg.funcSummary(typ.Methods, true)
 		found = true
@@ -591,11 +755,48 @@ func (pkg *Package) printMethodDoc(symbol, method string) bool {
 	}
 	found := false
 	for _, typ := range types {
-		for _, meth := range typ.Methods {
-			if match(method, meth.Name) {
-				decl := meth.Decl
-				decl.Body = nil
-				pkg.emit(meth.Doc, decl)
+		if len(typ.Methods) > 0 {
+			for _, meth := range typ.Methods {
+				if match(method, meth.Name) {
+					decl := meth.Decl
+					decl.Body = nil
+					pkg.emit(meth.Doc, decl)
+					found = true
+				}
+			}
+			continue
+		}
+		// Type may be an interface. The go/doc package does not attach
+		// an interface's methods to the doc.Type. We need to dig around.
+		spec := pkg.findTypeSpec(typ.Decl, typ.Name)
+		inter, ok := spec.Type.(*ast.InterfaceType)
+		if !ok {
+			// Not an interface type.
+			// TODO? Maybe handle struct fields here.
+			continue
+		}
+		for _, iMethod := range inter.Methods.List {
+			// This is an interface, so there can be only one name.
+			// TODO: Anonymous methods (embedding)
+			if len(iMethod.Names) == 0 {
+				continue
+			}
+			name := iMethod.Names[0].Name
+			if match(method, name) {
+				// pkg.oneLineField(iMethod, 0)
+				if iMethod.Doc != nil {
+					for _, comment := range iMethod.Doc.List {
+						doc.ToText(&pkg.buf, comment.Text, "", indent, indentedWidth)
+					}
+				}
+				s := pkg.oneLineNode(iMethod.Type)
+				// Hack: s starts "func" but there is no name present.
+				// We could instead build a FuncDecl but it's not worthwhile.
+				lineComment := ""
+				if iMethod.Comment != nil {
+					lineComment = fmt.Sprintf("  %s", iMethod.Comment.List[0].Text)
+				}
+				pkg.Printf("func %s%s%s\n", name, s[4:], lineComment)
 				found = true
 			}
 		}
diff --git a/src/cmd/doc/testdata/pkg.go b/src/cmd/doc/testdata/pkg.go
index 6a52ac2..924daa1 100644
--- a/src/cmd/doc/testdata/pkg.go
+++ b/src/cmd/doc/testdata/pkg.go
@@ -126,3 +126,49 @@ const Casematch = 2
 
 func ReturnUnexported() unexportedType { return 0 }
 func ReturnExported() ExportedType     { return ExportedType{} }
+
+const MultiLineConst = `
+	MultiLineString1
+	MultiLineString2
+	MultiLineString3
+`
+
+func MultiLineFunc(x interface {
+	MultiLineMethod1() int
+	MultiLineMethod2() int
+	MultiLineMethod3() int
+}) (r struct {
+	MultiLineField1 int
+	MultiLineField2 int
+	MultiLineField3 int
+}) {
+	return r
+}
+
+var MultiLineVar = map[struct {
+	MultiLineField1 string
+	MultiLineField2 uint64
+}]struct {
+	MultiLineField3 error
+	MultiLineField2 error
+}{
+	{"FieldVal1", 1}: {},
+	{"FieldVal2", 2}: {},
+	{"FieldVal3", 3}: {},
+}
+
+const (
+	_, _ uint64 = 2 * iota, 1 << iota
+	constLeft1, constRight1
+	ConstLeft2, constRight2
+	constLeft3, ConstRight3
+	ConstLeft4, ConstRight4
+)
+
+const (
+	ConstGroup1 unexportedType = iota
+	ConstGroup2
+	ConstGroup3
+)
+
+const ConstGroup4 ExportedType = ExportedType{}
diff --git a/src/cmd/fix/context.go b/src/cmd/fix/context.go
new file mode 100644
index 0000000..926a06c
--- /dev/null
+++ b/src/cmd/fix/context.go
@@ -0,0 +1,25 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"go/ast"
+)
+
+func init() {
+	register(contextFix)
+}
+
+var contextFix = fix{
+	name:     "context",
+	date:     "2016-09-09",
+	f:        ctxfix,
+	desc:     `Change imports of golang.org/x/net/context to context`,
+	disabled: true,
+}
+
+func ctxfix(f *ast.File) bool {
+	return rewriteImport(f, "golang.org/x/net/context", "context")
+}
diff --git a/src/cmd/fix/context_test.go b/src/cmd/fix/context_test.go
new file mode 100644
index 0000000..935d0d7
--- /dev/null
+++ b/src/cmd/fix/context_test.go
@@ -0,0 +1,42 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+	addTestCases(contextTests, ctxfix)
+}
+
+var contextTests = []testCase{
+	{
+		Name: "context.0",
+		In: `package main
+
+import "golang.org/x/net/context"
+
+var _ = "golang.org/x/net/context"
+`,
+		Out: `package main
+
+import "context"
+
+var _ = "golang.org/x/net/context"
+`,
+	},
+	{
+		Name: "context.1",
+		In: `package main
+
+import ctx "golang.org/x/net/context"
+
+var _ = ctx.Background()
+`,
+		Out: `package main
+
+import ctx "context"
+
+var _ = ctx.Background()
+`,
+	},
+}
diff --git a/src/cmd/fix/fix.go b/src/cmd/fix/fix.go
index ab16a21..03c828a 100644
--- a/src/cmd/fix/fix.go
+++ b/src/cmd/fix/fix.go
@@ -17,10 +17,11 @@ import (
 )
 
 type fix struct {
-	name string
-	date string // date that fix was introduced, in YYYY-MM-DD format
-	f    func(*ast.File) bool
-	desc string
+	name     string
+	date     string // date that fix was introduced, in YYYY-MM-DD format
+	f        func(*ast.File) bool
+	desc     string
+	disabled bool // whether this fix should be disabled by default
 }
 
 // main runs sort.Sort(byName(fixes)) before printing list of fixes.
diff --git a/src/cmd/fix/gotypes.go b/src/cmd/fix/gotypes.go
index bb29a0c..8a4019c 100644
--- a/src/cmd/fix/gotypes.go
+++ b/src/cmd/fix/gotypes.go
@@ -14,10 +14,10 @@ func init() {
 }
 
 var gotypesFix = fix{
-	"gotypes",
-	"2015-07-16",
-	gotypes,
-	`Change imports of golang.org/x/tools/go/{exact,types} to go/{constant,types}`,
+	name: "gotypes",
+	date: "2015-07-16",
+	f:    gotypes,
+	desc: `Change imports of golang.org/x/tools/go/{exact,types} to go/{constant,types}`,
 }
 
 func gotypes(f *ast.File) bool {
diff --git a/src/cmd/fix/main.go b/src/cmd/fix/main.go
index 8b62346..3b4130b 100644
--- a/src/cmd/fix/main.go
+++ b/src/cmd/fix/main.go
@@ -45,7 +45,11 @@ func usage() {
 	fmt.Fprintf(os.Stderr, "\nAvailable rewrites are:\n")
 	sort.Sort(byName(fixes))
 	for _, f := range fixes {
-		fmt.Fprintf(os.Stderr, "\n%s\n", f.name)
+		if f.disabled {
+			fmt.Fprintf(os.Stderr, "\n%s (disabled)\n", f.name)
+		} else {
+			fmt.Fprintf(os.Stderr, "\n%s\n", f.name)
+		}
 		desc := strings.TrimSpace(f.desc)
 		desc = strings.Replace(desc, "\n", "\n\t", -1)
 		fmt.Fprintf(os.Stderr, "\t%s\n", desc)
@@ -139,6 +143,9 @@ func processFile(filename string, useStdin bool) error {
 		if allowed != nil && !allowed[fix.name] {
 			continue
 		}
+		if fix.disabled && !force[fix.name] {
+			continue
+		}
 		if fix.f(newFile) {
 			fixed = true
 			fmt.Fprintf(&fixlog, " %s", fix.name)
diff --git a/src/cmd/fix/netipv6zone.go b/src/cmd/fix/netipv6zone.go
index 49cd307..3e502bd 100644
--- a/src/cmd/fix/netipv6zone.go
+++ b/src/cmd/fix/netipv6zone.go
@@ -11,10 +11,10 @@ func init() {
 }
 
 var netipv6zoneFix = fix{
-	"netipv6zone",
-	"2012-11-26",
-	netipv6zone,
-	`Adapt element key to IPAddr, UDPAddr or TCPAddr composite literals.
+	name: "netipv6zone",
+	date: "2012-11-26",
+	f:    netipv6zone,
+	desc: `Adapt element key to IPAddr, UDPAddr or TCPAddr composite literals.
 
 https://codereview.appspot.com/6849045/
 `,
diff --git a/src/cmd/fix/printerconfig.go b/src/cmd/fix/printerconfig.go
index 286c5f2..6d93996 100644
--- a/src/cmd/fix/printerconfig.go
+++ b/src/cmd/fix/printerconfig.go
@@ -11,10 +11,10 @@ func init() {
 }
 
 var printerconfigFix = fix{
-	"printerconfig",
-	"2012-12-11",
-	printerconfig,
-	`Add element keys to Config composite literals.`,
+	name: "printerconfig",
+	date: "2012-12-11",
+	f:    printerconfig,
+	desc: `Add element keys to Config composite literals.`,
 }
 
 func printerconfig(f *ast.File) bool {
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 58b0d16..b480742 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -17,6 +17,7 @@
 // 	clean       remove object files
 // 	doc         show documentation for package or symbol
 // 	env         print Go environment information
+// 	bug         print information for bug reports
 // 	fix         run go tool fix on packages
 // 	fmt         run gofmt on package sources
 // 	generate    generate Go files by processing source
@@ -323,6 +324,17 @@
 // each named variable on its own line.
 //
 //
+// Print information for bug reports
+//
+// Usage:
+//
+// 	go bug
+//
+// Bug prints information that helps file effective bug reports.
+//
+// Bugs may be reported at https://golang.org/issue/new.
+//
+//
 // Run go tool fix on packages
 //
 // Usage:
@@ -367,7 +379,7 @@
 //
 // Generate runs commands described by directives within existing
 // files. Those commands can run any process but the intent is to
-// create or update Go source files, for instance by running yacc.
+// create or update Go source files.
 //
 // Go generate is never run automatically by go build, go get, go test,
 // and so on. It must be run explicitly.
@@ -430,10 +442,10 @@
 // can be used to create aliases or to handle multiword generators.
 // For example,
 //
-// 	//go:generate -command yacc go tool yacc
+// 	//go:generate -command foo go tool foo
 //
-// specifies that the command "yacc" represents the generator
-// "go tool yacc".
+// specifies that the command "foo" represents the generator
+// "go tool foo".
 //
 // Generate processes packages in the order given on the command line,
 // one at a time. If the command line lists .go files, they are treated
@@ -496,11 +508,13 @@
 // and their dependencies.  By default, get uses the network to check out
 // missing packages but does not use it to look for updates to existing packages.
 //
+// The -v flag enables verbose progress and debug output.
+//
 // Get also accepts build flags to control the installation. See 'go help build'.
 //
 // When checking out a new package, get creates the target directory
 // GOPATH/src/<import-path>. If the GOPATH contains multiple entries,
-// get uses the first one. See 'go help gopath'.
+// get uses the first one. For more details see: 'go help gopath'.
 //
 // When checking out or updating a package, get looks for a branch or tag
 // that matches the locally installed version of Go. The most important
@@ -584,6 +598,8 @@
 //         SwigFiles      []string // .swig files
 //         SwigCXXFiles   []string // .swigcxx files
 //         SysoFiles      []string // .syso object files to add to archive
+//         TestGoFiles    []string // _test.go files in package
+//         XTestGoFiles   []string // _test.go files outside package
 //
 //         // Cgo directives
 //         CgoCFLAGS    []string // cgo: flags for C compiler
@@ -594,20 +610,23 @@
 //         CgoPkgConfig []string // cgo: pkg-config names
 //
 //         // Dependency information
-//         Imports []string // import paths used by this package
-//         Deps    []string // all (recursively) imported dependencies
+//         Imports      []string // import paths used by this package
+//         Deps         []string // all (recursively) imported dependencies
+//         TestImports  []string // imports from TestGoFiles
+//         XTestImports []string // imports from XTestGoFiles
 //
 //         // Error information
 //         Incomplete bool            // this package or a dependency has an error
 //         Error      *PackageError   // error loading package
 //         DepsErrors []*PackageError // errors loading dependencies
-//
-//         TestGoFiles  []string // _test.go files in package
-//         TestImports  []string // imports from TestGoFiles
-//         XTestGoFiles []string // _test.go files outside package
-//         XTestImports []string // imports from XTestGoFiles
 //     }
 //
+// Packages stored in vendor directories report an ImportPath that includes the
+// path to the vendor directory (for example, "d/vendor/p" instead of "p"),
+// so that the ImportPath uniquely identifies a given copy of a package.
+// The Imports, Deps, TestImports, and XTestImports lists also contain these
+// expanded imports paths. See golang.org/s/go15vendor for more about vendoring.
+//
 // The error information, if any, is
 //
 //     type PackageError struct {
@@ -852,6 +871,10 @@
 // 		position independent executables (PIE). Packages not named
 // 		main are ignored.
 //
+// 	-buildmode=plugin
+// 		Build the listed main packages, plus all packages that they
+// 		import, into a Go plugin. Packages not named main are ignored.
+//
 //
 // File types
 //
@@ -906,8 +929,11 @@
 // On Windows, the value is a semicolon-separated string.
 // On Plan 9, the value is a list.
 //
-// GOPATH must be set to get, build and install packages outside the
-// standard Go tree.
+// If the environment variable is unset, GOPATH defaults
+// to a subdirectory named "go" in the user's home directory
+// ($HOME/go on Unix, %USERPROFILE%\go on Windows),
+// unless that directory holds a Go distribution.
+// Run "go env GOPATH" to see the current GOPATH.
 //
 // Each directory listed in GOPATH must have a prescribed structure:
 //
@@ -935,9 +961,9 @@
 //
 // Here's an example directory layout:
 //
-//     GOPATH=/home/user/gocode
+//     GOPATH=/home/user/go
 //
-//     /home/user/gocode/
+//     /home/user/go/
 //         src/
 //             foo/
 //                 bar/               (go code in package bar)
@@ -963,7 +989,7 @@
 // by code in the directory tree rooted at the parent of "internal".
 // Here's an extended version of the directory layout above:
 //
-//     /home/user/gocode/
+//     /home/user/go/
 //         src/
 //             crash/
 //                 bang/              (go code in package bang)
@@ -1001,7 +1027,7 @@
 // but with the "internal" directory renamed to "vendor"
 // and a new foo/vendor/crash/bang directory added:
 //
-//     /home/user/gocode/
+//     /home/user/go/
 //         src/
 //             crash/
 //                 bang/              (go code in package bang)
@@ -1060,7 +1086,7 @@
 // 		The operating system for which to compile code.
 // 		Examples are linux, darwin, windows, netbsd.
 // 	GOPATH
-// 		See 'go help gopath'.
+// 		For more details see: 'go help gopath'.
 // 	GORACE
 // 		Options for the race detector.
 // 		See https://golang.org/doc/articles/race_detector.html.
@@ -1082,10 +1108,15 @@
 // 	CGO_CXXFLAGS
 // 		Flags that cgo will pass to the compiler when compiling
 // 		C++ code.
+// 	CGO_FFLAGS
+// 		Flags that cgo will pass to the compiler when compiling
+// 		Fortran code.
 // 	CGO_LDFLAGS
 // 		Flags that cgo will pass to the compiler when linking.
 // 	CXX
 // 		The command to use to compile C++ code.
+// 	PKG_CONFIG
+// 		Path to pkg-config tool.
 //
 // Architecture-specific environment variables:
 //
@@ -1107,14 +1138,18 @@
 // 		Whether the linker should use external linking mode
 // 		when using -linkmode=auto with code that uses cgo.
 // 		Set to 0 to disable external linking mode, 1 to enable it.
+// 	GIT_ALLOW_PROTOCOL
+// 		Defined by Git. A colon-separated list of schemes that are allowed to be used
+// 		with git fetch/clone. If set, any scheme not explicitly mentioned will be
+// 		considered insecure by 'go get'.
 //
 //
 // Import path syntax
 //
-// An import path (see 'go help packages') denotes a package
-// stored in the local file system.  In general, an import path denotes
-// either a standard package (such as "unicode/utf8") or a package
-// found in one of the work spaces (see 'go help gopath').
+// An import path (see 'go help packages') denotes a package stored in the local
+// file system.  In general, an import path denotes either a standard package (such
+// as "unicode/utf8") or a package found in one of the work spaces (For more
+// details see: 'go help gopath').
 //
 // Relative import paths
 //
@@ -1206,6 +1241,11 @@
 // each is tried in turn when downloading.  For example, a Git
 // download tries https://, then git+ssh://.
 //
+// By default, downloads are restricted to known secure protocols
+// (e.g. https, ssh). To override this setting for Git downloads, the
+// GIT_ALLOW_PROTOCOL environment variable can be set (For more details see:
+// 'go help environment').
+//
 // If the import path is not a known code hosting site and also lacks a
 // version control qualifier, the go tool attempts to fetch the import
 // over https/http and looks for a <meta> tag in the document's HTML
@@ -1246,8 +1286,8 @@
 // same meta tag and then git clone https://code.org/r/p/exproj into
 // GOPATH/src/example.org.
 //
-// New downloaded packages are written to the first directory
-// listed in the GOPATH environment variable (see 'go help gopath').
+// New downloaded packages are written to the first directory listed in the GOPATH
+// environment variable (For more details see: 'go help gopath').
 //
 // The go command attempts to download the version of the
 // package appropriate for the Go release being used.
@@ -1291,7 +1331,7 @@
 //
 // Otherwise, the import path P denotes the package found in
 // the directory DIR/src/P for some DIR listed in the GOPATH
-// environment variable (see 'go help gopath').
+// environment variable (For more details see: 'go help gopath').
 //
 // If no import paths are given, the action applies to the
 // package in the current directory.
@@ -1311,6 +1351,9 @@
 // - "cmd" expands to the Go repository's commands and their
 // internal libraries.
 //
+// Import paths beginning with "cmd/" only match source code in
+// the Go repository.
+//
 // An import path is a pattern if it includes one or more "..." wildcards,
 // each of which can match any string, including the empty string and
 // strings containing slashes.  Such a pattern expands to all package
@@ -1366,28 +1409,11 @@
 // 	    By default, no benchmarks run. To run all benchmarks,
 // 	    use '-bench .' or '-bench=.'.
 //
-// 	-benchmem
-// 	    Print memory allocation statistics for benchmarks.
-//
 // 	-benchtime t
 // 	    Run enough iterations of each benchmark to take t, specified
 // 	    as a time.Duration (for example, -benchtime 1h30s).
 // 	    The default is 1 second (1s).
 //
-// 	-blockprofile block.out
-// 	    Write a goroutine blocking profile to the specified file
-// 	    when all tests are complete.
-// 	    Writes test binary as -c would.
-//
-// 	-blockprofilerate n
-// 	    Control the detail provided in goroutine blocking profiles by
-// 	    calling runtime.SetBlockProfileRate with n.
-// 	    See 'go doc runtime.SetBlockProfileRate'.
-// 	    The profiler aims to sample, on average, one blocking event every
-// 	    n nanoseconds the program spends blocked.  By default,
-// 	    if -test.blockprofile is set without this flag, all blocking events
-// 	    are recorded, equivalent to -test.blockprofilerate=1.
-//
 // 	-count n
 // 	    Run each test and benchmark n times (default 1).
 // 	    If -cpu is set, run n times for each GOMAXPROCS value.
@@ -1413,33 +1439,11 @@
 // 	    Packages are specified as import paths.
 // 	    Sets -cover.
 //
-// 	-coverprofile cover.out
-// 	    Write a coverage profile to the file after all tests have passed.
-// 	    Sets -cover.
-//
 // 	-cpu 1,2,4
 // 	    Specify a list of GOMAXPROCS values for which the tests or
 // 	    benchmarks should be executed.  The default is the current value
 // 	    of GOMAXPROCS.
 //
-// 	-cpuprofile cpu.out
-// 	    Write a CPU profile to the specified file before exiting.
-// 	    Writes test binary as -c would.
-//
-// 	-memprofile mem.out
-// 	    Write a memory profile to the file after all tests have passed.
-// 	    Writes test binary as -c would.
-//
-// 	-memprofilerate n
-// 	    Enable more precise (and expensive) memory profiles by setting
-// 	    runtime.MemProfileRate.  See 'go doc runtime.MemProfileRate'.
-// 	    To profile all memory allocations, use -test.memprofilerate=1
-// 	    and pass --alloc_space flag to the pprof tool.
-//
-// 	-outputdir directory
-// 	    Place output files from profiling in the specified directory,
-// 	    by default the directory in which "go test" is running.
-//
 // 	-parallel n
 // 	    Allow parallel execution of test functions that call t.Parallel.
 // 	    The value of this flag is the maximum number of tests to run
@@ -1465,13 +1469,64 @@
 // 	    If a test runs longer than t, panic.
 // 	    The default is 10 minutes (10m).
 //
-// 	-trace trace.out
-// 	    Write an execution trace to the specified file before exiting.
-//
 // 	-v
 // 	    Verbose output: log all tests as they are run. Also print all
 // 	    text from Log and Logf calls even if the test succeeds.
 //
+// The following flags are also recognized by 'go test' and can be used to
+// profile the tests during execution::
+//
+// 	-benchmem
+// 	    Print memory allocation statistics for benchmarks.
+//
+// 	-blockprofile block.out
+// 	    Write a goroutine blocking profile to the specified file
+// 	    when all tests are complete.
+// 	    Writes test binary as -c would.
+//
+// 	-blockprofilerate n
+// 	    Control the detail provided in goroutine blocking profiles by
+// 	    calling runtime.SetBlockProfileRate with n.
+// 	    See 'go doc runtime.SetBlockProfileRate'.
+// 	    The profiler aims to sample, on average, one blocking event every
+// 	    n nanoseconds the program spends blocked.  By default,
+// 	    if -test.blockprofile is set without this flag, all blocking events
+// 	    are recorded, equivalent to -test.blockprofilerate=1.
+//
+// 	-coverprofile cover.out
+// 	    Write a coverage profile to the file after all tests have passed.
+// 	    Sets -cover.
+//
+// 	-cpuprofile cpu.out
+// 	    Write a CPU profile to the specified file before exiting.
+// 	    Writes test binary as -c would.
+//
+// 	-memprofile mem.out
+// 	    Write a memory profile to the file after all tests have passed.
+// 	    Writes test binary as -c would.
+//
+// 	-memprofilerate n
+// 	    Enable more precise (and expensive) memory profiles by setting
+// 	    runtime.MemProfileRate.  See 'go doc runtime.MemProfileRate'.
+// 	    To profile all memory allocations, use -test.memprofilerate=1
+// 	    and pass --alloc_space flag to the pprof tool.
+//
+// 	-mutexprofile mutex.out
+// 	    Write a mutex contention profile to the specified file
+// 	    when all tests are complete.
+// 	    Writes test binary as -c would.
+//
+// 	-mutexprofilefraction n
+//  	    Sample 1 in n stack traces of goroutines holding a
+// 	    contended mutex.
+//
+// 	-outputdir directory
+// 	    Place output files from profiling in the specified directory,
+// 	    by default the directory in which "go test" is running.
+//
+// 	-trace trace.out
+// 	    Write an execution trace to the specified file before exiting.
+//
 // Each of these flags is also recognized with an optional 'test.' prefix,
 // as in -test.v. When invoking the generated test binary (the result of
 // 'go test -c') directly, however, the prefix is mandatory.
diff --git a/src/cmd/go/bootstrap.go b/src/cmd/go/bootstrap.go
index caa9676..2148d12 100644
--- a/src/cmd/go/bootstrap.go
+++ b/src/cmd/go/bootstrap.go
@@ -36,3 +36,6 @@ func httpsOrHTTP(importPath string, security securityMode) (string, io.ReadClose
 func parseMetaGoImports(r io.Reader) ([]metaImport, error) {
 	panic("unreachable")
 }
+
+func queryEscape(s string) string { panic("unreachable") }
+func openBrowser(url string) bool { panic("unreachable") }
diff --git a/src/cmd/go/bug.go b/src/cmd/go/bug.go
new file mode 100644
index 0000000..2977c94
--- /dev/null
+++ b/src/cmd/go/bug.go
@@ -0,0 +1,209 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"regexp"
+	"runtime"
+	"strings"
+)
+
+var cmdBug = &Command{
+	Run:       runBug,
+	UsageLine: "bug",
+	Short:     "print information for bug reports",
+	Long: `
+Bug prints information that helps file effective bug reports.
+
+Bugs may be reported at https://golang.org/issue/new.
+	`,
+}
+
+func init() {
+	cmdBug.Flag.BoolVar(&buildV, "v", false, "")
+}
+
+func runBug(cmd *Command, args []string) {
+	var buf bytes.Buffer
+	buf.WriteString(bugHeader)
+	inspectGoVersion(&buf)
+	fmt.Fprint(&buf, "#### System details\n\n")
+	fmt.Fprintln(&buf, "```")
+	fmt.Fprintf(&buf, "go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
+	env := newEnv
+	env = append(env, extraEnvVars()...)
+	for _, e := range env {
+		fmt.Fprintf(&buf, "%s=\"%s\"\n", e.name, e.value)
+	}
+	printGoDetails(&buf)
+	printOSDetails(&buf)
+	printCDetails(&buf)
+	fmt.Fprintln(&buf, "```")
+
+	body := buf.String()
+	url := "https://github.com/golang/go/issues/new?body=" + queryEscape(body)
+	if !openBrowser(url) {
+		fmt.Print("Please file a new issue at golang.org/issue/new using this template:\n\n")
+		fmt.Print(body)
+	}
+}
+
+const bugHeader = `Please answer these questions before submitting your issue. Thanks!
+
+#### What did you do?
+If possible, provide a recipe for reproducing the error.
+A complete runnable program is good.
+A link on play.golang.org is best.
+
+
+#### What did you expect to see?
+
+
+#### What did you see instead?
+
+
+`
+
+func printGoDetails(w io.Writer) {
+	printCmdOut(w, "GOROOT/bin/go version: ", filepath.Join(runtime.GOROOT(), "bin/go"), "version")
+	printCmdOut(w, "GOROOT/bin/go tool compile -V: ", filepath.Join(runtime.GOROOT(), "bin/go"), "tool", "compile", "-V")
+}
+
+func printOSDetails(w io.Writer) {
+	switch runtime.GOOS {
+	case "darwin":
+		printCmdOut(w, "uname -v: ", "uname", "-v")
+		printCmdOut(w, "", "sw_vers")
+	case "linux":
+		printCmdOut(w, "uname -sr: ", "uname", "-sr")
+		printCmdOut(w, "", "lsb_release", "-a")
+		printGlibcVersion(w)
+	case "openbsd", "netbsd", "freebsd", "dragonfly":
+		printCmdOut(w, "uname -v: ", "uname", "-v")
+	case "solaris":
+		out, err := ioutil.ReadFile("/etc/release")
+		if err == nil {
+			fmt.Fprintf(w, "/etc/release: %s\n", out)
+		} else {
+			if buildV {
+				fmt.Printf("failed to read /etc/release: %v\n", err)
+			}
+		}
+	}
+}
+
+func printCDetails(w io.Writer) {
+	printCmdOut(w, "lldb --version: ", "lldb", "--version")
+	cmd := exec.Command("gdb", "--version")
+	out, err := cmd.Output()
+	if err == nil {
+		// There's apparently no combination of command line flags
+		// to get gdb to spit out its version without the license and warranty.
+		// Print up to the first newline.
+		fmt.Fprintf(w, "gdb --version: %s\n", firstLine(out))
+	} else {
+		if buildV {
+			fmt.Printf("failed to run gdb --version: %v\n", err)
+		}
+	}
+}
+
+func inspectGoVersion(w io.Writer) {
+	data, err := httpGET("https://golang.org/VERSION?m=text")
+	if err != nil {
+		if buildV {
+			fmt.Printf("failed to read from golang.org/VERSION: %v\n", err)
+		}
+		return
+	}
+
+	// golang.org/VERSION currently returns a whitespace-free string,
+	// but just in case, protect against that changing.
+	// Similarly so for runtime.Version.
+	release := string(bytes.TrimSpace(data))
+	vers := strings.TrimSpace(runtime.Version())
+
+	if vers == release {
+		// Up to date
+		return
+	}
+
+	// Devel version or outdated release. Either way, this request is apropos.
+	fmt.Fprintf(w, "#### Does this issue reproduce with the latest release (%s)?\n\n\n", release)
+}
+
+// printCmdOut prints the output of running the given command.
+// It ignores failures; 'go bug' is best effort.
+func printCmdOut(w io.Writer, prefix, path string, args ...string) {
+	cmd := exec.Command(path, args...)
+	out, err := cmd.Output()
+	if err != nil {
+		if buildV {
+			fmt.Printf("%s %s: %v\n", path, strings.Join(args, " "), err)
+		}
+		return
+	}
+	fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out))
+}
+
+// firstLine returns the first line of a given byte slice.
+func firstLine(buf []byte) []byte {
+	idx := bytes.IndexByte(buf, '\n')
+	if idx > 0 {
+		buf = buf[:idx]
+	}
+	return bytes.TrimSpace(buf)
+}
+
+// printGlibcVersion prints information about the glibc version.
+// It ignores failures.
+func printGlibcVersion(w io.Writer) {
+	tempdir := os.TempDir()
+	if tempdir == "" {
+		return
+	}
+	src := []byte(`int main() {}`)
+	srcfile := filepath.Join(tempdir, "go-bug.c")
+	outfile := filepath.Join(tempdir, "go-bug")
+	err := ioutil.WriteFile(srcfile, src, 0644)
+	if err != nil {
+		return
+	}
+	defer os.Remove(srcfile)
+	cmd := exec.Command("gcc", "-o", outfile, srcfile)
+	if _, err = cmd.CombinedOutput(); err != nil {
+		return
+	}
+	defer os.Remove(outfile)
+
+	cmd = exec.Command("ldd", outfile)
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		return
+	}
+	re := regexp.MustCompile(`libc\.so[^ ]* => ([^ ]+)`)
+	m := re.FindStringSubmatch(string(out))
+	if m == nil {
+		return
+	}
+	cmd = exec.Command(m[1])
+	out, err = cmd.Output()
+	if err != nil {
+		return
+	}
+	fmt.Fprintf(w, "%s: %s\n", m[1], firstLine(out))
+
+	// print another line (the one containing version string) in case of musl libc
+	if idx := bytes.IndexByte(out, '\n'); bytes.Index(out, []byte("musl")) != -1 && idx > -1 {
+		fmt.Fprintf(w, "%s\n", firstLine(out[idx+1:]))
+	}
+}
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 3c0b994..684d033 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -338,6 +338,13 @@ func buildModeInit() {
 		case "darwin/arm", "darwin/arm64":
 			codegenArg = "-shared"
 		default:
+			switch goos {
+			case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
+				// Use -shared so that the result is
+				// suitable for inclusion in a PIE or
+				// shared library.
+				codegenArg = "-shared"
+			}
 		}
 		exeSuffix = ".a"
 		ldBuildmode = "c-archive"
@@ -399,6 +406,22 @@ func buildModeInit() {
 			fatalf("-buildmode=shared and -o not supported together")
 		}
 		ldBuildmode = "shared"
+	case "plugin":
+		pkgsFilter = pkgsMain
+		if gccgo {
+			codegenArg = "-fPIC"
+		} else {
+			switch platform {
+			case "linux/amd64", "linux/arm", "linux/arm64", "linux/386",
+				"android/amd64", "android/arm", "android/arm64", "android/386",
+				"darwin/amd64":
+			default:
+				fatalf("-buildmode=plugin not supported on %s\n", platform)
+			}
+			codegenArg = "-dynlink"
+		}
+		exeSuffix = ".so"
+		ldBuildmode = "plugin"
 	default:
 		fatalf("buildmode=%s not supported", buildBuildmode)
 	}
@@ -424,10 +447,13 @@ func buildModeInit() {
 			buildAsmflags = append(buildAsmflags, codegenArg)
 			buildGcflags = append(buildGcflags, codegenArg)
 		}
-		if buildContext.InstallSuffix != "" {
-			buildContext.InstallSuffix += "_"
+		// Don't alter InstallSuffix when modifying default codegen args.
+		if buildBuildmode != "default" || buildLinkshared {
+			if buildContext.InstallSuffix != "" {
+				buildContext.InstallSuffix += "_"
+			}
+			buildContext.InstallSuffix += codegenArg[1:]
 		}
-		buildContext.InstallSuffix += codegenArg[1:]
 	}
 }
 
@@ -444,6 +470,11 @@ func runBuild(cmd *Command, args []string) {
 		*buildO += exeSuffix
 	}
 
+	// Special case -o /dev/null by not writing at all.
+	if *buildO == os.DevNull {
+		*buildO = ""
+	}
+
 	// sanity check some often mis-used options
 	switch buildContext.Compiler {
 	case "gccgo":
@@ -572,6 +603,10 @@ func libname(args []string, pkgs []*Package) (string, error) {
 }
 
 func runInstall(cmd *Command, args []string) {
+	installPackages(args, false)
+}
+
+func installPackages(args []string, forGet bool) {
 	if gobin != "" && !filepath.IsAbs(gobin) {
 		fatalf("cannot install, GOBIN must be an absolute path")
 	}
@@ -591,7 +626,7 @@ func runInstall(cmd *Command, args []string) {
 				errorf("go install: no install location for %s: hidden by %s", p.Dir, p.ConflictDir)
 			default:
 				errorf("go install: no install location for directory %s outside GOPATH\n"+
-					"\tFor more details see: go help gopath", p.Dir)
+					"\tFor more details see: 'go help gopath'", p.Dir)
 			}
 		}
 	}
@@ -599,6 +634,8 @@ func runInstall(cmd *Command, args []string) {
 
 	var b builder
 	b.init()
+	// Set the behavior for `go get` to not error on packages with test files only.
+	b.testFilesOnlyOK = forGet
 	var a *action
 	if buildBuildmode == "shared" {
 		if libName, err := libname(args, pkgs); err != nil {
@@ -689,6 +726,8 @@ type builder struct {
 	flagCache   map[string]bool      // a cache of supported compiler flags
 	print       func(args ...interface{}) (int, error)
 
+	testFilesOnlyOK bool // do not error if the packages only have test files
+
 	output    sync.Mutex
 	scriptDir string // current directory in printed script
 
@@ -1272,6 +1311,8 @@ func (b *builder) do(root *action) {
 		if err != nil {
 			if err == errPrintedOutput {
 				setExitStatus(2)
+			} else if _, ok := err.(*build.NoGoError); ok && len(a.p.TestGoFiles) > 0 && b.testFilesOnlyOK {
+				// Ignore the "no buildable Go source files" error for a package with only test files.
 			} else {
 				errorf("%s", err)
 			}
@@ -1358,7 +1399,7 @@ func (b *builder) build(a *action) (err error) {
 	}
 
 	defer func() {
-		if err != nil && err != errPrintedOutput {
+		if _, ok := err.(*build.NoGoError); err != nil && err != errPrintedOutput && !(ok && b.testFilesOnlyOK && len(a.p.TestGoFiles) > 0) {
 			err = fmt.Errorf("go build %s: %v", a.p.ImportPath, err)
 		}
 	}()
@@ -1389,7 +1430,7 @@ func (b *builder) build(a *action) (err error) {
 		}
 	}
 
-	var gofiles, cgofiles, cfiles, sfiles, cxxfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string
+	var gofiles, cgofiles, objdirCgofiles, cfiles, sfiles, cxxfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string
 
 	gofiles = append(gofiles, a.p.GoFiles...)
 	cgofiles = append(cgofiles, a.p.CgoFiles...)
@@ -1411,7 +1452,7 @@ func (b *builder) build(a *action) (err error) {
 		if err != nil {
 			return err
 		}
-		cgofiles = append(cgofiles, outGo...)
+		objdirCgofiles = append(objdirCgofiles, outGo...)
 		cfiles = append(cfiles, outC...)
 		cxxfiles = append(cxxfiles, outCXX...)
 	}
@@ -1446,7 +1487,7 @@ func (b *builder) build(a *action) (err error) {
 		if a.cgo != nil && a.cgo.target != "" {
 			cgoExe = a.cgo.target
 		}
-		outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, cxxfiles, a.p.MFiles, a.p.FFiles)
+		outGo, outObj, err := b.cgo(a, cgoExe, obj, pcCFLAGS, pcLDFLAGS, cgofiles, objdirCgofiles, gccfiles, cxxfiles, a.p.MFiles, a.p.FFiles)
 		if err != nil {
 			return err
 		}
@@ -1544,12 +1585,12 @@ func (b *builder) build(a *action) (err error) {
 	}
 
 	// Assemble .s files.
-	for _, file := range sfiles {
-		out := file[:len(file)-len(".s")] + ".o"
-		if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil {
+	if len(sfiles) > 0 {
+		ofiles, err := buildToolchain.asm(b, a.p, obj, sfiles)
+		if err != nil {
 			return err
 		}
-		objects = append(objects, out)
+		objects = append(objects, ofiles...)
 	}
 
 	// NOTE(rsc): On Windows, it is critically important that the
@@ -1588,23 +1629,62 @@ func (b *builder) build(a *action) (err error) {
 	return nil
 }
 
+// pkgconfigCmd returns a pkg-config binary name
+// defaultPkgConfig is defined in zdefaultcc.go, written by cmd/dist.
+func (b *builder) pkgconfigCmd() string {
+	return envList("PKG_CONFIG", defaultPkgConfig)[0]
+}
+
+// splitPkgConfigOutput parses the pkg-config output into a slice of
+// flags. pkg-config always uses \ to escape special characters.
+func splitPkgConfigOutput(out []byte) []string {
+	if len(out) == 0 {
+		return nil
+	}
+	var flags []string
+	flag := make([]byte, len(out))
+	r, w := 0, 0
+	for r < len(out) {
+		switch out[r] {
+		case ' ', '\t', '\r', '\n':
+			if w > 0 {
+				flags = append(flags, string(flag[:w]))
+			}
+			w = 0
+		case '\\':
+			r++
+			fallthrough
+		default:
+			if r < len(out) {
+				flag[w] = out[r]
+				w++
+			}
+		}
+		r++
+	}
+	if w > 0 {
+		flags = append(flags, string(flag[:w]))
+	}
+	return flags
+}
+
 // Calls pkg-config if needed and returns the cflags/ldflags needed to build the package.
 func (b *builder) getPkgConfigFlags(p *Package) (cflags, ldflags []string, err error) {
 	if pkgs := p.CgoPkgConfig; len(pkgs) > 0 {
 		var out []byte
-		out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs)
+		out, err = b.runOut(p.Dir, p.ImportPath, nil, b.pkgconfigCmd(), "--cflags", pkgs)
 		if err != nil {
-			b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out))
+			b.showOutput(p.Dir, b.pkgconfigCmd()+" --cflags "+strings.Join(pkgs, " "), string(out))
 			b.print(err.Error() + "\n")
 			err = errPrintedOutput
 			return
 		}
 		if len(out) > 0 {
-			cflags = strings.Fields(string(out))
+			cflags = splitPkgConfigOutput(out)
 		}
-		out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs)
+		out, err = b.runOut(p.Dir, p.ImportPath, nil, b.pkgconfigCmd(), "--libs", pkgs)
 		if err != nil {
-			b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out))
+			b.showOutput(p.Dir, b.pkgconfigCmd()+" --libs "+strings.Join(pkgs, " "), string(out))
 			b.print(err.Error() + "\n")
 			err = errPrintedOutput
 			return
@@ -1645,7 +1725,7 @@ func (b *builder) install(a *action) (err error) {
 	perm := os.FileMode(0666)
 	if a1.link {
 		switch buildBuildmode {
-		case "c-archive", "c-shared":
+		case "c-archive", "c-shared", "plugin":
 		default:
 			perm = 0777
 		}
@@ -2186,9 +2266,9 @@ type toolchain interface {
 	// cc runs the toolchain's C compiler in a directory on a C file
 	// to produce an output file.
 	cc(b *builder, p *Package, objdir, ofile, cfile string) error
-	// asm runs the assembler in a specific directory on a specific file
-	// to generate the named output file.
-	asm(b *builder, p *Package, obj, ofile, sfile string) error
+	// asm runs the assembler in a specific directory on specific files
+	// and returns a list of named output files.
+	asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error)
 	// pkgpath builds an appropriate path for a temporary package file.
 	pkgpath(basedir string, p *Package) string
 	// pack runs the archive packer in a specific directory to create
@@ -2225,8 +2305,8 @@ func (noToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool,
 	return "", nil, noCompiler()
 }
 
-func (noToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
-	return noCompiler()
+func (noToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) {
+	return nil, noCompiler()
 }
 
 func (noToolchain) pkgpath(basedir string, p *Package) string {
@@ -2323,10 +2403,10 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool,
 	return ofile, output, err
 }
 
-func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
+func (gcToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) {
 	// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
 	inc := filepath.Join(goroot, "pkg", "include")
-	sfile = mkAbs(p.Dir, sfile)
+	ofile := obj + "asm.o"
 	args := []interface{}{buildToolExec, tool("asm"), "-o", ofile, "-trimpath", b.work, "-I", obj, "-I", inc, "-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch, buildAsmflags}
 	if p.ImportPath == "runtime" && goarch == "386" {
 		for _, arg := range buildAsmflags {
@@ -2335,11 +2415,13 @@ func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
 			}
 		}
 	}
-	args = append(args, sfile)
+	for _, sfile := range sfiles {
+		args = append(args, mkAbs(p.Dir, sfile))
+	}
 	if err := b.run(p.Dir, p.ImportPath, nil, args...); err != nil {
-		return err
+		return nil, err
 	}
-	return nil
+	return []string{ofile}, nil
 }
 
 // toolVerify checks that the command line args writes the same output file
@@ -2497,6 +2579,13 @@ func (gcToolchain) ld(b *builder, root *action, out string, allactions []*action
 	if root.p.omitDWARF {
 		ldflags = append(ldflags, "-w")
 	}
+	if buildBuildmode == "plugin" {
+		pluginpath := root.p.ImportPath
+		if pluginpath == "command-line-arguments" {
+			pluginpath = "plugin/unnamed-" + root.p.buildID
+		}
+		ldflags = append(ldflags, "-pluginpath", pluginpath)
+	}
 
 	// If the user has not specified the -extld option, then specify the
 	// appropriate linker. In case of C++ code, use the compiler named
@@ -2606,15 +2695,24 @@ func (tools gccgoToolchain) gc(b *builder, p *Package, archive, obj string, asmh
 	return ofile, output, err
 }
 
-func (tools gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
-	sfile = mkAbs(p.Dir, sfile)
-	defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch}
-	if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
-		defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
+func (tools gccgoToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) {
+	var ofiles []string
+	for _, sfile := range sfiles {
+		ofile := obj + sfile[:len(sfile)-len(".s")] + ".o"
+		ofiles = append(ofiles, ofile)
+		sfile = mkAbs(p.Dir, sfile)
+		defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch}
+		if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
+			defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
+		}
+		defs = tools.maybePIC(defs)
+		defs = append(defs, b.gccArchArgs()...)
+		err := b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", obj, "-c", "-o", ofile, defs, sfile)
+		if err != nil {
+			return nil, err
+		}
 	}
-	defs = tools.maybePIC(defs)
-	defs = append(defs, b.gccArchArgs()...)
-	return b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", obj, "-c", "-o", ofile, defs, sfile)
+	return ofiles, nil
 }
 
 func (gccgoToolchain) pkgpath(basedir string, p *Package) string {
@@ -2736,7 +2834,7 @@ func (tools gccgoToolchain) link(b *builder, root *action, out string, allaction
 			if !apackagePathsSeen[a.p.ImportPath] {
 				apackagePathsSeen[a.p.ImportPath] = true
 				target := a.target
-				if len(a.p.CgoFiles) > 0 {
+				if len(a.p.CgoFiles) > 0 || a.p.usesSwig() {
 					target, err = readAndRemoveCgoFlags(target)
 					if err != nil {
 						return
@@ -2928,7 +3026,7 @@ func (tools gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile stri
 // maybePIC adds -fPIC to the list of arguments if needed.
 func (tools gccgoToolchain) maybePIC(args []string) []string {
 	switch buildBuildmode {
-	case "c-shared", "shared":
+	case "c-shared", "shared", "plugin":
 		args = append(args, "-fPIC")
 	}
 	return args
@@ -2969,9 +3067,19 @@ func (b *builder) gfortran(p *Package, out string, flags []string, ffile string)
 }
 
 // ccompile runs the given C or C++ compiler and creates an object from a single source file.
-func (b *builder) ccompile(p *Package, out string, flags []string, file string, compiler []string) error {
+func (b *builder) ccompile(p *Package, outfile string, flags []string, file string, compiler []string) error {
 	file = mkAbs(p.Dir, file)
-	return b.run(p.Dir, p.ImportPath, nil, compiler, flags, "-o", out, "-c", file)
+	desc := p.ImportPath
+	output, err := b.runOut(p.Dir, desc, nil, compiler, flags, "-o", outfile, "-c", file)
+	if len(output) > 0 {
+		b.showOutput(p.Dir, desc, b.processOutput(output))
+		if err != nil {
+			err = errPrintedOutput
+		} else if os.Getenv("GO_BUILDER_NAME") != "" {
+			return errors.New("C compiler warning promoted to error on Go builders")
+		}
+	}
+	return err
 }
 
 // gccld runs the gcc linker to create an executable from a set of object files.
@@ -3125,11 +3233,8 @@ func envList(key, def string) []string {
 }
 
 // Return the flags to use when invoking the C, C++ or Fortran compilers, or cgo.
-func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, fflags, ldflags []string) {
-	var defaults string
-	if def {
-		defaults = "-g -O2"
-	}
+func (b *builder) cflags(p *Package) (cppflags, cflags, cxxflags, fflags, ldflags []string) {
+	defaults := "-g -O2"
 
 	cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
 	cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
@@ -3141,9 +3246,9 @@ func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ffla
 
 var cgoRe = regexp.MustCompile(`[/\\:]`)
 
-func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles, ffiles []string) (outGo, outObj []string, err error) {
-	cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS := b.cflags(p, true)
-	_, cgoexeCFLAGS, _, _, _ := b.cflags(p, false)
+func (b *builder) cgo(a *action, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, objdirCgofiles, gccfiles, gxxfiles, mfiles, ffiles []string) (outGo, outObj []string, err error) {
+	p := a.p
+	cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoFFLAGS, cgoLDFLAGS := b.cflags(p)
 	cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
 	cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...)
 	// If we are compiling Objective-C code, then we need to link against libobjc
@@ -3164,7 +3269,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
 		}
 	}
 
-	if buildMSan && p.ImportPath != "runtime/cgo" {
+	if buildMSan {
 		cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...)
 		cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...)
 	}
@@ -3172,20 +3277,33 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
 	// Allows including _cgo_export.h from .[ch] files in the package.
 	cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj)
 
+	// If we have cgo files in the object directory, then copy any
+	// other cgo files into the object directory, and pass a
+	// -srcdir option to cgo.
+	var srcdirarg []string
+	if len(objdirCgofiles) > 0 {
+		for _, fn := range cgofiles {
+			if err := b.copyFile(a, obj+filepath.Base(fn), filepath.Join(p.Dir, fn), 0666, false); err != nil {
+				return nil, nil, err
+			}
+		}
+		cgofiles = append(cgofiles, objdirCgofiles...)
+		srcdirarg = []string{"-srcdir", obj}
+	}
+
 	// cgo
 	// TODO: CGO_FLAGS?
 	gofiles := []string{obj + "_cgo_gotypes.go"}
-	cfiles := []string{"_cgo_main.c", "_cgo_export.c"}
+	cfiles := []string{"_cgo_export.c"}
 	for _, fn := range cgofiles {
 		f := cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
 		gofiles = append(gofiles, obj+f+"cgo1.go")
 		cfiles = append(cfiles, f+"cgo2.c")
 	}
-	defunC := obj + "_cgo_defun.c"
 
-	cgoflags := []string{}
 	// TODO: make cgo not depend on $GOARCH?
 
+	cgoflags := []string{}
 	if p.Standard && p.ImportPath == "runtime/cgo" {
 		cgoflags = append(cgoflags, "-import_runtime_cgo=false")
 	}
@@ -3222,165 +3340,166 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
 		cgoflags = append(cgoflags, "-exportheader="+obj+"_cgo_install.h")
 	}
 
-	if err := b.run(p.Dir, p.ImportPath, cgoenv, buildToolExec, cgoExe, "-objdir", obj, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoexeCFLAGS, cgofiles); err != nil {
+	if err := b.run(p.Dir, p.ImportPath, cgoenv, buildToolExec, cgoExe, srcdirarg, "-objdir", obj, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, cgofiles); err != nil {
 		return nil, nil, err
 	}
 	outGo = append(outGo, gofiles...)
 
-	// cc _cgo_defun.c
-	_, gccgo := buildToolchain.(gccgoToolchain)
-	if gccgo {
-		defunObj := obj + "_cgo_defun.o"
-		if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil {
-			return nil, nil, err
-		}
-		outObj = append(outObj, defunObj)
-	}
-
 	// gcc
-	var linkobj []string
-
-	var bareLDFLAGS []string
-	// When linking relocatable objects, various flags need to be
-	// filtered out as they are inapplicable and can cause some linkers
-	// to fail.
-	for i := 0; i < len(cgoLDFLAGS); i++ {
-		f := cgoLDFLAGS[i]
-		switch {
-		// skip "-lc" or "-l somelib"
-		case strings.HasPrefix(f, "-l"):
-			if f == "-l" {
-				i++
-			}
-		// skip "-framework X" on Darwin
-		case goos == "darwin" && f == "-framework":
-			i++
-		// skip "*.{dylib,so,dll}"
-		case strings.HasSuffix(f, ".dylib"),
-			strings.HasSuffix(f, ".so"),
-			strings.HasSuffix(f, ".dll"):
-		// Remove any -fsanitize=foo flags.
-		// Otherwise the compiler driver thinks that we are doing final link
-		// and links sanitizer runtime into the object file. But we are not doing
-		// the final link, we will link the resulting object file again. And
-		// so the program ends up with two copies of sanitizer runtime.
-		// See issue 8788 for details.
-		case strings.HasPrefix(f, "-fsanitize="):
-			continue
-		// runpath flags not applicable unless building a shared
-		// object or executable; see issue 12115 for details. This
-		// is necessary as Go currently does not offer a way to
-		// specify the set of LDFLAGS that only apply to shared
-		// objects.
-		case strings.HasPrefix(f, "-Wl,-rpath"):
-			if f == "-Wl,-rpath" || f == "-Wl,-rpath-link" {
-				// Skip following argument to -rpath* too.
-				i++
-			}
-		default:
-			bareLDFLAGS = append(bareLDFLAGS, f)
-		}
-	}
-
-	var staticLibs []string
-	if goos == "windows" {
-		// libmingw32 and libmingwex have some inter-dependencies,
-		// so must use linker groups.
-		staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"}
-	}
-
 	cflags := stringList(cgoCPPFLAGS, cgoCFLAGS)
 	for _, cfile := range cfiles {
 		ofile := obj + cfile[:len(cfile)-1] + "o"
 		if err := b.gcc(p, ofile, cflags, obj+cfile); err != nil {
 			return nil, nil, err
 		}
-		linkobj = append(linkobj, ofile)
-		if !strings.HasSuffix(ofile, "_cgo_main.o") {
-			outObj = append(outObj, ofile)
-		}
+		outObj = append(outObj, ofile)
 	}
 
 	for _, file := range gccfiles {
-		ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o"
+		base := filepath.Base(file)
+		ofile := obj + cgoRe.ReplaceAllString(base[:len(base)-1], "_") + "o"
 		if err := b.gcc(p, ofile, cflags, file); err != nil {
 			return nil, nil, err
 		}
-		linkobj = append(linkobj, ofile)
 		outObj = append(outObj, ofile)
 	}
 
 	cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS)
 	for _, file := range gxxfiles {
 		// Append .o to the file, just in case the pkg has file.c and file.cpp
-		ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
+		ofile := obj + cgoRe.ReplaceAllString(filepath.Base(file), "_") + ".o"
 		if err := b.gxx(p, ofile, cxxflags, file); err != nil {
 			return nil, nil, err
 		}
-		linkobj = append(linkobj, ofile)
 		outObj = append(outObj, ofile)
 	}
 
 	for _, file := range mfiles {
 		// Append .o to the file, just in case the pkg has file.c and file.m
-		ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
+		ofile := obj + cgoRe.ReplaceAllString(filepath.Base(file), "_") + ".o"
 		if err := b.gcc(p, ofile, cflags, file); err != nil {
 			return nil, nil, err
 		}
-		linkobj = append(linkobj, ofile)
 		outObj = append(outObj, ofile)
 	}
 
 	fflags := stringList(cgoCPPFLAGS, cgoFFLAGS)
 	for _, file := range ffiles {
 		// Append .o to the file, just in case the pkg has file.c and file.f
-		ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
+		ofile := obj + cgoRe.ReplaceAllString(filepath.Base(file), "_") + ".o"
 		if err := b.gfortran(p, ofile, fflags, file); err != nil {
 			return nil, nil, err
 		}
-		linkobj = append(linkobj, ofile)
 		outObj = append(outObj, ofile)
 	}
 
-	linkobj = append(linkobj, p.SysoFiles...)
-	dynobj := obj + "_cgo_.o"
-	pie := (goarch == "arm" && goos == "linux") || goos == "android"
-	if pie { // we need to use -pie for Linux/ARM to get accurate imported sym
-		cgoLDFLAGS = append(cgoLDFLAGS, "-pie")
-	}
-	if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil {
-		return nil, nil, err
+	switch buildToolchain.(type) {
+	case gcToolchain:
+		importGo := obj + "_cgo_import.go"
+		if err := b.dynimport(p, obj, importGo, cgoExe, cflags, cgoLDFLAGS, outObj); err != nil {
+			return nil, nil, err
+		}
+		outGo = append(outGo, importGo)
+
+		ofile := obj + "_all.o"
+		if err := b.collect(p, obj, ofile, cgoLDFLAGS, outObj); err != nil {
+			return nil, nil, err
+		}
+		outObj = []string{ofile}
+
+	case gccgoToolchain:
+		defunC := obj + "_cgo_defun.c"
+		defunObj := obj + "_cgo_defun.o"
+		if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil {
+			return nil, nil, err
+		}
+		outObj = append(outObj, defunObj)
+
+	default:
+		noCompiler()
 	}
-	if pie { // but we don't need -pie for normal cgo programs
-		cgoLDFLAGS = cgoLDFLAGS[0 : len(cgoLDFLAGS)-1]
+
+	return outGo, outObj, nil
+}
+
+// dynimport creates a Go source file named importGo containing
+// //go:cgo_import_dynamic directives for each symbol or library
+// dynamically imported by the object files outObj.
+func (b *builder) dynimport(p *Package, obj, importGo, cgoExe string, cflags, cgoLDFLAGS, outObj []string) error {
+	cfile := obj + "_cgo_main.c"
+	ofile := obj + "_cgo_main.o"
+	if err := b.gcc(p, ofile, cflags, cfile); err != nil {
+		return err
 	}
 
-	if _, ok := buildToolchain.(gccgoToolchain); ok {
-		// we don't use dynimport when using gccgo.
-		return outGo, outObj, nil
+	linkobj := stringList(ofile, outObj, p.SysoFiles)
+	dynobj := obj + "_cgo_.o"
+
+	// we need to use -pie for Linux/ARM to get accurate imported sym
+	ldflags := cgoLDFLAGS
+	if (goarch == "arm" && goos == "linux") || goos == "android" {
+		ldflags = append(ldflags, "-pie")
+	}
+	if err := b.gccld(p, dynobj, ldflags, linkobj); err != nil {
+		return err
 	}
 
 	// cgo -dynimport
-	importGo := obj + "_cgo_import.go"
-	cgoflags = []string{}
+	var cgoflags []string
 	if p.Standard && p.ImportPath == "runtime/cgo" {
-		cgoflags = append(cgoflags, "-dynlinker") // record path to dynamic linker
+		cgoflags = []string{"-dynlinker"} // record path to dynamic linker
 	}
-	if err := b.run(p.Dir, p.ImportPath, nil, buildToolExec, cgoExe, "-objdir", obj, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags); err != nil {
-		return nil, nil, err
-	}
-	outGo = append(outGo, importGo)
+	return b.run(p.Dir, p.ImportPath, nil, buildToolExec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
+}
 
-	ofile := obj + "_all.o"
-	var gccObjs, nonGccObjs []string
-	for _, f := range outObj {
-		if strings.HasSuffix(f, ".o") {
-			gccObjs = append(gccObjs, f)
-		} else {
-			nonGccObjs = append(nonGccObjs, f)
+// collect partially links the object files outObj into a single
+// relocatable object file named ofile.
+func (b *builder) collect(p *Package, obj, ofile string, cgoLDFLAGS, outObj []string) error {
+	// When linking relocatable objects, various flags need to be
+	// filtered out as they are inapplicable and can cause some linkers
+	// to fail.
+	var ldflags []string
+	for i := 0; i < len(cgoLDFLAGS); i++ {
+		f := cgoLDFLAGS[i]
+		switch {
+		// skip "-lc" or "-l somelib"
+		case strings.HasPrefix(f, "-l"):
+			if f == "-l" {
+				i++
+			}
+		// skip "-framework X" on Darwin
+		case goos == "darwin" && f == "-framework":
+			i++
+		// skip "*.{dylib,so,dll,o,a}"
+		case strings.HasSuffix(f, ".dylib"),
+			strings.HasSuffix(f, ".so"),
+			strings.HasSuffix(f, ".dll"),
+			strings.HasSuffix(f, ".o"),
+			strings.HasSuffix(f, ".a"):
+		// Remove any -fsanitize=foo flags.
+		// Otherwise the compiler driver thinks that we are doing final link
+		// and links sanitizer runtime into the object file. But we are not doing
+		// the final link, we will link the resulting object file again. And
+		// so the program ends up with two copies of sanitizer runtime.
+		// See issue 8788 for details.
+		case strings.HasPrefix(f, "-fsanitize="):
+			continue
+		// runpath flags not applicable unless building a shared
+		// object or executable; see issue 12115 for details. This
+		// is necessary as Go currently does not offer a way to
+		// specify the set of LDFLAGS that only apply to shared
+		// objects.
+		case strings.HasPrefix(f, "-Wl,-rpath"):
+			if f == "-Wl,-rpath" || f == "-Wl,-rpath-link" {
+				// Skip following argument to -rpath* too.
+				i++
+			}
+		default:
+			ldflags = append(ldflags, f)
 		}
 	}
-	ldflags := stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs)
+
+	ldflags = append(ldflags, "-Wl,-r", "-nostdlib")
 
 	if b.gccSupportsNoPie() {
 		ldflags = append(ldflags, "-no-pie")
@@ -3389,16 +3508,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
 	// We are creating an object file, so we don't want a build ID.
 	ldflags = b.disableBuildID(ldflags)
 
-	if err := b.gccld(p, ofile, ldflags, gccObjs); err != nil {
-		return nil, nil, err
-	}
-
-	// NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows
-	// must be processed before the gcc-generated objects.
-	// Put it first.  https://golang.org/issue/2601
-	outObj = stringList(nonGccObjs, ofile)
-
-	return outGo, outObj, nil
+	return b.gccld(p, ofile, ldflags, outObj)
 }
 
 // Run SWIG on all SWIG input files.
@@ -3551,7 +3661,7 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) {
 
 // Run SWIG on one SWIG input file.
 func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outC string, err error) {
-	cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _ := b.cflags(p, true)
+	cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _, _ := b.cflags(p)
 	var cflags []string
 	if cxx {
 		cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS)
@@ -3614,7 +3724,7 @@ func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx b
 		b.showOutput(p.Dir, p.ImportPath, b.processOutput(out)) // swig warning
 	}
 
-	return obj + goFile, obj + gccBase + gccExt, nil
+	return goFile, obj + gccBase + gccExt, nil
 }
 
 // disableBuildID adjusts a linker command line to avoid creating a
diff --git a/src/cmd/go/build_test.go b/src/cmd/go/build_test.go
new file mode 100644
index 0000000..79bbd54
--- /dev/null
+++ b/src/cmd/go/build_test.go
@@ -0,0 +1,44 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"os"
+	"reflect"
+	"testing"
+)
+
+func TestRemoveDevNull(t *testing.T) {
+	fi, err := os.Lstat(os.DevNull)
+	if err != nil {
+		t.Skip(err)
+	}
+	if fi.Mode().IsRegular() {
+		t.Errorf("Lstat(%s).Mode().IsRegular() = true; expected false", os.DevNull)
+	}
+	mayberemovefile(os.DevNull)
+	_, err = os.Lstat(os.DevNull)
+	if err != nil {
+		t.Errorf("mayberemovefile(%s) did remove it; oops", os.DevNull)
+	}
+}
+
+func TestSplitPkgConfigOutput(t *testing.T) {
+	for _, test := range []struct {
+		in   []byte
+		want []string
+	}{
+		{[]byte(`-r:foo -L/usr/white\ space/lib -lfoo\ bar -lbar\ baz`), []string{"-r:foo", "-L/usr/white space/lib", "-lfoo bar", "-lbar baz"}},
+		{[]byte(`-lextra\ fun\ arg\\`), []string{`-lextra fun arg\`}},
+		{[]byte(`broken flag\`), []string{"broken", "flag"}},
+		{[]byte("\textra     whitespace\r\n"), []string{"extra", "whitespace"}},
+		{[]byte("     \r\n      "), nil},
+	} {
+		got := splitPkgConfigOutput(test.in)
+		if !reflect.DeepEqual(got, test.want) {
+			t.Errorf("splitPkgConfigOutput(%v) = %v; want %v", test.in, got, test.want)
+		}
+	}
+}
diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go
index 8aaaf46..31710b7 100644
--- a/src/cmd/go/env.go
+++ b/src/cmd/go/env.go
@@ -40,7 +40,7 @@ func mkEnv() []envVar {
 		{"GOHOSTARCH", runtime.GOARCH},
 		{"GOHOSTOS", runtime.GOOS},
 		{"GOOS", goos},
-		{"GOPATH", os.Getenv("GOPATH")},
+		{"GOPATH", buildContext.GOPATH},
 		{"GORACE", os.Getenv("GORACE")},
 		{"GOROOT", goroot},
 		{"GOTOOLDIR", toolDir},
@@ -49,14 +49,25 @@ func mkEnv() []envVar {
 		{"TERM", "dumb"},
 	}
 
-	if goos != "plan9" {
-		cmd := b.gccCmd(".")
-		env = append(env, envVar{"CC", cmd[0]})
-		env = append(env, envVar{"GOGCCFLAGS", strings.Join(cmd[3:], " ")})
-		cmd = b.gxxCmd(".")
-		env = append(env, envVar{"CXX", cmd[0]})
+	if gccgoBin != "" {
+		env = append(env, envVar{"GCCGO", gccgoBin})
+	} else {
+		env = append(env, envVar{"GCCGO", gccgoName})
+	}
+
+	switch goarch {
+	case "arm":
+		env = append(env, envVar{"GOARM", os.Getenv("GOARM")})
+	case "386":
+		env = append(env, envVar{"GO386", os.Getenv("GO386")})
 	}
 
+	cmd := b.gccCmd(".")
+	env = append(env, envVar{"CC", cmd[0]})
+	env = append(env, envVar{"GOGCCFLAGS", strings.Join(cmd[3:], " ")})
+	cmd = b.gxxCmd(".")
+	env = append(env, envVar{"CXX", cmd[0]})
+
 	if buildContext.CgoEnabled {
 		env = append(env, envVar{"CGO_ENABLED", "1"})
 	} else {
@@ -75,8 +86,24 @@ func findEnv(env []envVar, name string) string {
 	return ""
 }
 
+// extraEnvVars returns environment variables that should not leak into child processes.
+func extraEnvVars() []envVar {
+	var b builder
+	b.init()
+	cppflags, cflags, cxxflags, fflags, ldflags := b.cflags(&Package{})
+	return []envVar{
+		{"PKG_CONFIG", b.pkgconfigCmd()},
+		{"CGO_CFLAGS", strings.Join(cflags, " ")},
+		{"CGO_CPPFLAGS", strings.Join(cppflags, " ")},
+		{"CGO_CXXFLAGS", strings.Join(cxxflags, " ")},
+		{"CGO_FFLAGS", strings.Join(fflags, " ")},
+		{"CGO_LDFLAGS", strings.Join(ldflags, " ")},
+	}
+}
+
 func runEnv(cmd *Command, args []string) {
-	env := mkEnv()
+	env := newEnv
+	env = append(env, extraEnvVars()...)
 	if len(args) > 0 {
 		for _, name := range args {
 			fmt.Printf("%s\n", findEnv(env, name))
diff --git a/src/cmd/go/generate.go b/src/cmd/go/generate.go
index 3c6065e..2d92a0c 100644
--- a/src/cmd/go/generate.go
+++ b/src/cmd/go/generate.go
@@ -25,7 +25,7 @@ var cmdGenerate = &Command{
 	Long: `
 Generate runs commands described by directives within existing
 files. Those commands can run any process but the intent is to
-create or update Go source files, for instance by running yacc.
+create or update Go source files.
 
 Go generate is never run automatically by go build, go get, go test,
 and so on. It must be run explicitly.
@@ -88,10 +88,10 @@ string xxx represents the command identified by the arguments. This
 can be used to create aliases or to handle multiword generators.
 For example,
 
-	//go:generate -command yacc go tool yacc
+	//go:generate -command foo go tool foo
 
-specifies that the command "yacc" represents the generator
-"go tool yacc".
+specifies that the command "foo" represents the generator
+"go tool foo".
 
 Generate processes packages in the order given on the command line,
 one at a time. If the command line lists .go files, they are treated
@@ -136,6 +136,8 @@ func init() {
 }
 
 func runGenerate(cmd *Command, args []string) {
+	ignoreImports = true
+
 	if generateRunFlag != "" {
 		var err error
 		generateRunRE, err = regexp.Compile(generateRunFlag)
diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go
index 19858f7..82408d6 100644
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -43,11 +43,13 @@ The -u flag instructs get to use the network to update the named packages
 and their dependencies.  By default, get uses the network to check out
 missing packages but does not use it to look for updates to existing packages.
 
+The -v flag enables verbose progress and debug output.
+
 Get also accepts build flags to control the installation. See 'go help build'.
 
 When checking out a new package, get creates the target directory
 GOPATH/src/<import-path>. If the GOPATH contains multiple entries,
-get uses the first one. See 'go help gopath'.
+get uses the first one. For more details see: 'go help gopath'.
 
 When checking out or updating a package, get looks for a branch or tag
 that matches the locally installed version of Go. The most important
@@ -96,13 +98,31 @@ func runGet(cmd *Command, args []string) {
 		os.Setenv("GIT_TERMINAL_PROMPT", "0")
 	}
 
+	// Disable any ssh connection pooling by Git.
+	// If a Git subprocess forks a child into the background to cache a new connection,
+	// that child keeps stdout/stderr open. After the Git subprocess exits,
+	// os /exec expects to be able to read from the stdout/stderr pipe
+	// until EOF to get all the data that the Git subprocess wrote before exiting.
+	// The EOF doesn't come until the child exits too, because the child
+	// is holding the write end of the pipe.
+	// This is unfortunate, but it has come up at least twice
+	// (see golang.org/issue/13453 and golang.org/issue/16104)
+	// and confuses users when it does.
+	// If the user has explicitly set GIT_SSH or GIT_SSH_COMMAND,
+	// assume they know what they are doing and don't step on it.
+	// But default to turning off ControlMaster.
+	if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
+		os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no")
+	}
+
 	// Phase 1.  Download/update.
 	var stk importStack
 	mode := 0
 	if *getT {
 		mode |= getTestDeps
 	}
-	for _, arg := range downloadPaths(args) {
+	args = downloadPaths(args)
+	for _, arg := range args {
 		download(arg, nil, &stk, mode)
 	}
 	exitIfErrors()
@@ -137,7 +157,7 @@ func runGet(cmd *Command, args []string) {
 		return
 	}
 
-	runInstall(cmd, args)
+	installPackages(args, true)
 }
 
 // downloadPaths prepares the list of paths to pass to download.
@@ -177,7 +197,7 @@ var downloadCache = map[string]bool{}
 
 // downloadRootCache records the version control repository
 // root directories we have already considered during the download.
-// For example, all the packages in the code.google.com/p/codesearch repo
+// For example, all the packages in the github.com/google/codesearch repo
 // share the same root (the directory for that path), and we only need
 // to run the hg commands to consider each repository once.
 var downloadRootCache = map[string]bool{}
@@ -368,7 +388,7 @@ func downloadPackage(p *Package) error {
 							repo = resolved
 						}
 					}
-					if remote != repo && p.ImportComment != "" {
+					if remote != repo && rr.isCustom {
 						return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.root, repo, dir, remote)
 					}
 				}
@@ -391,12 +411,16 @@ func downloadPackage(p *Package) error {
 		// Package not found. Put in first directory of $GOPATH.
 		list := filepath.SplitList(buildContext.GOPATH)
 		if len(list) == 0 {
-			return fmt.Errorf("cannot download, $GOPATH not set. For more details see: go help gopath")
+			return fmt.Errorf("cannot download, $GOPATH not set. For more details see: 'go help gopath'")
 		}
 		// Guard against people setting GOPATH=$GOROOT.
 		if list[0] == goroot {
-			return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: go help gopath")
+			return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'")
+		}
+		if _, err := os.Stat(filepath.Join(list[0], "src/cmd/go/alldocs.go")); err == nil {
+			return fmt.Errorf("cannot download, %s is a GOROOT, not a GOPATH. For more details see: 'go help gopath'", list[0])
 		}
+		p.build.Root = list[0]
 		p.build.SrcRoot = filepath.Join(list[0], "src")
 		p.build.PkgRoot = filepath.Join(list[0], "pkg")
 	}
@@ -425,11 +449,19 @@ func downloadPackage(p *Package) error {
 		if _, err := os.Stat(root); err == nil {
 			return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta)
 		}
+
+		_, err := os.Stat(p.build.Root)
+		gopathExisted := err == nil
+
 		// Some version control tools require the parent of the target to exist.
 		parent, _ := filepath.Split(root)
 		if err = os.MkdirAll(parent, 0777); err != nil {
 			return err
 		}
+		if buildV && !gopathExisted && p.build.Root == buildContext.GOPATH {
+			fmt.Fprintf(os.Stderr, "created GOPATH=%s; see 'go help gopath'\n", p.build.Root)
+		}
+
 		if err = vcs.create(root, repo); err != nil {
 			return err
 		}
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index 66c6413..5731066 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -6,7 +6,6 @@ package main_test
 
 import (
 	"bytes"
-	"flag"
 	"fmt"
 	"go/build"
 	"go/format"
@@ -50,6 +49,17 @@ func init() {
 			// many linux/arm machines are too slow to run
 			// the full set of external tests.
 			skipExternal = true
+		case "mips", "mipsle", "mips64", "mips64le":
+			// Also slow.
+			skipExternal = true
+			if testenv.Builder() != "" {
+				// On the builders, skip the cmd/go
+				// tests. They're too slow and already
+				// covered by other ports. There's
+				// nothing os/arch specific in the
+				// tests.
+				canRun = false
+			}
 		}
 	case "freebsd":
 		switch runtime.GOARCH {
@@ -67,8 +77,6 @@ func init() {
 // The TestMain function creates a go command for testing purposes and
 // deletes it after the tests have been run.
 func TestMain(m *testing.M) {
-	flag.Parse()
-
 	if canRun {
 		args := []string{"build", "-tags", "testgo", "-o", "testgo" + exeSuffix}
 		if race.Enabled {
@@ -99,6 +107,14 @@ func TestMain(m *testing.M) {
 	// Don't let these environment variables confuse the test.
 	os.Unsetenv("GOBIN")
 	os.Unsetenv("GOPATH")
+	os.Unsetenv("GIT_ALLOW_PROTOCOL")
+	if home, ccacheDir := os.Getenv("HOME"), os.Getenv("CCACHE_DIR"); home != "" && ccacheDir == "" {
+		// On some systems the default C compiler is ccache.
+		// Setting HOME to a non-existent directory will break
+		// those systems.  Set CCACHE_DIR to cope.  Issue 17668.
+		os.Setenv("CCACHE_DIR", filepath.Join(home, ".ccache"))
+	}
+	os.Setenv("HOME", "/test-go-home-does-not-exist")
 
 	r := m.Run()
 
@@ -627,6 +643,7 @@ func TestProgramNameInCrashMessages(t *testing.T) {
 func TestBrokenTestsWithoutTestFunctionsAllFail(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	tg.runFail("test", "./testdata/src/badtest/...")
 	tg.grepBothNot("^ok", "test passed unexpectedly")
 	tg.grepBoth("FAIL.*badtest/badexec", "test did not run everything")
@@ -742,6 +759,7 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
 func TestGoListStandard(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	tg.cd(runtime.GOROOT() + "/src")
 	tg.run("list", "-f", "{{if not .Standard}}{{.ImportPath}}{{end}}", "./...")
 	stdout := tg.getStdout()
@@ -766,6 +784,7 @@ func TestGoListStandard(t *testing.T) {
 func TestGoInstallCleansUpAfterGoBuild(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	tg.tempFile("src/mycmd/main.go", `package main; func main(){}`)
 	tg.setenv("GOPATH", tg.path("."))
 	tg.cd(tg.path("src/mycmd"))
@@ -857,6 +876,7 @@ func TestGoInstallDetectsRemovedFiles(t *testing.T) {
 func TestWildcardMatchesSyntaxErrorDirs(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	tg.tempFile("src/mypkg/x.go", `package mypkg`)
 	tg.tempFile("src/mypkg/y.go", `pkg mypackage`)
 	tg.setenv("GOPATH", tg.path("."))
@@ -1013,6 +1033,7 @@ func copyBad(tg *testgoData) {
 func TestBadImportsEasy(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	copyBad(tg)
 	testLocalEasy(tg, badDirName)
 }
@@ -1042,14 +1063,14 @@ func TestInternalPackagesInGOROOTAreRespected(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
 	tg.runFail("build", "-v", "./testdata/testinternal")
-	tg.grepBoth("use of internal package not allowed", "wrong error message for testdata/testinternal")
+	tg.grepBoth(`testinternal(\/|\\)p\.go\:3\:8\: use of internal package not allowed`, "wrong error message for testdata/testinternal")
 }
 
 func TestInternalPackagesOutsideGOROOTAreRespected(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
 	tg.runFail("build", "-v", "./testdata/testinternal2")
-	tg.grepBoth("use of internal package not allowed", "wrote error message for testdata/testinternal2")
+	tg.grepBoth(`testinternal2(\/|\\)p\.go\:3\:8\: use of internal package not allowed`, "wrote error message for testdata/testinternal2")
 }
 
 func TestRunInternal(t *testing.T) {
@@ -1059,7 +1080,7 @@ func TestRunInternal(t *testing.T) {
 	tg.setenv("GOPATH", dir)
 	tg.run("run", filepath.Join(dir, "src/run/good.go"))
 	tg.runFail("run", filepath.Join(dir, "src/run/bad.go"))
-	tg.grepStderr("use of internal package not allowed", "unexpected error for run/bad.go")
+	tg.grepStderr(`testdata(\/|\\)src(\/|\\)run(\/|\\)bad\.go\:3\:8\: use of internal package not allowed`, "unexpected error for run/bad.go")
 }
 
 func testMove(t *testing.T, vcs, url, base, config string) {
@@ -1180,6 +1201,23 @@ func TestIssue10952(t *testing.T) {
 	tg.run("get", "-d", "-u", importPath)
 }
 
+func TestIssue16471(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+	if _, err := exec.LookPath("git"); err != nil {
+		t.Skip("skipping because git binary not found")
+	}
+
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.parallel()
+	tg.tempDir("src")
+	tg.setenv("GOPATH", tg.path("."))
+	tg.must(os.MkdirAll(tg.path("src/rsc.io/go-get-issue-10952"), 0755))
+	tg.runGit(tg.path("src/rsc.io"), "clone", "https://github.com/zombiezen/go-get-issue-10952")
+	tg.runFail("get", "-u", "rsc.io/go-get-issue-10952")
+	tg.grepStderr("rsc.io/go-get-issue-10952 is a custom import path for https://github.com/rsc/go-get-issue-10952, but .* is checked out from https://github.com/zombiezen/go-get-issue-10952", "did not detect updated import path")
+}
+
 // Test git clone URL that uses SCP-like syntax and custom import path checking.
 func TestIssue11457(t *testing.T) {
 	testenv.MustHaveExternalNetwork(t)
@@ -1192,7 +1230,7 @@ func TestIssue11457(t *testing.T) {
 	tg.parallel()
 	tg.tempDir("src")
 	tg.setenv("GOPATH", tg.path("."))
-	const importPath = "github.com/rsc/go-get-issue-11457"
+	const importPath = "rsc.io/go-get-issue-11457"
 	tg.run("get", "-d", "-u", importPath)
 	repoDir := tg.path("src/" + importPath)
 	tg.runGit(repoDir, "remote", "set-url", "origin", "git at github.com:rsc/go-get-issue-11457")
@@ -1267,11 +1305,23 @@ func TestRelativeImportsGoTestDashI(t *testing.T) {
 func TestRelativeImportsInCommandLinePackage(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	files, err := filepath.Glob("./testdata/testimport/*.go")
 	tg.must(err)
 	tg.run(append([]string{"test"}, files...)...)
 }
 
+func TestNonCanonicalImportPaths(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.parallel()
+	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
+	tg.runFail("build", "canonical/d")
+	tg.grepStderr("package canonical/d", "did not report canonical/d")
+	tg.grepStderr("imports canonical/b", "did not report canonical/b")
+	tg.grepStderr("imports canonical/a/: non-canonical", "did not report canonical/a/")
+}
+
 func TestVersionControlErrorMessageIncludesCorrectDirectory(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
@@ -1317,9 +1367,6 @@ func TestInstallIntoGOPATH(t *testing.T) {
 
 // Issue 12407
 func TestBuildOutputToDevNull(t *testing.T) {
-	if runtime.GOOS == "plan9" {
-		t.Skip("skipping because /dev/null is a regular file on plan9")
-	}
 	tg := testgo(t)
 	defer tg.cleanup()
 	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
@@ -1430,6 +1477,17 @@ func TestGoGetNonPkg(t *testing.T) {
 	tg.grepStderr("golang.org/x/tools: no buildable Go source files", "missing error")
 }
 
+func TestGoGetTestOnlyPkg(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.tempDir("gopath")
+	tg.setenv("GOPATH", tg.path("gopath"))
+	tg.run("get", "golang.org/x/tour/content")
+	tg.run("get", "-t", "golang.org/x/tour/content")
+}
+
 func TestInstalls(t *testing.T) {
 	if testing.Short() {
 		t.Skip("don't install into GOROOT in short mode")
@@ -1511,6 +1569,7 @@ func TestGoTestWithPackageListedMultipleTimes(t *testing.T) {
 func TestGoListHasAConsistentOrder(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	tg.parallel()
 	tg.run("list", "std")
 	first := tg.getStdout()
 	tg.run("list", "std")
@@ -1522,6 +1581,7 @@ func TestGoListHasAConsistentOrder(t *testing.T) {
 func TestGoListStdDoesNotIncludeCommands(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	tg.parallel()
 	tg.run("list", "std")
 	tg.grepStdoutNot("cmd/", "go list std shows commands")
 }
@@ -1529,6 +1589,7 @@ func TestGoListStdDoesNotIncludeCommands(t *testing.T) {
 func TestGoListCmdOnlyShowsCommands(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	tg.parallel()
 	tg.run("list", "cmd")
 	out := strings.TrimSpace(tg.getStdout())
 	for _, line := range strings.Split(out, "\n") {
@@ -1542,6 +1603,7 @@ func TestGoListCmdOnlyShowsCommands(t *testing.T) {
 func TestGoListDedupsPackages(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
 	tg.run("list", "xtestonly", "./testdata/src/xtestonly/...")
 	got := strings.TrimSpace(tg.getStdout())
@@ -1555,6 +1617,7 @@ func TestGoListDedupsPackages(t *testing.T) {
 func TestUnsuccessfulGoInstallShouldMentionMissingPackage(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	tg.parallel()
 	tg.runFail("install", "foo/quxx")
 	if tg.grepCountBoth(`cannot find package "foo/quxx" in any of`) != 1 {
 		t.Error(`go install foo/quxx expected error: .*cannot find package "foo/quxx" in any of`)
@@ -1564,6 +1627,7 @@ func TestUnsuccessfulGoInstallShouldMentionMissingPackage(t *testing.T) {
 func TestGOROOTSearchFailureReporting(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	tg.parallel()
 	tg.runFail("install", "foo/quxx")
 	if tg.grepCountBoth(regexp.QuoteMeta(filepath.Join("foo", "quxx"))+` \(from \$GOROOT\)$`) != 1 {
 		t.Error(`go install foo/quxx expected error: .*foo/quxx (from $GOROOT)`)
@@ -1573,6 +1637,7 @@ func TestGOROOTSearchFailureReporting(t *testing.T) {
 func TestMultipleGOPATHEntriesReportedSeparately(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	tg.parallel()
 	sep := string(filepath.ListSeparator)
 	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata", "a")+sep+filepath.Join(tg.pwd(), "testdata", "b"))
 	tg.runFail("install", "foo/quxx")
@@ -1585,6 +1650,7 @@ func TestMultipleGOPATHEntriesReportedSeparately(t *testing.T) {
 func TestMentionGOPATHInFirstGOPATHEntry(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	tg.parallel()
 	sep := string(filepath.ListSeparator)
 	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata", "a")+sep+filepath.Join(tg.pwd(), "testdata", "b"))
 	tg.runFail("install", "foo/quxx")
@@ -1597,6 +1663,7 @@ func TestMentionGOPATHInFirstGOPATHEntry(t *testing.T) {
 func TestMentionGOPATHNotOnSecondEntry(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	tg.parallel()
 	sep := string(filepath.ListSeparator)
 	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata", "a")+sep+filepath.Join(tg.pwd(), "testdata", "b"))
 	tg.runFail("install", "foo/quxx")
@@ -1605,14 +1672,150 @@ func TestMentionGOPATHNotOnSecondEntry(t *testing.T) {
 	}
 }
 
-// Test missing GOPATH is reported.
-func TestMissingGOPATHIsReported(t *testing.T) {
+func homeEnvName() string {
+	switch runtime.GOOS {
+	case "windows":
+		return "USERPROFILE"
+	case "plan9":
+		return "home"
+	default:
+		return "HOME"
+	}
+}
+
+// Test go env missing GOPATH shows default.
+func TestMissingGOPATHEnvShowsDefault(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	tg.parallel()
 	tg.setenv("GOPATH", "")
-	tg.runFail("install", "foo/quxx")
-	if tg.grepCountBoth(`\(\$GOPATH not set\)$`) != 1 {
-		t.Error(`go install foo/quxx expected error: ($GOPATH not set)`)
+	tg.run("env", "GOPATH")
+
+	want := filepath.Join(os.Getenv(homeEnvName()), "go")
+	got := strings.TrimSpace(tg.getStdout())
+	if got != want {
+		t.Errorf("got %q; want %q", got, want)
+	}
+}
+
+// Test go get missing GOPATH causes go get to warn if directory doesn't exist.
+func TestMissingGOPATHGetWarnsIfNotExists(t *testing.T) {
+	if _, err := exec.LookPath("git"); err != nil {
+		t.Skip("skipping because git binary not found")
+	}
+
+	tg := testgo(t)
+	defer tg.cleanup()
+
+	// setenv variables for test and defer deleting temporary home directory.
+	tg.setenv("GOPATH", "")
+	tmp, err := ioutil.TempDir("", "")
+	if err != nil {
+		t.Fatalf("could not create tmp home: %v", err)
+	}
+	defer os.RemoveAll(tmp)
+	tg.setenv(homeEnvName(), tmp)
+
+	tg.run("get", "-v", "github.com/golang/example/hello")
+
+	want := fmt.Sprintf("created GOPATH=%s; see 'go help gopath'", filepath.Join(tmp, "go"))
+	got := strings.TrimSpace(tg.getStderr())
+	if !strings.Contains(got, want) {
+		t.Errorf("got %q; want %q", got, want)
+	}
+}
+
+// Test go get missing GOPATH causes no warning if directory exists.
+func TestMissingGOPATHGetDoesntWarnIfExists(t *testing.T) {
+	if _, err := exec.LookPath("git"); err != nil {
+		t.Skip("skipping because git binary not found")
+	}
+
+	tg := testgo(t)
+	defer tg.cleanup()
+
+	// setenv variables for test and defer resetting them.
+	tg.setenv("GOPATH", "")
+	tmp, err := ioutil.TempDir("", "")
+	if err != nil {
+		t.Fatalf("could not create tmp home: %v", err)
+	}
+	defer os.RemoveAll(tmp)
+	if err := os.Mkdir(filepath.Join(tmp, "go"), 0777); err != nil {
+		t.Fatalf("could not create $HOME/go: %v", err)
+	}
+
+	tg.setenv(homeEnvName(), tmp)
+
+	tg.run("get", "github.com/golang/example/hello")
+
+	got := strings.TrimSpace(tg.getStderr())
+	if got != "" {
+		t.Errorf("got %q; wants empty", got)
+	}
+}
+
+// Test go get missing GOPATH fails if pointed file is not a directory.
+func TestMissingGOPATHGetFailsIfItsNotDirectory(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+
+	// setenv variables for test and defer resetting them.
+	tg.setenv("GOPATH", "")
+	tmp, err := ioutil.TempDir("", "")
+	if err != nil {
+		t.Fatalf("could not create tmp home: %v", err)
+	}
+	defer os.RemoveAll(tmp)
+
+	path := filepath.Join(tmp, "go")
+	if err := ioutil.WriteFile(path, nil, 0777); err != nil {
+		t.Fatalf("could not create GOPATH at %s: %v", path, err)
+	}
+	tg.setenv(homeEnvName(), tmp)
+
+	const pkg = "github.com/golang/example/hello"
+	tg.runFail("get", pkg)
+
+	msg := "not a directory"
+	if runtime.GOOS == "windows" {
+		msg = "The system cannot find the path specified."
+	}
+	want := fmt.Sprintf("package %s: mkdir %s: %s", pkg, filepath.Join(tmp, "go"), msg)
+	got := strings.TrimSpace(tg.getStderr())
+	if got != want {
+		t.Errorf("got %q; wants %q", got, want)
+	}
+}
+
+// Test go install of missing package when missing GOPATH fails and shows default GOPATH.
+func TestMissingGOPATHInstallMissingPackageFailsAndShowsDefault(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+
+	// setenv variables for test and defer resetting them.
+	tg.setenv("GOPATH", "")
+	tmp, err := ioutil.TempDir("", "")
+	if err != nil {
+		t.Fatalf("could not create tmp home: %v", err)
+	}
+	defer os.RemoveAll(tmp)
+	if err := os.Mkdir(filepath.Join(tmp, "go"), 0777); err != nil {
+		t.Fatalf("could not create $HOME/go: %v", err)
+	}
+	tg.setenv(homeEnvName(), tmp)
+
+	const pkg = "github.com/golang/example/hello"
+	tg.runFail("install", pkg)
+
+	pkgPath := filepath.Join(strings.Split(pkg, "/")...)
+	want := fmt.Sprintf("can't load package: package %s: cannot find package \"%s\" in any of:", pkg, pkg) +
+		fmt.Sprintf("\n\t%s (from $GOROOT)", filepath.Join(runtime.GOROOT(), "src", pkgPath)) +
+		fmt.Sprintf("\n\t%s (from $GOPATH)", filepath.Join(tmp, "go", "src", pkgPath))
+
+	got := strings.TrimSpace(tg.getStderr())
+	if got != want {
+		t.Errorf("got %q; wants %q", got, want)
 	}
 }
 
@@ -1659,6 +1862,7 @@ func TestLdflagsArgumentsWithSpacesIssue3941(t *testing.T) {
 func TestGoTestCpuprofileLeavesBinaryBehind(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	tg.makeTempdir()
 	tg.cd(tg.path("."))
 	tg.run("test", "-cpuprofile", "errors.prof", "errors")
@@ -1668,6 +1872,7 @@ func TestGoTestCpuprofileLeavesBinaryBehind(t *testing.T) {
 func TestGoTestCpuprofileDashOControlsBinaryLocation(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	tg.makeTempdir()
 	tg.cd(tg.path("."))
 	tg.run("test", "-cpuprofile", "errors.prof", "-o", "myerrors.test"+exeSuffix, "errors")
@@ -1692,6 +1897,16 @@ func TestGoTestDashOWritesBinary(t *testing.T) {
 	tg.wantExecutable(tg.path("myerrors.test"+exeSuffix), "go test -o myerrors.test did not create myerrors.test")
 }
 
+func TestGoTestDashIDashOWritesBinary(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.parallel()
+	tg.makeTempdir()
+	tg.run("test", "-v", "-i", "-o", tg.path("myerrors.test"+exeSuffix), "errors")
+	tg.grepBothNot("PASS|FAIL", "test should not have run")
+	tg.wantExecutable(tg.path("myerrors.test"+exeSuffix), "go test -o myerrors.test did not create myerrors.test")
+}
+
 // Issue 4568.
 func TestSymlinksList(t *testing.T) {
 	switch runtime.GOOS {
@@ -1701,6 +1916,7 @@ func TestSymlinksList(t *testing.T) {
 
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	tg.tempDir("src")
 	tg.must(os.Symlink(tg.path("."), tg.path("src/dir1")))
 	tg.tempFile("src/dir1/p.go", "package p")
@@ -1721,6 +1937,7 @@ func TestSymlinksVendor(t *testing.T) {
 
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	tg.tempDir("gopath/src/dir1/vendor/v")
 	tg.tempFile("gopath/src/dir1/p.go", "package main\nimport _ `v`\nfunc main(){}")
 	tg.tempFile("gopath/src/dir1/vendor/v/v.go", "package v")
@@ -1738,6 +1955,27 @@ func TestSymlinksVendor(t *testing.T) {
 	tg.run("install")
 }
 
+// Issue 15201.
+func TestSymlinksVendor15201(t *testing.T) {
+	switch runtime.GOOS {
+	case "plan9", "windows":
+		t.Skipf("skipping symlink test on %s", runtime.GOOS)
+	}
+
+	tg := testgo(t)
+	defer tg.cleanup()
+
+	tg.tempDir("gopath/src/x/y/_vendor/src/x")
+	tg.must(os.Symlink("../../..", tg.path("gopath/src/x/y/_vendor/src/x/y")))
+	tg.tempFile("gopath/src/x/y/w/w.go", "package w\nimport \"x/y/z\"\n")
+	tg.must(os.Symlink("../_vendor/src", tg.path("gopath/src/x/y/w/vendor")))
+	tg.tempFile("gopath/src/x/y/z/z.go", "package z\n")
+
+	tg.setenv("GOPATH", tg.path("gopath/src/x/y/_vendor")+string(filepath.ListSeparator)+tg.path("gopath"))
+	tg.cd(tg.path("gopath/src"))
+	tg.run("list", "./...")
+}
+
 func TestSymlinksInternal(t *testing.T) {
 	switch runtime.GOOS {
 	case "plan9", "windows":
@@ -1840,7 +2078,7 @@ func TestGoGetDashTIssue8181(t *testing.T) {
 	tg.setenv("GOPATH", tg.path("."))
 	tg.run("get", "-v", "-t", "github.com/rsc/go-get-issue-8181/a", "github.com/rsc/go-get-issue-8181/b")
 	tg.run("list", "...")
-	tg.grepStdout("x/build/cmd/cl", "missing expected x/build/cmd/cl")
+	tg.grepStdout("x/build/gerrit", "missing expected x/build/gerrit")
 }
 
 func TestIssue11307(t *testing.T) {
@@ -1997,6 +2235,16 @@ func TestCoverageUsesActualSettingToOverrideEvenForRace(t *testing.T) {
 	checkCoverage(tg, data)
 }
 
+func TestCoverageImportMainLoop(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
+	tg.runFail("test", "importmain/test")
+	tg.grepStderr("not an importable package", "did not detect import main")
+	tg.runFail("test", "-cover", "importmain/test")
+	tg.grepStderr("not an importable package", "did not detect import main")
+}
+
 func TestBuildDryRunWithCgo(t *testing.T) {
 	if !canCgo {
 		t.Skip("skipping because cgo not enabled")
@@ -2023,11 +2271,17 @@ func TestCoverageWithCgo(t *testing.T) {
 		t.Skip("skipping because cgo not enabled")
 	}
 
-	tg := testgo(t)
-	defer tg.cleanup()
-	tg.run("test", "-short", "-cover", "./testdata/cgocover")
-	data := tg.getStdout() + tg.getStderr()
-	checkCoverage(tg, data)
+	for _, dir := range []string{"cgocover", "cgocover2", "cgocover3", "cgocover4"} {
+		t.Run(dir, func(t *testing.T) {
+			tg := testgo(t)
+			tg.parallel()
+			defer tg.cleanup()
+			tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
+			tg.run("test", "-short", "-cover", dir)
+			data := tg.getStdout() + tg.getStderr()
+			checkCoverage(tg, data)
+		})
+	}
 }
 
 func TestCgoDependsOnSyscall(t *testing.T) {
@@ -2090,10 +2344,57 @@ func TestCgoHandlesWlORIGIN(t *testing.T) {
 	tg.run("build", "origin")
 }
 
+func TestCgoPkgConfig(t *testing.T) {
+	if !canCgo {
+		t.Skip("skipping because cgo not enabled")
+	}
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.parallel()
+
+	tg.run("env", "PKG_CONFIG")
+	pkgConfig := strings.TrimSpace(tg.getStdout())
+	if out, err := exec.Command(pkgConfig, "--atleast-pkgconfig-version", "0.24").CombinedOutput(); err != nil {
+		t.Skipf("%s --atleast-pkgconfig-version 0.24: %v\n%s", pkgConfig, err, out)
+	}
+
+	// OpenBSD's pkg-config is strict about whitespace and only
+	// supports backslash-escaped whitespace. It does not support
+	// quotes, which the normal freedesktop.org pkg-config does
+	// support. See http://man.openbsd.org/pkg-config.1
+	tg.tempFile("foo.pc", `
+Name: foo
+Description: The foo library
+Version: 1.0.0
+Cflags: -Dhello=10 -Dworld=+32 -DDEFINED_FROM_PKG_CONFIG=hello\ world
+`)
+	tg.tempFile("foo.go", `package main
+
+/*
+#cgo pkg-config: foo
+int value() {
+	return DEFINED_FROM_PKG_CONFIG;
+}
+*/
+import "C"
+import "os"
+
+func main() {
+	if C.value() != 42 {
+		println("value() =", C.value(), "wanted 42")
+		os.Exit(1)
+	}
+}
+`)
+	tg.setenv("PKG_CONFIG_PATH", tg.path("."))
+	tg.run("run", tg.path("foo.go"))
+}
+
 // "go test -c -test.bench=XXX errors" should not hang
 func TestIssue6480(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	// TODO: tg.parallel()
 	tg.makeTempdir()
 	tg.cd(tg.path("."))
 	tg.run("test", "-c", "-test.bench=XXX", "errors")
@@ -2124,8 +2425,7 @@ func main() { C.f() }`)
 }
 
 func TestListTemplateContextFunction(t *testing.T) {
-	tg := testgo(t)
-	defer tg.cleanup()
+	t.Parallel()
 	for _, tt := range []struct {
 		v    string
 		want string
@@ -2141,14 +2441,20 @@ func TestListTemplateContextFunction(t *testing.T) {
 		{"ReleaseTags", ""},
 		{"InstallSuffix", ""},
 	} {
-		tmpl := "{{context." + tt.v + "}}"
-		tg.run("list", "-f", tmpl)
-		if tt.want == "" {
-			continue
-		}
-		if got := strings.TrimSpace(tg.getStdout()); got != tt.want {
-			t.Errorf("go list -f %q: got %q; want %q", tmpl, got, tt.want)
-		}
+		tt := tt
+		t.Run(tt.v, func(t *testing.T) {
+			tg := testgo(t)
+			tg.parallel()
+			defer tg.cleanup()
+			tmpl := "{{context." + tt.v + "}}"
+			tg.run("list", "-f", tmpl)
+			if tt.want == "" {
+				return
+			}
+			if got := strings.TrimSpace(tg.getStdout()); got != tt.want {
+				t.Errorf("go list -f %q: got %q; want %q", tmpl, got, tt.want)
+			}
+		})
 	}
 }
 
@@ -2307,6 +2613,20 @@ func TestGoGenerateEnv(t *testing.T) {
 	}
 }
 
+func TestGoGenerateBadImports(t *testing.T) {
+	if runtime.GOOS == "windows" {
+		t.Skip("skipping because windows has no echo command")
+	}
+
+	// This package has an invalid import causing an import cycle,
+	// but go generate is supposed to still run.
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
+	tg.run("generate", "gencycle")
+	tg.grepStdout("hello world", "go generate gencycle did not run generator")
+}
+
 func TestGoGetCustomDomainWildcard(t *testing.T) {
 	testenv.MustHaveExternalNetwork(t)
 
@@ -2382,35 +2702,254 @@ func TestGoGetHTTPS404(t *testing.T) {
 }
 
 // Test that you cannot import a main package.
-func TestIssue4210(t *testing.T) {
+// See golang.org/issue/4210 and golang.org/issue/17475.
+func TestImportMain(t *testing.T) {
 	tg := testgo(t)
+	tg.parallel()
 	defer tg.cleanup()
+
+	// Importing package main from that package main's test should work.
 	tg.tempFile("src/x/main.go", `package main
 		var X int
 		func main() {}`)
-	tg.tempFile("src/y/main.go", `package main
-		import "fmt"
+	tg.tempFile("src/x/main_test.go", `package main_test
 		import xmain "x"
-		func main() {
-			fmt.Println(xmain.X)
-		}`)
+		import "testing"
+		var _ = xmain.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.setenv("GOPATH", tg.path("."))
+	tg.creatingTemp("x")
+	tg.run("build", "x")
+	tg.run("test", "x")
+
+	// Importing package main from another package should fail.
+	tg.tempFile("src/p1/p.go", `package p1
+		import xmain "x"
+		var _ = xmain.X
+	`)
+	tg.runFail("build", "p1")
+	tg.grepStderr("import \"x\" is a program, not an importable package", "did not diagnose package main")
+
+	// ... even in that package's test.
+	tg.tempFile("src/p2/p.go", `package p2
+	`)
+	tg.tempFile("src/p2/p_test.go", `package p2
+		import xmain "x"
+		import "testing"
+		var _ = xmain.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.run("build", "p2")
+	tg.runFail("test", "p2")
+	tg.grepStderr("import \"x\" is a program, not an importable package", "did not diagnose package main")
+
+	// ... even if that package's test is an xtest.
+	tg.tempFile("src/p3/p.go", `package p
+	`)
+	tg.tempFile("src/p3/p_test.go", `package p_test
+		import xmain "x"
+		import "testing"
+		var _ = xmain.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.run("build", "p3")
+	tg.runFail("test", "p3")
+	tg.grepStderr("import \"x\" is a program, not an importable package", "did not diagnose package main")
+
+	// ... even if that package is a package main
+	tg.tempFile("src/p4/p.go", `package main
+	func main() {}
+	`)
+	tg.tempFile("src/p4/p_test.go", `package main
+		import xmain "x"
+		import "testing"
+		var _ = xmain.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.creatingTemp("p4" + exeSuffix)
+	tg.run("build", "p4")
+	tg.runFail("test", "p4")
+	tg.grepStderr("import \"x\" is a program, not an importable package", "did not diagnose package main")
+
+	// ... even if that package is a package main using an xtest.
+	tg.tempFile("src/p5/p.go", `package main
+	func main() {}
+	`)
+	tg.tempFile("src/p5/p_test.go", `package main_test
+		import xmain "x"
+		import "testing"
+		var _ = xmain.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.creatingTemp("p5" + exeSuffix)
+	tg.run("build", "p5")
+	tg.runFail("test", "p5")
+	tg.grepStderr("import \"x\" is a program, not an importable package", "did not diagnose package main")
+}
+
+// Test that you cannot use a local import in a package
+// accessed by a non-local import (found in a GOPATH/GOROOT).
+// See golang.org/issue/17475.
+func TestImportLocal(t *testing.T) {
+	tg := testgo(t)
+	tg.parallel()
+	defer tg.cleanup()
+
+	tg.tempFile("src/dir/x/x.go", `package x
+		var X int
+	`)
 	tg.setenv("GOPATH", tg.path("."))
-	tg.runFail("build", "y")
-	tg.grepBoth("is a program", `did not find expected error message ("is a program")`)
+	tg.run("build", "dir/x")
+
+	// Ordinary import should work.
+	tg.tempFile("src/dir/p0/p.go", `package p0
+		import "dir/x"
+		var _ = x.X
+	`)
+	tg.run("build", "dir/p0")
+
+	// Relative import should not.
+	tg.tempFile("src/dir/p1/p.go", `package p1
+		import "../x"
+		var _ = x.X
+	`)
+	tg.runFail("build", "dir/p1")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// ... even in a test.
+	tg.tempFile("src/dir/p2/p.go", `package p2
+	`)
+	tg.tempFile("src/dir/p2/p_test.go", `package p2
+		import "../x"
+		import "testing"
+		var _ = x.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.run("build", "dir/p2")
+	tg.runFail("test", "dir/p2")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// ... even in an xtest.
+	tg.tempFile("src/dir/p2/p_test.go", `package p2_test
+		import "../x"
+		import "testing"
+		var _ = x.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.run("build", "dir/p2")
+	tg.runFail("test", "dir/p2")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// Relative import starting with ./ should not work either.
+	tg.tempFile("src/dir/d.go", `package dir
+		import "./x"
+		var _ = x.X
+	`)
+	tg.runFail("build", "dir")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// ... even in a test.
+	tg.tempFile("src/dir/d.go", `package dir
+	`)
+	tg.tempFile("src/dir/d_test.go", `package dir
+		import "./x"
+		import "testing"
+		var _ = x.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.run("build", "dir")
+	tg.runFail("test", "dir")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// ... even in an xtest.
+	tg.tempFile("src/dir/d_test.go", `package dir_test
+		import "./x"
+		import "testing"
+		var _ = x.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.run("build", "dir")
+	tg.runFail("test", "dir")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// Relative import plain ".." should not work.
+	tg.tempFile("src/dir/x/y/y.go", `package dir
+		import ".."
+		var _ = x.X
+	`)
+	tg.runFail("build", "dir/x/y")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// ... even in a test.
+	tg.tempFile("src/dir/x/y/y.go", `package y
+	`)
+	tg.tempFile("src/dir/x/y/y_test.go", `package y
+		import ".."
+		import "testing"
+		var _ = x.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.run("build", "dir/x/y")
+	tg.runFail("test", "dir/x/y")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// ... even in an x test.
+	tg.tempFile("src/dir/x/y/y_test.go", `package y_test
+		import ".."
+		import "testing"
+		var _ = x.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.run("build", "dir/x/y")
+	tg.runFail("test", "dir/x/y")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// Relative import "." should not work.
+	tg.tempFile("src/dir/x/xx.go", `package x
+		import "."
+		var _ = x.X
+	`)
+	tg.runFail("build", "dir/x")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// ... even in a test.
+	tg.tempFile("src/dir/x/xx.go", `package x
+	`)
+	tg.tempFile("src/dir/x/xx_test.go", `package x
+		import "."
+		import "testing"
+		var _ = x.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.run("build", "dir/x")
+	tg.runFail("test", "dir/x")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
+
+	// ... even in an xtest.
+	tg.tempFile("src/dir/x/xx.go", `package x
+	`)
+	tg.tempFile("src/dir/x/xx_test.go", `package x_test
+		import "."
+		import "testing"
+		var _ = x.X
+		func TestFoo(t *testing.T) {}
+	`)
+	tg.run("build", "dir/x")
+	tg.runFail("test", "dir/x")
+	tg.grepStderr("local import.*in non-local package", "did not diagnose local import")
 }
 
 func TestGoGetInsecure(t *testing.T) {
 	testenv.MustHaveExternalNetwork(t)
 
-	t.Skip("golang.org/issue/15410")
-
 	tg := testgo(t)
 	defer tg.cleanup()
 	tg.makeTempdir()
 	tg.setenv("GOPATH", tg.path("."))
 	tg.failSSH()
 
-	const repo = "wh3rd.net/git.git"
+	const repo = "insecure.go-get-issue-15410.appspot.com/pkg/p"
 
 	// Try go get -d of HTTP-only repo (should fail).
 	tg.runFail("get", "-d", repo)
@@ -2454,7 +2993,7 @@ func TestGoGetInsecureCustomDomain(t *testing.T) {
 	tg.makeTempdir()
 	tg.setenv("GOPATH", tg.path("."))
 
-	const repo = "wh3rd.net/repo"
+	const repo = "insecure.go-get-issue-15410.appspot.com/pkg/p"
 	tg.runFail("get", "-d", repo)
 	tg.run("get", "-d", "-insecure", repo)
 }
@@ -2471,6 +3010,7 @@ func TestGoRunDirs(t *testing.T) {
 
 func TestGoInstallPkgdir(t *testing.T) {
 	tg := testgo(t)
+	tg.parallel()
 	defer tg.cleanup()
 	tg.makeTempdir()
 	pkg := tg.path(".")
@@ -2509,6 +3049,27 @@ func TestGoTestRaceInstallCgo(t *testing.T) {
 	}
 }
 
+func TestGoTestRaceFailures(t *testing.T) {
+	if !canRace {
+		t.Skip("skipping because race detector not supported")
+	}
+
+	tg := testgo(t)
+	tg.parallel()
+	defer tg.cleanup()
+	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
+
+	tg.run("test", "testrace")
+
+	tg.runFail("test", "-race", "testrace")
+	tg.grepStdout("FAIL: TestRace", "TestRace did not fail")
+	tg.grepBothNot("PASS", "something passed")
+
+	tg.runFail("test", "-race", "testrace", "-run", "XXX", "-bench", ".")
+	tg.grepStdout("FAIL: BenchmarkRace", "BenchmarkRace did not fail")
+	tg.grepBothNot("PASS", "something passed")
+}
+
 func TestGoTestImportErrorStack(t *testing.T) {
 	const out = `package testdep/p1 (test)
 	imports testdep/p2
@@ -2773,6 +3334,7 @@ func TestIssue13655(t *testing.T) {
 // For issue 14337.
 func TestParallelTest(t *testing.T) {
 	tg := testgo(t)
+	tg.parallel()
 	defer tg.cleanup()
 	tg.makeTempdir()
 	const testSrc = `package package_test
@@ -2832,10 +3394,23 @@ func TestGoGetUpdateAllDoesNotTryToLoadDuplicates(t *testing.T) {
 	tg.grepStderrNot("duplicate loads of", "did not remove old packages from cache")
 }
 
+// Issue 17119 more duplicate load errors
+func TestIssue17119(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.parallel()
+	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
+	tg.runFail("build", "dupload")
+	tg.grepBothNot("duplicate load|internal error", "internal error")
+}
+
 func TestFatalInBenchmarkCauseNonZeroExitStatus(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
-	tg.runFail("test", "-bench", ".", "./testdata/src/benchfatal")
+	// TODO: tg.parallel()
+	tg.runFail("test", "-run", "^$", "-bench", ".", "./testdata/src/benchfatal")
 	tg.grepBothNot("^ok", "test passed unexpectedly")
 	tg.grepBoth("FAIL.*benchfatal", "test did not run everything")
 }
@@ -2843,6 +3418,7 @@ func TestFatalInBenchmarkCauseNonZeroExitStatus(t *testing.T) {
 func TestBinaryOnlyPackages(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
+	tg.parallel()
 	tg.makeTempdir()
 	tg.setenv("GOPATH", tg.path("."))
 
@@ -2907,6 +3483,16 @@ func TestBinaryOnlyPackages(t *testing.T) {
 
 	tg.run("run", tg.path("src/p3/p3.go"))
 	tg.grepStdout("hello from p1", "did not see message from p1")
+
+	tg.tempFile("src/p4/p4.go", `package main`)
+	tg.tempFile("src/p4/p4not.go", `//go:binary-only-package
+
+		// +build asdf
+
+		package main
+	`)
+	tg.run("list", "-f", "{{.BinaryOnly}}", "p4")
+	tg.grepStdout("false", "did not see BinaryOnly=false for p4")
 }
 
 // Issue 16050.
@@ -2954,3 +3540,179 @@ func TestGenerateUsesBuildContext(t *testing.T) {
 	tg.run("generate", "gen")
 	tg.grepStdout("darwin 386", "unexpected GOOS/GOARCH combination")
 }
+
+// Issue 14450: go get -u .../ tried to import not downloaded package
+func TestGoGetUpdateWithWildcard(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.parallel()
+	tg.makeTempdir()
+	tg.setenv("GOPATH", tg.path("."))
+	const aPkgImportPath = "github.com/tmwh/go-get-issue-14450/a"
+	tg.run("get", aPkgImportPath)
+	tg.run("get", "-u", ".../")
+	tg.grepStderrNot("cannot find package", "did not update packages given wildcard path")
+
+	var expectedPkgPaths = []string{
+		"src/github.com/tmwh/go-get-issue-14450/b",
+		"src/github.com/tmwh/go-get-issue-14450-b-dependency/c",
+		"src/github.com/tmwh/go-get-issue-14450-b-dependency/d",
+	}
+
+	for _, importPath := range expectedPkgPaths {
+		_, err := os.Stat(tg.path(importPath))
+		tg.must(err)
+	}
+	const notExpectedPkgPath = "src/github.com/tmwh/go-get-issue-14450-c-dependency/e"
+	tg.mustNotExist(tg.path(notExpectedPkgPath))
+}
+
+func TestGoEnv(t *testing.T) {
+	tg := testgo(t)
+	tg.parallel()
+	defer tg.cleanup()
+	tg.setenv("GOARCH", "arm")
+	tg.run("env", "GOARCH")
+	tg.grepStdout("^arm$", "GOARCH not honored")
+
+	tg.run("env", "GCCGO")
+	tg.grepStdout(".", "GCCGO unexpectedly empty")
+
+	tg.run("env", "CGO_CFLAGS")
+	tg.grepStdout(".", "default CGO_CFLAGS unexpectedly empty")
+
+	tg.setenv("CGO_CFLAGS", "-foobar")
+	tg.run("env", "CGO_CFLAGS")
+	tg.grepStdout("^-foobar$", "CGO_CFLAGS not honored")
+
+	tg.setenv("CC", "gcc -fmust -fgo -ffaster")
+	tg.run("env", "CC")
+	tg.grepStdout("gcc", "CC not found")
+	tg.run("env", "GOGCCFLAGS")
+	tg.grepStdout("-ffaster", "CC arguments not found")
+}
+
+const (
+	noMatchesPattern = `(?m)^ok.*\[no tests to run\]`
+	okPattern        = `(?m)^ok`
+)
+
+func TestMatchesNoTests(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	// TODO: tg.parallel()
+	tg.run("test", "-run", "ThisWillNotMatch", "testdata/standalone_test.go")
+	tg.grepBoth(noMatchesPattern, "go test did not say [no tests to run]")
+}
+
+func TestMatchesNoTestsDoesNotOverrideBuildFailure(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.parallel()
+	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
+	tg.runFail("test", "-run", "ThisWillNotMatch", "syntaxerror")
+	tg.grepBothNot(noMatchesPattern, "go test did say [no tests to run]")
+	tg.grepBoth("FAIL", "go test did not say FAIL")
+}
+
+func TestMatchesNoBenchmarksIsOK(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	// TODO: tg.parallel()
+	tg.run("test", "-run", "^$", "-bench", "ThisWillNotMatch", "testdata/standalone_benchmark_test.go")
+	tg.grepBothNot(noMatchesPattern, "go test did say [no tests to run]")
+	tg.grepBoth(okPattern, "go test did not say ok")
+}
+
+func TestMatchesOnlyExampleIsOK(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	// TODO: tg.parallel()
+	tg.run("test", "-run", "Example", "testdata/example1_test.go")
+	tg.grepBothNot(noMatchesPattern, "go test did say [no tests to run]")
+	tg.grepBoth(okPattern, "go test did not say ok")
+}
+
+func TestMatchesOnlyBenchmarkIsOK(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	// TODO: tg.parallel()
+	tg.run("test", "-run", "^$", "-bench", ".", "testdata/standalone_benchmark_test.go")
+	tg.grepBothNot(noMatchesPattern, "go test did say [no tests to run]")
+	tg.grepBoth(okPattern, "go test did not say ok")
+}
+
+func TestMatchesOnlyTestIsOK(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	// TODO: tg.parallel()
+	tg.run("test", "-run", "Test", "testdata/standalone_test.go")
+	tg.grepBothNot(noMatchesPattern, "go test did say [no tests to run]")
+	tg.grepBoth(okPattern, "go test did not say ok")
+}
+
+func TestMatchesNoTestsWithSubtests(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.run("test", "-run", "ThisWillNotMatch", "testdata/standalone_sub_test.go")
+	tg.grepBoth(noMatchesPattern, "go test did not say [no tests to run]")
+}
+
+func TestMatchesNoSubtestsMatch(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.run("test", "-run", "Test/ThisWillNotMatch", "testdata/standalone_sub_test.go")
+	tg.grepBoth(noMatchesPattern, "go test did not say [no tests to run]")
+}
+
+func TestMatchesNoSubtestsDoesNotOverrideFailure(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.runFail("test", "-run", "TestThatFails/ThisWillNotMatch", "testdata/standalone_fail_sub_test.go")
+	tg.grepBothNot(noMatchesPattern, "go test did say [no tests to run]")
+	tg.grepBoth("FAIL", "go test did not say FAIL")
+}
+
+func TestMatchesOnlySubtestIsOK(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.run("test", "-run", "Test/Sub", "testdata/standalone_sub_test.go")
+	tg.grepBothNot(noMatchesPattern, "go test did say [no tests to run]")
+	tg.grepBoth(okPattern, "go test did not say ok")
+}
+
+func TestMatchesNoSubtestsParallel(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.run("test", "-run", "Test/Sub/ThisWillNotMatch", "testdata/standalone_parallel_sub_test.go")
+	tg.grepBoth(noMatchesPattern, "go test did not say [no tests to run]")
+}
+
+func TestMatchesOnlySubtestParallelIsOK(t *testing.T) {
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.run("test", "-run", "Test/Sub/Nested", "testdata/standalone_parallel_sub_test.go")
+	tg.grepBothNot(noMatchesPattern, "go test did say [no tests to run]")
+	tg.grepBoth(okPattern, "go test did not say ok")
+}
+
+func TestLinkXImportPathEscape(t *testing.T) {
+	// golang.org/issue/16710
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.parallel()
+	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
+	exe := "./linkx" + exeSuffix
+	tg.creatingTemp(exe)
+	tg.run("build", "-o", exe, "-ldflags", "-X=my.pkg.Text=linkXworked", "my.pkg/main")
+	out, err := exec.Command(exe).CombinedOutput()
+	if err != nil {
+		tg.t.Fatal(err)
+	}
+	if string(out) != "linkXworked\n" {
+		tg.t.Log(string(out))
+		tg.t.Fatal(`incorrect output: expected "linkXworked\n"`)
+	}
+}
diff --git a/src/cmd/go/go_windows_test.go b/src/cmd/go/go_windows_test.go
index 53d695c..d8d04aa 100644
--- a/src/cmd/go/go_windows_test.go
+++ b/src/cmd/go/go_windows_test.go
@@ -5,6 +5,7 @@
 package main
 
 import (
+	"internal/testenv"
 	"io/ioutil"
 	"os"
 	"os/exec"
@@ -45,7 +46,7 @@ func TestAbsolutePath(t *testing.T) {
 
 	noVolume := file[len(filepath.VolumeName(file)):]
 	wrongPath := filepath.Join(dir, noVolume)
-	output, err := exec.Command("go", "build", noVolume).CombinedOutput()
+	output, err := exec.Command(testenv.GoToolPath(t), "build", noVolume).CombinedOutput()
 	if err == nil {
 		t.Fatal("build should fail")
 	}
diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go
index 056a0af..fb69d8e 100644
--- a/src/cmd/go/help.go
+++ b/src/cmd/go/help.go
@@ -42,7 +42,7 @@ denotes the package in that directory.
 
 Otherwise, the import path P denotes the package found in
 the directory DIR/src/P for some DIR listed in the GOPATH
-environment variable (see 'go help gopath').
+environment variable (For more details see: 'go help gopath').
 
 If no import paths are given, the action applies to the
 package in the current directory.
@@ -62,6 +62,9 @@ Go library.
 - "cmd" expands to the Go repository's commands and their
 internal libraries.
 
+Import paths beginning with "cmd/" only match source code in
+the Go repository.
+
 An import path is a pattern if it includes one or more "..." wildcards,
 each of which can match any string, including the empty string and
 strings containing slashes.  Such a pattern expands to all package
@@ -102,10 +105,10 @@ var helpImportPath = &Command{
 	Short:     "import path syntax",
 	Long: `
 
-An import path (see 'go help packages') denotes a package
-stored in the local file system.  In general, an import path denotes
-either a standard package (such as "unicode/utf8") or a package
-found in one of the work spaces (see 'go help gopath').
+An import path (see 'go help packages') denotes a package stored in the local
+file system.  In general, an import path denotes either a standard package (such
+as "unicode/utf8") or a package found in one of the work spaces (For more
+details see: 'go help gopath').
 
 Relative import paths
 
@@ -197,6 +200,11 @@ When a version control system supports multiple protocols,
 each is tried in turn when downloading.  For example, a Git
 download tries https://, then git+ssh://.
 
+By default, downloads are restricted to known secure protocols
+(e.g. https, ssh). To override this setting for Git downloads, the
+GIT_ALLOW_PROTOCOL environment variable can be set (For more details see:
+'go help environment').
+
 If the import path is not a known code hosting site and also lacks a
 version control qualifier, the go tool attempts to fetch the import
 over https/http and looks for a <meta> tag in the document's HTML
@@ -237,8 +245,8 @@ the go tool will verify that https://example.org/?go-get=1 contains the
 same meta tag and then git clone https://code.org/r/p/exproj into
 GOPATH/src/example.org.
 
-New downloaded packages are written to the first directory
-listed in the GOPATH environment variable (see 'go help gopath').
+New downloaded packages are written to the first directory listed in the GOPATH
+environment variable (For more details see: 'go help gopath').
 
 The go command attempts to download the version of the
 package appropriate for the Go release being used.
@@ -281,8 +289,11 @@ On Unix, the value is a colon-separated string.
 On Windows, the value is a semicolon-separated string.
 On Plan 9, the value is a list.
 
-GOPATH must be set to get, build and install packages outside the
-standard Go tree.
+If the environment variable is unset, GOPATH defaults
+to a subdirectory named "go" in the user's home directory
+($HOME/go on Unix, %USERPROFILE%\go on Windows),
+unless that directory holds a Go distribution.
+Run "go env GOPATH" to see the current GOPATH.
 
 Each directory listed in GOPATH must have a prescribed structure:
 
@@ -310,9 +321,9 @@ of DIR/bin. GOBIN must be an absolute path.
 
 Here's an example directory layout:
 
-    GOPATH=/home/user/gocode
+    GOPATH=/home/user/go
 
-    /home/user/gocode/
+    /home/user/go/
         src/
             foo/
                 bar/               (go code in package bar)
@@ -338,7 +349,7 @@ Code in or below a directory named "internal" is importable only
 by code in the directory tree rooted at the parent of "internal".
 Here's an extended version of the directory layout above:
 
-    /home/user/gocode/
+    /home/user/go/
         src/
             crash/
                 bang/              (go code in package bang)
@@ -376,7 +387,7 @@ Here's the example from the previous section,
 but with the "internal" directory renamed to "vendor"
 and a new foo/vendor/crash/bang directory added:
 
-    /home/user/gocode/
+    /home/user/go/
         src/
             crash/
                 bang/              (go code in package bang)
@@ -439,7 +450,7 @@ General-purpose environment variables:
 		The operating system for which to compile code.
 		Examples are linux, darwin, windows, netbsd.
 	GOPATH
-		See 'go help gopath'.
+		For more details see: 'go help gopath'.
 	GORACE
 		Options for the race detector.
 		See https://golang.org/doc/articles/race_detector.html.
@@ -461,10 +472,15 @@ Environment variables for use with cgo:
 	CGO_CXXFLAGS
 		Flags that cgo will pass to the compiler when compiling
 		C++ code.
+	CGO_FFLAGS
+		Flags that cgo will pass to the compiler when compiling
+		Fortran code.
 	CGO_LDFLAGS
 		Flags that cgo will pass to the compiler when linking.
 	CXX
 		The command to use to compile C++ code.
+	PKG_CONFIG
+		Path to pkg-config tool.
 
 Architecture-specific environment variables:
 
@@ -486,6 +502,10 @@ Special-purpose environment variables:
 		Whether the linker should use external linking mode
 		when using -linkmode=auto with code that uses cgo.
 		Set to 0 to disable external linking mode, 1 to enable it.
+	GIT_ALLOW_PROTOCOL
+		Defined by Git. A colon-separated list of schemes that are allowed to be used
+		with git fetch/clone. If set, any scheme not explicitly mentioned will be
+		considered insecure by 'go get'.
 	`,
 }
 
@@ -577,5 +597,9 @@ are:
 		Build the listed main packages and everything they import into
 		position independent executables (PIE). Packages not named
 		main are ignored.
+
+	-buildmode=plugin
+		Build the listed main packages, plus all packages that they
+		import, into a Go plugin. Packages not named main are ignored.
 `,
 }
diff --git a/src/cmd/go/http.go b/src/cmd/go/http.go
index 05ea503..1dc2c12 100644
--- a/src/cmd/go/http.go
+++ b/src/cmd/go/http.go
@@ -12,6 +12,7 @@
 package main
 
 import (
+	"cmd/internal/browser"
 	"crypto/tls"
 	"fmt"
 	"io"
@@ -113,3 +114,6 @@ func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body
 	}
 	return urlStr, res.Body, nil
 }
+
+func queryEscape(s string) string { return url.QueryEscape(s) }
+func openBrowser(url string) bool { return browser.Open(url) }
diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go
index 48678e7..2f24083 100644
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -59,6 +59,8 @@ syntax of package template.  The default output is equivalent to -f
         SwigFiles      []string // .swig files
         SwigCXXFiles   []string // .swigcxx files
         SysoFiles      []string // .syso object files to add to archive
+        TestGoFiles    []string // _test.go files in package
+        XTestGoFiles   []string // _test.go files outside package
 
         // Cgo directives
         CgoCFLAGS    []string // cgo: flags for C compiler
@@ -69,20 +71,23 @@ syntax of package template.  The default output is equivalent to -f
         CgoPkgConfig []string // cgo: pkg-config names
 
         // Dependency information
-        Imports []string // import paths used by this package
-        Deps    []string // all (recursively) imported dependencies
+        Imports      []string // import paths used by this package
+        Deps         []string // all (recursively) imported dependencies
+        TestImports  []string // imports from TestGoFiles
+        XTestImports []string // imports from XTestGoFiles
 
         // Error information
         Incomplete bool            // this package or a dependency has an error
         Error      *PackageError   // error loading package
         DepsErrors []*PackageError // errors loading dependencies
-
-        TestGoFiles  []string // _test.go files in package
-        TestImports  []string // imports from TestGoFiles
-        XTestGoFiles []string // _test.go files outside package
-        XTestImports []string // imports from XTestGoFiles
     }
 
+Packages stored in vendor directories report an ImportPath that includes the
+path to the vendor directory (for example, "d/vendor/p" instead of "p"),
+so that the ImportPath uniquely identifies a given copy of a package.
+The Imports, Deps, TestImports, and XTestImports lists also contain these
+expanded imports paths. See golang.org/s/go15vendor for more about vendoring.
+
 The error information, if any, is
 
     type PackageError struct {
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 65ec61b..07fc4e2 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -79,6 +79,7 @@ var commands = []*Command{
 	cmdClean,
 	cmdDoc,
 	cmdEnv,
+	cmdBug,
 	cmdFix,
 	cmdFmt,
 	cmdGenerate,
@@ -114,6 +115,7 @@ func setExitStatus(n int) {
 }
 
 var origEnv []string
+var newEnv []envVar
 
 func main() {
 	_ = go11tag
@@ -134,7 +136,7 @@ func main() {
 	// Diagnose common mistake: GOPATH==GOROOT.
 	// This setting is equivalent to not setting GOPATH at all,
 	// which is not what most people want when they do it.
-	if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() {
+	if gopath := buildContext.GOPATH; gopath == runtime.GOROOT() {
 		fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
 	} else {
 		for _, p := range filepath.SplitList(gopath) {
@@ -146,7 +148,7 @@ func main() {
 				os.Exit(2)
 			}
 			if !filepath.IsAbs(p) {
-				fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p)
+				fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nFor more details see: 'go help gopath'\n", p)
 				os.Exit(2)
 			}
 		}
@@ -163,7 +165,8 @@ func main() {
 	// but in practice there might be skew
 	// This makes sure we all agree.
 	origEnv = os.Environ()
-	for _, env := range mkEnv() {
+	newEnv = mkEnv()
+	for _, env := range newEnv {
 		if os.Getenv(env.name) != env.value {
 			os.Setenv(env.name, env.value)
 		}
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index 07aa3ff..852a1a0 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -24,6 +24,8 @@ import (
 	"unicode"
 )
 
+var ignoreImports bool // control whether we ignore imports in packages
+
 // A Package describes a single package found in a directory.
 type Package struct {
 	// Note: These fields are part of the go command's public API.
@@ -181,6 +183,11 @@ func (p *Package) copyBuild(pp *build.Package) {
 	p.TestImports = pp.TestImports
 	p.XTestGoFiles = pp.XTestGoFiles
 	p.XTestImports = pp.XTestImports
+	if ignoreImports {
+		p.Imports = nil
+		p.TestImports = nil
+		p.XTestImports = nil
+	}
 }
 
 // isStandardImportPath reports whether $GOROOT/src/path should be considered
@@ -334,62 +341,98 @@ func loadImport(path, srcDir string, parent *Package, stk *importStack, importPo
 		importPath = path
 	}
 
-	if p := packageCache[importPath]; p != nil {
-		if perr := disallowInternal(srcDir, p, stk); perr != p {
-			return perr
+	p := packageCache[importPath]
+	if p != nil {
+		p = reusePackage(p, stk)
+	} else {
+		p = new(Package)
+		p.local = isLocal
+		p.ImportPath = importPath
+		packageCache[importPath] = p
+
+		// Load package.
+		// Import always returns bp != nil, even if an error occurs,
+		// in order to return partial information.
+		//
+		// TODO: After Go 1, decide when to pass build.AllowBinary here.
+		// See issue 3268 for mistakes to avoid.
+		buildMode := build.ImportComment
+		if mode&useVendor == 0 || path != origPath {
+			// Not vendoring, or we already found the vendored path.
+			buildMode |= build.IgnoreVendor
 		}
-		if mode&useVendor != 0 {
-			if perr := disallowVendor(srcDir, origPath, p, stk); perr != p {
-				return perr
-			}
+		bp, err := buildContext.Import(path, srcDir, buildMode)
+		bp.ImportPath = importPath
+		if gobin != "" {
+			bp.BinDir = gobin
+		}
+		if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path &&
+			!strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") {
+			err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
+		}
+		p.load(stk, bp, err)
+		if p.Error != nil && p.Error.Pos == "" {
+			p = setErrorPos(p, importPos)
 		}
-		return reusePackage(p, stk)
-	}
-
-	p := new(Package)
-	p.local = isLocal
-	p.ImportPath = importPath
-	packageCache[importPath] = p
 
-	// Load package.
-	// Import always returns bp != nil, even if an error occurs,
-	// in order to return partial information.
-	//
-	// TODO: After Go 1, decide when to pass build.AllowBinary here.
-	// See issue 3268 for mistakes to avoid.
-	buildMode := build.ImportComment
-	if mode&useVendor == 0 || path != origPath {
-		// Not vendoring, or we already found the vendored path.
-		buildMode |= build.IgnoreVendor
-	}
-	bp, err := buildContext.Import(path, srcDir, buildMode)
-	bp.ImportPath = importPath
-	if gobin != "" {
-		bp.BinDir = gobin
-	}
-	if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path &&
-		!strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") {
-		err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
-	}
-	p.load(stk, bp, err)
-	if p.Error != nil && p.Error.Pos == "" && len(importPos) > 0 {
-		pos := importPos[0]
-		pos.Filename = shortPath(pos.Filename)
-		p.Error.Pos = pos.String()
+		if origPath != cleanImport(origPath) {
+			p.Error = &PackageError{
+				ImportStack: stk.copy(),
+				Err:         fmt.Sprintf("non-canonical import path: %q should be %q", origPath, pathpkg.Clean(origPath)),
+			}
+			p.Incomplete = true
+		}
 	}
 
+	// Checked on every import because the rules depend on the code doing the importing.
 	if perr := disallowInternal(srcDir, p, stk); perr != p {
-		return perr
+		return setErrorPos(perr, importPos)
 	}
 	if mode&useVendor != 0 {
 		if perr := disallowVendor(srcDir, origPath, p, stk); perr != p {
-			return perr
+			return setErrorPos(perr, importPos)
+		}
+	}
+
+	if p.Name == "main" && parent != nil && parent.Dir != p.Dir {
+		perr := *p
+		perr.Error = &PackageError{
+			ImportStack: stk.copy(),
+			Err:         fmt.Sprintf("import %q is a program, not an importable package", path),
 		}
+		return setErrorPos(&perr, importPos)
 	}
 
+	if p.local && parent != nil && !parent.local {
+		perr := *p
+		perr.Error = &PackageError{
+			ImportStack: stk.copy(),
+			Err:         fmt.Sprintf("local import %q in non-local package", path),
+		}
+		return setErrorPos(&perr, importPos)
+	}
+
+	return p
+}
+
+func setErrorPos(p *Package, importPos []token.Position) *Package {
+	if len(importPos) > 0 {
+		pos := importPos[0]
+		pos.Filename = shortPath(pos.Filename)
+		p.Error.Pos = pos.String()
+	}
 	return p
 }
 
+func cleanImport(path string) string {
+	orig := path
+	path = pathpkg.Clean(path)
+	if strings.HasPrefix(orig, "./") && path != ".." && path != "." && !strings.HasPrefix(path, "../") {
+		path = "./" + path
+	}
+	return path
+}
+
 var isDirCache = map[string]bool{}
 
 func isDir(path string) bool {
@@ -415,13 +458,26 @@ func vendoredImportPath(parent *Package, path string) (found string) {
 
 	dir := filepath.Clean(parent.Dir)
 	root := filepath.Join(parent.Root, "src")
-	if !hasFilePathPrefix(dir, root) {
+	if !hasFilePathPrefix(dir, root) || parent.ImportPath != "command-line-arguments" && filepath.Join(root, parent.ImportPath) != dir {
 		// Look for symlinks before reporting error.
 		dir = expandPath(dir)
 		root = expandPath(root)
 	}
-	if !hasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator {
-		fatalf("invalid vendoredImportPath: dir=%q root=%q separator=%q", dir, root, string(filepath.Separator))
+
+	if !hasFilePathPrefix(dir, root) || len(dir) <= len(root) || dir[len(root)] != filepath.Separator || parent.ImportPath != "command-line-arguments" && !parent.local && filepath.Join(root, parent.ImportPath) != dir {
+		fatalf("unexpected directory layout:\n"+
+			"	import path: %s\n"+
+			"	root: %s\n"+
+			"	dir: %s\n"+
+			"	expand root: %s\n"+
+			"	expand dir: %s\n"+
+			"	separator: %s",
+			parent.ImportPath,
+			filepath.Join(parent.Root, "src"),
+			filepath.Clean(parent.Dir),
+			root,
+			dir,
+			string(filepath.Separator))
 	}
 
 	vpath := "vendor/" + path
@@ -519,6 +575,19 @@ func disallowInternal(srcDir string, p *Package, stk *importStack) *Package {
 		return p
 	}
 
+	// The generated 'testmain' package is allowed to access testing/internal/...,
+	// as if it were generated into the testing directory tree
+	// (it's actually in a temporary directory outside any Go tree).
+	// This cleans up a former kludge in passing functionality to the testing package.
+	if strings.HasPrefix(p.ImportPath, "testing/internal") && len(*stk) >= 2 && (*stk)[len(*stk)-2] == "testmain" {
+		return p
+	}
+
+	// We can't check standard packages with gccgo.
+	if buildContext.Compiler == "gccgo" && p.Standard {
+		return p
+	}
+
 	// The stack includes p.ImportPath.
 	// If that's the only thing on the stack, we started
 	// with a name given on the command line, not an
@@ -691,24 +760,23 @@ const (
 
 // goTools is a map of Go program import path to install target directory.
 var goTools = map[string]targetDir{
-	"cmd/addr2line":                        toTool,
-	"cmd/api":                              toTool,
-	"cmd/asm":                              toTool,
-	"cmd/compile":                          toTool,
-	"cmd/cgo":                              toTool,
-	"cmd/cover":                            toTool,
-	"cmd/dist":                             toTool,
-	"cmd/doc":                              toTool,
-	"cmd/fix":                              toTool,
-	"cmd/link":                             toTool,
-	"cmd/newlink":                          toTool,
-	"cmd/nm":                               toTool,
-	"cmd/objdump":                          toTool,
-	"cmd/pack":                             toTool,
-	"cmd/pprof":                            toTool,
-	"cmd/trace":                            toTool,
-	"cmd/vet":                              toTool,
-	"cmd/yacc":                             toTool,
+	"cmd/addr2line": toTool,
+	"cmd/api":       toTool,
+	"cmd/asm":       toTool,
+	"cmd/compile":   toTool,
+	"cmd/cgo":       toTool,
+	"cmd/cover":     toTool,
+	"cmd/dist":      toTool,
+	"cmd/doc":       toTool,
+	"cmd/fix":       toTool,
+	"cmd/link":      toTool,
+	"cmd/newlink":   toTool,
+	"cmd/nm":        toTool,
+	"cmd/objdump":   toTool,
+	"cmd/pack":      toTool,
+	"cmd/pprof":     toTool,
+	"cmd/trace":     toTool,
+	"cmd/vet":       toTool,
 	"code.google.com/p/go.tools/cmd/cover": stalePath,
 	"code.google.com/p/go.tools/cmd/godoc": stalePath,
 	"code.google.com/p/go.tools/cmd/vet":   stalePath,
@@ -776,7 +844,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 	useBindir := p.Name == "main"
 	if !p.Standard {
 		switch buildBuildmode {
-		case "c-archive", "c-shared":
+		case "c-archive", "c-shared", "plugin":
 			useBindir = false
 		}
 	}
@@ -847,11 +915,25 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 		importPaths = append(importPaths, "syscall")
 	}
 
-	// Currently build modes c-shared, pie, and -linkshared force
-	// external linking mode, and external linking mode forces an
-	// import of runtime/cgo.
-	if p.Name == "main" && !p.Goroot && (buildBuildmode == "c-shared" || buildBuildmode == "pie" || buildLinkshared) {
-		importPaths = append(importPaths, "runtime/cgo")
+	if buildContext.CgoEnabled && p.Name == "main" && !p.Goroot {
+		// Currently build modes c-shared, pie (on systems that do not
+		// support PIE with internal linking mode), plugin, and
+		// -linkshared force external linking mode, as of course does
+		// -ldflags=-linkmode=external. External linking mode forces
+		// an import of runtime/cgo.
+		pieCgo := buildBuildmode == "pie" && (buildContext.GOOS != "linux" || buildContext.GOARCH != "amd64")
+		linkmodeExternal := false
+		for i, a := range buildLdflags {
+			if a == "-linkmode=external" {
+				linkmodeExternal = true
+			}
+			if a == "-linkmode" && i+1 < len(buildLdflags) && buildLdflags[i+1] == "external" {
+				linkmodeExternal = true
+			}
+		}
+		if buildBuildmode == "c-shared" || buildBuildmode == "plugin" || pieCgo || buildLinkshared || linkmodeExternal {
+			importPaths = append(importPaths, "runtime/cgo")
+		}
 	}
 
 	// Everything depends on runtime, except runtime, its internal
@@ -933,33 +1015,21 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 	// Build list of imported packages and full dependency list.
 	imports := make([]*Package, 0, len(p.Imports))
 	deps := make(map[string]*Package)
+	save := func(path string, p1 *Package) {
+		// The same import path could produce an error or not,
+		// depending on what tries to import it.
+		// Prefer to record entries with errors, so we can report them.
+		p0 := deps[path]
+		if p0 == nil || p1.Error != nil && (p0.Error == nil || len(p0.Error.ImportStack) > len(p1.Error.ImportStack)) {
+			deps[path] = p1
+		}
+	}
+
 	for i, path := range importPaths {
 		if path == "C" {
 			continue
 		}
 		p1 := loadImport(path, p.Dir, p, stk, p.build.ImportPos[path], useVendor)
-		if p1.Name == "main" {
-			p.Error = &PackageError{
-				ImportStack: stk.copy(),
-				Err:         fmt.Sprintf("import %q is a program, not an importable package", path),
-			}
-			pos := p.build.ImportPos[path]
-			if len(pos) > 0 {
-				p.Error.Pos = pos[0].String()
-			}
-		}
-		if p1.local {
-			if !p.local && p.Error == nil {
-				p.Error = &PackageError{
-					ImportStack: stk.copy(),
-					Err:         fmt.Sprintf("local import %q in non-local package", path),
-				}
-				pos := p.build.ImportPos[path]
-				if len(pos) > 0 {
-					p.Error.Pos = pos[0].String()
-				}
-			}
-		}
 		if p.Standard && p.Error == nil && !p1.Standard && p1.Error == nil {
 			p.Error = &PackageError{
 				ImportStack: stk.copy(),
@@ -976,15 +1046,11 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 		if i < len(p.Imports) {
 			p.Imports[i] = path
 		}
-		deps[path] = p1
+
+		save(path, p1)
 		imports = append(imports, p1)
 		for _, dep := range p1.deps {
-			// The same import path could produce an error or not,
-			// depending on what tries to import it.
-			// Prefer to record entries with errors, so we can report them.
-			if deps[dep.ImportPath] == nil || dep.Error != nil {
-				deps[dep.ImportPath] = dep
-			}
+			save(dep.ImportPath, dep)
 		}
 		if p1.Incomplete {
 			p.Incomplete = true
@@ -1570,7 +1636,7 @@ func computeBuildID(p *Package) {
 	// Include the content of runtime/internal/sys/zversion.go in the hash
 	// for package runtime. This will give package runtime a
 	// different build ID in each Go release.
-	if p.Standard && p.ImportPath == "runtime/internal/sys" {
+	if p.Standard && p.ImportPath == "runtime/internal/sys" && buildContext.Compiler != "gccgo" {
 		data, err := ioutil.ReadFile(filepath.Join(p.Dir, "zversion.go"))
 		if err != nil {
 			fatalf("go: %s", err)
diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go
index e1527da..95914d5 100644
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -13,6 +13,7 @@ import (
 	"go/doc"
 	"go/parser"
 	"go/token"
+	"io"
 	"os"
 	"os/exec"
 	"path"
@@ -135,28 +136,11 @@ const testFlag2 = `
 	    By default, no benchmarks run. To run all benchmarks,
 	    use '-bench .' or '-bench=.'.
 
-	-benchmem
-	    Print memory allocation statistics for benchmarks.
-
 	-benchtime t
 	    Run enough iterations of each benchmark to take t, specified
 	    as a time.Duration (for example, -benchtime 1h30s).
 	    The default is 1 second (1s).
 
-	-blockprofile block.out
-	    Write a goroutine blocking profile to the specified file
-	    when all tests are complete.
-	    Writes test binary as -c would.
-
-	-blockprofilerate n
-	    Control the detail provided in goroutine blocking profiles by
-	    calling runtime.SetBlockProfileRate with n.
-	    See 'go doc runtime.SetBlockProfileRate'.
-	    The profiler aims to sample, on average, one blocking event every
-	    n nanoseconds the program spends blocked.  By default,
-	    if -test.blockprofile is set without this flag, all blocking events
-	    are recorded, equivalent to -test.blockprofilerate=1.
-
 	-count n
 	    Run each test and benchmark n times (default 1).
 	    If -cpu is set, run n times for each GOMAXPROCS value.
@@ -182,33 +166,11 @@ const testFlag2 = `
 	    Packages are specified as import paths.
 	    Sets -cover.
 
-	-coverprofile cover.out
-	    Write a coverage profile to the file after all tests have passed.
-	    Sets -cover.
-
 	-cpu 1,2,4
 	    Specify a list of GOMAXPROCS values for which the tests or
 	    benchmarks should be executed.  The default is the current value
 	    of GOMAXPROCS.
 
-	-cpuprofile cpu.out
-	    Write a CPU profile to the specified file before exiting.
-	    Writes test binary as -c would.
-
-	-memprofile mem.out
-	    Write a memory profile to the file after all tests have passed.
-	    Writes test binary as -c would.
-
-	-memprofilerate n
-	    Enable more precise (and expensive) memory profiles by setting
-	    runtime.MemProfileRate.  See 'go doc runtime.MemProfileRate'.
-	    To profile all memory allocations, use -test.memprofilerate=1
-	    and pass --alloc_space flag to the pprof tool.
-
-	-outputdir directory
-	    Place output files from profiling in the specified directory,
-	    by default the directory in which "go test" is running.
-
 	-parallel n
 	    Allow parallel execution of test functions that call t.Parallel.
 	    The value of this flag is the maximum number of tests to run
@@ -234,13 +196,64 @@ const testFlag2 = `
 	    If a test runs longer than t, panic.
 	    The default is 10 minutes (10m).
 
-	-trace trace.out
-	    Write an execution trace to the specified file before exiting.
-
 	-v
 	    Verbose output: log all tests as they are run. Also print all
 	    text from Log and Logf calls even if the test succeeds.
 
+The following flags are also recognized by 'go test' and can be used to
+profile the tests during execution::
+
+	-benchmem
+	    Print memory allocation statistics for benchmarks.
+
+	-blockprofile block.out
+	    Write a goroutine blocking profile to the specified file
+	    when all tests are complete.
+	    Writes test binary as -c would.
+
+	-blockprofilerate n
+	    Control the detail provided in goroutine blocking profiles by
+	    calling runtime.SetBlockProfileRate with n.
+	    See 'go doc runtime.SetBlockProfileRate'.
+	    The profiler aims to sample, on average, one blocking event every
+	    n nanoseconds the program spends blocked.  By default,
+	    if -test.blockprofile is set without this flag, all blocking events
+	    are recorded, equivalent to -test.blockprofilerate=1.
+
+	-coverprofile cover.out
+	    Write a coverage profile to the file after all tests have passed.
+	    Sets -cover.
+
+	-cpuprofile cpu.out
+	    Write a CPU profile to the specified file before exiting.
+	    Writes test binary as -c would.
+
+	-memprofile mem.out
+	    Write a memory profile to the file after all tests have passed.
+	    Writes test binary as -c would.
+
+	-memprofilerate n
+	    Enable more precise (and expensive) memory profiles by setting
+	    runtime.MemProfileRate.  See 'go doc runtime.MemProfileRate'.
+	    To profile all memory allocations, use -test.memprofilerate=1
+	    and pass --alloc_space flag to the pprof tool.
+
+	-mutexprofile mutex.out
+	    Write a mutex contention profile to the specified file
+	    when all tests are complete.
+	    Writes test binary as -c would.
+
+	-mutexprofilefraction n
+ 	    Sample 1 in n stack traces of goroutines holding a
+	    contended mutex.
+
+	-outputdir directory
+	    Place output files from profiling in the specified directory,
+	    by default the directory in which "go test" is running.
+
+	-trace trace.out
+	    Write an execution trace to the specified file before exiting.
+
 Each of these flags is also recognized with an optional 'test.' prefix,
 as in -test.v. When invoking the generated test binary (the result of
 'go test -c') directly, however, the prefix is mandatory.
@@ -381,9 +394,9 @@ var (
 
 var testMainDeps = map[string]bool{
 	// Dependencies for testmain.
-	"testing": true,
-	"regexp":  true,
-	"os":      true,
+	"testing":                   true,
+	"testing/internal/testdeps": true,
+	"os": true,
 }
 
 func runTest(cmd *Command, args []string) {
@@ -432,6 +445,11 @@ func runTest(cmd *Command, args []string) {
 	testStreamOutput = len(pkgArgs) == 0 || testBench ||
 		(testShowPass && (len(pkgs) == 1 || buildP == 1))
 
+	// For 'go test -i -o x.test', we want to build x.test. Imply -c to make the logic easier.
+	if buildI && testO != "" {
+		testC = true
+	}
+
 	var b builder
 	b.init()
 
@@ -849,7 +867,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
 	if err != nil {
 		return nil, nil, nil, err
 	}
-	if len(ptest.GoFiles) > 0 {
+	if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 {
 		pmain.imports = append(pmain.imports, ptest)
 		t.ImportTest = true
 	}
@@ -1077,6 +1095,8 @@ func declareCoverVars(importPath string, files ...string) map[string]*CoverVar {
 	return coverVars
 }
 
+var noTestsToRun = []byte("\ntesting: warning: no tests to run\n")
+
 // runTest is the action for running a test binary.
 func (b *builder) runTest(a *action) error {
 	args := stringList(findExecCmd(), a.deps[0].target, testArgs)
@@ -1102,8 +1122,12 @@ func (b *builder) runTest(a *action) error {
 	cmd.Env = envForDir(cmd.Dir, origEnv)
 	var buf bytes.Buffer
 	if testStreamOutput {
-		cmd.Stdout = os.Stdout
-		cmd.Stderr = os.Stderr
+		// The only way to keep the ordering of the messages and still
+		// intercept its contents. os/exec will share the same Pipe for
+		// both Stdout and Stderr when running the test program.
+		mw := io.MultiWriter(os.Stdout, &buf)
+		cmd.Stdout = mw
+		cmd.Stderr = mw
 	} else {
 		cmd.Stdout = &buf
 		cmd.Stderr = &buf
@@ -1167,16 +1191,22 @@ func (b *builder) runTest(a *action) error {
 	out := buf.Bytes()
 	t := fmt.Sprintf("%.3fs", time.Since(t0).Seconds())
 	if err == nil {
-		if testShowPass {
+		norun := ""
+		if testShowPass && !testStreamOutput {
 			a.testOutput.Write(out)
 		}
-		fmt.Fprintf(a.testOutput, "ok  \t%s\t%s%s\n", a.p.ImportPath, t, coveragePercentage(out))
+		if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
+			norun = " [no tests to run]"
+		}
+		fmt.Fprintf(a.testOutput, "ok  \t%s\t%s%s%s\n", a.p.ImportPath, t, coveragePercentage(out), norun)
 		return nil
 	}
 
 	setExitStatus(1)
 	if len(out) > 0 {
-		a.testOutput.Write(out)
+		if !testStreamOutput {
+			a.testOutput.Write(out)
+		}
 		// assume printing the test binary's exit status is superfluous
 	} else {
 		fmt.Fprintf(a.testOutput, "%s\n", err)
@@ -1394,7 +1424,7 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
 		}
 	}
 	ex := doc.Examples(f)
-	sort.Sort(byOrder(ex))
+	sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order })
 	for _, e := range ex {
 		*doImport = true // import test file whether executed or not
 		if e.Output == "" && !e.EmptyOutput {
@@ -1416,12 +1446,6 @@ func checkTestFunc(fn *ast.FuncDecl, arg string) error {
 	return nil
 }
 
-type byOrder []*doc.Example
-
-func (x byOrder) Len() int           { return len(x) }
-func (x byOrder) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
-func (x byOrder) Less(i, j int) bool { return x[i].Order < x[j].Order }
-
 var testmainTmpl = template.Must(template.New("main").Parse(`
 package main
 
@@ -1429,8 +1453,8 @@ import (
 {{if not .TestMain}}
 	"os"
 {{end}}
-	"regexp"
 	"testing"
+	"testing/internal/testdeps"
 
 {{if .ImportTest}}
 	{{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}}
@@ -1465,20 +1489,6 @@ var examples = []testing.InternalExample{
 {{end}}
 }
 
-var matchPat string
-var matchRe *regexp.Regexp
-
-func matchString(pat, str string) (result bool, err error) {
-	if matchRe == nil || matchPat != pat {
-		matchPat = pat
-		matchRe, err = regexp.Compile(matchPat)
-		if err != nil {
-			return
-		}
-	}
-	return matchRe.MatchString(str), nil
-}
-
 {{if .CoverEnabled}}
 
 // Only updated by init functions, so no need for atomicity.
@@ -1527,7 +1537,7 @@ func main() {
 		CoveredPackages: {{printf "%q" .Covered}},
 	})
 {{end}}
-	m := testing.MainStart(matchString, tests, benchmarks, examples)
+	m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
 {{with .TestMain}}
 	{{.Package}}.{{.Name}}(m)
 {{else}}
diff --git a/src/cmd/go/testdata/src/canonical/a/a.go b/src/cmd/go/testdata/src/canonical/a/a.go
new file mode 100644
index 0000000..486cc48
--- /dev/null
+++ b/src/cmd/go/testdata/src/canonical/a/a.go
@@ -0,0 +1,3 @@
+package a
+
+import _ "c"
diff --git a/src/cmd/go/testdata/src/canonical/a/vendor/c/c.go b/src/cmd/go/testdata/src/canonical/a/vendor/c/c.go
new file mode 100644
index 0000000..7f96c22
--- /dev/null
+++ b/src/cmd/go/testdata/src/canonical/a/vendor/c/c.go
@@ -0,0 +1 @@
+package c
diff --git a/src/cmd/go/testdata/src/canonical/b/b.go b/src/cmd/go/testdata/src/canonical/b/b.go
new file mode 100644
index 0000000..ce0f4ce
--- /dev/null
+++ b/src/cmd/go/testdata/src/canonical/b/b.go
@@ -0,0 +1,3 @@
+package b
+
+import _ "canonical/a/"
diff --git a/src/cmd/go/testdata/src/canonical/d/d.go b/src/cmd/go/testdata/src/canonical/d/d.go
new file mode 100644
index 0000000..ef7dd7d
--- /dev/null
+++ b/src/cmd/go/testdata/src/canonical/d/d.go
@@ -0,0 +1,3 @@
+package d
+
+import _ "canonical/b"
diff --git a/src/cmd/go/testdata/cgocover/p.go b/src/cmd/go/testdata/src/cgocover/p.go
similarity index 100%
copy from src/cmd/go/testdata/cgocover/p.go
copy to src/cmd/go/testdata/src/cgocover/p.go
diff --git a/src/cmd/go/testdata/cgocover/p_test.go b/src/cmd/go/testdata/src/cgocover/p_test.go
similarity index 100%
rename from src/cmd/go/testdata/cgocover/p_test.go
rename to src/cmd/go/testdata/src/cgocover/p_test.go
diff --git a/src/cmd/go/testdata/cgocover/p.go b/src/cmd/go/testdata/src/cgocover2/p.go
similarity index 100%
copy from src/cmd/go/testdata/cgocover/p.go
copy to src/cmd/go/testdata/src/cgocover2/p.go
diff --git a/src/cmd/go/testdata/src/cgocover2/x_test.go b/src/cmd/go/testdata/src/cgocover2/x_test.go
new file mode 100644
index 0000000..f4790d2
--- /dev/null
+++ b/src/cmd/go/testdata/src/cgocover2/x_test.go
@@ -0,0 +1,10 @@
+package p_test
+
+import (
+	. "cgocover2"
+	"testing"
+)
+
+func TestF(t *testing.T) {
+	F()
+}
diff --git a/src/cmd/go/testdata/cgocover/p.go b/src/cmd/go/testdata/src/cgocover3/p.go
similarity index 100%
copy from src/cmd/go/testdata/cgocover/p.go
copy to src/cmd/go/testdata/src/cgocover3/p.go
diff --git a/src/cmd/go/testdata/src/cgocover3/p_test.go b/src/cmd/go/testdata/src/cgocover3/p_test.go
new file mode 100644
index 0000000..c89cd18
--- /dev/null
+++ b/src/cmd/go/testdata/src/cgocover3/p_test.go
@@ -0,0 +1 @@
+package p
diff --git a/src/cmd/go/testdata/src/cgocover3/x_test.go b/src/cmd/go/testdata/src/cgocover3/x_test.go
new file mode 100644
index 0000000..97d0e0f
--- /dev/null
+++ b/src/cmd/go/testdata/src/cgocover3/x_test.go
@@ -0,0 +1,10 @@
+package p_test
+
+import (
+	. "cgocover3"
+	"testing"
+)
+
+func TestF(t *testing.T) {
+	F()
+}
diff --git a/src/cmd/go/testdata/src/cgocover4/notcgo.go b/src/cmd/go/testdata/src/cgocover4/notcgo.go
new file mode 100644
index 0000000..c89cd18
--- /dev/null
+++ b/src/cmd/go/testdata/src/cgocover4/notcgo.go
@@ -0,0 +1 @@
+package p
diff --git a/src/cmd/go/testdata/cgocover/p.go b/src/cmd/go/testdata/src/cgocover4/p.go
similarity index 100%
rename from src/cmd/go/testdata/cgocover/p.go
rename to src/cmd/go/testdata/src/cgocover4/p.go
diff --git a/src/cmd/go/testdata/src/cgocover4/x_test.go b/src/cmd/go/testdata/src/cgocover4/x_test.go
new file mode 100644
index 0000000..fd9bae7
--- /dev/null
+++ b/src/cmd/go/testdata/src/cgocover4/x_test.go
@@ -0,0 +1,10 @@
+package p_test
+
+import (
+	. "cgocover4"
+	"testing"
+)
+
+func TestF(t *testing.T) {
+	F()
+}
diff --git a/src/cmd/go/testdata/src/dupload/dupload.go b/src/cmd/go/testdata/src/dupload/dupload.go
new file mode 100644
index 0000000..2f07852
--- /dev/null
+++ b/src/cmd/go/testdata/src/dupload/dupload.go
@@ -0,0 +1,8 @@
+package main
+
+import (
+	_ "dupload/p2"
+	_ "p"
+)
+
+func main() {}
diff --git a/src/cmd/go/testdata/src/dupload/p/p.go b/src/cmd/go/testdata/src/dupload/p/p.go
new file mode 100644
index 0000000..c89cd18
--- /dev/null
+++ b/src/cmd/go/testdata/src/dupload/p/p.go
@@ -0,0 +1 @@
+package p
diff --git a/src/cmd/go/testdata/src/dupload/p2/p2.go b/src/cmd/go/testdata/src/dupload/p2/p2.go
new file mode 100644
index 0000000..40f5a5b
--- /dev/null
+++ b/src/cmd/go/testdata/src/dupload/p2/p2.go
@@ -0,0 +1,2 @@
+package p2
+import _ "dupload/vendor/p"
diff --git a/src/cmd/go/testdata/src/dupload/vendor/p/p.go b/src/cmd/go/testdata/src/dupload/vendor/p/p.go
new file mode 100644
index 0000000..c89cd18
--- /dev/null
+++ b/src/cmd/go/testdata/src/dupload/vendor/p/p.go
@@ -0,0 +1 @@
+package p
diff --git a/src/cmd/go/testdata/src/gencycle/gencycle.go b/src/cmd/go/testdata/src/gencycle/gencycle.go
new file mode 100644
index 0000000..600afd9
--- /dev/null
+++ b/src/cmd/go/testdata/src/gencycle/gencycle.go
@@ -0,0 +1,5 @@
+//go:generate echo hello world
+
+package gencycle
+
+import _ "gencycle"
diff --git a/src/cmd/go/testdata/src/importmain/ismain/main.go b/src/cmd/go/testdata/src/importmain/ismain/main.go
new file mode 100644
index 0000000..bf01907
--- /dev/null
+++ b/src/cmd/go/testdata/src/importmain/ismain/main.go
@@ -0,0 +1,5 @@
+package main
+
+import _ "importmain/test"
+
+func main() {}
diff --git a/src/cmd/go/testdata/src/importmain/test/test.go b/src/cmd/go/testdata/src/importmain/test/test.go
new file mode 100644
index 0000000..56e5404
--- /dev/null
+++ b/src/cmd/go/testdata/src/importmain/test/test.go
@@ -0,0 +1 @@
+package test
diff --git a/src/cmd/go/testdata/src/importmain/test/test_test.go b/src/cmd/go/testdata/src/importmain/test/test_test.go
new file mode 100644
index 0000000..2268a82
--- /dev/null
+++ b/src/cmd/go/testdata/src/importmain/test/test_test.go
@@ -0,0 +1,6 @@
+package test_test
+
+import "testing"
+import _ "importmain/ismain"
+
+func TestCase(t *testing.T) {}
diff --git a/src/cmd/go/testdata/src/my.pkg/main/main.go b/src/cmd/go/testdata/src/my.pkg/main/main.go
new file mode 100644
index 0000000..397e8b6
--- /dev/null
+++ b/src/cmd/go/testdata/src/my.pkg/main/main.go
@@ -0,0 +1,5 @@
+package main
+import "my.pkg"
+func main() {
+	println(pkg.Text)
+}
diff --git a/src/cmd/go/testdata/src/my.pkg/pkg.go b/src/cmd/go/testdata/src/my.pkg/pkg.go
new file mode 100644
index 0000000..17702a6
--- /dev/null
+++ b/src/cmd/go/testdata/src/my.pkg/pkg.go
@@ -0,0 +1,3 @@
+package pkg
+
+var Text = "unset"
diff --git a/src/cmd/go/testdata/src/testrace/race_test.go b/src/cmd/go/testdata/src/testrace/race_test.go
new file mode 100644
index 0000000..264dcf0
--- /dev/null
+++ b/src/cmd/go/testdata/src/testrace/race_test.go
@@ -0,0 +1,29 @@
+package testrace
+
+import "testing"
+
+func TestRace(t *testing.T) {
+	for i := 0; i < 10; i++ {
+		c := make(chan int)
+		x := 1
+		go func() {
+			x = 2
+			c <- 1
+		}()
+		x = 3
+		<-c
+	}
+}
+
+func BenchmarkRace(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		c := make(chan int)
+		x := 1
+		go func() {
+			x = 2
+			c <- 1
+		}()
+		x = 3
+		<-c
+	}
+}
diff --git a/src/cmd/go/testdata/standalone_benchmark_test.go b/src/cmd/go/testdata/standalone_benchmark_test.go
new file mode 100644
index 0000000..4850f98
--- /dev/null
+++ b/src/cmd/go/testdata/standalone_benchmark_test.go
@@ -0,0 +1,6 @@
+package standalone_benchmark
+
+import "testing"
+
+func Benchmark(b *testing.B) {
+}
diff --git a/src/cmd/go/testdata/standalone_fail_sub_test.go b/src/cmd/go/testdata/standalone_fail_sub_test.go
new file mode 100644
index 0000000..ac483f9
--- /dev/null
+++ b/src/cmd/go/testdata/standalone_fail_sub_test.go
@@ -0,0 +1,8 @@
+package standalone_fail_sub_test
+
+import "testing"
+
+func TestThatFails(t *testing.T) {
+	t.Run("Sub", func(t *testing.T) {})
+	t.Fail()
+}
diff --git a/src/cmd/go/testdata/standalone_parallel_sub_test.go b/src/cmd/go/testdata/standalone_parallel_sub_test.go
new file mode 100644
index 0000000..d326de0
--- /dev/null
+++ b/src/cmd/go/testdata/standalone_parallel_sub_test.go
@@ -0,0 +1,14 @@
+package standalone_parallel_sub_test
+
+import "testing"
+
+func Test(t *testing.T) {
+	ch := make(chan bool, 1)
+	t.Run("Sub", func(t *testing.T) {
+		t.Parallel()
+		<-ch
+		t.Run("Nested", func(t *testing.T) {})
+	})
+	// Ensures that Sub will finish after its t.Run call already returned.
+	ch <- true
+}
diff --git a/src/cmd/go/testdata/standalone_sub_test.go b/src/cmd/go/testdata/standalone_sub_test.go
new file mode 100644
index 0000000..f6c31db
--- /dev/null
+++ b/src/cmd/go/testdata/standalone_sub_test.go
@@ -0,0 +1,7 @@
+package standalone_sub_test
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Run("Sub", func(t *testing.T) {})
+}
diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go
index a65ed1f..cf4d2b4 100644
--- a/src/cmd/go/testflag.go
+++ b/src/cmd/go/testflag.go
@@ -50,6 +50,8 @@ var testFlagDefn = []*testFlagSpec{
 	{name: "memprofilerate", passToTest: true},
 	{name: "blockprofile", passToTest: true},
 	{name: "blockprofilerate", passToTest: true},
+	{name: "mutexprofile", passToTest: true},
+	{name: "mutexprofilefraction", passToTest: true},
 	{name: "outputdir", passToTest: true},
 	{name: "parallel", passToTest: true},
 	{name: "run", passToTest: true},
@@ -152,7 +154,7 @@ func testFlags(args []string) (packageNames, passToTest []string) {
 			case "blockprofile", "cpuprofile", "memprofile":
 				testProfile = true
 				testNeedBinary = true
-			case "trace":
+			case "mutexprofile", "trace":
 				testProfile = true
 			case "coverpkg":
 				testCover = true
diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go
index b829037..38110cf 100644
--- a/src/cmd/go/tool.go
+++ b/src/cmd/go/tool.go
@@ -60,7 +60,7 @@ func tool(toolName string) string {
 		} else {
 			fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
 		}
-		setExitStatus(3)
+		setExitStatus(2)
 		exit()
 	}
 	return toolPath
diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go
index 53ddbe6..fcdce22 100644
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -41,7 +41,7 @@ type vcsCmd struct {
 	resolveRepo func(v *vcsCmd, rootDir, remoteRepo string) (realRepo string, err error)
 }
 
-var isSecureScheme = map[string]bool{
+var defaultSecureScheme = map[string]bool{
 	"https":   true,
 	"git+ssh": true,
 	"bzr+ssh": true,
@@ -55,7 +55,25 @@ func (v *vcsCmd) isSecure(repo string) bool {
 		// If repo is not a URL, it's not secure.
 		return false
 	}
-	return isSecureScheme[u.Scheme]
+	return v.isSecureScheme(u.Scheme)
+}
+
+func (v *vcsCmd) isSecureScheme(scheme string) bool {
+	switch v.cmd {
+	case "git":
+		// GIT_ALLOW_PROTOCOL is an environment variable defined by Git. It is a
+		// colon-separated list of schemes that are allowed to be used with git
+		// fetch/clone. Any scheme not mentioned will be considered insecure.
+		if allow := os.Getenv("GIT_ALLOW_PROTOCOL"); allow != "" {
+			for _, s := range strings.Split(allow, ":") {
+				if s == scheme {
+					return true
+				}
+			}
+			return false
+		}
+	}
+	return defaultSecureScheme[scheme]
 }
 
 // A tagCmd describes a command to list available tags
@@ -482,7 +500,7 @@ func vcsFromDir(dir, srcRoot string) (vcs *vcsCmd, root string, err error) {
 	origDir := dir
 	for len(dir) > len(srcRoot) {
 		for _, vcs := range vcsList {
-			if fi, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil && fi.IsDir() {
+			if _, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil {
 				return vcs, filepath.ToSlash(dir[len(srcRoot)+1:]), nil
 			}
 		}
@@ -510,6 +528,9 @@ type repoRoot struct {
 	// root is the import path corresponding to the root of the
 	// repository
 	root string
+
+	// isCustom is true for custom import paths (those defined by HTML meta tags)
+	isCustom bool
 }
 
 var httpPrefixRE = regexp.MustCompile(`^https?:`)
@@ -612,7 +633,7 @@ func repoRootFromVCSPaths(importPath, scheme string, security securityMode, vcsP
 				match["repo"] = scheme + "://" + match["repo"]
 			} else {
 				for _, scheme := range vcs.scheme {
-					if security == secure && !isSecureScheme[scheme] {
+					if security == secure && !vcs.isSecureScheme(scheme) {
 						continue
 					}
 					if vcs.ping(scheme, match["repo"]) == nil {
@@ -661,10 +682,10 @@ func repoRootForImportDynamic(importPath string, security securityMode) (*repoRo
 	// Find the matched meta import.
 	mmi, err := matchGoImport(imports, importPath)
 	if err != nil {
-		if err != errNoMatch {
+		if _, ok := err.(ImportMismatchError); !ok {
 			return nil, fmt.Errorf("parse %s: %v", urlStr, err)
 		}
-		return nil, fmt.Errorf("parse %s: no go-import meta tags", urlStr)
+		return nil, fmt.Errorf("parse %s: no go-import meta tags (%s)", urlStr, err)
 	}
 	if buildV {
 		log.Printf("get %q: found meta tag %#v at %s", importPath, mmi, urlStr)
@@ -695,9 +716,10 @@ func repoRootForImportDynamic(importPath string, security securityMode) (*repoRo
 		return nil, fmt.Errorf("%s: invalid repo root %q; no scheme", urlStr, mmi.RepoRoot)
 	}
 	rr := &repoRoot{
-		vcs:  vcsByCmd(mmi.VCS),
-		repo: mmi.RepoRoot,
-		root: mmi.Prefix,
+		vcs:      vcsByCmd(mmi.VCS),
+		repo:     mmi.RepoRoot,
+		root:     mmi.Prefix,
+		isCustom: true,
 	}
 	if rr.vcs == nil {
 		return nil, fmt.Errorf("%s: unknown vcs %q", urlStr, mmi.VCS)
@@ -764,9 +786,6 @@ type metaImport struct {
 	Prefix, VCS, RepoRoot string
 }
 
-// errNoMatch is returned from matchGoImport when there's no applicable match.
-var errNoMatch = errors.New("no import match")
-
 func splitPathHasPrefix(path, prefix []string) bool {
 	if len(path) < len(prefix) {
 		return false
@@ -779,28 +798,45 @@ func splitPathHasPrefix(path, prefix []string) bool {
 	return true
 }
 
+// A ImportMismatchError is returned where metaImport/s are present
+// but none match our import path.
+type ImportMismatchError struct {
+	importPath string
+	mismatches []string // the meta imports that were discarded for not matching our importPath
+}
+
+func (m ImportMismatchError) Error() string {
+	formattedStrings := make([]string, len(m.mismatches))
+	for i, pre := range m.mismatches {
+		formattedStrings[i] = fmt.Sprintf("meta tag %s did not match import path %s", pre, m.importPath)
+	}
+	return strings.Join(formattedStrings, ", ")
+}
+
 // matchGoImport returns the metaImport from imports matching importPath.
 // An error is returned if there are multiple matches.
 // errNoMatch is returned if none match.
-func matchGoImport(imports []metaImport, importPath string) (_ metaImport, err error) {
+func matchGoImport(imports []metaImport, importPath string) (metaImport, error) {
 	match := -1
 	imp := strings.Split(importPath, "/")
+
+	errImportMismatch := ImportMismatchError{importPath: importPath}
 	for i, im := range imports {
 		pre := strings.Split(im.Prefix, "/")
 
 		if !splitPathHasPrefix(imp, pre) {
+			errImportMismatch.mismatches = append(errImportMismatch.mismatches, im.Prefix)
 			continue
 		}
 
 		if match != -1 {
-			err = fmt.Errorf("multiple meta tags match import path %q", importPath)
-			return
+			return metaImport{}, fmt.Errorf("multiple meta tags match import path %q", importPath)
 		}
 		match = i
 	}
+
 	if match == -1 {
-		err = errNoMatch
-		return
+		return metaImport{}, errImportMismatch
 	}
 	return imports[match], nil
 }
diff --git a/src/cmd/go/vcs_test.go b/src/cmd/go/vcs_test.go
index 25e3866..c73f5d0 100644
--- a/src/cmd/go/vcs_test.go
+++ b/src/cmd/go/vcs_test.go
@@ -102,7 +102,7 @@ func TestRepoRootForImportPath(t *testing.T) {
 			"git.openstack.org/openstack/swift.git",
 			&repoRoot{
 				vcs:  vcsGit,
-				repo: "https://git.openstack.org/openstack/swift",
+				repo: "https://git.openstack.org/openstack/swift.git",
 			},
 		},
 		{
@@ -174,11 +174,23 @@ func TestFromDir(t *testing.T) {
 	}
 	defer os.RemoveAll(tempDir)
 
-	for _, vcs := range vcsList {
+	for j, vcs := range vcsList {
 		dir := filepath.Join(tempDir, "example.com", vcs.name, "."+vcs.cmd)
-		err := os.MkdirAll(dir, 0755)
-		if err != nil {
-			t.Fatal(err)
+		if j&1 == 0 {
+			err := os.MkdirAll(dir, 0755)
+			if err != nil {
+				t.Fatal(err)
+			}
+		} else {
+			err := os.MkdirAll(filepath.Dir(dir), 0755)
+			if err != nil {
+				t.Fatal(err)
+			}
+			f, err := os.Create(dir)
+			if err != nil {
+				t.Fatal(err)
+			}
+			f.Close()
 		}
 
 		want := repoRoot{
@@ -229,6 +241,46 @@ func TestIsSecure(t *testing.T) {
 	}
 }
 
+func TestIsSecureGitAllowProtocol(t *testing.T) {
+	tests := []struct {
+		vcs    *vcsCmd
+		url    string
+		secure bool
+	}{
+		// Same as TestIsSecure to verify same behavior.
+		{vcsGit, "http://example.com/foo.git", false},
+		{vcsGit, "https://example.com/foo.git", true},
+		{vcsBzr, "http://example.com/foo.bzr", false},
+		{vcsBzr, "https://example.com/foo.bzr", true},
+		{vcsSvn, "http://example.com/svn", false},
+		{vcsSvn, "https://example.com/svn", true},
+		{vcsHg, "http://example.com/foo.hg", false},
+		{vcsHg, "https://example.com/foo.hg", true},
+		{vcsGit, "user at server:path/to/repo.git", false},
+		{vcsGit, "user at server:", false},
+		{vcsGit, "server:repo.git", false},
+		{vcsGit, "server:path/to/repo.git", false},
+		{vcsGit, "example.com:path/to/repo.git", false},
+		{vcsGit, "path/that/contains/a:colon/repo.git", false},
+		{vcsHg, "ssh://user@example.com/path/to/repo.hg", true},
+		// New behavior.
+		{vcsGit, "ssh://user@example.com/foo.git", false},
+		{vcsGit, "foo://example.com/bar.git", true},
+		{vcsHg, "foo://example.com/bar.hg", false},
+		{vcsSvn, "foo://example.com/svn", false},
+		{vcsBzr, "foo://example.com/bar.bzr", false},
+	}
+
+	defer os.Unsetenv("GIT_ALLOW_PROTOCOL")
+	os.Setenv("GIT_ALLOW_PROTOCOL", "https:foo")
+	for _, test := range tests {
+		secure := test.vcs.isSecure(test.url)
+		if secure != test.secure {
+			t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure)
+		}
+	}
+}
+
 func TestMatchGoImport(t *testing.T) {
 	tests := []struct {
 		imports []metaImport
@@ -306,6 +358,13 @@ func TestMatchGoImport(t *testing.T) {
 			path: "example.com",
 			err:  errors.New("pathologically short path"),
 		},
+		{
+			imports: []metaImport{
+				{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
+			},
+			path: "different.example.com/user/foo",
+			err:  errors.New("meta tags do not match import path"),
+		},
 	}
 
 	for _, test := range tests {
diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go
index 9d0cd32..8b22f03 100644
--- a/src/cmd/gofmt/doc.go
+++ b/src/cmd/gofmt/doc.go
@@ -32,7 +32,8 @@ The flags are:
 	-w
 		Do not print reformatted sources to standard output.
 		If a file's formatting is different from gofmt's, overwrite it
-		with gofmt's version.
+		with gofmt's version. If an error occurred during overwriting,
+		the original file is restored from an automatic backup.
 
 Debugging support:
 	-cpuprofile filename
@@ -98,3 +99,5 @@ This may result in changes that are incompatible with earlier versions of Go.
 package main
 
 // BUG(rsc): The implementation of -r is a bit slow.
+// BUG(gri): If -w fails, the restored original file may not have some of the
+//           original file attributes.
diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go
index f29b6cb..e1ef0dd 100644
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -18,6 +18,7 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
+	"runtime"
 	"runtime/pprof"
 	"strings"
 )
@@ -72,13 +73,19 @@ func isGoFile(f os.FileInfo) bool {
 
 // If in == nil, the source is the contents of the file with the given filename.
 func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
+	var perm os.FileMode = 0644
 	if in == nil {
 		f, err := os.Open(filename)
 		if err != nil {
 			return err
 		}
 		defer f.Close()
+		fi, err := f.Stat()
+		if err != nil {
+			return err
+		}
 		in = f
+		perm = fi.Mode().Perm()
 	}
 
 	src, err := ioutil.ReadAll(in)
@@ -116,7 +123,17 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
 			fmt.Fprintln(out, filename)
 		}
 		if *write {
-			err = ioutil.WriteFile(filename, res, 0644)
+			// make a temporary backup before overwriting original
+			bakname, err := backupFile(filename+".", src, perm)
+			if err != nil {
+				return err
+			}
+			err = ioutil.WriteFile(filename, res, perm)
+			if err != nil {
+				os.Rename(bakname, filename)
+				return err
+			}
+			err = os.Remove(bakname)
 			if err != nil {
 				return err
 			}
@@ -235,3 +252,36 @@ func diff(b1, b2 []byte) (data []byte, err error) {
 	return
 
 }
+
+const chmodSupported = runtime.GOOS != "windows"
+
+// backupFile writes data to a new file named filename<number> with permissions perm,
+// with <number randomly chosen such that the file name is unique. backupFile returns
+// the chosen file name.
+func backupFile(filename string, data []byte, perm os.FileMode) (string, error) {
+	// create backup file
+	f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
+	if err != nil {
+		return "", err
+	}
+	bakname := f.Name()
+	if chmodSupported {
+		err = f.Chmod(perm)
+		if err != nil {
+			f.Close()
+			os.Remove(bakname)
+			return bakname, err
+		}
+	}
+
+	// write data to backup file
+	n, err := f.Write(data)
+	if err == nil && n < len(data) {
+		err = io.ErrShortWrite
+	}
+	if err1 := f.Close(); err == nil {
+		err = err1
+	}
+
+	return bakname, err
+}
diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go
index dea0127..b7ca9e8 100644
--- a/src/cmd/gofmt/gofmt_test.go
+++ b/src/cmd/gofmt/gofmt_test.go
@@ -171,3 +171,16 @@ func TestCRLF(t *testing.T) {
 		t.Errorf("%s contains CR's", golden)
 	}
 }
+
+func TestBackupFile(t *testing.T) {
+	dir, err := ioutil.TempDir("", "gofmt_test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+	name, err := backupFile(filepath.Join(dir, "foo.go"), []byte("  package main"), 0644)
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Logf("Created: %s", name)
+}
diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go
index 2ebf4cd..1a0e817 100644
--- a/src/cmd/gofmt/simplify.go
+++ b/src/cmd/gofmt/simplify.go
@@ -17,47 +17,33 @@ func (s simplifier) Visit(node ast.Node) ast.Visitor {
 	case *ast.CompositeLit:
 		// array, slice, and map composite literals may be simplified
 		outer := n
-		var eltType ast.Expr
+		var keyType, eltType ast.Expr
 		switch typ := outer.Type.(type) {
 		case *ast.ArrayType:
 			eltType = typ.Elt
 		case *ast.MapType:
+			keyType = typ.Key
 			eltType = typ.Value
 		}
 
 		if eltType != nil {
+			var ktyp reflect.Value
+			if keyType != nil {
+				ktyp = reflect.ValueOf(keyType)
+			}
 			typ := reflect.ValueOf(eltType)
 			for i, x := range outer.Elts {
 				px := &outer.Elts[i]
 				// look at value of indexed/named elements
 				if t, ok := x.(*ast.KeyValueExpr); ok {
+					if keyType != nil {
+						s.simplifyLiteral(ktyp, keyType, t.Key, &t.Key)
+					}
 					x = t.Value
 					px = &t.Value
 				}
-				ast.Walk(s, x) // simplify x
-				// if the element is a composite literal and its literal type
-				// matches the outer literal's element type exactly, the inner
-				// literal type may be omitted
-				if inner, ok := x.(*ast.CompositeLit); ok {
-					if match(nil, typ, reflect.ValueOf(inner.Type)) {
-						inner.Type = nil
-					}
-				}
-				// if the outer literal's element type is a pointer type *T
-				// and the element is & of a composite literal of type T,
-				// the inner &T may be omitted.
-				if ptr, ok := eltType.(*ast.StarExpr); ok {
-					if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND {
-						if inner, ok := addr.X.(*ast.CompositeLit); ok {
-							if match(nil, reflect.ValueOf(ptr.X), reflect.ValueOf(inner.Type)) {
-								inner.Type = nil // drop T
-								*px = inner      // drop &
-							}
-						}
-					}
-				}
+				s.simplifyLiteral(typ, eltType, x, px)
 			}
-
 			// node was simplified - stop walk (there are no subnodes to simplify)
 			return nil
 		}
@@ -113,6 +99,32 @@ func (s simplifier) Visit(node ast.Node) ast.Visitor {
 	return s
 }
 
+func (s simplifier) simplifyLiteral(typ reflect.Value, astType, x ast.Expr, px *ast.Expr) {
+	ast.Walk(s, x) // simplify x
+
+	// if the element is a composite literal and its literal type
+	// matches the outer literal's element type exactly, the inner
+	// literal type may be omitted
+	if inner, ok := x.(*ast.CompositeLit); ok {
+		if match(nil, typ, reflect.ValueOf(inner.Type)) {
+			inner.Type = nil
+		}
+	}
+	// if the outer literal's element type is a pointer type *T
+	// and the element is & of a composite literal of type T,
+	// the inner &T may be omitted.
+	if ptr, ok := astType.(*ast.StarExpr); ok {
+		if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND {
+			if inner, ok := addr.X.(*ast.CompositeLit); ok {
+				if match(nil, reflect.ValueOf(ptr.X), reflect.ValueOf(inner.Type)) {
+					inner.Type = nil // drop T
+					*px = inner      // drop &
+				}
+			}
+		}
+	}
+}
+
 func isBlank(x ast.Expr) bool {
 	ident, ok := x.(*ast.Ident)
 	return ok && ident.Name == "_"
diff --git a/src/cmd/gofmt/testdata/composites.golden b/src/cmd/gofmt/testdata/composites.golden
index fc9c98e..a06a69d 100644
--- a/src/cmd/gofmt/testdata/composites.golden
+++ b/src/cmd/gofmt/testdata/composites.golden
@@ -6,6 +6,10 @@ type T struct {
 	x, y int
 }
 
+type T2 struct {
+	w, z int
+}
+
 var _ = [42]T{
 	{},
 	{1, 2},
@@ -202,3 +206,13 @@ var pieces4 = []*Piece{
 	{2, 0, Point{4, 1}, []Point{{0, 0}, {1, 0}, {1, 0}, {1, 0}}, nil, nil},
 	{3, 0, Point{1, 4}, []Point{{0, 0}, {0, 1}, {0, 1}, {0, 1}}, nil, nil},
 }
+
+var _ = map[T]T2{
+	{1, 2}: {3, 4},
+	{5, 6}: {7, 8},
+}
+
+var _ = map[*T]*T2{
+	{1, 2}: {3, 4},
+	{5, 6}: {7, 8},
+}
diff --git a/src/cmd/gofmt/testdata/composites.input b/src/cmd/gofmt/testdata/composites.input
index fc7598a..9d28ac7 100644
--- a/src/cmd/gofmt/testdata/composites.input
+++ b/src/cmd/gofmt/testdata/composites.input
@@ -6,6 +6,10 @@ type T struct {
 	x, y int
 }
 
+type T2 struct {
+	w, z int
+}
+
 var _ = [42]T{
 	T{},
 	T{1, 2},
@@ -202,3 +206,13 @@ var pieces4 = []*Piece{
 	&Piece{2, 0, Point{4, 1}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil},
 	&Piece{3, 0, Point{1, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil},
 }
+
+var _ = map[T]T2{
+	T{1, 2}: T2{3, 4},
+	T{5, 6}: T2{7, 8},
+}
+
+var _ = map[*T]*T2{
+	&T{1, 2}: &T2{3, 4},
+	&T{5, 6}: &T2{7, 8},
+}
diff --git a/src/cmd/internal/browser/browser.go b/src/cmd/internal/browser/browser.go
new file mode 100644
index 0000000..897086f
--- /dev/null
+++ b/src/cmd/internal/browser/browser.go
@@ -0,0 +1,46 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package browser provides utilities for interacting with users' browsers.
+package browser
+
+import (
+	"os"
+	"os/exec"
+	"runtime"
+)
+
+// Commands returns a list of possible commands to use to open a url.
+func Commands() [][]string {
+	var cmds [][]string
+	if exe := os.Getenv("BROWSER"); exe != "" {
+		cmds = append(cmds, []string{exe})
+	}
+	switch runtime.GOOS {
+	case "darwin":
+		cmds = append(cmds, []string{"/usr/bin/open"})
+	case "windows":
+		cmds = append(cmds, []string{"cmd", "/c", "start"})
+	default:
+		cmds = append(cmds, []string{"xdg-open"})
+	}
+	cmds = append(cmds,
+		[]string{"chrome"},
+		[]string{"google-chrome"},
+		[]string{"chromium"},
+		[]string{"firefox"},
+	)
+	return cmds
+}
+
+// Open tries to open url in a browser and reports whether it succeeded.
+func Open(url string) bool {
+	for _, args := range Commands() {
+		cmd := exec.Command(args[0], append(args[1:], url)...)
+		if cmd.Start() == nil {
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
new file mode 100644
index 0000000..725f502
--- /dev/null
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -0,0 +1,604 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package dwarf generates DWARF debugging information.
+// DWARF generation is split between the compiler and the linker,
+// this package contains the shared code.
+package dwarf
+
+import (
+	"fmt"
+	"strings"
+)
+
+// InfoPrefix is the prefix for all the symbols containing DWARF info entries.
+const InfoPrefix = "go.info."
+
+// Sym represents a symbol.
+type Sym interface {
+}
+
+// A Var represents a local variable or a function parameter.
+type Var struct {
+	Name   string
+	Abbrev int // Either DW_ABRV_AUTO or DW_ABRV_PARAM
+	Offset int32
+	Type   Sym
+	Link   *Var
+}
+
+// A Context specifies how to add data to a Sym.
+type Context interface {
+	PtrSize() int
+	AddInt(s Sym, size int, i int64)
+	AddBytes(s Sym, b []byte)
+	AddAddress(s Sym, t interface{}, ofs int64)
+	AddSectionOffset(s Sym, size int, t interface{}, ofs int64)
+	AddString(s Sym, v string)
+	SymValue(s Sym) int64
+}
+
+// AppendUleb128 appends v to b using DWARF's unsigned LEB128 encoding.
+func AppendUleb128(b []byte, v uint64) []byte {
+	for {
+		c := uint8(v & 0x7f)
+		v >>= 7
+		if v != 0 {
+			c |= 0x80
+		}
+		b = append(b, c)
+		if c&0x80 == 0 {
+			break
+		}
+	}
+	return b
+}
+
+// AppendSleb128 appends v to b using DWARF's signed LEB128 encoding.
+func AppendSleb128(b []byte, v int64) []byte {
+	for {
+		c := uint8(v & 0x7f)
+		s := uint8(v & 0x40)
+		v >>= 7
+		if (v != -1 || s == 0) && (v != 0 || s != 0) {
+			c |= 0x80
+		}
+		b = append(b, c)
+		if c&0x80 == 0 {
+			break
+		}
+	}
+	return b
+}
+
+var encbuf [20]byte
+
+// AppendUleb128 appends v to s using DWARF's unsigned LEB128 encoding.
+func Uleb128put(ctxt Context, s Sym, v int64) {
+	b := AppendUleb128(encbuf[:0], uint64(v))
+	ctxt.AddBytes(s, b)
+}
+
+// AppendUleb128 appends v to s using DWARF's signed LEB128 encoding.
+func Sleb128put(ctxt Context, s Sym, v int64) {
+	b := AppendSleb128(encbuf[:0], v)
+	ctxt.AddBytes(s, b)
+}
+
+/*
+ * Defining Abbrevs.  This is hardcoded, and there will be
+ * only a handful of them.  The DWARF spec places no restriction on
+ * the ordering of attributes in the Abbrevs and DIEs, and we will
+ * always write them out in the order of declaration in the abbrev.
+ */
+type dwAttrForm struct {
+	attr uint16
+	form uint8
+}
+
+// Go-specific type attributes.
+const (
+	DW_AT_go_kind = 0x2900
+	DW_AT_go_key  = 0x2901
+	DW_AT_go_elem = 0x2902
+
+	DW_AT_internal_location = 253 // params and locals; not emitted
+)
+
+// Index into the abbrevs table below.
+// Keep in sync with ispubname() and ispubtype() below.
+// ispubtype considers >= NULLTYPE public
+const (
+	DW_ABRV_NULL = iota
+	DW_ABRV_COMPUNIT
+	DW_ABRV_FUNCTION
+	DW_ABRV_VARIABLE
+	DW_ABRV_AUTO
+	DW_ABRV_PARAM
+	DW_ABRV_STRUCTFIELD
+	DW_ABRV_FUNCTYPEPARAM
+	DW_ABRV_DOTDOTDOT
+	DW_ABRV_ARRAYRANGE
+	DW_ABRV_NULLTYPE
+	DW_ABRV_BASETYPE
+	DW_ABRV_ARRAYTYPE
+	DW_ABRV_CHANTYPE
+	DW_ABRV_FUNCTYPE
+	DW_ABRV_IFACETYPE
+	DW_ABRV_MAPTYPE
+	DW_ABRV_PTRTYPE
+	DW_ABRV_BARE_PTRTYPE // only for void*, no DW_AT_type attr to please gdb 6.
+	DW_ABRV_SLICETYPE
+	DW_ABRV_STRINGTYPE
+	DW_ABRV_STRUCTTYPE
+	DW_ABRV_TYPEDECL
+	DW_NABRV
+)
+
+type dwAbbrev struct {
+	tag      uint8
+	children uint8
+	attr     []dwAttrForm
+}
+
+var abbrevs = [DW_NABRV]dwAbbrev{
+	/* The mandatory DW_ABRV_NULL entry. */
+	{0, 0, []dwAttrForm{}},
+
+	/* COMPUNIT */
+	{
+		DW_TAG_compile_unit,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_language, DW_FORM_data1},
+			{DW_AT_low_pc, DW_FORM_addr},
+			{DW_AT_high_pc, DW_FORM_addr},
+			{DW_AT_stmt_list, DW_FORM_data4},
+			{DW_AT_comp_dir, DW_FORM_string},
+		},
+	},
+
+	/* FUNCTION */
+	{
+		DW_TAG_subprogram,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_low_pc, DW_FORM_addr},
+			{DW_AT_high_pc, DW_FORM_addr},
+			{DW_AT_external, DW_FORM_flag},
+		},
+	},
+
+	/* VARIABLE */
+	{
+		DW_TAG_variable,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_location, DW_FORM_block1},
+			{DW_AT_type, DW_FORM_ref_addr},
+			{DW_AT_external, DW_FORM_flag},
+		},
+	},
+
+	/* AUTO */
+	{
+		DW_TAG_variable,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_location, DW_FORM_block1},
+			{DW_AT_type, DW_FORM_ref_addr},
+		},
+	},
+
+	/* PARAM */
+	{
+		DW_TAG_formal_parameter,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_location, DW_FORM_block1},
+			{DW_AT_type, DW_FORM_ref_addr},
+		},
+	},
+
+	/* STRUCTFIELD */
+	{
+		DW_TAG_member,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_data_member_location, DW_FORM_block1},
+			{DW_AT_type, DW_FORM_ref_addr},
+		},
+	},
+
+	/* FUNCTYPEPARAM */
+	{
+		DW_TAG_formal_parameter,
+		DW_CHILDREN_no,
+
+		// No name!
+		[]dwAttrForm{
+			{DW_AT_type, DW_FORM_ref_addr},
+		},
+	},
+
+	/* DOTDOTDOT */
+	{
+		DW_TAG_unspecified_parameters,
+		DW_CHILDREN_no,
+		[]dwAttrForm{},
+	},
+
+	/* ARRAYRANGE */
+	{
+		DW_TAG_subrange_type,
+		DW_CHILDREN_no,
+
+		// No name!
+		[]dwAttrForm{
+			{DW_AT_type, DW_FORM_ref_addr},
+			{DW_AT_count, DW_FORM_udata},
+		},
+	},
+
+	// Below here are the types considered public by ispubtype
+	/* NULLTYPE */
+	{
+		DW_TAG_unspecified_type,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+		},
+	},
+
+	/* BASETYPE */
+	{
+		DW_TAG_base_type,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_encoding, DW_FORM_data1},
+			{DW_AT_byte_size, DW_FORM_data1},
+			{DW_AT_go_kind, DW_FORM_data1},
+		},
+	},
+
+	/* ARRAYTYPE */
+	// child is subrange with upper bound
+	{
+		DW_TAG_array_type,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_type, DW_FORM_ref_addr},
+			{DW_AT_byte_size, DW_FORM_udata},
+			{DW_AT_go_kind, DW_FORM_data1},
+		},
+	},
+
+	/* CHANTYPE */
+	{
+		DW_TAG_typedef,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_type, DW_FORM_ref_addr},
+			{DW_AT_go_kind, DW_FORM_data1},
+			{DW_AT_go_elem, DW_FORM_ref_addr},
+		},
+	},
+
+	/* FUNCTYPE */
+	{
+		DW_TAG_subroutine_type,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			// {DW_AT_type,	DW_FORM_ref_addr},
+			{DW_AT_go_kind, DW_FORM_data1},
+		},
+	},
+
+	/* IFACETYPE */
+	{
+		DW_TAG_typedef,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_type, DW_FORM_ref_addr},
+			{DW_AT_go_kind, DW_FORM_data1},
+		},
+	},
+
+	/* MAPTYPE */
+	{
+		DW_TAG_typedef,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_type, DW_FORM_ref_addr},
+			{DW_AT_go_kind, DW_FORM_data1},
+			{DW_AT_go_key, DW_FORM_ref_addr},
+			{DW_AT_go_elem, DW_FORM_ref_addr},
+		},
+	},
+
+	/* PTRTYPE */
+	{
+		DW_TAG_pointer_type,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_type, DW_FORM_ref_addr},
+			{DW_AT_go_kind, DW_FORM_data1},
+		},
+	},
+
+	/* BARE_PTRTYPE */
+	{
+		DW_TAG_pointer_type,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+		},
+	},
+
+	/* SLICETYPE */
+	{
+		DW_TAG_structure_type,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_byte_size, DW_FORM_udata},
+			{DW_AT_go_kind, DW_FORM_data1},
+			{DW_AT_go_elem, DW_FORM_ref_addr},
+		},
+	},
+
+	/* STRINGTYPE */
+	{
+		DW_TAG_structure_type,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_byte_size, DW_FORM_udata},
+			{DW_AT_go_kind, DW_FORM_data1},
+		},
+	},
+
+	/* STRUCTTYPE */
+	{
+		DW_TAG_structure_type,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_byte_size, DW_FORM_udata},
+			{DW_AT_go_kind, DW_FORM_data1},
+		},
+	},
+
+	/* TYPEDECL */
+	{
+		DW_TAG_typedef,
+		DW_CHILDREN_no,
+		[]dwAttrForm{
+			{DW_AT_name, DW_FORM_string},
+			{DW_AT_type, DW_FORM_ref_addr},
+		},
+	},
+}
+
+// GetAbbrev returns the contents of the .debug_abbrev section.
+func GetAbbrev() []byte {
+	var buf []byte
+	for i := 1; i < DW_NABRV; i++ {
+		// See section 7.5.3
+		buf = AppendUleb128(buf, uint64(i))
+
+		buf = AppendUleb128(buf, uint64(abbrevs[i].tag))
+		buf = append(buf, byte(abbrevs[i].children))
+		for _, f := range abbrevs[i].attr {
+			buf = AppendUleb128(buf, uint64(f.attr))
+			buf = AppendUleb128(buf, uint64(f.form))
+		}
+		buf = append(buf, 0, 0)
+	}
+	return append(buf, 0)
+}
+
+/*
+ * Debugging Information Entries and their attributes.
+ */
+
+// DWAttr represents an attribute of a DWDie.
+//
+// For DW_CLS_string and _block, value should contain the length, and
+// data the data, for _reference, value is 0 and data is a DWDie* to
+// the referenced instance, for all others, value is the whole thing
+// and data is null.
+type DWAttr struct {
+	Link  *DWAttr
+	Atr   uint16 // DW_AT_
+	Cls   uint8  // DW_CLS_
+	Value int64
+	Data  interface{}
+}
+
+// DWDie represents a DWARF debug info entry.
+type DWDie struct {
+	Abbrev int
+	Link   *DWDie
+	Child  *DWDie
+	Attr   *DWAttr
+	Sym    Sym
+}
+
+func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, data interface{}) error {
+	switch form {
+	case DW_FORM_addr: // address
+		ctxt.AddAddress(s, data, value)
+
+	case DW_FORM_block1: // block
+		if cls == DW_CLS_ADDRESS {
+			ctxt.AddInt(s, 1, int64(1+ctxt.PtrSize()))
+			ctxt.AddInt(s, 1, DW_OP_addr)
+			ctxt.AddAddress(s, data, 0)
+			break
+		}
+
+		value &= 0xff
+		ctxt.AddInt(s, 1, value)
+		p := data.([]byte)[:value]
+		ctxt.AddBytes(s, p)
+
+	case DW_FORM_block2: // block
+		value &= 0xffff
+
+		ctxt.AddInt(s, 2, value)
+		p := data.([]byte)[:value]
+		ctxt.AddBytes(s, p)
+
+	case DW_FORM_block4: // block
+		value &= 0xffffffff
+
+		ctxt.AddInt(s, 4, value)
+		p := data.([]byte)[:value]
+		ctxt.AddBytes(s, p)
+
+	case DW_FORM_block: // block
+		Uleb128put(ctxt, s, value)
+
+		p := data.([]byte)[:value]
+		ctxt.AddBytes(s, p)
+
+	case DW_FORM_data1: // constant
+		ctxt.AddInt(s, 1, value)
+
+	case DW_FORM_data2: // constant
+		ctxt.AddInt(s, 2, value)
+
+	case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr
+		if cls == DW_CLS_PTR { // DW_AT_stmt_list
+			ctxt.AddSectionOffset(s, 4, data, 0)
+			break
+		}
+		ctxt.AddInt(s, 4, value)
+
+	case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr
+		ctxt.AddInt(s, 8, value)
+
+	case DW_FORM_sdata: // constant
+		Sleb128put(ctxt, s, value)
+
+	case DW_FORM_udata: // constant
+		Uleb128put(ctxt, s, value)
+
+	case DW_FORM_string: // string
+		str := data.(string)
+		ctxt.AddString(s, str)
+		// TODO(ribrdb): verify padded strings are never used and remove this
+		for i := int64(len(str)); i < value; i++ {
+			ctxt.AddInt(s, 1, 0)
+		}
+
+	case DW_FORM_flag: // flag
+		if value != 0 {
+			ctxt.AddInt(s, 1, 1)
+		} else {
+			ctxt.AddInt(s, 1, 0)
+		}
+
+	// In DWARF 2 (which is what we claim to generate),
+	// the ref_addr is the same size as a normal address.
+	// In DWARF 3 it is always 32 bits, unless emitting a large
+	// (> 4 GB of debug info aka "64-bit") unit, which we don't implement.
+	case DW_FORM_ref_addr: // reference to a DIE in the .info section
+		if data == nil {
+			return fmt.Errorf("dwarf: null reference in %d", abbrev)
+		} else {
+			ctxt.AddSectionOffset(s, ctxt.PtrSize(), data, 0)
+		}
+
+	case DW_FORM_ref1, // reference within the compilation unit
+		DW_FORM_ref2,      // reference
+		DW_FORM_ref4,      // reference
+		DW_FORM_ref8,      // reference
+		DW_FORM_ref_udata, // reference
+
+		DW_FORM_strp,     // string
+		DW_FORM_indirect: // (see Section 7.5.3)
+		fallthrough
+	default:
+		return fmt.Errorf("dwarf: unsupported attribute form %d / class %d", form, cls)
+	}
+	return nil
+}
+
+// PutAttrs writes the attributes for a DIE to symbol 's'.
+//
+// Note that we can (and do) add arbitrary attributes to a DIE, but
+// only the ones actually listed in the Abbrev will be written out.
+func PutAttrs(ctxt Context, s Sym, abbrev int, attr *DWAttr) {
+Outer:
+	for _, f := range abbrevs[abbrev].attr {
+		for ap := attr; ap != nil; ap = ap.Link {
+			if ap.Atr == f.attr {
+				putattr(ctxt, s, abbrev, int(f.form), int(ap.Cls), ap.Value, ap.Data)
+				continue Outer
+			}
+		}
+
+		putattr(ctxt, s, abbrev, int(f.form), 0, 0, nil)
+	}
+}
+
+// HasChildren returns true if 'die' uses an abbrev that supports children.
+func HasChildren(die *DWDie) bool {
+	return abbrevs[die.Abbrev].children != 0
+}
+
+// PutFunc writes a DIE for a function to s.
+// It also writes child DIEs for each variable in vars.
+func PutFunc(ctxt Context, s Sym, name string, external bool, startPC Sym, size int64, vars *Var) {
+	Uleb128put(ctxt, s, DW_ABRV_FUNCTION)
+	putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
+	putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, 0, startPC)
+	putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, size+ctxt.SymValue(startPC), startPC)
+	var ev int64
+	if external {
+		ev = 1
+	}
+	putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
+	names := make(map[string]bool)
+	for v := vars; v != nil; v = v.Link {
+		if strings.Contains(v.Name, ".autotmp_") {
+			continue
+		}
+		var n string
+		if names[v.Name] {
+			n = fmt.Sprintf("%s#%d", v.Name, len(names))
+		} else {
+			n = v.Name
+		}
+		names[n] = true
+
+		Uleb128put(ctxt, s, int64(v.Abbrev))
+		putattr(ctxt, s, v.Abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
+		loc := append(encbuf[:0], DW_OP_call_frame_cfa)
+		if v.Offset != 0 {
+			loc = append(loc, DW_OP_consts)
+			loc = AppendSleb128(loc, int64(v.Offset))
+			loc = append(loc, DW_OP_plus)
+		}
+		putattr(ctxt, s, v.Abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
+		putattr(ctxt, s, v.Abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+
+	}
+	Uleb128put(ctxt, s, 0)
+}
diff --git a/src/cmd/internal/dwarf/dwarf_defs.go b/src/cmd/internal/dwarf/dwarf_defs.go
new file mode 100644
index 0000000..d1870b5
--- /dev/null
+++ b/src/cmd/internal/dwarf/dwarf_defs.go
@@ -0,0 +1,483 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package dwarf
+
+// Cut, pasted, tr-and-awk'ed from tables in
+// http://dwarfstd.org/doc/Dwarf3.pdf
+
+// Table 18
+const (
+	DW_TAG_array_type               = 0x01
+	DW_TAG_class_type               = 0x02
+	DW_TAG_entry_point              = 0x03
+	DW_TAG_enumeration_type         = 0x04
+	DW_TAG_formal_parameter         = 0x05
+	DW_TAG_imported_declaration     = 0x08
+	DW_TAG_label                    = 0x0a
+	DW_TAG_lexical_block            = 0x0b
+	DW_TAG_member                   = 0x0d
+	DW_TAG_pointer_type             = 0x0f
+	DW_TAG_reference_type           = 0x10
+	DW_TAG_compile_unit             = 0x11
+	DW_TAG_string_type              = 0x12
+	DW_TAG_structure_type           = 0x13
+	DW_TAG_subroutine_type          = 0x15
+	DW_TAG_typedef                  = 0x16
+	DW_TAG_union_type               = 0x17
+	DW_TAG_unspecified_parameters   = 0x18
+	DW_TAG_variant                  = 0x19
+	DW_TAG_common_block             = 0x1a
+	DW_TAG_common_inclusion         = 0x1b
+	DW_TAG_inheritance              = 0x1c
+	DW_TAG_inlined_subroutine       = 0x1d
+	DW_TAG_module                   = 0x1e
+	DW_TAG_ptr_to_member_type       = 0x1f
+	DW_TAG_set_type                 = 0x20
+	DW_TAG_subrange_type            = 0x21
+	DW_TAG_with_stmt                = 0x22
+	DW_TAG_access_declaration       = 0x23
+	DW_TAG_base_type                = 0x24
+	DW_TAG_catch_block              = 0x25
+	DW_TAG_const_type               = 0x26
+	DW_TAG_constant                 = 0x27
+	DW_TAG_enumerator               = 0x28
+	DW_TAG_file_type                = 0x29
+	DW_TAG_friend                   = 0x2a
+	DW_TAG_namelist                 = 0x2b
+	DW_TAG_namelist_item            = 0x2c
+	DW_TAG_packed_type              = 0x2d
+	DW_TAG_subprogram               = 0x2e
+	DW_TAG_template_type_parameter  = 0x2f
+	DW_TAG_template_value_parameter = 0x30
+	DW_TAG_thrown_type              = 0x31
+	DW_TAG_try_block                = 0x32
+	DW_TAG_variant_part             = 0x33
+	DW_TAG_variable                 = 0x34
+	DW_TAG_volatile_type            = 0x35
+	// Dwarf3
+	DW_TAG_dwarf_procedure  = 0x36
+	DW_TAG_restrict_type    = 0x37
+	DW_TAG_interface_type   = 0x38
+	DW_TAG_namespace        = 0x39
+	DW_TAG_imported_module  = 0x3a
+	DW_TAG_unspecified_type = 0x3b
+	DW_TAG_partial_unit     = 0x3c
+	DW_TAG_imported_unit    = 0x3d
+	DW_TAG_condition        = 0x3f
+	DW_TAG_shared_type      = 0x40
+	// Dwarf4
+	DW_TAG_type_unit             = 0x41
+	DW_TAG_rvalue_reference_type = 0x42
+	DW_TAG_template_alias        = 0x43
+
+	// User defined
+	DW_TAG_lo_user = 0x4080
+	DW_TAG_hi_user = 0xffff
+)
+
+// Table 19
+const (
+	DW_CHILDREN_no  = 0x00
+	DW_CHILDREN_yes = 0x01
+)
+
+// Not from the spec, but logically belongs here
+const (
+	DW_CLS_ADDRESS = 0x01 + iota
+	DW_CLS_BLOCK
+	DW_CLS_CONSTANT
+	DW_CLS_FLAG
+	DW_CLS_PTR // lineptr, loclistptr, macptr, rangelistptr
+	DW_CLS_REFERENCE
+	DW_CLS_ADDRLOC
+	DW_CLS_STRING
+)
+
+// Table 20
+const (
+	DW_AT_sibling              = 0x01 // reference
+	DW_AT_location             = 0x02 // block, loclistptr
+	DW_AT_name                 = 0x03 // string
+	DW_AT_ordering             = 0x09 // constant
+	DW_AT_byte_size            = 0x0b // block, constant, reference
+	DW_AT_bit_offset           = 0x0c // block, constant, reference
+	DW_AT_bit_size             = 0x0d // block, constant, reference
+	DW_AT_stmt_list            = 0x10 // lineptr
+	DW_AT_low_pc               = 0x11 // address
+	DW_AT_high_pc              = 0x12 // address
+	DW_AT_language             = 0x13 // constant
+	DW_AT_discr                = 0x15 // reference
+	DW_AT_discr_value          = 0x16 // constant
+	DW_AT_visibility           = 0x17 // constant
+	DW_AT_import               = 0x18 // reference
+	DW_AT_string_length        = 0x19 // block, loclistptr
+	DW_AT_common_reference     = 0x1a // reference
+	DW_AT_comp_dir             = 0x1b // string
+	DW_AT_const_value          = 0x1c // block, constant, string
+	DW_AT_containing_type      = 0x1d // reference
+	DW_AT_default_value        = 0x1e // reference
+	DW_AT_inline               = 0x20 // constant
+	DW_AT_is_optional          = 0x21 // flag
+	DW_AT_lower_bound          = 0x22 // block, constant, reference
+	DW_AT_producer             = 0x25 // string
+	DW_AT_prototyped           = 0x27 // flag
+	DW_AT_return_addr          = 0x2a // block, loclistptr
+	DW_AT_start_scope          = 0x2c // constant
+	DW_AT_bit_stride           = 0x2e // constant
+	DW_AT_upper_bound          = 0x2f // block, constant, reference
+	DW_AT_abstract_origin      = 0x31 // reference
+	DW_AT_accessibility        = 0x32 // constant
+	DW_AT_address_class        = 0x33 // constant
+	DW_AT_artificial           = 0x34 // flag
+	DW_AT_base_types           = 0x35 // reference
+	DW_AT_calling_convention   = 0x36 // constant
+	DW_AT_count                = 0x37 // block, constant, reference
+	DW_AT_data_member_location = 0x38 // block, constant, loclistptr
+	DW_AT_decl_column          = 0x39 // constant
+	DW_AT_decl_file            = 0x3a // constant
+	DW_AT_decl_line            = 0x3b // constant
+	DW_AT_declaration          = 0x3c // flag
+	DW_AT_discr_list           = 0x3d // block
+	DW_AT_encoding             = 0x3e // constant
+	DW_AT_external             = 0x3f // flag
+	DW_AT_frame_base           = 0x40 // block, loclistptr
+	DW_AT_friend               = 0x41 // reference
+	DW_AT_identifier_case      = 0x42 // constant
+	DW_AT_macro_info           = 0x43 // macptr
+	DW_AT_namelist_item        = 0x44 // block
+	DW_AT_priority             = 0x45 // reference
+	DW_AT_segment              = 0x46 // block, loclistptr
+	DW_AT_specification        = 0x47 // reference
+	DW_AT_static_link          = 0x48 // block, loclistptr
+	DW_AT_type                 = 0x49 // reference
+	DW_AT_use_location         = 0x4a // block, loclistptr
+	DW_AT_variable_parameter   = 0x4b // flag
+	DW_AT_virtuality           = 0x4c // constant
+	DW_AT_vtable_elem_location = 0x4d // block, loclistptr
+	// Dwarf3
+	DW_AT_allocated      = 0x4e // block, constant, reference
+	DW_AT_associated     = 0x4f // block, constant, reference
+	DW_AT_data_location  = 0x50 // block
+	DW_AT_byte_stride    = 0x51 // block, constant, reference
+	DW_AT_entry_pc       = 0x52 // address
+	DW_AT_use_UTF8       = 0x53 // flag
+	DW_AT_extension      = 0x54 // reference
+	DW_AT_ranges         = 0x55 // rangelistptr
+	DW_AT_trampoline     = 0x56 // address, flag, reference, string
+	DW_AT_call_column    = 0x57 // constant
+	DW_AT_call_file      = 0x58 // constant
+	DW_AT_call_line      = 0x59 // constant
+	DW_AT_description    = 0x5a // string
+	DW_AT_binary_scale   = 0x5b // constant
+	DW_AT_decimal_scale  = 0x5c // constant
+	DW_AT_small          = 0x5d // reference
+	DW_AT_decimal_sign   = 0x5e // constant
+	DW_AT_digit_count    = 0x5f // constant
+	DW_AT_picture_string = 0x60 // string
+	DW_AT_mutable        = 0x61 // flag
+	DW_AT_threads_scaled = 0x62 // flag
+	DW_AT_explicit       = 0x63 // flag
+	DW_AT_object_pointer = 0x64 // reference
+	DW_AT_endianity      = 0x65 // constant
+	DW_AT_elemental      = 0x66 // flag
+	DW_AT_pure           = 0x67 // flag
+	DW_AT_recursive      = 0x68 // flag
+
+	DW_AT_lo_user = 0x2000 // ---
+	DW_AT_hi_user = 0x3fff // ---
+)
+
+// Table 21
+const (
+	DW_FORM_addr      = 0x01 // address
+	DW_FORM_block2    = 0x03 // block
+	DW_FORM_block4    = 0x04 // block
+	DW_FORM_data2     = 0x05 // constant
+	DW_FORM_data4     = 0x06 // constant, lineptr, loclistptr, macptr, rangelistptr
+	DW_FORM_data8     = 0x07 // constant, lineptr, loclistptr, macptr, rangelistptr
+	DW_FORM_string    = 0x08 // string
+	DW_FORM_block     = 0x09 // block
+	DW_FORM_block1    = 0x0a // block
+	DW_FORM_data1     = 0x0b // constant
+	DW_FORM_flag      = 0x0c // flag
+	DW_FORM_sdata     = 0x0d // constant
+	DW_FORM_strp      = 0x0e // string
+	DW_FORM_udata     = 0x0f // constant
+	DW_FORM_ref_addr  = 0x10 // reference
+	DW_FORM_ref1      = 0x11 // reference
+	DW_FORM_ref2      = 0x12 // reference
+	DW_FORM_ref4      = 0x13 // reference
+	DW_FORM_ref8      = 0x14 // reference
+	DW_FORM_ref_udata = 0x15 // reference
+	DW_FORM_indirect  = 0x16 // (see Section 7.5.3)
+)
+
+// Table 24 (#operands, notes)
+const (
+	DW_OP_addr                = 0x03 // 1 constant address (size target specific)
+	DW_OP_deref               = 0x06 // 0
+	DW_OP_const1u             = 0x08 // 1 1-byte constant
+	DW_OP_const1s             = 0x09 // 1 1-byte constant
+	DW_OP_const2u             = 0x0a // 1 2-byte constant
+	DW_OP_const2s             = 0x0b // 1 2-byte constant
+	DW_OP_const4u             = 0x0c // 1 4-byte constant
+	DW_OP_const4s             = 0x0d // 1 4-byte constant
+	DW_OP_const8u             = 0x0e // 1 8-byte constant
+	DW_OP_const8s             = 0x0f // 1 8-byte constant
+	DW_OP_constu              = 0x10 // 1 ULEB128 constant
+	DW_OP_consts              = 0x11 // 1 SLEB128 constant
+	DW_OP_dup                 = 0x12 // 0
+	DW_OP_drop                = 0x13 // 0
+	DW_OP_over                = 0x14 // 0
+	DW_OP_pick                = 0x15 // 1 1-byte stack index
+	DW_OP_swap                = 0x16 // 0
+	DW_OP_rot                 = 0x17 // 0
+	DW_OP_xderef              = 0x18 // 0
+	DW_OP_abs                 = 0x19 // 0
+	DW_OP_and                 = 0x1a // 0
+	DW_OP_div                 = 0x1b // 0
+	DW_OP_minus               = 0x1c // 0
+	DW_OP_mod                 = 0x1d // 0
+	DW_OP_mul                 = 0x1e // 0
+	DW_OP_neg                 = 0x1f // 0
+	DW_OP_not                 = 0x20 // 0
+	DW_OP_or                  = 0x21 // 0
+	DW_OP_plus                = 0x22 // 0
+	DW_OP_plus_uconst         = 0x23 // 1 ULEB128 addend
+	DW_OP_shl                 = 0x24 // 0
+	DW_OP_shr                 = 0x25 // 0
+	DW_OP_shra                = 0x26 // 0
+	DW_OP_xor                 = 0x27 // 0
+	DW_OP_skip                = 0x2f // 1 signed 2-byte constant
+	DW_OP_bra                 = 0x28 // 1 signed 2-byte constant
+	DW_OP_eq                  = 0x29 // 0
+	DW_OP_ge                  = 0x2a // 0
+	DW_OP_gt                  = 0x2b // 0
+	DW_OP_le                  = 0x2c // 0
+	DW_OP_lt                  = 0x2d // 0
+	DW_OP_ne                  = 0x2e // 0
+	DW_OP_lit0                = 0x30 // 0 ...
+	DW_OP_lit31               = 0x4f // 0 literals 0..31 = (DW_OP_lit0 + literal)
+	DW_OP_reg0                = 0x50 // 0 ..
+	DW_OP_reg31               = 0x6f // 0 reg 0..31 = (DW_OP_reg0 + regnum)
+	DW_OP_breg0               = 0x70 // 1 ...
+	DW_OP_breg31              = 0x8f // 1 SLEB128 offset base register 0..31 = (DW_OP_breg0 + regnum)
+	DW_OP_regx                = 0x90 // 1 ULEB128 register
+	DW_OP_fbreg               = 0x91 // 1 SLEB128 offset
+	DW_OP_bregx               = 0x92 // 2 ULEB128 register followed by SLEB128 offset
+	DW_OP_piece               = 0x93 // 1 ULEB128 size of piece addressed
+	DW_OP_deref_size          = 0x94 // 1 1-byte size of data retrieved
+	DW_OP_xderef_size         = 0x95 // 1 1-byte size of data retrieved
+	DW_OP_nop                 = 0x96 // 0
+	DW_OP_push_object_address = 0x97 // 0
+	DW_OP_call2               = 0x98 // 1 2-byte offset of DIE
+	DW_OP_call4               = 0x99 // 1 4-byte offset of DIE
+	DW_OP_call_ref            = 0x9a // 1 4- or 8-byte offset of DIE
+	DW_OP_form_tls_address    = 0x9b // 0
+	DW_OP_call_frame_cfa      = 0x9c // 0
+	DW_OP_bit_piece           = 0x9d // 2
+	DW_OP_lo_user             = 0xe0
+	DW_OP_hi_user             = 0xff
+)
+
+// Table 25
+const (
+	DW_ATE_address         = 0x01
+	DW_ATE_boolean         = 0x02
+	DW_ATE_complex_float   = 0x03
+	DW_ATE_float           = 0x04
+	DW_ATE_signed          = 0x05
+	DW_ATE_signed_char     = 0x06
+	DW_ATE_unsigned        = 0x07
+	DW_ATE_unsigned_char   = 0x08
+	DW_ATE_imaginary_float = 0x09
+	DW_ATE_packed_decimal  = 0x0a
+	DW_ATE_numeric_string  = 0x0b
+	DW_ATE_edited          = 0x0c
+	DW_ATE_signed_fixed    = 0x0d
+	DW_ATE_unsigned_fixed  = 0x0e
+	DW_ATE_decimal_float   = 0x0f
+	DW_ATE_lo_user         = 0x80
+	DW_ATE_hi_user         = 0xff
+)
+
+// Table 26
+const (
+	DW_DS_unsigned           = 0x01
+	DW_DS_leading_overpunch  = 0x02
+	DW_DS_trailing_overpunch = 0x03
+	DW_DS_leading_separate   = 0x04
+	DW_DS_trailing_separate  = 0x05
+)
+
+// Table 27
+const (
+	DW_END_default = 0x00
+	DW_END_big     = 0x01
+	DW_END_little  = 0x02
+	DW_END_lo_user = 0x40
+	DW_END_hi_user = 0xff
+)
+
+// Table 28
+const (
+	DW_ACCESS_public    = 0x01
+	DW_ACCESS_protected = 0x02
+	DW_ACCESS_private   = 0x03
+)
+
+// Table 29
+const (
+	DW_VIS_local     = 0x01
+	DW_VIS_exported  = 0x02
+	DW_VIS_qualified = 0x03
+)
+
+// Table 30
+const (
+	DW_VIRTUALITY_none         = 0x00
+	DW_VIRTUALITY_virtual      = 0x01
+	DW_VIRTUALITY_pure_virtual = 0x02
+)
+
+// Table 31
+const (
+	DW_LANG_C89         = 0x0001
+	DW_LANG_C           = 0x0002
+	DW_LANG_Ada83       = 0x0003
+	DW_LANG_C_plus_plus = 0x0004
+	DW_LANG_Cobol74     = 0x0005
+	DW_LANG_Cobol85     = 0x0006
+	DW_LANG_Fortran77   = 0x0007
+	DW_LANG_Fortran90   = 0x0008
+	DW_LANG_Pascal83    = 0x0009
+	DW_LANG_Modula2     = 0x000a
+	// Dwarf3
+	DW_LANG_Java           = 0x000b
+	DW_LANG_C99            = 0x000c
+	DW_LANG_Ada95          = 0x000d
+	DW_LANG_Fortran95      = 0x000e
+	DW_LANG_PLI            = 0x000f
+	DW_LANG_ObjC           = 0x0010
+	DW_LANG_ObjC_plus_plus = 0x0011
+	DW_LANG_UPC            = 0x0012
+	DW_LANG_D              = 0x0013
+	// Dwarf4
+	DW_LANG_Python = 0x0014
+	// Dwarf5
+	DW_LANG_Go = 0x0016
+
+	DW_LANG_lo_user = 0x8000
+	DW_LANG_hi_user = 0xffff
+)
+
+// Table 32
+const (
+	DW_ID_case_sensitive   = 0x00
+	DW_ID_up_case          = 0x01
+	DW_ID_down_case        = 0x02
+	DW_ID_case_insensitive = 0x03
+)
+
+// Table 33
+const (
+	DW_CC_normal  = 0x01
+	DW_CC_program = 0x02
+	DW_CC_nocall  = 0x03
+	DW_CC_lo_user = 0x40
+	DW_CC_hi_user = 0xff
+)
+
+// Table 34
+const (
+	DW_INL_not_inlined          = 0x00
+	DW_INL_inlined              = 0x01
+	DW_INL_declared_not_inlined = 0x02
+	DW_INL_declared_inlined     = 0x03
+)
+
+// Table 35
+const (
+	DW_ORD_row_major = 0x00
+	DW_ORD_col_major = 0x01
+)
+
+// Table 36
+const (
+	DW_DSC_label = 0x00
+	DW_DSC_range = 0x01
+)
+
+// Table 37
+const (
+	DW_LNS_copy             = 0x01
+	DW_LNS_advance_pc       = 0x02
+	DW_LNS_advance_line     = 0x03
+	DW_LNS_set_file         = 0x04
+	DW_LNS_set_column       = 0x05
+	DW_LNS_negate_stmt      = 0x06
+	DW_LNS_set_basic_block  = 0x07
+	DW_LNS_const_add_pc     = 0x08
+	DW_LNS_fixed_advance_pc = 0x09
+	// Dwarf3
+	DW_LNS_set_prologue_end   = 0x0a
+	DW_LNS_set_epilogue_begin = 0x0b
+	DW_LNS_set_isa            = 0x0c
+)
+
+// Table 38
+const (
+	DW_LNE_end_sequence = 0x01
+	DW_LNE_set_address  = 0x02
+	DW_LNE_define_file  = 0x03
+	DW_LNE_lo_user      = 0x80
+	DW_LNE_hi_user      = 0xff
+)
+
+// Table 39
+const (
+	DW_MACINFO_define     = 0x01
+	DW_MACINFO_undef      = 0x02
+	DW_MACINFO_start_file = 0x03
+	DW_MACINFO_end_file   = 0x04
+	DW_MACINFO_vendor_ext = 0xff
+)
+
+// Table 40.
+const (
+	// operand,...
+	DW_CFA_nop              = 0x00
+	DW_CFA_set_loc          = 0x01 // address
+	DW_CFA_advance_loc1     = 0x02 // 1-byte delta
+	DW_CFA_advance_loc2     = 0x03 // 2-byte delta
+	DW_CFA_advance_loc4     = 0x04 // 4-byte delta
+	DW_CFA_offset_extended  = 0x05 // ULEB128 register, ULEB128 offset
+	DW_CFA_restore_extended = 0x06 // ULEB128 register
+	DW_CFA_undefined        = 0x07 // ULEB128 register
+	DW_CFA_same_value       = 0x08 // ULEB128 register
+	DW_CFA_register         = 0x09 // ULEB128 register, ULEB128 register
+	DW_CFA_remember_state   = 0x0a
+	DW_CFA_restore_state    = 0x0b
+
+	DW_CFA_def_cfa            = 0x0c // ULEB128 register, ULEB128 offset
+	DW_CFA_def_cfa_register   = 0x0d // ULEB128 register
+	DW_CFA_def_cfa_offset     = 0x0e // ULEB128 offset
+	DW_CFA_def_cfa_expression = 0x0f // BLOCK
+	DW_CFA_expression         = 0x10 // ULEB128 register, BLOCK
+	DW_CFA_offset_extended_sf = 0x11 // ULEB128 register, SLEB128 offset
+	DW_CFA_def_cfa_sf         = 0x12 // ULEB128 register, SLEB128 offset
+	DW_CFA_def_cfa_offset_sf  = 0x13 // SLEB128 offset
+	DW_CFA_val_offset         = 0x14 // ULEB128, ULEB128
+	DW_CFA_val_offset_sf      = 0x15 // ULEB128, SLEB128
+	DW_CFA_val_expression     = 0x16 // ULEB128, BLOCK
+
+	DW_CFA_lo_user = 0x1c
+	DW_CFA_hi_user = 0x3f
+
+	// Opcodes that take an addend operand.
+	DW_CFA_advance_loc = 0x1 << 6 // +delta
+	DW_CFA_offset      = 0x2 << 6 // +register (ULEB128 offset)
+	DW_CFA_restore     = 0x3 << 6 // +register
+)
diff --git a/src/cmd/internal/gcprog/gcprog.go b/src/cmd/internal/gcprog/gcprog.go
index 7880917..c8bf206 100644
--- a/src/cmd/internal/gcprog/gcprog.go
+++ b/src/cmd/internal/gcprog/gcprog.go
@@ -37,7 +37,6 @@ const progMaxLiteral = 127 // maximum n for literal n bit code
 // to describe the data type, and then finally call End.
 type Writer struct {
 	writeByte func(byte)
-	symoff    int
 	index     int64
 	b         [progMaxLiteral]byte
 	nb        int
diff --git a/src/cmd/internal/goobj/read.go b/src/cmd/internal/goobj/read.go
index 214f65c..ff4aa59 100644
--- a/src/cmd/internal/goobj/read.go
+++ b/src/cmd/internal/goobj/read.go
@@ -29,50 +29,48 @@ type SymKind int
 // TODO(rsc): Give idiomatic Go names.
 // TODO(rsc): Reduce the number of symbol types in the object files.
 const (
-	_ SymKind = iota
-
 	// readonly, executable
-	STEXT      SymKind = obj.STEXT
-	SELFRXSECT SymKind = obj.SELFRXSECT
+	STEXT      = SymKind(obj.STEXT)
+	SELFRXSECT = SymKind(obj.SELFRXSECT)
 
 	// readonly, non-executable
-	STYPE      SymKind = obj.STYPE
-	SSTRING    SymKind = obj.SSTRING
-	SGOSTRING  SymKind = obj.SGOSTRING
-	SGOFUNC    SymKind = obj.SGOFUNC
-	SRODATA    SymKind = obj.SRODATA
-	SFUNCTAB   SymKind = obj.SFUNCTAB
-	STYPELINK  SymKind = obj.STYPELINK
-	SITABLINK  SymKind = obj.SITABLINK
-	SSYMTAB    SymKind = obj.SSYMTAB // TODO: move to unmapped section
-	SPCLNTAB   SymKind = obj.SPCLNTAB
-	SELFROSECT SymKind = obj.SELFROSECT
+	STYPE      = SymKind(obj.STYPE)
+	SSTRING    = SymKind(obj.SSTRING)
+	SGOSTRING  = SymKind(obj.SGOSTRING)
+	SGOFUNC    = SymKind(obj.SGOFUNC)
+	SRODATA    = SymKind(obj.SRODATA)
+	SFUNCTAB   = SymKind(obj.SFUNCTAB)
+	STYPELINK  = SymKind(obj.STYPELINK)
+	SITABLINK  = SymKind(obj.SITABLINK)
+	SSYMTAB    = SymKind(obj.SSYMTAB) // TODO: move to unmapped section
+	SPCLNTAB   = SymKind(obj.SPCLNTAB)
+	SELFROSECT = SymKind(obj.SELFROSECT)
 
 	// writable, non-executable
-	SMACHOPLT  SymKind = obj.SMACHOPLT
-	SELFSECT   SymKind = obj.SELFSECT
-	SMACHO     SymKind = obj.SMACHO // Mach-O __nl_symbol_ptr
-	SMACHOGOT  SymKind = obj.SMACHOGOT
-	SWINDOWS   SymKind = obj.SWINDOWS
-	SELFGOT    SymKind = obj.SELFGOT
-	SNOPTRDATA SymKind = obj.SNOPTRDATA
-	SINITARR   SymKind = obj.SINITARR
-	SDATA      SymKind = obj.SDATA
-	SBSS       SymKind = obj.SBSS
-	SNOPTRBSS  SymKind = obj.SNOPTRBSS
-	STLSBSS    SymKind = obj.STLSBSS
+	SMACHOPLT  = SymKind(obj.SMACHOPLT)
+	SELFSECT   = SymKind(obj.SELFSECT)
+	SMACHO     = SymKind(obj.SMACHO) // Mach-O __nl_symbol_ptr
+	SMACHOGOT  = SymKind(obj.SMACHOGOT)
+	SWINDOWS   = SymKind(obj.SWINDOWS)
+	SELFGOT    = SymKind(obj.SELFGOT)
+	SNOPTRDATA = SymKind(obj.SNOPTRDATA)
+	SINITARR   = SymKind(obj.SINITARR)
+	SDATA      = SymKind(obj.SDATA)
+	SBSS       = SymKind(obj.SBSS)
+	SNOPTRBSS  = SymKind(obj.SNOPTRBSS)
+	STLSBSS    = SymKind(obj.STLSBSS)
 
 	// not mapped
-	SXREF             SymKind = obj.SXREF
-	SMACHOSYMSTR      SymKind = obj.SMACHOSYMSTR
-	SMACHOSYMTAB      SymKind = obj.SMACHOSYMTAB
-	SMACHOINDIRECTPLT SymKind = obj.SMACHOINDIRECTPLT
-	SMACHOINDIRECTGOT SymKind = obj.SMACHOINDIRECTGOT
-	SFILE             SymKind = obj.SFILE
-	SFILEPATH         SymKind = obj.SFILEPATH
-	SCONST            SymKind = obj.SCONST
-	SDYNIMPORT        SymKind = obj.SDYNIMPORT
-	SHOSTOBJ          SymKind = obj.SHOSTOBJ
+	SXREF             = SymKind(obj.SXREF)
+	SMACHOSYMSTR      = SymKind(obj.SMACHOSYMSTR)
+	SMACHOSYMTAB      = SymKind(obj.SMACHOSYMTAB)
+	SMACHOINDIRECTPLT = SymKind(obj.SMACHOINDIRECTPLT)
+	SMACHOINDIRECTGOT = SymKind(obj.SMACHOINDIRECTGOT)
+	SFILE             = SymKind(obj.SFILE)
+	SFILEPATH         = SymKind(obj.SFILEPATH)
+	SCONST            = SymKind(obj.SCONST)
+	SDYNIMPORT        = SymKind(obj.SDYNIMPORT)
+	SHOSTOBJ          = SymKind(obj.SHOSTOBJ)
 )
 
 var symKindStrings = []string{
@@ -163,7 +161,7 @@ type Data struct {
 // A Reloc describes a relocation applied to a memory image to refer
 // to an address within a particular symbol.
 type Reloc struct {
-	// The bytes at [Offset, Offset+Size) within the memory image
+	// The bytes at [Offset, Offset+Size) within the containing Sym
 	// should be updated to refer to the address Add bytes after the start
 	// of the symbol Sym.
 	Offset int
@@ -174,7 +172,7 @@ type Reloc struct {
 	// The Type records the form of address expected in the bytes
 	// described by the previous fields: absolute, PC-relative, and so on.
 	// TODO(rsc): The interpretation of Type is not exposed by this package.
-	Type int
+	Type obj.RelocType
 }
 
 // A Var describes a variable in a function stack frame: a declared
@@ -220,6 +218,7 @@ type Package struct {
 	SymRefs    []SymID  // list of symbol names and versions referred to by this pack
 	Syms       []*Sym   // symbols defined by this package
 	MaxVersion int      // maximum Version in any SymID in Syms
+	Arch       string   // architecture
 }
 
 var (
@@ -243,7 +242,6 @@ type objReader struct {
 	dataOffset int64
 	limit      int64
 	tmp        [256]byte
-	pkg        string
 	pkgprefix  string
 }
 
@@ -561,14 +559,13 @@ func (r *objReader) parseArchive() error {
 // The format of that part is defined in a comment at the top
 // of src/liblink/objfile.c.
 func (r *objReader) parseObject(prefix []byte) error {
-	// TODO(rsc): Maybe use prefix and the initial input to
-	// record the header line from the file, which would
-	// give the architecture and other version information.
-
 	r.p.MaxVersion++
+	h := make([]byte, 0, 256)
+	h = append(h, prefix...)
 	var c1, c2, c3 byte
 	for {
 		c1, c2, c3 = c2, c3, r.readByte()
+		h = append(h, c3)
 		// The new export format can contain 0 bytes.
 		// Don't consider them errors, only look for r.err != nil.
 		if r.err != nil {
@@ -579,6 +576,12 @@ func (r *objReader) parseObject(prefix []byte) error {
 		}
 	}
 
+	hs := strings.Fields(string(h))
+	if len(hs) >= 4 {
+		r.p.Arch = hs[3]
+	}
+	// TODO: extract OS + build ID if/when we need it
+
 	r.readFull(r.tmp[:8])
 	if !bytes.Equal(r.tmp[:8], []byte("\x00\x00go17ld")) {
 		return r.error(errCorruptObject)
@@ -643,7 +646,7 @@ func (r *objReader) parseObject(prefix []byte) error {
 			rel := &s.Reloc[i]
 			rel.Offset = r.readInt()
 			rel.Size = r.readInt()
-			rel.Type = r.readInt()
+			rel.Type = obj.RelocType(r.readInt())
 			rel.Add = r.readInt()
 			rel.Sym = r.readSymID()
 		}
@@ -693,3 +696,18 @@ func (r *objReader) parseObject(prefix []byte) error {
 
 	return nil
 }
+
+func (r *Reloc) String(insnOffset uint64) string {
+	delta := r.Offset - int(insnOffset)
+	s := fmt.Sprintf("[%d:%d]%s", delta, delta+r.Size, r.Type)
+	if r.Sym.Name != "" {
+		if r.Add != 0 {
+			return fmt.Sprintf("%s:%s+%d", s, r.Sym.Name, r.Add)
+		}
+		return fmt.Sprintf("%s:%s", s, r.Sym.Name)
+	}
+	if r.Add != 0 {
+		return fmt.Sprintf("%s:%d", s, r.Add)
+	}
+	return s
+}
diff --git a/src/cmd/internal/obj/addrtype_string.go b/src/cmd/internal/obj/addrtype_string.go
new file mode 100644
index 0000000..48d498d
--- /dev/null
+++ b/src/cmd/internal/obj/addrtype_string.go
@@ -0,0 +1,27 @@
+// Code generated by "stringer -type AddrType cmd/internal/obj"; DO NOT EDIT
+
+package obj
+
+import "fmt"
+
+const (
+	_AddrType_name_0 = "TYPE_NONE"
+	_AddrType_name_1 = "TYPE_BRANCHTYPE_TEXTSIZETYPE_MEMTYPE_CONSTTYPE_FCONSTTYPE_SCONSTTYPE_REGTYPE_ADDRTYPE_SHIFTTYPE_REGREGTYPE_REGREG2TYPE_INDIRTYPE_REGLIST"
+)
+
+var (
+	_AddrType_index_0 = [...]uint8{0, 9}
+	_AddrType_index_1 = [...]uint8{0, 11, 24, 32, 42, 53, 64, 72, 81, 91, 102, 114, 124, 136}
+)
+
+func (i AddrType) String() string {
+	switch {
+	case i == 0:
+		return _AddrType_name_0
+	case 6 <= i && i <= 18:
+		i -= 6
+		return _AddrType_name_1[_AddrType_index_1[i]:_AddrType_index_1[i+1]]
+	default:
+		return fmt.Sprintf("AddrType(%d)", i)
+	}
+}
diff --git a/src/cmd/internal/obj/arm/a.out.go b/src/cmd/internal/obj/arm/a.out.go
index 4234b59..ad19f2d 100644
--- a/src/cmd/internal/obj/arm/a.out.go
+++ b/src/cmd/internal/obj/arm/a.out.go
@@ -1,5 +1,5 @@
 // Inferno utils/5c/5.out.h
-// http://code.google.com/p/inferno-os/source/browse/utils/5c/5.out.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5c/5.out.h
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -234,6 +234,8 @@ const (
 	ASQRTD
 	AABSF
 	AABSD
+	ANEGF
+	ANEGD
 
 	ASRL
 	ASRA
diff --git a/src/cmd/internal/obj/arm/anames.go b/src/cmd/internal/obj/arm/anames.go
index 0ef68a6..6d7db2d 100644
--- a/src/cmd/internal/obj/arm/anames.go
+++ b/src/cmd/internal/obj/arm/anames.go
@@ -59,6 +59,8 @@ var Anames = []string{
 	"SQRTD",
 	"ABSF",
 	"ABSD",
+	"NEGF",
+	"NEGD",
 	"SRL",
 	"SRA",
 	"SLL",
diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go
index d37091f..a1213bc 100644
--- a/src/cmd/internal/obj/arm/asm5.go
+++ b/src/cmd/internal/obj/arm/asm5.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/span.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/span.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -648,7 +648,7 @@ func span5(ctxt *obj.Link, cursym *obj.LSym) {
 	var out [6 + 3]uint32
 	for {
 		if ctxt.Debugvlog != 0 {
-			fmt.Fprintf(ctxt.Bso, "%5.2f span1\n", obj.Cputime())
+			ctxt.Logf("%5.2f span1\n", obj.Cputime())
 		}
 		bflag = 0
 		c = 0
@@ -1026,16 +1026,15 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 
 		case obj.NAME_AUTO:
 			ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset
-			t := int(immaddr(int32(ctxt.Instoffset)))
-			if t != 0 {
+			if t := immaddr(int32(ctxt.Instoffset)); t != 0 {
 				if immhalf(int32(ctxt.Instoffset)) {
-					if immfloat(int32(t)) {
+					if immfloat(t) {
 						return C_HFAUTO
 					}
 					return C_HAUTO
 				}
 
-				if immfloat(int32(t)) {
+				if immfloat(t) {
 					return C_FAUTO
 				}
 				return C_SAUTO
@@ -1045,16 +1044,15 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 
 		case obj.NAME_PARAM:
 			ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + 4
-			t := int(immaddr(int32(ctxt.Instoffset)))
-			if t != 0 {
+			if t := immaddr(int32(ctxt.Instoffset)); t != 0 {
 				if immhalf(int32(ctxt.Instoffset)) {
-					if immfloat(int32(t)) {
+					if immfloat(t) {
 						return C_HFAUTO
 					}
 					return C_HAUTO
 				}
 
-				if immfloat(int32(t)) {
+				if immfloat(t) {
 					return C_FAUTO
 				}
 				return C_SAUTO
@@ -1064,20 +1062,18 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 
 		case obj.NAME_NONE:
 			ctxt.Instoffset = a.Offset
-			t := int(immaddr(int32(ctxt.Instoffset)))
-			if t != 0 {
+			if t := immaddr(int32(ctxt.Instoffset)); t != 0 {
 				if immhalf(int32(ctxt.Instoffset)) { /* n.b. that it will also satisfy immrot */
-					if immfloat(int32(t)) {
+					if immfloat(t) {
 						return C_HFOREG
 					}
 					return C_HOREG
 				}
 
-				if immfloat(int32(t)) {
+				if immfloat(t) {
 					return C_FOREG /* n.b. that it will also satisfy immrot */
 				}
-				t := int(immrot(uint32(ctxt.Instoffset)))
-				if t != 0 {
+				if immrot(uint32(ctxt.Instoffset)) != 0 {
 					return C_SROREG
 				}
 				if immhalf(int32(ctxt.Instoffset)) {
@@ -1086,8 +1082,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 				return C_SOREG
 			}
 
-			t = int(immrot(uint32(ctxt.Instoffset)))
-			if t != 0 {
+			if immrot(uint32(ctxt.Instoffset)) != 0 {
 				return C_ROREG
 			}
 			return C_LOREG
@@ -1116,12 +1111,10 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 				return aconsize(ctxt)
 			}
 
-			t := int(immrot(uint32(ctxt.Instoffset)))
-			if t != 0 {
+			if immrot(uint32(ctxt.Instoffset)) != 0 {
 				return C_RCON
 			}
-			t = int(immrot(^uint32(ctxt.Instoffset)))
-			if t != 0 {
+			if immrot(^uint32(ctxt.Instoffset)) != 0 {
 				return C_NCON
 			}
 			return C_LCON
@@ -1155,8 +1148,10 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 }
 
 func aconsize(ctxt *obj.Link) int {
-	t := int(immrot(uint32(ctxt.Instoffset)))
-	if t != 0 {
+	if immrot(uint32(ctxt.Instoffset)) != 0 {
+		return C_RACON
+	}
+	if immrot(uint32(-ctxt.Instoffset)) != 0 {
 		return C_RACON
 	}
 	return C_LACON
@@ -1191,7 +1186,7 @@ func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
 	}
 
 	if false { /*debug['O']*/
-		fmt.Printf("oplook %v %v %v %v\n", obj.Aconv(p.As), DRconv(a1), DRconv(a2), DRconv(a3))
+		fmt.Printf("oplook %v %v %v %v\n", p.As, DRconv(a1), DRconv(a2), DRconv(a3))
 		fmt.Printf("\t\t%d %d\n", p.From.Type, p.To.Type)
 	}
 
@@ -1343,7 +1338,7 @@ func buildop(ctxt *obj.Link) {
 
 		switch r {
 		default:
-			ctxt.Diag("unknown op in build: %v", obj.Aconv(r))
+			ctxt.Diag("unknown op in build: %v", r)
 			log.Fatalf("bad code")
 
 		case AADD:
@@ -1434,6 +1429,8 @@ func buildop(ctxt *obj.Link) {
 			opset(AMOVDF, r0)
 			opset(AABSF, r0)
 			opset(AABSD, r0)
+			opset(ANEGF, r0)
+			opset(ANEGD, r0)
 
 		case ACMPF:
 			opset(ACMPD, r0)
@@ -1535,11 +1532,15 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 	case 3: /* add R<<[IR],[R],R */
 		o1 = mov(ctxt, p)
 
-	case 4: /* add $I,[R],R */
+	case 4: /* MOVW $off(R), R -> add $off,[R],R */
 		aclass(ctxt, &p.From)
-
-		o1 = oprrr(ctxt, AADD, int(p.Scond))
-		o1 |= uint32(immrot(uint32(ctxt.Instoffset)))
+		if ctxt.Instoffset < 0 {
+			o1 = oprrr(ctxt, ASUB, int(p.Scond))
+			o1 |= uint32(immrot(uint32(-ctxt.Instoffset)))
+		} else {
+			o1 = oprrr(ctxt, AADD, int(p.Scond))
+			o1 |= uint32(immrot(uint32(ctxt.Instoffset)))
+		}
 		r := int(p.From.Reg)
 		if r == 0 {
 			r = int(o.param)
@@ -1930,7 +1931,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		r := int(p.Reg)
 		if r == 0 {
 			r = rt
-			if p.As == AMOVF || p.As == AMOVD || p.As == AMOVFD || p.As == AMOVDF || p.As == ASQRTF || p.As == ASQRTD || p.As == AABSF || p.As == AABSD {
+			if p.As == AMOVF || p.As == AMOVD || p.As == AMOVFD || p.As == AMOVDF || p.As == ASQRTF || p.As == ASQRTD || p.As == AABSF || p.As == AABSD || p.As == ANEGF || p.As == ANEGD {
 				r = 0
 			}
 		}
@@ -2357,7 +2358,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 			o1 |= uint32(p.From.Offset & 0xfff)
 		}
 
-		// This is supposed to be something that stops execution.
+	// This is supposed to be something that stops execution.
 	// It's not supposed to be reached, ever, but if it is, we'd
 	// like to be able to tell how we got there. Assemble as
 	// 0xf7fabcfd which is guaranteed to raise undefined instruction
@@ -2386,7 +2387,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		o1 |= (uint32(p.Reg) & 15) << 0
 		o1 |= uint32((p.To.Offset & 15) << 16)
 
-		// DATABUNDLE: BKPT $0x5be0, signify the start of NaCl data bundle;
+	// DATABUNDLE: BKPT $0x5be0, signify the start of NaCl data bundle;
 	// DATABUNDLEEND: zero width alignment marker
 	case 100:
 		if p.As == ADATABUNDLE {
@@ -2508,6 +2509,10 @@ func oprrr(ctxt *obj.Link, a obj.As, sc int) uint32 {
 		return o | 0xe<<24 | 0xb<<20 | 0<<16 | 0xb<<8 | 0xc<<4
 	case AABSF:
 		return o | 0xe<<24 | 0xb<<20 | 0<<16 | 0xa<<8 | 0xc<<4
+	case ANEGD:
+		return o | 0xe<<24 | 0xb<<20 | 1<<16 | 0xb<<8 | 0x4<<4
+	case ANEGF:
+		return o | 0xe<<24 | 0xb<<20 | 1<<16 | 0xa<<8 | 0x4<<4
 	case ACMPD:
 		return o | 0xe<<24 | 0xb<<20 | 4<<16 | 0xb<<8 | 0xc<<4
 	case ACMPF:
@@ -2630,7 +2635,7 @@ func opbra(ctxt *obj.Link, p *obj.Prog, a obj.As, sc int) uint32 {
 		return 0xe<<28 | 0x5<<25
 	}
 
-	ctxt.Diag("bad bra %v", obj.Aconv(a))
+	ctxt.Diag("bad bra %v", a)
 	prasm(ctxt.Curp)
 	return 0
 }
@@ -2750,7 +2755,7 @@ func ofsr(ctxt *obj.Link, a obj.As, r int, v int32, b int, sc int, p *obj.Prog)
 
 	switch a {
 	default:
-		ctxt.Diag("bad fst %v", obj.Aconv(a))
+		ctxt.Diag("bad fst %v", a)
 		fallthrough
 
 	case AMOVD:
@@ -2788,7 +2793,7 @@ func omvl(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, dr int) uint32 {
 
 func chipzero5(ctxt *obj.Link, e float64) int {
 	// We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions.
-	if ctxt.Goarm < 7 || e != 0 {
+	if obj.GOARM < 7 || e != 0 {
 		return -1
 	}
 	return 0
@@ -2796,7 +2801,7 @@ func chipzero5(ctxt *obj.Link, e float64) int {
 
 func chipfloat5(ctxt *obj.Link, e float64) int {
 	// We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions.
-	if ctxt.Goarm < 7 {
+	if obj.GOARM < 7 {
 		return -1
 	}
 
diff --git a/src/cmd/internal/obj/arm/list5.go b/src/cmd/internal/obj/arm/list5.go
index a0c210e..c25a8b7 100644
--- a/src/cmd/internal/obj/arm/list5.go
+++ b/src/cmd/internal/obj/arm/list5.go
@@ -1,5 +1,5 @@
 // Inferno utils/5c/list.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5c/list.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5c/list.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go
index 9cf2f29..6e5390c 100644
--- a/src/cmd/internal/obj/arm/obj5.go
+++ b/src/cmd/internal/obj/arm/obj5.go
@@ -1,5 +1,5 @@
 // Derived from Inferno utils/5c/swt.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5c/swt.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5c/swt.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -66,7 +66,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 				ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())
 			}
 
-			if ctxt.Goarm < 7 {
+			if obj.GOARM < 7 {
 				// Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension.
 				if progedit_tlsfallback == nil {
 					progedit_tlsfallback = obj.Linklookup(ctxt, "runtime.read_tls_fallback", 0)
@@ -175,7 +175,7 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 	// We only care about global data: NAME_EXTERN means a global
 	// symbol in the Go sense, and p.Sym.Local is true for a few
 	// internally defined symbols.
-	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
+	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
 		// MOVW $sym, Rx becomes MOVW sym at GOT, Rx
 		// MOVW $sym+<off>, Rx becomes MOVW sym at GOT, Rx; ADD <off>, Rx
 		if p.As != AMOVW {
@@ -202,12 +202,12 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 	// MOVx sym, Ry becomes MOVW sym at GOT, R9; MOVx (R9), Ry
 	// MOVx Ry, sym becomes MOVW sym at GOT, R9; MOVx Ry, (R9)
 	// An addition may be inserted between the two MOVs if there is an offset.
-	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
-		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
+	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
+		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
 		}
 		source = &p.From
-	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
+	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 		source = &p.To
 	} else {
 		return
@@ -359,15 +359,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 
 			if autosize == 0 && cursym.Text.Mark&LEAF == 0 {
 				if ctxt.Debugvlog != 0 {
-					fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name)
-					ctxt.Bso.Flush()
+					ctxt.Logf("save suppressed in: %s\n", cursym.Name)
 				}
 
 				cursym.Text.Mark |= LEAF
 			}
 
 			if cursym.Text.Mark&LEAF != 0 {
-				cursym.Leaf = true
+				cursym.Set(obj.AttrLeaf, true)
 				if autosize == 0 {
 					break
 				}
@@ -566,7 +565,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			p.To.Reg = REGTMP
 			p.To.Offset = 8 * 4 // offset of m.divmod
 
-			/* MOV b,REGTMP */
+			/* MOV b, R8 */
 			p = obj.Appendp(ctxt, p)
 			p.As = AMOVW
 			p.Lineno = q1.Lineno
@@ -576,7 +575,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				p.From.Reg = q1.To.Reg
 			}
 			p.To.Type = obj.TYPE_REG
-			p.To.Reg = REGTMP
+			p.To.Reg = REG_R8
 			p.To.Offset = 0
 
 			/* CALL appropriate */
@@ -627,7 +626,7 @@ func isfloatreg(a *obj.Addr) bool {
 }
 
 func softfloat(ctxt *obj.Link, cursym *obj.LSym) {
-	if ctxt.Goarm > 5 {
+	if obj.GOARM > 5 {
 		return
 	}
 
@@ -669,7 +668,9 @@ func softfloat(ctxt *obj.Link, cursym *obj.LSym) {
 			ASQRTF,
 			ASQRTD,
 			AABSF,
-			AABSD:
+			AABSD,
+			ANEGF,
+			ANEGD:
 			goto soft
 
 		default:
@@ -709,7 +710,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 	p.From.Type = obj.TYPE_MEM
 	p.From.Reg = REGG
 	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
-	if ctxt.Cursym.Cfunc {
+	if ctxt.Cursym.CFunc() {
 		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
 	}
 	p.To.Type = obj.TYPE_REG
@@ -802,12 +803,24 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
 	}
 
+	// Now we are at the end of the function, but logically
+	// we are still in function prologue. We need to fix the
+	// SP data and PCDATA.
 	spfix := obj.Appendp(ctxt, last)
 	spfix.As = obj.ANOP
 	spfix.Spadj = -framesize
 
+	pcdata := obj.Appendp(ctxt, spfix)
+	pcdata.Lineno = ctxt.Cursym.Text.Lineno
+	pcdata.Mode = ctxt.Cursym.Text.Mode
+	pcdata.As = obj.APCDATA
+	pcdata.From.Type = obj.TYPE_CONST
+	pcdata.From.Offset = obj.PCDATA_StackMapIndex
+	pcdata.To.Type = obj.TYPE_CONST
+	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
+
 	// MOVW	LR, R3
-	movw := obj.Appendp(ctxt, spfix)
+	movw := obj.Appendp(ctxt, pcdata)
 	movw.As = AMOVW
 	movw.From.Type = obj.TYPE_REG
 	movw.From.Reg = REGLINK
@@ -822,7 +835,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 	call.To.Type = obj.TYPE_BRANCH
 	morestack := "runtime.morestack"
 	switch {
-	case ctxt.Cursym.Cfunc:
+	case ctxt.Cursym.CFunc():
 		morestack = "runtime.morestackc"
 	case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
 		morestack = "runtime.morestack_noctxt"
diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go
index ab05894..bed129d89 100644
--- a/src/cmd/internal/obj/arm64/a.out.go
+++ b/src/cmd/internal/obj/arm64/a.out.go
@@ -229,14 +229,10 @@ const (
 	REGZERO = REG_R31
 	REGSP   = REG_RSP
 
-	FREGRET  = REG_F0
-	FREGMIN  = REG_F7  // first register variable
-	FREGMAX  = REG_F26 // last register variable for 7g only
-	FREGEXT  = REG_F26 // first external register
-	FREGZERO = REG_F28 // both float and double
-	FREGHALF = REG_F29 // double
-	FREGONE  = REG_F30 // double
-	FREGTWO  = REG_F31 // double
+	FREGRET = REG_F0
+	FREGMIN = REG_F7  // first register variable
+	FREGMAX = REG_F26 // last register variable for 7g only
+	FREGEXT = REG_F26 // first external register
 )
 
 const (
@@ -274,6 +270,7 @@ const (
 	C_ADDCON   // 12-bit unsigned, shifted left by 0 or 12
 	C_MOVCON   // generated by a 16-bit constant, optionally inverted and/or shifted by multiple of 16
 	C_BITCON   // bitfield and logical immediate masks
+	C_ABCON0   // could be C_ADDCON0 or C_BITCON
 	C_ABCON    // could be C_ADDCON or C_BITCON
 	C_MBCON    // could be C_MOVCON or C_BITCON
 	C_LCON     // 32-bit constant
@@ -713,3 +710,10 @@ const (
 	AB  = obj.AJMP
 	ABL = obj.ACALL
 )
+
+const (
+	// shift types
+	SHIFT_LL = 0 << 22
+	SHIFT_LR = 1 << 22
+	SHIFT_AR = 2 << 22
+)
diff --git a/src/cmd/internal/obj/arm64/anames7.go b/src/cmd/internal/obj/arm64/anames7.go
index eb348d4..d55b34f 100644
--- a/src/cmd/internal/obj/arm64/anames7.go
+++ b/src/cmd/internal/obj/arm64/anames7.go
@@ -20,6 +20,7 @@ var cnames7 = []string{
 	"ADDCON",
 	"MOVCON",
 	"BITCON",
+	"ABCON0",
 	"ABCON",
 	"MBCON",
 	"LCON",
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index 28bebaa..3409957 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -39,7 +39,7 @@ import (
 )
 
 const (
-	FuncAlign = 16
+	funcAlign = 16
 )
 
 const (
@@ -161,10 +161,12 @@ var optab = []Optab{
 	{AADD, C_ADDCON, C_RSP, C_RSP, 2, 4, 0, 0, 0},
 	{AADD, C_ADDCON, C_NONE, C_RSP, 2, 4, 0, 0, 0},
 	{ACMP, C_ADDCON, C_RSP, C_NONE, 2, 4, 0, 0, 0},
-	// TODO: these don't work properly.
-	// {AADD, C_MBCON, C_RSP, C_RSP, 2, 4, 0, 0, 0},
-	// {AADD, C_MBCON, C_NONE, C_RSP, 2, 4, 0, 0, 0},
-	// {ACMP, C_MBCON, C_RSP, C_NONE, 2, 4, 0, 0, 0},
+	{AADD, C_MOVCON, C_RSP, C_RSP, 62, 8, 0, 0, 0},
+	{AADD, C_MOVCON, C_NONE, C_RSP, 62, 8, 0, 0, 0},
+	{ACMP, C_MOVCON, C_RSP, C_NONE, 62, 8, 0, 0, 0},
+	{AADD, C_BITCON, C_RSP, C_RSP, 62, 8, 0, 0, 0},
+	{AADD, C_BITCON, C_NONE, C_RSP, 62, 8, 0, 0, 0},
+	{ACMP, C_BITCON, C_RSP, C_NONE, 62, 8, 0, 0, 0},
 	{AADD, C_VCON, C_RSP, C_RSP, 13, 8, 0, LFROM, 0},
 	{AADD, C_VCON, C_NONE, C_RSP, 13, 8, 0, LFROM, 0},
 	{ACMP, C_VCON, C_REG, C_NONE, 13, 8, 0, LFROM, 0},
@@ -188,11 +190,14 @@ var optab = []Optab{
 	{AAND, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
 	{ABIC, C_REG, C_REG, C_REG, 1, 4, 0, 0, 0},
 	{ABIC, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
-	// TODO: these don't work properly.
-	// {AAND, C_BITCON, C_REG, C_REG, 53, 4, 0, 0, 0},
-	// {AAND, C_BITCON, C_NONE, C_REG, 53, 4, 0, 0, 0},
-	// {ABIC, C_BITCON, C_REG, C_REG, 53, 4, 0, 0, 0},
-	// {ABIC, C_BITCON, C_NONE, C_REG, 53, 4, 0, 0, 0},
+	{AAND, C_BITCON, C_REG, C_REG, 53, 4, 0, 0, 0},
+	{AAND, C_BITCON, C_NONE, C_REG, 53, 4, 0, 0, 0},
+	{ABIC, C_BITCON, C_REG, C_REG, 53, 4, 0, 0, 0},
+	{ABIC, C_BITCON, C_NONE, C_REG, 53, 4, 0, 0, 0},
+	{AAND, C_MOVCON, C_REG, C_REG, 62, 8, 0, 0, 0},
+	{AAND, C_MOVCON, C_NONE, C_REG, 62, 8, 0, 0, 0},
+	{ABIC, C_MOVCON, C_REG, C_REG, 62, 8, 0, 0, 0},
+	{ABIC, C_MOVCON, C_NONE, C_REG, 62, 8, 0, 0, 0},
 	{AAND, C_VCON, C_REG, C_REG, 28, 8, 0, LFROM, 0},
 	{AAND, C_VCON, C_NONE, C_REG, 28, 8, 0, LFROM, 0},
 	{ABIC, C_VCON, C_REG, C_REG, 28, 8, 0, LFROM, 0},
@@ -216,8 +221,8 @@ var optab = []Optab{
 	// TODO: these don't work properly.
 	// { AMOVW,		C_ADDCON,	C_NONE,	C_REG,		2, 4, 0 , 0},
 	// { AMOVD,		C_ADDCON,	C_NONE,	C_REG,		2, 4, 0 , 0},
-	// { AMOVW,		C_BITCON,	C_NONE,	C_REG,		53, 4, 0 , 0},
-	// { AMOVD,		C_BITCON,	C_NONE,	C_REG,		53, 4, 0 , 0},
+	{AMOVW, C_BITCON, C_NONE, C_REG, 32, 4, 0, 0, 0},
+	{AMOVD, C_BITCON, C_NONE, C_REG, 32, 4, 0, 0, 0},
 
 	{AMOVK, C_VCON, C_NONE, C_REG, 33, 4, 0, 0, 0},
 	{AMOVD, C_AACON, C_NONE, C_REG, 4, 4, REGFROM, 0, 0},
@@ -461,6 +466,10 @@ var optab = []Optab{
 	{AFMOVD, C_FREG, C_NONE, C_FREG, 54, 4, 0, 0, 0},
 	{AFCVTZSD, C_FREG, C_NONE, C_REG, 29, 4, 0, 0, 0},
 	{ASCVTFD, C_REG, C_NONE, C_FREG, 29, 4, 0, 0, 0},
+	{AFMOVS, C_REG, C_NONE, C_FREG, 29, 4, 0, 0, 0},
+	{AFMOVS, C_FREG, C_NONE, C_REG, 29, 4, 0, 0, 0},
+	{AFMOVD, C_REG, C_NONE, C_FREG, 29, 4, 0, 0, 0},
+	{AFMOVD, C_FREG, C_NONE, C_REG, 29, 4, 0, 0, 0},
 	{AFCMPS, C_FREG, C_FREG, C_NONE, 56, 4, 0, 0, 0},
 	{AFCMPS, C_FCON, C_FREG, C_NONE, 56, 4, 0, 0, 0},
 	{AFCCMPS, C_COND, C_REG, C_VCON, 57, 4, 0, 0, 0},
@@ -580,7 +589,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym) {
 	 */
 	for bflag != 0 {
 		if ctxt.Debugvlog != 0 {
-			fmt.Fprintf(ctxt.Bso, "%5.2f span1\n", obj.Cputime())
+			ctxt.Logf("%5.2f span1\n", obj.Cputime())
 		}
 		bflag = 0
 		c = 0
@@ -592,7 +601,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym) {
 			o = oplook(ctxt, p)
 
 			/* very large branches */
-			if o.type_ == 7 && p.Pcond != nil {
+			if (o.type_ == 7 || o.type_ == 39) && p.Pcond != nil { // 7: BEQ and like, 39: CBZ and like
 				otxt := p.Pcond.Pc - c
 				if otxt <= -(1<<18)+10 || otxt >= (1<<18)-10 {
 					q := ctxt.NewProg()
@@ -624,7 +633,7 @@ func span7(ctxt *obj.Link, cursym *obj.LSym) {
 		}
 	}
 
-	c += -c & (FuncAlign - 1)
+	c += -c & (funcAlign - 1)
 	cursym.Size = c
 
 	/*
@@ -715,15 +724,18 @@ func flushpool(ctxt *obj.Link, p *obj.Prog, skip int) {
  */
 func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
 	c := aclass(ctxt, a)
+	lit := ctxt.Instoffset
 	t := *ctxt.NewProg()
 	t.As = AWORD
 	sz := 4
 
-	// MOVW foo(SB), R is actually
-	//	MOV addr, REGTEMP
-	//	MOVW REGTEMP, R
+	// MOVD foo(SB), R is actually
+	//	MOVD addr, REGTMP
+	//	MOVD REGTMP, R
 	// where addr is the address of the DWORD containing the address of foo.
-	if p.As == AMOVD || c == C_ADDR || c == C_VCON {
+	if p.As == AMOVD || c == C_ADDR || c == C_VCON || int64(lit) != int64(int32(lit)) || uint64(lit) != uint64(uint32(lit)) {
+		// conservative: don't know if we want signed or unsigned extension.
+		// in case of ambiguity, store 64-bit
 		t.As = ADWORD
 		sz = 8
 	}
@@ -740,29 +752,12 @@ func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
 		t.To.Type = a.Type
 		t.To.Name = a.Name
 
-		/* This is here to work around a bug where we generate negative
-		operands that match C_MOVCON, but we use them with
-		instructions that only accept unsigned immediates. This
-		will cause oplook to return a variant of the instruction
-		that loads the negative constant from memory, rather than
-		using the immediate form. Because of that load, we get here,
-		so we need to know what to do with C_MOVCON.
-
-		The correct fix is to use the "negation" instruction variant,
-		e.g. CMN $1, R instead of CMP $-1, R, or SUB $1, R instead
-		of ADD $-1, R. */
-	case C_MOVCON,
-
-		/* This is here because MOV uint12<<12, R is disabled in optab.
-		Because of this, we need to load the constant from memory. */
-		C_ADDCON,
-
-		/* These are here because they are disabled in optab.
-		Because of this, we need to load the constant from memory. */
-		C_BITCON,
-		C_ABCON,
-		C_MBCON,
-		C_PSAUTO,
+	/* This is here because MOV uint12<<12, R is disabled in optab.
+	Because of this, we need to load the constant from memory. */
+	case C_ADDCON:
+		fallthrough
+
+	case C_PSAUTO,
 		C_PPAUTO,
 		C_UAUTO4K,
 		C_UAUTO8K,
@@ -790,7 +785,7 @@ func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
 		}
 
 		t.To.Type = obj.TYPE_CONST
-		t.To.Offset = ctxt.Instoffset
+		t.To.Offset = lit
 		break
 	}
 
@@ -811,7 +806,7 @@ func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
 		ctxt.Elitrl.Link = q
 	}
 	ctxt.Elitrl = q
-	pool.size = -pool.size & (FuncAlign - 1)
+	pool.size = -pool.size & (funcAlign - 1)
 	pool.size += uint32(sz)
 	p.Pcond = q
 }
@@ -844,11 +839,137 @@ func isaddcon(v int64) bool {
 	return v <= 0xFFF
 }
 
-func isbitcon(v uint64) bool {
-	/*  fancy bimm32 or bimm64? */
-	// TODO(aram):
-	return false
-	// return findmask(v) != nil || (v>>32) == 0 && findmask(v|(v<<32)) != nil
+// isbitcon returns whether a constant can be encoded into a logical instruction.
+// bitcon has a binary form of repetition of a bit sequence of length 2, 4, 8, 16, 32, or 64,
+// which itself is a rotate (w.r.t. the length of the unit) of a sequence of ones.
+// special cases: 0 and -1 are not bitcon.
+// this function needs to run against virtually all the constants, so it needs to be fast.
+// for this reason, bitcon testing and bitcon encoding are separate functions.
+func isbitcon(x uint64) bool {
+	if x == 1<<64-1 || x == 0 {
+		return false
+	}
+	// determine the period and sign-extend a unit to 64 bits
+	switch {
+	case x != x>>32|x<<32:
+		// period is 64
+		// nothing to do
+	case x != x>>16|x<<48:
+		// period is 32
+		x = uint64(int64(int32(x)))
+	case x != x>>8|x<<56:
+		// period is 16
+		x = uint64(int64(int16(x)))
+	case x != x>>4|x<<60:
+		// period is 8
+		x = uint64(int64(int8(x)))
+	default:
+		// period is 4 or 2, always true
+		// 0001, 0010, 0100, 1000 -- 0001 rotate
+		// 0011, 0110, 1100, 1001 -- 0011 rotate
+		// 0111, 1011, 1101, 1110 -- 0111 rotate
+		// 0101, 1010             -- 01   rotate, repeat
+		return true
+	}
+	return sequenceOfOnes(x) || sequenceOfOnes(^x)
+}
+
+// sequenceOfOnes tests whether a constant is a sequence of ones in binary, with leading and trailing zeros
+func sequenceOfOnes(x uint64) bool {
+	y := x & -x // lowest set bit of x. x is good iff x+y is a power of 2
+	y += x
+	return (y-1)&y == 0
+}
+
+// bitconEncode returns the encoding of a bitcon used in logical instructions
+// x is known to be a bitcon
+// a bitcon is a sequence of n ones at low bits (i.e. 1<<n-1), right rotated
+// by R bits, and repeated with period of 64, 32, 16, 8, 4, or 2.
+// it is encoded in logical instructions with 3 bitfields
+// N (1 bit) : R (6 bits) : S (6 bits), where
+// N=1           -- period=64
+// N=0, S=0xxxxx -- period=32
+// N=0, S=10xxxx -- period=16
+// N=0, S=110xxx -- period=8
+// N=0, S=1110xx -- period=4
+// N=0, S=11110x -- period=2
+// R is the shift amount, low bits of S = n-1
+func bitconEncode(x uint64, mode int) uint32 {
+	var period uint32
+	// determine the period and sign-extend a unit to 64 bits
+	switch {
+	case x != x>>32|x<<32:
+		period = 64
+	case x != x>>16|x<<48:
+		period = 32
+		x = uint64(int64(int32(x)))
+	case x != x>>8|x<<56:
+		period = 16
+		x = uint64(int64(int16(x)))
+	case x != x>>4|x<<60:
+		period = 8
+		x = uint64(int64(int8(x)))
+	case x != x>>2|x<<62:
+		period = 4
+		x = uint64(int64(x<<60) >> 60)
+	default:
+		period = 2
+		x = uint64(int64(x<<62) >> 62)
+	}
+	neg := false
+	if int64(x) < 0 {
+		x = ^x
+		neg = true
+	}
+	y := x & -x // lowest set bit of x.
+	s := log2(y)
+	n := log2(x+y) - s // x (or ^x) is a sequence of n ones left shifted by s bits
+	if neg {
+		// ^x is a sequence of n ones left shifted by s bits
+		// adjust n, s for x
+		s = n + s
+		n = period - n
+	}
+
+	N := uint32(0)
+	if mode == 64 && period == 64 {
+		N = 1
+	}
+	R := (period - s) & (period - 1) & uint32(mode-1) // shift amount of right rotate
+	S := (n - 1) | 63&^(period<<1-1)                  // low bits = #ones - 1, high bits encodes period
+	return N<<22 | R<<16 | S<<10
+}
+
+func log2(x uint64) uint32 {
+	if x == 0 {
+		panic("log2 of 0")
+	}
+	n := uint32(0)
+	if x >= 1<<32 {
+		x >>= 32
+		n += 32
+	}
+	if x >= 1<<16 {
+		x >>= 16
+		n += 16
+	}
+	if x >= 1<<8 {
+		x >>= 8
+		n += 8
+	}
+	if x >= 1<<4 {
+		x >>= 4
+		n += 4
+	}
+	if x >= 1<<2 {
+		x >>= 2
+		n += 2
+	}
+	if x >= 1<<1 {
+		x >>= 1
+		n += 1
+	}
+	return n
 }
 
 func autoclass(l int64) int {
@@ -1019,6 +1140,9 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 			}
 			if isaddcon(v) {
 				if v <= 0xFFF {
+					if isbitcon(uint64(v)) {
+						return C_ABCON0
+					}
 					return C_ADDCON0
 				}
 				if isbitcon(uint64(v)) {
@@ -1085,6 +1209,10 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 	return C_GOK
 }
 
+func oclass(a *obj.Addr) int {
+	return int(a.Class) - 1
+}
+
 func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
 	a1 := int(p.Optab)
 	if a1 != 0 {
@@ -1110,7 +1238,7 @@ func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
 	}
 
 	if false {
-		fmt.Printf("oplook %v %d %d %d\n", obj.Aconv(p.As), a1, a2, a3)
+		fmt.Printf("oplook %v %d %d %d\n", p.As, a1, a2, a3)
 		fmt.Printf("\t\t%d %d\n", p.From.Type, p.To.Type)
 	}
 
@@ -1151,17 +1279,17 @@ func cmp(a int, b int) bool {
 		}
 
 	case C_ADDCON0:
-		if b == C_ZCON {
+		if b == C_ZCON || b == C_ABCON0 {
 			return true
 		}
 
 	case C_ADDCON:
-		if b == C_ZCON || b == C_ADDCON0 || b == C_ABCON {
+		if b == C_ZCON || b == C_ABCON0 || b == C_ADDCON0 || b == C_ABCON {
 			return true
 		}
 
 	case C_BITCON:
-		if b == C_ABCON || b == C_MBCON {
+		if b == C_ABCON0 || b == C_ABCON || b == C_MBCON {
 			return true
 		}
 
@@ -1171,7 +1299,7 @@ func cmp(a int, b int) bool {
 		}
 
 	case C_LCON:
-		if b == C_ZCON || b == C_BITCON || b == C_ADDCON || b == C_ADDCON0 || b == C_ABCON || b == C_MBCON || b == C_MOVCON {
+		if b == C_ZCON || b == C_BITCON || b == C_ADDCON || b == C_ADDCON0 || b == C_ABCON || b == C_ABCON0 || b == C_MBCON || b == C_MOVCON {
 			return true
 		}
 
@@ -1336,7 +1464,7 @@ func buildop(ctxt *obj.Link) {
 		oprangeset(r, t)
 		switch r {
 		default:
-			ctxt.Diag("unknown op in build: %v", obj.Aconv(r))
+			ctxt.Diag("unknown op in build: %v", r)
 			log.Fatalf("bad code")
 
 		case AADD:
@@ -1706,6 +1834,8 @@ func buildop(ctxt *obj.Link) {
 			oprangeset(ALDXRW, t)
 
 		case ALDAXR:
+			oprangeset(ALDAXRB, t)
+			oprangeset(ALDAXRH, t)
 			oprangeset(ALDAXRW, t)
 
 		case ALDXP:
@@ -1720,6 +1850,8 @@ func buildop(ctxt *obj.Link) {
 			oprangeset(ASTXRW, t)
 
 		case ASTLXR:
+			oprangeset(ASTLXRB, t)
+			oprangeset(ASTLXRH, t)
 			oprangeset(ASTLXRW, t)
 
 		case ASTXP:
@@ -2121,12 +2253,13 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 
 	case 20: /* movT R,O(R) -> strT */
 		v := int32(regoff(ctxt, &p.To))
+		sz := int32(1 << uint(movesize(p.As)))
 
 		r := int(p.To.Reg)
 		if r == 0 {
 			r = int(o.param)
 		}
-		if v < 0 { /* unscaled 9-bit signed */
+		if v < 0 || v%sz != 0 { /* unscaled 9-bit signed */
 			o1 = olsr9s(ctxt, int32(opstr9(ctxt, p.As)), v, r, int(p.From.Reg))
 		} else {
 			v = int32(offsetshift(ctxt, int64(v), int(o.a3)))
@@ -2135,16 +2268,16 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 
 	case 21: /* movT O(R),R -> ldrT */
 		v := int32(regoff(ctxt, &p.From))
+		sz := int32(1 << uint(movesize(p.As)))
 
 		r := int(p.From.Reg)
 		if r == 0 {
 			r = int(o.param)
 		}
-		if v < 0 { /* unscaled 9-bit signed */
+		if v < 0 || v%sz != 0 { /* unscaled 9-bit signed */
 			o1 = olsr9s(ctxt, int32(opldr9(ctxt, p.As)), v, r, int(p.To.Reg))
 		} else {
 			v = int32(offsetshift(ctxt, int64(v), int(o.a1)))
-
 			//print("offset=%lld v=%ld a1=%d\n", instoffset, v, o->a1);
 			o1 = olsr12u(ctxt, int32(opldr12(ctxt, p.As)), v, r, int(p.To.Reg))
 		}
@@ -2247,15 +2380,27 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		o2 |= uint32(p.To.Reg & 31)
 
 	case 29: /* op Rn, Rd */
-		o1 = oprrr(ctxt, p.As)
-
+		fc := aclass(ctxt, &p.From)
+		tc := aclass(ctxt, &p.To)
+		if (p.As == AFMOVD || p.As == AFMOVS) && (fc == C_REG || fc == C_ZCON || tc == C_REG || tc == C_ZCON) {
+			// FMOV Rx, Fy or FMOV Fy, Rx
+			o1 = FPCVTI(0, 0, 0, 0, 6)
+			if p.As == AFMOVD {
+				o1 |= 1<<31 | 1<<22 // 64-bit
+			}
+			if fc == C_REG || fc == C_ZCON {
+				o1 |= 1 << 16 // FMOV Rx, Fy
+			}
+		} else {
+			o1 = oprrr(ctxt, p.As)
+		}
 		o1 |= uint32(p.From.Reg&31)<<5 | uint32(p.To.Reg&31)
 
 	case 30: /* movT R,L(R) -> strT */
 		s := movesize(o.as)
 
 		if s < 0 {
-			ctxt.Diag("unexpected long move, op %v tab %v\n%v", obj.Aconv(p.As), obj.Aconv(o.as), p)
+			ctxt.Diag("unexpected long move, op %v tab %v\n%v", p.As, o.as, p)
 		}
 		v := int32(regoff(ctxt, &p.To))
 		if v < 0 {
@@ -2282,7 +2427,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		s := movesize(o.as)
 
 		if s < 0 {
-			ctxt.Diag("unexpected long move, op %v tab %v\n%v", obj.Aconv(p.As), obj.Aconv(o.as), p)
+			ctxt.Diag("unexpected long move, op %v tab %v\n%v", p.As, o.as, p)
 		}
 		v := int32(regoff(ctxt, &p.From))
 		if v < 0 {
@@ -2306,34 +2451,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		o2 = olsr12u(ctxt, int32(opldr12(ctxt, p.As)), ((v-hi)>>uint(s))&0xFFF, REGTMP, int(p.To.Reg))
 
 	case 32: /* mov $con, R -> movz/movn */
-		r := 32
-
-		if p.As == AMOVD {
-			r = 64
-		}
-		d := p.From.Offset
-		s := movcon(d)
-		if s < 0 || s >= r {
-			d = ^d
-			s = movcon(d)
-			if s < 0 || s >= r {
-				ctxt.Diag("impossible move wide: %#x\n%v", uint64(p.From.Offset), p)
-			}
-			if p.As == AMOVD {
-				o1 = opirr(ctxt, AMOVN)
-			} else {
-				o1 = opirr(ctxt, AMOVNW)
-			}
-		} else {
-			if p.As == AMOVD {
-				o1 = opirr(ctxt, AMOVZ)
-			} else {
-				o1 = opirr(ctxt, AMOVZW)
-			}
-		}
-
-		rt := int(p.To.Reg)
-		o1 |= uint32((((d >> uint(s*16)) & 0xFFFF) << 5) | int64((uint32(s)&3)<<21) | int64(rt&31))
+		o1 = omovconst(ctxt, p.As, p, &p.From, int(p.To.Reg))
 
 	case 33: /* movk $uimm16 << pos */
 		o1 = opirr(ctxt, p.As)
@@ -2540,7 +2658,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 			o1 = opbfm(ctxt, AUBFMW, 0, 15, rf, rt)
 
 		default:
-			ctxt.Diag("bad sxt %v", obj.Aconv(as))
+			ctxt.Diag("bad sxt %v", as)
 			break
 		}
 
@@ -2601,8 +2719,26 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 
 		o1 |= uint32((p.From.Offset & 0x7F) << 5)
 
-	case 53: /* and/or/eor/bic/... $bimmN, Rn, Rd -> op (N,r,s), Rn, Rd */
-		ctxt.Diag("bitmask immediate not implemented\n%v", p)
+	case 53: /* and/or/eor/bic/... $bitcon, Rn, Rd */
+		a := p.As
+		rt := int(p.To.Reg)
+		r := int(p.Reg)
+		if r == 0 {
+			r = rt
+		}
+		mode := 64
+		v := uint64(p.From.Offset)
+		switch p.As {
+		case AANDW, AORRW, AEORW, AANDSW:
+			mode = 32
+		case ABIC, AORN, AEON, ABICS:
+			v = ^v
+		case ABICW, AORNW, AEONW, ABICSW:
+			v = ^v
+			mode = 32
+		}
+		o1 = opirr(ctxt, a)
+		o1 |= bitconEncode(v, mode) | uint32(r&31)<<5 | uint32(rt&31)
 
 	case 54: /* floating point arith */
 		o1 = oprrr(ctxt, p.As)
@@ -2694,6 +2830,31 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 
 		o1 = ADR(0, uint32(d), uint32(p.To.Reg))
 
+	case 62: /* op $movcon, [R], R -> mov $movcon, REGTMP + op REGTMP, [R], R */
+		if p.Reg == REGTMP {
+			ctxt.Diag("cannot use REGTMP as source: %v\n", p)
+		}
+		o1 = omovconst(ctxt, AMOVD, p, &p.From, REGTMP)
+
+		rt := int(p.To.Reg)
+		if p.To.Type == obj.TYPE_NONE {
+			rt = REGZERO
+		}
+		r := int(p.Reg)
+		if r == 0 {
+			r = rt
+		}
+		if p.To.Type != obj.TYPE_NONE && (p.To.Reg == REGSP || r == REGSP) {
+			o2 = opxrrr(ctxt, p.As)
+			o2 |= REGTMP & 31 << 16
+			o2 |= LSL0_64
+		} else {
+			o2 = oprrr(ctxt, p.As)
+			o2 |= REGTMP & 31 << 16 /* shift is 0 */
+		}
+		o2 |= uint32(r&31) << 5
+		o2 |= uint32(rt & 31)
+
 		/* reloc ops */
 	case 64: /* movT R,addr -> adrp + add + movT R, (REGTMP) */
 		o1 = ADR(1, 0, REGTMP)
@@ -3330,7 +3491,7 @@ func oprrr(ctxt *obj.Link, a obj.As) uint32 {
 		return FPOP1S(0, 0, 3, 5)
 	}
 
-	ctxt.Diag("bad rrr %d %v", a, obj.Aconv(a))
+	ctxt.Diag("bad rrr %d %v", a, a)
 	prasm(ctxt.Curp)
 	return 0
 }
@@ -3374,28 +3535,28 @@ func opirr(ctxt *obj.Link, a obj.As) uint32 {
 		return 1<<31 | 0x10<<24
 
 		/* op $bimm, Rn, Rd */
-	case AAND:
+	case AAND, ABIC:
 		return S64 | 0<<29 | 0x24<<23
 
-	case AANDW:
+	case AANDW, ABICW:
 		return S32 | 0<<29 | 0x24<<23 | 0<<22
 
-	case AORR:
+	case AORR, AORN:
 		return S64 | 1<<29 | 0x24<<23
 
-	case AORRW:
+	case AORRW, AORNW:
 		return S32 | 1<<29 | 0x24<<23 | 0<<22
 
-	case AEOR:
+	case AEOR, AEON:
 		return S64 | 2<<29 | 0x24<<23
 
-	case AEORW:
+	case AEORW, AEONW:
 		return S32 | 2<<29 | 0x24<<23 | 0<<22
 
-	case AANDS:
+	case AANDS, ABICS:
 		return S64 | 3<<29 | 0x24<<23
 
-	case AANDSW:
+	case AANDSW, ABICSW:
 		return S32 | 3<<29 | 0x24<<23 | 0<<22
 
 	case AASR:
@@ -3517,7 +3678,7 @@ func opirr(ctxt *obj.Link, a obj.As) uint32 {
 		return SYSOP(0, 0, 3, 2, 0, 0, 0x1F)
 	}
 
-	ctxt.Diag("bad irr %v", obj.Aconv(a))
+	ctxt.Diag("bad irr %v", a)
 	prasm(ctxt.Curp)
 	return 0
 }
@@ -3593,7 +3754,7 @@ func opxrrr(ctxt *obj.Link, a obj.As) uint32 {
 		return S32 | 1<<30 | 1<<29 | 0x0b<<24 | 0<<22 | 1<<21 | LSL0_32
 	}
 
-	ctxt.Diag("bad opxrrr %v\n%v", obj.Aconv(a), ctxt.Curp)
+	ctxt.Diag("bad opxrrr %v\n%v", a, ctxt.Curp)
 	return 0
 }
 
@@ -3627,7 +3788,7 @@ func opimm(ctxt *obj.Link, a obj.As) uint32 {
 		return SYSOP(0, 0, 3, 3, 0, 2, 0x1F)
 	}
 
-	ctxt.Diag("bad imm %v", obj.Aconv(a))
+	ctxt.Diag("bad imm %v", a)
 	prasm(ctxt.Curp)
 	return 0
 }
@@ -3707,12 +3868,11 @@ func opbra(ctxt *obj.Link, a obj.As) uint32 {
 	case AB:
 		return 0<<31 | 5<<26 /* imm26 */
 
-	case obj.ADUFFZERO,
-		ABL:
+	case obj.ADUFFZERO, obj.ADUFFCOPY, ABL:
 		return 1<<31 | 5<<26
 	}
 
-	ctxt.Diag("bad bra %v", obj.Aconv(a))
+	ctxt.Diag("bad bra %v", a)
 	prasm(ctxt.Curp)
 	return 0
 }
@@ -3729,7 +3889,7 @@ func opbrr(ctxt *obj.Link, a obj.As) uint32 {
 		return OPBLR(2) /* RET */
 	}
 
-	ctxt.Diag("bad brr %v", obj.Aconv(a))
+	ctxt.Diag("bad brr %v", a)
 	prasm(ctxt.Curp)
 	return 0
 }
@@ -3761,7 +3921,7 @@ func op0(ctxt *obj.Link, a obj.As) uint32 {
 		return SYSHINT(5)
 	}
 
-	ctxt.Diag("bad op0 %v", obj.Aconv(a))
+	ctxt.Diag("bad op0 %v", a)
 	prasm(ctxt.Curp)
 	return 0
 }
@@ -3826,7 +3986,7 @@ func opload(ctxt *obj.Link, a obj.As) uint32 {
 		return S32 | 0<<30 | 5<<27 | 0<<26 | 0<<23 | 1<<22
 	}
 
-	ctxt.Diag("bad opload %v\n%v", obj.Aconv(a), ctxt.Curp)
+	ctxt.Diag("bad opload %v\n%v", a, ctxt.Curp)
 	return 0
 }
 
@@ -3893,7 +4053,7 @@ func opstore(ctxt *obj.Link, a obj.As) uint32 {
 		return S32 | 0<<30 | 5<<27 | 0<<26 | 0<<23 | 1<<22
 	}
 
-	ctxt.Diag("bad opstore %v\n%v", obj.Aconv(a), ctxt.Curp)
+	ctxt.Diag("bad opstore %v\n%v", a, ctxt.Curp)
 	return 0
 }
 
@@ -3941,7 +4101,7 @@ func opldr12(ctxt *obj.Link, a obj.As) uint32 {
 		return LDSTR12U(3, 1, 1)
 	}
 
-	ctxt.Diag("bad opldr12 %v\n%v", obj.Aconv(a), ctxt.Curp)
+	ctxt.Diag("bad opldr12 %v\n%v", a, ctxt.Curp)
 	return 0
 }
 
@@ -3992,7 +4152,7 @@ func opldr9(ctxt *obj.Link, a obj.As) uint32 {
 		return LDSTR9S(3, 1, 1)
 	}
 
-	ctxt.Diag("bad opldr9 %v\n%v", obj.Aconv(a), ctxt.Curp)
+	ctxt.Diag("bad opldr9 %v\n%v", a, ctxt.Curp)
 	return 0
 }
 
@@ -4024,7 +4184,7 @@ func opldrpp(ctxt *obj.Link, a obj.As) uint32 {
 		return 0<<30 | 7<<27 | 0<<26 | 0<<24 | 1<<22
 	}
 
-	ctxt.Diag("bad opldr %v\n%v", obj.Aconv(a), ctxt.Curp)
+	ctxt.Diag("bad opldr %v\n%v", a, ctxt.Curp)
 	return 0
 }
 
@@ -4056,7 +4216,7 @@ func omovlit(ctxt *obj.Link, as obj.As, p *obj.Prog, a *obj.Addr, dr int) uint32
 	var o1 int32
 	if p.Pcond == nil { /* not in literal pool */
 		aclass(ctxt, a)
-		fmt.Fprintf(ctxt.Bso, "omovlit add %d (%#x)\n", ctxt.Instoffset, uint64(ctxt.Instoffset))
+		ctxt.Logf("omovlit add %d (%#x)\n", ctxt.Instoffset, uint64(ctxt.Instoffset))
 
 		/* TODO: could be clever, and use general constant builder */
 		o1 = int32(opirr(ctxt, AADD))
@@ -4100,6 +4260,52 @@ func omovlit(ctxt *obj.Link, as obj.As, p *obj.Prog, a *obj.Addr, dr int) uint32
 	return uint32(o1)
 }
 
+// load a constant (MOVCON or BITCON) in a into rt
+func omovconst(ctxt *obj.Link, as obj.As, p *obj.Prog, a *obj.Addr, rt int) (o1 uint32) {
+	if c := oclass(a); c == C_BITCON || c == C_ABCON || c == C_ABCON0 {
+		// or $bitcon, REGZERO, rt
+		mode := 64
+		var as1 obj.As
+		switch as {
+		case AMOVW:
+			as1 = AORRW
+			mode = 32
+		case AMOVD:
+			as1 = AORR
+		}
+		o1 = opirr(ctxt, as1)
+		o1 |= bitconEncode(uint64(a.Offset), mode) | uint32(REGZERO&31)<<5 | uint32(rt&31)
+		return o1
+	}
+
+	r := 32
+	if as == AMOVD {
+		r = 64
+	}
+	d := a.Offset
+	s := movcon(d)
+	if s < 0 || s >= r {
+		d = ^d
+		s = movcon(d)
+		if s < 0 || s >= r {
+			ctxt.Diag("impossible move wide: %#x\n%v", uint64(a.Offset), p)
+		}
+		if as == AMOVD {
+			o1 = opirr(ctxt, AMOVN)
+		} else {
+			o1 = opirr(ctxt, AMOVNW)
+		}
+	} else {
+		if as == AMOVD {
+			o1 = opirr(ctxt, AMOVZ)
+		} else {
+			o1 = opirr(ctxt, AMOVZW)
+		}
+	}
+	o1 |= uint32((((d >> uint(s*16)) & 0xFFFF) << 5) | int64((uint32(s)&3)<<21) | int64(rt&31))
+	return o1
+}
+
 func opbfm(ctxt *obj.Link, a obj.As, r int, s int, rf int, rt int) uint32 {
 	var c uint32
 	o := opirr(ctxt, a)
diff --git a/src/cmd/internal/obj/arm64/asm_test.go b/src/cmd/internal/obj/arm64/asm_test.go
new file mode 100644
index 0000000..369c48f
--- /dev/null
+++ b/src/cmd/internal/obj/arm64/asm_test.go
@@ -0,0 +1,62 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package arm64
+
+import (
+	"bytes"
+	"fmt"
+	"internal/testenv"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"testing"
+)
+
+// TestLarge generates a very large file to verify that large
+// program builds successfully, in particular, too-far
+// conditional branches are fixed.
+func TestLarge(t *testing.T) {
+	if testing.Short() {
+		t.Skip("Skip in short mode")
+	}
+	testenv.MustHaveGoBuild(t)
+
+	dir, err := ioutil.TempDir("", "testlarge")
+	if err != nil {
+		t.Fatalf("could not create directory: %v", err)
+	}
+	defer os.RemoveAll(dir)
+
+	// generate a very large function
+	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
+	gen(buf)
+
+	tmpfile := filepath.Join(dir, "x.s")
+	err = ioutil.WriteFile(tmpfile, buf.Bytes(), 0644)
+	if err != nil {
+		t.Fatalf("can't write output: %v\n", err)
+	}
+
+	// build generated file
+	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
+	cmd.Env = []string{"GOARCH=arm64", "GOOS=linux"}
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Errorf("Build failed: %v, output: %s", err, out)
+	}
+}
+
+// gen generates a very large program, with a very far conditional branch.
+func gen(buf *bytes.Buffer) {
+	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
+	fmt.Fprintln(buf, "CBZ R0, label")
+	fmt.Fprintln(buf, "BEQ label")
+	for i := 0; i < 1<<19; i++ {
+		fmt.Fprintln(buf, "MOVD R0, R1")
+	}
+	fmt.Fprintln(buf, "label:")
+	fmt.Fprintln(buf, "RET")
+}
diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go
index ffa1b41..3ea78cd 100644
--- a/src/cmd/internal/obj/arm64/obj7.go
+++ b/src/cmd/internal/obj/arm64/obj7.go
@@ -57,7 +57,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 	p.From.Type = obj.TYPE_MEM
 	p.From.Reg = REGG
 	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
-	if ctxt.Cursym.Cfunc {
+	if ctxt.Cursym.CFunc() {
 		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
 	}
 	p.To.Type = obj.TYPE_REG
@@ -161,12 +161,24 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
 	}
 
+	// Now we are at the end of the function, but logically
+	// we are still in function prologue. We need to fix the
+	// SP data and PCDATA.
 	spfix := obj.Appendp(ctxt, last)
 	spfix.As = obj.ANOP
 	spfix.Spadj = -framesize
 
+	pcdata := obj.Appendp(ctxt, spfix)
+	pcdata.Lineno = ctxt.Cursym.Text.Lineno
+	pcdata.Mode = ctxt.Cursym.Text.Mode
+	pcdata.As = obj.APCDATA
+	pcdata.From.Type = obj.TYPE_CONST
+	pcdata.From.Offset = obj.PCDATA_StackMapIndex
+	pcdata.To.Type = obj.TYPE_CONST
+	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
+
 	// MOV	LR, R3
-	movlr := obj.Appendp(ctxt, spfix)
+	movlr := obj.Appendp(ctxt, pcdata)
 	movlr.As = AMOVD
 	movlr.From.Type = obj.TYPE_REG
 	movlr.From.Reg = REGLINK
@@ -193,7 +205,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 	call.To.Type = obj.TYPE_BRANCH
 	morestack := "runtime.morestack"
 	switch {
-	case ctxt.Cursym.Cfunc:
+	case ctxt.Cursym.CFunc():
 		morestack = "runtime.morestackc"
 	case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
 		morestack = "runtime.morestack_noctxt"
@@ -250,12 +262,17 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 		if p.From.Type == obj.TYPE_FCONST {
 			f32 := float32(p.From.Val.(float64))
 			i32 := math.Float32bits(f32)
+			if i32 == 0 {
+				p.From.Type = obj.TYPE_REG
+				p.From.Reg = REGZERO
+				break
+			}
 			literal := fmt.Sprintf("$f32.%08x", i32)
 			s := obj.Linklookup(ctxt, literal, 0)
 			s.Size = 4
 			p.From.Type = obj.TYPE_MEM
 			p.From.Sym = s
-			p.From.Sym.Local = true
+			p.From.Sym.Set(obj.AttrLocal, true)
 			p.From.Name = obj.NAME_EXTERN
 			p.From.Offset = 0
 		}
@@ -263,12 +280,17 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 	case AFMOVD:
 		if p.From.Type == obj.TYPE_FCONST {
 			i64 := math.Float64bits(p.From.Val.(float64))
+			if i64 == 0 {
+				p.From.Type = obj.TYPE_REG
+				p.From.Reg = REGZERO
+				break
+			}
 			literal := fmt.Sprintf("$f64.%016x", i64)
 			s := obj.Linklookup(ctxt, literal, 0)
 			s.Size = 8
 			p.From.Type = obj.TYPE_MEM
 			p.From.Sym = s
-			p.From.Sym.Local = true
+			p.From.Sym.Set(obj.AttrLocal, true)
 			p.From.Name = obj.NAME_EXTERN
 			p.From.Offset = 0
 		}
@@ -279,20 +301,30 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 	// Rewrite negative immediates as positive immediates with
 	// complementary instruction.
 	switch p.As {
-	case AADD,
-		AADDW,
-		ASUB,
-		ASUBW,
-		ACMP,
-		ACMPW,
-		ACMN,
-		ACMNW:
-		if p.From.Type == obj.NAME_EXTERN && p.From.Offset < 0 {
+	case AADD, ASUB, ACMP, ACMN:
+		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
+			p.From.Offset = -p.From.Offset
+			p.As = complements[p.As]
+		}
+	case AADDW, ASUBW, ACMPW, ACMNW:
+		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
 			p.From.Offset = -p.From.Offset
 			p.As = complements[p.As]
 		}
+	}
 
-		break
+	// For 32-bit logical instruction with constant,
+	// rewrite the high 32-bit to be a repetition of
+	// the low 32-bit, so that the BITCON test can be
+	// shared for both 32-bit and 64-bit. 32-bit ops
+	// will zero the high 32-bit of the destination
+	// register anyway.
+	switch p.As {
+	case AANDW, AORRW, AEORW, AANDSW:
+		if p.From.Type == obj.TYPE_CONST {
+			v := p.From.Offset & 0xffffffff
+			p.From.Offset = v | v<<32
+		}
 	}
 
 	if ctxt.Flag_dynlink {
@@ -339,7 +371,7 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 	// We only care about global data: NAME_EXTERN means a global
 	// symbol in the Go sense, and p.Sym.Local is true for a few
 	// internally defined symbols.
-	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
+	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
 		// MOVD $sym, Rx becomes MOVD sym at GOT, Rx
 		// MOVD $sym+<off>, Rx becomes MOVD sym at GOT, Rx; ADD <off>, Rx
 		if p.As != AMOVD {
@@ -366,12 +398,12 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 	// MOVx sym, Ry becomes MOVD sym at GOT, REGTMP; MOVx (REGTMP), Ry
 	// MOVx Ry, sym becomes MOVD sym at GOT, REGTMP; MOVD Ry, (REGTMP)
 	// An addition may be inserted between the two MOVs if there is an offset.
-	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
-		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
+	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
+		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
 		}
 		source = &p.From
-	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
+	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 		source = &p.To
 	} else {
 		return
@@ -455,9 +487,17 @@ func relinv(a obj.As) obj.As {
 		return ABLE
 	case ABLE:
 		return ABGT
+	case ACBZ:
+		return ACBNZ
+	case ACBNZ:
+		return ACBZ
+	case ACBZW:
+		return ACBNZW
+	case ACBNZW:
+		return ACBZW
 	}
 
-	log.Fatalf("unknown relation: %s", Anames[a])
+	log.Fatalf("unknown relation: %s", Anames[a-obj.ABaseARM64])
 	return 0
 }
 
@@ -607,7 +647,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 	 * strip NOPs
 	 * expand RET
 	 */
-	ctxt.Bso.Flush()
 	q := (*obj.Prog)(nil)
 	var q1 *obj.Prog
 	for p := cursym.Text; p != nil; p = p.Link {
@@ -703,9 +742,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			p.To.Offset = int64(ctxt.Autosize) - 8
 			if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) {
 				if ctxt.Debugvlog != 0 {
-					fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Text.From.Sym.Name)
+					ctxt.Logf("save suppressed in: %s\n", cursym.Text.From.Sym.Name)
 				}
-				ctxt.Bso.Flush()
 				cursym.Text.Mark |= LEAF
 			}
 
@@ -718,42 +756,58 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				aoffset = 0xF0
 			}
 			if cursym.Text.Mark&LEAF != 0 {
-				cursym.Leaf = true
+				cursym.Set(obj.AttrLeaf, true)
 				if ctxt.Autosize == 0 {
 					break
 				}
-				aoffset = 0
 			}
 
+			// Frame is non-empty. Make sure to save link register, even if
+			// it is a leaf function, so that traceback works.
 			q = p
 			if ctxt.Autosize > aoffset {
-				q = ctxt.NewProg()
-				q.As = ASUB
+				// Frame size is too large for a MOVD.W instruction.
+				// Store link register before decrementing SP, so if a signal comes
+				// during the execution of the function prologue, the traceback
+				// code will not see a half-updated stack frame.
+				q = obj.Appendp(ctxt, q)
 				q.Lineno = p.Lineno
+				q.As = ASUB
 				q.From.Type = obj.TYPE_CONST
-				q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
+				q.From.Offset = int64(ctxt.Autosize)
+				q.Reg = REGSP
 				q.To.Type = obj.TYPE_REG
-				q.To.Reg = REGSP
-				q.Spadj = int32(q.From.Offset)
-				q.Link = p.Link
-				p.Link = q
-				if cursym.Text.Mark&LEAF != 0 {
-					break
-				}
-			}
+				q.To.Reg = REGTMP
 
-			q1 = ctxt.NewProg()
-			q1.As = AMOVD
-			q1.Lineno = p.Lineno
-			q1.From.Type = obj.TYPE_REG
-			q1.From.Reg = REGLINK
-			q1.To.Type = obj.TYPE_MEM
-			q1.Scond = C_XPRE
-			q1.To.Offset = int64(-aoffset)
-			q1.To.Reg = REGSP
-			q1.Link = q.Link
-			q1.Spadj = aoffset
-			q.Link = q1
+				q = obj.Appendp(ctxt, q)
+				q.Lineno = p.Lineno
+				q.As = AMOVD
+				q.From.Type = obj.TYPE_REG
+				q.From.Reg = REGLINK
+				q.To.Type = obj.TYPE_MEM
+				q.To.Reg = REGTMP
+
+				q1 = obj.Appendp(ctxt, q)
+				q1.Lineno = p.Lineno
+				q1.As = AMOVD
+				q1.From.Type = obj.TYPE_REG
+				q1.From.Reg = REGTMP
+				q1.To.Type = obj.TYPE_REG
+				q1.To.Reg = REGSP
+				q1.Spadj = ctxt.Autosize
+			} else {
+				// small frame, update SP and save LR in a single MOVD.W instruction
+				q1 = obj.Appendp(ctxt, q)
+				q1.As = AMOVD
+				q1.Lineno = p.Lineno
+				q1.From.Type = obj.TYPE_REG
+				q1.From.Reg = REGLINK
+				q1.To.Type = obj.TYPE_MEM
+				q1.Scond = C_XPRE
+				q1.To.Offset = int64(-aoffset)
+				q1.To.Reg = REGSP
+				q1.Spadj = aoffset
+			}
 
 			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
diff --git a/src/cmd/internal/obj/data.go b/src/cmd/internal/obj/data.go
index 29530fa..d5565f2 100644
--- a/src/cmd/internal/obj/data.go
+++ b/src/cmd/internal/obj/data.go
@@ -1,6 +1,6 @@
 // Derived from Inferno utils/6l/obj.c and utils/6l/span.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -145,6 +145,22 @@ func (s *LSym) WriteOff(ctxt *Link, off int64, rsym *LSym, roff int64) {
 	r.Add = roff
 }
 
+// WriteWeakOff writes a weak 4 byte offset to rsym+roff into s at offset off.
+// After linking the 4 bytes stored at s+off will be
+// rsym+roff-(start of section that s is in).
+func (s *LSym) WriteWeakOff(ctxt *Link, off int64, rsym *LSym, roff int64) {
+	s.prepwrite(ctxt, off, 4)
+	r := Addrel(s)
+	r.Off = int32(off)
+	if int64(r.Off) != off {
+		ctxt.Diag("WriteOff: off overflow %d in %s", off, s.Name)
+	}
+	r.Siz = 4
+	r.Sym = rsym
+	r.Type = R_WEAKADDROFF
+	r.Add = roff
+}
+
 // WriteString writes a string of size siz into s at offset off.
 func (s *LSym) WriteString(ctxt *Link, off int64, siz int, str string) {
 	if siz < len(str) {
diff --git a/src/cmd/internal/obj/ld.go b/src/cmd/internal/obj/ld.go
index 81a16d1..54fde2f 100644
--- a/src/cmd/internal/obj/ld.go
+++ b/src/cmd/internal/obj/ld.go
@@ -1,6 +1,6 @@
 // Derived from Inferno utils/6l/obj.c and utils/6l/span.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index b6861f4..2ab2aec 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -1,5 +1,5 @@
 // Derived from Inferno utils/6l/l.h and related files.
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -33,6 +33,7 @@ package obj
 import (
 	"bufio"
 	"cmd/internal/sys"
+	"fmt"
 )
 
 // An Addr is an argument to an instruction.
@@ -112,13 +113,17 @@ import (
 //			val = int32(y)
 //
 //	reg<<shift, reg>>shift, reg->shift, reg@>shift
-//		Shifted register value, for ARM.
+//		Shifted register value, for ARM and ARM64.
 //		In this form, reg must be a register and shift can be a register or an integer constant.
 //		Encoding:
 //			type = TYPE_SHIFT
+//		On ARM:
 //			offset = (reg&15) | shifttype<<5 | count
 //			shifttype = 0, 1, 2, 3 for <<, >>, ->, @>
 //			count = (reg&15)<<8 | 1<<4 for a register shift count, (n&31)<<7 for an integer constant.
+//		On ARM64:
+//			offset = (reg&31)<<16 | shifttype<<22 | (count&63)<<10
+//			shifttype = 0, 1, 2 for <<, >>, ->
 //
 //	(reg, reg)
 //		A destination register pair. When used as the last argument of an instruction,
@@ -153,11 +158,8 @@ type Addr struct {
 	Type   AddrType
 	Name   int8
 	Class  int8
-	Etype  uint8
 	Offset int64
-	Width  int64
 	Sym    *LSym
-	Gotype *LSym
 
 	// argument value:
 	//	for TYPE_SCONST, a string
@@ -200,34 +202,55 @@ const (
 	TYPE_REGLIST
 )
 
-// TODO(rsc): Describe prog.
-// TODO(rsc): Describe TEXT/GLOBL flag in from3
+// Prog describes a single machine instruction.
+//
+// The general instruction form is:
+//
+//	As.Scond From, Reg, From3, To, RegTo2
+//
+// where As is an opcode and the others are arguments:
+// From, Reg, From3 are sources, and To, RegTo2 are destinations.
+// Usually, not all arguments are present.
+// For example, MOVL R1, R2 encodes using only As=MOVL, From=R1, To=R2.
+// The Scond field holds additional condition bits for systems (like arm)
+// that have generalized conditional execution.
+//
+// Jump instructions use the Pcond field to point to the target instruction,
+// which must be in the same linked list as the jump instruction.
+//
+// The Progs for a given function are arranged in a list linked through the Link field.
+//
+// Each Prog is charged to a specific source line in the debug information,
+// specified by Lineno, an index into the line history (see LineHist).
+// Every Prog has a Ctxt field that defines various context, including the current LineHist.
+// Progs should be allocated using ctxt.NewProg(), not new(Prog).
+//
+// The other fields not yet mentioned are for use by the back ends and should
+// be left zeroed by creators of Prog lists.
 type Prog struct {
-	Ctxt   *Link
-	Link   *Prog
-	From   Addr
-	From3  *Addr // optional
-	To     Addr
-	Opt    interface{}
-	Forwd  *Prog
-	Pcond  *Prog
-	Rel    *Prog // Source of forward jumps on x86; pcrel on arm
-	Pc     int64
-	Lineno int32
-	Spadj  int32
-	As     As // Assembler opcode.
-	Reg    int16
-	RegTo2 int16  // 2nd register output operand
-	Mark   uint16 // bitmask of arch-specific items
-	Optab  uint16
-	Scond  uint8
-	Back   uint8
-	Ft     uint8
-	Tt     uint8
-	Isize  uint8 // size of the instruction in bytes (x86 only)
-	Mode   int8
-
-	Info ProgInfo
+	Ctxt   *Link       // linker context
+	Link   *Prog       // next Prog in linked list
+	From   Addr        // first source operand
+	From3  *Addr       // third source operand (second is Reg below)
+	To     Addr        // destination operand (second is RegTo2 below)
+	Pcond  *Prog       // target of conditional jump
+	Opt    interface{} // available to optimization passes to hold per-Prog state
+	Forwd  *Prog       // for x86 back end
+	Rel    *Prog       // for x86, arm back ends
+	Pc     int64       // for back ends or assembler: virtual or actual program counter, depending on phase
+	Lineno int32       // line number of this instruction
+	Spadj  int32       // effect of instruction on stack pointer (increment or decrement amount)
+	As     As          // assembler opcode
+	Reg    int16       // 2nd source operand
+	RegTo2 int16       // 2nd destination operand
+	Mark   uint16      // bitmask of arch-specific items
+	Optab  uint16      // arch-specific opcode index
+	Scond  uint8       // condition bits for conditional instruction (e.g., on ARM)
+	Back   uint8       // for x86 back end: backwards branch state
+	Ft     uint8       // for x86 back end: type index of Prog.From
+	Tt     uint8       // for x86 back end: type index of Prog.To
+	Isize  uint8       // for x86 back end: size of the instruction in bytes
+	Mode   int8        // for x86 back end: 32- or 64-bit mode
 }
 
 // From3Type returns From3.Type, or TYPE_NONE when From3 is nil.
@@ -246,17 +269,6 @@ func (p *Prog) From3Offset() int64 {
 	return p.From3.Offset
 }
 
-// ProgInfo holds information about the instruction for use
-// by clients such as the compiler. The exact meaning of this
-// data is up to the client and is not interpreted by the cmd/internal/obj/... packages.
-type ProgInfo struct {
-	_        struct{} // to prevent unkeyed literals. Trailing zero-sized field will take space.
-	Flags    uint32   // flag bits
-	Reguse   uint64   // registers implicitly used by this instruction
-	Regset   uint64   // registers implicitly set by this instruction
-	Regindex uint64   // registers used by addressing mode
-}
-
 // An As denotes an assembler opcode.
 // There are some portable opcodes, declared here in package obj,
 // that are common to all architectures.
@@ -268,12 +280,10 @@ type As int16
 const (
 	AXXX As = iota
 	ACALL
-	ACHECKNIL
 	ADUFFCOPY
 	ADUFFZERO
 	AEND
 	AFUNCDATA
-	AGLOBL
 	AJMP
 	ANOP
 	APCDATA
@@ -296,28 +306,50 @@ const (
 // Subspaces are aligned to a power of two so opcodes can be masked
 // with AMask and used as compact array indices.
 const (
-	ABase386 = (1 + iota) << 12
+	ABase386 = (1 + iota) << 10
 	ABaseARM
 	ABaseAMD64
 	ABasePPC64
 	ABaseARM64
-	ABaseMIPS64
+	ABaseMIPS
 	ABaseS390X
 
-	AMask = 1<<12 - 1 // AND with this to use the opcode as an array index.
+	AllowedOpCodes = 1 << 10            // The number of opcodes available for any given architecture.
+	AMask          = AllowedOpCodes - 1 // AND with this to use the opcode as an array index.
 )
 
 // An LSym is the sort of symbol that is written to an object file.
 type LSym struct {
-	Name      string
-	Type      int16
-	Version   int16
-	Dupok     bool
-	Cfunc     bool
-	Nosplit   bool
-	Leaf      bool
-	Seenglobl bool
-	Onlist    bool
+	Name    string
+	Type    SymKind
+	Version int16
+	Attribute
+
+	RefIdx int // Index of this symbol in the symbol reference list.
+	Args   int32
+	Locals int32
+	Size   int64
+	Gotype *LSym
+	Autom  *Auto
+	Text   *Prog
+	Pcln   *Pcln
+	P      []byte
+	R      []Reloc
+}
+
+// Attribute is a set of symbol attributes.
+type Attribute int16
+
+const (
+	AttrDuplicateOK Attribute = 1 << iota
+	AttrCFunc
+	AttrNoSplit
+	AttrLeaf
+	AttrSeenGlobl
+	AttrOnList
+
+	// MakeTypelink means that the type should have an entry in the typelink table.
+	AttrMakeTypelink
 
 	// ReflectMethod means the function may call reflect.Type.Method or
 	// reflect.Type.MethodByName. Matching is imprecise (as reflect.Type
@@ -325,7 +357,7 @@ type LSym struct {
 	// set in some cases when the reflect package is not called.
 	//
 	// Used by the linker to determine what methods can be pruned.
-	ReflectMethod bool
+	AttrReflectMethod
 
 	// Local means make the symbol local even when compiling Go code to reference Go
 	// symbols in other shared libraries, as in this mode symbols are global by
@@ -333,18 +365,25 @@ type LSym struct {
 	// visible outside of the module (shared library or executable) that contains its
 	// definition. (When not compiling to support Go shared libraries, all symbols are
 	// local in this sense unless there is a cgo_export_* directive).
-	Local bool
+	AttrLocal
+)
 
-	RefIdx int // Index of this symbol in the symbol reference list.
-	Args   int32
-	Locals int32
-	Size   int64
-	Gotype *LSym
-	Autom  *Auto
-	Text   *Prog
-	Pcln   *Pcln
-	P      []byte
-	R      []Reloc
+func (a Attribute) DuplicateOK() bool   { return a&AttrDuplicateOK != 0 }
+func (a Attribute) MakeTypelink() bool  { return a&AttrMakeTypelink != 0 }
+func (a Attribute) CFunc() bool         { return a&AttrCFunc != 0 }
+func (a Attribute) NoSplit() bool       { return a&AttrNoSplit != 0 }
+func (a Attribute) Leaf() bool          { return a&AttrLeaf != 0 }
+func (a Attribute) SeenGlobl() bool     { return a&AttrSeenGlobl != 0 }
+func (a Attribute) OnList() bool        { return a&AttrOnList != 0 }
+func (a Attribute) ReflectMethod() bool { return a&AttrReflectMethod != 0 }
+func (a Attribute) Local() bool         { return a&AttrLocal != 0 }
+
+func (a *Attribute) Set(flag Attribute, value bool) {
+	if value {
+		*a |= flag
+	} else {
+		*a &^= flag
+	}
 }
 
 // The compiler needs LSym to satisfy fmt.Stringer, because it stores
@@ -365,21 +404,33 @@ type Pcln struct {
 	Lastindex   int
 }
 
-// LSym.type
+// A SymKind describes the kind of memory represented by a symbol.
+type SymKind int16
+
+// Defined SymKind values.
+//
+// TODO(rsc): Give idiomatic Go names.
+// TODO(rsc): Reduce the number of symbol types in the object files.
+//go:generate stringer -type=SymKind
 const (
-	Sxxx = iota
+	Sxxx SymKind = iota
 	STEXT
 	SELFRXSECT
 
+	// Read-only sections.
 	STYPE
 	SSTRING
 	SGOSTRING
-	SGOSTRINGHDR
 	SGOFUNC
 	SGCBITS
 	SRODATA
 	SFUNCTAB
 
+	SELFROSECT
+	SMACHOPLT
+
+	// Read-only sections with relocations.
+	//
 	// Types STYPE-SFUNCTAB above are written to the .rodata section by default.
 	// When linking a shared object, some conceptually "read only" types need to
 	// be written to by relocations and putting them in a section called
@@ -393,18 +444,18 @@ const (
 	STYPERELRO
 	SSTRINGRELRO
 	SGOSTRINGRELRO
-	SGOSTRINGHDRRELRO
 	SGOFUNCRELRO
 	SGCBITSRELRO
 	SRODATARELRO
 	SFUNCTABRELRO
 
+	// Part of .data.rel.ro if it exists, otherwise part of .rodata.
 	STYPELINK
 	SITABLINK
 	SSYMTAB
 	SPCLNTAB
-	SELFROSECT
-	SMACHOPLT
+
+	// Writable sections.
 	SELFSECT
 	SMACHO
 	SMACHOGOT
@@ -428,23 +479,50 @@ const (
 	SHOSTOBJ
 	SDWARFSECT
 	SDWARFINFO
-	SSUB       = 1 << 8
-	SMASK      = SSUB - 1
-	SHIDDEN    = 1 << 9
-	SCONTAINER = 1 << 10 // has a sub-symbol
+	SSUB       = SymKind(1 << 8)
+	SMASK      = SymKind(SSUB - 1)
+	SHIDDEN    = SymKind(1 << 9)
+	SCONTAINER = SymKind(1 << 10) // has a sub-symbol
 )
 
+// ReadOnly are the symbol kinds that form read-only sections. In some
+// cases, if they will require relocations, they are transformed into
+// rel-ro sections using RelROMap.
+var ReadOnly = []SymKind{
+	STYPE,
+	SSTRING,
+	SGOSTRING,
+	SGOFUNC,
+	SGCBITS,
+	SRODATA,
+	SFUNCTAB,
+}
+
+// RelROMap describes the transformation of read-only symbols to rel-ro
+// symbols.
+var RelROMap = map[SymKind]SymKind{
+	STYPE:     STYPERELRO,
+	SSTRING:   SSTRINGRELRO,
+	SGOSTRING: SGOSTRINGRELRO,
+	SGOFUNC:   SGOFUNCRELRO,
+	SGCBITS:   SGCBITSRELRO,
+	SRODATA:   SRODATARELRO,
+	SFUNCTAB:  SFUNCTABRELRO,
+}
+
 type Reloc struct {
 	Off  int32
 	Siz  uint8
-	Type int32
+	Type RelocType
 	Add  int64
 	Sym  *LSym
 }
 
-// Reloc.type
+type RelocType int32
+
+//go:generate stringer -type=RelocType
 const (
-	R_ADDR = 1 + iota
+	R_ADDR RelocType = 1 + iota
 	// R_ADDRPOWER relocates a pair of "D-form" instructions (instructions with 16-bit
 	// immediates in the low half of the instruction word), usually addis followed by
 	// another add or a load, inserting the "high adjusted" 16 bits of the address of
@@ -454,12 +532,17 @@ const (
 	// R_ADDRARM64 relocates an adrp, add pair to compute the address of the
 	// referenced symbol.
 	R_ADDRARM64
-	// R_ADDRMIPS (only used on mips64) resolves to the low 16 bits of an external
+	// R_ADDRMIPS (only used on mips/mips64) resolves to the low 16 bits of an external
 	// address, by encoding it into the instruction.
 	R_ADDRMIPS
 	// R_ADDROFF resolves to a 32-bit offset from the beginning of the section
 	// holding the data being relocated to the referenced symbol.
 	R_ADDROFF
+	// R_WEAKADDROFF resolves just like R_ADDROFF but is a weak relocation.
+	// A weak relocation does not make the symbol it refers to reachable,
+	// and is only honored by the linker if the symbol is in some other way
+	// reachable.
+	R_WEAKADDROFF
 	R_SIZE
 	R_CALL
 	R_CALLARM
@@ -582,7 +665,7 @@ const (
 	// TODO(mundaym): remove once variants can be serialized - see issue 14218.
 	R_PCRELDBL
 
-	// R_ADDRMIPSU (only used on mips64) resolves to the sign-adjusted "upper" 16
+	// R_ADDRMIPSU (only used on mips/mips64) resolves to the sign-adjusted "upper" 16
 	// bits (bit 16-31) of an external address, by encoding it into the instruction.
 	R_ADDRMIPSU
 	// R_ADDRMIPSTLS (only used on mips64) resolves to the low 16 bits of a TLS
@@ -590,6 +673,20 @@ const (
 	R_ADDRMIPSTLS
 )
 
+// IsDirectJump returns whether r is a relocation for a direct jump.
+// A direct jump is a CALL or JMP instruction that takes the target address
+// as immediate. The address is embedded into the instruction, possibly
+// with limited width.
+// An indirect jump is a CALL or JMP instruction that takes the target address
+// in register or memory.
+func (r RelocType) IsDirectJump() bool {
+	switch r {
+	case R_CALL, R_CALLARM, R_CALLARM64, R_CALLPOWER, R_CALLMIPS, R_JMPMIPS:
+		return true
+	}
+	return false
+}
+
 type Auto struct {
 	Asym    *LSym
 	Link    *Auto
@@ -617,8 +714,7 @@ const (
 // Link holds the context for writing object code from a compiler
 // to be linker input or for reading that input into the linker.
 type Link struct {
-	Goarm         int32
-	Headtype      int
+	Headtype      HeadType
 	Arch          *LinkArch
 	Debugasm      int32
 	Debugvlog     int32
@@ -629,13 +725,10 @@ type Link struct {
 	Flag_optimize bool
 	Bso           *bufio.Writer
 	Pathname      string
-	Goroot        string
-	Goroot_final  string
 	Hash          map[SymVer]*LSym
 	LineHist      LineHist
 	Imports       []string
-	Plist         *Plist
-	Plast         *Plist
+	Plists        []*Plist
 	Sym_div       *LSym
 	Sym_divu      *LSym
 	Sym_mod       *LSym
@@ -660,8 +753,6 @@ type Link struct {
 	Mode          int
 	Cursym        *LSym
 	Version       int
-	Textp         *LSym
-	Etextp        *LSym
 	Errors        int
 
 	Framepointer_enabled bool
@@ -680,6 +771,11 @@ func (ctxt *Link) Diag(format string, args ...interface{}) {
 	ctxt.DiagFunc(format, args...)
 }
 
+func (ctxt *Link) Logf(format string, args ...interface{}) {
+	fmt.Fprintf(ctxt.Bso, format, args...)
+	ctxt.Bso.Flush()
+}
+
 // The smallest possible offset from the hardware stack pointer to a local
 // variable on the stack. Architectures that use a link register save its value
 // on the stack in the function prologue and so always have a pointer between
@@ -712,9 +808,11 @@ type LinkArch struct {
 	UnaryDst   map[As]bool // Instruction takes one operand, a destination.
 }
 
-/* executable header types */
+// HeadType is the executable header type.
+type HeadType uint8
+
 const (
-	Hunknown = 0 + iota
+	Hunknown HeadType = iota
 	Hdarwin
 	Hdragonfly
 	Hfreebsd
@@ -725,8 +823,67 @@ const (
 	Hplan9
 	Hsolaris
 	Hwindows
+	Hwindowsgui
 )
 
+func (h *HeadType) Set(s string) error {
+	switch s {
+	case "darwin":
+		*h = Hdarwin
+	case "dragonfly":
+		*h = Hdragonfly
+	case "freebsd":
+		*h = Hfreebsd
+	case "linux", "android":
+		*h = Hlinux
+	case "nacl":
+		*h = Hnacl
+	case "netbsd":
+		*h = Hnetbsd
+	case "openbsd":
+		*h = Hopenbsd
+	case "plan9":
+		*h = Hplan9
+	case "solaris":
+		*h = Hsolaris
+	case "windows":
+		*h = Hwindows
+	case "windowsgui":
+		*h = Hwindowsgui
+	default:
+		return fmt.Errorf("invalid headtype: %q", s)
+	}
+	return nil
+}
+
+func (h *HeadType) String() string {
+	switch *h {
+	case Hdarwin:
+		return "darwin"
+	case Hdragonfly:
+		return "dragonfly"
+	case Hfreebsd:
+		return "freebsd"
+	case Hlinux:
+		return "linux"
+	case Hnacl:
+		return "nacl"
+	case Hnetbsd:
+		return "netbsd"
+	case Hopenbsd:
+		return "openbsd"
+	case Hplan9:
+		return "plan9"
+	case Hsolaris:
+		return "solaris"
+	case Hwindows:
+		return "windows"
+	case Hwindowsgui:
+		return "windowsgui"
+	}
+	return fmt.Sprintf("HeadType(%d)", *h)
+}
+
 // AsmBuf is a simple buffer to assemble variable-length x86 instructions into.
 type AsmBuf struct {
 	buf [100]byte
diff --git a/src/cmd/internal/obj/mips/a.out.go b/src/cmd/internal/obj/mips/a.out.go
index bc6b69d..f732ed5 100644
--- a/src/cmd/internal/obj/mips/a.out.go
+++ b/src/cmd/internal/obj/mips/a.out.go
@@ -44,7 +44,7 @@ const (
 )
 
 const (
-	REG_R0 = obj.RBaseMIPS64 + iota
+	REG_R0 = obj.RBaseMIPS + iota
 	REG_R1
 	REG_R2
 	REG_R3
@@ -185,22 +185,18 @@ const (
 
 	REG_SPECIAL = REG_M0
 
-	REGZERO  = REG_R0 /* set to zero */
-	REGSP    = REG_R29
-	REGSB    = REG_R28
-	REGLINK  = REG_R31
-	REGRET   = REG_R1
-	REGARG   = -1      /* -1 disables passing the first argument in register */
-	REGRT1   = REG_R1  /* reserved for runtime, duffzero and duffcopy */
-	REGRT2   = REG_R2  /* reserved for runtime, duffcopy */
-	REGCTXT  = REG_R22 /* context for closures */
-	REGG     = REG_R30 /* G */
-	REGTMP   = REG_R23 /* used by the linker */
-	FREGRET  = REG_F0
-	FREGZERO = REG_F24 /* both float and double */
-	FREGHALF = REG_F26 /* double */
-	FREGONE  = REG_F28 /* double */
-	FREGTWO  = REG_F30 /* double */
+	REGZERO = REG_R0 /* set to zero */
+	REGSP   = REG_R29
+	REGSB   = REG_R28
+	REGLINK = REG_R31
+	REGRET  = REG_R1
+	REGARG  = -1      /* -1 disables passing the first argument in register */
+	REGRT1  = REG_R1  /* reserved for runtime, duffzero and duffcopy */
+	REGRT2  = REG_R2  /* reserved for runtime, duffcopy */
+	REGCTXT = REG_R22 /* context for closures */
+	REGG    = REG_R30 /* G */
+	REGTMP  = REG_R23 /* used by the linker */
+	FREGRET = REG_F0
 )
 
 const (
@@ -222,6 +218,11 @@ const (
 )
 
 const (
+	Mips32 = 32
+	Mips64 = 64
+)
+
+const (
 	C_NONE = iota
 	C_REG
 	C_FREG
@@ -262,7 +263,7 @@ const (
 )
 
 const (
-	AABSD = obj.ABaseMIPS64 + obj.A_ARCHSPECIFIC + iota
+	AABSD = obj.ABaseMIPS + obj.A_ARCHSPECIFIC + iota
 	AABSF
 	AABSW
 	AADD
@@ -282,6 +283,12 @@ const (
 	ABLTZAL
 	ABNE
 	ABREAK
+	ACLO
+	ACLZ
+	ACMOVF
+	ACMOVN
+	ACMOVT
+	ACMOVZ
 	ACMPEQD
 	ACMPEQF
 	ACMPGED
@@ -294,6 +301,7 @@ const (
 	ADIVU
 	ADIVW
 	AGOK
+	ALL
 	ALUI
 	AMOVB
 	AMOVBU
@@ -323,9 +331,12 @@ const (
 	AREM
 	AREMU
 	ARFE
+	ASC
 	ASGT
 	ASGTU
 	ASLL
+	ASQRTD
+	ASQRTF
 	ASRA
 	ASRL
 	ASUB
@@ -333,11 +344,14 @@ const (
 	ASUBF
 	ASUBU
 	ASUBW
+	ASYNC
 	ASYSCALL
+	ATEQ
 	ATLBP
 	ATLBR
 	ATLBWI
 	ATLBWR
+	ATNE
 	AWORD
 	AXOR
 
diff --git a/src/cmd/internal/obj/mips/anames.go b/src/cmd/internal/obj/mips/anames.go
index c784809..8482a9e 100644
--- a/src/cmd/internal/obj/mips/anames.go
+++ b/src/cmd/internal/obj/mips/anames.go
@@ -26,6 +26,12 @@ var Anames = []string{
 	"BLTZAL",
 	"BNE",
 	"BREAK",
+	"CLO",
+	"CLZ",
+	"CMOVF",
+	"CMOVN",
+	"CMOVT",
+	"CMOVZ",
 	"CMPEQD",
 	"CMPEQF",
 	"CMPGED",
@@ -38,6 +44,7 @@ var Anames = []string{
 	"DIVU",
 	"DIVW",
 	"GOK",
+	"LL",
 	"LUI",
 	"MOVB",
 	"MOVBU",
@@ -67,9 +74,12 @@ var Anames = []string{
 	"REM",
 	"REMU",
 	"RFE",
+	"SC",
 	"SGT",
 	"SGTU",
 	"SLL",
+	"SQRTD",
+	"SQRTF",
 	"SRA",
 	"SRL",
 	"SUB",
@@ -77,11 +87,14 @@ var Anames = []string{
 	"SUBF",
 	"SUBU",
 	"SUBW",
+	"SYNC",
 	"SYSCALL",
+	"TEQ",
 	"TLBP",
 	"TLBR",
 	"TLBWI",
 	"TLBWR",
+	"TNE",
 	"WORD",
 	"XOR",
 	"MOVV",
diff --git a/src/cmd/internal/obj/mips/asm0.go b/src/cmd/internal/obj/mips/asm0.go
index 11aa202..c421dee 100644
--- a/src/cmd/internal/obj/mips/asm0.go
+++ b/src/cmd/internal/obj/mips/asm0.go
@@ -39,7 +39,7 @@ import (
 // Instruction layout.
 
 const (
-	FuncAlign = 8
+	mips64FuncAlign = 8
 )
 
 const (
@@ -54,270 +54,319 @@ type Optab struct {
 	type_ int8
 	size  int8
 	param int16
+	mode  int
 }
 
 var optab = []Optab{
-	{obj.ATEXT, C_LEXT, C_NONE, C_TEXTSIZE, 0, 0, 0},
-	{obj.ATEXT, C_ADDR, C_NONE, C_TEXTSIZE, 0, 0, 0},
-
-	{AMOVW, C_REG, C_NONE, C_REG, 1, 4, 0},
-	{AMOVV, C_REG, C_NONE, C_REG, 1, 4, 0},
-	{AMOVB, C_REG, C_NONE, C_REG, 12, 8, 0},
-	{AMOVBU, C_REG, C_NONE, C_REG, 13, 4, 0},
-	{AMOVWU, C_REG, C_NONE, C_REG, 14, 8, 0},
-
-	{ASUB, C_REG, C_REG, C_REG, 2, 4, 0},
-	{AADD, C_REG, C_REG, C_REG, 2, 4, 0},
-	{AAND, C_REG, C_REG, C_REG, 2, 4, 0},
-	{ASUB, C_REG, C_NONE, C_REG, 2, 4, 0},
-	{AADD, C_REG, C_NONE, C_REG, 2, 4, 0},
-	{AAND, C_REG, C_NONE, C_REG, 2, 4, 0},
-
-	{ASLL, C_REG, C_NONE, C_REG, 9, 4, 0},
-	{ASLL, C_REG, C_REG, C_REG, 9, 4, 0},
-
-	{AADDF, C_FREG, C_NONE, C_FREG, 32, 4, 0},
-	{AADDF, C_FREG, C_REG, C_FREG, 32, 4, 0},
-	{ACMPEQF, C_FREG, C_REG, C_NONE, 32, 4, 0},
-	{AABSF, C_FREG, C_NONE, C_FREG, 33, 4, 0},
-	{AMOVF, C_FREG, C_NONE, C_FREG, 33, 4, 0},
-	{AMOVD, C_FREG, C_NONE, C_FREG, 33, 4, 0},
-
-	{AMOVW, C_REG, C_NONE, C_SEXT, 7, 4, REGSB},
-	{AMOVWU, C_REG, C_NONE, C_SEXT, 7, 4, REGSB},
-	{AMOVV, C_REG, C_NONE, C_SEXT, 7, 4, REGSB},
-	{AMOVB, C_REG, C_NONE, C_SEXT, 7, 4, REGSB},
-	{AMOVBU, C_REG, C_NONE, C_SEXT, 7, 4, REGSB},
-	{AMOVWL, C_REG, C_NONE, C_SEXT, 7, 4, REGSB},
-	{AMOVW, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP},
-	{AMOVWU, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP},
-	{AMOVV, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP},
-	{AMOVB, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP},
-	{AMOVBU, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP},
-	{AMOVWL, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP},
-	{AMOVW, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO},
-	{AMOVWU, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO},
-	{AMOVV, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO},
-	{AMOVB, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO},
-	{AMOVBU, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO},
-	{AMOVWL, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO},
-
-	{AMOVW, C_SEXT, C_NONE, C_REG, 8, 4, REGSB},
-	{AMOVWU, C_SEXT, C_NONE, C_REG, 8, 4, REGSB},
-	{AMOVV, C_SEXT, C_NONE, C_REG, 8, 4, REGSB},
-	{AMOVB, C_SEXT, C_NONE, C_REG, 8, 4, REGSB},
-	{AMOVBU, C_SEXT, C_NONE, C_REG, 8, 4, REGSB},
-	{AMOVWL, C_SEXT, C_NONE, C_REG, 8, 4, REGSB},
-	{AMOVW, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP},
-	{AMOVWU, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP},
-	{AMOVV, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP},
-	{AMOVB, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP},
-	{AMOVBU, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP},
-	{AMOVWL, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP},
-	{AMOVW, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO},
-	{AMOVWU, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO},
-	{AMOVV, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO},
-	{AMOVB, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO},
-	{AMOVBU, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO},
-	{AMOVWL, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO},
-
-	{AMOVW, C_REG, C_NONE, C_LEXT, 35, 12, REGSB},
-	{AMOVWU, C_REG, C_NONE, C_LEXT, 35, 12, REGSB},
-	{AMOVV, C_REG, C_NONE, C_LEXT, 35, 12, REGSB},
-	{AMOVB, C_REG, C_NONE, C_LEXT, 35, 12, REGSB},
-	{AMOVBU, C_REG, C_NONE, C_LEXT, 35, 12, REGSB},
-	{AMOVW, C_REG, C_NONE, C_LAUTO, 35, 12, REGSP},
-	{AMOVWU, C_REG, C_NONE, C_LAUTO, 35, 12, REGSP},
-	{AMOVV, C_REG, C_NONE, C_LAUTO, 35, 12, REGSP},
-	{AMOVB, C_REG, C_NONE, C_LAUTO, 35, 12, REGSP},
-	{AMOVBU, C_REG, C_NONE, C_LAUTO, 35, 12, REGSP},
-	{AMOVW, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO},
-	{AMOVWU, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO},
-	{AMOVV, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO},
-	{AMOVB, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO},
-	{AMOVBU, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO},
-	{AMOVW, C_REG, C_NONE, C_ADDR, 50, 12, 0},
-	{AMOVWU, C_REG, C_NONE, C_ADDR, 50, 12, 0},
-	{AMOVV, C_REG, C_NONE, C_ADDR, 50, 12, 0},
-	{AMOVB, C_REG, C_NONE, C_ADDR, 50, 12, 0},
-	{AMOVBU, C_REG, C_NONE, C_ADDR, 50, 12, 0},
-	{AMOVW, C_REG, C_NONE, C_TLS, 53, 8, 0},
-	{AMOVWU, C_REG, C_NONE, C_TLS, 53, 8, 0},
-	{AMOVV, C_REG, C_NONE, C_TLS, 53, 8, 0},
-	{AMOVB, C_REG, C_NONE, C_TLS, 53, 8, 0},
-	{AMOVBU, C_REG, C_NONE, C_TLS, 53, 8, 0},
-
-	{AMOVW, C_LEXT, C_NONE, C_REG, 36, 12, REGSB},
-	{AMOVWU, C_LEXT, C_NONE, C_REG, 36, 12, REGSB},
-	{AMOVV, C_LEXT, C_NONE, C_REG, 36, 12, REGSB},
-	{AMOVB, C_LEXT, C_NONE, C_REG, 36, 12, REGSB},
-	{AMOVBU, C_LEXT, C_NONE, C_REG, 36, 12, REGSB},
-	{AMOVW, C_LAUTO, C_NONE, C_REG, 36, 12, REGSP},
-	{AMOVWU, C_LAUTO, C_NONE, C_REG, 36, 12, REGSP},
-	{AMOVV, C_LAUTO, C_NONE, C_REG, 36, 12, REGSP},
-	{AMOVB, C_LAUTO, C_NONE, C_REG, 36, 12, REGSP},
-	{AMOVBU, C_LAUTO, C_NONE, C_REG, 36, 12, REGSP},
-	{AMOVW, C_LOREG, C_NONE, C_REG, 36, 12, REGZERO},
-	{AMOVWU, C_LOREG, C_NONE, C_REG, 36, 12, REGZERO},
-	{AMOVV, C_LOREG, C_NONE, C_REG, 36, 12, REGZERO},
-	{AMOVB, C_LOREG, C_NONE, C_REG, 36, 12, REGZERO},
-	{AMOVBU, C_LOREG, C_NONE, C_REG, 36, 12, REGZERO},
-	{AMOVW, C_ADDR, C_NONE, C_REG, 51, 12, 0},
-	{AMOVWU, C_ADDR, C_NONE, C_REG, 51, 12, 0},
-	{AMOVV, C_ADDR, C_NONE, C_REG, 51, 12, 0},
-	{AMOVB, C_ADDR, C_NONE, C_REG, 51, 12, 0},
-	{AMOVBU, C_ADDR, C_NONE, C_REG, 51, 12, 0},
-	{AMOVW, C_TLS, C_NONE, C_REG, 54, 8, 0},
-	{AMOVWU, C_TLS, C_NONE, C_REG, 54, 8, 0},
-	{AMOVV, C_TLS, C_NONE, C_REG, 54, 8, 0},
-	{AMOVB, C_TLS, C_NONE, C_REG, 54, 8, 0},
-	{AMOVBU, C_TLS, C_NONE, C_REG, 54, 8, 0},
-
-	{AMOVW, C_SECON, C_NONE, C_REG, 3, 4, REGSB},
-	{AMOVV, C_SECON, C_NONE, C_REG, 3, 4, REGSB},
-	{AMOVW, C_SACON, C_NONE, C_REG, 3, 4, REGSP},
-	{AMOVV, C_SACON, C_NONE, C_REG, 3, 4, REGSP},
-	{AMOVW, C_LECON, C_NONE, C_REG, 52, 12, REGSB},
-	{AMOVV, C_LECON, C_NONE, C_REG, 52, 12, REGSB},
-	{AMOVW, C_LACON, C_NONE, C_REG, 26, 12, REGSP},
-	{AMOVV, C_LACON, C_NONE, C_REG, 26, 12, REGSP},
-	{AMOVW, C_ADDCON, C_NONE, C_REG, 3, 4, REGZERO},
-	{AMOVV, C_ADDCON, C_NONE, C_REG, 3, 4, REGZERO},
-	{AMOVW, C_ANDCON, C_NONE, C_REG, 3, 4, REGZERO},
-	{AMOVV, C_ANDCON, C_NONE, C_REG, 3, 4, REGZERO},
-	{AMOVW, C_STCON, C_NONE, C_REG, 55, 8, 0},
-	{AMOVV, C_STCON, C_NONE, C_REG, 55, 8, 0},
-
-	{AMOVW, C_UCON, C_NONE, C_REG, 24, 4, 0},
-	{AMOVV, C_UCON, C_NONE, C_REG, 24, 4, 0},
-	{AMOVW, C_LCON, C_NONE, C_REG, 19, 8, 0},
-	{AMOVV, C_LCON, C_NONE, C_REG, 19, 8, 0},
-
-	{AMOVW, C_HI, C_NONE, C_REG, 20, 4, 0},
-	{AMOVV, C_HI, C_NONE, C_REG, 20, 4, 0},
-	{AMOVW, C_LO, C_NONE, C_REG, 20, 4, 0},
-	{AMOVV, C_LO, C_NONE, C_REG, 20, 4, 0},
-	{AMOVW, C_REG, C_NONE, C_HI, 21, 4, 0},
-	{AMOVV, C_REG, C_NONE, C_HI, 21, 4, 0},
-	{AMOVW, C_REG, C_NONE, C_LO, 21, 4, 0},
-	{AMOVV, C_REG, C_NONE, C_LO, 21, 4, 0},
-
-	{AMUL, C_REG, C_REG, C_NONE, 22, 4, 0},
-
-	{AADD, C_ADD0CON, C_REG, C_REG, 4, 4, 0},
-	{AADD, C_ADD0CON, C_NONE, C_REG, 4, 4, 0},
-	{AADD, C_ANDCON, C_REG, C_REG, 10, 8, 0},
-	{AADD, C_ANDCON, C_NONE, C_REG, 10, 8, 0},
-
-	{AAND, C_AND0CON, C_REG, C_REG, 4, 4, 0},
-	{AAND, C_AND0CON, C_NONE, C_REG, 4, 4, 0},
-	{AAND, C_ADDCON, C_REG, C_REG, 10, 8, 0},
-	{AAND, C_ADDCON, C_NONE, C_REG, 10, 8, 0},
-
-	{AADD, C_UCON, C_REG, C_REG, 25, 8, 0},
-	{AADD, C_UCON, C_NONE, C_REG, 25, 8, 0},
-	{AAND, C_UCON, C_REG, C_REG, 25, 8, 0},
-	{AAND, C_UCON, C_NONE, C_REG, 25, 8, 0},
-
-	{AADD, C_LCON, C_NONE, C_REG, 23, 12, 0},
-	{AAND, C_LCON, C_NONE, C_REG, 23, 12, 0},
-	{AADD, C_LCON, C_REG, C_REG, 23, 12, 0},
-	{AAND, C_LCON, C_REG, C_REG, 23, 12, 0},
-
-	{ASLL, C_SCON, C_REG, C_REG, 16, 4, 0},
-	{ASLL, C_SCON, C_NONE, C_REG, 16, 4, 0},
-
-	{ASYSCALL, C_NONE, C_NONE, C_NONE, 5, 4, 0},
-
-	{ABEQ, C_REG, C_REG, C_SBRA, 6, 4, 0},
-	{ABEQ, C_REG, C_NONE, C_SBRA, 6, 4, 0},
-	{ABLEZ, C_REG, C_NONE, C_SBRA, 6, 4, 0},
-	{ABFPT, C_NONE, C_NONE, C_SBRA, 6, 8, 0},
-
-	{AJMP, C_NONE, C_NONE, C_LBRA, 11, 4, 0},
-	{AJAL, C_NONE, C_NONE, C_LBRA, 11, 4, 0},
-
-	{AJMP, C_NONE, C_NONE, C_ZOREG, 18, 4, REGZERO},
-	{AJAL, C_NONE, C_NONE, C_ZOREG, 18, 4, REGLINK},
-
-	{AMOVW, C_SEXT, C_NONE, C_FREG, 27, 4, REGSB},
-	{AMOVF, C_SEXT, C_NONE, C_FREG, 27, 4, REGSB},
-	{AMOVD, C_SEXT, C_NONE, C_FREG, 27, 4, REGSB},
-	{AMOVW, C_SAUTO, C_NONE, C_FREG, 27, 4, REGSP},
-	{AMOVF, C_SAUTO, C_NONE, C_FREG, 27, 4, REGSP},
-	{AMOVD, C_SAUTO, C_NONE, C_FREG, 27, 4, REGSP},
-	{AMOVW, C_SOREG, C_NONE, C_FREG, 27, 4, REGZERO},
-	{AMOVF, C_SOREG, C_NONE, C_FREG, 27, 4, REGZERO},
-	{AMOVD, C_SOREG, C_NONE, C_FREG, 27, 4, REGZERO},
-
-	{AMOVW, C_LEXT, C_NONE, C_FREG, 27, 12, REGSB},
-	{AMOVF, C_LEXT, C_NONE, C_FREG, 27, 12, REGSB},
-	{AMOVD, C_LEXT, C_NONE, C_FREG, 27, 12, REGSB},
-	{AMOVW, C_LAUTO, C_NONE, C_FREG, 27, 12, REGSP},
-	{AMOVF, C_LAUTO, C_NONE, C_FREG, 27, 12, REGSP},
-	{AMOVD, C_LAUTO, C_NONE, C_FREG, 27, 12, REGSP},
-	{AMOVW, C_LOREG, C_NONE, C_FREG, 27, 12, REGZERO},
-	{AMOVF, C_LOREG, C_NONE, C_FREG, 27, 12, REGZERO},
-	{AMOVD, C_LOREG, C_NONE, C_FREG, 27, 12, REGZERO},
-	{AMOVF, C_ADDR, C_NONE, C_FREG, 51, 12, 0},
-	{AMOVD, C_ADDR, C_NONE, C_FREG, 51, 12, 0},
-
-	{AMOVW, C_FREG, C_NONE, C_SEXT, 28, 4, REGSB},
-	{AMOVF, C_FREG, C_NONE, C_SEXT, 28, 4, REGSB},
-	{AMOVD, C_FREG, C_NONE, C_SEXT, 28, 4, REGSB},
-	{AMOVW, C_FREG, C_NONE, C_SAUTO, 28, 4, REGSP},
-	{AMOVF, C_FREG, C_NONE, C_SAUTO, 28, 4, REGSP},
-	{AMOVD, C_FREG, C_NONE, C_SAUTO, 28, 4, REGSP},
-	{AMOVW, C_FREG, C_NONE, C_SOREG, 28, 4, REGZERO},
-	{AMOVF, C_FREG, C_NONE, C_SOREG, 28, 4, REGZERO},
-	{AMOVD, C_FREG, C_NONE, C_SOREG, 28, 4, REGZERO},
-
-	{AMOVW, C_FREG, C_NONE, C_LEXT, 28, 12, REGSB},
-	{AMOVF, C_FREG, C_NONE, C_LEXT, 28, 12, REGSB},
-	{AMOVD, C_FREG, C_NONE, C_LEXT, 28, 12, REGSB},
-	{AMOVW, C_FREG, C_NONE, C_LAUTO, 28, 12, REGSP},
-	{AMOVF, C_FREG, C_NONE, C_LAUTO, 28, 12, REGSP},
-	{AMOVD, C_FREG, C_NONE, C_LAUTO, 28, 12, REGSP},
-	{AMOVW, C_FREG, C_NONE, C_LOREG, 28, 12, REGZERO},
-	{AMOVF, C_FREG, C_NONE, C_LOREG, 28, 12, REGZERO},
-	{AMOVD, C_FREG, C_NONE, C_LOREG, 28, 12, REGZERO},
-	{AMOVF, C_FREG, C_NONE, C_ADDR, 50, 12, 0},
-	{AMOVD, C_FREG, C_NONE, C_ADDR, 50, 12, 0},
-
-	{AMOVW, C_REG, C_NONE, C_FREG, 30, 4, 0},
-	{AMOVW, C_FREG, C_NONE, C_REG, 31, 4, 0},
-	{AMOVV, C_REG, C_NONE, C_FREG, 47, 4, 0},
-	{AMOVV, C_FREG, C_NONE, C_REG, 48, 4, 0},
-
-	{AMOVW, C_ADDCON, C_NONE, C_FREG, 34, 8, 0},
-	{AMOVW, C_ANDCON, C_NONE, C_FREG, 34, 8, 0},
-
-	{AMOVW, C_REG, C_NONE, C_MREG, 37, 4, 0},
-	{AMOVV, C_REG, C_NONE, C_MREG, 37, 4, 0},
-	{AMOVW, C_MREG, C_NONE, C_REG, 38, 4, 0},
-	{AMOVV, C_MREG, C_NONE, C_REG, 38, 4, 0},
-
-	{AWORD, C_LCON, C_NONE, C_NONE, 40, 4, 0},
-
-	{AMOVW, C_REG, C_NONE, C_FCREG, 41, 8, 0},
-	{AMOVV, C_REG, C_NONE, C_FCREG, 41, 8, 0},
-	{AMOVW, C_FCREG, C_NONE, C_REG, 42, 4, 0},
-	{AMOVV, C_FCREG, C_NONE, C_REG, 42, 4, 0},
-
-	{ABREAK, C_REG, C_NONE, C_SEXT, 7, 4, REGSB}, /* really CACHE instruction */
-	{ABREAK, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP},
-	{ABREAK, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO},
-	{ABREAK, C_NONE, C_NONE, C_NONE, 5, 4, 0},
-
-	{obj.AUNDEF, C_NONE, C_NONE, C_NONE, 49, 4, 0},
-	{obj.AUSEFIELD, C_ADDR, C_NONE, C_NONE, 0, 0, 0},
-	{obj.APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0},
-	{obj.AFUNCDATA, C_SCON, C_NONE, C_ADDR, 0, 0, 0},
-	{obj.ANOP, C_NONE, C_NONE, C_NONE, 0, 0, 0},
-	{obj.ADUFFZERO, C_NONE, C_NONE, C_LBRA, 11, 4, 0}, // same as AJMP
-	{obj.ADUFFCOPY, C_NONE, C_NONE, C_LBRA, 11, 4, 0}, // same as AJMP
-
-	{obj.AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0},
+	{obj.ATEXT, C_LEXT, C_NONE, C_TEXTSIZE, 0, 0, 0, Mips64},
+	{obj.ATEXT, C_ADDR, C_NONE, C_TEXTSIZE, 0, 0, 0, 0},
+
+	{AMOVW, C_REG, C_NONE, C_REG, 1, 4, 0, 0},
+	{AMOVV, C_REG, C_NONE, C_REG, 1, 4, 0, Mips64},
+	{AMOVB, C_REG, C_NONE, C_REG, 12, 8, 0, 0},
+	{AMOVBU, C_REG, C_NONE, C_REG, 13, 4, 0, 0},
+	{AMOVWU, C_REG, C_NONE, C_REG, 14, 8, 0, Mips64},
+
+	{ASUB, C_REG, C_REG, C_REG, 2, 4, 0, 0},
+	{ASUBV, C_REG, C_REG, C_REG, 2, 4, 0, Mips64},
+	{AADD, C_REG, C_REG, C_REG, 2, 4, 0, 0},
+	{AADDV, C_REG, C_REG, C_REG, 2, 4, 0, Mips64},
+	{AAND, C_REG, C_REG, C_REG, 2, 4, 0, 0},
+	{ASUB, C_REG, C_NONE, C_REG, 2, 4, 0, 0},
+	{ASUBV, C_REG, C_NONE, C_REG, 2, 4, 0, Mips64},
+	{AADD, C_REG, C_NONE, C_REG, 2, 4, 0, 0},
+	{AADDV, C_REG, C_NONE, C_REG, 2, 4, 0, Mips64},
+	{AAND, C_REG, C_NONE, C_REG, 2, 4, 0, 0},
+	{ACMOVN, C_REG, C_REG, C_REG, 2, 4, 0, 0},
+
+	{ASLL, C_REG, C_NONE, C_REG, 9, 4, 0, 0},
+	{ASLL, C_REG, C_REG, C_REG, 9, 4, 0, 0},
+	{ASLLV, C_REG, C_NONE, C_REG, 9, 4, 0, Mips64},
+	{ASLLV, C_REG, C_REG, C_REG, 9, 4, 0, Mips64},
+	{ACLO, C_REG, C_NONE, C_REG, 9, 4, 0, 0},
+
+	{AADDF, C_FREG, C_NONE, C_FREG, 32, 4, 0, 0},
+	{AADDF, C_FREG, C_REG, C_FREG, 32, 4, 0, 0},
+	{ACMPEQF, C_FREG, C_REG, C_NONE, 32, 4, 0, 0},
+	{AABSF, C_FREG, C_NONE, C_FREG, 33, 4, 0, 0},
+	{AMOVVF, C_FREG, C_NONE, C_FREG, 33, 4, 0, Mips64},
+	{AMOVF, C_FREG, C_NONE, C_FREG, 33, 4, 0, 0},
+	{AMOVD, C_FREG, C_NONE, C_FREG, 33, 4, 0, 0},
+
+	{AMOVW, C_REG, C_NONE, C_SEXT, 7, 4, REGSB, Mips64},
+	{AMOVWU, C_REG, C_NONE, C_SEXT, 7, 4, REGSB, Mips64},
+	{AMOVV, C_REG, C_NONE, C_SEXT, 7, 4, REGSB, Mips64},
+	{AMOVB, C_REG, C_NONE, C_SEXT, 7, 4, REGSB, Mips64},
+	{AMOVBU, C_REG, C_NONE, C_SEXT, 7, 4, REGSB, Mips64},
+	{AMOVWL, C_REG, C_NONE, C_SEXT, 7, 4, REGSB, Mips64},
+	{AMOVVL, C_REG, C_NONE, C_SEXT, 7, 4, REGSB, Mips64},
+	{AMOVW, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP, 0},
+	{AMOVWU, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP, Mips64},
+	{AMOVV, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP, Mips64},
+	{AMOVB, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP, 0},
+	{AMOVBU, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP, 0},
+	{AMOVWL, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP, 0},
+	{AMOVVL, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP, Mips64},
+	{AMOVW, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO, 0},
+	{AMOVWU, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO, Mips64},
+	{AMOVV, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO, Mips64},
+	{AMOVB, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO, 0},
+	{AMOVBU, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO, 0},
+	{AMOVWL, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO, 0},
+	{AMOVVL, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO, Mips64},
+	{ASC, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO, 0},
+
+	{AMOVW, C_SEXT, C_NONE, C_REG, 8, 4, REGSB, Mips64},
+	{AMOVWU, C_SEXT, C_NONE, C_REG, 8, 4, REGSB, Mips64},
+	{AMOVV, C_SEXT, C_NONE, C_REG, 8, 4, REGSB, Mips64},
+	{AMOVB, C_SEXT, C_NONE, C_REG, 8, 4, REGSB, Mips64},
+	{AMOVBU, C_SEXT, C_NONE, C_REG, 8, 4, REGSB, Mips64},
+	{AMOVWL, C_SEXT, C_NONE, C_REG, 8, 4, REGSB, Mips64},
+	{AMOVVL, C_SEXT, C_NONE, C_REG, 8, 4, REGSB, Mips64},
+	{AMOVW, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP, 0},
+	{AMOVWU, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP, Mips64},
+	{AMOVV, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP, Mips64},
+	{AMOVB, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP, 0},
+	{AMOVBU, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP, 0},
+	{AMOVWL, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP, 0},
+	{AMOVVL, C_SAUTO, C_NONE, C_REG, 8, 4, REGSP, Mips64},
+	{AMOVW, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO, 0},
+	{AMOVWU, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO, Mips64},
+	{AMOVV, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO, Mips64},
+	{AMOVB, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO, 0},
+	{AMOVBU, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO, 0},
+	{AMOVWL, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO, 0},
+	{AMOVVL, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO, Mips64},
+	{ALL, C_SOREG, C_NONE, C_REG, 8, 4, REGZERO, 0},
+
+	{AMOVW, C_REG, C_NONE, C_LEXT, 35, 12, REGSB, Mips64},
+	{AMOVWU, C_REG, C_NONE, C_LEXT, 35, 12, REGSB, Mips64},
+	{AMOVV, C_REG, C_NONE, C_LEXT, 35, 12, REGSB, Mips64},
+	{AMOVB, C_REG, C_NONE, C_LEXT, 35, 12, REGSB, Mips64},
+	{AMOVBU, C_REG, C_NONE, C_LEXT, 35, 12, REGSB, Mips64},
+	{AMOVW, C_REG, C_NONE, C_LAUTO, 35, 12, REGSP, 0},
+	{AMOVWU, C_REG, C_NONE, C_LAUTO, 35, 12, REGSP, Mips64},
+	{AMOVV, C_REG, C_NONE, C_LAUTO, 35, 12, REGSP, Mips64},
+	{AMOVB, C_REG, C_NONE, C_LAUTO, 35, 12, REGSP, 0},
+	{AMOVBU, C_REG, C_NONE, C_LAUTO, 35, 12, REGSP, 0},
+	{AMOVW, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO, 0},
+	{AMOVWU, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO, Mips64},
+	{AMOVV, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO, Mips64},
+	{AMOVB, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO, 0},
+	{AMOVBU, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO, 0},
+	{ASC, C_REG, C_NONE, C_LOREG, 35, 12, REGZERO, 0},
+	{AMOVW, C_REG, C_NONE, C_ADDR, 50, 8, 0, Mips32},
+	{AMOVW, C_REG, C_NONE, C_ADDR, 50, 12, 0, Mips64},
+	{AMOVWU, C_REG, C_NONE, C_ADDR, 50, 12, 0, Mips64},
+	{AMOVV, C_REG, C_NONE, C_ADDR, 50, 12, 0, Mips64},
+	{AMOVB, C_REG, C_NONE, C_ADDR, 50, 8, 0, Mips32},
+	{AMOVB, C_REG, C_NONE, C_ADDR, 50, 12, 0, Mips64},
+	{AMOVBU, C_REG, C_NONE, C_ADDR, 50, 8, 0, Mips32},
+	{AMOVBU, C_REG, C_NONE, C_ADDR, 50, 12, 0, Mips64},
+	{AMOVW, C_REG, C_NONE, C_TLS, 53, 8, 0, 0},
+	{AMOVWU, C_REG, C_NONE, C_TLS, 53, 8, 0, Mips64},
+	{AMOVV, C_REG, C_NONE, C_TLS, 53, 8, 0, Mips64},
+	{AMOVB, C_REG, C_NONE, C_TLS, 53, 8, 0, 0},
+	{AMOVBU, C_REG, C_NONE, C_TLS, 53, 8, 0, 0},
+
+	{AMOVW, C_LEXT, C_NONE, C_REG, 36, 12, REGSB, Mips64},
+	{AMOVWU, C_LEXT, C_NONE, C_REG, 36, 12, REGSB, Mips64},
+	{AMOVV, C_LEXT, C_NONE, C_REG, 36, 12, REGSB, Mips64},
+	{AMOVB, C_LEXT, C_NONE, C_REG, 36, 12, REGSB, Mips64},
+	{AMOVBU, C_LEXT, C_NONE, C_REG, 36, 12, REGSB, Mips64},
+	{AMOVW, C_LAUTO, C_NONE, C_REG, 36, 12, REGSP, 0},
+	{AMOVWU, C_LAUTO, C_NONE, C_REG, 36, 12, REGSP, Mips64},
+	{AMOVV, C_LAUTO, C_NONE, C_REG, 36, 12, REGSP, Mips64},
+	{AMOVB, C_LAUTO, C_NONE, C_REG, 36, 12, REGSP, 0},
+	{AMOVBU, C_LAUTO, C_NONE, C_REG, 36, 12, REGSP, 0},
+	{AMOVW, C_LOREG, C_NONE, C_REG, 36, 12, REGZERO, 0},
+	{AMOVWU, C_LOREG, C_NONE, C_REG, 36, 12, REGZERO, Mips64},
+	{AMOVV, C_LOREG, C_NONE, C_REG, 36, 12, REGZERO, Mips64},
+	{AMOVB, C_LOREG, C_NONE, C_REG, 36, 12, REGZERO, 0},
+	{AMOVBU, C_LOREG, C_NONE, C_REG, 36, 12, REGZERO, 0},
+	{AMOVW, C_ADDR, C_NONE, C_REG, 51, 8, 0, Mips32},
+	{AMOVW, C_ADDR, C_NONE, C_REG, 51, 12, 0, Mips64},
+	{AMOVWU, C_ADDR, C_NONE, C_REG, 51, 12, 0, Mips64},
+	{AMOVV, C_ADDR, C_NONE, C_REG, 51, 12, 0, Mips64},
+	{AMOVB, C_ADDR, C_NONE, C_REG, 51, 8, 0, Mips32},
+	{AMOVB, C_ADDR, C_NONE, C_REG, 51, 12, 0, Mips64},
+	{AMOVBU, C_ADDR, C_NONE, C_REG, 51, 8, 0, Mips32},
+	{AMOVBU, C_ADDR, C_NONE, C_REG, 51, 12, 0, Mips64},
+	{AMOVW, C_TLS, C_NONE, C_REG, 54, 8, 0, 0},
+	{AMOVWU, C_TLS, C_NONE, C_REG, 54, 8, 0, Mips64},
+	{AMOVV, C_TLS, C_NONE, C_REG, 54, 8, 0, Mips64},
+	{AMOVB, C_TLS, C_NONE, C_REG, 54, 8, 0, 0},
+	{AMOVBU, C_TLS, C_NONE, C_REG, 54, 8, 0, 0},
+
+	{AMOVW, C_SECON, C_NONE, C_REG, 3, 4, REGSB, Mips64},
+	{AMOVV, C_SECON, C_NONE, C_REG, 3, 4, REGSB, Mips64},
+	{AMOVW, C_SACON, C_NONE, C_REG, 3, 4, REGSP, 0},
+	{AMOVV, C_SACON, C_NONE, C_REG, 3, 4, REGSP, Mips64},
+	{AMOVW, C_LECON, C_NONE, C_REG, 52, 8, REGSB, Mips32},
+	{AMOVW, C_LECON, C_NONE, C_REG, 52, 12, REGSB, Mips64},
+	{AMOVV, C_LECON, C_NONE, C_REG, 52, 12, REGSB, Mips64},
+
+	{AMOVW, C_LACON, C_NONE, C_REG, 26, 12, REGSP, 0},
+	{AMOVV, C_LACON, C_NONE, C_REG, 26, 12, REGSP, Mips64},
+	{AMOVW, C_ADDCON, C_NONE, C_REG, 3, 4, REGZERO, 0},
+	{AMOVV, C_ADDCON, C_NONE, C_REG, 3, 4, REGZERO, Mips64},
+	{AMOVW, C_ANDCON, C_NONE, C_REG, 3, 4, REGZERO, 0},
+	{AMOVV, C_ANDCON, C_NONE, C_REG, 3, 4, REGZERO, Mips64},
+	{AMOVW, C_STCON, C_NONE, C_REG, 55, 8, 0, 0},
+	{AMOVV, C_STCON, C_NONE, C_REG, 55, 8, 0, Mips64},
+
+	{AMOVW, C_UCON, C_NONE, C_REG, 24, 4, 0, 0},
+	{AMOVV, C_UCON, C_NONE, C_REG, 24, 4, 0, Mips64},
+	{AMOVW, C_LCON, C_NONE, C_REG, 19, 8, 0, 0},
+	{AMOVV, C_LCON, C_NONE, C_REG, 19, 8, 0, Mips64},
+
+	{AMOVW, C_HI, C_NONE, C_REG, 20, 4, 0, 0},
+	{AMOVV, C_HI, C_NONE, C_REG, 20, 4, 0, Mips64},
+	{AMOVW, C_LO, C_NONE, C_REG, 20, 4, 0, 0},
+	{AMOVV, C_LO, C_NONE, C_REG, 20, 4, 0, Mips64},
+	{AMOVW, C_REG, C_NONE, C_HI, 21, 4, 0, 0},
+	{AMOVV, C_REG, C_NONE, C_HI, 21, 4, 0, Mips64},
+	{AMOVW, C_REG, C_NONE, C_LO, 21, 4, 0, 0},
+	{AMOVV, C_REG, C_NONE, C_LO, 21, 4, 0, Mips64},
+
+	{AMUL, C_REG, C_REG, C_NONE, 22, 4, 0, 0},
+	{AMUL, C_REG, C_REG, C_REG, 22, 4, 0, 0},
+	{AMULV, C_REG, C_REG, C_NONE, 22, 4, 0, Mips64},
+
+	{AADD, C_ADD0CON, C_REG, C_REG, 4, 4, 0, 0},
+	{AADD, C_ADD0CON, C_NONE, C_REG, 4, 4, 0, 0},
+	{AADD, C_ANDCON, C_REG, C_REG, 10, 8, 0, 0},
+	{AADD, C_ANDCON, C_NONE, C_REG, 10, 8, 0, 0},
+
+	{AADDV, C_ADD0CON, C_REG, C_REG, 4, 4, 0, Mips64},
+	{AADDV, C_ADD0CON, C_NONE, C_REG, 4, 4, 0, Mips64},
+	{AADDV, C_ANDCON, C_REG, C_REG, 10, 8, 0, Mips64},
+	{AADDV, C_ANDCON, C_NONE, C_REG, 10, 8, 0, Mips64},
+
+	{AAND, C_AND0CON, C_REG, C_REG, 4, 4, 0, 0},
+	{AAND, C_AND0CON, C_NONE, C_REG, 4, 4, 0, 0},
+	{AAND, C_ADDCON, C_REG, C_REG, 10, 8, 0, 0},
+	{AAND, C_ADDCON, C_NONE, C_REG, 10, 8, 0, 0},
+
+	{AADD, C_UCON, C_REG, C_REG, 25, 8, 0, 0},
+	{AADD, C_UCON, C_NONE, C_REG, 25, 8, 0, 0},
+	{AADDV, C_UCON, C_REG, C_REG, 25, 8, 0, Mips64},
+	{AADDV, C_UCON, C_NONE, C_REG, 25, 8, 0, Mips64},
+	{AAND, C_UCON, C_REG, C_REG, 25, 8, 0, 0},
+	{AAND, C_UCON, C_NONE, C_REG, 25, 8, 0, 0},
+
+	{AADD, C_LCON, C_NONE, C_REG, 23, 12, 0, 0},
+	{AADDV, C_LCON, C_NONE, C_REG, 23, 12, 0, Mips64},
+	{AAND, C_LCON, C_NONE, C_REG, 23, 12, 0, 0},
+	{AADD, C_LCON, C_REG, C_REG, 23, 12, 0, 0},
+	{AADDV, C_LCON, C_REG, C_REG, 23, 12, 0, Mips64},
+	{AAND, C_LCON, C_REG, C_REG, 23, 12, 0, 0},
+
+	{ASLL, C_SCON, C_REG, C_REG, 16, 4, 0, 0},
+	{ASLL, C_SCON, C_NONE, C_REG, 16, 4, 0, 0},
+
+	{ASLLV, C_SCON, C_REG, C_REG, 16, 4, 0, Mips64},
+	{ASLLV, C_SCON, C_NONE, C_REG, 16, 4, 0, Mips64},
+
+	{ASYSCALL, C_NONE, C_NONE, C_NONE, 5, 4, 0, 0},
+
+	{ABEQ, C_REG, C_REG, C_SBRA, 6, 4, 0, 0},
+	{ABEQ, C_REG, C_NONE, C_SBRA, 6, 4, 0, 0},
+	{ABLEZ, C_REG, C_NONE, C_SBRA, 6, 4, 0, 0},
+	{ABFPT, C_NONE, C_NONE, C_SBRA, 6, 8, 0, 0},
+
+	{AJMP, C_NONE, C_NONE, C_LBRA, 11, 4, 0, 0},
+	{AJAL, C_NONE, C_NONE, C_LBRA, 11, 4, 0, 0},
+
+	{AJMP, C_NONE, C_NONE, C_ZOREG, 18, 4, REGZERO, 0},
+	{AJAL, C_NONE, C_NONE, C_ZOREG, 18, 4, REGLINK, 0},
+
+	{AMOVW, C_SEXT, C_NONE, C_FREG, 27, 4, REGSB, Mips64},
+	{AMOVF, C_SEXT, C_NONE, C_FREG, 27, 4, REGSB, Mips64},
+	{AMOVD, C_SEXT, C_NONE, C_FREG, 27, 4, REGSB, Mips64},
+	{AMOVW, C_SAUTO, C_NONE, C_FREG, 27, 4, REGSP, Mips64},
+	{AMOVF, C_SAUTO, C_NONE, C_FREG, 27, 4, REGSP, 0},
+	{AMOVD, C_SAUTO, C_NONE, C_FREG, 27, 4, REGSP, 0},
+	{AMOVW, C_SOREG, C_NONE, C_FREG, 27, 4, REGZERO, Mips64},
+	{AMOVF, C_SOREG, C_NONE, C_FREG, 27, 4, REGZERO, 0},
+	{AMOVD, C_SOREG, C_NONE, C_FREG, 27, 4, REGZERO, 0},
+
+	{AMOVW, C_LEXT, C_NONE, C_FREG, 27, 12, REGSB, Mips64},
+	{AMOVF, C_LEXT, C_NONE, C_FREG, 27, 12, REGSB, Mips64},
+	{AMOVD, C_LEXT, C_NONE, C_FREG, 27, 12, REGSB, Mips64},
+	{AMOVW, C_LAUTO, C_NONE, C_FREG, 27, 12, REGSP, Mips64},
+	{AMOVF, C_LAUTO, C_NONE, C_FREG, 27, 12, REGSP, 0},
+	{AMOVD, C_LAUTO, C_NONE, C_FREG, 27, 12, REGSP, 0},
+	{AMOVW, C_LOREG, C_NONE, C_FREG, 27, 12, REGZERO, Mips64},
+	{AMOVF, C_LOREG, C_NONE, C_FREG, 27, 12, REGZERO, 0},
+	{AMOVD, C_LOREG, C_NONE, C_FREG, 27, 12, REGZERO, 0},
+	{AMOVF, C_ADDR, C_NONE, C_FREG, 51, 8, 0, Mips32},
+	{AMOVF, C_ADDR, C_NONE, C_FREG, 51, 12, 0, Mips64},
+	{AMOVD, C_ADDR, C_NONE, C_FREG, 51, 8, 0, Mips32},
+	{AMOVD, C_ADDR, C_NONE, C_FREG, 51, 12, 0, Mips64},
+
+	{AMOVW, C_FREG, C_NONE, C_SEXT, 28, 4, REGSB, Mips64},
+	{AMOVF, C_FREG, C_NONE, C_SEXT, 28, 4, REGSB, Mips64},
+	{AMOVD, C_FREG, C_NONE, C_SEXT, 28, 4, REGSB, Mips64},
+	{AMOVW, C_FREG, C_NONE, C_SAUTO, 28, 4, REGSP, Mips64},
+	{AMOVF, C_FREG, C_NONE, C_SAUTO, 28, 4, REGSP, 0},
+	{AMOVD, C_FREG, C_NONE, C_SAUTO, 28, 4, REGSP, 0},
+	{AMOVW, C_FREG, C_NONE, C_SOREG, 28, 4, REGZERO, Mips64},
+	{AMOVF, C_FREG, C_NONE, C_SOREG, 28, 4, REGZERO, 0},
+	{AMOVD, C_FREG, C_NONE, C_SOREG, 28, 4, REGZERO, 0},
+
+	{AMOVW, C_FREG, C_NONE, C_LEXT, 28, 12, REGSB, Mips64},
+	{AMOVF, C_FREG, C_NONE, C_LEXT, 28, 12, REGSB, Mips64},
+	{AMOVD, C_FREG, C_NONE, C_LEXT, 28, 12, REGSB, Mips64},
+	{AMOVW, C_FREG, C_NONE, C_LAUTO, 28, 12, REGSP, Mips64},
+	{AMOVF, C_FREG, C_NONE, C_LAUTO, 28, 12, REGSP, 0},
+	{AMOVD, C_FREG, C_NONE, C_LAUTO, 28, 12, REGSP, 0},
+	{AMOVW, C_FREG, C_NONE, C_LOREG, 28, 12, REGZERO, Mips64},
+	{AMOVF, C_FREG, C_NONE, C_LOREG, 28, 12, REGZERO, 0},
+	{AMOVD, C_FREG, C_NONE, C_LOREG, 28, 12, REGZERO, 0},
+	{AMOVF, C_FREG, C_NONE, C_ADDR, 50, 8, 0, Mips32},
+	{AMOVF, C_FREG, C_NONE, C_ADDR, 50, 12, 0, Mips64},
+	{AMOVD, C_FREG, C_NONE, C_ADDR, 50, 8, 0, Mips32},
+	{AMOVD, C_FREG, C_NONE, C_ADDR, 50, 12, 0, Mips64},
+
+	{AMOVW, C_REG, C_NONE, C_FREG, 30, 4, 0, 0},
+	{AMOVW, C_FREG, C_NONE, C_REG, 31, 4, 0, 0},
+	{AMOVV, C_REG, C_NONE, C_FREG, 47, 4, 0, Mips64},
+	{AMOVV, C_FREG, C_NONE, C_REG, 48, 4, 0, Mips64},
+
+	{AMOVW, C_ADDCON, C_NONE, C_FREG, 34, 8, 0, Mips64},
+	{AMOVW, C_ANDCON, C_NONE, C_FREG, 34, 8, 0, Mips64},
+
+	{AMOVW, C_REG, C_NONE, C_MREG, 37, 4, 0, 0},
+	{AMOVV, C_REG, C_NONE, C_MREG, 37, 4, 0, Mips64},
+	{AMOVW, C_MREG, C_NONE, C_REG, 38, 4, 0, 0},
+	{AMOVV, C_MREG, C_NONE, C_REG, 38, 4, 0, Mips64},
+
+	{AWORD, C_LCON, C_NONE, C_NONE, 40, 4, 0, 0},
+
+	{AMOVW, C_REG, C_NONE, C_FCREG, 41, 8, 0, 0},
+	{AMOVV, C_REG, C_NONE, C_FCREG, 41, 8, 0, Mips64},
+	{AMOVW, C_FCREG, C_NONE, C_REG, 42, 4, 0, 0},
+	{AMOVV, C_FCREG, C_NONE, C_REG, 42, 4, 0, Mips64},
+
+	{ATEQ, C_SCON, C_REG, C_REG, 15, 4, 0, 0},
+	{ATEQ, C_SCON, C_NONE, C_REG, 15, 4, 0, 0},
+	{ACMOVT, C_REG, C_NONE, C_REG, 17, 4, 0, 0},
+
+	{ABREAK, C_REG, C_NONE, C_SEXT, 7, 4, REGSB, Mips64}, /* really CACHE instruction */
+	{ABREAK, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP, Mips64},
+	{ABREAK, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO, Mips64},
+	{ABREAK, C_NONE, C_NONE, C_NONE, 5, 4, 0, 0},
+
+	{obj.AUNDEF, C_NONE, C_NONE, C_NONE, 49, 4, 0, 0},
+	{obj.AUSEFIELD, C_ADDR, C_NONE, C_NONE, 0, 0, 0, 0},
+	{obj.APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0, 0},
+	{obj.AFUNCDATA, C_SCON, C_NONE, C_ADDR, 0, 0, 0, 0},
+	{obj.ANOP, C_NONE, C_NONE, C_NONE, 0, 0, 0, 0},
+	{obj.ADUFFZERO, C_NONE, C_NONE, C_LBRA, 11, 4, 0, 0}, // same as AJMP
+	{obj.ADUFFCOPY, C_NONE, C_NONE, C_LBRA, 11, 4, 0, 0}, // same as AJMP
+
+	{obj.AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0, 0},
 }
 
 var oprange [ALAST & obj.AMask][]Optab
@@ -330,7 +379,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym) {
 		return
 	}
 	ctxt.Cursym = cursym
-	ctxt.Autosize = int32(p.To.Offset + 8)
+	ctxt.Autosize = int32(p.To.Offset + ctxt.FixedFrameSize())
 
 	if oprange[AOR&obj.AMask] == nil {
 		buildop(ctxt)
@@ -370,7 +419,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym) {
 	var q *obj.Prog
 	for bflag != 0 {
 		if ctxt.Debugvlog != 0 {
-			fmt.Fprintf(ctxt.Bso, "%5.2f span1\n", obj.Cputime())
+			ctxt.Logf("%5.2f span1\n", obj.Cputime())
 		}
 		bflag = 0
 		c = 0
@@ -417,8 +466,9 @@ func span0(ctxt *obj.Link, cursym *obj.LSym) {
 
 		cursym.Size = c
 	}
-
-	c += -c & (FuncAlign - 1)
+	if ctxt.Mode&Mips64 != 0 {
+		c += -c & (mips64FuncAlign - 1)
+	}
 	cursym.Size = c
 
 	/*
@@ -503,7 +553,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 			return C_LAUTO
 
 		case obj.NAME_PARAM:
-			ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + 8
+			ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + ctxt.FixedFrameSize()
 			if ctxt.Instoffset >= -BIG && ctxt.Instoffset < BIG {
 				return C_SAUTO
 			}
@@ -567,7 +617,7 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 			return C_LACON
 
 		case obj.NAME_PARAM:
-			ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + 8
+			ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + ctxt.FixedFrameSize()
 			if ctxt.Instoffset >= -BIG && ctxt.Instoffset < BIG {
 				return C_SACON
 			}
@@ -653,13 +703,13 @@ func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
 	c3 := &xcmp[a3]
 	for i := range ops {
 		op := &ops[i]
-		if int(op.a2) == a2 && c1[op.a1] && c3[op.a3] {
+		if int(op.a2) == a2 && c1[op.a1] && c3[op.a3] && (ctxt.Mode&op.mode == op.mode) {
 			p.Optab = uint16(cap(optab) - cap(ops) + i + 1)
 			return op
 		}
 	}
 
-	ctxt.Diag("illegal combination %v %v %v %v", obj.Aconv(p.As), DRconv(a1), DRconv(a2), DRconv(a3))
+	ctxt.Diag("illegal combination %v %v %v %v", p.As, DRconv(a1), DRconv(a2), DRconv(a3))
 	prasm(p)
 	if ops == nil {
 		ops = optab
@@ -809,7 +859,7 @@ func buildop(ctxt *obj.Link) {
 
 		switch r {
 		default:
-			ctxt.Diag("unknown op in build: %v", obj.Aconv(r))
+			ctxt.Diag("unknown op in build: %v", r)
 			log.Fatalf("bad code")
 
 		case AABSF:
@@ -824,18 +874,22 @@ func buildop(ctxt *obj.Link) {
 			opset(AABSD, r0)
 			opset(ATRUNCDW, r0)
 			opset(ATRUNCFW, r0)
-			opset(ATRUNCDV, r0)
-			opset(ATRUNCFV, r0)
-			opset(AMOVVF, r0)
-			opset(AMOVFV, r0)
+			opset(ASQRTF, r0)
+			opset(ASQRTD, r0)
+
+		case AMOVVF:
 			opset(AMOVVD, r0)
+			opset(AMOVFV, r0)
 			opset(AMOVDV, r0)
+			opset(ATRUNCDV, r0)
+			opset(ATRUNCFV, r0)
 
 		case AADD:
 			opset(ASGT, r0)
 			opset(ASGTU, r0)
 			opset(AADDU, r0)
-			opset(AADDV, r0)
+
+		case AADDV:
 			opset(AADDVU, r0)
 
 		case AADDF:
@@ -873,9 +927,10 @@ func buildop(ctxt *obj.Link) {
 			opset(ADIVU, r0)
 			opset(AMULU, r0)
 			opset(ADIV, r0)
+
+		case AMULV:
 			opset(ADIVV, r0)
 			opset(ADIVVU, r0)
-			opset(AMULV, r0)
 			opset(AMULVU, r0)
 			opset(AREMV, r0)
 			opset(AREMVU, r0)
@@ -883,17 +938,20 @@ func buildop(ctxt *obj.Link) {
 		case ASLL:
 			opset(ASRL, r0)
 			opset(ASRA, r0)
-			opset(ASLLV, r0)
+
+		case ASLLV:
 			opset(ASRAV, r0)
 			opset(ASRLV, r0)
 
 		case ASUB:
 			opset(ASUBU, r0)
-			opset(ASUBV, r0)
-			opset(ASUBVU, r0)
 			opset(ANOR, r0)
 
+		case ASUBV:
+			opset(ASUBVU, r0)
+
 		case ASYSCALL:
+			opset(ASYNC, r0)
 			opset(ATLBP, r0)
 			opset(ATLBR, r0)
 			opset(ATLBWI, r0)
@@ -911,8 +969,9 @@ func buildop(ctxt *obj.Link) {
 
 		case AMOVWL:
 			opset(AMOVWR, r0)
+
+		case AMOVVL:
 			opset(AMOVVR, r0)
-			opset(AMOVVL, r0)
 
 		case AMOVW,
 			AMOVD,
@@ -923,6 +982,8 @@ func buildop(ctxt *obj.Link) {
 			AJAL,
 			AJMP,
 			AMOVWU,
+			ALL,
+			ASC,
 			AWORD,
 			obj.ANOP,
 			obj.ATEXT,
@@ -933,6 +994,18 @@ func buildop(ctxt *obj.Link) {
 			obj.ADUFFZERO,
 			obj.ADUFFCOPY:
 			break
+
+		case ACMOVN:
+			opset(ACMOVZ, r0)
+
+		case ACMOVT:
+			opset(ACMOVF, r0)
+
+		case ACLO:
+			opset(ACLZ, r0)
+
+		case ATEQ:
+			opset(ATNE, r0)
 		}
 	}
 }
@@ -995,6 +1068,11 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 	o3 := uint32(0)
 	o4 := uint32(0)
 
+	add := AADDU
+
+	if ctxt.Mode&Mips64 != 0 {
+		add = AADDVU
+	}
 	switch o.type_ {
 	default:
 		ctxt.Diag("unknown type %d %v", o.type_)
@@ -1005,10 +1083,10 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 
 	case 1: /* mov r1,r2 ==> OR r1,r0,r2 */
 		a := AOR
-		if p.As == AMOVW {
+		if p.As == AMOVW && ctxt.Mode&Mips64 != 0 {
 			a = AADDU // sign-extended to high 32 bits
 		}
-		o1 = OP_RRR(oprrr(ctxt, a), uint32(p.From.Reg), uint32(REGZERO), uint32(p.To.Reg))
+		o1 = OP_RRR(oprrr(ctxt, a), uint32(REGZERO), uint32(p.From.Reg), uint32(p.To.Reg))
 
 	case 2: /* add/sub r1,[r2],r3 */
 		r := int(p.Reg)
@@ -1025,7 +1103,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		if r == 0 {
 			r = int(o.param)
 		}
-		a := AADDVU
+		a := add
 		if o.a1 == C_ANDCON {
 			a = AOR
 		}
@@ -1152,6 +1230,15 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		o1 = OP_SRR(opirr(ctxt, -ASLLV), uint32(0), uint32(p.From.Reg), uint32(p.To.Reg))
 		o2 = OP_SRR(opirr(ctxt, -ASRLV), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg))
 
+	case 15: /* teq $c r,r */
+		v := regoff(ctxt, &p.From)
+		r := int(p.Reg)
+		if r == 0 {
+			r = REGZERO
+		}
+		/* only use 10 bits of trap code */
+		o1 = OP_IRR(opirr(ctxt, p.As), (uint32(v)&0x3FF)<<6, uint32(p.Reg), uint32(p.To.Reg))
+
 	case 16: /* sll $c,[r1],r2 */
 		v := regoff(ctxt, &p.From)
 		r := int(p.Reg)
@@ -1166,6 +1253,9 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 			o1 = OP_SRR(opirr(ctxt, p.As), uint32(v), uint32(r), uint32(p.To.Reg))
 		}
 
+	case 17:
+		o1 = OP_RRR(oprrr(ctxt, p.As), uint32(REGZERO), uint32(p.From.Reg), uint32(p.To.Reg))
+
 	case 18: /* jmp [r1],0(r2) */
 		r := int(p.Reg)
 		if r == 0 {
@@ -1196,8 +1286,17 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		}
 		o1 = OP_RRR(a, uint32(REGZERO), uint32(p.From.Reg), uint32(REGZERO))
 
-	case 22: /* mul r1,r2 */
-		o1 = OP_RRR(oprrr(ctxt, p.As), uint32(p.From.Reg), uint32(p.Reg), uint32(REGZERO))
+	case 22: /* mul r1,r2 [r3]*/
+		if p.To.Reg != 0 {
+			r := int(p.Reg)
+			if r == 0 {
+				r = int(p.To.Reg)
+			}
+			a := SP(3, 4) | 2 /* mul */
+			o1 = OP_RRR(a, uint32(p.From.Reg), uint32(r), uint32(p.To.Reg))
+		} else {
+			o1 = OP_RRR(oprrr(ctxt, p.As), uint32(p.From.Reg), uint32(p.Reg), uint32(REGZERO))
+		}
 
 	case 23: /* add $lcon,r1,r2 ==> lu+or+add */
 		v := regoff(ctxt, &p.From)
@@ -1230,7 +1329,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		if r == 0 {
 			r = int(o.param)
 		}
-		o3 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGTMP), uint32(r), uint32(p.To.Reg))
+		o3 = OP_RRR(oprrr(ctxt, add), uint32(REGTMP), uint32(r), uint32(p.To.Reg))
 
 	case 27: /* mov [sl]ext/auto/oreg,fr ==> lwc1 o(r) */
 		v := regoff(ctxt, &p.From)
@@ -1245,7 +1344,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		switch o.size {
 		case 12:
 			o1 = OP_IRR(opirr(ctxt, ALUI), uint32((v+1<<15)>>16), uint32(REGZERO), uint32(REGTMP))
-			o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(r), uint32(REGTMP), uint32(REGTMP))
+			o2 = OP_RRR(oprrr(ctxt, add), uint32(r), uint32(REGTMP), uint32(REGTMP))
 			o3 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(REGTMP), uint32(p.To.Reg))
 
 		case 4:
@@ -1265,7 +1364,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		switch o.size {
 		case 12:
 			o1 = OP_IRR(opirr(ctxt, ALUI), uint32((v+1<<15)>>16), uint32(REGZERO), uint32(REGTMP))
-			o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(r), uint32(REGTMP), uint32(REGTMP))
+			o2 = OP_RRR(oprrr(ctxt, add), uint32(r), uint32(REGTMP), uint32(REGTMP))
 			o3 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(REGTMP), uint32(p.From.Reg))
 
 		case 4:
@@ -1306,7 +1405,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 			r = int(o.param)
 		}
 		o1 = OP_IRR(opirr(ctxt, ALUI), uint32((v+1<<15)>>16), uint32(REGZERO), uint32(REGTMP))
-		o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(r), uint32(REGTMP), uint32(REGTMP))
+		o2 = OP_RRR(oprrr(ctxt, add), uint32(r), uint32(REGTMP), uint32(REGTMP))
 		o3 = OP_IRR(opirr(ctxt, p.As), uint32(v), uint32(REGTMP), uint32(p.From.Reg))
 
 	case 36: /* mov lext/auto/oreg,r ==> lw o(REGTMP) */
@@ -1316,7 +1415,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 			r = int(o.param)
 		}
 		o1 = OP_IRR(opirr(ctxt, ALUI), uint32((v+1<<15)>>16), uint32(REGZERO), uint32(REGTMP))
-		o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(r), uint32(REGTMP), uint32(REGTMP))
+		o2 = OP_RRR(oprrr(ctxt, add), uint32(r), uint32(REGTMP), uint32(REGTMP))
 		o3 = OP_IRR(opirr(ctxt, -p.As), uint32(v), uint32(REGTMP), uint32(p.To.Reg))
 
 	case 37: /* movw r,mr */
@@ -1363,15 +1462,20 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		rel.Sym = p.To.Sym
 		rel.Add = p.To.Offset
 		rel.Type = obj.R_ADDRMIPSU
-		o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGSB), uint32(REGTMP), uint32(REGTMP))
-		o3 = OP_IRR(opirr(ctxt, p.As), uint32(0), uint32(REGTMP), uint32(p.From.Reg))
+		o2 = OP_IRR(opirr(ctxt, p.As), uint32(0), uint32(REGTMP), uint32(p.From.Reg))
 		rel2 := obj.Addrel(ctxt.Cursym)
-		rel2.Off = int32(ctxt.Pc + 8)
+		rel2.Off = int32(ctxt.Pc + 4)
 		rel2.Siz = 4
 		rel2.Sym = p.To.Sym
 		rel2.Add = p.To.Offset
 		rel2.Type = obj.R_ADDRMIPS
 
+		if o.size == 12 {
+			o3 = o2
+			o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGSB), uint32(REGTMP), uint32(REGTMP))
+			rel2.Off += 4
+		}
+
 	case 51: /* mov addr,r ==> lu + add REGSB, REGTMP + lw o(REGTMP) */
 		o1 = OP_IRR(opirr(ctxt, ALUI), uint32(0), uint32(REGZERO), uint32(REGTMP))
 		rel := obj.Addrel(ctxt.Cursym)
@@ -1380,15 +1484,20 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		rel.Sym = p.From.Sym
 		rel.Add = p.From.Offset
 		rel.Type = obj.R_ADDRMIPSU
-		o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGSB), uint32(REGTMP), uint32(REGTMP))
-		o3 = OP_IRR(opirr(ctxt, -p.As), uint32(0), uint32(REGTMP), uint32(p.To.Reg))
+		o2 = OP_IRR(opirr(ctxt, -p.As), uint32(0), uint32(REGTMP), uint32(p.To.Reg))
 		rel2 := obj.Addrel(ctxt.Cursym)
-		rel2.Off = int32(ctxt.Pc + 8)
+		rel2.Off = int32(ctxt.Pc + 4)
 		rel2.Siz = 4
 		rel2.Sym = p.From.Sym
 		rel2.Add = p.From.Offset
 		rel2.Type = obj.R_ADDRMIPS
 
+		if o.size == 12 {
+			o3 = o2
+			o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGSB), uint32(REGTMP), uint32(REGTMP))
+			rel2.Off += 4
+		}
+
 	case 52: /* mov $lext, r ==> lu + add REGSB, r + add */
 		o1 = OP_IRR(opirr(ctxt, ALUI), uint32(0), uint32(REGZERO), uint32(p.To.Reg))
 		rel := obj.Addrel(ctxt.Cursym)
@@ -1397,15 +1506,20 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		rel.Sym = p.From.Sym
 		rel.Add = p.From.Offset
 		rel.Type = obj.R_ADDRMIPSU
-		o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGSB), uint32(p.To.Reg), uint32(p.To.Reg))
-		o3 = OP_IRR(opirr(ctxt, AADDVU), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg))
+		o2 = OP_IRR(opirr(ctxt, add), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg))
 		rel2 := obj.Addrel(ctxt.Cursym)
-		rel2.Off = int32(ctxt.Pc + 8)
+		rel2.Off = int32(ctxt.Pc + 4)
 		rel2.Siz = 4
 		rel2.Sym = p.From.Sym
 		rel2.Add = p.From.Offset
 		rel2.Type = obj.R_ADDRMIPS
 
+		if o.size == 12 {
+			o3 = o2
+			o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGSB), uint32(p.To.Reg), uint32(p.To.Reg))
+			rel2.Off += 4
+		}
+
 	case 53: /* mov r, tlsvar ==> rdhwr + sw o(r3) */
 		// clobbers R3 !
 		// load thread pointer with RDHWR, R3 is used for fast kernel emulation on Linux
@@ -1432,7 +1546,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 	case 55: /* mov $tlsvar, r ==> rdhwr + add */
 		// clobbers R3 !
 		o1 = (037<<26 + 073) | (29 << 11) | (3 << 16) // rdhwr $29, r3
-		o2 = OP_IRR(opirr(ctxt, AADDVU), uint32(0), uint32(REG_R3), uint32(p.To.Reg))
+		o2 = OP_IRR(opirr(ctxt, add), uint32(0), uint32(REG_R3), uint32(p.To.Reg))
 		rel := obj.Addrel(ctxt.Cursym)
 		rel.Off = int32(ctxt.Pc + 4)
 		rel.Siz = 4
@@ -1609,12 +1723,33 @@ func oprrr(ctxt *obj.Link, a obj.As) uint32 {
 		return FPF(7, 6)
 	case ACMPGED:
 		return FPD(7, 6)
+
+	case ASQRTF:
+		return FPF(0, 4)
+	case ASQRTD:
+		return FPD(0, 4)
+
+	case ASYNC:
+		return OP(1, 7)
+
+	case ACMOVN:
+		return OP(1, 3)
+	case ACMOVZ:
+		return OP(1, 2)
+	case ACMOVT:
+		return OP(0, 1) | (1 << 16)
+	case ACMOVF:
+		return OP(0, 1) | (0 << 16)
+	case ACLO:
+		return SP(3, 4) | OP(4, 1)
+	case ACLZ:
+		return SP(3, 4) | OP(4, 0)
 	}
 
 	if a < 0 {
-		ctxt.Diag("bad rrr opcode -%v", obj.Aconv(-a))
+		ctxt.Diag("bad rrr opcode -%v", -a)
 	} else {
-		ctxt.Diag("bad rrr opcode %v", obj.Aconv(a))
+		ctxt.Diag("bad rrr opcode %v", a)
 	}
 	return 0
 }
@@ -1761,12 +1896,21 @@ func opirr(ctxt *obj.Link, a obj.As) uint32 {
 		return OP(7, 6)
 	case -ASRAV:
 		return OP(7, 7)
+
+	case ATEQ:
+		return OP(6, 4)
+	case ATNE:
+		return OP(6, 6)
+	case -ALL:
+		return SP(6, 0)
+	case ASC:
+		return SP(7, 0)
 	}
 
 	if a < 0 {
-		ctxt.Diag("bad irr opcode -%v", obj.Aconv(-a))
+		ctxt.Diag("bad irr opcode -%v", -a)
 	} else {
-		ctxt.Diag("bad irr opcode %v", obj.Aconv(a))
+		ctxt.Diag("bad irr opcode %v", a)
 	}
 	return 0
 }
diff --git a/src/cmd/internal/obj/mips/list0.go b/src/cmd/internal/obj/mips/list0.go
index 28881e8..4dd5e80 100644
--- a/src/cmd/internal/obj/mips/list0.go
+++ b/src/cmd/internal/obj/mips/list0.go
@@ -35,8 +35,8 @@ import (
 )
 
 func init() {
-	obj.RegisterRegister(obj.RBaseMIPS64, REG_LAST&^1023+1024, Rconv)
-	obj.RegisterOpcode(obj.ABaseMIPS64, Anames)
+	obj.RegisterRegister(obj.RBaseMIPS, REG_LAST+1, Rconv)
+	obj.RegisterOpcode(obj.ABaseMIPS, Anames)
 }
 
 func Rconv(r int) string {
@@ -70,7 +70,7 @@ func Rconv(r int) string {
 		return "LO"
 	}
 
-	return fmt.Sprintf("Rgok(%d)", r-obj.RBaseMIPS64)
+	return fmt.Sprintf("Rgok(%d)", r-obj.RBaseMIPS)
 }
 
 func DRconv(a int) string {
diff --git a/src/cmd/internal/obj/mips/obj0.go b/src/cmd/internal/obj/mips/obj0.go
index ade1ee5..221fd42 100644
--- a/src/cmd/internal/obj/mips/obj0.go
+++ b/src/cmd/internal/obj/mips/obj0.go
@@ -37,6 +37,18 @@ import (
 )
 
 func progedit(ctxt *obj.Link, p *obj.Prog) {
+	// Maintain information about code generation mode.
+	if ctxt.Mode == 0 {
+		switch ctxt.Arch.Family {
+		default:
+			ctxt.Diag("unsupported arch family")
+		case sys.MIPS:
+			ctxt.Mode = Mips32
+		case sys.MIPS64:
+			ctxt.Mode = Mips64
+		}
+	}
+
 	p.From.Class = 0
 	p.To.Class = 0
 
@@ -58,6 +70,12 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 		if p.From.Type == obj.TYPE_FCONST {
 			f32 := float32(p.From.Val.(float64))
 			i32 := math.Float32bits(f32)
+			if i32 == 0 {
+				p.As = AMOVW
+				p.From.Type = obj.TYPE_REG
+				p.From.Reg = REGZERO
+				break
+			}
 			literal := fmt.Sprintf("$f32.%08x", i32)
 			s := obj.Linklookup(ctxt, literal, 0)
 			s.Size = 4
@@ -70,6 +88,12 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 	case AMOVD:
 		if p.From.Type == obj.TYPE_FCONST {
 			i64 := math.Float64bits(p.From.Val.(float64))
+			if i64 == 0 && ctxt.Mode&Mips64 != 0 {
+				p.As = AMOVV
+				p.From.Type = obj.TYPE_REG
+				p.From.Reg = REGZERO
+				break
+			}
 			literal := fmt.Sprintf("$f64.%016x", i64)
 			s := obj.Linklookup(ctxt, literal, 0)
 			s.Size = 8
@@ -144,9 +168,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 	 * expand BECOME pseudo
 	 */
 	if ctxt.Debugvlog != 0 {
-		fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime())
+		ctxt.Logf("%5.2f noops\n", obj.Cputime())
 	}
-	ctxt.Bso.Flush()
 
 	var q *obj.Prog
 	var q1 *obj.Prog
@@ -260,6 +283,15 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 		}
 	}
 
+	var mov, add obj.As
+	if ctxt.Mode&Mips64 != 0 {
+		add = AADDV
+		mov = AMOVV
+	} else {
+		add = AADDU
+		mov = AMOVW
+	}
+
 	autosize := int32(0)
 	var p1 *obj.Prog
 	var p2 *obj.Prog
@@ -267,13 +299,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 		o := p.As
 		switch o {
 		case obj.ATEXT:
-			autosize = int32(textstksiz + 8)
-			if (p.Mark&LEAF != 0) && autosize <= 8 {
+			autosize = int32(textstksiz + ctxt.FixedFrameSize())
+			if (p.Mark&LEAF != 0) && autosize <= int32(ctxt.FixedFrameSize()) {
 				autosize = 0
-			} else if autosize&4 != 0 {
+			} else if autosize&4 != 0 && ctxt.Mode&Mips64 != 0 {
 				autosize += 4
 			}
-			p.To.Offset = int64(autosize) - 8
+
+			p.To.Offset = int64(autosize) - ctxt.FixedFrameSize()
 
 			if p.From3.Offset&obj.NOSPLIT == 0 {
 				p = stacksplit(ctxt, p, autosize) // emit split check
@@ -282,8 +315,22 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			q = p
 
 			if autosize != 0 {
-				q = obj.Appendp(ctxt, p)
-				q.As = AADDV
+				// Make sure to save link register for non-empty frame, even if
+				// it is a leaf function, so that traceback works.
+				// Store link register before decrement SP, so if a signal comes
+				// during the execution of the function prologue, the traceback
+				// code will not see a half-updated stack frame.
+				q = obj.Appendp(ctxt, q)
+				q.As = mov
+				q.Lineno = p.Lineno
+				q.From.Type = obj.TYPE_REG
+				q.From.Reg = REGLINK
+				q.To.Type = obj.TYPE_MEM
+				q.To.Offset = int64(-autosize)
+				q.To.Reg = REGSP
+
+				q = obj.Appendp(ctxt, q)
+				q.As = add
 				q.Lineno = p.Lineno
 				q.From.Type = obj.TYPE_CONST
 				q.From.Offset = int64(-autosize)
@@ -293,8 +340,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			} else if cursym.Text.Mark&LEAF == 0 {
 				if cursym.Text.From3.Offset&obj.NOSPLIT != 0 {
 					if ctxt.Debugvlog != 0 {
-						fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name)
-						ctxt.Bso.Flush()
+						ctxt.Logf("save suppressed in: %s\n", cursym.Name)
 					}
 
 					cursym.Text.Mark |= LEAF
@@ -302,29 +348,20 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			}
 
 			if cursym.Text.Mark&LEAF != 0 {
-				cursym.Leaf = true
+				cursym.Set(obj.AttrLeaf, true)
 				break
 			}
 
-			q = obj.Appendp(ctxt, q)
-			q.As = AMOVV
-			q.Lineno = p.Lineno
-			q.From.Type = obj.TYPE_REG
-			q.From.Reg = REGLINK
-			q.To.Type = obj.TYPE_MEM
-			q.To.Offset = int64(0)
-			q.To.Reg = REGSP
-
 			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
 				//
-				//	MOVV	g_panic(g), R1
-				//	BEQ		R1, end
-				//	MOVV	panic_argp(R1), R2
-				//	ADDV	$(autosize+8), R29, R3
-				//	BNE		R2, R3, end
-				//	ADDV	$8, R29, R2
-				//	MOVV	R2, panic_argp(R1)
+				//	MOV	g_panic(g), R1
+				//	BEQ	R1, end
+				//	MOV	panic_argp(R1), R2
+				//	ADD	$(autosize+FIXED_FRAME), R29, R3
+				//	BNE	R2, R3, end
+				//	ADD	$FIXED_FRAME, R29, R2
+				//	MOV	R2, panic_argp(R1)
 				// end:
 				//	NOP
 				//
@@ -333,7 +370,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 
 				q = obj.Appendp(ctxt, q)
 
-				q.As = AMOVV
+				q.As = mov
 				q.From.Type = obj.TYPE_MEM
 				q.From.Reg = REGG
 				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
@@ -349,7 +386,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				p1 = q
 
 				q = obj.Appendp(ctxt, q)
-				q.As = AMOVV
+				q.As = mov
 				q.From.Type = obj.TYPE_MEM
 				q.From.Reg = REG_R1
 				q.From.Offset = 0 // Panic.argp
@@ -357,9 +394,9 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				q.To.Reg = REG_R2
 
 				q = obj.Appendp(ctxt, q)
-				q.As = AADDV
+				q.As = add
 				q.From.Type = obj.TYPE_CONST
-				q.From.Offset = int64(autosize) + 8
+				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
 				q.Reg = REGSP
 				q.To.Type = obj.TYPE_REG
 				q.To.Reg = REG_R3
@@ -374,15 +411,15 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				p2 = q
 
 				q = obj.Appendp(ctxt, q)
-				q.As = AADDV
+				q.As = add
 				q.From.Type = obj.TYPE_CONST
-				q.From.Offset = 8
+				q.From.Offset = ctxt.FixedFrameSize()
 				q.Reg = REGSP
 				q.To.Type = obj.TYPE_REG
 				q.To.Reg = REG_R2
 
 				q = obj.Appendp(ctxt, q)
-				q.As = AMOVV
+				q.As = mov
 				q.From.Type = obj.TYPE_REG
 				q.From.Reg = REG_R2
 				q.To.Type = obj.TYPE_MEM
@@ -402,24 +439,28 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				break
 			}
 
-			if p.To.Sym != nil { // retjmp
-				p.As = AJMP
-				p.To.Type = obj.TYPE_BRANCH
-				break
-			}
+			retSym := p.To.Sym
+			p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
+			p.To.Sym = nil
 
 			if cursym.Text.Mark&LEAF != 0 {
 				if autosize == 0 {
 					p.As = AJMP
 					p.From = obj.Addr{}
-					p.To.Type = obj.TYPE_MEM
-					p.To.Offset = 0
-					p.To.Reg = REGLINK
+					if retSym != nil { // retjmp
+						p.To.Type = obj.TYPE_BRANCH
+						p.To.Name = obj.NAME_EXTERN
+						p.To.Sym = retSym
+					} else {
+						p.To.Type = obj.TYPE_MEM
+						p.To.Reg = REGLINK
+						p.To.Offset = 0
+					}
 					p.Mark |= BRANCH
 					break
 				}
 
-				p.As = AADDV
+				p.As = add
 				p.From.Type = obj.TYPE_CONST
 				p.From.Offset = int64(autosize)
 				p.To.Type = obj.TYPE_REG
@@ -440,33 +481,19 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				break
 			}
 
-			p.As = AMOVV
+			p.As = mov
 			p.From.Type = obj.TYPE_MEM
 			p.From.Offset = 0
 			p.From.Reg = REGSP
 			p.To.Type = obj.TYPE_REG
 			p.To.Reg = REG_R4
-
-			if false {
-				// Debug bad returns
-				q = ctxt.NewProg()
-
-				q.As = AMOVV
-				q.Lineno = p.Lineno
-				q.From.Type = obj.TYPE_MEM
-				q.From.Offset = 0
-				q.From.Reg = REG_R4
-				q.To.Type = obj.TYPE_REG
-				q.To.Reg = REGTMP
-
-				q.Link = p.Link
-				p.Link = q
-				p = q
+			if retSym != nil { // retjmp from non-leaf, need to restore LINK register
+				p.To.Reg = REGLINK
 			}
 
 			if autosize != 0 {
 				q = ctxt.NewProg()
-				q.As = AADDV
+				q.As = add
 				q.Lineno = p.Lineno
 				q.From.Type = obj.TYPE_CONST
 				q.From.Offset = int64(autosize)
@@ -481,16 +508,24 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			q1 = ctxt.NewProg()
 			q1.As = AJMP
 			q1.Lineno = p.Lineno
-			q1.To.Type = obj.TYPE_MEM
-			q1.To.Offset = 0
-			q1.To.Reg = REG_R4
+			if retSym != nil { // retjmp
+				q1.To.Type = obj.TYPE_BRANCH
+				q1.To.Name = obj.NAME_EXTERN
+				q1.To.Sym = retSym
+			} else {
+				q1.To.Type = obj.TYPE_MEM
+				q1.To.Offset = 0
+				q1.To.Reg = REG_R4
+			}
 			q1.Mark |= BRANCH
 			q1.Spadj = +autosize
 
 			q1.Link = q.Link
 			q.Link = q1
 
-		case AADDV,
+		case AADD,
+			AADDU,
+			AADDV,
 			AADDVU:
 			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
 				p.Spadj = int32(-p.From.Offset)
@@ -553,14 +588,31 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 }
 
 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
-	// MOVV	g_stackguard(g), R1
+	// Leaf function with no frame is effectively NOSPLIT.
+	if framesize == 0 {
+		return p
+	}
+
+	var mov, add, sub obj.As
+
+	if ctxt.Mode&Mips64 != 0 {
+		add = AADDV
+		mov = AMOVV
+		sub = ASUBVU
+	} else {
+		add = AADDU
+		mov = AMOVW
+		sub = ASUBU
+	}
+
+	// MOV	g_stackguard(g), R1
 	p = obj.Appendp(ctxt, p)
 
-	p.As = AMOVV
+	p.As = mov
 	p.From.Type = obj.TYPE_MEM
 	p.From.Reg = REGG
 	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
-	if ctxt.Cursym.Cfunc {
+	if ctxt.Cursym.CFunc() {
 		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
 	}
 	p.To.Type = obj.TYPE_REG
@@ -580,11 +632,11 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 		p.To.Reg = REG_R1
 	} else if framesize <= obj.StackBig {
 		// large stack: SP-framesize < stackguard-StackSmall
-		//	ADDV	$-framesize, SP, R2
+		//	ADD	$-framesize, SP, R2
 		//	SGTU	R2, stackguard, R1
 		p = obj.Appendp(ctxt, p)
 
-		p.As = AADDV
+		p.As = add
 		p.From.Type = obj.TYPE_CONST
 		p.From.Offset = int64(-framesize)
 		p.Reg = REGSP
@@ -608,15 +660,15 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 		// Preemption sets stackguard to StackPreempt, a very large value.
 		// That breaks the math above, so we have to check for that explicitly.
 		//	// stackguard is R1
-		//	MOVV	$StackPreempt, R2
+		//	MOV	$StackPreempt, R2
 		//	BEQ	R1, R2, label-of-call-to-morestack
-		//	ADDV	$StackGuard, SP, R2
-		//	SUBVU	R1, R2
-		//	MOVV	$(framesize+(StackGuard-StackSmall)), R1
+		//	ADD	$StackGuard, SP, R2
+		//	SUB	R1, R2
+		//	MOV	$(framesize+(StackGuard-StackSmall)), R1
 		//	SGTU	R2, R1, R1
 		p = obj.Appendp(ctxt, p)
 
-		p.As = AMOVV
+		p.As = mov
 		p.From.Type = obj.TYPE_CONST
 		p.From.Offset = obj.StackPreempt
 		p.To.Type = obj.TYPE_REG
@@ -632,7 +684,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 		p.Mark |= BRANCH
 
 		p = obj.Appendp(ctxt, p)
-		p.As = AADDV
+		p.As = add
 		p.From.Type = obj.TYPE_CONST
 		p.From.Offset = obj.StackGuard
 		p.Reg = REGSP
@@ -640,14 +692,14 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 		p.To.Reg = REG_R2
 
 		p = obj.Appendp(ctxt, p)
-		p.As = ASUBVU
+		p.As = sub
 		p.From.Type = obj.TYPE_REG
 		p.From.Reg = REG_R1
 		p.To.Type = obj.TYPE_REG
 		p.To.Reg = REG_R2
 
 		p = obj.Appendp(ctxt, p)
-		p.As = AMOVV
+		p.As = mov
 		p.From.Type = obj.TYPE_CONST
 		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
 		p.To.Type = obj.TYPE_REG
@@ -672,10 +724,10 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 	p.To.Type = obj.TYPE_BRANCH
 	p.Mark |= BRANCH
 
-	// MOVV	LINK, R3
+	// MOV	LINK, R3
 	p = obj.Appendp(ctxt, p)
 
-	p.As = AMOVV
+	p.As = mov
 	p.From.Type = obj.TYPE_REG
 	p.From.Reg = REGLINK
 	p.To.Type = obj.TYPE_REG
@@ -690,7 +742,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 
 	p.As = AJAL
 	p.To.Type = obj.TYPE_BRANCH
-	if ctxt.Cursym.Cfunc {
+	if ctxt.Cursym.CFunc() {
 		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
 	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
 		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
@@ -1483,3 +1535,19 @@ var Linkmips64le = obj.LinkArch{
 	Follow:     follow,
 	Progedit:   progedit,
 }
+
+var Linkmips = obj.LinkArch{
+	Arch:       sys.ArchMIPS,
+	Preprocess: preprocess,
+	Assemble:   span0,
+	Follow:     follow,
+	Progedit:   progedit,
+}
+
+var Linkmipsle = obj.LinkArch{
+	Arch:       sys.ArchMIPSLE,
+	Preprocess: preprocess,
+	Assemble:   span0,
+	Follow:     follow,
+	Progedit:   progedit,
+}
diff --git a/src/cmd/internal/obj/obj.go b/src/cmd/internal/obj/obj.go
index 3eb37b3..566263d 100644
--- a/src/cmd/internal/obj/obj.go
+++ b/src/cmd/internal/obj/obj.go
@@ -31,7 +31,6 @@ type LineHist struct {
 	TrimPathPrefix    string      // remove leading TrimPath from recorded file names
 	PrintFilenameOnly bool        // ignore path when pretty-printing a line; internal use only
 	GOROOT            string      // current GOROOT
-	GOROOT_FINAL      string      // target GOROOT
 }
 
 // A LineStack is an entry in the recorded line history.
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index a1fdee6..eb56c6f 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -52,7 +52,9 @@
 //	- type [int]
 //	- name & version [symref index]
 //	- flags [int]
-//		1 dupok
+//		1<<0 dupok
+//		1<<1 local
+//		1<<2 add to typelink table
 //	- size [int]
 //	- gotype [symref index]
 //	- p [data block]
@@ -109,6 +111,7 @@ package obj
 
 import (
 	"bufio"
+	"cmd/internal/dwarf"
 	"cmd/internal/sys"
 	"fmt"
 	"log"
@@ -317,19 +320,19 @@ func (w *objWriter) writeSymDebug(s *LSym) {
 	if s.Type != 0 {
 		fmt.Fprintf(ctxt.Bso, "t=%d ", s.Type)
 	}
-	if s.Dupok {
+	if s.DuplicateOK() {
 		fmt.Fprintf(ctxt.Bso, "dupok ")
 	}
-	if s.Cfunc {
+	if s.CFunc() {
 		fmt.Fprintf(ctxt.Bso, "cfunc ")
 	}
-	if s.Nosplit {
+	if s.NoSplit() {
 		fmt.Fprintf(ctxt.Bso, "nosplit ")
 	}
 	fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
 	if s.Type == STEXT {
 		fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Args), uint64(s.Locals))
-		if s.Leaf {
+		if s.Leaf() {
 			fmt.Fprintf(ctxt.Bso, " leaf")
 		}
 	}
@@ -388,12 +391,15 @@ func (w *objWriter) writeSym(s *LSym) {
 	w.writeInt(int64(s.Type))
 	w.writeRefIndex(s)
 	flags := int64(0)
-	if s.Dupok {
+	if s.DuplicateOK() {
 		flags |= 1
 	}
-	if s.Local {
+	if s.Local() {
 		flags |= 1 << 1
 	}
+	if s.MakeTypelink() {
+		flags |= 1 << 2
+	}
 	w.writeInt(flags)
 	w.writeInt(s.Size)
 	w.writeRefIndex(s.Gotype)
@@ -416,19 +422,19 @@ func (w *objWriter) writeSym(s *LSym) {
 
 	w.writeInt(int64(s.Args))
 	w.writeInt(int64(s.Locals))
-	if s.Nosplit {
+	if s.NoSplit() {
 		w.writeInt(1)
 	} else {
 		w.writeInt(0)
 	}
 	flags = int64(0)
-	if s.Leaf {
+	if s.Leaf() {
 		flags |= 1
 	}
-	if s.Cfunc {
+	if s.CFunc() {
 		flags |= 1 << 1
 	}
-	if s.ReflectMethod {
+	if s.ReflectMethod() {
 		flags |= 1 << 2
 	}
 	w.writeInt(flags)
@@ -506,3 +512,94 @@ type relocByOff []Reloc
 func (x relocByOff) Len() int           { return len(x) }
 func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off }
 func (x relocByOff) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
+
+// implement dwarf.Context
+type dwCtxt struct{ *Link }
+
+func (c dwCtxt) PtrSize() int {
+	return c.Arch.PtrSize
+}
+func (c dwCtxt) AddInt(s dwarf.Sym, size int, i int64) {
+	ls := s.(*LSym)
+	ls.WriteInt(c.Link, ls.Size, size, i)
+}
+func (c dwCtxt) AddBytes(s dwarf.Sym, b []byte) {
+	ls := s.(*LSym)
+	ls.WriteBytes(c.Link, ls.Size, b)
+}
+func (c dwCtxt) AddString(s dwarf.Sym, v string) {
+	ls := s.(*LSym)
+	ls.WriteString(c.Link, ls.Size, len(v), v)
+	ls.WriteInt(c.Link, ls.Size, 1, 0)
+}
+func (c dwCtxt) SymValue(s dwarf.Sym) int64 {
+	return 0
+}
+func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
+	rsym := data.(*LSym)
+	ls := s.(*LSym)
+	size := c.PtrSize()
+	ls.WriteAddr(c.Link, ls.Size, size, rsym, value)
+}
+func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
+	ls := s.(*LSym)
+	rsym := t.(*LSym)
+	ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs)
+	r := &ls.R[len(ls.R)-1]
+	r.Type = R_DWARFREF
+}
+
+func gendwarf(ctxt *Link, text []*LSym) []*LSym {
+	dctxt := dwCtxt{ctxt}
+	var dw []*LSym
+
+	for _, s := range text {
+		dsym := Linklookup(ctxt, dwarf.InfoPrefix+s.Name, int(s.Version))
+		if dsym.Size != 0 {
+			continue
+		}
+		dw = append(dw, dsym)
+		dsym.Type = SDWARFINFO
+		dsym.Set(AttrDuplicateOK, s.DuplicateOK())
+		var vars dwarf.Var
+		var abbrev int
+		var offs int32
+		for a := s.Autom; a != nil; a = a.Link {
+			switch a.Name {
+			case NAME_AUTO:
+				abbrev = dwarf.DW_ABRV_AUTO
+				offs = a.Aoffset
+				if ctxt.FixedFrameSize() == 0 {
+					offs -= int32(ctxt.Arch.PtrSize)
+				}
+				if Framepointer_enabled(GOOS, GOARCH) {
+					offs -= int32(ctxt.Arch.PtrSize)
+				}
+
+			case NAME_PARAM:
+				abbrev = dwarf.DW_ABRV_PARAM
+				offs = a.Aoffset + int32(ctxt.FixedFrameSize())
+
+			default:
+				continue
+			}
+			typename := dwarf.InfoPrefix + a.Gotype.Name[len("type."):]
+			dwvar := &dwarf.Var{
+				Name:   a.Asym.Name,
+				Abbrev: abbrev,
+				Offset: int32(offs),
+				Type:   Linklookup(ctxt, typename, 0),
+			}
+			dws := &vars.Link
+			for ; *dws != nil; dws = &(*dws).Link {
+				if offs <= (*dws).Offset {
+					break
+				}
+			}
+			dwvar.Link = *dws
+			*dws = dwvar
+		}
+		dwarf.PutFunc(dctxt, dsym, s.Name, s.Version == 0, s, s.Size, vars.Link)
+	}
+	return dw
+}
diff --git a/src/cmd/internal/obj/pass.go b/src/cmd/internal/obj/pass.go
index ffbff74..1d2f74b 100644
--- a/src/cmd/internal/obj/pass.go
+++ b/src/cmd/internal/obj/pass.go
@@ -1,5 +1,5 @@
 // Inferno utils/6l/pass.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/pass.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/pass.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
diff --git a/src/cmd/internal/obj/pcln.go b/src/cmd/internal/obj/pcln.go
index b1536eb..d9893e4 100644
--- a/src/cmd/internal/obj/pcln.go
+++ b/src/cmd/internal/obj/pcln.go
@@ -4,10 +4,7 @@
 
 package obj
 
-import (
-	"fmt"
-	"log"
-)
+import "log"
 
 func addvarint(ctxt *Link, d *Pcdata, val uint32) {
 	var v uint32
@@ -28,18 +25,19 @@ func addvarint(ctxt *Link, d *Pcdata, val uint32) {
 // where func is the function, val is the current value, p is the instruction being
 // considered, and arg can be used to further parameterize valfunc.
 func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) {
-	// To debug a specific function, uncomment second line and change name.
+	// To debug a specific function, uncomment lines and change name.
 	dbg := 0
 
-	//dbg = strcmp(func->name, "main.main") == 0;
-	//dbg = strcmp(desc, "pctofile") == 0;
+	//if func_.Name == "main.main" || desc == "pctospadj" {
+	//	dbg = 1
+	//}
 
 	ctxt.Debugpcln += int32(dbg)
 
 	dst.P = dst.P[:0]
 
 	if ctxt.Debugpcln != 0 {
-		fmt.Fprintf(ctxt.Bso, "funcpctab %s [valfunc=%s]\n", func_.Name, desc)
+		ctxt.Logf("funcpctab %s [valfunc=%s]\n", func_.Name, desc)
 	}
 
 	val := int32(-1)
@@ -52,7 +50,7 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
 	pc := func_.Text.Pc
 
 	if ctxt.Debugpcln != 0 {
-		fmt.Fprintf(ctxt.Bso, "%6x %6d %v\n", uint64(pc), val, func_.Text)
+		ctxt.Logf("%6x %6d %v\n", uint64(pc), val, func_.Text)
 	}
 
 	started := int32(0)
@@ -64,7 +62,7 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
 		if val == oldval && started != 0 {
 			val = valfunc(ctxt, func_, val, p, 1, arg)
 			if ctxt.Debugpcln != 0 {
-				fmt.Fprintf(ctxt.Bso, "%6x %6s %v\n", uint64(p.Pc), "", p)
+				ctxt.Logf("%6x %6s %v\n", uint64(p.Pc), "", p)
 			}
 			continue
 		}
@@ -76,7 +74,7 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
 		if p.Link != nil && p.Link.Pc == p.Pc {
 			val = valfunc(ctxt, func_, val, p, 1, arg)
 			if ctxt.Debugpcln != 0 {
-				fmt.Fprintf(ctxt.Bso, "%6x %6s %v\n", uint64(p.Pc), "", p)
+				ctxt.Logf("%6x %6s %v\n", uint64(p.Pc), "", p)
 			}
 			continue
 		}
@@ -96,7 +94,7 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
 		// where the 0x80 bit indicates that the integer continues.
 
 		if ctxt.Debugpcln != 0 {
-			fmt.Fprintf(ctxt.Bso, "%6x %6d %v\n", uint64(p.Pc), val, p)
+			ctxt.Logf("%6x %6d %v\n", uint64(p.Pc), val, p)
 		}
 
 		if started != 0 {
@@ -118,18 +116,18 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*
 
 	if started != 0 {
 		if ctxt.Debugpcln != 0 {
-			fmt.Fprintf(ctxt.Bso, "%6x done\n", uint64(func_.Text.Pc+func_.Size))
+			ctxt.Logf("%6x done\n", uint64(func_.Text.Pc+func_.Size))
 		}
 		addvarint(ctxt, dst, uint32((func_.Size-pc)/int64(ctxt.Arch.MinLC)))
 		addvarint(ctxt, dst, 0) // terminator
 	}
 
 	if ctxt.Debugpcln != 0 {
-		fmt.Fprintf(ctxt.Bso, "wrote %d bytes to %p\n", len(dst.P), dst)
+		ctxt.Logf("wrote %d bytes to %p\n", len(dst.P), dst)
 		for i := 0; i < len(dst.P); i++ {
-			fmt.Fprintf(ctxt.Bso, " %02x", dst.P[i])
+			ctxt.Logf(" %02x", dst.P[i])
 		}
-		fmt.Fprintf(ctxt.Bso, "\n")
+		ctxt.Logf("\n")
 	}
 
 	ctxt.Debugpcln -= int32(dbg)
@@ -217,9 +215,15 @@ func linkpcln(ctxt *Link, cursym *LSym) {
 	npcdata := 0
 	nfuncdata := 0
 	for p := cursym.Text; p != nil; p = p.Link {
-		if p.As == APCDATA && p.From.Offset >= int64(npcdata) {
+		// Find the highest ID of any used PCDATA table. This ignores PCDATA table
+		// that consist entirely of "-1", since that's the assumed default value.
+		//   From.Offset is table ID
+		//   To.Offset is data
+		if p.As == APCDATA && p.From.Offset >= int64(npcdata) && p.To.Offset != -1 { // ignore -1 as we start at -1, if we only see -1, nothing changed
 			npcdata = int(p.From.Offset + 1)
 		}
+		// Find the highest ID of any FUNCDATA table.
+		//   From.Offset is table ID
 		if p.As == AFUNCDATA && p.From.Offset >= int64(nfuncdata) {
 			nfuncdata = int(p.From.Offset + 1)
 		}
@@ -246,7 +250,7 @@ func linkpcln(ctxt *Link, cursym *LSym) {
 			havefunc[p.From.Offset/32] |= 1 << uint64(p.From.Offset%32)
 		}
 
-		if p.As == APCDATA {
+		if p.As == APCDATA && p.To.Offset != -1 {
 			havepc[p.From.Offset/32] |= 1 << uint64(p.From.Offset%32)
 		}
 	}
diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go
index e55dbec..804ea63 100644
--- a/src/cmd/internal/obj/plist.go
+++ b/src/cmd/internal/obj/plist.go
@@ -11,10 +11,7 @@ import (
 )
 
 type Plist struct {
-	Name    *LSym
 	Firstpc *Prog
-	Recur   int
-	Link    *Plist
 }
 
 /*
@@ -22,12 +19,7 @@ type Plist struct {
  */
 func Linknewplist(ctxt *Link) *Plist {
 	pl := new(Plist)
-	if ctxt.Plist == nil {
-		ctxt.Plist = pl
-	} else {
-		ctxt.Plast.Link = pl
-	}
-	ctxt.Plast = pl
+	ctxt.Plists = append(ctxt.Plists, pl)
 	return pl
 }
 
@@ -45,7 +37,7 @@ func flushplist(ctxt *Link, freeProgs bool) {
 	var etext *Prog
 	var text []*LSym
 
-	for pl := ctxt.Plist; pl != nil; pl = pl.Link {
+	for _, pl := range ctxt.Plists {
 		var plink *Prog
 		for p := pl.Firstpc; p != nil; p = plink {
 			if ctxt.Debugasm != 0 && ctxt.Debugvlog != 0 {
@@ -77,39 +69,11 @@ func flushplist(ctxt *Link, freeProgs bool) {
 				a.Asym = p.From.Sym
 				a.Aoffset = int32(p.From.Offset)
 				a.Name = int16(p.From.Name)
-				a.Gotype = p.From.Gotype
+				a.Gotype = p.To.Sym
 				a.Link = curtext.Autom
 				curtext.Autom = a
 				continue
 
-			case AGLOBL:
-				s := p.From.Sym
-				if s.Seenglobl {
-					fmt.Printf("duplicate %v\n", p)
-				}
-				s.Seenglobl = true
-				if s.Onlist {
-					log.Fatalf("symbol %s listed multiple times", s.Name)
-				}
-				s.Onlist = true
-				ctxt.Data = append(ctxt.Data, s)
-				s.Size = p.To.Offset
-				if s.Type == 0 || s.Type == SXREF {
-					s.Type = SBSS
-				}
-				flag := int(p.From3.Offset)
-				if flag&DUPOK != 0 {
-					s.Dupok = true
-				}
-				if flag&RODATA != 0 {
-					s.Type = SRODATA
-				} else if flag&NOPTR != 0 {
-					s.Type = SNOPTRBSS
-				} else if flag&TLSBSS != 0 {
-					s.Type = STLSBSS
-				}
-				continue
-
 			case ATEXT:
 				s := p.From.Sym
 				if s == nil {
@@ -122,20 +86,20 @@ func flushplist(ctxt *Link, freeProgs bool) {
 				if s.Text != nil {
 					log.Fatalf("duplicate TEXT for %s", s.Name)
 				}
-				if s.Onlist {
+				if s.OnList() {
 					log.Fatalf("symbol %s listed multiple times", s.Name)
 				}
-				s.Onlist = true
+				s.Set(AttrOnList, true)
 				text = append(text, s)
 				flag := int(p.From3Offset())
 				if flag&DUPOK != 0 {
-					s.Dupok = true
+					s.Set(AttrDuplicateOK, true)
 				}
 				if flag&NOSPLIT != 0 {
-					s.Nosplit = true
+					s.Set(AttrNoSplit, true)
 				}
 				if flag&REFLECTMETHOD != 0 {
-					s.ReflectMethod = true
+					s.Set(AttrReflectMethod, true)
 				}
 				s.Type = STEXT
 				s.Text = p
@@ -209,10 +173,36 @@ func flushplist(ctxt *Link, freeProgs bool) {
 
 	// Add to running list in ctxt.
 	ctxt.Text = append(ctxt.Text, text...)
-	ctxt.Plist = nil
-	ctxt.Plast = nil
+	ctxt.Data = append(ctxt.Data, gendwarf(ctxt, text)...)
+	ctxt.Plists = nil
 	ctxt.Curp = nil
 	if freeProgs {
 		ctxt.freeProgs()
 	}
 }
+
+func (ctxt *Link) Globl(s *LSym, size int64, flag int) {
+	if s.SeenGlobl() {
+		fmt.Printf("duplicate %v\n", s)
+	}
+	s.Set(AttrSeenGlobl, true)
+	if s.OnList() {
+		log.Fatalf("symbol %s listed multiple times", s.Name)
+	}
+	s.Set(AttrOnList, true)
+	ctxt.Data = append(ctxt.Data, s)
+	s.Size = size
+	if s.Type == 0 || s.Type == SXREF {
+		s.Type = SBSS
+	}
+	if flag&DUPOK != 0 {
+		s.Set(AttrDuplicateOK, true)
+	}
+	if flag&RODATA != 0 {
+		s.Type = SRODATA
+	} else if flag&NOPTR != 0 {
+		s.Type = SNOPTRBSS
+	} else if flag&TLSBSS != 0 {
+		s.Type = STLSBSS
+	}
+}
diff --git a/src/cmd/internal/obj/ppc64/a.out.go b/src/cmd/internal/obj/ppc64/a.out.go
index 8e58c59..6078131 100644
--- a/src/cmd/internal/obj/ppc64/a.out.go
+++ b/src/cmd/internal/obj/ppc64/a.out.go
@@ -44,6 +44,8 @@ const (
 )
 
 const (
+	/* RBasePPC64 = 4096 */
+	/* R0=4096 ... R31=4127 */
 	REG_R0 = obj.RBasePPC64 + iota
 	REG_R1
 	REG_R2
@@ -77,6 +79,7 @@ const (
 	REG_R30
 	REG_R31
 
+	/* F0=4128 ... F31=4159 */
 	REG_F0
 	REG_F1
 	REG_F2
@@ -110,6 +113,106 @@ const (
 	REG_F30
 	REG_F31
 
+	/* V0=4160 ... V31=4191 */
+	REG_V0
+	REG_V1
+	REG_V2
+	REG_V3
+	REG_V4
+	REG_V5
+	REG_V6
+	REG_V7
+	REG_V8
+	REG_V9
+	REG_V10
+	REG_V11
+	REG_V12
+	REG_V13
+	REG_V14
+	REG_V15
+	REG_V16
+	REG_V17
+	REG_V18
+	REG_V19
+	REG_V20
+	REG_V21
+	REG_V22
+	REG_V23
+	REG_V24
+	REG_V25
+	REG_V26
+	REG_V27
+	REG_V28
+	REG_V29
+	REG_V30
+	REG_V31
+
+	/* VS0=4192 ... VS63=4255 */
+	REG_VS0
+	REG_VS1
+	REG_VS2
+	REG_VS3
+	REG_VS4
+	REG_VS5
+	REG_VS6
+	REG_VS7
+	REG_VS8
+	REG_VS9
+	REG_VS10
+	REG_VS11
+	REG_VS12
+	REG_VS13
+	REG_VS14
+	REG_VS15
+	REG_VS16
+	REG_VS17
+	REG_VS18
+	REG_VS19
+	REG_VS20
+	REG_VS21
+	REG_VS22
+	REG_VS23
+	REG_VS24
+	REG_VS25
+	REG_VS26
+	REG_VS27
+	REG_VS28
+	REG_VS29
+	REG_VS30
+	REG_VS31
+	REG_VS32
+	REG_VS33
+	REG_VS34
+	REG_VS35
+	REG_VS36
+	REG_VS37
+	REG_VS38
+	REG_VS39
+	REG_VS40
+	REG_VS41
+	REG_VS42
+	REG_VS43
+	REG_VS44
+	REG_VS45
+	REG_VS46
+	REG_VS47
+	REG_VS48
+	REG_VS49
+	REG_VS50
+	REG_VS51
+	REG_VS52
+	REG_VS53
+	REG_VS54
+	REG_VS55
+	REG_VS56
+	REG_VS57
+	REG_VS58
+	REG_VS59
+	REG_VS60
+	REG_VS61
+	REG_VS62
+	REG_VS63
+
 	REG_CR0
 	REG_CR1
 	REG_CR2
@@ -132,29 +235,24 @@ const (
 	REG_LR  = REG_SPR0 + 8
 	REG_CTR = REG_SPR0 + 9
 
-	REGZERO  = REG_R0 /* set to zero */
-	REGSP    = REG_R1
-	REGSB    = REG_R2
-	REGRET   = REG_R3
-	REGARG   = -1      /* -1 disables passing the first argument in register */
-	REGRT1   = REG_R3  /* reserved for runtime, duffzero and duffcopy */
-	REGRT2   = REG_R4  /* reserved for runtime, duffcopy */
-	REGMIN   = REG_R7  /* register variables allocated from here to REGMAX */
-	REGCTXT  = REG_R11 /* context for closures */
-	REGTLS   = REG_R13 /* C ABI TLS base pointer */
-	REGMAX   = REG_R27
-	REGEXT   = REG_R30 /* external registers allocated from here down */
-	REGG     = REG_R30 /* G */
-	REGTMP   = REG_R31 /* used by the linker */
-	FREGRET  = REG_F0
-	FREGMIN  = REG_F17 /* first register variable */
-	FREGMAX  = REG_F26 /* last register variable for 9g only */
-	FREGEXT  = REG_F26 /* first external register */
-	FREGCVI  = REG_F27 /* floating conversion constant */
-	FREGZERO = REG_F28 /* both float and double */
-	FREGHALF = REG_F29 /* double */
-	FREGONE  = REG_F30 /* double */
-	FREGTWO  = REG_F31 /* double */
+	REGZERO = REG_R0 /* set to zero */
+	REGSP   = REG_R1
+	REGSB   = REG_R2
+	REGRET  = REG_R3
+	REGARG  = -1      /* -1 disables passing the first argument in register */
+	REGRT1  = REG_R3  /* reserved for runtime, duffzero and duffcopy */
+	REGRT2  = REG_R4  /* reserved for runtime, duffcopy */
+	REGMIN  = REG_R7  /* register variables allocated from here to REGMAX */
+	REGCTXT = REG_R11 /* context for closures */
+	REGTLS  = REG_R13 /* C ABI TLS base pointer */
+	REGMAX  = REG_R27
+	REGEXT  = REG_R30 /* external registers allocated from here down */
+	REGG    = REG_R30 /* G */
+	REGTMP  = REG_R31 /* used by the linker */
+	FREGRET = REG_F0
+	FREGMIN = REG_F17 /* first register variable */
+	FREGMAX = REG_F26 /* last register variable for 9g only */
+	FREGEXT = REG_F26 /* first external register */
 )
 
 /*
@@ -185,10 +283,58 @@ const (
 	NOSCHED = 1 << 9
 )
 
+// Values for use in branch instruction BC
+// BC B0,BI,label
+// BO is type of branch + likely bits described below
+// BI is CR value + branch type
+// ex: BEQ CR2,label is BC 12,10,label
+//   12 = BO_BCR
+//   10 = BI_CR2 + BI_EQ
+
+const (
+	BI_CR0 = 0
+	BI_CR1 = 4
+	BI_CR2 = 8
+	BI_CR3 = 12
+	BI_CR4 = 16
+	BI_CR5 = 20
+	BI_CR6 = 24
+	BI_CR7 = 28
+	BI_LT  = 0
+	BI_GT  = 1
+	BI_EQ  = 2
+	BI_OVF = 3
+)
+
+// Values for the BO field.  Add the branch type to
+// the likely bits, if a likely setting is known.
+// If branch likely or unlikely is not known, don't set it.
+// e.g. branch on cr+likely = 15
+
+const (
+	BO_BCTR     = 16 // branch on ctr value
+	BO_BCR      = 12 // branch on cr value
+	BO_BCRBCTR  = 8  // branch on ctr and cr value
+	BO_NOTBCR   = 4  // branch on not cr value
+	BO_UNLIKELY = 2  // value for unlikely
+	BO_LIKELY   = 3  // value for likely
+)
+
+// Bit settings from the CR
+
+const (
+	C_COND_LT = iota // 0 result is negative
+	C_COND_GT        // 1 result is positive
+	C_COND_EQ        // 2 result is zero
+	C_COND_SO        // 3 summary overflow or FP compare w/ NaN
+)
+
 const (
 	C_NONE = iota
 	C_REG
 	C_FREG
+	C_VREG
+	C_VSREG
 	C_CREG
 	C_SPR /* special processor register */
 	C_ZCON
@@ -210,8 +356,8 @@ const (
 	C_LAUTO
 	C_SEXT
 	C_LEXT
-	C_ZOREG
-	C_SOREG
+	C_ZOREG // conjecture: either (1) register + zeroed offset, or (2) "R0" implies zero or C_REG
+	C_SOREG // register + signed offset
 	C_LOREG
 	C_FPSCR
 	C_MSR
@@ -257,13 +403,13 @@ const (
 	ABC
 	ABCL
 	ABEQ
-	ABGE
+	ABGE // not LT = G/E/U
 	ABGT
-	ABLE
+	ABLE // not GT = L/E/U
 	ABLT
-	ABNE
-	ABVC
-	ABVS
+	ABNE // not EQ = L/G/U
+	ABVC // Unordered-clear
+	ABVS // Unordered-set
 	ACMP
 	ACMPU
 	ACNTLZW
@@ -315,6 +461,8 @@ const (
 	AFMOVDU
 	AFMOVS
 	AFMOVSU
+	AFMOVSX
+	AFMOVSZ
 	AFMSUB
 	AFMSUBCC
 	AFMSUBS
@@ -341,11 +489,13 @@ const (
 	AFSUBCC
 	AFSUBS
 	AFSUBSCC
+	AISEL
 	AMOVMW
 	ALBAR
 	ALSW
 	ALWAR
 	ALWSYNC
+	AMOVDBR
 	AMOVWBR
 	AMOVB
 	AMOVBU
@@ -455,6 +605,12 @@ const (
 	/* optional on 32-bit */
 	AFRES
 	AFRESCC
+	AFRIM
+	AFRIMCC
+	AFRIP
+	AFRIPCC
+	AFRIZ
+	AFRIZCC
 	AFRSQRTE
 	AFRSQRTECC
 	AFSEL
@@ -472,6 +628,10 @@ const (
 	ACMPWU
 	ADIVD
 	ADIVDCC
+	ADIVDE
+	ADIVDECC
+	ADIVDEU
+	ADIVDEUCC
 	ADIVDVCC
 	ADIVDV
 	ADIVDU
@@ -505,12 +665,18 @@ const (
 	ARFID
 	ARLDMI
 	ARLDMICC
+	ARLDIMI
+	ARLDIMICC
 	ARLDC
 	ARLDCCC
 	ARLDCR
 	ARLDCRCC
+	ARLDICR
+	ARLDICRCC
 	ARLDCL
 	ARLDCLCC
+	ARLDICL
+	ARLDICLCC
 	ASLBIA
 	ASLBIE
 	ASLBMFEE
@@ -539,6 +705,234 @@ const (
 	/* more 64-bit operations */
 	AHRFID
 
+	/* Vector */
+	ALV
+	ALVEBX
+	ALVEHX
+	ALVEWX
+	ALVX
+	ALVXL
+	ALVSL
+	ALVSR
+	ASTV
+	ASTVEBX
+	ASTVEHX
+	ASTVEWX
+	ASTVX
+	ASTVXL
+	AVAND
+	AVANDL
+	AVANDC
+	AVNAND
+	AVOR
+	AVORL
+	AVORC
+	AVNOR
+	AVXOR
+	AVEQV
+	AVADDUM
+	AVADDUBM
+	AVADDUHM
+	AVADDUWM
+	AVADDUDM
+	AVADDUQM
+	AVADDCU
+	AVADDCUQ
+	AVADDCUW
+	AVADDUS
+	AVADDUBS
+	AVADDUHS
+	AVADDUWS
+	AVADDSS
+	AVADDSBS
+	AVADDSHS
+	AVADDSWS
+	AVADDE
+	AVADDEUQM
+	AVADDECUQ
+	AVSUBUM
+	AVSUBUBM
+	AVSUBUHM
+	AVSUBUWM
+	AVSUBUDM
+	AVSUBUQM
+	AVSUBCU
+	AVSUBCUQ
+	AVSUBCUW
+	AVSUBUS
+	AVSUBUBS
+	AVSUBUHS
+	AVSUBUWS
+	AVSUBSS
+	AVSUBSBS
+	AVSUBSHS
+	AVSUBSWS
+	AVSUBE
+	AVSUBEUQM
+	AVSUBECUQ
+	AVR
+	AVRLB
+	AVRLH
+	AVRLW
+	AVRLD
+	AVS
+	AVSLB
+	AVSLH
+	AVSLW
+	AVSL
+	AVSLO
+	AVSRB
+	AVSRH
+	AVSRW
+	AVSR
+	AVSRO
+	AVSLD
+	AVSRD
+	AVSA
+	AVSRAB
+	AVSRAH
+	AVSRAW
+	AVSRAD
+	AVSOI
+	AVSLDOI
+	AVCLZ
+	AVCLZB
+	AVCLZH
+	AVCLZW
+	AVCLZD
+	AVPOPCNT
+	AVPOPCNTB
+	AVPOPCNTH
+	AVPOPCNTW
+	AVPOPCNTD
+	AVCMPEQ
+	AVCMPEQUB
+	AVCMPEQUBCC
+	AVCMPEQUH
+	AVCMPEQUHCC
+	AVCMPEQUW
+	AVCMPEQUWCC
+	AVCMPEQUD
+	AVCMPEQUDCC
+	AVCMPGT
+	AVCMPGTUB
+	AVCMPGTUBCC
+	AVCMPGTUH
+	AVCMPGTUHCC
+	AVCMPGTUW
+	AVCMPGTUWCC
+	AVCMPGTUD
+	AVCMPGTUDCC
+	AVCMPGTSB
+	AVCMPGTSBCC
+	AVCMPGTSH
+	AVCMPGTSHCC
+	AVCMPGTSW
+	AVCMPGTSWCC
+	AVCMPGTSD
+	AVCMPGTSDCC
+	AVPERM
+	AVSEL
+	AVSPLT
+	AVSPLTB
+	AVSPLTH
+	AVSPLTW
+	AVSPLTI
+	AVSPLTISB
+	AVSPLTISH
+	AVSPLTISW
+	AVCIPH
+	AVCIPHER
+	AVCIPHERLAST
+	AVNCIPH
+	AVNCIPHER
+	AVNCIPHERLAST
+	AVSBOX
+	AVSHASIGMA
+	AVSHASIGMAW
+	AVSHASIGMAD
+
+	/* VSX */
+	ALXV
+	ALXVD2X
+	ALXVDSX
+	ALXVW4X
+	ASTXV
+	ASTXVD2X
+	ASTXVW4X
+	ALXS
+	ALXSDX
+	ASTXS
+	ASTXSDX
+	ALXSI
+	ALXSIWAX
+	ALXSIWZX
+	ASTXSI
+	ASTXSIWX
+	AMFVSR
+	AMFVSRD
+	AMFVSRWZ
+	AMTVSR
+	AMTVSRD
+	AMTVSRWA
+	AMTVSRWZ
+	AXXLAND
+	AXXLANDQ
+	AXXLANDC
+	AXXLEQV
+	AXXLNAND
+	AXXLOR
+	AXXLORC
+	AXXLNOR
+	AXXLORQ
+	AXXLXOR
+	AXXSEL
+	AXXMRG
+	AXXMRGHW
+	AXXMRGLW
+	AXXSPLT
+	AXXSPLTW
+	AXXPERM
+	AXXPERMDI
+	AXXSI
+	AXXSLDWI
+	AXSCV
+	AXSCVDPSP
+	AXSCVSPDP
+	AXSCVDPSPN
+	AXSCVSPDPN
+	AXVCV
+	AXVCVDPSP
+	AXVCVSPDP
+	AXSCVX
+	AXSCVDPSXDS
+	AXSCVDPSXWS
+	AXSCVDPUXDS
+	AXSCVDPUXWS
+	AXSCVXP
+	AXSCVSXDDP
+	AXSCVUXDDP
+	AXSCVSXDSP
+	AXSCVUXDSP
+	AXVCVX
+	AXVCVDPSXDS
+	AXVCVDPSXWS
+	AXVCVDPUXDS
+	AXVCVDPUXWS
+	AXVCVSPSXDS
+	AXVCVSPSXWS
+	AXVCVSPUXDS
+	AXVCVSPUXWS
+	AXVCVXP
+	AXVCVSXDDP
+	AXVCVSXWDP
+	AXVCVUXDDP
+	AXVCVUXWDP
+	AXVCVSXDSP
+	AXVCVSXWSP
+	AXVCVUXDSP
+	AXVCVUXWSP
+
 	ALAST
 
 	// aliases
diff --git a/src/cmd/internal/obj/ppc64/anames.go b/src/cmd/internal/obj/ppc64/anames.go
index eb42c9a..19ddd3c 100644
--- a/src/cmd/internal/obj/ppc64/anames.go
+++ b/src/cmd/internal/obj/ppc64/anames.go
@@ -91,6 +91,8 @@ var Anames = []string{
 	"FMOVDU",
 	"FMOVS",
 	"FMOVSU",
+	"FMOVSX",
+	"FMOVSZ",
 	"FMSUB",
 	"FMSUBCC",
 	"FMSUBS",
@@ -117,11 +119,13 @@ var Anames = []string{
 	"FSUBCC",
 	"FSUBS",
 	"FSUBSCC",
+	"ISEL",
 	"MOVMW",
 	"LBAR",
 	"LSW",
 	"LWAR",
 	"LWSYNC",
+	"MOVDBR",
 	"MOVWBR",
 	"MOVB",
 	"MOVBU",
@@ -226,6 +230,12 @@ var Anames = []string{
 	"RFCI",
 	"FRES",
 	"FRESCC",
+	"FRIM",
+	"FRIMCC",
+	"FRIP",
+	"FRIPCC",
+	"FRIZ",
+	"FRIZCC",
 	"FRSQRTE",
 	"FRSQRTECC",
 	"FSEL",
@@ -240,6 +250,10 @@ var Anames = []string{
 	"CMPWU",
 	"DIVD",
 	"DIVDCC",
+	"DIVDE",
+	"DIVDECC",
+	"DIVDEU",
+	"DIVDEUCC",
 	"DIVDVCC",
 	"DIVDV",
 	"DIVDU",
@@ -272,12 +286,18 @@ var Anames = []string{
 	"RFID",
 	"RLDMI",
 	"RLDMICC",
+	"RLDIMI",
+	"RLDIMICC",
 	"RLDC",
 	"RLDCCC",
 	"RLDCR",
 	"RLDCRCC",
+	"RLDICR",
+	"RLDICRCC",
 	"RLDCL",
 	"RLDCLCC",
+	"RLDICL",
+	"RLDICLCC",
 	"SLBIA",
 	"SLBIE",
 	"SLBMFEE",
@@ -301,5 +321,229 @@ var Anames = []string{
 	"REMDUV",
 	"REMDUVCC",
 	"HRFID",
+	"LV",
+	"LVEBX",
+	"LVEHX",
+	"LVEWX",
+	"LVX",
+	"LVXL",
+	"LVSL",
+	"LVSR",
+	"STV",
+	"STVEBX",
+	"STVEHX",
+	"STVEWX",
+	"STVX",
+	"STVXL",
+	"VAND",
+	"VANDL",
+	"VANDC",
+	"VNAND",
+	"VOR",
+	"VORL",
+	"VORC",
+	"VNOR",
+	"VXOR",
+	"VEQV",
+	"VADDUM",
+	"VADDUBM",
+	"VADDUHM",
+	"VADDUWM",
+	"VADDUDM",
+	"VADDUQM",
+	"VADDCU",
+	"VADDCUQ",
+	"VADDCUW",
+	"VADDUS",
+	"VADDUBS",
+	"VADDUHS",
+	"VADDUWS",
+	"VADDSS",
+	"VADDSBS",
+	"VADDSHS",
+	"VADDSWS",
+	"VADDE",
+	"VADDEUQM",
+	"VADDECUQ",
+	"VSUBUM",
+	"VSUBUBM",
+	"VSUBUHM",
+	"VSUBUWM",
+	"VSUBUDM",
+	"VSUBUQM",
+	"VSUBCU",
+	"VSUBCUQ",
+	"VSUBCUW",
+	"VSUBUS",
+	"VSUBUBS",
+	"VSUBUHS",
+	"VSUBUWS",
+	"VSUBSS",
+	"VSUBSBS",
+	"VSUBSHS",
+	"VSUBSWS",
+	"VSUBE",
+	"VSUBEUQM",
+	"VSUBECUQ",
+	"VR",
+	"VRLB",
+	"VRLH",
+	"VRLW",
+	"VRLD",
+	"VS",
+	"VSLB",
+	"VSLH",
+	"VSLW",
+	"VSL",
+	"VSLO",
+	"VSRB",
+	"VSRH",
+	"VSRW",
+	"VSR",
+	"VSRO",
+	"VSLD",
+	"VSRD",
+	"VSA",
+	"VSRAB",
+	"VSRAH",
+	"VSRAW",
+	"VSRAD",
+	"VSOI",
+	"VSLDOI",
+	"VCLZ",
+	"VCLZB",
+	"VCLZH",
+	"VCLZW",
+	"VCLZD",
+	"VPOPCNT",
+	"VPOPCNTB",
+	"VPOPCNTH",
+	"VPOPCNTW",
+	"VPOPCNTD",
+	"VCMPEQ",
+	"VCMPEQUB",
+	"VCMPEQUBCC",
+	"VCMPEQUH",
+	"VCMPEQUHCC",
+	"VCMPEQUW",
+	"VCMPEQUWCC",
+	"VCMPEQUD",
+	"VCMPEQUDCC",
+	"VCMPGT",
+	"VCMPGTUB",
+	"VCMPGTUBCC",
+	"VCMPGTUH",
+	"VCMPGTUHCC",
+	"VCMPGTUW",
+	"VCMPGTUWCC",
+	"VCMPGTUD",
+	"VCMPGTUDCC",
+	"VCMPGTSB",
+	"VCMPGTSBCC",
+	"VCMPGTSH",
+	"VCMPGTSHCC",
+	"VCMPGTSW",
+	"VCMPGTSWCC",
+	"VCMPGTSD",
+	"VCMPGTSDCC",
+	"VPERM",
+	"VSEL",
+	"VSPLT",
+	"VSPLTB",
+	"VSPLTH",
+	"VSPLTW",
+	"VSPLTI",
+	"VSPLTISB",
+	"VSPLTISH",
+	"VSPLTISW",
+	"VCIPH",
+	"VCIPHER",
+	"VCIPHERLAST",
+	"VNCIPH",
+	"VNCIPHER",
+	"VNCIPHERLAST",
+	"VSBOX",
+	"VSHASIGMA",
+	"VSHASIGMAW",
+	"VSHASIGMAD",
+	"LXV",
+	"LXVD2X",
+	"LXVDSX",
+	"LXVW4X",
+	"STXV",
+	"STXVD2X",
+	"STXVW4X",
+	"LXS",
+	"LXSDX",
+	"STXS",
+	"STXSDX",
+	"LXSI",
+	"LXSIWAX",
+	"LXSIWZX",
+	"STXSI",
+	"STXSIWX",
+	"MFVSR",
+	"MFVSRD",
+	"MFVSRWZ",
+	"MTVSR",
+	"MTVSRD",
+	"MTVSRWA",
+	"MTVSRWZ",
+	"XXLAND",
+	"XXLANDQ",
+	"XXLANDC",
+	"XXLEQV",
+	"XXLNAND",
+	"XXLOR",
+	"XXLORC",
+	"XXLNOR",
+	"XXLORQ",
+	"XXLXOR",
+	"XXSEL",
+	"XXMRG",
+	"XXMRGHW",
+	"XXMRGLW",
+	"XXSPLT",
+	"XXSPLTW",
+	"XXPERM",
+	"XXPERMDI",
+	"XXSI",
+	"XXSLDWI",
+	"XSCV",
+	"XSCVDPSP",
+	"XSCVSPDP",
+	"XSCVDPSPN",
+	"XSCVSPDPN",
+	"XVCV",
+	"XVCVDPSP",
+	"XVCVSPDP",
+	"XSCVX",
+	"XSCVDPSXDS",
+	"XSCVDPSXWS",
+	"XSCVDPUXDS",
+	"XSCVDPUXWS",
+	"XSCVXP",
+	"XSCVSXDDP",
+	"XSCVUXDDP",
+	"XSCVSXDSP",
+	"XSCVUXDSP",
+	"XVCVX",
+	"XVCVDPSXDS",
+	"XVCVDPSXWS",
+	"XVCVDPUXDS",
+	"XVCVDPUXWS",
+	"XVCVSPSXDS",
+	"XVCVSPSXWS",
+	"XVCVSPUXDS",
+	"XVCVSPUXWS",
+	"XVCVXP",
+	"XVCVSXDDP",
+	"XVCVSXWDP",
+	"XVCVUXDDP",
+	"XVCVUXWDP",
+	"XVCVSXDSP",
+	"XVCVSXWSP",
+	"XVCVUXDSP",
+	"XVCVUXWSP",
 	"LAST",
 }
diff --git a/src/cmd/internal/obj/ppc64/anames9.go b/src/cmd/internal/obj/ppc64/anames9.go
index f7d1d77..6ec7b7b 100644
--- a/src/cmd/internal/obj/ppc64/anames9.go
+++ b/src/cmd/internal/obj/ppc64/anames9.go
@@ -8,6 +8,8 @@ var cnames9 = []string{
 	"NONE",
 	"REG",
 	"FREG",
+	"VREG",
+	"VSREG",
 	"CREG",
 	"SPR",
 	"ZCON",
diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go
index 5366809..4f86554 100644
--- a/src/cmd/internal/obj/ppc64/asm9.go
+++ b/src/cmd/internal/obj/ppc64/asm9.go
@@ -40,7 +40,7 @@ import (
 // Instruction layout.
 
 const (
-	FuncAlign = 8
+	funcAlign = 8
 )
 
 const (
@@ -48,12 +48,12 @@ const (
 )
 
 type Optab struct {
-	as    obj.As
+	as    obj.As // Opcode
 	a1    uint8
 	a2    uint8
 	a3    uint8
 	a4    uint8
-	type_ int8
+	type_ int8 // cases in asmout below. E.g., 44 = st r,(ra+rb); 45 = ld (ra+rb), r
 	size  int8
 	param int16
 }
@@ -139,15 +139,17 @@ var optab = []Optab{
 	{ARLDC, C_SCON, C_REG, C_LCON, C_REG, 29, 4, 0},
 	{ARLDCL, C_SCON, C_REG, C_LCON, C_REG, 29, 4, 0},
 	{ARLDCL, C_REG, C_REG, C_LCON, C_REG, 14, 4, 0},
+	{ARLDICL, C_REG, C_REG, C_LCON, C_REG, 14, 4, 0},
+	{ARLDICL, C_SCON, C_REG, C_LCON, C_REG, 14, 4, 0},
 	{ARLDCL, C_REG, C_NONE, C_LCON, C_REG, 14, 4, 0},
 	{AFADD, C_FREG, C_NONE, C_NONE, C_FREG, 2, 4, 0},
-	{AFADD, C_FREG, C_REG, C_NONE, C_FREG, 2, 4, 0},
+	{AFADD, C_FREG, C_FREG, C_NONE, C_FREG, 2, 4, 0},
 	{AFABS, C_FREG, C_NONE, C_NONE, C_FREG, 33, 4, 0},
 	{AFABS, C_NONE, C_NONE, C_NONE, C_FREG, 33, 4, 0},
 	{AFMOVD, C_FREG, C_NONE, C_NONE, C_FREG, 33, 4, 0},
-	{AFMADD, C_FREG, C_REG, C_FREG, C_FREG, 34, 4, 0},
+	{AFMADD, C_FREG, C_FREG, C_FREG, C_FREG, 34, 4, 0},
 	{AFMUL, C_FREG, C_NONE, C_NONE, C_FREG, 32, 4, 0},
-	{AFMUL, C_FREG, C_REG, C_NONE, C_FREG, 32, 4, 0},
+	{AFMUL, C_FREG, C_FREG, C_NONE, C_FREG, 32, 4, 0},
 
 	/* store, short offset */
 	{AMOVD, C_REG, C_REG, C_NONE, C_ZOREG, 7, 4, REGZERO},
@@ -310,6 +312,12 @@ var optab = []Optab{
 	{AFMOVD, C_FREG, C_NONE, C_NONE, C_LAUTO, 35, 8, REGSP},
 	{AFMOVD, C_FREG, C_NONE, C_NONE, C_LOREG, 35, 8, REGZERO},
 	{AFMOVD, C_FREG, C_NONE, C_NONE, C_ADDR, 74, 8, 0},
+	{AFMOVSX, C_ZOREG, C_REG, C_NONE, C_FREG, 45, 4, 0},
+	{AFMOVSX, C_ZOREG, C_NONE, C_NONE, C_FREG, 45, 4, 0},
+	{AFMOVSX, C_FREG, C_REG, C_NONE, C_ZOREG, 44, 4, 0},
+	{AFMOVSX, C_FREG, C_NONE, C_NONE, C_ZOREG, 44, 4, 0},
+	{AFMOVSZ, C_ZOREG, C_REG, C_NONE, C_FREG, 45, 4, 0},
+	{AFMOVSZ, C_ZOREG, C_NONE, C_NONE, C_FREG, 45, 4, 0},
 	{ASYNC, C_NONE, C_NONE, C_NONE, C_NONE, 46, 4, 0},
 	{AWORD, C_LCON, C_NONE, C_NONE, C_NONE, 40, 4, 0},
 	{ADWORD, C_LCON, C_NONE, C_NONE, C_NONE, 31, 8, 0},
@@ -317,6 +325,8 @@ var optab = []Optab{
 	{AADDME, C_REG, C_NONE, C_NONE, C_REG, 47, 4, 0},
 	{AEXTSB, C_REG, C_NONE, C_NONE, C_REG, 48, 4, 0},
 	{AEXTSB, C_NONE, C_NONE, C_NONE, C_REG, 48, 4, 0},
+	{AISEL, C_LCON, C_REG, C_REG, C_REG, 84, 4, 0},
+	{AISEL, C_ZCON, C_REG, C_REG, C_REG, 84, 4, 0},
 	{ANEG, C_REG, C_NONE, C_NONE, C_REG, 47, 4, 0},
 	{ANEG, C_NONE, C_NONE, C_NONE, C_REG, 47, 4, 0},
 	{AREM, C_REG, C_NONE, C_NONE, C_REG, 50, 12, 0},
@@ -336,6 +346,129 @@ var optab = []Optab{
 	{AMOVD, C_REG, C_NONE, C_NONE, C_MSR, 54, 4, 0},  /* mtmsrd */
 	{AMOVWZ, C_REG, C_NONE, C_NONE, C_MSR, 54, 4, 0}, /* mtmsr */
 
+	/* Vector instructions */
+
+	/* Vector load */
+	{ALV, C_SOREG, C_NONE, C_NONE, C_VREG, 45, 4, 0}, /* vector load, x-form */
+
+	/* Vector store */
+	{ASTV, C_VREG, C_NONE, C_NONE, C_SOREG, 44, 4, 0}, /* vector store, x-form */
+
+	/* Vector logical */
+	{AVAND, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector and, vx-form */
+	{AVOR, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0},  /* vector or, vx-form */
+
+	/* Vector add */
+	{AVADDUM, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector add unsigned modulo, vx-form */
+	{AVADDCU, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector add & write carry unsigned, vx-form */
+	{AVADDUS, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector add unsigned saturate, vx-form */
+	{AVADDSS, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector add signed saturate, vx-form */
+	{AVADDE, C_VREG, C_VREG, C_VREG, C_VREG, 83, 4, 0},  /* vector add extended, va-form */
+
+	/* Vector subtract */
+	{AVSUBUM, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector subtract unsigned modulo, vx-form */
+	{AVSUBCU, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector subtract & write carry unsigned, vx-form */
+	{AVSUBUS, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector subtract unsigned saturate, vx-form */
+	{AVSUBSS, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector subtract signed saturate, vx-form */
+	{AVSUBE, C_VREG, C_VREG, C_VREG, C_VREG, 83, 4, 0},  /* vector subtract extended, va-form */
+
+	/* Vector rotate */
+	{AVR, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector rotate, vx-form */
+
+	/* Vector shift */
+	{AVS, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0},     /* vector shift, vx-form */
+	{AVSA, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0},    /* vector shift algebraic, vx-form */
+	{AVSOI, C_ANDCON, C_VREG, C_VREG, C_VREG, 83, 4, 0}, /* vector shift by octet immediate, va-form */
+
+	/* Vector count */
+	{AVCLZ, C_VREG, C_NONE, C_NONE, C_VREG, 85, 4, 0},    /* vector count leading zeros, vx-form */
+	{AVPOPCNT, C_VREG, C_NONE, C_NONE, C_VREG, 85, 4, 0}, /* vector population count, vx-form */
+
+	/* Vector compare */
+	{AVCMPEQ, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector compare equal, vc-form */
+	{AVCMPGT, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector compare greater than, vc-form */
+
+	/* Vector permute */
+	{AVPERM, C_VREG, C_VREG, C_VREG, C_VREG, 83, 4, 0}, /* vector permute, va-form */
+
+	/* Vector select */
+	{AVSEL, C_VREG, C_VREG, C_VREG, C_VREG, 83, 4, 0}, /* vector select, va-form */
+
+	/* Vector splat */
+	{AVSPLT, C_SCON, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector splat, vx-form */
+	{AVSPLT, C_ADDCON, C_VREG, C_NONE, C_VREG, 82, 4, 0},
+	{AVSPLTI, C_SCON, C_NONE, C_NONE, C_VREG, 82, 4, 0}, /* vector splat immediate, vx-form */
+	{AVSPLTI, C_ADDCON, C_NONE, C_NONE, C_VREG, 82, 4, 0},
+
+	/* Vector AES */
+	{AVCIPH, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0},  /* vector AES cipher, vx-form */
+	{AVNCIPH, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector AES inverse cipher, vx-form */
+	{AVSBOX, C_VREG, C_NONE, C_NONE, C_VREG, 82, 4, 0},  /* vector AES subbytes, vx-form */
+
+	/* Vector SHA */
+	{AVSHASIGMA, C_ANDCON, C_VREG, C_ANDCON, C_VREG, 82, 4, 0}, /* vector SHA sigma, vx-form */
+
+	/* VSX vector load */
+	{ALXV, C_SOREG, C_NONE, C_NONE, C_VSREG, 87, 4, 0}, /* vsx vector load, xx1-form */
+
+	/* VSX vector store */
+	{ASTXV, C_VSREG, C_NONE, C_NONE, C_SOREG, 86, 4, 0}, /* vsx vector store, xx1-form */
+
+	/* VSX scalar load */
+	{ALXS, C_SOREG, C_NONE, C_NONE, C_VSREG, 87, 4, 0}, /* vsx scalar load, xx1-form */
+
+	/* VSX scalar store */
+	{ASTXS, C_VSREG, C_NONE, C_NONE, C_SOREG, 86, 4, 0}, /* vsx scalar store, xx1-form */
+
+	/* VSX scalar as integer load */
+	{ALXSI, C_SOREG, C_NONE, C_NONE, C_VSREG, 87, 4, 0}, /* vsx scalar as integer load, xx1-form */
+
+	/* VSX scalar store as integer */
+	{ASTXSI, C_VSREG, C_NONE, C_NONE, C_SOREG, 86, 4, 0}, /* vsx scalar as integer store, xx1-form */
+
+	/* VSX move from VSR */
+	{AMFVSR, C_VSREG, C_NONE, C_NONE, C_REG, 88, 4, 0}, /* vsx move from vsr, xx1-form */
+
+	/* VSX move to VSR */
+	{AMTVSR, C_REG, C_NONE, C_NONE, C_VSREG, 88, 4, 0}, /* vsx move to vsr, xx1-form */
+
+	/* VSX logical */
+	{AXXLAND, C_VSREG, C_VSREG, C_NONE, C_VSREG, 90, 4, 0}, /* vsx and, xx3-form */
+	{AXXLOR, C_VSREG, C_VSREG, C_NONE, C_VSREG, 90, 4, 0},  /* vsx or, xx3-form */
+
+	/* VSX select */
+	{AXXSEL, C_VSREG, C_VSREG, C_VSREG, C_VSREG, 91, 4, 0}, /* vsx select, xx4-form */
+
+	/* VSX merge */
+	{AXXMRG, C_VSREG, C_VSREG, C_NONE, C_VSREG, 90, 4, 0}, /* vsx merge, xx3-form */
+
+	/* VSX splat */
+	{AXXSPLT, C_VSREG, C_NONE, C_SCON, C_VSREG, 89, 4, 0}, /* vsx splat, xx2-form */
+
+	/* VSX permute */
+	{AXXPERM, C_VSREG, C_VSREG, C_SCON, C_VSREG, 90, 4, 0}, /* vsx permute, xx3-form */
+
+	/* VSX shift */
+	{AXXSI, C_VSREG, C_VSREG, C_SCON, C_VSREG, 90, 4, 0}, /* vsx shift immediate, xx3-form */
+
+	/* VSX scalar FP-FP conversion */
+	{AXSCV, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx scalar fp-fp conversion, xx2-form */
+
+	/* VSX vector FP-FP conversion */
+	{AXVCV, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx vector fp-fp conversion, xx2-form */
+
+	/* VSX scalar FP-integer conversion */
+	{AXSCVX, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx scalar fp-integer conversion, xx2-form */
+
+	/* VSX scalar integer-FP conversion */
+	{AXSCVXP, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx scalar integer-fp conversion, xx2-form */
+
+	/* VSX vector FP-integer conversion */
+	{AXVCVX, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx vector fp-integer conversion, xx2-form */
+
+	/* VSX vector integer-FP conversion */
+	{AXVCVXP, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx vector integer-fp conversion, xx2-form */
+
 	/* 64-bit special registers */
 	{AMOVD, C_REG, C_NONE, C_NONE, C_SPR, 66, 4, 0},
 	{AMOVD, C_REG, C_NONE, C_NONE, C_LR, 66, 4, 0},
@@ -453,7 +586,7 @@ func span9(ctxt *obj.Link, cursym *obj.LSym) {
 	var q *obj.Prog
 	for bflag != 0 {
 		if ctxt.Debugvlog != 0 {
-			fmt.Fprintf(ctxt.Bso, "%5.2f span1\n", obj.Cputime())
+			ctxt.Logf("%5.2f span1\n", obj.Cputime())
 		}
 		bflag = 0
 		c = 0
@@ -499,7 +632,7 @@ func span9(ctxt *obj.Link, cursym *obj.LSym) {
 		cursym.Size = c
 	}
 
-	c += -c & (FuncAlign - 1)
+	c += -c & (funcAlign - 1)
 	cursym.Size = c
 
 	/*
@@ -546,6 +679,12 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
 		if REG_F0 <= a.Reg && a.Reg <= REG_F31 {
 			return C_FREG
 		}
+		if REG_V0 <= a.Reg && a.Reg <= REG_V31 {
+			return C_VREG
+		}
+		if REG_VS0 <= a.Reg && a.Reg <= REG_VS63 {
+			return C_VSREG
+		}
 		if REG_CR0 <= a.Reg && a.Reg <= REG_CR7 || a.Reg == REG_CR {
 			return C_CREG
 		}
@@ -754,7 +893,15 @@ func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
 	a4--
 	a2 := C_NONE
 	if p.Reg != 0 {
-		a2 = C_REG
+		if REG_R0 <= p.Reg && p.Reg <= REG_R31 {
+			a2 = C_REG
+		} else if REG_V0 <= p.Reg && p.Reg <= REG_V31 {
+			a2 = C_VREG
+		} else if REG_VS0 <= p.Reg && p.Reg <= REG_VS63 {
+			a2 = C_VSREG
+		} else if REG_F0 <= p.Reg && p.Reg <= REG_F31 {
+			a2 = C_FREG
+		}
 	}
 
 	//print("oplook %v %d %d %d %d\n", p, a1, a2, a3, a4);
@@ -770,7 +917,7 @@ func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
 		}
 	}
 
-	ctxt.Diag("illegal combination %v %v %v %v %v", obj.Aconv(p.As), DRconv(a1), DRconv(a2), DRconv(a3), DRconv(a4))
+	ctxt.Diag("illegal combination %v %v %v %v %v", p.As, DRconv(a1), DRconv(a2), DRconv(a3), DRconv(a4))
 	prasm(p)
 	if ops == nil {
 		ops = optab
@@ -919,8 +1066,8 @@ func buildop(ctxt *obj.Link) {
 
 		switch r {
 		default:
-			ctxt.Diag("unknown op in build: %v", obj.Aconv(r))
-			log.Fatalf("bad code")
+			ctxt.Diag("unknown op in build: %v", r)
+			log.Fatalf("instruction missing from switch in asm9.go:buildop: %v", r)
 
 		case ADCBF: /* unary indexed: op (b+a); op (b) */
 			opset(ADCBI, r0)
@@ -1003,6 +1150,10 @@ func buildop(ctxt *obj.Link) {
 			opset(AMULLDV, r0)
 			opset(ADIVD, r0)
 			opset(ADIVDCC, r0)
+			opset(ADIVDE, r0)
+			opset(ADIVDEU, r0)
+			opset(ADIVDECC, r0)
+			opset(ADIVDEUCC, r0)
 			opset(ADIVDVCC, r0)
 			opset(ADIVDV, r0)
 			opset(ADIVDU, r0)
@@ -1024,6 +1175,285 @@ func buildop(ctxt *obj.Link) {
 			opset(AMOVDU, r0)
 			opset(AMOVMW, r0)
 
+		case ALV: /* lvebx, lvehx, lvewx, lvx, lvxl, lvsl, lvsr */
+			opset(ALVEBX, r0)
+			opset(ALVEHX, r0)
+			opset(ALVEWX, r0)
+			opset(ALVX, r0)
+			opset(ALVXL, r0)
+			opset(ALVSL, r0)
+			opset(ALVSR, r0)
+
+		case ASTV: /* stvebx, stvehx, stvewx, stvx, stvxl */
+			opset(ASTVEBX, r0)
+			opset(ASTVEHX, r0)
+			opset(ASTVEWX, r0)
+			opset(ASTVX, r0)
+			opset(ASTVXL, r0)
+
+		case AVAND: /* vand, vandc, vnand */
+			opset(AVANDL, r0)
+			opset(AVANDC, r0)
+			opset(AVNAND, r0)
+
+		case AVOR: /* vor, vorc, vxor, vnor, veqv */
+			opset(AVORL, r0)
+			opset(AVORC, r0)
+			opset(AVXOR, r0)
+			opset(AVNOR, r0)
+			opset(AVEQV, r0)
+
+		case AVADDUM: /* vaddubm, vadduhm, vadduwm, vaddudm, vadduqm */
+			opset(AVADDUBM, r0)
+			opset(AVADDUHM, r0)
+			opset(AVADDUWM, r0)
+			opset(AVADDUDM, r0)
+			opset(AVADDUQM, r0)
+
+		case AVADDCU: /* vaddcuq, vaddcuw */
+			opset(AVADDCUQ, r0)
+			opset(AVADDCUW, r0)
+
+		case AVADDUS: /* vaddubs, vadduhs, vadduws */
+			opset(AVADDUBS, r0)
+			opset(AVADDUHS, r0)
+			opset(AVADDUWS, r0)
+
+		case AVADDSS: /* vaddsbs, vaddshs, vaddsws */
+			opset(AVADDSBS, r0)
+			opset(AVADDSHS, r0)
+			opset(AVADDSWS, r0)
+
+		case AVADDE: /* vaddeuqm, vaddecuq */
+			opset(AVADDEUQM, r0)
+			opset(AVADDECUQ, r0)
+
+		case AVSUBUM: /* vsububm, vsubuhm, vsubuwm, vsubudm, vsubuqm */
+			opset(AVSUBUBM, r0)
+			opset(AVSUBUHM, r0)
+			opset(AVSUBUWM, r0)
+			opset(AVSUBUDM, r0)
+			opset(AVSUBUQM, r0)
+
+		case AVSUBCU: /* vsubcuq, vsubcuw */
+			opset(AVSUBCUQ, r0)
+			opset(AVSUBCUW, r0)
+
+		case AVSUBUS: /* vsububs, vsubuhs, vsubuws */
+			opset(AVSUBUBS, r0)
+			opset(AVSUBUHS, r0)
+			opset(AVSUBUWS, r0)
+
+		case AVSUBSS: /* vsubsbs, vsubshs, vsubsws */
+			opset(AVSUBSBS, r0)
+			opset(AVSUBSHS, r0)
+			opset(AVSUBSWS, r0)
+
+		case AVSUBE: /* vsubeuqm, vsubecuq */
+			opset(AVSUBEUQM, r0)
+			opset(AVSUBECUQ, r0)
+
+		case AVR: /* vrlb, vrlh, vrlw, vrld */
+			opset(AVRLB, r0)
+			opset(AVRLH, r0)
+			opset(AVRLW, r0)
+			opset(AVRLD, r0)
+
+		case AVS: /* vs[l,r], vs[l,r]o, vs[l,r]b, vs[l,r]h, vs[l,r]w, vs[l,r]d */
+			opset(AVSLB, r0)
+			opset(AVSLH, r0)
+			opset(AVSLW, r0)
+			opset(AVSL, r0)
+			opset(AVSLO, r0)
+			opset(AVSRB, r0)
+			opset(AVSRH, r0)
+			opset(AVSRW, r0)
+			opset(AVSR, r0)
+			opset(AVSRO, r0)
+			opset(AVSLD, r0)
+			opset(AVSRD, r0)
+
+		case AVSA: /* vsrab, vsrah, vsraw, vsrad */
+			opset(AVSRAB, r0)
+			opset(AVSRAH, r0)
+			opset(AVSRAW, r0)
+			opset(AVSRAD, r0)
+
+		case AVSOI: /* vsldoi */
+			opset(AVSLDOI, r0)
+
+		case AVCLZ: /* vclzb, vclzh, vclzw, vclzd */
+			opset(AVCLZB, r0)
+			opset(AVCLZH, r0)
+			opset(AVCLZW, r0)
+			opset(AVCLZD, r0)
+
+		case AVPOPCNT: /* vpopcntb, vpopcnth, vpopcntw, vpopcntd */
+			opset(AVPOPCNTB, r0)
+			opset(AVPOPCNTH, r0)
+			opset(AVPOPCNTW, r0)
+			opset(AVPOPCNTD, r0)
+
+		case AVCMPEQ: /* vcmpequb[.], vcmpequh[.], vcmpequw[.], vcmpequd[.] */
+			opset(AVCMPEQUB, r0)
+			opset(AVCMPEQUBCC, r0)
+			opset(AVCMPEQUH, r0)
+			opset(AVCMPEQUHCC, r0)
+			opset(AVCMPEQUW, r0)
+			opset(AVCMPEQUWCC, r0)
+			opset(AVCMPEQUD, r0)
+			opset(AVCMPEQUDCC, r0)
+
+		case AVCMPGT: /* vcmpgt[u,s]b[.], vcmpgt[u,s]h[.], vcmpgt[u,s]w[.], vcmpgt[u,s]d[.] */
+			opset(AVCMPGTUB, r0)
+			opset(AVCMPGTUBCC, r0)
+			opset(AVCMPGTUH, r0)
+			opset(AVCMPGTUHCC, r0)
+			opset(AVCMPGTUW, r0)
+			opset(AVCMPGTUWCC, r0)
+			opset(AVCMPGTUD, r0)
+			opset(AVCMPGTUDCC, r0)
+			opset(AVCMPGTSB, r0)
+			opset(AVCMPGTSBCC, r0)
+			opset(AVCMPGTSH, r0)
+			opset(AVCMPGTSHCC, r0)
+			opset(AVCMPGTSW, r0)
+			opset(AVCMPGTSWCC, r0)
+			opset(AVCMPGTSD, r0)
+			opset(AVCMPGTSDCC, r0)
+
+		case AVPERM: /* vperm */
+			opset(AVPERM, r0)
+
+		case AVSEL: /* vsel */
+			opset(AVSEL, r0)
+
+		case AVSPLT: /* vspltb, vsplth, vspltw */
+			opset(AVSPLTB, r0)
+			opset(AVSPLTH, r0)
+			opset(AVSPLTW, r0)
+
+		case AVSPLTI: /* vspltisb, vspltish, vspltisw */
+			opset(AVSPLTISB, r0)
+			opset(AVSPLTISH, r0)
+			opset(AVSPLTISW, r0)
+
+		case AVCIPH: /* vcipher, vcipherlast */
+			opset(AVCIPHER, r0)
+			opset(AVCIPHERLAST, r0)
+
+		case AVNCIPH: /* vncipher, vncipherlast */
+			opset(AVNCIPHER, r0)
+			opset(AVNCIPHERLAST, r0)
+
+		case AVSBOX: /* vsbox */
+			opset(AVSBOX, r0)
+
+		case AVSHASIGMA: /* vshasigmaw, vshasigmad */
+			opset(AVSHASIGMAW, r0)
+			opset(AVSHASIGMAD, r0)
+
+		case ALXV: /* lxvd2x, lxvdsx, lxvw4x */
+			opset(ALXVD2X, r0)
+			opset(ALXVDSX, r0)
+			opset(ALXVW4X, r0)
+
+		case ASTXV: /* stxvd2x, stxvdsx, stxvw4x */
+			opset(ASTXVD2X, r0)
+			opset(ASTXVW4X, r0)
+
+		case ALXS: /* lxsdx  */
+			opset(ALXSDX, r0)
+
+		case ASTXS: /* stxsdx */
+			opset(ASTXSDX, r0)
+
+		case ALXSI: /* lxsiwax, lxsiwzx  */
+			opset(ALXSIWAX, r0)
+			opset(ALXSIWZX, r0)
+
+		case ASTXSI: /* stxsiwx */
+			opset(ASTXSIWX, r0)
+
+		case AMFVSR: /* mfvsrd, mfvsrwz */
+			opset(AMFVSRD, r0)
+			opset(AMFVSRWZ, r0)
+
+		case AMTVSR: /* mtvsrd, mtvsrwa, mtvsrwz */
+			opset(AMTVSRD, r0)
+			opset(AMTVSRWA, r0)
+			opset(AMTVSRWZ, r0)
+
+		case AXXLAND: /* xxland, xxlandc, xxleqv, xxlnand */
+			opset(AXXLANDQ, r0)
+			opset(AXXLANDC, r0)
+			opset(AXXLEQV, r0)
+			opset(AXXLNAND, r0)
+
+		case AXXLOR: /* xxlorc, xxlnor, xxlor, xxlxor */
+			opset(AXXLORC, r0)
+			opset(AXXLNOR, r0)
+			opset(AXXLORQ, r0)
+			opset(AXXLXOR, r0)
+
+		case AXXSEL: /* xxsel */
+			opset(AXXSEL, r0)
+
+		case AXXMRG: /* xxmrghw, xxmrglw */
+			opset(AXXMRGHW, r0)
+			opset(AXXMRGLW, r0)
+
+		case AXXSPLT: /* xxspltw */
+			opset(AXXSPLTW, r0)
+
+		case AXXPERM: /* xxpermdi */
+			opset(AXXPERMDI, r0)
+
+		case AXXSI: /* xxsldwi */
+			opset(AXXSLDWI, r0)
+
+		case AXSCV: /* xscvdpsp, xscvspdp, xscvdpspn, xscvspdpn */
+			opset(AXSCVDPSP, r0)
+			opset(AXSCVSPDP, r0)
+			opset(AXSCVDPSPN, r0)
+			opset(AXSCVSPDPN, r0)
+
+		case AXVCV: /* xvcvdpsp, xvcvspdp */
+			opset(AXVCVDPSP, r0)
+			opset(AXVCVSPDP, r0)
+
+		case AXSCVX: /* xscvdpsxds, xscvdpsxws, xscvdpuxds, xscvdpuxws */
+			opset(AXSCVDPSXDS, r0)
+			opset(AXSCVDPSXWS, r0)
+			opset(AXSCVDPUXDS, r0)
+			opset(AXSCVDPUXWS, r0)
+
+		case AXSCVXP: /* xscvsxddp, xscvuxddp, xscvsxdsp, xscvuxdsp */
+			opset(AXSCVSXDDP, r0)
+			opset(AXSCVUXDDP, r0)
+			opset(AXSCVSXDSP, r0)
+			opset(AXSCVUXDSP, r0)
+
+		case AXVCVX: /* xvcvdpsxds, xvcvdpsxws, xvcvdpuxds, xvcvdpuxws, xvcvspsxds, xvcvspsxws, xvcvspuxds, xvcvspuxws */
+			opset(AXVCVDPSXDS, r0)
+			opset(AXVCVDPSXWS, r0)
+			opset(AXVCVDPUXDS, r0)
+			opset(AXVCVDPUXWS, r0)
+			opset(AXVCVSPSXDS, r0)
+			opset(AXVCVSPSXWS, r0)
+			opset(AXVCVSPUXDS, r0)
+			opset(AXVCVSPUXWS, r0)
+
+		case AXVCVXP: /* xvcvsxddp, xvcvsxwdp, xvcvuxddp, xvcvuxwdp, xvcvsxdsp, xvcvsxwsp, xvcvuxdsp, xvcvuxwsp */
+			opset(AXVCVSXDDP, r0)
+			opset(AXVCVSXWDP, r0)
+			opset(AXVCVUXDDP, r0)
+			opset(AXVCVUXWDP, r0)
+			opset(AXVCVSXDSP, r0)
+			opset(AXVCVSXWSP, r0)
+			opset(AXVCVUXDSP, r0)
+			opset(AXVCVUXWSP, r0)
+
 		case AAND: /* logical op Rb,Rs,Ra; no literal */
 			opset(AANDN, r0)
 
@@ -1110,6 +1540,12 @@ func buildop(ctxt *obj.Link) {
 			opset(AFCFIDUCC, r0)
 			opset(AFRES, r0)
 			opset(AFRESCC, r0)
+			opset(AFRIM, r0)
+			opset(AFRIMCC, r0)
+			opset(AFRIP, r0)
+			opset(AFRIPCC, r0)
+			opset(AFRIZ, r0)
+			opset(AFRIZCC, r0)
 			opset(AFRSQRTE, r0)
 			opset(AFRSQRTECC, r0)
 			opset(AFSQRT, r0)
@@ -1157,6 +1593,9 @@ func buildop(ctxt *obj.Link) {
 		case AFCMPO:
 			opset(AFCMPU, r0)
 
+		case AISEL:
+			opset(AISEL, r0)
+
 		case AMTFSB0:
 			opset(AMTFSB0CC, r0)
 			opset(AMTFSB1, r0)
@@ -1214,6 +1653,8 @@ func buildop(ctxt *obj.Link) {
 
 		case ARLDMI:
 			opset(ARLDMICC, r0)
+			opset(ARLDIMI, r0)
+			opset(ARLDIMICC, r0)
 
 		case ARLDC:
 			opset(ARLDCCC, r0)
@@ -1223,6 +1664,11 @@ func buildop(ctxt *obj.Link) {
 			opset(ARLDCLCC, r0)
 			opset(ARLDCRCC, r0)
 
+		case ARLDICL:
+			opset(ARLDICLCC, r0)
+			opset(ARLDICR, r0)
+			opset(ARLDICRCC, r0)
+
 		case AFMOVD:
 			opset(AFMOVDCC, r0)
 			opset(AFMOVDU, r0)
@@ -1243,6 +1689,7 @@ func buildop(ctxt *obj.Link) {
 
 		case AMOVHBR:
 			opset(AMOVWBR, r0)
+			opset(AMOVDBR, r0)
 
 		case ASLBMFEE:
 			opset(ASLBMFEV, r0)
@@ -1265,6 +1712,8 @@ func buildop(ctxt *obj.Link) {
 
 		case AADD,
 			AANDCC, /* and. Rb,Rs,Ra; andi. $uimm,Rs,Ra; andis. $uimm,Rs,Ra */
+			AFMOVSX,
+			AFMOVSZ,
 			ALSW,
 			AMOVW,
 			/* load/store/move word with sign extension; special 32-bit move; move 32-bit literals */
@@ -1293,6 +1742,30 @@ func buildop(ctxt *obj.Link) {
 	}
 }
 
+func OPVXX1(o uint32, xo uint32, oe uint32) uint32 {
+	return o<<26 | xo<<1 | oe<<11
+}
+
+func OPVXX2(o uint32, xo uint32, oe uint32) uint32 {
+	return o<<26 | xo<<2 | oe<<11
+}
+
+func OPVXX3(o uint32, xo uint32, oe uint32) uint32 {
+	return o<<26 | xo<<3 | oe<<11
+}
+
+func OPVXX4(o uint32, xo uint32, oe uint32) uint32 {
+	return o<<26 | xo<<4 | oe<<11
+}
+
+func OPVX(o uint32, xo uint32, oe uint32, rc uint32) uint32 {
+	return o<<26 | xo | oe<<11 | rc&1
+}
+
+func OPVC(o uint32, xo uint32, oe uint32, rc uint32) uint32 {
+	return o<<26 | xo | oe<<11 | (rc&1)<<10
+}
+
 func OPVCC(o uint32, xo uint32, oe uint32, rc uint32) uint32 {
 	return o<<26 | xo<<1 | oe<<10 | rc&1
 }
@@ -1310,10 +1783,80 @@ func AOP_RRR(op uint32, d uint32, a uint32, b uint32) uint32 {
 	return op | (d&31)<<21 | (a&31)<<16 | (b&31)<<11
 }
 
+/* VX-form 2-register operands, r/r/none */
+func AOP_RR(op uint32, d uint32, a uint32) uint32 {
+	return op | (d&31)<<21 | (a&31)<<11
+}
+
+/* VA-form 4-register operands */
+func AOP_RRRR(op uint32, d uint32, a uint32, b uint32, c uint32) uint32 {
+	return op | (d&31)<<21 | (a&31)<<16 | (b&31)<<11 | (c&31)<<6
+}
+
 func AOP_IRR(op uint32, d uint32, a uint32, simm uint32) uint32 {
 	return op | (d&31)<<21 | (a&31)<<16 | simm&0xFFFF
 }
 
+/* VX-form 2-register + UIM operands */
+func AOP_VIRR(op uint32, d uint32, a uint32, simm uint32) uint32 {
+	return op | (d&31)<<21 | (simm&0xFFFF)<<16 | (a&31)<<11
+}
+
+/* VX-form 2-register + ST + SIX operands */
+func AOP_IIRR(op uint32, d uint32, a uint32, sbit uint32, simm uint32) uint32 {
+	return op | (d&31)<<21 | (a&31)<<16 | (sbit&1)<<15 | (simm&0xF)<<11
+}
+
+/* VA-form 3-register + SHB operands */
+func AOP_IRRR(op uint32, d uint32, a uint32, b uint32, simm uint32) uint32 {
+	return op | (d&31)<<21 | (a&31)<<16 | (b&31)<<11 | (simm&0xF)<<6
+}
+
+/* VX-form 1-register + SIM operands */
+func AOP_IR(op uint32, d uint32, simm uint32) uint32 {
+	return op | (d&31)<<21 | (simm&31)<<16
+}
+
+/* XX1-form 3-register operands, 1 VSR operand */
+func AOP_XX1(op uint32, d uint32, a uint32, b uint32) uint32 {
+	/* For the XX-form encodings, we need the VSX register number to be exactly */
+	/* between 0-63, so we can properly set the rightmost bits. */
+	r := d - REG_VS0
+	return op | (r&31)<<21 | (a&31)<<16 | (b&31)<<11 | (r&32)>>5
+}
+
+/* XX2-form 3-register operands, 2 VSR operands */
+func AOP_XX2(op uint32, d uint32, a uint32, b uint32) uint32 {
+	xt := d - REG_VS0
+	xb := b - REG_VS0
+	return op | (xt&31)<<21 | (a&3)<<16 | (xb&31)<<11 | (xb&32)>>4 | (xt&32)>>5
+}
+
+/* XX3-form 3 VSR operands */
+func AOP_XX3(op uint32, d uint32, a uint32, b uint32) uint32 {
+	xt := d - REG_VS0
+	xa := a - REG_VS0
+	xb := b - REG_VS0
+	return op | (xt&31)<<21 | (xa&31)<<16 | (xb&31)<<11 | (xa&32)>>3 | (xb&32)>>4 | (xt&32)>>5
+}
+
+/* XX3-form 3 VSR operands + immediate */
+func AOP_XX3I(op uint32, d uint32, a uint32, b uint32, c uint32) uint32 {
+	xt := d - REG_VS0
+	xa := a - REG_VS0
+	xb := b - REG_VS0
+	return op | (xt&31)<<21 | (xa&31)<<16 | (xb&31)<<11 | (c&3)<<8 | (xa&32)>>3 | (xb&32)>>4 | (xt&32)>>5
+}
+
+/* XX4-form, 4 VSR operands */
+func AOP_XX4(op uint32, d uint32, a uint32, b uint32, c uint32) uint32 {
+	xt := d - REG_VS0
+	xa := a - REG_VS0
+	xb := b - REG_VS0
+	xc := c - REG_VS0
+	return op | (xt&31)<<21 | (xa&31)<<16 | (xb&31)<<11 | (xc&31)<<6 | (xc&32)>>2 | (xa&32)>>3 | (xb&32)>>4 | (xt&32)>>5
+}
+
 func LOP_RRR(op uint32, a uint32, s uint32, b uint32) uint32 {
 	return op | (s&31)<<21 | (a&31)<<16 | (b&31)<<11
 }
@@ -1338,6 +1881,10 @@ func OP_RLW(op uint32, a uint32, s uint32, sh uint32, mb uint32, me uint32) uint
 	return op | (s&31)<<21 | (a&31)<<16 | (sh&31)<<11 | (mb&31)<<6 | (me&31)<<1
 }
 
+func AOP_ISEL(op uint32, t uint32, a uint32, b uint32, bc uint32) uint32 {
+	return op | (t&31)<<21 | (a&31)<<16 | (b&31)<<11 | (bc&0x1F)<<6
+}
+
 const (
 	/* each rhs is OPVCC(_, _, _, _) */
 	OP_ADD    = 31<<26 | 266<<1 | 0<<10 | 0
@@ -1347,6 +1894,7 @@ const (
 	OP_EXTSB  = 31<<26 | 954<<1 | 0<<10 | 0
 	OP_EXTSH  = 31<<26 | 922<<1 | 0<<10 | 0
 	OP_EXTSW  = 31<<26 | 986<<1 | 0<<10 | 0
+	OP_ISEL   = 31<<26 | 15<<1 | 0<<10 | 0
 	OP_MCRF   = 19<<26 | 0<<1 | 0<<10 | 0
 	OP_MCRFS  = 63<<26 | 64<<1 | 0<<10 | 0
 	OP_MCRXR  = 31<<26 | 512<<1 | 0<<10 | 0
@@ -1791,22 +2339,34 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 			r = int(p.To.Reg)
 		}
 		d := vregoff(ctxt, p.From3)
-		var mask [2]uint8
-		maskgen64(ctxt, p, mask[:], uint64(d))
 		var a int
 		switch p.As {
+
+		// These opcodes expect a mask operand that has to be converted into the
+		// appropriate operand.  The way these were defined, not all valid masks are possible.
+		// Left here for compatibility in case they were used or generated.
 		case ARLDCL, ARLDCLCC:
+			var mask [2]uint8
+			maskgen64(ctxt, p, mask[:], uint64(d))
+
 			a = int(mask[0]) /* MB */
 			if mask[1] != 63 {
 				ctxt.Diag("invalid mask for rotate: %x (end != bit 63)\n%v", uint64(d), p)
 			}
 
 		case ARLDCR, ARLDCRCC:
+			var mask [2]uint8
+			maskgen64(ctxt, p, mask[:], uint64(d))
+
 			a = int(mask[1]) /* ME */
 			if mask[0] != 0 {
 				ctxt.Diag("invalid mask for rotate: %x (start != 0)\n%v", uint64(d), p)
 			}
 
+		// These opcodes use a shift count like the ppc64 asm, no mask conversion done
+		case ARLDICR, ARLDICRCC, ARLDICL, ARLDICLCC:
+			a = int(d)
+
 		default:
 			ctxt.Diag("unexpected op in rldc case\n%v", p)
 			a = 0
@@ -1822,12 +2382,35 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		16: /* bc bo,bi,sbra */
 		a := 0
 
+		r := int(p.Reg)
+
 		if p.From.Type == obj.TYPE_CONST {
 			a = int(regoff(ctxt, &p.From))
-		}
-		r := int(p.Reg)
-		if r == 0 {
-			r = 0
+		} else if p.From.Type == obj.TYPE_REG {
+			if r != 0 {
+				ctxt.Diag("unexpected register setting for branch with CR: %d\n", r)
+			}
+			// BI values for the CR
+			switch p.From.Reg {
+			case REG_CR0:
+				r = BI_CR0
+			case REG_CR1:
+				r = BI_CR1
+			case REG_CR2:
+				r = BI_CR2
+			case REG_CR3:
+				r = BI_CR3
+			case REG_CR4:
+				r = BI_CR4
+			case REG_CR5:
+				r = BI_CR5
+			case REG_CR6:
+				r = BI_CR6
+			case REG_CR7:
+				r = BI_CR7
+			default:
+				ctxt.Diag("unrecognized register: expecting CR\n")
+			}
 		}
 		v := int32(0)
 		if p.Pcond != nil {
@@ -2064,18 +2647,32 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		v := regoff(ctxt, &p.From)
 
 		d := vregoff(ctxt, p.From3)
-		var mask [2]uint8
-		maskgen64(ctxt, p, mask[:], uint64(d))
-		if int32(mask[1]) != (63 - v) {
-			ctxt.Diag("invalid mask for shift: %x (shift %d)\n%v", uint64(d), v, p)
-		}
-		o1 = AOP_RRR(opirr(ctxt, p.As), uint32(p.Reg), uint32(p.To.Reg), (uint32(v) & 0x1F))
-		o1 |= (uint32(mask[0]) & 31) << 6
-		if v&0x20 != 0 {
-			o1 |= 1 << 1
-		}
-		if mask[0]&0x20 != 0 {
-			o1 |= 1 << 5 /* mb[5] is top bit */
+
+		// Original opcodes had mask operands which had to be converted to a shift count as expected by
+		// the ppc64 asm.
+		switch p.As {
+		case ARLDMI, ARLDMICC:
+			var mask [2]uint8
+			maskgen64(ctxt, p, mask[:], uint64(d))
+			if int32(mask[1]) != (63 - v) {
+				ctxt.Diag("invalid mask for shift: %x (shift %d)\n%v", uint64(d), v, p)
+			}
+			o1 = AOP_RRR(opirr(ctxt, p.As), uint32(p.Reg), uint32(p.To.Reg), (uint32(v) & 0x1F))
+			o1 |= (uint32(mask[0]) & 31) << 6
+			if v&0x20 != 0 {
+				o1 |= 1 << 1
+			}
+			if mask[0]&0x20 != 0 {
+				o1 |= 1 << 5 /* mb[5] is top bit */
+			}
+
+		// Opcodes with shift count operands.
+		case ARLDIMI, ARLDIMICC:
+			o1 = AOP_RRR(opirr(ctxt, p.As), uint32(p.Reg), uint32(p.To.Reg), (uint32(v) & 0x1F))
+			o1 |= (uint32(d) & 31) << 6
+			if v&0x20 != 0 {
+				o1 |= 1 << 1
+			}
 		}
 
 	case 31: /* dword */
@@ -2510,6 +3107,98 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 		rel.Siz = 8
 		rel.Sym = p.From.Sym
 		rel.Type = obj.R_ADDRPOWER_GOT
+	case 82: /* vector instructions, VX-form and VC-form */
+		if p.From.Type == obj.TYPE_REG {
+			/* reg reg none OR reg reg reg */
+			/* 3-register operand order: VRA, VRB, VRT */
+			/* 2-register operand order: VRA, VRT */
+			o1 = AOP_RRR(oprrr(ctxt, p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg))
+		} else if p.From3Type() == obj.TYPE_CONST {
+			/* imm imm reg reg */
+			/* operand order: SIX, VRA, ST, VRT */
+			six := int(regoff(ctxt, &p.From))
+			st := int(regoff(ctxt, p.From3))
+			o1 = AOP_IIRR(opiirr(ctxt, p.As), uint32(p.To.Reg), uint32(p.Reg), uint32(st), uint32(six))
+		} else if p.From3Type() == obj.TYPE_NONE && p.Reg != 0 {
+			/* imm reg reg */
+			/* operand order: UIM, VRB, VRT */
+			uim := int(regoff(ctxt, &p.From))
+			o1 = AOP_VIRR(opirr(ctxt, p.As), uint32(p.To.Reg), uint32(p.Reg), uint32(uim))
+		} else {
+			/* imm reg */
+			/* operand order: SIM, VRT */
+			sim := int(regoff(ctxt, &p.From))
+			o1 = AOP_IR(opirr(ctxt, p.As), uint32(p.To.Reg), uint32(sim))
+		}
+
+	case 83: /* vector instructions, VA-form */
+		if p.From.Type == obj.TYPE_REG {
+			/* reg reg reg reg */
+			/* 4-register operand order: VRA, VRB, VRC, VRT */
+			o1 = AOP_RRRR(oprrr(ctxt, p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg), uint32(p.From3.Reg))
+		} else if p.From.Type == obj.TYPE_CONST {
+			/* imm reg reg reg */
+			/* operand order: SHB, VRA, VRB, VRT */
+			shb := int(regoff(ctxt, &p.From))
+			o1 = AOP_IRRR(opirrr(ctxt, p.As), uint32(p.To.Reg), uint32(p.Reg), uint32(p.From3.Reg), uint32(shb))
+		}
+
+	case 84: // ISEL BC,RA,RB,RT -> isel rt,ra,rb,bc
+		bc := vregoff(ctxt, &p.From)
+
+		// rt = To.Reg, ra = p.Reg, rb = p.From3.Reg
+		o1 = AOP_ISEL(OP_ISEL, uint32(p.To.Reg), uint32(p.Reg), uint32(p.From3.Reg), uint32(bc))
+
+	case 85: /* vector instructions, VX-form */
+		/* reg none reg */
+		/* 2-register operand order: VRB, VRT */
+		o1 = AOP_RR(oprrr(ctxt, p.As), uint32(p.To.Reg), uint32(p.From.Reg))
+
+	case 86: /* VSX indexed store, XX1-form */
+		/* reg reg reg */
+		/* 3-register operand order: XT, (RB)(RA*1) */
+		o1 = AOP_XX1(opstorex(ctxt, p.As), uint32(p.From.Reg), uint32(p.To.Index), uint32(p.To.Reg))
+
+	case 87: /* VSX indexed load, XX1-form */
+		/* reg reg reg */
+		/* 3-register operand order: (RB)(RA*1), XT */
+		o1 = AOP_XX1(oploadx(ctxt, p.As), uint32(p.To.Reg), uint32(p.From.Index), uint32(p.From.Reg))
+
+	case 88: /* VSX instructions, XX1-form */
+		/* reg reg none OR reg reg reg */
+		/* 3-register operand order: RA, RB, XT */
+		/* 2-register operand order: XS, RA or RA, XT */
+		xt := int32(p.To.Reg)
+		xs := int32(p.From.Reg)
+		if REG_VS0 <= xt && xt <= REG_VS63 {
+			o1 = AOP_XX1(oprrr(ctxt, p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg))
+		} else if REG_VS0 <= xs && xs <= REG_VS63 {
+			o1 = AOP_XX1(oprrr(ctxt, p.As), uint32(p.From.Reg), uint32(p.To.Reg), uint32(p.Reg))
+		}
+
+	case 89: /* VSX instructions, XX2-form */
+		/* reg none reg OR reg imm reg */
+		/* 2-register operand order: XB, XT or XB, UIM, XT*/
+		uim := int(regoff(ctxt, p.From3))
+		o1 = AOP_XX2(oprrr(ctxt, p.As), uint32(p.To.Reg), uint32(uim), uint32(p.From.Reg))
+
+	case 90: /* VSX instructions, XX3-form */
+		if p.From3Type() == obj.TYPE_NONE {
+			/* reg reg reg */
+			/* 3-register operand order: XA, XB, XT */
+			o1 = AOP_XX3(oprrr(ctxt, p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg))
+		} else if p.From3Type() == obj.TYPE_CONST {
+			/* reg reg reg imm */
+			/* operand order: XA, XB, DM, XT */
+			dm := int(regoff(ctxt, p.From3))
+			o1 = AOP_XX3I(oprrr(ctxt, p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg), uint32(dm))
+		}
+
+	case 91: /* VSX instructions, XX4-form */
+		/* reg reg reg reg */
+		/* 3-register operand order: XA, XB, XC, XT */
+		o1 = AOP_XX4(oprrr(ctxt, p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg), uint32(p.From3.Reg))
+
 	}
 
 	out[0] = o1
@@ -2662,6 +3351,18 @@ func oprrr(ctxt *obj.Link, a obj.As) uint32 {
 	case AREMDCC, ADIVDCC:
 		return OPVCC(31, 489, 0, 1)
 
+	case ADIVDE:
+		return OPVCC(31, 425, 0, 0)
+
+	case ADIVDECC:
+		return OPVCC(31, 425, 0, 1)
+
+	case ADIVDEU:
+		return OPVCC(31, 393, 0, 0)
+
+	case ADIVDEUCC:
+		return OPVCC(31, 393, 0, 1)
+
 	case AREMDV, ADIVDV:
 		return OPVCC(31, 489, 1, 0)
 
@@ -2806,6 +3507,18 @@ func oprrr(ctxt *obj.Link, a obj.As) uint32 {
 		return OPVCC(59, 24, 0, 0)
 	case AFRESCC:
 		return OPVCC(59, 24, 0, 1)
+	case AFRIM:
+		return OPVCC(63, 488, 0, 0)
+	case AFRIMCC:
+		return OPVCC(63, 488, 0, 1)
+	case AFRIP:
+		return OPVCC(63, 456, 0, 0)
+	case AFRIPCC:
+		return OPVCC(63, 456, 0, 1)
+	case AFRIZ:
+		return OPVCC(63, 424, 0, 0)
+	case AFRIZCC:
+		return OPVCC(63, 424, 0, 1)
 	case AFRSP:
 		return OPVCC(63, 12, 0, 0)
 	case AFRSPCC:
@@ -2931,6 +3644,15 @@ func oprrr(ctxt *obj.Link, a obj.As) uint32 {
 	case ARLDCR:
 		return OPVCC(30, 9, 0, 0)
 
+	case ARLDICL:
+		return OPVCC(30, 0, 0, 0)
+	case ARLDICLCC:
+		return OPVCC(30, 0, 0, 1)
+	case ARLDICR:
+		return OPVCC(30, 0, 0, 0) | 2<<1 // rldicr
+	case ARLDICRCC:
+		return OPVCC(30, 0, 0, 1) | 2<<1 // rldicr.
+
 	case ASYSCALL:
 		return OPVCC(17, 1, 0, 0)
 
@@ -3032,13 +3754,376 @@ func oprrr(ctxt *obj.Link, a obj.As) uint32 {
 	case ATD:
 		return OPVCC(31, 68, 0, 0)
 
+	/* Vector (VMX/Altivec) instructions */
+	/* ISA 2.03 enables these for PPC970. For POWERx processors, these */
+	/* are enabled starting at POWER6 (ISA 2.05). */
+	case AVANDL:
+		return OPVX(4, 1028, 0, 0) /* vand - v2.03 */
+	case AVANDC:
+		return OPVX(4, 1092, 0, 0) /* vandc - v2.03 */
+	case AVNAND:
+		return OPVX(4, 1412, 0, 0) /* vnand - v2.07 */
+
+	case AVORL:
+		return OPVX(4, 1156, 0, 0) /* vor - v2.03 */
+	case AVORC:
+		return OPVX(4, 1348, 0, 0) /* vorc - v2.07 */
+	case AVNOR:
+		return OPVX(4, 1284, 0, 0) /* vnor - v2.03 */
+	case AVXOR:
+		return OPVX(4, 1220, 0, 0) /* vxor - v2.03 */
+	case AVEQV:
+		return OPVX(4, 1668, 0, 0) /* veqv - v2.07 */
+
+	case AVADDUBM:
+		return OPVX(4, 0, 0, 0) /* vaddubm - v2.03 */
+	case AVADDUHM:
+		return OPVX(4, 64, 0, 0) /* vadduhm - v2.03 */
+	case AVADDUWM:
+		return OPVX(4, 128, 0, 0) /* vadduwm - v2.03 */
+	case AVADDUDM:
+		return OPVX(4, 192, 0, 0) /* vaddudm - v2.07 */
+	case AVADDUQM:
+		return OPVX(4, 256, 0, 0) /* vadduqm - v2.07 */
+
+	case AVADDCUQ:
+		return OPVX(4, 320, 0, 0) /* vaddcuq - v2.07 */
+	case AVADDCUW:
+		return OPVX(4, 384, 0, 0) /* vaddcuw - v2.03 */
+
+	case AVADDUBS:
+		return OPVX(4, 512, 0, 0) /* vaddubs - v2.03 */
+	case AVADDUHS:
+		return OPVX(4, 576, 0, 0) /* vadduhs - v2.03 */
+	case AVADDUWS:
+		return OPVX(4, 640, 0, 0) /* vadduws - v2.03 */
+
+	case AVADDSBS:
+		return OPVX(4, 768, 0, 0) /* vaddsbs - v2.03 */
+	case AVADDSHS:
+		return OPVX(4, 832, 0, 0) /* vaddshs - v2.03 */
+	case AVADDSWS:
+		return OPVX(4, 896, 0, 0) /* vaddsws - v2.03 */
+
+	case AVADDEUQM:
+		return OPVX(4, 60, 0, 0) /* vaddeuqm - v2.07 */
+	case AVADDECUQ:
+		return OPVX(4, 61, 0, 0) /* vaddecuq - v2.07 */
+
+	case AVSUBUBM:
+		return OPVX(4, 1024, 0, 0) /* vsububm - v2.03 */
+	case AVSUBUHM:
+		return OPVX(4, 1088, 0, 0) /* vsubuhm - v2.03 */
+	case AVSUBUWM:
+		return OPVX(4, 1152, 0, 0) /* vsubuwm - v2.03 */
+	case AVSUBUDM:
+		return OPVX(4, 1216, 0, 0) /* vsubudm - v2.07 */
+	case AVSUBUQM:
+		return OPVX(4, 1280, 0, 0) /* vsubuqm - v2.07 */
+
+	case AVSUBCUQ:
+		return OPVX(4, 1344, 0, 0) /* vsubcuq - v2.07 */
+	case AVSUBCUW:
+		return OPVX(4, 1408, 0, 0) /* vsubcuw - v2.03 */
+
+	case AVSUBUBS:
+		return OPVX(4, 1536, 0, 0) /* vsububs - v2.03 */
+	case AVSUBUHS:
+		return OPVX(4, 1600, 0, 0) /* vsubuhs - v2.03 */
+	case AVSUBUWS:
+		return OPVX(4, 1664, 0, 0) /* vsubuws - v2.03 */
+
+	case AVSUBSBS:
+		return OPVX(4, 1792, 0, 0) /* vsubsbs - v2.03 */
+	case AVSUBSHS:
+		return OPVX(4, 1856, 0, 0) /* vsubshs - v2.03 */
+	case AVSUBSWS:
+		return OPVX(4, 1920, 0, 0) /* vsubsws - v2.03 */
+
+	case AVSUBEUQM:
+		return OPVX(4, 62, 0, 0) /* vsubeuqm - v2.07 */
+	case AVSUBECUQ:
+		return OPVX(4, 63, 0, 0) /* vsubecuq - v2.07 */
+
+	case AVRLB:
+		return OPVX(4, 4, 0, 0) /* vrlb - v2.03 */
+	case AVRLH:
+		return OPVX(4, 68, 0, 0) /* vrlh - v2.03 */
+	case AVRLW:
+		return OPVX(4, 132, 0, 0) /* vrlw - v2.03 */
+	case AVRLD:
+		return OPVX(4, 196, 0, 0) /* vrld - v2.07 */
+
+	case AVSLB:
+		return OPVX(4, 260, 0, 0) /* vslh - v2.03 */
+	case AVSLH:
+		return OPVX(4, 324, 0, 0) /* vslh - v2.03 */
+	case AVSLW:
+		return OPVX(4, 388, 0, 0) /* vslw - v2.03 */
+	case AVSL:
+		return OPVX(4, 452, 0, 0) /* vsl - v2.03 */
+	case AVSLO:
+		return OPVX(4, 1036, 0, 0) /* vsl - v2.03 */
+	case AVSRB:
+		return OPVX(4, 516, 0, 0) /* vsrb - v2.03 */
+	case AVSRH:
+		return OPVX(4, 580, 0, 0) /* vsrh - v2.03 */
+	case AVSRW:
+		return OPVX(4, 644, 0, 0) /* vsrw - v2.03 */
+	case AVSR:
+		return OPVX(4, 708, 0, 0) /* vsr - v2.03 */
+	case AVSRO:
+		return OPVX(4, 1100, 0, 0) /* vsro - v2.03 */
+	case AVSLD:
+		return OPVX(4, 1476, 0, 0) /* vsld - v2.07 */
+	case AVSRD:
+		return OPVX(4, 1732, 0, 0) /* vsrd - v2.07 */
+
+	case AVSRAB:
+		return OPVX(4, 772, 0, 0) /* vsrab - v2.03 */
+	case AVSRAH:
+		return OPVX(4, 836, 0, 0) /* vsrah - v2.03 */
+	case AVSRAW:
+		return OPVX(4, 900, 0, 0) /* vsraw - v2.03 */
+	case AVSRAD:
+		return OPVX(4, 964, 0, 0) /* vsrad - v2.07 */
+
+	case AVCLZB:
+		return OPVX(4, 1794, 0, 0) /* vclzb - v2.07 */
+	case AVCLZH:
+		return OPVX(4, 1858, 0, 0) /* vclzh - v2.07 */
+	case AVCLZW:
+		return OPVX(4, 1922, 0, 0) /* vclzw - v2.07 */
+	case AVCLZD:
+		return OPVX(4, 1986, 0, 0) /* vclzd - v2.07 */
+
+	case AVPOPCNTB:
+		return OPVX(4, 1795, 0, 0) /* vpopcntb - v2.07 */
+	case AVPOPCNTH:
+		return OPVX(4, 1859, 0, 0) /* vpopcnth - v2.07 */
+	case AVPOPCNTW:
+		return OPVX(4, 1923, 0, 0) /* vpopcntw - v2.07 */
+	case AVPOPCNTD:
+		return OPVX(4, 1987, 0, 0) /* vpopcntd - v2.07 */
+
+	case AVCMPEQUB:
+		return OPVC(4, 6, 0, 0) /* vcmpequb - v2.03 */
+	case AVCMPEQUBCC:
+		return OPVC(4, 6, 0, 1) /* vcmpequb. - v2.03 */
+	case AVCMPEQUH:
+		return OPVC(4, 70, 0, 0) /* vcmpequh - v2.03 */
+	case AVCMPEQUHCC:
+		return OPVC(4, 70, 0, 1) /* vcmpequh. - v2.03 */
+	case AVCMPEQUW:
+		return OPVC(4, 134, 0, 0) /* vcmpequw - v2.03 */
+	case AVCMPEQUWCC:
+		return OPVC(4, 134, 0, 1) /* vcmpequw. - v2.03 */
+	case AVCMPEQUD:
+		return OPVC(4, 199, 0, 0) /* vcmpequd - v2.07 */
+	case AVCMPEQUDCC:
+		return OPVC(4, 199, 0, 1) /* vcmpequd. - v2.07 */
+
+	case AVCMPGTUB:
+		return OPVC(4, 518, 0, 0) /* vcmpgtub - v2.03 */
+	case AVCMPGTUBCC:
+		return OPVC(4, 518, 0, 1) /* vcmpgtub. - v2.03 */
+	case AVCMPGTUH:
+		return OPVC(4, 582, 0, 0) /* vcmpgtuh - v2.03 */
+	case AVCMPGTUHCC:
+		return OPVC(4, 582, 0, 1) /* vcmpgtuh. - v2.03 */
+	case AVCMPGTUW:
+		return OPVC(4, 646, 0, 0) /* vcmpgtuw - v2.03 */
+	case AVCMPGTUWCC:
+		return OPVC(4, 646, 0, 1) /* vcmpgtuw. - v2.03 */
+	case AVCMPGTUD:
+		return OPVC(4, 711, 0, 0) /* vcmpgtud - v2.07 */
+	case AVCMPGTUDCC:
+		return OPVC(4, 711, 0, 1) /* vcmpgtud. v2.07 */
+	case AVCMPGTSB:
+		return OPVC(4, 774, 0, 0) /* vcmpgtsb - v2.03 */
+	case AVCMPGTSBCC:
+		return OPVC(4, 774, 0, 1) /* vcmpgtsb. - v2.03 */
+	case AVCMPGTSH:
+		return OPVC(4, 838, 0, 0) /* vcmpgtsh - v2.03 */
+	case AVCMPGTSHCC:
+		return OPVC(4, 838, 0, 1) /* vcmpgtsh. - v2.03 */
+	case AVCMPGTSW:
+		return OPVC(4, 902, 0, 0) /* vcmpgtsw - v2.03 */
+	case AVCMPGTSWCC:
+		return OPVC(4, 902, 0, 1) /* vcmpgtsw. - v2.03 */
+	case AVCMPGTSD:
+		return OPVC(4, 967, 0, 0) /* vcmpgtsd - v2.07 */
+	case AVCMPGTSDCC:
+		return OPVC(4, 967, 0, 1) /* vcmpgtsd. - v2.07 */
+
+	case AVPERM:
+		return OPVX(4, 43, 0, 0) /* vperm - v2.03 */
+
+	case AVSEL:
+		return OPVX(4, 42, 0, 0) /* vsel - v2.03 */
+
+	case AVCIPHER:
+		return OPVX(4, 1288, 0, 0) /* vcipher - v2.07 */
+	case AVCIPHERLAST:
+		return OPVX(4, 1289, 0, 0) /* vcipherlast - v2.07 */
+	case AVNCIPHER:
+		return OPVX(4, 1352, 0, 0) /* vncipher - v2.07 */
+	case AVNCIPHERLAST:
+		return OPVX(4, 1353, 0, 0) /* vncipherlast - v2.07 */
+	case AVSBOX:
+		return OPVX(4, 1480, 0, 0) /* vsbox - v2.07 */
+	/* End of vector instructions */
+
+	/* Vector scalar (VSX) instructions */
+	/* ISA 2.06 enables these for POWER7. */
+	case AMFVSRD:
+		return OPVXX1(31, 51, 0) /* mfvsrd - v2.07 */
+	case AMFVSRWZ:
+		return OPVXX1(31, 115, 0) /* mfvsrwz - v2.07 */
+
+	case AMTVSRD:
+		return OPVXX1(31, 179, 0) /* mtvsrd - v2.07 */
+	case AMTVSRWA:
+		return OPVXX1(31, 211, 0) /* mtvsrwa - v2.07 */
+	case AMTVSRWZ:
+		return OPVXX1(31, 243, 0) /* mtvsrwz - v2.07 */
+
+	case AXXLANDQ:
+		return OPVXX3(60, 130, 0) /* xxland - v2.06 */
+	case AXXLANDC:
+		return OPVXX3(60, 138, 0) /* xxlandc - v2.06 */
+	case AXXLEQV:
+		return OPVXX3(60, 186, 0) /* xxleqv - v2.07 */
+	case AXXLNAND:
+		return OPVXX3(60, 178, 0) /* xxlnand - v2.07 */
+
+	case AXXLORC:
+		return OPVXX3(60, 170, 0) /* xxlorc - v2.07 */
+	case AXXLNOR:
+		return OPVXX3(60, 162, 0) /* xxlnor - v2.06 */
+	case AXXLORQ:
+		return OPVXX3(60, 146, 0) /* xxlor - v2.06 */
+	case AXXLXOR:
+		return OPVXX3(60, 154, 0) /* xxlxor - v2.06 */
+
+	case AXXSEL:
+		return OPVXX4(60, 3, 0) /* xxsel - v2.06 */
+
+	case AXXMRGHW:
+		return OPVXX3(60, 18, 0) /* xxmrghw - v2.06 */
+	case AXXMRGLW:
+		return OPVXX3(60, 50, 0) /* xxmrglw - v2.06 */
+
+	case AXXSPLTW:
+		return OPVXX2(60, 164, 0) /* xxspltw - v2.06 */
+
+	case AXXPERMDI:
+		return OPVXX3(60, 10, 0) /* xxpermdi - v2.06 */
+
+	case AXXSLDWI:
+		return OPVXX3(60, 2, 0) /* xxsldwi - v2.06 */
+
+	case AXSCVDPSP:
+		return OPVXX2(60, 265, 0) /* xscvdpsp - v2.06 */
+	case AXSCVSPDP:
+		return OPVXX2(60, 329, 0) /* xscvspdp - v2.06 */
+	case AXSCVDPSPN:
+		return OPVXX2(60, 267, 0) /* xscvdpspn - v2.07 */
+	case AXSCVSPDPN:
+		return OPVXX2(60, 331, 0) /* xscvspdpn - v2.07 */
+
+	case AXVCVDPSP:
+		return OPVXX2(60, 393, 0) /* xvcvdpsp - v2.06 */
+	case AXVCVSPDP:
+		return OPVXX2(60, 457, 0) /* xvcvspdp - v2.06 */
+
+	case AXSCVDPSXDS:
+		return OPVXX2(60, 344, 0) /* xscvdpsxds - v2.06 */
+	case AXSCVDPSXWS:
+		return OPVXX2(60, 88, 0) /* xscvdpsxws - v2.06 */
+	case AXSCVDPUXDS:
+		return OPVXX2(60, 328, 0) /* xscvdpuxds - v2.06 */
+	case AXSCVDPUXWS:
+		return OPVXX2(60, 72, 0) /* xscvdpuxws - v2.06 */
+
+	case AXSCVSXDDP:
+		return OPVXX2(60, 376, 0) /* xscvsxddp - v2.06 */
+	case AXSCVUXDDP:
+		return OPVXX2(60, 360, 0) /* xscvuxddp - v2.06 */
+	case AXSCVSXDSP:
+		return OPVXX2(60, 312, 0) /* xscvsxdsp - v2.06 */
+	case AXSCVUXDSP:
+		return OPVXX2(60, 296, 0) /* xscvuxdsp - v2.06 */
+
+	case AXVCVDPSXDS:
+		return OPVXX2(60, 472, 0) /* xvcvdpsxds - v2.06 */
+	case AXVCVDPSXWS:
+		return OPVXX2(60, 216, 0) /* xvcvdpsxws - v2.06 */
+	case AXVCVDPUXDS:
+		return OPVXX2(60, 456, 0) /* xvcvdpuxds - v2.06 */
+	case AXVCVDPUXWS:
+		return OPVXX2(60, 200, 0) /* xvcvdpuxws - v2.06 */
+	case AXVCVSPSXDS:
+		return OPVXX2(60, 408, 0) /* xvcvspsxds - v2.07 */
+	case AXVCVSPSXWS:
+		return OPVXX2(60, 152, 0) /* xvcvspsxws - v2.07 */
+	case AXVCVSPUXDS:
+		return OPVXX2(60, 392, 0) /* xvcvspuxds - v2.07 */
+	case AXVCVSPUXWS:
+		return OPVXX2(60, 136, 0) /* xvcvspuxws - v2.07 */
+
+	case AXVCVSXDDP:
+		return OPVXX2(60, 504, 0) /* xvcvsxddp - v2.06 */
+	case AXVCVSXWDP:
+		return OPVXX2(60, 248, 0) /* xvcvsxwdp - v2.06 */
+	case AXVCVUXDDP:
+		return OPVXX2(60, 488, 0) /* xvcvuxddp - v2.06 */
+	case AXVCVUXWDP:
+		return OPVXX2(60, 232, 0) /* xvcvuxwdp - v2.06 */
+	case AXVCVSXDSP:
+		return OPVXX2(60, 440, 0) /* xvcvsxdsp - v2.06 */
+	case AXVCVSXWSP:
+		return OPVXX2(60, 184, 0) /* xvcvsxwsp - v2.06 */
+	case AXVCVUXDSP:
+		return OPVXX2(60, 424, 0) /* xvcvuxdsp - v2.06 */
+	case AXVCVUXWSP:
+		return OPVXX2(60, 168, 0) /* xvcvuxwsp - v2.06 */
+	/* End of VSX instructions */
+
 	case AXOR:
 		return OPVCC(31, 316, 0, 0)
 	case AXORCC:
 		return OPVCC(31, 316, 0, 1)
 	}
 
-	ctxt.Diag("bad r/r opcode %v", obj.Aconv(a))
+	ctxt.Diag("bad r/r, r/r/r or r/r/r/r opcode %v", a)
+	return 0
+}
+
+func opirrr(ctxt *obj.Link, a obj.As) uint32 {
+	switch a {
+	/* Vector (VMX/Altivec) instructions */
+	/* ISA 2.03 enables these for PPC970. For POWERx processors, these */
+	/* are enabled starting at POWER6 (ISA 2.05). */
+	case AVSLDOI:
+		return OPVX(4, 44, 0, 0) /* vsldoi - v2.03 */
+	}
+
+	ctxt.Diag("bad i/r/r/r opcode %v", a)
+	return 0
+}
+
+func opiirr(ctxt *obj.Link, a obj.As) uint32 {
+	switch a {
+	/* Vector (VMX/Altivec) instructions */
+	/* ISA 2.07 enables these for POWER8 and beyond. */
+	case AVSHASIGMAW:
+		return OPVX(4, 1666, 0, 0) /* vshasigmaw - v2.07 */
+	case AVSHASIGMAD:
+		return OPVX(4, 1730, 0, 0) /* vshasigmad - v2.07 */
+	}
+
+	ctxt.Diag("bad i/i/r/r opcode %v", a)
 	return 0
 }
 
@@ -3084,9 +4169,9 @@ func opirr(ctxt *obj.Link, a obj.As) uint32 {
 	case ABNE:
 		return AOP_RRR(16<<26, 4, 2, 0)
 	case ABVC:
-		return AOP_RRR(16<<26, 4, 3, 0)
+		return AOP_RRR(16<<26, 4, 3, 0) // apparently unordered-clear
 	case ABVS:
-		return AOP_RRR(16<<26, 12, 3, 0)
+		return AOP_RRR(16<<26, 12, 3, 0) // apparently unordered-set
 
 	case ACMP:
 		return OPVCC(11, 0, 0, 0) | 1<<21 /* L=1 */
@@ -3115,7 +4200,10 @@ func opirr(ctxt *obj.Link, a obj.As) uint32 {
 		return OPVCC(30, 0, 0, 0) | 3<<2 /* rldimi */
 	case ARLDMICC:
 		return OPVCC(30, 0, 0, 1) | 3<<2
-
+	case ARLDIMI:
+		return OPVCC(30, 0, 0, 0) | 3<<2 /* rldimi */
+	case ARLDIMICC:
+		return OPVCC(30, 0, 0, 1) | 3<<2
 	case ARLWNM:
 		return OPVCC(21, 0, 0, 0) /* rlwinm */
 	case ARLWNMCC:
@@ -3154,13 +4242,31 @@ func opirr(ctxt *obj.Link, a obj.As) uint32 {
 	case ATD:
 		return OPVCC(2, 0, 0, 0)
 
+	/* Vector (VMX/Altivec) instructions */
+	/* ISA 2.03 enables these for PPC970. For POWERx processors, these */
+	/* are enabled starting at POWER6 (ISA 2.05). */
+	case AVSPLTB:
+		return OPVX(4, 524, 0, 0) /* vspltb - v2.03 */
+	case AVSPLTH:
+		return OPVX(4, 588, 0, 0) /* vsplth - v2.03 */
+	case AVSPLTW:
+		return OPVX(4, 652, 0, 0) /* vspltw - v2.03 */
+
+	case AVSPLTISB:
+		return OPVX(4, 780, 0, 0) /* vspltisb - v2.03 */
+	case AVSPLTISH:
+		return OPVX(4, 844, 0, 0) /* vspltish - v2.03 */
+	case AVSPLTISW:
+		return OPVX(4, 908, 0, 0) /* vspltisw - v2.03 */
+	/* End of vector instructions */
+
 	case AXOR:
 		return OPVCC(26, 0, 0, 0) /* XORIL */
 	case -AXOR:
 		return OPVCC(27, 0, 0, 0) /* XORIU */
 	}
 
-	ctxt.Diag("bad opcode i/r %v", obj.Aconv(a))
+	ctxt.Diag("bad opcode i/r or i/r/r %v", a)
 	return 0
 }
 
@@ -3207,7 +4313,7 @@ func opload(ctxt *obj.Link, a obj.As) uint32 {
 		return OPVCC(46, 0, 0, 0) /* lmw */
 	}
 
-	ctxt.Diag("bad load opcode %v", obj.Aconv(a))
+	ctxt.Diag("bad load opcode %v", a)
 	return 0
 }
 
@@ -3238,6 +4344,10 @@ func oploadx(ctxt *obj.Link, a obj.As) uint32 {
 		return OPVCC(31, 535, 0, 0) /* lfsx */
 	case AFMOVSU:
 		return OPVCC(31, 567, 0, 0) /* lfsux */
+	case AFMOVSX:
+		return OPVCC(31, 855, 0, 0) /* lfiwax - power6, isa 2.05 */
+	case AFMOVSZ:
+		return OPVCC(31, 887, 0, 0) /* lfiwzx - power7, isa 2.06 */
 	case AMOVH:
 		return OPVCC(31, 343, 0, 0) /* lhax */
 	case AMOVHU:
@@ -3246,6 +4356,8 @@ func oploadx(ctxt *obj.Link, a obj.As) uint32 {
 		return OPVCC(31, 790, 0, 0) /* lhbrx */
 	case AMOVWBR:
 		return OPVCC(31, 534, 0, 0) /* lwbrx */
+	case AMOVDBR:
+		return OPVCC(31, 532, 0, 0) /* ldbrx */
 	case AMOVHZ:
 		return OPVCC(31, 279, 0, 0) /* lhzx */
 	case AMOVHZU:
@@ -3264,9 +4376,47 @@ func oploadx(ctxt *obj.Link, a obj.As) uint32 {
 		return OPVCC(31, 21, 0, 0) /* ldx */
 	case AMOVDU:
 		return OPVCC(31, 53, 0, 0) /* ldux */
+
+	/* Vector (VMX/Altivec) instructions */
+	/* ISA 2.03 enables these for PPC970. For POWERx processors, these */
+	/* are enabled starting at POWER6 (ISA 2.05). */
+	case ALVEBX:
+		return OPVCC(31, 7, 0, 0) /* lvebx - v2.03 */
+	case ALVEHX:
+		return OPVCC(31, 39, 0, 0) /* lvehx - v2.03 */
+	case ALVEWX:
+		return OPVCC(31, 71, 0, 0) /* lvewx - v2.03 */
+	case ALVX:
+		return OPVCC(31, 103, 0, 0) /* lvx - v2.03 */
+	case ALVXL:
+		return OPVCC(31, 359, 0, 0) /* lvxl - v2.03 */
+	case ALVSL:
+		return OPVCC(31, 6, 0, 0) /* lvsl - v2.03 */
+	case ALVSR:
+		return OPVCC(31, 38, 0, 0) /* lvsr - v2.03 */
+		/* End of vector instructions */
+
+	/* Vector scalar (VSX) instructions */
+	/* ISA 2.06 enables these for POWER7. */
+	case ALXVD2X:
+		return OPVXX1(31, 844, 0) /* lxvd2x - v2.06 */
+	case ALXVDSX:
+		return OPVXX1(31, 332, 0) /* lxvdsx - v2.06 */
+	case ALXVW4X:
+		return OPVXX1(31, 780, 0) /* lxvw4x - v2.06 */
+
+	case ALXSDX:
+		return OPVXX1(31, 588, 0) /* lxsdx - v2.06 */
+
+	case ALXSIWAX:
+		return OPVXX1(31, 76, 0) /* lxsiwax - v2.07 */
+	case ALXSIWZX:
+		return OPVXX1(31, 12, 0) /* lxsiwzx - v2.07 */
+		/* End of vector scalar instructions */
+
 	}
 
-	ctxt.Diag("bad loadx opcode %v", obj.Aconv(a))
+	ctxt.Diag("bad loadx opcode %v", a)
 	return 0
 }
 
@@ -3310,7 +4460,7 @@ func opstore(ctxt *obj.Link, a obj.As) uint32 {
 		return OPVCC(62, 0, 0, 1) /* stdu */
 	}
 
-	ctxt.Diag("unknown store opcode %v", obj.Aconv(a))
+	ctxt.Diag("unknown store opcode %v", a)
 	return 0
 }
 
@@ -3332,6 +4482,8 @@ func opstorex(ctxt *obj.Link, a obj.As) uint32 {
 		return OPVCC(31, 663, 0, 0) /* stfsx */
 	case AFMOVSU:
 		return OPVCC(31, 695, 0, 0) /* stfsux */
+	case AFMOVSX:
+		return OPVCC(31, 983, 0, 0) /* stfiwx */
 
 	case AMOVHZ, AMOVH:
 		return OPVCC(31, 407, 0, 0) /* sthx */
@@ -3362,8 +4514,38 @@ func opstorex(ctxt *obj.Link, a obj.As) uint32 {
 		return OPVCC(31, 149, 0, 0) /* stdx */
 	case AMOVDU:
 		return OPVCC(31, 181, 0, 0) /* stdux */
+
+	/* Vector (VMX/Altivec) instructions */
+	/* ISA 2.03 enables these for PPC970. For POWERx processors, these */
+	/* are enabled starting at POWER6 (ISA 2.05). */
+	case ASTVEBX:
+		return OPVCC(31, 135, 0, 0) /* stvebx - v2.03 */
+	case ASTVEHX:
+		return OPVCC(31, 167, 0, 0) /* stvehx - v2.03 */
+	case ASTVEWX:
+		return OPVCC(31, 199, 0, 0) /* stvewx - v2.03 */
+	case ASTVX:
+		return OPVCC(31, 231, 0, 0) /* stvx - v2.03 */
+	case ASTVXL:
+		return OPVCC(31, 487, 0, 0) /* stvxl - v2.03 */
+		/* End of vector instructions */
+
+	/* Vector scalar (VSX) instructions */
+	/* ISA 2.06 enables these for POWER7. */
+	case ASTXVD2X:
+		return OPVXX1(31, 972, 0) /* stxvd2x - v2.06 */
+	case ASTXVW4X:
+		return OPVXX1(31, 908, 0) /* stxvw4x - v2.06 */
+
+	case ASTXSDX:
+		return OPVXX1(31, 716, 0) /* stxsdx - v2.06 */
+
+	case ASTXSIWX:
+		return OPVXX1(31, 140, 0) /* stxsiwx - v2.07 */
+		/* End of vector scalar instructions */
+
 	}
 
-	ctxt.Diag("unknown storex opcode %v", obj.Aconv(a))
+	ctxt.Diag("unknown storex opcode %v", a)
 	return 0
 }
diff --git a/src/cmd/internal/obj/ppc64/list9.go b/src/cmd/internal/obj/ppc64/list9.go
index d46297a..dfc4896 100644
--- a/src/cmd/internal/obj/ppc64/list9.go
+++ b/src/cmd/internal/obj/ppc64/list9.go
@@ -53,6 +53,12 @@ func Rconv(r int) string {
 	if REG_F0 <= r && r <= REG_F31 {
 		return fmt.Sprintf("F%d", r-REG_F0)
 	}
+	if REG_V0 <= r && r <= REG_V31 {
+		return fmt.Sprintf("V%d", r-REG_V0)
+	}
+	if REG_VS0 <= r && r <= REG_VS63 {
+		return fmt.Sprintf("VS%d", r-REG_VS0)
+	}
 	if REG_CR0 <= r && r <= REG_CR7 {
 		return fmt.Sprintf("CR%d", r-REG_CR0)
 	}
diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go
index 5f88307..68211ee 100644
--- a/src/cmd/internal/obj/ppc64/obj9.go
+++ b/src/cmd/internal/obj/ppc64/obj9.go
@@ -63,7 +63,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 			s.Size = 4
 			p.From.Type = obj.TYPE_MEM
 			p.From.Sym = s
-			p.From.Sym.Local = true
+			p.From.Sym.Set(obj.AttrLocal, true)
 			p.From.Name = obj.NAME_EXTERN
 			p.From.Offset = 0
 		}
@@ -76,7 +76,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 			s.Size = 8
 			p.From.Type = obj.TYPE_MEM
 			p.From.Sym = s
-			p.From.Sym.Local = true
+			p.From.Sym.Set(obj.AttrLocal, true)
 			p.From.Name = obj.NAME_EXTERN
 			p.From.Offset = 0
 		}
@@ -89,7 +89,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 			s.Size = 8
 			p.From.Type = obj.TYPE_MEM
 			p.From.Sym = s
-			p.From.Sym.Local = true
+			p.From.Sym.Set(obj.AttrLocal, true)
 			p.From.Name = obj.NAME_EXTERN
 			p.From.Offset = 0
 		}
@@ -168,7 +168,7 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 	// We only care about global data: NAME_EXTERN means a global
 	// symbol in the Go sense, and p.Sym.Local is true for a few
 	// internally defined symbols.
-	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
+	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
 		// MOVD $sym, Rx becomes MOVD sym at GOT, Rx
 		// MOVD $sym+<off>, Rx becomes MOVD sym at GOT, Rx; ADD <off>, Rx
 		if p.As != AMOVD {
@@ -195,12 +195,12 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 	// MOVx sym, Ry becomes MOVD sym at GOT, REGTMP; MOVx (REGTMP), Ry
 	// MOVx Ry, sym becomes MOVD sym at GOT, REGTMP; MOVx Ry, (REGTMP)
 	// An addition may be inserted between the two MOVs if there is an offset.
-	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
-		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
+	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
+		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
 		}
 		source = &p.From
-	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
+	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 		source = &p.To
 	} else {
 		return
@@ -275,9 +275,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 	 * expand BECOME pseudo
 	 */
 	if ctxt.Debugvlog != 0 {
-		fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime())
+		ctxt.Logf("%5.2f noops\n", obj.Cputime())
 	}
-	ctxt.Bso.Flush()
 
 	var q *obj.Prog
 	var q1 *obj.Prog
@@ -446,19 +445,15 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 	}
 
 	autosize := int32(0)
-	var aoffset int
-	var mov obj.As
 	var p1 *obj.Prog
 	var p2 *obj.Prog
 	for p := cursym.Text; p != nil; p = p.Link {
 		o := p.As
 		switch o {
 		case obj.ATEXT:
-			mov = AMOVD
-			aoffset = 0
 			autosize = int32(textstksiz)
 
-			if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 {
+			if p.Mark&LEAF != 0 && autosize == 0 {
 				// A leaf function with no locals has no frame.
 				p.From3.Offset |= obj.NOFRAME
 			}
@@ -469,6 +464,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				autosize += int32(ctxt.FixedFrameSize())
 			}
 
+			if p.Mark&LEAF != 0 && autosize < obj.StackSmall {
+				// A leaf function with a small stack can be marked
+				// NOSPLIT, avoiding a stack check.
+				p.From3.Offset |= obj.NOSPLIT
+			}
+
 			p.To.Offset = int64(autosize)
 
 			q = p
@@ -515,11 +516,49 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			}
 
 			if autosize != 0 {
-				/* use MOVDU to adjust R1 when saving R31, if autosize is small */
+				// Make sure to save link register for non-empty frame, even if
+				// it is a leaf function, so that traceback works.
 				if cursym.Text.Mark&LEAF == 0 && autosize >= -BIG && autosize <= BIG {
-					mov = AMOVDU
-					aoffset = int(-autosize)
+					// Use MOVDU to adjust R1 when saving R31, if autosize is small.
+					q = obj.Appendp(ctxt, q)
+					q.As = AMOVD
+					q.Lineno = p.Lineno
+					q.From.Type = obj.TYPE_REG
+					q.From.Reg = REG_LR
+					q.To.Type = obj.TYPE_REG
+					q.To.Reg = REGTMP
+
+					q = obj.Appendp(ctxt, q)
+					q.As = AMOVDU
+					q.Lineno = p.Lineno
+					q.From.Type = obj.TYPE_REG
+					q.From.Reg = REGTMP
+					q.To.Type = obj.TYPE_MEM
+					q.To.Offset = int64(-autosize)
+					q.To.Reg = REGSP
+					q.Spadj = int32(autosize)
 				} else {
+					// Frame size is too large for a MOVDU instruction.
+					// Store link register before decrementing SP, so if a signal comes
+					// during the execution of the function prologue, the traceback
+					// code will not see a half-updated stack frame.
+					q = obj.Appendp(ctxt, q)
+					q.As = AMOVD
+					q.Lineno = p.Lineno
+					q.From.Type = obj.TYPE_REG
+					q.From.Reg = REG_LR
+					q.To.Type = obj.TYPE_REG
+					q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction
+
+					q = obj.Appendp(ctxt, q)
+					q.As = AMOVD
+					q.Lineno = p.Lineno
+					q.From.Type = obj.TYPE_REG
+					q.From.Reg = REG_R29
+					q.To.Type = obj.TYPE_MEM
+					q.To.Offset = int64(-autosize)
+					q.To.Reg = REGSP
+
 					q = obj.Appendp(ctxt, q)
 					q.As = AADD
 					q.Lineno = p.Lineno
@@ -537,30 +576,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			}
 
 			if cursym.Text.Mark&LEAF != 0 {
-				cursym.Leaf = true
+				cursym.Set(obj.AttrLeaf, true)
 				break
 			}
 
-			q = obj.Appendp(ctxt, q)
-			q.As = AMOVD
-			q.Lineno = p.Lineno
-			q.From.Type = obj.TYPE_REG
-			q.From.Reg = REG_LR
-			q.To.Type = obj.TYPE_REG
-			q.To.Reg = REGTMP
-
-			q = obj.Appendp(ctxt, q)
-			q.As = mov
-			q.Lineno = p.Lineno
-			q.From.Type = obj.TYPE_REG
-			q.From.Reg = REGTMP
-			q.To.Type = obj.TYPE_MEM
-			q.To.Offset = int64(aoffset)
-			q.To.Reg = REGSP
-			if q.As == AMOVDU {
-				q.Spadj = int32(-aoffset)
-			}
-
 			if ctxt.Flag_shared {
 				q = obj.Appendp(ctxt, q)
 				q.As = AMOVD
@@ -824,6 +843,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 	}
 */
 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
+	p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode
+
 	// MOVD	g_stackguard(g), R3
 	p = obj.Appendp(ctxt, p)
 
@@ -831,7 +852,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 	p.From.Type = obj.TYPE_MEM
 	p.From.Reg = REGG
 	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
-	if ctxt.Cursym.Cfunc {
+	if ctxt.Cursym.CFunc() {
 		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
 	}
 	p.To.Type = obj.TYPE_REG
@@ -946,7 +967,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 	}
 
 	var morestacksym *obj.LSym
-	if ctxt.Cursym.Cfunc {
+	if ctxt.Cursym.CFunc() {
 		morestacksym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
 	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
 		morestacksym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
@@ -954,6 +975,24 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 		morestacksym = obj.Linklookup(ctxt, "runtime.morestack", 0)
 	}
 
+	if ctxt.Flag_shared {
+		// In PPC64 PIC code, R2 is used as TOC pointer derived from R12
+		// which is the address of function entry point when entering
+		// the function. We need to preserve R2 across call to morestack.
+		// Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in
+		// the caller's frame, but not used (0(SP) is caller's saved LR,
+		// 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2.
+
+		// MOVD R12, 8(SP)
+		p = obj.Appendp(ctxt, p)
+		p.As = AMOVD
+		p.From.Type = obj.TYPE_REG
+		p.From.Reg = REG_R2
+		p.To.Type = obj.TYPE_MEM
+		p.To.Reg = REGSP
+		p.To.Offset = 8
+	}
+
 	if ctxt.Flag_dynlink {
 		// Avoid calling morestack via a PLT when dynamically linking. The
 		// PLT stubs generated by the system linker on ppc64le when "std r2,
@@ -1001,12 +1040,23 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
 		p.To.Type = obj.TYPE_BRANCH
 		p.To.Sym = morestacksym
 	}
+
+	if ctxt.Flag_shared {
+		// MOVD 8(SP), R2
+		p = obj.Appendp(ctxt, p)
+		p.As = AMOVD
+		p.From.Type = obj.TYPE_MEM
+		p.From.Reg = REGSP
+		p.From.Offset = 8
+		p.To.Type = obj.TYPE_REG
+		p.To.Reg = REG_R2
+	}
+
 	// BR	start
 	p = obj.Appendp(ctxt, p)
-
 	p.As = ABR
 	p.To.Type = obj.TYPE_BRANCH
-	p.Pcond = ctxt.Cursym.Text.Link
+	p.Pcond = p0.Link
 
 	// placeholder for q1's jump target
 	p = obj.Appendp(ctxt, p)
diff --git a/src/cmd/internal/obj/reloctype_string.go b/src/cmd/internal/obj/reloctype_string.go
new file mode 100644
index 0000000..6de617c
--- /dev/null
+++ b/src/cmd/internal/obj/reloctype_string.go
@@ -0,0 +1,17 @@
+// Code generated by "stringer -type=RelocType"; DO NOT EDIT
+
+package obj
+
+import "fmt"
+
+const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLS"
+
+var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 195, 206, 216, 225, 235, 249, 263, 279, 293, 307, 318, 332, 347, 364, 382, 403, 413, 424, 437}
+
+func (i RelocType) String() string {
+	i -= 1
+	if i < 0 || i >= RelocType(len(_RelocType_index)-1) {
+		return fmt.Sprintf("RelocType(%d)", i+1)
+	}
+	return _RelocType_name[_RelocType_index[i]:_RelocType_index[i+1]]
+}
diff --git a/src/cmd/internal/obj/s390x/a.out.go b/src/cmd/internal/obj/s390x/a.out.go
index 490695c..87ee971 100644
--- a/src/cmd/internal/obj/s390x/a.out.go
+++ b/src/cmd/internal/obj/s390x/a.out.go
@@ -209,24 +209,27 @@ const (
 	// integer arithmetic
 	AADD = obj.ABaseS390X + obj.A_ARCHSPECIFIC + iota
 	AADDC
-	AADDME
 	AADDE
-	AADDZE
+	AADDW
 	ADIVW
 	ADIVWU
 	ADIVD
 	ADIVDU
+	AMODW
+	AMODWU
+	AMODD
+	AMODDU
 	AMULLW
 	AMULLD
 	AMULHD
 	AMULHDU
 	ASUB
 	ASUBC
-	ASUBME
 	ASUBV
 	ASUBE
-	ASUBZE
+	ASUBW
 	ANEG
+	ANEGW
 
 	// integer moves
 	AMOVWBR
@@ -240,14 +243,24 @@ const (
 	AMOVD
 	AMOVDBR
 
+	// conditional moves
+	AMOVDEQ
+	AMOVDGE
+	AMOVDGT
+	AMOVDLE
+	AMOVDLT
+	AMOVDNE
+
+	// find leftmost one
+	AFLOGR
+
 	// integer bitwise
 	AAND
-	AANDN
-	ANAND
-	ANOR
+	AANDW
 	AOR
-	AORN
+	AORW
 	AXOR
+	AXORW
 	ASLW
 	ASLD
 	ASRW
@@ -276,6 +289,7 @@ const (
 	AFMULS
 	AFNABS
 	AFNEG
+	AFNEGS
 	AFNMADD
 	AFNMADDS
 	AFNMSUB
@@ -286,6 +300,8 @@ const (
 	AFSUBS
 	AFSQRT
 	AFSQRTS
+	AFIEBR
+	AFIDBR
 
 	// convert from int32/int64 to float/float64
 	ACEFBRA
@@ -332,6 +348,8 @@ const (
 	ABGT
 	ABLE
 	ABLT
+	ABLEU
+	ABLTU
 	ABNE
 	ABVC
 	ABVS
@@ -364,6 +382,18 @@ const (
 	ALA
 	ALAY
 
+	// interlocked load and op
+	ALAA
+	ALAAG
+	ALAAL
+	ALAALG
+	ALAN
+	ALANG
+	ALAX
+	ALAXG
+	ALAO
+	ALAOG
+
 	// load/store multiple
 	ALMY
 	ALMG
diff --git a/src/cmd/internal/obj/s390x/anames.go b/src/cmd/internal/obj/s390x/anames.go
index 62dd181..51b9ffc 100644
--- a/src/cmd/internal/obj/s390x/anames.go
+++ b/src/cmd/internal/obj/s390x/anames.go
@@ -8,24 +8,27 @@ import "cmd/internal/obj"
 var Anames = []string{
 	obj.A_ARCHSPECIFIC: "ADD",
 	"ADDC",
-	"ADDME",
 	"ADDE",
-	"ADDZE",
+	"ADDW",
 	"DIVW",
 	"DIVWU",
 	"DIVD",
 	"DIVDU",
+	"MODW",
+	"MODWU",
+	"MODD",
+	"MODDU",
 	"MULLW",
 	"MULLD",
 	"MULHD",
 	"MULHDU",
 	"SUB",
 	"SUBC",
-	"SUBME",
 	"SUBV",
 	"SUBE",
-	"SUBZE",
+	"SUBW",
 	"NEG",
+	"NEGW",
 	"MOVWBR",
 	"MOVB",
 	"MOVBZ",
@@ -36,13 +39,19 @@ var Anames = []string{
 	"MOVWZ",
 	"MOVD",
 	"MOVDBR",
+	"MOVDEQ",
+	"MOVDGE",
+	"MOVDGT",
+	"MOVDLE",
+	"MOVDLT",
+	"MOVDNE",
+	"FLOGR",
 	"AND",
-	"ANDN",
-	"NAND",
-	"NOR",
+	"ANDW",
 	"OR",
-	"ORN",
+	"ORW",
 	"XOR",
+	"XORW",
 	"SLW",
 	"SLD",
 	"SRW",
@@ -69,6 +78,7 @@ var Anames = []string{
 	"FMULS",
 	"FNABS",
 	"FNEG",
+	"FNEGS",
 	"FNMADD",
 	"FNMADDS",
 	"FNMSUB",
@@ -79,6 +89,8 @@ var Anames = []string{
 	"FSUBS",
 	"FSQRT",
 	"FSQRTS",
+	"FIEBR",
+	"FIDBR",
 	"CEFBRA",
 	"CDFBRA",
 	"CEGBRA",
@@ -109,6 +121,8 @@ var Anames = []string{
 	"BGT",
 	"BLE",
 	"BLT",
+	"BLEU",
+	"BLTU",
 	"BNE",
 	"BVC",
 	"BVS",
@@ -134,6 +148,16 @@ var Anames = []string{
 	"LARL",
 	"LA",
 	"LAY",
+	"LAA",
+	"LAAG",
+	"LAAL",
+	"LAALG",
+	"LAN",
+	"LANG",
+	"LAX",
+	"LAXG",
+	"LAO",
+	"LAOG",
 	"LMY",
 	"LMG",
 	"STMY",
diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go
index 2a99bbe..cc039bd 100644
--- a/src/cmd/internal/obj/s390x/asmz.go
+++ b/src/cmd/internal/obj/s390x/asmz.go
@@ -38,7 +38,7 @@ import (
 
 // instruction layout.
 const (
-	FuncAlign = 16
+	funcAlign = 16
 )
 
 type Optab struct {
@@ -76,19 +76,16 @@ var optab = []Optab{
 	Optab{AMOVBZ, C_DCON, C_NONE, C_NONE, C_REG, 3, 0},
 
 	// store constant
-	Optab{AMOVD, C_SYMADDR, C_NONE, C_NONE, C_ADDR, 73, 0},
 	Optab{AMOVD, C_LCON, C_NONE, C_NONE, C_ADDR, 73, 0},
 	Optab{AMOVW, C_LCON, C_NONE, C_NONE, C_ADDR, 73, 0},
 	Optab{AMOVWZ, C_LCON, C_NONE, C_NONE, C_ADDR, 73, 0},
 	Optab{AMOVBZ, C_LCON, C_NONE, C_NONE, C_ADDR, 73, 0},
 	Optab{AMOVB, C_LCON, C_NONE, C_NONE, C_ADDR, 73, 0},
-	Optab{AMOVD, C_SYMADDR, C_NONE, C_NONE, C_LAUTO, 72, REGSP},
 	Optab{AMOVD, C_LCON, C_NONE, C_NONE, C_LAUTO, 72, REGSP},
 	Optab{AMOVW, C_LCON, C_NONE, C_NONE, C_LAUTO, 72, REGSP},
 	Optab{AMOVWZ, C_LCON, C_NONE, C_NONE, C_LAUTO, 72, REGSP},
 	Optab{AMOVB, C_LCON, C_NONE, C_NONE, C_LAUTO, 72, REGSP},
 	Optab{AMOVBZ, C_LCON, C_NONE, C_NONE, C_LAUTO, 72, REGSP},
-	Optab{AMOVD, C_SYMADDR, C_NONE, C_NONE, C_LOREG, 72, 0},
 	Optab{AMOVD, C_LCON, C_NONE, C_NONE, C_LOREG, 72, 0},
 	Optab{AMOVW, C_LCON, C_NONE, C_NONE, C_LOREG, 72, 0},
 	Optab{AMOVWZ, C_LCON, C_NONE, C_NONE, C_LOREG, 72, 0},
@@ -137,20 +134,26 @@ var optab = []Optab{
 	Optab{AMOVBZ, C_ADDR, C_NONE, C_NONE, C_REG, 75, 0},
 	Optab{AMOVB, C_ADDR, C_NONE, C_NONE, C_REG, 75, 0},
 
+	// interlocked load and op
+	Optab{ALAAG, C_REG, C_REG, C_NONE, C_LOREG, 99, 0},
+
 	// integer arithmetic
 	Optab{AADD, C_REG, C_REG, C_NONE, C_REG, 2, 0},
 	Optab{AADD, C_REG, C_NONE, C_NONE, C_REG, 2, 0},
 	Optab{AADD, C_LCON, C_REG, C_NONE, C_REG, 22, 0},
 	Optab{AADD, C_LCON, C_NONE, C_NONE, C_REG, 22, 0},
+	Optab{AADD, C_LOREG, C_NONE, C_NONE, C_REG, 12, 0},
+	Optab{AADD, C_LAUTO, C_NONE, C_NONE, C_REG, 12, REGSP},
+	Optab{ASUB, C_LCON, C_REG, C_NONE, C_REG, 21, 0},
+	Optab{ASUB, C_LCON, C_NONE, C_NONE, C_REG, 21, 0},
+	Optab{ASUB, C_LOREG, C_NONE, C_NONE, C_REG, 12, 0},
+	Optab{ASUB, C_LAUTO, C_NONE, C_NONE, C_REG, 12, REGSP},
 	Optab{AMULHD, C_REG, C_NONE, C_NONE, C_REG, 4, 0},
 	Optab{AMULHD, C_REG, C_REG, C_NONE, C_REG, 4, 0},
-	Optab{ASUBC, C_REG, C_REG, C_NONE, C_REG, 10, 0},
-	Optab{ASUBC, C_REG, C_NONE, C_NONE, C_REG, 10, 0},
 	Optab{ADIVW, C_REG, C_REG, C_NONE, C_REG, 2, 0},
 	Optab{ADIVW, C_REG, C_NONE, C_NONE, C_REG, 2, 0},
 	Optab{ASUB, C_REG, C_REG, C_NONE, C_REG, 10, 0},
 	Optab{ASUB, C_REG, C_NONE, C_NONE, C_REG, 10, 0},
-	Optab{AADDME, C_REG, C_NONE, C_NONE, C_REG, 47, 0},
 	Optab{ANEG, C_REG, C_NONE, C_NONE, C_REG, 47, 0},
 	Optab{ANEG, C_NONE, C_NONE, C_NONE, C_REG, 47, 0},
 
@@ -158,11 +161,13 @@ var optab = []Optab{
 	Optab{AAND, C_REG, C_REG, C_NONE, C_REG, 6, 0},
 	Optab{AAND, C_REG, C_NONE, C_NONE, C_REG, 6, 0},
 	Optab{AAND, C_LCON, C_NONE, C_NONE, C_REG, 23, 0},
-	Optab{AAND, C_LCON, C_REG, C_NONE, C_REG, 23, 0},
-	Optab{AOR, C_REG, C_REG, C_NONE, C_REG, 6, 0},
-	Optab{AOR, C_REG, C_NONE, C_NONE, C_REG, 6, 0},
-	Optab{AOR, C_LCON, C_NONE, C_NONE, C_REG, 23, 0},
-	Optab{AOR, C_LCON, C_REG, C_NONE, C_REG, 23, 0},
+	Optab{AAND, C_LOREG, C_NONE, C_NONE, C_REG, 12, 0},
+	Optab{AAND, C_LAUTO, C_NONE, C_NONE, C_REG, 12, REGSP},
+	Optab{AANDW, C_REG, C_REG, C_NONE, C_REG, 6, 0},
+	Optab{AANDW, C_REG, C_NONE, C_NONE, C_REG, 6, 0},
+	Optab{AANDW, C_LCON, C_NONE, C_NONE, C_REG, 24, 0},
+	Optab{AANDW, C_LOREG, C_NONE, C_NONE, C_REG, 12, 0},
+	Optab{AANDW, C_LAUTO, C_NONE, C_NONE, C_REG, 12, REGSP},
 	Optab{ASLD, C_REG, C_NONE, C_NONE, C_REG, 7, 0},
 	Optab{ASLD, C_REG, C_REG, C_NONE, C_REG, 7, 0},
 	Optab{ASLD, C_SCON, C_REG, C_NONE, C_REG, 7, 0},
@@ -188,6 +193,7 @@ var optab = []Optab{
 	Optab{AFMOVD, C_ZCON, C_NONE, C_NONE, C_FREG, 67, 0},
 	Optab{ACEFBRA, C_REG, C_NONE, C_NONE, C_FREG, 82, 0},
 	Optab{ACFEBRA, C_FREG, C_NONE, C_NONE, C_REG, 83, 0},
+	Optab{AFIEBR, C_SCON, C_FREG, C_NONE, C_FREG, 48, 0},
 
 	// load symbol address (plus offset)
 	Optab{AMOVD, C_SYMADDR, C_NONE, C_NONE, C_REG, 19, 0},
@@ -213,6 +219,12 @@ var optab = []Optab{
 	Optab{ACMPUBEQ, C_REG, C_REG, C_NONE, C_SBRA, 89, 0},
 	Optab{ACMPUBEQ, C_REG, C_NONE, C_ANDCON, C_SBRA, 90, 0},
 
+	// move on condition
+	Optab{AMOVDEQ, C_REG, C_NONE, C_NONE, C_REG, 17, 0},
+
+	// find leftmost one
+	Optab{AFLOGR, C_REG, C_NONE, C_NONE, C_REG, 8, 0},
+
 	// compare
 	Optab{ACMP, C_REG, C_NONE, C_NONE, C_REG, 70, 0},
 	Optab{ACMP, C_REG, C_NONE, C_NONE, C_LCON, 71, 0},
@@ -383,7 +395,7 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym) {
 	ctxt.Cursym = cursym
 	ctxt.Autosize = int32(p.To.Offset)
 
-	if oprange[AANDN&obj.AMask] == nil {
+	if oprange[AORW&obj.AMask] == nil {
 		buildop(ctxt)
 	}
 
@@ -420,8 +432,8 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym) {
 	}
 
 	cursym.Size = int64(len(buffer))
-	if cursym.Size%FuncAlign != 0 {
-		cursym.Size += FuncAlign - (cursym.Size % FuncAlign)
+	if cursym.Size%funcAlign != 0 {
+		cursym.Size += funcAlign - (cursym.Size % funcAlign)
 	}
 	cursym.Grow(cursym.Size)
 	copy(cursym.P, buffer)
@@ -651,7 +663,7 @@ func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
 	}
 
 	// cannot find a case; abort
-	ctxt.Diag("illegal combination %v %v %v %v %v\n", obj.Aconv(p.As), DRconv(a1), DRconv(a2), DRconv(a3), DRconv(a4))
+	ctxt.Diag("illegal combination %v %v %v %v %v\n", p.As, DRconv(a1), DRconv(a2), DRconv(a3), DRconv(a4))
 	ctxt.Diag("prog: %v\n", p)
 	return nil
 }
@@ -786,6 +798,7 @@ func buildop(ctxt *obj.Link) {
 		switch r {
 		case AADD:
 			opset(AADDC, r)
+			opset(AADDW, r)
 			opset(AMULLD, r)
 			opset(AMULLW, r)
 		case ADIVW:
@@ -793,6 +806,10 @@ func buildop(ctxt *obj.Link) {
 			opset(ADIVD, r)
 			opset(ADIVDU, r)
 			opset(ADIVWU, r)
+			opset(AMODD, r)
+			opset(AMODDU, r)
+			opset(AMODW, r)
+			opset(AMODWU, r)
 		case AMULHD:
 			opset(AMULHDU, r)
 		case AMOVBZ:
@@ -809,19 +826,20 @@ func buildop(ctxt *obj.Link) {
 			opset(ASTCKC, r)
 			opset(ASTCKE, r)
 			opset(ASTCKF, r)
+		case ALAAG:
+			opset(ALAA, r)
+			opset(ALAAL, r)
+			opset(ALAALG, r)
+			opset(ALAN, r)
+			opset(ALANG, r)
+			opset(ALAX, r)
+			opset(ALAXG, r)
+			opset(ALAO, r)
+			opset(ALAOG, r)
 		case ASTMG:
 			opset(ASTMY, r)
 		case ALMG:
 			opset(ALMY, r)
-		case AAND:
-			opset(AANDN, r)
-			opset(ANAND, r)
-			opset(ANOR, r)
-			opset(AORN, r)
-		case AADDME:
-			opset(AADDZE, r)
-			opset(ASUBME, r)
-			opset(ASUBZE, r)
 		case ABEQ:
 			opset(ABGE, r)
 			opset(ABGT, r)
@@ -830,6 +848,8 @@ func buildop(ctxt *obj.Link) {
 			opset(ABNE, r)
 			opset(ABVC, r)
 			opset(ABVS, r)
+			opset(ABLEU, r)
+			opset(ABLTU, r)
 		case ABR:
 			opset(ABL, r)
 		case ABC:
@@ -837,6 +857,7 @@ func buildop(ctxt *obj.Link) {
 		case AFABS:
 			opset(AFNABS, r)
 			opset(AFNEG, r)
+			opset(AFNEGS, r)
 			opset(ALEDBR, r)
 			opset(ALDEBR, r)
 			opset(AFSQRT, r)
@@ -860,8 +881,12 @@ func buildop(ctxt *obj.Link) {
 		case AFCMPO:
 			opset(AFCMPU, r)
 			opset(ACEBR, r)
-		case AOR:
+		case AAND:
+			opset(AOR, r)
 			opset(AXOR, r)
+		case AANDW:
+			opset(AORW, r)
+			opset(AXORW, r)
 		case ASLD:
 			opset(ASRD, r)
 			opset(ASLW, r)
@@ -875,6 +900,9 @@ func buildop(ctxt *obj.Link) {
 		case ASUB:
 			opset(ASUBC, r)
 			opset(ASUBE, r)
+			opset(ASUBW, r)
+		case ANEG:
+			opset(ANEGW, r)
 		case AFMOVD:
 			opset(AFMOVS, r)
 		case AMOVDBR:
@@ -899,6 +927,8 @@ func buildop(ctxt *obj.Link) {
 			opset(ACLFDBR, r)
 			opset(ACLGEBR, r)
 			opset(ACLGDBR, r)
+		case AFIEBR:
+			opset(AFIDBR, r)
 		case ACMPBEQ:
 			opset(ACMPBGE, r)
 			opset(ACMPBGT, r)
@@ -911,6 +941,12 @@ func buildop(ctxt *obj.Link) {
 			opset(ACMPUBLE, r)
 			opset(ACMPUBLT, r)
 			opset(ACMPUBNE, r)
+		case AMOVDEQ:
+			opset(AMOVDGE, r)
+			opset(AMOVDGT, r)
+			opset(AMOVDLE, r)
+			opset(AMOVDLT, r)
+			opset(AMOVDNE, r)
 		case AVL:
 			opset(AVLLEZB, r)
 			opset(AVLLEZH, r)
@@ -2494,22 +2530,26 @@ func addcallreloc(ctxt *obj.Link, sym *obj.LSym, add int64) *obj.Reloc {
 
 func branchMask(ctxt *obj.Link, p *obj.Prog) uint32 {
 	switch p.As {
-	case ABEQ, ACMPBEQ, ACMPUBEQ:
+	case ABEQ, ACMPBEQ, ACMPUBEQ, AMOVDEQ:
 		return 0x8
-	case ABGE, ACMPBGE, ACMPUBGE:
+	case ABGE, ACMPBGE, ACMPUBGE, AMOVDGE:
 		return 0xA
-	case ABGT, ACMPBGT, ACMPUBGT:
+	case ABGT, ACMPBGT, ACMPUBGT, AMOVDGT:
 		return 0x2
-	case ABLE, ACMPBLE, ACMPUBLE:
+	case ABLE, ACMPBLE, ACMPUBLE, AMOVDLE:
 		return 0xC
-	case ABLT, ACMPBLT, ACMPUBLT:
+	case ABLT, ACMPBLT, ACMPUBLT, AMOVDLT:
 		return 0x4
-	case ABNE, ACMPBNE, ACMPUBNE:
+	case ABNE, ACMPBNE, ACMPUBNE, AMOVDNE:
 		return 0x7
+	case ABLEU: // LE or unordered
+		return 0xD
+	case ABLTU: // LT or unordered
+		return 0x5
 	case ABVC:
-		return 0x0 //needs extra instruction
+		return 0x0 // needs extra instruction
 	case ABVS:
-		return 0x1
+		return 0x1 // unordered
 	}
 	ctxt.Diag("unknown conditional branch %v", p.As)
 	return 0xF
@@ -2558,9 +2598,9 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 		}
 
 	case 2: // arithmetic op reg [reg] reg
-		r := int(p.Reg)
+		r := p.Reg
 		if r == 0 {
-			r = int(p.To.Reg)
+			r = p.To.Reg
 		}
 
 		var opcode uint32
@@ -2574,17 +2614,19 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 			opcode = op_ALGRK
 		case AADDE:
 			opcode = op_ALCGR
+		case AADDW:
+			opcode = op_ARK
 		case AMULLW:
 			opcode = op_MSGFR
 		case AMULLD:
 			opcode = op_MSGR
-		case ADIVW:
+		case ADIVW, AMODW:
 			opcode = op_DSGFR
-		case ADIVWU:
+		case ADIVWU, AMODWU:
 			opcode = op_DLR
-		case ADIVD:
+		case ADIVD, AMODD:
 			opcode = op_DSGR
-		case ADIVDU:
+		case ADIVDU, AMODDU:
 			opcode = op_DLGR
 		case AFADD:
 			opcode = op_ADBR
@@ -2603,11 +2645,15 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 		switch p.As {
 		default:
 
-		case AADD, AADDC:
-			zRRF(opcode, uint32(p.From.Reg), 0, uint32(p.To.Reg), uint32(r), asm)
+		case AADD, AADDC, AADDW:
+			if p.As == AADDW && r == p.To.Reg {
+				zRR(op_AR, uint32(p.To.Reg), uint32(p.From.Reg), asm)
+			} else {
+				zRRF(opcode, uint32(p.From.Reg), 0, uint32(p.To.Reg), uint32(r), asm)
+			}
 
 		case AADDE, AMULLW, AMULLD:
-			if r == int(p.To.Reg) {
+			if r == p.To.Reg {
 				zRRE(opcode, uint32(p.To.Reg), uint32(p.From.Reg), asm)
 			} else if p.From.Reg == p.To.Reg {
 				zRRE(opcode, uint32(p.To.Reg), uint32(r), asm)
@@ -2618,14 +2664,22 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 
 		case ADIVW, ADIVWU, ADIVD, ADIVDU:
 			if p.As == ADIVWU || p.As == ADIVDU {
-				zRRE(op_LGR, REGTMP, REGZERO, asm)
+				zRI(op_LGHI, REGTMP, 0, asm)
 			}
 			zRRE(op_LGR, REGTMP2, uint32(r), asm)
 			zRRE(opcode, REGTMP, uint32(p.From.Reg), asm)
 			zRRE(op_LGR, uint32(p.To.Reg), REGTMP2, asm)
 
+		case AMODW, AMODWU, AMODD, AMODDU:
+			if p.As == AMODWU || p.As == AMODDU {
+				zRI(op_LGHI, REGTMP, 0, asm)
+			}
+			zRRE(op_LGR, REGTMP2, uint32(r), asm)
+			zRRE(opcode, REGTMP, uint32(p.From.Reg), asm)
+			zRRE(op_LGR, uint32(p.To.Reg), REGTMP, asm)
+
 		case AFADD, AFADDS:
-			if r == int(p.To.Reg) {
+			if r == p.To.Reg {
 				zRRE(opcode, uint32(p.To.Reg), uint32(p.From.Reg), asm)
 			} else if p.From.Reg == p.To.Reg {
 				zRRE(opcode, uint32(p.To.Reg), uint32(r), asm)
@@ -2635,7 +2689,7 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 			}
 
 		case AFSUB, AFSUBS, AFDIV, AFDIVS:
-			if r == int(p.To.Reg) {
+			if r == p.To.Reg {
 				zRRE(opcode, uint32(p.To.Reg), uint32(p.From.Reg), asm)
 			} else if p.From.Reg == p.To.Reg {
 				zRRE(op_LGDR, REGTMP, uint32(r), asm)
@@ -2665,16 +2719,14 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 		case AMOVW:
 			v = int64(int32(v))
 		}
-		if v&0xffff == v {
-			zRI(op_LLILL, uint32(p.To.Reg), uint32(v), asm)
+		if int64(int16(v)) == v {
+			zRI(op_LGHI, uint32(p.To.Reg), uint32(v), asm)
 		} else if v&0xffff0000 == v {
 			zRI(op_LLILH, uint32(p.To.Reg), uint32(v>>16), asm)
 		} else if v&0xffff00000000 == v {
 			zRI(op_LLIHL, uint32(p.To.Reg), uint32(v>>32), asm)
 		} else if uint64(v)&0xffff000000000000 == uint64(v) {
 			zRI(op_LLIHH, uint32(p.To.Reg), uint32(v>>48), asm)
-		} else if int64(int16(v)) == v {
-			zRI(op_LGHI, uint32(p.To.Reg), uint32(v), asm)
 		} else if int64(int32(v)) == v {
 			zRIL(_a, op_LGFI, uint32(p.To.Reg), uint32(v), asm)
 		} else if int64(uint32(v)) == v {
@@ -2712,74 +2764,35 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 		zI(op_SVC, 0, asm)
 
 	case 6: // logical op reg [reg] reg
-		if p.To.Reg == 0 {
-			ctxt.Diag("literal operation on R0\n%v", p)
-		}
-
+		var oprr, oprre, oprrf uint32
 		switch p.As {
-		case AAND, AOR, AXOR:
-			var opcode1, opcode2 uint32
-			switch p.As {
-			default:
-			case AAND:
-				opcode1 = op_NGR
-				opcode2 = op_NGRK
-			case AOR:
-				opcode1 = op_OGR
-				opcode2 = op_OGRK
-			case AXOR:
-				opcode1 = op_XGR
-				opcode2 = op_XGRK
-			}
-
-			r := int(p.Reg)
-			if r == 0 {
-				zRRE(opcode1, uint32(p.To.Reg), uint32(p.From.Reg), asm)
-			} else {
-				zRRF(opcode2, uint32(r), 0, uint32(p.To.Reg), uint32(p.From.Reg), asm)
-			}
-
-		case AANDN, AORN:
-			var opcode1, opcode2 uint32
-			switch p.As {
-			default:
-			case AANDN:
-				opcode1 = op_NGR
-				opcode2 = op_NGRK
-			case AORN:
-				opcode1 = op_OGR
-				opcode2 = op_OGRK
-			}
-
-			r := int(p.Reg)
-			if r == 0 {
-				zRRE(op_LCGR, uint32(p.To.Reg), uint32(p.To.Reg), asm)
-				zRRE(opcode1, uint32(p.To.Reg), uint32(p.From.Reg), asm)
-			} else {
-				zRRE(op_LCGR, REGTMP, uint32(r), asm)
-				zRRF(opcode2, REGTMP, 0, uint32(p.To.Reg), uint32(p.From.Reg), asm)
-			}
-
-		case ANAND, ANOR:
-			var opcode1, opcode2 uint32
-			switch p.As {
-			default:
-			case ANAND:
-				opcode1 = op_NGR
-				opcode2 = op_NGRK
-			case ANOR:
-				opcode1 = op_OGR
-				opcode2 = op_OGRK
-			}
-
-			r := int(p.Reg)
-			if r == 0 {
-				zRRE(opcode1, uint32(p.To.Reg), uint32(p.From.Reg), asm)
+		case AAND:
+			oprre = op_NGR
+			oprrf = op_NGRK
+		case AANDW:
+			oprr = op_NR
+			oprrf = op_NRK
+		case AOR:
+			oprre = op_OGR
+			oprrf = op_OGRK
+		case AORW:
+			oprr = op_OR
+			oprrf = op_ORK
+		case AXOR:
+			oprre = op_XGR
+			oprrf = op_XGRK
+		case AXORW:
+			oprr = op_XR
+			oprrf = op_XRK
+		}
+		if p.Reg == 0 {
+			if oprr != 0 {
+				zRR(oprr, uint32(p.To.Reg), uint32(p.From.Reg), asm)
 			} else {
-				zRRF(opcode2, uint32(r), 0, uint32(p.To.Reg), uint32(p.From.Reg), asm)
+				zRRE(oprre, uint32(p.To.Reg), uint32(p.From.Reg), asm)
 			}
-
-			zRRE(op_LCGR, uint32(p.To.Reg), uint32(p.To.Reg), asm)
+		} else {
+			zRRF(oprrf, uint32(p.Reg), 0, uint32(p.To.Reg), uint32(p.From.Reg), asm)
 		}
 
 	case 7: // shift/rotate reg [reg] reg
@@ -2812,6 +2825,13 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 		}
 		zRSY(opcode, uint32(r1), uint32(r3), uint32(b2), uint32(d2), asm)
 
+	case 8: // find leftmost one
+		if p.To.Reg&1 != 0 {
+			ctxt.Diag("target must be an even-numbered register")
+		}
+		// FLOGR also writes a mask to p.To.Reg+1.
+		zRRE(op_FLOGR, uint32(p.To.Reg), uint32(p.From.Reg), asm)
+
 	case 10: // subtract reg [reg] reg
 		r := int(p.Reg)
 
@@ -2829,7 +2849,6 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 			} else {
 				zRRF(op_SLGRK, uint32(p.From.Reg), 0, uint32(p.To.Reg), uint32(r), asm)
 			}
-
 		case ASUBE:
 			if r == 0 {
 				r = int(p.To.Reg)
@@ -2844,6 +2863,12 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 				zRRE(op_LGR, uint32(p.To.Reg), uint32(r), asm)
 				zRRE(op_SLBGR, uint32(p.To.Reg), uint32(p.From.Reg), asm)
 			}
+		case ASUBW:
+			if r == 0 {
+				zRR(op_SR, uint32(p.To.Reg), uint32(p.From.Reg), asm)
+			} else {
+				zRRF(op_SRK, uint32(p.From.Reg), 0, uint32(p.To.Reg), uint32(r), asm)
+			}
 		}
 
 	case 11: // br/bl
@@ -2866,6 +2891,67 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 			}
 		}
 
+	case 12:
+		r1 := p.To.Reg
+		d2 := vregoff(ctxt, &p.From)
+		b2 := p.From.Reg
+		if b2 == 0 {
+			b2 = o.param
+		}
+		x2 := p.From.Index
+		if -DISP20/2 > d2 || d2 >= DISP20/2 {
+			zRIL(_a, op_LGFI, REGTMP, uint32(d2), asm)
+			if x2 != 0 {
+				zRX(op_LA, REGTMP, REGTMP, uint32(x2), 0, asm)
+			}
+			x2 = REGTMP
+			d2 = 0
+		}
+		var opx, opxy uint32
+		switch p.As {
+		case AADD:
+			opxy = op_AG
+		case AADDC:
+			opxy = op_ALG
+		case AADDW:
+			opx = op_A
+			opxy = op_AY
+		case AMULLW:
+			opx = op_MS
+			opxy = op_MSY
+		case AMULLD:
+			opxy = op_MSG
+		case ASUB:
+			opxy = op_SG
+		case ASUBC:
+			opxy = op_SLG
+		case ASUBE:
+			opxy = op_SLBG
+		case ASUBW:
+			opx = op_S
+			opxy = op_SY
+		case AAND:
+			opxy = op_NG
+		case AANDW:
+			opx = op_N
+			opxy = op_NY
+		case AOR:
+			opxy = op_OG
+		case AORW:
+			opx = op_O
+			opxy = op_OY
+		case AXOR:
+			opxy = op_XG
+		case AXORW:
+			opx = op_X
+			opxy = op_XY
+		}
+		if opx != 0 && 0 <= d2 && d2 < DISP12 {
+			zRX(opx, uint32(r1), uint32(x2), uint32(b2), uint32(d2), asm)
+		} else {
+			zRXY(opxy, uint32(r1), uint32(x2), uint32(b2), uint32(d2), asm)
+		}
+
 	case 15: // br/bl (reg)
 		r := p.To.Reg
 		if p.As == ABCL || p.As == ABL {
@@ -2889,6 +2975,10 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 			addrilreloc(ctxt, p.To.Sym, p.To.Offset)
 		}
 
+	case 17: // move on condition
+		m3 := branchMask(ctxt, p)
+		zRRF(op_LOCGR, m3, 0, uint32(p.To.Reg), uint32(p.From.Reg), asm)
+
 	case 18: // br/bl reg
 		if p.As == ABL {
 			zRR(op_BASR, uint32(REG_LR), uint32(p.To.Reg), asm)
@@ -2905,107 +2995,143 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 		}
 		addrilreloc(ctxt, p.From.Sym, d)
 
-	case 22: // arithmetic op $constant [reg] reg
-		if p.From.Sym != nil {
-			ctxt.Diag("%v is not supported", p)
-		}
+	case 21: // subtract $constant [reg] reg
 		v := vregoff(ctxt, &p.From)
 		r := p.Reg
 		if r == 0 {
 			r = p.To.Reg
 		}
 		switch p.As {
-		default:
-		case AADD:
-			if r == p.To.Reg {
-				zRIL(_a, op_AGFI, uint32(p.To.Reg), uint32(v), asm)
-			} else if int64(int16(v)) == v {
-				zRIE(_d, op_AGHIK, uint32(p.To.Reg), uint32(r), uint32(v), 0, 0, 0, 0, asm)
-			} else {
-				zRRE(op_LGR, uint32(p.To.Reg), uint32(r), asm)
-				zRIL(_a, op_AGFI, uint32(p.To.Reg), uint32(v), asm)
-			}
-		case AADDC:
+		case ASUB:
+			zRIL(_a, op_LGFI, uint32(REGTMP), uint32(v), asm)
+			zRRF(op_SLGRK, uint32(REGTMP), 0, uint32(p.To.Reg), uint32(r), asm)
+		case ASUBC:
 			if r != p.To.Reg {
 				zRRE(op_LGR, uint32(p.To.Reg), uint32(r), asm)
 			}
-			zRIL(_a, op_ALGFI, uint32(p.To.Reg), uint32(v), asm)
-		case AMULLW, AMULLD:
+			zRIL(_a, op_SLGFI, uint32(p.To.Reg), uint32(v), asm)
+		case ASUBW:
 			if r != p.To.Reg {
-				zRRE(op_LGR, uint32(p.To.Reg), uint32(r), asm)
-			}
-			if int64(int16(v)) == v {
-				zRI(op_MGHI, uint32(p.To.Reg), uint32(v), asm)
-			} else {
-				zRIL(_a, op_MSGFI, uint32(p.To.Reg), uint32(v), asm)
+				zRR(op_LR, uint32(p.To.Reg), uint32(r), asm)
 			}
+			zRIL(_a, op_SLFI, uint32(p.To.Reg), uint32(v), asm)
 		}
 
-	case 23: // logical op $constant [reg] reg
+	case 22: // add/multiply $constant [reg] reg
 		v := vregoff(ctxt, &p.From)
-		var opcode uint32
 		r := p.Reg
 		if r == 0 {
 			r = p.To.Reg
 		}
-		if r == p.To.Reg {
+		var opri, opril, oprie uint32
+		switch p.As {
+		case AADD:
+			opri = op_AGHI
+			opril = op_AGFI
+			oprie = op_AGHIK
+		case AADDC:
+			opril = op_ALGFI
+			oprie = op_ALGHSIK
+		case AADDW:
+			opri = op_AHI
+			opril = op_AFI
+			oprie = op_AHIK
+		case AMULLW:
+			opri = op_MHI
+			opril = op_MSFI
+		case AMULLD:
+			opri = op_MGHI
+			opril = op_MSGFI
+		}
+		if r != p.To.Reg && (oprie == 0 || int64(int16(v)) != v) {
 			switch p.As {
-			default:
-				ctxt.Diag("%v is not supported", p)
-			case AAND:
-				if v >= 0 { // needs zero extend
-					zRIL(_a, op_LGFI, REGTMP, uint32(v), asm)
-					zRRE(op_NGR, uint32(p.To.Reg), REGTMP, asm)
-				} else if int64(int16(v)) == v {
-					zRI(op_NILL, uint32(p.To.Reg), uint32(v), asm)
-				} else { //  r.To.Reg & 0xffffffff00000000 & uint32(v)
-					zRIL(_a, op_NILF, uint32(p.To.Reg), uint32(v), asm)
-				}
-			case AOR:
-				if int64(uint32(v)) != v { // needs sign extend
-					zRIL(_a, op_LGFI, REGTMP, uint32(v), asm)
-					zRRE(op_OGR, uint32(p.To.Reg), REGTMP, asm)
-				} else if int64(uint16(v)) == v {
-					zRI(op_OILL, uint32(p.To.Reg), uint32(v), asm)
-				} else {
-					zRIL(_a, op_OILF, uint32(p.To.Reg), uint32(v), asm)
-				}
-			case AXOR:
-				if int64(uint32(v)) != v { // needs sign extend
-					zRIL(_a, op_LGFI, REGTMP, uint32(v), asm)
-					zRRE(op_XGR, uint32(p.To.Reg), REGTMP, asm)
-				} else {
-					zRIL(_a, op_XILF, uint32(p.To.Reg), uint32(v), asm)
-				}
+			case AADD, AADDC, AMULLD:
+				zRRE(op_LGR, uint32(p.To.Reg), uint32(r), asm)
+			case AADDW, AMULLW:
+				zRR(op_LR, uint32(p.To.Reg), uint32(r), asm)
+			}
+			r = p.To.Reg
+		}
+		if r == p.To.Reg {
+			if opri != 0 && int64(int16(v)) == v {
+				zRI(opri, uint32(p.To.Reg), uint32(v), asm)
+			} else {
+				zRIL(_a, opril, uint32(p.To.Reg), uint32(v), asm)
 			}
 		} else {
-			switch p.As {
-			default:
-				ctxt.Diag("%v is not supported", p)
-			case AAND:
-				opcode = op_NGRK
-			case AOR:
-				opcode = op_OGRK
-			case AXOR:
-				opcode = op_XGRK
+			zRIE(_d, oprie, uint32(p.To.Reg), uint32(r), uint32(v), 0, 0, 0, 0, asm)
+		}
+
+	case 23: // 64-bit logical op $constant reg
+		// TODO(mundaym): merge with case 24.
+		v := vregoff(ctxt, &p.From)
+		switch p.As {
+		default:
+			ctxt.Diag("%v is not supported", p)
+		case AAND:
+			if v >= 0 { // needs zero extend
+				zRIL(_a, op_LGFI, REGTMP, uint32(v), asm)
+				zRRE(op_NGR, uint32(p.To.Reg), REGTMP, asm)
+			} else if int64(int16(v)) == v {
+				zRI(op_NILL, uint32(p.To.Reg), uint32(v), asm)
+			} else { //  r.To.Reg & 0xffffffff00000000 & uint32(v)
+				zRIL(_a, op_NILF, uint32(p.To.Reg), uint32(v), asm)
+			}
+		case AOR:
+			if int64(uint32(v)) != v { // needs sign extend
+				zRIL(_a, op_LGFI, REGTMP, uint32(v), asm)
+				zRRE(op_OGR, uint32(p.To.Reg), REGTMP, asm)
+			} else if int64(uint16(v)) == v {
+				zRI(op_OILL, uint32(p.To.Reg), uint32(v), asm)
+			} else {
+				zRIL(_a, op_OILF, uint32(p.To.Reg), uint32(v), asm)
+			}
+		case AXOR:
+			if int64(uint32(v)) != v { // needs sign extend
+				zRIL(_a, op_LGFI, REGTMP, uint32(v), asm)
+				zRRE(op_XGR, uint32(p.To.Reg), REGTMP, asm)
+			} else {
+				zRIL(_a, op_XILF, uint32(p.To.Reg), uint32(v), asm)
 			}
-			zRIL(_a, op_LGFI, REGTMP, uint32(v), asm)
-			zRRF(opcode, uint32(r), 0, uint32(p.To.Reg), REGTMP, asm)
 		}
 
-	case 26: // mov $addr/sym reg
+	case 24: // 32-bit logical op $constant reg
+		v := vregoff(ctxt, &p.From)
+		switch p.As {
+		case AANDW:
+			if uint32(v&0xffff0000) == 0xffff0000 {
+				zRI(op_NILL, uint32(p.To.Reg), uint32(v), asm)
+			} else if uint32(v&0x0000ffff) == 0x0000ffff {
+				zRI(op_NILH, uint32(p.To.Reg), uint32(v)>>16, asm)
+			} else {
+				zRIL(_a, op_NILF, uint32(p.To.Reg), uint32(v), asm)
+			}
+		case AORW:
+			if uint32(v&0xffff0000) == 0 {
+				zRI(op_OILL, uint32(p.To.Reg), uint32(v), asm)
+			} else if uint32(v&0x0000ffff) == 0 {
+				zRI(op_OILH, uint32(p.To.Reg), uint32(v)>>16, asm)
+			} else {
+				zRIL(_a, op_OILF, uint32(p.To.Reg), uint32(v), asm)
+			}
+		case AXORW:
+			zRIL(_a, op_XILF, uint32(p.To.Reg), uint32(v), asm)
+		}
+
+	case 26: // MOVD $offset(base)(index), reg
 		v := regoff(ctxt, &p.From)
 		r := p.From.Reg
 		if r == 0 {
 			r = o.param
 		}
+		i := p.From.Index
 		if v >= 0 && v < DISP12 {
-			zRX(op_LA, uint32(p.To.Reg), uint32(r), 0, uint32(v), asm)
+			zRX(op_LA, uint32(p.To.Reg), uint32(r), uint32(i), uint32(v), asm)
 		} else if v >= -DISP20/2 && v < DISP20/2 {
-			zRXY(op_LAY, uint32(p.To.Reg), uint32(r), 0, uint32(v), asm)
+			zRXY(op_LAY, uint32(p.To.Reg), uint32(r), uint32(i), uint32(v), asm)
 		} else {
 			zRIL(_a, op_LGFI, REGTMP, uint32(v), asm)
-			zRX(op_LA, uint32(p.To.Reg), uint32(r), REGTMP, 0, asm)
+			zRX(op_LA, uint32(p.To.Reg), uint32(r), REGTMP, uint32(i), asm)
 		}
 
 	case 31: // dword
@@ -3060,6 +3186,8 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 			opcode = op_LNDBR
 		case AFNEG:
 			opcode = op_LCDFR
+		case AFNEGS:
+			opcode = op_LCEBR
 		case ALEDBR:
 			opcode = op_LEDBR
 		case ALDEBR:
@@ -3144,54 +3272,32 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 			*asm = append(*asm, uint8(wd))
 		}
 
-	case 47: // arithmetic op (carry) reg [reg] reg
+	case 47: // negate [reg] reg
+		r := p.From.Reg
+		if r == 0 {
+			r = p.To.Reg
+		}
 		switch p.As {
-		default:
-
-		case AADDME:
-			r := int(p.From.Reg)
-			if p.To.Reg == p.From.Reg {
-				zRRE(op_LGR, REGTMP, uint32(p.From.Reg), asm)
-				r = REGTMP
-			}
-			zRIL(_a, op_LGFI, uint32(p.To.Reg), 0xffffffff, asm) // p.To.Reg <- -1
-			zRRE(op_ALCGR, uint32(p.To.Reg), uint32(r), asm)
-
-		case AADDZE:
-			r := int(p.From.Reg)
-			if p.To.Reg == p.From.Reg {
-				zRRE(op_LGR, REGTMP, uint32(p.From.Reg), asm)
-				r = REGTMP
-			}
-			zRRE(op_LGR, uint32(p.To.Reg), REGZERO, asm) // p.To.Reg <- 0
-			zRRE(op_ALCGR, uint32(p.To.Reg), uint32(r), asm)
-
-		case ASUBME:
-			r := int(p.From.Reg)
-			if p.To.Reg == p.From.Reg {
-				zRRE(op_LGR, REGTMP, uint32(p.From.Reg), asm)
-				r = REGTMP
-			}
-			zRIL(_a, op_LGFI, uint32(p.To.Reg), 0xffffffff, asm) // p.To.Reg <- -1
-			zRRE(op_SLBGR, uint32(p.To.Reg), uint32(r), asm)
-
-		case ASUBZE:
-			r := int(p.From.Reg)
-			if p.To.Reg == p.From.Reg {
-				zRRE(op_LGR, REGTMP, uint32(p.From.Reg), asm)
-				r = REGTMP
-			}
-			zRRE(op_LGR, uint32(p.To.Reg), REGZERO, asm) // p.To.Reg <- 0
-			zRRE(op_SLBGR, uint32(p.To.Reg), uint32(r), asm)
-
 		case ANEG:
-			r := int(p.From.Reg)
-			if r == 0 {
-				r = int(p.To.Reg)
-			}
 			zRRE(op_LCGR, uint32(p.To.Reg), uint32(r), asm)
+		case ANEGW:
+			zRRE(op_LCGFR, uint32(p.To.Reg), uint32(r), asm)
 		}
 
+	case 48: // floating-point round to integer
+		m3 := vregoff(ctxt, &p.From)
+		if 0 > m3 || m3 > 7 {
+			ctxt.Diag("mask (%v) must be in the range [0, 7]", m3)
+		}
+		var opcode uint32
+		switch p.As {
+		case AFIEBR:
+			opcode = op_FIEBR
+		case AFIDBR:
+			opcode = op_FIDBR
+		}
+		zRRF(opcode, uint32(m3), 0, uint32(p.To.Reg), uint32(p.Reg), asm)
+
 	case 67: // fmov $0 freg
 		var opcode uint32
 		switch p.As {
@@ -3227,9 +3333,15 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 				ctxt.Diag("%v overflows a uint32", v)
 			}
 		}
-		zRIL(_a, zopril(ctxt, p.As), uint32(p.From.Reg), uint32(regoff(ctxt, &p.To)), asm)
+		if p.As == ACMP && int64(int16(v)) == v {
+			zRI(op_CGHI, uint32(p.From.Reg), uint32(v), asm)
+		} else if p.As == ACMPW && int64(int16(v)) == v {
+			zRI(op_CHI, uint32(p.From.Reg), uint32(v), asm)
+		} else {
+			zRIL(_a, zopril(ctxt, p.As), uint32(p.From.Reg), uint32(v), asm)
+		}
 
-	case 72: // mov $constant/$addr mem
+	case 72: // mov $constant mem
 		v := regoff(ctxt, &p.From)
 		d := regoff(ctxt, &p.To)
 		r := p.To.Reg
@@ -3237,23 +3349,7 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 		if r == 0 {
 			r = o.param
 		}
-		if p.From.Sym != nil {
-			zRIL(_b, op_LARL, REGTMP, 0, asm)
-			if v&0x1 != 0 {
-				v -= 1
-				zRX(op_LA, REGTMP, REGTMP, 0, 1, asm)
-			}
-			addrilreloc(ctxt, p.From.Sym, int64(v))
-			if d < -DISP20/2 || d >= DISP20/2 {
-				zRIL(_a, op_LGFI, REGTMP2, uint32(d), asm)
-				if x != 0 {
-					zRRE(op_AGR, REGTMP2, uint32(x), asm)
-				}
-				d = 0
-				x = REGTMP2
-			}
-			zRXY(zopstore(ctxt, p.As), REGTMP, uint32(x), uint32(r), uint32(d), asm)
-		} else if int32(int16(v)) == v && x == 0 {
+		if int32(int16(v)) == v && x == 0 {
 			if d < 0 || d >= DISP12 {
 				if r == REGTMP || r == REGTMP2 {
 					zRIL(_a, op_AGFI, uint32(r), uint32(d), asm)
@@ -3307,16 +3403,7 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 		}
 		zRIL(_b, op_LARL, REGTMP, uint32(d), asm)
 		addrilreloc(ctxt, p.To.Sym, int64(d))
-		if p.From.Sym != nil {
-			zRIL(_b, op_LARL, REGTMP2, 0, asm)
-			a := uint32(0)
-			if v&0x1 != 0 {
-				v -= 1
-				zRX(op_LA, REGTMP2, REGTMP2, 0, 1, asm)
-			}
-			addrilrelocoffset(ctxt, p.From.Sym, int64(v), sizeRIL)
-			zRXY(zopstore(ctxt, p.As), REGTMP2, 0, REGTMP, a, asm)
-		} else if int32(int16(v)) == v {
+		if int32(int16(v)) == v {
 			var opcode uint32
 			switch p.As {
 			case AMOVD:
@@ -3800,6 +3887,39 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 			zRSY(op_LMG, uint32(rstart), uint32(rend), uint32(reg), uint32(offset), asm)
 		}
 
+	case 99: // interlocked load and op
+		if p.To.Index != 0 {
+			ctxt.Diag("cannot use indexed address")
+		}
+		offset := regoff(ctxt, &p.To)
+		if offset < -DISP20/2 || offset >= DISP20/2 {
+			ctxt.Diag("%v does not fit into 20-bit signed integer", offset)
+		}
+		var opcode uint32
+		switch p.As {
+		case ALAA:
+			opcode = op_LAA
+		case ALAAG:
+			opcode = op_LAAG
+		case ALAAL:
+			opcode = op_LAAL
+		case ALAALG:
+			opcode = op_LAALG
+		case ALAN:
+			opcode = op_LAN
+		case ALANG:
+			opcode = op_LANG
+		case ALAX:
+			opcode = op_LAX
+		case ALAXG:
+			opcode = op_LAXG
+		case ALAO:
+			opcode = op_LAO
+		case ALAOG:
+			opcode = op_LAOG
+		}
+		zRSY(opcode, uint32(p.Reg), uint32(p.From.Reg), uint32(p.To.Reg), uint32(offset), asm)
+
 	case 100: // VRX STORE
 		op, m3, _ := vop(p.As)
 		if p.From3 != nil {
@@ -3980,7 +4100,7 @@ func asmout(ctxt *obj.Link, asm *[]byte) {
 		v2 := uint32(p.From3.Reg)
 		v3 := uint32(p.From.Reg)
 		v4 := uint32(p.Reg)
-		zVRRe(op, v1, v2, v3, m5, m6, v4, asm)
+		zVRRe(op, v1, v2, v3, m6, m5, v4, asm)
 
 	case 122: // VRR-f LOAD VRS FROM GRS DISJOINT
 		op, _, _ := vop(p.As)
@@ -4039,7 +4159,7 @@ func zopload(ctxt *obj.Link, a obj.As) uint32 {
 		return op_LRVH
 	}
 
-	ctxt.Diag("unknown store opcode %v", obj.Aconv(a))
+	ctxt.Diag("unknown store opcode %v", a)
 	return 0
 }
 
@@ -4071,7 +4191,7 @@ func zopstore(ctxt *obj.Link, a obj.As) uint32 {
 		return op_STRVH
 	}
 
-	ctxt.Diag("unknown store opcode %v", obj.Aconv(a))
+	ctxt.Diag("unknown store opcode %v", a)
 	return 0
 }
 
@@ -4089,7 +4209,7 @@ func zoprre(ctxt *obj.Link, a obj.As) uint32 {
 	case ACEBR:
 		return op_CEBR
 	}
-	ctxt.Diag("unknown rre opcode %v", obj.Aconv(a))
+	ctxt.Diag("unknown rre opcode %v", a)
 	return 0
 }
 
@@ -4101,7 +4221,7 @@ func zoprr(ctxt *obj.Link, a obj.As) uint32 {
 	case ACMPWU:
 		return op_CLR
 	}
-	ctxt.Diag("unknown rr opcode %v", obj.Aconv(a))
+	ctxt.Diag("unknown rr opcode %v", a)
 	return 0
 }
 
@@ -4117,7 +4237,7 @@ func zopril(ctxt *obj.Link, a obj.As) uint32 {
 	case ACMPWU:
 		return op_CLFI
 	}
-	ctxt.Diag("unknown ril opcode %v", obj.Aconv(a))
+	ctxt.Diag("unknown ril opcode %v", a)
 	return 0
 }
 
diff --git a/src/cmd/internal/obj/s390x/objz.go b/src/cmd/internal/obj/s390x/objz.go
index 0526a35..fca8f85 100644
--- a/src/cmd/internal/obj/s390x/objz.go
+++ b/src/cmd/internal/obj/s390x/objz.go
@@ -66,7 +66,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 			s.Size = 4
 			p.From.Type = obj.TYPE_MEM
 			p.From.Sym = s
-			p.From.Sym.Local = true
+			p.From.Sym.Set(obj.AttrLocal, true)
 			p.From.Name = obj.NAME_EXTERN
 			p.From.Offset = 0
 		}
@@ -82,7 +82,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 			s.Size = 8
 			p.From.Type = obj.TYPE_MEM
 			p.From.Sym = s
-			p.From.Sym.Local = true
+			p.From.Sym.Set(obj.AttrLocal, true)
 			p.From.Name = obj.NAME_EXTERN
 			p.From.Offset = 0
 		}
@@ -99,7 +99,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 				s.Size = 8
 				p.From.Type = obj.TYPE_MEM
 				p.From.Sym = s
-				p.From.Sym.Local = true
+				p.From.Sym.Set(obj.AttrLocal, true)
 				p.From.Name = obj.NAME_EXTERN
 				p.From.Offset = 0
 			}
@@ -109,13 +109,13 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 	// Rewrite SUB constants into ADD.
 	switch p.As {
 	case ASUBC:
-		if p.From.Type == obj.TYPE_CONST {
+		if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
 			p.From.Offset = -p.From.Offset
 			p.As = AADDC
 		}
 
 	case ASUB:
-		if p.From.Type == obj.TYPE_CONST {
+		if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
 			p.From.Offset = -p.From.Offset
 			p.As = AADD
 		}
@@ -137,7 +137,7 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 	// We only care about global data: NAME_EXTERN means a global
 	// symbol in the Go sense, and p.Sym.Local is true for a few
 	// internally defined symbols.
-	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
+	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
 		// MOVD $sym, Rx becomes MOVD sym at GOT, Rx
 		// MOVD $sym+<off>, Rx becomes MOVD sym at GOT, Rx; ADD <off>, Rx
 		if p.To.Type != obj.TYPE_REG || p.As != AMOVD {
@@ -162,12 +162,12 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 	// MOVD sym, Ry becomes MOVD sym at GOT, REGTMP; MOVD (REGTMP), Ry
 	// MOVD Ry, sym becomes MOVD sym at GOT, REGTMP; MOVD Ry, (REGTMP)
 	// An addition may be inserted between the two MOVs if there is an offset.
-	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
-		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
+	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
+		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
 		}
 		source = &p.From
-	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
+	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 		source = &p.To
 	} else {
 		return
@@ -242,9 +242,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 	 * expand BECOME pseudo
 	 */
 	if ctxt.Debugvlog != 0 {
-		fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime())
+		ctxt.Logf("%5.2f noops\n", obj.Cputime())
 	}
-	ctxt.Bso.Flush()
 
 	var q *obj.Prog
 	var q1 *obj.Prog
@@ -259,14 +258,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				p.Link.Mark |= LABEL
 			}
 
-		case ANOR:
-			q = p
-			if p.To.Type == obj.TYPE_REG {
-				if p.To.Reg == REGZERO {
-					p.Mark |= LABEL | SYNC
-				}
-			}
-
 		case ASYNC,
 			AWORD:
 			q = p
@@ -313,6 +304,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			ABGT,
 			ABLE,
 			ABLT,
+			ABLEU,
+			ABLTU,
 			ABNE,
 			ABR,
 			ABVC,
@@ -387,7 +380,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 		case obj.ATEXT:
 			autosize = int32(textstksiz)
 
-			if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 {
+			if p.Mark&LEAF != 0 && autosize == 0 {
 				// A leaf function with no locals has no frame.
 				p.From3.Offset |= obj.NOFRAME
 			}
@@ -398,6 +391,12 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				autosize += int32(ctxt.FixedFrameSize())
 			}
 
+			if p.Mark&LEAF != 0 && autosize < obj.StackSmall {
+				// A leaf function with a small stack can be marked
+				// NOSPLIT, avoiding a stack check.
+				p.From3.Offset |= obj.NOSPLIT
+			}
+
 			p.To.Offset = int64(autosize)
 
 			q = p
@@ -409,8 +408,21 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			}
 
 			if autosize != 0 {
+				// Make sure to save link register for non-empty frame, even if
+				// it is a leaf function, so that traceback works.
+				// Store link register before decrementing SP, so if a signal comes
+				// during the execution of the function prologue, the traceback
+				// code will not see a half-updated stack frame.
 				q = obj.Appendp(ctxt, p)
 				q.As = AMOVD
+				q.From.Type = obj.TYPE_REG
+				q.From.Reg = REG_LR
+				q.To.Type = obj.TYPE_MEM
+				q.To.Reg = REGSP
+				q.To.Offset = int64(-autosize)
+
+				q = obj.Appendp(ctxt, q)
+				q.As = AMOVD
 				q.From.Type = obj.TYPE_ADDR
 				q.From.Offset = int64(-autosize)
 				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
@@ -425,23 +437,15 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			}
 
 			if cursym.Text.Mark&LEAF != 0 {
-				cursym.Leaf = true
+				cursym.Set(obj.AttrLeaf, true)
 				break
 			}
 
-			q = obj.Appendp(ctxt, q)
-			q.As = AMOVD
-			q.From.Type = obj.TYPE_REG
-			q.From.Reg = REG_LR
-			q.To.Type = obj.TYPE_MEM
-			q.To.Reg = REGSP
-			q.To.Offset = 0
-
 			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
 				//
 				//	MOVD g_panic(g), R3
-				//	CMP R0, R3
+				//	CMP R3, $0
 				//	BEQ end
 				//	MOVD panic_argp(R3), R4
 				//	ADD $(autosize+8), R1, R5
@@ -467,9 +471,9 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 				q = obj.Appendp(ctxt, q)
 				q.As = ACMP
 				q.From.Type = obj.TYPE_REG
-				q.From.Reg = REG_R0
-				q.To.Type = obj.TYPE_REG
-				q.To.Reg = REG_R3
+				q.From.Reg = REG_R3
+				q.To.Type = obj.TYPE_CONST
+				q.To.Offset = 0
 
 				q = obj.Appendp(ctxt, q)
 				q.As = ABEQ
@@ -606,7 +610,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 		}
 	}
 	if wasSplit {
-		pLast = stacksplitPost(ctxt, pLast, pPre, pPreempt) // emit post part of split check
+		pLast = stacksplitPost(ctxt, pLast, pPre, pPreempt, autosize) // emit post part of split check
 	}
 }
 
@@ -665,7 +669,7 @@ func stacksplitPre(ctxt *obj.Link, p *obj.Prog, framesize int32) (*obj.Prog, *ob
 	p.From.Type = obj.TYPE_MEM
 	p.From.Reg = REGG
 	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
-	if ctxt.Cursym.Cfunc {
+	if ctxt.Cursym.CFunc() {
 		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
 	}
 	p.To.Type = obj.TYPE_REG
@@ -782,10 +786,25 @@ func stacksplitPre(ctxt *obj.Link, p *obj.Prog, framesize int32) (*obj.Prog, *ob
 	return p, q
 }
 
-func stacksplitPost(ctxt *obj.Link, p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog) *obj.Prog {
+func stacksplitPost(ctxt *obj.Link, p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
+	// Now we are at the end of the function, but logically
+	// we are still in function prologue. We need to fix the
+	// SP data and PCDATA.
+	spfix := obj.Appendp(ctxt, p)
+	spfix.As = obj.ANOP
+	spfix.Spadj = -framesize
+
+	pcdata := obj.Appendp(ctxt, spfix)
+	pcdata.Lineno = ctxt.Cursym.Text.Lineno
+	pcdata.Mode = ctxt.Cursym.Text.Mode
+	pcdata.As = obj.APCDATA
+	pcdata.From.Type = obj.TYPE_CONST
+	pcdata.From.Offset = obj.PCDATA_StackMapIndex
+	pcdata.To.Type = obj.TYPE_CONST
+	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
 
 	// MOVD	LR, R5
-	p = obj.Appendp(ctxt, p)
+	p = obj.Appendp(ctxt, pcdata)
 	pPre.Pcond = p
 	p.As = AMOVD
 	p.From.Type = obj.TYPE_REG
@@ -801,7 +820,7 @@ func stacksplitPost(ctxt *obj.Link, p *obj.Prog, pPre *obj.Prog, pPreempt *obj.P
 
 	p.As = ABL
 	p.To.Type = obj.TYPE_BRANCH
-	if ctxt.Cursym.Cfunc {
+	if ctxt.Cursym.CFunc() {
 		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
 	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
 		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
@@ -994,6 +1013,7 @@ var unaryDst = map[obj.As]bool{
 	ASTCKE: true,
 	ASTCKF: true,
 	ANEG:   true,
+	ANEGW:  true,
 	AVONE:  true,
 	AVZERO: true,
 }
diff --git a/src/cmd/internal/obj/sizeof_test.go b/src/cmd/internal/obj/sizeof_test.go
index f7173d3..b8a3c54 100644
--- a/src/cmd/internal/obj/sizeof_test.go
+++ b/src/cmd/internal/obj/sizeof_test.go
@@ -22,9 +22,9 @@ func TestSizeof(t *testing.T) {
 		_32bit uintptr     // size on 32bit platforms
 		_64bit uintptr     // size on 64bit platforms
 	}{
-		{Addr{}, 52, 80},
-		{LSym{}, 80, 136},
-		{Prog{}, 196, 288},
+		{Addr{}, 40, 64},
+		{LSym{}, 76, 128},
+		{Prog{}, 144, 224},
 	}
 
 	for _, tt := range tests {
diff --git a/src/cmd/internal/obj/stack.go b/src/cmd/internal/obj/stack.go
index 712a10f..687adf2 100644
--- a/src/cmd/internal/obj/stack.go
+++ b/src/cmd/internal/obj/stack.go
@@ -11,7 +11,7 @@ const (
 	STACKSYSTEM = 0
 	StackSystem = STACKSYSTEM
 	StackBig    = 4096
-	StackGuard  = 720*stackGuardMultiplier + StackSystem
+	StackGuard  = 880*stackGuardMultiplier + StackSystem
 	StackSmall  = 128
 	StackLimit  = StackGuard - StackSystem - StackSmall
 )
diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go
index e974ca8..84de5b6 100644
--- a/src/cmd/internal/obj/sym.go
+++ b/src/cmd/internal/obj/sym.go
@@ -1,6 +1,6 @@
 // Derived from Inferno utils/6l/obj.c and utils/6l/span.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -32,56 +32,16 @@
 package obj
 
 import (
-	"cmd/internal/sys"
 	"log"
 	"os"
 	"path/filepath"
-	"strconv"
 )
 
-var headers = []struct {
-	name string
-	val  int
-}{
-	{"darwin", Hdarwin},
-	{"dragonfly", Hdragonfly},
-	{"freebsd", Hfreebsd},
-	{"linux", Hlinux},
-	{"android", Hlinux}, // must be after "linux" entry or else headstr(Hlinux) == "android"
-	{"nacl", Hnacl},
-	{"netbsd", Hnetbsd},
-	{"openbsd", Hopenbsd},
-	{"plan9", Hplan9},
-	{"solaris", Hsolaris},
-	{"windows", Hwindows},
-	{"windowsgui", Hwindows},
-}
-
-func headtype(name string) int {
-	for i := 0; i < len(headers); i++ {
-		if name == headers[i].name {
-			return headers[i].val
-		}
-	}
-	return -1
-}
-
-func Headstr(v int) string {
-	for i := 0; i < len(headers); i++ {
-		if v == headers[i].val {
-			return headers[i].name
-		}
-	}
-	return strconv.Itoa(v)
-}
-
 func Linknew(arch *LinkArch) *Link {
 	ctxt := new(Link)
 	ctxt.Hash = make(map[SymVer]*LSym)
 	ctxt.Arch = arch
 	ctxt.Version = HistVersion
-	ctxt.Goroot = Getgoroot()
-	ctxt.Goroot_final = os.Getenv("GOROOT_FINAL")
 
 	var buf string
 	buf, _ = os.Getwd()
@@ -91,22 +51,16 @@ func Linknew(arch *LinkArch) *Link {
 	buf = filepath.ToSlash(buf)
 	ctxt.Pathname = buf
 
-	ctxt.LineHist.GOROOT = ctxt.Goroot
-	ctxt.LineHist.GOROOT_FINAL = ctxt.Goroot_final
+	ctxt.LineHist.GOROOT = GOROOT
 	ctxt.LineHist.Dir = ctxt.Pathname
 
-	ctxt.Headtype = headtype(Getgoos())
+	ctxt.Headtype.Set(GOOS)
 	if ctxt.Headtype < 0 {
-		log.Fatalf("unknown goos %s", Getgoos())
-	}
-
-	// On arm, record goarm.
-	if ctxt.Arch.Family == sys.ARM {
-		ctxt.Goarm = Getgoarm()
+		log.Fatalf("unknown goos %s", GOOS)
 	}
 
 	ctxt.Flag_optimize = true
-	ctxt.Framepointer_enabled = Framepointer_enabled(Getgoos(), arch.Name)
+	ctxt.Framepointer_enabled = Framepointer_enabled(GOOS, arch.Name)
 	return ctxt
 }
 
diff --git a/src/cmd/internal/obj/symkind_string.go b/src/cmd/internal/obj/symkind_string.go
new file mode 100644
index 0000000..fef8c35
--- /dev/null
+++ b/src/cmd/internal/obj/symkind_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=SymKind"; DO NOT EDIT
+
+package obj
+
+import "fmt"
+
+const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFO"
+
+var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 214, 220, 229, 237, 244, 254, 262, 267, 271, 280, 287, 292, 304, 316, 333, 350, 355, 364, 370, 380, 388, 398, 408}
+
+func (i SymKind) String() string {
+	if i < 0 || i >= SymKind(len(_SymKind_index)-1) {
+		return fmt.Sprintf("SymKind(%d)", i)
+	}
+	return _SymKind_name[_SymKind_index[i]:_SymKind_index[i+1]]
+}
diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go
index 18813c3..bc5d1c5 100644
--- a/src/cmd/internal/obj/util.go
+++ b/src/cmd/internal/obj/util.go
@@ -31,19 +31,16 @@ func envOr(key, value string) string {
 	return value
 }
 
-func Getgoroot() string {
-	return envOr("GOROOT", defaultGOROOT)
-}
-
-func Getgoarch() string {
-	return envOr("GOARCH", defaultGOARCH)
-}
-
-func Getgoos() string {
-	return envOr("GOOS", defaultGOOS)
-}
+var (
+	GOROOT  = envOr("GOROOT", defaultGOROOT)
+	GOARCH  = envOr("GOARCH", defaultGOARCH)
+	GOOS    = envOr("GOOS", defaultGOOS)
+	GO386   = envOr("GO386", defaultGO386)
+	GOARM   = goarm()
+	Version = version
+)
 
-func Getgoarm() int32 {
+func goarm() int {
 	switch v := envOr("GOARM", defaultGOARM); v {
 	case "5":
 		return 5
@@ -57,19 +54,10 @@ func Getgoarm() int32 {
 	panic("unreachable")
 }
 
-func Getgo386() string {
-	// Validated by cmd/compile.
-	return envOr("GO386", defaultGO386)
-}
-
 func Getgoextlinkenabled() string {
 	return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED)
 }
 
-func Getgoversion() string {
-	return version
-}
-
 func (p *Prog) Line() string {
 	return p.Ctxt.LineHist.LineString(int(p.Lineno))
 }
@@ -138,7 +126,7 @@ func (p *Prog) String() string {
 
 	var buf bytes.Buffer
 
-	fmt.Fprintf(&buf, "%.5d (%v)\t%v%s", p.Pc, p.Line(), Aconv(p.As), sc)
+	fmt.Fprintf(&buf, "%.5d (%v)\t%v%s", p.Pc, p.Line(), p.As, sc)
 	sep := "\t"
 	quadOpAmd64 := p.RegTo2 == -1
 	if quadOpAmd64 {
@@ -155,7 +143,7 @@ func (p *Prog) String() string {
 		sep = ", "
 	}
 	if p.From3Type() != TYPE_NONE {
-		if p.From3.Type == TYPE_CONST && (p.As == ATEXT || p.As == AGLOBL) {
+		if p.From3.Type == TYPE_CONST && p.As == ATEXT {
 			// Special case - omit $.
 			fmt.Fprintf(&buf, "%s%d", sep, p.From3.Offset)
 		} else if quadOpAmd64 {
@@ -252,9 +240,6 @@ func Dconv(p *Prog, a *Addr) string {
 		if a.Index != REG_NONE {
 			str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
 		}
-		if p != nil && p.As == ATYPE && a.Gotype != nil {
-			str += fmt.Sprintf("%s", a.Gotype.Name)
-		}
 
 	case TYPE_CONST:
 		if a.Reg != 0 {
@@ -286,14 +271,23 @@ func Dconv(p *Prog, a *Addr) string {
 
 	case TYPE_SHIFT:
 		v := int(a.Offset)
-		op := "<<>>->@>"[((v>>5)&3)<<1:]
-		if v&(1<<4) != 0 {
-			str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
-		} else {
-			str = fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
-		}
-		if a.Reg != 0 {
-			str += fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
+		ops := "<<>>->@>"
+		switch GOARCH {
+		case "arm":
+			op := ops[((v>>5)&3)<<1:]
+			if v&(1<<4) != 0 {
+				str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
+			} else {
+				str = fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
+			}
+			if a.Reg != 0 {
+				str += fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
+			}
+		case "arm64":
+			op := ops[((v>>22)&3)<<1:]
+			str = fmt.Sprintf("R%d%c%c%d", (v>>16)&31, op[0], op[1], (v>>10)&63)
+		default:
+			panic("TYPE_SHIFT is not supported on " + GOARCH)
 		}
 
 	case TYPE_REGREG:
@@ -390,13 +384,13 @@ var regSpace []regSet
 const (
 	// Because of masking operations in the encodings, each register
 	// space should start at 0 modulo some power of 2.
-	RBase386    = 1 * 1024
-	RBaseAMD64  = 2 * 1024
-	RBaseARM    = 3 * 1024
-	RBasePPC64  = 4 * 1024  // range [4k, 8k)
-	RBaseARM64  = 8 * 1024  // range [8k, 13k)
-	RBaseMIPS64 = 13 * 1024 // range [13k, 14k)
-	RBaseS390X  = 14 * 1024 // range [14k, 15k)
+	RBase386   = 1 * 1024
+	RBaseAMD64 = 2 * 1024
+	RBaseARM   = 3 * 1024
+	RBasePPC64 = 4 * 1024  // range [4k, 8k)
+	RBaseARM64 = 8 * 1024  // range [8k, 13k)
+	RBaseMIPS  = 13 * 1024 // range [13k, 14k)
+	RBaseS390X = 14 * 1024 // range [14k, 15k)
 )
 
 // RegisterRegister binds a pretty-printer (Rconv) for register
@@ -453,10 +447,13 @@ var aSpace []opSet
 // RegisterOpcode binds a list of instruction names
 // to a given instruction number range.
 func RegisterOpcode(lo As, Anames []string) {
+	if len(Anames) > AllowedOpCodes {
+		panic(fmt.Sprintf("too many instructions, have %d max %d", len(Anames), AllowedOpCodes))
+	}
 	aSpace = append(aSpace, opSet{lo, Anames})
 }
 
-func Aconv(a As) string {
+func (a As) String() string {
 	if 0 <= a && int(a) < len(Anames) {
 		return Anames[a]
 	}
@@ -472,12 +469,10 @@ func Aconv(a As) string {
 var Anames = []string{
 	"XXX",
 	"CALL",
-	"CHECKNIL",
 	"DUFFCOPY",
 	"DUFFZERO",
 	"END",
 	"FUNCDATA",
-	"GLOBL",
 	"JMP",
 	"NOP",
 	"PCDATA",
@@ -492,8 +487,13 @@ var Anames = []string{
 }
 
 func Bool2int(b bool) int {
+	// The compiler currently only optimizes this form.
+	// See issue 6011.
+	var i int
 	if b {
-		return 1
+		i = 1
+	} else {
+		i = 0
 	}
-	return 0
+	return i
 }
diff --git a/src/cmd/internal/obj/x86/a.out.go b/src/cmd/internal/obj/x86/a.out.go
index ab1dabc..02f92ed 100644
--- a/src/cmd/internal/obj/x86/a.out.go
+++ b/src/cmd/internal/obj/x86/a.out.go
@@ -1,5 +1,5 @@
 // Inferno utils/6c/6.out.h
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/6.out.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/6.out.h
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -771,6 +771,9 @@ const (
 	AROUNDSS
 	AROUNDPD
 	AROUNDSD
+	AMOVDDUP
+	AMOVSHDUP
+	AMOVSLDUP
 
 	APSHUFD
 	APCLMULQDQ
@@ -803,6 +806,11 @@ const (
 	AVPERM2I128
 	ARORXL
 	ARORXQ
+	AVBROADCASTSS
+	AVBROADCASTSD
+	AVMOVDDUP
+	AVMOVSHDUP
+	AVMOVSLDUP
 
 	// from 386
 	AJCXZW
diff --git a/src/cmd/internal/obj/x86/anames.go b/src/cmd/internal/obj/x86/anames.go
index 3b30154..8c5be80 100644
--- a/src/cmd/internal/obj/x86/anames.go
+++ b/src/cmd/internal/obj/x86/anames.go
@@ -708,6 +708,9 @@ var Anames = []string{
 	"ROUNDSS",
 	"ROUNDPD",
 	"ROUNDSD",
+	"MOVDDUP",
+	"MOVSHDUP",
+	"MOVSLDUP",
 	"PSHUFD",
 	"PCLMULQDQ",
 	"VZEROUPPER",
@@ -738,6 +741,11 @@ var Anames = []string{
 	"VPERM2I128",
 	"RORXL",
 	"RORXQ",
+	"VBROADCASTSS",
+	"VBROADCASTSD",
+	"VMOVDDUP",
+	"VMOVSHDUP",
+	"VMOVSLDUP",
 	"JCXZW",
 	"FCMOVCC",
 	"FCMOVCS",
diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go
index 676da40..bf67822 100644
--- a/src/cmd/internal/obj/x86/asm6.go
+++ b/src/cmd/internal/obj/x86/asm6.go
@@ -1,5 +1,5 @@
 // Inferno utils/6l/span.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -56,7 +56,7 @@ const (
 	//
 	LoopAlign  = 16
 	MaxLoopPad = 0
-	FuncAlign  = 16
+	funcAlign  = 16
 )
 
 type Optab struct {
@@ -352,14 +352,6 @@ var yxorb = []ytab{
 	{Ymb, Ynone, Yrb, Zm_r, 1},
 }
 
-var yxorl = []ytab{
-	{Yi8, Ynone, Yml, Zibo_m, 2},
-	{Yi32, Ynone, Yax, Zil_, 1},
-	{Yi32, Ynone, Yml, Zilo_m, 2},
-	{Yrl, Ynone, Yml, Zr_m, 1},
-	{Yml, Ynone, Yrl, Zm_r, 1},
-}
-
 var yaddl = []ytab{
 	{Yi8, Ynone, Yml, Zibo_m, 2},
 	{Yi32, Ynone, Yax, Zil_, 1},
@@ -368,14 +360,6 @@ var yaddl = []ytab{
 	{Yml, Ynone, Yrl, Zm_r, 1},
 }
 
-var yincb = []ytab{
-	{Ynone, Ynone, Ymb, Zo_m, 2},
-}
-
-var yincw = []ytab{
-	{Ynone, Ynone, Yml, Zo_m, 2},
-}
-
 var yincl = []ytab{
 	{Ynone, Ynone, Yrl, Z_rp, 1},
 	{Ynone, Ynone, Yml, Zo_m, 2},
@@ -413,13 +397,6 @@ var yshl = []ytab{
 	{Ycx, Ynone, Yml, Zo_m, 2},
 }
 
-var ytestb = []ytab{
-	{Yi32, Ynone, Yal, Zib_, 1},
-	{Yi32, Ynone, Ymb, Zibo_m, 2},
-	{Yrb, Ynone, Ymb, Zr_m, 1},
-	{Ymb, Ynone, Yrb, Zm_r, 1},
-}
-
 var ytestl = []ytab{
 	{Yi32, Ynone, Yax, Zil_, 1},
 	{Yi32, Ynone, Yml, Zilo_m, 2},
@@ -434,10 +411,6 @@ var ymovb = []ytab{
 	{Yi32, Ynone, Ymb, Zibo_m, 2},
 }
 
-var ymbs = []ytab{
-	{Ymb, Ynone, Ynone, Zm_o, 2},
-}
-
 var ybtl = []ytab{
 	{Yi8, Ynone, Yml, Zibo_m, 2},
 	{Yrl, Ynone, Yml, Zr_m, 1},
@@ -643,10 +616,6 @@ var yfadd = []ytab{
 	{Yf0, Ynone, Yrf, Zo_m, 2},
 }
 
-var yfaddp = []ytab{
-	{Yf0, Ynone, Yrf, Zo_m, 2},
-}
-
 var yfxch = []ytab{
 	{Yf0, Ynone, Yrf, Zo_m, 2},
 	{Yrf, Ynone, Yf0, Zm_o, 2},
@@ -661,11 +630,6 @@ var ystsw = []ytab{
 	{Ynone, Ynone, Yax, Zlit, 1},
 }
 
-var ystcw = []ytab{
-	{Ynone, Ynone, Ym, Zo_m, 2},
-	{Ym, Ynone, Ynone, Zm_o, 2},
-}
-
 var ysvrs = []ytab{
 	{Ynone, Ynone, Ym, Zo_m, 2},
 	{Ym, Ynone, Ynone, Zm_o, 2},
@@ -694,12 +658,6 @@ var yxcvm2 = []ytab{
 	{Ymm, Ynone, Yxr, Zm_r_xm, 2},
 }
 
-/*
-var yxmq = []ytab{
-	{Yxm, Ynone, Yxr, Zm_r_xm, 2},
-}
-*/
-
 var yxr = []ytab{
 	{Yxr, Ynone, Yxr, Zm_r_xm, 1},
 }
@@ -716,10 +674,6 @@ var ymr_ml = []ytab{
 	{Ymr, Ynone, Yml, Zr_m_xm, 1},
 }
 
-var yxcmp = []ytab{
-	{Yxm, Ynone, Yxr, Zm_r_xm, 1},
-}
-
 var yxcmpi = []ytab{
 	{Yxm, Yxr, Yi8, Zm_r_i_xm, 2},
 }
@@ -810,10 +764,6 @@ var yaes = []ytab{
 	{Yxm, Ynone, Yxr, Zlitm_r, 2},
 }
 
-var yaes2 = []ytab{
-	{Yu8, Yxm, Yxr, Zibm_r, 2},
-}
-
 var yxbegin = []ytab{
 	{Ynone, Ynone, Ybr, Zjmp, 1},
 }
@@ -855,6 +805,8 @@ var yvex_ri3 = []ytab{
 }
 
 var yvex_xyi3 = []ytab{
+	{Yu8, Yxm, Yxr, Zvex_i_rm_r, 2},
+	{Yu8, Yym, Yyr, Zvex_i_rm_r, 2},
 	{Yi8, Yxm, Yxr, Zvex_i_rm_r, 2},
 	{Yi8, Yym, Yyr, Zvex_i_rm_r, 2},
 }
@@ -881,12 +833,10 @@ var yvex_shift_dq = []ytab{
 
 var yvex_r3 = []ytab{
 	{Yml, Yrl, Yrl, Zvex_rm_v_r, 2},
-	{Yml, Yrl, Yrl, Zvex_rm_v_r, 2},
 }
 
 var yvex_vmr3 = []ytab{
 	{Yrl, Yml, Yrl, Zvex_v_rm_r, 2},
-	{Yrl, Yml, Yrl, Zvex_v_rm_r, 2},
 }
 
 var yvex_xy2 = []ytab{
@@ -916,6 +866,10 @@ var yvex_vpbroadcast = []ytab{
 	{Yxm, Ynone, Yyr, Zvex_rm_v_r, 2},
 }
 
+var yvex_vpbroadcast_sd = []ytab{
+	{Yxm, Ynone, Yyr, Zvex_rm_v_r, 2},
+}
+
 var ymmxmm0f38 = []ytab{
 	{Ymm, Ynone, Ymr, Zlitm_r, 3},
 	{Yxm, Ynone, Yxr, Zlitm_r, 5},
@@ -988,9 +942,9 @@ var optab =
 	{AAAM, ynone, P32, [23]uint8{0xd4, 0x0a}},
 	{AAAS, ynone, P32, [23]uint8{0x3f}},
 	{AADCB, yxorb, Pb, [23]uint8{0x14, 0x80, 02, 0x10, 0x10}},
-	{AADCL, yxorl, Px, [23]uint8{0x83, 02, 0x15, 0x81, 02, 0x11, 0x13}},
-	{AADCQ, yxorl, Pw, [23]uint8{0x83, 02, 0x15, 0x81, 02, 0x11, 0x13}},
-	{AADCW, yxorl, Pe, [23]uint8{0x83, 02, 0x15, 0x81, 02, 0x11, 0x13}},
+	{AADCL, yaddl, Px, [23]uint8{0x83, 02, 0x15, 0x81, 02, 0x11, 0x13}},
+	{AADCQ, yaddl, Pw, [23]uint8{0x83, 02, 0x15, 0x81, 02, 0x11, 0x13}},
+	{AADCW, yaddl, Pe, [23]uint8{0x83, 02, 0x15, 0x81, 02, 0x11, 0x13}},
 	{AADDB, yxorb, Pb, [23]uint8{0x04, 0x80, 00, 0x00, 0x02}},
 	{AADDL, yaddl, Px, [23]uint8{0x83, 00, 0x05, 0x81, 00, 0x01, 0x03}},
 	{AADDPD, yxm, Pq, [23]uint8{0x58}},
@@ -1001,13 +955,13 @@ var optab =
 	{AADDW, yaddl, Pe, [23]uint8{0x83, 00, 0x05, 0x81, 00, 0x01, 0x03}},
 	{AADJSP, nil, 0, [23]uint8{}},
 	{AANDB, yxorb, Pb, [23]uint8{0x24, 0x80, 04, 0x20, 0x22}},
-	{AANDL, yxorl, Px, [23]uint8{0x83, 04, 0x25, 0x81, 04, 0x21, 0x23}},
+	{AANDL, yaddl, Px, [23]uint8{0x83, 04, 0x25, 0x81, 04, 0x21, 0x23}},
 	{AANDNPD, yxm, Pq, [23]uint8{0x55}},
 	{AANDNPS, yxm, Pm, [23]uint8{0x55}},
 	{AANDPD, yxm, Pq, [23]uint8{0x54}},
 	{AANDPS, yxm, Pq, [23]uint8{0x54}},
-	{AANDQ, yxorl, Pw, [23]uint8{0x83, 04, 0x25, 0x81, 04, 0x21, 0x23}},
-	{AANDW, yxorl, Pe, [23]uint8{0x83, 04, 0x25, 0x81, 04, 0x21, 0x23}},
+	{AANDQ, yaddl, Pw, [23]uint8{0x83, 04, 0x25, 0x81, 04, 0x21, 0x23}},
+	{AANDW, yaddl, Pe, [23]uint8{0x83, 04, 0x25, 0x81, 04, 0x21, 0x23}},
 	{AARPL, yrl_ml, P32, [23]uint8{0x63}},
 	{ABOUNDL, yrl_m, P32, [23]uint8{0x62}},
 	{ABOUNDW, yrl_m, Pe, [23]uint8{0x62}},
@@ -1099,8 +1053,8 @@ var optab =
 	{ACMPSS, yxcmpi, Px, [23]uint8{Pf3, 0xc2}},
 	{ACMPSW, ynone, Pe, [23]uint8{0xa7}},
 	{ACMPW, ycmpl, Pe, [23]uint8{0x83, 07, 0x3d, 0x81, 07, 0x39, 0x3b}},
-	{ACOMISD, yxcmp, Pe, [23]uint8{0x2f}},
-	{ACOMISS, yxcmp, Pm, [23]uint8{0x2f}},
+	{ACOMISD, yxm, Pe, [23]uint8{0x2f}},
+	{ACOMISS, yxm, Pm, [23]uint8{0x2f}},
 	{ACPUID, ynone, Pm, [23]uint8{0xa2}},
 	{ACVTPL2PD, yxcvm2, Px, [23]uint8{Pf3, 0xe6, Pe, 0x2a}},
 	{ACVTPL2PS, yxcvm2, Pm, [23]uint8{0x5b, 0, 0x2a, 0}},
@@ -1128,10 +1082,10 @@ var optab =
 	{ACQO, ynone, Pw, [23]uint8{0x99}},
 	{ADAA, ynone, P32, [23]uint8{0x27}},
 	{ADAS, ynone, P32, [23]uint8{0x2f}},
-	{ADECB, yincb, Pb, [23]uint8{0xfe, 01}},
+	{ADECB, yscond, Pb, [23]uint8{0xfe, 01}},
 	{ADECL, yincl, Px1, [23]uint8{0x48, 0xff, 01}},
 	{ADECQ, yincq, Pw, [23]uint8{0xff, 01}},
-	{ADECW, yincw, Pe, [23]uint8{0xff, 01}},
+	{ADECW, yincq, Pe, [23]uint8{0xff, 01}},
 	{ADIVB, ydivb, Pb, [23]uint8{0xf6, 06}},
 	{ADIVL, ydivl, Px, [23]uint8{0xf7, 06}},
 	{ADIVPD, yxm, Pe, [23]uint8{0x5e}},
@@ -1146,7 +1100,6 @@ var optab =
 	{AFXSAVE, ysvrs, Pm, [23]uint8{0xae, 00, 0xae, 00}},
 	{AFXRSTOR64, ysvrs, Pw, [23]uint8{0x0f, 0xae, 01, 0x0f, 0xae, 01}},
 	{AFXSAVE64, ysvrs, Pw, [23]uint8{0x0f, 0xae, 00, 0x0f, 0xae, 00}},
-	{obj.AGLOBL, nil, 0, [23]uint8{}},
 	{AHLT, ynone, Px, [23]uint8{0xf4}},
 	{AIDIVB, ydivb, Pb, [23]uint8{0xf6, 07}},
 	{AIDIVL, ydivl, Px, [23]uint8{0xf7, 07}},
@@ -1158,10 +1111,10 @@ var optab =
 	{AIMULW, yimul, Pe, [23]uint8{0xf7, 05, 0x6b, 0x69, Pm, 0xaf}},
 	{AIMUL3Q, yimul3, Pw, [23]uint8{0x6b, 00}},
 	{AINB, yin, Pb, [23]uint8{0xe4, 0xec}},
-	{AINCB, yincb, Pb, [23]uint8{0xfe, 00}},
+	{AINCB, yscond, Pb, [23]uint8{0xfe, 00}},
 	{AINCL, yincl, Px1, [23]uint8{0x40, 0xff, 00}},
 	{AINCQ, yincq, Pw, [23]uint8{0xff, 00}},
-	{AINCW, yincw, Pe, [23]uint8{0xff, 00}},
+	{AINCW, yincq, Pe, [23]uint8{0xff, 00}},
 	{AINL, yin, Px, [23]uint8{0xe5, 0xed}},
 	{AINSB, ynone, Pb, [23]uint8{0x6c}},
 	{AINSL, ynone, Px, [23]uint8{0x6d}},
@@ -1287,11 +1240,11 @@ var optab =
 	{ANOTQ, yscond, Pw, [23]uint8{0xf7, 02}},
 	{ANOTW, yscond, Pe, [23]uint8{0xf7, 02}},
 	{AORB, yxorb, Pb, [23]uint8{0x0c, 0x80, 01, 0x08, 0x0a}},
-	{AORL, yxorl, Px, [23]uint8{0x83, 01, 0x0d, 0x81, 01, 0x09, 0x0b}},
+	{AORL, yaddl, Px, [23]uint8{0x83, 01, 0x0d, 0x81, 01, 0x09, 0x0b}},
 	{AORPD, yxm, Pq, [23]uint8{0x56}},
 	{AORPS, yxm, Pm, [23]uint8{0x56}},
-	{AORQ, yxorl, Pw, [23]uint8{0x83, 01, 0x0d, 0x81, 01, 0x09, 0x0b}},
-	{AORW, yxorl, Pe, [23]uint8{0x83, 01, 0x0d, 0x81, 01, 0x09, 0x0b}},
+	{AORQ, yaddl, Pw, [23]uint8{0x83, 01, 0x0d, 0x81, 01, 0x09, 0x0b}},
+	{AORW, yaddl, Pe, [23]uint8{0x83, 01, 0x0d, 0x81, 01, 0x09, 0x0b}},
 	{AOUTB, yin, Pb, [23]uint8{0xe6, 0xee}},
 	{AOUTL, yin, Px, [23]uint8{0xe7, 0xef}},
 	{AOUTSB, ynone, Pb, [23]uint8{0x6e}},
@@ -1449,9 +1402,9 @@ var optab =
 	{ASARQ, yshl, Pw, [23]uint8{0xd1, 07, 0xc1, 07, 0xd3, 07, 0xd3, 07}},
 	{ASARW, yshl, Pe, [23]uint8{0xd1, 07, 0xc1, 07, 0xd3, 07, 0xd3, 07}},
 	{ASBBB, yxorb, Pb, [23]uint8{0x1c, 0x80, 03, 0x18, 0x1a}},
-	{ASBBL, yxorl, Px, [23]uint8{0x83, 03, 0x1d, 0x81, 03, 0x19, 0x1b}},
-	{ASBBQ, yxorl, Pw, [23]uint8{0x83, 03, 0x1d, 0x81, 03, 0x19, 0x1b}},
-	{ASBBW, yxorl, Pe, [23]uint8{0x83, 03, 0x1d, 0x81, 03, 0x19, 0x1b}},
+	{ASBBL, yaddl, Px, [23]uint8{0x83, 03, 0x1d, 0x81, 03, 0x19, 0x1b}},
+	{ASBBQ, yaddl, Pw, [23]uint8{0x83, 03, 0x1d, 0x81, 03, 0x19, 0x1b}},
+	{ASBBW, yaddl, Pe, [23]uint8{0x83, 03, 0x1d, 0x81, 03, 0x19, 0x1b}},
 	{ASCASB, ynone, Pb, [23]uint8{0xae}},
 	{ASCASL, ynone, Px, [23]uint8{0xaf}},
 	{ASCASQ, ynone, Pw, [23]uint8{0xaf}},
@@ -1504,13 +1457,13 @@ var optab =
 	{ASUBW, yaddl, Pe, [23]uint8{0x83, 05, 0x2d, 0x81, 05, 0x29, 0x2b}},
 	{ASWAPGS, ynone, Pm, [23]uint8{0x01, 0xf8}},
 	{ASYSCALL, ynone, Px, [23]uint8{0x0f, 0x05}}, /* fast syscall */
-	{ATESTB, ytestb, Pb, [23]uint8{0xa8, 0xf6, 00, 0x84, 0x84}},
+	{ATESTB, yxorb, Pb, [23]uint8{0xa8, 0xf6, 00, 0x84, 0x84}},
 	{ATESTL, ytestl, Px, [23]uint8{0xa9, 0xf7, 00, 0x85, 0x85}},
 	{ATESTQ, ytestl, Pw, [23]uint8{0xa9, 0xf7, 00, 0x85, 0x85}},
 	{ATESTW, ytestl, Pe, [23]uint8{0xa9, 0xf7, 00, 0x85, 0x85}},
 	{obj.ATEXT, ytext, Px, [23]uint8{}},
-	{AUCOMISD, yxcmp, Pe, [23]uint8{0x2e}},
-	{AUCOMISS, yxcmp, Pm, [23]uint8{0x2e}},
+	{AUCOMISD, yxm, Pe, [23]uint8{0x2e}},
+	{AUCOMISS, yxm, Pm, [23]uint8{0x2e}},
 	{AUNPCKHPD, yxm, Pe, [23]uint8{0x15}},
 	{AUNPCKHPS, yxm, Pm, [23]uint8{0x15}},
 	{AUNPCKLPD, yxm, Pe, [23]uint8{0x14}},
@@ -1525,11 +1478,11 @@ var optab =
 	{AXCHGW, yxchg, Pe, [23]uint8{0x90, 0x90, 0x87, 0x87}},
 	{AXLAT, ynone, Px, [23]uint8{0xd7}},
 	{AXORB, yxorb, Pb, [23]uint8{0x34, 0x80, 06, 0x30, 0x32}},
-	{AXORL, yxorl, Px, [23]uint8{0x83, 06, 0x35, 0x81, 06, 0x31, 0x33}},
+	{AXORL, yaddl, Px, [23]uint8{0x83, 06, 0x35, 0x81, 06, 0x31, 0x33}},
 	{AXORPD, yxm, Pe, [23]uint8{0x57}},
 	{AXORPS, yxm, Pm, [23]uint8{0x57}},
-	{AXORQ, yxorl, Pw, [23]uint8{0x83, 06, 0x35, 0x81, 06, 0x31, 0x33}},
-	{AXORW, yxorl, Pe, [23]uint8{0x83, 06, 0x35, 0x81, 06, 0x31, 0x33}},
+	{AXORQ, yaddl, Pw, [23]uint8{0x83, 06, 0x35, 0x81, 06, 0x31, 0x33}},
+	{AXORW, yaddl, Pe, [23]uint8{0x83, 06, 0x35, 0x81, 06, 0x31, 0x33}},
 	{AFMOVB, yfmvx, Px, [23]uint8{0xdf, 04}},
 	{AFMOVBP, yfmvp, Px, [23]uint8{0xdf, 06}},
 	{AFMOVD, yfmvd, Px, [23]uint8{0xdd, 00, 0xdd, 02, 0xd9, 00, 0xdd, 02}},
@@ -1568,44 +1521,44 @@ var optab =
 	{AFUCOMIP, ycompp, Px, [23]uint8{0xdf, 05}},
 	{AFUCOMP, ycompp, Px, [23]uint8{0xdd, 05}},
 	{AFUCOMPP, ycompp, Px, [23]uint8{0xda, 13}},
-	{AFADDDP, yfaddp, Px, [23]uint8{0xde, 00}},
+	{AFADDDP, ycompp, Px, [23]uint8{0xde, 00}},
 	{AFADDW, yfmvx, Px, [23]uint8{0xde, 00}},
 	{AFADDL, yfmvx, Px, [23]uint8{0xda, 00}},
 	{AFADDF, yfmvx, Px, [23]uint8{0xd8, 00}},
 	{AFADDD, yfadd, Px, [23]uint8{0xdc, 00, 0xd8, 00, 0xdc, 00}},
-	{AFMULDP, yfaddp, Px, [23]uint8{0xde, 01}},
+	{AFMULDP, ycompp, Px, [23]uint8{0xde, 01}},
 	{AFMULW, yfmvx, Px, [23]uint8{0xde, 01}},
 	{AFMULL, yfmvx, Px, [23]uint8{0xda, 01}},
 	{AFMULF, yfmvx, Px, [23]uint8{0xd8, 01}},
 	{AFMULD, yfadd, Px, [23]uint8{0xdc, 01, 0xd8, 01, 0xdc, 01}},
-	{AFSUBDP, yfaddp, Px, [23]uint8{0xde, 05}},
+	{AFSUBDP, ycompp, Px, [23]uint8{0xde, 05}},
 	{AFSUBW, yfmvx, Px, [23]uint8{0xde, 04}},
 	{AFSUBL, yfmvx, Px, [23]uint8{0xda, 04}},
 	{AFSUBF, yfmvx, Px, [23]uint8{0xd8, 04}},
 	{AFSUBD, yfadd, Px, [23]uint8{0xdc, 04, 0xd8, 04, 0xdc, 05}},
-	{AFSUBRDP, yfaddp, Px, [23]uint8{0xde, 04}},
+	{AFSUBRDP, ycompp, Px, [23]uint8{0xde, 04}},
 	{AFSUBRW, yfmvx, Px, [23]uint8{0xde, 05}},
 	{AFSUBRL, yfmvx, Px, [23]uint8{0xda, 05}},
 	{AFSUBRF, yfmvx, Px, [23]uint8{0xd8, 05}},
 	{AFSUBRD, yfadd, Px, [23]uint8{0xdc, 05, 0xd8, 05, 0xdc, 04}},
-	{AFDIVDP, yfaddp, Px, [23]uint8{0xde, 07}},
+	{AFDIVDP, ycompp, Px, [23]uint8{0xde, 07}},
 	{AFDIVW, yfmvx, Px, [23]uint8{0xde, 06}},
 	{AFDIVL, yfmvx, Px, [23]uint8{0xda, 06}},
 	{AFDIVF, yfmvx, Px, [23]uint8{0xd8, 06}},
 	{AFDIVD, yfadd, Px, [23]uint8{0xdc, 06, 0xd8, 06, 0xdc, 07}},
-	{AFDIVRDP, yfaddp, Px, [23]uint8{0xde, 06}},
+	{AFDIVRDP, ycompp, Px, [23]uint8{0xde, 06}},
 	{AFDIVRW, yfmvx, Px, [23]uint8{0xde, 07}},
 	{AFDIVRL, yfmvx, Px, [23]uint8{0xda, 07}},
 	{AFDIVRF, yfmvx, Px, [23]uint8{0xd8, 07}},
 	{AFDIVRD, yfadd, Px, [23]uint8{0xdc, 07, 0xd8, 07, 0xdc, 06}},
 	{AFXCHD, yfxch, Px, [23]uint8{0xd9, 01, 0xd9, 01}},
 	{AFFREE, nil, 0, [23]uint8{}},
-	{AFLDCW, ystcw, Px, [23]uint8{0xd9, 05, 0xd9, 05}},
-	{AFLDENV, ystcw, Px, [23]uint8{0xd9, 04, 0xd9, 04}},
+	{AFLDCW, ysvrs, Px, [23]uint8{0xd9, 05, 0xd9, 05}},
+	{AFLDENV, ysvrs, Px, [23]uint8{0xd9, 04, 0xd9, 04}},
 	{AFRSTOR, ysvrs, Px, [23]uint8{0xdd, 04, 0xdd, 04}},
 	{AFSAVE, ysvrs, Px, [23]uint8{0xdd, 06, 0xdd, 06}},
-	{AFSTCW, ystcw, Px, [23]uint8{0xd9, 07, 0xd9, 07}},
-	{AFSTENV, ystcw, Px, [23]uint8{0xd9, 06, 0xd9, 06}},
+	{AFSTCW, ysvrs, Px, [23]uint8{0xd9, 07, 0xd9, 07}},
+	{AFSTENV, ysvrs, Px, [23]uint8{0xd9, 06, 0xd9, 06}},
 	{AFSTSW, ystsw, Px, [23]uint8{0xdd, 07, 0xdf, 0xe0}},
 	{AF2XM1, ynone, Px, [23]uint8{0xd9, 0xf0}},
 	{AFABS, ynone, Px, [23]uint8{0xd9, 0xe1}},
@@ -1643,7 +1596,7 @@ var optab =
 	{ACMPXCHGQ, yrl_ml, Pw, [23]uint8{0x0f, 0xb1}},
 	{ACMPXCHG8B, yscond, Pm, [23]uint8{0xc7, 01}},
 	{AINVD, ynone, Pm, [23]uint8{0x08}},
-	{AINVLPG, ymbs, Pm, [23]uint8{0x01, 07}},
+	{AINVLPG, ydivb, Pm, [23]uint8{0x01, 07}},
 	{ALFENCE, ynone, Pm, [23]uint8{0xae, 0xe8}},
 	{AMFENCE, ynone, Pm, [23]uint8{0xae, 0xf0}},
 	{AMOVNTIL, yrl_ml, Pm, [23]uint8{0xc3}},
@@ -1673,14 +1626,17 @@ var optab =
 	{AAESDEC, yaes, Pq, [23]uint8{0x38, 0xde, 0}},
 	{AAESDECLAST, yaes, Pq, [23]uint8{0x38, 0xdf, 0}},
 	{AAESIMC, yaes, Pq, [23]uint8{0x38, 0xdb, 0}},
-	{AAESKEYGENASSIST, yaes2, Pq, [23]uint8{0x3a, 0xdf, 0}},
-	{AROUNDPD, yaes2, Pq, [23]uint8{0x3a, 0x09, 0}},
-	{AROUNDPS, yaes2, Pq, [23]uint8{0x3a, 0x08, 0}},
-	{AROUNDSD, yaes2, Pq, [23]uint8{0x3a, 0x0b, 0}},
-	{AROUNDSS, yaes2, Pq, [23]uint8{0x3a, 0x0a, 0}},
+	{AAESKEYGENASSIST, yxshuf, Pq, [23]uint8{0x3a, 0xdf, 0}},
+	{AROUNDPD, yxshuf, Pq, [23]uint8{0x3a, 0x09, 0}},
+	{AROUNDPS, yxshuf, Pq, [23]uint8{0x3a, 0x08, 0}},
+	{AROUNDSD, yxshuf, Pq, [23]uint8{0x3a, 0x0b, 0}},
+	{AROUNDSS, yxshuf, Pq, [23]uint8{0x3a, 0x0a, 0}},
 	{APSHUFD, yxshuf, Pq, [23]uint8{0x70, 0}},
 	{APCLMULQDQ, yxshuf, Pq, [23]uint8{0x3a, 0x44, 0}},
 	{APCMPESTRI, yxshuf, Pq, [23]uint8{0x3a, 0x61, 0}},
+	{AMOVDDUP, yxm, Pf2, [23]uint8{0x12}},
+	{AMOVSHDUP, yxm, Pf3, [23]uint8{0x16}},
+	{AMOVSLDUP, yxm, Pf3, [23]uint8{0x12}},
 
 	{AANDNL, yvex_r3, Pvex, [23]uint8{VEX_LZ_0F38_W0, 0xF2}},
 	{AANDNQ, yvex_r3, Pvex, [23]uint8{VEX_LZ_0F38_W1, 0xF2}},
@@ -1712,7 +1668,7 @@ var optab =
 	{AVPBROADCASTB, yvex_vpbroadcast, Pvex, [23]uint8{VEX_128_66_0F38_W0, 0x78, VEX_256_66_0F38_W0, 0x78}},
 	{AVPTEST, yvex_xy2, Pvex, [23]uint8{VEX_128_66_0F38_WIG, 0x17, VEX_256_66_0F38_WIG, 0x17}},
 	{AVPSHUFB, yvex_xy3, Pvex, [23]uint8{VEX_128_66_0F38_WIG, 0x00, VEX_256_66_0F38_WIG, 0x00}},
-	{AVPSHUFD, yvex_xyi3, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0x70, VEX_256_66_0F_WIG, 0x70}},
+	{AVPSHUFD, yvex_xyi3, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0x70, VEX_256_66_0F_WIG, 0x70, VEX_128_66_0F_WIG, 0x70, VEX_256_66_0F_WIG, 0x70}},
 	{AVPOR, yvex_xy3, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0xeb, VEX_256_66_0F_WIG, 0xeb}},
 	{AVPADDQ, yvex_xy3, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0xd4, VEX_256_66_0F_WIG, 0xd4}},
 	{AVPADDD, yvex_xy3, Pvex, [23]uint8{VEX_128_66_0F_WIG, 0xfe, VEX_256_66_0F_WIG, 0xfe}},
@@ -1729,6 +1685,11 @@ var optab =
 	{AVPERM2I128, yvex_yyi4, Pvex, [23]uint8{VEX_256_66_0F3A_WIG, 0x46}},
 	{ARORXL, yvex_ri3, Pvex, [23]uint8{VEX_LZ_F2_0F3A_W0, 0xf0}},
 	{ARORXQ, yvex_ri3, Pvex, [23]uint8{VEX_LZ_F2_0F3A_W1, 0xf0}},
+	{AVBROADCASTSD, yvex_vpbroadcast_sd, Pvex, [23]uint8{VEX_256_66_0F38_W0, 0x19}},
+	{AVBROADCASTSS, yvex_vpbroadcast, Pvex, [23]uint8{VEX_128_66_0F38_W0, 0x18, VEX_256_66_0F38_W0, 0x18}},
+	{AVMOVDDUP, yvex_xy2, Pvex, [23]uint8{VEX_128_F2_0F_WIG, 0x12, VEX_256_F2_0F_WIG, 0x12}},
+	{AVMOVSHDUP, yvex_xy2, Pvex, [23]uint8{VEX_128_F3_0F_WIG, 0x16, VEX_256_F3_0F_WIG, 0x16}},
+	{AVMOVSLDUP, yvex_xy2, Pvex, [23]uint8{VEX_128_F3_0F_WIG, 0x12, VEX_256_F3_0F_WIG, 0x12}},
 
 	{AXACQUIRE, ynone, Px, [23]uint8{0xf2}},
 	{AXRELEASE, ynone, Px, [23]uint8{0xf3}},
@@ -1741,7 +1702,6 @@ var optab =
 	{obj.ATYPE, nil, 0, [23]uint8{}},
 	{obj.AFUNCDATA, yfuncdata, Px, [23]uint8{0, 0}},
 	{obj.APCDATA, ypcdata, Px, [23]uint8{0, 0}},
-	{obj.ACHECKNIL, nil, 0, [23]uint8{}},
 	{obj.AVARDEF, nil, 0, [23]uint8{}},
 	{obj.AVARKILL, nil, 0, [23]uint8{}},
 	{obj.ADUFFCOPY, yduff, Px, [23]uint8{0xe8}},
@@ -2013,7 +1973,7 @@ func instinit() {
 	for i := 1; optab[i].as != 0; i++ {
 		c := optab[i].as
 		if opindex[c&obj.AMask] != nil {
-			log.Fatalf("phase error in optab: %d (%v)", i, obj.Aconv(c))
+			log.Fatalf("phase error in optab: %d (%v)", i, c)
 		}
 		opindex[c&obj.AMask] = &optab[i]
 	}
@@ -2150,7 +2110,7 @@ func instinit() {
 	}
 }
 
-var isAndroid = (obj.Getgoos() == "android")
+var isAndroid = (obj.GOOS == "android")
 
 func prefixof(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
 	if a.Reg < REG_CS && a.Index < REG_CS { // fast path
@@ -2186,7 +2146,7 @@ func prefixof(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
 					if isAndroid {
 						return 0x65 // GS
 					}
-					log.Fatalf("unknown TLS base register for %s", obj.Headstr(ctxt.Headtype))
+					log.Fatalf("unknown TLS base register for %v", ctxt.Headtype)
 
 				case obj.Hdarwin,
 					obj.Hdragonfly,
@@ -2199,7 +2159,7 @@ func prefixof(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
 
 			switch ctxt.Headtype {
 			default:
-				log.Fatalf("unknown TLS base register for %s", obj.Headstr(ctxt.Headtype))
+				log.Fatalf("unknown TLS base register for %v", ctxt.Headtype)
 
 			case obj.Hlinux:
 				if isAndroid {
@@ -2769,7 +2729,7 @@ func vaddr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r *obj.Reloc) int64 {
 			log.Fatalf("reloc")
 		}
 
-		if !ctxt.Flag_shared || isAndroid {
+		if !ctxt.Flag_shared || isAndroid || ctxt.Headtype == obj.Hdarwin {
 			r.Type = obj.R_TLS_LE
 			r.Siz = 4
 			r.Off = -1 // caller must fill in
@@ -2835,7 +2795,9 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
 				goto bad
 			}
 			if p.Mode == 32 && ctxt.Flag_shared {
-				base = REG_CX
+				// The base register has already been set. It holds the PC
+				// of this instruction returned by a PC-reading thunk.
+				// See obj6.go:rewriteToPcrel.
 			} else {
 				base = REG_NONE
 			}
@@ -2880,7 +2842,9 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
 			ctxt.Diag("bad addr: %v", p)
 		}
 		if p.Mode == 32 && ctxt.Flag_shared {
-			base = REG_CX
+			// The base register has already been set. It holds the PC
+			// of this instruction returned by a PC-reading thunk.
+			// See obj6.go:rewriteToPcrel.
 		} else {
 			base = REG_NONE
 		}
@@ -3427,7 +3391,8 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
 				log.Fatalf("asmins bad table %v", p)
 			}
 			op = int(o.op[z])
-			if op == 0x0f {
+			// In vex case 0x0f is actually VEX_256_F2_0F_WIG
+			if op == 0x0f && o.prefix != Pvex {
 				ctxt.AsmBuf.Put1(byte(op))
 				z++
 				op = int(o.op[z])
@@ -3739,7 +3704,11 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
 				ctxt.AsmBuf.Put2(byte(op), o.op[z+1])
 				r = obj.Addrel(ctxt.Cursym)
 				r.Off = int32(p.Pc + int64(ctxt.AsmBuf.Len()))
-				r.Type = obj.R_ADDR
+				if p.Mode == 64 {
+					r.Type = obj.R_PCREL
+				} else {
+					r.Type = obj.R_ADDR
+				}
 				r.Siz = 4
 				r.Add = p.To.Offset
 				r.Sym = p.To.Sym
@@ -4010,31 +3979,32 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
 						// are handled in prefixof above and should not be listed here.
 						switch ctxt.Headtype {
 						default:
-							log.Fatalf("unknown TLS base location for %s", obj.Headstr(ctxt.Headtype))
+							log.Fatalf("unknown TLS base location for %v", ctxt.Headtype)
 
 						case obj.Hlinux,
 							obj.Hnacl:
 							if ctxt.Flag_shared {
 								// Note that this is not generating the same insns as the other cases.
-								//     MOV TLS, R_to
+								//     MOV TLS, dst
 								// becomes
-								//     call __x86.get_pc_thunk.cx
-								//     movl (gotpc + g at gotntpoff)(%ecx),$R_To
+								//     call __x86.get_pc_thunk.dst
+								//     movl (gotpc + g at gotntpoff)(dst), dst
 								// which is encoded as
-								//     call __x86.get_pc_thunk.cx
-								//     movq 0(%ecx), R_to
+								//     call __x86.get_pc_thunk.dst
+								//     movq 0(dst), dst
 								// and R_CALL & R_TLS_IE relocs. This all assumes the only tls variable we access
 								// is g, which we can't check here, but will when we assemble the second
 								// instruction.
+								dst := p.To.Reg
 								ctxt.AsmBuf.Put1(0xe8)
 								r = obj.Addrel(ctxt.Cursym)
 								r.Off = int32(p.Pc + int64(ctxt.AsmBuf.Len()))
 								r.Type = obj.R_CALL
 								r.Siz = 4
-								r.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk.cx", 0)
+								r.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk."+strings.ToLower(Rconv(int(dst))), 0)
 								ctxt.AsmBuf.PutInt32(0)
 
-								ctxt.AsmBuf.Put2(0x8B, byte(2<<6|reg[REG_CX]|(reg[p.To.Reg]<<3)))
+								ctxt.AsmBuf.Put2(0x8B, byte(2<<6|reg[dst]|(reg[dst]<<3)))
 								r = obj.Addrel(ctxt.Cursym)
 								r.Off = int32(p.Pc + int64(ctxt.AsmBuf.Len()))
 								r.Type = obj.R_TLS_IE
@@ -4067,7 +4037,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
 							ctxt.AsmBuf.Put1(0x8B)
 							asmand(ctxt, p, &pp.From, &p.To)
 
-						case obj.Hwindows:
+						case obj.Hwindows, obj.Hwindowsgui:
 							// Windows TLS base is always 0x14(FS).
 							pp.From = p.From
 
@@ -4085,7 +4055,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
 
 					switch ctxt.Headtype {
 					default:
-						log.Fatalf("unknown TLS base location for %s", obj.Headstr(ctxt.Headtype))
+						log.Fatalf("unknown TLS base location for %v", ctxt.Headtype)
 
 					case obj.Hlinux:
 						if !ctxt.Flag_shared {
@@ -4139,7 +4109,7 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
 							0x8B)
 						asmand(ctxt, p, &pp.From, &p.To)
 
-					case obj.Hwindows:
+					case obj.Hwindows, obj.Hwindowsgui:
 						// Windows TLS base is always 0x28(GS).
 						pp.From = p.From
 
diff --git a/src/cmd/internal/obj/x86/list6.go b/src/cmd/internal/obj/x86/list6.go
index fa9ddca..a1a49ed 100644
--- a/src/cmd/internal/obj/x86/list6.go
+++ b/src/cmd/internal/obj/x86/list6.go
@@ -1,5 +1,5 @@
 // Inferno utils/6c/list.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6c/list.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/list.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index 75638a0..102d8c3 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -1,5 +1,5 @@
 // Inferno utils/6l/pass.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/pass.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/pass.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -36,6 +36,7 @@ import (
 	"fmt"
 	"log"
 	"math"
+	"strings"
 )
 
 func CanUse1InsnTLS(ctxt *obj.Link) bool {
@@ -54,7 +55,8 @@ func CanUse1InsnTLS(ctxt *obj.Link) bool {
 		case obj.Hlinux,
 			obj.Hnacl,
 			obj.Hplan9,
-			obj.Hwindows:
+			obj.Hwindows,
+			obj.Hwindowsgui:
 			return false
 		}
 
@@ -62,8 +64,7 @@ func CanUse1InsnTLS(ctxt *obj.Link) bool {
 	}
 
 	switch ctxt.Headtype {
-	case obj.Hplan9,
-		obj.Hwindows:
+	case obj.Hplan9, obj.Hwindows, obj.Hwindowsgui:
 		return false
 	case obj.Hlinux:
 		return !ctxt.Flag_shared
@@ -180,7 +181,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 	}
 
 	// TODO: Remove.
-	if ctxt.Headtype == obj.Hwindows && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 {
+	if (ctxt.Headtype == obj.Hwindows || ctxt.Headtype == obj.Hwindowsgui) && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 {
 		if p.From.Scale == 1 && p.From.Index == REG_TLS {
 			p.From.Scale = 2
 		}
@@ -265,7 +266,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 			p.From.Type = obj.TYPE_MEM
 			p.From.Name = obj.NAME_EXTERN
 			p.From.Sym = s
-			p.From.Sym.Local = true
+			p.From.Sym.Set(obj.AttrLocal, true)
 			p.From.Offset = 0
 		}
 
@@ -305,7 +306,7 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
 			p.From.Type = obj.TYPE_MEM
 			p.From.Name = obj.NAME_EXTERN
 			p.From.Sym = s
-			p.From.Sym.Local = true
+			p.From.Sym.Set(obj.AttrLocal, true)
 			p.From.Offset = 0
 		}
 	}
@@ -333,6 +334,13 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 		lea = ALEAL
 		mov = AMOVL
 		reg = REG_CX
+		if p.As == ALEAL && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index {
+			// Special case: clobber the destination register with
+			// the PC so we don't have to clobber CX.
+			// The SSA backend depends on CX not being clobbered across LEAL.
+			// See cmd/compile/internal/ssa/gen/386.rules (search for Flag_shared).
+			reg = p.To.Reg
+		}
 	}
 
 	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
@@ -371,12 +379,12 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 	// We only care about global data: NAME_EXTERN means a global
 	// symbol in the Go sense, and p.Sym.Local is true for a few
 	// internally defined symbols.
-	if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
+	if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
 		// $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below
 		p.As = mov
 		p.From.Type = obj.TYPE_ADDR
 	}
-	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
+	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
 		// $MOV $sym, Rx becomes $MOV sym at GOT, Rx
 		// $MOV $sym+<off>, Rx becomes $MOV sym at GOT, Rx; $LEA <off>(Rx), Rx
 		// On 386 only, more complicated things like PUSHL $sym become $MOV sym at GOT, CX; PUSHL CX
@@ -391,7 +399,7 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 			dest = p.To
 			p.As = mov
 			p.To.Type = obj.TYPE_REG
-			p.To.Reg = REG_CX
+			p.To.Reg = reg
 			p.To.Sym = nil
 			p.To.Name = obj.NAME_NONE
 		}
@@ -412,7 +420,7 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 			q.As = pAs
 			q.To = dest
 			q.From.Type = obj.TYPE_REG
-			q.From.Reg = REG_CX
+			q.From.Reg = reg
 		}
 	}
 	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
@@ -422,12 +430,12 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 	// MOVx sym, Ry becomes $MOV sym at GOT, R15; MOVx (R15), Ry
 	// MOVx Ry, sym becomes $MOV sym at GOT, R15; MOVx Ry, (R15)
 	// An addition may be inserted between the two MOVs if there is an offset.
-	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
-		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
+	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
+		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
 		}
 		source = &p.From
-	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
+	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 		source = &p.To
 	} else {
 		return
@@ -437,7 +445,7 @@ func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
 		// to a PLT, so make sure the GOT pointer is loaded into BX.
 		// RegTo2 is set on the replacement call insn to stop it being
 		// processed when it is in turn passed to progedit.
-		if p.Mode == 64 || (p.To.Sym != nil && p.To.Sym.Local) || p.RegTo2 != 0 {
+		if p.Mode == 64 || (p.To.Sym != nil && p.To.Sym.Local()) || p.RegTo2 != 0 {
 			return
 		}
 		p1 := obj.Appendp(ctxt, p)
@@ -509,7 +517,7 @@ func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) {
 		return
 	}
 	// Any Prog (aside from the above special cases) with an Addr with Name ==
-	// NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.cx
+	// NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.XX
 	// inserted before it.
 	isName := func(a *obj.Addr) bool {
 		if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 {
@@ -542,21 +550,36 @@ func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) {
 	if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) {
 		return
 	}
+	var dst int16 = REG_CX
+	if (p.As == ALEAL || p.As == AMOVL) && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index {
+		dst = p.To.Reg
+		// Why?  See the comment near the top of rewriteToUseGot above.
+		// AMOVLs might be introduced by the GOT rewrites.
+	}
 	q := obj.Appendp(ctxt, p)
 	q.RegTo2 = 1
 	r := obj.Appendp(ctxt, q)
 	r.RegTo2 = 1
 	q.As = obj.ACALL
-	q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk.cx", 0)
+	q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk."+strings.ToLower(Rconv(int(dst))), 0)
 	q.To.Type = obj.TYPE_MEM
 	q.To.Name = obj.NAME_EXTERN
-	q.To.Sym.Local = true
+	q.To.Sym.Set(obj.AttrLocal, true)
 	r.As = p.As
 	r.Scond = p.Scond
 	r.From = p.From
 	r.From3 = p.From3
 	r.Reg = p.Reg
 	r.To = p.To
+	if isName(&p.From) {
+		r.From.Reg = dst
+	}
+	if isName(&p.To) {
+		r.To.Reg = dst
+	}
+	if p.From3 != nil && isName(p.From3) {
+		r.From3.Reg = dst
+	}
 	obj.Nopout(p)
 }
 
@@ -632,17 +655,29 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 
 	// TODO(rsc): Remove 'p.Mode == 64 &&'.
 	if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 {
+		leaf := true
+	LeafSearch:
 		for q := p; q != nil; q = q.Link {
-			if q.As == obj.ACALL {
-				goto noleaf
-			}
-			if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 {
-				goto noleaf
+			switch q.As {
+			case obj.ACALL:
+				// Treat common runtime calls that take no arguments
+				// the same as duffcopy and duffzero.
+				if !isZeroArgRuntimeCall(q.To.Sym) {
+					leaf = false
+					break LeafSearch
+				}
+				fallthrough
+			case obj.ADUFFCOPY, obj.ADUFFZERO:
+				if autoffset >= obj.StackSmall-8 {
+					leaf = false
+					break LeafSearch
+				}
 			}
 		}
 
-		p.From3.Offset |= obj.NOSPLIT
-	noleaf:
+		if leaf {
+			p.From3.Offset |= obj.NOSPLIT
+		}
 	}
 
 	if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 {
@@ -663,17 +698,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 		p.From.Type = obj.TYPE_CONST
 		p.From.Offset = int64(autoffset)
 		p.Spadj = autoffset
-	} else {
-		// zero-byte stack adjustment.
-		// Insert a fake non-zero adjustment so that stkcheck can
-		// recognize the end of the stack-splitting prolog.
-		p = obj.Appendp(ctxt, p)
-
-		p.As = obj.ANOP
-		p.Spadj = int32(-ctxt.Arch.PtrSize)
-		p = obj.Appendp(ctxt, p)
-		p.As = obj.ANOP
-		p.Spadj = int32(ctxt.Arch.PtrSize)
 	}
 
 	deltasp := autoffset
@@ -810,31 +834,26 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 		p2.Pcond = p
 	}
 
-	var a int
-	var pcsize int
 	for ; p != nil; p = p.Link {
-		pcsize = int(p.Mode) / 8
-		a = int(p.From.Name)
-		if a == obj.NAME_AUTO {
+		pcsize := int(p.Mode) / 8
+		switch p.From.Name {
+		case obj.NAME_AUTO:
 			p.From.Offset += int64(deltasp) - int64(bpsize)
-		}
-		if a == obj.NAME_PARAM {
+		case obj.NAME_PARAM:
 			p.From.Offset += int64(deltasp) + int64(pcsize)
 		}
 		if p.From3 != nil {
-			a = int(p.From3.Name)
-			if a == obj.NAME_AUTO {
+			switch p.From3.Name {
+			case obj.NAME_AUTO:
 				p.From3.Offset += int64(deltasp) - int64(bpsize)
-			}
-			if a == obj.NAME_PARAM {
+			case obj.NAME_PARAM:
 				p.From3.Offset += int64(deltasp) + int64(pcsize)
 			}
 		}
-		a = int(p.To.Name)
-		if a == obj.NAME_AUTO {
+		switch p.To.Name {
+		case obj.NAME_AUTO:
 			p.To.Offset += int64(deltasp) - int64(bpsize)
-		}
-		if a == obj.NAME_PARAM {
+		case obj.NAME_PARAM:
 			p.To.Offset += int64(deltasp) + int64(pcsize)
 		}
 
@@ -873,7 +892,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 			continue
 
 		case obj.ARET:
-			break
+			// do nothing
 		}
 
 		if autoffset != deltasp {
@@ -914,6 +933,17 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
 	}
 }
 
+func isZeroArgRuntimeCall(s *obj.LSym) bool {
+	if s == nil {
+		return false
+	}
+	switch s.Name {
+	case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide":
+		return true
+	}
+	return false
+}
+
 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
 	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
 		a.Type = obj.TYPE_MEM
@@ -984,7 +1014,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *ob
 		p.From.Reg = REG_SP
 		indir_cx(ctxt, p, &p.To)
 		p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
-		if ctxt.Cursym.Cfunc {
+		if ctxt.Cursym.CFunc() {
 			p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
 		}
 	} else if framesize <= obj.StackBig {
@@ -1006,7 +1036,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *ob
 		p.From.Reg = REG_AX
 		indir_cx(ctxt, p, &p.To)
 		p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
-		if ctxt.Cursym.Cfunc {
+		if ctxt.Cursym.CFunc() {
 			p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
 		}
 	} else {
@@ -1030,7 +1060,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *ob
 		p.As = mov
 		indir_cx(ctxt, p, &p.From)
 		p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
-		if ctxt.Cursym.Cfunc {
+		if ctxt.Cursym.CFunc() {
 			p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
 		}
 		p.To.Type = obj.TYPE_REG
@@ -1083,11 +1113,23 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *ob
 	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
 	}
 
+	// Now we are at the end of the function, but logically
+	// we are still in function prologue. We need to fix the
+	// SP data and PCDATA.
 	spfix := obj.Appendp(ctxt, last)
 	spfix.As = obj.ANOP
 	spfix.Spadj = -framesize
 
-	call := obj.Appendp(ctxt, spfix)
+	pcdata := obj.Appendp(ctxt, spfix)
+	pcdata.Lineno = ctxt.Cursym.Text.Lineno
+	pcdata.Mode = ctxt.Cursym.Text.Mode
+	pcdata.As = obj.APCDATA
+	pcdata.From.Type = obj.TYPE_CONST
+	pcdata.From.Offset = obj.PCDATA_StackMapIndex
+	pcdata.To.Type = obj.TYPE_CONST
+	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
+
+	call := obj.Appendp(ctxt, pcdata)
 	call.Lineno = ctxt.Cursym.Text.Lineno
 	call.Mode = ctxt.Cursym.Text.Mode
 	call.As = obj.ACALL
@@ -1095,7 +1137,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *ob
 	call.To.Name = obj.NAME_EXTERN
 	morestack := "runtime.morestack"
 	switch {
-	case ctxt.Cursym.Cfunc:
+	case ctxt.Cursym.CFunc():
 		morestack = "runtime.morestackc"
 	case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0:
 		morestack = "runtime.morestack_noctxt"
@@ -1208,7 +1250,7 @@ func relinv(a obj.As) obj.As {
 		return AJOS
 	}
 
-	log.Fatalf("unknown relation: %s", obj.Aconv(a))
+	log.Fatalf("unknown relation: %s", a)
 	return 0
 }
 
diff --git a/src/cmd/internal/obj/x86/obj6_test.go b/src/cmd/internal/obj/x86/obj6_test.go
index fe1f95c..e311c62 100644
--- a/src/cmd/internal/obj/x86/obj6_test.go
+++ b/src/cmd/internal/obj/x86/obj6_test.go
@@ -4,7 +4,6 @@ import (
 	"bufio"
 	"bytes"
 	"fmt"
-	"go/build"
 	"internal/testenv"
 	"io/ioutil"
 	"os"
@@ -96,13 +95,8 @@ func asmOutput(t *testing.T, s string) []byte {
 	if err != nil {
 		t.Fatal(err)
 	}
-	gofolder := filepath.Join(build.Default.GOROOT, "bin")
-	if gobin := os.Getenv("GOBIN"); len(gobin) != 0 {
-		gofolder = gobin
-	}
-
 	cmd := exec.Command(
-		filepath.Join(gofolder, "go"), "tool", "asm", "-S", "-dynlink",
+		testenv.GoToolPath(t), "tool", "asm", "-S", "-dynlink",
 		"-o", filepath.Join(tmpdir, "output.6"), tmpfile.Name())
 
 	var env []string
diff --git a/src/cmd/internal/objfile/disasm.go b/src/cmd/internal/objfile/disasm.go
index 25c3301..8af0c8f 100644
--- a/src/cmd/internal/objfile/disasm.go
+++ b/src/cmd/internal/objfile/disasm.go
@@ -16,13 +16,14 @@ import (
 	"text/tabwriter"
 
 	"golang.org/x/arch/arm/armasm"
+	"golang.org/x/arch/ppc64/ppc64asm"
 	"golang.org/x/arch/x86/x86asm"
 )
 
 // Disasm is a disassembler for a given File.
 type Disasm struct {
 	syms      []Sym            //symbols in file, sorted by address
-	pcln      *gosym.Table     // pcln table
+	pcln      Liner            // pcln table
 	text      []byte           // bytes of text segment (actual instructions)
 	textStart uint64           // start PC of text
 	textEnd   uint64           // end PC of text
@@ -116,6 +117,7 @@ func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64) {
 	for _, sym := range d.syms {
 		symStart := sym.Addr
 		symEnd := sym.Addr + uint64(sym.Size)
+		relocs := sym.Relocs
 		if sym.Code != 'T' && sym.Code != 't' ||
 			symStart < d.textStart ||
 			symEnd <= start || end <= symStart ||
@@ -135,7 +137,7 @@ func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64) {
 			symEnd = end
 		}
 		code := d.text[:end-d.textStart]
-		d.Decode(symStart, symEnd, func(pc, size uint64, file string, line int, text string) {
+		d.Decode(symStart, symEnd, relocs, func(pc, size uint64, file string, line int, text string) {
 			i := pc - d.textStart
 			fmt.Fprintf(tw, "\t%s:%d\t%#x\t", base(file), line, pc)
 			if size%4 != 0 || d.goarch == "386" || d.goarch == "amd64" {
@@ -158,7 +160,7 @@ func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64) {
 }
 
 // Decode disassembles the text segment range [start, end), calling f for each instruction.
-func (d *Disasm) Decode(start, end uint64, f func(pc, size uint64, file string, line int, text string)) {
+func (d *Disasm) Decode(start, end uint64, relocs []Reloc, f func(pc, size uint64, file string, line int, text string)) {
 	if start < d.textStart {
 		start = d.textStart
 	}
@@ -169,21 +171,32 @@ func (d *Disasm) Decode(start, end uint64, f func(pc, size uint64, file string,
 	lookup := d.lookup
 	for pc := start; pc < end; {
 		i := pc - d.textStart
-		text, size := d.disasm(code[i:], pc, lookup)
+		text, size := d.disasm(code[i:], pc, lookup, d.byteOrder)
 		file, line, _ := d.pcln.PCToLine(pc)
+		text += "\t"
+		first := true
+		for len(relocs) > 0 && relocs[0].Addr < i+uint64(size) {
+			if first {
+				first = false
+			} else {
+				text += " "
+			}
+			text += relocs[0].Stringer.String(pc - start)
+			relocs = relocs[1:]
+		}
 		f(pc, uint64(size), file, line, text)
 		pc += uint64(size)
 	}
 }
 
 type lookupFunc func(addr uint64) (sym string, base uint64)
-type disasmFunc func(code []byte, pc uint64, lookup lookupFunc) (text string, size int)
+type disasmFunc func(code []byte, pc uint64, lookup lookupFunc, ord binary.ByteOrder) (text string, size int)
 
-func disasm_386(code []byte, pc uint64, lookup lookupFunc) (string, int) {
+func disasm_386(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder) (string, int) {
 	return disasm_x86(code, pc, lookup, 32)
 }
 
-func disasm_amd64(code []byte, pc uint64, lookup lookupFunc) (string, int) {
+func disasm_amd64(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder) (string, int) {
 	return disasm_x86(code, pc, lookup, 64)
 }
 
@@ -220,7 +233,7 @@ func (r textReader) ReadAt(data []byte, off int64) (n int, err error) {
 	return
 }
 
-func disasm_arm(code []byte, pc uint64, lookup lookupFunc) (string, int) {
+func disasm_arm(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder) (string, int) {
 	inst, err := armasm.Decode(code, armasm.ModeARM)
 	var text string
 	size := inst.Len
@@ -233,10 +246,25 @@ func disasm_arm(code []byte, pc uint64, lookup lookupFunc) (string, int) {
 	return text, size
 }
 
+func disasm_ppc64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder) (string, int) {
+	inst, err := ppc64asm.Decode(code, byteOrder)
+	var text string
+	size := inst.Len
+	if err != nil || size == 0 || inst.Op == 0 {
+		size = 4
+		text = "?"
+	} else {
+		text = ppc64asm.GoSyntax(inst, pc, lookup)
+	}
+	return text, size
+}
+
 var disasms = map[string]disasmFunc{
-	"386":   disasm_386,
-	"amd64": disasm_amd64,
-	"arm":   disasm_arm,
+	"386":     disasm_386,
+	"amd64":   disasm_amd64,
+	"arm":     disasm_arm,
+	"ppc64":   disasm_ppc64,
+	"ppc64le": disasm_ppc64,
 }
 
 var byteOrders = map[string]binary.ByteOrder{
@@ -247,3 +275,9 @@ var byteOrders = map[string]binary.ByteOrder{
 	"ppc64le": binary.LittleEndian,
 	"s390x":   binary.BigEndian,
 }
+
+type Liner interface {
+	// Given a pc, returns the corresponding file, line, and function data.
+	// If unknown, returns "",0,nil.
+	PCToLine(uint64) (string, int, *gosym.Func)
+}
diff --git a/src/cmd/internal/objfile/elf.go b/src/cmd/internal/objfile/elf.go
index c811460..4ab7e6d 100644
--- a/src/cmd/internal/objfile/elf.go
+++ b/src/cmd/internal/objfile/elf.go
@@ -9,6 +9,7 @@ package objfile
 import (
 	"debug/dwarf"
 	"debug/elf"
+	"encoding/binary"
 	"fmt"
 	"os"
 )
@@ -99,6 +100,9 @@ func (f *elfFile) goarch() string {
 	case elf.EM_ARM:
 		return "arm"
 	case elf.EM_PPC64:
+		if f.elf.ByteOrder == binary.LittleEndian {
+			return "ppc64le"
+		}
 		return "ppc64"
 	case elf.EM_S390:
 		return "s390x"
diff --git a/src/cmd/internal/objfile/goobj.go b/src/cmd/internal/objfile/goobj.go
index 43435ef..230137e 100644
--- a/src/cmd/internal/objfile/goobj.go
+++ b/src/cmd/internal/objfile/goobj.go
@@ -8,7 +8,9 @@ package objfile
 
 import (
 	"cmd/internal/goobj"
+	"cmd/internal/sys"
 	"debug/dwarf"
+	"debug/gosym"
 	"errors"
 	"fmt"
 	"os"
@@ -16,6 +18,7 @@ import (
 
 type goobjFile struct {
 	goobj *goobj.Package
+	f     *os.File // the underlying .o or .a file
 }
 
 func openGoobj(r *os.File) (rawFile, error) {
@@ -23,7 +26,7 @@ func openGoobj(r *os.File) (rawFile, error) {
 	if err != nil {
 		return nil, err
 	}
-	return &goobjFile{f}, nil
+	return &goobjFile{goobj: f, f: r}, nil
 }
 
 func goobjName(id goobj.SymID) string {
@@ -55,6 +58,9 @@ func (f *goobjFile) symbols() ([]Sym, error) {
 		if s.Version != 0 {
 			sym.Code += 'a' - 'A'
 		}
+		for i, r := range s.Reloc {
+			sym.Relocs = append(sym.Relocs, Reloc{Addr: uint64(s.Data.Offset) + uint64(r.Offset), Size: uint64(r.Size), Stringer: &s.Reloc[i]})
+		}
 		syms = append(syms, sym)
 	}
 
@@ -75,23 +81,68 @@ func (f *goobjFile) symbols() ([]Sym, error) {
 	return syms, nil
 }
 
-// pcln does not make sense for Go object files, because each
-// symbol has its own individual pcln table, so there is no global
-// space of addresses to map.
 func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+	// Should never be called.  We implement Liner below, callers
+	// should use that instead.
 	return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
 }
 
-// text does not make sense for Go object files, because
-// each function has a separate section.
+// Find returns the file name, line, and function data for the given pc.
+// Returns "",0,nil if unknown.
+// This function implements the Liner interface in preference to pcln() above.
+func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
+	// TODO: this is really inefficient.  Binary search?  Memoize last result?
+	var arch *sys.Arch
+	for _, a := range sys.Archs {
+		if a.Name == f.goobj.Arch {
+			arch = a
+			break
+		}
+	}
+	if arch == nil {
+		return "", 0, nil
+	}
+	for _, s := range f.goobj.Syms {
+		if pc < uint64(s.Data.Offset) || pc >= uint64(s.Data.Offset+s.Data.Size) {
+			continue
+		}
+		if s.Func == nil {
+			return "", 0, nil
+		}
+		pcfile := make([]byte, s.Func.PCFile.Size)
+		_, err := f.f.ReadAt(pcfile, s.Func.PCFile.Offset)
+		if err != nil {
+			return "", 0, nil
+		}
+		fileID := gosym.PCValue(pcfile, pc-uint64(s.Data.Offset), arch.MinLC)
+		fileName := s.Func.File[fileID]
+		pcline := make([]byte, s.Func.PCLine.Size)
+		_, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset)
+		if err != nil {
+			return "", 0, nil
+		}
+		line := gosym.PCValue(pcline, pc-uint64(s.Data.Offset), arch.MinLC)
+		// Note: we provide only the name in the Func structure.
+		// We could provide more if needed.
+		return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: s.Name}}
+	}
+	return "", 0, nil
+}
+
+// We treat the whole object file as the text section.
 func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
-	return 0, nil, fmt.Errorf("text not available in go object file")
+	var info os.FileInfo
+	info, err = f.f.Stat()
+	if err != nil {
+		return
+	}
+	text = make([]byte, info.Size())
+	_, err = f.f.ReadAt(text, 0)
+	return
 }
 
-// goarch makes sense but is not exposed in debug/goobj's API,
-// and we don't need it yet for any users of internal/objfile.
 func (f *goobjFile) goarch() string {
-	return "GOARCH unimplemented for debug/goobj files"
+	return f.goobj.Arch
 }
 
 func (f *goobjFile) loadAddress() (uint64, error) {
diff --git a/src/cmd/internal/objfile/objfile.go b/src/cmd/internal/objfile/objfile.go
index e5d99f0..2bf6363 100644
--- a/src/cmd/internal/objfile/objfile.go
+++ b/src/cmd/internal/objfile/objfile.go
@@ -30,11 +30,24 @@ type File struct {
 
 // A Sym is a symbol defined in an executable file.
 type Sym struct {
-	Name string // symbol name
-	Addr uint64 // virtual address of symbol
-	Size int64  // size in bytes
-	Code rune   // nm code (T for text, D for data, and so on)
-	Type string // XXX?
+	Name   string  // symbol name
+	Addr   uint64  // virtual address of symbol
+	Size   int64   // size in bytes
+	Code   rune    // nm code (T for text, D for data, and so on)
+	Type   string  // XXX?
+	Relocs []Reloc // in increasing Addr order
+}
+
+type Reloc struct {
+	Addr     uint64 // Address of first byte that reloc applies to.
+	Size     uint64 // Number of bytes
+	Stringer RelocStringer
+}
+
+type RelocStringer interface {
+	// insnOffset is the offset of the instruction containing the relocation
+	// from the start of the symbol containing the relocation.
+	String(insnOffset uint64) string
 }
 
 var openers = []func(*os.File) (rawFile, error){
@@ -80,7 +93,13 @@ func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
 func (x byAddr) Len() int           { return len(x) }
 func (x byAddr) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
 
-func (f *File) PCLineTable() (*gosym.Table, error) {
+func (f *File) PCLineTable() (Liner, error) {
+	// If the raw file implements Liner directly, use that.
+	// Currently, only Go intermediate objects and archives (goobj) use this path.
+	if pcln, ok := f.raw.(Liner); ok {
+		return pcln, nil
+	}
+	// Otherwise, read the pcln tables and build a Liner out of that.
 	textStart, symtab, pclntab, err := f.raw.pcln()
 	if err != nil {
 		return nil, err
diff --git a/src/cmd/internal/pprof/commands/commands.go b/src/cmd/internal/pprof/commands/commands.go
deleted file mode 100644
index 5dfbbd4..0000000
--- a/src/cmd/internal/pprof/commands/commands.go
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package commands defines and manages the basic pprof commands
-package commands
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"os/exec"
-	"runtime"
-	"strings"
-	"time"
-
-	"cmd/internal/pprof/plugin"
-	"cmd/internal/pprof/report"
-	"cmd/internal/pprof/svg"
-	"cmd/internal/pprof/tempfile"
-)
-
-// Commands describes the commands accepted by pprof.
-type Commands map[string]*Command
-
-// Command describes the actions for a pprof command. Includes a
-// function for command-line completion, the report format to use
-// during report generation, any postprocessing functions, and whether
-// the command expects a regexp parameter (typically a function name).
-type Command struct {
-	Complete    Completer     // autocomplete for interactive mode
-	Format      int           // report format to generate
-	PostProcess PostProcessor // postprocessing to run on report
-	HasParam    bool          // Collect a parameter from the CLI
-	Usage       string        // Help text
-}
-
-// Completer is a function for command-line autocompletion
-type Completer func(prefix string) string
-
-// PostProcessor is a function that applies post-processing to the report output
-type PostProcessor func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error
-
-// PProf returns the basic pprof report-generation commands
-func PProf(c Completer, interactive **bool) Commands {
-	return Commands{
-		// Commands that require no post-processing.
-		"tags":   {nil, report.Tags, nil, false, "Outputs all tags in the profile"},
-		"raw":    {c, report.Raw, nil, false, "Outputs a text representation of the raw profile"},
-		"dot":    {c, report.Dot, nil, false, "Outputs a graph in DOT format"},
-		"top":    {c, report.Text, nil, false, "Outputs top entries in text form"},
-		"tree":   {c, report.Tree, nil, false, "Outputs a text rendering of call graph"},
-		"text":   {c, report.Text, nil, false, "Outputs top entries in text form"},
-		"disasm": {c, report.Dis, nil, true, "Output annotated assembly for functions matching regexp or address"},
-		"list":   {c, report.List, nil, true, "Output annotated source for functions matching regexp"},
-		"peek":   {c, report.Tree, nil, true, "Output callers/callees of functions matching regexp"},
-
-		// Save binary formats to a file
-		"callgrind": {c, report.Callgrind, awayFromTTY("callgraph.out"), false, "Outputs a graph in callgrind format"},
-		"proto":     {c, report.Proto, awayFromTTY("pb.gz"), false, "Outputs the profile in compressed protobuf format"},
-
-		// Generate report in DOT format and postprocess with dot
-		"gif": {c, report.Dot, invokeDot("gif"), false, "Outputs a graph image in GIF format"},
-		"pdf": {c, report.Dot, invokeDot("pdf"), false, "Outputs a graph in PDF format"},
-		"png": {c, report.Dot, invokeDot("png"), false, "Outputs a graph image in PNG format"},
-		"ps":  {c, report.Dot, invokeDot("ps"), false, "Outputs a graph in PS format"},
-
-		// Save SVG output into a file after including svgpan library
-		"svg": {c, report.Dot, saveSVGToFile(), false, "Outputs a graph in SVG format"},
-
-		// Visualize postprocessed dot output
-		"eog":    {c, report.Dot, invokeVisualizer(interactive, invokeDot("svg"), "svg", []string{"eog"}), false, "Visualize graph through eog"},
-		"evince": {c, report.Dot, invokeVisualizer(interactive, invokeDot("pdf"), "pdf", []string{"evince"}), false, "Visualize graph through evince"},
-		"gv":     {c, report.Dot, invokeVisualizer(interactive, invokeDot("ps"), "ps", []string{"gv --noantialias"}), false, "Visualize graph through gv"},
-		"web":    {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(), "svg", browsers()), false, "Visualize graph through web browser"},
-
-		// Visualize HTML directly generated by report.
-		"weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY("html"), "html", browsers()), true, "Output annotated source in HTML for functions matching regexp or address"},
-	}
-}
-
-// browsers returns a list of commands to attempt for web visualization
-// on the current platform
-func browsers() []string {
-	var cmds []string
-	if exe := os.Getenv("BROWSER"); exe != "" {
-		cmds = append(cmds, exe)
-	}
-	switch runtime.GOOS {
-	case "darwin":
-		cmds = append(cmds, "/usr/bin/open")
-	case "windows":
-		cmds = append(cmds, "cmd /c start")
-	default:
-		cmds = append(cmds, "xdg-open")
-	}
-	cmds = append(cmds, "chrome", "google-chrome", "firefox")
-	return cmds
-}
-
-// NewCompleter creates an autocompletion function for a set of commands.
-func NewCompleter(cs Commands) Completer {
-	return func(line string) string {
-		switch tokens := strings.Fields(line); len(tokens) {
-		case 0:
-			// Nothing to complete
-		case 1:
-			// Single token -- complete command name
-			found := ""
-			for c := range cs {
-				if strings.HasPrefix(c, tokens[0]) {
-					if found != "" {
-						return line
-					}
-					found = c
-				}
-			}
-			if found != "" {
-				return found
-			}
-		default:
-			// Multiple tokens -- complete using command completer
-			if c, ok := cs[tokens[0]]; ok {
-				if c.Complete != nil {
-					lastTokenIdx := len(tokens) - 1
-					lastToken := tokens[lastTokenIdx]
-					if strings.HasPrefix(lastToken, "-") {
-						lastToken = "-" + c.Complete(lastToken[1:])
-					} else {
-						lastToken = c.Complete(lastToken)
-					}
-					return strings.Join(append(tokens[:lastTokenIdx], lastToken), " ")
-				}
-			}
-		}
-		return line
-	}
-}
-
-// awayFromTTY saves the output in a file if it would otherwise go to
-// the terminal screen. This is used to avoid dumping binary data on
-// the screen.
-func awayFromTTY(format string) PostProcessor {
-	return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
-		if output == os.Stdout && ui.IsTerminal() {
-			tempFile, err := tempfile.New("", "profile", "."+format)
-			if err != nil {
-				return err
-			}
-			ui.PrintErr("Generating report in ", tempFile.Name())
-			_, err = fmt.Fprint(tempFile, input)
-			return err
-		}
-		_, err := fmt.Fprint(output, input)
-		return err
-	}
-}
-
-func invokeDot(format string) PostProcessor {
-	divert := awayFromTTY(format)
-	return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
-		if _, err := exec.LookPath("dot"); err != nil {
-			ui.PrintErr("Cannot find dot, have you installed Graphviz?")
-			return err
-		}
-		cmd := exec.Command("dot", "-T"+format)
-		var buf bytes.Buffer
-		cmd.Stdin, cmd.Stdout, cmd.Stderr = input, &buf, os.Stderr
-		if err := cmd.Run(); err != nil {
-			return err
-		}
-		return divert(&buf, output, ui)
-	}
-}
-
-func saveSVGToFile() PostProcessor {
-	generateSVG := invokeDot("svg")
-	divert := awayFromTTY("svg")
-	return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
-		baseSVG := &bytes.Buffer{}
-		generateSVG(input, baseSVG, ui)
-		massaged := &bytes.Buffer{}
-		fmt.Fprint(massaged, svg.Massage(*baseSVG))
-		return divert(massaged, output, ui)
-	}
-}
-
-var vizTmpDir string
-
-func makeVizTmpDir() error {
-	if vizTmpDir != "" {
-		return nil
-	}
-	name, err := ioutil.TempDir("", "pprof-")
-	if err != nil {
-		return err
-	}
-	tempfile.DeferDelete(name)
-	vizTmpDir = name
-	return nil
-}
-
-func invokeVisualizer(interactive **bool, format PostProcessor, suffix string, visualizers []string) PostProcessor {
-	return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
-		if err := makeVizTmpDir(); err != nil {
-			return err
-		}
-		tempFile, err := tempfile.New(vizTmpDir, "pprof", "."+suffix)
-		if err != nil {
-			return err
-		}
-		tempfile.DeferDelete(tempFile.Name())
-		if err = format(input, tempFile, ui); err != nil {
-			return err
-		}
-		tempFile.Close() // on windows, if the file is Open, start cannot access it.
-		// Try visualizers until one is successful
-		for _, v := range visualizers {
-			// Separate command and arguments for exec.Command.
-			args := strings.Split(v, " ")
-			if len(args) == 0 {
-				continue
-			}
-			viewer := exec.Command(args[0], append(args[1:], tempFile.Name())...)
-			viewer.Stderr = os.Stderr
-			if err = viewer.Start(); err == nil {
-				// The viewer might just send a message to another program
-				// to open the file. Give that program a little time to open the
-				// file before we remove it.
-				time.Sleep(1 * time.Second)
-
-				if !**interactive {
-					// In command-line mode, wait for the viewer to be closed
-					// before proceeding
-					return viewer.Wait()
-				}
-				return nil
-			}
-		}
-		return err
-	}
-}
diff --git a/src/cmd/internal/pprof/driver/driver.go b/src/cmd/internal/pprof/driver/driver.go
deleted file mode 100644
index 782acfd..0000000
--- a/src/cmd/internal/pprof/driver/driver.go
+++ /dev/null
@@ -1,1041 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package driver implements the core pprof functionality. It can be
-// parameterized with a flag implementation, fetch and symbolize
-// mechanisms.
-package driver
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"net/url"
-	"os"
-	"path/filepath"
-	"regexp"
-	"sort"
-	"strconv"
-	"strings"
-	"sync"
-	"time"
-
-	"cmd/internal/pprof/commands"
-	"cmd/internal/pprof/plugin"
-	"cmd/internal/pprof/profile"
-	"cmd/internal/pprof/report"
-	"cmd/internal/pprof/tempfile"
-)
-
-// PProf acquires a profile, and symbolizes it using a profile
-// manager. Then it generates a report formatted according to the
-// options selected through the flags package.
-func PProf(flagset plugin.FlagSet, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, overrides commands.Commands) error {
-	// Remove any temporary files created during pprof processing.
-	defer tempfile.Cleanup()
-
-	f, err := getFlags(flagset, overrides, ui)
-	if err != nil {
-		return err
-	}
-
-	obj.SetConfig(*f.flagTools)
-
-	sources := f.profileSource
-	if len(sources) > 1 {
-		source := sources[0]
-		// If the first argument is a supported object file, treat as executable.
-		if file, err := obj.Open(source, 0); err == nil {
-			file.Close()
-			f.profileExecName = source
-			sources = sources[1:]
-		} else if *f.flagBuildID == "" && isBuildID(source) {
-			f.flagBuildID = &source
-			sources = sources[1:]
-		}
-	}
-
-	// errMu protects concurrent accesses to errset and err. errset is set if an
-	// error is encountered by one of the goroutines grabbing a profile.
-	errMu, errset := sync.Mutex{}, false
-
-	// Fetch profiles.
-	wg := sync.WaitGroup{}
-	profs := make([]*profile.Profile, len(sources))
-	for i, source := range sources {
-		wg.Add(1)
-		go func(i int, src string) {
-			defer wg.Done()
-			p, grabErr := grabProfile(src, f.profileExecName, *f.flagBuildID, fetch, sym, obj, ui, f)
-			if grabErr != nil {
-				errMu.Lock()
-				defer errMu.Unlock()
-				errset, err = true, grabErr
-				return
-			}
-			profs[i] = p
-		}(i, source)
-	}
-	wg.Wait()
-	if errset {
-		return err
-	}
-
-	// Merge profiles.
-	prof := profs[0]
-	for _, p := range profs[1:] {
-		if err = prof.Merge(p, 1); err != nil {
-			return err
-		}
-	}
-
-	if *f.flagBase != "" {
-		// Fetch base profile and subtract from current profile.
-		base, err := grabProfile(*f.flagBase, f.profileExecName, *f.flagBuildID, fetch, sym, obj, ui, f)
-		if err != nil {
-			return err
-		}
-
-		if err = prof.Merge(base, -1); err != nil {
-			return err
-		}
-	}
-
-	if err := processFlags(prof, ui, f); err != nil {
-		return err
-	}
-
-	if !*f.flagRuntime {
-		prof.RemoveUninteresting()
-	}
-
-	if *f.flagInteractive {
-		return interactive(prof, obj, ui, f)
-	}
-
-	return generate(false, prof, obj, ui, f)
-}
-
-// isBuildID determines if the profile may contain a build ID, by
-// checking that it is a string of hex digits.
-func isBuildID(id string) bool {
-	return strings.Trim(id, "0123456789abcdefABCDEF") == ""
-}
-
-// adjustURL updates the profile source URL based on heuristics. It
-// will append ?seconds=sec for CPU profiles if not already
-// specified. Returns the hostname if the profile is remote.
-func adjustURL(source string, sec int, ui plugin.UI) (adjusted, host string, duration time.Duration) {
-	// If there is a local file with this name, just use it.
-	if _, err := os.Stat(source); err == nil {
-		return source, "", 0
-	}
-
-	url, err := url.Parse(source)
-
-	// Automatically add http:// to URLs of the form hostname:port/path.
-	// url.Parse treats "hostname" as the Scheme.
-	if err != nil || (url.Host == "" && url.Scheme != "" && url.Scheme != "file") {
-		url, err = url.Parse("http://" + source)
-		if err != nil {
-			return source, "", 0
-		}
-	}
-	if scheme := strings.ToLower(url.Scheme); scheme == "" || scheme == "file" {
-		url.Scheme = ""
-		return url.String(), "", 0
-	}
-
-	values := url.Query()
-	if urlSeconds := values.Get("seconds"); urlSeconds != "" {
-		if us, err := strconv.ParseInt(urlSeconds, 10, 32); err == nil {
-			if sec >= 0 {
-				ui.PrintErr("Overriding -seconds for URL ", source)
-			}
-			sec = int(us)
-		}
-	}
-
-	switch strings.ToLower(url.Path) {
-	case "", "/":
-		// Apply default /profilez.
-		url.Path = "/profilez"
-	case "/protoz":
-		// Rewrite to /profilez?type=proto
-		url.Path = "/profilez"
-		values.Set("type", "proto")
-	}
-
-	if hasDuration(url.Path) {
-		if sec > 0 {
-			duration = time.Duration(sec) * time.Second
-			values.Set("seconds", fmt.Sprintf("%d", sec))
-		} else {
-			// Assume default duration: 30 seconds
-			duration = 30 * time.Second
-		}
-	}
-	url.RawQuery = values.Encode()
-	return url.String(), url.Host, duration
-}
-
-func hasDuration(path string) bool {
-	for _, trigger := range []string{"profilez", "wallz", "/profile"} {
-		if strings.Contains(path, trigger) {
-			return true
-		}
-	}
-	return false
-}
-
-// preprocess does filtering and aggregation of a profile based on the
-// requested options.
-func preprocess(prof *profile.Profile, ui plugin.UI, f *flags) error {
-	if *f.flagFocus != "" || *f.flagIgnore != "" || *f.flagHide != "" {
-		focus, ignore, hide, err := compileFocusIgnore(*f.flagFocus, *f.flagIgnore, *f.flagHide)
-		if err != nil {
-			return err
-		}
-		fm, im, hm := prof.FilterSamplesByName(focus, ignore, hide)
-
-		warnNoMatches(fm, *f.flagFocus, "Focus", ui)
-		warnNoMatches(im, *f.flagIgnore, "Ignore", ui)
-		warnNoMatches(hm, *f.flagHide, "Hide", ui)
-	}
-
-	if *f.flagTagFocus != "" || *f.flagTagIgnore != "" {
-		focus, err := compileTagFilter(*f.flagTagFocus, ui)
-		if err != nil {
-			return err
-		}
-		ignore, err := compileTagFilter(*f.flagTagIgnore, ui)
-		if err != nil {
-			return err
-		}
-		fm, im := prof.FilterSamplesByTag(focus, ignore)
-
-		warnNoMatches(fm, *f.flagTagFocus, "TagFocus", ui)
-		warnNoMatches(im, *f.flagTagIgnore, "TagIgnore", ui)
-	}
-
-	return aggregate(prof, f)
-}
-
-func compileFocusIgnore(focus, ignore, hide string) (f, i, h *regexp.Regexp, err error) {
-	if focus != "" {
-		if f, err = regexp.Compile(focus); err != nil {
-			return nil, nil, nil, fmt.Errorf("parsing focus regexp: %v", err)
-		}
-	}
-
-	if ignore != "" {
-		if i, err = regexp.Compile(ignore); err != nil {
-			return nil, nil, nil, fmt.Errorf("parsing ignore regexp: %v", err)
-		}
-	}
-
-	if hide != "" {
-		if h, err = regexp.Compile(hide); err != nil {
-			return nil, nil, nil, fmt.Errorf("parsing hide regexp: %v", err)
-		}
-	}
-	return
-}
-
-func compileTagFilter(filter string, ui plugin.UI) (f func(string, string, int64) bool, err error) {
-	if filter == "" {
-		return nil, nil
-	}
-	if numFilter := parseTagFilterRange(filter); numFilter != nil {
-		ui.PrintErr("Interpreted '", filter, "' as range, not regexp")
-		return func(key, val string, num int64) bool {
-			if val != "" {
-				return false
-			}
-			return numFilter(num, key)
-		}, nil
-	}
-	fx, err := regexp.Compile(filter)
-	if err != nil {
-		return nil, err
-	}
-
-	return func(key, val string, num int64) bool {
-		if val == "" {
-			return false
-		}
-		return fx.MatchString(key + ":" + val)
-	}, nil
-}
-
-var tagFilterRangeRx = regexp.MustCompile("([[:digit:]]+)([[:alpha:]]+)")
-
-// parseTagFilterRange returns a function to checks if a value is
-// contained on the range described by a string. It can recognize
-// strings of the form:
-// "32kb" -- matches values == 32kb
-// ":64kb" -- matches values <= 64kb
-// "4mb:" -- matches values >= 4mb
-// "12kb:64mb" -- matches values between 12kb and 64mb (both included).
-func parseTagFilterRange(filter string) func(int64, string) bool {
-	ranges := tagFilterRangeRx.FindAllStringSubmatch(filter, 2)
-	if len(ranges) == 0 {
-		return nil // No ranges were identified
-	}
-	v, err := strconv.ParseInt(ranges[0][1], 10, 64)
-	if err != nil {
-		panic(fmt.Errorf("Failed to parse int %s: %v", ranges[0][1], err))
-	}
-	value, unit := report.ScaleValue(v, ranges[0][2], ranges[0][2])
-	if len(ranges) == 1 {
-		switch match := ranges[0][0]; filter {
-		case match:
-			return func(v int64, u string) bool {
-				sv, su := report.ScaleValue(v, u, unit)
-				return su == unit && sv == value
-			}
-		case match + ":":
-			return func(v int64, u string) bool {
-				sv, su := report.ScaleValue(v, u, unit)
-				return su == unit && sv >= value
-			}
-		case ":" + match:
-			return func(v int64, u string) bool {
-				sv, su := report.ScaleValue(v, u, unit)
-				return su == unit && sv <= value
-			}
-		}
-		return nil
-	}
-	if filter != ranges[0][0]+":"+ranges[1][0] {
-		return nil
-	}
-	if v, err = strconv.ParseInt(ranges[1][1], 10, 64); err != nil {
-		panic(fmt.Errorf("Failed to parse int %s: %v", ranges[1][1], err))
-	}
-	value2, unit2 := report.ScaleValue(v, ranges[1][2], unit)
-	if unit != unit2 {
-		return nil
-	}
-	return func(v int64, u string) bool {
-		sv, su := report.ScaleValue(v, u, unit)
-		return su == unit && sv >= value && sv <= value2
-	}
-}
-
-func warnNoMatches(match bool, rx, option string, ui plugin.UI) {
-	if !match && rx != "" && rx != "." {
-		ui.PrintErr(option + " expression matched no samples: " + rx)
-	}
-}
-
-// grabProfile fetches and symbolizes a profile.
-func grabProfile(source, exec, buildid string, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, f *flags) (*profile.Profile, error) {
-	source, host, duration := adjustURL(source, *f.flagSeconds, ui)
-	remote := host != ""
-
-	if remote {
-		ui.Print("Fetching profile from ", source)
-		if duration != 0 {
-			ui.Print("Please wait... (" + duration.String() + ")")
-		}
-	}
-
-	now := time.Now()
-	// Fetch profile from source.
-	// Give 50% slack on the timeout.
-	p, err := fetch(source, duration+duration/2, ui)
-	if err != nil {
-		return nil, err
-	}
-
-	// Update the time/duration if the profile source doesn't include it.
-	// TODO(rsilvera): Remove this when we remove support for legacy profiles.
-	if remote {
-		if p.TimeNanos == 0 {
-			p.TimeNanos = now.UnixNano()
-		}
-		if duration != 0 && p.DurationNanos == 0 {
-			p.DurationNanos = int64(duration)
-		}
-	}
-
-	// Replace executable/buildID with the options provided in the
-	// command line. Assume the executable is the first Mapping entry.
-	if exec != "" || buildid != "" {
-		if len(p.Mapping) == 0 {
-			// Create a fake mapping to hold the user option, and associate
-			// all samples to it.
-			m := &profile.Mapping{
-				ID: 1,
-			}
-			for _, l := range p.Location {
-				l.Mapping = m
-			}
-			p.Mapping = []*profile.Mapping{m}
-		}
-		if exec != "" {
-			p.Mapping[0].File = exec
-		}
-		if buildid != "" {
-			p.Mapping[0].BuildID = buildid
-		}
-	}
-
-	if err := sym(*f.flagSymbolize, source, p, obj, ui); err != nil {
-		return nil, err
-	}
-
-	// Save a copy of any remote profiles, unless the user is explicitly
-	// saving it.
-	if remote && !f.isFormat("proto") {
-		prefix := "pprof."
-		if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
-			prefix = prefix + filepath.Base(p.Mapping[0].File) + "."
-		}
-		if !strings.ContainsRune(host, os.PathSeparator) {
-			prefix = prefix + host + "."
-		}
-		for _, s := range p.SampleType {
-			prefix = prefix + s.Type + "."
-		}
-
-		dir := os.Getenv("PPROF_TMPDIR")
-		tempFile, err := tempfile.New(dir, prefix, ".pb.gz")
-		if err == nil {
-			if err = p.Write(tempFile); err == nil {
-				ui.PrintErr("Saved profile in ", tempFile.Name())
-			}
-		}
-		if err != nil {
-			ui.PrintErr("Could not save profile: ", err)
-		}
-	}
-
-	if err := p.Demangle(obj.Demangle); err != nil {
-		ui.PrintErr("Failed to demangle profile: ", err)
-	}
-
-	if err := p.CheckValid(); err != nil {
-		return nil, fmt.Errorf("Grab %s: %v", source, err)
-	}
-
-	return p, nil
-}
-
-type flags struct {
-	flagInteractive   *bool              // Accept commands interactively
-	flagCommands      map[string]*bool   // pprof commands without parameters
-	flagParamCommands map[string]*string // pprof commands with parameters
-
-	flagOutput *string // Output file name
-
-	flagCum      *bool // Sort by cumulative data
-	flagCallTree *bool // generate a context-sensitive call tree
-
-	flagAddresses *bool // Report at address level
-	flagLines     *bool // Report at source line level
-	flagFiles     *bool // Report at file level
-	flagFunctions *bool // Report at function level [default]
-
-	flagSymbolize *string // Symbolization options (=none to disable)
-	flagBuildID   *string // Override build if for first mapping
-
-	flagNodeCount    *int     // Max number of nodes to show
-	flagNodeFraction *float64 // Hide nodes below <f>*total
-	flagEdgeFraction *float64 // Hide edges below <f>*total
-	flagTrim         *bool    // Set to false to ignore NodeCount/*Fraction
-	flagRuntime      *bool    // Show runtime call frames in memory profiles
-	flagFocus        *string  // Restricts to paths going through a node matching regexp
-	flagIgnore       *string  // Skips paths going through any nodes matching regexp
-	flagHide         *string  // Skips sample locations matching regexp
-	flagTagFocus     *string  // Restrict to samples tagged with key:value matching regexp
-	flagTagIgnore    *string  // Discard samples tagged with key:value matching regexp
-	flagDropNegative *bool    // Skip negative values
-
-	flagBase *string // Source for base profile to user for comparison
-
-	flagSeconds *int // Length of time for dynamic profiles
-
-	flagTotalDelay  *bool // Display total delay at each region
-	flagContentions *bool // Display number of delays at each region
-	flagMeanDelay   *bool // Display mean delay at each region
-
-	flagInUseSpace   *bool    // Display in-use memory size
-	flagInUseObjects *bool    // Display in-use object counts
-	flagAllocSpace   *bool    // Display allocated memory size
-	flagAllocObjects *bool    // Display allocated object counts
-	flagDisplayUnit  *string  // Measurement unit to use on reports
-	flagDivideBy     *float64 // Ratio to divide sample values
-
-	flagSampleIndex *int  // Sample value to use in reports.
-	flagMean        *bool // Use mean of sample_index over count
-
-	flagTools       *string
-	profileSource   []string
-	profileExecName string
-
-	extraUsage string
-	commands   commands.Commands
-}
-
-func (f *flags) isFormat(format string) bool {
-	if fl := f.flagCommands[format]; fl != nil {
-		return *fl
-	}
-	if fl := f.flagParamCommands[format]; fl != nil {
-		return *fl != ""
-	}
-	return false
-}
-
-// String provides a printable representation for the current set of flags.
-func (f *flags) String(p *profile.Profile) string {
-	var ret string
-
-	if ix := *f.flagSampleIndex; ix != -1 {
-		ret += fmt.Sprintf("  %-25s : %d (%s)\n", "sample_index", ix, p.SampleType[ix].Type)
-	}
-	if ix := *f.flagMean; ix {
-		ret += boolFlagString("mean")
-	}
-	if *f.flagDisplayUnit != "minimum" {
-		ret += stringFlagString("unit", *f.flagDisplayUnit)
-	}
-
-	switch {
-	case *f.flagInteractive:
-		ret += boolFlagString("interactive")
-	}
-	for name, fl := range f.flagCommands {
-		if *fl {
-			ret += boolFlagString(name)
-		}
-	}
-
-	if *f.flagCum {
-		ret += boolFlagString("cum")
-	}
-	if *f.flagCallTree {
-		ret += boolFlagString("call_tree")
-	}
-
-	switch {
-	case *f.flagAddresses:
-		ret += boolFlagString("addresses")
-	case *f.flagLines:
-		ret += boolFlagString("lines")
-	case *f.flagFiles:
-		ret += boolFlagString("files")
-	case *f.flagFunctions:
-		ret += boolFlagString("functions")
-	}
-
-	if *f.flagNodeCount != -1 {
-		ret += intFlagString("nodecount", *f.flagNodeCount)
-	}
-
-	ret += floatFlagString("nodefraction", *f.flagNodeFraction)
-	ret += floatFlagString("edgefraction", *f.flagEdgeFraction)
-
-	if *f.flagFocus != "" {
-		ret += stringFlagString("focus", *f.flagFocus)
-	}
-	if *f.flagIgnore != "" {
-		ret += stringFlagString("ignore", *f.flagIgnore)
-	}
-	if *f.flagHide != "" {
-		ret += stringFlagString("hide", *f.flagHide)
-	}
-
-	if *f.flagTagFocus != "" {
-		ret += stringFlagString("tagfocus", *f.flagTagFocus)
-	}
-	if *f.flagTagIgnore != "" {
-		ret += stringFlagString("tagignore", *f.flagTagIgnore)
-	}
-
-	return ret
-}
-
-func boolFlagString(label string) string {
-	return fmt.Sprintf("  %-25s : true\n", label)
-}
-
-func stringFlagString(label, value string) string {
-	return fmt.Sprintf("  %-25s : %s\n", label, value)
-}
-
-func intFlagString(label string, value int) string {
-	return fmt.Sprintf("  %-25s : %d\n", label, value)
-}
-
-func floatFlagString(label string, value float64) string {
-	return fmt.Sprintf("  %-25s : %f\n", label, value)
-}
-
-// Utility routines to set flag values.
-func newBool(b bool) *bool {
-	return &b
-}
-
-func newString(s string) *string {
-	return &s
-}
-
-func newFloat64(fl float64) *float64 {
-	return &fl
-}
-
-func newInt(i int) *int {
-	return &i
-}
-
-func (f *flags) usage(ui plugin.UI) {
-	var commandMsg []string
-	for name, cmd := range f.commands {
-		if cmd.HasParam {
-			name = name + "=p"
-		}
-		commandMsg = append(commandMsg,
-			fmt.Sprintf("  -%-16s %s", name, cmd.Usage))
-	}
-
-	sort.Strings(commandMsg)
-
-	text := usageMsgHdr + strings.Join(commandMsg, "\n") + "\n" + usageMsg + "\n"
-	if f.extraUsage != "" {
-		text += f.extraUsage + "\n"
-	}
-	text += usageMsgVars
-	ui.Print(text)
-}
-
-func getFlags(flag plugin.FlagSet, overrides commands.Commands, ui plugin.UI) (*flags, error) {
-	f := &flags{
-		flagInteractive:   flag.Bool("interactive", false, "Accepts commands interactively"),
-		flagCommands:      make(map[string]*bool),
-		flagParamCommands: make(map[string]*string),
-
-		// Filename for file-based output formats, stdout by default.
-		flagOutput: flag.String("output", "", "Output filename for file-based outputs "),
-		// Comparisons.
-		flagBase:         flag.String("base", "", "Source for base profile for comparison"),
-		flagDropNegative: flag.Bool("drop_negative", false, "Ignore negative differences"),
-
-		// Data sorting criteria.
-		flagCum: flag.Bool("cum", false, "Sort by cumulative data"),
-		// Graph handling options.
-		flagCallTree: flag.Bool("call_tree", false, "Create a context-sensitive call tree"),
-		// Granularity of output resolution.
-		flagAddresses: flag.Bool("addresses", false, "Report at address level"),
-		flagLines:     flag.Bool("lines", false, "Report at source line level"),
-		flagFiles:     flag.Bool("files", false, "Report at source file level"),
-		flagFunctions: flag.Bool("functions", false, "Report at function level [default]"),
-		// Internal options.
-		flagSymbolize: flag.String("symbolize", "", "Options for profile symbolization"),
-		flagBuildID:   flag.String("buildid", "", "Override build id for first mapping"),
-		// Filtering options
-		flagNodeCount:    flag.Int("nodecount", -1, "Max number of nodes to show"),
-		flagNodeFraction: flag.Float64("nodefraction", 0.005, "Hide nodes below <f>*total"),
-		flagEdgeFraction: flag.Float64("edgefraction", 0.001, "Hide edges below <f>*total"),
-		flagTrim:         flag.Bool("trim", true, "Honor nodefraction/edgefraction/nodecount defaults"),
-		flagRuntime:      flag.Bool("runtime", false, "Show runtime call frames in memory profiles"),
-		flagFocus:        flag.String("focus", "", "Restricts to paths going through a node matching regexp"),
-		flagIgnore:       flag.String("ignore", "", "Skips paths going through any nodes matching regexp"),
-		flagHide:         flag.String("hide", "", "Skips nodes matching regexp"),
-		flagTagFocus:     flag.String("tagfocus", "", "Restrict to samples with tags in range or matched by regexp"),
-		flagTagIgnore:    flag.String("tagignore", "", "Discard samples with tags in range or matched by regexp"),
-		// CPU profile options
-		flagSeconds: flag.Int("seconds", -1, "Length of time for dynamic profiles"),
-		// Heap profile options
-		flagInUseSpace:   flag.Bool("inuse_space", false, "Display in-use memory size"),
-		flagInUseObjects: flag.Bool("inuse_objects", false, "Display in-use object counts"),
-		flagAllocSpace:   flag.Bool("alloc_space", false, "Display allocated memory size"),
-		flagAllocObjects: flag.Bool("alloc_objects", false, "Display allocated object counts"),
-		flagDisplayUnit:  flag.String("unit", "minimum", "Measurement units to display"),
-		flagDivideBy:     flag.Float64("divide_by", 1.0, "Ratio to divide all samples before visualization"),
-		flagSampleIndex:  flag.Int("sample_index", -1, "Index of sample value to report"),
-		flagMean:         flag.Bool("mean", false, "Average sample value over first value (count)"),
-		// Contention profile options
-		flagTotalDelay:  flag.Bool("total_delay", false, "Display total delay at each region"),
-		flagContentions: flag.Bool("contentions", false, "Display number of delays at each region"),
-		flagMeanDelay:   flag.Bool("mean_delay", false, "Display mean delay at each region"),
-		flagTools:       flag.String("tools", os.Getenv("PPROF_TOOLS"), "Path for object tool pathnames"),
-		extraUsage:      flag.ExtraUsage(),
-	}
-
-	// Flags used during command processing
-	interactive := &f.flagInteractive
-	f.commands = commands.PProf(functionCompleter, interactive)
-
-	// Override commands
-	for name, cmd := range overrides {
-		f.commands[name] = cmd
-	}
-
-	for name, cmd := range f.commands {
-		if cmd.HasParam {
-			f.flagParamCommands[name] = flag.String(name, "", "Generate a report in "+name+" format, matching regexp")
-		} else {
-			f.flagCommands[name] = flag.Bool(name, false, "Generate a report in "+name+" format")
-		}
-	}
-
-	args := flag.Parse(func() { f.usage(ui) })
-	if len(args) == 0 {
-		return nil, fmt.Errorf("no profile source specified")
-	}
-
-	f.profileSource = args
-
-	// Instruct legacy heapz parsers to grab historical allocation data,
-	// instead of the default in-use data. Not available with tcmalloc.
-	if *f.flagAllocSpace || *f.flagAllocObjects {
-		profile.LegacyHeapAllocated = true
-	}
-
-	if profileDir := os.Getenv("PPROF_TMPDIR"); profileDir == "" {
-		profileDir = os.Getenv("HOME") + "/pprof"
-		os.Setenv("PPROF_TMPDIR", profileDir)
-		if err := os.MkdirAll(profileDir, 0755); err != nil {
-			return nil, fmt.Errorf("failed to access temp dir %s: %v", profileDir, err)
-		}
-	}
-
-	return f, nil
-}
-
-func processFlags(p *profile.Profile, ui plugin.UI, f *flags) error {
-	flagDis := f.isFormat("disasm")
-	flagPeek := f.isFormat("peek")
-	flagWebList := f.isFormat("weblist")
-	flagList := f.isFormat("list")
-
-	if flagDis || flagWebList {
-		// Collect all samples at address granularity for assembly
-		// listing.
-		f.flagNodeCount = newInt(0)
-		f.flagAddresses = newBool(true)
-		f.flagLines = newBool(false)
-		f.flagFiles = newBool(false)
-		f.flagFunctions = newBool(false)
-	}
-
-	if flagPeek {
-		// Collect all samples at function granularity for peek command
-		f.flagNodeCount = newInt(0)
-		f.flagAddresses = newBool(false)
-		f.flagLines = newBool(false)
-		f.flagFiles = newBool(false)
-		f.flagFunctions = newBool(true)
-	}
-
-	if flagList {
-		// Collect all samples at fileline granularity for source
-		// listing.
-		f.flagNodeCount = newInt(0)
-		f.flagAddresses = newBool(false)
-		f.flagLines = newBool(true)
-		f.flagFiles = newBool(false)
-		f.flagFunctions = newBool(false)
-	}
-
-	if !*f.flagTrim {
-		f.flagNodeCount = newInt(0)
-		f.flagNodeFraction = newFloat64(0)
-		f.flagEdgeFraction = newFloat64(0)
-	}
-
-	if oc := countFlagMap(f.flagCommands, f.flagParamCommands); oc == 0 {
-		f.flagInteractive = newBool(true)
-	} else if oc > 1 {
-		f.usage(ui)
-		return fmt.Errorf("must set at most one output format")
-	}
-
-	// Apply nodecount defaults for non-interactive mode. The
-	// interactive shell will apply defaults for the interactive mode.
-	if *f.flagNodeCount < 0 && !*f.flagInteractive {
-		switch {
-		default:
-			f.flagNodeCount = newInt(80)
-		case f.isFormat("text"):
-			f.flagNodeCount = newInt(0)
-		}
-	}
-
-	// Apply legacy options and diagnose conflicts.
-	if rc := countFlags([]*bool{f.flagAddresses, f.flagLines, f.flagFiles, f.flagFunctions}); rc == 0 {
-		f.flagFunctions = newBool(true)
-	} else if rc > 1 {
-		f.usage(ui)
-		return fmt.Errorf("must set at most one granularity option")
-	}
-
-	var err error
-	si, sm := *f.flagSampleIndex, *f.flagMean || *f.flagMeanDelay
-	si, err = sampleIndex(p, &f.flagTotalDelay, si, 1, "delay", "-total_delay", err)
-	si, err = sampleIndex(p, &f.flagMeanDelay, si, 1, "delay", "-mean_delay", err)
-	si, err = sampleIndex(p, &f.flagContentions, si, 0, "contentions", "-contentions", err)
-
-	si, err = sampleIndex(p, &f.flagInUseSpace, si, 1, "inuse_space", "-inuse_space", err)
-	si, err = sampleIndex(p, &f.flagInUseObjects, si, 0, "inuse_objects", "-inuse_objects", err)
-	si, err = sampleIndex(p, &f.flagAllocSpace, si, 1, "alloc_space", "-alloc_space", err)
-	si, err = sampleIndex(p, &f.flagAllocObjects, si, 0, "alloc_objects", "-alloc_objects", err)
-
-	if si == -1 {
-		// Use last value if none is requested.
-		si = len(p.SampleType) - 1
-	} else if si < 0 || si >= len(p.SampleType) {
-		err = fmt.Errorf("sample_index value %d out of range [0..%d]", si, len(p.SampleType)-1)
-	}
-
-	if err != nil {
-		f.usage(ui)
-		return err
-	}
-	f.flagSampleIndex, f.flagMean = newInt(si), newBool(sm)
-	return nil
-}
-
-func sampleIndex(p *profile.Profile, flag **bool,
-	sampleIndex int,
-	newSampleIndex int,
-	sampleType, option string,
-	err error) (int, error) {
-	if err != nil || !**flag {
-		return sampleIndex, err
-	}
-	*flag = newBool(false)
-	if sampleIndex != -1 {
-		return 0, fmt.Errorf("set at most one sample value selection option")
-	}
-	if newSampleIndex >= len(p.SampleType) ||
-		p.SampleType[newSampleIndex].Type != sampleType {
-		return 0, fmt.Errorf("option %s not valid for this profile", option)
-	}
-	return newSampleIndex, nil
-}
-
-func countFlags(bs []*bool) int {
-	var c int
-	for _, b := range bs {
-		if *b {
-			c++
-		}
-	}
-	return c
-}
-
-func countFlagMap(bms map[string]*bool, bmrxs map[string]*string) int {
-	var c int
-	for _, b := range bms {
-		if *b {
-			c++
-		}
-	}
-	for _, s := range bmrxs {
-		if *s != "" {
-			c++
-		}
-	}
-	return c
-}
-
-var usageMsgHdr = "usage: pprof [options] [binary] <profile source> ...\n" +
-	"Output format (only set one):\n"
-
-var usageMsg = "Output file parameters (for file-based output formats):\n" +
-	"  -output=f         Generate output on file f (stdout by default)\n" +
-	"Output granularity (only set one):\n" +
-	"  -functions        Report at function level [default]\n" +
-	"  -files            Report at source file level\n" +
-	"  -lines            Report at source line level\n" +
-	"  -addresses        Report at address level\n" +
-	"Comparison options:\n" +
-	"  -base <profile>   Show delta from this profile\n" +
-	"  -drop_negative    Ignore negative differences\n" +
-	"Sorting options:\n" +
-	"  -cum              Sort by cumulative data\n\n" +
-	"Dynamic profile options:\n" +
-	"  -seconds=N        Length of time for dynamic profiles\n" +
-	"Profile trimming options:\n" +
-	"  -nodecount=N      Max number of nodes to show\n" +
-	"  -nodefraction=f   Hide nodes below <f>*total\n" +
-	"  -edgefraction=f   Hide edges below <f>*total\n" +
-	"Sample value selection option (by index):\n" +
-	"  -sample_index      Index of sample value to display\n" +
-	"  -mean              Average sample value over first value\n" +
-	"Sample value selection option (for heap profiles):\n" +
-	"  -inuse_space      Display in-use memory size\n" +
-	"  -inuse_objects    Display in-use object counts\n" +
-	"  -alloc_space      Display allocated memory size\n" +
-	"  -alloc_objects    Display allocated object counts\n" +
-	"Sample value selection option (for contention profiles):\n" +
-	"  -total_delay      Display total delay at each region\n" +
-	"  -contentions      Display number of delays at each region\n" +
-	"  -mean_delay       Display mean delay at each region\n" +
-	"Filtering options:\n" +
-	"  -runtime          Show runtime call frames in memory profiles\n" +
-	"  -focus=r          Restricts to paths going through a node matching regexp\n" +
-	"  -ignore=r         Skips paths going through any nodes matching regexp\n" +
-	"  -tagfocus=r       Restrict to samples tagged with key:value matching regexp\n" +
-	"                    Restrict to samples with numeric tags in range (eg \"32kb:1mb\")\n" +
-	"  -tagignore=r      Discard samples tagged with key:value matching regexp\n" +
-	"                    Avoid samples with numeric tags in range (eg \"1mb:\")\n" +
-	"Miscellaneous:\n" +
-	"  -call_tree        Generate a context-sensitive call tree\n" +
-	"  -unit=u           Convert all samples to unit u for display\n" +
-	"  -divide_by=f      Scale all samples by dividing them by f\n" +
-	"  -buildid=id       Override build id for main binary in profile\n" +
-	"  -tools=path       Search path for object-level tools\n" +
-	"  -help             This message"
-
-var usageMsgVars = "Environment Variables:\n" +
-	"   PPROF_TMPDIR       Location for saved profiles (default $HOME/pprof)\n" +
-	"   PPROF_TOOLS        Search path for object-level tools\n" +
-	"   PPROF_BINARY_PATH  Search path for local binary files\n" +
-	"                      default: $HOME/pprof/binaries\n" +
-	"                      finds binaries by $name and $buildid/$name"
-
-func aggregate(prof *profile.Profile, f *flags) error {
-	switch {
-	case f.isFormat("proto"), f.isFormat("raw"):
-		// No aggregation for raw profiles.
-	case f.isFormat("callgrind"):
-		// Aggregate to file/line for callgrind.
-		fallthrough
-	case *f.flagLines:
-		return prof.Aggregate(true, true, true, true, false)
-	case *f.flagFiles:
-		return prof.Aggregate(true, false, true, false, false)
-	case *f.flagFunctions:
-		return prof.Aggregate(true, true, false, false, false)
-	case f.isFormat("weblist"), f.isFormat("disasm"):
-		return prof.Aggregate(false, true, true, true, true)
-	}
-	return nil
-}
-
-// parseOptions parses the options into report.Options
-// Returns a function to postprocess the report after generation.
-func parseOptions(f *flags) (o *report.Options, p commands.PostProcessor, err error) {
-
-	if *f.flagDivideBy == 0 {
-		return nil, nil, fmt.Errorf("zero divisor specified")
-	}
-
-	o = &report.Options{
-		CumSort:        *f.flagCum,
-		CallTree:       *f.flagCallTree,
-		PrintAddresses: *f.flagAddresses,
-		DropNegative:   *f.flagDropNegative,
-		Ratio:          1 / *f.flagDivideBy,
-
-		NodeCount:    *f.flagNodeCount,
-		NodeFraction: *f.flagNodeFraction,
-		EdgeFraction: *f.flagEdgeFraction,
-		OutputUnit:   *f.flagDisplayUnit,
-	}
-
-	for cmd, b := range f.flagCommands {
-		if *b {
-			pcmd := f.commands[cmd]
-			o.OutputFormat = pcmd.Format
-			return o, pcmd.PostProcess, nil
-		}
-	}
-
-	for cmd, rx := range f.flagParamCommands {
-		if *rx != "" {
-			pcmd := f.commands[cmd]
-			if o.Symbol, err = regexp.Compile(*rx); err != nil {
-				return nil, nil, fmt.Errorf("parsing -%s regexp: %v", cmd, err)
-			}
-			o.OutputFormat = pcmd.Format
-			return o, pcmd.PostProcess, nil
-		}
-	}
-
-	return nil, nil, fmt.Errorf("no output format selected")
-}
-
-type sampleValueFunc func(*profile.Sample) int64
-
-// sampleFormat returns a function to extract values out of a profile.Sample,
-// and the type/units of those values.
-func sampleFormat(p *profile.Profile, f *flags) (sampleValueFunc, string, string) {
-	valueIndex := *f.flagSampleIndex
-
-	if *f.flagMean {
-		return meanExtractor(valueIndex), "mean_" + p.SampleType[valueIndex].Type, p.SampleType[valueIndex].Unit
-	}
-
-	return valueExtractor(valueIndex), p.SampleType[valueIndex].Type, p.SampleType[valueIndex].Unit
-}
-
-func valueExtractor(ix int) sampleValueFunc {
-	return func(s *profile.Sample) int64 {
-		return s.Value[ix]
-	}
-}
-
-func meanExtractor(ix int) sampleValueFunc {
-	return func(s *profile.Sample) int64 {
-		if s.Value[0] == 0 {
-			return 0
-		}
-		return s.Value[ix] / s.Value[0]
-	}
-}
-
-func generate(interactive bool, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, f *flags) error {
-	o, postProcess, err := parseOptions(f)
-	if err != nil {
-		return err
-	}
-
-	var w io.Writer
-	if *f.flagOutput == "" {
-		w = os.Stdout
-	} else {
-		ui.PrintErr("Generating report in ", *f.flagOutput)
-		outputFile, err := os.Create(*f.flagOutput)
-		if err != nil {
-			return err
-		}
-		defer outputFile.Close()
-		w = outputFile
-	}
-
-	if prof.Empty() {
-		return fmt.Errorf("profile is empty")
-	}
-
-	value, stype, unit := sampleFormat(prof, f)
-	o.SampleType = stype
-	rpt := report.New(prof, *o, value, unit)
-
-	// Do not apply filters if we're just generating a proto, so we
-	// still have all the data.
-	if o.OutputFormat != report.Proto {
-		// Delay applying focus/ignore until after creating the report so
-		// the report reflects the total number of samples.
-		if err := preprocess(prof, ui, f); err != nil {
-			return err
-		}
-	}
-
-	if postProcess == nil {
-		return report.Generate(w, rpt, obj)
-	}
-
-	var dot bytes.Buffer
-	if err = report.Generate(&dot, rpt, obj); err != nil {
-		return err
-	}
-
-	return postProcess(&dot, w, ui)
-}
diff --git a/src/cmd/internal/pprof/driver/interactive.go b/src/cmd/internal/pprof/driver/interactive.go
deleted file mode 100644
index 1b08226..0000000
--- a/src/cmd/internal/pprof/driver/interactive.go
+++ /dev/null
@@ -1,492 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package driver
-
-import (
-	"fmt"
-	"io"
-	"regexp"
-	"sort"
-	"strconv"
-	"strings"
-
-	"cmd/internal/pprof/commands"
-	"cmd/internal/pprof/plugin"
-	"cmd/internal/pprof/profile"
-)
-
-var profileFunctionNames = []string{}
-
-// functionCompleter replaces provided substring with a function
-// name retrieved from a profile if a single match exists. Otherwise,
-// it returns unchanged substring. It defaults to no-op if the profile
-// is not specified.
-func functionCompleter(substring string) string {
-	found := ""
-	for _, fName := range profileFunctionNames {
-		if strings.Contains(fName, substring) {
-			if found != "" {
-				return substring
-			}
-			found = fName
-		}
-	}
-	if found != "" {
-		return found
-	}
-	return substring
-}
-
-// updateAutoComplete enhances autocompletion with information that can be
-// retrieved from the profile
-func updateAutoComplete(p *profile.Profile) {
-	profileFunctionNames = nil // remove function names retrieved previously
-	for _, fn := range p.Function {
-		profileFunctionNames = append(profileFunctionNames, fn.Name)
-	}
-}
-
-// splitCommand splits the command line input into tokens separated by
-// spaces. Takes care to separate commands of the form 'top10' into
-// two tokens: 'top' and '10'
-func splitCommand(input string) []string {
-	fields := strings.Fields(input)
-	if num := strings.IndexAny(fields[0], "0123456789"); num != -1 {
-		inputNumber := fields[0][num:]
-		fields[0] = fields[0][:num]
-		fields = append([]string{fields[0], inputNumber}, fields[1:]...)
-	}
-	return fields
-}
-
-// interactive displays a prompt and reads commands for profile
-// manipulation/visualization.
-func interactive(p *profile.Profile, obj plugin.ObjTool, ui plugin.UI, f *flags) error {
-	updateAutoComplete(p)
-
-	// Enter command processing loop.
-	ui.Print("Entering interactive mode (type \"help\" for commands)")
-	ui.SetAutoComplete(commands.NewCompleter(f.commands))
-
-	for {
-		input, err := readCommand(p, ui, f)
-		if err != nil {
-			if err != io.EOF {
-				return err
-			}
-			if input == "" {
-				return nil
-			}
-		}
-		// Process simple commands.
-		switch input {
-		case "":
-			continue
-		case ":":
-			f.flagFocus = newString("")
-			f.flagIgnore = newString("")
-			f.flagTagFocus = newString("")
-			f.flagTagIgnore = newString("")
-			f.flagHide = newString("")
-			continue
-		}
-
-		fields := splitCommand(input)
-		// Process report generation commands.
-		if _, ok := f.commands[fields[0]]; ok {
-			if err := generateReport(p, fields, obj, ui, f); err != nil {
-				if err == io.EOF {
-					return nil
-				}
-				ui.PrintErr(err)
-			}
-			continue
-		}
-
-		switch cmd := fields[0]; cmd {
-		case "help":
-			commandHelp(fields, ui, f)
-			continue
-		case "exit", "quit":
-			return nil
-		}
-
-		// Process option settings.
-		if of, err := optFlags(p, input, f); err == nil {
-			f = of
-		} else {
-			ui.PrintErr("Error: ", err.Error())
-		}
-	}
-}
-
-func generateReport(p *profile.Profile, cmd []string, obj plugin.ObjTool, ui plugin.UI, f *flags) error {
-	prof := p.Copy()
-
-	cf, err := cmdFlags(prof, cmd, ui, f)
-	if err != nil {
-		return err
-	}
-
-	return generate(true, prof, obj, ui, cf)
-}
-
-// validateRegex checks if a string is a valid regular expression.
-func validateRegex(v string) error {
-	_, err := regexp.Compile(v)
-	return err
-}
-
-// readCommand prompts for and reads the next command.
-func readCommand(p *profile.Profile, ui plugin.UI, f *flags) (string, error) {
-	//ui.Print("Options:\n", f.String(p))
-	s, err := ui.ReadLine()
-	return strings.TrimSpace(s), err
-}
-
-func commandHelp(_ []string, ui plugin.UI, f *flags) error {
-	help := `
- Commands:
-   cmd [n] [--cum] [focus_regex]* [-ignore_regex]*
-       Produce a text report with the top n entries.
-       Include samples matching focus_regex, and exclude ignore_regex.
-       Add --cum to sort using cumulative data.
-       Available commands:
-`
-	var commands []string
-	for name, cmd := range f.commands {
-		commands = append(commands, fmt.Sprintf("         %-12s %s", name, cmd.Usage))
-	}
-	sort.Strings(commands)
-
-	help = help + strings.Join(commands, "\n") + `
-   peek func_regex
-       Display callers and callees of functions matching func_regex.
-
-   dot [n] [focus_regex]* [-ignore_regex]* [>file]
-       Produce an annotated callgraph with the top n entries.
-       Include samples matching focus_regex, and exclude ignore_regex.
-       For other outputs, replace dot with:
-       - Graphic formats: dot, svg, pdf, ps, gif, png (use > to name output file)
-       - Graph viewer:    gv, web, evince, eog
-
-   callgrind [n] [focus_regex]* [-ignore_regex]* [>file]
-       Produce a file in callgrind-compatible format.
-       Include samples matching focus_regex, and exclude ignore_regex.
-
-   weblist func_regex [-ignore_regex]*
-       Show annotated source with interspersed assembly in a web browser.
-
-   list func_regex [-ignore_regex]*
-       Print source for routines matching func_regex, and exclude ignore_regex.
-
-   disasm func_regex [-ignore_regex]*
-       Disassemble routines matching func_regex, and exclude ignore_regex.
-
-   tags tag_regex [-ignore_regex]*
-       List tags with key:value matching tag_regex and exclude ignore_regex.
-
-   quit/exit/^D
- 	     Exit pprof.
-
-   option=value
-       The following options can be set individually:
-           cum/flat:           Sort entries based on cumulative or flat data
-           call_tree:          Build context-sensitive call trees
-           nodecount:          Max number of entries to display
-           nodefraction:       Min frequency ratio of nodes to display
-           edgefraction:       Min frequency ratio of edges to display
-           focus/ignore:       Regexp to include/exclude samples by name/file
-           tagfocus/tagignore: Regexp or value range to filter samples by tag
-                               eg "1mb", "1mb:2mb", ":64kb"
-
-           functions:          Level of aggregation for sample data
-           files:
-           lines:
-           addresses:
-
-           unit:               Measurement unit to use on reports
-
-           Sample value selection by index:
-            sample_index:      Index of sample value to display
-            mean:              Average sample value over first value
-
-           Sample value selection by name:
-            alloc_space        for heap profiles
-            alloc_objects
-            inuse_space
-            inuse_objects
-
-            total_delay        for contention profiles
-            mean_delay
-            contentions
-
-   :   Clear focus/ignore/hide/tagfocus/tagignore`
-
-	ui.Print(help)
-	return nil
-}
-
-// cmdFlags parses the options of an interactive command and returns
-// an updated flags object.
-func cmdFlags(prof *profile.Profile, input []string, ui plugin.UI, f *flags) (*flags, error) {
-	cf := *f
-
-	var focus, ignore string
-	output := *cf.flagOutput
-	nodeCount := *cf.flagNodeCount
-	cmd := input[0]
-
-	// Update output flags based on parameters.
-	tokens := input[1:]
-	for p := 0; p < len(tokens); p++ {
-		t := tokens[p]
-		if t == "" {
-			continue
-		}
-		if c, err := strconv.ParseInt(t, 10, 32); err == nil {
-			nodeCount = int(c)
-			continue
-		}
-		switch t[0] {
-		case '>':
-			if len(t) > 1 {
-				output = t[1:]
-				continue
-			}
-			// find next token
-			for p++; p < len(tokens); p++ {
-				if tokens[p] != "" {
-					output = tokens[p]
-					break
-				}
-			}
-		case '-':
-			if t == "--cum" || t == "-cum" {
-				cf.flagCum = newBool(true)
-				continue
-			}
-			ignore = catRegex(ignore, t[1:])
-		default:
-			focus = catRegex(focus, t)
-		}
-	}
-
-	pcmd, ok := f.commands[cmd]
-	if !ok {
-		return nil, fmt.Errorf("Unexpected parse failure: %v", input)
-	}
-	// Reset flags
-	cf.flagCommands = make(map[string]*bool)
-	cf.flagParamCommands = make(map[string]*string)
-
-	if !pcmd.HasParam {
-		cf.flagCommands[cmd] = newBool(true)
-
-		switch cmd {
-		case "tags":
-			cf.flagTagFocus = newString(focus)
-			cf.flagTagIgnore = newString(ignore)
-		default:
-			cf.flagFocus = newString(catRegex(*cf.flagFocus, focus))
-			cf.flagIgnore = newString(catRegex(*cf.flagIgnore, ignore))
-		}
-	} else {
-		if focus == "" {
-			focus = "."
-		}
-		cf.flagParamCommands[cmd] = newString(focus)
-		cf.flagIgnore = newString(catRegex(*cf.flagIgnore, ignore))
-	}
-
-	if nodeCount < 0 {
-		switch cmd {
-		case "text", "top":
-			// Default text/top to 10 nodes on interactive mode
-			nodeCount = 10
-		default:
-			nodeCount = 80
-		}
-	}
-
-	cf.flagNodeCount = newInt(nodeCount)
-	cf.flagOutput = newString(output)
-
-	// Do regular flags processing
-	if err := processFlags(prof, ui, &cf); err != nil {
-		cf.usage(ui)
-		return nil, err
-	}
-
-	return &cf, nil
-}
-
-func catRegex(a, b string) string {
-	if a == "" {
-		return b
-	}
-	if b == "" {
-		return a
-	}
-	return a + "|" + b
-}
-
-// optFlags parses an interactive option setting and returns
-// an updated flags object.
-func optFlags(p *profile.Profile, input string, f *flags) (*flags, error) {
-	inputs := strings.SplitN(input, "=", 2)
-	option := strings.ToLower(strings.TrimSpace(inputs[0]))
-	var value string
-	if len(inputs) == 2 {
-		value = strings.TrimSpace(inputs[1])
-	}
-
-	of := *f
-
-	var err error
-	var bv bool
-	var uv uint64
-	var fv float64
-
-	switch option {
-	case "cum":
-		if bv, err = parseBool(value); err != nil {
-			return nil, err
-		}
-		of.flagCum = newBool(bv)
-	case "flat":
-		if bv, err = parseBool(value); err != nil {
-			return nil, err
-		}
-		of.flagCum = newBool(!bv)
-	case "call_tree":
-		if bv, err = parseBool(value); err != nil {
-			return nil, err
-		}
-		of.flagCallTree = newBool(bv)
-	case "unit":
-		of.flagDisplayUnit = newString(value)
-	case "sample_index":
-		if uv, err = strconv.ParseUint(value, 10, 32); err != nil {
-			return nil, err
-		}
-		if ix := int(uv); ix < 0 || ix >= len(p.SampleType) {
-			return nil, fmt.Errorf("sample_index out of range [0..%d]", len(p.SampleType)-1)
-		}
-		of.flagSampleIndex = newInt(int(uv))
-	case "mean":
-		if bv, err = parseBool(value); err != nil {
-			return nil, err
-		}
-		of.flagMean = newBool(bv)
-	case "nodecount":
-		if uv, err = strconv.ParseUint(value, 10, 32); err != nil {
-			return nil, err
-		}
-		of.flagNodeCount = newInt(int(uv))
-	case "nodefraction":
-		if fv, err = strconv.ParseFloat(value, 64); err != nil {
-			return nil, err
-		}
-		of.flagNodeFraction = newFloat64(fv)
-	case "edgefraction":
-		if fv, err = strconv.ParseFloat(value, 64); err != nil {
-			return nil, err
-		}
-		of.flagEdgeFraction = newFloat64(fv)
-	case "focus":
-		if err = validateRegex(value); err != nil {
-			return nil, err
-		}
-		of.flagFocus = newString(value)
-	case "ignore":
-		if err = validateRegex(value); err != nil {
-			return nil, err
-		}
-		of.flagIgnore = newString(value)
-	case "tagfocus":
-		if err = validateRegex(value); err != nil {
-			return nil, err
-		}
-		of.flagTagFocus = newString(value)
-	case "tagignore":
-		if err = validateRegex(value); err != nil {
-			return nil, err
-		}
-		of.flagTagIgnore = newString(value)
-	case "hide":
-		if err = validateRegex(value); err != nil {
-			return nil, err
-		}
-		of.flagHide = newString(value)
-	case "addresses", "files", "lines", "functions":
-		if bv, err = parseBool(value); err != nil {
-			return nil, err
-		}
-		if !bv {
-			return nil, fmt.Errorf("select one of addresses/files/lines/functions")
-		}
-		setGranularityToggle(option, &of)
-	default:
-		if ix := findSampleIndex(p, "", option); ix >= 0 {
-			of.flagSampleIndex = newInt(ix)
-		} else if ix := findSampleIndex(p, "total_", option); ix >= 0 {
-			of.flagSampleIndex = newInt(ix)
-			of.flagMean = newBool(false)
-		} else if ix := findSampleIndex(p, "mean_", option); ix >= 1 {
-			of.flagSampleIndex = newInt(ix)
-			of.flagMean = newBool(true)
-		} else {
-			return nil, fmt.Errorf("unrecognized command: %s", input)
-		}
-	}
-	return &of, nil
-}
-
-// parseBool parses a string as a boolean value.
-func parseBool(v string) (bool, error) {
-	switch strings.ToLower(v) {
-	case "true", "t", "yes", "y", "1", "":
-		return true, nil
-	case "false", "f", "no", "n", "0":
-		return false, nil
-	}
-	return false, fmt.Errorf(`illegal input "%s" for bool value`, v)
-}
-
-func findSampleIndex(p *profile.Profile, prefix, sampleType string) int {
-	if !strings.HasPrefix(sampleType, prefix) {
-		return -1
-	}
-	sampleType = strings.TrimPrefix(sampleType, prefix)
-	for i, r := range p.SampleType {
-		if r.Type == sampleType {
-			return i
-		}
-	}
-	return -1
-}
-
-// setGranularityToggle manages the set of granularity options. These
-// operate as a toggle; turning one on turns the others off.
-func setGranularityToggle(o string, fl *flags) {
-	t, f := newBool(true), newBool(false)
-	fl.flagFunctions = f
-	fl.flagFiles = f
-	fl.flagLines = f
-	fl.flagAddresses = f
-	switch o {
-	case "functions":
-		fl.flagFunctions = t
-	case "files":
-		fl.flagFiles = t
-	case "lines":
-		fl.flagLines = t
-	case "addresses":
-		fl.flagAddresses = t
-	default:
-		panic(fmt.Errorf("unexpected option %s", o))
-	}
-}
diff --git a/src/cmd/internal/pprof/fetch/fetch.go b/src/cmd/internal/pprof/fetch/fetch.go
deleted file mode 100644
index ffd282e..0000000
--- a/src/cmd/internal/pprof/fetch/fetch.go
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package fetch provides an extensible mechanism to fetch a profile
-// from a data source.
-package fetch
-
-import (
-	"fmt"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"net/url"
-	"os"
-	"strings"
-	"time"
-
-	"cmd/internal/pprof/plugin"
-	"cmd/internal/pprof/profile"
-)
-
-// FetchProfile reads from a data source (network, file) and generates a
-// profile.
-func FetchProfile(source string, timeout time.Duration) (*profile.Profile, error) {
-	return Fetcher(source, timeout, plugin.StandardUI())
-}
-
-// Fetcher is the plugin.Fetcher version of FetchProfile.
-func Fetcher(source string, timeout time.Duration, ui plugin.UI) (*profile.Profile, error) {
-	var f io.ReadCloser
-	var err error
-
-	url, err := url.Parse(source)
-	if err == nil && url.Host != "" {
-		f, err = FetchURL(source, timeout)
-	} else {
-		f, err = os.Open(source)
-	}
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-	return profile.Parse(f)
-}
-
-// FetchURL fetches a profile from a URL using HTTP.
-func FetchURL(source string, timeout time.Duration) (io.ReadCloser, error) {
-	resp, err := httpGet(source, timeout)
-	if err != nil {
-		return nil, fmt.Errorf("http fetch %s: %v", source, err)
-	}
-	if resp.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("server response: %s", resp.Status)
-	}
-
-	return resp.Body, nil
-}
-
-// PostURL issues a POST to a URL over HTTP.
-func PostURL(source, post string) ([]byte, error) {
-	resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post))
-	if err != nil {
-		return nil, fmt.Errorf("http post %s: %v", source, err)
-	}
-	if resp.StatusCode != http.StatusOK {
-		return nil, fmt.Errorf("server response: %s", resp.Status)
-	}
-	defer resp.Body.Close()
-	return ioutil.ReadAll(resp.Body)
-}
-
-// httpGet is a wrapper around http.Get; it is defined as a variable
-// so it can be redefined during for testing.
-var httpGet = func(url string, timeout time.Duration) (*http.Response, error) {
-	client := &http.Client{
-		Transport: &http.Transport{
-			ResponseHeaderTimeout: timeout + 5*time.Second,
-		},
-	}
-	return client.Get(url)
-}
diff --git a/src/cmd/internal/pprof/plugin/plugin.go b/src/cmd/internal/pprof/plugin/plugin.go
deleted file mode 100644
index d5025d5..0000000
--- a/src/cmd/internal/pprof/plugin/plugin.go
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package plugin defines the plugin implementations that the main pprof driver requires.
-package plugin
-
-import (
-	"bufio"
-	"fmt"
-	"os"
-	"regexp"
-	"strings"
-	"time"
-
-	"cmd/internal/pprof/profile"
-)
-
-// A FlagSet creates and parses command-line flags.
-// It is similar to the standard flag.FlagSet.
-type FlagSet interface {
-	// Bool, Int, Float64, and String define new flags,
-	// like the functions of the same name in package flag.
-	Bool(name string, def bool, usage string) *bool
-	Int(name string, def int, usage string) *int
-	Float64(name string, def float64, usage string) *float64
-	String(name string, def string, usage string) *string
-
-	// ExtraUsage returns any additional text that should be
-	// printed after the standard usage message.
-	// The typical use of ExtraUsage is to show any custom flags
-	// defined by the specific pprof plugins being used.
-	ExtraUsage() string
-
-	// Parse initializes the flags with their values for this run
-	// and returns the non-flag command line arguments.
-	// If an unknown flag is encountered or there are no arguments,
-	// Parse should call usage and return nil.
-	Parse(usage func()) []string
-}
-
-// An ObjTool inspects shared libraries and executable files.
-type ObjTool interface {
-	// Open opens the named object file.
-	// If the object is a shared library, start is the address where
-	// it is mapped into memory in the address space being inspected.
-	Open(file string, start uint64) (ObjFile, error)
-
-	// Demangle translates a batch of symbol names from mangled
-	// form to human-readable form.
-	Demangle(names []string) (map[string]string, error)
-
-	// Disasm disassembles the named object file, starting at
-	// the start address and stopping at (before) the end address.
-	Disasm(file string, start, end uint64) ([]Inst, error)
-
-	// SetConfig configures the tool.
-	// The implementation defines the meaning of the string
-	// and can ignore it entirely.
-	SetConfig(config string)
-}
-
-// NoObjTool returns a trivial implementation of the ObjTool interface.
-// Open returns an error indicating that the requested file does not exist.
-// Demangle returns an empty map and a nil error.
-// Disasm returns an error.
-// SetConfig is a no-op.
-func NoObjTool() ObjTool {
-	return noObjTool{}
-}
-
-type noObjTool struct{}
-
-func (noObjTool) Open(file string, start uint64) (ObjFile, error) {
-	return nil, &os.PathError{Op: "open", Path: file, Err: os.ErrNotExist}
-}
-
-func (noObjTool) Demangle(name []string) (map[string]string, error) {
-	return make(map[string]string), nil
-}
-
-func (noObjTool) Disasm(file string, start, end uint64) ([]Inst, error) {
-	return nil, fmt.Errorf("disassembly not supported")
-}
-
-func (noObjTool) SetConfig(config string) {
-}
-
-// An ObjFile is a single object file: a shared library or executable.
-type ObjFile interface {
-	// Name returns the underlyinf file name, if available
-	Name() string
-
-	// Base returns the base address to use when looking up symbols in the file.
-	Base() uint64
-
-	// BuildID returns the GNU build ID of the file, or an empty string.
-	BuildID() string
-
-	// SourceLine reports the source line information for a given
-	// address in the file. Due to inlining, the source line information
-	// is in general a list of positions representing a call stack,
-	// with the leaf function first.
-	SourceLine(addr uint64) ([]Frame, error)
-
-	// Symbols returns a list of symbols in the object file.
-	// If r is not nil, Symbols restricts the list to symbols
-	// with names matching the regular expression.
-	// If addr is not zero, Symbols restricts the list to symbols
-	// containing that address.
-	Symbols(r *regexp.Regexp, addr uint64) ([]*Sym, error)
-
-	// Close closes the file, releasing associated resources.
-	Close() error
-}
-
-// A Frame describes a single line in a source file.
-type Frame struct {
-	Func string // name of function
-	File string // source file name
-	Line int    // line in file
-}
-
-// A Sym describes a single symbol in an object file.
-type Sym struct {
-	Name  []string // names of symbol (many if symbol was dedup'ed)
-	File  string   // object file containing symbol
-	Start uint64   // start virtual address
-	End   uint64   // virtual address of last byte in sym (Start+size-1)
-}
-
-// An Inst is a single instruction in an assembly listing.
-type Inst struct {
-	Addr uint64 // virtual address of instruction
-	Text string // instruction text
-	File string // source file
-	Line int    // source line
-}
-
-// A UI manages user interactions.
-type UI interface {
-	// Read returns a line of text (a command) read from the user.
-	ReadLine() (string, error)
-
-	// Print shows a message to the user.
-	// It formats the text as fmt.Print would and adds a final \n if not already present.
-	// For line-based UI, Print writes to standard error.
-	// (Standard output is reserved for report data.)
-	Print(...interface{})
-
-	// PrintErr shows an error message to the user.
-	// It formats the text as fmt.Print would and adds a final \n if not already present.
-	// For line-based UI, PrintErr writes to standard error.
-	PrintErr(...interface{})
-
-	// IsTerminal returns whether the UI is known to be tied to an
-	// interactive terminal (as opposed to being redirected to a file).
-	IsTerminal() bool
-
-	// SetAutoComplete instructs the UI to call complete(cmd) to obtain
-	// the auto-completion of cmd, if the UI supports auto-completion at all.
-	SetAutoComplete(complete func(string) string)
-}
-
-// StandardUI returns a UI that reads from standard input,
-// prints messages to standard output,
-// prints errors to standard error, and doesn't use auto-completion.
-func StandardUI() UI {
-	return &stdUI{r: bufio.NewReader(os.Stdin)}
-}
-
-type stdUI struct {
-	r *bufio.Reader
-}
-
-func (ui *stdUI) ReadLine() (string, error) {
-	os.Stdout.WriteString("(pprof) ")
-	return ui.r.ReadString('\n')
-}
-
-func (ui *stdUI) Print(args ...interface{}) {
-	ui.fprint(os.Stderr, args)
-}
-
-func (ui *stdUI) PrintErr(args ...interface{}) {
-	ui.fprint(os.Stderr, args)
-}
-
-func (ui *stdUI) IsTerminal() bool {
-	return false
-}
-
-func (ui *stdUI) SetAutoComplete(func(string) string) {
-}
-
-func (ui *stdUI) fprint(f *os.File, args []interface{}) {
-	text := fmt.Sprint(args...)
-	if !strings.HasSuffix(text, "\n") {
-		text += "\n"
-	}
-	f.WriteString(text)
-}
-
-// A Fetcher reads and returns the profile named by src.
-// It gives up after the given timeout, unless src contains a timeout override
-// (as defined by the implementation).
-// It can print messages to ui.
-type Fetcher func(src string, timeout time.Duration, ui UI) (*profile.Profile, error)
-
-// A Symbolizer annotates a profile with symbol information.
-// The profile was fetch from src.
-// The meaning of mode is defined by the implementation.
-type Symbolizer func(mode, src string, prof *profile.Profile, obj ObjTool, ui UI) error
diff --git a/src/cmd/internal/pprof/profile/legacy_profile.go b/src/cmd/internal/pprof/profile/legacy_profile.go
deleted file mode 100644
index 8ccfe45..0000000
--- a/src/cmd/internal/pprof/profile/legacy_profile.go
+++ /dev/null
@@ -1,1236 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file implements parsers to convert legacy profiles into the
-// profile.proto format.
-
-package profile
-
-import (
-	"bufio"
-	"bytes"
-	"fmt"
-	"io"
-	"math"
-	"regexp"
-	"strconv"
-	"strings"
-)
-
-var (
-	countStartRE = regexp.MustCompile(`\A(\w+) profile: total \d+\n\z`)
-	countRE      = regexp.MustCompile(`\A(\d+) @(( 0x[0-9a-f]+)+)\n\z`)
-
-	heapHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] *@ *(heap[_a-z0-9]*)/?(\d*)`)
-	heapSampleRE = regexp.MustCompile(`(-?\d+): *(-?\d+) *\[ *(\d+): *(\d+) *] @([ x0-9a-f]*)`)
-
-	contentionSampleRE = regexp.MustCompile(`(\d+) *(\d+) @([ x0-9a-f]*)`)
-
-	hexNumberRE = regexp.MustCompile(`0x[0-9a-f]+`)
-
-	growthHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ growthz`)
-
-	fragmentationHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ fragmentationz`)
-
-	threadzStartRE = regexp.MustCompile(`--- threadz \d+ ---`)
-	threadStartRE  = regexp.MustCompile(`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---`)
-
-	procMapsRE = regexp.MustCompile(`([[:xdigit:]]+)-([[:xdigit:]]+)\s+([-rwxp]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+):([[:xdigit:]]+)\s+([[:digit:]]+)\s*(\S+)?`)
-
-	briefMapsRE = regexp.MustCompile(`\s*([[:xdigit:]]+)-([[:xdigit:]]+):\s*(\S+)(\s.*@)?([[:xdigit:]]+)?`)
-
-	// LegacyHeapAllocated instructs the heapz parsers to use the
-	// allocated memory stats instead of the default in-use memory. Note
-	// that tcmalloc doesn't provide all allocated memory, only in-use
-	// stats.
-	LegacyHeapAllocated bool
-)
-
-func isSpaceOrComment(line string) bool {
-	trimmed := strings.TrimSpace(line)
-	return len(trimmed) == 0 || trimmed[0] == '#'
-}
-
-// parseGoCount parses a Go count profile (e.g., threadcreate or
-// goroutine) and returns a new Profile.
-func parseGoCount(b []byte) (*Profile, error) {
-	r := bytes.NewBuffer(b)
-
-	var line string
-	var err error
-	for {
-		// Skip past comments and empty lines seeking a real header.
-		line, err = r.ReadString('\n')
-		if err != nil {
-			return nil, err
-		}
-		if !isSpaceOrComment(line) {
-			break
-		}
-	}
-
-	m := countStartRE.FindStringSubmatch(line)
-	if m == nil {
-		return nil, errUnrecognized
-	}
-	profileType := m[1]
-	p := &Profile{
-		PeriodType: &ValueType{Type: profileType, Unit: "count"},
-		Period:     1,
-		SampleType: []*ValueType{{Type: profileType, Unit: "count"}},
-	}
-	locations := make(map[uint64]*Location)
-	for {
-		line, err = r.ReadString('\n')
-		if err != nil {
-			if err == io.EOF {
-				break
-			}
-			return nil, err
-		}
-		if isSpaceOrComment(line) {
-			continue
-		}
-		if strings.HasPrefix(line, "---") {
-			break
-		}
-		m := countRE.FindStringSubmatch(line)
-		if m == nil {
-			return nil, errMalformed
-		}
-		n, err := strconv.ParseInt(m[1], 0, 64)
-		if err != nil {
-			return nil, errMalformed
-		}
-		fields := strings.Fields(m[2])
-		locs := make([]*Location, 0, len(fields))
-		for _, stk := range fields {
-			addr, err := strconv.ParseUint(stk, 0, 64)
-			if err != nil {
-				return nil, errMalformed
-			}
-			// Adjust all frames by -1 to land on the call instruction.
-			addr--
-			loc := locations[addr]
-			if loc == nil {
-				loc = &Location{
-					Address: addr,
-				}
-				locations[addr] = loc
-				p.Location = append(p.Location, loc)
-			}
-			locs = append(locs, loc)
-		}
-		p.Sample = append(p.Sample, &Sample{
-			Location: locs,
-			Value:    []int64{n},
-		})
-	}
-
-	if err = parseAdditionalSections(strings.TrimSpace(line), r, p); err != nil {
-		return nil, err
-	}
-	return p, nil
-}
-
-// remapLocationIDs ensures there is a location for each address
-// referenced by a sample, and remaps the samples to point to the new
-// location ids.
-func (p *Profile) remapLocationIDs() {
-	seen := make(map[*Location]bool, len(p.Location))
-	var locs []*Location
-
-	for _, s := range p.Sample {
-		for _, l := range s.Location {
-			if seen[l] {
-				continue
-			}
-			l.ID = uint64(len(locs) + 1)
-			locs = append(locs, l)
-			seen[l] = true
-		}
-	}
-	p.Location = locs
-}
-
-func (p *Profile) remapFunctionIDs() {
-	seen := make(map[*Function]bool, len(p.Function))
-	var fns []*Function
-
-	for _, l := range p.Location {
-		for _, ln := range l.Line {
-			fn := ln.Function
-			if fn == nil || seen[fn] {
-				continue
-			}
-			fn.ID = uint64(len(fns) + 1)
-			fns = append(fns, fn)
-			seen[fn] = true
-		}
-	}
-	p.Function = fns
-}
-
-// remapMappingIDs matches location addresses with existing mappings
-// and updates them appropriately. This is O(N*M), if this ever shows
-// up as a bottleneck, evaluate sorting the mappings and doing a
-// binary search, which would make it O(N*log(M)).
-func (p *Profile) remapMappingIDs() {
-	if len(p.Mapping) == 0 {
-		return
-	}
-
-	// Some profile handlers will incorrectly set regions for the main
-	// executable if its section is remapped. Fix them through heuristics.
-
-	// Remove the initial mapping if named '/anon_hugepage' and has a
-	// consecutive adjacent mapping.
-	if m := p.Mapping[0]; strings.HasPrefix(m.File, "/anon_hugepage") {
-		if len(p.Mapping) > 1 && m.Limit == p.Mapping[1].Start {
-			p.Mapping = p.Mapping[1:]
-		}
-	}
-
-	// Subtract the offset from the start of the main mapping if it
-	// ends up at a recognizable start address.
-	const expectedStart = 0x400000
-	if m := p.Mapping[0]; m.Start-m.Offset == expectedStart {
-		m.Start = expectedStart
-		m.Offset = 0
-	}
-
-	for _, l := range p.Location {
-		if a := l.Address; a != 0 {
-			for _, m := range p.Mapping {
-				if m.Start <= a && a < m.Limit {
-					l.Mapping = m
-					break
-				}
-			}
-		}
-	}
-
-	// Reset all mapping IDs.
-	for i, m := range p.Mapping {
-		m.ID = uint64(i + 1)
-	}
-}
-
-var cpuInts = []func([]byte) (uint64, []byte){
-	get32l,
-	get32b,
-	get64l,
-	get64b,
-}
-
-func get32l(b []byte) (uint64, []byte) {
-	if len(b) < 4 {
-		return 0, nil
-	}
-	return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24, b[4:]
-}
-
-func get32b(b []byte) (uint64, []byte) {
-	if len(b) < 4 {
-		return 0, nil
-	}
-	return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24, b[4:]
-}
-
-func get64l(b []byte) (uint64, []byte) {
-	if len(b) < 8 {
-		return 0, nil
-	}
-	return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56, b[8:]
-}
-
-func get64b(b []byte) (uint64, []byte) {
-	if len(b) < 8 {
-		return 0, nil
-	}
-	return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56, b[8:]
-}
-
-// ParseTracebacks parses a set of tracebacks and returns a newly
-// populated profile. It will accept any text file and generate a
-// Profile out of it with any hex addresses it can identify, including
-// a process map if it can recognize one. Each sample will include a
-// tag "source" with the addresses recognized in string format.
-func ParseTracebacks(b []byte) (*Profile, error) {
-	r := bytes.NewBuffer(b)
-
-	p := &Profile{
-		PeriodType: &ValueType{Type: "trace", Unit: "count"},
-		Period:     1,
-		SampleType: []*ValueType{
-			{Type: "trace", Unit: "count"},
-		},
-	}
-
-	var sources []string
-	var sloc []*Location
-
-	locs := make(map[uint64]*Location)
-	for {
-		l, err := r.ReadString('\n')
-		if err != nil {
-			if err != io.EOF {
-				return nil, err
-			}
-			if l == "" {
-				break
-			}
-		}
-		if sectionTrigger(l) == memoryMapSection {
-			break
-		}
-		if s, addrs := extractHexAddresses(l); len(s) > 0 {
-			for _, addr := range addrs {
-				// Addresses from stack traces point to the next instruction after
-				// each call. Adjust by -1 to land somewhere on the actual call.
-				addr--
-				loc := locs[addr]
-				if locs[addr] == nil {
-					loc = &Location{
-						Address: addr,
-					}
-					p.Location = append(p.Location, loc)
-					locs[addr] = loc
-				}
-				sloc = append(sloc, loc)
-			}
-
-			sources = append(sources, s...)
-		} else {
-			if len(sources) > 0 || len(sloc) > 0 {
-				addTracebackSample(sloc, sources, p)
-				sloc, sources = nil, nil
-			}
-		}
-	}
-
-	// Add final sample to save any leftover data.
-	if len(sources) > 0 || len(sloc) > 0 {
-		addTracebackSample(sloc, sources, p)
-	}
-
-	if err := p.ParseMemoryMap(r); err != nil {
-		return nil, err
-	}
-	return p, nil
-}
-
-func addTracebackSample(l []*Location, s []string, p *Profile) {
-	p.Sample = append(p.Sample,
-		&Sample{
-			Value:    []int64{1},
-			Location: l,
-			Label:    map[string][]string{"source": s},
-		})
-}
-
-// parseCPU parses a profilez legacy profile and returns a newly
-// populated Profile.
-//
-// The general format for profilez samples is a sequence of words in
-// binary format. The first words are a header with the following data:
-//   1st word -- 0
-//   2nd word -- 3
-//   3rd word -- 0 if a c++ application, 1 if a java application.
-//   4th word -- Sampling period (in microseconds).
-//   5th word -- Padding.
-func parseCPU(b []byte) (*Profile, error) {
-	var parse func([]byte) (uint64, []byte)
-	var n1, n2, n3, n4, n5 uint64
-	for _, parse = range cpuInts {
-		var tmp []byte
-		n1, tmp = parse(b)
-		n2, tmp = parse(tmp)
-		n3, tmp = parse(tmp)
-		n4, tmp = parse(tmp)
-		n5, tmp = parse(tmp)
-
-		if tmp != nil && n1 == 0 && n2 == 3 && n3 == 0 && n4 > 0 && n5 == 0 {
-			b = tmp
-			return cpuProfile(b, int64(n4), parse)
-		}
-	}
-	return nil, errUnrecognized
-}
-
-// cpuProfile returns a new Profile from C++ profilez data.
-// b is the profile bytes after the header, period is the profiling
-// period, and parse is a function to parse 8-byte chunks from the
-// profile in its native endianness.
-func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) {
-	p := &Profile{
-		Period:     period * 1000,
-		PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"},
-		SampleType: []*ValueType{
-			{Type: "samples", Unit: "count"},
-			{Type: "cpu", Unit: "nanoseconds"},
-		},
-	}
-	var err error
-	if b, _, err = parseCPUSamples(b, parse, true, p); err != nil {
-		return nil, err
-	}
-
-	// If all samples have the same second-to-the-bottom frame, it
-	// strongly suggests that it is an uninteresting artifact of
-	// measurement -- a stack frame pushed by the signal handler. The
-	// bottom frame is always correct as it is picked up from the signal
-	// structure, not the stack. Check if this is the case and if so,
-	// remove.
-	if len(p.Sample) > 1 && len(p.Sample[0].Location) > 1 {
-		allSame := true
-		id1 := p.Sample[0].Location[1].Address
-		for _, s := range p.Sample {
-			if len(s.Location) < 2 || id1 != s.Location[1].Address {
-				allSame = false
-				break
-			}
-		}
-		if allSame {
-			for _, s := range p.Sample {
-				s.Location = append(s.Location[:1], s.Location[2:]...)
-			}
-		}
-	}
-
-	if err := p.ParseMemoryMap(bytes.NewBuffer(b)); err != nil {
-		return nil, err
-	}
-	return p, nil
-}
-
-// parseCPUSamples parses a collection of profilez samples from a
-// profile.
-//
-// profilez samples are a repeated sequence of stack frames of the
-// form:
-//    1st word -- The number of times this stack was encountered.
-//    2nd word -- The size of the stack (StackSize).
-//    3rd word -- The first address on the stack.
-//    ...
-//    StackSize + 2 -- The last address on the stack
-// The last stack trace is of the form:
-//   1st word -- 0
-//   2nd word -- 1
-//   3rd word -- 0
-//
-// Addresses from stack traces may point to the next instruction after
-// each call. Optionally adjust by -1 to land somewhere on the actual
-// call (except for the leaf, which is not a call).
-func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), adjust bool, p *Profile) ([]byte, map[uint64]*Location, error) {
-	locs := make(map[uint64]*Location)
-	for len(b) > 0 {
-		var count, nstk uint64
-		count, b = parse(b)
-		nstk, b = parse(b)
-		if b == nil || nstk > uint64(len(b)/4) {
-			return nil, nil, errUnrecognized
-		}
-		var sloc []*Location
-		addrs := make([]uint64, nstk)
-		for i := 0; i < int(nstk); i++ {
-			addrs[i], b = parse(b)
-		}
-
-		if count == 0 && nstk == 1 && addrs[0] == 0 {
-			// End of data marker
-			break
-		}
-		for i, addr := range addrs {
-			if adjust && i > 0 {
-				addr--
-			}
-			loc := locs[addr]
-			if loc == nil {
-				loc = &Location{
-					Address: addr,
-				}
-				locs[addr] = loc
-				p.Location = append(p.Location, loc)
-			}
-			sloc = append(sloc, loc)
-		}
-		p.Sample = append(p.Sample,
-			&Sample{
-				Value:    []int64{int64(count), int64(count) * p.Period},
-				Location: sloc,
-			})
-	}
-	// Reached the end without finding the EOD marker.
-	return b, locs, nil
-}
-
-// parseHeap parses a heapz legacy or a growthz profile and
-// returns a newly populated Profile.
-func parseHeap(b []byte) (p *Profile, err error) {
-	r := bytes.NewBuffer(b)
-	l, err := r.ReadString('\n')
-	if err != nil {
-		return nil, errUnrecognized
-	}
-
-	sampling := ""
-
-	if header := heapHeaderRE.FindStringSubmatch(l); header != nil {
-		p = &Profile{
-			SampleType: []*ValueType{
-				{Type: "objects", Unit: "count"},
-				{Type: "space", Unit: "bytes"},
-			},
-			PeriodType: &ValueType{Type: "objects", Unit: "bytes"},
-		}
-
-		var period int64
-		if len(header[6]) > 0 {
-			if period, err = strconv.ParseInt(header[6], 10, 64); err != nil {
-				return nil, errUnrecognized
-			}
-		}
-
-		switch header[5] {
-		case "heapz_v2", "heap_v2":
-			sampling, p.Period = "v2", period
-		case "heapprofile":
-			sampling, p.Period = "", 1
-		case "heap":
-			sampling, p.Period = "v2", period/2
-		default:
-			return nil, errUnrecognized
-		}
-	} else if header = growthHeaderRE.FindStringSubmatch(l); header != nil {
-		p = &Profile{
-			SampleType: []*ValueType{
-				{Type: "objects", Unit: "count"},
-				{Type: "space", Unit: "bytes"},
-			},
-			PeriodType: &ValueType{Type: "heapgrowth", Unit: "count"},
-			Period:     1,
-		}
-	} else if header = fragmentationHeaderRE.FindStringSubmatch(l); header != nil {
-		p = &Profile{
-			SampleType: []*ValueType{
-				{Type: "objects", Unit: "count"},
-				{Type: "space", Unit: "bytes"},
-			},
-			PeriodType: &ValueType{Type: "allocations", Unit: "count"},
-			Period:     1,
-		}
-	} else {
-		return nil, errUnrecognized
-	}
-
-	if LegacyHeapAllocated {
-		for _, st := range p.SampleType {
-			st.Type = "alloc_" + st.Type
-		}
-	} else {
-		for _, st := range p.SampleType {
-			st.Type = "inuse_" + st.Type
-		}
-	}
-
-	locs := make(map[uint64]*Location)
-	for {
-		l, err = r.ReadString('\n')
-		if err != nil {
-			if err != io.EOF {
-				return nil, err
-			}
-
-			if l == "" {
-				break
-			}
-		}
-
-		if isSpaceOrComment(l) {
-			continue
-		}
-		l = strings.TrimSpace(l)
-
-		if sectionTrigger(l) != unrecognizedSection {
-			break
-		}
-
-		value, blocksize, addrs, err := parseHeapSample(l, p.Period, sampling)
-		if err != nil {
-			return nil, err
-		}
-		var sloc []*Location
-		for _, addr := range addrs {
-			// Addresses from stack traces point to the next instruction after
-			// each call. Adjust by -1 to land somewhere on the actual call.
-			addr--
-			loc := locs[addr]
-			if locs[addr] == nil {
-				loc = &Location{
-					Address: addr,
-				}
-				p.Location = append(p.Location, loc)
-				locs[addr] = loc
-			}
-			sloc = append(sloc, loc)
-		}
-
-		p.Sample = append(p.Sample, &Sample{
-			Value:    value,
-			Location: sloc,
-			NumLabel: map[string][]int64{"bytes": {blocksize}},
-		})
-	}
-
-	if err = parseAdditionalSections(l, r, p); err != nil {
-		return nil, err
-	}
-	return p, nil
-}
-
-// parseHeapSample parses a single row from a heap profile into a new Sample.
-func parseHeapSample(line string, rate int64, sampling string) (value []int64, blocksize int64, addrs []uint64, err error) {
-	sampleData := heapSampleRE.FindStringSubmatch(line)
-	if len(sampleData) != 6 {
-		return value, blocksize, addrs, fmt.Errorf("unexpected number of sample values: got %d, want 6", len(sampleData))
-	}
-
-	// Use first two values by default; tcmalloc sampling generates the
-	// same value for both, only the older heap-profile collect separate
-	// stats for in-use and allocated objects.
-	valueIndex := 1
-	if LegacyHeapAllocated {
-		valueIndex = 3
-	}
-
-	var v1, v2 int64
-	if v1, err = strconv.ParseInt(sampleData[valueIndex], 10, 64); err != nil {
-		return value, blocksize, addrs, fmt.Errorf("malformed sample: %s: %v", line, err)
-	}
-	if v2, err = strconv.ParseInt(sampleData[valueIndex+1], 10, 64); err != nil {
-		return value, blocksize, addrs, fmt.Errorf("malformed sample: %s: %v", line, err)
-	}
-
-	if v1 == 0 {
-		if v2 != 0 {
-			return value, blocksize, addrs, fmt.Errorf("allocation count was 0 but allocation bytes was %d", v2)
-		}
-	} else {
-		blocksize = v2 / v1
-		if sampling == "v2" {
-			v1, v2 = scaleHeapSample(v1, v2, rate)
-		}
-	}
-
-	value = []int64{v1, v2}
-	addrs = parseHexAddresses(sampleData[5])
-
-	return value, blocksize, addrs, nil
-}
-
-// extractHexAddresses extracts hex numbers from a string and returns
-// them, together with their numeric value, in a slice.
-func extractHexAddresses(s string) ([]string, []uint64) {
-	hexStrings := hexNumberRE.FindAllString(s, -1)
-	var ids []uint64
-	for _, s := range hexStrings {
-		if id, err := strconv.ParseUint(s, 0, 64); err == nil {
-			ids = append(ids, id)
-		} else {
-			// Do not expect any parsing failures due to the regexp matching.
-			panic("failed to parse hex value:" + s)
-		}
-	}
-	return hexStrings, ids
-}
-
-// parseHexAddresses parses hex numbers from a string and returns them
-// in a slice.
-func parseHexAddresses(s string) []uint64 {
-	_, ids := extractHexAddresses(s)
-	return ids
-}
-
-// scaleHeapSample adjusts the data from a heapz Sample to
-// account for its probability of appearing in the collected
-// data. heapz profiles are a sampling of the memory allocations
-// requests in a program. We estimate the unsampled value by dividing
-// each collected sample by its probability of appearing in the
-// profile. heapz v2 profiles rely on a poisson process to determine
-// which samples to collect, based on the desired average collection
-// rate R. The probability of a sample of size S to appear in that
-// profile is 1-exp(-S/R).
-func scaleHeapSample(count, size, rate int64) (int64, int64) {
-	if count == 0 || size == 0 {
-		return 0, 0
-	}
-
-	if rate <= 1 {
-		// if rate==1 all samples were collected so no adjustment is needed.
-		// if rate<1 treat as unknown and skip scaling.
-		return count, size
-	}
-
-	avgSize := float64(size) / float64(count)
-	scale := 1 / (1 - math.Exp(-avgSize/float64(rate)))
-
-	return int64(float64(count) * scale), int64(float64(size) * scale)
-}
-
-// parseContention parses a contentionz profile and returns a newly
-// populated Profile.
-func parseContention(b []byte) (p *Profile, err error) {
-	r := bytes.NewBuffer(b)
-	l, err := r.ReadString('\n')
-	if err != nil {
-		return nil, errUnrecognized
-	}
-
-	if !strings.HasPrefix(l, "--- contention") {
-		return nil, errUnrecognized
-	}
-
-	p = &Profile{
-		PeriodType: &ValueType{Type: "contentions", Unit: "count"},
-		Period:     1,
-		SampleType: []*ValueType{
-			{Type: "contentions", Unit: "count"},
-			{Type: "delay", Unit: "nanoseconds"},
-		},
-	}
-
-	var cpuHz int64
-	// Parse text of the form "attribute = value" before the samples.
-	const delimiter = "="
-	for {
-		l, err = r.ReadString('\n')
-		if err != nil {
-			if err != io.EOF {
-				return nil, err
-			}
-
-			if l == "" {
-				break
-			}
-		}
-
-		if l = strings.TrimSpace(l); l == "" {
-			continue
-		}
-
-		if strings.HasPrefix(l, "---") {
-			break
-		}
-
-		attr := strings.SplitN(l, delimiter, 2)
-		if len(attr) != 2 {
-			break
-		}
-		key, val := strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1])
-		var err error
-		switch key {
-		case "cycles/second":
-			if cpuHz, err = strconv.ParseInt(val, 0, 64); err != nil {
-				return nil, errUnrecognized
-			}
-		case "sampling period":
-			if p.Period, err = strconv.ParseInt(val, 0, 64); err != nil {
-				return nil, errUnrecognized
-			}
-		case "ms since reset":
-			ms, err := strconv.ParseInt(val, 0, 64)
-			if err != nil {
-				return nil, errUnrecognized
-			}
-			p.DurationNanos = ms * 1000 * 1000
-		case "format":
-			// CPP contentionz profiles don't have format.
-			return nil, errUnrecognized
-		case "resolution":
-			// CPP contentionz profiles don't have resolution.
-			return nil, errUnrecognized
-		case "discarded samples":
-		default:
-			return nil, errUnrecognized
-		}
-	}
-
-	locs := make(map[uint64]*Location)
-	for {
-		if l = strings.TrimSpace(l); strings.HasPrefix(l, "---") {
-			break
-		}
-		value, addrs, err := parseContentionSample(l, p.Period, cpuHz)
-		if err != nil {
-			return nil, err
-		}
-		var sloc []*Location
-		for _, addr := range addrs {
-			// Addresses from stack traces point to the next instruction after
-			// each call. Adjust by -1 to land somewhere on the actual call.
-			addr--
-			loc := locs[addr]
-			if locs[addr] == nil {
-				loc = &Location{
-					Address: addr,
-				}
-				p.Location = append(p.Location, loc)
-				locs[addr] = loc
-			}
-			sloc = append(sloc, loc)
-		}
-		p.Sample = append(p.Sample, &Sample{
-			Value:    value,
-			Location: sloc,
-		})
-
-		if l, err = r.ReadString('\n'); err != nil {
-			if err != io.EOF {
-				return nil, err
-			}
-			if l == "" {
-				break
-			}
-		}
-	}
-
-	if err = parseAdditionalSections(l, r, p); err != nil {
-		return nil, err
-	}
-
-	return p, nil
-}
-
-// parseContentionSample parses a single row from a contention profile
-// into a new Sample.
-func parseContentionSample(line string, period, cpuHz int64) (value []int64, addrs []uint64, err error) {
-	sampleData := contentionSampleRE.FindStringSubmatch(line)
-	if sampleData == nil {
-		return value, addrs, errUnrecognized
-	}
-
-	v1, err := strconv.ParseInt(sampleData[1], 10, 64)
-	if err != nil {
-		return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err)
-	}
-	v2, err := strconv.ParseInt(sampleData[2], 10, 64)
-	if err != nil {
-		return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err)
-	}
-
-	// Unsample values if period and cpuHz are available.
-	// - Delays are scaled to cycles and then to nanoseconds.
-	// - Contentions are scaled to cycles.
-	if period > 0 {
-		if cpuHz > 0 {
-			cpuGHz := float64(cpuHz) / 1e9
-			v1 = int64(float64(v1) * float64(period) / cpuGHz)
-		}
-		v2 = v2 * period
-	}
-
-	value = []int64{v2, v1}
-	addrs = parseHexAddresses(sampleData[3])
-
-	return value, addrs, nil
-}
-
-// parseThread parses a Threadz profile and returns a new Profile.
-func parseThread(b []byte) (*Profile, error) {
-	r := bytes.NewBuffer(b)
-
-	var line string
-	var err error
-	for {
-		// Skip past comments and empty lines seeking a real header.
-		line, err = r.ReadString('\n')
-		if err != nil {
-			return nil, err
-		}
-		if !isSpaceOrComment(line) {
-			break
-		}
-	}
-
-	if m := threadzStartRE.FindStringSubmatch(line); m != nil {
-		// Advance over initial comments until first stack trace.
-		for {
-			line, err = r.ReadString('\n')
-			if err != nil {
-				if err != io.EOF {
-					return nil, err
-				}
-
-				if line == "" {
-					break
-				}
-			}
-			if sectionTrigger(line) != unrecognizedSection || line[0] == '-' {
-				break
-			}
-		}
-	} else if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 {
-		return nil, errUnrecognized
-	}
-
-	p := &Profile{
-		SampleType: []*ValueType{{Type: "thread", Unit: "count"}},
-		PeriodType: &ValueType{Type: "thread", Unit: "count"},
-		Period:     1,
-	}
-
-	locs := make(map[uint64]*Location)
-	// Recognize each thread and populate profile samples.
-	for sectionTrigger(line) == unrecognizedSection {
-		if strings.HasPrefix(line, "---- no stack trace for") {
-			line = ""
-			break
-		}
-		if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 {
-			return nil, errUnrecognized
-		}
-
-		var addrs []uint64
-		line, addrs, err = parseThreadSample(r)
-		if err != nil {
-			return nil, errUnrecognized
-		}
-		if len(addrs) == 0 {
-			// We got a --same as previous threads--. Bump counters.
-			if len(p.Sample) > 0 {
-				s := p.Sample[len(p.Sample)-1]
-				s.Value[0]++
-			}
-			continue
-		}
-
-		var sloc []*Location
-		for _, addr := range addrs {
-			// Addresses from stack traces point to the next instruction after
-			// each call. Adjust by -1 to land somewhere on the actual call.
-			addr--
-			loc := locs[addr]
-			if locs[addr] == nil {
-				loc = &Location{
-					Address: addr,
-				}
-				p.Location = append(p.Location, loc)
-				locs[addr] = loc
-			}
-			sloc = append(sloc, loc)
-		}
-
-		p.Sample = append(p.Sample, &Sample{
-			Value:    []int64{1},
-			Location: sloc,
-		})
-	}
-
-	if err = parseAdditionalSections(line, r, p); err != nil {
-		return nil, err
-	}
-
-	return p, nil
-}
-
-// parseThreadSample parses a symbolized or unsymbolized stack trace.
-// Returns the first line after the traceback, the sample (or nil if
-// it hits a 'same-as-previous' marker) and an error.
-func parseThreadSample(b *bytes.Buffer) (nextl string, addrs []uint64, err error) {
-	var l string
-	sameAsPrevious := false
-	for {
-		if l, err = b.ReadString('\n'); err != nil {
-			if err != io.EOF {
-				return "", nil, err
-			}
-			if l == "" {
-				break
-			}
-		}
-		if l = strings.TrimSpace(l); l == "" {
-			continue
-		}
-
-		if strings.HasPrefix(l, "---") {
-			break
-		}
-		if strings.Contains(l, "same as previous thread") {
-			sameAsPrevious = true
-			continue
-		}
-
-		addrs = append(addrs, parseHexAddresses(l)...)
-	}
-
-	if sameAsPrevious {
-		return l, nil, nil
-	}
-	return l, addrs, nil
-}
-
-// parseAdditionalSections parses any additional sections in the
-// profile, ignoring any unrecognized sections.
-func parseAdditionalSections(l string, b *bytes.Buffer, p *Profile) (err error) {
-	for {
-		if sectionTrigger(l) == memoryMapSection {
-			break
-		}
-		// Ignore any unrecognized sections.
-		if l, err := b.ReadString('\n'); err != nil {
-			if err != io.EOF {
-				return err
-			}
-			if l == "" {
-				break
-			}
-		}
-	}
-	return p.ParseMemoryMap(b)
-}
-
-// ParseMemoryMap parses a memory map in the format of
-// /proc/self/maps, and overrides the mappings in the current profile.
-// It renumbers the samples and locations in the profile correspondingly.
-func (p *Profile) ParseMemoryMap(rd io.Reader) error {
-	b := bufio.NewReader(rd)
-
-	var attrs []string
-	var r *strings.Replacer
-	const delimiter = "="
-	for {
-		l, err := b.ReadString('\n')
-		if err != nil {
-			if err != io.EOF {
-				return err
-			}
-			if l == "" {
-				break
-			}
-		}
-		if l = strings.TrimSpace(l); l == "" {
-			continue
-		}
-
-		if r != nil {
-			l = r.Replace(l)
-		}
-		m, err := parseMappingEntry(l)
-		if err != nil {
-			if err == errUnrecognized {
-				// Recognize assignments of the form: attr=value, and replace
-				// $attr with value on subsequent mappings.
-				if attr := strings.SplitN(l, delimiter, 2); len(attr) == 2 {
-					attrs = append(attrs, "$"+strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1]))
-					r = strings.NewReplacer(attrs...)
-				}
-				// Ignore any unrecognized entries
-				continue
-			}
-			return err
-		}
-		if m == nil || (m.File == "" && len(p.Mapping) != 0) {
-			// In some cases the first entry may include the address range
-			// but not the name of the file. It should be followed by
-			// another entry with the name.
-			continue
-		}
-		if len(p.Mapping) == 1 && p.Mapping[0].File == "" {
-			// Update the name if this is the entry following that empty one.
-			p.Mapping[0].File = m.File
-			continue
-		}
-		p.Mapping = append(p.Mapping, m)
-	}
-	p.remapLocationIDs()
-	p.remapFunctionIDs()
-	p.remapMappingIDs()
-	return nil
-}
-
-func parseMappingEntry(l string) (*Mapping, error) {
-	mapping := &Mapping{}
-	var err error
-	if me := procMapsRE.FindStringSubmatch(l); len(me) == 9 {
-		if !strings.Contains(me[3], "x") {
-			// Skip non-executable entries.
-			return nil, nil
-		}
-		if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil {
-			return nil, errUnrecognized
-		}
-		if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil {
-			return nil, errUnrecognized
-		}
-		if me[4] != "" {
-			if mapping.Offset, err = strconv.ParseUint(me[4], 16, 64); err != nil {
-				return nil, errUnrecognized
-			}
-		}
-		mapping.File = me[8]
-		return mapping, nil
-	}
-
-	if me := briefMapsRE.FindStringSubmatch(l); len(me) == 6 {
-		if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil {
-			return nil, errUnrecognized
-		}
-		if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil {
-			return nil, errUnrecognized
-		}
-		mapping.File = me[3]
-		if me[5] != "" {
-			if mapping.Offset, err = strconv.ParseUint(me[5], 16, 64); err != nil {
-				return nil, errUnrecognized
-			}
-		}
-		return mapping, nil
-	}
-
-	return nil, errUnrecognized
-}
-
-type sectionType int
-
-const (
-	unrecognizedSection sectionType = iota
-	memoryMapSection
-)
-
-var memoryMapTriggers = []string{
-	"--- Memory map: ---",
-	"MAPPED_LIBRARIES:",
-}
-
-func sectionTrigger(line string) sectionType {
-	for _, trigger := range memoryMapTriggers {
-		if strings.Contains(line, trigger) {
-			return memoryMapSection
-		}
-	}
-	return unrecognizedSection
-}
-
-func (p *Profile) addLegacyFrameInfo() {
-	switch {
-	case isProfileType(p, heapzSampleTypes) ||
-		isProfileType(p, heapzInUseSampleTypes) ||
-		isProfileType(p, heapzAllocSampleTypes):
-		p.DropFrames, p.KeepFrames = allocRxStr, allocSkipRxStr
-	case isProfileType(p, contentionzSampleTypes):
-		p.DropFrames, p.KeepFrames = lockRxStr, ""
-	default:
-		p.DropFrames, p.KeepFrames = cpuProfilerRxStr, ""
-	}
-}
-
-var heapzSampleTypes = []string{"allocations", "size"} // early Go pprof profiles
-var heapzInUseSampleTypes = []string{"inuse_objects", "inuse_space"}
-var heapzAllocSampleTypes = []string{"alloc_objects", "alloc_space"}
-var contentionzSampleTypes = []string{"contentions", "delay"}
-
-func isProfileType(p *Profile, t []string) bool {
-	st := p.SampleType
-	if len(st) != len(t) {
-		return false
-	}
-
-	for i := range st {
-		if st[i].Type != t[i] {
-			return false
-		}
-	}
-	return true
-}
-
-var allocRxStr = strings.Join([]string{
-	// POSIX entry points.
-	`calloc`,
-	`cfree`,
-	`malloc`,
-	`free`,
-	`memalign`,
-	`do_memalign`,
-	`(__)?posix_memalign`,
-	`pvalloc`,
-	`valloc`,
-	`realloc`,
-
-	// TC malloc.
-	`tcmalloc::.*`,
-	`tc_calloc`,
-	`tc_cfree`,
-	`tc_malloc`,
-	`tc_free`,
-	`tc_memalign`,
-	`tc_posix_memalign`,
-	`tc_pvalloc`,
-	`tc_valloc`,
-	`tc_realloc`,
-	`tc_new`,
-	`tc_delete`,
-	`tc_newarray`,
-	`tc_deletearray`,
-	`tc_new_nothrow`,
-	`tc_newarray_nothrow`,
-
-	// Memory-allocation routines on OS X.
-	`malloc_zone_malloc`,
-	`malloc_zone_calloc`,
-	`malloc_zone_valloc`,
-	`malloc_zone_realloc`,
-	`malloc_zone_memalign`,
-	`malloc_zone_free`,
-
-	// Go runtime
-	`runtime\..*`,
-
-	// Other misc. memory allocation routines
-	`BaseArena::.*`,
-	`(::)?do_malloc_no_errno`,
-	`(::)?do_malloc_pages`,
-	`(::)?do_malloc`,
-	`DoSampledAllocation`,
-	`MallocedMemBlock::MallocedMemBlock`,
-	`_M_allocate`,
-	`__builtin_(vec_)?delete`,
-	`__builtin_(vec_)?new`,
-	`__gnu_cxx::new_allocator::allocate`,
-	`__libc_malloc`,
-	`__malloc_alloc_template::allocate`,
-	`allocate`,
-	`cpp_alloc`,
-	`operator new(\[\])?`,
-	`simple_alloc::allocate`,
-}, `|`)
-
-var allocSkipRxStr = strings.Join([]string{
-	// Preserve Go runtime frames that appear in the middle/bottom of
-	// the stack.
-	`runtime\.panic`,
-}, `|`)
-
-var cpuProfilerRxStr = strings.Join([]string{
-	`ProfileData::Add`,
-	`ProfileData::prof_handler`,
-	`CpuProfiler::prof_handler`,
-	`__pthread_sighandler`,
-	`__restore`,
-}, `|`)
-
-var lockRxStr = strings.Join([]string{
-	`RecordLockProfileData`,
-	`(base::)?RecordLockProfileData.*`,
-	`(base::)?SubmitMutexProfileData.*`,
-	`(base::)?SubmitSpinLockProfileData.*`,
-	`(Mutex::)?AwaitCommon.*`,
-	`(Mutex::)?Unlock.*`,
-	`(Mutex::)?UnlockSlow.*`,
-	`(Mutex::)?ReaderUnlock.*`,
-	`(MutexLock::)?~MutexLock.*`,
-	`(SpinLock::)?Unlock.*`,
-	`(SpinLock::)?SlowUnlock.*`,
-	`(SpinLockHolder::)?~SpinLockHolder.*`,
-}, `|`)
diff --git a/src/cmd/internal/pprof/profile/profile_test.go b/src/cmd/internal/pprof/profile/profile_test.go
deleted file mode 100644
index 09b11a4..0000000
--- a/src/cmd/internal/pprof/profile/profile_test.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package profile
-
-import (
-	"bytes"
-	"testing"
-)
-
-func TestEmptyProfile(t *testing.T) {
-	var buf bytes.Buffer
-	p, err := Parse(&buf)
-	if err != nil {
-		t.Error("Want no error, got", err)
-	}
-	if p == nil {
-		t.Fatal("Want a valid profile, got <nil>")
-	}
-	if !p.Empty() {
-		t.Errorf("Profile should be empty, got %#v", p)
-	}
-}
diff --git a/src/cmd/internal/pprof/report/report.go b/src/cmd/internal/pprof/report/report.go
deleted file mode 100644
index b11ad2a..0000000
--- a/src/cmd/internal/pprof/report/report.go
+++ /dev/null
@@ -1,1684 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package report summarizes a performance profile into a
-// human-readable report.
-package report
-
-import (
-	"fmt"
-	"io"
-	"math"
-	"path/filepath"
-	"regexp"
-	"sort"
-	"strconv"
-	"strings"
-	"time"
-
-	"cmd/internal/pprof/plugin"
-	"cmd/internal/pprof/profile"
-)
-
-// Generate generates a report as directed by the Report.
-func Generate(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
-	o := rpt.options
-
-	switch o.OutputFormat {
-	case Dot:
-		return printDOT(w, rpt)
-	case Tree:
-		return printTree(w, rpt)
-	case Text:
-		return printText(w, rpt)
-	case Raw:
-		fmt.Fprint(w, rpt.prof.String())
-		return nil
-	case Tags:
-		return printTags(w, rpt)
-	case Proto:
-		return rpt.prof.Write(w)
-	case Dis:
-		return printAssembly(w, rpt, obj)
-	case List:
-		return printSource(w, rpt)
-	case WebList:
-		return printWebSource(w, rpt, obj)
-	case Callgrind:
-		return printCallgrind(w, rpt)
-	}
-	return fmt.Errorf("unexpected output format")
-}
-
-// printAssembly prints an annotated assembly listing.
-func printAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
-	g, err := newGraph(rpt)
-	if err != nil {
-		return err
-	}
-
-	o := rpt.options
-	prof := rpt.prof
-
-	// If the regexp source can be parsed as an address, also match
-	// functions that land on that address.
-	var address *uint64
-	if hex, err := strconv.ParseUint(o.Symbol.String(), 0, 64); err == nil {
-		address = &hex
-	}
-
-	fmt.Fprintln(w, "Total:", rpt.formatValue(rpt.total))
-	symbols := symbolsFromBinaries(prof, g, o.Symbol, address, obj)
-	symNodes := nodesPerSymbol(g.ns, symbols)
-	// Sort function names for printing.
-	var syms objSymbols
-	for s := range symNodes {
-		syms = append(syms, s)
-	}
-	sort.Sort(syms)
-
-	// Correlate the symbols from the binary with the profile samples.
-	for _, s := range syms {
-		sns := symNodes[s]
-
-		// Gather samples for this symbol.
-		flatSum, cumSum := sumNodes(sns)
-
-		// Get the function assembly.
-		insns, err := obj.Disasm(s.sym.File, s.sym.Start, s.sym.End)
-		if err != nil {
-			return err
-		}
-
-		ns := annotateAssembly(insns, sns, s.base)
-
-		fmt.Fprintf(w, "ROUTINE ======================== %s\n", s.sym.Name[0])
-		for _, name := range s.sym.Name[1:] {
-			fmt.Fprintf(w, "    AKA ======================== %s\n", name)
-		}
-		fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n",
-			rpt.formatValue(flatSum), rpt.formatValue(cumSum),
-			percentage(cumSum, rpt.total))
-
-		for _, n := range ns {
-			fmt.Fprintf(w, "%10s %10s %10x: %s\n", valueOrDot(n.flat, rpt), valueOrDot(n.cum, rpt), n.info.address, n.info.name)
-		}
-	}
-	return nil
-}
-
-// symbolsFromBinaries examines the binaries listed on the profile
-// that have associated samples, and identifies symbols matching rx.
-func symbolsFromBinaries(prof *profile.Profile, g graph, rx *regexp.Regexp, address *uint64, obj plugin.ObjTool) []*objSymbol {
-	hasSamples := make(map[string]bool)
-	// Only examine mappings that have samples that match the
-	// regexp. This is an optimization to speed up pprof.
-	for _, n := range g.ns {
-		if name := n.info.prettyName(); rx.MatchString(name) && n.info.objfile != "" {
-			hasSamples[n.info.objfile] = true
-		}
-	}
-
-	// Walk all mappings looking for matching functions with samples.
-	var objSyms []*objSymbol
-	for _, m := range prof.Mapping {
-		if !hasSamples[filepath.Base(m.File)] {
-			if address == nil || !(m.Start <= *address && *address <= m.Limit) {
-				continue
-			}
-		}
-
-		f, err := obj.Open(m.File, m.Start)
-		if err != nil {
-			fmt.Printf("%v\n", err)
-			continue
-		}
-
-		// Find symbols in this binary matching the user regexp.
-		var addr uint64
-		if address != nil {
-			addr = *address
-		}
-		msyms, err := f.Symbols(rx, addr)
-		base := f.Base()
-		f.Close()
-		if err != nil {
-			continue
-		}
-		for _, ms := range msyms {
-			objSyms = append(objSyms,
-				&objSymbol{
-					sym:  ms,
-					base: base,
-				},
-			)
-		}
-	}
-
-	return objSyms
-}
-
-// objSym represents a symbol identified from a binary. It includes
-// the SymbolInfo from the disasm package and the base that must be
-// added to correspond to sample addresses
-type objSymbol struct {
-	sym  *plugin.Sym
-	base uint64
-}
-
-// objSymbols is a wrapper type to enable sorting of []*objSymbol.
-type objSymbols []*objSymbol
-
-func (o objSymbols) Len() int {
-	return len(o)
-}
-
-func (o objSymbols) Less(i, j int) bool {
-	if namei, namej := o[i].sym.Name[0], o[j].sym.Name[0]; namei != namej {
-		return namei < namej
-	}
-	return o[i].sym.Start < o[j].sym.Start
-}
-
-func (o objSymbols) Swap(i, j int) {
-	o[i], o[j] = o[j], o[i]
-}
-
-// nodesPerSymbol classifies nodes into a group of symbols.
-func nodesPerSymbol(ns nodes, symbols []*objSymbol) map[*objSymbol]nodes {
-	symNodes := make(map[*objSymbol]nodes)
-	for _, s := range symbols {
-		// Gather samples for this symbol.
-		for _, n := range ns {
-			address := n.info.address - s.base
-			if address >= s.sym.Start && address < s.sym.End {
-				symNodes[s] = append(symNodes[s], n)
-			}
-		}
-	}
-	return symNodes
-}
-
-// annotateAssembly annotates a set of assembly instructions with a
-// set of samples. It returns a set of nodes to display.  base is an
-// offset to adjust the sample addresses.
-func annotateAssembly(insns []plugin.Inst, samples nodes, base uint64) nodes {
-	// Add end marker to simplify printing loop.
-	insns = append(insns, plugin.Inst{
-		Addr: ^uint64(0),
-	})
-
-	// Ensure samples are sorted by address.
-	samples.sort(addressOrder)
-
-	var s int
-	var asm nodes
-	for ix, in := range insns[:len(insns)-1] {
-		n := node{
-			info: nodeInfo{
-				address: in.Addr,
-				name:    in.Text,
-				file:    trimPath(in.File),
-				lineno:  in.Line,
-			},
-		}
-
-		// Sum all the samples until the next instruction (to account
-		// for samples attributed to the middle of an instruction).
-		for next := insns[ix+1].Addr; s < len(samples) && samples[s].info.address-base < next; s++ {
-			n.flat += samples[s].flat
-			n.cum += samples[s].cum
-			if samples[s].info.file != "" {
-				n.info.file = trimPath(samples[s].info.file)
-				n.info.lineno = samples[s].info.lineno
-			}
-		}
-		asm = append(asm, &n)
-	}
-
-	return asm
-}
-
-// valueOrDot formats a value according to a report, intercepting zero
-// values.
-func valueOrDot(value int64, rpt *Report) string {
-	if value == 0 {
-		return "."
-	}
-	return rpt.formatValue(value)
-}
-
-// printTags collects all tags referenced in the profile and prints
-// them in a sorted table.
-func printTags(w io.Writer, rpt *Report) error {
-	p := rpt.prof
-
-	// Hashtable to keep accumulate tags as key,value,count.
-	tagMap := make(map[string]map[string]int64)
-	for _, s := range p.Sample {
-		for key, vals := range s.Label {
-			for _, val := range vals {
-				if valueMap, ok := tagMap[key]; ok {
-					valueMap[val] = valueMap[val] + s.Value[0]
-					continue
-				}
-				valueMap := make(map[string]int64)
-				valueMap[val] = s.Value[0]
-				tagMap[key] = valueMap
-			}
-		}
-		for key, vals := range s.NumLabel {
-			for _, nval := range vals {
-				val := scaledValueLabel(nval, key, "auto")
-				if valueMap, ok := tagMap[key]; ok {
-					valueMap[val] = valueMap[val] + s.Value[0]
-					continue
-				}
-				valueMap := make(map[string]int64)
-				valueMap[val] = s.Value[0]
-				tagMap[key] = valueMap
-			}
-		}
-	}
-
-	tagKeys := make(tags, 0, len(tagMap))
-	for key := range tagMap {
-		tagKeys = append(tagKeys, &tag{name: key})
-	}
-	sort.Sort(tagKeys)
-
-	for _, tagKey := range tagKeys {
-		var total int64
-		key := tagKey.name
-		tags := make(tags, 0, len(tagMap[key]))
-		for t, c := range tagMap[key] {
-			total += c
-			tags = append(tags, &tag{name: t, weight: c})
-		}
-
-		sort.Sort(tags)
-		fmt.Fprintf(w, "%s: Total %d\n", key, total)
-		for _, t := range tags {
-			if total > 0 {
-				fmt.Fprintf(w, "  %8d (%s): %s\n", t.weight,
-					percentage(t.weight, total), t.name)
-			} else {
-				fmt.Fprintf(w, "  %8d: %s\n", t.weight, t.name)
-			}
-		}
-		fmt.Fprintln(w)
-	}
-	return nil
-}
-
-// printText prints a flat text report for a profile.
-func printText(w io.Writer, rpt *Report) error {
-	g, err := newGraph(rpt)
-	if err != nil {
-		return err
-	}
-
-	origCount, droppedNodes, _ := g.preprocess(rpt)
-	fmt.Fprintln(w, strings.Join(legendDetailLabels(rpt, g, origCount, droppedNodes, 0), "\n"))
-
-	fmt.Fprintf(w, "%10s %5s%% %5s%% %10s %5s%%\n",
-		"flat", "flat", "sum", "cum", "cum")
-
-	var flatSum int64
-	for _, n := range g.ns {
-		name, flat, cum := n.info.prettyName(), n.flat, n.cum
-
-		flatSum += flat
-		fmt.Fprintf(w, "%10s %s %s %10s %s  %s\n",
-			rpt.formatValue(flat),
-			percentage(flat, rpt.total),
-			percentage(flatSum, rpt.total),
-			rpt.formatValue(cum),
-			percentage(cum, rpt.total),
-			name)
-	}
-	return nil
-}
-
-// printCallgrind prints a graph for a profile on callgrind format.
-func printCallgrind(w io.Writer, rpt *Report) error {
-	g, err := newGraph(rpt)
-	if err != nil {
-		return err
-	}
-
-	o := rpt.options
-	rpt.options.NodeFraction = 0
-	rpt.options.EdgeFraction = 0
-	rpt.options.NodeCount = 0
-
-	g.preprocess(rpt)
-
-	fmt.Fprintln(w, "events:", o.SampleType+"("+o.OutputUnit+")")
-
-	files := make(map[string]int)
-	names := make(map[string]int)
-	for _, n := range g.ns {
-		fmt.Fprintln(w, "fl="+callgrindName(files, n.info.file))
-		fmt.Fprintln(w, "fn="+callgrindName(names, n.info.name))
-		sv, _ := ScaleValue(n.flat, o.SampleUnit, o.OutputUnit)
-		fmt.Fprintf(w, "%d %d\n", n.info.lineno, int(sv))
-
-		// Print outgoing edges.
-		for _, out := range sortedEdges(n.out) {
-			c, _ := ScaleValue(out.weight, o.SampleUnit, o.OutputUnit)
-			count := fmt.Sprintf("%d", int(c))
-			callee := out.dest
-			fmt.Fprintln(w, "cfl="+callgrindName(files, callee.info.file))
-			fmt.Fprintln(w, "cfn="+callgrindName(names, callee.info.name))
-			fmt.Fprintln(w, "calls="+count, callee.info.lineno)
-			fmt.Fprintln(w, n.info.lineno, count)
-		}
-		fmt.Fprintln(w)
-	}
-
-	return nil
-}
-
-// callgrindName implements the callgrind naming compression scheme.
-// For names not previously seen returns "(N) name", where N is a
-// unique index. For names previously seen returns "(N)" where N is
-// the index returned the first time.
-func callgrindName(names map[string]int, name string) string {
-	if name == "" {
-		return ""
-	}
-	if id, ok := names[name]; ok {
-		return fmt.Sprintf("(%d)", id)
-	}
-	id := len(names) + 1
-	names[name] = id
-	return fmt.Sprintf("(%d) %s", id, name)
-}
-
-// printTree prints a tree-based report in text form.
-func printTree(w io.Writer, rpt *Report) error {
-	const separator = "----------------------------------------------------------+-------------"
-	const legend = "      flat  flat%   sum%        cum   cum%   calls calls% + context 	 	 "
-
-	g, err := newGraph(rpt)
-	if err != nil {
-		return err
-	}
-
-	origCount, droppedNodes, _ := g.preprocess(rpt)
-	fmt.Fprintln(w, strings.Join(legendDetailLabels(rpt, g, origCount, droppedNodes, 0), "\n"))
-
-	fmt.Fprintln(w, separator)
-	fmt.Fprintln(w, legend)
-	var flatSum int64
-
-	rx := rpt.options.Symbol
-	for _, n := range g.ns {
-		name, flat, cum := n.info.prettyName(), n.flat, n.cum
-
-		// Skip any entries that do not match the regexp (for the "peek" command).
-		if rx != nil && !rx.MatchString(name) {
-			continue
-		}
-
-		fmt.Fprintln(w, separator)
-		// Print incoming edges.
-		inEdges := sortedEdges(n.in)
-		inSum := inEdges.sum()
-		for _, in := range inEdges {
-			fmt.Fprintf(w, "%50s %s |   %s\n", rpt.formatValue(in.weight),
-				percentage(in.weight, inSum), in.src.info.prettyName())
-		}
-
-		// Print current node.
-		flatSum += flat
-		fmt.Fprintf(w, "%10s %s %s %10s %s                | %s\n",
-			rpt.formatValue(flat),
-			percentage(flat, rpt.total),
-			percentage(flatSum, rpt.total),
-			rpt.formatValue(cum),
-			percentage(cum, rpt.total),
-			name)
-
-		// Print outgoing edges.
-		outEdges := sortedEdges(n.out)
-		outSum := outEdges.sum()
-		for _, out := range outEdges {
-			fmt.Fprintf(w, "%50s %s |   %s\n", rpt.formatValue(out.weight),
-				percentage(out.weight, outSum), out.dest.info.prettyName())
-		}
-	}
-	if len(g.ns) > 0 {
-		fmt.Fprintln(w, separator)
-	}
-	return nil
-}
-
-// printDOT prints an annotated callgraph in DOT format.
-func printDOT(w io.Writer, rpt *Report) error {
-	g, err := newGraph(rpt)
-	if err != nil {
-		return err
-	}
-
-	origCount, droppedNodes, droppedEdges := g.preprocess(rpt)
-
-	prof := rpt.prof
-	graphname := "unnamed"
-	if len(prof.Mapping) > 0 {
-		graphname = filepath.Base(prof.Mapping[0].File)
-	}
-	fmt.Fprintln(w, `digraph "`+graphname+`" {`)
-	fmt.Fprintln(w, `node [style=filled fillcolor="#f8f8f8"]`)
-	fmt.Fprintln(w, dotLegend(rpt, g, origCount, droppedNodes, droppedEdges))
-
-	if len(g.ns) == 0 {
-		fmt.Fprintln(w, "}")
-		return nil
-	}
-
-	// Make sure nodes have a unique consistent id.
-	nodeIndex := make(map[*node]int)
-	maxFlat := float64(g.ns[0].flat)
-	for i, n := range g.ns {
-		nodeIndex[n] = i + 1
-		if float64(n.flat) > maxFlat {
-			maxFlat = float64(n.flat)
-		}
-	}
-	var edges edgeList
-	for _, n := range g.ns {
-		node := dotNode(rpt, maxFlat, nodeIndex[n], n)
-		fmt.Fprintln(w, node)
-		if nodelets := dotNodelets(rpt, nodeIndex[n], n); nodelets != "" {
-			fmt.Fprint(w, nodelets)
-		}
-
-		// Collect outgoing edges.
-		for _, e := range n.out {
-			edges = append(edges, e)
-		}
-	}
-	// Sort edges by frequency as a hint to the graph layout engine.
-	sort.Sort(edges)
-	for _, e := range edges {
-		fmt.Fprintln(w, dotEdge(rpt, nodeIndex[e.src], nodeIndex[e.dest], e))
-	}
-	fmt.Fprintln(w, "}")
-	return nil
-}
-
-// percentage computes the percentage of total of a value, and encodes
-// it as a string. At least two digits of precision are printed.
-func percentage(value, total int64) string {
-	var ratio float64
-	if total != 0 {
-		ratio = float64(value) / float64(total) * 100
-	}
-	switch {
-	case ratio >= 99.95:
-		return "  100%"
-	case ratio >= 1.0:
-		return fmt.Sprintf("%5.2f%%", ratio)
-	default:
-		return fmt.Sprintf("%5.2g%%", ratio)
-	}
-}
-
-// dotLegend generates the overall graph label for a report in DOT format.
-func dotLegend(rpt *Report, g graph, origCount, droppedNodes, droppedEdges int) string {
-	label := legendLabels(rpt)
-	label = append(label, legendDetailLabels(rpt, g, origCount, droppedNodes, droppedEdges)...)
-	return fmt.Sprintf(`subgraph cluster_L { L [shape=box fontsize=32 label="%s\l"] }`, strings.Join(label, `\l`))
-}
-
-// legendLabels generates labels exclusive to graph visualization.
-func legendLabels(rpt *Report) []string {
-	prof := rpt.prof
-	o := rpt.options
-	var label []string
-	if len(prof.Mapping) > 0 {
-		if prof.Mapping[0].File != "" {
-			label = append(label, "File: "+filepath.Base(prof.Mapping[0].File))
-		}
-		if prof.Mapping[0].BuildID != "" {
-			label = append(label, "Build ID: "+prof.Mapping[0].BuildID)
-		}
-	}
-	if o.SampleType != "" {
-		label = append(label, "Type: "+o.SampleType)
-	}
-	if prof.TimeNanos != 0 {
-		const layout = "Jan 2, 2006 at 3:04pm (MST)"
-		label = append(label, "Time: "+time.Unix(0, prof.TimeNanos).Format(layout))
-	}
-	if prof.DurationNanos != 0 {
-		label = append(label, fmt.Sprintf("Duration: %v", time.Duration(prof.DurationNanos)))
-	}
-	return label
-}
-
-// legendDetailLabels generates labels common to graph and text visualization.
-func legendDetailLabels(rpt *Report, g graph, origCount, droppedNodes, droppedEdges int) []string {
-	nodeFraction := rpt.options.NodeFraction
-	edgeFraction := rpt.options.EdgeFraction
-	nodeCount := rpt.options.NodeCount
-
-	label := []string{}
-
-	var flatSum int64
-	for _, n := range g.ns {
-		flatSum = flatSum + n.flat
-	}
-
-	label = append(label, fmt.Sprintf("%s of %s total (%s)", rpt.formatValue(flatSum), rpt.formatValue(rpt.total), percentage(flatSum, rpt.total)))
-
-	if rpt.total > 0 {
-		if droppedNodes > 0 {
-			label = append(label, genLabel(droppedNodes, "node", "cum",
-				rpt.formatValue(int64(float64(rpt.total)*nodeFraction))))
-		}
-		if droppedEdges > 0 {
-			label = append(label, genLabel(droppedEdges, "edge", "freq",
-				rpt.formatValue(int64(float64(rpt.total)*edgeFraction))))
-		}
-		if nodeCount > 0 && nodeCount < origCount {
-			label = append(label, fmt.Sprintf("Showing top %d nodes out of %d (cum >= %s)",
-				nodeCount, origCount,
-				rpt.formatValue(g.ns[len(g.ns)-1].cum)))
-		}
-	}
-	return label
-}
-
-func genLabel(d int, n, l, f string) string {
-	if d > 1 {
-		n = n + "s"
-	}
-	return fmt.Sprintf("Dropped %d %s (%s <= %s)", d, n, l, f)
-}
-
-// dotNode generates a graph node in DOT format.
-func dotNode(rpt *Report, maxFlat float64, rIndex int, n *node) string {
-	flat, cum := n.flat, n.cum
-
-	labels := strings.Split(n.info.prettyName(), "::")
-	label := strings.Join(labels, `\n`) + `\n`
-
-	flatValue := rpt.formatValue(flat)
-	if flat > 0 {
-		label = label + fmt.Sprintf(`%s(%s)`,
-			flatValue,
-			strings.TrimSpace(percentage(flat, rpt.total)))
-	} else {
-		label = label + "0"
-	}
-	cumValue := flatValue
-	if cum != flat {
-		if flat > 0 {
-			label = label + `\n`
-		} else {
-			label = label + " "
-		}
-		cumValue = rpt.formatValue(cum)
-		label = label + fmt.Sprintf(`of %s(%s)`,
-			cumValue,
-			strings.TrimSpace(percentage(cum, rpt.total)))
-	}
-
-	// Scale font sizes from 8 to 24 based on percentage of flat frequency.
-	// Use non linear growth to emphasize the size difference.
-	baseFontSize, maxFontGrowth := 8, 16.0
-	fontSize := baseFontSize
-	if maxFlat > 0 && flat > 0 && float64(flat) <= maxFlat {
-		fontSize += int(math.Ceil(maxFontGrowth * math.Sqrt(float64(flat)/maxFlat)))
-	}
-	return fmt.Sprintf(`N%d [label="%s" fontsize=%d shape=box tooltip="%s (%s)"]`,
-		rIndex,
-		label,
-		fontSize, n.info.prettyName(), cumValue)
-}
-
-// dotEdge generates a graph edge in DOT format.
-func dotEdge(rpt *Report, from, to int, e *edgeInfo) string {
-	w := rpt.formatValue(e.weight)
-	attr := fmt.Sprintf(`label=" %s"`, w)
-	if rpt.total > 0 {
-		if weight := 1 + int(e.weight*100/rpt.total); weight > 1 {
-			attr = fmt.Sprintf(`%s weight=%d`, attr, weight)
-		}
-		if width := 1 + int(e.weight*5/rpt.total); width > 1 {
-			attr = fmt.Sprintf(`%s penwidth=%d`, attr, width)
-		}
-	}
-	arrow := "->"
-	if e.residual {
-		arrow = "..."
-	}
-	tooltip := fmt.Sprintf(`"%s %s %s (%s)"`,
-		e.src.info.prettyName(), arrow, e.dest.info.prettyName(), w)
-	attr = fmt.Sprintf(`%s tooltip=%s labeltooltip=%s`,
-		attr, tooltip, tooltip)
-
-	if e.residual {
-		attr = attr + ` style="dotted"`
-	}
-
-	if len(e.src.tags) > 0 {
-		// Separate children further if source has tags.
-		attr = attr + " minlen=2"
-	}
-	return fmt.Sprintf("N%d -> N%d [%s]", from, to, attr)
-}
-
-// dotNodelets generates the DOT boxes for the node tags.
-func dotNodelets(rpt *Report, rIndex int, n *node) (dot string) {
-	const maxNodelets = 4    // Number of nodelets for alphanumeric labels
-	const maxNumNodelets = 4 // Number of nodelets for numeric labels
-
-	var ts, nts tags
-	for _, t := range n.tags {
-		if t.unit == "" {
-			ts = append(ts, t)
-		} else {
-			nts = append(nts, t)
-		}
-	}
-
-	// Select the top maxNodelets alphanumeric labels by weight
-	sort.Sort(ts)
-	if len(ts) > maxNodelets {
-		ts = ts[:maxNodelets]
-	}
-	for i, t := range ts {
-		weight := rpt.formatValue(t.weight)
-		dot += fmt.Sprintf(`N%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", rIndex, i, t.name, weight)
-		dot += fmt.Sprintf(`N%d -> N%d_%d [label=" %s" weight=100 tooltip="\L" labeltooltip="\L"]`+"\n", rIndex, rIndex, i, weight)
-	}
-
-	// Collapse numeric labels into maxNumNodelets buckets, of the form:
-	// 1MB..2MB, 3MB..5MB, ...
-	nts = collapseTags(nts, maxNumNodelets)
-	sort.Sort(nts)
-	for i, t := range nts {
-		weight := rpt.formatValue(t.weight)
-		dot += fmt.Sprintf(`NN%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", rIndex, i, t.name, weight)
-		dot += fmt.Sprintf(`N%d -> NN%d_%d [label=" %s" weight=100 tooltip="\L" labeltooltip="\L"]`+"\n", rIndex, rIndex, i, weight)
-	}
-
-	return dot
-}
-
-// graph summarizes a performance profile into a format that is
-// suitable for visualization.
-type graph struct {
-	ns nodes
-}
-
-// nodes is an ordered collection of graph nodes.
-type nodes []*node
-
-// tags represent sample annotations
-type tags []*tag
-type tagMap map[string]*tag
-
-type tag struct {
-	name   string
-	unit   string // Describe the value, "" for non-numeric tags
-	value  int64
-	weight int64
-}
-
-func (t tags) Len() int      { return len(t) }
-func (t tags) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
-func (t tags) Less(i, j int) bool {
-	if t[i].weight == t[j].weight {
-		return t[i].name < t[j].name
-	}
-	return t[i].weight > t[j].weight
-}
-
-// node is an entry on a profiling report. It represents a unique
-// program location. It can include multiple names to represent
-// inlined functions.
-type node struct {
-	info nodeInfo // Information associated to this entry.
-
-	// values associated to this node.
-	// flat is exclusive to this node, cum includes all descendents.
-	flat, cum int64
-
-	// in and out contains the nodes immediately reaching or reached by this nodes.
-	in, out edgeMap
-
-	// tags provide additional information about subsets of a sample.
-	tags tagMap
-}
-
-type nodeInfo struct {
-	name              string
-	origName          string
-	address           uint64
-	file              string
-	startLine, lineno int
-	inline            bool
-	lowPriority       bool
-	objfile           string
-	parent            *node // Used only if creating a calltree
-}
-
-func (n *node) addTags(s *profile.Sample, weight int64) {
-	// Add a tag with all string labels
-	var labels []string
-	for key, vals := range s.Label {
-		for _, v := range vals {
-			labels = append(labels, key+":"+v)
-		}
-	}
-	if len(labels) > 0 {
-		sort.Strings(labels)
-		l := n.tags.findOrAddTag(strings.Join(labels, `\n`), "", 0)
-		l.weight += weight
-	}
-
-	for key, nvals := range s.NumLabel {
-		for _, v := range nvals {
-			label := scaledValueLabel(v, key, "auto")
-			l := n.tags.findOrAddTag(label, key, v)
-			l.weight += weight
-		}
-	}
-}
-
-func (m tagMap) findOrAddTag(label, unit string, value int64) *tag {
-	if l := m[label]; l != nil {
-		return l
-	}
-	l := &tag{
-		name:  label,
-		unit:  unit,
-		value: value,
-	}
-	m[label] = l
-	return l
-}
-
-// collapseTags reduces the number of entries in a tagMap by merging
-// adjacent nodes into ranges. It uses a greedy approach to merge
-// starting with the entries with the lowest weight.
-func collapseTags(ts tags, count int) tags {
-	if len(ts) <= count {
-		return ts
-	}
-
-	sort.Sort(ts)
-	tagGroups := make([]tags, count)
-	for i, t := range ts[:count] {
-		tagGroups[i] = tags{t}
-	}
-	for _, t := range ts[count:] {
-		g, d := 0, tagDistance(t, tagGroups[0][0])
-		for i := 1; i < count; i++ {
-			if nd := tagDistance(t, tagGroups[i][0]); nd < d {
-				g, d = i, nd
-			}
-		}
-		tagGroups[g] = append(tagGroups[g], t)
-	}
-
-	var nts tags
-	for _, g := range tagGroups {
-		l, w := tagGroupLabel(g)
-		nts = append(nts, &tag{
-			name:   l,
-			weight: w,
-		})
-	}
-	return nts
-}
-
-func tagDistance(t, u *tag) float64 {
-	v, _ := ScaleValue(u.value, u.unit, t.unit)
-	if v < float64(t.value) {
-		return float64(t.value) - v
-	}
-	return v - float64(t.value)
-}
-
-func tagGroupLabel(g tags) (string, int64) {
-	if len(g) == 1 {
-		t := g[0]
-		return scaledValueLabel(t.value, t.unit, "auto"), t.weight
-	}
-	min := g[0]
-	max := g[0]
-	w := min.weight
-	for _, t := range g[1:] {
-		if v, _ := ScaleValue(t.value, t.unit, min.unit); int64(v) < min.value {
-			min = t
-		}
-		if v, _ := ScaleValue(t.value, t.unit, max.unit); int64(v) > max.value {
-			max = t
-		}
-		w += t.weight
-	}
-	return scaledValueLabel(min.value, min.unit, "auto") + ".." +
-		scaledValueLabel(max.value, max.unit, "auto"), w
-}
-
-// sumNodes adds the flat and sum values on a report.
-func sumNodes(ns nodes) (flat int64, cum int64) {
-	for _, n := range ns {
-		flat += n.flat
-		cum += n.cum
-	}
-	return
-}
-
-type edgeMap map[*node]*edgeInfo
-
-// edgeInfo contains any attributes to be represented about edges in a graph/
-type edgeInfo struct {
-	src, dest *node
-	// The summary weight of the edge
-	weight int64
-	// residual edges connect nodes that were connected through a
-	// separate node, which has been removed from the report.
-	residual bool
-}
-
-// bumpWeight increases the weight of an edge. If there isn't such an
-// edge in the map one is created.
-func bumpWeight(from, to *node, w int64, residual bool) {
-	if from.out[to] != to.in[from] {
-		panic(fmt.Errorf("asymmetric edges %v %v", *from, *to))
-	}
-
-	if n := from.out[to]; n != nil {
-		n.weight += w
-		if n.residual && !residual {
-			n.residual = false
-		}
-		return
-	}
-
-	info := &edgeInfo{src: from, dest: to, weight: w, residual: residual}
-	from.out[to] = info
-	to.in[from] = info
-}
-
-// Output formats.
-const (
-	Proto = iota
-	Dot
-	Tags
-	Tree
-	Text
-	Raw
-	Dis
-	List
-	WebList
-	Callgrind
-)
-
-// Options are the formatting and filtering options used to generate a
-// profile.
-type Options struct {
-	OutputFormat int
-
-	CumSort        bool
-	CallTree       bool
-	PrintAddresses bool
-	DropNegative   bool
-	Ratio          float64
-
-	NodeCount    int
-	NodeFraction float64
-	EdgeFraction float64
-
-	SampleType string
-	SampleUnit string // Unit for the sample data from the profile.
-	OutputUnit string // Units for data formatting in report.
-
-	Symbol *regexp.Regexp // Symbols to include on disassembly report.
-}
-
-// newGraph summarizes performance data from a profile into a graph.
-func newGraph(rpt *Report) (g graph, err error) {
-	prof := rpt.prof
-	o := rpt.options
-
-	// Generate a tree for graphical output if requested.
-	buildTree := o.CallTree && o.OutputFormat == Dot
-
-	locations := make(map[uint64][]nodeInfo)
-	for _, l := range prof.Location {
-		locations[l.ID] = newLocInfo(l)
-	}
-
-	nm := make(nodeMap)
-	for _, sample := range prof.Sample {
-		if sample.Location == nil {
-			continue
-		}
-
-		// Construct list of node names for sample.
-		var stack []nodeInfo
-		for _, loc := range sample.Location {
-			id := loc.ID
-			stack = append(stack, locations[id]...)
-		}
-
-		// Upfront pass to update the parent chains, to prevent the
-		// merging of nodes with different parents.
-		if buildTree {
-			var nn *node
-			for i := len(stack); i > 0; i-- {
-				n := &stack[i-1]
-				n.parent = nn
-				nn = nm.findOrInsertNode(*n)
-			}
-		}
-
-		leaf := nm.findOrInsertNode(stack[0])
-		weight := rpt.sampleValue(sample)
-		leaf.addTags(sample, weight)
-
-		// Aggregate counter data.
-		leaf.flat += weight
-		seen := make(map[*node]bool)
-		var nn *node
-		for _, s := range stack {
-			n := nm.findOrInsertNode(s)
-			if !seen[n] {
-				seen[n] = true
-				n.cum += weight
-
-				if nn != nil {
-					bumpWeight(n, nn, weight, false)
-				}
-			}
-			nn = n
-		}
-	}
-
-	// Collect new nodes into a report.
-	ns := make(nodes, 0, len(nm))
-	for _, n := range nm {
-		if rpt.options.DropNegative && n.flat < 0 {
-			continue
-		}
-		ns = append(ns, n)
-	}
-
-	return graph{ns}, nil
-}
-
-// Create a slice of formatted names for a location.
-func newLocInfo(l *profile.Location) []nodeInfo {
-	var objfile string
-
-	if m := l.Mapping; m != nil {
-		objfile = filepath.Base(m.File)
-	}
-
-	if len(l.Line) == 0 {
-		return []nodeInfo{
-			{
-				address: l.Address,
-				objfile: objfile,
-			},
-		}
-	}
-	var info []nodeInfo
-	numInlineFrames := len(l.Line) - 1
-	for li, line := range l.Line {
-		ni := nodeInfo{
-			address: l.Address,
-			lineno:  int(line.Line),
-			inline:  li < numInlineFrames,
-			objfile: objfile,
-		}
-
-		if line.Function != nil {
-			ni.name = line.Function.Name
-			ni.origName = line.Function.SystemName
-			ni.file = line.Function.Filename
-			ni.startLine = int(line.Function.StartLine)
-		}
-
-		info = append(info, ni)
-	}
-	return info
-}
-
-// nodeMap maps from a node info struct to a node. It is used to merge
-// report entries with the same info.
-type nodeMap map[nodeInfo]*node
-
-func (m nodeMap) findOrInsertNode(info nodeInfo) *node {
-	rr := m[info]
-	if rr == nil {
-		rr = &node{
-			info: info,
-			in:   make(edgeMap),
-			out:  make(edgeMap),
-			tags: make(map[string]*tag),
-		}
-		m[info] = rr
-	}
-	return rr
-}
-
-// preprocess does any required filtering/sorting according to the
-// report options. Returns the mapping from each node to any nodes
-// removed by path compression and statistics on the nodes/edges removed.
-func (g *graph) preprocess(rpt *Report) (origCount, droppedNodes, droppedEdges int) {
-	o := rpt.options
-
-	// Compute total weight of current set of nodes.
-	// This is <= rpt.total because of node filtering.
-	var totalValue int64
-	for _, n := range g.ns {
-		totalValue += n.flat
-	}
-
-	// Remove nodes with value <= total*nodeFraction
-	if nodeFraction := o.NodeFraction; nodeFraction > 0 {
-		var removed nodes
-		minValue := int64(float64(totalValue) * nodeFraction)
-		kept := make(nodes, 0, len(g.ns))
-		for _, n := range g.ns {
-			if n.cum < minValue {
-				removed = append(removed, n)
-			} else {
-				kept = append(kept, n)
-				tagsKept := make(map[string]*tag)
-				for s, t := range n.tags {
-					if t.weight >= minValue {
-						tagsKept[s] = t
-					}
-				}
-				n.tags = tagsKept
-			}
-		}
-		droppedNodes = len(removed)
-		removeNodes(removed, false, false)
-		g.ns = kept
-	}
-
-	// Remove edges below minimum frequency.
-	if edgeFraction := o.EdgeFraction; edgeFraction > 0 {
-		minEdge := int64(float64(totalValue) * edgeFraction)
-		for _, n := range g.ns {
-			for src, e := range n.in {
-				if e.weight < minEdge {
-					delete(n.in, src)
-					delete(src.out, n)
-					droppedEdges++
-				}
-			}
-		}
-	}
-
-	sortOrder := flatName
-	if o.CumSort {
-		// Force cum sorting for graph output, to preserve connectivity.
-		sortOrder = cumName
-	}
-
-	// Nodes that have flat==0 and a single in/out do not provide much
-	// information. Give them first chance to be removed. Do not consider edges
-	// from/to nodes that are expected to be removed.
-	maxNodes := o.NodeCount
-	if o.OutputFormat == Dot {
-		if maxNodes > 0 && maxNodes < len(g.ns) {
-			sortOrder = cumName
-			g.ns.sort(cumName)
-			cumCutoff := g.ns[maxNodes].cum
-			for _, n := range g.ns {
-				if n.flat == 0 {
-					if count := countEdges(n.out, cumCutoff); count > 1 {
-						continue
-					}
-					if count := countEdges(n.in, cumCutoff); count != 1 {
-						continue
-					}
-					n.info.lowPriority = true
-				}
-			}
-		}
-	}
-
-	g.ns.sort(sortOrder)
-	if maxNodes > 0 {
-		origCount = len(g.ns)
-		for index, nodes := 0, 0; index < len(g.ns); index++ {
-			nodes++
-			// For DOT output, count the tags as nodes since we will draw
-			// boxes for them.
-			if o.OutputFormat == Dot {
-				nodes += len(g.ns[index].tags)
-			}
-			if nodes > maxNodes {
-				// Trim to the top n nodes. Create dotted edges to bridge any
-				// broken connections.
-				removeNodes(g.ns[index:], true, true)
-				g.ns = g.ns[:index]
-				break
-			}
-		}
-	}
-	removeRedundantEdges(g.ns)
-
-	// Select best unit for profile output.
-	// Find the appropriate units for the smallest non-zero sample
-	if o.OutputUnit == "minimum" && len(g.ns) > 0 {
-		var maxValue, minValue int64
-
-		for _, n := range g.ns {
-			if n.flat > 0 && (minValue == 0 || n.flat < minValue) {
-				minValue = n.flat
-			}
-			if n.cum > maxValue {
-				maxValue = n.cum
-			}
-		}
-		if r := o.Ratio; r > 0 && r != 1 {
-			minValue = int64(float64(minValue) * r)
-			maxValue = int64(float64(maxValue) * r)
-		}
-
-		_, minUnit := ScaleValue(minValue, o.SampleUnit, "minimum")
-		_, maxUnit := ScaleValue(maxValue, o.SampleUnit, "minimum")
-
-		unit := minUnit
-		if minUnit != maxUnit && minValue*100 < maxValue && o.OutputFormat != Callgrind {
-			// Minimum and maximum values have different units. Scale
-			// minimum by 100 to use larger units, allowing minimum value to
-			// be scaled down to 0.01, except for callgrind reports since
-			// they can only represent integer values.
-			_, unit = ScaleValue(100*minValue, o.SampleUnit, "minimum")
-		}
-
-		if unit != "" {
-			o.OutputUnit = unit
-		} else {
-			o.OutputUnit = o.SampleUnit
-		}
-	}
-	return
-}
-
-// countEdges counts the number of edges below the specified cutoff.
-func countEdges(el edgeMap, cutoff int64) int {
-	count := 0
-	for _, e := range el {
-		if e.weight > cutoff {
-			count++
-		}
-	}
-	return count
-}
-
-// removeNodes removes nodes from a report, optionally bridging
-// connections between in/out edges and spreading out their weights
-// proportionally. residual marks new bridge edges as residual
-// (dotted).
-func removeNodes(toRemove nodes, bridge, residual bool) {
-	for _, n := range toRemove {
-		for ei := range n.in {
-			delete(ei.out, n)
-		}
-		if bridge {
-			for ei, wi := range n.in {
-				for eo, wo := range n.out {
-					var weight int64
-					if n.cum != 0 {
-						weight = int64(float64(wo.weight) * (float64(wi.weight) / float64(n.cum)))
-					}
-					bumpWeight(ei, eo, weight, residual)
-				}
-			}
-		}
-		for eo := range n.out {
-			delete(eo.in, n)
-		}
-	}
-}
-
-// removeRedundantEdges removes residual edges if the destination can
-// be reached through another path. This is done to simplify the graph
-// while preserving connectivity.
-func removeRedundantEdges(ns nodes) {
-	// Walk the nodes and outgoing edges in reverse order to prefer
-	// removing edges with the lowest weight.
-	for i := len(ns); i > 0; i-- {
-		n := ns[i-1]
-		in := sortedEdges(n.in)
-		for j := len(in); j > 0; j-- {
-			if e := in[j-1]; e.residual && isRedundant(e) {
-				delete(e.src.out, e.dest)
-				delete(e.dest.in, e.src)
-			}
-		}
-	}
-}
-
-// isRedundant determines if an edge can be removed without impacting
-// connectivity of the whole graph. This is implemented by checking if the
-// nodes have a common ancestor after removing the edge.
-func isRedundant(e *edgeInfo) bool {
-	destPred := predecessors(e, e.dest)
-	if len(destPred) == 1 {
-		return false
-	}
-	srcPred := predecessors(e, e.src)
-
-	for n := range srcPred {
-		if destPred[n] && n != e.dest {
-			return true
-		}
-	}
-	return false
-}
-
-// predecessors collects all the predecessors to node n, excluding edge e.
-func predecessors(e *edgeInfo, n *node) map[*node]bool {
-	seen := map[*node]bool{n: true}
-	queue := []*node{n}
-	for len(queue) > 0 {
-		n := queue[0]
-		queue = queue[1:]
-		for _, ie := range n.in {
-			if e == ie || seen[ie.src] {
-				continue
-			}
-			seen[ie.src] = true
-			queue = append(queue, ie.src)
-		}
-	}
-	return seen
-}
-
-// nodeSorter is a mechanism used to allow a report to be sorted
-// in different ways.
-type nodeSorter struct {
-	rs   nodes
-	less func(i, j int) bool
-}
-
-func (s nodeSorter) Len() int           { return len(s.rs) }
-func (s nodeSorter) Swap(i, j int)      { s.rs[i], s.rs[j] = s.rs[j], s.rs[i] }
-func (s nodeSorter) Less(i, j int) bool { return s.less(i, j) }
-
-type nodeOrder int
-
-const (
-	flatName nodeOrder = iota
-	flatCumName
-	cumName
-	nameOrder
-	fileOrder
-	addressOrder
-)
-
-// sort reorders the entries in a report based on the specified
-// ordering criteria. The result is sorted in decreasing order for
-// numeric quantities, alphabetically for text, and increasing for
-// addresses.
-func (ns nodes) sort(o nodeOrder) error {
-	var s nodeSorter
-
-	switch o {
-	case flatName:
-		s = nodeSorter{ns,
-			func(i, j int) bool {
-				if iv, jv := ns[i].flat, ns[j].flat; iv != jv {
-					return iv > jv
-				}
-				if ns[i].info.prettyName() != ns[j].info.prettyName() {
-					return ns[i].info.prettyName() < ns[j].info.prettyName()
-				}
-				iv, jv := ns[i].cum, ns[j].cum
-				return iv > jv
-			},
-		}
-	case flatCumName:
-		s = nodeSorter{ns,
-			func(i, j int) bool {
-				if iv, jv := ns[i].flat, ns[j].flat; iv != jv {
-					return iv > jv
-				}
-				if iv, jv := ns[i].cum, ns[j].cum; iv != jv {
-					return iv > jv
-				}
-				return ns[i].info.prettyName() < ns[j].info.prettyName()
-			},
-		}
-	case cumName:
-		s = nodeSorter{ns,
-			func(i, j int) bool {
-				if ns[i].info.lowPriority != ns[j].info.lowPriority {
-					return ns[j].info.lowPriority
-				}
-				if iv, jv := ns[i].cum, ns[j].cum; iv != jv {
-					return iv > jv
-				}
-				if ns[i].info.prettyName() != ns[j].info.prettyName() {
-					return ns[i].info.prettyName() < ns[j].info.prettyName()
-				}
-				iv, jv := ns[i].flat, ns[j].flat
-				return iv > jv
-			},
-		}
-	case nameOrder:
-		s = nodeSorter{ns,
-			func(i, j int) bool {
-				return ns[i].info.name < ns[j].info.name
-			},
-		}
-	case fileOrder:
-		s = nodeSorter{ns,
-			func(i, j int) bool {
-				return ns[i].info.file < ns[j].info.file
-			},
-		}
-	case addressOrder:
-		s = nodeSorter{ns,
-			func(i, j int) bool {
-				return ns[i].info.address < ns[j].info.address
-			},
-		}
-	default:
-		return fmt.Errorf("report: unrecognized sort ordering: %d", o)
-	}
-	sort.Sort(s)
-	return nil
-}
-
-type edgeList []*edgeInfo
-
-// sortedEdges return a slice of the edges in the map, sorted for
-// visualization. The sort order is first based on the edge weight
-// (higher-to-lower) and then by the node names to avoid flakiness.
-func sortedEdges(edges map[*node]*edgeInfo) edgeList {
-	el := make(edgeList, 0, len(edges))
-	for _, w := range edges {
-		el = append(el, w)
-	}
-
-	sort.Sort(el)
-	return el
-}
-
-func (el edgeList) Len() int {
-	return len(el)
-}
-
-func (el edgeList) Less(i, j int) bool {
-	if el[i].weight != el[j].weight {
-		return el[i].weight > el[j].weight
-	}
-
-	from1 := el[i].src.info.prettyName()
-	from2 := el[j].src.info.prettyName()
-	if from1 != from2 {
-		return from1 < from2
-	}
-
-	to1 := el[i].dest.info.prettyName()
-	to2 := el[j].dest.info.prettyName()
-
-	return to1 < to2
-}
-
-func (el edgeList) Swap(i, j int) {
-	el[i], el[j] = el[j], el[i]
-}
-
-func (el edgeList) sum() int64 {
-	var ret int64
-	for _, e := range el {
-		ret += e.weight
-	}
-	return ret
-}
-
-// ScaleValue reformats a value from a unit to a different unit.
-func ScaleValue(value int64, fromUnit, toUnit string) (sv float64, su string) {
-	// Avoid infinite recursion on overflow.
-	if value < 0 && -value > 0 {
-		v, u := ScaleValue(-value, fromUnit, toUnit)
-		return -v, u
-	}
-	if m, u, ok := memoryLabel(value, fromUnit, toUnit); ok {
-		return m, u
-	}
-	if t, u, ok := timeLabel(value, fromUnit, toUnit); ok {
-		return t, u
-	}
-	// Skip non-interesting units.
-	switch toUnit {
-	case "count", "sample", "unit", "minimum":
-		return float64(value), ""
-	default:
-		return float64(value), toUnit
-	}
-}
-
-func scaledValueLabel(value int64, fromUnit, toUnit string) string {
-	v, u := ScaleValue(value, fromUnit, toUnit)
-
-	sv := strings.TrimSuffix(fmt.Sprintf("%.2f", v), ".00")
-	if sv == "0" || sv == "-0" {
-		return "0"
-	}
-	return sv + u
-}
-
-func memoryLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok bool) {
-	fromUnit = strings.TrimSuffix(strings.ToLower(fromUnit), "s")
-	toUnit = strings.TrimSuffix(strings.ToLower(toUnit), "s")
-
-	switch fromUnit {
-	case "byte", "b":
-	case "kilobyte", "kb":
-		value *= 1024
-	case "megabyte", "mb":
-		value *= 1024 * 1024
-	case "gigabyte", "gb":
-		value *= 1024 * 1024 * 1024
-	default:
-		return 0, "", false
-	}
-
-	if toUnit == "minimum" || toUnit == "auto" {
-		switch {
-		case value < 1024:
-			toUnit = "b"
-		case value < 1024*1024:
-			toUnit = "kb"
-		case value < 1024*1024*1024:
-			toUnit = "mb"
-		default:
-			toUnit = "gb"
-		}
-	}
-
-	var output float64
-	switch toUnit {
-	default:
-		output, toUnit = float64(value), "B"
-	case "kb", "kbyte", "kilobyte":
-		output, toUnit = float64(value)/1024, "kB"
-	case "mb", "mbyte", "megabyte":
-		output, toUnit = float64(value)/(1024*1024), "MB"
-	case "gb", "gbyte", "gigabyte":
-		output, toUnit = float64(value)/(1024*1024*1024), "GB"
-	}
-	return output, toUnit, true
-}
-
-func timeLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok bool) {
-	fromUnit = strings.ToLower(fromUnit)
-	if len(fromUnit) > 2 {
-		fromUnit = strings.TrimSuffix(fromUnit, "s")
-	}
-
-	toUnit = strings.ToLower(toUnit)
-	if len(toUnit) > 2 {
-		toUnit = strings.TrimSuffix(toUnit, "s")
-	}
-
-	var d time.Duration
-	switch fromUnit {
-	case "nanosecond", "ns":
-		d = time.Duration(value) * time.Nanosecond
-	case "microsecond":
-		d = time.Duration(value) * time.Microsecond
-	case "millisecond", "ms":
-		d = time.Duration(value) * time.Millisecond
-	case "second", "sec":
-		d = time.Duration(value) * time.Second
-	case "cycle":
-		return float64(value), "", true
-	default:
-		return 0, "", false
-	}
-
-	if toUnit == "minimum" || toUnit == "auto" {
-		switch {
-		case d < 1*time.Microsecond:
-			toUnit = "ns"
-		case d < 1*time.Millisecond:
-			toUnit = "us"
-		case d < 1*time.Second:
-			toUnit = "ms"
-		case d < 1*time.Minute:
-			toUnit = "sec"
-		case d < 1*time.Hour:
-			toUnit = "min"
-		case d < 24*time.Hour:
-			toUnit = "hour"
-		case d < 15*24*time.Hour:
-			toUnit = "day"
-		case d < 120*24*time.Hour:
-			toUnit = "week"
-		default:
-			toUnit = "year"
-		}
-	}
-
-	var output float64
-	dd := float64(d)
-	switch toUnit {
-	case "ns", "nanosecond":
-		output, toUnit = dd/float64(time.Nanosecond), "ns"
-	case "us", "microsecond":
-		output, toUnit = dd/float64(time.Microsecond), "us"
-	case "ms", "millisecond":
-		output, toUnit = dd/float64(time.Millisecond), "ms"
-	case "min", "minute":
-		output, toUnit = dd/float64(time.Minute), "mins"
-	case "hour", "hr":
-		output, toUnit = dd/float64(time.Hour), "hrs"
-	case "day":
-		output, toUnit = dd/float64(24*time.Hour), "days"
-	case "week", "wk":
-		output, toUnit = dd/float64(7*24*time.Hour), "wks"
-	case "year", "yr":
-		output, toUnit = dd/float64(365*7*24*time.Hour), "yrs"
-	default:
-		fallthrough
-	case "sec", "second", "s":
-		output, toUnit = dd/float64(time.Second), "s"
-	}
-	return output, toUnit, true
-}
-
-// prettyName determines the printable name to be used for a node.
-func (info *nodeInfo) prettyName() string {
-	var name string
-	if info.address != 0 {
-		name = fmt.Sprintf("%016x", info.address)
-	}
-
-	if info.name != "" {
-		name = name + " " + info.name
-	}
-
-	if info.file != "" {
-		name += " " + trimPath(info.file)
-		if info.lineno != 0 {
-			name += fmt.Sprintf(":%d", info.lineno)
-		}
-	}
-
-	if info.inline {
-		name = name + " (inline)"
-	}
-
-	if name = strings.TrimSpace(name); name == "" && info.objfile != "" {
-		name = "[" + info.objfile + "]"
-	}
-	return name
-}
-
-// New builds a new report indexing the sample values interpreting the
-// samples with the provided function.
-func New(prof *profile.Profile, options Options, value func(s *profile.Sample) int64, unit string) *Report {
-	o := &options
-	if o.SampleUnit == "" {
-		o.SampleUnit = unit
-	}
-	format := func(v int64) string {
-		if r := o.Ratio; r > 0 && r != 1 {
-			fv := float64(v) * r
-			v = int64(fv)
-		}
-		return scaledValueLabel(v, o.SampleUnit, o.OutputUnit)
-	}
-	return &Report{prof, computeTotal(prof, value), o, value, format}
-}
-
-// NewDefault builds a new report indexing the sample values with the
-// last value available.
-func NewDefault(prof *profile.Profile, options Options) *Report {
-	index := len(prof.SampleType) - 1
-	o := &options
-	if o.SampleUnit == "" {
-		o.SampleUnit = strings.ToLower(prof.SampleType[index].Unit)
-	}
-	value := func(s *profile.Sample) int64 {
-		return s.Value[index]
-	}
-	format := func(v int64) string {
-		if r := o.Ratio; r > 0 && r != 1 {
-			fv := float64(v) * r
-			v = int64(fv)
-		}
-		return scaledValueLabel(v, o.SampleUnit, o.OutputUnit)
-	}
-	return &Report{prof, computeTotal(prof, value), o, value, format}
-}
-
-func computeTotal(prof *profile.Profile, value func(s *profile.Sample) int64) int64 {
-	var ret int64
-	for _, sample := range prof.Sample {
-		ret += value(sample)
-	}
-	return ret
-}
-
-// Report contains the data and associated routines to extract a
-// report from a profile.
-type Report struct {
-	prof        *profile.Profile
-	total       int64
-	options     *Options
-	sampleValue func(*profile.Sample) int64
-	formatValue func(int64) string
-}
diff --git a/src/cmd/internal/pprof/report/source.go b/src/cmd/internal/pprof/report/source.go
deleted file mode 100644
index 608e4d5..0000000
--- a/src/cmd/internal/pprof/report/source.go
+++ /dev/null
@@ -1,454 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package report
-
-// This file contains routines related to the generation of annotated
-// source listings.
-
-import (
-	"bufio"
-	"fmt"
-	"html/template"
-	"io"
-	"os"
-	"path/filepath"
-	"sort"
-	"strconv"
-	"strings"
-
-	"cmd/internal/pprof/plugin"
-)
-
-// printSource prints an annotated source listing, include all
-// functions with samples that match the regexp rpt.options.symbol.
-// The sources are sorted by function name and then by filename to
-// eliminate potential nondeterminism.
-func printSource(w io.Writer, rpt *Report) error {
-	o := rpt.options
-	g, err := newGraph(rpt)
-	if err != nil {
-		return err
-	}
-
-	// Identify all the functions that match the regexp provided.
-	// Group nodes for each matching function.
-	var functions nodes
-	functionNodes := make(map[string]nodes)
-	for _, n := range g.ns {
-		if !o.Symbol.MatchString(n.info.name) {
-			continue
-		}
-		if functionNodes[n.info.name] == nil {
-			functions = append(functions, n)
-		}
-		functionNodes[n.info.name] = append(functionNodes[n.info.name], n)
-	}
-	functions.sort(nameOrder)
-
-	fmt.Fprintf(w, "Total: %s\n", rpt.formatValue(rpt.total))
-	for _, fn := range functions {
-		name := fn.info.name
-
-		// Identify all the source files associated to this function.
-		// Group nodes for each source file.
-		var sourceFiles nodes
-		fileNodes := make(map[string]nodes)
-		for _, n := range functionNodes[name] {
-			if n.info.file == "" {
-				continue
-			}
-			if fileNodes[n.info.file] == nil {
-				sourceFiles = append(sourceFiles, n)
-			}
-			fileNodes[n.info.file] = append(fileNodes[n.info.file], n)
-		}
-
-		if len(sourceFiles) == 0 {
-			fmt.Printf("No source information for %s\n", name)
-			continue
-		}
-
-		sourceFiles.sort(fileOrder)
-
-		// Print each file associated with this function.
-		for _, fl := range sourceFiles {
-			filename := fl.info.file
-			fns := fileNodes[filename]
-			flatSum, cumSum := sumNodes(fns)
-
-			fnodes, path, err := getFunctionSource(name, filename, fns, 0, 0)
-			fmt.Fprintf(w, "ROUTINE ======================== %s in %s\n", name, path)
-			fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n",
-				rpt.formatValue(flatSum), rpt.formatValue(cumSum),
-				percentage(cumSum, rpt.total))
-
-			if err != nil {
-				fmt.Fprintf(w, " Error: %v\n", err)
-				continue
-			}
-
-			for _, fn := range fnodes {
-				fmt.Fprintf(w, "%10s %10s %6d:%s\n", valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt), fn.info.lineno, fn.info.name)
-			}
-		}
-	}
-	return nil
-}
-
-// printWebSource prints an annotated source listing, include all
-// functions with samples that match the regexp rpt.options.symbol.
-func printWebSource(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
-	o := rpt.options
-	g, err := newGraph(rpt)
-	if err != nil {
-		return err
-	}
-
-	// If the regexp source can be parsed as an address, also match
-	// functions that land on that address.
-	var address *uint64
-	if hex, err := strconv.ParseUint(o.Symbol.String(), 0, 64); err == nil {
-		address = &hex
-	}
-
-	// Extract interesting symbols from binary files in the profile and
-	// classify samples per symbol.
-	symbols := symbolsFromBinaries(rpt.prof, g, o.Symbol, address, obj)
-	symNodes := nodesPerSymbol(g.ns, symbols)
-
-	// Sort symbols for printing.
-	var syms objSymbols
-	for s := range symNodes {
-		syms = append(syms, s)
-	}
-	sort.Sort(syms)
-
-	if len(syms) == 0 {
-		return fmt.Errorf("no samples found on routines matching: %s", o.Symbol.String())
-	}
-
-	printHeader(w, rpt)
-	for _, s := range syms {
-		name := s.sym.Name[0]
-		// Identify sources associated to a symbol by examining
-		// symbol samples. Classify samples per source file.
-		var sourceFiles nodes
-		fileNodes := make(map[string]nodes)
-		for _, n := range symNodes[s] {
-			if n.info.file == "" {
-				continue
-			}
-			if fileNodes[n.info.file] == nil {
-				sourceFiles = append(sourceFiles, n)
-			}
-			fileNodes[n.info.file] = append(fileNodes[n.info.file], n)
-		}
-
-		if len(sourceFiles) == 0 {
-			fmt.Printf("No source information for %s\n", name)
-			continue
-		}
-
-		sourceFiles.sort(fileOrder)
-
-		// Print each file associated with this function.
-		for _, fl := range sourceFiles {
-			filename := fl.info.file
-			fns := fileNodes[filename]
-
-			asm := assemblyPerSourceLine(symbols, fns, filename, obj)
-			start, end := sourceCoordinates(asm)
-
-			fnodes, path, err := getFunctionSource(name, filename, fns, start, end)
-			if err != nil {
-				fnodes, path = getMissingFunctionSource(filename, asm, start, end)
-			}
-
-			flatSum, cumSum := sumNodes(fnodes)
-			printFunctionHeader(w, name, path, flatSum, cumSum, rpt)
-			for _, fn := range fnodes {
-				printFunctionSourceLine(w, fn, asm[fn.info.lineno], rpt)
-			}
-			printFunctionClosing(w)
-		}
-	}
-	printPageClosing(w)
-	return nil
-}
-
-// sourceCoordinates returns the lowest and highest line numbers from
-// a set of assembly statements.
-func sourceCoordinates(asm map[int]nodes) (start, end int) {
-	for l := range asm {
-		if start == 0 || l < start {
-			start = l
-		}
-		if end == 0 || l > end {
-			end = l
-		}
-	}
-	return start, end
-}
-
-// assemblyPerSourceLine disassembles the binary containing a symbol
-// and classifies the assembly instructions according to its
-// corresponding source line, annotating them with a set of samples.
-func assemblyPerSourceLine(objSyms []*objSymbol, rs nodes, src string, obj plugin.ObjTool) map[int]nodes {
-	assembly := make(map[int]nodes)
-	// Identify symbol to use for this collection of samples.
-	o := findMatchingSymbol(objSyms, rs)
-	if o == nil {
-		return assembly
-	}
-
-	// Extract assembly for matched symbol
-	insns, err := obj.Disasm(o.sym.File, o.sym.Start, o.sym.End)
-	if err != nil {
-		return assembly
-	}
-
-	srcBase := filepath.Base(src)
-	anodes := annotateAssembly(insns, rs, o.base)
-	var lineno = 0
-	for _, an := range anodes {
-		if filepath.Base(an.info.file) == srcBase {
-			lineno = an.info.lineno
-		}
-		if lineno != 0 {
-			assembly[lineno] = append(assembly[lineno], an)
-		}
-	}
-
-	return assembly
-}
-
-// findMatchingSymbol looks for the symbol that corresponds to a set
-// of samples, by comparing their addresses.
-func findMatchingSymbol(objSyms []*objSymbol, ns nodes) *objSymbol {
-	for _, n := range ns {
-		for _, o := range objSyms {
-			if filepath.Base(o.sym.File) == n.info.objfile &&
-				o.sym.Start <= n.info.address-o.base &&
-				n.info.address-o.base <= o.sym.End {
-				return o
-			}
-		}
-	}
-	return nil
-}
-
-// printHeader prints the page header for a weblist report.
-func printHeader(w io.Writer, rpt *Report) {
-	fmt.Fprintln(w, weblistPageHeader)
-
-	var labels []string
-	for _, l := range legendLabels(rpt) {
-		labels = append(labels, template.HTMLEscapeString(l))
-	}
-
-	fmt.Fprintf(w, `<div class="legend">%s<br>Total: %s</div>`,
-		strings.Join(labels, "<br>\n"),
-		rpt.formatValue(rpt.total),
-	)
-}
-
-// printFunctionHeader prints a function header for a weblist report.
-func printFunctionHeader(w io.Writer, name, path string, flatSum, cumSum int64, rpt *Report) {
-	fmt.Fprintf(w, `<h1>%s</h1>%s
-<pre onClick="pprof_toggle_asm(event)">
-  Total:  %10s %10s (flat, cum) %s
-`,
-		template.HTMLEscapeString(name), template.HTMLEscapeString(path),
-		rpt.formatValue(flatSum), rpt.formatValue(cumSum),
-		percentage(cumSum, rpt.total))
-}
-
-// printFunctionSourceLine prints a source line and the corresponding assembly.
-func printFunctionSourceLine(w io.Writer, fn *node, assembly nodes, rpt *Report) {
-	if len(assembly) == 0 {
-		fmt.Fprintf(w,
-			"<span class=line> %6d</span> <span class=nop>  %10s %10s %s </span>\n",
-			fn.info.lineno,
-			valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt),
-			template.HTMLEscapeString(fn.info.name))
-		return
-	}
-
-	fmt.Fprintf(w,
-		"<span class=line> %6d</span> <span class=deadsrc>  %10s %10s %s </span>",
-		fn.info.lineno,
-		valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt),
-		template.HTMLEscapeString(fn.info.name))
-	fmt.Fprint(w, "<span class=asm>")
-	for _, an := range assembly {
-		var fileline string
-		class := "disasmloc"
-		if an.info.file != "" {
-			fileline = fmt.Sprintf("%s:%d", template.HTMLEscapeString(an.info.file), an.info.lineno)
-			if an.info.lineno != fn.info.lineno {
-				class = "unimportant"
-			}
-		}
-		fmt.Fprintf(w, " %8s %10s %10s %8x: %-48s <span class=%s>%s</span>\n", "",
-			valueOrDot(an.flat, rpt), valueOrDot(an.cum, rpt),
-			an.info.address,
-			template.HTMLEscapeString(an.info.name),
-			class,
-			template.HTMLEscapeString(fileline))
-	}
-	fmt.Fprintln(w, "</span>")
-}
-
-// printFunctionClosing prints the end of a function in a weblist report.
-func printFunctionClosing(w io.Writer) {
-	fmt.Fprintln(w, "</pre>")
-}
-
-// printPageClosing prints the end of the page in a weblist report.
-func printPageClosing(w io.Writer) {
-	fmt.Fprintln(w, weblistPageClosing)
-}
-
-// getFunctionSource collects the sources of a function from a source
-// file and annotates it with the samples in fns. Returns the sources
-// as nodes, using the info.name field to hold the source code.
-func getFunctionSource(fun, file string, fns nodes, start, end int) (nodes, string, error) {
-	f, file, err := adjustSourcePath(file)
-	if err != nil {
-		return nil, file, err
-	}
-
-	lineNodes := make(map[int]nodes)
-
-	// Collect source coordinates from profile.
-	const margin = 5 // Lines before first/after last sample.
-	if start == 0 {
-		if fns[0].info.startLine != 0 {
-			start = fns[0].info.startLine
-		} else {
-			start = fns[0].info.lineno - margin
-		}
-	} else {
-		start -= margin
-	}
-	if end == 0 {
-		end = fns[0].info.lineno
-	}
-	end += margin
-	for _, n := range fns {
-		lineno := n.info.lineno
-		nodeStart := n.info.startLine
-		if nodeStart == 0 {
-			nodeStart = lineno - margin
-		}
-		nodeEnd := lineno + margin
-		if nodeStart < start {
-			start = nodeStart
-		} else if nodeEnd > end {
-			end = nodeEnd
-		}
-		lineNodes[lineno] = append(lineNodes[lineno], n)
-	}
-
-	var src nodes
-	buf := bufio.NewReader(f)
-	lineno := 1
-	for {
-		line, err := buf.ReadString('\n')
-		if err != nil {
-			if err != io.EOF {
-				return nil, file, err
-			}
-			if line == "" {
-				// end was at or past EOF; that's okay
-				break
-			}
-		}
-		if lineno >= start {
-			flat, cum := sumNodes(lineNodes[lineno])
-
-			src = append(src, &node{
-				info: nodeInfo{
-					name:   strings.TrimRight(line, "\n"),
-					lineno: lineno,
-				},
-				flat: flat,
-				cum:  cum,
-			})
-		}
-		lineno++
-		if lineno > end {
-			break
-		}
-	}
-	return src, file, nil
-}
-
-// getMissingFunctionSource creates a dummy function body to point to
-// the source file and annotates it with the samples in asm.
-func getMissingFunctionSource(filename string, asm map[int]nodes, start, end int) (nodes, string) {
-	var fnodes nodes
-	for i := start; i <= end; i++ {
-		lrs := asm[i]
-		if len(lrs) == 0 {
-			continue
-		}
-		flat, cum := sumNodes(lrs)
-		fnodes = append(fnodes, &node{
-			info: nodeInfo{
-				name:   "???",
-				lineno: i,
-			},
-			flat: flat,
-			cum:  cum,
-		})
-	}
-	return fnodes, filename
-}
-
-// adjustSourcePath adjusts the path for a source file by trimming
-// known prefixes and searching for the file on all parents of the
-// current working dir.
-func adjustSourcePath(path string) (*os.File, string, error) {
-	path = trimPath(path)
-	f, err := os.Open(path)
-	if err == nil {
-		return f, path, nil
-	}
-
-	if dir, wderr := os.Getwd(); wderr == nil {
-		for {
-			parent := filepath.Dir(dir)
-			if parent == dir {
-				break
-			}
-			if f, err := os.Open(filepath.Join(parent, path)); err == nil {
-				return f, filepath.Join(parent, path), nil
-			}
-
-			dir = parent
-		}
-	}
-
-	return nil, path, err
-}
-
-// trimPath cleans up a path by removing prefixes that are commonly
-// found on profiles.
-func trimPath(path string) string {
-	basePaths := []string{
-		"/proc/self/cwd/./",
-		"/proc/self/cwd/",
-	}
-
-	sPath := filepath.ToSlash(path)
-
-	for _, base := range basePaths {
-		if strings.HasPrefix(sPath, base) {
-			return filepath.FromSlash(sPath[len(base):])
-		}
-	}
-	return path
-}
diff --git a/src/cmd/internal/pprof/symbolizer/symbolizer.go b/src/cmd/internal/pprof/symbolizer/symbolizer.go
deleted file mode 100644
index bc22800..0000000
--- a/src/cmd/internal/pprof/symbolizer/symbolizer.go
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package symbolizer provides a routine to populate a profile with
-// symbol, file and line number information. It relies on the
-// addr2liner and demangler packages to do the actual work.
-package symbolizer
-
-import (
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"cmd/internal/pprof/plugin"
-	"cmd/internal/pprof/profile"
-)
-
-// Symbolize adds symbol and line number information to all locations
-// in a profile. mode enables some options to control
-// symbolization. Currently only recognizes "force", which causes it
-// to overwrite any existing data.
-func Symbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
-	force := false
-	// Disable some mechanisms based on mode string.
-	for _, o := range strings.Split(strings.ToLower(mode), ":") {
-		switch o {
-		case "force":
-			force = true
-		default:
-		}
-	}
-
-	if len(prof.Mapping) == 0 {
-		return fmt.Errorf("no known mappings")
-	}
-
-	mt, err := newMapping(prof, obj, ui, force)
-	if err != nil {
-		return err
-	}
-	defer mt.close()
-
-	functions := make(map[profile.Function]*profile.Function)
-	for _, l := range mt.prof.Location {
-		m := l.Mapping
-		segment := mt.segments[m]
-		if segment == nil {
-			// Nothing to do
-			continue
-		}
-
-		stack, err := segment.SourceLine(l.Address)
-		if err != nil || len(stack) == 0 {
-			// No answers from addr2line
-			continue
-		}
-
-		l.Line = make([]profile.Line, len(stack))
-		for i, frame := range stack {
-			if frame.Func != "" {
-				m.HasFunctions = true
-			}
-			if frame.File != "" {
-				m.HasFilenames = true
-			}
-			if frame.Line != 0 {
-				m.HasLineNumbers = true
-			}
-			f := &profile.Function{
-				Name:       frame.Func,
-				SystemName: frame.Func,
-				Filename:   frame.File,
-			}
-			if fp := functions[*f]; fp != nil {
-				f = fp
-			} else {
-				functions[*f] = f
-				f.ID = uint64(len(mt.prof.Function)) + 1
-				mt.prof.Function = append(mt.prof.Function, f)
-			}
-			l.Line[i] = profile.Line{
-				Function: f,
-				Line:     int64(frame.Line),
-			}
-		}
-
-		if len(stack) > 0 {
-			m.HasInlineFrames = true
-		}
-	}
-	return nil
-}
-
-// newMapping creates a mappingTable for a profile.
-func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) {
-	mt := &mappingTable{
-		prof:     prof,
-		segments: make(map[*profile.Mapping]plugin.ObjFile),
-	}
-
-	// Identify used mappings
-	mappings := make(map[*profile.Mapping]bool)
-	for _, l := range prof.Location {
-		mappings[l.Mapping] = true
-	}
-
-	for _, m := range prof.Mapping {
-		if !mappings[m] {
-			continue
-		}
-		// Do not attempt to re-symbolize a mapping that has already been symbolized.
-		if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) {
-			continue
-		}
-
-		f, err := locateFile(obj, m.File, m.BuildID, m.Start)
-		if err != nil {
-			ui.PrintErr("Local symbolization failed for ", filepath.Base(m.File), ": ", err)
-			// Move on to other mappings
-			continue
-		}
-
-		if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID {
-			// Build ID mismatch - ignore.
-			f.Close()
-			continue
-		}
-
-		mt.segments[m] = f
-	}
-
-	return mt, nil
-}
-
-// locateFile opens a local file for symbolization on the search path
-// at $PPROF_BINARY_PATH. Looks inside these directories for files
-// named $BUILDID/$BASENAME and $BASENAME (if build id is available).
-func locateFile(obj plugin.ObjTool, file, buildID string, start uint64) (plugin.ObjFile, error) {
-	// Construct search path to examine
-	searchPath := os.Getenv("PPROF_BINARY_PATH")
-	if searchPath == "" {
-		// Use $HOME/pprof/binaries as default directory for local symbolization binaries
-		searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries")
-	}
-
-	// Collect names to search: {buildid/basename, basename}
-	var fileNames []string
-	if baseName := filepath.Base(file); buildID != "" {
-		fileNames = []string{filepath.Join(buildID, baseName), baseName}
-	} else {
-		fileNames = []string{baseName}
-	}
-	for _, path := range filepath.SplitList(searchPath) {
-		for nameIndex, name := range fileNames {
-			file := filepath.Join(path, name)
-			if f, err := obj.Open(file, start); err == nil {
-				fileBuildID := f.BuildID()
-				if buildID == "" || buildID == fileBuildID {
-					return f, nil
-				}
-				f.Close()
-				if nameIndex == 0 {
-					// If this is the first name, the path includes the build id. Report inconsistency.
-					return nil, fmt.Errorf("found file %s with inconsistent build id %s", file, fileBuildID)
-				}
-			}
-		}
-	}
-	// Try original file name
-	f, err := obj.Open(file, start)
-	if err == nil && buildID != "" {
-		if fileBuildID := f.BuildID(); fileBuildID != "" && fileBuildID != buildID {
-			// Mismatched build IDs, ignore
-			f.Close()
-			return nil, fmt.Errorf("mismatched build ids %s != %s", fileBuildID, buildID)
-		}
-	}
-	return f, err
-}
-
-// mappingTable contains the mechanisms for symbolization of a
-// profile.
-type mappingTable struct {
-	prof     *profile.Profile
-	segments map[*profile.Mapping]plugin.ObjFile
-}
-
-// Close releases any external processes being used for the mapping.
-func (mt *mappingTable) close() {
-	for _, segment := range mt.segments {
-		segment.Close()
-	}
-}
diff --git a/src/cmd/internal/pprof/symbolz/symbolz.go b/src/cmd/internal/pprof/symbolz/symbolz.go
deleted file mode 100644
index 2f2850a..0000000
--- a/src/cmd/internal/pprof/symbolz/symbolz.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package symbolz symbolizes a profile using the output from the symbolz
-// service.
-package symbolz
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"net/url"
-	"regexp"
-	"strconv"
-	"strings"
-
-	"cmd/internal/pprof/profile"
-)
-
-var (
-	symbolzRE = regexp.MustCompile(`(0x[[:xdigit:]]+)\s+(.*)`)
-)
-
-// Symbolize symbolizes profile p by parsing data returned by a
-// symbolz handler. syms receives the symbolz query (hex addresses
-// separated by '+') and returns the symbolz output in a string. It
-// symbolizes all locations based on their addresses, regardless of
-// mapping.
-func Symbolize(source string, syms func(string, string) ([]byte, error), p *profile.Profile) error {
-	if source = symbolz(source, p); source == "" {
-		// If the source is not a recognizable URL, do nothing.
-		return nil
-	}
-
-	// Construct query of addresses to symbolize.
-	var a []string
-	for _, l := range p.Location {
-		if l.Address != 0 && len(l.Line) == 0 {
-			a = append(a, fmt.Sprintf("%#x", l.Address))
-		}
-	}
-
-	if len(a) == 0 {
-		// No addresses to symbolize.
-		return nil
-	}
-	lines := make(map[uint64]profile.Line)
-	functions := make(map[string]*profile.Function)
-	if b, err := syms(source, strings.Join(a, "+")); err == nil {
-		buf := bytes.NewBuffer(b)
-		for {
-			l, err := buf.ReadString('\n')
-
-			if err != nil {
-				if err == io.EOF {
-					break
-				}
-				return err
-			}
-
-			if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 {
-				addr, err := strconv.ParseUint(symbol[1], 0, 64)
-				if err != nil {
-					return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err)
-				}
-
-				name := symbol[2]
-				fn := functions[name]
-				if fn == nil {
-					fn = &profile.Function{
-						ID:         uint64(len(p.Function) + 1),
-						Name:       name,
-						SystemName: name,
-					}
-					functions[name] = fn
-					p.Function = append(p.Function, fn)
-				}
-
-				lines[addr] = profile.Line{Function: fn}
-			}
-		}
-	}
-
-	for _, l := range p.Location {
-		if line, ok := lines[l.Address]; ok {
-			l.Line = []profile.Line{line}
-			if l.Mapping != nil {
-				l.Mapping.HasFunctions = true
-			}
-		}
-	}
-
-	return nil
-}
-
-// symbolz returns the corresponding symbolz source for a profile URL.
-func symbolz(source string, p *profile.Profile) string {
-	if url, err := url.Parse(source); err == nil && url.Host != "" {
-		if last := strings.LastIndex(url.Path, "/"); last != -1 {
-			if strings.HasSuffix(url.Path[:last], "pprof") {
-				url.Path = url.Path[:last] + "/symbol"
-			} else {
-				url.Path = url.Path[:last] + "/symbolz"
-			}
-			return url.String()
-		}
-	}
-
-	return ""
-}
diff --git a/src/cmd/internal/sys/arch.go b/src/cmd/internal/sys/arch.go
index 18accde..22c8c32 100644
--- a/src/cmd/internal/sys/arch.go
+++ b/src/cmd/internal/sys/arch.go
@@ -16,6 +16,7 @@ const (
 	ARM
 	ARM64
 	I386
+	MIPS
 	MIPS64
 	PPC64
 	S390X
@@ -97,6 +98,26 @@ var ArchARM64 = &Arch{
 	MinLC:     4,
 }
 
+var ArchMIPS = &Arch{
+	Name:      "mips",
+	Family:    MIPS,
+	ByteOrder: binary.BigEndian,
+	IntSize:   4,
+	PtrSize:   4,
+	RegSize:   4,
+	MinLC:     4,
+}
+
+var ArchMIPSLE = &Arch{
+	Name:      "mipsle",
+	Family:    MIPS,
+	ByteOrder: binary.LittleEndian,
+	IntSize:   4,
+	PtrSize:   4,
+	RegSize:   4,
+	MinLC:     4,
+}
+
 var ArchMIPS64 = &Arch{
 	Name:      "mips64",
 	Family:    MIPS64,
@@ -146,3 +167,18 @@ var ArchS390X = &Arch{
 	RegSize:   8,
 	MinLC:     2,
 }
+
+var Archs = [...]*Arch{
+	Arch386,
+	ArchAMD64,
+	ArchAMD64P32,
+	ArchARM,
+	ArchARM64,
+	ArchMIPS,
+	ArchMIPSLE,
+	ArchMIPS64,
+	ArchMIPS64LE,
+	ArchPPC64,
+	ArchPPC64LE,
+	ArchS390X,
+}
diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go
index ffaead7..16fddf2 100644
--- a/src/cmd/link/doc.go
+++ b/src/cmd/link/doc.go
@@ -85,6 +85,8 @@ Flags:
 		Link with C/C++ memory sanitizer support.
 	-o file
 		Write output to file (default a.out, or a.out.exe on Windows).
+	-pluginpath path
+		The path name used to prefix exported plugin symbols.
 	-r dir1:dir2:...
 		Set the ELF dynamic linker search path.
 	-race
diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go
index eff9a22..60bd45c 100644
--- a/src/cmd/link/internal/amd64/asm.go
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -1,5 +1,5 @@
 // Inferno utils/6l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -34,7 +34,6 @@ import (
 	"cmd/internal/obj"
 	"cmd/link/internal/ld"
 	"debug/elf"
-	"fmt"
 	"log"
 )
 
@@ -42,11 +41,11 @@ func PADDR(x uint32) uint32 {
 	return x &^ 0x80000000
 }
 
-func Addcall(ctxt *ld.Link, s *ld.LSym, t *ld.LSym) int64 {
+func Addcall(ctxt *ld.Link, s *ld.Symbol, t *ld.Symbol) int64 {
 	s.Attr |= ld.AttrReachable
 	i := s.Size
 	s.Size += 4
-	ld.Symgrow(ctxt, s, s.Size)
+	ld.Symgrow(s, s.Size)
 	r := ld.Addrel(s)
 	r.Sym = t
 	r.Off = int32(i)
@@ -55,78 +54,80 @@ func Addcall(ctxt *ld.Link, s *ld.LSym, t *ld.LSym) int64 {
 	return i + int64(r.Siz)
 }
 
-func gentext() {
-	if !ld.DynlinkingGo() {
+func gentext(ctxt *ld.Link) {
+	if !ctxt.DynlinkingGo() {
 		return
 	}
-	addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
-	if addmoduledata.Type == obj.STEXT {
+	addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+	if addmoduledata.Type == obj.STEXT && ld.Buildmode != ld.BuildmodePlugin {
 		// we're linking a module containing the runtime -> no need for
 		// an init function
 		return
 	}
 	addmoduledata.Attr |= ld.AttrReachable
-	initfunc := ld.Linklookup(ld.Ctxt, "go.link.addmoduledata", 0)
+	initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
 	initfunc.Type = obj.STEXT
 	initfunc.Attr |= ld.AttrLocal
 	initfunc.Attr |= ld.AttrReachable
 	o := func(op ...uint8) {
 		for _, op1 := range op {
-			ld.Adduint8(ld.Ctxt, initfunc, op1)
+			ld.Adduint8(ctxt, initfunc, op1)
 		}
 	}
 	// 0000000000000000 <local.dso_init>:
 	//    0:	48 8d 3d 00 00 00 00 	lea    0x0(%rip),%rdi        # 7 <local.dso_init+0x7>
 	// 			3: R_X86_64_PC32	runtime.firstmoduledata-0x4
 	o(0x48, 0x8d, 0x3d)
-	ld.Addpcrelplus(ld.Ctxt, initfunc, ld.Ctxt.Moduledata, 0)
+	ld.Addpcrelplus(ctxt, initfunc, ctxt.Moduledata, 0)
 	//    7:	e8 00 00 00 00       	callq  c <local.dso_init+0xc>
 	// 			8: R_X86_64_PLT32	runtime.addmoduledata-0x4
 	o(0xe8)
-	Addcall(ld.Ctxt, initfunc, addmoduledata)
+	Addcall(ctxt, initfunc, addmoduledata)
 	//    c:	c3                   	retq
 	o(0xc3)
-	ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
-	initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
+	if ld.Buildmode == ld.BuildmodePlugin {
+		ctxt.Textp = append(ctxt.Textp, addmoduledata)
+	}
+	ctxt.Textp = append(ctxt.Textp, initfunc)
+	initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
 	initarray_entry.Attr |= ld.AttrReachable
 	initarray_entry.Attr |= ld.AttrLocal
 	initarray_entry.Type = obj.SINITARR
-	ld.Addaddr(ld.Ctxt, initarray_entry, initfunc)
+	ld.Addaddr(ctxt, initarray_entry, initfunc)
 }
 
-func adddynrel(s *ld.LSym, r *ld.Reloc) {
+func adddynrel(ctxt *ld.Link, s *ld.Symbol, r *ld.Reloc) bool {
 	targ := r.Sym
-	ld.Ctxt.Cursym = s
 
 	switch r.Type {
 	default:
 		if r.Type >= 256 {
-			ld.Diag("unexpected relocation type %d", r.Type)
-			return
+			ld.Errorf(s, "unexpected relocation type %d", r.Type)
+			return false
 		}
 
 		// Handle relocations found in ELF object files.
 	case 256 + ld.R_X86_64_PC32:
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ.Name)
 		}
 		if targ.Type == 0 || targ.Type == obj.SXREF {
-			ld.Diag("unknown symbol %s in pcrel", targ.Name)
+			ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name)
 		}
 		r.Type = obj.R_PCREL
 		r.Add += 4
-		return
+		return true
 
 	case 256 + ld.R_X86_64_PLT32:
 		r.Type = obj.R_PCREL
 		r.Add += 4
 		if targ.Type == obj.SDYNIMPORT {
-			addpltsym(targ)
-			r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			addpltsym(ctxt, targ)
+			r.Sym = ctxt.Syms.Lookup(".plt", 0)
 			r.Add += int64(targ.Plt)
 		}
 
-		return
+		return true
 
 	case 256 + ld.R_X86_64_GOTPCREL, 256 + ld.R_X86_64_GOTPCRELX, 256 + ld.R_X86_64_REX_GOTPCRELX:
 		if targ.Type != obj.SDYNIMPORT {
@@ -137,26 +138,26 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 
 				r.Type = obj.R_PCREL
 				r.Add += 4
-				return
+				return true
 			}
 		}
 
 		// fall back to using GOT and hope for the best (CMOV*)
 		// TODO: just needs relocation, no need to put in .dynsym
-		addgotsym(targ)
+		addgotsym(ctxt, targ)
 
 		r.Type = obj.R_PCREL
-		r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0)
+		r.Sym = ctxt.Syms.Lookup(".got", 0)
 		r.Add += 4
 		r.Add += int64(targ.Got)
-		return
+		return true
 
 	case 256 + ld.R_X86_64_64:
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected R_X86_64_64 relocation for dynamic symbol %s", targ.Name)
 		}
 		r.Type = obj.R_ADDR
-		return
+		return true
 
 	// Handle relocations found in Mach-O object files.
 	case 512 + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0,
@@ -166,17 +167,17 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 		r.Type = obj.R_ADDR
 
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected reloc for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected reloc for dynamic symbol %s", targ.Name)
 		}
-		return
+		return true
 
 	case 512 + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1:
 		if targ.Type == obj.SDYNIMPORT {
-			addpltsym(targ)
-			r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			addpltsym(ctxt, targ)
+			r.Sym = ctxt.Syms.Lookup(".plt", 0)
 			r.Add = int64(targ.Plt)
 			r.Type = obj.R_PCREL
-			return
+			return true
 		}
 		fallthrough
 
@@ -189,92 +190,134 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 		r.Type = obj.R_PCREL
 
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected pc-relative reloc for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected pc-relative reloc for dynamic symbol %s", targ.Name)
 		}
-		return
+		return true
 
 	case 512 + ld.MACHO_X86_64_RELOC_GOT_LOAD*2 + 1:
 		if targ.Type != obj.SDYNIMPORT {
 			// have symbol
 			// turn MOVQ of GOT entry into LEAQ of symbol itself
 			if r.Off < 2 || s.P[r.Off-2] != 0x8b {
-				ld.Diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ.Name)
-				return
+				ld.Errorf(s, "unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ.Name)
+				return false
 			}
 
 			s.P[r.Off-2] = 0x8d
 			r.Type = obj.R_PCREL
-			return
+			return true
 		}
 		fallthrough
 
 		// fall through
 	case 512 + ld.MACHO_X86_64_RELOC_GOT*2 + 1:
 		if targ.Type != obj.SDYNIMPORT {
-			ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name)
 		}
-		addgotsym(targ)
+		addgotsym(ctxt, targ)
 		r.Type = obj.R_PCREL
-		r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0)
+		r.Sym = ctxt.Syms.Lookup(".got", 0)
 		r.Add += int64(targ.Got)
-		return
-	}
-
-	// Handle references to ELF symbols from our own object files.
-	if targ.Type != obj.SDYNIMPORT {
-		return
+		return true
 	}
 
 	switch r.Type {
 	case obj.R_CALL,
 		obj.R_PCREL:
-		if ld.HEADTYPE == obj.Hwindows {
+		if targ.Type != obj.SDYNIMPORT {
+			// nothing to do, the relocation will be laid out in reloc
+			return true
+		}
+		if ld.Headtype == obj.Hwindows || ld.Headtype == obj.Hwindowsgui {
 			// nothing to do, the relocation will be laid out in pereloc1
-			return
+			return true
 		} else {
 			// for both ELF and Mach-O
-			addpltsym(targ)
-			r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			addpltsym(ctxt, targ)
+			r.Sym = ctxt.Syms.Lookup(".plt", 0)
 			r.Add = int64(targ.Plt)
-			return
+			return true
 		}
 
 	case obj.R_ADDR:
 		if s.Type == obj.STEXT && ld.Iself {
-			if ld.HEADTYPE == obj.Hsolaris {
-				addpltsym(targ)
-				r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			if ld.Headtype == obj.Hsolaris {
+				addpltsym(ctxt, targ)
+				r.Sym = ctxt.Syms.Lookup(".plt", 0)
 				r.Add += int64(targ.Plt)
-				return
+				return true
 			}
 			// The code is asking for the address of an external
 			// function. We provide it with the address of the
 			// correspondent GOT symbol.
-			addgotsym(targ)
+			addgotsym(ctxt, targ)
 
-			r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0)
+			r.Sym = ctxt.Syms.Lookup(".got", 0)
 			r.Add += int64(targ.Got)
-			return
+			return true
+		}
+
+		// Process dynamic relocations for the data sections.
+		if ld.Buildmode == ld.BuildmodePIE && ld.Linkmode == ld.LinkInternal {
+			// When internally linking, generate dynamic relocations
+			// for all typical R_ADDR relocations. The exception
+			// are those R_ADDR that are created as part of generating
+			// the dynamic relocations and must be resolved statically.
+			//
+			// There are three phases relevant to understanding this:
+			//
+			//	dodata()  // we are here
+			//	address() // symbol address assignment
+			//	reloc()   // resolution of static R_ADDR relocs
+			//
+			// At this point symbol addresses have not been
+			// assigned yet (as the final size of the .rela section
+			// will affect the addresses), and so we cannot write
+			// the Elf64_Rela.r_offset now. Instead we delay it
+			// until after the 'address' phase of the linker is
+			// complete. We do this via Addaddrplus, which creates
+			// a new R_ADDR relocation which will be resolved in
+			// the 'reloc' phase.
+			//
+			// These synthetic static R_ADDR relocs must be skipped
+			// now, or else we will be caught in an infinite loop
+			// of generating synthetic relocs for our synthetic
+			// relocs.
+			switch s.Name {
+			case ".dynsym", ".rela", ".got.plt", ".dynamic":
+				return false
+			}
+		} else {
+			// Either internally linking a static executable,
+			// in which case we can resolve these relocations
+			// statically in the 'reloc' phase, or externally
+			// linking, in which case the relocation will be
+			// prepared in the 'reloc' phase and passed to the
+			// external linker in the 'asmb' phase.
+			if s.Type != obj.SDATA && s.Type != obj.SRODATA {
+				break
+			}
 		}
 
-		if s.Type != obj.SDATA {
-			break
-		}
 		if ld.Iself {
-			ld.Adddynsym(ld.Ctxt, targ)
-			rela := ld.Linklookup(ld.Ctxt, ".rela", 0)
-			ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off))
+			// TODO: We generate a R_X86_64_64 relocation for every R_ADDR, even
+			// though it would be more efficient (for the dynamic linker) if we
+			// generated R_X86_RELATIVE instead.
+			ld.Adddynsym(ctxt, targ)
+			rela := ctxt.Syms.Lookup(".rela", 0)
+			ld.Addaddrplus(ctxt, rela, s, int64(r.Off))
 			if r.Siz == 8 {
-				ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_X86_64_64))
+				ld.Adduint64(ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_X86_64_64))
 			} else {
-				ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_X86_64_32))
+				// TODO: never happens, remove.
+				ld.Adduint64(ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_X86_64_32))
 			}
-			ld.Adduint64(ld.Ctxt, rela, uint64(r.Add))
+			ld.Adduint64(ctxt, rela, uint64(r.Add))
 			r.Type = 256 // ignore during relocsym
-			return
+			return true
 		}
 
-		if ld.HEADTYPE == obj.Hdarwin && s.Size == int64(ld.SysArch.PtrSize) && r.Off == 0 {
+		if ld.Headtype == obj.Hdarwin && s.Size == int64(ld.SysArch.PtrSize) && r.Off == 0 {
 			// Mach-O relocations are a royal pain to lay out.
 			// They use a compact stateful bytecode representation
 			// that is too much bother to deal with.
@@ -285,31 +328,30 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 			// just in case the C code assigns to the variable,
 			// and of course it only works for single pointers,
 			// but we only need to support cgo and that's all it needs.
-			ld.Adddynsym(ld.Ctxt, targ)
+			ld.Adddynsym(ctxt, targ)
 
-			got := ld.Linklookup(ld.Ctxt, ".got", 0)
+			got := ctxt.Syms.Lookup(".got", 0)
 			s.Type = got.Type | obj.SSUB
 			s.Outer = got
 			s.Sub = got.Sub
 			got.Sub = s
 			s.Value = got.Size
-			ld.Adduint64(ld.Ctxt, got, 0)
-			ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(targ.Dynid))
+			ld.Adduint64(ctxt, got, 0)
+			ld.Adduint32(ctxt, ctxt.Syms.Lookup(".linkedit.got", 0), uint32(targ.Dynid))
 			r.Type = 256 // ignore during relocsym
-			return
+			return true
 		}
 
-		if ld.HEADTYPE == obj.Hwindows {
+		if ld.Headtype == obj.Hwindows || ld.Headtype == obj.Hwindowsgui {
 			// nothing to do, the relocation will be laid out in pereloc1
-			return
+			return true
 		}
 	}
 
-	ld.Ctxt.Cursym = s
-	ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type)
+	return false
 }
 
-func elfreloc1(r *ld.Reloc, sectoff int64) int {
+func elfreloc1(ctxt *ld.Link, r *ld.Reloc, sectoff int64) int {
 	ld.Thearch.Vput(uint64(sectoff))
 
 	elfsym := r.Xsym.ElfsymForReloc()
@@ -343,7 +385,7 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
 	case obj.R_CALL:
 		if r.Siz == 4 {
 			if r.Xsym.Type == obj.SDYNIMPORT {
-				if ld.DynlinkingGo() {
+				if ctxt.DynlinkingGo() {
 					ld.Thearch.Vput(ld.R_X86_64_PLT32 | uint64(elfsym)<<32)
 				} else {
 					ld.Thearch.Vput(ld.R_X86_64_GOTPCREL | uint64(elfsym)<<32)
@@ -378,14 +420,14 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func machoreloc1(r *ld.Reloc, sectoff int64) int {
+func machoreloc1(s *ld.Symbol, r *ld.Reloc, sectoff int64) int {
 	var v uint32
 
 	rs := r.Xsym
 
-	if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_PCREL {
+	if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_PCREL || r.Type == obj.R_GOTPCREL {
 		if rs.Dynid < 0 {
-			ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type)
+			ld.Errorf(s, "reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type)
 			return -1
 		}
 
@@ -394,7 +436,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 	} else {
 		v = uint32(rs.Sect.Extnum)
 		if v == 0 {
-			ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
+			ld.Errorf(s, "reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
 			return -1
 		}
 	}
@@ -414,6 +456,9 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 	case obj.R_PCREL:
 		v |= 1 << 24 // pc-relative bit
 		v |= ld.MACHO_X86_64_RELOC_SIGNED << 28
+	case obj.R_GOTPCREL:
+		v |= 1 << 24 // pc-relative bit
+		v |= ld.MACHO_X86_64_RELOC_GOT_LOAD << 28
 	}
 
 	switch r.Siz {
@@ -438,13 +483,13 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func pereloc1(r *ld.Reloc, sectoff int64) bool {
+func pereloc1(s *ld.Symbol, r *ld.Reloc, sectoff int64) bool {
 	var v uint32
 
 	rs := r.Xsym
 
 	if rs.Dynid < 0 {
-		ld.Diag("reloc %d to non-coff symbol %s type=%d", r.Type, rs.Name, rs.Type)
+		ld.Errorf(s, "reloc %d to non-coff symbol %s type=%d", r.Type, rs.Name, rs.Type)
 		return false
 	}
 
@@ -472,84 +517,84 @@ func pereloc1(r *ld.Reloc, sectoff int64) bool {
 	return true
 }
 
-func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
+func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
 	return -1
 }
 
-func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
+func archrelocvariant(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, t int64) int64 {
 	log.Fatalf("unexpected relocation variant")
 	return t
 }
 
-func elfsetupplt() {
-	plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
-	got := ld.Linklookup(ld.Ctxt, ".got.plt", 0)
+func elfsetupplt(ctxt *ld.Link) {
+	plt := ctxt.Syms.Lookup(".plt", 0)
+	got := ctxt.Syms.Lookup(".got.plt", 0)
 	if plt.Size == 0 {
 		// pushq got+8(IP)
-		ld.Adduint8(ld.Ctxt, plt, 0xff)
+		ld.Adduint8(ctxt, plt, 0xff)
 
-		ld.Adduint8(ld.Ctxt, plt, 0x35)
-		ld.Addpcrelplus(ld.Ctxt, plt, got, 8)
+		ld.Adduint8(ctxt, plt, 0x35)
+		ld.Addpcrelplus(ctxt, plt, got, 8)
 
 		// jmpq got+16(IP)
-		ld.Adduint8(ld.Ctxt, plt, 0xff)
+		ld.Adduint8(ctxt, plt, 0xff)
 
-		ld.Adduint8(ld.Ctxt, plt, 0x25)
-		ld.Addpcrelplus(ld.Ctxt, plt, got, 16)
+		ld.Adduint8(ctxt, plt, 0x25)
+		ld.Addpcrelplus(ctxt, plt, got, 16)
 
 		// nopl 0(AX)
-		ld.Adduint32(ld.Ctxt, plt, 0x00401f0f)
+		ld.Adduint32(ctxt, plt, 0x00401f0f)
 
 		// assume got->size == 0 too
-		ld.Addaddrplus(ld.Ctxt, got, ld.Linklookup(ld.Ctxt, ".dynamic", 0), 0)
+		ld.Addaddrplus(ctxt, got, ctxt.Syms.Lookup(".dynamic", 0), 0)
 
-		ld.Adduint64(ld.Ctxt, got, 0)
-		ld.Adduint64(ld.Ctxt, got, 0)
+		ld.Adduint64(ctxt, got, 0)
+		ld.Adduint64(ctxt, got, 0)
 	}
 }
 
-func addpltsym(s *ld.LSym) {
+func addpltsym(ctxt *ld.Link, s *ld.Symbol) {
 	if s.Plt >= 0 {
 		return
 	}
 
-	ld.Adddynsym(ld.Ctxt, s)
+	ld.Adddynsym(ctxt, s)
 
 	if ld.Iself {
-		plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
-		got := ld.Linklookup(ld.Ctxt, ".got.plt", 0)
-		rela := ld.Linklookup(ld.Ctxt, ".rela.plt", 0)
+		plt := ctxt.Syms.Lookup(".plt", 0)
+		got := ctxt.Syms.Lookup(".got.plt", 0)
+		rela := ctxt.Syms.Lookup(".rela.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt()
+			elfsetupplt(ctxt)
 		}
 
 		// jmpq *got+size(IP)
-		ld.Adduint8(ld.Ctxt, plt, 0xff)
+		ld.Adduint8(ctxt, plt, 0xff)
 
-		ld.Adduint8(ld.Ctxt, plt, 0x25)
-		ld.Addpcrelplus(ld.Ctxt, plt, got, got.Size)
+		ld.Adduint8(ctxt, plt, 0x25)
+		ld.Addpcrelplus(ctxt, plt, got, got.Size)
 
 		// add to got: pointer to current pos in plt
-		ld.Addaddrplus(ld.Ctxt, got, plt, plt.Size)
+		ld.Addaddrplus(ctxt, got, plt, plt.Size)
 
 		// pushq $x
-		ld.Adduint8(ld.Ctxt, plt, 0x68)
+		ld.Adduint8(ctxt, plt, 0x68)
 
-		ld.Adduint32(ld.Ctxt, plt, uint32((got.Size-24-8)/8))
+		ld.Adduint32(ctxt, plt, uint32((got.Size-24-8)/8))
 
 		// jmpq .plt
-		ld.Adduint8(ld.Ctxt, plt, 0xe9)
+		ld.Adduint8(ctxt, plt, 0xe9)
 
-		ld.Adduint32(ld.Ctxt, plt, uint32(-(plt.Size + 4)))
+		ld.Adduint32(ctxt, plt, uint32(-(plt.Size + 4)))
 
 		// rela
-		ld.Addaddrplus(ld.Ctxt, rela, got, got.Size-8)
+		ld.Addaddrplus(ctxt, rela, got, got.Size-8)
 
-		ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_X86_64_JMP_SLOT))
-		ld.Adduint64(ld.Ctxt, rela, 0)
+		ld.Adduint64(ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_X86_64_JMP_SLOT))
+		ld.Adduint64(ctxt, rela, 0)
 
 		s.Plt = int32(plt.Size - 16)
-	} else if ld.HEADTYPE == obj.Hdarwin {
+	} else if ld.Headtype == obj.Hdarwin {
 		// To do lazy symbol lookup right, we're supposed
 		// to tell the dynamic loader which library each
 		// symbol comes from and format the link info
@@ -560,54 +605,52 @@ func addpltsym(s *ld.LSym) {
 		// http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html
 		// has details about what we're avoiding.
 
-		addgotsym(s)
-		plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
+		addgotsym(ctxt, s)
+		plt := ctxt.Syms.Lookup(".plt", 0)
 
-		ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.plt", 0), uint32(s.Dynid))
+		ld.Adduint32(ctxt, ctxt.Syms.Lookup(".linkedit.plt", 0), uint32(s.Dynid))
 
 		// jmpq *got+size(IP)
 		s.Plt = int32(plt.Size)
 
-		ld.Adduint8(ld.Ctxt, plt, 0xff)
-		ld.Adduint8(ld.Ctxt, plt, 0x25)
-		ld.Addpcrelplus(ld.Ctxt, plt, ld.Linklookup(ld.Ctxt, ".got", 0), int64(s.Got))
+		ld.Adduint8(ctxt, plt, 0xff)
+		ld.Adduint8(ctxt, plt, 0x25)
+		ld.Addpcrelplus(ctxt, plt, ctxt.Syms.Lookup(".got", 0), int64(s.Got))
 	} else {
-		ld.Diag("addpltsym: unsupported binary format")
+		ld.Errorf(s, "addpltsym: unsupported binary format")
 	}
 }
 
-func addgotsym(s *ld.LSym) {
+func addgotsym(ctxt *ld.Link, s *ld.Symbol) {
 	if s.Got >= 0 {
 		return
 	}
 
-	ld.Adddynsym(ld.Ctxt, s)
-	got := ld.Linklookup(ld.Ctxt, ".got", 0)
+	ld.Adddynsym(ctxt, s)
+	got := ctxt.Syms.Lookup(".got", 0)
 	s.Got = int32(got.Size)
-	ld.Adduint64(ld.Ctxt, got, 0)
+	ld.Adduint64(ctxt, got, 0)
 
 	if ld.Iself {
-		rela := ld.Linklookup(ld.Ctxt, ".rela", 0)
-		ld.Addaddrplus(ld.Ctxt, rela, got, int64(s.Got))
-		ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_X86_64_GLOB_DAT))
-		ld.Adduint64(ld.Ctxt, rela, 0)
-	} else if ld.HEADTYPE == obj.Hdarwin {
-		ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(s.Dynid))
+		rela := ctxt.Syms.Lookup(".rela", 0)
+		ld.Addaddrplus(ctxt, rela, got, int64(s.Got))
+		ld.Adduint64(ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_X86_64_GLOB_DAT))
+		ld.Adduint64(ctxt, rela, 0)
+	} else if ld.Headtype == obj.Hdarwin {
+		ld.Adduint32(ctxt, ctxt.Syms.Lookup(".linkedit.got", 0), uint32(s.Dynid))
 	} else {
-		ld.Diag("addgotsym: unsupported binary format")
+		ld.Errorf(s, "addgotsym: unsupported binary format")
 	}
 }
 
-func asmb() {
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
+func asmb(ctxt *ld.Link) {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f asmb\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f codeblk\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f codeblk\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	if ld.Iself {
 		ld.Asmbelfsetup()
@@ -616,48 +659,52 @@ func asmb() {
 	sect := ld.Segtext.Sect
 	ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
 	// 0xCC is INT $3 - breakpoint instruction
-	ld.CodeblkPad(int64(sect.Vaddr), int64(sect.Length), []byte{0xCC})
+	ld.CodeblkPad(ctxt, int64(sect.Vaddr), int64(sect.Length), []byte{0xCC})
 	for sect = sect.Next; sect != nil; sect = sect.Next {
 		ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-		ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
+		ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
 	}
 
 	if ld.Segrodata.Filelen > 0 {
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f rodatblk\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-
 		ld.Cseek(int64(ld.Segrodata.Fileoff))
-		ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+		ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+	}
+	if ld.Segrelrodata.Filelen > 0 {
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f relrodatblk\n", obj.Cputime())
+		}
+		ld.Cseek(int64(ld.Segrelrodata.Fileoff))
+		ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
 	}
 
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f datblk\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	ld.Cseek(int64(ld.Segdata.Fileoff))
-	ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+	ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
 
 	ld.Cseek(int64(ld.Segdwarf.Fileoff))
-	ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+	ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
 
 	machlink := int64(0)
-	if ld.HEADTYPE == obj.Hdarwin {
-		machlink = ld.Domacholink()
+	if ld.Headtype == obj.Hdarwin {
+		machlink = ld.Domacholink(ctxt)
 	}
 
-	switch ld.HEADTYPE {
+	switch ld.Headtype {
 	default:
-		ld.Diag("unknown header type %d", ld.HEADTYPE)
+		ld.Errorf(nil, "unknown header type %v", ld.Headtype)
 		fallthrough
 
 	case obj.Hplan9:
 		break
 
 	case obj.Hdarwin:
-		ld.Debug['8'] = 1 /* 64-bit addresses */
+		ld.Flag8 = true /* 64-bit addresses */
 
 	case obj.Hlinux,
 		obj.Hfreebsd,
@@ -665,10 +712,11 @@ func asmb() {
 		obj.Hopenbsd,
 		obj.Hdragonfly,
 		obj.Hsolaris:
-		ld.Debug['8'] = 1 /* 64-bit addresses */
+		ld.Flag8 = true /* 64-bit addresses */
 
 	case obj.Hnacl,
-		obj.Hwindows:
+		obj.Hwindows,
+		obj.Hwindowsgui:
 		break
 	}
 
@@ -676,19 +724,18 @@ func asmb() {
 	ld.Spsize = 0
 	ld.Lcsize = 0
 	symo := int64(0)
-	if ld.Debug['s'] == 0 {
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime())
+	if !*ld.FlagS {
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f sym\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 		case obj.Hplan9:
-			ld.Debug['s'] = 1
+			*ld.FlagS = true
 			symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = int64(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = int64(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink))
 
 		case obj.Hlinux,
 			obj.Hfreebsd,
@@ -698,36 +745,37 @@ func asmb() {
 			obj.Hsolaris,
 			obj.Hnacl:
 			symo = int64(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
-			symo = ld.Rnd(symo, int64(ld.INITRND))
+			symo = ld.Rnd(symo, int64(*ld.FlagRound))
 
-		case obj.Hwindows:
+		case obj.Hwindows,
+			obj.Hwindowsgui:
 			symo = int64(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
 			symo = ld.Rnd(symo, ld.PEFILEALIGN)
 		}
 
 		ld.Cseek(symo)
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
 				ld.Cseek(symo)
-				ld.Asmelfsym()
+				ld.Asmelfsym(ctxt)
 				ld.Cflush()
 				ld.Cwrite(ld.Elfstrdat)
 
-				if ld.Debug['v'] != 0 {
-					fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime())
+				if ctxt.Debugvlog != 0 {
+					ctxt.Logf("%5.2f dwarf\n", obj.Cputime())
 				}
 
 				if ld.Linkmode == ld.LinkExternal {
-					ld.Elfemitreloc()
+					ld.Elfemitreloc(ctxt)
 				}
 			}
 
 		case obj.Hplan9:
-			ld.Asmplan9sym()
+			ld.Asmplan9sym(ctxt)
 			ld.Cflush()
 
-			sym := ld.Linklookup(ld.Ctxt, "pclntab", 0)
+			sym := ctxt.Syms.Lookup("pclntab", 0)
 			if sym != nil {
 				ld.Lcsize = int32(len(sym.P))
 				for i := 0; int32(i) < ld.Lcsize; i++ {
@@ -737,24 +785,23 @@ func asmb() {
 				ld.Cflush()
 			}
 
-		case obj.Hwindows:
-			if ld.Debug['v'] != 0 {
-				fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime())
+		case obj.Hwindows, obj.Hwindowsgui:
+			if ctxt.Debugvlog != 0 {
+				ctxt.Logf("%5.2f dwarf\n", obj.Cputime())
 			}
 
 		case obj.Hdarwin:
 			if ld.Linkmode == ld.LinkExternal {
-				ld.Machoemitreloc()
+				ld.Machoemitreloc(ctxt)
 			}
 		}
 	}
 
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f headr\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f headr\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 	ld.Cseek(0)
-	switch ld.HEADTYPE {
+	switch ld.Headtype {
 	default:
 	case obj.Hplan9: /* plan9 */
 		magic := int32(4*26*26 + 7)
@@ -765,14 +812,14 @@ func asmb() {
 		ld.Lputb(uint32(ld.Segdata.Filelen))
 		ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
 		ld.Lputb(uint32(ld.Symsize)) /* nsyms */
-		vl := ld.Entryvalue()
+		vl := ld.Entryvalue(ctxt)
 		ld.Lputb(PADDR(uint32(vl))) /* va of entry */
 		ld.Lputb(uint32(ld.Spsize)) /* sp offsets */
 		ld.Lputb(uint32(ld.Lcsize)) /* line offsets */
 		ld.Vputb(uint64(vl))        /* va of entry */
 
 	case obj.Hdarwin:
-		ld.Asmbmacho()
+		ld.Asmbmacho(ctxt)
 
 	case obj.Hlinux,
 		obj.Hfreebsd,
@@ -781,11 +828,48 @@ func asmb() {
 		obj.Hdragonfly,
 		obj.Hsolaris,
 		obj.Hnacl:
-		ld.Asmbelf(symo)
+		ld.Asmbelf(ctxt, symo)
 
-	case obj.Hwindows:
-		ld.Asmbpe()
+	case obj.Hwindows,
+		obj.Hwindowsgui:
+		ld.Asmbpe(ctxt)
 	}
 
 	ld.Cflush()
 }
+
+func tlsIEtoLE(s *ld.Symbol, off, size int) {
+	// Transform the PC-relative instruction into a constant load.
+	// That is,
+	//
+	//	MOVQ X(IP), REG  ->  MOVQ $Y, REG
+	//
+	// To determine the instruction and register, we study the op codes.
+	// Consult an AMD64 instruction encoding guide to decipher this.
+	if off < 3 {
+		log.Fatal("R_X86_64_GOTTPOFF reloc not preceded by MOVQ or ADDQ instruction")
+	}
+	op := s.P[off-3 : off]
+	reg := op[2] >> 3
+
+	if op[1] == 0x8b || reg == 4 {
+		// MOVQ
+		if op[0] == 0x4c {
+			op[0] = 0x49
+		} else if size == 4 && op[0] == 0x44 {
+			op[0] = 0x41
+		}
+		if op[1] == 0x8b {
+			op[1] = 0xc7
+		} else {
+			op[1] = 0x81 // special case for SP
+		}
+		op[2] = 0xc0 | reg
+	} else {
+		// An alternate op is ADDQ. This is handled by GNU gold,
+		// but right now is not generated by the Go compiler:
+		//	ADDQ X(IP), REG  ->  ADDQ $Y, REG
+		// Consider adding support for it here.
+		log.Fatalf("expected TLS IE op to be MOVQ, got %v", op)
+	}
+}
diff --git a/src/cmd/link/internal/amd64/l.go b/src/cmd/link/internal/amd64/l.go
index 05f7fa3..393da6b 100644
--- a/src/cmd/link/internal/amd64/l.go
+++ b/src/cmd/link/internal/amd64/l.go
@@ -1,5 +1,5 @@
 // Inferno utils/6l/l.h
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -31,13 +31,13 @@
 package amd64
 
 const (
-	MaxAlign  = 32 // max data alignment
-	MinAlign  = 1  // min data alignment
-	FuncAlign = 16
+	maxAlign  = 32 // max data alignment
+	minAlign  = 1  // min data alignment
+	funcAlign = 16
 )
 
 /* Used by ../internal/ld/dwarf.go */
 const (
-	DWARFREGSP = 7
-	DWARFREGLR = 16
+	dwarfRegSP = 7
+	dwarfRegLR = 16
 )
diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go
index f62f237..9646b60 100644
--- a/src/cmd/link/internal/amd64/obj.go
+++ b/src/cmd/link/internal/amd64/obj.go
@@ -1,5 +1,5 @@
 // Inferno utils/6l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -35,27 +35,19 @@ import (
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
 	"fmt"
-	"log"
 )
 
-// Reading object files.
-
-func Main() {
-	linkarchinit()
-	ld.Ldmain()
-}
-
-func linkarchinit() {
+func Init() {
 	ld.SysArch = sys.ArchAMD64
-	if obj.Getgoarch() == "amd64p32" {
+	if obj.GOARCH == "amd64p32" {
 		ld.SysArch = sys.ArchAMD64P32
 	}
 
-	ld.Thearch.Funcalign = FuncAlign
-	ld.Thearch.Maxalign = MaxAlign
-	ld.Thearch.Minalign = MinAlign
-	ld.Thearch.Dwarfregsp = DWARFREGSP
-	ld.Thearch.Dwarfreglr = DWARFREGLR
+	ld.Thearch.Funcalign = funcAlign
+	ld.Thearch.Maxalign = maxAlign
+	ld.Thearch.Minalign = minAlign
+	ld.Thearch.Dwarfregsp = dwarfRegSP
+	ld.Thearch.Dwarfreglr = dwarfRegLR
 
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Archinit = archinit
@@ -73,6 +65,7 @@ func linkarchinit() {
 	ld.Thearch.Append16 = ld.Append16l
 	ld.Thearch.Append32 = ld.Append32l
 	ld.Thearch.Append64 = ld.Append64l
+	ld.Thearch.TLSIEtoLE = tlsIEtoLE
 
 	ld.Thearch.Linuxdynld = "/lib64/ld-linux-x86-64.so.2"
 	ld.Thearch.Freebsddynld = "/libexec/ld-elf.so.1"
@@ -82,67 +75,36 @@ func linkarchinit() {
 	ld.Thearch.Solarisdynld = "/lib/amd64/ld.so.1"
 }
 
-func archinit() {
-	// getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
-	// Go was built; see ../../make.bash.
-	if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" {
-		ld.Linkmode = ld.LinkInternal
-	}
-
-	if ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ld.DynlinkingGo() {
-		ld.Linkmode = ld.LinkExternal
-	}
-
-	switch ld.HEADTYPE {
-	default:
-		if ld.Linkmode == ld.LinkAuto {
-			ld.Linkmode = ld.LinkInternal
-		}
-		if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" {
-			log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE)))
-		}
-
-	case obj.Hdarwin,
-		obj.Hdragonfly,
-		obj.Hfreebsd,
-		obj.Hlinux,
-		obj.Hnacl,
-		obj.Hnetbsd,
-		obj.Hopenbsd,
-		obj.Hsolaris,
-		obj.Hwindows:
-		break
-	}
-
-	switch ld.HEADTYPE {
+func archinit(ctxt *ld.Link) {
+	switch ld.Headtype {
 	default:
-		ld.Exitf("unknown -H option: %v", ld.HEADTYPE)
+		ld.Exitf("unknown -H option: %v", ld.Headtype)
 
 	case obj.Hplan9: /* plan 9 */
 		ld.HEADR = 32 + 8
 
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x200000 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x200000 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x200000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x200000
 		}
 
 	case obj.Hdarwin: /* apple MACH */
 		ld.Machoinit()
 
 		ld.HEADR = ld.INITIAL_MACHO_HEADR
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 4096 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x1000000 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
 
 	case obj.Hlinux, /* elf64 executable */
@@ -151,50 +113,50 @@ func archinit() {
 		obj.Hopenbsd,   /* openbsd */
 		obj.Hdragonfly, /* dragonfly */
 		obj.Hsolaris:   /* solaris */
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 
 		ld.HEADR = ld.ELFRESERVE
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = (1 << 22) + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = (1 << 22) + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
 
 	case obj.Hnacl:
-		ld.Elfinit()
-		ld.Debug['w']++ // disable dwarf, which gets confused and is useless anyway
+		ld.Elfinit(ctxt)
+		*ld.FlagW = true // disable dwarf, which gets confused and is useless anyway
 		ld.HEADR = 0x10000
 		ld.Funcalign = 32
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x20000
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x20000
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x10000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
 		}
 
-	case obj.Hwindows: /* PE executable */
-		ld.Peinit()
+	case obj.Hwindows, obj.Hwindowsgui: /* PE executable */
+		ld.Peinit(ctxt)
 
 		ld.HEADR = ld.PEFILEHEADR
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = ld.PEBASE + int64(ld.PESECTHEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = ld.PEBASE + int64(ld.PESECTHEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = ld.PESECTALIGN
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = ld.PESECTALIGN
 		}
 	}
 
-	if ld.INITDAT != 0 && ld.INITRND != 0 {
-		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND))
+	if *ld.FlagDataAddr != 0 && *ld.FlagRound != 0 {
+		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(*ld.FlagDataAddr), uint32(*ld.FlagRound))
 	}
 }
diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go
index 0c3e957..ee57df1 100644
--- a/src/cmd/link/internal/arm/asm.go
+++ b/src/cmd/link/internal/arm/asm.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -58,23 +58,23 @@ import (
 //    c:        00000004        .word   0x00000004
 //                      c: R_ARM_GOT_PREL       local.moduledata
 
-func gentext() {
-	if !ld.DynlinkingGo() {
+func gentext(ctxt *ld.Link) {
+	if !ctxt.DynlinkingGo() {
 		return
 	}
-	addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
-	if addmoduledata.Type == obj.STEXT {
+	addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+	if addmoduledata.Type == obj.STEXT && ld.Buildmode != ld.BuildmodePlugin {
 		// we're linking a module containing the runtime -> no need for
 		// an init function
 		return
 	}
 	addmoduledata.Attr |= ld.AttrReachable
-	initfunc := ld.Linklookup(ld.Ctxt, "go.link.addmoduledata", 0)
+	initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
 	initfunc.Type = obj.STEXT
 	initfunc.Attr |= ld.AttrLocal
 	initfunc.Attr |= ld.AttrReachable
 	o := func(op uint32) {
-		ld.Adduint32(ld.Ctxt, initfunc, op)
+		ld.Adduint32(ctxt, initfunc, op)
 	}
 	o(0xe59f0004)
 	o(0xe08f0000)
@@ -83,7 +83,7 @@ func gentext() {
 	rel := ld.Addrel(initfunc)
 	rel.Off = 8
 	rel.Siz = 4
-	rel.Sym = ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
+	rel.Sym = ctxt.Syms.Lookup("runtime.addmoduledata", 0)
 	rel.Type = obj.R_CALLARM
 	rel.Add = 0xeafffffe // vomit
 
@@ -91,16 +91,19 @@ func gentext() {
 	rel = ld.Addrel(initfunc)
 	rel.Off = 12
 	rel.Siz = 4
-	rel.Sym = ld.Ctxt.Moduledata
+	rel.Sym = ctxt.Moduledata
 	rel.Type = obj.R_PCREL
 	rel.Add = 4
 
-	ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
-	initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
+	if ld.Buildmode == ld.BuildmodePlugin {
+		ctxt.Textp = append(ctxt.Textp, addmoduledata)
+	}
+	ctxt.Textp = append(ctxt.Textp, initfunc)
+	initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
 	initarray_entry.Attr |= ld.AttrReachable
 	initarray_entry.Attr |= ld.AttrLocal
 	initarray_entry.Type = obj.SINITARR
-	ld.Addaddr(ld.Ctxt, initarray_entry, initfunc)
+	ld.Addaddr(ctxt, initarray_entry, initfunc)
 }
 
 // Preserve highest 8 bits of a, and do addition to lower 24-bit
@@ -109,15 +112,14 @@ func braddoff(a int32, b int32) int32 {
 	return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b))
 }
 
-func adddynrel(s *ld.LSym, r *ld.Reloc) {
+func adddynrel(ctxt *ld.Link, s *ld.Symbol, r *ld.Reloc) bool {
 	targ := r.Sym
-	ld.Ctxt.Cursym = s
 
 	switch r.Type {
 	default:
 		if r.Type >= 256 {
-			ld.Diag("unexpected relocation type %d", r.Type)
-			return
+			ld.Errorf(s, "unexpected relocation type %d", r.Type)
+			return false
 		}
 
 		// Handle relocations found in ELF object files.
@@ -125,75 +127,75 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 		r.Type = obj.R_CALLARM
 
 		if targ.Type == obj.SDYNIMPORT {
-			addpltsym(ld.Ctxt, targ)
-			r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			addpltsym(ctxt, targ)
+			r.Sym = ctxt.Syms.Lookup(".plt", 0)
 			r.Add = int64(braddoff(int32(r.Add), targ.Plt/4))
 		}
 
-		return
+		return true
 
 	case 256 + ld.R_ARM_THM_PC22: // R_ARM_THM_CALL
 		ld.Exitf("R_ARM_THM_CALL, are you using -marm?")
-		return
+		return false
 
 	case 256 + ld.R_ARM_GOT32: // R_ARM_GOT_BREL
 		if targ.Type != obj.SDYNIMPORT {
-			addgotsyminternal(ld.Ctxt, targ)
+			addgotsyminternal(ctxt, targ)
 		} else {
-			addgotsym(ld.Ctxt, targ)
+			addgotsym(ctxt, targ)
 		}
 
 		r.Type = obj.R_CONST // write r->add during relocsym
 		r.Sym = nil
 		r.Add += int64(targ.Got)
-		return
+		return true
 
 	case 256 + ld.R_ARM_GOT_PREL: // GOT(nil) + A - nil
 		if targ.Type != obj.SDYNIMPORT {
-			addgotsyminternal(ld.Ctxt, targ)
+			addgotsyminternal(ctxt, targ)
 		} else {
-			addgotsym(ld.Ctxt, targ)
+			addgotsym(ctxt, targ)
 		}
 
 		r.Type = obj.R_PCREL
-		r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0)
+		r.Sym = ctxt.Syms.Lookup(".got", 0)
 		r.Add += int64(targ.Got) + 4
-		return
+		return true
 
 	case 256 + ld.R_ARM_GOTOFF: // R_ARM_GOTOFF32
 		r.Type = obj.R_GOTOFF
 
-		return
+		return true
 
 	case 256 + ld.R_ARM_GOTPC: // R_ARM_BASE_PREL
 		r.Type = obj.R_PCREL
 
-		r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0)
+		r.Sym = ctxt.Syms.Lookup(".got", 0)
 		r.Add += 4
-		return
+		return true
 
 	case 256 + ld.R_ARM_CALL:
 		r.Type = obj.R_CALLARM
 		if targ.Type == obj.SDYNIMPORT {
-			addpltsym(ld.Ctxt, targ)
-			r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			addpltsym(ctxt, targ)
+			r.Sym = ctxt.Syms.Lookup(".plt", 0)
 			r.Add = int64(braddoff(int32(r.Add), targ.Plt/4))
 		}
 
-		return
+		return true
 
 	case 256 + ld.R_ARM_REL32: // R_ARM_REL32
 		r.Type = obj.R_PCREL
 
 		r.Add += 4
-		return
+		return true
 
 	case 256 + ld.R_ARM_ABS32:
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ.Name)
 		}
 		r.Type = obj.R_ADDR
-		return
+		return true
 
 		// we can just ignore this, because we are targeting ARM V5+ anyway
 	case 256 + ld.R_ARM_V4BX:
@@ -203,52 +205,51 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 		}
 
 		r.Sym = nil
-		return
+		return true
 
 	case 256 + ld.R_ARM_PC24,
 		256 + ld.R_ARM_JUMP24:
 		r.Type = obj.R_CALLARM
 		if targ.Type == obj.SDYNIMPORT {
-			addpltsym(ld.Ctxt, targ)
-			r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			addpltsym(ctxt, targ)
+			r.Sym = ctxt.Syms.Lookup(".plt", 0)
 			r.Add = int64(braddoff(int32(r.Add), targ.Plt/4))
 		}
 
-		return
+		return true
 	}
 
 	// Handle references to ELF symbols from our own object files.
 	if targ.Type != obj.SDYNIMPORT {
-		return
+		return true
 	}
 
 	switch r.Type {
 	case obj.R_CALLARM:
-		addpltsym(ld.Ctxt, targ)
-		r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+		addpltsym(ctxt, targ)
+		r.Sym = ctxt.Syms.Lookup(".plt", 0)
 		r.Add = int64(targ.Plt)
-		return
+		return true
 
 	case obj.R_ADDR:
 		if s.Type != obj.SDATA {
 			break
 		}
 		if ld.Iself {
-			ld.Adddynsym(ld.Ctxt, targ)
-			rel := ld.Linklookup(ld.Ctxt, ".rel", 0)
-			ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off))
-			ld.Adduint32(ld.Ctxt, rel, ld.ELF32_R_INFO(uint32(targ.Dynid), ld.R_ARM_GLOB_DAT)) // we need a nil + A dynamic reloc
-			r.Type = obj.R_CONST                                                               // write r->add during relocsym
+			ld.Adddynsym(ctxt, targ)
+			rel := ctxt.Syms.Lookup(".rel", 0)
+			ld.Addaddrplus(ctxt, rel, s, int64(r.Off))
+			ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(targ.Dynid), ld.R_ARM_GLOB_DAT)) // we need a nil + A dynamic reloc
+			r.Type = obj.R_CONST                                                            // write r->add during relocsym
 			r.Sym = nil
-			return
+			return true
 		}
 	}
 
-	ld.Ctxt.Cursym = s
-	ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type)
+	return false
 }
 
-func elfreloc1(r *ld.Reloc, sectoff int64) int {
+func elfreloc1(ctxt *ld.Link, r *ld.Reloc, sectoff int64) int {
 	ld.Thearch.Lput(uint32(sectoff))
 
 	elfsym := r.Xsym.ElfsymForReloc()
@@ -298,41 +299,41 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func elfsetupplt() {
-	plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
-	got := ld.Linklookup(ld.Ctxt, ".got.plt", 0)
+func elfsetupplt(ctxt *ld.Link) {
+	plt := ctxt.Syms.Lookup(".plt", 0)
+	got := ctxt.Syms.Lookup(".got.plt", 0)
 	if plt.Size == 0 {
 		// str lr, [sp, #-4]!
-		ld.Adduint32(ld.Ctxt, plt, 0xe52de004)
+		ld.Adduint32(ctxt, plt, 0xe52de004)
 
 		// ldr lr, [pc, #4]
-		ld.Adduint32(ld.Ctxt, plt, 0xe59fe004)
+		ld.Adduint32(ctxt, plt, 0xe59fe004)
 
 		// add lr, pc, lr
-		ld.Adduint32(ld.Ctxt, plt, 0xe08fe00e)
+		ld.Adduint32(ctxt, plt, 0xe08fe00e)
 
 		// ldr pc, [lr, #8]!
-		ld.Adduint32(ld.Ctxt, plt, 0xe5bef008)
+		ld.Adduint32(ctxt, plt, 0xe5bef008)
 
 		// .word &GLOBAL_OFFSET_TABLE[0] - .
-		ld.Addpcrelplus(ld.Ctxt, plt, got, 4)
+		ld.Addpcrelplus(ctxt, plt, got, 4)
 
 		// the first .plt entry requires 3 .plt.got entries
-		ld.Adduint32(ld.Ctxt, got, 0)
+		ld.Adduint32(ctxt, got, 0)
 
-		ld.Adduint32(ld.Ctxt, got, 0)
-		ld.Adduint32(ld.Ctxt, got, 0)
+		ld.Adduint32(ctxt, got, 0)
+		ld.Adduint32(ctxt, got, 0)
 	}
 }
 
-func machoreloc1(r *ld.Reloc, sectoff int64) int {
+func machoreloc1(s *ld.Symbol, r *ld.Reloc, sectoff int64) int {
 	var v uint32
 
 	rs := r.Xsym
 
 	if r.Type == obj.R_PCREL {
 		if rs.Type == obj.SHOSTOBJ {
-			ld.Diag("pc-relative relocation of external symbol is not supported")
+			ld.Errorf(s, "pc-relative relocation of external symbol is not supported")
 			return -1
 		}
 		if r.Siz != 4 {
@@ -356,13 +357,13 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 		ld.Thearch.Lput(o1)
 		ld.Thearch.Lput(uint32(ld.Symaddr(rs)))
 		ld.Thearch.Lput(o2)
-		ld.Thearch.Lput(uint32(ld.Ctxt.Cursym.Value + int64(r.Off)))
+		ld.Thearch.Lput(uint32(s.Value + int64(r.Off)))
 		return 0
 	}
 
 	if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM {
 		if rs.Dynid < 0 {
-			ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type)
+			ld.Errorf(s, "reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type)
 			return -1
 		}
 
@@ -371,7 +372,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 	} else {
 		v = uint32(rs.Sect.Extnum)
 		if v == 0 {
-			ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
+			ld.Errorf(s, "reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
 			return -1
 		}
 	}
@@ -410,7 +411,164 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
+// sign extend a 24-bit integer
+func signext24(x int64) int32 {
+	return (int32(x) << 8) >> 8
+}
+
+// encode an immediate in ARM's imm12 format. copied from ../../../internal/obj/arm/asm5.go
+func immrot(v uint32) uint32 {
+	for i := 0; i < 16; i++ {
+		if v&^0xff == 0 {
+			return uint32(i<<8) | v | 1<<25
+		}
+		v = v<<2 | v>>30
+	}
+	return 0
+}
+
+// Convert the direct jump relocation r to refer to a trampoline if the target is too far
+func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) {
+	switch r.Type {
+	case obj.R_CALLARM:
+		// r.Add is the instruction
+		// low 24-bit encodes the target address
+		t := (ld.Symaddr(r.Sym) + int64(signext24(r.Add&0xffffff)*4) - (s.Value + int64(r.Off))) / 4
+		if t > 0x7fffff || t < -0x800000 || (*ld.FlagDebugTramp > 1 && s.File != r.Sym.File) {
+			// direct call too far, need to insert trampoline.
+			// look up existing trampolines first. if we found one within the range
+			// of direct call, we can reuse it. otherwise create a new one.
+			offset := (signext24(r.Add&0xffffff) + 2) * 4
+			var tramp *ld.Symbol
+			for i := 0; ; i++ {
+				name := r.Sym.Name + fmt.Sprintf("%+d-tramp%d", offset, i)
+				tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
+				if tramp.Type == obj.SDYNIMPORT {
+					// don't reuse trampoline defined in other module
+					continue
+				}
+				if tramp.Value == 0 {
+					// either the trampoline does not exist -- we need to create one,
+					// or found one the address which is not assigned -- this will be
+					// laid down immediately after the current function. use this one.
+					break
+				}
+
+				t = (ld.Symaddr(tramp) - 8 - (s.Value + int64(r.Off))) / 4
+				if t >= -0x800000 && t < 0x7fffff {
+					// found an existing trampoline that is not too far
+					// we can just use it
+					break
+				}
+			}
+			if tramp.Type == 0 {
+				// trampoline does not exist, create one
+				ctxt.AddTramp(tramp)
+				if ctxt.DynlinkingGo() {
+					if immrot(uint32(offset)) == 0 {
+						ld.Errorf(s, "odd offset in dynlink direct call: %v+%d", r.Sym, offset)
+					}
+					gentrampdyn(tramp, r.Sym, int64(offset))
+				} else if ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ld.Buildmode == ld.BuildmodePIE {
+					gentramppic(tramp, r.Sym, int64(offset))
+				} else {
+					gentramp(tramp, r.Sym, int64(offset))
+				}
+			}
+			// modify reloc to point to tramp, which will be resolved later
+			r.Sym = tramp
+			r.Add = r.Add&0xff000000 | 0xfffffe // clear the offset embedded in the instruction
+			r.Done = 0
+		}
+	default:
+		ld.Errorf(s, "trampoline called with non-jump reloc: %v", r.Type)
+	}
+}
+
+// generate a trampoline to target+offset
+func gentramp(tramp, target *ld.Symbol, offset int64) {
+	tramp.Size = 12 // 3 instructions
+	tramp.P = make([]byte, tramp.Size)
+	t := ld.Symaddr(target) + int64(offset)
+	o1 := uint32(0xe5900000 | 11<<12 | 15<<16) // MOVW (R15), R11 // R15 is actual pc + 8
+	o2 := uint32(0xe12fff10 | 11)              // JMP  (R11)
+	o3 := uint32(t)                            // WORD $target
+	ld.SysArch.ByteOrder.PutUint32(tramp.P, o1)
+	ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2)
+	ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3)
+
+	if ld.Linkmode == ld.LinkExternal {
+		r := ld.Addrel(tramp)
+		r.Off = 8
+		r.Type = obj.R_ADDR
+		r.Siz = 4
+		r.Sym = target
+		r.Add = offset
+	}
+}
+
+// generate a trampoline to target+offset in position independent code
+func gentramppic(tramp, target *ld.Symbol, offset int64) {
+	tramp.Size = 16 // 4 instructions
+	tramp.P = make([]byte, tramp.Size)
+	o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 4)  // MOVW 4(R15), R11 // R15 is actual pc + 8
+	o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11
+	o3 := uint32(0xe12fff10 | 11)                   // JMP  (R11)
+	o4 := uint32(0)                                 // WORD $(target-pc) // filled in with relocation
+	ld.SysArch.ByteOrder.PutUint32(tramp.P, o1)
+	ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2)
+	ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3)
+	ld.SysArch.ByteOrder.PutUint32(tramp.P[12:], o4)
+
+	r := ld.Addrel(tramp)
+	r.Off = 12
+	r.Type = obj.R_PCREL
+	r.Siz = 4
+	r.Sym = target
+	r.Add = offset + 4
+}
+
+// generate a trampoline to target+offset in dynlink mode (using GOT)
+func gentrampdyn(tramp, target *ld.Symbol, offset int64) {
+	tramp.Size = 20                                 // 5 instructions
+	o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 8)  // MOVW 8(R15), R11 // R15 is actual pc + 8
+	o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11
+	o3 := uint32(0xe5900000 | 11<<12 | 11<<16)      // MOVW (R11), R11
+	o4 := uint32(0xe12fff10 | 11)                   // JMP  (R11)
+	o5 := uint32(0)                                 // WORD $target at GOT // filled in with relocation
+	o6 := uint32(0)
+	if offset != 0 {
+		// insert an instruction to add offset
+		tramp.Size = 24 // 6 instructions
+		o6 = o5
+		o5 = o4
+		o4 = uint32(0xe2800000 | 11<<12 | 11<<16 | immrot(uint32(offset))) // ADD $offset, R11, R11
+		o1 = uint32(0xe5900000 | 11<<12 | 15<<16 | 12)                     // MOVW 12(R15), R11
+	}
+	tramp.P = make([]byte, tramp.Size)
+	ld.SysArch.ByteOrder.PutUint32(tramp.P, o1)
+	ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2)
+	ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3)
+	ld.SysArch.ByteOrder.PutUint32(tramp.P[12:], o4)
+	ld.SysArch.ByteOrder.PutUint32(tramp.P[16:], o5)
+	if offset != 0 {
+		ld.SysArch.ByteOrder.PutUint32(tramp.P[20:], o6)
+	}
+
+	r := ld.Addrel(tramp)
+	r.Off = 16
+	r.Type = obj.R_GOTPCREL
+	r.Siz = 4
+	r.Sym = target
+	r.Add = 8
+	if offset != 0 {
+		// increase reloc offset by 4 as we inserted an ADD instruction
+		r.Off = 20
+		r.Add = 12
+	}
+}
+
+func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
 	if ld.Linkmode == ld.LinkExternal {
 		switch r.Type {
 		case obj.R_CALLARM:
@@ -419,10 +577,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 			// set up addend for eventual relocation via outer symbol.
 			rs := r.Sym
 
-			r.Xadd = r.Add
-			if r.Xadd&0x800000 != 0 {
-				r.Xadd |= ^0xffffff
-			}
+			r.Xadd = int64(signext24(r.Add & 0xffffff))
 			r.Xadd *= 4
 			for rs.Outer != nil {
 				r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer)
@@ -430,7 +585,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 			}
 
 			if rs.Type != obj.SHOSTOBJ && rs.Type != obj.SDYNIMPORT && rs.Sect == nil {
-				ld.Diag("missing section for %s", rs.Name)
+				ld.Errorf(s, "missing section for %s", rs.Name)
 			}
 			r.Xsym = rs
 
@@ -439,10 +594,14 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 			// the section load address.
 			// we need to compensate that by removing the instruction's address
 			// from addend.
-			if ld.HEADTYPE == obj.Hdarwin {
+			if ld.Headtype == obj.Hdarwin {
 				r.Xadd -= ld.Symaddr(s) + int64(r.Off)
 			}
 
+			if r.Xadd/4 > 0x7fffff || r.Xadd/4 < -0x800000 {
+				ld.Errorf(s, "direct call too far %d", r.Xadd/4)
+			}
+
 			*val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32(r.Xadd/4))))
 			return 0
 		}
@@ -456,30 +615,36 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 		return 0
 
 	case obj.R_GOTOFF:
-		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0))
+		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0))
 		return 0
 
-		// The following three arch specific relocations are only for generation of
+	// The following three arch specific relocations are only for generation of
 	// Linux/ARM ELF's PLT entry (3 assembler instruction)
 	case obj.R_PLT0: // add ip, pc, #0xXX00000
-		if ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got.plt", 0)) < ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0)) {
-			ld.Diag(".got.plt should be placed after .plt section.")
+		if ld.Symaddr(ctxt.Syms.Lookup(".got.plt", 0)) < ld.Symaddr(ctxt.Syms.Lookup(".plt", 0)) {
+			ld.Errorf(s, ".got.plt should be placed after .plt section.")
 		}
-		*val = 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add)) >> 20))
+		*val = 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add)) >> 20))
 		return 0
 
 	case obj.R_PLT1: // add ip, ip, #0xYY000
-		*val = 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add+4)) >> 12))
+		*val = 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add+4)) >> 12))
 
 		return 0
 
 	case obj.R_PLT2: // ldr pc, [ip, #0xZZZ]!
-		*val = 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add+8)))
+		*val = 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ctxt.Syms.Lookup(".plt", 0))+int64(r.Off))+r.Add+8)))
 
 		return 0
 
 	case obj.R_CALLARM: // bl XXXXXX or b YYYYYY
-		*val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32((ld.Symaddr(r.Sym)+int64((uint32(r.Add))*4)-(s.Value+int64(r.Off)))/4))))
+		// r.Add is the instruction
+		// low 24-bit encodes the target address
+		t := (ld.Symaddr(r.Sym) + int64(signext24(r.Add&0xffffff)*4) - (s.Value + int64(r.Off))) / 4
+		if t > 0x7fffff || t < -0x800000 {
+			ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t)
+		}
+		*val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&t)))
 
 		return 0
 	}
@@ -487,27 +652,27 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 	return -1
 }
 
-func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
+func archrelocvariant(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, t int64) int64 {
 	log.Fatalf("unexpected relocation variant")
 	return t
 }
 
-func addpltreloc(ctxt *ld.Link, plt *ld.LSym, got *ld.LSym, sym *ld.LSym, typ int) *ld.Reloc {
+func addpltreloc(ctxt *ld.Link, plt *ld.Symbol, got *ld.Symbol, sym *ld.Symbol, typ obj.RelocType) *ld.Reloc {
 	r := ld.Addrel(plt)
 	r.Sym = got
 	r.Off = int32(plt.Size)
 	r.Siz = 4
-	r.Type = int32(typ)
+	r.Type = typ
 	r.Add = int64(sym.Got) - 8
 
 	plt.Attr |= ld.AttrReachable
 	plt.Size += 4
-	ld.Symgrow(ctxt, plt, plt.Size)
+	ld.Symgrow(plt, plt.Size)
 
 	return r
 }
 
-func addpltsym(ctxt *ld.Link, s *ld.LSym) {
+func addpltsym(ctxt *ld.Link, s *ld.Symbol) {
 	if s.Plt >= 0 {
 		return
 	}
@@ -515,11 +680,11 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 	ld.Adddynsym(ctxt, s)
 
 	if ld.Iself {
-		plt := ld.Linklookup(ctxt, ".plt", 0)
-		got := ld.Linklookup(ctxt, ".got.plt", 0)
-		rel := ld.Linklookup(ctxt, ".rel.plt", 0)
+		plt := ctxt.Syms.Lookup(".plt", 0)
+		got := ctxt.Syms.Lookup(".got.plt", 0)
+		rel := ctxt.Syms.Lookup(".rel.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt()
+			elfsetupplt(ctxt)
 		}
 
 		// .got entry
@@ -542,50 +707,49 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 
 		ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_ARM_JUMP_SLOT))
 	} else {
-		ld.Diag("addpltsym: unsupported binary format")
+		ld.Errorf(s, "addpltsym: unsupported binary format")
 	}
 }
 
-func addgotsyminternal(ctxt *ld.Link, s *ld.LSym) {
+func addgotsyminternal(ctxt *ld.Link, s *ld.Symbol) {
 	if s.Got >= 0 {
 		return
 	}
 
-	got := ld.Linklookup(ctxt, ".got", 0)
+	got := ctxt.Syms.Lookup(".got", 0)
 	s.Got = int32(got.Size)
 
 	ld.Addaddrplus(ctxt, got, s, 0)
 
 	if ld.Iself {
 	} else {
-		ld.Diag("addgotsyminternal: unsupported binary format")
+		ld.Errorf(s, "addgotsyminternal: unsupported binary format")
 	}
 }
 
-func addgotsym(ctxt *ld.Link, s *ld.LSym) {
+func addgotsym(ctxt *ld.Link, s *ld.Symbol) {
 	if s.Got >= 0 {
 		return
 	}
 
 	ld.Adddynsym(ctxt, s)
-	got := ld.Linklookup(ctxt, ".got", 0)
+	got := ctxt.Syms.Lookup(".got", 0)
 	s.Got = int32(got.Size)
 	ld.Adduint32(ctxt, got, 0)
 
 	if ld.Iself {
-		rel := ld.Linklookup(ctxt, ".rel", 0)
+		rel := ctxt.Syms.Lookup(".rel", 0)
 		ld.Addaddrplus(ctxt, rel, got, int64(s.Got))
 		ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_ARM_GLOB_DAT))
 	} else {
-		ld.Diag("addgotsym: unsupported binary format")
+		ld.Errorf(s, "addgotsym: unsupported binary format")
 	}
 }
 
-func asmb() {
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
+func asmb(ctxt *ld.Link) {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f asmb\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	if ld.Iself {
 		ld.Asmbelfsetup()
@@ -593,36 +757,40 @@ func asmb() {
 
 	sect := ld.Segtext.Sect
 	ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-	ld.Codeblk(int64(sect.Vaddr), int64(sect.Length))
+	ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
 	for sect = sect.Next; sect != nil; sect = sect.Next {
 		ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-		ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
+		ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
 	}
 
 	if ld.Segrodata.Filelen > 0 {
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f rodatblk\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-
 		ld.Cseek(int64(ld.Segrodata.Fileoff))
-		ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+		ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+	}
+	if ld.Segrelrodata.Filelen > 0 {
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f relrodatblk\n", obj.Cputime())
+		}
+		ld.Cseek(int64(ld.Segrelrodata.Fileoff))
+		ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
 	}
 
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f datblk\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	ld.Cseek(int64(ld.Segdata.Fileoff))
-	ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+	ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
 
 	ld.Cseek(int64(ld.Segdwarf.Fileoff))
-	ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+	ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
 
 	machlink := uint32(0)
-	if ld.HEADTYPE == obj.Hdarwin {
-		machlink = uint32(ld.Domacholink())
+	if ld.Headtype == obj.Hdarwin {
+		machlink = uint32(ld.Domacholink(ctxt))
 	}
 
 	/* output symbol table */
@@ -630,47 +798,46 @@ func asmb() {
 
 	ld.Lcsize = 0
 	symo := uint32(0)
-	if ld.Debug['s'] == 0 {
+	if !*ld.FlagS {
 		// TODO: rationalize
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f sym\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
 				symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
-				symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
+				symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
 			}
 
 		case obj.Hplan9:
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink))
 		}
 
 		ld.Cseek(int64(symo))
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
-				if ld.Debug['v'] != 0 {
-					fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+				if ctxt.Debugvlog != 0 {
+					ctxt.Logf("%5.2f elfsym\n", obj.Cputime())
 				}
-				ld.Asmelfsym()
+				ld.Asmelfsym(ctxt)
 				ld.Cflush()
 				ld.Cwrite(ld.Elfstrdat)
 
 				if ld.Linkmode == ld.LinkExternal {
-					ld.Elfemitreloc()
+					ld.Elfemitreloc(ctxt)
 				}
 			}
 
 		case obj.Hplan9:
-			ld.Asmplan9sym()
+			ld.Asmplan9sym(ctxt)
 			ld.Cflush()
 
-			sym := ld.Linklookup(ld.Ctxt, "pclntab", 0)
+			sym := ctxt.Syms.Lookup("pclntab", 0)
 			if sym != nil {
 				ld.Lcsize = int32(len(sym.P))
 				for i := 0; int32(i) < ld.Lcsize; i++ {
@@ -682,26 +849,24 @@ func asmb() {
 
 		case obj.Hdarwin:
 			if ld.Linkmode == ld.LinkExternal {
-				ld.Machoemitreloc()
+				ld.Machoemitreloc(ctxt)
 			}
 		}
 	}
 
-	ld.Ctxt.Cursym = nil
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f header\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 	ld.Cseek(0)
-	switch ld.HEADTYPE {
+	switch ld.Headtype {
 	default:
 	case obj.Hplan9: /* plan 9 */
 		ld.Lputb(0x647)                      /* magic */
 		ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */
 		ld.Lputb(uint32(ld.Segdata.Filelen))
 		ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
-		ld.Lputb(uint32(ld.Symsize))      /* nsyms */
-		ld.Lputb(uint32(ld.Entryvalue())) /* va of entry */
+		ld.Lputb(uint32(ld.Symsize))          /* nsyms */
+		ld.Lputb(uint32(ld.Entryvalue(ctxt))) /* va of entry */
 		ld.Lputb(0)
 		ld.Lputb(uint32(ld.Lcsize))
 
@@ -710,14 +875,14 @@ func asmb() {
 		obj.Hnetbsd,
 		obj.Hopenbsd,
 		obj.Hnacl:
-		ld.Asmbelf(int64(symo))
+		ld.Asmbelf(ctxt, int64(symo))
 
 	case obj.Hdarwin:
-		ld.Asmbmacho()
+		ld.Asmbmacho(ctxt)
 	}
 
 	ld.Cflush()
-	if ld.Debug['c'] != 0 {
+	if *ld.FlagC {
 		fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
 		fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
 		fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
diff --git a/src/cmd/link/internal/arm/l.go b/src/cmd/link/internal/arm/l.go
index 63b1165..a83d26b 100644
--- a/src/cmd/link/internal/arm/l.go
+++ b/src/cmd/link/internal/arm/l.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -33,7 +33,7 @@ package arm
 // Writing object files.
 
 // Inferno utils/5l/l.h
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/l.h
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -63,13 +63,13 @@ package arm
 // THE SOFTWARE.
 
 const (
-	MaxAlign  = 8 // max data alignment
-	MinAlign  = 1 // min data alignment
-	FuncAlign = 4 // single-instruction alignment
+	maxAlign  = 8 // max data alignment
+	minAlign  = 1 // min data alignment
+	funcAlign = 4 // single-instruction alignment
 )
 
 /* Used by ../internal/ld/dwarf.go */
 const (
-	DWARFREGSP = 13
-	DWARFREGLR = 14
+	dwarfRegSP = 13
+	dwarfRegLR = 14
 )
diff --git a/src/cmd/link/internal/arm/obj.go b/src/cmd/link/internal/arm/obj.go
index 9ea9771..4feaa09 100644
--- a/src/cmd/link/internal/arm/obj.go
+++ b/src/cmd/link/internal/arm/obj.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -35,29 +35,22 @@ import (
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
 	"fmt"
-	"log"
 )
 
-// Reading object files.
-
-func Main() {
-	linkarchinit()
-	ld.Ldmain()
-}
-
-func linkarchinit() {
+func Init() {
 	ld.SysArch = sys.ArchARM
 
-	ld.Thearch.Funcalign = FuncAlign
-	ld.Thearch.Maxalign = MaxAlign
-	ld.Thearch.Minalign = MinAlign
-	ld.Thearch.Dwarfregsp = DWARFREGSP
-	ld.Thearch.Dwarfreglr = DWARFREGLR
+	ld.Thearch.Funcalign = funcAlign
+	ld.Thearch.Maxalign = maxAlign
+	ld.Thearch.Minalign = minAlign
+	ld.Thearch.Dwarfregsp = dwarfRegSP
+	ld.Thearch.Dwarfreglr = dwarfRegLR
 
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Archinit = archinit
 	ld.Thearch.Archreloc = archreloc
 	ld.Thearch.Archrelocvariant = archrelocvariant
+	ld.Thearch.Trampoline = trampoline
 	ld.Thearch.Asmb = asmb
 	ld.Thearch.Elfreloc1 = elfreloc1
 	ld.Thearch.Elfsetupplt = elfsetupplt
@@ -78,98 +71,72 @@ func linkarchinit() {
 	ld.Thearch.Solarisdynld = "XXX"
 }
 
-func archinit() {
-	// getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
-	// Go was built; see ../../make.bash.
-	if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" {
-		ld.Linkmode = ld.LinkInternal
-	}
-
-	if ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ld.DynlinkingGo() {
-		ld.Linkmode = ld.LinkExternal
-	}
-
-	switch ld.HEADTYPE {
-	default:
-		if ld.Linkmode == ld.LinkAuto {
-			ld.Linkmode = ld.LinkInternal
-		}
-		if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" {
-			log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE)))
-		}
-
-	case obj.Hlinux,
-		obj.Hfreebsd,
-		obj.Hnacl,
-		obj.Hdarwin:
-		break
-	}
-
-	switch ld.HEADTYPE {
+func archinit(ctxt *ld.Link) {
+	switch ld.Headtype {
 	default:
-		ld.Exitf("unknown -H option: %v", ld.HEADTYPE)
+		ld.Exitf("unknown -H option: %v", ld.Headtype)
 
 	case obj.Hplan9: /* plan 9 */
 		ld.HEADR = 32
 
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 4128
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 4128
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
 
 	case obj.Hlinux, /* arm elf */
 		obj.Hfreebsd,
 		obj.Hnetbsd,
 		obj.Hopenbsd:
-		ld.Debug['d'] = 0
+		*ld.FlagD = false
 		// with dynamic linking
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 		ld.HEADR = ld.ELFRESERVE
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x10000 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
 
 	case obj.Hnacl:
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 		ld.HEADR = 0x10000
 		ld.Funcalign = 16
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x20000
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x20000
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x10000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
 		}
 
 	case obj.Hdarwin: /* apple MACH */
-		ld.Debug['w'] = 1 // disable DWARF generation
+		*ld.FlagW = true // disable DWARF generation
 		ld.Machoinit()
 		ld.HEADR = ld.INITIAL_MACHO_HEADR
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 4096 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 4096 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
 	}
 
-	if ld.INITDAT != 0 && ld.INITRND != 0 {
-		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND))
+	if *ld.FlagDataAddr != 0 && *ld.FlagRound != 0 {
+		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(*ld.FlagDataAddr), uint32(*ld.FlagRound))
 	}
 }
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
index 7832e91..118a575 100644
--- a/src/cmd/link/internal/arm64/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -38,23 +38,23 @@ import (
 	"log"
 )
 
-func gentext() {
-	if !ld.DynlinkingGo() {
+func gentext(ctxt *ld.Link) {
+	if !ctxt.DynlinkingGo() {
 		return
 	}
-	addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
+	addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0)
 	if addmoduledata.Type == obj.STEXT {
 		// we're linking a module containing the runtime -> no need for
 		// an init function
 		return
 	}
 	addmoduledata.Attr |= ld.AttrReachable
-	initfunc := ld.Linklookup(ld.Ctxt, "go.link.addmoduledata", 0)
+	initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
 	initfunc.Type = obj.STEXT
 	initfunc.Attr |= ld.AttrLocal
 	initfunc.Attr |= ld.AttrReachable
 	o := func(op uint32) {
-		ld.Adduint32(ld.Ctxt, initfunc, op)
+		ld.Adduint32(ctxt, initfunc, op)
 	}
 	// 0000000000000000 <local.dso_init>:
 	// 0:	90000000 	adrp	x0, 0 <runtime.firstmoduledata>
@@ -66,7 +66,7 @@ func gentext() {
 	rel := ld.Addrel(initfunc)
 	rel.Off = 0
 	rel.Siz = 8
-	rel.Sym = ld.Ctxt.Moduledata
+	rel.Sym = ctxt.Moduledata
 	rel.Type = obj.R_ADDRARM64
 
 	// 8:	14000000 	bl	0 <runtime.addmoduledata>
@@ -75,22 +75,23 @@ func gentext() {
 	rel = ld.Addrel(initfunc)
 	rel.Off = 8
 	rel.Siz = 4
-	rel.Sym = ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
+	rel.Sym = ctxt.Syms.Lookup("runtime.addmoduledata", 0)
 	rel.Type = obj.R_CALLARM64 // Really should be R_AARCH64_JUMP26 but doesn't seem to make any difference
 
-	ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
-	initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
+	ctxt.Textp = append(ctxt.Textp, initfunc)
+	initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
 	initarray_entry.Attr |= ld.AttrReachable
 	initarray_entry.Attr |= ld.AttrLocal
 	initarray_entry.Type = obj.SINITARR
-	ld.Addaddr(ld.Ctxt, initarray_entry, initfunc)
+	ld.Addaddr(ctxt, initarray_entry, initfunc)
 }
 
-func adddynrel(s *ld.LSym, r *ld.Reloc) {
+func adddynrel(ctxt *ld.Link, s *ld.Symbol, r *ld.Reloc) bool {
 	log.Fatalf("adddynrel not implemented")
+	return false
 }
 
-func elfreloc1(r *ld.Reloc, sectoff int64) int {
+func elfreloc1(ctxt *ld.Link, r *ld.Reloc, sectoff int64) int {
 	ld.Thearch.Vput(uint64(sectoff))
 
 	elfsym := r.Xsym.ElfsymForReloc()
@@ -142,12 +143,12 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func elfsetupplt() {
+func elfsetupplt(ctxt *ld.Link) {
 	// TODO(aram)
 	return
 }
 
-func machoreloc1(r *ld.Reloc, sectoff int64) int {
+func machoreloc1(s *ld.Symbol, r *ld.Reloc, sectoff int64) int {
 	var v uint32
 
 	rs := r.Xsym
@@ -157,7 +158,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 	// UNSIGNED relocation at all.
 	if rs.Type == obj.SHOSTOBJ || r.Type == obj.R_CALLARM64 || r.Type == obj.R_ADDRARM64 || r.Type == obj.R_ADDR {
 		if rs.Dynid < 0 {
-			ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type)
+			ld.Errorf(s, "reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type)
 			return -1
 		}
 
@@ -166,7 +167,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 	} else {
 		v = uint32(rs.Sect.Extnum)
 		if v == 0 {
-			ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
+			ld.Errorf(s, "reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
 			return -1
 		}
 	}
@@ -180,7 +181,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 
 	case obj.R_CALLARM64:
 		if r.Xadd != 0 {
-			ld.Diag("ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", rs.Name, r.Xadd)
+			ld.Errorf(s, "ld64 doesn't allow BR26 reloc with non-zero addend: %s+%d", rs.Name, r.Xadd)
 		}
 
 		v |= 1 << 24 // pc-relative bit
@@ -226,7 +227,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
+func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
 	if ld.Linkmode == ld.LinkExternal {
 		switch r.Type {
 		default:
@@ -234,7 +235,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 
 		case obj.R_ARM64_GOTPCREL:
 			var o1, o2 uint32
-			if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+			if ctxt.Arch.ByteOrder == binary.BigEndian {
 				o1 = uint32(*val >> 32)
 				o2 = uint32(*val)
 			} else {
@@ -249,14 +250,14 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 			// (https://sourceware.org/bugzilla/show_bug.cgi?id=18270). So
 			// we convert the adrp; ld64 + R_ARM64_GOTPCREL into adrp;
 			// add + R_ADDRARM64.
-			if !(r.Sym.Version != 0 || (r.Sym.Type&obj.SHIDDEN != 0) || r.Sym.Attr.Local()) && r.Sym.Type == obj.STEXT && ld.DynlinkingGo() {
+			if !(r.Sym.Version != 0 || (r.Sym.Type&obj.SHIDDEN != 0) || r.Sym.Attr.Local()) && r.Sym.Type == obj.STEXT && ctxt.DynlinkingGo() {
 				if o2&0xffc00000 != 0xf9400000 {
-					ld.Ctxt.Diag("R_ARM64_GOTPCREL against unexpected instruction %x", o2)
+					ld.Errorf(s, "R_ARM64_GOTPCREL against unexpected instruction %x", o2)
 				}
 				o2 = 0x91000000 | (o2 & 0x000003ff)
 				r.Type = obj.R_ADDRARM64
 			}
-			if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+			if ctxt.Arch.ByteOrder == binary.BigEndian {
 				*val = int64(o1)<<32 | int64(o2)
 			} else {
 				*val = int64(o2)<<32 | int64(o1)
@@ -275,7 +276,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 			}
 
 			if rs.Type != obj.SHOSTOBJ && rs.Type != obj.SDYNIMPORT && rs.Sect == nil {
-				ld.Diag("missing section for %s", rs.Name)
+				ld.Errorf(s, "missing section for %s", rs.Name)
 			}
 			r.Xsym = rs
 
@@ -284,10 +285,10 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 			// the BR26 relocation should be fully resolved at link time.
 			// That is the reason why the next if block is disabled. When the bug in ld64
 			// is fixed, we can enable this block and also enable duff's device in cmd/7g.
-			if false && ld.HEADTYPE == obj.Hdarwin {
+			if false && ld.Headtype == obj.Hdarwin {
 				var o0, o1 uint32
 
-				if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+				if ctxt.Arch.ByteOrder == binary.BigEndian {
 					o0 = uint32(*val >> 32)
 					o1 = uint32(*val)
 				} else {
@@ -304,7 +305,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 				r.Xadd = 0
 
 				// when laid out, the instruction order must always be o1, o2.
-				if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+				if ctxt.Arch.ByteOrder == binary.BigEndian {
 					*val = int64(o0)<<32 | int64(o1)
 				} else {
 					*val = int64(o1)<<32 | int64(o0)
@@ -329,18 +330,18 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 		return 0
 
 	case obj.R_GOTOFF:
-		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0))
+		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0))
 		return 0
 
 	case obj.R_ADDRARM64:
 		t := ld.Symaddr(r.Sym) + r.Add - ((s.Value + int64(r.Off)) &^ 0xfff)
 		if t >= 1<<32 || t < -1<<32 {
-			ld.Diag("program too large, address relocation distance = %d", t)
+			ld.Errorf(s, "program too large, address relocation distance = %d", t)
 		}
 
 		var o0, o1 uint32
 
-		if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+		if ctxt.Arch.ByteOrder == binary.BigEndian {
 			o0 = uint32(*val >> 32)
 			o1 = uint32(*val)
 		} else {
@@ -352,7 +353,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 		o1 |= uint32(t&0xfff) << 10
 
 		// when laid out, the instruction order must always be o1, o2.
-		if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+		if ctxt.Arch.ByteOrder == binary.BigEndian {
 			*val = int64(o0)<<32 | int64(o1)
 		} else {
 			*val = int64(o1)<<32 | int64(o0)
@@ -361,14 +362,14 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 
 	case obj.R_ARM64_TLS_LE:
 		r.Done = 0
-		if ld.HEADTYPE != obj.Hlinux {
-			ld.Diag("TLS reloc on unsupported OS %s", ld.Headstr(int(ld.HEADTYPE)))
+		if ld.Headtype != obj.Hlinux {
+			ld.Errorf(s, "TLS reloc on unsupported OS %v", ld.Headtype)
 		}
 		// The TCB is two pointers. This is not documented anywhere, but is
 		// de facto part of the ABI.
 		v := r.Sym.Value + int64(2*ld.SysArch.PtrSize)
 		if v < 0 || v >= 32678 {
-			ld.Diag("TLS offset out of range %d", v)
+			ld.Errorf(s, "TLS offset out of range %d", v)
 		}
 		*val |= v << 5
 		return 0
@@ -376,7 +377,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 	case obj.R_CALLARM64:
 		t := (ld.Symaddr(r.Sym) + r.Add) - (s.Value + int64(r.Off))
 		if t >= 1<<27 || t < -1<<27 {
-			ld.Diag("program too large, call relocation distance = %d", t)
+			ld.Errorf(s, "program too large, call relocation distance = %d", t)
 		}
 		*val |= (t >> 2) & 0x03ffffff
 		return 0
@@ -385,16 +386,15 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 	return -1
 }
 
-func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
+func archrelocvariant(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, t int64) int64 {
 	log.Fatalf("unexpected relocation variant")
 	return -1
 }
 
-func asmb() {
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
+func asmb(ctxt *ld.Link) {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f asmb\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	if ld.Iself {
 		ld.Asmbelfsetup()
@@ -402,36 +402,40 @@ func asmb() {
 
 	sect := ld.Segtext.Sect
 	ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-	ld.Codeblk(int64(sect.Vaddr), int64(sect.Length))
+	ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
 	for sect = sect.Next; sect != nil; sect = sect.Next {
 		ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-		ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
+		ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
 	}
 
 	if ld.Segrodata.Filelen > 0 {
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f rodatblk\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-
 		ld.Cseek(int64(ld.Segrodata.Fileoff))
-		ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+		ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+	}
+	if ld.Segrelrodata.Filelen > 0 {
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f relrodatblk\n", obj.Cputime())
+		}
+		ld.Cseek(int64(ld.Segrelrodata.Fileoff))
+		ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
 	}
 
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f datblk\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	ld.Cseek(int64(ld.Segdata.Fileoff))
-	ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+	ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
 
 	ld.Cseek(int64(ld.Segdwarf.Fileoff))
-	ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+	ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
 
 	machlink := uint32(0)
-	if ld.HEADTYPE == obj.Hdarwin {
-		machlink = uint32(ld.Domacholink())
+	if ld.Headtype == obj.Hdarwin {
+		machlink = uint32(ld.Domacholink(ctxt))
 	}
 
 	/* output symbol table */
@@ -439,47 +443,46 @@ func asmb() {
 
 	ld.Lcsize = 0
 	symo := uint32(0)
-	if ld.Debug['s'] == 0 {
+	if !*ld.FlagS {
 		// TODO: rationalize
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f sym\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
 				symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
-				symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
+				symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
 			}
 
 		case obj.Hplan9:
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink))
 		}
 
 		ld.Cseek(int64(symo))
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
-				if ld.Debug['v'] != 0 {
-					fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+				if ctxt.Debugvlog != 0 {
+					ctxt.Logf("%5.2f elfsym\n", obj.Cputime())
 				}
-				ld.Asmelfsym()
+				ld.Asmelfsym(ctxt)
 				ld.Cflush()
 				ld.Cwrite(ld.Elfstrdat)
 
 				if ld.Linkmode == ld.LinkExternal {
-					ld.Elfemitreloc()
+					ld.Elfemitreloc(ctxt)
 				}
 			}
 
 		case obj.Hplan9:
-			ld.Asmplan9sym()
+			ld.Asmplan9sym(ctxt)
 			ld.Cflush()
 
-			sym := ld.Linklookup(ld.Ctxt, "pclntab", 0)
+			sym := ctxt.Syms.Lookup("pclntab", 0)
 			if sym != nil {
 				ld.Lcsize = int32(len(sym.P))
 				for i := 0; int32(i) < ld.Lcsize; i++ {
@@ -491,26 +494,24 @@ func asmb() {
 
 		case obj.Hdarwin:
 			if ld.Linkmode == ld.LinkExternal {
-				ld.Machoemitreloc()
+				ld.Machoemitreloc(ctxt)
 			}
 		}
 	}
 
-	ld.Ctxt.Cursym = nil
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f header\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 	ld.Cseek(0)
-	switch ld.HEADTYPE {
+	switch ld.Headtype {
 	default:
 	case obj.Hplan9: /* plan 9 */
 		ld.Thearch.Lput(0x647)                      /* magic */
 		ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */
 		ld.Thearch.Lput(uint32(ld.Segdata.Filelen))
 		ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
-		ld.Thearch.Lput(uint32(ld.Symsize))      /* nsyms */
-		ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */
+		ld.Thearch.Lput(uint32(ld.Symsize))          /* nsyms */
+		ld.Thearch.Lput(uint32(ld.Entryvalue(ctxt))) /* va of entry */
 		ld.Thearch.Lput(0)
 		ld.Thearch.Lput(uint32(ld.Lcsize))
 
@@ -519,14 +520,14 @@ func asmb() {
 		obj.Hnetbsd,
 		obj.Hopenbsd,
 		obj.Hnacl:
-		ld.Asmbelf(int64(symo))
+		ld.Asmbelf(ctxt, int64(symo))
 
 	case obj.Hdarwin:
-		ld.Asmbmacho()
+		ld.Asmbmacho(ctxt)
 	}
 
 	ld.Cflush()
-	if ld.Debug['c'] != 0 {
+	if *ld.FlagC {
 		fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
 		fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
 		fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
diff --git a/src/cmd/link/internal/arm64/l.go b/src/cmd/link/internal/arm64/l.go
index cbee2a3..50b88e4 100644
--- a/src/cmd/link/internal/arm64/l.go
+++ b/src/cmd/link/internal/arm64/l.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -62,13 +62,13 @@ package arm64
 // THE SOFTWARE.
 
 const (
-	MaxAlign  = 32 // max data alignment
-	MinAlign  = 1  // min data alignment
-	FuncAlign = 8
+	maxAlign  = 32 // max data alignment
+	minAlign  = 1  // min data alignment
+	funcAlign = 8
 )
 
 /* Used by ../internal/ld/dwarf.go */
 const (
-	DWARFREGSP = 31
-	DWARFREGLR = 30
+	dwarfRegSP = 31
+	dwarfRegLR = 30
 )
diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go
index 86f9ff7..7d49163 100644
--- a/src/cmd/link/internal/arm64/obj.go
+++ b/src/cmd/link/internal/arm64/obj.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -35,24 +35,16 @@ import (
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
 	"fmt"
-	"log"
 )
 
-// Reading object files.
-
-func Main() {
-	linkarchinit()
-	ld.Ldmain()
-}
-
-func linkarchinit() {
+func Init() {
 	ld.SysArch = sys.ArchARM64
 
-	ld.Thearch.Funcalign = FuncAlign
-	ld.Thearch.Maxalign = MaxAlign
-	ld.Thearch.Minalign = MinAlign
-	ld.Thearch.Dwarfregsp = DWARFREGSP
-	ld.Thearch.Dwarfreglr = DWARFREGLR
+	ld.Thearch.Funcalign = funcAlign
+	ld.Thearch.Maxalign = maxAlign
+	ld.Thearch.Minalign = minAlign
+	ld.Thearch.Dwarfregsp = dwarfRegSP
+	ld.Thearch.Dwarfreglr = dwarfRegLR
 
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Archinit = archinit
@@ -79,94 +71,67 @@ func linkarchinit() {
 	ld.Thearch.Solarisdynld = "XXX"
 }
 
-func archinit() {
-	// getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
-	// Go was built; see ../../make.bash.
-	if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" {
-		ld.Linkmode = ld.LinkInternal
-	}
-
-	// Darwin/arm64 only supports external linking
-	if ld.HEADTYPE == obj.Hdarwin {
-		ld.Linkmode = ld.LinkExternal
-	}
-
-	switch ld.HEADTYPE {
-	default:
-		if ld.Linkmode == ld.LinkAuto {
-			ld.Linkmode = ld.LinkInternal
-		}
-		if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" {
-			log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE)))
-		}
-	case obj.Hlinux, obj.Hdarwin:
-		break
-	}
-
-	if ld.Buildmode == ld.BuildmodeCShared || ld.DynlinkingGo() {
-		ld.Linkmode = ld.LinkExternal
-	}
-
-	switch ld.HEADTYPE {
+func archinit(ctxt *ld.Link) {
+	switch ld.Headtype {
 	default:
-		ld.Exitf("unknown -H option: %v", ld.HEADTYPE)
+		ld.Exitf("unknown -H option: %v", ld.Headtype)
 
 	case obj.Hplan9: /* plan 9 */
 		ld.HEADR = 32
 
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 4128
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 4096 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
 
 	case obj.Hlinux: /* arm64 elf */
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 		ld.HEADR = ld.ELFRESERVE
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x10000 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x10000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
 		}
 
 	case obj.Hdarwin: /* apple MACH */
-		ld.Debug['w'] = 1 // disable DWARF generation
+		*ld.FlagW = true // disable DWARF generation
 		ld.Machoinit()
 		ld.HEADR = ld.INITIAL_MACHO_HEADR
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 4096 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 4096 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
 
 	case obj.Hnacl:
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 		ld.HEADR = 0x10000
 		ld.Funcalign = 16
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x20000
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x20000
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x10000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
 		}
 	}
 
-	if ld.INITDAT != 0 && ld.INITRND != 0 {
-		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND))
+	if *ld.FlagDataAddr != 0 && *ld.FlagRound != 0 {
+		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(*ld.FlagDataAddr), uint32(*ld.FlagRound))
 	}
 }
diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
index 80c33ce..6db672f 100644
--- a/src/cmd/link/internal/ld/ar.go
+++ b/src/cmd/link/internal/ld/ar.go
@@ -1,5 +1,5 @@
 // Inferno utils/include/ar.h
-// http://code.google.com/p/inferno-os/source/browse/utils/include/ar.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/include/ar.h
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -63,13 +63,13 @@ type ArHdr struct {
 // file, but it has an armap listing symbols and the objects that
 // define them. This is used for the compiler support library
 // libgcc.a.
-func hostArchive(name string) {
+func hostArchive(ctxt *Link, name string) {
 	f, err := bio.Open(name)
 	if err != nil {
 		if os.IsNotExist(err) {
 			// It's OK if we don't have a libgcc file at all.
-			if Debug['v'] != 0 {
-				fmt.Fprintf(Bso, "skipping libgcc file: %v\n", err)
+			if ctxt.Debugvlog != 0 {
+				ctxt.Logf("skipping libgcc file: %v\n", err)
 			}
 			return
 		}
@@ -99,7 +99,7 @@ func hostArchive(name string) {
 	any := true
 	for any {
 		var load []uint64
-		for _, s := range Ctxt.Allsym {
+		for _, s := range ctxt.Syms.Allsym {
 			for _, r := range s.R {
 				if r.Sym != nil && r.Sym.Type&obj.SMASK == obj.SXREF {
 					if off := armap[r.Sym.Name]; off != 0 && !loaded[off] {
@@ -118,9 +118,10 @@ func hostArchive(name string) {
 			pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
 			l = atolwhex(arhdr.size)
 
-			h := ldobj(f, "libgcc", l, pname, name, ArchiveObj)
+			libgcc := Library{Pkg: "libgcc"}
+			h := ldobj(ctxt, f, &libgcc, l, pname, name, ArchiveObj)
 			f.Seek(h.off, 0)
-			h.ld(f, h.pkg, h.length, h.pn)
+			h.ld(ctxt, f, h.pkg, h.length, h.pn)
 		}
 
 		any = len(load) > 0
@@ -165,7 +166,7 @@ func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap {
 
 		// For Mach-O and PE/386 files we strip a leading
 		// underscore from the symbol name.
-		if goos == "darwin" || (goos == "windows" && goarch == "386") {
+		if obj.GOOS == "darwin" || (obj.GOOS == "windows" && obj.GOARCH == "386") {
 			if name[0] == '_' && len(name) > 1 {
 				name = name[1:]
 			}
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
new file mode 100644
index 0000000..c9ee884
--- /dev/null
+++ b/src/cmd/link/internal/ld/config.go
@@ -0,0 +1,250 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+	"cmd/internal/obj"
+	"cmd/internal/sys"
+	"fmt"
+	"log"
+)
+
+var (
+	Linkmode  LinkMode
+	Buildmode BuildMode
+)
+
+// A BuildMode indicates the sort of object we are building.
+//
+// Possible build modes are the same as those for the -buildmode flag
+// in cmd/go, and are documented in 'go help buildmode'.
+type BuildMode uint8
+
+const (
+	BuildmodeUnset BuildMode = iota
+	BuildmodeExe
+	BuildmodePIE
+	BuildmodeCArchive
+	BuildmodeCShared
+	BuildmodeShared
+	BuildmodePlugin
+)
+
+func (mode *BuildMode) Set(s string) error {
+	badmode := func() error {
+		return fmt.Errorf("buildmode %s not supported on %s/%s", s, obj.GOOS, obj.GOARCH)
+	}
+	switch s {
+	default:
+		return fmt.Errorf("invalid buildmode: %q", s)
+	case "exe":
+		*mode = BuildmodeExe
+	case "pie":
+		switch obj.GOOS {
+		case "android", "linux":
+		default:
+			return badmode()
+		}
+		*mode = BuildmodePIE
+	case "c-archive":
+		switch obj.GOOS {
+		case "darwin", "linux":
+		case "windows":
+			switch obj.GOARCH {
+			case "amd64", "386":
+			default:
+				return badmode()
+			}
+		default:
+			return badmode()
+		}
+		*mode = BuildmodeCArchive
+	case "c-shared":
+		switch obj.GOARCH {
+		case "386", "amd64", "arm", "arm64":
+		default:
+			return badmode()
+		}
+		*mode = BuildmodeCShared
+	case "shared":
+		switch obj.GOOS {
+		case "linux":
+			switch obj.GOARCH {
+			case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
+			default:
+				return badmode()
+			}
+		default:
+			return badmode()
+		}
+		*mode = BuildmodeShared
+	case "plugin":
+		switch obj.GOOS {
+		case "linux":
+			switch obj.GOARCH {
+			case "386", "amd64", "arm", "arm64":
+			default:
+				return badmode()
+			}
+		case "darwin":
+			switch obj.GOARCH {
+			case "amd64":
+			default:
+				return badmode()
+			}
+		default:
+			return badmode()
+		}
+		*mode = BuildmodePlugin
+	}
+	return nil
+}
+
+func (mode *BuildMode) String() string {
+	switch *mode {
+	case BuildmodeUnset:
+		return "" // avoid showing a default in usage message
+	case BuildmodeExe:
+		return "exe"
+	case BuildmodePIE:
+		return "pie"
+	case BuildmodeCArchive:
+		return "c-archive"
+	case BuildmodeCShared:
+		return "c-shared"
+	case BuildmodeShared:
+		return "shared"
+	case BuildmodePlugin:
+		return "plugin"
+	}
+	return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
+}
+
+// LinkMode indicates whether an external linker is used for the final link.
+type LinkMode uint8
+
+const (
+	LinkAuto LinkMode = iota
+	LinkInternal
+	LinkExternal
+)
+
+func (mode *LinkMode) Set(s string) error {
+	switch s {
+	default:
+		return fmt.Errorf("invalid linkmode: %q", s)
+	case "auto":
+		*mode = LinkAuto
+	case "internal":
+		*mode = LinkInternal
+	case "external":
+		*mode = LinkExternal
+	}
+	return nil
+}
+
+func (mode *LinkMode) String() string {
+	switch *mode {
+	case LinkAuto:
+		return "auto"
+	case LinkInternal:
+		return "internal"
+	case LinkExternal:
+		return "external"
+	}
+	return fmt.Sprintf("LinkMode(%d)", uint8(*mode))
+}
+
+// mustLinkExternal reports whether the program being linked requires
+// the external linker be used to complete the link.
+func mustLinkExternal(ctxt *Link) (res bool, reason string) {
+	if ctxt.Debugvlog > 1 {
+		defer func() {
+			if res {
+				log.Printf("external linking is forced by: %s\n", reason)
+			}
+		}()
+	}
+
+	switch obj.GOOS {
+	case "android":
+		return true, "android"
+	case "darwin":
+		if SysArch.InFamily(sys.ARM, sys.ARM64) {
+			return true, "iOS"
+		}
+	}
+
+	if *flagMsan {
+		return true, "msan"
+	}
+
+	// Internally linking cgo is incomplete on some architectures.
+	// https://golang.org/issue/10373
+	// https://golang.org/issue/14449
+	if iscgo && SysArch.InFamily(sys.ARM64, sys.MIPS64) {
+		return true, obj.GOARCH + " does not support internal cgo"
+	}
+
+	// Some build modes require work the internal linker cannot do (yet).
+	switch Buildmode {
+	case BuildmodeCArchive:
+		return true, "buildmode=c-archive"
+	case BuildmodeCShared:
+		return true, "buildmode=c-shared"
+	case BuildmodePIE:
+		switch obj.GOOS + "/" + obj.GOARCH {
+		case "linux/amd64":
+		default:
+			// Internal linking does not support TLS_IE.
+			return true, "buildmode=pie"
+		}
+	case BuildmodePlugin:
+		return true, "buildmode=plugin"
+	case BuildmodeShared:
+		return true, "buildmode=shared"
+	}
+	if *FlagLinkshared {
+		return true, "dynamically linking with a shared library"
+	}
+
+	return false, ""
+}
+
+// determineLinkMode sets Linkmode.
+//
+// It is called after flags are processed and inputs are processed,
+// so the Linkmode variable has an initial value from the -linkmode
+// flag and the iscgo externalobj variables are set.
+func determineLinkMode(ctxt *Link) {
+	switch Linkmode {
+	case LinkAuto:
+		// The environment variable GO_EXTLINK_ENABLED controls the
+		// default value of -linkmode. If it is not set when the
+		// linker is called we take the value it was set to when
+		// cmd/link was compiled. (See make.bash.)
+		switch obj.Getgoextlinkenabled() {
+		case "0":
+			if needed, reason := mustLinkExternal(ctxt); needed {
+				Exitf("internal linking requested via GO_EXTLINK_ENABLED, but external linking required: %s", reason)
+			}
+			Linkmode = LinkInternal
+		case "1":
+			Linkmode = LinkExternal
+		default:
+			if needed, _ := mustLinkExternal(ctxt); needed {
+				Linkmode = LinkExternal
+			} else if iscgo && externalobj {
+				Linkmode = LinkExternal
+			} else {
+				Linkmode = LinkInternal
+			}
+		}
+	case LinkInternal:
+		if needed, reason := mustLinkExternal(ctxt); needed {
+			Exitf("internal linking requested but external linking required: %s", reason)
+		}
+	}
+}
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 57a0dad..eaf6aa2 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -1,6 +1,6 @@
 // Derived from Inferno utils/6l/obj.c and utils/6l/span.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -44,7 +44,7 @@ import (
 	"sync"
 )
 
-func Symgrow(ctxt *Link, s *LSym, siz int64) {
+func Symgrow(s *Symbol, siz int64) {
 	if int64(int(siz)) != siz {
 		log.Fatalf("symgrow size %d too long", siz)
 	}
@@ -58,19 +58,19 @@ func Symgrow(ctxt *Link, s *LSym, siz int64) {
 	s.P = s.P[:siz]
 }
 
-func Addrel(s *LSym) *Reloc {
+func Addrel(s *Symbol) *Reloc {
 	s.R = append(s.R, Reloc{})
 	return &s.R[len(s.R)-1]
 }
 
-func setuintxx(ctxt *Link, s *LSym, off int64, v uint64, wid int64) int64 {
+func setuintxx(ctxt *Link, s *Symbol, off int64, v uint64, wid int64) int64 {
 	if s.Type == 0 {
 		s.Type = obj.SDATA
 	}
 	s.Attr |= AttrReachable
 	if s.Size < off+wid {
 		s.Size = off + wid
-		Symgrow(ctxt, s, s.Size)
+		Symgrow(s, s.Size)
 	}
 
 	switch wid {
@@ -87,7 +87,7 @@ func setuintxx(ctxt *Link, s *LSym, off int64, v uint64, wid int64) int64 {
 	return off + wid
 }
 
-func Addbytes(ctxt *Link, s *LSym, bytes []byte) int64 {
+func Addbytes(s *Symbol, bytes []byte) int64 {
 	if s.Type == 0 {
 		s.Type = obj.SDATA
 	}
@@ -98,13 +98,13 @@ func Addbytes(ctxt *Link, s *LSym, bytes []byte) int64 {
 	return s.Size
 }
 
-func adduintxx(ctxt *Link, s *LSym, v uint64, wid int) int64 {
+func adduintxx(ctxt *Link, s *Symbol, v uint64, wid int) int64 {
 	off := s.Size
 	setuintxx(ctxt, s, off, v, int64(wid))
 	return off
 }
 
-func Adduint8(ctxt *Link, s *LSym, v uint8) int64 {
+func Adduint8(ctxt *Link, s *Symbol, v uint8) int64 {
 	off := s.Size
 	if s.Type == 0 {
 		s.Type = obj.SDATA
@@ -116,38 +116,38 @@ func Adduint8(ctxt *Link, s *LSym, v uint8) int64 {
 	return off
 }
 
-func Adduint16(ctxt *Link, s *LSym, v uint16) int64 {
+func Adduint16(ctxt *Link, s *Symbol, v uint16) int64 {
 	return adduintxx(ctxt, s, uint64(v), 2)
 }
 
-func Adduint32(ctxt *Link, s *LSym, v uint32) int64 {
+func Adduint32(ctxt *Link, s *Symbol, v uint32) int64 {
 	return adduintxx(ctxt, s, uint64(v), 4)
 }
 
-func Adduint64(ctxt *Link, s *LSym, v uint64) int64 {
+func Adduint64(ctxt *Link, s *Symbol, v uint64) int64 {
 	return adduintxx(ctxt, s, v, 8)
 }
 
-func adduint(ctxt *Link, s *LSym, v uint64) int64 {
+func adduint(ctxt *Link, s *Symbol, v uint64) int64 {
 	return adduintxx(ctxt, s, v, SysArch.IntSize)
 }
 
-func setuint8(ctxt *Link, s *LSym, r int64, v uint8) int64 {
+func setuint8(ctxt *Link, s *Symbol, r int64, v uint8) int64 {
 	return setuintxx(ctxt, s, r, uint64(v), 1)
 }
 
-func setuint32(ctxt *Link, s *LSym, r int64, v uint32) int64 {
+func setuint32(ctxt *Link, s *Symbol, r int64, v uint32) int64 {
 	return setuintxx(ctxt, s, r, uint64(v), 4)
 }
 
-func Addaddrplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
+func Addaddrplus(ctxt *Link, s *Symbol, t *Symbol, add int64) int64 {
 	if s.Type == 0 {
 		s.Type = obj.SDATA
 	}
 	s.Attr |= AttrReachable
 	i := s.Size
 	s.Size += int64(ctxt.Arch.PtrSize)
-	Symgrow(ctxt, s, s.Size)
+	Symgrow(s, s.Size)
 	r := Addrel(s)
 	r.Sym = t
 	r.Off = int32(i)
@@ -157,14 +157,14 @@ func Addaddrplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
 	return i + int64(r.Siz)
 }
 
-func Addpcrelplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
+func Addpcrelplus(ctxt *Link, s *Symbol, t *Symbol, add int64) int64 {
 	if s.Type == 0 {
 		s.Type = obj.SDATA
 	}
 	s.Attr |= AttrReachable
 	i := s.Size
 	s.Size += 4
-	Symgrow(ctxt, s, s.Size)
+	Symgrow(s, s.Size)
 	r := Addrel(s)
 	r.Sym = t
 	r.Off = int32(i)
@@ -177,18 +177,18 @@ func Addpcrelplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
 	return i + int64(r.Siz)
 }
 
-func Addaddr(ctxt *Link, s *LSym, t *LSym) int64 {
+func Addaddr(ctxt *Link, s *Symbol, t *Symbol) int64 {
 	return Addaddrplus(ctxt, s, t, 0)
 }
 
-func setaddrplus(ctxt *Link, s *LSym, off int64, t *LSym, add int64) int64 {
+func setaddrplus(ctxt *Link, s *Symbol, off int64, t *Symbol, add int64) int64 {
 	if s.Type == 0 {
 		s.Type = obj.SDATA
 	}
 	s.Attr |= AttrReachable
 	if off+int64(ctxt.Arch.PtrSize) > s.Size {
 		s.Size = off + int64(ctxt.Arch.PtrSize)
-		Symgrow(ctxt, s, s.Size)
+		Symgrow(s, s.Size)
 	}
 
 	r := Addrel(s)
@@ -200,18 +200,18 @@ func setaddrplus(ctxt *Link, s *LSym, off int64, t *LSym, add int64) int64 {
 	return off + int64(r.Siz)
 }
 
-func setaddr(ctxt *Link, s *LSym, off int64, t *LSym) int64 {
+func setaddr(ctxt *Link, s *Symbol, off int64, t *Symbol) int64 {
 	return setaddrplus(ctxt, s, off, t, 0)
 }
 
-func addsize(ctxt *Link, s *LSym, t *LSym) int64 {
+func addsize(ctxt *Link, s *Symbol, t *Symbol) int64 {
 	if s.Type == 0 {
 		s.Type = obj.SDATA
 	}
 	s.Attr |= AttrReachable
 	i := s.Size
 	s.Size += int64(ctxt.Arch.PtrSize)
-	Symgrow(ctxt, s, s.Size)
+	Symgrow(s, s.Size)
 	r := Addrel(s)
 	r.Sym = t
 	r.Off = int32(i)
@@ -220,14 +220,14 @@ func addsize(ctxt *Link, s *LSym, t *LSym) int64 {
 	return i + int64(r.Siz)
 }
 
-func addaddrplus4(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
+func addaddrplus4(ctxt *Link, s *Symbol, t *Symbol, add int64) int64 {
 	if s.Type == 0 {
 		s.Type = obj.SDATA
 	}
 	s.Attr |= AttrReachable
 	i := s.Size
 	s.Size += 4
-	Symgrow(ctxt, s, s.Size)
+	Symgrow(s, s.Size)
 	r := Addrel(s)
 	r.Sym = t
 	r.Off = int32(i)
@@ -238,46 +238,41 @@ func addaddrplus4(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
 }
 
 /*
- * divide-and-conquer list-link
- * sort of LSym* structures.
- * Used for the data block.
+ * divide-and-conquer list-link (by Sub) sort of Symbol* by Value.
+ * Used for sub-symbols when loading host objects (see e.g. ldelf.go).
  */
 
-func listsubp(s *LSym) **LSym {
-	return &s.Sub
-}
-
-func listsort(l *LSym, cmp func(*LSym, *LSym) int, nextp func(*LSym) **LSym) *LSym {
-	if l == nil || *nextp(l) == nil {
+func listsort(l *Symbol) *Symbol {
+	if l == nil || l.Sub == nil {
 		return l
 	}
 
 	l1 := l
 	l2 := l
 	for {
-		l2 = *nextp(l2)
+		l2 = l2.Sub
 		if l2 == nil {
 			break
 		}
-		l2 = *nextp(l2)
+		l2 = l2.Sub
 		if l2 == nil {
 			break
 		}
-		l1 = *nextp(l1)
+		l1 = l1.Sub
 	}
 
-	l2 = *nextp(l1)
-	*nextp(l1) = nil
-	l1 = listsort(l, cmp, nextp)
-	l2 = listsort(l2, cmp, nextp)
+	l2 = l1.Sub
+	l1.Sub = nil
+	l1 = listsort(l)
+	l2 = listsort(l2)
 
 	/* set up lead element */
-	if cmp(l1, l2) < 0 {
+	if l1.Value < l2.Value {
 		l = l1
-		l1 = *nextp(l1)
+		l1 = l1.Sub
 	} else {
 		l = l2
-		l2 = *nextp(l2)
+		l2 = l2.Sub
 	}
 
 	le := l
@@ -285,67 +280,121 @@ func listsort(l *LSym, cmp func(*LSym, *LSym) int, nextp func(*LSym) **LSym) *LS
 	for {
 		if l1 == nil {
 			for l2 != nil {
-				*nextp(le) = l2
+				le.Sub = l2
 				le = l2
-				l2 = *nextp(l2)
+				l2 = l2.Sub
 			}
 
-			*nextp(le) = nil
+			le.Sub = nil
 			break
 		}
 
 		if l2 == nil {
 			for l1 != nil {
-				*nextp(le) = l1
+				le.Sub = l1
 				le = l1
-				l1 = *nextp(l1)
+				l1 = l1.Sub
 			}
 
 			break
 		}
 
-		if cmp(l1, l2) < 0 {
-			*nextp(le) = l1
+		if l1.Value < l2.Value {
+			le.Sub = l1
 			le = l1
-			l1 = *nextp(l1)
+			l1 = l1.Sub
 		} else {
-			*nextp(le) = l2
+			le.Sub = l2
 			le = l2
-			l2 = *nextp(l2)
+			l2 = l2.Sub
 		}
 	}
 
-	*nextp(le) = nil
+	le.Sub = nil
 	return l
 }
 
-func relocsym(s *LSym) {
+// isRuntimeDepPkg returns whether pkg is the runtime package or its dependency
+func isRuntimeDepPkg(pkg string) bool {
+	switch pkg {
+	case "runtime",
+		"sync/atomic": // runtime may call to sync/atomic, due to go:linkname
+		return true
+	}
+	return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test")
+}
+
+// detect too-far jumps in function s, and add trampolines if necessary
+// ARM supports trampoline insertion for internal and external linking
+// PPC64 & PPC64LE support trampoline insertion for internal linking only
+func trampoline(ctxt *Link, s *Symbol) {
+	if Thearch.Trampoline == nil {
+		return // no need or no support of trampolines on this arch
+	}
+
+	if Linkmode == LinkExternal && SysArch.Family == sys.PPC64 {
+		return
+	}
+
+	for ri := range s.R {
+		r := &s.R[ri]
+		if !r.Type.IsDirectJump() {
+			continue
+		}
+		if Symaddr(r.Sym) == 0 && r.Sym.Type != obj.SDYNIMPORT {
+			if r.Sym.File != s.File {
+				if !isRuntimeDepPkg(s.File) || !isRuntimeDepPkg(r.Sym.File) {
+					Errorf(s, "unresolved inter-package jump to %s(%s)", r.Sym, r.Sym.File)
+				}
+				// runtime and its dependent packages may call to each other.
+				// they are fine, as they will be laid down together.
+			}
+			continue
+		}
+
+		Thearch.Trampoline(ctxt, r, s)
+	}
+
+}
+
+// resolve relocations in s.
+func relocsym(ctxt *Link, s *Symbol) {
 	var r *Reloc
-	var rs *LSym
+	var rs *Symbol
 	var i16 int16
 	var off int32
 	var siz int32
 	var fl int32
 	var o int64
 
-	Ctxt.Cursym = s
 	for ri := int32(0); ri < int32(len(s.R)); ri++ {
 		r = &s.R[ri]
+
 		r.Done = 1
 		off = r.Off
 		siz = int32(r.Siz)
 		if off < 0 || off+siz > int32(len(s.P)) {
-			Diag("%s: invalid relocation %d+%d not in [%d,%d)", s.Name, off, siz, 0, len(s.P))
+			rname := ""
+			if r.Sym != nil {
+				rname = r.Sym.Name
+			}
+			Errorf(s, "invalid relocation %s: %d+%d not in [%d,%d)", rname, off, siz, 0, len(s.P))
 			continue
 		}
 
 		if r.Sym != nil && (r.Sym.Type&(obj.SMASK|obj.SHIDDEN) == 0 || r.Sym.Type&obj.SMASK == obj.SXREF) {
 			// When putting the runtime but not main into a shared library
 			// these symbols are undefined and that's OK.
-			if Buildmode == BuildmodeShared && (r.Sym.Name == "main.main" || r.Sym.Name == "main.init") {
-				r.Sym.Type = obj.SDYNIMPORT
+			if Buildmode == BuildmodeShared {
+				if r.Sym.Name == "main.main" || r.Sym.Name == "main.init" {
+					r.Sym.Type = obj.SDYNIMPORT
+				} else if strings.HasPrefix(r.Sym.Name, "go.info.") {
+					// Skip go.info symbols. They are only needed to communicate
+					// DWARF info between the compiler and linker.
+					continue
+				}
 			} else {
-				Diag("%s: not defined", r.Sym.Name)
+				Errorf(s, "relocation target %s not defined", r.Sym.Name)
 				continue
 			}
 		}
@@ -359,13 +408,13 @@ func relocsym(s *LSym) {
 
 		// We need to be able to reference dynimport symbols when linking against
 		// shared libraries, and Solaris needs it always
-		if HEADTYPE != obj.Hsolaris && r.Sym != nil && r.Sym.Type == obj.SDYNIMPORT && !DynlinkingGo() {
+		if Headtype != obj.Hsolaris && r.Sym != nil && r.Sym.Type == obj.SDYNIMPORT && !ctxt.DynlinkingGo() {
 			if !(SysArch.Family == sys.PPC64 && Linkmode == LinkExternal && r.Sym.Name == ".TOC.") {
-				Diag("unhandled relocation for %s (type %d rtype %d)", r.Sym.Name, r.Sym.Type, r.Type)
+				Errorf(s, "unhandled relocation for %s (type %d rtype %d)", r.Sym.Name, r.Sym.Type, r.Type)
 			}
 		}
-		if r.Sym != nil && r.Sym.Type != obj.STLSBSS && !r.Sym.Attr.Reachable() {
-			Diag("unreachable sym in relocation: %s %s", s.Name, r.Sym.Name)
+		if r.Sym != nil && r.Sym.Type != obj.STLSBSS && r.Type != obj.R_WEAKADDROFF && !r.Sym.Attr.Reachable() {
+			Errorf(s, "unreachable sym in relocation: %s", r.Sym.Name)
 		}
 
 		// TODO(mundaym): remove this special case - see issue 14218.
@@ -383,27 +432,27 @@ func relocsym(s *LSym) {
 		default:
 			switch siz {
 			default:
-				Diag("bad reloc size %#x for %s", uint32(siz), r.Sym.Name)
+				Errorf(s, "bad reloc size %#x for %s", uint32(siz), r.Sym.Name)
 			case 1:
 				o = int64(s.P[off])
 			case 2:
-				o = int64(Ctxt.Arch.ByteOrder.Uint16(s.P[off:]))
+				o = int64(ctxt.Arch.ByteOrder.Uint16(s.P[off:]))
 			case 4:
-				o = int64(Ctxt.Arch.ByteOrder.Uint32(s.P[off:]))
+				o = int64(ctxt.Arch.ByteOrder.Uint32(s.P[off:]))
 			case 8:
-				o = int64(Ctxt.Arch.ByteOrder.Uint64(s.P[off:]))
+				o = int64(ctxt.Arch.ByteOrder.Uint64(s.P[off:]))
 			}
-			if Thearch.Archreloc(r, s, &o) < 0 {
-				Diag("unknown reloc %d", r.Type)
+			if Thearch.Archreloc(ctxt, r, s, &o) < 0 {
+				Errorf(s, "unknown reloc to %v: %v", r.Sym.Name, r.Type)
 			}
 
 		case obj.R_TLS_LE:
-			isAndroidX86 := goos == "android" && (SysArch.InFamily(sys.AMD64, sys.I386))
+			isAndroidX86 := obj.GOOS == "android" && (SysArch.InFamily(sys.AMD64, sys.I386))
 
-			if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd && !isAndroidX86 {
+			if Linkmode == LinkExternal && Iself && Headtype != obj.Hopenbsd && !isAndroidX86 {
 				r.Done = 0
 				if r.Sym == nil {
-					r.Sym = Ctxt.Tlsg
+					r.Sym = ctxt.Tlsg
 				}
 				r.Xsym = r.Sym
 				r.Xadd = r.Add
@@ -423,21 +472,21 @@ func relocsym(s *LSym) {
 				// related to the fact that our own TLS storage happens
 				// to take up 8 bytes.
 				o = 8 + r.Sym.Value
-			} else if Iself || Ctxt.Headtype == obj.Hplan9 || Ctxt.Headtype == obj.Hdarwin || isAndroidX86 {
-				o = int64(Ctxt.Tlsoffset) + r.Add
-			} else if Ctxt.Headtype == obj.Hwindows {
+			} else if Iself || Headtype == obj.Hplan9 || Headtype == obj.Hdarwin || isAndroidX86 {
+				o = int64(ctxt.Tlsoffset) + r.Add
+			} else if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
 				o = r.Add
 			} else {
-				log.Fatalf("unexpected R_TLS_LE relocation for %s", Headstr(Ctxt.Headtype))
+				log.Fatalf("unexpected R_TLS_LE relocation for %v", Headtype)
 			}
 
 		case obj.R_TLS_IE:
-			isAndroidX86 := goos == "android" && (SysArch.InFamily(sys.AMD64, sys.I386))
+			isAndroidX86 := obj.GOOS == "android" && (SysArch.InFamily(sys.AMD64, sys.I386))
 
-			if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd && !isAndroidX86 {
+			if Linkmode == LinkExternal && Iself && Headtype != obj.Hopenbsd && !isAndroidX86 {
 				r.Done = 0
 				if r.Sym == nil {
-					r.Sym = Ctxt.Tlsg
+					r.Sym = ctxt.Tlsg
 				}
 				r.Xsym = r.Sym
 				r.Xadd = r.Add
@@ -447,7 +496,20 @@ func relocsym(s *LSym) {
 				}
 				break
 			}
-			log.Fatalf("cannot handle R_TLS_IE when linking internally")
+			if Buildmode == BuildmodePIE && Iself {
+				// We are linking the final executable, so we
+				// can optimize any TLS IE relocation to LE.
+				if Thearch.TLSIEtoLE == nil {
+					log.Fatalf("internal linking of TLS IE not supported on %v", SysArch.Family)
+				}
+				Thearch.TLSIEtoLE(s, int(off), int(r.Siz))
+				o = int64(ctxt.Tlsoffset)
+				// TODO: o += r.Add when SysArch.Family != sys.AMD64?
+				// Why do we treat r.Add differently on AMD64?
+				// Is the external linker using Xadd at all?
+			} else {
+				log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", s.Name)
+			}
 
 		case obj.R_ADDR:
 			if Linkmode == LinkExternal && r.Sym.Type != obj.SCONST {
@@ -463,7 +525,7 @@ func relocsym(s *LSym) {
 				}
 
 				if rs.Type != obj.SHOSTOBJ && rs.Type != obj.SDYNIMPORT && rs.Sect == nil {
-					Diag("missing section for %s", rs.Name)
+					Errorf(s, "missing section for relocation target %s", rs.Name)
 				}
 				r.Xsym = rs
 
@@ -472,7 +534,7 @@ func relocsym(s *LSym) {
 					if SysArch.Family == sys.AMD64 {
 						o = 0
 					}
-				} else if HEADTYPE == obj.Hdarwin {
+				} else if Headtype == obj.Hdarwin {
 					// ld64 for arm64 has a bug where if the address pointed to by o exists in the
 					// symbol table (dynid >= 0), or is inside a symbol that exists in the symbol
 					// table, then it will add o twice into the relocated value.
@@ -480,16 +542,16 @@ func relocsym(s *LSym) {
 					// extern relocation by requiring rs->dynid >= 0.
 					if rs.Type != obj.SHOSTOBJ {
 						if SysArch.Family == sys.ARM64 && rs.Dynid < 0 {
-							Diag("R_ADDR reloc to %s+%d is not supported on darwin/arm64", rs.Name, o)
+							Errorf(s, "R_ADDR reloc to %s+%d is not supported on darwin/arm64", rs.Name, o)
 						}
 						if SysArch.Family != sys.ARM64 {
 							o += Symaddr(rs)
 						}
 					}
-				} else if HEADTYPE == obj.Hwindows {
+				} else if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
 					// nothing to do
 				} else {
-					Diag("unhandled pcrel relocation for %s", headstring)
+					Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, Headtype)
 				}
 
 				break
@@ -503,19 +565,19 @@ func relocsym(s *LSym) {
 			// Instead of special casing only amd64, we treat this as an error on all
 			// 64-bit architectures so as to be future-proof.
 			if int32(o) < 0 && SysArch.PtrSize > 4 && siz == 4 {
-				Diag("non-pc-relative relocation address is too big: %#x (%#x + %#x)", uint64(o), Symaddr(r.Sym), r.Add)
+				Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", r.Sym.Name, uint64(o), Symaddr(r.Sym), r.Add)
 				errorexit()
 			}
 
 		case obj.R_DWARFREF:
 			if r.Sym.Sect == nil {
-				Diag("missing DWARF section: %s from %s", r.Sym.Name, s.Name)
+				Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name)
 			}
 			if Linkmode == LinkExternal {
 				r.Done = 0
 				r.Type = obj.R_ADDR
 
-				r.Xsym = Linkrlookup(Ctxt, r.Sym.Sect.Name, 0)
+				r.Xsym = ctxt.Syms.ROLookup(r.Sym.Sect.Name, 0)
 				r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr)
 				o = r.Xadd
 				rs = r.Xsym
@@ -526,12 +588,36 @@ func relocsym(s *LSym) {
 			}
 			o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr)
 
+		case obj.R_WEAKADDROFF:
+			if !r.Sym.Attr.Reachable() {
+				continue
+			}
+			fallthrough
 		case obj.R_ADDROFF:
-			o = Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + r.Add
+			// The method offset tables using this relocation expect the offset to be relative
+			// to the start of the first text section, even if there are multiple.
+
+			if r.Sym.Sect.Name == ".text" {
+				o = Symaddr(r.Sym) - int64(Segtext.Sect.Vaddr) + r.Add
+			} else {
+				o = Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + r.Add
+			}
 
 			// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
-		case obj.R_CALL, obj.R_GOTPCREL, obj.R_PCREL:
-			if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != obj.SCONST && (r.Sym.Sect != Ctxt.Cursym.Sect || r.Type == obj.R_GOTPCREL) {
+		case obj.R_GOTPCREL:
+			if ctxt.DynlinkingGo() && Headtype == obj.Hdarwin && r.Sym != nil && r.Sym.Type != obj.SCONST {
+				r.Done = 0
+				r.Xadd = r.Add
+				r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk
+				r.Xsym = r.Sym
+
+				o = r.Xadd
+				o += int64(r.Siz)
+				break
+			}
+			fallthrough
+		case obj.R_CALL, obj.R_PCREL:
+			if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != obj.SCONST && (r.Sym.Sect != s.Sect || r.Type == obj.R_GOTPCREL) {
 				r.Done = 0
 
 				// set up addend for eventual relocation via outer symbol.
@@ -545,7 +631,7 @@ func relocsym(s *LSym) {
 
 				r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk
 				if rs.Type != obj.SHOSTOBJ && rs.Type != obj.SDYNIMPORT && rs.Sect == nil {
-					Diag("missing section for %s", rs.Name)
+					Errorf(s, "missing section for relocation target %s", rs.Name)
 				}
 				r.Xsym = rs
 
@@ -554,7 +640,7 @@ func relocsym(s *LSym) {
 					if SysArch.Family == sys.AMD64 {
 						o = 0
 					}
-				} else if HEADTYPE == obj.Hdarwin {
+				} else if Headtype == obj.Hdarwin {
 					if r.Type == obj.R_CALL {
 						if rs.Type != obj.SHOSTOBJ {
 							o += int64(uint64(Symaddr(rs)) - rs.Sect.Vaddr)
@@ -562,11 +648,11 @@ func relocsym(s *LSym) {
 						o -= int64(r.Off) // relative to section offset, not symbol
 					} else if SysArch.Family == sys.ARM {
 						// see ../arm/asm.go:/machoreloc1
-						o += Symaddr(rs) - int64(Ctxt.Cursym.Value) - int64(r.Off)
+						o += Symaddr(rs) - int64(s.Value) - int64(r.Off)
 					} else {
 						o += int64(r.Siz)
 					}
-				} else if HEADTYPE == obj.Hwindows && SysArch.Family == sys.AMD64 { // only amd64 needs PCREL
+				} else if (Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui) && SysArch.Family == sys.AMD64 { // only amd64 needs PCREL
 					// PE/COFF's PC32 relocation uses the address after the relocated
 					// bytes as the base. Compensate by skewing the addend.
 					o += int64(r.Siz)
@@ -574,7 +660,7 @@ func relocsym(s *LSym) {
 					// relocated address, compensate that.
 					o -= int64(s.Sect.Vaddr - PEBASE)
 				} else {
-					Diag("unhandled pcrel relocation for %s", headstring)
+					Errorf(s, "unhandled pcrel relocation to %s on %v", rs.Name, Headtype)
 				}
 
 				break
@@ -585,20 +671,14 @@ func relocsym(s *LSym) {
 				o += Symaddr(r.Sym)
 			}
 
-			// NOTE: The (int32) cast on the next line works around a bug in Plan 9's 8c
-			// compiler. The expression s->value + r->off + r->siz is int32 + int32 +
-			// uchar, and Plan 9 8c incorrectly treats the expression as type uint32
-			// instead of int32, causing incorrect values when sign extended for adding
-			// to o. The bug only occurs on Plan 9, because this C program is compiled by
-			// the standard host compiler (gcc on most other systems).
-			o += r.Add - (s.Value + int64(r.Off) + int64(int32(r.Siz)))
+			o += r.Add - (s.Value + int64(r.Off) + int64(r.Siz))
 
 		case obj.R_SIZE:
 			o = r.Sym.Size + r.Add
 		}
 
 		if r.Variant != RV_NONE {
-			o = Thearch.Archrelocvariant(r, s, o)
+			o = Thearch.Archrelocvariant(ctxt, r, s, o)
 		}
 
 		if false {
@@ -610,8 +690,7 @@ func relocsym(s *LSym) {
 		}
 		switch siz {
 		default:
-			Ctxt.Cursym = s
-			Diag("bad reloc size %#x for %s", uint32(siz), r.Sym.Name)
+			Errorf(s, "bad reloc size %#x for %s", uint32(siz), r.Sym.Name)
 			fallthrough
 
 			// TODO(rsc): Remove.
@@ -620,51 +699,50 @@ func relocsym(s *LSym) {
 
 		case 2:
 			if o != int64(int16(o)) {
-				Diag("relocation address is too big: %#x", o)
+				Errorf(s, "relocation address for %s is too big: %#x", r.Sym.Name, o)
 			}
 			i16 = int16(o)
-			Ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16))
+			ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16))
 
 		case 4:
 			if r.Type == obj.R_PCREL || r.Type == obj.R_CALL {
 				if o != int64(int32(o)) {
-					Diag("pc-relative relocation address is too big: %#x", o)
+					Errorf(s, "pc-relative relocation address for %s is too big: %#x", r.Sym.Name, o)
 				}
 			} else {
 				if o != int64(int32(o)) && o != int64(uint32(o)) {
-					Diag("non-pc-relative relocation address is too big: %#x", uint64(o))
+					Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", r.Sym.Name, uint64(o))
 				}
 			}
 
 			fl = int32(o)
-			Ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl))
+			ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl))
 
 		case 8:
-			Ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o))
+			ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o))
 		}
 	}
 }
 
-func reloc() {
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "%5.2f reloc\n", obj.Cputime())
+func (ctxt *Link) reloc() {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f reloc\n", obj.Cputime())
 	}
-	Bso.Flush()
 
-	for _, s := range Ctxt.Textp {
-		relocsym(s)
+	for _, s := range ctxt.Textp {
+		relocsym(ctxt, s)
 	}
 	for _, sym := range datap {
-		relocsym(sym)
+		relocsym(ctxt, sym)
 	}
-	for s := dwarfp; s != nil; s = s.Next {
-		relocsym(s)
+	for _, s := range dwarfp {
+		relocsym(ctxt, s)
 	}
 }
 
-func dynrelocsym(s *LSym) {
-	if HEADTYPE == obj.Hwindows && Linkmode != LinkExternal {
-		rel := Linklookup(Ctxt, ".rel", 0)
+func dynrelocsym(ctxt *Link, s *Symbol) {
+	if (Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui) && Linkmode != LinkExternal {
+		rel := ctxt.Syms.Lookup(".rel", 0)
 		if s == rel {
 			return
 		}
@@ -675,7 +753,10 @@ func dynrelocsym(s *LSym) {
 				continue
 			}
 			if !targ.Attr.Reachable() {
-				Diag("internal inconsistency: dynamic symbol %s is not reachable.", targ.Name)
+				if r.Type == obj.R_WEAKADDROFF {
+					continue
+				}
+				Errorf(s, "dynamic relocation to unreachable symbol %s", targ.Name)
 			}
 			if r.Sym.Plt == -2 && r.Sym.Got != -2 { // make dynimport JMP table for PE object files.
 				targ.Plt = int32(rel.Size)
@@ -684,17 +765,17 @@ func dynrelocsym(s *LSym) {
 
 				// jmp *addr
 				if SysArch.Family == sys.I386 {
-					Adduint8(Ctxt, rel, 0xff)
-					Adduint8(Ctxt, rel, 0x25)
-					Addaddr(Ctxt, rel, targ)
-					Adduint8(Ctxt, rel, 0x90)
-					Adduint8(Ctxt, rel, 0x90)
+					Adduint8(ctxt, rel, 0xff)
+					Adduint8(ctxt, rel, 0x25)
+					Addaddr(ctxt, rel, targ)
+					Adduint8(ctxt, rel, 0x90)
+					Adduint8(ctxt, rel, 0x90)
 				} else {
-					Adduint8(Ctxt, rel, 0xff)
-					Adduint8(Ctxt, rel, 0x24)
-					Adduint8(Ctxt, rel, 0x25)
-					addaddrplus4(Ctxt, rel, targ, 0)
-					Adduint8(Ctxt, rel, 0x90)
+					Adduint8(ctxt, rel, 0xff)
+					Adduint8(ctxt, rel, 0x24)
+					Adduint8(ctxt, rel, 0x25)
+					addaddrplus4(ctxt, rel, targ, 0)
+					Adduint8(ctxt, rel, 0x90)
 				}
 			} else if r.Sym.Plt >= 0 {
 				r.Sym = rel
@@ -707,104 +788,63 @@ func dynrelocsym(s *LSym) {
 
 	for ri := 0; ri < len(s.R); ri++ {
 		r := &s.R[ri]
+		if Buildmode == BuildmodePIE && Linkmode == LinkInternal {
+			// It's expected that some relocations will be done
+			// later by relocsym (R_TLS_LE, R_ADDROFF), so
+			// don't worry if Adddynrel returns false.
+			Thearch.Adddynrel(ctxt, s, r)
+			continue
+		}
 		if r.Sym != nil && r.Sym.Type == obj.SDYNIMPORT || r.Type >= 256 {
 			if r.Sym != nil && !r.Sym.Attr.Reachable() {
-				Diag("internal inconsistency: dynamic symbol %s is not reachable.", r.Sym.Name)
+				Errorf(s, "dynamic relocation to unreachable symbol %s", r.Sym.Name)
+			}
+			if !Thearch.Adddynrel(ctxt, s, r) {
+				Errorf(s, "unsupported dynamic relocation for symbol %s (type=%d stype=%d)", r.Sym.Name, r.Type, r.Sym.Type)
 			}
-			Thearch.Adddynrel(s, r)
 		}
 	}
 }
 
-func dynreloc(data *[obj.SXREF][]*LSym) {
+func dynreloc(ctxt *Link, data *[obj.SXREF][]*Symbol) {
 	// -d suppresses dynamic loader format, so we may as well not
 	// compute these sections or mark their symbols as reachable.
-	if Debug['d'] != 0 && HEADTYPE != obj.Hwindows {
+	if *FlagD && Headtype != obj.Hwindows && Headtype != obj.Hwindowsgui {
 		return
 	}
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "%5.2f reloc\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f reloc\n", obj.Cputime())
 	}
-	Bso.Flush()
 
-	for _, s := range Ctxt.Textp {
-		dynrelocsym(s)
+	for _, s := range ctxt.Textp {
+		dynrelocsym(ctxt, s)
 	}
 	for _, syms := range data {
 		for _, sym := range syms {
-			dynrelocsym(sym)
+			dynrelocsym(ctxt, sym)
 		}
 	}
 	if Iself {
-		elfdynhash()
-	}
-}
-
-func blk(start *LSym, addr int64, size int64) {
-	var sym *LSym
-
-	for sym = start; sym != nil; sym = sym.Next {
-		if sym.Type&obj.SSUB == 0 && sym.Value >= addr {
-			break
-		}
-	}
-
-	eaddr := addr + size
-	for ; sym != nil; sym = sym.Next {
-		if sym.Type&obj.SSUB != 0 {
-			continue
-		}
-		if sym.Value >= eaddr {
-			break
-		}
-		Ctxt.Cursym = sym
-		if sym.Value < addr {
-			Diag("phase error: addr=%#x but sym=%#x type=%d", addr, sym.Value, sym.Type)
-			errorexit()
-		}
-
-		if addr < sym.Value {
-			strnput("", int(sym.Value-addr))
-			addr = sym.Value
-		}
-		Cwrite(sym.P)
-		addr += int64(len(sym.P))
-		if addr < sym.Value+sym.Size {
-			strnput("", int(sym.Value+sym.Size-addr))
-			addr = sym.Value + sym.Size
-		}
-		if addr != sym.Value+sym.Size {
-			Diag("phase error: addr=%#x value+size=%#x", addr, sym.Value+sym.Size)
-			errorexit()
-		}
-
-		if sym.Value+sym.Size >= eaddr {
-			break
-		}
-	}
-
-	if addr < eaddr {
-		strnput("", int(eaddr-addr))
+		elfdynhash(ctxt)
 	}
-	Cflush()
 }
 
-func Codeblk(addr int64, size int64) {
-	CodeblkPad(addr, size, zeros[:])
+func Codeblk(ctxt *Link, addr int64, size int64) {
+	CodeblkPad(ctxt, addr, size, zeros[:])
 }
-func CodeblkPad(addr int64, size int64, pad []byte) {
-	if Debug['a'] != 0 {
-		fmt.Fprintf(Bso, "codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos())
+func CodeblkPad(ctxt *Link, addr int64, size int64, pad []byte) {
+	if *flagA {
+		ctxt.Logf("codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, coutbuf.Offset())
 	}
 
-	blkSlice(Ctxt.Textp, addr, size, pad)
+	blk(ctxt, ctxt.Textp, addr, size, pad)
 
 	/* again for printing */
-	if Debug['a'] == 0 {
+	if !*flagA {
 		return
 	}
 
-	syms := Ctxt.Textp
+	syms := ctxt.Textp
 	for i, sym := range syms {
 		if !sym.Attr.Reachable() {
 			continue
@@ -826,42 +866,37 @@ func CodeblkPad(addr int64, size int64, pad []byte) {
 		}
 
 		if addr < sym.Value {
-			fmt.Fprintf(Bso, "%-20s %.8x|", "_", uint64(addr))
+			ctxt.Logf("%-20s %.8x|", "_", uint64(addr))
 			for ; addr < sym.Value; addr++ {
-				fmt.Fprintf(Bso, " %.2x", 0)
+				ctxt.Logf(" %.2x", 0)
 			}
-			fmt.Fprintf(Bso, "\n")
+			ctxt.Logf("\n")
 		}
 
-		fmt.Fprintf(Bso, "%.6x\t%-20s\n", uint64(addr), sym.Name)
+		ctxt.Logf("%.6x\t%-20s\n", uint64(addr), sym.Name)
 		q = sym.P
 
 		for len(q) >= 16 {
-			fmt.Fprintf(Bso, "%.6x\t% x\n", uint64(addr), q[:16])
+			ctxt.Logf("%.6x\t% x\n", uint64(addr), q[:16])
 			addr += 16
 			q = q[16:]
 		}
 
 		if len(q) > 0 {
-			fmt.Fprintf(Bso, "%.6x\t% x\n", uint64(addr), q)
+			ctxt.Logf("%.6x\t% x\n", uint64(addr), q)
 			addr += int64(len(q))
 		}
 	}
 
 	if addr < eaddr {
-		fmt.Fprintf(Bso, "%-20s %.8x|", "_", uint64(addr))
+		ctxt.Logf("%-20s %.8x|", "_", uint64(addr))
 		for ; addr < eaddr; addr++ {
-			fmt.Fprintf(Bso, " %.2x", 0)
+			ctxt.Logf(" %.2x", 0)
 		}
 	}
-
-	Bso.Flush()
 }
 
-// blkSlice is a variant of blk that processes slices.
-// After text symbols are converted from a linked list to a slice,
-// delete blk and give this function its name.
-func blkSlice(syms []*LSym, addr, size int64, pad []byte) {
+func blk(ctxt *Link, syms []*Symbol, addr, size int64, pad []byte) {
 	for i, s := range syms {
 		if s.Type&obj.SSUB == 0 && s.Value >= addr {
 			syms = syms[i:]
@@ -877,9 +912,8 @@ func blkSlice(syms []*LSym, addr, size int64, pad []byte) {
 		if s.Value >= eaddr {
 			break
 		}
-		Ctxt.Cursym = s
 		if s.Value < addr {
-			Diag("phase error: addr=%#x but sym=%#x type=%d", addr, s.Value, s.Type)
+			Errorf(s, "phase error: addr=%#x but sym=%#x type=%d", addr, s.Value, s.Type)
 			errorexit()
 		}
 		if addr < s.Value {
@@ -893,7 +927,7 @@ func blkSlice(syms []*LSym, addr, size int64, pad []byte) {
 			addr = s.Value + s.Size
 		}
 		if addr != s.Value+s.Size {
-			Diag("phase error: addr=%#x value+size=%#x", addr, s.Value+s.Size)
+			Errorf(s, "phase error: addr=%#x value+size=%#x", addr, s.Value+s.Size)
 			errorexit()
 		}
 		if s.Value+s.Size >= eaddr {
@@ -907,15 +941,15 @@ func blkSlice(syms []*LSym, addr, size int64, pad []byte) {
 	Cflush()
 }
 
-func Datblk(addr int64, size int64) {
-	if Debug['a'] != 0 {
-		fmt.Fprintf(Bso, "datblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos())
+func Datblk(ctxt *Link, addr int64, size int64) {
+	if *flagA {
+		ctxt.Logf("datblk [%#x,%#x) at offset %#x\n", addr, addr+size, coutbuf.Offset())
 	}
 
-	blkSlice(datap, addr, size, zeros[:])
+	blk(ctxt, datap, addr, size, zeros[:])
 
 	/* again for printing */
-	if Debug['a'] == 0 {
+	if !*flagA {
 		return
 	}
 
@@ -933,23 +967,23 @@ func Datblk(addr int64, size int64) {
 			break
 		}
 		if addr < sym.Value {
-			fmt.Fprintf(Bso, "\t%.8x| 00 ...\n", uint64(addr))
+			ctxt.Logf("\t%.8x| 00 ...\n", uint64(addr))
 			addr = sym.Value
 		}
 
-		fmt.Fprintf(Bso, "%s\n\t%.8x|", sym.Name, uint64(addr))
+		ctxt.Logf("%s\n\t%.8x|", sym.Name, uint64(addr))
 		for i, b := range sym.P {
 			if i > 0 && i%16 == 0 {
-				fmt.Fprintf(Bso, "\n\t%.8x|", uint64(addr)+uint64(i))
+				ctxt.Logf("\n\t%.8x|", uint64(addr)+uint64(i))
 			}
-			fmt.Fprintf(Bso, " %.2x", b)
+			ctxt.Logf(" %.2x", b)
 		}
 
 		addr += int64(len(sym.P))
 		for ; addr < sym.Value+sym.Size; addr++ {
-			fmt.Fprintf(Bso, " %.2x", 0)
+			ctxt.Logf(" %.2x", 0)
 		}
-		fmt.Fprintf(Bso, "\n")
+		ctxt.Logf("\n")
 
 		if Linkmode != LinkExternal {
 			continue
@@ -968,22 +1002,22 @@ func Datblk(addr int64, size int64) {
 			case obj.R_CALL:
 				typ = "call"
 			}
-			fmt.Fprintf(Bso, "\treloc %.8x/%d %s %s+%#x [%#x]\n", uint(sym.Value+int64(r.Off)), r.Siz, typ, rsname, r.Add, r.Sym.Value+r.Add)
+			ctxt.Logf("\treloc %.8x/%d %s %s+%#x [%#x]\n", uint(sym.Value+int64(r.Off)), r.Siz, typ, rsname, r.Add, r.Sym.Value+r.Add)
 		}
 	}
 
 	if addr < eaddr {
-		fmt.Fprintf(Bso, "\t%.8x| 00 ...\n", uint(addr))
+		ctxt.Logf("\t%.8x| 00 ...\n", uint(addr))
 	}
-	fmt.Fprintf(Bso, "\t%.8x|\n", uint(eaddr))
+	ctxt.Logf("\t%.8x|\n", uint(eaddr))
 }
 
-func Dwarfblk(addr int64, size int64) {
-	if Debug['a'] != 0 {
-		fmt.Fprintf(Bso, "dwarfblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos())
+func Dwarfblk(ctxt *Link, addr int64, size int64) {
+	if *flagA {
+		ctxt.Logf("dwarfblk [%#x,%#x) at offset %#x\n", addr, addr+size, coutbuf.Offset())
 	}
 
-	blk(dwarfp, addr, size)
+	blk(ctxt, dwarfp, addr, size, zeros[:])
 }
 
 var zeros [512]byte
@@ -1013,29 +1047,30 @@ func strnputPad(s string, n int, pad []byte) {
 	}
 }
 
-var strdata []*LSym
+var strdata []*Symbol
 
-func addstrdata1(arg string) {
-	i := strings.Index(arg, "=")
-	if i < 0 {
+func addstrdata1(ctxt *Link, arg string) {
+	eq := strings.Index(arg, "=")
+	dot := strings.LastIndex(arg[:eq+1], ".")
+	if eq < 0 || dot < 0 {
 		Exitf("-X flag requires argument of the form importpath.name=value")
 	}
-	addstrdata(arg[:i], arg[i+1:])
+	addstrdata(ctxt, pathtoprefix(arg[:dot])+arg[dot:eq], arg[eq+1:])
 }
 
-func addstrdata(name string, value string) {
+func addstrdata(ctxt *Link, name string, value string) {
 	p := fmt.Sprintf("%s.str", name)
-	sp := Linklookup(Ctxt, p, 0)
+	sp := ctxt.Syms.Lookup(p, 0)
 
 	Addstring(sp, value)
 	sp.Type = obj.SRODATA
 
-	s := Linklookup(Ctxt, name, 0)
+	s := ctxt.Syms.Lookup(name, 0)
 	s.Size = 0
 	s.Attr |= AttrDuplicateOK
 	reachable := s.Attr.Reachable()
-	Addaddr(Ctxt, s, sp)
-	adduintxx(Ctxt, s, uint64(len(value)), SysArch.PtrSize)
+	Addaddr(ctxt, s, sp)
+	adduintxx(ctxt, s, uint64(len(value)), SysArch.PtrSize)
 
 	// addstring, addaddr, etc., mark the symbols as reachable.
 	// In this case that is not necessarily true, so stick to what
@@ -1047,24 +1082,24 @@ func addstrdata(name string, value string) {
 	sp.Attr.Set(AttrReachable, reachable)
 }
 
-func checkstrdata() {
+func (ctxt *Link) checkstrdata() {
 	for _, s := range strdata {
 		if s.Type == obj.STEXT {
-			Diag("cannot use -X with text symbol %s", s.Name)
+			Errorf(s, "cannot use -X with text symbol")
 		} else if s.Gotype != nil && s.Gotype.Name != "type.string" {
-			Diag("cannot use -X with non-string symbol %s", s.Name)
+			Errorf(s, "cannot use -X with non-string symbol")
 		}
 	}
 }
 
-func Addstring(s *LSym, str string) int64 {
+func Addstring(s *Symbol, str string) int64 {
 	if s.Type == 0 {
 		s.Type = obj.SNOPTRDATA
 	}
 	s.Attr |= AttrReachable
 	r := s.Size
 	if s.Name == ".shstrtab" {
-		elfsetstring(str, int(r))
+		elfsetstring(s, str, int(r))
 	}
 	s.P = append(s.P, str...)
 	s.P = append(s.P, 0)
@@ -1074,31 +1109,31 @@ func Addstring(s *LSym, str string) int64 {
 
 // addgostring adds str, as a Go string value, to s. symname is the name of the
 // symbol used to define the string data and must be unique per linked object.
-func addgostring(s *LSym, symname, str string) {
-	sym := Linklookup(Ctxt, symname, 0)
+func addgostring(ctxt *Link, s *Symbol, symname, str string) {
+	sym := ctxt.Syms.Lookup(symname, 0)
 	if sym.Type != obj.Sxxx {
-		Diag("duplicate symname in addgostring: %s", symname)
+		Errorf(s, "duplicate symname in addgostring: %s", symname)
 	}
 	sym.Attr |= AttrReachable
 	sym.Attr |= AttrLocal
 	sym.Type = obj.SRODATA
 	sym.Size = int64(len(str))
 	sym.P = []byte(str)
-	Addaddr(Ctxt, s, sym)
-	adduint(Ctxt, s, uint64(len(str)))
+	Addaddr(ctxt, s, sym)
+	adduint(ctxt, s, uint64(len(str)))
 }
 
-func addinitarrdata(s *LSym) {
+func addinitarrdata(ctxt *Link, s *Symbol) {
 	p := s.Name + ".ptr"
-	sp := Linklookup(Ctxt, p, 0)
+	sp := ctxt.Syms.Lookup(p, 0)
 	sp.Type = obj.SINITARR
 	sp.Size = 0
 	sp.Attr |= AttrDuplicateOK
-	Addaddr(Ctxt, sp, s)
+	Addaddr(ctxt, sp, s)
 }
 
-func dosymtype() {
-	for _, s := range Ctxt.Allsym {
+func dosymtype(ctxt *Link) {
+	for _, s := range ctxt.Syms.Allsym {
 		if len(s.P) > 0 {
 			if s.Type == obj.SBSS {
 				s.Type = obj.SDATA
@@ -1111,22 +1146,22 @@ func dosymtype() {
 		// library initializer function.
 		switch Buildmode {
 		case BuildmodeCArchive, BuildmodeCShared:
-			if s.Name == INITENTRY {
-				addinitarrdata(s)
+			if s.Name == *flagEntrySymbol {
+				addinitarrdata(ctxt, s)
 			}
 		}
 	}
 }
 
 // symalign returns the required alignment for the given symbol s.
-func symalign(s *LSym) int32 {
+func symalign(s *Symbol) int32 {
 	min := int32(Thearch.Minalign)
 	if s.Align >= min {
 		return s.Align
 	} else if s.Align != 0 {
 		return min
 	}
-	if (strings.HasPrefix(s.Name, "go.string.") && !strings.HasPrefix(s.Name, "go.string.hdr.")) || strings.HasPrefix(s.Name, "type..namedata.") {
+	if strings.HasPrefix(s.Name, "go.string.") || strings.HasPrefix(s.Name, "type..namedata.") {
 		// String data is just bytes.
 		// If we align it, we waste a lot of space to padding.
 		return min
@@ -1138,28 +1173,32 @@ func symalign(s *LSym) int32 {
 	return align
 }
 
-func aligndatsize(datsize int64, s *LSym) int64 {
+func aligndatsize(datsize int64, s *Symbol) int64 {
 	return Rnd(datsize, int64(symalign(s)))
 }
 
 const debugGCProg = false
 
 type GCProg struct {
-	sym *LSym
-	w   gcprog.Writer
+	ctxt *Link
+	sym  *Symbol
+	w    gcprog.Writer
 }
 
-func (p *GCProg) Init(name string) {
-	p.sym = Linklookup(Ctxt, name, 0)
-	p.w.Init(p.writeByte)
+func (p *GCProg) Init(ctxt *Link, name string) {
+	p.ctxt = ctxt
+	p.sym = ctxt.Syms.Lookup(name, 0)
+	p.w.Init(p.writeByte(ctxt))
 	if debugGCProg {
 		fmt.Fprintf(os.Stderr, "ld: start GCProg %s\n", name)
 		p.w.Debug(os.Stderr)
 	}
 }
 
-func (p *GCProg) writeByte(x byte) {
-	Adduint8(Ctxt, p.sym, x)
+func (p *GCProg) writeByte(ctxt *Link) func(x byte) {
+	return func(x byte) {
+		Adduint8(ctxt, p.sym, x)
+	}
 }
 
 func (p *GCProg) End(size int64) {
@@ -1170,25 +1209,32 @@ func (p *GCProg) End(size int64) {
 	}
 }
 
-func (p *GCProg) AddSym(s *LSym) {
+func (p *GCProg) AddSym(s *Symbol) {
 	typ := s.Gotype
 	// Things without pointers should be in SNOPTRDATA or SNOPTRBSS;
 	// everything we see should have pointers and should therefore have a type.
 	if typ == nil {
-		Diag("missing Go type information for global symbol: %s size %d", s.Name, int(s.Size))
+		switch s.Name {
+		case "runtime.data", "runtime.edata", "runtime.bss", "runtime.ebss":
+			// Ignore special symbols that are sometimes laid out
+			// as real symbols. See comment about dyld on darwin in
+			// the address function.
+			return
+		}
+		Errorf(s, "missing Go type information for global symbol: size %d", s.Size)
 		return
 	}
 
 	ptrsize := int64(SysArch.PtrSize)
-	nptr := decodetype_ptrdata(typ) / ptrsize
+	nptr := decodetypePtrdata(p.ctxt.Arch, typ) / ptrsize
 
 	if debugGCProg {
 		fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", s.Name, s.Value, s.Value/ptrsize, nptr)
 	}
 
-	if decodetype_usegcprog(typ) == 0 {
+	if decodetypeUsegcprog(typ) == 0 {
 		// Copy pointers from mask into program.
-		mask := decodetype_gcmask(typ)
+		mask := decodetypeGcmask(p.ctxt, typ)
 		for i := int64(0); i < nptr; i++ {
 			if (mask[i/8]>>uint(i%8))&1 != 0 {
 				p.w.Ptr(s.Value/ptrsize + i)
@@ -1198,17 +1244,17 @@ func (p *GCProg) AddSym(s *LSym) {
 	}
 
 	// Copy program.
-	prog := decodetype_gcprog(typ)
+	prog := decodetypeGcprog(p.ctxt, typ)
 	p.w.ZeroUntil(s.Value / ptrsize)
 	p.w.Append(prog[4:], nptr)
 }
 
-// dataSortKey is used to sort a slice of data symbol *LSym pointers.
+// dataSortKey is used to sort a slice of data symbol *Symbol pointers.
 // The sort keys are kept inline to improve cache behaviour while sorting.
 type dataSortKey struct {
 	size int64
 	name string
-	lsym *LSym
+	sym  *Symbol
 }
 
 type bySizeAndName []dataSortKey
@@ -1225,33 +1271,64 @@ func (d bySizeAndName) Less(i, j int) bool {
 
 const cutoff int64 = 2e9 // 2 GB (or so; looks better in errors than 2^31)
 
-func checkdatsize(datsize int64, symn int) {
+func checkdatsize(ctxt *Link, datsize int64, symn obj.SymKind) {
 	if datsize > cutoff {
-		Diag("too much data in section %v (over %d bytes)", symn, cutoff)
+		Errorf(nil, "too much data in section %v (over %d bytes)", symn, cutoff)
 	}
 }
 
-func list2slice(s *LSym) []*LSym {
-	var syms []*LSym
-	for ; s != nil; s = s.Next {
-		syms = append(syms, s)
-	}
-	return syms
-}
-
 // datap is a collection of reachable data symbols in address order.
 // Generated by dodata.
-var datap []*LSym
-
-func dodata() {
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "%5.2f dodata\n", obj.Cputime())
+var datap []*Symbol
+
+func (ctxt *Link) dodata() {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f dodata\n", obj.Cputime())
+	}
+
+	if ctxt.DynlinkingGo() && Headtype == obj.Hdarwin {
+		// The values in moduledata are filled out by relocations
+		// pointing to the addresses of these special symbols.
+		// Typically these symbols have no size and are not laid
+		// out with their matching section.
+		//
+		// However on darwin, dyld will find the special symbol
+		// in the first loaded module, even though it is local.
+		//
+		// (An hypothesis, formed without looking in the dyld sources:
+		// these special symbols have no size, so their address
+		// matches a real symbol. The dynamic linker assumes we
+		// want the normal symbol with the same address and finds
+		// it in the other module.)
+		//
+		// To work around this we lay out the symbls whose
+		// addresses are vital for multi-module programs to work
+		// as normal symbols, and give them a little size.
+		bss := ctxt.Syms.Lookup("runtime.bss", 0)
+		bss.Size = 8
+		bss.Attr.Set(AttrSpecial, false)
+
+		ctxt.Syms.Lookup("runtime.ebss", 0).Attr.Set(AttrSpecial, false)
+
+		data := ctxt.Syms.Lookup("runtime.data", 0)
+		data.Size = 8
+		data.Attr.Set(AttrSpecial, false)
+
+		ctxt.Syms.Lookup("runtime.edata", 0).Attr.Set(AttrSpecial, false)
+
+		types := ctxt.Syms.Lookup("runtime.types", 0)
+		types.Type = obj.STYPE
+		types.Size = 8
+		types.Attr.Set(AttrSpecial, false)
+
+		etypes := ctxt.Syms.Lookup("runtime.etypes", 0)
+		etypes.Type = obj.SFUNCTAB
+		etypes.Attr.Set(AttrSpecial, false)
 	}
-	Bso.Flush()
 
 	// Collect data symbols by type into data.
-	var data [obj.SXREF][]*LSym
-	for _, s := range Ctxt.Allsym {
+	var data [obj.SXREF][]*Symbol
+	for _, s := range ctxt.Syms.Allsym {
 		if !s.Attr.Reachable() || s.Attr.Special() {
 			continue
 		}
@@ -1267,25 +1344,25 @@ func dodata() {
 	// symbol, which is itself data.
 	//
 	// On darwin, we need the symbol table numbers for dynreloc.
-	if HEADTYPE == obj.Hdarwin {
-		machosymorder()
+	if Headtype == obj.Hdarwin {
+		machosymorder(ctxt)
 	}
-	dynreloc(&data)
+	dynreloc(ctxt, &data)
 
 	if UseRelro() {
 		// "read only" data with relocations needs to go in its own section
 		// when building a shared library. We do this by boosting objects of
 		// type SXXX with relocations to type SXXXRELRO.
-		for symnro := int16(obj.STYPE); symnro < obj.STYPERELRO; symnro++ {
-			symnrelro := symnro + obj.STYPERELRO - obj.STYPE
+		for _, symnro := range obj.ReadOnly {
+			symnrelro := obj.RelROMap[symnro]
 
-			ro := []*LSym{}
+			ro := []*Symbol{}
 			relro := data[symnrelro]
 
 			for _, s := range data[symnro] {
 				isRelro := len(s.R) > 0
 				switch s.Type {
-				case obj.STYPE, obj.SGOSTRINGHDR, obj.STYPERELRO, obj.SGOSTRINGHDRRELRO:
+				case obj.STYPE, obj.STYPERELRO, obj.SGOFUNCRELRO:
 					// Symbols are not sorted yet, so it is possible
 					// that an Outer symbol has been changed to a
 					// relro Type before it reaches here.
@@ -1308,8 +1385,8 @@ func dodata() {
 			// symbol and the outer end up in the same section).
 			for _, s := range relro {
 				if s.Outer != nil && s.Outer.Type != s.Type {
-					Diag("inconsistent types for %s and its Outer %s (%d != %d)",
-						s.Name, s.Outer.Name, s.Type, s.Outer.Type)
+					Errorf(s, "inconsistent types for symbol and its Outer %s (%v != %v)",
+						s.Outer.Name, s.Type, s.Outer.Type)
 				}
 			}
 
@@ -1322,10 +1399,10 @@ func dodata() {
 	var dataMaxAlign [obj.SXREF]int32
 	var wg sync.WaitGroup
 	for symn := range data {
-		symn := symn
+		symn := obj.SymKind(symn)
 		wg.Add(1)
 		go func() {
-			data[symn], dataMaxAlign[symn] = dodataSect(symn, data[symn])
+			data[symn], dataMaxAlign[symn] = dodataSect(ctxt, symn, data[symn])
 			wg.Done()
 		}()
 	}
@@ -1337,14 +1414,14 @@ func dodata() {
 	// to generate garbage collection information.
 	datsize := int64(0)
 
-	// Writable sections.
-	writableSects := []int{
+	// Writable data sections that do not need any specialized handling.
+	writable := []obj.SymKind{
 		obj.SELFSECT,
 		obj.SMACHO,
 		obj.SMACHOGOT,
 		obj.SWINDOWS,
 	}
-	for _, symn := range writableSects {
+	for _, symn := range writable {
 		for _, s := range data[symn] {
 			sect := addsection(&Segdata, s.Name, 06)
 			sect.Align = symalign(s)
@@ -1356,7 +1433,7 @@ func dodata() {
 			datsize += s.Size
 			sect.Length = uint64(datsize) - sect.Vaddr
 		}
-		checkdatsize(datsize, symn)
+		checkdatsize(ctxt, datsize, symn)
 	}
 
 	// .got (and .toc on ppc64)
@@ -1365,7 +1442,7 @@ func dodata() {
 		sect.Align = dataMaxAlign[obj.SELFGOT]
 		datsize = Rnd(datsize, int64(sect.Align))
 		sect.Vaddr = uint64(datsize)
-		var toc *LSym
+		var toc *Symbol
 		for _, s := range data[obj.SELFGOT] {
 			datsize = aligndatsize(datsize, s)
 			s.Sect = sect
@@ -1373,7 +1450,7 @@ func dodata() {
 			s.Value = int64(uint64(datsize) - sect.Vaddr)
 
 			// Resolve .TOC. symbol for this object file (ppc64)
-			toc = Linkrlookup(Ctxt, ".TOC.", int(s.Version))
+			toc = ctxt.Syms.ROLookup(".TOC.", int(s.Version))
 			if toc != nil {
 				toc.Sect = sect
 				toc.Outer = s
@@ -1385,7 +1462,7 @@ func dodata() {
 
 			datsize += s.Size
 		}
-		checkdatsize(datsize, obj.SELFGOT)
+		checkdatsize(ctxt, datsize, obj.SELFGOT)
 		sect.Length = uint64(datsize) - sect.Vaddr
 	}
 
@@ -1394,8 +1471,8 @@ func dodata() {
 	sect.Align = dataMaxAlign[obj.SNOPTRDATA]
 	datsize = Rnd(datsize, int64(sect.Align))
 	sect.Vaddr = uint64(datsize)
-	Linklookup(Ctxt, "runtime.noptrdata", 0).Sect = sect
-	Linklookup(Ctxt, "runtime.enoptrdata", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.enoptrdata", 0).Sect = sect
 	for _, s := range data[obj.SNOPTRDATA] {
 		datsize = aligndatsize(datsize, s)
 		s.Sect = sect
@@ -1403,14 +1480,14 @@ func dodata() {
 		s.Value = int64(uint64(datsize) - sect.Vaddr)
 		datsize += s.Size
 	}
-	checkdatsize(datsize, obj.SNOPTRDATA)
+	checkdatsize(ctxt, datsize, obj.SNOPTRDATA)
 	sect.Length = uint64(datsize) - sect.Vaddr
 
-	hasinitarr := Linkshared
+	hasinitarr := *FlagLinkshared
 
 	/* shared library initializer */
 	switch Buildmode {
-	case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
+	case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePlugin:
 		hasinitarr = true
 	}
 	if hasinitarr {
@@ -1425,7 +1502,7 @@ func dodata() {
 			datsize += s.Size
 		}
 		sect.Length = uint64(datsize) - sect.Vaddr
-		checkdatsize(datsize, obj.SINITARR)
+		checkdatsize(ctxt, datsize, obj.SINITARR)
 	}
 
 	/* data */
@@ -1433,10 +1510,10 @@ func dodata() {
 	sect.Align = dataMaxAlign[obj.SDATA]
 	datsize = Rnd(datsize, int64(sect.Align))
 	sect.Vaddr = uint64(datsize)
-	Linklookup(Ctxt, "runtime.data", 0).Sect = sect
-	Linklookup(Ctxt, "runtime.edata", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.data", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.edata", 0).Sect = sect
 	var gc GCProg
-	gc.Init("runtime.gcdata")
+	gc.Init(ctxt, "runtime.gcdata")
 	for _, s := range data[obj.SDATA] {
 		s.Sect = sect
 		s.Type = obj.SDATA
@@ -1445,7 +1522,7 @@ func dodata() {
 		gc.AddSym(s)
 		datsize += s.Size
 	}
-	checkdatsize(datsize, obj.SDATA)
+	checkdatsize(ctxt, datsize, obj.SDATA)
 	sect.Length = uint64(datsize) - sect.Vaddr
 	gc.End(int64(sect.Length))
 
@@ -1454,10 +1531,10 @@ func dodata() {
 	sect.Align = dataMaxAlign[obj.SBSS]
 	datsize = Rnd(datsize, int64(sect.Align))
 	sect.Vaddr = uint64(datsize)
-	Linklookup(Ctxt, "runtime.bss", 0).Sect = sect
-	Linklookup(Ctxt, "runtime.ebss", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.bss", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.ebss", 0).Sect = sect
 	gc = GCProg{}
-	gc.Init("runtime.gcbss")
+	gc.Init(ctxt, "runtime.gcbss")
 	for _, s := range data[obj.SBSS] {
 		s.Sect = sect
 		datsize = aligndatsize(datsize, s)
@@ -1465,7 +1542,7 @@ func dodata() {
 		gc.AddSym(s)
 		datsize += s.Size
 	}
-	checkdatsize(datsize, obj.SBSS)
+	checkdatsize(ctxt, datsize, obj.SBSS)
 	sect.Length = uint64(datsize) - sect.Vaddr
 	gc.End(int64(sect.Length))
 
@@ -1474,8 +1551,8 @@ func dodata() {
 	sect.Align = dataMaxAlign[obj.SNOPTRBSS]
 	datsize = Rnd(datsize, int64(sect.Align))
 	sect.Vaddr = uint64(datsize)
-	Linklookup(Ctxt, "runtime.noptrbss", 0).Sect = sect
-	Linklookup(Ctxt, "runtime.enoptrbss", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.noptrbss", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.enoptrbss", 0).Sect = sect
 	for _, s := range data[obj.SNOPTRBSS] {
 		datsize = aligndatsize(datsize, s)
 		s.Sect = sect
@@ -1484,12 +1561,12 @@ func dodata() {
 	}
 
 	sect.Length = uint64(datsize) - sect.Vaddr
-	Linklookup(Ctxt, "runtime.end", 0).Sect = sect
-	checkdatsize(datsize, obj.SNOPTRBSS)
+	ctxt.Syms.Lookup("runtime.end", 0).Sect = sect
+	checkdatsize(ctxt, datsize, obj.SNOPTRBSS)
 
 	if len(data[obj.STLSBSS]) > 0 {
 		var sect *Section
-		if Iself && (Linkmode == LinkExternal || Debug['d'] == 0) && HEADTYPE != obj.Hopenbsd {
+		if Iself && (Linkmode == LinkExternal || !*FlagD) && Headtype != obj.Hopenbsd {
 			sect = addsection(&Segdata, ".tbss", 06)
 			sect.Align = int32(SysArch.PtrSize)
 			sect.Vaddr = 0
@@ -1502,7 +1579,7 @@ func dodata() {
 			s.Value = datsize
 			datsize += s.Size
 		}
-		checkdatsize(datsize, obj.STLSBSS)
+		checkdatsize(ctxt, datsize, obj.STLSBSS)
 
 		if sect != nil {
 			sect.Length = uint64(datsize)
@@ -1530,7 +1607,7 @@ func dodata() {
 
 	/* read-only executable ELF, Mach-O sections */
 	if len(data[obj.STEXT]) != 0 {
-		Diag("dodata found an STEXT symbol: %s", data[obj.STEXT][0].Name)
+		Errorf(nil, "dodata found an STEXT symbol: %s", data[obj.STEXT][0].Name)
 	}
 	for _, s := range data[obj.SELFRXSECT] {
 		sect := addsection(&Segtext, s.Name, 04)
@@ -1542,37 +1619,27 @@ func dodata() {
 		s.Value = int64(uint64(datsize) - sect.Vaddr)
 		datsize += s.Size
 		sect.Length = uint64(datsize) - sect.Vaddr
-		checkdatsize(datsize, obj.SELFRXSECT)
+		checkdatsize(ctxt, datsize, obj.SELFRXSECT)
 	}
 
 	/* read-only data */
 	sect = addsection(segro, ".rodata", 04)
 
 	sect.Vaddr = 0
-	Linklookup(Ctxt, "runtime.rodata", 0).Sect = sect
-	Linklookup(Ctxt, "runtime.erodata", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.rodata", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.erodata", 0).Sect = sect
 	if !UseRelro() {
-		Linklookup(Ctxt, "runtime.types", 0).Sect = sect
-		Linklookup(Ctxt, "runtime.etypes", 0).Sect = sect
-	}
-	roSects := []int{
-		obj.STYPE,
-		obj.SSTRING,
-		obj.SGOSTRING,
-		obj.SGOSTRINGHDR,
-		obj.SGOFUNC,
-		obj.SGCBITS,
-		obj.SRODATA,
-		obj.SFUNCTAB,
-	}
-	for _, symn := range roSects {
+		ctxt.Syms.Lookup("runtime.types", 0).Sect = sect
+		ctxt.Syms.Lookup("runtime.etypes", 0).Sect = sect
+	}
+	for _, symn := range obj.ReadOnly {
 		align := dataMaxAlign[symn]
 		if sect.Align < align {
 			sect.Align = align
 		}
 	}
 	datsize = Rnd(datsize, int64(sect.Align))
-	for _, symn := range roSects {
+	for _, symn := range obj.ReadOnly {
 		for _, s := range data[symn] {
 			datsize = aligndatsize(datsize, s)
 			s.Sect = sect
@@ -1580,10 +1647,37 @@ func dodata() {
 			s.Value = int64(uint64(datsize) - sect.Vaddr)
 			datsize += s.Size
 		}
-		checkdatsize(datsize, symn)
+		checkdatsize(ctxt, datsize, symn)
 	}
 	sect.Length = uint64(datsize) - sect.Vaddr
 
+	/* read-only ELF, Mach-O sections */
+	for _, s := range data[obj.SELFROSECT] {
+		sect = addsection(segro, s.Name, 04)
+		sect.Align = symalign(s)
+		datsize = Rnd(datsize, int64(sect.Align))
+		sect.Vaddr = uint64(datsize)
+		s.Sect = sect
+		s.Type = obj.SRODATA
+		s.Value = int64(uint64(datsize) - sect.Vaddr)
+		datsize += s.Size
+		sect.Length = uint64(datsize) - sect.Vaddr
+	}
+	checkdatsize(ctxt, datsize, obj.SELFROSECT)
+
+	for _, s := range data[obj.SMACHOPLT] {
+		sect = addsection(segro, s.Name, 04)
+		sect.Align = symalign(s)
+		datsize = Rnd(datsize, int64(sect.Align))
+		sect.Vaddr = uint64(datsize)
+		s.Sect = sect
+		s.Type = obj.SRODATA
+		s.Value = int64(uint64(datsize) - sect.Vaddr)
+		datsize += s.Size
+		sect.Length = uint64(datsize) - sect.Vaddr
+	}
+	checkdatsize(ctxt, datsize, obj.SMACHOPLT)
+
 	// There is some data that are conceptually read-only but are written to by
 	// relocations. On GNU systems, we can arrange for the dynamic linker to
 	// mprotect sections after relocations are applied by giving them write
@@ -1594,77 +1688,75 @@ func dodata() {
 	// situation.
 	// TODO(mwhudson): It would make sense to do this more widely, but it makes
 	// the system linker segfault on darwin.
-	relro_perms := 04
-	relro_prefix := ""
+	addrelrosection := func(suffix string) *Section {
+		return addsection(segro, suffix, 04)
+	}
 
 	if UseRelro() {
-		relro_perms = 06
-		relro_prefix = ".data.rel.ro"
+		addrelrosection = func(suffix string) *Section {
+			seg := &Segrelrodata
+			if Linkmode == LinkExternal {
+				// Using a separate segment with an external
+				// linker results in some programs moving
+				// their data sections unexpectedly, which
+				// corrupts the moduledata. So we use the
+				// rodata segment and let the external linker
+				// sort out a rel.ro segment.
+				seg = &Segrodata
+			}
+			return addsection(seg, ".data.rel.ro"+suffix, 06)
+		}
 		/* data only written by relocations */
-		sect = addsection(segro, ".data.rel.ro", 06)
+		sect = addrelrosection("")
 
 		sect.Vaddr = 0
-		Linklookup(Ctxt, "runtime.types", 0).Sect = sect
-		Linklookup(Ctxt, "runtime.etypes", 0).Sect = sect
-		relroSects := []int{
-			obj.STYPERELRO,
-			obj.SSTRINGRELRO,
-			obj.SGOSTRINGRELRO,
-			obj.SGOSTRINGHDRRELRO,
-			obj.SGOFUNCRELRO,
-			obj.SGCBITSRELRO,
-			obj.SRODATARELRO,
-			obj.SFUNCTABRELRO,
-		}
-		for _, symn := range relroSects {
+		ctxt.Syms.Lookup("runtime.types", 0).Sect = sect
+		ctxt.Syms.Lookup("runtime.etypes", 0).Sect = sect
+		for _, symnro := range obj.ReadOnly {
+			symn := obj.RelROMap[symnro]
 			align := dataMaxAlign[symn]
 			if sect.Align < align {
 				sect.Align = align
 			}
 		}
 		datsize = Rnd(datsize, int64(sect.Align))
-		for _, symn := range relroSects {
+		for _, symnro := range obj.ReadOnly {
+			symn := obj.RelROMap[symnro]
 			for _, s := range data[symn] {
 				datsize = aligndatsize(datsize, s)
 				if s.Outer != nil && s.Outer.Sect != nil && s.Outer.Sect != sect {
-					Diag("s.Outer (%s) in different section from s (%s)", s.Outer.Name, s.Name)
+					Errorf(s, "s.Outer (%s) in different section from s, %s != %s", s.Outer.Name, s.Outer.Sect.Name, sect.Name)
 				}
 				s.Sect = sect
 				s.Type = obj.SRODATA
 				s.Value = int64(uint64(datsize) - sect.Vaddr)
 				datsize += s.Size
 			}
-			checkdatsize(datsize, symn)
+			checkdatsize(ctxt, datsize, symn)
 		}
 
 		sect.Length = uint64(datsize) - sect.Vaddr
-
 	}
 
 	/* typelink */
-	sect = addsection(segro, relro_prefix+".typelink", relro_perms)
+	sect = addrelrosection(".typelink")
 	sect.Align = dataMaxAlign[obj.STYPELINK]
 	datsize = Rnd(datsize, int64(sect.Align))
 	sect.Vaddr = uint64(datsize)
-	Linklookup(Ctxt, "runtime.typelink", 0).Sect = sect
-	Linklookup(Ctxt, "runtime.etypelink", 0).Sect = sect
-	for _, s := range data[obj.STYPELINK] {
-		datsize = aligndatsize(datsize, s)
-		s.Sect = sect
-		s.Type = obj.SRODATA
-		s.Value = int64(uint64(datsize) - sect.Vaddr)
-		datsize += s.Size
-	}
-	checkdatsize(datsize, obj.STYPELINK)
+	typelink := ctxt.Syms.Lookup("runtime.typelink", 0)
+	typelink.Sect = sect
+	typelink.Type = obj.RODATA
+	datsize += typelink.Size
+	checkdatsize(ctxt, datsize, obj.STYPELINK)
 	sect.Length = uint64(datsize) - sect.Vaddr
 
 	/* itablink */
-	sect = addsection(segro, relro_prefix+".itablink", relro_perms)
+	sect = addrelrosection(".itablink")
 	sect.Align = dataMaxAlign[obj.SITABLINK]
 	datsize = Rnd(datsize, int64(sect.Align))
 	sect.Vaddr = uint64(datsize)
-	Linklookup(Ctxt, "runtime.itablink", 0).Sect = sect
-	Linklookup(Ctxt, "runtime.eitablink", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.itablink", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.eitablink", 0).Sect = sect
 	for _, s := range data[obj.SITABLINK] {
 		datsize = aligndatsize(datsize, s)
 		s.Sect = sect
@@ -1672,16 +1764,16 @@ func dodata() {
 		s.Value = int64(uint64(datsize) - sect.Vaddr)
 		datsize += s.Size
 	}
-	checkdatsize(datsize, obj.SITABLINK)
+	checkdatsize(ctxt, datsize, obj.SITABLINK)
 	sect.Length = uint64(datsize) - sect.Vaddr
 
 	/* gosymtab */
-	sect = addsection(segro, relro_prefix+".gosymtab", relro_perms)
+	sect = addrelrosection(".gosymtab")
 	sect.Align = dataMaxAlign[obj.SSYMTAB]
 	datsize = Rnd(datsize, int64(sect.Align))
 	sect.Vaddr = uint64(datsize)
-	Linklookup(Ctxt, "runtime.symtab", 0).Sect = sect
-	Linklookup(Ctxt, "runtime.esymtab", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.symtab", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.esymtab", 0).Sect = sect
 	for _, s := range data[obj.SSYMTAB] {
 		datsize = aligndatsize(datsize, s)
 		s.Sect = sect
@@ -1689,16 +1781,16 @@ func dodata() {
 		s.Value = int64(uint64(datsize) - sect.Vaddr)
 		datsize += s.Size
 	}
-	checkdatsize(datsize, obj.SSYMTAB)
+	checkdatsize(ctxt, datsize, obj.SSYMTAB)
 	sect.Length = uint64(datsize) - sect.Vaddr
 
 	/* gopclntab */
-	sect = addsection(segro, relro_prefix+".gopclntab", relro_perms)
+	sect = addrelrosection(".gopclntab")
 	sect.Align = dataMaxAlign[obj.SPCLNTAB]
 	datsize = Rnd(datsize, int64(sect.Align))
 	sect.Vaddr = uint64(datsize)
-	Linklookup(Ctxt, "runtime.pclntab", 0).Sect = sect
-	Linklookup(Ctxt, "runtime.epclntab", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.pclntab", 0).Sect = sect
+	ctxt.Syms.Lookup("runtime.epclntab", 0).Sect = sect
 	for _, s := range data[obj.SPCLNTAB] {
 		datsize = aligndatsize(datsize, s)
 		s.Sect = sect
@@ -1706,49 +1798,26 @@ func dodata() {
 		s.Value = int64(uint64(datsize) - sect.Vaddr)
 		datsize += s.Size
 	}
-	checkdatsize(datsize, obj.SRODATA)
+	checkdatsize(ctxt, datsize, obj.SRODATA)
 	sect.Length = uint64(datsize) - sect.Vaddr
 
-	/* read-only ELF, Mach-O sections */
-	for _, s := range data[obj.SELFROSECT] {
-		sect = addsection(segro, s.Name, 04)
-		sect.Align = symalign(s)
-		datsize = Rnd(datsize, int64(sect.Align))
-		sect.Vaddr = uint64(datsize)
-		s.Sect = sect
-		s.Type = obj.SRODATA
-		s.Value = int64(uint64(datsize) - sect.Vaddr)
-		datsize += s.Size
-		sect.Length = uint64(datsize) - sect.Vaddr
-	}
-	checkdatsize(datsize, obj.SELFROSECT)
-
-	for _, s := range data[obj.SMACHOPLT] {
-		sect = addsection(segro, s.Name, 04)
-		sect.Align = symalign(s)
-		datsize = Rnd(datsize, int64(sect.Align))
-		sect.Vaddr = uint64(datsize)
-		s.Sect = sect
-		s.Type = obj.SRODATA
-		s.Value = int64(uint64(datsize) - sect.Vaddr)
-		datsize += s.Size
-		sect.Length = uint64(datsize) - sect.Vaddr
-	}
-	checkdatsize(datsize, obj.SMACHOPLT)
-
 	// 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits.
 	if datsize != int64(uint32(datsize)) {
-		Diag("read-only data segment too large")
+		Errorf(nil, "read-only data segment too large: %d", datsize)
 	}
 
 	for symn := obj.SELFRXSECT; symn < obj.SXREF; symn++ {
 		datap = append(datap, data[symn]...)
 	}
 
-	dwarfgeneratedebugsyms()
+	dwarfgeneratedebugsyms(ctxt)
 
-	var s *LSym
-	for s = dwarfp; s != nil && s.Type == obj.SDWARFSECT; s = s.Next {
+	var s *Symbol
+	var i int
+	for i, s = range dwarfp {
+		if s.Type != obj.SDWARFSECT {
+			break
+		}
 		sect = addsection(&Segdwarf, s.Name, 04)
 		sect.Align = 1
 		datsize = Rnd(datsize, int64(sect.Align))
@@ -1759,14 +1828,17 @@ func dodata() {
 		datsize += s.Size
 		sect.Length = uint64(datsize) - sect.Vaddr
 	}
-	checkdatsize(datsize, obj.SDWARFSECT)
+	checkdatsize(ctxt, datsize, obj.SDWARFSECT)
 
-	if s != nil {
+	if i < len(dwarfp) {
 		sect = addsection(&Segdwarf, ".debug_info", 04)
 		sect.Align = 1
 		datsize = Rnd(datsize, int64(sect.Align))
 		sect.Vaddr = uint64(datsize)
-		for ; s != nil && s.Type == obj.SDWARFINFO; s = s.Next {
+		for _, s := range dwarfp[i:] {
+			if s.Type != obj.SDWARFINFO {
+				break
+			}
 			s.Sect = sect
 			s.Type = obj.SRODATA
 			s.Value = int64(uint64(datsize) - sect.Vaddr)
@@ -1774,7 +1846,7 @@ func dodata() {
 			datsize += s.Size
 		}
 		sect.Length = uint64(datsize) - sect.Vaddr
-		checkdatsize(datsize, obj.SDWARFINFO)
+		checkdatsize(ctxt, datsize, obj.SDWARFINFO)
 	}
 
 	/* number the sections */
@@ -1788,6 +1860,10 @@ func dodata() {
 		sect.Extnum = int16(n)
 		n++
 	}
+	for sect := Segrelrodata.Sect; sect != nil; sect = sect.Next {
+		sect.Extnum = int16(n)
+		n++
+	}
 	for sect := Segdata.Sect; sect != nil; sect = sect.Next {
 		sect.Extnum = int16(n)
 		n++
@@ -1798,38 +1874,53 @@ func dodata() {
 	}
 }
 
-func dodataSect(symn int, syms []*LSym) (result []*LSym, maxAlign int32) {
-	if HEADTYPE == obj.Hdarwin {
+func dodataSect(ctxt *Link, symn obj.SymKind, syms []*Symbol) (result []*Symbol, maxAlign int32) {
+	if Headtype == obj.Hdarwin {
 		// Some symbols may no longer belong in syms
 		// due to movement in machosymorder.
-		newSyms := make([]*LSym, 0, len(syms))
+		newSyms := make([]*Symbol, 0, len(syms))
 		for _, s := range syms {
-			if int(s.Type) == symn {
+			if s.Type == symn {
 				newSyms = append(newSyms, s)
 			}
 		}
 		syms = newSyms
 	}
 
-	symsSort := make([]dataSortKey, len(syms))
-	for i, s := range syms {
+	var head, tail *Symbol
+	symsSort := make([]dataSortKey, 0, len(syms))
+	for _, s := range syms {
 		if s.Attr.OnList() {
 			log.Fatalf("symbol %s listed multiple times", s.Name)
 		}
 		s.Attr |= AttrOnList
 		switch {
 		case s.Size < int64(len(s.P)):
-			Diag("%s: initialize bounds (%d < %d)", s.Name, s.Size, len(s.P))
+			Errorf(s, "initialize bounds (%d < %d)", s.Size, len(s.P))
 		case s.Size < 0:
-			Diag("%s: negative size (%d bytes)", s.Name, s.Size)
+			Errorf(s, "negative size (%d bytes)", s.Size)
 		case s.Size > cutoff:
-			Diag("%s: symbol too large (%d bytes)", s.Name, s.Size)
+			Errorf(s, "symbol too large (%d bytes)", s.Size)
 		}
 
-		symsSort[i] = dataSortKey{
+		// If the usually-special section-marker symbols are being laid
+		// out as regular symbols, put them either at the beginning or
+		// end of their section.
+		if ctxt.DynlinkingGo() && Headtype == obj.Hdarwin {
+			switch s.Name {
+			case "runtime.text", "runtime.bss", "runtime.data", "runtime.types":
+				head = s
+				continue
+			case "runtime.etext", "runtime.ebss", "runtime.edata", "runtime.etypes":
+				tail = s
+				continue
+			}
+		}
+
+		key := dataSortKey{
 			size: s.Size,
 			name: s.Name,
-			lsym: s,
+			sym:  s,
 		}
 
 		switch s.Type {
@@ -1838,23 +1929,29 @@ func dodataSect(symn int, syms []*LSym) (result []*LSym, maxAlign int32) {
 			// from input files. Both are type SELFGOT, so in that case
 			// we skip size comparison and fall through to the name
 			// comparison (conveniently, .got sorts before .toc).
-			symsSort[i].size = 0
-		case obj.STYPELINK:
-			// Sort typelinks by the rtype.string field so the reflect
-			// package can binary search type links.
-			symsSort[i].name = string(decodetype_str(s.R[0].Sym))
+			key.size = 0
 		}
+
+		symsSort = append(symsSort, key)
 	}
 
 	sort.Sort(bySizeAndName(symsSort))
 
+	off := 0
+	if head != nil {
+		syms[0] = head
+		off++
+	}
 	for i, symSort := range symsSort {
-		syms[i] = symSort.lsym
-		align := symalign(symSort.lsym)
+		syms[i+off] = symSort.sym
+		align := symalign(symSort.sym)
 		if maxAlign < align {
 			maxAlign = align
 		}
 	}
+	if tail != nil {
+		syms[len(syms)-1] = tail
+	}
 
 	if Iself && symn == obj.SELFROSECT {
 		// Make .rela and .rela.plt contiguous, the ELF ABI requires this
@@ -1890,27 +1987,27 @@ func dodataSect(symn int, syms []*LSym) (result []*LSym, maxAlign int32) {
 // give us a place to put the Go build ID. On those systems, we put it
 // at the very beginning of the text segment.
 // This ``header'' is read by cmd/go.
-func textbuildid() {
-	if Iself || buildid == "" {
+func (ctxt *Link) textbuildid() {
+	if Iself || Buildmode == BuildmodePlugin || *flagBuildid == "" {
 		return
 	}
 
-	sym := Linklookup(Ctxt, "go.buildid", 0)
+	sym := ctxt.Syms.Lookup("go.buildid", 0)
 	sym.Attr |= AttrReachable
 	// The \xff is invalid UTF-8, meant to make it less likely
 	// to find one of these accidentally.
-	data := "\xff Go build ID: " + strconv.Quote(buildid) + "\n \xff"
+	data := "\xff Go build ID: " + strconv.Quote(*flagBuildid) + "\n \xff"
 	sym.Type = obj.STEXT
 	sym.P = []byte(data)
 	sym.Size = int64(len(sym.P))
 
-	Ctxt.Textp = append(Ctxt.Textp, nil)
-	copy(Ctxt.Textp[1:], Ctxt.Textp)
-	Ctxt.Textp[0] = sym
+	ctxt.Textp = append(ctxt.Textp, nil)
+	copy(ctxt.Textp[1:], ctxt.Textp)
+	ctxt.Textp[0] = sym
 }
 
 // assign addresses to text
-func textaddress() {
+func (ctxt *Link) textaddress() {
 	addsection(&Segtext, ".text", 05)
 
 	// Assign PCs in text segment.
@@ -1919,43 +2016,110 @@ func textaddress() {
 	sect := Segtext.Sect
 
 	sect.Align = int32(Funcalign)
-	Linklookup(Ctxt, "runtime.text", 0).Sect = sect
-	Linklookup(Ctxt, "runtime.etext", 0).Sect = sect
-	if HEADTYPE == obj.Hwindows {
-		Linklookup(Ctxt, ".text", 0).Sect = sect
+
+	text := ctxt.Syms.Lookup("runtime.text", 0)
+	text.Sect = sect
+
+	if ctxt.DynlinkingGo() && Headtype == obj.Hdarwin {
+		etext := ctxt.Syms.Lookup("runtime.etext", 0)
+		etext.Sect = sect
+
+		ctxt.Textp = append(ctxt.Textp, etext, nil)
+		copy(ctxt.Textp[1:], ctxt.Textp)
+		ctxt.Textp[0] = text
+	}
+
+	if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
+		ctxt.Syms.Lookup(".text", 0).Sect = sect
 	}
-	va := uint64(INITTEXT)
+	va := uint64(*FlagTextAddr)
+	n := 1
 	sect.Vaddr = va
-	for _, sym := range Ctxt.Textp {
-		sym.Sect = sect
-		if sym.Type&obj.SSUB != 0 {
-			continue
-		}
-		if sym.Align != 0 {
-			va = uint64(Rnd(int64(va), int64(sym.Align)))
-		} else {
-			va = uint64(Rnd(int64(va), int64(Funcalign)))
-		}
-		sym.Value = 0
-		for sub := sym; sub != nil; sub = sub.Sub {
-			sub.Value += int64(va)
-		}
-		if sym.Size == 0 && sym.Sub != nil {
-			Ctxt.Cursym = sym
-		}
-		if sym.Size < MINFUNC {
-			va += MINFUNC // spacing required for findfunctab
-		} else {
-			va += uint64(sym.Size)
+	ntramps := 0
+	for _, sym := range ctxt.Textp {
+		sect, n, va = assignAddress(ctxt, sect, n, sym, va)
+
+		trampoline(ctxt, sym) // resolve jumps, may add trampolines if jump too far
+
+		// lay down trampolines after each function
+		for ; ntramps < len(ctxt.tramps); ntramps++ {
+			tramp := ctxt.tramps[ntramps]
+			sect, n, va = assignAddress(ctxt, sect, n, tramp, va)
 		}
 	}
 
 	sect.Length = va - sect.Vaddr
+	ctxt.Syms.Lookup("runtime.etext", 0).Sect = sect
+
+	// merge tramps into Textp, keeping Textp in address order
+	if ntramps != 0 {
+		newtextp := make([]*Symbol, 0, len(ctxt.Textp)+ntramps)
+		i := 0
+		for _, sym := range ctxt.Textp {
+			for ; i < ntramps && ctxt.tramps[i].Value < sym.Value; i++ {
+				newtextp = append(newtextp, ctxt.tramps[i])
+			}
+			newtextp = append(newtextp, sym)
+		}
+		newtextp = append(newtextp, ctxt.tramps[i:ntramps]...)
+
+		ctxt.Textp = newtextp
+	}
+}
+
+// assigns address for a text symbol, returns (possibly new) section, its number, and the address
+// Note: once we have trampoline insertion support for external linking, this function
+// will not need to create new text sections, and so no need to return sect and n.
+func assignAddress(ctxt *Link, sect *Section, n int, sym *Symbol, va uint64) (*Section, int, uint64) {
+	sym.Sect = sect
+	if sym.Type&obj.SSUB != 0 {
+		return sect, n, va
+	}
+	if sym.Align != 0 {
+		va = uint64(Rnd(int64(va), int64(sym.Align)))
+	} else {
+		va = uint64(Rnd(int64(va), int64(Funcalign)))
+	}
+	sym.Value = 0
+	for sub := sym; sub != nil; sub = sub.Sub {
+		sub.Value += int64(va)
+	}
+
+	funcsize := uint64(MINFUNC) // spacing required for findfunctab
+	if sym.Size > MINFUNC {
+		funcsize = uint64(sym.Size)
+	}
+
+	// On ppc64x a text section should not be larger than 2^26 bytes due to the size of
+	// call target offset field in the bl instruction.  Splitting into smaller text
+	// sections smaller than this limit allows the GNU linker to modify the long calls
+	// appropriately.  The limit allows for the space needed for tables inserted by the linker.
+
+	// If this function doesn't fit in the current text section, then create a new one.
+
+	// Only break at outermost syms.
+
+	if SysArch.InFamily(sys.PPC64) && sym.Outer == nil && Iself && Linkmode == LinkExternal && va-sect.Vaddr+funcsize > 0x1c00000 {
+
+		// Set the length for the previous text section
+		sect.Length = va - sect.Vaddr
+
+		// Create new section, set the starting Vaddr
+		sect = addsection(&Segtext, ".text", 05)
+		sect.Vaddr = va
+
+		// Create a symbol for the start of the secondary text sections
+		ctxt.Syms.Lookup(fmt.Sprintf("runtime.text.%d", n), 0).Sect = sect
+		n++
+	}
+	va += funcsize
+
+	return sect, n, va
 }
 
 // assign addresses
-func address() {
-	va := uint64(INITTEXT)
+func (ctxt *Link) address() {
+	va := uint64(*FlagTextAddr)
 	Segtext.Rwx = 05
 	Segtext.Vaddr = va
 	Segtext.Fileoff = uint64(HEADR)
@@ -1965,16 +2129,27 @@ func address() {
 		va += s.Length
 	}
 
-	Segtext.Length = va - uint64(INITTEXT)
+	Segtext.Length = va - uint64(*FlagTextAddr)
 	Segtext.Filelen = Segtext.Length
-	if HEADTYPE == obj.Hnacl {
+	if Headtype == obj.Hnacl {
 		va += 32 // room for the "halt sled"
 	}
 
 	if Segrodata.Sect != nil {
 		// align to page boundary so as not to mix
 		// rodata and executable text.
-		va = uint64(Rnd(int64(va), int64(INITRND)))
+		//
+		// Note: gold or GNU ld will reduce the size of the executable
+		// file by arranging for the relro segment to end at a page
+		// boundary, and overlap the end of the text segment with the
+		// start of the relro segment in the file.  The PT_LOAD segments
+		// will be such that the last page of the text segment will be
+		// mapped twice, once r-x and once starting out rw- and, after
+		// relocation processing, changed to r--.
+		//
+		// Ideally the last page of the text segment would not be
+		// writable even for this short period.
+		va = uint64(Rnd(int64(va), int64(*FlagRound)))
 
 		Segrodata.Rwx = 04
 		Segrodata.Vaddr = va
@@ -1989,16 +2164,34 @@ func address() {
 		Segrodata.Length = va - Segrodata.Vaddr
 		Segrodata.Filelen = Segrodata.Length
 	}
+	if Segrelrodata.Sect != nil {
+		// align to page boundary so as not to mix
+		// rodata, rel-ro data, and executable text.
+		va = uint64(Rnd(int64(va), int64(*FlagRound)))
+
+		Segrelrodata.Rwx = 06
+		Segrelrodata.Vaddr = va
+		Segrelrodata.Fileoff = va - Segrodata.Vaddr + Segrodata.Fileoff
+		Segrelrodata.Filelen = 0
+		for s := Segrelrodata.Sect; s != nil; s = s.Next {
+			va = uint64(Rnd(int64(va), int64(s.Align)))
+			s.Vaddr = va
+			va += s.Length
+		}
+
+		Segrelrodata.Length = va - Segrelrodata.Vaddr
+		Segrelrodata.Filelen = Segrelrodata.Length
+	}
 
-	va = uint64(Rnd(int64(va), int64(INITRND)))
+	va = uint64(Rnd(int64(va), int64(*FlagRound)))
 	Segdata.Rwx = 06
 	Segdata.Vaddr = va
 	Segdata.Fileoff = va - Segtext.Vaddr + Segtext.Fileoff
 	Segdata.Filelen = 0
-	if HEADTYPE == obj.Hwindows {
+	if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
 		Segdata.Fileoff = Segtext.Fileoff + uint64(Rnd(int64(Segtext.Length), PEFILEALIGN))
 	}
-	if HEADTYPE == obj.Hplan9 {
+	if Headtype == obj.Hplan9 {
 		Segdata.Fileoff = Segtext.Fileoff + Segtext.Filelen
 	}
 	var data *Section
@@ -2033,12 +2226,12 @@ func address() {
 
 	Segdata.Filelen = bss.Vaddr - Segdata.Vaddr
 
-	va = uint64(Rnd(int64(va), int64(INITRND)))
+	va = uint64(Rnd(int64(va), int64(*FlagRound)))
 	Segdwarf.Rwx = 06
 	Segdwarf.Vaddr = va
-	Segdwarf.Fileoff = Segdata.Fileoff + uint64(Rnd(int64(Segdata.Filelen), int64(INITRND)))
+	Segdwarf.Fileoff = Segdata.Fileoff + uint64(Rnd(int64(Segdata.Filelen), int64(*FlagRound)))
 	Segdwarf.Filelen = 0
-	if HEADTYPE == obj.Hwindows {
+	if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
 		Segdwarf.Fileoff = Segdata.Fileoff + uint64(Rnd(int64(Segdata.Filelen), int64(PEFILEALIGN)))
 	}
 	for s := Segdwarf.Sect; s != nil; s = s.Next {
@@ -2048,7 +2241,7 @@ func address() {
 		}
 		s.Vaddr = va
 		va += uint64(vlen)
-		if HEADTYPE == obj.Hwindows {
+		if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
 			va = uint64(Rnd(int64(va), PEFILEALIGN))
 		}
 		Segdwarf.Length = va - Segdwarf.Vaddr
@@ -2056,27 +2249,21 @@ func address() {
 
 	Segdwarf.Filelen = va - Segdwarf.Vaddr
 
-	text := Segtext.Sect
-	var rodata *Section
-	if Segrodata.Sect != nil {
-		rodata = Segrodata.Sect
-	} else {
-		rodata = text.Next
-	}
-	var relrodata *Section
-	typelink := rodata.Next
-	if UseRelro() {
-		// There is another section (.data.rel.ro) when building a shared
-		// object on elf systems.
-		relrodata = typelink
-		typelink = typelink.Next
+	var (
+		text     = Segtext.Sect
+		rodata   = ctxt.Syms.Lookup("runtime.rodata", 0).Sect
+		itablink = ctxt.Syms.Lookup("runtime.itablink", 0).Sect
+		symtab   = ctxt.Syms.Lookup("runtime.symtab", 0).Sect
+		pclntab  = ctxt.Syms.Lookup("runtime.pclntab", 0).Sect
+		types    = ctxt.Syms.Lookup("runtime.types", 0).Sect
+	)
+	lasttext := text
+	// Could be multiple .text sections
+	for sect := text.Next; sect != nil && sect.Name == ".text"; sect = sect.Next {
+		lasttext = sect
 	}
-	itablink := typelink.Next
-	symtab := itablink.Next
-	pclntab := symtab.Next
 
 	for _, s := range datap {
-		Ctxt.Cursym = s
 		if s.Sect != nil {
 			s.Value += int64(s.Sect.Vaddr)
 		}
@@ -2085,8 +2272,7 @@ func address() {
 		}
 	}
 
-	for sym := dwarfp; sym != nil; sym = sym.Next {
-		Ctxt.Cursym = sym
+	for _, sym := range dwarfp {
 		if sym.Sect != nil {
 			sym.Value += int64(sym.Sect.Vaddr)
 		}
@@ -2096,52 +2282,66 @@ func address() {
 	}
 
 	if Buildmode == BuildmodeShared {
-		s := Linklookup(Ctxt, "go.link.abihashbytes", 0)
-		sectSym := Linklookup(Ctxt, ".note.go.abihash", 0)
+		s := ctxt.Syms.Lookup("go.link.abihashbytes", 0)
+		sectSym := ctxt.Syms.Lookup(".note.go.abihash", 0)
 		s.Sect = sectSym.Sect
 		s.Value = int64(sectSym.Sect.Vaddr + 16)
 	}
 
-	types := relrodata
-	if types == nil {
-		types = rodata
+	ctxt.xdefine("runtime.text", obj.STEXT, int64(text.Vaddr))
+	ctxt.xdefine("runtime.etext", obj.STEXT, int64(lasttext.Vaddr+lasttext.Length))
+	if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
+		ctxt.xdefine(".text", obj.STEXT, int64(text.Vaddr))
 	}
 
-	xdefine("runtime.text", obj.STEXT, int64(text.Vaddr))
-	xdefine("runtime.etext", obj.STEXT, int64(text.Vaddr+text.Length))
-	if HEADTYPE == obj.Hwindows {
-		xdefine(".text", obj.STEXT, int64(text.Vaddr))
+	// If there are multiple text sections, create runtime.text.n for
+	// their section Vaddr, using n for index
+	n := 1
+	for sect := Segtext.Sect.Next; sect != nil && sect.Name == ".text"; sect = sect.Next {
+		symname := fmt.Sprintf("runtime.text.%d", n)
+		ctxt.xdefine(symname, obj.STEXT, int64(sect.Vaddr))
+		n++
 	}
-	xdefine("runtime.rodata", obj.SRODATA, int64(rodata.Vaddr))
-	xdefine("runtime.erodata", obj.SRODATA, int64(rodata.Vaddr+rodata.Length))
-	xdefine("runtime.types", obj.SRODATA, int64(types.Vaddr))
-	xdefine("runtime.etypes", obj.SRODATA, int64(types.Vaddr+types.Length))
-	xdefine("runtime.typelink", obj.SRODATA, int64(typelink.Vaddr))
-	xdefine("runtime.etypelink", obj.SRODATA, int64(typelink.Vaddr+typelink.Length))
-	xdefine("runtime.itablink", obj.SRODATA, int64(itablink.Vaddr))
-	xdefine("runtime.eitablink", obj.SRODATA, int64(itablink.Vaddr+itablink.Length))
 
-	sym := Linklookup(Ctxt, "runtime.gcdata", 0)
+	ctxt.xdefine("runtime.rodata", obj.SRODATA, int64(rodata.Vaddr))
+	ctxt.xdefine("runtime.erodata", obj.SRODATA, int64(rodata.Vaddr+rodata.Length))
+	ctxt.xdefine("runtime.types", obj.SRODATA, int64(types.Vaddr))
+	ctxt.xdefine("runtime.etypes", obj.SRODATA, int64(types.Vaddr+types.Length))
+	ctxt.xdefine("runtime.itablink", obj.SRODATA, int64(itablink.Vaddr))
+	ctxt.xdefine("runtime.eitablink", obj.SRODATA, int64(itablink.Vaddr+itablink.Length))
+
+	sym := ctxt.Syms.Lookup("runtime.gcdata", 0)
 	sym.Attr |= AttrLocal
-	xdefine("runtime.egcdata", obj.SRODATA, Symaddr(sym)+sym.Size)
-	Linklookup(Ctxt, "runtime.egcdata", 0).Sect = sym.Sect
+	ctxt.xdefine("runtime.egcdata", obj.SRODATA, Symaddr(sym)+sym.Size)
+	ctxt.Syms.Lookup("runtime.egcdata", 0).Sect = sym.Sect
 
-	sym = Linklookup(Ctxt, "runtime.gcbss", 0)
+	sym = ctxt.Syms.Lookup("runtime.gcbss", 0)
 	sym.Attr |= AttrLocal
-	xdefine("runtime.egcbss", obj.SRODATA, Symaddr(sym)+sym.Size)
-	Linklookup(Ctxt, "runtime.egcbss", 0).Sect = sym.Sect
-
-	xdefine("runtime.symtab", obj.SRODATA, int64(symtab.Vaddr))
-	xdefine("runtime.esymtab", obj.SRODATA, int64(symtab.Vaddr+symtab.Length))
-	xdefine("runtime.pclntab", obj.SRODATA, int64(pclntab.Vaddr))
-	xdefine("runtime.epclntab", obj.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
-	xdefine("runtime.noptrdata", obj.SNOPTRDATA, int64(noptr.Vaddr))
-	xdefine("runtime.enoptrdata", obj.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length))
-	xdefine("runtime.bss", obj.SBSS, int64(bss.Vaddr))
-	xdefine("runtime.ebss", obj.SBSS, int64(bss.Vaddr+bss.Length))
-	xdefine("runtime.data", obj.SDATA, int64(data.Vaddr))
-	xdefine("runtime.edata", obj.SDATA, int64(data.Vaddr+data.Length))
-	xdefine("runtime.noptrbss", obj.SNOPTRBSS, int64(noptrbss.Vaddr))
-	xdefine("runtime.enoptrbss", obj.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length))
-	xdefine("runtime.end", obj.SBSS, int64(Segdata.Vaddr+Segdata.Length))
+	ctxt.xdefine("runtime.egcbss", obj.SRODATA, Symaddr(sym)+sym.Size)
+	ctxt.Syms.Lookup("runtime.egcbss", 0).Sect = sym.Sect
+
+	ctxt.xdefine("runtime.symtab", obj.SRODATA, int64(symtab.Vaddr))
+	ctxt.xdefine("runtime.esymtab", obj.SRODATA, int64(symtab.Vaddr+symtab.Length))
+	ctxt.xdefine("runtime.pclntab", obj.SRODATA, int64(pclntab.Vaddr))
+	ctxt.xdefine("runtime.epclntab", obj.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
+	ctxt.xdefine("runtime.noptrdata", obj.SNOPTRDATA, int64(noptr.Vaddr))
+	ctxt.xdefine("runtime.enoptrdata", obj.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length))
+	ctxt.xdefine("runtime.bss", obj.SBSS, int64(bss.Vaddr))
+	ctxt.xdefine("runtime.ebss", obj.SBSS, int64(bss.Vaddr+bss.Length))
+	ctxt.xdefine("runtime.data", obj.SDATA, int64(data.Vaddr))
+	ctxt.xdefine("runtime.edata", obj.SDATA, int64(data.Vaddr+data.Length))
+	ctxt.xdefine("runtime.noptrbss", obj.SNOPTRBSS, int64(noptrbss.Vaddr))
+	ctxt.xdefine("runtime.enoptrbss", obj.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length))
+	ctxt.xdefine("runtime.end", obj.SBSS, int64(Segdata.Vaddr+Segdata.Length))
+}
+
+// add a trampoline with symbol s (to be laid down after the current function)
+func (ctxt *Link) AddTramp(s *Symbol) {
+	s.Type = obj.STEXT
+	s.Attr |= AttrReachable
+	s.Attr |= AttrOnList
+	ctxt.tramps = append(ctxt.tramps, s)
+	if *FlagDebugTramp > 0 && ctxt.Debugvlog > 0 {
+		ctxt.Logf("trampoline %s inserted\n", s)
+	}
 }
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index aaed6cd..ae51681 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -15,7 +15,7 @@ import (
 // deadcode marks all reachable symbols.
 //
 // The basis of the dead code elimination is a flood fill of symbols,
-// following their relocations, beginning at INITENTRY.
+// following their relocations, beginning at *flagEntrySymbol.
 //
 // This flood fill is wrapped in logic for pruning unused methods.
 // All methods are mentioned by relocations on their receiver's *rtype.
@@ -45,8 +45,8 @@ import (
 //
 // Any unreached text symbols are removed from ctxt.Textp.
 func deadcode(ctxt *Link) {
-	if Debug['v'] != 0 {
-		fmt.Fprintf(ctxt.Bso, "%5.2f deadcode\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f deadcode\n", obj.Cputime())
 	}
 
 	d := &deadcodepass{
@@ -55,15 +55,15 @@ func deadcode(ctxt *Link) {
 	}
 
 	// First, flood fill any symbols directly reachable in the call
-	// graph from INITENTRY. Ignore all methods not directly called.
+	// graph from *flagEntrySymbol. Ignore all methods not directly called.
 	d.init()
 	d.flood()
 
-	callSym := Linkrlookup(ctxt, "reflect.Value.Call", 0)
-	methSym := Linkrlookup(ctxt, "reflect.Value.Method", 0)
+	callSym := ctxt.Syms.ROLookup("reflect.Value.Call", 0)
+	methSym := ctxt.Syms.ROLookup("reflect.Value.Method", 0)
 	reflectSeen := false
 
-	if DynlinkingGo() {
+	if ctxt.DynlinkingGo() {
 		// Exported methods may satisfy interfaces we don't know
 		// about yet when dynamically linking.
 		reflectSeen = true
@@ -108,18 +108,17 @@ func deadcode(ctxt *Link) {
 	}
 
 	if Buildmode != BuildmodeShared {
-		// Keep a typelink or itablink if the symbol it points at is being kept.
-		// (When BuildmodeShared, always keep typelinks and itablinks.)
-		for _, s := range ctxt.Allsym {
-			if strings.HasPrefix(s.Name, "go.typelink.") ||
-				strings.HasPrefix(s.Name, "go.itablink.") {
+		// Keep a itablink if the symbol it points at is being kept.
+		// (When BuildmodeShared, always keep itablinks.)
+		for _, s := range ctxt.Syms.Allsym {
+			if strings.HasPrefix(s.Name, "go.itablink.") {
 				s.Attr.Set(AttrReachable, len(s.R) == 1 && s.R[0].Sym.Attr.Reachable())
 			}
 		}
 	}
 
 	// Remove dead text but keep file information (z symbols).
-	textp := make([]*LSym, 0, len(ctxt.Textp))
+	textp := make([]*Symbol, 0, len(ctxt.Textp))
 	for _, s := range ctxt.Textp {
 		if s.Attr.Reachable() {
 			textp = append(textp, s)
@@ -154,11 +153,11 @@ var markextra = []string{
 // the reflect.method struct: mtyp, ifn, and tfn.
 type methodref struct {
 	m   methodsig
-	src *LSym     // receiver type symbol
+	src *Symbol   // receiver type symbol
 	r   [3]*Reloc // R_METHODOFF relocations to fields of runtime.method
 }
 
-func (m methodref) ifn() *LSym { return m.r[1].Sym }
+func (m methodref) ifn() *Symbol { return m.r[1].Sym }
 
 func (m methodref) isExported() bool {
 	for _, r := range m.m {
@@ -170,7 +169,7 @@ func (m methodref) isExported() bool {
 // deadcodepass holds state for the deadcode flood fill.
 type deadcodepass struct {
 	ctxt            *Link
-	markQueue       []*LSym            // symbols to flood fill in next pass
+	markQueue       []*Symbol          // symbols to flood fill in next pass
 	ifaceMethod     map[methodsig]bool // methods declared in reached interfaces
 	markableMethods []methodref        // methods of reached types
 	reflectMethod   bool
@@ -180,8 +179,8 @@ func (d *deadcodepass) cleanupReloc(r *Reloc) {
 	if r.Sym.Attr.Reachable() {
 		r.Type = obj.R_ADDROFF
 	} else {
-		if Debug['v'] > 1 {
-			fmt.Fprintf(d.ctxt.Bso, "removing method %s\n", r.Sym.Name)
+		if d.ctxt.Debugvlog > 1 {
+			d.ctxt.Logf("removing method %s\n", r.Sym.Name)
 		}
 		r.Sym = nil
 		r.Siz = 0
@@ -189,14 +188,14 @@ func (d *deadcodepass) cleanupReloc(r *Reloc) {
 }
 
 // mark appends a symbol to the mark queue for flood filling.
-func (d *deadcodepass) mark(s, parent *LSym) {
+func (d *deadcodepass) mark(s, parent *Symbol) {
 	if s == nil || s.Attr.Reachable() {
 		return
 	}
 	if s.Attr.ReflectMethod() {
 		d.reflectMethod = true
 	}
-	if flag_dumpdep {
+	if *flagDumpDep {
 		p := "_"
 		if parent != nil {
 			p = parent.Name
@@ -217,13 +216,13 @@ func (d *deadcodepass) markMethod(m methodref) {
 }
 
 // init marks all initial symbols as reachable.
-// In a typical binary, this is INITENTRY.
+// In a typical binary, this is *flagEntrySymbol.
 func (d *deadcodepass) init() {
 	var names []string
 
 	if SysArch.Family == sys.ARM {
 		// mark some functions that are only referenced after linker code editing
-		if d.ctxt.Goarm == 5 {
+		if obj.GOARM == 5 {
 			names = append(names, "_sfloat")
 		}
 		names = append(names, "runtime.read_tls_fallback")
@@ -232,7 +231,7 @@ func (d *deadcodepass) init() {
 	if Buildmode == BuildmodeShared {
 		// Mark all symbols defined in this library as reachable when
 		// building a shared library.
-		for _, s := range d.ctxt.Allsym {
+		for _, s := range d.ctxt.Syms.Allsym {
 			if s.Type != 0 && s.Type != obj.SDYNIMPORT {
 				d.mark(s, nil)
 			}
@@ -240,9 +239,20 @@ func (d *deadcodepass) init() {
 	} else {
 		// In a normal binary, start at main.main and the init
 		// functions and mark what is reachable from there.
-		names = append(names, INITENTRY)
-		if Linkshared && Buildmode == BuildmodeExe {
+		names = append(names, *flagEntrySymbol)
+		if *FlagLinkshared && (Buildmode == BuildmodeExe || Buildmode == BuildmodePIE) {
 			names = append(names, "main.main", "main.init")
+		} else if Buildmode == BuildmodePlugin {
+			names = append(names, *flagPluginPath+".init", *flagPluginPath+".main", "go.plugin.tabs")
+
+			// We don't keep the go.plugin.exports symbol,
+			// but we do keep the symbols it refers to.
+			exports := d.ctxt.Syms.ROLookup("go.plugin.exports", 0)
+			if exports != nil {
+				for _, r := range exports.R {
+					d.mark(r.Sym, nil)
+				}
+			}
 		}
 		for _, name := range markextra {
 			names = append(names, name)
@@ -253,7 +263,7 @@ func (d *deadcodepass) init() {
 	}
 
 	for _, name := range names {
-		d.mark(Linkrlookup(d.ctxt, name, 0), nil)
+		d.mark(d.ctxt.Syms.ROLookup(name, 0), nil)
 	}
 }
 
@@ -264,8 +274,8 @@ func (d *deadcodepass) flood() {
 		s := d.markQueue[0]
 		d.markQueue = d.markQueue[1:]
 		if s.Type == obj.STEXT {
-			if Debug['v'] > 1 {
-				fmt.Fprintf(d.ctxt.Bso, "marktext %s\n", s.Name)
+			if d.ctxt.Debugvlog > 1 {
+				d.ctxt.Logf("marktext %s\n", s.Name)
 			}
 			if s.FuncInfo != nil {
 				for _, a := range s.FuncInfo.Autom {
@@ -276,10 +286,15 @@ func (d *deadcodepass) flood() {
 		}
 
 		if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' {
-			if decodetype_kind(s)&kindMask == kindInterface {
-				for _, sig := range decodetype_ifacemethods(s) {
-					if Debug['v'] > 1 {
-						fmt.Fprintf(d.ctxt.Bso, "reached iface method: %s\n", sig)
+			if len(s.P) == 0 {
+				// Probably a bug. The undefined symbol check
+				// later will give a better error than deadcode.
+				continue
+			}
+			if decodetypeKind(s)&kindMask == kindInterface {
+				for _, sig := range decodeIfaceMethods(d.ctxt.Arch, s) {
+					if d.ctxt.Debugvlog > 1 {
+						d.ctxt.Logf("reached iface method: %s\n", sig)
 					}
 					d.ifaceMethod[sig] = true
 				}
@@ -293,6 +308,12 @@ func (d *deadcodepass) flood() {
 			if r.Sym == nil {
 				continue
 			}
+			if r.Type == obj.R_WEAKADDROFF {
+				// An R_WEAKADDROFF relocation is not reason
+				// enough to mark the pointed-to symbol as
+				// reachable.
+				continue
+			}
 			if r.Type != obj.R_METHODOFF {
 				d.mark(r.Sym, s)
 				continue
@@ -315,7 +336,7 @@ func (d *deadcodepass) flood() {
 			// Decode runtime type information for type methods
 			// to help work out which methods can be called
 			// dynamically via interfaces.
-			methodsigs := decodetype_methods(s)
+			methodsigs := decodetypeMethods(d.ctxt.Arch, s)
 			if len(methods) != len(methodsigs) {
 				panic(fmt.Sprintf("%q has %d method relocations for %d methods", s.Name, len(methods), len(methodsigs)))
 			}
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
index a1eef03..d111b00 100644
--- a/src/cmd/link/internal/ld/decodesym.go
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -28,7 +28,7 @@ const (
 	tflagExtraStar = 1 << 1
 )
 
-func decode_reloc(s *LSym, off int32) *Reloc {
+func decodeReloc(s *Symbol, off int32) *Reloc {
 	for i := range s.R {
 		if s.R[i].Off == off {
 			return &s.R[i]
@@ -37,22 +37,22 @@ func decode_reloc(s *LSym, off int32) *Reloc {
 	return nil
 }
 
-func decode_reloc_sym(s *LSym, off int32) *LSym {
-	r := decode_reloc(s, off)
+func decodeRelocSym(s *Symbol, off int32) *Symbol {
+	r := decodeReloc(s, off)
 	if r == nil {
 		return nil
 	}
 	return r.Sym
 }
 
-func decode_inuxi(p []byte, sz int) uint64 {
+func decodeInuxi(arch *sys.Arch, p []byte, sz int) uint64 {
 	switch sz {
 	case 2:
-		return uint64(Ctxt.Arch.ByteOrder.Uint16(p))
+		return uint64(arch.ByteOrder.Uint16(p))
 	case 4:
-		return uint64(Ctxt.Arch.ByteOrder.Uint32(p))
+		return uint64(arch.ByteOrder.Uint32(p))
 	case 8:
-		return Ctxt.Arch.ByteOrder.Uint64(p)
+		return arch.ByteOrder.Uint64(p)
 	default:
 		Exitf("dwarf: decode inuxi %d", sz)
 		panic("unreachable")
@@ -64,33 +64,33 @@ func structfieldSize() int { return 3 * SysArch.PtrSize }       // runtime.struc
 func uncommonSize() int    { return 4 + 2 + 2 + 4 + 4 }         // runtime.uncommontype
 
 // Type.commonType.kind
-func decodetype_kind(s *LSym) uint8 {
+func decodetypeKind(s *Symbol) uint8 {
 	return s.P[2*SysArch.PtrSize+7] & obj.KindMask //  0x13 / 0x1f
 }
 
 // Type.commonType.kind
-func decodetype_usegcprog(s *LSym) uint8 {
+func decodetypeUsegcprog(s *Symbol) uint8 {
 	return s.P[2*SysArch.PtrSize+7] & obj.KindGCProg //  0x13 / 0x1f
 }
 
 // Type.commonType.size
-func decodetype_size(s *LSym) int64 {
-	return int64(decode_inuxi(s.P, SysArch.PtrSize)) // 0x8 / 0x10
+func decodetypeSize(arch *sys.Arch, s *Symbol) int64 {
+	return int64(decodeInuxi(arch, s.P, SysArch.PtrSize)) // 0x8 / 0x10
 }
 
 // Type.commonType.ptrdata
-func decodetype_ptrdata(s *LSym) int64 {
-	return int64(decode_inuxi(s.P[SysArch.PtrSize:], SysArch.PtrSize)) // 0x8 / 0x10
+func decodetypePtrdata(arch *sys.Arch, s *Symbol) int64 {
+	return int64(decodeInuxi(arch, s.P[SysArch.PtrSize:], SysArch.PtrSize)) // 0x8 / 0x10
 }
 
 // Type.commonType.tflag
-func decodetype_hasUncommon(s *LSym) bool {
+func decodetypeHasUncommon(s *Symbol) bool {
 	return s.P[2*SysArch.PtrSize+4]&tflagUncommon != 0
 }
 
 // Find the elf.Section of a given shared library that contains a given address.
-func findShlibSection(path string, addr uint64) *elf.Section {
-	for _, shlib := range Ctxt.Shlibs {
+func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section {
+	for _, shlib := range ctxt.Shlibs {
 		if shlib.Path == path {
 			for _, sect := range shlib.File.Sections {
 				if sect.Addr <= addr && addr <= sect.Addr+sect.Size {
@@ -103,42 +103,42 @@ func findShlibSection(path string, addr uint64) *elf.Section {
 }
 
 // Type.commonType.gc
-func decodetype_gcprog(s *LSym) []byte {
+func decodetypeGcprog(ctxt *Link, s *Symbol) []byte {
 	if s.Type == obj.SDYNIMPORT {
-		addr := decodetype_gcprog_shlib(s)
-		sect := findShlibSection(s.File, addr)
+		addr := decodetypeGcprogShlib(ctxt, s)
+		sect := findShlibSection(ctxt, s.File, addr)
 		if sect != nil {
 			// A gcprog is a 4-byte uint32 indicating length, followed by
 			// the actual program.
 			progsize := make([]byte, 4)
 			sect.ReadAt(progsize, int64(addr-sect.Addr))
-			progbytes := make([]byte, Ctxt.Arch.ByteOrder.Uint32(progsize))
+			progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize))
 			sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
 			return append(progsize, progbytes...)
 		}
 		Exitf("cannot find gcprog for %s", s.Name)
 		return nil
 	}
-	return decode_reloc_sym(s, 2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize)).P
+	return decodeRelocSym(s, 2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize)).P
 }
 
-func decodetype_gcprog_shlib(s *LSym) uint64 {
+func decodetypeGcprogShlib(ctxt *Link, s *Symbol) uint64 {
 	if SysArch.Family == sys.ARM64 {
-		for _, shlib := range Ctxt.Shlibs {
+		for _, shlib := range ctxt.Shlibs {
 			if shlib.Path == s.File {
-				return shlib.gcdata_addresses[s]
+				return shlib.gcdataAddresses[s]
 			}
 		}
 		return 0
 	}
-	return decode_inuxi(s.P[2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize):], SysArch.PtrSize)
+	return decodeInuxi(ctxt.Arch, s.P[2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize):], SysArch.PtrSize)
 }
 
-func decodetype_gcmask(s *LSym) []byte {
+func decodetypeGcmask(ctxt *Link, s *Symbol) []byte {
 	if s.Type == obj.SDYNIMPORT {
-		addr := decodetype_gcprog_shlib(s)
-		ptrdata := decodetype_ptrdata(s)
-		sect := findShlibSection(s.File, addr)
+		addr := decodetypeGcprogShlib(ctxt, s)
+		ptrdata := decodetypePtrdata(ctxt.Arch, s)
+		sect := findShlibSection(ctxt, s.File, addr)
 		if sect != nil {
 			r := make([]byte, ptrdata/int64(SysArch.PtrSize))
 			sect.ReadAt(r, int64(addr-sect.Addr))
@@ -147,93 +147,93 @@ func decodetype_gcmask(s *LSym) []byte {
 		Exitf("cannot find gcmask for %s", s.Name)
 		return nil
 	}
-	mask := decode_reloc_sym(s, 2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize))
+	mask := decodeRelocSym(s, 2*int32(SysArch.PtrSize)+8+1*int32(SysArch.PtrSize))
 	return mask.P
 }
 
 // Type.ArrayType.elem and Type.SliceType.Elem
-func decodetype_arrayelem(s *LSym) *LSym {
-	return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30
+func decodetypeArrayElem(s *Symbol) *Symbol {
+	return decodeRelocSym(s, int32(commonsize())) // 0x1c / 0x30
 }
 
-func decodetype_arraylen(s *LSym) int64 {
-	return int64(decode_inuxi(s.P[commonsize()+2*SysArch.PtrSize:], SysArch.PtrSize))
+func decodetypeArrayLen(arch *sys.Arch, s *Symbol) int64 {
+	return int64(decodeInuxi(arch, s.P[commonsize()+2*SysArch.PtrSize:], SysArch.PtrSize))
 }
 
 // Type.PtrType.elem
-func decodetype_ptrelem(s *LSym) *LSym {
-	return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30
+func decodetypePtrElem(s *Symbol) *Symbol {
+	return decodeRelocSym(s, int32(commonsize())) // 0x1c / 0x30
 }
 
 // Type.MapType.key, elem
-func decodetype_mapkey(s *LSym) *LSym {
-	return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30
+func decodetypeMapKey(s *Symbol) *Symbol {
+	return decodeRelocSym(s, int32(commonsize())) // 0x1c / 0x30
 }
 
-func decodetype_mapvalue(s *LSym) *LSym {
-	return decode_reloc_sym(s, int32(commonsize())+int32(SysArch.PtrSize)) // 0x20 / 0x38
+func decodetypeMapValue(s *Symbol) *Symbol {
+	return decodeRelocSym(s, int32(commonsize())+int32(SysArch.PtrSize)) // 0x20 / 0x38
 }
 
 // Type.ChanType.elem
-func decodetype_chanelem(s *LSym) *LSym {
-	return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30
+func decodetypeChanElem(s *Symbol) *Symbol {
+	return decodeRelocSym(s, int32(commonsize())) // 0x1c / 0x30
 }
 
 // Type.FuncType.dotdotdot
-func decodetype_funcdotdotdot(s *LSym) bool {
-	return uint16(decode_inuxi(s.P[commonsize()+2:], 2))&(1<<15) != 0
+func decodetypeFuncDotdotdot(arch *sys.Arch, s *Symbol) bool {
+	return uint16(decodeInuxi(arch, s.P[commonsize()+2:], 2))&(1<<15) != 0
 }
 
 // Type.FuncType.inCount
-func decodetype_funcincount(s *LSym) int {
-	return int(decode_inuxi(s.P[commonsize():], 2))
+func decodetypeFuncInCount(arch *sys.Arch, s *Symbol) int {
+	return int(decodeInuxi(arch, s.P[commonsize():], 2))
 }
 
-func decodetype_funcoutcount(s *LSym) int {
-	return int(uint16(decode_inuxi(s.P[commonsize()+2:], 2)) & (1<<15 - 1))
+func decodetypeFuncOutCount(arch *sys.Arch, s *Symbol) int {
+	return int(uint16(decodeInuxi(arch, s.P[commonsize()+2:], 2)) & (1<<15 - 1))
 }
 
-func decodetype_funcintype(s *LSym, i int) *LSym {
+func decodetypeFuncInType(s *Symbol, i int) *Symbol {
 	uadd := commonsize() + 4
 	if SysArch.PtrSize == 8 {
 		uadd += 4
 	}
-	if decodetype_hasUncommon(s) {
+	if decodetypeHasUncommon(s) {
 		uadd += uncommonSize()
 	}
-	return decode_reloc_sym(s, int32(uadd+i*SysArch.PtrSize))
+	return decodeRelocSym(s, int32(uadd+i*SysArch.PtrSize))
 }
 
-func decodetype_funcouttype(s *LSym, i int) *LSym {
-	return decodetype_funcintype(s, i+decodetype_funcincount(s))
+func decodetypeFuncOutType(arch *sys.Arch, s *Symbol, i int) *Symbol {
+	return decodetypeFuncInType(s, i+decodetypeFuncInCount(arch, s))
 }
 
 // Type.StructType.fields.Slice::length
-func decodetype_structfieldcount(s *LSym) int {
-	return int(decode_inuxi(s.P[commonsize()+2*SysArch.PtrSize:], SysArch.IntSize))
+func decodetypeStructFieldCount(arch *sys.Arch, s *Symbol) int {
+	return int(decodeInuxi(arch, s.P[commonsize()+2*SysArch.PtrSize:], SysArch.IntSize))
 }
 
-func decodetype_structfieldarrayoff(s *LSym, i int) int {
+func decodetypeStructFieldArrayOff(s *Symbol, i int) int {
 	off := commonsize() + 2*SysArch.PtrSize + 2*SysArch.IntSize
-	if decodetype_hasUncommon(s) {
+	if decodetypeHasUncommon(s) {
 		off += uncommonSize()
 	}
 	off += i * structfieldSize()
 	return off
 }
 
-// decodetype_str returns the contents of an rtype's str field (a nameOff).
-func decodetype_str(s *LSym) string {
-	str := decodetype_name(s, 4*SysArch.PtrSize+8)
+// decodetypeStr returns the contents of an rtype's str field (a nameOff).
+func decodetypeStr(s *Symbol) string {
+	str := decodetypeName(s, 4*SysArch.PtrSize+8)
 	if s.P[2*SysArch.PtrSize+4]&tflagExtraStar != 0 {
 		return str[1:]
 	}
 	return str
 }
 
-// decodetype_name decodes the name from a reflect.name.
-func decodetype_name(s *LSym, off int) string {
-	r := decode_reloc(s, int32(off))
+// decodetypeName decodes the name from a reflect.name.
+func decodetypeName(s *Symbol, off int) string {
+	r := decodeReloc(s, int32(off))
 	if r == nil {
 		return ""
 	}
@@ -243,24 +243,24 @@ func decodetype_name(s *LSym, off int) string {
 	return string(data[3 : 3+namelen])
 }
 
-func decodetype_structfieldname(s *LSym, i int) string {
-	off := decodetype_structfieldarrayoff(s, i)
-	return decodetype_name(s, off)
+func decodetypeStructFieldName(s *Symbol, i int) string {
+	off := decodetypeStructFieldArrayOff(s, i)
+	return decodetypeName(s, off)
 }
 
-func decodetype_structfieldtype(s *LSym, i int) *LSym {
-	off := decodetype_structfieldarrayoff(s, i)
-	return decode_reloc_sym(s, int32(off+SysArch.PtrSize))
+func decodetypeStructFieldType(s *Symbol, i int) *Symbol {
+	off := decodetypeStructFieldArrayOff(s, i)
+	return decodeRelocSym(s, int32(off+SysArch.PtrSize))
 }
 
-func decodetype_structfieldoffs(s *LSym, i int) int64 {
-	off := decodetype_structfieldarrayoff(s, i)
-	return int64(decode_inuxi(s.P[off+2*SysArch.PtrSize:], SysArch.IntSize))
+func decodetypeStructFieldOffs(arch *sys.Arch, s *Symbol, i int) int64 {
+	off := decodetypeStructFieldArrayOff(s, i)
+	return int64(decodeInuxi(arch, s.P[off+2*SysArch.PtrSize:], SysArch.IntSize))
 }
 
 // InterfaceType.methods.length
-func decodetype_ifacemethodcount(s *LSym) int64 {
-	return int64(decode_inuxi(s.P[commonsize()+2*SysArch.PtrSize:], SysArch.IntSize))
+func decodetypeIfaceMethodCount(arch *sys.Arch, s *Symbol) int64 {
+	return int64(decodeInuxi(arch, s.P[commonsize()+2*SysArch.PtrSize:], SysArch.IntSize))
 }
 
 // methodsig is a fully qualified typed method signature, like
@@ -280,34 +280,34 @@ const (
 	kindMask      = (1 << 5) - 1
 )
 
-// decode_methodsig decodes an array of method signature information.
+// decodeMethodSig decodes an array of method signature information.
 // Each element of the array is size bytes. The first 4 bytes is a
 // nameOff for the method name, and the next 4 bytes is a typeOff for
 // the function type.
 //
 // Conveniently this is the layout of both runtime.method and runtime.imethod.
-func decode_methodsig(s *LSym, off, size, count int) []methodsig {
+func decodeMethodSig(arch *sys.Arch, s *Symbol, off, size, count int) []methodsig {
 	var buf bytes.Buffer
 	var methods []methodsig
 	for i := 0; i < count; i++ {
-		buf.WriteString(decodetype_name(s, off))
-		mtypSym := decode_reloc_sym(s, int32(off+4))
+		buf.WriteString(decodetypeName(s, off))
+		mtypSym := decodeRelocSym(s, int32(off+4))
 
 		buf.WriteRune('(')
-		inCount := decodetype_funcincount(mtypSym)
+		inCount := decodetypeFuncInCount(arch, mtypSym)
 		for i := 0; i < inCount; i++ {
 			if i > 0 {
 				buf.WriteString(", ")
 			}
-			buf.WriteString(decodetype_funcintype(mtypSym, i).Name)
+			buf.WriteString(decodetypeFuncInType(mtypSym, i).Name)
 		}
 		buf.WriteString(") (")
-		outCount := decodetype_funcoutcount(mtypSym)
+		outCount := decodetypeFuncOutCount(arch, mtypSym)
 		for i := 0; i < outCount; i++ {
 			if i > 0 {
 				buf.WriteString(", ")
 			}
-			buf.WriteString(decodetype_funcouttype(mtypSym, i).Name)
+			buf.WriteString(decodetypeFuncOutType(arch, mtypSym, i).Name)
 		}
 		buf.WriteRune(')')
 
@@ -318,11 +318,11 @@ func decode_methodsig(s *LSym, off, size, count int) []methodsig {
 	return methods
 }
 
-func decodetype_ifacemethods(s *LSym) []methodsig {
-	if decodetype_kind(s)&kindMask != kindInterface {
+func decodeIfaceMethods(arch *sys.Arch, s *Symbol) []methodsig {
+	if decodetypeKind(s)&kindMask != kindInterface {
 		panic(fmt.Sprintf("symbol %q is not an interface", s.Name))
 	}
-	r := decode_reloc(s, int32(commonsize()+SysArch.PtrSize))
+	r := decodeReloc(s, int32(commonsize()+SysArch.PtrSize))
 	if r == nil {
 		return nil
 	}
@@ -330,17 +330,17 @@ func decodetype_ifacemethods(s *LSym) []methodsig {
 		panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", s.Name))
 	}
 	off := int(r.Add) // array of reflect.imethod values
-	numMethods := int(decodetype_ifacemethodcount(s))
+	numMethods := int(decodetypeIfaceMethodCount(arch, s))
 	sizeofIMethod := 4 + 4
-	return decode_methodsig(s, off, sizeofIMethod, numMethods)
+	return decodeMethodSig(arch, s, off, sizeofIMethod, numMethods)
 }
 
-func decodetype_methods(s *LSym) []methodsig {
-	if !decodetype_hasUncommon(s) {
+func decodetypeMethods(arch *sys.Arch, s *Symbol) []methodsig {
+	if !decodetypeHasUncommon(s) {
 		panic(fmt.Sprintf("no methods on %q", s.Name))
 	}
 	off := commonsize() // reflect.rtype
-	switch decodetype_kind(s) & kindMask {
+	switch decodetypeKind(s) & kindMask {
 	case kindStruct: // reflect.structType
 		off += 2*SysArch.PtrSize + 2*SysArch.IntSize
 	case kindPtr: // reflect.ptrType
@@ -361,9 +361,9 @@ func decodetype_methods(s *LSym) []methodsig {
 		// just Sizeof(rtype)
 	}
 
-	mcount := int(decode_inuxi(s.P[off+4:], 2))
-	moff := int(decode_inuxi(s.P[off+4+2+2:], 4))
+	mcount := int(decodeInuxi(arch, s.P[off+4:], 2))
+	moff := int(decodeInuxi(arch, s.P[off+4+2+2:], 4))
 	off += moff                // offset to array of reflect.method values
 	const sizeofMethod = 4 * 4 // sizeof reflect.method in program
-	return decode_methodsig(s, off, sizeofMethod, mcount)
+	return decodeMethodSig(arch, s, off, sizeofMethod, mcount)
 }
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index fa7105f..61d3e4f 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -15,6 +15,7 @@
 package ld
 
 import (
+	"cmd/internal/dwarf"
 	"cmd/internal/obj"
 	"fmt"
 	"log"
@@ -22,505 +23,148 @@ import (
 	"strings"
 )
 
-const infoprefix = "go.dwarf.info."
-
-/*
- * Offsets and sizes of the debug_* sections in the cout file.
- */
-var abbrevsym *LSym
-var arangessec *LSym
-var framesec *LSym
-var infosec *LSym
-var linesec *LSym
-
-var gdbscript string
-
-/*
- *  Basic I/O
- */
-func addrput(s *LSym, addr int64) {
-	switch SysArch.PtrSize {
-	case 4:
-		Adduint32(Ctxt, s, uint32(addr))
-
-	case 8:
-		Adduint64(Ctxt, s, uint64(addr))
-	}
+type dwctxt struct {
+	linkctxt *Link
 }
 
-func appendUleb128(b []byte, v uint64) []byte {
-	for {
-		c := uint8(v & 0x7f)
-		v >>= 7
-		if v != 0 {
-			c |= 0x80
-		}
-		b = append(b, c)
-		if c&0x80 == 0 {
-			break
-		}
-	}
-	return b
-}
-
-func appendSleb128(b []byte, v int64) []byte {
-	for {
-		c := uint8(v & 0x7f)
-		s := uint8(v & 0x40)
-		v >>= 7
-		if (v != -1 || s == 0) && (v != 0 || s != 0) {
-			c |= 0x80
-		}
-		b = append(b, c)
-		if c&0x80 == 0 {
-			break
-		}
-	}
-	return b
+func (c dwctxt) PtrSize() int {
+	return SysArch.PtrSize
 }
-
-var encbuf [10]byte
-
-func uleb128put(s *LSym, v int64) {
-	b := appendUleb128(encbuf[:0], uint64(v))
-	Addbytes(Ctxt, s, b)
+func (c dwctxt) AddInt(s dwarf.Sym, size int, i int64) {
+	ls := s.(*Symbol)
+	adduintxx(c.linkctxt, ls, uint64(i), size)
 }
-
-func sleb128put(s *LSym, v int64) {
-	b := appendSleb128(encbuf[:0], v)
-	Addbytes(Ctxt, s, b)
+func (c dwctxt) AddBytes(s dwarf.Sym, b []byte) {
+	ls := s.(*Symbol)
+	Addbytes(ls, b)
 }
-
-/*
- * Defining Abbrevs.  This is hardcoded, and there will be
- * only a handful of them.  The DWARF spec places no restriction on
- * the ordering of attributes in the Abbrevs and DIEs, and we will
- * always write them out in the order of declaration in the abbrev.
- */
-type DWAttrForm struct {
-	attr uint16
-	form uint8
+func (c dwctxt) AddString(s dwarf.Sym, v string) {
+	Addstring(s.(*Symbol), v)
 }
-
-// Go-specific type attributes.
-const (
-	DW_AT_go_kind = 0x2900
-	DW_AT_go_key  = 0x2901
-	DW_AT_go_elem = 0x2902
-
-	DW_AT_internal_location = 253 // params and locals; not emitted
-)
-
-// Index into the abbrevs table below.
-// Keep in sync with ispubname() and ispubtype() below.
-// ispubtype considers >= NULLTYPE public
-const (
-	DW_ABRV_NULL = iota
-	DW_ABRV_COMPUNIT
-	DW_ABRV_FUNCTION
-	DW_ABRV_VARIABLE
-	DW_ABRV_AUTO
-	DW_ABRV_PARAM
-	DW_ABRV_STRUCTFIELD
-	DW_ABRV_FUNCTYPEPARAM
-	DW_ABRV_DOTDOTDOT
-	DW_ABRV_ARRAYRANGE
-	DW_ABRV_NULLTYPE
-	DW_ABRV_BASETYPE
-	DW_ABRV_ARRAYTYPE
-	DW_ABRV_CHANTYPE
-	DW_ABRV_FUNCTYPE
-	DW_ABRV_IFACETYPE
-	DW_ABRV_MAPTYPE
-	DW_ABRV_PTRTYPE
-	DW_ABRV_BARE_PTRTYPE // only for void*, no DW_AT_type attr to please gdb 6.
-	DW_ABRV_SLICETYPE
-	DW_ABRV_STRINGTYPE
-	DW_ABRV_STRUCTTYPE
-	DW_ABRV_TYPEDECL
-	DW_NABRV
-)
-
-type DWAbbrev struct {
-	tag      uint8
-	children uint8
-	attr     []DWAttrForm
+func (c dwctxt) SymValue(s dwarf.Sym) int64 {
+	return s.(*Symbol).Value
 }
 
-var abbrevs = [DW_NABRV]DWAbbrev{
-	/* The mandatory DW_ABRV_NULL entry. */
-	{0, 0, []DWAttrForm{}},
-
-	/* COMPUNIT */
-	{
-		DW_TAG_compile_unit,
-		DW_CHILDREN_yes,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_language, DW_FORM_data1},
-			{DW_AT_low_pc, DW_FORM_addr},
-			{DW_AT_high_pc, DW_FORM_addr},
-			{DW_AT_stmt_list, DW_FORM_data4},
-			{DW_AT_comp_dir, DW_FORM_string},
-		},
-	},
-
-	/* FUNCTION */
-	{
-		DW_TAG_subprogram,
-		DW_CHILDREN_yes,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_low_pc, DW_FORM_addr},
-			{DW_AT_high_pc, DW_FORM_addr},
-			{DW_AT_external, DW_FORM_flag},
-		},
-	},
-
-	/* VARIABLE */
-	{
-		DW_TAG_variable,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_location, DW_FORM_block1},
-			{DW_AT_type, DW_FORM_ref_addr},
-			{DW_AT_external, DW_FORM_flag},
-		},
-	},
-
-	/* AUTO */
-	{
-		DW_TAG_variable,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_location, DW_FORM_block1},
-			{DW_AT_type, DW_FORM_ref_addr},
-		},
-	},
-
-	/* PARAM */
-	{
-		DW_TAG_formal_parameter,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_location, DW_FORM_block1},
-			{DW_AT_type, DW_FORM_ref_addr},
-		},
-	},
-
-	/* STRUCTFIELD */
-	{
-		DW_TAG_member,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_data_member_location, DW_FORM_block1},
-			{DW_AT_type, DW_FORM_ref_addr},
-		},
-	},
-
-	/* FUNCTYPEPARAM */
-	{
-		DW_TAG_formal_parameter,
-		DW_CHILDREN_no,
-
-		// No name!
-		[]DWAttrForm{
-			{DW_AT_type, DW_FORM_ref_addr},
-		},
-	},
-
-	/* DOTDOTDOT */
-	{
-		DW_TAG_unspecified_parameters,
-		DW_CHILDREN_no,
-		[]DWAttrForm{},
-	},
-
-	/* ARRAYRANGE */
-	{
-		DW_TAG_subrange_type,
-		DW_CHILDREN_no,
-
-		// No name!
-		[]DWAttrForm{
-			{DW_AT_type, DW_FORM_ref_addr},
-			{DW_AT_count, DW_FORM_udata},
-		},
-	},
-
-	// Below here are the types considered public by ispubtype
-	/* NULLTYPE */
-	{
-		DW_TAG_unspecified_type,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-		},
-	},
-
-	/* BASETYPE */
-	{
-		DW_TAG_base_type,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_encoding, DW_FORM_data1},
-			{DW_AT_byte_size, DW_FORM_data1},
-			{DW_AT_go_kind, DW_FORM_data1},
-		},
-	},
-
-	/* ARRAYTYPE */
-	// child is subrange with upper bound
-	{
-		DW_TAG_array_type,
-		DW_CHILDREN_yes,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_type, DW_FORM_ref_addr},
-			{DW_AT_byte_size, DW_FORM_udata},
-			{DW_AT_go_kind, DW_FORM_data1},
-		},
-	},
-
-	/* CHANTYPE */
-	{
-		DW_TAG_typedef,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_type, DW_FORM_ref_addr},
-			{DW_AT_go_kind, DW_FORM_data1},
-			{DW_AT_go_elem, DW_FORM_ref_addr},
-		},
-	},
-
-	/* FUNCTYPE */
-	{
-		DW_TAG_subroutine_type,
-		DW_CHILDREN_yes,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			// {DW_AT_type,	DW_FORM_ref_addr},
-			{DW_AT_go_kind, DW_FORM_data1},
-		},
-	},
-
-	/* IFACETYPE */
-	{
-		DW_TAG_typedef,
-		DW_CHILDREN_yes,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_type, DW_FORM_ref_addr},
-			{DW_AT_go_kind, DW_FORM_data1},
-		},
-	},
-
-	/* MAPTYPE */
-	{
-		DW_TAG_typedef,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_type, DW_FORM_ref_addr},
-			{DW_AT_go_kind, DW_FORM_data1},
-			{DW_AT_go_key, DW_FORM_ref_addr},
-			{DW_AT_go_elem, DW_FORM_ref_addr},
-		},
-	},
-
-	/* PTRTYPE */
-	{
-		DW_TAG_pointer_type,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_type, DW_FORM_ref_addr},
-			{DW_AT_go_kind, DW_FORM_data1},
-		},
-	},
-
-	/* BARE_PTRTYPE */
-	{
-		DW_TAG_pointer_type,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-		},
-	},
-
-	/* SLICETYPE */
-	{
-		DW_TAG_structure_type,
-		DW_CHILDREN_yes,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_byte_size, DW_FORM_udata},
-			{DW_AT_go_kind, DW_FORM_data1},
-			{DW_AT_go_elem, DW_FORM_ref_addr},
-		},
-	},
-
-	/* STRINGTYPE */
-	{
-		DW_TAG_structure_type,
-		DW_CHILDREN_yes,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_byte_size, DW_FORM_udata},
-			{DW_AT_go_kind, DW_FORM_data1},
-		},
-	},
-
-	/* STRUCTTYPE */
-	{
-		DW_TAG_structure_type,
-		DW_CHILDREN_yes,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_byte_size, DW_FORM_udata},
-			{DW_AT_go_kind, DW_FORM_data1},
-		},
-	},
-
-	/* TYPEDECL */
-	{
-		DW_TAG_typedef,
-		DW_CHILDREN_no,
-		[]DWAttrForm{
-			{DW_AT_name, DW_FORM_string},
-			{DW_AT_type, DW_FORM_ref_addr},
-		},
-	},
+func (c dwctxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
+	if value != 0 {
+		value -= (data.(*Symbol)).Value
+	}
+	Addaddrplus(c.linkctxt, s.(*Symbol), data.(*Symbol), value)
 }
 
-var dwarfp *LSym
-
-func writeabbrev() *LSym {
-	s := Linklookup(Ctxt, ".debug_abbrev", 0)
-	s.Type = obj.SDWARFSECT
-	abbrevsym = s
-
-	for i := 1; i < DW_NABRV; i++ {
-		// See section 7.5.3
-		uleb128put(s, int64(i))
-
-		uleb128put(s, int64(abbrevs[i].tag))
-		Adduint8(Ctxt, s, abbrevs[i].children)
-		for _, f := range abbrevs[i].attr {
-			uleb128put(s, int64(f.attr))
-			uleb128put(s, int64(f.form))
-		}
-		uleb128put(s, 0)
-		uleb128put(s, 0)
+func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
+	ls := s.(*Symbol)
+	switch size {
+	default:
+		Errorf(ls, "invalid size %d in adddwarfref\n", size)
+		fallthrough
+	case SysArch.PtrSize:
+		Addaddr(c.linkctxt, ls, t.(*Symbol))
+	case 4:
+		addaddrplus4(c.linkctxt, ls, t.(*Symbol), 0)
 	}
-
-	Adduint8(Ctxt, s, 0)
-	return s
+	r := &ls.R[len(ls.R)-1]
+	r.Type = obj.R_DWARFREF
+	r.Add = ofs
 }
 
 /*
- * Debugging Information Entries and their attributes.
+ * Offsets and sizes of the debug_* sections in the cout file.
  */
+var abbrevsym *Symbol
+var arangessec *Symbol
+var framesec *Symbol
+var infosec *Symbol
+var linesec *Symbol
 
-// For DW_CLS_string and _block, value should contain the length, and
-// data the data, for _reference, value is 0 and data is a DWDie* to
-// the referenced instance, for all others, value is the whole thing
-// and data is null.
-
-type DWAttr struct {
-	link  *DWAttr
-	atr   uint16 // DW_AT_
-	cls   uint8  // DW_CLS_
-	value int64
-	data  interface{}
-}
+var gdbscript string
+
+var dwarfp []*Symbol
 
-type DWDie struct {
-	abbrev int
-	link   *DWDie
-	child  *DWDie
-	attr   *DWAttr
-	sym    *LSym
+func writeabbrev(ctxt *Link, syms []*Symbol) []*Symbol {
+	s := ctxt.Syms.Lookup(".debug_abbrev", 0)
+	s.Type = obj.SDWARFSECT
+	abbrevsym = s
+	Addbytes(s, dwarf.GetAbbrev())
+	return append(syms, s)
 }
 
 /*
  * Root DIEs for compilation units, types and global variables.
  */
-var dwroot DWDie
+var dwroot dwarf.DWDie
 
-var dwtypes DWDie
+var dwtypes dwarf.DWDie
 
-var dwglobals DWDie
+var dwglobals dwarf.DWDie
 
-func newattr(die *DWDie, attr uint16, cls int, value int64, data interface{}) *DWAttr {
-	a := new(DWAttr)
-	a.link = die.attr
-	die.attr = a
-	a.atr = attr
-	a.cls = uint8(cls)
-	a.value = value
-	a.data = data
+func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) *dwarf.DWAttr {
+	a := new(dwarf.DWAttr)
+	a.Link = die.Attr
+	die.Attr = a
+	a.Atr = attr
+	a.Cls = uint8(cls)
+	a.Value = value
+	a.Data = data
 	return a
 }
 
 // Each DIE (except the root ones) has at least 1 attribute: its
 // name. getattr moves the desired one to the front so
 // frequently searched ones are found faster.
-func getattr(die *DWDie, attr uint16) *DWAttr {
-	if die.attr.atr == attr {
-		return die.attr
+func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr {
+	if die.Attr.Atr == attr {
+		return die.Attr
 	}
 
-	a := die.attr
-	b := a.link
+	a := die.Attr
+	b := a.Link
 	for b != nil {
-		if b.atr == attr {
-			a.link = b.link
-			b.link = die.attr
-			die.attr = b
+		if b.Atr == attr {
+			a.Link = b.Link
+			b.Link = die.Attr
+			die.Attr = b
 			return b
 		}
 
 		a = b
-		b = b.link
+		b = b.Link
 	}
 
 	return nil
 }
 
-// Every DIE has at least a DW_AT_name attribute (but it will only be
+// Every DIE has at least a AT_name attribute (but it will only be
 // written out if it is listed in the abbrev).
-func newdie(parent *DWDie, abbrev int, name string, version int) *DWDie {
-	die := new(DWDie)
-	die.abbrev = abbrev
-	die.link = parent.child
-	parent.child = die
-
-	newattr(die, DW_AT_name, DW_CLS_STRING, int64(len(name)), name)
-
-	if name != "" && (abbrev <= DW_ABRV_VARIABLE || abbrev >= DW_ABRV_NULLTYPE) {
-		if abbrev != DW_ABRV_VARIABLE || version == 0 {
-			die.sym = Linklookup(Ctxt, infoprefix+name, version)
-			die.sym.Attr |= AttrHidden
-			die.sym.Type = obj.SDWARFINFO
+func newdie(ctxt *Link, parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie {
+	die := new(dwarf.DWDie)
+	die.Abbrev = abbrev
+	die.Link = parent.Child
+	parent.Child = die
+
+	newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name)
+
+	if name != "" && (abbrev <= dwarf.DW_ABRV_VARIABLE || abbrev >= dwarf.DW_ABRV_NULLTYPE) {
+		if abbrev != dwarf.DW_ABRV_VARIABLE || version == 0 {
+			sym := ctxt.Syms.Lookup(dwarf.InfoPrefix+name, version)
+			sym.Attr |= AttrHidden
+			sym.Type = obj.SDWARFINFO
+			die.Sym = sym
 		}
 	}
 
 	return die
 }
 
-func walktypedef(die *DWDie) *DWDie {
+func walktypedef(die *dwarf.DWDie) *dwarf.DWDie {
+	if die == nil {
+		return nil
+	}
 	// Resolve typedef if present.
-	if die.abbrev == DW_ABRV_TYPEDECL {
-		for attr := die.attr; attr != nil; attr = attr.link {
-			if attr.atr == DW_AT_type && attr.cls == DW_CLS_REFERENCE && attr.data != nil {
-				return attr.data.(*DWDie)
+	if die.Abbrev == dwarf.DW_ABRV_TYPEDECL {
+		for attr := die.Attr; attr != nil; attr = attr.Link {
+			if attr.Atr == dwarf.DW_AT_type && attr.Cls == dwarf.DW_CLS_REFERENCE && attr.Data != nil {
+				return attr.Data.(*dwarf.DWDie)
 			}
 		}
 	}
@@ -528,8 +172,8 @@ func walktypedef(die *DWDie) *DWDie {
 	return die
 }
 
-func walksymtypedef(s *LSym) *LSym {
-	if t := Linkrlookup(Ctxt, s.Name+"..def", int(s.Version)); t != nil {
+func walksymtypedef(ctxt *Link, s *Symbol) *Symbol {
+	if t := ctxt.Syms.ROLookup(s.Name+"..def", int(s.Version)); t != nil {
 		return t
 	}
 	return s
@@ -537,11 +181,11 @@ func walksymtypedef(s *LSym) *LSym {
 
 // Find child by AT_name using hashtable if available or linear scan
 // if not.
-func findchild(die *DWDie, name string) *DWDie {
-	var prev *DWDie
+func findchild(die *dwarf.DWDie, name string) *dwarf.DWDie {
+	var prev *dwarf.DWDie
 	for ; die != prev; prev, die = die, walktypedef(die) {
-		for a := die.child; a != nil; a = a.link {
-			if name == getattr(a, DW_AT_name).data {
+		for a := die.Child; a != nil; a = a.Link {
+			if name == getattr(a, dwarf.DW_AT_name).Data {
 				return a
 			}
 		}
@@ -551,29 +195,32 @@ func findchild(die *DWDie, name string) *DWDie {
 }
 
 // Used to avoid string allocation when looking up dwarf symbols
-var prefixBuf = []byte(infoprefix)
+var prefixBuf = []byte(dwarf.InfoPrefix)
 
-func find(name string) *LSym {
+func find(ctxt *Link, name string) *Symbol {
 	n := append(prefixBuf, name...)
 	// The string allocation below is optimized away because it is only used in a map lookup.
-	s := Linkrlookup(Ctxt, string(n), 0)
-	prefixBuf = n[:len(infoprefix)]
-	return s
+	s := ctxt.Syms.ROLookup(string(n), 0)
+	prefixBuf = n[:len(dwarf.InfoPrefix)]
+	if s != nil && s.Type == obj.SDWARFINFO {
+		return s
+	}
+	return nil
 }
 
-func mustFind(name string) *LSym {
-	r := find(name)
+func mustFind(ctxt *Link, name string) *Symbol {
+	r := find(ctxt, name)
 	if r == nil {
 		Exitf("dwarf find: cannot find %s", name)
 	}
 	return r
 }
 
-func adddwarfref(ctxt *Link, s *LSym, t *LSym, size int) int64 {
+func adddwarfref(ctxt *Link, s *Symbol, t *Symbol, size int) int64 {
 	var result int64
 	switch size {
 	default:
-		Diag("invalid size %d in adddwarfref\n", size)
+		Errorf(s, "invalid size %d in adddwarfref\n", size)
 		fallthrough
 	case SysArch.PtrSize:
 		result = Addaddr(ctxt, s, t)
@@ -585,182 +232,54 @@ func adddwarfref(ctxt *Link, s *LSym, t *LSym, size int) int64 {
 	return result
 }
 
-func newrefattr(die *DWDie, attr uint16, ref *LSym) *DWAttr {
+func newrefattr(die *dwarf.DWDie, attr uint16, ref *Symbol) *dwarf.DWAttr {
 	if ref == nil {
 		return nil
 	}
-	return newattr(die, attr, DW_CLS_REFERENCE, 0, ref)
+	return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, ref)
 }
 
-func putattr(s *LSym, abbrev int, form int, cls int, value int64, data interface{}) {
-	switch form {
-	case DW_FORM_addr: // address
-		if Linkmode == LinkExternal {
-			value -= (data.(*LSym)).Value
-			Addaddrplus(Ctxt, s, data.(*LSym), value)
-			break
-		}
-
-		addrput(s, value)
-
-	case DW_FORM_block1: // block
-		if cls == DW_CLS_ADDRESS {
-			Adduint8(Ctxt, s, uint8(1+SysArch.PtrSize))
-			Adduint8(Ctxt, s, DW_OP_addr)
-			Addaddr(Ctxt, s, data.(*LSym))
-			break
-		}
-
-		value &= 0xff
-		Adduint8(Ctxt, s, uint8(value))
-		p := data.([]byte)
-		for i := 0; int64(i) < value; i++ {
-			Adduint8(Ctxt, s, p[i])
-		}
-
-	case DW_FORM_block2: // block
-		value &= 0xffff
-
-		Adduint16(Ctxt, s, uint16(value))
-		p := data.([]byte)
-		for i := 0; int64(i) < value; i++ {
-			Adduint8(Ctxt, s, p[i])
-		}
-
-	case DW_FORM_block4: // block
-		value &= 0xffffffff
-
-		Adduint32(Ctxt, s, uint32(value))
-		p := data.([]byte)
-		for i := 0; int64(i) < value; i++ {
-			Adduint8(Ctxt, s, p[i])
-		}
-
-	case DW_FORM_block: // block
-		uleb128put(s, value)
-
-		p := data.([]byte)
-		for i := 0; int64(i) < value; i++ {
-			Adduint8(Ctxt, s, p[i])
-		}
-
-	case DW_FORM_data1: // constant
-		Adduint8(Ctxt, s, uint8(value))
-
-	case DW_FORM_data2: // constant
-		Adduint16(Ctxt, s, uint16(value))
-
-	case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr
-		if Linkmode == LinkExternal && cls == DW_CLS_PTR {
-			adddwarfref(Ctxt, s, linesec, 4)
-			break
-		}
-
-		Adduint32(Ctxt, s, uint32(value))
-
-	case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr
-		Adduint64(Ctxt, s, uint64(value))
-
-	case DW_FORM_sdata: // constant
-		sleb128put(s, value)
-
-	case DW_FORM_udata: // constant
-		uleb128put(s, value)
-
-	case DW_FORM_string: // string
-		str := data.(string)
-		Addstring(s, str)
-		for i := int64(len(str)); i < value; i++ {
-			Adduint8(Ctxt, s, 0)
-		}
-
-	case DW_FORM_flag: // flag
-		if value != 0 {
-			Adduint8(Ctxt, s, 1)
-		} else {
-			Adduint8(Ctxt, s, 0)
-		}
-
-		// In DWARF 2 (which is what we claim to generate),
-	// the ref_addr is the same size as a normal address.
-	// In DWARF 3 it is always 32 bits, unless emitting a large
-	// (> 4 GB of debug info aka "64-bit") unit, which we don't implement.
-	case DW_FORM_ref_addr: // reference to a DIE in the .info section
-		if data == nil {
-			Diag("dwarf: null reference in %d", abbrev)
-			if SysArch.PtrSize == 8 {
-				Adduint64(Ctxt, s, 0) // invalid dwarf, gdb will complain.
-			} else {
-				Adduint32(Ctxt, s, 0) // invalid dwarf, gdb will complain.
-			}
-		} else {
-			dsym := data.(*LSym)
-			adddwarfref(Ctxt, s, dsym, SysArch.PtrSize)
-		}
-
-	case DW_FORM_ref1, // reference within the compilation unit
-		DW_FORM_ref2,      // reference
-		DW_FORM_ref4,      // reference
-		DW_FORM_ref8,      // reference
-		DW_FORM_ref_udata, // reference
-
-		DW_FORM_strp,     // string
-		DW_FORM_indirect: // (see Section 7.5.3)
-		fallthrough
-	default:
-		Exitf("dwarf: unsupported attribute form %d / class %d", form, cls)
+func putdies(linkctxt *Link, ctxt dwarf.Context, syms []*Symbol, die *dwarf.DWDie) []*Symbol {
+	for ; die != nil; die = die.Link {
+		syms = putdie(linkctxt, ctxt, syms, die)
 	}
-}
-
-// Note that we can (and do) add arbitrary attributes to a DIE, but
-// only the ones actually listed in the Abbrev will be written out.
-func putattrs(s *LSym, abbrev int, attr *DWAttr) {
-Outer:
-	for _, f := range abbrevs[abbrev].attr {
-		for ap := attr; ap != nil; ap = ap.link {
-			if ap.atr == f.attr {
-				putattr(s, abbrev, int(f.form), int(ap.cls), ap.value, ap.data)
-				continue Outer
-			}
-		}
+	Adduint8(linkctxt, syms[len(syms)-1], 0)
 
-		putattr(s, abbrev, int(f.form), 0, 0, nil)
-	}
+	return syms
 }
 
-func putdies(prev *LSym, die *DWDie) *LSym {
-	for ; die != nil; die = die.link {
-		prev = putdie(prev, die)
+func dtolsym(s dwarf.Sym) *Symbol {
+	if s == nil {
+		return nil
 	}
-	Adduint8(Ctxt, prev, 0)
-	return prev
+	return s.(*Symbol)
 }
 
-func putdie(prev *LSym, die *DWDie) *LSym {
-	s := die.sym
+func putdie(linkctxt *Link, ctxt dwarf.Context, syms []*Symbol, die *dwarf.DWDie) []*Symbol {
+	s := dtolsym(die.Sym)
 	if s == nil {
-		s = prev
+		s = syms[len(syms)-1]
 	} else {
 		if s.Attr.OnList() {
 			log.Fatalf("symbol %s listed multiple times", s.Name)
 		}
 		s.Attr |= AttrOnList
-		prev.Next = s
+		syms = append(syms, s)
 	}
-	uleb128put(s, int64(die.abbrev))
-	putattrs(s, die.abbrev, die.attr)
-	if abbrevs[die.abbrev].children != 0 {
-		return putdies(s, die.child)
+	dwarf.Uleb128put(ctxt, s, int64(die.Abbrev))
+	dwarf.PutAttrs(ctxt, s, die.Abbrev, die.Attr)
+	if dwarf.HasChildren(die) {
+		return putdies(linkctxt, ctxt, syms, die.Child)
 	}
-	return s
+	return syms
 }
 
-func reverselist(list **DWDie) {
+func reverselist(list **dwarf.DWDie) {
 	curr := *list
-	var prev *DWDie
+	var prev *dwarf.DWDie
 	for curr != nil {
-		var next *DWDie = curr.link
-		curr.link = prev
+		var next *dwarf.DWDie = curr.Link
+		curr.Link = prev
 		prev = curr
 		curr = next
 	}
@@ -768,32 +287,32 @@ func reverselist(list **DWDie) {
 	*list = prev
 }
 
-func reversetree(list **DWDie) {
+func reversetree(list **dwarf.DWDie) {
 	reverselist(list)
-	for die := *list; die != nil; die = die.link {
-		if abbrevs[die.abbrev].children != 0 {
-			reversetree(&die.child)
+	for die := *list; die != nil; die = die.Link {
+		if dwarf.HasChildren(die) {
+			reversetree(&die.Child)
 		}
 	}
 }
 
-func newmemberoffsetattr(die *DWDie, offs int32) {
+func newmemberoffsetattr(die *dwarf.DWDie, offs int32) {
 	var block [20]byte
-	b := append(block[:0], DW_OP_plus_uconst)
-	b = appendUleb128(b, uint64(offs))
-	newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, int64(len(b)), b)
+	b := append(block[:0], dwarf.DW_OP_plus_uconst)
+	b = dwarf.AppendUleb128(b, uint64(offs))
+	newattr(die, dwarf.DW_AT_data_member_location, dwarf.DW_CLS_BLOCK, int64(len(b)), b)
 }
 
-// GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a
+// GDB doesn't like FORM_addr for AT_location, so emit a
 // location expression that evals to a const.
-func newabslocexprattr(die *DWDie, addr int64, sym *LSym) {
-	newattr(die, DW_AT_location, DW_CLS_ADDRESS, addr, sym)
+func newabslocexprattr(die *dwarf.DWDie, addr int64, sym *Symbol) {
+	newattr(die, dwarf.DW_AT_location, dwarf.DW_CLS_ADDRESS, addr, sym)
 	// below
 }
 
 // Lookup predefined types
-func lookup_or_diag(n string) *LSym {
-	s := Linkrlookup(Ctxt, n, 0)
+func lookupOrDiag(ctxt *Link, n string) *Symbol {
+	s := ctxt.Syms.ROLookup(n, 0)
 	if s == nil || s.Size == 0 {
 		Exitf("dwarf: missing type: %s", n)
 	}
@@ -801,7 +320,7 @@ func lookup_or_diag(n string) *LSym {
 	return s
 }
 
-func dotypedef(parent *DWDie, name string, def *DWDie) {
+func dotypedef(ctxt *Link, parent *dwarf.DWDie, name string, def *dwarf.DWDie) {
 	// Only emit typedefs for real names.
 	if strings.HasPrefix(name, "map[") {
 		return
@@ -816,64 +335,65 @@ func dotypedef(parent *DWDie, name string, def *DWDie) {
 		return
 	}
 	if def == nil {
-		Diag("dwarf: bad def in dotypedef")
+		Errorf(nil, "dwarf: bad def in dotypedef")
 	}
 
-	def.sym = Linklookup(Ctxt, def.sym.Name+"..def", 0)
-	def.sym.Attr |= AttrHidden
-	def.sym.Type = obj.SDWARFINFO
+	sym := ctxt.Syms.Lookup(dtolsym(def.Sym).Name+"..def", 0)
+	sym.Attr |= AttrHidden
+	sym.Type = obj.SDWARFINFO
+	def.Sym = sym
 
 	// The typedef entry must be created after the def,
 	// so that future lookups will find the typedef instead
 	// of the real definition. This hooks the typedef into any
 	// circular definition loops, so that gdb can understand them.
-	die := newdie(parent, DW_ABRV_TYPEDECL, name, 0)
+	die := newdie(ctxt, parent, dwarf.DW_ABRV_TYPEDECL, name, 0)
 
-	newrefattr(die, DW_AT_type, def.sym)
+	newrefattr(die, dwarf.DW_AT_type, sym)
 }
 
 // Define gotype, for composite ones recurse into constituents.
-func defgotype(gotype *LSym) *LSym {
+func defgotype(ctxt *Link, gotype *Symbol) *Symbol {
 	if gotype == nil {
-		return mustFind("<unspecified>")
+		return mustFind(ctxt, "<unspecified>")
 	}
 
 	if !strings.HasPrefix(gotype.Name, "type.") {
-		Diag("dwarf: type name doesn't start with \"type.\": %s", gotype.Name)
-		return mustFind("<unspecified>")
+		Errorf(gotype, "dwarf: type name doesn't start with \"type.\"")
+		return mustFind(ctxt, "<unspecified>")
 	}
 
 	name := gotype.Name[5:] // could also decode from Type.string
 
-	sdie := find(name)
+	sdie := find(ctxt, name)
 
 	if sdie != nil {
 		return sdie
 	}
 
-	return newtype(gotype).sym
+	return newtype(ctxt, gotype).Sym.(*Symbol)
 }
 
-func newtype(gotype *LSym) *DWDie {
+func newtype(ctxt *Link, gotype *Symbol) *dwarf.DWDie {
 	name := gotype.Name[5:] // could also decode from Type.string
-	kind := decodetype_kind(gotype)
-	bytesize := decodetype_size(gotype)
+	kind := decodetypeKind(gotype)
+	bytesize := decodetypeSize(ctxt.Arch, gotype)
 
-	var die *DWDie
+	var die *dwarf.DWDie
 	switch kind {
 	case obj.KindBool:
-		die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0)
-		newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_boolean, 0)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case obj.KindInt,
 		obj.KindInt8,
 		obj.KindInt16,
 		obj.KindInt32,
 		obj.KindInt64:
-		die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0)
-		newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case obj.KindUint,
 		obj.KindUint8,
@@ -881,137 +401,137 @@ func newtype(gotype *LSym) *DWDie {
 		obj.KindUint32,
 		obj.KindUint64,
 		obj.KindUintptr:
-		die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0)
-		newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case obj.KindFloat32,
 		obj.KindFloat64:
-		die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0)
-		newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_float, 0)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case obj.KindComplex64,
 		obj.KindComplex128:
-		die = newdie(&dwtypes, DW_ABRV_BASETYPE, name, 0)
-		newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_complex_float, 0)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0)
+		newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case obj.KindArray:
-		die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name, 0)
-		dotypedef(&dwtypes, name, die)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
-		s := decodetype_arrayelem(gotype)
-		newrefattr(die, DW_AT_type, defgotype(s))
-		fld := newdie(die, DW_ABRV_ARRAYRANGE, "range", 0)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0)
+		dotypedef(ctxt, &dwtypes, name, die)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+		s := decodetypeArrayElem(gotype)
+		newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
+		fld := newdie(ctxt, die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0)
 
 		// use actual length not upper bound; correct for 0-length arrays.
-		newattr(fld, DW_AT_count, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0)
+		newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(ctxt.Arch, gotype), 0)
 
-		newrefattr(fld, DW_AT_type, mustFind("uintptr"))
+		newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
 
 	case obj.KindChan:
-		die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name, 0)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
-		s := decodetype_chanelem(gotype)
-		newrefattr(die, DW_AT_go_elem, defgotype(s))
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+		s := decodetypeChanElem(gotype)
+		newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s))
 		// Save elem type for synthesizechantypes. We could synthesize here
 		// but that would change the order of DIEs we output.
-		newrefattr(die, DW_AT_type, s)
+		newrefattr(die, dwarf.DW_AT_type, s)
 
 	case obj.KindFunc:
-		die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name, 0)
-		dotypedef(&dwtypes, name, die)
-		newrefattr(die, DW_AT_type, mustFind("void"))
-		nfields := decodetype_funcincount(gotype)
-		var fld *DWDie
-		var s *LSym
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0)
+		dotypedef(ctxt, &dwtypes, name, die)
+		newrefattr(die, dwarf.DW_AT_type, mustFind(ctxt, "void"))
+		nfields := decodetypeFuncInCount(ctxt.Arch, gotype)
+		var fld *dwarf.DWDie
+		var s *Symbol
 		for i := 0; i < nfields; i++ {
-			s = decodetype_funcintype(gotype, i)
-			fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
-			newrefattr(fld, DW_AT_type, defgotype(s))
+			s = decodetypeFuncInType(gotype, i)
+			fld = newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
+			newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s))
 		}
 
-		if decodetype_funcdotdotdot(gotype) {
-			newdie(die, DW_ABRV_DOTDOTDOT, "...", 0)
+		if decodetypeFuncDotdotdot(ctxt.Arch, gotype) {
+			newdie(ctxt, die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0)
 		}
-		nfields = decodetype_funcoutcount(gotype)
+		nfields = decodetypeFuncOutCount(ctxt.Arch, gotype)
 		for i := 0; i < nfields; i++ {
-			s = decodetype_funcouttype(gotype, i)
-			fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
-			newrefattr(fld, DW_AT_type, defptrto(defgotype(s)))
+			s = decodetypeFuncOutType(ctxt.Arch, gotype, i)
+			fld = newdie(ctxt, die, dwarf.DW_ABRV_FUNCTYPEPARAM, s.Name[5:], 0)
+			newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, defgotype(ctxt, s)))
 		}
 
 	case obj.KindInterface:
-		die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name, 0)
-		dotypedef(&dwtypes, name, die)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
-		nfields := int(decodetype_ifacemethodcount(gotype))
-		var s *LSym
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0)
+		dotypedef(ctxt, &dwtypes, name, die)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+		nfields := int(decodetypeIfaceMethodCount(ctxt.Arch, gotype))
+		var s *Symbol
 		if nfields == 0 {
-			s = lookup_or_diag("type.runtime.eface")
+			s = lookupOrDiag(ctxt, "type.runtime.eface")
 		} else {
-			s = lookup_or_diag("type.runtime.iface")
+			s = lookupOrDiag(ctxt, "type.runtime.iface")
 		}
-		newrefattr(die, DW_AT_type, defgotype(s))
+		newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
 
 	case obj.KindMap:
-		die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name, 0)
-		s := decodetype_mapkey(gotype)
-		newrefattr(die, DW_AT_go_key, defgotype(s))
-		s = decodetype_mapvalue(gotype)
-		newrefattr(die, DW_AT_go_elem, defgotype(s))
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0)
+		s := decodetypeMapKey(gotype)
+		newrefattr(die, dwarf.DW_AT_go_key, defgotype(ctxt, s))
+		s = decodetypeMapValue(gotype)
+		newrefattr(die, dwarf.DW_AT_go_elem, defgotype(ctxt, s))
 		// Save gotype for use in synthesizemaptypes. We could synthesize here,
 		// but that would change the order of the DIEs.
-		newrefattr(die, DW_AT_type, gotype)
+		newrefattr(die, dwarf.DW_AT_type, gotype)
 
 	case obj.KindPtr:
-		die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name, 0)
-		dotypedef(&dwtypes, name, die)
-		s := decodetype_ptrelem(gotype)
-		newrefattr(die, DW_AT_type, defgotype(s))
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0)
+		dotypedef(ctxt, &dwtypes, name, die)
+		s := decodetypePtrElem(gotype)
+		newrefattr(die, dwarf.DW_AT_type, defgotype(ctxt, s))
 
 	case obj.KindSlice:
-		die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name, 0)
-		dotypedef(&dwtypes, name, die)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
-		s := decodetype_arrayelem(gotype)
-		elem := defgotype(s)
-		newrefattr(die, DW_AT_go_elem, elem)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0)
+		dotypedef(ctxt, &dwtypes, name, die)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+		s := decodetypeArrayElem(gotype)
+		elem := defgotype(ctxt, s)
+		newrefattr(die, dwarf.DW_AT_go_elem, elem)
 
 	case obj.KindString:
-		die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name, 0)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
 
 	case obj.KindStruct:
-		die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name, 0)
-		dotypedef(&dwtypes, name, die)
-		newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0)
-		nfields := decodetype_structfieldcount(gotype)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0)
+		dotypedef(ctxt, &dwtypes, name, die)
+		newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0)
+		nfields := decodetypeStructFieldCount(ctxt.Arch, gotype)
 		var f string
-		var fld *DWDie
-		var s *LSym
+		var fld *dwarf.DWDie
+		var s *Symbol
 		for i := 0; i < nfields; i++ {
-			f = decodetype_structfieldname(gotype, i)
-			s = decodetype_structfieldtype(gotype, i)
+			f = decodetypeStructFieldName(gotype, i)
+			s = decodetypeStructFieldType(gotype, i)
 			if f == "" {
 				f = s.Name[5:] // skip "type."
 			}
-			fld = newdie(die, DW_ABRV_STRUCTFIELD, f, 0)
-			newrefattr(fld, DW_AT_type, defgotype(s))
-			newmemberoffsetattr(fld, int32(decodetype_structfieldoffs(gotype, i)))
+			fld = newdie(ctxt, die, dwarf.DW_ABRV_STRUCTFIELD, f, 0)
+			newrefattr(fld, dwarf.DW_AT_type, defgotype(ctxt, s))
+			newmemberoffsetattr(fld, int32(decodetypeStructFieldOffs(ctxt.Arch, gotype, i)))
 		}
 
 	case obj.KindUnsafePointer:
-		die = newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, name, 0)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0)
 
 	default:
-		Diag("dwarf: definition of unknown kind %d: %s", kind, gotype.Name)
-		die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name, 0)
-		newrefattr(die, DW_AT_type, mustFind("<unspecified>"))
+		Errorf(gotype, "dwarf: definition of unknown kind %d", kind)
+		die = newdie(ctxt, &dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0)
+		newrefattr(die, dwarf.DW_AT_type, mustFind(ctxt, "<unspecified>"))
 	}
 
-	newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, int64(kind), 0)
+	newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(kind), 0)
 
 	if _, ok := prototypedies[gotype.Name]; ok {
 		prototypedies[gotype.Name] = die
@@ -1020,18 +540,18 @@ func newtype(gotype *LSym) *DWDie {
 	return die
 }
 
-func nameFromDIESym(dwtype *LSym) string {
-	return strings.TrimSuffix(dwtype.Name[len(infoprefix):], "..def")
+func nameFromDIESym(dwtype *Symbol) string {
+	return strings.TrimSuffix(dwtype.Name[len(dwarf.InfoPrefix):], "..def")
 }
 
 // Find or construct *T given T.
-func defptrto(dwtype *LSym) *LSym {
+func defptrto(ctxt *Link, dwtype *Symbol) *Symbol {
 	ptrname := "*" + nameFromDIESym(dwtype)
-	die := find(ptrname)
+	die := find(ctxt, ptrname)
 	if die == nil {
-		pdie := newdie(&dwtypes, DW_ABRV_PTRTYPE, ptrname, 0)
-		newrefattr(pdie, DW_AT_type, dwtype)
-		return pdie.sym
+		pdie := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0)
+		newrefattr(pdie, dwarf.DW_AT_type, dwtype)
+		return dtolsym(pdie.Sym)
 	}
 
 	return die
@@ -1040,79 +560,79 @@ func defptrto(dwtype *LSym) *LSym {
 // Copies src's children into dst. Copies attributes by value.
 // DWAttr.data is copied as pointer only. If except is one of
 // the top-level children, it will not be copied.
-func copychildrenexcept(dst *DWDie, src *DWDie, except *DWDie) {
-	for src = src.child; src != nil; src = src.link {
+func copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie, except *dwarf.DWDie) {
+	for src = src.Child; src != nil; src = src.Link {
 		if src == except {
 			continue
 		}
-		c := newdie(dst, src.abbrev, getattr(src, DW_AT_name).data.(string), 0)
-		for a := src.attr; a != nil; a = a.link {
-			newattr(c, a.atr, int(a.cls), a.value, a.data)
+		c := newdie(ctxt, dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0)
+		for a := src.Attr; a != nil; a = a.Link {
+			newattr(c, a.Atr, int(a.Cls), a.Value, a.Data)
 		}
-		copychildrenexcept(c, src, nil)
+		copychildrenexcept(ctxt, c, src, nil)
 	}
 
-	reverselist(&dst.child)
+	reverselist(&dst.Child)
 }
 
-func copychildren(dst *DWDie, src *DWDie) {
-	copychildrenexcept(dst, src, nil)
+func copychildren(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWDie) {
+	copychildrenexcept(ctxt, dst, src, nil)
 }
 
-// Search children (assumed to have DW_TAG_member) for the one named
-// field and set its DW_AT_type to dwtype
-func substitutetype(structdie *DWDie, field string, dwtype *LSym) {
+// Search children (assumed to have TAG_member) for the one named
+// field and set its AT_type to dwtype
+func substitutetype(structdie *dwarf.DWDie, field string, dwtype *Symbol) {
 	child := findchild(structdie, field)
 	if child == nil {
 		Exitf("dwarf substitutetype: %s does not have member %s",
-			getattr(structdie, DW_AT_name).data, field)
+			getattr(structdie, dwarf.DW_AT_name).Data, field)
 		return
 	}
 
-	a := getattr(child, DW_AT_type)
+	a := getattr(child, dwarf.DW_AT_type)
 	if a != nil {
-		a.data = dwtype
+		a.Data = dwtype
 	} else {
-		newrefattr(child, DW_AT_type, dwtype)
+		newrefattr(child, dwarf.DW_AT_type, dwtype)
 	}
 }
 
-func findprotodie(name string) *DWDie {
+func findprotodie(ctxt *Link, name string) *dwarf.DWDie {
 	die, ok := prototypedies[name]
 	if ok && die == nil {
-		defgotype(lookup_or_diag(name))
+		defgotype(ctxt, lookupOrDiag(ctxt, name))
 		die = prototypedies[name]
 	}
 	return die
 }
 
-func synthesizestringtypes(die *DWDie) {
-	prototype := walktypedef(findprotodie("type.runtime.stringStructDWARF"))
+func synthesizestringtypes(ctxt *Link, die *dwarf.DWDie) {
+	prototype := walktypedef(findprotodie(ctxt, "type.runtime.stringStructDWARF"))
 	if prototype == nil {
 		return
 	}
 
-	for ; die != nil; die = die.link {
-		if die.abbrev != DW_ABRV_STRINGTYPE {
+	for ; die != nil; die = die.Link {
+		if die.Abbrev != dwarf.DW_ABRV_STRINGTYPE {
 			continue
 		}
-		copychildren(die, prototype)
+		copychildren(ctxt, die, prototype)
 	}
 }
 
-func synthesizeslicetypes(die *DWDie) {
-	prototype := walktypedef(findprotodie("type.runtime.slice"))
+func synthesizeslicetypes(ctxt *Link, die *dwarf.DWDie) {
+	prototype := walktypedef(findprotodie(ctxt, "type.runtime.slice"))
 	if prototype == nil {
 		return
 	}
 
-	for ; die != nil; die = die.link {
-		if die.abbrev != DW_ABRV_SLICETYPE {
+	for ; die != nil; die = die.Link {
+		if die.Abbrev != dwarf.DW_ABRV_SLICETYPE {
 			continue
 		}
-		copychildren(die, prototype)
-		elem := getattr(die, DW_AT_go_elem).data.(*LSym)
-		substitutetype(die, "array", defptrto(elem))
+		copychildren(ctxt, die, prototype)
+		elem := getattr(die, dwarf.DW_AT_go_elem).Data.(*Symbol)
+		substitutetype(die, "array", defptrto(ctxt, elem))
 	}
 }
 
@@ -1135,166 +655,166 @@ const (
 	BucketSize = 8
 )
 
-func mkinternaltype(abbrev int, typename, keyname, valname string, f func(*DWDie)) *LSym {
+func mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valname string, f func(*dwarf.DWDie)) *Symbol {
 	name := mkinternaltypename(typename, keyname, valname)
-	symname := infoprefix + name
-	s := Linkrlookup(Ctxt, symname, 0)
-	if s != nil {
+	symname := dwarf.InfoPrefix + name
+	s := ctxt.Syms.ROLookup(symname, 0)
+	if s != nil && s.Type == obj.SDWARFINFO {
 		return s
 	}
-	die := newdie(&dwtypes, abbrev, name, 0)
+	die := newdie(ctxt, &dwtypes, abbrev, name, 0)
 	f(die)
-	return die.sym
+	return dtolsym(die.Sym)
 }
 
-func synthesizemaptypes(die *DWDie) {
-	hash := walktypedef(findprotodie("type.runtime.hmap"))
-	bucket := walktypedef(findprotodie("type.runtime.bmap"))
+func synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
+	hash := walktypedef(findprotodie(ctxt, "type.runtime.hmap"))
+	bucket := walktypedef(findprotodie(ctxt, "type.runtime.bmap"))
 
 	if hash == nil {
 		return
 	}
 
-	for ; die != nil; die = die.link {
-		if die.abbrev != DW_ABRV_MAPTYPE {
+	for ; die != nil; die = die.Link {
+		if die.Abbrev != dwarf.DW_ABRV_MAPTYPE {
 			continue
 		}
-		gotype := getattr(die, DW_AT_type).data.(*LSym)
-		keytype := decodetype_mapkey(gotype)
-		valtype := decodetype_mapvalue(gotype)
-		keysize, valsize := decodetype_size(keytype), decodetype_size(valtype)
-		keytype, valtype = walksymtypedef(defgotype(keytype)), walksymtypedef(defgotype(valtype))
+		gotype := getattr(die, dwarf.DW_AT_type).Data.(*Symbol)
+		keytype := decodetypeMapKey(gotype)
+		valtype := decodetypeMapValue(gotype)
+		keysize, valsize := decodetypeSize(ctxt.Arch, keytype), decodetypeSize(ctxt.Arch, valtype)
+		keytype, valtype = walksymtypedef(ctxt, defgotype(ctxt, keytype)), walksymtypedef(ctxt, defgotype(ctxt, valtype))
 
 		// compute size info like hashmap.c does.
-		indirect_key, indirect_val := false, false
+		indirectKey, indirectVal := false, false
 		if keysize > MaxKeySize {
 			keysize = int64(SysArch.PtrSize)
-			indirect_key = true
+			indirectKey = true
 		}
 		if valsize > MaxValSize {
 			valsize = int64(SysArch.PtrSize)
-			indirect_val = true
+			indirectVal = true
 		}
 
 		// Construct type to represent an array of BucketSize keys
 		keyname := nameFromDIESym(keytype)
-		dwhks := mkinternaltype(DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *DWDie) {
-			newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*keysize, 0)
+		dwhks := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) {
+			newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*keysize, 0)
 			t := keytype
-			if indirect_key {
-				t = defptrto(keytype)
+			if indirectKey {
+				t = defptrto(ctxt, keytype)
 			}
-			newrefattr(dwhk, DW_AT_type, t)
-			fld := newdie(dwhk, DW_ABRV_ARRAYRANGE, "size", 0)
-			newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0)
-			newrefattr(fld, DW_AT_type, mustFind("uintptr"))
+			newrefattr(dwhk, dwarf.DW_AT_type, t)
+			fld := newdie(ctxt, dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
+			newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
+			newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
 		})
 
 		// Construct type to represent an array of BucketSize values
 		valname := nameFromDIESym(valtype)
-		dwhvs := mkinternaltype(DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *DWDie) {
-			newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*valsize, 0)
+		dwhvs := mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) {
+			newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize*valsize, 0)
 			t := valtype
-			if indirect_val {
-				t = defptrto(valtype)
+			if indirectVal {
+				t = defptrto(ctxt, valtype)
 			}
-			newrefattr(dwhv, DW_AT_type, t)
-			fld := newdie(dwhv, DW_ABRV_ARRAYRANGE, "size", 0)
-			newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0)
-			newrefattr(fld, DW_AT_type, mustFind("uintptr"))
+			newrefattr(dwhv, dwarf.DW_AT_type, t)
+			fld := newdie(ctxt, dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0)
+			newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0)
+			newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
 		})
 
 		// Construct bucket<K,V>
-		dwhbs := mkinternaltype(DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *DWDie) {
+		dwhbs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) {
 			// Copy over all fields except the field "data" from the generic
 			// bucket. "data" will be replaced with keys/values below.
-			copychildrenexcept(dwhb, bucket, findchild(bucket, "data"))
+			copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
 
-			fld := newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys", 0)
-			newrefattr(fld, DW_AT_type, dwhks)
+			fld := newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0)
+			newrefattr(fld, dwarf.DW_AT_type, dwhks)
 			newmemberoffsetattr(fld, BucketSize)
-			fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values", 0)
-			newrefattr(fld, DW_AT_type, dwhvs)
+			fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0)
+			newrefattr(fld, dwarf.DW_AT_type, dwhvs)
 			newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize))
-			fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow", 0)
-			newrefattr(fld, DW_AT_type, defptrto(dwhb.sym))
+			fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0)
+			newrefattr(fld, dwarf.DW_AT_type, defptrto(ctxt, dtolsym(dwhb.Sym)))
 			newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize)))
 			if SysArch.RegSize > SysArch.PtrSize {
-				fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad", 0)
-				newrefattr(fld, DW_AT_type, mustFind("uintptr"))
+				fld = newdie(ctxt, dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0)
+				newrefattr(fld, dwarf.DW_AT_type, mustFind(ctxt, "uintptr"))
 				newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(SysArch.PtrSize))
 			}
 
-			newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(SysArch.RegSize), 0)
+			newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, BucketSize+BucketSize*keysize+BucketSize*valsize+int64(SysArch.RegSize), 0)
 		})
 
 		// Construct hash<K,V>
-		dwhs := mkinternaltype(DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *DWDie) {
-			copychildren(dwh, hash)
-			substitutetype(dwh, "buckets", defptrto(dwhbs))
-			substitutetype(dwh, "oldbuckets", defptrto(dwhbs))
-			newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hash, DW_AT_byte_size).value, nil)
+		dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) {
+			copychildren(ctxt, dwh, hash)
+			substitutetype(dwh, "buckets", defptrto(ctxt, dwhbs))
+			substitutetype(dwh, "oldbuckets", defptrto(ctxt, dwhbs))
+			newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil)
 		})
 
 		// make map type a pointer to hash<K,V>
-		newrefattr(die, DW_AT_type, defptrto(dwhs))
+		newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs))
 	}
 }
 
-func synthesizechantypes(die *DWDie) {
-	sudog := walktypedef(findprotodie("type.runtime.sudog"))
-	waitq := walktypedef(findprotodie("type.runtime.waitq"))
-	hchan := walktypedef(findprotodie("type.runtime.hchan"))
+func synthesizechantypes(ctxt *Link, die *dwarf.DWDie) {
+	sudog := walktypedef(findprotodie(ctxt, "type.runtime.sudog"))
+	waitq := walktypedef(findprotodie(ctxt, "type.runtime.waitq"))
+	hchan := walktypedef(findprotodie(ctxt, "type.runtime.hchan"))
 	if sudog == nil || waitq == nil || hchan == nil {
 		return
 	}
 
-	sudogsize := int(getattr(sudog, DW_AT_byte_size).value)
+	sudogsize := int(getattr(sudog, dwarf.DW_AT_byte_size).Value)
 
-	for ; die != nil; die = die.link {
-		if die.abbrev != DW_ABRV_CHANTYPE {
+	for ; die != nil; die = die.Link {
+		if die.Abbrev != dwarf.DW_ABRV_CHANTYPE {
 			continue
 		}
-		elemgotype := getattr(die, DW_AT_type).data.(*LSym)
-		elemsize := decodetype_size(elemgotype)
+		elemgotype := getattr(die, dwarf.DW_AT_type).Data.(*Symbol)
+		elemsize := decodetypeSize(ctxt.Arch, elemgotype)
 		elemname := elemgotype.Name[5:]
-		elemtype := walksymtypedef(defgotype(elemgotype))
+		elemtype := walksymtypedef(ctxt, defgotype(ctxt, elemgotype))
 
 		// sudog<T>
-		dwss := mkinternaltype(DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *DWDie) {
-			copychildren(dws, sudog)
+		dwss := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "sudog", elemname, "", func(dws *dwarf.DWDie) {
+			copychildren(ctxt, dws, sudog)
 			substitutetype(dws, "elem", elemtype)
 			if elemsize > 8 {
 				elemsize -= 8
 			} else {
 				elemsize = 0
 			}
-			newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, int64(sudogsize)+elemsize, nil)
+			newattr(dws, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(sudogsize)+elemsize, nil)
 		})
 
 		// waitq<T>
-		dwws := mkinternaltype(DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *DWDie) {
+		dwws := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "waitq", elemname, "", func(dww *dwarf.DWDie) {
 
-			copychildren(dww, waitq)
-			substitutetype(dww, "first", defptrto(dwss))
-			substitutetype(dww, "last", defptrto(dwss))
-			newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(waitq, DW_AT_byte_size).value, nil)
+			copychildren(ctxt, dww, waitq)
+			substitutetype(dww, "first", defptrto(ctxt, dwss))
+			substitutetype(dww, "last", defptrto(ctxt, dwss))
+			newattr(dww, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(waitq, dwarf.DW_AT_byte_size).Value, nil)
 		})
 
 		// hchan<T>
-		dwhs := mkinternaltype(DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *DWDie) {
-			copychildren(dwh, hchan)
+		dwhs := mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hchan", elemname, "", func(dwh *dwarf.DWDie) {
+			copychildren(ctxt, dwh, hchan)
 			substitutetype(dwh, "recvq", dwws)
 			substitutetype(dwh, "sendq", dwws)
-			newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hchan, DW_AT_byte_size).value, nil)
+			newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hchan, dwarf.DW_AT_byte_size).Value, nil)
 		})
 
-		newrefattr(die, DW_AT_type, defptrto(dwhs))
+		newrefattr(die, dwarf.DW_AT_type, defptrto(ctxt, dwhs))
 	}
 }
 
 // For use with pass.c::genasmsym
-func defdwsymb(sym *LSym, s string, t int, v int64, size int64, ver int, gotype *LSym) {
+func defdwsymb(ctxt *Link, sym *Symbol, s string, t SymbolType, v int64, gotype *Symbol) {
 	if strings.HasPrefix(s, "go.string.") {
 		return
 	}
@@ -1303,44 +823,48 @@ func defdwsymb(sym *LSym, s string, t int, v int64, size int64, ver int, gotype
 	}
 
 	if strings.HasPrefix(s, "type.") && s != "type.*" && !strings.HasPrefix(s, "type..") {
-		defgotype(sym)
+		defgotype(ctxt, sym)
 		return
 	}
 
-	var dv *DWDie
+	var dv *dwarf.DWDie
 
-	var dt *LSym
+	var dt *Symbol
 	switch t {
 	default:
 		return
 
-	case 'd', 'b', 'D', 'B':
-		dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s, ver)
+	case DataSym, BSSSym:
+		dv = newdie(ctxt, &dwglobals, dwarf.DW_ABRV_VARIABLE, s, int(sym.Version))
 		newabslocexprattr(dv, v, sym)
-		if ver == 0 {
-			newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0)
+		if sym.Version == 0 {
+			newattr(dv, dwarf.DW_AT_external, dwarf.DW_CLS_FLAG, 1, 0)
 		}
 		fallthrough
 
-	case 'a', 'p':
-		dt = defgotype(gotype)
+	case AutoSym, ParamSym:
+		dt = defgotype(ctxt, gotype)
 	}
 
 	if dv != nil {
-		newrefattr(dv, DW_AT_type, dt)
+		newrefattr(dv, dwarf.DW_AT_type, dt)
 	}
 }
 
-func movetomodule(parent *DWDie) {
-	die := dwroot.child.child
-	for die.link != nil {
-		die = die.link
+func movetomodule(parent *dwarf.DWDie) {
+	die := dwroot.Child.Child
+	if die == nil {
+		dwroot.Child.Child = parent.Child
+		return
+	}
+	for die.Link != nil {
+		die = die.Link
 	}
-	die.link = parent.child
+	die.Link = parent.Child
 }
 
 // If the pcln table contains runtime/runtime.go, use that to set gdbscript path.
-func finddebugruntimepath(s *LSym) {
+func finddebugruntimepath(s *Symbol) {
 	if gdbscript != "" {
 		return
 	}
@@ -1355,51 +879,104 @@ func finddebugruntimepath(s *LSym) {
 }
 
 /*
- * Generate short opcodes when possible, long ones when necessary.
+ * Generate a sequence of opcodes that is as short as possible.
  * See section 6.2.5
  */
 const (
-	LINE_BASE   = -1
-	LINE_RANGE  = 4
+	LINE_BASE   = -4
+	LINE_RANGE  = 10
+	PC_RANGE    = (255 - OPCODE_BASE) / LINE_RANGE
 	OPCODE_BASE = 10
 )
 
-func putpclcdelta(s *LSym, delta_pc int64, delta_lc int64) {
-	if LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE {
-		var opcode int64 = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc)
-		if OPCODE_BASE <= opcode && opcode < 256 {
-			Adduint8(Ctxt, s, uint8(opcode))
-			return
+func putpclcdelta(linkctxt *Link, ctxt dwarf.Context, s *Symbol, deltaPC uint64, deltaLC int64) {
+	// Choose a special opcode that minimizes the number of bytes needed to
+	// encode the remaining PC delta and LC delta.
+	var opcode int64
+	if deltaLC < LINE_BASE {
+		if deltaPC >= PC_RANGE {
+			opcode = OPCODE_BASE + (LINE_RANGE * PC_RANGE)
+		} else {
+			opcode = OPCODE_BASE + (LINE_RANGE * int64(deltaPC))
+		}
+	} else if deltaLC < LINE_BASE+LINE_RANGE {
+		if deltaPC >= PC_RANGE {
+			opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * PC_RANGE)
+			if opcode > 255 {
+				opcode -= LINE_RANGE
+			}
+		} else {
+			opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * int64(deltaPC))
+		}
+	} else {
+		if deltaPC <= PC_RANGE {
+			opcode = OPCODE_BASE + (LINE_RANGE - 1) + (LINE_RANGE * int64(deltaPC))
+			if opcode > 255 {
+				opcode = 255
+			}
+		} else {
+			// Use opcode 249 (pc+=23, lc+=5) or 255 (pc+=24, lc+=1).
+			//
+			// Let x=deltaPC-PC_RANGE.  If we use opcode 255, x will be the remaining
+			// deltaPC that we need to encode separately before emitting 255.  If we
+			// use opcode 249, we will need to encode x+1.  If x+1 takes one more
+			// byte to encode than x, then we use opcode 255.
+			//
+			// In all other cases x and x+1 take the same number of bytes to encode,
+			// so we use opcode 249, which may save us a byte in encoding deltaLC,
+			// for similar reasons.
+			switch deltaPC - PC_RANGE {
+			// PC_RANGE is the largest deltaPC we can encode in one byte, using
+			// DW_LNS_const_add_pc.
+			//
+			// (1<<16)-1 is the largest deltaPC we can encode in three bytes, using
+			// DW_LNS_fixed_advance_pc.
+			//
+			// (1<<(7n))-1 is the largest deltaPC we can encode in n+1 bytes for
+			// n=1,3,4,5,..., using DW_LNS_advance_pc.
+			case PC_RANGE, (1 << 7) - 1, (1 << 16) - 1, (1 << 21) - 1, (1 << 28) - 1,
+				(1 << 35) - 1, (1 << 42) - 1, (1 << 49) - 1, (1 << 56) - 1, (1 << 63) - 1:
+				opcode = 255
+			default:
+				opcode = OPCODE_BASE + LINE_RANGE*PC_RANGE - 1 // 249
+			}
 		}
 	}
-
-	if delta_pc != 0 {
-		Adduint8(Ctxt, s, DW_LNS_advance_pc)
-		sleb128put(s, delta_pc)
+	if opcode < OPCODE_BASE || opcode > 255 {
+		panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
 	}
 
-	Adduint8(Ctxt, s, DW_LNS_advance_line)
-	sleb128put(s, delta_lc)
-	Adduint8(Ctxt, s, DW_LNS_copy)
-}
-
-func newcfaoffsetattr(die *DWDie, offs int32) {
-	var block [20]byte
-	b := append(block[:0], DW_OP_call_frame_cfa)
+	// Subtract from deltaPC and deltaLC the amounts that the opcode will add.
+	deltaPC -= uint64((opcode - OPCODE_BASE) / LINE_RANGE)
+	deltaLC -= int64((opcode-OPCODE_BASE)%LINE_RANGE + LINE_BASE)
 
-	if offs != 0 {
-		b = append(b, DW_OP_consts)
-		b = appendSleb128(b, int64(offs))
-		b = append(b, DW_OP_plus)
+	// Encode deltaPC.
+	if deltaPC != 0 {
+		if deltaPC <= PC_RANGE {
+			// Adjust the opcode so that we can use the 1-byte DW_LNS_const_add_pc
+			// instruction.
+			opcode -= LINE_RANGE * int64(PC_RANGE-deltaPC)
+			if opcode < OPCODE_BASE {
+				panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
+			}
+			Adduint8(linkctxt, s, dwarf.DW_LNS_const_add_pc)
+		} else if (1<<14) <= deltaPC && deltaPC < (1<<16) {
+			Adduint8(linkctxt, s, dwarf.DW_LNS_fixed_advance_pc)
+			Adduint16(linkctxt, s, uint16(deltaPC))
+		} else {
+			Adduint8(linkctxt, s, dwarf.DW_LNS_advance_pc)
+			dwarf.Uleb128put(ctxt, s, int64(deltaPC))
+		}
 	}
 
-	newattr(die, DW_AT_location, DW_CLS_BLOCK, int64(len(b)), b)
-}
+	// Encode deltaLC.
+	if deltaLC != 0 {
+		Adduint8(linkctxt, s, dwarf.DW_LNS_advance_line)
+		dwarf.Sleb128put(ctxt, s, deltaLC)
+	}
 
-func mkvarname(name string, da int) string {
-	buf := fmt.Sprintf("%s#%d", name, da)
-	n := buf
-	return n
+	// Output the special opcode.
+	Adduint8(linkctxt, s, uint8(opcode))
 }
 
 /*
@@ -1413,100 +990,109 @@ func getCompilationDir() string {
 	return "/"
 }
 
-func writelines(prev *LSym) *LSym {
+func writelines(ctxt *Link, syms []*Symbol) ([]*Symbol, []*Symbol) {
+	var dwarfctxt dwarf.Context = dwctxt{ctxt}
 	if linesec == nil {
-		linesec = Linklookup(Ctxt, ".debug_line", 0)
+		linesec = ctxt.Syms.Lookup(".debug_line", 0)
 	}
 	linesec.Type = obj.SDWARFSECT
 	linesec.R = linesec.R[:0]
 
 	ls := linesec
-	prev.Next = ls
+	syms = append(syms, ls)
+	var funcs []*Symbol
 
 	unitstart := int64(-1)
 	headerstart := int64(-1)
 	headerend := int64(-1)
 	epc := int64(0)
-	var epcs *LSym
-	var dwinfo *DWDie
+	var epcs *Symbol
+	var dwinfo *dwarf.DWDie
 
-	lang := DW_LANG_Go
+	lang := dwarf.DW_LANG_Go
 
-	s := Ctxt.Textp[0]
+	s := ctxt.Textp[0]
+	if ctxt.DynlinkingGo() && Headtype == obj.Hdarwin {
+		s = ctxt.Textp[1] // skip runtime.text
+	}
 
-	dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, "go", 0)
-	newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, int64(lang), 0)
-	newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, 0, 0)
-	newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s)
+	dwinfo = newdie(ctxt, &dwroot, dwarf.DW_ABRV_COMPUNIT, "go", 0)
+	newattr(dwinfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(lang), 0)
+	newattr(dwinfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, 0, linesec)
+	newattr(dwinfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, s.Value, s)
 	// OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
 	compDir := getCompilationDir()
-	newattr(dwinfo, DW_AT_comp_dir, DW_CLS_STRING, int64(len(compDir)), compDir)
+	newattr(dwinfo, dwarf.DW_AT_comp_dir, dwarf.DW_CLS_STRING, int64(len(compDir)), compDir)
 
 	// Write .debug_line Line Number Program Header (sec 6.2.4)
 	// Fields marked with (*) must be changed for 64-bit dwarf
-	unit_length_offset := ls.Size
-	Adduint32(Ctxt, ls, 0) // unit_length (*), filled in at end.
+	unitLengthOffset := ls.Size
+	Adduint32(ctxt, ls, 0) // unit_length (*), filled in at end.
 	unitstart = ls.Size
-	Adduint16(Ctxt, ls, 2) // dwarf version (appendix F)
-	header_length_offset := ls.Size
-	Adduint32(Ctxt, ls, 0) // header_length (*), filled in at end.
+	Adduint16(ctxt, ls, 2) // dwarf version (appendix F)
+	headerLengthOffset := ls.Size
+	Adduint32(ctxt, ls, 0) // header_length (*), filled in at end.
 	headerstart = ls.Size
 
 	// cpos == unitstart + 4 + 2 + 4
-	Adduint8(Ctxt, ls, 1)              // minimum_instruction_length
-	Adduint8(Ctxt, ls, 1)              // default_is_stmt
-	Adduint8(Ctxt, ls, LINE_BASE&0xFF) // line_base
-	Adduint8(Ctxt, ls, LINE_RANGE)     // line_range
-	Adduint8(Ctxt, ls, OPCODE_BASE)    // opcode_base
-	Adduint8(Ctxt, ls, 0)              // standard_opcode_lengths[1]
-	Adduint8(Ctxt, ls, 1)              // standard_opcode_lengths[2]
-	Adduint8(Ctxt, ls, 1)              // standard_opcode_lengths[3]
-	Adduint8(Ctxt, ls, 1)              // standard_opcode_lengths[4]
-	Adduint8(Ctxt, ls, 1)              // standard_opcode_lengths[5]
-	Adduint8(Ctxt, ls, 0)              // standard_opcode_lengths[6]
-	Adduint8(Ctxt, ls, 0)              // standard_opcode_lengths[7]
-	Adduint8(Ctxt, ls, 0)              // standard_opcode_lengths[8]
-	Adduint8(Ctxt, ls, 1)              // standard_opcode_lengths[9]
-	Adduint8(Ctxt, ls, 0)              // include_directories  (empty)
-
-	for _, f := range Ctxt.Filesyms {
+	Adduint8(ctxt, ls, 1)              // minimum_instruction_length
+	Adduint8(ctxt, ls, 1)              // default_is_stmt
+	Adduint8(ctxt, ls, LINE_BASE&0xFF) // line_base
+	Adduint8(ctxt, ls, LINE_RANGE)     // line_range
+	Adduint8(ctxt, ls, OPCODE_BASE)    // opcode_base
+	Adduint8(ctxt, ls, 0)              // standard_opcode_lengths[1]
+	Adduint8(ctxt, ls, 1)              // standard_opcode_lengths[2]
+	Adduint8(ctxt, ls, 1)              // standard_opcode_lengths[3]
+	Adduint8(ctxt, ls, 1)              // standard_opcode_lengths[4]
+	Adduint8(ctxt, ls, 1)              // standard_opcode_lengths[5]
+	Adduint8(ctxt, ls, 0)              // standard_opcode_lengths[6]
+	Adduint8(ctxt, ls, 0)              // standard_opcode_lengths[7]
+	Adduint8(ctxt, ls, 0)              // standard_opcode_lengths[8]
+	Adduint8(ctxt, ls, 1)              // standard_opcode_lengths[9]
+	Adduint8(ctxt, ls, 0)              // include_directories  (empty)
+
+	for _, f := range ctxt.Filesyms {
 		Addstring(ls, f.Name)
-		Adduint8(Ctxt, ls, 0)
-		Adduint8(Ctxt, ls, 0)
-		Adduint8(Ctxt, ls, 0)
+		Adduint8(ctxt, ls, 0)
+		Adduint8(ctxt, ls, 0)
+		Adduint8(ctxt, ls, 0)
 	}
 
 	// 4 zeros: the string termination + 3 fields.
-	Adduint8(Ctxt, ls, 0)
+	Adduint8(ctxt, ls, 0)
 	// terminate file_names.
 	headerend = ls.Size
 
-	Adduint8(Ctxt, ls, 0) // start extended opcode
-	uleb128put(ls, 1+int64(SysArch.PtrSize))
-	Adduint8(Ctxt, ls, DW_LNE_set_address)
+	Adduint8(ctxt, ls, 0) // start extended opcode
+	dwarf.Uleb128put(dwarfctxt, ls, 1+int64(SysArch.PtrSize))
+	Adduint8(ctxt, ls, dwarf.DW_LNE_set_address)
 
 	pc := s.Value
 	line := 1
 	file := 1
-	if Linkmode == LinkExternal {
-		Addaddr(Ctxt, ls, s)
-	} else {
-		addrput(ls, pc)
-	}
+	Addaddr(ctxt, ls, s)
 
 	var pcfile Pciter
 	var pcline Pciter
-	for _, Ctxt.Cursym = range Ctxt.Textp {
-		s := Ctxt.Cursym
+	for _, s := range ctxt.Textp {
 
-		dwfunc := newdie(dwinfo, DW_ABRV_FUNCTION, s.Name, int(s.Version))
-		newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s)
 		epc = s.Value + s.Size
 		epcs = s
-		newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, s)
-		if s.Version == 0 {
-			newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0)
+
+		dsym := ctxt.Syms.Lookup(dwarf.InfoPrefix+s.Name, int(s.Version))
+		dsym.Attr |= AttrHidden
+		dsym.Type = obj.SDWARFINFO
+		for _, r := range dsym.R {
+			if r.Type == obj.R_DWARFREF && r.Sym.Size == 0 {
+				if Buildmode == BuildmodeShared {
+					// These type symbols may not be present in BuildmodeShared. Skip.
+					continue
+				}
+				n := nameFromDIESym(r.Sym)
+				defgotype(ctxt, ctxt.Syms.Lookup("type."+n, 0))
+			}
 		}
+		funcs = append(funcs, dsym)
 
 		if s.FuncInfo == nil {
 			continue
@@ -1514,8 +1100,8 @@ func writelines(prev *LSym) *LSym {
 
 		finddebugruntimepath(s)
 
-		pciterinit(Ctxt, &pcfile, &s.FuncInfo.Pcfile)
-		pciterinit(Ctxt, &pcline, &s.FuncInfo.Pcline)
+		pciterinit(ctxt, &pcfile, &s.FuncInfo.Pcfile)
+		pciterinit(ctxt, &pcline, &s.FuncInfo.Pcline)
 		epc = pc
 		for pcfile.done == 0 && pcline.done == 0 {
 			if epc-s.Value >= int64(pcfile.nextpc) {
@@ -1529,12 +1115,12 @@ func writelines(prev *LSym) *LSym {
 			}
 
 			if int32(file) != pcfile.value {
-				Adduint8(Ctxt, ls, DW_LNS_set_file)
-				uleb128put(ls, int64(pcfile.value))
+				Adduint8(ctxt, ls, dwarf.DW_LNS_set_file)
+				dwarf.Uleb128put(dwarfctxt, ls, int64(pcfile.value))
 				file = int(pcfile.value)
 			}
 
-			putpclcdelta(ls, s.Value+int64(pcline.pc)-pc, int64(pcline.value)-int64(line))
+			putpclcdelta(ctxt, dwarfctxt, ls, uint64(s.Value+int64(pcline.pc)-pc), int64(pcline.value)-int64(line))
 
 			pc = s.Value + int64(pcline.pc)
 			line = int(pcline.value)
@@ -1545,80 +1131,18 @@ func writelines(prev *LSym) *LSym {
 			}
 			epc += s.Value
 		}
-
-		var (
-			dt, da int
-			offs   int64
-		)
-		for _, a := range s.FuncInfo.Autom {
-			switch a.Name {
-			case obj.A_AUTO:
-				dt = DW_ABRV_AUTO
-				offs = int64(a.Aoffset)
-				if !haslinkregister() {
-					offs -= int64(SysArch.PtrSize)
-				}
-				if obj.Framepointer_enabled(obj.Getgoos(), obj.Getgoarch()) {
-					// The frame pointer is saved
-					// between the CFA and the
-					// autos.
-					offs -= int64(SysArch.PtrSize)
-				}
-
-			case obj.A_PARAM:
-				dt = DW_ABRV_PARAM
-				offs = int64(a.Aoffset) + Ctxt.FixedFrameSize()
-
-			default:
-				continue
-			}
-
-			if strings.Contains(a.Asym.Name, ".autotmp_") {
-				continue
-			}
-			var n string
-			if findchild(dwfunc, a.Asym.Name) != nil {
-				n = mkvarname(a.Asym.Name, da)
-			} else {
-				n = a.Asym.Name
-			}
-
-			// Drop the package prefix from locals and arguments.
-			if i := strings.LastIndex(n, "."); i >= 0 {
-				n = n[i+1:]
-			}
-
-			dwvar := newdie(dwfunc, dt, n, 0)
-			newcfaoffsetattr(dwvar, int32(offs))
-			newrefattr(dwvar, DW_AT_type, defgotype(a.Gotype))
-
-			// push dwvar down dwfunc->child to preserve order
-			newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, nil)
-
-			dwfunc.child = dwvar.link // take dwvar out from the top of the list
-			dws := &dwfunc.child
-			for ; *dws != nil; dws = &(*dws).link {
-				if offs > getattr(*dws, DW_AT_internal_location).value {
-					break
-				}
-			}
-			dwvar.link = *dws
-			*dws = dwvar
-
-			da++
-		}
 	}
 
-	Adduint8(Ctxt, ls, 0) // start extended opcode
-	uleb128put(ls, 1)
-	Adduint8(Ctxt, ls, DW_LNE_end_sequence)
+	Adduint8(ctxt, ls, 0) // start extended opcode
+	dwarf.Uleb128put(dwarfctxt, ls, 1)
+	Adduint8(ctxt, ls, dwarf.DW_LNE_end_sequence)
 
-	newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, epc+1, epcs)
+	newattr(dwinfo, dwarf.DW_AT_high_pc, dwarf.DW_CLS_ADDRESS, epc+1, epcs)
 
-	setuint32(Ctxt, ls, unit_length_offset, uint32(ls.Size-unitstart))
-	setuint32(Ctxt, ls, header_length_offset, uint32(headerend-headerstart))
+	setuint32(ctxt, ls, unitLengthOffset, uint32(ls.Size-unitstart))
+	setuint32(ctxt, ls, headerLengthOffset, uint32(headerend-headerstart))
 
-	return ls
+	return syms, funcs
 }
 
 /*
@@ -1630,64 +1154,65 @@ const (
 
 // appendPCDeltaCFA appends per-PC CFA deltas to b and returns the final slice.
 func appendPCDeltaCFA(b []byte, deltapc, cfa int64) []byte {
-	b = append(b, DW_CFA_def_cfa_offset_sf)
-	b = appendSleb128(b, cfa/dataAlignmentFactor)
+	b = append(b, dwarf.DW_CFA_def_cfa_offset_sf)
+	b = dwarf.AppendSleb128(b, cfa/dataAlignmentFactor)
 
 	switch {
 	case deltapc < 0x40:
-		b = append(b, uint8(DW_CFA_advance_loc+deltapc))
+		b = append(b, uint8(dwarf.DW_CFA_advance_loc+deltapc))
 	case deltapc < 0x100:
-		b = append(b, DW_CFA_advance_loc1)
+		b = append(b, dwarf.DW_CFA_advance_loc1)
 		b = append(b, uint8(deltapc))
 	case deltapc < 0x10000:
-		b = append(b, DW_CFA_advance_loc2)
+		b = append(b, dwarf.DW_CFA_advance_loc2)
 		b = Thearch.Append16(b, uint16(deltapc))
 	default:
-		b = append(b, DW_CFA_advance_loc4)
+		b = append(b, dwarf.DW_CFA_advance_loc4)
 		b = Thearch.Append32(b, uint32(deltapc))
 	}
 	return b
 }
 
-func writeframes(prev *LSym) *LSym {
+func writeframes(ctxt *Link, syms []*Symbol) []*Symbol {
+	var dwarfctxt dwarf.Context = dwctxt{ctxt}
 	if framesec == nil {
-		framesec = Linklookup(Ctxt, ".debug_frame", 0)
+		framesec = ctxt.Syms.Lookup(".debug_frame", 0)
 	}
 	framesec.Type = obj.SDWARFSECT
 	framesec.R = framesec.R[:0]
 	fs := framesec
-	prev.Next = fs
+	syms = append(syms, fs)
 
 	// Emit the CIE, Section 6.4.1
 	cieReserve := uint32(16)
-	if haslinkregister() {
+	if haslinkregister(ctxt) {
 		cieReserve = 32
 	}
-	Adduint32(Ctxt, fs, cieReserve)           // initial length, must be multiple of pointer size
-	Adduint32(Ctxt, fs, 0xffffffff)           // cid.
-	Adduint8(Ctxt, fs, 3)                     // dwarf version (appendix F)
-	Adduint8(Ctxt, fs, 0)                     // augmentation ""
-	uleb128put(fs, 1)                         // code_alignment_factor
-	sleb128put(fs, dataAlignmentFactor)       // all CFI offset calculations include multiplication with this factor
-	uleb128put(fs, int64(Thearch.Dwarfreglr)) // return_address_register
-
-	Adduint8(Ctxt, fs, DW_CFA_def_cfa)        // Set the current frame address..
-	uleb128put(fs, int64(Thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)...
-	if haslinkregister() {
-		uleb128put(fs, int64(0)) // ...plus a 0 offset.
-
-		Adduint8(Ctxt, fs, DW_CFA_same_value) // The platform's link register is unchanged during the prologue.
-		uleb128put(fs, int64(Thearch.Dwarfreglr))
-
-		Adduint8(Ctxt, fs, DW_CFA_val_offset)     // The previous value...
-		uleb128put(fs, int64(Thearch.Dwarfregsp)) // ...of the platform's SP register...
-		uleb128put(fs, int64(0))                  // ...is CFA+0.
+	Adduint32(ctxt, fs, cieReserve)                            // initial length, must be multiple of thearch.ptrsize
+	Adduint32(ctxt, fs, 0xffffffff)                            // cid.
+	Adduint8(ctxt, fs, 3)                                      // dwarf version (appendix F)
+	Adduint8(ctxt, fs, 0)                                      // augmentation ""
+	dwarf.Uleb128put(dwarfctxt, fs, 1)                         // code_alignment_factor
+	dwarf.Sleb128put(dwarfctxt, fs, dataAlignmentFactor)       // all CFI offset calculations include multiplication with this factor
+	dwarf.Uleb128put(dwarfctxt, fs, int64(Thearch.Dwarfreglr)) // return_address_register
+
+	Adduint8(ctxt, fs, dwarf.DW_CFA_def_cfa)                   // Set the current frame address..
+	dwarf.Uleb128put(dwarfctxt, fs, int64(Thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)...
+	if haslinkregister(ctxt) {
+		dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...plus a 0 offset.
+
+		Adduint8(ctxt, fs, dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue.
+		dwarf.Uleb128put(dwarfctxt, fs, int64(Thearch.Dwarfreglr))
+
+		Adduint8(ctxt, fs, dwarf.DW_CFA_val_offset)                // The previous value...
+		dwarf.Uleb128put(dwarfctxt, fs, int64(Thearch.Dwarfregsp)) // ...of the platform's SP register...
+		dwarf.Uleb128put(dwarfctxt, fs, int64(0))                  // ...is CFA+0.
 	} else {
-		uleb128put(fs, int64(SysArch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame).
+		dwarf.Uleb128put(dwarfctxt, fs, int64(SysArch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame).
 
-		Adduint8(Ctxt, fs, DW_CFA_offset_extended)                  // The previous value...
-		uleb128put(fs, int64(Thearch.Dwarfreglr))                   // ...of the return address...
-		uleb128put(fs, int64(-SysArch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)].
+		Adduint8(ctxt, fs, dwarf.DW_CFA_offset_extended)                             // The previous value...
+		dwarf.Uleb128put(dwarfctxt, fs, int64(Thearch.Dwarfreglr))                   // ...of the return address...
+		dwarf.Uleb128put(dwarfctxt, fs, int64(-SysArch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)].
 	}
 
 	// 4 is to exclude the length field.
@@ -1697,12 +1222,11 @@ func writeframes(prev *LSym) *LSym {
 		Exitf("dwarf: cieReserve too small by %d bytes.", -pad)
 	}
 
-	Addbytes(Ctxt, fs, zeros[:pad])
+	Addbytes(fs, zeros[:pad])
 
 	var deltaBuf []byte
 	var pcsp Pciter
-	for _, Ctxt.Cursym = range Ctxt.Textp {
-		s := Ctxt.Cursym
+	for _, s := range ctxt.Textp {
 		if s.FuncInfo == nil {
 			continue
 		}
@@ -1710,7 +1234,7 @@ func writeframes(prev *LSym) *LSym {
 		// Emit a FDE, Section 6.4.1.
 		// First build the section contents into a byte buffer.
 		deltaBuf = deltaBuf[:0]
-		for pciterinit(Ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
+		for pciterinit(ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
 			nextpc := pcsp.nextpc
 
 			// pciterinit goes up to the end of the function,
@@ -1722,21 +1246,21 @@ func writeframes(prev *LSym) *LSym {
 				}
 			}
 
-			if haslinkregister() {
+			if haslinkregister(ctxt) {
 				// TODO(bryanpkc): This is imprecise. In general, the instruction
 				// that stores the return address to the stack frame is not the
 				// same one that allocates the frame.
 				if pcsp.value > 0 {
 					// The return address is preserved at (CFA-frame_size)
 					// after a stack frame has been allocated.
-					deltaBuf = append(deltaBuf, DW_CFA_offset_extended_sf)
-					deltaBuf = appendUleb128(deltaBuf, uint64(Thearch.Dwarfreglr))
-					deltaBuf = appendSleb128(deltaBuf, -int64(pcsp.value)/dataAlignmentFactor)
+					deltaBuf = append(deltaBuf, dwarf.DW_CFA_offset_extended_sf)
+					deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(Thearch.Dwarfreglr))
+					deltaBuf = dwarf.AppendSleb128(deltaBuf, -int64(pcsp.value)/dataAlignmentFactor)
 				} else {
 					// The return address is restored into the link register
 					// when a stack frame has been de-allocated.
-					deltaBuf = append(deltaBuf, DW_CFA_same_value)
-					deltaBuf = appendUleb128(deltaBuf, uint64(Thearch.Dwarfreglr))
+					deltaBuf = append(deltaBuf, dwarf.DW_CFA_same_value)
+					deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(Thearch.Dwarfreglr))
 				}
 				deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(pcsp.value))
 			} else {
@@ -1751,17 +1275,17 @@ func writeframes(prev *LSym) *LSym {
 		//	4 bytes: Pointer to the CIE above, at offset 0
 		//	ptrsize: initial location
 		//	ptrsize: address range
-		Adduint32(Ctxt, fs, uint32(4+2*SysArch.PtrSize+len(deltaBuf))) // length (excludes itself)
+		Adduint32(ctxt, fs, uint32(4+2*SysArch.PtrSize+len(deltaBuf))) // length (excludes itself)
 		if Linkmode == LinkExternal {
-			adddwarfref(Ctxt, fs, framesec, 4)
+			adddwarfref(ctxt, fs, framesec, 4)
 		} else {
-			Adduint32(Ctxt, fs, 0) // CIE offset
+			Adduint32(ctxt, fs, 0) // CIE offset
 		}
-		Addaddr(Ctxt, fs, s)
-		addrput(fs, s.Size) // address range
-		Addbytes(Ctxt, fs, deltaBuf)
+		Addaddr(ctxt, fs, s)
+		adduintxx(ctxt, fs, uint64(s.Size), SysArch.PtrSize) // address range
+		Addbytes(fs, deltaBuf)
 	}
-	return fs
+	return syms
 }
 
 /*
@@ -1771,163 +1295,171 @@ const (
 	COMPUNITHEADERSIZE = 4 + 2 + 4 + 1
 )
 
-func writeinfo(prev *LSym) *LSym {
+func writeinfo(ctxt *Link, syms []*Symbol, funcs []*Symbol) []*Symbol {
 	if infosec == nil {
-		infosec = Linklookup(Ctxt, ".debug_info", 0)
+		infosec = ctxt.Syms.Lookup(".debug_info", 0)
 	}
 	infosec.R = infosec.R[:0]
 	infosec.Type = obj.SDWARFINFO
 	infosec.Attr |= AttrReachable
-	prev.Next, prev = infosec, infosec
+	syms = append(syms, infosec)
 
 	if arangessec == nil {
-		arangessec = Linklookup(Ctxt, ".dwarfaranges", 0)
+		arangessec = ctxt.Syms.Lookup(".dwarfaranges", 0)
 	}
 	arangessec.R = arangessec.R[:0]
 
-	for compunit := dwroot.child; compunit != nil; compunit = compunit.link {
-		s := compunit.sym
-		prev.Next, prev = s, s
+	var dwarfctxt dwarf.Context = dwctxt{ctxt}
+
+	for compunit := dwroot.Child; compunit != nil; compunit = compunit.Link {
+		s := dtolsym(compunit.Sym)
 
 		// Write .debug_info Compilation Unit Header (sec 7.5.1)
 		// Fields marked with (*) must be changed for 64-bit dwarf
 		// This must match COMPUNITHEADERSIZE above.
-		Adduint32(Ctxt, s, 0) // unit_length (*), will be filled in later.
-		Adduint16(Ctxt, s, 2) // dwarf version (appendix F)
+		Adduint32(ctxt, s, 0) // unit_length (*), will be filled in later.
+		Adduint16(ctxt, s, 2) // dwarf version (appendix F)
 
 		// debug_abbrev_offset (*)
-		adddwarfref(Ctxt, s, abbrevsym, 4)
+		adddwarfref(ctxt, s, abbrevsym, 4)
 
-		Adduint8(Ctxt, s, uint8(SysArch.PtrSize)) // address_size
+		Adduint8(ctxt, s, uint8(SysArch.PtrSize)) // address_size
 
-		prev = putdie(prev, compunit)
-		cusize := s.Size - 4 // exclude the length field.
-		for child := s.Next; child != nil; child = child.Next {
+		dwarf.Uleb128put(dwarfctxt, s, int64(compunit.Abbrev))
+		dwarf.PutAttrs(dwarfctxt, s, compunit.Abbrev, compunit.Attr)
+
+		cu := []*Symbol{s}
+		if funcs != nil {
+			cu = append(cu, funcs...)
+			funcs = nil
+		}
+		cu = putdies(ctxt, dwarfctxt, cu, compunit.Child)
+		var cusize int64
+		for _, child := range cu {
 			cusize += child.Size
 		}
-
-		setuint32(Ctxt, s, 0, uint32(cusize))
-		newattr(compunit, DW_AT_byte_size, DW_CLS_CONSTANT, cusize, 0)
+		cusize -= 4 // exclude the length field.
+		setuint32(ctxt, s, 0, uint32(cusize))
+		newattr(compunit, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, cusize, 0)
+		syms = append(syms, cu...)
 	}
-	return prev
+	return syms
 }
 
 /*
  *  Emit .debug_pubnames/_types.  _info must have been written before,
  *  because we need die->offs and infoo/infosize;
  */
-func ispubname(die *DWDie) bool {
-	switch die.abbrev {
-	case DW_ABRV_FUNCTION, DW_ABRV_VARIABLE:
-		a := getattr(die, DW_AT_external)
-		return a != nil && a.value != 0
+func ispubname(die *dwarf.DWDie) bool {
+	switch die.Abbrev {
+	case dwarf.DW_ABRV_FUNCTION, dwarf.DW_ABRV_VARIABLE:
+		a := getattr(die, dwarf.DW_AT_external)
+		return a != nil && a.Value != 0
 	}
 
 	return false
 }
 
-func ispubtype(die *DWDie) bool {
-	return die.abbrev >= DW_ABRV_NULLTYPE
+func ispubtype(die *dwarf.DWDie) bool {
+	return die.Abbrev >= dwarf.DW_ABRV_NULLTYPE
 }
 
-func writepub(sname string, ispub func(*DWDie) bool, prev *LSym) *LSym {
-	s := Linklookup(Ctxt, sname, 0)
+func writepub(ctxt *Link, sname string, ispub func(*dwarf.DWDie) bool, syms []*Symbol) []*Symbol {
+	s := ctxt.Syms.Lookup(sname, 0)
 	s.Type = obj.SDWARFSECT
-	prev.Next = s
+	syms = append(syms, s)
 
-	for compunit := dwroot.child; compunit != nil; compunit = compunit.link {
+	for compunit := dwroot.Child; compunit != nil; compunit = compunit.Link {
 		sectionstart := s.Size
-		culength := uint32(getattr(compunit, DW_AT_byte_size).value) + 4
+		culength := uint32(getattr(compunit, dwarf.DW_AT_byte_size).Value) + 4
 
 		// Write .debug_pubnames/types	Header (sec 6.1.1)
-		Adduint32(Ctxt, s, 0)                 // unit_length (*), will be filled in later.
-		Adduint16(Ctxt, s, 2)                 // dwarf version (appendix F)
-		adddwarfref(Ctxt, s, compunit.sym, 4) // debug_info_offset (of the Comp unit Header)
-		Adduint32(Ctxt, s, culength)          // debug_info_length
+		Adduint32(ctxt, s, 0)                          // unit_length (*), will be filled in later.
+		Adduint16(ctxt, s, 2)                          // dwarf version (appendix F)
+		adddwarfref(ctxt, s, dtolsym(compunit.Sym), 4) // debug_info_offset (of the Comp unit Header)
+		Adduint32(ctxt, s, culength)                   // debug_info_length
 
-		for die := compunit.child; die != nil; die = die.link {
+		for die := compunit.Child; die != nil; die = die.Link {
 			if !ispub(die) {
 				continue
 			}
-			dwa := getattr(die, DW_AT_name)
-			name := dwa.data.(string)
-			if die.sym == nil {
+			dwa := getattr(die, dwarf.DW_AT_name)
+			name := dwa.Data.(string)
+			if die.Sym == nil {
 				fmt.Println("Missing sym for ", name)
 			}
-			adddwarfref(Ctxt, s, die.sym, 4)
+			adddwarfref(ctxt, s, dtolsym(die.Sym), 4)
 			Addstring(s, name)
 		}
 
-		Adduint32(Ctxt, s, 0)
+		Adduint32(ctxt, s, 0)
 
-		setuint32(Ctxt, s, sectionstart, uint32(s.Size-sectionstart)-4) // exclude the length field.
+		setuint32(ctxt, s, sectionstart, uint32(s.Size-sectionstart)-4) // exclude the length field.
 	}
 
-	return s
+	return syms
 }
 
 /*
  *  emit .debug_aranges.  _info must have been written before,
- *  because we need die->offs of dw_globals.
+ *  because we need die->offs of dwarf.DW_globals.
  */
-func writearanges(prev *LSym) *LSym {
-	s := Linklookup(Ctxt, ".debug_aranges", 0)
+func writearanges(ctxt *Link, syms []*Symbol) []*Symbol {
+	s := ctxt.Syms.Lookup(".debug_aranges", 0)
 	s.Type = obj.SDWARFSECT
 	// The first tuple is aligned to a multiple of the size of a single tuple
 	// (twice the size of an address)
 	headersize := int(Rnd(4+2+4+1+1, int64(SysArch.PtrSize*2))) // don't count unit_length field itself
 
-	for compunit := dwroot.child; compunit != nil; compunit = compunit.link {
-		b := getattr(compunit, DW_AT_low_pc)
+	for compunit := dwroot.Child; compunit != nil; compunit = compunit.Link {
+		b := getattr(compunit, dwarf.DW_AT_low_pc)
 		if b == nil {
 			continue
 		}
-		e := getattr(compunit, DW_AT_high_pc)
+		e := getattr(compunit, dwarf.DW_AT_high_pc)
 		if e == nil {
 			continue
 		}
 
 		// Write .debug_aranges	 Header + entry	 (sec 6.1.2)
 		unitlength := uint32(headersize) + 4*uint32(SysArch.PtrSize) - 4
-		Adduint32(Ctxt, s, unitlength) // unit_length (*)
-		Adduint16(Ctxt, s, 2)          // dwarf version (appendix F)
+		Adduint32(ctxt, s, unitlength) // unit_length (*)
+		Adduint16(ctxt, s, 2)          // dwarf version (appendix F)
 
-		adddwarfref(Ctxt, s, compunit.sym, 4)
+		adddwarfref(ctxt, s, dtolsym(compunit.Sym), 4)
 
-		Adduint8(Ctxt, s, uint8(SysArch.PtrSize)) // address_size
-		Adduint8(Ctxt, s, 0)                      // segment_size
+		Adduint8(ctxt, s, uint8(SysArch.PtrSize)) // address_size
+		Adduint8(ctxt, s, 0)                      // segment_size
 		padding := headersize - (4 + 2 + 4 + 1 + 1)
 		for i := 0; i < padding; i++ {
-			Adduint8(Ctxt, s, 0)
+			Adduint8(ctxt, s, 0)
 		}
 
-		Addaddrplus(Ctxt, s, b.data.(*LSym), b.value-(b.data.(*LSym)).Value)
-		addrput(s, e.value-b.value)
-		addrput(s, 0)
-		addrput(s, 0)
+		Addaddrplus(ctxt, s, b.Data.(*Symbol), b.Value-(b.Data.(*Symbol)).Value)
+		adduintxx(ctxt, s, uint64(e.Value-b.Value), SysArch.PtrSize)
+		adduintxx(ctxt, s, 0, SysArch.PtrSize)
+		adduintxx(ctxt, s, 0, SysArch.PtrSize)
 	}
 	if s.Size > 0 {
-		prev.Next = s
-		prev = s
+		syms = append(syms, s)
 	}
-	return prev
+	return syms
 }
 
-func writegdbscript(prev *LSym) *LSym {
+func writegdbscript(ctxt *Link, syms []*Symbol) []*Symbol {
 
 	if gdbscript != "" {
-		s := Linklookup(Ctxt, ".debug_gdb_scripts", 0)
+		s := ctxt.Syms.Lookup(".debug_gdb_scripts", 0)
 		s.Type = obj.SDWARFSECT
-		prev.Next = s
-		prev = s
-		Adduint8(Ctxt, s, 1) // magic 1 byte?
+		syms = append(syms, s)
+		Adduint8(ctxt, s, 1) // magic 1 byte?
 		Addstring(s, gdbscript)
 	}
 
-	return prev
+	return syms
 }
 
-var prototypedies map[string]*DWDie
+var prototypedies map[string]*dwarf.DWDie
 
 /*
  * This is the main entry point for generating dwarf.  After emitting
@@ -1938,43 +1470,43 @@ var prototypedies map[string]*DWDie
  * passes.
  *
  */
-func dwarfgeneratedebugsyms() {
-	if Debug['w'] != 0 { // disable dwarf
+func dwarfgeneratedebugsyms(ctxt *Link) {
+	if *FlagW { // disable dwarf
 		return
 	}
-	if Debug['s'] != 0 && HEADTYPE != obj.Hdarwin {
+	if *FlagS && Headtype != obj.Hdarwin {
 		return
 	}
-	if HEADTYPE == obj.Hplan9 {
+	if Headtype == obj.Hplan9 {
 		return
 	}
 
 	if Linkmode == LinkExternal {
-		if !Iself && HEADTYPE != obj.Hdarwin {
+		if !Iself && Headtype != obj.Hdarwin {
 			return
 		}
 	}
 
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "%5.2f dwarf\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f dwarf\n", obj.Cputime())
 	}
 
-	// For diagnostic messages.
-	newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, int64(len("dwtypes")), "dwtypes")
+	// Forctxt.Diagnostic messages.
+	newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes")
 
 	// Some types that must exist to define other ones.
-	newdie(&dwtypes, DW_ABRV_NULLTYPE, "<unspecified>", 0)
+	newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "<unspecified>", 0)
 
-	newdie(&dwtypes, DW_ABRV_NULLTYPE, "void", 0)
-	newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0)
+	newdie(ctxt, &dwtypes, dwarf.DW_ABRV_NULLTYPE, "void", 0)
+	newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer", 0)
 
-	die := newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size
-	newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0)
-	newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, int64(SysArch.PtrSize), 0)
-	newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, obj.KindUintptr, 0)
+	die := newdie(ctxt, &dwtypes, dwarf.DW_ABRV_BASETYPE, "uintptr", 0) // needed for array size
+	newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0)
+	newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, int64(SysArch.PtrSize), 0)
+	newattr(die, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, obj.KindUintptr, 0)
 
 	// Prototypes needed for type synthesis.
-	prototypedies = map[string]*DWDie{
+	prototypedies = map[string]*dwarf.DWDie{
 		"type.runtime.stringStructDWARF": nil,
 		"type.runtime.slice":             nil,
 		"type.runtime.hmap":              nil,
@@ -1985,47 +1517,46 @@ func dwarfgeneratedebugsyms() {
 	}
 
 	// Needed by the prettyprinter code for interface inspection.
-	defgotype(lookup_or_diag("type.runtime._type"))
+	defgotype(ctxt, lookupOrDiag(ctxt, "type.runtime._type"))
 
-	defgotype(lookup_or_diag("type.runtime.interfacetype"))
-	defgotype(lookup_or_diag("type.runtime.itab"))
+	defgotype(ctxt, lookupOrDiag(ctxt, "type.runtime.interfacetype"))
+	defgotype(ctxt, lookupOrDiag(ctxt, "type.runtime.itab"))
 
-	genasmsym(defdwsymb)
+	genasmsym(ctxt, defdwsymb)
 
-	dwarfp = writeabbrev()
-	last := dwarfp
-	last = writelines(last)
-	last = writeframes(last)
+	syms := writeabbrev(ctxt, nil)
+	syms, funcs := writelines(ctxt, syms)
+	syms = writeframes(ctxt, syms)
 
-	synthesizestringtypes(dwtypes.child)
-	synthesizeslicetypes(dwtypes.child)
-	synthesizemaptypes(dwtypes.child)
-	synthesizechantypes(dwtypes.child)
+	synthesizestringtypes(ctxt, dwtypes.Child)
+	synthesizeslicetypes(ctxt, dwtypes.Child)
+	synthesizemaptypes(ctxt, dwtypes.Child)
+	synthesizechantypes(ctxt, dwtypes.Child)
 
-	reversetree(&dwroot.child)
-	reversetree(&dwtypes.child)
-	reversetree(&dwglobals.child)
+	reversetree(&dwroot.Child)
+	reversetree(&dwtypes.Child)
+	reversetree(&dwglobals.Child)
 
 	movetomodule(&dwtypes)
 	movetomodule(&dwglobals)
 
 	// Need to reorder symbols so SDWARFINFO is after all SDWARFSECT
 	// (but we need to generate dies before writepub)
-	writeinfo(last)
-	infosyms := last.Next
-
-	last = writepub(".debug_pubnames", ispubname, last)
-	last = writepub(".debug_pubtypes", ispubtype, last)
-	last = writearanges(last)
-	last = writegdbscript(last)
-	last.Next = infosyms
+	infosyms := writeinfo(ctxt, nil, funcs)
+
+	syms = writepub(ctxt, ".debug_pubnames", ispubname, syms)
+	syms = writepub(ctxt, ".debug_pubtypes", ispubtype, syms)
+	syms = writearanges(ctxt, syms)
+	syms = writegdbscript(ctxt, syms)
+	syms = append(syms, infosyms...)
+	dwarfp = syms
 }
 
 /*
  *  Elf.
  */
-func dwarfaddshstrings(shstrtab *LSym) {
-	if Debug['w'] != 0 { // disable dwarf
+func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
+	if *FlagW { // disable dwarf
 		return
 	}
 
@@ -2049,36 +1580,35 @@ func dwarfaddshstrings(shstrtab *LSym) {
 
 // Add section symbols for DWARF debug info.  This is called before
 // dwarfaddelfheaders.
-func dwarfaddelfsectionsyms() {
-	if Debug['w'] != 0 { // disable dwarf
+func dwarfaddelfsectionsyms(ctxt *Link) {
+	if *FlagW { // disable dwarf
 		return
 	}
 	if Linkmode != LinkExternal {
 		return
 	}
-	sym := Linklookup(Ctxt, ".debug_info", 0)
+	sym := ctxt.Syms.Lookup(".debug_info", 0)
 	putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
-	sym = Linklookup(Ctxt, ".debug_abbrev", 0)
+	sym = ctxt.Syms.Lookup(".debug_abbrev", 0)
 	putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
-	sym = Linklookup(Ctxt, ".debug_line", 0)
+	sym = ctxt.Syms.Lookup(".debug_line", 0)
 	putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
-	sym = Linklookup(Ctxt, ".debug_frame", 0)
+	sym = ctxt.Syms.Lookup(".debug_frame", 0)
 	putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
 }
 
 /*
  * Windows PE
  */
-func dwarfaddpeheaders() {
-	if Debug['w'] != 0 { // disable dwarf
+func dwarfaddpeheaders(ctxt *Link) {
+	if *FlagW { // disable dwarf
 		return
 	}
 	for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
-		h := newPEDWARFSection(sect.Name, int64(sect.Length))
+		h := newPEDWARFSection(ctxt, sect.Name, int64(sect.Length))
 		fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff
 		if uint64(h.PointerToRawData) != fileoff {
-			Diag("%s.PointerToRawData = %#x, want %#x", sect.Name, h.PointerToRawData, fileoff)
-			errorexit()
+			Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.PointerToRawData, fileoff)
 		}
 	}
 }
diff --git a/src/cmd/link/internal/ld/dwarf_defs.go b/src/cmd/link/internal/ld/dwarf_defs.go
deleted file mode 100644
index c52879c..0000000
--- a/src/cmd/link/internal/ld/dwarf_defs.go
+++ /dev/null
@@ -1,516 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package ld
-
-// TODO/NICETOHAVE:
-//   - eliminate DW_CLS_ if not used
-//   - package info in compilation units
-//   - assign global variables and types to their packages
-//   - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg
-//     ptype struct '[]uint8' and qualifiers need to be quoted away
-//   - lexical scoping is lost, so gdb gets confused as to which 'obj.i' you mean.
-//   - file:line info for variables
-//   - make strings a typedef so prettyprinters can see the underlying string type
-//
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
- * Emit debug_abbrevs, debug_info and debug_line sections to current
- * offset in cout.
- */
-
-/*
- * Add the dwarf section names to the ELF
- * s[ection]h[eader]str[ing]tab.  Prerequisite for
- * dwarfaddelfheaders().
- */
-
-/*
- * Add section headers pointing to the sections emitted in
- * dwarfemitdebugsections.
- */
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Cut, pasted, tr-and-awk'ed from tables in
-// http://dwarfstd.org/doc/Dwarf3.pdf
-
-// Table 18
-const (
-	DW_TAG_array_type               = 0x01
-	DW_TAG_class_type               = 0x02
-	DW_TAG_entry_point              = 0x03
-	DW_TAG_enumeration_type         = 0x04
-	DW_TAG_formal_parameter         = 0x05
-	DW_TAG_imported_declaration     = 0x08
-	DW_TAG_label                    = 0x0a
-	DW_TAG_lexical_block            = 0x0b
-	DW_TAG_member                   = 0x0d
-	DW_TAG_pointer_type             = 0x0f
-	DW_TAG_reference_type           = 0x10
-	DW_TAG_compile_unit             = 0x11
-	DW_TAG_string_type              = 0x12
-	DW_TAG_structure_type           = 0x13
-	DW_TAG_subroutine_type          = 0x15
-	DW_TAG_typedef                  = 0x16
-	DW_TAG_union_type               = 0x17
-	DW_TAG_unspecified_parameters   = 0x18
-	DW_TAG_variant                  = 0x19
-	DW_TAG_common_block             = 0x1a
-	DW_TAG_common_inclusion         = 0x1b
-	DW_TAG_inheritance              = 0x1c
-	DW_TAG_inlined_subroutine       = 0x1d
-	DW_TAG_module                   = 0x1e
-	DW_TAG_ptr_to_member_type       = 0x1f
-	DW_TAG_set_type                 = 0x20
-	DW_TAG_subrange_type            = 0x21
-	DW_TAG_with_stmt                = 0x22
-	DW_TAG_access_declaration       = 0x23
-	DW_TAG_base_type                = 0x24
-	DW_TAG_catch_block              = 0x25
-	DW_TAG_const_type               = 0x26
-	DW_TAG_constant                 = 0x27
-	DW_TAG_enumerator               = 0x28
-	DW_TAG_file_type                = 0x29
-	DW_TAG_friend                   = 0x2a
-	DW_TAG_namelist                 = 0x2b
-	DW_TAG_namelist_item            = 0x2c
-	DW_TAG_packed_type              = 0x2d
-	DW_TAG_subprogram               = 0x2e
-	DW_TAG_template_type_parameter  = 0x2f
-	DW_TAG_template_value_parameter = 0x30
-	DW_TAG_thrown_type              = 0x31
-	DW_TAG_try_block                = 0x32
-	DW_TAG_variant_part             = 0x33
-	DW_TAG_variable                 = 0x34
-	DW_TAG_volatile_type            = 0x35
-	// Dwarf3
-	DW_TAG_dwarf_procedure  = 0x36
-	DW_TAG_restrict_type    = 0x37
-	DW_TAG_interface_type   = 0x38
-	DW_TAG_namespace        = 0x39
-	DW_TAG_imported_module  = 0x3a
-	DW_TAG_unspecified_type = 0x3b
-	DW_TAG_partial_unit     = 0x3c
-	DW_TAG_imported_unit    = 0x3d
-	DW_TAG_condition        = 0x3f
-	DW_TAG_shared_type      = 0x40
-	// Dwarf4
-	DW_TAG_type_unit             = 0x41
-	DW_TAG_rvalue_reference_type = 0x42
-	DW_TAG_template_alias        = 0x43
-
-	// User defined
-	DW_TAG_lo_user = 0x4080
-	DW_TAG_hi_user = 0xffff
-)
-
-// Table 19
-const (
-	DW_CHILDREN_no  = 0x00
-	DW_CHILDREN_yes = 0x01
-)
-
-// Not from the spec, but logically belongs here
-const (
-	DW_CLS_ADDRESS = 0x01 + iota
-	DW_CLS_BLOCK
-	DW_CLS_CONSTANT
-	DW_CLS_FLAG
-	DW_CLS_PTR // lineptr, loclistptr, macptr, rangelistptr
-	DW_CLS_REFERENCE
-	DW_CLS_ADDRLOC
-	DW_CLS_STRING
-)
-
-// Table 20
-const (
-	DW_AT_sibling              = 0x01 // reference
-	DW_AT_location             = 0x02 // block, loclistptr
-	DW_AT_name                 = 0x03 // string
-	DW_AT_ordering             = 0x09 // constant
-	DW_AT_byte_size            = 0x0b // block, constant, reference
-	DW_AT_bit_offset           = 0x0c // block, constant, reference
-	DW_AT_bit_size             = 0x0d // block, constant, reference
-	DW_AT_stmt_list            = 0x10 // lineptr
-	DW_AT_low_pc               = 0x11 // address
-	DW_AT_high_pc              = 0x12 // address
-	DW_AT_language             = 0x13 // constant
-	DW_AT_discr                = 0x15 // reference
-	DW_AT_discr_value          = 0x16 // constant
-	DW_AT_visibility           = 0x17 // constant
-	DW_AT_import               = 0x18 // reference
-	DW_AT_string_length        = 0x19 // block, loclistptr
-	DW_AT_common_reference     = 0x1a // reference
-	DW_AT_comp_dir             = 0x1b // string
-	DW_AT_const_value          = 0x1c // block, constant, string
-	DW_AT_containing_type      = 0x1d // reference
-	DW_AT_default_value        = 0x1e // reference
-	DW_AT_inline               = 0x20 // constant
-	DW_AT_is_optional          = 0x21 // flag
-	DW_AT_lower_bound          = 0x22 // block, constant, reference
-	DW_AT_producer             = 0x25 // string
-	DW_AT_prototyped           = 0x27 // flag
-	DW_AT_return_addr          = 0x2a // block, loclistptr
-	DW_AT_start_scope          = 0x2c // constant
-	DW_AT_bit_stride           = 0x2e // constant
-	DW_AT_upper_bound          = 0x2f // block, constant, reference
-	DW_AT_abstract_origin      = 0x31 // reference
-	DW_AT_accessibility        = 0x32 // constant
-	DW_AT_address_class        = 0x33 // constant
-	DW_AT_artificial           = 0x34 // flag
-	DW_AT_base_types           = 0x35 // reference
-	DW_AT_calling_convention   = 0x36 // constant
-	DW_AT_count                = 0x37 // block, constant, reference
-	DW_AT_data_member_location = 0x38 // block, constant, loclistptr
-	DW_AT_decl_column          = 0x39 // constant
-	DW_AT_decl_file            = 0x3a // constant
-	DW_AT_decl_line            = 0x3b // constant
-	DW_AT_declaration          = 0x3c // flag
-	DW_AT_discr_list           = 0x3d // block
-	DW_AT_encoding             = 0x3e // constant
-	DW_AT_external             = 0x3f // flag
-	DW_AT_frame_base           = 0x40 // block, loclistptr
-	DW_AT_friend               = 0x41 // reference
-	DW_AT_identifier_case      = 0x42 // constant
-	DW_AT_macro_info           = 0x43 // macptr
-	DW_AT_namelist_item        = 0x44 // block
-	DW_AT_priority             = 0x45 // reference
-	DW_AT_segment              = 0x46 // block, loclistptr
-	DW_AT_specification        = 0x47 // reference
-	DW_AT_static_link          = 0x48 // block, loclistptr
-	DW_AT_type                 = 0x49 // reference
-	DW_AT_use_location         = 0x4a // block, loclistptr
-	DW_AT_variable_parameter   = 0x4b // flag
-	DW_AT_virtuality           = 0x4c // constant
-	DW_AT_vtable_elem_location = 0x4d // block, loclistptr
-	// Dwarf3
-	DW_AT_allocated      = 0x4e // block, constant, reference
-	DW_AT_associated     = 0x4f // block, constant, reference
-	DW_AT_data_location  = 0x50 // block
-	DW_AT_byte_stride    = 0x51 // block, constant, reference
-	DW_AT_entry_pc       = 0x52 // address
-	DW_AT_use_UTF8       = 0x53 // flag
-	DW_AT_extension      = 0x54 // reference
-	DW_AT_ranges         = 0x55 // rangelistptr
-	DW_AT_trampoline     = 0x56 // address, flag, reference, string
-	DW_AT_call_column    = 0x57 // constant
-	DW_AT_call_file      = 0x58 // constant
-	DW_AT_call_line      = 0x59 // constant
-	DW_AT_description    = 0x5a // string
-	DW_AT_binary_scale   = 0x5b // constant
-	DW_AT_decimal_scale  = 0x5c // constant
-	DW_AT_small          = 0x5d // reference
-	DW_AT_decimal_sign   = 0x5e // constant
-	DW_AT_digit_count    = 0x5f // constant
-	DW_AT_picture_string = 0x60 // string
-	DW_AT_mutable        = 0x61 // flag
-	DW_AT_threads_scaled = 0x62 // flag
-	DW_AT_explicit       = 0x63 // flag
-	DW_AT_object_pointer = 0x64 // reference
-	DW_AT_endianity      = 0x65 // constant
-	DW_AT_elemental      = 0x66 // flag
-	DW_AT_pure           = 0x67 // flag
-	DW_AT_recursive      = 0x68 // flag
-
-	DW_AT_lo_user = 0x2000 // ---
-	DW_AT_hi_user = 0x3fff // ---
-)
-
-// Table 21
-const (
-	DW_FORM_addr      = 0x01 // address
-	DW_FORM_block2    = 0x03 // block
-	DW_FORM_block4    = 0x04 // block
-	DW_FORM_data2     = 0x05 // constant
-	DW_FORM_data4     = 0x06 // constant, lineptr, loclistptr, macptr, rangelistptr
-	DW_FORM_data8     = 0x07 // constant, lineptr, loclistptr, macptr, rangelistptr
-	DW_FORM_string    = 0x08 // string
-	DW_FORM_block     = 0x09 // block
-	DW_FORM_block1    = 0x0a // block
-	DW_FORM_data1     = 0x0b // constant
-	DW_FORM_flag      = 0x0c // flag
-	DW_FORM_sdata     = 0x0d // constant
-	DW_FORM_strp      = 0x0e // string
-	DW_FORM_udata     = 0x0f // constant
-	DW_FORM_ref_addr  = 0x10 // reference
-	DW_FORM_ref1      = 0x11 // reference
-	DW_FORM_ref2      = 0x12 // reference
-	DW_FORM_ref4      = 0x13 // reference
-	DW_FORM_ref8      = 0x14 // reference
-	DW_FORM_ref_udata = 0x15 // reference
-	DW_FORM_indirect  = 0x16 // (see Section 7.5.3)
-)
-
-// Table 24 (#operands, notes)
-const (
-	DW_OP_addr                = 0x03 // 1 constant address (size target specific)
-	DW_OP_deref               = 0x06 // 0
-	DW_OP_const1u             = 0x08 // 1 1-byte constant
-	DW_OP_const1s             = 0x09 // 1 1-byte constant
-	DW_OP_const2u             = 0x0a // 1 2-byte constant
-	DW_OP_const2s             = 0x0b // 1 2-byte constant
-	DW_OP_const4u             = 0x0c // 1 4-byte constant
-	DW_OP_const4s             = 0x0d // 1 4-byte constant
-	DW_OP_const8u             = 0x0e // 1 8-byte constant
-	DW_OP_const8s             = 0x0f // 1 8-byte constant
-	DW_OP_constu              = 0x10 // 1 ULEB128 constant
-	DW_OP_consts              = 0x11 // 1 SLEB128 constant
-	DW_OP_dup                 = 0x12 // 0
-	DW_OP_drop                = 0x13 // 0
-	DW_OP_over                = 0x14 // 0
-	DW_OP_pick                = 0x15 // 1 1-byte stack index
-	DW_OP_swap                = 0x16 // 0
-	DW_OP_rot                 = 0x17 // 0
-	DW_OP_xderef              = 0x18 // 0
-	DW_OP_abs                 = 0x19 // 0
-	DW_OP_and                 = 0x1a // 0
-	DW_OP_div                 = 0x1b // 0
-	DW_OP_minus               = 0x1c // 0
-	DW_OP_mod                 = 0x1d // 0
-	DW_OP_mul                 = 0x1e // 0
-	DW_OP_neg                 = 0x1f // 0
-	DW_OP_not                 = 0x20 // 0
-	DW_OP_or                  = 0x21 // 0
-	DW_OP_plus                = 0x22 // 0
-	DW_OP_plus_uconst         = 0x23 // 1 ULEB128 addend
-	DW_OP_shl                 = 0x24 // 0
-	DW_OP_shr                 = 0x25 // 0
-	DW_OP_shra                = 0x26 // 0
-	DW_OP_xor                 = 0x27 // 0
-	DW_OP_skip                = 0x2f // 1 signed 2-byte constant
-	DW_OP_bra                 = 0x28 // 1 signed 2-byte constant
-	DW_OP_eq                  = 0x29 // 0
-	DW_OP_ge                  = 0x2a // 0
-	DW_OP_gt                  = 0x2b // 0
-	DW_OP_le                  = 0x2c // 0
-	DW_OP_lt                  = 0x2d // 0
-	DW_OP_ne                  = 0x2e // 0
-	DW_OP_lit0                = 0x30 // 0 ...
-	DW_OP_lit31               = 0x4f // 0 literals 0..31 = (DW_OP_lit0 + literal)
-	DW_OP_reg0                = 0x50 // 0 ..
-	DW_OP_reg31               = 0x6f // 0 reg 0..31 = (DW_OP_reg0 + regnum)
-	DW_OP_breg0               = 0x70 // 1 ...
-	DW_OP_breg31              = 0x8f // 1 SLEB128 offset base register 0..31 = (DW_OP_breg0 + regnum)
-	DW_OP_regx                = 0x90 // 1 ULEB128 register
-	DW_OP_fbreg               = 0x91 // 1 SLEB128 offset
-	DW_OP_bregx               = 0x92 // 2 ULEB128 register followed by SLEB128 offset
-	DW_OP_piece               = 0x93 // 1 ULEB128 size of piece addressed
-	DW_OP_deref_size          = 0x94 // 1 1-byte size of data retrieved
-	DW_OP_xderef_size         = 0x95 // 1 1-byte size of data retrieved
-	DW_OP_nop                 = 0x96 // 0
-	DW_OP_push_object_address = 0x97 // 0
-	DW_OP_call2               = 0x98 // 1 2-byte offset of DIE
-	DW_OP_call4               = 0x99 // 1 4-byte offset of DIE
-	DW_OP_call_ref            = 0x9a // 1 4- or 8-byte offset of DIE
-	DW_OP_form_tls_address    = 0x9b // 0
-	DW_OP_call_frame_cfa      = 0x9c // 0
-	DW_OP_bit_piece           = 0x9d // 2
-	DW_OP_lo_user             = 0xe0
-	DW_OP_hi_user             = 0xff
-)
-
-// Table 25
-const (
-	DW_ATE_address         = 0x01
-	DW_ATE_boolean         = 0x02
-	DW_ATE_complex_float   = 0x03
-	DW_ATE_float           = 0x04
-	DW_ATE_signed          = 0x05
-	DW_ATE_signed_char     = 0x06
-	DW_ATE_unsigned        = 0x07
-	DW_ATE_unsigned_char   = 0x08
-	DW_ATE_imaginary_float = 0x09
-	DW_ATE_packed_decimal  = 0x0a
-	DW_ATE_numeric_string  = 0x0b
-	DW_ATE_edited          = 0x0c
-	DW_ATE_signed_fixed    = 0x0d
-	DW_ATE_unsigned_fixed  = 0x0e
-	DW_ATE_decimal_float   = 0x0f
-	DW_ATE_lo_user         = 0x80
-	DW_ATE_hi_user         = 0xff
-)
-
-// Table 26
-const (
-	DW_DS_unsigned           = 0x01
-	DW_DS_leading_overpunch  = 0x02
-	DW_DS_trailing_overpunch = 0x03
-	DW_DS_leading_separate   = 0x04
-	DW_DS_trailing_separate  = 0x05
-)
-
-// Table 27
-const (
-	DW_END_default = 0x00
-	DW_END_big     = 0x01
-	DW_END_little  = 0x02
-	DW_END_lo_user = 0x40
-	DW_END_hi_user = 0xff
-)
-
-// Table 28
-const (
-	DW_ACCESS_public    = 0x01
-	DW_ACCESS_protected = 0x02
-	DW_ACCESS_private   = 0x03
-)
-
-// Table 29
-const (
-	DW_VIS_local     = 0x01
-	DW_VIS_exported  = 0x02
-	DW_VIS_qualified = 0x03
-)
-
-// Table 30
-const (
-	DW_VIRTUALITY_none         = 0x00
-	DW_VIRTUALITY_virtual      = 0x01
-	DW_VIRTUALITY_pure_virtual = 0x02
-)
-
-// Table 31
-const (
-	DW_LANG_C89         = 0x0001
-	DW_LANG_C           = 0x0002
-	DW_LANG_Ada83       = 0x0003
-	DW_LANG_C_plus_plus = 0x0004
-	DW_LANG_Cobol74     = 0x0005
-	DW_LANG_Cobol85     = 0x0006
-	DW_LANG_Fortran77   = 0x0007
-	DW_LANG_Fortran90   = 0x0008
-	DW_LANG_Pascal83    = 0x0009
-	DW_LANG_Modula2     = 0x000a
-	// Dwarf3
-	DW_LANG_Java           = 0x000b
-	DW_LANG_C99            = 0x000c
-	DW_LANG_Ada95          = 0x000d
-	DW_LANG_Fortran95      = 0x000e
-	DW_LANG_PLI            = 0x000f
-	DW_LANG_ObjC           = 0x0010
-	DW_LANG_ObjC_plus_plus = 0x0011
-	DW_LANG_UPC            = 0x0012
-	DW_LANG_D              = 0x0013
-	// Dwarf4
-	DW_LANG_Python = 0x0014
-	// Dwarf5
-	DW_LANG_Go = 0x0016
-
-	DW_LANG_lo_user = 0x8000
-	DW_LANG_hi_user = 0xffff
-)
-
-// Table 32
-const (
-	DW_ID_case_sensitive   = 0x00
-	DW_ID_up_case          = 0x01
-	DW_ID_down_case        = 0x02
-	DW_ID_case_insensitive = 0x03
-)
-
-// Table 33
-const (
-	DW_CC_normal  = 0x01
-	DW_CC_program = 0x02
-	DW_CC_nocall  = 0x03
-	DW_CC_lo_user = 0x40
-	DW_CC_hi_user = 0xff
-)
-
-// Table 34
-const (
-	DW_INL_not_inlined          = 0x00
-	DW_INL_inlined              = 0x01
-	DW_INL_declared_not_inlined = 0x02
-	DW_INL_declared_inlined     = 0x03
-)
-
-// Table 35
-const (
-	DW_ORD_row_major = 0x00
-	DW_ORD_col_major = 0x01
-)
-
-// Table 36
-const (
-	DW_DSC_label = 0x00
-	DW_DSC_range = 0x01
-)
-
-// Table 37
-const (
-	DW_LNS_copy             = 0x01
-	DW_LNS_advance_pc       = 0x02
-	DW_LNS_advance_line     = 0x03
-	DW_LNS_set_file         = 0x04
-	DW_LNS_set_column       = 0x05
-	DW_LNS_negate_stmt      = 0x06
-	DW_LNS_set_basic_block  = 0x07
-	DW_LNS_const_add_pc     = 0x08
-	DW_LNS_fixed_advance_pc = 0x09
-	// Dwarf3
-	DW_LNS_set_prologue_end   = 0x0a
-	DW_LNS_set_epilogue_begin = 0x0b
-	DW_LNS_set_isa            = 0x0c
-)
-
-// Table 38
-const (
-	DW_LNE_end_sequence = 0x01
-	DW_LNE_set_address  = 0x02
-	DW_LNE_define_file  = 0x03
-	DW_LNE_lo_user      = 0x80
-	DW_LNE_hi_user      = 0xff
-)
-
-// Table 39
-const (
-	DW_MACINFO_define     = 0x01
-	DW_MACINFO_undef      = 0x02
-	DW_MACINFO_start_file = 0x03
-	DW_MACINFO_end_file   = 0x04
-	DW_MACINFO_vendor_ext = 0xff
-)
-
-// Table 40.
-const (
-	// operand,...
-	DW_CFA_nop              = 0x00
-	DW_CFA_set_loc          = 0x01 // address
-	DW_CFA_advance_loc1     = 0x02 // 1-byte delta
-	DW_CFA_advance_loc2     = 0x03 // 2-byte delta
-	DW_CFA_advance_loc4     = 0x04 // 4-byte delta
-	DW_CFA_offset_extended  = 0x05 // ULEB128 register, ULEB128 offset
-	DW_CFA_restore_extended = 0x06 // ULEB128 register
-	DW_CFA_undefined        = 0x07 // ULEB128 register
-	DW_CFA_same_value       = 0x08 // ULEB128 register
-	DW_CFA_register         = 0x09 // ULEB128 register, ULEB128 register
-	DW_CFA_remember_state   = 0x0a
-	DW_CFA_restore_state    = 0x0b
-
-	DW_CFA_def_cfa            = 0x0c // ULEB128 register, ULEB128 offset
-	DW_CFA_def_cfa_register   = 0x0d // ULEB128 register
-	DW_CFA_def_cfa_offset     = 0x0e // ULEB128 offset
-	DW_CFA_def_cfa_expression = 0x0f // BLOCK
-	DW_CFA_expression         = 0x10 // ULEB128 register, BLOCK
-	DW_CFA_offset_extended_sf = 0x11 // ULEB128 register, SLEB128 offset
-	DW_CFA_def_cfa_sf         = 0x12 // ULEB128 register, SLEB128 offset
-	DW_CFA_def_cfa_offset_sf  = 0x13 // SLEB128 offset
-	DW_CFA_val_offset         = 0x14 // ULEB128, ULEB128
-	DW_CFA_val_offset_sf      = 0x15 // ULEB128, SLEB128
-	DW_CFA_val_expression     = 0x16 // ULEB128, BLOCK
-
-	DW_CFA_lo_user = 0x1c
-	DW_CFA_hi_user = 0x3f
-
-	// Opcodes that take an addend operand.
-	DW_CFA_advance_loc = 0x1 << 6 // +delta
-	DW_CFA_offset      = 0x2 << 6 // +register (ULEB128 offset)
-	DW_CFA_restore     = 0x3 << 6 // +register
-)
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index 39d3609..b4d5aae 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -66,10 +66,10 @@ import (
  * padded to a word boundary.  The values of n_namesz and n_descsz do
  * not include the padding.
  */
-type Elf_Note struct {
-	n_namesz uint32
-	n_descsz uint32
-	n_type   uint32
+type elfNote struct {
+	nNamesz uint32
+	nDescsz uint32
+	nType   uint32
 }
 
 const (
@@ -236,7 +236,9 @@ const (
 	PT_LOPROC            = 0x70000000
 	PT_HIPROC            = 0x7fffffff
 	PT_GNU_STACK         = 0x6474e551
+	PT_GNU_RELRO         = 0x6474e552
 	PT_PAX_FLAGS         = 0x65041580
+	PT_SUNWSTACK         = 0x6ffffffb
 	PF_X                 = 0x1
 	PF_W                 = 0x2
 	PF_R                 = 0x4
@@ -806,7 +808,7 @@ type ElfShdr struct {
 	addralign uint64
 	entsize   uint64
 	shnum     int
-	secsym    *LSym
+	secsym    *Symbol
 }
 
 /*
@@ -878,7 +880,7 @@ const (
  * written in the 32-bit format on the 32-bit machines.
  */
 const (
-	NSECT = 48
+	NSECT = 400
 )
 
 var (
@@ -913,7 +915,7 @@ var buildinfo []byte
  Initialize the global variable that describes the ELF header. It will be updated as
  we write section and prog headers.
 */
-func Elfinit() {
+func Elfinit(ctxt *Link) {
 	Iself = true
 
 	if SysArch.InFamily(sys.AMD64, sys.ARM64, sys.MIPS64, sys.PPC64, sys.S390X) {
@@ -925,7 +927,7 @@ func Elfinit() {
 	switch SysArch.Family {
 	// 64-bit architectures
 	case sys.PPC64, sys.S390X:
-		if Ctxt.Arch.ByteOrder == binary.BigEndian {
+		if ctxt.Arch.ByteOrder == binary.BigEndian {
 			ehdr.flags = 1 /* Version 1 ABI */
 		} else {
 			ehdr.flags = 2 /* Version 2 ABI */
@@ -943,20 +945,23 @@ func Elfinit() {
 		ehdr.phentsize = ELF64PHDRSIZE /* Must be ELF64PHDRSIZE */
 		ehdr.shentsize = ELF64SHDRSIZE /* Must be ELF64SHDRSIZE */
 
-	// we use EABI on both linux/arm and freebsd/arm.
 	// 32-bit architectures
-	case sys.ARM:
-		// we use EABI on both linux/arm and freebsd/arm.
-		if HEADTYPE == obj.Hlinux || HEADTYPE == obj.Hfreebsd {
-			// We set a value here that makes no indication of which
-			// float ABI the object uses, because this is information
-			// used by the dynamic linker to compare executables and
-			// shared libraries -- so it only matters for cgo calls, and
-			// the information properly comes from the object files
-			// produced by the host C compiler. parseArmAttributes in
-			// ldelf.go reads that information and updates this field as
-			// appropriate.
-			ehdr.flags = 0x5000002 // has entry point, Version5 EABI
+	case sys.ARM, sys.MIPS:
+		if SysArch.Family == sys.ARM {
+			// we use EABI on linux/arm, freebsd/arm, netbsd/arm.
+			if Headtype == obj.Hlinux || Headtype == obj.Hfreebsd || Headtype == obj.Hnetbsd {
+				// We set a value here that makes no indication of which
+				// float ABI the object uses, because this is information
+				// used by the dynamic linker to compare executables and
+				// shared libraries -- so it only matters for cgo calls, and
+				// the information properly comes from the object files
+				// produced by the host C compiler. parseArmAttributes in
+				// ldelf.go reads that information and updates this field as
+				// appropriate.
+				ehdr.flags = 0x5000002 // has entry point, Version5 EABI
+			}
+		} else if SysArch.Family == sys.MIPS {
+			ehdr.flags = 0x50000000 /* MIPS 32 */
 		}
 		fallthrough
 	default:
@@ -1054,13 +1059,13 @@ func elfwriteshdrs() uint32 {
 	return uint32(ehdr.shnum) * ELF32SHDRSIZE
 }
 
-func elfsetstring(s string, off int) {
+func elfsetstring(s *Symbol, str string, off int) {
 	if nelfstr >= len(elfstr) {
-		Diag("too many elf strings")
+		Errorf(s, "too many elf strings")
 		errorexit()
 	}
 
-	elfstr[nelfstr].s = s
+	elfstr[nelfstr].s = str
 	elfstr[nelfstr].off = off
 	nelfstr++
 }
@@ -1082,7 +1087,7 @@ func elfwritephdrs() uint32 {
 func newElfPhdr() *ElfPhdr {
 	e := new(ElfPhdr)
 	if ehdr.phnum >= NSECT {
-		Diag("too many phdrs")
+		Errorf(nil, "too many phdrs")
 	} else {
 		phdr[ehdr.phnum] = e
 		ehdr.phnum++
@@ -1100,7 +1105,7 @@ func newElfShdr(name int64) *ElfShdr {
 	e.name = uint32(name)
 	e.shnum = int(ehdr.shnum)
 	if ehdr.shnum >= NSECT {
-		Diag("too many shdrs")
+		Errorf(nil, "too many shdrs")
 	} else {
 		shdr[ehdr.shnum] = e
 		ehdr.shnum++
@@ -1173,36 +1178,36 @@ func elfhash(name string) uint32 {
 	return h
 }
 
-func Elfwritedynent(s *LSym, tag int, val uint64) {
+func Elfwritedynent(ctxt *Link, s *Symbol, tag int, val uint64) {
 	if elf64 {
-		Adduint64(Ctxt, s, uint64(tag))
-		Adduint64(Ctxt, s, val)
+		Adduint64(ctxt, s, uint64(tag))
+		Adduint64(ctxt, s, val)
 	} else {
-		Adduint32(Ctxt, s, uint32(tag))
-		Adduint32(Ctxt, s, uint32(val))
+		Adduint32(ctxt, s, uint32(tag))
+		Adduint32(ctxt, s, uint32(val))
 	}
 }
 
-func elfwritedynentsym(s *LSym, tag int, t *LSym) {
-	Elfwritedynentsymplus(s, tag, t, 0)
+func elfwritedynentsym(ctxt *Link, s *Symbol, tag int, t *Symbol) {
+	Elfwritedynentsymplus(ctxt, s, tag, t, 0)
 }
 
-func Elfwritedynentsymplus(s *LSym, tag int, t *LSym, add int64) {
+func Elfwritedynentsymplus(ctxt *Link, s *Symbol, tag int, t *Symbol, add int64) {
 	if elf64 {
-		Adduint64(Ctxt, s, uint64(tag))
+		Adduint64(ctxt, s, uint64(tag))
 	} else {
-		Adduint32(Ctxt, s, uint32(tag))
+		Adduint32(ctxt, s, uint32(tag))
 	}
-	Addaddrplus(Ctxt, s, t, add)
+	Addaddrplus(ctxt, s, t, add)
 }
 
-func elfwritedynentsymsize(s *LSym, tag int, t *LSym) {
+func elfwritedynentsymsize(ctxt *Link, s *Symbol, tag int, t *Symbol) {
 	if elf64 {
-		Adduint64(Ctxt, s, uint64(tag))
+		Adduint64(ctxt, s, uint64(tag))
 	} else {
-		Adduint32(Ctxt, s, uint32(tag))
+		Adduint32(ctxt, s, uint32(tag))
 	}
-	addsize(Ctxt, s, t)
+	addsize(ctxt, s, t)
 }
 
 func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int {
@@ -1355,7 +1360,7 @@ func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int {
 }
 
 func elfgobuildid(sh *ElfShdr, startva uint64, resoff uint64) int {
-	n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(buildid)), 4))
+	n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(*flagBuildid)), 4))
 	return elfnote(sh, startva, resoff, n, true)
 }
 
@@ -1374,15 +1379,15 @@ func elfwritebuildinfo() int {
 }
 
 func elfwritegobuildid() int {
-	sh := elfwritenotehdr(".note.go.buildid", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(buildid)), ELF_NOTE_GOBUILDID_TAG)
+	sh := elfwritenotehdr(".note.go.buildid", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(*flagBuildid)), ELF_NOTE_GOBUILDID_TAG)
 	if sh == nil {
 		return 0
 	}
 
 	Cwrite(ELF_NOTE_GO_NAME)
-	Cwrite([]byte(buildid))
+	Cwrite([]byte(*flagBuildid))
 	var zero = make([]byte, 4)
-	Cwrite(zero[:int(Rnd(int64(len(buildid)), 4)-int64(len(buildid)))])
+	Cwrite(zero[:int(Rnd(int64(len(*flagBuildid)), 4)-int64(len(*flagBuildid)))])
 
 	return int(sh.size)
 }
@@ -1438,13 +1443,13 @@ havelib:
 	return aux
 }
 
-func elfdynhash() {
+func elfdynhash(ctxt *Link) {
 	if !Iself {
 		return
 	}
 
 	nsym := Nelfsym
-	s := Linklookup(Ctxt, ".hash", 0)
+	s := ctxt.Syms.Lookup(".hash", 0)
 	s.Type = obj.SELFROSECT
 	s.Attr |= AttrReachable
 
@@ -1461,7 +1466,7 @@ func elfdynhash() {
 	buckets := make([]uint32, nbucket)
 
 	var b int
-	for _, sy := range Ctxt.Allsym {
+	for _, sy := range ctxt.Syms.Allsym {
 		if sy.Dynid <= 0 {
 			continue
 		}
@@ -1480,29 +1485,29 @@ func elfdynhash() {
 
 	// s390x (ELF64) hash table entries are 8 bytes
 	if SysArch.Family == sys.S390X {
-		Adduint64(Ctxt, s, uint64(nbucket))
-		Adduint64(Ctxt, s, uint64(nsym))
+		Adduint64(ctxt, s, uint64(nbucket))
+		Adduint64(ctxt, s, uint64(nsym))
 		for i := 0; i < nbucket; i++ {
-			Adduint64(Ctxt, s, uint64(buckets[i]))
+			Adduint64(ctxt, s, uint64(buckets[i]))
 		}
 		for i := 0; i < nsym; i++ {
-			Adduint64(Ctxt, s, uint64(chain[i]))
+			Adduint64(ctxt, s, uint64(chain[i]))
 		}
 	} else {
-		Adduint32(Ctxt, s, uint32(nbucket))
-		Adduint32(Ctxt, s, uint32(nsym))
+		Adduint32(ctxt, s, uint32(nbucket))
+		Adduint32(ctxt, s, uint32(nsym))
 		for i := 0; i < nbucket; i++ {
-			Adduint32(Ctxt, s, buckets[i])
+			Adduint32(ctxt, s, buckets[i])
 		}
 		for i := 0; i < nsym; i++ {
-			Adduint32(Ctxt, s, chain[i])
+			Adduint32(ctxt, s, chain[i])
 		}
 	}
 
 	// version symbols
-	dynstr := Linklookup(Ctxt, ".dynstr", 0)
+	dynstr := ctxt.Syms.Lookup(".dynstr", 0)
 
-	s = Linklookup(Ctxt, ".gnu.version_r", 0)
+	s = ctxt.Syms.Lookup(".gnu.version_r", 0)
 	i = 2
 	nfile := 0
 	var j int
@@ -1511,18 +1516,18 @@ func elfdynhash() {
 		nfile++
 
 		// header
-		Adduint16(Ctxt, s, 1) // table version
+		Adduint16(ctxt, s, 1) // table version
 		j = 0
 		for x = l.aux; x != nil; x = x.next {
 			j++
 		}
-		Adduint16(Ctxt, s, uint16(j))                         // aux count
-		Adduint32(Ctxt, s, uint32(Addstring(dynstr, l.file))) // file string offset
-		Adduint32(Ctxt, s, 16)                                // offset from header to first aux
+		Adduint16(ctxt, s, uint16(j))                         // aux count
+		Adduint32(ctxt, s, uint32(Addstring(dynstr, l.file))) // file string offset
+		Adduint32(ctxt, s, 16)                                // offset from header to first aux
 		if l.next != nil {
-			Adduint32(Ctxt, s, 16+uint32(j)*16) // offset from this header to next
+			Adduint32(ctxt, s, 16+uint32(j)*16) // offset from this header to next
 		} else {
-			Adduint32(Ctxt, s, 0)
+			Adduint32(ctxt, s, 0)
 		}
 
 		for x = l.aux; x != nil; x = x.next {
@@ -1530,51 +1535,51 @@ func elfdynhash() {
 			i++
 
 			// aux struct
-			Adduint32(Ctxt, s, elfhash(x.vers))                   // hash
-			Adduint16(Ctxt, s, 0)                                 // flags
-			Adduint16(Ctxt, s, uint16(x.num))                     // other - index we refer to this by
-			Adduint32(Ctxt, s, uint32(Addstring(dynstr, x.vers))) // version string offset
+			Adduint32(ctxt, s, elfhash(x.vers))                   // hash
+			Adduint16(ctxt, s, 0)                                 // flags
+			Adduint16(ctxt, s, uint16(x.num))                     // other - index we refer to this by
+			Adduint32(ctxt, s, uint32(Addstring(dynstr, x.vers))) // version string offset
 			if x.next != nil {
-				Adduint32(Ctxt, s, 16) // offset from this aux to next
+				Adduint32(ctxt, s, 16) // offset from this aux to next
 			} else {
-				Adduint32(Ctxt, s, 0)
+				Adduint32(ctxt, s, 0)
 			}
 		}
 	}
 
 	// version references
-	s = Linklookup(Ctxt, ".gnu.version", 0)
+	s = ctxt.Syms.Lookup(".gnu.version", 0)
 
 	for i := 0; i < nsym; i++ {
 		if i == 0 {
-			Adduint16(Ctxt, s, 0) // first entry - no symbol
+			Adduint16(ctxt, s, 0) // first entry - no symbol
 		} else if need[i] == nil {
-			Adduint16(Ctxt, s, 1) // global
+			Adduint16(ctxt, s, 1) // global
 		} else {
-			Adduint16(Ctxt, s, uint16(need[i].num))
+			Adduint16(ctxt, s, uint16(need[i].num))
 		}
 	}
 
-	s = Linklookup(Ctxt, ".dynamic", 0)
+	s = ctxt.Syms.Lookup(".dynamic", 0)
 	elfverneed = nfile
 	if elfverneed != 0 {
-		elfwritedynentsym(s, DT_VERNEED, Linklookup(Ctxt, ".gnu.version_r", 0))
-		Elfwritedynent(s, DT_VERNEEDNUM, uint64(nfile))
-		elfwritedynentsym(s, DT_VERSYM, Linklookup(Ctxt, ".gnu.version", 0))
+		elfwritedynentsym(ctxt, s, DT_VERNEED, ctxt.Syms.Lookup(".gnu.version_r", 0))
+		Elfwritedynent(ctxt, s, DT_VERNEEDNUM, uint64(nfile))
+		elfwritedynentsym(ctxt, s, DT_VERSYM, ctxt.Syms.Lookup(".gnu.version", 0))
 	}
 
-	sy := Linklookup(Ctxt, elfRelType+".plt", 0)
+	sy := ctxt.Syms.Lookup(elfRelType+".plt", 0)
 	if sy.Size > 0 {
 		if elfRelType == ".rela" {
-			Elfwritedynent(s, DT_PLTREL, DT_RELA)
+			Elfwritedynent(ctxt, s, DT_PLTREL, DT_RELA)
 		} else {
-			Elfwritedynent(s, DT_PLTREL, DT_REL)
+			Elfwritedynent(ctxt, s, DT_PLTREL, DT_REL)
 		}
-		elfwritedynentsymsize(s, DT_PLTRELSZ, sy)
-		elfwritedynentsym(s, DT_JMPREL, sy)
+		elfwritedynentsymsize(ctxt, s, DT_PLTRELSZ, sy)
+		elfwritedynentsym(ctxt, s, DT_JMPREL, sy)
 	}
 
-	Elfwritedynent(s, DT_NULL, 0)
+	Elfwritedynent(ctxt, s, DT_NULL, 0)
 }
 
 func elfphload(seg *Segment) *ElfPhdr {
@@ -1594,11 +1599,22 @@ func elfphload(seg *Segment) *ElfPhdr {
 	ph.memsz = seg.Length
 	ph.off = seg.Fileoff
 	ph.filesz = seg.Filelen
-	ph.align = uint64(INITRND)
+	ph.align = uint64(*FlagRound)
 
 	return ph
 }
 
+func elfphrelro(seg *Segment) {
+	ph := newElfPhdr()
+	ph.type_ = PT_GNU_RELRO
+	ph.vaddr = seg.Vaddr
+	ph.paddr = seg.Vaddr
+	ph.memsz = seg.Length
+	ph.off = seg.Fileoff
+	ph.filesz = seg.Filelen
+	ph.align = uint64(*FlagRound)
+}
+
 func elfshname(name string) *ElfShdr {
 	var off int
 	var sh *ElfShdr
@@ -1618,7 +1634,25 @@ func elfshname(name string) *ElfShdr {
 		}
 	}
 
-	Diag("cannot find elf name %s", name)
+	Exitf("cannot find elf name %s", name)
+	return nil
+}
+
+// Create an ElfShdr for the section with name.
+// Create a duplicate if one already exists with that name
+func elfshnamedup(name string) *ElfShdr {
+	var off int
+	var sh *ElfShdr
+
+	for i := 0; i < nelfstr; i++ {
+		if name == elfstr[i].s {
+			off = elfstr[i].off
+			sh = newElfShdr(int64(off))
+			return sh
+		}
+	}
+
+	Errorf(nil, "cannot find elf name %s", name)
 	errorexit()
 	return nil
 }
@@ -1630,7 +1664,17 @@ func elfshalloc(sect *Section) *ElfShdr {
 }
 
 func elfshbits(sect *Section) *ElfShdr {
-	sh := elfshalloc(sect)
+	var sh *ElfShdr
+
+	if sect.Name == ".text" {
+		if sect.Elfsect == nil {
+			sect.Elfsect = elfshnamedup(sect.Name)
+		}
+		sh = sect.Elfsect
+	} else {
+		sh = elfshalloc(sect)
+	}
+
 	// If this section has already been set up as a note, we assume type_ and
 	// flags are already correct, but the other fields still need filling in.
 	if sh.type_ == SHT_NOTE {
@@ -1640,9 +1684,9 @@ func elfshbits(sect *Section) *ElfShdr {
 			// in a loadable segment (e.g. the abihash note) but not for
 			// notes that we do not want to be mapped (e.g. the package
 			// list note). The real fix is probably to define new values
-			// for LSym.Type corresponding to mapped and unmapped notes
+			// for Symbol.Type corresponding to mapped and unmapped notes
 			// and handle them in dodata().
-			Diag("sh.type_ == SHT_NOTE in elfshbits when linking internally")
+			Errorf(nil, "sh.type_ == SHT_NOTE in elfshbits when linking internally")
 		}
 		sh.addralign = uint64(sect.Align)
 		sh.size = sect.Length
@@ -1706,6 +1750,15 @@ func elfshreloc(sect *Section) *ElfShdr {
 	}
 
 	sh := elfshname(elfRelType + sect.Name)
+	// There could be multiple text sections but each needs
+	// its own .rela.text.
+
+	if sect.Name == ".text" {
+		if sh.info != 0 && sh.info != uint32(sect.Elfsect.shnum) {
+			sh = elfshnamedup(elfRelType + sect.Name)
+		}
+	}
+
 	sh.type_ = uint32(typ)
 	sh.entsize = uint64(SysArch.RegSize) * 2
 	if typ == SHT_RELA {
@@ -1719,7 +1772,7 @@ func elfshreloc(sect *Section) *ElfShdr {
 	return sh
 }
 
-func elfrelocsect(sect *Section, syms []*LSym) {
+func elfrelocsect(ctxt *Link, sect *Section, syms []*Symbol) {
 	// If main section is SHT_NOBITS, nothing to relocate.
 	// Also nothing to relocate in .shstrtab.
 	if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
@@ -1729,7 +1782,7 @@ func elfrelocsect(sect *Section, syms []*LSym) {
 		return
 	}
 
-	sect.Reloff = uint64(Cpos())
+	sect.Reloff = uint64(coutbuf.Offset())
 	for i, s := range syms {
 		if !s.Attr.Reachable() {
 			continue
@@ -1748,59 +1801,67 @@ func elfrelocsect(sect *Section, syms []*LSym) {
 		if sym.Value >= int64(eaddr) {
 			break
 		}
-		Ctxt.Cursym = sym
-
 		for ri := 0; ri < len(sym.R); ri++ {
 			r := &sym.R[ri]
 			if r.Done != 0 {
 				continue
 			}
 			if r.Xsym == nil {
-				Diag("missing xsym in relocation")
+				Errorf(sym, "missing xsym in relocation")
 				continue
 			}
 			if r.Xsym.ElfsymForReloc() == 0 {
-				Diag("reloc %d to non-elf symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type)
+				Errorf(sym, "reloc %d to non-elf symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type)
 			}
-			if Thearch.Elfreloc1(r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 {
-				Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name)
+			if !r.Xsym.Attr.Reachable() {
+				Errorf(sym, "unreachable reloc %v target %v", r.Type, r.Xsym.Name)
+			}
+			if Thearch.Elfreloc1(ctxt, r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 {
+				Errorf(sym, "unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name)
 			}
 		}
 	}
 
-	sect.Rellen = uint64(Cpos()) - sect.Reloff
+	sect.Rellen = uint64(coutbuf.Offset()) - sect.Reloff
 }
 
-func Elfemitreloc() {
-	for Cpos()&7 != 0 {
+func Elfemitreloc(ctxt *Link) {
+	for coutbuf.Offset()&7 != 0 {
 		Cput(0)
 	}
 
-	elfrelocsect(Segtext.Sect, Ctxt.Textp)
-	for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next {
-		elfrelocsect(sect, datap)
+	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
+		if sect.Name == ".text" {
+			elfrelocsect(ctxt, sect, ctxt.Textp)
+		} else {
+			elfrelocsect(ctxt, sect, datap)
+		}
 	}
+
 	for sect := Segrodata.Sect; sect != nil; sect = sect.Next {
-		elfrelocsect(sect, datap)
+		elfrelocsect(ctxt, sect, datap)
+	}
+	for sect := Segrelrodata.Sect; sect != nil; sect = sect.Next {
+		elfrelocsect(ctxt, sect, datap)
 	}
 	for sect := Segdata.Sect; sect != nil; sect = sect.Next {
-		elfrelocsect(sect, datap)
+		elfrelocsect(ctxt, sect, datap)
 	}
 	for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
-		elfrelocsect(sect, list2slice(dwarfp))
+		elfrelocsect(ctxt, sect, dwarfp)
 	}
 }
 
-func addgonote(sectionName string, tag uint32, desc []byte) {
-	s := Linklookup(Ctxt, sectionName, 0)
+func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) {
+	s := ctxt.Syms.Lookup(sectionName, 0)
 	s.Attr |= AttrReachable
 	s.Type = obj.SELFROSECT
 	// namesz
-	Adduint32(Ctxt, s, uint32(len(ELF_NOTE_GO_NAME)))
+	Adduint32(ctxt, s, uint32(len(ELF_NOTE_GO_NAME)))
 	// descsz
-	Adduint32(Ctxt, s, uint32(len(desc)))
+	Adduint32(ctxt, s, uint32(len(desc)))
 	// tag
-	Adduint32(Ctxt, s, tag)
+	Adduint32(ctxt, s, tag)
 	// name + padding
 	s.P = append(s.P, ELF_NOTE_GO_NAME...)
 	for len(s.P)%4 != 0 {
@@ -1814,13 +1875,13 @@ func addgonote(sectionName string, tag uint32, desc []byte) {
 	s.Size = int64(len(s.P))
 }
 
-func doelf() {
+func (ctxt *Link) doelf() {
 	if !Iself {
 		return
 	}
 
 	/* predefine strings we need for section headers */
-	shstrtab := Linklookup(Ctxt, ".shstrtab", 0)
+	shstrtab := ctxt.Syms.Lookup(".shstrtab", 0)
 
 	shstrtab.Type = obj.SELFROSECT
 	shstrtab.Attr |= AttrReachable
@@ -1836,21 +1897,21 @@ func doelf() {
 	// for dynamic internal linker or external linking, so that various
 	// binutils could correctly calculate PT_TLS size.
 	// see https://golang.org/issue/5200.
-	if HEADTYPE != obj.Hopenbsd {
-		if Debug['d'] == 0 || Linkmode == LinkExternal {
+	if Headtype != obj.Hopenbsd {
+		if !*FlagD || Linkmode == LinkExternal {
 			Addstring(shstrtab, ".tbss")
 		}
 	}
-	if HEADTYPE == obj.Hnetbsd {
+	if Headtype == obj.Hnetbsd {
 		Addstring(shstrtab, ".note.netbsd.ident")
 	}
-	if HEADTYPE == obj.Hopenbsd {
+	if Headtype == obj.Hopenbsd {
 		Addstring(shstrtab, ".note.openbsd.ident")
 	}
 	if len(buildinfo) > 0 {
 		Addstring(shstrtab, ".note.gnu.build-id")
 	}
-	if buildid != "" {
+	if *flagBuildid != "" {
 		Addstring(shstrtab, ".note.go.buildid")
 	}
 	Addstring(shstrtab, ".elfdata")
@@ -1867,7 +1928,7 @@ func doelf() {
 	Addstring(shstrtab, relro_prefix+".gopclntab")
 
 	if Linkmode == LinkExternal {
-		Debug['d'] = 1
+		*FlagD = true
 
 		Addstring(shstrtab, elfRelType+".text")
 		Addstring(shstrtab, elfRelType+".rodata")
@@ -1891,11 +1952,11 @@ func doelf() {
 		}
 	}
 
-	hasinitarr := Linkshared
+	hasinitarr := *FlagLinkshared
 
 	/* shared library initializer */
 	switch Buildmode {
-	case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
+	case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePlugin:
 		hasinitarr = true
 	}
 
@@ -1904,15 +1965,15 @@ func doelf() {
 		Addstring(shstrtab, elfRelType+".init_array")
 	}
 
-	if Debug['s'] == 0 {
+	if !*FlagS {
 		Addstring(shstrtab, ".symtab")
 		Addstring(shstrtab, ".strtab")
-		dwarfaddshstrings(shstrtab)
+		dwarfaddshstrings(ctxt, shstrtab)
 	}
 
 	Addstring(shstrtab, ".shstrtab")
 
-	if Debug['d'] == 0 { /* -d suppresses dynamic loader format */
+	if !*FlagD { /* -d suppresses dynamic loader format */
 		Addstring(shstrtab, ".interp")
 		Addstring(shstrtab, ".hash")
 		Addstring(shstrtab, ".got")
@@ -1931,7 +1992,7 @@ func doelf() {
 		Addstring(shstrtab, ".gnu.version_r")
 
 		/* dynamic symbol table - first entry all zeros */
-		s := Linklookup(Ctxt, ".dynsym", 0)
+		s := ctxt.Syms.Lookup(".dynsym", 0)
 
 		s.Type = obj.SELFROSECT
 		s.Attr |= AttrReachable
@@ -1942,7 +2003,7 @@ func doelf() {
 		}
 
 		/* dynamic string table */
-		s = Linklookup(Ctxt, ".dynstr", 0)
+		s = ctxt.Syms.Lookup(".dynstr", 0)
 
 		s.Type = obj.SELFROSECT
 		s.Attr |= AttrReachable
@@ -1952,34 +2013,34 @@ func doelf() {
 		dynstr := s
 
 		/* relocation table */
-		s = Linklookup(Ctxt, elfRelType, 0)
+		s = ctxt.Syms.Lookup(elfRelType, 0)
 		s.Attr |= AttrReachable
 		s.Type = obj.SELFROSECT
 
 		/* global offset table */
-		s = Linklookup(Ctxt, ".got", 0)
+		s = ctxt.Syms.Lookup(".got", 0)
 
 		s.Attr |= AttrReachable
 		s.Type = obj.SELFGOT // writable
 
 		/* ppc64 glink resolver */
 		if SysArch.Family == sys.PPC64 {
-			s := Linklookup(Ctxt, ".glink", 0)
+			s := ctxt.Syms.Lookup(".glink", 0)
 			s.Attr |= AttrReachable
 			s.Type = obj.SELFRXSECT
 		}
 
 		/* hash */
-		s = Linklookup(Ctxt, ".hash", 0)
+		s = ctxt.Syms.Lookup(".hash", 0)
 
 		s.Attr |= AttrReachable
 		s.Type = obj.SELFROSECT
 
-		s = Linklookup(Ctxt, ".got.plt", 0)
+		s = ctxt.Syms.Lookup(".got.plt", 0)
 		s.Attr |= AttrReachable
 		s.Type = obj.SELFSECT // writable
 
-		s = Linklookup(Ctxt, ".plt", 0)
+		s = ctxt.Syms.Lookup(".plt", 0)
 
 		s.Attr |= AttrReachable
 		if SysArch.Family == sys.PPC64 {
@@ -1990,22 +2051,22 @@ func doelf() {
 			s.Type = obj.SELFRXSECT
 		}
 
-		Thearch.Elfsetupplt()
+		Thearch.Elfsetupplt(ctxt)
 
-		s = Linklookup(Ctxt, elfRelType+".plt", 0)
+		s = ctxt.Syms.Lookup(elfRelType+".plt", 0)
 		s.Attr |= AttrReachable
 		s.Type = obj.SELFROSECT
 
-		s = Linklookup(Ctxt, ".gnu.version", 0)
+		s = ctxt.Syms.Lookup(".gnu.version", 0)
 		s.Attr |= AttrReachable
 		s.Type = obj.SELFROSECT
 
-		s = Linklookup(Ctxt, ".gnu.version_r", 0)
+		s = ctxt.Syms.Lookup(".gnu.version_r", 0)
 		s.Attr |= AttrReachable
 		s.Type = obj.SELFROSECT
 
 		/* define dynamic elf table */
-		s = Linklookup(Ctxt, ".dynamic", 0)
+		s = ctxt.Syms.Lookup(".dynamic", 0)
 
 		s.Attr |= AttrReachable
 		s.Type = obj.SELFSECT // writable
@@ -2013,85 +2074,85 @@ func doelf() {
 		/*
 		 * .dynamic table
 		 */
-		elfwritedynentsym(s, DT_HASH, Linklookup(Ctxt, ".hash", 0))
+		elfwritedynentsym(ctxt, s, DT_HASH, ctxt.Syms.Lookup(".hash", 0))
 
-		elfwritedynentsym(s, DT_SYMTAB, Linklookup(Ctxt, ".dynsym", 0))
+		elfwritedynentsym(ctxt, s, DT_SYMTAB, ctxt.Syms.Lookup(".dynsym", 0))
 		if elf64 {
-			Elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE)
+			Elfwritedynent(ctxt, s, DT_SYMENT, ELF64SYMSIZE)
 		} else {
-			Elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE)
+			Elfwritedynent(ctxt, s, DT_SYMENT, ELF32SYMSIZE)
 		}
-		elfwritedynentsym(s, DT_STRTAB, Linklookup(Ctxt, ".dynstr", 0))
-		elfwritedynentsymsize(s, DT_STRSZ, Linklookup(Ctxt, ".dynstr", 0))
+		elfwritedynentsym(ctxt, s, DT_STRTAB, ctxt.Syms.Lookup(".dynstr", 0))
+		elfwritedynentsymsize(ctxt, s, DT_STRSZ, ctxt.Syms.Lookup(".dynstr", 0))
 		if elfRelType == ".rela" {
-			elfwritedynentsym(s, DT_RELA, Linklookup(Ctxt, ".rela", 0))
-			elfwritedynentsymsize(s, DT_RELASZ, Linklookup(Ctxt, ".rela", 0))
-			Elfwritedynent(s, DT_RELAENT, ELF64RELASIZE)
+			elfwritedynentsym(ctxt, s, DT_RELA, ctxt.Syms.Lookup(".rela", 0))
+			elfwritedynentsymsize(ctxt, s, DT_RELASZ, ctxt.Syms.Lookup(".rela", 0))
+			Elfwritedynent(ctxt, s, DT_RELAENT, ELF64RELASIZE)
 		} else {
-			elfwritedynentsym(s, DT_REL, Linklookup(Ctxt, ".rel", 0))
-			elfwritedynentsymsize(s, DT_RELSZ, Linklookup(Ctxt, ".rel", 0))
-			Elfwritedynent(s, DT_RELENT, ELF32RELSIZE)
+			elfwritedynentsym(ctxt, s, DT_REL, ctxt.Syms.Lookup(".rel", 0))
+			elfwritedynentsymsize(ctxt, s, DT_RELSZ, ctxt.Syms.Lookup(".rel", 0))
+			Elfwritedynent(ctxt, s, DT_RELENT, ELF32RELSIZE)
 		}
 
 		if rpath.val != "" {
-			Elfwritedynent(s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val)))
+			Elfwritedynent(ctxt, s, DT_RUNPATH, uint64(Addstring(dynstr, rpath.val)))
 		}
 
 		if SysArch.Family == sys.PPC64 {
-			elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".plt", 0))
+			elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".plt", 0))
 		} else if SysArch.Family == sys.S390X {
-			elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got", 0))
+			elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got", 0))
 		} else {
-			elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got.plt", 0))
+			elfwritedynentsym(ctxt, s, DT_PLTGOT, ctxt.Syms.Lookup(".got.plt", 0))
 		}
 
 		if SysArch.Family == sys.PPC64 {
-			Elfwritedynent(s, DT_PPC64_OPT, 0)
+			Elfwritedynent(ctxt, s, DT_PPC64_OPT, 0)
 		}
 
 		// Solaris dynamic linker can't handle an empty .rela.plt if
 		// DT_JMPREL is emitted so we have to defer generation of DT_PLTREL,
 		// DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the
 		// size of .rel(a).plt section.
-		Elfwritedynent(s, DT_DEBUG, 0)
+		Elfwritedynent(ctxt, s, DT_DEBUG, 0)
 	}
 
 	if Buildmode == BuildmodeShared {
 		// The go.link.abihashbytes symbol will be pointed at the appropriate
 		// part of the .note.go.abihash section in data.go:func address().
-		s := Linklookup(Ctxt, "go.link.abihashbytes", 0)
+		s := ctxt.Syms.Lookup("go.link.abihashbytes", 0)
 		s.Attr |= AttrLocal
 		s.Type = obj.SRODATA
 		s.Attr |= AttrSpecial
 		s.Attr |= AttrReachable
 		s.Size = int64(sha1.Size)
 
-		sort.Sort(byPkg(Ctxt.Library))
+		sort.Sort(byPkg(ctxt.Library))
 		h := sha1.New()
-		for _, l := range Ctxt.Library {
+		for _, l := range ctxt.Library {
 			h.Write(l.hash)
 		}
-		addgonote(".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{}))
-		addgonote(".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, pkglistfornote)
+		addgonote(ctxt, ".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{}))
+		addgonote(ctxt, ".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, pkglistfornote)
 		var deplist []string
-		for _, shlib := range Ctxt.Shlibs {
+		for _, shlib := range ctxt.Shlibs {
 			deplist = append(deplist, filepath.Base(shlib.Path))
 		}
-		addgonote(".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n")))
+		addgonote(ctxt, ".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n")))
 	}
 
-	if Linkmode == LinkExternal && buildid != "" {
-		addgonote(".note.go.buildid", ELF_NOTE_GOBUILDID_TAG, []byte(buildid))
+	if Linkmode == LinkExternal && *flagBuildid != "" {
+		addgonote(ctxt, ".note.go.buildid", ELF_NOTE_GOBUILDID_TAG, []byte(*flagBuildid))
 	}
 }
 
 // Do not write DT_NULL.  elfdynhash will finish it.
-func shsym(sh *ElfShdr, s *LSym) {
+func shsym(sh *ElfShdr, s *Symbol) {
 	addr := Symaddr(s)
 	if sh.flags&SHF_ALLOC != 0 {
 		sh.addr = uint64(addr)
 	}
-	sh.off = uint64(datoff(addr))
+	sh.off = uint64(datoff(s, addr))
 	sh.size = uint64(s.Size)
 }
 
@@ -2109,11 +2170,22 @@ func Asmbelfsetup() {
 	elfshname("")
 
 	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
-		elfshalloc(sect)
+		// There could be multiple .text sections. Instead check the Elfsect
+		// field to determine if already has an ElfShdr and if not, create one.
+		if sect.Name == ".text" {
+			if sect.Elfsect == nil {
+				sect.Elfsect = elfshnamedup(sect.Name)
+			}
+		} else {
+			elfshalloc(sect)
+		}
 	}
 	for sect := Segrodata.Sect; sect != nil; sect = sect.Next {
 		elfshalloc(sect)
 	}
+	for sect := Segrelrodata.Sect; sect != nil; sect = sect.Next {
+		elfshalloc(sect)
+	}
 	for sect := Segdata.Sect; sect != nil; sect = sect.Next {
 		elfshalloc(sect)
 	}
@@ -2122,12 +2194,12 @@ func Asmbelfsetup() {
 	}
 }
 
-func Asmbelf(symo int64) {
+func Asmbelf(ctxt *Link, symo int64) {
 	eh := getElfEhdr()
 	switch SysArch.Family {
 	default:
 		Exitf("unknown architecture in asmbelf: %v", SysArch.Family)
-	case sys.MIPS64:
+	case sys.MIPS, sys.MIPS64:
 		eh.machine = EM_MIPS
 	case sys.ARM:
 		eh.machine = EM_ARM
@@ -2144,7 +2216,24 @@ func Asmbelf(symo int64) {
 	}
 
 	elfreserve := int64(ELFRESERVE)
-	startva := INITTEXT - int64(HEADR)
+
+	numtext := int64(0)
+	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
+		if sect.Name == ".text" {
+			numtext++
+		}
+	}
+
+	// If there are multiple text sections, extra space is needed
+	// in the elfreserve for the additional .text and .rela.text
+	// section headers.  It can handle 4 extra now. Headers are
+	// 64 bytes.
+
+	if numtext > 4 {
+		elfreserve += elfreserve + numtext*64*2
+	}
+
+	startva := *FlagTextAddr - int64(HEADR)
 	resoff := elfreserve
 
 	var pph *ElfPhdr
@@ -2165,7 +2254,7 @@ func Asmbelf(symo int64) {
 			sh.type_ = SHT_NOTE
 		}
 
-		if buildid != "" {
+		if *flagBuildid != "" {
 			sh := elfshname(".note.go.buildid")
 			sh.type_ = SHT_NOTE
 			sh.flags = SHF_ALLOC
@@ -2180,16 +2269,16 @@ func Asmbelf(symo int64) {
 	pph.type_ = PT_PHDR
 	pph.flags = PF_R
 	pph.off = uint64(eh.ehsize)
-	pph.vaddr = uint64(INITTEXT) - uint64(HEADR) + pph.off
-	pph.paddr = uint64(INITTEXT) - uint64(HEADR) + pph.off
-	pph.align = uint64(INITRND)
+	pph.vaddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.off
+	pph.paddr = uint64(*FlagTextAddr) - uint64(HEADR) + pph.off
+	pph.align = uint64(*FlagRound)
 
 	/*
 	 * PHDR must be in a loaded segment. Adjust the text
 	 * segment boundaries downwards to include it.
 	 * Except on NaCl where it must not be loaded.
 	 */
-	if HEADTYPE != obj.Hnacl {
+	if Headtype != obj.Hnacl {
 		o := int64(Segtext.Vaddr - pph.vaddr)
 		Segtext.Vaddr -= uint64(o)
 		Segtext.Length += uint64(o)
@@ -2198,7 +2287,7 @@ func Asmbelf(symo int64) {
 		Segtext.Filelen += uint64(o)
 	}
 
-	if Debug['d'] == 0 { /* -d suppresses dynamic loader format */
+	if !*FlagD { /* -d suppresses dynamic loader format */
 		/* interpreter */
 		sh := elfshname(".interp")
 
@@ -2206,7 +2295,7 @@ func Asmbelf(symo int64) {
 		sh.flags = SHF_ALLOC
 		sh.addralign = 1
 		if interpreter == "" {
-			switch HEADTYPE {
+			switch Headtype {
 			case obj.Hlinux:
 				interpreter = Thearch.Linuxdynld
 
@@ -2236,9 +2325,9 @@ func Asmbelf(symo int64) {
 	}
 
 	pnote = nil
-	if HEADTYPE == obj.Hnetbsd || HEADTYPE == obj.Hopenbsd {
+	if Headtype == obj.Hnetbsd || Headtype == obj.Hopenbsd {
 		var sh *ElfShdr
-		switch HEADTYPE {
+		switch Headtype {
 		case obj.Hnetbsd:
 			sh = elfshname(".note.netbsd.ident")
 			resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff)))
@@ -2267,7 +2356,7 @@ func Asmbelf(symo int64) {
 		phsh(pnote, sh)
 	}
 
-	if buildid != "" {
+	if *flagBuildid != "" {
 		sh := elfshname(".note.go.buildid")
 		resoff -= int64(elfgobuildid(sh, uint64(startva), uint64(resoff)))
 
@@ -2283,10 +2372,14 @@ func Asmbelf(symo int64) {
 	if Segrodata.Sect != nil {
 		elfphload(&Segrodata)
 	}
+	if Segrelrodata.Sect != nil {
+		elfphload(&Segrelrodata)
+		elfphrelro(&Segrelrodata)
+	}
 	elfphload(&Segdata)
 
 	/* Dynamic linking sections */
-	if Debug['d'] == 0 {
+	if !*FlagD {
 		sh := elfshname(".dynsym")
 		sh.type_ = SHT_DYNSYM
 		sh.flags = SHF_ALLOC
@@ -2299,13 +2392,13 @@ func Asmbelf(symo int64) {
 		sh.link = uint32(elfshname(".dynstr").shnum)
 
 		// sh->info = index of first non-local symbol (number of local symbols)
-		shsym(sh, Linklookup(Ctxt, ".dynsym", 0))
+		shsym(sh, ctxt.Syms.Lookup(".dynsym", 0))
 
 		sh = elfshname(".dynstr")
 		sh.type_ = SHT_STRTAB
 		sh.flags = SHF_ALLOC
 		sh.addralign = 1
-		shsym(sh, Linklookup(Ctxt, ".dynstr", 0))
+		shsym(sh, ctxt.Syms.Lookup(".dynstr", 0))
 
 		if elfverneed != 0 {
 			sh := elfshname(".gnu.version")
@@ -2314,7 +2407,7 @@ func Asmbelf(symo int64) {
 			sh.addralign = 2
 			sh.link = uint32(elfshname(".dynsym").shnum)
 			sh.entsize = 2
-			shsym(sh, Linklookup(Ctxt, ".gnu.version", 0))
+			shsym(sh, ctxt.Syms.Lookup(".gnu.version", 0))
 
 			sh = elfshname(".gnu.version_r")
 			sh.type_ = SHT_GNU_VERNEED
@@ -2322,7 +2415,7 @@ func Asmbelf(symo int64) {
 			sh.addralign = uint64(SysArch.RegSize)
 			sh.info = uint32(elfverneed)
 			sh.link = uint32(elfshname(".dynstr").shnum)
-			shsym(sh, Linklookup(Ctxt, ".gnu.version_r", 0))
+			shsym(sh, ctxt.Syms.Lookup(".gnu.version_r", 0))
 		}
 
 		if elfRelType == ".rela" {
@@ -2333,7 +2426,7 @@ func Asmbelf(symo int64) {
 			sh.addralign = uint64(SysArch.RegSize)
 			sh.link = uint32(elfshname(".dynsym").shnum)
 			sh.info = uint32(elfshname(".plt").shnum)
-			shsym(sh, Linklookup(Ctxt, ".rela.plt", 0))
+			shsym(sh, ctxt.Syms.Lookup(".rela.plt", 0))
 
 			sh = elfshname(".rela")
 			sh.type_ = SHT_RELA
@@ -2341,7 +2434,7 @@ func Asmbelf(symo int64) {
 			sh.entsize = ELF64RELASIZE
 			sh.addralign = 8
 			sh.link = uint32(elfshname(".dynsym").shnum)
-			shsym(sh, Linklookup(Ctxt, ".rela", 0))
+			shsym(sh, ctxt.Syms.Lookup(".rela", 0))
 		} else {
 			sh := elfshname(".rel.plt")
 			sh.type_ = SHT_REL
@@ -2349,7 +2442,7 @@ func Asmbelf(symo int64) {
 			sh.entsize = ELF32RELSIZE
 			sh.addralign = 4
 			sh.link = uint32(elfshname(".dynsym").shnum)
-			shsym(sh, Linklookup(Ctxt, ".rel.plt", 0))
+			shsym(sh, ctxt.Syms.Lookup(".rel.plt", 0))
 
 			sh = elfshname(".rel")
 			sh.type_ = SHT_REL
@@ -2357,7 +2450,7 @@ func Asmbelf(symo int64) {
 			sh.entsize = ELF32RELSIZE
 			sh.addralign = 4
 			sh.link = uint32(elfshname(".dynsym").shnum)
-			shsym(sh, Linklookup(Ctxt, ".rel", 0))
+			shsym(sh, ctxt.Syms.Lookup(".rel", 0))
 		}
 
 		if eh.machine == EM_PPC64 {
@@ -2365,7 +2458,7 @@ func Asmbelf(symo int64) {
 			sh.type_ = SHT_PROGBITS
 			sh.flags = SHF_ALLOC + SHF_EXECINSTR
 			sh.addralign = 4
-			shsym(sh, Linklookup(Ctxt, ".glink", 0))
+			shsym(sh, ctxt.Syms.Lookup(".glink", 0))
 		}
 
 		sh = elfshname(".plt")
@@ -2386,7 +2479,7 @@ func Asmbelf(symo int64) {
 			sh.entsize = 4
 		}
 		sh.addralign = sh.entsize
-		shsym(sh, Linklookup(Ctxt, ".plt", 0))
+		shsym(sh, ctxt.Syms.Lookup(".plt", 0))
 
 		// On ppc64, .got comes from the input files, so don't
 		// create it here, and .got.plt is not used.
@@ -2396,14 +2489,14 @@ func Asmbelf(symo int64) {
 			sh.flags = SHF_ALLOC + SHF_WRITE
 			sh.entsize = uint64(SysArch.RegSize)
 			sh.addralign = uint64(SysArch.RegSize)
-			shsym(sh, Linklookup(Ctxt, ".got", 0))
+			shsym(sh, ctxt.Syms.Lookup(".got", 0))
 
 			sh = elfshname(".got.plt")
 			sh.type_ = SHT_PROGBITS
 			sh.flags = SHF_ALLOC + SHF_WRITE
 			sh.entsize = uint64(SysArch.RegSize)
 			sh.addralign = uint64(SysArch.RegSize)
-			shsym(sh, Linklookup(Ctxt, ".got.plt", 0))
+			shsym(sh, ctxt.Syms.Lookup(".got.plt", 0))
 		}
 
 		sh = elfshname(".hash")
@@ -2412,7 +2505,7 @@ func Asmbelf(symo int64) {
 		sh.entsize = 4
 		sh.addralign = uint64(SysArch.RegSize)
 		sh.link = uint32(elfshname(".dynsym").shnum)
-		shsym(sh, Linklookup(Ctxt, ".hash", 0))
+		shsym(sh, ctxt.Syms.Lookup(".hash", 0))
 
 		/* sh and PT_DYNAMIC for .dynamic section */
 		sh = elfshname(".dynamic")
@@ -2422,7 +2515,7 @@ func Asmbelf(symo int64) {
 		sh.entsize = 2 * uint64(SysArch.RegSize)
 		sh.addralign = uint64(SysArch.RegSize)
 		sh.link = uint32(elfshname(".dynstr").shnum)
-		shsym(sh, Linklookup(Ctxt, ".dynamic", 0))
+		shsym(sh, ctxt.Syms.Lookup(".dynamic", 0))
 		ph := newElfPhdr()
 		ph.type_ = PT_DYNAMIC
 		ph.flags = PF_R + PF_W
@@ -2434,7 +2527,7 @@ func Asmbelf(symo int64) {
 		// Do not emit PT_TLS for OpenBSD since ld.so(1) does
 		// not currently support it. This is handled
 		// appropriately in runtime/cgo.
-		if HEADTYPE != obj.Hopenbsd {
+		if Headtype != obj.Hopenbsd {
 			tlssize := uint64(0)
 			for sect := Segdata.Sect; sect != nil; sect = sect.Next {
 				if sect.Name == ".tbss" {
@@ -2451,7 +2544,7 @@ func Asmbelf(symo int64) {
 		}
 	}
 
-	if HEADTYPE == obj.Hlinux {
+	if Headtype == obj.Hlinux {
 		ph := newElfPhdr()
 		ph.type_ = PT_GNU_STACK
 		ph.flags = PF_W + PF_R
@@ -2461,17 +2554,21 @@ func Asmbelf(symo int64) {
 		ph.type_ = PT_PAX_FLAGS
 		ph.flags = 0x2a00 // mprotect, randexec, emutramp disabled
 		ph.align = uint64(SysArch.RegSize)
+	} else if Headtype == obj.Hsolaris {
+		ph := newElfPhdr()
+		ph.type_ = PT_SUNWSTACK
+		ph.flags = PF_W + PF_R
 	}
 
 elfobj:
 	sh := elfshname(".shstrtab")
 	sh.type_ = SHT_STRTAB
 	sh.addralign = 1
-	shsym(sh, Linklookup(Ctxt, ".shstrtab", 0))
+	shsym(sh, ctxt.Syms.Lookup(".shstrtab", 0))
 	eh.shstrndx = uint16(sh.shnum)
 
 	// put these sections early in the list
-	if Debug['s'] == 0 {
+	if !*FlagS {
 		elfshname(".symtab")
 		elfshname(".strtab")
 	}
@@ -2482,6 +2579,9 @@ elfobj:
 	for sect := Segrodata.Sect; sect != nil; sect = sect.Next {
 		elfshbits(sect)
 	}
+	for sect := Segrelrodata.Sect; sect != nil; sect = sect.Next {
+		elfshbits(sect)
+	}
 	for sect := Segdata.Sect; sect != nil; sect = sect.Next {
 		elfshbits(sect)
 	}
@@ -2496,10 +2596,13 @@ elfobj:
 		for sect := Segrodata.Sect; sect != nil; sect = sect.Next {
 			elfshreloc(sect)
 		}
+		for sect := Segrelrodata.Sect; sect != nil; sect = sect.Next {
+			elfshreloc(sect)
+		}
 		for sect := Segdata.Sect; sect != nil; sect = sect.Next {
 			elfshreloc(sect)
 		}
-		for s := dwarfp; s != nil; s = s.Next {
+		for _, s := range dwarfp {
 			if len(s.R) > 0 || s.Type == obj.SDWARFINFO {
 				elfshreloc(s.Sect)
 			}
@@ -2515,7 +2618,7 @@ elfobj:
 		sh.flags = 0
 	}
 
-	if Debug['s'] == 0 {
+	if !*FlagS {
 		sh := elfshname(".symtab")
 		sh.type_ = SHT_SYMTAB
 		sh.off = uint64(symo)
@@ -2538,13 +2641,13 @@ elfobj:
 	eh.ident[EI_MAG1] = 'E'
 	eh.ident[EI_MAG2] = 'L'
 	eh.ident[EI_MAG3] = 'F'
-	if HEADTYPE == obj.Hfreebsd {
+	if Headtype == obj.Hfreebsd {
 		eh.ident[EI_OSABI] = ELFOSABI_FREEBSD
-	} else if HEADTYPE == obj.Hnetbsd {
+	} else if Headtype == obj.Hnetbsd {
 		eh.ident[EI_OSABI] = ELFOSABI_NETBSD
-	} else if HEADTYPE == obj.Hopenbsd {
+	} else if Headtype == obj.Hopenbsd {
 		eh.ident[EI_OSABI] = ELFOSABI_OPENBSD
-	} else if HEADTYPE == obj.Hdragonfly {
+	} else if Headtype == obj.Hdragonfly {
 		eh.ident[EI_OSABI] = ELFOSABI_NONE
 	}
 	if elf64 {
@@ -2552,7 +2655,7 @@ elfobj:
 	} else {
 		eh.ident[EI_CLASS] = ELFCLASS32
 	}
-	if Ctxt.Arch.ByteOrder == binary.BigEndian {
+	if ctxt.Arch.ByteOrder == binary.BigEndian {
 		eh.ident[EI_DATA] = ELFDATA2MSB
 	} else {
 		eh.ident[EI_DATA] = ELFDATA2LSB
@@ -2561,12 +2664,14 @@ elfobj:
 
 	if Linkmode == LinkExternal {
 		eh.type_ = ET_REL
+	} else if Buildmode == BuildmodePIE {
+		eh.type_ = ET_DYN
 	} else {
 		eh.type_ = ET_EXEC
 	}
 
 	if Linkmode != LinkExternal {
-		eh.entry = uint64(Entryvalue())
+		eh.entry = uint64(Entryvalue(ctxt))
 	}
 
 	eh.version = EV_CURRENT
@@ -2581,38 +2686,38 @@ elfobj:
 	a += int64(elfwritehdr())
 	a += int64(elfwritephdrs())
 	a += int64(elfwriteshdrs())
-	if Debug['d'] == 0 {
+	if !*FlagD {
 		a += int64(elfwriteinterp())
 	}
 	if Linkmode != LinkExternal {
-		if HEADTYPE == obj.Hnetbsd {
+		if Headtype == obj.Hnetbsd {
 			a += int64(elfwritenetbsdsig())
 		}
-		if HEADTYPE == obj.Hopenbsd {
+		if Headtype == obj.Hopenbsd {
 			a += int64(elfwriteopenbsdsig())
 		}
 		if len(buildinfo) > 0 {
 			a += int64(elfwritebuildinfo())
 		}
-		if buildid != "" {
+		if *flagBuildid != "" {
 			a += int64(elfwritegobuildid())
 		}
 	}
 
 	if a > elfreserve {
-		Diag("ELFRESERVE too small: %d > %d", a, elfreserve)
+		Errorf(nil, "ELFRESERVE too small: %d > %d with %d text sections", a, elfreserve, numtext)
 	}
 }
 
-func Elfadddynsym(ctxt *Link, s *LSym) {
+func Elfadddynsym(ctxt *Link, s *Symbol) {
 	if elf64 {
 		s.Dynid = int32(Nelfsym)
 		Nelfsym++
 
-		d := Linklookup(ctxt, ".dynsym", 0)
+		d := ctxt.Syms.Lookup(".dynsym", 0)
 
 		name := s.Extname
-		Adduint32(ctxt, d, uint32(Addstring(Linklookup(ctxt, ".dynstr", 0), name)))
+		Adduint32(ctxt, d, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name)))
 
 		/* type */
 		t := STB_GLOBAL << 4
@@ -2645,18 +2750,18 @@ func Elfadddynsym(ctxt *Link, s *LSym) {
 		Adduint64(ctxt, d, uint64(s.Size))
 
 		if SysArch.Family == sys.AMD64 && !s.Attr.CgoExportDynamic() && s.Dynimplib != "" && !seenlib[s.Dynimplib] {
-			Elfwritedynent(Linklookup(ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(Linklookup(ctxt, ".dynstr", 0), s.Dynimplib)))
+			Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(ctxt.Syms.Lookup(".dynstr", 0), s.Dynimplib)))
 		}
 	} else {
 		s.Dynid = int32(Nelfsym)
 		Nelfsym++
 
-		d := Linklookup(ctxt, ".dynsym", 0)
+		d := ctxt.Syms.Lookup(".dynsym", 0)
 
 		/* name */
 		name := s.Extname
 
-		Adduint32(ctxt, d, uint32(Addstring(Linklookup(ctxt, ".dynstr", 0), name)))
+		Adduint32(ctxt, d, uint32(Addstring(ctxt.Syms.Lookup(".dynstr", 0), name)))
 
 		/* value */
 		if s.Type == obj.SDYNIMPORT {
diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
index 79cdae0..5b84c3d 100644
--- a/src/cmd/link/internal/ld/go.go
+++ b/src/cmd/link/internal/ld/go.go
@@ -28,16 +28,16 @@ func expandpkg(t0 string, pkg string) string {
 //	once the dust settles, try to move some code to
 //		libmach, so that other linkers and ar can share.
 
-func ldpkg(f *bio.Reader, pkg string, length int64, filename string, whence int) {
+func ldpkg(ctxt *Link, f *bio.Reader, pkg string, length int64, filename string, whence int) {
 	var p0, p1 int
 
-	if Debug['g'] != 0 {
+	if *flagG {
 		return
 	}
 
 	if int64(int(length)) != length {
 		fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
-		if Debug['u'] != 0 {
+		if *flagU {
 			errorexit()
 		}
 		return
@@ -52,7 +52,7 @@ func ldpkg(f *bio.Reader, pkg string, length int64, filename string, whence int)
 	bdata := make([]byte, length)
 	if _, err := io.ReadFull(f, bdata); err != nil {
 		fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
-		if Debug['u'] != 0 {
+		if *flagU {
 			errorexit()
 		}
 		return
@@ -84,7 +84,7 @@ func ldpkg(f *bio.Reader, pkg string, length int64, filename string, whence int)
 		if pkg == "main" && !isMain {
 			Exitf("%s: not package main", filename)
 		}
-		if Debug['u'] != 0 && whence != ArchiveObj && !isSafe {
+		if *flagU && whence != ArchiveObj && !isSafe {
 			Exitf("load of unsafe package %s", filename)
 		}
 	}
@@ -101,7 +101,7 @@ func ldpkg(f *bio.Reader, pkg string, length int64, filename string, whence int)
 		i := strings.IndexByte(data[p0+1:], '\n')
 		if i < 0 {
 			fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
-			if Debug['u'] != 0 {
+			if *flagU {
 				errorexit()
 			}
 			return
@@ -114,25 +114,25 @@ func ldpkg(f *bio.Reader, pkg string, length int64, filename string, whence int)
 		}
 		if p1 < 0 {
 			fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
-			if Debug['u'] != 0 {
+			if *flagU {
 				errorexit()
 			}
 			return
 		}
 		p1 += p0
 
-		loadcgo(filename, pkg, data[p0:p1])
+		loadcgo(ctxt, filename, pkg, data[p0:p1])
 	}
 }
 
-func loadcgo(file string, pkg string, p string) {
+func loadcgo(ctxt *Link, file string, pkg string, p string) {
 	var next string
 	var q string
 	var f []string
 	var local string
 	var remote string
 	var lib string
-	var s *LSym
+	var s *Symbol
 
 	p0 := ""
 	for ; p != ""; p = next {
@@ -163,7 +163,7 @@ func loadcgo(file string, pkg string, p string) {
 				lib = f[3]
 			}
 
-			if Debug['d'] != 0 {
+			if *FlagD {
 				fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
 				nerrors++
 				return
@@ -174,7 +174,7 @@ func loadcgo(file string, pkg string, p string) {
 				// to force a link of foo.so.
 				havedynamic = 1
 
-				if HEADTYPE == obj.Hdarwin {
+				if Headtype == obj.Hdarwin {
 					Machoadddynlib(lib)
 				} else {
 					dynlib = append(dynlib, lib)
@@ -187,7 +187,7 @@ func loadcgo(file string, pkg string, p string) {
 			if i := strings.Index(remote, "#"); i >= 0 {
 				remote, q = remote[:i], remote[i+1:]
 			}
-			s = Linklookup(Ctxt, local, 0)
+			s = ctxt.Syms.Lookup(local, 0)
 			if local != f[1] {
 			}
 			if s.Type == 0 || s.Type == obj.SXREF || s.Type == obj.SHOSTOBJ {
@@ -208,7 +208,7 @@ func loadcgo(file string, pkg string, p string) {
 				goto err
 			}
 			local = f[1]
-			s = Linklookup(Ctxt, local, 0)
+			s = ctxt.Syms.Lookup(local, 0)
 			s.Type = obj.SHOSTOBJ
 			s.Size = 0
 			continue
@@ -225,11 +225,11 @@ func loadcgo(file string, pkg string, p string) {
 				remote = local
 			}
 			local = expandpkg(local, pkg)
-			s = Linklookup(Ctxt, local, 0)
+			s = ctxt.Syms.Lookup(local, 0)
 
 			switch Buildmode {
-			case BuildmodeCShared, BuildmodeCArchive:
-				if s == Linklookup(Ctxt, "main", 0) {
+			case BuildmodeCShared, BuildmodeCArchive, BuildmodePlugin:
+				if s == ctxt.Syms.Lookup("main", 0) {
 					continue
 				}
 			}
@@ -267,7 +267,7 @@ func loadcgo(file string, pkg string, p string) {
 				goto err
 			}
 
-			if Debug['I'] == 0 {
+			if *flagInterpreter == "" {
 				if interpreter != "" && interpreter != f[1] {
 					fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
 					nerrors++
@@ -298,43 +298,43 @@ err:
 
 var seenlib = make(map[string]bool)
 
-func adddynlib(lib string) {
+func adddynlib(ctxt *Link, lib string) {
 	if seenlib[lib] || Linkmode == LinkExternal {
 		return
 	}
 	seenlib[lib] = true
 
 	if Iself {
-		s := Linklookup(Ctxt, ".dynstr", 0)
+		s := ctxt.Syms.Lookup(".dynstr", 0)
 		if s.Size == 0 {
 			Addstring(s, "")
 		}
-		Elfwritedynent(Linklookup(Ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
+		Elfwritedynent(ctxt, ctxt.Syms.Lookup(".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
 	} else {
-		Diag("adddynlib: unsupported binary format")
+		Errorf(nil, "adddynlib: unsupported binary format")
 	}
 }
 
-func Adddynsym(ctxt *Link, s *LSym) {
+func Adddynsym(ctxt *Link, s *Symbol) {
 	if s.Dynid >= 0 || Linkmode == LinkExternal {
 		return
 	}
 
 	if Iself {
 		Elfadddynsym(ctxt, s)
-	} else if HEADTYPE == obj.Hdarwin {
-		Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname)
-	} else if HEADTYPE == obj.Hwindows {
+	} else if Headtype == obj.Hdarwin {
+		Errorf(s, "adddynsym: missed symbol (Extname=%s)", s.Extname)
+	} else if Headtype == obj.Hwindows {
 		// already taken care of
 	} else {
-		Diag("adddynsym: unsupported binary format")
+		Errorf(s, "adddynsym: unsupported binary format")
 	}
 }
 
 func fieldtrack(ctxt *Link) {
 	// record field tracking references
 	var buf bytes.Buffer
-	for _, s := range ctxt.Allsym {
+	for _, s := range ctxt.Syms.Allsym {
 		if strings.HasPrefix(s.Name, "go.track.") {
 			s.Attr |= AttrSpecial // do not lay out in data segment
 			s.Attr |= AttrHidden
@@ -352,26 +352,26 @@ func fieldtrack(ctxt *Link) {
 		}
 	}
 
-	if tracksym == "" {
+	if *flagFieldTrack == "" {
 		return
 	}
-	s := Linklookup(ctxt, tracksym, 0)
+	s := ctxt.Syms.Lookup(*flagFieldTrack, 0)
 	if !s.Attr.Reachable() {
 		return
 	}
-	addstrdata(tracksym, buf.String())
+	addstrdata(ctxt, *flagFieldTrack, buf.String())
 }
 
-func addexport() {
-	if HEADTYPE == obj.Hdarwin {
+func (ctxt *Link) addexport() {
+	if Headtype == obj.Hdarwin {
 		return
 	}
 
 	for _, exp := range dynexp {
-		Adddynsym(Ctxt, exp)
+		Adddynsym(ctxt, exp)
 	}
 	for _, lib := range dynlib {
-		adddynlib(lib)
+		adddynlib(ctxt, lib)
 	}
 }
 
@@ -419,15 +419,3 @@ func importcycles() {
 		p.cycle()
 	}
 }
-
-func setlinkmode(arg string) {
-	if arg == "internal" {
-		Linkmode = LinkInternal
-	} else if arg == "external" {
-		Linkmode = LinkExternal
-	} else if arg == "auto" {
-		Linkmode = LinkAuto
-	} else {
-		Exitf("unknown link mode -linkmode %s", arg)
-	}
-}
diff --git a/src/cmd/link/internal/ld/ld.go b/src/cmd/link/internal/ld/ld.go
index bbbfd3e..4750e82 100644
--- a/src/cmd/link/internal/ld/ld.go
+++ b/src/cmd/link/internal/ld/ld.go
@@ -1,6 +1,6 @@
 // Derived from Inferno utils/6l/obj.c and utils/6l/span.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -33,15 +33,15 @@ package ld
 
 import (
 	"cmd/internal/obj"
-	"fmt"
 	"io/ioutil"
 	"os"
 	"path"
+	"path/filepath"
 	"strconv"
 	"strings"
 )
 
-func addlib(ctxt *Link, src string, obj string, pathname string) {
+func addlib(ctxt *Link, src string, obj string, pathname string) *Library {
 	name := path.Clean(pathname)
 
 	// runtime.a -> runtime, runtime.6 -> runtime
@@ -53,18 +53,18 @@ func addlib(ctxt *Link, src string, obj string, pathname string) {
 	// already loaded?
 	for i := 0; i < len(ctxt.Library); i++ {
 		if ctxt.Library[i].Pkg == pkg {
-			return
+			return ctxt.Library[i]
 		}
 	}
 
 	var pname string
 	isshlib := false
-	if (ctxt.Windows == 0 && strings.HasPrefix(name, "/")) || (ctxt.Windows != 0 && len(name) >= 2 && name[1] == ':') {
+	if filepath.IsAbs(name) {
 		pname = name
 	} else {
 		// try dot, -L "libdir", and then goroot.
 		for _, dir := range ctxt.Libdir {
-			if Linkshared {
+			if *FlagLinkshared {
 				pname = dir + "/" + pkg + ".shlibname"
 				if _, err := os.Stat(pname); err == nil {
 					isshlib = true
@@ -80,33 +80,32 @@ func addlib(ctxt *Link, src string, obj string, pathname string) {
 
 	pname = path.Clean(pname)
 
-	if ctxt.Debugvlog > 1 && ctxt.Bso != nil {
-		fmt.Fprintf(ctxt.Bso, "%5.2f addlib: %s %s pulls in %s isshlib %v\n", elapsed(), obj, src, pname, isshlib)
+	if ctxt.Debugvlog > 1 {
+		ctxt.Logf("%5.2f addlib: %s %s pulls in %s isshlib %v\n", elapsed(), obj, src, pname, isshlib)
 	}
 
 	if isshlib {
-		addlibpath(ctxt, src, obj, "", pkg, pname)
-	} else {
-		addlibpath(ctxt, src, obj, pname, pkg, "")
+		return addlibpath(ctxt, src, obj, "", pkg, pname)
 	}
+	return addlibpath(ctxt, src, obj, pname, pkg, "")
 }
 
 /*
- * add library to library list.
+ * add library to library list, return added library.
  *	srcref: src file referring to package
  *	objref: object file referring to package
  *	file: object file, e.g., /home/rsc/go/pkg/container/vector.a
  *	pkg: package import path, e.g. container/vector
  */
-func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string, shlibnamefile string) {
+func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string, shlibnamefile string) *Library {
 	for i := 0; i < len(ctxt.Library); i++ {
 		if pkg == ctxt.Library[i].Pkg {
-			return
+			return ctxt.Library[i]
 		}
 	}
 
-	if ctxt.Debugvlog > 1 && ctxt.Bso != nil {
-		fmt.Fprintf(ctxt.Bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s shlibnamefile: %s\n", obj.Cputime(), srcref, objref, file, pkg, shlibnamefile)
+	if ctxt.Debugvlog > 1 {
+		ctxt.Logf("%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s shlibnamefile: %s\n", obj.Cputime(), srcref, objref, file, pkg, shlibnamefile)
 	}
 
 	ctxt.Library = append(ctxt.Library, &Library{})
@@ -118,10 +117,11 @@ func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg strin
 	if shlibnamefile != "" {
 		shlibbytes, err := ioutil.ReadFile(shlibnamefile)
 		if err != nil {
-			Diag("cannot read %s: %v", shlibnamefile, err)
+			Errorf(nil, "cannot read %s: %v", shlibnamefile, err)
 		}
 		l.Shlib = strings.TrimSpace(string(shlibbytes))
 	}
+	return l
 }
 
 func atolwhex(s string) int64 {
diff --git a/src/cmd/link/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go
index af60a5c..00e8f37 100644
--- a/src/cmd/link/internal/ld/ldelf.go
+++ b/src/cmd/link/internal/ld/ldelf.go
@@ -137,6 +137,8 @@ const (
 	ElfSymTypeFunc    = 2
 	ElfSymTypeSection = 3
 	ElfSymTypeFile    = 4
+	ElfSymTypeCommon  = 5
+	ElfSymTypeTLS     = 6
 )
 
 const (
@@ -264,7 +266,7 @@ type ElfSect struct {
 	align   uint64
 	entsize uint64
 	base    []byte
-	sym     *LSym
+	sym     *Symbol
 }
 
 type ElfObj struct {
@@ -303,29 +305,19 @@ type ElfSym struct {
 	type_ uint8
 	other uint8
 	shndx uint16
-	sym   *LSym
+	sym   *Symbol
 }
 
 var ElfMagic = [4]uint8{0x7F, 'E', 'L', 'F'}
 
-func valuecmp(a *LSym, b *LSym) int {
-	if a.Value < b.Value {
-		return -1
-	}
-	if a.Value > b.Value {
-		return +1
-	}
-	return 0
-}
-
 const (
-	Tag_file                 = 1
-	Tag_CPU_name             = 4
-	Tag_CPU_raw_name         = 5
-	Tag_compatibility        = 32
-	Tag_nodefaults           = 64
-	Tag_also_compatible_with = 65
-	Tag_ABI_VFP_args         = 28
+	TagFile               = 1
+	TagCPUName            = 4
+	TagCPURawName         = 5
+	TagCompatibility      = 32
+	TagNoDefaults         = 64
+	TagAlsoCompatibleWith = 65
+	TagABIVFPArgs         = 28
 )
 
 type elfAttribute struct {
@@ -366,7 +358,7 @@ func (a *elfAttributeList) uleb128() uint64 {
 func (a *elfAttributeList) armAttr() elfAttribute {
 	attr := elfAttribute{tag: a.uleb128()}
 	switch {
-	case attr.tag == Tag_compatibility:
+	case attr.tag == TagCompatibility:
 		attr.ival = a.uleb128()
 		attr.sval = a.string()
 
@@ -377,7 +369,7 @@ func (a *elfAttributeList) armAttr() elfAttribute {
 		attr.sval = a.string()
 
 	// Tag with string argument
-	case attr.tag == Tag_CPU_name || attr.tag == Tag_CPU_raw_name || (attr.tag >= 32 && attr.tag&1 != 0):
+	case attr.tag == TagCPUName || attr.tag == TagCPURawName || (attr.tag >= 32 && attr.tag&1 != 0):
 		attr.sval = a.string()
 
 	default: // Tag with integer argument
@@ -399,13 +391,14 @@ func (a *elfAttributeList) done() bool {
 // find the one we are looking for. This format is slightly documented in "ELF
 // for the ARM Architecture" but mostly this is derived from reading the source
 // to gold and readelf.
-func parseArmAttributes(e binary.ByteOrder, data []byte) {
+func parseArmAttributes(ctxt *Link, e binary.ByteOrder, data []byte) {
 	// We assume the soft-float ABI unless we see a tag indicating otherwise.
 	if ehdr.flags == 0x5000002 {
 		ehdr.flags = 0x5000202
 	}
 	if data[0] != 'A' {
-		fmt.Fprintf(Bso, ".ARM.attributes has unexpected format %c\n", data[0])
+		// TODO(dfc) should this be ctxt.Diag ?
+		ctxt.Logf(".ARM.attributes has unexpected format %c\n", data[0])
 		return
 	}
 	data = data[1:]
@@ -416,7 +409,8 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) {
 
 		nulIndex := bytes.IndexByte(sectiondata, 0)
 		if nulIndex < 0 {
-			fmt.Fprintf(Bso, "corrupt .ARM.attributes (section name not NUL-terminated)\n")
+			// TODO(dfc) should this be ctxt.Diag ?
+			ctxt.Logf("corrupt .ARM.attributes (section name not NUL-terminated)\n")
 			return
 		}
 		name := string(sectiondata[:nulIndex])
@@ -431,28 +425,29 @@ func parseArmAttributes(e binary.ByteOrder, data []byte) {
 			subsectiondata := sectiondata[sz+4 : subsectionsize]
 			sectiondata = sectiondata[subsectionsize:]
 
-			if subsectiontag == Tag_file {
+			if subsectiontag == TagFile {
 				attrList := elfAttributeList{data: subsectiondata}
 				for !attrList.done() {
 					attr := attrList.armAttr()
-					if attr.tag == Tag_ABI_VFP_args && attr.ival == 1 {
+					if attr.tag == TagABIVFPArgs && attr.ival == 1 {
 						ehdr.flags = 0x5000402 // has entry point, Version5 EABI, hard-float ABI
 					}
 				}
 				if attrList.err != nil {
-					fmt.Fprintf(Bso, "could not parse .ARM.attributes\n")
+					// TODO(dfc) should this be ctxt.Diag ?
+					ctxt.Logf("could not parse .ARM.attributes\n")
 				}
 			}
 		}
 	}
 }
 
-func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn)
+func ldelf(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f ldelf %s\n", obj.Cputime(), pn)
 	}
 
-	Ctxt.IncVersion()
+	localSymVersion := ctxt.Syms.IncVersion()
 	base := f.Offset()
 
 	var add uint64
@@ -472,10 +467,10 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 	var rela int
 	var rp *Reloc
 	var rsect *ElfSect
-	var s *LSym
+	var s *Symbol
 	var sect *ElfSect
 	var sym ElfSym
-	var symbols []*LSym
+	var symbols []*Symbol
 	if _, err := io.ReadFull(f, hdrbuf[:]); err != nil {
 		goto bad
 	}
@@ -544,54 +539,60 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 	}
 
 	if e.Uint16(hdr.Type[:]) != ElfTypeRelocatable {
-		Diag("%s: elf but not elf relocatable object", pn)
+		Errorf(nil, "%s: elf but not elf relocatable object", pn)
 		return
 	}
 
 	switch SysArch.Family {
 	default:
-		Diag("%s: elf %s unimplemented", pn, SysArch.Name)
+		Errorf(nil, "%s: elf %s unimplemented", pn, SysArch.Name)
 		return
 
+	case sys.MIPS:
+		if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass32 {
+			Errorf(nil, "%s: elf object but not mips", pn)
+			return
+		}
+
 	case sys.MIPS64:
 		if elfobj.machine != ElfMachMips || hdr.Ident[4] != ElfClass64 {
-			Diag("%s: elf object but not mips64", pn)
+			Errorf(nil, "%s: elf object but not mips64", pn)
 			return
 		}
 
 	case sys.ARM:
 		if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 {
-			Diag("%s: elf object but not arm", pn)
+			Errorf(nil, "%s: elf object but not arm", pn)
 			return
 		}
 
 	case sys.AMD64:
 		if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 {
-			Diag("%s: elf object but not amd64", pn)
+			Errorf(nil, "%s: elf object but not amd64", pn)
 			return
 		}
 
 	case sys.ARM64:
 		if e != binary.LittleEndian || elfobj.machine != ElfMachArm64 || hdr.Ident[4] != ElfClass64 {
-			Diag("%s: elf object but not arm64", pn)
+			Errorf(nil, "%s: elf object but not arm64", pn)
 			return
 		}
 
 	case sys.I386:
 		if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 {
-			Diag("%s: elf object but not 386", pn)
+			Errorf(nil, "%s: elf object but not 386", pn)
 			return
 		}
 
 	case sys.PPC64:
 		if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 {
-			Diag("%s: elf object but not ppc64", pn)
+			Errorf(nil, "%s: elf object but not ppc64", pn)
 			return
 		}
 
 	case sys.S390X:
 		if elfobj.machine != ElfMachS390 || hdr.Ident[4] != ElfClass64 {
-			Diag("%s: elf object but not s390x", pn)
+			Errorf(nil, "%s: elf object but not s390x", pn)
 			return
 		}
 	}
@@ -667,7 +668,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 	}
 
 	if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) {
-		Diag("%s: elf object has symbol table with invalid string table link", pn)
+		Errorf(nil, "%s: elf object has symbol table with invalid string table link", pn)
 		return
 	}
 
@@ -697,7 +698,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 			if err = elfmap(elfobj, sect); err != nil {
 				goto bad
 			}
-			parseArmAttributes(e, sect.base[:sect.size])
+			parseArmAttributes(ctxt, e, sect.base[:sect.size])
 		}
 		if (sect.type_ != ElfSectProgbits && sect.type_ != ElfSectNobits) || sect.flags&ElfSectFlagAlloc == 0 {
 			continue
@@ -709,7 +710,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 		}
 
 		name = fmt.Sprintf("%s(%s)", pkg, sect.name)
-		s = Linklookup(Ctxt, name, Ctxt.Version)
+		s = ctxt.Syms.Lookup(name, localSymVersion)
 
 		switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) {
 		default:
@@ -745,17 +746,17 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 
 	// enter sub-symbols into symbol table.
 	// symbol 0 is the null symbol.
-	symbols = make([]*LSym, elfobj.nsymtab)
+	symbols = make([]*Symbol, elfobj.nsymtab)
 
 	for i := 1; i < elfobj.nsymtab; i++ {
-		if err = readelfsym(elfobj, i, &sym, 1); err != nil {
+		if err = readelfsym(ctxt, elfobj, i, &sym, 1, localSymVersion); err != nil {
 			goto bad
 		}
 		symbols[i] = sym.sym
-		if sym.type_ != ElfSymTypeFunc && sym.type_ != ElfSymTypeObject && sym.type_ != ElfSymTypeNone {
+		if sym.type_ != ElfSymTypeFunc && sym.type_ != ElfSymTypeObject && sym.type_ != ElfSymTypeNone && sym.type_ != ElfSymTypeCommon {
 			continue
 		}
-		if sym.shndx == ElfSymShnCommon {
+		if sym.shndx == ElfSymShnCommon || sym.type_ == ElfSymTypeCommon {
 			s = sym.sym
 			if uint64(s.Size) < sym.size {
 				s.Size = int64(sym.size)
@@ -789,7 +790,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 			if strings.HasPrefix(sym.name, ".LASF") { // gcc on s390x does this
 				continue
 			}
-			Diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type_)
+			Errorf(sym.sym, "%s: sym#%d: ignoring symbol in section %d (type %d)", pn, i, sym.shndx, sym.type_)
 			continue
 		}
 
@@ -812,7 +813,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 		s.Outer = sect.sym
 		if sect.sym.Type == obj.STEXT {
 			if s.Attr.External() && !s.Attr.DuplicateOK() {
-				Diag("%s: duplicate definition of %s", pn, s.Name)
+				Errorf(s, "%s: duplicate symbol definition", pn)
 			}
 			s.Attr |= AttrExternal
 		}
@@ -822,7 +823,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 			if 2 <= flag && flag <= 6 {
 				s.Localentry = 1 << uint(flag-2)
 			} else if flag == 7 {
-				Diag("%s: invalid sym.other 0x%x for %s", pn, sym.other, s.Name)
+				Errorf(s, "%s: invalid sym.other 0x%x", pn, sym.other)
 			}
 		}
 	}
@@ -835,20 +836,20 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 			continue
 		}
 		if s.Sub != nil {
-			s.Sub = listsort(s.Sub, valuecmp, listsubp)
+			s.Sub = listsort(s.Sub)
 		}
 		if s.Type == obj.STEXT {
 			if s.Attr.OnList() {
 				log.Fatalf("symbol %s listed multiple times", s.Name)
 			}
 			s.Attr |= AttrOnList
-			Ctxt.Textp = append(Ctxt.Textp, s)
+			ctxt.Textp = append(ctxt.Textp, s)
 			for s = s.Sub; s != nil; s = s.Sub {
 				if s.Attr.OnList() {
 					log.Fatalf("symbol %s listed multiple times", s.Name)
 				}
 				s.Attr |= AttrOnList
-				Ctxt.Textp = append(Ctxt.Textp, s)
+				ctxt.Textp = append(ctxt.Textp, s)
 			}
 		}
 	}
@@ -910,7 +911,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 			if info>>32 == 0 { // absolute relocation, don't bother reading the null symbol
 				rp.Sym = nil
 			} else {
-				if err = readelfsym(elfobj, int(info>>32), &sym, 0); err != nil {
+				if err = readelfsym(ctxt, elfobj, int(info>>32), &sym, 0, 0); err != nil {
 					goto bad
 				}
 				sym.sym = symbols[info>>32]
@@ -922,8 +923,8 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 				rp.Sym = sym.sym
 			}
 
-			rp.Type = 256 + int32(info)
-			rp.Siz = relSize(pn, uint32(info))
+			rp.Type = 256 + obj.RelocType(info)
+			rp.Siz = relSize(ctxt, pn, uint32(info))
 			if rela != 0 {
 				rp.Add = int64(add)
 			} else {
@@ -933,7 +934,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 				} else if rp.Siz == 8 {
 					rp.Add = int64(e.Uint64(sect.base[rp.Off:]))
 				} else {
-					Diag("invalid rela size %d", rp.Siz)
+					Errorf(nil, "invalid rela size %d", rp.Siz)
 				}
 			}
 
@@ -957,7 +958,7 @@ func ldelf(f *bio.Reader, pkg string, length int64, pn string) {
 	return
 
 bad:
-	Diag("%s: malformed elf file: %v", pn, err)
+	Errorf(nil, "%s: malformed elf file: %v", pn, err)
 }
 
 func section(elfobj *ElfObj, name string) *ElfSect {
@@ -990,14 +991,14 @@ func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) {
 	return nil
 }
 
-func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) {
+func readelfsym(ctxt *Link, elfobj *ElfObj, i int, sym *ElfSym, needSym int, localSymVersion int) (err error) {
 	if i >= elfobj.nsymtab || i < 0 {
 		err = fmt.Errorf("invalid elf symbol index")
 		return err
 	}
 
 	if i == 0 {
-		Diag("readym: read null symbol!")
+		Errorf(nil, "readym: read null symbol!")
 	}
 
 	if elfobj.is64 != 0 {
@@ -1022,7 +1023,7 @@ func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) {
 		sym.other = b.Other
 	}
 
-	var s *LSym
+	var s *Symbol
 	if sym.name == "_GLOBAL_OFFSET_TABLE_" {
 		sym.name = ".got"
 	}
@@ -1036,11 +1037,11 @@ func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) {
 	case ElfSymTypeSection:
 		s = elfobj.sect[sym.shndx].sym
 
-	case ElfSymTypeObject, ElfSymTypeFunc, ElfSymTypeNone:
+	case ElfSymTypeObject, ElfSymTypeFunc, ElfSymTypeNone, ElfSymTypeCommon:
 		switch sym.bind {
 		case ElfSymBindGlobal:
 			if needSym != 0 {
-				s = Linklookup(Ctxt, sym.name, 0)
+				s = ctxt.Syms.Lookup(sym.name, 0)
 
 				// for global scoped hidden symbols we should insert it into
 				// symbol hash table, but mark them as hidden.
@@ -1066,7 +1067,7 @@ func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) {
 				// We need to be able to look this up,
 				// so put it in the hash table.
 				if needSym != 0 {
-					s = Linklookup(Ctxt, sym.name, Ctxt.Version)
+					s = ctxt.Syms.Lookup(sym.name, localSymVersion)
 					s.Type |= obj.SHIDDEN
 				}
 
@@ -1077,14 +1078,14 @@ func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) {
 				// local names and hidden global names are unique
 				// and should only be referenced by their index, not name, so we
 				// don't bother to add them into the hash table
-				s = linknewsym(Ctxt, sym.name, Ctxt.Version)
+				s = ctxt.Syms.newsym(sym.name, localSymVersion)
 
 				s.Type |= obj.SHIDDEN
 			}
 
 		case ElfSymBindWeak:
 			if needSym != 0 {
-				s = Linklookup(Ctxt, sym.name, 0)
+				s = ctxt.Syms.Lookup(sym.name, 0)
 				if sym.other == 2 {
 					s.Type |= obj.SHIDDEN
 				}
@@ -1126,7 +1127,7 @@ func (x rbyoff) Less(i, j int) bool {
 	return false
 }
 
-func relSize(pn string, elftype uint32) uint8 {
+func relSize(ctxt *Link, pn string, elftype uint32) uint8 {
 	// TODO(mdempsky): Replace this with a struct-valued switch statement
 	// once golang.org/issue/15164 is fixed or found to not impair cmd/link
 	// performance.
@@ -1141,7 +1142,7 @@ func relSize(pn string, elftype uint32) uint8 {
 
 	switch uint32(SysArch.Family) | elftype<<24 {
 	default:
-		Diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype)
+		Errorf(nil, "%s: unknown relocation type %d; compiled without -fpic?", pn, elftype)
 		fallthrough
 
 	case S390X | R_390_8<<24:
diff --git a/src/cmd/link/internal/ld/ldmacho.go b/src/cmd/link/internal/ld/ldmacho.go
index a101249..54812b1 100644
--- a/src/cmd/link/internal/ld/ldmacho.go
+++ b/src/cmd/link/internal/ld/ldmacho.go
@@ -43,7 +43,7 @@ const (
 	N_STAB = 0xe0
 )
 
-type LdMachoObj struct {
+type ldMachoObj struct {
 	f          *bio.Reader
 	base       int64 // off in f where Mach-O begins
 	length     int64 // length of Mach-O
@@ -54,20 +54,20 @@ type LdMachoObj struct {
 	subcputype uint
 	filetype   uint32
 	flags      uint32
-	cmd        []LdMachoCmd
+	cmd        []ldMachoCmd
 	ncmd       uint
 }
 
-type LdMachoCmd struct {
+type ldMachoCmd struct {
 	type_ int
 	off   uint32
 	size  uint32
-	seg   LdMachoSeg
-	sym   LdMachoSymtab
-	dsym  LdMachoDysymtab
+	seg   ldMachoSeg
+	sym   ldMachoSymtab
+	dsym  ldMachoDysymtab
 }
 
-type LdMachoSeg struct {
+type ldMachoSeg struct {
 	name     string
 	vmaddr   uint64
 	vmsize   uint64
@@ -77,10 +77,10 @@ type LdMachoSeg struct {
 	initprot uint32
 	nsect    uint32
 	flags    uint32
-	sect     []LdMachoSect
+	sect     []ldMachoSect
 }
 
-type LdMachoSect struct {
+type ldMachoSect struct {
 	name    string
 	segname string
 	addr    uint64
@@ -92,11 +92,11 @@ type LdMachoSect struct {
 	flags   uint32
 	res1    uint32
 	res2    uint32
-	sym     *LSym
-	rel     []LdMachoRel
+	sym     *Symbol
+	rel     []ldMachoRel
 }
 
-type LdMachoRel struct {
+type ldMachoRel struct {
 	addr      uint32
 	symnum    uint32
 	pcrel     uint8
@@ -107,26 +107,26 @@ type LdMachoRel struct {
 	value     uint32
 }
 
-type LdMachoSymtab struct {
+type ldMachoSymtab struct {
 	symoff  uint32
 	nsym    uint32
 	stroff  uint32
 	strsize uint32
 	str     []byte
-	sym     []LdMachoSym
+	sym     []ldMachoSym
 }
 
-type LdMachoSym struct {
+type ldMachoSym struct {
 	name    string
 	type_   uint8
 	sectnum uint8
 	desc    uint16
 	kind    int8
 	value   uint64
-	sym     *LSym
+	sym     *Symbol
 }
 
-type LdMachoDysymtab struct {
+type ldMachoDysymtab struct {
 	ilocalsym      uint32
 	nlocalsym      uint32
 	iextdefsym     uint32
@@ -175,7 +175,7 @@ const (
 	LdMachoFilePreload    = 5
 )
 
-func unpackcmd(p []byte, m *LdMachoObj, c *LdMachoCmd, type_ uint, sz uint) int {
+func unpackcmd(p []byte, m *ldMachoObj, c *ldMachoCmd, type_ uint, sz uint) int {
 	e4 := m.e.Uint32
 	e8 := m.e.Uint64
 
@@ -198,12 +198,12 @@ func unpackcmd(p []byte, m *LdMachoObj, c *LdMachoCmd, type_ uint, sz uint) int
 		c.seg.initprot = e4(p[44:])
 		c.seg.nsect = e4(p[48:])
 		c.seg.flags = e4(p[52:])
-		c.seg.sect = make([]LdMachoSect, c.seg.nsect)
+		c.seg.sect = make([]ldMachoSect, c.seg.nsect)
 		if uint32(sz) < 56+c.seg.nsect*68 {
 			return -1
 		}
 		p = p[56:]
-		var s *LdMachoSect
+		var s *ldMachoSect
 		for i := 0; uint32(i) < c.seg.nsect; i++ {
 			s = &c.seg.sect[i]
 			s.name = cstring(p[0:16])
@@ -233,12 +233,12 @@ func unpackcmd(p []byte, m *LdMachoObj, c *LdMachoCmd, type_ uint, sz uint) int
 		c.seg.initprot = e4(p[60:])
 		c.seg.nsect = e4(p[64:])
 		c.seg.flags = e4(p[68:])
-		c.seg.sect = make([]LdMachoSect, c.seg.nsect)
+		c.seg.sect = make([]ldMachoSect, c.seg.nsect)
 		if uint32(sz) < 72+c.seg.nsect*80 {
 			return -1
 		}
 		p = p[72:]
-		var s *LdMachoSect
+		var s *ldMachoSect
 		for i := 0; uint32(i) < c.seg.nsect; i++ {
 			s = &c.seg.sect[i]
 			s.name = cstring(p[0:16])
@@ -293,11 +293,11 @@ func unpackcmd(p []byte, m *LdMachoObj, c *LdMachoCmd, type_ uint, sz uint) int
 	return 0
 }
 
-func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int {
+func macholoadrel(m *ldMachoObj, sect *ldMachoSect) int {
 	if sect.rel != nil || sect.nreloc == 0 {
 		return 0
 	}
-	rel := make([]LdMachoRel, sect.nreloc)
+	rel := make([]ldMachoRel, sect.nreloc)
 	n := int(sect.nreloc * 8)
 	buf := make([]byte, n)
 	if m.f.Seek(m.base+int64(sect.reloff), 0) < 0 {
@@ -307,7 +307,7 @@ func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int {
 		return -1
 	}
 	var p []byte
-	var r *LdMachoRel
+	var r *ldMachoRel
 	var v uint32
 	for i := 0; uint32(i) < sect.nreloc; i++ {
 		r = &rel[i]
@@ -345,7 +345,7 @@ func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int {
 	return 0
 }
 
-func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int {
+func macholoaddsym(m *ldMachoObj, d *ldMachoDysymtab) int {
 	n := int(d.nindirectsyms)
 
 	p := make([]byte, n*4)
@@ -363,7 +363,7 @@ func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int {
 	return 0
 }
 
-func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int {
+func macholoadsym(m *ldMachoObj, symtab *ldMachoSymtab) int {
 	if symtab.sym != nil {
 		return 0
 	}
@@ -388,9 +388,9 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int {
 	if _, err := io.ReadFull(m.f, symbuf); err != nil {
 		return -1
 	}
-	sym := make([]LdMachoSym, symtab.nsym)
+	sym := make([]ldMachoSym, symtab.nsym)
 	p := symbuf
-	var s *LdMachoSym
+	var s *ldMachoSym
 	var v uint32
 	for i := 0; uint32(i) < symtab.nsym; i++ {
 		s = &sym[i]
@@ -415,7 +415,7 @@ func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int {
 	return 0
 }
 
-func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
+func ldmacho(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
 	var err error
 	var j int
 	var is64 bool
@@ -428,23 +428,23 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 	var ty uint32
 	var sz uint32
 	var off uint32
-	var m *LdMachoObj
+	var m *ldMachoObj
 	var e binary.ByteOrder
-	var sect *LdMachoSect
-	var rel *LdMachoRel
+	var sect *ldMachoSect
+	var rel *ldMachoRel
 	var rpi int
-	var s *LSym
-	var s1 *LSym
-	var outer *LSym
-	var c *LdMachoCmd
-	var symtab *LdMachoSymtab
-	var dsymtab *LdMachoDysymtab
-	var sym *LdMachoSym
+	var s *Symbol
+	var s1 *Symbol
+	var outer *Symbol
+	var c *ldMachoCmd
+	var symtab *ldMachoSymtab
+	var dsymtab *ldMachoDysymtab
+	var sym *ldMachoSym
 	var r []Reloc
 	var rp *Reloc
 	var name string
 
-	Ctxt.IncVersion()
+	localSymVersion := ctxt.Syms.IncVersion()
 	base := f.Offset()
 	if _, err := io.ReadFull(f, hdr[:]); err != nil {
 		goto bad
@@ -471,7 +471,7 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 		f.Seek(4, 1) // skip reserved word in header
 	}
 
-	m = new(LdMachoObj)
+	m = new(ldMachoObj)
 
 	m.f = f
 	m.e = e
@@ -487,23 +487,23 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 
 	switch SysArch.Family {
 	default:
-		Diag("%s: mach-o %s unimplemented", pn, SysArch.Name)
+		Errorf(nil, "%s: mach-o %s unimplemented", pn, SysArch.Name)
 		return
 
 	case sys.AMD64:
 		if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 {
-			Diag("%s: mach-o object but not amd64", pn)
+			Errorf(nil, "%s: mach-o object but not amd64", pn)
 			return
 		}
 
 	case sys.I386:
 		if e != binary.LittleEndian || m.cputype != LdMachoCpu386 {
-			Diag("%s: mach-o object but not 386", pn)
+			Errorf(nil, "%s: mach-o object but not 386", pn)
 			return
 		}
 	}
 
-	m.cmd = make([]LdMachoCmd, ncmd)
+	m.cmd = make([]ldMachoCmd, ncmd)
 	off = uint32(len(hdr))
 	cmdp = make([]byte, cmdsz)
 	if _, err2 := io.ReadFull(f, cmdp); err2 != nil {
@@ -587,7 +587,7 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 			continue
 		}
 		name = fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name)
-		s = Linklookup(Ctxt, name, Ctxt.Version)
+		s = ctxt.Syms.Lookup(name, localSymVersion)
 		if s.Type != 0 {
 			err = fmt.Errorf("duplicate %s/%s", sect.segname, sect.name)
 			goto bad
@@ -634,9 +634,9 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 		}
 		v := 0
 		if sym.type_&N_EXT == 0 {
-			v = Ctxt.Version
+			v = localSymVersion
 		}
-		s = Linklookup(Ctxt, name, v)
+		s = ctxt.Syms.Lookup(name, v)
 		if sym.type_&N_EXT == 0 {
 			s.Attr |= AttrDuplicateOK
 		}
@@ -673,7 +673,7 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 		}
 		if outer.Type == obj.STEXT {
 			if s.Attr.External() && !s.Attr.DuplicateOK() {
-				Diag("%s: duplicate definition of %s", pn, s.Name)
+				Errorf(s, "%s: duplicate symbol definition", pn)
 			}
 			s.Attr |= AttrExternal
 		}
@@ -690,7 +690,7 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 			continue
 		}
 		if s.Sub != nil {
-			s.Sub = listsort(s.Sub, valuecmp, listsubp)
+			s.Sub = listsort(s.Sub)
 
 			// assign sizes, now that we know symbols in sorted order.
 			for s1 = s.Sub; s1 != nil; s1 = s1.Sub {
@@ -707,13 +707,13 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 				log.Fatalf("symbol %s listed multiple times", s.Name)
 			}
 			s.Attr |= AttrOnList
-			Ctxt.Textp = append(Ctxt.Textp, s)
+			ctxt.Textp = append(ctxt.Textp, s)
 			for s1 = s.Sub; s1 != nil; s1 = s1.Sub {
 				if s1.Attr.OnList() {
 					log.Fatalf("symbol %s listed multiple times", s1.Name)
 				}
 				s1.Attr |= AttrOnList
-				Ctxt.Textp = append(Ctxt.Textp, s1)
+				ctxt.Textp = append(ctxt.Textp, s1)
 			}
 		}
 	}
@@ -738,7 +738,7 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 			if rel.scattered != 0 {
 				if SysArch.Family != sys.I386 {
 					// mach-o only uses scattered relocation on 32-bit platforms
-					Diag("unexpected scattered relocation")
+					Errorf(s, "unexpected scattered relocation")
 					continue
 				}
 
@@ -778,7 +778,7 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 
 				// now consider the desired symbol.
 				// find the section where it lives.
-				var ks *LdMachoSect
+				var ks *ldMachoSect
 				for k := 0; uint32(k) < c.seg.nsect; k++ {
 					ks = &c.seg.sect[k]
 					if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size {
@@ -828,7 +828,7 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 			}
 
 			rp.Siz = rel.length
-			rp.Type = 512 + (int32(rel.type_) << 1) + int32(rel.pcrel)
+			rp.Type = 512 + (obj.RelocType(rel.type_) << 1) + obj.RelocType(rel.pcrel)
 			rp.Off = int32(rel.addr)
 
 			// Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0).
@@ -900,5 +900,5 @@ func ldmacho(f *bio.Reader, pkg string, length int64, pn string) {
 	return
 
 bad:
-	Diag("%s: malformed mach-o file: %v", pn, err)
+	Errorf(nil, "%s: malformed mach-o file: %v", pn, err)
 }
diff --git a/src/cmd/link/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go
index 7eb26bc..f9c49d0 100644
--- a/src/cmd/link/internal/ld/ldpe.go
+++ b/src/cmd/link/internal/ld/ldpe.go
@@ -8,12 +8,12 @@ import (
 	"cmd/internal/bio"
 	"cmd/internal/obj"
 	"cmd/internal/sys"
-	"encoding/binary"
+	"debug/pe"
+	"errors"
 	"fmt"
 	"io"
 	"log"
 	"sort"
-	"strconv"
 	"strings"
 )
 
@@ -100,155 +100,77 @@ const (
 	IMAGE_REL_AMD64_SSPAN32          = 0x0010
 )
 
-type PeSym struct {
-	name    string
-	value   uint32
-	sectnum uint16
-	type_   uint16
-	sclass  uint8
-	aux     uint8
-	sym     *LSym
-}
-
-type PeSect struct {
-	name string
-	base []byte
-	size uint64
-	sym  *LSym
-	sh   IMAGE_SECTION_HEADER
-}
-
-type PeObj struct {
-	f      *bio.Reader
-	name   string
-	base   uint32
-	sect   []PeSect
-	nsect  uint
-	pesym  []PeSym
-	npesym uint
-	fh     IMAGE_FILE_HEADER
-	snames []byte
-}
-
-func ldpe(f *bio.Reader, pkg string, length int64, pn string) {
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn)
-	}
+// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating peBiobuf
 
-	var sect *PeSect
-	Ctxt.IncVersion()
-	base := f.Offset()
+// peBiobuf makes bio.Reader look like io.ReaderAt.
+type peBiobuf bio.Reader
 
-	peobj := new(PeObj)
-	peobj.f = f
-	peobj.base = uint32(base)
-	peobj.name = pn
-
-	// read header
-	var err error
-	var j int
-	var l uint32
-	var name string
-	var numaux int
-	var r []Reloc
-	var rp *Reloc
-	var rsect *PeSect
-	var s *LSym
-	var sym *PeSym
-	var symbuf [18]uint8
-	if err = binary.Read(f, binary.LittleEndian, &peobj.fh); err != nil {
-		goto bad
+func (f *peBiobuf) ReadAt(p []byte, off int64) (int, error) {
+	ret := ((*bio.Reader)(f)).Seek(off, 0)
+	if ret < 0 {
+		return 0, errors.New("fail to seek")
 	}
-
-	// load section list
-	peobj.sect = make([]PeSect, peobj.fh.NumberOfSections)
-
-	peobj.nsect = uint(peobj.fh.NumberOfSections)
-	for i := 0; i < int(peobj.fh.NumberOfSections); i++ {
-		if err = binary.Read(f, binary.LittleEndian, &peobj.sect[i].sh); err != nil {
-			goto bad
-		}
-		peobj.sect[i].size = uint64(peobj.sect[i].sh.SizeOfRawData)
-		peobj.sect[i].name = cstring(peobj.sect[i].sh.Name[:])
+	n, err := f.Read(p)
+	if err != nil {
+		return 0, err
 	}
+	return n, nil
+}
 
-	// TODO return error if found .cormeta
-
-	// load string table
-	f.Seek(base+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0)
-
-	if _, err := io.ReadFull(f, symbuf[:4]); err != nil {
-		goto bad
-	}
-	l = Le32(symbuf[:])
-	peobj.snames = make([]byte, l)
-	f.Seek(base+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0)
-	if _, err := io.ReadFull(f, peobj.snames); err != nil {
-		goto bad
+func ldpe(ctxt *Link, input *bio.Reader, pkg string, length int64, pn string) {
+	err := ldpeError(ctxt, input, pkg, length, pn)
+	if err != nil {
+		Errorf(nil, "%s: malformed pe file: %v", pn, err)
 	}
+}
 
-	// rewrite section names if they start with /
-	for i := 0; i < int(peobj.fh.NumberOfSections); i++ {
-		if peobj.sect[i].name == "" {
-			continue
-		}
-		if peobj.sect[i].name[0] != '/' {
-			continue
-		}
-		n, _ := strconv.Atoi(peobj.sect[i].name[1:])
-		peobj.sect[i].name = cstring(peobj.snames[n:])
+func ldpeError(ctxt *Link, input *bio.Reader, pkg string, length int64, pn string) error {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f ldpe %s\n", obj.Cputime(), pn)
 	}
 
-	// read symbols
-	peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols)
+	localSymVersion := ctxt.Syms.IncVersion()
 
-	peobj.npesym = uint(peobj.fh.NumberOfSymbols)
-	f.Seek(base+int64(peobj.fh.PointerToSymbolTable), 0)
-	for i := 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 {
-		f.Seek(base+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0)
-		if _, err := io.ReadFull(f, symbuf[:]); err != nil {
-			goto bad
-		}
+	sectsyms := make(map[*pe.Section]*Symbol)
+	sectdata := make(map[*pe.Section][]byte)
 
-		if (symbuf[0] == 0) && (symbuf[1] == 0) && (symbuf[2] == 0) && (symbuf[3] == 0) {
-			l = Le32(symbuf[4:])
-			peobj.pesym[i].name = cstring(peobj.snames[l:]) // sym name length <= 8
-		} else {
-			peobj.pesym[i].name = cstring(symbuf[:8])
-		}
+	// Some input files are archives containing multiple of
+	// object files, and pe.NewFile seeks to the start of
+	// input file and get confused. Create section reader
+	// to stop pe.NewFile looking before current position.
+	sr := io.NewSectionReader((*peBiobuf)(input), input.Offset(), 1<<63-1)
 
-		peobj.pesym[i].value = Le32(symbuf[8:])
-		peobj.pesym[i].sectnum = Le16(symbuf[12:])
-		peobj.pesym[i].sclass = symbuf[16]
-		peobj.pesym[i].aux = symbuf[17]
-		peobj.pesym[i].type_ = Le16(symbuf[14:])
-		numaux = int(peobj.pesym[i].aux)
-		if numaux < 0 {
-			numaux = 0
-		}
+	// TODO: replace pe.NewFile with pe.Load (grep for "add Load function" in debug/pe for details)
+	f, err := pe.NewFile(sr)
+	if err != nil {
+		return err
 	}
+	defer f.Close()
+
+	// TODO return error if found .cormeta
 
 	// create symbols for mapped sections
-	for i := 0; uint(i) < peobj.nsect; i++ {
-		sect = &peobj.sect[i]
-		if sect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+	for _, sect := range f.Sections {
+		if sect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
 			continue
 		}
 
-		if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
+		if sect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
 			// This has been seen for .idata sections, which we
 			// want to ignore. See issues 5106 and 5273.
 			continue
 		}
 
-		if pemap(peobj, sect) < 0 {
-			goto bad
+		data, err := sect.Data()
+		if err != nil {
+			return err
 		}
+		sectdata[sect] = data
 
-		name = fmt.Sprintf("%s(%s)", pkg, sect.name)
-		s = Linklookup(Ctxt, name, Ctxt.Version)
+		name := fmt.Sprintf("%s(%s)", pkg, sect.Name)
+		s := ctxt.Syms.Lookup(name, localSymVersion)
 
-		switch sect.sh.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
+		switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
 		case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
 			s.Type = obj.SRODATA
 
@@ -262,58 +184,59 @@ func ldpe(f *bio.Reader, pkg string, length int64, pn string) {
 			s.Type = obj.STEXT
 
 		default:
-			err = fmt.Errorf("unexpected flags %#06x for PE section %s", sect.sh.Characteristics, sect.name)
-			goto bad
+			return fmt.Errorf("unexpected flags %#06x for PE section %s", sect.Characteristics, sect.Name)
 		}
 
-		s.P = sect.base
-		s.P = s.P[:sect.size]
-		s.Size = int64(sect.size)
-		sect.sym = s
-		if sect.name == ".rsrc" {
-			setpersrc(sect.sym)
+		s.P = data
+		s.Size = int64(len(data))
+		sectsyms[sect] = s
+		if sect.Name == ".rsrc" {
+			setpersrc(ctxt, s)
 		}
 	}
 
 	// load relocations
-	for i := 0; uint(i) < peobj.nsect; i++ {
-		rsect = &peobj.sect[i]
-		if rsect.sym == nil || rsect.sh.NumberOfRelocations == 0 {
+	for _, rsect := range f.Sections {
+		if _, found := sectsyms[rsect]; !found {
+			continue
+		}
+		if rsect.NumberOfRelocations == 0 {
 			continue
 		}
-		if rsect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
+		if rsect.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 {
 			continue
 		}
-		if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
+		if rsect.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 {
 			// This has been seen for .idata sections, which we
 			// want to ignore. See issues 5106 and 5273.
 			continue
 		}
 
-		r = make([]Reloc, rsect.sh.NumberOfRelocations)
-		f.Seek(int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0)
-		for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ {
-			rp = &r[j]
-			if _, err := io.ReadFull(f, symbuf[:10]); err != nil {
-				goto bad
+		rs := make([]Reloc, rsect.NumberOfRelocations)
+		for j, r := range rsect.Relocs {
+			rp := &rs[j]
+			if int(r.SymbolTableIndex) >= len(f.COFFSymbols) {
+				return fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
 			}
-			rva := Le32(symbuf[0:])
-			symindex := Le32(symbuf[4:])
-			type_ := Le16(symbuf[8:])
-			if err = readpesym(peobj, int(symindex), &sym); err != nil {
-				goto bad
+			pesym := &f.COFFSymbols[r.SymbolTableIndex]
+			gosym, err := readpesym(ctxt, f, pesym, sectsyms, localSymVersion)
+			if err != nil {
+				return err
 			}
-			if sym.sym == nil {
-				err = fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", sym.name, symindex, sym.type_)
-				goto bad
+			if gosym == nil {
+				name, err := pesym.FullName(f.StringTable)
+				if err != nil {
+					name = string(pesym.Name[:])
+				}
+				return fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", name, r.SymbolTableIndex, pesym.Type)
 			}
 
-			rp.Sym = sym.sym
+			rp.Sym = gosym
 			rp.Siz = 4
-			rp.Off = int32(rva)
-			switch type_ {
+			rp.Off = int32(r.VirtualAddress)
+			switch r.Type {
 			default:
-				Diag("%s: unknown relocation type %d;", pn, type_)
+				Errorf(sectsyms[rsect], "%s: unknown relocation type %d;", pn, r.Type)
 				fallthrough
 
 			case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
@@ -321,13 +244,13 @@ func ldpe(f *bio.Reader, pkg string, length int64, pn string) {
 				IMAGE_REL_AMD64_ADDR32NB:
 				rp.Type = obj.R_PCREL
 
-				rp.Add = int64(int32(Le32(rsect.base[rp.Off:])))
+				rp.Add = int64(int32(Le32(sectdata[rsect][rp.Off:])))
 
 			case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32:
 				rp.Type = obj.R_ADDR
 
 				// load addend from image
-				rp.Add = int64(int32(Le32(rsect.base[rp.Off:])))
+				rp.Add = int64(int32(Le32(sectdata[rsect][rp.Off:])))
 
 			case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
 				rp.Siz = 8
@@ -335,86 +258,99 @@ func ldpe(f *bio.Reader, pkg string, length int64, pn string) {
 				rp.Type = obj.R_ADDR
 
 				// load addend from image
-				rp.Add = int64(Le64(rsect.base[rp.Off:]))
+				rp.Add = int64(Le64(sectdata[rsect][rp.Off:]))
 			}
 
 			// ld -r could generate multiple section symbols for the
 			// same section but with different values, we have to take
 			// that into account
-			if issect(&peobj.pesym[symindex]) {
-				rp.Add += int64(peobj.pesym[symindex].value)
+			if issect(pesym) {
+				rp.Add += int64(pesym.Value)
 			}
 		}
 
-		sort.Sort(rbyoff(r[:rsect.sh.NumberOfRelocations]))
+		sort.Sort(rbyoff(rs[:rsect.NumberOfRelocations]))
 
-		s = rsect.sym
-		s.R = r
-		s.R = s.R[:rsect.sh.NumberOfRelocations]
+		s := sectsyms[rsect]
+		s.R = rs
+		s.R = s.R[:rsect.NumberOfRelocations]
 	}
 
 	// enter sub-symbols into symbol table.
-	for i := 0; uint(i) < peobj.npesym; i++ {
-		if peobj.pesym[i].name == "" {
+	for i, numaux := 0, 0; i < len(f.COFFSymbols); i += numaux + 1 {
+		pesym := &f.COFFSymbols[i]
+
+		numaux = int(pesym.NumberOfAuxSymbols)
+
+		name, err := pesym.FullName(f.StringTable)
+		if err != nil {
+			return err
+		}
+		if name == "" {
+			continue
+		}
+		if issect(pesym) {
 			continue
 		}
-		if issect(&peobj.pesym[i]) {
+		if int(pesym.SectionNumber) > len(f.Sections) {
 			continue
 		}
-		if uint(peobj.pesym[i].sectnum) > peobj.nsect {
+		if pesym.SectionNumber == IMAGE_SYM_DEBUG {
 			continue
 		}
-		if peobj.pesym[i].sectnum > 0 {
-			sect = &peobj.sect[peobj.pesym[i].sectnum-1]
-			if sect.sym == nil {
+		var sect *pe.Section
+		if pesym.SectionNumber > 0 {
+			sect = f.Sections[pesym.SectionNumber-1]
+			if _, found := sectsyms[sect]; !found {
 				continue
 			}
 		}
 
-		if err = readpesym(peobj, i, &sym); err != nil {
-			goto bad
+		s, err := readpesym(ctxt, f, pesym, sectsyms, localSymVersion)
+		if err != nil {
+			return err
 		}
 
-		s = sym.sym
-		if sym.sectnum == 0 { // extern
+		if pesym.SectionNumber == 0 { // extern
 			if s.Type == obj.SDYNIMPORT {
 				s.Plt = -2 // flag for dynimport in PE object files.
 			}
-			if s.Type == obj.SXREF && sym.value > 0 { // global data
+			if s.Type == obj.SXREF && pesym.Value > 0 { // global data
 				s.Type = obj.SNOPTRDATA
-				s.Size = int64(sym.value)
+				s.Size = int64(pesym.Value)
 			}
 
 			continue
-		} else if sym.sectnum > 0 && uint(sym.sectnum) <= peobj.nsect {
-			sect = &peobj.sect[sym.sectnum-1]
-			if sect.sym == nil {
-				Diag("%s: %s sym == 0!", pn, s.Name)
+		} else if pesym.SectionNumber > 0 && int(pesym.SectionNumber) <= len(f.Sections) {
+			sect = f.Sections[pesym.SectionNumber-1]
+			if _, found := sectsyms[sect]; !found {
+				Errorf(s, "%s: missing sect.sym", pn)
 			}
 		} else {
-			Diag("%s: %s sectnum < 0!", pn, s.Name)
+			Errorf(s, "%s: sectnum < 0!", pn)
 		}
 
 		if sect == nil {
-			return
+			return nil
 		}
 
 		if s.Outer != nil {
 			if s.Attr.DuplicateOK() {
 				continue
 			}
-			Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name)
+			Exitf("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sectsyms[sect].Name)
 		}
 
-		s.Sub = sect.sym.Sub
-		sect.sym.Sub = s
-		s.Type = sect.sym.Type | obj.SSUB
-		s.Value = int64(sym.value)
+		sectsym := sectsyms[sect]
+		s.Sub = sectsym.Sub
+		sectsym.Sub = s
+		s.Type = sectsym.Type | obj.SSUB
+		s.Value = int64(pesym.Value)
 		s.Size = 4
-		s.Outer = sect.sym
-		if sect.sym.Type == obj.STEXT {
+		s.Outer = sectsym
+		if sectsym.Type == obj.STEXT {
 			if s.Attr.External() && !s.Attr.DuplicateOK() {
-				Diag("%s: duplicate definition of %s", pn, s.Name)
+				Errorf(s, "%s: duplicate symbol definition", pn)
 			}
 			s.Attr |= AttrExternal
 		}
@@ -422,73 +358,47 @@ func ldpe(f *bio.Reader, pkg string, length int64, pn string) {
 
 	// Sort outer lists by address, adding to textp.
 	// This keeps textp in increasing address order.
-	for i := 0; uint(i) < peobj.nsect; i++ {
-		s = peobj.sect[i].sym
+	for _, sect := range f.Sections {
+		s := sectsyms[sect]
 		if s == nil {
 			continue
 		}
 		if s.Sub != nil {
-			s.Sub = listsort(s.Sub, valuecmp, listsubp)
+			s.Sub = listsort(s.Sub)
 		}
 		if s.Type == obj.STEXT {
 			if s.Attr.OnList() {
 				log.Fatalf("symbol %s listed multiple times", s.Name)
 			}
 			s.Attr |= AttrOnList
-			Ctxt.Textp = append(Ctxt.Textp, s)
+			ctxt.Textp = append(ctxt.Textp, s)
 			for s = s.Sub; s != nil; s = s.Sub {
 				if s.Attr.OnList() {
 					log.Fatalf("symbol %s listed multiple times", s.Name)
 				}
 				s.Attr |= AttrOnList
-				Ctxt.Textp = append(Ctxt.Textp, s)
+				ctxt.Textp = append(ctxt.Textp, s)
 			}
 		}
 	}
 
-	return
-
-bad:
-	Diag("%s: malformed pe file: %v", pn, err)
-}
-
-func pemap(peobj *PeObj, sect *PeSect) int {
-	if sect.base != nil {
-		return 0
-	}
-
-	sect.base = make([]byte, sect.sh.SizeOfRawData)
-	if sect.sh.PointerToRawData == 0 { // .bss doesn't have data in object file
-		return 0
-	}
-	if peobj.f.Seek(int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 {
-		return -1
-	}
-	if _, err := io.ReadFull(peobj.f, sect.base); err != nil {
-		return -1
-	}
-
-	return 0
+	return nil
 }
 
-func issect(s *PeSym) bool {
-	return s.sclass == IMAGE_SYM_CLASS_STATIC && s.type_ == 0 && s.name[0] == '.'
+func issect(s *pe.COFFSymbol) bool {
+	return s.StorageClass == IMAGE_SYM_CLASS_STATIC && s.Type == 0 && s.Name[0] == '.'
 }
 
-func readpesym(peobj *PeObj, i int, y **PeSym) (err error) {
-	if uint(i) >= peobj.npesym || i < 0 {
-		err = fmt.Errorf("invalid pe symbol index")
-		return err
+func readpesym(ctxt *Link, f *pe.File, sym *pe.COFFSymbol, sectsyms map[*pe.Section]*Symbol, localSymVersion int) (*Symbol, error) {
+	symname, err := sym.FullName(f.StringTable)
+	if err != nil {
+		return nil, err
 	}
-
-	sym := &peobj.pesym[i]
-	*y = sym
-
 	var name string
 	if issect(sym) {
-		name = peobj.sect[sym.sectnum-1].sym.Name
+		name = sectsyms[f.Sections[sym.SectionNumber-1]].Name
 	} else {
-		name = sym.name
+		name = symname
 		if strings.HasPrefix(name, "__imp_") {
 			name = name[6:] // __imp_Name => Name
 		}
@@ -502,34 +412,31 @@ func readpesym(peobj *PeObj, i int, y **PeSym) (err error) {
 		name = name[:i]
 	}
 
-	var s *LSym
-	switch sym.type_ {
+	var s *Symbol
+	switch sym.Type {
 	default:
-		err = fmt.Errorf("%s: invalid symbol type %d", sym.name, sym.type_)
-		return err
+		return nil, fmt.Errorf("%s: invalid symbol type %d", symname, sym.Type)
 
 	case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
-		switch sym.sclass {
+		switch sym.StorageClass {
 		case IMAGE_SYM_CLASS_EXTERNAL: //global
-			s = Linklookup(Ctxt, name, 0)
+			s = ctxt.Syms.Lookup(name, 0)
 
 		case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL:
-			s = Linklookup(Ctxt, name, Ctxt.Version)
+			s = ctxt.Syms.Lookup(name, localSymVersion)
 			s.Attr |= AttrDuplicateOK
 
 		default:
-			err = fmt.Errorf("%s: invalid symbol binding %d", sym.name, sym.sclass)
-			return err
+			return nil, fmt.Errorf("%s: invalid symbol binding %d", symname, sym.StorageClass)
 		}
 	}
 
-	if s != nil && s.Type == 0 && (sym.sclass != IMAGE_SYM_CLASS_STATIC || sym.value != 0) {
+	if s != nil && s.Type == 0 && (sym.StorageClass != IMAGE_SYM_CLASS_STATIC || sym.Value != 0) {
 		s.Type = obj.SXREF
 	}
-	if strings.HasPrefix(sym.name, "__imp_") {
+	if strings.HasPrefix(symname, "__imp_") {
 		s.Got = -2 // flag for __imp_
 	}
-	sym.sym = s
 
-	return nil
+	return s, nil
 }
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 14f4fa9..fb32190 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1,5 +1,5 @@
 // Inferno utils/8l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/8l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -54,7 +54,7 @@ import (
 // Data layout and relocation.
 
 // Derived from Inferno utils/6l/l.h
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -95,42 +95,35 @@ type Arch struct {
 	Openbsddynld     string
 	Dragonflydynld   string
 	Solarisdynld     string
-	Adddynrel        func(*LSym, *Reloc)
-	Archinit         func()
-	Archreloc        func(*Reloc, *LSym, *int64) int
-	Archrelocvariant func(*Reloc, *LSym, int64) int64
-	Asmb             func()
-	Elfreloc1        func(*Reloc, int64) int
-	Elfsetupplt      func()
-	Gentext          func()
-	Machoreloc1      func(*Reloc, int64) int
-	PEreloc1         func(*Reloc, int64) bool
+	Adddynrel        func(*Link, *Symbol, *Reloc) bool
+	Archinit         func(*Link)
+	Archreloc        func(*Link, *Reloc, *Symbol, *int64) int
+	Archrelocvariant func(*Link, *Reloc, *Symbol, int64) int64
+	Trampoline       func(*Link, *Reloc, *Symbol)
+	Asmb             func(*Link)
+	Elfreloc1        func(*Link, *Reloc, int64) int
+	Elfsetupplt      func(*Link)
+	Gentext          func(*Link)
+	Machoreloc1      func(*Symbol, *Reloc, int64) int
+	PEreloc1         func(*Symbol, *Reloc, int64) bool
 	Wput             func(uint16)
 	Lput             func(uint32)
 	Vput             func(uint64)
 	Append16         func(b []byte, v uint16) []byte
 	Append32         func(b []byte, v uint32) []byte
 	Append64         func(b []byte, v uint64) []byte
-}
-
-type Rpath struct {
-	set bool
-	val string
-}
-
-func (r *Rpath) Set(val string) error {
-	r.set = true
-	r.val = val
-	return nil
-}
 
-func (r *Rpath) String() string {
-	return r.val
+	// TLSIEtoLE converts a TLS Initial Executable relocation to
+	// a TLS Local Executable relocation.
+	//
+	// This is possible when a TLS IE relocation refers to a local
+	// symbol in an executable, which is typical when internally
+	// linking PIE binaries.
+	TLSIEtoLE func(s *Symbol, off, size int)
 }
 
 var (
 	Thearch Arch
-	Debug   [128]int
 	Lcsize  int32
 	rpath   Rpath
 	Spsize  int32
@@ -171,66 +164,52 @@ type Section struct {
 
 // DynlinkingGo returns whether we are producing Go code that can live
 // in separate shared libraries linked together at runtime.
-func DynlinkingGo() bool {
-	return Buildmode == BuildmodeShared || Linkshared
+func (ctxt *Link) DynlinkingGo() bool {
+	if !ctxt.Loaded {
+		panic("DynlinkingGo called before all symbols loaded")
+	}
+	canUsePlugins := ctxt.Syms.ROLookup("plugin.Open", 0) != nil
+	return Buildmode == BuildmodeShared || *FlagLinkshared || Buildmode == BuildmodePlugin || canUsePlugins
 }
 
 // UseRelro returns whether to make use of "read only relocations" aka
 // relro.
 func UseRelro() bool {
 	switch Buildmode {
-	case BuildmodeCShared, BuildmodeShared, BuildmodePIE:
+	case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared, BuildmodePIE, BuildmodePlugin:
 		return Iself
 	default:
-		return Linkshared
+		return *FlagLinkshared
 	}
 }
 
 var (
-	SysArch            *sys.Arch
-	outfile            string
-	dynexp             []*LSym
-	dynlib             []string
-	ldflag             []string
-	havedynamic        int
-	Funcalign          int
-	iscgo              bool
-	elfglobalsymndx    int
-	flag_dumpdep       bool
-	flag_installsuffix string
-	flag_race          int
-	flag_msan          int
-	Buildmode          BuildMode
-	Linkshared         bool
-	tracksym           string
-	interpreter        string
-	tmpdir             string
-	extld              string
-	extldflags         string
-	extar              string
-	libgccfile         string
-	debug_s            int // backup old value of debug['s']
-	Ctxt               *Link
-	HEADR              int32
-	HEADTYPE           int32
-	INITRND            int32
-	INITTEXT           int64
-	INITDAT            int64
-	INITENTRY          string /* entry point */
-	nerrors            int
-	Linkmode           int
-	liveness           int64
+	SysArch         *sys.Arch
+	dynexp          []*Symbol
+	dynlib          []string
+	ldflag          []string
+	havedynamic     int
+	Funcalign       int
+	iscgo           bool
+	elfglobalsymndx int
+	interpreter     string
+
+	debug_s  bool // backup old value of debug['s']
+	HEADR    int32
+	Headtype obj.HeadType
+
+	nerrors  int
+	liveness int64
 )
 
 var (
-	Segtext   Segment
-	Segrodata Segment
-	Segdata   Segment
-	Segdwarf  Segment
+	Segtext      Segment
+	Segrodata    Segment
+	Segrelrodata Segment
+	Segdata      Segment
+	Segdwarf     Segment
 )
 
-/* set by call to mywhatsys() */
-
 /* whence for ldpkg */
 const (
 	FileObj = 0 + iota
@@ -238,12 +217,6 @@ const (
 	Pkgdef
 )
 
-var (
-	headstring string
-	// buffered output
-	Bso *bufio.Writer
-)
-
 // TODO(dfc) outBuf duplicates bio.Writer
 type outBuf struct {
 	w   *bufio.Writer
@@ -263,6 +236,10 @@ func (w *outBuf) WriteString(s string) (n int, err error) {
 	return n, err
 }
 
+func (w *outBuf) Offset() int64 {
+	return w.off
+}
+
 var coutbuf outBuf
 
 const pkgname = "__.PKGDEF"
@@ -271,105 +248,11 @@ var (
 	// Set if we see an object compiled by the host compiler that is not
 	// from a package that is known to support internal linking mode.
 	externalobj = false
-	goroot      string
-	goarch      string
-	goos        string
 	theline     string
 )
 
-func Lflag(arg string) {
-	Ctxt.Libdir = append(Ctxt.Libdir, arg)
-}
-
-// A BuildMode indicates the sort of object we are building:
-//   "exe": build a main package and everything it imports into an executable.
-//   "c-shared": build a main package, plus all packages that it imports, into a
-//     single C shared library. The only callable symbols will be those functions
-//     marked as exported.
-//   "shared": combine all packages passed on the command line, and their
-//     dependencies, into a single shared library that will be used when
-//     building with the -linkshared option.
-type BuildMode uint8
-
-const (
-	BuildmodeUnset BuildMode = iota
-	BuildmodeExe
-	BuildmodePIE
-	BuildmodeCArchive
-	BuildmodeCShared
-	BuildmodeShared
-)
-
-func (mode *BuildMode) Set(s string) error {
-	goos := obj.Getgoos()
-	goarch := obj.Getgoarch()
-	badmode := func() error {
-		return fmt.Errorf("buildmode %s not supported on %s/%s", s, goos, goarch)
-	}
-	switch s {
-	default:
-		return fmt.Errorf("invalid buildmode: %q", s)
-	case "exe":
-		*mode = BuildmodeExe
-	case "pie":
-		switch goos {
-		case "android", "linux":
-		default:
-			return badmode()
-		}
-		*mode = BuildmodePIE
-	case "c-archive":
-		switch goos {
-		case "darwin", "linux":
-		case "windows":
-			switch goarch {
-			case "amd64", "386":
-			default:
-				return badmode()
-			}
-		default:
-			return badmode()
-		}
-		*mode = BuildmodeCArchive
-	case "c-shared":
-		switch goarch {
-		case "386", "amd64", "arm", "arm64":
-		default:
-			return badmode()
-		}
-		*mode = BuildmodeCShared
-	case "shared":
-		switch goos {
-		case "linux":
-			switch goarch {
-			case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
-			default:
-				return badmode()
-			}
-		default:
-			return badmode()
-		}
-		*mode = BuildmodeShared
-	}
-	return nil
-}
-
-func (mode *BuildMode) String() string {
-	switch *mode {
-	case BuildmodeUnset:
-		return "" // avoid showing a default in usage message
-	case BuildmodeExe:
-		return "exe"
-	case BuildmodePIE:
-		return "pie"
-	case BuildmodeCArchive:
-		return "c-archive"
-	case BuildmodeCShared:
-		return "c-shared"
-	case BuildmodeShared:
-		return "shared"
-	}
-	return fmt.Sprintf("BuildMode(%d)", uint8(*mode))
+func Lflag(ctxt *Link, arg string) {
+	ctxt.Libdir = append(ctxt.Libdir, arg)
 }
 
 /*
@@ -379,67 +262,53 @@ func (mode *BuildMode) String() string {
  * S_ISREG() does not exist on Plan 9.
  */
 func mayberemoveoutfile() {
-	if fi, err := os.Lstat(outfile); err == nil && !fi.Mode().IsRegular() {
+	if fi, err := os.Lstat(*flagOutfile); err == nil && !fi.Mode().IsRegular() {
 		return
 	}
-	os.Remove(outfile)
+	os.Remove(*flagOutfile)
 }
 
-func libinit() {
+func libinit(ctxt *Link) {
 	Funcalign = Thearch.Funcalign
-	mywhatsys() // get goroot, goarch, goos
 
 	// add goroot to the end of the libdir list.
 	suffix := ""
 
 	suffixsep := ""
-	if flag_installsuffix != "" {
+	if *flagInstallSuffix != "" {
 		suffixsep = "_"
-		suffix = flag_installsuffix
-	} else if flag_race != 0 {
+		suffix = *flagInstallSuffix
+	} else if *flagRace {
 		suffixsep = "_"
 		suffix = "race"
-	} else if flag_msan != 0 {
+	} else if *flagMsan {
 		suffixsep = "_"
 		suffix = "msan"
 	}
 
-	Lflag(filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s%s%s", goos, goarch, suffixsep, suffix)))
+	Lflag(ctxt, filepath.Join(obj.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", obj.GOOS, obj.GOARCH, suffixsep, suffix)))
 
 	mayberemoveoutfile()
-	f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775)
+	f, err := os.OpenFile(*flagOutfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775)
 	if err != nil {
-		Exitf("cannot create %s: %v", outfile, err)
+		Exitf("cannot create %s: %v", *flagOutfile, err)
 	}
 
 	coutbuf.w = bufio.NewWriter(f)
 	coutbuf.f = f
 
-	if INITENTRY == "" {
+	if *flagEntrySymbol == "" {
 		switch Buildmode {
 		case BuildmodeCShared, BuildmodeCArchive:
-			INITENTRY = fmt.Sprintf("_rt0_%s_%s_lib", goarch, goos)
+			*flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s_lib", obj.GOARCH, obj.GOOS)
 		case BuildmodeExe, BuildmodePIE:
-			INITENTRY = fmt.Sprintf("_rt0_%s_%s", goarch, goos)
-		case BuildmodeShared:
-			// No INITENTRY for -buildmode=shared
+			*flagEntrySymbol = fmt.Sprintf("_rt0_%s_%s", obj.GOARCH, obj.GOOS)
+		case BuildmodeShared, BuildmodePlugin:
+			// No *flagEntrySymbol for -buildmode=shared and plugin
 		default:
-			Diag("unknown INITENTRY for buildmode %v", Buildmode)
+			Errorf(nil, "unknown *flagEntrySymbol for buildmode %v", Buildmode)
 		}
 	}
-
-	if !DynlinkingGo() {
-		Linklookup(Ctxt, INITENTRY, 0).Type = obj.SXREF
-	}
-}
-
-func Exitf(format string, a ...interface{}) {
-	fmt.Fprintf(os.Stderr, os.Args[0]+": "+format+"\n", a...)
-	if coutbuf.f != nil {
-		coutbuf.f.Close()
-		mayberemoveoutfile()
-	}
-	Exit(2)
 }
 
 func errorexit() {
@@ -463,117 +332,106 @@ func errorexit() {
 	Exit(0)
 }
 
-func loadinternal(name string) {
-	found := 0
-	for i := 0; i < len(Ctxt.Libdir); i++ {
-		if Linkshared {
-			shlibname := filepath.Join(Ctxt.Libdir[i], name+".shlibname")
-			if Debug['v'] != 0 {
-				fmt.Fprintf(Bso, "searching for %s.a in %s\n", name, shlibname)
+func loadinternal(ctxt *Link, name string) *Library {
+	for i := 0; i < len(ctxt.Libdir); i++ {
+		if *FlagLinkshared {
+			shlibname := filepath.Join(ctxt.Libdir[i], name+".shlibname")
+			if ctxt.Debugvlog != 0 {
+				ctxt.Logf("searching for %s.a in %s\n", name, shlibname)
 			}
 			if _, err := os.Stat(shlibname); err == nil {
-				addlibpath(Ctxt, "internal", "internal", "", name, shlibname)
-				found = 1
-				break
+				return addlibpath(ctxt, "internal", "internal", "", name, shlibname)
 			}
 		}
-		pname := filepath.Join(Ctxt.Libdir[i], name+".a")
-		if Debug['v'] != 0 {
-			fmt.Fprintf(Bso, "searching for %s.a in %s\n", name, pname)
+		pname := filepath.Join(ctxt.Libdir[i], name+".a")
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("searching for %s.a in %s\n", name, pname)
 		}
 		if _, err := os.Stat(pname); err == nil {
-			addlibpath(Ctxt, "internal", "internal", pname, name, "")
-			found = 1
-			break
+			return addlibpath(ctxt, "internal", "internal", pname, name, "")
 		}
 	}
 
-	if found == 0 {
-		fmt.Fprintf(Bso, "warning: unable to find %s.a\n", name)
+	ctxt.Logf("warning: unable to find %s.a\n", name)
+	return nil
+}
+
+// findLibPathCmd uses cmd command to find gcc library libname.
+// It returns library full path if found, or "none" if not found.
+func (ctxt *Link) findLibPathCmd(cmd, libname string) string {
+	if *flagExtld == "" {
+		*flagExtld = "gcc"
+	}
+	args := hostlinkArchArgs()
+	args = append(args, cmd)
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%s %v\n", *flagExtld, args)
+	}
+	out, err := exec.Command(*flagExtld, args...).Output()
+	if err != nil {
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("not using a %s file because compiler failed\n%v\n%s\n", libname, err, out)
+		}
+		return "none"
 	}
+	return strings.TrimSpace(string(out))
+}
+
+// findLibPath searches for library libname.
+// It returns library full path if found, or "none" if not found.
+func (ctxt *Link) findLibPath(libname string) string {
+	return ctxt.findLibPathCmd("--print-file-name="+libname, libname)
 }
 
-func loadlib() {
+func (ctxt *Link) loadlib() {
 	switch Buildmode {
-	case BuildmodeCShared:
-		s := Linklookup(Ctxt, "runtime.islibrary", 0)
+	case BuildmodeCShared, BuildmodePlugin:
+		s := ctxt.Syms.Lookup("runtime.islibrary", 0)
 		s.Attr |= AttrDuplicateOK
-		Adduint8(Ctxt, s, 1)
+		Adduint8(ctxt, s, 1)
 	case BuildmodeCArchive:
-		s := Linklookup(Ctxt, "runtime.isarchive", 0)
+		s := ctxt.Syms.Lookup("runtime.isarchive", 0)
 		s.Attr |= AttrDuplicateOK
-		Adduint8(Ctxt, s, 1)
+		Adduint8(ctxt, s, 1)
 	}
 
-	loadinternal("runtime")
+	loadinternal(ctxt, "runtime")
 	if SysArch.Family == sys.ARM {
-		loadinternal("math")
+		loadinternal(ctxt, "math")
 	}
-	if flag_race != 0 {
-		loadinternal("runtime/race")
+	if *flagRace {
+		loadinternal(ctxt, "runtime/race")
 	}
-	if flag_msan != 0 {
-		loadinternal("runtime/msan")
+	if *flagMsan {
+		loadinternal(ctxt, "runtime/msan")
 	}
 
 	var i int
-	for i = 0; i < len(Ctxt.Library); i++ {
-		iscgo = iscgo || Ctxt.Library[i].Pkg == "runtime/cgo"
-		if Ctxt.Library[i].Shlib == "" {
-			if Debug['v'] > 1 {
-				fmt.Fprintf(Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].File, Ctxt.Library[i].Objref)
+	for i = 0; i < len(ctxt.Library); i++ {
+		iscgo = iscgo || ctxt.Library[i].Pkg == "runtime/cgo"
+		if ctxt.Library[i].Shlib == "" {
+			if ctxt.Debugvlog > 1 {
+				ctxt.Logf("%5.2f autolib: %s (from %s)\n", obj.Cputime(), ctxt.Library[i].File, ctxt.Library[i].Objref)
 			}
-			objfile(Ctxt.Library[i])
+			objfile(ctxt, ctxt.Library[i])
 		}
 	}
 
-	for i = 0; i < len(Ctxt.Library); i++ {
-		if Ctxt.Library[i].Shlib != "" {
-			if Debug['v'] > 1 {
-				fmt.Fprintf(Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].Shlib, Ctxt.Library[i].Objref)
+	for i = 0; i < len(ctxt.Library); i++ {
+		if ctxt.Library[i].Shlib != "" {
+			if ctxt.Debugvlog > 1 {
+				ctxt.Logf("%5.2f autolib: %s (from %s)\n", obj.Cputime(), ctxt.Library[i].Shlib, ctxt.Library[i].Objref)
 			}
-			ldshlibsyms(Ctxt.Library[i].Shlib)
+			ldshlibsyms(ctxt, ctxt.Library[i].Shlib)
 		}
 	}
 
-	if Linkmode == LinkAuto {
-		if iscgo && externalobj {
-			Linkmode = LinkExternal
-		} else {
-			Linkmode = LinkInternal
-		}
-
-		// Force external linking for android.
-		if goos == "android" {
-			Linkmode = LinkExternal
-		}
-
-		// Force external linking for PIE executables, as
-		// internal linking does not support TLS_IE.
-		if Buildmode == BuildmodePIE {
-			Linkmode = LinkExternal
-		}
-
-		// cgo on Darwin must use external linking
-		// we can always use external linking, but then there will be circular
-		// dependency problems when compiling natively (external linking requires
-		// runtime/cgo, runtime/cgo requires cmd/cgo, but cmd/cgo needs to be
-		// compiled using external linking.)
-		if SysArch.InFamily(sys.ARM, sys.ARM64) && HEADTYPE == obj.Hdarwin && iscgo {
-			Linkmode = LinkExternal
-		}
-
-		// Force external linking for msan.
-		if flag_msan != 0 {
-			Linkmode = LinkExternal
-		}
-	}
+	// We now have enough information to determine the link mode.
+	determineLinkMode(ctxt)
 
-	// cmd/7l doesn't support cgo internal linking
-	// This is https://golang.org/issue/10373.
-	// mips64x doesn't support cgo internal linking either (golang.org/issue/14449)
-	if iscgo && (goarch == "arm64" || goarch == "mips64" || goarch == "mips64le") {
-		Linkmode = LinkExternal
+	if Linkmode == LinkExternal && SysArch.Family == sys.PPC64 {
+		toc := ctxt.Syms.Lookup(".TOC.", 0)
+		toc.Type = obj.SDYNIMPORT
 	}
 
 	if Linkmode == LinkExternal && !iscgo {
@@ -581,16 +439,16 @@ func loadlib() {
 		// The startup code uses an import of runtime/cgo to decide
 		// whether to initialize the TLS.  So give it one. This could
 		// be handled differently but it's an unusual case.
-		loadinternal("runtime/cgo")
+		loadinternal(ctxt, "runtime/cgo")
 
-		if i < len(Ctxt.Library) {
-			if Ctxt.Library[i].Shlib != "" {
-				ldshlibsyms(Ctxt.Library[i].Shlib)
+		if i < len(ctxt.Library) {
+			if ctxt.Library[i].Shlib != "" {
+				ldshlibsyms(ctxt, ctxt.Library[i].Shlib)
 			} else {
-				if DynlinkingGo() {
+				if Buildmode == BuildmodeShared || *FlagLinkshared {
 					Exitf("cannot implicitly include runtime/cgo in a shared library")
 				}
-				objfile(Ctxt.Library[i])
+				objfile(ctxt, ctxt.Library[i])
 			}
 		}
 	}
@@ -598,7 +456,7 @@ func loadlib() {
 	if Linkmode == LinkInternal {
 		// Drop all the cgo_import_static declarations.
 		// Turns out we won't be needing them.
-		for _, s := range Ctxt.Allsym {
+		for _, s := range ctxt.Syms.Allsym {
 			if s.Type == obj.SHOSTOBJ {
 				// If a symbol was marked both
 				// cgo_import_static and cgo_import_dynamic,
@@ -613,7 +471,7 @@ func loadlib() {
 		}
 	}
 
-	tlsg := Linklookup(Ctxt, "runtime.tlsg", 0)
+	tlsg := ctxt.Syms.Lookup("runtime.tlsg", 0)
 
 	// runtime.tlsg is used for external linking on platforms that do not define
 	// a variable to hold g in assembly (currently only intel).
@@ -621,12 +479,18 @@ func loadlib() {
 		tlsg.Type = obj.STLSBSS
 		tlsg.Size = int64(SysArch.PtrSize)
 	} else if tlsg.Type != obj.SDYNIMPORT {
-		Diag("internal error: runtime declared tlsg variable %d", tlsg.Type)
+		Errorf(nil, "runtime declared tlsg variable %v", tlsg.Type)
 	}
 	tlsg.Attr |= AttrReachable
-	Ctxt.Tlsg = tlsg
+	ctxt.Tlsg = tlsg
 
-	moduledata := Linklookup(Ctxt, "runtime.firstmoduledata", 0)
+	var moduledata *Symbol
+	if Buildmode == BuildmodePlugin {
+		moduledata = ctxt.Syms.Lookup("local.pluginmoduledata", 0)
+		moduledata.Attr |= AttrLocal
+	} else {
+		moduledata = ctxt.Syms.Lookup("runtime.firstmoduledata", 0)
+	}
 	if moduledata.Type != 0 && moduledata.Type != obj.SDYNIMPORT {
 		// If the module (toolchain-speak for "executable or shared
 		// library") we are linking contains the runtime package, it
@@ -638,29 +502,29 @@ func loadlib() {
 		// In addition, on ARM, the runtime depends on the linker
 		// recording the value of GOARM.
 		if SysArch.Family == sys.ARM {
-			s := Linklookup(Ctxt, "runtime.goarm", 0)
+			s := ctxt.Syms.Lookup("runtime.goarm", 0)
 			s.Type = obj.SRODATA
 			s.Size = 0
-			Adduint8(Ctxt, s, uint8(Ctxt.Goarm))
+			Adduint8(ctxt, s, uint8(obj.GOARM))
 		}
 
-		if obj.Framepointer_enabled(obj.Getgoos(), obj.Getgoarch()) {
-			s := Linklookup(Ctxt, "runtime.framepointer_enabled", 0)
+		if obj.Framepointer_enabled(obj.GOOS, obj.GOARCH) {
+			s := ctxt.Syms.Lookup("runtime.framepointer_enabled", 0)
 			s.Type = obj.SRODATA
 			s.Size = 0
-			Adduint8(Ctxt, s, 1)
+			Adduint8(ctxt, s, 1)
 		}
 	} else {
 		// If OTOH the module does not contain the runtime package,
 		// create a local symbol for the moduledata.
-		moduledata = Linklookup(Ctxt, "local.moduledata", 0)
+		moduledata = ctxt.Syms.Lookup("local.moduledata", 0)
 		moduledata.Attr |= AttrLocal
 	}
 	// In all cases way we mark the moduledata as noptrdata to hide it from
 	// the GC.
 	moduledata.Type = obj.SNOPTRDATA
 	moduledata.Attr |= AttrReachable
-	Ctxt.Moduledata = moduledata
+	ctxt.Moduledata = moduledata
 
 	// Now that we know the link mode, trim the dynexp list.
 	x := AttrCgoExportDynamic
@@ -679,12 +543,12 @@ func loadlib() {
 
 	// In internal link mode, read the host object files.
 	if Linkmode == LinkInternal {
-		hostobjs()
+		hostobjs(ctxt)
 
 		// If we have any undefined symbols in external
 		// objects, try to read them from the libgcc file.
 		any := false
-		for _, s := range Ctxt.Allsym {
+		for _, s := range ctxt.Syms.Allsym {
 			for _, r := range s.R {
 				if r.Sym != nil && r.Sym.Type&obj.SMASK == obj.SXREF && r.Sym.Name != ".got" {
 					any = true
@@ -693,29 +557,27 @@ func loadlib() {
 			}
 		}
 		if any {
-			if libgccfile == "" {
-				if extld == "" {
-					extld = "gcc"
-				}
-				args := hostlinkArchArgs()
-				args = append(args, "--print-libgcc-file-name")
-				if Debug['v'] != 0 {
-					fmt.Fprintf(Bso, "%s %v\n", extld, args)
+			if *flagLibGCC == "" {
+				*flagLibGCC = ctxt.findLibPathCmd("--print-libgcc-file-name", "libgcc")
+			}
+			if *flagLibGCC != "none" {
+				hostArchive(ctxt, *flagLibGCC)
+			}
+			if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
+				if p := ctxt.findLibPath("libmingwex.a"); p != "none" {
+					hostArchive(ctxt, p)
 				}
-				out, err := exec.Command(extld, args...).Output()
-				if err != nil {
-					if Debug['v'] != 0 {
-						fmt.Fprintln(Bso, "not using a libgcc file because compiler failed")
-						fmt.Fprintf(Bso, "%v\n%s\n", err, out)
-					}
-					libgccfile = "none"
-				} else {
-					libgccfile = strings.TrimSpace(string(out))
+				if p := ctxt.findLibPath("libmingw32.a"); p != "none" {
+					hostArchive(ctxt, p)
 				}
-			}
-
-			if libgccfile != "none" {
-				hostArchive(libgccfile)
+				// TODO: maybe do something similar to peimporteddlls to collect all lib names
+				// and try link them all to final exe just like libmingwex.a and libmingw32.a:
+				/*
+					for:
+					#cgo windows LDFLAGS: -lmsvcrt -lm
+					import:
+					libmsvcrt.a libm.a
+				*/
 			}
 		}
 	} else {
@@ -723,6 +585,8 @@ func loadlib() {
 	}
 
 	// We've loaded all the code now.
+	ctxt.Loaded = true
+
 	// If there are no dynamic libraries needed, gcc disables dynamic linking.
 	// Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13)
 	// assumes that a dynamic binary always refers to at least one dynamic library.
@@ -733,14 +597,53 @@ func loadlib() {
 	// binaries, so leave it enabled on OS X (Mach-O) binaries.
 	// Also leave it enabled on Solaris which doesn't support
 	// statically linked binaries.
-	switch Buildmode {
-	case BuildmodeExe, BuildmodePIE:
-		if havedynamic == 0 && HEADTYPE != obj.Hdarwin && HEADTYPE != obj.Hsolaris {
-			Debug['d'] = 1
+	if Buildmode == BuildmodeExe {
+		if havedynamic == 0 && Headtype != obj.Hdarwin && Headtype != obj.Hsolaris {
+			*FlagD = true
+		}
+	}
+
+	if SysArch == sys.Arch386 {
+		if (Buildmode == BuildmodeCArchive && Iself) || Buildmode == BuildmodeCShared || Buildmode == BuildmodePIE || ctxt.DynlinkingGo() {
+			got := ctxt.Syms.Lookup("_GLOBAL_OFFSET_TABLE_", 0)
+			got.Type = obj.SDYNIMPORT
+			got.Attr |= AttrReachable
 		}
 	}
 
 	importcycles()
+
+	// put symbols into Textp
+	// do it in postorder so that packages are laid down in dependency order
+	// internal first, then everything else
+	ctxt.Library = postorder(ctxt.Library)
+	for _, doInternal := range [2]bool{true, false} {
+		for _, lib := range ctxt.Library {
+			if isRuntimeDepPkg(lib.Pkg) != doInternal {
+				continue
+			}
+			ctxt.Textp = append(ctxt.Textp, lib.textp...)
+			for _, s := range lib.dupTextSyms {
+				if !s.Attr.OnList() {
+					ctxt.Textp = append(ctxt.Textp, s)
+					s.Attr |= AttrOnList
+				}
+			}
+		}
+	}
+
+	if len(ctxt.Shlibs) > 0 {
+		// We might have overwritten some functions above (this tends to happen for the
+		// autogenerated type equality/hashing functions) and we don't want to generated
+		// pcln table entries for these any more so remove them from Textp.
+		textp := make([]*Symbol, 0, len(ctxt.Textp))
+		for _, s := range ctxt.Textp {
+			if s.Type != obj.SDYNIMPORT {
+				textp = append(textp, s)
+			}
+		}
+		ctxt.Textp = textp
+	}
 }
 
 /*
@@ -775,13 +678,12 @@ func nextar(bp *bio.Reader, off int64, a *ArHdr) int64 {
 	return arsize + SAR_HDR
 }
 
-func objfile(lib *Library) {
+func objfile(ctxt *Link, lib *Library) {
 	pkg := pathtoprefix(lib.Pkg)
 
-	if Debug['v'] > 1 {
-		fmt.Fprintf(Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), lib.File, pkg)
+	if ctxt.Debugvlog > 1 {
+		ctxt.Logf("%5.2f ldobj: %s (%s)\n", obj.Cputime(), lib.File, pkg)
 	}
-	Bso.Flush()
 	f, err := bio.Open(lib.File)
 	if err != nil {
 		Exitf("cannot open file %s: %v", lib.File, err)
@@ -796,7 +698,7 @@ func objfile(lib *Library) {
 		l := f.Seek(0, 2)
 
 		f.Seek(0, 0)
-		ldobj(f, pkg, l, lib.File, lib.File, FileObj)
+		ldobj(ctxt, f, lib, l, lib.File, lib.File, FileObj)
 		f.Close()
 
 		return
@@ -809,20 +711,20 @@ func objfile(lib *Library) {
 	l := nextar(f, off, &arhdr)
 	var pname string
 	if l <= 0 {
-		Diag("%s: short read on archive file symbol header", lib.File)
+		Errorf(nil, "%s: short read on archive file symbol header", lib.File)
 		goto out
 	}
 
 	if !strings.HasPrefix(arhdr.name, pkgname) {
-		Diag("%s: cannot find package header", lib.File)
+		Errorf(nil, "%s: cannot find package header", lib.File)
 		goto out
 	}
 
-	if Buildmode == BuildmodeShared {
+	if Buildmode == BuildmodeShared || Buildmode == BuildmodePlugin || ctxt.Syms.ROLookup("plugin.Open", 0) != nil {
 		before := f.Offset()
 		pkgdefBytes := make([]byte, atolwhex(arhdr.size))
 		if _, err := io.ReadFull(f, pkgdefBytes); err != nil {
-			Diag("%s: short read on archive file symbol header: %v", lib.File, err)
+			Errorf(nil, "%s: short read on archive file symbol header: %v", lib.File, err)
 		}
 		hash := sha1.Sum(pkgdefBytes)
 		lib.hash = hash[:]
@@ -831,7 +733,7 @@ func objfile(lib *Library) {
 
 	off += l
 
-	ldpkg(f, pkg, atolwhex(arhdr.size), lib.File, Pkgdef)
+	ldpkg(ctxt, f, pkg, atolwhex(arhdr.size), lib.File, Pkgdef)
 
 	/*
 	 * load all the object files from the archive now.
@@ -858,7 +760,7 @@ func objfile(lib *Library) {
 
 		pname = fmt.Sprintf("%s(%s)", lib.File, arhdr.name)
 		l = atolwhex(arhdr.size)
-		ldobj(f, pkg, l, pname, lib.File, ArchiveObj)
+		ldobj(ctxt, f, lib, l, pname, lib.File, ArchiveObj)
 	}
 
 out:
@@ -866,7 +768,7 @@ out:
 }
 
 type Hostobj struct {
-	ld     func(*bio.Reader, string, int64, string)
+	ld     func(*Link, *bio.Reader, string, int64, string)
 	pkg    string
 	pn     string
 	file   string
@@ -887,7 +789,7 @@ var internalpkg = []string{
 	"runtime/msan",
 }
 
-func ldhostobj(ld func(*bio.Reader, string, int64, string), f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj {
+func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj {
 	isinternal := false
 	for i := 0; i < len(internalpkg); i++ {
 		if pkg == internalpkg[i] {
@@ -902,7 +804,7 @@ func ldhostobj(ld func(*bio.Reader, string, int64, string), f *bio.Reader, pkg s
 	// force external linking for any libraries that link in code that
 	// uses errno. This can be removed if the Go linker ever supports
 	// these relocation types.
-	if HEADTYPE == obj.Hdragonfly {
+	if Headtype == obj.Hdragonfly {
 		if pkg == "net" || pkg == "os/user" {
 			isinternal = false
 		}
@@ -923,7 +825,7 @@ func ldhostobj(ld func(*bio.Reader, string, int64, string), f *bio.Reader, pkg s
 	return h
 }
 
-func hostobjs() {
+func hostobjs(ctxt *Link) {
 	var h *Hostobj
 
 	for i := 0; i < len(hostobj); i++ {
@@ -934,7 +836,7 @@ func hostobjs() {
 		}
 
 		f.Seek(h.off, 0)
-		h.ld(f, h.pkg, h.length, h.pn)
+		h.ld(ctxt, f, h.pkg, h.length, h.pn)
 		f.Close()
 	}
 }
@@ -942,7 +844,7 @@ func hostobjs() {
 // provided by lib9
 
 func rmtemp() {
-	os.RemoveAll(tmpdir)
+	os.RemoveAll(*flagTmpdir)
 }
 
 func hostlinksetup() {
@@ -953,16 +855,16 @@ func hostlinksetup() {
 	// For external link, record that we need to tell the external linker -s,
 	// and turn off -s internally: the external linker needs the symbol
 	// information for its final link.
-	debug_s = Debug['s']
-	Debug['s'] = 0
+	debug_s = *FlagS
+	*FlagS = false
 
 	// create temporary directory and arrange cleanup
-	if tmpdir == "" {
+	if *flagTmpdir == "" {
 		dir, err := ioutil.TempDir("", "go-link-")
 		if err != nil {
 			log.Fatal(err)
 		}
-		tmpdir = dir
+		*flagTmpdir = dir
 		AtExit(rmtemp)
 	}
 
@@ -970,7 +872,7 @@ func hostlinksetup() {
 	coutbuf.f.Close()
 	mayberemoveoutfile()
 
-	p := filepath.Join(tmpdir, "go.o")
+	p := filepath.Join(*flagTmpdir, "go.o")
 	var err error
 	f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775)
 	if err != nil {
@@ -988,7 +890,7 @@ func hostobjCopy() (paths []string) {
 	sema := make(chan struct{}, runtime.NumCPU()) // limit open file descriptors
 	for i, h := range hostobj {
 		h := h
-		dst := filepath.Join(tmpdir, fmt.Sprintf("%06d.o", i))
+		dst := filepath.Join(*flagTmpdir, fmt.Sprintf("%06d.o", i))
 		paths = append(paths, dst)
 
 		wg.Add(1)
@@ -1023,13 +925,13 @@ func hostobjCopy() (paths []string) {
 }
 
 // archive builds a .a archive from the hostobj object files.
-func archive() {
+func (ctxt *Link) archive() {
 	if Buildmode != BuildmodeCArchive {
 		return
 	}
 
-	if extar == "" {
-		extar = "ar"
+	if *flagExtar == "" {
+		*flagExtar = "ar"
 	}
 
 	mayberemoveoutfile()
@@ -1042,13 +944,12 @@ func archive() {
 	}
 	coutbuf.f = nil
 
-	argv := []string{extar, "-q", "-c", "-s", outfile}
-	argv = append(argv, filepath.Join(tmpdir, "go.o"))
+	argv := []string{*flagExtar, "-q", "-c", "-s", *flagOutfile}
+	argv = append(argv, filepath.Join(*flagTmpdir, "go.o"))
 	argv = append(argv, hostobjCopy()...)
 
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "archive: %s\n", strings.Join(argv, " "))
-		Bso.Flush()
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("archive: %s\n", strings.Join(argv, " "))
 	}
 
 	if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
@@ -1056,7 +957,7 @@ func archive() {
 	}
 }
 
-func hostlink() {
+func (l *Link) hostlink() {
 	if Linkmode != LinkExternal || nerrors > 0 {
 		return
 	}
@@ -1064,37 +965,39 @@ func hostlink() {
 		return
 	}
 
-	if extld == "" {
-		extld = "gcc"
+	if *flagExtld == "" {
+		*flagExtld = "gcc"
 	}
 
 	var argv []string
-	argv = append(argv, extld)
+	argv = append(argv, *flagExtld)
 	argv = append(argv, hostlinkArchArgs()...)
 
-	if Debug['s'] == 0 && debug_s == 0 {
+	if !*FlagS && !debug_s {
 		argv = append(argv, "-gdwarf-2")
 	} else {
 		argv = append(argv, "-s")
 	}
 
-	if HEADTYPE == obj.Hdarwin {
-		argv = append(argv, "-Wl,-no_pie,-headerpad,1144")
-	}
-	if HEADTYPE == obj.Hopenbsd {
-		argv = append(argv, "-Wl,-nopie")
-	}
-	if HEADTYPE == obj.Hwindows {
-		if headstring == "windowsgui" {
-			argv = append(argv, "-mwindows")
+	switch Headtype {
+	case obj.Hdarwin:
+		argv = append(argv, "-Wl,-headerpad,1144")
+		if l.DynlinkingGo() {
+			argv = append(argv, "-Wl,-flat_namespace")
 		} else {
-			argv = append(argv, "-mconsole")
+			argv = append(argv, "-Wl,-no_pie")
 		}
+	case obj.Hopenbsd:
+		argv = append(argv, "-Wl,-nopie")
+	case obj.Hwindows:
+		argv = append(argv, "-mconsole")
+	case obj.Hwindowsgui:
+		argv = append(argv, "-mwindows")
 	}
 
 	switch Buildmode {
 	case BuildmodeExe:
-		if HEADTYPE == obj.Hdarwin {
+		if Headtype == obj.Hdarwin {
 			argv = append(argv, "-Wl,-pagezero_size,4000000")
 		}
 	case BuildmodePIE:
@@ -1103,7 +1006,7 @@ func hostlink() {
 		}
 		argv = append(argv, "-pie")
 	case BuildmodeCShared:
-		if HEADTYPE == obj.Hdarwin {
+		if Headtype == obj.Hdarwin {
 			argv = append(argv, "-dynamiclib", "-Wl,-read_only_relocs,suppress")
 		} else {
 			// ELF.
@@ -1120,9 +1023,18 @@ func hostlink() {
 			argv = append(argv, "-Wl,-z,relro")
 		}
 		argv = append(argv, "-shared")
+	case BuildmodePlugin:
+		if Headtype == obj.Hdarwin {
+			argv = append(argv, "-dynamiclib")
+		} else {
+			if UseRelro() {
+				argv = append(argv, "-Wl,-z,relro")
+			}
+			argv = append(argv, "-shared")
+		}
 	}
 
-	if Iself && DynlinkingGo() {
+	if Iself && l.DynlinkingGo() {
 		// We force all symbol resolution to be done at program startup
 		// because lazy PLT resolution can use large amounts of stack at
 		// times we cannot allow it to do so.
@@ -1147,7 +1059,7 @@ func hostlink() {
 			// If gold is not installed, gcc will silently switch
 			// back to ld.bfd. So we parse the version information
 			// and provide a useful error if gold is missing.
-			cmd := exec.Command(extld, "-fuse-ld=gold", "-Wl,--version")
+			cmd := exec.Command(*flagExtld, "-fuse-ld=gold", "-Wl,--version")
 			if out, err := cmd.CombinedOutput(); err == nil {
 				if !bytes.Contains(out, []byte("GNU gold")) {
 					log.Fatalf("ARM external linker must be gold (issue #15696), but is not: %s", out)
@@ -1166,8 +1078,8 @@ func hostlink() {
 	// will decide that the file already has an extension. We
 	// only want to do this when producing a Windows output file
 	// on a Windows host.
-	outopt := outfile
-	if goos == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" {
+	outopt := *flagOutfile
+	if obj.GOOS == "windows" && runtime.GOOS == "windows" && filepath.Ext(outopt) == "" {
 		outopt += "."
 	}
 	argv = append(argv, "-o")
@@ -1186,10 +1098,10 @@ func hostlink() {
 		argv = append(argv, "-Qunused-arguments")
 	}
 
-	argv = append(argv, filepath.Join(tmpdir, "go.o"))
+	argv = append(argv, filepath.Join(*flagTmpdir, "go.o"))
 	argv = append(argv, hostobjCopy()...)
 
-	if Linkshared {
+	if *FlagLinkshared {
 		seenDirs := make(map[string]bool)
 		seenLibs := make(map[string]bool)
 		addshlib := func(path string) {
@@ -1208,13 +1120,13 @@ func hostlink() {
 				seenLibs[base] = true
 			}
 		}
-		for _, shlib := range Ctxt.Shlibs {
+		for _, shlib := range l.Shlibs {
 			addshlib(shlib.Path)
 			for _, dep := range shlib.Deps {
 				if dep == "" {
 					continue
 				}
-				libpath := findshlib(dep)
+				libpath := findshlib(l, dep)
 				if libpath != "" {
 					addshlib(libpath)
 				}
@@ -1222,27 +1134,22 @@ func hostlink() {
 		}
 	}
 
-	sanitizers := flag_race != 0
-
-	for _, flag := range ldflag {
-		if strings.HasPrefix(flag, "-fsanitize=") {
-			sanitizers = true
-		}
-	}
-
 	argv = append(argv, ldflag...)
 
-	if sanitizers {
-		// On a system where the toolchain creates position independent
-		// executables by default, tsan/msan/asan/etc initialization can
-		// fail. So we pass -no-pie here, but support for that flag is quite
-		// new and we test for its support first.
-		src := filepath.Join(tmpdir, "trivial.c")
+	// When building a program with the default -buildmode=exe the
+	// gc compiler generates code requires DT_TEXTREL in a
+	// position independent executable (PIE). On systems where the
+	// toolchain creates PIEs by default, and where DT_TEXTREL
+	// does not work, the resulting programs will not run. See
+	// issue #17847. To avoid this problem pass -no-pie to the
+	// toolchain if it is supported.
+	if Buildmode == BuildmodeExe {
+		src := filepath.Join(*flagTmpdir, "trivial.c")
 		if err := ioutil.WriteFile(src, []byte{}, 0666); err != nil {
-			Ctxt.Diag("WriteFile trivial.c failed: %v", err)
+			Errorf(nil, "WriteFile trivial.c failed: %v", err)
 		}
 		cmd := exec.Command(argv[0], "-c", "-no-pie", "trivial.c")
-		cmd.Dir = tmpdir
+		cmd.Dir = *flagTmpdir
 		cmd.Env = append([]string{"LC_ALL=C"}, os.Environ()...)
 		out, err := cmd.CombinedOutput()
 		supported := err == nil && !bytes.Contains(out, []byte("unrecognized"))
@@ -1251,14 +1158,14 @@ func hostlink() {
 		}
 	}
 
-	for _, p := range strings.Fields(extldflags) {
+	for _, p := range strings.Fields(*flagExtldflags) {
 		argv = append(argv, p)
 
 		// clang, unlike GCC, passes -rdynamic to the linker
 		// even when linking with -static, causing a linker
 		// error when using GNU ld. So take out -rdynamic if
 		// we added it. We do it in this order, rather than
-		// only adding -rdynamic later, so that -extldflags
+		// only adding -rdynamic later, so that -*extldflags
 		// can override -rdynamic without using -static.
 		if Iself && p == "-static" {
 			for i := range argv {
@@ -1268,32 +1175,32 @@ func hostlink() {
 			}
 		}
 	}
-	if HEADTYPE == obj.Hwindows {
+	if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
+		// libmingw32 and libmingwex have some inter-dependencies,
+		// so must use linker groups.
+		argv = append(argv, "-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group")
 		argv = append(argv, peimporteddlls()...)
 	}
 
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "host link:")
+	if l.Debugvlog != 0 {
+		l.Logf("%5.2f host link:", obj.Cputime())
 		for _, v := range argv {
-			fmt.Fprintf(Bso, " %q", v)
+			l.Logf(" %q", v)
 		}
-		fmt.Fprintf(Bso, "\n")
-		Bso.Flush()
+		l.Logf("\n")
 	}
 
 	if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
 		Exitf("running %s failed: %v\n%s", argv[0], err, out)
-	} else if Debug['v'] != 0 && len(out) > 0 {
-		fmt.Fprintf(Bso, "%s", out)
-		Bso.Flush()
+	} else if l.Debugvlog != 0 && len(out) > 0 {
+		l.Logf("%s", out)
 	}
 
-	if Debug['s'] == 0 && debug_s == 0 && HEADTYPE == obj.Hdarwin {
+	if !*FlagS && !debug_s && Headtype == obj.Hdarwin {
 		// Skip combining dwarf on arm.
 		if !SysArch.InFamily(sys.ARM, sys.ARM64) {
-			dsym := filepath.Join(tmpdir, "go.dwarf")
-			if out, err := exec.Command("dsymutil", "-f", outfile, "-o", dsym).CombinedOutput(); err != nil {
-				Ctxt.Cursym = nil
+			dsym := filepath.Join(*flagTmpdir, "go.dwarf")
+			if out, err := exec.Command("dsymutil", "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil {
 				Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out)
 			}
 			// Skip combining if `dsymutil` didn't generate a file. See #11994.
@@ -1301,14 +1208,12 @@ func hostlink() {
 				return
 			}
 			// For os.Rename to work reliably, must be in same directory as outfile.
-			combinedOutput := outfile + "~"
-			if err := machoCombineDwarf(outfile, dsym, combinedOutput); err != nil {
-				Ctxt.Cursym = nil
+			combinedOutput := *flagOutfile + "~"
+			if err := machoCombineDwarf(*flagOutfile, dsym, combinedOutput); err != nil {
 				Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
 			}
-			os.Remove(outfile)
-			if err := os.Rename(combinedOutput, outfile); err != nil {
-				Ctxt.Cursym = nil
+			os.Remove(*flagOutfile)
+			if err := os.Rename(combinedOutput, *flagOutfile); err != nil {
 				Exitf("%s: %v", os.Args[0], err)
 			}
 		}
@@ -1336,9 +1241,10 @@ func hostlinkArchArgs() []string {
 // ldobj loads an input object. If it is a host object (an object
 // compiled by a non-Go compiler) it returns the Hostobj pointer. If
 // it is a Go object, it returns nil.
-func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, whence int) *Hostobj {
-	eof := f.Offset() + length
+func ldobj(ctxt *Link, f *bio.Reader, lib *Library, length int64, pn string, file string, whence int) *Hostobj {
+	pkg := pathtoprefix(lib.Pkg)
 
+	eof := f.Offset() + length
 	start := f.Offset()
 	c1 := bgetc(f)
 	c2 := bgetc(f)
@@ -1362,7 +1268,7 @@ func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, when
 	/* check the header */
 	line, err := f.ReadString('\n')
 	if err != nil {
-		Diag("truncated object file: %s: %v", pn, err)
+		Errorf(nil, "truncated object file: %s: %v", pn, err)
 		return nil
 	}
 
@@ -1374,20 +1280,20 @@ func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, when
 
 		if line == SysArch.Name {
 			// old header format: just $GOOS
-			Diag("%s: stale object file", pn)
+			Errorf(nil, "%s: stale object file", pn)
 			return nil
 		}
 
-		Diag("%s: not an object file", pn)
+		Errorf(nil, "%s: not an object file", pn)
 		return nil
 	}
 
-	// First, check that the basic goos, goarch, and version match.
-	t := fmt.Sprintf("%s %s %s ", goos, obj.Getgoarch(), obj.Getgoversion())
+	// First, check that the basic GOOS, GOARCH, and Version match.
+	t := fmt.Sprintf("%s %s %s ", obj.GOOS, obj.GOARCH, obj.Version)
 
 	line = strings.TrimRight(line, "\n")
-	if !strings.HasPrefix(line[10:]+" ", t) && Debug['f'] == 0 {
-		Diag("%s: object is [%s] expected [%s]", pn, line[10:], t)
+	if !strings.HasPrefix(line[10:]+" ", t) && !*flagF {
+		Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], t)
 		return nil
 	}
 
@@ -1398,7 +1304,7 @@ func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, when
 		if theline == "" {
 			theline = line[10:]
 		} else if theline != line[10:] {
-			Diag("%s: object is [%s] expected [%s]", pn, line[10:], theline)
+			Errorf(nil, "%s: object is [%s] expected [%s]", pn, line[10:], theline)
 			return nil
 		}
 	}
@@ -1414,7 +1320,7 @@ func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, when
 		c2 = c3
 		c3 = bgetc(f)
 		if c3 == -1 {
-			Diag("truncated object file: %s", pn)
+			Errorf(nil, "truncated object file: %s", pn)
 			return nil
 		}
 	}
@@ -1422,22 +1328,22 @@ func ldobj(f *bio.Reader, pkg string, length int64, pn string, file string, when
 	import1 := f.Offset()
 
 	f.Seek(import0, 0)
-	ldpkg(f, pkg, import1-import0-2, pn, whence) // -2 for !\n
+	ldpkg(ctxt, f, pkg, import1-import0-2, pn, whence) // -2 for !\n
 	f.Seek(import1, 0)
 
-	LoadObjFile(Ctxt, f, pkg, eof-f.Offset(), pn)
+	LoadObjFile(ctxt, f, lib, eof-f.Offset(), pn)
 	return nil
 }
 
-func readelfsymboldata(f *elf.File, sym *elf.Symbol) []byte {
+func readelfsymboldata(ctxt *Link, f *elf.File, sym *elf.Symbol) []byte {
 	data := make([]byte, sym.Size)
 	sect := f.Sections[sym.Section]
 	if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE {
-		Diag("reading %s from non-data section", sym.Name)
+		Errorf(nil, "reading %s from non-data section", sym.Name)
 	}
 	n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr))
 	if uint64(n) != sym.Size {
-		Diag("reading contents of %s: %v", sym.Name, err)
+		Errorf(nil, "reading contents of %s: %v", sym.Name, err)
 	}
 	return data
 }
@@ -1491,62 +1397,61 @@ func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) {
 	return nil, nil
 }
 
-func findshlib(shlib string) string {
-	for _, libdir := range Ctxt.Libdir {
+func findshlib(ctxt *Link, shlib string) string {
+	for _, libdir := range ctxt.Libdir {
 		libpath := filepath.Join(libdir, shlib)
 		if _, err := os.Stat(libpath); err == nil {
 			return libpath
 		}
 	}
-	Diag("cannot find shared library: %s", shlib)
+	Errorf(nil, "cannot find shared library: %s", shlib)
 	return ""
 }
 
-func ldshlibsyms(shlib string) {
-	libpath := findshlib(shlib)
+func ldshlibsyms(ctxt *Link, shlib string) {
+	libpath := findshlib(ctxt, shlib)
 	if libpath == "" {
 		return
 	}
-	for _, processedlib := range Ctxt.Shlibs {
+	for _, processedlib := range ctxt.Shlibs {
 		if processedlib.Path == libpath {
 			return
 		}
 	}
-	if Ctxt.Debugvlog > 1 && Ctxt.Bso != nil {
-		fmt.Fprintf(Ctxt.Bso, "%5.2f ldshlibsyms: found library with name %s at %s\n", obj.Cputime(), shlib, libpath)
-		Ctxt.Bso.Flush()
+	if ctxt.Debugvlog > 1 {
+		ctxt.Logf("%5.2f ldshlibsyms: found library with name %s at %s\n", obj.Cputime(), shlib, libpath)
 	}
 
 	f, err := elf.Open(libpath)
 	if err != nil {
-		Diag("cannot open shared library: %s", libpath)
+		Errorf(nil, "cannot open shared library: %s", libpath)
 		return
 	}
 
 	hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG)
 	if err != nil {
-		Diag("cannot read ABI hash from shared library %s: %v", libpath, err)
+		Errorf(nil, "cannot read ABI hash from shared library %s: %v", libpath, err)
 		return
 	}
 
 	depsbytes, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GODEPS_TAG)
 	if err != nil {
-		Diag("cannot read dep list from shared library %s: %v", libpath, err)
+		Errorf(nil, "cannot read dep list from shared library %s: %v", libpath, err)
 		return
 	}
 	deps := strings.Split(string(depsbytes), "\n")
 
 	syms, err := f.DynamicSymbols()
 	if err != nil {
-		Diag("cannot read symbols from shared library: %s", libpath)
+		Errorf(nil, "cannot read symbols from shared library: %s", libpath)
 		return
 	}
-	gcdata_locations := make(map[uint64]*LSym)
+	gcdataLocations := make(map[uint64]*Symbol)
 	for _, elfsym := range syms {
 		if elf.ST_TYPE(elfsym.Info) == elf.STT_NOTYPE || elf.ST_TYPE(elfsym.Info) == elf.STT_SECTION {
 			continue
 		}
-		lsym := Linklookup(Ctxt, elfsym.Name, 0)
+		lsym := ctxt.Syms.Lookup(elfsym.Name, 0)
 		// Because loadlib above loads all .a files before loading any shared
 		// libraries, any non-dynimport symbols we find that duplicate symbols
 		// already loaded should be ignored (the symbols from the .a files
@@ -1563,12 +1468,12 @@ func ldshlibsyms(shlib string) {
 			// The decodetype_* functions in decodetype.go need access to
 			// the type data.
 			if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") {
-				lsym.P = readelfsymboldata(f, &elfsym)
-				gcdata_locations[elfsym.Value+2*uint64(SysArch.PtrSize)+8+1*uint64(SysArch.PtrSize)] = lsym
+				lsym.P = readelfsymboldata(ctxt, f, &elfsym)
+				gcdataLocations[elfsym.Value+2*uint64(SysArch.PtrSize)+8+1*uint64(SysArch.PtrSize)] = lsym
 			}
 		}
 	}
-	gcdata_addresses := make(map[*LSym]uint64)
+	gcdataAddresses := make(map[*Symbol]uint64)
 	if SysArch.Family == sys.ARM64 {
 		for _, sect := range f.Sections {
 			if sect.Type == elf.SHT_RELA {
@@ -1579,39 +1484,22 @@ func ldshlibsyms(shlib string) {
 					if err == io.EOF {
 						break
 					} else if err != nil {
-						Diag("reading relocation failed %v", err)
+						Errorf(nil, "reading relocation failed %v", err)
 						return
 					}
 					t := elf.R_AARCH64(rela.Info & 0xffff)
 					if t != elf.R_AARCH64_RELATIVE {
 						continue
 					}
-					if lsym, ok := gcdata_locations[rela.Off]; ok {
-						gcdata_addresses[lsym] = uint64(rela.Addend)
+					if lsym, ok := gcdataLocations[rela.Off]; ok {
+						gcdataAddresses[lsym] = uint64(rela.Addend)
 					}
 				}
 			}
 		}
 	}
 
-	// We might have overwritten some functions above (this tends to happen for the
-	// autogenerated type equality/hashing functions) and we don't want to generated
-	// pcln table entries for these any more so remove them from Textp.
-	textp := make([]*LSym, 0, len(Ctxt.Textp))
-	for _, s := range Ctxt.Textp {
-		if s.Type != obj.SDYNIMPORT {
-			textp = append(textp, s)
-		}
-	}
-	Ctxt.Textp = textp
-
-	Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, gcdata_addresses: gcdata_addresses})
-}
-
-func mywhatsys() {
-	goroot = obj.Getgoroot()
-	goos = obj.Getgoos()
-	goarch = obj.Getgoarch()
+	ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, gcdataAddresses: gcdataAddresses})
 }
 
 // Copied from ../gc/subr.c:/^pathtoprefix; must stay in sync.
@@ -1677,32 +1565,32 @@ func Be32(b []byte) uint32 {
 	return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
 }
 
-type Chain struct {
-	sym   *LSym
-	up    *Chain
+type chain struct {
+	sym   *Symbol
+	up    *chain
 	limit int // limit on entry to sym
 }
 
-var morestack *LSym
+var morestack *Symbol
 
 // TODO: Record enough information in new object files to
 // allow stack checks here.
 
-func haslinkregister() bool {
-	return Ctxt.FixedFrameSize() != 0
+func haslinkregister(ctxt *Link) bool {
+	return ctxt.FixedFrameSize() != 0
 }
 
-func callsize() int {
-	if haslinkregister() {
+func callsize(ctxt *Link) int {
+	if haslinkregister(ctxt) {
 		return 0
 	}
 	return SysArch.RegSize
 }
 
-func dostkcheck() {
-	var ch Chain
+func (ctxt *Link) dostkcheck() {
+	var ch chain
 
-	morestack = Linklookup(Ctxt, "runtime.morestack", 0)
+	morestack = ctxt.Syms.Lookup("runtime.morestack", 0)
 
 	// Every splitting function ensures that there are at least StackLimit
 	// bytes available below SP when the splitting prologue finishes.
@@ -1713,11 +1601,11 @@ func dostkcheck() {
 	// of non-splitting functions.
 	ch.up = nil
 
-	ch.limit = obj.StackLimit - callsize()
+	ch.limit = obj.StackLimit - callsize(ctxt)
 
 	// Check every function, but do the nosplit functions in a first pass,
 	// to make the printed failure chains as short as possible.
-	for _, s := range Ctxt.Textp {
+	for _, s := range ctxt.Textp {
 		// runtime.racesymbolizethunk is called from gcc-compiled C
 		// code running on the operating system thread stack.
 		// It uses more than the usual amount of stack but that's okay.
@@ -1726,28 +1614,26 @@ func dostkcheck() {
 		}
 
 		if s.Attr.NoSplit() {
-			Ctxt.Cursym = s
 			ch.sym = s
-			stkcheck(&ch, 0)
+			stkcheck(ctxt, &ch, 0)
 		}
 	}
 
-	for _, s := range Ctxt.Textp {
+	for _, s := range ctxt.Textp {
 		if !s.Attr.NoSplit() {
-			Ctxt.Cursym = s
 			ch.sym = s
-			stkcheck(&ch, 0)
+			stkcheck(ctxt, &ch, 0)
 		}
 	}
 }
 
-func stkcheck(up *Chain, depth int) int {
+func stkcheck(ctxt *Link, up *chain, depth int) int {
 	limit := up.limit
 	s := up.sym
 
 	// Don't duplicate work: only need to consider each
 	// function at top of safe zone once.
-	top := limit == obj.StackLimit-callsize()
+	top := limit == obj.StackLimit-callsize(ctxt)
 	if top {
 		if s.Attr.StackCheck() {
 			return 0
@@ -1756,25 +1642,26 @@ func stkcheck(up *Chain, depth int) int {
 	}
 
 	if depth > 100 {
-		Diag("nosplit stack check too deep")
-		stkbroke(up, 0)
+		Errorf(s, "nosplit stack check too deep")
+		stkbroke(ctxt, up, 0)
 		return -1
 	}
 
 	if s.Attr.External() || s.FuncInfo == nil {
 		// external function.
 		// should never be called directly.
-		// only diagnose the direct caller.
+		// onlyctxt.Diagnose the direct caller.
 		// TODO(mwhudson): actually think about this.
-		if depth == 1 && s.Type != obj.SXREF && !DynlinkingGo() &&
-			Buildmode != BuildmodePIE && Buildmode != BuildmodeCShared {
-			Diag("call to external function %s", s.Name)
+		if depth == 1 && s.Type != obj.SXREF && !ctxt.DynlinkingGo() &&
+			Buildmode != BuildmodeCArchive && Buildmode != BuildmodePIE && Buildmode != BuildmodeCShared && Buildmode != BuildmodePlugin {
+
+			Errorf(s, "call to external function")
 		}
 		return -1
 	}
 
 	if limit < 0 {
-		stkbroke(up, limit)
+		stkbroke(ctxt, up, limit)
 		return -1
 	}
 
@@ -1784,14 +1671,14 @@ func stkcheck(up *Chain, depth int) int {
 		return 0
 	}
 
-	var ch Chain
+	var ch chain
 	ch.up = up
 
 	if !s.Attr.NoSplit() {
 		// Ensure we have enough stack to call morestack.
-		ch.limit = limit - callsize()
+		ch.limit = limit - callsize(ctxt)
 		ch.sym = morestack
-		if stkcheck(&ch, depth+1) < 0 {
+		if stkcheck(ctxt, &ch, depth+1) < 0 {
 			return -1
 		}
 		if !top {
@@ -1802,22 +1689,22 @@ func stkcheck(up *Chain, depth int) int {
 		if s.FuncInfo != nil {
 			locals = s.FuncInfo.Locals
 		}
-		limit = int(obj.StackLimit+locals) + int(Ctxt.FixedFrameSize())
+		limit = int(obj.StackLimit+locals) + int(ctxt.FixedFrameSize())
 	}
 
 	// Walk through sp adjustments in function, consuming relocs.
 	ri := 0
 
 	endr := len(s.R)
-	var ch1 Chain
+	var ch1 chain
 	var pcsp Pciter
 	var r *Reloc
-	for pciterinit(Ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
+	for pciterinit(ctxt, &pcsp, &s.FuncInfo.Pcsp); pcsp.done == 0; pciternext(&pcsp) {
 		// pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
 
 		// Check stack size in effect for this span.
 		if int32(limit)-pcsp.value < 0 {
-			stkbroke(up, int(int32(limit)-pcsp.value))
+			stkbroke(ctxt, up, int(int32(limit)-pcsp.value))
 			return -1
 		}
 
@@ -1827,9 +1714,9 @@ func stkcheck(up *Chain, depth int) int {
 			switch r.Type {
 			// Direct call.
 			case obj.R_CALL, obj.R_CALLARM, obj.R_CALLARM64, obj.R_CALLPOWER, obj.R_CALLMIPS:
-				ch.limit = int(int32(limit) - pcsp.value - int32(callsize()))
+				ch.limit = int(int32(limit) - pcsp.value - int32(callsize(ctxt)))
 				ch.sym = r.Sym
-				if stkcheck(&ch, depth+1) < 0 {
+				if stkcheck(ctxt, &ch, depth+1) < 0 {
 					return -1
 				}
 
@@ -1838,13 +1725,13 @@ func stkcheck(up *Chain, depth int) int {
 			// Arrange the data structures to report both calls, so that
 			// if there is an error, stkprint shows all the steps involved.
 			case obj.R_CALLIND:
-				ch.limit = int(int32(limit) - pcsp.value - int32(callsize()))
+				ch.limit = int(int32(limit) - pcsp.value - int32(callsize(ctxt)))
 
 				ch.sym = nil
-				ch1.limit = ch.limit - callsize() // for morestack in called prologue
+				ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue
 				ch1.up = &ch
 				ch1.sym = morestack
-				if stkcheck(&ch1, depth+2) < 0 {
+				if stkcheck(ctxt, &ch1, depth+2) < 0 {
 					return -1
 				}
 			}
@@ -1854,12 +1741,12 @@ func stkcheck(up *Chain, depth int) int {
 	return 0
 }
 
-func stkbroke(ch *Chain, limit int) {
-	Diag("nosplit stack overflow")
-	stkprint(ch, limit)
+func stkbroke(ctxt *Link, ch *chain, limit int) {
+	Errorf(ch.sym, "nosplit stack overflow")
+	stkprint(ctxt, ch, limit)
 }
 
-func stkprint(ch *Chain, limit int) {
+func stkprint(ctxt *Link, ch *chain, limit int) {
 	var name string
 
 	if ch.sym != nil {
@@ -1879,8 +1766,8 @@ func stkprint(ch *Chain, limit int) {
 			fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name)
 		}
 	} else {
-		stkprint(ch.up, ch.limit+callsize())
-		if !haslinkregister() {
+		stkprint(ctxt, ch.up, ch.limit+callsize(ctxt))
+		if !haslinkregister(ctxt) {
 			fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name)
 		}
 	}
@@ -1896,10 +1783,6 @@ func Cflush() {
 	}
 }
 
-func Cpos() int64 {
-	return coutbuf.off
-}
-
 func Cseek(p int64) {
 	if p == coutbuf.off {
 		return
@@ -1930,38 +1813,59 @@ func usage() {
 	Exit(2)
 }
 
-func setheadtype(s string) {
-	h := headtype(s)
-	if h < 0 {
-		Exitf("unknown header type -H %s", s)
-	}
-
-	headstring = s
-	HEADTYPE = int32(headtype(s))
+func doversion() {
+	Exitf("version %s", obj.Version)
 }
 
-func setinterp(s string) {
-	Debug['I'] = 1 // denote cmdline interpreter override
-	interpreter = s
-}
+type SymbolType int8
 
-func doversion() {
-	Exitf("version %s", obj.Getgoversion())
-}
+const (
+	TextSym      SymbolType = 'T'
+	DataSym                 = 'D'
+	BSSSym                  = 'B'
+	UndefinedSym            = 'U'
+	TLSSym                  = 't'
+	FileSym                 = 'f'
+	FrameSym                = 'm'
+	ParamSym                = 'p'
+	AutoSym                 = 'a'
+)
 
-func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
+func genasmsym(ctxt *Link, put func(*Link, *Symbol, string, SymbolType, int64, *Symbol)) {
 	// These symbols won't show up in the first loop below because we
 	// skip STEXT symbols. Normal STEXT symbols are emitted by walking textp.
-	s := Linklookup(Ctxt, "runtime.text", 0)
+	s := ctxt.Syms.Lookup("runtime.text", 0)
 	if s.Type == obj.STEXT {
-		put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil)
+		put(ctxt, s, s.Name, TextSym, s.Value, nil)
+	}
+
+	n := 0
+
+	// Generate base addresses for all text sections if there are multiple
+	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
+		if n == 0 {
+			n++
+			continue
+		}
+		if sect.Name != ".text" {
+			break
+		}
+		s = ctxt.Syms.ROLookup(fmt.Sprintf("runtime.text.%d", n), 0)
+		if s == nil {
+			break
+		}
+		if s.Type == obj.STEXT {
+			put(ctxt, s, s.Name, TextSym, s.Value, nil)
+		}
+		n++
 	}
-	s = Linklookup(Ctxt, "runtime.etext", 0)
+
+	s = ctxt.Syms.Lookup("runtime.etext", 0)
 	if s.Type == obj.STEXT {
-		put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil)
+		put(ctxt, s, s.Name, TextSym, s.Value, nil)
 	}
 
-	for _, s := range Ctxt.Allsym {
+	for _, s := range ctxt.Syms.Allsym {
 		if s.Attr.Hidden() {
 			continue
 		}
@@ -1981,13 +1885,11 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
 			obj.STYPE,
 			obj.SSTRING,
 			obj.SGOSTRING,
-			obj.SGOSTRINGHDR,
 			obj.SGOFUNC,
 			obj.SGCBITS,
 			obj.STYPERELRO,
 			obj.SSTRINGRELRO,
 			obj.SGOSTRINGRELRO,
-			obj.SGOSTRINGHDRRELRO,
 			obj.SGOFUNCRELRO,
 			obj.SGCBITSRELRO,
 			obj.SRODATARELRO,
@@ -1997,48 +1899,48 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
 			if !s.Attr.Reachable() {
 				continue
 			}
-			put(s, s.Name, 'D', Symaddr(s), s.Size, int(s.Version), s.Gotype)
+			put(ctxt, s, s.Name, DataSym, Symaddr(s), s.Gotype)
 
 		case obj.SBSS, obj.SNOPTRBSS:
 			if !s.Attr.Reachable() {
 				continue
 			}
 			if len(s.P) > 0 {
-				Diag("%s should not be bss (size=%d type=%d special=%v)", s.Name, len(s.P), s.Type, s.Attr.Special())
+				Errorf(s, "should not be bss (size=%d type=%d special=%v)", len(s.P), s.Type, s.Attr.Special())
 			}
-			put(s, s.Name, 'B', Symaddr(s), s.Size, int(s.Version), s.Gotype)
+			put(ctxt, s, s.Name, BSSSym, Symaddr(s), s.Gotype)
 
 		case obj.SFILE:
-			put(nil, s.Name, 'f', s.Value, 0, int(s.Version), nil)
+			put(ctxt, nil, s.Name, FileSym, s.Value, nil)
 
 		case obj.SHOSTOBJ:
-			if HEADTYPE == obj.Hwindows || Iself {
-				put(s, s.Name, 'U', s.Value, 0, int(s.Version), nil)
+			if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui || Iself {
+				put(ctxt, s, s.Name, UndefinedSym, s.Value, nil)
 			}
 
 		case obj.SDYNIMPORT:
 			if !s.Attr.Reachable() {
 				continue
 			}
-			put(s, s.Extname, 'U', 0, 0, int(s.Version), nil)
+			put(ctxt, s, s.Extname, UndefinedSym, 0, nil)
 
 		case obj.STLSBSS:
-			if Linkmode == LinkExternal && HEADTYPE != obj.Hopenbsd {
-				put(s, s.Name, 't', Symaddr(s), s.Size, int(s.Version), s.Gotype)
+			if Linkmode == LinkExternal && Headtype != obj.Hopenbsd {
+				put(ctxt, s, s.Name, TLSSym, Symaddr(s), s.Gotype)
 			}
 		}
 	}
 
 	var off int32
-	for _, s := range Ctxt.Textp {
-		put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype)
+	for _, s := range ctxt.Textp {
+		put(ctxt, s, s.Name, TextSym, s.Value, s.Gotype)
 
 		locals := int32(0)
 		if s.FuncInfo != nil {
 			locals = s.FuncInfo.Locals
 		}
 		// NOTE(ality): acid can't produce a stack trace without .frame symbols
-		put(nil, ".frame", 'm', int64(locals)+int64(SysArch.PtrSize), 0, 0, nil)
+		put(ctxt, nil, ".frame", FrameSym, int64(locals)+int64(SysArch.PtrSize), nil)
 
 		if s.FuncInfo == nil {
 			continue
@@ -2059,135 +1961,116 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
 
 			// FP
 			if off >= 0 {
-				put(nil, a.Asym.Name, 'p', int64(off), 0, 0, a.Gotype)
+				put(ctxt, nil, a.Asym.Name, ParamSym, int64(off), a.Gotype)
 				continue
 			}
 
 			// SP
 			if off <= int32(-SysArch.PtrSize) {
-				put(nil, a.Asym.Name, 'a', -(int64(off) + int64(SysArch.PtrSize)), 0, 0, a.Gotype)
+				put(ctxt, nil, a.Asym.Name, AutoSym, -(int64(off) + int64(SysArch.PtrSize)), a.Gotype)
 				continue
 			}
+			// Otherwise, off is addressing the saved program counter.
+			// Something underhanded is going on. Say nothing.
 		}
 	}
 
-	// Otherwise, off is addressing the saved program counter.
-	// Something underhanded is going on. Say nothing.
-	if Debug['v'] != 0 || Debug['n'] != 0 {
-		fmt.Fprintf(Bso, "%5.2f symsize = %d\n", obj.Cputime(), uint32(Symsize))
+	if ctxt.Debugvlog != 0 || *flagN {
+		ctxt.Logf("%5.2f symsize = %d\n", obj.Cputime(), uint32(Symsize))
 	}
-	Bso.Flush()
 }
 
-func Symaddr(s *LSym) int64 {
+func Symaddr(s *Symbol) int64 {
 	if !s.Attr.Reachable() {
-		Diag("unreachable symbol in symaddr - %s", s.Name)
+		Errorf(s, "unreachable symbol in symaddr")
 	}
 	return s.Value
 }
 
-func xdefine(p string, t int, v int64) {
-	s := Linklookup(Ctxt, p, 0)
-	s.Type = int16(t)
+func (ctxt *Link) xdefine(p string, t obj.SymKind, v int64) {
+	s := ctxt.Syms.Lookup(p, 0)
+	s.Type = t
 	s.Value = v
 	s.Attr |= AttrReachable
 	s.Attr |= AttrSpecial
 	s.Attr |= AttrLocal
 }
 
-func datoff(addr int64) int64 {
+func datoff(s *Symbol, addr int64) int64 {
 	if uint64(addr) >= Segdata.Vaddr {
 		return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff)
 	}
 	if uint64(addr) >= Segtext.Vaddr {
 		return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff)
 	}
-	Diag("datoff %#x", addr)
+	Errorf(s, "invalid datoff %#x", addr)
 	return 0
 }
 
-func Entryvalue() int64 {
-	a := INITENTRY
+func Entryvalue(ctxt *Link) int64 {
+	a := *flagEntrySymbol
 	if a[0] >= '0' && a[0] <= '9' {
 		return atolwhex(a)
 	}
-	s := Linklookup(Ctxt, a, 0)
+	s := ctxt.Syms.Lookup(a, 0)
 	if s.Type == 0 {
-		return INITTEXT
+		return *FlagTextAddr
 	}
 	if s.Type != obj.STEXT {
-		Diag("entry not text: %s", s.Name)
+		Errorf(s, "entry not text")
 	}
 	return s.Value
 }
 
-func undefsym(s *LSym) {
+func undefsym(ctxt *Link, s *Symbol) {
 	var r *Reloc
 
-	Ctxt.Cursym = s
 	for i := 0; i < len(s.R); i++ {
 		r = &s.R[i]
 		if r.Sym == nil { // happens for some external ARM relocs
 			continue
 		}
 		if r.Sym.Type == obj.Sxxx || r.Sym.Type == obj.SXREF {
-			Diag("undefined: %s", r.Sym.Name)
+			Errorf(s, "undefined: %q", r.Sym.Name)
 		}
-		if !r.Sym.Attr.Reachable() {
-			Diag("use of unreachable symbol: %s", r.Sym.Name)
+		if !r.Sym.Attr.Reachable() && r.Type != obj.R_WEAKADDROFF {
+			Errorf(s, "relocation target %q", r.Sym.Name)
 		}
 	}
 }
 
-func undef() {
-	for _, s := range Ctxt.Textp {
-		undefsym(s)
+func (ctxt *Link) undef() {
+	for _, s := range ctxt.Textp {
+		undefsym(ctxt, s)
 	}
 	for _, s := range datap {
-		undefsym(s)
+		undefsym(ctxt, s)
 	}
 	if nerrors > 0 {
 		errorexit()
 	}
 }
 
-func callgraph() {
-	if Debug['c'] == 0 {
+func (ctxt *Link) callgraph() {
+	if !*FlagC {
 		return
 	}
 
 	var i int
 	var r *Reloc
-	for _, s := range Ctxt.Textp {
+	for _, s := range ctxt.Textp {
 		for i = 0; i < len(s.R); i++ {
 			r = &s.R[i]
 			if r.Sym == nil {
 				continue
 			}
 			if (r.Type == obj.R_CALL || r.Type == obj.R_CALLARM || r.Type == obj.R_CALLPOWER || r.Type == obj.R_CALLMIPS) && r.Sym.Type == obj.STEXT {
-				fmt.Fprintf(Bso, "%s calls %s\n", s.Name, r.Sym.Name)
+				ctxt.Logf("%s calls %s\n", s.Name, r.Sym.Name)
 			}
 		}
 	}
 }
 
-func Diag(format string, args ...interface{}) {
-	tn := ""
-	sep := ""
-	if Ctxt.Cursym != nil {
-		tn = Ctxt.Cursym.Name
-		sep = ": "
-	}
-	fmt.Printf("%s%s%s\n", tn, sep, fmt.Sprintf(format, args...))
-	nerrors++
-	if Debug['h'] != 0 {
-		panic("error")
-	}
-	if nerrors > 20 {
-		Exitf("too many errors")
-	}
-}
-
 func Rnd(v int64, r int64) int64 {
 	if r <= 0 {
 		return v
@@ -2211,3 +2094,34 @@ func bgetc(r *bio.Reader) int {
 	}
 	return int(c)
 }
+
+type markKind uint8 // for postorder traversal
+const (
+	unvisited markKind = iota
+	visiting
+	visited
+)
+
+func postorder(libs []*Library) []*Library {
+	order := make([]*Library, 0, len(libs)) // hold the result
+	mark := make(map[*Library]markKind, len(libs))
+	for _, lib := range libs {
+		dfs(lib, mark, &order)
+	}
+	return order
+}
+
+func dfs(lib *Library, mark map[*Library]markKind, order *[]*Library) {
+	if mark[lib] == visited {
+		return
+	}
+	if mark[lib] == visiting {
+		panic("found import cycle while visiting " + lib.Pkg)
+	}
+	mark[lib] = visiting
+	for _, i := range lib.imports {
+		dfs(i, mark, order)
+	}
+	mark[lib] = visited
+	*order = append(*order, lib)
+}
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index 9bab68b..ab7e49b 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -1,5 +1,5 @@
 // Derived from Inferno utils/6l/l.h and related files.
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -32,15 +32,17 @@ package ld
 
 import (
 	"bufio"
+	"cmd/internal/obj"
 	"cmd/internal/sys"
 	"debug/elf"
 	"fmt"
 )
 
-type LSym struct {
+// Symbol is an entry in the symbol table.
+type Symbol struct {
 	Name        string
 	Extname     string
-	Type        int16
+	Type        obj.SymKind
 	Version     int16
 	Attr        Attribute
 	Localentry  uint8
@@ -56,28 +58,28 @@ type LSym struct {
 	// is not set for symbols defined by the packages being linked or by symbols
 	// read by ldelf (and so is left as elf.STT_NOTYPE).
 	ElfType     elf.SymType
-	Next        *LSym
-	Sub         *LSym
-	Outer       *LSym
-	Gotype      *LSym
-	Reachparent *LSym
+	Sub         *Symbol
+	Outer       *Symbol
+	Gotype      *Symbol
+	Reachparent *Symbol
 	File        string
 	Dynimplib   string
 	Dynimpvers  string
 	Sect        *Section
 	FuncInfo    *FuncInfo
-	P           []byte
-	R           []Reloc
+	// P contains the raw symbol data.
+	P []byte
+	R []Reloc
 }
 
-func (s *LSym) String() string {
+func (s *Symbol) String() string {
 	if s.Version == 0 {
 		return s.Name
 	}
 	return fmt.Sprintf("%s<%d>", s.Name, s.Version)
 }
 
-func (s *LSym) ElfsymForReloc() int32 {
+func (s *Symbol) ElfsymForReloc() int32 {
 	// If putelfsym created a local version of this symbol, use that in all
 	// relocations.
 	if s.LocalElfsym != 0 {
@@ -103,6 +105,7 @@ const (
 	AttrOnList
 	AttrLocal
 	AttrReflectMethod
+	AttrMakeTypelink
 )
 
 func (a Attribute) DuplicateOK() bool      { return a&AttrDuplicateOK != 0 }
@@ -117,6 +120,7 @@ func (a Attribute) Hidden() bool           { return a&AttrHidden != 0 }
 func (a Attribute) OnList() bool           { return a&AttrOnList != 0 }
 func (a Attribute) Local() bool            { return a&AttrLocal != 0 }
 func (a Attribute) ReflectMethod() bool    { return a&AttrReflectMethod != 0 }
+func (a Attribute) MakeTypelink() bool     { return a&AttrMakeTypelink != 0 }
 
 func (a Attribute) CgoExport() bool {
 	return a.CgoExportDynamic() || a.CgoExportStatic()
@@ -130,58 +134,65 @@ func (a *Attribute) Set(flag Attribute, value bool) {
 	}
 }
 
+// Reloc is a relocation.
+//
+// The typical Reloc rewrites part of a symbol at offset Off to address Sym.
+// A Reloc is stored in a slice on the Symbol it rewrites.
+//
+// Relocations are generated by the compiler as the type
+// cmd/internal/obj.Reloc, which is encoded into the object file wire
+// format and decoded by the linker into this type. A separate type is
+// used to hold linker-specific state about the relocation.
+//
+// Some relocations are created by cmd/link.
 type Reloc struct {
-	Off     int32
-	Siz     uint8
-	Done    uint8
-	Type    int32
-	Variant int32
-	Add     int64
-	Xadd    int64
-	Sym     *LSym
-	Xsym    *LSym
+	Off     int32         // offset to rewrite
+	Siz     uint8         // number of bytes to rewrite, 1, 2, or 4
+	Done    uint8         // set to 1 when relocation is complete
+	Variant RelocVariant  // variation on Type
+	Type    obj.RelocType // the relocation type
+	Add     int64         // addend
+	Xadd    int64         // addend passed to external linker
+	Sym     *Symbol       // symbol the relocation addresses
+	Xsym    *Symbol       // symbol passed to external linker
 }
 
 type Auto struct {
-	Asym    *LSym
-	Gotype  *LSym
+	Asym    *Symbol
+	Gotype  *Symbol
 	Aoffset int32
 	Name    int16
 }
 
 type Shlib struct {
-	Path             string
-	Hash             []byte
-	Deps             []string
-	File             *elf.File
-	gcdata_addresses map[*LSym]uint64
+	Path            string
+	Hash            []byte
+	Deps            []string
+	File            *elf.File
+	gcdataAddresses map[*Symbol]uint64
 }
 
+// Link holds the context for writing object code from a compiler
+// or for reading that input into the linker.
 type Link struct {
-	Goarm     int32
-	Headtype  int
+	Syms *Symbols
+
 	Arch      *sys.Arch
-	Debugvlog int32
+	Debugvlog int
 	Bso       *bufio.Writer
-	Windows   int32
-	Goroot    string
 
-	// Symbol lookup based on name and indexed by version.
-	Hash []map[string]*LSym
+	Loaded bool // set after all inputs have been loaded as symbols
 
-	Allsym     []*LSym
-	Tlsg       *LSym
+	Tlsg       *Symbol
 	Libdir     []string
 	Library    []*Library
 	Shlibs     []Shlib
 	Tlsoffset  int
-	Diag       func(string, ...interface{})
-	Cursym     *LSym
-	Version    int
-	Textp      []*LSym
-	Filesyms   []*LSym
-	Moduledata *LSym
-	LSymBatch  []LSym
+	Textp      []*Symbol
+	Filesyms   []*Symbol
+	Moduledata *Symbol
+
+	tramps []*Symbol // trampolines
 }
 
 // The smallest possible offset from the hardware stack pointer to a local
@@ -201,18 +212,25 @@ func (ctxt *Link) FixedFrameSize() int64 {
 	}
 }
 
-func (l *Link) IncVersion() {
-	l.Version++
-	l.Hash = append(l.Hash, make(map[string]*LSym))
+func (l *Link) Logf(format string, args ...interface{}) {
+	fmt.Fprintf(l.Bso, format, args...)
+	l.Bso.Flush()
 }
 
 type Library struct {
-	Objref string
-	Srcref string
-	File   string
-	Pkg    string
-	Shlib  string
-	hash   []byte
+	Objref      string
+	Srcref      string
+	File        string
+	Pkg         string
+	Shlib       string
+	hash        []byte
+	imports     []*Library
+	textp       []*Symbol // text symbols defined in this library
+	dupTextSyms []*Symbol // dupok text symbols defined in this library
+}
+
+func (l Library) String() string {
+	return l.Pkg
 }
 
 type FuncInfo struct {
@@ -223,9 +241,9 @@ type FuncInfo struct {
 	Pcfile      Pcdata
 	Pcline      Pcdata
 	Pcdata      []Pcdata
-	Funcdata    []*LSym
+	Funcdata    []*Symbol
 	Funcdataoff []int64
-	File        []*LSym
+	File        []*Symbol
 }
 
 type Pcdata struct {
@@ -243,9 +261,11 @@ type Pciter struct {
 	done    int
 }
 
-// Reloc.variant
+// RelocVariant is a linker-internal variation on a relocation.
+type RelocVariant uint8
+
 const (
-	RV_NONE = iota
+	RV_NONE RelocVariant = iota
 	RV_POWER_LO
 	RV_POWER_HI
 	RV_POWER_HA
@@ -256,20 +276,6 @@ const (
 	// divided by 2.
 	RV_390_DBL
 
-	RV_CHECK_OVERFLOW = 1 << 8
-	RV_TYPE_MASK      = RV_CHECK_OVERFLOW - 1
-)
-
-// Pcdata iterator.
-//	for(pciterinit(ctxt, &it, &pcd); !it.done; pciternext(&it)) { it.value holds in [it.pc, it.nextpc) }
-
-// Link holds the context for writing object code from a compiler
-// to be linker input or for reading that input into the linker.
-
-// LinkArch is the definition of a single architecture.
-
-const (
-	LinkAuto = 0 + iota
-	LinkInternal
-	LinkExternal
+	RV_CHECK_OVERFLOW RelocVariant = 1 << 7
+	RV_TYPE_MASK      RelocVariant = RV_CHECK_OVERFLOW - 1
 )
diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
index 53cc962..c88af64 100644
--- a/src/cmd/link/internal/ld/macho.go
+++ b/src/cmd/link/internal/ld/macho.go
@@ -121,7 +121,7 @@ const (
 
 var nkind [NumSymKind]int
 
-var sortsym []*LSym
+var sortsym []*Symbol
 
 var nsortsym int
 
@@ -131,7 +131,7 @@ var nsortsym int
 // "big enough" header size. The initial header is
 // one page, the non-dynamic library stuff takes
 // up about 1300 bytes; we overestimate that as 2k.
-var load_budget int = INITIAL_MACHO_HEADR - 2*1024
+var loadBudget int = INITIAL_MACHO_HEADR - 2*1024
 
 func Machoinit() {
 	macho64 = SysArch.RegSize == 8
@@ -186,7 +186,7 @@ var dylib []string
 var linkoff int64
 
 func machowrite() int {
-	o1 := Cpos()
+	o1 := coutbuf.Offset()
 
 	loadsize := 4 * 4 * ndebug
 	for i := 0; i < len(load); i++ {
@@ -291,41 +291,41 @@ func machowrite() int {
 		}
 	}
 
-	return int(Cpos() - o1)
+	return int(coutbuf.Offset() - o1)
 }
 
-func domacho() {
-	if Debug['d'] != 0 {
+func (ctxt *Link) domacho() {
+	if *FlagD {
 		return
 	}
 
 	// empirically, string table must begin with " \x00".
-	s := Linklookup(Ctxt, ".machosymstr", 0)
+	s := ctxt.Syms.Lookup(".machosymstr", 0)
 
 	s.Type = obj.SMACHOSYMSTR
 	s.Attr |= AttrReachable
-	Adduint8(Ctxt, s, ' ')
-	Adduint8(Ctxt, s, '\x00')
+	Adduint8(ctxt, s, ' ')
+	Adduint8(ctxt, s, '\x00')
 
-	s = Linklookup(Ctxt, ".machosymtab", 0)
+	s = ctxt.Syms.Lookup(".machosymtab", 0)
 	s.Type = obj.SMACHOSYMTAB
 	s.Attr |= AttrReachable
 
 	if Linkmode != LinkExternal {
-		s := Linklookup(Ctxt, ".plt", 0) // will be __symbol_stub
+		s := ctxt.Syms.Lookup(".plt", 0) // will be __symbol_stub
 		s.Type = obj.SMACHOPLT
 		s.Attr |= AttrReachable
 
-		s = Linklookup(Ctxt, ".got", 0) // will be __nl_symbol_ptr
+		s = ctxt.Syms.Lookup(".got", 0) // will be __nl_symbol_ptr
 		s.Type = obj.SMACHOGOT
 		s.Attr |= AttrReachable
 		s.Align = 4
 
-		s = Linklookup(Ctxt, ".linkedit.plt", 0) // indirect table for .plt
+		s = ctxt.Syms.Lookup(".linkedit.plt", 0) // indirect table for .plt
 		s.Type = obj.SMACHOINDIRECTPLT
 		s.Attr |= AttrReachable
 
-		s = Linklookup(Ctxt, ".linkedit.got", 0) // indirect table for .got
+		s = ctxt.Syms.Lookup(".linkedit.got", 0) // indirect table for .got
 		s.Type = obj.SMACHOINDIRECTGOT
 		s.Attr |= AttrReachable
 	}
@@ -336,24 +336,24 @@ func Machoadddynlib(lib string) {
 	// and 24 bytes of header metadata. If not enough
 	// space, grab another page of initial space at the
 	// beginning of the output file.
-	load_budget -= (len(lib)+7)/8*8 + 24
+	loadBudget -= (len(lib)+7)/8*8 + 24
 
-	if load_budget < 0 {
+	if loadBudget < 0 {
 		HEADR += 4096
-		INITTEXT += 4096
-		load_budget += 4096
+		*FlagTextAddr += 4096
+		loadBudget += 4096
 	}
 
 	dylib = append(dylib, lib)
 }
 
-func machoshbits(mseg *MachoSeg, sect *Section, segname string) {
+func machoshbits(ctxt *Link, mseg *MachoSeg, sect *Section, segname string) {
 	buf := "__" + strings.Replace(sect.Name[1:], ".", "_", -1)
 
 	var msect *MachoSect
 	if sect.Rwx&1 == 0 && segname != "__DWARF" && (SysArch.Family == sys.ARM64 ||
-		(SysArch.Family == sys.AMD64 && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive)) ||
-		(SysArch.Family == sys.ARM && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive))) {
+		(SysArch.Family == sys.AMD64 && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive || Buildmode == BuildmodePlugin)) ||
+		(SysArch.Family == sys.ARM && (Buildmode == BuildmodeCShared || Buildmode == BuildmodeCArchive || Buildmode == BuildmodePlugin))) {
 		// Darwin external linker on arm64 and on amd64 and arm in c-shared/c-archive buildmode
 		// complains about absolute relocs in __TEXT, so if the section is not
 		// executable, put it in __DATA segment.
@@ -376,7 +376,7 @@ func machoshbits(mseg *MachoSeg, sect *Section, segname string) {
 	if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen {
 		// data in file
 		if sect.Length > sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr {
-			Diag("macho cannot represent section %s crossing data and bss", sect.Name)
+			Errorf(nil, "macho cannot represent section %s crossing data and bss", sect.Name)
 		}
 		msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr)
 	} else {
@@ -400,7 +400,7 @@ func machoshbits(mseg *MachoSeg, sect *Section, segname string) {
 	if sect.Name == ".got" {
 		msect.name = "__nl_symbol_ptr"
 		msect.flag = 6                                                     /* section with nonlazy symbol pointers */
-		msect.res1 = uint32(Linklookup(Ctxt, ".linkedit.plt", 0).Size / 4) /* offset into indirect symbol table */
+		msect.res1 = uint32(ctxt.Syms.Lookup(".linkedit.plt", 0).Size / 4) /* offset into indirect symbol table */
 	}
 
 	if sect.Name == ".init_array" {
@@ -413,9 +413,9 @@ func machoshbits(mseg *MachoSeg, sect *Section, segname string) {
 	}
 }
 
-func Asmbmacho() {
+func Asmbmacho(ctxt *Link) {
 	/* apple MACH */
-	va := INITTEXT - int64(HEADR)
+	va := *FlagTextAddr - int64(HEADR)
 
 	mh := getMachoHdr()
 	switch SysArch.Family {
@@ -460,7 +460,7 @@ func Asmbmacho() {
 	}
 
 	/* text */
-	v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(INITRND))
+	v := Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound))
 
 	if Linkmode != LinkExternal {
 		ms = newMachoSeg("__TEXT", 20)
@@ -473,7 +473,7 @@ func Asmbmacho() {
 	}
 
 	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
-		machoshbits(ms, sect, "__TEXT")
+		machoshbits(ctxt, ms, sect, "__TEXT")
 	}
 
 	/* data */
@@ -489,11 +489,11 @@ func Asmbmacho() {
 	}
 
 	for sect := Segdata.Sect; sect != nil; sect = sect.Next {
-		machoshbits(ms, sect, "__DATA")
+		machoshbits(ctxt, ms, sect, "__DATA")
 	}
 
 	/* dwarf */
-	if Debug['w'] == 0 {
+	if !*FlagW {
 		if Linkmode != LinkExternal {
 			ms = newMachoSeg("__DWARF", 20)
 			ms.vaddr = Segdwarf.Vaddr
@@ -502,7 +502,7 @@ func Asmbmacho() {
 			ms.filesize = Segdwarf.Filelen
 		}
 		for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
-			machoshbits(ms, sect, "__DWARF")
+			machoshbits(ctxt, ms, sect, "__DWARF")
 		}
 	}
 
@@ -512,43 +512,43 @@ func Asmbmacho() {
 			Exitf("unknown macho architecture: %v", SysArch.Family)
 
 		case sys.ARM:
-			ml := newMachoLoad(5, 17+2)          /* unix thread */
-			ml.data[0] = 1                       /* thread type */
-			ml.data[1] = 17                      /* word count */
-			ml.data[2+15] = uint32(Entryvalue()) /* start pc */
+			ml := newMachoLoad(5, 17+2)              /* unix thread */
+			ml.data[0] = 1                           /* thread type */
+			ml.data[1] = 17                          /* word count */
+			ml.data[2+15] = uint32(Entryvalue(ctxt)) /* start pc */
 
 		case sys.AMD64:
-			ml := newMachoLoad(5, 42+2)          /* unix thread */
-			ml.data[0] = 4                       /* thread type */
-			ml.data[1] = 42                      /* word count */
-			ml.data[2+32] = uint32(Entryvalue()) /* start pc */
-			ml.data[2+32+1] = uint32(Entryvalue() >> 32)
+			ml := newMachoLoad(5, 42+2)              /* unix thread */
+			ml.data[0] = 4                           /* thread type */
+			ml.data[1] = 42                          /* word count */
+			ml.data[2+32] = uint32(Entryvalue(ctxt)) /* start pc */
+			ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32)
 
 		case sys.ARM64:
-			ml := newMachoLoad(5, 68+2)          /* unix thread */
-			ml.data[0] = 6                       /* thread type */
-			ml.data[1] = 68                      /* word count */
-			ml.data[2+64] = uint32(Entryvalue()) /* start pc */
-			ml.data[2+64+1] = uint32(Entryvalue() >> 32)
+			ml := newMachoLoad(5, 68+2)              /* unix thread */
+			ml.data[0] = 6                           /* thread type */
+			ml.data[1] = 68                          /* word count */
+			ml.data[2+64] = uint32(Entryvalue(ctxt)) /* start pc */
+			ml.data[2+64+1] = uint32(Entryvalue(ctxt) >> 32)
 
 		case sys.I386:
-			ml := newMachoLoad(5, 16+2)          /* unix thread */
-			ml.data[0] = 1                       /* thread type */
-			ml.data[1] = 16                      /* word count */
-			ml.data[2+10] = uint32(Entryvalue()) /* start pc */
+			ml := newMachoLoad(5, 16+2)              /* unix thread */
+			ml.data[0] = 1                           /* thread type */
+			ml.data[1] = 16                          /* word count */
+			ml.data[2+10] = uint32(Entryvalue(ctxt)) /* start pc */
 		}
 	}
 
-	if Debug['d'] == 0 {
+	if !*FlagD {
 		// must match domacholink below
-		s1 := Linklookup(Ctxt, ".machosymtab", 0)
-		s2 := Linklookup(Ctxt, ".linkedit.plt", 0)
-		s3 := Linklookup(Ctxt, ".linkedit.got", 0)
-		s4 := Linklookup(Ctxt, ".machosymstr", 0)
+		s1 := ctxt.Syms.Lookup(".machosymtab", 0)
+		s2 := ctxt.Syms.Lookup(".linkedit.plt", 0)
+		s3 := ctxt.Syms.Lookup(".linkedit.got", 0)
+		s4 := ctxt.Syms.Lookup(".machosymstr", 0)
 
 		if Linkmode != LinkExternal {
 			ms := newMachoSeg("__LINKEDIT", 0)
-			ms.vaddr = uint64(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(INITRND)))
+			ms.vaddr = uint64(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(*FlagRound)))
 			ms.vsize = uint64(s1.Size) + uint64(s2.Size) + uint64(s3.Size) + uint64(s4.Size)
 			ms.fileoffset = uint64(linkoff)
 			ms.filesize = ms.vsize
@@ -562,7 +562,7 @@ func Asmbmacho() {
 		ml.data[2] = uint32(linkoff + s1.Size + s2.Size + s3.Size) /* stroff */
 		ml.data[3] = uint32(s4.Size)                               /* strsize */
 
-		machodysymtab()
+		machodysymtab(ctxt)
 
 		if Linkmode != LinkExternal {
 			ml := newMachoLoad(14, 6) /* LC_LOAD_DYLINKER */
@@ -602,7 +602,7 @@ func Asmbmacho() {
 	}
 }
 
-func symkind(s *LSym) int {
+func symkind(s *Symbol) int {
 	if s.Type == obj.SDYNIMPORT {
 		return SymKindUndef
 	}
@@ -612,7 +612,7 @@ func symkind(s *LSym) int {
 	return SymKindLocal
 }
 
-func addsym(s *LSym, name string, type_ int, addr int64, size int64, ver int, gotype *LSym) {
+func addsym(ctxt *Link, s *Symbol, name string, type_ SymbolType, addr int64, gotype *Symbol) {
 	if s == nil {
 		return
 	}
@@ -621,7 +621,7 @@ func addsym(s *LSym, name string, type_ int, addr int64, size int64, ver int, go
 	default:
 		return
 
-	case 'D', 'B', 'T':
+	case DataSym, BSSSym, TextSym:
 		break
 	}
 
@@ -633,7 +633,7 @@ func addsym(s *LSym, name string, type_ int, addr int64, size int64, ver int, go
 	nsortsym++
 }
 
-type machoscmp []*LSym
+type machoscmp []*Symbol
 
 func (x machoscmp) Len() int {
 	return len(x)
@@ -656,78 +656,83 @@ func (x machoscmp) Less(i, j int) bool {
 	return s1.Extname < s2.Extname
 }
 
-func machogenasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
-	genasmsym(put)
-	for _, s := range Ctxt.Allsym {
+func machogenasmsym(ctxt *Link) {
+	genasmsym(ctxt, addsym)
+	for _, s := range ctxt.Syms.Allsym {
 		if s.Type == obj.SDYNIMPORT || s.Type == obj.SHOSTOBJ {
 			if s.Attr.Reachable() {
-				put(s, "", 'D', 0, 0, 0, nil)
+				addsym(ctxt, s, "", DataSym, 0, nil)
 			}
 		}
 	}
 }
 
-func machosymorder() {
+func machosymorder(ctxt *Link) {
 	// On Mac OS X Mountain Lion, we must sort exported symbols
 	// So we sort them here and pre-allocate dynid for them
 	// See https://golang.org/issue/4029
 	for i := 0; i < len(dynexp); i++ {
 		dynexp[i].Attr |= AttrReachable
 	}
-	machogenasmsym(addsym)
-	sortsym = make([]*LSym, nsortsym)
+	machogenasmsym(ctxt)
+	sortsym = make([]*Symbol, nsortsym)
 	nsortsym = 0
-	machogenasmsym(addsym)
+	machogenasmsym(ctxt)
 	sort.Sort(machoscmp(sortsym[:nsortsym]))
 	for i := 0; i < nsortsym; i++ {
 		sortsym[i].Dynid = int32(i)
 	}
 }
 
-func machosymtab() {
-	symtab := Linklookup(Ctxt, ".machosymtab", 0)
-	symstr := Linklookup(Ctxt, ".machosymstr", 0)
+func machosymtab(ctxt *Link) {
+	symtab := ctxt.Syms.Lookup(".machosymtab", 0)
+	symstr := ctxt.Syms.Lookup(".machosymstr", 0)
 
 	for i := 0; i < nsortsym; i++ {
 		s := sortsym[i]
-		Adduint32(Ctxt, symtab, uint32(symstr.Size))
+		Adduint32(ctxt, symtab, uint32(symstr.Size))
 
-		// Only add _ to C symbols. Go symbols have dot in the name.
-		if !strings.Contains(s.Extname, ".") {
-			Adduint8(Ctxt, symstr, '_')
+		// In normal buildmodes, only add _ to C symbols, as
+		// Go symbols have dot in the name.
+		//
+		// When dynamically linking, prefix all non-local
+		// symbols with _ as dlsym on darwin requires it to
+		// resolve any symbol.
+		if !strings.Contains(s.Extname, ".") || (ctxt.DynlinkingGo() && !s.Attr.Local()) {
+			Adduint8(ctxt, symstr, '_')
 		}
 
 		// replace "·" as ".", because DTrace cannot handle it.
 		Addstring(symstr, strings.Replace(s.Extname, "·", ".", -1))
 
 		if s.Type == obj.SDYNIMPORT || s.Type == obj.SHOSTOBJ {
-			Adduint8(Ctxt, symtab, 0x01)                // type N_EXT, external symbol
-			Adduint8(Ctxt, symtab, 0)                   // no section
-			Adduint16(Ctxt, symtab, 0)                  // desc
-			adduintxx(Ctxt, symtab, 0, SysArch.PtrSize) // no value
+			Adduint8(ctxt, symtab, 0x01)                // type N_EXT, external symbol
+			Adduint8(ctxt, symtab, 0)                   // no section
+			Adduint16(ctxt, symtab, 0)                  // desc
+			adduintxx(ctxt, symtab, 0, SysArch.PtrSize) // no value
 		} else {
-			if s.Attr.CgoExport() {
-				Adduint8(Ctxt, symtab, 0x0f)
+			if s.Attr.CgoExport() || (ctxt.DynlinkingGo() && !s.Attr.Local()) {
+				Adduint8(ctxt, symtab, 0x0f)
 			} else {
-				Adduint8(Ctxt, symtab, 0x0e)
+				Adduint8(ctxt, symtab, 0x0e)
 			}
 			o := s
 			for o.Outer != nil {
 				o = o.Outer
 			}
 			if o.Sect == nil {
-				Diag("missing section for %s", s.Name)
-				Adduint8(Ctxt, symtab, 0)
+				Errorf(s, "missing section for symbol")
+				Adduint8(ctxt, symtab, 0)
 			} else {
-				Adduint8(Ctxt, symtab, uint8(o.Sect.Extnum))
+				Adduint8(ctxt, symtab, uint8(o.Sect.Extnum))
 			}
-			Adduint16(Ctxt, symtab, 0) // desc
-			adduintxx(Ctxt, symtab, uint64(Symaddr(s)), SysArch.PtrSize)
+			Adduint16(ctxt, symtab, 0) // desc
+			adduintxx(ctxt, symtab, uint64(Symaddr(s)), SysArch.PtrSize)
 		}
 	}
 }
 
-func machodysymtab() {
+func machodysymtab(ctxt *Link) {
 	ml := newMachoLoad(11, 18) /* LC_DYSYMTAB */
 
 	n := 0
@@ -750,10 +755,10 @@ func machodysymtab() {
 	ml.data[11] = 0 /* nextrefsyms */
 
 	// must match domacholink below
-	s1 := Linklookup(Ctxt, ".machosymtab", 0)
+	s1 := ctxt.Syms.Lookup(".machosymtab", 0)
 
-	s2 := Linklookup(Ctxt, ".linkedit.plt", 0)
-	s3 := Linklookup(Ctxt, ".linkedit.got", 0)
+	s2 := ctxt.Syms.Lookup(".linkedit.plt", 0)
+	s3 := ctxt.Syms.Lookup(".linkedit.got", 0)
 	ml.data[12] = uint32(linkoff + s1.Size)       /* indirectsymoff */
 	ml.data[13] = uint32((s2.Size + s3.Size) / 4) /* nindirectsyms */
 
@@ -763,15 +768,15 @@ func machodysymtab() {
 	ml.data[17] = 0 /* nlocrel */
 }
 
-func Domacholink() int64 {
-	machosymtab()
+func Domacholink(ctxt *Link) int64 {
+	machosymtab(ctxt)
 
 	// write data that will be linkedit section
-	s1 := Linklookup(Ctxt, ".machosymtab", 0)
+	s1 := ctxt.Syms.Lookup(".machosymtab", 0)
 
-	s2 := Linklookup(Ctxt, ".linkedit.plt", 0)
-	s3 := Linklookup(Ctxt, ".linkedit.got", 0)
-	s4 := Linklookup(Ctxt, ".machosymstr", 0)
+	s2 := ctxt.Syms.Lookup(".linkedit.plt", 0)
+	s3 := ctxt.Syms.Lookup(".linkedit.got", 0)
+	s4 := ctxt.Syms.Lookup(".machosymstr", 0)
 
 	// Force the linkedit section to end on a 16-byte
 	// boundary. This allows pure (non-cgo) Go binaries
@@ -791,13 +796,13 @@ func Domacholink() int64 {
 	// any alignment padding itself, working around the
 	// issue.
 	for s4.Size%16 != 0 {
-		Adduint8(Ctxt, s4, 0)
+		Adduint8(ctxt, s4, 0)
 	}
 
 	size := int(s1.Size + s2.Size + s3.Size + s4.Size)
 
 	if size > 0 {
-		linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(INITRND)) + Rnd(int64(Segdata.Filelen), int64(INITRND)) + Rnd(int64(Segdwarf.Filelen), int64(INITRND))
+		linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(*FlagRound)) + Rnd(int64(Segdata.Filelen), int64(*FlagRound)) + Rnd(int64(Segdwarf.Filelen), int64(*FlagRound))
 		Cseek(linkoff)
 
 		Cwrite(s1.P[:s1.Size])
@@ -806,16 +811,16 @@ func Domacholink() int64 {
 		Cwrite(s4.P[:s4.Size])
 	}
 
-	return Rnd(int64(size), int64(INITRND))
+	return Rnd(int64(size), int64(*FlagRound))
 }
 
-func machorelocsect(sect *Section, syms []*LSym) {
+func machorelocsect(ctxt *Link, sect *Section, syms []*Symbol) {
 	// If main section has no bits, nothing to relocate.
 	if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
 		return
 	}
 
-	sect.Reloff = uint64(Cpos())
+	sect.Reloff = uint64(coutbuf.Offset())
 	for i, s := range syms {
 		if !s.Attr.Reachable() {
 			continue
@@ -834,35 +839,40 @@ func machorelocsect(sect *Section, syms []*LSym) {
 		if sym.Value >= int64(eaddr) {
 			break
 		}
-		Ctxt.Cursym = sym
-
 		for ri := 0; ri < len(sym.R); ri++ {
 			r := &sym.R[ri]
 			if r.Done != 0 {
 				continue
 			}
-			if Thearch.Machoreloc1(r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 {
-				Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name)
+			if r.Xsym == nil {
+				Errorf(sym, "missing xsym in relocation")
+				continue
+			}
+			if !r.Xsym.Attr.Reachable() {
+				Errorf(sym, "unreachable reloc %v target %v", r.Type, r.Xsym.Name)
+			}
+			if Thearch.Machoreloc1(sym, r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 {
+				Errorf(sym, "unsupported obj reloc %v/%d to %s", r.Type, r.Siz, r.Sym.Name)
 			}
 		}
 	}
 
-	sect.Rellen = uint64(Cpos()) - sect.Reloff
+	sect.Rellen = uint64(coutbuf.Offset()) - sect.Reloff
 }
 
-func Machoemitreloc() {
-	for Cpos()&7 != 0 {
+func Machoemitreloc(ctxt *Link) {
+	for coutbuf.Offset()&7 != 0 {
 		Cput(0)
 	}
 
-	machorelocsect(Segtext.Sect, Ctxt.Textp)
+	machorelocsect(ctxt, Segtext.Sect, ctxt.Textp)
 	for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next {
-		machorelocsect(sect, datap)
+		machorelocsect(ctxt, sect, datap)
 	}
 	for sect := Segdata.Sect; sect != nil; sect = sect.Next {
-		machorelocsect(sect, datap)
+		machorelocsect(ctxt, sect, datap)
 	}
 	for sect := Segdwarf.Sect; sect != nil; sect = sect.Next {
-		machorelocsect(sect, list2slice(dwarfp))
+		machorelocsect(ctxt, sect, dwarfp)
 	}
 }
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
new file mode 100644
index 0000000..2fd92f6
--- /dev/null
+++ b/src/cmd/link/internal/ld/main.go
@@ -0,0 +1,264 @@
+// Inferno utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
+//	Portions Copyright © 1997-1999 Vita Nuova Limited
+//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+//	Portions Copyright © 2004,2006 Bruce Ellis
+//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
+//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+//	Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+import (
+	"bufio"
+	"cmd/internal/obj"
+	"cmd/internal/sys"
+	"flag"
+	"log"
+	"os"
+	"runtime"
+	"runtime/pprof"
+	"strings"
+)
+
+var (
+	pkglistfornote []byte
+)
+
+func init() {
+	flag.Var(&Linkmode, "linkmode", "set link `mode`")
+	flag.Var(&Buildmode, "buildmode", "set build `mode`")
+	flag.Var(&Headtype, "H", "set header `type`")
+	flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...")
+}
+
+// Flags used by the linker. The exported flags are used by the architecture-specific packages.
+var (
+	flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id")
+
+	flagOutfile    = flag.String("o", "", "write output to `file`")
+	flagPluginPath = flag.String("pluginpath", "", "full path name for plugin")
+	FlagLinkshared = flag.Bool("linkshared", false, "link against installed Go shared libraries")
+
+	flagInstallSuffix = flag.String("installsuffix", "", "set package directory `suffix`")
+	flagDumpDep       = flag.Bool("dumpdep", false, "dump symbol dependency graph")
+	flagRace          = flag.Bool("race", false, "enable race detector")
+	flagMsan          = flag.Bool("msan", false, "enable MSan interface")
+
+	flagFieldTrack = flag.String("k", "", "set field tracking `symbol`")
+	flagLibGCC     = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable")
+	flagTmpdir     = flag.String("tmpdir", "", "use `directory` for temporary files")
+
+	flagExtld      = flag.String("extld", "", "use `linker` when linking in external mode")
+	flagExtldflags = flag.String("extldflags", "", "pass `flags` to external linker")
+	flagExtar      = flag.String("extar", "", "archive program for buildmode=c-archive")
+
+	flagA           = flag.Bool("a", false, "disassemble output")
+	FlagC           = flag.Bool("c", false, "dump call graph")
+	FlagD           = flag.Bool("d", false, "disable dynamic executable")
+	flagF           = flag.Bool("f", false, "ignore version mismatch")
+	flagG           = flag.Bool("g", false, "disable go package data checks")
+	flagH           = flag.Bool("h", false, "halt on error")
+	flagN           = flag.Bool("n", false, "dump symbol table")
+	FlagS           = flag.Bool("s", false, "disable symbol table")
+	flagU           = flag.Bool("u", false, "reject unsafe packages")
+	FlagW           = flag.Bool("w", false, "disable DWARF generation")
+	Flag8           bool // use 64-bit addresses in symbol table
+	flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
+	FlagDebugTramp  = flag.Int("debugtramp", 0, "debug trampolines")
+
+	FlagRound       = flag.Int("R", -1, "set address rounding `quantum`")
+	FlagTextAddr    = flag.Int64("T", -1, "set text segment `address`")
+	FlagDataAddr    = flag.Int64("D", -1, "set data segment `address`")
+	flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
+
+	cpuprofile     = flag.String("cpuprofile", "", "write cpu profile to `file`")
+	memprofile     = flag.String("memprofile", "", "write memory profile to `file`")
+	memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
+)
+
+// Main is the main entry point for the linker code.
+func Main() {
+	ctxt := linknew(SysArch)
+	ctxt.Bso = bufio.NewWriter(os.Stdout)
+
+	// For testing behavior of go command when tools crash silently.
+	// Undocumented, not in standard flag parser to avoid
+	// exposing in usage message.
+	for _, arg := range os.Args {
+		if arg == "-crash_for_testing" {
+			os.Exit(2)
+		}
+	}
+
+	// TODO(matloob): define these above and then check flag values here
+	if SysArch.Family == sys.AMD64 && obj.GOOS == "plan9" {
+		flag.BoolVar(&Flag8, "8", false, "use 64-bit addresses in symbol table")
+	}
+	obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo)
+	obj.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) })
+	obj.Flagfn0("V", "print version and exit", doversion)
+	obj.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })
+	obj.Flagcount("v", "print link trace", &ctxt.Debugvlog)
+
+	obj.Flagparse(usage)
+
+	startProfile()
+	if Buildmode == BuildmodeUnset {
+		Buildmode = BuildmodeExe
+	}
+
+	if Buildmode != BuildmodeShared && flag.NArg() != 1 {
+		usage()
+	}
+
+	if *flagOutfile == "" {
+		*flagOutfile = "a.out"
+		if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
+			*flagOutfile += ".exe"
+		}
+	}
+
+	interpreter = *flagInterpreter
+
+	libinit(ctxt) // creates outfile
+
+	if Headtype == obj.Hunknown {
+		Headtype.Set(obj.GOOS)
+	}
+
+	ctxt.computeTLSOffset()
+	Thearch.Archinit(ctxt)
+
+	if *FlagLinkshared && !Iself {
+		Exitf("-linkshared can only be used on elf systems")
+	}
+
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", Headtype, uint64(*FlagTextAddr), uint64(*FlagDataAddr), uint32(*FlagRound))
+	}
+
+	switch Buildmode {
+	case BuildmodeShared:
+		for i := 0; i < flag.NArg(); i++ {
+			arg := flag.Arg(i)
+			parts := strings.SplitN(arg, "=", 2)
+			var pkgpath, file string
+			if len(parts) == 1 {
+				pkgpath, file = "main", arg
+			} else {
+				pkgpath, file = parts[0], parts[1]
+			}
+			pkglistfornote = append(pkglistfornote, pkgpath...)
+			pkglistfornote = append(pkglistfornote, '\n')
+			addlibpath(ctxt, "command line", "command line", file, pkgpath, "")
+		}
+	case BuildmodePlugin:
+		addlibpath(ctxt, "command line", "command line", flag.Arg(0), *flagPluginPath, "")
+	default:
+		addlibpath(ctxt, "command line", "command line", flag.Arg(0), "main", "")
+	}
+	ctxt.loadlib()
+
+	ctxt.checkstrdata()
+	deadcode(ctxt)
+	fieldtrack(ctxt)
+	ctxt.callgraph()
+
+	ctxt.doelf()
+	if Headtype == obj.Hdarwin {
+		ctxt.domacho()
+	}
+	ctxt.dostkcheck()
+	if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
+		ctxt.dope()
+	}
+	ctxt.addexport()
+	Thearch.Gentext(ctxt) // trampolines, call stubs, etc.
+	ctxt.textbuildid()
+	ctxt.textaddress()
+	ctxt.pclntab()
+	ctxt.findfunctab()
+	ctxt.typelink()
+	ctxt.symtab()
+	ctxt.dodata()
+	ctxt.address()
+	ctxt.reloc()
+	Thearch.Asmb(ctxt)
+	ctxt.undef()
+	ctxt.hostlink()
+	ctxt.archive()
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f cpu time\n", obj.Cputime())
+		ctxt.Logf("%d symbols\n", len(ctxt.Syms.Allsym))
+		ctxt.Logf("%d liveness data\n", liveness)
+	}
+
+	ctxt.Bso.Flush()
+
+	errorexit()
+}
+
+type Rpath struct {
+	set bool
+	val string
+}
+
+func (r *Rpath) Set(val string) error {
+	r.set = true
+	r.val = val
+	return nil
+}
+
+func (r *Rpath) String() string {
+	return r.val
+}
+
+func startProfile() {
+	if *cpuprofile != "" {
+		f, err := os.Create(*cpuprofile)
+		if err != nil {
+			log.Fatalf("%v", err)
+		}
+		if err := pprof.StartCPUProfile(f); err != nil {
+			log.Fatalf("%v", err)
+		}
+		AtExit(pprof.StopCPUProfile)
+	}
+	if *memprofile != "" {
+		if *memprofilerate != 0 {
+			runtime.MemProfileRate = int(*memprofilerate)
+		}
+		f, err := os.Create(*memprofile)
+		if err != nil {
+			log.Fatalf("%v", err)
+		}
+		AtExit(func() {
+			runtime.GC() // profile all outstanding allocations
+			if err := pprof.WriteHeapProfile(f); err != nil {
+				log.Fatalf("%v", err)
+			}
+		})
+	}
+}
diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go
index be9832d..7626a4f 100644
--- a/src/cmd/link/internal/ld/objfile.go
+++ b/src/cmd/link/internal/ld/objfile.go
@@ -44,17 +44,19 @@ package ld
 // A symbol reference is a string name followed by a version.
 //
 // A symbol points to other symbols using an index into the symbol
-// reference sequence. Index 0 corresponds to a nil LSym* pointer.
+// reference sequence. Index 0 corresponds to a nil Object* pointer.
 // In the symbol layout described below "symref index" stands for this
 // index.
 //
-// Each symbol is laid out as the following fields (taken from LSym*):
+// Each symbol is laid out as the following fields (taken from Object*):
 //
 //	- byte 0xfe (sanity check for synchronization)
 //	- type [int]
 //	- name & version [symref index]
 //	- flags [int]
-//		1 dupok
+//		1<<0 dupok
+//		1<<1 local
+//		1<<2 add to typelink table
 //	- size [int]
 //	- gotype [symref index]
 //	- p [data block]
@@ -111,6 +113,7 @@ import (
 	"bufio"
 	"bytes"
 	"cmd/internal/bio"
+	"cmd/internal/dwarf"
 	"cmd/internal/obj"
 	"crypto/sha1"
 	"encoding/base64"
@@ -129,34 +132,37 @@ var emptyPkg = []byte(`"".`)
 
 // objReader reads Go object files.
 type objReader struct {
-	rd   *bufio.Reader
-	ctxt *Link
-	pkg  string
-	pn   string
-	// List of symbol references for the file being read.
-	dupSym *LSym
+	rd              *bufio.Reader
+	ctxt            *Link
+	lib             *Library
+	pn              string
+	dupSym          *Symbol
+	localSymVersion int
 
 	// rdBuf is used by readString and readSymName as scratch for reading strings.
 	rdBuf []byte
 
-	refs        []*LSym
+	// List of symbol references for the file being read.
+	refs        []*Symbol
 	data        []byte
 	reloc       []Reloc
 	pcdata      []Pcdata
 	autom       []Auto
-	funcdata    []*LSym
+	funcdata    []*Symbol
 	funcdataoff []int64
-	file        []*LSym
+	file        []*Symbol
 }
 
-func LoadObjFile(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
+func LoadObjFile(ctxt *Link, f *bio.Reader, lib *Library, length int64, pn string) {
+
 	start := f.Offset()
 	r := &objReader{
-		rd:     f.Reader,
-		pkg:    pkg,
-		ctxt:   ctxt,
-		pn:     pn,
-		dupSym: &LSym{Name: ".dup"},
+		rd:              f.Reader,
+		lib:             lib,
+		ctxt:            ctxt,
+		pn:              pn,
+		dupSym:          &Symbol{Name: ".dup"},
+		localSymVersion: ctxt.Syms.IncVersion(),
 	}
 	r.loadObjFile()
 	if f.Offset() != start+length {
@@ -165,8 +171,7 @@ func LoadObjFile(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string)
 }
 
 func (r *objReader) loadObjFile() {
-	// Increment context version, versions are used to differentiate static files in different packages
-	r.ctxt.IncVersion()
+	pkg := pathtoprefix(r.lib.Pkg)
 
 	// Magic header
 	var buf [8]uint8
@@ -187,11 +192,14 @@ func (r *objReader) loadObjFile() {
 		if lib == "" {
 			break
 		}
-		addlib(r.ctxt, r.pkg, r.pn, lib)
+		l := addlib(r.ctxt, pkg, r.pn, lib)
+		if l != nil {
+			r.lib.imports = append(r.lib.imports, l)
+		}
 	}
 
 	// Symbol references
-	r.refs = []*LSym{nil} // zeroth ref is nil
+	r.refs = []*Symbol{nil} // zeroth ref is nil
 	for {
 		c, err := r.rd.Peek(1)
 		if err != nil {
@@ -240,10 +248,10 @@ func (r *objReader) readSlices() {
 	n = r.readInt()
 	r.autom = make([]Auto, n)
 	n = r.readInt()
-	r.funcdata = make([]*LSym, n)
+	r.funcdata = make([]*Symbol, n)
 	r.funcdataoff = make([]int64, n)
 	n = r.readInt()
-	r.file = make([]*LSym, n)
+	r.file = make([]*Symbol, n)
 }
 
 // Symbols are prefixed so their content doesn't get confused with the magic footer.
@@ -253,18 +261,20 @@ func (r *objReader) readSym() {
 	if c, err := r.rd.ReadByte(); c != symPrefix || err != nil {
 		log.Fatalln("readSym out of sync")
 	}
-	t := r.readInt()
+	t := obj.SymKind(r.readInt())
 	s := r.readSymIndex()
 	flags := r.readInt()
 	dupok := flags&1 != 0
 	local := flags&2 != 0
+	makeTypelink := flags&4 != 0
 	size := r.readInt()
 	typ := r.readSymIndex()
 	data := r.readData()
 	nreloc := r.readInt()
+	pkg := pathtoprefix(r.lib.Pkg)
 	isdup := false
 
-	var dup *LSym
+	var dup *Symbol
 	if s.Type != 0 && s.Type != obj.SXREF {
 		if (t == obj.SDATA || t == obj.SBSS || t == obj.SNOPTRBSS) && len(data) == 0 && nreloc == 0 {
 			if s.Size < int64(size) {
@@ -290,7 +300,7 @@ func (r *objReader) readSym() {
 	}
 
 overwrite:
-	s.File = r.pkg
+	s.File = pkg
 	if dupok {
 		s.Attr |= AttrDuplicateOK
 	}
@@ -301,13 +311,14 @@ overwrite:
 		log.Fatalf("missing type for %s in %s", s.Name, r.pn)
 	}
 	if t == obj.SBSS && (s.Type == obj.SRODATA || s.Type == obj.SNOPTRBSS) {
-		t = int(s.Type)
+		t = s.Type
 	}
-	s.Type = int16(t)
+	s.Type = t
 	if s.Size < int64(size) {
 		s.Size = int64(size)
 	}
 	s.Attr.Set(AttrLocal, local)
+	s.Attr.Set(AttrMakeTypelink, makeTypelink)
 	if typ != nil {
 		s.Gotype = typ
 	}
@@ -325,7 +336,7 @@ overwrite:
 			s.R[i] = Reloc{
 				Off:  r.readInt32(),
 				Siz:  r.readUint8(),
-				Type: r.readInt32(),
+				Type: obj.RelocType(r.readInt32()),
 				Add:  r.readInt64(),
 				Sym:  r.readSymIndex(),
 			}
@@ -393,12 +404,51 @@ overwrite:
 			pc.File[i] = r.readSymIndex()
 		}
 
-		if !isdup {
+		if !dupok {
 			if s.Attr.OnList() {
 				log.Fatalf("symbol %s listed multiple times", s.Name)
 			}
 			s.Attr |= AttrOnList
-			r.ctxt.Textp = append(r.ctxt.Textp, s)
+			r.lib.textp = append(r.lib.textp, s)
+		} else {
+			// there may ba a dup in another package
+			// put into a temp list and add to text later
+			if !isdup {
+				r.lib.dupTextSyms = append(r.lib.dupTextSyms, s)
+			} else {
+				r.lib.dupTextSyms = append(r.lib.dupTextSyms, dup)
+			}
+		}
+	}
+	if s.Type == obj.SDWARFINFO {
+		r.patchDWARFName(s)
+	}
+}
+
+func (r *objReader) patchDWARFName(s *Symbol) {
+	// This is kind of ugly. Really the package name should not
+	// even be included here.
+	if s.Size < 1 || s.P[0] != dwarf.DW_ABRV_FUNCTION {
+		return
+	}
+	e := bytes.IndexByte(s.P, 0)
+	if e == -1 {
+		return
+	}
+	p := bytes.Index(s.P[:e], emptyPkg)
+	if p == -1 {
+		return
+	}
+	pkgprefix := []byte(pathtoprefix(r.lib.Pkg) + ".")
+	patched := bytes.Replace(s.P[:e], emptyPkg, pkgprefix, -1)
+
+	s.P = append(patched, s.P[e:]...)
+	delta := int64(len(s.P)) - s.Size
+	s.Size = int64(len(s.P))
+	for i := range s.R {
+		r := &s.R[i]
+		if r.Off > int32(e) {
+			r.Off += int32(delta)
 		}
 	}
 }
@@ -420,9 +470,9 @@ func (r *objReader) readRef() {
 		log.Fatalf("invalid symbol version %d", v)
 	}
 	if v == 1 {
-		v = r.ctxt.Version
+		v = r.localSymVersion
 	}
-	s := Linklookup(r.ctxt, name, v)
+	s := r.ctxt.Syms.Lookup(name, v)
 	r.refs = append(r.refs, s)
 
 	if s == nil || v != 0 {
@@ -522,7 +572,7 @@ func (r *objReader) readData() []byte {
 
 // readSymName reads a symbol name, replacing all "". with pkg.
 func (r *objReader) readSymName() string {
-	pkg := r.pkg
+	pkg := pathtoprefix(r.lib.Pkg)
 	n := r.readInt()
 	if n == 0 {
 		r.readInt64()
@@ -553,7 +603,7 @@ func (r *objReader) readSymName() string {
 			}
 			r.rdBuf = adjName[:0] // in case 2*n wasn't enough
 
-			if DynlinkingGo() {
+			if Buildmode == BuildmodeShared || *FlagLinkshared {
 				// These types are included in the symbol
 				// table when dynamically linking. To keep
 				// binary size down, we replace the names
@@ -564,8 +614,10 @@ func (r *objReader) readSymName() string {
 				// the symbol is not decodable.
 				//
 				// Leave type.runtime. symbols alone, because
-				// other parts of the linker manipulates them.
-				if strings.HasPrefix(s, "type.") && !strings.HasPrefix(s, "type.runtime.") {
+				// other parts of the linker manipulates them,
+				// and also symbols whose names would not be
+				// shortened by this process.
+				if len(s) > 14 && strings.HasPrefix(s, "type.") && !strings.HasPrefix(s, "type.runtime.") {
 					hash := sha1.Sum([]byte(s))
 					prefix := "type."
 					if s[5] == '.' {
@@ -584,7 +636,7 @@ func (r *objReader) readSymName() string {
 }
 
 // Reads the index of a symbol reference and resolves it to a symbol
-func (r *objReader) readSymIndex() *LSym {
+func (r *objReader) readSymIndex() *Symbol {
 	i := r.readInt()
 	return r.refs[i]
 }
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 991b9ef..5a6c425 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -6,39 +6,11 @@ package ld
 
 import (
 	"cmd/internal/obj"
-	"fmt"
 	"log"
 	"os"
 	"path/filepath"
 )
 
-// funcpctab writes to dst a pc-value table mapping the code in func to the values
-// returned by valfunc parameterized by arg. The invocation of valfunc to update the
-// current value is, for each p,
-//
-//	val = valfunc(func, val, p, 0, arg);
-//	record val as value at p->pc;
-//	val = valfunc(func, val, p, 1, arg);
-//
-// where func is the function, val is the current value, p is the instruction being
-// considered, and arg can be used to further parameterize valfunc.
-
-// pctofileline computes either the file number (arg == 0)
-// or the line number (arg == 1) to use at p.
-// Because p->lineno applies to p, phase == 0 (before p)
-// takes care of the update.
-
-// pctospadj computes the sp adjustment in effect.
-// It is oldval plus any adjustment made by p itself.
-// The adjustment by p takes effect only after p, so we
-// apply the change during phase == 1.
-
-// pctopcdata computes the pcdata value in effect at p.
-// A PCDATA instruction sets the value in effect at future
-// non-PCDATA instructions.
-// Since PCDATA instructions have no width in the final code,
-// it does not matter which phase we use for the update.
-
 // iteration over encoded pcdata tables.
 
 func getvarint(pp *[]byte) uint32 {
@@ -97,10 +69,6 @@ func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) {
 	pciternext(it)
 }
 
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 func addvarint(d *Pcdata, val uint32) {
 	n := int32(0)
 	for v := val; v >= 0x80; v >>= 7 {
@@ -123,25 +91,25 @@ func addvarint(d *Pcdata, val uint32) {
 	p[0] = byte(v)
 }
 
-func addpctab(ftab *LSym, off int32, d *Pcdata) int32 {
+func addpctab(ctxt *Link, ftab *Symbol, off int32, d *Pcdata) int32 {
 	var start int32
 	if len(d.P) > 0 {
 		start = int32(len(ftab.P))
-		Addbytes(Ctxt, ftab, d.P)
+		Addbytes(ftab, d.P)
 	}
-	return int32(setuint32(Ctxt, ftab, int64(off), uint32(start)))
+	return int32(setuint32(ctxt, ftab, int64(off), uint32(start)))
 }
 
-func ftabaddstring(ftab *LSym, s string) int32 {
+func ftabaddstring(ctxt *Link, ftab *Symbol, s string) int32 {
 	n := int32(len(s)) + 1
 	start := int32(len(ftab.P))
-	Symgrow(Ctxt, ftab, int64(start)+int64(n)+1)
+	Symgrow(ftab, int64(start)+int64(n)+1)
 	copy(ftab.P[start:], s)
 	return start
 }
 
-func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) {
-	var f *LSym
+func renumberfiles(ctxt *Link, files []*Symbol, d *Pcdata) {
+	var f *Symbol
 
 	// Give files numbers.
 	for i := 0; i < len(files); i++ {
@@ -186,7 +154,7 @@ func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) {
 	*d = out
 }
 
-func container(s *LSym) int {
+func container(s *Symbol) int {
 	// We want to generate func table entries only for the "lowest level" symbols,
 	// not containers of subsymbols.
 	if s != nil && s.Type&obj.SCONTAINER != 0 {
@@ -198,18 +166,18 @@ func container(s *LSym) int {
 // pclntab initializes the pclntab symbol with
 // runtime function and file name information.
 
-var pclntab_zpcln FuncInfo
+var pclntabZpcln FuncInfo
 
 // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
 var pclntabNfunc int32
 var pclntabFiletabOffset int32
 var pclntabPclntabOffset int32
-var pclntabFirstFunc *LSym
-var pclntabLastFunc *LSym
+var pclntabFirstFunc *Symbol
+var pclntabLastFunc *Symbol
 
-func pclntab() {
-	funcdata_bytes := int64(0)
-	ftab := Linklookup(Ctxt, "runtime.pclntab", 0)
+func (ctxt *Link) pclntab() {
+	funcdataBytes := int64(0)
+	ftab := ctxt.Syms.Lookup("runtime.pclntab", 0)
 	ftab.Type = obj.SPCLNTAB
 	ftab.Attr |= AttrReachable
 
@@ -222,47 +190,47 @@ func pclntab() {
 	nfunc := int32(0)
 
 	// Find container symbols, mark them with SCONTAINER
-	for _, s := range Ctxt.Textp {
+	for _, s := range ctxt.Textp {
 		if s.Outer != nil {
 			s.Outer.Type |= obj.SCONTAINER
 		}
 	}
 
-	for _, s := range Ctxt.Textp {
+	for _, s := range ctxt.Textp {
 		if container(s) == 0 {
 			nfunc++
 		}
 	}
 
 	pclntabNfunc = nfunc
-	Symgrow(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize)+4)
-	setuint32(Ctxt, ftab, 0, 0xfffffffb)
-	setuint8(Ctxt, ftab, 6, uint8(SysArch.MinLC))
-	setuint8(Ctxt, ftab, 7, uint8(SysArch.PtrSize))
-	setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(SysArch.PtrSize))
+	Symgrow(ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize)+4)
+	setuint32(ctxt, ftab, 0, 0xfffffffb)
+	setuint8(ctxt, ftab, 6, uint8(SysArch.MinLC))
+	setuint8(ctxt, ftab, 7, uint8(SysArch.PtrSize))
+	setuintxx(ctxt, ftab, 8, uint64(nfunc), int64(SysArch.PtrSize))
 	pclntabPclntabOffset = int32(8 + SysArch.PtrSize)
 
 	nfunc = 0
-	var last *LSym
-	for _, Ctxt.Cursym = range Ctxt.Textp {
-		last = Ctxt.Cursym
-		if container(Ctxt.Cursym) != 0 {
+	var last *Symbol
+	for _, s := range ctxt.Textp {
+		last = s
+		if container(s) != 0 {
 			continue
 		}
-		pcln := Ctxt.Cursym.FuncInfo
+		pcln := s.FuncInfo
 		if pcln == nil {
-			pcln = &pclntab_zpcln
+			pcln = &pclntabZpcln
 		}
 
 		if pclntabFirstFunc == nil {
-			pclntabFirstFunc = Ctxt.Cursym
+			pclntabFirstFunc = s
 		}
 
 		funcstart := int32(len(ftab.P))
 		funcstart += int32(-len(ftab.P)) & (int32(SysArch.PtrSize) - 1)
 
-		setaddr(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize), Ctxt.Cursym)
-		setuintxx(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize), uint64(funcstart), int64(SysArch.PtrSize))
+		setaddr(ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize), s)
+		setuintxx(ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize), uint64(funcstart), int64(SysArch.PtrSize))
 
 		// fixed size of struct, checked below
 		off := funcstart
@@ -271,37 +239,37 @@ func pclntab() {
 		if len(pcln.Funcdata) > 0 && (end&int32(SysArch.PtrSize-1) != 0) {
 			end += 4
 		}
-		Symgrow(Ctxt, ftab, int64(end))
+		Symgrow(ftab, int64(end))
 
 		// entry uintptr
-		off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym))
+		off = int32(setaddr(ctxt, ftab, int64(off), s))
 
 		// name int32
-		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name))))
+		off = int32(setuint32(ctxt, ftab, int64(off), uint32(ftabaddstring(ctxt, ftab, s.Name))))
 
 		// args int32
 		// TODO: Move into funcinfo.
 		args := uint32(0)
-		if Ctxt.Cursym.FuncInfo != nil {
-			args = uint32(Ctxt.Cursym.FuncInfo.Args)
+		if s.FuncInfo != nil {
+			args = uint32(s.FuncInfo.Args)
 		}
-		off = int32(setuint32(Ctxt, ftab, int64(off), args))
+		off = int32(setuint32(ctxt, ftab, int64(off), args))
 
 		// frame int32
 		// This has been removed (it was never set quite correctly anyway).
 		// Nothing should use it.
 		// Leave an obviously incorrect value.
 		// TODO: Remove entirely.
-		off = int32(setuint32(Ctxt, ftab, int64(off), 0x1234567))
+		off = int32(setuint32(ctxt, ftab, int64(off), 0x1234567))
 
-		if pcln != &pclntab_zpcln {
-			renumberfiles(Ctxt, pcln.File, &pcln.Pcfile)
+		if pcln != &pclntabZpcln {
+			renumberfiles(ctxt, pcln.File, &pcln.Pcfile)
 			if false {
 				// Sanity check the new numbering
 				var it Pciter
-				for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) {
-					if it.value < 1 || it.value > int32(len(Ctxt.Filesyms)) {
-						Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, len(Ctxt.Filesyms))
+				for pciterinit(ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) {
+					if it.value < 1 || it.value > int32(len(ctxt.Filesyms)) {
+						Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.value, len(ctxt.Filesyms))
 						errorexit()
 					}
 				}
@@ -309,14 +277,14 @@ func pclntab() {
 		}
 
 		// pcdata
-		off = addpctab(ftab, off, &pcln.Pcsp)
+		off = addpctab(ctxt, ftab, off, &pcln.Pcsp)
 
-		off = addpctab(ftab, off, &pcln.Pcfile)
-		off = addpctab(ftab, off, &pcln.Pcline)
-		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(len(pcln.Pcdata))))
-		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(len(pcln.Funcdata))))
+		off = addpctab(ctxt, ftab, off, &pcln.Pcfile)
+		off = addpctab(ctxt, ftab, off, &pcln.Pcline)
+		off = int32(setuint32(ctxt, ftab, int64(off), uint32(len(pcln.Pcdata))))
+		off = int32(setuint32(ctxt, ftab, int64(off), uint32(len(pcln.Funcdata))))
 		for i := 0; i < len(pcln.Pcdata); i++ {
-			off = addpctab(ftab, off, &pcln.Pcdata[i])
+			off = addpctab(ctxt, ftab, off, &pcln.Pcdata[i])
 		}
 
 		// funcdata, must be pointer-aligned and we're only int32-aligned.
@@ -327,12 +295,12 @@ func pclntab() {
 			}
 			for i := 0; i < len(pcln.Funcdata); i++ {
 				if pcln.Funcdata[i] == nil {
-					setuintxx(Ctxt, ftab, int64(off)+int64(SysArch.PtrSize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(SysArch.PtrSize))
+					setuintxx(ctxt, ftab, int64(off)+int64(SysArch.PtrSize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(SysArch.PtrSize))
 				} else {
 					// TODO: Dedup.
-					funcdata_bytes += pcln.Funcdata[i].Size
+					funcdataBytes += pcln.Funcdata[i].Size
 
-					setaddrplus(Ctxt, ftab, int64(off)+int64(SysArch.PtrSize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i])
+					setaddrplus(ctxt, ftab, int64(off)+int64(SysArch.PtrSize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i])
 				}
 			}
 
@@ -340,7 +308,7 @@ func pclntab() {
 		}
 
 		if off != end {
-			Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcln.Pcdata), len(pcln.Funcdata), SysArch.PtrSize)
+			Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcln.Pcdata), len(pcln.Funcdata), SysArch.PtrSize)
 			errorexit()
 		}
 
@@ -349,33 +317,33 @@ func pclntab() {
 
 	pclntabLastFunc = last
 	// Final entry of table is just end pc.
-	setaddrplus(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize), last, last.Size)
+	setaddrplus(ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize), last, last.Size)
 
 	// Start file table.
 	start := int32(len(ftab.P))
 
 	start += int32(-len(ftab.P)) & (int32(SysArch.PtrSize) - 1)
 	pclntabFiletabOffset = start
-	setuint32(Ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize), uint32(start))
+	setuint32(ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize), uint32(start))
 
-	Symgrow(Ctxt, ftab, int64(start)+(int64(len(Ctxt.Filesyms))+1)*4)
-	setuint32(Ctxt, ftab, int64(start), uint32(len(Ctxt.Filesyms)))
-	for i := len(Ctxt.Filesyms) - 1; i >= 0; i-- {
-		s := Ctxt.Filesyms[i]
-		setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name)))
+	Symgrow(ftab, int64(start)+(int64(len(ctxt.Filesyms))+1)*4)
+	setuint32(ctxt, ftab, int64(start), uint32(len(ctxt.Filesyms)+1))
+	for i := len(ctxt.Filesyms) - 1; i >= 0; i-- {
+		s := ctxt.Filesyms[i]
+		setuint32(ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ctxt, ftab, s.Name)))
 	}
 
 	ftab.Size = int64(len(ftab.P))
 
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), ftab.Size, funcdata_bytes)
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), ftab.Size, funcdataBytes)
 	}
 }
 
 func expandGoroot(s string) string {
 	const n = len("$GOROOT")
 	if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') {
-		root := goroot
+		root := obj.GOROOT
 		if final := os.Getenv("GOROOT_FINAL"); final != "" {
 			root = final
 		}
@@ -393,16 +361,16 @@ const (
 
 // findfunctab generates a lookup table to quickly find the containing
 // function for a pc. See src/runtime/symtab.go:findfunc for details.
-func findfunctab() {
-	t := Linklookup(Ctxt, "runtime.findfunctab", 0)
+func (ctxt *Link) findfunctab() {
+	t := ctxt.Syms.Lookup("runtime.findfunctab", 0)
 	t.Type = obj.SRODATA
 	t.Attr |= AttrReachable
 	t.Attr |= AttrLocal
 
 	// find min and max address
-	min := Ctxt.Textp[0].Value
+	min := ctxt.Textp[0].Value
 	max := int64(0)
-	for _, s := range Ctxt.Textp {
+	for _, s := range ctxt.Textp {
 		max = s.Value + s.Size
 	}
 
@@ -415,18 +383,18 @@ func findfunctab() {
 		indexes[i] = NOIDX
 	}
 	idx := int32(0)
-	for i, s := range Ctxt.Textp {
+	for i, s := range ctxt.Textp {
 		if container(s) != 0 {
 			continue
 		}
 		p := s.Value
-		var e *LSym
+		var e *Symbol
 		i++
-		if i < len(Ctxt.Textp) {
-			e = Ctxt.Textp[i]
+		if i < len(ctxt.Textp) {
+			e = ctxt.Textp[i]
 		}
-		for container(e) != 0 && i < len(Ctxt.Textp) {
-			e = Ctxt.Textp[i]
+		for container(e) != 0 && i < len(ctxt.Textp) {
+			e = ctxt.Textp[i]
 			i++
 		}
 		q := max
@@ -452,25 +420,25 @@ func findfunctab() {
 	// allocate table
 	nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
 
-	Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n))
+	Symgrow(t, 4*int64(nbuckets)+int64(n))
 
 	// fill in table
 	for i := int32(0); i < nbuckets; i++ {
 		base := indexes[i*SUBBUCKETS]
 		if base == NOIDX {
-			Diag("hole in findfunctab")
+			Errorf(nil, "hole in findfunctab")
 		}
-		setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base))
+		setuint32(ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base))
 		for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
 			idx = indexes[i*SUBBUCKETS+j]
 			if idx == NOIDX {
-				Diag("hole in findfunctab")
+				Errorf(nil, "hole in findfunctab")
 			}
 			if idx-base >= 256 {
-				Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
+				Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
 			}
 
-			setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
+			setuint8(ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
 		}
 	}
 }
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
index 839aa6c..517ed6c 100644
--- a/src/cmd/link/internal/ld/pe.go
+++ b/src/cmd/link/internal/ld/pe.go
@@ -324,7 +324,7 @@ var dosstub = []uint8{
 	0x00,
 }
 
-var rsrcsym *LSym
+var rsrcsym *Symbol
 
 var strtbl []byte
 
@@ -357,7 +357,7 @@ var sh [16]IMAGE_SECTION_HEADER
 var dd []IMAGE_DATA_DIRECTORY
 
 type Imp struct {
-	s       *LSym
+	s       *Symbol
 	off     uint64
 	next    *Imp
 	argsize int
@@ -373,13 +373,13 @@ type Dll struct {
 
 var dr *Dll
 
-var dexport [1024]*LSym
+var dexport [1024]*Symbol
 
 var nexport int
 
-func addpesection(name string, sectsize int, filesize int) *IMAGE_SECTION_HEADER {
+func addpesection(ctxt *Link, name string, sectsize int, filesize int) *IMAGE_SECTION_HEADER {
 	if pensect == 16 {
-		Diag("too many sections")
+		Errorf(nil, "too many sections")
 		errorexit()
 	}
 
@@ -398,26 +398,26 @@ func addpesection(name string, sectsize int, filesize int) *IMAGE_SECTION_HEADER
 	return h
 }
 
-func chksectoff(h *IMAGE_SECTION_HEADER, off int64) {
+func chksectoff(ctxt *Link, h *IMAGE_SECTION_HEADER, off int64) {
 	if off != int64(h.PointerToRawData) {
-		Diag("%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(off))
+		Errorf(nil, "%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(off))
 		errorexit()
 	}
 }
 
-func chksectseg(h *IMAGE_SECTION_HEADER, s *Segment) {
+func chksectseg(ctxt *Link, h *IMAGE_SECTION_HEADER, s *Segment) {
 	if s.Vaddr-PEBASE != uint64(h.VirtualAddress) {
-		Diag("%s.VirtualAddress = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.VirtualAddress)), uint64(int64(s.Vaddr-PEBASE)))
+		Errorf(nil, "%s.VirtualAddress = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.VirtualAddress)), uint64(int64(s.Vaddr-PEBASE)))
 		errorexit()
 	}
 
 	if s.Fileoff != uint64(h.PointerToRawData) {
-		Diag("%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(int64(s.Fileoff)))
+		Errorf(nil, "%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(int64(s.Fileoff)))
 		errorexit()
 	}
 }
 
-func Peinit() {
+func Peinit(ctxt *Link) {
 	var l int
 
 	switch SysArch.Family {
@@ -441,9 +441,9 @@ func Peinit() {
 	nextfileoff = int(PEFILEHEADR)
 
 	// some mingw libs depend on this symbol, for example, FindPESectionByName
-	xdefine("__image_base__", obj.SDATA, PEBASE)
+	ctxt.xdefine("__image_base__", obj.SDATA, PEBASE)
 
-	xdefine("_image_base__", obj.SDATA, PEBASE)
+	ctxt.xdefine("_image_base__", obj.SDATA, PEBASE)
 }
 
 func pewrite() {
@@ -472,12 +472,12 @@ func strput(s string) {
 	}
 }
 
-func initdynimport() *Dll {
+func initdynimport(ctxt *Link) *Dll {
 	var d *Dll
 
 	dr = nil
 	var m *Imp
-	for _, s := range Ctxt.Allsym {
+	for _, s := range ctxt.Syms.Allsym {
 		if !s.Attr.Reachable() || s.Type != obj.SDYNIMPORT {
 			continue
 		}
@@ -505,7 +505,7 @@ func initdynimport() *Dll {
 			var err error
 			m.argsize, err = strconv.Atoi(s.Extname[i+1:])
 			if err != nil {
-				Diag("failed to parse stdcall decoration: %v", err)
+				Errorf(s, "failed to parse stdcall decoration: %v", err)
 			}
 			m.argsize *= SysArch.PtrSize
 			s.Extname = s.Extname[:i]
@@ -521,13 +521,13 @@ func initdynimport() *Dll {
 		for d := dr; d != nil; d = d.next {
 			for m = d.ms; m != nil; m = m.next {
 				m.s.Type = obj.SDATA
-				Symgrow(Ctxt, m.s, int64(SysArch.PtrSize))
+				Symgrow(m.s, int64(SysArch.PtrSize))
 				dynName := m.s.Extname
 				// only windows/386 requires stdcall decoration
 				if SysArch.Family == sys.I386 && m.argsize >= 0 {
 					dynName += fmt.Sprintf("@%d", m.argsize)
 				}
-				dynSym := Linklookup(Ctxt, dynName, 0)
+				dynSym := ctxt.Syms.Lookup(dynName, 0)
 				dynSym.Attr |= AttrReachable
 				dynSym.Type = obj.SHOSTOBJ
 				r := Addrel(m.s)
@@ -538,7 +538,7 @@ func initdynimport() *Dll {
 			}
 		}
 	} else {
-		dynamic := Linklookup(Ctxt, ".windynamic", 0)
+		dynamic := ctxt.Syms.Lookup(".windynamic", 0)
 		dynamic.Attr |= AttrReachable
 		dynamic.Type = obj.SWINDOWS
 		for d := dr; d != nil; d = d.next {
@@ -569,9 +569,9 @@ func peimporteddlls() []string {
 	return dlls
 }
 
-func addimports(datsect *IMAGE_SECTION_HEADER) {
-	startoff := Cpos()
-	dynamic := Linklookup(Ctxt, ".windynamic", 0)
+func addimports(ctxt *Link, datsect *IMAGE_SECTION_HEADER) {
+	startoff := coutbuf.Offset()
+	dynamic := ctxt.Syms.Lookup(".windynamic", 0)
 
 	// skip import descriptor table (will write it later)
 	n := uint64(0)
@@ -583,7 +583,7 @@ func addimports(datsect *IMAGE_SECTION_HEADER) {
 
 	// write dll names
 	for d := dr; d != nil; d = d.next {
-		d.nameoff = uint64(Cpos()) - uint64(startoff)
+		d.nameoff = uint64(coutbuf.Offset()) - uint64(startoff)
 		strput(d.name)
 	}
 
@@ -591,18 +591,18 @@ func addimports(datsect *IMAGE_SECTION_HEADER) {
 	var m *Imp
 	for d := dr; d != nil; d = d.next {
 		for m = d.ms; m != nil; m = m.next {
-			m.off = uint64(nextsectoff) + uint64(Cpos()) - uint64(startoff)
+			m.off = uint64(nextsectoff) + uint64(coutbuf.Offset()) - uint64(startoff)
 			Wputl(0) // hint
 			strput(m.s.Extname)
 		}
 	}
 
 	// write OriginalFirstThunks
-	oftbase := uint64(Cpos()) - uint64(startoff)
+	oftbase := uint64(coutbuf.Offset()) - uint64(startoff)
 
-	n = uint64(Cpos())
+	n = uint64(coutbuf.Offset())
 	for d := dr; d != nil; d = d.next {
-		d.thunkoff = uint64(Cpos()) - n
+		d.thunkoff = uint64(coutbuf.Offset()) - n
 		for m = d.ms; m != nil; m = m.next {
 			if pe64 != 0 {
 				Vputl(m.off)
@@ -619,13 +619,13 @@ func addimports(datsect *IMAGE_SECTION_HEADER) {
 	}
 
 	// add pe section and pad it at the end
-	n = uint64(Cpos()) - uint64(startoff)
+	n = uint64(coutbuf.Offset()) - uint64(startoff)
 
-	isect := addpesection(".idata", int(n), int(n))
+	isect := addpesection(ctxt, ".idata", int(n), int(n))
 	isect.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
-	chksectoff(isect, startoff)
+	chksectoff(ctxt, isect, startoff)
 	strnput("", int(uint64(isect.SizeOfRawData)-n))
-	endoff := Cpos()
+	endoff := coutbuf.Offset()
 
 	// write FirstThunks (allocated in .data section)
 	ftbase := uint64(dynamic.Value) - uint64(datsect.VirtualAddress) - PEBASE
@@ -666,7 +666,6 @@ func addimports(datsect *IMAGE_SECTION_HEADER) {
 
 	// update data directory
 	dd[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.VirtualAddress
-
 	dd[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.VirtualSize
 	dd[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE)
 	dd[IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size)
@@ -674,20 +673,20 @@ func addimports(datsect *IMAGE_SECTION_HEADER) {
 	Cseek(endoff)
 }
 
-type byExtname []*LSym
+type byExtname []*Symbol
 
 func (s byExtname) Len() int           { return len(s) }
 func (s byExtname) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
 func (s byExtname) Less(i, j int) bool { return s[i].Extname < s[j].Extname }
 
-func initdynexport() {
+func initdynexport(ctxt *Link) {
 	nexport = 0
-	for _, s := range Ctxt.Allsym {
+	for _, s := range ctxt.Syms.Allsym {
 		if !s.Attr.Reachable() || !s.Attr.CgoExportDynamic() {
 			continue
 		}
 		if nexport+1 > len(dexport) {
-			Diag("pe dynexport table is full")
+			Errorf(s, "pe dynexport table is full")
 			errorexit()
 		}
 
@@ -698,10 +697,10 @@ func initdynexport() {
 	sort.Sort(byExtname(dexport[:nexport]))
 }
 
-func addexports() {
+func addexports(ctxt *Link) {
 	var e IMAGE_EXPORT_DIRECTORY
 
-	size := binary.Size(&e) + 10*nexport + len(outfile) + 1
+	size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1
 	for i := 0; i < nexport; i++ {
 		size += len(dexport[i].Extname) + 1
 	}
@@ -710,16 +709,16 @@ func addexports() {
 		return
 	}
 
-	sect := addpesection(".edata", size, size)
+	sect := addpesection(ctxt, ".edata", size, size)
 	sect.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
-	chksectoff(sect, Cpos())
+	chksectoff(ctxt, sect, coutbuf.Offset())
 	va := int(sect.VirtualAddress)
 	dd[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va)
 	dd[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.VirtualSize
 
-	va_name := va + binary.Size(&e) + nexport*4
-	va_addr := va + binary.Size(&e)
-	va_na := va + binary.Size(&e) + nexport*8
+	vaName := va + binary.Size(&e) + nexport*4
+	vaAddr := va + binary.Size(&e)
+	vaNa := va + binary.Size(&e) + nexport*8
 
 	e.Characteristics = 0
 	e.MajorVersion = 0
@@ -728,9 +727,9 @@ func addexports() {
 	e.NumberOfNames = uint32(nexport)
 	e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names.
 	e.Base = 1
-	e.AddressOfFunctions = uint32(va_addr)
-	e.AddressOfNames = uint32(va_name)
-	e.AddressOfNameOrdinals = uint32(va_na)
+	e.AddressOfFunctions = uint32(vaAddr)
+	e.AddressOfNames = uint32(vaName)
+	e.AddressOfNameOrdinals = uint32(vaNa)
 
 	// put IMAGE_EXPORT_DIRECTORY
 	binary.Write(&coutbuf, binary.LittleEndian, &e)
@@ -741,7 +740,7 @@ func addexports() {
 	}
 
 	// put EXPORT Name Pointer Table
-	v := int(e.Name + uint32(len(outfile)) + 1)
+	v := int(e.Name + uint32(len(*flagOutfile)) + 1)
 
 	for i := 0; i < nexport; i++ {
 		Lputl(uint32(v))
@@ -754,7 +753,7 @@ func addexports() {
 	}
 
 	// put Names
-	strnput(outfile, len(outfile)+1)
+	strnput(*flagOutfile, len(*flagOutfile)+1)
 
 	for i := 0; i < nexport; i++ {
 		strnput(dexport[i].Extname, len(dexport[i].Extname)+1)
@@ -764,7 +763,7 @@ func addexports() {
 
 // perelocsect relocates symbols from first in section sect, and returns
 // the total number of relocations emitted.
-func perelocsect(sect *Section, syms []*LSym) int {
+func perelocsect(ctxt *Link, sect *Section, syms []*Symbol) int {
 	// If main section has no bits, nothing to relocate.
 	if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
 		return 0
@@ -772,7 +771,7 @@ func perelocsect(sect *Section, syms []*LSym) int {
 
 	relocs := 0
 
-	sect.Reloff = uint64(Cpos())
+	sect.Reloff = uint64(coutbuf.Offset())
 	for i, s := range syms {
 		if !s.Attr.Reachable() {
 			continue
@@ -791,52 +790,50 @@ func perelocsect(sect *Section, syms []*LSym) int {
 		if sym.Value >= int64(eaddr) {
 			break
 		}
-		Ctxt.Cursym = sym
-
 		for ri := 0; ri < len(sym.R); ri++ {
 			r := &sym.R[ri]
 			if r.Done != 0 {
 				continue
 			}
 			if r.Xsym == nil {
-				Diag("missing xsym in relocation")
+				Errorf(sym, "missing xsym in relocation")
 				continue
 			}
 
 			if r.Xsym.Dynid < 0 {
-				Diag("reloc %d to non-coff symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type)
+				Errorf(sym, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type)
 			}
-			if !Thearch.PEreloc1(r, int64(uint64(sym.Value+int64(r.Off))-PEBASE)) {
-				Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name)
+			if !Thearch.PEreloc1(sym, r, int64(uint64(sym.Value+int64(r.Off))-PEBASE)) {
+				Errorf(sym, "unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name)
 			}
 
 			relocs++
 		}
 	}
 
-	sect.Rellen = uint64(Cpos()) - sect.Reloff
+	sect.Rellen = uint64(coutbuf.Offset()) - sect.Reloff
 
 	return relocs
 }
 
 // peemitreloc emits relocation entries for go.o in external linking.
-func peemitreloc(text, data, ctors *IMAGE_SECTION_HEADER) {
-	for Cpos()&7 != 0 {
+func peemitreloc(ctxt *Link, text, data, ctors *IMAGE_SECTION_HEADER) {
+	for coutbuf.Offset()&7 != 0 {
 		Cput(0)
 	}
 
-	text.PointerToRelocations = uint32(Cpos())
+	text.PointerToRelocations = uint32(coutbuf.Offset())
 	// first entry: extended relocs
 	Lputl(0) // placeholder for number of relocation + 1
 	Lputl(0)
 	Wputl(0)
 
-	n := perelocsect(Segtext.Sect, Ctxt.Textp) + 1
+	n := perelocsect(ctxt, Segtext.Sect, ctxt.Textp) + 1
 	for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next {
-		n += perelocsect(sect, datap)
+		n += perelocsect(ctxt, sect, datap)
 	}
 
-	cpos := Cpos()
+	cpos := coutbuf.Offset()
 	Cseek(int64(text.PointerToRelocations))
 	Lputl(uint32(n))
 	Cseek(cpos)
@@ -856,10 +853,10 @@ func peemitreloc(text, data, ctors *IMAGE_SECTION_HEADER) {
 
 	n = 1
 	for sect := Segdata.Sect; sect != nil; sect = sect.Next {
-		n += perelocsect(sect, datap)
+		n += perelocsect(ctxt, sect, datap)
 	}
 
-	cpos = Cpos()
+	cpos = coutbuf.Offset()
 	Cseek(int64(data.PointerToRelocations))
 	Lputl(uint32(n))
 	Cseek(cpos)
@@ -871,15 +868,15 @@ func peemitreloc(text, data, ctors *IMAGE_SECTION_HEADER) {
 	}
 	data.NumberOfRelocations = uint16(n - 1)
 
-	dottext := Linklookup(Ctxt, ".text", 0)
+	dottext := ctxt.Syms.Lookup(".text", 0)
 	ctors.NumberOfRelocations = 1
-	ctors.PointerToRelocations = uint32(Cpos())
+	ctors.PointerToRelocations = uint32(coutbuf.Offset())
 	sectoff := ctors.VirtualAddress
 	Lputl(sectoff)
 	Lputl(uint32(dottext.Dynid))
-	switch obj.Getgoarch() {
+	switch obj.GOARCH {
 	default:
-		fmt.Fprintf(os.Stderr, "link: unknown architecture for PE: %q\n", obj.Getgoarch())
+		fmt.Fprintf(os.Stderr, "link: unknown architecture for PE: %q\n", obj.GOARCH)
 		os.Exit(2)
 	case "386":
 		Wputl(IMAGE_REL_I386_DIR32)
@@ -888,15 +885,15 @@ func peemitreloc(text, data, ctors *IMAGE_SECTION_HEADER) {
 	}
 }
 
-func dope() {
+func (ctxt *Link) dope() {
 	/* relocation table */
-	rel := Linklookup(Ctxt, ".rel", 0)
+	rel := ctxt.Syms.Lookup(".rel", 0)
 
 	rel.Attr |= AttrReachable
 	rel.Type = obj.SELFROSECT
 
-	initdynimport()
-	initdynexport()
+	initdynimport(ctxt)
+	initdynexport(ctxt)
 }
 
 func strtbladd(name string) int {
@@ -913,14 +910,14 @@ func strtbladd(name string) int {
  * reference: pecoff_v8.docx Page 24.
  * <http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx>
  */
-func newPEDWARFSection(name string, size int64) *IMAGE_SECTION_HEADER {
+func newPEDWARFSection(ctxt *Link, name string, size int64) *IMAGE_SECTION_HEADER {
 	if size == 0 {
 		return nil
 	}
 
 	off := strtbladd(name)
 	s := fmt.Sprintf("/%d", off)
-	h := addpesection(s, int(size), int(size))
+	h := addpesection(ctxt, s, int(size), int(size))
 	h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
 
 	return h
@@ -928,20 +925,20 @@ func newPEDWARFSection(name string, size int64) *IMAGE_SECTION_HEADER {
 
 // writePESymTableRecords writes all COFF symbol table records.
 // It returns number of records written.
-func writePESymTableRecords() int {
+func writePESymTableRecords(ctxt *Link) int {
 	var symcnt int
 
-	put := func(s *LSym, name string, type_ int, addr int64, size int64, ver int, gotype *LSym) {
+	put := func(ctxt *Link, s *Symbol, name string, type_ SymbolType, addr int64, gotype *Symbol) {
 		if s == nil {
 			return
 		}
-		if s.Sect == nil && type_ != 'U' {
+		if s.Sect == nil && type_ != UndefinedSym {
 			return
 		}
 		switch type_ {
 		default:
 			return
-		case 'D', 'B', 'T', 'U':
+		case DataSym, BSSSym, TextSym, UndefinedSym:
 		}
 
 		// only windows/386 requires underscore prefix on external symbols
@@ -967,10 +964,10 @@ func writePESymTableRecords() int {
 		} else if uint64(s.Value) >= Segtext.Vaddr {
 			value = int64(uint64(s.Value) - Segtext.Vaddr)
 			sect = textsect
-		} else if type_ == 'U' {
+		} else if type_ == UndefinedSym {
 			typ = IMAGE_SYM_DTYPE_FUNCTION
 		} else {
-			Diag("addpesym %#x", addr)
+			Errorf(s, "addpesym %#x", addr)
 		}
 
 		// write COFF symbol table record
@@ -1001,28 +998,28 @@ func writePESymTableRecords() int {
 		for d := dr; d != nil; d = d.next {
 			for m := d.ms; m != nil; m = m.next {
 				s := m.s.R[0].Xsym
-				put(s, s.Name, 'U', 0, int64(SysArch.PtrSize), 0, nil)
+				put(ctxt, s, s.Name, UndefinedSym, 0, nil)
 			}
 		}
 
-		s := Linklookup(Ctxt, ".text", 0)
+		s := ctxt.Syms.Lookup(".text", 0)
 		if s.Type == obj.STEXT {
-			put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil)
+			put(ctxt, s, s.Name, TextSym, s.Value, nil)
 		}
 	}
 
-	genasmsym(put)
+	genasmsym(ctxt, put)
 
 	return symcnt
 }
 
-func addpesymtable() {
-	symtabStartPos := Cpos()
+func addpesymtable(ctxt *Link) {
+	symtabStartPos := coutbuf.Offset()
 
 	// write COFF symbol table
 	var symcnt int
-	if Debug['s'] == 0 || Linkmode == LinkExternal {
-		symcnt = writePESymTableRecords()
+	if !*FlagS || Linkmode == LinkExternal {
+		symcnt = writePESymTableRecords(ctxt)
 	}
 
 	// update COFF file header and section table
@@ -1031,9 +1028,9 @@ func addpesymtable() {
 	if Linkmode != LinkExternal {
 		// We do not really need .symtab for go.o, and if we have one, ld
 		// will also include it in the exe, and that will confuse windows.
-		h = addpesection(".symtab", size, size)
+		h = addpesection(ctxt, ".symtab", size, size)
 		h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
-		chksectoff(h, symtabStartPos)
+		chksectoff(ctxt, h, symtabStartPos)
 	}
 	fh.PointerToSymbolTable = uint32(symtabStartPos)
 	fh.NumberOfSymbols = uint32(symcnt)
@@ -1048,22 +1045,22 @@ func addpesymtable() {
 	}
 }
 
-func setpersrc(sym *LSym) {
+func setpersrc(ctxt *Link, sym *Symbol) {
 	if rsrcsym != nil {
-		Diag("too many .rsrc sections")
+		Errorf(sym, "too many .rsrc sections")
 	}
 
 	rsrcsym = sym
 }
 
-func addpersrc() {
+func addpersrc(ctxt *Link) {
 	if rsrcsym == nil {
 		return
 	}
 
-	h := addpesection(".rsrc", int(rsrcsym.Size), int(rsrcsym.Size))
+	h := addpesection(ctxt, ".rsrc", int(rsrcsym.Size), int(rsrcsym.Size))
 	h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA
-	chksectoff(h, Cpos())
+	chksectoff(ctxt, h, coutbuf.Offset())
 
 	// relocation
 	var p []byte
@@ -1091,16 +1088,16 @@ func addpersrc() {
 	dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.VirtualSize
 }
 
-func addinitarray() (c *IMAGE_SECTION_HEADER) {
+func addinitarray(ctxt *Link) (c *IMAGE_SECTION_HEADER) {
 	// The size below was determined by the specification for array relocations,
 	// and by observing what GCC writes here. If the initarray section grows to
 	// contain more than one constructor entry, the size will need to be 8 * constructor_count.
 	// However, the entire Go runtime is initialized from just one function, so it is unlikely
 	// that this will need to grow in the future.
 	var size int
-	switch obj.Getgoarch() {
+	switch obj.GOARCH {
 	default:
-		fmt.Fprintf(os.Stderr, "link: unknown architecture for PE: %q\n", obj.Getgoarch())
+		fmt.Fprintf(os.Stderr, "link: unknown architecture for PE: %q\n", obj.GOARCH)
 		os.Exit(2)
 	case "386":
 		size = 4
@@ -1108,16 +1105,16 @@ func addinitarray() (c *IMAGE_SECTION_HEADER) {
 		size = 8
 	}
 
-	c = addpesection(".ctors", size, size)
+	c = addpesection(ctxt, ".ctors", size, size)
 	c.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
 	c.SizeOfRawData = uint32(size)
 
 	Cseek(int64(c.PointerToRawData))
-	chksectoff(c, Cpos())
-	init_entry := Linklookup(Ctxt, INITENTRY, 0)
+	chksectoff(ctxt, c, coutbuf.Offset())
+	init_entry := ctxt.Syms.Lookup(*flagEntrySymbol, 0)
 	addr := uint64(init_entry.Value) - init_entry.Sect.Vaddr
 
-	switch obj.Getgoarch() {
+	switch obj.GOARCH {
 	case "386":
 		Lputl(uint32(addr))
 	case "amd64":
@@ -1127,7 +1124,7 @@ func addinitarray() (c *IMAGE_SECTION_HEADER) {
 	return c
 }
 
-func Asmbpe() {
+func Asmbpe(ctxt *Link) {
 	switch SysArch.Family {
 	default:
 		Exitf("unknown PE architecture: %v", SysArch.Family)
@@ -1137,50 +1134,50 @@ func Asmbpe() {
 		fh.Machine = IMAGE_FILE_MACHINE_I386
 	}
 
-	t := addpesection(".text", int(Segtext.Length), int(Segtext.Length))
+	t := addpesection(ctxt, ".text", int(Segtext.Length), int(Segtext.Length))
 	t.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
 	if Linkmode == LinkExternal {
 		// some data symbols (e.g. masks) end up in the .text section, and they normally
 		// expect larger alignment requirement than the default text section alignment.
 		t.Characteristics |= IMAGE_SCN_ALIGN_32BYTES
 	}
-	chksectseg(t, &Segtext)
+	chksectseg(ctxt, t, &Segtext)
 	textsect = pensect
 
 	var d *IMAGE_SECTION_HEADER
 	var c *IMAGE_SECTION_HEADER
 	if Linkmode != LinkExternal {
-		d = addpesection(".data", int(Segdata.Length), int(Segdata.Filelen))
+		d = addpesection(ctxt, ".data", int(Segdata.Length), int(Segdata.Filelen))
 		d.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
-		chksectseg(d, &Segdata)
+		chksectseg(ctxt, d, &Segdata)
 		datasect = pensect
 	} else {
-		d = addpesection(".data", int(Segdata.Filelen), int(Segdata.Filelen))
+		d = addpesection(ctxt, ".data", int(Segdata.Filelen), int(Segdata.Filelen))
 		d.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
-		chksectseg(d, &Segdata)
+		chksectseg(ctxt, d, &Segdata)
 		datasect = pensect
 
-		b := addpesection(".bss", int(Segdata.Length-Segdata.Filelen), 0)
+		b := addpesection(ctxt, ".bss", int(Segdata.Length-Segdata.Filelen), 0)
 		b.Characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
 		b.PointerToRawData = 0
 		bsssect = pensect
 
-		c = addinitarray()
+		c = addinitarray(ctxt)
 	}
 
-	if Debug['s'] == 0 {
-		dwarfaddpeheaders()
+	if !*FlagS {
+		dwarfaddpeheaders(ctxt)
 	}
 
 	Cseek(int64(nextfileoff))
 	if Linkmode != LinkExternal {
-		addimports(d)
-		addexports()
+		addimports(ctxt, d)
+		addexports(ctxt)
 	}
-	addpesymtable()
-	addpersrc()
+	addpesymtable(ctxt)
+	addpersrc(ctxt)
 	if Linkmode == LinkExternal {
-		peemitreloc(t, d, c)
+		peemitreloc(ctxt, t, d, c)
 	}
 
 	fh.NumberOfSections = uint16(pensect)
@@ -1218,8 +1215,8 @@ func Asmbpe() {
 	oh64.SizeOfUninitializedData = 0
 	oh.SizeOfUninitializedData = 0
 	if Linkmode != LinkExternal {
-		oh64.AddressOfEntryPoint = uint32(Entryvalue() - PEBASE)
-		oh.AddressOfEntryPoint = uint32(Entryvalue() - PEBASE)
+		oh64.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
+		oh.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
 	}
 	oh64.BaseOfCode = t.VirtualAddress
 	oh.BaseOfCode = t.VirtualAddress
@@ -1245,7 +1242,7 @@ func Asmbpe() {
 	oh.SizeOfImage = uint32(nextsectoff)
 	oh64.SizeOfHeaders = uint32(PEFILEHEADR)
 	oh.SizeOfHeaders = uint32(PEFILEHEADR)
-	if headstring == "windowsgui" {
+	if Headtype == obj.Hwindowsgui {
 		oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
 		oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
 	} else {
diff --git a/src/cmd/link/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go
deleted file mode 100644
index 7a4555f..0000000
--- a/src/cmd/link/internal/ld/pobj.go
+++ /dev/null
@@ -1,227 +0,0 @@
-// Inferno utils/6l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c
-//
-//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-//	Portions Copyright © 1997-1999 Vita Nuova Limited
-//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-//	Portions Copyright © 2004,2006 Bruce Ellis
-//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-//	Portions Copyright © 2009 The Go Authors. All rights reserved.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-package ld
-
-import (
-	"bufio"
-	"cmd/internal/obj"
-	"cmd/internal/sys"
-	"flag"
-	"fmt"
-	"os"
-	"strings"
-)
-
-var (
-	pkglistfornote []byte
-	buildid        string
-)
-
-func Ldmain() {
-	Bso = bufio.NewWriter(os.Stdout)
-
-	Ctxt = linknew(SysArch)
-	Ctxt.Diag = Diag
-	Ctxt.Bso = Bso
-
-	Debug = [128]int{}
-	nerrors = 0
-	outfile = ""
-	HEADTYPE = -1
-	INITTEXT = -1
-	INITDAT = -1
-	INITRND = -1
-	INITENTRY = ""
-	Linkmode = LinkAuto
-
-	// For testing behavior of go command when tools crash silently.
-	// Undocumented, not in standard flag parser to avoid
-	// exposing in usage message.
-	for _, arg := range os.Args {
-		if arg == "-crash_for_testing" {
-			os.Exit(2)
-		}
-	}
-
-	if SysArch.Family == sys.AMD64 && obj.Getgoos() == "plan9" {
-		obj.Flagcount("8", "use 64-bit addresses in symbol table", &Debug['8'])
-	}
-	obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo)
-	obj.Flagcount("C", "check Go calls to C code", &Debug['C'])
-	obj.Flagint64("D", "set data segment `address`", &INITDAT)
-	obj.Flagstr("E", "set `entry` symbol name", &INITENTRY)
-	obj.Flagfn1("I", "use `linker` as ELF dynamic linker", setinterp)
-	obj.Flagfn1("L", "add specified `directory` to library path", Lflag)
-	obj.Flagfn1("H", "set header `type`", setheadtype)
-	obj.Flagint32("R", "set address rounding `quantum`", &INITRND)
-	obj.Flagint64("T", "set text segment `address`", &INITTEXT)
-	obj.Flagfn0("V", "print version and exit", doversion)
-	obj.Flagfn1("X", "add string value `definition` of the form importpath.name=value", addstrdata1)
-	obj.Flagcount("a", "disassemble output", &Debug['a'])
-	obj.Flagstr("buildid", "record `id` as Go toolchain build id", &buildid)
-	flag.Var(&Buildmode, "buildmode", "set build `mode`")
-	obj.Flagcount("c", "dump call graph", &Debug['c'])
-	obj.Flagcount("d", "disable dynamic executable", &Debug['d'])
-	flag.BoolVar(&flag_dumpdep, "dumpdep", false, "dump symbol dependency graph")
-	obj.Flagstr("extar", "archive program for buildmode=c-archive", &extar)
-	obj.Flagstr("extld", "use `linker` when linking in external mode", &extld)
-	obj.Flagstr("extldflags", "pass `flags` to external linker", &extldflags)
-	obj.Flagcount("f", "ignore version mismatch", &Debug['f'])
-	obj.Flagcount("g", "disable go package data checks", &Debug['g'])
-	obj.Flagcount("h", "halt on error", &Debug['h'])
-	obj.Flagstr("installsuffix", "set package directory `suffix`", &flag_installsuffix)
-	obj.Flagstr("k", "set field tracking `symbol`", &tracksym)
-	obj.Flagstr("libgcc", "compiler support lib for internal linking; use \"none\" to disable", &libgccfile)
-	obj.Flagfn1("linkmode", "set link `mode` (internal, external, auto)", setlinkmode)
-	flag.BoolVar(&Linkshared, "linkshared", false, "link against installed Go shared libraries")
-	obj.Flagcount("msan", "enable MSan interface", &flag_msan)
-	obj.Flagcount("n", "dump symbol table", &Debug['n'])
-	obj.Flagstr("o", "write output to `file`", &outfile)
-	flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...")
-	obj.Flagcount("race", "enable race detector", &flag_race)
-	obj.Flagcount("s", "disable symbol table", &Debug['s'])
-	var flagShared int
-	if SysArch.InFamily(sys.ARM, sys.AMD64) {
-		obj.Flagcount("shared", "generate shared object (implies -linkmode external)", &flagShared)
-	}
-	obj.Flagstr("tmpdir", "use `directory` for temporary files", &tmpdir)
-	obj.Flagcount("u", "reject unsafe packages", &Debug['u'])
-	obj.Flagcount("v", "print link trace", &Debug['v'])
-	obj.Flagcount("w", "disable DWARF generation", &Debug['w'])
-
-	obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile)
-	obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile)
-	obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate)
-
-	obj.Flagparse(usage)
-
-	startProfile()
-	Ctxt.Bso = Bso
-	Ctxt.Debugvlog = int32(Debug['v'])
-	if flagShared != 0 {
-		if Buildmode == BuildmodeUnset {
-			Buildmode = BuildmodeCShared
-		} else if Buildmode != BuildmodeCShared {
-			Exitf("-shared and -buildmode=%s are incompatible", Buildmode.String())
-		}
-	}
-	if Buildmode == BuildmodeUnset {
-		Buildmode = BuildmodeExe
-	}
-
-	if Buildmode != BuildmodeShared && flag.NArg() != 1 {
-		usage()
-	}
-
-	if outfile == "" {
-		outfile = "a.out"
-		if HEADTYPE == obj.Hwindows {
-			outfile += ".exe"
-		}
-	}
-
-	libinit() // creates outfile
-
-	if HEADTYPE == -1 {
-		HEADTYPE = int32(headtype(goos))
-	}
-	Ctxt.Headtype = int(HEADTYPE)
-	if headstring == "" {
-		headstring = Headstr(int(HEADTYPE))
-	}
-
-	Thearch.Archinit()
-
-	if Linkshared && !Iself {
-		Exitf("-linkshared can only be used on elf systems")
-	}
-
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND))
-	}
-	Bso.Flush()
-
-	if Buildmode == BuildmodeShared {
-		for i := 0; i < flag.NArg(); i++ {
-			arg := flag.Arg(i)
-			parts := strings.SplitN(arg, "=", 2)
-			var pkgpath, file string
-			if len(parts) == 1 {
-				pkgpath, file = "main", arg
-			} else {
-				pkgpath, file = parts[0], parts[1]
-			}
-			pkglistfornote = append(pkglistfornote, pkgpath...)
-			pkglistfornote = append(pkglistfornote, '\n')
-			addlibpath(Ctxt, "command line", "command line", file, pkgpath, "")
-		}
-	} else {
-		addlibpath(Ctxt, "command line", "command line", flag.Arg(0), "main", "")
-	}
-	loadlib()
-
-	checkstrdata()
-	deadcode(Ctxt)
-	fieldtrack(Ctxt)
-	callgraph()
-
-	doelf()
-	if HEADTYPE == obj.Hdarwin {
-		domacho()
-	}
-	dostkcheck()
-	if HEADTYPE == obj.Hwindows {
-		dope()
-	}
-	addexport()
-	Thearch.Gentext() // trampolines, call stubs, etc.
-	textbuildid()
-	textaddress()
-	pclntab()
-	findfunctab()
-	symtab()
-	dodata()
-	address()
-	reloc()
-	Thearch.Asmb()
-	undef()
-	hostlink()
-	archive()
-	if Debug['v'] != 0 {
-		fmt.Fprintf(Bso, "%5.2f cpu time\n", obj.Cputime())
-		fmt.Fprintf(Bso, "%d symbols\n", len(Ctxt.Allsym))
-		fmt.Fprintf(Bso, "%d liveness data\n", liveness)
-	}
-
-	Bso.Flush()
-
-	errorexit()
-}
diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go
index a44c8de..4908e34 100644
--- a/src/cmd/link/internal/ld/sym.go
+++ b/src/cmd/link/internal/ld/sym.go
@@ -1,6 +1,6 @@
 // Derived from Inferno utils/6l/obj.c and utils/6l/span.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -35,56 +35,35 @@ import (
 	"cmd/internal/obj"
 	"cmd/internal/sys"
 	"log"
-	"strconv"
 )
 
-var headers = []struct {
-	name string
-	val  int
-}{
-	{"darwin", obj.Hdarwin},
-	{"dragonfly", obj.Hdragonfly},
-	{"freebsd", obj.Hfreebsd},
-	{"linux", obj.Hlinux},
-	{"android", obj.Hlinux}, // must be after "linux" entry or else headstr(Hlinux) == "android"
-	{"nacl", obj.Hnacl},
-	{"netbsd", obj.Hnetbsd},
-	{"openbsd", obj.Hopenbsd},
-	{"plan9", obj.Hplan9},
-	{"solaris", obj.Hsolaris},
-	{"windows", obj.Hwindows},
-	{"windowsgui", obj.Hwindows},
-}
-
 func linknew(arch *sys.Arch) *Link {
 	ctxt := &Link{
-		Hash: []map[string]*LSym{
-			// preallocate about 2mb for hash of
-			// non static symbols
-			make(map[string]*LSym, 100000),
+		Syms: &Symbols{
+			hash: []map[string]*Symbol{
+				// preallocate about 2mb for hash of
+				// non static symbols
+				make(map[string]*Symbol, 100000),
+			},
+			Allsym: make([]*Symbol, 0, 100000),
 		},
-		Allsym: make([]*LSym, 0, 100000),
-		Arch:   arch,
-		Goroot: obj.Getgoroot(),
+		Arch: arch,
 	}
 
-	p := obj.Getgoarch()
-	if p != arch.Name {
-		log.Fatalf("invalid goarch %s (want %s)", p, arch.Name)
+	if obj.GOARCH != arch.Name {
+		log.Fatalf("invalid obj.GOARCH %s (want %s)", obj.GOARCH, arch.Name)
 	}
 
-	ctxt.Headtype = headtype(obj.Getgoos())
-	if ctxt.Headtype < 0 {
-		log.Fatalf("unknown goos %s", obj.Getgoos())
-	}
+	return ctxt
+}
 
-	// Record thread-local storage offset.
-	// TODO(rsc): Move tlsoffset back into the linker.
-	switch ctxt.Headtype {
+// computeTLSOffset records the thread-local storage offset.
+func (ctxt *Link) computeTLSOffset() {
+	switch Headtype {
 	default:
-		log.Fatalf("unknown thread-local storage offset for %s", Headstr(ctxt.Headtype))
+		log.Fatalf("unknown thread-local storage offset for %v", Headtype)
 
-	case obj.Hplan9, obj.Hwindows:
+	case obj.Hplan9, obj.Hwindows, obj.Hwindowsgui:
 		break
 
 		/*
@@ -98,7 +77,7 @@ func linknew(arch *sys.Arch) *Link {
 		obj.Hopenbsd,
 		obj.Hdragonfly,
 		obj.Hsolaris:
-		if obj.Getgoos() == "android" {
+		if obj.GOOS == "android" {
 			switch ctxt.Arch.Family {
 			case sys.AMD64:
 				// Android/amd64 constant - offset from 0(FS) to our TLS slot.
@@ -152,63 +131,4 @@ func linknew(arch *sys.Arch) *Link {
 		}
 	}
 
-	// On arm, record goarm.
-	if ctxt.Arch.Family == sys.ARM {
-		ctxt.Goarm = obj.Getgoarm()
-	}
-
-	return ctxt
-}
-
-func linknewsym(ctxt *Link, name string, v int) *LSym {
-	batch := ctxt.LSymBatch
-	if len(batch) == 0 {
-		batch = make([]LSym, 1000)
-	}
-	s := &batch[0]
-	ctxt.LSymBatch = batch[1:]
-
-	s.Dynid = -1
-	s.Plt = -1
-	s.Got = -1
-	s.Name = name
-	s.Version = int16(v)
-	ctxt.Allsym = append(ctxt.Allsym, s)
-
-	return s
-}
-
-func Linklookup(ctxt *Link, name string, v int) *LSym {
-	m := ctxt.Hash[v]
-	s := m[name]
-	if s != nil {
-		return s
-	}
-	s = linknewsym(ctxt, name, v)
-	s.Extname = s.Name
-	m[name] = s
-	return s
-}
-
-// read-only lookup
-func Linkrlookup(ctxt *Link, name string, v int) *LSym {
-	return ctxt.Hash[v][name]
-}
-
-func Headstr(v int) string {
-	for i := 0; i < len(headers); i++ {
-		if v == headers[i].val {
-			return headers[i].name
-		}
-	}
-	return strconv.Itoa(v)
-}
-
-func headtype(name string) int {
-	for i := 0; i < len(headers); i++ {
-		if name == headers[i].name {
-			return headers[i].val
-		}
-	}
-	return -1
 }
diff --git a/src/cmd/link/internal/ld/symbols.go b/src/cmd/link/internal/ld/symbols.go
new file mode 100644
index 0000000..154507d
--- /dev/null
+++ b/src/cmd/link/internal/ld/symbols.go
@@ -0,0 +1,84 @@
+// Derived from Inferno utils/6l/l.h and related files.
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
+//	Portions Copyright © 1997-1999 Vita Nuova Limited
+//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+//	Portions Copyright © 2004,2006 Bruce Ellis
+//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
+//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+//	Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package ld
+
+type Symbols struct {
+	symbolBatch []Symbol
+
+	// Symbol lookup based on name and indexed by version.
+	hash []map[string]*Symbol
+
+	Allsym []*Symbol
+}
+
+func (syms *Symbols) newsym(name string, v int) *Symbol {
+	batch := syms.symbolBatch
+	if len(batch) == 0 {
+		batch = make([]Symbol, 1000)
+	}
+	s := &batch[0]
+	syms.symbolBatch = batch[1:]
+
+	s.Dynid = -1
+	s.Plt = -1
+	s.Got = -1
+	s.Name = name
+	s.Version = int16(v)
+	syms.Allsym = append(syms.Allsym, s)
+
+	return s
+}
+
+// Look up the symbol with the given name and version, creating the
+// symbol if it is not found.
+func (syms *Symbols) Lookup(name string, v int) *Symbol {
+	m := syms.hash[v]
+	s := m[name]
+	if s != nil {
+		return s
+	}
+	s = syms.newsym(name, v)
+	s.Extname = s.Name
+	m[name] = s
+	return s
+}
+
+// Look up the symbol with the given name and version, returning nil
+// if it is not found.
+func (syms *Symbols) ROLookup(name string, v int) *Symbol {
+	return syms.hash[v][name]
+}
+
+// Allocate a new version (i.e. symbol namespace).
+func (syms *Symbols) IncVersion() int {
+	syms.hash = append(syms.hash, make(map[string]*Symbol))
+	return len(syms.hash) - 1
+}
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index 06d7792..98ce3ad 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -1,5 +1,5 @@
 // Inferno utils/6l/span.c
-// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -46,14 +46,6 @@ func putelfstr(s string) int {
 		putelfstr("")
 	}
 
-	// When dynamically linking, we create LSym's by reading the names from
-	// the symbol tables of the shared libraries and so the names need to
-	// match exactly. Tools like DTrace will have to wait for now.
-	if !DynlinkingGo() {
-		// Rewrite · to . for ASCII-only tools like DTrace (sigh)
-		s = strings.Replace(s, "·", ".", -1)
-	}
-
 	off := len(Elfstrdat)
 	Elfstrdat = append(Elfstrdat, s...)
 	Elfstrdat = append(Elfstrdat, 0)
@@ -84,29 +76,31 @@ var numelfsym int = 1 // 0 is reserved
 
 var elfbind int
 
-func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *LSym) {
-	var type_ int
+func putelfsym(ctxt *Link, x *Symbol, s string, t SymbolType, addr int64, go_ *Symbol) {
+	var typ int
 
 	switch t {
 	default:
 		return
 
-	case 'T':
-		type_ = STT_FUNC
+	case TextSym:
+		typ = STT_FUNC
 
-	case 'D':
-		type_ = STT_OBJECT
+	case DataSym, BSSSym:
+		typ = STT_OBJECT
 
-	case 'B':
-		type_ = STT_OBJECT
-
-	case 'U':
+	case UndefinedSym:
 		// ElfType is only set for symbols read from Go shared libraries, but
 		// for other symbols it is left as STT_NOTYPE which is fine.
-		type_ = int(x.ElfType)
+		typ = int(x.ElfType)
+
+	case TLSSym:
+		typ = STT_TLS
+	}
 
-	case 't':
-		type_ = STT_TLS
+	size := x.Size
+	if t == UndefinedSym {
+		size = 0
 	}
 
 	xo := x
@@ -119,13 +113,11 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L
 		elfshnum = SHN_UNDEF
 	} else {
 		if xo.Sect == nil {
-			Ctxt.Cursym = x
-			Diag("missing section in putelfsym")
+			Errorf(x, "missing section in putelfsym")
 			return
 		}
 		if xo.Sect.Elfsect == nil {
-			Ctxt.Cursym = x
-			Diag("missing ELF section in putelfsym")
+			Errorf(x, "missing ELF section in putelfsym")
 			return
 		}
 		elfshnum = xo.Sect.Elfsect.shnum
@@ -135,7 +127,7 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L
 	// maybe one day STB_WEAK.
 	bind := STB_GLOBAL
 
-	if ver != 0 || (x.Type&obj.SHIDDEN != 0) || x.Attr.Local() {
+	if x.Version != 0 || (x.Type&obj.SHIDDEN != 0) || x.Attr.Local() {
 		bind = STB_LOCAL
 	}
 
@@ -144,7 +136,7 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L
 	// To avoid filling the dynamic table with lots of unnecessary symbols,
 	// mark all Go symbols local (not global) in the final executable.
 	// But when we're dynamically linking, we need all those global symbols.
-	if !DynlinkingGo() && Linkmode == LinkExternal && !x.Attr.CgoExportStatic() && elfshnum != SHN_UNDEF {
+	if !ctxt.DynlinkingGo() && Linkmode == LinkExternal && !x.Attr.CgoExportStatic() && elfshnum != SHN_UNDEF {
 		bind = STB_LOCAL
 	}
 
@@ -155,23 +147,31 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L
 	if x.Type&obj.SHIDDEN != 0 {
 		other = STV_HIDDEN
 	}
-	if (Buildmode == BuildmodePIE || DynlinkingGo()) && SysArch.Family == sys.PPC64 && type_ == STT_FUNC && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" {
+	if (Buildmode == BuildmodeCArchive || Buildmode == BuildmodePIE || ctxt.DynlinkingGo()) && SysArch.Family == sys.PPC64 && typ == STT_FUNC && x.Name != "runtime.duffzero" && x.Name != "runtime.duffcopy" {
 		// On ppc64 the top three bits of the st_other field indicate how
 		// many instructions separate the global and local entry points. In
 		// our case it is two instructions, indicated by the value 3.
 		other |= 3 << 5
 	}
 
-	if DynlinkingGo() && bind == STB_GLOBAL && elfbind == STB_LOCAL && x.Type == obj.STEXT {
+	// When dynamically linking, we create Symbols by reading the names from
+	// the symbol tables of the shared libraries and so the names need to
+	// match exactly. Tools like DTrace will have to wait for now.
+	if !ctxt.DynlinkingGo() {
+		// Rewrite · to . for ASCII-only tools like DTrace (sigh)
+		s = strings.Replace(s, "·", ".", -1)
+	}
+
+	if ctxt.DynlinkingGo() && bind == STB_GLOBAL && elfbind == STB_LOCAL && x.Type == obj.STEXT {
 		// When dynamically linking, we want references to functions defined
 		// in this module to always be to the function object, not to the
 		// PLT. We force this by writing an additional local symbol for every
 		// global function symbol and making all relocations against the
 		// global symbol refer to this local symbol instead (see
-		// (*LSym).ElfsymForReloc). This is approximately equivalent to the
+		// (*Symbol).ElfsymForReloc). This is approximately equivalent to the
 		// ELF linker -Bsymbolic-functions option, but that is buggy on
 		// several platforms.
-		putelfsyment(putelfstr("local."+s), addr, size, STB_LOCAL<<4|type_&0xf, elfshnum, other)
+		putelfsyment(putelfstr("local."+s), addr, size, STB_LOCAL<<4|typ&0xf, elfshnum, other)
 		x.LocalElfsym = int32(numelfsym)
 		numelfsym++
 		return
@@ -179,22 +179,22 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L
 		return
 	}
 
-	putelfsyment(putelfstr(s), addr, size, bind<<4|type_&0xf, elfshnum, other)
+	putelfsyment(putelfstr(s), addr, size, bind<<4|typ&0xf, elfshnum, other)
 	x.Elfsym = int32(numelfsym)
 	numelfsym++
 }
 
-func putelfsectionsym(s *LSym, shndx int) {
+func putelfsectionsym(s *Symbol, shndx int) {
 	putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_SECTION, shndx, 0)
 	s.Elfsym = int32(numelfsym)
 	numelfsym++
 }
 
-func Asmelfsym() {
+func Asmelfsym(ctxt *Link) {
 	// the first symbol entry is reserved
 	putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_NOTYPE, 0, 0)
 
-	dwarfaddelfsectionsyms()
+	dwarfaddelfsectionsyms(ctxt)
 
 	// Some linkers will add a FILE sym if one is not present.
 	// Avoid having the working directory inserted into the symbol table.
@@ -204,29 +204,25 @@ func Asmelfsym() {
 	numelfsym++
 
 	elfbind = STB_LOCAL
-	genasmsym(putelfsym)
+	genasmsym(ctxt, putelfsym)
 
 	elfbind = STB_GLOBAL
 	elfglobalsymndx = numelfsym
-	genasmsym(putelfsym)
+	genasmsym(ctxt, putelfsym)
 }
 
-func putplan9sym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *LSym) {
-	switch t {
-	case 'T', 'L', 'D', 'B':
-		if ver != 0 {
+func putplan9sym(ctxt *Link, x *Symbol, s string, typ SymbolType, addr int64, go_ *Symbol) {
+	t := int(typ)
+	switch typ {
+	case TextSym, DataSym, BSSSym:
+		if x.Version != 0 {
 			t += 'a' - 'A'
 		}
 		fallthrough
 
-	case 'a',
-		'p',
-		'f',
-		'z',
-		'Z',
-		'm':
+	case AutoSym, ParamSym, FileSym, FrameSym:
 		l := 4
-		if HEADTYPE == obj.Hplan9 && SysArch.Family == sys.AMD64 && Debug['8'] == 0 {
+		if Headtype == obj.Hplan9 && SysArch.Family == sys.AMD64 && !Flag8 {
 			Lputb(uint32(addr >> 32))
 			l = 8
 		}
@@ -235,26 +231,15 @@ func putplan9sym(x *LSym, s string, t int, addr int64, size int64, ver int, go_
 		Cput(uint8(t + 0x80)) /* 0x80 is variable length */
 
 		var i int
-		if t == 'z' || t == 'Z' {
-			Cput(s[0])
-			for i = 1; s[i] != 0 || s[i+1] != 0; i += 2 {
-				Cput(s[i])
-				Cput(s[i+1])
-			}
 
-			Cput(0)
-			Cput(0)
-			i++
-		} else {
-			/* skip the '<' in filenames */
-			if t == 'f' {
-				s = s[1:]
-			}
-			for i = 0; i < len(s); i++ {
-				Cput(s[i])
-			}
-			Cput(0)
+		/* skip the '<' in filenames */
+		if t == FileSym {
+			s = s[1:]
 		}
+		for i = 0; i < len(s); i++ {
+			Cput(s[i])
+		}
+		Cput(0)
 
 		Symsize += int32(l) + 1 + int32(i) + 1
 
@@ -263,11 +248,13 @@ func putplan9sym(x *LSym, s string, t int, addr int64, size int64, ver int, go_
 	}
 }
 
-func Asmplan9sym() {
-	genasmsym(putplan9sym)
+func Asmplan9sym(ctxt *Link) {
+	genasmsym(ctxt, putplan9sym)
 }
 
-var symt *LSym
+var symt *Symbol
+
+var encbuf [10]byte
 
 func Wputb(w uint16) { Cwrite(Append16b(encbuf[:0], w)) }
 func Lputb(l uint32) { Cwrite(Append32b(encbuf[:0], l)) }
@@ -315,67 +302,121 @@ func (libs byPkg) Swap(a, b int) {
 	libs[a], libs[b] = libs[b], libs[a]
 }
 
-func symtab() {
-	dosymtype()
+// Create a table with information on the text sections.
+
+func textsectionmap(ctxt *Link) uint32 {
+
+	t := ctxt.Syms.Lookup("runtime.textsectionmap", 0)
+	t.Type = obj.SRODATA
+	t.Attr |= AttrReachable
+	nsections := int64(0)
+
+	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
+		if sect.Name == ".text" {
+			nsections++
+		} else {
+			break
+		}
+	}
+	Symgrow(t, nsections*(2*int64(SysArch.IntSize)+int64(SysArch.PtrSize)))
+
+	off := int64(0)
+	n := 0
+
+	// The vaddr for each text section is the difference between the section's
+	// Vaddr and the Vaddr for the first text section as determined at compile
+	// time.
+
+	// The symbol for the first text section is named runtime.text as before.
+	// Additional text sections are named runtime.text.n where n is the
+	// order of creation starting with 1. These symbols provide the section's
+	// address after relocation by the linker.
+
+	textbase := Segtext.Sect.Vaddr
+	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
+		if sect.Name != ".text" {
+			break
+		}
+		off = setuintxx(ctxt, t, off, sect.Vaddr-textbase, int64(SysArch.IntSize))
+		off = setuintxx(ctxt, t, off, sect.Length, int64(SysArch.IntSize))
+		if n == 0 {
+			s := ctxt.Syms.ROLookup("runtime.text", 0)
+			if s == nil {
+				Errorf(nil, "Unable to find symbol runtime.text\n")
+			}
+			off = setaddr(ctxt, t, off, s)
+
+		} else {
+			s := ctxt.Syms.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
+			if s == nil {
+				Errorf(nil, "Unable to find symbol runtime.text.%d\n", n)
+			}
+			off = setaddr(ctxt, t, off, s)
+		}
+		n++
+	}
+	return uint32(n)
+}
+
+func (ctxt *Link) symtab() {
+	dosymtype(ctxt)
 
 	// Define these so that they'll get put into the symbol table.
 	// data.c:/^address will provide the actual values.
-	xdefine("runtime.text", obj.STEXT, 0)
-
-	xdefine("runtime.etext", obj.STEXT, 0)
-	xdefine("runtime.typelink", obj.SRODATA, 0)
-	xdefine("runtime.etypelink", obj.SRODATA, 0)
-	xdefine("runtime.itablink", obj.SRODATA, 0)
-	xdefine("runtime.eitablink", obj.SRODATA, 0)
-	xdefine("runtime.rodata", obj.SRODATA, 0)
-	xdefine("runtime.erodata", obj.SRODATA, 0)
-	xdefine("runtime.types", obj.SRODATA, 0)
-	xdefine("runtime.etypes", obj.SRODATA, 0)
-	xdefine("runtime.noptrdata", obj.SNOPTRDATA, 0)
-	xdefine("runtime.enoptrdata", obj.SNOPTRDATA, 0)
-	xdefine("runtime.data", obj.SDATA, 0)
-	xdefine("runtime.edata", obj.SDATA, 0)
-	xdefine("runtime.bss", obj.SBSS, 0)
-	xdefine("runtime.ebss", obj.SBSS, 0)
-	xdefine("runtime.noptrbss", obj.SNOPTRBSS, 0)
-	xdefine("runtime.enoptrbss", obj.SNOPTRBSS, 0)
-	xdefine("runtime.end", obj.SBSS, 0)
-	xdefine("runtime.epclntab", obj.SRODATA, 0)
-	xdefine("runtime.esymtab", obj.SRODATA, 0)
+	ctxt.xdefine("runtime.text", obj.STEXT, 0)
+
+	ctxt.xdefine("runtime.etext", obj.STEXT, 0)
+	ctxt.xdefine("runtime.itablink", obj.SRODATA, 0)
+	ctxt.xdefine("runtime.eitablink", obj.SRODATA, 0)
+	ctxt.xdefine("runtime.rodata", obj.SRODATA, 0)
+	ctxt.xdefine("runtime.erodata", obj.SRODATA, 0)
+	ctxt.xdefine("runtime.types", obj.SRODATA, 0)
+	ctxt.xdefine("runtime.etypes", obj.SRODATA, 0)
+	ctxt.xdefine("runtime.noptrdata", obj.SNOPTRDATA, 0)
+	ctxt.xdefine("runtime.enoptrdata", obj.SNOPTRDATA, 0)
+	ctxt.xdefine("runtime.data", obj.SDATA, 0)
+	ctxt.xdefine("runtime.edata", obj.SDATA, 0)
+	ctxt.xdefine("runtime.bss", obj.SBSS, 0)
+	ctxt.xdefine("runtime.ebss", obj.SBSS, 0)
+	ctxt.xdefine("runtime.noptrbss", obj.SNOPTRBSS, 0)
+	ctxt.xdefine("runtime.enoptrbss", obj.SNOPTRBSS, 0)
+	ctxt.xdefine("runtime.end", obj.SBSS, 0)
+	ctxt.xdefine("runtime.epclntab", obj.SRODATA, 0)
+	ctxt.xdefine("runtime.esymtab", obj.SRODATA, 0)
 
 	// garbage collection symbols
-	s := Linklookup(Ctxt, "runtime.gcdata", 0)
+	s := ctxt.Syms.Lookup("runtime.gcdata", 0)
 
 	s.Type = obj.SRODATA
 	s.Size = 0
 	s.Attr |= AttrReachable
-	xdefine("runtime.egcdata", obj.SRODATA, 0)
+	ctxt.xdefine("runtime.egcdata", obj.SRODATA, 0)
 
-	s = Linklookup(Ctxt, "runtime.gcbss", 0)
+	s = ctxt.Syms.Lookup("runtime.gcbss", 0)
 	s.Type = obj.SRODATA
 	s.Size = 0
 	s.Attr |= AttrReachable
-	xdefine("runtime.egcbss", obj.SRODATA, 0)
+	ctxt.xdefine("runtime.egcbss", obj.SRODATA, 0)
 
 	// pseudo-symbols to mark locations of type, string, and go string data.
-	var symtype *LSym
-	var symtyperel *LSym
-	if UseRelro() && (Buildmode == BuildmodeCShared || Buildmode == BuildmodePIE) {
-		s = Linklookup(Ctxt, "type.*", 0)
+	var symtype *Symbol
+	var symtyperel *Symbol
+	if UseRelro() && (Buildmode == BuildmodeCArchive || Buildmode == BuildmodeCShared || Buildmode == BuildmodePIE) {
+		s = ctxt.Syms.Lookup("type.*", 0)
 
 		s.Type = obj.STYPE
 		s.Size = 0
 		s.Attr |= AttrReachable
 		symtype = s
 
-		s = Linklookup(Ctxt, "typerel.*", 0)
+		s = ctxt.Syms.Lookup("typerel.*", 0)
 
 		s.Type = obj.STYPERELRO
 		s.Size = 0
 		s.Attr |= AttrReachable
 		symtyperel = s
-	} else if !DynlinkingGo() {
-		s = Linklookup(Ctxt, "type.*", 0)
+	} else if !ctxt.DynlinkingGo() {
+		s = ctxt.Syms.Lookup("type.*", 0)
 
 		s.Type = obj.STYPE
 		s.Size = 0
@@ -384,47 +425,51 @@ func symtab() {
 		symtyperel = s
 	}
 
-	groupSym := func(name string, t int16) *LSym {
-		s := Linklookup(Ctxt, name, 0)
+	groupSym := func(name string, t obj.SymKind) *Symbol {
+		s := ctxt.Syms.Lookup(name, 0)
 		s.Type = t
 		s.Size = 0
 		s.Attr |= AttrLocal | AttrReachable
 		return s
 	}
 	var (
-		symgostring    = groupSym("go.string.*", obj.SGOSTRING)
-		symgostringhdr = groupSym("go.string.hdr.*", obj.SGOSTRINGHDR)
-		symgofunc      = groupSym("go.func.*", obj.SGOFUNC)
-		symgcbits      = groupSym("runtime.gcbits.*", obj.SGCBITS)
+		symgostring = groupSym("go.string.*", obj.SGOSTRING)
+		symgofunc   = groupSym("go.func.*", obj.SGOFUNC)
+		symgcbits   = groupSym("runtime.gcbits.*", obj.SGCBITS)
 	)
 
-	symtypelink := Linklookup(Ctxt, "runtime.typelink", 0)
-	symtypelink.Type = obj.STYPELINK
+	var symgofuncrel *Symbol
+	if !ctxt.DynlinkingGo() {
+		if UseRelro() {
+			symgofuncrel = groupSym("go.funcrel.*", obj.SGOFUNCRELRO)
+		} else {
+			symgofuncrel = symgofunc
+		}
+	}
 
-	symitablink := Linklookup(Ctxt, "runtime.itablink", 0)
+	symitablink := ctxt.Syms.Lookup("runtime.itablink", 0)
 	symitablink.Type = obj.SITABLINK
 
-	symt = Linklookup(Ctxt, "runtime.symtab", 0)
+	symt = ctxt.Syms.Lookup("runtime.symtab", 0)
 	symt.Attr |= AttrLocal
 	symt.Type = obj.SSYMTAB
 	symt.Size = 0
 	symt.Attr |= AttrReachable
 
-	ntypelinks := 0
 	nitablinks := 0
 
 	// assign specific types so that they sort together.
 	// within a type they sort by size, so the .* symbols
 	// just defined above will be first.
 	// hide the specific symbols.
-	for _, s := range Ctxt.Allsym {
+	for _, s := range ctxt.Syms.Allsym {
 		if !s.Attr.Reachable() || s.Attr.Special() || s.Type != obj.SRODATA {
 			continue
 		}
 
 		switch {
 		case strings.HasPrefix(s.Name, "type."):
-			if !DynlinkingGo() {
+			if !ctxt.DynlinkingGo() {
 				s.Attr |= AttrHidden
 			}
 			if UseRelro() {
@@ -440,12 +485,6 @@ func symtab() {
 			// names, as they can be referred to by a section offset.
 			s.Type = obj.STYPERELRO
 
-		case strings.HasPrefix(s.Name, "go.typelink."):
-			ntypelinks++
-			s.Type = obj.STYPELINK
-			s.Attr |= AttrHidden
-			s.Outer = symtypelink
-
 		case strings.HasPrefix(s.Name, "go.itablink."):
 			nitablinks++
 			s.Type = obj.SITABLINK
@@ -456,20 +495,23 @@ func symtab() {
 			s.Type = obj.SGOSTRING
 			s.Attr |= AttrHidden
 			s.Outer = symgostring
-			if strings.HasPrefix(s.Name, "go.string.hdr.") {
-				s.Type = obj.SGOSTRINGHDR
-				s.Outer = symgostringhdr
-			}
 
 		case strings.HasPrefix(s.Name, "runtime.gcbits."):
 			s.Type = obj.SGCBITS
 			s.Attr |= AttrHidden
 			s.Outer = symgcbits
 
-		case strings.HasPrefix(s.Name, "go.func."):
-			s.Type = obj.SGOFUNC
-			s.Attr |= AttrHidden
-			s.Outer = symgofunc
+		case strings.HasSuffix(s.Name, "·f"):
+			if !ctxt.DynlinkingGo() {
+				s.Attr |= AttrHidden
+			}
+			if UseRelro() {
+				s.Type = obj.SGOFUNCRELRO
+				s.Outer = symgofuncrel
+			} else {
+				s.Type = obj.SGOFUNC
+				s.Outer = symgofunc
+			}
 
 		case strings.HasPrefix(s.Name, "gcargs."), strings.HasPrefix(s.Name, "gclocals."), strings.HasPrefix(s.Name, "gclocals·"):
 			s.Type = obj.SGOFUNC
@@ -481,106 +523,172 @@ func symtab() {
 	}
 
 	if Buildmode == BuildmodeShared {
-		abihashgostr := Linklookup(Ctxt, "go.link.abihash."+filepath.Base(outfile), 0)
+		abihashgostr := ctxt.Syms.Lookup("go.link.abihash."+filepath.Base(*flagOutfile), 0)
 		abihashgostr.Attr |= AttrReachable
 		abihashgostr.Type = obj.SRODATA
-		hashsym := Linklookup(Ctxt, "go.link.abihashbytes", 0)
-		Addaddr(Ctxt, abihashgostr, hashsym)
-		adduint(Ctxt, abihashgostr, uint64(hashsym.Size))
+		hashsym := ctxt.Syms.Lookup("go.link.abihashbytes", 0)
+		Addaddr(ctxt, abihashgostr, hashsym)
+		adduint(ctxt, abihashgostr, uint64(hashsym.Size))
+	}
+	if Buildmode == BuildmodePlugin || ctxt.Syms.ROLookup("plugin.Open", 0) != nil {
+		for _, l := range ctxt.Library {
+			s := ctxt.Syms.Lookup("go.link.pkghashbytes."+l.Pkg, 0)
+			s.Attr |= AttrReachable
+			s.Type = obj.SRODATA
+			s.Size = int64(len(l.hash))
+			s.P = []byte(l.hash)
+			str := ctxt.Syms.Lookup("go.link.pkghash."+l.Pkg, 0)
+			str.Attr |= AttrReachable
+			str.Type = obj.SRODATA
+			Addaddr(ctxt, str, s)
+			adduint(ctxt, str, uint64(len(l.hash)))
+		}
 	}
 
+	nsections := textsectionmap(ctxt)
+
 	// Information about the layout of the executable image for the
 	// runtime to use. Any changes here must be matched by changes to
 	// the definition of moduledata in runtime/symtab.go.
 	// This code uses several global variables that are set by pcln.go:pclntab.
-	moduledata := Ctxt.Moduledata
+	moduledata := ctxt.Moduledata
 	// The pclntab slice
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.pclntab", 0))
-	adduint(Ctxt, moduledata, uint64(Linklookup(Ctxt, "runtime.pclntab", 0).Size))
-	adduint(Ctxt, moduledata, uint64(Linklookup(Ctxt, "runtime.pclntab", 0).Size))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.pclntab", 0))
+	adduint(ctxt, moduledata, uint64(ctxt.Syms.Lookup("runtime.pclntab", 0).Size))
+	adduint(ctxt, moduledata, uint64(ctxt.Syms.Lookup("runtime.pclntab", 0).Size))
 	// The ftab slice
-	Addaddrplus(Ctxt, moduledata, Linklookup(Ctxt, "runtime.pclntab", 0), int64(pclntabPclntabOffset))
-	adduint(Ctxt, moduledata, uint64(pclntabNfunc+1))
-	adduint(Ctxt, moduledata, uint64(pclntabNfunc+1))
+	Addaddrplus(ctxt, moduledata, ctxt.Syms.Lookup("runtime.pclntab", 0), int64(pclntabPclntabOffset))
+	adduint(ctxt, moduledata, uint64(pclntabNfunc+1))
+	adduint(ctxt, moduledata, uint64(pclntabNfunc+1))
 	// The filetab slice
-	Addaddrplus(Ctxt, moduledata, Linklookup(Ctxt, "runtime.pclntab", 0), int64(pclntabFiletabOffset))
-	adduint(Ctxt, moduledata, uint64(len(Ctxt.Filesyms))+1)
-	adduint(Ctxt, moduledata, uint64(len(Ctxt.Filesyms))+1)
+	Addaddrplus(ctxt, moduledata, ctxt.Syms.Lookup("runtime.pclntab", 0), int64(pclntabFiletabOffset))
+	adduint(ctxt, moduledata, uint64(len(ctxt.Filesyms))+1)
+	adduint(ctxt, moduledata, uint64(len(ctxt.Filesyms))+1)
 	// findfunctab
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.findfunctab", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.findfunctab", 0))
 	// minpc, maxpc
-	Addaddr(Ctxt, moduledata, pclntabFirstFunc)
-	Addaddrplus(Ctxt, moduledata, pclntabLastFunc, pclntabLastFunc.Size)
+	Addaddr(ctxt, moduledata, pclntabFirstFunc)
+	Addaddrplus(ctxt, moduledata, pclntabLastFunc, pclntabLastFunc.Size)
 	// pointers to specific parts of the module
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.text", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.etext", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.noptrdata", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.enoptrdata", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.data", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.edata", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.bss", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.ebss", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.noptrbss", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.enoptrbss", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.end", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcdata", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcbss", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.types", 0))
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.etypes", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.text", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.etext", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.noptrdata", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.enoptrdata", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.data", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.edata", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.bss", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.ebss", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.noptrbss", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.enoptrbss", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.end", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.gcdata", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.gcbss", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.types", 0))
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.etypes", 0))
+
+	// text section information
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.textsectionmap", 0))
+	adduint(ctxt, moduledata, uint64(nsections))
+	adduint(ctxt, moduledata, uint64(nsections))
+
 	// The typelinks slice
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.typelink", 0))
-	adduint(Ctxt, moduledata, uint64(ntypelinks))
-	adduint(Ctxt, moduledata, uint64(ntypelinks))
+	typelinkSym := ctxt.Syms.Lookup("runtime.typelink", 0)
+	ntypelinks := uint64(typelinkSym.Size) / 4
+	Addaddr(ctxt, moduledata, typelinkSym)
+	adduint(ctxt, moduledata, ntypelinks)
+	adduint(ctxt, moduledata, ntypelinks)
 	// The itablinks slice
-	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.itablink", 0))
-	adduint(Ctxt, moduledata, uint64(nitablinks))
-	adduint(Ctxt, moduledata, uint64(nitablinks))
-	if len(Ctxt.Shlibs) > 0 {
-		thismodulename := filepath.Base(outfile)
+	Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.itablink", 0))
+	adduint(ctxt, moduledata, uint64(nitablinks))
+	adduint(ctxt, moduledata, uint64(nitablinks))
+	// The ptab slice
+	if ptab := ctxt.Syms.ROLookup("go.plugin.tabs", 0); ptab != nil {
+		ptab.Attr |= AttrReachable
+		ptab.Attr |= AttrLocal
+		ptab.Type = obj.SRODATA
+
+		nentries := uint64(len(ptab.P) / 8) // sizeof(nameOff) + sizeof(typeOff)
+		Addaddr(ctxt, moduledata, ptab)
+		adduint(ctxt, moduledata, nentries)
+		adduint(ctxt, moduledata, nentries)
+	} else {
+		adduint(ctxt, moduledata, 0)
+		adduint(ctxt, moduledata, 0)
+		adduint(ctxt, moduledata, 0)
+	}
+	if Buildmode == BuildmodePlugin {
+		addgostring(ctxt, moduledata, "go.link.thispluginpath", *flagPluginPath)
+
+		pkghashes := ctxt.Syms.Lookup("go.link.pkghashes", 0)
+		pkghashes.Attr |= AttrReachable
+		pkghashes.Attr |= AttrLocal
+		pkghashes.Type = obj.SRODATA
+
+		for i, l := range ctxt.Library {
+			// pkghashes[i].name
+			addgostring(ctxt, pkghashes, fmt.Sprintf("go.link.pkgname.%d", i), l.Pkg)
+			// pkghashes[i].linktimehash
+			addgostring(ctxt, pkghashes, fmt.Sprintf("go.link.pkglinkhash.%d", i), string(l.hash))
+			// pkghashes[i].runtimehash
+			hash := ctxt.Syms.ROLookup("go.link.pkghash."+l.Pkg, 0)
+			Addaddr(ctxt, pkghashes, hash)
+		}
+		Addaddr(ctxt, moduledata, pkghashes)
+		adduint(ctxt, moduledata, uint64(len(ctxt.Library)))
+		adduint(ctxt, moduledata, uint64(len(ctxt.Library)))
+	} else {
+		adduint(ctxt, moduledata, 0) // pluginpath
+		adduint(ctxt, moduledata, 0)
+		adduint(ctxt, moduledata, 0) // pkghashes slice
+		adduint(ctxt, moduledata, 0)
+		adduint(ctxt, moduledata, 0)
+	}
+	if len(ctxt.Shlibs) > 0 {
+		thismodulename := filepath.Base(*flagOutfile)
 		switch Buildmode {
 		case BuildmodeExe, BuildmodePIE:
 			// When linking an executable, outfile is just "a.out". Make
 			// it something slightly more comprehensible.
 			thismodulename = "the executable"
 		}
-		addgostring(moduledata, "go.link.thismodulename", thismodulename)
+		addgostring(ctxt, moduledata, "go.link.thismodulename", thismodulename)
 
-		modulehashes := Linklookup(Ctxt, "go.link.abihashes", 0)
+		modulehashes := ctxt.Syms.Lookup("go.link.abihashes", 0)
 		modulehashes.Attr |= AttrReachable
 		modulehashes.Attr |= AttrLocal
 		modulehashes.Type = obj.SRODATA
 
-		for i, shlib := range Ctxt.Shlibs {
+		for i, shlib := range ctxt.Shlibs {
 			// modulehashes[i].modulename
 			modulename := filepath.Base(shlib.Path)
-			addgostring(modulehashes, fmt.Sprintf("go.link.libname.%d", i), modulename)
+			addgostring(ctxt, modulehashes, fmt.Sprintf("go.link.libname.%d", i), modulename)
 
 			// modulehashes[i].linktimehash
-			addgostring(modulehashes, fmt.Sprintf("go.link.linkhash.%d", i), string(shlib.Hash))
+			addgostring(ctxt, modulehashes, fmt.Sprintf("go.link.linkhash.%d", i), string(shlib.Hash))
 
 			// modulehashes[i].runtimehash
-			abihash := Linklookup(Ctxt, "go.link.abihash."+modulename, 0)
+			abihash := ctxt.Syms.Lookup("go.link.abihash."+modulename, 0)
 			abihash.Attr |= AttrReachable
-			Addaddr(Ctxt, modulehashes, abihash)
+			Addaddr(ctxt, modulehashes, abihash)
 		}
 
-		Addaddr(Ctxt, moduledata, modulehashes)
-		adduint(Ctxt, moduledata, uint64(len(Ctxt.Shlibs)))
-		adduint(Ctxt, moduledata, uint64(len(Ctxt.Shlibs)))
+		Addaddr(ctxt, moduledata, modulehashes)
+		adduint(ctxt, moduledata, uint64(len(ctxt.Shlibs)))
+		adduint(ctxt, moduledata, uint64(len(ctxt.Shlibs)))
 	}
 
 	// The rest of moduledata is zero initialized.
 	// When linking an object that does not contain the runtime we are
 	// creating the moduledata from scratch and it does not have a
 	// compiler-provided size, so read it from the type data.
-	moduledatatype := Linkrlookup(Ctxt, "type.runtime.moduledata", 0)
-	moduledata.Size = decodetype_size(moduledatatype)
-	Symgrow(Ctxt, moduledata, moduledata.Size)
+	moduledatatype := ctxt.Syms.ROLookup("type.runtime.moduledata", 0)
+	moduledata.Size = decodetypeSize(ctxt.Arch, moduledatatype)
+	Symgrow(moduledata, moduledata.Size)
 
-	lastmoduledatap := Linklookup(Ctxt, "runtime.lastmoduledatap", 0)
+	lastmoduledatap := ctxt.Syms.Lookup("runtime.lastmoduledatap", 0)
 	if lastmoduledatap.Type != obj.SDYNIMPORT {
 		lastmoduledatap.Type = obj.SNOPTRDATA
 		lastmoduledatap.Size = 0 // overwrite existing value
-		Addaddr(Ctxt, lastmoduledatap, moduledata)
+		Addaddr(ctxt, lastmoduledatap, moduledata)
 	}
 }
diff --git a/src/cmd/link/internal/ld/typelink.go b/src/cmd/link/internal/ld/typelink.go
new file mode 100644
index 0000000..48a1104
--- /dev/null
+++ b/src/cmd/link/internal/ld/typelink.go
@@ -0,0 +1,49 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+	"sort"
+
+	"cmd/internal/obj"
+)
+
+type byTypeStr []typelinkSortKey
+
+type typelinkSortKey struct {
+	TypeStr string
+	Type    *Symbol
+}
+
+func (s byTypeStr) Less(i, j int) bool { return s[i].TypeStr < s[j].TypeStr }
+func (s byTypeStr) Len() int           { return len(s) }
+func (s byTypeStr) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+
+// typelink generates the typelink table which is used by reflect.typelinks().
+// Types that should be added to the typelinks table are marked with the
+// MakeTypelink attribute by the compiler.
+func (ctxt *Link) typelink() {
+	typelinks := byTypeStr{}
+	for _, s := range ctxt.Syms.Allsym {
+		if s.Attr.Reachable() && s.Attr.MakeTypelink() {
+			typelinks = append(typelinks, typelinkSortKey{decodetypeStr(s), s})
+		}
+	}
+	sort.Sort(typelinks)
+
+	tl := ctxt.Syms.Lookup("runtime.typelink", 0)
+	tl.Type = obj.STYPELINK
+	tl.Attr |= AttrReachable | AttrLocal
+	tl.Size = int64(4 * len(typelinks))
+	tl.P = make([]byte, tl.Size)
+	tl.R = make([]Reloc, len(typelinks))
+	for i, s := range typelinks {
+		r := &tl.R[i]
+		r.Sym = s.Type
+		r.Off = int32(i * 4)
+		r.Siz = 4
+		r.Type = obj.R_ADDROFF
+	}
+}
diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go
index 19b3688..925aab6 100644
--- a/src/cmd/link/internal/ld/util.go
+++ b/src/cmd/link/internal/ld/util.go
@@ -7,10 +7,8 @@ package ld
 import (
 	"bytes"
 	"encoding/binary"
-	"log"
+	"fmt"
 	"os"
-	"runtime"
-	"runtime/pprof"
 	"strings"
 	"time"
 )
@@ -73,46 +71,42 @@ func AtExit(f func()) {
 	atExitFuncs = append(atExitFuncs, f)
 }
 
+// Exit exits with code after executing all atExitFuncs.
 func Exit(code int) {
 	for i := len(atExitFuncs) - 1; i >= 0; i-- {
-		f := atExitFuncs[i]
-		atExitFuncs = atExitFuncs[:i]
-		f()
+		atExitFuncs[i]()
 	}
 	os.Exit(code)
 }
 
-var (
-	cpuprofile     string
-	memprofile     string
-	memprofilerate int64
-)
+// Exitf logs an error message then calls Exit(2).
+func Exitf(format string, a ...interface{}) {
+	fmt.Fprintf(os.Stderr, os.Args[0]+": "+format+"\n", a...)
+	if coutbuf.f != nil {
+		coutbuf.f.Close()
+		mayberemoveoutfile()
+	}
+	Exit(2)
+}
 
-func startProfile() {
-	if cpuprofile != "" {
-		f, err := os.Create(cpuprofile)
-		if err != nil {
-			log.Fatalf("%v", err)
-		}
-		if err := pprof.StartCPUProfile(f); err != nil {
-			log.Fatalf("%v", err)
-		}
-		AtExit(pprof.StopCPUProfile)
+// Errorf logs an error message.
+//
+// If more than 20 errors have been printed, exit with an error.
+//
+// Logging an error means that on exit cmd/link will delete any
+// output file and return a non-zero error code.
+func Errorf(s *Symbol, format string, args ...interface{}) {
+	if s != nil {
+		format = s.Name + ": " + format
 	}
-	if memprofile != "" {
-		if memprofilerate != 0 {
-			runtime.MemProfileRate = int(memprofilerate)
-		}
-		f, err := os.Create(memprofile)
-		if err != nil {
-			log.Fatalf("%v", err)
-		}
-		AtExit(func() {
-			runtime.GC() // profile all outstanding allocations
-			if err := pprof.WriteHeapProfile(f); err != nil {
-				log.Fatalf("%v", err)
-			}
-		})
+	format += "\n"
+	fmt.Fprintf(os.Stderr, format, args...)
+	nerrors++
+	if *flagH {
+		panic("error")
+	}
+	if nerrors > 20 {
+		Exitf("too many errors")
 	}
 }
 
diff --git a/src/cmd/link/internal/mips/asm.go b/src/cmd/link/internal/mips/asm.go
new file mode 100644
index 0000000..b2c7289
--- /dev/null
+++ b/src/cmd/link/internal/mips/asm.go
@@ -0,0 +1,191 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
+//	Portions Copyright © 1997-1999 Vita Nuova Limited
+//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+//	Portions Copyright © 2004,2006 Bruce Ellis
+//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
+//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+//	Portions Copyright © 2016 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package mips
+
+import (
+	"cmd/internal/obj"
+	"cmd/link/internal/ld"
+	"fmt"
+	"log"
+)
+
+func gentext(ctxt *ld.Link) {
+	return
+}
+
+func adddynrel(ctxt *ld.Link, s *ld.Symbol, r *ld.Reloc) bool {
+	log.Fatalf("adddynrel not implemented")
+	return false
+}
+
+func elfreloc1(ctxt *ld.Link, r *ld.Reloc, sectoff int64) int {
+	return -1
+}
+
+func elfsetupplt(ctxt *ld.Link) {
+	return
+}
+
+func machoreloc1(s *ld.Symbol, r *ld.Reloc, sectoff int64) int {
+	return -1
+}
+
+func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
+	if ld.Linkmode == ld.LinkExternal {
+		return -1
+	}
+
+	switch r.Type {
+	case obj.R_CONST:
+		*val = r.Add
+		return 0
+
+	case obj.R_GOTOFF:
+		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0))
+		return 0
+
+	case obj.R_ADDRMIPS,
+		obj.R_ADDRMIPSU:
+		t := ld.Symaddr(r.Sym) + r.Add
+		o1 := ld.SysArch.ByteOrder.Uint32(s.P[r.Off:])
+		if r.Type == obj.R_ADDRMIPS {
+			*val = int64(o1&0xffff0000 | uint32(t)&0xffff)
+		} else {
+			*val = int64(o1&0xffff0000 | uint32((t+1<<15)>>16)&0xffff)
+		}
+		return 0
+
+	case obj.R_CALLMIPS,
+		obj.R_JMPMIPS:
+		// Low 26 bits = (S + A) >> 2
+		t := ld.Symaddr(r.Sym) + r.Add
+		o1 := ld.SysArch.ByteOrder.Uint32(s.P[r.Off:])
+		*val = int64(o1&0xfc000000 | uint32(t>>2)&^0xfc000000)
+		return 0
+	}
+
+	return -1
+}
+
+func archrelocvariant(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, t int64) int64 {
+	return -1
+}
+
+func asmb(ctxt *ld.Link) {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f asmb\n", obj.Cputime())
+	}
+
+	if ld.Iself {
+		ld.Asmbelfsetup()
+	}
+
+	sect := ld.Segtext.Sect
+	ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+	ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+	for sect = sect.Next; sect != nil; sect = sect.Next {
+		ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
+		ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+	}
+
+	if ld.Segrodata.Filelen > 0 {
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f rodatblk\n", obj.Cputime())
+		}
+
+		ld.Cseek(int64(ld.Segrodata.Fileoff))
+		ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+	}
+
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f datblk\n", obj.Cputime())
+	}
+
+	ld.Cseek(int64(ld.Segdata.Fileoff))
+	ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+
+	ld.Cseek(int64(ld.Segdwarf.Fileoff))
+	ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+
+	/* output symbol table */
+	ld.Symsize = 0
+
+	ld.Lcsize = 0
+	symo := uint32(0)
+	if !*ld.FlagS {
+		if !ld.Iself {
+			ld.Errorf(nil, "unsupported executable format")
+		}
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f sym\n", obj.Cputime())
+		}
+		symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+		symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
+
+		ld.Cseek(int64(symo))
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f elfsym\n", obj.Cputime())
+		}
+		ld.Asmelfsym(ctxt)
+		ld.Cflush()
+		ld.Cwrite(ld.Elfstrdat)
+
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f dwarf\n", obj.Cputime())
+		}
+
+		if ld.Linkmode == ld.LinkExternal {
+			ld.Elfemitreloc(ctxt)
+		}
+	}
+
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f header\n", obj.Cputime())
+	}
+
+	ld.Cseek(0)
+	switch ld.Headtype {
+	default:
+		ld.Errorf(nil, "unsupported operating system")
+	case obj.Hlinux:
+		ld.Asmbelf(ctxt, int64(symo))
+	}
+
+	ld.Cflush()
+	if *ld.FlagC {
+		fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
+		fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
+		fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
+		fmt.Printf("symsize=%d\n", ld.Symsize)
+		fmt.Printf("lcsize=%d\n", ld.Lcsize)
+		fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize))
+	}
+}
diff --git a/src/cmd/link/internal/mips/l.go b/src/cmd/link/internal/mips/l.go
new file mode 100644
index 0000000..adbde40
--- /dev/null
+++ b/src/cmd/link/internal/mips/l.go
@@ -0,0 +1,74 @@
+// Inferno utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
+//	Portions Copyright © 1997-1999 Vita Nuova Limited
+//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+//	Portions Copyright © 2004,2006 Bruce Ellis
+//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
+//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+//	Portions Copyright © 2016 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package mips
+
+// Writing object files.
+
+// cmd/9l/l.h from Vita Nuova.
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
+//	Portions Copyright © 1997-1999 Vita Nuova Limited
+//	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
+//	Portions Copyright © 2004,2006 Bruce Ellis
+//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
+//	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
+//	Portions Copyright © 2016 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+const (
+	MaxAlign  = 32 // max data alignment
+	MinAlign  = 1  // min data alignment
+	FuncAlign = 4
+)
+
+/* Used by ../internal/ld/dwarf.go */
+const (
+	DWARFREGSP = 29
+	DWARFREGLR = 31
+)
diff --git a/src/cmd/link/internal/mips/obj.go b/src/cmd/link/internal/mips/obj.go
new file mode 100644
index 0000000..a333876
--- /dev/null
+++ b/src/cmd/link/internal/mips/obj.go
@@ -0,0 +1,110 @@
+// Inferno utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+//	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
+//	Portions Copyright © 1997-1999 Vita Nuova Limited
+//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+//	Portions Copyright © 2004,2006 Bruce Ellis
+//	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
+//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+//	Portions Copyright © 2016 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package mips
+
+import (
+	"cmd/internal/obj"
+	"cmd/internal/sys"
+	"cmd/link/internal/ld"
+	"fmt"
+)
+
+// Reading object files.
+
+func Init() {
+	if obj.GOARCH == "mipsle" {
+		ld.SysArch = sys.ArchMIPSLE
+	} else {
+		ld.SysArch = sys.ArchMIPS
+	}
+
+	ld.Thearch.Funcalign = FuncAlign
+	ld.Thearch.Maxalign = MaxAlign
+	ld.Thearch.Minalign = MinAlign
+	ld.Thearch.Dwarfregsp = DWARFREGSP
+	ld.Thearch.Dwarfreglr = DWARFREGLR
+
+	ld.Thearch.Adddynrel = adddynrel
+	ld.Thearch.Archinit = archinit
+	ld.Thearch.Archreloc = archreloc
+	ld.Thearch.Archrelocvariant = archrelocvariant
+	ld.Thearch.Asmb = asmb
+	ld.Thearch.Elfreloc1 = elfreloc1
+	ld.Thearch.Elfsetupplt = elfsetupplt
+	ld.Thearch.Gentext = gentext
+	ld.Thearch.Machoreloc1 = machoreloc1
+	if ld.SysArch == sys.ArchMIPSLE {
+		ld.Thearch.Lput = ld.Lputl
+		ld.Thearch.Wput = ld.Wputl
+		ld.Thearch.Vput = ld.Vputl
+		ld.Thearch.Append16 = ld.Append16l
+		ld.Thearch.Append32 = ld.Append32l
+		ld.Thearch.Append64 = ld.Append64l
+	} else {
+		ld.Thearch.Lput = ld.Lputb
+		ld.Thearch.Wput = ld.Wputb
+		ld.Thearch.Vput = ld.Vputb
+		ld.Thearch.Append16 = ld.Append16b
+		ld.Thearch.Append32 = ld.Append32b
+		ld.Thearch.Append64 = ld.Append64b
+	}
+
+	ld.Thearch.Linuxdynld = "/lib/ld.so.1"
+
+	ld.Thearch.Freebsddynld = "XXX"
+	ld.Thearch.Openbsddynld = "XXX"
+	ld.Thearch.Netbsddynld = "XXX"
+	ld.Thearch.Dragonflydynld = "XXX"
+	ld.Thearch.Solarisdynld = "XXX"
+}
+
+func archinit(ctxt *ld.Link) {
+	switch ld.Headtype {
+	default:
+		ld.Exitf("unknown -H option: %v", ld.Headtype)
+	case obj.Hlinux: /* mips elf */
+		ld.Elfinit(ctxt)
+		ld.HEADR = ld.ELFRESERVE
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
+		}
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
+		}
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
+		}
+	}
+
+	if *ld.FlagDataAddr != 0 && *ld.FlagRound != 0 {
+		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(*ld.FlagDataAddr), uint32(*ld.FlagRound))
+	}
+}
diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go
index 32d9f23..1c3216f 100644
--- a/src/cmd/link/internal/mips64/asm.go
+++ b/src/cmd/link/internal/mips64/asm.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -38,13 +38,14 @@ import (
 	"log"
 )
 
-func gentext() {}
+func gentext(ctxt *ld.Link) {}
 
-func adddynrel(s *ld.LSym, r *ld.Reloc) {
+func adddynrel(ctxt *ld.Link, s *ld.Symbol, r *ld.Reloc) bool {
 	log.Fatalf("adddynrel not implemented")
+	return false
 }
 
-func elfreloc1(r *ld.Reloc, sectoff int64) int {
+func elfreloc1(ctxt *ld.Link, r *ld.Reloc, sectoff int64) int {
 	// mips64 ELF relocation (endian neutral)
 	//		offset	uint64
 	//		sym		uint32
@@ -93,15 +94,15 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func elfsetupplt() {
+func elfsetupplt(ctxt *ld.Link) {
 	return
 }
 
-func machoreloc1(r *ld.Reloc, sectoff int64) int {
+func machoreloc1(s *ld.Symbol, r *ld.Reloc, sectoff int64) int {
 	return -1
 }
 
-func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
+func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
 	if ld.Linkmode == ld.LinkExternal {
 		switch r.Type {
 		default:
@@ -120,7 +121,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 			}
 
 			if rs.Type != obj.SHOSTOBJ && rs.Type != obj.SDYNIMPORT && rs.Sect == nil {
-				ld.Diag("missing section for %s", rs.Name)
+				ld.Errorf(s, "missing section for %s", rs.Name)
 			}
 			r.Xsym = rs
 
@@ -142,7 +143,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 		return 0
 
 	case obj.R_GOTOFF:
-		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0))
+		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0))
 		return 0
 
 	case obj.R_ADDRMIPS,
@@ -160,7 +161,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 		// thread pointer is at 0x7000 offset from the start of TLS data area
 		t := ld.Symaddr(r.Sym) + r.Add - 0x7000
 		if t < -32768 || t >= 32678 {
-			ld.Diag("TLS offset out of range %d", t)
+			ld.Errorf(s, "TLS offset out of range %d", t)
 		}
 		o1 := ld.SysArch.ByteOrder.Uint32(s.P[r.Off:])
 		*val = int64(o1&0xffff0000 | uint32(t)&0xffff)
@@ -178,15 +179,14 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 	return -1
 }
 
-func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
+func archrelocvariant(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, t int64) int64 {
 	return -1
 }
 
-func asmb() {
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
+func asmb(ctxt *ld.Link) {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f asmb\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	if ld.Iself {
 		ld.Asmbelfsetup()
@@ -194,49 +194,52 @@ func asmb() {
 
 	sect := ld.Segtext.Sect
 	ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-	ld.Codeblk(int64(sect.Vaddr), int64(sect.Length))
+	ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
 	for sect = sect.Next; sect != nil; sect = sect.Next {
 		ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-		ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
+		ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
 	}
 
 	if ld.Segrodata.Filelen > 0 {
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f rodatblk\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-
 		ld.Cseek(int64(ld.Segrodata.Fileoff))
-		ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+		ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+	}
+	if ld.Segrelrodata.Filelen > 0 {
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f rodatblk\n", obj.Cputime())
+		}
+		ld.Cseek(int64(ld.Segrelrodata.Fileoff))
+		ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
 	}
 
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f datblk\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	ld.Cseek(int64(ld.Segdata.Fileoff))
-	ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+	ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
 
 	ld.Cseek(int64(ld.Segdwarf.Fileoff))
-	ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+	ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
 
 	/* output symbol table */
 	ld.Symsize = 0
 
 	ld.Lcsize = 0
 	symo := uint32(0)
-	if ld.Debug['s'] == 0 {
+	if !*ld.FlagS {
 		// TODO: rationalize
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f sym\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
 				symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
-				symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
+				symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
 			}
 
 		case obj.Hplan9:
@@ -244,26 +247,26 @@ func asmb() {
 		}
 
 		ld.Cseek(int64(symo))
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
-				if ld.Debug['v'] != 0 {
-					fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+				if ctxt.Debugvlog != 0 {
+					ctxt.Logf("%5.2f elfsym\n", obj.Cputime())
 				}
-				ld.Asmelfsym()
+				ld.Asmelfsym(ctxt)
 				ld.Cflush()
 				ld.Cwrite(ld.Elfstrdat)
 
 				if ld.Linkmode == ld.LinkExternal {
-					ld.Elfemitreloc()
+					ld.Elfemitreloc(ctxt)
 				}
 			}
 
 		case obj.Hplan9:
-			ld.Asmplan9sym()
+			ld.Asmplan9sym(ctxt)
 			ld.Cflush()
 
-			sym := ld.Linklookup(ld.Ctxt, "pclntab", 0)
+			sym := ctxt.Syms.Lookup("pclntab", 0)
 			if sym != nil {
 				ld.Lcsize = int32(len(sym.P))
 				for i := 0; int32(i) < ld.Lcsize; i++ {
@@ -275,13 +278,11 @@ func asmb() {
 		}
 	}
 
-	ld.Ctxt.Cursym = nil
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f header\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 	ld.Cseek(0)
-	switch ld.HEADTYPE {
+	switch ld.Headtype {
 	default:
 	case obj.Hplan9: /* plan 9 */
 		magic := uint32(4*18*18 + 7)
@@ -292,8 +293,8 @@ func asmb() {
 		ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */
 		ld.Thearch.Lput(uint32(ld.Segdata.Filelen))
 		ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
-		ld.Thearch.Lput(uint32(ld.Symsize))      /* nsyms */
-		ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */
+		ld.Thearch.Lput(uint32(ld.Symsize))          /* nsyms */
+		ld.Thearch.Lput(uint32(ld.Entryvalue(ctxt))) /* va of entry */
 		ld.Thearch.Lput(0)
 		ld.Thearch.Lput(uint32(ld.Lcsize))
 
@@ -302,11 +303,11 @@ func asmb() {
 		obj.Hnetbsd,
 		obj.Hopenbsd,
 		obj.Hnacl:
-		ld.Asmbelf(int64(symo))
+		ld.Asmbelf(ctxt, int64(symo))
 	}
 
 	ld.Cflush()
-	if ld.Debug['c'] != 0 {
+	if *ld.FlagC {
 		fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
 		fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
 		fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
diff --git a/src/cmd/link/internal/mips64/l.go b/src/cmd/link/internal/mips64/l.go
index e3f4fb3..d794122 100644
--- a/src/cmd/link/internal/mips64/l.go
+++ b/src/cmd/link/internal/mips64/l.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -62,13 +62,13 @@ package mips64
 // THE SOFTWARE.
 
 const (
-	MaxAlign  = 32 // max data alignment
-	MinAlign  = 1  // min data alignment
-	FuncAlign = 8
+	maxAlign  = 32 // max data alignment
+	minAlign  = 1  // min data alignment
+	funcAlign = 8
 )
 
 /* Used by ../internal/ld/dwarf.go */
 const (
-	DWARFREGSP = 29
-	DWARFREGLR = 31
+	dwarfRegSP = 29
+	dwarfRegLR = 31
 )
diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go
index ae9a280..b79cd9d 100644
--- a/src/cmd/link/internal/mips64/obj.go
+++ b/src/cmd/link/internal/mips64/obj.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -35,28 +35,20 @@ import (
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
 	"fmt"
-	"log"
 )
 
-// Reading object files.
-
-func Main() {
-	linkarchinit()
-	ld.Ldmain()
-}
-
-func linkarchinit() {
-	if obj.Getgoarch() == "mips64le" {
+func Init() {
+	if obj.GOARCH == "mips64le" {
 		ld.SysArch = sys.ArchMIPS64LE
 	} else {
 		ld.SysArch = sys.ArchMIPS64
 	}
 
-	ld.Thearch.Funcalign = FuncAlign
-	ld.Thearch.Maxalign = MaxAlign
-	ld.Thearch.Minalign = MinAlign
-	ld.Thearch.Dwarfregsp = DWARFREGSP
-	ld.Thearch.Dwarfreglr = DWARFREGLR
+	ld.Thearch.Funcalign = funcAlign
+	ld.Thearch.Maxalign = maxAlign
+	ld.Thearch.Minalign = minAlign
+	ld.Thearch.Dwarfregsp = dwarfRegSP
+	ld.Thearch.Dwarfreglr = dwarfRegLR
 
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Archinit = archinit
@@ -92,72 +84,53 @@ func linkarchinit() {
 	ld.Thearch.Solarisdynld = "XXX"
 }
 
-func archinit() {
-	// getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
-	// Go was built; see ../../make.bash.
-	if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" {
-		ld.Linkmode = ld.LinkInternal
-	}
-
-	switch ld.HEADTYPE {
-	default:
-		if ld.Linkmode == ld.LinkAuto {
-			ld.Linkmode = ld.LinkInternal
-		}
-		if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" {
-			log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE)))
-		}
-
-	case obj.Hlinux:
-		break
-	}
-
-	switch ld.HEADTYPE {
+func archinit(ctxt *ld.Link) {
+	switch ld.Headtype {
 	default:
-		ld.Exitf("unknown -H option: %v", ld.HEADTYPE)
+		ld.Exitf("unknown -H option: %v", ld.Headtype)
 
 	case obj.Hplan9: /* plan 9 */
 		ld.HEADR = 32
 
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 16*1024 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 16*1024 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 16 * 1024
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 16 * 1024
 		}
 
 	case obj.Hlinux: /* mips64 elf */
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 		ld.HEADR = ld.ELFRESERVE
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x10000 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x10000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
 		}
 
 	case obj.Hnacl:
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 		ld.HEADR = 0x10000
 		ld.Funcalign = 16
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x20000
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x20000
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x10000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
 		}
 	}
 
-	if ld.INITDAT != 0 && ld.INITRND != 0 {
-		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND))
+	if *ld.FlagDataAddr != 0 && *ld.FlagRound != 0 {
+		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(*ld.FlagDataAddr), uint32(*ld.FlagRound))
 	}
 }
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
index bd2e23f..97107b9 100644
--- a/src/cmd/link/internal/ppc64/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -38,7 +38,7 @@ import (
 	"log"
 )
 
-func genplt() {
+func genplt(ctxt *ld.Link) {
 	// The ppc64 ABI PLT has similar concepts to other
 	// architectures, but is laid out quite differently. When we
 	// see an R_PPC64_REL24 relocation to a dynamic symbol
@@ -87,7 +87,7 @@ func genplt() {
 	//
 	// This assumes "case 1" from the ABI, where the caller needs
 	// us to save and restore the TOC pointer.
-	for _, s := range ld.Ctxt.Textp {
+	for _, s := range ctxt.Textp {
 		for i := range s.R {
 			r := &s.R[i]
 			if r.Type != 256+ld.R_PPC64_REL24 || r.Sym.Type != obj.SDYNIMPORT {
@@ -96,20 +96,20 @@ func genplt() {
 
 			// Reserve PLT entry and generate symbol
 			// resolver
-			addpltsym(ld.Ctxt, r.Sym)
+			addpltsym(ctxt, r.Sym)
 
 			// Generate call stub
 			n := fmt.Sprintf("%s.%s", s.Name, r.Sym.Name)
 
-			stub := ld.Linklookup(ld.Ctxt, n, 0)
+			stub := ctxt.Syms.Lookup(n, 0)
 			if s.Attr.Reachable() {
 				stub.Attr |= ld.AttrReachable
 			}
 			if stub.Size == 0 {
 				// Need outer to resolve .TOC.
 				stub.Outer = s
-				ld.Ctxt.Textp = append(ld.Ctxt.Textp, stub)
-				gencallstub(1, stub, r.Sym)
+				ctxt.Textp = append(ctxt.Textp, stub)
+				gencallstub(ctxt, 1, stub, r.Sym)
 			}
 
 			// Update the relocation to use the call stub
@@ -118,29 +118,29 @@ func genplt() {
 			// Restore TOC after bl. The compiler put a
 			// nop here for us to overwrite.
 			const o1 = 0xe8410018 // ld r2,24(r1)
-			ld.Ctxt.Arch.ByteOrder.PutUint32(s.P[r.Off+4:], o1)
+			ctxt.Arch.ByteOrder.PutUint32(s.P[r.Off+4:], o1)
 		}
 	}
 }
 
-func genaddmoduledata() {
-	addmoduledata := ld.Linkrlookup(ld.Ctxt, "runtime.addmoduledata", 0)
+func genaddmoduledata(ctxt *ld.Link) {
+	addmoduledata := ctxt.Syms.ROLookup("runtime.addmoduledata", 0)
 	if addmoduledata.Type == obj.STEXT {
 		return
 	}
 	addmoduledata.Attr |= ld.AttrReachable
-	initfunc := ld.Linklookup(ld.Ctxt, "go.link.addmoduledata", 0)
+	initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
 	initfunc.Type = obj.STEXT
 	initfunc.Attr |= ld.AttrLocal
 	initfunc.Attr |= ld.AttrReachable
 	o := func(op uint32) {
-		ld.Adduint32(ld.Ctxt, initfunc, op)
+		ld.Adduint32(ctxt, initfunc, op)
 	}
 	// addis r2, r12, .TOC.-func at ha
 	rel := ld.Addrel(initfunc)
 	rel.Off = int32(initfunc.Size)
 	rel.Siz = 8
-	rel.Sym = ld.Linklookup(ld.Ctxt, ".TOC.", 0)
+	rel.Sym = ctxt.Syms.Lookup(".TOC.", 0)
 	rel.Type = obj.R_ADDRPOWER_PCREL
 	o(0x3c4c0000)
 	// addi r2, r2, .TOC.-func at l
@@ -153,7 +153,7 @@ func genaddmoduledata() {
 	rel = ld.Addrel(initfunc)
 	rel.Off = int32(initfunc.Size)
 	rel.Siz = 8
-	rel.Sym = ld.Linklookup(ld.Ctxt, "local.moduledata", 0)
+	rel.Sym = ctxt.Syms.Lookup("local.moduledata", 0)
 	rel.Type = obj.R_ADDRPOWER_GOT
 	o(0x3c620000)
 	// ld r3, local.moduledata at got@l(r3)
@@ -176,39 +176,39 @@ func genaddmoduledata() {
 	// blr
 	o(0x4e800020)
 
-	ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
-	initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
+	initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
+	ctxt.Textp = append(ctxt.Textp, initfunc)
 	initarray_entry.Attr |= ld.AttrReachable
 	initarray_entry.Attr |= ld.AttrLocal
 	initarray_entry.Type = obj.SINITARR
-	ld.Addaddr(ld.Ctxt, initarray_entry, initfunc)
+	ld.Addaddr(ctxt, initarray_entry, initfunc)
 }
 
-func gentext() {
-	if ld.DynlinkingGo() {
-		genaddmoduledata()
+func gentext(ctxt *ld.Link) {
+	if ctxt.DynlinkingGo() {
+		genaddmoduledata(ctxt)
 	}
 
 	if ld.Linkmode == ld.LinkInternal {
-		genplt()
+		genplt(ctxt)
 	}
 }
 
 // Construct a call stub in stub that calls symbol targ via its PLT
 // entry.
-func gencallstub(abicase int, stub *ld.LSym, targ *ld.LSym) {
+func gencallstub(ctxt *ld.Link, abicase int, stub *ld.Symbol, targ *ld.Symbol) {
 	if abicase != 1 {
 		// If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC
 		// relocations, we'll need to implement cases 2 and 3.
 		log.Fatalf("gencallstub only implements case 1 calls")
 	}
 
-	plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
+	plt := ctxt.Syms.Lookup(".plt", 0)
 
 	stub.Type = obj.STEXT
 
 	// Save TOC pointer in TOC save slot
-	ld.Adduint32(ld.Ctxt, stub, 0xf8410018) // std r2,24(r1)
+	ld.Adduint32(ctxt, stub, 0xf8410018) // std r2,24(r1)
 
 	// Load the function pointer from the PLT.
 	r := ld.Addrel(stub)
@@ -217,38 +217,37 @@ func gencallstub(abicase int, stub *ld.LSym, targ *ld.LSym) {
 	r.Sym = plt
 	r.Add = int64(targ.Plt)
 	r.Siz = 2
-	if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+	if ctxt.Arch.ByteOrder == binary.BigEndian {
 		r.Off += int32(r.Siz)
 	}
 	r.Type = obj.R_POWER_TOC
 	r.Variant = ld.RV_POWER_HA
-	ld.Adduint32(ld.Ctxt, stub, 0x3d820000) // addis r12,r2,targ at plt@toc at ha
+	ld.Adduint32(ctxt, stub, 0x3d820000) // addis r12,r2,targ at plt@toc at ha
 	r = ld.Addrel(stub)
 	r.Off = int32(stub.Size)
 	r.Sym = plt
 	r.Add = int64(targ.Plt)
 	r.Siz = 2
-	if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+	if ctxt.Arch.ByteOrder == binary.BigEndian {
 		r.Off += int32(r.Siz)
 	}
 	r.Type = obj.R_POWER_TOC
 	r.Variant = ld.RV_POWER_LO
-	ld.Adduint32(ld.Ctxt, stub, 0xe98c0000) // ld r12,targ at plt@toc at l(r12)
+	ld.Adduint32(ctxt, stub, 0xe98c0000) // ld r12,targ at plt@toc at l(r12)
 
 	// Jump to the loaded pointer
-	ld.Adduint32(ld.Ctxt, stub, 0x7d8903a6) // mtctr r12
-	ld.Adduint32(ld.Ctxt, stub, 0x4e800420) // bctr
+	ld.Adduint32(ctxt, stub, 0x7d8903a6) // mtctr r12
+	ld.Adduint32(ctxt, stub, 0x4e800420) // bctr
 }
 
-func adddynrel(s *ld.LSym, r *ld.Reloc) {
+func adddynrel(ctxt *ld.Link, s *ld.Symbol, r *ld.Reloc) bool {
 	targ := r.Sym
-	ld.Ctxt.Cursym = s
 
 	switch r.Type {
 	default:
 		if r.Type >= 256 {
-			ld.Diag("unexpected relocation type %d", r.Type)
-			return
+			ld.Errorf(s, "unexpected relocation type %d", r.Type)
+			return false
 		}
 
 		// Handle relocations found in ELF object files.
@@ -264,96 +263,96 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 
 		if targ.Type == obj.SDYNIMPORT {
 			// Should have been handled in elfsetupplt
-			ld.Diag("unexpected R_PPC64_REL24 for dyn import")
+			ld.Errorf(s, "unexpected R_PPC64_REL24 for dyn import")
 		}
 
-		return
+		return true
 
 	case 256 + ld.R_PPC_REL32:
 		r.Type = obj.R_PCREL
 		r.Add += 4
 
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected R_PPC_REL32 for dyn import")
+			ld.Errorf(s, "unexpected R_PPC_REL32 for dyn import")
 		}
 
-		return
+		return true
 
 	case 256 + ld.R_PPC64_ADDR64:
 		r.Type = obj.R_ADDR
 		if targ.Type == obj.SDYNIMPORT {
 			// These happen in .toc sections
-			ld.Adddynsym(ld.Ctxt, targ)
+			ld.Adddynsym(ctxt, targ)
 
-			rela := ld.Linklookup(ld.Ctxt, ".rela", 0)
-			ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off))
-			ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_PPC64_ADDR64))
-			ld.Adduint64(ld.Ctxt, rela, uint64(r.Add))
+			rela := ctxt.Syms.Lookup(".rela", 0)
+			ld.Addaddrplus(ctxt, rela, s, int64(r.Off))
+			ld.Adduint64(ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_PPC64_ADDR64))
+			ld.Adduint64(ctxt, rela, uint64(r.Add))
 			r.Type = 256 // ignore during relocsym
 		}
 
-		return
+		return true
 
 	case 256 + ld.R_PPC64_TOC16:
 		r.Type = obj.R_POWER_TOC
 		r.Variant = ld.RV_POWER_LO | ld.RV_CHECK_OVERFLOW
-		return
+		return true
 
 	case 256 + ld.R_PPC64_TOC16_LO:
 		r.Type = obj.R_POWER_TOC
 		r.Variant = ld.RV_POWER_LO
-		return
+		return true
 
 	case 256 + ld.R_PPC64_TOC16_HA:
 		r.Type = obj.R_POWER_TOC
 		r.Variant = ld.RV_POWER_HA | ld.RV_CHECK_OVERFLOW
-		return
+		return true
 
 	case 256 + ld.R_PPC64_TOC16_HI:
 		r.Type = obj.R_POWER_TOC
 		r.Variant = ld.RV_POWER_HI | ld.RV_CHECK_OVERFLOW
-		return
+		return true
 
 	case 256 + ld.R_PPC64_TOC16_DS:
 		r.Type = obj.R_POWER_TOC
 		r.Variant = ld.RV_POWER_DS | ld.RV_CHECK_OVERFLOW
-		return
+		return true
 
 	case 256 + ld.R_PPC64_TOC16_LO_DS:
 		r.Type = obj.R_POWER_TOC
 		r.Variant = ld.RV_POWER_DS
-		return
+		return true
 
 	case 256 + ld.R_PPC64_REL16_LO:
 		r.Type = obj.R_PCREL
 		r.Variant = ld.RV_POWER_LO
 		r.Add += 2 // Compensate for relocation size of 2
-		return
+		return true
 
 	case 256 + ld.R_PPC64_REL16_HI:
 		r.Type = obj.R_PCREL
 		r.Variant = ld.RV_POWER_HI | ld.RV_CHECK_OVERFLOW
 		r.Add += 2
-		return
+		return true
 
 	case 256 + ld.R_PPC64_REL16_HA:
 		r.Type = obj.R_PCREL
 		r.Variant = ld.RV_POWER_HA | ld.RV_CHECK_OVERFLOW
 		r.Add += 2
-		return
+		return true
 	}
 
 	// Handle references to ELF symbols from our own object files.
 	if targ.Type != obj.SDYNIMPORT {
-		return
+		return true
 	}
 
 	// TODO(austin): Translate our relocations to ELF
 
-	ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type)
+	return false
 }
 
-func elfreloc1(r *ld.Reloc, sectoff int64) int {
+func elfreloc1(ctxt *ld.Link, r *ld.Reloc, sectoff int64) int {
 	ld.Thearch.Vput(uint64(sectoff))
 
 	elfsym := r.Xsym.ElfsymForReloc()
@@ -432,8 +431,8 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func elfsetupplt() {
-	plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
+func elfsetupplt(ctxt *ld.Link) {
+	plt := ctxt.Syms.Lookup(".plt", 0)
 	if plt.Size == 0 {
 		// The dynamic linker stores the address of the
 		// dynamic resolver and the DSO identifier in the two
@@ -443,31 +442,31 @@ func elfsetupplt() {
 	}
 }
 
-func machoreloc1(r *ld.Reloc, sectoff int64) int {
+func machoreloc1(s *ld.Symbol, r *ld.Reloc, sectoff int64) int {
 	return -1
 }
 
 // Return the value of .TOC. for symbol s
-func symtoc(s *ld.LSym) int64 {
-	var toc *ld.LSym
+func symtoc(ctxt *ld.Link, s *ld.Symbol) int64 {
+	var toc *ld.Symbol
 
 	if s.Outer != nil {
-		toc = ld.Linkrlookup(ld.Ctxt, ".TOC.", int(s.Outer.Version))
+		toc = ctxt.Syms.ROLookup(".TOC.", int(s.Outer.Version))
 	} else {
-		toc = ld.Linkrlookup(ld.Ctxt, ".TOC.", int(s.Version))
+		toc = ctxt.Syms.ROLookup(".TOC.", int(s.Version))
 	}
 
 	if toc == nil {
-		ld.Diag("TOC-relative relocation in object without .TOC.")
+		ld.Errorf(s, "TOC-relative relocation in object without .TOC.")
 		return 0
 	}
 
 	return toc.Value
 }
 
-func archrelocaddr(r *ld.Reloc, s *ld.LSym, val *int64) int {
+func archrelocaddr(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
 	var o1, o2 uint32
-	if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+	if ctxt.Arch.ByteOrder == binary.BigEndian {
 		o1 = uint32(*val >> 32)
 		o2 = uint32(*val)
 	} else {
@@ -484,7 +483,7 @@ func archrelocaddr(r *ld.Reloc, s *ld.LSym, val *int64) int {
 
 	t := ld.Symaddr(r.Sym) + r.Add
 	if t < 0 || t >= 1<<31 {
-		ld.Ctxt.Diag("relocation for %s is too big (>=2G): %d", s.Name, ld.Symaddr(r.Sym))
+		ld.Errorf(s, "relocation for %s is too big (>=2G): %d", s.Name, ld.Symaddr(r.Sym))
 	}
 	if t&0x8000 != 0 {
 		t += 0x10000
@@ -498,7 +497,7 @@ func archrelocaddr(r *ld.Reloc, s *ld.LSym, val *int64) int {
 	case obj.R_ADDRPOWER_DS:
 		o1 |= (uint32(t) >> 16) & 0xffff
 		if t&3 != 0 {
-			ld.Ctxt.Diag("bad DS reloc for %s: %d", s.Name, ld.Symaddr(r.Sym))
+			ld.Errorf(s, "bad DS reloc for %s: %d", s.Name, ld.Symaddr(r.Sym))
 		}
 		o2 |= uint32(t) & 0xfffc
 
@@ -506,7 +505,7 @@ func archrelocaddr(r *ld.Reloc, s *ld.LSym, val *int64) int {
 		return -1
 	}
 
-	if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+	if ctxt.Arch.ByteOrder == binary.BigEndian {
 		*val = int64(o1)<<32 | int64(o2)
 	} else {
 		*val = int64(o2)<<32 | int64(o1)
@@ -514,7 +513,70 @@ func archrelocaddr(r *ld.Reloc, s *ld.LSym, val *int64) int {
 	return 0
 }
 
-func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
+// resolve direct jump relocation r in s, and add trampoline if necessary
+func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) {
+
+	t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off))
+	switch r.Type {
+	case obj.R_CALLPOWER:
+
+		// If branch offset is too far then create a trampoline.
+
+		if int64(int32(t<<6)>>6) != t || (*ld.FlagDebugTramp > 1 && s.File != r.Sym.File) {
+			var tramp *ld.Symbol
+			for i := 0; ; i++ {
+
+				// Using r.Add as part of the name is significant in functions like duffzero where the call
+				// target is at some offset within the function.  Calls to duff+8 and duff+256 must appear as
+				// distinct trampolines.
+
+				name := r.Sym.Name
+				if r.Add == 0 {
+					name = name + fmt.Sprintf("-tramp%d", i)
+				} else {
+					name = name + fmt.Sprintf("%+x-tramp%d", r.Add, i)
+				}
+
+				// Look up the trampoline in case it already exists
+
+				tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
+				if tramp.Value == 0 {
+					break
+				}
+
+				t = ld.Symaddr(tramp) + r.Add - (s.Value + int64(r.Off))
+
+				// If the offset of the trampoline that has been found is within range, use it.
+				if int64(int32(t<<6)>>6) == t {
+					break
+				}
+			}
+			if tramp.Type == 0 {
+				ctxt.AddTramp(tramp)
+				tramp.Size = 16 // 4 instructions
+				tramp.P = make([]byte, tramp.Size)
+				t = ld.Symaddr(r.Sym) + r.Add
+				f := t & 0xffff0000
+				o1 := uint32(0x3fe00000 | (f >> 16)) // lis r31,trampaddr hi (r31 is temp reg)
+				f = t & 0xffff
+				o2 := uint32(0x63ff0000 | f) // ori r31,trampaddr lo
+				o3 := uint32(0x7fe903a6)     // mtctr
+				o4 := uint32(0x4e800420)     // bctr
+				ld.SysArch.ByteOrder.PutUint32(tramp.P, o1)
+				ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2)
+				ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3)
+				ld.SysArch.ByteOrder.PutUint32(tramp.P[12:], o4)
+			}
+			r.Sym = tramp
+			r.Add = 0 // This was folded into the trampoline target address
+			r.Done = 0
+		}
+	default:
+		ld.Errorf(s, "trampoline called with non-jump reloc: %v", r.Type)
+	}
+}
+
+func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
 	if ld.Linkmode == ld.LinkExternal {
 		switch r.Type {
 		default:
@@ -544,7 +606,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 			}
 
 			if rs.Type != obj.SHOSTOBJ && rs.Type != obj.SDYNIMPORT && rs.Sect == nil {
-				ld.Diag("missing section for %s", rs.Name)
+				ld.Errorf(s, "missing section for %s", rs.Name)
 			}
 			r.Xsym = rs
 
@@ -564,30 +626,30 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 		return 0
 
 	case obj.R_GOTOFF:
-		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0))
+		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0))
 		return 0
 
 	case obj.R_ADDRPOWER, obj.R_ADDRPOWER_DS:
-		return archrelocaddr(r, s, val)
+		return archrelocaddr(ctxt, r, s, val)
 
 	case obj.R_CALLPOWER:
 		// Bits 6 through 29 = (S + A - P) >> 2
 
 		t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off))
+
 		if t&3 != 0 {
-			ld.Ctxt.Diag("relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t)
+			ld.Errorf(s, "relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t)
 		}
+		// If branch offset is too far then create a trampoline.
+
 		if int64(int32(t<<6)>>6) != t {
-			// TODO(austin) This can happen if text > 32M.
-			// Add a call trampoline to .text in that case.
-			ld.Ctxt.Diag("relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t)
+			ld.Errorf(s, "direct call too far: %s %x", r.Sym.Name, t)
 		}
-
 		*val |= int64(uint32(t) &^ 0xfc000003)
 		return 0
 
 	case obj.R_POWER_TOC: // S + A - .TOC.
-		*val = ld.Symaddr(r.Sym) + r.Add - symtoc(s)
+		*val = ld.Symaddr(r.Sym) + r.Add - symtoc(ctxt, s)
 
 		return 0
 
@@ -598,7 +660,7 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 		// Specification".
 		v := r.Sym.Value - 0x7000
 		if int64(int16(v)) != v {
-			ld.Diag("TLS offset out of range %d", v)
+			ld.Errorf(s, "TLS offset out of range %d", v)
 		}
 		*val = (*val &^ 0xffff) | (v & 0xffff)
 		return 0
@@ -607,10 +669,10 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 	return -1
 }
 
-func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
+func archrelocvariant(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, t int64) int64 {
 	switch r.Variant & ld.RV_TYPE_MASK {
 	default:
-		ld.Diag("unexpected relocation variant %d", r.Variant)
+		ld.Errorf(s, "unexpected relocation variant %d", r.Variant)
 		fallthrough
 
 	case ld.RV_NONE:
@@ -621,7 +683,7 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
 			// Whether to check for signed or unsigned
 			// overflow depends on the instruction
 			var o1 uint32
-			if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+			if ctxt.Arch.ByteOrder == binary.BigEndian {
 				o1 = ld.Be32(s.P[r.Off-2:])
 			} else {
 				o1 = ld.Le32(s.P[r.Off:])
@@ -655,7 +717,7 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
 			// Whether to check for signed or unsigned
 			// overflow depends on the instruction
 			var o1 uint32
-			if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+			if ctxt.Arch.ByteOrder == binary.BigEndian {
 				o1 = ld.Be32(s.P[r.Off-2:])
 			} else {
 				o1 = ld.Le32(s.P[r.Off:])
@@ -679,13 +741,13 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
 
 	case ld.RV_POWER_DS:
 		var o1 uint32
-		if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+		if ctxt.Arch.ByteOrder == binary.BigEndian {
 			o1 = uint32(ld.Be16(s.P[r.Off:]))
 		} else {
 			o1 = uint32(ld.Le16(s.P[r.Off:]))
 		}
 		if t&3 != 0 {
-			ld.Diag("relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t)
+			ld.Errorf(s, "relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t)
 		}
 		if (r.Variant&ld.RV_CHECK_OVERFLOW != 0) && int64(int16(t)) != t {
 			goto overflow
@@ -694,11 +756,11 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
 	}
 
 overflow:
-	ld.Diag("relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t)
+	ld.Errorf(s, "relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t)
 	return t
 }
 
-func addpltsym(ctxt *ld.Link, s *ld.LSym) {
+func addpltsym(ctxt *ld.Link, s *ld.Symbol) {
 	if s.Plt >= 0 {
 		return
 	}
@@ -706,14 +768,14 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 	ld.Adddynsym(ctxt, s)
 
 	if ld.Iself {
-		plt := ld.Linklookup(ctxt, ".plt", 0)
-		rela := ld.Linklookup(ctxt, ".rela.plt", 0)
+		plt := ctxt.Syms.Lookup(".plt", 0)
+		rela := ctxt.Syms.Lookup(".rela.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt()
+			elfsetupplt(ctxt)
 		}
 
 		// Create the glink resolver if necessary
-		glink := ensureglinkresolver()
+		glink := ensureglinkresolver(ctxt)
 
 		// Write symbol resolver stub (just a branch to the
 		// glink resolver stub)
@@ -739,13 +801,13 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 		ld.Adduint64(ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_PPC64_JMP_SLOT))
 		ld.Adduint64(ctxt, rela, 0)
 	} else {
-		ld.Diag("addpltsym: unsupported binary format")
+		ld.Errorf(s, "addpltsym: unsupported binary format")
 	}
 }
 
 // Generate the glink resolver stub if necessary and return the .glink section
-func ensureglinkresolver() *ld.LSym {
-	glink := ld.Linklookup(ld.Ctxt, ".glink", 0)
+func ensureglinkresolver(ctxt *ld.Link) *ld.Symbol {
+	glink := ctxt.Syms.Lookup(".glink", 0)
 	if glink.Size != 0 {
 		return glink
 	}
@@ -757,107 +819,111 @@ func ensureglinkresolver() *ld.LSym {
 	//
 	// This stub is PIC, so first get the PC of label 1 into r11.
 	// Other things will be relative to this.
-	ld.Adduint32(ld.Ctxt, glink, 0x7c0802a6) // mflr r0
-	ld.Adduint32(ld.Ctxt, glink, 0x429f0005) // bcl 20,31,1f
-	ld.Adduint32(ld.Ctxt, glink, 0x7d6802a6) // 1: mflr r11
-	ld.Adduint32(ld.Ctxt, glink, 0x7c0803a6) // mtlf r0
+	ld.Adduint32(ctxt, glink, 0x7c0802a6) // mflr r0
+	ld.Adduint32(ctxt, glink, 0x429f0005) // bcl 20,31,1f
+	ld.Adduint32(ctxt, glink, 0x7d6802a6) // 1: mflr r11
+	ld.Adduint32(ctxt, glink, 0x7c0803a6) // mtlf r0
 
 	// Compute the .plt array index from the entry point address.
 	// Because this is PIC, everything is relative to label 1b (in
 	// r11):
 	//   r0 = ((r12 - r11) - (res_0 - r11)) / 4 = (r12 - res_0) / 4
-	ld.Adduint32(ld.Ctxt, glink, 0x3800ffd0) // li r0,-(res_0-1b)=-48
-	ld.Adduint32(ld.Ctxt, glink, 0x7c006214) // add r0,r0,r12
-	ld.Adduint32(ld.Ctxt, glink, 0x7c0b0050) // sub r0,r0,r11
-	ld.Adduint32(ld.Ctxt, glink, 0x7800f082) // srdi r0,r0,2
+	ld.Adduint32(ctxt, glink, 0x3800ffd0) // li r0,-(res_0-1b)=-48
+	ld.Adduint32(ctxt, glink, 0x7c006214) // add r0,r0,r12
+	ld.Adduint32(ctxt, glink, 0x7c0b0050) // sub r0,r0,r11
+	ld.Adduint32(ctxt, glink, 0x7800f082) // srdi r0,r0,2
 
 	// r11 = address of the first byte of the PLT
 	r := ld.Addrel(glink)
 
 	r.Off = int32(glink.Size)
-	r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+	r.Sym = ctxt.Syms.Lookup(".plt", 0)
 	r.Siz = 8
 	r.Type = obj.R_ADDRPOWER
 
-	ld.Adduint32(ld.Ctxt, glink, 0x3d600000) // addis r11,0,.plt at ha
-	ld.Adduint32(ld.Ctxt, glink, 0x396b0000) // addi r11,r11,.plt at l
+	ld.Adduint32(ctxt, glink, 0x3d600000) // addis r11,0,.plt at ha
+	ld.Adduint32(ctxt, glink, 0x396b0000) // addi r11,r11,.plt at l
 
 	// Load r12 = dynamic resolver address and r11 = DSO
 	// identifier from the first two doublewords of the PLT.
-	ld.Adduint32(ld.Ctxt, glink, 0xe98b0000) // ld r12,0(r11)
-	ld.Adduint32(ld.Ctxt, glink, 0xe96b0008) // ld r11,8(r11)
+	ld.Adduint32(ctxt, glink, 0xe98b0000) // ld r12,0(r11)
+	ld.Adduint32(ctxt, glink, 0xe96b0008) // ld r11,8(r11)
 
 	// Jump to the dynamic resolver
-	ld.Adduint32(ld.Ctxt, glink, 0x7d8903a6) // mtctr r12
-	ld.Adduint32(ld.Ctxt, glink, 0x4e800420) // bctr
+	ld.Adduint32(ctxt, glink, 0x7d8903a6) // mtctr r12
+	ld.Adduint32(ctxt, glink, 0x4e800420) // bctr
 
 	// The symbol resolvers must immediately follow.
 	//   res_0:
 
 	// Add DT_PPC64_GLINK .dynamic entry, which points to 32 bytes
 	// before the first symbol resolver stub.
-	s := ld.Linklookup(ld.Ctxt, ".dynamic", 0)
+	s := ctxt.Syms.Lookup(".dynamic", 0)
 
-	ld.Elfwritedynentsymplus(s, ld.DT_PPC64_GLINK, glink, glink.Size-32)
+	ld.Elfwritedynentsymplus(ctxt, s, ld.DT_PPC64_GLINK, glink, glink.Size-32)
 
 	return glink
 }
 
-func asmb() {
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
+func asmb(ctxt *ld.Link) {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f asmb\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	if ld.Iself {
 		ld.Asmbelfsetup()
 	}
 
-	sect := ld.Segtext.Sect
-	ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-	ld.Codeblk(int64(sect.Vaddr), int64(sect.Length))
-	for sect = sect.Next; sect != nil; sect = sect.Next {
+	for sect := ld.Segtext.Sect; sect != nil; sect = sect.Next {
 		ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-		ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
+		// Handle additional text sections with Codeblk
+		if sect.Name == ".text" {
+			ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+		} else {
+			ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+		}
 	}
 
 	if ld.Segrodata.Filelen > 0 {
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f rodatblk\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-
 		ld.Cseek(int64(ld.Segrodata.Fileoff))
-		ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+		ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+	}
+	if ld.Segrelrodata.Filelen > 0 {
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f relrodatblk\n", obj.Cputime())
+		}
+		ld.Cseek(int64(ld.Segrelrodata.Fileoff))
+		ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
 	}
 
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f datblk\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	ld.Cseek(int64(ld.Segdata.Fileoff))
-	ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+	ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
 
 	ld.Cseek(int64(ld.Segdwarf.Fileoff))
-	ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+	ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
 
 	/* output symbol table */
 	ld.Symsize = 0
 
 	ld.Lcsize = 0
 	symo := uint32(0)
-	if ld.Debug['s'] == 0 {
+	if !*ld.FlagS {
 		// TODO: rationalize
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f sym\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
 				symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
-				symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
+				symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
 			}
 
 		case obj.Hplan9:
@@ -865,26 +931,26 @@ func asmb() {
 		}
 
 		ld.Cseek(int64(symo))
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
-				if ld.Debug['v'] != 0 {
-					fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+				if ctxt.Debugvlog != 0 {
+					ctxt.Logf("%5.2f elfsym\n", obj.Cputime())
 				}
-				ld.Asmelfsym()
+				ld.Asmelfsym(ctxt)
 				ld.Cflush()
 				ld.Cwrite(ld.Elfstrdat)
 
 				if ld.Linkmode == ld.LinkExternal {
-					ld.Elfemitreloc()
+					ld.Elfemitreloc(ctxt)
 				}
 			}
 
 		case obj.Hplan9:
-			ld.Asmplan9sym()
+			ld.Asmplan9sym(ctxt)
 			ld.Cflush()
 
-			sym := ld.Linklookup(ld.Ctxt, "pclntab", 0)
+			sym := ctxt.Syms.Lookup("pclntab", 0)
 			if sym != nil {
 				ld.Lcsize = int32(len(sym.P))
 				for i := 0; int32(i) < ld.Lcsize; i++ {
@@ -896,21 +962,19 @@ func asmb() {
 		}
 	}
 
-	ld.Ctxt.Cursym = nil
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f header\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 	ld.Cseek(0)
-	switch ld.HEADTYPE {
+	switch ld.Headtype {
 	default:
 	case obj.Hplan9: /* plan 9 */
 		ld.Thearch.Lput(0x647)                      /* magic */
 		ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */
 		ld.Thearch.Lput(uint32(ld.Segdata.Filelen))
 		ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
-		ld.Thearch.Lput(uint32(ld.Symsize))      /* nsyms */
-		ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */
+		ld.Thearch.Lput(uint32(ld.Symsize))          /* nsyms */
+		ld.Thearch.Lput(uint32(ld.Entryvalue(ctxt))) /* va of entry */
 		ld.Thearch.Lput(0)
 		ld.Thearch.Lput(uint32(ld.Lcsize))
 
@@ -919,11 +983,11 @@ func asmb() {
 		obj.Hnetbsd,
 		obj.Hopenbsd,
 		obj.Hnacl:
-		ld.Asmbelf(int64(symo))
+		ld.Asmbelf(ctxt, int64(symo))
 	}
 
 	ld.Cflush()
-	if ld.Debug['c'] != 0 {
+	if *ld.FlagC {
 		fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
 		fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
 		fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
diff --git a/src/cmd/link/internal/ppc64/l.go b/src/cmd/link/internal/ppc64/l.go
index 2e5c235..f7ae33d 100644
--- a/src/cmd/link/internal/ppc64/l.go
+++ b/src/cmd/link/internal/ppc64/l.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -62,13 +62,13 @@ package ppc64
 // THE SOFTWARE.
 
 const (
-	MaxAlign  = 32 // max data alignment
-	MinAlign  = 1  // min data alignment
-	FuncAlign = 8
+	maxAlign  = 32 // max data alignment
+	minAlign  = 1  // min data alignment
+	funcAlign = 8
 )
 
 /* Used by ../internal/ld/dwarf.go */
 const (
-	DWARFREGSP = 1
-	DWARFREGLR = 65
+	dwarfRegSP = 1
+	dwarfRegLR = 65
 )
diff --git a/src/cmd/link/internal/ppc64/obj.go b/src/cmd/link/internal/ppc64/obj.go
index b619eb9..6eff2f4 100644
--- a/src/cmd/link/internal/ppc64/obj.go
+++ b/src/cmd/link/internal/ppc64/obj.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -35,28 +35,20 @@ import (
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
 	"fmt"
-	"log"
 )
 
-// Reading object files.
-
-func Main() {
-	linkarchinit()
-	ld.Ldmain()
-}
-
-func linkarchinit() {
-	if obj.Getgoarch() == "ppc64le" {
+func Init() {
+	if obj.GOARCH == "ppc64le" {
 		ld.SysArch = sys.ArchPPC64LE
 	} else {
 		ld.SysArch = sys.ArchPPC64
 	}
 
-	ld.Thearch.Funcalign = FuncAlign
-	ld.Thearch.Maxalign = MaxAlign
-	ld.Thearch.Minalign = MinAlign
-	ld.Thearch.Dwarfregsp = DWARFREGSP
-	ld.Thearch.Dwarfreglr = DWARFREGLR
+	ld.Thearch.Funcalign = funcAlign
+	ld.Thearch.Maxalign = maxAlign
+	ld.Thearch.Minalign = minAlign
+	ld.Thearch.Dwarfregsp = dwarfRegSP
+	ld.Thearch.Dwarfreglr = dwarfRegLR
 
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Archinit = archinit
@@ -66,6 +58,7 @@ func linkarchinit() {
 	ld.Thearch.Elfreloc1 = elfreloc1
 	ld.Thearch.Elfsetupplt = elfsetupplt
 	ld.Thearch.Gentext = gentext
+	ld.Thearch.Trampoline = trampoline
 	ld.Thearch.Machoreloc1 = machoreloc1
 	if ld.SysArch == sys.ArchPPC64LE {
 		ld.Thearch.Lput = ld.Lputl
@@ -93,89 +86,56 @@ func linkarchinit() {
 	ld.Thearch.Solarisdynld = "XXX"
 }
 
-func archinit() {
-	// getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
-	// Go was built; see ../../make.bash.
-	if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" {
-		ld.Linkmode = ld.LinkInternal
-	}
-
-	switch ld.Buildmode {
-	case ld.BuildmodePIE, ld.BuildmodeShared:
-		ld.Linkmode = ld.LinkExternal
-	}
-
-	if ld.Linkshared {
-		ld.Linkmode = ld.LinkExternal
-	}
-
-	if ld.Linkmode == ld.LinkExternal {
-		toc := ld.Linklookup(ld.Ctxt, ".TOC.", 0)
-		toc.Type = obj.SDYNIMPORT
-	}
-
-	switch ld.HEADTYPE {
-	default:
-		if ld.Linkmode == ld.LinkAuto {
-			ld.Linkmode = ld.LinkInternal
-		}
-		if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" {
-			log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE)))
-		}
-
-	case obj.Hlinux:
-		break
-	}
-
-	switch ld.HEADTYPE {
+func archinit(ctxt *ld.Link) {
+	switch ld.Headtype {
 	default:
-		ld.Exitf("unknown -H option: %v", ld.HEADTYPE)
+		ld.Exitf("unknown -H option: %v", ld.Headtype)
 
 	case obj.Hplan9: /* plan 9 */
 		ld.HEADR = 32
 
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 4128
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 4128
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
 
 	case obj.Hlinux: /* ppc64 elf */
 		if ld.SysArch == sys.ArchPPC64 {
-			ld.Debug['d'] = 1 // TODO(austin): ELF ABI v1 not supported yet
+			*ld.FlagD = true // TODO(austin): ELF ABI v1 not supported yet
 		}
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 		ld.HEADR = ld.ELFRESERVE
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x10000 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x10000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
 		}
 
 	case obj.Hnacl:
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 		ld.HEADR = 0x10000
 		ld.Funcalign = 16
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x20000
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x20000
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x10000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
 		}
 	}
 
-	if ld.INITDAT != 0 && ld.INITRND != 0 {
-		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND))
+	if *ld.FlagDataAddr != 0 && *ld.FlagRound != 0 {
+		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(*ld.FlagDataAddr), uint32(*ld.FlagRound))
 	}
 }
diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go
index 9864749..4a5f48c 100644
--- a/src/cmd/link/internal/s390x/asm.go
+++ b/src/cmd/link/internal/s390x/asm.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -47,102 +47,101 @@ import (
 //	undef
 //
 // The job of appending the moduledata is delegated to runtime.addmoduledata.
-func gentext() {
-	if !ld.DynlinkingGo() {
+func gentext(ctxt *ld.Link) {
+	if !ctxt.DynlinkingGo() {
 		return
 	}
-	addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
+	addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0)
 	if addmoduledata.Type == obj.STEXT {
 		// we're linking a module containing the runtime -> no need for
 		// an init function
 		return
 	}
 	addmoduledata.Attr |= ld.AttrReachable
-	initfunc := ld.Linklookup(ld.Ctxt, "go.link.addmoduledata", 0)
+	initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
 	initfunc.Type = obj.STEXT
 	initfunc.Attr |= ld.AttrLocal
 	initfunc.Attr |= ld.AttrReachable
 
 	// larl %r2, <local.moduledata>
-	ld.Adduint8(ld.Ctxt, initfunc, 0xc0)
-	ld.Adduint8(ld.Ctxt, initfunc, 0x20)
+	ld.Adduint8(ctxt, initfunc, 0xc0)
+	ld.Adduint8(ctxt, initfunc, 0x20)
 	lmd := ld.Addrel(initfunc)
 	lmd.Off = int32(initfunc.Size)
 	lmd.Siz = 4
-	lmd.Sym = ld.Ctxt.Moduledata
+	lmd.Sym = ctxt.Moduledata
 	lmd.Type = obj.R_PCREL
 	lmd.Variant = ld.RV_390_DBL
 	lmd.Add = 2 + int64(lmd.Siz)
-	ld.Adduint32(ld.Ctxt, initfunc, 0)
+	ld.Adduint32(ctxt, initfunc, 0)
 
 	// jg <runtime.addmoduledata[@plt]>
-	ld.Adduint8(ld.Ctxt, initfunc, 0xc0)
-	ld.Adduint8(ld.Ctxt, initfunc, 0xf4)
+	ld.Adduint8(ctxt, initfunc, 0xc0)
+	ld.Adduint8(ctxt, initfunc, 0xf4)
 	rel := ld.Addrel(initfunc)
 	rel.Off = int32(initfunc.Size)
 	rel.Siz = 4
-	rel.Sym = ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
+	rel.Sym = ctxt.Syms.Lookup("runtime.addmoduledata", 0)
 	rel.Type = obj.R_CALL
 	rel.Variant = ld.RV_390_DBL
 	rel.Add = 2 + int64(rel.Siz)
-	ld.Adduint32(ld.Ctxt, initfunc, 0)
+	ld.Adduint32(ctxt, initfunc, 0)
 
 	// undef (for debugging)
-	ld.Adduint32(ld.Ctxt, initfunc, 0)
+	ld.Adduint32(ctxt, initfunc, 0)
 
-	ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
-	initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
+	ctxt.Textp = append(ctxt.Textp, initfunc)
+	initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
 	initarray_entry.Attr |= ld.AttrLocal
 	initarray_entry.Attr |= ld.AttrReachable
 	initarray_entry.Type = obj.SINITARR
-	ld.Addaddr(ld.Ctxt, initarray_entry, initfunc)
+	ld.Addaddr(ctxt, initarray_entry, initfunc)
 }
 
-func adddynrel(s *ld.LSym, r *ld.Reloc) {
+func adddynrel(ctxt *ld.Link, s *ld.Symbol, r *ld.Reloc) bool {
 	targ := r.Sym
-	ld.Ctxt.Cursym = s
 
 	switch r.Type {
 	default:
 		if r.Type >= 256 {
-			ld.Diag("unexpected relocation type %d", r.Type)
-			return
+			ld.Errorf(s, "unexpected relocation type %d", r.Type)
+			return false
 		}
 
 		// Handle relocations found in ELF object files.
 	case 256 + ld.R_390_12,
 		256 + ld.R_390_GOT12:
-		ld.Diag("s390x 12-bit relocations have not been implemented (relocation type %d)", r.Type-256)
-		return
+		ld.Errorf(s, "s390x 12-bit relocations have not been implemented (relocation type %d)", r.Type-256)
+		return false
 
 	case 256 + ld.R_390_8,
 		256 + ld.R_390_16,
 		256 + ld.R_390_32,
 		256 + ld.R_390_64:
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected R_390_nn relocation for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected R_390_nn relocation for dynamic symbol %s", targ.Name)
 		}
 		r.Type = obj.R_ADDR
-		return
+		return true
 
 	case 256 + ld.R_390_PC16,
 		256 + ld.R_390_PC32,
 		256 + ld.R_390_PC64:
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected R_390_PCnn relocation for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected R_390_PCnn relocation for dynamic symbol %s", targ.Name)
 		}
 		if targ.Type == 0 || targ.Type == obj.SXREF {
-			ld.Diag("unknown symbol %s in pcrel", targ.Name)
+			ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name)
 		}
 		r.Type = obj.R_PCREL
 		r.Add += int64(r.Siz)
-		return
+		return true
 
 	case 256 + ld.R_390_GOT16,
 		256 + ld.R_390_GOT32,
 		256 + ld.R_390_GOT64:
-		ld.Diag("unimplemented S390x relocation: %v", r.Type-256)
-		return
+		ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-256)
+		return true
 
 	case 256 + ld.R_390_PLT16DBL,
 		256 + ld.R_390_PLT32DBL:
@@ -150,47 +149,51 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 		r.Variant = ld.RV_390_DBL
 		r.Add += int64(r.Siz)
 		if targ.Type == obj.SDYNIMPORT {
-			addpltsym(ld.Ctxt, targ)
-			r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			addpltsym(ctxt, targ)
+			r.Sym = ctxt.Syms.Lookup(".plt", 0)
 			r.Add += int64(targ.Plt)
 		}
-		return
+		return true
 
 	case 256 + ld.R_390_PLT32,
 		256 + ld.R_390_PLT64:
 		r.Type = obj.R_PCREL
 		r.Add += int64(r.Siz)
 		if targ.Type == obj.SDYNIMPORT {
-			addpltsym(ld.Ctxt, targ)
-			r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			addpltsym(ctxt, targ)
+			r.Sym = ctxt.Syms.Lookup(".plt", 0)
 			r.Add += int64(targ.Plt)
 		}
-		return
+		return true
 
 	case 256 + ld.R_390_COPY:
-		ld.Diag("unimplemented S390x relocation: %v", r.Type-256)
+		ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-256)
+		return false
 
 	case 256 + ld.R_390_GLOB_DAT:
-		ld.Diag("unimplemented S390x relocation: %v", r.Type-256)
+		ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-256)
+		return false
 
 	case 256 + ld.R_390_JMP_SLOT:
-		ld.Diag("unimplemented S390x relocation: %v", r.Type-256)
+		ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-256)
+		return false
 
 	case 256 + ld.R_390_RELATIVE:
-		ld.Diag("unimplemented S390x relocation: %v", r.Type-256)
+		ld.Errorf(s, "unimplemented S390x relocation: %v", r.Type-256)
+		return false
 
 	case 256 + ld.R_390_GOTOFF:
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected R_390_GOTOFF relocation for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected R_390_GOTOFF relocation for dynamic symbol %s", targ.Name)
 		}
 		r.Type = obj.R_GOTOFF
-		return
+		return true
 
 	case 256 + ld.R_390_GOTPC:
 		r.Type = obj.R_PCREL
-		r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0)
+		r.Sym = ctxt.Syms.Lookup(".got", 0)
 		r.Add += int64(r.Siz)
-		return
+		return true
 
 	case 256 + ld.R_390_PC16DBL,
 		256 + ld.R_390_PC32DBL:
@@ -198,36 +201,36 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 		r.Variant = ld.RV_390_DBL
 		r.Add += int64(r.Siz)
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected R_390_PCnnDBL relocation for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected R_390_PCnnDBL relocation for dynamic symbol %s", targ.Name)
 		}
-		return
+		return true
 
 	case 256 + ld.R_390_GOTPCDBL:
 		r.Type = obj.R_PCREL
 		r.Variant = ld.RV_390_DBL
-		r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0)
+		r.Sym = ctxt.Syms.Lookup(".got", 0)
 		r.Add += int64(r.Siz)
-		return
+		return true
 
 	case 256 + ld.R_390_GOTENT:
-		addgotsym(targ)
+		addgotsym(ctxt, targ)
 
 		r.Type = obj.R_PCREL
 		r.Variant = ld.RV_390_DBL
-		r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0)
+		r.Sym = ctxt.Syms.Lookup(".got", 0)
 		r.Add += int64(targ.Got)
 		r.Add += int64(r.Siz)
-		return
+		return true
 	}
 	// Handle references to ELF symbols from our own object files.
 	if targ.Type != obj.SDYNIMPORT {
-		return
+		return true
 	}
 
-	ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type)
+	return false
 }
 
-func elfreloc1(r *ld.Reloc, sectoff int64) int {
+func elfreloc1(ctxt *ld.Link, r *ld.Reloc, sectoff int64) int {
 	ld.Thearch.Vput(uint64(sectoff))
 
 	elfsym := r.Xsym.ElfsymForReloc()
@@ -326,61 +329,61 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func elfsetupplt() {
-	plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
-	got := ld.Linklookup(ld.Ctxt, ".got", 0)
+func elfsetupplt(ctxt *ld.Link) {
+	plt := ctxt.Syms.Lookup(".plt", 0)
+	got := ctxt.Syms.Lookup(".got", 0)
 	if plt.Size == 0 {
 		// stg     %r1,56(%r15)
-		ld.Adduint8(ld.Ctxt, plt, 0xe3)
-		ld.Adduint8(ld.Ctxt, plt, 0x10)
-		ld.Adduint8(ld.Ctxt, plt, 0xf0)
-		ld.Adduint8(ld.Ctxt, plt, 0x38)
-		ld.Adduint8(ld.Ctxt, plt, 0x00)
-		ld.Adduint8(ld.Ctxt, plt, 0x24)
+		ld.Adduint8(ctxt, plt, 0xe3)
+		ld.Adduint8(ctxt, plt, 0x10)
+		ld.Adduint8(ctxt, plt, 0xf0)
+		ld.Adduint8(ctxt, plt, 0x38)
+		ld.Adduint8(ctxt, plt, 0x00)
+		ld.Adduint8(ctxt, plt, 0x24)
 		// larl    %r1,_GLOBAL_OFFSET_TABLE_
-		ld.Adduint8(ld.Ctxt, plt, 0xc0)
-		ld.Adduint8(ld.Ctxt, plt, 0x10)
-		ld.Addpcrelplus(ld.Ctxt, plt, got, 6)
+		ld.Adduint8(ctxt, plt, 0xc0)
+		ld.Adduint8(ctxt, plt, 0x10)
+		ld.Addpcrelplus(ctxt, plt, got, 6)
 		// mvc     48(8,%r15),8(%r1)
-		ld.Adduint8(ld.Ctxt, plt, 0xd2)
-		ld.Adduint8(ld.Ctxt, plt, 0x07)
-		ld.Adduint8(ld.Ctxt, plt, 0xf0)
-		ld.Adduint8(ld.Ctxt, plt, 0x30)
-		ld.Adduint8(ld.Ctxt, plt, 0x10)
-		ld.Adduint8(ld.Ctxt, plt, 0x08)
+		ld.Adduint8(ctxt, plt, 0xd2)
+		ld.Adduint8(ctxt, plt, 0x07)
+		ld.Adduint8(ctxt, plt, 0xf0)
+		ld.Adduint8(ctxt, plt, 0x30)
+		ld.Adduint8(ctxt, plt, 0x10)
+		ld.Adduint8(ctxt, plt, 0x08)
 		// lg      %r1,16(%r1)
-		ld.Adduint8(ld.Ctxt, plt, 0xe3)
-		ld.Adduint8(ld.Ctxt, plt, 0x10)
-		ld.Adduint8(ld.Ctxt, plt, 0x10)
-		ld.Adduint8(ld.Ctxt, plt, 0x10)
-		ld.Adduint8(ld.Ctxt, plt, 0x00)
-		ld.Adduint8(ld.Ctxt, plt, 0x04)
+		ld.Adduint8(ctxt, plt, 0xe3)
+		ld.Adduint8(ctxt, plt, 0x10)
+		ld.Adduint8(ctxt, plt, 0x10)
+		ld.Adduint8(ctxt, plt, 0x10)
+		ld.Adduint8(ctxt, plt, 0x00)
+		ld.Adduint8(ctxt, plt, 0x04)
 		// br      %r1
-		ld.Adduint8(ld.Ctxt, plt, 0x07)
-		ld.Adduint8(ld.Ctxt, plt, 0xf1)
+		ld.Adduint8(ctxt, plt, 0x07)
+		ld.Adduint8(ctxt, plt, 0xf1)
 		// nopr    %r0
-		ld.Adduint8(ld.Ctxt, plt, 0x07)
-		ld.Adduint8(ld.Ctxt, plt, 0x00)
+		ld.Adduint8(ctxt, plt, 0x07)
+		ld.Adduint8(ctxt, plt, 0x00)
 		// nopr    %r0
-		ld.Adduint8(ld.Ctxt, plt, 0x07)
-		ld.Adduint8(ld.Ctxt, plt, 0x00)
+		ld.Adduint8(ctxt, plt, 0x07)
+		ld.Adduint8(ctxt, plt, 0x00)
 		// nopr    %r0
-		ld.Adduint8(ld.Ctxt, plt, 0x07)
-		ld.Adduint8(ld.Ctxt, plt, 0x00)
+		ld.Adduint8(ctxt, plt, 0x07)
+		ld.Adduint8(ctxt, plt, 0x00)
 
 		// assume got->size == 0 too
-		ld.Addaddrplus(ld.Ctxt, got, ld.Linklookup(ld.Ctxt, ".dynamic", 0), 0)
+		ld.Addaddrplus(ctxt, got, ctxt.Syms.Lookup(".dynamic", 0), 0)
 
-		ld.Adduint64(ld.Ctxt, got, 0)
-		ld.Adduint64(ld.Ctxt, got, 0)
+		ld.Adduint64(ctxt, got, 0)
+		ld.Adduint64(ctxt, got, 0)
 	}
 }
 
-func machoreloc1(r *ld.Reloc, sectoff int64) int {
+func machoreloc1(s *ld.Symbol, r *ld.Reloc, sectoff int64) int {
 	return -1
 }
 
-func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
+func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
 	if ld.Linkmode == ld.LinkExternal {
 		return -1
 	}
@@ -391,17 +394,17 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 		return 0
 
 	case obj.R_GOTOFF:
-		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0))
+		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0))
 		return 0
 	}
 
 	return -1
 }
 
-func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
+func archrelocvariant(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, t int64) int64 {
 	switch r.Variant & ld.RV_TYPE_MASK {
 	default:
-		ld.Diag("unexpected relocation variant %d", r.Variant)
+		ld.Errorf(s, "unexpected relocation variant %d", r.Variant)
 		return t
 
 	case ld.RV_NONE:
@@ -409,13 +412,13 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
 
 	case ld.RV_390_DBL:
 		if (t & 1) != 0 {
-			ld.Diag("%s+%v is not 2-byte aligned", r.Sym.Name, r.Sym.Value)
+			ld.Errorf(s, "%s+%v is not 2-byte aligned", r.Sym.Name, r.Sym.Value)
 		}
 		return t >> 1
 	}
 }
 
-func addpltsym(ctxt *ld.Link, s *ld.LSym) {
+func addpltsym(ctxt *ld.Link, s *ld.Symbol) {
 	if s.Plt >= 0 {
 		return
 	}
@@ -423,11 +426,11 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 	ld.Adddynsym(ctxt, s)
 
 	if ld.Iself {
-		plt := ld.Linklookup(ctxt, ".plt", 0)
-		got := ld.Linklookup(ctxt, ".got", 0)
-		rela := ld.Linklookup(ctxt, ".rela.plt", 0)
+		plt := ctxt.Syms.Lookup(".plt", 0)
+		got := ctxt.Syms.Lookup(".got", 0)
+		rela := ctxt.Syms.Lookup(".rela.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt()
+			elfsetupplt(ctxt)
 		}
 		// larl    %r1,_GLOBAL_OFFSET_TABLE_+index
 
@@ -474,35 +477,34 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 		s.Plt = int32(plt.Size - 32)
 
 	} else {
-		ld.Diag("addpltsym: unsupported binary format")
+		ld.Errorf(s, "addpltsym: unsupported binary format")
 	}
 }
 
-func addgotsym(s *ld.LSym) {
+func addgotsym(ctxt *ld.Link, s *ld.Symbol) {
 	if s.Got >= 0 {
 		return
 	}
 
-	ld.Adddynsym(ld.Ctxt, s)
-	got := ld.Linklookup(ld.Ctxt, ".got", 0)
+	ld.Adddynsym(ctxt, s)
+	got := ctxt.Syms.Lookup(".got", 0)
 	s.Got = int32(got.Size)
-	ld.Adduint64(ld.Ctxt, got, 0)
+	ld.Adduint64(ctxt, got, 0)
 
 	if ld.Iself {
-		rela := ld.Linklookup(ld.Ctxt, ".rela", 0)
-		ld.Addaddrplus(ld.Ctxt, rela, got, int64(s.Got))
-		ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_390_GLOB_DAT))
-		ld.Adduint64(ld.Ctxt, rela, 0)
+		rela := ctxt.Syms.Lookup(".rela", 0)
+		ld.Addaddrplus(ctxt, rela, got, int64(s.Got))
+		ld.Adduint64(ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_390_GLOB_DAT))
+		ld.Adduint64(ctxt, rela, 0)
 	} else {
-		ld.Diag("addgotsym: unsupported binary format")
+		ld.Errorf(s, "addgotsym: unsupported binary format")
 	}
 }
 
-func asmb() {
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
+func asmb(ctxt *ld.Link) {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f asmb\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	if ld.Iself {
 		ld.Asmbelfsetup()
@@ -510,81 +512,82 @@ func asmb() {
 
 	sect := ld.Segtext.Sect
 	ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-	ld.Codeblk(int64(sect.Vaddr), int64(sect.Length))
+	ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
 	for sect = sect.Next; sect != nil; sect = sect.Next {
 		ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-		ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
+		ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
 	}
 
 	if ld.Segrodata.Filelen > 0 {
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f rodatblk\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-
 		ld.Cseek(int64(ld.Segrodata.Fileoff))
-		ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+		ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+	}
+	if ld.Segrelrodata.Filelen > 0 {
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f rodatblk\n", obj.Cputime())
+		}
+		ld.Cseek(int64(ld.Segrelrodata.Fileoff))
+		ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
 	}
 
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f datblk\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	ld.Cseek(int64(ld.Segdata.Fileoff))
-	ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+	ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
 
 	ld.Cseek(int64(ld.Segdwarf.Fileoff))
-	ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+	ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
 
 	/* output symbol table */
 	ld.Symsize = 0
 
 	ld.Lcsize = 0
 	symo := uint32(0)
-	if ld.Debug['s'] == 0 {
+	if !*ld.FlagS {
 		if !ld.Iself {
-			ld.Diag("unsupported executable format")
+			ld.Errorf(nil, "unsupported executable format")
 		}
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f sym\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
 		symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
-		symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
+		symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
 
 		ld.Cseek(int64(symo))
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f elfsym\n", obj.Cputime())
 		}
-		ld.Asmelfsym()
+		ld.Asmelfsym(ctxt)
 		ld.Cflush()
 		ld.Cwrite(ld.Elfstrdat)
 
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f dwarf\n", obj.Cputime())
 		}
 
 		if ld.Linkmode == ld.LinkExternal {
-			ld.Elfemitreloc()
+			ld.Elfemitreloc(ctxt)
 		}
 	}
 
-	ld.Ctxt.Cursym = nil
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f header\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f header\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 	ld.Cseek(0)
-	switch ld.HEADTYPE {
+	switch ld.Headtype {
 	default:
-		ld.Diag("unsupported operating system")
+		ld.Errorf(nil, "unsupported operating system")
 	case obj.Hlinux:
-		ld.Asmbelf(int64(symo))
+		ld.Asmbelf(ctxt, int64(symo))
 	}
 
 	ld.Cflush()
-	if ld.Debug['c'] != 0 {
+	if *ld.FlagC {
 		fmt.Printf("textsize=%d\n", ld.Segtext.Filelen)
 		fmt.Printf("datsize=%d\n", ld.Segdata.Filelen)
 		fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen)
diff --git a/src/cmd/link/internal/s390x/l.go b/src/cmd/link/internal/s390x/l.go
index 7c92bdb..87d10ee 100644
--- a/src/cmd/link/internal/s390x/l.go
+++ b/src/cmd/link/internal/s390x/l.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -62,13 +62,13 @@ package s390x
 // THE SOFTWARE.
 
 const (
-	MaxAlign  = 32 // max data alignment
-	MinAlign  = 2  // min data alignment
-	FuncAlign = 16
+	maxAlign  = 32 // max data alignment
+	minAlign  = 2  // min data alignment
+	funcAlign = 16
 )
 
 /* Used by ../internal/ld/dwarf.go */
 const (
-	DWARFREGSP = 15
-	DWARFREGLR = 14
+	dwarfRegSP = 15
+	dwarfRegLR = 14
 )
diff --git a/src/cmd/link/internal/s390x/obj.go b/src/cmd/link/internal/s390x/obj.go
index b77f57d..eea8978 100644
--- a/src/cmd/link/internal/s390x/obj.go
+++ b/src/cmd/link/internal/s390x/obj.go
@@ -1,5 +1,5 @@
 // Inferno utils/5l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5l/obj.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -37,21 +37,14 @@ import (
 	"fmt"
 )
 
-// Reading object files.
-
-func Main() {
-	linkarchinit()
-	ld.Ldmain()
-}
-
-func linkarchinit() {
+func Init() {
 	ld.SysArch = sys.ArchS390X
 
-	ld.Thearch.Funcalign = FuncAlign
-	ld.Thearch.Maxalign = MaxAlign
-	ld.Thearch.Minalign = MinAlign
-	ld.Thearch.Dwarfregsp = DWARFREGSP
-	ld.Thearch.Dwarfreglr = DWARFREGLR
+	ld.Thearch.Funcalign = funcAlign
+	ld.Thearch.Maxalign = maxAlign
+	ld.Thearch.Minalign = minAlign
+	ld.Thearch.Dwarfregsp = dwarfRegSP
+	ld.Thearch.Dwarfreglr = dwarfRegLR
 
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Archinit = archinit
@@ -79,36 +72,26 @@ func linkarchinit() {
 	ld.Thearch.Solarisdynld = "XXX"
 }
 
-func archinit() {
-	// getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
-	// Go was built; see ../../make.bash.
-	if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" {
-		ld.Linkmode = ld.LinkInternal
-	}
-
-	if ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ld.DynlinkingGo() {
-		ld.Linkmode = ld.LinkExternal
-	}
-
-	switch ld.HEADTYPE {
+func archinit(ctxt *ld.Link) {
+	switch ld.Headtype {
 	default:
-		ld.Exitf("unknown -H option: %v", ld.HEADTYPE)
+		ld.Exitf("unknown -H option: %v", ld.Headtype)
 
 	case obj.Hlinux: // s390x ELF
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 		ld.HEADR = ld.ELFRESERVE
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x10000 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x10000 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x10000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
 		}
 	}
 
-	if ld.INITDAT != 0 && ld.INITRND != 0 {
-		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND))
+	if *ld.FlagDataAddr != 0 && *ld.FlagRound != 0 {
+		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(*ld.FlagDataAddr), uint32(*ld.FlagRound))
 	}
 }
diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go
index cc8f96f..af702c2 100644
--- a/src/cmd/link/internal/x86/asm.go
+++ b/src/cmd/link/internal/x86/asm.go
@@ -1,5 +1,5 @@
 // Inferno utils/8l/asm.c
-// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/8l/asm.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -33,16 +33,15 @@ package x86
 import (
 	"cmd/internal/obj"
 	"cmd/link/internal/ld"
-	"fmt"
 	"log"
 )
 
 // Append 4 bytes to s and create a R_CALL relocation targeting t to fill them in.
-func addcall(ctxt *ld.Link, s *ld.LSym, t *ld.LSym) {
+func addcall(ctxt *ld.Link, s *ld.Symbol, t *ld.Symbol) {
 	s.Attr |= ld.AttrReachable
 	i := s.Size
 	s.Size += 4
-	ld.Symgrow(ctxt, s, s.Size)
+	ld.Symgrow(s, s.Size)
 	r := ld.Addrel(s)
 	r.Sym = t
 	r.Off = int32(i)
@@ -50,29 +49,58 @@ func addcall(ctxt *ld.Link, s *ld.LSym, t *ld.LSym) {
 	r.Siz = 4
 }
 
-func gentext() {
-	if !ld.DynlinkingGo() && ld.Buildmode != ld.BuildmodePIE && ld.Buildmode != ld.BuildmodeCShared {
-		return
+func gentext(ctxt *ld.Link) {
+	if ctxt.DynlinkingGo() {
+		// We need get_pc_thunk.
+	} else {
+		switch ld.Buildmode {
+		case ld.BuildmodeCArchive:
+			if !ld.Iself {
+				return
+			}
+		case ld.BuildmodePIE, ld.BuildmodeCShared, ld.BuildmodePlugin:
+			// We need get_pc_thunk.
+		default:
+			return
+		}
 	}
 
-	thunkfunc := ld.Linklookup(ld.Ctxt, "__x86.get_pc_thunk.cx", 0)
-	thunkfunc.Type = obj.STEXT
-	thunkfunc.Attr |= ld.AttrLocal
-	thunkfunc.Attr |= ld.AttrReachable
-	o := func(op ...uint8) {
-		for _, op1 := range op {
-			ld.Adduint8(ld.Ctxt, thunkfunc, op1)
+	// Generate little thunks that load the PC of the next instruction into a register.
+	thunks := make([]*ld.Symbol, 0, 7+len(ctxt.Textp))
+	for _, r := range [...]struct {
+		name string
+		num  uint8
+	}{
+		{"ax", 0},
+		{"cx", 1},
+		{"dx", 2},
+		{"bx", 3},
+		// sp
+		{"bp", 5},
+		{"si", 6},
+		{"di", 7},
+	} {
+		thunkfunc := ctxt.Syms.Lookup("__x86.get_pc_thunk."+r.name, 0)
+		thunkfunc.Type = obj.STEXT
+		thunkfunc.Attr |= ld.AttrLocal
+		thunkfunc.Attr |= ld.AttrReachable //TODO: remove?
+		o := func(op ...uint8) {
+			for _, op1 := range op {
+				ld.Adduint8(ctxt, thunkfunc, op1)
+			}
 		}
-	}
-	// 8b 0c 24	mov    (%esp),%ecx
-	o(0x8b, 0x0c, 0x24)
-	// c3		ret
-	o(0xc3)
+		// 8b 04 24	mov    (%esp),%eax
+		// Destination register is in bits 3-5 of the middle byte, so add that in.
+		o(0x8b, 0x04+r.num<<3, 0x24)
+		// c3		ret
+		o(0xc3)
 
-	ld.Ctxt.Textp = append(ld.Ctxt.Textp, thunkfunc)
+		thunks = append(thunks, thunkfunc)
+	}
+	ctxt.Textp = append(thunks, ctxt.Textp...) // keep Textp in dependency order
 
-	addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
-	if addmoduledata.Type == obj.STEXT {
+	addmoduledata := ctxt.Syms.Lookup("runtime.addmoduledata", 0)
+	if addmoduledata.Type == obj.STEXT && ld.Buildmode != ld.BuildmodePlugin {
 		// we're linking a module containing the runtime -> no need for
 		// an init function
 		return
@@ -80,20 +108,20 @@ func gentext() {
 
 	addmoduledata.Attr |= ld.AttrReachable
 
-	initfunc := ld.Linklookup(ld.Ctxt, "go.link.addmoduledata", 0)
+	initfunc := ctxt.Syms.Lookup("go.link.addmoduledata", 0)
 	initfunc.Type = obj.STEXT
 	initfunc.Attr |= ld.AttrLocal
 	initfunc.Attr |= ld.AttrReachable
-	o = func(op ...uint8) {
+	o := func(op ...uint8) {
 		for _, op1 := range op {
-			ld.Adduint8(ld.Ctxt, initfunc, op1)
+			ld.Adduint8(ctxt, initfunc, op1)
 		}
 	}
 
 	// go.link.addmoduledata:
 	//      53                      push %ebx
 	//      e8 00 00 00 00          call __x86.get_pc_thunk.cx + R_CALL __x86.get_pc_thunk.cx
-	//      8d 81 00 00 00 00       lea 0x0(%ecx), %eax + R_PCREL ld.Ctxt.Moduledata
+	//      8d 81 00 00 00 00       lea 0x0(%ecx), %eax + R_PCREL ctxt.Moduledata
 	//      8d 99 00 00 00 00       lea 0x0(%ecx), %ebx + R_GOTPC _GLOBAL_OFFSET_TABLE_
 	//      e8 00 00 00 00          call runtime.addmoduledata at plt + R_CALL runtime.addmoduledata
 	//      5b                      pop %ebx
@@ -102,70 +130,72 @@ func gentext() {
 	o(0x53)
 
 	o(0xe8)
-	addcall(ld.Ctxt, initfunc, ld.Linklookup(ld.Ctxt, "__x86.get_pc_thunk.cx", 0))
+	addcall(ctxt, initfunc, ctxt.Syms.Lookup("__x86.get_pc_thunk.cx", 0))
 
 	o(0x8d, 0x81)
-	ld.Addpcrelplus(ld.Ctxt, initfunc, ld.Ctxt.Moduledata, 6)
+	ld.Addpcrelplus(ctxt, initfunc, ctxt.Moduledata, 6)
 
 	o(0x8d, 0x99)
 	i := initfunc.Size
 	initfunc.Size += 4
-	ld.Symgrow(ld.Ctxt, initfunc, initfunc.Size)
+	ld.Symgrow(initfunc, initfunc.Size)
 	r := ld.Addrel(initfunc)
-	r.Sym = ld.Linklookup(ld.Ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
+	r.Sym = ctxt.Syms.Lookup("_GLOBAL_OFFSET_TABLE_", 0)
 	r.Off = int32(i)
 	r.Type = obj.R_PCREL
 	r.Add = 12
 	r.Siz = 4
 
 	o(0xe8)
-	addcall(ld.Ctxt, initfunc, addmoduledata)
+	addcall(ctxt, initfunc, addmoduledata)
 
 	o(0x5b)
 
 	o(0xc3)
 
-	ld.Ctxt.Textp = append(ld.Ctxt.Textp, initfunc)
-	initarray_entry := ld.Linklookup(ld.Ctxt, "go.link.addmoduledatainit", 0)
+	if ld.Buildmode == ld.BuildmodePlugin {
+		ctxt.Textp = append(ctxt.Textp, addmoduledata)
+	}
+	ctxt.Textp = append(ctxt.Textp, initfunc)
+	initarray_entry := ctxt.Syms.Lookup("go.link.addmoduledatainit", 0)
 	initarray_entry.Attr |= ld.AttrReachable
 	initarray_entry.Attr |= ld.AttrLocal
 	initarray_entry.Type = obj.SINITARR
-	ld.Addaddr(ld.Ctxt, initarray_entry, initfunc)
+	ld.Addaddr(ctxt, initarray_entry, initfunc)
 }
 
-func adddynrel(s *ld.LSym, r *ld.Reloc) {
+func adddynrel(ctxt *ld.Link, s *ld.Symbol, r *ld.Reloc) bool {
 	targ := r.Sym
-	ld.Ctxt.Cursym = s
 
 	switch r.Type {
 	default:
 		if r.Type >= 256 {
-			ld.Diag("unexpected relocation type %d", r.Type)
-			return
+			ld.Errorf(s, "unexpected relocation type %d", r.Type)
+			return false
 		}
 
 		// Handle relocations found in ELF object files.
 	case 256 + ld.R_386_PC32:
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected R_386_PC32 relocation for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected R_386_PC32 relocation for dynamic symbol %s", targ.Name)
 		}
 		if targ.Type == 0 || targ.Type == obj.SXREF {
-			ld.Diag("unknown symbol %s in pcrel", targ.Name)
+			ld.Errorf(s, "unknown symbol %s in pcrel", targ.Name)
 		}
 		r.Type = obj.R_PCREL
 		r.Add += 4
-		return
+		return true
 
 	case 256 + ld.R_386_PLT32:
 		r.Type = obj.R_PCREL
 		r.Add += 4
 		if targ.Type == obj.SDYNIMPORT {
-			addpltsym(ld.Ctxt, targ)
-			r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			addpltsym(ctxt, targ)
+			r.Sym = ctxt.Syms.Lookup(".plt", 0)
 			r.Add += int64(targ.Plt)
 		}
 
-		return
+		return true
 
 	case 256 + ld.R_386_GOT32, 256 + ld.R_386_GOT32X:
 		if targ.Type != obj.SDYNIMPORT {
@@ -175,7 +205,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 				s.P[r.Off-2] = 0x8d
 
 				r.Type = obj.R_GOTOFF
-				return
+				return true
 			}
 
 			if r.Off >= 2 && s.P[r.Off-2] == 0xff && s.P[r.Off-1] == 0xb3 {
@@ -185,104 +215,103 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 
 				s.P[r.Off-1] = 0x68
 				r.Type = obj.R_ADDR
-				return
+				return true
 			}
 
-			ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name)
-			return
+			ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name)
+			return false
 		}
 
-		addgotsym(ld.Ctxt, targ)
+		addgotsym(ctxt, targ)
 		r.Type = obj.R_CONST // write r->add during relocsym
 		r.Sym = nil
 		r.Add += int64(targ.Got)
-		return
+		return true
 
 	case 256 + ld.R_386_GOTOFF:
 		r.Type = obj.R_GOTOFF
-		return
+		return true
 
 	case 256 + ld.R_386_GOTPC:
 		r.Type = obj.R_PCREL
-		r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0)
+		r.Sym = ctxt.Syms.Lookup(".got", 0)
 		r.Add += 4
-		return
+		return true
 
 	case 256 + ld.R_386_32:
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected R_386_32 relocation for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected R_386_32 relocation for dynamic symbol %s", targ.Name)
 		}
 		r.Type = obj.R_ADDR
-		return
+		return true
 
 	case 512 + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0:
 		r.Type = obj.R_ADDR
 		if targ.Type == obj.SDYNIMPORT {
-			ld.Diag("unexpected reloc for dynamic symbol %s", targ.Name)
+			ld.Errorf(s, "unexpected reloc for dynamic symbol %s", targ.Name)
 		}
-		return
+		return true
 
 	case 512 + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1:
 		if targ.Type == obj.SDYNIMPORT {
-			addpltsym(ld.Ctxt, targ)
-			r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+			addpltsym(ctxt, targ)
+			r.Sym = ctxt.Syms.Lookup(".plt", 0)
 			r.Add = int64(targ.Plt)
 			r.Type = obj.R_PCREL
-			return
+			return true
 		}
 
 		r.Type = obj.R_PCREL
-		return
+		return true
 
 	case 512 + ld.MACHO_FAKE_GOTPCREL:
 		if targ.Type != obj.SDYNIMPORT {
 			// have symbol
 			// turn MOVL of GOT entry into LEAL of symbol itself
 			if r.Off < 2 || s.P[r.Off-2] != 0x8b {
-				ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name)
-				return
+				ld.Errorf(s, "unexpected GOT reloc for non-dynamic symbol %s", targ.Name)
+				return false
 			}
 
 			s.P[r.Off-2] = 0x8d
 			r.Type = obj.R_PCREL
-			return
+			return true
 		}
 
-		addgotsym(ld.Ctxt, targ)
-		r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0)
+		addgotsym(ctxt, targ)
+		r.Sym = ctxt.Syms.Lookup(".got", 0)
 		r.Add += int64(targ.Got)
 		r.Type = obj.R_PCREL
-		return
+		return true
 	}
 
 	// Handle references to ELF symbols from our own object files.
 	if targ.Type != obj.SDYNIMPORT {
-		return
+		return true
 	}
-
 	switch r.Type {
 	case obj.R_CALL,
 		obj.R_PCREL:
-		addpltsym(ld.Ctxt, targ)
-		r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0)
+		addpltsym(ctxt, targ)
+		r.Sym = ctxt.Syms.Lookup(".plt", 0)
 		r.Add = int64(targ.Plt)
-		return
+		return true
 
 	case obj.R_ADDR:
 		if s.Type != obj.SDATA {
 			break
 		}
 		if ld.Iself {
-			ld.Adddynsym(ld.Ctxt, targ)
-			rel := ld.Linklookup(ld.Ctxt, ".rel", 0)
-			ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off))
-			ld.Adduint32(ld.Ctxt, rel, ld.ELF32_R_INFO(uint32(targ.Dynid), ld.R_386_32))
+			ld.Adddynsym(ctxt, targ)
+			rel := ctxt.Syms.Lookup(".rel", 0)
+			ld.Addaddrplus(ctxt, rel, s, int64(r.Off))
+			ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(targ.Dynid), ld.R_386_32))
 			r.Type = obj.R_CONST // write r->add during relocsym
 			r.Sym = nil
-			return
+			return true
 		}
 
-		if ld.HEADTYPE == obj.Hdarwin && s.Size == int64(ld.SysArch.PtrSize) && r.Off == 0 {
+		if ld.Headtype == obj.Hdarwin && s.Size == int64(ld.SysArch.PtrSize) && r.Off == 0 {
 			// Mach-O relocations are a royal pain to lay out.
 			// They use a compact stateful bytecode representation
 			// that is too much bother to deal with.
@@ -293,31 +322,30 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 			// just in case the C code assigns to the variable,
 			// and of course it only works for single pointers,
 			// but we only need to support cgo and that's all it needs.
-			ld.Adddynsym(ld.Ctxt, targ)
+			ld.Adddynsym(ctxt, targ)
 
-			got := ld.Linklookup(ld.Ctxt, ".got", 0)
+			got := ctxt.Syms.Lookup(".got", 0)
 			s.Type = got.Type | obj.SSUB
 			s.Outer = got
 			s.Sub = got.Sub
 			got.Sub = s
 			s.Value = got.Size
-			ld.Adduint32(ld.Ctxt, got, 0)
-			ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(targ.Dynid))
+			ld.Adduint32(ctxt, got, 0)
+			ld.Adduint32(ctxt, ctxt.Syms.Lookup(".linkedit.got", 0), uint32(targ.Dynid))
 			r.Type = 256 // ignore during relocsym
-			return
+			return true
 		}
 
-		if ld.HEADTYPE == obj.Hwindows && s.Size == int64(ld.SysArch.PtrSize) {
+		if (ld.Headtype == obj.Hwindows || ld.Headtype == obj.Hwindowsgui) && s.Size == int64(ld.SysArch.PtrSize) {
 			// nothing to do, the relocation will be laid out in pereloc1
-			return
+			return true
 		}
 	}
 
-	ld.Ctxt.Cursym = s
-	ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type)
+	return false
 }
 
-func elfreloc1(r *ld.Reloc, sectoff int64) int {
+func elfreloc1(ctxt *ld.Link, r *ld.Reloc, sectoff int64) int {
 	ld.Thearch.Lput(uint32(sectoff))
 
 	elfsym := r.Xsym.ElfsymForReloc()
@@ -381,14 +409,14 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func machoreloc1(r *ld.Reloc, sectoff int64) int {
+func machoreloc1(s *ld.Symbol, r *ld.Reloc, sectoff int64) int {
 	var v uint32
 
 	rs := r.Xsym
 
 	if rs.Type == obj.SHOSTOBJ {
 		if rs.Dynid < 0 {
-			ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type)
+			ld.Errorf(s, "reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type)
 			return -1
 		}
 
@@ -397,7 +425,7 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 	} else {
 		v = uint32(rs.Sect.Extnum)
 		if v == 0 {
-			ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
+			ld.Errorf(s, "reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type)
 			return -1
 		}
 	}
@@ -437,13 +465,13 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
 	return 0
 }
 
-func pereloc1(r *ld.Reloc, sectoff int64) bool {
+func pereloc1(s *ld.Symbol, r *ld.Reloc, sectoff int64) bool {
 	var v uint32
 
 	rs := r.Xsym
 
 	if rs.Dynid < 0 {
-		ld.Diag("reloc %d to non-coff symbol %s type=%d", r.Type, rs.Name, rs.Type)
+		ld.Errorf(s, "reloc %d to non-coff symbol %s type=%d", r.Type, rs.Name, rs.Type)
 		return false
 	}
 
@@ -467,7 +495,7 @@ func pereloc1(r *ld.Reloc, sectoff int64) bool {
 	return true
 }
 
-func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
+func archreloc(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, val *int64) int {
 	if ld.Linkmode == ld.LinkExternal {
 		return -1
 	}
@@ -477,46 +505,46 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
 		return 0
 
 	case obj.R_GOTOFF:
-		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0))
+		*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ctxt.Syms.Lookup(".got", 0))
 		return 0
 	}
 
 	return -1
 }
 
-func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
+func archrelocvariant(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol, t int64) int64 {
 	log.Fatalf("unexpected relocation variant")
 	return t
 }
 
-func elfsetupplt() {
-	plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
-	got := ld.Linklookup(ld.Ctxt, ".got.plt", 0)
+func elfsetupplt(ctxt *ld.Link) {
+	plt := ctxt.Syms.Lookup(".plt", 0)
+	got := ctxt.Syms.Lookup(".got.plt", 0)
 	if plt.Size == 0 {
 		// pushl got+4
-		ld.Adduint8(ld.Ctxt, plt, 0xff)
+		ld.Adduint8(ctxt, plt, 0xff)
 
-		ld.Adduint8(ld.Ctxt, plt, 0x35)
-		ld.Addaddrplus(ld.Ctxt, plt, got, 4)
+		ld.Adduint8(ctxt, plt, 0x35)
+		ld.Addaddrplus(ctxt, plt, got, 4)
 
 		// jmp *got+8
-		ld.Adduint8(ld.Ctxt, plt, 0xff)
+		ld.Adduint8(ctxt, plt, 0xff)
 
-		ld.Adduint8(ld.Ctxt, plt, 0x25)
-		ld.Addaddrplus(ld.Ctxt, plt, got, 8)
+		ld.Adduint8(ctxt, plt, 0x25)
+		ld.Addaddrplus(ctxt, plt, got, 8)
 
 		// zero pad
-		ld.Adduint32(ld.Ctxt, plt, 0)
+		ld.Adduint32(ctxt, plt, 0)
 
 		// assume got->size == 0 too
-		ld.Addaddrplus(ld.Ctxt, got, ld.Linklookup(ld.Ctxt, ".dynamic", 0), 0)
+		ld.Addaddrplus(ctxt, got, ctxt.Syms.Lookup(".dynamic", 0), 0)
 
-		ld.Adduint32(ld.Ctxt, got, 0)
-		ld.Adduint32(ld.Ctxt, got, 0)
+		ld.Adduint32(ctxt, got, 0)
+		ld.Adduint32(ctxt, got, 0)
 	}
 }
 
-func addpltsym(ctxt *ld.Link, s *ld.LSym) {
+func addpltsym(ctxt *ld.Link, s *ld.Symbol) {
 	if s.Plt >= 0 {
 		return
 	}
@@ -524,11 +552,11 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 	ld.Adddynsym(ctxt, s)
 
 	if ld.Iself {
-		plt := ld.Linklookup(ctxt, ".plt", 0)
-		got := ld.Linklookup(ctxt, ".got.plt", 0)
-		rel := ld.Linklookup(ctxt, ".rel.plt", 0)
+		plt := ctxt.Syms.Lookup(".plt", 0)
+		got := ctxt.Syms.Lookup(".got.plt", 0)
+		rel := ctxt.Syms.Lookup(".rel.plt", 0)
 		if plt.Size == 0 {
-			elfsetupplt()
+			elfsetupplt(ctxt)
 		}
 
 		// jmpq *got+size
@@ -556,52 +584,51 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 		ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_386_JMP_SLOT))
 
 		s.Plt = int32(plt.Size - 16)
-	} else if ld.HEADTYPE == obj.Hdarwin {
+	} else if ld.Headtype == obj.Hdarwin {
 		// Same laziness as in 6l.
 
-		plt := ld.Linklookup(ctxt, ".plt", 0)
+		plt := ctxt.Syms.Lookup(".plt", 0)
 
 		addgotsym(ctxt, s)
 
-		ld.Adduint32(ctxt, ld.Linklookup(ctxt, ".linkedit.plt", 0), uint32(s.Dynid))
+		ld.Adduint32(ctxt, ctxt.Syms.Lookup(".linkedit.plt", 0), uint32(s.Dynid))
 
 		// jmpq *got+size(IP)
 		s.Plt = int32(plt.Size)
 
 		ld.Adduint8(ctxt, plt, 0xff)
 		ld.Adduint8(ctxt, plt, 0x25)
-		ld.Addaddrplus(ctxt, plt, ld.Linklookup(ctxt, ".got", 0), int64(s.Got))
+		ld.Addaddrplus(ctxt, plt, ctxt.Syms.Lookup(".got", 0), int64(s.Got))
 	} else {
-		ld.Diag("addpltsym: unsupported binary format")
+		ld.Errorf(s, "addpltsym: unsupported binary format")
 	}
 }
 
-func addgotsym(ctxt *ld.Link, s *ld.LSym) {
+func addgotsym(ctxt *ld.Link, s *ld.Symbol) {
 	if s.Got >= 0 {
 		return
 	}
 
 	ld.Adddynsym(ctxt, s)
-	got := ld.Linklookup(ctxt, ".got", 0)
+	got := ctxt.Syms.Lookup(".got", 0)
 	s.Got = int32(got.Size)
 	ld.Adduint32(ctxt, got, 0)
 
 	if ld.Iself {
-		rel := ld.Linklookup(ctxt, ".rel", 0)
+		rel := ctxt.Syms.Lookup(".rel", 0)
 		ld.Addaddrplus(ctxt, rel, got, int64(s.Got))
 		ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_386_GLOB_DAT))
-	} else if ld.HEADTYPE == obj.Hdarwin {
-		ld.Adduint32(ctxt, ld.Linklookup(ctxt, ".linkedit.got", 0), uint32(s.Dynid))
+	} else if ld.Headtype == obj.Hdarwin {
+		ld.Adduint32(ctxt, ctxt.Syms.Lookup(".linkedit.got", 0), uint32(s.Dynid))
 	} else {
-		ld.Diag("addgotsym: unsupported binary format")
+		ld.Errorf(s, "addgotsym: unsupported binary format")
 	}
 }
 
-func asmb() {
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f asmb\n", obj.Cputime())
+func asmb(ctxt *ld.Link) {
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f asmb\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	if ld.Iself {
 		ld.Asmbelfsetup()
@@ -610,87 +637,91 @@ func asmb() {
 	sect := ld.Segtext.Sect
 	ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
 	// 0xCC is INT $3 - breakpoint instruction
-	ld.CodeblkPad(int64(sect.Vaddr), int64(sect.Length), []byte{0xCC})
+	ld.CodeblkPad(ctxt, int64(sect.Vaddr), int64(sect.Length), []byte{0xCC})
 	for sect = sect.Next; sect != nil; sect = sect.Next {
 		ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
-		ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
+		ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
 	}
 
 	if ld.Segrodata.Filelen > 0 {
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f rodatblk\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f rodatblk\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
 
 		ld.Cseek(int64(ld.Segrodata.Fileoff))
-		ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+		ld.Datblk(ctxt, int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen))
+	}
+	if ld.Segrelrodata.Filelen > 0 {
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f relrodatblk\n", obj.Cputime())
+		}
+		ld.Cseek(int64(ld.Segrelrodata.Fileoff))
+		ld.Datblk(ctxt, int64(ld.Segrelrodata.Vaddr), int64(ld.Segrelrodata.Filelen))
 	}
 
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f datblk\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f datblk\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 
 	ld.Cseek(int64(ld.Segdata.Fileoff))
-	ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
+	ld.Datblk(ctxt, int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen))
 
 	ld.Cseek(int64(ld.Segdwarf.Fileoff))
-	ld.Dwarfblk(int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+	ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
 
 	machlink := uint32(0)
-	if ld.HEADTYPE == obj.Hdarwin {
-		machlink = uint32(ld.Domacholink())
+	if ld.Headtype == obj.Hdarwin {
+		machlink = uint32(ld.Domacholink(ctxt))
 	}
 
 	ld.Symsize = 0
 	ld.Spsize = 0
 	ld.Lcsize = 0
 	symo := uint32(0)
-	if ld.Debug['s'] == 0 {
+	if !*ld.FlagS {
 		// TODO: rationalize
-		if ld.Debug['v'] != 0 {
-			fmt.Fprintf(ld.Bso, "%5.2f sym\n", obj.Cputime())
+		if ctxt.Debugvlog != 0 {
+			ctxt.Logf("%5.2f sym\n", obj.Cputime())
 		}
-		ld.Bso.Flush()
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
 				symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
-				symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND)))
+				symo = uint32(ld.Rnd(int64(symo), int64(*ld.FlagRound)))
 			}
 
 		case obj.Hplan9:
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink))
 
-		case obj.Hwindows:
+		case obj.Hwindows, obj.Hwindowsgui:
 			symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
 			symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN))
 		}
 
 		ld.Cseek(int64(symo))
-		switch ld.HEADTYPE {
+		switch ld.Headtype {
 		default:
 			if ld.Iself {
-				if ld.Debug['v'] != 0 {
-					fmt.Fprintf(ld.Bso, "%5.2f elfsym\n", obj.Cputime())
+				if ctxt.Debugvlog != 0 {
+					ctxt.Logf("%5.2f elfsym\n", obj.Cputime())
 				}
-				ld.Asmelfsym()
+				ld.Asmelfsym(ctxt)
 				ld.Cflush()
 				ld.Cwrite(ld.Elfstrdat)
 
 				if ld.Linkmode == ld.LinkExternal {
-					ld.Elfemitreloc()
+					ld.Elfemitreloc(ctxt)
 				}
 			}
 
 		case obj.Hplan9:
-			ld.Asmplan9sym()
+			ld.Asmplan9sym(ctxt)
 			ld.Cflush()
 
-			sym := ld.Linklookup(ld.Ctxt, "pclntab", 0)
+			sym := ctxt.Syms.Lookup("pclntab", 0)
 			if sym != nil {
 				ld.Lcsize = int32(len(sym.P))
 				for i := 0; int32(i) < ld.Lcsize; i++ {
@@ -700,24 +731,23 @@ func asmb() {
 				ld.Cflush()
 			}
 
-		case obj.Hwindows:
-			if ld.Debug['v'] != 0 {
-				fmt.Fprintf(ld.Bso, "%5.2f dwarf\n", obj.Cputime())
+		case obj.Hwindows, obj.Hwindowsgui:
+			if ctxt.Debugvlog != 0 {
+				ctxt.Logf("%5.2f dwarf\n", obj.Cputime())
 			}
 
 		case obj.Hdarwin:
 			if ld.Linkmode == ld.LinkExternal {
-				ld.Machoemitreloc()
+				ld.Machoemitreloc(ctxt)
 			}
 		}
 	}
 
-	if ld.Debug['v'] != 0 {
-		fmt.Fprintf(ld.Bso, "%5.2f headr\n", obj.Cputime())
+	if ctxt.Debugvlog != 0 {
+		ctxt.Logf("%5.2f headr\n", obj.Cputime())
 	}
-	ld.Bso.Flush()
 	ld.Cseek(0)
-	switch ld.HEADTYPE {
+	switch ld.Headtype {
 	default:
 	case obj.Hplan9: /* plan9 */
 		magic := int32(4*11*11 + 7)
@@ -726,23 +756,23 @@ func asmb() {
 		ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */
 		ld.Lputb(uint32(ld.Segdata.Filelen))
 		ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen))
-		ld.Lputb(uint32(ld.Symsize))      /* nsyms */
-		ld.Lputb(uint32(ld.Entryvalue())) /* va of entry */
-		ld.Lputb(uint32(ld.Spsize))       /* sp offsets */
-		ld.Lputb(uint32(ld.Lcsize))       /* line offsets */
+		ld.Lputb(uint32(ld.Symsize))          /* nsyms */
+		ld.Lputb(uint32(ld.Entryvalue(ctxt))) /* va of entry */
+		ld.Lputb(uint32(ld.Spsize))           /* sp offsets */
+		ld.Lputb(uint32(ld.Lcsize))           /* line offsets */
 
 	case obj.Hdarwin:
-		ld.Asmbmacho()
+		ld.Asmbmacho(ctxt)
 
 	case obj.Hlinux,
 		obj.Hfreebsd,
 		obj.Hnetbsd,
 		obj.Hopenbsd,
 		obj.Hnacl:
-		ld.Asmbelf(int64(symo))
+		ld.Asmbelf(ctxt, int64(symo))
 
-	case obj.Hwindows:
-		ld.Asmbpe()
+	case obj.Hwindows, obj.Hwindowsgui:
+		ld.Asmbpe(ctxt)
 	}
 
 	ld.Cflush()
diff --git a/src/cmd/link/internal/x86/l.go b/src/cmd/link/internal/x86/l.go
index 065508e..0f104ea 100644
--- a/src/cmd/link/internal/x86/l.go
+++ b/src/cmd/link/internal/x86/l.go
@@ -1,5 +1,5 @@
 // Inferno utils/8l/l.h
-// http://code.google.com/p/inferno-os/source/browse/utils/8l/l.h
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/8l/l.h
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -31,13 +31,13 @@
 package x86
 
 const (
-	MaxAlign  = 32 // max data alignment
-	MinAlign  = 1  // min data alignment
-	FuncAlign = 16
+	maxAlign  = 32 // max data alignment
+	minAlign  = 1  // min data alignment
+	funcAlign = 16
 )
 
 /* Used by ../internal/ld/dwarf.go */
 const (
-	DWARFREGSP = 4
-	DWARFREGLR = 8
+	dwarfRegSP = 4
+	dwarfRegLR = 8
 )
diff --git a/src/cmd/link/internal/x86/obj.go b/src/cmd/link/internal/x86/obj.go
index a4d8f50..ea213be 100644
--- a/src/cmd/link/internal/x86/obj.go
+++ b/src/cmd/link/internal/x86/obj.go
@@ -1,5 +1,5 @@
 // Inferno utils/8l/obj.c
-// http://code.google.com/p/inferno-os/source/browse/utils/8l/obj.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/8l/obj.c
 //
 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
@@ -35,24 +35,16 @@ import (
 	"cmd/internal/sys"
 	"cmd/link/internal/ld"
 	"fmt"
-	"log"
 )
 
-// Reading object files.
-
-func Main() {
-	linkarchinit()
-	ld.Ldmain()
-}
-
-func linkarchinit() {
+func Init() {
 	ld.SysArch = sys.Arch386
 
-	ld.Thearch.Funcalign = FuncAlign
-	ld.Thearch.Maxalign = MaxAlign
-	ld.Thearch.Minalign = MinAlign
-	ld.Thearch.Dwarfregsp = DWARFREGSP
-	ld.Thearch.Dwarfreglr = DWARFREGLR
+	ld.Thearch.Funcalign = funcAlign
+	ld.Thearch.Maxalign = maxAlign
+	ld.Thearch.Minalign = minAlign
+	ld.Thearch.Dwarfregsp = dwarfRegSP
+	ld.Thearch.Dwarfreglr = dwarfRegLR
 
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Archinit = archinit
@@ -78,116 +70,85 @@ func linkarchinit() {
 	ld.Thearch.Solarisdynld = "/lib/ld.so.1"
 }
 
-func archinit() {
-	// getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
-	// Go was built; see ../../make.bash.
-	if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" {
-		ld.Linkmode = ld.LinkInternal
-	}
-
-	if ld.Buildmode == ld.BuildmodeCShared || ld.Buildmode == ld.BuildmodePIE || ld.DynlinkingGo() {
-		ld.Linkmode = ld.LinkExternal
-		got := ld.Linklookup(ld.Ctxt, "_GLOBAL_OFFSET_TABLE_", 0)
-		got.Type = obj.SDYNIMPORT
-		got.Attr |= ld.AttrReachable
-	}
-
-	switch ld.HEADTYPE {
-	default:
-		if ld.Linkmode == ld.LinkAuto {
-			ld.Linkmode = ld.LinkInternal
-		}
-		if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" {
-			log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE)))
-		}
-
-	case obj.Hdarwin,
-		obj.Hfreebsd,
-		obj.Hlinux,
-		obj.Hnetbsd,
-		obj.Hopenbsd,
-		obj.Hwindows:
-		break
-	}
-
-	switch ld.HEADTYPE {
+func archinit(ctxt *ld.Link) {
+	switch ld.Headtype {
 	default:
-		ld.Exitf("unknown -H option: %v", ld.HEADTYPE)
+		ld.Exitf("unknown -H option: %v", ld.Headtype)
 
 	case obj.Hplan9: /* plan 9 */
 		ld.HEADR = 32
 
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 4096 + 32
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 4096 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
 
 	case obj.Hdarwin: /* apple MACH */
 		ld.Machoinit()
 
 		ld.HEADR = ld.INITIAL_MACHO_HEADR
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 4096 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 4096 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
 
 	case obj.Hlinux, /* elf32 executable */
 		obj.Hfreebsd,
 		obj.Hnetbsd,
 		obj.Hopenbsd:
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 
 		ld.HEADR = ld.ELFRESERVE
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x08048000 + int64(ld.HEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x08048000 + int64(ld.HEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 4096
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 4096
 		}
 
 	case obj.Hnacl:
-		ld.Elfinit()
+		ld.Elfinit(ctxt)
 		ld.HEADR = 0x10000
 		ld.Funcalign = 32
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = 0x20000
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = 0x20000
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = 0x10000
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = 0x10000
 		}
 
-	case obj.Hwindows: /* PE executable */
-		ld.Peinit()
+	case obj.Hwindows, obj.Hwindowsgui: /* PE executable */
+		ld.Peinit(ctxt)
 
 		ld.HEADR = ld.PEFILEHEADR
-		if ld.INITTEXT == -1 {
-			ld.INITTEXT = ld.PEBASE + int64(ld.PESECTHEADR)
+		if *ld.FlagTextAddr == -1 {
+			*ld.FlagTextAddr = ld.PEBASE + int64(ld.PESECTHEADR)
 		}
-		if ld.INITDAT == -1 {
-			ld.INITDAT = 0
+		if *ld.FlagDataAddr == -1 {
+			*ld.FlagDataAddr = 0
 		}
-		if ld.INITRND == -1 {
-			ld.INITRND = ld.PESECTALIGN
+		if *ld.FlagRound == -1 {
+			*ld.FlagRound = ld.PESECTALIGN
 		}
 	}
 
-	if ld.INITDAT != 0 && ld.INITRND != 0 {
-		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND))
+	if *ld.FlagDataAddr != 0 && *ld.FlagRound != 0 {
+		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(*ld.FlagDataAddr), uint32(*ld.FlagRound))
 	}
 }
diff --git a/src/cmd/link/linkbig_test.go b/src/cmd/link/linkbig_test.go
new file mode 100644
index 0000000..d793c2f
--- /dev/null
+++ b/src/cmd/link/linkbig_test.go
@@ -0,0 +1,109 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This program generates a test to verify that a program can be
+// successfully linked even when there are very large text
+// sections present.
+
+package main
+
+import (
+	"bytes"
+	"cmd/internal/obj"
+	"fmt"
+	"internal/testenv"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"testing"
+)
+
+func TestLargeText(t *testing.T) {
+	if testing.Short() || (obj.GOARCH != "ppc64le" && obj.GOARCH != "ppc64" && obj.GOARCH != "arm") {
+		t.Skip("Skipping large text section test in short mode or on %s", obj.GOARCH)
+	}
+	testenv.MustHaveGoBuild(t)
+
+	var w bytes.Buffer
+	const FN = 4
+	tmpdir, err := ioutil.TempDir("", "bigtext")
+
+	defer os.RemoveAll(tmpdir)
+
+	// Generate the scenario where the total amount of text exceeds the
+	// limit for the jmp/call instruction, on RISC architectures like ppc64le,
+	// which is 2^26.  When that happens the call requires special trampolines or
+	// long branches inserted by the linker where supported.
+	// Multiple .s files are generated instead of one.
+	instOnArch := map[string]string{
+		"ppc64":   "\tMOVD\tR0,R3\n",
+		"ppc64le": "\tMOVD\tR0,R3\n",
+		"arm":     "\tMOVW\tR0,R1\n",
+	}
+	inst := instOnArch[obj.GOARCH]
+	for j := 0; j < FN; j++ {
+		testname := fmt.Sprintf("bigfn%d", j)
+		fmt.Fprintf(&w, "TEXT ·%s(SB),$0\n", testname)
+		for i := 0; i < 2200000; i++ {
+			fmt.Fprintf(&w, inst)
+		}
+		fmt.Fprintf(&w, "\tRET\n")
+		err := ioutil.WriteFile(tmpdir+"/"+testname+".s", w.Bytes(), 0666)
+		if err != nil {
+			t.Fatalf("can't write output: %v\n", err)
+		}
+		w.Reset()
+	}
+	fmt.Fprintf(&w, "package main\n")
+	fmt.Fprintf(&w, "\nimport (\n")
+	fmt.Fprintf(&w, "\t\"os\"\n")
+	fmt.Fprintf(&w, "\t\"fmt\"\n")
+	fmt.Fprintf(&w, ")\n\n")
+
+	for i := 0; i < FN; i++ {
+		fmt.Fprintf(&w, "func bigfn%d()\n", i)
+	}
+	fmt.Fprintf(&w, "\nfunc main() {\n")
+
+	// There are lots of dummy code generated in the .s files just to generate a lot
+	// of text. Link them in but guard their call so their code is not executed but
+	// the main part of the program can be run.
+	fmt.Fprintf(&w, "\tif os.Getenv(\"LINKTESTARG\") != \"\" {\n")
+	for i := 0; i < FN; i++ {
+		fmt.Fprintf(&w, "\t\tbigfn%d()\n", i)
+	}
+	fmt.Fprintf(&w, "\t}\n")
+	fmt.Fprintf(&w, "\tfmt.Printf(\"PASS\\n\")\n")
+	fmt.Fprintf(&w, "}")
+	err = ioutil.WriteFile(tmpdir+"/bigfn.go", w.Bytes(), 0666)
+	if err != nil {
+		t.Fatalf("can't write output: %v\n", err)
+	}
+
+	// Build and run with internal linking.
+	os.Chdir(tmpdir)
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "bigtext")
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("Build failed for big text program with internal linking: %v, output: %s", err, out)
+	}
+	cmd = exec.Command(tmpdir + "/bigtext")
+	out, err = cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("Program built with internal linking failed to run with err %v, output: %s", err, out)
+	}
+
+	// Build and run with external linking
+	os.Chdir(tmpdir)
+	cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", "bigtext", "-ldflags", "'-linkmode=external'")
+	out, err = cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("Build failed for big text program with external linking: %v, output: %s", err, out)
+	}
+	cmd = exec.Command(tmpdir + "/bigtext")
+	out, err = cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("Program built with external linking failed to run with err %v, output: %s", err, out)
+	}
+}
diff --git a/src/cmd/link/main.go b/src/cmd/link/main.go
index f92e02e..a9aeb1e 100644
--- a/src/cmd/link/main.go
+++ b/src/cmd/link/main.go
@@ -9,6 +9,8 @@ import (
 	"cmd/link/internal/amd64"
 	"cmd/link/internal/arm"
 	"cmd/link/internal/arm64"
+	"cmd/link/internal/ld"
+	"cmd/link/internal/mips"
 	"cmd/link/internal/mips64"
 	"cmd/link/internal/ppc64"
 	"cmd/link/internal/s390x"
@@ -17,24 +19,41 @@ import (
 	"os"
 )
 
+// The bulk of the linker implementation lives in cmd/link/internal/ld.
+// Architecture-specific code lives in cmd/link/internal/GOARCH.
+//
+// Program initialization:
+//
+// Before any argument parsing is done, the Init function of relevant
+// architecture package is called. The only job done in Init is
+// configuration of the ld.Thearch and ld.SysArch variables.
+//
+// Then control flow passes to ld.Main, which parses flags, makes
+// some configuration decisions, and then gives the architecture
+// packages a second chance to modify the linker's configuration
+// via the ld.Thearch.Archinit function.
+
 func main() {
-	switch obj.Getgoarch() {
+	switch obj.GOARCH {
 	default:
-		fmt.Fprintf(os.Stderr, "link: unknown architecture %q\n", obj.Getgoarch())
+		fmt.Fprintf(os.Stderr, "link: unknown architecture %q\n", obj.GOARCH)
 		os.Exit(2)
 	case "386":
-		x86.Main()
+		x86.Init()
 	case "amd64", "amd64p32":
-		amd64.Main()
+		amd64.Init()
 	case "arm":
-		arm.Main()
+		arm.Init()
 	case "arm64":
-		arm64.Main()
+		arm64.Init()
+	case "mips", "mipsle":
+		mips.Init()
 	case "mips64", "mips64le":
-		mips64.Main()
+		mips64.Init()
 	case "ppc64", "ppc64le":
-		ppc64.Main()
+		ppc64.Init()
 	case "s390x":
-		s390x.Main()
+		s390x.Init()
 	}
+	ld.Main()
 }
diff --git a/src/cmd/nm/nm.go b/src/cmd/nm/nm.go
index 462c4c5..4384af8 100644
--- a/src/cmd/nm/nm.go
+++ b/src/cmd/nm/nm.go
@@ -103,11 +103,11 @@ func nm(file string) {
 
 	switch *sortOrder {
 	case "address":
-		sort.Sort(byAddr(syms))
+		sort.Slice(syms, func(i, j int) bool { return syms[i].Addr < syms[j].Addr })
 	case "name":
-		sort.Sort(byName(syms))
+		sort.Slice(syms, func(i, j int) bool { return syms[i].Name < syms[j].Name })
 	case "size":
-		sort.Sort(bySize(syms))
+		sort.Slice(syms, func(i, j int) bool { return syms[i].Size > syms[j].Size })
 	}
 
 	w := bufio.NewWriter(os.Stdout)
@@ -131,21 +131,3 @@ func nm(file string) {
 	}
 	w.Flush()
 }
-
-type byAddr []objfile.Sym
-
-func (x byAddr) Len() int           { return len(x) }
-func (x byAddr) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
-func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
-
-type byName []objfile.Sym
-
-func (x byName) Len() int           { return len(x) }
-func (x byName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
-func (x byName) Less(i, j int) bool { return x[i].Name < x[j].Name }
-
-type bySize []objfile.Sym
-
-func (x bySize) Len() int           { return len(x) }
-func (x bySize) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
-func (x bySize) Less(i, j int) bool { return x[i].Size > x[j].Size }
diff --git a/src/cmd/nm/nm_test.go b/src/cmd/nm/nm_test.go
index 602a288..ed1ad0d 100644
--- a/src/cmd/nm/nm_test.go
+++ b/src/cmd/nm/nm_test.go
@@ -65,7 +65,7 @@ func TestNM(t *testing.T) {
 	defer os.RemoveAll(tmpDir)
 
 	testnmpath := filepath.Join(tmpDir, "testnm.exe")
-	out, err := exec.Command("go", "build", "-o", testnmpath, "cmd/nm").CombinedOutput()
+	out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", testnmpath, "cmd/nm").CombinedOutput()
 	if err != nil {
 		t.Fatalf("go build -o %v cmd/nm: %v\n%s", testnmpath, err, string(out))
 	}
diff --git a/src/cmd/objdump/objdump_test.go b/src/cmd/objdump/objdump_test.go
index 899db06..30b964d 100644
--- a/src/cmd/objdump/objdump_test.go
+++ b/src/cmd/objdump/objdump_test.go
@@ -5,6 +5,8 @@
 package main
 
 import (
+	"flag"
+	"fmt"
 	"go/build"
 	"internal/testenv"
 	"io/ioutil"
@@ -16,21 +18,42 @@ import (
 	"testing"
 )
 
-func buildObjdump(t *testing.T) (tmp, exe string) {
-	testenv.MustHaveGoBuild(t)
+var tmp, exe string // populated by buildObjdump
 
-	tmp, err := ioutil.TempDir("", "TestObjDump")
+func TestMain(m *testing.M) {
+	if !testenv.HasGoBuild() {
+		return
+	}
+	var exitcode int
+	if err := buildObjdump(); err == nil {
+		exitcode = m.Run()
+	} else {
+		fmt.Println(err)
+		exitcode = 1
+	}
+	os.RemoveAll(tmp)
+	os.Exit(exitcode)
+}
+
+func buildObjdump() error {
+	var err error
+	tmp, err = ioutil.TempDir("", "TestObjDump")
 	if err != nil {
-		t.Fatal("TempDir failed: ", err)
+		return fmt.Errorf("TempDir failed: %v", err)
 	}
 
 	exe = filepath.Join(tmp, "testobjdump.exe")
-	out, err := exec.Command("go", "build", "-o", exe, "cmd/objdump").CombinedOutput()
+	gotool, err := testenv.GoTool()
+	if err != nil {
+		return err
+	}
+	out, err := exec.Command(gotool, "build", "-o", exe, "cmd/objdump").CombinedOutput()
 	if err != nil {
 		os.RemoveAll(tmp)
-		t.Fatalf("go build -o %v cmd/objdump: %v\n%s", exe, err, string(out))
+		return fmt.Errorf("go build -o %v cmd/objdump: %v\n%s", exe, err, string(out))
 	}
-	return
+
+	return nil
 }
 
 var x86Need = []string{
@@ -49,6 +72,16 @@ var armNeed = []string{
 	"RET",
 }
 
+var ppcNeed = []string{
+	"fmthello.go:6",
+	"TEXT main.main(SB)",
+	"BR main.main(SB)",
+	"CALL fmt.Println(SB)",
+	"RET",
+}
+
+var target = flag.String("target", "", "test disassembly of `goos/goarch` binary")
+
 // objdump is fully cross platform: it can handle binaries
 // from any known operating system and architecture.
 // We could in principle add binaries to testdata and check
@@ -59,14 +92,24 @@ var armNeed = []string{
 // can handle that one.
 
 func testDisasm(t *testing.T, flags ...string) {
-	tmp, exe := buildObjdump(t)
-	defer os.RemoveAll(tmp)
+	goarch := runtime.GOARCH
+	if *target != "" {
+		f := strings.Split(*target, "/")
+		if len(f) != 2 {
+			t.Fatalf("-target argument must be goos/goarch")
+		}
+		defer os.Setenv("GOOS", os.Getenv("GOOS"))
+		defer os.Setenv("GOARCH", os.Getenv("GOARCH"))
+		os.Setenv("GOOS", f[0])
+		os.Setenv("GOARCH", f[1])
+		goarch = f[1]
+	}
 
 	hello := filepath.Join(tmp, "hello.exe")
 	args := []string{"build", "-o", hello}
 	args = append(args, flags...)
 	args = append(args, "testdata/fmthello.go")
-	out, err := exec.Command("go", args...).CombinedOutput()
+	out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput()
 	if err != nil {
 		t.Fatalf("go build fmthello.go: %v\n%s", err, out)
 	}
@@ -74,11 +117,13 @@ func testDisasm(t *testing.T, flags ...string) {
 		"fmthello.go:6",
 		"TEXT main.main(SB)",
 	}
-	switch runtime.GOARCH {
+	switch goarch {
 	case "amd64", "386":
 		need = append(need, x86Need...)
 	case "arm":
 		need = append(need, armNeed...)
+	case "ppc64", "ppc64le":
+		need = append(need, ppcNeed...)
 	}
 
 	out, err = exec.Command(exe, "-s", "main.main", hello).CombinedOutput()
@@ -101,11 +146,9 @@ func testDisasm(t *testing.T, flags ...string) {
 
 func TestDisasm(t *testing.T) {
 	switch runtime.GOARCH {
-	case "ppc64", "ppc64le":
-		t.Skipf("skipping on %s, issue 9039", runtime.GOARCH)
 	case "arm64":
 		t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
-	case "mips64", "mips64le":
+	case "mips", "mipsle", "mips64", "mips64le":
 		t.Skipf("skipping on %s, issue 12559", runtime.GOARCH)
 	case "s390x":
 		t.Skipf("skipping on %s, issue 15255", runtime.GOARCH)
@@ -119,7 +162,7 @@ func TestDisasmExtld(t *testing.T) {
 		t.Skipf("skipping on %s", runtime.GOOS)
 	}
 	switch runtime.GOARCH {
-	case "ppc64", "ppc64le":
+	case "ppc64":
 		t.Skipf("skipping on %s, no support for external linking, issue 9038", runtime.GOARCH)
 	case "arm64":
 		t.Skipf("skipping on %s, issue 10106", runtime.GOARCH)
diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go
index c305a87..79d9cde 100644
--- a/src/cmd/pack/pack_test.go
+++ b/src/cmd/pack/pack_test.go
@@ -218,10 +218,11 @@ func TestHello(t *testing.T) {
 		return doRun(t, dir, args...)
 	}
 
-	run("go", "build", "cmd/pack") // writes pack binary to dir
-	run("go", "tool", "compile", "hello.go")
+	goBin := testenv.GoToolPath(t)
+	run(goBin, "build", "cmd/pack") // writes pack binary to dir
+	run(goBin, "tool", "compile", "hello.go")
 	run("./pack", "grc", "hello.a", "hello.o")
-	run("go", "tool", "link", "-o", "a.out", "hello.a")
+	run(goBin, "tool", "link", "-o", "a.out", "hello.a")
 	out := run("./a.out")
 	if out != "hello world\n" {
 		t.Fatalf("incorrect output: %q, want %q", out, "hello world\n")
@@ -282,11 +283,12 @@ func TestLargeDefs(t *testing.T) {
 		return doRun(t, dir, args...)
 	}
 
-	run("go", "build", "cmd/pack") // writes pack binary to dir
-	run("go", "tool", "compile", "large.go")
+	goBin := testenv.GoToolPath(t)
+	run(goBin, "build", "cmd/pack") // writes pack binary to dir
+	run(goBin, "tool", "compile", "large.go")
 	run("./pack", "grc", "large.a", "large.o")
-	run("go", "tool", "compile", "-I", ".", "main.go")
-	run("go", "tool", "link", "-L", ".", "-o", "a.out", "main.o")
+	run(goBin, "tool", "compile", "-I", ".", "main.go")
+	run(goBin, "tool", "link", "-L", ".", "-o", "a.out", "main.o")
 	out := run("./a.out")
 	if out != "ok\n" {
 		t.Fatalf("incorrect output: %q, want %q", out, "ok\n")
diff --git a/src/cmd/pprof/internal/commands/commands.go b/src/cmd/pprof/internal/commands/commands.go
new file mode 100644
index 0000000..9669cb9
--- /dev/null
+++ b/src/cmd/pprof/internal/commands/commands.go
@@ -0,0 +1,235 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package commands defines and manages the basic pprof commands
+package commands
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"strings"
+	"time"
+
+	"cmd/internal/browser"
+	"cmd/pprof/internal/plugin"
+	"cmd/pprof/internal/report"
+	"cmd/pprof/internal/svg"
+	"cmd/pprof/internal/tempfile"
+)
+
+// Commands describes the commands accepted by pprof.
+type Commands map[string]*Command
+
+// Command describes the actions for a pprof command. Includes a
+// function for command-line completion, the report format to use
+// during report generation, any postprocessing functions, and whether
+// the command expects a regexp parameter (typically a function name).
+type Command struct {
+	Complete    Completer     // autocomplete for interactive mode
+	Format      int           // report format to generate
+	PostProcess PostProcessor // postprocessing to run on report
+	HasParam    bool          // Collect a parameter from the CLI
+	Usage       string        // Help text
+}
+
+// Completer is a function for command-line autocompletion
+type Completer func(prefix string) string
+
+// PostProcessor is a function that applies post-processing to the report output
+type PostProcessor func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error
+
+// PProf returns the basic pprof report-generation commands
+func PProf(c Completer, interactive **bool) Commands {
+	return Commands{
+		// Commands that require no post-processing.
+		"tags":   {nil, report.Tags, nil, false, "Outputs all tags in the profile"},
+		"raw":    {c, report.Raw, nil, false, "Outputs a text representation of the raw profile"},
+		"dot":    {c, report.Dot, nil, false, "Outputs a graph in DOT format"},
+		"top":    {c, report.Text, nil, false, "Outputs top entries in text form"},
+		"tree":   {c, report.Tree, nil, false, "Outputs a text rendering of call graph"},
+		"text":   {c, report.Text, nil, false, "Outputs top entries in text form"},
+		"disasm": {c, report.Dis, nil, true, "Output annotated assembly for functions matching regexp or address"},
+		"list":   {c, report.List, nil, true, "Output annotated source for functions matching regexp"},
+		"peek":   {c, report.Tree, nil, true, "Output callers/callees of functions matching regexp"},
+
+		// Save binary formats to a file
+		"callgrind": {c, report.Callgrind, awayFromTTY(interactive, "callgraph.out"), false, "Outputs a graph in callgrind format"},
+		"proto":     {c, report.Proto, awayFromTTY(interactive, "pb.gz"), false, "Outputs the profile in compressed protobuf format"},
+
+		// Generate report in DOT format and postprocess with dot
+		"gif": {c, report.Dot, invokeDot(interactive, "gif"), false, "Outputs a graph image in GIF format"},
+		"pdf": {c, report.Dot, invokeDot(interactive, "pdf"), false, "Outputs a graph in PDF format"},
+		"png": {c, report.Dot, invokeDot(interactive, "png"), false, "Outputs a graph image in PNG format"},
+		"ps":  {c, report.Dot, invokeDot(interactive, "ps"), false, "Outputs a graph in PS format"},
+
+		// Save SVG output into a file after including svgpan library
+		"svg": {c, report.Dot, saveSVGToFile(interactive), false, "Outputs a graph in SVG format"},
+
+		// Visualize postprocessed dot output
+		"eog":    {c, report.Dot, invokeVisualizer(interactive, invokeDot(nil, "svg"), "svg", []string{"eog"}), false, "Visualize graph through eog"},
+		"evince": {c, report.Dot, invokeVisualizer(interactive, invokeDot(nil, "pdf"), "pdf", []string{"evince"}), false, "Visualize graph through evince"},
+		"gv":     {c, report.Dot, invokeVisualizer(interactive, invokeDot(nil, "ps"), "ps", []string{"gv --noantialias"}), false, "Visualize graph through gv"},
+		"web":    {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(nil), "svg", browsers()), false, "Visualize graph through web browser"},
+
+		// Visualize HTML directly generated by report.
+		"weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY(nil, "html"), "html", browsers()), true, "Output annotated source in HTML for functions matching regexp or address"},
+	}
+}
+
+// browsers returns a list of commands to attempt for web visualization
+// on the current platform
+func browsers() []string {
+	var cmds []string
+	for _, cmd := range browser.Commands() {
+		cmds = append(cmds, strings.Join(cmd, " "))
+	}
+	return cmds
+}
+
+// NewCompleter creates an autocompletion function for a set of commands.
+func NewCompleter(cs Commands) Completer {
+	return func(line string) string {
+		switch tokens := strings.Fields(line); len(tokens) {
+		case 0:
+			// Nothing to complete
+		case 1:
+			// Single token -- complete command name
+			found := ""
+			for c := range cs {
+				if strings.HasPrefix(c, tokens[0]) {
+					if found != "" {
+						return line
+					}
+					found = c
+				}
+			}
+			if found != "" {
+				return found
+			}
+		default:
+			// Multiple tokens -- complete using command completer
+			if c, ok := cs[tokens[0]]; ok {
+				if c.Complete != nil {
+					lastTokenIdx := len(tokens) - 1
+					lastToken := tokens[lastTokenIdx]
+					if strings.HasPrefix(lastToken, "-") {
+						lastToken = "-" + c.Complete(lastToken[1:])
+					} else {
+						lastToken = c.Complete(lastToken)
+					}
+					return strings.Join(append(tokens[:lastTokenIdx], lastToken), " ")
+				}
+			}
+		}
+		return line
+	}
+}
+
+// awayFromTTY saves the output in a file if it would otherwise go to
+// the terminal screen. This is used to avoid dumping binary data on
+// the screen.
+func awayFromTTY(interactive **bool, format string) PostProcessor {
+	return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
+		if output == os.Stdout && (ui.IsTerminal() || interactive != nil && **interactive) {
+			tempFile, err := tempfile.New("", "profile", "."+format)
+			if err != nil {
+				return err
+			}
+			ui.PrintErr("Generating report in ", tempFile.Name())
+			_, err = fmt.Fprint(tempFile, input)
+			return err
+		}
+		_, err := fmt.Fprint(output, input)
+		return err
+	}
+}
+
+func invokeDot(interactive **bool, format string) PostProcessor {
+	divert := awayFromTTY(interactive, format)
+	return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
+		if _, err := exec.LookPath("dot"); err != nil {
+			ui.PrintErr("Cannot find dot, have you installed Graphviz?")
+			return err
+		}
+		cmd := exec.Command("dot", "-T"+format)
+		var buf bytes.Buffer
+		cmd.Stdin, cmd.Stdout, cmd.Stderr = input, &buf, os.Stderr
+		if err := cmd.Run(); err != nil {
+			return err
+		}
+		return divert(&buf, output, ui)
+	}
+}
+
+func saveSVGToFile(interactive **bool) PostProcessor {
+	generateSVG := invokeDot(nil, "svg")
+	divert := awayFromTTY(interactive, "svg")
+	return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
+		baseSVG := &bytes.Buffer{}
+		generateSVG(input, baseSVG, ui)
+		massaged := &bytes.Buffer{}
+		fmt.Fprint(massaged, svg.Massage(*baseSVG))
+		return divert(massaged, output, ui)
+	}
+}
+
+var vizTmpDir string
+
+func makeVizTmpDir() error {
+	if vizTmpDir != "" {
+		return nil
+	}
+	name, err := ioutil.TempDir("", "pprof-")
+	if err != nil {
+		return err
+	}
+	tempfile.DeferDelete(name)
+	vizTmpDir = name
+	return nil
+}
+
+func invokeVisualizer(interactive **bool, format PostProcessor, suffix string, visualizers []string) PostProcessor {
+	return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
+		if err := makeVizTmpDir(); err != nil {
+			return err
+		}
+		tempFile, err := tempfile.New(vizTmpDir, "pprof", "."+suffix)
+		if err != nil {
+			return err
+		}
+		tempfile.DeferDelete(tempFile.Name())
+		if err = format(input, tempFile, ui); err != nil {
+			return err
+		}
+		tempFile.Close() // on windows, if the file is Open, start cannot access it.
+		// Try visualizers until one is successful
+		for _, v := range visualizers {
+			// Separate command and arguments for exec.Command.
+			args := strings.Split(v, " ")
+			if len(args) == 0 {
+				continue
+			}
+			viewer := exec.Command(args[0], append(args[1:], tempFile.Name())...)
+			viewer.Stderr = os.Stderr
+			if err = viewer.Start(); err == nil {
+				// The viewer might just send a message to another program
+				// to open the file. Give that program a little time to open the
+				// file before we remove it.
+				time.Sleep(1 * time.Second)
+
+				if !**interactive {
+					// In command-line mode, wait for the viewer to be closed
+					// before proceeding
+					return viewer.Wait()
+				}
+				return nil
+			}
+		}
+		return err
+	}
+}
diff --git a/src/cmd/pprof/internal/driver/driver.go b/src/cmd/pprof/internal/driver/driver.go
new file mode 100644
index 0000000..931985a
--- /dev/null
+++ b/src/cmd/pprof/internal/driver/driver.go
@@ -0,0 +1,1042 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package driver implements the core pprof functionality. It can be
+// parameterized with a flag implementation, fetch and symbolize
+// mechanisms.
+package driver
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"net/url"
+	"os"
+	"path/filepath"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"cmd/pprof/internal/commands"
+	"cmd/pprof/internal/plugin"
+	"cmd/pprof/internal/report"
+	"cmd/pprof/internal/tempfile"
+	"internal/pprof/profile"
+)
+
+// cpuProfileHandler is the Go pprof CPU profile handler URL.
+const cpuProfileHandler = "/debug/pprof/profile"
+
+// PProf acquires a profile, and symbolizes it using a profile
+// manager. Then it generates a report formatted according to the
+// options selected through the flags package.
+func PProf(flagset plugin.FlagSet, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, overrides commands.Commands) error {
+	// Remove any temporary files created during pprof processing.
+	defer tempfile.Cleanup()
+
+	f, err := getFlags(flagset, overrides, ui)
+	if err != nil {
+		return err
+	}
+
+	obj.SetConfig(*f.flagTools)
+
+	sources := f.profileSource
+	if len(sources) > 1 {
+		source := sources[0]
+		// If the first argument is a supported object file, treat as executable.
+		if file, err := obj.Open(source, 0); err == nil {
+			file.Close()
+			f.profileExecName = source
+			sources = sources[1:]
+		} else if *f.flagBuildID == "" && isBuildID(source) {
+			f.flagBuildID = &source
+			sources = sources[1:]
+		}
+	}
+
+	// errMu protects concurrent accesses to errset and err. errset is set if an
+	// error is encountered by one of the goroutines grabbing a profile.
+	errMu, errset := sync.Mutex{}, false
+
+	// Fetch profiles.
+	wg := sync.WaitGroup{}
+	profs := make([]*profile.Profile, len(sources))
+	for i, source := range sources {
+		wg.Add(1)
+		go func(i int, src string) {
+			defer wg.Done()
+			p, grabErr := grabProfile(src, f.profileExecName, *f.flagBuildID, fetch, sym, obj, ui, f)
+			if grabErr != nil {
+				errMu.Lock()
+				defer errMu.Unlock()
+				errset, err = true, grabErr
+				return
+			}
+			profs[i] = p
+		}(i, source)
+	}
+	wg.Wait()
+	if errset {
+		return err
+	}
+
+	// Merge profiles.
+	prof := profs[0]
+	for _, p := range profs[1:] {
+		if err = prof.Merge(p, 1); err != nil {
+			return err
+		}
+	}
+
+	if *f.flagBase != "" {
+		// Fetch base profile and subtract from current profile.
+		base, err := grabProfile(*f.flagBase, f.profileExecName, *f.flagBuildID, fetch, sym, obj, ui, f)
+		if err != nil {
+			return err
+		}
+
+		if err = prof.Merge(base, -1); err != nil {
+			return err
+		}
+	}
+
+	if err := processFlags(prof, ui, f); err != nil {
+		return err
+	}
+
+	if !*f.flagRuntime {
+		prof.RemoveUninteresting()
+	}
+
+	if *f.flagInteractive {
+		return interactive(prof, obj, ui, f)
+	}
+
+	return generate(false, prof, obj, ui, f)
+}
+
+// isBuildID determines if the profile may contain a build ID, by
+// checking that it is a string of hex digits.
+func isBuildID(id string) bool {
+	return strings.Trim(id, "0123456789abcdefABCDEF") == ""
+}
+
+// adjustURL updates the profile source URL based on heuristics. It
+// will append ?seconds=sec for CPU profiles if not already
+// specified. Returns the hostname if the profile is remote.
+func adjustURL(source string, sec int, ui plugin.UI) (adjusted, host string, duration time.Duration) {
+	// If there is a local file with this name, just use it.
+	if _, err := os.Stat(source); err == nil {
+		return source, "", 0
+	}
+
+	url, err := url.Parse(source)
+
+	// Automatically add http:// to URLs of the form hostname:port/path.
+	// url.Parse treats "hostname" as the Scheme.
+	if err != nil || (url.Host == "" && url.Scheme != "" && url.Scheme != "file") {
+		url, err = url.Parse("http://" + source)
+		if err != nil {
+			return source, "", 0
+		}
+	}
+	if scheme := strings.ToLower(url.Scheme); scheme == "" || scheme == "file" {
+		url.Scheme = ""
+		return url.String(), "", 0
+	}
+
+	values := url.Query()
+	if urlSeconds := values.Get("seconds"); urlSeconds != "" {
+		if us, err := strconv.ParseInt(urlSeconds, 10, 32); err == nil {
+			if sec >= 0 {
+				ui.PrintErr("Overriding -seconds for URL ", source)
+			}
+			sec = int(us)
+		}
+	}
+
+	switch strings.ToLower(url.Path) {
+	case "", "/":
+		// Apply default /profilez.
+		url.Path = cpuProfileHandler
+	case "/protoz":
+		// Rewrite to /profilez?type=proto
+		url.Path = cpuProfileHandler
+		values.Set("type", "proto")
+	}
+
+	if hasDuration(url.Path) {
+		if sec > 0 {
+			duration = time.Duration(sec) * time.Second
+			values.Set("seconds", fmt.Sprintf("%d", sec))
+		} else {
+			// Assume default duration: 30 seconds
+			duration = 30 * time.Second
+		}
+	}
+	url.RawQuery = values.Encode()
+	return url.String(), url.Host, duration
+}
+
+func hasDuration(path string) bool {
+	for _, trigger := range []string{"profilez", "wallz", "/profile"} {
+		if strings.Contains(path, trigger) {
+			return true
+		}
+	}
+	return false
+}
+
+// preprocess does filtering and aggregation of a profile based on the
+// requested options.
+func preprocess(prof *profile.Profile, ui plugin.UI, f *flags) error {
+	if *f.flagFocus != "" || *f.flagIgnore != "" || *f.flagHide != "" {
+		focus, ignore, hide, err := compileFocusIgnore(*f.flagFocus, *f.flagIgnore, *f.flagHide)
+		if err != nil {
+			return err
+		}
+		fm, im, hm := prof.FilterSamplesByName(focus, ignore, hide)
+
+		warnNoMatches(fm, *f.flagFocus, "Focus", ui)
+		warnNoMatches(im, *f.flagIgnore, "Ignore", ui)
+		warnNoMatches(hm, *f.flagHide, "Hide", ui)
+	}
+
+	if *f.flagTagFocus != "" || *f.flagTagIgnore != "" {
+		focus, err := compileTagFilter(*f.flagTagFocus, ui)
+		if err != nil {
+			return err
+		}
+		ignore, err := compileTagFilter(*f.flagTagIgnore, ui)
+		if err != nil {
+			return err
+		}
+		fm, im := prof.FilterSamplesByTag(focus, ignore)
+
+		warnNoMatches(fm, *f.flagTagFocus, "TagFocus", ui)
+		warnNoMatches(im, *f.flagTagIgnore, "TagIgnore", ui)
+	}
+
+	return aggregate(prof, f)
+}
+
+func compileFocusIgnore(focus, ignore, hide string) (f, i, h *regexp.Regexp, err error) {
+	if focus != "" {
+		if f, err = regexp.Compile(focus); err != nil {
+			return nil, nil, nil, fmt.Errorf("parsing focus regexp: %v", err)
+		}
+	}
+
+	if ignore != "" {
+		if i, err = regexp.Compile(ignore); err != nil {
+			return nil, nil, nil, fmt.Errorf("parsing ignore regexp: %v", err)
+		}
+	}
+
+	if hide != "" {
+		if h, err = regexp.Compile(hide); err != nil {
+			return nil, nil, nil, fmt.Errorf("parsing hide regexp: %v", err)
+		}
+	}
+	return
+}
+
+func compileTagFilter(filter string, ui plugin.UI) (f func(string, string, int64) bool, err error) {
+	if filter == "" {
+		return nil, nil
+	}
+	if numFilter := parseTagFilterRange(filter); numFilter != nil {
+		ui.PrintErr("Interpreted '", filter, "' as range, not regexp")
+		return func(key, val string, num int64) bool {
+			if val != "" {
+				return false
+			}
+			return numFilter(num, key)
+		}, nil
+	}
+	fx, err := regexp.Compile(filter)
+	if err != nil {
+		return nil, err
+	}
+
+	return func(key, val string, num int64) bool {
+		if val == "" {
+			return false
+		}
+		return fx.MatchString(key + ":" + val)
+	}, nil
+}
+
+var tagFilterRangeRx = regexp.MustCompile("([[:digit:]]+)([[:alpha:]]+)")
+
+// parseTagFilterRange returns a function to checks if a value is
+// contained on the range described by a string. It can recognize
+// strings of the form:
+// "32kb" -- matches values == 32kb
+// ":64kb" -- matches values <= 64kb
+// "4mb:" -- matches values >= 4mb
+// "12kb:64mb" -- matches values between 12kb and 64mb (both included).
+func parseTagFilterRange(filter string) func(int64, string) bool {
+	ranges := tagFilterRangeRx.FindAllStringSubmatch(filter, 2)
+	if len(ranges) == 0 {
+		return nil // No ranges were identified
+	}
+	v, err := strconv.ParseInt(ranges[0][1], 10, 64)
+	if err != nil {
+		panic(fmt.Errorf("Failed to parse int %s: %v", ranges[0][1], err))
+	}
+	value, unit := report.ScaleValue(v, ranges[0][2], ranges[0][2])
+	if len(ranges) == 1 {
+		switch match := ranges[0][0]; filter {
+		case match:
+			return func(v int64, u string) bool {
+				sv, su := report.ScaleValue(v, u, unit)
+				return su == unit && sv == value
+			}
+		case match + ":":
+			return func(v int64, u string) bool {
+				sv, su := report.ScaleValue(v, u, unit)
+				return su == unit && sv >= value
+			}
+		case ":" + match:
+			return func(v int64, u string) bool {
+				sv, su := report.ScaleValue(v, u, unit)
+				return su == unit && sv <= value
+			}
+		}
+		return nil
+	}
+	if filter != ranges[0][0]+":"+ranges[1][0] {
+		return nil
+	}
+	if v, err = strconv.ParseInt(ranges[1][1], 10, 64); err != nil {
+		panic(fmt.Errorf("Failed to parse int %s: %v", ranges[1][1], err))
+	}
+	value2, unit2 := report.ScaleValue(v, ranges[1][2], unit)
+	if unit != unit2 {
+		return nil
+	}
+	return func(v int64, u string) bool {
+		sv, su := report.ScaleValue(v, u, unit)
+		return su == unit && sv >= value && sv <= value2
+	}
+}
+
+func warnNoMatches(match bool, rx, option string, ui plugin.UI) {
+	if !match && rx != "" && rx != "." {
+		ui.PrintErr(option + " expression matched no samples: " + rx)
+	}
+}
+
+// grabProfile fetches and symbolizes a profile.
+func grabProfile(source, exec, buildid string, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, f *flags) (*profile.Profile, error) {
+	source, host, duration := adjustURL(source, *f.flagSeconds, ui)
+	remote := host != ""
+
+	if remote {
+		ui.Print("Fetching profile from ", source)
+		if duration != 0 {
+			ui.Print("Please wait... (" + duration.String() + ")")
+		}
+	}
+
+	now := time.Now()
+	// Fetch profile from source.
+	// Give 50% slack on the timeout.
+	p, err := fetch(source, duration+duration/2, ui)
+	if err != nil {
+		return nil, err
+	}
+
+	// Update the time/duration if the profile source doesn't include it.
+	// TODO(rsilvera): Remove this when we remove support for legacy profiles.
+	if remote {
+		if p.TimeNanos == 0 {
+			p.TimeNanos = now.UnixNano()
+		}
+		if duration != 0 && p.DurationNanos == 0 {
+			p.DurationNanos = int64(duration)
+		}
+	}
+
+	// Replace executable/buildID with the options provided in the
+	// command line. Assume the executable is the first Mapping entry.
+	if exec != "" || buildid != "" {
+		if len(p.Mapping) == 0 {
+			// Create a fake mapping to hold the user option, and associate
+			// all samples to it.
+			m := &profile.Mapping{
+				ID: 1,
+			}
+			for _, l := range p.Location {
+				l.Mapping = m
+			}
+			p.Mapping = []*profile.Mapping{m}
+		}
+		if exec != "" {
+			p.Mapping[0].File = exec
+		}
+		if buildid != "" {
+			p.Mapping[0].BuildID = buildid
+		}
+	}
+
+	if err := sym(*f.flagSymbolize, source, p, obj, ui); err != nil {
+		return nil, err
+	}
+
+	// Save a copy of any remote profiles, unless the user is explicitly
+	// saving it.
+	if remote && !f.isFormat("proto") {
+		prefix := "pprof."
+		if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
+			prefix = prefix + filepath.Base(p.Mapping[0].File) + "."
+		}
+		if !strings.ContainsRune(host, os.PathSeparator) {
+			prefix = prefix + host + "."
+		}
+		for _, s := range p.SampleType {
+			prefix = prefix + s.Type + "."
+		}
+
+		dir := os.Getenv("PPROF_TMPDIR")
+		tempFile, err := tempfile.New(dir, prefix, ".pb.gz")
+		if err == nil {
+			if err = p.Write(tempFile); err == nil {
+				ui.PrintErr("Saved profile in ", tempFile.Name())
+			}
+		}
+		if err != nil {
+			ui.PrintErr("Could not save profile: ", err)
+		}
+	}
+
+	if err := p.Demangle(obj.Demangle); err != nil {
+		ui.PrintErr("Failed to demangle profile: ", err)
+	}
+
+	if err := p.CheckValid(); err != nil {
+		return nil, fmt.Errorf("Grab %s: %v", source, err)
+	}
+
+	return p, nil
+}
+
+type flags struct {
+	flagInteractive   *bool              // Accept commands interactively
+	flagCommands      map[string]*bool   // pprof commands without parameters
+	flagParamCommands map[string]*string // pprof commands with parameters
+
+	flagOutput *string // Output file name
+
+	flagCum      *bool // Sort by cumulative data
+	flagCallTree *bool // generate a context-sensitive call tree
+
+	flagAddresses *bool // Report at address level
+	flagLines     *bool // Report at source line level
+	flagFiles     *bool // Report at file level
+	flagFunctions *bool // Report at function level [default]
+
+	flagSymbolize *string // Symbolization options (=none to disable)
+	flagBuildID   *string // Override build if for first mapping
+
+	flagNodeCount    *int     // Max number of nodes to show
+	flagNodeFraction *float64 // Hide nodes below <f>*total
+	flagEdgeFraction *float64 // Hide edges below <f>*total
+	flagTrim         *bool    // Set to false to ignore NodeCount/*Fraction
+	flagRuntime      *bool    // Show runtime call frames in memory profiles
+	flagFocus        *string  // Restricts to paths going through a node matching regexp
+	flagIgnore       *string  // Skips paths going through any nodes matching regexp
+	flagHide         *string  // Skips sample locations matching regexp
+	flagTagFocus     *string  // Restrict to samples tagged with key:value matching regexp
+	flagTagIgnore    *string  // Discard samples tagged with key:value matching regexp
+	flagDropNegative *bool    // Skip negative values
+
+	flagBase *string // Source for base profile to user for comparison
+
+	flagSeconds *int // Length of time for dynamic profiles
+
+	flagTotalDelay  *bool // Display total delay at each region
+	flagContentions *bool // Display number of delays at each region
+	flagMeanDelay   *bool // Display mean delay at each region
+
+	flagInUseSpace   *bool    // Display in-use memory size
+	flagInUseObjects *bool    // Display in-use object counts
+	flagAllocSpace   *bool    // Display allocated memory size
+	flagAllocObjects *bool    // Display allocated object counts
+	flagDisplayUnit  *string  // Measurement unit to use on reports
+	flagDivideBy     *float64 // Ratio to divide sample values
+
+	flagSampleIndex *int  // Sample value to use in reports.
+	flagMean        *bool // Use mean of sample_index over count
+
+	flagTools       *string
+	profileSource   []string
+	profileExecName string
+
+	extraUsage string
+	commands   commands.Commands
+}
+
+func (f *flags) isFormat(format string) bool {
+	if fl := f.flagCommands[format]; fl != nil {
+		return *fl
+	}
+	if fl := f.flagParamCommands[format]; fl != nil {
+		return *fl != ""
+	}
+	return false
+}
+
+// String provides a printable representation for the current set of flags.
+func (f *flags) String(p *profile.Profile) string {
+	var ret string
+
+	if ix := *f.flagSampleIndex; ix != -1 {
+		ret += fmt.Sprintf("  %-25s : %d (%s)\n", "sample_index", ix, p.SampleType[ix].Type)
+	}
+	if ix := *f.flagMean; ix {
+		ret += boolFlagString("mean")
+	}
+	if *f.flagDisplayUnit != "minimum" {
+		ret += stringFlagString("unit", *f.flagDisplayUnit)
+	}
+
+	switch {
+	case *f.flagInteractive:
+		ret += boolFlagString("interactive")
+	}
+	for name, fl := range f.flagCommands {
+		if *fl {
+			ret += boolFlagString(name)
+		}
+	}
+
+	if *f.flagCum {
+		ret += boolFlagString("cum")
+	}
+	if *f.flagCallTree {
+		ret += boolFlagString("call_tree")
+	}
+
+	switch {
+	case *f.flagAddresses:
+		ret += boolFlagString("addresses")
+	case *f.flagLines:
+		ret += boolFlagString("lines")
+	case *f.flagFiles:
+		ret += boolFlagString("files")
+	case *f.flagFunctions:
+		ret += boolFlagString("functions")
+	}
+
+	if *f.flagNodeCount != -1 {
+		ret += intFlagString("nodecount", *f.flagNodeCount)
+	}
+
+	ret += floatFlagString("nodefraction", *f.flagNodeFraction)
+	ret += floatFlagString("edgefraction", *f.flagEdgeFraction)
+
+	if *f.flagFocus != "" {
+		ret += stringFlagString("focus", *f.flagFocus)
+	}
+	if *f.flagIgnore != "" {
+		ret += stringFlagString("ignore", *f.flagIgnore)
+	}
+	if *f.flagHide != "" {
+		ret += stringFlagString("hide", *f.flagHide)
+	}
+
+	if *f.flagTagFocus != "" {
+		ret += stringFlagString("tagfocus", *f.flagTagFocus)
+	}
+	if *f.flagTagIgnore != "" {
+		ret += stringFlagString("tagignore", *f.flagTagIgnore)
+	}
+
+	return ret
+}
+
+func boolFlagString(label string) string {
+	return fmt.Sprintf("  %-25s : true\n", label)
+}
+
+func stringFlagString(label, value string) string {
+	return fmt.Sprintf("  %-25s : %s\n", label, value)
+}
+
+func intFlagString(label string, value int) string {
+	return fmt.Sprintf("  %-25s : %d\n", label, value)
+}
+
+func floatFlagString(label string, value float64) string {
+	return fmt.Sprintf("  %-25s : %f\n", label, value)
+}
+
+// Utility routines to set flag values.
+func newBool(b bool) *bool {
+	return &b
+}
+
+func newString(s string) *string {
+	return &s
+}
+
+func newFloat64(fl float64) *float64 {
+	return &fl
+}
+
+func newInt(i int) *int {
+	return &i
+}
+
+func (f *flags) usage(ui plugin.UI) {
+	var commandMsg []string
+	for name, cmd := range f.commands {
+		if cmd.HasParam {
+			name = name + "=p"
+		}
+		commandMsg = append(commandMsg,
+			fmt.Sprintf("  -%-16s %s", name, cmd.Usage))
+	}
+
+	sort.Strings(commandMsg)
+
+	text := usageMsgHdr + strings.Join(commandMsg, "\n") + "\n" + usageMsg + "\n"
+	if f.extraUsage != "" {
+		text += f.extraUsage + "\n"
+	}
+	text += usageMsgVars
+	ui.Print(text)
+}
+
+func getFlags(flag plugin.FlagSet, overrides commands.Commands, ui plugin.UI) (*flags, error) {
+	f := &flags{
+		flagInteractive:   flag.Bool("interactive", false, "Accepts commands interactively"),
+		flagCommands:      make(map[string]*bool),
+		flagParamCommands: make(map[string]*string),
+
+		// Filename for file-based output formats, stdout by default.
+		flagOutput: flag.String("output", "", "Output filename for file-based outputs "),
+		// Comparisons.
+		flagBase:         flag.String("base", "", "Source for base profile for comparison"),
+		flagDropNegative: flag.Bool("drop_negative", false, "Ignore negative differences"),
+
+		// Data sorting criteria.
+		flagCum: flag.Bool("cum", false, "Sort by cumulative data"),
+		// Graph handling options.
+		flagCallTree: flag.Bool("call_tree", false, "Create a context-sensitive call tree"),
+		// Granularity of output resolution.
+		flagAddresses: flag.Bool("addresses", false, "Report at address level"),
+		flagLines:     flag.Bool("lines", false, "Report at source line level"),
+		flagFiles:     flag.Bool("files", false, "Report at source file level"),
+		flagFunctions: flag.Bool("functions", false, "Report at function level [default]"),
+		// Internal options.
+		flagSymbolize: flag.String("symbolize", "", "Options for profile symbolization"),
+		flagBuildID:   flag.String("buildid", "", "Override build id for first mapping"),
+		// Filtering options
+		flagNodeCount:    flag.Int("nodecount", -1, "Max number of nodes to show"),
+		flagNodeFraction: flag.Float64("nodefraction", 0.005, "Hide nodes below <f>*total"),
+		flagEdgeFraction: flag.Float64("edgefraction", 0.001, "Hide edges below <f>*total"),
+		flagTrim:         flag.Bool("trim", true, "Honor nodefraction/edgefraction/nodecount defaults"),
+		flagRuntime:      flag.Bool("runtime", false, "Show runtime call frames in memory profiles"),
+		flagFocus:        flag.String("focus", "", "Restricts to paths going through a node matching regexp"),
+		flagIgnore:       flag.String("ignore", "", "Skips paths going through any nodes matching regexp"),
+		flagHide:         flag.String("hide", "", "Skips nodes matching regexp"),
+		flagTagFocus:     flag.String("tagfocus", "", "Restrict to samples with tags in range or matched by regexp"),
+		flagTagIgnore:    flag.String("tagignore", "", "Discard samples with tags in range or matched by regexp"),
+		// CPU profile options
+		flagSeconds: flag.Int("seconds", -1, "Length of time for dynamic profiles"),
+		// Heap profile options
+		flagInUseSpace:   flag.Bool("inuse_space", false, "Display in-use memory size"),
+		flagInUseObjects: flag.Bool("inuse_objects", false, "Display in-use object counts"),
+		flagAllocSpace:   flag.Bool("alloc_space", false, "Display allocated memory size"),
+		flagAllocObjects: flag.Bool("alloc_objects", false, "Display allocated object counts"),
+		flagDisplayUnit:  flag.String("unit", "minimum", "Measurement units to display"),
+		flagDivideBy:     flag.Float64("divide_by", 1.0, "Ratio to divide all samples before visualization"),
+		flagSampleIndex:  flag.Int("sample_index", -1, "Index of sample value to report"),
+		flagMean:         flag.Bool("mean", false, "Average sample value over first value (count)"),
+		// Contention profile options
+		flagTotalDelay:  flag.Bool("total_delay", false, "Display total delay at each region"),
+		flagContentions: flag.Bool("contentions", false, "Display number of delays at each region"),
+		flagMeanDelay:   flag.Bool("mean_delay", false, "Display mean delay at each region"),
+		flagTools:       flag.String("tools", os.Getenv("PPROF_TOOLS"), "Path for object tool pathnames"),
+		extraUsage:      flag.ExtraUsage(),
+	}
+
+	// Flags used during command processing
+	interactive := &f.flagInteractive
+	f.commands = commands.PProf(functionCompleter, interactive)
+
+	// Override commands
+	for name, cmd := range overrides {
+		f.commands[name] = cmd
+	}
+
+	for name, cmd := range f.commands {
+		if cmd.HasParam {
+			f.flagParamCommands[name] = flag.String(name, "", "Generate a report in "+name+" format, matching regexp")
+		} else {
+			f.flagCommands[name] = flag.Bool(name, false, "Generate a report in "+name+" format")
+		}
+	}
+
+	args := flag.Parse(func() { f.usage(ui) })
+	if len(args) == 0 {
+		return nil, fmt.Errorf("no profile source specified")
+	}
+
+	f.profileSource = args
+
+	// Instruct legacy heapz parsers to grab historical allocation data,
+	// instead of the default in-use data. Not available with tcmalloc.
+	if *f.flagAllocSpace || *f.flagAllocObjects {
+		profile.LegacyHeapAllocated = true
+	}
+
+	if profileDir := os.Getenv("PPROF_TMPDIR"); profileDir == "" {
+		profileDir = os.Getenv("HOME") + "/pprof"
+		os.Setenv("PPROF_TMPDIR", profileDir)
+		if err := os.MkdirAll(profileDir, 0755); err != nil {
+			return nil, fmt.Errorf("failed to access temp dir %s: %v", profileDir, err)
+		}
+	}
+
+	return f, nil
+}
+
+func processFlags(p *profile.Profile, ui plugin.UI, f *flags) error {
+	flagDis := f.isFormat("disasm")
+	flagPeek := f.isFormat("peek")
+	flagWebList := f.isFormat("weblist")
+	flagList := f.isFormat("list")
+	flagCallgrind := f.isFormat("callgrind")
+
+	if flagDis || flagWebList || flagCallgrind {
+		// Collect all samples at address granularity for assembly
+		// listing.
+		f.flagNodeCount = newInt(0)
+		f.flagAddresses = newBool(true)
+		f.flagLines = newBool(false)
+		f.flagFiles = newBool(false)
+		f.flagFunctions = newBool(false)
+	}
+
+	if flagPeek {
+		// Collect all samples at function granularity for peek command
+		f.flagNodeCount = newInt(0)
+		f.flagAddresses = newBool(false)
+		f.flagLines = newBool(false)
+		f.flagFiles = newBool(false)
+		f.flagFunctions = newBool(true)
+	}
+
+	if flagList {
+		// Collect all samples at fileline granularity for source
+		// listing.
+		f.flagNodeCount = newInt(0)
+		f.flagAddresses = newBool(false)
+		f.flagLines = newBool(true)
+		f.flagFiles = newBool(false)
+		f.flagFunctions = newBool(false)
+	}
+
+	if !*f.flagTrim {
+		f.flagNodeCount = newInt(0)
+		f.flagNodeFraction = newFloat64(0)
+		f.flagEdgeFraction = newFloat64(0)
+	}
+
+	if oc := countFlagMap(f.flagCommands, f.flagParamCommands); oc == 0 {
+		f.flagInteractive = newBool(true)
+	} else if oc > 1 {
+		f.usage(ui)
+		return fmt.Errorf("must set at most one output format")
+	}
+
+	// Apply nodecount defaults for non-interactive mode. The
+	// interactive shell will apply defaults for the interactive mode.
+	if *f.flagNodeCount < 0 && !*f.flagInteractive {
+		switch {
+		default:
+			f.flagNodeCount = newInt(80)
+		case f.isFormat("text"):
+			f.flagNodeCount = newInt(0)
+		}
+	}
+
+	// Apply legacy options and diagnose conflicts.
+	if rc := countFlags([]*bool{f.flagAddresses, f.flagLines, f.flagFiles, f.flagFunctions}); rc == 0 {
+		f.flagFunctions = newBool(true)
+	} else if rc > 1 {
+		f.usage(ui)
+		return fmt.Errorf("must set at most one granularity option")
+	}
+
+	var err error
+	si, sm := *f.flagSampleIndex, *f.flagMean || *f.flagMeanDelay
+	si, err = sampleIndex(p, &f.flagTotalDelay, si, 1, "delay", "-total_delay", err)
+	si, err = sampleIndex(p, &f.flagMeanDelay, si, 1, "delay", "-mean_delay", err)
+	si, err = sampleIndex(p, &f.flagContentions, si, 0, "contentions", "-contentions", err)
+
+	si, err = sampleIndex(p, &f.flagInUseSpace, si, 1, "inuse_space", "-inuse_space", err)
+	si, err = sampleIndex(p, &f.flagInUseObjects, si, 0, "inuse_objects", "-inuse_objects", err)
+	si, err = sampleIndex(p, &f.flagAllocSpace, si, 1, "alloc_space", "-alloc_space", err)
+	si, err = sampleIndex(p, &f.flagAllocObjects, si, 0, "alloc_objects", "-alloc_objects", err)
+
+	if si == -1 {
+		// Use last value if none is requested.
+		si = len(p.SampleType) - 1
+	} else if si < 0 || si >= len(p.SampleType) {
+		err = fmt.Errorf("sample_index value %d out of range [0..%d]", si, len(p.SampleType)-1)
+	}
+
+	if err != nil {
+		f.usage(ui)
+		return err
+	}
+	f.flagSampleIndex, f.flagMean = newInt(si), newBool(sm)
+	return nil
+}
+
+func sampleIndex(p *profile.Profile, flag **bool,
+	sampleIndex int,
+	newSampleIndex int,
+	sampleType, option string,
+	err error) (int, error) {
+	if err != nil || !**flag {
+		return sampleIndex, err
+	}
+	*flag = newBool(false)
+	if sampleIndex != -1 {
+		return 0, fmt.Errorf("set at most one sample value selection option")
+	}
+	if newSampleIndex >= len(p.SampleType) ||
+		p.SampleType[newSampleIndex].Type != sampleType {
+		return 0, fmt.Errorf("option %s not valid for this profile", option)
+	}
+	return newSampleIndex, nil
+}
+
+func countFlags(bs []*bool) int {
+	var c int
+	for _, b := range bs {
+		if *b {
+			c++
+		}
+	}
+	return c
+}
+
+func countFlagMap(bms map[string]*bool, bmrxs map[string]*string) int {
+	var c int
+	for _, b := range bms {
+		if *b {
+			c++
+		}
+	}
+	for _, s := range bmrxs {
+		if *s != "" {
+			c++
+		}
+	}
+	return c
+}
+
+var usageMsgHdr = "usage: pprof [options] [binary] <profile source> ...\n" +
+	"Output format (only set one):\n"
+
+var usageMsg = "Output file parameters (for file-based output formats):\n" +
+	"  -output=f         Generate output on file f (stdout by default)\n" +
+	"Output granularity (only set one):\n" +
+	"  -functions        Report at function level [default]\n" +
+	"  -files            Report at source file level\n" +
+	"  -lines            Report at source line level\n" +
+	"  -addresses        Report at address level\n" +
+	"Comparison options:\n" +
+	"  -base <profile>   Show delta from this profile\n" +
+	"  -drop_negative    Ignore negative differences\n" +
+	"Sorting options:\n" +
+	"  -cum              Sort by cumulative data\n\n" +
+	"Dynamic profile options:\n" +
+	"  -seconds=N        Length of time for dynamic profiles\n" +
+	"Profile trimming options:\n" +
+	"  -nodecount=N      Max number of nodes to show\n" +
+	"  -nodefraction=f   Hide nodes below <f>*total\n" +
+	"  -edgefraction=f   Hide edges below <f>*total\n" +
+	"Sample value selection option (by index):\n" +
+	"  -sample_index      Index of sample value to display\n" +
+	"  -mean              Average sample value over first value\n" +
+	"Sample value selection option (for heap profiles):\n" +
+	"  -inuse_space      Display in-use memory size\n" +
+	"  -inuse_objects    Display in-use object counts\n" +
+	"  -alloc_space      Display allocated memory size\n" +
+	"  -alloc_objects    Display allocated object counts\n" +
+	"Sample value selection option (for contention profiles):\n" +
+	"  -total_delay      Display total delay at each region\n" +
+	"  -contentions      Display number of delays at each region\n" +
+	"  -mean_delay       Display mean delay at each region\n" +
+	"Filtering options:\n" +
+	"  -runtime          Show runtime call frames in memory profiles\n" +
+	"  -focus=r          Restricts to paths going through a node matching regexp\n" +
+	"  -ignore=r         Skips paths going through any nodes matching regexp\n" +
+	"  -tagfocus=r       Restrict to samples tagged with key:value matching regexp\n" +
+	"                    Restrict to samples with numeric tags in range (eg \"32kb:1mb\")\n" +
+	"  -tagignore=r      Discard samples tagged with key:value matching regexp\n" +
+	"                    Avoid samples with numeric tags in range (eg \"1mb:\")\n" +
+	"Miscellaneous:\n" +
+	"  -call_tree        Generate a context-sensitive call tree\n" +
+	"  -unit=u           Convert all samples to unit u for display\n" +
+	"  -divide_by=f      Scale all samples by dividing them by f\n" +
+	"  -buildid=id       Override build id for main binary in profile\n" +
+	"  -tools=path       Search path for object-level tools\n" +
+	"  -help             This message"
+
+var usageMsgVars = "Environment Variables:\n" +
+	"   PPROF_TMPDIR       Location for saved profiles (default $HOME/pprof)\n" +
+	"   PPROF_TOOLS        Search path for object-level tools\n" +
+	"   PPROF_BINARY_PATH  Search path for local binary files\n" +
+	"                      default: $HOME/pprof/binaries\n" +
+	"                      finds binaries by $name and $buildid/$name"
+
+func aggregate(prof *profile.Profile, f *flags) error {
+	switch {
+	case f.isFormat("proto"), f.isFormat("raw"):
+		// No aggregation for raw profiles.
+	case *f.flagLines:
+		return prof.Aggregate(true, true, true, true, false)
+	case *f.flagFiles:
+		return prof.Aggregate(true, false, true, false, false)
+	case *f.flagFunctions:
+		return prof.Aggregate(true, true, false, false, false)
+	case f.isFormat("weblist"), f.isFormat("disasm"), f.isFormat("callgrind"):
+		return prof.Aggregate(false, true, true, true, true)
+	}
+	return nil
+}
+
+// parseOptions parses the options into report.Options
+// Returns a function to postprocess the report after generation.
+func parseOptions(f *flags) (o *report.Options, p commands.PostProcessor, err error) {
+
+	if *f.flagDivideBy == 0 {
+		return nil, nil, fmt.Errorf("zero divisor specified")
+	}
+
+	o = &report.Options{
+		CumSort:        *f.flagCum,
+		CallTree:       *f.flagCallTree,
+		PrintAddresses: *f.flagAddresses,
+		DropNegative:   *f.flagDropNegative,
+		Ratio:          1 / *f.flagDivideBy,
+
+		NodeCount:    *f.flagNodeCount,
+		NodeFraction: *f.flagNodeFraction,
+		EdgeFraction: *f.flagEdgeFraction,
+		OutputUnit:   *f.flagDisplayUnit,
+	}
+
+	for cmd, b := range f.flagCommands {
+		if *b {
+			pcmd := f.commands[cmd]
+			o.OutputFormat = pcmd.Format
+			return o, pcmd.PostProcess, nil
+		}
+	}
+
+	for cmd, rx := range f.flagParamCommands {
+		if *rx != "" {
+			pcmd := f.commands[cmd]
+			if o.Symbol, err = regexp.Compile(*rx); err != nil {
+				return nil, nil, fmt.Errorf("parsing -%s regexp: %v", cmd, err)
+			}
+			o.OutputFormat = pcmd.Format
+			return o, pcmd.PostProcess, nil
+		}
+	}
+
+	return nil, nil, fmt.Errorf("no output format selected")
+}
+
+type sampleValueFunc func(*profile.Sample) int64
+
+// sampleFormat returns a function to extract values out of a profile.Sample,
+// and the type/units of those values.
+func sampleFormat(p *profile.Profile, f *flags) (sampleValueFunc, string, string) {
+	valueIndex := *f.flagSampleIndex
+
+	if *f.flagMean {
+		return meanExtractor(valueIndex), "mean_" + p.SampleType[valueIndex].Type, p.SampleType[valueIndex].Unit
+	}
+
+	return valueExtractor(valueIndex), p.SampleType[valueIndex].Type, p.SampleType[valueIndex].Unit
+}
+
+func valueExtractor(ix int) sampleValueFunc {
+	return func(s *profile.Sample) int64 {
+		return s.Value[ix]
+	}
+}
+
+func meanExtractor(ix int) sampleValueFunc {
+	return func(s *profile.Sample) int64 {
+		if s.Value[0] == 0 {
+			return 0
+		}
+		return s.Value[ix] / s.Value[0]
+	}
+}
+
+func generate(interactive bool, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, f *flags) error {
+	o, postProcess, err := parseOptions(f)
+	if err != nil {
+		return err
+	}
+
+	var w io.Writer
+	if *f.flagOutput == "" {
+		w = os.Stdout
+	} else {
+		ui.PrintErr("Generating report in ", *f.flagOutput)
+		outputFile, err := os.Create(*f.flagOutput)
+		if err != nil {
+			return err
+		}
+		defer outputFile.Close()
+		w = outputFile
+	}
+
+	if prof.Empty() {
+		return fmt.Errorf("profile is empty")
+	}
+
+	value, stype, unit := sampleFormat(prof, f)
+	o.SampleType = stype
+	rpt := report.New(prof, *o, value, unit)
+
+	// Do not apply filters if we're just generating a proto, so we
+	// still have all the data.
+	if o.OutputFormat != report.Proto {
+		// Delay applying focus/ignore until after creating the report so
+		// the report reflects the total number of samples.
+		if err := preprocess(prof, ui, f); err != nil {
+			return err
+		}
+	}
+
+	if postProcess == nil {
+		return report.Generate(w, rpt, obj)
+	}
+
+	var dot bytes.Buffer
+	if err = report.Generate(&dot, rpt, obj); err != nil {
+		return err
+	}
+
+	return postProcess(&dot, w, ui)
+}
diff --git a/src/cmd/pprof/internal/driver/interactive.go b/src/cmd/pprof/internal/driver/interactive.go
new file mode 100644
index 0000000..1fa07a2
--- /dev/null
+++ b/src/cmd/pprof/internal/driver/interactive.go
@@ -0,0 +1,492 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package driver
+
+import (
+	"fmt"
+	"io"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+
+	"cmd/pprof/internal/commands"
+	"cmd/pprof/internal/plugin"
+	"internal/pprof/profile"
+)
+
+var profileFunctionNames = []string{}
+
+// functionCompleter replaces provided substring with a function
+// name retrieved from a profile if a single match exists. Otherwise,
+// it returns unchanged substring. It defaults to no-op if the profile
+// is not specified.
+func functionCompleter(substring string) string {
+	found := ""
+	for _, fName := range profileFunctionNames {
+		if strings.Contains(fName, substring) {
+			if found != "" {
+				return substring
+			}
+			found = fName
+		}
+	}
+	if found != "" {
+		return found
+	}
+	return substring
+}
+
+// updateAutoComplete enhances autocompletion with information that can be
+// retrieved from the profile
+func updateAutoComplete(p *profile.Profile) {
+	profileFunctionNames = nil // remove function names retrieved previously
+	for _, fn := range p.Function {
+		profileFunctionNames = append(profileFunctionNames, fn.Name)
+	}
+}
+
+// splitCommand splits the command line input into tokens separated by
+// spaces. Takes care to separate commands of the form 'top10' into
+// two tokens: 'top' and '10'
+func splitCommand(input string) []string {
+	fields := strings.Fields(input)
+	if num := strings.IndexAny(fields[0], "0123456789"); num != -1 {
+		inputNumber := fields[0][num:]
+		fields[0] = fields[0][:num]
+		fields = append([]string{fields[0], inputNumber}, fields[1:]...)
+	}
+	return fields
+}
+
+// interactive displays a prompt and reads commands for profile
+// manipulation/visualization.
+func interactive(p *profile.Profile, obj plugin.ObjTool, ui plugin.UI, f *flags) error {
+	updateAutoComplete(p)
+
+	// Enter command processing loop.
+	ui.Print("Entering interactive mode (type \"help\" for commands)")
+	ui.SetAutoComplete(commands.NewCompleter(f.commands))
+
+	for {
+		input, err := readCommand(p, ui, f)
+		if err != nil {
+			if err != io.EOF {
+				return err
+			}
+			if input == "" {
+				return nil
+			}
+		}
+		// Process simple commands.
+		switch input {
+		case "":
+			continue
+		case ":":
+			f.flagFocus = newString("")
+			f.flagIgnore = newString("")
+			f.flagTagFocus = newString("")
+			f.flagTagIgnore = newString("")
+			f.flagHide = newString("")
+			continue
+		}
+
+		fields := splitCommand(input)
+		// Process report generation commands.
+		if _, ok := f.commands[fields[0]]; ok {
+			if err := generateReport(p, fields, obj, ui, f); err != nil {
+				if err == io.EOF {
+					return nil
+				}
+				ui.PrintErr(err)
+			}
+			continue
+		}
+
+		switch cmd := fields[0]; cmd {
+		case "help":
+			commandHelp(fields, ui, f)
+			continue
+		case "exit", "quit":
+			return nil
+		}
+
+		// Process option settings.
+		if of, err := optFlags(p, input, f); err == nil {
+			f = of
+		} else {
+			ui.PrintErr("Error: ", err.Error())
+		}
+	}
+}
+
+func generateReport(p *profile.Profile, cmd []string, obj plugin.ObjTool, ui plugin.UI, f *flags) error {
+	prof := p.Copy()
+
+	cf, err := cmdFlags(prof, cmd, ui, f)
+	if err != nil {
+		return err
+	}
+
+	return generate(true, prof, obj, ui, cf)
+}
+
+// validateRegex checks if a string is a valid regular expression.
+func validateRegex(v string) error {
+	_, err := regexp.Compile(v)
+	return err
+}
+
+// readCommand prompts for and reads the next command.
+func readCommand(p *profile.Profile, ui plugin.UI, f *flags) (string, error) {
+	//ui.Print("Options:\n", f.String(p))
+	s, err := ui.ReadLine()
+	return strings.TrimSpace(s), err
+}
+
+func commandHelp(_ []string, ui plugin.UI, f *flags) error {
+	help := `
+ Commands:
+   cmd [n] [--cum] [focus_regex]* [-ignore_regex]*
+       Produce a text report with the top n entries.
+       Include samples matching focus_regex, and exclude ignore_regex.
+       Add --cum to sort using cumulative data.
+       Available commands:
+`
+	var commands []string
+	for name, cmd := range f.commands {
+		commands = append(commands, fmt.Sprintf("         %-12s %s", name, cmd.Usage))
+	}
+	sort.Strings(commands)
+
+	help = help + strings.Join(commands, "\n") + `
+   peek func_regex
+       Display callers and callees of functions matching func_regex.
+
+   dot [n] [focus_regex]* [-ignore_regex]* [>file]
+       Produce an annotated callgraph with the top n entries.
+       Include samples matching focus_regex, and exclude ignore_regex.
+       For other outputs, replace dot with:
+       - Graphic formats: dot, svg, pdf, ps, gif, png (use > to name output file)
+       - Graph viewer:    gv, web, evince, eog
+
+   callgrind [n] [focus_regex]* [-ignore_regex]* [>file]
+       Produce a file in callgrind-compatible format.
+       Include samples matching focus_regex, and exclude ignore_regex.
+
+   weblist func_regex [-ignore_regex]*
+       Show annotated source with interspersed assembly in a web browser.
+
+   list func_regex [-ignore_regex]*
+       Print source for routines matching func_regex, and exclude ignore_regex.
+
+   disasm func_regex [-ignore_regex]*
+       Disassemble routines matching func_regex, and exclude ignore_regex.
+
+   tags tag_regex [-ignore_regex]*
+       List tags with key:value matching tag_regex and exclude ignore_regex.
+
+   quit/exit/^D
+ 	     Exit pprof.
+
+   option=value
+       The following options can be set individually:
+           cum/flat:           Sort entries based on cumulative or flat data
+           call_tree:          Build context-sensitive call trees
+           nodecount:          Max number of entries to display
+           nodefraction:       Min frequency ratio of nodes to display
+           edgefraction:       Min frequency ratio of edges to display
+           focus/ignore:       Regexp to include/exclude samples by name/file
+           tagfocus/tagignore: Regexp or value range to filter samples by tag
+                               eg "1mb", "1mb:2mb", ":64kb"
+
+           functions:          Level of aggregation for sample data
+           files:
+           lines:
+           addresses:
+
+           unit:               Measurement unit to use on reports
+
+           Sample value selection by index:
+            sample_index:      Index of sample value to display
+            mean:              Average sample value over first value
+
+           Sample value selection by name:
+            alloc_space        for heap profiles
+            alloc_objects
+            inuse_space
+            inuse_objects
+
+            total_delay        for contention profiles
+            mean_delay
+            contentions
+
+   :   Clear focus/ignore/hide/tagfocus/tagignore`
+
+	ui.Print(help)
+	return nil
+}
+
+// cmdFlags parses the options of an interactive command and returns
+// an updated flags object.
+func cmdFlags(prof *profile.Profile, input []string, ui plugin.UI, f *flags) (*flags, error) {
+	cf := *f
+
+	var focus, ignore string
+	output := *cf.flagOutput
+	nodeCount := *cf.flagNodeCount
+	cmd := input[0]
+
+	// Update output flags based on parameters.
+	tokens := input[1:]
+	for p := 0; p < len(tokens); p++ {
+		t := tokens[p]
+		if t == "" {
+			continue
+		}
+		if c, err := strconv.ParseInt(t, 10, 32); err == nil {
+			nodeCount = int(c)
+			continue
+		}
+		switch t[0] {
+		case '>':
+			if len(t) > 1 {
+				output = t[1:]
+				continue
+			}
+			// find next token
+			for p++; p < len(tokens); p++ {
+				if tokens[p] != "" {
+					output = tokens[p]
+					break
+				}
+			}
+		case '-':
+			if t == "--cum" || t == "-cum" {
+				cf.flagCum = newBool(true)
+				continue
+			}
+			ignore = catRegex(ignore, t[1:])
+		default:
+			focus = catRegex(focus, t)
+		}
+	}
+
+	pcmd, ok := f.commands[cmd]
+	if !ok {
+		return nil, fmt.Errorf("Unexpected parse failure: %v", input)
+	}
+	// Reset flags
+	cf.flagCommands = make(map[string]*bool)
+	cf.flagParamCommands = make(map[string]*string)
+
+	if !pcmd.HasParam {
+		cf.flagCommands[cmd] = newBool(true)
+
+		switch cmd {
+		case "tags":
+			cf.flagTagFocus = newString(focus)
+			cf.flagTagIgnore = newString(ignore)
+		default:
+			cf.flagFocus = newString(catRegex(*cf.flagFocus, focus))
+			cf.flagIgnore = newString(catRegex(*cf.flagIgnore, ignore))
+		}
+	} else {
+		if focus == "" {
+			focus = "."
+		}
+		cf.flagParamCommands[cmd] = newString(focus)
+		cf.flagIgnore = newString(catRegex(*cf.flagIgnore, ignore))
+	}
+
+	if nodeCount < 0 {
+		switch cmd {
+		case "text", "top":
+			// Default text/top to 10 nodes on interactive mode
+			nodeCount = 10
+		default:
+			nodeCount = 80
+		}
+	}
+
+	cf.flagNodeCount = newInt(nodeCount)
+	cf.flagOutput = newString(output)
+
+	// Do regular flags processing
+	if err := processFlags(prof, ui, &cf); err != nil {
+		cf.usage(ui)
+		return nil, err
+	}
+
+	return &cf, nil
+}
+
+func catRegex(a, b string) string {
+	if a == "" {
+		return b
+	}
+	if b == "" {
+		return a
+	}
+	return a + "|" + b
+}
+
+// optFlags parses an interactive option setting and returns
+// an updated flags object.
+func optFlags(p *profile.Profile, input string, f *flags) (*flags, error) {
+	inputs := strings.SplitN(input, "=", 2)
+	option := strings.ToLower(strings.TrimSpace(inputs[0]))
+	var value string
+	if len(inputs) == 2 {
+		value = strings.TrimSpace(inputs[1])
+	}
+
+	of := *f
+
+	var err error
+	var bv bool
+	var uv uint64
+	var fv float64
+
+	switch option {
+	case "cum":
+		if bv, err = parseBool(value); err != nil {
+			return nil, err
+		}
+		of.flagCum = newBool(bv)
+	case "flat":
+		if bv, err = parseBool(value); err != nil {
+			return nil, err
+		}
+		of.flagCum = newBool(!bv)
+	case "call_tree":
+		if bv, err = parseBool(value); err != nil {
+			return nil, err
+		}
+		of.flagCallTree = newBool(bv)
+	case "unit":
+		of.flagDisplayUnit = newString(value)
+	case "sample_index":
+		if uv, err = strconv.ParseUint(value, 10, 32); err != nil {
+			return nil, err
+		}
+		if ix := int(uv); ix < 0 || ix >= len(p.SampleType) {
+			return nil, fmt.Errorf("sample_index out of range [0..%d]", len(p.SampleType)-1)
+		}
+		of.flagSampleIndex = newInt(int(uv))
+	case "mean":
+		if bv, err = parseBool(value); err != nil {
+			return nil, err
+		}
+		of.flagMean = newBool(bv)
+	case "nodecount":
+		if uv, err = strconv.ParseUint(value, 10, 32); err != nil {
+			return nil, err
+		}
+		of.flagNodeCount = newInt(int(uv))
+	case "nodefraction":
+		if fv, err = strconv.ParseFloat(value, 64); err != nil {
+			return nil, err
+		}
+		of.flagNodeFraction = newFloat64(fv)
+	case "edgefraction":
+		if fv, err = strconv.ParseFloat(value, 64); err != nil {
+			return nil, err
+		}
+		of.flagEdgeFraction = newFloat64(fv)
+	case "focus":
+		if err = validateRegex(value); err != nil {
+			return nil, err
+		}
+		of.flagFocus = newString(value)
+	case "ignore":
+		if err = validateRegex(value); err != nil {
+			return nil, err
+		}
+		of.flagIgnore = newString(value)
+	case "tagfocus":
+		if err = validateRegex(value); err != nil {
+			return nil, err
+		}
+		of.flagTagFocus = newString(value)
+	case "tagignore":
+		if err = validateRegex(value); err != nil {
+			return nil, err
+		}
+		of.flagTagIgnore = newString(value)
+	case "hide":
+		if err = validateRegex(value); err != nil {
+			return nil, err
+		}
+		of.flagHide = newString(value)
+	case "addresses", "files", "lines", "functions":
+		if bv, err = parseBool(value); err != nil {
+			return nil, err
+		}
+		if !bv {
+			return nil, fmt.Errorf("select one of addresses/files/lines/functions")
+		}
+		setGranularityToggle(option, &of)
+	default:
+		if ix := findSampleIndex(p, "", option); ix >= 0 {
+			of.flagSampleIndex = newInt(ix)
+		} else if ix := findSampleIndex(p, "total_", option); ix >= 0 {
+			of.flagSampleIndex = newInt(ix)
+			of.flagMean = newBool(false)
+		} else if ix := findSampleIndex(p, "mean_", option); ix >= 1 {
+			of.flagSampleIndex = newInt(ix)
+			of.flagMean = newBool(true)
+		} else {
+			return nil, fmt.Errorf("unrecognized command: %s", input)
+		}
+	}
+	return &of, nil
+}
+
+// parseBool parses a string as a boolean value.
+func parseBool(v string) (bool, error) {
+	switch strings.ToLower(v) {
+	case "true", "t", "yes", "y", "1", "":
+		return true, nil
+	case "false", "f", "no", "n", "0":
+		return false, nil
+	}
+	return false, fmt.Errorf(`illegal input "%s" for bool value`, v)
+}
+
+func findSampleIndex(p *profile.Profile, prefix, sampleType string) int {
+	if !strings.HasPrefix(sampleType, prefix) {
+		return -1
+	}
+	sampleType = strings.TrimPrefix(sampleType, prefix)
+	for i, r := range p.SampleType {
+		if r.Type == sampleType {
+			return i
+		}
+	}
+	return -1
+}
+
+// setGranularityToggle manages the set of granularity options. These
+// operate as a toggle; turning one on turns the others off.
+func setGranularityToggle(o string, fl *flags) {
+	t, f := newBool(true), newBool(false)
+	fl.flagFunctions = f
+	fl.flagFiles = f
+	fl.flagLines = f
+	fl.flagAddresses = f
+	switch o {
+	case "functions":
+		fl.flagFunctions = t
+	case "files":
+		fl.flagFiles = t
+	case "lines":
+		fl.flagLines = t
+	case "addresses":
+		fl.flagAddresses = t
+	default:
+		panic(fmt.Errorf("unexpected option %s", o))
+	}
+}
diff --git a/src/cmd/pprof/internal/fetch/fetch.go b/src/cmd/pprof/internal/fetch/fetch.go
new file mode 100644
index 0000000..d3ccb65
--- /dev/null
+++ b/src/cmd/pprof/internal/fetch/fetch.go
@@ -0,0 +1,98 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package fetch provides an extensible mechanism to fetch a profile
+// from a data source.
+package fetch
+
+import (
+	"crypto/tls"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"os"
+	"strings"
+	"time"
+
+	"cmd/pprof/internal/plugin"
+	"internal/pprof/profile"
+)
+
+// FetchProfile reads from a data source (network, file) and generates a
+// profile.
+func FetchProfile(source string, timeout time.Duration) (*profile.Profile, error) {
+	return Fetcher(source, timeout, plugin.StandardUI())
+}
+
+// Fetcher is the plugin.Fetcher version of FetchProfile.
+func Fetcher(source string, timeout time.Duration, ui plugin.UI) (*profile.Profile, error) {
+	var f io.ReadCloser
+	var err error
+
+	url, err := url.Parse(source)
+	if err == nil && url.Host != "" {
+		f, err = FetchURL(source, timeout)
+	} else {
+		f, err = os.Open(source)
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	return profile.Parse(f)
+}
+
+// FetchURL fetches a profile from a URL using HTTP.
+func FetchURL(source string, timeout time.Duration) (io.ReadCloser, error) {
+	resp, err := httpGet(source, timeout)
+	if err != nil {
+		return nil, fmt.Errorf("http fetch %s: %v", source, err)
+	}
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("server response: %s", resp.Status)
+	}
+
+	return resp.Body, nil
+}
+
+// PostURL issues a POST to a URL over HTTP.
+func PostURL(source, post string) ([]byte, error) {
+	resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post))
+	if err != nil {
+		return nil, fmt.Errorf("http post %s: %v", source, err)
+	}
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("server response: %s", resp.Status)
+	}
+	defer resp.Body.Close()
+	return ioutil.ReadAll(resp.Body)
+}
+
+// httpGet is a wrapper around http.Get; it is defined as a variable
+// so it can be redefined during for testing.
+var httpGet = func(source string, timeout time.Duration) (*http.Response, error) {
+	url, err := url.Parse(source)
+	if err != nil {
+		return nil, err
+	}
+
+	var tlsConfig *tls.Config
+	if url.Scheme == "https+insecure" {
+		tlsConfig = &tls.Config{
+			InsecureSkipVerify: true,
+		}
+		url.Scheme = "https"
+		source = url.String()
+	}
+
+	client := &http.Client{
+		Transport: &http.Transport{
+			ResponseHeaderTimeout: timeout + 5*time.Second,
+			TLSClientConfig:       tlsConfig,
+		},
+	}
+	return client.Get(source)
+}
diff --git a/src/cmd/pprof/internal/plugin/plugin.go b/src/cmd/pprof/internal/plugin/plugin.go
new file mode 100644
index 0000000..ff1e8ad
--- /dev/null
+++ b/src/cmd/pprof/internal/plugin/plugin.go
@@ -0,0 +1,213 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package plugin defines the plugin implementations that the main pprof driver requires.
+package plugin
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+	"regexp"
+	"strings"
+	"time"
+
+	"internal/pprof/profile"
+)
+
+// A FlagSet creates and parses command-line flags.
+// It is similar to the standard flag.FlagSet.
+type FlagSet interface {
+	// Bool, Int, Float64, and String define new flags,
+	// like the functions of the same name in package flag.
+	Bool(name string, def bool, usage string) *bool
+	Int(name string, def int, usage string) *int
+	Float64(name string, def float64, usage string) *float64
+	String(name string, def string, usage string) *string
+
+	// ExtraUsage returns any additional text that should be
+	// printed after the standard usage message.
+	// The typical use of ExtraUsage is to show any custom flags
+	// defined by the specific pprof plugins being used.
+	ExtraUsage() string
+
+	// Parse initializes the flags with their values for this run
+	// and returns the non-flag command line arguments.
+	// If an unknown flag is encountered or there are no arguments,
+	// Parse should call usage and return nil.
+	Parse(usage func()) []string
+}
+
+// An ObjTool inspects shared libraries and executable files.
+type ObjTool interface {
+	// Open opens the named object file.
+	// If the object is a shared library, start is the address where
+	// it is mapped into memory in the address space being inspected.
+	Open(file string, start uint64) (ObjFile, error)
+
+	// Demangle translates a batch of symbol names from mangled
+	// form to human-readable form.
+	Demangle(names []string) (map[string]string, error)
+
+	// Disasm disassembles the named object file, starting at
+	// the start address and stopping at (before) the end address.
+	Disasm(file string, start, end uint64) ([]Inst, error)
+
+	// SetConfig configures the tool.
+	// The implementation defines the meaning of the string
+	// and can ignore it entirely.
+	SetConfig(config string)
+}
+
+// NoObjTool returns a trivial implementation of the ObjTool interface.
+// Open returns an error indicating that the requested file does not exist.
+// Demangle returns an empty map and a nil error.
+// Disasm returns an error.
+// SetConfig is a no-op.
+func NoObjTool() ObjTool {
+	return noObjTool{}
+}
+
+type noObjTool struct{}
+
+func (noObjTool) Open(file string, start uint64) (ObjFile, error) {
+	return nil, &os.PathError{Op: "open", Path: file, Err: os.ErrNotExist}
+}
+
+func (noObjTool) Demangle(name []string) (map[string]string, error) {
+	return make(map[string]string), nil
+}
+
+func (noObjTool) Disasm(file string, start, end uint64) ([]Inst, error) {
+	return nil, fmt.Errorf("disassembly not supported")
+}
+
+func (noObjTool) SetConfig(config string) {
+}
+
+// An ObjFile is a single object file: a shared library or executable.
+type ObjFile interface {
+	// Name returns the underlyinf file name, if available
+	Name() string
+
+	// Base returns the base address to use when looking up symbols in the file.
+	Base() uint64
+
+	// BuildID returns the GNU build ID of the file, or an empty string.
+	BuildID() string
+
+	// SourceLine reports the source line information for a given
+	// address in the file. Due to inlining, the source line information
+	// is in general a list of positions representing a call stack,
+	// with the leaf function first.
+	SourceLine(addr uint64) ([]Frame, error)
+
+	// Symbols returns a list of symbols in the object file.
+	// If r is not nil, Symbols restricts the list to symbols
+	// with names matching the regular expression.
+	// If addr is not zero, Symbols restricts the list to symbols
+	// containing that address.
+	Symbols(r *regexp.Regexp, addr uint64) ([]*Sym, error)
+
+	// Close closes the file, releasing associated resources.
+	Close() error
+}
+
+// A Frame describes a single line in a source file.
+type Frame struct {
+	Func string // name of function
+	File string // source file name
+	Line int    // line in file
+}
+
+// A Sym describes a single symbol in an object file.
+type Sym struct {
+	Name  []string // names of symbol (many if symbol was dedup'ed)
+	File  string   // object file containing symbol
+	Start uint64   // start virtual address
+	End   uint64   // virtual address of last byte in sym (Start+size-1)
+}
+
+// An Inst is a single instruction in an assembly listing.
+type Inst struct {
+	Addr uint64 // virtual address of instruction
+	Text string // instruction text
+	File string // source file
+	Line int    // source line
+}
+
+// A UI manages user interactions.
+type UI interface {
+	// Read returns a line of text (a command) read from the user.
+	ReadLine() (string, error)
+
+	// Print shows a message to the user.
+	// It formats the text as fmt.Print would and adds a final \n if not already present.
+	// For line-based UI, Print writes to standard error.
+	// (Standard output is reserved for report data.)
+	Print(...interface{})
+
+	// PrintErr shows an error message to the user.
+	// It formats the text as fmt.Print would and adds a final \n if not already present.
+	// For line-based UI, PrintErr writes to standard error.
+	PrintErr(...interface{})
+
+	// IsTerminal returns whether the UI is known to be tied to an
+	// interactive terminal (as opposed to being redirected to a file).
+	IsTerminal() bool
+
+	// SetAutoComplete instructs the UI to call complete(cmd) to obtain
+	// the auto-completion of cmd, if the UI supports auto-completion at all.
+	SetAutoComplete(complete func(string) string)
+}
+
+// StandardUI returns a UI that reads from standard input,
+// prints messages to standard output,
+// prints errors to standard error, and doesn't use auto-completion.
+func StandardUI() UI {
+	return &stdUI{r: bufio.NewReader(os.Stdin)}
+}
+
+type stdUI struct {
+	r *bufio.Reader
+}
+
+func (ui *stdUI) ReadLine() (string, error) {
+	os.Stdout.WriteString("(pprof) ")
+	return ui.r.ReadString('\n')
+}
+
+func (ui *stdUI) Print(args ...interface{}) {
+	ui.fprint(os.Stderr, args)
+}
+
+func (ui *stdUI) PrintErr(args ...interface{}) {
+	ui.fprint(os.Stderr, args)
+}
+
+func (ui *stdUI) IsTerminal() bool {
+	return false
+}
+
+func (ui *stdUI) SetAutoComplete(func(string) string) {
+}
+
+func (ui *stdUI) fprint(f *os.File, args []interface{}) {
+	text := fmt.Sprint(args...)
+	if !strings.HasSuffix(text, "\n") {
+		text += "\n"
+	}
+	f.WriteString(text)
+}
+
+// A Fetcher reads and returns the profile named by src.
+// It gives up after the given timeout, unless src contains a timeout override
+// (as defined by the implementation).
+// It can print messages to ui.
+type Fetcher func(src string, timeout time.Duration, ui UI) (*profile.Profile, error)
+
+// A Symbolizer annotates a profile with symbol information.
+// The profile was fetch from src.
+// The meaning of mode is defined by the implementation.
+type Symbolizer func(mode, src string, prof *profile.Profile, obj ObjTool, ui UI) error
diff --git a/src/cmd/pprof/internal/report/report.go b/src/cmd/pprof/internal/report/report.go
new file mode 100644
index 0000000..14875c1
--- /dev/null
+++ b/src/cmd/pprof/internal/report/report.go
@@ -0,0 +1,1726 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package report summarizes a performance profile into a
+// human-readable report.
+package report
+
+import (
+	"fmt"
+	"io"
+	"math"
+	"path/filepath"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+
+	"cmd/pprof/internal/plugin"
+	"internal/pprof/profile"
+)
+
+// Generate generates a report as directed by the Report.
+func Generate(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
+	o := rpt.options
+
+	switch o.OutputFormat {
+	case Dot:
+		return printDOT(w, rpt)
+	case Tree:
+		return printTree(w, rpt)
+	case Text:
+		return printText(w, rpt)
+	case Raw:
+		fmt.Fprint(w, rpt.prof.String())
+		return nil
+	case Tags:
+		return printTags(w, rpt)
+	case Proto:
+		return rpt.prof.Write(w)
+	case Dis:
+		return printAssembly(w, rpt, obj)
+	case List:
+		return printSource(w, rpt)
+	case WebList:
+		return printWebSource(w, rpt, obj)
+	case Callgrind:
+		return printCallgrind(w, rpt)
+	}
+	return fmt.Errorf("unexpected output format")
+}
+
+// printAssembly prints an annotated assembly listing.
+func printAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
+	g, err := newGraph(rpt)
+	if err != nil {
+		return err
+	}
+
+	o := rpt.options
+	prof := rpt.prof
+
+	// If the regexp source can be parsed as an address, also match
+	// functions that land on that address.
+	var address *uint64
+	if hex, err := strconv.ParseUint(o.Symbol.String(), 0, 64); err == nil {
+		address = &hex
+	}
+
+	fmt.Fprintln(w, "Total:", rpt.formatValue(rpt.total))
+	symbols := symbolsFromBinaries(prof, g, o.Symbol, address, obj)
+	symNodes := nodesPerSymbol(g.ns, symbols)
+	// Sort function names for printing.
+	var syms objSymbols
+	for s := range symNodes {
+		syms = append(syms, s)
+	}
+	sort.Sort(syms)
+
+	// Correlate the symbols from the binary with the profile samples.
+	for _, s := range syms {
+		sns := symNodes[s]
+
+		// Gather samples for this symbol.
+		flatSum, cumSum := sumNodes(sns)
+
+		// Get the function assembly.
+		insns, err := obj.Disasm(s.sym.File, s.sym.Start, s.sym.End)
+		if err != nil {
+			return err
+		}
+
+		ns := annotateAssembly(insns, sns, s.base)
+
+		fmt.Fprintf(w, "ROUTINE ======================== %s\n", s.sym.Name[0])
+		for _, name := range s.sym.Name[1:] {
+			fmt.Fprintf(w, "    AKA ======================== %s\n", name)
+		}
+		fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n",
+			rpt.formatValue(flatSum), rpt.formatValue(cumSum),
+			percentage(cumSum, rpt.total))
+
+		for _, n := range ns {
+			fmt.Fprintf(w, "%10s %10s %10x: %s\n", valueOrDot(n.flat, rpt), valueOrDot(n.cum, rpt), n.info.address, n.info.name)
+		}
+	}
+	return nil
+}
+
+// symbolsFromBinaries examines the binaries listed on the profile
+// that have associated samples, and identifies symbols matching rx.
+func symbolsFromBinaries(prof *profile.Profile, g graph, rx *regexp.Regexp, address *uint64, obj plugin.ObjTool) []*objSymbol {
+	hasSamples := make(map[string]bool)
+	// Only examine mappings that have samples that match the
+	// regexp. This is an optimization to speed up pprof.
+	for _, n := range g.ns {
+		if name := n.info.prettyName(); rx.MatchString(name) && n.info.objfile != "" {
+			hasSamples[n.info.objfile] = true
+		}
+	}
+
+	// Walk all mappings looking for matching functions with samples.
+	var objSyms []*objSymbol
+	for _, m := range prof.Mapping {
+		if !hasSamples[filepath.Base(m.File)] {
+			if address == nil || !(m.Start <= *address && *address <= m.Limit) {
+				continue
+			}
+		}
+
+		f, err := obj.Open(m.File, m.Start)
+		if err != nil {
+			fmt.Printf("%v\n", err)
+			continue
+		}
+
+		// Find symbols in this binary matching the user regexp.
+		var addr uint64
+		if address != nil {
+			addr = *address
+		}
+		msyms, err := f.Symbols(rx, addr)
+		base := f.Base()
+		f.Close()
+		if err != nil {
+			continue
+		}
+		for _, ms := range msyms {
+			objSyms = append(objSyms,
+				&objSymbol{
+					sym:  ms,
+					base: base,
+				},
+			)
+		}
+	}
+
+	return objSyms
+}
+
+// objSym represents a symbol identified from a binary. It includes
+// the SymbolInfo from the disasm package and the base that must be
+// added to correspond to sample addresses
+type objSymbol struct {
+	sym  *plugin.Sym
+	base uint64
+}
+
+// objSymbols is a wrapper type to enable sorting of []*objSymbol.
+type objSymbols []*objSymbol
+
+func (o objSymbols) Len() int {
+	return len(o)
+}
+
+func (o objSymbols) Less(i, j int) bool {
+	if namei, namej := o[i].sym.Name[0], o[j].sym.Name[0]; namei != namej {
+		return namei < namej
+	}
+	return o[i].sym.Start < o[j].sym.Start
+}
+
+func (o objSymbols) Swap(i, j int) {
+	o[i], o[j] = o[j], o[i]
+}
+
+// nodesPerSymbol classifies nodes into a group of symbols.
+func nodesPerSymbol(ns nodes, symbols []*objSymbol) map[*objSymbol]nodes {
+	symNodes := make(map[*objSymbol]nodes)
+	for _, s := range symbols {
+		// Gather samples for this symbol.
+		for _, n := range ns {
+			address := n.info.address - s.base
+			if address >= s.sym.Start && address < s.sym.End {
+				symNodes[s] = append(symNodes[s], n)
+			}
+		}
+	}
+	return symNodes
+}
+
+// annotateAssembly annotates a set of assembly instructions with a
+// set of samples. It returns a set of nodes to display.  base is an
+// offset to adjust the sample addresses.
+func annotateAssembly(insns []plugin.Inst, samples nodes, base uint64) nodes {
+	// Add end marker to simplify printing loop.
+	insns = append(insns, plugin.Inst{
+		Addr: ^uint64(0),
+	})
+
+	// Ensure samples are sorted by address.
+	samples.sort(addressOrder)
+
+	var s int
+	var asm nodes
+	for ix, in := range insns[:len(insns)-1] {
+		n := node{
+			info: nodeInfo{
+				address: in.Addr,
+				name:    in.Text,
+				file:    trimPath(in.File),
+				lineno:  in.Line,
+			},
+		}
+
+		// Sum all the samples until the next instruction (to account
+		// for samples attributed to the middle of an instruction).
+		for next := insns[ix+1].Addr; s < len(samples) && samples[s].info.address-base < next; s++ {
+			n.flat += samples[s].flat
+			n.cum += samples[s].cum
+			if samples[s].info.file != "" {
+				n.info.file = trimPath(samples[s].info.file)
+				n.info.lineno = samples[s].info.lineno
+			}
+		}
+		asm = append(asm, &n)
+	}
+
+	return asm
+}
+
+// valueOrDot formats a value according to a report, intercepting zero
+// values.
+func valueOrDot(value int64, rpt *Report) string {
+	if value == 0 {
+		return "."
+	}
+	return rpt.formatValue(value)
+}
+
+// printTags collects all tags referenced in the profile and prints
+// them in a sorted table.
+func printTags(w io.Writer, rpt *Report) error {
+	p := rpt.prof
+
+	// Hashtable to keep accumulate tags as key,value,count.
+	tagMap := make(map[string]map[string]int64)
+	for _, s := range p.Sample {
+		for key, vals := range s.Label {
+			for _, val := range vals {
+				if valueMap, ok := tagMap[key]; ok {
+					valueMap[val] = valueMap[val] + s.Value[0]
+					continue
+				}
+				valueMap := make(map[string]int64)
+				valueMap[val] = s.Value[0]
+				tagMap[key] = valueMap
+			}
+		}
+		for key, vals := range s.NumLabel {
+			for _, nval := range vals {
+				val := scaledValueLabel(nval, key, "auto")
+				if valueMap, ok := tagMap[key]; ok {
+					valueMap[val] = valueMap[val] + s.Value[0]
+					continue
+				}
+				valueMap := make(map[string]int64)
+				valueMap[val] = s.Value[0]
+				tagMap[key] = valueMap
+			}
+		}
+	}
+
+	tagKeys := make(tags, 0, len(tagMap))
+	for key := range tagMap {
+		tagKeys = append(tagKeys, &tag{name: key})
+	}
+	sort.Sort(tagKeys)
+
+	for _, tagKey := range tagKeys {
+		var total int64
+		key := tagKey.name
+		tags := make(tags, 0, len(tagMap[key]))
+		for t, c := range tagMap[key] {
+			total += c
+			tags = append(tags, &tag{name: t, weight: c})
+		}
+
+		sort.Sort(tags)
+		fmt.Fprintf(w, "%s: Total %d\n", key, total)
+		for _, t := range tags {
+			if total > 0 {
+				fmt.Fprintf(w, "  %8d (%s): %s\n", t.weight,
+					percentage(t.weight, total), t.name)
+			} else {
+				fmt.Fprintf(w, "  %8d: %s\n", t.weight, t.name)
+			}
+		}
+		fmt.Fprintln(w)
+	}
+	return nil
+}
+
+// printText prints a flat text report for a profile.
+func printText(w io.Writer, rpt *Report) error {
+	g, err := newGraph(rpt)
+	if err != nil {
+		return err
+	}
+
+	origCount, droppedNodes, _ := g.preprocess(rpt)
+	fmt.Fprintln(w, strings.Join(legendDetailLabels(rpt, g, origCount, droppedNodes, 0), "\n"))
+
+	fmt.Fprintf(w, "%10s %5s%% %5s%% %10s %5s%%\n",
+		"flat", "flat", "sum", "cum", "cum")
+
+	var flatSum int64
+	for _, n := range g.ns {
+		name, flat, cum := n.info.prettyName(), n.flat, n.cum
+
+		flatSum += flat
+		fmt.Fprintf(w, "%10s %s %s %10s %s  %s\n",
+			rpt.formatValue(flat),
+			percentage(flat, rpt.total),
+			percentage(flatSum, rpt.total),
+			rpt.formatValue(cum),
+			percentage(cum, rpt.total),
+			name)
+	}
+	return nil
+}
+
+// printCallgrind prints a graph for a profile on callgrind format.
+func printCallgrind(w io.Writer, rpt *Report) error {
+	g, err := newGraph(rpt)
+	if err != nil {
+		return err
+	}
+
+	o := rpt.options
+	rpt.options.NodeFraction = 0
+	rpt.options.EdgeFraction = 0
+	rpt.options.NodeCount = 0
+
+	g.preprocess(rpt)
+
+	fmt.Fprintln(w, "positions: instr line")
+	fmt.Fprintln(w, "events:", o.SampleType+"("+o.OutputUnit+")")
+
+	objfiles := make(map[string]int)
+	files := make(map[string]int)
+	names := make(map[string]int)
+
+	// prevInfo points to the previous nodeInfo.
+	// It is used to group cost lines together as much as possible.
+	var prevInfo *nodeInfo
+	for _, n := range g.ns {
+		if prevInfo == nil || n.info.objfile != prevInfo.objfile || n.info.file != prevInfo.file || n.info.name != prevInfo.name {
+			fmt.Fprintln(w)
+			fmt.Fprintln(w, "ob="+callgrindName(objfiles, n.info.objfile))
+			fmt.Fprintln(w, "fl="+callgrindName(files, n.info.file))
+			fmt.Fprintln(w, "fn="+callgrindName(names, n.info.name))
+		}
+
+		addr := callgrindAddress(prevInfo, n.info.address)
+		sv, _ := ScaleValue(n.flat, o.SampleUnit, o.OutputUnit)
+		fmt.Fprintf(w, "%s %d %d\n", addr, n.info.lineno, int(sv))
+
+		// Print outgoing edges.
+		for _, out := range sortedEdges(n.out) {
+			c, _ := ScaleValue(out.weight, o.SampleUnit, o.OutputUnit)
+			callee := out.dest
+			fmt.Fprintln(w, "cfl="+callgrindName(files, callee.info.file))
+			fmt.Fprintln(w, "cfn="+callgrindName(names, callee.info.name))
+			fmt.Fprintf(w, "calls=%d %s %d\n", int(c), callgrindAddress(prevInfo, callee.info.address), callee.info.lineno)
+			// TODO: This address may be in the middle of a call
+			// instruction. It would be best to find the beginning
+			// of the instruction, but the tools seem to handle
+			// this OK.
+			fmt.Fprintf(w, "* * %d\n", int(c))
+		}
+
+		prevInfo = &n.info
+	}
+
+	return nil
+}
+
+// callgrindName implements the callgrind naming compression scheme.
+// For names not previously seen returns "(N) name", where N is a
+// unique index. For names previously seen returns "(N)" where N is
+// the index returned the first time.
+func callgrindName(names map[string]int, name string) string {
+	if name == "" {
+		return ""
+	}
+	if id, ok := names[name]; ok {
+		return fmt.Sprintf("(%d)", id)
+	}
+	id := len(names) + 1
+	names[name] = id
+	return fmt.Sprintf("(%d) %s", id, name)
+}
+
+// callgrindAddress implements the callgrind subposition compression scheme if
+// possible. If prevInfo != nil, it contains the previous address. The current
+// address can be given relative to the previous address, with an explicit +/-
+// to indicate it is relative, or * for the same address.
+func callgrindAddress(prevInfo *nodeInfo, curr uint64) string {
+	abs := fmt.Sprintf("%#x", curr)
+	if prevInfo == nil {
+		return abs
+	}
+
+	prev := prevInfo.address
+	if prev == curr {
+		return "*"
+	}
+
+	diff := int64(curr - prev)
+	relative := fmt.Sprintf("%+d", diff)
+
+	// Only bother to use the relative address if it is actually shorter.
+	if len(relative) < len(abs) {
+		return relative
+	}
+
+	return abs
+}
+
+// printTree prints a tree-based report in text form.
+func printTree(w io.Writer, rpt *Report) error {
+	const separator = "----------------------------------------------------------+-------------"
+	const legend = "      flat  flat%   sum%        cum   cum%   calls calls% + context 	 	 "
+
+	g, err := newGraph(rpt)
+	if err != nil {
+		return err
+	}
+
+	origCount, droppedNodes, _ := g.preprocess(rpt)
+	fmt.Fprintln(w, strings.Join(legendDetailLabels(rpt, g, origCount, droppedNodes, 0), "\n"))
+
+	fmt.Fprintln(w, separator)
+	fmt.Fprintln(w, legend)
+	var flatSum int64
+
+	rx := rpt.options.Symbol
+	for _, n := range g.ns {
+		name, flat, cum := n.info.prettyName(), n.flat, n.cum
+
+		// Skip any entries that do not match the regexp (for the "peek" command).
+		if rx != nil && !rx.MatchString(name) {
+			continue
+		}
+
+		fmt.Fprintln(w, separator)
+		// Print incoming edges.
+		inEdges := sortedEdges(n.in)
+		inSum := inEdges.sum()
+		for _, in := range inEdges {
+			fmt.Fprintf(w, "%50s %s |   %s\n", rpt.formatValue(in.weight),
+				percentage(in.weight, inSum), in.src.info.prettyName())
+		}
+
+		// Print current node.
+		flatSum += flat
+		fmt.Fprintf(w, "%10s %s %s %10s %s                | %s\n",
+			rpt.formatValue(flat),
+			percentage(flat, rpt.total),
+			percentage(flatSum, rpt.total),
+			rpt.formatValue(cum),
+			percentage(cum, rpt.total),
+			name)
+
+		// Print outgoing edges.
+		outEdges := sortedEdges(n.out)
+		outSum := outEdges.sum()
+		for _, out := range outEdges {
+			fmt.Fprintf(w, "%50s %s |   %s\n", rpt.formatValue(out.weight),
+				percentage(out.weight, outSum), out.dest.info.prettyName())
+		}
+	}
+	if len(g.ns) > 0 {
+		fmt.Fprintln(w, separator)
+	}
+	return nil
+}
+
+// printDOT prints an annotated callgraph in DOT format.
+func printDOT(w io.Writer, rpt *Report) error {
+	g, err := newGraph(rpt)
+	if err != nil {
+		return err
+	}
+
+	origCount, droppedNodes, droppedEdges := g.preprocess(rpt)
+
+	prof := rpt.prof
+	graphname := "unnamed"
+	if len(prof.Mapping) > 0 {
+		graphname = filepath.Base(prof.Mapping[0].File)
+	}
+	fmt.Fprintln(w, `digraph "`+graphname+`" {`)
+	fmt.Fprintln(w, `node [style=filled fillcolor="#f8f8f8"]`)
+	fmt.Fprintln(w, dotLegend(rpt, g, origCount, droppedNodes, droppedEdges))
+
+	if len(g.ns) == 0 {
+		fmt.Fprintln(w, "}")
+		return nil
+	}
+
+	// Make sure nodes have a unique consistent id.
+	nodeIndex := make(map[*node]int)
+	maxFlat := float64(g.ns[0].flat)
+	for i, n := range g.ns {
+		nodeIndex[n] = i + 1
+		if float64(n.flat) > maxFlat {
+			maxFlat = float64(n.flat)
+		}
+	}
+	var edges edgeList
+	for _, n := range g.ns {
+		node := dotNode(rpt, maxFlat, nodeIndex[n], n)
+		fmt.Fprintln(w, node)
+		if nodelets := dotNodelets(rpt, nodeIndex[n], n); nodelets != "" {
+			fmt.Fprint(w, nodelets)
+		}
+
+		// Collect outgoing edges.
+		for _, e := range n.out {
+			edges = append(edges, e)
+		}
+	}
+	// Sort edges by frequency as a hint to the graph layout engine.
+	sort.Sort(edges)
+	for _, e := range edges {
+		fmt.Fprintln(w, dotEdge(rpt, nodeIndex[e.src], nodeIndex[e.dest], e))
+	}
+	fmt.Fprintln(w, "}")
+	return nil
+}
+
+// percentage computes the percentage of total of a value, and encodes
+// it as a string. At least two digits of precision are printed.
+func percentage(value, total int64) string {
+	var ratio float64
+	if total != 0 {
+		ratio = float64(value) / float64(total) * 100
+	}
+	switch {
+	case ratio >= 99.95:
+		return "  100%"
+	case ratio >= 1.0:
+		return fmt.Sprintf("%5.2f%%", ratio)
+	default:
+		return fmt.Sprintf("%5.2g%%", ratio)
+	}
+}
+
+// dotLegend generates the overall graph label for a report in DOT format.
+func dotLegend(rpt *Report, g graph, origCount, droppedNodes, droppedEdges int) string {
+	label := legendLabels(rpt)
+	label = append(label, legendDetailLabels(rpt, g, origCount, droppedNodes, droppedEdges)...)
+	return fmt.Sprintf(`subgraph cluster_L { L [shape=box fontsize=32 label="%s\l"] }`, strings.Join(label, `\l`))
+}
+
+// legendLabels generates labels exclusive to graph visualization.
+func legendLabels(rpt *Report) []string {
+	prof := rpt.prof
+	o := rpt.options
+	var label []string
+	if len(prof.Mapping) > 0 {
+		if prof.Mapping[0].File != "" {
+			label = append(label, "File: "+filepath.Base(prof.Mapping[0].File))
+		}
+		if prof.Mapping[0].BuildID != "" {
+			label = append(label, "Build ID: "+prof.Mapping[0].BuildID)
+		}
+	}
+	if o.SampleType != "" {
+		label = append(label, "Type: "+o.SampleType)
+	}
+	if prof.TimeNanos != 0 {
+		const layout = "Jan 2, 2006 at 3:04pm (MST)"
+		label = append(label, "Time: "+time.Unix(0, prof.TimeNanos).Format(layout))
+	}
+	if prof.DurationNanos != 0 {
+		label = append(label, fmt.Sprintf("Duration: %v", time.Duration(prof.DurationNanos)))
+	}
+	return label
+}
+
+// legendDetailLabels generates labels common to graph and text visualization.
+func legendDetailLabels(rpt *Report, g graph, origCount, droppedNodes, droppedEdges int) []string {
+	nodeFraction := rpt.options.NodeFraction
+	edgeFraction := rpt.options.EdgeFraction
+	nodeCount := rpt.options.NodeCount
+
+	label := []string{}
+
+	var flatSum int64
+	for _, n := range g.ns {
+		flatSum = flatSum + n.flat
+	}
+
+	label = append(label, fmt.Sprintf("%s of %s total (%s)", rpt.formatValue(flatSum), rpt.formatValue(rpt.total), percentage(flatSum, rpt.total)))
+
+	if rpt.total > 0 {
+		if droppedNodes > 0 {
+			label = append(label, genLabel(droppedNodes, "node", "cum",
+				rpt.formatValue(int64(float64(rpt.total)*nodeFraction))))
+		}
+		if droppedEdges > 0 {
+			label = append(label, genLabel(droppedEdges, "edge", "freq",
+				rpt.formatValue(int64(float64(rpt.total)*edgeFraction))))
+		}
+		if nodeCount > 0 && nodeCount < origCount {
+			label = append(label, fmt.Sprintf("Showing top %d nodes out of %d (cum >= %s)",
+				nodeCount, origCount,
+				rpt.formatValue(g.ns[len(g.ns)-1].cum)))
+		}
+	}
+	return label
+}
+
+func genLabel(d int, n, l, f string) string {
+	if d > 1 {
+		n = n + "s"
+	}
+	return fmt.Sprintf("Dropped %d %s (%s <= %s)", d, n, l, f)
+}
+
+// dotNode generates a graph node in DOT format.
+func dotNode(rpt *Report, maxFlat float64, rIndex int, n *node) string {
+	flat, cum := n.flat, n.cum
+
+	labels := strings.Split(n.info.prettyName(), "::")
+	label := strings.Join(labels, `\n`) + `\n`
+
+	flatValue := rpt.formatValue(flat)
+	if flat > 0 {
+		label = label + fmt.Sprintf(`%s(%s)`,
+			flatValue,
+			strings.TrimSpace(percentage(flat, rpt.total)))
+	} else {
+		label = label + "0"
+	}
+	cumValue := flatValue
+	if cum != flat {
+		if flat > 0 {
+			label = label + `\n`
+		} else {
+			label = label + " "
+		}
+		cumValue = rpt.formatValue(cum)
+		label = label + fmt.Sprintf(`of %s(%s)`,
+			cumValue,
+			strings.TrimSpace(percentage(cum, rpt.total)))
+	}
+
+	// Scale font sizes from 8 to 24 based on percentage of flat frequency.
+	// Use non linear growth to emphasize the size difference.
+	baseFontSize, maxFontGrowth := 8, 16.0
+	fontSize := baseFontSize
+	if maxFlat > 0 && flat > 0 && float64(flat) <= maxFlat {
+		fontSize += int(math.Ceil(maxFontGrowth * math.Sqrt(float64(flat)/maxFlat)))
+	}
+	return fmt.Sprintf(`N%d [label="%s" fontsize=%d shape=box tooltip="%s (%s)"]`,
+		rIndex,
+		label,
+		fontSize, n.info.prettyName(), cumValue)
+}
+
+// dotEdge generates a graph edge in DOT format.
+func dotEdge(rpt *Report, from, to int, e *edgeInfo) string {
+	w := rpt.formatValue(e.weight)
+	attr := fmt.Sprintf(`label=" %s"`, w)
+	if rpt.total > 0 {
+		if weight := 1 + int(e.weight*100/rpt.total); weight > 1 {
+			attr = fmt.Sprintf(`%s weight=%d`, attr, weight)
+		}
+		if width := 1 + int(e.weight*5/rpt.total); width > 1 {
+			attr = fmt.Sprintf(`%s penwidth=%d`, attr, width)
+		}
+	}
+	arrow := "->"
+	if e.residual {
+		arrow = "..."
+	}
+	tooltip := fmt.Sprintf(`"%s %s %s (%s)"`,
+		e.src.info.prettyName(), arrow, e.dest.info.prettyName(), w)
+	attr = fmt.Sprintf(`%s tooltip=%s labeltooltip=%s`,
+		attr, tooltip, tooltip)
+
+	if e.residual {
+		attr = attr + ` style="dotted"`
+	}
+
+	if len(e.src.tags) > 0 {
+		// Separate children further if source has tags.
+		attr = attr + " minlen=2"
+	}
+	return fmt.Sprintf("N%d -> N%d [%s]", from, to, attr)
+}
+
+// dotNodelets generates the DOT boxes for the node tags.
+func dotNodelets(rpt *Report, rIndex int, n *node) (dot string) {
+	const maxNodelets = 4    // Number of nodelets for alphanumeric labels
+	const maxNumNodelets = 4 // Number of nodelets for numeric labels
+
+	var ts, nts tags
+	for _, t := range n.tags {
+		if t.unit == "" {
+			ts = append(ts, t)
+		} else {
+			nts = append(nts, t)
+		}
+	}
+
+	// Select the top maxNodelets alphanumeric labels by weight
+	sort.Sort(ts)
+	if len(ts) > maxNodelets {
+		ts = ts[:maxNodelets]
+	}
+	for i, t := range ts {
+		weight := rpt.formatValue(t.weight)
+		dot += fmt.Sprintf(`N%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", rIndex, i, t.name, weight)
+		dot += fmt.Sprintf(`N%d -> N%d_%d [label=" %s" weight=100 tooltip="\L" labeltooltip="\L"]`+"\n", rIndex, rIndex, i, weight)
+	}
+
+	// Collapse numeric labels into maxNumNodelets buckets, of the form:
+	// 1MB..2MB, 3MB..5MB, ...
+	nts = collapseTags(nts, maxNumNodelets)
+	sort.Sort(nts)
+	for i, t := range nts {
+		weight := rpt.formatValue(t.weight)
+		dot += fmt.Sprintf(`NN%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", rIndex, i, t.name, weight)
+		dot += fmt.Sprintf(`N%d -> NN%d_%d [label=" %s" weight=100 tooltip="\L" labeltooltip="\L"]`+"\n", rIndex, rIndex, i, weight)
+	}
+
+	return dot
+}
+
+// graph summarizes a performance profile into a format that is
+// suitable for visualization.
+type graph struct {
+	ns nodes
+}
+
+// nodes is an ordered collection of graph nodes.
+type nodes []*node
+
+// tags represent sample annotations
+type tags []*tag
+type tagMap map[string]*tag
+
+type tag struct {
+	name   string
+	unit   string // Describe the value, "" for non-numeric tags
+	value  int64
+	weight int64
+}
+
+func (t tags) Len() int      { return len(t) }
+func (t tags) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
+func (t tags) Less(i, j int) bool {
+	if t[i].weight == t[j].weight {
+		return t[i].name < t[j].name
+	}
+	return t[i].weight > t[j].weight
+}
+
+// node is an entry on a profiling report. It represents a unique
+// program location. It can include multiple names to represent
+// inlined functions.
+type node struct {
+	info nodeInfo // Information associated to this entry.
+
+	// values associated to this node.
+	// flat is exclusive to this node, cum includes all descendents.
+	flat, cum int64
+
+	// in and out contains the nodes immediately reaching or reached by this nodes.
+	in, out edgeMap
+
+	// tags provide additional information about subsets of a sample.
+	tags tagMap
+}
+
+type nodeInfo struct {
+	name              string
+	origName          string
+	address           uint64
+	file              string
+	startLine, lineno int
+	inline            bool
+	lowPriority       bool
+	objfile           string
+	parent            *node // Used only if creating a calltree
+}
+
+func (n *node) addTags(s *profile.Sample, weight int64) {
+	// Add a tag with all string labels
+	var labels []string
+	for key, vals := range s.Label {
+		for _, v := range vals {
+			labels = append(labels, key+":"+v)
+		}
+	}
+	if len(labels) > 0 {
+		sort.Strings(labels)
+		l := n.tags.findOrAddTag(strings.Join(labels, `\n`), "", 0)
+		l.weight += weight
+	}
+
+	for key, nvals := range s.NumLabel {
+		for _, v := range nvals {
+			label := scaledValueLabel(v, key, "auto")
+			l := n.tags.findOrAddTag(label, key, v)
+			l.weight += weight
+		}
+	}
+}
+
+func (m tagMap) findOrAddTag(label, unit string, value int64) *tag {
+	if l := m[label]; l != nil {
+		return l
+	}
+	l := &tag{
+		name:  label,
+		unit:  unit,
+		value: value,
+	}
+	m[label] = l
+	return l
+}
+
+// collapseTags reduces the number of entries in a tagMap by merging
+// adjacent nodes into ranges. It uses a greedy approach to merge
+// starting with the entries with the lowest weight.
+func collapseTags(ts tags, count int) tags {
+	if len(ts) <= count {
+		return ts
+	}
+
+	sort.Sort(ts)
+	tagGroups := make([]tags, count)
+	for i, t := range ts[:count] {
+		tagGroups[i] = tags{t}
+	}
+	for _, t := range ts[count:] {
+		g, d := 0, tagDistance(t, tagGroups[0][0])
+		for i := 1; i < count; i++ {
+			if nd := tagDistance(t, tagGroups[i][0]); nd < d {
+				g, d = i, nd
+			}
+		}
+		tagGroups[g] = append(tagGroups[g], t)
+	}
+
+	var nts tags
+	for _, g := range tagGroups {
+		l, w := tagGroupLabel(g)
+		nts = append(nts, &tag{
+			name:   l,
+			weight: w,
+		})
+	}
+	return nts
+}
+
+func tagDistance(t, u *tag) float64 {
+	v, _ := ScaleValue(u.value, u.unit, t.unit)
+	if v < float64(t.value) {
+		return float64(t.value) - v
+	}
+	return v - float64(t.value)
+}
+
+func tagGroupLabel(g tags) (string, int64) {
+	if len(g) == 1 {
+		t := g[0]
+		return scaledValueLabel(t.value, t.unit, "auto"), t.weight
+	}
+	min := g[0]
+	max := g[0]
+	w := min.weight
+	for _, t := range g[1:] {
+		if v, _ := ScaleValue(t.value, t.unit, min.unit); int64(v) < min.value {
+			min = t
+		}
+		if v, _ := ScaleValue(t.value, t.unit, max.unit); int64(v) > max.value {
+			max = t
+		}
+		w += t.weight
+	}
+	return scaledValueLabel(min.value, min.unit, "auto") + ".." +
+		scaledValueLabel(max.value, max.unit, "auto"), w
+}
+
+// sumNodes adds the flat and sum values on a report.
+func sumNodes(ns nodes) (flat int64, cum int64) {
+	for _, n := range ns {
+		flat += n.flat
+		cum += n.cum
+	}
+	return
+}
+
+type edgeMap map[*node]*edgeInfo
+
+// edgeInfo contains any attributes to be represented about edges in a graph/
+type edgeInfo struct {
+	src, dest *node
+	// The summary weight of the edge
+	weight int64
+	// residual edges connect nodes that were connected through a
+	// separate node, which has been removed from the report.
+	residual bool
+}
+
+// bumpWeight increases the weight of an edge. If there isn't such an
+// edge in the map one is created.
+func bumpWeight(from, to *node, w int64, residual bool) {
+	if from.out[to] != to.in[from] {
+		panic(fmt.Errorf("asymmetric edges %v %v", *from, *to))
+	}
+
+	if n := from.out[to]; n != nil {
+		n.weight += w
+		if n.residual && !residual {
+			n.residual = false
+		}
+		return
+	}
+
+	info := &edgeInfo{src: from, dest: to, weight: w, residual: residual}
+	from.out[to] = info
+	to.in[from] = info
+}
+
+// Output formats.
+const (
+	Proto = iota
+	Dot
+	Tags
+	Tree
+	Text
+	Raw
+	Dis
+	List
+	WebList
+	Callgrind
+)
+
+// Options are the formatting and filtering options used to generate a
+// profile.
+type Options struct {
+	OutputFormat int
+
+	CumSort        bool
+	CallTree       bool
+	PrintAddresses bool
+	DropNegative   bool
+	Ratio          float64
+
+	NodeCount    int
+	NodeFraction float64
+	EdgeFraction float64
+
+	SampleType string
+	SampleUnit string // Unit for the sample data from the profile.
+	OutputUnit string // Units for data formatting in report.
+
+	Symbol *regexp.Regexp // Symbols to include on disassembly report.
+}
+
+// newGraph summarizes performance data from a profile into a graph.
+func newGraph(rpt *Report) (g graph, err error) {
+	prof := rpt.prof
+	o := rpt.options
+
+	// Generate a tree for graphical output if requested.
+	buildTree := o.CallTree && o.OutputFormat == Dot
+
+	locations := make(map[uint64][]nodeInfo)
+	for _, l := range prof.Location {
+		locations[l.ID] = newLocInfo(l)
+	}
+
+	nm := make(nodeMap)
+	for _, sample := range prof.Sample {
+		if sample.Location == nil {
+			continue
+		}
+
+		// Construct list of node names for sample.
+		var stack []nodeInfo
+		for _, loc := range sample.Location {
+			id := loc.ID
+			stack = append(stack, locations[id]...)
+		}
+
+		// Upfront pass to update the parent chains, to prevent the
+		// merging of nodes with different parents.
+		if buildTree {
+			var nn *node
+			for i := len(stack); i > 0; i-- {
+				n := &stack[i-1]
+				n.parent = nn
+				nn = nm.findOrInsertNode(*n)
+			}
+		}
+
+		leaf := nm.findOrInsertNode(stack[0])
+		weight := rpt.sampleValue(sample)
+		leaf.addTags(sample, weight)
+
+		// Aggregate counter data.
+		leaf.flat += weight
+		seen := make(map[*node]bool)
+		var nn *node
+		for _, s := range stack {
+			n := nm.findOrInsertNode(s)
+			if !seen[n] {
+				seen[n] = true
+				n.cum += weight
+
+				if nn != nil {
+					bumpWeight(n, nn, weight, false)
+				}
+			}
+			nn = n
+		}
+	}
+
+	// Collect new nodes into a report.
+	ns := make(nodes, 0, len(nm))
+	for _, n := range nm {
+		if rpt.options.DropNegative && n.flat < 0 {
+			continue
+		}
+		ns = append(ns, n)
+	}
+
+	return graph{ns}, nil
+}
+
+// Create a slice of formatted names for a location.
+func newLocInfo(l *profile.Location) []nodeInfo {
+	var objfile string
+
+	if m := l.Mapping; m != nil {
+		objfile = m.File
+	}
+
+	if len(l.Line) == 0 {
+		return []nodeInfo{
+			{
+				address: l.Address,
+				objfile: objfile,
+			},
+		}
+	}
+	var info []nodeInfo
+	numInlineFrames := len(l.Line) - 1
+	for li, line := range l.Line {
+		ni := nodeInfo{
+			address: l.Address,
+			lineno:  int(line.Line),
+			inline:  li < numInlineFrames,
+			objfile: objfile,
+		}
+
+		if line.Function != nil {
+			ni.name = line.Function.Name
+			ni.origName = line.Function.SystemName
+			ni.file = line.Function.Filename
+			ni.startLine = int(line.Function.StartLine)
+		}
+
+		info = append(info, ni)
+	}
+	return info
+}
+
+// nodeMap maps from a node info struct to a node. It is used to merge
+// report entries with the same info.
+type nodeMap map[nodeInfo]*node
+
+func (m nodeMap) findOrInsertNode(info nodeInfo) *node {
+	rr := m[info]
+	if rr == nil {
+		rr = &node{
+			info: info,
+			in:   make(edgeMap),
+			out:  make(edgeMap),
+			tags: make(map[string]*tag),
+		}
+		m[info] = rr
+	}
+	return rr
+}
+
+// preprocess does any required filtering/sorting according to the
+// report options. Returns the mapping from each node to any nodes
+// removed by path compression and statistics on the nodes/edges removed.
+func (g *graph) preprocess(rpt *Report) (origCount, droppedNodes, droppedEdges int) {
+	o := rpt.options
+
+	// Compute total weight of current set of nodes.
+	// This is <= rpt.total because of node filtering.
+	var totalValue int64
+	for _, n := range g.ns {
+		totalValue += n.flat
+	}
+
+	// Remove nodes with value <= total*nodeFraction
+	if nodeFraction := o.NodeFraction; nodeFraction > 0 {
+		var removed nodes
+		minValue := int64(float64(totalValue) * nodeFraction)
+		kept := make(nodes, 0, len(g.ns))
+		for _, n := range g.ns {
+			if n.cum < minValue {
+				removed = append(removed, n)
+			} else {
+				kept = append(kept, n)
+				tagsKept := make(map[string]*tag)
+				for s, t := range n.tags {
+					if t.weight >= minValue {
+						tagsKept[s] = t
+					}
+				}
+				n.tags = tagsKept
+			}
+		}
+		droppedNodes = len(removed)
+		removeNodes(removed, false, false)
+		g.ns = kept
+	}
+
+	// Remove edges below minimum frequency.
+	if edgeFraction := o.EdgeFraction; edgeFraction > 0 {
+		minEdge := int64(float64(totalValue) * edgeFraction)
+		for _, n := range g.ns {
+			for src, e := range n.in {
+				if e.weight < minEdge {
+					delete(n.in, src)
+					delete(src.out, n)
+					droppedEdges++
+				}
+			}
+		}
+	}
+
+	sortOrder := flatName
+	if o.CumSort {
+		// Force cum sorting for graph output, to preserve connectivity.
+		sortOrder = cumName
+	}
+
+	// Nodes that have flat==0 and a single in/out do not provide much
+	// information. Give them first chance to be removed. Do not consider edges
+	// from/to nodes that are expected to be removed.
+	maxNodes := o.NodeCount
+	if o.OutputFormat == Dot {
+		if maxNodes > 0 && maxNodes < len(g.ns) {
+			sortOrder = cumName
+			g.ns.sort(cumName)
+			cumCutoff := g.ns[maxNodes].cum
+			for _, n := range g.ns {
+				if n.flat == 0 {
+					if count := countEdges(n.out, cumCutoff); count > 1 {
+						continue
+					}
+					if count := countEdges(n.in, cumCutoff); count != 1 {
+						continue
+					}
+					n.info.lowPriority = true
+				}
+			}
+		}
+	}
+
+	g.ns.sort(sortOrder)
+	if maxNodes > 0 {
+		origCount = len(g.ns)
+		for index, nodes := 0, 0; index < len(g.ns); index++ {
+			nodes++
+			// For DOT output, count the tags as nodes since we will draw
+			// boxes for them.
+			if o.OutputFormat == Dot {
+				nodes += len(g.ns[index].tags)
+			}
+			if nodes > maxNodes {
+				// Trim to the top n nodes. Create dotted edges to bridge any
+				// broken connections.
+				removeNodes(g.ns[index:], true, true)
+				g.ns = g.ns[:index]
+				break
+			}
+		}
+	}
+	removeRedundantEdges(g.ns)
+
+	// Select best unit for profile output.
+	// Find the appropriate units for the smallest non-zero sample
+	if o.OutputUnit == "minimum" && len(g.ns) > 0 {
+		var maxValue, minValue int64
+
+		for _, n := range g.ns {
+			if n.flat > 0 && (minValue == 0 || n.flat < minValue) {
+				minValue = n.flat
+			}
+			if n.cum > maxValue {
+				maxValue = n.cum
+			}
+		}
+		if r := o.Ratio; r > 0 && r != 1 {
+			minValue = int64(float64(minValue) * r)
+			maxValue = int64(float64(maxValue) * r)
+		}
+
+		_, minUnit := ScaleValue(minValue, o.SampleUnit, "minimum")
+		_, maxUnit := ScaleValue(maxValue, o.SampleUnit, "minimum")
+
+		unit := minUnit
+		if minUnit != maxUnit && minValue*100 < maxValue && o.OutputFormat != Callgrind {
+			// Minimum and maximum values have different units. Scale
+			// minimum by 100 to use larger units, allowing minimum value to
+			// be scaled down to 0.01, except for callgrind reports since
+			// they can only represent integer values.
+			_, unit = ScaleValue(100*minValue, o.SampleUnit, "minimum")
+		}
+
+		if unit != "" {
+			o.OutputUnit = unit
+		} else {
+			o.OutputUnit = o.SampleUnit
+		}
+	}
+	return
+}
+
+// countEdges counts the number of edges below the specified cutoff.
+func countEdges(el edgeMap, cutoff int64) int {
+	count := 0
+	for _, e := range el {
+		if e.weight > cutoff {
+			count++
+		}
+	}
+	return count
+}
+
+// removeNodes removes nodes from a report, optionally bridging
+// connections between in/out edges and spreading out their weights
+// proportionally. residual marks new bridge edges as residual
+// (dotted).
+func removeNodes(toRemove nodes, bridge, residual bool) {
+	for _, n := range toRemove {
+		for ei := range n.in {
+			delete(ei.out, n)
+		}
+		if bridge {
+			for ei, wi := range n.in {
+				for eo, wo := range n.out {
+					var weight int64
+					if n.cum != 0 {
+						weight = int64(float64(wo.weight) * (float64(wi.weight) / float64(n.cum)))
+					}
+					bumpWeight(ei, eo, weight, residual)
+				}
+			}
+		}
+		for eo := range n.out {
+			delete(eo.in, n)
+		}
+	}
+}
+
+// removeRedundantEdges removes residual edges if the destination can
+// be reached through another path. This is done to simplify the graph
+// while preserving connectivity.
+func removeRedundantEdges(ns nodes) {
+	// Walk the nodes and outgoing edges in reverse order to prefer
+	// removing edges with the lowest weight.
+	for i := len(ns); i > 0; i-- {
+		n := ns[i-1]
+		in := sortedEdges(n.in)
+		for j := len(in); j > 0; j-- {
+			if e := in[j-1]; e.residual && isRedundant(e) {
+				delete(e.src.out, e.dest)
+				delete(e.dest.in, e.src)
+			}
+		}
+	}
+}
+
+// isRedundant determines if an edge can be removed without impacting
+// connectivity of the whole graph. This is implemented by checking if the
+// nodes have a common ancestor after removing the edge.
+func isRedundant(e *edgeInfo) bool {
+	destPred := predecessors(e, e.dest)
+	if len(destPred) == 1 {
+		return false
+	}
+	srcPred := predecessors(e, e.src)
+
+	for n := range srcPred {
+		if destPred[n] && n != e.dest {
+			return true
+		}
+	}
+	return false
+}
+
+// predecessors collects all the predecessors to node n, excluding edge e.
+func predecessors(e *edgeInfo, n *node) map[*node]bool {
+	seen := map[*node]bool{n: true}
+	queue := []*node{n}
+	for len(queue) > 0 {
+		n := queue[0]
+		queue = queue[1:]
+		for _, ie := range n.in {
+			if e == ie || seen[ie.src] {
+				continue
+			}
+			seen[ie.src] = true
+			queue = append(queue, ie.src)
+		}
+	}
+	return seen
+}
+
+// nodeSorter is a mechanism used to allow a report to be sorted
+// in different ways.
+type nodeSorter struct {
+	rs   nodes
+	less func(i, j int) bool
+}
+
+func (s nodeSorter) Len() int           { return len(s.rs) }
+func (s nodeSorter) Swap(i, j int)      { s.rs[i], s.rs[j] = s.rs[j], s.rs[i] }
+func (s nodeSorter) Less(i, j int) bool { return s.less(i, j) }
+
+type nodeOrder int
+
+const (
+	flatName nodeOrder = iota
+	flatCumName
+	cumName
+	nameOrder
+	fileOrder
+	addressOrder
+)
+
+// sort reorders the entries in a report based on the specified
+// ordering criteria. The result is sorted in decreasing order for
+// numeric quantities, alphabetically for text, and increasing for
+// addresses.
+func (ns nodes) sort(o nodeOrder) error {
+	var s nodeSorter
+
+	switch o {
+	case flatName:
+		s = nodeSorter{ns,
+			func(i, j int) bool {
+				if iv, jv := ns[i].flat, ns[j].flat; iv != jv {
+					return iv > jv
+				}
+				if ns[i].info.prettyName() != ns[j].info.prettyName() {
+					return ns[i].info.prettyName() < ns[j].info.prettyName()
+				}
+				iv, jv := ns[i].cum, ns[j].cum
+				return iv > jv
+			},
+		}
+	case flatCumName:
+		s = nodeSorter{ns,
+			func(i, j int) bool {
+				if iv, jv := ns[i].flat, ns[j].flat; iv != jv {
+					return iv > jv
+				}
+				if iv, jv := ns[i].cum, ns[j].cum; iv != jv {
+					return iv > jv
+				}
+				return ns[i].info.prettyName() < ns[j].info.prettyName()
+			},
+		}
+	case cumName:
+		s = nodeSorter{ns,
+			func(i, j int) bool {
+				if ns[i].info.lowPriority != ns[j].info.lowPriority {
+					return ns[j].info.lowPriority
+				}
+				if iv, jv := ns[i].cum, ns[j].cum; iv != jv {
+					return iv > jv
+				}
+				if ns[i].info.prettyName() != ns[j].info.prettyName() {
+					return ns[i].info.prettyName() < ns[j].info.prettyName()
+				}
+				iv, jv := ns[i].flat, ns[j].flat
+				return iv > jv
+			},
+		}
+	case nameOrder:
+		s = nodeSorter{ns,
+			func(i, j int) bool {
+				return ns[i].info.name < ns[j].info.name
+			},
+		}
+	case fileOrder:
+		s = nodeSorter{ns,
+			func(i, j int) bool {
+				return ns[i].info.file < ns[j].info.file
+			},
+		}
+	case addressOrder:
+		s = nodeSorter{ns,
+			func(i, j int) bool {
+				return ns[i].info.address < ns[j].info.address
+			},
+		}
+	default:
+		return fmt.Errorf("report: unrecognized sort ordering: %d", o)
+	}
+	sort.Sort(s)
+	return nil
+}
+
+type edgeList []*edgeInfo
+
+// sortedEdges return a slice of the edges in the map, sorted for
+// visualization. The sort order is first based on the edge weight
+// (higher-to-lower) and then by the node names to avoid flakiness.
+func sortedEdges(edges map[*node]*edgeInfo) edgeList {
+	el := make(edgeList, 0, len(edges))
+	for _, w := range edges {
+		el = append(el, w)
+	}
+
+	sort.Sort(el)
+	return el
+}
+
+func (el edgeList) Len() int {
+	return len(el)
+}
+
+func (el edgeList) Less(i, j int) bool {
+	if el[i].weight != el[j].weight {
+		return el[i].weight > el[j].weight
+	}
+
+	from1 := el[i].src.info.prettyName()
+	from2 := el[j].src.info.prettyName()
+	if from1 != from2 {
+		return from1 < from2
+	}
+
+	to1 := el[i].dest.info.prettyName()
+	to2 := el[j].dest.info.prettyName()
+
+	return to1 < to2
+}
+
+func (el edgeList) Swap(i, j int) {
+	el[i], el[j] = el[j], el[i]
+}
+
+func (el edgeList) sum() int64 {
+	var ret int64
+	for _, e := range el {
+		ret += e.weight
+	}
+	return ret
+}
+
+// ScaleValue reformats a value from a unit to a different unit.
+func ScaleValue(value int64, fromUnit, toUnit string) (sv float64, su string) {
+	// Avoid infinite recursion on overflow.
+	if value < 0 && -value > 0 {
+		v, u := ScaleValue(-value, fromUnit, toUnit)
+		return -v, u
+	}
+	if m, u, ok := memoryLabel(value, fromUnit, toUnit); ok {
+		return m, u
+	}
+	if t, u, ok := timeLabel(value, fromUnit, toUnit); ok {
+		return t, u
+	}
+	// Skip non-interesting units.
+	switch toUnit {
+	case "count", "sample", "unit", "minimum":
+		return float64(value), ""
+	default:
+		return float64(value), toUnit
+	}
+}
+
+func scaledValueLabel(value int64, fromUnit, toUnit string) string {
+	v, u := ScaleValue(value, fromUnit, toUnit)
+
+	sv := strings.TrimSuffix(fmt.Sprintf("%.2f", v), ".00")
+	if sv == "0" || sv == "-0" {
+		return "0"
+	}
+	return sv + u
+}
+
+func memoryLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok bool) {
+	fromUnit = strings.TrimSuffix(strings.ToLower(fromUnit), "s")
+	toUnit = strings.TrimSuffix(strings.ToLower(toUnit), "s")
+
+	switch fromUnit {
+	case "byte", "b":
+	case "kilobyte", "kb":
+		value *= 1024
+	case "megabyte", "mb":
+		value *= 1024 * 1024
+	case "gigabyte", "gb":
+		value *= 1024 * 1024 * 1024
+	default:
+		return 0, "", false
+	}
+
+	if toUnit == "minimum" || toUnit == "auto" {
+		switch {
+		case value < 1024:
+			toUnit = "b"
+		case value < 1024*1024:
+			toUnit = "kb"
+		case value < 1024*1024*1024:
+			toUnit = "mb"
+		default:
+			toUnit = "gb"
+		}
+	}
+
+	var output float64
+	switch toUnit {
+	default:
+		output, toUnit = float64(value), "B"
+	case "kb", "kbyte", "kilobyte":
+		output, toUnit = float64(value)/1024, "kB"
+	case "mb", "mbyte", "megabyte":
+		output, toUnit = float64(value)/(1024*1024), "MB"
+	case "gb", "gbyte", "gigabyte":
+		output, toUnit = float64(value)/(1024*1024*1024), "GB"
+	}
+	return output, toUnit, true
+}
+
+func timeLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok bool) {
+	fromUnit = strings.ToLower(fromUnit)
+	if len(fromUnit) > 2 {
+		fromUnit = strings.TrimSuffix(fromUnit, "s")
+	}
+
+	toUnit = strings.ToLower(toUnit)
+	if len(toUnit) > 2 {
+		toUnit = strings.TrimSuffix(toUnit, "s")
+	}
+
+	var d time.Duration
+	switch fromUnit {
+	case "nanosecond", "ns":
+		d = time.Duration(value) * time.Nanosecond
+	case "microsecond":
+		d = time.Duration(value) * time.Microsecond
+	case "millisecond", "ms":
+		d = time.Duration(value) * time.Millisecond
+	case "second", "sec":
+		d = time.Duration(value) * time.Second
+	case "cycle":
+		return float64(value), "", true
+	default:
+		return 0, "", false
+	}
+
+	if toUnit == "minimum" || toUnit == "auto" {
+		switch {
+		case d < 1*time.Microsecond:
+			toUnit = "ns"
+		case d < 1*time.Millisecond:
+			toUnit = "us"
+		case d < 1*time.Second:
+			toUnit = "ms"
+		case d < 1*time.Minute:
+			toUnit = "sec"
+		case d < 1*time.Hour:
+			toUnit = "min"
+		case d < 24*time.Hour:
+			toUnit = "hour"
+		case d < 15*24*time.Hour:
+			toUnit = "day"
+		case d < 120*24*time.Hour:
+			toUnit = "week"
+		default:
+			toUnit = "year"
+		}
+	}
+
+	var output float64
+	dd := float64(d)
+	switch toUnit {
+	case "ns", "nanosecond":
+		output, toUnit = dd/float64(time.Nanosecond), "ns"
+	case "us", "microsecond":
+		output, toUnit = dd/float64(time.Microsecond), "us"
+	case "ms", "millisecond":
+		output, toUnit = dd/float64(time.Millisecond), "ms"
+	case "min", "minute":
+		output, toUnit = dd/float64(time.Minute), "mins"
+	case "hour", "hr":
+		output, toUnit = dd/float64(time.Hour), "hrs"
+	case "day":
+		output, toUnit = dd/float64(24*time.Hour), "days"
+	case "week", "wk":
+		output, toUnit = dd/float64(7*24*time.Hour), "wks"
+	case "year", "yr":
+		output, toUnit = dd/float64(365*7*24*time.Hour), "yrs"
+	default:
+		fallthrough
+	case "sec", "second", "s":
+		output, toUnit = dd/float64(time.Second), "s"
+	}
+	return output, toUnit, true
+}
+
+// prettyName determines the printable name to be used for a node.
+func (info *nodeInfo) prettyName() string {
+	var name string
+	if info.address != 0 {
+		name = fmt.Sprintf("%016x", info.address)
+	}
+
+	if info.name != "" {
+		name = name + " " + info.name
+	}
+
+	if info.file != "" {
+		name += " " + trimPath(info.file)
+		if info.lineno != 0 {
+			name += fmt.Sprintf(":%d", info.lineno)
+		}
+	}
+
+	if info.inline {
+		name = name + " (inline)"
+	}
+
+	if name = strings.TrimSpace(name); name == "" && info.objfile != "" {
+		name = "[" + filepath.Base(info.objfile) + "]"
+	}
+	return name
+}
+
+// New builds a new report indexing the sample values interpreting the
+// samples with the provided function.
+func New(prof *profile.Profile, options Options, value func(s *profile.Sample) int64, unit string) *Report {
+	o := &options
+	if o.SampleUnit == "" {
+		o.SampleUnit = unit
+	}
+	format := func(v int64) string {
+		if r := o.Ratio; r > 0 && r != 1 {
+			fv := float64(v) * r
+			v = int64(fv)
+		}
+		return scaledValueLabel(v, o.SampleUnit, o.OutputUnit)
+	}
+	return &Report{prof, computeTotal(prof, value), o, value, format}
+}
+
+// NewDefault builds a new report indexing the sample values with the
+// last value available.
+func NewDefault(prof *profile.Profile, options Options) *Report {
+	index := len(prof.SampleType) - 1
+	o := &options
+	if o.SampleUnit == "" {
+		o.SampleUnit = strings.ToLower(prof.SampleType[index].Unit)
+	}
+	value := func(s *profile.Sample) int64 {
+		return s.Value[index]
+	}
+	format := func(v int64) string {
+		if r := o.Ratio; r > 0 && r != 1 {
+			fv := float64(v) * r
+			v = int64(fv)
+		}
+		return scaledValueLabel(v, o.SampleUnit, o.OutputUnit)
+	}
+	return &Report{prof, computeTotal(prof, value), o, value, format}
+}
+
+func computeTotal(prof *profile.Profile, value func(s *profile.Sample) int64) int64 {
+	var ret int64
+	for _, sample := range prof.Sample {
+		ret += value(sample)
+	}
+	return ret
+}
+
+// Report contains the data and associated routines to extract a
+// report from a profile.
+type Report struct {
+	prof        *profile.Profile
+	total       int64
+	options     *Options
+	sampleValue func(*profile.Sample) int64
+	formatValue func(int64) string
+}
diff --git a/src/cmd/pprof/internal/report/source.go b/src/cmd/pprof/internal/report/source.go
new file mode 100644
index 0000000..7ab7e38
--- /dev/null
+++ b/src/cmd/pprof/internal/report/source.go
@@ -0,0 +1,454 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package report
+
+// This file contains routines related to the generation of annotated
+// source listings.
+
+import (
+	"bufio"
+	"fmt"
+	"html/template"
+	"io"
+	"os"
+	"path/filepath"
+	"sort"
+	"strconv"
+	"strings"
+
+	"cmd/pprof/internal/plugin"
+)
+
+// printSource prints an annotated source listing, include all
+// functions with samples that match the regexp rpt.options.symbol.
+// The sources are sorted by function name and then by filename to
+// eliminate potential nondeterminism.
+func printSource(w io.Writer, rpt *Report) error {
+	o := rpt.options
+	g, err := newGraph(rpt)
+	if err != nil {
+		return err
+	}
+
+	// Identify all the functions that match the regexp provided.
+	// Group nodes for each matching function.
+	var functions nodes
+	functionNodes := make(map[string]nodes)
+	for _, n := range g.ns {
+		if !o.Symbol.MatchString(n.info.name) {
+			continue
+		}
+		if functionNodes[n.info.name] == nil {
+			functions = append(functions, n)
+		}
+		functionNodes[n.info.name] = append(functionNodes[n.info.name], n)
+	}
+	functions.sort(nameOrder)
+
+	fmt.Fprintf(w, "Total: %s\n", rpt.formatValue(rpt.total))
+	for _, fn := range functions {
+		name := fn.info.name
+
+		// Identify all the source files associated to this function.
+		// Group nodes for each source file.
+		var sourceFiles nodes
+		fileNodes := make(map[string]nodes)
+		for _, n := range functionNodes[name] {
+			if n.info.file == "" {
+				continue
+			}
+			if fileNodes[n.info.file] == nil {
+				sourceFiles = append(sourceFiles, n)
+			}
+			fileNodes[n.info.file] = append(fileNodes[n.info.file], n)
+		}
+
+		if len(sourceFiles) == 0 {
+			fmt.Printf("No source information for %s\n", name)
+			continue
+		}
+
+		sourceFiles.sort(fileOrder)
+
+		// Print each file associated with this function.
+		for _, fl := range sourceFiles {
+			filename := fl.info.file
+			fns := fileNodes[filename]
+			flatSum, cumSum := sumNodes(fns)
+
+			fnodes, path, err := getFunctionSource(name, filename, fns, 0, 0)
+			fmt.Fprintf(w, "ROUTINE ======================== %s in %s\n", name, path)
+			fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n",
+				rpt.formatValue(flatSum), rpt.formatValue(cumSum),
+				percentage(cumSum, rpt.total))
+
+			if err != nil {
+				fmt.Fprintf(w, " Error: %v\n", err)
+				continue
+			}
+
+			for _, fn := range fnodes {
+				fmt.Fprintf(w, "%10s %10s %6d:%s\n", valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt), fn.info.lineno, fn.info.name)
+			}
+		}
+	}
+	return nil
+}
+
+// printWebSource prints an annotated source listing, include all
+// functions with samples that match the regexp rpt.options.symbol.
+func printWebSource(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
+	o := rpt.options
+	g, err := newGraph(rpt)
+	if err != nil {
+		return err
+	}
+
+	// If the regexp source can be parsed as an address, also match
+	// functions that land on that address.
+	var address *uint64
+	if hex, err := strconv.ParseUint(o.Symbol.String(), 0, 64); err == nil {
+		address = &hex
+	}
+
+	// Extract interesting symbols from binary files in the profile and
+	// classify samples per symbol.
+	symbols := symbolsFromBinaries(rpt.prof, g, o.Symbol, address, obj)
+	symNodes := nodesPerSymbol(g.ns, symbols)
+
+	// Sort symbols for printing.
+	var syms objSymbols
+	for s := range symNodes {
+		syms = append(syms, s)
+	}
+	sort.Sort(syms)
+
+	if len(syms) == 0 {
+		return fmt.Errorf("no samples found on routines matching: %s", o.Symbol.String())
+	}
+
+	printHeader(w, rpt)
+	for _, s := range syms {
+		name := s.sym.Name[0]
+		// Identify sources associated to a symbol by examining
+		// symbol samples. Classify samples per source file.
+		var sourceFiles nodes
+		fileNodes := make(map[string]nodes)
+		for _, n := range symNodes[s] {
+			if n.info.file == "" {
+				continue
+			}
+			if fileNodes[n.info.file] == nil {
+				sourceFiles = append(sourceFiles, n)
+			}
+			fileNodes[n.info.file] = append(fileNodes[n.info.file], n)
+		}
+
+		if len(sourceFiles) == 0 {
+			fmt.Printf("No source information for %s\n", name)
+			continue
+		}
+
+		sourceFiles.sort(fileOrder)
+
+		// Print each file associated with this function.
+		for _, fl := range sourceFiles {
+			filename := fl.info.file
+			fns := fileNodes[filename]
+
+			asm := assemblyPerSourceLine(symbols, fns, filename, obj)
+			start, end := sourceCoordinates(asm)
+
+			fnodes, path, err := getFunctionSource(name, filename, fns, start, end)
+			if err != nil {
+				fnodes, path = getMissingFunctionSource(filename, asm, start, end)
+			}
+
+			flatSum, cumSum := sumNodes(fnodes)
+			printFunctionHeader(w, name, path, flatSum, cumSum, rpt)
+			for _, fn := range fnodes {
+				printFunctionSourceLine(w, fn, asm[fn.info.lineno], rpt)
+			}
+			printFunctionClosing(w)
+		}
+	}
+	printPageClosing(w)
+	return nil
+}
+
+// sourceCoordinates returns the lowest and highest line numbers from
+// a set of assembly statements.
+func sourceCoordinates(asm map[int]nodes) (start, end int) {
+	for l := range asm {
+		if start == 0 || l < start {
+			start = l
+		}
+		if end == 0 || l > end {
+			end = l
+		}
+	}
+	return start, end
+}
+
+// assemblyPerSourceLine disassembles the binary containing a symbol
+// and classifies the assembly instructions according to its
+// corresponding source line, annotating them with a set of samples.
+func assemblyPerSourceLine(objSyms []*objSymbol, rs nodes, src string, obj plugin.ObjTool) map[int]nodes {
+	assembly := make(map[int]nodes)
+	// Identify symbol to use for this collection of samples.
+	o := findMatchingSymbol(objSyms, rs)
+	if o == nil {
+		return assembly
+	}
+
+	// Extract assembly for matched symbol
+	insns, err := obj.Disasm(o.sym.File, o.sym.Start, o.sym.End)
+	if err != nil {
+		return assembly
+	}
+
+	srcBase := filepath.Base(src)
+	anodes := annotateAssembly(insns, rs, o.base)
+	var lineno = 0
+	for _, an := range anodes {
+		if filepath.Base(an.info.file) == srcBase {
+			lineno = an.info.lineno
+		}
+		if lineno != 0 {
+			assembly[lineno] = append(assembly[lineno], an)
+		}
+	}
+
+	return assembly
+}
+
+// findMatchingSymbol looks for the symbol that corresponds to a set
+// of samples, by comparing their addresses.
+func findMatchingSymbol(objSyms []*objSymbol, ns nodes) *objSymbol {
+	for _, n := range ns {
+		for _, o := range objSyms {
+			if filepath.Base(o.sym.File) == n.info.objfile &&
+				o.sym.Start <= n.info.address-o.base &&
+				n.info.address-o.base <= o.sym.End {
+				return o
+			}
+		}
+	}
+	return nil
+}
+
+// printHeader prints the page header for a weblist report.
+func printHeader(w io.Writer, rpt *Report) {
+	fmt.Fprintln(w, weblistPageHeader)
+
+	var labels []string
+	for _, l := range legendLabels(rpt) {
+		labels = append(labels, template.HTMLEscapeString(l))
+	}
+
+	fmt.Fprintf(w, `<div class="legend">%s<br>Total: %s</div>`,
+		strings.Join(labels, "<br>\n"),
+		rpt.formatValue(rpt.total),
+	)
+}
+
+// printFunctionHeader prints a function header for a weblist report.
+func printFunctionHeader(w io.Writer, name, path string, flatSum, cumSum int64, rpt *Report) {
+	fmt.Fprintf(w, `<h1>%s</h1>%s
+<pre onClick="pprof_toggle_asm(event)">
+  Total:  %10s %10s (flat, cum) %s
+`,
+		template.HTMLEscapeString(name), template.HTMLEscapeString(path),
+		rpt.formatValue(flatSum), rpt.formatValue(cumSum),
+		percentage(cumSum, rpt.total))
+}
+
+// printFunctionSourceLine prints a source line and the corresponding assembly.
+func printFunctionSourceLine(w io.Writer, fn *node, assembly nodes, rpt *Report) {
+	if len(assembly) == 0 {
+		fmt.Fprintf(w,
+			"<span class=line> %6d</span> <span class=nop>  %10s %10s %s </span>\n",
+			fn.info.lineno,
+			valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt),
+			template.HTMLEscapeString(fn.info.name))
+		return
+	}
+
+	fmt.Fprintf(w,
+		"<span class=line> %6d</span> <span class=deadsrc>  %10s %10s %s </span>",
+		fn.info.lineno,
+		valueOrDot(fn.flat, rpt), valueOrDot(fn.cum, rpt),
+		template.HTMLEscapeString(fn.info.name))
+	fmt.Fprint(w, "<span class=asm>")
+	for _, an := range assembly {
+		var fileline string
+		class := "disasmloc"
+		if an.info.file != "" {
+			fileline = fmt.Sprintf("%s:%d", template.HTMLEscapeString(an.info.file), an.info.lineno)
+			if an.info.lineno != fn.info.lineno {
+				class = "unimportant"
+			}
+		}
+		fmt.Fprintf(w, " %8s %10s %10s %8x: %-48s <span class=%s>%s</span>\n", "",
+			valueOrDot(an.flat, rpt), valueOrDot(an.cum, rpt),
+			an.info.address,
+			template.HTMLEscapeString(an.info.name),
+			class,
+			template.HTMLEscapeString(fileline))
+	}
+	fmt.Fprintln(w, "</span>")
+}
+
+// printFunctionClosing prints the end of a function in a weblist report.
+func printFunctionClosing(w io.Writer) {
+	fmt.Fprintln(w, "</pre>")
+}
+
+// printPageClosing prints the end of the page in a weblist report.
+func printPageClosing(w io.Writer) {
+	fmt.Fprintln(w, weblistPageClosing)
+}
+
+// getFunctionSource collects the sources of a function from a source
+// file and annotates it with the samples in fns. Returns the sources
+// as nodes, using the info.name field to hold the source code.
+func getFunctionSource(fun, file string, fns nodes, start, end int) (nodes, string, error) {
+	f, file, err := adjustSourcePath(file)
+	if err != nil {
+		return nil, file, err
+	}
+
+	lineNodes := make(map[int]nodes)
+
+	// Collect source coordinates from profile.
+	const margin = 5 // Lines before first/after last sample.
+	if start == 0 {
+		if fns[0].info.startLine != 0 {
+			start = fns[0].info.startLine
+		} else {
+			start = fns[0].info.lineno - margin
+		}
+	} else {
+		start -= margin
+	}
+	if end == 0 {
+		end = fns[0].info.lineno
+	}
+	end += margin
+	for _, n := range fns {
+		lineno := n.info.lineno
+		nodeStart := n.info.startLine
+		if nodeStart == 0 {
+			nodeStart = lineno - margin
+		}
+		nodeEnd := lineno + margin
+		if nodeStart < start {
+			start = nodeStart
+		} else if nodeEnd > end {
+			end = nodeEnd
+		}
+		lineNodes[lineno] = append(lineNodes[lineno], n)
+	}
+
+	var src nodes
+	buf := bufio.NewReader(f)
+	lineno := 1
+	for {
+		line, err := buf.ReadString('\n')
+		if err != nil {
+			if err != io.EOF {
+				return nil, file, err
+			}
+			if line == "" {
+				// end was at or past EOF; that's okay
+				break
+			}
+		}
+		if lineno >= start {
+			flat, cum := sumNodes(lineNodes[lineno])
+
+			src = append(src, &node{
+				info: nodeInfo{
+					name:   strings.TrimRight(line, "\n"),
+					lineno: lineno,
+				},
+				flat: flat,
+				cum:  cum,
+			})
+		}
+		lineno++
+		if lineno > end {
+			break
+		}
+	}
+	return src, file, nil
+}
+
+// getMissingFunctionSource creates a dummy function body to point to
+// the source file and annotates it with the samples in asm.
+func getMissingFunctionSource(filename string, asm map[int]nodes, start, end int) (nodes, string) {
+	var fnodes nodes
+	for i := start; i <= end; i++ {
+		lrs := asm[i]
+		if len(lrs) == 0 {
+			continue
+		}
+		flat, cum := sumNodes(lrs)
+		fnodes = append(fnodes, &node{
+			info: nodeInfo{
+				name:   "???",
+				lineno: i,
+			},
+			flat: flat,
+			cum:  cum,
+		})
+	}
+	return fnodes, filename
+}
+
+// adjustSourcePath adjusts the path for a source file by trimming
+// known prefixes and searching for the file on all parents of the
+// current working dir.
+func adjustSourcePath(path string) (*os.File, string, error) {
+	path = trimPath(path)
+	f, err := os.Open(path)
+	if err == nil {
+		return f, path, nil
+	}
+
+	if dir, wderr := os.Getwd(); wderr == nil {
+		for {
+			parent := filepath.Dir(dir)
+			if parent == dir {
+				break
+			}
+			if f, err := os.Open(filepath.Join(parent, path)); err == nil {
+				return f, filepath.Join(parent, path), nil
+			}
+
+			dir = parent
+		}
+	}
+
+	return nil, path, err
+}
+
+// trimPath cleans up a path by removing prefixes that are commonly
+// found on profiles.
+func trimPath(path string) string {
+	basePaths := []string{
+		"/proc/self/cwd/./",
+		"/proc/self/cwd/",
+	}
+
+	sPath := filepath.ToSlash(path)
+
+	for _, base := range basePaths {
+		if strings.HasPrefix(sPath, base) {
+			return filepath.FromSlash(sPath[len(base):])
+		}
+	}
+	return path
+}
diff --git a/src/cmd/internal/pprof/report/source_html.go b/src/cmd/pprof/internal/report/source_html.go
similarity index 100%
rename from src/cmd/internal/pprof/report/source_html.go
rename to src/cmd/pprof/internal/report/source_html.go
diff --git a/src/cmd/internal/pprof/svg/svg.go b/src/cmd/pprof/internal/svg/svg.go
similarity index 100%
rename from src/cmd/internal/pprof/svg/svg.go
rename to src/cmd/pprof/internal/svg/svg.go
diff --git a/src/cmd/internal/pprof/svg/svgpan.go b/src/cmd/pprof/internal/svg/svgpan.go
similarity index 100%
rename from src/cmd/internal/pprof/svg/svgpan.go
rename to src/cmd/pprof/internal/svg/svgpan.go
diff --git a/src/cmd/pprof/internal/symbolizer/symbolizer.go b/src/cmd/pprof/internal/symbolizer/symbolizer.go
new file mode 100644
index 0000000..06a3976
--- /dev/null
+++ b/src/cmd/pprof/internal/symbolizer/symbolizer.go
@@ -0,0 +1,195 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package symbolizer provides a routine to populate a profile with
+// symbol, file and line number information. It relies on the
+// addr2liner and demangler packages to do the actual work.
+package symbolizer
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"cmd/pprof/internal/plugin"
+	"internal/pprof/profile"
+)
+
+// Symbolize adds symbol and line number information to all locations
+// in a profile. mode enables some options to control
+// symbolization. Currently only recognizes "force", which causes it
+// to overwrite any existing data.
+func Symbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
+	force := false
+	// Disable some mechanisms based on mode string.
+	for _, o := range strings.Split(strings.ToLower(mode), ":") {
+		switch o {
+		case "force":
+			force = true
+		default:
+		}
+	}
+
+	if len(prof.Mapping) == 0 {
+		return fmt.Errorf("no known mappings")
+	}
+
+	mt, err := newMapping(prof, obj, ui, force)
+	if err != nil {
+		return err
+	}
+	defer mt.close()
+
+	functions := make(map[profile.Function]*profile.Function)
+	for _, l := range mt.prof.Location {
+		m := l.Mapping
+		segment := mt.segments[m]
+		if segment == nil {
+			// Nothing to do
+			continue
+		}
+
+		stack, err := segment.SourceLine(l.Address)
+		if err != nil || len(stack) == 0 {
+			// No answers from addr2line
+			continue
+		}
+
+		l.Line = make([]profile.Line, len(stack))
+		for i, frame := range stack {
+			if frame.Func != "" {
+				m.HasFunctions = true
+			}
+			if frame.File != "" {
+				m.HasFilenames = true
+			}
+			if frame.Line != 0 {
+				m.HasLineNumbers = true
+			}
+			f := &profile.Function{
+				Name:       frame.Func,
+				SystemName: frame.Func,
+				Filename:   frame.File,
+			}
+			if fp := functions[*f]; fp != nil {
+				f = fp
+			} else {
+				functions[*f] = f
+				f.ID = uint64(len(mt.prof.Function)) + 1
+				mt.prof.Function = append(mt.prof.Function, f)
+			}
+			l.Line[i] = profile.Line{
+				Function: f,
+				Line:     int64(frame.Line),
+			}
+		}
+
+		if len(stack) > 0 {
+			m.HasInlineFrames = true
+		}
+	}
+	return nil
+}
+
+// newMapping creates a mappingTable for a profile.
+func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) {
+	mt := &mappingTable{
+		prof:     prof,
+		segments: make(map[*profile.Mapping]plugin.ObjFile),
+	}
+
+	// Identify used mappings
+	mappings := make(map[*profile.Mapping]bool)
+	for _, l := range prof.Location {
+		mappings[l.Mapping] = true
+	}
+
+	for _, m := range prof.Mapping {
+		if !mappings[m] {
+			continue
+		}
+		// Do not attempt to re-symbolize a mapping that has already been symbolized.
+		if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) {
+			continue
+		}
+
+		f, err := locateFile(obj, m.File, m.BuildID, m.Start)
+		if err != nil {
+			ui.PrintErr("Local symbolization failed for ", filepath.Base(m.File), ": ", err)
+			// Move on to other mappings
+			continue
+		}
+
+		if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID {
+			// Build ID mismatch - ignore.
+			f.Close()
+			continue
+		}
+
+		mt.segments[m] = f
+	}
+
+	return mt, nil
+}
+
+// locateFile opens a local file for symbolization on the search path
+// at $PPROF_BINARY_PATH. Looks inside these directories for files
+// named $BUILDID/$BASENAME and $BASENAME (if build id is available).
+func locateFile(obj plugin.ObjTool, file, buildID string, start uint64) (plugin.ObjFile, error) {
+	// Construct search path to examine
+	searchPath := os.Getenv("PPROF_BINARY_PATH")
+	if searchPath == "" {
+		// Use $HOME/pprof/binaries as default directory for local symbolization binaries
+		searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries")
+	}
+
+	// Collect names to search: {buildid/basename, basename}
+	var fileNames []string
+	if baseName := filepath.Base(file); buildID != "" {
+		fileNames = []string{filepath.Join(buildID, baseName), baseName}
+	} else {
+		fileNames = []string{baseName}
+	}
+	for _, path := range filepath.SplitList(searchPath) {
+		for nameIndex, name := range fileNames {
+			file := filepath.Join(path, name)
+			if f, err := obj.Open(file, start); err == nil {
+				fileBuildID := f.BuildID()
+				if buildID == "" || buildID == fileBuildID {
+					return f, nil
+				}
+				f.Close()
+				if nameIndex == 0 {
+					// If this is the first name, the path includes the build id. Report inconsistency.
+					return nil, fmt.Errorf("found file %s with inconsistent build id %s", file, fileBuildID)
+				}
+			}
+		}
+	}
+	// Try original file name
+	f, err := obj.Open(file, start)
+	if err == nil && buildID != "" {
+		if fileBuildID := f.BuildID(); fileBuildID != "" && fileBuildID != buildID {
+			// Mismatched build IDs, ignore
+			f.Close()
+			return nil, fmt.Errorf("mismatched build ids %s != %s", fileBuildID, buildID)
+		}
+	}
+	return f, err
+}
+
+// mappingTable contains the mechanisms for symbolization of a
+// profile.
+type mappingTable struct {
+	prof     *profile.Profile
+	segments map[*profile.Mapping]plugin.ObjFile
+}
+
+// Close releases any external processes being used for the mapping.
+func (mt *mappingTable) close() {
+	for _, segment := range mt.segments {
+		segment.Close()
+	}
+}
diff --git a/src/cmd/pprof/internal/symbolz/symbolz.go b/src/cmd/pprof/internal/symbolz/symbolz.go
new file mode 100644
index 0000000..6e58001
--- /dev/null
+++ b/src/cmd/pprof/internal/symbolz/symbolz.go
@@ -0,0 +1,111 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package symbolz symbolizes a profile using the output from the symbolz
+// service.
+package symbolz
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"net/url"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"internal/pprof/profile"
+)
+
+var (
+	symbolzRE = regexp.MustCompile(`(0x[[:xdigit:]]+)\s+(.*)`)
+)
+
+// Symbolize symbolizes profile p by parsing data returned by a
+// symbolz handler. syms receives the symbolz query (hex addresses
+// separated by '+') and returns the symbolz output in a string. It
+// symbolizes all locations based on their addresses, regardless of
+// mapping.
+func Symbolize(source string, syms func(string, string) ([]byte, error), p *profile.Profile) error {
+	if source = symbolz(source, p); source == "" {
+		// If the source is not a recognizable URL, do nothing.
+		return nil
+	}
+
+	// Construct query of addresses to symbolize.
+	var a []string
+	for _, l := range p.Location {
+		if l.Address != 0 && len(l.Line) == 0 {
+			a = append(a, fmt.Sprintf("%#x", l.Address))
+		}
+	}
+
+	if len(a) == 0 {
+		// No addresses to symbolize.
+		return nil
+	}
+	lines := make(map[uint64]profile.Line)
+	functions := make(map[string]*profile.Function)
+	if b, err := syms(source, strings.Join(a, "+")); err == nil {
+		buf := bytes.NewBuffer(b)
+		for {
+			l, err := buf.ReadString('\n')
+
+			if err != nil {
+				if err == io.EOF {
+					break
+				}
+				return err
+			}
+
+			if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 {
+				addr, err := strconv.ParseUint(symbol[1], 0, 64)
+				if err != nil {
+					return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err)
+				}
+
+				name := symbol[2]
+				fn := functions[name]
+				if fn == nil {
+					fn = &profile.Function{
+						ID:         uint64(len(p.Function) + 1),
+						Name:       name,
+						SystemName: name,
+					}
+					functions[name] = fn
+					p.Function = append(p.Function, fn)
+				}
+
+				lines[addr] = profile.Line{Function: fn}
+			}
+		}
+	}
+
+	for _, l := range p.Location {
+		if line, ok := lines[l.Address]; ok {
+			l.Line = []profile.Line{line}
+			if l.Mapping != nil {
+				l.Mapping.HasFunctions = true
+			}
+		}
+	}
+
+	return nil
+}
+
+// symbolz returns the corresponding symbolz source for a profile URL.
+func symbolz(source string, p *profile.Profile) string {
+	if url, err := url.Parse(source); err == nil && url.Host != "" {
+		if last := strings.LastIndex(url.Path, "/"); last != -1 {
+			if strings.HasSuffix(url.Path[:last], "pprof") {
+				url.Path = url.Path[:last] + "/symbol"
+			} else {
+				url.Path = url.Path[:last] + "/symbolz"
+			}
+			return url.String()
+		}
+	}
+
+	return ""
+}
diff --git a/src/cmd/internal/pprof/tempfile/tempfile.go b/src/cmd/pprof/internal/tempfile/tempfile.go
similarity index 100%
rename from src/cmd/internal/pprof/tempfile/tempfile.go
rename to src/cmd/pprof/internal/tempfile/tempfile.go
diff --git a/src/cmd/pprof/pprof.go b/src/cmd/pprof/pprof.go
index 5ee8a11..18479b4 100644
--- a/src/cmd/pprof/pprof.go
+++ b/src/cmd/pprof/pprof.go
@@ -6,7 +6,6 @@ package main
 
 import (
 	"debug/dwarf"
-	"debug/gosym"
 	"flag"
 	"fmt"
 	"net/url"
@@ -16,13 +15,13 @@ import (
 	"sync"
 
 	"cmd/internal/objfile"
-	"cmd/internal/pprof/commands"
-	"cmd/internal/pprof/driver"
-	"cmd/internal/pprof/fetch"
-	"cmd/internal/pprof/plugin"
-	"cmd/internal/pprof/profile"
-	"cmd/internal/pprof/symbolizer"
-	"cmd/internal/pprof/symbolz"
+	"cmd/pprof/internal/commands"
+	"cmd/pprof/internal/driver"
+	"cmd/pprof/internal/fetch"
+	"cmd/pprof/internal/plugin"
+	"cmd/pprof/internal/symbolizer"
+	"cmd/pprof/internal/symbolz"
+	"internal/pprof/profile"
 )
 
 func main() {
@@ -161,7 +160,7 @@ func (t *objTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error)
 		return nil, err
 	}
 	var asm []plugin.Inst
-	d.Decode(start, end, func(pc, size uint64, file string, line int, text string) {
+	d.Decode(start, end, nil, func(pc, size uint64, file string, line int, text string) {
 		asm = append(asm, plugin.Inst{Addr: pc, File: file, Line: line, Text: text})
 	})
 	return asm, nil
@@ -203,7 +202,7 @@ type file struct {
 	offset uint64
 	sym    []objfile.Sym
 	file   *objfile.File
-	pcln   *gosym.Table
+	pcln   objfile.Liner
 
 	triedDwarf bool
 	dwarf      *dwarf.Data
diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go
index 893719e..c7bf75e 100644
--- a/src/cmd/trace/main.go
+++ b/src/cmd/trace/main.go
@@ -15,21 +15,29 @@ Generate a trace file with 'go test':
 	go test -trace trace.out pkg
 View the trace in a web browser:
 	go tool trace trace.out
+Generate a pprof-like profile from the trace:
+	go tool trace -pprof=TYPE trace.out > TYPE.pprof
+
+Supported profile types are:
+	- net: network blocking profile
+	- sync: synchronization blocking profile
+	- syscall: syscall blocking profile
+	- sched: scheduler latency profile
 */
 package main
 
 import (
 	"bufio"
+	"cmd/internal/browser"
 	"flag"
 	"fmt"
 	"html/template"
 	"internal/trace"
+	"io"
 	"log"
 	"net"
 	"net/http"
 	"os"
-	"os/exec"
-	"runtime"
 	"sync"
 )
 
@@ -40,15 +48,27 @@ Given a trace file produced by 'go test':
 
 Open a web browser displaying trace:
 	go tool trace [flags] [pkg.test] trace.out
+
+Generate a pprof-like profile from the trace:
+    go tool trace -pprof=TYPE [pkg.test] trace.out
+
 [pkg.test] argument is required for traces produced by Go 1.6 and below.
 Go 1.7 does not require the binary argument.
 
+Supported profile types are:
+    - net: network blocking profile
+    - sync: synchronization blocking profile
+    - syscall: syscall blocking profile
+    - sched: scheduler latency profile
+
 Flags:
 	-http=addr: HTTP service address (e.g., ':6060')
+	-pprof=type: print a pprof-like profile instead
 `
 
 var (
-	httpFlag = flag.String("http", "localhost:0", "HTTP service address (e.g., ':6060')")
+	httpFlag  = flag.String("http", "localhost:0", "HTTP service address (e.g., ':6060')")
+	pprofFlag = flag.String("pprof", "", "print a pprof-like profile instead")
 
 	// The binary file name, left here for serveSVGProfile.
 	programBinary string
@@ -74,6 +94,27 @@ func main() {
 		flag.Usage()
 	}
 
+	var pprofFunc func(io.Writer) error
+	switch *pprofFlag {
+	case "net":
+		pprofFunc = pprofIO
+	case "sync":
+		pprofFunc = pprofBlock
+	case "syscall":
+		pprofFunc = pprofSyscall
+	case "sched":
+		pprofFunc = pprofSched
+	}
+	if pprofFunc != nil {
+		if err := pprofFunc(os.Stdout); err != nil {
+			dief("failed to generate pprof: %v\n", err)
+		}
+		os.Exit(0)
+	}
+	if *pprofFlag != "" {
+		dief("unknown pprof type %s\n", *pprofFlag)
+	}
+
 	ln, err := net.Listen("tcp", *httpFlag)
 	if err != nil {
 		dief("failed to create server socket: %v\n", err)
@@ -90,13 +131,16 @@ func main() {
 		events:  events,
 		endTime: int64(1<<63 - 1),
 	}
-	data := generateTrace(params)
+	data, err := generateTrace(params)
+	if err != nil {
+		dief("%v\n", err)
+	}
 
 	log.Printf("Splitting trace...")
 	ranges = splitTrace(data)
 
 	log.Printf("Opening browser")
-	if !startBrowser("http://" + ln.Addr().String()) {
+	if !browser.Open("http://" + ln.Addr().String()) {
 		fmt.Fprintf(os.Stderr, "Trace viewer is listening on http://%s\n", ln.Addr().String())
 	}
 
@@ -162,24 +206,6 @@ var templMain = template.Must(template.New("").Parse(`
 </html>
 `))
 
-// startBrowser tries to open the URL in a browser
-// and reports whether it succeeds.
-// Note: copied from x/tools/cmd/cover/html.go
-func startBrowser(url string) bool {
-	// try to start the browser
-	var args []string
-	switch runtime.GOOS {
-	case "darwin":
-		args = []string{"open"}
-	case "windows":
-		args = []string{"cmd", "/c", "start"}
-	default:
-		args = []string{"xdg-open"}
-	}
-	cmd := exec.Command(args[0], append(args[1:], url)...)
-	return cmd.Start() == nil
-}
-
 func dief(msg string, args ...interface{}) {
 	fmt.Fprintf(os.Stderr, msg, args...)
 	os.Exit(1)
diff --git a/src/cmd/trace/pprof.go b/src/cmd/trace/pprof.go
index fdda6d8..dea3a74 100644
--- a/src/cmd/trace/pprof.go
+++ b/src/cmd/trace/pprof.go
@@ -8,9 +8,10 @@ package main
 
 import (
 	"bufio"
-	"cmd/internal/pprof/profile"
 	"fmt"
+	"internal/pprof/profile"
 	"internal/trace"
+	"io"
 	"io/ioutil"
 	"net/http"
 	"os"
@@ -18,10 +19,10 @@ import (
 )
 
 func init() {
-	http.HandleFunc("/io", httpIO)
-	http.HandleFunc("/block", httpBlock)
-	http.HandleFunc("/syscall", httpSyscall)
-	http.HandleFunc("/sched", httpSched)
+	http.HandleFunc("/io", serveSVGProfile(pprofIO))
+	http.HandleFunc("/block", serveSVGProfile(pprofBlock))
+	http.HandleFunc("/syscall", serveSVGProfile(pprofSyscall))
+	http.HandleFunc("/sched", serveSVGProfile(pprofSched))
 }
 
 // Record represents one entry in pprof-like profiles.
@@ -31,12 +32,11 @@ type Record struct {
 	time int64
 }
 
-// httpIO serves IO pprof-like profile (time spent in IO wait).
-func httpIO(w http.ResponseWriter, r *http.Request) {
+// pprofIO generates IO pprof-like profile (time spent in IO wait).
+func pprofIO(w io.Writer) error {
 	events, err := parseEvents()
 	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
+		return err
 	}
 	prof := make(map[uint64]Record)
 	for _, ev := range events {
@@ -49,21 +49,20 @@ func httpIO(w http.ResponseWriter, r *http.Request) {
 		rec.time += ev.Link.Ts - ev.Ts
 		prof[ev.StkID] = rec
 	}
-	serveSVGProfile(w, r, prof)
+	return buildProfile(prof).Write(w)
 }
 
-// httpBlock serves blocking pprof-like profile (time spent blocked on synchronization primitives).
-func httpBlock(w http.ResponseWriter, r *http.Request) {
+// pprofBlock generates blocking pprof-like profile (time spent blocked on synchronization primitives).
+func pprofBlock(w io.Writer) error {
 	events, err := parseEvents()
 	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
+		return err
 	}
 	prof := make(map[uint64]Record)
 	for _, ev := range events {
 		switch ev.Type {
 		case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect,
-			trace.EvGoBlockSync, trace.EvGoBlockCond:
+			trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockGC:
 		default:
 			continue
 		}
@@ -76,15 +75,14 @@ func httpBlock(w http.ResponseWriter, r *http.Request) {
 		rec.time += ev.Link.Ts - ev.Ts
 		prof[ev.StkID] = rec
 	}
-	serveSVGProfile(w, r, prof)
+	return buildProfile(prof).Write(w)
 }
 
-// httpSyscall serves syscall pprof-like profile (time spent blocked in syscalls).
-func httpSyscall(w http.ResponseWriter, r *http.Request) {
+// pprofSyscall generates syscall pprof-like profile (time spent blocked in syscalls).
+func pprofSyscall(w io.Writer) error {
 	events, err := parseEvents()
 	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
+		return err
 	}
 	prof := make(map[uint64]Record)
 	for _, ev := range events {
@@ -97,16 +95,15 @@ func httpSyscall(w http.ResponseWriter, r *http.Request) {
 		rec.time += ev.Link.Ts - ev.Ts
 		prof[ev.StkID] = rec
 	}
-	serveSVGProfile(w, r, prof)
+	return buildProfile(prof).Write(w)
 }
 
-// httpSched serves scheduler latency pprof-like profile
+// pprofSched generates scheduler latency pprof-like profile
 // (time between a goroutine become runnable and actually scheduled for execution).
-func httpSched(w http.ResponseWriter, r *http.Request) {
+func pprofSched(w io.Writer) error {
 	events, err := parseEvents()
 	if err != nil {
-		http.Error(w, err.Error(), http.StatusInternalServerError)
-		return
+		return err
 	}
 	prof := make(map[uint64]Record)
 	for _, ev := range events {
@@ -120,45 +117,43 @@ func httpSched(w http.ResponseWriter, r *http.Request) {
 		rec.time += ev.Link.Ts - ev.Ts
 		prof[ev.StkID] = rec
 	}
-	serveSVGProfile(w, r, prof)
+	return buildProfile(prof).Write(w)
 }
 
-// generateSVGProfile generates pprof-like profile stored in prof and writes in to w.
-func serveSVGProfile(w http.ResponseWriter, r *http.Request, prof map[uint64]Record) {
-	if len(prof) == 0 {
-		http.Error(w, "The profile is empty", http.StatusNotFound)
-		return
-	}
-	blockf, err := ioutil.TempFile("", "block")
-	if err != nil {
-		http.Error(w, fmt.Sprintf("failed to create temp file: %v", err), http.StatusInternalServerError)
-		return
-	}
-	defer func() {
-		blockf.Close()
-		os.Remove(blockf.Name())
-	}()
-	blockb := bufio.NewWriter(blockf)
-	if err := buildProfile(prof).Write(blockb); err != nil {
-		http.Error(w, fmt.Sprintf("failed to write profile: %v", err), http.StatusInternalServerError)
-		return
-	}
-	if err := blockb.Flush(); err != nil {
-		http.Error(w, fmt.Sprintf("failed to flush temp file: %v", err), http.StatusInternalServerError)
-		return
-	}
-	if err := blockf.Close(); err != nil {
-		http.Error(w, fmt.Sprintf("failed to close temp file: %v", err), http.StatusInternalServerError)
-		return
-	}
-	svgFilename := blockf.Name() + ".svg"
-	if output, err := exec.Command("go", "tool", "pprof", "-svg", "-output", svgFilename, blockf.Name()).CombinedOutput(); err != nil {
-		http.Error(w, fmt.Sprintf("failed to execute go tool pprof: %v\n%s", err, output), http.StatusInternalServerError)
-		return
+// serveSVGProfile serves pprof-like profile generated by prof as svg.
+func serveSVGProfile(prof func(w io.Writer) error) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		blockf, err := ioutil.TempFile("", "block")
+		if err != nil {
+			http.Error(w, fmt.Sprintf("failed to create temp file: %v", err), http.StatusInternalServerError)
+			return
+		}
+		defer func() {
+			blockf.Close()
+			os.Remove(blockf.Name())
+		}()
+		blockb := bufio.NewWriter(blockf)
+		if err := prof(blockb); err != nil {
+			http.Error(w, fmt.Sprintf("failed to generate profile: %v", err), http.StatusInternalServerError)
+			return
+		}
+		if err := blockb.Flush(); err != nil {
+			http.Error(w, fmt.Sprintf("failed to flush temp file: %v", err), http.StatusInternalServerError)
+			return
+		}
+		if err := blockf.Close(); err != nil {
+			http.Error(w, fmt.Sprintf("failed to close temp file: %v", err), http.StatusInternalServerError)
+			return
+		}
+		svgFilename := blockf.Name() + ".svg"
+		if output, err := exec.Command("go", "tool", "pprof", "-svg", "-output", svgFilename, blockf.Name()).CombinedOutput(); err != nil {
+			http.Error(w, fmt.Sprintf("failed to execute go tool pprof: %v\n%s", err, output), http.StatusInternalServerError)
+			return
+		}
+		defer os.Remove(svgFilename)
+		w.Header().Set("Content-Type", "image/svg+xml")
+		http.ServeFile(w, r, svgFilename)
 	}
-	defer os.Remove(svgFilename)
-	w.Header().Set("Content-Type", "image/svg+xml")
-	http.ServeFile(w, r, svgFilename)
 }
 
 func buildProfile(prof map[uint64]Record) *profile.Profile {
diff --git a/src/cmd/trace/trace.go b/src/cmd/trace/trace.go
index 2b6a37b..f5a2df4 100644
--- a/src/cmd/trace/trace.go
+++ b/src/cmd/trace/trace.go
@@ -160,7 +160,11 @@ func httpJsonTrace(w http.ResponseWriter, r *http.Request) {
 		params.gs = trace.RelatedGoroutines(events, goid)
 	}
 
-	data := generateTrace(params)
+	data, err := generateTrace(params)
+	if err != nil {
+		log.Printf("failed to generate trace: %v", err)
+		return
+	}
 
 	if startStr, endStr := r.FormValue("start"), r.FormValue("end"); startStr != "" && endStr != "" {
 		// If start/end arguments are present, we are rendering a range of the trace.
@@ -250,11 +254,19 @@ type traceContext struct {
 	frameTree frameNode
 	frameSeq  int
 	arrowSeq  uint64
+	gcount    uint64
+
+	heapStats, prevHeapStats     heapStats
+	threadStats, prevThreadStats threadStats
+	gstates, prevGstates         [gStateCount]uint64
+}
+
+type heapStats struct {
 	heapAlloc uint64
 	nextGC    uint64
-	gcount    uint64
-	grunnable uint64
-	grunning  uint64
+}
+
+type threadStats struct {
 	insyscall uint64
 	prunning  uint64
 }
@@ -264,6 +276,18 @@ type frameNode struct {
 	children map[uint64]frameNode
 }
 
+type gState int
+
+const (
+	gDead gState = iota
+	gRunnable
+	gRunning
+	gWaiting
+	gWaitingGC
+
+	gStateCount
+)
+
 type ViewerData struct {
 	Events   []*ViewerEvent         `json:"traceEvents"`
 	Frames   map[string]ViewerFrame `json:"stackFrames"`
@@ -307,17 +331,34 @@ type SortIndexArg struct {
 // If gtrace=true, generate trace for goroutine goid, otherwise whole trace.
 // startTime, endTime determine part of the trace that we are interested in.
 // gset restricts goroutines that are included in the resulting trace.
-func generateTrace(params *traceParams) ViewerData {
+func generateTrace(params *traceParams) (ViewerData, error) {
 	ctx := &traceContext{traceParams: params}
 	ctx.frameTree.children = make(map[uint64]frameNode)
 	ctx.data.Frames = make(map[string]ViewerFrame)
 	ctx.data.TimeUnit = "ns"
 	maxProc := 0
 	gnames := make(map[uint64]string)
+	gstates := make(map[uint64]gState)
+	// Since we make many calls to setGState, we record a sticky
+	// error in setGStateErr and check it after every event.
+	var setGStateErr error
+	setGState := func(ev *trace.Event, g uint64, oldState, newState gState) {
+		if oldState == gWaiting && gstates[g] == gWaitingGC {
+			// For checking, gWaiting counts as any gWaiting*.
+			oldState = gstates[g]
+		}
+		if gstates[g] != oldState && setGStateErr == nil {
+			setGStateErr = fmt.Errorf("expected G %d to be in state %d, but got state %d", g, oldState, newState)
+		}
+		ctx.gstates[gstates[g]]--
+		ctx.gstates[newState]++
+		gstates[g] = newState
+	}
 	for _, ev := range ctx.events {
-		// Handle trace.EvGoStart separately, because we need the goroutine name
-		// even if ignore the event otherwise.
-		if ev.Type == trace.EvGoStart {
+		// Handle state transitions before we filter out events.
+		switch ev.Type {
+		case trace.EvGoStart, trace.EvGoStartLabel:
+			setGState(ev, ev.G, gRunnable, gRunning)
 			if _, ok := gnames[ev.G]; !ok {
 				if len(ev.Stk) > 0 {
 					gnames[ev.G] = fmt.Sprintf("G%v %s", ev.G, ev.Stk[0].Fn)
@@ -325,6 +366,48 @@ func generateTrace(params *traceParams) ViewerData {
 					gnames[ev.G] = fmt.Sprintf("G%v", ev.G)
 				}
 			}
+		case trace.EvProcStart:
+			ctx.threadStats.prunning++
+		case trace.EvProcStop:
+			ctx.threadStats.prunning--
+		case trace.EvGoCreate:
+			ctx.gcount++
+			setGState(ev, ev.Args[0], gDead, gRunnable)
+		case trace.EvGoEnd:
+			ctx.gcount--
+			setGState(ev, ev.G, gRunning, gDead)
+		case trace.EvGoUnblock:
+			setGState(ev, ev.Args[0], gWaiting, gRunnable)
+		case trace.EvGoSysExit:
+			setGState(ev, ev.G, gWaiting, gRunnable)
+			ctx.threadStats.insyscall--
+		case trace.EvGoSysBlock:
+			setGState(ev, ev.G, gRunning, gWaiting)
+			ctx.threadStats.insyscall++
+		case trace.EvGoSched, trace.EvGoPreempt:
+			setGState(ev, ev.G, gRunning, gRunnable)
+		case trace.EvGoStop,
+			trace.EvGoSleep, trace.EvGoBlock, trace.EvGoBlockSend, trace.EvGoBlockRecv,
+			trace.EvGoBlockSelect, trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockNet:
+			setGState(ev, ev.G, gRunning, gWaiting)
+		case trace.EvGoBlockGC:
+			setGState(ev, ev.G, gRunning, gWaitingGC)
+		case trace.EvGoWaiting:
+			setGState(ev, ev.G, gRunnable, gWaiting)
+		case trace.EvGoInSyscall:
+			// Cancel out the effect of EvGoCreate at the beginning.
+			setGState(ev, ev.G, gRunnable, gWaiting)
+			ctx.threadStats.insyscall++
+		case trace.EvHeapAlloc:
+			ctx.heapStats.heapAlloc = ev.Args[0]
+		case trace.EvNextGC:
+			ctx.heapStats.nextGC = ev.Args[0]
+		}
+		if setGStateErr != nil {
+			return ctx.data, setGStateErr
+		}
+		if ctx.gstates[gRunnable] < 0 || ctx.gstates[gRunning] < 0 || ctx.threadStats.insyscall < 0 {
+			return ctx.data, fmt.Errorf("invalid state after processing %v: runnable=%d running=%d insyscall=%d", ev, ctx.gstates[gRunnable], ctx.gstates[gRunning], ctx.threadStats.insyscall)
 		}
 
 		// Ignore events that are from uninteresting goroutines
@@ -340,20 +423,17 @@ func generateTrace(params *traceParams) ViewerData {
 			maxProc = ev.P
 		}
 
+		// Emit trace objects.
 		switch ev.Type {
 		case trace.EvProcStart:
 			if ctx.gtrace {
 				continue
 			}
-			ctx.prunning++
-			ctx.emitThreadCounters(ev)
 			ctx.emitInstant(ev, "proc start")
 		case trace.EvProcStop:
 			if ctx.gtrace {
 				continue
 			}
-			ctx.prunning--
-			ctx.emitThreadCounters(ev)
 			ctx.emitInstant(ev, "proc stop")
 		case trace.EvGCStart:
 			ctx.emitSlice(ev, "GC")
@@ -362,64 +442,28 @@ func generateTrace(params *traceParams) ViewerData {
 			if ctx.gtrace {
 				continue
 			}
-			ctx.emitSlice(ev, "MARK")
+			ctx.emitSlice(ev, "MARK TERMINATION")
 		case trace.EvGCScanDone:
 		case trace.EvGCSweepStart:
 			ctx.emitSlice(ev, "SWEEP")
 		case trace.EvGCSweepDone:
 		case trace.EvGoStart:
-			ctx.grunnable--
-			ctx.grunning++
-			ctx.emitGoroutineCounters(ev)
 			ctx.emitSlice(ev, gnames[ev.G])
+		case trace.EvGoStartLabel:
+			ctx.emitSlice(ev, ev.SArgs[0])
 		case trace.EvGoCreate:
-			ctx.gcount++
-			ctx.grunnable++
-			ctx.emitGoroutineCounters(ev)
 			ctx.emitArrow(ev, "go")
-		case trace.EvGoEnd:
-			ctx.gcount--
-			ctx.grunning--
-			ctx.emitGoroutineCounters(ev)
 		case trace.EvGoUnblock:
-			ctx.grunnable++
-			ctx.emitGoroutineCounters(ev)
 			ctx.emitArrow(ev, "unblock")
 		case trace.EvGoSysCall:
 			ctx.emitInstant(ev, "syscall")
 		case trace.EvGoSysExit:
-			ctx.grunnable++
-			ctx.emitGoroutineCounters(ev)
-			ctx.insyscall--
-			ctx.emitThreadCounters(ev)
 			ctx.emitArrow(ev, "sysexit")
-		case trace.EvGoSysBlock:
-			ctx.grunning--
-			ctx.emitGoroutineCounters(ev)
-			ctx.insyscall++
-			ctx.emitThreadCounters(ev)
-		case trace.EvGoSched, trace.EvGoPreempt:
-			ctx.grunnable++
-			ctx.grunning--
-			ctx.emitGoroutineCounters(ev)
-		case trace.EvGoStop,
-			trace.EvGoSleep, trace.EvGoBlock, trace.EvGoBlockSend, trace.EvGoBlockRecv,
-			trace.EvGoBlockSelect, trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockNet:
-			ctx.grunning--
-			ctx.emitGoroutineCounters(ev)
-		case trace.EvGoWaiting:
-			ctx.grunnable--
-			ctx.emitGoroutineCounters(ev)
-		case trace.EvGoInSyscall:
-			ctx.insyscall++
-			ctx.emitThreadCounters(ev)
-		case trace.EvHeapAlloc:
-			ctx.heapAlloc = ev.Args[0]
-			ctx.emitHeapCounters(ev)
-		case trace.EvNextGC:
-			ctx.nextGC = ev.Args[0]
-			ctx.emitHeapCounters(ev)
 		}
+		// Emit any counter updates.
+		ctx.emitThreadCounters(ev)
+		ctx.emitHeapCounters(ev)
+		ctx.emitGoroutineCounters(ev)
 	}
 
 	ctx.data.footer = len(ctx.data.Events)
@@ -429,6 +473,9 @@ func generateTrace(params *traceParams) ViewerData {
 	ctx.emit(&ViewerEvent{Name: "process_name", Phase: "M", Pid: 1, Arg: &NameArg{"STATS"}})
 	ctx.emit(&ViewerEvent{Name: "process_sort_index", Phase: "M", Pid: 1, Arg: &SortIndexArg{0}})
 
+	ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: trace.GCP, Arg: &NameArg{"GC"}})
+	ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: trace.GCP, Arg: &SortIndexArg{-6}})
+
 	ctx.emit(&ViewerEvent{Name: "thread_name", Phase: "M", Pid: 0, Tid: trace.NetpollP, Arg: &NameArg{"Network"}})
 	ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: trace.NetpollP, Arg: &SortIndexArg{-5}})
 
@@ -456,7 +503,7 @@ func generateTrace(params *traceParams) ViewerData {
 		ctx.emit(&ViewerEvent{Name: "thread_sort_index", Phase: "M", Pid: 0, Tid: 0, Arg: &SortIndexArg{-1}})
 	}
 
-	return ctx.data
+	return ctx.data, nil
 }
 
 func (ctx *traceContext) emit(e *ViewerEvent) {
@@ -488,41 +535,57 @@ func (ctx *traceContext) emitSlice(ev *trace.Event, name string) {
 	})
 }
 
+type heapCountersArg struct {
+	Allocated uint64
+	NextGC    uint64
+}
+
 func (ctx *traceContext) emitHeapCounters(ev *trace.Event) {
-	type Arg struct {
-		Allocated uint64
-		NextGC    uint64
-	}
 	if ctx.gtrace {
 		return
 	}
+	if ctx.prevHeapStats == ctx.heapStats {
+		return
+	}
 	diff := uint64(0)
-	if ctx.nextGC > ctx.heapAlloc {
-		diff = ctx.nextGC - ctx.heapAlloc
+	if ctx.heapStats.nextGC > ctx.heapStats.heapAlloc {
+		diff = ctx.heapStats.nextGC - ctx.heapStats.heapAlloc
 	}
-	ctx.emit(&ViewerEvent{Name: "Heap", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &Arg{ctx.heapAlloc, diff}})
+	ctx.emit(&ViewerEvent{Name: "Heap", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &heapCountersArg{ctx.heapStats.heapAlloc, diff}})
+	ctx.prevHeapStats = ctx.heapStats
+}
+
+type goroutineCountersArg struct {
+	Running   uint64
+	Runnable  uint64
+	GCWaiting uint64
 }
 
 func (ctx *traceContext) emitGoroutineCounters(ev *trace.Event) {
-	type Arg struct {
-		Running  uint64
-		Runnable uint64
-	}
 	if ctx.gtrace {
 		return
 	}
-	ctx.emit(&ViewerEvent{Name: "Goroutines", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &Arg{ctx.grunning, ctx.grunnable}})
+	if ctx.prevGstates == ctx.gstates {
+		return
+	}
+	ctx.emit(&ViewerEvent{Name: "Goroutines", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &goroutineCountersArg{ctx.gstates[gRunning], ctx.gstates[gRunnable], ctx.gstates[gWaitingGC]}})
+	ctx.prevGstates = ctx.gstates
+}
+
+type threadCountersArg struct {
+	Running   uint64
+	InSyscall uint64
 }
 
 func (ctx *traceContext) emitThreadCounters(ev *trace.Event) {
-	type Arg struct {
-		Running   uint64
-		InSyscall uint64
-	}
 	if ctx.gtrace {
 		return
 	}
-	ctx.emit(&ViewerEvent{Name: "Threads", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &Arg{ctx.prunning, ctx.insyscall}})
+	if ctx.prevThreadStats == ctx.threadStats {
+		return
+	}
+	ctx.emit(&ViewerEvent{Name: "Threads", Phase: "C", Time: ctx.time(ev), Pid: 1, Arg: &threadCountersArg{ctx.threadStats.prunning, ctx.threadStats.insyscall}})
+	ctx.prevThreadStats = ctx.threadStats
 }
 
 func (ctx *traceContext) emitInstant(ev *trace.Event, name string) {
diff --git a/src/cmd/trace/trace_test.go b/src/cmd/trace/trace_test.go
new file mode 100644
index 0000000..d14239c
--- /dev/null
+++ b/src/cmd/trace/trace_test.go
@@ -0,0 +1,101 @@
+package main
+
+import (
+	"internal/trace"
+	"testing"
+)
+
+// TestGoroutineCount tests runnable/running goroutine counts computed by generateTrace
+// remain in the valid range.
+//   - the counts must not be negative. generateTrace will return an error.
+//   - the counts must not include goroutines blocked waiting on channels or in syscall.
+func TestGoroutineCount(t *testing.T) {
+	w := trace.NewWriter()
+	w.Emit(trace.EvBatch, 0, 0)  // start of per-P batch event [pid, timestamp]
+	w.Emit(trace.EvFrequency, 1) // [ticks per second]
+
+	// In this test, we assume a valid trace contains EvGoWaiting or EvGoInSyscall
+	// event for every blocked goroutine.
+
+	// goroutine 10: blocked
+	w.Emit(trace.EvGoCreate, 1, 10, 1, 1) // [timestamp, new goroutine id, new stack id, stack id]
+	w.Emit(trace.EvGoWaiting, 1, 10)      // [timestamp, goroutine id]
+
+	// goroutine 20: in syscall
+	w.Emit(trace.EvGoCreate, 1, 20, 2, 1)
+	w.Emit(trace.EvGoInSyscall, 1, 20) // [timestamp, goroutine id]
+
+	// goroutine 30: runnable
+	w.Emit(trace.EvGoCreate, 1, 30, 5, 1)
+
+	w.Emit(trace.EvProcStart, 2, 0) // [timestamp, thread id]
+
+	// goroutine 40: runnable->running->runnable
+	w.Emit(trace.EvGoCreate, 1, 40, 7, 1)
+	w.Emit(trace.EvGoStartLocal, 1, 40) // [timestamp, goroutine id]
+	w.Emit(trace.EvGoSched, 1, 8)       // [timestamp, stack]
+
+	events, err := trace.Parse(w, "")
+	if err != nil {
+		t.Fatalf("failed to parse test trace: %v", err)
+	}
+
+	params := &traceParams{
+		events:  events,
+		endTime: int64(1<<63 - 1),
+	}
+
+	// If the counts drop below 0, generateTrace will return an error.
+	viewerData, err := generateTrace(params)
+	if err != nil {
+		t.Fatalf("generateTrace failed: %v", err)
+	}
+	for _, ev := range viewerData.Events {
+		if ev.Name == "Goroutines" {
+			cnt := ev.Arg.(*goroutineCountersArg)
+			if cnt.Runnable+cnt.Running > 2 {
+				t.Errorf("goroutine count=%+v; want no more than 2 goroutines in runnable/running state", cnt)
+			}
+			t.Logf("read %+v %+v", ev, cnt)
+		}
+	}
+}
+
+func TestGoroutineFilter(t *testing.T) {
+	// Test that we handle state changes to selected goroutines
+	// caused by events on goroutines that are not selected.
+
+	w := trace.NewWriter()
+	w.Emit(trace.EvBatch, 0, 0)  // start of per-P batch event [pid, timestamp]
+	w.Emit(trace.EvFrequency, 1) // [ticks per second]
+
+	// goroutine 10: blocked
+	w.Emit(trace.EvGoCreate, 1, 10, 1, 1) // [timestamp, new goroutine id, new stack id, stack id]
+	w.Emit(trace.EvGoWaiting, 1, 10)      // [timestamp, goroutine id]
+
+	// goroutine 20: runnable->running->unblock 10
+	w.Emit(trace.EvGoCreate, 1, 20, 7, 1)
+	w.Emit(trace.EvGoStartLocal, 1, 20)      // [timestamp, goroutine id]
+	w.Emit(trace.EvGoUnblockLocal, 1, 10, 8) // [timestamp, goroutine id, stack]
+	w.Emit(trace.EvGoEnd, 1)                 // [timestamp]
+
+	// goroutine 10: runnable->running->block
+	w.Emit(trace.EvGoStartLocal, 1, 10) // [timestamp, goroutine id]
+	w.Emit(trace.EvGoBlock, 1, 9)       // [timestamp, stack]
+
+	events, err := trace.Parse(w, "")
+	if err != nil {
+		t.Fatalf("failed to parse test trace: %v", err)
+	}
+
+	params := &traceParams{
+		events:  events,
+		endTime: int64(1<<63 - 1),
+		gs:      map[uint64]bool{10: true},
+	}
+
+	_, err = generateTrace(params)
+	if err != nil {
+		t.Fatalf("generateTrace failed: %v", err)
+	}
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/decode.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/decode.go
new file mode 100644
index 0000000..e1518d5
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/decode.go
@@ -0,0 +1,179 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ppc64asm
+
+import (
+	"encoding/binary"
+	"fmt"
+	"log"
+)
+
+const debugDecode = false
+
+// instFormat is a decoding rule for one specific instruction form.
+// a uint32 instruction ins matches the rule if ins&Mask == Value
+// DontCare bits should be zero, but the machine might not reject
+// ones in those bits, they are mainly reserved for future expansion
+// of the instruction set.
+// The Args are stored in the same order as the instruction manual.
+type instFormat struct {
+	Op       Op
+	Mask     uint32
+	Value    uint32
+	DontCare uint32
+	Args     [5]*argField
+}
+
+// argField indicate how to decode an argument to an instruction.
+// First parse the value from the BitFields, shift it left by Shift
+// bits to get the actual numerical value.
+type argField struct {
+	Type  ArgType
+	Shift uint8
+	BitFields
+}
+
+// Parse parses the Arg out from the given binary instruction i.
+func (a argField) Parse(i uint32) Arg {
+	switch a.Type {
+	default:
+		return nil
+	case TypeUnknown:
+		return nil
+	case TypeReg:
+		return R0 + Reg(a.BitFields.Parse(i))
+	case TypeCondRegBit:
+		return Cond0LT + CondReg(a.BitFields.Parse(i))
+	case TypeCondRegField:
+		return CR0 + CondReg(a.BitFields.Parse(i))
+	case TypeFPReg:
+		return F0 + Reg(a.BitFields.Parse(i))
+	case TypeVecReg:
+		return V0 + Reg(a.BitFields.Parse(i))
+	case TypeVecSReg:
+		return VS0 + Reg(a.BitFields.Parse(i))
+	case TypeSpReg:
+		return SpReg(a.BitFields.Parse(i))
+	case TypeImmSigned:
+		return Imm(a.BitFields.ParseSigned(i) << a.Shift)
+	case TypeImmUnsigned:
+		return Imm(a.BitFields.Parse(i) << a.Shift)
+	case TypePCRel:
+		return PCRel(a.BitFields.ParseSigned(i) << a.Shift)
+	case TypeLabel:
+		return Label(a.BitFields.ParseSigned(i) << a.Shift)
+	case TypeOffset:
+		return Offset(a.BitFields.ParseSigned(i) << a.Shift)
+	}
+}
+
+type ArgType int8
+
+const (
+	TypeUnknown      ArgType = iota
+	TypePCRel                // PC-relative address
+	TypeLabel                // absolute address
+	TypeReg                  // integer register
+	TypeCondRegBit           // conditional register bit (0-31)
+	TypeCondRegField         // conditional register field (0-7)
+	TypeFPReg                // floating point register
+	TypeVecReg               // vector register
+	TypeVecSReg              // VSX register
+	TypeSpReg                // special register (depends on Op)
+	TypeImmSigned            // signed immediate
+	TypeImmUnsigned          // unsigned immediate/flag/mask, this is the catch-all type
+	TypeOffset               // signed offset in load/store
+	TypeLast                 // must be the last one
+)
+
+func (t ArgType) String() string {
+	switch t {
+	default:
+		return fmt.Sprintf("ArgType(%d)", int(t))
+	case TypeUnknown:
+		return "Unknown"
+	case TypeReg:
+		return "Reg"
+	case TypeCondRegBit:
+		return "CondRegBit"
+	case TypeCondRegField:
+		return "CondRegField"
+	case TypeFPReg:
+		return "FPReg"
+	case TypeVecReg:
+		return "VecReg"
+	case TypeVecSReg:
+		return "VecSReg"
+	case TypeSpReg:
+		return "SpReg"
+	case TypeImmSigned:
+		return "ImmSigned"
+	case TypeImmUnsigned:
+		return "ImmUnsigned"
+	case TypePCRel:
+		return "PCRel"
+	case TypeLabel:
+		return "Label"
+	case TypeOffset:
+		return "Offset"
+	}
+}
+
+func (t ArgType) GoString() string {
+	s := t.String()
+	if t > 0 && t < TypeLast {
+		return "Type" + s
+	}
+	return s
+}
+
+var (
+	// Errors
+	errShort   = fmt.Errorf("truncated instruction")
+	errUnknown = fmt.Errorf("unknown instruction")
+)
+
+var decoderCover []bool
+
+// Decode decodes the leading bytes in src as a single instruction using
+// byte order ord.
+func Decode(src []byte, ord binary.ByteOrder) (inst Inst, err error) {
+	if len(src) < 4 {
+		return inst, errShort
+	}
+	if decoderCover == nil {
+		decoderCover = make([]bool, len(instFormats))
+	}
+	inst.Len = 4 // only 4-byte instructions are supported
+	ui := ord.Uint32(src[:inst.Len])
+	inst.Enc = ui
+	for i, iform := range instFormats {
+		if ui&iform.Mask != iform.Value {
+			continue
+		}
+		if ui&iform.DontCare != 0 {
+			if debugDecode {
+				log.Printf("Decode(%#x): unused bit is 1 for Op %s", ui, iform.Op)
+			}
+			// to match GNU objdump (libopcodes), we ignore don't care bits
+		}
+		for i, argfield := range iform.Args {
+			if argfield == nil {
+				break
+			}
+			inst.Args[i] = argfield.Parse(ui)
+		}
+		inst.Op = iform.Op
+		if debugDecode {
+			log.Printf("%#x: search entry %d", ui, i)
+			continue
+		}
+		break
+	}
+	if inst.Op == 0 {
+		return inst, errUnknown
+	}
+	return inst, nil
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/decode_test.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/decode_test.go
new file mode 100644
index 0000000..71f64d6
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/decode_test.go
@@ -0,0 +1,64 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ppc64asm
+
+import (
+	"encoding/binary"
+	"encoding/hex"
+	"io/ioutil"
+	"strings"
+	"testing"
+)
+
+func TestDecode(t *testing.T) {
+	data, err := ioutil.ReadFile("testdata/decode.txt")
+	if err != nil {
+		t.Fatal(err)
+	}
+	all := string(data)
+	for strings.Contains(all, "\t\t") {
+		all = strings.Replace(all, "\t\t", "\t", -1)
+	}
+	for _, line := range strings.Split(all, "\n") {
+		line = strings.TrimSpace(line)
+		if line == "" || strings.HasPrefix(line, "#") {
+			continue
+		}
+		f := strings.SplitN(line, "\t", 3)
+		i := strings.Index(f[0], "|")
+		if i < 0 {
+			t.Errorf("parsing %q: missing | separator", f[0])
+			continue
+		}
+		if i%2 != 0 {
+			t.Errorf("parsing %q: misaligned | separator", f[0])
+		}
+		size := i / 2
+		code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
+		if err != nil {
+			t.Errorf("parsing %q: %v", f[0], err)
+			continue
+		}
+		syntax, asm := f[1], f[2]
+		inst, err := Decode(code, binary.BigEndian)
+		var out string
+		if err != nil {
+			out = "error: " + err.Error()
+		} else {
+			switch syntax {
+			case "gnu":
+				out = GNUSyntax(inst)
+			//case "plan9":
+			//	out = GoSyntax(inst, 0, nil, nil)
+			default:
+				t.Errorf("unknown syntax %q", syntax)
+				continue
+			}
+		}
+		if out != asm || inst.Len != size {
+			t.Errorf("Decode(%s) [%s] = %s want %s", f[0], syntax, out, asm)
+		}
+	}
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/doc.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/doc.go
new file mode 100644
index 0000000..5f4ef7d
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/doc.go
@@ -0,0 +1,6 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package ppc64asm implements decoding of 64-bit PowerPC machine code.
+package ppc64asm
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/ext_test.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/ext_test.go
new file mode 100644
index 0000000..b553984
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/ext_test.go
@@ -0,0 +1,535 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Support for testing against external disassembler program.
+// Copied and simplified from rsc.io/arm/armasm/ext_test.go.
+
+package ppc64asm
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/binary"
+	"encoding/hex"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"math/rand"
+	"os"
+	"os/exec"
+	"regexp"
+	"runtime"
+	"strings"
+	"testing"
+	"time"
+)
+
+var (
+	printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
+	dumpTest   = flag.Bool("dump", false, "dump all encodings")
+	mismatch   = flag.Bool("mismatch", false, "log allowed mismatches")
+	longTest   = flag.Bool("long", false, "long test")
+	keep       = flag.Bool("keep", false, "keep object files around")
+	debug      = false
+)
+
+// A ExtInst represents a single decoded instruction parsed
+// from an external disassembler's output.
+type ExtInst struct {
+	addr uint32
+	enc  [4]byte
+	nenc int
+	text string
+}
+
+func (r ExtInst) String() string {
+	return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
+}
+
+// An ExtDis is a connection between an external disassembler and a test.
+type ExtDis struct {
+	Dec      chan ExtInst
+	File     *os.File
+	Size     int
+	KeepFile bool
+	Cmd      *exec.Cmd
+}
+
+// Run runs the given command - the external disassembler - and returns
+// a buffered reader of its standard output.
+func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
+	if *keep {
+		log.Printf("%s\n", strings.Join(cmd, " "))
+	}
+	ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
+	out, err := ext.Cmd.StdoutPipe()
+	if err != nil {
+		return nil, fmt.Errorf("stdoutpipe: %v", err)
+	}
+	if err := ext.Cmd.Start(); err != nil {
+		return nil, fmt.Errorf("exec: %v", err)
+	}
+
+	b := bufio.NewReaderSize(out, 1<<20)
+	return b, nil
+}
+
+// Wait waits for the command started with Run to exit.
+func (ext *ExtDis) Wait() error {
+	return ext.Cmd.Wait()
+}
+
+// testExtDis tests a set of byte sequences against an external disassembler.
+// The disassembler is expected to produce the given syntax and be run
+// in the given architecture mode (16, 32, or 64-bit).
+// The extdis function must start the external disassembler
+// and then parse its output, sending the parsed instructions on ext.Dec.
+// The generate function calls its argument f once for each byte sequence
+// to be tested. The generate function itself will be called twice, and it must
+// make the same sequence of calls to f each time.
+// When a disassembly does not match the internal decoding,
+// allowedMismatch determines whether this mismatch should be
+// allowed, or else considered an error.
+func testExtDis(
+	t *testing.T,
+	syntax string,
+	extdis func(ext *ExtDis) error,
+	generate func(f func([]byte)),
+	allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
+) {
+	start := time.Now()
+	ext := &ExtDis{
+		Dec: make(chan ExtInst),
+	}
+	errc := make(chan error)
+
+	// First pass: write instructions to input file for external disassembler.
+	file, f, size, err := writeInst(generate)
+	if err != nil {
+		t.Fatal(err)
+	}
+	ext.Size = size
+	ext.File = f
+	defer func() {
+		f.Close()
+		if !*keep {
+			os.Remove(file)
+		}
+	}()
+
+	// Second pass: compare disassembly against our decodings.
+	var (
+		totalTests  = 0
+		totalSkips  = 0
+		totalErrors = 0
+
+		errors = make([]string, 0, 100) // sampled errors, at most cap
+	)
+	go func() {
+		errc <- extdis(ext)
+	}()
+	generate(func(enc []byte) {
+		dec, ok := <-ext.Dec
+		if !ok {
+			t.Errorf("decoding stream ended early")
+			return
+		}
+		inst, text := disasm(syntax, pad(enc))
+		totalTests++
+		if *dumpTest {
+			fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
+		}
+		if text != dec.text || inst.Len != dec.nenc {
+			suffix := ""
+			if allowedMismatch(text, size, &inst, dec) {
+				totalSkips++
+				if !*mismatch {
+					return
+				}
+				suffix += " (allowed mismatch)"
+			}
+			totalErrors++
+			if len(errors) >= cap(errors) {
+				j := rand.Intn(totalErrors)
+				if j >= cap(errors) {
+					return
+				}
+				errors = append(errors[:j], errors[j+1:]...)
+			}
+			errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
+		}
+	})
+
+	if *mismatch {
+		totalErrors -= totalSkips
+	}
+
+	for _, b := range errors {
+		t.Log(b)
+	}
+
+	if totalErrors > 0 {
+		t.Fail()
+	}
+	t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
+
+	if err := <-errc; err != nil {
+		t.Fatalf("external disassembler: %v", err)
+	}
+
+}
+
+const start = 0x8000 // start address of text
+
+// writeInst writes the generated byte sequences to a new file
+// starting at offset start. That file is intended to be the input to
+// the external disassembler.
+func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
+	f, err = ioutil.TempFile("", "ppc64asm")
+	if err != nil {
+		return
+	}
+
+	file = f.Name()
+
+	f.Seek(start, 0)
+	w := bufio.NewWriter(f)
+	defer w.Flush()
+	size = 0
+	generate(func(x []byte) {
+		if len(x) > 4 {
+			x = x[:4]
+		}
+		if debug {
+			fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):])
+		}
+		w.Write(x)
+		w.Write(zeros[len(x):])
+		size += len(zeros)
+	})
+	return file, f, size, nil
+}
+
+var zeros = []byte{0, 0, 0, 0}
+
+// pad pads the code sequence with pops.
+func pad(enc []byte) []byte {
+	if len(enc) < 4 {
+		enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...)
+	}
+	return enc
+}
+
+// disasm returns the decoded instruction and text
+// for the given source bytes, using the given syntax and mode.
+func disasm(syntax string, src []byte) (inst Inst, text string) {
+	// If printTests is set, we record the coverage value
+	// before and after, and we write out the inputs for which
+	// coverage went up, in the format expected in testdata/decode.text.
+	// This produces a fairly small set of test cases that exercise nearly
+	// all the code.
+	var cover float64
+	if *printTests {
+		cover -= coverage()
+	}
+
+	inst, err := Decode(src, binary.BigEndian)
+	if err != nil {
+		text = "error: " + err.Error()
+	} else {
+		text = inst.String()
+		switch syntax {
+		//case "arm":
+		//	text = ARMSyntax(inst)
+		case "gnu":
+			text = GNUSyntax(inst)
+		//case "plan9":
+		//	text = GoSyntax(inst, 0, nil)
+		default:
+			text = "error: unknown syntax " + syntax
+		}
+	}
+
+	if *printTests {
+		cover += coverage()
+		if cover > 0 {
+			max := len(src)
+			if max > 4 && inst.Len <= 4 {
+				max = 4
+			}
+			fmt.Printf("%x|%x\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], syntax, text)
+		}
+	}
+
+	return
+}
+
+// coverage returns a floating point number denoting the
+// test coverage until now. The number increases when new code paths are exercised,
+// both in the Go program and in the decoder byte code.
+func coverage() float64 {
+	var f float64
+	f += testing.Coverage()
+	f += decodeCoverage()
+	return f
+}
+
+func decodeCoverage() float64 {
+	n := 0
+	for _, t := range decoderCover {
+		if t {
+			n++
+		}
+	}
+	return float64(1+n) / float64(1+len(decoderCover))
+}
+
+// Helpers for writing disassembler output parsers.
+
+// hasPrefix reports whether any of the space-separated words in the text s
+// begins with any of the given prefixes.
+func hasPrefix(s string, prefixes ...string) bool {
+	for _, prefix := range prefixes {
+		for s := s; s != ""; {
+			if strings.HasPrefix(s, prefix) {
+				return true
+			}
+			i := strings.Index(s, " ")
+			if i < 0 {
+				break
+			}
+			s = s[i+1:]
+		}
+	}
+	return false
+}
+
+// contains reports whether the text s contains any of the given substrings.
+func contains(s string, substrings ...string) bool {
+	for _, sub := range substrings {
+		if strings.Contains(s, sub) {
+			return true
+		}
+	}
+	return false
+}
+
+// isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
+func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
+
+// parseHex parses the hexadecimal byte dump in hex,
+// appending the parsed bytes to raw and returning the updated slice.
+// The returned bool signals whether any invalid hex was found.
+// Spaces and tabs between bytes are okay but any other non-hex is not.
+func parseHex(hex []byte, raw []byte) ([]byte, bool) {
+	hex = trimSpace(hex)
+	for j := 0; j < len(hex); {
+		for hex[j] == ' ' || hex[j] == '\t' {
+			j++
+		}
+		if j >= len(hex) {
+			break
+		}
+		if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
+			return nil, false
+		}
+		raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
+		j += 2
+	}
+	return raw, true
+}
+
+var unhex = [256]byte{
+	'0': 0,
+	'1': 1,
+	'2': 2,
+	'3': 3,
+	'4': 4,
+	'5': 5,
+	'6': 6,
+	'7': 7,
+	'8': 8,
+	'9': 9,
+	'A': 10,
+	'B': 11,
+	'C': 12,
+	'D': 13,
+	'E': 14,
+	'F': 15,
+	'a': 10,
+	'b': 11,
+	'c': 12,
+	'd': 13,
+	'e': 14,
+	'f': 15,
+}
+
+// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
+func index(s []byte, t string) int {
+	i := 0
+	for {
+		j := bytes.IndexByte(s[i:], t[0])
+		if j < 0 {
+			return -1
+		}
+		i = i + j
+		if i+len(t) > len(s) {
+			return -1
+		}
+		for k := 1; k < len(t); k++ {
+			if s[i+k] != t[k] {
+				goto nomatch
+			}
+		}
+		return i
+	nomatch:
+		i++
+	}
+}
+
+// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
+// If s must be rewritten, it is rewritten in place.
+func fixSpace(s []byte) []byte {
+	s = trimSpace(s)
+	for i := 0; i < len(s); i++ {
+		if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
+			goto Fix
+		}
+	}
+	return s
+
+Fix:
+	b := s
+	w := 0
+	for i := 0; i < len(s); i++ {
+		c := s[i]
+		if c == '\t' || c == '\n' {
+			c = ' '
+		}
+		if c == ' ' && w > 0 && b[w-1] == ' ' {
+			continue
+		}
+		b[w] = c
+		w++
+	}
+	if w > 0 && b[w-1] == ' ' {
+		w--
+	}
+	return b[:w]
+}
+
+// trimSpace trims leading and trailing space from s, returning a subslice of s.
+func trimSpace(s []byte) []byte {
+	j := len(s)
+	for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
+		j--
+	}
+	i := 0
+	for i < j && (s[i] == ' ' || s[i] == '\t') {
+		i++
+	}
+	return s[i:j]
+}
+
+// pcrel matches instructions using relative addressing mode.
+var (
+	pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bc)[^ac ]* (?:(?:[0-9]{1,2},)|(?:[0-7]\*)|\+|lt|gt|eq|so|cr[0-7]|,)*)0x([0-9a-f]+)$`)
+)
+
+// Generators.
+//
+// The test cases are described as functions that invoke a callback repeatedly,
+// with a new input sequence each time. These helpers make writing those
+// a little easier.
+
+// randomCases generates random instructions.
+func randomCases(t *testing.T) func(func([]byte)) {
+	return func(try func([]byte)) {
+		// All the strides are relatively prime to 2 and therefore to 2²⁸,
+		// so we will not repeat any instructions until we have tried all 2²⁸.
+		// Using a stride other than 1 is meant to visit the instructions in a
+		// pseudorandom order, which gives better variety in the set of
+		// test cases chosen by -printtests.
+		stride := uint32(10007)
+		n := 1 << 28 / 7
+		if testing.Short() {
+			stride = 100003
+			n = 1 << 28 / 1001
+		} else if *longTest {
+			stride = 2000033
+			n = 1 << 29
+		}
+		x := uint32(0)
+		for i := 0; i < n; i++ {
+			enc := (x%15)<<28 | x&(1<<28-1)
+			try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)})
+			x += stride
+		}
+	}
+}
+
+// hexCases generates the cases written in hexadecimal in the encoded string.
+// Spaces in 'encoded' separate entire test cases, not individual bytes.
+func hexCases(t *testing.T, encoded string) func(func([]byte)) {
+	return func(try func([]byte)) {
+		for _, x := range strings.Fields(encoded) {
+			src, err := hex.DecodeString(x)
+			if err != nil {
+				t.Errorf("parsing %q: %v", x, err)
+			}
+			try(src)
+		}
+	}
+}
+
+// testdataCases generates the test cases recorded in testdata/decode.txt.
+// It only uses the inputs; it ignores the answers recorded in that file.
+func testdataCases(t *testing.T) func(func([]byte)) {
+	var codes [][]byte
+	data, err := ioutil.ReadFile("testdata/decode.txt")
+	if err != nil {
+		t.Fatal(err)
+	}
+	for _, line := range strings.Split(string(data), "\n") {
+		line = strings.TrimSpace(line)
+		if line == "" || strings.HasPrefix(line, "#") {
+			continue
+		}
+		f := strings.Fields(line)[0]
+		i := strings.Index(f, "|")
+		if i < 0 {
+			t.Errorf("parsing %q: missing | separator", f)
+			continue
+		}
+		if i%2 != 0 {
+			t.Errorf("parsing %q: misaligned | separator", f)
+		}
+		code, err := hex.DecodeString(f[:i] + f[i+1:])
+		if err != nil {
+			t.Errorf("parsing %q: %v", f, err)
+			continue
+		}
+		codes = append(codes, code)
+	}
+
+	return func(try func([]byte)) {
+		for _, code := range codes {
+			try(code)
+		}
+	}
+}
+
+func caller(skip int) string {
+	pc, _, _, _ := runtime.Caller(skip)
+	f := runtime.FuncForPC(pc)
+	name := "?"
+	if f != nil {
+		name = f.Name()
+		if i := strings.LastIndex(name, "."); i >= 0 {
+			name = name[i+1:]
+		}
+	}
+	return name
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/field.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/field.go
new file mode 100644
index 0000000..26a4fdf
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/field.go
@@ -0,0 +1,84 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ppc64asm
+
+import (
+	"fmt"
+	"strings"
+)
+
+// A BitField is a bit-field in a 32-bit word.
+// Bits are counted from 0 from the MSB to 31 as the LSB.
+type BitField struct {
+	Offs uint8 // the offset of the left-most bit.
+	Bits uint8 // length in bits.
+}
+
+func (b BitField) String() string {
+	if b.Bits > 1 {
+		return fmt.Sprintf("[%d:%d]", b.Offs, int(b.Offs+b.Bits)-1)
+	} else if b.Bits == 1 {
+		return fmt.Sprintf("[%d]", b.Offs)
+	} else {
+		return fmt.Sprintf("[%d, len=0]", b.Offs)
+	}
+}
+
+// Parse extracts the bitfield b from i, and return it as an unsigned integer.
+// Parse will panic if b is invalid.
+func (b BitField) Parse(i uint32) uint32 {
+	if b.Bits > 32 || b.Bits == 0 || b.Offs > 31 || b.Offs+b.Bits > 32 {
+		panic(fmt.Sprintf("invalid bitfiled %v", b))
+	}
+	return (i >> (32 - b.Offs - b.Bits)) & ((1 << b.Bits) - 1)
+}
+
+// ParseSigned extracts the bitfield b from i, and return it as a signed integer.
+// ParseSigned will panic if b is invalid.
+func (b BitField) ParseSigned(i uint32) int32 {
+	u := int32(b.Parse(i))
+	return u << (32 - b.Bits) >> (32 - b.Bits)
+}
+
+// BitFields is a series of BitFields representing a single number.
+type BitFields []BitField
+
+func (bs BitFields) String() string {
+	ss := make([]string, len(bs))
+	for i, bf := range bs {
+		ss[i] = bf.String()
+	}
+	return fmt.Sprintf("<%s>", strings.Join(ss, "|"))
+}
+
+func (bs *BitFields) Append(b BitField) {
+	*bs = append(*bs, b)
+}
+
+// parse extracts the bitfields from i, concatenate them and return the result
+// as an unsigned integer and the total length of all the bitfields.
+// parse will panic if any bitfield in b is invalid, but it doesn't check if
+// the sequence of bitfields is reasonable.
+func (bs BitFields) parse(i uint32) (u uint32, Bits uint8) {
+	for _, b := range bs {
+		u = (u << b.Bits) | b.Parse(i)
+		Bits += b.Bits
+	}
+	return u, Bits
+}
+
+// Parse extracts the bitfields from i, concatenate them and return the result
+// as an unsigned integer. Parse will panic if any bitfield in b is invalid.
+func (bs BitFields) Parse(i uint32) uint32 {
+	u, _ := bs.parse(i)
+	return u
+}
+
+// Parse extracts the bitfields from i, concatenate them and return the result
+// as a signed integer. Parse will panic if any bitfield in b is invalid.
+func (bs BitFields) ParseSigned(i uint32) int32 {
+	u, l := bs.parse(i)
+	return int32(u) << (32 - l) >> (32 - l)
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/field_test.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/field_test.go
new file mode 100644
index 0000000..14eb2f8
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/field_test.go
@@ -0,0 +1,60 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ppc64asm
+
+import (
+	"testing"
+)
+
+func panicOrNot(f func()) (panicked bool) {
+	defer func() {
+		if err := recover(); err != nil {
+			panicked = true
+		}
+	}()
+	f()
+	return false
+}
+
+func TestBitField(t *testing.T) {
+	var tests = []struct {
+		b    BitField
+		i    uint32 // input
+		u    uint32 // unsigned output
+		s    int32  // signed output
+		fail bool   // if the check should panic
+	}{
+		{BitField{0, 0}, 0, 0, 0, true},
+		{BitField{31, 2}, 0, 0, 0, true},
+		{BitField{31, 1}, 1, 1, -1, false},
+		{BitField{29, 2}, 0 << 1, 0, 0, false},
+		{BitField{29, 2}, 1 << 1, 1, 1, false},
+		{BitField{29, 2}, 2 << 1, 2, -2, false},
+		{BitField{29, 2}, 3 << 1, 3, -1, false},
+		{BitField{0, 32}, 1<<32 - 1, 1<<32 - 1, -1, false},
+		{BitField{16, 3}, 1 << 15, 4, -4, false},
+	}
+	for i, tst := range tests {
+		var (
+			ou uint32
+			os int32
+		)
+		failed := panicOrNot(func() {
+			ou = tst.b.Parse(tst.i)
+			os = tst.b.ParseSigned(tst.i)
+		})
+		if failed != tst.fail {
+			t.Errorf("case %d: %v: fail test failed, got %v, expected %v", i, tst.b, failed, tst.fail)
+			continue
+		}
+		if ou != tst.u {
+			t.Errorf("case %d: %v.Parse(%d) returned %d, expected %d", i, tst.b, tst.i, ou, tst.u)
+			continue
+		}
+		if os != tst.s {
+			t.Errorf("case %d: %v.ParseSigned(%d) returned %d, expected %d", i, tst.b, tst.i, os, tst.s)
+		}
+	}
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/gnu.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/gnu.go
new file mode 100644
index 0000000..63be379
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/gnu.go
@@ -0,0 +1,125 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ppc64asm
+
+import (
+	"bytes"
+	"fmt"
+	"strings"
+)
+
+// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
+// This form typically matches the syntax defined in the Power ISA Reference Manual.
+func GNUSyntax(inst Inst) string {
+	var buf bytes.Buffer
+	if inst.Op == 0 {
+		return "error: unkown instruction"
+	}
+	buf.WriteString(inst.Op.String())
+	sep := " "
+	for i, arg := range inst.Args[:] {
+		if arg == nil {
+			break
+		}
+		text := gnuArg(&inst, i, arg)
+		if text == "" {
+			continue
+		}
+		buf.WriteString(sep)
+		sep = ","
+		buf.WriteString(text)
+	}
+	return buf.String()
+}
+
+// gnuArg formats arg (which is the argIndex's arg in inst) according to GNU rules.
+// NOTE: because GNUSyntax is the only caller of this func, and it receives a copy
+//       of inst, it's ok to modify inst.Args here.
+func gnuArg(inst *Inst, argIndex int, arg Arg) string {
+	// special cases for load/store instructions
+	if _, ok := arg.(Offset); ok {
+		if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
+			panic(fmt.Errorf("wrong table: offset not followed by register"))
+		}
+	}
+	switch arg := arg.(type) {
+	case Reg:
+		if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
+			return "0"
+		}
+		return arg.String()
+	case CondReg:
+		if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
+			return "" // don't show cr0 for cmp instructions
+		} else if arg >= CR0 {
+			return fmt.Sprintf("cr%d", int(arg-CR0))
+		}
+		bit := [4]string{"lt", "gt", "eq", "so"}[(arg-Cond0LT)%4]
+		if arg <= Cond0SO {
+			return bit
+		}
+		return fmt.Sprintf("4*cr%d+%s", int(arg-Cond0LT)/4, bit)
+	case Imm:
+		return fmt.Sprintf("%d", arg)
+	case SpReg:
+		return fmt.Sprintf("%d", int(arg))
+	case PCRel:
+		return fmt.Sprintf(".%+#x", int(arg))
+	case Label:
+		return fmt.Sprintf("%#x", uint32(arg))
+	case Offset:
+		reg := inst.Args[argIndex+1].(Reg)
+		removeArg(inst, argIndex+1)
+		if reg == R0 {
+			return fmt.Sprintf("%d(0)", int(arg))
+		}
+		return fmt.Sprintf("%d(r%d)", int(arg), reg-R0)
+	}
+	return fmt.Sprintf("???(%v)", arg)
+}
+
+// removeArg removes the arg in inst.Args[index].
+func removeArg(inst *Inst, index int) {
+	for i := index; i < len(inst.Args); i++ {
+		if i+1 < len(inst.Args) {
+			inst.Args[i] = inst.Args[i+1]
+		} else {
+			inst.Args[i] = nil
+		}
+	}
+}
+
+// isLoadStoreOp returns true if op is a load or store instruction
+func isLoadStoreOp(op Op) bool {
+	switch op {
+	case LBZ, LBZU, LBZX, LBZUX:
+		return true
+	case LHZ, LHZU, LHZX, LHZUX:
+		return true
+	case LHA, LHAU, LHAX, LHAUX:
+		return true
+	case LWZ, LWZU, LWZX, LWZUX:
+		return true
+	case LWA, LWAX, LWAUX:
+		return true
+	case LD, LDU, LDX, LDUX:
+		return true
+	case LQ:
+		return true
+	case STB, STBU, STBX, STBUX:
+		return true
+	case STH, STHU, STHX, STHUX:
+		return true
+	case STW, STWU, STWX, STWUX:
+		return true
+	case STD, STDU, STDX, STDUX:
+		return true
+	case STQ:
+		return true
+	case LHBRX, LWBRX, STHBRX, STWBRX:
+		return true
+	}
+	return false
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/inst.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/inst.go
new file mode 100644
index 0000000..bd86b92
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/inst.go
@@ -0,0 +1,344 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ppc64asm
+
+import (
+	"bytes"
+	"fmt"
+)
+
+type Inst struct {
+	Op   Op     // Opcode mnemonic
+	Enc  uint32 // Raw encoding bits
+	Len  int    // Length of encoding in bytes.
+	Args Args   // Instruction arguments, in Power ISA manual order.
+}
+
+func (i Inst) String() string {
+	var buf bytes.Buffer
+	buf.WriteString(i.Op.String())
+	for j, arg := range i.Args {
+		if arg == nil {
+			break
+		}
+		if j == 0 {
+			buf.WriteString(" ")
+		} else {
+			buf.WriteString(", ")
+		}
+		buf.WriteString(arg.String())
+	}
+	return buf.String()
+}
+
+// An Op is an instruction operation.
+type Op uint16
+
+func (o Op) String() string {
+	if int(o) >= len(opstr) || opstr[o] == "" {
+		return fmt.Sprintf("Op(%d)", int(o))
+	}
+	return opstr[o]
+}
+
+// An Arg is a single instruction argument, one of these types: Reg, CondReg, SpReg, Imm, PCRel, Label, or Offset.
+type Arg interface {
+	IsArg()
+	String() string
+}
+
+// An Args holds the instruction arguments.
+// If an instruction has fewer than 4 arguments,
+// the final elements in the array are nil.
+type Args [5]Arg
+
+// A Reg is a single register. The zero value means R0, not the absence of a register.
+// It also includes special registers.
+type Reg uint16
+
+const (
+	_ Reg = iota
+	R0
+	R1
+	R2
+	R3
+	R4
+	R5
+	R6
+	R7
+	R8
+	R9
+	R10
+	R11
+	R12
+	R13
+	R14
+	R15
+	R16
+	R17
+	R18
+	R19
+	R20
+	R21
+	R22
+	R23
+	R24
+	R25
+	R26
+	R27
+	R28
+	R29
+	R30
+	R31
+	F0
+	F1
+	F2
+	F3
+	F4
+	F5
+	F6
+	F7
+	F8
+	F9
+	F10
+	F11
+	F12
+	F13
+	F14
+	F15
+	F16
+	F17
+	F18
+	F19
+	F20
+	F21
+	F22
+	F23
+	F24
+	F25
+	F26
+	F27
+	F28
+	F29
+	F30
+	F31
+	V0 // VSX extension, F0 is V0[0:63].
+	V1
+	V2
+	V3
+	V4
+	V5
+	V6
+	V7
+	V8
+	V9
+	V10
+	V11
+	V12
+	V13
+	V14
+	V15
+	V16
+	V17
+	V18
+	V19
+	V20
+	V21
+	V22
+	V23
+	V24
+	V25
+	V26
+	V27
+	V28
+	V29
+	V30
+	V31
+	VS0
+	VS1
+	VS2
+	VS3
+	VS4
+	VS5
+	VS6
+	VS7
+	VS8
+	VS9
+	VS10
+	VS11
+	VS12
+	VS13
+	VS14
+	VS15
+	VS16
+	VS17
+	VS18
+	VS19
+	VS20
+	VS21
+	VS22
+	VS23
+	VS24
+	VS25
+	VS26
+	VS27
+	VS28
+	VS29
+	VS30
+	VS31
+	VS32
+	VS33
+	VS34
+	VS35
+	VS36
+	VS37
+	VS38
+	VS39
+	VS40
+	VS41
+	VS42
+	VS43
+	VS44
+	VS45
+	VS46
+	VS47
+	VS48
+	VS49
+	VS50
+	VS51
+	VS52
+	VS53
+	VS54
+	VS55
+	VS56
+	VS57
+	VS58
+	VS59
+	VS60
+	VS61
+	VS62
+	VS63
+)
+
+func (Reg) IsArg() {}
+func (r Reg) String() string {
+	switch {
+	case R0 <= r && r <= R31:
+		return fmt.Sprintf("r%d", int(r-R0))
+	case F0 <= r && r <= F31:
+		return fmt.Sprintf("f%d", int(r-F0))
+	case V0 <= r && r <= V31:
+		return fmt.Sprintf("v%d", int(r-V0))
+	case VS0 <= r && r <= VS63:
+		return fmt.Sprintf("vs%d", int(r-VS0))
+	default:
+		return fmt.Sprintf("Reg(%d)", int(r))
+	}
+}
+
+// CondReg is a bit or field in the conditon register.
+type CondReg int8
+
+const (
+	_ CondReg = iota
+	// Condition Regster bits
+	Cond0LT
+	Cond0GT
+	Cond0EQ
+	Cond0SO
+	Cond1LT
+	Cond1GT
+	Cond1EQ
+	Cond1SO
+	Cond2LT
+	Cond2GT
+	Cond2EQ
+	Cond2SO
+	Cond3LT
+	Cond3GT
+	Cond3EQ
+	Cond3SO
+	Cond4LT
+	Cond4GT
+	Cond4EQ
+	Cond4SO
+	Cond5LT
+	Cond5GT
+	Cond5EQ
+	Cond5SO
+	Cond6LT
+	Cond6GT
+	Cond6EQ
+	Cond6SO
+	Cond7LT
+	Cond7GT
+	Cond7EQ
+	Cond7SO
+	// Condition Register Fields
+	CR0
+	CR1
+	CR2
+	CR3
+	CR4
+	CR5
+	CR6
+	CR7
+)
+
+func (CondReg) IsArg() {}
+func (c CondReg) String() string {
+	switch {
+	default:
+		return fmt.Sprintf("CondReg(%d)", int(c))
+	case c >= CR0:
+		return fmt.Sprintf("CR%d", int(c-CR0))
+	case c >= Cond0LT && c < CR0:
+		return fmt.Sprintf("Cond%d%s", int((c-Cond0LT)/4), [4]string{"LT", "GT", "EQ", "SO"}[(c-Cond0LT)%4])
+	}
+}
+
+// SpReg is a special register, its meaning depends on Op.
+type SpReg uint16
+
+const (
+	SpRegZero SpReg = 0
+)
+
+func (SpReg) IsArg() {}
+func (s SpReg) String() string {
+	return fmt.Sprintf("SpReg(%d)", int(s))
+}
+
+// PCRel is a PC-relative offset, used only in branch instructions.
+type PCRel int32
+
+func (PCRel) IsArg() {}
+func (r PCRel) String() string {
+	return fmt.Sprintf("PC%+#x", int32(r))
+}
+
+// A Label is a code (text) address, used only in absolute branch instructions.
+type Label uint32
+
+func (Label) IsArg() {}
+func (l Label) String() string {
+	return fmt.Sprintf("%#x", uint32(l))
+}
+
+// Imm represents an immediate number.
+type Imm int32
+
+func (Imm) IsArg() {}
+func (i Imm) String() string {
+	return fmt.Sprintf("%d", int32(i))
+}
+
+// Offset represents a memory offset immediate.
+type Offset int32
+
+func (Offset) IsArg() {}
+func (o Offset) String() string {
+	return fmt.Sprintf("%+d", int32(o))
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/objdump_test.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/objdump_test.go
new file mode 100644
index 0000000..ae825fd
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/objdump_test.go
@@ -0,0 +1,133 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ppc64asm
+
+import (
+	"encoding/binary"
+	"strings"
+	"testing"
+)
+
+func TestObjdumpPowerTestdata(t *testing.T) { testObjdump(t, testdataCases(t)) }
+func TestObjdumpPowerManual(t *testing.T)   { testObjdump(t, hexCases(t, objdumpManualTests)) }
+
+// Disable this for now since generating all possible bit combinations within a word
+// generates lots of ppc64x instructions not possible with golang so not worth supporting..
+//func TestObjdumpPowerRandom(t *testing.T)   { testObjdump(t, randomCases(t)) }
+
+// objdumpManualTests holds test cases that will be run by TestObjdumpARMManual.
+// If you are debugging a few cases that turned up in a longer run, it can be useful
+// to list them here and then use -run=Manual, particularly with tracing enabled.
+// Note that these are byte sequences, so they must be reversed from the usual
+// word presentation.
+var objdumpManualTests = `
+6d746162
+4c040000
+88000017
+`
+
+// allowedMismatchObjdump reports whether the mismatch between text and dec
+// should be allowed by the test.
+func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool {
+	if hasPrefix(dec.text, deleted...) {
+		return true
+	}
+
+	// we support more instructions than binutils
+	if strings.Contains(dec.text, ".long") {
+		return true
+	}
+
+	if hasPrefix(text, "error:") {
+		if hasPrefix(dec.text, unsupported...) {
+			return true
+		}
+	}
+
+	switch inst.Op {
+	case BC, BCA, BL, BLA, BCL, BCLA, TDI, TWI, TW, TD:
+		return true // TODO(minux): we lack the support for extended opcodes here
+	case RLWNM, RLWNM_, RLDICL, RLDICL_, RLWINM, RLWINM_, RLDCL, RLDCL_:
+		return true // TODO(minux): we lack the support for extended opcodes here
+	case DCBTST, DCBT:
+		return true // objdump uses the embedded argument order, we use the server argument order
+	case MTFSF, MTFSF_: // objdump doesn't show the last two arguments
+		return true
+	case VSPLTB, VSPLTH, VSPLTW: // objdump generates unreasonable result "vspltw v6,v19,4" for 10c49a8c, the last 4 should be 0.
+		return true
+	}
+	if hasPrefix(text, "evm", "evl", "efs") { // objdump will disassemble them wrong (e.g. evmhoumia as vsldoi)
+		return true
+	}
+
+	if len(dec.enc) >= 4 {
+		_ = binary.BigEndian.Uint32(dec.enc[:4])
+	}
+
+	return false
+}
+
+// Instructions known to libopcodes (or xed) but not to us.
+// TODO(minux): those single precision instructions are missing from ppc64.csv
+// those data cache instructions are deprecated, but must be treated as no-ops, see 4.3.2.1 pg. 774.
+var unsupported = strings.Fields(`
+fmsubs
+fmsubs.
+fnmadds
+fnmadds.
+fnmsubs
+fnmsubs.
+fmuls
+fmuls.
+fdivs
+fdivs.
+fadds
+fadds.
+fsubs
+fsubs.
+dst
+dstst
+dssall
+`)
+
+// Instructions explicitly dropped in Power ISA that were in POWER architecture.
+// See A.30 Deleted Instructions and A.31 Discontiued Opcodes
+var deleted = strings.Fields(`
+abs
+clcs
+clf
+cli
+dclst
+div
+divs
+doz
+dozi
+lscbx
+maskg
+maskir
+mfsri
+mul
+nabs
+rac
+rfi
+rfsvc
+rlmi
+rrib
+sle
+sleq
+sliq
+slliq
+sllq
+slq
+sraiq
+sraq
+sre
+srea
+sreq
+sriq
+srliq
+srlq
+srq
+maskg`)
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/objdumpext_test.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/objdumpext_test.go
new file mode 100644
index 0000000..7483543
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/objdumpext_test.go
@@ -0,0 +1,255 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Copied and simplified from rsc.io/arm/armasm/objdumpext_test.go.
+
+package ppc64asm
+
+import (
+	"bytes"
+	"debug/elf"
+	"encoding/binary"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"runtime"
+	"strconv"
+	"strings"
+	"testing"
+)
+
+const objdumpPath = "/usr/bin/objdump"
+
+func testObjdump(t *testing.T, generate func(func([]byte))) {
+	if testing.Short() {
+		t.Skip("skipping objdump test in short mode")
+	}
+	if runtime.GOARCH != "ppc64le" && runtime.GOARCH != "ppc64" {
+		t.Skip("skipping; test requires host tool objdump for ppc64 or ppc64le")
+	}
+	if _, err := os.Stat(objdumpPath); err != nil {
+		t.Skip(err)
+	}
+
+	testExtDis(t, "gnu", objdump, generate, allowedMismatchObjdump)
+}
+
+func objdump(ext *ExtDis) error {
+	// File already written with instructions; add ELF header.
+	if err := writeELF64(ext.File, ext.Size); err != nil {
+		return err
+	}
+
+	b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
+	if err != nil {
+		return err
+	}
+
+	var (
+		nmatch  int
+		reading bool
+		next    uint32 = start
+		addr    uint32
+		encbuf  [4]byte
+		enc     []byte
+		text    string
+	)
+	flush := func() {
+		if addr == next {
+			if m := pcrel.FindStringSubmatch(text); m != nil {
+				targ, _ := strconv.ParseUint(m[2], 16, 64)
+				text = fmt.Sprintf("%s.%+#x", m[1], int32(uint32(targ)-addr))
+			}
+			if strings.HasPrefix(text, "stmia") {
+				text = "stm" + text[5:]
+			}
+			if strings.HasPrefix(text, "stmfd") {
+				text = "stmdb" + text[5:]
+			}
+			if strings.HasPrefix(text, "ldmfd") {
+				text = "ldm" + text[5:]
+			}
+			text = strings.Replace(text, "#0.0", "#0", -1)
+			if text == "undefined" && len(enc) == 4 {
+				text = "error: unknown instruction"
+				enc = nil
+			}
+			if len(enc) == 4 {
+				// prints as word but we want to record bytes
+				enc[0], enc[3] = enc[3], enc[0]
+				enc[1], enc[2] = enc[2], enc[1]
+			}
+			ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
+			encbuf = [4]byte{}
+			enc = nil
+			next += 4
+		}
+	}
+	var textangle = []byte("<.text>:")
+	for {
+		line, err := b.ReadSlice('\n')
+		if err != nil {
+			if err == io.EOF {
+				break
+			}
+			return fmt.Errorf("reading objdump output: %v", err)
+		}
+		if bytes.Contains(line, textangle) {
+			reading = true
+			continue
+		}
+		if !reading {
+			continue
+		}
+		if debug {
+			os.Stdout.Write(line)
+		}
+		if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
+			enc = enc1
+			continue
+		}
+		flush()
+		nmatch++
+		addr, enc, text = parseLine(line, encbuf[:0])
+		if addr > next {
+			return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
+		}
+	}
+	flush()
+	if next != start+uint32(ext.Size) {
+		return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
+	}
+	if err := ext.Wait(); err != nil {
+		return fmt.Errorf("exec: %v", err)
+	}
+
+	return nil
+}
+
+var (
+	undefined      = []byte("<UNDEFINED>")
+	unpredictable  = []byte("<UNPREDICTABLE>")
+	illegalShifter = []byte("<illegal shifter operand>")
+)
+
+func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
+	oline := line
+	i := index(line, ":\t")
+	if i < 0 {
+		log.Fatalf("cannot parse disassembly: %q", oline)
+	}
+	x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
+	if err != nil {
+		log.Fatalf("cannot parse disassembly: %q", oline)
+	}
+	addr = uint32(x)
+	line = line[i+2:]
+	i = bytes.IndexByte(line, '\t')
+	if i < 0 {
+		log.Fatalf("cannot parse disassembly: %q", oline)
+	}
+	enc, ok := parseHex(line[:i], encstart)
+	if !ok {
+		log.Fatalf("cannot parse disassembly: %q", oline)
+	}
+	line = trimSpace(line[i:])
+	if bytes.Contains(line, undefined) {
+		text = "undefined"
+		return
+	}
+	if bytes.Contains(line, illegalShifter) {
+		text = "undefined"
+		return
+	}
+	if false && bytes.Contains(line, unpredictable) {
+		text = "unpredictable"
+		return
+	}
+	if i := bytes.IndexByte(line, ';'); i >= 0 {
+		line = trimSpace(line[:i])
+	}
+	text = string(fixSpace(line))
+	return
+}
+
+func parseContinuation(line []byte, enc []byte) []byte {
+	i := index(line, ":\t")
+	if i < 0 {
+		return nil
+	}
+	line = line[i+1:]
+	enc, _ = parseHex(line, enc)
+	return enc
+}
+
+// writeELF64 writes an ELF64 header to the file,
+// describing a text segment that starts at start
+// and extends for size bytes.
+func writeELF64(f *os.File, size int) error {
+	f.Seek(0, 0)
+	var hdr elf.Header64
+	var prog elf.Prog64
+	var sect elf.Section64
+	var buf bytes.Buffer
+	binary.Write(&buf, binary.BigEndian, &hdr)
+	off1 := buf.Len()
+	binary.Write(&buf, binary.BigEndian, &prog)
+	off2 := buf.Len()
+	binary.Write(&buf, binary.BigEndian, &sect)
+	off3 := buf.Len()
+	buf.Reset()
+	data := byte(elf.ELFDATA2MSB)
+	hdr = elf.Header64{
+		Ident:     [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
+		Type:      2,
+		Machine:   uint16(elf.EM_PPC64),
+		Version:   1,
+		Entry:     start,
+		Phoff:     uint64(off1),
+		Shoff:     uint64(off2),
+		Flags:     0x05000002,
+		Ehsize:    uint16(off1),
+		Phentsize: uint16(off2 - off1),
+		Phnum:     1,
+		Shentsize: uint16(off3 - off2),
+		Shnum:     3,
+		Shstrndx:  2,
+	}
+	binary.Write(&buf, binary.BigEndian, &hdr)
+	prog = elf.Prog64{
+		Type:   1,
+		Off:    start,
+		Vaddr:  start,
+		Paddr:  start,
+		Filesz: uint64(size),
+		Memsz:  uint64(size),
+		Flags:  5,
+		Align:  start,
+	}
+	binary.Write(&buf, binary.BigEndian, &prog)
+	binary.Write(&buf, binary.BigEndian, &sect) // NULL section
+	sect = elf.Section64{
+		Name:      1,
+		Type:      uint32(elf.SHT_PROGBITS),
+		Addr:      start,
+		Off:       start,
+		Size:      uint64(size),
+		Flags:     uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
+		Addralign: 4,
+	}
+	binary.Write(&buf, binary.BigEndian, &sect) // .text
+	sect = elf.Section64{
+		Name:      uint32(len("\x00.text\x00")),
+		Type:      uint32(elf.SHT_STRTAB),
+		Addr:      0,
+		Off:       uint64(off2 + (off3-off2)*3),
+		Size:      uint64(len("\x00.text\x00.shstrtab\x00")),
+		Addralign: 1,
+	}
+	binary.Write(&buf, binary.BigEndian, &sect)
+	buf.WriteString("\x00.text\x00.shstrtab\x00")
+	f.Write(buf.Bytes())
+	return nil
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/plan9.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/plan9.go
new file mode 100644
index 0000000..57a761e
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/plan9.go
@@ -0,0 +1,172 @@
+// Copyright 2015 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ppc64asm
+
+import (
+	"fmt"
+	"strings"
+)
+
+// GoSyntax returns the Go assembler syntax for the instruction.
+// The pc is the program counter of the first instruction, used for expanding
+// PC-relative addresses into absolute ones.
+// The symname function queries the symbol table for the program
+// being disassembled. It returns the name and base address of the symbol
+// containing the target, if any; otherwise it returns "", 0.
+func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) string {
+	if symname == nil {
+		symname = func(uint64) (string, uint64) { return "", 0 }
+	}
+	if inst.Op == 0 {
+		return "?"
+	}
+	var args []string
+	for i, a := range inst.Args[:] {
+		if a == nil {
+			break
+		}
+		if s := plan9Arg(&inst, i, pc, a, symname); s != "" {
+			args = append(args, s)
+		}
+	}
+	var op string
+	op = plan9OpMap[inst.Op]
+	if op == "" {
+		op = strings.ToUpper(inst.Op.String())
+	}
+	// laid out the instruction
+	switch inst.Op {
+	default: // dst, sA, sB, ...
+		if len(args) == 0 {
+			return op
+		} else if len(args) == 1 {
+			return fmt.Sprintf("%s %s", op, args[0])
+		}
+		args = append(args, args[0])
+		return op + " " + strings.Join(args[1:], ", ")
+	// store instructions always have the memory operand at the end, no need to reorder
+	case STB, STBU, STBX, STBUX,
+		STH, STHU, STHX, STHUX,
+		STW, STWU, STWX, STWUX,
+		STD, STDU, STDX, STDUX,
+		STQ,
+		STHBRX, STWBRX:
+		return op + " " + strings.Join(args, ", ")
+	// branch instructions needs additional handling
+	case BCLR:
+		if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
+			return "RET"
+		}
+		return op + " " + strings.Join(args, ", ")
+	case BC:
+		if int(inst.Args[0].(Imm))&0x1c == 12 { // jump on cond bit set
+			return fmt.Sprintf("B%s %s", args[1], args[2])
+		} else if int(inst.Args[0].(Imm))&0x1c == 4 && revCondMap[args[1]] != "" { // jump on cond bit not set
+			return fmt.Sprintf("B%s %s", revCondMap[args[1]], args[2])
+		}
+		return op + " " + strings.Join(args, ", ")
+	case BCCTR:
+		if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
+			return "BR (CTR)"
+		}
+		return op + " " + strings.Join(args, ", ")
+	case BCCTRL:
+		if int(inst.Args[0].(Imm))&20 == 20 { // unconditional
+			return "BL (CTR)"
+		}
+		return op + " " + strings.Join(args, ", ")
+	case BCA, BCL, BCLA, BCLRL, BCTAR, BCTARL:
+		return op + " " + strings.Join(args, ", ")
+	}
+}
+
+// plan9Arg formats arg (which is the argIndex's arg in inst) according to Plan 9 rules.
+// NOTE: because Plan9Syntax is the only caller of this func, and it receives a copy
+//       of inst, it's ok to modify inst.Args here.
+func plan9Arg(inst *Inst, argIndex int, pc uint64, arg Arg, symname func(uint64) (string, uint64)) string {
+	// special cases for load/store instructions
+	if _, ok := arg.(Offset); ok {
+		if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
+			panic(fmt.Errorf("wrong table: offset not followed by register"))
+		}
+	}
+	switch arg := arg.(type) {
+	case Reg:
+		if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
+			return "0"
+		}
+		if arg == R30 {
+			return "g"
+		}
+		return strings.ToUpper(arg.String())
+	case CondReg:
+		if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
+			return "" // don't show cr0 for cmp instructions
+		} else if arg >= CR0 {
+			return fmt.Sprintf("CR%d", int(arg-CR0))
+		}
+		bit := [4]string{"LT", "GT", "EQ", "SO"}[(arg-Cond0LT)%4]
+		if arg <= Cond0SO {
+			return bit
+		}
+		return fmt.Sprintf("4*CR%d+%s", int(arg-Cond0LT)/4, bit)
+	case Imm:
+		return fmt.Sprintf("$%d", arg)
+	case SpReg:
+		switch arg {
+		case 8:
+			return "LR"
+		case 9:
+			return "CTR"
+		}
+		return fmt.Sprintf("SPR(%d)", int(arg))
+	case PCRel:
+		addr := pc + uint64(int64(arg))
+		if s, base := symname(addr); s != "" && base == addr {
+			return fmt.Sprintf("%s(SB)", s)
+		}
+		return fmt.Sprintf("%#x", addr)
+	case Label:
+		return fmt.Sprintf("%#x", int(arg))
+	case Offset:
+		reg := inst.Args[argIndex+1].(Reg)
+		removeArg(inst, argIndex+1)
+		if reg == R0 {
+			return fmt.Sprintf("%d(0)", int(arg))
+		}
+		return fmt.Sprintf("%d(R%d)", int(arg), reg-R0)
+	}
+	return fmt.Sprintf("???(%v)", arg)
+}
+
+// revCondMap maps a conditional register bit to its inverse, if possible.
+var revCondMap = map[string]string{
+	"LT": "GE", "GT": "LE", "EQ": "NE",
+}
+
+// plan9OpMap maps an Op to its Plan 9 mnemonics, if different than its GNU mnemonics.
+var plan9OpMap = map[Op]string{
+	LWARX: "LWAR", STWCX_: "STWCCC",
+	LDARX: "LDAR", STDCX_: "STDCCC",
+	LHARX: "LHAR", STHCX_: "STHCCC",
+	LBARX: "LBAR", STBCX_: "STBCCC",
+	ADDI: "ADD",
+	ADD_: "ADDCC",
+	LBZ:  "MOVBZ", STB: "MOVB",
+	LBZU: "MOVBZU", STBU: "MOVBU", // TODO(minux): indexed forms are not handled
+	LHZ: "MOVHZ", LHA: "MOVH", STH: "MOVH",
+	LHZU: "MOVHZU", STHU: "MOVHU",
+	LI:  "MOVD",
+	LIS: "ADDIS",
+	LWZ: "MOVWZ", LWA: "MOVW", STW: "MOVW",
+	LWZU: "MOVWZU", STWU: "MOVWU",
+	LD: "MOVD", STD: "MOVD",
+	LDU: "MOVDU", STDU: "MOVDU",
+	MTSPR: "MOVD", MFSPR: "MOVD", // the width is ambiguous for SPRs
+	B:     "BR",
+	BL:    "CALL",
+	CMPLD: "CMPU", CMPLW: "CMPWU",
+	CMPD: "CMP", CMPW: "CMPW",
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/tables.go b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/tables.go
new file mode 100644
index 0000000..24c745c
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/tables.go
@@ -0,0 +1,5421 @@
+// DO NOT EDIT
+// generated by: ppc64map -fmt=decoder ../pp64.csv
+
+package ppc64asm
+
+const (
+	_ Op = iota
+	CNTLZW
+	CNTLZW_
+	B
+	BA
+	BL
+	BLA
+	BC
+	BCA
+	BCL
+	BCLA
+	BCLR
+	BCLRL
+	BCCTR
+	BCCTRL
+	BCTAR
+	BCTARL
+	CRAND
+	CROR
+	CRNAND
+	CRXOR
+	CRNOR
+	CRANDC
+	MCRF
+	CREQV
+	CRORC
+	SC
+	CLRBHRB
+	MFBHRBE
+	LBZ
+	LBZU
+	LBZX
+	LBZUX
+	LHZ
+	LHZU
+	LHZX
+	LHZUX
+	LHA
+	LHAU
+	LHAX
+	LHAUX
+	LWZ
+	LWZU
+	LWZX
+	LWZUX
+	LWA
+	LWAX
+	LWAUX
+	LD
+	LDU
+	LDX
+	LDUX
+	STB
+	STBU
+	STBX
+	STBUX
+	STH
+	STHU
+	STHX
+	STHUX
+	STW
+	STWU
+	STWX
+	STWUX
+	STD
+	STDU
+	STDX
+	STDUX
+	LQ
+	STQ
+	LHBRX
+	LWBRX
+	STHBRX
+	STWBRX
+	LDBRX
+	STDBRX
+	LMW
+	STMW
+	LSWI
+	LSWX
+	STSWI
+	STSWX
+	LI
+	ADDI
+	LIS
+	ADDIS
+	ADD
+	ADD_
+	ADDO
+	ADDO_
+	ADDIC
+	SUBF
+	SUBF_
+	SUBFO
+	SUBFO_
+	ADDIC_
+	SUBFIC
+	ADDC
+	ADDC_
+	ADDCO
+	ADDCO_
+	SUBFC
+	SUBFC_
+	SUBFCO
+	SUBFCO_
+	ADDE
+	ADDE_
+	ADDEO
+	ADDEO_
+	ADDME
+	ADDME_
+	ADDMEO
+	ADDMEO_
+	SUBFE
+	SUBFE_
+	SUBFEO
+	SUBFEO_
+	SUBFME
+	SUBFME_
+	SUBFMEO
+	SUBFMEO_
+	ADDZE
+	ADDZE_
+	ADDZEO
+	ADDZEO_
+	SUBFZE
+	SUBFZE_
+	SUBFZEO
+	SUBFZEO_
+	NEG
+	NEG_
+	NEGO
+	NEGO_
+	MULLI
+	MULLW
+	MULLW_
+	MULLWO
+	MULLWO_
+	MULHW
+	MULHW_
+	MULHWU
+	MULHWU_
+	DIVW
+	DIVW_
+	DIVWO
+	DIVWO_
+	DIVWU
+	DIVWU_
+	DIVWUO
+	DIVWUO_
+	DIVWE
+	DIVWE_
+	DIVWEO
+	DIVWEO_
+	DIVWEU
+	DIVWEU_
+	DIVWEUO
+	DIVWEUO_
+	MULLD
+	MULLD_
+	MULLDO
+	MULLDO_
+	MULHDU
+	MULHDU_
+	MULHD
+	MULHD_
+	DIVD
+	DIVD_
+	DIVDO
+	DIVDO_
+	DIVDU
+	DIVDU_
+	DIVDUO
+	DIVDUO_
+	DIVDE
+	DIVDE_
+	DIVDEO
+	DIVDEO_
+	DIVDEU
+	DIVDEU_
+	DIVDEUO
+	DIVDEUO_
+	CMPWI
+	CMPDI
+	CMPW
+	CMPD
+	CMPLWI
+	CMPLDI
+	CMPLW
+	CMPLD
+	TWI
+	TW
+	TDI
+	ISEL
+	TD
+	ANDI_
+	ANDIS_
+	ORI
+	ORIS
+	XORI
+	XORIS
+	AND
+	AND_
+	XOR
+	XOR_
+	NAND
+	NAND_
+	OR
+	OR_
+	NOR
+	NOR_
+	ANDC
+	ANDC_
+	EXTSB
+	EXTSB_
+	EQV
+	EQV_
+	ORC
+	ORC_
+	EXTSH
+	EXTSH_
+	CMPB
+	POPCNTB
+	POPCNTW
+	PRTYD
+	PRTYW
+	EXTSW
+	EXTSW_
+	CNTLZD
+	CNTLZD_
+	POPCNTD
+	BPERMD
+	RLWINM
+	RLWINM_
+	RLWNM
+	RLWNM_
+	RLWIMI
+	RLWIMI_
+	RLDICL
+	RLDICL_
+	RLDICR
+	RLDICR_
+	RLDIC
+	RLDIC_
+	RLDCL
+	RLDCL_
+	RLDCR
+	RLDCR_
+	RLDIMI
+	RLDIMI_
+	SLW
+	SLW_
+	SRW
+	SRW_
+	SRAWI
+	SRAWI_
+	SRAW
+	SRAW_
+	SLD
+	SLD_
+	SRD
+	SRD_
+	SRADI
+	SRADI_
+	SRAD
+	SRAD_
+	CDTBCD
+	CBCDTD
+	ADDG6S
+	MTSPR
+	MFSPR
+	MTCRF
+	MFCR
+	MTSLE
+	MFVSRD
+	MFVSRWZ
+	MTVSRD
+	MTVSRWA
+	MTVSRWZ
+	MTOCRF
+	MFOCRF
+	MCRXR
+	MTDCRUX
+	MFDCRUX
+	LFS
+	LFSU
+	LFSX
+	LFSUX
+	LFD
+	LFDU
+	LFDX
+	LFDUX
+	LFIWAX
+	LFIWZX
+	STFS
+	STFSU
+	STFSX
+	STFSUX
+	STFD
+	STFDU
+	STFDX
+	STFDUX
+	STFIWX
+	LFDP
+	LFDPX
+	STFDP
+	STFDPX
+	FMR
+	FMR_
+	FABS
+	FABS_
+	FNABS
+	FNABS_
+	FNEG
+	FNEG_
+	FCPSGN
+	FCPSGN_
+	FMRGEW
+	FMRGOW
+	FADD
+	FADD_
+	FADDS
+	FADDS_
+	FSUB
+	FSUB_
+	FSUBS
+	FSUBS_
+	FMUL
+	FMUL_
+	FMULS
+	FMULS_
+	FDIV
+	FDIV_
+	FDIVS
+	FDIVS_
+	FSQRT
+	FSQRT_
+	FSQRTS
+	FSQRTS_
+	FRE
+	FRE_
+	FRES
+	FRES_
+	FRSQRTE
+	FRSQRTE_
+	FRSQRTES
+	FRSQRTES_
+	FTDIV
+	FTSQRT
+	FMADD
+	FMADD_
+	FMADDS
+	FMADDS_
+	FMSUB
+	FMSUB_
+	FMSUBS
+	FMSUBS_
+	FNMADD
+	FNMADD_
+	FNMADDS
+	FNMADDS_
+	FNMSUB
+	FNMSUB_
+	FNMSUBS
+	FNMSUBS_
+	FRSP
+	FRSP_
+	FCTID
+	FCTID_
+	FCTIDZ
+	FCTIDZ_
+	FCTIDU
+	FCTIDU_
+	FCTIDUZ
+	FCTIDUZ_
+	FCTIW
+	FCTIW_
+	FCTIWZ
+	FCTIWZ_
+	FCTIWU
+	FCTIWU_
+	FCTIWUZ
+	FCTIWUZ_
+	FCFID
+	FCFID_
+	FCFIDU
+	FCFIDU_
+	FCFIDS
+	FCFIDS_
+	FCFIDUS
+	FCFIDUS_
+	FRIN
+	FRIN_
+	FRIZ
+	FRIZ_
+	FRIP
+	FRIP_
+	FRIM
+	FRIM_
+	FCMPU
+	FCMPO
+	FSEL
+	FSEL_
+	MFFS
+	MFFS_
+	MCRFS
+	MTFSFI
+	MTFSFI_
+	MTFSF
+	MTFSF_
+	MTFSB0
+	MTFSB0_
+	MTFSB1
+	MTFSB1_
+	LVEBX
+	LVEHX
+	LVEWX
+	LVX
+	LVXL
+	STVEBX
+	STVEHX
+	STVEWX
+	STVX
+	STVXL
+	LVSL
+	LVSR
+	VPKPX
+	VPKSDSS
+	VPKSDUS
+	VPKSHSS
+	VPKSHUS
+	VPKSWSS
+	VPKSWUS
+	VPKUDUM
+	VPKUDUS
+	VPKUHUM
+	VPKUHUS
+	VPKUWUM
+	VPKUWUS
+	VUPKHPX
+	VUPKLPX
+	VUPKHSB
+	VUPKHSH
+	VUPKHSW
+	VUPKLSB
+	VUPKLSH
+	VUPKLSW
+	VMRGHB
+	VMRGHH
+	VMRGLB
+	VMRGLH
+	VMRGHW
+	VMRGLW
+	VMRGEW
+	VMRGOW
+	VSPLTB
+	VSPLTH
+	VSPLTW
+	VSPLTISB
+	VSPLTISH
+	VSPLTISW
+	VPERM
+	VSEL
+	VSL
+	VSLDOI
+	VSLO
+	VSR
+	VSRO
+	VADDCUW
+	VADDSBS
+	VADDSHS
+	VADDSWS
+	VADDUBM
+	VADDUDM
+	VADDUHM
+	VADDUWM
+	VADDUBS
+	VADDUHS
+	VADDUWS
+	VADDUQM
+	VADDEUQM
+	VADDCUQ
+	VADDECUQ
+	VSUBCUW
+	VSUBSBS
+	VSUBSHS
+	VSUBSWS
+	VSUBUBM
+	VSUBUDM
+	VSUBUHM
+	VSUBUWM
+	VSUBUBS
+	VSUBUHS
+	VSUBUWS
+	VSUBUQM
+	VSUBEUQM
+	VSUBCUQ
+	VSUBECUQ
+	VMULESB
+	VMULEUB
+	VMULOSB
+	VMULOUB
+	VMULESH
+	VMULEUH
+	VMULOSH
+	VMULOUH
+	VMULESW
+	VMULEUW
+	VMULOSW
+	VMULOUW
+	VMULUWM
+	VMHADDSHS
+	VMHRADDSHS
+	VMLADDUHM
+	VMSUMUBM
+	VMSUMMBM
+	VMSUMSHM
+	VMSUMSHS
+	VMSUMUHM
+	VMSUMUHS
+	VSUMSWS
+	VSUM2SWS
+	VSUM4SBS
+	VSUM4SHS
+	VSUM4UBS
+	VAVGSB
+	VAVGSH
+	VAVGSW
+	VAVGUB
+	VAVGUW
+	VAVGUH
+	VMAXSB
+	VMAXSD
+	VMAXUB
+	VMAXUD
+	VMAXSH
+	VMAXSW
+	VMAXUH
+	VMAXUW
+	VMINSB
+	VMINSD
+	VMINUB
+	VMINUD
+	VMINSH
+	VMINSW
+	VMINUH
+	VMINUW
+	VCMPEQUB
+	VCMPEQUB_
+	VCMPEQUH
+	VCMPEQUH_
+	VCMPEQUW
+	VCMPEQUW_
+	VCMPEQUD
+	VCMPEQUD_
+	VCMPGTSB
+	VCMPGTSB_
+	VCMPGTSD
+	VCMPGTSD_
+	VCMPGTSH
+	VCMPGTSH_
+	VCMPGTSW
+	VCMPGTSW_
+	VCMPGTUB
+	VCMPGTUB_
+	VCMPGTUD
+	VCMPGTUD_
+	VCMPGTUH
+	VCMPGTUH_
+	VCMPGTUW
+	VCMPGTUW_
+	VAND
+	VANDC
+	VEQV
+	VNAND
+	VORC
+	VNOR
+	VOR
+	VXOR
+	VRLB
+	VRLH
+	VRLW
+	VRLD
+	VSLB
+	VSLH
+	VSLW
+	VSLD
+	VSRB
+	VSRH
+	VSRW
+	VSRD
+	VSRAB
+	VSRAH
+	VSRAW
+	VSRAD
+	VADDFP
+	VSUBFP
+	VMADDFP
+	VNMSUBFP
+	VMAXFP
+	VMINFP
+	VCTSXS
+	VCTUXS
+	VCFSX
+	VCFUX
+	VRFIM
+	VRFIN
+	VRFIP
+	VRFIZ
+	VCMPBFP
+	VCMPBFP_
+	VCMPEQFP
+	VCMPEQFP_
+	VCMPGEFP
+	VCMPGEFP_
+	VCMPGTFP
+	VCMPGTFP_
+	VEXPTEFP
+	VLOGEFP
+	VREFP
+	VRSQRTEFP
+	VCIPHER
+	VCIPHERLAST
+	VNCIPHER
+	VNCIPHERLAST
+	VSBOX
+	VSHASIGMAD
+	VSHASIGMAW
+	VPMSUMB
+	VPMSUMD
+	VPMSUMH
+	VPMSUMW
+	VPERMXOR
+	VGBBD
+	VCLZB
+	VCLZH
+	VCLZW
+	VCLZD
+	VPOPCNTB
+	VPOPCNTD
+	VPOPCNTH
+	VPOPCNTW
+	VBPERMQ
+	BCDADD_
+	BCDSUB_
+	MTVSCR
+	MFVSCR
+	DADD
+	DADD_
+	DSUB
+	DSUB_
+	DMUL
+	DMUL_
+	DDIV
+	DDIV_
+	DCMPU
+	DCMPO
+	DTSTDC
+	DTSTDG
+	DTSTEX
+	DTSTSF
+	DQUAI
+	DQUAI_
+	DQUA
+	DQUA_
+	DRRND
+	DRRND_
+	DRINTX
+	DRINTX_
+	DRINTN
+	DRINTN_
+	DCTDP
+	DCTDP_
+	DCTQPQ
+	DCTQPQ_
+	DRSP
+	DRSP_
+	DRDPQ
+	DRDPQ_
+	DCFFIX
+	DCFFIX_
+	DCFFIXQ
+	DCFFIXQ_
+	DCTFIX
+	DCTFIX_
+	DDEDPD
+	DDEDPD_
+	DENBCD
+	DENBCD_
+	DXEX
+	DXEX_
+	DIEX
+	DIEX_
+	DSCLI
+	DSCLI_
+	DSCRI
+	DSCRI_
+	LXSDX
+	LXSIWAX
+	LXSIWZX
+	LXSSPX
+	LXVD2X
+	LXVDSX
+	LXVW4X
+	STXSDX
+	STXSIWX
+	STXSSPX
+	STXVD2X
+	STXVW4X
+	XSABSDP
+	XSADDDP
+	XSADDSP
+	XSCMPODP
+	XSCMPUDP
+	XSCPSGNDP
+	XSCVDPSP
+	XSCVDPSPN
+	XSCVDPSXDS
+	XSCVDPSXWS
+	XSCVDPUXDS
+	XSCVDPUXWS
+	XSCVSPDP
+	XSCVSPDPN
+	XSCVSXDDP
+	XSCVSXDSP
+	XSCVUXDDP
+	XSCVUXDSP
+	XSDIVDP
+	XSDIVSP
+	XSMADDADP
+	XSMADDASP
+	XSMAXDP
+	XSMINDP
+	XSMSUBADP
+	XSMSUBASP
+	XSMULDP
+	XSMULSP
+	XSNABSDP
+	XSNEGDP
+	XSNMADDADP
+	XSNMADDASP
+	XSNMSUBADP
+	XSNMSUBASP
+	XSRDPI
+	XSRDPIC
+	XSRDPIM
+	XSRDPIP
+	XSRDPIZ
+	XSREDP
+	XSRESP
+	XSRSP
+	XSRSQRTEDP
+	XSRSQRTESP
+	XSSQRTDP
+	XSSQRTSP
+	XSSUBDP
+	XSSUBSP
+	XSTDIVDP
+	XSTSQRTDP
+	XVABSDP
+	XVABSSP
+	XVADDDP
+	XVADDSP
+	XVCMPEQDP
+	XVCMPEQDP_
+	XVCMPEQSP
+	XVCMPEQSP_
+	XVCMPGEDP
+	XVCMPGEDP_
+	XVCMPGESP
+	XVCMPGESP_
+	XVCMPGTDP
+	XVCMPGTDP_
+	XVCMPGTSP
+	XVCMPGTSP_
+	XVCPSGNDP
+	XVCPSGNSP
+	XVCVDPSP
+	XVCVDPSXDS
+	XVCVDPSXWS
+	XVCVDPUXDS
+	XVCVDPUXWS
+	XVCVSPDP
+	XVCVSPSXDS
+	XVCVSPSXWS
+	XVCVSPUXDS
+	XVCVSPUXWS
+	XVCVSXDDP
+	XVCVSXDSP
+	XVCVSXWDP
+	XVCVSXWSP
+	XVCVUXDDP
+	XVCVUXDSP
+	XVCVUXWDP
+	XVCVUXWSP
+	XVDIVDP
+	XVDIVSP
+	XVMADDADP
+	XVMADDASP
+	XVMAXDP
+	XVMAXSP
+	XVMINDP
+	XVMINSP
+	XVMSUBADP
+	XVMSUBASP
+	XVMULDP
+	XVMULSP
+	XVNABSDP
+	XVNABSSP
+	XVNEGDP
+	XVNEGSP
+	XVNMADDADP
+	XVNMADDASP
+	XVNMSUBADP
+	XVNMSUBASP
+	XVRDPI
+	XVRDPIC
+	XVRDPIM
+	XVRDPIP
+	XVRDPIZ
+	XVREDP
+	XVRESP
+	XVRSPI
+	XVRSPIC
+	XVRSPIM
+	XVRSPIP
+	XVRSPIZ
+	XVRSQRTEDP
+	XVRSQRTESP
+	XVSQRTDP
+	XVSQRTSP
+	XVSUBDP
+	XVSUBSP
+	XVTDIVDP
+	XVTDIVSP
+	XVTSQRTDP
+	XVTSQRTSP
+	XXLAND
+	XXLANDC
+	XXLEQV
+	XXLNAND
+	XXLORC
+	XXLNOR
+	XXLOR
+	XXLXOR
+	XXMRGHW
+	XXMRGLW
+	XXPERMDI
+	XXSEL
+	XXSLDWI
+	XXSPLTW
+	BRINC
+	EVABS
+	EVADDIW
+	EVADDSMIAAW
+	EVADDSSIAAW
+	EVADDUMIAAW
+	EVADDUSIAAW
+	EVADDW
+	EVAND
+	EVCMPEQ
+	EVANDC
+	EVCMPGTS
+	EVCMPGTU
+	EVCMPLTU
+	EVCMPLTS
+	EVCNTLSW
+	EVCNTLZW
+	EVDIVWS
+	EVDIVWU
+	EVEQV
+	EVEXTSB
+	EVEXTSH
+	EVLDD
+	EVLDH
+	EVLDDX
+	EVLDHX
+	EVLDW
+	EVLHHESPLAT
+	EVLDWX
+	EVLHHESPLATX
+	EVLHHOSSPLAT
+	EVLHHOUSPLAT
+	EVLHHOSSPLATX
+	EVLHHOUSPLATX
+	EVLWHE
+	EVLWHOS
+	EVLWHEX
+	EVLWHOSX
+	EVLWHOU
+	EVLWHSPLAT
+	EVLWHOUX
+	EVLWHSPLATX
+	EVLWWSPLAT
+	EVMERGEHI
+	EVLWWSPLATX
+	EVMERGELO
+	EVMERGEHILO
+	EVMHEGSMFAA
+	EVMERGELOHI
+	EVMHEGSMFAN
+	EVMHEGSMIAA
+	EVMHEGUMIAA
+	EVMHEGSMIAN
+	EVMHEGUMIAN
+	EVMHESMF
+	EVMHESMFAAW
+	EVMHESMFA
+	EVMHESMFANW
+	EVMHESMI
+	EVMHESMIAAW
+	EVMHESMIA
+	EVMHESMIANW
+	EVMHESSF
+	EVMHESSFA
+	EVMHESSFAAW
+	EVMHESSFANW
+	EVMHESSIAAW
+	EVMHESSIANW
+	EVMHEUMI
+	EVMHEUMIAAW
+	EVMHEUMIA
+	EVMHEUMIANW
+	EVMHEUSIAAW
+	EVMHEUSIANW
+	EVMHOGSMFAA
+	EVMHOGSMIAA
+	EVMHOGSMFAN
+	EVMHOGSMIAN
+	EVMHOGUMIAA
+	EVMHOSMF
+	EVMHOGUMIAN
+	EVMHOSMFA
+	EVMHOSMFAAW
+	EVMHOSMI
+	EVMHOSMFANW
+	EVMHOSMIA
+	EVMHOSMIAAW
+	EVMHOSMIANW
+	EVMHOSSF
+	EVMHOSSFA
+	EVMHOSSFAAW
+	EVMHOSSFANW
+	EVMHOSSIAAW
+	EVMHOUMI
+	EVMHOSSIANW
+	EVMHOUMIA
+	EVMHOUMIAAW
+	EVMHOUSIAAW
+	EVMHOUMIANW
+	EVMHOUSIANW
+	EVMRA
+	EVMWHSMF
+	EVMWHSMI
+	EVMWHSMFA
+	EVMWHSMIA
+	EVMWHSSF
+	EVMWHUMI
+	EVMWHSSFA
+	EVMWHUMIA
+	EVMWLSMIAAW
+	EVMWLSSIAAW
+	EVMWLSMIANW
+	EVMWLSSIANW
+	EVMWLUMI
+	EVMWLUMIAAW
+	EVMWLUMIA
+	EVMWLUMIANW
+	EVMWLUSIAAW
+	EVMWSMF
+	EVMWLUSIANW
+	EVMWSMFA
+	EVMWSMFAA
+	EVMWSMI
+	EVMWSMIAA
+	EVMWSMFAN
+	EVMWSMIA
+	EVMWSMIAN
+	EVMWSSF
+	EVMWSSFA
+	EVMWSSFAA
+	EVMWUMI
+	EVMWSSFAN
+	EVMWUMIA
+	EVMWUMIAA
+	EVNAND
+	EVMWUMIAN
+	EVNEG
+	EVNOR
+	EVORC
+	EVOR
+	EVRLW
+	EVRLWI
+	EVSEL
+	EVRNDW
+	EVSLW
+	EVSPLATFI
+	EVSRWIS
+	EVSLWI
+	EVSPLATI
+	EVSRWIU
+	EVSRWS
+	EVSTDD
+	EVSRWU
+	EVSTDDX
+	EVSTDH
+	EVSTDW
+	EVSTDHX
+	EVSTDWX
+	EVSTWHE
+	EVSTWHO
+	EVSTWWE
+	EVSTWHEX
+	EVSTWHOX
+	EVSTWWEX
+	EVSTWWO
+	EVSUBFSMIAAW
+	EVSTWWOX
+	EVSUBFSSIAAW
+	EVSUBFUMIAAW
+	EVSUBFUSIAAW
+	EVSUBFW
+	EVSUBIFW
+	EVXOR
+	EVFSABS
+	EVFSNABS
+	EVFSNEG
+	EVFSADD
+	EVFSMUL
+	EVFSSUB
+	EVFSDIV
+	EVFSCMPGT
+	EVFSCMPLT
+	EVFSCMPEQ
+	EVFSTSTGT
+	EVFSTSTLT
+	EVFSTSTEQ
+	EVFSCFSI
+	EVFSCFSF
+	EVFSCFUI
+	EVFSCFUF
+	EVFSCTSI
+	EVFSCTUI
+	EVFSCTSIZ
+	EVFSCTUIZ
+	EVFSCTSF
+	EVFSCTUF
+	EFSABS
+	EFSNEG
+	EFSNABS
+	EFSADD
+	EFSMUL
+	EFSSUB
+	EFSDIV
+	EFSCMPGT
+	EFSCMPLT
+	EFSCMPEQ
+	EFSTSTGT
+	EFSTSTLT
+	EFSTSTEQ
+	EFSCFSI
+	EFSCFSF
+	EFSCTSI
+	EFSCFUI
+	EFSCFUF
+	EFSCTUI
+	EFSCTSIZ
+	EFSCTSF
+	EFSCTUIZ
+	EFSCTUF
+	EFDABS
+	EFDNEG
+	EFDNABS
+	EFDADD
+	EFDMUL
+	EFDSUB
+	EFDDIV
+	EFDCMPGT
+	EFDCMPEQ
+	EFDCMPLT
+	EFDTSTGT
+	EFDTSTLT
+	EFDCFSI
+	EFDTSTEQ
+	EFDCFUI
+	EFDCFSID
+	EFDCFSF
+	EFDCFUF
+	EFDCFUID
+	EFDCTSI
+	EFDCTUI
+	EFDCTSIDZ
+	EFDCTUIDZ
+	EFDCTSIZ
+	EFDCTSF
+	EFDCTUF
+	EFDCTUIZ
+	EFDCFS
+	EFSCFD
+	DLMZB
+	DLMZB_
+	MACCHW
+	MACCHW_
+	MACCHWO
+	MACCHWO_
+	MACCHWS
+	MACCHWS_
+	MACCHWSO
+	MACCHWSO_
+	MACCHWU
+	MACCHWU_
+	MACCHWUO
+	MACCHWUO_
+	MACCHWSU
+	MACCHWSU_
+	MACCHWSUO
+	MACCHWSUO_
+	MACHHW
+	MACHHW_
+	MACHHWO
+	MACHHWO_
+	MACHHWS
+	MACHHWS_
+	MACHHWSO
+	MACHHWSO_
+	MACHHWU
+	MACHHWU_
+	MACHHWUO
+	MACHHWUO_
+	MACHHWSU
+	MACHHWSU_
+	MACHHWSUO
+	MACHHWSUO_
+	MACLHW
+	MACLHW_
+	MACLHWO
+	MACLHWO_
+	MACLHWS
+	MACLHWS_
+	MACLHWSO
+	MACLHWSO_
+	MACLHWU
+	MACLHWU_
+	MACLHWUO
+	MACLHWUO_
+	MULCHW
+	MULCHW_
+	MACLHWSU
+	MACLHWSU_
+	MACLHWSUO
+	MACLHWSUO_
+	MULCHWU
+	MULCHWU_
+	MULHHW
+	MULHHW_
+	MULLHW
+	MULLHW_
+	MULHHWU
+	MULHHWU_
+	MULLHWU
+	MULLHWU_
+	NMACCHW
+	NMACCHW_
+	NMACCHWO
+	NMACCHWO_
+	NMACCHWS
+	NMACCHWS_
+	NMACCHWSO
+	NMACCHWSO_
+	NMACHHW
+	NMACHHW_
+	NMACHHWO
+	NMACHHWO_
+	NMACHHWS
+	NMACHHWS_
+	NMACHHWSO
+	NMACHHWSO_
+	NMACLHW
+	NMACLHW_
+	NMACLHWO
+	NMACLHWO_
+	NMACLHWS
+	NMACLHWS_
+	NMACLHWSO
+	NMACLHWSO_
+	ICBI
+	ICBT
+	DCBA
+	DCBT
+	DCBTST
+	DCBZ
+	DCBST
+	DCBF
+	ISYNC
+	LBARX
+	LHARX
+	LWARX
+	STBCX_
+	STHCX_
+	STWCX_
+	LDARX
+	STDCX_
+	LQARX
+	STQCX_
+	SYNC
+	EIEIO
+	MBAR
+	WAIT
+	TBEGIN_
+	TEND_
+	TABORT_
+	TABORTWC_
+	TABORTWCI_
+	TABORTDC_
+	TABORTDCI_
+	TSR_
+	TCHECK
+	MFTB
+	RFEBB
+	LBDX
+	LHDX
+	LWDX
+	LDDX
+	LFDDX
+	STBDX
+	STHDX
+	STWDX
+	STDDX
+	STFDDX
+	DSN
+	ECIWX
+	ECOWX
+	RFID
+	HRFID
+	DOZE
+	NAP
+	SLEEP
+	RVWINKLE
+	LBZCIX
+	LWZCIX
+	LHZCIX
+	LDCIX
+	STBCIX
+	STWCIX
+	STHCIX
+	STDCIX
+	TRECLAIM_
+	TRECHKPT_
+	MTMSR
+	MTMSRD
+	MFMSR
+	SLBIE
+	SLBIA
+	SLBMTE
+	SLBMFEV
+	SLBMFEE
+	SLBFEE_
+	MTSR
+	MTSRIN
+	MFSR
+	MFSRIN
+	TLBIE
+	TLBIEL
+	TLBIA
+	TLBSYNC
+	MSGSND
+	MSGCLR
+	MSGSNDP
+	MSGCLRP
+	MTTMR
+	RFI
+	RFCI
+	RFDI
+	RFMCI
+	RFGI
+	EHPRIV
+	MTDCR
+	MTDCRX
+	MFDCR
+	MFDCRX
+	WRTEE
+	WRTEEI
+	LBEPX
+	LHEPX
+	LWEPX
+	LDEPX
+	STBEPX
+	STHEPX
+	STWEPX
+	STDEPX
+	DCBSTEP
+	DCBTEP
+	DCBFEP
+	DCBTSTEP
+	ICBIEP
+	DCBZEP
+	LFDEPX
+	STFDEPX
+	EVLDDEPX
+	EVSTDDEPX
+	LVEPX
+	LVEPXL
+	STVEPX
+	STVEPXL
+	DCBI
+	DCBLQ_
+	ICBLQ_
+	DCBTLS
+	DCBTSTLS
+	ICBTLS
+	ICBLC
+	DCBLC
+	TLBIVAX
+	TLBILX
+	TLBSX
+	TLBSRX_
+	TLBRE
+	TLBWE
+	DNH
+	DCI
+	ICI
+	DCREAD
+	ICREAD
+	MFPMR
+	MTPMR
+)
+
+var opstr = [...]string{
+	CNTLZW:        "cntlzw",
+	CNTLZW_:       "cntlzw.",
+	B:             "b",
+	BA:            "ba",
+	BL:            "bl",
+	BLA:           "bla",
+	BC:            "bc",
+	BCA:           "bca",
+	BCL:           "bcl",
+	BCLA:          "bcla",
+	BCLR:          "bclr",
+	BCLRL:         "bclrl",
+	BCCTR:         "bcctr",
+	BCCTRL:        "bcctrl",
+	BCTAR:         "bctar",
+	BCTARL:        "bctarl",
+	CRAND:         "crand",
+	CROR:          "cror",
+	CRNAND:        "crnand",
+	CRXOR:         "crxor",
+	CRNOR:         "crnor",
+	CRANDC:        "crandc",
+	MCRF:          "mcrf",
+	CREQV:         "creqv",
+	CRORC:         "crorc",
+	SC:            "sc",
+	CLRBHRB:       "clrbhrb",
+	MFBHRBE:       "mfbhrbe",
+	LBZ:           "lbz",
+	LBZU:          "lbzu",
+	LBZX:          "lbzx",
+	LBZUX:         "lbzux",
+	LHZ:           "lhz",
+	LHZU:          "lhzu",
+	LHZX:          "lhzx",
+	LHZUX:         "lhzux",
+	LHA:           "lha",
+	LHAU:          "lhau",
+	LHAX:          "lhax",
+	LHAUX:         "lhaux",
+	LWZ:           "lwz",
+	LWZU:          "lwzu",
+	LWZX:          "lwzx",
+	LWZUX:         "lwzux",
+	LWA:           "lwa",
+	LWAX:          "lwax",
+	LWAUX:         "lwaux",
+	LD:            "ld",
+	LDU:           "ldu",
+	LDX:           "ldx",
+	LDUX:          "ldux",
+	STB:           "stb",
+	STBU:          "stbu",
+	STBX:          "stbx",
+	STBUX:         "stbux",
+	STH:           "sth",
+	STHU:          "sthu",
+	STHX:          "sthx",
+	STHUX:         "sthux",
+	STW:           "stw",
+	STWU:          "stwu",
+	STWX:          "stwx",
+	STWUX:         "stwux",
+	STD:           "std",
+	STDU:          "stdu",
+	STDX:          "stdx",
+	STDUX:         "stdux",
+	LQ:            "lq",
+	STQ:           "stq",
+	LHBRX:         "lhbrx",
+	LWBRX:         "lwbrx",
+	STHBRX:        "sthbrx",
+	STWBRX:        "stwbrx",
+	LDBRX:         "ldbrx",
+	STDBRX:        "stdbrx",
+	LMW:           "lmw",
+	STMW:          "stmw",
+	LSWI:          "lswi",
+	LSWX:          "lswx",
+	STSWI:         "stswi",
+	STSWX:         "stswx",
+	LI:            "li",
+	ADDI:          "addi",
+	LIS:           "lis",
+	ADDIS:         "addis",
+	ADD:           "add",
+	ADD_:          "add.",
+	ADDO:          "addo",
+	ADDO_:         "addo.",
+	ADDIC:         "addic",
+	SUBF:          "subf",
+	SUBF_:         "subf.",
+	SUBFO:         "subfo",
+	SUBFO_:        "subfo.",
+	ADDIC_:        "addic.",
+	SUBFIC:        "subfic",
+	ADDC:          "addc",
+	ADDC_:         "addc.",
+	ADDCO:         "addco",
+	ADDCO_:        "addco.",
+	SUBFC:         "subfc",
+	SUBFC_:        "subfc.",
+	SUBFCO:        "subfco",
+	SUBFCO_:       "subfco.",
+	ADDE:          "adde",
+	ADDE_:         "adde.",
+	ADDEO:         "addeo",
+	ADDEO_:        "addeo.",
+	ADDME:         "addme",
+	ADDME_:        "addme.",
+	ADDMEO:        "addmeo",
+	ADDMEO_:       "addmeo.",
+	SUBFE:         "subfe",
+	SUBFE_:        "subfe.",
+	SUBFEO:        "subfeo",
+	SUBFEO_:       "subfeo.",
+	SUBFME:        "subfme",
+	SUBFME_:       "subfme.",
+	SUBFMEO:       "subfmeo",
+	SUBFMEO_:      "subfmeo.",
+	ADDZE:         "addze",
+	ADDZE_:        "addze.",
+	ADDZEO:        "addzeo",
+	ADDZEO_:       "addzeo.",
+	SUBFZE:        "subfze",
+	SUBFZE_:       "subfze.",
+	SUBFZEO:       "subfzeo",
+	SUBFZEO_:      "subfzeo.",
+	NEG:           "neg",
+	NEG_:          "neg.",
+	NEGO:          "nego",
+	NEGO_:         "nego.",
+	MULLI:         "mulli",
+	MULLW:         "mullw",
+	MULLW_:        "mullw.",
+	MULLWO:        "mullwo",
+	MULLWO_:       "mullwo.",
+	MULHW:         "mulhw",
+	MULHW_:        "mulhw.",
+	MULHWU:        "mulhwu",
+	MULHWU_:       "mulhwu.",
+	DIVW:          "divw",
+	DIVW_:         "divw.",
+	DIVWO:         "divwo",
+	DIVWO_:        "divwo.",
+	DIVWU:         "divwu",
+	DIVWU_:        "divwu.",
+	DIVWUO:        "divwuo",
+	DIVWUO_:       "divwuo.",
+	DIVWE:         "divwe",
+	DIVWE_:        "divwe.",
+	DIVWEO:        "divweo",
+	DIVWEO_:       "divweo.",
+	DIVWEU:        "divweu",
+	DIVWEU_:       "divweu.",
+	DIVWEUO:       "divweuo",
+	DIVWEUO_:      "divweuo.",
+	MULLD:         "mulld",
+	MULLD_:        "mulld.",
+	MULLDO:        "mulldo",
+	MULLDO_:       "mulldo.",
+	MULHDU:        "mulhdu",
+	MULHDU_:       "mulhdu.",
+	MULHD:         "mulhd",
+	MULHD_:        "mulhd.",
+	DIVD:          "divd",
+	DIVD_:         "divd.",
+	DIVDO:         "divdo",
+	DIVDO_:        "divdo.",
+	DIVDU:         "divdu",
+	DIVDU_:        "divdu.",
+	DIVDUO:        "divduo",
+	DIVDUO_:       "divduo.",
+	DIVDE:         "divde",
+	DIVDE_:        "divde.",
+	DIVDEO:        "divdeo",
+	DIVDEO_:       "divdeo.",
+	DIVDEU:        "divdeu",
+	DIVDEU_:       "divdeu.",
+	DIVDEUO:       "divdeuo",
+	DIVDEUO_:      "divdeuo.",
+	CMPWI:         "cmpwi",
+	CMPDI:         "cmpdi",
+	CMPW:          "cmpw",
+	CMPD:          "cmpd",
+	CMPLWI:        "cmplwi",
+	CMPLDI:        "cmpldi",
+	CMPLW:         "cmplw",
+	CMPLD:         "cmpld",
+	TWI:           "twi",
+	TW:            "tw",
+	TDI:           "tdi",
+	ISEL:          "isel",
+	TD:            "td",
+	ANDI_:         "andi.",
+	ANDIS_:        "andis.",
+	ORI:           "ori",
+	ORIS:          "oris",
+	XORI:          "xori",
+	XORIS:         "xoris",
+	AND:           "and",
+	AND_:          "and.",
+	XOR:           "xor",
+	XOR_:          "xor.",
+	NAND:          "nand",
+	NAND_:         "nand.",
+	OR:            "or",
+	OR_:           "or.",
+	NOR:           "nor",
+	NOR_:          "nor.",
+	ANDC:          "andc",
+	ANDC_:         "andc.",
+	EXTSB:         "extsb",
+	EXTSB_:        "extsb.",
+	EQV:           "eqv",
+	EQV_:          "eqv.",
+	ORC:           "orc",
+	ORC_:          "orc.",
+	EXTSH:         "extsh",
+	EXTSH_:        "extsh.",
+	CMPB:          "cmpb",
+	POPCNTB:       "popcntb",
+	POPCNTW:       "popcntw",
+	PRTYD:         "prtyd",
+	PRTYW:         "prtyw",
+	EXTSW:         "extsw",
+	EXTSW_:        "extsw.",
+	CNTLZD:        "cntlzd",
+	CNTLZD_:       "cntlzd.",
+	POPCNTD:       "popcntd",
+	BPERMD:        "bpermd",
+	RLWINM:        "rlwinm",
+	RLWINM_:       "rlwinm.",
+	RLWNM:         "rlwnm",
+	RLWNM_:        "rlwnm.",
+	RLWIMI:        "rlwimi",
+	RLWIMI_:       "rlwimi.",
+	RLDICL:        "rldicl",
+	RLDICL_:       "rldicl.",
+	RLDICR:        "rldicr",
+	RLDICR_:       "rldicr.",
+	RLDIC:         "rldic",
+	RLDIC_:        "rldic.",
+	RLDCL:         "rldcl",
+	RLDCL_:        "rldcl.",
+	RLDCR:         "rldcr",
+	RLDCR_:        "rldcr.",
+	RLDIMI:        "rldimi",
+	RLDIMI_:       "rldimi.",
+	SLW:           "slw",
+	SLW_:          "slw.",
+	SRW:           "srw",
+	SRW_:          "srw.",
+	SRAWI:         "srawi",
+	SRAWI_:        "srawi.",
+	SRAW:          "sraw",
+	SRAW_:         "sraw.",
+	SLD:           "sld",
+	SLD_:          "sld.",
+	SRD:           "srd",
+	SRD_:          "srd.",
+	SRADI:         "sradi",
+	SRADI_:        "sradi.",
+	SRAD:          "srad",
+	SRAD_:         "srad.",
+	CDTBCD:        "cdtbcd",
+	CBCDTD:        "cbcdtd",
+	ADDG6S:        "addg6s",
+	MTSPR:         "mtspr",
+	MFSPR:         "mfspr",
+	MTCRF:         "mtcrf",
+	MFCR:          "mfcr",
+	MTSLE:         "mtsle",
+	MFVSRD:        "mfvsrd",
+	MFVSRWZ:       "mfvsrwz",
+	MTVSRD:        "mtvsrd",
+	MTVSRWA:       "mtvsrwa",
+	MTVSRWZ:       "mtvsrwz",
+	MTOCRF:        "mtocrf",
+	MFOCRF:        "mfocrf",
+	MCRXR:         "mcrxr",
+	MTDCRUX:       "mtdcrux",
+	MFDCRUX:       "mfdcrux",
+	LFS:           "lfs",
+	LFSU:          "lfsu",
+	LFSX:          "lfsx",
+	LFSUX:         "lfsux",
+	LFD:           "lfd",
+	LFDU:          "lfdu",
+	LFDX:          "lfdx",
+	LFDUX:         "lfdux",
+	LFIWAX:        "lfiwax",
+	LFIWZX:        "lfiwzx",
+	STFS:          "stfs",
+	STFSU:         "stfsu",
+	STFSX:         "stfsx",
+	STFSUX:        "stfsux",
+	STFD:          "stfd",
+	STFDU:         "stfdu",
+	STFDX:         "stfdx",
+	STFDUX:        "stfdux",
+	STFIWX:        "stfiwx",
+	LFDP:          "lfdp",
+	LFDPX:         "lfdpx",
+	STFDP:         "stfdp",
+	STFDPX:        "stfdpx",
+	FMR:           "fmr",
+	FMR_:          "fmr.",
+	FABS:          "fabs",
+	FABS_:         "fabs.",
+	FNABS:         "fnabs",
+	FNABS_:        "fnabs.",
+	FNEG:          "fneg",
+	FNEG_:         "fneg.",
+	FCPSGN:        "fcpsgn",
+	FCPSGN_:       "fcpsgn.",
+	FMRGEW:        "fmrgew",
+	FMRGOW:        "fmrgow",
+	FADD:          "fadd",
+	FADD_:         "fadd.",
+	FADDS:         "fadds",
+	FADDS_:        "fadds.",
+	FSUB:          "fsub",
+	FSUB_:         "fsub.",
+	FSUBS:         "fsubs",
+	FSUBS_:        "fsubs.",
+	FMUL:          "fmul",
+	FMUL_:         "fmul.",
+	FMULS:         "fmuls",
+	FMULS_:        "fmuls.",
+	FDIV:          "fdiv",
+	FDIV_:         "fdiv.",
+	FDIVS:         "fdivs",
+	FDIVS_:        "fdivs.",
+	FSQRT:         "fsqrt",
+	FSQRT_:        "fsqrt.",
+	FSQRTS:        "fsqrts",
+	FSQRTS_:       "fsqrts.",
+	FRE:           "fre",
+	FRE_:          "fre.",
+	FRES:          "fres",
+	FRES_:         "fres.",
+	FRSQRTE:       "frsqrte",
+	FRSQRTE_:      "frsqrte.",
+	FRSQRTES:      "frsqrtes",
+	FRSQRTES_:     "frsqrtes.",
+	FTDIV:         "ftdiv",
+	FTSQRT:        "ftsqrt",
+	FMADD:         "fmadd",
+	FMADD_:        "fmadd.",
+	FMADDS:        "fmadds",
+	FMADDS_:       "fmadds.",
+	FMSUB:         "fmsub",
+	FMSUB_:        "fmsub.",
+	FMSUBS:        "fmsubs",
+	FMSUBS_:       "fmsubs.",
+	FNMADD:        "fnmadd",
+	FNMADD_:       "fnmadd.",
+	FNMADDS:       "fnmadds",
+	FNMADDS_:      "fnmadds.",
+	FNMSUB:        "fnmsub",
+	FNMSUB_:       "fnmsub.",
+	FNMSUBS:       "fnmsubs",
+	FNMSUBS_:      "fnmsubs.",
+	FRSP:          "frsp",
+	FRSP_:         "frsp.",
+	FCTID:         "fctid",
+	FCTID_:        "fctid.",
+	FCTIDZ:        "fctidz",
+	FCTIDZ_:       "fctidz.",
+	FCTIDU:        "fctidu",
+	FCTIDU_:       "fctidu.",
+	FCTIDUZ:       "fctiduz",
+	FCTIDUZ_:      "fctiduz.",
+	FCTIW:         "fctiw",
+	FCTIW_:        "fctiw.",
+	FCTIWZ:        "fctiwz",
+	FCTIWZ_:       "fctiwz.",
+	FCTIWU:        "fctiwu",
+	FCTIWU_:       "fctiwu.",
+	FCTIWUZ:       "fctiwuz",
+	FCTIWUZ_:      "fctiwuz.",
+	FCFID:         "fcfid",
+	FCFID_:        "fcfid.",
+	FCFIDU:        "fcfidu",
+	FCFIDU_:       "fcfidu.",
+	FCFIDS:        "fcfids",
+	FCFIDS_:       "fcfids.",
+	FCFIDUS:       "fcfidus",
+	FCFIDUS_:      "fcfidus.",
+	FRIN:          "frin",
+	FRIN_:         "frin.",
+	FRIZ:          "friz",
+	FRIZ_:         "friz.",
+	FRIP:          "frip",
+	FRIP_:         "frip.",
+	FRIM:          "frim",
+	FRIM_:         "frim.",
+	FCMPU:         "fcmpu",
+	FCMPO:         "fcmpo",
+	FSEL:          "fsel",
+	FSEL_:         "fsel.",
+	MFFS:          "mffs",
+	MFFS_:         "mffs.",
+	MCRFS:         "mcrfs",
+	MTFSFI:        "mtfsfi",
+	MTFSFI_:       "mtfsfi.",
+	MTFSF:         "mtfsf",
+	MTFSF_:        "mtfsf.",
+	MTFSB0:        "mtfsb0",
+	MTFSB0_:       "mtfsb0.",
+	MTFSB1:        "mtfsb1",
+	MTFSB1_:       "mtfsb1.",
+	LVEBX:         "lvebx",
+	LVEHX:         "lvehx",
+	LVEWX:         "lvewx",
+	LVX:           "lvx",
+	LVXL:          "lvxl",
+	STVEBX:        "stvebx",
+	STVEHX:        "stvehx",
+	STVEWX:        "stvewx",
+	STVX:          "stvx",
+	STVXL:         "stvxl",
+	LVSL:          "lvsl",
+	LVSR:          "lvsr",
+	VPKPX:         "vpkpx",
+	VPKSDSS:       "vpksdss",
+	VPKSDUS:       "vpksdus",
+	VPKSHSS:       "vpkshss",
+	VPKSHUS:       "vpkshus",
+	VPKSWSS:       "vpkswss",
+	VPKSWUS:       "vpkswus",
+	VPKUDUM:       "vpkudum",
+	VPKUDUS:       "vpkudus",
+	VPKUHUM:       "vpkuhum",
+	VPKUHUS:       "vpkuhus",
+	VPKUWUM:       "vpkuwum",
+	VPKUWUS:       "vpkuwus",
+	VUPKHPX:       "vupkhpx",
+	VUPKLPX:       "vupklpx",
+	VUPKHSB:       "vupkhsb",
+	VUPKHSH:       "vupkhsh",
+	VUPKHSW:       "vupkhsw",
+	VUPKLSB:       "vupklsb",
+	VUPKLSH:       "vupklsh",
+	VUPKLSW:       "vupklsw",
+	VMRGHB:        "vmrghb",
+	VMRGHH:        "vmrghh",
+	VMRGLB:        "vmrglb",
+	VMRGLH:        "vmrglh",
+	VMRGHW:        "vmrghw",
+	VMRGLW:        "vmrglw",
+	VMRGEW:        "vmrgew",
+	VMRGOW:        "vmrgow",
+	VSPLTB:        "vspltb",
+	VSPLTH:        "vsplth",
+	VSPLTW:        "vspltw",
+	VSPLTISB:      "vspltisb",
+	VSPLTISH:      "vspltish",
+	VSPLTISW:      "vspltisw",
+	VPERM:         "vperm",
+	VSEL:          "vsel",
+	VSL:           "vsl",
+	VSLDOI:        "vsldoi",
+	VSLO:          "vslo",
+	VSR:           "vsr",
+	VSRO:          "vsro",
+	VADDCUW:       "vaddcuw",
+	VADDSBS:       "vaddsbs",
+	VADDSHS:       "vaddshs",
+	VADDSWS:       "vaddsws",
+	VADDUBM:       "vaddubm",
+	VADDUDM:       "vaddudm",
+	VADDUHM:       "vadduhm",
+	VADDUWM:       "vadduwm",
+	VADDUBS:       "vaddubs",
+	VADDUHS:       "vadduhs",
+	VADDUWS:       "vadduws",
+	VADDUQM:       "vadduqm",
+	VADDEUQM:      "vaddeuqm",
+	VADDCUQ:       "vaddcuq",
+	VADDECUQ:      "vaddecuq",
+	VSUBCUW:       "vsubcuw",
+	VSUBSBS:       "vsubsbs",
+	VSUBSHS:       "vsubshs",
+	VSUBSWS:       "vsubsws",
+	VSUBUBM:       "vsububm",
+	VSUBUDM:       "vsubudm",
+	VSUBUHM:       "vsubuhm",
+	VSUBUWM:       "vsubuwm",
+	VSUBUBS:       "vsububs",
+	VSUBUHS:       "vsubuhs",
+	VSUBUWS:       "vsubuws",
+	VSUBUQM:       "vsubuqm",
+	VSUBEUQM:      "vsubeuqm",
+	VSUBCUQ:       "vsubcuq",
+	VSUBECUQ:      "vsubecuq",
+	VMULESB:       "vmulesb",
+	VMULEUB:       "vmuleub",
+	VMULOSB:       "vmulosb",
+	VMULOUB:       "vmuloub",
+	VMULESH:       "vmulesh",
+	VMULEUH:       "vmuleuh",
+	VMULOSH:       "vmulosh",
+	VMULOUH:       "vmulouh",
+	VMULESW:       "vmulesw",
+	VMULEUW:       "vmuleuw",
+	VMULOSW:       "vmulosw",
+	VMULOUW:       "vmulouw",
+	VMULUWM:       "vmuluwm",
+	VMHADDSHS:     "vmhaddshs",
+	VMHRADDSHS:    "vmhraddshs",
+	VMLADDUHM:     "vmladduhm",
+	VMSUMUBM:      "vmsumubm",
+	VMSUMMBM:      "vmsummbm",
+	VMSUMSHM:      "vmsumshm",
+	VMSUMSHS:      "vmsumshs",
+	VMSUMUHM:      "vmsumuhm",
+	VMSUMUHS:      "vmsumuhs",
+	VSUMSWS:       "vsumsws",
+	VSUM2SWS:      "vsum2sws",
+	VSUM4SBS:      "vsum4sbs",
+	VSUM4SHS:      "vsum4shs",
+	VSUM4UBS:      "vsum4ubs",
+	VAVGSB:        "vavgsb",
+	VAVGSH:        "vavgsh",
+	VAVGSW:        "vavgsw",
+	VAVGUB:        "vavgub",
+	VAVGUW:        "vavguw",
+	VAVGUH:        "vavguh",
+	VMAXSB:        "vmaxsb",
+	VMAXSD:        "vmaxsd",
+	VMAXUB:        "vmaxub",
+	VMAXUD:        "vmaxud",
+	VMAXSH:        "vmaxsh",
+	VMAXSW:        "vmaxsw",
+	VMAXUH:        "vmaxuh",
+	VMAXUW:        "vmaxuw",
+	VMINSB:        "vminsb",
+	VMINSD:        "vminsd",
+	VMINUB:        "vminub",
+	VMINUD:        "vminud",
+	VMINSH:        "vminsh",
+	VMINSW:        "vminsw",
+	VMINUH:        "vminuh",
+	VMINUW:        "vminuw",
+	VCMPEQUB:      "vcmpequb",
+	VCMPEQUB_:     "vcmpequb.",
+	VCMPEQUH:      "vcmpequh",
+	VCMPEQUH_:     "vcmpequh.",
+	VCMPEQUW:      "vcmpequw",
+	VCMPEQUW_:     "vcmpequw.",
+	VCMPEQUD:      "vcmpequd",
+	VCMPEQUD_:     "vcmpequd.",
+	VCMPGTSB:      "vcmpgtsb",
+	VCMPGTSB_:     "vcmpgtsb.",
+	VCMPGTSD:      "vcmpgtsd",
+	VCMPGTSD_:     "vcmpgtsd.",
+	VCMPGTSH:      "vcmpgtsh",
+	VCMPGTSH_:     "vcmpgtsh.",
+	VCMPGTSW:      "vcmpgtsw",
+	VCMPGTSW_:     "vcmpgtsw.",
+	VCMPGTUB:      "vcmpgtub",
+	VCMPGTUB_:     "vcmpgtub.",
+	VCMPGTUD:      "vcmpgtud",
+	VCMPGTUD_:     "vcmpgtud.",
+	VCMPGTUH:      "vcmpgtuh",
+	VCMPGTUH_:     "vcmpgtuh.",
+	VCMPGTUW:      "vcmpgtuw",
+	VCMPGTUW_:     "vcmpgtuw.",
+	VAND:          "vand",
+	VANDC:         "vandc",
+	VEQV:          "veqv",
+	VNAND:         "vnand",
+	VORC:          "vorc",
+	VNOR:          "vnor",
+	VOR:           "vor",
+	VXOR:          "vxor",
+	VRLB:          "vrlb",
+	VRLH:          "vrlh",
+	VRLW:          "vrlw",
+	VRLD:          "vrld",
+	VSLB:          "vslb",
+	VSLH:          "vslh",
+	VSLW:          "vslw",
+	VSLD:          "vsld",
+	VSRB:          "vsrb",
+	VSRH:          "vsrh",
+	VSRW:          "vsrw",
+	VSRD:          "vsrd",
+	VSRAB:         "vsrab",
+	VSRAH:         "vsrah",
+	VSRAW:         "vsraw",
+	VSRAD:         "vsrad",
+	VADDFP:        "vaddfp",
+	VSUBFP:        "vsubfp",
+	VMADDFP:       "vmaddfp",
+	VNMSUBFP:      "vnmsubfp",
+	VMAXFP:        "vmaxfp",
+	VMINFP:        "vminfp",
+	VCTSXS:        "vctsxs",
+	VCTUXS:        "vctuxs",
+	VCFSX:         "vcfsx",
+	VCFUX:         "vcfux",
+	VRFIM:         "vrfim",
+	VRFIN:         "vrfin",
+	VRFIP:         "vrfip",
+	VRFIZ:         "vrfiz",
+	VCMPBFP:       "vcmpbfp",
+	VCMPBFP_:      "vcmpbfp.",
+	VCMPEQFP:      "vcmpeqfp",
+	VCMPEQFP_:     "vcmpeqfp.",
+	VCMPGEFP:      "vcmpgefp",
+	VCMPGEFP_:     "vcmpgefp.",
+	VCMPGTFP:      "vcmpgtfp",
+	VCMPGTFP_:     "vcmpgtfp.",
+	VEXPTEFP:      "vexptefp",
+	VLOGEFP:       "vlogefp",
+	VREFP:         "vrefp",
+	VRSQRTEFP:     "vrsqrtefp",
+	VCIPHER:       "vcipher",
+	VCIPHERLAST:   "vcipherlast",
+	VNCIPHER:      "vncipher",
+	VNCIPHERLAST:  "vncipherlast",
+	VSBOX:         "vsbox",
+	VSHASIGMAD:    "vshasigmad",
+	VSHASIGMAW:    "vshasigmaw",
+	VPMSUMB:       "vpmsumb",
+	VPMSUMD:       "vpmsumd",
+	VPMSUMH:       "vpmsumh",
+	VPMSUMW:       "vpmsumw",
+	VPERMXOR:      "vpermxor",
+	VGBBD:         "vgbbd",
+	VCLZB:         "vclzb",
+	VCLZH:         "vclzh",
+	VCLZW:         "vclzw",
+	VCLZD:         "vclzd",
+	VPOPCNTB:      "vpopcntb",
+	VPOPCNTD:      "vpopcntd",
+	VPOPCNTH:      "vpopcnth",
+	VPOPCNTW:      "vpopcntw",
+	VBPERMQ:       "vbpermq",
+	BCDADD_:       "bcdadd.",
+	BCDSUB_:       "bcdsub.",
+	MTVSCR:        "mtvscr",
+	MFVSCR:        "mfvscr",
+	DADD:          "dadd",
+	DADD_:         "dadd.",
+	DSUB:          "dsub",
+	DSUB_:         "dsub.",
+	DMUL:          "dmul",
+	DMUL_:         "dmul.",
+	DDIV:          "ddiv",
+	DDIV_:         "ddiv.",
+	DCMPU:         "dcmpu",
+	DCMPO:         "dcmpo",
+	DTSTDC:        "dtstdc",
+	DTSTDG:        "dtstdg",
+	DTSTEX:        "dtstex",
+	DTSTSF:        "dtstsf",
+	DQUAI:         "dquai",
+	DQUAI_:        "dquai.",
+	DQUA:          "dqua",
+	DQUA_:         "dqua.",
+	DRRND:         "drrnd",
+	DRRND_:        "drrnd.",
+	DRINTX:        "drintx",
+	DRINTX_:       "drintx.",
+	DRINTN:        "drintn",
+	DRINTN_:       "drintn.",
+	DCTDP:         "dctdp",
+	DCTDP_:        "dctdp.",
+	DCTQPQ:        "dctqpq",
+	DCTQPQ_:       "dctqpq.",
+	DRSP:          "drsp",
+	DRSP_:         "drsp.",
+	DRDPQ:         "drdpq",
+	DRDPQ_:        "drdpq.",
+	DCFFIX:        "dcffix",
+	DCFFIX_:       "dcffix.",
+	DCFFIXQ:       "dcffixq",
+	DCFFIXQ_:      "dcffixq.",
+	DCTFIX:        "dctfix",
+	DCTFIX_:       "dctfix.",
+	DDEDPD:        "ddedpd",
+	DDEDPD_:       "ddedpd.",
+	DENBCD:        "denbcd",
+	DENBCD_:       "denbcd.",
+	DXEX:          "dxex",
+	DXEX_:         "dxex.",
+	DIEX:          "diex",
+	DIEX_:         "diex.",
+	DSCLI:         "dscli",
+	DSCLI_:        "dscli.",
+	DSCRI:         "dscri",
+	DSCRI_:        "dscri.",
+	LXSDX:         "lxsdx",
+	LXSIWAX:       "lxsiwax",
+	LXSIWZX:       "lxsiwzx",
+	LXSSPX:        "lxsspx",
+	LXVD2X:        "lxvd2x",
+	LXVDSX:        "lxvdsx",
+	LXVW4X:        "lxvw4x",
+	STXSDX:        "stxsdx",
+	STXSIWX:       "stxsiwx",
+	STXSSPX:       "stxsspx",
+	STXVD2X:       "stxvd2x",
+	STXVW4X:       "stxvw4x",
+	XSABSDP:       "xsabsdp",
+	XSADDDP:       "xsadddp",
+	XSADDSP:       "xsaddsp",
+	XSCMPODP:      "xscmpodp",
+	XSCMPUDP:      "xscmpudp",
+	XSCPSGNDP:     "xscpsgndp",
+	XSCVDPSP:      "xscvdpsp",
+	XSCVDPSPN:     "xscvdpspn",
+	XSCVDPSXDS:    "xscvdpsxds",
+	XSCVDPSXWS:    "xscvdpsxws",
+	XSCVDPUXDS:    "xscvdpuxds",
+	XSCVDPUXWS:    "xscvdpuxws",
+	XSCVSPDP:      "xscvspdp",
+	XSCVSPDPN:     "xscvspdpn",
+	XSCVSXDDP:     "xscvsxddp",
+	XSCVSXDSP:     "xscvsxdsp",
+	XSCVUXDDP:     "xscvuxddp",
+	XSCVUXDSP:     "xscvuxdsp",
+	XSDIVDP:       "xsdivdp",
+	XSDIVSP:       "xsdivsp",
+	XSMADDADP:     "xsmaddadp",
+	XSMADDASP:     "xsmaddasp",
+	XSMAXDP:       "xsmaxdp",
+	XSMINDP:       "xsmindp",
+	XSMSUBADP:     "xsmsubadp",
+	XSMSUBASP:     "xsmsubasp",
+	XSMULDP:       "xsmuldp",
+	XSMULSP:       "xsmulsp",
+	XSNABSDP:      "xsnabsdp",
+	XSNEGDP:       "xsnegdp",
+	XSNMADDADP:    "xsnmaddadp",
+	XSNMADDASP:    "xsnmaddasp",
+	XSNMSUBADP:    "xsnmsubadp",
+	XSNMSUBASP:    "xsnmsubasp",
+	XSRDPI:        "xsrdpi",
+	XSRDPIC:       "xsrdpic",
+	XSRDPIM:       "xsrdpim",
+	XSRDPIP:       "xsrdpip",
+	XSRDPIZ:       "xsrdpiz",
+	XSREDP:        "xsredp",
+	XSRESP:        "xsresp",
+	XSRSP:         "xsrsp",
+	XSRSQRTEDP:    "xsrsqrtedp",
+	XSRSQRTESP:    "xsrsqrtesp",
+	XSSQRTDP:      "xssqrtdp",
+	XSSQRTSP:      "xssqrtsp",
+	XSSUBDP:       "xssubdp",
+	XSSUBSP:       "xssubsp",
+	XSTDIVDP:      "xstdivdp",
+	XSTSQRTDP:     "xstsqrtdp",
+	XVABSDP:       "xvabsdp",
+	XVABSSP:       "xvabssp",
+	XVADDDP:       "xvadddp",
+	XVADDSP:       "xvaddsp",
+	XVCMPEQDP:     "xvcmpeqdp",
+	XVCMPEQDP_:    "xvcmpeqdp.",
+	XVCMPEQSP:     "xvcmpeqsp",
+	XVCMPEQSP_:    "xvcmpeqsp.",
+	XVCMPGEDP:     "xvcmpgedp",
+	XVCMPGEDP_:    "xvcmpgedp.",
+	XVCMPGESP:     "xvcmpgesp",
+	XVCMPGESP_:    "xvcmpgesp.",
+	XVCMPGTDP:     "xvcmpgtdp",
+	XVCMPGTDP_:    "xvcmpgtdp.",
+	XVCMPGTSP:     "xvcmpgtsp",
+	XVCMPGTSP_:    "xvcmpgtsp.",
+	XVCPSGNDP:     "xvcpsgndp",
+	XVCPSGNSP:     "xvcpsgnsp",
+	XVCVDPSP:      "xvcvdpsp",
+	XVCVDPSXDS:    "xvcvdpsxds",
+	XVCVDPSXWS:    "xvcvdpsxws",
+	XVCVDPUXDS:    "xvcvdpuxds",
+	XVCVDPUXWS:    "xvcvdpuxws",
+	XVCVSPDP:      "xvcvspdp",
+	XVCVSPSXDS:    "xvcvspsxds",
+	XVCVSPSXWS:    "xvcvspsxws",
+	XVCVSPUXDS:    "xvcvspuxds",
+	XVCVSPUXWS:    "xvcvspuxws",
+	XVCVSXDDP:     "xvcvsxddp",
+	XVCVSXDSP:     "xvcvsxdsp",
+	XVCVSXWDP:     "xvcvsxwdp",
+	XVCVSXWSP:     "xvcvsxwsp",
+	XVCVUXDDP:     "xvcvuxddp",
+	XVCVUXDSP:     "xvcvuxdsp",
+	XVCVUXWDP:     "xvcvuxwdp",
+	XVCVUXWSP:     "xvcvuxwsp",
+	XVDIVDP:       "xvdivdp",
+	XVDIVSP:       "xvdivsp",
+	XVMADDADP:     "xvmaddadp",
+	XVMADDASP:     "xvmaddasp",
+	XVMAXDP:       "xvmaxdp",
+	XVMAXSP:       "xvmaxsp",
+	XVMINDP:       "xvmindp",
+	XVMINSP:       "xvminsp",
+	XVMSUBADP:     "xvmsubadp",
+	XVMSUBASP:     "xvmsubasp",
+	XVMULDP:       "xvmuldp",
+	XVMULSP:       "xvmulsp",
+	XVNABSDP:      "xvnabsdp",
+	XVNABSSP:      "xvnabssp",
+	XVNEGDP:       "xvnegdp",
+	XVNEGSP:       "xvnegsp",
+	XVNMADDADP:    "xvnmaddadp",
+	XVNMADDASP:    "xvnmaddasp",
+	XVNMSUBADP:    "xvnmsubadp",
+	XVNMSUBASP:    "xvnmsubasp",
+	XVRDPI:        "xvrdpi",
+	XVRDPIC:       "xvrdpic",
+	XVRDPIM:       "xvrdpim",
+	XVRDPIP:       "xvrdpip",
+	XVRDPIZ:       "xvrdpiz",
+	XVREDP:        "xvredp",
+	XVRESP:        "xvresp",
+	XVRSPI:        "xvrspi",
+	XVRSPIC:       "xvrspic",
+	XVRSPIM:       "xvrspim",
+	XVRSPIP:       "xvrspip",
+	XVRSPIZ:       "xvrspiz",
+	XVRSQRTEDP:    "xvrsqrtedp",
+	XVRSQRTESP:    "xvrsqrtesp",
+	XVSQRTDP:      "xvsqrtdp",
+	XVSQRTSP:      "xvsqrtsp",
+	XVSUBDP:       "xvsubdp",
+	XVSUBSP:       "xvsubsp",
+	XVTDIVDP:      "xvtdivdp",
+	XVTDIVSP:      "xvtdivsp",
+	XVTSQRTDP:     "xvtsqrtdp",
+	XVTSQRTSP:     "xvtsqrtsp",
+	XXLAND:        "xxland",
+	XXLANDC:       "xxlandc",
+	XXLEQV:        "xxleqv",
+	XXLNAND:       "xxlnand",
+	XXLORC:        "xxlorc",
+	XXLNOR:        "xxlnor",
+	XXLOR:         "xxlor",
+	XXLXOR:        "xxlxor",
+	XXMRGHW:       "xxmrghw",
+	XXMRGLW:       "xxmrglw",
+	XXPERMDI:      "xxpermdi",
+	XXSEL:         "xxsel",
+	XXSLDWI:       "xxsldwi",
+	XXSPLTW:       "xxspltw",
+	BRINC:         "brinc",
+	EVABS:         "evabs",
+	EVADDIW:       "evaddiw",
+	EVADDSMIAAW:   "evaddsmiaaw",
+	EVADDSSIAAW:   "evaddssiaaw",
+	EVADDUMIAAW:   "evaddumiaaw",
+	EVADDUSIAAW:   "evaddusiaaw",
+	EVADDW:        "evaddw",
+	EVAND:         "evand",
+	EVCMPEQ:       "evcmpeq",
+	EVANDC:        "evandc",
+	EVCMPGTS:      "evcmpgts",
+	EVCMPGTU:      "evcmpgtu",
+	EVCMPLTU:      "evcmpltu",
+	EVCMPLTS:      "evcmplts",
+	EVCNTLSW:      "evcntlsw",
+	EVCNTLZW:      "evcntlzw",
+	EVDIVWS:       "evdivws",
+	EVDIVWU:       "evdivwu",
+	EVEQV:         "eveqv",
+	EVEXTSB:       "evextsb",
+	EVEXTSH:       "evextsh",
+	EVLDD:         "evldd",
+	EVLDH:         "evldh",
+	EVLDDX:        "evlddx",
+	EVLDHX:        "evldhx",
+	EVLDW:         "evldw",
+	EVLHHESPLAT:   "evlhhesplat",
+	EVLDWX:        "evldwx",
+	EVLHHESPLATX:  "evlhhesplatx",
+	EVLHHOSSPLAT:  "evlhhossplat",
+	EVLHHOUSPLAT:  "evlhhousplat",
+	EVLHHOSSPLATX: "evlhhossplatx",
+	EVLHHOUSPLATX: "evlhhousplatx",
+	EVLWHE:        "evlwhe",
+	EVLWHOS:       "evlwhos",
+	EVLWHEX:       "evlwhex",
+	EVLWHOSX:      "evlwhosx",
+	EVLWHOU:       "evlwhou",
+	EVLWHSPLAT:    "evlwhsplat",
+	EVLWHOUX:      "evlwhoux",
+	EVLWHSPLATX:   "evlwhsplatx",
+	EVLWWSPLAT:    "evlwwsplat",
+	EVMERGEHI:     "evmergehi",
+	EVLWWSPLATX:   "evlwwsplatx",
+	EVMERGELO:     "evmergelo",
+	EVMERGEHILO:   "evmergehilo",
+	EVMHEGSMFAA:   "evmhegsmfaa",
+	EVMERGELOHI:   "evmergelohi",
+	EVMHEGSMFAN:   "evmhegsmfan",
+	EVMHEGSMIAA:   "evmhegsmiaa",
+	EVMHEGUMIAA:   "evmhegumiaa",
+	EVMHEGSMIAN:   "evmhegsmian",
+	EVMHEGUMIAN:   "evmhegumian",
+	EVMHESMF:      "evmhesmf",
+	EVMHESMFAAW:   "evmhesmfaaw",
+	EVMHESMFA:     "evmhesmfa",
+	EVMHESMFANW:   "evmhesmfanw",
+	EVMHESMI:      "evmhesmi",
+	EVMHESMIAAW:   "evmhesmiaaw",
+	EVMHESMIA:     "evmhesmia",
+	EVMHESMIANW:   "evmhesmianw",
+	EVMHESSF:      "evmhessf",
+	EVMHESSFA:     "evmhessfa",
+	EVMHESSFAAW:   "evmhessfaaw",
+	EVMHESSFANW:   "evmhessfanw",
+	EVMHESSIAAW:   "evmhessiaaw",
+	EVMHESSIANW:   "evmhessianw",
+	EVMHEUMI:      "evmheumi",
+	EVMHEUMIAAW:   "evmheumiaaw",
+	EVMHEUMIA:     "evmheumia",
+	EVMHEUMIANW:   "evmheumianw",
+	EVMHEUSIAAW:   "evmheusiaaw",
+	EVMHEUSIANW:   "evmheusianw",
+	EVMHOGSMFAA:   "evmhogsmfaa",
+	EVMHOGSMIAA:   "evmhogsmiaa",
+	EVMHOGSMFAN:   "evmhogsmfan",
+	EVMHOGSMIAN:   "evmhogsmian",
+	EVMHOGUMIAA:   "evmhogumiaa",
+	EVMHOSMF:      "evmhosmf",
+	EVMHOGUMIAN:   "evmhogumian",
+	EVMHOSMFA:     "evmhosmfa",
+	EVMHOSMFAAW:   "evmhosmfaaw",
+	EVMHOSMI:      "evmhosmi",
+	EVMHOSMFANW:   "evmhosmfanw",
+	EVMHOSMIA:     "evmhosmia",
+	EVMHOSMIAAW:   "evmhosmiaaw",
+	EVMHOSMIANW:   "evmhosmianw",
+	EVMHOSSF:      "evmhossf",
+	EVMHOSSFA:     "evmhossfa",
+	EVMHOSSFAAW:   "evmhossfaaw",
+	EVMHOSSFANW:   "evmhossfanw",
+	EVMHOSSIAAW:   "evmhossiaaw",
+	EVMHOUMI:      "evmhoumi",
+	EVMHOSSIANW:   "evmhossianw",
+	EVMHOUMIA:     "evmhoumia",
+	EVMHOUMIAAW:   "evmhoumiaaw",
+	EVMHOUSIAAW:   "evmhousiaaw",
+	EVMHOUMIANW:   "evmhoumianw",
+	EVMHOUSIANW:   "evmhousianw",
+	EVMRA:         "evmra",
+	EVMWHSMF:      "evmwhsmf",
+	EVMWHSMI:      "evmwhsmi",
+	EVMWHSMFA:     "evmwhsmfa",
+	EVMWHSMIA:     "evmwhsmia",
+	EVMWHSSF:      "evmwhssf",
+	EVMWHUMI:      "evmwhumi",
+	EVMWHSSFA:     "evmwhssfa",
+	EVMWHUMIA:     "evmwhumia",
+	EVMWLSMIAAW:   "evmwlsmiaaw",
+	EVMWLSSIAAW:   "evmwlssiaaw",
+	EVMWLSMIANW:   "evmwlsmianw",
+	EVMWLSSIANW:   "evmwlssianw",
+	EVMWLUMI:      "evmwlumi",
+	EVMWLUMIAAW:   "evmwlumiaaw",
+	EVMWLUMIA:     "evmwlumia",
+	EVMWLUMIANW:   "evmwlumianw",
+	EVMWLUSIAAW:   "evmwlusiaaw",
+	EVMWSMF:       "evmwsmf",
+	EVMWLUSIANW:   "evmwlusianw",
+	EVMWSMFA:      "evmwsmfa",
+	EVMWSMFAA:     "evmwsmfaa",
+	EVMWSMI:       "evmwsmi",
+	EVMWSMIAA:     "evmwsmiaa",
+	EVMWSMFAN:     "evmwsmfan",
+	EVMWSMIA:      "evmwsmia",
+	EVMWSMIAN:     "evmwsmian",
+	EVMWSSF:       "evmwssf",
+	EVMWSSFA:      "evmwssfa",
+	EVMWSSFAA:     "evmwssfaa",
+	EVMWUMI:       "evmwumi",
+	EVMWSSFAN:     "evmwssfan",
+	EVMWUMIA:      "evmwumia",
+	EVMWUMIAA:     "evmwumiaa",
+	EVNAND:        "evnand",
+	EVMWUMIAN:     "evmwumian",
+	EVNEG:         "evneg",
+	EVNOR:         "evnor",
+	EVORC:         "evorc",
+	EVOR:          "evor",
+	EVRLW:         "evrlw",
+	EVRLWI:        "evrlwi",
+	EVSEL:         "evsel",
+	EVRNDW:        "evrndw",
+	EVSLW:         "evslw",
+	EVSPLATFI:     "evsplatfi",
+	EVSRWIS:       "evsrwis",
+	EVSLWI:        "evslwi",
+	EVSPLATI:      "evsplati",
+	EVSRWIU:       "evsrwiu",
+	EVSRWS:        "evsrws",
+	EVSTDD:        "evstdd",
+	EVSRWU:        "evsrwu",
+	EVSTDDX:       "evstddx",
+	EVSTDH:        "evstdh",
+	EVSTDW:        "evstdw",
+	EVSTDHX:       "evstdhx",
+	EVSTDWX:       "evstdwx",
+	EVSTWHE:       "evstwhe",
+	EVSTWHO:       "evstwho",
+	EVSTWWE:       "evstwwe",
+	EVSTWHEX:      "evstwhex",
+	EVSTWHOX:      "evstwhox",
+	EVSTWWEX:      "evstwwex",
+	EVSTWWO:       "evstwwo",
+	EVSUBFSMIAAW:  "evsubfsmiaaw",
+	EVSTWWOX:      "evstwwox",
+	EVSUBFSSIAAW:  "evsubfssiaaw",
+	EVSUBFUMIAAW:  "evsubfumiaaw",
+	EVSUBFUSIAAW:  "evsubfusiaaw",
+	EVSUBFW:       "evsubfw",
+	EVSUBIFW:      "evsubifw",
+	EVXOR:         "evxor",
+	EVFSABS:       "evfsabs",
+	EVFSNABS:      "evfsnabs",
+	EVFSNEG:       "evfsneg",
+	EVFSADD:       "evfsadd",
+	EVFSMUL:       "evfsmul",
+	EVFSSUB:       "evfssub",
+	EVFSDIV:       "evfsdiv",
+	EVFSCMPGT:     "evfscmpgt",
+	EVFSCMPLT:     "evfscmplt",
+	EVFSCMPEQ:     "evfscmpeq",
+	EVFSTSTGT:     "evfststgt",
+	EVFSTSTLT:     "evfststlt",
+	EVFSTSTEQ:     "evfststeq",
+	EVFSCFSI:      "evfscfsi",
+	EVFSCFSF:      "evfscfsf",
+	EVFSCFUI:      "evfscfui",
+	EVFSCFUF:      "evfscfuf",
+	EVFSCTSI:      "evfsctsi",
+	EVFSCTUI:      "evfsctui",
+	EVFSCTSIZ:     "evfsctsiz",
+	EVFSCTUIZ:     "evfsctuiz",
+	EVFSCTSF:      "evfsctsf",
+	EVFSCTUF:      "evfsctuf",
+	EFSABS:        "efsabs",
+	EFSNEG:        "efsneg",
+	EFSNABS:       "efsnabs",
+	EFSADD:        "efsadd",
+	EFSMUL:        "efsmul",
+	EFSSUB:        "efssub",
+	EFSDIV:        "efsdiv",
+	EFSCMPGT:      "efscmpgt",
+	EFSCMPLT:      "efscmplt",
+	EFSCMPEQ:      "efscmpeq",
+	EFSTSTGT:      "efststgt",
+	EFSTSTLT:      "efststlt",
+	EFSTSTEQ:      "efststeq",
+	EFSCFSI:       "efscfsi",
+	EFSCFSF:       "efscfsf",
+	EFSCTSI:       "efsctsi",
+	EFSCFUI:       "efscfui",
+	EFSCFUF:       "efscfuf",
+	EFSCTUI:       "efsctui",
+	EFSCTSIZ:      "efsctsiz",
+	EFSCTSF:       "efsctsf",
+	EFSCTUIZ:      "efsctuiz",
+	EFSCTUF:       "efsctuf",
+	EFDABS:        "efdabs",
+	EFDNEG:        "efdneg",
+	EFDNABS:       "efdnabs",
+	EFDADD:        "efdadd",
+	EFDMUL:        "efdmul",
+	EFDSUB:        "efdsub",
+	EFDDIV:        "efddiv",
+	EFDCMPGT:      "efdcmpgt",
+	EFDCMPEQ:      "efdcmpeq",
+	EFDCMPLT:      "efdcmplt",
+	EFDTSTGT:      "efdtstgt",
+	EFDTSTLT:      "efdtstlt",
+	EFDCFSI:       "efdcfsi",
+	EFDTSTEQ:      "efdtsteq",
+	EFDCFUI:       "efdcfui",
+	EFDCFSID:      "efdcfsid",
+	EFDCFSF:       "efdcfsf",
+	EFDCFUF:       "efdcfuf",
+	EFDCFUID:      "efdcfuid",
+	EFDCTSI:       "efdctsi",
+	EFDCTUI:       "efdctui",
+	EFDCTSIDZ:     "efdctsidz",
+	EFDCTUIDZ:     "efdctuidz",
+	EFDCTSIZ:      "efdctsiz",
+	EFDCTSF:       "efdctsf",
+	EFDCTUF:       "efdctuf",
+	EFDCTUIZ:      "efdctuiz",
+	EFDCFS:        "efdcfs",
+	EFSCFD:        "efscfd",
+	DLMZB:         "dlmzb",
+	DLMZB_:        "dlmzb.",
+	MACCHW:        "macchw",
+	MACCHW_:       "macchw.",
+	MACCHWO:       "macchwo",
+	MACCHWO_:      "macchwo.",
+	MACCHWS:       "macchws",
+	MACCHWS_:      "macchws.",
+	MACCHWSO:      "macchwso",
+	MACCHWSO_:     "macchwso.",
+	MACCHWU:       "macchwu",
+	MACCHWU_:      "macchwu.",
+	MACCHWUO:      "macchwuo",
+	MACCHWUO_:     "macchwuo.",
+	MACCHWSU:      "macchwsu",
+	MACCHWSU_:     "macchwsu.",
+	MACCHWSUO:     "macchwsuo",
+	MACCHWSUO_:    "macchwsuo.",
+	MACHHW:        "machhw",
+	MACHHW_:       "machhw.",
+	MACHHWO:       "machhwo",
+	MACHHWO_:      "machhwo.",
+	MACHHWS:       "machhws",
+	MACHHWS_:      "machhws.",
+	MACHHWSO:      "machhwso",
+	MACHHWSO_:     "machhwso.",
+	MACHHWU:       "machhwu",
+	MACHHWU_:      "machhwu.",
+	MACHHWUO:      "machhwuo",
+	MACHHWUO_:     "machhwuo.",
+	MACHHWSU:      "machhwsu",
+	MACHHWSU_:     "machhwsu.",
+	MACHHWSUO:     "machhwsuo",
+	MACHHWSUO_:    "machhwsuo.",
+	MACLHW:        "maclhw",
+	MACLHW_:       "maclhw.",
+	MACLHWO:       "maclhwo",
+	MACLHWO_:      "maclhwo.",
+	MACLHWS:       "maclhws",
+	MACLHWS_:      "maclhws.",
+	MACLHWSO:      "maclhwso",
+	MACLHWSO_:     "maclhwso.",
+	MACLHWU:       "maclhwu",
+	MACLHWU_:      "maclhwu.",
+	MACLHWUO:      "maclhwuo",
+	MACLHWUO_:     "maclhwuo.",
+	MULCHW:        "mulchw",
+	MULCHW_:       "mulchw.",
+	MACLHWSU:      "maclhwsu",
+	MACLHWSU_:     "maclhwsu.",
+	MACLHWSUO:     "maclhwsuo",
+	MACLHWSUO_:    "maclhwsuo.",
+	MULCHWU:       "mulchwu",
+	MULCHWU_:      "mulchwu.",
+	MULHHW:        "mulhhw",
+	MULHHW_:       "mulhhw.",
+	MULLHW:        "mullhw",
+	MULLHW_:       "mullhw.",
+	MULHHWU:       "mulhhwu",
+	MULHHWU_:      "mulhhwu.",
+	MULLHWU:       "mullhwu",
+	MULLHWU_:      "mullhwu.",
+	NMACCHW:       "nmacchw",
+	NMACCHW_:      "nmacchw.",
+	NMACCHWO:      "nmacchwo",
+	NMACCHWO_:     "nmacchwo.",
+	NMACCHWS:      "nmacchws",
+	NMACCHWS_:     "nmacchws.",
+	NMACCHWSO:     "nmacchwso",
+	NMACCHWSO_:    "nmacchwso.",
+	NMACHHW:       "nmachhw",
+	NMACHHW_:      "nmachhw.",
+	NMACHHWO:      "nmachhwo",
+	NMACHHWO_:     "nmachhwo.",
+	NMACHHWS:      "nmachhws",
+	NMACHHWS_:     "nmachhws.",
+	NMACHHWSO:     "nmachhwso",
+	NMACHHWSO_:    "nmachhwso.",
+	NMACLHW:       "nmaclhw",
+	NMACLHW_:      "nmaclhw.",
+	NMACLHWO:      "nmaclhwo",
+	NMACLHWO_:     "nmaclhwo.",
+	NMACLHWS:      "nmaclhws",
+	NMACLHWS_:     "nmaclhws.",
+	NMACLHWSO:     "nmaclhwso",
+	NMACLHWSO_:    "nmaclhwso.",
+	ICBI:          "icbi",
+	ICBT:          "icbt",
+	DCBA:          "dcba",
+	DCBT:          "dcbt",
+	DCBTST:        "dcbtst",
+	DCBZ:          "dcbz",
+	DCBST:         "dcbst",
+	DCBF:          "dcbf",
+	ISYNC:         "isync",
+	LBARX:         "lbarx",
+	LHARX:         "lharx",
+	LWARX:         "lwarx",
+	STBCX_:        "stbcx.",
+	STHCX_:        "sthcx.",
+	STWCX_:        "stwcx.",
+	LDARX:         "ldarx",
+	STDCX_:        "stdcx.",
+	LQARX:         "lqarx",
+	STQCX_:        "stqcx.",
+	SYNC:          "sync",
+	EIEIO:         "eieio",
+	MBAR:          "mbar",
+	WAIT:          "wait",
+	TBEGIN_:       "tbegin.",
+	TEND_:         "tend.",
+	TABORT_:       "tabort.",
+	TABORTWC_:     "tabortwc.",
+	TABORTWCI_:    "tabortwci.",
+	TABORTDC_:     "tabortdc.",
+	TABORTDCI_:    "tabortdci.",
+	TSR_:          "tsr.",
+	TCHECK:        "tcheck",
+	MFTB:          "mftb",
+	RFEBB:         "rfebb",
+	LBDX:          "lbdx",
+	LHDX:          "lhdx",
+	LWDX:          "lwdx",
+	LDDX:          "lddx",
+	LFDDX:         "lfddx",
+	STBDX:         "stbdx",
+	STHDX:         "sthdx",
+	STWDX:         "stwdx",
+	STDDX:         "stddx",
+	STFDDX:        "stfddx",
+	DSN:           "dsn",
+	ECIWX:         "eciwx",
+	ECOWX:         "ecowx",
+	RFID:          "rfid",
+	HRFID:         "hrfid",
+	DOZE:          "doze",
+	NAP:           "nap",
+	SLEEP:         "sleep",
+	RVWINKLE:      "rvwinkle",
+	LBZCIX:        "lbzcix",
+	LWZCIX:        "lwzcix",
+	LHZCIX:        "lhzcix",
+	LDCIX:         "ldcix",
+	STBCIX:        "stbcix",
+	STWCIX:        "stwcix",
+	STHCIX:        "sthcix",
+	STDCIX:        "stdcix",
+	TRECLAIM_:     "treclaim.",
+	TRECHKPT_:     "trechkpt.",
+	MTMSR:         "mtmsr",
+	MTMSRD:        "mtmsrd",
+	MFMSR:         "mfmsr",
+	SLBIE:         "slbie",
+	SLBIA:         "slbia",
+	SLBMTE:        "slbmte",
+	SLBMFEV:       "slbmfev",
+	SLBMFEE:       "slbmfee",
+	SLBFEE_:       "slbfee.",
+	MTSR:          "mtsr",
+	MTSRIN:        "mtsrin",
+	MFSR:          "mfsr",
+	MFSRIN:        "mfsrin",
+	TLBIE:         "tlbie",
+	TLBIEL:        "tlbiel",
+	TLBIA:         "tlbia",
+	TLBSYNC:       "tlbsync",
+	MSGSND:        "msgsnd",
+	MSGCLR:        "msgclr",
+	MSGSNDP:       "msgsndp",
+	MSGCLRP:       "msgclrp",
+	MTTMR:         "mttmr",
+	RFI:           "rfi",
+	RFCI:          "rfci",
+	RFDI:          "rfdi",
+	RFMCI:         "rfmci",
+	RFGI:          "rfgi",
+	EHPRIV:        "ehpriv",
+	MTDCR:         "mtdcr",
+	MTDCRX:        "mtdcrx",
+	MFDCR:         "mfdcr",
+	MFDCRX:        "mfdcrx",
+	WRTEE:         "wrtee",
+	WRTEEI:        "wrteei",
+	LBEPX:         "lbepx",
+	LHEPX:         "lhepx",
+	LWEPX:         "lwepx",
+	LDEPX:         "ldepx",
+	STBEPX:        "stbepx",
+	STHEPX:        "sthepx",
+	STWEPX:        "stwepx",
+	STDEPX:        "stdepx",
+	DCBSTEP:       "dcbstep",
+	DCBTEP:        "dcbtep",
+	DCBFEP:        "dcbfep",
+	DCBTSTEP:      "dcbtstep",
+	ICBIEP:        "icbiep",
+	DCBZEP:        "dcbzep",
+	LFDEPX:        "lfdepx",
+	STFDEPX:       "stfdepx",
+	EVLDDEPX:      "evlddepx",
+	EVSTDDEPX:     "evstddepx",
+	LVEPX:         "lvepx",
+	LVEPXL:        "lvepxl",
+	STVEPX:        "stvepx",
+	STVEPXL:       "stvepxl",
+	DCBI:          "dcbi",
+	DCBLQ_:        "dcblq.",
+	ICBLQ_:        "icblq.",
+	DCBTLS:        "dcbtls",
+	DCBTSTLS:      "dcbtstls",
+	ICBTLS:        "icbtls",
+	ICBLC:         "icblc",
+	DCBLC:         "dcblc",
+	TLBIVAX:       "tlbivax",
+	TLBILX:        "tlbilx",
+	TLBSX:         "tlbsx",
+	TLBSRX_:       "tlbsrx.",
+	TLBRE:         "tlbre",
+	TLBWE:         "tlbwe",
+	DNH:           "dnh",
+	DCI:           "dci",
+	ICI:           "ici",
+	DCREAD:        "dcread",
+	ICREAD:        "icread",
+	MFPMR:         "mfpmr",
+	MTPMR:         "mtpmr",
+}
+
+var (
+	ap_Reg_11_15               = &argField{Type: TypeReg, Shift: 0, BitFields: BitFields{{11, 5}}}
+	ap_Reg_6_10                = &argField{Type: TypeReg, Shift: 0, BitFields: BitFields{{6, 5}}}
+	ap_PCRel_6_29_shift2       = &argField{Type: TypePCRel, Shift: 2, BitFields: BitFields{{6, 24}}}
+	ap_Label_6_29_shift2       = &argField{Type: TypeLabel, Shift: 2, BitFields: BitFields{{6, 24}}}
+	ap_ImmUnsigned_6_10        = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{6, 5}}}
+	ap_CondRegBit_11_15        = &argField{Type: TypeCondRegBit, Shift: 0, BitFields: BitFields{{11, 5}}}
+	ap_PCRel_16_29_shift2      = &argField{Type: TypePCRel, Shift: 2, BitFields: BitFields{{16, 14}}}
+	ap_Label_16_29_shift2      = &argField{Type: TypeLabel, Shift: 2, BitFields: BitFields{{16, 14}}}
+	ap_ImmUnsigned_19_20       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{19, 2}}}
+	ap_CondRegBit_6_10         = &argField{Type: TypeCondRegBit, Shift: 0, BitFields: BitFields{{6, 5}}}
+	ap_CondRegBit_16_20        = &argField{Type: TypeCondRegBit, Shift: 0, BitFields: BitFields{{16, 5}}}
+	ap_CondRegField_6_8        = &argField{Type: TypeCondRegField, Shift: 0, BitFields: BitFields{{6, 3}}}
+	ap_CondRegField_11_13      = &argField{Type: TypeCondRegField, Shift: 0, BitFields: BitFields{{11, 3}}}
+	ap_ImmUnsigned_20_26       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{20, 7}}}
+	ap_SpReg_11_20             = &argField{Type: TypeSpReg, Shift: 0, BitFields: BitFields{{11, 10}}}
+	ap_Offset_16_31            = &argField{Type: TypeOffset, Shift: 0, BitFields: BitFields{{16, 16}}}
+	ap_Reg_16_20               = &argField{Type: TypeReg, Shift: 0, BitFields: BitFields{{16, 5}}}
+	ap_Offset_16_29_shift2     = &argField{Type: TypeOffset, Shift: 2, BitFields: BitFields{{16, 14}}}
+	ap_Offset_16_27_shift4     = &argField{Type: TypeOffset, Shift: 4, BitFields: BitFields{{16, 12}}}
+	ap_ImmUnsigned_16_20       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{16, 5}}}
+	ap_ImmSigned_16_31         = &argField{Type: TypeImmSigned, Shift: 0, BitFields: BitFields{{16, 16}}}
+	ap_ImmUnsigned_16_31       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{16, 16}}}
+	ap_CondRegBit_21_25        = &argField{Type: TypeCondRegBit, Shift: 0, BitFields: BitFields{{21, 5}}}
+	ap_ImmUnsigned_21_25       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{21, 5}}}
+	ap_ImmUnsigned_26_30       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{26, 5}}}
+	ap_ImmUnsigned_30_30_16_20 = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{30, 1}, {16, 5}}}
+	ap_ImmUnsigned_26_26_21_25 = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{26, 1}, {21, 5}}}
+	ap_SpReg_16_20_11_15       = &argField{Type: TypeSpReg, Shift: 0, BitFields: BitFields{{16, 5}, {11, 5}}}
+	ap_ImmUnsigned_12_19       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{12, 8}}}
+	ap_ImmUnsigned_10_10       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{10, 1}}}
+	ap_VecSReg_31_31_6_10      = &argField{Type: TypeVecSReg, Shift: 0, BitFields: BitFields{{31, 1}, {6, 5}}}
+	ap_FPReg_6_10              = &argField{Type: TypeFPReg, Shift: 0, BitFields: BitFields{{6, 5}}}
+	ap_FPReg_16_20             = &argField{Type: TypeFPReg, Shift: 0, BitFields: BitFields{{16, 5}}}
+	ap_FPReg_11_15             = &argField{Type: TypeFPReg, Shift: 0, BitFields: BitFields{{11, 5}}}
+	ap_FPReg_21_25             = &argField{Type: TypeFPReg, Shift: 0, BitFields: BitFields{{21, 5}}}
+	ap_ImmUnsigned_16_19       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{16, 4}}}
+	ap_ImmUnsigned_15_15       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{15, 1}}}
+	ap_ImmUnsigned_7_14        = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{7, 8}}}
+	ap_ImmUnsigned_6_6         = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{6, 1}}}
+	ap_VecReg_6_10             = &argField{Type: TypeVecReg, Shift: 0, BitFields: BitFields{{6, 5}}}
+	ap_VecReg_11_15            = &argField{Type: TypeVecReg, Shift: 0, BitFields: BitFields{{11, 5}}}
+	ap_VecReg_16_20            = &argField{Type: TypeVecReg, Shift: 0, BitFields: BitFields{{16, 5}}}
+	ap_ImmUnsigned_12_15       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{12, 4}}}
+	ap_ImmUnsigned_13_15       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{13, 3}}}
+	ap_ImmUnsigned_14_15       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{14, 2}}}
+	ap_ImmSigned_11_15         = &argField{Type: TypeImmSigned, Shift: 0, BitFields: BitFields{{11, 5}}}
+	ap_VecReg_21_25            = &argField{Type: TypeVecReg, Shift: 0, BitFields: BitFields{{21, 5}}}
+	ap_ImmUnsigned_22_25       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{22, 4}}}
+	ap_ImmUnsigned_11_15       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{11, 5}}}
+	ap_ImmUnsigned_16_16       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{16, 1}}}
+	ap_ImmUnsigned_17_20       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{17, 4}}}
+	ap_ImmUnsigned_22_22       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{22, 1}}}
+	ap_ImmUnsigned_16_21       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{16, 6}}}
+	ap_ImmUnsigned_21_22       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{21, 2}}}
+	ap_ImmUnsigned_11_12       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{11, 2}}}
+	ap_ImmUnsigned_11_11       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{11, 1}}}
+	ap_VecSReg_30_30_16_20     = &argField{Type: TypeVecSReg, Shift: 0, BitFields: BitFields{{30, 1}, {16, 5}}}
+	ap_VecSReg_29_29_11_15     = &argField{Type: TypeVecSReg, Shift: 0, BitFields: BitFields{{29, 1}, {11, 5}}}
+	ap_ImmUnsigned_22_23       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{22, 2}}}
+	ap_VecSReg_28_28_21_25     = &argField{Type: TypeVecSReg, Shift: 0, BitFields: BitFields{{28, 1}, {21, 5}}}
+	ap_CondRegField_29_31      = &argField{Type: TypeCondRegField, Shift: 0, BitFields: BitFields{{29, 3}}}
+	ap_ImmUnsigned_7_10        = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{7, 4}}}
+	ap_ImmUnsigned_9_10        = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{9, 2}}}
+	ap_ImmUnsigned_31_31       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{31, 1}}}
+	ap_ImmSigned_16_20         = &argField{Type: TypeImmSigned, Shift: 0, BitFields: BitFields{{16, 5}}}
+	ap_ImmUnsigned_20_20       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{20, 1}}}
+	ap_ImmUnsigned_8_10        = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{8, 3}}}
+	ap_SpReg_12_15             = &argField{Type: TypeSpReg, Shift: 0, BitFields: BitFields{{12, 4}}}
+	ap_ImmUnsigned_6_20        = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{6, 15}}}
+	ap_ImmUnsigned_11_20       = &argField{Type: TypeImmUnsigned, Shift: 0, BitFields: BitFields{{11, 10}}}
+)
+
+var instFormats = [...]instFormat{
+	{CNTLZW, 0xfc0007ff, 0x7c000034, 0xf800, // Count Leading Zeros Word X-form (cntlzw RA, RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{CNTLZW_, 0xfc0007ff, 0x7c000035, 0xf800, // Count Leading Zeros Word X-form (cntlzw. RA, RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{B, 0xfc000003, 0x48000000, 0x0, // Branch I-form (b target_addr)
+		[5]*argField{ap_PCRel_6_29_shift2}},
+	{BA, 0xfc000003, 0x48000002, 0x0, // Branch I-form (ba target_addr)
+		[5]*argField{ap_Label_6_29_shift2}},
+	{BL, 0xfc000003, 0x48000001, 0x0, // Branch I-form (bl target_addr)
+		[5]*argField{ap_PCRel_6_29_shift2}},
+	{BLA, 0xfc000003, 0x48000003, 0x0, // Branch I-form (bla target_addr)
+		[5]*argField{ap_Label_6_29_shift2}},
+	{BC, 0xfc000003, 0x40000000, 0x0, // Branch Conditional B-form (bc BO,BI,target_addr)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_CondRegBit_11_15, ap_PCRel_16_29_shift2}},
+	{BCA, 0xfc000003, 0x40000002, 0x0, // Branch Conditional B-form (bca BO,BI,target_addr)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_CondRegBit_11_15, ap_Label_16_29_shift2}},
+	{BCL, 0xfc000003, 0x40000001, 0x0, // Branch Conditional B-form (bcl BO,BI,target_addr)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_CondRegBit_11_15, ap_PCRel_16_29_shift2}},
+	{BCLA, 0xfc000003, 0x40000003, 0x0, // Branch Conditional B-form (bcla BO,BI,target_addr)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_CondRegBit_11_15, ap_Label_16_29_shift2}},
+	{BCLR, 0xfc0007ff, 0x4c000020, 0xe000, // Branch Conditional to Link Register XL-form (bclr BO,BI,BH)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_CondRegBit_11_15, ap_ImmUnsigned_19_20}},
+	{BCLRL, 0xfc0007ff, 0x4c000021, 0xe000, // Branch Conditional to Link Register XL-form (bclrl BO,BI,BH)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_CondRegBit_11_15, ap_ImmUnsigned_19_20}},
+	{BCCTR, 0xfc0007ff, 0x4c000420, 0xe000, // Branch Conditional to Count Register XL-form (bcctr BO,BI,BH)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_CondRegBit_11_15, ap_ImmUnsigned_19_20}},
+	{BCCTRL, 0xfc0007ff, 0x4c000421, 0xe000, // Branch Conditional to Count Register XL-form (bcctrl BO,BI,BH)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_CondRegBit_11_15, ap_ImmUnsigned_19_20}},
+	{BCTAR, 0xfc0007ff, 0x4c000460, 0xe000, // Branch Conditional to Branch Target Address Register XL-form (bctar BO,BI,BH)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_CondRegBit_11_15, ap_ImmUnsigned_19_20}},
+	{BCTARL, 0xfc0007ff, 0x4c000461, 0xe000, // Branch Conditional to Branch Target Address Register XL-form (bctarl BO,BI,BH)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_CondRegBit_11_15, ap_ImmUnsigned_19_20}},
+	{CRAND, 0xfc0007fe, 0x4c000202, 0x1, // Condition Register AND XL-form (crand BT,BA,BB)
+		[5]*argField{ap_CondRegBit_6_10, ap_CondRegBit_11_15, ap_CondRegBit_16_20}},
+	{CROR, 0xfc0007fe, 0x4c000382, 0x1, // Condition Register OR XL-form (cror BT,BA,BB)
+		[5]*argField{ap_CondRegBit_6_10, ap_CondRegBit_11_15, ap_CondRegBit_16_20}},
+	{CRNAND, 0xfc0007fe, 0x4c0001c2, 0x1, // Condition Register NAND XL-form (crnand BT,BA,BB)
+		[5]*argField{ap_CondRegBit_6_10, ap_CondRegBit_11_15, ap_CondRegBit_16_20}},
+	{CRXOR, 0xfc0007fe, 0x4c000182, 0x1, // Condition Register XOR XL-form (crxor BT,BA,BB)
+		[5]*argField{ap_CondRegBit_6_10, ap_CondRegBit_11_15, ap_CondRegBit_16_20}},
+	{CRNOR, 0xfc0007fe, 0x4c000042, 0x1, // Condition Register NOR XL-form (crnor BT,BA,BB)
+		[5]*argField{ap_CondRegBit_6_10, ap_CondRegBit_11_15, ap_CondRegBit_16_20}},
+	{CRANDC, 0xfc0007fe, 0x4c000102, 0x1, // Condition Register AND with Complement XL-form (crandc BT,BA,BB)
+		[5]*argField{ap_CondRegBit_6_10, ap_CondRegBit_11_15, ap_CondRegBit_16_20}},
+	{MCRF, 0xfc0007fe, 0x4c000000, 0x63f801, // Move Condition Register Field XL-form (mcrf BF,BFA)
+		[5]*argField{ap_CondRegField_6_8, ap_CondRegField_11_13}},
+	{CREQV, 0xfc0007fe, 0x4c000242, 0x1, // Condition Register Equivalent XL-form (creqv BT,BA,BB)
+		[5]*argField{ap_CondRegBit_6_10, ap_CondRegBit_11_15, ap_CondRegBit_16_20}},
+	{CRORC, 0xfc0007fe, 0x4c000342, 0x1, // Condition Register OR with Complement XL-form (crorc BT,BA,BB)
+		[5]*argField{ap_CondRegBit_6_10, ap_CondRegBit_11_15, ap_CondRegBit_16_20}},
+	{SC, 0xfc000002, 0x44000002, 0x3fff01d, // System Call SC-form (sc LEV)
+		[5]*argField{ap_ImmUnsigned_20_26}},
+	{CLRBHRB, 0xfc0007fe, 0x7c00035c, 0x3fff801, // Clear BHRB X-form (clrbhrb)
+		[5]*argField{}},
+	{MFBHRBE, 0xfc0007fe, 0x7c00025c, 0x1, // Move From Branch History Rolling Buffer XFX-form (mfbhrbe RT,BHRBE)
+		[5]*argField{ap_Reg_6_10, ap_SpReg_11_20}},
+	{LBZ, 0xfc000000, 0x88000000, 0x0, // Load Byte and Zero D-form (lbz RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LBZU, 0xfc000000, 0x8c000000, 0x0, // Load Byte and Zero with Update D-form (lbzu RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LBZX, 0xfc0007fe, 0x7c0000ae, 0x1, // Load Byte and Zero Indexed X-form (lbzx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LBZUX, 0xfc0007fe, 0x7c0000ee, 0x1, // Load Byte and Zero with Update Indexed X-form (lbzux RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LHZ, 0xfc000000, 0xa0000000, 0x0, // Load Halfword and Zero D-form (lhz RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LHZU, 0xfc000000, 0xa4000000, 0x0, // Load Halfword and Zero with Update D-form (lhzu RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LHZX, 0xfc0007fe, 0x7c00022e, 0x1, // Load Halfword and Zero Indexed X-form (lhzx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LHZUX, 0xfc0007fe, 0x7c00026e, 0x1, // Load Halfword and Zero with Update Indexed X-form (lhzux RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LHA, 0xfc000000, 0xa8000000, 0x0, // Load Halfword Algebraic D-form (lha RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LHAU, 0xfc000000, 0xac000000, 0x0, // Load Halfword Algebraic with Update D-form (lhau RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LHAX, 0xfc0007fe, 0x7c0002ae, 0x1, // Load Halfword Algebraic Indexed X-form (lhax RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LHAUX, 0xfc0007fe, 0x7c0002ee, 0x1, // Load Halfword Algebraic with Update Indexed X-form (lhaux RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LWZ, 0xfc000000, 0x80000000, 0x0, // Load Word and Zero D-form (lwz RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LWZU, 0xfc000000, 0x84000000, 0x0, // Load Word and Zero with Update D-form (lwzu RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LWZX, 0xfc0007fe, 0x7c00002e, 0x1, // Load Word and Zero Indexed X-form (lwzx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LWZUX, 0xfc0007fe, 0x7c00006e, 0x1, // Load Word and Zero with Update Indexed X-form (lwzux RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LWA, 0xfc000003, 0xe8000002, 0x0, // Load Word Algebraic DS-form (lwa RT,DS(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_29_shift2, ap_Reg_11_15}},
+	{LWAX, 0xfc0007fe, 0x7c0002aa, 0x1, // Load Word Algebraic Indexed X-form (lwax RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LWAUX, 0xfc0007fe, 0x7c0002ea, 0x1, // Load Word Algebraic with Update Indexed X-form (lwaux RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LD, 0xfc000003, 0xe8000000, 0x0, // Load Doubleword DS-form (ld RT,DS(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_29_shift2, ap_Reg_11_15}},
+	{LDU, 0xfc000003, 0xe8000001, 0x0, // Load Doubleword with Update DS-form (ldu RT,DS(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_29_shift2, ap_Reg_11_15}},
+	{LDX, 0xfc0007fe, 0x7c00002a, 0x1, // Load Doubleword Indexed X-form (ldx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LDUX, 0xfc0007fe, 0x7c00006a, 0x1, // Load Doubleword with Update Indexed X-form (ldux RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STB, 0xfc000000, 0x98000000, 0x0, // Store Byte D-form (stb RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STBU, 0xfc000000, 0x9c000000, 0x0, // Store Byte with Update D-form (stbu RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STBX, 0xfc0007fe, 0x7c0001ae, 0x1, // Store Byte Indexed X-form (stbx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STBUX, 0xfc0007fe, 0x7c0001ee, 0x1, // Store Byte with Update Indexed X-form (stbux RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STH, 0xfc000000, 0xb0000000, 0x0, // Store Halfword D-form (sth RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STHU, 0xfc000000, 0xb4000000, 0x0, // Store Halfword with Update D-form (sthu RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STHX, 0xfc0007fe, 0x7c00032e, 0x1, // Store Halfword Indexed X-form (sthx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STHUX, 0xfc0007fe, 0x7c00036e, 0x1, // Store Halfword with Update Indexed X-form (sthux RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STW, 0xfc000000, 0x90000000, 0x0, // Store Word D-form (stw RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STWU, 0xfc000000, 0x94000000, 0x0, // Store Word with Update D-form (stwu RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STWX, 0xfc0007fe, 0x7c00012e, 0x1, // Store Word Indexed X-form (stwx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STWUX, 0xfc0007fe, 0x7c00016e, 0x1, // Store Word with Update Indexed X-form (stwux RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STD, 0xfc000003, 0xf8000000, 0x0, // Store Doubleword DS-form (std RS,DS(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_29_shift2, ap_Reg_11_15}},
+	{STDU, 0xfc000003, 0xf8000001, 0x0, // Store Doubleword with Update DS-form (stdu RS,DS(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_29_shift2, ap_Reg_11_15}},
+	{STDX, 0xfc0007fe, 0x7c00012a, 0x1, // Store Doubleword Indexed X-form (stdx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STDUX, 0xfc0007fe, 0x7c00016a, 0x1, // Store Doubleword with Update Indexed X-form (stdux RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LQ, 0xfc000000, 0xe0000000, 0xf, // Load Quadword DQ-form (lq RTp,DQ(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_27_shift4, ap_Reg_11_15}},
+	{STQ, 0xfc000003, 0xf8000002, 0x0, // Store Quadword DS-form (stq RSp,DS(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_29_shift2, ap_Reg_11_15}},
+	{LHBRX, 0xfc0007fe, 0x7c00062c, 0x1, // Load Halfword Byte-Reverse Indexed X-form (lhbrx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LWBRX, 0xfc0007fe, 0x7c00042c, 0x1, // Load Word Byte-Reverse Indexed X-form (lwbrx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STHBRX, 0xfc0007fe, 0x7c00072c, 0x1, // Store Halfword Byte-Reverse Indexed X-form (sthbrx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STWBRX, 0xfc0007fe, 0x7c00052c, 0x1, // Store Word Byte-Reverse Indexed X-form (stwbrx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LDBRX, 0xfc0007fe, 0x7c000428, 0x1, // Load Doubleword Byte-Reverse Indexed X-form (ldbrx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STDBRX, 0xfc0007fe, 0x7c000528, 0x1, // Store Doubleword Byte-Reverse Indexed X-form (stdbrx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LMW, 0xfc000000, 0xb8000000, 0x0, // Load Multiple Word D-form (lmw RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STMW, 0xfc000000, 0xbc000000, 0x0, // Store Multiple Word D-form (stmw RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LSWI, 0xfc0007fe, 0x7c0004aa, 0x1, // Load String Word Immediate X-form (lswi RT,RA,NB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmUnsigned_16_20}},
+	{LSWX, 0xfc0007fe, 0x7c00042a, 0x1, // Load String Word Indexed X-form (lswx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STSWI, 0xfc0007fe, 0x7c0005aa, 0x1, // Store String Word Immediate X-form (stswi RS,RA,NB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmUnsigned_16_20}},
+	{STSWX, 0xfc0007fe, 0x7c00052a, 0x1, // Store String Word Indexed X-form (stswx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LI, 0xfc1f0000, 0x38000000, 0x0, // Add Immediate D-form (li RT,SI)
+		[5]*argField{ap_Reg_6_10, ap_ImmSigned_16_31}},
+	{ADDI, 0xfc000000, 0x38000000, 0x0, // Add Immediate D-form (addi RT,RA,SI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
+	{LIS, 0xfc1f0000, 0x3c000000, 0x0, // Add Immediate Shifted D-form (lis RT, SI)
+		[5]*argField{ap_Reg_6_10, ap_ImmSigned_16_31}},
+	{ADDIS, 0xfc000000, 0x3c000000, 0x0, // Add Immediate Shifted D-form (addis RT,RA,SI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
+	{ADD, 0xfc0007ff, 0x7c000214, 0x0, // Add XO-form (add RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADD_, 0xfc0007ff, 0x7c000215, 0x0, // Add XO-form (add. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDO, 0xfc0007ff, 0x7c000614, 0x0, // Add XO-form (addo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDO_, 0xfc0007ff, 0x7c000615, 0x0, // Add XO-form (addo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDIC, 0xfc000000, 0x30000000, 0x0, // Add Immediate Carrying D-form (addic RT,RA,SI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
+	{SUBF, 0xfc0007ff, 0x7c000050, 0x0, // Subtract From XO-form (subf RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBF_, 0xfc0007ff, 0x7c000051, 0x0, // Subtract From XO-form (subf. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBFO, 0xfc0007ff, 0x7c000450, 0x0, // Subtract From XO-form (subfo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBFO_, 0xfc0007ff, 0x7c000451, 0x0, // Subtract From XO-form (subfo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDIC_, 0xfc000000, 0x34000000, 0x0, // Add Immediate Carrying and Record D-form (addic. RT,RA,SI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
+	{SUBFIC, 0xfc000000, 0x20000000, 0x0, // Subtract From Immediate Carrying D-form (subfic RT,RA,SI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
+	{ADDC, 0xfc0007ff, 0x7c000014, 0x0, // Add Carrying XO-form (addc RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDC_, 0xfc0007ff, 0x7c000015, 0x0, // Add Carrying XO-form (addc. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDCO, 0xfc0007ff, 0x7c000414, 0x0, // Add Carrying XO-form (addco RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDCO_, 0xfc0007ff, 0x7c000415, 0x0, // Add Carrying XO-form (addco. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBFC, 0xfc0007ff, 0x7c000010, 0x0, // Subtract From Carrying XO-form (subfc RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBFC_, 0xfc0007ff, 0x7c000011, 0x0, // Subtract From Carrying XO-form (subfc. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBFCO, 0xfc0007ff, 0x7c000410, 0x0, // Subtract From Carrying XO-form (subfco RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBFCO_, 0xfc0007ff, 0x7c000411, 0x0, // Subtract From Carrying XO-form (subfco. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDE, 0xfc0007ff, 0x7c000114, 0x0, // Add Extended XO-form (adde RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDE_, 0xfc0007ff, 0x7c000115, 0x0, // Add Extended XO-form (adde. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDEO, 0xfc0007ff, 0x7c000514, 0x0, // Add Extended XO-form (addeo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDEO_, 0xfc0007ff, 0x7c000515, 0x0, // Add Extended XO-form (addeo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ADDME, 0xfc0007ff, 0x7c0001d4, 0xf800, // Add to Minus One Extended XO-form (addme RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{ADDME_, 0xfc0007ff, 0x7c0001d5, 0xf800, // Add to Minus One Extended XO-form (addme. RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{ADDMEO, 0xfc0007ff, 0x7c0005d4, 0xf800, // Add to Minus One Extended XO-form (addmeo RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{ADDMEO_, 0xfc0007ff, 0x7c0005d5, 0xf800, // Add to Minus One Extended XO-form (addmeo. RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{SUBFE, 0xfc0007ff, 0x7c000110, 0x0, // Subtract From Extended XO-form (subfe RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBFE_, 0xfc0007ff, 0x7c000111, 0x0, // Subtract From Extended XO-form (subfe. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBFEO, 0xfc0007ff, 0x7c000510, 0x0, // Subtract From Extended XO-form (subfeo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBFEO_, 0xfc0007ff, 0x7c000511, 0x0, // Subtract From Extended XO-form (subfeo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SUBFME, 0xfc0007ff, 0x7c0001d0, 0xf800, // Subtract From Minus One Extended XO-form (subfme RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{SUBFME_, 0xfc0007ff, 0x7c0001d1, 0xf800, // Subtract From Minus One Extended XO-form (subfme. RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{SUBFMEO, 0xfc0007ff, 0x7c0005d0, 0xf800, // Subtract From Minus One Extended XO-form (subfmeo RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{SUBFMEO_, 0xfc0007ff, 0x7c0005d1, 0xf800, // Subtract From Minus One Extended XO-form (subfmeo. RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{ADDZE, 0xfc0007ff, 0x7c000194, 0xf800, // Add to Zero Extended XO-form (addze RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{ADDZE_, 0xfc0007ff, 0x7c000195, 0xf800, // Add to Zero Extended XO-form (addze. RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{ADDZEO, 0xfc0007ff, 0x7c000594, 0xf800, // Add to Zero Extended XO-form (addzeo RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{ADDZEO_, 0xfc0007ff, 0x7c000595, 0xf800, // Add to Zero Extended XO-form (addzeo. RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{SUBFZE, 0xfc0007ff, 0x7c000190, 0xf800, // Subtract From Zero Extended XO-form (subfze RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{SUBFZE_, 0xfc0007ff, 0x7c000191, 0xf800, // Subtract From Zero Extended XO-form (subfze. RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{SUBFZEO, 0xfc0007ff, 0x7c000590, 0xf800, // Subtract From Zero Extended XO-form (subfzeo RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{SUBFZEO_, 0xfc0007ff, 0x7c000591, 0xf800, // Subtract From Zero Extended XO-form (subfzeo. RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{NEG, 0xfc0007ff, 0x7c0000d0, 0xf800, // Negate XO-form (neg RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{NEG_, 0xfc0007ff, 0x7c0000d1, 0xf800, // Negate XO-form (neg. RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{NEGO, 0xfc0007ff, 0x7c0004d0, 0xf800, // Negate XO-form (nego RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{NEGO_, 0xfc0007ff, 0x7c0004d1, 0xf800, // Negate XO-form (nego. RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{MULLI, 0xfc000000, 0x1c000000, 0x0, // Multiply Low Immediate D-form (mulli RT,RA,SI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
+	{MULLW, 0xfc0007ff, 0x7c0001d6, 0x0, // Multiply Low Word XO-form (mullw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLW_, 0xfc0007ff, 0x7c0001d7, 0x0, // Multiply Low Word XO-form (mullw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLWO, 0xfc0007ff, 0x7c0005d6, 0x0, // Multiply Low Word XO-form (mullwo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLWO_, 0xfc0007ff, 0x7c0005d7, 0x0, // Multiply Low Word XO-form (mullwo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHW, 0xfc0003ff, 0x7c000096, 0x400, // Multiply High Word XO-form (mulhw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHW_, 0xfc0003ff, 0x7c000097, 0x400, // Multiply High Word XO-form (mulhw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHWU, 0xfc0003ff, 0x7c000016, 0x400, // Multiply High Word Unsigned XO-form (mulhwu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHWU_, 0xfc0003ff, 0x7c000017, 0x400, // Multiply High Word Unsigned XO-form (mulhwu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVW, 0xfc0007ff, 0x7c0003d6, 0x0, // Divide Word XO-form (divw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVW_, 0xfc0007ff, 0x7c0003d7, 0x0, // Divide Word XO-form (divw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWO, 0xfc0007ff, 0x7c0007d6, 0x0, // Divide Word XO-form (divwo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWO_, 0xfc0007ff, 0x7c0007d7, 0x0, // Divide Word XO-form (divwo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWU, 0xfc0007ff, 0x7c000396, 0x0, // Divide Word Unsigned XO-form (divwu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWU_, 0xfc0007ff, 0x7c000397, 0x0, // Divide Word Unsigned XO-form (divwu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWUO, 0xfc0007ff, 0x7c000796, 0x0, // Divide Word Unsigned XO-form (divwuo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWUO_, 0xfc0007ff, 0x7c000797, 0x0, // Divide Word Unsigned XO-form (divwuo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWE, 0xfc0007ff, 0x7c000356, 0x0, // Divide Word Extended XO-form (divwe RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWE_, 0xfc0007ff, 0x7c000357, 0x0, // Divide Word Extended XO-form (divwe. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWEO, 0xfc0007ff, 0x7c000756, 0x0, // Divide Word Extended XO-form (divweo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWEO_, 0xfc0007ff, 0x7c000757, 0x0, // Divide Word Extended XO-form (divweo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWEU, 0xfc0007ff, 0x7c000316, 0x0, // Divide Word Extended Unsigned XO-form (divweu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWEU_, 0xfc0007ff, 0x7c000317, 0x0, // Divide Word Extended Unsigned XO-form (divweu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWEUO, 0xfc0007ff, 0x7c000716, 0x0, // Divide Word Extended Unsigned XO-form (divweuo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVWEUO_, 0xfc0007ff, 0x7c000717, 0x0, // Divide Word Extended Unsigned XO-form (divweuo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLD, 0xfc0007ff, 0x7c0001d2, 0x0, // Multiply Low Doubleword XO-form (mulld RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLD_, 0xfc0007ff, 0x7c0001d3, 0x0, // Multiply Low Doubleword XO-form (mulld. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLDO, 0xfc0007ff, 0x7c0005d2, 0x0, // Multiply Low Doubleword XO-form (mulldo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLDO_, 0xfc0007ff, 0x7c0005d3, 0x0, // Multiply Low Doubleword XO-form (mulldo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHDU, 0xfc0003ff, 0x7c000012, 0x400, // Multiply High Doubleword Unsigned XO-form (mulhdu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHDU_, 0xfc0003ff, 0x7c000013, 0x400, // Multiply High Doubleword Unsigned XO-form (mulhdu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHD, 0xfc0003ff, 0x7c000092, 0x400, // Multiply High Doubleword XO-form (mulhd RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHD_, 0xfc0003ff, 0x7c000093, 0x400, // Multiply High Doubleword XO-form (mulhd. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVD, 0xfc0007ff, 0x7c0003d2, 0x0, // Divide Doubleword XO-form (divd RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVD_, 0xfc0007ff, 0x7c0003d3, 0x0, // Divide Doubleword XO-form (divd. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDO, 0xfc0007ff, 0x7c0007d2, 0x0, // Divide Doubleword XO-form (divdo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDO_, 0xfc0007ff, 0x7c0007d3, 0x0, // Divide Doubleword XO-form (divdo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDU, 0xfc0007ff, 0x7c000392, 0x0, // Divide Doubleword Unsigned XO-form (divdu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDU_, 0xfc0007ff, 0x7c000393, 0x0, // Divide Doubleword Unsigned XO-form (divdu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDUO, 0xfc0007ff, 0x7c000792, 0x0, // Divide Doubleword Unsigned XO-form (divduo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDUO_, 0xfc0007ff, 0x7c000793, 0x0, // Divide Doubleword Unsigned XO-form (divduo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDE, 0xfc0007ff, 0x7c000352, 0x0, // Divide Doubleword Extended XO-form (divde RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDE_, 0xfc0007ff, 0x7c000353, 0x0, // Divide Doubleword Extended XO-form (divde. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDEO, 0xfc0007ff, 0x7c000752, 0x0, // Divide Doubleword Extended XO-form (divdeo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDEO_, 0xfc0007ff, 0x7c000753, 0x0, // Divide Doubleword Extended XO-form (divdeo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDEU, 0xfc0007ff, 0x7c000312, 0x0, // Divide Doubleword Extended Unsigned XO-form (divdeu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDEU_, 0xfc0007ff, 0x7c000313, 0x0, // Divide Doubleword Extended Unsigned XO-form (divdeu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDEUO, 0xfc0007ff, 0x7c000712, 0x0, // Divide Doubleword Extended Unsigned XO-form (divdeuo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DIVDEUO_, 0xfc0007ff, 0x7c000713, 0x0, // Divide Doubleword Extended Unsigned XO-form (divdeuo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{CMPWI, 0xfc200000, 0x2c000000, 0x400000, // Compare Immediate D-form (cmpwi BF,RA,SI)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_ImmSigned_16_31}},
+	{CMPDI, 0xfc200000, 0x2c200000, 0x400000, // Compare Immediate D-form (cmpdi BF,RA,SI)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_ImmSigned_16_31}},
+	{CMPW, 0xfc2007fe, 0x7c000000, 0x400001, // Compare X-form (cmpw BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{CMPD, 0xfc2007fe, 0x7c200000, 0x400001, // Compare X-form (cmpd BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{CMPLWI, 0xfc200000, 0x28000000, 0x400000, // Compare Logical Immediate D-form (cmplwi BF,RA,UI)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_ImmUnsigned_16_31}},
+	{CMPLDI, 0xfc200000, 0x28200000, 0x400000, // Compare Logical Immediate D-form (cmpldi BF,RA,UI)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_ImmUnsigned_16_31}},
+	{CMPLW, 0xfc2007fe, 0x7c000040, 0x400001, // Compare Logical X-form (cmplw BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{CMPLD, 0xfc2007fe, 0x7c200040, 0x400001, // Compare Logical X-form (cmpld BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{TWI, 0xfc000000, 0xc000000, 0x0, // Trap Word Immediate D-form (twi TO,RA,SI)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
+	{TW, 0xfc0007fe, 0x7c000008, 0x1, // Trap Word X-form (tw TO,RA,RB)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{TDI, 0xfc000000, 0x8000000, 0x0, // Trap Doubleword Immediate D-form (tdi TO,RA,SI)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_ImmSigned_16_31}},
+	{ISEL, 0xfc00003e, 0x7c00001e, 0x1, // Integer Select A-form (isel RT,RA,RB,BC)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20, ap_CondRegBit_21_25}},
+	{TD, 0xfc0007fe, 0x7c000088, 0x1, // Trap Doubleword X-form (td TO,RA,RB)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ANDI_, 0xfc000000, 0x70000000, 0x0, // AND Immediate D-form (andi. RA,RS,UI)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_31}},
+	{ANDIS_, 0xfc000000, 0x74000000, 0x0, // AND Immediate Shifted D-form (andis. RA,RS,UI)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_31}},
+	{ORI, 0xfc000000, 0x60000000, 0x0, // OR Immediate D-form (ori RA,RS,UI)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_31}},
+	{ORIS, 0xfc000000, 0x64000000, 0x0, // OR Immediate Shifted D-form (oris RA,RS,UI)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_31}},
+	{XORI, 0xfc000000, 0x68000000, 0x0, // XOR Immediate D-form (xori RA,RS,UI)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_31}},
+	{XORIS, 0xfc000000, 0x6c000000, 0x0, // XOR Immediate Shifted D-form (xoris RA,RS,UI)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_31}},
+	{AND, 0xfc0007ff, 0x7c000038, 0x0, // AND X-form (and RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{AND_, 0xfc0007ff, 0x7c000039, 0x0, // AND X-form (and. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{XOR, 0xfc0007ff, 0x7c000278, 0x0, // XOR X-form (xor RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{XOR_, 0xfc0007ff, 0x7c000279, 0x0, // XOR X-form (xor. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{NAND, 0xfc0007ff, 0x7c0003b8, 0x0, // NAND X-form (nand RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{NAND_, 0xfc0007ff, 0x7c0003b9, 0x0, // NAND X-form (nand. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{OR, 0xfc0007ff, 0x7c000378, 0x0, // OR X-form (or RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{OR_, 0xfc0007ff, 0x7c000379, 0x0, // OR X-form (or. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{NOR, 0xfc0007ff, 0x7c0000f8, 0x0, // NOR X-form (nor RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{NOR_, 0xfc0007ff, 0x7c0000f9, 0x0, // NOR X-form (nor. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{ANDC, 0xfc0007ff, 0x7c000078, 0x0, // AND with Complement X-form (andc RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{ANDC_, 0xfc0007ff, 0x7c000079, 0x0, // AND with Complement X-form (andc. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{EXTSB, 0xfc0007ff, 0x7c000774, 0xf800, // Extend Sign Byte X-form (extsb RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{EXTSB_, 0xfc0007ff, 0x7c000775, 0xf800, // Extend Sign Byte X-form (extsb. RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{EQV, 0xfc0007ff, 0x7c000238, 0x0, // Equivalent X-form (eqv RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{EQV_, 0xfc0007ff, 0x7c000239, 0x0, // Equivalent X-form (eqv. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{ORC, 0xfc0007ff, 0x7c000338, 0x0, // OR with Complement X-form (orc RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{ORC_, 0xfc0007ff, 0x7c000339, 0x0, // OR with Complement X-form (orc. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{EXTSH, 0xfc0007ff, 0x7c000734, 0xf800, // Extend Sign Halfword X-form (extsh RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{EXTSH_, 0xfc0007ff, 0x7c000735, 0xf800, // Extend Sign Halfword X-form (extsh. RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{CMPB, 0xfc0007fe, 0x7c0003f8, 0x1, // Compare Bytes X-form (cmpb RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{POPCNTB, 0xfc0007fe, 0x7c0000f4, 0xf801, // Population Count Bytes X-form (popcntb RA, RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{POPCNTW, 0xfc0007fe, 0x7c0002f4, 0xf801, // Population Count Words X-form (popcntw RA, RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{PRTYD, 0xfc0007fe, 0x7c000174, 0xf801, // Parity Doubleword X-form (prtyd RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{PRTYW, 0xfc0007fe, 0x7c000134, 0xf801, // Parity Word X-form (prtyw RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{EXTSW, 0xfc0007ff, 0x7c0007b4, 0xf800, // Extend Sign Word X-form (extsw RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{EXTSW_, 0xfc0007ff, 0x7c0007b5, 0xf800, // Extend Sign Word X-form (extsw. RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{CNTLZD, 0xfc0007ff, 0x7c000074, 0xf800, // Count Leading Zeros Doubleword X-form (cntlzd RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{CNTLZD_, 0xfc0007ff, 0x7c000075, 0xf800, // Count Leading Zeros Doubleword X-form (cntlzd. RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{POPCNTD, 0xfc0007fe, 0x7c0003f4, 0xf801, // Population Count Doubleword X-form (popcntd RA, RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{BPERMD, 0xfc0007fe, 0x7c0001f8, 0x1, // Bit Permute Doubleword X-form (bpermd RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{RLWINM, 0xfc000001, 0x54000000, 0x0, // Rotate Left Word Immediate then AND with Mask M-form (rlwinm RA,RS,SH,MB,ME)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_ImmUnsigned_21_25, ap_ImmUnsigned_26_30}},
+	{RLWINM_, 0xfc000001, 0x54000001, 0x0, // Rotate Left Word Immediate then AND with Mask M-form (rlwinm. RA,RS,SH,MB,ME)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_ImmUnsigned_21_25, ap_ImmUnsigned_26_30}},
+	{RLWNM, 0xfc000001, 0x5c000000, 0x0, // Rotate Left Word then AND with Mask M-form (rlwnm RA,RS,RB,MB,ME)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20, ap_ImmUnsigned_21_25, ap_ImmUnsigned_26_30}},
+	{RLWNM_, 0xfc000001, 0x5c000001, 0x0, // Rotate Left Word then AND with Mask M-form (rlwnm. RA,RS,RB,MB,ME)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20, ap_ImmUnsigned_21_25, ap_ImmUnsigned_26_30}},
+	{RLWIMI, 0xfc000001, 0x50000000, 0x0, // Rotate Left Word Immediate then Mask Insert M-form (rlwimi RA,RS,SH,MB,ME)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_ImmUnsigned_21_25, ap_ImmUnsigned_26_30}},
+	{RLWIMI_, 0xfc000001, 0x50000001, 0x0, // Rotate Left Word Immediate then Mask Insert M-form (rlwimi. RA,RS,SH,MB,ME)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_ImmUnsigned_21_25, ap_ImmUnsigned_26_30}},
+	{RLDICL, 0xfc00001d, 0x78000000, 0x0, // Rotate Left Doubleword Immediate then Clear Left MD-form (rldicl RA,RS,SH,MB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_30_30_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDICL_, 0xfc00001d, 0x78000001, 0x0, // Rotate Left Doubleword Immediate then Clear Left MD-form (rldicl. RA,RS,SH,MB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_30_30_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDICR, 0xfc00001d, 0x78000004, 0x0, // Rotate Left Doubleword Immediate then Clear Right MD-form (rldicr RA,RS,SH,ME)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_30_30_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDICR_, 0xfc00001d, 0x78000005, 0x0, // Rotate Left Doubleword Immediate then Clear Right MD-form (rldicr. RA,RS,SH,ME)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_30_30_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDIC, 0xfc00001d, 0x78000008, 0x0, // Rotate Left Doubleword Immediate then Clear MD-form (rldic RA,RS,SH,MB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_30_30_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDIC_, 0xfc00001d, 0x78000009, 0x0, // Rotate Left Doubleword Immediate then Clear MD-form (rldic. RA,RS,SH,MB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_30_30_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDCL, 0xfc00001f, 0x78000010, 0x0, // Rotate Left Doubleword then Clear Left MDS-form (rldcl RA,RS,RB,MB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDCL_, 0xfc00001f, 0x78000011, 0x0, // Rotate Left Doubleword then Clear Left MDS-form (rldcl. RA,RS,RB,MB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDCR, 0xfc00001f, 0x78000012, 0x0, // Rotate Left Doubleword then Clear Right MDS-form (rldcr RA,RS,RB,ME)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDCR_, 0xfc00001f, 0x78000013, 0x0, // Rotate Left Doubleword then Clear Right MDS-form (rldcr. RA,RS,RB,ME)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDIMI, 0xfc00001d, 0x7800000c, 0x0, // Rotate Left Doubleword Immediate then Mask Insert MD-form (rldimi RA,RS,SH,MB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_30_30_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{RLDIMI_, 0xfc00001d, 0x7800000d, 0x0, // Rotate Left Doubleword Immediate then Mask Insert MD-form (rldimi. RA,RS,SH,MB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_30_30_16_20, ap_ImmUnsigned_26_26_21_25}},
+	{SLW, 0xfc0007ff, 0x7c000030, 0x0, // Shift Left Word X-form (slw RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SLW_, 0xfc0007ff, 0x7c000031, 0x0, // Shift Left Word X-form (slw. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SRW, 0xfc0007ff, 0x7c000430, 0x0, // Shift Right Word X-form (srw RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SRW_, 0xfc0007ff, 0x7c000431, 0x0, // Shift Right Word X-form (srw. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SRAWI, 0xfc0007ff, 0x7c000670, 0x0, // Shift Right Algebraic Word Immediate X-form (srawi RA,RS,SH)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_20}},
+	{SRAWI_, 0xfc0007ff, 0x7c000671, 0x0, // Shift Right Algebraic Word Immediate X-form (srawi. RA,RS,SH)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_16_20}},
+	{SRAW, 0xfc0007ff, 0x7c000630, 0x0, // Shift Right Algebraic Word X-form (sraw RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SRAW_, 0xfc0007ff, 0x7c000631, 0x0, // Shift Right Algebraic Word X-form (sraw. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SLD, 0xfc0007ff, 0x7c000036, 0x0, // Shift Left Doubleword X-form (sld RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SLD_, 0xfc0007ff, 0x7c000037, 0x0, // Shift Left Doubleword X-form (sld. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SRD, 0xfc0007ff, 0x7c000436, 0x0, // Shift Right Doubleword X-form (srd RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SRD_, 0xfc0007ff, 0x7c000437, 0x0, // Shift Right Doubleword X-form (srd. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SRADI, 0xfc0007fd, 0x7c000674, 0x0, // Shift Right Algebraic Doubleword Immediate XS-form (sradi RA,RS,SH)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_30_30_16_20}},
+	{SRADI_, 0xfc0007fd, 0x7c000675, 0x0, // Shift Right Algebraic Doubleword Immediate XS-form (sradi. RA,RS,SH)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_ImmUnsigned_30_30_16_20}},
+	{SRAD, 0xfc0007ff, 0x7c000634, 0x0, // Shift Right Algebraic Doubleword X-form (srad RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{SRAD_, 0xfc0007ff, 0x7c000635, 0x0, // Shift Right Algebraic Doubleword X-form (srad. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{CDTBCD, 0xfc0007fe, 0x7c000234, 0xf801, // Convert Declets To Binary Coded Decimal X-form (cdtbcd RA, RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{CBCDTD, 0xfc0007fe, 0x7c000274, 0xf801, // Convert Binary Coded Decimal To Declets X-form (cbcdtd RA, RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{ADDG6S, 0xfc0003fe, 0x7c000094, 0x401, // Add and Generate Sixes XO-form (addg6s RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MTSPR, 0xfc0007fe, 0x7c0003a6, 0x1, // Move To Special Purpose Register XFX-form (mtspr SPR,RS)
+		[5]*argField{ap_SpReg_16_20_11_15, ap_Reg_6_10}},
+	{MFSPR, 0xfc0007fe, 0x7c0002a6, 0x1, // Move From Special Purpose Register XFX-form (mfspr RT,SPR)
+		[5]*argField{ap_Reg_6_10, ap_SpReg_16_20_11_15}},
+	{MTCRF, 0xfc1007fe, 0x7c000120, 0x801, // Move To Condition Register Fields XFX-form (mtcrf FXM,RS)
+		[5]*argField{ap_ImmUnsigned_12_19, ap_Reg_6_10}},
+	{MFCR, 0xfc1007fe, 0x7c000026, 0xff801, // Move From Condition Register XFX-form (mfcr RT)
+		[5]*argField{ap_Reg_6_10}},
+	{MTSLE, 0xfc0007fe, 0x7c000126, 0x3dff801, // Move To Split Little Endian X-form (mtsle L)
+		[5]*argField{ap_ImmUnsigned_10_10}},
+	{MFVSRD, 0xfc0007fe, 0x7c000066, 0xf800, // Move From VSR Doubleword XX1-form (mfvsrd RA,XS)
+		[5]*argField{ap_Reg_11_15, ap_VecSReg_31_31_6_10}},
+	{MFVSRWZ, 0xfc0007fe, 0x7c0000e6, 0xf800, // Move From VSR Word and Zero XX1-form (mfvsrwz RA,XS)
+		[5]*argField{ap_Reg_11_15, ap_VecSReg_31_31_6_10}},
+	{MTVSRD, 0xfc0007fe, 0x7c000166, 0xf800, // Move To VSR Doubleword XX1-form (mtvsrd XT,RA)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15}},
+	{MTVSRWA, 0xfc0007fe, 0x7c0001a6, 0xf800, // Move To VSR Word Algebraic XX1-form (mtvsrwa XT,RA)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15}},
+	{MTVSRWZ, 0xfc0007fe, 0x7c0001e6, 0xf800, // Move To VSR Word and Zero XX1-form (mtvsrwz XT,RA)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15}},
+	{MTOCRF, 0xfc1007fe, 0x7c100120, 0x801, // Move To One Condition Register Field XFX-form (mtocrf FXM,RS)
+		[5]*argField{ap_ImmUnsigned_12_19, ap_Reg_6_10}},
+	{MFOCRF, 0xfc1007fe, 0x7c100026, 0x801, // Move From One Condition Register Field XFX-form (mfocrf RT,FXM)
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_12_19}},
+	{MCRXR, 0xfc0007fe, 0x7c000400, 0x7ff801, // Move to Condition Register from XER X-form (mcrxr BF)
+		[5]*argField{ap_CondRegField_6_8}},
+	{MTDCRUX, 0xfc0007fe, 0x7c000346, 0xf801, // Move To Device Control Register User-mode Indexed X-form (mtdcrux RS,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{MFDCRUX, 0xfc0007fe, 0x7c000246, 0xf801, // Move From Device Control Register User-mode Indexed X-form (mfdcrux RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{LFS, 0xfc000000, 0xc0000000, 0x0, // Load Floating-Point Single D-form (lfs FRT,D(RA))
+		[5]*argField{ap_FPReg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LFSU, 0xfc000000, 0xc4000000, 0x0, // Load Floating-Point Single with Update D-form (lfsu FRT,D(RA))
+		[5]*argField{ap_FPReg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LFSX, 0xfc0007fe, 0x7c00042e, 0x1, // Load Floating-Point Single Indexed X-form (lfsx FRT,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LFSUX, 0xfc0007fe, 0x7c00046e, 0x1, // Load Floating-Point Single with Update Indexed X-form (lfsux FRT,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LFD, 0xfc000000, 0xc8000000, 0x0, // Load Floating-Point Double D-form (lfd FRT,D(RA))
+		[5]*argField{ap_FPReg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LFDU, 0xfc000000, 0xcc000000, 0x0, // Load Floating-Point Double with Update D-form (lfdu FRT,D(RA))
+		[5]*argField{ap_FPReg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{LFDX, 0xfc0007fe, 0x7c0004ae, 0x1, // Load Floating-Point Double Indexed X-form (lfdx FRT,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LFDUX, 0xfc0007fe, 0x7c0004ee, 0x1, // Load Floating-Point Double with Update Indexed X-form (lfdux FRT,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LFIWAX, 0xfc0007fe, 0x7c0006ae, 0x1, // Load Floating-Point as Integer Word Algebraic Indexed X-form (lfiwax FRT,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LFIWZX, 0xfc0007fe, 0x7c0006ee, 0x1, // Load Floating-Point as Integer Word and Zero Indexed X-form (lfiwzx FRT,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STFS, 0xfc000000, 0xd0000000, 0x0, // Store Floating-Point Single D-form (stfs FRS,D(RA))
+		[5]*argField{ap_FPReg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STFSU, 0xfc000000, 0xd4000000, 0x0, // Store Floating-Point Single with Update D-form (stfsu FRS,D(RA))
+		[5]*argField{ap_FPReg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STFSX, 0xfc0007fe, 0x7c00052e, 0x1, // Store Floating-Point Single Indexed X-form (stfsx FRS,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STFSUX, 0xfc0007fe, 0x7c00056e, 0x1, // Store Floating-Point Single with Update Indexed X-form (stfsux FRS,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STFD, 0xfc000000, 0xd8000000, 0x0, // Store Floating-Point Double D-form (stfd FRS,D(RA))
+		[5]*argField{ap_FPReg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STFDU, 0xfc000000, 0xdc000000, 0x0, // Store Floating-Point Double with Update D-form (stfdu FRS,D(RA))
+		[5]*argField{ap_FPReg_6_10, ap_Offset_16_31, ap_Reg_11_15}},
+	{STFDX, 0xfc0007fe, 0x7c0005ae, 0x1, // Store Floating-Point Double Indexed X-form (stfdx FRS,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STFDUX, 0xfc0007fe, 0x7c0005ee, 0x1, // Store Floating-Point Double with Update Indexed X-form (stfdux FRS,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STFIWX, 0xfc0007fe, 0x7c0007ae, 0x1, // Store Floating-Point as Integer Word Indexed X-form (stfiwx FRS,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LFDP, 0xfc000003, 0xe4000000, 0x0, // Load Floating-Point Double Pair DS-form (lfdp FRTp,DS(RA))
+		[5]*argField{ap_FPReg_6_10, ap_Offset_16_29_shift2, ap_Reg_11_15}},
+	{LFDPX, 0xfc0007fe, 0x7c00062e, 0x1, // Load Floating-Point Double Pair Indexed X-form (lfdpx FRTp,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STFDP, 0xfc000003, 0xf4000000, 0x0, // Store Floating-Point Double Pair DS-form (stfdp FRSp,DS(RA))
+		[5]*argField{ap_FPReg_6_10, ap_Offset_16_29_shift2, ap_Reg_11_15}},
+	{STFDPX, 0xfc0007fe, 0x7c00072e, 0x1, // Store Floating-Point Double Pair Indexed X-form (stfdpx FRSp,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{FMR, 0xfc0007ff, 0xfc000090, 0x1f0000, // Floating Move Register X-form (fmr FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FMR_, 0xfc0007ff, 0xfc000091, 0x1f0000, // Floating Move Register X-form (fmr. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FABS, 0xfc0007ff, 0xfc000210, 0x1f0000, // Floating Absolute Value X-form (fabs FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FABS_, 0xfc0007ff, 0xfc000211, 0x1f0000, // Floating Absolute Value X-form (fabs. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FNABS, 0xfc0007ff, 0xfc000110, 0x1f0000, // Floating Negative Absolute Value X-form (fnabs FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FNABS_, 0xfc0007ff, 0xfc000111, 0x1f0000, // Floating Negative Absolute Value X-form (fnabs. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FNEG, 0xfc0007ff, 0xfc000050, 0x1f0000, // Floating Negate X-form (fneg FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FNEG_, 0xfc0007ff, 0xfc000051, 0x1f0000, // Floating Negate X-form (fneg. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCPSGN, 0xfc0007ff, 0xfc000010, 0x0, // Floating Copy Sign X-form (fcpsgn FRT, FRA, FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FCPSGN_, 0xfc0007ff, 0xfc000011, 0x0, // Floating Copy Sign X-form (fcpsgn. FRT, FRA, FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FMRGEW, 0xfc0007fe, 0xfc00078c, 0x1, // Floating Merge Even Word X-form (fmrgew FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FMRGOW, 0xfc0007fe, 0xfc00068c, 0x1, // Floating Merge Odd Word X-form (fmrgow FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FADD, 0xfc00003f, 0xfc00002a, 0x7c0, // Floating Add [Single] A-form (fadd FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FADD_, 0xfc00003f, 0xfc00002b, 0x7c0, // Floating Add [Single] A-form (fadd. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FADDS, 0xfc00003f, 0xec00002a, 0x7c0, // Floating Add [Single] A-form (fadds FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FADDS_, 0xfc00003f, 0xec00002b, 0x7c0, // Floating Add [Single] A-form (fadds. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FSUB, 0xfc00003f, 0xfc000028, 0x7c0, // Floating Subtract [Single] A-form (fsub FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FSUB_, 0xfc00003f, 0xfc000029, 0x7c0, // Floating Subtract [Single] A-form (fsub. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FSUBS, 0xfc00003f, 0xec000028, 0x7c0, // Floating Subtract [Single] A-form (fsubs FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FSUBS_, 0xfc00003f, 0xec000029, 0x7c0, // Floating Subtract [Single] A-form (fsubs. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FMUL, 0xfc00003f, 0xfc000032, 0xf800, // Floating Multiply [Single] A-form (fmul FRT,FRA,FRC)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25}},
+	{FMUL_, 0xfc00003f, 0xfc000033, 0xf800, // Floating Multiply [Single] A-form (fmul. FRT,FRA,FRC)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25}},
+	{FMULS, 0xfc00003f, 0xec000032, 0xf800, // Floating Multiply [Single] A-form (fmuls FRT,FRA,FRC)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25}},
+	{FMULS_, 0xfc00003f, 0xec000033, 0xf800, // Floating Multiply [Single] A-form (fmuls. FRT,FRA,FRC)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25}},
+	{FDIV, 0xfc00003f, 0xfc000024, 0x7c0, // Floating Divide [Single] A-form (fdiv FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FDIV_, 0xfc00003f, 0xfc000025, 0x7c0, // Floating Divide [Single] A-form (fdiv. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FDIVS, 0xfc00003f, 0xec000024, 0x7c0, // Floating Divide [Single] A-form (fdivs FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FDIVS_, 0xfc00003f, 0xec000025, 0x7c0, // Floating Divide [Single] A-form (fdivs. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FSQRT, 0xfc00003f, 0xfc00002c, 0x1f07c0, // Floating Square Root [Single] A-form (fsqrt FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FSQRT_, 0xfc00003f, 0xfc00002d, 0x1f07c0, // Floating Square Root [Single] A-form (fsqrt. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FSQRTS, 0xfc00003f, 0xec00002c, 0x1f07c0, // Floating Square Root [Single] A-form (fsqrts FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FSQRTS_, 0xfc00003f, 0xec00002d, 0x1f07c0, // Floating Square Root [Single] A-form (fsqrts. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRE, 0xfc00003f, 0xfc000030, 0x1f07c0, // Floating Reciprocal Estimate [Single] A-form (fre FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRE_, 0xfc00003f, 0xfc000031, 0x1f07c0, // Floating Reciprocal Estimate [Single] A-form (fre. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRES, 0xfc00003f, 0xec000030, 0x1f07c0, // Floating Reciprocal Estimate [Single] A-form (fres FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRES_, 0xfc00003f, 0xec000031, 0x1f07c0, // Floating Reciprocal Estimate [Single] A-form (fres. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRSQRTE, 0xfc00003f, 0xfc000034, 0x1f07c0, // Floating Reciprocal Square Root Estimate [Single] A-form (frsqrte FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRSQRTE_, 0xfc00003f, 0xfc000035, 0x1f07c0, // Floating Reciprocal Square Root Estimate [Single] A-form (frsqrte. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRSQRTES, 0xfc00003f, 0xec000034, 0x1f07c0, // Floating Reciprocal Square Root Estimate [Single] A-form (frsqrtes FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRSQRTES_, 0xfc00003f, 0xec000035, 0x1f07c0, // Floating Reciprocal Square Root Estimate [Single] A-form (frsqrtes. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FTDIV, 0xfc0007fe, 0xfc000100, 0x600001, // Floating Test for software Divide X-form (ftdiv BF,FRA,FRB)
+		[5]*argField{ap_CondRegField_6_8, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FTSQRT, 0xfc0007fe, 0xfc000140, 0x7f0001, // Floating Test for software Square Root X-form (ftsqrt BF,FRB)
+		[5]*argField{ap_CondRegField_6_8, ap_FPReg_16_20}},
+	{FMADD, 0xfc00003f, 0xfc00003a, 0x0, // Floating Multiply-Add [Single] A-form (fmadd FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FMADD_, 0xfc00003f, 0xfc00003b, 0x0, // Floating Multiply-Add [Single] A-form (fmadd. FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FMADDS, 0xfc00003f, 0xec00003a, 0x0, // Floating Multiply-Add [Single] A-form (fmadds FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FMADDS_, 0xfc00003f, 0xec00003b, 0x0, // Floating Multiply-Add [Single] A-form (fmadds. FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FMSUB, 0xfc00003f, 0xfc000038, 0x0, // Floating Multiply-Subtract [Single] A-form (fmsub FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FMSUB_, 0xfc00003f, 0xfc000039, 0x0, // Floating Multiply-Subtract [Single] A-form (fmsub. FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FMSUBS, 0xfc00003f, 0xec000038, 0x0, // Floating Multiply-Subtract [Single] A-form (fmsubs FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FMSUBS_, 0xfc00003f, 0xec000039, 0x0, // Floating Multiply-Subtract [Single] A-form (fmsubs. FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FNMADD, 0xfc00003f, 0xfc00003e, 0x0, // Floating Negative Multiply-Add [Single] A-form (fnmadd FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FNMADD_, 0xfc00003f, 0xfc00003f, 0x0, // Floating Negative Multiply-Add [Single] A-form (fnmadd. FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FNMADDS, 0xfc00003f, 0xec00003e, 0x0, // Floating Negative Multiply-Add [Single] A-form (fnmadds FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FNMADDS_, 0xfc00003f, 0xec00003f, 0x0, // Floating Negative Multiply-Add [Single] A-form (fnmadds. FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FNMSUB, 0xfc00003f, 0xfc00003c, 0x0, // Floating Negative Multiply-Subtract [Single] A-form (fnmsub FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FNMSUB_, 0xfc00003f, 0xfc00003d, 0x0, // Floating Negative Multiply-Subtract [Single] A-form (fnmsub. FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FNMSUBS, 0xfc00003f, 0xec00003c, 0x0, // Floating Negative Multiply-Subtract [Single] A-form (fnmsubs FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FNMSUBS_, 0xfc00003f, 0xec00003d, 0x0, // Floating Negative Multiply-Subtract [Single] A-form (fnmsubs. FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FRSP, 0xfc0007ff, 0xfc000018, 0x1f0000, // Floating Round to Single-Precision X-form (frsp FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRSP_, 0xfc0007ff, 0xfc000019, 0x1f0000, // Floating Round to Single-Precision X-form (frsp. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTID, 0xfc0007ff, 0xfc00065c, 0x1f0000, // Floating Convert To Integer Doubleword X-form (fctid FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTID_, 0xfc0007ff, 0xfc00065d, 0x1f0000, // Floating Convert To Integer Doubleword X-form (fctid. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIDZ, 0xfc0007ff, 0xfc00065e, 0x1f0000, // Floating Convert To Integer Doubleword with round toward Zero X-form (fctidz FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIDZ_, 0xfc0007ff, 0xfc00065f, 0x1f0000, // Floating Convert To Integer Doubleword with round toward Zero X-form (fctidz. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIDU, 0xfc0007ff, 0xfc00075c, 0x1f0000, // Floating Convert To Integer Doubleword Unsigned X-form (fctidu FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIDU_, 0xfc0007ff, 0xfc00075d, 0x1f0000, // Floating Convert To Integer Doubleword Unsigned X-form (fctidu. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIDUZ, 0xfc0007ff, 0xfc00075e, 0x1f0000, // Floating Convert To Integer Doubleword Unsigned with round toward Zero X-form (fctiduz FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIDUZ_, 0xfc0007ff, 0xfc00075f, 0x1f0000, // Floating Convert To Integer Doubleword Unsigned with round toward Zero X-form (fctiduz. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIW, 0xfc0007ff, 0xfc00001c, 0x1f0000, // Floating Convert To Integer Word X-form (fctiw FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIW_, 0xfc0007ff, 0xfc00001d, 0x1f0000, // Floating Convert To Integer Word X-form (fctiw. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIWZ, 0xfc0007ff, 0xfc00001e, 0x1f0000, // Floating Convert To Integer Word with round toward Zero X-form (fctiwz FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIWZ_, 0xfc0007ff, 0xfc00001f, 0x1f0000, // Floating Convert To Integer Word with round toward Zero X-form (fctiwz. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIWU, 0xfc0007ff, 0xfc00011c, 0x1f0000, // Floating Convert To Integer Word Unsigned X-form (fctiwu FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIWU_, 0xfc0007ff, 0xfc00011d, 0x1f0000, // Floating Convert To Integer Word Unsigned X-form (fctiwu. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIWUZ, 0xfc0007ff, 0xfc00011e, 0x1f0000, // Floating Convert To Integer Word Unsigned with round toward Zero X-form (fctiwuz FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCTIWUZ_, 0xfc0007ff, 0xfc00011f, 0x1f0000, // Floating Convert To Integer Word Unsigned with round toward Zero X-form (fctiwuz. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCFID, 0xfc0007ff, 0xfc00069c, 0x1f0000, // Floating Convert From Integer Doubleword X-form (fcfid FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCFID_, 0xfc0007ff, 0xfc00069d, 0x1f0000, // Floating Convert From Integer Doubleword X-form (fcfid. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCFIDU, 0xfc0007ff, 0xfc00079c, 0x1f0000, // Floating Convert From Integer Doubleword Unsigned X-form (fcfidu FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCFIDU_, 0xfc0007ff, 0xfc00079d, 0x1f0000, // Floating Convert From Integer Doubleword Unsigned X-form (fcfidu. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCFIDS, 0xfc0007ff, 0xec00069c, 0x1f0000, // Floating Convert From Integer Doubleword Single X-form (fcfids FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCFIDS_, 0xfc0007ff, 0xec00069d, 0x1f0000, // Floating Convert From Integer Doubleword Single X-form (fcfids. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCFIDUS, 0xfc0007ff, 0xec00079c, 0x1f0000, // Floating Convert From Integer Doubleword Unsigned Single X-form (fcfidus FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCFIDUS_, 0xfc0007ff, 0xec00079d, 0x1f0000, // Floating Convert From Integer Doubleword Unsigned Single X-form (fcfidus. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRIN, 0xfc0007ff, 0xfc000310, 0x1f0000, // Floating Round to Integer Nearest X-form (frin FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRIN_, 0xfc0007ff, 0xfc000311, 0x1f0000, // Floating Round to Integer Nearest X-form (frin. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRIZ, 0xfc0007ff, 0xfc000350, 0x1f0000, // Floating Round to Integer Toward Zero X-form (friz FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRIZ_, 0xfc0007ff, 0xfc000351, 0x1f0000, // Floating Round to Integer Toward Zero X-form (friz. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRIP, 0xfc0007ff, 0xfc000390, 0x1f0000, // Floating Round to Integer Plus X-form (frip FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRIP_, 0xfc0007ff, 0xfc000391, 0x1f0000, // Floating Round to Integer Plus X-form (frip. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRIM, 0xfc0007ff, 0xfc0003d0, 0x1f0000, // Floating Round to Integer Minus X-form (frim FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FRIM_, 0xfc0007ff, 0xfc0003d1, 0x1f0000, // Floating Round to Integer Minus X-form (frim. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{FCMPU, 0xfc0007fe, 0xfc000000, 0x600001, // Floating Compare Unordered X-form (fcmpu BF,FRA,FRB)
+		[5]*argField{ap_CondRegField_6_8, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FCMPO, 0xfc0007fe, 0xfc000040, 0x600001, // Floating Compare Ordered X-form (fcmpo BF,FRA,FRB)
+		[5]*argField{ap_CondRegField_6_8, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{FSEL, 0xfc00003f, 0xfc00002e, 0x0, // Floating Select A-form (fsel FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{FSEL_, 0xfc00003f, 0xfc00002f, 0x0, // Floating Select A-form (fsel. FRT,FRA,FRC,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_21_25, ap_FPReg_16_20}},
+	{MFFS, 0xfc0007ff, 0xfc00048e, 0x1ff800, // Move From FPSCR X-form (mffs FRT)
+		[5]*argField{ap_FPReg_6_10}},
+	{MFFS_, 0xfc0007ff, 0xfc00048f, 0x1ff800, // Move From FPSCR X-form (mffs. FRT)
+		[5]*argField{ap_FPReg_6_10}},
+	{MCRFS, 0xfc0007fe, 0xfc000080, 0x63f801, // Move to Condition Register from FPSCR X-form (mcrfs BF,BFA)
+		[5]*argField{ap_CondRegField_6_8, ap_CondRegField_11_13}},
+	{MTFSFI, 0xfc0007ff, 0xfc00010c, 0x7e0800, // Move To FPSCR Field Immediate X-form (mtfsfi BF,U,W)
+		[5]*argField{ap_CondRegField_6_8, ap_ImmUnsigned_16_19, ap_ImmUnsigned_15_15}},
+	{MTFSFI_, 0xfc0007ff, 0xfc00010d, 0x7e0800, // Move To FPSCR Field Immediate X-form (mtfsfi. BF,U,W)
+		[5]*argField{ap_CondRegField_6_8, ap_ImmUnsigned_16_19, ap_ImmUnsigned_15_15}},
+	{MTFSF, 0xfc0007ff, 0xfc00058e, 0x0, // Move To FPSCR Fields XFL-form (mtfsf FLM,FRB,L,W)
+		[5]*argField{ap_ImmUnsigned_7_14, ap_FPReg_16_20, ap_ImmUnsigned_6_6, ap_ImmUnsigned_15_15}},
+	{MTFSF_, 0xfc0007ff, 0xfc00058f, 0x0, // Move To FPSCR Fields XFL-form (mtfsf. FLM,FRB,L,W)
+		[5]*argField{ap_ImmUnsigned_7_14, ap_FPReg_16_20, ap_ImmUnsigned_6_6, ap_ImmUnsigned_15_15}},
+	{MTFSB0, 0xfc0007ff, 0xfc00008c, 0x1ff800, // Move To FPSCR Bit 0 X-form (mtfsb0 BT)
+		[5]*argField{ap_CondRegBit_6_10}},
+	{MTFSB0_, 0xfc0007ff, 0xfc00008d, 0x1ff800, // Move To FPSCR Bit 0 X-form (mtfsb0. BT)
+		[5]*argField{ap_CondRegBit_6_10}},
+	{MTFSB1, 0xfc0007ff, 0xfc00004c, 0x1ff800, // Move To FPSCR Bit 1 X-form (mtfsb1 BT)
+		[5]*argField{ap_CondRegBit_6_10}},
+	{MTFSB1_, 0xfc0007ff, 0xfc00004d, 0x1ff800, // Move To FPSCR Bit 1 X-form (mtfsb1. BT)
+		[5]*argField{ap_CondRegBit_6_10}},
+	{LVEBX, 0xfc0007fe, 0x7c00000e, 0x1, // Load Vector Element Byte Indexed X-form (lvebx VRT,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LVEHX, 0xfc0007fe, 0x7c00004e, 0x1, // Load Vector Element Halfword Indexed X-form (lvehx VRT,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LVEWX, 0xfc0007fe, 0x7c00008e, 0x1, // Load Vector Element Word Indexed X-form (lvewx VRT,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LVX, 0xfc0007fe, 0x7c0000ce, 0x1, // Load Vector Indexed X-form (lvx VRT,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LVXL, 0xfc0007fe, 0x7c0002ce, 0x1, // Load Vector Indexed LRU X-form (lvxl VRT,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STVEBX, 0xfc0007fe, 0x7c00010e, 0x1, // Store Vector Element Byte Indexed X-form (stvebx VRS,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STVEHX, 0xfc0007fe, 0x7c00014e, 0x1, // Store Vector Element Halfword Indexed X-form (stvehx VRS,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STVEWX, 0xfc0007fe, 0x7c00018e, 0x1, // Store Vector Element Word Indexed X-form (stvewx VRS,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STVX, 0xfc0007fe, 0x7c0001ce, 0x1, // Store Vector Indexed X-form (stvx VRS,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STVXL, 0xfc0007fe, 0x7c0003ce, 0x1, // Store Vector Indexed LRU X-form (stvxl VRS,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LVSL, 0xfc0007fe, 0x7c00000c, 0x1, // Load Vector for Shift Left Indexed X-form (lvsl VRT,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LVSR, 0xfc0007fe, 0x7c00004c, 0x1, // Load Vector for Shift Right Indexed X-form (lvsr VRT,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{VPKPX, 0xfc0007ff, 0x1000030e, 0x0, // Vector Pack Pixel VX-form (vpkpx VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKSDSS, 0xfc0007ff, 0x100005ce, 0x0, // Vector Pack Signed Doubleword Signed Saturate VX-form (vpksdss VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKSDUS, 0xfc0007ff, 0x1000054e, 0x0, // Vector Pack Signed Doubleword Unsigned Saturate VX-form (vpksdus VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKSHSS, 0xfc0007ff, 0x1000018e, 0x0, // Vector Pack Signed Halfword Signed Saturate VX-form (vpkshss VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKSHUS, 0xfc0007ff, 0x1000010e, 0x0, // Vector Pack Signed Halfword Unsigned Saturate VX-form (vpkshus VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKSWSS, 0xfc0007ff, 0x100001ce, 0x0, // Vector Pack Signed Word Signed Saturate VX-form (vpkswss VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKSWUS, 0xfc0007ff, 0x1000014e, 0x0, // Vector Pack Signed Word Unsigned Saturate VX-form (vpkswus VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKUDUM, 0xfc0007ff, 0x1000044e, 0x0, // Vector Pack Unsigned Doubleword Unsigned Modulo VX-form (vpkudum VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKUDUS, 0xfc0007ff, 0x100004ce, 0x0, // Vector Pack Unsigned Doubleword Unsigned Saturate VX-form (vpkudus VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKUHUM, 0xfc0007ff, 0x1000000e, 0x0, // Vector Pack Unsigned Halfword Unsigned Modulo VX-form (vpkuhum VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKUHUS, 0xfc0007ff, 0x1000008e, 0x0, // Vector Pack Unsigned Halfword Unsigned Saturate VX-form (vpkuhus VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKUWUM, 0xfc0007ff, 0x1000004e, 0x0, // Vector Pack Unsigned Word Unsigned Modulo VX-form (vpkuwum VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPKUWUS, 0xfc0007ff, 0x100000ce, 0x0, // Vector Pack Unsigned Word Unsigned Saturate VX-form (vpkuwus VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VUPKHPX, 0xfc0007ff, 0x1000034e, 0x1f0000, // Vector Unpack High Pixel VX-form (vupkhpx VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VUPKLPX, 0xfc0007ff, 0x100003ce, 0x1f0000, // Vector Unpack Low Pixel VX-form (vupklpx VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VUPKHSB, 0xfc0007ff, 0x1000020e, 0x1f0000, // Vector Unpack High Signed Byte VX-form (vupkhsb VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VUPKHSH, 0xfc0007ff, 0x1000024e, 0x1f0000, // Vector Unpack High Signed Halfword VX-form (vupkhsh VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VUPKHSW, 0xfc0007ff, 0x1000064e, 0x1f0000, // Vector Unpack High Signed Word VX-form (vupkhsw VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VUPKLSB, 0xfc0007ff, 0x1000028e, 0x1f0000, // Vector Unpack Low Signed Byte VX-form (vupklsb VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VUPKLSH, 0xfc0007ff, 0x100002ce, 0x1f0000, // Vector Unpack Low Signed Halfword VX-form (vupklsh VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VUPKLSW, 0xfc0007ff, 0x100006ce, 0x1f0000, // Vector Unpack Low Signed Word VX-form (vupklsw VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VMRGHB, 0xfc0007ff, 0x1000000c, 0x0, // Vector Merge High Byte VX-form (vmrghb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMRGHH, 0xfc0007ff, 0x1000004c, 0x0, // Vector Merge High Halfword VX-form (vmrghh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMRGLB, 0xfc0007ff, 0x1000010c, 0x0, // Vector Merge Low Byte VX-form (vmrglb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMRGLH, 0xfc0007ff, 0x1000014c, 0x0, // Vector Merge Low Halfword VX-form (vmrglh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMRGHW, 0xfc0007ff, 0x1000008c, 0x0, // Vector Merge High Word VX-form (vmrghw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMRGLW, 0xfc0007ff, 0x1000018c, 0x0, // Vector Merge Low Word VX-form (vmrglw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMRGEW, 0xfc0007ff, 0x1000078c, 0x0, // Vector Merge Even Word VX-form (vmrgew VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMRGOW, 0xfc0007ff, 0x1000068c, 0x0, // Vector Merge Odd Word VX-form (vmrgow VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSPLTB, 0xfc0007ff, 0x1000020c, 0x100000, // Vector Splat Byte VX-form (vspltb VRT,VRB,UIM)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20, ap_ImmUnsigned_12_15}},
+	{VSPLTH, 0xfc0007ff, 0x1000024c, 0x180000, // Vector Splat Halfword VX-form (vsplth VRT,VRB,UIM)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20, ap_ImmUnsigned_13_15}},
+	{VSPLTW, 0xfc0007ff, 0x1000028c, 0x1c0000, // Vector Splat Word VX-form (vspltw VRT,VRB,UIM)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20, ap_ImmUnsigned_14_15}},
+	{VSPLTISB, 0xfc0007ff, 0x1000030c, 0xf800, // Vector Splat Immediate Signed Byte VX-form (vspltisb VRT,SIM)
+		[5]*argField{ap_VecReg_6_10, ap_ImmSigned_11_15}},
+	{VSPLTISH, 0xfc0007ff, 0x1000034c, 0xf800, // Vector Splat Immediate Signed Halfword VX-form (vspltish VRT,SIM)
+		[5]*argField{ap_VecReg_6_10, ap_ImmSigned_11_15}},
+	{VSPLTISW, 0xfc0007ff, 0x1000038c, 0xf800, // Vector Splat Immediate Signed Word VX-form (vspltisw VRT,SIM)
+		[5]*argField{ap_VecReg_6_10, ap_ImmSigned_11_15}},
+	{VPERM, 0xfc00003f, 0x1000002b, 0x0, // Vector Permute VA-form (vperm VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VSEL, 0xfc00003f, 0x1000002a, 0x0, // Vector Select VA-form (vsel VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VSL, 0xfc0007ff, 0x100001c4, 0x0, // Vector Shift Left VX-form (vsl VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSLDOI, 0xfc00003f, 0x1000002c, 0x400, // Vector Shift Left Double by Octet Immediate VA-form (vsldoi VRT,VRA,VRB,SHB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_ImmUnsigned_22_25}},
+	{VSLO, 0xfc0007ff, 0x1000040c, 0x0, // Vector Shift Left by Octet VX-form (vslo VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSR, 0xfc0007ff, 0x100002c4, 0x0, // Vector Shift Right VX-form (vsr VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSRO, 0xfc0007ff, 0x1000044c, 0x0, // Vector Shift Right by Octet VX-form (vsro VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDCUW, 0xfc0007ff, 0x10000180, 0x0, // Vector Add and Write Carry-Out Unsigned Word VX-form (vaddcuw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDSBS, 0xfc0007ff, 0x10000300, 0x0, // Vector Add Signed Byte Saturate VX-form (vaddsbs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDSHS, 0xfc0007ff, 0x10000340, 0x0, // Vector Add Signed Halfword Saturate VX-form (vaddshs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDSWS, 0xfc0007ff, 0x10000380, 0x0, // Vector Add Signed Word Saturate VX-form (vaddsws VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDUBM, 0xfc0007ff, 0x10000000, 0x0, // Vector Add Unsigned Byte Modulo VX-form (vaddubm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDUDM, 0xfc0007ff, 0x100000c0, 0x0, // Vector Add Unsigned Doubleword Modulo VX-form (vaddudm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDUHM, 0xfc0007ff, 0x10000040, 0x0, // Vector Add Unsigned Halfword Modulo VX-form (vadduhm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDUWM, 0xfc0007ff, 0x10000080, 0x0, // Vector Add Unsigned Word Modulo VX-form (vadduwm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDUBS, 0xfc0007ff, 0x10000200, 0x0, // Vector Add Unsigned Byte Saturate VX-form (vaddubs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDUHS, 0xfc0007ff, 0x10000240, 0x0, // Vector Add Unsigned Halfword Saturate VX-form (vadduhs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDUWS, 0xfc0007ff, 0x10000280, 0x0, // Vector Add Unsigned Word Saturate VX-form (vadduws VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDUQM, 0xfc0007ff, 0x10000100, 0x0, // Vector Add Unsigned Quadword Modulo VX-form (vadduqm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDEUQM, 0xfc00003f, 0x1000003c, 0x0, // Vector Add Extended Unsigned Quadword Modulo VA-form (vaddeuqm VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VADDCUQ, 0xfc0007ff, 0x10000140, 0x0, // Vector Add & write Carry Unsigned Quadword VX-form (vaddcuq VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDECUQ, 0xfc00003f, 0x1000003d, 0x0, // Vector Add Extended & write Carry Unsigned Quadword VA-form (vaddecuq VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VSUBCUW, 0xfc0007ff, 0x10000580, 0x0, // Vector Subtract and Write Carry-Out Unsigned Word VX-form (vsubcuw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBSBS, 0xfc0007ff, 0x10000700, 0x0, // Vector Subtract Signed Byte Saturate VX-form (vsubsbs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBSHS, 0xfc0007ff, 0x10000740, 0x0, // Vector Subtract Signed Halfword Saturate VX-form (vsubshs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBSWS, 0xfc0007ff, 0x10000780, 0x0, // Vector Subtract Signed Word Saturate VX-form (vsubsws VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBUBM, 0xfc0007ff, 0x10000400, 0x0, // Vector Subtract Unsigned Byte Modulo VX-form (vsububm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBUDM, 0xfc0007ff, 0x100004c0, 0x0, // Vector Subtract Unsigned Doubleword Modulo VX-form (vsubudm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBUHM, 0xfc0007ff, 0x10000440, 0x0, // Vector Subtract Unsigned Halfword Modulo VX-form (vsubuhm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBUWM, 0xfc0007ff, 0x10000480, 0x0, // Vector Subtract Unsigned Word Modulo VX-form (vsubuwm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBUBS, 0xfc0007ff, 0x10000600, 0x0, // Vector Subtract Unsigned Byte Saturate VX-form (vsububs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBUHS, 0xfc0007ff, 0x10000640, 0x0, // Vector Subtract Unsigned Halfword Saturate VX-form (vsubuhs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBUWS, 0xfc0007ff, 0x10000680, 0x0, // Vector Subtract Unsigned Word Saturate VX-form (vsubuws VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBUQM, 0xfc0007ff, 0x10000500, 0x0, // Vector Subtract Unsigned Quadword Modulo VX-form (vsubuqm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBEUQM, 0xfc00003f, 0x1000003e, 0x0, // Vector Subtract Extended Unsigned Quadword Modulo VA-form (vsubeuqm VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VSUBCUQ, 0xfc0007ff, 0x10000540, 0x0, // Vector Subtract & write Carry Unsigned Quadword VX-form (vsubcuq VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBECUQ, 0xfc00003f, 0x1000003f, 0x0, // Vector Subtract Extended & write Carry Unsigned Quadword VA-form (vsubecuq VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VMULESB, 0xfc0007ff, 0x10000308, 0x0, // Vector Multiply Even Signed Byte VX-form (vmulesb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULEUB, 0xfc0007ff, 0x10000208, 0x0, // Vector Multiply Even Unsigned Byte VX-form (vmuleub VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULOSB, 0xfc0007ff, 0x10000108, 0x0, // Vector Multiply Odd Signed Byte VX-form (vmulosb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULOUB, 0xfc0007ff, 0x10000008, 0x0, // Vector Multiply Odd Unsigned Byte VX-form (vmuloub VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULESH, 0xfc0007ff, 0x10000348, 0x0, // Vector Multiply Even Signed Halfword VX-form (vmulesh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULEUH, 0xfc0007ff, 0x10000248, 0x0, // Vector Multiply Even Unsigned Halfword VX-form (vmuleuh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULOSH, 0xfc0007ff, 0x10000148, 0x0, // Vector Multiply Odd Signed Halfword VX-form (vmulosh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULOUH, 0xfc0007ff, 0x10000048, 0x0, // Vector Multiply Odd Unsigned Halfword VX-form (vmulouh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULESW, 0xfc0007ff, 0x10000388, 0x0, // Vector Multiply Even Signed Word VX-form (vmulesw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULEUW, 0xfc0007ff, 0x10000288, 0x0, // Vector Multiply Even Unsigned Word VX-form (vmuleuw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULOSW, 0xfc0007ff, 0x10000188, 0x0, // Vector Multiply Odd Signed Word VX-form (vmulosw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULOUW, 0xfc0007ff, 0x10000088, 0x0, // Vector Multiply Odd Unsigned Word VX-form (vmulouw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMULUWM, 0xfc0007ff, 0x10000089, 0x0, // Vector Multiply Unsigned Word Modulo VX-form (vmuluwm VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMHADDSHS, 0xfc00003f, 0x10000020, 0x0, // Vector Multiply-High-Add Signed Halfword Saturate VA-form (vmhaddshs VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VMHRADDSHS, 0xfc00003f, 0x10000021, 0x0, // Vector Multiply-High-Round-Add Signed Halfword Saturate VA-form (vmhraddshs VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VMLADDUHM, 0xfc00003f, 0x10000022, 0x0, // Vector Multiply-Low-Add Unsigned Halfword Modulo VA-form (vmladduhm VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VMSUMUBM, 0xfc00003f, 0x10000024, 0x0, // Vector Multiply-Sum Unsigned Byte Modulo VA-form (vmsumubm VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VMSUMMBM, 0xfc00003f, 0x10000025, 0x0, // Vector Multiply-Sum Mixed Byte Modulo VA-form (vmsummbm VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VMSUMSHM, 0xfc00003f, 0x10000028, 0x0, // Vector Multiply-Sum Signed Halfword Modulo VA-form (vmsumshm VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VMSUMSHS, 0xfc00003f, 0x10000029, 0x0, // Vector Multiply-Sum Signed Halfword Saturate VA-form (vmsumshs VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VMSUMUHM, 0xfc00003f, 0x10000026, 0x0, // Vector Multiply-Sum Unsigned Halfword Modulo VA-form (vmsumuhm VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VMSUMUHS, 0xfc00003f, 0x10000027, 0x0, // Vector Multiply-Sum Unsigned Halfword Saturate VA-form (vmsumuhs VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VSUMSWS, 0xfc0007ff, 0x10000788, 0x0, // Vector Sum across Signed Word Saturate VX-form (vsumsws VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUM2SWS, 0xfc0007ff, 0x10000688, 0x0, // Vector Sum across Half Signed Word Saturate VX-form (vsum2sws VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUM4SBS, 0xfc0007ff, 0x10000708, 0x0, // Vector Sum across Quarter Signed Byte Saturate VX-form (vsum4sbs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUM4SHS, 0xfc0007ff, 0x10000648, 0x0, // Vector Sum across Quarter Signed Halfword Saturate VX-form (vsum4shs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUM4UBS, 0xfc0007ff, 0x10000608, 0x0, // Vector Sum across Quarter Unsigned Byte Saturate VX-form (vsum4ubs VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VAVGSB, 0xfc0007ff, 0x10000502, 0x0, // Vector Average Signed Byte VX-form (vavgsb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VAVGSH, 0xfc0007ff, 0x10000542, 0x0, // Vector Average Signed Halfword VX-form (vavgsh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VAVGSW, 0xfc0007ff, 0x10000582, 0x0, // Vector Average Signed Word VX-form (vavgsw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VAVGUB, 0xfc0007ff, 0x10000402, 0x0, // Vector Average Unsigned Byte VX-form (vavgub VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VAVGUW, 0xfc0007ff, 0x10000482, 0x0, // Vector Average Unsigned Word VX-form (vavguw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VAVGUH, 0xfc0007ff, 0x10000442, 0x0, // Vector Average Unsigned Halfword VX-form (vavguh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMAXSB, 0xfc0007ff, 0x10000102, 0x0, // Vector Maximum Signed Byte VX-form (vmaxsb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMAXSD, 0xfc0007ff, 0x100001c2, 0x0, // Vector Maximum Signed Doubleword VX-form (vmaxsd VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMAXUB, 0xfc0007ff, 0x10000002, 0x0, // Vector Maximum Unsigned Byte VX-form (vmaxub VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMAXUD, 0xfc0007ff, 0x100000c2, 0x0, // Vector Maximum Unsigned Doubleword VX-form (vmaxud VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMAXSH, 0xfc0007ff, 0x10000142, 0x0, // Vector Maximum Signed Halfword VX-form (vmaxsh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMAXSW, 0xfc0007ff, 0x10000182, 0x0, // Vector Maximum Signed Word VX-form (vmaxsw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMAXUH, 0xfc0007ff, 0x10000042, 0x0, // Vector Maximum Unsigned Halfword VX-form (vmaxuh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMAXUW, 0xfc0007ff, 0x10000082, 0x0, // Vector Maximum Unsigned Word VX-form (vmaxuw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMINSB, 0xfc0007ff, 0x10000302, 0x0, // Vector Minimum Signed Byte VX-form (vminsb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMINSD, 0xfc0007ff, 0x100003c2, 0x0, // Vector Minimum Signed Doubleword VX-form (vminsd VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMINUB, 0xfc0007ff, 0x10000202, 0x0, // Vector Minimum Unsigned Byte VX-form (vminub VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMINUD, 0xfc0007ff, 0x100002c2, 0x0, // Vector Minimum Unsigned Doubleword VX-form (vminud VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMINSH, 0xfc0007ff, 0x10000342, 0x0, // Vector Minimum Signed Halfword VX-form (vminsh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMINSW, 0xfc0007ff, 0x10000382, 0x0, // Vector Minimum Signed Word VX-form (vminsw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMINUH, 0xfc0007ff, 0x10000242, 0x0, // Vector Minimum Unsigned Halfword VX-form (vminuh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMINUW, 0xfc0007ff, 0x10000282, 0x0, // Vector Minimum Unsigned Word VX-form (vminuw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPEQUB, 0xfc0007ff, 0x10000006, 0x0, // Vector Compare Equal To Unsigned Byte VC-form (vcmpequb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPEQUB_, 0xfc0007ff, 0x10000406, 0x0, // Vector Compare Equal To Unsigned Byte VC-form (vcmpequb. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPEQUH, 0xfc0007ff, 0x10000046, 0x0, // Vector Compare Equal To Unsigned Halfword VC-form (vcmpequh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPEQUH_, 0xfc0007ff, 0x10000446, 0x0, // Vector Compare Equal To Unsigned Halfword VC-form (vcmpequh. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPEQUW, 0xfc0007ff, 0x10000086, 0x0, // Vector Compare Equal To Unsigned Word VC-form (vcmpequw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPEQUW_, 0xfc0007ff, 0x10000486, 0x0, // Vector Compare Equal To Unsigned Word VC-form (vcmpequw. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPEQUD, 0xfc0007ff, 0x100000c7, 0x0, // Vector Compare Equal To Unsigned Doubleword VX-form (vcmpequd VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPEQUD_, 0xfc0007ff, 0x100004c7, 0x0, // Vector Compare Equal To Unsigned Doubleword VX-form (vcmpequd. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTSB, 0xfc0007ff, 0x10000306, 0x0, // Vector Compare Greater Than Signed Byte VC-form (vcmpgtsb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTSB_, 0xfc0007ff, 0x10000706, 0x0, // Vector Compare Greater Than Signed Byte VC-form (vcmpgtsb. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTSD, 0xfc0007ff, 0x100003c7, 0x0, // Vector Compare Greater Than Signed Doubleword VX-form (vcmpgtsd VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTSD_, 0xfc0007ff, 0x100007c7, 0x0, // Vector Compare Greater Than Signed Doubleword VX-form (vcmpgtsd. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTSH, 0xfc0007ff, 0x10000346, 0x0, // Vector Compare Greater Than Signed Halfword VC-form (vcmpgtsh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTSH_, 0xfc0007ff, 0x10000746, 0x0, // Vector Compare Greater Than Signed Halfword VC-form (vcmpgtsh. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTSW, 0xfc0007ff, 0x10000386, 0x0, // Vector Compare Greater Than Signed Word VC-form (vcmpgtsw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTSW_, 0xfc0007ff, 0x10000786, 0x0, // Vector Compare Greater Than Signed Word VC-form (vcmpgtsw. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTUB, 0xfc0007ff, 0x10000206, 0x0, // Vector Compare Greater Than Unsigned Byte VC-form (vcmpgtub VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTUB_, 0xfc0007ff, 0x10000606, 0x0, // Vector Compare Greater Than Unsigned Byte VC-form (vcmpgtub. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTUD, 0xfc0007ff, 0x100002c7, 0x0, // Vector Compare Greater Than Unsigned Doubleword VX-form (vcmpgtud VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTUD_, 0xfc0007ff, 0x100006c7, 0x0, // Vector Compare Greater Than Unsigned Doubleword VX-form (vcmpgtud. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTUH, 0xfc0007ff, 0x10000246, 0x0, // Vector Compare Greater Than Unsigned Halfword VC-form (vcmpgtuh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTUH_, 0xfc0007ff, 0x10000646, 0x0, // Vector Compare Greater Than Unsigned Halfword VC-form (vcmpgtuh. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTUW, 0xfc0007ff, 0x10000286, 0x0, // Vector Compare Greater Than Unsigned Word VC-form (vcmpgtuw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTUW_, 0xfc0007ff, 0x10000686, 0x0, // Vector Compare Greater Than Unsigned Word VC-form (vcmpgtuw. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VAND, 0xfc0007ff, 0x10000404, 0x0, // Vector Logical AND VX-form (vand VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VANDC, 0xfc0007ff, 0x10000444, 0x0, // Vector Logical AND with Complement VX-form (vandc VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VEQV, 0xfc0007ff, 0x10000684, 0x0, // Vector Logical Equivalent VX-form (veqv VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VNAND, 0xfc0007ff, 0x10000584, 0x0, // Vector Logical NAND VX-form (vnand VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VORC, 0xfc0007ff, 0x10000544, 0x0, // Vector Logical OR with Complement VX-form (vorc VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VNOR, 0xfc0007ff, 0x10000504, 0x0, // Vector Logical NOR VX-form (vnor VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VOR, 0xfc0007ff, 0x10000484, 0x0, // Vector Logical OR VX-form (vor VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VXOR, 0xfc0007ff, 0x100004c4, 0x0, // Vector Logical XOR VX-form (vxor VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VRLB, 0xfc0007ff, 0x10000004, 0x0, // Vector Rotate Left Byte VX-form (vrlb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VRLH, 0xfc0007ff, 0x10000044, 0x0, // Vector Rotate Left Halfword VX-form (vrlh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VRLW, 0xfc0007ff, 0x10000084, 0x0, // Vector Rotate Left Word VX-form (vrlw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VRLD, 0xfc0007ff, 0x100000c4, 0x0, // Vector Rotate Left Doubleword VX-form (vrld VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSLB, 0xfc0007ff, 0x10000104, 0x0, // Vector Shift Left Byte VX-form (vslb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSLH, 0xfc0007ff, 0x10000144, 0x0, // Vector Shift Left Halfword VX-form (vslh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSLW, 0xfc0007ff, 0x10000184, 0x0, // Vector Shift Left Word VX-form (vslw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSLD, 0xfc0007ff, 0x100005c4, 0x0, // Vector Shift Left Doubleword VX-form (vsld VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSRB, 0xfc0007ff, 0x10000204, 0x0, // Vector Shift Right Byte VX-form (vsrb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSRH, 0xfc0007ff, 0x10000244, 0x0, // Vector Shift Right Halfword VX-form (vsrh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSRW, 0xfc0007ff, 0x10000284, 0x0, // Vector Shift Right Word VX-form (vsrw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSRD, 0xfc0007ff, 0x100006c4, 0x0, // Vector Shift Right Doubleword VX-form (vsrd VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSRAB, 0xfc0007ff, 0x10000304, 0x0, // Vector Shift Right Algebraic Byte VX-form (vsrab VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSRAH, 0xfc0007ff, 0x10000344, 0x0, // Vector Shift Right Algebraic Halfword VX-form (vsrah VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSRAW, 0xfc0007ff, 0x10000384, 0x0, // Vector Shift Right Algebraic Word VX-form (vsraw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSRAD, 0xfc0007ff, 0x100003c4, 0x0, // Vector Shift Right Algebraic Doubleword VX-form (vsrad VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VADDFP, 0xfc0007ff, 0x1000000a, 0x0, // Vector Add Single-Precision VX-form (vaddfp VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSUBFP, 0xfc0007ff, 0x1000004a, 0x0, // Vector Subtract Single-Precision VX-form (vsubfp VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMADDFP, 0xfc00003f, 0x1000002e, 0x0, // Vector Multiply-Add Single-Precision VA-form (vmaddfp VRT,VRA,VRC,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_21_25, ap_VecReg_16_20}},
+	{VNMSUBFP, 0xfc00003f, 0x1000002f, 0x0, // Vector Negative Multiply-Subtract Single-Precision VA-form (vnmsubfp VRT,VRA,VRC,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_21_25, ap_VecReg_16_20}},
+	{VMAXFP, 0xfc0007ff, 0x1000040a, 0x0, // Vector Maximum Single-Precision VX-form (vmaxfp VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VMINFP, 0xfc0007ff, 0x1000044a, 0x0, // Vector Minimum Single-Precision VX-form (vminfp VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCTSXS, 0xfc0007ff, 0x100003ca, 0x0, // Vector Convert To Signed Fixed-Point Word Saturate VX-form (vctsxs VRT,VRB,UIM)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20, ap_ImmUnsigned_11_15}},
+	{VCTUXS, 0xfc0007ff, 0x1000038a, 0x0, // Vector Convert To Unsigned Fixed-Point Word Saturate VX-form (vctuxs VRT,VRB,UIM)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20, ap_ImmUnsigned_11_15}},
+	{VCFSX, 0xfc0007ff, 0x1000034a, 0x0, // Vector Convert From Signed Fixed-Point Word VX-form (vcfsx VRT,VRB,UIM)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20, ap_ImmUnsigned_11_15}},
+	{VCFUX, 0xfc0007ff, 0x1000030a, 0x0, // Vector Convert From Unsigned Fixed-Point Word VX-form (vcfux VRT,VRB,UIM)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20, ap_ImmUnsigned_11_15}},
+	{VRFIM, 0xfc0007ff, 0x100002ca, 0x1f0000, // Vector Round to Single-Precision Integer toward -Infinity VX-form (vrfim VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VRFIN, 0xfc0007ff, 0x1000020a, 0x1f0000, // Vector Round to Single-Precision Integer Nearest VX-form (vrfin VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VRFIP, 0xfc0007ff, 0x1000028a, 0x1f0000, // Vector Round to Single-Precision Integer toward +Infinity VX-form (vrfip VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VRFIZ, 0xfc0007ff, 0x1000024a, 0x1f0000, // Vector Round to Single-Precision Integer toward Zero VX-form (vrfiz VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VCMPBFP, 0xfc0007ff, 0x100003c6, 0x0, // Vector Compare Bounds Single-Precision VC-form (vcmpbfp VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPBFP_, 0xfc0007ff, 0x100007c6, 0x0, // Vector Compare Bounds Single-Precision VC-form (vcmpbfp. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPEQFP, 0xfc0007ff, 0x100000c6, 0x0, // Vector Compare Equal To Single-Precision VC-form (vcmpeqfp VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPEQFP_, 0xfc0007ff, 0x100004c6, 0x0, // Vector Compare Equal To Single-Precision VC-form (vcmpeqfp. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGEFP, 0xfc0007ff, 0x100001c6, 0x0, // Vector Compare Greater Than or Equal To Single-Precision VC-form (vcmpgefp VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGEFP_, 0xfc0007ff, 0x100005c6, 0x0, // Vector Compare Greater Than or Equal To Single-Precision VC-form (vcmpgefp. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTFP, 0xfc0007ff, 0x100002c6, 0x0, // Vector Compare Greater Than Single-Precision VC-form (vcmpgtfp VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCMPGTFP_, 0xfc0007ff, 0x100006c6, 0x0, // Vector Compare Greater Than Single-Precision VC-form (vcmpgtfp. VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VEXPTEFP, 0xfc0007ff, 0x1000018a, 0x1f0000, // Vector 2 Raised to the Exponent Estimate Floating-Point VX-form (vexptefp VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VLOGEFP, 0xfc0007ff, 0x100001ca, 0x1f0000, // Vector Log Base 2 Estimate Floating-Point VX-form (vlogefp VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VREFP, 0xfc0007ff, 0x1000010a, 0x1f0000, // Vector Reciprocal Estimate Single-Precision VX-form (vrefp VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VRSQRTEFP, 0xfc0007ff, 0x1000014a, 0x1f0000, // Vector Reciprocal Square Root Estimate Single-Precision VX-form (vrsqrtefp VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VCIPHER, 0xfc0007ff, 0x10000508, 0x0, // Vector AES Cipher VX-form (vcipher VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VCIPHERLAST, 0xfc0007ff, 0x10000509, 0x0, // Vector AES Cipher Last VX-form (vcipherlast VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VNCIPHER, 0xfc0007ff, 0x10000548, 0x0, // Vector AES Inverse Cipher VX-form (vncipher VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VNCIPHERLAST, 0xfc0007ff, 0x10000549, 0x0, // Vector AES Inverse Cipher Last VX-form (vncipherlast VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VSBOX, 0xfc0007ff, 0x100005c8, 0xf800, // Vector AES SubBytes VX-form (vsbox VRT,VRA)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15}},
+	{VSHASIGMAD, 0xfc0007ff, 0x100006c2, 0x0, // Vector SHA-512 Sigma Doubleword VX-form (vshasigmad VRT,VRA,ST,SIX)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_ImmUnsigned_16_16, ap_ImmUnsigned_17_20}},
+	{VSHASIGMAW, 0xfc0007ff, 0x10000682, 0x0, // Vector SHA-256 Sigma Word VX-form (vshasigmaw VRT,VRA,ST,SIX)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_ImmUnsigned_16_16, ap_ImmUnsigned_17_20}},
+	{VPMSUMB, 0xfc0007ff, 0x10000408, 0x0, // Vector Polynomial Multiply-Sum Byte VX-form (vpmsumb VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPMSUMD, 0xfc0007ff, 0x100004c8, 0x0, // Vector Polynomial Multiply-Sum Doubleword VX-form (vpmsumd VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPMSUMH, 0xfc0007ff, 0x10000448, 0x0, // Vector Polynomial Multiply-Sum Halfword VX-form (vpmsumh VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPMSUMW, 0xfc0007ff, 0x10000488, 0x0, // Vector Polynomial Multiply-Sum Word VX-form (vpmsumw VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{VPERMXOR, 0xfc00003f, 0x1000002d, 0x0, // Vector Permute and Exclusive-OR VA-form (vpermxor VRT,VRA,VRB,VRC)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_VecReg_21_25}},
+	{VGBBD, 0xfc0007ff, 0x1000050c, 0x1f0000, // Vector Gather Bits by Bytes by Doubleword VX-form (vgbbd VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VCLZB, 0xfc0007ff, 0x10000702, 0x1f0000, // Vector Count Leading Zeros Byte VX-form (vclzb VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VCLZH, 0xfc0007ff, 0x10000742, 0x1f0000, // Vector Count Leading Zeros Halfword VX-form (vclzh VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VCLZW, 0xfc0007ff, 0x10000782, 0x1f0000, // Vector Count Leading Zeros Word VX-form (vclzw VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VCLZD, 0xfc0007ff, 0x100007c2, 0x1f0000, // Vector Count Leading Zeros Doubleword (vclzd VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VPOPCNTB, 0xfc0007ff, 0x10000703, 0x1f0000, // Vector Population Count Byte (vpopcntb VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VPOPCNTD, 0xfc0007ff, 0x100007c3, 0x1f0000, // Vector Population Count Doubleword (vpopcntd VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VPOPCNTH, 0xfc0007ff, 0x10000743, 0x1f0000, // Vector Population Count Halfword (vpopcnth VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VPOPCNTW, 0xfc0007ff, 0x10000783, 0x1f0000, // Vector Population Count Word (vpopcntw VRT,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_16_20}},
+	{VBPERMQ, 0xfc0007ff, 0x1000054c, 0x0, // Vector Bit Permute Quadword VX-form (vbpermq VRT,VRA,VRB)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20}},
+	{BCDADD_, 0xfc0005ff, 0x10000401, 0x0, // Decimal Add Modulo VX-form (bcdadd. VRT,VRA,VRB,PS)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_ImmUnsigned_22_22}},
+	{BCDSUB_, 0xfc0005ff, 0x10000441, 0x0, // Decimal Subtract Modulo VX-form (bcdsub. VRT,VRA,VRB,PS)
+		[5]*argField{ap_VecReg_6_10, ap_VecReg_11_15, ap_VecReg_16_20, ap_ImmUnsigned_22_22}},
+	{MTVSCR, 0xfc0007ff, 0x10000644, 0x3ff0000, // Move To Vector Status and Control Register VX-form (mtvscr VRB)
+		[5]*argField{ap_VecReg_16_20}},
+	{MFVSCR, 0xfc0007ff, 0x10000604, 0x1ff800, // Move From Vector Status and Control Register VX-form (mfvscr VRT)
+		[5]*argField{ap_VecReg_6_10}},
+	{DADD, 0xfc0007ff, 0xec000004, 0x0, // DFP Add [Quad] X-form (dadd FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DADD_, 0xfc0007ff, 0xec000005, 0x0, // DFP Add [Quad] X-form (dadd. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DSUB, 0xfc0007ff, 0xec000404, 0x0, // DFP Subtract [Quad] X-form (dsub FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DSUB_, 0xfc0007ff, 0xec000405, 0x0, // DFP Subtract [Quad] X-form (dsub. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DMUL, 0xfc0007ff, 0xec000044, 0x0, // DFP Multiply [Quad] X-form (dmul FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DMUL_, 0xfc0007ff, 0xec000045, 0x0, // DFP Multiply [Quad] X-form (dmul. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DDIV, 0xfc0007ff, 0xec000444, 0x0, // DFP Divide [Quad] X-form (ddiv FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DDIV_, 0xfc0007ff, 0xec000445, 0x0, // DFP Divide [Quad] X-form (ddiv. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DCMPU, 0xfc0007fe, 0xec000504, 0x600001, // DFP Compare Unordered [Quad] X-form (dcmpu BF,FRA,FRB)
+		[5]*argField{ap_CondRegField_6_8, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DCMPO, 0xfc0007fe, 0xec000104, 0x600001, // DFP Compare Ordered [Quad] X-form (dcmpo BF,FRA,FRB)
+		[5]*argField{ap_CondRegField_6_8, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DTSTDC, 0xfc0003fe, 0xec000184, 0x600001, // DFP Test Data Class [Quad] Z22-form (dtstdc BF,FRA,DCM)
+		[5]*argField{ap_CondRegField_6_8, ap_FPReg_11_15, ap_ImmUnsigned_16_21}},
+	{DTSTDG, 0xfc0003fe, 0xec0001c4, 0x600001, // DFP Test Data Group [Quad] Z22-form (dtstdg BF,FRA,DGM)
+		[5]*argField{ap_CondRegField_6_8, ap_FPReg_11_15, ap_ImmUnsigned_16_21}},
+	{DTSTEX, 0xfc0007fe, 0xec000144, 0x600001, // DFP Test Exponent [Quad] X-form (dtstex BF,FRA,FRB)
+		[5]*argField{ap_CondRegField_6_8, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DTSTSF, 0xfc0007fe, 0xec000544, 0x600001, // DFP Test Significance [Quad] X-form (dtstsf BF,FRA,FRB)
+		[5]*argField{ap_CondRegField_6_8, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DQUAI, 0xfc0001ff, 0xec000086, 0x0, // DFP Quantize Immediate [Quad] Z23-form (dquai TE,FRT,FRB,RMC)
+		[5]*argField{ap_ImmSigned_11_15, ap_FPReg_6_10, ap_FPReg_16_20, ap_ImmUnsigned_21_22}},
+	{DQUAI_, 0xfc0001ff, 0xec000087, 0x0, // DFP Quantize Immediate [Quad] Z23-form (dquai. TE,FRT,FRB,RMC)
+		[5]*argField{ap_ImmSigned_11_15, ap_FPReg_6_10, ap_FPReg_16_20, ap_ImmUnsigned_21_22}},
+	{DQUA, 0xfc0001ff, 0xec000006, 0x0, // DFP Quantize [Quad] Z23-form (dqua FRT,FRA,FRB,RMC)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20, ap_ImmUnsigned_21_22}},
+	{DQUA_, 0xfc0001ff, 0xec000007, 0x0, // DFP Quantize [Quad] Z23-form (dqua. FRT,FRA,FRB,RMC)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20, ap_ImmUnsigned_21_22}},
+	{DRRND, 0xfc0001ff, 0xec000046, 0x0, // DFP Reround [Quad] Z23-form (drrnd FRT,FRA,FRB,RMC)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20, ap_ImmUnsigned_21_22}},
+	{DRRND_, 0xfc0001ff, 0xec000047, 0x0, // DFP Reround [Quad] Z23-form (drrnd. FRT,FRA,FRB,RMC)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20, ap_ImmUnsigned_21_22}},
+	{DRINTX, 0xfc0001ff, 0xec0000c6, 0x1e0000, // DFP Round To FP Integer With Inexact [Quad] Z23-form (drintx R,FRT,FRB,RMC)
+		[5]*argField{ap_ImmUnsigned_15_15, ap_FPReg_6_10, ap_FPReg_16_20, ap_ImmUnsigned_21_22}},
+	{DRINTX_, 0xfc0001ff, 0xec0000c7, 0x1e0000, // DFP Round To FP Integer With Inexact [Quad] Z23-form (drintx. R,FRT,FRB,RMC)
+		[5]*argField{ap_ImmUnsigned_15_15, ap_FPReg_6_10, ap_FPReg_16_20, ap_ImmUnsigned_21_22}},
+	{DRINTN, 0xfc0001ff, 0xec0001c6, 0x1e0000, // DFP Round To FP Integer Without Inexact [Quad] Z23-form (drintn R,FRT,FRB,RMC)
+		[5]*argField{ap_ImmUnsigned_15_15, ap_FPReg_6_10, ap_FPReg_16_20, ap_ImmUnsigned_21_22}},
+	{DRINTN_, 0xfc0001ff, 0xec0001c7, 0x1e0000, // DFP Round To FP Integer Without Inexact [Quad] Z23-form (drintn. R,FRT,FRB,RMC)
+		[5]*argField{ap_ImmUnsigned_15_15, ap_FPReg_6_10, ap_FPReg_16_20, ap_ImmUnsigned_21_22}},
+	{DCTDP, 0xfc0007ff, 0xec000204, 0x1f0000, // DFP Convert To DFP Long X-form (dctdp FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DCTDP_, 0xfc0007ff, 0xec000205, 0x1f0000, // DFP Convert To DFP Long X-form (dctdp. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DCTQPQ, 0xfc0007ff, 0xfc000204, 0x1f0000, // DFP Convert To DFP Extended X-form (dctqpq FRTp,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DCTQPQ_, 0xfc0007ff, 0xfc000205, 0x1f0000, // DFP Convert To DFP Extended X-form (dctqpq. FRTp,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DRSP, 0xfc0007ff, 0xec000604, 0x1f0000, // DFP Round To DFP Short X-form (drsp FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DRSP_, 0xfc0007ff, 0xec000605, 0x1f0000, // DFP Round To DFP Short X-form (drsp. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DRDPQ, 0xfc0007ff, 0xfc000604, 0x1f0000, // DFP Round To DFP Long X-form (drdpq FRTp,FRBp)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DRDPQ_, 0xfc0007ff, 0xfc000605, 0x1f0000, // DFP Round To DFP Long X-form (drdpq. FRTp,FRBp)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DCFFIX, 0xfc0007ff, 0xec000644, 0x1f0000, // DFP Convert From Fixed X-form (dcffix FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DCFFIX_, 0xfc0007ff, 0xec000645, 0x1f0000, // DFP Convert From Fixed X-form (dcffix. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DCFFIXQ, 0xfc0007ff, 0xfc000644, 0x1f0000, // DFP Convert From Fixed Quad X-form (dcffixq FRTp,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DCFFIXQ_, 0xfc0007ff, 0xfc000645, 0x1f0000, // DFP Convert From Fixed Quad X-form (dcffixq. FRTp,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DCTFIX, 0xfc0007ff, 0xec000244, 0x1f0000, // DFP Convert To Fixed [Quad] X-form (dctfix FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DCTFIX_, 0xfc0007ff, 0xec000245, 0x1f0000, // DFP Convert To Fixed [Quad] X-form (dctfix. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DDEDPD, 0xfc0007ff, 0xec000284, 0x70000, // DFP Decode DPD To BCD [Quad] X-form (ddedpd SP,FRT,FRB)
+		[5]*argField{ap_ImmUnsigned_11_12, ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DDEDPD_, 0xfc0007ff, 0xec000285, 0x70000, // DFP Decode DPD To BCD [Quad] X-form (ddedpd. SP,FRT,FRB)
+		[5]*argField{ap_ImmUnsigned_11_12, ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DENBCD, 0xfc0007ff, 0xec000684, 0xf0000, // DFP Encode BCD To DPD [Quad] X-form (denbcd S,FRT,FRB)
+		[5]*argField{ap_ImmUnsigned_11_11, ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DENBCD_, 0xfc0007ff, 0xec000685, 0xf0000, // DFP Encode BCD To DPD [Quad] X-form (denbcd. S,FRT,FRB)
+		[5]*argField{ap_ImmUnsigned_11_11, ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DXEX, 0xfc0007ff, 0xec0002c4, 0x1f0000, // DFP Extract Biased Exponent [Quad] X-form (dxex FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DXEX_, 0xfc0007ff, 0xec0002c5, 0x1f0000, // DFP Extract Biased Exponent [Quad] X-form (dxex. FRT,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_16_20}},
+	{DIEX, 0xfc0007ff, 0xec0006c4, 0x0, // DFP Insert Biased Exponent [Quad] X-form (diex FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DIEX_, 0xfc0007ff, 0xec0006c5, 0x0, // DFP Insert Biased Exponent [Quad] X-form (diex. FRT,FRA,FRB)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_FPReg_16_20}},
+	{DSCLI, 0xfc0003ff, 0xec000084, 0x0, // DFP Shift Significand Left Immediate [Quad] Z22-form (dscli FRT,FRA,SH)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_ImmUnsigned_16_21}},
+	{DSCLI_, 0xfc0003ff, 0xec000085, 0x0, // DFP Shift Significand Left Immediate [Quad] Z22-form (dscli. FRT,FRA,SH)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_ImmUnsigned_16_21}},
+	{DSCRI, 0xfc0003ff, 0xec0000c4, 0x0, // DFP Shift Significand Right Immediate [Quad] Z22-form (dscri FRT,FRA,SH)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_ImmUnsigned_16_21}},
+	{DSCRI_, 0xfc0003ff, 0xec0000c5, 0x0, // DFP Shift Significand Right Immediate [Quad] Z22-form (dscri. FRT,FRA,SH)
+		[5]*argField{ap_FPReg_6_10, ap_FPReg_11_15, ap_ImmUnsigned_16_21}},
+	{LXSDX, 0xfc0007fe, 0x7c000498, 0x0, // Load VSX Scalar Doubleword Indexed XX1-form (lxsdx XT,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LXSIWAX, 0xfc0007fe, 0x7c000098, 0x0, // Load VSX Scalar as Integer Word Algebraic Indexed XX1-form (lxsiwax XT,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LXSIWZX, 0xfc0007fe, 0x7c000018, 0x0, // Load VSX Scalar as Integer Word and Zero Indexed XX1-form (lxsiwzx XT,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LXSSPX, 0xfc0007fe, 0x7c000418, 0x0, // Load VSX Scalar Single-Precision Indexed XX1-form (lxsspx XT,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LXVD2X, 0xfc0007fe, 0x7c000698, 0x0, // Load VSX Vector Doubleword*2 Indexed XX1-form (lxvd2x XT,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LXVDSX, 0xfc0007fe, 0x7c000298, 0x0, // Load VSX Vector Doubleword & Splat Indexed XX1-form (lxvdsx XT,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LXVW4X, 0xfc0007fe, 0x7c000618, 0x0, // Load VSX Vector Word*4 Indexed XX1-form (lxvw4x XT,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STXSDX, 0xfc0007fe, 0x7c000598, 0x0, // Store VSX Scalar Doubleword Indexed XX1-form (stxsdx XS,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STXSIWX, 0xfc0007fe, 0x7c000118, 0x0, // Store VSX Scalar as Integer Word Indexed XX1-form (stxsiwx XS,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STXSSPX, 0xfc0007fe, 0x7c000518, 0x0, // Store VSX Scalar Single-Precision Indexed XX1-form (stxsspx XS,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STXVD2X, 0xfc0007fe, 0x7c000798, 0x0, // Store VSX Vector Doubleword*2 Indexed XX1-form (stxvd2x XS,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STXVW4X, 0xfc0007fe, 0x7c000718, 0x0, // Store VSX Vector Word*4 Indexed XX1-form (stxvw4x XS,RA,RB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{XSABSDP, 0xfc0007fc, 0xf0000564, 0x1f0000, // VSX Scalar Absolute Value Double-Precision XX2-form (xsabsdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSADDDP, 0xfc0007f8, 0xf0000100, 0x0, // VSX Scalar Add Double-Precision XX3-form (xsadddp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSADDSP, 0xfc0007f8, 0xf0000000, 0x0, // VSX Scalar Add Single-Precision XX3-form (xsaddsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSCMPODP, 0xfc0007f8, 0xf0000158, 0x600001, // VSX Scalar Compare Ordered Double-Precision XX3-form (xscmpodp BF,XA,XB)
+		[5]*argField{ap_CondRegField_6_8, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSCMPUDP, 0xfc0007f8, 0xf0000118, 0x600001, // VSX Scalar Compare Unordered Double-Precision XX3-form (xscmpudp BF,XA,XB)
+		[5]*argField{ap_CondRegField_6_8, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSCPSGNDP, 0xfc0007f8, 0xf0000580, 0x0, // VSX Scalar Copy Sign Double-Precision XX3-form (xscpsgndp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSCVDPSP, 0xfc0007fc, 0xf0000424, 0x1f0000, // VSX Scalar round Double-Precision to single-precision and Convert to Single-Precision format XX2-form (xscvdpsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVDPSPN, 0xfc0007fc, 0xf000042c, 0x1f0000, // VSX Scalar Convert Scalar Single-Precision to Vector Single-Precision format Non-signalling XX2-form (xscvdpspn XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVDPSXDS, 0xfc0007fc, 0xf0000560, 0x1f0000, // VSX Scalar truncate Double-Precision to integer and Convert to Signed Integer Doubleword format with Saturate XX2-form (xscvdpsxds XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVDPSXWS, 0xfc0007fc, 0xf0000160, 0x1f0000, // VSX Scalar truncate Double-Precision to integer and Convert to Signed Integer Word format with Saturate XX2-form (xscvdpsxws XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVDPUXDS, 0xfc0007fc, 0xf0000520, 0x1f0000, // VSX Scalar truncate Double-Precision integer and Convert to Unsigned Integer Doubleword format with Saturate XX2-form (xscvdpuxds XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVDPUXWS, 0xfc0007fc, 0xf0000120, 0x1f0000, // VSX Scalar truncate Double-Precision to integer and Convert to Unsigned Integer Word format with Saturate XX2-form (xscvdpuxws XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVSPDP, 0xfc0007fc, 0xf0000524, 0x1f0000, // VSX Scalar Convert Single-Precision to Double-Precision format XX2-form (xscvspdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVSPDPN, 0xfc0007fc, 0xf000052c, 0x1f0000, // VSX Scalar Convert Single-Precision to Double-Precision format Non-signalling XX2-form (xscvspdpn XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVSXDDP, 0xfc0007fc, 0xf00005e0, 0x1f0000, // VSX Scalar Convert Signed Integer Doubleword to floating-point format and round to Double-Precision format XX2-form (xscvsxddp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVSXDSP, 0xfc0007fc, 0xf00004e0, 0x1f0000, // VSX Scalar Convert Signed Integer Doubleword to floating-point format and round to Single-Precision XX2-form (xscvsxdsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVUXDDP, 0xfc0007fc, 0xf00005a0, 0x1f0000, // VSX Scalar Convert Unsigned Integer Doubleword to floating-point format and round to Double-Precision format XX2-form (xscvuxddp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSCVUXDSP, 0xfc0007fc, 0xf00004a0, 0x1f0000, // VSX Scalar Convert Unsigned Integer Doubleword to floating-point format and round to Single-Precision XX2-form (xscvuxdsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSDIVDP, 0xfc0007f8, 0xf00001c0, 0x0, // VSX Scalar Divide Double-Precision XX3-form (xsdivdp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSDIVSP, 0xfc0007f8, 0xf00000c0, 0x0, // VSX Scalar Divide Single-Precision XX3-form (xsdivsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSMADDADP, 0xfc0007f8, 0xf0000108, 0x0, // VSX Scalar Multiply-Add Double-Precision XX3-form (xsmaddadp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSMADDASP, 0xfc0007f8, 0xf0000008, 0x0, // VSX Scalar Multiply-Add Single-Precision XX3-form (xsmaddasp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSMAXDP, 0xfc0007f8, 0xf0000500, 0x0, // VSX Scalar Maximum Double-Precision XX3-form (xsmaxdp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSMINDP, 0xfc0007f8, 0xf0000540, 0x0, // VSX Scalar Minimum Double-Precision XX3-form (xsmindp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSMSUBADP, 0xfc0007f8, 0xf0000188, 0x0, // VSX Scalar Multiply-Subtract Double-Precision XX3-form (xsmsubadp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSMSUBASP, 0xfc0007f8, 0xf0000088, 0x0, // VSX Scalar Multiply-Subtract Single-Precision XX3-form (xsmsubasp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSMULDP, 0xfc0007f8, 0xf0000180, 0x0, // VSX Scalar Multiply Double-Precision XX3-form (xsmuldp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSMULSP, 0xfc0007f8, 0xf0000080, 0x0, // VSX Scalar Multiply Single-Precision XX3-form (xsmulsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSNABSDP, 0xfc0007fc, 0xf00005a4, 0x1f0000, // VSX Scalar Negative Absolute Value Double-Precision XX2-form (xsnabsdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSNEGDP, 0xfc0007fc, 0xf00005e4, 0x1f0000, // VSX Scalar Negate Double-Precision XX2-form (xsnegdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSNMADDADP, 0xfc0007f8, 0xf0000508, 0x0, // VSX Scalar Negative Multiply-Add Double-Precision XX3-form (xsnmaddadp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSNMADDASP, 0xfc0007f8, 0xf0000408, 0x0, // VSX Scalar Negative Multiply-Add Single-Precision XX3-form (xsnmaddasp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSNMSUBADP, 0xfc0007f8, 0xf0000588, 0x0, // VSX Scalar Negative Multiply-Subtract Double-Precision XX3-form (xsnmsubadp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSNMSUBASP, 0xfc0007f8, 0xf0000488, 0x0, // VSX Scalar Negative Multiply-Subtract Single-Precision XX3-form (xsnmsubasp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSRDPI, 0xfc0007fc, 0xf0000124, 0x1f0000, // VSX Scalar Round to Double-Precision Integer using round to Nearest Away XX2-form (xsrdpi XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSRDPIC, 0xfc0007fc, 0xf00001ac, 0x1f0000, // VSX Scalar Round to Double-Precision Integer exact using Current rounding mode XX2-form (xsrdpic XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSRDPIM, 0xfc0007fc, 0xf00001e4, 0x1f0000, // VSX Scalar Round to Double-Precision Integer using round toward -Infinity XX2-form (xsrdpim XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSRDPIP, 0xfc0007fc, 0xf00001a4, 0x1f0000, // VSX Scalar Round to Double-Precision Integer using round toward +Infinity XX2-form (xsrdpip XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSRDPIZ, 0xfc0007fc, 0xf0000164, 0x1f0000, // VSX Scalar Round to Double-Precision Integer using round toward Zero XX2-form (xsrdpiz XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSREDP, 0xfc0007fc, 0xf0000168, 0x1f0000, // VSX Scalar Reciprocal Estimate Double-Precision XX2-form (xsredp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSRESP, 0xfc0007fc, 0xf0000068, 0x1f0000, // VSX Scalar Reciprocal Estimate Single-Precision XX2-form (xsresp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSRSP, 0xfc0007fc, 0xf0000464, 0x1f0000, // VSX Scalar Round to Single-Precision XX2-form (xsrsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSRSQRTEDP, 0xfc0007fc, 0xf0000128, 0x1f0000, // VSX Scalar Reciprocal Square Root Estimate Double-Precision XX2-form (xsrsqrtedp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSRSQRTESP, 0xfc0007fc, 0xf0000028, 0x1f0000, // VSX Scalar Reciprocal Square Root Estimate Single-Precision XX2-form (xsrsqrtesp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSSQRTDP, 0xfc0007fc, 0xf000012c, 0x1f0000, // VSX Scalar Square Root Double-Precision XX2-form (xssqrtdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSSQRTSP, 0xfc0007fc, 0xf000002c, 0x1f0000, // VSX Scalar Square Root Single-Precision XX-form (xssqrtsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XSSUBDP, 0xfc0007f8, 0xf0000140, 0x0, // VSX Scalar Subtract Double-Precision XX3-form (xssubdp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSSUBSP, 0xfc0007f8, 0xf0000040, 0x0, // VSX Scalar Subtract Single-Precision XX3-form (xssubsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSTDIVDP, 0xfc0007f8, 0xf00001e8, 0x600001, // VSX Scalar Test for software Divide Double-Precision XX3-form (xstdivdp BF,XA,XB)
+		[5]*argField{ap_CondRegField_6_8, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XSTSQRTDP, 0xfc0007fc, 0xf00001a8, 0x7f0001, // VSX Scalar Test for software Square Root Double-Precision XX2-form (xstsqrtdp BF,XB)
+		[5]*argField{ap_CondRegField_6_8, ap_VecSReg_30_30_16_20}},
+	{XVABSDP, 0xfc0007fc, 0xf0000764, 0x1f0000, // VSX Vector Absolute Value Double-Precision XX2-form (xvabsdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVABSSP, 0xfc0007fc, 0xf0000664, 0x1f0000, // VSX Vector Absolute Value Single-Precision XX2-form (xvabssp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVADDDP, 0xfc0007f8, 0xf0000300, 0x0, // VSX Vector Add Double-Precision XX3-form (xvadddp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVADDSP, 0xfc0007f8, 0xf0000200, 0x0, // VSX Vector Add Single-Precision XX3-form (xvaddsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPEQDP, 0xfc0007f8, 0xf0000318, 0x0, // VSX Vector Compare Equal To Double-Precision [ & Record ] XX3-form (xvcmpeqdp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPEQDP_, 0xfc0007f8, 0xf0000718, 0x0, // VSX Vector Compare Equal To Double-Precision [ & Record ] XX3-form (xvcmpeqdp. XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPEQSP, 0xfc0007f8, 0xf0000218, 0x0, // VSX Vector Compare Equal To Single-Precision [ & Record ] XX3-form (xvcmpeqsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPEQSP_, 0xfc0007f8, 0xf0000618, 0x0, // VSX Vector Compare Equal To Single-Precision [ & Record ] XX3-form (xvcmpeqsp. XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPGEDP, 0xfc0007f8, 0xf0000398, 0x0, // VSX Vector Compare Greater Than or Equal To Double-Precision [ & Record ] XX3-form (xvcmpgedp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPGEDP_, 0xfc0007f8, 0xf0000798, 0x0, // VSX Vector Compare Greater Than or Equal To Double-Precision [ & Record ] XX3-form (xvcmpgedp. XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPGESP, 0xfc0007f8, 0xf0000298, 0x0, // VSX Vector Compare Greater Than or Equal To Single-Precision [ & record CR6 ] XX3-form (xvcmpgesp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPGESP_, 0xfc0007f8, 0xf0000698, 0x0, // VSX Vector Compare Greater Than or Equal To Single-Precision [ & record CR6 ] XX3-form (xvcmpgesp. XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPGTDP, 0xfc0007f8, 0xf0000358, 0x0, // VSX Vector Compare Greater Than Double-Precision [ & record CR6 ] XX3-form (xvcmpgtdp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPGTDP_, 0xfc0007f8, 0xf0000758, 0x0, // VSX Vector Compare Greater Than Double-Precision [ & record CR6 ] XX3-form (xvcmpgtdp. XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPGTSP, 0xfc0007f8, 0xf0000258, 0x0, // VSX Vector Compare Greater Than Single-Precision [ & record CR6 ] XX3-form (xvcmpgtsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCMPGTSP_, 0xfc0007f8, 0xf0000658, 0x0, // VSX Vector Compare Greater Than Single-Precision [ & record CR6 ] XX3-form (xvcmpgtsp. XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCPSGNDP, 0xfc0007f8, 0xf0000780, 0x0, // VSX Vector Copy Sign Double-Precision XX3-form (xvcpsgndp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCPSGNSP, 0xfc0007f8, 0xf0000680, 0x0, // VSX Vector Copy Sign Single-Precision XX3-form (xvcpsgnsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVCVDPSP, 0xfc0007fc, 0xf0000624, 0x1f0000, // VSX Vector round Double-Precision to single-precision and Convert to Single-Precision format XX2-form (xvcvdpsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVDPSXDS, 0xfc0007fc, 0xf0000760, 0x1f0000, // VSX Vector truncate Double-Precision to integer and Convert to Signed Integer Doubleword format with Saturate XX2-form (xvcvdpsxds XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVDPSXWS, 0xfc0007fc, 0xf0000360, 0x1f0000, // VSX Vector truncate Double-Precision to integer and Convert to Signed Integer Word format with Saturate XX2-form (xvcvdpsxws XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVDPUXDS, 0xfc0007fc, 0xf0000720, 0x1f0000, // VSX Vector truncate Double-Precision to integer and Convert to Unsigned Integer Doubleword format with Saturate XX2-form (xvcvdpuxds XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVDPUXWS, 0xfc0007fc, 0xf0000320, 0x1f0000, // VSX Vector truncate Double-Precision to integer and Convert to Unsigned Integer Word format with Saturate XX2-form (xvcvdpuxws XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVSPDP, 0xfc0007fc, 0xf0000724, 0x1f0000, // VSX Vector Convert Single-Precision to Double-Precision format XX2-form (xvcvspdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVSPSXDS, 0xfc0007fc, 0xf0000660, 0x1f0000, // VSX Vector truncate Single-Precision to integer and Convert to Signed Integer Doubleword format with Saturate XX2-form (xvcvspsxds XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVSPSXWS, 0xfc0007fc, 0xf0000260, 0x1f0000, // VSX Vector truncate Single-Precision to integer and Convert to Signed Integer Word format with Saturate XX2-form (xvcvspsxws XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVSPUXDS, 0xfc0007fc, 0xf0000620, 0x1f0000, // VSX Vector truncate Single-Precision to integer and Convert to Unsigned Integer Doubleword format with Saturate XX2-form (xvcvspuxds XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVSPUXWS, 0xfc0007fc, 0xf0000220, 0x1f0000, // VSX Vector truncate Single-Precision to integer and Convert to Unsigned Integer Word format with Saturate XX2-form (xvcvspuxws XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVSXDDP, 0xfc0007fc, 0xf00007e0, 0x1f0000, // VSX Vector Convert and round Signed Integer Doubleword to Double-Precision format XX2-form (xvcvsxddp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVSXDSP, 0xfc0007fc, 0xf00006e0, 0x1f0000, // VSX Vector Convert and round Signed Integer Doubleword to Single-Precision format XX2-form (xvcvsxdsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVSXWDP, 0xfc0007fc, 0xf00003e0, 0x1f0000, // VSX Vector Convert Signed Integer Word to Double-Precision format XX2-form (xvcvsxwdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVSXWSP, 0xfc0007fc, 0xf00002e0, 0x1f0000, // VSX Vector Convert and round Signed Integer Word to Single-Precision format XX2-form (xvcvsxwsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVUXDDP, 0xfc0007fc, 0xf00007a0, 0x1f0000, // VSX Vector Convert and round Unsigned Integer Doubleword to Double-Precision format XX2-form (xvcvuxddp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVUXDSP, 0xfc0007fc, 0xf00006a0, 0x1f0000, // VSX Vector Convert and round Unsigned Integer Doubleword to Single-Precision format XX2-form (xvcvuxdsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVUXWDP, 0xfc0007fc, 0xf00003a0, 0x1f0000, // VSX Vector Convert and round Unsigned Integer Word to Double-Precision format XX2-form (xvcvuxwdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVCVUXWSP, 0xfc0007fc, 0xf00002a0, 0x1f0000, // VSX Vector Convert and round Unsigned Integer Word to Single-Precision format XX2-form (xvcvuxwsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVDIVDP, 0xfc0007f8, 0xf00003c0, 0x0, // VSX Vector Divide Double-Precision XX3-form (xvdivdp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVDIVSP, 0xfc0007f8, 0xf00002c0, 0x0, // VSX Vector Divide Single-Precision XX3-form (xvdivsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVMADDADP, 0xfc0007f8, 0xf0000308, 0x0, // VSX Vector Multiply-Add Double-Precision XX3-form (xvmaddadp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVMADDASP, 0xfc0007f8, 0xf0000208, 0x0, // VSX Vector Multiply-Add Single-Precision XX3-form (xvmaddasp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVMAXDP, 0xfc0007f8, 0xf0000700, 0x0, // VSX Vector Maximum Double-Precision XX3-form (xvmaxdp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVMAXSP, 0xfc0007f8, 0xf0000600, 0x0, // VSX Vector Maximum Single-Precision XX3-form (xvmaxsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVMINDP, 0xfc0007f8, 0xf0000740, 0x0, // VSX Vector Minimum Double-Precision XX3-form (xvmindp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVMINSP, 0xfc0007f8, 0xf0000640, 0x0, // VSX Vector Minimum Single-Precision XX3-form (xvminsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVMSUBADP, 0xfc0007f8, 0xf0000388, 0x0, // VSX Vector Multiply-Subtract Double-Precision XX3-form (xvmsubadp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVMSUBASP, 0xfc0007f8, 0xf0000288, 0x0, // VSX Vector Multiply-Subtract Single-Precision XX3-form (xvmsubasp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVMULDP, 0xfc0007f8, 0xf0000380, 0x0, // VSX Vector Multiply Double-Precision XX3-form (xvmuldp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVMULSP, 0xfc0007f8, 0xf0000280, 0x0, // VSX Vector Multiply Single-Precision XX3-form (xvmulsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVNABSDP, 0xfc0007fc, 0xf00007a4, 0x1f0000, // VSX Vector Negative Absolute Value Double-Precision XX2-form (xvnabsdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVNABSSP, 0xfc0007fc, 0xf00006a4, 0x1f0000, // VSX Vector Negative Absolute Value Single-Precision XX2-form (xvnabssp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVNEGDP, 0xfc0007fc, 0xf00007e4, 0x1f0000, // VSX Vector Negate Double-Precision XX2-form (xvnegdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVNEGSP, 0xfc0007fc, 0xf00006e4, 0x1f0000, // VSX Vector Negate Single-Precision XX2-form (xvnegsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVNMADDADP, 0xfc0007f8, 0xf0000708, 0x0, // VSX Vector Negative Multiply-Add Double-Precision XX3-form (xvnmaddadp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVNMADDASP, 0xfc0007f8, 0xf0000608, 0x0, // VSX Vector Negative Multiply-Add Single-Precision XX3-form (xvnmaddasp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVNMSUBADP, 0xfc0007f8, 0xf0000788, 0x0, // VSX Vector Negative Multiply-Subtract Double-Precision XX3-form (xvnmsubadp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVNMSUBASP, 0xfc0007f8, 0xf0000688, 0x0, // VSX Vector Negative Multiply-Subtract Single-Precision XX3-form (xvnmsubasp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVRDPI, 0xfc0007fc, 0xf0000324, 0x1f0000, // VSX Vector Round to Double-Precision Integer using round to Nearest Away XX2-form (xvrdpi XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRDPIC, 0xfc0007fc, 0xf00003ac, 0x1f0000, // VSX Vector Round to Double-Precision Integer Exact using Current rounding mode XX2-form (xvrdpic XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRDPIM, 0xfc0007fc, 0xf00003e4, 0x1f0000, // VSX Vector Round to Double-Precision Integer using round toward -Infinity XX2-form (xvrdpim XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRDPIP, 0xfc0007fc, 0xf00003a4, 0x1f0000, // VSX Vector Round to Double-Precision Integer using round toward +Infinity XX2-form (xvrdpip XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRDPIZ, 0xfc0007fc, 0xf0000364, 0x1f0000, // VSX Vector Round to Double-Precision Integer using round toward Zero XX2-form (xvrdpiz XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVREDP, 0xfc0007fc, 0xf0000368, 0x1f0000, // VSX Vector Reciprocal Estimate Double-Precision XX2-form (xvredp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRESP, 0xfc0007fc, 0xf0000268, 0x1f0000, // VSX Vector Reciprocal Estimate Single-Precision XX2-form (xvresp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRSPI, 0xfc0007fc, 0xf0000224, 0x1f0000, // VSX Vector Round to Single-Precision Integer using round to Nearest Away XX2-form (xvrspi XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRSPIC, 0xfc0007fc, 0xf00002ac, 0x1f0000, // VSX Vector Round to Single-Precision Integer Exact using Current rounding mode XX2-form (xvrspic XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRSPIM, 0xfc0007fc, 0xf00002e4, 0x1f0000, // VSX Vector Round to Single-Precision Integer using round toward -Infinity XX2-form (xvrspim XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRSPIP, 0xfc0007fc, 0xf00002a4, 0x1f0000, // VSX Vector Round to Single-Precision Integer using round toward +Infinity XX2-form (xvrspip XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRSPIZ, 0xfc0007fc, 0xf0000264, 0x1f0000, // VSX Vector Round to Single-Precision Integer using round toward Zero XX2-form (xvrspiz XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRSQRTEDP, 0xfc0007fc, 0xf0000328, 0x1f0000, // VSX Vector Reciprocal Square Root Estimate Double-Precision XX2-form (xvrsqrtedp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVRSQRTESP, 0xfc0007fc, 0xf0000228, 0x1f0000, // VSX Vector Reciprocal Square Root Estimate Single-Precision XX2-form (xvrsqrtesp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVSQRTDP, 0xfc0007fc, 0xf000032c, 0x1f0000, // VSX Vector Square Root Double-Precision XX2-form (xvsqrtdp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVSQRTSP, 0xfc0007fc, 0xf000022c, 0x1f0000, // VSX Vector Square Root Single-Precision XX2-form (xvsqrtsp XT,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20}},
+	{XVSUBDP, 0xfc0007f8, 0xf0000340, 0x0, // VSX Vector Subtract Double-Precision XX3-form (xvsubdp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVSUBSP, 0xfc0007f8, 0xf0000240, 0x0, // VSX Vector Subtract Single-Precision XX3-form (xvsubsp XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVTDIVDP, 0xfc0007f8, 0xf00003e8, 0x600001, // VSX Vector Test for software Divide Double-Precision XX3-form (xvtdivdp BF,XA,XB)
+		[5]*argField{ap_CondRegField_6_8, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVTDIVSP, 0xfc0007f8, 0xf00002e8, 0x600001, // VSX Vector Test for software Divide Single-Precision XX3-form (xvtdivsp BF,XA,XB)
+		[5]*argField{ap_CondRegField_6_8, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XVTSQRTDP, 0xfc0007fc, 0xf00003a8, 0x7f0001, // VSX Vector Test for software Square Root Double-Precision XX2-form (xvtsqrtdp BF,XB)
+		[5]*argField{ap_CondRegField_6_8, ap_VecSReg_30_30_16_20}},
+	{XVTSQRTSP, 0xfc0007fc, 0xf00002a8, 0x7f0001, // VSX Vector Test for software Square Root Single-Precision XX2-form (xvtsqrtsp BF,XB)
+		[5]*argField{ap_CondRegField_6_8, ap_VecSReg_30_30_16_20}},
+	{XXLAND, 0xfc0007f8, 0xf0000410, 0x0, // VSX Logical AND XX3-form (xxland XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XXLANDC, 0xfc0007f8, 0xf0000450, 0x0, // VSX Logical AND with Complement XX3-form (xxlandc XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XXLEQV, 0xfc0007f8, 0xf00005d0, 0x0, // VSX Logical Equivalence XX3-form (xxleqv XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XXLNAND, 0xfc0007f8, 0xf0000590, 0x0, // VSX Logical NAND XX3-form (xxlnand XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XXLORC, 0xfc0007f8, 0xf0000550, 0x0, // VSX Logical OR with Complement XX3-form (xxlorc XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XXLNOR, 0xfc0007f8, 0xf0000510, 0x0, // VSX Logical NOR XX3-form (xxlnor XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XXLOR, 0xfc0007f8, 0xf0000490, 0x0, // VSX Logical OR XX3-form (xxlor XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XXLXOR, 0xfc0007f8, 0xf00004d0, 0x0, // VSX Logical XOR XX3-form (xxlxor XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XXMRGHW, 0xfc0007f8, 0xf0000090, 0x0, // VSX Merge High Word XX3-form (xxmrghw XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XXMRGLW, 0xfc0007f8, 0xf0000190, 0x0, // VSX Merge Low Word XX3-form (xxmrglw XT,XA,XB)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20}},
+	{XXPERMDI, 0xfc0004f8, 0xf0000050, 0x0, // VSX Permute Doubleword Immediate XX3-form (xxpermdi XT,XA,XB,DM)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20, ap_ImmUnsigned_22_23}},
+	{XXSEL, 0xfc000030, 0xf0000030, 0x0, // VSX Select XX4-form (xxsel XT,XA,XB,XC)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20, ap_VecSReg_28_28_21_25}},
+	{XXSLDWI, 0xfc0004f8, 0xf0000010, 0x0, // VSX Shift Left Double by Word Immediate XX3-form (xxsldwi XT,XA,XB,SHW)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_29_29_11_15, ap_VecSReg_30_30_16_20, ap_ImmUnsigned_22_23}},
+	{XXSPLTW, 0xfc0007fc, 0xf0000290, 0x1c0000, // VSX Splat Word XX2-form (xxspltw XT,XB,UIM)
+		[5]*argField{ap_VecSReg_31_31_6_10, ap_VecSReg_30_30_16_20, ap_ImmUnsigned_14_15}},
+	{BRINC, 0xfc0007ff, 0x1000020f, 0x0, // Bit Reversed Increment EVX-form (brinc RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVABS, 0xfc0007ff, 0x10000208, 0xf800, // Vector Absolute Value EVX-form (evabs RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVADDIW, 0xfc0007ff, 0x10000202, 0x0, // Vector Add Immediate Word EVX-form (evaddiw RT,RB,UI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20, ap_ImmUnsigned_11_15}},
+	{EVADDSMIAAW, 0xfc0007ff, 0x100004c9, 0xf800, // Vector Add Signed, Modulo, Integer to Accumulator Word EVX-form (evaddsmiaaw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVADDSSIAAW, 0xfc0007ff, 0x100004c1, 0xf800, // Vector Add Signed, Saturate, Integer to Accumulator Word EVX-form (evaddssiaaw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVADDUMIAAW, 0xfc0007ff, 0x100004c8, 0xf800, // Vector Add Unsigned, Modulo, Integer to Accumulator Word EVX-form (evaddumiaaw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVADDUSIAAW, 0xfc0007ff, 0x100004c0, 0xf800, // Vector Add Unsigned, Saturate, Integer to Accumulator Word EVX-form (evaddusiaaw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVADDW, 0xfc0007ff, 0x10000200, 0x0, // Vector Add Word EVX-form (evaddw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVAND, 0xfc0007ff, 0x10000211, 0x0, // Vector AND EVX-form (evand RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVCMPEQ, 0xfc0007ff, 0x10000234, 0x600000, // Vector Compare Equal EVX-form (evcmpeq BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVANDC, 0xfc0007ff, 0x10000212, 0x0, // Vector AND with Complement EVX-form (evandc RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVCMPGTS, 0xfc0007ff, 0x10000231, 0x600000, // Vector Compare Greater Than Signed EVX-form (evcmpgts BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVCMPGTU, 0xfc0007ff, 0x10000230, 0x600000, // Vector Compare Greater Than Unsigned EVX-form (evcmpgtu BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVCMPLTU, 0xfc0007ff, 0x10000232, 0x600000, // Vector Compare Less Than Unsigned EVX-form (evcmpltu BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVCMPLTS, 0xfc0007ff, 0x10000233, 0x600000, // Vector Compare Less Than Signed EVX-form (evcmplts BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVCNTLSW, 0xfc0007ff, 0x1000020e, 0xf800, // Vector Count Leading Signed Bits Word EVX-form (evcntlsw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVCNTLZW, 0xfc0007ff, 0x1000020d, 0xf800, // Vector Count Leading Zeros Word EVX-form (evcntlzw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVDIVWS, 0xfc0007ff, 0x100004c6, 0x0, // Vector Divide Word Signed EVX-form (evdivws RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVDIVWU, 0xfc0007ff, 0x100004c7, 0x0, // Vector Divide Word Unsigned EVX-form (evdivwu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVEQV, 0xfc0007ff, 0x10000219, 0x0, // Vector Equivalent EVX-form (eveqv RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVEXTSB, 0xfc0007ff, 0x1000020a, 0xf800, // Vector Extend Sign Byte EVX-form (evextsb RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVEXTSH, 0xfc0007ff, 0x1000020b, 0xf800, // Vector Extend Sign Halfword EVX-form (evextsh RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVLDD, 0xfc0007ff, 0x10000301, 0x0, // Vector Load Double Word into Double Word EVX-form (evldd RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVLDH, 0xfc0007ff, 0x10000305, 0x0, // Vector Load Double into Four Halfwords EVX-form (evldh RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVLDDX, 0xfc0007ff, 0x10000300, 0x0, // Vector Load Double Word into Double Word Indexed EVX-form (evlddx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLDHX, 0xfc0007ff, 0x10000304, 0x0, // Vector Load Double into Four Halfwords Indexed EVX-form (evldhx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLDW, 0xfc0007ff, 0x10000303, 0x0, // Vector Load Double into Two Words EVX-form (evldw RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVLHHESPLAT, 0xfc0007ff, 0x10000309, 0x0, // Vector Load Halfword into Halfwords Even and Splat EVX-form (evlhhesplat RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVLDWX, 0xfc0007ff, 0x10000302, 0x0, // Vector Load Double into Two Words Indexed EVX-form (evldwx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLHHESPLATX, 0xfc0007ff, 0x10000308, 0x0, // Vector Load Halfword into Halfwords Even and Splat Indexed EVX-form (evlhhesplatx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLHHOSSPLAT, 0xfc0007ff, 0x1000030f, 0x0, // Vector Load Halfword into Halfword Odd Signed and Splat EVX-form (evlhhossplat RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVLHHOUSPLAT, 0xfc0007ff, 0x1000030d, 0x0, // Vector Load Halfword into Halfword Odd Unsigned and Splat EVX-form (evlhhousplat RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVLHHOSSPLATX, 0xfc0007ff, 0x1000030e, 0x0, // Vector Load Halfword into Halfword Odd Signed and Splat Indexed EVX-form (evlhhossplatx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLHHOUSPLATX, 0xfc0007ff, 0x1000030c, 0x0, // Vector Load Halfword into Halfword Odd Unsigned and Splat Indexed EVX-form (evlhhousplatx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLWHE, 0xfc0007ff, 0x10000311, 0x0, // Vector Load Word into Two Halfwords Even EVX-form (evlwhe RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVLWHOS, 0xfc0007ff, 0x10000317, 0x0, // Vector Load Word into Two Halfwords Odd Signed (with sign extension) EVX-form (evlwhos RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVLWHEX, 0xfc0007ff, 0x10000310, 0x0, // Vector Load Word into Two Halfwords Even Indexed EVX-form (evlwhex RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLWHOSX, 0xfc0007ff, 0x10000316, 0x0, // Vector Load Word into Two Halfwords Odd Signed Indexed (with sign extension) EVX-form (evlwhosx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLWHOU, 0xfc0007ff, 0x10000315, 0x0, // Vector Load Word into Two Halfwords Odd Unsigned (zero-extended) EVX-form (evlwhou RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVLWHSPLAT, 0xfc0007ff, 0x1000031d, 0x0, // Vector Load Word into Two Halfwords and Splat EVX-form (evlwhsplat RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVLWHOUX, 0xfc0007ff, 0x10000314, 0x0, // Vector Load Word into Two Halfwords Odd Unsigned Indexed (zero-extended) EVX-form (evlwhoux RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLWHSPLATX, 0xfc0007ff, 0x1000031c, 0x0, // Vector Load Word into Two Halfwords and Splat Indexed EVX-form (evlwhsplatx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLWWSPLAT, 0xfc0007ff, 0x10000319, 0x0, // Vector Load Word into Word and Splat EVX-form (evlwwsplat RT,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVMERGEHI, 0xfc0007ff, 0x1000022c, 0x0, // Vector Merge High EVX-form (evmergehi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLWWSPLATX, 0xfc0007ff, 0x10000318, 0x0, // Vector Load Word into Word and Splat Indexed EVX-form (evlwwsplatx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMERGELO, 0xfc0007ff, 0x1000022d, 0x0, // Vector Merge Low EVX-form (evmergelo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMERGEHILO, 0xfc0007ff, 0x1000022e, 0x0, // Vector Merge High/Low EVX-form (evmergehilo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEGSMFAA, 0xfc0007ff, 0x1000052b, 0x0, // Vector Multiply Halfwords, Even, Guarded, Signed, Modulo, Fractional and Accumulate EVX-form (evmhegsmfaa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMERGELOHI, 0xfc0007ff, 0x1000022f, 0x0, // Vector Merge Low/High EVX-form (evmergelohi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEGSMFAN, 0xfc0007ff, 0x100005ab, 0x0, // Vector Multiply Halfwords, Even, Guarded, Signed, Modulo, Fractional and Accumulate Negative EVX-form (evmhegsmfan RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEGSMIAA, 0xfc0007ff, 0x10000529, 0x0, // Vector Multiply Halfwords, Even, Guarded, Signed, Modulo, Integer and Accumulate EVX-form (evmhegsmiaa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEGUMIAA, 0xfc0007ff, 0x10000528, 0x0, // Vector Multiply Halfwords, Even, Guarded, Unsigned, Modulo, Integer and Accumulate EVX-form (evmhegumiaa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEGSMIAN, 0xfc0007ff, 0x100005a9, 0x0, // Vector Multiply Halfwords, Even, Guarded, Signed, Modulo, Integer and Accumulate Negative EVX-form (evmhegsmian RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEGUMIAN, 0xfc0007ff, 0x100005a8, 0x0, // Vector Multiply Halfwords, Even, Guarded, Unsigned, Modulo, Integer and Accumulate Negative EVX-form (evmhegumian RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESMF, 0xfc0007ff, 0x1000040b, 0x0, // Vector Multiply Halfwords, Even, Signed, Modulo, Fractional EVX-form (evmhesmf RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESMFAAW, 0xfc0007ff, 0x1000050b, 0x0, // Vector Multiply Halfwords, Even, Signed, Modulo, Fractional and Accumulate into Words EVX-form (evmhesmfaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESMFA, 0xfc0007ff, 0x1000042b, 0x0, // Vector Multiply Halfwords, Even, Signed, Modulo, Fractional to Accumulator EVX-form (evmhesmfa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESMFANW, 0xfc0007ff, 0x1000058b, 0x0, // Vector Multiply Halfwords, Even, Signed, Modulo, Fractional and Accumulate Negative into Words EVX-form (evmhesmfanw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESMI, 0xfc0007ff, 0x10000409, 0x0, // Vector Multiply Halfwords, Even, Signed, Modulo, Integer EVX-form (evmhesmi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESMIAAW, 0xfc0007ff, 0x10000509, 0x0, // Vector Multiply Halfwords, Even, Signed, Modulo, Integer and Accumulate into Words EVX-form (evmhesmiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESMIA, 0xfc0007ff, 0x10000429, 0x0, // Vector Multiply Halfwords, Even, Signed, Modulo, Integer to Accumulator EVX-form (evmhesmia RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESMIANW, 0xfc0007ff, 0x10000589, 0x0, // Vector Multiply Halfwords, Even, Signed, Modulo, Integer and Accumulate Negative into Words EVX-form (evmhesmianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESSF, 0xfc0007ff, 0x10000403, 0x0, // Vector Multiply Halfwords, Even, Signed, Saturate, Fractional EVX-form (evmhessf RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESSFA, 0xfc0007ff, 0x10000423, 0x0, // Vector Multiply Halfwords, Even, Signed, Saturate, Fractional to Accumulator EVX-form (evmhessfa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESSFAAW, 0xfc0007ff, 0x10000503, 0x0, // Vector Multiply Halfwords, Even, Signed, Saturate, Fractional and Accumulate into Words EVX-form (evmhessfaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESSFANW, 0xfc0007ff, 0x10000583, 0x0, // Vector Multiply Halfwords, Even, Signed, Saturate, Fractional and Accumulate Negative into Words EVX-form (evmhessfanw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESSIAAW, 0xfc0007ff, 0x10000501, 0x0, // Vector Multiply Halfwords, Even, Signed, Saturate, Integer and Accumulate into Words EVX-form (evmhessiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHESSIANW, 0xfc0007ff, 0x10000581, 0x0, // Vector Multiply Halfwords, Even, Signed, Saturate, Integer and Accumulate Negative into Words EVX-form (evmhessianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEUMI, 0xfc0007ff, 0x10000408, 0x0, // Vector Multiply Halfwords, Even, Unsigned, Modulo, Integer EVX-form (evmheumi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEUMIAAW, 0xfc0007ff, 0x10000508, 0x0, // Vector Multiply Halfwords, Even, Unsigned, Modulo, Integer and Accumulate into Words EVX-form (evmheumiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEUMIA, 0xfc0007ff, 0x10000428, 0x0, // Vector Multiply Halfwords, Even, Unsigned, Modulo, Integer to Accumulator EVX-form (evmheumia RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEUMIANW, 0xfc0007ff, 0x10000588, 0x0, // Vector Multiply Halfwords, Even, Unsigned, Modulo, Integer and Accumulate Negative into Words EVX-form (evmheumianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEUSIAAW, 0xfc0007ff, 0x10000500, 0x0, // Vector Multiply Halfwords, Even, Unsigned, Saturate, Integer and Accumulate into Words EVX-form (evmheusiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHEUSIANW, 0xfc0007ff, 0x10000580, 0x0, // Vector Multiply Halfwords, Even, Unsigned, Saturate, Integer and Accumulate Negative into Words EVX-form (evmheusianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOGSMFAA, 0xfc0007ff, 0x1000052f, 0x0, // Vector Multiply Halfwords, Odd, Guarded, Signed, Modulo, Fractional and Accumulate EVX-form (evmhogsmfaa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOGSMIAA, 0xfc0007ff, 0x1000052d, 0x0, // Vector Multiply Halfwords, Odd, Guarded, Signed, Modulo, Integer and Accumulate EVX-form (evmhogsmiaa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOGSMFAN, 0xfc0007ff, 0x100005af, 0x0, // Vector Multiply Halfwords, Odd, Guarded, Signed, Modulo, Fractional and Accumulate Negative EVX-form (evmhogsmfan RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOGSMIAN, 0xfc0007ff, 0x100005ad, 0x0, // Vector Multiply Halfwords, Odd, Guarded, Signed, Modulo, Integer and Accumulate Negative EVX-form (evmhogsmian RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOGUMIAA, 0xfc0007ff, 0x1000052c, 0x0, // Vector Multiply Halfwords, Odd, Guarded, Unsigned, Modulo, Integer and Accumulate EVX-form (evmhogumiaa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSMF, 0xfc0007ff, 0x1000040f, 0x0, // Vector Multiply Halfwords, Odd, Signed, Modulo, Fractional EVX-form (evmhosmf RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOGUMIAN, 0xfc0007ff, 0x100005ac, 0x0, // Vector Multiply Halfwords, Odd, Guarded, Unsigned, Modulo, Integer and Accumulate Negative EVX-form (evmhogumian RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSMFA, 0xfc0007ff, 0x1000042f, 0x0, // Vector Multiply Halfwords, Odd, Signed, Modulo, Fractional to Accumulator EVX-form (evmhosmfa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSMFAAW, 0xfc0007ff, 0x1000050f, 0x0, // Vector Multiply Halfwords, Odd, Signed, Modulo, Fractional and Accumulate into Words EVX-form (evmhosmfaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSMI, 0xfc0007ff, 0x1000040d, 0x0, // Vector Multiply Halfwords, Odd, Signed, Modulo, Integer EVX-form (evmhosmi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSMFANW, 0xfc0007ff, 0x1000058f, 0x0, // Vector Multiply Halfwords, Odd, Signed, Modulo, Fractional and Accumulate Negative into Words EVX-form (evmhosmfanw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSMIA, 0xfc0007ff, 0x1000042d, 0x0, // Vector Multiply Halfwords, Odd, Signed, Modulo, Integer to Accumulator EVX-form (evmhosmia RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSMIAAW, 0xfc0007ff, 0x1000050d, 0x0, // Vector Multiply Halfwords, Odd, Signed, Modulo, Integer and Accumulate into Words EVX-form (evmhosmiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSMIANW, 0xfc0007ff, 0x1000058d, 0x0, // Vector Multiply Halfwords, Odd, Signed, Modulo, Integer and Accumulate Negative into Words EVX-form (evmhosmianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSSF, 0xfc0007ff, 0x10000407, 0x0, // Vector Multiply Halfwords, Odd, Signed, Saturate, Fractional EVX-form (evmhossf RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSSFA, 0xfc0007ff, 0x10000427, 0x0, // Vector Multiply Halfwords, Odd, Signed, Saturate, Fractional to Accumulator EVX-form (evmhossfa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSSFAAW, 0xfc0007ff, 0x10000507, 0x0, // Vector Multiply Halfwords, Odd, Signed, Saturate, Fractional and Accumulate into Words EVX-form (evmhossfaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSSFANW, 0xfc0007ff, 0x10000587, 0x0, // Vector Multiply Halfwords, Odd, Signed, Saturate, Fractional and Accumulate Negative into Words EVX-form (evmhossfanw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSSIAAW, 0xfc0007ff, 0x10000505, 0x0, // Vector Multiply Halfwords, Odd, Signed, Saturate, Integer and Accumulate into Words EVX-form (evmhossiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOUMI, 0xfc0007ff, 0x1000040c, 0x0, // Vector Multiply Halfwords, Odd, Unsigned, Modulo, Integer EVX-form (evmhoumi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOSSIANW, 0xfc0007ff, 0x10000585, 0x0, // Vector Multiply Halfwords, Odd, Signed, Saturate, Integer and Accumulate Negative into Words EVX-form (evmhossianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOUMIA, 0xfc0007ff, 0x1000042c, 0x0, // Vector Multiply Halfwords, Odd, Unsigned, Modulo, Integer to Accumulator EVX-form (evmhoumia RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOUMIAAW, 0xfc0007ff, 0x1000050c, 0x0, // Vector Multiply Halfwords, Odd, Unsigned, Modulo, Integer and Accumulate into Words EVX-form (evmhoumiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOUSIAAW, 0xfc0007ff, 0x10000504, 0x0, // Vector Multiply Halfwords, Odd, Unsigned, Saturate, Integer and Accumulate into Words EVX-form (evmhousiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOUMIANW, 0xfc0007ff, 0x1000058c, 0x0, // Vector Multiply Halfwords, Odd, Unsigned, Modulo, Integer and Accumulate Negative into Words EVX-form (evmhoumianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMHOUSIANW, 0xfc0007ff, 0x10000584, 0x0, // Vector Multiply Halfwords, Odd, Unsigned, Saturate, Integer and Accumulate Negative into Words EVX-form (evmhousianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMRA, 0xfc0007ff, 0x100004c4, 0xf800, // Initialize Accumulator EVX-form (evmra RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVMWHSMF, 0xfc0007ff, 0x1000044f, 0x0, // Vector Multiply Word High Signed, Modulo, Fractional EVX-form (evmwhsmf RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWHSMI, 0xfc0007ff, 0x1000044d, 0x0, // Vector Multiply Word High Signed, Modulo, Integer EVX-form (evmwhsmi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWHSMFA, 0xfc0007ff, 0x1000046f, 0x0, // Vector Multiply Word High Signed, Modulo, Fractional to Accumulator EVX-form (evmwhsmfa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWHSMIA, 0xfc0007ff, 0x1000046d, 0x0, // Vector Multiply Word High Signed, Modulo, Integer to Accumulator EVX-form (evmwhsmia RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWHSSF, 0xfc0007ff, 0x10000447, 0x0, // Vector Multiply Word High Signed, Saturate, Fractional EVX-form (evmwhssf RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWHUMI, 0xfc0007ff, 0x1000044c, 0x0, // Vector Multiply Word High Unsigned, Modulo, Integer EVX-form (evmwhumi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWHSSFA, 0xfc0007ff, 0x10000467, 0x0, // Vector Multiply Word High Signed, Saturate, Fractional to Accumulator EVX-form (evmwhssfa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWHUMIA, 0xfc0007ff, 0x1000046c, 0x0, // Vector Multiply Word High Unsigned, Modulo, Integer to Accumulator EVX-form (evmwhumia RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWLSMIAAW, 0xfc0007ff, 0x10000549, 0x0, // Vector Multiply Word Low Signed, Modulo, Integer and Accumulate into Words EVX-form (evmwlsmiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWLSSIAAW, 0xfc0007ff, 0x10000541, 0x0, // Vector Multiply Word Low Signed, Saturate, Integer and Accumulate into Words EVX-form (evmwlssiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWLSMIANW, 0xfc0007ff, 0x100005c9, 0x0, // Vector Multiply Word Low Signed, Modulo, Integer and Accumulate Negative in Words EVX-form (evmwlsmianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWLSSIANW, 0xfc0007ff, 0x100005c1, 0x0, // Vector Multiply Word Low Signed, Saturate, Integer and Accumulate Negative in Words EVX-form (evmwlssianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWLUMI, 0xfc0007ff, 0x10000448, 0x0, // Vector Multiply Word Low Unsigned, Modulo, Integer EVX-form (evmwlumi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWLUMIAAW, 0xfc0007ff, 0x10000548, 0x0, // Vector Multiply Word Low Unsigned, Modulo, Integer and Accumulate into Words EVX-form (evmwlumiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWLUMIA, 0xfc0007ff, 0x10000468, 0x0, // Vector Multiply Word Low Unsigned, Modulo, Integer to Accumulator EVX-form (evmwlumia RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWLUMIANW, 0xfc0007ff, 0x100005c8, 0x0, // Vector Multiply Word Low Unsigned, Modulo, Integer and Accumulate Negative in Words EVX-form (evmwlumianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWLUSIAAW, 0xfc0007ff, 0x10000540, 0x0, // Vector Multiply Word Low Unsigned, Saturate, Integer and Accumulate into Words EVX-form (evmwlusiaaw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSMF, 0xfc0007ff, 0x1000045b, 0x0, // Vector Multiply Word Signed, Modulo, Fractional EVX-form (evmwsmf RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWLUSIANW, 0xfc0007ff, 0x100005c0, 0x0, // Vector Multiply Word Low Unsigned, Saturate, Integer and Accumulate Negative in Words EVX-form (evmwlusianw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSMFA, 0xfc0007ff, 0x1000047b, 0x0, // Vector Multiply Word Signed, Modulo, Fractional to Accumulator EVX-form (evmwsmfa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSMFAA, 0xfc0007ff, 0x1000055b, 0x0, // Vector Multiply Word Signed, Modulo, Fractional and Accumulate EVX-form (evmwsmfaa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSMI, 0xfc0007ff, 0x10000459, 0x0, // Vector Multiply Word Signed, Modulo, Integer EVX-form (evmwsmi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSMIAA, 0xfc0007ff, 0x10000559, 0x0, // Vector Multiply Word Signed, Modulo, Integer and Accumulate EVX-form (evmwsmiaa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSMFAN, 0xfc0007ff, 0x100005db, 0x0, // Vector Multiply Word Signed, Modulo, Fractional and Accumulate Negative EVX-form (evmwsmfan RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSMIA, 0xfc0007ff, 0x10000479, 0x0, // Vector Multiply Word Signed, Modulo, Integer to Accumulator EVX-form (evmwsmia RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSMIAN, 0xfc0007ff, 0x100005d9, 0x0, // Vector Multiply Word Signed, Modulo, Integer and Accumulate Negative EVX-form (evmwsmian RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSSF, 0xfc0007ff, 0x10000453, 0x0, // Vector Multiply Word Signed, Saturate, Fractional EVX-form (evmwssf RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSSFA, 0xfc0007ff, 0x10000473, 0x0, // Vector Multiply Word Signed, Saturate, Fractional to Accumulator EVX-form (evmwssfa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSSFAA, 0xfc0007ff, 0x10000553, 0x0, // Vector Multiply Word Signed, Saturate, Fractional and Accumulate EVX-form (evmwssfaa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWUMI, 0xfc0007ff, 0x10000458, 0x0, // Vector Multiply Word Unsigned, Modulo, Integer EVX-form (evmwumi RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWSSFAN, 0xfc0007ff, 0x100005d3, 0x0, // Vector Multiply Word Signed, Saturate, Fractional and Accumulate Negative EVX-form (evmwssfan RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWUMIA, 0xfc0007ff, 0x10000478, 0x0, // Vector Multiply Word Unsigned, Modulo, Integer to Accumulator EVX-form (evmwumia RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWUMIAA, 0xfc0007ff, 0x10000558, 0x0, // Vector Multiply Word Unsigned, Modulo, Integer and Accumulate EVX-form (evmwumiaa RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVNAND, 0xfc0007ff, 0x1000021e, 0x0, // Vector NAND EVX-form (evnand RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVMWUMIAN, 0xfc0007ff, 0x100005d8, 0x0, // Vector Multiply Word Unsigned, Modulo, Integer and Accumulate Negative EVX-form (evmwumian RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVNEG, 0xfc0007ff, 0x10000209, 0xf800, // Vector Negate EVX-form (evneg RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVNOR, 0xfc0007ff, 0x10000218, 0x0, // Vector NOR EVX-form (evnor RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVORC, 0xfc0007ff, 0x1000021b, 0x0, // Vector OR with Complement EVX-form (evorc RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVOR, 0xfc0007ff, 0x10000217, 0x0, // Vector OR EVX-form (evor RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVRLW, 0xfc0007ff, 0x10000228, 0x0, // Vector Rotate Left Word EVX-form (evrlw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVRLWI, 0xfc0007ff, 0x1000022a, 0x0, // Vector Rotate Left Word Immediate EVX-form (evrlwi RT,RA,UI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmUnsigned_16_20}},
+	{EVSEL, 0xfc0007f8, 0x10000278, 0x0, // Vector Select EVS-form (evsel RT,RA,RB,BFA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20, ap_CondRegField_29_31}},
+	{EVRNDW, 0xfc0007ff, 0x1000020c, 0xf800, // Vector Round Word EVX-form (evrndw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVSLW, 0xfc0007ff, 0x10000224, 0x0, // Vector Shift Left Word EVX-form (evslw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSPLATFI, 0xfc0007ff, 0x1000022b, 0xf800, // Vector Splat Fractional Immediate EVX-form (evsplatfi RT,SI)
+		[5]*argField{ap_Reg_6_10, ap_ImmSigned_11_15}},
+	{EVSRWIS, 0xfc0007ff, 0x10000223, 0x0, // Vector Shift Right Word Immediate Signed EVX-form (evsrwis RT,RA,UI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmUnsigned_16_20}},
+	{EVSLWI, 0xfc0007ff, 0x10000226, 0x0, // Vector Shift Left Word Immediate EVX-form (evslwi RT,RA,UI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmUnsigned_16_20}},
+	{EVSPLATI, 0xfc0007ff, 0x10000229, 0xf800, // Vector Splat Immediate EVX-form (evsplati RT,SI)
+		[5]*argField{ap_Reg_6_10, ap_ImmSigned_11_15}},
+	{EVSRWIU, 0xfc0007ff, 0x10000222, 0x0, // Vector Shift Right Word Immediate Unsigned EVX-form (evsrwiu RT,RA,UI)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_ImmUnsigned_16_20}},
+	{EVSRWS, 0xfc0007ff, 0x10000221, 0x0, // Vector Shift Right Word Signed EVX-form (evsrws RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSTDD, 0xfc0007ff, 0x10000321, 0x0, // Vector Store Double of Double EVX-form (evstdd RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVSRWU, 0xfc0007ff, 0x10000220, 0x0, // Vector Shift Right Word Unsigned EVX-form (evsrwu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSTDDX, 0xfc0007ff, 0x10000320, 0x0, // Vector Store Double of Double Indexed EVX-form (evstddx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSTDH, 0xfc0007ff, 0x10000325, 0x0, // Vector Store Double of Four Halfwords EVX-form (evstdh RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVSTDW, 0xfc0007ff, 0x10000323, 0x0, // Vector Store Double of Two Words EVX-form (evstdw RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVSTDHX, 0xfc0007ff, 0x10000324, 0x0, // Vector Store Double of Four Halfwords Indexed EVX-form (evstdhx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSTDWX, 0xfc0007ff, 0x10000322, 0x0, // Vector Store Double of Two Words Indexed EVX-form (evstdwx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSTWHE, 0xfc0007ff, 0x10000331, 0x0, // Vector Store Word of Two Halfwords from Even EVX-form (evstwhe RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVSTWHO, 0xfc0007ff, 0x10000335, 0x0, // Vector Store Word of Two Halfwords from Odd EVX-form (evstwho RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVSTWWE, 0xfc0007ff, 0x10000339, 0x0, // Vector Store Word of Word from Even EVX-form (evstwwe RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVSTWHEX, 0xfc0007ff, 0x10000330, 0x0, // Vector Store Word of Two Halfwords from Even Indexed EVX-form (evstwhex RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSTWHOX, 0xfc0007ff, 0x10000334, 0x0, // Vector Store Word of Two Halfwords from Odd Indexed EVX-form (evstwhox RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSTWWEX, 0xfc0007ff, 0x10000338, 0x0, // Vector Store Word of Word from Even Indexed EVX-form (evstwwex RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSTWWO, 0xfc0007ff, 0x1000033d, 0x0, // Vector Store Word of Word from Odd EVX-form (evstwwo RS,D(RA))
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_16_20, ap_Reg_11_15}},
+	{EVSUBFSMIAAW, 0xfc0007ff, 0x100004cb, 0xf800, // Vector Subtract Signed, Modulo, Integer to Accumulator Word EVX-form (evsubfsmiaaw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVSTWWOX, 0xfc0007ff, 0x1000033c, 0x0, // Vector Store Word of Word from Odd Indexed EVX-form (evstwwox RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSUBFSSIAAW, 0xfc0007ff, 0x100004c3, 0xf800, // Vector Subtract Signed, Saturate, Integer to Accumulator Word EVX-form (evsubfssiaaw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVSUBFUMIAAW, 0xfc0007ff, 0x100004ca, 0xf800, // Vector Subtract Unsigned, Modulo, Integer to Accumulator Word EVX-form (evsubfumiaaw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVSUBFUSIAAW, 0xfc0007ff, 0x100004c2, 0xf800, // Vector Subtract Unsigned, Saturate, Integer to Accumulator Word EVX-form (evsubfusiaaw RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVSUBFW, 0xfc0007ff, 0x10000204, 0x0, // Vector Subtract from Word EVX-form (evsubfw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSUBIFW, 0xfc0007ff, 0x10000206, 0x0, // Vector Subtract Immediate from Word EVX-form (evsubifw RT,UI,RB)
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_11_15, ap_Reg_16_20}},
+	{EVXOR, 0xfc0007ff, 0x10000216, 0x0, // Vector XOR EVX-form (evxor RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSABS, 0xfc0007ff, 0x10000284, 0xf800, // Vector Floating-Point Single-Precision Absolute Value EVX-form (evfsabs RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVFSNABS, 0xfc0007ff, 0x10000285, 0xf800, // Vector Floating-Point Single-Precision Negative Absolute Value EVX-form (evfsnabs RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVFSNEG, 0xfc0007ff, 0x10000286, 0xf800, // Vector Floating-Point Single-Precision Negate EVX-form (evfsneg RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EVFSADD, 0xfc0007ff, 0x10000280, 0x0, // Vector Floating-Point Single-Precision Add EVX-form (evfsadd RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSMUL, 0xfc0007ff, 0x10000288, 0x0, // Vector Floating-Point Single-Precision Multiply EVX-form (evfsmul RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSSUB, 0xfc0007ff, 0x10000281, 0x0, // Vector Floating-Point Single-Precision Subtract EVX-form (evfssub RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSDIV, 0xfc0007ff, 0x10000289, 0x0, // Vector Floating-Point Single-Precision Divide EVX-form (evfsdiv RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSCMPGT, 0xfc0007ff, 0x1000028c, 0x600000, // Vector Floating-Point Single-Precision Compare Greater Than EVX-form (evfscmpgt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSCMPLT, 0xfc0007ff, 0x1000028d, 0x600000, // Vector Floating-Point Single-Precision Compare Less Than EVX-form (evfscmplt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSCMPEQ, 0xfc0007ff, 0x1000028e, 0x600000, // Vector Floating-Point Single-Precision Compare Equal EVX-form (evfscmpeq BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSTSTGT, 0xfc0007ff, 0x1000029c, 0x600000, // Vector Floating-Point Single-Precision Test Greater Than EVX-form (evfststgt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSTSTLT, 0xfc0007ff, 0x1000029d, 0x600000, // Vector Floating-Point Single-Precision Test Less Than EVX-form (evfststlt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSTSTEQ, 0xfc0007ff, 0x1000029e, 0x600000, // Vector Floating-Point Single-Precision Test Equal EVX-form (evfststeq BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVFSCFSI, 0xfc0007ff, 0x10000291, 0x1f0000, // Vector Convert Floating-Point Single-Precision from Signed Integer EVX-form (evfscfsi RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EVFSCFSF, 0xfc0007ff, 0x10000293, 0x1f0000, // Vector Convert Floating-Point Single-Precision from Signed Fraction EVX-form (evfscfsf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EVFSCFUI, 0xfc0007ff, 0x10000290, 0x1f0000, // Vector Convert Floating-Point Single-Precision from Unsigned Integer EVX-form (evfscfui RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EVFSCFUF, 0xfc0007ff, 0x10000292, 0x1f0000, // Vector Convert Floating-Point Single-Precision from Unsigned Fraction EVX-form (evfscfuf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EVFSCTSI, 0xfc0007ff, 0x10000295, 0x1f0000, // Vector Convert Floating-Point Single-Precision to Signed Integer EVX-form (evfsctsi RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EVFSCTUI, 0xfc0007ff, 0x10000294, 0x1f0000, // Vector Convert Floating-Point Single-Precision to Unsigned Integer EVX-form (evfsctui RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EVFSCTSIZ, 0xfc0007ff, 0x1000029a, 0x1f0000, // Vector Convert Floating-Point Single-Precision to Signed Integer with Round toward Zero EVX-form (evfsctsiz RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EVFSCTUIZ, 0xfc0007ff, 0x10000298, 0x1f0000, // Vector Convert Floating-Point Single-Precision to Unsigned Integer with Round toward Zero EVX-form (evfsctuiz RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EVFSCTSF, 0xfc0007ff, 0x10000297, 0x1f0000, // Vector Convert Floating-Point Single-Precision to Signed Fraction EVX-form (evfsctsf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EVFSCTUF, 0xfc0007ff, 0x10000296, 0x1f0000, // Vector Convert Floating-Point Single-Precision to Unsigned Fraction EVX-form (evfsctuf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSABS, 0xfc0007ff, 0x100002c4, 0xf800, // Floating-Point Single-Precision Absolute Value EVX-form (efsabs RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EFSNEG, 0xfc0007ff, 0x100002c6, 0xf800, // Floating-Point Single-Precision Negate EVX-form (efsneg RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EFSNABS, 0xfc0007ff, 0x100002c5, 0xf800, // Floating-Point Single-Precision Negative Absolute Value EVX-form (efsnabs RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EFSADD, 0xfc0007ff, 0x100002c0, 0x0, // Floating-Point Single-Precision Add EVX-form (efsadd RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFSMUL, 0xfc0007ff, 0x100002c8, 0x0, // Floating-Point Single-Precision Multiply EVX-form (efsmul RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFSSUB, 0xfc0007ff, 0x100002c1, 0x0, // Floating-Point Single-Precision Subtract EVX-form (efssub RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFSDIV, 0xfc0007ff, 0x100002c9, 0x0, // Floating-Point Single-Precision Divide EVX-form (efsdiv RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFSCMPGT, 0xfc0007ff, 0x100002cc, 0x600000, // Floating-Point Single-Precision Compare Greater Than EVX-form (efscmpgt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFSCMPLT, 0xfc0007ff, 0x100002cd, 0x600000, // Floating-Point Single-Precision Compare Less Than EVX-form (efscmplt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFSCMPEQ, 0xfc0007ff, 0x100002ce, 0x600000, // Floating-Point Single-Precision Compare Equal EVX-form (efscmpeq BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFSTSTGT, 0xfc0007ff, 0x100002dc, 0x600000, // Floating-Point Single-Precision Test Greater Than EVX-form (efststgt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFSTSTLT, 0xfc0007ff, 0x100002dd, 0x600000, // Floating-Point Single-Precision Test Less Than EVX-form (efststlt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFSTSTEQ, 0xfc0007ff, 0x100002de, 0x600000, // Floating-Point Single-Precision Test Equal EVX-form (efststeq BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFSCFSI, 0xfc0007ff, 0x100002d1, 0x1f0000, // Convert Floating-Point Single-Precision from Signed Integer EVX-form (efscfsi RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSCFSF, 0xfc0007ff, 0x100002d3, 0x1f0000, // Convert Floating-Point Single-Precision from Signed Fraction EVX-form (efscfsf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSCTSI, 0xfc0007ff, 0x100002d5, 0x1f0000, // Convert Floating-Point Single-Precision to Signed Integer EVX-form (efsctsi RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSCFUI, 0xfc0007ff, 0x100002d0, 0x1f0000, // Convert Floating-Point Single-Precision from Unsigned Integer EVX-form (efscfui RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSCFUF, 0xfc0007ff, 0x100002d2, 0x1f0000, // Convert Floating-Point Single-Precision from Unsigned Fraction EVX-form (efscfuf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSCTUI, 0xfc0007ff, 0x100002d4, 0x1f0000, // Convert Floating-Point Single-Precision to Unsigned Integer EVX-form (efsctui RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSCTSIZ, 0xfc0007ff, 0x100002da, 0x1f0000, // Convert Floating-Point Single-Precision to Signed Integer with Round toward Zero EVX-form (efsctsiz RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSCTSF, 0xfc0007ff, 0x100002d7, 0x1f0000, // Convert Floating-Point Single-Precision to Signed Fraction EVX-form (efsctsf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSCTUIZ, 0xfc0007ff, 0x100002d8, 0x1f0000, // Convert Floating-Point Single-Precision to Unsigned Integer with Round toward Zero EVX-form (efsctuiz RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSCTUF, 0xfc0007ff, 0x100002d6, 0x1f0000, // Convert Floating-Point Single-Precision to Unsigned Fraction EVX-form (efsctuf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDABS, 0xfc0007ff, 0x100002e4, 0xf800, // Floating-Point Double-Precision Absolute Value EVX-form (efdabs RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EFDNEG, 0xfc0007ff, 0x100002e6, 0xf800, // Floating-Point Double-Precision Negate EVX-form (efdneg RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EFDNABS, 0xfc0007ff, 0x100002e5, 0xf800, // Floating-Point Double-Precision Negative Absolute Value EVX-form (efdnabs RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{EFDADD, 0xfc0007ff, 0x100002e0, 0x0, // Floating-Point Double-Precision Add EVX-form (efdadd RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFDMUL, 0xfc0007ff, 0x100002e8, 0x0, // Floating-Point Double-Precision Multiply EVX-form (efdmul RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFDSUB, 0xfc0007ff, 0x100002e1, 0x0, // Floating-Point Double-Precision Subtract EVX-form (efdsub RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFDDIV, 0xfc0007ff, 0x100002e9, 0x0, // Floating-Point Double-Precision Divide EVX-form (efddiv RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFDCMPGT, 0xfc0007ff, 0x100002ec, 0x600000, // Floating-Point Double-Precision Compare Greater Than EVX-form (efdcmpgt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFDCMPEQ, 0xfc0007ff, 0x100002ee, 0x600000, // Floating-Point Double-Precision Compare Equal EVX-form (efdcmpeq BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFDCMPLT, 0xfc0007ff, 0x100002ed, 0x600000, // Floating-Point Double-Precision Compare Less Than EVX-form (efdcmplt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFDTSTGT, 0xfc0007ff, 0x100002fc, 0x600000, // Floating-Point Double-Precision Test Greater Than EVX-form (efdtstgt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFDTSTLT, 0xfc0007ff, 0x100002fd, 0x600000, // Floating-Point Double-Precision Test Less Than EVX-form (efdtstlt BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFDCFSI, 0xfc0007ff, 0x100002f1, 0x1f0000, // Convert Floating-Point Double-Precision from Signed Integer EVX-form (efdcfsi RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDTSTEQ, 0xfc0007ff, 0x100002fe, 0x600000, // Floating-Point Double-Precision Test Equal EVX-form (efdtsteq BF,RA,RB)
+		[5]*argField{ap_CondRegField_6_8, ap_Reg_11_15, ap_Reg_16_20}},
+	{EFDCFUI, 0xfc0007ff, 0x100002f0, 0x1f0000, // Convert Floating-Point Double-Precision from Unsigned Integer EVX-form (efdcfui RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCFSID, 0xfc0007ff, 0x100002e3, 0x1f0000, // Convert Floating-Point Double-Precision from Signed Integer Doubleword EVX-form (efdcfsid RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCFSF, 0xfc0007ff, 0x100002f3, 0x1f0000, // Convert Floating-Point Double-Precision from Signed Fraction EVX-form (efdcfsf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCFUF, 0xfc0007ff, 0x100002f2, 0x1f0000, // Convert Floating-Point Double-Precision from Unsigned Fraction EVX-form (efdcfuf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCFUID, 0xfc0007ff, 0x100002e2, 0x1f0000, // Convert Floating-Point Double-Precision from Unsigned Integer Doubleword EVX-form (efdcfuid RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCTSI, 0xfc0007ff, 0x100002f5, 0x1f0000, // Convert Floating-Point Double-Precision to Signed Integer EVX-form (efdctsi RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCTUI, 0xfc0007ff, 0x100002f4, 0x1f0000, // Convert Floating-Point Double-Precision to Unsigned Integer EVX-form (efdctui RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCTSIDZ, 0xfc0007ff, 0x100002eb, 0x1f0000, // Convert Floating-Point Double-Precision to Signed Integer Doubleword with Round toward Zero EVX-form (efdctsidz RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCTUIDZ, 0xfc0007ff, 0x100002ea, 0x1f0000, // Convert Floating-Point Double-Precision to Unsigned Integer Doubleword with Round toward Zero EVX-form (efdctuidz RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCTSIZ, 0xfc0007ff, 0x100002fa, 0x1f0000, // Convert Floating-Point Double-Precision to Signed Integer with Round toward Zero EVX-form (efdctsiz RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCTSF, 0xfc0007ff, 0x100002f7, 0x1f0000, // Convert Floating-Point Double-Precision to Signed Fraction EVX-form (efdctsf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCTUF, 0xfc0007ff, 0x100002f6, 0x1f0000, // Convert Floating-Point Double-Precision to Unsigned Fraction EVX-form (efdctuf RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCTUIZ, 0xfc0007ff, 0x100002f8, 0x1f0000, // Convert Floating-Point Double-Precision to Unsigned Integer with Round toward Zero EVX-form (efdctuiz RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFDCFS, 0xfc0007ff, 0x100002ef, 0x1f0000, // Floating-Point Double-Precision Convert from Single-Precision EVX-form (efdcfs RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{EFSCFD, 0xfc0007ff, 0x100002cf, 0x1f0000, // Floating-Point Single-Precision Convert from Double-Precision EVX-form (efscfd RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{DLMZB, 0xfc0007ff, 0x7c00009c, 0x0, // Determine Leftmost Zero Byte X-form (dlmzb RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{DLMZB_, 0xfc0007ff, 0x7c00009d, 0x0, // Determine Leftmost Zero Byte X-form (dlmzb. RA,RS,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10, ap_Reg_16_20}},
+	{MACCHW, 0xfc0007ff, 0x10000158, 0x0, // Multiply Accumulate Cross Halfword to Word Modulo Signed XO-form (macchw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHW_, 0xfc0007ff, 0x10000159, 0x0, // Multiply Accumulate Cross Halfword to Word Modulo Signed XO-form (macchw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWO, 0xfc0007ff, 0x10000558, 0x0, // Multiply Accumulate Cross Halfword to Word Modulo Signed XO-form (macchwo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWO_, 0xfc0007ff, 0x10000559, 0x0, // Multiply Accumulate Cross Halfword to Word Modulo Signed XO-form (macchwo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWS, 0xfc0007ff, 0x100001d8, 0x0, // Multiply Accumulate Cross Halfword to Word Saturate Signed XO-form (macchws RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWS_, 0xfc0007ff, 0x100001d9, 0x0, // Multiply Accumulate Cross Halfword to Word Saturate Signed XO-form (macchws. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWSO, 0xfc0007ff, 0x100005d8, 0x0, // Multiply Accumulate Cross Halfword to Word Saturate Signed XO-form (macchwso RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWSO_, 0xfc0007ff, 0x100005d9, 0x0, // Multiply Accumulate Cross Halfword to Word Saturate Signed XO-form (macchwso. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWU, 0xfc0007ff, 0x10000118, 0x0, // Multiply Accumulate Cross Halfword to Word Modulo Unsigned XO-form (macchwu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWU_, 0xfc0007ff, 0x10000119, 0x0, // Multiply Accumulate Cross Halfword to Word Modulo Unsigned XO-form (macchwu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWUO, 0xfc0007ff, 0x10000518, 0x0, // Multiply Accumulate Cross Halfword to Word Modulo Unsigned XO-form (macchwuo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWUO_, 0xfc0007ff, 0x10000519, 0x0, // Multiply Accumulate Cross Halfword to Word Modulo Unsigned XO-form (macchwuo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWSU, 0xfc0007ff, 0x10000198, 0x0, // Multiply Accumulate Cross Halfword to Word Saturate Unsigned XO-form (macchwsu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWSU_, 0xfc0007ff, 0x10000199, 0x0, // Multiply Accumulate Cross Halfword to Word Saturate Unsigned XO-form (macchwsu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWSUO, 0xfc0007ff, 0x10000598, 0x0, // Multiply Accumulate Cross Halfword to Word Saturate Unsigned XO-form (macchwsuo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACCHWSUO_, 0xfc0007ff, 0x10000599, 0x0, // Multiply Accumulate Cross Halfword to Word Saturate Unsigned XO-form (macchwsuo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHW, 0xfc0007ff, 0x10000058, 0x0, // Multiply Accumulate High Halfword to Word Modulo Signed XO-form (machhw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHW_, 0xfc0007ff, 0x10000059, 0x0, // Multiply Accumulate High Halfword to Word Modulo Signed XO-form (machhw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWO, 0xfc0007ff, 0x10000458, 0x0, // Multiply Accumulate High Halfword to Word Modulo Signed XO-form (machhwo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWO_, 0xfc0007ff, 0x10000459, 0x0, // Multiply Accumulate High Halfword to Word Modulo Signed XO-form (machhwo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWS, 0xfc0007ff, 0x100000d8, 0x0, // Multiply Accumulate High Halfword to Word Saturate Signed XO-form (machhws RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWS_, 0xfc0007ff, 0x100000d9, 0x0, // Multiply Accumulate High Halfword to Word Saturate Signed XO-form (machhws. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWSO, 0xfc0007ff, 0x100004d8, 0x0, // Multiply Accumulate High Halfword to Word Saturate Signed XO-form (machhwso RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWSO_, 0xfc0007ff, 0x100004d9, 0x0, // Multiply Accumulate High Halfword to Word Saturate Signed XO-form (machhwso. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWU, 0xfc0007ff, 0x10000018, 0x0, // Multiply Accumulate High Halfword to Word Modulo Unsigned XO-form (machhwu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWU_, 0xfc0007ff, 0x10000019, 0x0, // Multiply Accumulate High Halfword to Word Modulo Unsigned XO-form (machhwu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWUO, 0xfc0007ff, 0x10000418, 0x0, // Multiply Accumulate High Halfword to Word Modulo Unsigned XO-form (machhwuo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWUO_, 0xfc0007ff, 0x10000419, 0x0, // Multiply Accumulate High Halfword to Word Modulo Unsigned XO-form (machhwuo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWSU, 0xfc0007ff, 0x10000098, 0x0, // Multiply Accumulate High Halfword to Word Saturate Unsigned XO-form (machhwsu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWSU_, 0xfc0007ff, 0x10000099, 0x0, // Multiply Accumulate High Halfword to Word Saturate Unsigned XO-form (machhwsu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWSUO, 0xfc0007ff, 0x10000498, 0x0, // Multiply Accumulate High Halfword to Word Saturate Unsigned XO-form (machhwsuo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACHHWSUO_, 0xfc0007ff, 0x10000499, 0x0, // Multiply Accumulate High Halfword to Word Saturate Unsigned XO-form (machhwsuo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHW, 0xfc0007ff, 0x10000358, 0x0, // Multiply Accumulate Low Halfword to Word Modulo Signed XO-form (maclhw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHW_, 0xfc0007ff, 0x10000359, 0x0, // Multiply Accumulate Low Halfword to Word Modulo Signed XO-form (maclhw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWO, 0xfc0007ff, 0x10000758, 0x0, // Multiply Accumulate Low Halfword to Word Modulo Signed XO-form (maclhwo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWO_, 0xfc0007ff, 0x10000759, 0x0, // Multiply Accumulate Low Halfword to Word Modulo Signed XO-form (maclhwo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWS, 0xfc0007ff, 0x100003d8, 0x0, // Multiply Accumulate Low Halfword to Word Saturate Signed XO-form (maclhws RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWS_, 0xfc0007ff, 0x100003d9, 0x0, // Multiply Accumulate Low Halfword to Word Saturate Signed XO-form (maclhws. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWSO, 0xfc0007ff, 0x100007d8, 0x0, // Multiply Accumulate Low Halfword to Word Saturate Signed XO-form (maclhwso RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWSO_, 0xfc0007ff, 0x100007d9, 0x0, // Multiply Accumulate Low Halfword to Word Saturate Signed XO-form (maclhwso. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWU, 0xfc0007ff, 0x10000318, 0x0, // Multiply Accumulate Low Halfword to Word Modulo Unsigned XO-form (maclhwu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWU_, 0xfc0007ff, 0x10000319, 0x0, // Multiply Accumulate Low Halfword to Word Modulo Unsigned XO-form (maclhwu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWUO, 0xfc0007ff, 0x10000718, 0x0, // Multiply Accumulate Low Halfword to Word Modulo Unsigned XO-form (maclhwuo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWUO_, 0xfc0007ff, 0x10000719, 0x0, // Multiply Accumulate Low Halfword to Word Modulo Unsigned XO-form (maclhwuo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULCHW, 0xfc0007ff, 0x10000150, 0x0, // Multiply Cross Halfword to Word Signed X-form (mulchw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULCHW_, 0xfc0007ff, 0x10000151, 0x0, // Multiply Cross Halfword to Word Signed X-form (mulchw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWSU, 0xfc0007ff, 0x10000398, 0x0, // Multiply Accumulate Low Halfword to Word Saturate Unsigned XO-form (maclhwsu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWSU_, 0xfc0007ff, 0x10000399, 0x0, // Multiply Accumulate Low Halfword to Word Saturate Unsigned XO-form (maclhwsu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWSUO, 0xfc0007ff, 0x10000798, 0x0, // Multiply Accumulate Low Halfword to Word Saturate Unsigned XO-form (maclhwsuo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MACLHWSUO_, 0xfc0007ff, 0x10000799, 0x0, // Multiply Accumulate Low Halfword to Word Saturate Unsigned XO-form (maclhwsuo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULCHWU, 0xfc0007ff, 0x10000110, 0x0, // Multiply Cross Halfword to Word Unsigned X-form (mulchwu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULCHWU_, 0xfc0007ff, 0x10000111, 0x0, // Multiply Cross Halfword to Word Unsigned X-form (mulchwu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHHW, 0xfc0007ff, 0x10000050, 0x0, // Multiply High Halfword to Word Signed X-form (mulhhw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHHW_, 0xfc0007ff, 0x10000051, 0x0, // Multiply High Halfword to Word Signed X-form (mulhhw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLHW, 0xfc0007ff, 0x10000350, 0x0, // Multiply Low Halfword to Word Signed X-form (mullhw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLHW_, 0xfc0007ff, 0x10000351, 0x0, // Multiply Low Halfword to Word Signed X-form (mullhw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHHWU, 0xfc0007ff, 0x10000010, 0x0, // Multiply High Halfword to Word Unsigned X-form (mulhhwu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULHHWU_, 0xfc0007ff, 0x10000011, 0x0, // Multiply High Halfword to Word Unsigned X-form (mulhhwu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLHWU, 0xfc0007ff, 0x10000310, 0x0, // Multiply Low Halfword to Word Unsigned X-form (mullhwu RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{MULLHWU_, 0xfc0007ff, 0x10000311, 0x0, // Multiply Low Halfword to Word Unsigned X-form (mullhwu. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACCHW, 0xfc0007ff, 0x1000015c, 0x0, // Negative Multiply Accumulate Cross Halfword to Word Modulo Signed XO-form (nmacchw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACCHW_, 0xfc0007ff, 0x1000015d, 0x0, // Negative Multiply Accumulate Cross Halfword to Word Modulo Signed XO-form (nmacchw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACCHWO, 0xfc0007ff, 0x1000055c, 0x0, // Negative Multiply Accumulate Cross Halfword to Word Modulo Signed XO-form (nmacchwo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACCHWO_, 0xfc0007ff, 0x1000055d, 0x0, // Negative Multiply Accumulate Cross Halfword to Word Modulo Signed XO-form (nmacchwo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACCHWS, 0xfc0007ff, 0x100001dc, 0x0, // Negative Multiply Accumulate Cross Halfword to Word Saturate Signed XO-form (nmacchws RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACCHWS_, 0xfc0007ff, 0x100001dd, 0x0, // Negative Multiply Accumulate Cross Halfword to Word Saturate Signed XO-form (nmacchws. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACCHWSO, 0xfc0007ff, 0x100005dc, 0x0, // Negative Multiply Accumulate Cross Halfword to Word Saturate Signed XO-form (nmacchwso RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACCHWSO_, 0xfc0007ff, 0x100005dd, 0x0, // Negative Multiply Accumulate Cross Halfword to Word Saturate Signed XO-form (nmacchwso. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACHHW, 0xfc0007ff, 0x1000005c, 0x0, // Negative Multiply Accumulate High Halfword to Word Modulo Signed XO-form (nmachhw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACHHW_, 0xfc0007ff, 0x1000005d, 0x0, // Negative Multiply Accumulate High Halfword to Word Modulo Signed XO-form (nmachhw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACHHWO, 0xfc0007ff, 0x1000045c, 0x0, // Negative Multiply Accumulate High Halfword to Word Modulo Signed XO-form (nmachhwo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACHHWO_, 0xfc0007ff, 0x1000045d, 0x0, // Negative Multiply Accumulate High Halfword to Word Modulo Signed XO-form (nmachhwo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACHHWS, 0xfc0007ff, 0x100000dc, 0x0, // Negative Multiply Accumulate High Halfword to Word Saturate Signed XO-form (nmachhws RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACHHWS_, 0xfc0007ff, 0x100000dd, 0x0, // Negative Multiply Accumulate High Halfword to Word Saturate Signed XO-form (nmachhws. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACHHWSO, 0xfc0007ff, 0x100004dc, 0x0, // Negative Multiply Accumulate High Halfword to Word Saturate Signed XO-form (nmachhwso RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACHHWSO_, 0xfc0007ff, 0x100004dd, 0x0, // Negative Multiply Accumulate High Halfword to Word Saturate Signed XO-form (nmachhwso. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACLHW, 0xfc0007ff, 0x1000035c, 0x0, // Negative Multiply Accumulate Low Halfword to Word Modulo Signed XO-form (nmaclhw RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACLHW_, 0xfc0007ff, 0x1000035d, 0x0, // Negative Multiply Accumulate Low Halfword to Word Modulo Signed XO-form (nmaclhw. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACLHWO, 0xfc0007ff, 0x1000075c, 0x0, // Negative Multiply Accumulate Low Halfword to Word Modulo Signed XO-form (nmaclhwo RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACLHWO_, 0xfc0007ff, 0x1000075d, 0x0, // Negative Multiply Accumulate Low Halfword to Word Modulo Signed XO-form (nmaclhwo. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACLHWS, 0xfc0007ff, 0x100003dc, 0x0, // Negative Multiply Accumulate Low Halfword to Word Saturate Signed XO-form (nmaclhws RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACLHWS_, 0xfc0007ff, 0x100003dd, 0x0, // Negative Multiply Accumulate Low Halfword to Word Saturate Signed XO-form (nmaclhws. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACLHWSO, 0xfc0007ff, 0x100007dc, 0x0, // Negative Multiply Accumulate Low Halfword to Word Saturate Signed XO-form (nmaclhwso RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{NMACLHWSO_, 0xfc0007ff, 0x100007dd, 0x0, // Negative Multiply Accumulate Low Halfword to Word Saturate Signed XO-form (nmaclhwso. RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ICBI, 0xfc0007fe, 0x7c0007ac, 0x3e00001, // Instruction Cache Block Invalidate X-form (icbi RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{ICBT, 0xfc0007fe, 0x7c00002c, 0x2000001, // Instruction Cache Block Touch X-form (icbt CT, RA, RB)
+		[5]*argField{ap_ImmUnsigned_7_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBA, 0xfc0007fe, 0x7c0005ec, 0x3e00001, // Data Cache Block Allocate X-form (dcba RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBT, 0xfc0007fe, 0x7c00022c, 0x1, // Data Cache Block Touch X-form (dcbt RA,RB,TH)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20, ap_ImmUnsigned_6_10}},
+	{DCBT, 0xfc0007fe, 0x7c00022c, 0x1, // Data Cache Block Touch X-form (dcbt TH,RA,RB)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBTST, 0xfc0007fe, 0x7c0001ec, 0x1, // Data Cache Block Touch for Store X-form (dcbtst RA,RB,TH)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20, ap_ImmUnsigned_6_10}},
+	{DCBTST, 0xfc0007fe, 0x7c0001ec, 0x1, // Data Cache Block Touch for Store X-form (dcbtst TH,RA,RB)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBZ, 0xfc0007fe, 0x7c0007ec, 0x3e00001, // Data Cache Block set to Zero X-form (dcbz RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBST, 0xfc0007fe, 0x7c00006c, 0x3e00001, // Data Cache Block Store X-form (dcbst RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBF, 0xfc0007fe, 0x7c0000ac, 0x3800001, // Data Cache Block Flush X-form (dcbf RA,RB,L)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20, ap_ImmUnsigned_9_10}},
+	{ISYNC, 0xfc0007fe, 0x4c00012c, 0x3fff801, // Instruction Synchronize XL-form (isync)
+		[5]*argField{}},
+	{LBARX, 0xfc0007ff, 0x7c000068, 0x0, // Load Byte And Reserve Indexed X-form [Category: Phased-In] (lbarx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LBARX, 0xfc0007fe, 0x7c000068, 0x0, // Load Byte And Reserve Indexed X-form [Category: Phased-In] (lbarx RT,RA,RB,EH)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20, ap_ImmUnsigned_31_31}},
+	{LHARX, 0xfc0007ff, 0x7c0000e8, 0x0, // Load Halfword And Reserve Indexed X-form [Category: Phased-In] (lharx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LHARX, 0xfc0007fe, 0x7c0000e8, 0x0, // Load Halfword And Reserve Indexed X-form [Category: Phased-In] (lharx RT,RA,RB,EH)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20, ap_ImmUnsigned_31_31}},
+	{LWARX, 0xfc0007ff, 0x7c000028, 0x0, // Load Word And Reserve Indexed X-form (lwarx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LWARX, 0xfc0007ff, 0x7c000028, 0x0, // Load Word And Reserve Indexed X-form (lwarx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LWARX, 0xfc0007fe, 0x7c000028, 0x0, // Load Word And Reserve Indexed X-form (lwarx RT,RA,RB,EH)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20, ap_ImmUnsigned_31_31}},
+	{STBCX_, 0xfc0007ff, 0x7c00056d, 0x0, // Store Byte Conditional Indexed X-form [Category: Phased-In] (stbcx. RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STHCX_, 0xfc0007ff, 0x7c0005ad, 0x0, // Store Halfword Conditional Indexed X-form [Category: Phased-In] (sthcx. RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STWCX_, 0xfc0007ff, 0x7c00012d, 0x0, // Store Word Conditional Indexed X-form (stwcx. RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LDARX, 0xfc0007ff, 0x7c0000a8, 0x0, // Load Doubleword And Reserve Indexed X-form (ldarx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LDARX, 0xfc0007fe, 0x7c0000a8, 0x0, // Load Doubleword And Reserve Indexed X-form (ldarx RT,RA,RB,EH)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20, ap_ImmUnsigned_31_31}},
+	{STDCX_, 0xfc0007ff, 0x7c0001ad, 0x0, // Store Doubleword Conditional Indexed X-form (stdcx. RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LQARX, 0xfc0007ff, 0x7c000228, 0x0, // Load Quadword And Reserve Indexed X-form (lqarx RTp,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LQARX, 0xfc0007fe, 0x7c000228, 0x0, // Load Quadword And Reserve Indexed X-form (lqarx RTp,RA,RB,EH)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20, ap_ImmUnsigned_31_31}},
+	{STQCX_, 0xfc0007ff, 0x7c00016d, 0x0, // Store Quadword Conditional Indexed X-form (stqcx. RSp,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SYNC, 0xfc0007fe, 0x7c0004ac, 0x390f801, // Synchronize X-form (sync L, E)
+		[5]*argField{ap_ImmUnsigned_9_10, ap_ImmUnsigned_12_15}},
+	{EIEIO, 0xfc0007fe, 0x7c0006ac, 0x3fff801, // Enforce In-order Execution of I/O X-form (eieio)
+		[5]*argField{}},
+	{MBAR, 0xfc0007fe, 0x7c0006ac, 0x1ff801, // Memory Barrier X-form (mbar MO)
+		[5]*argField{ap_ImmUnsigned_6_10}},
+	{WAIT, 0xfc0007fe, 0x7c00007c, 0x39ff801, // Wait X-form (wait WC)
+		[5]*argField{ap_ImmUnsigned_9_10}},
+	{TBEGIN_, 0xfc0007ff, 0x7c00051d, 0x1dff800, // Transaction Begin X-form (tbegin. R)
+		[5]*argField{ap_ImmUnsigned_10_10}},
+	{TEND_, 0xfc0007ff, 0x7c00055d, 0x1fff800, // Transaction End X-form (tend. A)
+		[5]*argField{ap_ImmUnsigned_6_6}},
+	{TABORT_, 0xfc0007ff, 0x7c00071d, 0x3e0f800, // Transaction Abort X-form (tabort. RA)
+		[5]*argField{ap_Reg_11_15}},
+	{TABORTWC_, 0xfc0007ff, 0x7c00061d, 0x0, // Transaction Abort Word Conditional X-form (tabortwc. TO,RA,RB)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{TABORTWCI_, 0xfc0007ff, 0x7c00069d, 0x0, // Transaction Abort Word Conditional Immediate X-form (tabortwci. TO,RA,SI)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_ImmSigned_16_20}},
+	{TABORTDC_, 0xfc0007ff, 0x7c00065d, 0x0, // Transaction Abort Doubleword Conditional X-form (tabortdc. TO,RA,RB)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{TABORTDCI_, 0xfc0007ff, 0x7c0006dd, 0x0, // Transaction Abort Doubleword Conditional Immediate X-form (tabortdci. TO,RA, SI)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_ImmSigned_16_20}},
+	{TSR_, 0xfc0007ff, 0x7c0005dd, 0x3dff800, // Transaction Suspend or Resume X-form (tsr. L)
+		[5]*argField{ap_ImmUnsigned_10_10}},
+	{TCHECK, 0xfc0007fe, 0x7c00059c, 0x7ff801, // Transaction Check X-form (tcheck BF)
+		[5]*argField{ap_CondRegField_6_8}},
+	{MFTB, 0xfc0007fe, 0x7c0002e6, 0x1, // Move From Time Base XFX-form (mftb RT,TBR)
+		[5]*argField{ap_Reg_6_10, ap_SpReg_16_20_11_15}},
+	{RFEBB, 0xfc0007fe, 0x4c000124, 0x3fff001, // Return from Event-Based Branch XL-form (rfebb S)
+		[5]*argField{ap_ImmUnsigned_20_20}},
+	{LBDX, 0xfc0007fe, 0x7c000406, 0x1, // Load Byte with Decoration Indexed X-form (lbdx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LHDX, 0xfc0007fe, 0x7c000446, 0x1, // Load Halfword with Decoration Indexed X-form (lhdx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LWDX, 0xfc0007fe, 0x7c000486, 0x1, // Load Word with Decoration Indexed X-form (lwdx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LDDX, 0xfc0007fe, 0x7c0004c6, 0x1, // Load Doubleword with Decoration Indexed X-form (lddx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LFDDX, 0xfc0007fe, 0x7c000646, 0x1, // Load Floating Doubleword with Decoration Indexed X-form (lfddx FRT,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STBDX, 0xfc0007fe, 0x7c000506, 0x1, // Store Byte with Decoration Indexed X-form (stbdx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STHDX, 0xfc0007fe, 0x7c000546, 0x1, // Store Halfword with Decoration Indexed X-form (sthdx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STWDX, 0xfc0007fe, 0x7c000586, 0x1, // Store Word with Decoration Indexed X-form (stwdx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STDDX, 0xfc0007fe, 0x7c0005c6, 0x1, // Store Doubleword with Decoration Indexed X-form (stddx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STFDDX, 0xfc0007fe, 0x7c000746, 0x1, // Store Floating Doubleword with Decoration Indexed X-form (stfddx FRS,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DSN, 0xfc0007fe, 0x7c0003c6, 0x3e00001, // Decorated Storage Notify X-form (dsn RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{ECIWX, 0xfc0007fe, 0x7c00026c, 0x1, // External Control In Word Indexed X-form (eciwx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ECOWX, 0xfc0007fe, 0x7c00036c, 0x1, // External Control Out Word Indexed X-form (ecowx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{SC, 0xfc000002, 0x44000002, 0x3fff01d, // System Call SC-form (sc LEV)
+		[5]*argField{ap_ImmUnsigned_20_26}},
+	{RFID, 0xfc0007fe, 0x4c000024, 0x3fff801, // Return From Interrupt Doubleword XL-form (rfid)
+		[5]*argField{}},
+	{HRFID, 0xfc0007fe, 0x4c000224, 0x3fff801, // Hypervisor Return From Interrupt Doubleword XL-form (hrfid)
+		[5]*argField{}},
+	{DOZE, 0xfc0007fe, 0x4c000324, 0x3fff801, // Doze XL-form (doze)
+		[5]*argField{}},
+	{NAP, 0xfc0007fe, 0x4c000364, 0x3fff801, // Nap XL-form (nap)
+		[5]*argField{}},
+	{SLEEP, 0xfc0007fe, 0x4c0003a4, 0x3fff801, // Sleep XL-form (sleep)
+		[5]*argField{}},
+	{RVWINKLE, 0xfc0007fe, 0x4c0003e4, 0x3fff801, // Rip Van Winkle XL-form (rvwinkle)
+		[5]*argField{}},
+	{LBZCIX, 0xfc0007fe, 0x7c0006aa, 0x1, // Load Byte and Zero Caching Inhibited Indexed X-form (lbzcix RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LWZCIX, 0xfc0007fe, 0x7c00062a, 0x1, // Load Word and Zero Caching Inhibited Indexed X-form (lwzcix RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LHZCIX, 0xfc0007fe, 0x7c00066a, 0x1, // Load Halfword and Zero Caching Inhibited Indexed X-form (lhzcix RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LDCIX, 0xfc0007fe, 0x7c0006ea, 0x1, // Load Doubleword Caching Inhibited Indexed X-form (ldcix RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STBCIX, 0xfc0007fe, 0x7c0007aa, 0x1, // Store Byte Caching Inhibited Indexed X-form (stbcix RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STWCIX, 0xfc0007fe, 0x7c00072a, 0x1, // Store Word Caching Inhibited Indexed X-form (stwcix RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STHCIX, 0xfc0007fe, 0x7c00076a, 0x1, // Store Halfword Caching Inhibited Indexed X-form (sthcix RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STDCIX, 0xfc0007fe, 0x7c0007ea, 0x1, // Store Doubleword Caching Inhibited Indexed X-form (stdcix RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{TRECLAIM_, 0xfc0007ff, 0x7c00075d, 0x3e0f800, // Transaction Reclaim X-form (treclaim. RA)
+		[5]*argField{ap_Reg_11_15}},
+	{TRECHKPT_, 0xfc0007ff, 0x7c0007dd, 0x3fff800, // Transaction Recheckpoint X-form (trechkpt.)
+		[5]*argField{}},
+	{MTSPR, 0xfc0007fe, 0x7c0003a6, 0x1, // Move To Special Purpose Register XFX-form (mtspr SPR,RS)
+		[5]*argField{ap_SpReg_16_20_11_15, ap_Reg_6_10}},
+	{MFSPR, 0xfc0007fe, 0x7c0002a6, 0x1, // Move From Special Purpose Register XFX-form (mfspr RT,SPR)
+		[5]*argField{ap_Reg_6_10, ap_SpReg_16_20_11_15}},
+	{MTMSR, 0xfc0007fe, 0x7c000124, 0x1ef801, // Move To Machine State Register X-form (mtmsr RS,L)
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_15_15}},
+	{MTMSRD, 0xfc0007fe, 0x7c000164, 0x1ef801, // Move To Machine State Register Doubleword X-form (mtmsrd RS,L)
+		[5]*argField{ap_Reg_6_10, ap_ImmUnsigned_15_15}},
+	{MFMSR, 0xfc0007fe, 0x7c0000a6, 0x1ff801, // Move From Machine State Register X-form (mfmsr RT)
+		[5]*argField{ap_Reg_6_10}},
+	{SLBIE, 0xfc0007fe, 0x7c000364, 0x3ff0001, // SLB Invalidate Entry X-form (slbie RB)
+		[5]*argField{ap_Reg_16_20}},
+	{SLBIA, 0xfc0007fe, 0x7c0003e4, 0x31ff801, // SLB Invalidate All X-form (slbia IH)
+		[5]*argField{ap_ImmUnsigned_8_10}},
+	{SLBMTE, 0xfc0007fe, 0x7c000324, 0x1f0001, // SLB Move To Entry X-form (slbmte RS,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{SLBMFEV, 0xfc0007fe, 0x7c0006a6, 0x1f0001, // SLB Move From Entry VSID X-form (slbmfev RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{SLBMFEE, 0xfc0007fe, 0x7c000726, 0x1f0001, // SLB Move From Entry ESID X-form (slbmfee RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{SLBFEE_, 0xfc0007ff, 0x7c0007a7, 0x1f0000, // SLB Find Entry ESID X-form (slbfee. RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{MTSR, 0xfc0007fe, 0x7c0001a4, 0x10f801, // Move To Segment Register X-form (mtsr SR,RS)
+		[5]*argField{ap_SpReg_12_15, ap_Reg_6_10}},
+	{MTSRIN, 0xfc0007fe, 0x7c0001e4, 0x1f0001, // Move To Segment Register Indirect X-form (mtsrin RS,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{MFSR, 0xfc0007fe, 0x7c0004a6, 0x10f801, // Move From Segment Register X-form (mfsr RT,SR)
+		[5]*argField{ap_Reg_6_10, ap_SpReg_12_15}},
+	{MFSRIN, 0xfc0007fe, 0x7c000526, 0x1f0001, // Move From Segment Register Indirect X-form (mfsrin RT,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_16_20}},
+	{TLBIE, 0xfc0007fe, 0x7c000264, 0x1f0001, // TLB Invalidate Entry X-form (tlbie RB,RS)
+		[5]*argField{ap_Reg_16_20, ap_Reg_6_10}},
+	{TLBIEL, 0xfc0007fe, 0x7c000224, 0x3ff0001, // TLB Invalidate Entry Local X-form (tlbiel RB)
+		[5]*argField{ap_Reg_16_20}},
+	{TLBIA, 0xfc0007fe, 0x7c0002e4, 0x3fff801, // TLB Invalidate All X-form (tlbia)
+		[5]*argField{}},
+	{TLBSYNC, 0xfc0007fe, 0x7c00046c, 0x3fff801, // TLB Synchronize X-form (tlbsync)
+		[5]*argField{}},
+	{MSGSND, 0xfc0007fe, 0x7c00019c, 0x3ff0001, // Message Send X-form (msgsnd RB)
+		[5]*argField{ap_Reg_16_20}},
+	{MSGCLR, 0xfc0007fe, 0x7c0001dc, 0x3ff0001, // Message Clear X-form (msgclr RB)
+		[5]*argField{ap_Reg_16_20}},
+	{MSGSNDP, 0xfc0007fe, 0x7c00011c, 0x3ff0001, // Message Send Privileged X-form (msgsndp RB)
+		[5]*argField{ap_Reg_16_20}},
+	{MSGCLRP, 0xfc0007fe, 0x7c00015c, 0x3ff0001, // Message Clear Privileged X-form (msgclrp RB)
+		[5]*argField{ap_Reg_16_20}},
+	{MTTMR, 0xfc0007fe, 0x7c0003dc, 0x1, // Move To Thread Management Register XFX-form (mttmr TMR,RS)
+		[5]*argField{ap_SpReg_16_20_11_15, ap_Reg_6_10}},
+	{SC, 0xfc000002, 0x44000002, 0x3fffffd, // System Call SC-form (sc)
+		[5]*argField{}},
+	{RFI, 0xfc0007fe, 0x4c000064, 0x3fff801, // Return From Interrupt XL-form (rfi)
+		[5]*argField{}},
+	{RFCI, 0xfc0007fe, 0x4c000066, 0x3fff801, // Return From Critical Interrupt XL-form (rfci)
+		[5]*argField{}},
+	{RFDI, 0xfc0007fe, 0x4c00004e, 0x3fff801, // Return From Debug Interrupt X-form (rfdi)
+		[5]*argField{}},
+	{RFMCI, 0xfc0007fe, 0x4c00004c, 0x3fff801, // Return From Machine Check Interrupt XL-form (rfmci)
+		[5]*argField{}},
+	{RFGI, 0xfc0007fe, 0x4c0000cc, 0x3fff801, // Return From Guest Interrupt XL-form (rfgi)
+		[5]*argField{}},
+	{EHPRIV, 0xfc0007fe, 0x7c00021c, 0x1, // Embedded Hypervisor Privilege XL-form (ehpriv OC)
+		[5]*argField{ap_ImmUnsigned_6_20}},
+	{MTSPR, 0xfc0007fe, 0x7c0003a6, 0x1, // Move To Special Purpose Register XFX-form (mtspr SPR,RS)
+		[5]*argField{ap_SpReg_16_20_11_15, ap_Reg_6_10}},
+	{MFSPR, 0xfc0007fe, 0x7c0002a6, 0x1, // Move From Special Purpose Register XFX-form (mfspr RT,SPR)
+		[5]*argField{ap_Reg_6_10, ap_SpReg_16_20_11_15}},
+	{MTDCR, 0xfc0007fe, 0x7c000386, 0x1, // Move To Device Control Register XFX-form (mtdcr DCRN,RS)
+		[5]*argField{ap_SpReg_16_20_11_15, ap_Reg_6_10}},
+	{MTDCRX, 0xfc0007fe, 0x7c000306, 0xf801, // Move To Device Control Register Indexed X-form (mtdcrx RA,RS)
+		[5]*argField{ap_Reg_11_15, ap_Reg_6_10}},
+	{MFDCR, 0xfc0007fe, 0x7c000286, 0x1, // Move From Device Control Register XFX-form (mfdcr RT,DCRN)
+		[5]*argField{ap_Reg_6_10, ap_SpReg_16_20_11_15}},
+	{MFDCRX, 0xfc0007fe, 0x7c000206, 0xf801, // Move From Device Control Register Indexed X-form (mfdcrx RT,RA)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15}},
+	{MTMSR, 0xfc0007fe, 0x7c000124, 0x1ff801, // Move To Machine State Register X-form (mtmsr RS)
+		[5]*argField{ap_Reg_6_10}},
+	{MFMSR, 0xfc0007fe, 0x7c0000a6, 0x1ff801, // Move From Machine State Register X-form (mfmsr RT)
+		[5]*argField{ap_Reg_6_10}},
+	{WRTEE, 0xfc0007fe, 0x7c000106, 0x1ff801, // Write MSR External Enable X-form (wrtee RS)
+		[5]*argField{ap_Reg_6_10}},
+	{WRTEEI, 0xfc0007fe, 0x7c000146, 0x3ff7801, // Write MSR External Enable Immediate X-form (wrteei E)
+		[5]*argField{ap_ImmUnsigned_16_16}},
+	{LBEPX, 0xfc0007fe, 0x7c0000be, 0x1, // Load Byte by External Process ID Indexed X-form (lbepx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LHEPX, 0xfc0007fe, 0x7c00023e, 0x1, // Load Halfword by External Process ID Indexed X-form (lhepx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LWEPX, 0xfc0007fe, 0x7c00003e, 0x1, // Load Word by External Process ID Indexed X-form (lwepx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LDEPX, 0xfc0007fe, 0x7c00003a, 0x1, // Load Doubleword by External Process ID Indexed X-form (ldepx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STBEPX, 0xfc0007fe, 0x7c0001be, 0x1, // Store Byte by External Process ID Indexed X-form (stbepx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STHEPX, 0xfc0007fe, 0x7c00033e, 0x1, // Store Halfword by External Process ID Indexed X-form (sthepx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STWEPX, 0xfc0007fe, 0x7c00013e, 0x1, // Store Word by External Process ID Indexed X-form (stwepx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STDEPX, 0xfc0007fe, 0x7c00013a, 0x1, // Store Doubleword by External Process ID Indexed X-form (stdepx RS,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBSTEP, 0xfc0007fe, 0x7c00007e, 0x3e00001, // Data Cache Block Store by External PID X-form (dcbstep RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBTEP, 0xfc0007fe, 0x7c00027e, 0x1, // Data Cache Block Touch by External PID X-form (dcbtep TH,RA,RB)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBFEP, 0xfc0007fe, 0x7c0000fe, 0x3800001, // Data Cache Block Flush by External PID X-form (dcbfep RA,RB,L)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20, ap_ImmUnsigned_9_10}},
+	{DCBTSTEP, 0xfc0007fe, 0x7c0001fe, 0x1, // Data Cache Block Touch for Store by External PID X-form (dcbtstep TH,RA,RB)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ICBIEP, 0xfc0007fe, 0x7c0007be, 0x3e00001, // Instruction Cache Block Invalidate by External PID X-form (icbiep RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBZEP, 0xfc0007fe, 0x7c0007fe, 0x3e00001, // Data Cache Block set to Zero by External PID X-form (dcbzep RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{LFDEPX, 0xfc0007fe, 0x7c0004be, 0x1, // Load Floating-Point Double by External Process ID Indexed X-form (lfdepx FRT,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STFDEPX, 0xfc0007fe, 0x7c0005be, 0x1, // Store Floating-Point Double by External Process ID Indexed X-form (stfdepx FRS,RA,RB)
+		[5]*argField{ap_FPReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVLDDEPX, 0xfc0007fe, 0x7c00063e, 0x1, // Vector Load Doubleword into Doubleword by External Process ID Indexed EVX-form (evlddepx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{EVSTDDEPX, 0xfc0007fe, 0x7c00073e, 0x1, // Vector Store Doubleword into Doubleword by External Process ID Indexed EVX-form (evstddepx RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LVEPX, 0xfc0007fe, 0x7c00024e, 0x1, // Load Vector by External Process ID Indexed X-form (lvepx VRT,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{LVEPXL, 0xfc0007fe, 0x7c00020e, 0x1, // Load Vector by External Process ID Indexed LRU X-form (lvepxl VRT,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STVEPX, 0xfc0007fe, 0x7c00064e, 0x1, // Store Vector by External Process ID Indexed X-form (stvepx VRS,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{STVEPXL, 0xfc0007fe, 0x7c00060e, 0x1, // Store Vector by External Process ID Indexed LRU X-form (stvepxl VRS,RA,RB)
+		[5]*argField{ap_VecReg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBI, 0xfc0007fe, 0x7c0003ac, 0x3e00001, // Data Cache Block Invalidate X-form (dcbi RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBLQ_, 0xfc0007ff, 0x7c00034d, 0x2000000, // Data Cache Block Lock Query X-form (dcblq. CT,RA,RB)
+		[5]*argField{ap_ImmUnsigned_7_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ICBLQ_, 0xfc0007ff, 0x7c00018d, 0x2000000, // Instruction Cache Block Lock Query X-form (icblq. CT,RA,RB)
+		[5]*argField{ap_ImmUnsigned_7_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBTLS, 0xfc0007fe, 0x7c00014c, 0x2000001, // Data Cache Block Touch and Lock Set X-form (dcbtls CT,RA,RB)
+		[5]*argField{ap_ImmUnsigned_7_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBTSTLS, 0xfc0007fe, 0x7c00010c, 0x2000001, // Data Cache Block Touch for Store and Lock Set X-form (dcbtstls CT,RA,RB)
+		[5]*argField{ap_ImmUnsigned_7_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ICBTLS, 0xfc0007fe, 0x7c0003cc, 0x2000001, // Instruction Cache Block Touch and Lock Set X-form (icbtls CT,RA,RB)
+		[5]*argField{ap_ImmUnsigned_7_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ICBLC, 0xfc0007fe, 0x7c0001cc, 0x2000001, // Instruction Cache Block Lock Clear X-form (icblc CT,RA,RB)
+		[5]*argField{ap_ImmUnsigned_7_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{DCBLC, 0xfc0007fe, 0x7c00030c, 0x2000001, // Data Cache Block Lock Clear X-form (dcblc CT,RA,RB)
+		[5]*argField{ap_ImmUnsigned_7_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{TLBIVAX, 0xfc0007fe, 0x7c000624, 0x3e00001, // TLB Invalidate Virtual Address Indexed X-form (tlbivax RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{TLBILX, 0xfc0007fe, 0x7c000024, 0x3800001, // TLB Invalidate Local Indexed X-form (tlbilx RA,RB])
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{TLBSX, 0xfc0007fe, 0x7c000724, 0x3e00001, // TLB Search Indexed X-form (tlbsx RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{TLBSRX_, 0xfc0007ff, 0x7c0006a5, 0x3e00000, // TLB Search and Reserve Indexed X-form (tlbsrx. RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{TLBRE, 0xfc0007fe, 0x7c000764, 0x3fff801, // TLB Read Entry X-form (tlbre)
+		[5]*argField{}},
+	{TLBSYNC, 0xfc0007fe, 0x7c00046c, 0x3fff801, // TLB Synchronize X-form (tlbsync)
+		[5]*argField{}},
+	{TLBWE, 0xfc0007fe, 0x7c0007a4, 0x3fff801, // TLB Write Entry X-form (tlbwe)
+		[5]*argField{}},
+	{DNH, 0xfc0007fe, 0x4c00018c, 0x1, // Debugger Notify Halt XFX-form (dnh DUI,DUIS)
+		[5]*argField{ap_ImmUnsigned_6_10, ap_ImmUnsigned_11_20}},
+	{MSGSND, 0xfc0007fe, 0x7c00019c, 0x3ff0001, // Message Send X-form (msgsnd RB)
+		[5]*argField{ap_Reg_16_20}},
+	{MSGCLR, 0xfc0007fe, 0x7c0001dc, 0x3ff0001, // Message Clear X-form (msgclr RB)
+		[5]*argField{ap_Reg_16_20}},
+	{DCI, 0xfc0007fe, 0x7c00038c, 0x21ff801, // Data Cache Invalidate X-form (dci CT)
+		[5]*argField{ap_ImmUnsigned_7_10}},
+	{ICI, 0xfc0007fe, 0x7c00078c, 0x21ff801, // Instruction Cache Invalidate X-form (ici CT)
+		[5]*argField{ap_ImmUnsigned_7_10}},
+	{DCREAD, 0xfc0007fe, 0x7c0003cc, 0x1, // Data Cache Read X-form (dcread RT,RA,RB)
+		[5]*argField{ap_Reg_6_10, ap_Reg_11_15, ap_Reg_16_20}},
+	{ICREAD, 0xfc0007fe, 0x7c0007cc, 0x3e00001, // Instruction Cache Read X-form (icread RA,RB)
+		[5]*argField{ap_Reg_11_15, ap_Reg_16_20}},
+	{MFPMR, 0xfc0007fe, 0x7c00029c, 0x1, // Move From Performance Monitor Register XFX-form (mfpmr RT,PMRN)
+		[5]*argField{ap_Reg_6_10, ap_SpReg_11_20}},
+	{MTPMR, 0xfc0007fe, 0x7c00039c, 0x1, // Move To Performance Monitor Register XFX-form (mtpmr PMRN,RS)
+		[5]*argField{ap_SpReg_11_20, ap_Reg_6_10}},
+}
diff --git a/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/testdata/decode.txt b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/testdata/decode.txt
new file mode 100644
index 0000000..b4e5db2
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/arch/ppc64/ppc64asm/testdata/decode.txt
@@ -0,0 +1,25 @@
+6d746162|	gnu	xoris r20,r11,24930
+4c040000|	gnu	mcrf cr0,cr1
+88000017|	gnu	lbz r0,23(0)
+4abaa88a|	gnu	ba 0xfebaa888
+7d8fc2a6|	gnu	mfspr r12,783
+00000000|	gnu	error: unknown instruction
+a1841e80|	gnu	lhz r12,7808(r4)
+42093d10|	gnu	bc 16,4*cr2+gt,.+0x3d10
+e38d5b90|	gnu	lq r28,23440(r13)
+84127a20|	gnu	lwzu r0,31264(r18)
+c61bb730|	gnu	lfsu f16,-18640(r27)
+0825f440|	gnu	tdi 1,r5,-3008
+a9a912c1|	gnu	lha r13,4801(r9)
+ebb24fd1|	gnu	ldu r29,20432(r18)
+b1ce0612|	gnu	sth r14,1554(r14)
+f3c04322|	gnu	xvcvdpuxws vs30,vs40
+945c62a2|	gnu	stwu r2,25250(r28)
+9c8156e3|	gnu	stbu r4,22243(r1)
+f91b9c7a|	gnu	stq r8,-25480(r27)
+2c1c81b4|	gnu	cmpwi r28,-32332
+f87b904d|	gnu	stdu r3,-28596(r27)
+eab3c832|	gnu	lwa r21,-14288(r19)
+4320336b|	gnu	bcla 25,lt,0x3368
+7e40092e|	gnu	stwx r18,0,r1
+7c103c2c|	gnu	lwbrx r0,r16,r7
diff --git a/src/cmd/vendor/vendor.json b/src/cmd/vendor/vendor.json
index 565498f..8fc8897 100644
--- a/src/cmd/vendor/vendor.json
+++ b/src/cmd/vendor/vendor.json
@@ -11,6 +11,12 @@
 			"local": "golang.org/x/arch/arm/armasm",
 			"revision": "ad6a463afcf9bd5b38c81fa9ba612dae11859d40",
 			"revisionTime": "2015-08-28T15:42:14Z"
+		},
+		{
+			"canonical": "golang.org/x/arch/ppc64/ppc64asm",
+			"local": "golang.org/x/arch/ppc64/ppc64asm",
+			"revision": "4831b0a617f7a819d4bf3c877d8e827d0283542c",
+			"revisionTime": "2016-10-12T18:28:04Z"
 		}
 	]
 }
diff --git a/src/cmd/vet/all/main.go b/src/cmd/vet/all/main.go
new file mode 100644
index 0000000..f4ee8fe
--- /dev/null
+++ b/src/cmd/vet/all/main.go
@@ -0,0 +1,332 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// The vet/all command runs go vet on the standard library and commands.
+// It compares the output against a set of whitelists
+// maintained in the whitelist directory.
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"flag"
+	"fmt"
+	"go/build"
+	"internal/testenv"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+var (
+	flagPlatforms = flag.String("p", "", "platform(s) to use e.g. linux/amd64,darwin/386")
+	flagAll       = flag.Bool("all", false, "run all platforms")
+	flagNoLines   = flag.Bool("n", false, "don't print line numbers")
+)
+
+var cmdGoPath string
+
+func main() {
+	log.SetPrefix("vet/all: ")
+	log.SetFlags(0)
+
+	var err error
+	cmdGoPath, err = testenv.GoTool()
+	if err != nil {
+		log.Print("could not find cmd/go; skipping")
+		// We're on a platform that can't run cmd/go.
+		// We want this script to be able to run as part of all.bash,
+		// so return cleanly rather than with exit code 1.
+		return
+	}
+
+	flag.Parse()
+	switch {
+	case *flagAll && *flagPlatforms != "":
+		log.Print("-all and -p flags are incompatible")
+		flag.Usage()
+		os.Exit(2)
+	case *flagPlatforms != "":
+		vetPlatforms(parseFlagPlatforms())
+	case *flagAll:
+		vetPlatforms(allPlatforms())
+	default:
+		host := platform{os: build.Default.GOOS, arch: build.Default.GOARCH}
+		host.vet(runtime.GOMAXPROCS(-1))
+	}
+}
+
+func allPlatforms() []platform {
+	var pp []platform
+	cmd := exec.Command(cmdGoPath, "tool", "dist", "list")
+	out, err := cmd.Output()
+	if err != nil {
+		log.Fatal(err)
+	}
+	lines := bytes.Split(out, []byte{'\n'})
+	for _, line := range lines {
+		if len(line) == 0 {
+			continue
+		}
+		pp = append(pp, parsePlatform(string(line)))
+	}
+	return pp
+}
+
+func parseFlagPlatforms() []platform {
+	var pp []platform
+	components := strings.Split(*flagPlatforms, ",")
+	for _, c := range components {
+		pp = append(pp, parsePlatform(c))
+	}
+	return pp
+}
+
+func parsePlatform(s string) platform {
+	vv := strings.Split(s, "/")
+	if len(vv) != 2 {
+		log.Fatalf("could not parse platform %s, must be of form goos/goarch", s)
+	}
+	return platform{os: vv[0], arch: vv[1]}
+}
+
+type whitelist map[string]int
+
+// load adds entries from the whitelist file, if present, for os/arch to w.
+func (w whitelist) load(goos string, goarch string) {
+	// Look up whether goarch is a 32-bit or 64-bit architecture.
+	archbits, ok := nbits[goarch]
+	if !ok {
+		log.Fatalf("unknown bitwidth for arch %q", goarch)
+	}
+
+	// Look up whether goarch has a shared arch suffix,
+	// such as mips64x for mips64 and mips64le.
+	archsuff := goarch
+	if x, ok := archAsmX[goarch]; ok {
+		archsuff = x
+	}
+
+	// Load whitelists.
+	filenames := []string{
+		"all.txt",
+		goos + ".txt",
+		goarch + ".txt",
+		goos + "_" + goarch + ".txt",
+		fmt.Sprintf("%dbit.txt", archbits),
+	}
+	if goarch != archsuff {
+		filenames = append(filenames,
+			archsuff+".txt",
+			goos+"_"+archsuff+".txt",
+		)
+	}
+
+	// We allow error message templates using GOOS and GOARCH.
+	if goos == "android" {
+		goos = "linux" // so many special cases :(
+	}
+
+	// Read whitelists and do template substitution.
+	replace := strings.NewReplacer("GOOS", goos, "GOARCH", goarch, "ARCHSUFF", archsuff)
+
+	for _, filename := range filenames {
+		path := filepath.Join("whitelist", filename)
+		f, err := os.Open(path)
+		if err != nil {
+			// Allow not-exist errors; not all combinations have whitelists.
+			if os.IsNotExist(err) {
+				continue
+			}
+			log.Fatal(err)
+		}
+		scan := bufio.NewScanner(f)
+		for scan.Scan() {
+			line := scan.Text()
+			if len(line) == 0 || strings.HasPrefix(line, "//") {
+				continue
+			}
+			w[replace.Replace(line)]++
+		}
+		if err := scan.Err(); err != nil {
+			log.Fatal(err)
+		}
+	}
+}
+
+type platform struct {
+	os   string
+	arch string
+}
+
+func (p platform) String() string {
+	return p.os + "/" + p.arch
+}
+
+// ignorePathPrefixes are file path prefixes that should be ignored wholesale.
+var ignorePathPrefixes = [...]string{
+	// These testdata dirs have lots of intentionally broken/bad code for tests.
+	"cmd/go/testdata/",
+	"cmd/vet/testdata/",
+	"go/printer/testdata/",
+}
+
+func vetPlatforms(pp []platform) {
+	ncpus := runtime.GOMAXPROCS(-1) / len(pp)
+	if ncpus < 1 {
+		ncpus = 1
+	}
+	var wg sync.WaitGroup
+	wg.Add(len(pp))
+	for _, p := range pp {
+		p := p
+		go func() {
+			p.vet(ncpus)
+			wg.Done()
+		}()
+	}
+	wg.Wait()
+}
+
+func (p platform) vet(ncpus int) {
+	var buf bytes.Buffer
+	fmt.Fprintf(&buf, "go run main.go -p %s\n", p)
+
+	// Load whitelist(s).
+	w := make(whitelist)
+	w.load(p.os, p.arch)
+
+	env := append(os.Environ(), "GOOS="+p.os, "GOARCH="+p.arch)
+
+	// Do 'go install std' before running vet.
+	// It is cheap when already installed.
+	// Not installing leads to non-obvious failures due to inability to typecheck.
+	// TODO: If go/loader ever makes it to the standard library, have vet use it,
+	// at which point vet can work off source rather than compiled packages.
+	cmd := exec.Command(cmdGoPath, "install", "-p", strconv.Itoa(ncpus), "std")
+	cmd.Env = env
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		log.Fatalf("failed to run GOOS=%s GOARCH=%s 'go install std': %v\n%s", p.os, p.arch, err, out)
+	}
+
+	// 'go tool vet .' is considerably faster than 'go vet ./...'
+	// TODO: The unsafeptr checks are disabled for now,
+	// because there are so many false positives,
+	// and no clear way to improve vet to eliminate large chunks of them.
+	// And having them in the whitelists will just cause annoyance
+	// and churn when working on the runtime.
+	cmd = exec.Command(cmdGoPath, "tool", "vet", "-unsafeptr=false", ".")
+	cmd.Dir = filepath.Join(runtime.GOROOT(), "src")
+	cmd.Env = env
+	stderr, err := cmd.StderrPipe()
+	if err != nil {
+		log.Fatal(err)
+	}
+	if err := cmd.Start(); err != nil {
+		log.Fatal(err)
+	}
+
+	// Process vet output.
+	scan := bufio.NewScanner(stderr)
+NextLine:
+	for scan.Scan() {
+		line := scan.Text()
+		if strings.HasPrefix(line, "vet: ") {
+			// Typecheck failure: Malformed syntax or multiple packages or the like.
+			// This will yield nicer error messages elsewhere, so ignore them here.
+			continue
+		}
+
+		fields := strings.SplitN(line, ":", 3)
+		var file, lineno, msg string
+		switch len(fields) {
+		case 2:
+			// vet message with no line number
+			file, msg = fields[0], fields[1]
+		case 3:
+			file, lineno, msg = fields[0], fields[1], fields[2]
+		default:
+			log.Fatalf("could not parse vet output line:\n%s", line)
+		}
+		msg = strings.TrimSpace(msg)
+
+		for _, ignore := range ignorePathPrefixes {
+			if strings.HasPrefix(file, filepath.FromSlash(ignore)) {
+				continue NextLine
+			}
+		}
+
+		key := file + ": " + msg
+		if w[key] == 0 {
+			// Vet error with no match in the whitelist. Print it.
+			if *flagNoLines {
+				fmt.Fprintf(&buf, "%s: %s\n", file, msg)
+			} else {
+				fmt.Fprintf(&buf, "%s:%s: %s\n", file, lineno, msg)
+			}
+			continue
+		}
+		w[key]--
+	}
+	if scan.Err() != nil {
+		log.Fatalf("failed to scan vet output: %v", scan.Err())
+	}
+	err = cmd.Wait()
+	// We expect vet to fail.
+	// Make sure it has failed appropriately, though (for example, not a PathError).
+	if _, ok := err.(*exec.ExitError); !ok {
+		log.Fatalf("unexpected go vet execution failure: %v", err)
+	}
+	printedHeader := false
+	if len(w) > 0 {
+		for k, v := range w {
+			if v != 0 {
+				if !printedHeader {
+					fmt.Fprintln(&buf, "unmatched whitelist entries:")
+					printedHeader = true
+				}
+				for i := 0; i < v; i++ {
+					fmt.Fprintln(&buf, k)
+				}
+			}
+		}
+	}
+
+	os.Stdout.Write(buf.Bytes())
+}
+
+// nbits maps from architecture names to the number of bits in a pointer.
+// TODO: figure out a clean way to avoid get this info rather than listing it here yet again.
+var nbits = map[string]int{
+	"386":      32,
+	"amd64":    64,
+	"amd64p32": 32,
+	"arm":      32,
+	"arm64":    64,
+	"mips":     32,
+	"mipsle":   32,
+	"mips64":   64,
+	"mips64le": 64,
+	"ppc64":    64,
+	"ppc64le":  64,
+	"s390x":    64,
+}
+
+// archAsmX maps architectures to the suffix usually used for their assembly files,
+// if different than the arch name itself.
+var archAsmX = map[string]string{
+	"android":  "linux",
+	"mips64":   "mips64x",
+	"mips64le": "mips64x",
+	"ppc64":    "ppc64x",
+	"ppc64le":  "ppc64x",
+}
diff --git a/src/cmd/vet/all/whitelist/386.txt b/src/cmd/vet/all/whitelist/386.txt
new file mode 100644
index 0000000..33b63e1
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/386.txt
@@ -0,0 +1,29 @@
+// 386-specific vet whitelist. See readme.txt for details.
+
+runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes
+
+// reflect trampolines intentionally omit arg size. Same for morestack.
+reflect/asm_386.s: [386] makeFuncStub: use of 4(SP) points beyond argument frame
+reflect/asm_386.s: [386] methodValueCall: use of 4(SP) points beyond argument frame
+runtime/asm_386.s: [386] morestack: use of 4(SP) points beyond argument frame
+runtime/asm_386.s: [386] morestack: use of 8(SP) points beyond argument frame
+runtime/asm_386.s: [386] morestack: use of 4(SP) points beyond argument frame
+
+// Intentionally missing declarations. These are special assembly routines.
+runtime/asm_386.s: [386] ldt0setup: function ldt0setup missing Go declaration
+runtime/asm_386.s: [386] emptyfunc: function emptyfunc missing Go declaration
+runtime/asm_386.s: [386] aeshashbody: function aeshashbody missing Go declaration
+runtime/asm_386.s: [386] memeqbody: function memeqbody missing Go declaration
+runtime/asm_386.s: [386] cmpbody: function cmpbody missing Go declaration
+runtime/asm_386.s: [386] addmoduledata: function addmoduledata missing Go declaration
+runtime/duff_386.s: [386] duffzero: function duffzero missing Go declaration
+runtime/duff_386.s: [386] duffcopy: function duffcopy missing Go declaration
+
+runtime/asm_386.s: [386] uint32tofloat64: function uint32tofloat64 missing Go declaration
+runtime/asm_386.s: [386] float64touint32: function float64touint32 missing Go declaration
+
+runtime/asm_386.s: [386] stackcheck: function stackcheck missing Go declaration
+
+// Clearer using FP than SP, but that requires named offsets.
+runtime/asm_386.s: [386] rt0_go: unknown variable argc
+runtime/asm_386.s: [386] rt0_go: unknown variable argv
diff --git a/src/cmd/vet/all/whitelist/64bit.txt b/src/cmd/vet/all/whitelist/64bit.txt
new file mode 100644
index 0000000..35b9eb3
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/64bit.txt
@@ -0,0 +1,13 @@
+// 64-bit-specific vet whitelist. See readme.txt for details.
+
+// False positives.
+
+// Clever const tricks outwit the "large shift" check.
+runtime/hashmap.go: hash might be too small for shift of 56
+runtime/hashmap.go: hash might be too small for shift of 56
+runtime/hashmap.go: hash might be too small for shift of 56
+runtime/hashmap.go: hash might be too small for shift of 56
+runtime/hashmap.go: hash might be too small for shift of 56
+runtime/hashmap.go: hash might be too small for shift of 56
+runtime/hashmap_fast.go: hash might be too small for shift of 56
+runtime/hashmap_fast.go: hash might be too small for shift of 56
diff --git a/src/cmd/vet/all/whitelist/all.txt b/src/cmd/vet/all/whitelist/all.txt
new file mode 100644
index 0000000..7250de1
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/all.txt
@@ -0,0 +1,92 @@
+// Non-platform-specific vet whitelist. See readme.txt for details.
+
+// Issue 17580 (remove when fixed)
+cmd/go/go_test.go: +build comment must appear before package clause and be followed by a blank line
+
+
+// Real problems that we can't fix.
+
+// This is a bad WriteTo signature. Errors are being ignored!
+// However, we can't change it due to the Go 1 compatibility promise.
+go/types/scope.go: method WriteTo(w io.Writer, n int, recurse bool) should have signature WriteTo(io.Writer) (int64, error)
+
+
+// False positives.
+
+// Nothing much to do about cross-package assembly. Unfortunate.
+runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: call is in package reflect
+runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Equal is in package bytes
+runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: IndexByte is in package bytes
+runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: IndexByte is in package strings
+runtime/sys_GOOS_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: now is in package time
+
+// Legitimate vet complaints in which we are testing for correct runtime behavior
+// in bad situations that vet can also detect statically.
+cmd/cover/testdata/test.go: unreachable code
+fmt/fmt_test.go: arg nil for printf verb %s of wrong type: untyped nil
+encoding/json/decode_test.go: struct field m has json tag but is not exported
+encoding/json/decode_test.go: struct field m2 has json tag but is not exported
+encoding/json/tagkey_test.go: struct field tag `:"BadFormat"` not compatible with reflect.StructTag.Get: bad syntax for struct tag key
+runtime/testdata/testprog/deadlock.go: unreachable code
+runtime/testdata/testprog/deadlock.go: unreachable code
+sync/cond_test.go: assignment copies lock value to c2: sync.Cond contains sync.noCopy
+
+// Non-standard method signatures.
+// These cases are basically ok.
+// Errors are handled reasonably and there's no clear need for interface satisfaction.
+// Except for the runtime/pprof case, the API is not exported.
+cmd/internal/bio/buf.go: method Seek(offset int64, whence int) int64 should have signature Seek(int64, int) (int64, error)
+cmd/internal/bio/buf.go: method Seek(offset int64, whence int) int64 should have signature Seek(int64, int) (int64, error)
+cmd/internal/obj/link.go: method Peek(i int) byte should have signature Peek(int) ([]byte, error)
+fmt/print.go: method WriteByte(c byte) should have signature WriteByte(byte) error
+runtime/pprof/pprof.go: method WriteTo(w io.Writer, debug int) error should have signature WriteTo(io.Writer) (int64, error)
+
+// These encoding/xml methods have the correct signature.
+// vet doesn't know it because they are *in* the encoding/xml package.
+// It's not worth teaching vet about the distinction, so whitelist them.
+encoding/gob/encode.go: method WriteByte(c byte) should have signature WriteByte(byte) error
+encoding/xml/marshal.go: method MarshalXML(e *Encoder, start StartElement) error should have signature MarshalXML(*xml.Encoder, xml.StartElement) error
+encoding/xml/marshal_test.go: method MarshalXML(e *Encoder, start StartElement) error should have signature MarshalXML(*xml.Encoder, xml.StartElement) error
+encoding/xml/read.go: method UnmarshalXML(d *Decoder, start StartElement) error should have signature UnmarshalXML(*xml.Decoder, xml.StartElement) error
+encoding/xml/read_test.go: method UnmarshalXML(d *Decoder, start StartElement) error should have signature UnmarshalXML(*xml.Decoder, xml.StartElement) error
+
+// Lots of false positives from the "large shift" check.
+// Mostly code that uses clever const tricks to determine
+// or use the size of an int or pointer (and related values).
+image/png/paeth.go: x might be too small for shift of 63
+math/big/arith.go: x might be too small for shift of 32
+math/big/arith.go: y might be too small for shift of 32
+math/big/arith.go: w0 might be too small for shift of 32
+math/big/arith.go: t might be too small for shift of 32
+math/big/arith.go: w1 might be too small for shift of 32
+math/big/arith.go: v might be too small for shift of 32
+math/big/arith.go: un10 might be too small for shift of 32
+math/big/arith.go: (xi&yi | (xi|yi)&^zi) might be too small for shift of 63
+math/big/arith.go: (yi&^xi | (yi|^xi)&zi) might be too small for shift of 63
+math/big/arith.go: xi &^ zi might be too small for shift of 63
+math/big/arith.go: (zi &^ xi) might be too small for shift of 63
+math/big/float.go: x[i] might be too small for shift of 32
+math/big/nat.go: t too small for shift of 64
+math/big/nat.go: x too small for shift of 64
+math/big/nat.go: ((x & -x) * (deBruijn64 & _M)) might be too small for shift of 58
+math/big/nat.go: Word(rand.Uint32()) might be too small for shift of 32
+math/big/nat.go: yi might be too small for shift of 60
+math/big/nat.go: yi might be too small for shift of 60
+runtime/cpuprof.go: h might be too small for shift of 56
+runtime/malloc.go: uintptr(i) might be too small for shift of 40
+runtime/malloc.go: uintptr(i) might be too small for shift of 40
+runtime/malloc.go: uintptr(i) might be too small for shift of 40
+sync/atomic/atomic_test.go: uintptr(seed + i) might be too small for shift of 32
+sync/atomic/atomic_test.go: uintptr(seed+i) << 32 might be too small for shift of 32
+sync/atomic/atomic_test.go: uintptr(seed + i) might be too small for shift of 32
+sync/atomic/atomic_test.go: old might be too small for shift of 32
+sync/atomic/atomic_test.go: old << 32 might be too small for shift of 32
+sync/atomic/atomic_test.go: old might be too small for shift of 32
+sync/atomic/atomic_test.go: v might be too small for shift of 32
+sync/atomic/atomic_test.go: v might be too small for shift of 32
+
+// Long struct tags used to test reflect internals
+cmd/link/link_test.go: struct field tag "\n\tLondon. Michaelmas term lately over, and the Lord Chancellor sitting in Lincoln’s Inn Hall. Implacable November weather. As much mud in the streets as if the waters had but newly retired from the face of the earth, and it would not be wonderful to meet a Megalosaurus, forty feet long or so, waddling like an elephantine lizard up Holborn Hill. Smoke lowering down from chimney-pots, making a soft black drizzle, with flakes of soot in it as big a [...]
+cmd/link/link_test.go: struct field tag "\n\tIt was grand to see how the wind awoke, and bent the trees, and drove the rain before it like a cloud of smoke; and to hear the solemn thunder, and to see the lightning; and while thinking with awe of the tremendous powers by which our little lives are encompassed, to consider how beneficent they are, and how upon the smallest flower and leaf there was already a freshness poured from all this seeming rage, which seemed to make creation new aga [...]
+cmd/link/link_test.go: struct field tag "\n\tJarndyce and Jarndyce drones on. This scarecrow of a suit has, over the course of time, become so complicated, that no man alive knows what it means. The parties to it understand it least; but it has been observed that no two Chancery lawyers can talk about it for five minutes, without coming to a total disagreement as to all the premises. Innumerable children have been born into the cause; innumerable young people have married into it; innume [...]
+cmd/link/link_test.go: struct field tag "\n\tThe one great principle of the English law is, to make business for itself. There is no other principle distinctly, certainly, and consistently maintained through all its narrow turnings. Viewed by this light it becomes a coherent scheme, and not the monstrous maze the laity are apt to think it. Let them but once clearly perceive that its grand principle is to make business for itself at their expense, and surely they will cease to grumble." n [...]
diff --git a/src/cmd/vet/all/whitelist/amd64.txt b/src/cmd/vet/all/whitelist/amd64.txt
new file mode 100644
index 0000000..df4ec84
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/amd64.txt
@@ -0,0 +1,35 @@
+// amd64-specific vet whitelist. See readme.txt for details.
+
+
+// False positives.
+
+
+// reflect trampolines intentionally omit arg size. Same for morestack.
+reflect/asm_amd64.s: [amd64] makeFuncStub: use of 8(SP) points beyond argument frame
+reflect/asm_amd64.s: [amd64] methodValueCall: use of 8(SP) points beyond argument frame
+runtime/asm_amd64.s: [amd64] morestack: use of 8(SP) points beyond argument frame
+runtime/asm_amd64.s: [amd64] morestack: use of 16(SP) points beyond argument frame
+runtime/asm_amd64.s: [amd64] morestack: use of 8(SP) points beyond argument frame
+
+// Nothing much to do about cross-package assembly. Unfortunate.
+runtime/asm_amd64.s: [amd64] cannot check cross-package assembly function: indexShortStr is in package strings
+runtime/asm_amd64.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes
+runtime/asm_amd64.s: [amd64] cannot check cross-package assembly function: indexShortStr is in package bytes
+runtime/asm_amd64.s: [amd64] cannot check cross-package assembly function: supportAVX2 is in package strings
+runtime/asm_amd64.s: [amd64] cannot check cross-package assembly function: supportAVX2 is in package bytes
+
+// Intentionally missing declarations. These are special assembly routines.
+// Some are jumped into from other routines, with values in specific registers.
+// duff* have direct calls from the compiler.
+// Others use the platform ABI.
+// There is no sensible corresponding Go prototype.
+runtime/asm_amd64.s: [amd64] aeshashbody: function aeshashbody missing Go declaration
+runtime/asm_amd64.s: [amd64] memeqbody: function memeqbody missing Go declaration
+runtime/asm_amd64.s: [amd64] cmpbody: function cmpbody missing Go declaration
+runtime/asm_amd64.s: [amd64] indexbytebody: function indexbytebody missing Go declaration
+runtime/asm_amd64.s: [amd64] addmoduledata: function addmoduledata missing Go declaration
+runtime/duff_amd64.s: [amd64] duffzero: function duffzero missing Go declaration
+runtime/duff_amd64.s: [amd64] duffcopy: function duffcopy missing Go declaration
+runtime/asm_amd64.s: [amd64] stackcheck: function stackcheck missing Go declaration
+runtime/asm_amd64.s: [amd64] indexShortStr: function indexShortStr missing Go declaration
+
diff --git a/src/cmd/vet/all/whitelist/android_386.txt b/src/cmd/vet/all/whitelist/android_386.txt
new file mode 100644
index 0000000..5095f2f
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/android_386.txt
@@ -0,0 +1,8 @@
+// android/386-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_linux_386.s: [386] setldt: function setldt missing Go declaration
+
+// These SP references occur after a stack-altering call. They're fine.
+runtime/sys_linux_386.s: [386] clone: 12(SP) should be mp+8(FP)
+runtime/sys_linux_386.s: [386] clone: 4(SP) should be flags+0(FP)
+runtime/sys_linux_386.s: [386] clone: 8(SP) should be stk+4(FP)
diff --git a/src/cmd/vet/all/whitelist/android_amd64.txt b/src/cmd/vet/all/whitelist/android_amd64.txt
new file mode 100644
index 0000000..90dabb0
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/android_amd64.txt
@@ -0,0 +1,3 @@
+// android/amd64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_linux_amd64.s: [amd64] settls: function settls missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/android_arm.txt b/src/cmd/vet/all/whitelist/android_arm.txt
new file mode 100644
index 0000000..fbd569e
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/android_arm.txt
@@ -0,0 +1,5 @@
+// android/arm-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_linux_arm.s: [arm] clone: 12(R13) should be stk+4(FP)
+runtime/sys_linux_arm.s: [arm] clone: 8(R13) should be flags+0(FP)
+runtime/sys_linux_arm.s: [arm] read_tls_fallback: function read_tls_fallback missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/arm.txt b/src/cmd/vet/all/whitelist/arm.txt
new file mode 100644
index 0000000..c0ab9de
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/arm.txt
@@ -0,0 +1,26 @@
+// arm-specific vet whitelist. See readme.txt for details.
+
+runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes
+
+// reflect trampolines intentionally omit arg size. Same for morestack.
+reflect/asm_arm.s: [arm] makeFuncStub: use of 8(R13) points beyond argument frame
+reflect/asm_arm.s: [arm] methodValueCall: use of 8(R13) points beyond argument frame
+runtime/asm_arm.s: [arm] morestack: use of 4(R13) points beyond argument frame
+
+// Intentionally missing declarations.
+runtime/asm_arm.s: [arm] emptyfunc: function emptyfunc missing Go declaration
+runtime/asm_arm.s: [arm] abort: function abort missing Go declaration
+runtime/asm_arm.s: [arm] armPublicationBarrier: function armPublicationBarrier missing Go declaration
+runtime/asm_arm.s: [arm] cmpbody: function cmpbody missing Go declaration
+runtime/asm_arm.s: [arm] usplitR0: function usplitR0 missing Go declaration
+runtime/asm_arm.s: [arm] addmoduledata: function addmoduledata missing Go declaration
+runtime/duff_arm.s: [arm] duffzero: function duffzero missing Go declaration
+runtime/duff_arm.s: [arm] duffcopy: function duffcopy missing Go declaration
+runtime/tls_arm.s: [arm] save_g: function save_g missing Go declaration
+runtime/tls_arm.s: [arm] load_g: function load_g missing Go declaration
+runtime/tls_arm.s: [arm] _initcgo: function _initcgo missing Go declaration
+
+// Clearer using FP than SP, but that requires named offsets.
+runtime/asm_arm.s: [arm] rt0_go: use of 4(R13) points beyond argument frame
+
+runtime/internal/atomic/asm_arm.s: [arm] cas: function cas missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/arm64.txt b/src/cmd/vet/all/whitelist/arm64.txt
new file mode 100644
index 0000000..8a3c891
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/arm64.txt
@@ -0,0 +1,17 @@
+// arm64-specific vet whitelist. See readme.txt for details.
+
+runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes
+
+// False positives.
+
+// reflect trampolines intentionally omit arg size. Same for morestack.
+reflect/asm_arm64.s: [arm64] makeFuncStub: use of 16(RSP) points beyond argument frame
+reflect/asm_arm64.s: [arm64] methodValueCall: use of 16(RSP) points beyond argument frame
+
+// Intentionally missing declarations.
+runtime/asm_arm64.s: [arm64] abort: function abort missing Go declaration
+runtime/asm_arm64.s: [arm64] addmoduledata: function addmoduledata missing Go declaration
+runtime/duff_arm64.s: [arm64] duffzero: function duffzero missing Go declaration
+runtime/duff_arm64.s: [arm64] duffcopy: function duffcopy missing Go declaration
+runtime/tls_arm64.s: [arm64] load_g: function load_g missing Go declaration
+runtime/tls_arm64.s: [arm64] save_g: function save_g missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/darwin_386.txt b/src/cmd/vet/all/whitelist/darwin_386.txt
new file mode 100644
index 0000000..c5c51d0
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/darwin_386.txt
@@ -0,0 +1,8 @@
+// darwin/386-specific vet whitelist. See readme.txt for details.
+
+// Ok
+
+runtime/sys_darwin_386.s: [386] now: function now missing Go declaration
+runtime/sys_darwin_386.s: [386] bsdthread_start: function bsdthread_start missing Go declaration
+runtime/sys_darwin_386.s: [386] sysenter: function sysenter missing Go declaration
+runtime/sys_darwin_386.s: [386] setldt: function setldt missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/darwin_amd64.txt b/src/cmd/vet/all/whitelist/darwin_amd64.txt
new file mode 100644
index 0000000..277abd7
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/darwin_amd64.txt
@@ -0,0 +1,4 @@
+// darwin/amd64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_darwin_amd64.s: [amd64] bsdthread_start: function bsdthread_start missing Go declaration
+runtime/sys_darwin_amd64.s: [amd64] settls: function settls missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/darwin_arm.txt b/src/cmd/vet/all/whitelist/darwin_arm.txt
new file mode 100644
index 0000000..0e619be
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/darwin_arm.txt
@@ -0,0 +1,12 @@
+// darwin/arm-specific vet whitelist. See readme.txt for details.
+
+// False positives due to comments in assembly.
+// To be removed. See CL 27154.
+
+runtime/sys_darwin_arm.s: [arm] sigfwd: use of unnamed argument 0(FP); offset 0 is fn+0(FP)
+
+
+// Ok.
+
+runtime/sys_darwin_arm.s: [arm] bsdthread_start: function bsdthread_start missing Go declaration
+runtime/asm_arm.s: [arm] sigreturn: function sigreturn missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/darwin_arm64.txt b/src/cmd/vet/all/whitelist/darwin_arm64.txt
new file mode 100644
index 0000000..080a4ca
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/darwin_arm64.txt
@@ -0,0 +1,14 @@
+// darwin/arm64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_darwin_arm64.s: [arm64] sigtramp: 24(RSP) should be infostyle+8(FP)
+runtime/sys_darwin_arm64.s: [arm64] sigtramp: 24(RSP) should be infostyle+8(FP)
+runtime/sys_darwin_arm64.s: [arm64] bsdthread_create: RET without writing to 4-byte ret+24(FP)
+runtime/sys_darwin_arm64.s: [arm64] bsdthread_start: function bsdthread_start missing Go declaration
+runtime/sys_darwin_arm64.s: [arm64] bsdthread_register: RET without writing to 4-byte ret+0(FP)
+runtime/cgo/signal_darwin_arm64.s: [arm64] panicmem: use of 8(RSP) points beyond argument frame
+runtime/cgo/signal_darwin_arm64.s: [arm64] panicmem: use of 8(RSP) points beyond argument frame
+runtime/cgo/signal_darwin_arm64.s: [arm64] panicmem: use of 16(RSP) points beyond argument frame
+runtime/cgo/signal_darwin_arm64.s: [arm64] panicmem: use of 8(RSP) points beyond argument frame
+runtime/cgo/signal_darwin_arm64.s: [arm64] panicmem: use of 16(RSP) points beyond argument frame
+runtime/cgo/signal_darwin_arm64.s: [arm64] panicmem: use of 16(RSP) points beyond argument frame
+runtime/asm_arm64.s: [arm64] sigreturn: function sigreturn missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/dragonfly_amd64.txt b/src/cmd/vet/all/whitelist/dragonfly_amd64.txt
new file mode 100644
index 0000000..6c44159
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/dragonfly_amd64.txt
@@ -0,0 +1,7 @@
+// dragonfly/amd64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_dragonfly_amd64.s: [amd64] settls: function settls missing Go declaration
+
+syscall/asm9_unix2_amd64.s: [amd64] Syscall9: 8(SP) should be num+0(FP)
+syscall/asm9_unix2_amd64.s: [amd64] Syscall9: 16(SP) should be a1+8(FP)
+syscall/asm9_unix2_amd64.s: [amd64] Syscall9: 24(SP) should be a2+16(FP)
diff --git a/src/cmd/vet/all/whitelist/freebsd_386.txt b/src/cmd/vet/all/whitelist/freebsd_386.txt
new file mode 100644
index 0000000..d37132c
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/freebsd_386.txt
@@ -0,0 +1,19 @@
+// freebsd/386-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_freebsd_386.s: [386] thr_start: unknown variable mm
+runtime/sys_freebsd_386.s: [386] sigtramp: unknown variable signo
+runtime/sys_freebsd_386.s: [386] sigtramp: unknown variable info
+runtime/sys_freebsd_386.s: [386] sigtramp: unknown variable context
+runtime/sys_freebsd_386.s: [386] sigtramp: unknown variable context
+runtime/sys_freebsd_386.s: [386] setldt: function setldt missing Go declaration
+runtime/sys_freebsd_386.s: [386] i386_set_ldt: function i386_set_ldt missing Go declaration
+syscall/asm_unix_386.s: [386] Syscall: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] Syscall: 4(SP) should be trap+0(FP)
+syscall/asm_unix_386.s: [386] Syscall6: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] Syscall6: 4(SP) should be trap+0(FP)
+syscall/asm_unix_386.s: [386] Syscall9: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] Syscall9: 4(SP) should be num+0(FP)
+syscall/asm_unix_386.s: [386] RawSyscall: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] RawSyscall: 4(SP) should be trap+0(FP)
+syscall/asm_unix_386.s: [386] RawSyscall6: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] RawSyscall6: 4(SP) should be trap+0(FP)
diff --git a/src/cmd/vet/all/whitelist/freebsd_amd64.txt b/src/cmd/vet/all/whitelist/freebsd_amd64.txt
new file mode 100644
index 0000000..a910f48
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/freebsd_amd64.txt
@@ -0,0 +1,6 @@
+// freebsd/amd64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_freebsd_amd64.s: [amd64] settls: function settls missing Go declaration
+syscall/asm9_unix2_amd64.s: [amd64] Syscall9: 8(SP) should be num+0(FP)
+syscall/asm9_unix2_amd64.s: [amd64] Syscall9: 16(SP) should be a1+8(FP)
+syscall/asm9_unix2_amd64.s: [amd64] Syscall9: 24(SP) should be a2+16(FP)
diff --git a/src/cmd/vet/all/whitelist/freebsd_arm.txt b/src/cmd/vet/all/whitelist/freebsd_arm.txt
new file mode 100644
index 0000000..11e5c42
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/freebsd_arm.txt
@@ -0,0 +1,4 @@
+// freebsd/arm-specific vet whitelist. See readme.txt for details.
+
+runtime/asm_arm.s: [arm] sigreturn: function sigreturn missing Go declaration
+runtime/sys_freebsd_arm.s: [arm] read_tls_fallback: function read_tls_fallback missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/linux_386.txt b/src/cmd/vet/all/whitelist/linux_386.txt
new file mode 100644
index 0000000..a5111ca
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/linux_386.txt
@@ -0,0 +1,13 @@
+// linux/386-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_linux_386.s: [386] setldt: function setldt missing Go declaration
+
+// These SP references occur after a stack-altering call. They're fine.
+runtime/sys_linux_386.s: [386] clone: 12(SP) should be mp+8(FP)
+runtime/sys_linux_386.s: [386] clone: 4(SP) should be flags+0(FP)
+runtime/sys_linux_386.s: [386] clone: 8(SP) should be stk+4(FP)
+
+// Android-specific; stubs missing on other linux platforms.
+runtime/sys_linux_386.s: [386] access: function access missing Go declaration
+runtime/sys_linux_386.s: [386] connect: function connect missing Go declaration
+runtime/sys_linux_386.s: [386] socket: function socket missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/linux_amd64.txt b/src/cmd/vet/all/whitelist/linux_amd64.txt
new file mode 100644
index 0000000..69ba65d
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/linux_amd64.txt
@@ -0,0 +1,8 @@
+// linux/amd64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_linux_amd64.s: [amd64] settls: function settls missing Go declaration
+
+// Android-specific; stubs missing on other linux platforms.
+runtime/sys_linux_amd64.s: [amd64] access: function access missing Go declaration
+runtime/sys_linux_amd64.s: [amd64] connect: function connect missing Go declaration
+runtime/sys_linux_amd64.s: [amd64] socket: function socket missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/linux_arm.txt b/src/cmd/vet/all/whitelist/linux_arm.txt
new file mode 100644
index 0000000..fbf0e27
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/linux_arm.txt
@@ -0,0 +1,12 @@
+// linux/arm-specific vet whitelist. See readme.txt for details.
+
+
+// These SP references occur after a stack-altering call. They're fine.
+runtime/sys_linux_arm.s: [arm] clone: 12(R13) should be stk+4(FP)
+runtime/sys_linux_arm.s: [arm] clone: 8(R13) should be flags+0(FP)
+
+// Special functions.
+runtime/sys_linux_arm.s: [arm] read_tls_fallback: function read_tls_fallback missing Go declaration
+runtime/sys_linux_arm.s: [arm] access: function access missing Go declaration
+runtime/sys_linux_arm.s: [arm] connect: function connect missing Go declaration
+runtime/sys_linux_arm.s: [arm] socket: function socket missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/linux_arm64.txt b/src/cmd/vet/all/whitelist/linux_arm64.txt
new file mode 100644
index 0000000..67280b7
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/linux_arm64.txt
@@ -0,0 +1,5 @@
+// linux/arm64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_linux_arm64.s: [arm64] access: function access missing Go declaration
+runtime/sys_linux_arm64.s: [arm64] connect: function connect missing Go declaration
+runtime/sys_linux_arm64.s: [arm64] socket: function socket missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/linux_ppc64x.txt b/src/cmd/vet/all/whitelist/linux_ppc64x.txt
new file mode 100644
index 0000000..21e87e3
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/linux_ppc64x.txt
@@ -0,0 +1,5 @@
+// linux/ppc64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_linux_ppc64x.s: [GOARCH] _sigtramp: function _sigtramp missing Go declaration
+runtime/sys_linux_ppc64x.s: [GOARCH] _cgoSigtramp: function _cgoSigtramp missing Go declaration
+runtime/asm_ppc64x.s: [GOARCH] procyield: use of 24(R1) points beyond argument frame
diff --git a/src/cmd/vet/all/whitelist/mips64x.txt b/src/cmd/vet/all/whitelist/mips64x.txt
new file mode 100644
index 0000000..b29cf3e
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/mips64x.txt
@@ -0,0 +1,8 @@
+// mips64-specific vet whitelist. See readme.txt for details.
+
+reflect/asm_mips64x.s: [GOARCH] makeFuncStub: use of 16(R29) points beyond argument frame
+reflect/asm_mips64x.s: [GOARCH] methodValueCall: use of 16(R29) points beyond argument frame
+runtime/asm_mips64x.s: [GOARCH] abort: function abort missing Go declaration
+runtime/duff_mips64x.s: [GOARCH] duffzero: function duffzero missing Go declaration
+runtime/tls_mips64x.s: [GOARCH] save_g: function save_g missing Go declaration
+runtime/tls_mips64x.s: [GOARCH] load_g: function load_g missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/nacl_386.txt b/src/cmd/vet/all/whitelist/nacl_386.txt
new file mode 100644
index 0000000..68bba51
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/nacl_386.txt
@@ -0,0 +1,13 @@
+// nacl/386-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_nacl_386.s: [386] cannot check cross-package assembly function: naclWrite is in package syscall
+runtime/sys_nacl_386.s: [386] cannot check cross-package assembly function: now is in package syscall
+runtime/sys_nacl_386.s: [386] nacl_clock_gettime: function nacl_clock_gettime missing Go declaration
+runtime/sys_nacl_386.s: [386] setldt: function setldt missing Go declaration
+runtime/sys_nacl_386.s: [386] sigtramp: use of 20(SP) points beyond argument frame
+runtime/sys_nacl_386.s: [386] sigtramp: use of 4(SP) points beyond argument frame
+runtime/sys_nacl_386.s: [386] sigtramp: unknown variable ctxt
+runtime/sys_nacl_386.s: [386] sigtramp: use of 8(SP) points beyond argument frame
+runtime/sys_nacl_386.s: [386] sigtramp: use of 12(SP) points beyond argument frame
+runtime/sys_nacl_386.s: [386] sigtramp: use of 20(SP) points beyond argument frame
+runtime/sys_nacl_386.s: [386] sigtramp: unknown variable ctxt
diff --git a/src/cmd/vet/all/whitelist/nacl_amd64p32.txt b/src/cmd/vet/all/whitelist/nacl_amd64p32.txt
new file mode 100644
index 0000000..83bcfe9
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/nacl_amd64p32.txt
@@ -0,0 +1,31 @@
+// nacl/amd64p32-specific vet whitelist. See readme.txt for details.
+
+// reflect trampolines intentionally omit arg size. Same for morestack.
+reflect/asm_amd64p32.s: [amd64p32] makeFuncStub: use of 4(SP) points beyond argument frame
+reflect/asm_amd64p32.s: [amd64p32] methodValueCall: use of 4(SP) points beyond argument frame
+runtime/asm_amd64p32.s: [amd64p32] morestack: use of 8(SP) points beyond argument frame
+runtime/asm_amd64p32.s: [amd64p32] morestack: use of 16(SP) points beyond argument frame
+runtime/asm_amd64p32.s: [amd64p32] morestack: use of 8(SP) points beyond argument frame
+
+runtime/sys_nacl_amd64p32.s: [amd64p32] sigtramp: unknown variable ctxt
+runtime/sys_nacl_amd64p32.s: [amd64p32] sigtramp: unknown variable ctxt
+runtime/sys_nacl_amd64p32.s: [amd64p32] sigtramp: unknown variable ctxt
+runtime/sys_nacl_amd64p32.s: [amd64p32] sigtramp: unknown variable ctxt
+runtime/sys_nacl_amd64p32.s: [amd64p32] sigtramp: unknown variable ctxt
+runtime/sys_nacl_amd64p32.s: [amd64p32] nacl_sysinfo: function nacl_sysinfo missing Go declaration
+runtime/sys_nacl_amd64p32.s: [amd64p32] cannot check cross-package assembly function: naclWrite is in package syscall
+runtime/sys_nacl_amd64p32.s: [amd64p32] cannot check cross-package assembly function: now is in package syscall
+runtime/sys_nacl_amd64p32.s: [amd64p32] nacl_clock_gettime: function nacl_clock_gettime missing Go declaration
+runtime/sys_nacl_amd64p32.s: [amd64p32] settls: function settls missing Go declaration
+
+// Clearer using FP than SP, but that requires named offsets.
+runtime/asm_amd64p32.s: [amd64p32] rt0_go: unknown variable argc
+runtime/asm_amd64p32.s: [amd64p32] rt0_go: unknown variable argv
+
+runtime/asm_amd64p32.s: [amd64p32] memeqbody: function memeqbody missing Go declaration
+runtime/asm_amd64p32.s: [amd64p32] cannot check cross-package assembly function: Compare is in package bytes
+runtime/asm_amd64p32.s: [amd64p32] cmpbody: function cmpbody missing Go declaration
+runtime/asm_amd64p32.s: [amd64p32] indexbytebody: function indexbytebody missing Go declaration
+runtime/asm_amd64p32.s: [amd64p32] asmcgocall: RET without writing to 4-byte ret+8(FP)
+
+runtime/asm_amd64p32.s: [amd64p32] stackcheck: function stackcheck missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/nacl_arm.txt b/src/cmd/vet/all/whitelist/nacl_arm.txt
new file mode 100644
index 0000000..cc0fcba
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/nacl_arm.txt
@@ -0,0 +1,8 @@
+// nacl/arm-specific vet whitelist. See readme.txt for details.
+
+runtime/asm_arm.s: [arm] sigreturn: function sigreturn missing Go declaration
+runtime/sys_nacl_arm.s: [arm] cannot check cross-package assembly function: naclWrite is in package syscall
+runtime/sys_nacl_arm.s: [arm] cannot check cross-package assembly function: now is in package syscall
+runtime/sys_nacl_arm.s: [arm] nacl_clock_gettime: function nacl_clock_gettime missing Go declaration
+runtime/sys_nacl_arm.s: [arm] nacl_sysinfo: function nacl_sysinfo missing Go declaration
+runtime/sys_nacl_arm.s: [arm] read_tls_fallback: function read_tls_fallback missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/netbsd.txt b/src/cmd/vet/all/whitelist/netbsd.txt
new file mode 100644
index 0000000..48bfde5
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/netbsd.txt
@@ -0,0 +1,3 @@
+// netbsd-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_netbsd_ARCHSUFF.s: [GOARCH] sigreturn_tramp: function sigreturn_tramp missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/netbsd_386.txt b/src/cmd/vet/all/whitelist/netbsd_386.txt
new file mode 100644
index 0000000..1d1f323
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/netbsd_386.txt
@@ -0,0 +1,23 @@
+// netbsd/386-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_netbsd_ARCHSUFF.s: [GOARCH] settls: function settls missing Go declaration
+
+runtime/sys_netbsd_386.s: [386] sigreturn_tramp: use of 140(SP) points beyond argument frame
+runtime/sys_netbsd_386.s: [386] sigreturn_tramp: use of 4(SP) points beyond argument frame
+runtime/sys_netbsd_386.s: [386] sigreturn_tramp: use of 4(SP) points beyond argument frame
+runtime/sys_netbsd_386.s: [386] sigtramp: unknown variable signo
+runtime/sys_netbsd_386.s: [386] sigtramp: unknown variable info
+runtime/sys_netbsd_386.s: [386] sigtramp: unknown variable context
+runtime/sys_netbsd_386.s: [386] setldt: function setldt missing Go declaration
+runtime/sys_netbsd_386.s: [386] setldt: use of 16(SP) points beyond argument frame
+
+syscall/asm_unix_386.s: [386] Syscall: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] Syscall: 4(SP) should be trap+0(FP)
+syscall/asm_unix_386.s: [386] Syscall6: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] Syscall6: 4(SP) should be trap+0(FP)
+syscall/asm_unix_386.s: [386] Syscall9: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] Syscall9: 4(SP) should be num+0(FP)
+syscall/asm_unix_386.s: [386] RawSyscall: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] RawSyscall: 4(SP) should be trap+0(FP)
+syscall/asm_unix_386.s: [386] RawSyscall6: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] RawSyscall6: 4(SP) should be trap+0(FP)
diff --git a/src/cmd/vet/all/whitelist/netbsd_amd64.txt b/src/cmd/vet/all/whitelist/netbsd_amd64.txt
new file mode 100644
index 0000000..8b14dc5
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/netbsd_amd64.txt
@@ -0,0 +1,3 @@
+// netbsd/amd64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_netbsd_amd64.s: [amd64] settls: function settls missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/netbsd_arm.txt b/src/cmd/vet/all/whitelist/netbsd_arm.txt
new file mode 100644
index 0000000..c0a0aa2
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/netbsd_arm.txt
@@ -0,0 +1,5 @@
+// netbsd/arm-specific vet whitelist. See readme.txt for details.
+
+runtime/asm_arm.s: [arm] sigreturn: function sigreturn missing Go declaration
+runtime/sys_netbsd_arm.s: [arm] read_tls_fallback: function read_tls_fallback missing Go declaration
+syscall/asm_netbsd_arm.s: [arm] Syscall9: unknown variable trap; offset 0 is num+0(FP)
diff --git a/src/cmd/vet/all/whitelist/openbsd_386.txt b/src/cmd/vet/all/whitelist/openbsd_386.txt
new file mode 100644
index 0000000..b5c0a73
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/openbsd_386.txt
@@ -0,0 +1,17 @@
+// openbsd/386-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_openbsd_386.s: [386] sigtramp: unknown variable signo
+runtime/sys_openbsd_386.s: [386] sigtramp: unknown variable info
+runtime/sys_openbsd_386.s: [386] sigtramp: unknown variable context
+runtime/sys_openbsd_386.s: [386] setldt: function setldt missing Go declaration
+runtime/sys_openbsd_386.s: [386] settls: function settls missing Go declaration
+syscall/asm_unix_386.s: [386] Syscall: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] Syscall: 4(SP) should be trap+0(FP)
+syscall/asm_unix_386.s: [386] Syscall6: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] Syscall6: 4(SP) should be trap+0(FP)
+syscall/asm_unix_386.s: [386] Syscall9: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] Syscall9: 4(SP) should be num+0(FP)
+syscall/asm_unix_386.s: [386] RawSyscall: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] RawSyscall: 4(SP) should be trap+0(FP)
+syscall/asm_unix_386.s: [386] RawSyscall6: 8(SP) should be a1+4(FP)
+syscall/asm_unix_386.s: [386] RawSyscall6: 4(SP) should be trap+0(FP)
diff --git a/src/cmd/vet/all/whitelist/openbsd_amd64.txt b/src/cmd/vet/all/whitelist/openbsd_amd64.txt
new file mode 100644
index 0000000..433f62c
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/openbsd_amd64.txt
@@ -0,0 +1,3 @@
+// openbsd/amd64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_openbsd_amd64.s: [amd64] settls: function settls missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/openbsd_arm.txt b/src/cmd/vet/all/whitelist/openbsd_arm.txt
new file mode 100644
index 0000000..16bf26c
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/openbsd_arm.txt
@@ -0,0 +1,4 @@
+// openbsd/arm-specific vet whitelist. See readme.txt for details.
+
+runtime/asm_arm.s: [arm] sigreturn: function sigreturn missing Go declaration
+runtime/sys_openbsd_arm.s: [arm] read_tls_fallback: function read_tls_fallback missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/plan9_386.txt b/src/cmd/vet/all/whitelist/plan9_386.txt
new file mode 100644
index 0000000..1531161
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/plan9_386.txt
@@ -0,0 +1,3 @@
+// plan9/386-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_plan9_386.s: [386] setldt: function setldt missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/plan9_amd64.txt b/src/cmd/vet/all/whitelist/plan9_amd64.txt
new file mode 100644
index 0000000..39fc8e2
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/plan9_amd64.txt
@@ -0,0 +1,4 @@
+// plan9/amd64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_plan9_amd64.s: [amd64] setldt: function setldt missing Go declaration
+runtime/sys_plan9_amd64.s: [amd64] settls: function settls missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/plan9_arm.txt b/src/cmd/vet/all/whitelist/plan9_arm.txt
new file mode 100644
index 0000000..5af3271
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/plan9_arm.txt
@@ -0,0 +1,4 @@
+// plan9/arm-specific vet whitelist. See readme.txt for details.
+
+runtime/asm_arm.s: [arm] sigreturn: function sigreturn missing Go declaration
+runtime/sys_plan9_arm.s: [arm] read_tls_fallback: function read_tls_fallback missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/ppc64x.txt b/src/cmd/vet/all/whitelist/ppc64x.txt
new file mode 100644
index 0000000..4f6444e
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/ppc64x.txt
@@ -0,0 +1,12 @@
+// ppc64-specific vet whitelist. See readme.txt for details.
+
+runtime/asm_ARCHSUFF.s: [GOARCH] cannot check cross-package assembly function: Compare is in package bytes
+
+runtime/asm_ppc64x.s: [GOARCH] reginit: function reginit missing Go declaration
+runtime/asm_ppc64x.s: [GOARCH] abort: function abort missing Go declaration
+runtime/asm_ppc64x.s: [GOARCH] memeqbody: function memeqbody missing Go declaration
+runtime/asm_ppc64x.s: [GOARCH] goexit: use of 24(R1) points beyond argument frame
+runtime/asm_ppc64x.s: [GOARCH] addmoduledata: function addmoduledata missing Go declaration
+runtime/duff_ppc64x.s: [GOARCH] duffzero: function duffzero missing Go declaration
+runtime/tls_ppc64x.s: [GOARCH] save_g: function save_g missing Go declaration
+runtime/tls_ppc64x.s: [GOARCH] load_g: function load_g missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/readme.txt b/src/cmd/vet/all/whitelist/readme.txt
new file mode 100644
index 0000000..4f83757
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/readme.txt
@@ -0,0 +1,4 @@
+This directory contains whitelists for vet complaints about the standard library and commands.
+They are line-based and unordered, although counts of duplicated lines matter.
+Each line matches vet's output, except that line numbers are removed to avoid churn.
+There are also os-, arch-, and bitwidth-specific whitelists.
diff --git a/src/cmd/vet/all/whitelist/s390x.txt b/src/cmd/vet/all/whitelist/s390x.txt
new file mode 100644
index 0000000..875835e
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/s390x.txt
@@ -0,0 +1,19 @@
+reflect/asm_s390x.s: [s390x] makeFuncStub: use of 16(R15) points beyond argument frame
+reflect/asm_s390x.s: [s390x] methodValueCall: use of 16(R15) points beyond argument frame
+runtime/asm_s390x.s: [s390x] abort: function abort missing Go declaration
+runtime/asm_s390x.s: [s390x] memeqbody: function memeqbody missing Go declaration
+runtime/asm_s390x.s: [s390x] memeqbodyclc: function memeqbodyclc missing Go declaration
+runtime/asm_s390x.s: [s390x] indexbytebody: function indexbytebody missing Go declaration
+runtime/asm_s390x.s: [s390x] cannot check cross-package assembly function: Compare is in package bytes
+runtime/asm_s390x.s: [s390x] cmpbody: function cmpbody missing Go declaration
+runtime/asm_s390x.s: [s390x] cmpbodyclc: function cmpbodyclc missing Go declaration
+runtime/asm_s390x.s: [s390x] cannot check cross-package assembly function: supportsVX is in package strings
+runtime/asm_s390x.s: [s390x] cannot check cross-package assembly function: supportsVX is in package bytes
+runtime/asm_s390x.s: [s390x] cannot check cross-package assembly function: indexShortStr is in package strings
+runtime/asm_s390x.s: [s390x] cannot check cross-package assembly function: indexShortStr is in package bytes
+runtime/asm_s390x.s: [s390x] indexShortStr: function indexShortStr missing Go declaration
+runtime/asm_s390x.s: [s390x] addmoduledata: function addmoduledata missing Go declaration
+runtime/memclr_s390x.s: [s390x] memclr_s390x_exrl_xc: function memclr_s390x_exrl_xc missing Go declaration
+runtime/memmove_s390x.s: [s390x] memmove_s390x_exrl_mvc: function memmove_s390x_exrl_mvc missing Go declaration
+runtime/tls_s390x.s: [s390x] save_g: function save_g missing Go declaration
+runtime/tls_s390x.s: [s390x] load_g: function load_g missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/solaris_amd64.txt b/src/cmd/vet/all/whitelist/solaris_amd64.txt
new file mode 100644
index 0000000..26a9da4
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/solaris_amd64.txt
@@ -0,0 +1,6 @@
+// solaris/amd64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_solaris_amd64.s: [amd64] settls: function settls missing Go declaration
+runtime/sys_solaris_amd64.s: [amd64] pipe1: function pipe1 missing Go declaration
+runtime/sys_solaris_amd64.s: [amd64] asmsysvicall6: function asmsysvicall6 missing Go declaration
+runtime/sys_solaris_amd64.s: [amd64] usleep2: function usleep2 missing Go declaration
diff --git a/src/cmd/vet/all/whitelist/windows.txt b/src/cmd/vet/all/whitelist/windows.txt
new file mode 100644
index 0000000..e80a92f
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/windows.txt
@@ -0,0 +1,5 @@
+// windows-specific vet whitelist. See readme.txt for details.
+
+path/filepath/path_windows_test.go: possible formatting directive in Fatal call
+runtime/sys_windows_ARCHSUFF.s: [GOARCH] sigtramp: function sigtramp missing Go declaration
+runtime/sys_windows_ARCHSUFF.s: [GOARCH] onosstack: unknown variable usec; offset 0 is fn+0(FP)
diff --git a/src/cmd/vet/all/whitelist/windows_386.txt b/src/cmd/vet/all/whitelist/windows_386.txt
new file mode 100644
index 0000000..7a6d23f
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/windows_386.txt
@@ -0,0 +1,9 @@
+// windows/386-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_windows_386.s: [386] profileloop: use of 4(SP) points beyond argument frame
+runtime/sys_windows_386.s: [386] ctrlhandler: 4(SP) should be _type+0(FP)
+runtime/sys_windows_386.s: [386] setldt: function setldt missing Go declaration
+runtime/zcallback_windows.s: [386] callbackasm: function callbackasm missing Go declaration
+runtime/sys_windows_386.s: [386] callbackasm1+0: function callbackasm1+0 missing Go declaration
+runtime/sys_windows_386.s: [386] tstart: function tstart missing Go declaration
+runtime/sys_windows_386.s: [386] tstart_stdcall: RET without writing to 4-byte ret+4(FP)
diff --git a/src/cmd/vet/all/whitelist/windows_amd64.txt b/src/cmd/vet/all/whitelist/windows_amd64.txt
new file mode 100644
index 0000000..a2e1844
--- /dev/null
+++ b/src/cmd/vet/all/whitelist/windows_amd64.txt
@@ -0,0 +1,8 @@
+// windows/amd64-specific vet whitelist. See readme.txt for details.
+
+runtime/sys_windows_amd64.s: [amd64] ctrlhandler: 16(SP) should be _type+0(FP)
+runtime/sys_windows_amd64.s: [amd64] ctrlhandler: RET without writing to 4-byte ret+8(FP)
+runtime/sys_windows_amd64.s: [amd64] callbackasm1: function callbackasm1 missing Go declaration
+runtime/sys_windows_amd64.s: [amd64] tstart_stdcall: RET without writing to 4-byte ret+8(FP)
+runtime/sys_windows_amd64.s: [amd64] settls: function settls missing Go declaration
+runtime/zcallback_windows.s: [amd64] callbackasm: function callbackasm missing Go declaration
diff --git a/src/cmd/vet/asmdecl.go b/src/cmd/vet/asmdecl.go
index d543b2e..a516cc4 100644
--- a/src/cmd/vet/asmdecl.go
+++ b/src/cmd/vet/asmdecl.go
@@ -10,7 +10,9 @@ import (
 	"bytes"
 	"fmt"
 	"go/ast"
+	"go/build"
 	"go/token"
+	"go/types"
 	"regexp"
 	"strconv"
 	"strings"
@@ -24,16 +26,17 @@ type asmKind int
 const (
 	asmString asmKind = 100 + iota
 	asmSlice
+	asmArray
 	asmInterface
 	asmEmptyInterface
+	asmStruct
+	asmComplex
 )
 
 // An asmArch describes assembly parameters for an architecture
 type asmArch struct {
 	name      string
-	ptrSize   int
-	intSize   int
-	maxAlign  int
+	sizes     *types.StdSizes
 	bigEndian bool
 	stack     string
 	lr        bool
@@ -57,16 +60,26 @@ type asmVar struct {
 	inner []*asmVar
 }
 
+// Common architecture word sizes and alignments.
 var (
-	asmArch386      = asmArch{"386", 4, 4, 4, false, "SP", false}
-	asmArchArm      = asmArch{"arm", 4, 4, 4, false, "R13", true}
-	asmArchArm64    = asmArch{"arm64", 8, 8, 8, false, "RSP", true}
-	asmArchAmd64    = asmArch{"amd64", 8, 8, 8, false, "SP", false}
-	asmArchAmd64p32 = asmArch{"amd64p32", 4, 4, 8, false, "SP", false}
-	asmArchMips64   = asmArch{"mips64", 8, 8, 8, true, "R29", true}
-	asmArchMips64LE = asmArch{"mips64", 8, 8, 8, false, "R29", true}
-	asmArchPpc64    = asmArch{"ppc64", 8, 8, 8, true, "R1", true}
-	asmArchPpc64LE  = asmArch{"ppc64le", 8, 8, 8, false, "R1", true}
+	size44 = &types.StdSizes{WordSize: 4, MaxAlign: 4}
+	size48 = &types.StdSizes{WordSize: 4, MaxAlign: 8}
+	size88 = &types.StdSizes{WordSize: 8, MaxAlign: 8}
+)
+
+var (
+	asmArch386      = asmArch{"386", size44, false, "SP", false}
+	asmArchArm      = asmArch{"arm", size44, false, "R13", true}
+	asmArchArm64    = asmArch{"arm64", size88, false, "RSP", true}
+	asmArchAmd64    = asmArch{"amd64", size88, false, "SP", false}
+	asmArchAmd64p32 = asmArch{"amd64p32", size48, false, "SP", false}
+	asmArchMips     = asmArch{"mips", size44, true, "R29", true}
+	asmArchMipsLE   = asmArch{"mipsle", size44, false, "R29", true}
+	asmArchMips64   = asmArch{"mips64", size88, true, "R29", true}
+	asmArchMips64LE = asmArch{"mips64le", size88, false, "R29", true}
+	asmArchPpc64    = asmArch{"ppc64", size88, true, "R1", true}
+	asmArchPpc64LE  = asmArch{"ppc64le", size88, false, "R1", true}
+	asmArchS390X    = asmArch{"s390x", size88, true, "R15", true}
 
 	arches = []*asmArch{
 		&asmArch386,
@@ -74,17 +87,24 @@ var (
 		&asmArchArm64,
 		&asmArchAmd64,
 		&asmArchAmd64p32,
+		&asmArchMips,
+		&asmArchMipsLE,
 		&asmArchMips64,
 		&asmArchMips64LE,
 		&asmArchPpc64,
 		&asmArchPpc64LE,
+		&asmArchS390X,
 	}
 )
 
+func (a *asmArch) intSize() int  { return int(a.sizes.WordSize) }
+func (a *asmArch) ptrSize() int  { return int(a.sizes.WordSize) }
+func (a *asmArch) maxAlign() int { return int(a.sizes.MaxAlign) }
+
 var (
 	re           = regexp.MustCompile
 	asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`)
-	asmTEXT      = re(`\bTEXT\b.*·([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`)
+	asmTEXT      = re(`\bTEXT\b(.*)·([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`)
 	asmDATA      = re(`\b(DATA|GLOBL)\b`)
 	asmNamedFP   = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`)
 	asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`)
@@ -163,40 +183,73 @@ Files:
 			if arch == "" {
 				// Determine architecture from +build line if possible.
 				if m := asmPlusBuild.FindStringSubmatch(line); m != nil {
-				Fields:
+					// There can be multiple architectures in a single +build line,
+					// so accumulate them all and then prefer the one that
+					// matches build.Default.GOARCH.
+					var archCandidates []*asmArch
 					for _, fld := range strings.Fields(m[1]) {
 						for _, a := range arches {
 							if a.name == fld {
-								arch = a.name
-								archDef = a
-								break Fields
+								archCandidates = append(archCandidates, a)
 							}
 						}
 					}
+					for _, a := range archCandidates {
+						if a.name == build.Default.GOARCH {
+							archCandidates = []*asmArch{a}
+							break
+						}
+					}
+					if len(archCandidates) > 0 {
+						arch = archCandidates[0].name
+						archDef = archCandidates[0]
+					}
 				}
 			}
 
 			if m := asmTEXT.FindStringSubmatch(line); m != nil {
 				flushRet()
 				if arch == "" {
-					f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name)
-					continue Files
+					// Arch not specified by filename or build tags.
+					// Fall back to build.Default.GOARCH.
+					for _, a := range arches {
+						if a.name == build.Default.GOARCH {
+							arch = a.name
+							archDef = a
+							break
+						}
+					}
+					if arch == "" {
+						f.Warnf(token.NoPos, "%s: cannot determine architecture for assembly file", f.name)
+						continue Files
+					}
+				}
+				fnName = m[2]
+				if pkgName := strings.TrimSpace(m[1]); pkgName != "" {
+					pathParts := strings.Split(pkgName, "∕")
+					pkgName = pathParts[len(pathParts)-1]
+					if pkgName != f.pkg.path {
+						f.Warnf(token.NoPos, "%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", f.name, lineno, arch, fnName, pkgName)
+						fn = nil
+						fnName = ""
+						continue
+					}
 				}
-				fnName = m[1]
-				fn = knownFunc[m[1]][arch]
+				fn = knownFunc[fnName][arch]
 				if fn != nil {
-					size, _ := strconv.Atoi(m[4])
-					if size != fn.size && (m[2] != "7" && !strings.Contains(m[2], "NOSPLIT") || size != 0) {
+					size, _ := strconv.Atoi(m[5])
+					flag := m[3]
+					if size != fn.size && (flag != "7" && !strings.Contains(flag, "NOSPLIT") || size != 0) {
 						badf("wrong argument size %d; expected $...-%d", size, fn.size)
 					}
 				}
-				localSize, _ = strconv.Atoi(m[3])
-				localSize += archDef.intSize
+				localSize, _ = strconv.Atoi(m[4])
+				localSize += archDef.intSize()
 				if archDef.lr {
 					// Account for caller's saved LR
-					localSize += archDef.intSize
+					localSize += archDef.intSize()
 				}
-				argSize, _ = strconv.Atoi(m[4])
+				argSize, _ = strconv.Atoi(m[5])
 				if fn == nil && !strings.Contains(fnName, "<>") {
 					badf("function %s missing Go declaration", fnName)
 				}
@@ -300,199 +353,179 @@ Files:
 	}
 }
 
+func asmKindForType(t types.Type, size int) asmKind {
+	switch t := t.Underlying().(type) {
+	case *types.Basic:
+		switch t.Kind() {
+		case types.String:
+			return asmString
+		case types.Complex64, types.Complex128:
+			return asmComplex
+		}
+		return asmKind(size)
+	case *types.Pointer, *types.Chan, *types.Map, *types.Signature:
+		return asmKind(size)
+	case *types.Struct:
+		return asmStruct
+	case *types.Interface:
+		if t.Empty() {
+			return asmEmptyInterface
+		}
+		return asmInterface
+	case *types.Array:
+		return asmArray
+	case *types.Slice:
+		return asmSlice
+	}
+	panic("unreachable")
+}
+
+// A component is an assembly-addressable component of a composite type,
+// or a composite type itself.
+type component struct {
+	size   int
+	offset int
+	kind   asmKind
+	typ    string
+	suffix string // Such as _base for string base, _0_lo for lo half of first element of [1]uint64 on 32 bit machine.
+	outer  string // The suffix for immediately containing composite type.
+}
+
+func newComponent(suffix string, kind asmKind, typ string, offset, size int, outer string) component {
+	return component{suffix: suffix, kind: kind, typ: typ, offset: offset, size: size, outer: outer}
+}
+
+// componentsOfType generates a list of components of type t.
+// For example, given string, the components are the string itself, the base, and the length.
+func componentsOfType(arch *asmArch, t types.Type) []component {
+	return appendComponentsRecursive(arch, t, nil, "", 0)
+}
+
+// appendComponentsRecursive implements componentsOfType.
+// Recursion is required to correct handle structs and arrays,
+// which can contain arbitrary other types.
+func appendComponentsRecursive(arch *asmArch, t types.Type, cc []component, suffix string, off int) []component {
+	s := t.String()
+	size := int(arch.sizes.Sizeof(t))
+	kind := asmKindForType(t, size)
+	cc = append(cc, newComponent(suffix, kind, s, off, size, suffix))
+
+	switch kind {
+	case 8:
+		if arch.ptrSize() == 4 {
+			w1, w2 := "lo", "hi"
+			if arch.bigEndian {
+				w1, w2 = w2, w1
+			}
+			cc = append(cc, newComponent(suffix+"_"+w1, 4, "half "+s, off, 4, suffix))
+			cc = append(cc, newComponent(suffix+"_"+w2, 4, "half "+s, off+4, 4, suffix))
+		}
+
+	case asmEmptyInterface:
+		cc = append(cc, newComponent(suffix+"_type", asmKind(arch.ptrSize()), "interface type", off, arch.ptrSize(), suffix))
+		cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize()), "interface data", off+arch.ptrSize(), arch.ptrSize(), suffix))
+
+	case asmInterface:
+		cc = append(cc, newComponent(suffix+"_itable", asmKind(arch.ptrSize()), "interface itable", off, arch.ptrSize(), suffix))
+		cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize()), "interface data", off+arch.ptrSize(), arch.ptrSize(), suffix))
+
+	case asmSlice:
+		cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize()), "slice base", off, arch.ptrSize(), suffix))
+		cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize()), "slice len", off+arch.ptrSize(), arch.intSize(), suffix))
+		cc = append(cc, newComponent(suffix+"_cap", asmKind(arch.intSize()), "slice cap", off+arch.ptrSize()+arch.intSize(), arch.intSize(), suffix))
+
+	case asmString:
+		cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize()), "string base", off, arch.ptrSize(), suffix))
+		cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize()), "string len", off+arch.ptrSize(), arch.intSize(), suffix))
+
+	case asmComplex:
+		fsize := size / 2
+		cc = append(cc, newComponent(suffix+"_real", asmKind(fsize), fmt.Sprintf("real(complex%d)", size*8), off, fsize, suffix))
+		cc = append(cc, newComponent(suffix+"_imag", asmKind(fsize), fmt.Sprintf("imag(complex%d)", size*8), off+fsize, fsize, suffix))
+
+	case asmStruct:
+		tu := t.Underlying().(*types.Struct)
+		fields := make([]*types.Var, tu.NumFields())
+		for i := 0; i < tu.NumFields(); i++ {
+			fields[i] = tu.Field(i)
+		}
+		offsets := arch.sizes.Offsetsof(fields)
+		for i, f := range fields {
+			cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
+		}
+
+	case asmArray:
+		tu := t.Underlying().(*types.Array)
+		elem := tu.Elem()
+		// Calculate offset of each element array.
+		fields := []*types.Var{
+			types.NewVar(token.NoPos, nil, "fake0", elem),
+			types.NewVar(token.NoPos, nil, "fake1", elem),
+		}
+		offsets := arch.sizes.Offsetsof(fields)
+		elemoff := int(offsets[1])
+		for i := 0; i < int(tu.Len()); i++ {
+			cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff)
+		}
+	}
+
+	return cc
+}
+
 // asmParseDecl parses a function decl for expected assembly variables.
 func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc {
 	var (
 		arch   *asmArch
 		fn     *asmFunc
 		offset int
-		failed bool
 	)
 
-	addVar := func(outer string, v asmVar) {
-		if vo := fn.vars[outer]; vo != nil {
-			vo.inner = append(vo.inner, &v)
-		}
-		fn.vars[v.name] = &v
-		for i := 0; i < v.size; i++ {
-			fn.varByOffset[v.off+i] = &v
-		}
-	}
-
-	addParams := func(list []*ast.Field) {
-		for i, fld := range list {
-			// Determine alignment, size, and kind of type in declaration.
-			var align, size int
-			var kind asmKind
-			names := fld.Names
-			typ := f.gofmt(fld.Type)
-			switch t := fld.Type.(type) {
-			default:
-				switch typ {
-				default:
-					f.Warnf(fld.Type.Pos(), "unknown assembly argument type %s", typ)
-					failed = true
-					return
-				case "int8", "uint8", "byte", "bool":
-					size = 1
-				case "int16", "uint16":
-					size = 2
-				case "int32", "uint32", "float32":
-					size = 4
-				case "int64", "uint64", "float64":
-					align = arch.maxAlign
-					size = 8
-				case "int", "uint":
-					size = arch.intSize
-				case "uintptr", "iword", "Word", "Errno", "unsafe.Pointer":
-					size = arch.ptrSize
-				case "string", "ErrorString":
-					size = arch.ptrSize * 2
-					align = arch.ptrSize
-					kind = asmString
-				}
-			case *ast.ChanType, *ast.FuncType, *ast.MapType, *ast.StarExpr:
-				size = arch.ptrSize
-			case *ast.InterfaceType:
-				align = arch.ptrSize
-				size = 2 * arch.ptrSize
-				if len(t.Methods.List) > 0 {
-					kind = asmInterface
-				} else {
-					kind = asmEmptyInterface
-				}
-			case *ast.ArrayType:
-				if t.Len == nil {
-					size = arch.ptrSize + 2*arch.intSize
-					align = arch.ptrSize
-					kind = asmSlice
-					break
-				}
-				f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ)
-				failed = true
-			case *ast.StructType:
-				f.Warnf(fld.Type.Pos(), "unsupported assembly argument type %s", typ)
-				failed = true
-			}
-			if align == 0 {
-				align = size
-			}
-			if kind == 0 {
-				kind = asmKind(size)
-			}
+	// addParams adds asmVars for each of the parameters in list.
+	// isret indicates whether the list are the arguments or the return values.
+	addParams := func(list []*ast.Field, isret bool) {
+		argnum := 0
+		for _, fld := range list {
+			t := f.pkg.types[fld.Type].Type
+			align := int(arch.sizes.Alignof(t))
+			size := int(arch.sizes.Sizeof(t))
 			offset += -offset & (align - 1)
+			cc := componentsOfType(arch, t)
 
-			// Create variable for each name being declared with this type.
+			// names is the list of names with this type.
+			names := fld.Names
 			if len(names) == 0 {
-				name := "unnamed"
-				if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 && &list[0] == &decl.Type.Results.List[0] && i == 0 {
-					// Assume assembly will refer to single unnamed result as r.
+				// Anonymous args will be called arg, arg1, arg2, ...
+				// Similarly so for return values: ret, ret1, ret2, ...
+				name := "arg"
+				if isret {
 					name = "ret"
 				}
-				names = []*ast.Ident{{Name: name}}
+				if argnum > 0 {
+					name += strconv.Itoa(argnum)
+				}
+				names = []*ast.Ident{ast.NewIdent(name)}
 			}
+			argnum += len(names)
+
+			// Create variable for each name.
 			for _, id := range names {
 				name := id.Name
-				addVar("", asmVar{
-					name: name,
-					kind: kind,
-					typ:  typ,
-					off:  offset,
-					size: size,
-				})
-				switch kind {
-				case 8:
-					if arch.ptrSize == 4 {
-						w1, w2 := "lo", "hi"
-						if arch.bigEndian {
-							w1, w2 = w2, w1
-						}
-						addVar(name, asmVar{
-							name: name + "_" + w1,
-							kind: 4,
-							typ:  "half " + typ,
-							off:  offset,
-							size: 4,
-						})
-						addVar(name, asmVar{
-							name: name + "_" + w2,
-							kind: 4,
-							typ:  "half " + typ,
-							off:  offset + 4,
-							size: 4,
-						})
+				for _, c := range cc {
+					outer := name + c.outer
+					v := asmVar{
+						name: name + c.suffix,
+						kind: c.kind,
+						typ:  c.typ,
+						off:  offset + c.offset,
+						size: c.size,
+					}
+					if vo := fn.vars[outer]; vo != nil {
+						vo.inner = append(vo.inner, &v)
+					}
+					fn.vars[v.name] = &v
+					for i := 0; i < v.size; i++ {
+						fn.varByOffset[v.off+i] = &v
 					}
-
-				case asmEmptyInterface:
-					addVar(name, asmVar{
-						name: name + "_type",
-						kind: asmKind(arch.ptrSize),
-						typ:  "interface type",
-						off:  offset,
-						size: arch.ptrSize,
-					})
-					addVar(name, asmVar{
-						name: name + "_data",
-						kind: asmKind(arch.ptrSize),
-						typ:  "interface data",
-						off:  offset + arch.ptrSize,
-						size: arch.ptrSize,
-					})
-
-				case asmInterface:
-					addVar(name, asmVar{
-						name: name + "_itable",
-						kind: asmKind(arch.ptrSize),
-						typ:  "interface itable",
-						off:  offset,
-						size: arch.ptrSize,
-					})
-					addVar(name, asmVar{
-						name: name + "_data",
-						kind: asmKind(arch.ptrSize),
-						typ:  "interface data",
-						off:  offset + arch.ptrSize,
-						size: arch.ptrSize,
-					})
-
-				case asmSlice:
-					addVar(name, asmVar{
-						name: name + "_base",
-						kind: asmKind(arch.ptrSize),
-						typ:  "slice base",
-						off:  offset,
-						size: arch.ptrSize,
-					})
-					addVar(name, asmVar{
-						name: name + "_len",
-						kind: asmKind(arch.intSize),
-						typ:  "slice len",
-						off:  offset + arch.ptrSize,
-						size: arch.intSize,
-					})
-					addVar(name, asmVar{
-						name: name + "_cap",
-						kind: asmKind(arch.intSize),
-						typ:  "slice cap",
-						off:  offset + arch.ptrSize + arch.intSize,
-						size: arch.intSize,
-					})
-
-				case asmString:
-					addVar(name, asmVar{
-						name: name + "_base",
-						kind: asmKind(arch.ptrSize),
-						typ:  "string base",
-						off:  offset,
-						size: arch.ptrSize,
-					})
-					addVar(name, asmVar{
-						name: name + "_len",
-						kind: asmKind(arch.intSize),
-						typ:  "string len",
-						off:  offset + arch.ptrSize,
-						size: arch.intSize,
-					})
 				}
 				offset += size
 			}
@@ -507,18 +540,15 @@ func (f *File) asmParseDecl(decl *ast.FuncDecl) map[string]*asmFunc {
 			varByOffset: make(map[int]*asmVar),
 		}
 		offset = 0
-		addParams(decl.Type.Params.List)
+		addParams(decl.Type.Params.List, false)
 		if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
-			offset += -offset & (arch.maxAlign - 1)
-			addParams(decl.Type.Results.List)
+			offset += -offset & (arch.maxAlign() - 1)
+			addParams(decl.Type.Results.List, true)
 		}
 		fn.size = offset
 		m[arch.name] = fn
 	}
 
-	if failed {
-		return nil
-	}
 	return m
 }
 
@@ -613,7 +643,7 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri
 					src = 8
 				}
 			}
-		case "mips64", "mips64le":
+		case "mips", "mipsle", "mips64", "mips64le":
 			switch op {
 			case "MOVB", "MOVBU":
 				src = 1
@@ -624,6 +654,17 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri
 			case "MOVV", "MOVD":
 				src = 8
 			}
+		case "s390x":
+			switch op {
+			case "MOVB", "MOVBZ":
+				src = 1
+			case "MOVH", "MOVHZ":
+				src = 2
+			case "MOVW", "MOVWZ", "FMOVS":
+				src = 4
+			case "MOVD", "FMOVD":
+				src = 8
+			}
 		}
 	}
 	if dst == 0 {
@@ -639,11 +680,13 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri
 	}
 
 	vk := v.kind
+	vs := v.size
 	vt := v.typ
 	switch vk {
 	case asmInterface, asmEmptyInterface, asmString, asmSlice:
 		// allow reference to first word (pointer)
 		vk = v.inner[0].kind
+		vs = v.inner[0].size
 		vt = v.inner[0].typ
 	}
 
@@ -677,6 +720,6 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri
 				fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
 			}
 		}
-		badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vk, inner.String())
+		badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vs, inner.String())
 	}
 }
diff --git a/src/cmd/vet/cgo.go b/src/cmd/vet/cgo.go
index b896862..d233e9a 100644
--- a/src/cmd/vet/cgo.go
+++ b/src/cmd/vet/cgo.go
@@ -38,6 +38,11 @@ func checkCgoCall(f *File, node ast.Node) {
 		return
 	}
 
+	// A call to C.CBytes passes a pointer but is always safe.
+	if sel.Sel.Name == "CBytes" {
+		return
+	}
+
 	for _, arg := range x.Args {
 		if !typeOKForCgoCall(cgoBaseType(f, arg)) {
 			f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
diff --git a/src/cmd/vet/copylock.go b/src/cmd/vet/copylock.go
index 6533768..31c1257 100644
--- a/src/cmd/vet/copylock.go
+++ b/src/cmd/vet/copylock.go
@@ -210,6 +210,14 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath {
 		return nil
 	}
 
+	for {
+		atyp, ok := typ.Underlying().(*types.Array)
+		if !ok {
+			break
+		}
+		typ = atyp.Elem()
+	}
+
 	// We're only interested in the case in which the underlying
 	// type is a struct. (Interfaces and pointers are safe to copy.)
 	styp, ok := typ.Underlying().(*types.Struct)
diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go
index 69d5f9c..5cbe116 100644
--- a/src/cmd/vet/doc.go
+++ b/src/cmd/vet/doc.go
@@ -84,14 +84,14 @@ Flag: -copylocks
 
 Locks that are erroneously passed by value.
 
-Tests, benchmarks and documentation examples
+HTTP responses used incorrectly
 
-Flag: -tests
+Flag: -httpresponse
 
-Mistakes involving tests including functions with incorrect names or signatures
-and example tests that document identifiers not in the package.
+Mistakes deferring a function call on an HTTP response before
+checking whether the error returned with the response was nil.
 
-Failure to call the cancelation function returned by context.WithCancel.
+Failure to call the cancelation function returned by WithCancel
 
 Flag: -lostcancel
 
@@ -137,8 +137,6 @@ complains about arguments that look like format descriptor strings.
 It also checks for errors such as using a Writer as the first argument of
 Printf.
 
-Struct tags
-
 Range loop variables
 
 Flag: -rangeloops
@@ -157,11 +155,20 @@ Flag: -shift
 
 Shifts equal to or longer than the variable's length.
 
+Struct tags
+
 Flag: -structtags
 
 Struct tags that do not follow the format understood by reflect.StructTag.Get.
 Well-known encoding struct tags (json, xml) used with unexported fields.
 
+Tests and documentation examples
+
+Flag: -tests
+
+Mistakes involving tests including functions with incorrect names or signatures
+and example tests that document identifiers not in the package.
+
 Unreachable code
 
 Flag: -unreachable
diff --git a/src/cmd/vet/httpresponse.go b/src/cmd/vet/httpresponse.go
new file mode 100644
index 0000000..f667edb
--- /dev/null
+++ b/src/cmd/vet/httpresponse.go
@@ -0,0 +1,153 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains the check for http.Response values being used before
+// checking for errors.
+
+package main
+
+import (
+	"go/ast"
+	"go/types"
+)
+
+var (
+	httpResponseType types.Type
+	httpClientType   types.Type
+)
+
+func init() {
+	if typ := importType("net/http", "Response"); typ != nil {
+		httpResponseType = typ
+	}
+	if typ := importType("net/http", "Client"); typ != nil {
+		httpClientType = typ
+	}
+	// if http.Response or http.Client are not defined don't register this check.
+	if httpResponseType == nil || httpClientType == nil {
+		return
+	}
+
+	register("httpresponse",
+		"check errors are checked before using an http Response",
+		checkHTTPResponse, callExpr)
+}
+
+func checkHTTPResponse(f *File, node ast.Node) {
+	call := node.(*ast.CallExpr)
+	if !isHTTPFuncOrMethodOnClient(f, call) {
+		return // the function call is not related to this check.
+	}
+
+	finder := &blockStmtFinder{node: call}
+	ast.Walk(finder, f.file)
+	stmts := finder.stmts()
+	if len(stmts) < 2 {
+		return // the call to the http function is the last statement of the block.
+	}
+
+	asg, ok := stmts[0].(*ast.AssignStmt)
+	if !ok {
+		return // the first statement is not assignment.
+	}
+	resp := rootIdent(asg.Lhs[0])
+	if resp == nil {
+		return // could not find the http.Response in the assignment.
+	}
+
+	def, ok := stmts[1].(*ast.DeferStmt)
+	if !ok {
+		return // the following statement is not a defer.
+	}
+	root := rootIdent(def.Call.Fun)
+	if root == nil {
+		return // could not find the receiver of the defer call.
+	}
+
+	if resp.Obj == root.Obj {
+		f.Badf(root.Pos(), "using %s before checking for errors", resp.Name)
+	}
+}
+
+// isHTTPFuncOrMethodOnClient checks whether the given call expression is on
+// either a function of the net/http package or a method of http.Client that
+// returns (*http.Response, error).
+func isHTTPFuncOrMethodOnClient(f *File, expr *ast.CallExpr) bool {
+	fun, _ := expr.Fun.(*ast.SelectorExpr)
+	sig, _ := f.pkg.types[fun].Type.(*types.Signature)
+	if sig == nil {
+		return false // the call is not on of the form x.f()
+	}
+
+	res := sig.Results()
+	if res.Len() != 2 {
+		return false // the function called does not return two values.
+	}
+	if ptr, ok := res.At(0).Type().(*types.Pointer); !ok || !types.Identical(ptr.Elem(), httpResponseType) {
+		return false // the first return type is not *http.Response.
+	}
+	if !types.Identical(res.At(1).Type().Underlying(), errorType) {
+		return false // the second return type is not error
+	}
+
+	typ := f.pkg.types[fun.X].Type
+	if typ == nil {
+		id, ok := fun.X.(*ast.Ident)
+		return ok && id.Name == "http" // function in net/http package.
+	}
+
+	if types.Identical(typ, httpClientType) {
+		return true // method on http.Client.
+	}
+	ptr, ok := typ.(*types.Pointer)
+	return ok && types.Identical(ptr.Elem(), httpClientType) // method on *http.Client.
+}
+
+// blockStmtFinder is an ast.Visitor that given any ast node can find the
+// statement containing it and its succeeding statements in the same block.
+type blockStmtFinder struct {
+	node  ast.Node       // target of search
+	stmt  ast.Stmt       // innermost statement enclosing argument to Visit
+	block *ast.BlockStmt // innermost block enclosing argument to Visit.
+}
+
+// Visit finds f.node performing a search down the ast tree.
+// It keeps the last block statement and statement seen for later use.
+func (f *blockStmtFinder) Visit(node ast.Node) ast.Visitor {
+	if node == nil || f.node.Pos() < node.Pos() || f.node.End() > node.End() {
+		return nil // not here
+	}
+	switch n := node.(type) {
+	case *ast.BlockStmt:
+		f.block = n
+	case ast.Stmt:
+		f.stmt = n
+	}
+	if f.node.Pos() == node.Pos() && f.node.End() == node.End() {
+		return nil // found
+	}
+	return f // keep looking
+}
+
+// stmts returns the statements of f.block starting from the one including f.node.
+func (f *blockStmtFinder) stmts() []ast.Stmt {
+	for i, v := range f.block.List {
+		if f.stmt == v {
+			return f.block.List[i:]
+		}
+	}
+	return nil
+}
+
+// rootIdent finds the root identifier x in a chain of selections x.y.z, or nil if not found.
+func rootIdent(n ast.Node) *ast.Ident {
+	switch n := n.(type) {
+	case *ast.SelectorExpr:
+		return rootIdent(n.X)
+	case *ast.Ident:
+		return n
+	default:
+		return nil
+	}
+}
diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go
index 4f3cca8..3da0b3c 100644
--- a/src/cmd/vet/main.go
+++ b/src/cmd/vet/main.go
@@ -25,7 +25,7 @@ import (
 
 var (
 	verbose = flag.Bool("v", false, "verbose")
-	tags    = flag.String("tags", "", "comma-separated list of build tags to apply when parsing")
+	tags    = flag.String("tags", "", "space-separated list of build tags to apply when parsing")
 	tagList = []string{} // exploded version of tags flag; set in main
 )
 
@@ -133,13 +133,13 @@ var (
 	callExpr      *ast.CallExpr
 	compositeLit  *ast.CompositeLit
 	exprStmt      *ast.ExprStmt
-	field         *ast.Field
 	funcDecl      *ast.FuncDecl
 	funcLit       *ast.FuncLit
 	genDecl       *ast.GenDecl
 	interfaceType *ast.InterfaceType
 	rangeStmt     *ast.RangeStmt
 	returnStmt    *ast.ReturnStmt
+	structType    *ast.StructType
 
 	// checkers is a two-level map.
 	// The outer level is keyed by a nil pointer, one of the AST vars above.
@@ -161,7 +161,7 @@ func register(name, usage string, fn func(*File, ast.Node), types ...ast.Node) {
 
 // Usage is a replacement usage function for the flags package.
 func Usage() {
-	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
+	fmt.Fprintf(os.Stderr, "Usage of vet:\n")
 	fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
 	fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
 	fmt.Fprintf(os.Stderr, "By default, -all is set and all non-experimental checks are run.\n")
@@ -208,7 +208,10 @@ func main() {
 		}
 	}
 
-	tagList = strings.Split(*tags, ",")
+	// Accept space-separated tags because that matches
+	// the go command's other subcommands.
+	// Accept commas because go tool vet traditionally has.
+	tagList = strings.Fields(strings.Replace(*tags, ",", " ", -1))
 
 	initPrintFlags()
 	initUnusedFlags()
@@ -440,14 +443,22 @@ func (f *File) loc(pos token.Pos) string {
 	return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
 }
 
+// locPrefix returns a formatted representation of the position for use as a line prefix.
+func (f *File) locPrefix(pos token.Pos) string {
+	if pos == token.NoPos {
+		return ""
+	}
+	return fmt.Sprintf("%s: ", f.loc(pos))
+}
+
 // Warn reports an error but does not set the exit code.
 func (f *File) Warn(pos token.Pos, args ...interface{}) {
-	fmt.Fprintf(os.Stderr, "%s: %s", f.loc(pos), fmt.Sprintln(args...))
+	fmt.Fprintf(os.Stderr, "%s%s", f.locPrefix(pos), fmt.Sprintln(args...))
 }
 
 // Warnf reports a formatted error but does not set the exit code.
 func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
-	fmt.Fprintf(os.Stderr, "%s: %s\n", f.loc(pos), fmt.Sprintf(format, args...))
+	fmt.Fprintf(os.Stderr, "%s%s\n", f.locPrefix(pos), fmt.Sprintf(format, args...))
 }
 
 // walkFile walks the file's tree.
@@ -470,8 +481,6 @@ func (f *File) Visit(node ast.Node) ast.Visitor {
 		key = compositeLit
 	case *ast.ExprStmt:
 		key = exprStmt
-	case *ast.Field:
-		key = field
 	case *ast.FuncDecl:
 		key = funcDecl
 	case *ast.FuncLit:
@@ -484,6 +493,8 @@ func (f *File) Visit(node ast.Node) ast.Visitor {
 		key = rangeStmt
 	case *ast.ReturnStmt:
 		key = returnStmt
+	case *ast.StructType:
+		key = structType
 	}
 	for _, fn := range f.checkers[key] {
 		fn(f, node)
diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go
index f4b985c..9998dda 100644
--- a/src/cmd/vet/print.go
+++ b/src/cmd/vet/print.go
@@ -94,7 +94,7 @@ func formatString(f *File, call *ast.CallExpr) (string, int) {
 	if typ != nil {
 		if sig, ok := typ.(*types.Signature); ok {
 			if !sig.Variadic() {
-				// Skip checking non-variadic functions
+				// Skip checking non-variadic functions.
 				return "", -1
 			}
 			idx := sig.Params().Len() - 2
@@ -103,30 +103,36 @@ func formatString(f *File, call *ast.CallExpr) (string, int) {
 				// fixed arguments.
 				return "", -1
 			}
-			s, ok := stringLiteralArg(f, call, idx)
+			s, ok := stringConstantArg(f, call, idx)
 			if !ok {
-				// The last argument before variadic args isn't a string
+				// The last argument before variadic args isn't a string.
 				return "", -1
 			}
 			return s, idx
 		}
 	}
 
-	// Cannot determine call's signature. Fallback to scanning for the first
-	// string argument in the call
+	// Cannot determine call's signature. Fall back to scanning for the first
+	// string constant in the call.
 	for idx := range call.Args {
-		if s, ok := stringLiteralArg(f, call, idx); ok {
+		if s, ok := stringConstantArg(f, call, idx); ok {
 			return s, idx
 		}
+		if f.pkg.types[call.Args[idx]].Type == types.Typ[types.String] {
+			// Skip checking a call with a non-constant format
+			// string argument, since its contents are unavailable
+			// for validation.
+			return "", -1
+		}
 	}
 	return "", -1
 }
 
-// stringLiteralArg returns call's string constant argument at the index idx.
+// stringConstantArg returns call's string constant argument at the index idx.
 //
 // ("", false) is returned if call's argument at the index idx isn't a string
-// literal.
-func stringLiteralArg(f *File, call *ast.CallExpr, idx int) (string, bool) {
+// constant.
+func stringConstantArg(f *File, call *ast.CallExpr, idx int) (string, bool) {
 	if idx >= len(call.Args) {
 		return "", false
 	}
@@ -186,6 +192,12 @@ func isStringer(f *File, d *ast.FuncDecl) bool {
 		f.pkg.types[d.Type.Results.List[0].Type].Type == types.Typ[types.String]
 }
 
+// isFormatter reports whether t satisfies fmt.Formatter.
+// Unlike fmt.Stringer, it's impossible to satisfy fmt.Formatter without importing fmt.
+func (f *File) isFormatter(t types.Type) bool {
+	return formatterType != nil && types.Implements(t, formatterType)
+}
+
 // formatState holds the parsed representation of a printf directive such as "%3.*[4]d".
 // It is constructed by parsePrintfVerb.
 type formatState struct {
@@ -194,7 +206,6 @@ type formatState struct {
 	name     string // Printf, Sprintf etc.
 	flags    []byte // the list of # + etc.
 	argNums  []int  // the successive argument numbers that are consumed, adjusted to refer to actual arg in call
-	indexed  bool   // whether an indexing expression appears: %[1]d.
 	firstArg int    // Index of first argument after the format in the Printf call.
 	// Used only during parse.
 	file         *File
@@ -223,7 +234,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) {
 	}
 	// Hard part: check formats against args.
 	argNum := firstArg
-	indexed := false
+	maxArgNum := firstArg
 	for i, w := 0, 0; i < len(format); i += w {
 		w = 1
 		if format[i] == '%' {
@@ -232,9 +243,6 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) {
 				return
 			}
 			w = len(state.format)
-			if state.indexed {
-				indexed = true
-			}
 			if !f.okPrintfArg(call, state) { // One error per format is enough.
 				return
 			}
@@ -242,16 +250,20 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) {
 				// Continue with the next sequential argument.
 				argNum = state.argNums[len(state.argNums)-1] + 1
 			}
+			for _, n := range state.argNums {
+				if n >= maxArgNum {
+					maxArgNum = n + 1
+				}
+			}
 		}
 	}
 	// Dotdotdot is hard.
-	if call.Ellipsis.IsValid() && argNum >= len(call.Args)-1 {
+	if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 {
 		return
 	}
-	// If the arguments were direct indexed, we assume the programmer knows what's up.
-	// Otherwise, there should be no leftover arguments.
-	if !indexed && argNum != len(call.Args) {
-		expect := argNum - firstArg
+	// There should be no leftover arguments.
+	if maxArgNum != len(call.Args) {
+		expect := maxArgNum - firstArg
 		numArgs := len(call.Args) - firstArg
 		f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs)
 	}
@@ -286,17 +298,20 @@ func (s *formatState) parseIndex() bool {
 		return true
 	}
 	// Argument index present.
-	s.indexed = true
 	s.nbytes++ // skip '['
 	start := s.nbytes
 	s.scanNum()
 	if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' {
-		s.file.Badf(s.call.Pos(), "illegal syntax for printf argument index")
+		end := strings.Index(s.format, "]")
+		if end < 0 {
+			end = len(s.format)
+		}
+		s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: [%s]", s.format[start:end])
 		return false
 	}
 	arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32)
 	if err != nil {
-		s.file.Badf(s.call.Pos(), "illegal syntax for printf argument index: %s", err)
+		s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: %s", err)
 		return false
 	}
 	s.nbytes++ // skip ']'
@@ -349,14 +364,12 @@ func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg
 		argNum:   argNum,
 		argNums:  make([]int, 0, 1),
 		nbytes:   1, // There's guaranteed to be a percent sign.
-		indexed:  false,
 		firstArg: firstArg,
 		file:     f,
 		call:     call,
 	}
 	// There may be flags.
 	state.parseFlags()
-	indexPending := false
 	// There may be an index.
 	if !state.parseIndex() {
 		return nil
@@ -370,7 +383,7 @@ func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg
 		return nil
 	}
 	// Now a verb, possibly prefixed by an index (which we may already have).
-	if !indexPending && !state.parseIndex() {
+	if !state.indexPending && !state.parseIndex() {
 		return nil
 	}
 	if state.nbytes == len(state.format) {
@@ -416,8 +429,6 @@ const (
 )
 
 // printVerbs identifies which flags are known to printf for each verb.
-// TODO: A type that implements Formatter may do what it wants, and vet
-// will complain incorrectly.
 var printVerbs = []printVerb{
 	// '-' is a width modifier, always valid.
 	// '.' is a precision for float, max width for strings.
@@ -459,7 +470,16 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) {
 			break
 		}
 	}
-	if !found {
+
+	// Does current arg implement fmt.Formatter?
+	formatter := false
+	if state.argNum < len(call.Args) {
+		if tv, ok := f.pkg.types[call.Args[state.argNum]]; ok {
+			formatter = f.isFormatter(tv.Type)
+		}
+	}
+
+	if !found && !formatter {
 		f.Badf(call.Pos(), "unrecognized printf verb %q", state.verb)
 		return false
 	}
@@ -487,7 +507,7 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) {
 			return false
 		}
 	}
-	if state.verb == '%' {
+	if state.verb == '%' || formatter {
 		return true
 	}
 	argNum := state.argNums[len(state.argNums)-1]
@@ -626,7 +646,10 @@ func (f *File) checkPrint(call *ast.CallExpr, name string) {
 	}
 	arg := args[0]
 	if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
-		if strings.Contains(lit.Value, "%") {
+		// Ignore trailing % character in lit.Value.
+		// The % in "abc 0.0%" couldn't be a formatting directive.
+		s := strings.TrimSuffix(lit.Value, `%"`)
+		if strings.Contains(s, "%") {
 			f.Badf(call.Pos(), "possible formatting directive in %s call", name)
 		}
 	}
diff --git a/src/cmd/vet/shift.go b/src/cmd/vet/shift.go
index 8c038b4..55f3ea3 100644
--- a/src/cmd/vet/shift.go
+++ b/src/cmd/vet/shift.go
@@ -41,6 +41,12 @@ func checkShift(f *File, node ast.Node) {
 // checkLongShift checks if shift or shift-assign operations shift by more than
 // the length of the underlying variable.
 func checkLongShift(f *File, node ast.Node, x, y ast.Expr) {
+	if f.pkg.types[x].Value != nil {
+		// Ignore shifts of constants.
+		// These are frequently used for bit-twiddling tricks
+		// like ^uint(0) >> 63 for 32/64 bit detection and compatibility.
+		return
+	}
 	v := f.pkg.types[y].Value
 	if v == nil {
 		return
diff --git a/src/cmd/vet/structtag.go b/src/cmd/vet/structtag.go
index abff14f..814bbda 100644
--- a/src/cmd/vet/structtag.go
+++ b/src/cmd/vet/structtag.go
@@ -9,20 +9,31 @@ package main
 import (
 	"errors"
 	"go/ast"
+	"go/token"
 	"reflect"
 	"strconv"
+	"strings"
 )
 
 func init() {
 	register("structtags",
 		"check that struct field tags have canonical format and apply to exported fields as needed",
-		checkCanonicalFieldTag,
-		field)
+		checkStructFieldTags,
+		structType)
 }
 
-// checkCanonicalFieldTag checks a struct field tag.
-func checkCanonicalFieldTag(f *File, node ast.Node) {
-	field := node.(*ast.Field)
+// checkStructFieldTags checks all the field tags of a struct, including checking for duplicates.
+func checkStructFieldTags(f *File, node ast.Node) {
+	var seen map[[2]string]token.Pos
+	for _, field := range node.(*ast.StructType).Fields.List {
+		checkCanonicalFieldTag(f, field, &seen)
+	}
+}
+
+var checkTagDups = []string{"json", "xml"}
+
+// checkCanonicalFieldTag checks a single struct field tag.
+func checkCanonicalFieldTag(f *File, field *ast.Field, seen *map[[2]string]token.Pos) {
 	if field.Tag == nil {
 		return
 	}
@@ -34,7 +45,26 @@ func checkCanonicalFieldTag(f *File, node ast.Node) {
 	}
 
 	if err := validateStructTag(tag); err != nil {
-		f.Badf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get: %s", field.Tag.Value, err)
+		raw, _ := strconv.Unquote(field.Tag.Value) // field.Tag.Value is known to be a quoted string
+		f.Badf(field.Pos(), "struct field tag %#q not compatible with reflect.StructTag.Get: %s", raw, err)
+	}
+
+	for _, key := range checkTagDups {
+		val := reflect.StructTag(tag).Get(key)
+		if val == "" || val == "-" || val[0] == ',' {
+			continue
+		}
+		if i := strings.Index(val, ","); i >= 0 {
+			val = val[:i]
+		}
+		if *seen == nil {
+			*seen = map[[2]string]token.Pos{}
+		}
+		if pos, ok := (*seen)[[2]string{key, val}]; ok {
+			f.Badf(field.Pos(), "struct field %s repeats %s tag %q also at %s", field.Names[0].Name, key, val, f.loc(pos))
+		} else {
+			(*seen)[[2]string{key, val}] = field.Pos()
+		}
 	}
 
 	// Check for use of json or xml tags with unexported fields.
@@ -49,9 +79,8 @@ func checkCanonicalFieldTag(f *File, node ast.Node) {
 		return
 	}
 
-	st := reflect.StructTag(tag)
 	for _, enc := range [...]string{"json", "xml"} {
-		if st.Get(enc) != "" {
+		if reflect.StructTag(tag).Get(enc) != "" {
 			f.Badf(field.Pos(), "struct field %s has %s tag but is not exported", field.Names[0].Name, enc)
 			return
 		}
@@ -62,6 +91,7 @@ var (
 	errTagSyntax      = errors.New("bad syntax for struct tag pair")
 	errTagKeySyntax   = errors.New("bad syntax for struct tag key")
 	errTagValueSyntax = errors.New("bad syntax for struct tag value")
+	errTagSpace       = errors.New("key:\"value\" pairs not separated by spaces")
 )
 
 // validateStructTag parses the struct tag and returns an error if it is not
@@ -70,7 +100,13 @@ var (
 func validateStructTag(tag string) error {
 	// This code is based on the StructTag.Get code in package reflect.
 
-	for tag != "" {
+	n := 0
+	for ; tag != ""; n++ {
+		if n > 0 && tag != "" && tag[0] != ' ' {
+			// More restrictive than reflect, but catches likely mistakes
+			// like `x:"foo",y:"bar"`, which parses as `x:"foo" ,y:"bar"` with second key ",y".
+			return errTagSpace
+		}
 		// Skip leading space.
 		i := 0
 		for i < len(tag) && tag[i] == ' ' {
diff --git a/src/cmd/vet/testdata/asm.go b/src/cmd/vet/testdata/asm.go
deleted file mode 100644
index 8194710..0000000
--- a/src/cmd/vet/testdata/asm.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-// This file contains declarations to test the assembly in test_asm.s.
-
-package testdata
-
-func arg1(x int8, y uint8)
-func arg2(x int16, y uint16)
-func arg4(x int32, y uint32)
-func arg8(x int64, y uint64)
-func argint(x int, y uint)
-func argptr(x *byte, y *byte, c chan int, m map[int]int, f func())
-func argstring(x, y string)
-func argslice(x, y []string)
-func argiface(x interface{}, y interface {
-	m()
-})
-func returnint() int
-func returnbyte(x int) byte
-func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte)
-func returnintmissing() int
-func leaf(x, y int) int
-
-func noprof(x int)
-func dupok(x int)
-func nosplit(x int)
-func rodata(x int)
-func noptr(x int)
-func wrapper(x int)
-
-func f15271() (x uint32)
diff --git a/src/cmd/vet/testdata/asm/asm.go b/src/cmd/vet/testdata/asm/asm.go
new file mode 100644
index 0000000..e6d6d03
--- /dev/null
+++ b/src/cmd/vet/testdata/asm/asm.go
@@ -0,0 +1,45 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// This file contains declarations to test the assembly in test_asm.s.
+
+package testdata
+
+type S struct {
+	i int32
+	b bool
+	s string
+}
+
+func arg1(x int8, y uint8)
+func arg2(x int16, y uint16)
+func arg4(x int32, y uint32)
+func arg8(x int64, y uint64)
+func argint(x int, y uint)
+func argptr(x *byte, y *byte, c chan int, m map[int]int, f func())
+func argstring(x, y string)
+func argslice(x, y []string)
+func argiface(x interface{}, y interface {
+	m()
+})
+func argcomplex(x complex64, y complex128)
+func argstruct(x S, y struct{})
+func argarray(x [2]S)
+func returnint() int
+func returnbyte(x int) byte
+func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte)
+func returnintmissing() int
+func leaf(x, y int) int
+
+func noprof(x int)
+func dupok(x int)
+func nosplit(x int)
+func rodata(x int)
+func noptr(x int)
+func wrapper(x int)
+
+func f15271() (x uint32)
+func f17584(x float32, y complex64)
diff --git a/src/cmd/vet/testdata/asm/asm1.s b/src/cmd/vet/testdata/asm/asm1.s
new file mode 100644
index 0000000..cac6ed2
--- /dev/null
+++ b/src/cmd/vet/testdata/asm/asm1.s
@@ -0,0 +1,315 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64
+// +build vet_test
+
+TEXT ·arg1(SB),0,$0-2
+	MOVB	x+0(FP), AX
+	// MOVB x+0(FP), AX // commented out instructions used to panic
+	MOVB	y+1(FP), BX
+	MOVW	x+0(FP), AX // ERROR "\[amd64\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
+	MOVW	y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
+	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value"
+	MOVL	y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value"
+	MOVQ	x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value"
+	MOVQ	y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value"
+	MOVB	x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
+	MOVB	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
+	TESTB	x+0(FP), AX
+	TESTB	y+1(FP), BX
+	TESTW	x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value"
+	TESTW	y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value"
+	TESTL	x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value"
+	TESTL	y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value"
+	TESTQ	x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value"
+	TESTQ	y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value"
+	TESTB	x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
+	TESTB	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
+	MOVB	8(SP), AX // ERROR "8\(SP\) should be x\+0\(FP\)"
+	MOVB	9(SP), AX // ERROR "9\(SP\) should be y\+1\(FP\)"
+	MOVB	10(SP), AX // ERROR "use of 10\(SP\) points beyond argument frame"
+	RET
+
+TEXT ·arg2(SB),0,$0-4
+	MOVB	x+0(FP), AX // ERROR "arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
+	MOVB	y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
+	MOVW	x+0(FP), AX
+	MOVW	y+2(FP), BX
+	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value"
+	MOVL	y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value"
+	MOVQ	x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value"
+	MOVQ	y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value"
+	MOVW	x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
+	MOVW	y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
+	TESTB	x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value"
+	TESTB	y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value"
+	TESTW	x+0(FP), AX
+	TESTW	y+2(FP), BX
+	TESTL	x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value"
+	TESTL	y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value"
+	TESTQ	x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value"
+	TESTQ	y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value"
+	TESTW	x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
+	TESTW	y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
+	RET
+
+TEXT ·arg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8"
+	MOVB	x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
+	MOVB	y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
+	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value"
+	MOVW	y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value"
+	MOVL	x+0(FP), AX
+	MOVL	y+4(FP), AX
+	MOVQ	x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value"
+	MOVQ	y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value"
+	MOVL	x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+	MOVL	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+	TESTB	x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value"
+	TESTB	y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value"
+	TESTW	x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value"
+	TESTW	y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value"
+	TESTL	x+0(FP), AX
+	TESTL	y+4(FP), AX
+	TESTQ	x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value"
+	TESTQ	y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value"
+	TESTL	x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+	TESTL	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+	RET
+
+TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+	MOVB	x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
+	MOVB	y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
+	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
+	MOVW	y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
+	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value"
+	MOVL	y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value"
+	MOVQ	x+0(FP), AX
+	MOVQ	y+8(FP), AX
+	MOVQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	MOVQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	TESTB	x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value"
+	TESTB	y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value"
+	TESTW	x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value"
+	TESTW	y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value"
+	TESTL	x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value"
+	TESTL	y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value"
+	TESTQ	x+0(FP), AX
+	TESTQ	y+8(FP), AX
+	TESTQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	TESTQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	RET
+
+TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+	MOVB	x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value"
+	MOVB	y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value"
+	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value"
+	MOVW	y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value"
+	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int is 8-byte value"
+	MOVL	y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint is 8-byte value"
+	MOVQ	x+0(FP), AX
+	MOVQ	y+8(FP), AX
+	MOVQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	MOVQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	TESTB	x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 8-byte value"
+	TESTB	y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint is 8-byte value"
+	TESTW	x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 8-byte value"
+	TESTW	y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint is 8-byte value"
+	TESTL	x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int is 8-byte value"
+	TESTL	y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint is 8-byte value"
+	TESTQ	x+0(FP), AX
+	TESTQ	y+8(FP), AX
+	TESTQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	TESTQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	RET
+
+TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-40"
+	MOVB	x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value"
+	MOVB	y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value"
+	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value"
+	MOVW	y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value"
+	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); \*byte is 8-byte value"
+	MOVL	y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); \*byte is 8-byte value"
+	MOVQ	x+0(FP), AX
+	MOVQ	y+8(FP), AX
+	MOVQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	MOVQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	TESTB	x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 8-byte value"
+	TESTB	y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); \*byte is 8-byte value"
+	TESTW	x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 8-byte value"
+	TESTW	y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); \*byte is 8-byte value"
+	TESTL	x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); \*byte is 8-byte value"
+	TESTL	y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); \*byte is 8-byte value"
+	TESTQ	x+0(FP), AX
+	TESTQ	y+8(FP), AX
+	TESTQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	TESTQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	MOVL	c+16(FP), AX // ERROR "invalid MOVL of c\+16\(FP\); chan int is 8-byte value"
+	MOVL	m+24(FP), AX // ERROR "invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value"
+	MOVL	f+32(FP), AX // ERROR "invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value"
+	RET
+
+TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32"
+	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value"
+	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); string base is 8-byte value"
+	MOVQ	x+0(FP), AX
+	MOVW	x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value"
+	MOVL	x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); string base is 8-byte value"
+	MOVQ	x_base+0(FP), AX
+	MOVW	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVL	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVQ	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVW	x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value"
+	MOVL	x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); string len is 8-byte value"
+	MOVQ	x_len+8(FP), AX
+	MOVQ	y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)"
+	MOVQ	y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)"
+	RET
+
+TEXT ·argslice(SB),0,$48 // ERROR "wrong argument size 0; expected \$\.\.\.-48"
+	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value"
+	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); slice base is 8-byte value"
+	MOVQ	x+0(FP), AX
+	MOVW	x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value"
+	MOVL	x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); slice base is 8-byte value"
+	MOVQ	x_base+0(FP), AX
+	MOVW	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVL	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVQ	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVW	x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value"
+	MOVL	x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); slice len is 8-byte value"
+	MOVQ	x_len+8(FP), AX
+	MOVW	x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+	MOVL	x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+	MOVQ	x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+	MOVW	x_cap+16(FP), AX // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value"
+	MOVL	x_cap+16(FP), AX // ERROR "invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value"
+	MOVQ	x_cap+16(FP), AX
+	MOVQ	y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)"
+	MOVQ	y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)"
+	MOVQ	y_cap+16(FP), AX // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)"
+	RET
+
+TEXT ·argiface(SB),0,$0-32
+	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value"
+	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); interface type is 8-byte value"
+	MOVQ	x+0(FP), AX
+	MOVW	x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value"
+	MOVL	x_type+0(FP), AX // ERROR "invalid MOVL of x_type\+0\(FP\); interface type is 8-byte value"
+	MOVQ	x_type+0(FP), AX
+	MOVQ	x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
+	MOVQ	x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
+	MOVW	x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+	MOVL	x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+	MOVQ	x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+	MOVW	x_data+8(FP), AX // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value"
+	MOVL	x_data+8(FP), AX // ERROR "invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value"
+	MOVQ	x_data+8(FP), AX
+	MOVW	y+16(FP), AX // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value"
+	MOVL	y+16(FP), AX // ERROR "invalid MOVL of y\+16\(FP\); interface itable is 8-byte value"
+	MOVQ	y+16(FP), AX
+	MOVW	y_itable+16(FP), AX // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value"
+	MOVL	y_itable+16(FP), AX // ERROR "invalid MOVL of y_itable\+16\(FP\); interface itable is 8-byte value"
+	MOVQ	y_itable+16(FP), AX
+	MOVQ	y_type+16(FP), AX // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)"
+	MOVW	y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+	MOVL	y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+	MOVQ	y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+	MOVW	y_data+24(FP), AX // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value"
+	MOVL	y_data+24(FP), AX // ERROR "invalid MOVL of y_data\+24\(FP\); interface data is 8-byte value"
+	MOVQ	y_data+24(FP), AX
+	RET
+
+TEXT ·argcomplex(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
+	MOVSS	x+0(FP), X0 // ERROR "invalid MOVSS of x\+0\(FP\); complex64 is 8-byte value containing x_real\+0\(FP\) and x_imag\+4\(FP\)"
+	MOVSD	x+0(FP), X0 // ERROR "invalid MOVSD of x\+0\(FP\); complex64 is 8-byte value containing x_real\+0\(FP\) and x_imag\+4\(FP\)"
+	MOVSS	x_real+0(FP), X0
+	MOVSD	x_real+0(FP), X0 // ERROR "invalid MOVSD of x_real\+0\(FP\); real\(complex64\) is 4-byte value"
+	MOVSS	x_real+4(FP), X0 // ERROR "invalid offset x_real\+4\(FP\); expected x_real\+0\(FP\)"
+	MOVSS	x_imag+4(FP), X0
+	MOVSD	x_imag+4(FP), X0 // ERROR "invalid MOVSD of x_imag\+4\(FP\); imag\(complex64\) is 4-byte value"
+	MOVSS	x_imag+8(FP), X0 // ERROR "invalid offset x_imag\+8\(FP\); expected x_imag\+4\(FP\)"
+	MOVSD	y+8(FP), X0 // ERROR "invalid MOVSD of y\+8\(FP\); complex128 is 16-byte value containing y_real\+8\(FP\) and y_imag\+16\(FP\)"
+	MOVSS	y_real+8(FP), X0 // ERROR "invalid MOVSS of y_real\+8\(FP\); real\(complex128\) is 8-byte value"
+	MOVSD	y_real+8(FP), X0
+	MOVSS	y_real+16(FP), X0 // ERROR "invalid offset y_real\+16\(FP\); expected y_real\+8\(FP\)"
+	MOVSS	y_imag+16(FP), X0 // ERROR "invalid MOVSS of y_imag\+16\(FP\); imag\(complex128\) is 8-byte value"
+	MOVSD	y_imag+16(FP), X0
+	MOVSS	y_imag+24(FP), X0 // ERROR "invalid offset y_imag\+24\(FP\); expected y_imag\+16\(FP\)"
+	RET
+
+TEXT ·argstruct(SB),0,$64 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
+	MOVQ	x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); testdata.S is 24-byte value"
+	MOVQ	x_i+0(FP), AX // ERROR "invalid MOVQ of x_i\+0\(FP\); int32 is 4-byte value"
+	MOVQ	x_b+0(FP), AX // ERROR "invalid offset x_b\+0\(FP\); expected x_b\+4\(FP\)"
+	MOVQ	x_s+8(FP), AX
+	MOVQ	x_s_base+8(FP), AX
+	MOVQ	x_s+16(FP), AX // ERROR "invalid offset x_s\+16\(FP\); expected x_s\+8\(FP\), x_s_base\+8\(FP\), or x_s_len\+16\(FP\)"
+	MOVQ	x_s_len+16(FP), AX
+	RET
+
+TEXT ·argarray(SB),0,$64 // ERROR "wrong argument size 0; expected \$\.\.\.-48"
+	MOVQ	x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); \[2\]testdata.S is 48-byte value"
+	MOVQ	x_0_i+0(FP), AX // ERROR "invalid MOVQ of x_0_i\+0\(FP\); int32 is 4-byte value"
+	MOVQ	x_0_b+0(FP), AX // ERROR "invalid offset x_0_b\+0\(FP\); expected x_0_b\+4\(FP\)"
+	MOVQ	x_0_s+8(FP), AX
+	MOVQ	x_0_s_base+8(FP), AX
+	MOVQ	x_0_s+16(FP), AX // ERROR "invalid offset x_0_s\+16\(FP\); expected x_0_s\+8\(FP\), x_0_s_base\+8\(FP\), or x_0_s_len\+16\(FP\)"
+	MOVQ	x_0_s_len+16(FP), AX
+	MOVB	foo+25(FP), AX // ERROR "unknown variable foo; offset 25 is x_1_i\+24\(FP\)"
+	MOVQ	x_1_s+32(FP), AX
+	MOVQ	x_1_s_base+32(FP), AX
+	MOVQ	x_1_s+40(FP), AX // ERROR "invalid offset x_1_s\+40\(FP\); expected x_1_s\+32\(FP\), x_1_s_base\+32\(FP\), or x_1_s_len\+40\(FP\)"
+	MOVQ	x_1_s_len+40(FP), AX
+	RET
+
+TEXT ·returnint(SB),0,$0-8
+	MOVB	AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value"
+	MOVW	AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value"
+	MOVL	AX, ret+0(FP) // ERROR "invalid MOVL of ret\+0\(FP\); int is 8-byte value"
+	MOVQ	AX, ret+0(FP)
+	MOVQ	AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
+	MOVQ	AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
+	RET
+
+TEXT ·returnbyte(SB),0,$0-9
+	MOVQ	x+0(FP), AX
+	MOVB	AX, ret+8(FP)
+	MOVW	AX, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value"
+	MOVL	AX, ret+8(FP) // ERROR "invalid MOVL of ret\+8\(FP\); byte is 1-byte value"
+	MOVQ	AX, ret+8(FP) // ERROR "invalid MOVQ of ret\+8\(FP\); byte is 1-byte value"
+	MOVB	AX, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)"
+	RET
+
+TEXT ·returnnamed(SB),0,$0-41
+	MOVB	x+0(FP), AX
+	MOVQ	AX, r1+8(FP)
+	MOVW	AX, r2+16(FP)
+	MOVQ	AX, r3+24(FP)
+	MOVQ	AX, r3_base+24(FP)
+	MOVQ	AX, r3_len+32(FP)
+	MOVB	AX, r4+40(FP)
+	MOVL	AX, r1+8(FP) // ERROR "invalid MOVL of r1\+8\(FP\); int is 8-byte value"
+	RET
+
+TEXT ·returnintmissing(SB),0,$0-8
+	RET // ERROR "RET without writing to 8-byte ret\+0\(FP\)"
+
+
+// issue 15271
+TEXT ·f15271(SB), NOSPLIT, $0-4
+    // Stick 123 into the low 32 bits of X0.
+    MOVQ $123, AX
+    PINSRD $0, AX, X0
+
+    // Return them.
+    PEXTRD $0, X0, x+0(FP)
+    RET
+
+// issue 17584
+TEXT ·f17584(SB), NOSPLIT, $12
+	MOVSS	x+0(FP), X0
+	MOVSS	y_real+4(FP), X0
+	MOVSS	y_imag+8(FP), X0
+	RET
diff --git a/src/cmd/vet/testdata/asm2.s b/src/cmd/vet/testdata/asm/asm2.s
similarity index 100%
rename from src/cmd/vet/testdata/asm2.s
rename to src/cmd/vet/testdata/asm/asm2.s
diff --git a/src/cmd/vet/testdata/asm3.s b/src/cmd/vet/testdata/asm/asm3.s
similarity index 100%
rename from src/cmd/vet/testdata/asm3.s
rename to src/cmd/vet/testdata/asm/asm3.s
diff --git a/src/cmd/vet/testdata/asm4.s b/src/cmd/vet/testdata/asm/asm4.s
similarity index 100%
rename from src/cmd/vet/testdata/asm4.s
rename to src/cmd/vet/testdata/asm/asm4.s
diff --git a/src/cmd/vet/testdata/asm5.s b/src/cmd/vet/testdata/asm/asm5.s
similarity index 100%
rename from src/cmd/vet/testdata/asm5.s
rename to src/cmd/vet/testdata/asm/asm5.s
diff --git a/src/cmd/vet/testdata/asm/asm6.s b/src/cmd/vet/testdata/asm/asm6.s
new file mode 100644
index 0000000..4e85ab3
--- /dev/null
+++ b/src/cmd/vet/testdata/asm/asm6.s
@@ -0,0 +1,193 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x
+// +build vet_test
+
+TEXT ·arg1(SB),0,$0-2
+	MOVB	x+0(FP), R1
+	MOVBZ	y+1(FP), R2
+	MOVH	x+0(FP), R1 // ERROR "\[s390x\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
+	MOVHZ	y+1(FP), R1 // ERROR "invalid MOVHZ of y\+1\(FP\); uint8 is 1-byte value"
+	MOVW	x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
+	MOVWZ	y+1(FP), R1 // ERROR "invalid MOVWZ of y\+1\(FP\); uint8 is 1-byte value"
+	MOVD	x+0(FP), R1 // ERROR "invalid MOVD of x\+0\(FP\); int8 is 1-byte value"
+	MOVD	y+1(FP), R1 // ERROR "invalid MOVD of y\+1\(FP\); uint8 is 1-byte value"
+	MOVB	x+1(FP), R1 // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
+	MOVBZ	y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
+	MOVB	16(R15), R1 // ERROR "16\(R15\) should be x\+0\(FP\)"
+	MOVB	17(R15), R1 // ERROR "17\(R15\) should be y\+1\(FP\)"
+	MOVB	18(R15), R1 // ERROR "use of 18\(R15\) points beyond argument frame"
+	RET
+
+TEXT ·arg2(SB),0,$0-4
+	MOVBZ	x+0(FP), R1 // ERROR "arg2: invalid MOVBZ of x\+0\(FP\); int16 is 2-byte value"
+	MOVB	y+2(FP), R1 // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
+	MOVHZ	x+0(FP), R1
+	MOVH	y+2(FP), R2
+	MOVWZ	x+0(FP), R1 // ERROR "invalid MOVWZ of x\+0\(FP\); int16 is 2-byte value"
+	MOVW	y+2(FP), R1 // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
+	MOVD	x+0(FP), R1 // ERROR "invalid MOVD of x\+0\(FP\); int16 is 2-byte value"
+	MOVD	y+2(FP), R1 // ERROR "invalid MOVD of y\+2\(FP\); uint16 is 2-byte value"
+	MOVHZ	x+2(FP), R1 // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
+	MOVH	y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
+	RET
+
+TEXT ·arg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8"
+	MOVB	x+0(FP), R1 // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
+	MOVB	y+4(FP), R2 // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
+	MOVH	y+4(FP), R1 // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
+	MOVW	x+0(FP), R1
+	MOVW	y+4(FP), R1
+	MOVD	x+0(FP), R1 // ERROR "invalid MOVD of x\+0\(FP\); int32 is 4-byte value"
+	MOVD	y+4(FP), R1 // ERROR "invalid MOVD of y\+4\(FP\); uint32 is 4-byte value"
+	MOVW	x+4(FP), R1 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+	MOVW	y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+	RET
+
+TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+	MOVB	x+0(FP), R1 // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
+	MOVB	y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
+	MOVH	y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
+	MOVW	x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
+	MOVW	y+8(FP), R1 // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
+	MOVD	x+0(FP), R1
+	MOVD	y+8(FP), R1
+	MOVD	x+8(FP), R1 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	MOVD	y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	RET
+
+TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+	MOVB	x+0(FP), R1 // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value"
+	MOVB	y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int is 8-byte value"
+	MOVH	y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); uint is 8-byte value"
+	MOVW	x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value"
+	MOVW	y+8(FP), R1 // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value"
+	MOVD	x+0(FP), R1
+	MOVD	y+8(FP), R1
+	MOVD	x+8(FP), R1 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	MOVD	y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	RET
+
+TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-40"
+	MOVB	x+0(FP), R1 // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value"
+	MOVB	y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 8-byte value"
+	MOVH	y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); \*byte is 8-byte value"
+	MOVW	x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value"
+	MOVW	y+8(FP), R1 // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value"
+	MOVD	x+0(FP), R1
+	MOVD	y+8(FP), R1
+	MOVD	x+8(FP), R1 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	MOVD	y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	MOVW	c+16(FP), R1 // ERROR "invalid MOVW of c\+16\(FP\); chan int is 8-byte value"
+	MOVW	m+24(FP), R1 // ERROR "invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value"
+	MOVW	f+32(FP), R1 // ERROR "invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value"
+	RET
+
+TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); string base is 8-byte value"
+	MOVW	x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value"
+	MOVD	x+0(FP), R1
+	MOVH	x_base+0(FP), R1 // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 8-byte value"
+	MOVW	x_base+0(FP), R1 // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value"
+	MOVD	x_base+0(FP), R1
+	MOVH	x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVW	x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVD	x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVH	x_len+8(FP), R1 // ERROR "invalid MOVH of x_len\+8\(FP\); string len is 8-byte value"
+	MOVW	x_len+8(FP), R1 // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value"
+	MOVD	x_len+8(FP), R1
+	MOVD	y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)"
+	MOVD	y_len+8(FP), R1 // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)"
+	RET
+
+TEXT ·argslice(SB),0,$48 // ERROR "wrong argument size 0; expected \$\.\.\.-48"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); slice base is 8-byte value"
+	MOVW	x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value"
+	MOVD	x+0(FP), R1
+	MOVH	x_base+0(FP), R1 // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value"
+	MOVW	x_base+0(FP), R1 // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value"
+	MOVD	x_base+0(FP), R1
+	MOVH	x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVW	x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVD	x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVH	x_len+8(FP), R1 // ERROR "invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value"
+	MOVW	x_len+8(FP), R1 // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value"
+	MOVD	x_len+8(FP), R1
+	MOVH	x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+	MOVW	x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+	MOVD	x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+	MOVH	x_cap+16(FP), R1 // ERROR "invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value"
+	MOVW	x_cap+16(FP), R1 // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value"
+	MOVD	x_cap+16(FP), R1
+	MOVD	y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)"
+	MOVD	y_len+8(FP), R1 // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)"
+	MOVD	y_cap+16(FP), R1 // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)"
+	RET
+
+TEXT ·argiface(SB),0,$0-32
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); interface type is 8-byte value"
+	MOVW	x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value"
+	MOVD	x+0(FP), R1
+	MOVH	x_type+0(FP), R1 // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value"
+	MOVW	x_type+0(FP), R1 // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value"
+	MOVD	x_type+0(FP), R1
+	MOVD	x_itable+0(FP), R1 // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
+	MOVD	x_itable+1(FP), R1 // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
+	MOVH	x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+	MOVW	x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+	MOVD	x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+	MOVH	x_data+8(FP), R1 // ERROR "invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value"
+	MOVW	x_data+8(FP), R1 // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value"
+	MOVD	x_data+8(FP), R1
+	MOVH	y+16(FP), R1 // ERROR "invalid MOVH of y\+16\(FP\); interface itable is 8-byte value"
+	MOVW	y+16(FP), R1 // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value"
+	MOVD	y+16(FP), R1
+	MOVH	y_itable+16(FP), R1 // ERROR "invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value"
+	MOVW	y_itable+16(FP), R1 // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value"
+	MOVD	y_itable+16(FP), R1
+	MOVD	y_type+16(FP), R1 // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)"
+	MOVH	y_data+16(FP), R1 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+	MOVW	y_data+16(FP), R1 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+	MOVD	y_data+16(FP), R1 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+	MOVH	y_data+24(FP), R1 // ERROR "invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value"
+	MOVW	y_data+24(FP), R1 // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value"
+	MOVD	y_data+24(FP), R1
+	RET
+
+TEXT ·returnint(SB),0,$0-8
+	MOVB	R1, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value"
+	MOVH	R1, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 8-byte value"
+	MOVW	R1, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value"
+	MOVD	R1, ret+0(FP)
+	MOVD	R1, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
+	MOVD	R1, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
+	RET
+
+TEXT ·returnbyte(SB),0,$0-9
+	MOVD	x+0(FP), R1
+	MOVB	R1, ret+8(FP)
+	MOVH	R1, ret+8(FP) // ERROR "invalid MOVH of ret\+8\(FP\); byte is 1-byte value"
+	MOVW	R1, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value"
+	MOVD	R1, ret+8(FP) // ERROR "invalid MOVD of ret\+8\(FP\); byte is 1-byte value"
+	MOVB	R1, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)"
+	RET
+
+TEXT ·returnnamed(SB),0,$0-41
+	MOVB	x+0(FP), R1
+	MOVD	R1, r1+8(FP)
+	MOVH	R1, r2+16(FP)
+	MOVD	R1, r3+24(FP)
+	MOVD	R1, r3_base+24(FP)
+	MOVD	R1, r3_len+32(FP)
+	MOVB	R1, r4+40(FP)
+	MOVW	R1, r1+8(FP) // ERROR "invalid MOVW of r1\+8\(FP\); int is 8-byte value"
+	RET
+
+TEXT ·returnintmissing(SB),0,$0-8
+	RET // ERROR "RET without writing to 8-byte ret\+0\(FP\)"
diff --git a/src/cmd/vet/testdata/asm/asm7.s b/src/cmd/vet/testdata/asm/asm7.s
new file mode 100644
index 0000000..d5ff546
--- /dev/null
+++ b/src/cmd/vet/testdata/asm/asm7.s
@@ -0,0 +1,193 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ppc64 ppc64le
+// +build vet_test
+
+TEXT ·arg1(SB),0,$0-2
+	MOVB	x+0(FP), R3
+	MOVBZ	y+1(FP), R4
+	MOVH	x+0(FP), R3 // ERROR "\[(ppc64|ppc64le)\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
+	MOVHZ	y+1(FP), R3 // ERROR "invalid MOVHZ of y\+1\(FP\); uint8 is 1-byte value"
+	MOVW	x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
+	MOVWZ	y+1(FP), R3 // ERROR "invalid MOVWZ of y\+1\(FP\); uint8 is 1-byte value"
+	MOVD	x+0(FP), R3 // ERROR "invalid MOVD of x\+0\(FP\); int8 is 1-byte value"
+	MOVD	y+1(FP), R3 // ERROR "invalid MOVD of y\+1\(FP\); uint8 is 1-byte value"
+	MOVB	x+1(FP), R3 // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
+	MOVBZ	y+2(FP), R3 // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
+	MOVB	16(R1), R3 // ERROR "16\(R1\) should be x\+0\(FP\)"
+	MOVB	17(R1), R3 // ERROR "17\(R1\) should be y\+1\(FP\)"
+	MOVB	18(R1), R3 // ERROR "use of 18\(R1\) points beyond argument frame"
+	RET
+
+TEXT ·arg2(SB),0,$0-4
+	MOVBZ	x+0(FP), R3 // ERROR "arg2: invalid MOVBZ of x\+0\(FP\); int16 is 2-byte value"
+	MOVB	y+2(FP), R3 // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
+	MOVHZ	x+0(FP), R3
+	MOVH	y+2(FP), R4
+	MOVWZ	x+0(FP), R3 // ERROR "invalid MOVWZ of x\+0\(FP\); int16 is 2-byte value"
+	MOVW	y+2(FP), R3 // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
+	MOVD	x+0(FP), R3 // ERROR "invalid MOVD of x\+0\(FP\); int16 is 2-byte value"
+	MOVD	y+2(FP), R3 // ERROR "invalid MOVD of y\+2\(FP\); uint16 is 2-byte value"
+	MOVHZ	x+2(FP), R3 // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
+	MOVH	y+0(FP), R3 // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
+	RET
+
+TEXT ·arg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8"
+	MOVB	x+0(FP), R3 // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
+	MOVB	y+4(FP), R4 // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
+	MOVH	x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
+	MOVH	y+4(FP), R3 // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
+	MOVW	x+0(FP), R3
+	MOVW	y+4(FP), R3
+	MOVD	x+0(FP), R3 // ERROR "invalid MOVD of x\+0\(FP\); int32 is 4-byte value"
+	MOVD	y+4(FP), R3 // ERROR "invalid MOVD of y\+4\(FP\); uint32 is 4-byte value"
+	MOVW	x+4(FP), R3 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+	MOVW	y+2(FP), R3 // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+	RET
+
+TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+	MOVB	x+0(FP), R3 // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
+	MOVB	y+8(FP), R4 // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
+	MOVH	x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
+	MOVH	y+8(FP), R3 // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
+	MOVW	x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
+	MOVW	y+8(FP), R3 // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
+	MOVD	x+0(FP), R3
+	MOVD	y+8(FP), R3
+	MOVD	x+8(FP), R3 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	MOVD	y+2(FP), R3 // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	RET
+
+TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+	MOVB	x+0(FP), R3 // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value"
+	MOVB	y+8(FP), R4 // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value"
+	MOVH	x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); int is 8-byte value"
+	MOVH	y+8(FP), R3 // ERROR "invalid MOVH of y\+8\(FP\); uint is 8-byte value"
+	MOVW	x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value"
+	MOVW	y+8(FP), R3 // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value"
+	MOVD	x+0(FP), R3
+	MOVD	y+8(FP), R3
+	MOVD	x+8(FP), R3 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	MOVD	y+2(FP), R3 // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	RET
+
+TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-40"
+	MOVB	x+0(FP), R3 // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value"
+	MOVB	y+8(FP), R4 // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value"
+	MOVH	x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 8-byte value"
+	MOVH	y+8(FP), R3 // ERROR "invalid MOVH of y\+8\(FP\); \*byte is 8-byte value"
+	MOVW	x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value"
+	MOVW	y+8(FP), R3 // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value"
+	MOVD	x+0(FP), R3
+	MOVD	y+8(FP), R3
+	MOVD	x+8(FP), R3 // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
+	MOVD	y+2(FP), R3 // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
+	MOVW	c+16(FP), R3 // ERROR "invalid MOVW of c\+16\(FP\); chan int is 8-byte value"
+	MOVW	m+24(FP), R3 // ERROR "invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value"
+	MOVW	f+32(FP), R3 // ERROR "invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value"
+	RET
+
+TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32"
+	MOVH	x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); string base is 8-byte value"
+	MOVW	x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value"
+	MOVD	x+0(FP), R3
+	MOVH	x_base+0(FP), R3 // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 8-byte value"
+	MOVW	x_base+0(FP), R3 // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value"
+	MOVD	x_base+0(FP), R3
+	MOVH	x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVW	x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVD	x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVH	x_len+8(FP), R3 // ERROR "invalid MOVH of x_len\+8\(FP\); string len is 8-byte value"
+	MOVW	x_len+8(FP), R3 // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value"
+	MOVD	x_len+8(FP), R3
+	MOVD	y+0(FP), R3 // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)"
+	MOVD	y_len+8(FP), R3 // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)"
+	RET
+
+TEXT ·argslice(SB),0,$48 // ERROR "wrong argument size 0; expected \$\.\.\.-48"
+	MOVH	x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); slice base is 8-byte value"
+	MOVW	x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value"
+	MOVD	x+0(FP), R3
+	MOVH	x_base+0(FP), R3 // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value"
+	MOVW	x_base+0(FP), R3 // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value"
+	MOVD	x_base+0(FP), R3
+	MOVH	x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVW	x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVD	x_len+0(FP), R3 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
+	MOVH	x_len+8(FP), R3 // ERROR "invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value"
+	MOVW	x_len+8(FP), R3 // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value"
+	MOVD	x_len+8(FP), R3
+	MOVH	x_cap+0(FP), R3 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+	MOVW	x_cap+0(FP), R3 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+	MOVD	x_cap+0(FP), R3 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
+	MOVH	x_cap+16(FP), R3 // ERROR "invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value"
+	MOVW	x_cap+16(FP), R3 // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value"
+	MOVD	x_cap+16(FP), R3
+	MOVD	y+0(FP), R3 // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)"
+	MOVD	y_len+8(FP), R3 // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)"
+	MOVD	y_cap+16(FP), R3 // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)"
+	RET
+
+TEXT ·argiface(SB),0,$0-32
+	MOVH	x+0(FP), R3 // ERROR "invalid MOVH of x\+0\(FP\); interface type is 8-byte value"
+	MOVW	x+0(FP), R3 // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value"
+	MOVD	x+0(FP), R3
+	MOVH	x_type+0(FP), R3 // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value"
+	MOVW	x_type+0(FP), R3 // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value"
+	MOVD	x_type+0(FP), R3
+	MOVD	x_itable+0(FP), R3 // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
+	MOVD	x_itable+1(FP), R3 // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
+	MOVH	x_data+0(FP), R3 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+	MOVW	x_data+0(FP), R3 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+	MOVD	x_data+0(FP), R3 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
+	MOVH	x_data+8(FP), R3 // ERROR "invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value"
+	MOVW	x_data+8(FP), R3 // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value"
+	MOVD	x_data+8(FP), R3
+	MOVH	y+16(FP), R3 // ERROR "invalid MOVH of y\+16\(FP\); interface itable is 8-byte value"
+	MOVW	y+16(FP), R3 // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value"
+	MOVD	y+16(FP), R3
+	MOVH	y_itable+16(FP), R3 // ERROR "invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value"
+	MOVW	y_itable+16(FP), R3 // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value"
+	MOVD	y_itable+16(FP), R3
+	MOVD	y_type+16(FP), R3 // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)"
+	MOVH	y_data+16(FP), R3 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+	MOVW	y_data+16(FP), R3 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+	MOVD	y_data+16(FP), R3 // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
+	MOVH	y_data+24(FP), R3 // ERROR "invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value"
+	MOVW	y_data+24(FP), R3 // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value"
+	MOVD	y_data+24(FP), R3
+	RET
+
+TEXT ·returnint(SB),0,$0-8
+	MOVB	R3, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value"
+	MOVH	R3, ret+0(FP) // ERROR "invalid MOVH of ret\+0\(FP\); int is 8-byte value"
+	MOVW	R3, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value"
+	MOVD	R3, ret+0(FP)
+	MOVD	R3, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
+	MOVD	R3, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
+	RET
+
+TEXT ·returnbyte(SB),0,$0-9
+	MOVD	x+0(FP), R3
+	MOVB	R3, ret+8(FP)
+	MOVH	R3, ret+8(FP) // ERROR "invalid MOVH of ret\+8\(FP\); byte is 1-byte value"
+	MOVW	R3, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value"
+	MOVD	R3, ret+8(FP) // ERROR "invalid MOVD of ret\+8\(FP\); byte is 1-byte value"
+	MOVB	R3, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)"
+	RET
+
+TEXT ·returnnamed(SB),0,$0-41
+	MOVB	x+0(FP), R3
+	MOVD	R3, r1+8(FP)
+	MOVH	R3, r2+16(FP)
+	MOVD	R3, r3+24(FP)
+	MOVD	R3, r3_base+24(FP)
+	MOVD	R3, r3_len+32(FP)
+	MOVB	R3, r4+40(FP)
+	MOVW	R3, r1+8(FP) // ERROR "invalid MOVW of r1\+8\(FP\); int is 8-byte value"
+	RET
+
+TEXT ·returnintmissing(SB),0,$0-8
+	RET // ERROR "RET without writing to 8-byte ret\+0\(FP\)"
diff --git a/src/cmd/vet/testdata/asm1.s b/src/cmd/vet/testdata/asm1.s
deleted file mode 100644
index 2c6f13b..0000000
--- a/src/cmd/vet/testdata/asm1.s
+++ /dev/null
@@ -1,265 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build amd64
-// +build vet_test
-
-TEXT ·arg1(SB),0,$0-2
-	MOVB	x+0(FP), AX
-	// MOVB x+0(FP), AX // commented out instructions used to panic
-	MOVB	y+1(FP), BX
-	MOVW	x+0(FP), AX // ERROR "\[amd64\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
-	MOVW	y+1(FP), AX // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
-	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int8 is 1-byte value"
-	MOVL	y+1(FP), AX // ERROR "invalid MOVL of y\+1\(FP\); uint8 is 1-byte value"
-	MOVQ	x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int8 is 1-byte value"
-	MOVQ	y+1(FP), AX // ERROR "invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value"
-	MOVB	x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
-	MOVB	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
-	TESTB	x+0(FP), AX
-	TESTB	y+1(FP), BX
-	TESTW	x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int8 is 1-byte value"
-	TESTW	y+1(FP), AX // ERROR "invalid TESTW of y\+1\(FP\); uint8 is 1-byte value"
-	TESTL	x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int8 is 1-byte value"
-	TESTL	y+1(FP), AX // ERROR "invalid TESTL of y\+1\(FP\); uint8 is 1-byte value"
-	TESTQ	x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int8 is 1-byte value"
-	TESTQ	y+1(FP), AX // ERROR "invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value"
-	TESTB	x+1(FP), AX // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
-	TESTB	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
-	MOVB	8(SP), AX // ERROR "8\(SP\) should be x\+0\(FP\)"
-	MOVB	9(SP), AX // ERROR "9\(SP\) should be y\+1\(FP\)"
-	MOVB	10(SP), AX // ERROR "use of 10\(SP\) points beyond argument frame"
-	RET
-
-TEXT ·arg2(SB),0,$0-4
-	MOVB	x+0(FP), AX // ERROR "arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value"
-	MOVB	y+2(FP), AX // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
-	MOVW	x+0(FP), AX
-	MOVW	y+2(FP), BX
-	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int16 is 2-byte value"
-	MOVL	y+2(FP), AX // ERROR "invalid MOVL of y\+2\(FP\); uint16 is 2-byte value"
-	MOVQ	x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int16 is 2-byte value"
-	MOVQ	y+2(FP), AX // ERROR "invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value"
-	MOVW	x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
-	MOVW	y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
-	TESTB	x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int16 is 2-byte value"
-	TESTB	y+2(FP), AX // ERROR "invalid TESTB of y\+2\(FP\); uint16 is 2-byte value"
-	TESTW	x+0(FP), AX
-	TESTW	y+2(FP), BX
-	TESTL	x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int16 is 2-byte value"
-	TESTL	y+2(FP), AX // ERROR "invalid TESTL of y\+2\(FP\); uint16 is 2-byte value"
-	TESTQ	x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int16 is 2-byte value"
-	TESTQ	y+2(FP), AX // ERROR "invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value"
-	TESTW	x+2(FP), AX // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
-	TESTW	y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
-	RET
-
-TEXT ·arg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8"
-	MOVB	x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
-	MOVB	y+4(FP), BX // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
-	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int32 is 4-byte value"
-	MOVW	y+4(FP), AX // ERROR "invalid MOVW of y\+4\(FP\); uint32 is 4-byte value"
-	MOVL	x+0(FP), AX
-	MOVL	y+4(FP), AX
-	MOVQ	x+0(FP), AX // ERROR "invalid MOVQ of x\+0\(FP\); int32 is 4-byte value"
-	MOVQ	y+4(FP), AX // ERROR "invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value"
-	MOVL	x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
-	MOVL	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
-	TESTB	x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int32 is 4-byte value"
-	TESTB	y+4(FP), BX // ERROR "invalid TESTB of y\+4\(FP\); uint32 is 4-byte value"
-	TESTW	x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int32 is 4-byte value"
-	TESTW	y+4(FP), AX // ERROR "invalid TESTW of y\+4\(FP\); uint32 is 4-byte value"
-	TESTL	x+0(FP), AX
-	TESTL	y+4(FP), AX
-	TESTQ	x+0(FP), AX // ERROR "invalid TESTQ of x\+0\(FP\); int32 is 4-byte value"
-	TESTQ	y+4(FP), AX // ERROR "invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value"
-	TESTL	x+4(FP), AX // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
-	TESTL	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
-	RET
-
-TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
-	MOVB	x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
-	MOVB	y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
-	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value"
-	MOVW	y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value"
-	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int64 is 8-byte value"
-	MOVL	y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint64 is 8-byte value"
-	MOVQ	x+0(FP), AX
-	MOVQ	y+8(FP), AX
-	MOVQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
-	MOVQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
-	TESTB	x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int64 is 8-byte value"
-	TESTB	y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint64 is 8-byte value"
-	TESTW	x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int64 is 8-byte value"
-	TESTW	y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint64 is 8-byte value"
-	TESTL	x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int64 is 8-byte value"
-	TESTL	y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint64 is 8-byte value"
-	TESTQ	x+0(FP), AX
-	TESTQ	y+8(FP), AX
-	TESTQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
-	TESTQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
-	RET
-
-TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
-	MOVB	x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); int is 8-byte value"
-	MOVB	y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); uint is 8-byte value"
-	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); int is 8-byte value"
-	MOVW	y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); uint is 8-byte value"
-	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); int is 8-byte value"
-	MOVL	y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); uint is 8-byte value"
-	MOVQ	x+0(FP), AX
-	MOVQ	y+8(FP), AX
-	MOVQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
-	MOVQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
-	TESTB	x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); int is 8-byte value"
-	TESTB	y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); uint is 8-byte value"
-	TESTW	x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); int is 8-byte value"
-	TESTW	y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); uint is 8-byte value"
-	TESTL	x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); int is 8-byte value"
-	TESTL	y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); uint is 8-byte value"
-	TESTQ	x+0(FP), AX
-	TESTQ	y+8(FP), AX
-	TESTQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
-	TESTQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
-	RET
-
-TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-40"
-	MOVB	x+0(FP), AX // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 8-byte value"
-	MOVB	y+8(FP), BX // ERROR "invalid MOVB of y\+8\(FP\); \*byte is 8-byte value"
-	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); \*byte is 8-byte value"
-	MOVW	y+8(FP), AX // ERROR "invalid MOVW of y\+8\(FP\); \*byte is 8-byte value"
-	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); \*byte is 8-byte value"
-	MOVL	y+8(FP), AX // ERROR "invalid MOVL of y\+8\(FP\); \*byte is 8-byte value"
-	MOVQ	x+0(FP), AX
-	MOVQ	y+8(FP), AX
-	MOVQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
-	MOVQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
-	TESTB	x+0(FP), AX // ERROR "invalid TESTB of x\+0\(FP\); \*byte is 8-byte value"
-	TESTB	y+8(FP), BX // ERROR "invalid TESTB of y\+8\(FP\); \*byte is 8-byte value"
-	TESTW	x+0(FP), AX // ERROR "invalid TESTW of x\+0\(FP\); \*byte is 8-byte value"
-	TESTW	y+8(FP), AX // ERROR "invalid TESTW of y\+8\(FP\); \*byte is 8-byte value"
-	TESTL	x+0(FP), AX // ERROR "invalid TESTL of x\+0\(FP\); \*byte is 8-byte value"
-	TESTL	y+8(FP), AX // ERROR "invalid TESTL of y\+8\(FP\); \*byte is 8-byte value"
-	TESTQ	x+0(FP), AX
-	TESTQ	y+8(FP), AX
-	TESTQ	x+8(FP), AX // ERROR "invalid offset x\+8\(FP\); expected x\+0\(FP\)"
-	TESTQ	y+2(FP), AX // ERROR "invalid offset y\+2\(FP\); expected y\+8\(FP\)"
-	MOVL	c+16(FP), AX // ERROR "invalid MOVL of c\+16\(FP\); chan int is 8-byte value"
-	MOVL	m+24(FP), AX // ERROR "invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value"
-	MOVL	f+32(FP), AX // ERROR "invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value"
-	RET
-
-TEXT ·argstring(SB),0,$32 // ERROR "wrong argument size 0; expected \$\.\.\.-32"
-	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); string base is 8-byte value"
-	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); string base is 8-byte value"
-	MOVQ	x+0(FP), AX
-	MOVW	x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); string base is 8-byte value"
-	MOVL	x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); string base is 8-byte value"
-	MOVQ	x_base+0(FP), AX
-	MOVW	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
-	MOVL	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
-	MOVQ	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
-	MOVW	x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); string len is 8-byte value"
-	MOVL	x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); string len is 8-byte value"
-	MOVQ	x_len+8(FP), AX
-	MOVQ	y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+16\(FP\)"
-	MOVQ	y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)"
-	RET
-
-TEXT ·argslice(SB),0,$48 // ERROR "wrong argument size 0; expected \$\.\.\.-48"
-	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); slice base is 8-byte value"
-	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); slice base is 8-byte value"
-	MOVQ	x+0(FP), AX
-	MOVW	x_base+0(FP), AX // ERROR "invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value"
-	MOVL	x_base+0(FP), AX // ERROR "invalid MOVL of x_base\+0\(FP\); slice base is 8-byte value"
-	MOVQ	x_base+0(FP), AX
-	MOVW	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
-	MOVL	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
-	MOVQ	x_len+0(FP), AX // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)"
-	MOVW	x_len+8(FP), AX // ERROR "invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value"
-	MOVL	x_len+8(FP), AX // ERROR "invalid MOVL of x_len\+8\(FP\); slice len is 8-byte value"
-	MOVQ	x_len+8(FP), AX
-	MOVW	x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
-	MOVL	x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
-	MOVQ	x_cap+0(FP), AX // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)"
-	MOVW	x_cap+16(FP), AX // ERROR "invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value"
-	MOVL	x_cap+16(FP), AX // ERROR "invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value"
-	MOVQ	x_cap+16(FP), AX
-	MOVQ	y+0(FP), AX // ERROR "invalid offset y\+0\(FP\); expected y\+24\(FP\)"
-	MOVQ	y_len+8(FP), AX // ERROR "invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)"
-	MOVQ	y_cap+16(FP), AX // ERROR "invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)"
-	RET
-
-TEXT ·argiface(SB),0,$0-32
-	MOVW	x+0(FP), AX // ERROR "invalid MOVW of x\+0\(FP\); interface type is 8-byte value"
-	MOVL	x+0(FP), AX // ERROR "invalid MOVL of x\+0\(FP\); interface type is 8-byte value"
-	MOVQ	x+0(FP), AX
-	MOVW	x_type+0(FP), AX // ERROR "invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value"
-	MOVL	x_type+0(FP), AX // ERROR "invalid MOVL of x_type\+0\(FP\); interface type is 8-byte value"
-	MOVQ	x_type+0(FP), AX
-	MOVQ	x_itable+0(FP), AX // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
-	MOVQ	x_itable+1(FP), AX // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
-	MOVW	x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
-	MOVL	x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
-	MOVQ	x_data+0(FP), AX // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)"
-	MOVW	x_data+8(FP), AX // ERROR "invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value"
-	MOVL	x_data+8(FP), AX // ERROR "invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value"
-	MOVQ	x_data+8(FP), AX
-	MOVW	y+16(FP), AX // ERROR "invalid MOVW of y\+16\(FP\); interface itable is 8-byte value"
-	MOVL	y+16(FP), AX // ERROR "invalid MOVL of y\+16\(FP\); interface itable is 8-byte value"
-	MOVQ	y+16(FP), AX
-	MOVW	y_itable+16(FP), AX // ERROR "invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value"
-	MOVL	y_itable+16(FP), AX // ERROR "invalid MOVL of y_itable\+16\(FP\); interface itable is 8-byte value"
-	MOVQ	y_itable+16(FP), AX
-	MOVQ	y_type+16(FP), AX // ERROR "unknown variable y_type; offset 16 is y_itable\+16\(FP\)"
-	MOVW	y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
-	MOVL	y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
-	MOVQ	y_data+16(FP), AX // ERROR "invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)"
-	MOVW	y_data+24(FP), AX // ERROR "invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value"
-	MOVL	y_data+24(FP), AX // ERROR "invalid MOVL of y_data\+24\(FP\); interface data is 8-byte value"
-	MOVQ	y_data+24(FP), AX
-	RET
-
-TEXT ·returnint(SB),0,$0-8
-	MOVB	AX, ret+0(FP) // ERROR "invalid MOVB of ret\+0\(FP\); int is 8-byte value"
-	MOVW	AX, ret+0(FP) // ERROR "invalid MOVW of ret\+0\(FP\); int is 8-byte value"
-	MOVL	AX, ret+0(FP) // ERROR "invalid MOVL of ret\+0\(FP\); int is 8-byte value"
-	MOVQ	AX, ret+0(FP)
-	MOVQ	AX, ret+1(FP) // ERROR "invalid offset ret\+1\(FP\); expected ret\+0\(FP\)"
-	MOVQ	AX, r+0(FP) // ERROR "unknown variable r; offset 0 is ret\+0\(FP\)"
-	RET
-
-TEXT ·returnbyte(SB),0,$0-9
-	MOVQ	x+0(FP), AX
-	MOVB	AX, ret+8(FP)
-	MOVW	AX, ret+8(FP) // ERROR "invalid MOVW of ret\+8\(FP\); byte is 1-byte value"
-	MOVL	AX, ret+8(FP) // ERROR "invalid MOVL of ret\+8\(FP\); byte is 1-byte value"
-	MOVQ	AX, ret+8(FP) // ERROR "invalid MOVQ of ret\+8\(FP\); byte is 1-byte value"
-	MOVB	AX, ret+7(FP) // ERROR "invalid offset ret\+7\(FP\); expected ret\+8\(FP\)"
-	RET
-
-TEXT ·returnnamed(SB),0,$0-41
-	MOVB	x+0(FP), AX
-	MOVQ	AX, r1+8(FP)
-	MOVW	AX, r2+16(FP)
-	MOVQ	AX, r3+24(FP)
-	MOVQ	AX, r3_base+24(FP)
-	MOVQ	AX, r3_len+32(FP)
-	MOVB	AX, r4+40(FP)
-	MOVL	AX, r1+8(FP) // ERROR "invalid MOVL of r1\+8\(FP\); int is 8-byte value"
-	RET
-
-TEXT ·returnintmissing(SB),0,$0-8
-	RET // ERROR "RET without writing to 8-byte ret\+0\(FP\)"
-
-
-// issue 15271
-TEXT ·f15271(SB), NOSPLIT, $0-4
-    // Stick 123 into the low 32 bits of X0.
-    MOVQ $123, AX
-    PINSRD $0, AX, X0
-
-    // Return them.
-    PEXTRD $0, X0, x+0(FP)
-    RET
diff --git a/src/cmd/vet/testdata/asm8.s b/src/cmd/vet/testdata/asm8.s
new file mode 100644
index 0000000..550d92a
--- /dev/null
+++ b/src/cmd/vet/testdata/asm8.s
@@ -0,0 +1,165 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mipsle
+// +build vet_test
+
+TEXT ·arg1(SB),0,$0-2
+	MOVB	x+0(FP), R1
+	MOVBU	y+1(FP), R2
+	MOVH	x+0(FP), R1 // ERROR "\[mipsle\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value"
+	MOVHU	y+1(FP), R1 // ERROR "invalid MOVHU of y\+1\(FP\); uint8 is 1-byte value"
+	MOVW	x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int8 is 1-byte value"
+	MOVWU	y+1(FP), R1 // ERROR "invalid MOVWU of y\+1\(FP\); uint8 is 1-byte value"
+	MOVW	y+1(FP), R1 // ERROR "invalid MOVW of y\+1\(FP\); uint8 is 1-byte value"
+	MOVB	x+1(FP), R1 // ERROR "invalid offset x\+1\(FP\); expected x\+0\(FP\)"
+	MOVBU	y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+1\(FP\)"
+	MOVB	8(R29), R1 // ERROR "8\(R29\) should be x\+0\(FP\)"
+	MOVB	9(R29), R1 // ERROR "9\(R29\) should be y\+1\(FP\)"
+	MOVB	10(R29), R1 // ERROR "use of 10\(R29\) points beyond argument frame"
+	RET
+
+TEXT ·arg2(SB),0,$0-4
+	MOVBU	x+0(FP), R1 // ERROR "arg2: invalid MOVBU of x\+0\(FP\); int16 is 2-byte value"
+	MOVB	y+2(FP), R1 // ERROR "invalid MOVB of y\+2\(FP\); uint16 is 2-byte value"
+	MOVHU	x+0(FP), R1
+	MOVH	y+2(FP), R2
+	MOVWU	x+0(FP), R1 // ERROR "invalid MOVWU of x\+0\(FP\); int16 is 2-byte value"
+	MOVW	y+2(FP), R1 // ERROR "invalid MOVW of y\+2\(FP\); uint16 is 2-byte value"
+	MOVHU	x+2(FP), R1 // ERROR "invalid offset x\+2\(FP\); expected x\+0\(FP\)"
+	MOVH	y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+2\(FP\)"
+	RET
+
+TEXT ·arg4(SB),0,$0-2 // ERROR "arg4: wrong argument size 2; expected \$\.\.\.-8"
+	MOVB	x+0(FP), R1 // ERROR "invalid MOVB of x\+0\(FP\); int32 is 4-byte value"
+	MOVB	y+4(FP), R2 // ERROR "invalid MOVB of y\+4\(FP\); uint32 is 4-byte value"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int32 is 4-byte value"
+	MOVH	y+4(FP), R1 // ERROR "invalid MOVH of y\+4\(FP\); uint32 is 4-byte value"
+	MOVW	x+0(FP), R1
+	MOVW	y+4(FP), R1
+	MOVW	x+4(FP), R1 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+	MOVW	y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+	RET
+
+TEXT ·arg8(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-16"
+	MOVB	x+0(FP), R1 // ERROR "invalid MOVB of x\+0\(FP\); int64 is 8-byte value"
+	MOVB	y+8(FP), R2 // ERROR "invalid MOVB of y\+8\(FP\); uint64 is 8-byte value"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int64 is 8-byte value"
+	MOVH	y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); uint64 is 8-byte value"
+	MOVW	x+0(FP), R1 // ERROR "invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)"
+	MOVW	x_lo+0(FP), R1
+	MOVW	x_hi+4(FP), R1
+	MOVW	y+8(FP), R1 // ERROR "invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)"
+	MOVW	y_lo+8(FP),  R1
+	MOVW	y_hi+12(FP), R1
+	RET
+
+TEXT ·argint(SB),0,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-8"
+	MOVB	x+0(FP), R1 // ERROR "invalid MOVB of x\+0\(FP\); int is 4-byte value"
+	MOVB	y+4(FP), R2 // ERROR "invalid MOVB of y\+4\(FP\); uint is 4-byte value"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); int is 4-byte value"
+	MOVH	y+4(FP), R1 // ERROR "invalid MOVH of y\+4\(FP\); uint is 4-byte value"
+	MOVW	x+0(FP), R1
+	MOVW	y+4(FP), R1
+	MOVW	x+4(FP), R1 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+	MOVW	y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+	RET
+
+TEXT ·argptr(SB),7,$0-2 // ERROR "wrong argument size 2; expected \$\.\.\.-20"
+	MOVB	x+0(FP), R1 // ERROR "invalid MOVB of x\+0\(FP\); \*byte is 4-byte value"
+	MOVB	y+4(FP), R2 // ERROR "invalid MOVB of y\+4\(FP\); \*byte is 4-byte value"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); \*byte is 4-byte value"
+	MOVH	y+4(FP), R1 // ERROR "invalid MOVH of y\+4\(FP\); \*byte is 4-byte value"
+	MOVW	x+0(FP), R1
+	MOVW	y+4(FP), R1
+	MOVW	x+4(FP), R1 // ERROR "invalid offset x\+4\(FP\); expected x\+0\(FP\)"
+	MOVW	y+2(FP), R1 // ERROR "invalid offset y\+2\(FP\); expected y\+4\(FP\)"
+	MOVH	c+8(FP), R1 // ERROR "invalid MOVH of c\+8\(FP\); chan int is 4-byte value"
+	MOVH	m+12(FP), R1 // ERROR "invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value"
+	MOVH	f+16(FP), R1 // ERROR "invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value"
+	RET
+
+TEXT ·argstring(SB),0,$16 // ERROR "wrong argument size 0; expected \$\.\.\.-16"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); string base is 4-byte value"
+	MOVW	x+0(FP), R1
+	MOVH	x_base+0(FP), R1 // ERROR "invalid MOVH of x_base\+0\(FP\); string base is 4-byte value"
+	MOVW	x_base+0(FP), R1
+	MOVH	x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+	MOVW	x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+	MOVH	x_len+4(FP), R1 // ERROR "invalid MOVH of x_len\+4\(FP\); string len is 4-byte value"
+	MOVW	x_len+4(FP), R1
+	MOVW	y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+8\(FP\)"
+	MOVW	y_len+4(FP), R1 // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)"
+	RET
+
+TEXT ·argslice(SB),0,$24 // ERROR "wrong argument size 0; expected \$\.\.\.-24"
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); slice base is 4-byte value"
+	MOVW	x+0(FP), R1
+	MOVH	x_base+0(FP), R1 // ERROR "invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value"
+	MOVW	x_base+0(FP), R1
+	MOVH	x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+	MOVW	x_len+0(FP), R1 // ERROR "invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)"
+	MOVH	x_len+4(FP), R1 // ERROR "invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value"
+	MOVW	x_len+4(FP), R1
+	MOVH	x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
+	MOVW	x_cap+0(FP), R1 // ERROR "invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)"
+	MOVH	x_cap+8(FP), R1 // ERROR "invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value"
+	MOVW	x_cap+8(FP), R1
+	MOVW	y+0(FP), R1 // ERROR "invalid offset y\+0\(FP\); expected y\+12\(FP\)"
+	MOVW	y_len+4(FP), R1 // ERROR "invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)"
+	MOVW	y_cap+8(FP), R1 // ERROR "invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)"
+	RET
+
+TEXT ·argiface(SB),0,$0-16
+	MOVH	x+0(FP), R1 // ERROR "invalid MOVH of x\+0\(FP\); interface type is 4-byte value"
+	MOVW	x+0(FP), R1
+	MOVH	x_type+0(FP), R1 // ERROR "invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value"
+	MOVW	x_type+0(FP), R1
+	MOVQ	x_itable+0(FP), R1 // ERROR "unknown variable x_itable; offset 0 is x_type\+0\(FP\)"
+	MOVQ	x_itable+1(FP), R1 // ERROR "unknown variable x_itable; offset 1 is x_type\+0\(FP\)"
+	MOVH	x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
+	MOVW	x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
+	MOVQ	x_data+0(FP), R1 // ERROR "invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)"
+	MOVH	x_data+4(FP), R1 // ERROR "invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value"
+	MOVW	x_data+4(FP), R1
+	MOVH	y+8(FP), R1 // ERROR "invalid MOVH of y\+8\(FP\); interface itable is 4-byte value"
+	MOVW	y+8(FP), R1
+	MOVH	y_itable+8(FP), R1 // ERROR "invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value"
+	MOVW	y_itable+8(FP), R1
+	MOVW	y_type+8(FP), AX // ERROR "unknown variable y_type; offset 8 is y_itable\+8\(FP\)"
+	MOVH	y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
+	MOVW	y_data+8(FP), AX // ERROR "invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)"
+	MOVH	y_data+12(FP), AX // ERROR "invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value"
+	MOVW	y_data+12(FP), AX
+	RET
+
+TEXT ·returnbyte(SB),0,$0-5
+	MOVW	x+0(FP), R1
+	MOVB	R1, ret+4(FP)
+	MOVH	R1, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value"
+	MOVW	R1, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
+	MOVB	R1, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)"
+	RET
+
+TEXT ·returnbyte(SB),0,$0-5
+	MOVW	x+0(FP), R1
+	MOVB	R1, ret+4(FP)
+	MOVH	R1, ret+4(FP) // ERROR "invalid MOVH of ret\+4\(FP\); byte is 1-byte value"
+	MOVW	R1, ret+4(FP) // ERROR "invalid MOVW of ret\+4\(FP\); byte is 1-byte value"
+	MOVB	R1, ret+3(FP) // ERROR "invalid offset ret\+3\(FP\); expected ret\+4\(FP\)"
+	RET
+
+TEXT ·returnnamed(SB),0,$0-21
+	MOVB	x+0(FP), AX
+	MOVW	R1, r1+4(FP)
+	MOVH	R1, r2+8(FP)
+	MOVW	R1, r3+12(FP)
+	MOVW	R1, r3_base+12(FP)
+	MOVW	R1, r3_len+16(FP)
+	MOVB	R1, r4+20(FP)
+	MOVB	R1, r1+4(FP) // ERROR "invalid MOVB of r1\+4\(FP\); int is 4-byte value"
+	RET
+
+TEXT ·returnintmissing(SB),0,$0-4
+	RET // ERROR "RET without writing to 4-byte ret\+0\(FP\)"
diff --git a/src/cmd/vet/testdata/buildtag.go b/src/cmd/vet/testdata/buildtag/buildtag.go
similarity index 100%
rename from src/cmd/vet/testdata/buildtag.go
rename to src/cmd/vet/testdata/buildtag/buildtag.go
diff --git a/src/cmd/vet/testdata/buildtag_bad.go b/src/cmd/vet/testdata/buildtag/buildtag_bad.go
similarity index 100%
rename from src/cmd/vet/testdata/buildtag_bad.go
rename to src/cmd/vet/testdata/buildtag/buildtag_bad.go
diff --git a/src/cmd/vet/testdata/cgo.go b/src/cmd/vet/testdata/cgo.go
deleted file mode 100644
index 5ce6007..0000000
--- a/src/cmd/vet/testdata/cgo.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file contains tests for the cgo checker.
-
-package testdata
-
-// void f(void *);
-import "C"
-
-import "unsafe"
-
-func CgoTests() {
-	var c chan bool
-	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&c))) // ERROR "embedded pointer"
-	C.f(unsafe.Pointer(&c))                     // ERROR "embedded pointer"
-
-	var m map[string]string
-	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&m))) // ERROR "embedded pointer"
-	C.f(unsafe.Pointer(&m))                     // ERROR "embedded pointer"
-
-	var f func()
-	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&f))) // ERROR "embedded pointer"
-	C.f(unsafe.Pointer(&f))                     // ERROR "embedded pointer"
-
-	var s []int
-	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s))) // ERROR "embedded pointer"
-	C.f(unsafe.Pointer(&s))                     // ERROR "embedded pointer"
-
-	var a [1][]int
-	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a))) // ERROR "embedded pointer"
-	C.f(unsafe.Pointer(&a))                     // ERROR "embedded pointer"
-
-	var st struct{ f []int }
-	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st))) // ERROR "embedded pointer"
-	C.f(unsafe.Pointer(&st))                     // ERROR "embedded pointer"
-
-	// The following cases are OK.
-	var i int
-	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&i)))
-	C.f(unsafe.Pointer(&i))
-
-	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s[0])))
-	C.f(unsafe.Pointer(&s[0]))
-
-	var a2 [1]int
-	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a2)))
-	C.f(unsafe.Pointer(&a2))
-
-	var st2 struct{ i int }
-	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st2)))
-	C.f(unsafe.Pointer(&st2))
-}
diff --git a/src/cmd/vet/testdata/cgo/cgo.go b/src/cmd/vet/testdata/cgo/cgo.go
new file mode 100644
index 0000000..25d395b
--- /dev/null
+++ b/src/cmd/vet/testdata/cgo/cgo.go
@@ -0,0 +1,56 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains tests for the cgo checker.
+
+package testdata
+
+// void f(void *);
+import "C"
+
+import "unsafe"
+
+func CgoTests() {
+	var c chan bool
+	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&c))) // ERROR "embedded pointer"
+	C.f(unsafe.Pointer(&c))                     // ERROR "embedded pointer"
+
+	var m map[string]string
+	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&m))) // ERROR "embedded pointer"
+	C.f(unsafe.Pointer(&m))                     // ERROR "embedded pointer"
+
+	var f func()
+	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&f))) // ERROR "embedded pointer"
+	C.f(unsafe.Pointer(&f))                     // ERROR "embedded pointer"
+
+	var s []int
+	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s))) // ERROR "embedded pointer"
+	C.f(unsafe.Pointer(&s))                     // ERROR "embedded pointer"
+
+	var a [1][]int
+	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a))) // ERROR "embedded pointer"
+	C.f(unsafe.Pointer(&a))                     // ERROR "embedded pointer"
+
+	var st struct{ f []int }
+	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st))) // ERROR "embedded pointer"
+	C.f(unsafe.Pointer(&st))                     // ERROR "embedded pointer"
+
+	// The following cases are OK.
+	var i int
+	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&i)))
+	C.f(unsafe.Pointer(&i))
+
+	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s[0])))
+	C.f(unsafe.Pointer(&s[0]))
+
+	var a2 [1]int
+	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a2)))
+	C.f(unsafe.Pointer(&a2))
+
+	var st2 struct{ i int }
+	C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st2)))
+	C.f(unsafe.Pointer(&st2))
+
+	C.CBytes([]byte("hello"))
+}
diff --git a/src/cmd/vet/testdata/cgo2.go b/src/cmd/vet/testdata/cgo/cgo2.go
similarity index 100%
rename from src/cmd/vet/testdata/cgo2.go
rename to src/cmd/vet/testdata/cgo/cgo2.go
diff --git a/src/cmd/vet/testdata/copylock.go b/src/cmd/vet/testdata/copylock.go
index d49f468..35ed766 100644
--- a/src/cmd/vet/testdata/copylock.go
+++ b/src/cmd/vet/testdata/copylock.go
@@ -68,6 +68,24 @@ func BadFunc() {
 	// override 'new' keyword
 	new := func(interface{}) {}
 	new(t) // ERROR "function call copies lock value: testdata.Tlock contains sync.Once contains sync.Mutex"
+
+	// copy of array of locks
+	var muA [5]sync.Mutex
+	muB := muA        // ERROR "assignment copies lock value to muB: sync.Mutex"
+	muA = muB         // ERROR "assignment copies lock value to muA: sync.Mutex"
+	muSlice := muA[:] // OK
+
+	// multidimensional array
+	var mmuA [5][5]sync.Mutex
+	mmuB := mmuA        // ERROR "assignment copies lock value to mmuB: sync.Mutex"
+	mmuA = mmuB         // ERROR "assignment copies lock value to mmuA: sync.Mutex"
+	mmuSlice := mmuA[:] // OK
+
+	// slice copy is ok
+	var fmuA [5][][5]sync.Mutex
+	fmuB := fmuA        // OK
+	fmuA = fmuB         // OK
+	fmuSlice := fmuA[:] // OK
 }
 
 // SyncTypesCheck checks copying of sync.* types except sync.Mutex
diff --git a/src/cmd/vet/testdata/httpresponse.go b/src/cmd/vet/testdata/httpresponse.go
new file mode 100644
index 0000000..7302a64
--- /dev/null
+++ b/src/cmd/vet/testdata/httpresponse.go
@@ -0,0 +1,85 @@
+package testdata
+
+import (
+	"log"
+	"net/http"
+)
+
+func goodHTTPGet() {
+	res, err := http.Get("http://foo.com")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer res.Body.Close()
+}
+
+func badHTTPGet() {
+	res, err := http.Get("http://foo.com")
+	defer res.Body.Close() // ERROR "using res before checking for errors"
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+func badHTTPHead() {
+	res, err := http.Head("http://foo.com")
+	defer res.Body.Close() // ERROR "using res before checking for errors"
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+func goodClientGet() {
+	client := http.DefaultClient
+	res, err := client.Get("http://foo.com")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer res.Body.Close()
+}
+
+func badClientPtrGet() {
+	client := http.DefaultClient
+	resp, err := client.Get("http://foo.com")
+	defer resp.Body.Close() // ERROR "using resp before checking for errors"
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+func badClientGet() {
+	client := http.Client{}
+	resp, err := client.Get("http://foo.com")
+	defer resp.Body.Close() // ERROR "using resp before checking for errors"
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+func badClientPtrDo() {
+	client := http.DefaultClient
+	req, err := http.NewRequest("GET", "http://foo.com", nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	resp, err := client.Do(req)
+	defer resp.Body.Close() // ERROR "using resp before checking for errors"
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+func badClientDo() {
+	var client http.Client
+	req, err := http.NewRequest("GET", "http://foo.com", nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	resp, err := client.Do(req)
+	defer resp.Body.Close() // ERROR "using resp before checking for errors"
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/src/cmd/vet/testdata/print.go b/src/cmd/vet/testdata/print.go
index ab97256..b5c59eb 100644
--- a/src/cmd/vet/testdata/print.go
+++ b/src/cmd/vet/testdata/print.go
@@ -128,11 +128,14 @@ func PrintfTests() {
 	fmt.Printf("%t", stringerarrayv)           // ERROR "arg stringerarrayv for printf verb %t of wrong type"
 	fmt.Printf("%t", notstringerarrayv)        // ERROR "arg notstringerarrayv for printf verb %t of wrong type"
 	fmt.Printf("%q", notstringerarrayv)        // ERROR "arg notstringerarrayv for printf verb %q of wrong type"
-	fmt.Printf("%d", Formatter(true))          // correct (the type is responsible for formatting)
-	fmt.Printf("%s", nonemptyinterface)        // correct (the dynamic type of nonemptyinterface may be a stringer)
+	fmt.Printf("%d", Formatter(true))          // ERROR "arg Formatter\(true\) for printf verb %d of wrong type: testdata.Formatter"
+	fmt.Printf("%z", FormatterVal(true))       // correct (the type is responsible for formatting)
+	fmt.Printf("%d", FormatterVal(true))       // correct (the type is responsible for formatting)
+	fmt.Printf("%s", nonemptyinterface)        // correct (the type is responsible for formatting)
 	fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
 	fmt.Println()                              // not an error
 	fmt.Println("%s", "hi")                    // ERROR "possible formatting directive in Println call"
+	fmt.Println("0.0%")                        // correct (trailing % couldn't be a formatting directive)
 	fmt.Printf("%s", "hi", 3)                  // ERROR "wrong number of args for format in Printf call"
 	_ = fmt.Sprintf("%"+("s"), "hi", 3)        // ERROR "wrong number of args for format in Sprintf call"
 	fmt.Printf("%s%%%d", "hi", 3)              // correct
@@ -173,8 +176,8 @@ func PrintfTests() {
 	Printf("%[2]*.[1]*[3]d", 2, 3, 4)
 	fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly.
 	// Bad argument reorderings.
-	Printf("%[xd", 3)                    // ERROR "illegal syntax for printf argument index"
-	Printf("%[x]d", 3)                   // ERROR "illegal syntax for printf argument index"
+	Printf("%[xd", 3)                    // ERROR "bad syntax for printf argument index: \[xd\]"
+	Printf("%[x]d", 3)                   // ERROR "bad syntax for printf argument index: \[x\]"
 	Printf("%[3]*s", "hi", 2)            // ERROR "missing argument for Printf.* reads arg 3, have only 2"
 	_ = fmt.Sprintf("%[3]d", 2)          // ERROR "missing argument for Sprintf.* reads arg 3, have only 1"
 	Printf("%[2]*.[1]*[3]d", 2, "hi", 4) // ERROR "arg .hi. for \* in printf format not of type int"
@@ -199,6 +202,11 @@ func PrintfTests() {
 	et4.Error() // ok, not an error method.
 	var et5 errorTest5
 	et5.error() // ok, not an error method.
+	// Interfaces can be used with any verb.
+	var iface interface {
+		ToTheMadness() bool // Method ToTheMadness usually returns false
+	}
+	fmt.Printf("%f", iface) // ok: fmt treats interfaces as transparent and iface may well have a float concrete type
 	// Can't print a function.
 	Printf("%d", someFunction) // ERROR "arg someFunction in printf call is a function value, not a function call"
 	Printf("%v", someFunction) // ERROR "arg someFunction in printf call is a function value, not a function call"
@@ -232,6 +240,9 @@ func PrintfTests() {
 	externalprintf.Logf(level, "%d", 42)                        // OK
 	externalprintf.Errorf(level, level, "foo %q bar", "foobar") // OK
 	externalprintf.Logf(level, "%d")                            // ERROR "format reads arg 1, have only 0 args"
+	var formatStr = "%s %s"
+	externalprintf.Sprintf(formatStr, "a", "b")     // OK
+	externalprintf.Logf(level, formatStr, "a", "b") // OK
 
 	// user-defined Println-like functions
 	ss := &someStruct{}
@@ -243,6 +254,15 @@ func PrintfTests() {
 	ss.log(someFunction)                 // OK
 	ss.log(someFunction, "bar", 1.33)    // OK
 	ss.log(someFunction, someFunction)   // ERROR "arg someFunction in log call is a function value, not a function call"
+
+	// indexed arguments
+	Printf("%d %[3]d %d %[2]d", 1, 2, 3, 4)             // OK
+	Printf("%d %[0]d %d %[2]d", 1, 2, 3, 4)             // ERROR "indexes start at 1"
+	Printf("%d %[3]d %d %[-2]d", 1, 2, 3, 4)            // ERROR "bad syntax for printf argument index: \[-2\]"
+	Printf("%d %[3]d %d %[2234234234234]d", 1, 2, 3, 4) // ERROR "bad syntax for printf argument index: .+ value out of range"
+	Printf("%d %[3]d %d %[2]d", 1, 2, 3)                // ERROR "format reads arg 4, have only 3 args"
+	Printf("%d %[3]d %d %[2]d", 1, 2, 3, 4, 5)          // ERROR "wrong number of args for format in Printf call: 4 needed but 5 args"
+	Printf("%[1][3]d", 1, 2)                            // ERROR "unrecognized printf verb '\['"
 }
 
 type someStruct struct{}
@@ -398,6 +418,12 @@ type Formatter bool
 func (*Formatter) Format(fmt.State, rune) {
 }
 
+// Formatter with value receiver
+type FormatterVal bool
+
+func (FormatterVal) Format(fmt.State, rune) {
+}
+
 type RecursiveSlice []RecursiveSlice
 
 var recursiveSliceV = &RecursiveSlice{}
diff --git a/src/cmd/vet/testdata/shift.go b/src/cmd/vet/testdata/shift.go
index 6624f09..99acaad 100644
--- a/src/cmd/vet/testdata/shift.go
+++ b/src/cmd/vet/testdata/shift.go
@@ -75,4 +75,6 @@ func ShiftTest() {
 	_ = p >> 32 // ERROR "p might be too small for shift of 32"
 	p <<= 32    // ERROR "p might be too small for shift of 32"
 	p >>= 32    // ERROR "p might be too small for shift of 32"
+
+	const oneIf64Bit = ^uint(0) >> 63 // allow large shifts of constants; they are used for 32/64 bit compatibility tricks
 }
diff --git a/src/cmd/vet/testdata/structtag.go b/src/cmd/vet/testdata/structtag.go
index 6878f56..cba990f 100644
--- a/src/cmd/vet/testdata/structtag.go
+++ b/src/cmd/vet/testdata/structtag.go
@@ -15,6 +15,8 @@ type StructTagTest struct {
 	F   int `:"emptykey"`      // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag key"
 	G   int `x:"noEndQuote`    // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value"
 	H   int `x:"trunc\x0"`     // ERROR "not compatible with reflect.StructTag.Get: bad syntax for struct tag value"
+	I   int `x:"foo",y:"bar"`  // ERROR "not compatible with reflect.StructTag.Get: key:.value. pairs not separated by spaces"
+	J   int `x:"foo"y:"bar"`   // ERROR "not compatible with reflect.StructTag.Get: key:.value. pairs not separated by spaces"
 	OK0 int `x:"y" u:"v" w:""`
 	OK1 int `x:"y:z" u:"v" w:""` // note multiple colons.
 	OK2 int "k0:\"values contain spaces\" k1:\"literal\ttabs\" k2:\"and\\tescaped\\tabs\""
@@ -34,3 +36,31 @@ type JSONEmbeddedField struct {
 	UnexportedEncodingTagTest `is:"embedded"`
 	unexp                     `is:"embedded,notexported" json:"unexp"` // OK for now, see issue 7363
 }
+
+type DuplicateJSONFields struct {
+	JSON              int `json:"a"`
+	DuplicateJSON     int `json:"a"` // ERROR "struct field DuplicateJSON repeats json tag .a. also at testdata/structtag.go:41"
+	IgnoredJSON       int `json:"-"`
+	OtherIgnoredJSON  int `json:"-"`
+	OmitJSON          int `json:",omitempty"`
+	OtherOmitJSON     int `json:",omitempty"`
+	DuplicateOmitJSON int `json:"a,omitempty"` // ERROR "struct field DuplicateOmitJSON repeats json tag .a. also at testdata/structtag.go:41"
+	NonJSON           int `foo:"a"`
+	DuplicateNonJSON  int `foo:"a"`
+	Embedded          struct {
+		DuplicateJSON int `json:"a"` // OK because its not in the same struct type
+	}
+
+	XML              int `xml:"a"`
+	DuplicateXML     int `xml:"a"` // ERROR "struct field DuplicateXML repeats xml tag .a. also at testdata/structtag.go:54"
+	IgnoredXML       int `xml:"-"`
+	OtherIgnoredXML  int `xml:"-"`
+	OmitXML          int `xml:",omitempty"`
+	OtherOmitXML     int `xml:",omitempty"`
+	DuplicateOmitXML int `xml:"a,omitempty"` // ERROR "struct field DuplicateOmitXML repeats xml tag .a. also at testdata/structtag.go:54"
+	NonXML           int `foo:"a"`
+	DuplicateNonXML  int `foo:"a"`
+	Embedded         struct {
+		DuplicateXML int `xml:"a"` // OK because its not in the same struct type
+	}
+}
diff --git a/src/cmd/vet/testdata/testingpkg/tests.go b/src/cmd/vet/testdata/testingpkg/tests.go
new file mode 100644
index 0000000..69d29d3
--- /dev/null
+++ b/src/cmd/vet/testdata/testingpkg/tests.go
@@ -0,0 +1 @@
+package testdata
diff --git a/src/cmd/vet/testdata/tests_test.go b/src/cmd/vet/testdata/testingpkg/tests_test.go
similarity index 100%
rename from src/cmd/vet/testdata/tests_test.go
rename to src/cmd/vet/testdata/testingpkg/tests_test.go
diff --git a/src/cmd/vet/testdata/unsafeptr.go b/src/cmd/vet/testdata/unsafeptr.go
index e04856e..ce85200 100644
--- a/src/cmd/vet/testdata/unsafeptr.go
+++ b/src/cmd/vet/testdata/unsafeptr.go
@@ -15,13 +15,15 @@ func f() {
 	x = unsafe.Pointer(y) // ERROR "possible misuse of unsafe.Pointer"
 	y = uintptr(x)
 
-	// only allowed pointer arithmetic is ptr +/- num.
+	// only allowed pointer arithmetic is ptr +/-/&^ num.
 	// num+ptr is technically okay but still flagged: write ptr+num instead.
 	x = unsafe.Pointer(uintptr(x) + 1)
 	x = unsafe.Pointer(1 + uintptr(x))          // ERROR "possible misuse of unsafe.Pointer"
 	x = unsafe.Pointer(uintptr(x) + uintptr(x)) // ERROR "possible misuse of unsafe.Pointer"
 	x = unsafe.Pointer(uintptr(x) - 1)
 	x = unsafe.Pointer(1 - uintptr(x)) // ERROR "possible misuse of unsafe.Pointer"
+	x = unsafe.Pointer(uintptr(x) &^ 3)
+	x = unsafe.Pointer(1 &^ uintptr(x)) // ERROR "possible misuse of unsafe.Pointer"
 
 	// certain uses of reflect are okay
 	var v reflect.Value
diff --git a/src/cmd/vet/types.go b/src/cmd/vet/types.go
index 4d0e615..8357d3c 100644
--- a/src/cmd/vet/types.go
+++ b/src/cmd/vet/types.go
@@ -113,8 +113,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
 		}
 	}
 	// If the type implements fmt.Formatter, we have nothing to check.
-	// formatterTyp may be nil - be conservative and check for Format method in that case.
-	if formatterType != nil && types.Implements(typ, formatterType) || f.hasMethod(typ, "Format") {
+	if f.isFormatter(typ) {
 		return true
 	}
 	// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
@@ -185,13 +184,10 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
 		return f.matchStructArgType(t, typ, arg, inProgress)
 
 	case *types.Interface:
-		// If the static type of the argument is empty interface, there's little we can do.
-		// Example:
-		//	func f(x interface{}) { fmt.Printf("%s", x) }
-		// Whether x is valid for %s depends on the type of the argument to f. One day
-		// we will be able to do better. For now, we assume that empty interface is OK
-		// but non-empty interfaces, with Stringer and Error handled above, are errors.
-		return typ.NumMethods() == 0
+		// There's little we can do.
+		// Whether any particular verb is valid depends on the argument.
+		// The user may have reasonable prior knowledge of the contents of the interface.
+		return true
 
 	case *types.Basic:
 		switch typ.Kind() {
diff --git a/src/cmd/vet/unsafeptr.go b/src/cmd/vet/unsafeptr.go
index a143e4d..cb2cc81 100644
--- a/src/cmd/vet/unsafeptr.go
+++ b/src/cmd/vet/unsafeptr.go
@@ -89,7 +89,7 @@ func (f *File) isSafeUintptr(x ast.Expr) bool {
 
 	case *ast.BinaryExpr:
 		switch x.Op {
-		case token.ADD, token.SUB:
+		case token.ADD, token.SUB, token.AND_NOT:
 			return f.isSafeUintptr(x.X) && !f.isSafeUintptr(x.Y)
 		}
 	}
diff --git a/src/cmd/vet/vet_test.go b/src/cmd/vet/vet_test.go
index 31d4b90..725f013 100644
--- a/src/cmd/vet/vet_test.go
+++ b/src/cmd/vet/vet_test.go
@@ -6,13 +6,13 @@ package main_test
 
 import (
 	"bytes"
-	"flag"
 	"fmt"
 	"internal/testenv"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"runtime"
+	"sync"
 	"testing"
 )
 
@@ -23,7 +23,6 @@ const (
 
 // We implement TestMain so remove the test binary when all is done.
 func TestMain(m *testing.M) {
-	flag.Parse()
 	result := m.Run()
 	os.Remove(binary)
 	os.Exit(result)
@@ -40,20 +39,23 @@ func MustHavePerl(t *testing.T) {
 }
 
 var (
-	built  = false // We have built the binary.
-	failed = false // We have failed to build the binary, don't try again.
+	buildMu sync.Mutex // guards following
+	built   = false    // We have built the binary.
+	failed  = false    // We have failed to build the binary, don't try again.
 )
 
 func Build(t *testing.T) {
-	testenv.MustHaveGoBuild(t)
-	MustHavePerl(t)
+	buildMu.Lock()
+	defer buildMu.Unlock()
 	if built {
 		return
 	}
 	if failed {
 		t.Skip("cannot run on this environment")
 	}
-	cmd := exec.Command("go", "build", "-o", binary)
+	testenv.MustHaveGoBuild(t)
+	MustHavePerl(t)
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", binary)
 	output, err := cmd.CombinedOutput()
 	if err != nil {
 		failed = true
@@ -83,66 +85,122 @@ func Vet(t *testing.T, files []string) {
 // 	rm testvet
 //
 
+// TestVet tests self-contained files in testdata/*.go.
+//
+// If a file contains assembly or has inter-dependencies, it should be
+// in its own test, like TestVetAsm, TestDivergentPackagesExamples,
+// etc below.
 func TestVet(t *testing.T) {
 	Build(t)
+	t.Parallel()
 
 	// errchk ./testvet
 	gos, err := filepath.Glob(filepath.Join(dataDir, "*.go"))
 	if err != nil {
 		t.Fatal(err)
 	}
-	asms, err := filepath.Glob(filepath.Join(dataDir, "*.s"))
-	if err != nil {
-		t.Fatal(err)
+	wide := runtime.GOMAXPROCS(0)
+	if wide > len(gos) {
+		wide = len(gos)
+	}
+	batch := make([][]string, wide)
+	for i, file := range gos {
+		batch[i%wide] = append(batch[i%wide], file)
+	}
+	for i, files := range batch {
+		files := files
+		t.Run(fmt.Sprint(i), func(t *testing.T) {
+			t.Parallel()
+			t.Logf("files: %q", files)
+			Vet(t, files)
+		})
 	}
-	files := append(gos, asms...)
-	Vet(t, files)
 }
 
-func TestDivergentPackagesExamples(t *testing.T) {
+func TestVetAsm(t *testing.T) {
 	Build(t)
+
+	asmDir := filepath.Join(dataDir, "asm")
+	gos, err := filepath.Glob(filepath.Join(asmDir, "*.go"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	asms, err := filepath.Glob(filepath.Join(asmDir, "*.s"))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	t.Parallel()
 	// errchk ./testvet
-	Vet(t, []string{"testdata/divergent"})
+	Vet(t, append(gos, asms...))
 }
 
-func TestIncompleteExamples(t *testing.T) {
+func TestVetDirs(t *testing.T) {
+	t.Parallel()
 	Build(t)
-	// errchk ./testvet
-	Vet(t, []string{"testdata/incomplete/examples_test.go"})
+	for _, dir := range []string{
+		"testingpkg",
+		"divergent",
+		"buildtag",
+		"incomplete", // incomplete examples
+	} {
+		dir := dir
+		t.Run(dir, func(t *testing.T) {
+			t.Parallel()
+			gos, err := filepath.Glob(filepath.Join("testdata", dir, "*.go"))
+			if err != nil {
+				t.Fatal(err)
+			}
+			Vet(t, gos)
+		})
+	}
 }
 
 func run(c *exec.Cmd, t *testing.T) bool {
 	output, err := c.CombinedOutput()
-	os.Stderr.Write(output)
 	if err != nil {
+		t.Logf("vet output:\n%s", output)
 		t.Fatal(err)
 	}
 	// Errchk delights by not returning non-zero status if it finds errors, so we look at the output.
 	// It prints "BUG" if there is a failure.
 	if !c.ProcessState.Success() {
+		t.Logf("vet output:\n%s", output)
 		return false
 	}
-	return !bytes.Contains(output, []byte("BUG"))
+	ok := !bytes.Contains(output, []byte("BUG"))
+	if !ok {
+		t.Logf("vet output:\n%s", output)
+	}
+	return ok
 }
 
 // TestTags verifies that the -tags argument controls which files to check.
 func TestTags(t *testing.T) {
+	t.Parallel()
 	Build(t)
-	args := []string{
-		"-tags=testtag",
-		"-v", // We're going to look at the files it examines.
-		"testdata/tagtest",
-	}
-	cmd := exec.Command("./"+binary, args...)
-	output, err := cmd.CombinedOutput()
-	if err != nil {
-		t.Fatal(err)
-	}
-	// file1 has testtag and file2 has !testtag.
-	if !bytes.Contains(output, []byte(filepath.Join("tagtest", "file1.go"))) {
-		t.Error("file1 was excluded, should be included")
-	}
-	if bytes.Contains(output, []byte(filepath.Join("tagtest", "file2.go"))) {
-		t.Error("file2 was included, should be excluded")
+	for _, tag := range []string{"testtag", "x testtag y", "x,testtag,y"} {
+		tag := tag
+		t.Run(tag, func(t *testing.T) {
+			t.Parallel()
+			t.Logf("-tags=%s", tag)
+			args := []string{
+				"-tags=" + tag,
+				"-v", // We're going to look at the files it examines.
+				"testdata/tagtest",
+			}
+			cmd := exec.Command("./"+binary, args...)
+			output, err := cmd.CombinedOutput()
+			if err != nil {
+				t.Fatal(err)
+			}
+			// file1 has testtag and file2 has !testtag.
+			if !bytes.Contains(output, []byte(filepath.Join("tagtest", "file1.go"))) {
+				t.Error("file1 was excluded, should be included")
+			}
+			if bytes.Contains(output, []byte(filepath.Join("tagtest", "file2.go"))) {
+				t.Error("file2 was included, should be excluded")
+			}
+		})
 	}
 }
diff --git a/src/cmd/yacc/doc.go b/src/cmd/yacc/doc.go
deleted file mode 100644
index c9bb573..0000000
--- a/src/cmd/yacc/doc.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
-
-Yacc is a version of yacc for Go.
-It is written in Go and generates parsers written in Go.
-
-Usage:
-
-	go tool yacc args...
-
-It is largely transliterated from the Inferno version written in Limbo
-which in turn was largely transliterated from the Plan 9 version
-written in C and documented at
-
-	https://9p.io/magic/man2html/1/yacc
-
-Adepts of the original yacc will have no trouble adapting to this
-form of the tool.
-
-The directory $GOROOT/src/cmd/yacc/testdata/expr is a yacc program
-for a very simple expression parser. See expr.y and main.go in that
-directory for examples of how to write and build yacc programs.
-
-The generated parser is reentrant. The parsing function yyParse expects
-to be given an argument that conforms to the following interface:
-
-	type yyLexer interface {
-		Lex(lval *yySymType) int
-		Error(e string)
-	}
-
-Lex should return the token identifier, and place other token
-information in lval (which replaces the usual yylval).
-Error is equivalent to yyerror in the original yacc.
-
-Code inside the grammar actions may refer to the variable yylex,
-which holds the yyLexer passed to yyParse.
-
-Clients that need to understand more about the parser state can
-create the parser separately from invoking it. The function yyNewParser
-returns a yyParser conforming to the following interface:
-
-	type yyParser interface {
-		Parse(yyLex) int
-		Lookahead() int
-	}
-
-Parse runs the parser; the top-level call yyParse(yylex) is equivalent
-to yyNewParser().Parse(yylex).
-
-Lookahead can be called during grammar actions to read (but not consume)
-the value of the current lookahead token, as returned by yylex.Lex.
-If there is no current lookahead token (because the parser has not called Lex
-or has consumed the token returned by the most recent call to Lex),
-Lookahead returns -1. Calling Lookahead is equivalent to reading
-yychar from within in a grammar action.
-
-Multiple grammars compiled into a single program should be placed in
-distinct packages.  If that is impossible, the "-p prefix" flag to
-yacc sets the prefix, by default yy, that begins the names of
-symbols, including types, the parser, and the lexer, generated and
-referenced by yacc's generated code.  Setting it to distinct values
-allows multiple grammars to be placed in a single package.
-
-*/
-package main
diff --git a/src/cmd/yacc/testdata/expr/README b/src/cmd/yacc/testdata/expr/README
deleted file mode 100644
index 302ef57..0000000
--- a/src/cmd/yacc/testdata/expr/README
+++ /dev/null
@@ -1,20 +0,0 @@
-This directory contains a simple program demonstrating how to use
-the Go version of yacc.
-
-To build it:
-
-	$ go generate
-	$ go build
-
-or
-
-	$ go generate
-	$ go run expr.go
-
-The file main.go contains the "go generate" command to run yacc to
-create expr.go from expr.y. It also has the package doc comment,
-as godoc will not scan the .y file.
-
-The actual implementation is in expr.y.
-
-The program is not installed in the binary distributions of Go.
diff --git a/src/cmd/yacc/testdata/expr/expr.y b/src/cmd/yacc/testdata/expr/expr.y
deleted file mode 100644
index c39f919..0000000
--- a/src/cmd/yacc/testdata/expr/expr.y
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This is an example of a goyacc program.
-// To build it:
-// go tool yacc -p "expr" expr.y (produces y.go)
-// go build -o expr y.go
-// expr
-// > <type an expression>
-
-%{
-
-package main
-
-import (
-	"bufio"
-	"bytes"
-	"fmt"
-	"io"
-	"log"
-	"math/big"
-	"os"
-	"unicode/utf8"
-)
-
-%}
-
-%union {
-	num *big.Rat
-}
-
-%type	<num>	expr expr1 expr2 expr3
-
-%token '+' '-' '*' '/' '(' ')'
-
-%token	<num>	NUM
-
-%%
-
-top:
-	expr
-	{
-		if $1.IsInt() {
-			fmt.Println($1.Num().String())
-		} else {
-			fmt.Println($1.String())
-		}
-	}
-
-expr:
-	expr1
-|	'+' expr
-	{
-		$$ = $2
-	}
-|	'-' expr
-	{
-		$$ = $2.Neg($2)
-	}
-
-expr1:
-	expr2
-|	expr1 '+' expr2
-	{
-		$$ = $1.Add($1, $3)
-	}
-|	expr1 '-' expr2
-	{
-		$$ = $1.Sub($1, $3)
-	}
-
-expr2:
-	expr3
-|	expr2 '*' expr3
-	{
-		$$ = $1.Mul($1, $3)
-	}
-|	expr2 '/' expr3
-	{
-		$$ = $1.Quo($1, $3)
-	}
-
-expr3:
-	NUM
-|	'(' expr ')'
-	{
-		$$ = $2
-	}
-
-
-%%
-
-// The parser expects the lexer to return 0 on EOF.  Give it a name
-// for clarity.
-const eof = 0
-
-// The parser uses the type <prefix>Lex as a lexer. It must provide
-// the methods Lex(*<prefix>SymType) int and Error(string).
-type exprLex struct {
-	line []byte
-	peek rune
-}
-
-// The parser calls this method to get each new token. This
-// implementation returns operators and NUM.
-func (x *exprLex) Lex(yylval *exprSymType) int {
-	for {
-		c := x.next()
-		switch c {
-		case eof:
-			return eof
-		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
-			return x.num(c, yylval)
-		case '+', '-', '*', '/', '(', ')':
-			return int(c)
-
-		// Recognize Unicode multiplication and division
-		// symbols, returning what the parser expects.
-		case '×':
-			return '*'
-		case '÷':
-			return '/'
-
-		case ' ', '\t', '\n', '\r':
-		default:
-			log.Printf("unrecognized character %q", c)
-		}
-	}
-}
-
-// Lex a number.
-func (x *exprLex) num(c rune, yylval *exprSymType) int {
-	add := func(b *bytes.Buffer, c rune) {
-		if _, err := b.WriteRune(c); err != nil {
-			log.Fatalf("WriteRune: %s", err)
-		}
-	}
-	var b bytes.Buffer
-	add(&b, c)
-	L: for {
-		c = x.next()
-		switch c {
-		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E':
-			add(&b, c)
-		default:
-			break L
-		}
-	}
-	if c != eof {
-		x.peek = c
-	}
-	yylval.num = &big.Rat{}
-	_, ok := yylval.num.SetString(b.String())
-	if !ok {
-		log.Printf("bad number %q", b.String())
-		return eof
-	}
-	return NUM
-}
-
-// Return the next rune for the lexer.
-func (x *exprLex) next() rune {
-	if x.peek != eof {
-		r := x.peek
-		x.peek = eof
-		return r
-	}
-	if len(x.line) == 0 {
-		return eof
-	}
-	c, size := utf8.DecodeRune(x.line)
-	x.line = x.line[size:]
-	if c == utf8.RuneError && size == 1 {
-		log.Print("invalid utf8")
-		return x.next()
-	}
-	return c
-}
-
-// The parser calls this method on a parse error.
-func (x *exprLex) Error(s string) {
-	log.Printf("parse error: %s", s)
-}
-
-func main() {
-	in := bufio.NewReader(os.Stdin)
-	for {
-		if _, err := os.Stdout.WriteString("> "); err != nil {
-			log.Fatalf("WriteString: %s", err)
-		}
-		line, err := in.ReadBytes('\n')
-		if err == io.EOF {
-			return
-		}
-		if err != nil {
-			log.Fatalf("ReadBytes: %s", err)
-		}
-
-		exprParse(&exprLex{line: line})
-	}
-}
diff --git a/src/cmd/yacc/testdata/expr/main.go b/src/cmd/yacc/testdata/expr/main.go
deleted file mode 100644
index 37f0023..0000000
--- a/src/cmd/yacc/testdata/expr/main.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file holds the go generate command to run yacc on the grammar in expr.y.
-// To build expr:
-//	% go generate
-//	% go build
-
-//go:generate -command yacc go tool yacc
-//go:generate yacc -o expr.go -p "expr" expr.y
-
-// Expr is a simple expression evaluator that serves as a working example of
-// how to use Go's yacc implementation.
-package main
diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go
deleted file mode 100644
index 8a5df05..0000000
--- a/src/cmd/yacc/yacc.go
+++ /dev/null
@@ -1,3641 +0,0 @@
-/*
-Derived from Inferno's utils/iyacc/yacc.c
-http://code.google.com/p/inferno-os/source/browse/utils/iyacc/yacc.c
-
-This copyright NOTICE applies to all files in this directory and
-subdirectories, unless another copyright notice appears in a given
-file or subdirectory.  If you take substantial code from this software to use in
-other programs, you must somehow include with it an appropriate
-copyright notice that includes the copyright notice and the other
-notices below.  It is fine (and often tidier) to do that in a separate
-file such as NOTICE, LICENCE or COPYING.
-
-	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
-	Portions Copyright © 1995-1997 C H Forsyth (forsyth at terzarima.net)
-	Portions Copyright © 1997-1999 Vita Nuova Limited
-	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
-	Portions Copyright © 2004,2006 Bruce Ellis
-	Portions Copyright © 2005-2007 C H Forsyth (forsyth at terzarima.net)
-	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
-	Portions Copyright © 2009 The Go Authors. All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
-package main
-
-// yacc
-// major difference is lack of stem ("y" variable)
-//
-
-import (
-	"bufio"
-	"bytes"
-	"flag"
-	"fmt"
-	"go/format"
-	"io/ioutil"
-	"os"
-	"strconv"
-	"strings"
-	"unicode"
-)
-
-// the following are adjustable
-// according to memory size
-const (
-	ACTSIZE  = 30000
-	NSTATES  = 2000
-	TEMPSIZE = 2000
-
-	SYMINC   = 50  // increase for non-term or term
-	RULEINC  = 50  // increase for max rule length prodptr[i]
-	PRODINC  = 100 // increase for productions     prodptr
-	WSETINC  = 50  // increase for working sets    wsets
-	STATEINC = 200 // increase for states          statemem
-
-	NAMESIZE = 50
-	NTYPES   = 63
-	ISIZE    = 400
-
-	PRIVATE = 0xE000 // unicode private use
-
-	// relationships which must hold:
-	//	TEMPSIZE >= NTERMS + NNONTERM + 1;
-	//	TEMPSIZE >= NSTATES;
-	//
-
-	NTBASE     = 010000
-	ERRCODE    = 8190
-	ACCEPTCODE = 8191
-	YYLEXUNK   = 3
-	TOKSTART   = 4 //index of first defined token
-)
-
-// no, left, right, binary assoc.
-const (
-	NOASC = iota
-	LASC
-	RASC
-	BASC
-)
-
-// flags for state generation
-const (
-	DONE = iota
-	MUSTDO
-	MUSTLOOKAHEAD
-)
-
-// flags for a rule having an action, and being reduced
-const (
-	ACTFLAG = 1 << (iota + 2)
-	REDFLAG
-)
-
-// output parser flags
-const yyFlag = -1000
-
-// parse tokens
-const (
-	IDENTIFIER = PRIVATE + iota
-	MARK
-	TERM
-	LEFT
-	RIGHT
-	BINARY
-	PREC
-	LCURLY
-	IDENTCOLON
-	NUMBER
-	START
-	TYPEDEF
-	TYPENAME
-	UNION
-	ERROR
-)
-
-const ENDFILE = 0
-const EMPTY = 1
-const WHOKNOWS = 0
-const OK = 1
-const NOMORE = -1000
-
-// macros for getting associativity and precedence levels
-func ASSOC(i int) int { return i & 3 }
-
-func PLEVEL(i int) int { return (i >> 4) & 077 }
-
-func TYPE(i int) int { return (i >> 10) & 077 }
-
-// macros for setting associativity and precedence levels
-func SETASC(i, j int) int { return i | j }
-
-func SETPLEV(i, j int) int { return i | (j << 4) }
-
-func SETTYPE(i, j int) int { return i | (j << 10) }
-
-// I/O descriptors
-var finput *bufio.Reader // input file
-var stderr *bufio.Writer
-var ftable *bufio.Writer    // y.go file
-var fcode = &bytes.Buffer{} // saved code
-var foutput *bufio.Writer   // y.output file
-
-var fmtImported bool // output file has recorded an import of "fmt"
-
-var oflag string  // -o [y.go]		- y.go file
-var vflag string  // -v [y.output]	- y.output file
-var lflag bool    // -l			- disable line directives
-var prefix string // name prefix for identifiers, default yy
-
-func init() {
-	flag.StringVar(&oflag, "o", "y.go", "parser output")
-	flag.StringVar(&prefix, "p", "yy", "name prefix to use in generated code")
-	flag.StringVar(&vflag, "v", "y.output", "create parsing tables")
-	flag.BoolVar(&lflag, "l", false, "disable line directives")
-}
-
-var initialstacksize = 16
-
-// communication variables between various I/O routines
-var infile string  // input file name
-var numbval int    // value of an input number
-var tokname string // input token name, slop for runes and 0
-var tokflag = false
-
-// structure declarations
-type Lkset []int
-
-type Pitem struct {
-	prod   []int
-	off    int // offset within the production
-	first  int // first term or non-term in item
-	prodno int // production number for sorting
-}
-
-type Item struct {
-	pitem Pitem
-	look  Lkset
-}
-
-type Symb struct {
-	name    string
-	noconst bool
-	value   int
-}
-
-type Wset struct {
-	pitem Pitem
-	flag  int
-	ws    Lkset
-}
-
-// storage of types
-var ntypes int             // number of types defined
-var typeset [NTYPES]string // pointers to type tags
-
-// token information
-
-var ntokens = 0 // number of tokens
-var tokset []Symb
-var toklev []int // vector with the precedence of the terminals
-
-// nonterminal information
-
-var nnonter = -1 // the number of nonterminals
-var nontrst []Symb
-var start int // start symbol
-
-// state information
-
-var nstate = 0                      // number of states
-var pstate = make([]int, NSTATES+2) // index into statemem to the descriptions of the states
-var statemem []Item
-var tystate = make([]int, NSTATES) // contains type information about the states
-var tstates []int                  // states generated by terminal gotos
-var ntstates []int                 // states generated by nonterminal gotos
-var mstates = make([]int, NSTATES) // chain of overflows of term/nonterm generation lists
-var lastred int                    // number of last reduction of a state
-var defact = make([]int, NSTATES)  // default actions of states
-
-// lookahead set information
-
-var nolook = 0  // flag to turn off lookahead computations
-var tbitset = 0 // size of lookahead sets
-var clset Lkset // temporary storage for lookahead computations
-
-// working set information
-
-var wsets []Wset
-var cwp int
-
-// storage for action table
-
-var amem []int                   // action table storage
-var memp int                     // next free action table position
-var indgo = make([]int, NSTATES) // index to the stored goto table
-
-// temporary vector, indexable by states, terms, or ntokens
-
-var temp1 = make([]int, TEMPSIZE) // temporary storage, indexed by terms + ntokens or states
-var lineno = 1                    // current input line number
-var fatfl = 1                     // if on, error is fatal
-var nerrors = 0                   // number of errors
-
-// assigned token type values
-
-var extval = 0
-
-// grammar rule information
-
-var nprod = 1      // number of productions
-var prdptr [][]int // pointers to descriptions of productions
-var levprd []int   // precedence levels for the productions
-var rlines []int   // line number for this rule
-
-// statistics collection variables
-
-var zzgoent = 0
-var zzgobest = 0
-var zzacent = 0
-var zzexcp = 0
-var zzclose = 0
-var zzrrconf = 0
-var zzsrconf = 0
-var zzstate = 0
-
-// optimizer arrays
-
-var yypgo [][]int
-var optst [][]int
-var ggreed []int
-var pgo []int
-
-var maxspr int // maximum spread of any entry
-var maxoff int // maximum offset into a array
-var maxa int
-
-// storage for information about the nonterminals
-
-var pres [][][]int // vector of pointers to productions yielding each nonterminal
-var pfirst []Lkset
-var pempty []int // vector of nonterminals nontrivially deriving e
-
-// random stuff picked out from between functions
-
-var indebug = 0 // debugging flag for cpfir
-var pidebug = 0 // debugging flag for putitem
-var gsdebug = 0 // debugging flag for stagen
-var cldebug = 0 // debugging flag for closure
-var pkdebug = 0 // debugging flag for apack
-var g2debug = 0 // debugging for go2gen
-var adb = 0     // debugging for callopt
-
-type Resrv struct {
-	name  string
-	value int
-}
-
-var resrv = []Resrv{
-	{"binary", BINARY},
-	{"left", LEFT},
-	{"nonassoc", BINARY},
-	{"prec", PREC},
-	{"right", RIGHT},
-	{"start", START},
-	{"term", TERM},
-	{"token", TERM},
-	{"type", TYPEDEF},
-	{"union", UNION},
-	{"struct", UNION},
-	{"error", ERROR},
-}
-
-type Error struct {
-	lineno int
-	tokens []string
-	msg    string
-}
-
-var errors []Error
-
-type Row struct {
-	actions       []int
-	defaultAction int
-}
-
-var stateTable []Row
-
-var zznewstate = 0
-
-const EOF = -1
-
-func main() {
-
-	setup() // initialize and read productions
-
-	tbitset = (ntokens + 32) / 32
-	cpres()  // make table of which productions yield a given nonterminal
-	cempty() // make a table of which nonterminals can match the empty string
-	cpfir()  // make a table of firsts of nonterminals
-
-	stagen() // generate the states
-
-	yypgo = make([][]int, nnonter+1)
-	optst = make([][]int, nstate)
-	output() // write the states and the tables
-	go2out()
-
-	hideprod()
-	summary()
-
-	callopt()
-
-	others()
-
-	exit(0)
-}
-
-func setup() {
-	var j, ty int
-
-	stderr = bufio.NewWriter(os.Stderr)
-	foutput = nil
-
-	flag.Parse()
-	if flag.NArg() != 1 {
-		usage()
-	}
-	if initialstacksize < 1 {
-		// never set so cannot happen
-		fmt.Fprintf(stderr, "yacc: stack size too small\n")
-		usage()
-	}
-	yaccpar = strings.Replace(yaccpartext, "$$", prefix, -1)
-	openup()
-
-	defin(0, "$end")
-	extval = PRIVATE // tokens start in unicode 'private use'
-	defin(0, "error")
-	defin(1, "$accept")
-	defin(0, "$unk")
-	i := 0
-
-	t := gettok()
-
-outer:
-	for {
-		switch t {
-		default:
-			errorf("syntax error tok=%v", t-PRIVATE)
-
-		case MARK, ENDFILE:
-			break outer
-
-		case ';':
-
-		case START:
-			t = gettok()
-			if t != IDENTIFIER {
-				errorf("bad %%start construction")
-			}
-			start = chfind(1, tokname)
-
-		case ERROR:
-			lno := lineno
-			var tokens []string
-			for {
-				t := gettok()
-				if t == ':' {
-					break
-				}
-				if t != IDENTIFIER && t != IDENTCOLON {
-					errorf("bad syntax in %%error")
-				}
-				tokens = append(tokens, tokname)
-				if t == IDENTCOLON {
-					break
-				}
-			}
-			if gettok() != IDENTIFIER {
-				errorf("bad syntax in %%error")
-			}
-			errors = append(errors, Error{lno, tokens, tokname})
-
-		case TYPEDEF:
-			t = gettok()
-			if t != TYPENAME {
-				errorf("bad syntax in %%type")
-			}
-			ty = numbval
-			for {
-				t = gettok()
-				switch t {
-				case IDENTIFIER:
-					t = chfind(1, tokname)
-					if t < NTBASE {
-						j = TYPE(toklev[t])
-						if j != 0 && j != ty {
-							errorf("type redeclaration of token %s",
-								tokset[t].name)
-						} else {
-							toklev[t] = SETTYPE(toklev[t], ty)
-						}
-					} else {
-						j = nontrst[t-NTBASE].value
-						if j != 0 && j != ty {
-							errorf("type redeclaration of nonterminal %v",
-								nontrst[t-NTBASE].name)
-						} else {
-							nontrst[t-NTBASE].value = ty
-						}
-					}
-					continue
-
-				case ',':
-					continue
-				}
-				break
-			}
-			continue
-
-		case UNION:
-			cpyunion()
-
-		case LEFT, BINARY, RIGHT, TERM:
-			// nonzero means new prec. and assoc.
-			lev := t - TERM
-			if lev != 0 {
-				i++
-			}
-			ty = 0
-
-			// get identifiers so defined
-			t = gettok()
-
-			// there is a type defined
-			if t == TYPENAME {
-				ty = numbval
-				t = gettok()
-			}
-			for {
-				switch t {
-				case ',':
-					t = gettok()
-					continue
-
-				case ';':
-					break
-
-				case IDENTIFIER:
-					j = chfind(0, tokname)
-					if j >= NTBASE {
-						errorf("%v defined earlier as nonterminal", tokname)
-					}
-					if lev != 0 {
-						if ASSOC(toklev[j]) != 0 {
-							errorf("redeclaration of precedence of %v", tokname)
-						}
-						toklev[j] = SETASC(toklev[j], lev)
-						toklev[j] = SETPLEV(toklev[j], i)
-					}
-					if ty != 0 {
-						if TYPE(toklev[j]) != 0 {
-							errorf("redeclaration of type of %v", tokname)
-						}
-						toklev[j] = SETTYPE(toklev[j], ty)
-					}
-					t = gettok()
-					if t == NUMBER {
-						tokset[j].value = numbval
-						t = gettok()
-					}
-
-					continue
-				}
-				break
-			}
-			continue
-
-		case LCURLY:
-			cpycode()
-		}
-		t = gettok()
-	}
-
-	if t == ENDFILE {
-		errorf("unexpected EOF before %%")
-	}
-
-	fmt.Fprintf(fcode, "switch %snt {\n", prefix)
-
-	moreprod()
-	prdptr[0] = []int{NTBASE, start, 1, 0}
-
-	nprod = 1
-	curprod := make([]int, RULEINC)
-	t = gettok()
-	if t != IDENTCOLON {
-		errorf("bad syntax on first rule")
-	}
-
-	if start == 0 {
-		prdptr[0][1] = chfind(1, tokname)
-	}
-
-	// read rules
-	// put into prdptr array in the format
-	// target
-	// followed by id's of terminals and non-terminals
-	// followed by -nprod
-
-	for t != MARK && t != ENDFILE {
-		mem := 0
-
-		// process a rule
-		rlines[nprod] = lineno
-		ruleline := lineno
-		if t == '|' {
-			curprod[mem] = prdptr[nprod-1][0]
-			mem++
-		} else if t == IDENTCOLON {
-			curprod[mem] = chfind(1, tokname)
-			if curprod[mem] < NTBASE {
-				lerrorf(ruleline, "token illegal on LHS of grammar rule")
-			}
-			mem++
-		} else {
-			lerrorf(ruleline, "illegal rule: missing semicolon or | ?")
-		}
-
-		// read rule body
-		t = gettok()
-		for {
-			for t == IDENTIFIER {
-				curprod[mem] = chfind(1, tokname)
-				if curprod[mem] < NTBASE {
-					levprd[nprod] = toklev[curprod[mem]]
-				}
-				mem++
-				if mem >= len(curprod) {
-					ncurprod := make([]int, mem+RULEINC)
-					copy(ncurprod, curprod)
-					curprod = ncurprod
-				}
-				t = gettok()
-			}
-			if t == PREC {
-				if gettok() != IDENTIFIER {
-					lerrorf(ruleline, "illegal %%prec syntax")
-				}
-				j = chfind(2, tokname)
-				if j >= NTBASE {
-					lerrorf(ruleline, "nonterminal "+nontrst[j-NTBASE].name+" illegal after %%prec")
-				}
-				levprd[nprod] = toklev[j]
-				t = gettok()
-			}
-			if t != '=' {
-				break
-			}
-			levprd[nprod] |= ACTFLAG
-			fmt.Fprintf(fcode, "\n\tcase %v:", nprod)
-			fmt.Fprintf(fcode, "\n\t\t%sDollar = %sS[%spt-%v:%spt+1]", prefix, prefix, prefix, mem-1, prefix)
-			cpyact(curprod, mem)
-
-			// action within rule...
-			t = gettok()
-			if t == IDENTIFIER {
-				// make it a nonterminal
-				j = chfind(1, fmt.Sprintf("$$%v", nprod))
-
-				//
-				// the current rule will become rule number nprod+1
-				// enter null production for action
-				//
-				prdptr[nprod] = make([]int, 2)
-				prdptr[nprod][0] = j
-				prdptr[nprod][1] = -nprod
-
-				// update the production information
-				nprod++
-				moreprod()
-				levprd[nprod] = levprd[nprod-1] & ^ACTFLAG
-				levprd[nprod-1] = ACTFLAG
-				rlines[nprod] = lineno
-
-				// make the action appear in the original rule
-				curprod[mem] = j
-				mem++
-				if mem >= len(curprod) {
-					ncurprod := make([]int, mem+RULEINC)
-					copy(ncurprod, curprod)
-					curprod = ncurprod
-				}
-			}
-		}
-
-		for t == ';' {
-			t = gettok()
-		}
-		curprod[mem] = -nprod
-		mem++
-
-		// check that default action is reasonable
-		if ntypes != 0 && (levprd[nprod]&ACTFLAG) == 0 &&
-			nontrst[curprod[0]-NTBASE].value != 0 {
-			// no explicit action, LHS has value
-			tempty := curprod[1]
-			if tempty < 0 {
-				lerrorf(ruleline, "must return a value, since LHS has a type")
-			}
-			if tempty >= NTBASE {
-				tempty = nontrst[tempty-NTBASE].value
-			} else {
-				tempty = TYPE(toklev[tempty])
-			}
-			if tempty != nontrst[curprod[0]-NTBASE].value {
-				lerrorf(ruleline, "default action causes potential type clash")
-			}
-		}
-		moreprod()
-		prdptr[nprod] = make([]int, mem)
-		copy(prdptr[nprod], curprod)
-		nprod++
-		moreprod()
-		levprd[nprod] = 0
-	}
-
-	if TEMPSIZE < ntokens+nnonter+1 {
-		errorf("too many tokens (%d) or non-terminals (%d)", ntokens, nnonter)
-	}
-
-	//
-	// end of all rules
-	// dump out the prefix code
-	//
-
-	fmt.Fprintf(fcode, "\n\t}")
-
-	// put out non-literal terminals
-	for i := TOKSTART; i <= ntokens; i++ {
-		// non-literals
-		if !tokset[i].noconst {
-			fmt.Fprintf(ftable, "const %v = %v\n", tokset[i].name, tokset[i].value)
-		}
-	}
-
-	// put out names of tokens
-	ftable.WriteRune('\n')
-	fmt.Fprintf(ftable, "var %sToknames = [...]string{\n", prefix)
-	for i := 1; i <= ntokens; i++ {
-		fmt.Fprintf(ftable, "\t%q,\n", tokset[i].name)
-	}
-	fmt.Fprintf(ftable, "}\n")
-
-	// put out names of states.
-	// commented out to avoid a huge table just for debugging.
-	// re-enable to have the names in the binary.
-	fmt.Fprintf(ftable, "var %sStatenames = [...]string{", prefix)
-	//	for i:=TOKSTART; i<=ntokens; i++ {
-	//		fmt.Fprintf(ftable, "\t%q,\n", tokset[i].name);
-	//	}
-	fmt.Fprintf(ftable, "}\n")
-
-	ftable.WriteRune('\n')
-	fmt.Fprintf(ftable, "const %sEofCode = 1\n", prefix)
-	fmt.Fprintf(ftable, "const %sErrCode = 2\n", prefix)
-	fmt.Fprintf(ftable, "const %sInitialStackSize = %v\n", prefix, initialstacksize)
-
-	//
-	// copy any postfix code
-	//
-	if t == MARK {
-		if !lflag {
-			fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
-		}
-		for {
-			c := getrune(finput)
-			if c == EOF {
-				break
-			}
-			ftable.WriteRune(c)
-		}
-	}
-}
-
-//
-// allocate enough room to hold another production
-//
-func moreprod() {
-	n := len(prdptr)
-	if nprod >= n {
-		nn := n + PRODINC
-		aprod := make([][]int, nn)
-		alevprd := make([]int, nn)
-		arlines := make([]int, nn)
-
-		copy(aprod, prdptr)
-		copy(alevprd, levprd)
-		copy(arlines, rlines)
-
-		prdptr = aprod
-		levprd = alevprd
-		rlines = arlines
-	}
-}
-
-//
-// define s to be a terminal if nt==0
-// or a nonterminal if nt==1
-//
-func defin(nt int, s string) int {
-	val := 0
-	if nt != 0 {
-		nnonter++
-		if nnonter >= len(nontrst) {
-			anontrst := make([]Symb, nnonter+SYMINC)
-			copy(anontrst, nontrst)
-			nontrst = anontrst
-		}
-		nontrst[nnonter] = Symb{name: s}
-		return NTBASE + nnonter
-	}
-
-	// must be a token
-	ntokens++
-	if ntokens >= len(tokset) {
-		nn := ntokens + SYMINC
-		atokset := make([]Symb, nn)
-		atoklev := make([]int, nn)
-
-		copy(atoklev, toklev)
-		copy(atokset, tokset)
-
-		tokset = atokset
-		toklev = atoklev
-	}
-	tokset[ntokens].name = s
-	toklev[ntokens] = 0
-
-	// establish value for token
-	// single character literal
-	if s[0] == '\'' || s[0] == '"' {
-		q, err := strconv.Unquote(s)
-		if err != nil {
-			errorf("invalid token: %s", err)
-		}
-		rq := []rune(q)
-		if len(rq) != 1 {
-			errorf("character token too long: %s", s)
-		}
-		val = int(rq[0])
-		if val == 0 {
-			errorf("token value 0 is illegal")
-		}
-		tokset[ntokens].noconst = true
-	} else {
-		val = extval
-		extval++
-		if s[0] == '$' {
-			tokset[ntokens].noconst = true
-		}
-	}
-
-	tokset[ntokens].value = val
-	return ntokens
-}
-
-var peekline = 0
-
-func gettok() int {
-	var i int
-	var match, c rune
-
-	tokname = ""
-	for {
-		lineno += peekline
-		peekline = 0
-		c = getrune(finput)
-		for c == ' ' || c == '\n' || c == '\t' || c == '\v' || c == '\r' {
-			if c == '\n' {
-				lineno++
-			}
-			c = getrune(finput)
-		}
-
-		// skip comment -- fix
-		if c != '/' {
-			break
-		}
-		lineno += skipcom()
-	}
-
-	switch c {
-	case EOF:
-		if tokflag {
-			fmt.Printf(">>> ENDFILE %v\n", lineno)
-		}
-		return ENDFILE
-
-	case '{':
-		ungetrune(finput, c)
-		if tokflag {
-			fmt.Printf(">>> ={ %v\n", lineno)
-		}
-		return '='
-
-	case '<':
-		// get, and look up, a type name (union member name)
-		c = getrune(finput)
-		for c != '>' && c != EOF && c != '\n' {
-			tokname += string(c)
-			c = getrune(finput)
-		}
-
-		if c != '>' {
-			errorf("unterminated < ... > clause")
-		}
-
-		for i = 1; i <= ntypes; i++ {
-			if typeset[i] == tokname {
-				numbval = i
-				if tokflag {
-					fmt.Printf(">>> TYPENAME old <%v> %v\n", tokname, lineno)
-				}
-				return TYPENAME
-			}
-		}
-		ntypes++
-		numbval = ntypes
-		typeset[numbval] = tokname
-		if tokflag {
-			fmt.Printf(">>> TYPENAME new <%v> %v\n", tokname, lineno)
-		}
-		return TYPENAME
-
-	case '"', '\'':
-		match = c
-		tokname = string(c)
-		for {
-			c = getrune(finput)
-			if c == '\n' || c == EOF {
-				errorf("illegal or missing ' or \"")
-			}
-			if c == '\\' {
-				tokname += string('\\')
-				c = getrune(finput)
-			} else if c == match {
-				if tokflag {
-					fmt.Printf(">>> IDENTIFIER \"%v\" %v\n", tokname, lineno)
-				}
-				tokname += string(c)
-				return IDENTIFIER
-			}
-			tokname += string(c)
-		}
-
-	case '%':
-		c = getrune(finput)
-		switch c {
-		case '%':
-			if tokflag {
-				fmt.Printf(">>> MARK %%%% %v\n", lineno)
-			}
-			return MARK
-		case '=':
-			if tokflag {
-				fmt.Printf(">>> PREC %%= %v\n", lineno)
-			}
-			return PREC
-		case '{':
-			if tokflag {
-				fmt.Printf(">>> LCURLY %%{ %v\n", lineno)
-			}
-			return LCURLY
-		}
-
-		getword(c)
-		// find a reserved word
-		for i := range resrv {
-			if tokname == resrv[i].name {
-				if tokflag {
-					fmt.Printf(">>> %%%v %v %v\n", tokname,
-						resrv[i].value-PRIVATE, lineno)
-				}
-				return resrv[i].value
-			}
-		}
-		errorf("invalid escape, or illegal reserved word: %v", tokname)
-
-	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
-		numbval = int(c - '0')
-		for {
-			c = getrune(finput)
-			if !isdigit(c) {
-				break
-			}
-			numbval = numbval*10 + int(c-'0')
-		}
-		ungetrune(finput, c)
-		if tokflag {
-			fmt.Printf(">>> NUMBER %v %v\n", numbval, lineno)
-		}
-		return NUMBER
-
-	default:
-		if isword(c) || c == '.' || c == '$' {
-			getword(c)
-			break
-		}
-		if tokflag {
-			fmt.Printf(">>> OPERATOR %v %v\n", string(c), lineno)
-		}
-		return int(c)
-	}
-
-	// look ahead to distinguish IDENTIFIER from IDENTCOLON
-	c = getrune(finput)
-	for c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\r' || c == '/' {
-		if c == '\n' {
-			peekline++
-		}
-		// look for comments
-		if c == '/' {
-			peekline += skipcom()
-		}
-		c = getrune(finput)
-	}
-	if c == ':' {
-		if tokflag {
-			fmt.Printf(">>> IDENTCOLON %v: %v\n", tokname, lineno)
-		}
-		return IDENTCOLON
-	}
-
-	ungetrune(finput, c)
-	if tokflag {
-		fmt.Printf(">>> IDENTIFIER %v %v\n", tokname, lineno)
-	}
-	return IDENTIFIER
-}
-
-func getword(c rune) {
-	tokname = ""
-	for isword(c) || isdigit(c) || c == '.' || c == '$' {
-		tokname += string(c)
-		c = getrune(finput)
-	}
-	ungetrune(finput, c)
-}
-
-//
-// determine the type of a symbol
-//
-func fdtype(t int) int {
-	var v int
-	var s string
-
-	if t >= NTBASE {
-		v = nontrst[t-NTBASE].value
-		s = nontrst[t-NTBASE].name
-	} else {
-		v = TYPE(toklev[t])
-		s = tokset[t].name
-	}
-	if v <= 0 {
-		errorf("must specify type for %v", s)
-	}
-	return v
-}
-
-func chfind(t int, s string) int {
-	if s[0] == '"' || s[0] == '\'' {
-		t = 0
-	}
-	for i := 0; i <= ntokens; i++ {
-		if s == tokset[i].name {
-			return i
-		}
-	}
-	for i := 0; i <= nnonter; i++ {
-		if s == nontrst[i].name {
-			return NTBASE + i
-		}
-	}
-
-	// cannot find name
-	if t > 1 {
-		errorf("%v should have been defined earlier", s)
-	}
-	return defin(t, s)
-}
-
-//
-// copy the union declaration to the output, and the define file if present
-//
-func cpyunion() {
-
-	if !lflag {
-		fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
-	}
-	fmt.Fprintf(ftable, "type %sSymType struct", prefix)
-
-	level := 0
-
-out:
-	for {
-		c := getrune(finput)
-		if c == EOF {
-			errorf("EOF encountered while processing %%union")
-		}
-		ftable.WriteRune(c)
-		switch c {
-		case '\n':
-			lineno++
-		case '{':
-			if level == 0 {
-				fmt.Fprintf(ftable, "\n\tyys int")
-			}
-			level++
-		case '}':
-			level--
-			if level == 0 {
-				break out
-			}
-		}
-	}
-	fmt.Fprintf(ftable, "\n\n")
-}
-
-//
-// saves code between %{ and %}
-// adds an import for __fmt__ the first time
-//
-func cpycode() {
-	lno := lineno
-
-	c := getrune(finput)
-	if c == '\n' {
-		c = getrune(finput)
-		lineno++
-	}
-	if !lflag {
-		fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
-	}
-	// accumulate until %}
-	code := make([]rune, 0, 1024)
-	for c != EOF {
-		if c == '%' {
-			c = getrune(finput)
-			if c == '}' {
-				emitcode(code, lno+1)
-				return
-			}
-			code = append(code, '%')
-		}
-		code = append(code, c)
-		if c == '\n' {
-			lineno++
-		}
-		c = getrune(finput)
-	}
-	lineno = lno
-	errorf("eof before %%}")
-}
-
-//
-// emits code saved up from between %{ and %}
-// called by cpycode
-// adds an import for __yyfmt__ after the package clause
-//
-func emitcode(code []rune, lineno int) {
-	for i, line := range lines(code) {
-		writecode(line)
-		if !fmtImported && isPackageClause(line) {
-			fmt.Fprintln(ftable, `import __yyfmt__ "fmt"`)
-			if !lflag {
-				fmt.Fprintf(ftable, "//line %v:%v\n\t\t", infile, lineno+i)
-			}
-			fmtImported = true
-		}
-	}
-}
-
-//
-// does this line look like a package clause?  not perfect: might be confused by early comments.
-//
-func isPackageClause(line []rune) bool {
-	line = skipspace(line)
-
-	// must be big enough.
-	if len(line) < len("package X\n") {
-		return false
-	}
-
-	// must start with "package"
-	for i, r := range []rune("package") {
-		if line[i] != r {
-			return false
-		}
-	}
-	line = skipspace(line[len("package"):])
-
-	// must have another identifier.
-	if len(line) == 0 || (!unicode.IsLetter(line[0]) && line[0] != '_') {
-		return false
-	}
-	for len(line) > 0 {
-		if !unicode.IsLetter(line[0]) && !unicode.IsDigit(line[0]) && line[0] != '_' {
-			break
-		}
-		line = line[1:]
-	}
-	line = skipspace(line)
-
-	// eol, newline, or comment must follow
-	if len(line) == 0 {
-		return true
-	}
-	if line[0] == '\r' || line[0] == '\n' {
-		return true
-	}
-	if len(line) >= 2 {
-		return line[0] == '/' && (line[1] == '/' || line[1] == '*')
-	}
-	return false
-}
-
-//
-// skip initial spaces
-//
-func skipspace(line []rune) []rune {
-	for len(line) > 0 {
-		if line[0] != ' ' && line[0] != '\t' {
-			break
-		}
-		line = line[1:]
-	}
-	return line
-}
-
-//
-// break code into lines
-//
-func lines(code []rune) [][]rune {
-	l := make([][]rune, 0, 100)
-	for len(code) > 0 {
-		// one line per loop
-		var i int
-		for i = range code {
-			if code[i] == '\n' {
-				break
-			}
-		}
-		l = append(l, code[:i+1])
-		code = code[i+1:]
-	}
-	return l
-}
-
-//
-// writes code to ftable
-//
-func writecode(code []rune) {
-	for _, r := range code {
-		ftable.WriteRune(r)
-	}
-}
-
-//
-// skip over comments
-// skipcom is called after reading a '/'
-//
-func skipcom() int {
-	var c rune
-
-	c = getrune(finput)
-	if c == '/' {
-		for c != EOF {
-			if c == '\n' {
-				return 1
-			}
-			c = getrune(finput)
-		}
-		errorf("EOF inside comment")
-		return 0
-	}
-	if c != '*' {
-		errorf("illegal comment")
-	}
-
-	nl := 0 // lines skipped
-	c = getrune(finput)
-
-l1:
-	switch c {
-	case '*':
-		c = getrune(finput)
-		if c == '/' {
-			break
-		}
-		goto l1
-
-	case '\n':
-		nl++
-		fallthrough
-
-	default:
-		c = getrune(finput)
-		goto l1
-	}
-	return nl
-}
-
-func dumpprod(curprod []int, max int) {
-	fmt.Printf("\n")
-	for i := 0; i < max; i++ {
-		p := curprod[i]
-		if p < 0 {
-			fmt.Printf("[%v] %v\n", i, p)
-		} else {
-			fmt.Printf("[%v] %v\n", i, symnam(p))
-		}
-	}
-}
-
-//
-// copy action to the next ; or closing }
-//
-func cpyact(curprod []int, max int) {
-
-	if !lflag {
-		fmt.Fprintf(fcode, "\n\t\t//line %v:%v", infile, lineno)
-	}
-	fmt.Fprint(fcode, "\n\t\t")
-
-	lno := lineno
-	brac := 0
-
-loop:
-	for {
-		c := getrune(finput)
-
-	swt:
-		switch c {
-		case ';':
-			if brac == 0 {
-				fcode.WriteRune(c)
-				return
-			}
-
-		case '{':
-			if brac == 0 {
-			}
-			brac++
-
-		case '$':
-			s := 1
-			tok := -1
-			c = getrune(finput)
-
-			// type description
-			if c == '<' {
-				ungetrune(finput, c)
-				if gettok() != TYPENAME {
-					errorf("bad syntax on $<ident> clause")
-				}
-				tok = numbval
-				c = getrune(finput)
-			}
-			if c == '$' {
-				fmt.Fprintf(fcode, "%sVAL", prefix)
-
-				// put out the proper tag...
-				if ntypes != 0 {
-					if tok < 0 {
-						tok = fdtype(curprod[0])
-					}
-					fmt.Fprintf(fcode, ".%v", typeset[tok])
-				}
-				continue loop
-			}
-			if c == '-' {
-				s = -s
-				c = getrune(finput)
-			}
-			j := 0
-			if isdigit(c) {
-				for isdigit(c) {
-					j = j*10 + int(c-'0')
-					c = getrune(finput)
-				}
-				ungetrune(finput, c)
-				j = j * s
-				if j >= max {
-					errorf("Illegal use of $%v", j)
-				}
-			} else if isword(c) || c == '.' {
-				// look for $name
-				ungetrune(finput, c)
-				if gettok() != IDENTIFIER {
-					errorf("$ must be followed by an identifier")
-				}
-				tokn := chfind(2, tokname)
-				fnd := -1
-				c = getrune(finput)
-				if c != '@' {
-					ungetrune(finput, c)
-				} else if gettok() != NUMBER {
-					errorf("@ must be followed by number")
-				} else {
-					fnd = numbval
-				}
-				for j = 1; j < max; j++ {
-					if tokn == curprod[j] {
-						fnd--
-						if fnd <= 0 {
-							break
-						}
-					}
-				}
-				if j >= max {
-					errorf("$name or $name at number not found")
-				}
-			} else {
-				fcode.WriteRune('$')
-				if s < 0 {
-					fcode.WriteRune('-')
-				}
-				ungetrune(finput, c)
-				continue loop
-			}
-			fmt.Fprintf(fcode, "%sDollar[%v]", prefix, j)
-
-			// put out the proper tag
-			if ntypes != 0 {
-				if j <= 0 && tok < 0 {
-					errorf("must specify type of $%v", j)
-				}
-				if tok < 0 {
-					tok = fdtype(curprod[j])
-				}
-				fmt.Fprintf(fcode, ".%v", typeset[tok])
-			}
-			continue loop
-
-		case '}':
-			brac--
-			if brac != 0 {
-				break
-			}
-			fcode.WriteRune(c)
-			return
-
-		case '/':
-			nc := getrune(finput)
-			if nc != '/' && nc != '*' {
-				ungetrune(finput, nc)
-				break
-			}
-			// a comment
-			fcode.WriteRune(c)
-			fcode.WriteRune(nc)
-			c = getrune(finput)
-			for c != EOF {
-				switch {
-				case c == '\n':
-					lineno++
-					if nc == '/' { // end of // comment
-						break swt
-					}
-				case c == '*' && nc == '*': // end of /* comment?
-					nnc := getrune(finput)
-					if nnc == '/' {
-						fcode.WriteRune('*')
-						fcode.WriteRune('/')
-						c = getrune(finput)
-						break swt
-					}
-					ungetrune(finput, nnc)
-				}
-				fcode.WriteRune(c)
-				c = getrune(finput)
-			}
-			errorf("EOF inside comment")
-
-		case '\'', '"':
-			// character string or constant
-			match := c
-			fcode.WriteRune(c)
-			c = getrune(finput)
-			for c != EOF {
-				if c == '\\' {
-					fcode.WriteRune(c)
-					c = getrune(finput)
-					if c == '\n' {
-						lineno++
-					}
-				} else if c == match {
-					break swt
-				}
-				if c == '\n' {
-					errorf("newline in string or char const")
-				}
-				fcode.WriteRune(c)
-				c = getrune(finput)
-			}
-			errorf("EOF in string or character constant")
-
-		case EOF:
-			lineno = lno
-			errorf("action does not terminate")
-
-		case '\n':
-			fmt.Fprint(fcode, "\n\t")
-			lineno++
-			continue loop
-		}
-
-		fcode.WriteRune(c)
-	}
-}
-
-func openup() {
-	infile = flag.Arg(0)
-	finput = open(infile)
-	if finput == nil {
-		errorf("cannot open %v", infile)
-	}
-
-	foutput = nil
-	if vflag != "" {
-		foutput = create(vflag)
-		if foutput == nil {
-			errorf("can't create file %v", vflag)
-		}
-	}
-
-	ftable = nil
-	if oflag == "" {
-		oflag = "y.go"
-	}
-	ftable = create(oflag)
-	if ftable == nil {
-		errorf("can't create file %v", oflag)
-	}
-
-}
-
-//
-// return a pointer to the name of symbol i
-//
-func symnam(i int) string {
-	var s string
-
-	if i >= NTBASE {
-		s = nontrst[i-NTBASE].name
-	} else {
-		s = tokset[i].name
-	}
-	return s
-}
-
-//
-// set elements 0 through n-1 to c
-//
-func aryfil(v []int, n, c int) {
-	for i := 0; i < n; i++ {
-		v[i] = c
-	}
-}
-
-//
-// compute an array with the beginnings of productions yielding given nonterminals
-// The array pres points to these lists
-// the array pyield has the lists: the total size is only NPROD+1
-//
-func cpres() {
-	pres = make([][][]int, nnonter+1)
-	curres := make([][]int, nprod)
-
-	if false {
-		for j := 0; j <= nnonter; j++ {
-			fmt.Printf("nnonter[%v] = %v\n", j, nontrst[j].name)
-		}
-		for j := 0; j < nprod; j++ {
-			fmt.Printf("prdptr[%v][0] = %v+NTBASE\n", j, prdptr[j][0]-NTBASE)
-		}
-	}
-
-	fatfl = 0 // make undefined symbols nonfatal
-	for i := 0; i <= nnonter; i++ {
-		n := 0
-		c := i + NTBASE
-		for j := 0; j < nprod; j++ {
-			if prdptr[j][0] == c {
-				curres[n] = prdptr[j][1:]
-				n++
-			}
-		}
-		if n == 0 {
-			errorf("nonterminal %v not defined", nontrst[i].name)
-			continue
-		}
-		pres[i] = make([][]int, n)
-		copy(pres[i], curres)
-	}
-	fatfl = 1
-	if nerrors != 0 {
-		summary()
-		exit(1)
-	}
-}
-
-func dumppres() {
-	for i := 0; i <= nnonter; i++ {
-		fmt.Printf("nonterm %d\n", i)
-		curres := pres[i]
-		for j := 0; j < len(curres); j++ {
-			fmt.Printf("\tproduction %d:", j)
-			prd := curres[j]
-			for k := 0; k < len(prd); k++ {
-				fmt.Printf(" %d", prd[k])
-			}
-			fmt.Print("\n")
-		}
-	}
-}
-
-//
-// mark nonterminals which derive the empty string
-// also, look for nonterminals which don't derive any token strings
-//
-func cempty() {
-	var i, p, np int
-	var prd []int
-
-	pempty = make([]int, nnonter+1)
-
-	// first, use the array pempty to detect productions that can never be reduced
-	// set pempty to WHONOWS
-	aryfil(pempty, nnonter+1, WHOKNOWS)
-
-	// now, look at productions, marking nonterminals which derive something
-more:
-	for {
-		for i = 0; i < nprod; i++ {
-			prd = prdptr[i]
-			if pempty[prd[0]-NTBASE] != 0 {
-				continue
-			}
-			np = len(prd) - 1
-			for p = 1; p < np; p++ {
-				if prd[p] >= NTBASE && pempty[prd[p]-NTBASE] == WHOKNOWS {
-					break
-				}
-			}
-			// production can be derived
-			if p == np {
-				pempty[prd[0]-NTBASE] = OK
-				continue more
-			}
-		}
-		break
-	}
-
-	// now, look at the nonterminals, to see if they are all OK
-	for i = 0; i <= nnonter; i++ {
-		// the added production rises or falls as the start symbol ...
-		if i == 0 {
-			continue
-		}
-		if pempty[i] != OK {
-			fatfl = 0
-			errorf("nonterminal " + nontrst[i].name + " never derives any token string")
-		}
-	}
-
-	if nerrors != 0 {
-		summary()
-		exit(1)
-	}
-
-	// now, compute the pempty array, to see which nonterminals derive the empty string
-	// set pempty to WHOKNOWS
-	aryfil(pempty, nnonter+1, WHOKNOWS)
-
-	// loop as long as we keep finding empty nonterminals
-
-again:
-	for {
-	next:
-		for i = 1; i < nprod; i++ {
-			// not known to be empty
-			prd = prdptr[i]
-			if pempty[prd[0]-NTBASE] != WHOKNOWS {
-				continue
-			}
-			np = len(prd) - 1
-			for p = 1; p < np; p++ {
-				if prd[p] < NTBASE || pempty[prd[p]-NTBASE] != EMPTY {
-					continue next
-				}
-			}
-
-			// we have a nontrivially empty nonterminal
-			pempty[prd[0]-NTBASE] = EMPTY
-
-			// got one ... try for another
-			continue again
-		}
-		return
-	}
-}
-
-func dumpempty() {
-	for i := 0; i <= nnonter; i++ {
-		if pempty[i] == EMPTY {
-			fmt.Printf("non-term %d %s matches empty\n", i, symnam(i+NTBASE))
-		}
-	}
-}
-
-//
-// compute an array with the first of nonterminals
-//
-func cpfir() {
-	var s, n, p, np, ch, i int
-	var curres [][]int
-	var prd []int
-
-	wsets = make([]Wset, nnonter+WSETINC)
-	pfirst = make([]Lkset, nnonter+1)
-	for i = 0; i <= nnonter; i++ {
-		wsets[i].ws = mkset()
-		pfirst[i] = mkset()
-		curres = pres[i]
-		n = len(curres)
-
-		// initially fill the sets
-		for s = 0; s < n; s++ {
-			prd = curres[s]
-			np = len(prd) - 1
-			for p = 0; p < np; p++ {
-				ch = prd[p]
-				if ch < NTBASE {
-					setbit(pfirst[i], ch)
-					break
-				}
-				if pempty[ch-NTBASE] == 0 {
-					break
-				}
-			}
-		}
-	}
-
-	// now, reflect transitivity
-	changes := 1
-	for changes != 0 {
-		changes = 0
-		for i = 0; i <= nnonter; i++ {
-			curres = pres[i]
-			n = len(curres)
-			for s = 0; s < n; s++ {
-				prd = curres[s]
-				np = len(prd) - 1
-				for p = 0; p < np; p++ {
-					ch = prd[p] - NTBASE
-					if ch < 0 {
-						break
-					}
-					changes |= setunion(pfirst[i], pfirst[ch])
-					if pempty[ch] == 0 {
-						break
-					}
-				}
-			}
-		}
-	}
-
-	if indebug == 0 {
-		return
-	}
-	if foutput != nil {
-		for i = 0; i <= nnonter; i++ {
-			fmt.Fprintf(foutput, "\n%v: %v %v\n",
-				nontrst[i].name, pfirst[i], pempty[i])
-		}
-	}
-}
-
-//
-// generate the states
-//
-func stagen() {
-	// initialize
-	nstate = 0
-	tstates = make([]int, ntokens+1)  // states generated by terminal gotos
-	ntstates = make([]int, nnonter+1) // states generated by nonterminal gotos
-	amem = make([]int, ACTSIZE)
-	memp = 0
-
-	clset = mkset()
-	pstate[0] = 0
-	pstate[1] = 0
-	aryfil(clset, tbitset, 0)
-	putitem(Pitem{prdptr[0], 0, 0, 0}, clset)
-	tystate[0] = MUSTDO
-	nstate = 1
-	pstate[2] = pstate[1]
-
-	//
-	// now, the main state generation loop
-	// first pass generates all of the states
-	// later passes fix up lookahead
-	// could be sped up a lot by remembering
-	// results of the first pass rather than recomputing
-	//
-	first := 1
-	for more := 1; more != 0; first = 0 {
-		more = 0
-		for i := 0; i < nstate; i++ {
-			if tystate[i] != MUSTDO {
-				continue
-			}
-
-			tystate[i] = DONE
-			aryfil(temp1, nnonter+1, 0)
-
-			// take state i, close it, and do gotos
-			closure(i)
-
-			// generate goto's
-			for p := 0; p < cwp; p++ {
-				pi := wsets[p]
-				if pi.flag != 0 {
-					continue
-				}
-				wsets[p].flag = 1
-				c := pi.pitem.first
-				if c <= 1 {
-					if pstate[i+1]-pstate[i] <= p {
-						tystate[i] = MUSTLOOKAHEAD
-					}
-					continue
-				}
-
-				// do a goto on c
-				putitem(wsets[p].pitem, wsets[p].ws)
-				for q := p + 1; q < cwp; q++ {
-					// this item contributes to the goto
-					if c == wsets[q].pitem.first {
-						putitem(wsets[q].pitem, wsets[q].ws)
-						wsets[q].flag = 1
-					}
-				}
-
-				if c < NTBASE {
-					state(c) // register new state
-				} else {
-					temp1[c-NTBASE] = state(c)
-				}
-			}
-
-			if gsdebug != 0 && foutput != nil {
-				fmt.Fprintf(foutput, "%v: ", i)
-				for j := 0; j <= nnonter; j++ {
-					if temp1[j] != 0 {
-						fmt.Fprintf(foutput, "%v %v,", nontrst[j].name, temp1[j])
-					}
-				}
-				fmt.Fprintf(foutput, "\n")
-			}
-
-			if first != 0 {
-				indgo[i] = apack(temp1[1:], nnonter-1) - 1
-			}
-
-			more++
-		}
-	}
-}
-
-//
-// generate the closure of state i
-//
-func closure(i int) {
-	zzclose++
-
-	// first, copy kernel of state i to wsets
-	cwp = 0
-	q := pstate[i+1]
-	for p := pstate[i]; p < q; p++ {
-		wsets[cwp].pitem = statemem[p].pitem
-		wsets[cwp].flag = 1 // this item must get closed
-		copy(wsets[cwp].ws, statemem[p].look)
-		cwp++
-	}
-
-	// now, go through the loop, closing each item
-	work := 1
-	for work != 0 {
-		work = 0
-		for u := 0; u < cwp; u++ {
-			if wsets[u].flag == 0 {
-				continue
-			}
-
-			// dot is before c
-			c := wsets[u].pitem.first
-			if c < NTBASE {
-				wsets[u].flag = 0
-				// only interesting case is where . is before nonterminal
-				continue
-			}
-
-			// compute the lookahead
-			aryfil(clset, tbitset, 0)
-
-			// find items involving c
-			for v := u; v < cwp; v++ {
-				if wsets[v].flag != 1 || wsets[v].pitem.first != c {
-					continue
-				}
-				pi := wsets[v].pitem.prod
-				ipi := wsets[v].pitem.off + 1
-
-				wsets[v].flag = 0
-				if nolook != 0 {
-					continue
-				}
-
-				ch := pi[ipi]
-				ipi++
-				for ch > 0 {
-					// terminal symbol
-					if ch < NTBASE {
-						setbit(clset, ch)
-						break
-					}
-
-					// nonterminal symbol
-					setunion(clset, pfirst[ch-NTBASE])
-					if pempty[ch-NTBASE] == 0 {
-						break
-					}
-					ch = pi[ipi]
-					ipi++
-				}
-				if ch <= 0 {
-					setunion(clset, wsets[v].ws)
-				}
-			}
-
-			//
-			// now loop over productions derived from c
-			//
-			curres := pres[c-NTBASE]
-			n := len(curres)
-
-		nexts:
-			// initially fill the sets
-			for s := 0; s < n; s++ {
-				prd := curres[s]
-
-				//
-				// put these items into the closure
-				// is the item there
-				//
-				for v := 0; v < cwp; v++ {
-					// yes, it is there
-					if wsets[v].pitem.off == 0 &&
-						aryeq(wsets[v].pitem.prod, prd) != 0 {
-						if nolook == 0 &&
-							setunion(wsets[v].ws, clset) != 0 {
-							wsets[v].flag = 1
-							work = 1
-						}
-						continue nexts
-					}
-				}
-
-				//  not there; make a new entry
-				if cwp >= len(wsets) {
-					awsets := make([]Wset, cwp+WSETINC)
-					copy(awsets, wsets)
-					wsets = awsets
-				}
-				wsets[cwp].pitem = Pitem{prd, 0, prd[0], -prd[len(prd)-1]}
-				wsets[cwp].flag = 1
-				wsets[cwp].ws = mkset()
-				if nolook == 0 {
-					work = 1
-					copy(wsets[cwp].ws, clset)
-				}
-				cwp++
-			}
-		}
-	}
-
-	// have computed closure; flags are reset; return
-	if cldebug != 0 && foutput != nil {
-		fmt.Fprintf(foutput, "\nState %v, nolook = %v\n", i, nolook)
-		for u := 0; u < cwp; u++ {
-			if wsets[u].flag != 0 {
-				fmt.Fprintf(foutput, "flag set\n")
-			}
-			wsets[u].flag = 0
-			fmt.Fprintf(foutput, "\t%v", writem(wsets[u].pitem))
-			prlook(wsets[u].ws)
-			fmt.Fprintf(foutput, "\n")
-		}
-	}
-}
-
-//
-// sorts last state,and sees if it equals earlier ones. returns state number
-//
-func state(c int) int {
-	zzstate++
-	p1 := pstate[nstate]
-	p2 := pstate[nstate+1]
-	if p1 == p2 {
-		return 0 // null state
-	}
-
-	// sort the items
-	var k, l int
-	for k = p1 + 1; k < p2; k++ { // make k the biggest
-		for l = k; l > p1; l-- {
-			if statemem[l].pitem.prodno < statemem[l-1].pitem.prodno ||
-				statemem[l].pitem.prodno == statemem[l-1].pitem.prodno &&
-					statemem[l].pitem.off < statemem[l-1].pitem.off {
-				s := statemem[l]
-				statemem[l] = statemem[l-1]
-				statemem[l-1] = s
-			} else {
-				break
-			}
-		}
-	}
-
-	size1 := p2 - p1 // size of state
-
-	var i int
-	if c >= NTBASE {
-		i = ntstates[c-NTBASE]
-	} else {
-		i = tstates[c]
-	}
-
-look:
-	for ; i != 0; i = mstates[i] {
-		// get ith state
-		q1 := pstate[i]
-		q2 := pstate[i+1]
-		size2 := q2 - q1
-		if size1 != size2 {
-			continue
-		}
-		k = p1
-		for l = q1; l < q2; l++ {
-			if aryeq(statemem[l].pitem.prod, statemem[k].pitem.prod) == 0 ||
-				statemem[l].pitem.off != statemem[k].pitem.off {
-				continue look
-			}
-			k++
-		}
-
-		// found it
-		pstate[nstate+1] = pstate[nstate] // delete last state
-
-		// fix up lookaheads
-		if nolook != 0 {
-			return i
-		}
-		k = p1
-		for l = q1; l < q2; l++ {
-			if setunion(statemem[l].look, statemem[k].look) != 0 {
-				tystate[i] = MUSTDO
-			}
-			k++
-		}
-		return i
-	}
-
-	// state is new
-	zznewstate++
-	if nolook != 0 {
-		errorf("yacc state/nolook error")
-	}
-	pstate[nstate+2] = p2
-	if nstate+1 >= NSTATES {
-		errorf("too many states")
-	}
-	if c >= NTBASE {
-		mstates[nstate] = ntstates[c-NTBASE]
-		ntstates[c-NTBASE] = nstate
-	} else {
-		mstates[nstate] = tstates[c]
-		tstates[c] = nstate
-	}
-	tystate[nstate] = MUSTDO
-	nstate++
-	return nstate - 1
-}
-
-func putitem(p Pitem, set Lkset) {
-	p.off++
-	p.first = p.prod[p.off]
-
-	if pidebug != 0 && foutput != nil {
-		fmt.Fprintf(foutput, "putitem(%v), state %v\n", writem(p), nstate)
-	}
-	j := pstate[nstate+1]
-	if j >= len(statemem) {
-		asm := make([]Item, j+STATEINC)
-		copy(asm, statemem)
-		statemem = asm
-	}
-	statemem[j].pitem = p
-	if nolook == 0 {
-		s := mkset()
-		copy(s, set)
-		statemem[j].look = s
-	}
-	j++
-	pstate[nstate+1] = j
-}
-
-//
-// creates output string for item pointed to by pp
-//
-func writem(pp Pitem) string {
-	var i int
-
-	p := pp.prod
-	q := chcopy(nontrst[prdptr[pp.prodno][0]-NTBASE].name) + ": "
-	npi := pp.off
-
-	pi := aryeq(p, prdptr[pp.prodno])
-
-	for {
-		c := ' '
-		if pi == npi {
-			c = '.'
-		}
-		q += string(c)
-
-		i = p[pi]
-		pi++
-		if i <= 0 {
-			break
-		}
-		q += chcopy(symnam(i))
-	}
-
-	// an item calling for a reduction
-	i = p[npi]
-	if i < 0 {
-		q += fmt.Sprintf("    (%v)", -i)
-	}
-
-	return q
-}
-
-//
-// pack state i from temp1 into amem
-//
-func apack(p []int, n int) int {
-	//
-	// we don't need to worry about checking because
-	// we will only look at entries known to be there...
-	// eliminate leading and trailing 0's
-	//
-	off := 0
-	pp := 0
-	for ; pp <= n && p[pp] == 0; pp++ {
-		off--
-	}
-
-	// no actions
-	if pp > n {
-		return 0
-	}
-	for ; n > pp && p[n] == 0; n-- {
-	}
-	p = p[pp : n+1]
-
-	// now, find a place for the elements from p to q, inclusive
-	r := len(amem) - len(p)
-
-nextk:
-	for rr := 0; rr <= r; rr++ {
-		qq := rr
-		for pp = 0; pp < len(p); pp++ {
-			if p[pp] != 0 {
-				if p[pp] != amem[qq] && amem[qq] != 0 {
-					continue nextk
-				}
-			}
-			qq++
-		}
-
-		// we have found an acceptable k
-		if pkdebug != 0 && foutput != nil {
-			fmt.Fprintf(foutput, "off = %v, k = %v\n", off+rr, rr)
-		}
-		qq = rr
-		for pp = 0; pp < len(p); pp++ {
-			if p[pp] != 0 {
-				if qq > memp {
-					memp = qq
-				}
-				amem[qq] = p[pp]
-			}
-			qq++
-		}
-		if pkdebug != 0 && foutput != nil {
-			for pp = 0; pp <= memp; pp += 10 {
-				fmt.Fprintf(foutput, "\n")
-				for qq = pp; qq <= pp+9; qq++ {
-					fmt.Fprintf(foutput, "%v ", amem[qq])
-				}
-				fmt.Fprintf(foutput, "\n")
-			}
-		}
-		return off + rr
-	}
-	errorf("no space in action table")
-	return 0
-}
-
-//
-// print the output for the states
-//
-func output() {
-	var c, u, v int
-
-	if !lflag {
-		fmt.Fprintf(ftable, "\n//line yacctab:1")
-	}
-	fmt.Fprintf(ftable, "\nvar %sExca = [...]int{\n", prefix)
-
-	if len(errors) > 0 {
-		stateTable = make([]Row, nstate)
-	}
-
-	noset := mkset()
-
-	// output the stuff for state i
-	for i := 0; i < nstate; i++ {
-		nolook = 0
-		if tystate[i] != MUSTLOOKAHEAD {
-			nolook = 1
-		}
-		closure(i)
-
-		// output actions
-		nolook = 1
-		aryfil(temp1, ntokens+nnonter+1, 0)
-		for u = 0; u < cwp; u++ {
-			c = wsets[u].pitem.first
-			if c > 1 && c < NTBASE && temp1[c] == 0 {
-				for v = u; v < cwp; v++ {
-					if c == wsets[v].pitem.first {
-						putitem(wsets[v].pitem, noset)
-					}
-				}
-				temp1[c] = state(c)
-			} else if c > NTBASE {
-				c -= NTBASE
-				if temp1[c+ntokens] == 0 {
-					temp1[c+ntokens] = amem[indgo[i]+c]
-				}
-			}
-		}
-		if i == 1 {
-			temp1[1] = ACCEPTCODE
-		}
-
-		// now, we have the shifts; look at the reductions
-		lastred = 0
-		for u = 0; u < cwp; u++ {
-			c = wsets[u].pitem.first
-
-			// reduction
-			if c > 0 {
-				continue
-			}
-			lastred = -c
-			us := wsets[u].ws
-			for k := 0; k <= ntokens; k++ {
-				if bitset(us, k) == 0 {
-					continue
-				}
-				if temp1[k] == 0 {
-					temp1[k] = c
-				} else if temp1[k] < 0 { // reduce/reduce conflict
-					if foutput != nil {
-						fmt.Fprintf(foutput,
-							"\n %v: reduce/reduce conflict  (red'ns "+
-								"%v and %v) on %v",
-							i, -temp1[k], lastred, symnam(k))
-					}
-					if -temp1[k] > lastred {
-						temp1[k] = -lastred
-					}
-					zzrrconf++
-				} else {
-					// potential shift/reduce conflict
-					precftn(lastred, k, i)
-				}
-			}
-		}
-		wract(i)
-	}
-
-	fmt.Fprintf(ftable, "}\n")
-	ftable.WriteRune('\n')
-	fmt.Fprintf(ftable, "const %sNprod = %v\n", prefix, nprod)
-	fmt.Fprintf(ftable, "const %sPrivate = %v\n", prefix, PRIVATE)
-	ftable.WriteRune('\n')
-	fmt.Fprintf(ftable, "var %sTokenNames []string\n", prefix)
-	fmt.Fprintf(ftable, "var %sStates []string\n", prefix)
-}
-
-//
-// decide a shift/reduce conflict by precedence.
-// r is a rule number, t a token number
-// the conflict is in state s
-// temp1[t] is changed to reflect the action
-//
-func precftn(r, t, s int) {
-	var action int
-
-	lp := levprd[r]
-	lt := toklev[t]
-	if PLEVEL(lt) == 0 || PLEVEL(lp) == 0 {
-		// conflict
-		if foutput != nil {
-			fmt.Fprintf(foutput,
-				"\n%v: shift/reduce conflict (shift %v(%v), red'n %v(%v)) on %v",
-				s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t))
-		}
-		zzsrconf++
-		return
-	}
-	if PLEVEL(lt) == PLEVEL(lp) {
-		action = ASSOC(lt)
-	} else if PLEVEL(lt) > PLEVEL(lp) {
-		action = RASC // shift
-	} else {
-		action = LASC
-	} // reduce
-	switch action {
-	case BASC: // error action
-		temp1[t] = ERRCODE
-	case LASC: // reduce
-		temp1[t] = -r
-	}
-}
-
-//
-// output state i
-// temp1 has the actions, lastred the default
-//
-func wract(i int) {
-	var p, p1 int
-
-	// find the best choice for lastred
-	lastred = 0
-	ntimes := 0
-	for j := 0; j <= ntokens; j++ {
-		if temp1[j] >= 0 {
-			continue
-		}
-		if temp1[j]+lastred == 0 {
-			continue
-		}
-		// count the number of appearances of temp1[j]
-		count := 0
-		tred := -temp1[j]
-		levprd[tred] |= REDFLAG
-		for p = 0; p <= ntokens; p++ {
-			if temp1[p]+tred == 0 {
-				count++
-			}
-		}
-		if count > ntimes {
-			lastred = tred
-			ntimes = count
-		}
-	}
-
-	//
-	// for error recovery, arrange that, if there is a shift on the
-	// error recovery token, `error', that the default be the error action
-	//
-	if temp1[2] > 0 {
-		lastred = 0
-	}
-
-	// clear out entries in temp1 which equal lastred
-	// count entries in optst table
-	n := 0
-	for p = 0; p <= ntokens; p++ {
-		p1 = temp1[p]
-		if p1+lastred == 0 {
-			temp1[p] = 0
-			p1 = 0
-		}
-		if p1 > 0 && p1 != ACCEPTCODE && p1 != ERRCODE {
-			n++
-		}
-	}
-
-	wrstate(i)
-	defact[i] = lastred
-	flag := 0
-	os := make([]int, n*2)
-	n = 0
-	for p = 0; p <= ntokens; p++ {
-		p1 = temp1[p]
-		if p1 != 0 {
-			if p1 < 0 {
-				p1 = -p1
-			} else if p1 == ACCEPTCODE {
-				p1 = -1
-			} else if p1 == ERRCODE {
-				p1 = 0
-			} else {
-				os[n] = p
-				n++
-				os[n] = p1
-				n++
-				zzacent++
-				continue
-			}
-			if flag == 0 {
-				fmt.Fprintf(ftable, "\t-1, %v,\n", i)
-			}
-			flag++
-			fmt.Fprintf(ftable, "\t%v, %v,\n", p, p1)
-			zzexcp++
-		}
-	}
-	if flag != 0 {
-		defact[i] = -2
-		fmt.Fprintf(ftable, "\t-2, %v,\n", lastred)
-	}
-	optst[i] = os
-}
-
-//
-// writes state i
-//
-func wrstate(i int) {
-	var j0, j1, u int
-	var pp, qq int
-
-	if len(errors) > 0 {
-		actions := append([]int(nil), temp1...)
-		defaultAction := ERRCODE
-		if lastred != 0 {
-			defaultAction = -lastred
-		}
-		stateTable[i] = Row{actions, defaultAction}
-	}
-
-	if foutput == nil {
-		return
-	}
-	fmt.Fprintf(foutput, "\nstate %v\n", i)
-	qq = pstate[i+1]
-	for pp = pstate[i]; pp < qq; pp++ {
-		fmt.Fprintf(foutput, "\t%v\n", writem(statemem[pp].pitem))
-	}
-	if tystate[i] == MUSTLOOKAHEAD {
-		// print out empty productions in closure
-		for u = pstate[i+1] - pstate[i]; u < cwp; u++ {
-			if wsets[u].pitem.first < 0 {
-				fmt.Fprintf(foutput, "\t%v\n", writem(wsets[u].pitem))
-			}
-		}
-	}
-
-	// check for state equal to another
-	for j0 = 0; j0 <= ntokens; j0++ {
-		j1 = temp1[j0]
-		if j1 != 0 {
-			fmt.Fprintf(foutput, "\n\t%v  ", symnam(j0))
-
-			// shift, error, or accept
-			if j1 > 0 {
-				if j1 == ACCEPTCODE {
-					fmt.Fprintf(foutput, "accept")
-				} else if j1 == ERRCODE {
-					fmt.Fprintf(foutput, "error")
-				} else {
-					fmt.Fprintf(foutput, "shift %v", j1)
-				}
-			} else {
-				fmt.Fprintf(foutput, "reduce %v (src line %v)", -j1, rlines[-j1])
-			}
-		}
-	}
-
-	// output the final production
-	if lastred != 0 {
-		fmt.Fprintf(foutput, "\n\t.  reduce %v (src line %v)\n\n",
-			lastred, rlines[lastred])
-	} else {
-		fmt.Fprintf(foutput, "\n\t.  error\n\n")
-	}
-
-	// now, output nonterminal actions
-	j1 = ntokens
-	for j0 = 1; j0 <= nnonter; j0++ {
-		j1++
-		if temp1[j1] != 0 {
-			fmt.Fprintf(foutput, "\t%v  goto %v\n", symnam(j0+NTBASE), temp1[j1])
-		}
-	}
-}
-
-//
-// output the gotos for the nontermninals
-//
-func go2out() {
-	for i := 1; i <= nnonter; i++ {
-		go2gen(i)
-
-		// find the best one to make default
-		best := -1
-		times := 0
-
-		// is j the most frequent
-		for j := 0; j < nstate; j++ {
-			if tystate[j] == 0 {
-				continue
-			}
-			if tystate[j] == best {
-				continue
-			}
-
-			// is tystate[j] the most frequent
-			count := 0
-			cbest := tystate[j]
-			for k := j; k < nstate; k++ {
-				if tystate[k] == cbest {
-					count++
-				}
-			}
-			if count > times {
-				best = cbest
-				times = count
-			}
-		}
-
-		// best is now the default entry
-		zzgobest += times - 1
-		n := 0
-		for j := 0; j < nstate; j++ {
-			if tystate[j] != 0 && tystate[j] != best {
-				n++
-			}
-		}
-		goent := make([]int, 2*n+1)
-		n = 0
-		for j := 0; j < nstate; j++ {
-			if tystate[j] != 0 && tystate[j] != best {
-				goent[n] = j
-				n++
-				goent[n] = tystate[j]
-				n++
-				zzgoent++
-			}
-		}
-
-		// now, the default
-		if best == -1 {
-			best = 0
-		}
-
-		zzgoent++
-		goent[n] = best
-		yypgo[i] = goent
-	}
-}
-
-//
-// output the gotos for nonterminal c
-//
-func go2gen(c int) {
-	var i, cc, p, q int
-
-	// first, find nonterminals with gotos on c
-	aryfil(temp1, nnonter+1, 0)
-	temp1[c] = 1
-	work := 1
-	for work != 0 {
-		work = 0
-		for i = 0; i < nprod; i++ {
-			// cc is a nonterminal with a goto on c
-			cc = prdptr[i][1] - NTBASE
-			if cc >= 0 && temp1[cc] != 0 {
-				// thus, the left side of production i does too
-				cc = prdptr[i][0] - NTBASE
-				if temp1[cc] == 0 {
-					work = 1
-					temp1[cc] = 1
-				}
-			}
-		}
-	}
-
-	// now, we have temp1[c] = 1 if a goto on c in closure of cc
-	if g2debug != 0 && foutput != nil {
-		fmt.Fprintf(foutput, "%v: gotos on ", nontrst[c].name)
-		for i = 0; i <= nnonter; i++ {
-			if temp1[i] != 0 {
-				fmt.Fprintf(foutput, "%v ", nontrst[i].name)
-			}
-		}
-		fmt.Fprintf(foutput, "\n")
-	}
-
-	// now, go through and put gotos into tystate
-	aryfil(tystate, nstate, 0)
-	for i = 0; i < nstate; i++ {
-		q = pstate[i+1]
-		for p = pstate[i]; p < q; p++ {
-			cc = statemem[p].pitem.first
-			if cc >= NTBASE {
-				// goto on c is possible
-				if temp1[cc-NTBASE] != 0 {
-					tystate[i] = amem[indgo[i]+c]
-					break
-				}
-			}
-		}
-	}
-}
-
-//
-// in order to free up the mem and amem arrays for the optimizer,
-// and still be able to output yyr1, etc., after the sizes of
-// the action array is known, we hide the nonterminals
-// derived by productions in levprd.
-//
-func hideprod() {
-	nred := 0
-	levprd[0] = 0
-	for i := 1; i < nprod; i++ {
-		if (levprd[i] & REDFLAG) == 0 {
-			if foutput != nil {
-				fmt.Fprintf(foutput, "Rule not reduced: %v\n",
-					writem(Pitem{prdptr[i], 0, 0, i}))
-			}
-			fmt.Printf("rule %v never reduced\n", writem(Pitem{prdptr[i], 0, 0, i}))
-			nred++
-		}
-		levprd[i] = prdptr[i][0] - NTBASE
-	}
-	if nred != 0 {
-		fmt.Printf("%v rules never reduced\n", nred)
-	}
-}
-
-func callopt() {
-	var j, k, p, q, i int
-	var v []int
-
-	pgo = make([]int, nnonter+1)
-	pgo[0] = 0
-	maxoff = 0
-	maxspr = 0
-	for i = 0; i < nstate; i++ {
-		k = 32000
-		j = 0
-		v = optst[i]
-		q = len(v)
-		for p = 0; p < q; p += 2 {
-			if v[p] > j {
-				j = v[p]
-			}
-			if v[p] < k {
-				k = v[p]
-			}
-		}
-
-		// nontrivial situation
-		if k <= j {
-			// j is now the range
-			//			j -= k;			// call scj
-			if k > maxoff {
-				maxoff = k
-			}
-		}
-		tystate[i] = q + 2*j
-		if j > maxspr {
-			maxspr = j
-		}
-	}
-
-	// initialize ggreed table
-	ggreed = make([]int, nnonter+1)
-	for i = 1; i <= nnonter; i++ {
-		ggreed[i] = 1
-		j = 0
-
-		// minimum entry index is always 0
-		v = yypgo[i]
-		q = len(v) - 1
-		for p = 0; p < q; p += 2 {
-			ggreed[i] += 2
-			if v[p] > j {
-				j = v[p]
-			}
-		}
-		ggreed[i] = ggreed[i] + 2*j
-		if j > maxoff {
-			maxoff = j
-		}
-	}
-
-	// now, prepare to put the shift actions into the amem array
-	for i = 0; i < ACTSIZE; i++ {
-		amem[i] = 0
-	}
-	maxa = 0
-	for i = 0; i < nstate; i++ {
-		if tystate[i] == 0 && adb > 1 {
-			fmt.Fprintf(ftable, "State %v: null\n", i)
-		}
-		indgo[i] = yyFlag
-	}
-
-	i = nxti()
-	for i != NOMORE {
-		if i >= 0 {
-			stin(i)
-		} else {
-			gin(-i)
-		}
-		i = nxti()
-	}
-
-	// print amem array
-	if adb > 2 {
-		for p = 0; p <= maxa; p += 10 {
-			fmt.Fprintf(ftable, "%v  ", p)
-			for i = 0; i < 10; i++ {
-				fmt.Fprintf(ftable, "%v  ", amem[p+i])
-			}
-			ftable.WriteRune('\n')
-		}
-	}
-
-	aoutput()
-	osummary()
-}
-
-//
-// finds the next i
-//
-func nxti() int {
-	max := 0
-	maxi := 0
-	for i := 1; i <= nnonter; i++ {
-		if ggreed[i] >= max {
-			max = ggreed[i]
-			maxi = -i
-		}
-	}
-	for i := 0; i < nstate; i++ {
-		if tystate[i] >= max {
-			max = tystate[i]
-			maxi = i
-		}
-	}
-	if max == 0 {
-		return NOMORE
-	}
-	return maxi
-}
-
-func gin(i int) {
-	var s int
-
-	// enter gotos on nonterminal i into array amem
-	ggreed[i] = 0
-
-	q := yypgo[i]
-	nq := len(q) - 1
-
-	// now, find amem place for it
-nextgp:
-	for p := 0; p < ACTSIZE; p++ {
-		if amem[p] != 0 {
-			continue
-		}
-		for r := 0; r < nq; r += 2 {
-			s = p + q[r] + 1
-			if s > maxa {
-				maxa = s
-				if maxa >= ACTSIZE {
-					errorf("a array overflow")
-				}
-			}
-			if amem[s] != 0 {
-				continue nextgp
-			}
-		}
-
-		// we have found amem spot
-		amem[p] = q[nq]
-		if p > maxa {
-			maxa = p
-		}
-		for r := 0; r < nq; r += 2 {
-			s = p + q[r] + 1
-			amem[s] = q[r+1]
-		}
-		pgo[i] = p
-		if adb > 1 {
-			fmt.Fprintf(ftable, "Nonterminal %v, entry at %v\n", i, pgo[i])
-		}
-		return
-	}
-	errorf("cannot place goto %v\n", i)
-}
-
-func stin(i int) {
-	var s int
-
-	tystate[i] = 0
-
-	// enter state i into the amem array
-	q := optst[i]
-	nq := len(q)
-
-nextn:
-	// find an acceptable place
-	for n := -maxoff; n < ACTSIZE; n++ {
-		flag := 0
-		for r := 0; r < nq; r += 2 {
-			s = q[r] + n
-			if s < 0 || s > ACTSIZE {
-				continue nextn
-			}
-			if amem[s] == 0 {
-				flag++
-			} else if amem[s] != q[r+1] {
-				continue nextn
-			}
-		}
-
-		// check the position equals another only if the states are identical
-		for j := 0; j < nstate; j++ {
-			if indgo[j] == n {
-
-				// we have some disagreement
-				if flag != 0 {
-					continue nextn
-				}
-				if nq == len(optst[j]) {
-
-					// states are equal
-					indgo[i] = n
-					if adb > 1 {
-						fmt.Fprintf(ftable, "State %v: entry at"+
-							"%v equals state %v\n",
-							i, n, j)
-					}
-					return
-				}
-
-				// we have some disagreement
-				continue nextn
-			}
-		}
-
-		for r := 0; r < nq; r += 2 {
-			s = q[r] + n
-			if s > maxa {
-				maxa = s
-			}
-			if amem[s] != 0 && amem[s] != q[r+1] {
-				errorf("clobber of a array, pos'n %v, by %v", s, q[r+1])
-			}
-			amem[s] = q[r+1]
-		}
-		indgo[i] = n
-		if adb > 1 {
-			fmt.Fprintf(ftable, "State %v: entry at %v\n", i, indgo[i])
-		}
-		return
-	}
-	errorf("Error; failure to place state %v", i)
-}
-
-//
-// this version is for limbo
-// write out the optimized parser
-//
-func aoutput() {
-	ftable.WriteRune('\n')
-	fmt.Fprintf(ftable, "const %sLast = %v\n\n", prefix, maxa+1)
-	arout("Act", amem, maxa+1)
-	arout("Pact", indgo, nstate)
-	arout("Pgo", pgo, nnonter+1)
-}
-
-//
-// put out other arrays, copy the parsers
-//
-func others() {
-	var i, j int
-
-	arout("R1", levprd, nprod)
-	aryfil(temp1, nprod, 0)
-
-	//
-	//yyr2 is the number of rules for each production
-	//
-	for i = 1; i < nprod; i++ {
-		temp1[i] = len(prdptr[i]) - 2
-	}
-	arout("R2", temp1, nprod)
-
-	aryfil(temp1, nstate, -1000)
-	for i = 0; i <= ntokens; i++ {
-		for j := tstates[i]; j != 0; j = mstates[j] {
-			temp1[j] = i
-		}
-	}
-	for i = 0; i <= nnonter; i++ {
-		for j = ntstates[i]; j != 0; j = mstates[j] {
-			temp1[j] = -i
-		}
-	}
-	arout("Chk", temp1, nstate)
-	arout("Def", defact, nstate)
-
-	// put out token translation tables
-	// table 1 has 0-256
-	aryfil(temp1, 256, 0)
-	c := 0
-	for i = 1; i <= ntokens; i++ {
-		j = tokset[i].value
-		if j >= 0 && j < 256 {
-			if temp1[j] != 0 {
-				fmt.Print("yacc bug -- cannot have 2 different Ts with same value\n")
-				fmt.Printf("	%s and %s\n", tokset[i].name, tokset[temp1[j]].name)
-				nerrors++
-			}
-			temp1[j] = i
-			if j > c {
-				c = j
-			}
-		}
-	}
-	for i = 0; i <= c; i++ {
-		if temp1[i] == 0 {
-			temp1[i] = YYLEXUNK
-		}
-	}
-	arout("Tok1", temp1, c+1)
-
-	// table 2 has PRIVATE-PRIVATE+256
-	aryfil(temp1, 256, 0)
-	c = 0
-	for i = 1; i <= ntokens; i++ {
-		j = tokset[i].value - PRIVATE
-		if j >= 0 && j < 256 {
-			if temp1[j] != 0 {
-				fmt.Print("yacc bug -- cannot have 2 different Ts with same value\n")
-				fmt.Printf("	%s and %s\n", tokset[i].name, tokset[temp1[j]].name)
-				nerrors++
-			}
-			temp1[j] = i
-			if j > c {
-				c = j
-			}
-		}
-	}
-	arout("Tok2", temp1, c+1)
-
-	// table 3 has everything else
-	fmt.Fprintf(ftable, "var %sTok3 = [...]int{\n\t", prefix)
-	c = 0
-	for i = 1; i <= ntokens; i++ {
-		j = tokset[i].value
-		if j >= 0 && j < 256 {
-			continue
-		}
-		if j >= PRIVATE && j < 256+PRIVATE {
-			continue
-		}
-
-		if c%5 != 0 {
-			ftable.WriteRune(' ')
-		}
-		fmt.Fprintf(ftable, "%d, %d,", j, i)
-		c++
-		if c%5 == 0 {
-			fmt.Fprint(ftable, "\n\t")
-		}
-	}
-	if c%5 != 0 {
-		ftable.WriteRune(' ')
-	}
-	fmt.Fprintf(ftable, "%d,\n}\n", 0)
-
-	// Custom error messages.
-	fmt.Fprintf(ftable, "\n")
-	fmt.Fprintf(ftable, "var %sErrorMessages = [...]struct {\n", prefix)
-	fmt.Fprintf(ftable, "\tstate int\n")
-	fmt.Fprintf(ftable, "\ttoken int\n")
-	fmt.Fprintf(ftable, "\tmsg   string\n")
-	fmt.Fprintf(ftable, "}{\n")
-	for _, error := range errors {
-		lineno = error.lineno
-		state, token := runMachine(error.tokens)
-		fmt.Fprintf(ftable, "\t{%v, %v, %s},\n", state, token, error.msg)
-	}
-	fmt.Fprintf(ftable, "}\n")
-
-	// copy parser text
-	ch := getrune(finput)
-	for ch != EOF {
-		ftable.WriteRune(ch)
-		ch = getrune(finput)
-	}
-
-	// copy yaccpar
-	if !lflag {
-		fmt.Fprintf(ftable, "\n//line yaccpar:1\n")
-	}
-
-	parts := strings.SplitN(yaccpar, prefix+"run()", 2)
-	fmt.Fprintf(ftable, "%v", parts[0])
-	ftable.Write(fcode.Bytes())
-	fmt.Fprintf(ftable, "%v", parts[1])
-}
-
-func runMachine(tokens []string) (state, token int) {
-	var stack []int
-	i := 0
-	token = -1
-
-Loop:
-	if token < 0 {
-		token = chfind(2, tokens[i])
-		i++
-	}
-
-	row := stateTable[state]
-
-	c := token
-	if token >= NTBASE {
-		c = token - NTBASE + ntokens
-	}
-	action := row.actions[c]
-	if action == 0 {
-		action = row.defaultAction
-	}
-
-	switch {
-	case action == ACCEPTCODE:
-		errorf("tokens are accepted")
-		return
-	case action == ERRCODE:
-		if token >= NTBASE {
-			errorf("error at non-terminal token %s", symnam(token))
-		}
-		return
-	case action > 0:
-		// Shift to state action.
-		stack = append(stack, state)
-		state = action
-		token = -1
-		goto Loop
-	default:
-		// Reduce by production -action.
-		prod := prdptr[-action]
-		if rhsLen := len(prod) - 2; rhsLen > 0 {
-			n := len(stack) - rhsLen
-			state = stack[n]
-			stack = stack[:n]
-		}
-		if token >= 0 {
-			i--
-		}
-		token = prod[0]
-		goto Loop
-	}
-}
-
-func arout(s string, v []int, n int) {
-	s = prefix + s
-	fmt.Fprintf(ftable, "var %v = [...]int{\n", s)
-	for i := 0; i < n; i++ {
-		if i%10 == 0 {
-			fmt.Fprintf(ftable, "\n\t")
-		} else {
-			ftable.WriteRune(' ')
-		}
-		fmt.Fprintf(ftable, "%d,", v[i])
-	}
-	fmt.Fprintf(ftable, "\n}\n")
-}
-
-//
-// output the summary on y.output
-//
-func summary() {
-	if foutput != nil {
-		fmt.Fprintf(foutput, "\n%v terminals, %v nonterminals\n", ntokens, nnonter+1)
-		fmt.Fprintf(foutput, "%v grammar rules, %v/%v states\n", nprod, nstate, NSTATES)
-		fmt.Fprintf(foutput, "%v shift/reduce, %v reduce/reduce conflicts reported\n", zzsrconf, zzrrconf)
-		fmt.Fprintf(foutput, "%v working sets used\n", len(wsets))
-		fmt.Fprintf(foutput, "memory: parser %v/%v\n", memp, ACTSIZE)
-		fmt.Fprintf(foutput, "%v extra closures\n", zzclose-2*nstate)
-		fmt.Fprintf(foutput, "%v shift entries, %v exceptions\n", zzacent, zzexcp)
-		fmt.Fprintf(foutput, "%v goto entries\n", zzgoent)
-		fmt.Fprintf(foutput, "%v entries saved by goto default\n", zzgobest)
-	}
-	if zzsrconf != 0 || zzrrconf != 0 {
-		fmt.Printf("\nconflicts: ")
-		if zzsrconf != 0 {
-			fmt.Printf("%v shift/reduce", zzsrconf)
-		}
-		if zzsrconf != 0 && zzrrconf != 0 {
-			fmt.Printf(", ")
-		}
-		if zzrrconf != 0 {
-			fmt.Printf("%v reduce/reduce", zzrrconf)
-		}
-		fmt.Printf("\n")
-	}
-}
-
-//
-// write optimizer summary
-//
-func osummary() {
-	if foutput == nil {
-		return
-	}
-	i := 0
-	for p := maxa; p >= 0; p-- {
-		if amem[p] == 0 {
-			i++
-		}
-	}
-
-	fmt.Fprintf(foutput, "Optimizer space used: output %v/%v\n", maxa+1, ACTSIZE)
-	fmt.Fprintf(foutput, "%v table entries, %v zero\n", maxa+1, i)
-	fmt.Fprintf(foutput, "maximum spread: %v, maximum offset: %v\n", maxspr, maxoff)
-}
-
-//
-// copies and protects "'s in q
-//
-func chcopy(q string) string {
-	s := ""
-	i := 0
-	j := 0
-	for i = 0; i < len(q); i++ {
-		if q[i] == '"' {
-			s += q[j:i] + "\\"
-			j = i
-		}
-	}
-	return s + q[j:i]
-}
-
-func usage() {
-	fmt.Fprintf(stderr, "usage: yacc [-o output] [-v parsetable] input\n")
-	exit(1)
-}
-
-func bitset(set Lkset, bit int) int { return set[bit>>5] & (1 << uint(bit&31)) }
-
-func setbit(set Lkset, bit int) { set[bit>>5] |= (1 << uint(bit&31)) }
-
-func mkset() Lkset { return make([]int, tbitset) }
-
-//
-// set a to the union of a and b
-// return 1 if b is not a subset of a, 0 otherwise
-//
-func setunion(a, b []int) int {
-	sub := 0
-	for i := 0; i < tbitset; i++ {
-		x := a[i]
-		y := x | b[i]
-		a[i] = y
-		if y != x {
-			sub = 1
-		}
-	}
-	return sub
-}
-
-func prlook(p Lkset) {
-	if p == nil {
-		fmt.Fprintf(foutput, "\tNULL")
-		return
-	}
-	fmt.Fprintf(foutput, " { ")
-	for j := 0; j <= ntokens; j++ {
-		if bitset(p, j) != 0 {
-			fmt.Fprintf(foutput, "%v ", symnam(j))
-		}
-	}
-	fmt.Fprintf(foutput, "}")
-}
-
-//
-// utility routines
-//
-var peekrune rune
-
-func isdigit(c rune) bool { return c >= '0' && c <= '9' }
-
-func isword(c rune) bool {
-	return c >= 0xa0 || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
-}
-
-//
-// return 1 if 2 arrays are equal
-// return 0 if not equal
-//
-func aryeq(a []int, b []int) int {
-	n := len(a)
-	if len(b) != n {
-		return 0
-	}
-	for ll := 0; ll < n; ll++ {
-		if a[ll] != b[ll] {
-			return 0
-		}
-	}
-	return 1
-}
-
-func getrune(f *bufio.Reader) rune {
-	var r rune
-
-	if peekrune != 0 {
-		if peekrune == EOF {
-			return EOF
-		}
-		r = peekrune
-		peekrune = 0
-		return r
-	}
-
-	c, n, err := f.ReadRune()
-	if n == 0 {
-		return EOF
-	}
-	if err != nil {
-		errorf("read error: %v", err)
-	}
-	//fmt.Printf("rune = %v n=%v\n", string(c), n);
-	return c
-}
-
-func ungetrune(f *bufio.Reader, c rune) {
-	if f != finput {
-		panic("ungetc - not finput")
-	}
-	if peekrune != 0 {
-		panic("ungetc - 2nd unget")
-	}
-	peekrune = c
-}
-
-func write(f *bufio.Writer, b []byte, n int) int {
-	panic("write")
-}
-
-func open(s string) *bufio.Reader {
-	fi, err := os.Open(s)
-	if err != nil {
-		errorf("error opening %v: %v", s, err)
-	}
-	//fmt.Printf("open %v\n", s);
-	return bufio.NewReader(fi)
-}
-
-func create(s string) *bufio.Writer {
-	fo, err := os.Create(s)
-	if err != nil {
-		errorf("error creating %v: %v", s, err)
-	}
-	//fmt.Printf("create %v mode %v\n", s);
-	return bufio.NewWriter(fo)
-}
-
-//
-// write out error comment
-//
-func lerrorf(lineno int, s string, v ...interface{}) {
-	nerrors++
-	fmt.Fprintf(stderr, s, v...)
-	fmt.Fprintf(stderr, ": %v:%v\n", infile, lineno)
-	if fatfl != 0 {
-		summary()
-		exit(1)
-	}
-}
-
-func errorf(s string, v ...interface{}) {
-	lerrorf(lineno, s, v...)
-}
-
-func exit(status int) {
-	if ftable != nil {
-		ftable.Flush()
-		ftable = nil
-		gofmt()
-	}
-	if foutput != nil {
-		foutput.Flush()
-		foutput = nil
-	}
-	if stderr != nil {
-		stderr.Flush()
-		stderr = nil
-	}
-	os.Exit(status)
-}
-
-func gofmt() {
-	src, err := ioutil.ReadFile(oflag)
-	if err != nil {
-		return
-	}
-	src, err = format.Source(src)
-	if err != nil {
-		return
-	}
-	ioutil.WriteFile(oflag, src, 0666)
-}
-
-var yaccpar string // will be processed version of yaccpartext: s/$$/prefix/g
-var yaccpartext = `
-/*	parser for yacc output	*/
-
-var (
-	$$Debug        = 0
-	$$ErrorVerbose = false
-)
-
-type $$Lexer interface {
-	Lex(lval *$$SymType) int
-	Error(s string)
-}
-
-type $$Parser interface {
-	Parse($$Lexer) int
-	Lookahead() int
-}
-
-type $$ParserImpl struct {
-	lval  $$SymType
-	stack [$$InitialStackSize]$$SymType
-	char  int
-}
-
-func (p *$$ParserImpl) Lookahead() int {
-	return p.char
-}
-
-func $$NewParser() $$Parser {
-	return &$$ParserImpl{}
-}
-
-const $$Flag = -1000
-
-func $$Tokname(c int) string {
-	if c >= 1 && c-1 < len($$Toknames) {
-		if $$Toknames[c-1] != "" {
-			return $$Toknames[c-1]
-		}
-	}
-	return __yyfmt__.Sprintf("tok-%v", c)
-}
-
-func $$Statname(s int) string {
-	if s >= 0 && s < len($$Statenames) {
-		if $$Statenames[s] != "" {
-			return $$Statenames[s]
-		}
-	}
-	return __yyfmt__.Sprintf("state-%v", s)
-}
-
-func $$ErrorMessage(state, lookAhead int) string {
-	const TOKSTART = 4
-
-	if !$$ErrorVerbose {
-		return "syntax error"
-	}
-
-	for _, e := range $$ErrorMessages {
-		if e.state == state && e.token == lookAhead {
-			return "syntax error: " + e.msg
-		}
-	}
-
-	res := "syntax error: unexpected " + $$Tokname(lookAhead)
-
-	// To match Bison, suggest at most four expected tokens.
-	expected := make([]int, 0, 4)
-
-	// Look for shiftable tokens.
-	base := $$Pact[state]
-	for tok := TOKSTART; tok-1 < len($$Toknames); tok++ {
-		if n := base + tok; n >= 0 && n < $$Last && $$Chk[$$Act[n]] == tok {
-			if len(expected) == cap(expected) {
-				return res
-			}
-			expected = append(expected, tok)
-		}
-	}
-
-	if $$Def[state] == -2 {
-		i := 0
-		for $$Exca[i] != -1 || $$Exca[i+1] != state {
-			i += 2
-		}
-
-		// Look for tokens that we accept or reduce.
-		for i += 2; $$Exca[i] >= 0; i += 2 {
-			tok := $$Exca[i]
-			if tok < TOKSTART || $$Exca[i+1] == 0 {
-				continue
-			}
-			if len(expected) == cap(expected) {
-				return res
-			}
-			expected = append(expected, tok)
-		}
-
-		// If the default action is to accept or reduce, give up.
-		if $$Exca[i+1] != 0 {
-			return res
-		}
-	}
-
-	for i, tok := range expected {
-		if i == 0 {
-			res += ", expecting "
-		} else {
-			res += " or "
-		}
-		res += $$Tokname(tok)
-	}
-	return res
-}
-
-func $$lex1(lex $$Lexer, lval *$$SymType) (char, token int) {
-	token = 0
-	char = lex.Lex(lval)
-	if char <= 0 {
-		token = $$Tok1[0]
-		goto out
-	}
-	if char < len($$Tok1) {
-		token = $$Tok1[char]
-		goto out
-	}
-	if char >= $$Private {
-		if char < $$Private+len($$Tok2) {
-			token = $$Tok2[char-$$Private]
-			goto out
-		}
-	}
-	for i := 0; i < len($$Tok3); i += 2 {
-		token = $$Tok3[i+0]
-		if token == char {
-			token = $$Tok3[i+1]
-			goto out
-		}
-	}
-
-out:
-	if token == 0 {
-		token = $$Tok2[1] /* unknown char */
-	}
-	if $$Debug >= 3 {
-		__yyfmt__.Printf("lex %s(%d)\n", $$Tokname(token), uint(char))
-	}
-	return char, token
-}
-
-func $$Parse($$lex $$Lexer) int {
-	return $$NewParser().Parse($$lex)
-}
-
-func ($$rcvr *$$ParserImpl) Parse($$lex $$Lexer) int {
-	var $$n int
-	var $$VAL $$SymType
-	var $$Dollar []$$SymType
-	_ = $$Dollar // silence set and not used
-	$$S := $$rcvr.stack[:]
-
-	Nerrs := 0   /* number of errors */
-	Errflag := 0 /* error recovery flag */
-	$$state := 0
-	$$rcvr.char = -1
-	$$token := -1 // $$rcvr.char translated into internal numbering
-	defer func() {
-		// Make sure we report no lookahead when not parsing.
-		$$state = -1
-		$$rcvr.char = -1
-		$$token = -1
-	}()
-	$$p := -1
-	goto $$stack
-
-ret0:
-	return 0
-
-ret1:
-	return 1
-
-$$stack:
-	/* put a state and value onto the stack */
-	if $$Debug >= 4 {
-		__yyfmt__.Printf("char %v in %v\n", $$Tokname($$token), $$Statname($$state))
-	}
-
-	$$p++
-	if $$p >= len($$S) {
-		nyys := make([]$$SymType, len($$S)*2)
-		copy(nyys, $$S)
-		$$S = nyys
-	}
-	$$S[$$p] = $$VAL
-	$$S[$$p].yys = $$state
-
-$$newstate:
-	$$n = $$Pact[$$state]
-	if $$n <= $$Flag {
-		goto $$default /* simple state */
-	}
-	if $$rcvr.char < 0 {
-		$$rcvr.char, $$token = $$lex1($$lex, &$$rcvr.lval)
-	}
-	$$n += $$token
-	if $$n < 0 || $$n >= $$Last {
-		goto $$default
-	}
-	$$n = $$Act[$$n]
-	if $$Chk[$$n] == $$token { /* valid shift */
-		$$rcvr.char = -1
-		$$token = -1
-		$$VAL = $$rcvr.lval
-		$$state = $$n
-		if Errflag > 0 {
-			Errflag--
-		}
-		goto $$stack
-	}
-
-$$default:
-	/* default state action */
-	$$n = $$Def[$$state]
-	if $$n == -2 {
-		if $$rcvr.char < 0 {
-			$$rcvr.char, $$token = $$lex1($$lex, &$$rcvr.lval)
-		}
-
-		/* look through exception table */
-		xi := 0
-		for {
-			if $$Exca[xi+0] == -1 && $$Exca[xi+1] == $$state {
-				break
-			}
-			xi += 2
-		}
-		for xi += 2; ; xi += 2 {
-			$$n = $$Exca[xi+0]
-			if $$n < 0 || $$n == $$token {
-				break
-			}
-		}
-		$$n = $$Exca[xi+1]
-		if $$n < 0 {
-			goto ret0
-		}
-	}
-	if $$n == 0 {
-		/* error ... attempt to resume parsing */
-		switch Errflag {
-		case 0: /* brand new error */
-			$$lex.Error($$ErrorMessage($$state, $$token))
-			Nerrs++
-			if $$Debug >= 1 {
-				__yyfmt__.Printf("%s", $$Statname($$state))
-				__yyfmt__.Printf(" saw %s\n", $$Tokname($$token))
-			}
-			fallthrough
-
-		case 1, 2: /* incompletely recovered error ... try again */
-			Errflag = 3
-
-			/* find a state where "error" is a legal shift action */
-			for $$p >= 0 {
-				$$n = $$Pact[$$S[$$p].yys] + $$ErrCode
-				if $$n >= 0 && $$n < $$Last {
-					$$state = $$Act[$$n] /* simulate a shift of "error" */
-					if $$Chk[$$state] == $$ErrCode {
-						goto $$stack
-					}
-				}
-
-				/* the current p has no shift on "error", pop stack */
-				if $$Debug >= 2 {
-					__yyfmt__.Printf("error recovery pops state %d\n", $$S[$$p].yys)
-				}
-				$$p--
-			}
-			/* there is no state on the stack with an error shift ... abort */
-			goto ret1
-
-		case 3: /* no shift yet; clobber input char */
-			if $$Debug >= 2 {
-				__yyfmt__.Printf("error recovery discards %s\n", $$Tokname($$token))
-			}
-			if $$token == $$EofCode {
-				goto ret1
-			}
-			$$rcvr.char = -1
-			$$token = -1
-			goto $$newstate /* try again in the same state */
-		}
-	}
-
-	/* reduction by production $$n */
-	if $$Debug >= 2 {
-		__yyfmt__.Printf("reduce %v in:\n\t%v\n", $$n, $$Statname($$state))
-	}
-
-	$$nt := $$n
-	$$pt := $$p
-	_ = $$pt // guard against "declared and not used"
-
-	$$p -= $$R2[$$n]
-	// $$p is now the index of $0. Perform the default action. Iff the
-	// reduced production is ε, $1 is possibly out of range.
-	if $$p+1 >= len($$S) {
-		nyys := make([]$$SymType, len($$S)*2)
-		copy(nyys, $$S)
-		$$S = nyys
-	}
-	$$VAL = $$S[$$p+1]
-
-	/* consult goto table to find next state */
-	$$n = $$R1[$$n]
-	$$g := $$Pgo[$$n]
-	$$j := $$g + $$S[$$p].yys + 1
-
-	if $$j >= $$Last {
-		$$state = $$Act[$$g]
-	} else {
-		$$state = $$Act[$$j]
-		if $$Chk[$$state] != -$$n {
-			$$state = $$Act[$$g]
-		}
-	}
-	// dummy call; replaced with literal code
-	$$run()
-	goto $$stack /* stack new state and value */
-}
-`
diff --git a/src/cmp.bash b/src/cmp.bash
index 68086c3..dac9ca0 100644
--- a/src/cmp.bash
+++ b/src/cmp.bash
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-# Copyright 2016 The Go Authors.  All rights reserved.
+# Copyright 2016 The Go Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
diff --git a/src/compress/flate/deflate.go b/src/compress/flate/deflate.go
index 9f53d51..97265b3 100644
--- a/src/compress/flate/deflate.go
+++ b/src/compress/flate/deflate.go
@@ -84,9 +84,10 @@ type compressor struct {
 	bulkHasher func([]byte, []uint32)
 
 	// compression algorithm
-	fill func(*compressor, []byte) int // copy data to window
-	step func(*compressor)             // process window
-	sync bool                          // requesting flush
+	fill      func(*compressor, []byte) int // copy data to window
+	step      func(*compressor)             // process window
+	sync      bool                          // requesting flush
+	bestSpeed *deflateFast                  // Encoder for BestSpeed
 
 	// Input hash chains
 	// hashHead[hashValue] contains the largest inputIndex with the specified hash value
@@ -346,12 +347,13 @@ func (d *compressor) encSpeed() {
 				d.err = d.w.err
 			}
 			d.windowEnd = 0
+			d.bestSpeed.reset()
 			return
 		}
 
 	}
 	// Encode the block.
-	d.tokens = encodeBestSpeed(d.tokens[:0], d.window[:d.windowEnd])
+	d.tokens = d.bestSpeed.encode(d.tokens[:0], d.window[:d.windowEnd])
 
 	// If we removed less than 1/16th, Huffman compress the block.
 	if len(d.tokens) > d.windowEnd-(d.windowEnd>>4) {
@@ -519,10 +521,10 @@ func (d *compressor) fillStore(b []byte) int {
 }
 
 func (d *compressor) store() {
-	if d.windowEnd > 0 {
+	if d.windowEnd > 0 && (d.windowEnd == maxStoreBlockSize || d.sync) {
 		d.err = d.writeStoredBlock(d.window[:d.windowEnd])
+		d.windowEnd = 0
 	}
-	d.windowEnd = 0
 }
 
 // storeHuff compresses and stores the currently added data
@@ -584,6 +586,7 @@ func (d *compressor) init(w io.Writer, level int) (err error) {
 		d.window = make([]byte, maxStoreBlockSize)
 		d.fill = (*compressor).fillStore
 		d.step = (*compressor).encSpeed
+		d.bestSpeed = newDeflateFast()
 		d.tokens = make([]token, maxStoreBlockSize)
 	case level == DefaultCompression:
 		level = 6
@@ -609,6 +612,7 @@ func (d *compressor) reset(w io.Writer) {
 	case BestSpeed:
 		d.windowEnd = 0
 		d.tokens = d.tokens[:0]
+		d.bestSpeed.reset()
 	default:
 		d.chainHead = -1
 		for i := range d.hashHead {
@@ -702,10 +706,12 @@ func (w *Writer) Write(data []byte) (n int, err error) {
 	return w.d.write(data)
 }
 
-// Flush flushes any pending compressed data to the underlying writer.
+// Flush flushes any pending data to the underlying writer.
 // It is useful mainly in compressed network protocols, to ensure that
 // a remote reader has enough data to reconstruct a packet.
 // Flush does not return until the data has been written.
+// Calling Flush when there is no pending data still causes the Writer
+// to emit a sync marker of at least 4 bytes.
 // If the underlying writer returns an error, Flush returns that error.
 //
 // In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
diff --git a/src/compress/flate/deflate_test.go b/src/compress/flate/deflate_test.go
index 3322c40..521a260 100644
--- a/src/compress/flate/deflate_test.go
+++ b/src/compress/flate/deflate_test.go
@@ -342,6 +342,7 @@ func testToFromWithLimit(t *testing.T, input []byte, name string, limit [11]int)
 }
 
 func TestDeflateInflate(t *testing.T) {
+	t.Parallel()
 	for i, h := range deflateInflateTests {
 		testToFromWithLimit(t, h.in, fmt.Sprintf("#%d", i), [11]int{})
 	}
@@ -376,6 +377,7 @@ var deflateInflateStringTests = []deflateInflateStringTest{
 }
 
 func TestDeflateInflateString(t *testing.T) {
+	t.Parallel()
 	if testing.Short() && testenv.Builder() == "" {
 		t.Skip("skipping in short mode")
 	}
@@ -463,6 +465,7 @@ func TestRegression2508(t *testing.T) {
 }
 
 func TestWriterReset(t *testing.T) {
+	t.Parallel()
 	for level := 0; level <= 9; level++ {
 		if testing.Short() && level > 1 {
 			break
@@ -490,6 +493,7 @@ func TestWriterReset(t *testing.T) {
 		w.d.fill, wref.d.fill = nil, nil
 		w.d.step, wref.d.step = nil, nil
 		w.d.bulkHasher, wref.d.bulkHasher = nil, nil
+		w.d.bestSpeed, wref.d.bestSpeed = nil, nil
 		// hashMatch is always overwritten when used.
 		copy(w.d.hashMatch[:], wref.d.hashMatch[:])
 		if len(w.d.tokens) != 0 {
@@ -558,6 +562,7 @@ func testResetOutput(t *testing.T, newWriter func(w io.Writer) (*Writer, error))
 // compressor.encSpeed method (0, 16, 128), as well as near maxStoreBlockSize
 // (65535).
 func TestBestSpeed(t *testing.T) {
+	t.Parallel()
 	abc := make([]byte, 128)
 	for i := range abc {
 		abc[i] = byte(i)
@@ -647,6 +652,7 @@ func (w *failWriter) Write(b []byte) (int, error) {
 }
 
 func TestWriterPersistentError(t *testing.T) {
+	t.Parallel()
 	d, err := ioutil.ReadFile("../testdata/Mark.Twain-Tom.Sawyer.txt")
 	if err != nil {
 		t.Fatalf("ReadFile: %v", err)
@@ -681,3 +687,180 @@ func TestWriterPersistentError(t *testing.T) {
 		}
 	}
 }
+
+func TestBestSpeedMatch(t *testing.T) {
+	t.Parallel()
+	cases := []struct {
+		previous, current []byte
+		t, s, want        int32
+	}{{
+		previous: []byte{0, 0, 0, 1, 2},
+		current:  []byte{3, 4, 5, 0, 1, 2, 3, 4, 5},
+		t:        -3,
+		s:        3,
+		want:     6,
+	}, {
+		previous: []byte{0, 0, 0, 1, 2},
+		current:  []byte{2, 4, 5, 0, 1, 2, 3, 4, 5},
+		t:        -3,
+		s:        3,
+		want:     3,
+	}, {
+		previous: []byte{0, 0, 0, 1, 1},
+		current:  []byte{3, 4, 5, 0, 1, 2, 3, 4, 5},
+		t:        -3,
+		s:        3,
+		want:     2,
+	}, {
+		previous: []byte{0, 0, 0, 1, 2},
+		current:  []byte{2, 2, 2, 2, 1, 2, 3, 4, 5},
+		t:        -1,
+		s:        0,
+		want:     4,
+	}, {
+		previous: []byte{0, 0, 0, 1, 2, 3, 4, 5, 2, 2},
+		current:  []byte{2, 2, 2, 2, 1, 2, 3, 4, 5},
+		t:        -7,
+		s:        4,
+		want:     5,
+	}, {
+		previous: []byte{9, 9, 9, 9, 9},
+		current:  []byte{2, 2, 2, 2, 1, 2, 3, 4, 5},
+		t:        -1,
+		s:        0,
+		want:     0,
+	}, {
+		previous: []byte{9, 9, 9, 9, 9},
+		current:  []byte{9, 2, 2, 2, 1, 2, 3, 4, 5},
+		t:        0,
+		s:        1,
+		want:     0,
+	}, {
+		previous: []byte{},
+		current:  []byte{9, 2, 2, 2, 1, 2, 3, 4, 5},
+		t:        -5,
+		s:        1,
+		want:     0,
+	}, {
+		previous: []byte{},
+		current:  []byte{9, 2, 2, 2, 1, 2, 3, 4, 5},
+		t:        -1,
+		s:        1,
+		want:     0,
+	}, {
+		previous: []byte{},
+		current:  []byte{2, 2, 2, 2, 1, 2, 3, 4, 5},
+		t:        0,
+		s:        1,
+		want:     3,
+	}, {
+		previous: []byte{3, 4, 5},
+		current:  []byte{3, 4, 5},
+		t:        -3,
+		s:        0,
+		want:     3,
+	}, {
+		previous: make([]byte, 1000),
+		current:  make([]byte, 1000),
+		t:        -1000,
+		s:        0,
+		want:     maxMatchLength - 4,
+	}, {
+		previous: make([]byte, 200),
+		current:  make([]byte, 500),
+		t:        -200,
+		s:        0,
+		want:     maxMatchLength - 4,
+	}, {
+		previous: make([]byte, 200),
+		current:  make([]byte, 500),
+		t:        0,
+		s:        1,
+		want:     maxMatchLength - 4,
+	}, {
+		previous: make([]byte, maxMatchLength-4),
+		current:  make([]byte, 500),
+		t:        -(maxMatchLength - 4),
+		s:        0,
+		want:     maxMatchLength - 4,
+	}, {
+		previous: make([]byte, 200),
+		current:  make([]byte, 500),
+		t:        -200,
+		s:        400,
+		want:     100,
+	}, {
+		previous: make([]byte, 10),
+		current:  make([]byte, 500),
+		t:        200,
+		s:        400,
+		want:     100,
+	}}
+	for i, c := range cases {
+		e := deflateFast{prev: c.previous}
+		got := e.matchLen(c.s, c.t, c.current)
+		if got != c.want {
+			t.Errorf("Test %d: match length, want %d, got %d", i, c.want, got)
+		}
+	}
+}
+
+func TestBestSpeedMaxMatchOffset(t *testing.T) {
+	t.Parallel()
+	const abc, xyz = "abcdefgh", "stuvwxyz"
+	for _, matchBefore := range []bool{false, true} {
+		for _, extra := range []int{0, inputMargin - 1, inputMargin, inputMargin + 1, 2 * inputMargin} {
+			for offsetAdj := -5; offsetAdj <= +5; offsetAdj++ {
+				report := func(desc string, err error) {
+					t.Errorf("matchBefore=%t, extra=%d, offsetAdj=%d: %s%v",
+						matchBefore, extra, offsetAdj, desc, err)
+				}
+
+				offset := maxMatchOffset + offsetAdj
+
+				// Make src to be a []byte of the form
+				//	"%s%s%s%s%s" % (abc, zeros0, xyzMaybe, abc, zeros1)
+				// where:
+				//	zeros0 is approximately maxMatchOffset zeros.
+				//	xyzMaybe is either xyz or the empty string.
+				//	zeros1 is between 0 and 30 zeros.
+				// The difference between the two abc's will be offset, which
+				// is maxMatchOffset plus or minus a small adjustment.
+				src := make([]byte, offset+len(abc)+extra)
+				copy(src, abc)
+				if !matchBefore {
+					copy(src[offset-len(xyz):], xyz)
+				}
+				copy(src[offset:], abc)
+
+				buf := new(bytes.Buffer)
+				w, err := NewWriter(buf, BestSpeed)
+				if err != nil {
+					report("NewWriter: ", err)
+					continue
+				}
+				if _, err := w.Write(src); err != nil {
+					report("Write: ", err)
+					continue
+				}
+				if err := w.Close(); err != nil {
+					report("Writer.Close: ", err)
+					continue
+				}
+
+				r := NewReader(buf)
+				dst, err := ioutil.ReadAll(r)
+				r.Close()
+				if err != nil {
+					report("ReadAll: ", err)
+					continue
+				}
+
+				if !bytes.Equal(dst, src) {
+					report("", fmt.Errorf("bytes differ after round-tripping"))
+					continue
+				}
+			}
+		}
+	}
+}
diff --git a/src/compress/flate/deflatefast.go b/src/compress/flate/deflatefast.go
index 6b881a4..a1636a3 100644
--- a/src/compress/flate/deflatefast.go
+++ b/src/compress/flate/deflatefast.go
@@ -14,12 +14,12 @@ const (
 	tableShift = 32 - tableBits // Right-shift to get the tableBits most significant bits of a uint32.
 )
 
-func load32(b []byte, i int) uint32 {
+func load32(b []byte, i int32) uint32 {
 	b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line.
 	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
 }
 
-func load64(b []byte, i int) uint64 {
+func load64(b []byte, i int32) uint64 {
 	b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line.
 	return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
 		uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
@@ -38,31 +38,49 @@ const (
 	minNonLiteralBlockSize = 1 + 1 + inputMargin
 )
 
-func encodeBestSpeed(dst []token, src []byte) []token {
+type tableEntry struct {
+	val    uint32 // Value at destination
+	offset int32
+}
+
+// deflateFast maintains the table for matches,
+// and the previous byte block for cross block matching.
+type deflateFast struct {
+	table [tableSize]tableEntry
+	prev  []byte // Previous block, zero length if unknown.
+	cur   int32  // Current match offset.
+}
+
+func newDeflateFast() *deflateFast {
+	return &deflateFast{cur: maxStoreBlockSize, prev: make([]byte, 0, maxStoreBlockSize)}
+}
+
+// encode encodes a block given in src and appends tokens
+// to dst and returns the result.
+func (e *deflateFast) encode(dst []token, src []byte) []token {
+	// Ensure that e.cur doesn't wrap.
+	if e.cur > 1<<30 {
+		*e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]}
+	}
+
 	// This check isn't in the Snappy implementation, but there, the caller
 	// instead of the callee handles this case.
 	if len(src) < minNonLiteralBlockSize {
+		e.cur += maxStoreBlockSize
+		e.prev = e.prev[:0]
 		return emitLiteral(dst, src)
 	}
 
-	// Initialize the hash table.
-	//
-	// The table element type is uint16, as s < sLimit and sLimit < len(src)
-	// and len(src) <= maxStoreBlockSize and maxStoreBlockSize == 65535.
-	var table [tableSize]uint16
-
 	// sLimit is when to stop looking for offset/length copies. The inputMargin
 	// lets us use a fast path for emitLiteral in the main loop, while we are
 	// looking for copies.
-	sLimit := len(src) - inputMargin
+	sLimit := int32(len(src) - inputMargin)
 
 	// nextEmit is where in src the next emitLiteral should start from.
-	nextEmit := 0
-
-	// The encoded form must start with a literal, as there are no previous
-	// bytes to copy, so we start looking for hash matches at s == 1.
-	s := 1
-	nextHash := hash(load32(src, s))
+	nextEmit := int32(0)
+	s := int32(0)
+	cv := load32(src, s)
+	nextHash := hash(cv)
 
 	for {
 		// Copied from the C++ snappy implementation:
@@ -80,10 +98,10 @@ func encodeBestSpeed(dst []token, src []byte) []token {
 		// The "skip" variable keeps track of how many bytes there are since
 		// the last match; dividing it by 32 (ie. right-shifting by five) gives
 		// the number of bytes to move ahead for each iteration.
-		skip := 32
+		skip := int32(32)
 
 		nextS := s
-		candidate := 0
+		var candidate tableEntry
 		for {
 			s = nextS
 			bytesBetweenHashLookups := skip >> 5
@@ -92,13 +110,18 @@ func encodeBestSpeed(dst []token, src []byte) []token {
 			if nextS > sLimit {
 				goto emitRemainder
 			}
-			candidate = int(table[nextHash&tableMask])
-			table[nextHash&tableMask] = uint16(s)
-			nextHash = hash(load32(src, nextS))
-			// TODO: < should be <=, and add a test for that.
-			if s-candidate < maxMatchOffset && load32(src, s) == load32(src, candidate) {
-				break
+			candidate = e.table[nextHash&tableMask]
+			now := load32(src, nextS)
+			e.table[nextHash&tableMask] = tableEntry{offset: s + e.cur, val: cv}
+			nextHash = hash(now)
+
+			offset := s - (candidate.offset - e.cur)
+			if offset > maxMatchOffset || cv != candidate.val {
+				// Out of range or not matched.
+				cv = now
+				continue
 			}
+			break
 		}
 
 		// A 4-byte match has been found. We'll later see if more than 4 bytes
@@ -117,22 +140,16 @@ func encodeBestSpeed(dst []token, src []byte) []token {
 		for {
 			// Invariant: we have a 4-byte match at s, and no need to emit any
 			// literal bytes prior to s.
-			base := s
 
 			// Extend the 4-byte match as long as possible.
 			//
-			// This is an inlined version of Snappy's:
-			//	s = extendMatch(src, candidate+4, s+4)
 			s += 4
-			s1 := base + maxMatchLength
-			if s1 > len(src) {
-				s1 = len(src)
-			}
-			for i := candidate + 4; s < s1 && src[i] == src[s]; i, s = i+1, s+1 {
-			}
+			t := candidate.offset - e.cur + 4
+			l := e.matchLen(s, t, src)
 
-			// matchToken is flate's equivalent of Snappy's emitCopy.
-			dst = append(dst, matchToken(uint32(s-base-baseMatchLength), uint32(base-candidate-baseMatchOffset)))
+			// matchToken is flate's equivalent of Snappy's emitCopy. (length,offset)
+			dst = append(dst, matchToken(uint32(l+4-baseMatchLength), uint32(s-t-baseMatchOffset)))
+			s += l
 			nextEmit = s
 			if s >= sLimit {
 				goto emitRemainder
@@ -145,14 +162,17 @@ func encodeBestSpeed(dst []token, src []byte) []token {
 			// are faster as one load64 call (with some shifts) instead of
 			// three load32 calls.
 			x := load64(src, s-1)
-			prevHash := hash(uint32(x >> 0))
-			table[prevHash&tableMask] = uint16(s - 1)
-			currHash := hash(uint32(x >> 8))
-			candidate = int(table[currHash&tableMask])
-			table[currHash&tableMask] = uint16(s)
-			// TODO: >= should be >, and add a test for that.
-			if s-candidate >= maxMatchOffset || uint32(x>>8) != load32(src, candidate) {
-				nextHash = hash(uint32(x >> 16))
+			prevHash := hash(uint32(x))
+			e.table[prevHash&tableMask] = tableEntry{offset: e.cur + s - 1, val: uint32(x)}
+			x >>= 8
+			currHash := hash(uint32(x))
+			candidate = e.table[currHash&tableMask]
+			e.table[currHash&tableMask] = tableEntry{offset: e.cur + s, val: uint32(x)}
+
+			offset := s - (candidate.offset - e.cur)
+			if offset > maxMatchOffset || uint32(x) != candidate.val {
+				cv = uint32(x >> 8)
+				nextHash = hash(cv)
 				s++
 				break
 			}
@@ -160,15 +180,91 @@ func encodeBestSpeed(dst []token, src []byte) []token {
 	}
 
 emitRemainder:
-	if nextEmit < len(src) {
+	if int(nextEmit) < len(src) {
 		dst = emitLiteral(dst, src[nextEmit:])
 	}
+	e.cur += int32(len(src))
+	e.prev = e.prev[:len(src)]
+	copy(e.prev, src)
 	return dst
 }
 
 func emitLiteral(dst []token, lit []byte) []token {
 	for _, v := range lit {
-		dst = append(dst, token(v))
+		dst = append(dst, literalToken(uint32(v)))
 	}
 	return dst
 }
+
+// matchLen returns the match length between src[s:] and src[t:].
+// t can be negative to indicate the match is starting in e.prev.
+// We assume that src[s-4:s] and src[t-4:t] already match.
+func (e *deflateFast) matchLen(s, t int32, src []byte) int32 {
+	s1 := int(s) + maxMatchLength - 4
+	if s1 > len(src) {
+		s1 = len(src)
+	}
+
+	// If we are inside the current block
+	if t >= 0 {
+		b := src[t:]
+		a := src[s:s1]
+		b = b[:len(a)]
+		// Extend the match to be as long as possible.
+		for i := range a {
+			if a[i] != b[i] {
+				return int32(i)
+			}
+		}
+		return int32(len(a))
+	}
+
+	// We found a match in the previous block.
+	tp := int32(len(e.prev)) + t
+	if tp < 0 {
+		return 0
+	}
+
+	// Extend the match to be as long as possible.
+	a := src[s:s1]
+	b := e.prev[tp:]
+	if len(b) > len(a) {
+		b = b[:len(a)]
+	}
+	a = a[:len(b)]
+	for i := range b {
+		if a[i] != b[i] {
+			return int32(i)
+		}
+	}
+
+	// If we reached our limit, we matched everything we are
+	// allowed to in the previous block and we return.
+	n := int32(len(b))
+	if int(s+n) == s1 {
+		return n
+	}
+
+	// Continue looking for more matches in the current block.
+	a = src[s+n : s1]
+	b = src[:len(a)]
+	for i := range a {
+		if a[i] != b[i] {
+			return int32(i) + n
+		}
+	}
+	return int32(len(a)) + n
+}
+
+// Reset resets the encoding history.
+// This ensures that no matches are made to the previous block.
+func (e *deflateFast) reset() {
+	e.prev = e.prev[:0]
+	// Bump the offset, so all matches will fail distance check.
+	e.cur += maxMatchOffset
+
+	// Protect against e.cur wraparound.
+	if e.cur > 1<<30 {
+		*e = deflateFast{cur: maxStoreBlockSize, prev: e.prev[:0]}
+	}
+}
diff --git a/src/compress/flate/example_test.go b/src/compress/flate/example_test.go
new file mode 100644
index 0000000..5780092
--- /dev/null
+++ b/src/compress/flate/example_test.go
@@ -0,0 +1,243 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flate_test
+
+import (
+	"bytes"
+	"compress/flate"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"strings"
+	"sync"
+)
+
+// In performance critical applications, Reset can be used to discard the
+// current compressor or decompressor state and reinitialize them quickly
+// by taking advantage of previously allocated memory.
+func Example_reset() {
+	proverbs := []string{
+		"Don't communicate by sharing memory, share memory by communicating.\n",
+		"Concurrency is not parallelism.\n",
+		"The bigger the interface, the weaker the abstraction.\n",
+		"Documentation is for users.\n",
+	}
+
+	var r strings.Reader
+	var b bytes.Buffer
+	buf := make([]byte, 32<<10)
+
+	zw, err := flate.NewWriter(nil, flate.DefaultCompression)
+	if err != nil {
+		log.Fatal(err)
+	}
+	zr := flate.NewReader(nil)
+
+	for _, s := range proverbs {
+		r.Reset(s)
+		b.Reset()
+
+		// Reset the compressor and encode from some input stream.
+		zw.Reset(&b)
+		if _, err := io.CopyBuffer(zw, &r, buf); err != nil {
+			log.Fatal(err)
+		}
+		if err := zw.Close(); err != nil {
+			log.Fatal(err)
+		}
+
+		// Reset the decompressor and decode to some output stream.
+		if err := zr.(flate.Resetter).Reset(&b, nil); err != nil {
+			log.Fatal(err)
+		}
+		if _, err := io.CopyBuffer(os.Stdout, zr, buf); err != nil {
+			log.Fatal(err)
+		}
+		if err := zr.Close(); err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	// Output:
+	// Don't communicate by sharing memory, share memory by communicating.
+	// Concurrency is not parallelism.
+	// The bigger the interface, the weaker the abstraction.
+	// Documentation is for users.
+}
+
+// A preset dictionary can be used to improve the compression ratio.
+// The downside to using a dictionary is that the compressor and decompressor
+// must agree in advance what dictionary to use.
+func Example_dictionary() {
+	// The dictionary is a string of bytes. When compressing some input data,
+	// the compressor will attempt to substitute substrings with matches found
+	// in the dictionary. As such, the dictionary should only contain substrings
+	// that are expected to be found in the actual data stream.
+	const dict = `<?xml version="1.0"?>` + `<book>` + `<data>` + `<meta name="` + `" content="`
+
+	// The data to compress should (but is not required to) contain frequent
+	// substrings that match those in the dictionary.
+	const data = `<?xml version="1.0"?>
+<book>
+	<meta name="title" content="The Go Programming Language"/>
+	<meta name="authors" content="Alan Donovan and Brian Kernighan"/>
+	<meta name="published" content="2015-10-26"/>
+	<meta name="isbn" content="978-0134190440"/>
+	<data>...</data>
+</book>
+`
+
+	var b bytes.Buffer
+
+	// Compress the data using the specially crafted dictionary.
+	zw, err := flate.NewWriterDict(&b, flate.DefaultCompression, []byte(dict))
+	if err != nil {
+		log.Fatal(err)
+	}
+	if _, err := io.Copy(zw, strings.NewReader(data)); err != nil {
+		log.Fatal(err)
+	}
+	if err := zw.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	// The decompressor must use the same dictionary as the compressor.
+	// Otherwise, the input may appear as corrupted.
+	fmt.Println("Decompressed output using the dictionary:")
+	zr := flate.NewReaderDict(bytes.NewReader(b.Bytes()), []byte(dict))
+	if _, err := io.Copy(os.Stdout, zr); err != nil {
+		log.Fatal(err)
+	}
+	if err := zr.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Println()
+
+	// Substitute all of the bytes in the dictionary with a '#' to visually
+	// demonstrate the approximate effectiveness of using a preset dictionary.
+	fmt.Println("Substrings matched by the dictionary are marked with #:")
+	hashDict := []byte(dict)
+	for i := range hashDict {
+		hashDict[i] = '#'
+	}
+	zr = flate.NewReaderDict(&b, hashDict)
+	if _, err := io.Copy(os.Stdout, zr); err != nil {
+		log.Fatal(err)
+	}
+	if err := zr.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	// Output:
+	// Decompressed output using the dictionary:
+	// <?xml version="1.0"?>
+	// <book>
+	// 	<meta name="title" content="The Go Programming Language"/>
+	// 	<meta name="authors" content="Alan Donovan and Brian Kernighan"/>
+	// 	<meta name="published" content="2015-10-26"/>
+	// 	<meta name="isbn" content="978-0134190440"/>
+	// 	<data>...</data>
+	// </book>
+	//
+	// Substrings matched by the dictionary are marked with #:
+	// #####################
+	// ######
+	// 	############title###########The Go Programming Language"/#
+	// 	############authors###########Alan Donovan and Brian Kernighan"/#
+	// 	############published###########2015-10-26"/#
+	// 	############isbn###########978-0134190440"/#
+	// 	######...</#####
+	// </#####
+}
+
+// DEFLATE is suitable for transmitting compressed data across the network.
+func Example_synchronization() {
+	var wg sync.WaitGroup
+	defer wg.Wait()
+
+	// Use io.Pipe to simulate a network connection.
+	// A real network application should take care to properly close the
+	// underlying connection.
+	rp, wp := io.Pipe()
+
+	// Start a goroutine to act as the transmitter.
+	wg.Add(1)
+	go func() {
+		defer wg.Done()
+
+		zw, err := flate.NewWriter(wp, flate.BestSpeed)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		b := make([]byte, 256)
+		for _, m := range strings.Fields("A long time ago in a galaxy far, far away...") {
+			// We use a simple framing format where the first byte is the
+			// message length, followed the message itself.
+			b[0] = uint8(copy(b[1:], m))
+
+			if _, err := zw.Write(b[:1+len(m)]); err != nil {
+				log.Fatal(err)
+			}
+
+			// Flush ensures that the receiver can read all data sent so far.
+			if err := zw.Flush(); err != nil {
+				log.Fatal(err)
+			}
+		}
+
+		if err := zw.Close(); err != nil {
+			log.Fatal(err)
+		}
+	}()
+
+	// Start a goroutine to act as the receiver.
+	wg.Add(1)
+	go func() {
+		defer wg.Done()
+
+		zr := flate.NewReader(rp)
+
+		b := make([]byte, 256)
+		for {
+			// Read the message length.
+			// This is guaranteed to return for every corresponding
+			// Flush and Close on the transmitter side.
+			if _, err := io.ReadFull(zr, b[:1]); err != nil {
+				if err == io.EOF {
+					break // The transmitter closed the stream
+				}
+				log.Fatal(err)
+			}
+
+			// Read the message content.
+			n := int(b[0])
+			if _, err := io.ReadFull(zr, b[:n]); err != nil {
+				log.Fatal(err)
+			}
+
+			fmt.Printf("Received %d bytes: %s\n", n, b[:n])
+		}
+		fmt.Println()
+
+		if err := zr.Close(); err != nil {
+			log.Fatal(err)
+		}
+	}()
+
+	// Output:
+	// Received 1 bytes: A
+	// Received 4 bytes: long
+	// Received 4 bytes: time
+	// Received 3 bytes: ago
+	// Received 2 bytes: in
+	// Received 1 bytes: a
+	// Received 6 bytes: galaxy
+	// Received 4 bytes: far,
+	// Received 3 bytes: far
+	// Received 7 bytes: away...
+}
diff --git a/src/compress/flate/flate_test.go b/src/compress/flate/flate_test.go
index 83c2049..1e45077 100644
--- a/src/compress/flate/flate_test.go
+++ b/src/compress/flate/flate_test.go
@@ -281,6 +281,7 @@ func TestTruncatedStreams(t *testing.T) {
 //
 // See https://github.com/google/go-github/pull/317 for background.
 func TestReaderEarlyEOF(t *testing.T) {
+	t.Parallel()
 	testSizes := []int{
 		1, 2, 3, 4, 5, 6, 7, 8,
 		100, 1000, 10000, 100000,
diff --git a/src/compress/flate/huffman_bit_writer.go b/src/compress/flate/huffman_bit_writer.go
index d8b5a3e..6cd6281 100644
--- a/src/compress/flate/huffman_bit_writer.go
+++ b/src/compress/flate/huffman_bit_writer.go
@@ -520,7 +520,7 @@ func (w *huffmanBitWriter) writeBlockDynamic(tokens []token, eof bool, input []b
 	// the literalEncoding and the offsetEncoding.
 	w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding)
 	w.codegenEncoding.generate(w.codegenFreq[:], 7)
-	size, numCodegens := w.dynamicSize(w.literalEncoding, huffOffset, 0)
+	size, numCodegens := w.dynamicSize(w.literalEncoding, w.offsetEncoding, 0)
 
 	// Store bytes, if we don't get a reasonable improvement.
 	if ssize, storable := w.storedSize(input); storable && ssize < (size+size>>4) {
diff --git a/src/compress/flate/inflate.go b/src/compress/flate/inflate.go
index 68cc232..9a8c4fc 100644
--- a/src/compress/flate/inflate.go
+++ b/src/compress/flate/inflate.go
@@ -344,6 +344,9 @@ func (f *decompressor) Read(b []byte) (int, error) {
 			return 0, f.err
 		}
 		f.step(f)
+		if f.err != nil && len(f.toRead) == 0 {
+			f.toRead = f.dict.readFlush() // Flush what's left in case of error
+		}
 	}
 }
 
diff --git a/src/compress/flate/inflate_test.go b/src/compress/flate/inflate_test.go
index e0bce71..951decd 100644
--- a/src/compress/flate/inflate_test.go
+++ b/src/compress/flate/inflate_test.go
@@ -7,6 +7,8 @@ package flate
 import (
 	"bytes"
 	"io"
+	"io/ioutil"
+	"strings"
 	"testing"
 )
 
@@ -38,6 +40,33 @@ func TestReset(t *testing.T) {
 	}
 }
 
+func TestReaderTruncated(t *testing.T) {
+	vectors := []struct{ input, output string }{
+		{"\x00", ""},
+		{"\x00\f", ""},
+		{"\x00\f\x00", ""},
+		{"\x00\f\x00\xf3\xff", ""},
+		{"\x00\f\x00\xf3\xffhello", "hello"},
+		{"\x00\f\x00\xf3\xffhello, world", "hello, world"},
+		{"\x02", ""},
+		{"\xf2H\xcd", "He"},
+		{"\xf2H͙0a\u0084\t", "Hel\x90\x90\x90\x90\x90"},
+		{"\xf2H͙0a\u0084\t\x00", "Hel\x90\x90\x90\x90\x90"},
+	}
+
+	for i, v := range vectors {
+		r := strings.NewReader(v.input)
+		zr := NewReader(r)
+		b, err := ioutil.ReadAll(zr)
+		if err != io.ErrUnexpectedEOF {
+			t.Errorf("test %d, error mismatch: got %v, want io.ErrUnexpectedEOF", i, err)
+		}
+		if string(b) != v.output {
+			t.Errorf("test %d, output mismatch: got %q, want %q", i, b, v.output)
+		}
+	}
+}
+
 func TestResetDict(t *testing.T) {
 	dict := []byte("the lorem fox")
 	ss := []string{
diff --git a/src/compress/flate/writer_test.go b/src/compress/flate/writer_test.go
index 21cd0b2..c4d36aa 100644
--- a/src/compress/flate/writer_test.go
+++ b/src/compress/flate/writer_test.go
@@ -56,6 +56,7 @@ func (e *errorWriter) Write(b []byte) (int, error) {
 
 // Test if errors from the underlying writer is passed upwards.
 func TestWriteError(t *testing.T) {
+	t.Parallel()
 	buf := new(bytes.Buffer)
 	n := 65536
 	if !testing.Short() {
@@ -75,7 +76,7 @@ func TestWriteError(t *testing.T) {
 			if err != nil {
 				t.Fatalf("NewWriter: level %d: %v", l, err)
 			}
-			n, err := io.CopyBuffer(w, bytes.NewBuffer(in), copyBuffer)
+			n, err := io.CopyBuffer(w, struct{ io.Reader }{bytes.NewBuffer(in)}, copyBuffer)
 			if err == nil {
 				t.Fatalf("Level %d: Expected an error, writer was %#v", l, ew)
 			}
@@ -113,6 +114,7 @@ func TestWriteError(t *testing.T) {
 // Test if two runs produce identical results
 // even when writing different sizes to the Writer.
 func TestDeterministic(t *testing.T) {
+	t.Parallel()
 	for i := 0; i <= 9; i++ {
 		t.Run(fmt.Sprint("L", i), func(t *testing.T) { testDeterministic(i, t) })
 	}
@@ -120,6 +122,7 @@ func TestDeterministic(t *testing.T) {
 }
 
 func testDeterministic(i int, t *testing.T) {
+	t.Parallel()
 	// Test so much we cross a good number of block boundaries.
 	var length = maxStoreBlockSize*30 + 500
 	if testing.Short() {
@@ -142,7 +145,7 @@ func testDeterministic(i int, t *testing.T) {
 	}
 	// Use a very small prime sized buffer.
 	cbuf := make([]byte, 787)
-	_, err = io.CopyBuffer(w, br, cbuf)
+	_, err = io.CopyBuffer(w, struct{ io.Reader }{br}, cbuf)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -157,7 +160,7 @@ func testDeterministic(i int, t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	_, err = io.CopyBuffer(w2, br2, cbuf)
+	_, err = io.CopyBuffer(w2, struct{ io.Reader }{br2}, cbuf)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/src/compress/gzip/example_test.go b/src/compress/gzip/example_test.go
new file mode 100644
index 0000000..ce29e9b
--- /dev/null
+++ b/src/compress/gzip/example_test.go
@@ -0,0 +1,128 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gzip_test
+
+import (
+	"bytes"
+	"compress/gzip"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"time"
+)
+
+func Example_writerReader() {
+	var buf bytes.Buffer
+	zw := gzip.NewWriter(&buf)
+
+	// Setting the Header fields is optional.
+	zw.Name = "a-new-hope.txt"
+	zw.Comment = "an epic space opera by George Lucas"
+	zw.ModTime = time.Date(1977, time.May, 25, 0, 0, 0, 0, time.UTC)
+
+	_, err := zw.Write([]byte("A long time ago in a galaxy far, far away..."))
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if err := zw.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	zr, err := gzip.NewReader(&buf)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("Name: %s\nComment: %s\nModTime: %s\n\n", zr.Name, zr.Comment, zr.ModTime.UTC())
+
+	if _, err := io.Copy(os.Stdout, zr); err != nil {
+		log.Fatal(err)
+	}
+
+	if err := zr.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	// Output:
+	// Name: a-new-hope.txt
+	// Comment: an epic space opera by George Lucas
+	// ModTime: 1977-05-25 00:00:00 +0000 UTC
+	//
+	// A long time ago in a galaxy far, far away...
+}
+
+func ExampleReader_Multistream() {
+	var buf bytes.Buffer
+	zw := gzip.NewWriter(&buf)
+
+	var files = []struct {
+		name    string
+		comment string
+		modTime time.Time
+		data    string
+	}{
+		{"file-1.txt", "file-header-1", time.Date(2006, time.February, 1, 3, 4, 5, 0, time.UTC), "Hello Gophers - 1"},
+		{"file-2.txt", "file-header-2", time.Date(2007, time.March, 2, 4, 5, 6, 1, time.UTC), "Hello Gophers - 2"},
+	}
+
+	for _, file := range files {
+		zw.Name = file.name
+		zw.Comment = file.comment
+		zw.ModTime = file.modTime
+
+		if _, err := zw.Write([]byte(file.data)); err != nil {
+			log.Fatal(err)
+		}
+
+		if err := zw.Close(); err != nil {
+			log.Fatal(err)
+		}
+
+		zw.Reset(&buf)
+	}
+
+	zr, err := gzip.NewReader(&buf)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	for {
+		zr.Multistream(false)
+		fmt.Printf("Name: %s\nComment: %s\nModTime: %s\n\n", zr.Name, zr.Comment, zr.ModTime.UTC())
+
+		if _, err := io.Copy(os.Stdout, zr); err != nil {
+			log.Fatal(err)
+		}
+
+		fmt.Print("\n\n")
+
+		err = zr.Reset(&buf)
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			log.Fatal(err)
+		}
+	}
+
+	if err := zr.Close(); err != nil {
+		log.Fatal(err)
+	}
+
+	// Output:
+	// Name: file-1.txt
+	// Comment: file-header-1
+	// ModTime: 2006-02-01 03:04:05 +0000 UTC
+	//
+	// Hello Gophers - 1
+	//
+	// Name: file-2.txt
+	// Comment: file-header-2
+	// ModTime: 2007-03-02 04:05:06 +0000 UTC
+	//
+	// Hello Gophers - 2
+}
diff --git a/src/compress/gzip/gunzip.go b/src/compress/gzip/gunzip.go
index 7e64069..8bd750b 100644
--- a/src/compress/gzip/gunzip.go
+++ b/src/compress/gzip/gunzip.go
@@ -186,7 +186,11 @@ func (z *Reader) readHeader() (hdr Header, err error) {
 		return hdr, ErrHeader
 	}
 	flg := z.buf[3]
-	hdr.ModTime = time.Unix(int64(le.Uint32(z.buf[4:8])), 0)
+	if t := int64(le.Uint32(z.buf[4:8])); t > 0 {
+		// Section 2.3.1, the zero value for MTIME means that the
+		// modified time is not set.
+		hdr.ModTime = time.Unix(t, 0)
+	}
 	// z.buf[8] is XFL and is currently ignored.
 	hdr.OS = z.buf[9]
 	z.digest = crc32.ChecksumIEEE(z.buf[:10])
@@ -238,6 +242,7 @@ func (z *Reader) readHeader() (hdr Header, err error) {
 	return hdr, nil
 }
 
+// Read implements io.Reader, reading uncompressed bytes from its underlying Reader.
 func (z *Reader) Read(p []byte) (n int, err error) {
 	if z.err != nil {
 		return 0, z.err
diff --git a/src/compress/gzip/gunzip_test.go b/src/compress/gzip/gunzip_test.go
index fdce919..fdea0c5 100644
--- a/src/compress/gzip/gunzip_test.go
+++ b/src/compress/gzip/gunzip_test.go
@@ -339,6 +339,26 @@ var gunzipTests = []gunzipTest{
 		},
 		nil,
 	},
+	{
+		"",
+		"truncated gzip file amid raw-block",
+		"hello",
+		[]byte{
+			0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+			0x00, 0x0c, 0x00, 0xf3, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
+		},
+		io.ErrUnexpectedEOF,
+	},
+	{
+		"",
+		"truncated gzip file amid fixed-block",
+		"He",
+		[]byte{
+			0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+			0xf2, 0x48, 0xcd,
+		},
+		io.ErrUnexpectedEOF,
+	},
 }
 
 func TestDecompressor(t *testing.T) {
diff --git a/src/compress/gzip/gzip.go b/src/compress/gzip/gzip.go
index c702c49..aafb442 100644
--- a/src/compress/gzip/gzip.go
+++ b/src/compress/gzip/gzip.go
@@ -10,6 +10,7 @@ import (
 	"fmt"
 	"hash/crc32"
 	"io"
+	"time"
 )
 
 // These constants are copied from the flate package, so that code that imports
@@ -19,6 +20,7 @@ const (
 	BestSpeed          = flate.BestSpeed
 	BestCompression    = flate.BestCompression
 	DefaultCompression = flate.DefaultCompression
+	HuffmanOnly        = flate.HuffmanOnly
 )
 
 // A Writer is an io.WriteCloser.
@@ -52,11 +54,11 @@ func NewWriter(w io.Writer) *Writer {
 // NewWriterLevel is like NewWriter but specifies the compression level instead
 // of assuming DefaultCompression.
 //
-// The compression level can be DefaultCompression, NoCompression, or any
-// integer value between BestSpeed and BestCompression inclusive. The error
-// returned will be nil if the level is valid.
+// The compression level can be DefaultCompression, NoCompression, HuffmanOnly
+// or any integer value between BestSpeed and BestCompression inclusive.
+// The error returned will be nil if the level is valid.
 func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
-	if level < DefaultCompression || level > BestCompression {
+	if level < HuffmanOnly || level > BestCompression {
 		return nil, fmt.Errorf("gzip: invalid compression level: %d", level)
 	}
 	z := new(Writer)
@@ -142,10 +144,7 @@ func (z *Writer) Write(p []byte) (int, error) {
 	// Write the GZIP header lazily.
 	if !z.wroteHeader {
 		z.wroteHeader = true
-		z.buf[0] = gzipID1
-		z.buf[1] = gzipID2
-		z.buf[2] = gzipDeflate
-		z.buf[3] = 0
+		z.buf = [10]byte{0: gzipID1, 1: gzipID2, 2: gzipDeflate}
 		if z.Extra != nil {
 			z.buf[3] |= 0x04
 		}
@@ -155,13 +154,15 @@ func (z *Writer) Write(p []byte) (int, error) {
 		if z.Comment != "" {
 			z.buf[3] |= 0x10
 		}
-		le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix()))
+		if z.ModTime.After(time.Unix(0, 0)) {
+			// Section 2.3.1, the zero value for MTIME means that the
+			// modified time is not set.
+			le.PutUint32(z.buf[4:8], uint32(z.ModTime.Unix()))
+		}
 		if z.level == BestCompression {
 			z.buf[8] = 2
 		} else if z.level == BestSpeed {
 			z.buf[8] = 4
-		} else {
-			z.buf[8] = 0
 		}
 		z.buf[9] = z.OS
 		n, z.err = z.w.Write(z.buf[:10])
diff --git a/src/compress/gzip/gzip_test.go b/src/compress/gzip/gzip_test.go
index 09271b2..865c529 100644
--- a/src/compress/gzip/gzip_test.go
+++ b/src/compress/gzip/gzip_test.go
@@ -8,6 +8,7 @@ import (
 	"bufio"
 	"bytes"
 	"io/ioutil"
+	"reflect"
 	"testing"
 	"time"
 )
@@ -24,6 +25,9 @@ func TestEmpty(t *testing.T) {
 	if err != nil {
 		t.Fatalf("NewReader: %v", err)
 	}
+	if want := (Header{OS: 255}); !reflect.DeepEqual(r.Header, want) {
+		t.Errorf("Header mismatch:\ngot  %#v\nwant %#v", r.Header, want)
+	}
 	b, err := ioutil.ReadAll(r)
 	if err != nil {
 		t.Fatalf("ReadAll: %v", err)
diff --git a/src/compress/gzip/issue14937_test.go b/src/compress/gzip/issue14937_test.go
index 432ad16..e76d47c 100644
--- a/src/compress/gzip/issue14937_test.go
+++ b/src/compress/gzip/issue14937_test.go
@@ -7,7 +7,6 @@ import (
 	"runtime"
 	"strings"
 	"testing"
-	"time"
 )
 
 // Per golang.org/issue/14937, check that every .gz file
@@ -16,8 +15,12 @@ func TestGZIPFilesHaveZeroMTimes(t *testing.T) {
 	if testing.Short() && testenv.Builder() == "" {
 		t.Skip("skipping in short mode")
 	}
+	goroot, err := filepath.EvalSymlinks(runtime.GOROOT())
+	if err != nil {
+		t.Fatal("error evaluating GOROOT: ", err)
+	}
 	var files []string
-	err := filepath.Walk(runtime.GOROOT(), func(path string, info os.FileInfo, err error) error {
+	err = filepath.Walk(goroot, func(path string, info os.FileInfo, err error) error {
 		if err != nil {
 			return err
 		}
@@ -53,7 +56,7 @@ func checkZeroMTime(t *testing.T, path string) {
 		return
 	}
 	defer gz.Close()
-	if !gz.ModTime.Equal(time.Unix(0, 0)) {
+	if !gz.ModTime.IsZero() {
 		t.Errorf("gzip file %s has non-zero mtime (%s)", path, gz.ModTime)
 	}
 }
diff --git a/src/compress/zlib/reader_test.go b/src/compress/zlib/reader_test.go
index f74bff1..7e27aec 100644
--- a/src/compress/zlib/reader_test.go
+++ b/src/compress/zlib/reader_test.go
@@ -121,6 +121,24 @@ var zlibTests = []zlibTest{
 		},
 		ErrDictionary,
 	},
+	{
+		"truncated zlib stream amid raw-block",
+		"hello",
+		[]byte{
+			0x78, 0x9c, 0x00, 0x0c, 0x00, 0xf3, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
+		},
+		nil,
+		io.ErrUnexpectedEOF,
+	},
+	{
+		"truncated zlib stream amid fixed-block",
+		"He",
+		[]byte{
+			0x78, 0x9c, 0xf2, 0x48, 0xcd,
+		},
+		nil,
+		io.ErrUnexpectedEOF,
+	},
 }
 
 func TestDecompressor(t *testing.T) {
diff --git a/src/compress/zlib/writer.go b/src/compress/zlib/writer.go
index 3b4313a..1620c00 100644
--- a/src/compress/zlib/writer.go
+++ b/src/compress/zlib/writer.go
@@ -19,6 +19,7 @@ const (
 	BestSpeed          = flate.BestSpeed
 	BestCompression    = flate.BestCompression
 	DefaultCompression = flate.DefaultCompression
+	HuffmanOnly        = flate.HuffmanOnly
 )
 
 // A Writer takes data written to it and writes the compressed
@@ -47,9 +48,9 @@ func NewWriter(w io.Writer) *Writer {
 // NewWriterLevel is like NewWriter but specifies the compression level instead
 // of assuming DefaultCompression.
 //
-// The compression level can be DefaultCompression, NoCompression, or any
-// integer value between BestSpeed and BestCompression inclusive. The error
-// returned will be nil if the level is valid.
+// The compression level can be DefaultCompression, NoCompression, HuffmanOnly
+// or any integer value between BestSpeed and BestCompression inclusive.
+// The error returned will be nil if the level is valid.
 func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
 	return NewWriterLevelDict(w, level, nil)
 }
@@ -60,7 +61,7 @@ func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
 // The dictionary may be nil. If not, its contents should not be modified until
 // the Writer is closed.
 func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) {
-	if level < DefaultCompression || level > BestCompression {
+	if level < HuffmanOnly || level > BestCompression {
 		return nil, fmt.Errorf("zlib: invalid compression level: %d", level)
 	}
 	return &Writer{
@@ -99,7 +100,7 @@ func (z *Writer) writeHeader() (err error) {
 	// The next bit, FDICT, is set if a dictionary is given.
 	// The final five FCHECK bits form a mod-31 checksum.
 	switch z.level {
-	case 0, 1:
+	case -2, 0, 1:
 		z.scratch[1] = 0 << 6
 	case 2, 3, 4, 5:
 		z.scratch[1] = 1 << 6
diff --git a/src/compress/zlib/writer_test.go b/src/compress/zlib/writer_test.go
index dd94165..d501974 100644
--- a/src/compress/zlib/writer_test.go
+++ b/src/compress/zlib/writer_test.go
@@ -147,6 +147,7 @@ func TestWriter(t *testing.T) {
 		tag := fmt.Sprintf("#%d", i)
 		testLevelDict(t, tag, b, DefaultCompression, "")
 		testLevelDict(t, tag, b, NoCompression, "")
+		testLevelDict(t, tag, b, HuffmanOnly, "")
 		for level := BestSpeed; level <= BestCompression; level++ {
 			testLevelDict(t, tag, b, level, "")
 		}
@@ -157,6 +158,7 @@ func TestWriterBig(t *testing.T) {
 	for i, fn := range filenames {
 		testFileLevelDict(t, fn, DefaultCompression, "")
 		testFileLevelDict(t, fn, NoCompression, "")
+		testFileLevelDict(t, fn, HuffmanOnly, "")
 		for level := BestSpeed; level <= BestCompression; level++ {
 			testFileLevelDict(t, fn, level, "")
 			if level >= 1 && testing.Short() && testenv.Builder() == "" {
@@ -174,6 +176,7 @@ func TestWriterDict(t *testing.T) {
 	for i, fn := range filenames {
 		testFileLevelDict(t, fn, DefaultCompression, dictionary)
 		testFileLevelDict(t, fn, NoCompression, dictionary)
+		testFileLevelDict(t, fn, HuffmanOnly, dictionary)
 		for level := BestSpeed; level <= BestCompression; level++ {
 			testFileLevelDict(t, fn, level, dictionary)
 			if level >= 1 && testing.Short() && testenv.Builder() == "" {
@@ -191,8 +194,10 @@ func TestWriterReset(t *testing.T) {
 	for _, fn := range filenames {
 		testFileLevelDictReset(t, fn, NoCompression, nil)
 		testFileLevelDictReset(t, fn, DefaultCompression, nil)
+		testFileLevelDictReset(t, fn, HuffmanOnly, nil)
 		testFileLevelDictReset(t, fn, NoCompression, []byte(dictionary))
 		testFileLevelDictReset(t, fn, DefaultCompression, []byte(dictionary))
+		testFileLevelDictReset(t, fn, HuffmanOnly, []byte(dictionary))
 		if testing.Short() {
 			break
 		}
diff --git a/src/container/heap/heap.go b/src/container/heap/heap.go
index 5fe23b9..7110c51 100644
--- a/src/container/heap/heap.go
+++ b/src/container/heap/heap.go
@@ -83,8 +83,9 @@ func Remove(h Interface, i int) interface{} {
 // but less expensive than, calling Remove(h, i) followed by a Push of the new value.
 // The complexity is O(log(n)) where n = h.Len().
 func Fix(h Interface, i int) {
-	down(h, i, h.Len())
-	up(h, i)
+	if !down(h, i, h.Len()) {
+		up(h, i)
+	}
 }
 
 func up(h Interface, j int) {
@@ -98,7 +99,8 @@ func up(h Interface, j int) {
 	}
 }
 
-func down(h Interface, i, n int) {
+func down(h Interface, i0, n int) bool {
+	i := i0
 	for {
 		j1 := 2*i + 1
 		if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
@@ -114,4 +116,5 @@ func down(h Interface, i, n int) {
 		h.Swap(i, j)
 		i = j
 	}
+	return i > i0
 }
diff --git a/src/container/list/list_test.go b/src/container/list/list_test.go
index e3bfe53..99e006f 100644
--- a/src/container/list/list_test.go
+++ b/src/container/list/list_test.go
@@ -271,7 +271,7 @@ func TestMove(t *testing.T) {
 
 	l.MoveBefore(e2, e4)
 	checkListPointers(t, l, []*Element{e1, e3, e2, e4})
-	e1, e2, e3, e4 = e1, e3, e2, e4
+	e2, e3 = e3, e2
 
 	l.MoveBefore(e4, e1)
 	checkListPointers(t, l, []*Element{e4, e1, e2, e3})
@@ -279,11 +279,11 @@ func TestMove(t *testing.T) {
 
 	l.MoveAfter(e4, e1)
 	checkListPointers(t, l, []*Element{e1, e4, e2, e3})
-	e1, e2, e3, e4 = e1, e4, e2, e3
+	e2, e3, e4 = e4, e2, e3
 
 	l.MoveAfter(e2, e3)
 	checkListPointers(t, l, []*Element{e1, e3, e2, e4})
-	e1, e2, e3, e4 = e1, e3, e2, e4
+	e2, e3 = e3, e2
 }
 
 // Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
diff --git a/src/context/benchmark_test.go b/src/context/benchmark_test.go
new file mode 100644
index 0000000..b792327
--- /dev/null
+++ b/src/context/benchmark_test.go
@@ -0,0 +1,44 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package context_test
+
+import (
+	. "context"
+	"fmt"
+	"testing"
+)
+
+func BenchmarkContextCancelTree(b *testing.B) {
+	depths := []int{1, 10, 100, 1000}
+	for _, d := range depths {
+		b.Run(fmt.Sprintf("depth=%d", d), func(b *testing.B) {
+			b.Run("Root=Background", func(b *testing.B) {
+				for i := 0; i < b.N; i++ {
+					buildContextTree(Background(), d)
+				}
+			})
+			b.Run("Root=OpenCanceler", func(b *testing.B) {
+				for i := 0; i < b.N; i++ {
+					ctx, cancel := WithCancel(Background())
+					buildContextTree(ctx, d)
+					cancel()
+				}
+			})
+			b.Run("Root=ClosedCanceler", func(b *testing.B) {
+				for i := 0; i < b.N; i++ {
+					ctx, cancel := WithCancel(Background())
+					cancel()
+					buildContextTree(ctx, d)
+				}
+			})
+		})
+	}
+}
+
+func buildContextTree(root Context, depth int) {
+	for d := 0; d < depth; d++ {
+		root, _ = WithCancel(root)
+	}
+}
diff --git a/src/context/context.go b/src/context/context.go
index f8ce9cc..0aa7c24 100644
--- a/src/context/context.go
+++ b/src/context/context.go
@@ -159,9 +159,9 @@ var DeadlineExceeded error = deadlineExceededError{}
 
 type deadlineExceededError struct{}
 
-func (deadlineExceededError) Error() string { return "context deadline exceeded" }
-
-func (deadlineExceededError) Timeout() bool { return true }
+func (deadlineExceededError) Error() string   { return "context deadline exceeded" }
+func (deadlineExceededError) Timeout() bool   { return true }
+func (deadlineExceededError) Temporary() bool { return true }
 
 // An emptyCtx is never canceled, has no values, and has no deadline. It is not
 // struct{}, since vars of this type must have distinct addresses.
@@ -252,9 +252,9 @@ func propagateCancel(parent Context, child canceler) {
 			child.cancel(false, p.err)
 		} else {
 			if p.children == nil {
-				p.children = make(map[canceler]bool)
+				p.children = make(map[canceler]struct{})
 			}
-			p.children[child] = true
+			p.children[child] = struct{}{}
 		}
 		p.mu.Unlock()
 	} else {
@@ -314,8 +314,8 @@ type cancelCtx struct {
 	done chan struct{} // closed by the first cancel call.
 
 	mu       sync.Mutex
-	children map[canceler]bool // set to nil by the first cancel call
-	err      error             // set to non-nil by the first cancel call
+	children map[canceler]struct{} // set to nil by the first cancel call
+	err      error                 // set to non-nil by the first cancel call
 }
 
 func (c *cancelCtx) Done() <-chan struct{} {
@@ -376,7 +376,7 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
 		deadline:  deadline,
 	}
 	propagateCancel(parent, c)
-	d := deadline.Sub(time.Now())
+	d := time.Until(deadline)
 	if d <= 0 {
 		c.cancel(true, DeadlineExceeded) // deadline has already passed
 		return c, func() { c.cancel(true, Canceled) }
@@ -406,7 +406,7 @@ func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
 }
 
 func (c *timerCtx) String() string {
-	return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
+	return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, time.Until(c.deadline))
 }
 
 func (c *timerCtx) cancel(removeFromParent bool, err error) {
@@ -443,7 +443,13 @@ func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
 // Use context Values only for request-scoped data that transits processes and
 // APIs, not for passing optional parameters to functions.
 //
-// The provided key must be comparable.
+// The provided key must be comparable and should not be of type
+// string or any other built-in type to avoid collisions between
+// packages using context. Users of WithValue should define their own
+// types for keys. To avoid allocating when assigning to an
+// interface{}, context keys often have concrete type
+// struct{}. Alternatively, exported context key variables' static
+// type should be a pointer or interface.
 func WithValue(parent Context, key, val interface{}) Context {
 	if key == nil {
 		panic("nil key")
diff --git a/src/context/context_test.go b/src/context/context_test.go
index cf18211..2d604a0 100644
--- a/src/context/context_test.go
+++ b/src/context/context_test.go
@@ -10,10 +10,26 @@ import (
 	"runtime"
 	"strings"
 	"sync"
-	"testing"
 	"time"
 )
 
+type testingT interface {
+	Error(args ...interface{})
+	Errorf(format string, args ...interface{})
+	Fail()
+	FailNow()
+	Failed() bool
+	Fatal(args ...interface{})
+	Fatalf(format string, args ...interface{})
+	Log(args ...interface{})
+	Logf(format string, args ...interface{})
+	Name() string
+	Skip(args ...interface{})
+	SkipNow()
+	Skipf(format string, args ...interface{})
+	Skipped() bool
+}
+
 // otherContext is a Context that's not one of the types defined in context.go.
 // This lets us test code paths that differ based on the underlying type of the
 // Context.
@@ -21,7 +37,7 @@ type otherContext struct {
 	Context
 }
 
-func TestBackground(t *testing.T) {
+func XTestBackground(t testingT) {
 	c := Background()
 	if c == nil {
 		t.Fatalf("Background returned nil")
@@ -36,7 +52,7 @@ func TestBackground(t *testing.T) {
 	}
 }
 
-func TestTODO(t *testing.T) {
+func XTestTODO(t testingT) {
 	c := TODO()
 	if c == nil {
 		t.Fatalf("TODO returned nil")
@@ -51,7 +67,7 @@ func TestTODO(t *testing.T) {
 	}
 }
 
-func TestWithCancel(t *testing.T) {
+func XTestWithCancel(t testingT) {
 	c1, cancel := WithCancel(Background())
 
 	if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
@@ -92,7 +108,12 @@ func TestWithCancel(t *testing.T) {
 	}
 }
 
-func TestParentFinishesChild(t *testing.T) {
+func contains(m map[canceler]struct{}, key canceler) bool {
+	_, ret := m[key]
+	return ret
+}
+
+func XTestParentFinishesChild(t testingT) {
 	// Context tree:
 	// parent -> cancelChild
 	// parent -> valueChild -> timerChild
@@ -120,7 +141,7 @@ func TestParentFinishesChild(t *testing.T) {
 	cc := cancelChild.(*cancelCtx)
 	tc := timerChild.(*timerCtx)
 	pc.mu.Lock()
-	if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] {
+	if len(pc.children) != 2 || !contains(pc.children, cc) || !contains(pc.children, tc) {
 		t.Errorf("bad linkage: pc.children = %v, want %v and %v",
 			pc.children, cc, tc)
 	}
@@ -169,7 +190,7 @@ func TestParentFinishesChild(t *testing.T) {
 	}
 }
 
-func TestChildFinishesFirst(t *testing.T) {
+func XTestChildFinishesFirst(t testingT) {
 	cancelable, stop := WithCancel(Background())
 	defer stop()
 	for _, parent := range []Context{Background(), cancelable} {
@@ -191,7 +212,7 @@ func TestChildFinishesFirst(t *testing.T) {
 
 		if pcok {
 			pc.mu.Lock()
-			if len(pc.children) != 1 || !pc.children[cc] {
+			if len(pc.children) != 1 || !contains(pc.children, cc) {
 				t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
 			}
 			pc.mu.Unlock()
@@ -229,7 +250,7 @@ func TestChildFinishesFirst(t *testing.T) {
 	}
 }
 
-func testDeadline(c Context, name string, failAfter time.Duration, t *testing.T) {
+func testDeadline(c Context, name string, failAfter time.Duration, t testingT) {
 	select {
 	case <-time.After(failAfter):
 		t.Fatalf("%s: context should have timed out", name)
@@ -240,7 +261,7 @@ func testDeadline(c Context, name string, failAfter time.Duration, t *testing.T)
 	}
 }
 
-func TestDeadline(t *testing.T) {
+func XTestDeadline(t testingT) {
 	c, _ := WithDeadline(Background(), time.Now().Add(50*time.Millisecond))
 	if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
 		t.Errorf("c.String() = %q want prefix %q", got, prefix)
@@ -263,7 +284,7 @@ func TestDeadline(t *testing.T) {
 	testDeadline(c, "WithDeadline+now", time.Second, t)
 }
 
-func TestTimeout(t *testing.T) {
+func XTestTimeout(t testingT) {
 	c, _ := WithTimeout(Background(), 50*time.Millisecond)
 	if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
 		t.Errorf("c.String() = %q want prefix %q", got, prefix)
@@ -280,7 +301,7 @@ func TestTimeout(t *testing.T) {
 	testDeadline(c, "WithTimeout+otherContext+WithTimeout", 2*time.Second, t)
 }
 
-func TestCanceledTimeout(t *testing.T) {
+func XTestCanceledTimeout(t testingT) {
 	c, _ := WithTimeout(Background(), time.Second)
 	o := otherContext{c}
 	c, cancel := WithTimeout(o, 2*time.Second)
@@ -303,7 +324,7 @@ var k1 = key1(1)
 var k2 = key2(1) // same int as k1, different type
 var k3 = key2(3) // same type as k2, different int
 
-func TestValues(t *testing.T) {
+func XTestValues(t testingT) {
 	check := func(c Context, nm, v1, v2, v3 string) {
 		if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
 			t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
@@ -351,7 +372,7 @@ func TestValues(t *testing.T) {
 	check(o4, "o4", "", "c2k2", "")
 }
 
-func TestAllocs(t *testing.T) {
+func XTestAllocs(t testingT, testingShort func() bool, testingAllocsPerRun func(int, func()) float64) {
 	bg := Background()
 	for _, test := range []struct {
 		desc       string
@@ -411,16 +432,16 @@ func TestAllocs(t *testing.T) {
 			limit = test.gccgoLimit
 		}
 		numRuns := 100
-		if testing.Short() {
+		if testingShort() {
 			numRuns = 10
 		}
-		if n := testing.AllocsPerRun(numRuns, test.f); n > limit {
+		if n := testingAllocsPerRun(numRuns, test.f); n > limit {
 			t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
 		}
 	}
 }
 
-func TestSimultaneousCancels(t *testing.T) {
+func XTestSimultaneousCancels(t testingT) {
 	root, cancel := WithCancel(Background())
 	m := map[Context]CancelFunc{root: cancel}
 	q := []Context{root}
@@ -468,7 +489,7 @@ func TestSimultaneousCancels(t *testing.T) {
 	}
 }
 
-func TestInterlockedCancels(t *testing.T) {
+func XTestInterlockedCancels(t testingT) {
 	parent, cancelParent := WithCancel(Background())
 	child, cancelChild := WithCancel(parent)
 	go func() {
@@ -485,15 +506,15 @@ func TestInterlockedCancels(t *testing.T) {
 	}
 }
 
-func TestLayersCancel(t *testing.T) {
+func XTestLayersCancel(t testingT) {
 	testLayers(t, time.Now().UnixNano(), false)
 }
 
-func TestLayersTimeout(t *testing.T) {
+func XTestLayersTimeout(t testingT) {
 	testLayers(t, time.Now().UnixNano(), true)
 }
 
-func testLayers(t *testing.T, seed int64, testTimeout bool) {
+func testLayers(t testingT, seed int64, testTimeout bool) {
 	rand.Seed(seed)
 	errorf := func(format string, a ...interface{}) {
 		t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
@@ -562,7 +583,7 @@ func testLayers(t *testing.T, seed int64, testTimeout bool) {
 	}
 }
 
-func TestCancelRemoves(t *testing.T) {
+func XTestCancelRemoves(t testingT) {
 	checkChildren := func(when string, ctx Context, want int) {
 		if got := len(ctx.(*cancelCtx).children); got != want {
 			t.Errorf("%s: context has %d children, want %d", when, got, want)
@@ -584,7 +605,22 @@ func TestCancelRemoves(t *testing.T) {
 	checkChildren("after cancelling WithTimeout child", ctx, 0)
 }
 
-func TestWithValueChecksKey(t *testing.T) {
+func XTestWithCancelCanceledParent(t testingT) {
+	parent, pcancel := WithCancel(Background())
+	pcancel()
+
+	c, _ := WithCancel(parent)
+	select {
+	case <-c.Done():
+	case <-time.After(5 * time.Second):
+		t.Fatal("timeout waiting for Done")
+	}
+	if got, want := c.Err(), Canceled; got != want {
+		t.Errorf("child not cancelled; got = %v, want = %v", got, want)
+	}
+}
+
+func XTestWithValueChecksKey(t testingT) {
 	panicVal := recoveredValue(func() { WithValue(Background(), []byte("foo"), "bar") })
 	if panicVal == nil {
 		t.Error("expected panic")
@@ -601,7 +637,7 @@ func recoveredValue(fn func()) (v interface{}) {
 	return
 }
 
-func TestDeadlineExceededSupportsTimeout(t *testing.T) {
+func XTestDeadlineExceededSupportsTimeout(t testingT) {
 	i, ok := DeadlineExceeded.(interface {
 		Timeout() bool
 	})
diff --git a/src/context/example_test.go b/src/context/example_test.go
new file mode 100644
index 0000000..2d48d4e
--- /dev/null
+++ b/src/context/example_test.go
@@ -0,0 +1,116 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package context_test
+
+import (
+	"context"
+	"fmt"
+	"time"
+)
+
+// This example demonstrates the use of a cancelable context to prevent a
+// goroutine leak. By the end of the example function, the goroutine started
+// by gen will return without leaking.
+func ExampleWithCancel() {
+	// gen generates integers in a separate goroutine and
+	// sends them to the returned channel.
+	// The callers of gen need to cancel the context once
+	// they are done consuming generated integers not to leak
+	// the internal goroutine started by gen.
+	gen := func(ctx context.Context) <-chan int {
+		dst := make(chan int)
+		n := 1
+		go func() {
+			for {
+				select {
+				case <-ctx.Done():
+					return // returning not to leak the goroutine
+				case dst <- n:
+					n++
+				}
+			}
+		}()
+		return dst
+	}
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel() // cancel when we are finished consuming integers
+
+	for n := range gen(ctx) {
+		fmt.Println(n)
+		if n == 5 {
+			break
+		}
+	}
+	// Output:
+	// 1
+	// 2
+	// 3
+	// 4
+	// 5
+}
+
+// This example passes a context with a arbitrary deadline to tell a blocking
+// function that it should abandon its work as soon as it gets to it.
+func ExampleWithDeadline() {
+	d := time.Now().Add(50 * time.Millisecond)
+	ctx, cancel := context.WithDeadline(context.Background(), d)
+
+	// Even though ctx will be expired, it is good practice to call its
+	// cancelation function in any case. Failure to do so may keep the
+	// context and its parent alive longer than necessary.
+	defer cancel()
+
+	select {
+	case <-time.After(1 * time.Second):
+		fmt.Println("overslept")
+	case <-ctx.Done():
+		fmt.Println(ctx.Err())
+	}
+
+	// Output:
+	// context deadline exceeded
+}
+
+// This example passes a context with a timeout to tell a blocking function that
+// it should abandon its work after the timeout elapses.
+func ExampleWithTimeout() {
+	// Pass a context with a timeout to tell a blocking function that it
+	// should abandon its work after the timeout elapses.
+	ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
+	defer cancel()
+
+	select {
+	case <-time.After(1 * time.Second):
+		fmt.Println("overslept")
+	case <-ctx.Done():
+		fmt.Println(ctx.Err()) // prints "context deadline exceeded"
+	}
+
+	// Output:
+	// context deadline exceeded
+}
+
+func ExampleWithValue() {
+	type favContextKey string
+
+	f := func(ctx context.Context, k favContextKey) {
+		if v := ctx.Value(k); v != nil {
+			fmt.Println("found value:", v)
+			return
+		}
+		fmt.Println("key not found:", k)
+	}
+
+	k := favContextKey("language")
+	ctx := context.WithValue(context.Background(), k, "Go")
+
+	f(ctx, k)
+	f(ctx, favContextKey("color"))
+
+	// Output:
+	// found value: Go
+	// key not found: color
+}
diff --git a/src/context/net_test.go b/src/context/net_test.go
new file mode 100644
index 0000000..a007689
--- /dev/null
+++ b/src/context/net_test.go
@@ -0,0 +1,21 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package context_test
+
+import (
+	"context"
+	"net"
+	"testing"
+)
+
+func TestDeadlineExceededIsNetError(t *testing.T) {
+	err, ok := context.DeadlineExceeded.(net.Error)
+	if !ok {
+		t.Fatal("DeadlineExceeded does not implement net.Error")
+	}
+	if !err.Timeout() || !err.Temporary() {
+		t.Fatalf("Timeout() = %v, Temporary() = %v, want true, true", err.Timeout(), err.Temporary())
+	}
+}
diff --git a/src/context/withtimeout_test.go b/src/context/withtimeout_test.go
deleted file mode 100644
index a3e8979..0000000
--- a/src/context/withtimeout_test.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package context_test
-
-import (
-	"context"
-	"fmt"
-	"time"
-)
-
-func ExampleWithTimeout() {
-	// Pass a context with a timeout to tell a blocking function that it
-	// should abandon its work after the timeout elapses.
-	ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
-
-	select {
-	case <-time.After(1 * time.Second):
-		fmt.Println("overslept")
-	case <-ctx.Done():
-		fmt.Println(ctx.Err()) // prints "context deadline exceeded"
-	}
-
-	// Even though ctx should have expired already, it is good
-	// practice to call its cancelation function in any case.
-	// Failure to do so may keep the context and its parent alive
-	// longer than necessary.
-	cancel()
-
-	// Output:
-	// context deadline exceeded
-}
diff --git a/src/context/x_test.go b/src/context/x_test.go
new file mode 100644
index 0000000..d14b6f1
--- /dev/null
+++ b/src/context/x_test.go
@@ -0,0 +1,29 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package context_test
+
+import (
+	. "context"
+	"testing"
+)
+
+func TestBackground(t *testing.T)                      { XTestBackground(t) }
+func TestTODO(t *testing.T)                            { XTestTODO(t) }
+func TestWithCancel(t *testing.T)                      { XTestWithCancel(t) }
+func TestParentFinishesChild(t *testing.T)             { XTestParentFinishesChild(t) }
+func TestChildFinishesFirst(t *testing.T)              { XTestChildFinishesFirst(t) }
+func TestDeadline(t *testing.T)                        { XTestDeadline(t) }
+func TestTimeout(t *testing.T)                         { XTestTimeout(t) }
+func TestCanceledTimeout(t *testing.T)                 { XTestCanceledTimeout(t) }
+func TestValues(t *testing.T)                          { XTestValues(t) }
+func TestAllocs(t *testing.T)                          { XTestAllocs(t, testing.Short, testing.AllocsPerRun) }
+func TestSimultaneousCancels(t *testing.T)             { XTestSimultaneousCancels(t) }
+func TestInterlockedCancels(t *testing.T)              { XTestInterlockedCancels(t) }
+func TestLayersCancel(t *testing.T)                    { XTestLayersCancel(t) }
+func TestLayersTimeout(t *testing.T)                   { XTestLayersTimeout(t) }
+func TestCancelRemoves(t *testing.T)                   { XTestCancelRemoves(t) }
+func TestWithCancelCanceledParent(t *testing.T)        { XTestWithCancelCanceledParent(t) }
+func TestWithValueChecksKey(t *testing.T)              { XTestWithValueChecksKey(t) }
+func TestDeadlineExceededSupportsTimeout(t *testing.T) { XTestDeadlineExceededSupportsTimeout(t) }
diff --git a/src/crypto/aes/aes_gcm.go b/src/crypto/aes/aes_gcm.go
index a894a68..5e2de02 100644
--- a/src/crypto/aes/aes_gcm.go
+++ b/src/crypto/aes/aes_gcm.go
@@ -99,6 +99,9 @@ func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
 	if len(nonce) != g.nonceSize {
 		panic("cipher: incorrect nonce length given to GCM")
 	}
+	if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize {
+		panic("cipher: message too large for GCM")
+	}
 
 	var counter, tagMask [gcmBlockSize]byte
 
@@ -137,6 +140,10 @@ func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
 	if len(ciphertext) < gcmTagSize {
 		return nil, errOpen
 	}
+	if uint64(len(ciphertext)) > ((1<<32)-2)*BlockSize+gcmTagSize {
+		return nil, errOpen
+	}
+
 	tag := ciphertext[len(ciphertext)-gcmTagSize:]
 	ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
 
diff --git a/src/crypto/aes/asm_amd64.s b/src/crypto/aes/asm_amd64.s
index b257998..ad871ec 100644
--- a/src/crypto/aes/asm_amd64.s
+++ b/src/crypto/aes/asm_amd64.s
@@ -4,17 +4,6 @@
 
 #include "textflag.h"
 
-// func hasAsm() bool
-// returns whether AES-NI is supported
-TEXT ·hasAsm(SB),NOSPLIT,$0
-	XORQ AX, AX
-	INCL AX
-	CPUID
-	SHRQ $25, CX
-	ANDQ $1, CX
-	MOVB CX, ret+0(FP)
-	RET
-
 // func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
 TEXT ·encryptBlockAsm(SB),NOSPLIT,$0
 	MOVQ nr+0(FP), CX
diff --git a/src/crypto/aes/asm_s390x.s b/src/crypto/aes/asm_s390x.s
index e31415a..2cf3ddd 100644
--- a/src/crypto/aes/asm_s390x.s
+++ b/src/crypto/aes/asm_s390x.s
@@ -4,44 +4,20 @@
 
 #include "textflag.h"
 
-// func hasAsm() bool
-TEXT ·hasAsm(SB),NOSPLIT,$16-1
-	XOR	R0, R0          // set function code to 0 (query)
-	LA	mask-16(SP), R1 // 16-byte stack variable for mask
-	MOVD	$(0x38<<40), R3 // mask for bits 18-20 (big endian)
-
-	// check for KM AES functions
-	WORD	$0xB92E0024 // cipher message (KM)
-	MOVD	mask-16(SP), R2
-	AND	R3, R2
-	CMPBNE	R2, R3, notfound
-
-	// check for KMC AES functions
-	WORD	$0xB92F0024 // cipher message with chaining (KMC)
-	MOVD	mask-16(SP), R2
-	AND	R3, R2
-	CMPBNE	R2, R3, notfound
-
-	MOVB	$1, ret+0(FP)
-	RET
-notfound:
-	MOVB	$0, ret+0(FP)
-	RET
-
-// func cryptBlocks(function code, key, dst, src *byte, length int)
+// func cryptBlocks(c code, key, dst, src *byte, length int)
 TEXT ·cryptBlocks(SB),NOSPLIT,$0-40
 	MOVD	key+8(FP), R1
 	MOVD	dst+16(FP), R2
 	MOVD	src+24(FP), R4
 	MOVD	length+32(FP), R5
-	MOVD	function+0(FP), R0
+	MOVD	c+0(FP), R0
 loop:
 	WORD	$0xB92E0024 // cipher message (KM)
 	BVS	loop        // branch back if interrupted
 	XOR	R0, R0
 	RET
 
-// func cryptBlocksChain(function code, iv, key, dst, src *byte, length int)
+// func cryptBlocksChain(c code, iv, key, dst, src *byte, length int)
 TEXT ·cryptBlocksChain(SB),NOSPLIT,$48-48
 	LA	params-48(SP), R1
 	MOVD	iv+8(FP), R8
@@ -51,7 +27,7 @@ TEXT ·cryptBlocksChain(SB),NOSPLIT,$48-48
 	MOVD	dst+24(FP), R2
 	MOVD	src+32(FP), R4
 	MOVD	length+40(FP), R5
-	MOVD	function+0(FP), R0
+	MOVD	c+0(FP), R0
 loop:
 	WORD	$0xB92F0024       // cipher message with chaining (KMC)
 	BVS	loop              // branch back if interrupted
@@ -91,3 +67,86 @@ tail:
 	BR	tail
 done:
 	RET
+
+// func cryptBlocksGCM(fn code, key, dst, src, buf []byte, cnt *[16]byte)
+TEXT ·cryptBlocksGCM(SB),NOSPLIT,$0-112
+	MOVD	src_len+64(FP), R0
+	MOVD	buf_base+80(FP), R1
+	MOVD	cnt+104(FP), R12
+	LMG	(R12), R2, R3
+
+	// Check that the src size is less than or equal to the buffer size.
+	MOVD	buf_len+88(FP), R4
+	CMP	R0, R4
+	BGT	crash
+
+	// Check that the src size is a multiple of 16-bytes.
+	MOVD	R0, R4
+	AND	$0xf, R4
+	BLT	crash // non-zero
+
+	// Check that the src size is less than or equal to the dst size.
+	MOVD	dst_len+40(FP), R4
+	CMP	R0, R4
+	BGT	crash
+
+	MOVD	R2, R4
+	MOVD	R2, R6
+	MOVD	R2, R8
+	MOVD	R3, R5
+	MOVD	R3, R7
+	MOVD	R3, R9
+	ADDW	$1, R5
+	ADDW	$2, R7
+	ADDW	$3, R9
+incr:
+	CMP	R0, $64
+	BLT	tail
+	STMG	R2, R9, (R1)
+	ADDW	$4, R3
+	ADDW	$4, R5
+	ADDW	$4, R7
+	ADDW	$4, R9
+	MOVD	$64(R1), R1
+	SUB	$64, R0
+	BR	incr
+tail:
+	CMP	R0, $0
+	BEQ	crypt
+	STMG	R2, R3, (R1)
+	ADDW	$1, R3
+	MOVD	$16(R1), R1
+	SUB	$16, R0
+	BR	tail
+crypt:
+	STMG	R2, R3, (R12)       // update next counter value
+	MOVD	fn+0(FP), R0        // function code (encryption)
+	MOVD	key_base+8(FP), R1  // key
+	MOVD	buf_base+80(FP), R2 // counter values
+	MOVD	dst_base+32(FP), R4 // dst
+	MOVD	src_base+56(FP), R6 // src
+	MOVD	src_len+64(FP), R7  // len
+loop:
+	WORD	$0xB92D2046         // cipher message with counter (KMCTR)
+	BVS	loop                // branch back if interrupted
+	RET
+crash:
+	MOVD	$0, (R0)
+	RET
+
+// func ghash(key *gcmHashKey, hash *[16]byte, data []byte)
+TEXT ·ghash(SB),NOSPLIT,$32-40
+	MOVD    $65, R0 // GHASH function code
+	MOVD	key+0(FP), R2
+	LMG	(R2), R6, R7
+	MOVD	hash+8(FP), R8
+	LMG	(R8), R4, R5
+	MOVD	$params-32(SP), R1
+	STMG	R4, R7, (R1)
+	LMG	data+16(FP), R2, R3 // R2=base, R3=len
+loop:
+	WORD    $0xB93E0002 // compute intermediate message digest (KIMD)
+	BVS     loop        // branch back if interrupted
+	MVC     $16, (R1), (R8)
+	MOVD	$0, R0
+	RET
diff --git a/src/crypto/aes/cipher_amd64.go b/src/crypto/aes/cipher_amd64.go
index b33c8ff..43de3bd 100644
--- a/src/crypto/aes/cipher_amd64.go
+++ b/src/crypto/aes/cipher_amd64.go
@@ -6,10 +6,10 @@ package aes
 
 import (
 	"crypto/cipher"
+	"crypto/internal/cipherhw"
 )
 
 // defined in asm_amd64.s
-func hasAsm() bool
 func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
 func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
 func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
@@ -18,7 +18,7 @@ type aesCipherAsm struct {
 	aesCipher
 }
 
-var useAsm = hasAsm()
+var useAsm = cipherhw.AESGCMSupport()
 
 func newCipher(key []byte) (cipher.Block, error) {
 	if !useAsm {
diff --git a/src/crypto/aes/cipher_s390x.go b/src/crypto/aes/cipher_s390x.go
index bec5933..6030c25 100644
--- a/src/crypto/aes/cipher_s390x.go
+++ b/src/crypto/aes/cipher_s390x.go
@@ -6,6 +6,7 @@ package aes
 
 import (
 	"crypto/cipher"
+	"crypto/internal/cipherhw"
 )
 
 type code int
@@ -23,18 +24,13 @@ type aesCipherAsm struct {
 	storage  [256]byte // array backing key slice
 }
 
-// hasAsm reports whether the AES-128, AES-192 and AES-256
-// cipher message (KM) function codes are supported.
-// Note: this function call is expensive.
-func hasAsm() bool
-
 // cryptBlocks invokes the cipher message (KM) instruction with
 // the given function code. This is equivalent to AES in ECB
 // mode. The length must be a multiple of BlockSize (16).
 //go:noesape
 func cryptBlocks(c code, key, dst, src *byte, length int)
 
-var useAsm = hasAsm()
+var useAsm = cipherhw.AESGCMSupport()
 
 func newCipher(key []byte) (cipher.Block, error) {
 	if !useAsm {
diff --git a/src/crypto/aes/gcm_s390x.go b/src/crypto/aes/gcm_s390x.go
new file mode 100644
index 0000000..9eaaf7c
--- /dev/null
+++ b/src/crypto/aes/gcm_s390x.go
@@ -0,0 +1,270 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package aes
+
+import (
+	"crypto/cipher"
+	"crypto/subtle"
+	"errors"
+)
+
+// gcmCount represents a 16-byte big-endian count value.
+type gcmCount [16]byte
+
+// inc increments the rightmost 32-bits of the count value by 1.
+func (x *gcmCount) inc() {
+	// The compiler should optimize this to a 32-bit addition.
+	n := uint32(x[15]) | uint32(x[14])<<8 | uint32(x[13])<<16 | uint32(x[12])<<24
+	n += 1
+	x[12] = byte(n >> 24)
+	x[13] = byte(n >> 16)
+	x[14] = byte(n >> 8)
+	x[15] = byte(n)
+}
+
+// gcmLengths writes len0 || len1 as big-endian values to a 16-byte array.
+func gcmLengths(len0, len1 uint64) [16]byte {
+	return [16]byte{
+		byte(len0 >> 56),
+		byte(len0 >> 48),
+		byte(len0 >> 40),
+		byte(len0 >> 32),
+		byte(len0 >> 24),
+		byte(len0 >> 16),
+		byte(len0 >> 8),
+		byte(len0),
+		byte(len1 >> 56),
+		byte(len1 >> 48),
+		byte(len1 >> 40),
+		byte(len1 >> 32),
+		byte(len1 >> 24),
+		byte(len1 >> 16),
+		byte(len1 >> 8),
+		byte(len1),
+	}
+}
+
+// gcmHashKey represents the 16-byte hash key required by the GHASH algorithm.
+type gcmHashKey [16]byte
+
+type gcmAsm struct {
+	block     *aesCipherAsm
+	hashKey   gcmHashKey
+	nonceSize int
+}
+
+const (
+	gcmBlockSize         = 16
+	gcmTagSize           = 16
+	gcmStandardNonceSize = 12
+)
+
+var errOpen = errors.New("cipher: message authentication failed")
+
+// Assert that aesCipherAsm implements the gcmAble interface.
+var _ gcmAble = (*aesCipherAsm)(nil)
+
+// NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
+// called by crypto/cipher.NewGCM via the gcmAble interface.
+func (c *aesCipherAsm) NewGCM(nonceSize int) (cipher.AEAD, error) {
+	var hk gcmHashKey
+	c.Encrypt(hk[:], hk[:])
+	g := &gcmAsm{
+		block:     c,
+		hashKey:   hk,
+		nonceSize: nonceSize,
+	}
+	return g, nil
+}
+
+func (g *gcmAsm) NonceSize() int {
+	return g.nonceSize
+}
+
+func (*gcmAsm) Overhead() int {
+	return gcmTagSize
+}
+
+// sliceForAppend takes a slice and a requested number of bytes. It returns a
+// slice with the contents of the given slice followed by that many bytes and a
+// second slice that aliases into it and contains only the extra bytes. If the
+// original slice has sufficient capacity then no allocation is performed.
+func sliceForAppend(in []byte, n int) (head, tail []byte) {
+	if total := len(in) + n; cap(in) >= total {
+		head = in[:total]
+	} else {
+		head = make([]byte, total)
+		copy(head, in)
+	}
+	tail = head[len(in):]
+	return
+}
+
+// ghash uses the GHASH algorithm to hash data with the given key. The initial
+// hash value is given by hash which will be updated with the new hash value.
+// The length of data must be a multiple of 16-bytes.
+//go:noescape
+func ghash(key *gcmHashKey, hash *[16]byte, data []byte)
+
+// paddedGHASH pads data with zeroes until its length is a multiple of
+// 16-bytes. It then calculates a new value for hash using the GHASH algorithm.
+func (g *gcmAsm) paddedGHASH(hash *[16]byte, data []byte) {
+	siz := len(data) &^ 0xf // align size to 16-bytes
+	if siz > 0 {
+		ghash(&g.hashKey, hash, data[:siz])
+		data = data[siz:]
+	}
+	if len(data) > 0 {
+		var s [16]byte
+		copy(s[:], data)
+		ghash(&g.hashKey, hash, s[:])
+	}
+}
+
+// cryptBlocksGCM encrypts src using AES in counter mode using the given
+// function code and key. The rightmost 32-bits of the counter are incremented
+// between each block as required by the GCM spec. The initial counter value
+// is given by cnt, which is updated with the value of the next counter value
+// to use.
+//
+// The lengths of both dst and buf must be greater than or equal to the length
+// of src. buf may be partially or completely overwritten during the execution
+// of the function.
+//go:noescape
+func cryptBlocksGCM(fn code, key, dst, src, buf []byte, cnt *gcmCount)
+
+// counterCrypt encrypts src using AES in counter mode and places the result
+// into dst. cnt is the initial count value and will be updated with the next
+// count value. The length of dst must be greater than or equal to the length
+// of src.
+func (g *gcmAsm) counterCrypt(dst, src []byte, cnt *gcmCount) {
+	// Copying src into a buffer improves performance on some models when
+	// src and dst point to the same underlying array. We also need a
+	// buffer for counter values.
+	var ctrbuf, srcbuf [2048]byte
+	for len(src) >= 16 {
+		siz := len(src)
+		if len(src) > len(ctrbuf) {
+			siz = len(ctrbuf)
+		}
+		siz &^= 0xf // align siz to 16-bytes
+		copy(srcbuf[:], src[:siz])
+		cryptBlocksGCM(g.block.function, g.block.key, dst[:siz], srcbuf[:siz], ctrbuf[:], cnt)
+		src = src[siz:]
+		dst = dst[siz:]
+	}
+	if len(src) > 0 {
+		var x [16]byte
+		g.block.Encrypt(x[:], cnt[:])
+		for i := range src {
+			dst[i] = src[i] ^ x[i]
+		}
+		cnt.inc()
+	}
+}
+
+// deriveCounter computes the initial GCM counter state from the given nonce.
+// See NIST SP 800-38D, section 7.1.
+func (g *gcmAsm) deriveCounter(nonce []byte) gcmCount {
+	// GCM has two modes of operation with respect to the initial counter
+	// state: a "fast path" for 96-bit (12-byte) nonces, and a "slow path"
+	// for nonces of other lengths. For a 96-bit nonce, the nonce, along
+	// with a four-byte big-endian counter starting at one, is used
+	// directly as the starting counter. For other nonce sizes, the counter
+	// is computed by passing it through the GHASH function.
+	var counter gcmCount
+	if len(nonce) == gcmStandardNonceSize {
+		copy(counter[:], nonce)
+		counter[gcmBlockSize-1] = 1
+	} else {
+		var hash [16]byte
+		g.paddedGHASH(&hash, nonce)
+		lens := gcmLengths(0, uint64(len(nonce))*8)
+		g.paddedGHASH(&hash, lens[:])
+		copy(counter[:], hash[:])
+	}
+	return counter
+}
+
+// auth calculates GHASH(ciphertext, additionalData), masks the result with
+// tagMask and writes the result to out.
+func (g *gcmAsm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]byte) {
+	var hash [16]byte
+	g.paddedGHASH(&hash, additionalData)
+	g.paddedGHASH(&hash, ciphertext)
+	lens := gcmLengths(uint64(len(additionalData))*8, uint64(len(ciphertext))*8)
+	g.paddedGHASH(&hash, lens[:])
+
+	copy(out, hash[:])
+	for i := range out {
+		out[i] ^= tagMask[i]
+	}
+}
+
+// Seal encrypts and authenticates plaintext. See the cipher.AEAD interface for
+// details.
+func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
+	if len(nonce) != g.nonceSize {
+		panic("cipher: incorrect nonce length given to GCM")
+	}
+	if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize {
+		panic("cipher: message too large for GCM")
+	}
+
+	ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
+
+	counter := g.deriveCounter(nonce)
+
+	var tagMask [gcmBlockSize]byte
+	g.block.Encrypt(tagMask[:], counter[:])
+	counter.inc()
+
+	g.counterCrypt(out, plaintext, &counter)
+	g.auth(out[len(plaintext):], out[:len(plaintext)], data, &tagMask)
+
+	return ret
+}
+
+// Open authenticates and decrypts ciphertext. See the cipher.AEAD interface
+// for details.
+func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
+	if len(nonce) != g.nonceSize {
+		panic("cipher: incorrect nonce length given to GCM")
+	}
+	if len(ciphertext) < gcmTagSize {
+		return nil, errOpen
+	}
+	if uint64(len(ciphertext)) > ((1<<32)-2)*BlockSize+gcmTagSize {
+		return nil, errOpen
+	}
+
+	tag := ciphertext[len(ciphertext)-gcmTagSize:]
+	ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
+
+	counter := g.deriveCounter(nonce)
+
+	var tagMask [gcmBlockSize]byte
+	g.block.Encrypt(tagMask[:], counter[:])
+	counter.inc()
+
+	var expectedTag [gcmTagSize]byte
+	g.auth(expectedTag[:], ciphertext, data, &tagMask)
+
+	ret, out := sliceForAppend(dst, len(ciphertext))
+
+	if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 {
+		// The AESNI code decrypts and authenticates concurrently, and
+		// so overwrites dst in the event of a tag mismatch. That
+		// behaviour is mimicked here in order to be consistent across
+		// platforms.
+		for i := range out {
+			out[i] = 0
+		}
+		return nil, errOpen
+	}
+
+	g.counterCrypt(out, ciphertext, &counter)
+	return ret, nil
+}
diff --git a/src/crypto/cipher/example_test.go b/src/crypto/cipher/example_test.go
index 9abe782..956cc2e 100644
--- a/src/crypto/cipher/example_test.go
+++ b/src/crypto/cipher/example_test.go
@@ -14,7 +14,7 @@ import (
 	"os"
 )
 
-func ExampleNewGCMEncrypter() {
+func ExampleNewGCM_encrypt() {
 	// The key argument should be the AES key, either 16 or 32 bytes
 	// to select AES-128 or AES-256.
 	key := []byte("AES256Key-32Characters1234567890")
@@ -40,7 +40,7 @@ func ExampleNewGCMEncrypter() {
 	fmt.Printf("%x\n", ciphertext)
 }
 
-func ExampleNewGCMDecrypter() {
+func ExampleNewGCM_decrypt() {
 	// The key argument should be the AES key, either 16 or 32 bytes
 	// to select AES-128 or AES-256.
 	key := []byte("AES256Key-32Characters1234567890")
diff --git a/src/crypto/cipher/gcm.go b/src/crypto/cipher/gcm.go
index 3868d71..cfc5769 100644
--- a/src/crypto/cipher/gcm.go
+++ b/src/crypto/cipher/gcm.go
@@ -135,6 +135,10 @@ func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte {
 	if len(nonce) != g.nonceSize {
 		panic("cipher: incorrect nonce length given to GCM")
 	}
+	if uint64(len(plaintext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize()) {
+		panic("cipher: message too large for GCM")
+	}
+
 	ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
 
 	var counter, tagMask [gcmBlockSize]byte
@@ -159,6 +163,10 @@ func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
 	if len(ciphertext) < gcmTagSize {
 		return nil, errOpen
 	}
+	if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize())+gcmTagSize {
+		return nil, errOpen
+	}
+
 	tag := ciphertext[len(ciphertext)-gcmTagSize:]
 	ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
 
diff --git a/src/crypto/cipher/gcm_test.go b/src/crypto/cipher/gcm_test.go
index bb1ab3c..6878b4c 100644
--- a/src/crypto/cipher/gcm_test.go
+++ b/src/crypto/cipher/gcm_test.go
@@ -8,7 +8,11 @@ import (
 	"bytes"
 	"crypto/aes"
 	"crypto/cipher"
+	"crypto/rand"
 	"encoding/hex"
+	"errors"
+	"io"
+	"reflect"
 	"testing"
 )
 
@@ -274,3 +278,157 @@ func TestTagFailureOverwrite(t *testing.T) {
 		}
 	}
 }
+
+func TestGCMCounterWrap(t *testing.T) {
+	// Test that the last 32-bits of the counter wrap correctly.
+	tests := []struct {
+		nonce, tag string
+	}{
+		{"0fa72e25", "37e1948cdfff09fbde0c40ad99fee4a7"},   // counter: 7eb59e4d961dad0dfdd75aaffffffff0
+		{"afe05cc1", "438f3aa9fee5e54903b1927bca26bbdf"},   // counter: 75d492a7e6e6bfc979ad3a8ffffffff4
+		{"9ffecbef", "7b88ca424df9703e9e8611071ec7e16e"},   // counter: c8bb108b0ecdc71747b9d57ffffffff5
+		{"ffc3e5b3", "38d49c86e0abe853ac250e66da54c01a"},   // counter: 706414d2de9b36ab3b900a9ffffffff6
+		{"cfdd729d", "e08402eaac36a1a402e09b1bd56500e8"},   // counter: cd0b96fe36b04e750584e56ffffffff7
+		{"010ae3d486", "5405bb490b1f95d01e2ba735687154bc"}, // counter: e36c18e69406c49722808104fffffff8
+		{"01b1107a9d", "939a585f342e01e17844627492d44dbf"}, // counter: e6d56eaf9127912b6d62c6dcffffffff
+	}
+	key, err := aes.NewCipher(make([]byte, 16))
+	if err != nil {
+		t.Fatal(err)
+	}
+	plaintext := make([]byte, 16*17+1)
+	for i, test := range tests {
+		nonce, _ := hex.DecodeString(test.nonce)
+		want, _ := hex.DecodeString(test.tag)
+		aead, err := cipher.NewGCMWithNonceSize(key, len(nonce))
+		if err != nil {
+			t.Fatal(err)
+		}
+		got := aead.Seal(nil, nonce, plaintext, nil)
+		if !bytes.Equal(got[len(plaintext):], want) {
+			t.Errorf("test[%v]: got: %x, want: %x", i, got[len(plaintext):], want)
+		}
+		_, err = aead.Open(nil, nonce, got, nil)
+		if err != nil {
+			t.Errorf("test[%v]: authentication failed", i)
+		}
+	}
+}
+
+var _ cipher.Block = (*wrapper)(nil)
+
+type wrapper struct {
+	block cipher.Block
+}
+
+func (w *wrapper) BlockSize() int          { return w.block.BlockSize() }
+func (w *wrapper) Encrypt(dst, src []byte) { w.block.Encrypt(dst, src) }
+func (w *wrapper) Decrypt(dst, src []byte) { w.block.Decrypt(dst, src) }
+
+// wrap wraps the Block interface so that it does not fulfill
+// any optimizing interfaces such as gcmAble.
+func wrap(b cipher.Block) cipher.Block {
+	return &wrapper{b}
+}
+
+func TestGCMAsm(t *testing.T) {
+	// Create a new pair of AEADs, one using the assembly implementation
+	// and one using the generic Go implementation.
+	newAESGCM := func(key []byte) (asm, generic cipher.AEAD, err error) {
+		block, err := aes.NewCipher(key[:])
+		if err != nil {
+			return nil, nil, err
+		}
+		asm, err = cipher.NewGCM(block)
+		if err != nil {
+			return nil, nil, err
+		}
+		generic, err = cipher.NewGCM(wrap(block))
+		if err != nil {
+			return nil, nil, err
+		}
+		return asm, generic, nil
+	}
+
+	// check for assembly implementation
+	var key [16]byte
+	asm, generic, err := newAESGCM(key[:])
+	if err != nil {
+		t.Fatal(err)
+	}
+	if reflect.TypeOf(asm) == reflect.TypeOf(generic) {
+		t.Skipf("no assembly implementation of GCM")
+	}
+
+	// generate permutations
+	type pair struct{ align, length int }
+	lengths := []int{0, 8192, 8193, 8208}
+	keySizes := []int{16, 24, 32}
+	alignments := []int{0, 1, 2, 3}
+	if testing.Short() {
+		keySizes = []int{16}
+		alignments = []int{1}
+	}
+	perms := make([]pair, 0)
+	for _, l := range lengths {
+		for _, a := range alignments {
+			if a != 0 && l == 0 {
+				continue
+			}
+			perms = append(perms, pair{align: a, length: l})
+		}
+	}
+
+	// run test for all permutations
+	test := func(ks int, pt, ad []byte) error {
+		key := make([]byte, ks)
+		if _, err := io.ReadFull(rand.Reader, key); err != nil {
+			return err
+		}
+		asm, generic, err := newAESGCM(key)
+		if err != nil {
+			return err
+		}
+		if _, err := io.ReadFull(rand.Reader, pt); err != nil {
+			return err
+		}
+		if _, err := io.ReadFull(rand.Reader, ad); err != nil {
+			return err
+		}
+		nonce := make([]byte, 12)
+		if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
+			return err
+		}
+		want := generic.Seal(nil, nonce, pt, ad)
+		got := asm.Seal(nil, nonce, pt, ad)
+		if !bytes.Equal(want, got) {
+			return errors.New("incorrect Seal output")
+		}
+		got, err = asm.Open(nil, nonce, want, ad)
+		if err != nil {
+			return errors.New("authentication failed")
+		}
+		if !bytes.Equal(pt, got) {
+			return errors.New("incorrect Open output")
+		}
+		return nil
+	}
+	for _, a := range perms {
+		ad := make([]byte, a.align+a.length)
+		ad = ad[a.align:]
+		for _, p := range perms {
+			pt := make([]byte, p.align+p.length)
+			pt = pt[p.align:]
+			for _, ks := range keySizes {
+				if err := test(ks, pt, ad); err != nil {
+					t.Error(err)
+					t.Errorf("	key size: %v", ks)
+					t.Errorf("	plaintext alignment: %v", p.align)
+					t.Errorf("	plaintext length: %v", p.length)
+					t.Errorf("	additionalData alignment: %v", a.align)
+					t.Fatalf("	additionalData length: %v", a.length)
+				}
+			}
+		}
+	}
+}
diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go
index 72fb499..02848fd 100644
--- a/src/crypto/ecdsa/ecdsa.go
+++ b/src/crypto/ecdsa/ecdsa.go
@@ -149,7 +149,7 @@ var errZeroParam = errors.New("zero parameter")
 // returns the signature as a pair of integers. The security of the private key
 // depends on the entropy of rand.
 func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
-	// Get max(log2(q) / 2, 256) bits of entropy from rand.
+	// Get min(log2(q) / 2, 256) bits of entropy from rand.
 	entropylen := (priv.Curve.Params().BitSize + 7) / 16
 	if entropylen > 32 {
 		entropylen = 32
diff --git a/src/crypto/ecdsa/ecdsa_test.go b/src/crypto/ecdsa/ecdsa_test.go
index fc25fd7..9546f67 100644
--- a/src/crypto/ecdsa/ecdsa_test.go
+++ b/src/crypto/ecdsa/ecdsa_test.go
@@ -54,6 +54,18 @@ func BenchmarkSignP256(b *testing.B) {
 	}
 }
 
+func BenchmarkSignP384(b *testing.B) {
+	b.ResetTimer()
+	p384 := elliptic.P384()
+	hashed := []byte("testing")
+	priv, _ := GenerateKey(p384, rand.Reader)
+
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		_, _, _ = Sign(rand.Reader, priv, hashed)
+	}
+}
+
 func BenchmarkVerifyP256(b *testing.B) {
 	b.ResetTimer()
 	p256 := elliptic.P256()
diff --git a/src/crypto/elliptic/elliptic_test.go b/src/crypto/elliptic/elliptic_test.go
index 7f3f1a2..902c414 100644
--- a/src/crypto/elliptic/elliptic_test.go
+++ b/src/crypto/elliptic/elliptic_test.go
@@ -28,7 +28,7 @@ func TestOffCurve(t *testing.T) {
 	b := Marshal(p224, x, y)
 	x1, y1 := Unmarshal(p224, b)
 	if x1 != nil || y1 != nil {
-		t.Errorf("FAIL: unmarshalling a point not on the curve succeeded")
+		t.Errorf("FAIL: unmarshaling a point not on the curve succeeded")
 	}
 }
 
diff --git a/src/crypto/elliptic/p256.go b/src/crypto/elliptic/p256.go
index 05a3311..bbf0087 100644
--- a/src/crypto/elliptic/p256.go
+++ b/src/crypto/elliptic/p256.go
@@ -17,7 +17,8 @@ type p256Curve struct {
 }
 
 var (
-	p256 p256Curve
+	p256Params *CurveParams
+
 	// RInverse contains 1/R mod p - the inverse of the Montgomery constant
 	// (2**257).
 	p256RInverse *big.Int
@@ -25,15 +26,18 @@ var (
 
 func initP256() {
 	// See FIPS 186-3, section D.2.3
-	p256.CurveParams = &CurveParams{Name: "P-256"}
-	p256.P, _ = new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10)
-	p256.N, _ = new(big.Int).SetString("115792089210356248762697446949407573529996955224135760342422259061068512044369", 10)
-	p256.B, _ = new(big.Int).SetString("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)
-	p256.Gx, _ = new(big.Int).SetString("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16)
-	p256.Gy, _ = new(big.Int).SetString("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16)
-	p256.BitSize = 256
+	p256Params = &CurveParams{Name: "P-256"}
+	p256Params.P, _ = new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10)
+	p256Params.N, _ = new(big.Int).SetString("115792089210356248762697446949407573529996955224135760342422259061068512044369", 10)
+	p256Params.B, _ = new(big.Int).SetString("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)
+	p256Params.Gx, _ = new(big.Int).SetString("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16)
+	p256Params.Gy, _ = new(big.Int).SetString("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16)
+	p256Params.BitSize = 256
 
 	p256RInverse, _ = new(big.Int).SetString("7fffffff00000001fffffffe8000000100000000ffffffff0000000180000000", 16)
+
+	// Arch-specific initialization, i.e. let a platform dynamically pick a P256 implementation
+	initP256Arch()
 }
 
 func (curve p256Curve) Params() *CurveParams {
@@ -47,8 +51,8 @@ func p256GetScalar(out *[32]byte, in []byte) {
 	n := new(big.Int).SetBytes(in)
 	var scalarBytes []byte
 
-	if n.Cmp(p256.N) >= 0 {
-		n.Mod(n, p256.N)
+	if n.Cmp(p256Params.N) >= 0 {
+		n.Mod(n, p256Params.N)
 		scalarBytes = n.Bytes()
 	} else {
 		scalarBytes = in
@@ -1143,7 +1147,7 @@ func p256ScalarMult(xOut, yOut, zOut, x, y *[p256Limbs]uint32, scalar *[32]uint8
 // p256FromBig sets out = R*in.
 func p256FromBig(out *[p256Limbs]uint32, in *big.Int) {
 	tmp := new(big.Int).Lsh(in, 257)
-	tmp.Mod(tmp, p256.P)
+	tmp.Mod(tmp, p256Params.P)
 
 	for i := 0; i < p256Limbs; i++ {
 		if bits := tmp.Bits(); len(bits) > 0 {
@@ -1183,6 +1187,6 @@ func p256ToBig(in *[p256Limbs]uint32) *big.Int {
 	}
 
 	result.Mul(result, p256RInverse)
-	result.Mod(result, p256.P)
+	result.Mod(result, p256Params.P)
 	return result
 }
diff --git a/src/crypto/elliptic/p256_asm_s390x.s b/src/crypto/elliptic/p256_asm_s390x.s
new file mode 100644
index 0000000..96b59be
--- /dev/null
+++ b/src/crypto/elliptic/p256_asm_s390x.s
@@ -0,0 +1,2201 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+DATA p256ordK0<>+0x00(SB)/4, $0xee00bc4f
+DATA p256ord<>+0x00(SB)/8, $0xffffffff00000000
+DATA p256ord<>+0x08(SB)/8, $0xffffffffffffffff
+DATA p256ord<>+0x10(SB)/8, $0xbce6faada7179e84
+DATA p256ord<>+0x18(SB)/8, $0xf3b9cac2fc632551
+DATA p256<>+0x00(SB)/8, $0xffffffff00000001 // P256
+DATA p256<>+0x08(SB)/8, $0x0000000000000000 // P256
+DATA p256<>+0x10(SB)/8, $0x00000000ffffffff // P256
+DATA p256<>+0x18(SB)/8, $0xffffffffffffffff // P256
+DATA p256<>+0x20(SB)/8, $0x0c0d0e0f1c1d1e1f // SEL d1 d0 d1 d0
+DATA p256<>+0x28(SB)/8, $0x0c0d0e0f1c1d1e1f // SEL d1 d0 d1 d0
+DATA p256<>+0x30(SB)/8, $0x0000000010111213 // SEL 0  d1 d0  0
+DATA p256<>+0x38(SB)/8, $0x1415161700000000 // SEL 0  d1 d0  0
+DATA p256<>+0x40(SB)/8, $0x18191a1b1c1d1e1f // SEL d1 d0 d1 d0
+DATA p256<>+0x48(SB)/8, $0x18191a1b1c1d1e1f // SEL d1 d0 d1 d0
+DATA p256mul<>+0x00(SB)/8, $0xffffffff00000001 // P256
+DATA p256mul<>+0x08(SB)/8, $0x0000000000000000 // P256
+DATA p256mul<>+0x10(SB)/8, $0x00000000ffffffff // P256
+DATA p256mul<>+0x18(SB)/8, $0xffffffffffffffff // P256
+DATA p256mul<>+0x20(SB)/8, $0x1c1d1e1f00000000 // SEL d0  0  0 d0
+DATA p256mul<>+0x28(SB)/8, $0x000000001c1d1e1f // SEL d0  0  0 d0
+DATA p256mul<>+0x30(SB)/8, $0x0001020304050607 // SEL d0  0 d1 d0
+DATA p256mul<>+0x38(SB)/8, $0x1c1d1e1f0c0d0e0f // SEL d0  0 d1 d0
+DATA p256mul<>+0x40(SB)/8, $0x040506071c1d1e1f // SEL  0 d1 d0 d1
+DATA p256mul<>+0x48(SB)/8, $0x0c0d0e0f1c1d1e1f // SEL  0 d1 d0 d1
+DATA p256mul<>+0x50(SB)/8, $0x0405060704050607 // SEL  0  0 d1 d0
+DATA p256mul<>+0x58(SB)/8, $0x1c1d1e1f0c0d0e0f // SEL  0  0 d1 d0
+DATA p256mul<>+0x60(SB)/8, $0x0c0d0e0f1c1d1e1f // SEL d1 d0 d1 d0
+DATA p256mul<>+0x68(SB)/8, $0x0c0d0e0f1c1d1e1f // SEL d1 d0 d1 d0
+DATA p256mul<>+0x70(SB)/8, $0x141516170c0d0e0f // SEL 0  d1 d0  0
+DATA p256mul<>+0x78(SB)/8, $0x1c1d1e1f14151617 // SEL 0  d1 d0  0
+DATA p256mul<>+0x80(SB)/8, $0x00000000fffffffe // (1*2^256)%P256
+DATA p256mul<>+0x88(SB)/8, $0xffffffffffffffff // (1*2^256)%P256
+DATA p256mul<>+0x90(SB)/8, $0xffffffff00000000 // (1*2^256)%P256
+DATA p256mul<>+0x98(SB)/8, $0x0000000000000001 // (1*2^256)%P256
+GLOBL p256ordK0<>(SB), 8, $4
+GLOBL p256ord<>(SB), 8, $32
+GLOBL p256<>(SB), 8, $80
+GLOBL p256mul<>(SB), 8, $160
+
+// func hasVectorFacility() bool
+TEXT ·hasVectorFacility(SB), NOSPLIT, $24-1
+	MOVD  $x-24(SP), R1
+	XC    $24, 0(R1), 0(R1) // clear the storage
+	MOVD  $2, R0            // R0 is the number of double words stored -1
+	WORD  $0xB2B01000       // STFLE 0(R1)
+	XOR   R0, R0            // reset the value of R0
+	MOVBZ z-8(SP), R1
+	AND   $0x40, R1
+	BEQ   novector
+
+vectorinstalled:
+	// check if the vector instruction has been enabled
+	VLEIB  $0, $0xF, V16
+	VLGVB  $0, V16, R1
+	CMPBNE R1, $0xF, novector
+	MOVB   $1, ret+0(FP) // have vx
+	RET
+
+novector:
+	MOVB $0, ret+0(FP)   // no vx
+	RET
+
+// ---------------------------------------
+// iff cond == 1  val <- -val
+// func p256NegCond(val *p256Point, cond int)
+#define P1ptr   R1
+#define CPOOL   R4
+
+#define Y1L   V0
+#define Y1H   V1
+#define T1L   V2
+#define T1H   V3
+
+#define PL    V30
+#define PH    V31
+
+#define ZER   V4
+#define SEL1  V5
+#define CAR1  V6
+TEXT ·p256NegCond(SB), NOSPLIT, $0
+	MOVD val+0(FP), P1ptr
+
+	MOVD $p256mul<>+0x00(SB), CPOOL
+	VL   16(CPOOL), PL
+	VL   0(CPOOL), PH
+
+	VL 32(P1ptr), Y1H
+	VL 48(P1ptr), Y1L
+
+	VLREPG cond+8(FP), SEL1
+	VZERO  ZER
+	VCEQG  SEL1, ZER, SEL1
+
+	VSCBIQ Y1L, PL, CAR1
+	VSQ    Y1L, PL, T1L
+	VSBIQ  PH, Y1H, CAR1, T1H
+
+	VSEL Y1L, T1L, SEL1, Y1L
+	VSEL Y1H, T1H, SEL1, Y1H
+
+	VST Y1H, 32(P1ptr)
+	VST Y1L, 48(P1ptr)
+	RET
+
+#undef P1ptr
+#undef CPOOL
+#undef Y1L
+#undef Y1H
+#undef T1L
+#undef T1H
+#undef PL
+#undef PH
+#undef ZER
+#undef SEL1
+#undef CAR1
+
+// ---------------------------------------
+// if cond == 0 res <- b; else res <- a
+// func p256MovCond(res, a, b *p256Point, cond int)
+#define P3ptr   R1
+#define P1ptr   R2
+#define P2ptr   R3
+
+#define X1L    V0
+#define X1H    V1
+#define Y1L    V2
+#define Y1H    V3
+#define Z1L    V4
+#define Z1H    V5
+#define X2L    V6
+#define X2H    V7
+#define Y2L    V8
+#define Y2H    V9
+#define Z2L    V10
+#define Z2H    V11
+
+#define ZER   V18
+#define SEL1  V19
+TEXT ·p256MovCond(SB), NOSPLIT, $0
+	MOVD   res+0(FP), P3ptr
+	MOVD   a+8(FP), P1ptr
+	MOVD   b+16(FP), P2ptr
+	VLREPG cond+24(FP), SEL1
+	VZERO  ZER
+	VCEQG  SEL1, ZER, SEL1
+
+	VL 0(P1ptr), X1H
+	VL 16(P1ptr), X1L
+	VL 32(P1ptr), Y1H
+	VL 48(P1ptr), Y1L
+	VL 64(P1ptr), Z1H
+	VL 80(P1ptr), Z1L
+
+	VL 0(P2ptr), X2H
+	VL 16(P2ptr), X2L
+	VL 32(P2ptr), Y2H
+	VL 48(P2ptr), Y2L
+	VL 64(P2ptr), Z2H
+	VL 80(P2ptr), Z2L
+
+	VSEL X2L, X1L, SEL1, X1L
+	VSEL X2H, X1H, SEL1, X1H
+	VSEL Y2L, Y1L, SEL1, Y1L
+	VSEL Y2H, Y1H, SEL1, Y1H
+	VSEL Z2L, Z1L, SEL1, Z1L
+	VSEL Z2H, Z1H, SEL1, Z1H
+
+	VST X1H, 0(P3ptr)
+	VST X1L, 16(P3ptr)
+	VST Y1H, 32(P3ptr)
+	VST Y1L, 48(P3ptr)
+	VST Z1H, 64(P3ptr)
+	VST Z1L, 80(P3ptr)
+
+	RET
+
+#undef P3ptr
+#undef P1ptr
+#undef P2ptr
+#undef X1L
+#undef X1H
+#undef Y1L
+#undef Y1H
+#undef Z1L
+#undef Z1H
+#undef X2L
+#undef X2H
+#undef Y2L
+#undef Y2H
+#undef Z2L
+#undef Z2H
+#undef ZER
+#undef SEL1
+
+// ---------------------------------------
+// Constant time table access
+// Indexed from 1 to 15, with -1 offset
+// (index 0 is implicitly point at infinity)
+// func p256Select(point *p256Point, table []p256Point, idx int)
+#define P3ptr   R1
+#define P1ptr   R2
+#define COUNT   R4
+
+#define X1L    V0
+#define X1H    V1
+#define Y1L    V2
+#define Y1H    V3
+#define Z1L    V4
+#define Z1H    V5
+#define X2L    V6
+#define X2H    V7
+#define Y2L    V8
+#define Y2H    V9
+#define Z2L    V10
+#define Z2H    V11
+
+#define ONE   V18
+#define IDX   V19
+#define SEL1  V20
+#define SEL2  V21
+TEXT ·p256Select(SB), NOSPLIT, $0
+	MOVD   point+0(FP), P3ptr
+	MOVD   table+8(FP), P1ptr
+	VLREPB idx+(32+7)(FP), IDX
+	VREPIB $1, ONE
+	VREPIB $1, SEL2
+	MOVD   $1, COUNT
+
+	VZERO X1H
+	VZERO X1L
+	VZERO Y1H
+	VZERO Y1L
+	VZERO Z1H
+	VZERO Z1L
+
+loop_select:
+	VL 0(P1ptr), X2H
+	VL 16(P1ptr), X2L
+	VL 32(P1ptr), Y2H
+	VL 48(P1ptr), Y2L
+	VL 64(P1ptr), Z2H
+	VL 80(P1ptr), Z2L
+
+	VCEQG SEL2, IDX, SEL1
+
+	VSEL X2L, X1L, SEL1, X1L
+	VSEL X2H, X1H, SEL1, X1H
+	VSEL Y2L, Y1L, SEL1, Y1L
+	VSEL Y2H, Y1H, SEL1, Y1H
+	VSEL Z2L, Z1L, SEL1, Z1L
+	VSEL Z2H, Z1H, SEL1, Z1H
+
+	VAB  SEL2, ONE, SEL2
+	ADDW $1, COUNT
+	ADD  $96, P1ptr
+	CMPW COUNT, $17
+	BLT  loop_select
+
+	VST X1H, 0(P3ptr)
+	VST X1L, 16(P3ptr)
+	VST Y1H, 32(P3ptr)
+	VST Y1L, 48(P3ptr)
+	VST Z1H, 64(P3ptr)
+	VST Z1L, 80(P3ptr)
+	RET
+
+#undef P3ptr
+#undef P1ptr
+#undef COUNT
+#undef X1L
+#undef X1H
+#undef Y1L
+#undef Y1H
+#undef Z1L
+#undef Z1H
+#undef X2L
+#undef X2H
+#undef Y2L
+#undef Y2H
+#undef Z2L
+#undef Z2H
+#undef ONE
+#undef IDX
+#undef SEL1
+#undef SEL2
+
+// ---------------------------------------
+// Constant time table access
+// Indexed from 1 to 15, with -1 offset
+// (index 0 is implicitly point at infinity)
+// func p256SelectBase(point *p256Point, table []p256Point, idx int)
+#define P3ptr   R1
+#define P1ptr   R2
+#define COUNT   R4
+
+#define X1L    V0
+#define X1H    V1
+#define Y1L    V2
+#define Y1H    V3
+#define Z1L    V4
+#define Z1H    V5
+#define X2L    V6
+#define X2H    V7
+#define Y2L    V8
+#define Y2H    V9
+#define Z2L    V10
+#define Z2H    V11
+
+#define ONE   V18
+#define IDX   V19
+#define SEL1  V20
+#define SEL2  V21
+TEXT ·p256SelectBase(SB), NOSPLIT, $0
+	MOVD   point+0(FP), P3ptr
+	MOVD   table+8(FP), P1ptr
+	VLREPB idx+(32+7)(FP), IDX
+	VREPIB $1, ONE
+	VREPIB $1, SEL2
+	MOVD   $1, COUNT
+
+	VZERO X1H
+	VZERO X1L
+	VZERO Y1H
+	VZERO Y1L
+	VZERO Z1H
+	VZERO Z1L
+
+loop_select:
+	VL 0(P1ptr), X2H
+	VL 16(P1ptr), X2L
+	VL 32(P1ptr), Y2H
+	VL 48(P1ptr), Y2L
+	VL 64(P1ptr), Z2H
+	VL 80(P1ptr), Z2L
+
+	VCEQG SEL2, IDX, SEL1
+
+	VSEL X2L, X1L, SEL1, X1L
+	VSEL X2H, X1H, SEL1, X1H
+	VSEL Y2L, Y1L, SEL1, Y1L
+	VSEL Y2H, Y1H, SEL1, Y1H
+	VSEL Z2L, Z1L, SEL1, Z1L
+	VSEL Z2H, Z1H, SEL1, Z1H
+
+	VAB  SEL2, ONE, SEL2
+	ADDW $1, COUNT
+	ADD  $96, P1ptr
+	CMPW COUNT, $65
+	BLT  loop_select
+
+	VST X1H, 0(P3ptr)
+	VST X1L, 16(P3ptr)
+	VST Y1H, 32(P3ptr)
+	VST Y1L, 48(P3ptr)
+	VST Z1H, 64(P3ptr)
+	VST Z1L, 80(P3ptr)
+	RET
+
+#undef P3ptr
+#undef P1ptr
+#undef COUNT
+#undef X1L
+#undef X1H
+#undef Y1L
+#undef Y1H
+#undef Z1L
+#undef Z1H
+#undef X2L
+#undef X2H
+#undef Y2L
+#undef Y2H
+#undef Z2L
+#undef Z2H
+#undef ONE
+#undef IDX
+#undef SEL1
+#undef SEL2
+
+// ---------------------------------------
+// func p256FromMont(res, in []byte)
+#define res_ptr R1
+#define x_ptr   R2
+#define CPOOL   R4
+
+#define T0   V0
+#define T1   V1
+#define T2   V2
+#define TT0  V3
+#define TT1  V4
+
+#define ZER   V6
+#define SEL1  V7
+#define SEL2  V8
+#define CAR1  V9
+#define CAR2  V10
+#define RED1  V11
+#define RED2  V12
+#define PL    V13
+#define PH    V14
+
+TEXT ·p256FromMont(SB), NOSPLIT, $0
+	MOVD res+0(FP), res_ptr
+	MOVD in+24(FP), x_ptr
+
+	VZERO T2
+	VZERO ZER
+	MOVD  $p256<>+0x00(SB), CPOOL
+	VL    16(CPOOL), PL
+	VL    0(CPOOL), PH
+	VL    48(CPOOL), SEL2
+	VL    64(CPOOL), SEL1
+
+	VL (1*16)(x_ptr), T0
+	VL (0*16)(x_ptr), T1
+
+	// First round
+	VPERM T1, T0, SEL1, RED2    // d1 d0 d1 d0
+	VPERM ZER, RED2, SEL2, RED1 // 0  d1 d0  0
+	VSQ   RED1, RED2, RED2      // Guaranteed not to underflow
+
+	VSLDB $8, T1, T0, T0
+	VSLDB $8, T2, T1, T1
+
+	VACCQ  T0, RED1, CAR1
+	VAQ    T0, RED1, T0
+	VACCCQ T1, RED2, CAR1, CAR2
+	VACQ   T1, RED2, CAR1, T1
+	VAQ    T2, CAR2, T2
+
+	// Second round
+	VPERM T1, T0, SEL1, RED2    // d1 d0 d1 d0
+	VPERM ZER, RED2, SEL2, RED1 // 0  d1 d0  0
+	VSQ   RED1, RED2, RED2      // Guaranteed not to underflow
+
+	VSLDB $8, T1, T0, T0
+	VSLDB $8, T2, T1, T1
+
+	VACCQ  T0, RED1, CAR1
+	VAQ    T0, RED1, T0
+	VACCCQ T1, RED2, CAR1, CAR2
+	VACQ   T1, RED2, CAR1, T1
+	VAQ    T2, CAR2, T2
+
+	// Third round
+	VPERM T1, T0, SEL1, RED2    // d1 d0 d1 d0
+	VPERM ZER, RED2, SEL2, RED1 // 0  d1 d0  0
+	VSQ   RED1, RED2, RED2      // Guaranteed not to underflow
+
+	VSLDB $8, T1, T0, T0
+	VSLDB $8, T2, T1, T1
+
+	VACCQ  T0, RED1, CAR1
+	VAQ    T0, RED1, T0
+	VACCCQ T1, RED2, CAR1, CAR2
+	VACQ   T1, RED2, CAR1, T1
+	VAQ    T2, CAR2, T2
+
+	// Last round
+	VPERM T1, T0, SEL1, RED2    // d1 d0 d1 d0
+	VPERM ZER, RED2, SEL2, RED1 // 0  d1 d0  0
+	VSQ   RED1, RED2, RED2      // Guaranteed not to underflow
+
+	VSLDB $8, T1, T0, T0
+	VSLDB $8, T2, T1, T1
+
+	VACCQ  T0, RED1, CAR1
+	VAQ    T0, RED1, T0
+	VACCCQ T1, RED2, CAR1, CAR2
+	VACQ   T1, RED2, CAR1, T1
+	VAQ    T2, CAR2, T2
+
+	// ---------------------------------------------------
+
+	VSCBIQ  PL, T0, CAR1
+	VSQ     PL, T0, TT0
+	VSBCBIQ T1, PH, CAR1, CAR2
+	VSBIQ   T1, PH, CAR1, TT1
+	VSBIQ   T2, ZER, CAR2, T2
+
+	// what output to use, TT1||TT0 or T1||T0?
+	VSEL T0, TT0, T2, T0
+	VSEL T1, TT1, T2, T1
+
+	VST T0, (1*16)(res_ptr)
+	VST T1, (0*16)(res_ptr)
+	RET
+
+#undef res_ptr
+#undef x_ptr
+#undef CPOOL
+#undef T0
+#undef T1
+#undef T2
+#undef TT0
+#undef TT1
+#undef ZER
+#undef SEL1
+#undef SEL2
+#undef CAR1
+#undef CAR2
+#undef RED1
+#undef RED2
+#undef PL
+#undef PH
+
+// ---------------------------------------
+// func p256OrdMul(res, in1, in2 []byte)
+#define res_ptr R1
+#define x_ptr R2
+#define y_ptr R3
+#define X0    V0
+#define X1    V1
+#define Y0    V2
+#define Y1    V3
+#define M0    V4
+#define M1    V5
+#define T0    V6
+#define T1    V7
+#define T2    V8
+#define YDIG  V9
+
+#define ADD1  V16
+#define ADD1H V17
+#define ADD2  V18
+#define ADD2H V19
+#define RED1  V20
+#define RED1H V21
+#define RED2  V22
+#define RED2H V23
+#define CAR1  V24
+#define CAR1M V25
+
+#define MK0   V30
+#define K0    V31
+TEXT ·p256OrdMul(SB), NOSPLIT, $0
+	MOVD res+0(FP), res_ptr
+	MOVD in1+24(FP), x_ptr
+	MOVD in2+48(FP), y_ptr
+
+	VZERO T2
+	MOVD  $p256ordK0<>+0x00(SB), R4
+
+	// VLEF    $3, 0(R4), K0
+	WORD $0xE7F40000
+	BYTE $0x38
+	BYTE $0x03
+	MOVD $p256ord<>+0x00(SB), R4
+	VL   16(R4), M0
+	VL   0(R4), M1
+
+	VL (1*16)(x_ptr), X0
+	VL (0*16)(x_ptr), X1
+	VL (1*16)(y_ptr), Y0
+	VL (0*16)(y_ptr), Y1
+
+	// ---------------------------------------------------------------------------/
+	VREPF $3, Y0, YDIG
+	VMLF  X0, YDIG, ADD1
+	VMLF  ADD1, K0, MK0
+	VREPF $3, MK0, MK0
+
+	VMLF  X1, YDIG, ADD2
+	VMLHF X0, YDIG, ADD1H
+	VMLHF X1, YDIG, ADD2H
+
+	VMALF  M0, MK0, ADD1, RED1
+	VMALHF M0, MK0, ADD1, RED1H
+	VMALF  M1, MK0, ADD2, RED2
+	VMALHF M1, MK0, ADD2, RED2H
+
+	VSLDB $12, RED2, RED1, RED1
+	VSLDB $12, T2, RED2, RED2
+
+	VACCQ RED1, ADD1H, CAR1
+	VAQ   RED1, ADD1H, T0
+	VACCQ RED1H, T0, CAR1M
+	VAQ   RED1H, T0, T0
+
+	// << ready for next MK0
+
+	VACQ   RED2, ADD2H, CAR1, T1
+	VACCCQ RED2, ADD2H, CAR1, CAR1
+	VACCCQ RED2H, T1, CAR1M, T2
+	VACQ   RED2H, T1, CAR1M, T1
+	VAQ    CAR1, T2, T2
+
+	// ---------------------------------------------------
+/* *
+ * ---+--------+--------+
+ *  T2|   T1   |   T0   |
+ * ---+--------+--------+
+ *           *(add)*
+ *    +--------+--------+
+ *    |   X1   |   X0   |
+ *    +--------+--------+
+ *           *(mul)*
+ *    +--------+--------+
+ *    |  YDIG  |  YDIG  |
+ *    +--------+--------+
+ *           *(add)*
+ *    +--------+--------+
+ *    |   M1   |   M0   |
+ *    +--------+--------+
+ *           *(mul)*
+ *    +--------+--------+
+ *    |   MK0  |   MK0  |
+ *    +--------+--------+
+ *
+ *   ---------------------
+ *
+ *    +--------+--------+
+ *    |  ADD2  |  ADD1  |
+ *    +--------+--------+
+ *  +--------+--------+
+ *  | ADD2H  | ADD1H  |
+ *  +--------+--------+
+ *    +--------+--------+
+ *    |  RED2  |  RED1  |
+ *    +--------+--------+
+ *  +--------+--------+
+ *  | RED2H  | RED1H  |
+ *  +--------+--------+
+ */
+	VREPF $2, Y0, YDIG
+	VMALF X0, YDIG, T0, ADD1
+	VMLF  ADD1, K0, MK0
+	VREPF $3, MK0, MK0
+
+	VMALF  X1, YDIG, T1, ADD2
+	VMALHF X0, YDIG, T0, ADD1H
+	VMALHF X1, YDIG, T1, ADD2H
+
+	VMALF  M0, MK0, ADD1, RED1
+	VMALHF M0, MK0, ADD1, RED1H
+	VMALF  M1, MK0, ADD2, RED2
+	VMALHF M1, MK0, ADD2, RED2H
+
+	VSLDB $12, RED2, RED1, RED1
+	VSLDB $12, T2, RED2, RED2
+
+	VACCQ RED1, ADD1H, CAR1
+	VAQ   RED1, ADD1H, T0
+	VACCQ RED1H, T0, CAR1M
+	VAQ   RED1H, T0, T0
+
+	// << ready for next MK0
+
+	VACQ   RED2, ADD2H, CAR1, T1
+	VACCCQ RED2, ADD2H, CAR1, CAR1
+	VACCCQ RED2H, T1, CAR1M, T2
+	VACQ   RED2H, T1, CAR1M, T1
+	VAQ    CAR1, T2, T2
+
+	// ---------------------------------------------------
+	VREPF $1, Y0, YDIG
+	VMALF X0, YDIG, T0, ADD1
+	VMLF  ADD1, K0, MK0
+	VREPF $3, MK0, MK0
+
+	VMALF  X1, YDIG, T1, ADD2
+	VMALHF X0, YDIG, T0, ADD1H
+	VMALHF X1, YDIG, T1, ADD2H
+
+	VMALF  M0, MK0, ADD1, RED1
+	VMALHF M0, MK0, ADD1, RED1H
+	VMALF  M1, MK0, ADD2, RED2
+	VMALHF M1, MK0, ADD2, RED2H
+
+	VSLDB $12, RED2, RED1, RED1
+	VSLDB $12, T2, RED2, RED2
+
+	VACCQ RED1, ADD1H, CAR1
+	VAQ   RED1, ADD1H, T0
+	VACCQ RED1H, T0, CAR1M
+	VAQ   RED1H, T0, T0
+
+	// << ready for next MK0
+
+	VACQ   RED2, ADD2H, CAR1, T1
+	VACCCQ RED2, ADD2H, CAR1, CAR1
+	VACCCQ RED2H, T1, CAR1M, T2
+	VACQ   RED2H, T1, CAR1M, T1
+	VAQ    CAR1, T2, T2
+
+	// ---------------------------------------------------
+	VREPF $0, Y0, YDIG
+	VMALF X0, YDIG, T0, ADD1
+	VMLF  ADD1, K0, MK0
+	VREPF $3, MK0, MK0
+
+	VMALF  X1, YDIG, T1, ADD2
+	VMALHF X0, YDIG, T0, ADD1H
+	VMALHF X1, YDIG, T1, ADD2H
+
+	VMALF  M0, MK0, ADD1, RED1
+	VMALHF M0, MK0, ADD1, RED1H
+	VMALF  M1, MK0, ADD2, RED2
+	VMALHF M1, MK0, ADD2, RED2H
+
+	VSLDB $12, RED2, RED1, RED1
+	VSLDB $12, T2, RED2, RED2
+
+	VACCQ RED1, ADD1H, CAR1
+	VAQ   RED1, ADD1H, T0
+	VACCQ RED1H, T0, CAR1M
+	VAQ   RED1H, T0, T0
+
+	// << ready for next MK0
+
+	VACQ   RED2, ADD2H, CAR1, T1
+	VACCCQ RED2, ADD2H, CAR1, CAR1
+	VACCCQ RED2H, T1, CAR1M, T2
+	VACQ   RED2H, T1, CAR1M, T1
+	VAQ    CAR1, T2, T2
+
+	// ---------------------------------------------------
+	VREPF $3, Y1, YDIG
+	VMALF X0, YDIG, T0, ADD1
+	VMLF  ADD1, K0, MK0
+	VREPF $3, MK0, MK0
+
+	VMALF  X1, YDIG, T1, ADD2
+	VMALHF X0, YDIG, T0, ADD1H
+	VMALHF X1, YDIG, T1, ADD2H
+
+	VMALF  M0, MK0, ADD1, RED1
+	VMALHF M0, MK0, ADD1, RED1H
+	VMALF  M1, MK0, ADD2, RED2
+	VMALHF M1, MK0, ADD2, RED2H
+
+	VSLDB $12, RED2, RED1, RED1
+	VSLDB $12, T2, RED2, RED2
+
+	VACCQ RED1, ADD1H, CAR1
+	VAQ   RED1, ADD1H, T0
+	VACCQ RED1H, T0, CAR1M
+	VAQ   RED1H, T0, T0
+
+	// << ready for next MK0
+
+	VACQ   RED2, ADD2H, CAR1, T1
+	VACCCQ RED2, ADD2H, CAR1, CAR1
+	VACCCQ RED2H, T1, CAR1M, T2
+	VACQ   RED2H, T1, CAR1M, T1
+	VAQ    CAR1, T2, T2
+
+	// ---------------------------------------------------
+	VREPF $2, Y1, YDIG
+	VMALF X0, YDIG, T0, ADD1
+	VMLF  ADD1, K0, MK0
+	VREPF $3, MK0, MK0
+
+	VMALF  X1, YDIG, T1, ADD2
+	VMALHF X0, YDIG, T0, ADD1H
+	VMALHF X1, YDIG, T1, ADD2H
+
+	VMALF  M0, MK0, ADD1, RED1
+	VMALHF M0, MK0, ADD1, RED1H
+	VMALF  M1, MK0, ADD2, RED2
+	VMALHF M1, MK0, ADD2, RED2H
+
+	VSLDB $12, RED2, RED1, RED1
+	VSLDB $12, T2, RED2, RED2
+
+	VACCQ RED1, ADD1H, CAR1
+	VAQ   RED1, ADD1H, T0
+	VACCQ RED1H, T0, CAR1M
+	VAQ   RED1H, T0, T0
+
+	// << ready for next MK0
+
+	VACQ   RED2, ADD2H, CAR1, T1
+	VACCCQ RED2, ADD2H, CAR1, CAR1
+	VACCCQ RED2H, T1, CAR1M, T2
+	VACQ   RED2H, T1, CAR1M, T1
+	VAQ    CAR1, T2, T2
+
+	// ---------------------------------------------------
+	VREPF $1, Y1, YDIG
+	VMALF X0, YDIG, T0, ADD1
+	VMLF  ADD1, K0, MK0
+	VREPF $3, MK0, MK0
+
+	VMALF  X1, YDIG, T1, ADD2
+	VMALHF X0, YDIG, T0, ADD1H
+	VMALHF X1, YDIG, T1, ADD2H
+
+	VMALF  M0, MK0, ADD1, RED1
+	VMALHF M0, MK0, ADD1, RED1H
+	VMALF  M1, MK0, ADD2, RED2
+	VMALHF M1, MK0, ADD2, RED2H
+
+	VSLDB $12, RED2, RED1, RED1
+	VSLDB $12, T2, RED2, RED2
+
+	VACCQ RED1, ADD1H, CAR1
+	VAQ   RED1, ADD1H, T0
+	VACCQ RED1H, T0, CAR1M
+	VAQ   RED1H, T0, T0
+
+	// << ready for next MK0
+
+	VACQ   RED2, ADD2H, CAR1, T1
+	VACCCQ RED2, ADD2H, CAR1, CAR1
+	VACCCQ RED2H, T1, CAR1M, T2
+	VACQ   RED2H, T1, CAR1M, T1
+	VAQ    CAR1, T2, T2
+
+	// ---------------------------------------------------
+	VREPF $0, Y1, YDIG
+	VMALF X0, YDIG, T0, ADD1
+	VMLF  ADD1, K0, MK0
+	VREPF $3, MK0, MK0
+
+	VMALF  X1, YDIG, T1, ADD2
+	VMALHF X0, YDIG, T0, ADD1H
+	VMALHF X1, YDIG, T1, ADD2H
+
+	VMALF  M0, MK0, ADD1, RED1
+	VMALHF M0, MK0, ADD1, RED1H
+	VMALF  M1, MK0, ADD2, RED2
+	VMALHF M1, MK0, ADD2, RED2H
+
+	VSLDB $12, RED2, RED1, RED1
+	VSLDB $12, T2, RED2, RED2
+
+	VACCQ RED1, ADD1H, CAR1
+	VAQ   RED1, ADD1H, T0
+	VACCQ RED1H, T0, CAR1M
+	VAQ   RED1H, T0, T0
+
+	// << ready for next MK0
+
+	VACQ   RED2, ADD2H, CAR1, T1
+	VACCCQ RED2, ADD2H, CAR1, CAR1
+	VACCCQ RED2H, T1, CAR1M, T2
+	VACQ   RED2H, T1, CAR1M, T1
+	VAQ    CAR1, T2, T2
+
+	// ---------------------------------------------------
+
+	VZERO   RED1
+	VSCBIQ  M0, T0, CAR1
+	VSQ     M0, T0, ADD1
+	VSBCBIQ T1, M1, CAR1, CAR1M
+	VSBIQ   T1, M1, CAR1, ADD2
+	VSBIQ   T2, RED1, CAR1M, T2
+
+	// what output to use, ADD2||ADD1 or T1||T0?
+	VSEL T0, ADD1, T2, T0
+	VSEL T1, ADD2, T2, T1
+
+	VST T0, (1*16)(res_ptr)
+	VST T1, (0*16)(res_ptr)
+	RET
+
+#undef res_ptr
+#undef x_ptr
+#undef y_ptr
+#undef X0
+#undef X1
+#undef Y0
+#undef Y1
+#undef M0
+#undef M1
+#undef T0
+#undef T1
+#undef T2
+#undef YDIG
+
+#undef ADD1
+#undef ADD1H
+#undef ADD2
+#undef ADD2H
+#undef RED1
+#undef RED1H
+#undef RED2
+#undef RED2H
+#undef CAR1
+#undef CAR1M
+
+#undef MK0
+#undef K0
+
+// ---------------------------------------
+// p256MulInternal
+// V0-V3,V30,V31 - Not Modified
+// V4-V15 - Volatile
+
+#define CPOOL   R4
+
+// Parameters
+#define X0    V0 // Not modified
+#define X1    V1 // Not modified
+#define Y0    V2 // Not modified
+#define Y1    V3 // Not modified
+#define T0    V4
+#define T1    V5
+#define P0    V30 // Not modified
+#define P1    V31 // Not modified
+
+// Temporaries
+#define YDIG  V6 // Overloaded with CAR2, ZER
+#define ADD1H V7 // Overloaded with ADD3H
+#define ADD2H V8 // Overloaded with ADD4H
+#define ADD3  V9 // Overloaded with SEL2,SEL5
+#define ADD4  V10 // Overloaded with SEL3,SEL6
+#define RED1  V11 // Overloaded with CAR2
+#define RED2  V12
+#define RED3  V13 // Overloaded with SEL1
+#define T2    V14
+// Overloaded temporaries
+#define ADD1  V4 // Overloaded with T0
+#define ADD2  V5 // Overloaded with T1
+#define ADD3H V7 // Overloaded with ADD1H
+#define ADD4H V8 // Overloaded with ADD2H
+#define ZER   V6 // Overloaded with YDIG, CAR2
+#define CAR1  V6 // Overloaded with YDIG, ZER
+#define CAR2  V11 // Overloaded with RED1
+// Constant Selects
+#define SEL1  V13 // Overloaded with RED3
+#define SEL2  V9 // Overloaded with ADD3,SEL5
+#define SEL3  V10 // Overloaded with ADD4,SEL6
+#define SEL4  V6 // Overloaded with YDIG,CAR2,ZER
+#define SEL5  V9 // Overloaded with ADD3,SEL2
+#define SEL6  V10 // Overloaded with ADD4,SEL3
+
+/* *
+ * To follow the flow of bits, for your own sanity a stiff drink, need you shall.
+ * Of a single round, a 'helpful' picture, here is. Meaning, column position has.
+ * With you, SIMD be...
+ *
+ *                                           +--------+--------+
+ *                                  +--------|  RED2  |  RED1  |
+ *                                  |        +--------+--------+
+ *                                  |       ---+--------+--------+
+ *                                  |  +---- T2|   T1   |   T0   |--+
+ *                                  |  |    ---+--------+--------+  |
+ *                                  |  |                            |
+ *                                  |  |    ======================= |
+ *                                  |  |                            |
+ *                                  |  |       +--------+--------+<-+
+ *                                  |  +-------|  ADD2  |  ADD1  |--|-----+
+ *                                  |  |       +--------+--------+  |     |
+ *                                  |  |     +--------+--------+<---+     |
+ *                                  |  |     | ADD2H  | ADD1H  |--+       |
+ *                                  |  |     +--------+--------+  |       |
+ *                                  |  |     +--------+--------+<-+       |
+ *                                  |  |     |  ADD4  |  ADD3  |--|-+     |
+ *                                  |  |     +--------+--------+  | |     |
+ *                                  |  |   +--------+--------+<---+ |     |
+ *                                  |  |   | ADD4H  | ADD3H  |------|-+   |(+vzero)
+ *                                  |  |   +--------+--------+      | |   V
+ *                                  |  | ------------------------   | | +--------+
+ *                                  |  |                            | | |  RED3  |  [d0 0 0 d0]
+ *                                  |  |                            | | +--------+
+ *                                  |  +---->+--------+--------+    | |   |
+ *   (T2[1w]||ADD2[4w]||ADD1[3w])   +--------|   T1   |   T0   |    | |   |
+ *                                  |        +--------+--------+    | |   |
+ *                                  +---->---+--------+--------+    | |   |
+ *                                         T2|   T1   |   T0   |----+ |   |
+ *                                        ---+--------+--------+    | |   |
+ *                                        ---+--------+--------+<---+ |   |
+ *                                    +--- T2|   T1   |   T0   |----------+
+ *                                    |   ---+--------+--------+      |   |
+ *                                    |  +--------+--------+<-------------+
+ *                                    |  |  RED2  |  RED1  |-----+    |   | [0 d1 d0 d1] [d0 0 d1 d0]
+ *                                    |  +--------+--------+     |    |   |
+ *                                    |  +--------+<----------------------+
+ *                                    |  |  RED3  |--------------+    |     [0 0 d1 d0]
+ *                                    |  +--------+              |    |
+ *                                    +--->+--------+--------+   |    |
+ *                                         |   T1   |   T0   |--------+
+ *                                         +--------+--------+   |    |
+ *                                   --------------------------- |    |
+ *                                                               |    |
+ *                                       +--------+--------+<----+    |
+ *                                       |  RED2  |  RED1  |          |
+ *                                       +--------+--------+          |
+ *                                      ---+--------+--------+<-------+
+ *                                       T2|   T1   |   T0   |            (H1P-H1P-H00RRAY!)
+ *                                      ---+--------+--------+
+ *
+ *                                                                *Mi obra de arte de siglo XXI @vpaprots
+ *
+ *
+ * First group is special, doesnt get the two inputs:
+ *                                             +--------+--------+<-+
+ *                                     +-------|  ADD2  |  ADD1  |--|-----+
+ *                                     |       +--------+--------+  |     |
+ *                                     |     +--------+--------+<---+     |
+ *                                     |     | ADD2H  | ADD1H  |--+       |
+ *                                     |     +--------+--------+  |       |
+ *                                     |     +--------+--------+<-+       |
+ *                                     |     |  ADD4  |  ADD3  |--|-+     |
+ *                                     |     +--------+--------+  | |     |
+ *                                     |   +--------+--------+<---+ |     |
+ *                                     |   | ADD4H  | ADD3H  |------|-+   |(+vzero)
+ *                                     |   +--------+--------+      | |   V
+ *                                     | ------------------------   | | +--------+
+ *                                     |                            | | |  RED3  |  [d0 0 0 d0]
+ *                                     |                            | | +--------+
+ *                                     +---->+--------+--------+    | |   |
+ *   (T2[1w]||ADD2[4w]||ADD1[3w])            |   T1   |   T0   |----+ |   |
+ *                                           +--------+--------+    | |   |
+ *                                        ---+--------+--------+<---+ |   |
+ *                                    +--- T2|   T1   |   T0   |----------+
+ *                                    |   ---+--------+--------+      |   |
+ *                                    |  +--------+--------+<-------------+
+ *                                    |  |  RED2  |  RED1  |-----+    |   | [0 d1 d0 d1] [d0 0 d1 d0]
+ *                                    |  +--------+--------+     |    |   |
+ *                                    |  +--------+<----------------------+
+ *                                    |  |  RED3  |--------------+    |     [0 0 d1 d0]
+ *                                    |  +--------+              |    |
+ *                                    +--->+--------+--------+   |    |
+ *                                         |   T1   |   T0   |--------+
+ *                                         +--------+--------+   |    |
+ *                                   --------------------------- |    |
+ *                                                               |    |
+ *                                       +--------+--------+<----+    |
+ *                                       |  RED2  |  RED1  |          |
+ *                                       +--------+--------+          |
+ *                                      ---+--------+--------+<-------+
+ *                                       T2|   T1   |   T0   |            (H1P-H1P-H00RRAY!)
+ *                                      ---+--------+--------+
+ *
+ * Last 'group' needs to RED2||RED1 shifted less
+ */
+TEXT p256MulInternal<>(SB), NOSPLIT, $0-0
+	VL 32(CPOOL), SEL1
+	VL 48(CPOOL), SEL2
+	VL 64(CPOOL), SEL3
+	VL 80(CPOOL), SEL4
+
+	// ---------------------------------------------------
+
+	VREPF $3, Y0, YDIG
+	VMLHF X0, YDIG, ADD1H
+	VMLHF X1, YDIG, ADD2H
+	VMLF  X0, YDIG, ADD1
+	VMLF  X1, YDIG, ADD2
+
+	VREPF  $2, Y0, YDIG
+	VMALF  X0, YDIG, ADD1H, ADD3
+	VMALF  X1, YDIG, ADD2H, ADD4
+	VMALHF X0, YDIG, ADD1H, ADD3H // ADD1H Free
+	VMALHF X1, YDIG, ADD2H, ADD4H // ADD2H Free
+
+	VZERO ZER
+	VL    32(CPOOL), SEL1
+	VPERM ZER, ADD1, SEL1, RED3 // [d0 0 0 d0]
+
+	VSLDB $12, ADD2, ADD1, T0 // ADD1 Free
+	VSLDB $12, ZER, ADD2, T1  // ADD2 Free
+
+	VACCQ  T0, ADD3, CAR1
+	VAQ    T0, ADD3, T0       // ADD3 Free
+	VACCCQ T1, ADD4, CAR1, T2
+	VACQ   T1, ADD4, CAR1, T1 // ADD4 Free
+
+	VL    48(CPOOL), SEL2
+	VL    64(CPOOL), SEL3
+	VL    80(CPOOL), SEL4
+	VPERM RED3, T0, SEL2, RED1 // [d0  0 d1 d0]
+	VPERM RED3, T0, SEL3, RED2 // [ 0 d1 d0 d1]
+	VPERM RED3, T0, SEL4, RED3 // [ 0  0 d1 d0]
+	VSQ   RED3, RED2, RED2     // Guaranteed not to underflow
+
+	VSLDB $12, T1, T0, T0
+	VSLDB $12, T2, T1, T1
+
+	VACCQ  T0, ADD3H, CAR1
+	VAQ    T0, ADD3H, T0
+	VACCCQ T1, ADD4H, CAR1, T2
+	VACQ   T1, ADD4H, CAR1, T1
+
+	// ---------------------------------------------------
+
+	VREPF  $1, Y0, YDIG
+	VMALHF X0, YDIG, T0, ADD1H
+	VMALHF X1, YDIG, T1, ADD2H
+	VMALF  X0, YDIG, T0, ADD1  // T0 Free->ADD1
+	VMALF  X1, YDIG, T1, ADD2  // T1 Free->ADD2
+
+	VREPF  $0, Y0, YDIG
+	VMALF  X0, YDIG, ADD1H, ADD3
+	VMALF  X1, YDIG, ADD2H, ADD4
+	VMALHF X0, YDIG, ADD1H, ADD3H // ADD1H Free->ADD3H
+	VMALHF X1, YDIG, ADD2H, ADD4H // ADD2H Free->ADD4H , YDIG Free->ZER
+
+	VZERO ZER
+	VL    32(CPOOL), SEL1
+	VPERM ZER, ADD1, SEL1, RED3 // [d0 0 0 d0]
+
+	VSLDB $12, ADD2, ADD1, T0 // ADD1 Free->T0
+	VSLDB $12, T2, ADD2, T1   // ADD2 Free->T1, T2 Free
+
+	VACCQ  T0, RED1, CAR1
+	VAQ    T0, RED1, T0
+	VACCCQ T1, RED2, CAR1, T2
+	VACQ   T1, RED2, CAR1, T1
+
+	VACCQ  T0, ADD3, CAR1
+	VAQ    T0, ADD3, T0
+	VACCCQ T1, ADD4, CAR1, CAR2
+	VACQ   T1, ADD4, CAR1, T1
+	VAQ    T2, CAR2, T2
+
+	VL    48(CPOOL), SEL2
+	VL    64(CPOOL), SEL3
+	VL    80(CPOOL), SEL4
+	VPERM RED3, T0, SEL2, RED1 // [d0  0 d1 d0]
+	VPERM RED3, T0, SEL3, RED2 // [ 0 d1 d0 d1]
+	VPERM RED3, T0, SEL4, RED3 // [ 0  0 d1 d0]
+	VSQ   RED3, RED2, RED2     // Guaranteed not to underflow
+
+	VSLDB $12, T1, T0, T0
+	VSLDB $12, T2, T1, T1
+
+	VACCQ  T0, ADD3H, CAR1
+	VAQ    T0, ADD3H, T0
+	VACCCQ T1, ADD4H, CAR1, T2
+	VACQ   T1, ADD4H, CAR1, T1
+
+	// ---------------------------------------------------
+
+	VREPF  $3, Y1, YDIG
+	VMALHF X0, YDIG, T0, ADD1H
+	VMALHF X1, YDIG, T1, ADD2H
+	VMALF  X0, YDIG, T0, ADD1
+	VMALF  X1, YDIG, T1, ADD2
+
+	VREPF  $2, Y1, YDIG
+	VMALF  X0, YDIG, ADD1H, ADD3
+	VMALF  X1, YDIG, ADD2H, ADD4
+	VMALHF X0, YDIG, ADD1H, ADD3H // ADD1H Free
+	VMALHF X1, YDIG, ADD2H, ADD4H // ADD2H Free
+
+	VZERO ZER
+	VL    32(CPOOL), SEL1
+	VPERM ZER, ADD1, SEL1, RED3 // [d0 0 0 d0]
+
+	VSLDB $12, ADD2, ADD1, T0 // ADD1 Free
+	VSLDB $12, T2, ADD2, T1   // ADD2 Free
+
+	VACCQ  T0, RED1, CAR1
+	VAQ    T0, RED1, T0
+	VACCCQ T1, RED2, CAR1, T2
+	VACQ   T1, RED2, CAR1, T1
+
+	VACCQ  T0, ADD3, CAR1
+	VAQ    T0, ADD3, T0
+	VACCCQ T1, ADD4, CAR1, CAR2
+	VACQ   T1, ADD4, CAR1, T1
+	VAQ    T2, CAR2, T2
+
+	VL    48(CPOOL), SEL2
+	VL    64(CPOOL), SEL3
+	VL    80(CPOOL), SEL4
+	VPERM RED3, T0, SEL2, RED1 // [d0  0 d1 d0]
+	VPERM RED3, T0, SEL3, RED2 // [ 0 d1 d0 d1]
+	VPERM RED3, T0, SEL4, RED3 // [ 0  0 d1 d0]
+	VSQ   RED3, RED2, RED2     // Guaranteed not to underflow
+
+	VSLDB $12, T1, T0, T0
+	VSLDB $12, T2, T1, T1
+
+	VACCQ  T0, ADD3H, CAR1
+	VAQ    T0, ADD3H, T0
+	VACCCQ T1, ADD4H, CAR1, T2
+	VACQ   T1, ADD4H, CAR1, T1
+
+	// ---------------------------------------------------
+
+	VREPF  $1, Y1, YDIG
+	VMALHF X0, YDIG, T0, ADD1H
+	VMALHF X1, YDIG, T1, ADD2H
+	VMALF  X0, YDIG, T0, ADD1
+	VMALF  X1, YDIG, T1, ADD2
+
+	VREPF  $0, Y1, YDIG
+	VMALF  X0, YDIG, ADD1H, ADD3
+	VMALF  X1, YDIG, ADD2H, ADD4
+	VMALHF X0, YDIG, ADD1H, ADD3H
+	VMALHF X1, YDIG, ADD2H, ADD4H
+
+	VZERO ZER
+	VL    32(CPOOL), SEL1
+	VPERM ZER, ADD1, SEL1, RED3 // [d0 0 0 d0]
+
+	VSLDB $12, ADD2, ADD1, T0
+	VSLDB $12, T2, ADD2, T1
+
+	VACCQ  T0, RED1, CAR1
+	VAQ    T0, RED1, T0
+	VACCCQ T1, RED2, CAR1, T2
+	VACQ   T1, RED2, CAR1, T1
+
+	VACCQ  T0, ADD3, CAR1
+	VAQ    T0, ADD3, T0
+	VACCCQ T1, ADD4, CAR1, CAR2
+	VACQ   T1, ADD4, CAR1, T1
+	VAQ    T2, CAR2, T2
+
+	VL    96(CPOOL), SEL5
+	VL    112(CPOOL), SEL6
+	VPERM T0, RED3, SEL5, RED2 // [d1 d0 d1 d0]
+	VPERM T0, RED3, SEL6, RED1 // [ 0 d1 d0  0]
+	VSQ   RED1, RED2, RED2     // Guaranteed not to underflow
+
+	VSLDB $12, T1, T0, T0
+	VSLDB $12, T2, T1, T1
+
+	VACCQ  T0, ADD3H, CAR1
+	VAQ    T0, ADD3H, T0
+	VACCCQ T1, ADD4H, CAR1, T2
+	VACQ   T1, ADD4H, CAR1, T1
+
+	VACCQ  T0, RED1, CAR1
+	VAQ    T0, RED1, T0
+	VACCCQ T1, RED2, CAR1, CAR2
+	VACQ   T1, RED2, CAR1, T1
+	VAQ    T2, CAR2, T2
+
+	// ---------------------------------------------------
+
+	VZERO   RED3
+	VSCBIQ  P0, T0, CAR1
+	VSQ     P0, T0, ADD1H
+	VSBCBIQ T1, P1, CAR1, CAR2
+	VSBIQ   T1, P1, CAR1, ADD2H
+	VSBIQ   T2, RED3, CAR2, T2
+
+	// what output to use, ADD2H||ADD1H or T1||T0?
+	VSEL T0, ADD1H, T2, T0
+	VSEL T1, ADD2H, T2, T1
+	RET
+
+#undef CPOOL
+
+#undef X0
+#undef X1
+#undef Y0
+#undef Y1
+#undef T0
+#undef T1
+#undef P0
+#undef P1
+
+#undef SEL1
+#undef SEL2
+#undef SEL3
+#undef SEL4
+#undef SEL5
+#undef SEL6
+
+#undef YDIG
+#undef ADD1H
+#undef ADD2H
+#undef ADD3
+#undef ADD4
+#undef RED1
+#undef RED2
+#undef RED3
+#undef T2
+#undef ADD1
+#undef ADD2
+#undef ADD3H
+#undef ADD4H
+#undef ZER
+#undef CAR1
+#undef CAR2
+
+#define p256SubInternal(T1, T0, X1, X0, Y1, Y0) \
+	VZERO   ZER                \
+	VSCBIQ  Y0, X0, CAR1       \
+	VSQ     Y0, X0, T0         \
+	VSBCBIQ X1, Y1, CAR1, SEL1 \
+	VSBIQ   X1, Y1, CAR1, T1   \
+	VSQ     SEL1, ZER, SEL1    \
+	                           \
+	VACCQ   T0, PL, CAR1       \
+	VAQ     T0, PL, TT0        \
+	VACQ    T1, PH, CAR1, TT1  \
+	                           \
+	VSEL    T0, TT0, SEL1, T0  \
+	VSEL    T1, TT1, SEL1, T1  \
+
+#define p256AddInternal(T1, T0, X1, X0, Y1, Y0) \
+	VACCQ   X0, Y0, CAR1        \
+	VAQ     X0, Y0, T0          \
+	VACCCQ  X1, Y1, CAR1, T2    \
+	VACQ    X1, Y1, CAR1, T1    \
+	                            \
+	VZERO   ZER                 \
+	VSCBIQ  PL, T0, CAR1        \
+	VSQ     PL, T0, TT0         \
+	VSBCBIQ T1, PH, CAR1, CAR2  \
+	VSBIQ   T1, PH, CAR1, TT1   \
+	VSBIQ   T2, ZER, CAR2, SEL1 \
+	                            \
+	VSEL    T0, TT0, SEL1, T0   \
+	VSEL    T1, TT1, SEL1, T1
+
+#define p256HalfInternal(T1, T0, X1, X0) \
+	VZERO  ZER                \
+	VSBIQ  ZER, ZER, X0, SEL1 \
+	                          \
+	VACCQ  X0, PL, CAR1       \
+	VAQ    X0, PL, T0         \
+	VACCCQ X1, PH, CAR1, T2   \
+	VACQ   X1, PH, CAR1, T1   \
+	                          \
+	VSEL   X0, T0, SEL1, T0   \
+	VSEL   X1, T1, SEL1, T1   \
+	VSEL   ZER, T2, SEL1, T2  \
+	                          \
+	VSLDB  $15, T2, ZER, TT1  \
+	VSLDB  $15, T1, ZER, TT0  \
+	VREPIB $1, SEL1           \
+	VSRL   SEL1, T0, T0       \
+	VSRL   SEL1, T1, T1       \
+	VREPIB $7, SEL1           \
+	VSL    SEL1, TT0, TT0     \
+	VSL    SEL1, TT1, TT1     \
+	VO     T0, TT0, T0        \
+	VO     T1, TT1, T1
+
+// ---------------------------------------
+// func p256MulAsm(res, in1, in2 []byte)
+#define res_ptr R1
+#define x_ptr   R2
+#define y_ptr   R3
+#define CPOOL   R4
+
+// Parameters
+#define X0    V0
+#define X1    V1
+#define Y0    V2
+#define Y1    V3
+#define T0    V4
+#define T1    V5
+
+// Constants
+#define P0    V30
+#define P1    V31
+TEXT ·p256MulAsm(SB), NOSPLIT, $0
+	MOVD res+0(FP), res_ptr
+	MOVD in1+24(FP), x_ptr
+	MOVD in2+48(FP), y_ptr
+
+	VL (1*16)(x_ptr), X0
+	VL (0*16)(x_ptr), X1
+	VL (1*16)(y_ptr), Y0
+	VL (0*16)(y_ptr), Y1
+
+	MOVD $p256mul<>+0x00(SB), CPOOL
+	VL   16(CPOOL), P0
+	VL   0(CPOOL), P1
+
+	CALL p256MulInternal<>(SB)
+
+	VST T0, (1*16)(res_ptr)
+	VST T1, (0*16)(res_ptr)
+	RET
+
+#undef res_ptr
+#undef x_ptr
+#undef y_ptr
+#undef CPOOL
+
+#undef X0
+#undef X1
+#undef Y0
+#undef Y1
+#undef T0
+#undef T1
+#undef P0
+#undef P1
+
+// Point add with P2 being affine point
+// If sign == 1 -> P2 = -P2
+// If sel == 0 -> P3 = P1
+// if zero == 0 -> P3 = P2
+// p256PointAddAffineAsm(P3, P1, P2 *p256Point, sign, sel, zero int)
+#define P3ptr   R1
+#define P1ptr   R2
+#define P2ptr   R3
+#define CPOOL   R4
+
+// Temporaries in REGs
+#define Y2L    V15
+#define Y2H    V16
+#define T1L    V17
+#define T1H    V18
+#define T2L    V19
+#define T2H    V20
+#define T3L    V21
+#define T3H    V22
+#define T4L    V23
+#define T4H    V24
+
+// Temps for Sub and Add
+#define TT0  V11
+#define TT1  V12
+#define T2   V13
+
+// p256MulAsm Parameters
+#define X0    V0
+#define X1    V1
+#define Y0    V2
+#define Y1    V3
+#define T0    V4
+#define T1    V5
+
+#define PL    V30
+#define PH    V31
+
+// Names for zero/sel selects
+#define X1L    V0
+#define X1H    V1
+#define Y1L    V2 // p256MulAsmParmY
+#define Y1H    V3 // p256MulAsmParmY
+#define Z1L    V4
+#define Z1H    V5
+#define X2L    V0
+#define X2H    V1
+#define Z2L    V4
+#define Z2H    V5
+#define X3L    V17 // T1L
+#define X3H    V18 // T1H
+#define Y3L    V21 // T3L
+#define Y3H    V22 // T3H
+#define Z3L    V28
+#define Z3H    V29
+
+#define ZER   V6
+#define SEL1  V7
+#define CAR1  V8
+#define CAR2  V9
+/* *
+ * Three operand formula:
+ * Source: 2004 Hankerson–Menezes–Vanstone, page 91.
+ * T1 = Z1²
+ * T2 = T1*Z1
+ * T1 = T1*X2
+ * T2 = T2*Y2
+ * T1 = T1-X1
+ * T2 = T2-Y1
+ * Z3 = Z1*T1
+ * T3 = T1²
+ * T4 = T3*T1
+ * T3 = T3*X1
+ * T1 = 2*T3
+ * X3 = T2²
+ * X3 = X3-T1
+ * X3 = X3-T4
+ * T3 = T3-X3
+ * T3 = T3*T2
+ * T4 = T4*Y1
+ * Y3 = T3-T4
+
+ * Three operand formulas, but with MulInternal X,Y used to store temps
+X=Z1; Y=Z1; MUL;T-   // T1 = Z1²      T1
+X=T ; Y-  ; MUL;T2=T // T2 = T1*Z1    T1   T2
+X-  ; Y=X2; MUL;T1=T // T1 = T1*X2    T1   T2
+X=T2; Y=Y2; MUL;T-   // T2 = T2*Y2    T1   T2
+SUB(T2<T-Y1)         // T2 = T2-Y1    T1   T2
+SUB(Y<T1-X1)         // T1 = T1-X1    T1   T2
+X=Z1; Y- ;  MUL;Z3:=T// Z3 = Z1*T1         T2
+X=Y;  Y- ;  MUL;X=T  // T3 = T1*T1         T2
+X- ;  Y- ;  MUL;T4=T // T4 = T3*T1         T2        T4
+X- ;  Y=X1; MUL;T3=T // T3 = T3*X1         T2   T3   T4
+ADD(T1<T+T)          // T1 = T3+T3    T1   T2   T3   T4
+X=T2; Y=T2; MUL;T-   // X3 = T2*T2    T1   T2   T3   T4
+SUB(T<T-T1)          // X3 = X3-T1    T1   T2   T3   T4
+SUB(T<T-T4) X3:=T    // X3 = X3-T4         T2   T3   T4
+SUB(X<T3-T)          // T3 = T3-X3         T2   T3   T4
+X- ;  Y- ;  MUL;T3=T // T3 = T3*T2         T2   T3   T4
+X=T4; Y=Y1; MUL;T-   // T4 = T4*Y1              T3   T4
+SUB(T<T3-T) Y3:=T    // Y3 = T3-T4              T3   T4
+
+	*/
+TEXT ·p256PointAddAffineAsm(SB), NOSPLIT, $0
+	MOVD P3+0(FP), P3ptr
+	MOVD P1+8(FP), P1ptr
+	MOVD P2+16(FP), P2ptr
+
+	MOVD $p256mul<>+0x00(SB), CPOOL
+	VL   16(CPOOL), PL
+	VL   0(CPOOL), PH
+
+	//	if (sign == 1) {
+	//		Y2 = fromBig(new(big.Int).Mod(new(big.Int).Sub(p256.P, new(big.Int).SetBytes(Y2)), p256.P)) // Y2  = P-Y2
+	//	}
+
+	VL 32(P2ptr), Y2H
+	VL 48(P2ptr), Y2L
+
+	VLREPG sign+24(FP), SEL1
+	VZERO  ZER
+	VCEQG  SEL1, ZER, SEL1
+
+	VSCBIQ Y2L, PL, CAR1
+	VSQ    Y2L, PL, T1L
+	VSBIQ  PH, Y2H, CAR1, T1H
+
+	VSEL Y2L, T1L, SEL1, Y2L
+	VSEL Y2H, T1H, SEL1, Y2H
+
+/* *
+ * Three operand formula:
+ * Source: 2004 Hankerson–Menezes–Vanstone, page 91.
+ */
+	// X=Z1; Y=Z1; MUL; T-   // T1 = Z1²      T1
+	VL   64(P1ptr), X1       // Z1H
+	VL   80(P1ptr), X0       // Z1L
+	VLR  X0, Y0
+	VLR  X1, Y1
+	CALL p256MulInternal<>(SB)
+
+	// X=T ; Y-  ; MUL; T2=T // T2 = T1*Z1    T1   T2
+	VLR  T0, X0
+	VLR  T1, X1
+	CALL p256MulInternal<>(SB)
+	VLR  T0, T2L
+	VLR  T1, T2H
+
+	// X-  ; Y=X2; MUL; T1=T // T1 = T1*X2    T1   T2
+	VL   0(P2ptr), Y1        // X2H
+	VL   16(P2ptr), Y0       // X2L
+	CALL p256MulInternal<>(SB)
+	VLR  T0, T1L
+	VLR  T1, T1H
+
+	// X=T2; Y=Y2; MUL; T-   // T2 = T2*Y2    T1   T2
+	VLR  T2L, X0
+	VLR  T2H, X1
+	VLR  Y2L, Y0
+	VLR  Y2H, Y1
+	CALL p256MulInternal<>(SB)
+
+	// SUB(T2<T-Y1)          // T2 = T2-Y1    T1   T2
+	VL 32(P1ptr), Y1H
+	VL 48(P1ptr), Y1L
+	p256SubInternal(T2H,T2L,T1,T0,Y1H,Y1L)
+
+	// SUB(Y<T1-X1)          // T1 = T1-X1    T1   T2
+	VL 0(P1ptr), X1H
+	VL 16(P1ptr), X1L
+	p256SubInternal(Y1,Y0,T1H,T1L,X1H,X1L)
+
+	// X=Z1; Y- ;  MUL; Z3:=T// Z3 = Z1*T1         T2
+	VL   64(P1ptr), X1       // Z1H
+	VL   80(P1ptr), X0       // Z1L
+	CALL p256MulInternal<>(SB)
+
+	// VST T1, 64(P3ptr)
+	// VST T0, 80(P3ptr)
+	VLR T0, Z3L
+	VLR T1, Z3H
+
+	// X=Y;  Y- ;  MUL; X=T  // T3 = T1*T1         T2
+	VLR  Y0, X0
+	VLR  Y1, X1
+	CALL p256MulInternal<>(SB)
+	VLR  T0, X0
+	VLR  T1, X1
+
+	// X- ;  Y- ;  MUL; T4=T // T4 = T3*T1         T2        T4
+	CALL p256MulInternal<>(SB)
+	VLR  T0, T4L
+	VLR  T1, T4H
+
+	// X- ;  Y=X1; MUL; T3=T // T3 = T3*X1         T2   T3   T4
+	VL   0(P1ptr), Y1        // X1H
+	VL   16(P1ptr), Y0       // X1L
+	CALL p256MulInternal<>(SB)
+	VLR  T0, T3L
+	VLR  T1, T3H
+
+	// ADD(T1<T+T)           // T1 = T3+T3    T1   T2   T3   T4
+	p256AddInternal(T1H,T1L, T1,T0,T1,T0)
+
+	// X=T2; Y=T2; MUL; T-   // X3 = T2*T2    T1   T2   T3   T4
+	VLR  T2L, X0
+	VLR  T2H, X1
+	VLR  T2L, Y0
+	VLR  T2H, Y1
+	CALL p256MulInternal<>(SB)
+
+	// SUB(T<T-T1)           // X3 = X3-T1    T1   T2   T3   T4  (T1 = X3)
+	p256SubInternal(T1,T0,T1,T0,T1H,T1L)
+
+	// SUB(T<T-T4) X3:=T     // X3 = X3-T4         T2   T3   T4
+	p256SubInternal(T1,T0,T1,T0,T4H,T4L)
+	VLR T0, X3L
+	VLR T1, X3H
+
+	// SUB(X<T3-T)           // T3 = T3-X3         T2   T3   T4
+	p256SubInternal(X1,X0,T3H,T3L,T1,T0)
+
+	// X- ;  Y- ;  MUL; T3=T // T3 = T3*T2         T2   T3   T4
+	CALL p256MulInternal<>(SB)
+	VLR  T0, T3L
+	VLR  T1, T3H
+
+	// X=T4; Y=Y1; MUL; T-   // T4 = T4*Y1              T3   T4
+	VLR  T4L, X0
+	VLR  T4H, X1
+	VL   32(P1ptr), Y1       // Y1H
+	VL   48(P1ptr), Y0       // Y1L
+	CALL p256MulInternal<>(SB)
+
+	// SUB(T<T3-T) Y3:=T     // Y3 = T3-T4              T3   T4  (T3 = Y3)
+	p256SubInternal(Y3H,Y3L,T3H,T3L,T1,T0)
+
+	//	if (sel == 0) {
+	//		copy(P3.x[:], X1)
+	//		copy(P3.y[:], Y1)
+	//		copy(P3.z[:], Z1)
+	//	}
+
+	VL 0(P1ptr), X1H
+	VL 16(P1ptr), X1L
+
+	// Y1 already loaded, left over from addition
+	VL 64(P1ptr), Z1H
+	VL 80(P1ptr), Z1L
+
+	VLREPG sel+32(FP), SEL1
+	VZERO  ZER
+	VCEQG  SEL1, ZER, SEL1
+
+	VSEL X1L, X3L, SEL1, X3L
+	VSEL X1H, X3H, SEL1, X3H
+	VSEL Y1L, Y3L, SEL1, Y3L
+	VSEL Y1H, Y3H, SEL1, Y3H
+	VSEL Z1L, Z3L, SEL1, Z3L
+	VSEL Z1H, Z3H, SEL1, Z3H
+
+	//	if (zero == 0) {
+	//		copy(P3.x[:], X2)
+	//		copy(P3.y[:], Y2)
+	//		copy(P3.z[:], []byte{0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	//			0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})  //(p256.z*2^256)%p
+	//	}
+	VL 0(P2ptr), X2H
+	VL 16(P2ptr), X2L
+
+	// Y2 already loaded
+	VL 128(CPOOL), Z2H
+	VL 144(CPOOL), Z2L
+
+	VLREPG zero+40(FP), SEL1
+	VZERO  ZER
+	VCEQG  SEL1, ZER, SEL1
+
+	VSEL X2L, X3L, SEL1, X3L
+	VSEL X2H, X3H, SEL1, X3H
+	VSEL Y2L, Y3L, SEL1, Y3L
+	VSEL Y2H, Y3H, SEL1, Y3H
+	VSEL Z2L, Z3L, SEL1, Z3L
+	VSEL Z2H, Z3H, SEL1, Z3H
+
+	// All done, store out the result!!!
+	VST X3H, 0(P3ptr)
+	VST X3L, 16(P3ptr)
+	VST Y3H, 32(P3ptr)
+	VST Y3L, 48(P3ptr)
+	VST Z3H, 64(P3ptr)
+	VST Z3L, 80(P3ptr)
+
+	RET
+
+#undef P3ptr
+#undef P1ptr
+#undef P2ptr
+#undef CPOOL
+
+#undef Y2L
+#undef Y2H
+#undef T1L
+#undef T1H
+#undef T2L
+#undef T2H
+#undef T3L
+#undef T3H
+#undef T4L
+#undef T4H
+
+#undef TT0
+#undef TT1
+#undef T2
+
+#undef X0
+#undef X1
+#undef Y0
+#undef Y1
+#undef T0
+#undef T1
+
+#undef PL
+#undef PH
+
+#undef X1L
+#undef X1H
+#undef Y1L
+#undef Y1H
+#undef Z1L
+#undef Z1H
+#undef X2L
+#undef X2H
+#undef Z2L
+#undef Z2H
+#undef X3L
+#undef X3H
+#undef Y3L
+#undef Y3H
+#undef Z3L
+#undef Z3H
+
+#undef ZER
+#undef SEL1
+#undef CAR1
+#undef CAR2
+
+// p256PointDoubleAsm(P3, P1 *p256Point)
+// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl
+// http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html
+// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-projective-3.html
+#define P3ptr   R1
+#define P1ptr   R2
+#define CPOOL   R4
+
+// Temporaries in REGs
+#define X3L    V15
+#define X3H    V16
+#define Y3L    V17
+#define Y3H    V18
+#define T1L    V19
+#define T1H    V20
+#define T2L    V21
+#define T2H    V22
+#define T3L    V23
+#define T3H    V24
+
+#define X1L    V6
+#define X1H    V7
+#define Y1L    V8
+#define Y1H    V9
+#define Z1L    V10
+#define Z1H    V11
+
+// Temps for Sub and Add
+#define TT0  V11
+#define TT1  V12
+#define T2   V13
+
+// p256MulAsm Parameters
+#define X0    V0
+#define X1    V1
+#define Y0    V2
+#define Y1    V3
+#define T0    V4
+#define T1    V5
+
+#define PL    V30
+#define PH    V31
+
+#define Z3L    V23
+#define Z3H    V24
+
+#define ZER   V26
+#define SEL1  V27
+#define CAR1  V28
+#define CAR2  V29
+/*
+ * http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2004-hmv
+ * Cost: 4M + 4S + 1*half + 5add + 2*2 + 1*3.
+ * Source: 2004 Hankerson–Menezes–Vanstone, page 91.
+ * 	A  = 3(X₁-Z₁²)×(X₁+Z₁²)
+ * 	B  = 2Y₁
+ * 	Z₃ = B×Z₁
+ * 	C  = B²
+ * 	D  = C×X₁
+ * 	X₃ = A²-2D
+ * 	Y₃ = (D-X₃)×A-C²/2
+ *
+ * Three-operand formula:
+ *       T1 = Z1²
+ *       T2 = X1-T1
+ *       T1 = X1+T1
+ *       T2 = T2*T1
+ *       T2 = 3*T2
+ *       Y3 = 2*Y1
+ *       Z3 = Y3*Z1
+ *       Y3 = Y3²
+ *       T3 = Y3*X1
+ *       Y3 = Y3²
+ *       Y3 = half*Y3
+ *       X3 = T2²
+ *       T1 = 2*T3
+ *       X3 = X3-T1
+ *       T1 = T3-X3
+ *       T1 = T1*T2
+ *       Y3 = T1-Y3
+ */
+
+TEXT ·p256PointDoubleAsm(SB), NOSPLIT, $0
+	MOVD P3+0(FP), P3ptr
+	MOVD P1+8(FP), P1ptr
+
+	MOVD $p256mul<>+0x00(SB), CPOOL
+	VL   16(CPOOL), PL
+	VL   0(CPOOL), PH
+
+	// X=Z1; Y=Z1; MUL; T-    // T1 = Z1²
+	VL   64(P1ptr), X1       // Z1H
+	VL   80(P1ptr), X0       // Z1L
+	VLR  X0, Y0
+	VLR  X1, Y1
+	CALL p256MulInternal<>(SB)
+
+	// SUB(X<X1-T)            // T2 = X1-T1
+	VL 0(P1ptr), X1H
+	VL 16(P1ptr), X1L
+	p256SubInternal(X1,X0,X1H,X1L,T1,T0)
+
+	// ADD(Y<X1+T)            // T1 = X1+T1
+	p256AddInternal(Y1,Y0,X1H,X1L,T1,T0)
+
+	// X-  ; Y-  ; MUL; T-    // T2 = T2*T1
+	CALL p256MulInternal<>(SB)
+
+	// ADD(T2<T+T); ADD(T2<T2+T)  // T2 = 3*T2
+	p256AddInternal(T2H,T2L,T1,T0,T1,T0)
+	p256AddInternal(T2H,T2L,T2H,T2L,T1,T0)
+
+	// ADD(X<Y1+Y1)           // Y3 = 2*Y1
+	VL 32(P1ptr), Y1H
+	VL 48(P1ptr), Y1L
+	p256AddInternal(X1,X0,Y1H,Y1L,Y1H,Y1L)
+
+	// X-  ; Y=Z1; MUL; Z3:=T // Z3 = Y3*Z1
+	VL   64(P1ptr), Y1       // Z1H
+	VL   80(P1ptr), Y0       // Z1L
+	CALL p256MulInternal<>(SB)
+	VST  T1, 64(P3ptr)
+	VST  T0, 80(P3ptr)
+
+	// X-  ; Y=X ; MUL; T-    // Y3 = Y3²
+	VLR  X0, Y0
+	VLR  X1, Y1
+	CALL p256MulInternal<>(SB)
+
+	// X=T ; Y=X1; MUL; T3=T  // T3 = Y3*X1
+	VLR  T0, X0
+	VLR  T1, X1
+	VL   0(P1ptr), Y1
+	VL   16(P1ptr), Y0
+	CALL p256MulInternal<>(SB)
+	VLR  T0, T3L
+	VLR  T1, T3H
+
+	// X-  ; Y=X ; MUL; T-    // Y3 = Y3²
+	VLR  X0, Y0
+	VLR  X1, Y1
+	CALL p256MulInternal<>(SB)
+
+	// HAL(Y3<T)              // Y3 = half*Y3
+	p256HalfInternal(Y3H,Y3L, T1,T0)
+
+	// X=T2; Y=T2; MUL; T-    // X3 = T2²
+	VLR  T2L, X0
+	VLR  T2H, X1
+	VLR  T2L, Y0
+	VLR  T2H, Y1
+	CALL p256MulInternal<>(SB)
+
+	// ADD(T1<T3+T3)          // T1 = 2*T3
+	p256AddInternal(T1H,T1L,T3H,T3L,T3H,T3L)
+
+	// SUB(X3<T-T1) X3:=X3    // X3 = X3-T1
+	p256SubInternal(X3H,X3L,T1,T0,T1H,T1L)
+	VST X3H, 0(P3ptr)
+	VST X3L, 16(P3ptr)
+
+	// SUB(X<T3-X3)           // T1 = T3-X3
+	p256SubInternal(X1,X0,T3H,T3L,X3H,X3L)
+
+	// X-  ; Y-  ; MUL; T-    // T1 = T1*T2
+	CALL p256MulInternal<>(SB)
+
+	// SUB(Y3<T-Y3)           // Y3 = T1-Y3
+	p256SubInternal(Y3H,Y3L,T1,T0,Y3H,Y3L)
+
+	VST Y3H, 32(P3ptr)
+	VST Y3L, 48(P3ptr)
+	RET
+
+#undef P3ptr
+#undef P1ptr
+#undef CPOOL
+#undef X3L
+#undef X3H
+#undef Y3L
+#undef Y3H
+#undef T1L
+#undef T1H
+#undef T2L
+#undef T2H
+#undef T3L
+#undef T3H
+#undef X1L
+#undef X1H
+#undef Y1L
+#undef Y1H
+#undef Z1L
+#undef Z1H
+#undef TT0
+#undef TT1
+#undef T2
+#undef X0
+#undef X1
+#undef Y0
+#undef Y1
+#undef T0
+#undef T1
+#undef PL
+#undef PH
+#undef Z3L
+#undef Z3H
+#undef ZER
+#undef SEL1
+#undef CAR1
+#undef CAR2
+
+// p256PointAddAsm(P3, P1, P2 *p256Point)
+#define P3ptr   R1
+#define P1ptr   R2
+#define P2ptr   R3
+#define CPOOL   R4
+
+// Temporaries in REGs
+#define T1L   V16
+#define T1H   V17
+#define T2L   V18
+#define T2H   V19
+#define U1L   V20
+#define U1H   V21
+#define S1L   V22
+#define S1H   V23
+#define HL    V24
+#define HH    V25
+#define RL    V26
+#define RH    V27
+
+// Temps for Sub and Add
+#define ZER   V6
+#define SEL1  V7
+#define CAR1  V8
+#define CAR2  V9
+#define TT0  V11
+#define TT1  V12
+#define T2   V13
+
+// p256MulAsm Parameters
+#define X0    V0
+#define X1    V1
+#define Y0    V2
+#define Y1    V3
+#define T0    V4
+#define T1    V5
+
+#define PL    V30
+#define PH    V31
+/*
+ * https://choucroutage.com/Papers/SideChannelAttacks/ctrsa-2011-brown.pdf "Software Implementation of the NIST Elliptic Curves Over Prime Fields"
+ *
+ * A = X₁×Z₂²
+ * B = Y₁×Z₂³
+ * C = X₂×Z₁²-A
+ * D = Y₂×Z₁³-B
+ * X₃ = D² - 2A×C² - C³
+ * Y₃ = D×(A×C² - X₃) - B×C³
+ * Z₃ = Z₁×Z₂×C
+ *
+ * Three-operand formula (adopted): http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2
+ * Temp storage: T1,T2,U1,H,Z3=X3=Y3,S1,R
+ *
+ * T1 = Z1*Z1
+ * T2 = Z2*Z2
+ * U1 = X1*T2
+ * H  = X2*T1
+ * H  = H-U1
+ * Z3 = Z1*Z2
+ * Z3 = Z3*H << store-out Z3 result reg.. could override Z1, if slices have same backing array
+ *
+ * S1 = Z2*T2
+ * S1 = Y1*S1
+ * R  = Z1*T1
+ * R  = Y2*R
+ * R  = R-S1
+ *
+ * T1 = H*H
+ * T2 = H*T1
+ * U1 = U1*T1
+ *
+ * X3 = R*R
+ * X3 = X3-T2
+ * T1 = 2*U1
+ * X3 = X3-T1 << store-out X3 result reg
+ *
+ * T2 = S1*T2
+ * Y3 = U1-X3
+ * Y3 = R*Y3
+ * Y3 = Y3-T2 << store-out Y3 result reg
+
+ 	// X=Z1; Y=Z1; MUL; T-   // T1 = Z1*Z1
+	// X-  ; Y=T ; MUL; R=T  // R  = Z1*T1
+	// X=X2; Y-  ; MUL; H=T  // H  = X2*T1
+	// X=Z2; Y=Z2; MUL; T-   // T2 = Z2*Z2
+	// X-  ; Y=T ; MUL; S1=T // S1 = Z2*T2
+	// X=X1; Y-  ; MUL; U1=T // U1 = X1*T2
+	// SUB(H<H-T)            // H  = H-U1
+	// X=Z1; Y=Z2; MUL; T-   // Z3 = Z1*Z2
+	// X=T ; Y=H ; MUL; Z3:=T// Z3 = Z3*H << store-out Z3 result reg.. could override Z1, if slices have same backing array
+	// X=Y1; Y=S1; MUL; S1=T // S1 = Y1*S1
+	// X=Y2; Y=R ; MUL; T-   // R  = Y2*R
+	// SUB(R<T-S1)           // R  = R-S1
+	// X=H ; Y=H ; MUL; T-   // T1 = H*H
+	// X-  ; Y=T ; MUL; T2=T // T2 = H*T1
+	// X=U1; Y-  ; MUL; U1=T // U1 = U1*T1
+	// X=R ; Y=R ; MUL; T-   // X3 = R*R
+	// SUB(T<T-T2)           // X3 = X3-T2
+	// ADD(X<U1+U1)          // T1 = 2*U1
+	// SUB(T<T-X) X3:=T      // X3 = X3-T1 << store-out X3 result reg
+	// SUB(Y<U1-T)           // Y3 = U1-X3
+	// X=R ; Y-  ; MUL; U1=T // Y3 = R*Y3
+	// X=S1; Y=T2; MUL; T-   // T2 = S1*T2
+	// SUB(T<U1-T); Y3:=T    // Y3 = Y3-T2 << store-out Y3 result reg
+	*/
+TEXT ·p256PointAddAsm(SB), NOSPLIT, $0
+	MOVD P3+0(FP), P3ptr
+	MOVD P1+8(FP), P1ptr
+	MOVD P2+16(FP), P2ptr
+
+	MOVD $p256mul<>+0x00(SB), CPOOL
+	VL   16(CPOOL), PL
+	VL   0(CPOOL), PH
+
+	// X=Z1; Y=Z1; MUL; T-   // T1 = Z1*Z1
+	VL   64(P1ptr), X1       // Z1H
+	VL   80(P1ptr), X0       // Z1L
+	VLR  X0, Y0
+	VLR  X1, Y1
+	CALL p256MulInternal<>(SB)
+
+	// X-  ; Y=T ; MUL; R=T  // R  = Z1*T1
+	VLR  T0, Y0
+	VLR  T1, Y1
+	CALL p256MulInternal<>(SB)
+	VLR  T0, RL
+	VLR  T1, RH
+
+	// X=X2; Y-  ; MUL; H=T  // H  = X2*T1
+	VL   0(P2ptr), X1        // X2H
+	VL   16(P2ptr), X0       // X2L
+	CALL p256MulInternal<>(SB)
+	VLR  T0, HL
+	VLR  T1, HH
+
+	// X=Z2; Y=Z2; MUL; T-   // T2 = Z2*Z2
+	VL   64(P2ptr), X1       // Z2H
+	VL   80(P2ptr), X0       // Z2L
+	VLR  X0, Y0
+	VLR  X1, Y1
+	CALL p256MulInternal<>(SB)
+
+	// X-  ; Y=T ; MUL; S1=T // S1 = Z2*T2
+	VLR  T0, Y0
+	VLR  T1, Y1
+	CALL p256MulInternal<>(SB)
+	VLR  T0, S1L
+	VLR  T1, S1H
+
+	// X=X1; Y-  ; MUL; U1=T // U1 = X1*T2
+	VL   0(P1ptr), X1        // X1H
+	VL   16(P1ptr), X0       // X1L
+	CALL p256MulInternal<>(SB)
+	VLR  T0, U1L
+	VLR  T1, U1H
+
+	// SUB(H<H-T)            // H  = H-U1
+	p256SubInternal(HH,HL,HH,HL,T1,T0)
+
+	// X=Z1; Y=Z2; MUL; T-   // Z3 = Z1*Z2
+	VL   64(P1ptr), X1       // Z1H
+	VL   80(P1ptr), X0       // Z1L
+	VL   64(P2ptr), Y1       // Z2H
+	VL   80(P2ptr), Y0       // Z2L
+	CALL p256MulInternal<>(SB)
+
+	// X=T ; Y=H ; MUL; Z3:=T// Z3 = Z3*H
+	VLR  T0, X0
+	VLR  T1, X1
+	VLR  HL, Y0
+	VLR  HH, Y1
+	CALL p256MulInternal<>(SB)
+	VST  T1, 64(P3ptr)
+	VST  T0, 80(P3ptr)
+
+	// X=Y1; Y=S1; MUL; S1=T // S1 = Y1*S1
+	VL   32(P1ptr), X1
+	VL   48(P1ptr), X0
+	VLR  S1L, Y0
+	VLR  S1H, Y1
+	CALL p256MulInternal<>(SB)
+	VLR  T0, S1L
+	VLR  T1, S1H
+
+	// X=Y2; Y=R ; MUL; T-   // R  = Y2*R
+	VL   32(P2ptr), X1
+	VL   48(P2ptr), X0
+	VLR  RL, Y0
+	VLR  RH, Y1
+	CALL p256MulInternal<>(SB)
+
+	// SUB(R<T-S1)           // R  = T-S1
+	p256SubInternal(RH,RL,T1,T0,S1H,S1L)
+
+	// X=H ; Y=H ; MUL; T-   // T1 = H*H
+	VLR  HL, X0
+	VLR  HH, X1
+	VLR  HL, Y0
+	VLR  HH, Y1
+	CALL p256MulInternal<>(SB)
+
+	// X-  ; Y=T ; MUL; T2=T // T2 = H*T1
+	VLR  T0, Y0
+	VLR  T1, Y1
+	CALL p256MulInternal<>(SB)
+	VLR  T0, T2L
+	VLR  T1, T2H
+
+	// X=U1; Y-  ; MUL; U1=T // U1 = U1*T1
+	VLR  U1L, X0
+	VLR  U1H, X1
+	CALL p256MulInternal<>(SB)
+	VLR  T0, U1L
+	VLR  T1, U1H
+
+	// X=R ; Y=R ; MUL; T-   // X3 = R*R
+	VLR  RL, X0
+	VLR  RH, X1
+	VLR  RL, Y0
+	VLR  RH, Y1
+	CALL p256MulInternal<>(SB)
+
+	// SUB(T<T-T2)           // X3 = X3-T2
+	p256SubInternal(T1,T0,T1,T0,T2H,T2L)
+
+	// ADD(X<U1+U1)          // T1 = 2*U1
+	p256AddInternal(X1,X0,U1H,U1L,U1H,U1L)
+
+	// SUB(T<T-X) X3:=T      // X3 = X3-T1 << store-out X3 result reg
+	p256SubInternal(T1,T0,T1,T0,X1,X0)
+	VST T1, 0(P3ptr)
+	VST T0, 16(P3ptr)
+
+	// SUB(Y<U1-T)           // Y3 = U1-X3
+	p256SubInternal(Y1,Y0,U1H,U1L,T1,T0)
+
+	// X=R ; Y-  ; MUL; U1=T // Y3 = R*Y3
+	VLR  RL, X0
+	VLR  RH, X1
+	CALL p256MulInternal<>(SB)
+	VLR  T0, U1L
+	VLR  T1, U1H
+
+	// X=S1; Y=T2; MUL; T-   // T2 = S1*T2
+	VLR  S1L, X0
+	VLR  S1H, X1
+	VLR  T2L, Y0
+	VLR  T2H, Y1
+	CALL p256MulInternal<>(SB)
+
+	// SUB(T<U1-T); Y3:=T    // Y3 = Y3-T2 << store-out Y3 result reg
+	p256SubInternal(T1,T0,U1H,U1L,T1,T0)
+	VST T1, 32(P3ptr)
+	VST T0, 48(P3ptr)
+
+	RET
diff --git a/src/crypto/elliptic/p256_generic.go b/src/crypto/elliptic/p256_generic.go
new file mode 100644
index 0000000..9963fca
--- /dev/null
+++ b/src/crypto/elliptic/p256_generic.go
@@ -0,0 +1,16 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64,!s390x
+
+package elliptic
+
+var (
+	p256 p256Curve
+)
+
+func initP256Arch() {
+	// Use pure Go implementation.
+	p256 = p256Curve{p256Params}
+}
diff --git a/src/crypto/elliptic/p256_s390x.go b/src/crypto/elliptic/p256_s390x.go
new file mode 100644
index 0000000..2ed4c0b
--- /dev/null
+++ b/src/crypto/elliptic/p256_s390x.go
@@ -0,0 +1,513 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x
+
+package elliptic
+
+import (
+	"math/big"
+)
+
+type p256CurveFast struct {
+	*CurveParams
+}
+
+type p256Point struct {
+	x [32]byte
+	y [32]byte
+	z [32]byte
+}
+
+var (
+	p256        Curve
+	p256PreFast *[37][64]p256Point
+)
+
+// hasVectorFacility reports whether the machine has the z/Architecture
+// vector facility installed and enabled.
+func hasVectorFacility() bool
+
+var hasVX = hasVectorFacility()
+
+func initP256Arch() {
+	if hasVX {
+		p256 = p256CurveFast{p256Params}
+		initTable()
+		return
+	}
+
+	// No vector support, use pure Go implementation.
+	p256 = p256Curve{p256Params}
+	return
+}
+
+func (curve p256CurveFast) Params() *CurveParams {
+	return curve.CurveParams
+}
+
+// Functions implemented in p256_asm_s390x.s
+// Montgomery multiplication modulo P256
+func p256MulAsm(res, in1, in2 []byte)
+
+// Montgomery square modulo P256
+func p256Sqr(res, in []byte) {
+	p256MulAsm(res, in, in)
+}
+
+// Montgomery multiplication by 1
+func p256FromMont(res, in []byte)
+
+// iff cond == 1  val <- -val
+func p256NegCond(val *p256Point, cond int)
+
+// if cond == 0 res <- b; else res <- a
+func p256MovCond(res, a, b *p256Point, cond int)
+
+// Constant time table access
+func p256Select(point *p256Point, table []p256Point, idx int)
+func p256SelectBase(point *p256Point, table []p256Point, idx int)
+
+// Montgomery multiplication modulo Ord(G)
+func p256OrdMul(res, in1, in2 []byte)
+
+// Montgomery square modulo Ord(G), repeated n times
+func p256OrdSqr(res, in []byte, n int) {
+	copy(res, in)
+	for i := 0; i < n; i += 1 {
+		p256OrdMul(res, res, res)
+	}
+}
+
+// Point add with P2 being affine point
+// If sign == 1 -> P2 = -P2
+// If sel == 0 -> P3 = P1
+// if zero == 0 -> P3 = P2
+func p256PointAddAffineAsm(P3, P1, P2 *p256Point, sign, sel, zero int)
+
+// Point add
+func p256PointAddAsm(P3, P1, P2 *p256Point)
+func p256PointDoubleAsm(P3, P1 *p256Point)
+
+func (curve p256CurveFast) Inverse(k *big.Int) *big.Int {
+	if k.Cmp(p256Params.N) >= 0 {
+		// This should never happen.
+		reducedK := new(big.Int).Mod(k, p256Params.N)
+		k = reducedK
+	}
+
+	// table will store precomputed powers of x. The 32 bytes at index
+	// i store x^(i+1).
+	var table [15][32]byte
+
+	x := fromBig(k)
+	// This code operates in the Montgomery domain where R = 2^256 mod n
+	// and n is the order of the scalar field. (See initP256 for the
+	// value.) Elements in the Montgomery domain take the form a×R and
+	// multiplication of x and y in the calculates (x × y × R^-1) mod n. RR
+	// is R×R mod n thus the Montgomery multiplication x and RR gives x×R,
+	// i.e. converts x into the Montgomery domain. Stored in BigEndian form
+	RR := []byte{0x66, 0xe1, 0x2d, 0x94, 0xf3, 0xd9, 0x56, 0x20, 0x28, 0x45, 0xb2, 0x39, 0x2b, 0x6b, 0xec, 0x59,
+		0x46, 0x99, 0x79, 0x9c, 0x49, 0xbd, 0x6f, 0xa6, 0x83, 0x24, 0x4c, 0x95, 0xbe, 0x79, 0xee, 0xa2}
+
+	p256OrdMul(table[0][:], x, RR)
+
+	// Prepare the table, no need in constant time access, because the
+	// power is not a secret. (Entry 0 is never used.)
+	for i := 2; i < 16; i += 2 {
+		p256OrdSqr(table[i-1][:], table[(i/2)-1][:], 1)
+		p256OrdMul(table[i][:], table[i-1][:], table[0][:])
+	}
+
+	copy(x, table[14][:]) // f
+
+	p256OrdSqr(x[0:32], x[0:32], 4)
+	p256OrdMul(x[0:32], x[0:32], table[14][:]) // ff
+	t := make([]byte, 32)
+	copy(t, x)
+
+	p256OrdSqr(x, x, 8)
+	p256OrdMul(x, x, t) // ffff
+	copy(t, x)
+
+	p256OrdSqr(x, x, 16)
+	p256OrdMul(x, x, t) // ffffffff
+	copy(t, x)
+
+	p256OrdSqr(x, x, 64) // ffffffff0000000000000000
+	p256OrdMul(x, x, t)  // ffffffff00000000ffffffff
+	p256OrdSqr(x, x, 32) // ffffffff00000000ffffffff00000000
+	p256OrdMul(x, x, t)  // ffffffff00000000ffffffffffffffff
+
+	// Remaining 32 windows
+	expLo := [32]byte{0xb, 0xc, 0xe, 0x6, 0xf, 0xa, 0xa, 0xd, 0xa, 0x7, 0x1, 0x7, 0x9, 0xe, 0x8, 0x4,
+		0xf, 0x3, 0xb, 0x9, 0xc, 0xa, 0xc, 0x2, 0xf, 0xc, 0x6, 0x3, 0x2, 0x5, 0x4, 0xf}
+	for i := 0; i < 32; i++ {
+		p256OrdSqr(x, x, 4)
+		p256OrdMul(x, x, table[expLo[i]-1][:])
+	}
+
+	// Multiplying by one in the Montgomery domain converts a Montgomery
+	// value out of the domain.
+	one := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
+	p256OrdMul(x, x, one)
+
+	return new(big.Int).SetBytes(x)
+}
+
+// fromBig converts a *big.Int into a format used by this code.
+func fromBig(big *big.Int) []byte {
+	// This could be done a lot more efficiently...
+	res := big.Bytes()
+	if 32 == len(res) {
+		return res
+	}
+	t := make([]byte, 32)
+	offset := 32 - len(res)
+	for i := len(res) - 1; i >= 0; i-- {
+		t[i+offset] = res[i]
+	}
+	return t
+}
+
+// p256GetMultiplier makes sure byte array will have 32 byte elements, If the scalar
+// is equal or greater than the order of the group, it's reduced modulo that order.
+func p256GetMultiplier(in []byte) []byte {
+	n := new(big.Int).SetBytes(in)
+
+	if n.Cmp(p256Params.N) >= 0 {
+		n.Mod(n, p256Params.N)
+	}
+	return fromBig(n)
+}
+
+// p256MulAsm operates in a Montgomery domain with R = 2^256 mod p, where p is the
+// underlying field of the curve. (See initP256 for the value.) Thus rr here is
+// R×R mod p. See comment in Inverse about how this is used.
+var rr = []byte{0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+	0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}
+
+// (This is one, in the Montgomery domain.)
+var one = []byte{0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
+
+func maybeReduceModP(in *big.Int) *big.Int {
+	if in.Cmp(p256Params.P) < 0 {
+		return in
+	}
+	return new(big.Int).Mod(in, p256Params.P)
+}
+
+func (curve p256CurveFast) CombinedMult(bigX, bigY *big.Int, baseScalar, scalar []byte) (x, y *big.Int) {
+	var r1, r2 p256Point
+	r1.p256BaseMult(p256GetMultiplier(baseScalar))
+
+	copy(r2.x[:], fromBig(maybeReduceModP(bigX)))
+	copy(r2.y[:], fromBig(maybeReduceModP(bigY)))
+	copy(r2.z[:], one)
+	p256MulAsm(r2.x[:], r2.x[:], rr[:])
+	p256MulAsm(r2.y[:], r2.y[:], rr[:])
+
+	r2.p256ScalarMult(p256GetMultiplier(scalar))
+	p256PointAddAsm(&r1, &r1, &r2)
+	return r1.p256PointToAffine()
+}
+
+func (curve p256CurveFast) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
+	var r p256Point
+	r.p256BaseMult(p256GetMultiplier(scalar))
+	return r.p256PointToAffine()
+}
+
+func (curve p256CurveFast) ScalarMult(bigX, bigY *big.Int, scalar []byte) (x, y *big.Int) {
+	var r p256Point
+	copy(r.x[:], fromBig(maybeReduceModP(bigX)))
+	copy(r.y[:], fromBig(maybeReduceModP(bigY)))
+	copy(r.z[:], one)
+	p256MulAsm(r.x[:], r.x[:], rr[:])
+	p256MulAsm(r.y[:], r.y[:], rr[:])
+	r.p256ScalarMult(p256GetMultiplier(scalar))
+	return r.p256PointToAffine()
+}
+
+func (p *p256Point) p256PointToAffine() (x, y *big.Int) {
+	zInv := make([]byte, 32)
+	zInvSq := make([]byte, 32)
+
+	p256Inverse(zInv, p.z[:])
+	p256Sqr(zInvSq, zInv)
+	p256MulAsm(zInv, zInv, zInvSq)
+
+	p256MulAsm(zInvSq, p.x[:], zInvSq)
+	p256MulAsm(zInv, p.y[:], zInv)
+
+	p256FromMont(zInvSq, zInvSq)
+	p256FromMont(zInv, zInv)
+
+	return new(big.Int).SetBytes(zInvSq), new(big.Int).SetBytes(zInv)
+}
+
+// p256Inverse sets out to in^-1 mod p.
+func p256Inverse(out, in []byte) {
+	var stack [6 * 32]byte
+	p2 := stack[32*0 : 32*0+32]
+	p4 := stack[32*1 : 32*1+32]
+	p8 := stack[32*2 : 32*2+32]
+	p16 := stack[32*3 : 32*3+32]
+	p32 := stack[32*4 : 32*4+32]
+
+	p256Sqr(out, in)
+	p256MulAsm(p2, out, in) // 3*p
+
+	p256Sqr(out, p2)
+	p256Sqr(out, out)
+	p256MulAsm(p4, out, p2) // f*p
+
+	p256Sqr(out, p4)
+	p256Sqr(out, out)
+	p256Sqr(out, out)
+	p256Sqr(out, out)
+	p256MulAsm(p8, out, p4) // ff*p
+
+	p256Sqr(out, p8)
+
+	for i := 0; i < 7; i++ {
+		p256Sqr(out, out)
+	}
+	p256MulAsm(p16, out, p8) // ffff*p
+
+	p256Sqr(out, p16)
+	for i := 0; i < 15; i++ {
+		p256Sqr(out, out)
+	}
+	p256MulAsm(p32, out, p16) // ffffffff*p
+
+	p256Sqr(out, p32)
+
+	for i := 0; i < 31; i++ {
+		p256Sqr(out, out)
+	}
+	p256MulAsm(out, out, in)
+
+	for i := 0; i < 32*4; i++ {
+		p256Sqr(out, out)
+	}
+	p256MulAsm(out, out, p32)
+
+	for i := 0; i < 32; i++ {
+		p256Sqr(out, out)
+	}
+	p256MulAsm(out, out, p32)
+
+	for i := 0; i < 16; i++ {
+		p256Sqr(out, out)
+	}
+	p256MulAsm(out, out, p16)
+
+	for i := 0; i < 8; i++ {
+		p256Sqr(out, out)
+	}
+	p256MulAsm(out, out, p8)
+
+	p256Sqr(out, out)
+	p256Sqr(out, out)
+	p256Sqr(out, out)
+	p256Sqr(out, out)
+	p256MulAsm(out, out, p4)
+
+	p256Sqr(out, out)
+	p256Sqr(out, out)
+	p256MulAsm(out, out, p2)
+
+	p256Sqr(out, out)
+	p256Sqr(out, out)
+	p256MulAsm(out, out, in)
+}
+
+func boothW5(in uint) (int, int) {
+	var s uint = ^((in >> 5) - 1)
+	var d uint = (1 << 6) - in - 1
+	d = (d & s) | (in & (^s))
+	d = (d >> 1) + (d & 1)
+	return int(d), int(s & 1)
+}
+
+func boothW7(in uint) (int, int) {
+	var s uint = ^((in >> 7) - 1)
+	var d uint = (1 << 8) - in - 1
+	d = (d & s) | (in & (^s))
+	d = (d >> 1) + (d & 1)
+	return int(d), int(s & 1)
+}
+
+func initTable() {
+	p256PreFast = new([37][64]p256Point) //z coordinate not used
+	basePoint := p256Point{
+		x: [32]byte{0x18, 0x90, 0x5f, 0x76, 0xa5, 0x37, 0x55, 0xc6, 0x79, 0xfb, 0x73, 0x2b, 0x77, 0x62, 0x25, 0x10,
+			0x75, 0xba, 0x95, 0xfc, 0x5f, 0xed, 0xb6, 0x01, 0x79, 0xe7, 0x30, 0xd4, 0x18, 0xa9, 0x14, 0x3c}, //(p256.x*2^256)%p
+		y: [32]byte{0x85, 0x71, 0xff, 0x18, 0x25, 0x88, 0x5d, 0x85, 0xd2, 0xe8, 0x86, 0x88, 0xdd, 0x21, 0xf3, 0x25,
+			0x8b, 0x4a, 0xb8, 0xe4, 0xba, 0x19, 0xe4, 0x5c, 0xdd, 0xf2, 0x53, 0x57, 0xce, 0x95, 0x56, 0x0a}, //(p256.y*2^256)%p
+		z: [32]byte{0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, //(p256.z*2^256)%p
+	}
+
+	t1 := new(p256Point)
+	t2 := new(p256Point)
+	*t2 = basePoint
+
+	zInv := make([]byte, 32)
+	zInvSq := make([]byte, 32)
+	for j := 0; j < 64; j++ {
+		*t1 = *t2
+		for i := 0; i < 37; i++ {
+			// The window size is 7 so we need to double 7 times.
+			if i != 0 {
+				for k := 0; k < 7; k++ {
+					p256PointDoubleAsm(t1, t1)
+				}
+			}
+			// Convert the point to affine form. (Its values are
+			// still in Montgomery form however.)
+			p256Inverse(zInv, t1.z[:])
+			p256Sqr(zInvSq, zInv)
+			p256MulAsm(zInv, zInv, zInvSq)
+
+			p256MulAsm(t1.x[:], t1.x[:], zInvSq)
+			p256MulAsm(t1.y[:], t1.y[:], zInv)
+
+			copy(t1.z[:], basePoint.z[:])
+			// Update the table entry
+			copy(p256PreFast[i][j].x[:], t1.x[:])
+			copy(p256PreFast[i][j].y[:], t1.y[:])
+		}
+		if j == 0 {
+			p256PointDoubleAsm(t2, &basePoint)
+		} else {
+			p256PointAddAsm(t2, t2, &basePoint)
+		}
+	}
+}
+
+func (p *p256Point) p256BaseMult(scalar []byte) {
+	wvalue := (uint(scalar[31]) << 1) & 0xff
+	sel, sign := boothW7(uint(wvalue))
+	p256SelectBase(p, p256PreFast[0][:], sel)
+	p256NegCond(p, sign)
+
+	copy(p.z[:], one[:])
+	var t0 p256Point
+
+	copy(t0.z[:], one[:])
+
+	index := uint(6)
+	zero := sel
+
+	for i := 1; i < 37; i++ {
+		if index < 247 {
+			wvalue = ((uint(scalar[31-index/8]) >> (index % 8)) + (uint(scalar[31-index/8-1]) << (8 - (index % 8)))) & 0xff
+		} else {
+			wvalue = (uint(scalar[31-index/8]) >> (index % 8)) & 0xff
+		}
+		index += 7
+		sel, sign = boothW7(uint(wvalue))
+		p256SelectBase(&t0, p256PreFast[i][:], sel)
+		p256PointAddAffineAsm(p, p, &t0, sign, sel, zero)
+		zero |= sel
+	}
+}
+
+func (p *p256Point) p256ScalarMult(scalar []byte) {
+	// precomp is a table of precomputed points that stores powers of p
+	// from p^1 to p^16.
+	var precomp [16]p256Point
+	var t0, t1, t2, t3 p256Point
+
+	// Prepare the table
+	*&precomp[0] = *p
+
+	p256PointDoubleAsm(&t0, p)
+	p256PointDoubleAsm(&t1, &t0)
+	p256PointDoubleAsm(&t2, &t1)
+	p256PointDoubleAsm(&t3, &t2)
+	*&precomp[1] = t0  // 2
+	*&precomp[3] = t1  // 4
+	*&precomp[7] = t2  // 8
+	*&precomp[15] = t3 // 16
+
+	p256PointAddAsm(&t0, &t0, p)
+	p256PointAddAsm(&t1, &t1, p)
+	p256PointAddAsm(&t2, &t2, p)
+	*&precomp[2] = t0 // 3
+	*&precomp[4] = t1 // 5
+	*&precomp[8] = t2 // 9
+
+	p256PointDoubleAsm(&t0, &t0)
+	p256PointDoubleAsm(&t1, &t1)
+	*&precomp[5] = t0 // 6
+	*&precomp[9] = t1 // 10
+
+	p256PointAddAsm(&t2, &t0, p)
+	p256PointAddAsm(&t1, &t1, p)
+	*&precomp[6] = t2  // 7
+	*&precomp[10] = t1 // 11
+
+	p256PointDoubleAsm(&t0, &t0)
+	p256PointDoubleAsm(&t2, &t2)
+	*&precomp[11] = t0 // 12
+	*&precomp[13] = t2 // 14
+
+	p256PointAddAsm(&t0, &t0, p)
+	p256PointAddAsm(&t2, &t2, p)
+	*&precomp[12] = t0 // 13
+	*&precomp[14] = t2 // 15
+
+	// Start scanning the window from top bit
+	index := uint(254)
+	var sel, sign int
+
+	wvalue := (uint(scalar[31-index/8]) >> (index % 8)) & 0x3f
+	sel, _ = boothW5(uint(wvalue))
+	p256Select(p, precomp[:], sel)
+	zero := sel
+
+	for index > 4 {
+		index -= 5
+		p256PointDoubleAsm(p, p)
+		p256PointDoubleAsm(p, p)
+		p256PointDoubleAsm(p, p)
+		p256PointDoubleAsm(p, p)
+		p256PointDoubleAsm(p, p)
+
+		if index < 247 {
+			wvalue = ((uint(scalar[31-index/8]) >> (index % 8)) + (uint(scalar[31-index/8-1]) << (8 - (index % 8)))) & 0x3f
+		} else {
+			wvalue = (uint(scalar[31-index/8]) >> (index % 8)) & 0x3f
+		}
+
+		sel, sign = boothW5(uint(wvalue))
+
+		p256Select(&t0, precomp[:], sel)
+		p256NegCond(&t0, sign)
+		p256PointAddAsm(&t1, p, &t0)
+		p256MovCond(&t1, &t1, p, sel)
+		p256MovCond(p, &t1, &t0, zero)
+		zero |= sel
+	}
+
+	p256PointDoubleAsm(p, p)
+	p256PointDoubleAsm(p, p)
+	p256PointDoubleAsm(p, p)
+	p256PointDoubleAsm(p, p)
+	p256PointDoubleAsm(p, p)
+
+	wvalue = (uint(scalar[31]) << 1) & 0x3f
+	sel, sign = boothW5(uint(wvalue))
+
+	p256Select(&t0, precomp[:], sel)
+	p256NegCond(&t0, sign)
+	p256PointAddAsm(&t1, p, &t0)
+	p256MovCond(&t1, &t1, p, sel)
+	p256MovCond(p, &t1, &t0, zero)
+}
diff --git a/src/crypto/hmac/hmac.go b/src/crypto/hmac/hmac.go
index a748107..9ef9c44 100644
--- a/src/crypto/hmac/hmac.go
+++ b/src/crypto/hmac/hmac.go
@@ -94,5 +94,5 @@ func Equal(mac1, mac2 []byte) bool {
 	// We don't have to be constant time if the lengths of the MACs are
 	// different as that suggests that a completely different hash function
 	// was used.
-	return len(mac1) == len(mac2) && subtle.ConstantTimeCompare(mac1, mac2) == 1
+	return subtle.ConstantTimeCompare(mac1, mac2) == 1
 }
diff --git a/src/crypto/internal/cipherhw/asm_amd64.s b/src/crypto/internal/cipherhw/asm_amd64.s
new file mode 100644
index 0000000..dd1afd4
--- /dev/null
+++ b/src/crypto/internal/cipherhw/asm_amd64.s
@@ -0,0 +1,17 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!gccgo,!appengine
+
+#include "textflag.h"
+
+// func hasAESNI() bool
+TEXT ·hasAESNI(SB),NOSPLIT,$0
+	XORQ AX, AX
+	INCL AX
+	CPUID
+	SHRQ $25, CX
+	ANDQ $1, CX
+	MOVB CX, ret+0(FP)
+	RET
diff --git a/src/crypto/internal/cipherhw/asm_s390x.s b/src/crypto/internal/cipherhw/asm_s390x.s
new file mode 100644
index 0000000..51dc1c6
--- /dev/null
+++ b/src/crypto/internal/cipherhw/asm_s390x.s
@@ -0,0 +1,44 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x,!gccgo,!appengine
+
+#include "textflag.h"
+
+// func hasHWSupport() bool
+TEXT ·hasHWSupport(SB),NOSPLIT,$16-1
+	XOR	R0, R0          // set function code to 0 (query)
+	LA	mask-16(SP), R1 // 16-byte stack variable for mask
+	MOVD	$(0x38<<40), R3 // mask for bits 18-20 (big endian)
+
+	// check for KM AES functions
+	WORD	$0xB92E0024 // cipher message (KM)
+	MOVD	mask-16(SP), R2
+	AND	R3, R2
+	CMPBNE	R2, R3, notfound
+
+	// check for KMC AES functions
+	WORD	$0xB92F0024 // cipher message with chaining (KMC)
+	MOVD	mask-16(SP), R2
+	AND	R3, R2
+	CMPBNE	R2, R3, notfound
+
+	// check for KMCTR AES functions
+	WORD	$0xB92D4024 // cipher message with counter (KMCTR)
+	MOVD	mask-16(SP), R2
+	AND	R3, R2
+	CMPBNE	R2, R3, notfound
+
+	// check for KIMD GHASH function
+	WORD	$0xB93E0024    // compute intermediate message digest (KIMD)
+	MOVD	mask-8(SP), R2 // bits 64-127
+	MOVD	$(1<<62), R5
+	AND	R5, R2
+	CMPBNE	R2, R5, notfound
+
+	MOVB	$1, ret+0(FP)
+	RET
+notfound:
+	MOVB	$0, ret+0(FP)
+	RET
diff --git a/src/crypto/internal/cipherhw/cipherhw_amd64.go b/src/crypto/internal/cipherhw/cipherhw_amd64.go
new file mode 100644
index 0000000..be0d490
--- /dev/null
+++ b/src/crypto/internal/cipherhw/cipherhw_amd64.go
@@ -0,0 +1,16 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!gccgo,!appengine
+
+package cipherhw
+
+// defined in asm_amd64.s
+func hasAESNI() bool
+
+// AESGCMSupport returns true if the Go standard library supports AES-GCM in
+// hardware.
+func AESGCMSupport() bool {
+	return hasAESNI()
+}
diff --git a/src/crypto/internal/cipherhw/cipherhw_s390x.go b/src/crypto/internal/cipherhw/cipherhw_s390x.go
new file mode 100644
index 0000000..9cd7679
--- /dev/null
+++ b/src/crypto/internal/cipherhw/cipherhw_s390x.go
@@ -0,0 +1,18 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x,!gccgo,!appengine
+
+package cipherhw
+
+// hasHWSupport reports whether the AES-128, AES-192 and AES-256 cipher message
+// (KM) function codes are supported. Note that this function is expensive.
+// defined in asm_s390x.s
+func hasHWSupport() bool
+
+var hwSupport = hasHWSupport()
+
+func AESGCMSupport() bool {
+	return hwSupport
+}
diff --git a/src/crypto/internal/cipherhw/doc.go b/src/crypto/internal/cipherhw/doc.go
new file mode 100644
index 0000000..a75fcf6
--- /dev/null
+++ b/src/crypto/internal/cipherhw/doc.go
@@ -0,0 +1,7 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package cipherhw exposes common functions for detecting whether hardware
+// support for certain ciphers and authenticators is present.
+package cipherhw
diff --git a/src/crypto/internal/cipherhw/generic.go b/src/crypto/internal/cipherhw/generic.go
new file mode 100644
index 0000000..64d90d3
--- /dev/null
+++ b/src/crypto/internal/cipherhw/generic.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64,!s390x gccgo appengine
+
+package cipherhw
+
+func AESGCMSupport() bool {
+	return false
+}
diff --git a/src/crypto/md5/example_test.go b/src/crypto/md5/example_test.go
index d47bb45..af8c1bf 100644
--- a/src/crypto/md5/example_test.go
+++ b/src/crypto/md5/example_test.go
@@ -8,6 +8,8 @@ import (
 	"crypto/md5"
 	"fmt"
 	"io"
+	"log"
+	"os"
 )
 
 func ExampleNew() {
@@ -23,3 +25,18 @@ func ExampleSum() {
 	fmt.Printf("%x", md5.Sum(data))
 	// Output: b0804ec967f48520697662a204f5fe72
 }
+
+func ExampleNew_file() {
+	f, err := os.Open("file.txt")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer f.Close()
+
+	h := md5.New()
+	if _, err := io.Copy(h, f); err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("%x", h.Sum(nil))
+}
diff --git a/src/crypto/md5/md5block_amd64p32.s b/src/crypto/md5/md5block_amd64p32.s
index d918a67..ee05f86 100644
--- a/src/crypto/md5/md5block_amd64p32.s
+++ b/src/crypto/md5/md5block_amd64p32.s
@@ -17,7 +17,7 @@
 // Licence: I hereby disclaim the copyright on this code and place it
 // in the public domain.
 
-TEXT	·block(SB),NOSPLIT,$0-32
+TEXT	·block(SB),NOSPLIT,$0-16
 	MOVL	dig+0(FP),	R11
 	MOVL	p+4(FP),	SI
 	MOVL	p_len+8(FP), DX
diff --git a/src/crypto/rand/util_test.go b/src/crypto/rand/util_test.go
index 7b07689..48a2c3f 100644
--- a/src/crypto/rand/util_test.go
+++ b/src/crypto/rand/util_test.go
@@ -7,7 +7,9 @@ package rand_test
 import (
 	"crypto/rand"
 	"math/big"
+	mathrand "math/rand"
 	"testing"
+	"time"
 )
 
 // https://golang.org/issue/6849.
@@ -63,3 +65,10 @@ func TestIntNegativeMaxPanics(t *testing.T) {
 	b := new(big.Int).SetInt64(int64(-1))
 	testIntPanics(t, b)
 }
+
+func BenchmarkPrime(b *testing.B) {
+	r := mathrand.New(mathrand.NewSource(time.Now().UnixNano()))
+	for i := 0; i < b.N; i++ {
+		rand.Prime(r, 1024)
+	}
+}
diff --git a/src/crypto/rc4/rc4_arm.s b/src/crypto/rc4/rc4_arm.s
index 05e94cb..c726d6d 100644
--- a/src/crypto/rc4/rc4_arm.s
+++ b/src/crypto/rc4/rc4_arm.s
@@ -25,8 +25,8 @@ TEXT ·xorKeyStream(SB),NOSPLIT,$0
 	MOVW src+4(FP), Rsrc
 	MOVW n+8(FP), Rn
 	MOVW state+12(FP), Rstate
-	MOVW pi+16(FP), Rpi
-	MOVW pj+20(FP), Rpj
+	MOVW i+16(FP), Rpi
+	MOVW j+20(FP), Rpj
 	MOVBU (Rpi), Ri
 	MOVBU (Rpj), Rj
 	MOVW $0, Rk
diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go
index 5943056..f809a9b 100644
--- a/src/crypto/rsa/rsa.go
+++ b/src/crypto/rsa/rsa.go
@@ -27,6 +27,7 @@ import (
 	"errors"
 	"hash"
 	"io"
+	"math"
 	"math/big"
 )
 
@@ -214,6 +215,21 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (*PrivateKey
 		return nil, errors.New("crypto/rsa: GenerateMultiPrimeKey: nprimes must be >= 2")
 	}
 
+	if bits < 64 {
+		primeLimit := float64(uint64(1) << uint(bits/nprimes))
+		// pi approximates the number of primes less than primeLimit
+		pi := primeLimit / (math.Log(primeLimit) - 1)
+		// Generated primes start with 11 (in binary) so we can only
+		// use a quarter of them.
+		pi /= 4
+		// Use a factor of two to ensure that key generation terminates
+		// in a reasonable amount of time.
+		pi /= 2
+		if pi <= float64(nprimes) {
+			return nil, errors.New("crypto/rsa: too few primes of given length to generate an RSA key")
+		}
+	}
+
 	primes := make([]*big.Int, nprimes)
 
 NextSetOfPrimes:
@@ -268,9 +284,8 @@ NextSetOfPrimes:
 
 		g := new(big.Int)
 		priv.D = new(big.Int)
-		y := new(big.Int)
 		e := big.NewInt(int64(priv.E))
-		g.GCD(priv.D, y, e, totient)
+		g.GCD(priv.D, nil, e, totient)
 
 		if g.Cmp(bigOne) == 0 {
 			if priv.D.Sign() < 0 {
@@ -347,8 +362,8 @@ func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int {
 // values could be used to ensure that a ciphertext for one purpose cannot be
 // used for another by an attacker. If not required it can be empty.
 //
-// The message must be no longer than the length of the public modulus less
-// twice the hash length plus 2.
+// The message must be no longer than the length of the public modulus minus
+// twice the hash length, minus a further 2.
 func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) {
 	if err := checkPub(pub); err != nil {
 		return nil, err
diff --git a/src/crypto/rsa/rsa_test.go b/src/crypto/rsa/rsa_test.go
index 6902f9a..84b1674 100644
--- a/src/crypto/rsa/rsa_test.go
+++ b/src/crypto/rsa/rsa_test.go
@@ -73,6 +73,17 @@ func TestNPrimeKeyGeneration(t *testing.T) {
 	}
 }
 
+func TestImpossibleKeyGeneration(t *testing.T) {
+	// This test ensures that trying to generate toy RSA keys doesn't enter
+	// an infinite loop.
+	for i := 0; i < 32; i++ {
+		GenerateKey(rand.Reader, i)
+		GenerateMultiPrimeKey(rand.Reader, 3, i)
+		GenerateMultiPrimeKey(rand.Reader, 4, i)
+		GenerateMultiPrimeKey(rand.Reader, 5, i)
+	}
+}
+
 func TestGnuTLSKey(t *testing.T) {
 	// This is a key generated by `certtool --generate-privkey --bits 128`.
 	// It's such that de ≢ 1 mod φ(n), but is congruent mod the order of
diff --git a/src/crypto/sha1/example_test.go b/src/crypto/sha1/example_test.go
index 42aec8a..499055c 100644
--- a/src/crypto/sha1/example_test.go
+++ b/src/crypto/sha1/example_test.go
@@ -8,6 +8,8 @@ import (
 	"crypto/sha1"
 	"fmt"
 	"io"
+	"log"
+	"os"
 )
 
 func ExampleNew() {
@@ -23,3 +25,18 @@ func ExampleSum() {
 	fmt.Printf("% x", sha1.Sum(data))
 	// Output: af 06 49 23 bb f2 30 15 96 aa c4 c2 73 ba 32 17 8e bc 4a 96
 }
+
+func ExampleNew_file() {
+	f, err := os.Open("file.txt")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer f.Close()
+
+	h := sha1.New()
+	if _, err := io.Copy(h, f); err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("% x", h.Sum(nil))
+}
diff --git a/src/crypto/sha1/sha1.go b/src/crypto/sha1/sha1.go
index ac593b1..fbb2f94 100644
--- a/src/crypto/sha1/sha1.go
+++ b/src/crypto/sha1/sha1.go
@@ -90,7 +90,7 @@ func (d0 *digest) Sum(in []byte) []byte {
 
 func (d *digest) checkSum() [Size]byte {
 	len := d.len
-	// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
+	// Padding.  Add a 1 bit and 0 bits until 56 bytes mod 64.
 	var tmp [64]byte
 	tmp[0] = 0x80
 	if len%64 < 56 {
@@ -121,6 +121,74 @@ func (d *digest) checkSum() [Size]byte {
 	return digest
 }
 
+// ConstantTimeSum computes the same result of Sum() but in constant time
+func (d0 *digest) ConstantTimeSum(in []byte) []byte {
+	d := *d0
+	hash := d.constSum()
+	return append(in, hash[:]...)
+}
+
+func (d *digest) constSum() [Size]byte {
+	var length [8]byte
+	l := d.len << 3
+	for i := uint(0); i < 8; i++ {
+		length[i] = byte(l >> (56 - 8*i))
+	}
+
+	nx := byte(d.nx)
+	t := nx - 56                 // if nx < 56 then the MSB of t is one
+	mask1b := byte(int8(t) >> 7) // mask1b is 0xFF iff one block is enough
+
+	separator := byte(0x80) // gets reset to 0x00 once used
+	for i := byte(0); i < chunk; i++ {
+		mask := byte(int8(i-nx) >> 7) // 0x00 after the end of data
+
+		// if we reached the end of the data, replace with 0x80 or 0x00
+		d.x[i] = (^mask & separator) | (mask & d.x[i])
+
+		// zero the separator once used
+		separator &= mask
+
+		if i >= 56 {
+			// we might have to write the length here if all fit in one block
+			d.x[i] |= mask1b & length[i-56]
+		}
+	}
+
+	// compress, and only keep the digest if all fit in one block
+	block(d, d.x[:])
+
+	var digest [Size]byte
+	for i, s := range d.h {
+		digest[i*4] = mask1b & byte(s>>24)
+		digest[i*4+1] = mask1b & byte(s>>16)
+		digest[i*4+2] = mask1b & byte(s>>8)
+		digest[i*4+3] = mask1b & byte(s)
+	}
+
+	for i := byte(0); i < chunk; i++ {
+		// second block, it's always past the end of data, might start with 0x80
+		if i < 56 {
+			d.x[i] = separator
+			separator = 0
+		} else {
+			d.x[i] = length[i-56]
+		}
+	}
+
+	// compress, and only keep the digest if we actually needed the second block
+	block(d, d.x[:])
+
+	for i, s := range d.h {
+		digest[i*4] |= ^mask1b & byte(s>>24)
+		digest[i*4+1] |= ^mask1b & byte(s>>16)
+		digest[i*4+2] |= ^mask1b & byte(s>>8)
+		digest[i*4+3] |= ^mask1b & byte(s)
+	}
+
+	return digest
+}
+
 // Sum returns the SHA1 checksum of the data.
 func Sum(data []byte) [Size]byte {
 	var d digest
diff --git a/src/crypto/sha1/sha1_test.go b/src/crypto/sha1/sha1_test.go
index 214afc5..3e59a5d 100644
--- a/src/crypto/sha1/sha1_test.go
+++ b/src/crypto/sha1/sha1_test.go
@@ -61,15 +61,24 @@ func TestGolden(t *testing.T) {
 			t.Fatalf("Sum function: sha1(%s) = %s want %s", g.in, s, g.out)
 		}
 		c := New()
-		for j := 0; j < 3; j++ {
-			if j < 2 {
+		for j := 0; j < 4; j++ {
+			var sum []byte
+			switch j {
+			case 0, 1:
 				io.WriteString(c, g.in)
-			} else {
+				sum = c.Sum(nil)
+			case 2:
 				io.WriteString(c, g.in[0:len(g.in)/2])
 				c.Sum(nil)
 				io.WriteString(c, g.in[len(g.in)/2:])
+				sum = c.Sum(nil)
+			case 3:
+				io.WriteString(c, g.in[0:len(g.in)/2])
+				c.(*digest).ConstantTimeSum(nil)
+				io.WriteString(c, g.in[len(g.in)/2:])
+				sum = c.(*digest).ConstantTimeSum(nil)
 			}
-			s := fmt.Sprintf("%x", c.Sum(nil))
+			s := fmt.Sprintf("%x", sum)
 			if s != g.out {
 				t.Fatalf("sha1[%d](%s) = %s want %s", j, g.in, s, g.out)
 			}
diff --git a/src/crypto/sha1/sha1block_amd64.go b/src/crypto/sha1/sha1block_amd64.go
index fd85a42..0320e41 100644
--- a/src/crypto/sha1/sha1block_amd64.go
+++ b/src/crypto/sha1/sha1block_amd64.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The Go Authors.  All rights reserved.
+// Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
diff --git a/src/crypto/sha1/sha1block_amd64p32.s b/src/crypto/sha1/sha1block_amd64p32.s
index efebbf0..0159d23 100644
--- a/src/crypto/sha1/sha1block_amd64p32.s
+++ b/src/crypto/sha1/sha1block_amd64p32.s
@@ -91,7 +91,7 @@
 	FUNC4(a, b, c, d, e); \
 	MIX(a, b, c, d, e, 0xCA62C1D6)
 
-TEXT ·block(SB),NOSPLIT,$64-32
+TEXT ·block(SB),NOSPLIT,$64-16
 	MOVL	dig+0(FP),	R14
 	MOVL	p_base+4(FP),	SI
 	MOVL	p_len+8(FP),	DX
diff --git a/src/crypto/sha256/example_test.go b/src/crypto/sha256/example_test.go
new file mode 100644
index 0000000..7d73120
--- /dev/null
+++ b/src/crypto/sha256/example_test.go
@@ -0,0 +1,41 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sha256_test
+
+import (
+	"crypto/sha256"
+	"fmt"
+	"io"
+	"log"
+	"os"
+)
+
+func ExampleSum256() {
+	sum := sha256.Sum256([]byte("hello world\n"))
+	fmt.Printf("%x", sum)
+	// Output: a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
+}
+
+func ExampleNew() {
+	h := sha256.New()
+	h.Write([]byte("hello world\n"))
+	fmt.Printf("%x", h.Sum(nil))
+	// Output: a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
+}
+
+func ExampleNew_file() {
+	f, err := os.Open("file.txt")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer f.Close()
+
+	h := sha256.New()
+	if _, err := io.Copy(h, f); err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("%x", h.Sum(nil))
+}
diff --git a/src/crypto/sha256/sha256block_386.s b/src/crypto/sha256/sha256block_386.s
index e0353c3..33ed027 100644
--- a/src/crypto/sha256/sha256block_386.s
+++ b/src/crypto/sha256/sha256block_386.s
@@ -141,7 +141,7 @@
 	MSGSCHEDULE1(index); \
 	SHA256ROUND(index, const, a, b, c, d, e, f, g, h)
 
-TEXT ·block(SB),0,$296-12
+TEXT ·block(SB),0,$296-16
 	MOVL	p_base+4(FP), SI
 	MOVL	p_len+8(FP), DX
 	SHRL	$6, DX
diff --git a/src/crypto/sha256/sha256block_amd64.s b/src/crypto/sha256/sha256block_amd64.s
index 6ab3b52..edf7ad1 100644
--- a/src/crypto/sha256/sha256block_amd64.s
+++ b/src/crypto/sha256/sha256block_amd64.s
@@ -1,4 +1,4 @@
-// Copyright 2013 The Go Authors.  All rights reserved.
+// Copyright 2013 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
@@ -302,7 +302,7 @@
 	ADDL    y0, y2;                      \ // y2 = S1 + CH					// --
 	;                                    \
 	VPXOR   XTMP4, XTMP3, XTMP1;         \ // XTMP1 = s0
-	VPSHUFD $-6, XDWORD3, XTMP2;         \ // XTMP2 = W[-2] {BBAA}
+	VPSHUFD $0xFA, XDWORD3, XTMP2;       \ // XTMP2 = W[-2] {BBAA}
 	ORL     T1, y3;                      \ // y3 = MAJ = (a|c)&b)|(a&c)             // MAJ
 	ADDL    y1, h;                       \ // h = k + w + h + S0                    // --
 	;                                    \
diff --git a/src/crypto/sha256/sha256block_decl.go b/src/crypto/sha256/sha256block_decl.go
index e6caff9..fe07e53 100644
--- a/src/crypto/sha256/sha256block_decl.go
+++ b/src/crypto/sha256/sha256block_decl.go
@@ -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 386 amd64 s390x
+// +build 386 amd64 s390x ppc64le
 
 package sha256
 
diff --git a/src/crypto/sha256/sha256block_generic.go b/src/crypto/sha256/sha256block_generic.go
index 1a01969..a182a5e 100644
--- a/src/crypto/sha256/sha256block_generic.go
+++ b/src/crypto/sha256/sha256block_generic.go
@@ -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 !amd64,!386,!s390x
+// +build !amd64,!386,!s390x,!ppc64le
 
 package sha256
 
diff --git a/src/crypto/sha256/sha256block_ppc64le.s b/src/crypto/sha256/sha256block_ppc64le.s
new file mode 100644
index 0000000..7ac5000
--- /dev/null
+++ b/src/crypto/sha256/sha256block_ppc64le.s
@@ -0,0 +1,269 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// SHA256 block routine. See sha256block.go for Go equivalent.
+//
+// The algorithm is detailed in FIPS 180-4:
+//
+//  http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
+//
+// Wt = Mt; for 0 <= t <= 15
+// Wt = SIGMA1(Wt-2) + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63
+//
+// a = H0
+// b = H1
+// c = H2
+// d = H3
+// e = H4
+// f = H5
+// g = H6
+// h = H7
+//
+// for t = 0 to 63 {
+//    T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + Kt + Wt
+//    T2 = BIGSIGMA0(a) + Maj(a,b,c)
+//    h = g
+//    g = f
+//    f = e
+//    e = d + T1
+//    d = c
+//    c = b
+//    b = a
+//    a = T1 + T2
+// }
+//
+// H0 = a + H0
+// H1 = b + H1
+// H2 = c + H2
+// H3 = d + H3
+// H4 = e + H4
+// H5 = f + H5
+// H6 = g + H6
+// H7 = h + H7
+
+// Wt = Mt; for 0 <= t <= 15
+#define MSGSCHEDULE0(index) \
+	MOVWZ	(index*4)(R26), R7; \
+	RLWNM	$24, R7, $-1, R11; \
+	RLWMI	$8, R7, $0x00FF0000, R11; \
+	RLWMI	$8, R7, $0x000000FF, R11; \
+	MOVWZ	R11, R7; \
+	MOVWZ	R7, (index*4)(R27)
+
+// Wt = SIGMA1(Wt-2) + Wt-7 + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63
+//   SIGMA0(x) = ROTR(7,x) XOR ROTR(18,x) XOR SHR(3,x)
+//   SIGMA1(x) = ROTR(17,x) XOR ROTR(19,x) XOR SHR(10,x)
+#define MSGSCHEDULE1(index) \
+	MOVWZ	((index-2)*4)(R27), R7; \
+	MOVWZ	R7, R9; \
+	RLWNM	$32-17, R7, $-1, R7; \
+	MOVWZ	R9, R10; \
+	RLWNM	$32-19, R9, $-1, R9; \
+	SRW	$10, R10; \
+	MOVWZ	((index-15)*4)(R27), R8; \
+	XOR	R9, R7; \
+	MOVWZ	R8, R9; \
+	XOR	R10, R7; \
+	RLWNM	$32-7, R8, $-1, R8; \
+	MOVWZ	R9, R10; \
+	SRW	$3, R10; \
+	RLWNM	$32-18, R9, $-1, R9; \
+	MOVWZ	((index-7)*4)(R27), R11; \
+	ADD	R11, R7; \
+	XOR	R9, R8; \
+	XOR	R10, R8; \
+	MOVWZ	((index-16)*4)(R27), R11; \
+	ADD	R11, R8; \
+	ADD	R8, R7; \
+	MOVWZ	R7, ((index)*4)(R27)
+
+// T1 = h + BIGSIGMA1(e) + Ch(e, f, g) + Kt + Wt
+//   BIGSIGMA1(x) = ROTR(6,x) XOR ROTR(11,x) XOR ROTR(25,x)
+//   Ch(x, y, z) = (x AND y) XOR (NOT x AND z)
+#define SHA256T1(const, e, f, g, h) \
+	ADD	R7, h; \
+	MOVWZ	e, R7; \
+	ADD	$const, h; \
+	MOVWZ	e, R9; \
+	RLWNM	$32-6, R7, $-1, R7; \
+	MOVWZ	e, R10; \
+	RLWNM	$32-11, R9, $-1, R9; \
+	XOR	R9, R7; \
+	MOVWZ	e, R9; \
+	RLWNM	$32-25, R10, $-1, R10; \
+	AND	f, R9; \
+	XOR	R7, R10; \
+	MOVWZ	e, R7; \
+	NOR	R7, R7, R7; \
+	ADD	R10, h; \
+	AND	g, R7; \
+	XOR	R9, R7; \
+	ADD	h, R7
+
+// T2 = BIGSIGMA0(a) + Maj(a, b, c)
+//   BIGSIGMA0(x) = ROTR(2,x) XOR ROTR(13,x) XOR ROTR(22,x)
+//   Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z)
+#define SHA256T2(a, b, c) \
+	MOVWZ	a, R28; \
+	MOVWZ	c, R8; \
+	RLWNM	$32-2, R28, $-1, R28; \
+	MOVWZ	a, R10; \
+	AND	b, R8; \
+	RLWNM	$32-13, R10, $-1, R10; \
+	MOVWZ	a, R9; \
+	AND	c, R9; \
+	XOR	R10, R28; \
+	XOR	R9, R8; \
+	MOVWZ	a, R10; \
+	MOVWZ	b, R9; \
+	RLWNM	$32-22, R10, $-1, R10; \
+	AND	a, R9; \
+	XOR	R9, R8; \
+	XOR	R10, R28; \
+	ADD	R28, R8
+
+// Calculate T1 and T2, then e = d + T1 and a = T1 + T2.
+// The values for e and a are stored in d and h, ready for rotation.
+#define SHA256ROUND(index, const, a, b, c, d, e, f, g, h) \
+	SHA256T1(const, e, f, g, h); \
+	SHA256T2(a, b, c); \
+	MOVWZ	R8, h; \
+	ADD	R7, d; \
+	ADD	R7, h
+
+#define SHA256ROUND0(index, const, a, b, c, d, e, f, g, h) \
+	MSGSCHEDULE0(index); \
+	SHA256ROUND(index, const, a, b, c, d, e, f, g, h)
+
+#define SHA256ROUND1(index, const, a, b, c, d, e, f, g, h) \
+	MSGSCHEDULE1(index); \
+	SHA256ROUND(index, const, a, b, c, d, e, f, g, h)
+
+// func block(dig *digest, p []byte)
+TEXT ·block(SB),0,$296-32
+	MOVD	p_base+8(FP), R26
+	MOVD	p_len+16(FP), R29
+	SRD	$6, R29
+	SLD	$6, R29
+
+	ADD	R26, R29, R28
+
+	MOVD	R28, 256(R1)
+	CMP	R26, R28
+	BEQ	end
+
+	MOVD	dig+0(FP), R27
+	MOVWZ	(0*4)(R27), R14		// a = H0
+	MOVWZ	(1*4)(R27), R15		// b = H1
+	MOVWZ	(2*4)(R27), R16		// c = H2
+	MOVWZ	(3*4)(R27), R17		// d = H3
+	MOVWZ	(4*4)(R27), R18		// e = H4
+	MOVWZ	(5*4)(R27), R19		// f = H5
+	MOVWZ	(6*4)(R27), R20		// g = H6
+	MOVWZ	(7*4)(R27), R21		// h = H7
+
+loop:
+	MOVD	R1, R27		// R27: message schedule
+
+	SHA256ROUND0(0, 0x428a2f98, R14, R15, R16, R17, R18, R19, R20, R21)
+	SHA256ROUND0(1, 0x71374491, R21, R14, R15, R16, R17, R18, R19, R20)
+	SHA256ROUND0(2, 0xb5c0fbcf, R20, R21, R14, R15, R16, R17, R18, R19)
+	SHA256ROUND0(3, 0xe9b5dba5, R19, R20, R21, R14, R15, R16, R17, R18)
+	SHA256ROUND0(4, 0x3956c25b, R18, R19, R20, R21, R14, R15, R16, R17)
+	SHA256ROUND0(5, 0x59f111f1, R17, R18, R19, R20, R21, R14, R15, R16)
+	SHA256ROUND0(6, 0x923f82a4, R16, R17, R18, R19, R20, R21, R14, R15)
+	SHA256ROUND0(7, 0xab1c5ed5, R15, R16, R17, R18, R19, R20, R21, R14)
+	SHA256ROUND0(8, 0xd807aa98, R14, R15, R16, R17, R18, R19, R20, R21)
+	SHA256ROUND0(9, 0x12835b01, R21, R14, R15, R16, R17, R18, R19, R20)
+	SHA256ROUND0(10, 0x243185be, R20, R21, R14, R15, R16, R17, R18, R19)
+	SHA256ROUND0(11, 0x550c7dc3, R19, R20, R21, R14, R15, R16, R17, R18)
+	SHA256ROUND0(12, 0x72be5d74, R18, R19, R20, R21, R14, R15, R16, R17)
+	SHA256ROUND0(13, 0x80deb1fe, R17, R18, R19, R20, R21, R14, R15, R16)
+	SHA256ROUND0(14, 0x9bdc06a7, R16, R17, R18, R19, R20, R21, R14, R15)
+	SHA256ROUND0(15, 0xc19bf174, R15, R16, R17, R18, R19, R20, R21, R14)
+
+	SHA256ROUND1(16, 0xe49b69c1, R14, R15, R16, R17, R18, R19, R20, R21)
+	SHA256ROUND1(17, 0xefbe4786, R21, R14, R15, R16, R17, R18, R19, R20)
+	SHA256ROUND1(18, 0x0fc19dc6, R20, R21, R14, R15, R16, R17, R18, R19)
+	SHA256ROUND1(19, 0x240ca1cc, R19, R20, R21, R14, R15, R16, R17, R18)
+	SHA256ROUND1(20, 0x2de92c6f, R18, R19, R20, R21, R14, R15, R16, R17)
+	SHA256ROUND1(21, 0x4a7484aa, R17, R18, R19, R20, R21, R14, R15, R16)
+	SHA256ROUND1(22, 0x5cb0a9dc, R16, R17, R18, R19, R20, R21, R14, R15)
+	SHA256ROUND1(23, 0x76f988da, R15, R16, R17, R18, R19, R20, R21, R14)
+	SHA256ROUND1(24, 0x983e5152, R14, R15, R16, R17, R18, R19, R20, R21)
+	SHA256ROUND1(25, 0xa831c66d, R21, R14, R15, R16, R17, R18, R19, R20)
+	SHA256ROUND1(26, 0xb00327c8, R20, R21, R14, R15, R16, R17, R18, R19)
+	SHA256ROUND1(27, 0xbf597fc7, R19, R20, R21, R14, R15, R16, R17, R18)
+	SHA256ROUND1(28, 0xc6e00bf3, R18, R19, R20, R21, R14, R15, R16, R17)
+	SHA256ROUND1(29, 0xd5a79147, R17, R18, R19, R20, R21, R14, R15, R16)
+	SHA256ROUND1(30, 0x06ca6351, R16, R17, R18, R19, R20, R21, R14, R15)
+	SHA256ROUND1(31, 0x14292967, R15, R16, R17, R18, R19, R20, R21, R14)
+	SHA256ROUND1(32, 0x27b70a85, R14, R15, R16, R17, R18, R19, R20, R21)
+	SHA256ROUND1(33, 0x2e1b2138, R21, R14, R15, R16, R17, R18, R19, R20)
+	SHA256ROUND1(34, 0x4d2c6dfc, R20, R21, R14, R15, R16, R17, R18, R19)
+	SHA256ROUND1(35, 0x53380d13, R19, R20, R21, R14, R15, R16, R17, R18)
+	SHA256ROUND1(36, 0x650a7354, R18, R19, R20, R21, R14, R15, R16, R17)
+	SHA256ROUND1(37, 0x766a0abb, R17, R18, R19, R20, R21, R14, R15, R16)
+	SHA256ROUND1(38, 0x81c2c92e, R16, R17, R18, R19, R20, R21, R14, R15)
+	SHA256ROUND1(39, 0x92722c85, R15, R16, R17, R18, R19, R20, R21, R14)
+	SHA256ROUND1(40, 0xa2bfe8a1, R14, R15, R16, R17, R18, R19, R20, R21)
+	SHA256ROUND1(41, 0xa81a664b, R21, R14, R15, R16, R17, R18, R19, R20)
+	SHA256ROUND1(42, 0xc24b8b70, R20, R21, R14, R15, R16, R17, R18, R19)
+	SHA256ROUND1(43, 0xc76c51a3, R19, R20, R21, R14, R15, R16, R17, R18)
+	SHA256ROUND1(44, 0xd192e819, R18, R19, R20, R21, R14, R15, R16, R17)
+	SHA256ROUND1(45, 0xd6990624, R17, R18, R19, R20, R21, R14, R15, R16)
+	SHA256ROUND1(46, 0xf40e3585, R16, R17, R18, R19, R20, R21, R14, R15)
+	SHA256ROUND1(47, 0x106aa070, R15, R16, R17, R18, R19, R20, R21, R14)
+	SHA256ROUND1(48, 0x19a4c116, R14, R15, R16, R17, R18, R19, R20, R21)
+	SHA256ROUND1(49, 0x1e376c08, R21, R14, R15, R16, R17, R18, R19, R20)
+	SHA256ROUND1(50, 0x2748774c, R20, R21, R14, R15, R16, R17, R18, R19)
+	SHA256ROUND1(51, 0x34b0bcb5, R19, R20, R21, R14, R15, R16, R17, R18)
+	SHA256ROUND1(52, 0x391c0cb3, R18, R19, R20, R21, R14, R15, R16, R17)
+	SHA256ROUND1(53, 0x4ed8aa4a, R17, R18, R19, R20, R21, R14, R15, R16)
+	SHA256ROUND1(54, 0x5b9cca4f, R16, R17, R18, R19, R20, R21, R14, R15)
+	SHA256ROUND1(55, 0x682e6ff3, R15, R16, R17, R18, R19, R20, R21, R14)
+	SHA256ROUND1(56, 0x748f82ee, R14, R15, R16, R17, R18, R19, R20, R21)
+	SHA256ROUND1(57, 0x78a5636f, R21, R14, R15, R16, R17, R18, R19, R20)
+	SHA256ROUND1(58, 0x84c87814, R20, R21, R14, R15, R16, R17, R18, R19)
+	SHA256ROUND1(59, 0x8cc70208, R19, R20, R21, R14, R15, R16, R17, R18)
+	SHA256ROUND1(60, 0x90befffa, R18, R19, R20, R21, R14, R15, R16, R17)
+	SHA256ROUND1(61, 0xa4506ceb, R17, R18, R19, R20, R21, R14, R15, R16)
+	SHA256ROUND1(62, 0xbef9a3f7, R16, R17, R18, R19, R20, R21, R14, R15)
+	SHA256ROUND1(63, 0xc67178f2, R15, R16, R17, R18, R19, R20, R21, R14)
+
+	MOVD	dig+0(FP), R27
+	MOVWZ	(0*4)(R27), R11
+	ADD	R11, R14	// H0 = a + H0
+	MOVWZ	R14, (0*4)(R27)
+	MOVWZ	(1*4)(R27), R11
+	ADD	R11, R15	// H1 = b + H1
+	MOVWZ	R15, (1*4)(R27)
+	MOVWZ	(2*4)(R27), R11
+	ADD	R11, R16	// H2 = c + H2
+	MOVWZ	R16, (2*4)(R27)
+	MOVWZ	(3*4)(R27), R11
+	ADD	R11, R17	// H3 = d + H3
+	MOVWZ	R17, (3*4)(R27)
+	MOVWZ	(4*4)(R27), R11
+	ADD	R11, R18	// H4 = e + H4
+	MOVWZ	R18, (4*4)(R27)
+	MOVWZ	(5*4)(R27), R11
+	ADD	R11, R19	// H5 = f + H5
+	MOVWZ	R19, (5*4)(R27)
+	MOVWZ	(6*4)(R27), R11
+	ADD	R11, R20	// H6 = g + H6
+	MOVWZ	R20, (6*4)(R27)
+	MOVWZ	(7*4)(R27), R11
+	ADD	R11, R21	// H7 = h + H7
+	MOVWZ	R21, (7*4)(R27)
+
+	ADD	$64, R26
+	MOVD	256(R1), R11
+	CMPU	R26, R11
+	BLT	loop
+
+end:
+	RET
diff --git a/src/crypto/sha512/sha512block_decl.go b/src/crypto/sha512/sha512block_decl.go
index 47d656a..8194506 100644
--- a/src/crypto/sha512/sha512block_decl.go
+++ b/src/crypto/sha512/sha512block_decl.go
@@ -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 amd64 s390x
+// +build amd64 s390x ppc64le
 
 package sha512
 
diff --git a/src/crypto/sha512/sha512block_generic.go b/src/crypto/sha512/sha512block_generic.go
index 2c691ba..08f2e07 100644
--- a/src/crypto/sha512/sha512block_generic.go
+++ b/src/crypto/sha512/sha512block_generic.go
@@ -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 !amd64,!s390x
+// +build !amd64,!s390x,!ppc64le
 
 package sha512
 
diff --git a/src/crypto/sha512/sha512block_ppc64le.s b/src/crypto/sha512/sha512block_ppc64le.s
new file mode 100644
index 0000000..7b338d8
--- /dev/null
+++ b/src/crypto/sha512/sha512block_ppc64le.s
@@ -0,0 +1,293 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// SHA512 block routine. See sha512block.go for Go equivalent.
+//
+// The algorithm is detailed in FIPS 180-4:
+//
+//  http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
+//
+// Wt = Mt; for 0 <= t <= 15
+// Wt = SIGMA1(Wt-2) + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 79
+//
+// a = H0
+// b = H1
+// c = H2
+// d = H3
+// e = H4
+// f = H5
+// g = H6
+// h = H7
+//
+// for t = 0 to 79 {
+//    T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + Kt + Wt
+//    T2 = BIGSIGMA0(a) + Maj(a,b,c)
+//    h = g
+//    g = f
+//    f = e
+//    e = d + T1
+//    d = c
+//    c = b
+//    b = a
+//    a = T1 + T2
+// }
+//
+// H0 = a + H0
+// H1 = b + H1
+// H2 = c + H2
+// H3 = d + H3
+// H4 = e + H4
+// H5 = f + H5
+// H6 = g + H6
+// H7 = h + H7
+
+// Wt = Mt; for 0 <= t <= 15
+#define MSGSCHEDULE0(index) \
+	MOVD	(index*8)(R6), R14; \
+	RLWNM	$24, R14, $-1, R21; \
+	RLWMI	$8, R14, $0x00FF0000, R21; \
+	RLWMI	$8, R14, $0x000000FF, R21; \
+	SLD	$32, R21; \
+	SRD	$32, R14, R20; \
+	RLWNM	$24, R20, $-1, R14; \
+	RLWMI	$8, R20, $0x00FF0000, R14; \
+	RLWMI	$8, R20, $0x000000FF, R14; \
+	OR	R21, R14; \
+	MOVD	R14, (index*8)(R9)
+
+// Wt = SIGMA1(Wt-2) + Wt-7 + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 79
+//   SIGMA0(x) = ROTR(1,x) XOR ROTR(8,x) XOR SHR(7,x)
+//   SIGMA1(x) = ROTR(19,x) XOR ROTR(61,x) XOR SHR(6,x)
+#define MSGSCHEDULE1(index) \
+	MOVD	((index-2)*8)(R9), R14; \
+	MOVD	R14, R16; \
+	RLDCL	$64-19, R14, $-1, R14; \
+	MOVD	R16, R17; \
+	RLDCL	$64-61, R16, $-1, R16; \
+	SRD	$6, R17; \
+	MOVD	((index-15)*8)(R9), R15; \
+	XOR	R16, R14; \
+	MOVD	R15, R16; \
+	XOR	R17, R14; \
+	RLDCL	$64-1, R15, $-1, R15; \
+	MOVD	R16, R17; \
+	SRD	$7, R17; \
+	RLDCL	$64-8, R16, $-1, R16; \
+	MOVD	((index-7)*8)(R9), R21; \
+	ADD	R21, R14; \
+	XOR	R16, R15; \
+	XOR	R17, R15; \
+	MOVD	((index-16)*8)(R9), R21; \
+	ADD	R21, R15; \
+	ADD	R15, R14; \
+	MOVD	R14, ((index)*8)(R9)
+
+// Calculate T1 in R14 - uses R14, R16 and R17 registers.
+// h is also used as an accumulator. Wt is passed in R14.
+//   T1 = h + BIGSIGMA1(e) + Ch(e, f, g) + Kt + Wt
+//     BIGSIGMA1(x) = ROTR(14,x) XOR ROTR(18,x) XOR ROTR(41,x)
+//     Ch(x, y, z) = (x AND y) XOR (NOT x AND z)
+#define SHA512T1(const, e, f, g, h) \
+	MOVD	$const, R17; \
+	ADD	R14, h; \
+	MOVD	e, R14; \
+	ADD	R17, h; \
+	MOVD	e, R16; \
+	RLDCL	$64-14, R14, $-1, R14; \
+	MOVD	e, R17; \
+	RLDCL	$64-18, R16, $-1, R16; \
+	XOR	R16, R14; \
+	MOVD	e, R16; \
+	RLDCL	$64-41, R17, $-1, R17; \
+	AND	f, R16; \
+	XOR	R14, R17; \
+	MOVD	e, R14; \
+	NOR	R14, R14, R14; \
+	ADD	R17, h; \
+	AND	g, R14; \
+	XOR	R16, R14; \
+	ADD	h, R14
+
+// Calculate T2 in R15 - uses R15, R16, R17 and R8 registers.
+//   T2 = BIGSIGMA0(a) + Maj(a, b, c)
+//     BIGSIGMA0(x) = ROTR(28,x) XOR ROTR(34,x) XOR ROTR(39,x)
+//     Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z)
+#define SHA512T2(a, b, c) \
+	MOVD	a, R8; \
+	MOVD	c, R15; \
+	RLDCL	$64-28, R8, $-1, R8; \
+	MOVD	a, R17; \
+	AND	b, R15; \
+	RLDCL	$64-34, R17, $-1, R17; \
+	MOVD	a, R16; \
+	AND	c, R16; \
+	XOR	R17, R8; \
+	XOR	R16, R15; \
+	MOVD	a, R17; \
+	MOVD	b, R16; \
+	RLDCL	$64-39, R17, $-1, R17; \
+	AND	a, R16; \
+	XOR	R16, R15; \
+	XOR	R17, R8; \
+	ADD	R8, R15
+
+// Calculate T1 and T2, then e = d + T1 and a = T1 + T2.
+// The values for e and a are stored in d and h, ready for rotation.
+#define SHA512ROUND(index, const, a, b, c, d, e, f, g, h) \
+	SHA512T1(const, e, f, g, h); \
+	SHA512T2(a, b, c); \
+	MOVD	R15, h; \
+	ADD	R14, d; \
+	ADD	R14, h
+
+#define SHA512ROUND0(index, const, a, b, c, d, e, f, g, h) \
+	MSGSCHEDULE0(index); \
+	SHA512ROUND(index, const, a, b, c, d, e, f, g, h)
+
+#define SHA512ROUND1(index, const, a, b, c, d, e, f, g, h) \
+	MSGSCHEDULE1(index); \
+	SHA512ROUND(index, const, a, b, c, d, e, f, g, h)
+
+// func block(dig *digest, p []byte)
+TEXT ·block(SB),0,$680-32
+	MOVD	p_base+8(FP), R6
+	MOVD	p_len+16(FP), R7
+	SRD	$7, R7
+	SLD	$7, R7
+
+	ADD	R6, R7, R8
+	MOVD	R8, 640(R1)
+	CMP	R6, R8
+	BEQ	end
+
+	MOVD	dig+0(FP), R9
+	MOVD	(0*8)(R9), R22		// a = H0
+	MOVD	(1*8)(R9), R23		// b = H1
+	MOVD	(2*8)(R9), R24		// c = H2
+	MOVD	(3*8)(R9), R25		// d = H3
+	MOVD	(4*8)(R9), R26		// e = H4
+	MOVD	(5*8)(R9), R27		// f = H5
+	MOVD	(6*8)(R9), R28		// g = H6
+	MOVD	(7*8)(R9), R29		// h = H7
+
+loop:
+	MOVD	R1, R9			// R9: message schedule
+
+	SHA512ROUND0(0, 0x428a2f98d728ae22, R22, R23, R24, R25, R26, R27, R28, R29)
+	SHA512ROUND0(1, 0x7137449123ef65cd, R29, R22, R23, R24, R25, R26, R27, R28)
+	SHA512ROUND0(2, 0xb5c0fbcfec4d3b2f, R28, R29, R22, R23, R24, R25, R26, R27)
+	SHA512ROUND0(3, 0xe9b5dba58189dbbc, R27, R28, R29, R22, R23, R24, R25, R26)
+	SHA512ROUND0(4, 0x3956c25bf348b538, R26, R27, R28, R29, R22, R23, R24, R25)
+	SHA512ROUND0(5, 0x59f111f1b605d019, R25, R26, R27, R28, R29, R22, R23, R24)
+	SHA512ROUND0(6, 0x923f82a4af194f9b, R24, R25, R26, R27, R28, R29, R22, R23)
+	SHA512ROUND0(7, 0xab1c5ed5da6d8118, R23, R24, R25, R26, R27, R28, R29, R22)
+	SHA512ROUND0(8, 0xd807aa98a3030242, R22, R23, R24, R25, R26, R27, R28, R29)
+	SHA512ROUND0(9, 0x12835b0145706fbe, R29, R22, R23, R24, R25, R26, R27, R28)
+	SHA512ROUND0(10, 0x243185be4ee4b28c, R28, R29, R22, R23, R24, R25, R26, R27)
+	SHA512ROUND0(11, 0x550c7dc3d5ffb4e2, R27, R28, R29, R22, R23, R24, R25, R26)
+	SHA512ROUND0(12, 0x72be5d74f27b896f, R26, R27, R28, R29, R22, R23, R24, R25)
+	SHA512ROUND0(13, 0x80deb1fe3b1696b1, R25, R26, R27, R28, R29, R22, R23, R24)
+	SHA512ROUND0(14, 0x9bdc06a725c71235, R24, R25, R26, R27, R28, R29, R22, R23)
+	SHA512ROUND0(15, 0xc19bf174cf692694, R23, R24, R25, R26, R27, R28, R29, R22)
+
+	SHA512ROUND1(16, 0xe49b69c19ef14ad2, R22, R23, R24, R25, R26, R27, R28, R29)
+	SHA512ROUND1(17, 0xefbe4786384f25e3, R29, R22, R23, R24, R25, R26, R27, R28)
+	SHA512ROUND1(18, 0x0fc19dc68b8cd5b5, R28, R29, R22, R23, R24, R25, R26, R27)
+	SHA512ROUND1(19, 0x240ca1cc77ac9c65, R27, R28, R29, R22, R23, R24, R25, R26)
+	SHA512ROUND1(20, 0x2de92c6f592b0275, R26, R27, R28, R29, R22, R23, R24, R25)
+	SHA512ROUND1(21, 0x4a7484aa6ea6e483, R25, R26, R27, R28, R29, R22, R23, R24)
+	SHA512ROUND1(22, 0x5cb0a9dcbd41fbd4, R24, R25, R26, R27, R28, R29, R22, R23)
+	SHA512ROUND1(23, 0x76f988da831153b5, R23, R24, R25, R26, R27, R28, R29, R22)
+	SHA512ROUND1(24, 0x983e5152ee66dfab, R22, R23, R24, R25, R26, R27, R28, R29)
+	SHA512ROUND1(25, 0xa831c66d2db43210, R29, R22, R23, R24, R25, R26, R27, R28)
+	SHA512ROUND1(26, 0xb00327c898fb213f, R28, R29, R22, R23, R24, R25, R26, R27)
+	SHA512ROUND1(27, 0xbf597fc7beef0ee4, R27, R28, R29, R22, R23, R24, R25, R26)
+	SHA512ROUND1(28, 0xc6e00bf33da88fc2, R26, R27, R28, R29, R22, R23, R24, R25)
+	SHA512ROUND1(29, 0xd5a79147930aa725, R25, R26, R27, R28, R29, R22, R23, R24)
+	SHA512ROUND1(30, 0x06ca6351e003826f, R24, R25, R26, R27, R28, R29, R22, R23)
+	SHA512ROUND1(31, 0x142929670a0e6e70, R23, R24, R25, R26, R27, R28, R29, R22)
+	SHA512ROUND1(32, 0x27b70a8546d22ffc, R22, R23, R24, R25, R26, R27, R28, R29)
+	SHA512ROUND1(33, 0x2e1b21385c26c926, R29, R22, R23, R24, R25, R26, R27, R28)
+	SHA512ROUND1(34, 0x4d2c6dfc5ac42aed, R28, R29, R22, R23, R24, R25, R26, R27)
+	SHA512ROUND1(35, 0x53380d139d95b3df, R27, R28, R29, R22, R23, R24, R25, R26)
+	SHA512ROUND1(36, 0x650a73548baf63de, R26, R27, R28, R29, R22, R23, R24, R25)
+	SHA512ROUND1(37, 0x766a0abb3c77b2a8, R25, R26, R27, R28, R29, R22, R23, R24)
+	SHA512ROUND1(38, 0x81c2c92e47edaee6, R24, R25, R26, R27, R28, R29, R22, R23)
+	SHA512ROUND1(39, 0x92722c851482353b, R23, R24, R25, R26, R27, R28, R29, R22)
+	SHA512ROUND1(40, 0xa2bfe8a14cf10364, R22, R23, R24, R25, R26, R27, R28, R29)
+	SHA512ROUND1(41, 0xa81a664bbc423001, R29, R22, R23, R24, R25, R26, R27, R28)
+	SHA512ROUND1(42, 0xc24b8b70d0f89791, R28, R29, R22, R23, R24, R25, R26, R27)
+	SHA512ROUND1(43, 0xc76c51a30654be30, R27, R28, R29, R22, R23, R24, R25, R26)
+	SHA512ROUND1(44, 0xd192e819d6ef5218, R26, R27, R28, R29, R22, R23, R24, R25)
+	SHA512ROUND1(45, 0xd69906245565a910, R25, R26, R27, R28, R29, R22, R23, R24)
+	SHA512ROUND1(46, 0xf40e35855771202a, R24, R25, R26, R27, R28, R29, R22, R23)
+	SHA512ROUND1(47, 0x106aa07032bbd1b8, R23, R24, R25, R26, R27, R28, R29, R22)
+	SHA512ROUND1(48, 0x19a4c116b8d2d0c8, R22, R23, R24, R25, R26, R27, R28, R29)
+	SHA512ROUND1(49, 0x1e376c085141ab53, R29, R22, R23, R24, R25, R26, R27, R28)
+	SHA512ROUND1(50, 0x2748774cdf8eeb99, R28, R29, R22, R23, R24, R25, R26, R27)
+	SHA512ROUND1(51, 0x34b0bcb5e19b48a8, R27, R28, R29, R22, R23, R24, R25, R26)
+	SHA512ROUND1(52, 0x391c0cb3c5c95a63, R26, R27, R28, R29, R22, R23, R24, R25)
+	SHA512ROUND1(53, 0x4ed8aa4ae3418acb, R25, R26, R27, R28, R29, R22, R23, R24)
+	SHA512ROUND1(54, 0x5b9cca4f7763e373, R24, R25, R26, R27, R28, R29, R22, R23)
+	SHA512ROUND1(55, 0x682e6ff3d6b2b8a3, R23, R24, R25, R26, R27, R28, R29, R22)
+	SHA512ROUND1(56, 0x748f82ee5defb2fc, R22, R23, R24, R25, R26, R27, R28, R29)
+	SHA512ROUND1(57, 0x78a5636f43172f60, R29, R22, R23, R24, R25, R26, R27, R28)
+	SHA512ROUND1(58, 0x84c87814a1f0ab72, R28, R29, R22, R23, R24, R25, R26, R27)
+	SHA512ROUND1(59, 0x8cc702081a6439ec, R27, R28, R29, R22, R23, R24, R25, R26)
+	SHA512ROUND1(60, 0x90befffa23631e28, R26, R27, R28, R29, R22, R23, R24, R25)
+	SHA512ROUND1(61, 0xa4506cebde82bde9, R25, R26, R27, R28, R29, R22, R23, R24)
+	SHA512ROUND1(62, 0xbef9a3f7b2c67915, R24, R25, R26, R27, R28, R29, R22, R23)
+	SHA512ROUND1(63, 0xc67178f2e372532b, R23, R24, R25, R26, R27, R28, R29, R22)
+	SHA512ROUND1(64, 0xca273eceea26619c, R22, R23, R24, R25, R26, R27, R28, R29)
+	SHA512ROUND1(65, 0xd186b8c721c0c207, R29, R22, R23, R24, R25, R26, R27, R28)
+	SHA512ROUND1(66, 0xeada7dd6cde0eb1e, R28, R29, R22, R23, R24, R25, R26, R27)
+	SHA512ROUND1(67, 0xf57d4f7fee6ed178, R27, R28, R29, R22, R23, R24, R25, R26)
+	SHA512ROUND1(68, 0x06f067aa72176fba, R26, R27, R28, R29, R22, R23, R24, R25)
+	SHA512ROUND1(69, 0x0a637dc5a2c898a6, R25, R26, R27, R28, R29, R22, R23, R24)
+	SHA512ROUND1(70, 0x113f9804bef90dae, R24, R25, R26, R27, R28, R29, R22, R23)
+	SHA512ROUND1(71, 0x1b710b35131c471b, R23, R24, R25, R26, R27, R28, R29, R22)
+	SHA512ROUND1(72, 0x28db77f523047d84, R22, R23, R24, R25, R26, R27, R28, R29)
+	SHA512ROUND1(73, 0x32caab7b40c72493, R29, R22, R23, R24, R25, R26, R27, R28)
+	SHA512ROUND1(74, 0x3c9ebe0a15c9bebc, R28, R29, R22, R23, R24, R25, R26, R27)
+	SHA512ROUND1(75, 0x431d67c49c100d4c, R27, R28, R29, R22, R23, R24, R25, R26)
+	SHA512ROUND1(76, 0x4cc5d4becb3e42b6, R26, R27, R28, R29, R22, R23, R24, R25)
+	SHA512ROUND1(77, 0x597f299cfc657e2a, R25, R26, R27, R28, R29, R22, R23, R24)
+	SHA512ROUND1(78, 0x5fcb6fab3ad6faec, R24, R25, R26, R27, R28, R29, R22, R23)
+	SHA512ROUND1(79, 0x6c44198c4a475817, R23, R24, R25, R26, R27, R28, R29, R22)
+
+	MOVD	dig+0(FP), R9
+	MOVD	(0*8)(R9), R21
+	ADD	R21, R22	// H0 = a + H0
+	MOVD	R22, (0*8)(R9)
+	MOVD	(1*8)(R9), R21
+	ADD	R21, R23	// H1 = b + H1
+	MOVD	R23, (1*8)(R9)
+	MOVD	(2*8)(R9), R21
+	ADD	R21, R24	// H2 = c + H2
+	MOVD	R24, (2*8)(R9)
+	MOVD	(3*8)(R9), R21
+	ADD	R21, R25	// H3 = d + H3
+	MOVD	R25, (3*8)(R9)
+	MOVD	(4*8)(R9), R21
+	ADD	R21, R26	// H4 = e + H4
+	MOVD	R26, (4*8)(R9)
+	MOVD	(5*8)(R9), R21
+	ADD	R21, R27	// H5 = f + H5
+	MOVD	R27, (5*8)(R9)
+	MOVD	(6*8)(R9), R21
+	ADD	R21, R28	// H6 = g + H6
+	MOVD	R28, (6*8)(R9)
+	MOVD	(7*8)(R9), R21
+	ADD	R21, R29	// H7 = h + H7
+	MOVD	R29, (7*8)(R9)
+
+	ADD	$128, R6
+	MOVD	640(R1), R21
+	CMPU	R6, R21
+	BLT	loop
+
+end:
+	RET
diff --git a/src/crypto/tls/alert.go b/src/crypto/tls/alert.go
index 9cf9922..4929868 100644
--- a/src/crypto/tls/alert.go
+++ b/src/crypto/tls/alert.go
@@ -38,6 +38,7 @@ const (
 	alertInappropriateFallback  alert = 86
 	alertUserCanceled           alert = 90
 	alertNoRenegotiation        alert = 100
+	alertNoApplicationProtocol  alert = 120
 )
 
 var alertText = map[alert]string{
@@ -64,6 +65,7 @@ var alertText = map[alert]string{
 	alertInappropriateFallback:  "inappropriate fallback",
 	alertUserCanceled:           "user canceled",
 	alertNoRenegotiation:        "no renegotiation",
+	alertNoApplicationProtocol:  "no application protocol",
 }
 
 func (e alert) String() string {
diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go
index e69f5f9..0514674 100644
--- a/src/crypto/tls/cipher_suites.go
+++ b/src/crypto/tls/cipher_suites.go
@@ -11,8 +11,11 @@ import (
 	"crypto/hmac"
 	"crypto/rc4"
 	"crypto/sha1"
+	"crypto/sha256"
 	"crypto/x509"
 	"hash"
+
+	"golang_org/x/crypto/chacha20poly1305"
 )
 
 // a keyAgreement implements the client and server side of a TLS key agreement
@@ -73,25 +76,32 @@ type cipherSuite struct {
 }
 
 var cipherSuites = []*cipherSuite{
-	// Ciphersuite order is chosen so that ECDHE comes before plain RSA
-	// and RC4 comes before AES-CBC (because of the Lucky13 attack).
+	// Ciphersuite order is chosen so that ECDHE comes before plain RSA and
+	// AEADs are the top preference.
+	{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
+	{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
 	{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
-	{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE | suiteDefaultOff, cipherRC4, macSHA1, nil},
-	{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteDefaultOff, cipherRC4, macSHA1, nil},
+	{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
 	{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
+	{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, cipherAES, macSHA256, nil},
 	{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
 	{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
 	{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
 	{TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
-	{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, suiteDefaultOff, cipherRC4, macSHA1, nil},
+	{TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
 	{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
 	{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
 	{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
 	{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
+
+	// RC4-based cipher suites are disabled by default.
+	{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, suiteDefaultOff, cipherRC4, macSHA1, nil},
+	{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE | suiteDefaultOff, cipherRC4, macSHA1, nil},
+	{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteDefaultOff, cipherRC4, macSHA1, nil},
 }
 
 func cipherRC4(key, iv []byte, isRead bool) interface{} {
@@ -125,35 +135,84 @@ func macSHA1(version uint16, key []byte) macFunction {
 		copy(mac.key, key)
 		return mac
 	}
-	return tls10MAC{hmac.New(sha1.New, key)}
+	return tls10MAC{hmac.New(newConstantTimeHash(sha1.New), key)}
+}
+
+// macSHA256 returns a SHA-256 based MAC. These are only supported in TLS 1.2
+// so the given version is ignored.
+func macSHA256(version uint16, key []byte) macFunction {
+	return tls10MAC{hmac.New(sha256.New, key)}
 }
 
 type macFunction interface {
 	Size() int
-	MAC(digestBuf, seq, header, data []byte) []byte
+	MAC(digestBuf, seq, header, data, extra []byte) []byte
+}
+
+type aead interface {
+	cipher.AEAD
+
+	// explicitIVLen returns the number of bytes used by the explicit nonce
+	// that is included in the record. This is eight for older AEADs and
+	// zero for modern ones.
+	explicitNonceLen() int
 }
 
 // fixedNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
 // each call.
 type fixedNonceAEAD struct {
-	// sealNonce and openNonce are buffers where the larger nonce will be
-	// constructed. Since a seal and open operation may be running
-	// concurrently, there is a separate buffer for each.
-	sealNonce, openNonce []byte
-	aead                 cipher.AEAD
+	// nonce contains the fixed part of the nonce in the first four bytes.
+	nonce [12]byte
+	aead  cipher.AEAD
 }
 
-func (f *fixedNonceAEAD) NonceSize() int { return 8 }
-func (f *fixedNonceAEAD) Overhead() int  { return f.aead.Overhead() }
+func (f *fixedNonceAEAD) NonceSize() int        { return 8 }
+func (f *fixedNonceAEAD) Overhead() int         { return f.aead.Overhead() }
+func (f *fixedNonceAEAD) explicitNonceLen() int { return 8 }
 
 func (f *fixedNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
-	copy(f.sealNonce[len(f.sealNonce)-8:], nonce)
-	return f.aead.Seal(out, f.sealNonce, plaintext, additionalData)
+	copy(f.nonce[4:], nonce)
+	return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
 }
 
 func (f *fixedNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) {
-	copy(f.openNonce[len(f.openNonce)-8:], nonce)
-	return f.aead.Open(out, f.openNonce, plaintext, additionalData)
+	copy(f.nonce[4:], nonce)
+	return f.aead.Open(out, f.nonce[:], plaintext, additionalData)
+}
+
+// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
+// before each call.
+type xorNonceAEAD struct {
+	nonceMask [12]byte
+	aead      cipher.AEAD
+}
+
+func (f *xorNonceAEAD) NonceSize() int        { return 8 }
+func (f *xorNonceAEAD) Overhead() int         { return f.aead.Overhead() }
+func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
+
+func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+	for i, b := range nonce {
+		f.nonceMask[4+i] ^= b
+	}
+	result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
+	for i, b := range nonce {
+		f.nonceMask[4+i] ^= b
+	}
+
+	return result
+}
+
+func (f *xorNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) {
+	for i, b := range nonce {
+		f.nonceMask[4+i] ^= b
+	}
+	result, err := f.aead.Open(out, f.nonceMask[:], plaintext, additionalData)
+	for i, b := range nonce {
+		f.nonceMask[4+i] ^= b
+	}
+
+	return result, err
 }
 
 func aeadAESGCM(key, fixedNonce []byte) cipher.AEAD {
@@ -166,11 +225,20 @@ func aeadAESGCM(key, fixedNonce []byte) cipher.AEAD {
 		panic(err)
 	}
 
-	nonce1, nonce2 := make([]byte, 12), make([]byte, 12)
-	copy(nonce1, fixedNonce)
-	copy(nonce2, fixedNonce)
+	ret := &fixedNonceAEAD{aead: aead}
+	copy(ret.nonce[:], fixedNonce)
+	return ret
+}
+
+func aeadChaCha20Poly1305(key, fixedNonce []byte) cipher.AEAD {
+	aead, err := chacha20poly1305.New(key)
+	if err != nil {
+		panic(err)
+	}
 
-	return &fixedNonceAEAD{nonce1, nonce2, aead}
+	ret := &xorNonceAEAD{aead: aead}
+	copy(ret.nonceMask[:], fixedNonce)
+	return ret
 }
 
 // ssl30MAC implements the SSLv3 MAC function, as defined in
@@ -188,7 +256,9 @@ var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0
 
 var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c}
 
-func (s ssl30MAC) MAC(digestBuf, seq, header, data []byte) []byte {
+// MAC does not offer constant timing guarantees for SSL v3.0, since it's deemed
+// useless considering the similar, protocol-level POODLE vulnerability.
+func (s ssl30MAC) MAC(digestBuf, seq, header, data, extra []byte) []byte {
 	padLength := 48
 	if s.h.Size() == 20 {
 		padLength = 40
@@ -210,6 +280,29 @@ func (s ssl30MAC) MAC(digestBuf, seq, header, data []byte) []byte {
 	return s.h.Sum(digestBuf[:0])
 }
 
+type constantTimeHash interface {
+	hash.Hash
+	ConstantTimeSum(b []byte) []byte
+}
+
+// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces
+// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC.
+type cthWrapper struct {
+	h constantTimeHash
+}
+
+func (c *cthWrapper) Size() int                   { return c.h.Size() }
+func (c *cthWrapper) BlockSize() int              { return c.h.BlockSize() }
+func (c *cthWrapper) Reset()                      { c.h.Reset() }
+func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) }
+func (c *cthWrapper) Sum(b []byte) []byte         { return c.h.ConstantTimeSum(b) }
+
+func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
+	return func() hash.Hash {
+		return &cthWrapper{h().(constantTimeHash)}
+	}
+}
+
 // tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3.
 type tls10MAC struct {
 	h hash.Hash
@@ -219,12 +312,19 @@ func (s tls10MAC) Size() int {
 	return s.h.Size()
 }
 
-func (s tls10MAC) MAC(digestBuf, seq, header, data []byte) []byte {
+// MAC is guaranteed to take constant time, as long as
+// len(seq)+len(header)+len(data)+len(extra) is constant. extra is not fed into
+// the MAC, but is only provided to make the timing profile constant.
+func (s tls10MAC) MAC(digestBuf, seq, header, data, extra []byte) []byte {
 	s.h.Reset()
 	s.h.Write(seq)
 	s.h.Write(header)
 	s.h.Write(data)
-	return s.h.Sum(digestBuf[:0])
+	res := s.h.Sum(digestBuf[:0])
+	if extra != nil {
+		s.h.Write(extra)
+	}
+	return res
 }
 
 func rsaKA(version uint16) keyAgreement {
@@ -270,6 +370,7 @@ const (
 	TLS_RSA_WITH_3DES_EDE_CBC_SHA           uint16 = 0x000a
 	TLS_RSA_WITH_AES_128_CBC_SHA            uint16 = 0x002f
 	TLS_RSA_WITH_AES_256_CBC_SHA            uint16 = 0x0035
+	TLS_RSA_WITH_AES_128_CBC_SHA256         uint16 = 0x003c
 	TLS_RSA_WITH_AES_128_GCM_SHA256         uint16 = 0x009c
 	TLS_RSA_WITH_AES_256_GCM_SHA384         uint16 = 0x009d
 	TLS_ECDHE_ECDSA_WITH_RC4_128_SHA        uint16 = 0xc007
@@ -279,10 +380,14 @@ const (
 	TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA     uint16 = 0xc012
 	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA      uint16 = 0xc013
 	TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA      uint16 = 0xc014
+	TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
+	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256   uint16 = 0xc027
 	TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256   uint16 = 0xc02f
 	TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
 	TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384   uint16 = 0xc030
 	TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
+	TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305    uint16 = 0xcca8
+	TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305  uint16 = 0xcca9
 
 	// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
 	// that the client is doing version fallback. See
diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go
index 9fc7420..276d176 100644
--- a/src/crypto/tls/common.go
+++ b/src/crypto/tls/common.go
@@ -7,6 +7,7 @@ package tls
 import (
 	"container/list"
 	"crypto"
+	"crypto/internal/cipherhw"
 	"crypto/rand"
 	"crypto/sha512"
 	"crypto/x509"
@@ -14,6 +15,7 @@ import (
 	"fmt"
 	"io"
 	"math/big"
+	"net"
 	"strings"
 	"sync"
 	"time"
@@ -95,6 +97,7 @@ const (
 	CurveP256 CurveID = 23
 	CurveP384 CurveID = 24
 	CurveP521 CurveID = 25
+	X25519    CurveID = 29
 )
 
 // TLS Elliptic Curve Point Formats
@@ -213,6 +216,25 @@ type ClientSessionCache interface {
 	Put(sessionKey string, cs *ClientSessionState)
 }
 
+// SignatureScheme identifies a signature algorithm supported by TLS. See
+// https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.3.
+type SignatureScheme uint16
+
+const (
+	PKCS1WithSHA1   SignatureScheme = 0x0201
+	PKCS1WithSHA256 SignatureScheme = 0x0401
+	PKCS1WithSHA384 SignatureScheme = 0x0501
+	PKCS1WithSHA512 SignatureScheme = 0x0601
+
+	PSSWithSHA256 SignatureScheme = 0x0804
+	PSSWithSHA384 SignatureScheme = 0x0805
+	PSSWithSHA512 SignatureScheme = 0x0806
+
+	ECDSAWithP256AndSHA256 SignatureScheme = 0x0403
+	ECDSAWithP384AndSHA384 SignatureScheme = 0x0503
+	ECDSAWithP521AndSHA512 SignatureScheme = 0x0603
+)
+
 // ClientHelloInfo contains information from a ClientHello message in order to
 // guide certificate selection in the GetCertificate callback.
 type ClientHelloInfo struct {
@@ -237,6 +259,47 @@ type ClientHelloInfo struct {
 	// is being used (see
 	// http://tools.ietf.org/html/rfc4492#section-5.1.2).
 	SupportedPoints []uint8
+
+	// SignatureSchemes lists the signature and hash schemes that the client
+	// is willing to verify. SignatureSchemes is set only if the Signature
+	// Algorithms Extension is being used (see
+	// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1).
+	SignatureSchemes []SignatureScheme
+
+	// SupportedProtos lists the application protocols supported by the client.
+	// SupportedProtos is set only if the Application-Layer Protocol
+	// Negotiation Extension is being used (see
+	// https://tools.ietf.org/html/rfc7301#section-3.1).
+	//
+	// Servers can select a protocol by setting Config.NextProtos in a
+	// GetConfigForClient return value.
+	SupportedProtos []string
+
+	// SupportedVersions lists the TLS versions supported by the client.
+	// For TLS versions less than 1.3, this is extrapolated from the max
+	// version advertised by the client, so values other than the greatest
+	// might be rejected if used.
+	SupportedVersions []uint16
+
+	// Conn is the underlying net.Conn for the connection. Do not read
+	// from, or write to, this connection; that will cause the TLS
+	// connection to fail.
+	Conn net.Conn
+}
+
+// CertificateRequestInfo contains information from a server's
+// CertificateRequest message, which is used to demand a certificate and proof
+// of control from a client.
+type CertificateRequestInfo struct {
+	// AcceptableCAs contains zero or more, DER-encoded, X.501
+	// Distinguished Names. These are the names of root or intermediate CAs
+	// that the server wishes the returned certificate to be signed by. An
+	// empty slice indicates that the server has no preference.
+	AcceptableCAs [][]byte
+
+	// SignatureSchemes lists the signature schemes that the server is
+	// willing to verify.
+	SignatureSchemes []SignatureScheme
 }
 
 // RenegotiationSupport enumerates the different levels of support for TLS
@@ -281,10 +344,11 @@ type Config struct {
 	// If Time is nil, TLS uses time.Now.
 	Time func() time.Time
 
-	// Certificates contains one or more certificate chains
-	// to present to the other side of the connection.
-	// Server configurations must include at least one certificate
-	// or else set GetCertificate.
+	// Certificates contains one or more certificate chains to present to
+	// the other side of the connection. Server configurations must include
+	// at least one certificate or else set GetCertificate. Clients doing
+	// client-authentication may set either Certificates or
+	// GetClientCertificate.
 	Certificates []Certificate
 
 	// NameToCertificate maps from a certificate name to an element of
@@ -302,7 +366,54 @@ type Config struct {
 	// If GetCertificate is nil or returns nil, then the certificate is
 	// retrieved from NameToCertificate. If NameToCertificate is nil, the
 	// first element of Certificates will be used.
-	GetCertificate func(clientHello *ClientHelloInfo) (*Certificate, error)
+	GetCertificate func(*ClientHelloInfo) (*Certificate, error)
+
+	// GetClientCertificate, if not nil, is called when a server requests a
+	// certificate from a client. If set, the contents of Certificates will
+	// be ignored.
+	//
+	// If GetClientCertificate returns an error, the handshake will be
+	// aborted and that error will be returned. Otherwise
+	// GetClientCertificate must return a non-nil Certificate. If
+	// Certificate.Certificate is empty then no certificate will be sent to
+	// the server. If this is unacceptable to the server then it may abort
+	// the handshake.
+	//
+	// GetClientCertificate may be called multiple times for the same
+	// connection if renegotiation occurs or if TLS 1.3 is in use.
+	GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error)
+
+	// GetConfigForClient, if not nil, is called after a ClientHello is
+	// received from a client. It may return a non-nil Config in order to
+	// change the Config that will be used to handle this connection. If
+	// the returned Config is nil, the original Config will be used. The
+	// Config returned by this callback may not be subsequently modified.
+	//
+	// If GetConfigForClient is nil, the Config passed to Server() will be
+	// used for all connections.
+	//
+	// Uniquely for the fields in the returned Config, session ticket keys
+	// will be duplicated from the original Config if not set.
+	// Specifically, if SetSessionTicketKeys was called on the original
+	// config but not on the returned config then the ticket keys from the
+	// original config will be copied into the new config before use.
+	// Otherwise, if SessionTicketKey was set in the original config but
+	// not in the returned config then it will be copied into the returned
+	// config before use. If neither of those cases applies then the key
+	// material from the returned config will be used for session tickets.
+	GetConfigForClient func(*ClientHelloInfo) (*Config, error)
+
+	// VerifyPeerCertificate, if not nil, is called after normal
+	// certificate verification by either a TLS client or server. It
+	// receives the raw ASN.1 certificates provided by the peer and also
+	// any verified chains that normal processing found. If it returns a
+	// non-nil error, the handshake is aborted and that error results.
+	//
+	// If normal verification fails then the handshake will abort before
+	// considering this callback. If normal verification is disabled by
+	// setting InsecureSkipVerify then this callback will be considered but
+	// the verifiedChains argument will always be nil.
+	VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
 
 	// RootCAs defines the set of root certificate authorities
 	// that clients use when verifying server certificates.
@@ -387,15 +498,27 @@ type Config struct {
 	// The default, none, is correct for the vast majority of applications.
 	Renegotiation RenegotiationSupport
 
+	// KeyLogWriter optionally specifies a destination for TLS master secrets
+	// in NSS key log format that can be used to allow external programs
+	// such as Wireshark to decrypt TLS connections.
+	// See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format.
+	// Use of KeyLogWriter compromises security and should only be
+	// used for debugging.
+	KeyLogWriter io.Writer
+
 	serverInitOnce sync.Once // guards calling (*Config).serverInit
 
-	// mutex protects sessionTicketKeys
+	// mutex protects sessionTicketKeys and originalConfig.
 	mutex sync.RWMutex
 	// sessionTicketKeys contains zero or more ticket keys. If the length
 	// is zero, SessionTicketsDisabled must be true. The first key is used
 	// for new tickets and any subsequent keys can be used to decrypt old
 	// tickets.
 	sessionTicketKeys []ticketKey
+	// originalConfig is set to the Config that was passed to Server if
+	// this Config is returned by a GetConfigForClient callback. It's used
+	// by serverInit in order to copy session ticket keys if needed.
+	originalConfig *Config
 }
 
 // ticketKeyNameLen is the number of bytes of identifier that is prepended to
@@ -422,14 +545,26 @@ func ticketKeyFromBytes(b [32]byte) (key ticketKey) {
 	return key
 }
 
-// clone returns a copy of c. Only the exported fields are copied.
-func (c *Config) clone() *Config {
+// Clone returns a shallow clone of c. It is safe to clone a Config that is
+// being used concurrently by a TLS client or server.
+func (c *Config) Clone() *Config {
+	// Running serverInit ensures that it's safe to read
+	// SessionTicketsDisabled.
+	c.serverInitOnce.Do(c.serverInit)
+
+	var sessionTicketKeys []ticketKey
+	c.mutex.RLock()
+	sessionTicketKeys = c.sessionTicketKeys
+	c.mutex.RUnlock()
+
 	return &Config{
 		Rand:                        c.Rand,
 		Time:                        c.Time,
 		Certificates:                c.Certificates,
 		NameToCertificate:           c.NameToCertificate,
 		GetCertificate:              c.GetCertificate,
+		GetConfigForClient:          c.GetConfigForClient,
+		VerifyPeerCertificate:       c.VerifyPeerCertificate,
 		RootCAs:                     c.RootCAs,
 		NextProtos:                  c.NextProtos,
 		ServerName:                  c.ServerName,
@@ -446,14 +581,22 @@ func (c *Config) clone() *Config {
 		CurvePreferences:            c.CurvePreferences,
 		DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
 		Renegotiation:               c.Renegotiation,
+		KeyLogWriter:                c.KeyLogWriter,
+		sessionTicketKeys:           sessionTicketKeys,
+		// originalConfig is deliberately not duplicated.
 	}
 }
 
 func (c *Config) serverInit() {
-	if c.SessionTicketsDisabled {
+	if c.SessionTicketsDisabled || len(c.ticketKeys()) != 0 {
 		return
 	}
 
+	var originalConfig *Config
+	c.mutex.Lock()
+	originalConfig, c.originalConfig = c.originalConfig, nil
+	c.mutex.Unlock()
+
 	alreadySet := false
 	for _, b := range c.SessionTicketKey {
 		if b != 0 {
@@ -463,13 +606,21 @@ func (c *Config) serverInit() {
 	}
 
 	if !alreadySet {
-		if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil {
+		if originalConfig != nil {
+			copy(c.SessionTicketKey[:], originalConfig.SessionTicketKey[:])
+		} else if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil {
 			c.SessionTicketsDisabled = true
 			return
 		}
 	}
 
-	c.sessionTicketKeys = []ticketKey{ticketKeyFromBytes(c.SessionTicketKey)}
+	if originalConfig != nil {
+		originalConfig.mutex.RLock()
+		c.sessionTicketKeys = originalConfig.sessionTicketKeys
+		originalConfig.mutex.RUnlock()
+	} else {
+		c.sessionTicketKeys = []ticketKey{ticketKeyFromBytes(c.SessionTicketKey)}
+	}
 }
 
 func (c *Config) ticketKeys() []ticketKey {
@@ -539,7 +690,7 @@ func (c *Config) maxVersion() uint16 {
 	return c.MaxVersion
 }
 
-var defaultCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521}
+var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521}
 
 func (c *Config) curvePreferences() []CurveID {
 	if c == nil || len(c.CurvePreferences) == 0 {
@@ -627,6 +778,26 @@ func (c *Config) BuildNameToCertificate() {
 	}
 }
 
+// writeKeyLog logs client random and master secret if logging was enabled by
+// setting c.KeyLogWriter.
+func (c *Config) writeKeyLog(clientRandom, masterSecret []byte) error {
+	if c.KeyLogWriter == nil {
+		return nil
+	}
+
+	logLine := []byte(fmt.Sprintf("CLIENT_RANDOM %x %x\n", clientRandom, masterSecret))
+
+	writerMutex.Lock()
+	_, err := c.KeyLogWriter.Write(logLine)
+	writerMutex.Unlock()
+
+	return err
+}
+
+// writerMutex protects all KeyLogWriters globally. It is rarely enabled,
+// and is only for debugging, so a global mutex saves space.
+var writerMutex sync.Mutex
+
 // A Certificate is a chain of one or more certificates, leaf first.
 type Certificate struct {
 	Certificate [][]byte
@@ -749,11 +920,46 @@ func defaultCipherSuites() []uint16 {
 }
 
 func initDefaultCipherSuites() {
+	var topCipherSuites []uint16
+	if cipherhw.AESGCMSupport() {
+		// If AES-GCM hardware is provided then prioritise AES-GCM
+		// cipher suites.
+		topCipherSuites = []uint16{
+			TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+			TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+			TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+			TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+			TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+			TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+		}
+	} else {
+		// Without AES-GCM hardware, we put the ChaCha20-Poly1305
+		// cipher suites first.
+		topCipherSuites = []uint16{
+			TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+			TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+			TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+			TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+			TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+			TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+		}
+	}
+
 	varDefaultCipherSuites = make([]uint16, 0, len(cipherSuites))
+	for _, topCipher := range topCipherSuites {
+		varDefaultCipherSuites = append(varDefaultCipherSuites, topCipher)
+	}
+
+NextCipherSuite:
 	for _, suite := range cipherSuites {
 		if suite.flags&suiteDefaultOff != 0 {
 			continue
 		}
+		for _, existing := range varDefaultCipherSuites {
+			if existing == suite.id {
+				continue NextCipherSuite
+			}
+		}
 		varDefaultCipherSuites = append(varDefaultCipherSuites, suite.id)
 	}
 }
diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go
index 77fd6d3..4b2702a 100644
--- a/src/crypto/tls/conn.go
+++ b/src/crypto/tls/conn.go
@@ -64,6 +64,13 @@ type Conn struct {
 	// the first transmitted Finished message is the tls-unique
 	// channel-binding value.
 	clientFinishedIsFirst bool
+
+	// closeNotifyErr is any error from sending the alertCloseNotify record.
+	closeNotifyErr error
+	// closeNotifySent is true if the Conn attempted to send an
+	// alertCloseNotify record.
+	closeNotifySent bool
+
 	// clientFinished and serverFinished contain the Finished message sent
 	// by the client or server in the most recent handshake. This is
 	// retained to support the renegotiation extension and tls-unique
@@ -193,18 +200,18 @@ func (hc *halfConn) incSeq() {
 	panic("TLS: sequence number wraparound")
 }
 
-// removePadding returns an unpadded slice, in constant time, which is a prefix
-// of the input. It also returns a byte which is equal to 255 if the padding
-// was valid and 0 otherwise. See RFC 2246, section 6.2.3.2
-func removePadding(payload []byte) ([]byte, byte) {
+// extractPadding returns, in constant time, the length of the padding to remove
+// from the end of payload. It also returns a byte which is equal to 255 if the
+// padding was valid and 0 otherwise. See RFC 2246, section 6.2.3.2
+func extractPadding(payload []byte) (toRemove int, good byte) {
 	if len(payload) < 1 {
-		return payload, 0
+		return 0, 0
 	}
 
 	paddingLen := payload[len(payload)-1]
 	t := uint(len(payload)-1) - uint(paddingLen)
 	// if len(payload) >= (paddingLen - 1) then the MSB of t is zero
-	good := byte(int32(^t) >> 31)
+	good = byte(int32(^t) >> 31)
 
 	toCheck := 255 // the maximum possible padding length
 	// The length of the padded data is public, so we can use an if here
@@ -227,24 +234,24 @@ func removePadding(payload []byte) ([]byte, byte) {
 	good &= good << 1
 	good = uint8(int8(good) >> 7)
 
-	toRemove := good&paddingLen + 1
-	return payload[:len(payload)-int(toRemove)], good
+	toRemove = int(paddingLen) + 1
+	return
 }
 
-// removePaddingSSL30 is a replacement for removePadding in the case that the
+// extractPaddingSSL30 is a replacement for extractPadding in the case that the
 // protocol version is SSLv3. In this version, the contents of the padding
 // are random and cannot be checked.
-func removePaddingSSL30(payload []byte) ([]byte, byte) {
+func extractPaddingSSL30(payload []byte) (toRemove int, good byte) {
 	if len(payload) < 1 {
-		return payload, 0
+		return 0, 0
 	}
 
 	paddingLen := int(payload[len(payload)-1]) + 1
 	if paddingLen > len(payload) {
-		return payload, 0
+		return 0, 0
 	}
 
-	return payload[:len(payload)-paddingLen], 255
+	return paddingLen, 255
 }
 
 func roundUp(a, b int) int {
@@ -270,6 +277,7 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
 	}
 
 	paddingGood := byte(255)
+	paddingLen := 0
 	explicitIVLen := 0
 
 	// decrypt
@@ -277,13 +285,17 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
 		switch c := hc.cipher.(type) {
 		case cipher.Stream:
 			c.XORKeyStream(payload, payload)
-		case cipher.AEAD:
-			explicitIVLen = 8
+		case aead:
+			explicitIVLen = c.explicitNonceLen()
 			if len(payload) < explicitIVLen {
 				return false, 0, alertBadRecordMAC
 			}
-			nonce := payload[:8]
-			payload = payload[8:]
+			nonce := payload[:explicitIVLen]
+			payload = payload[explicitIVLen:]
+
+			if len(nonce) == 0 {
+				nonce = hc.seq[:]
+			}
 
 			copy(hc.additionalData[:], hc.seq[:])
 			copy(hc.additionalData[8:], b.data[:3])
@@ -312,22 +324,17 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
 			}
 			c.CryptBlocks(payload, payload)
 			if hc.version == VersionSSL30 {
-				payload, paddingGood = removePaddingSSL30(payload)
+				paddingLen, paddingGood = extractPaddingSSL30(payload)
 			} else {
-				payload, paddingGood = removePadding(payload)
+				paddingLen, paddingGood = extractPadding(payload)
+
+				// To protect against CBC padding oracles like Lucky13, the data
+				// past paddingLen (which is secret) is passed to the MAC
+				// function as extra data, to be fed into the HMAC after
+				// computing the digest. This makes the MAC constant time as
+				// long as the digest computation is constant time and does not
+				// affect the subsequent write.
 			}
-			b.resize(recordHeaderLen + explicitIVLen + len(payload))
-
-			// note that we still have a timing side-channel in the
-			// MAC check, below. An attacker can align the record
-			// so that a correct padding will cause one less hash
-			// block to be calculated. Then they can iteratively
-			// decrypt a record by breaking each byte. See
-			// "Password Interception in a SSL/TLS Channel", Brice
-			// Canvel et al.
-			//
-			// However, our behavior matches OpenSSL, so we leak
-			// only as much as they do.
 		default:
 			panic("unknown cipher type")
 		}
@@ -340,17 +347,19 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
 		}
 
 		// strip mac off payload, b.data
-		n := len(payload) - macSize
+		n := len(payload) - macSize - paddingLen
+		n = subtle.ConstantTimeSelect(int(uint32(n)>>31), 0, n) // if n < 0 { n = 0 }
 		b.data[3] = byte(n >> 8)
 		b.data[4] = byte(n)
-		b.resize(recordHeaderLen + explicitIVLen + n)
-		remoteMAC := payload[n:]
-		localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], payload[:n])
+		remoteMAC := payload[n : n+macSize]
+		localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], payload[:n], payload[n+macSize:])
 
 		if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 {
 			return false, 0, alertBadRecordMAC
 		}
 		hc.inDigestBuf = localMAC
+
+		b.resize(recordHeaderLen + explicitIVLen + n)
 	}
 	hc.incSeq()
 
@@ -378,7 +387,7 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) {
 func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
 	// mac
 	if hc.mac != nil {
-		mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:])
+		mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:], nil)
 
 		n := len(b.data)
 		b.resize(n + len(mac))
@@ -393,10 +402,13 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
 		switch c := hc.cipher.(type) {
 		case cipher.Stream:
 			c.XORKeyStream(payload, payload)
-		case cipher.AEAD:
+		case aead:
 			payloadLen := len(b.data) - recordHeaderLen - explicitIVLen
 			b.resize(len(b.data) + c.Overhead())
 			nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
+			if len(nonce) == 0 {
+				nonce = hc.seq[:]
+			}
 			payload := b.data[recordHeaderLen+explicitIVLen:]
 			payload = payload[:payloadLen]
 
@@ -632,9 +644,10 @@ Again:
 
 	// Process message.
 	b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n)
-	ok, off, err := c.in.decrypt(b)
+	ok, off, alertValue := c.in.decrypt(b)
 	if !ok {
-		c.in.setErrorLocked(c.sendAlert(err))
+		c.in.freeBlock(b)
+		return c.in.setErrorLocked(c.sendAlert(alertValue))
 	}
 	b.off = off
 	data := b.data[b.off:]
@@ -853,15 +866,16 @@ func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) {
 			}
 		}
 		if explicitIVLen == 0 {
-			if _, ok := c.out.cipher.(cipher.AEAD); ok {
-				explicitIVLen = 8
+			if c, ok := c.out.cipher.(aead); ok {
+				explicitIVLen = c.explicitNonceLen()
+
 				// The AES-GCM construction in TLS has an
 				// explicit nonce so that the nonce can be
 				// random. However, the nonce is only 8 bytes
 				// which is too small for a secure, random
 				// nonce. Therefore we use the sequence number
 				// as the nonce.
-				explicitIVIsSeq = true
+				explicitIVIsSeq = explicitIVLen > 0
 			}
 		}
 		m := len(data)
@@ -982,7 +996,7 @@ func (c *Conn) readHandshake() (interface{}, error) {
 		return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 	}
 
-	// The handshake message unmarshallers
+	// The handshake message unmarshalers
 	// expect to be able to keep references to data,
 	// so pass in a fresh copy that won't be overwritten.
 	data = append([]byte(nil), data...)
@@ -993,7 +1007,10 @@ func (c *Conn) readHandshake() (interface{}, error) {
 	return m, nil
 }
 
-var errClosed = errors.New("tls: use of closed connection")
+var (
+	errClosed   = errors.New("tls: use of closed connection")
+	errShutdown = errors.New("tls: protocol is shutdown")
+)
 
 // Write writes data to the connection.
 func (c *Conn) Write(b []byte) (int, error) {
@@ -1024,6 +1041,10 @@ func (c *Conn) Write(b []byte) (int, error) {
 		return 0, alertInternalError
 	}
 
+	if c.closeNotifySent {
+		return 0, errShutdown
+	}
+
 	// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
 	// attack when using block mode ciphers due to predictable IVs.
 	// This can be prevented by splitting each Application Data
@@ -1187,7 +1208,7 @@ func (c *Conn) Close() error {
 	c.handshakeMutex.Lock()
 	defer c.handshakeMutex.Unlock()
 	if c.handshakeComplete {
-		alertErr = c.sendAlert(alertCloseNotify)
+		alertErr = c.closeNotify()
 	}
 
 	if err := c.conn.Close(); err != nil {
@@ -1196,6 +1217,32 @@ func (c *Conn) Close() error {
 	return alertErr
 }
 
+var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake complete")
+
+// CloseWrite shuts down the writing side of the connection. It should only be
+// called once the handshake has completed and does not call CloseWrite on the
+// underlying connection. Most callers should just use Close.
+func (c *Conn) CloseWrite() error {
+	c.handshakeMutex.Lock()
+	defer c.handshakeMutex.Unlock()
+	if !c.handshakeComplete {
+		return errEarlyCloseWrite
+	}
+
+	return c.closeNotify()
+}
+
+func (c *Conn) closeNotify() error {
+	c.out.Lock()
+	defer c.out.Unlock()
+
+	if !c.closeNotifySent {
+		c.closeNotifyErr = c.sendAlertLocked(alertCloseNotify)
+		c.closeNotifySent = true
+	}
+	return c.closeNotifyErr
+}
+
 // Handshake runs the client or server handshake
 // protocol if it has not yet been run.
 // Most uses of this package need not call Handshake
@@ -1263,6 +1310,10 @@ func (c *Conn) Handshake() error {
 	}
 	if c.handshakeErr == nil {
 		c.handshakes++
+	} else {
+		// If an error occurred during the hadshake try to flush the
+		// alert that might be left in the buffer.
+		c.flush()
 	}
 
 	if c.handshakeErr == nil && !c.handshakeComplete {
@@ -1284,6 +1335,8 @@ func (c *Conn) ConnectionState() ConnectionState {
 
 	var state ConnectionState
 	state.HandshakeComplete = c.handshakeComplete
+	state.ServerName = c.serverName
+
 	if c.handshakeComplete {
 		state.Version = c.vers
 		state.NegotiatedProtocol = c.clientProtocol
@@ -1292,7 +1345,6 @@ func (c *Conn) ConnectionState() ConnectionState {
 		state.CipherSuite = c.cipherSuite
 		state.PeerCertificates = c.peerCertificates
 		state.VerifiedChains = c.verifiedChains
-		state.ServerName = c.serverName
 		state.SignedCertificateTimestamps = c.scts
 		state.OCSPResponse = c.ocspResponse
 		if !c.didResume {
diff --git a/src/crypto/tls/conn_test.go b/src/crypto/tls/conn_test.go
index 5cff7e7..5e5c7a2 100644
--- a/src/crypto/tls/conn_test.go
+++ b/src/crypto/tls/conn_test.go
@@ -40,7 +40,7 @@ var paddingTests = []struct {
 
 func TestRemovePadding(t *testing.T) {
 	for i, test := range paddingTests {
-		payload, good := removePadding(test.in)
+		paddingLen, good := extractPadding(test.in)
 		expectedGood := byte(255)
 		if !test.good {
 			expectedGood = 0
@@ -48,19 +48,19 @@ func TestRemovePadding(t *testing.T) {
 		if good != expectedGood {
 			t.Errorf("#%d: wrong validity, want:%d got:%d", i, expectedGood, good)
 		}
-		if good == 255 && len(payload) != test.expectedLen {
-			t.Errorf("#%d: got %d, want %d", i, len(payload), test.expectedLen)
+		if good == 255 && len(test.in)-paddingLen != test.expectedLen {
+			t.Errorf("#%d: got %d, want %d", i, len(test.in)-paddingLen, test.expectedLen)
 		}
 	}
 }
 
-var certExampleCom = `308201403081eda003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313138353835325a170d3132303933303138353835325a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31a301830160603551d11040f300d820b6578 [...]
+var certExampleCom = `308201713082011ba003020102021005a75ddf21014d5f417083b7a010ba2e300d06092a864886f70d01010b050030123110300e060355040a130741636d6520436f301e170d3136303831373231343135335a170d3137303831373231343135335a30123110300e060355040a130741636d6520436f305c300d06092a864886f70d0101010500034b003048024100b37f0fdd67e715bf532046ac34acbd8fdc4dabe2b598588f3f58b1f12e6219a16cbfe54d2b4b665396013589262360b6721efa27d546854f17cc9aeec6751db10203010001a34d304b300e0603551d0f0101ff0404030205a0301306 [...]
 
-var certWildcardExampleCom = `308201423081efa003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303034365a170d3132303933303139303034365a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31c301a30180603551d110411300f [...]
+var certWildcardExampleCom = `308201743082011ea003020102021100a7aa6297c9416a4633af8bec2958c607300d06092a864886f70d01010b050030123110300e060355040a130741636d6520436f301e170d3136303831373231343231395a170d3137303831373231343231395a30123110300e060355040a130741636d6520436f305c300d06092a864886f70d0101010500034b003048024100b105afc859a711ee864114e7d2d46c2dcbe392d3506249f6c2285b0eb342cc4bf2d803677c61c0abde443f084745c1a6d62080e5664ef2cc8f50ad8a0ab8870b0203010001a34f304d300e0603551d0f0101ff04040302 [...]
 
-var certFooExampleCom = `308201443081f1a003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303131345a170d3132303933303139303131345a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31e301c301a0603551d1104133011820f6 [...]
+var certFooExampleCom = `308201753082011fa00302010202101bbdb6070b0aeffc49008cde74deef29300d06092a864886f70d01010b050030123110300e060355040a130741636d6520436f301e170d3136303831373231343234345a170d3137303831373231343234345a30123110300e060355040a130741636d6520436f305c300d06092a864886f70d0101010500034b003048024100f00ac69d8ca2829f26216c7b50f1d4bbabad58d447706476cd89a2f3e1859943748aa42c15eedc93ac7c49e40d3b05ed645cb6b81c4efba60d961f44211a54eb0203010001a351304f300e0603551d0f0101ff0404030205a0301 [...]
 
-var certDoubleWildcardExampleCom = `308201443081f1a003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303134315a170d3132303933303139303134315a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31e301c301a0603551d1104 [...]
+var certDoubleWildcardExampleCom = `308201753082011fa003020102021039d262d8538db8ffba30d204e02ddeb5300d06092a864886f70d01010b050030123110300e060355040a130741636d6520436f301e170d3136303831373231343331335a170d3137303831373231343331335a30123110300e060355040a130741636d6520436f305c300d06092a864886f70d0101010500034b003048024100abb6bd84b8b9be3fb9415d00f22b4ddcaec7c99855b9d818c09003e084578430e5cfd2e35faa3561f036d496aa43a9ca6e6cf23c72a763c04ae324004f6cbdbb0203010001a351304f300e0603551d0f0101ff0404 [...]
 
 func TestCertificateSelection(t *testing.T) {
 	config := Config{
@@ -124,7 +124,7 @@ func TestCertificateSelection(t *testing.T) {
 func runDynamicRecordSizingTest(t *testing.T, config *Config) {
 	clientConn, serverConn := net.Pipe()
 
-	serverConfig := config.clone()
+	serverConfig := config.Clone()
 	serverConfig.DynamicRecordSizingDisabled = false
 	tlsConn := Server(serverConn, serverConfig)
 
@@ -225,19 +225,19 @@ func runDynamicRecordSizingTest(t *testing.T, config *Config) {
 }
 
 func TestDynamicRecordSizingWithStreamCipher(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA}
 	runDynamicRecordSizingTest(t, config)
 }
 
 func TestDynamicRecordSizingWithCBC(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.CipherSuites = []uint16{TLS_RSA_WITH_AES_256_CBC_SHA}
 	runDynamicRecordSizingTest(t, config)
 }
 
 func TestDynamicRecordSizingWithAEAD(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
 	runDynamicRecordSizingTest(t, config)
 }
diff --git a/src/crypto/tls/example_test.go b/src/crypto/tls/example_test.go
index 7628e43..02d0f18 100644
--- a/src/crypto/tls/example_test.go
+++ b/src/crypto/tls/example_test.go
@@ -7,8 +7,23 @@ package tls_test
 import (
 	"crypto/tls"
 	"crypto/x509"
+	"log"
+	"net/http"
+	"net/http/httptest"
+	"os"
 )
 
+// zeroSource is an io.Reader that returns an unlimited number of zero bytes.
+type zeroSource struct{}
+
+func (zeroSource) Read(b []byte) (n int, err error) {
+	for i := range b {
+		b[i] = 0
+	}
+
+	return len(b), nil
+}
+
 func ExampleDial() {
 	// Connecting with a custom root-certificate set.
 
@@ -55,3 +70,46 @@ yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx
 	}
 	conn.Close()
 }
+
+func ExampleConfig_keyLogWriter() {
+	// Debugging TLS applications by decrypting a network traffic capture.
+
+	// WARNING: Use of KeyLogWriter compromises security and should only be
+	// used for debugging.
+
+	// Dummy test HTTP server for the example with insecure random so output is
+	// reproducible.
+	server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
+	server.TLS = &tls.Config{
+		Rand: zeroSource{}, // for example only; don't do this.
+	}
+	server.StartTLS()
+	defer server.Close()
+
+	// Typically the log would go to an open file:
+	// w, err := os.OpenFile("tls-secrets.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
+	w := os.Stdout
+
+	client := &http.Client{
+		Transport: &http.Transport{
+			TLSClientConfig: &tls.Config{
+				KeyLogWriter: w,
+
+				Rand:               zeroSource{}, // for reproducible output; don't do this.
+				InsecureSkipVerify: true,         // test server certificate is not trusted.
+			},
+		},
+	}
+	resp, err := client.Get(server.URL)
+	if err != nil {
+		log.Fatalf("Failed to get URL: %v", err)
+	}
+	resp.Body.Close()
+
+	// The resulting file can be used with Wireshark to decrypt the TLS
+	// connection by setting (Pre)-Master-Secret log filename in SSL Protocol
+	// preferences.
+
+	// Output:
+	// CLIENT_RANDOM 0000000000000000000000000000000000000000000000000000000000000000 baca0df460a688e44ce018b025183cc2353ae01f89755ef766eedd3ecc302888ee3b3a22962e45f48c20df15a98c0e80
+}
diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go
index f789e6f..6eda18d 100644
--- a/src/crypto/tls/handshake_client.go
+++ b/src/crypto/tls/handshake_client.go
@@ -115,7 +115,7 @@ NextCipherSuite:
 
 	// Session resumption is not allowed if renegotiating because
 	// renegotiation is primarily used to allow a client to send a client
-	// certificate, which would be skipped if session resumption occured.
+	// certificate, which would be skipped if session resumption occurred.
 	if sessionCache != nil && c.handshakes == 0 {
 		// Try to resume a previously negotiated TLS session, if
 		// available.
@@ -199,7 +199,7 @@ NextCipherSuite:
 	// Otherwise, in a full handshake, if we don't have any certificates
 	// configured then we will never send a CertificateVerify message and
 	// thus no signatures are needed in that case either.
-	if isResume || len(c.config.Certificates) == 0 {
+	if isResume || (len(c.config.Certificates) == 0 && c.config.GetClientCertificate == nil) {
 		hs.finishedHash.discardHandshakeBuffer()
 	}
 
@@ -304,6 +304,13 @@ func (hs *clientHandshakeState) doFullHandshake() error {
 			}
 		}
 
+		if c.config.VerifyPeerCertificate != nil {
+			if err := c.config.VerifyPeerCertificate(certMsg.certificates, c.verifiedChains); err != nil {
+				c.sendAlert(alertBadCertificate)
+				return err
+			}
+		}
+
 		switch certs[0].PublicKey.(type) {
 		case *rsa.PublicKey, *ecdsa.PublicKey:
 			break
@@ -370,71 +377,11 @@ func (hs *clientHandshakeState) doFullHandshake() error {
 	certReq, ok := msg.(*certificateRequestMsg)
 	if ok {
 		certRequested = true
-
-		// RFC 4346 on the certificateAuthorities field:
-		// A list of the distinguished names of acceptable certificate
-		// authorities. These distinguished names may specify a desired
-		// distinguished name for a root CA or for a subordinate CA;
-		// thus, this message can be used to describe both known roots
-		// and a desired authorization space. If the
-		// certificate_authorities list is empty then the client MAY
-		// send any certificate of the appropriate
-		// ClientCertificateType, unless there is some external
-		// arrangement to the contrary.
-
 		hs.finishedHash.Write(certReq.marshal())
 
-		var rsaAvail, ecdsaAvail bool
-		for _, certType := range certReq.certificateTypes {
-			switch certType {
-			case certTypeRSASign:
-				rsaAvail = true
-			case certTypeECDSASign:
-				ecdsaAvail = true
-			}
-		}
-
-		// We need to search our list of client certs for one
-		// where SignatureAlgorithm is acceptable to the server and the
-		// Issuer is in certReq.certificateAuthorities
-	findCert:
-		for i, chain := range c.config.Certificates {
-			if !rsaAvail && !ecdsaAvail {
-				continue
-			}
-
-			for j, cert := range chain.Certificate {
-				x509Cert := chain.Leaf
-				// parse the certificate if this isn't the leaf
-				// node, or if chain.Leaf was nil
-				if j != 0 || x509Cert == nil {
-					if x509Cert, err = x509.ParseCertificate(cert); err != nil {
-						c.sendAlert(alertInternalError)
-						return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error())
-					}
-				}
-
-				switch {
-				case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA:
-				case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA:
-				default:
-					continue findCert
-				}
-
-				if len(certReq.certificateAuthorities) == 0 {
-					// they gave us an empty list, so just take the
-					// first cert from c.config.Certificates
-					chainToSend = &chain
-					break findCert
-				}
-
-				for _, ca := range certReq.certificateAuthorities {
-					if bytes.Equal(x509Cert.RawIssuer, ca) {
-						chainToSend = &chain
-						break findCert
-					}
-				}
-			}
+		if chainToSend, err = hs.getCertificate(certReq); err != nil {
+			c.sendAlert(alertInternalError)
+			return err
 		}
 
 		msg, err = c.readHandshake()
@@ -455,9 +402,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
 	// certificate to send.
 	if certRequested {
 		certMsg = new(certificateMsg)
-		if chainToSend != nil {
-			certMsg.certificates = chainToSend.Certificate
-		}
+		certMsg.certificates = chainToSend.Certificate
 		hs.finishedHash.Write(certMsg.marshal())
 		if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
 			return err
@@ -476,7 +421,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
 		}
 	}
 
-	if chainToSend != nil {
+	if chainToSend != nil && len(chainToSend.Certificate) > 0 {
 		certVerify := &certificateVerifyMsg{
 			hasSignatureAndHash: c.vers >= VersionTLS12,
 		}
@@ -521,6 +466,10 @@ func (hs *clientHandshakeState) doFullHandshake() error {
 	}
 
 	hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
+	if err := c.config.writeKeyLog(hs.hello.random, hs.masterSecret); err != nil {
+		c.sendAlert(alertInternalError)
+		return errors.New("tls: failed to write to key log: " + err.Error())
+	}
 
 	hs.finishedHash.discardHandshakeBuffer()
 
@@ -716,6 +665,117 @@ func (hs *clientHandshakeState) sendFinished(out []byte) error {
 	return nil
 }
 
+// tls11SignatureSchemes contains the signature schemes that we synthesise for
+// a TLS <= 1.1 connection, based on the supported certificate types.
+var tls11SignatureSchemes = []SignatureScheme{ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1}
+
+const (
+	// tls11SignatureSchemesNumECDSA is the number of initial elements of
+	// tls11SignatureSchemes that use ECDSA.
+	tls11SignatureSchemesNumECDSA = 3
+	// tls11SignatureSchemesNumRSA is the number of trailing elements of
+	// tls11SignatureSchemes that use RSA.
+	tls11SignatureSchemesNumRSA = 4
+)
+
+func (hs *clientHandshakeState) getCertificate(certReq *certificateRequestMsg) (*Certificate, error) {
+	c := hs.c
+
+	var rsaAvail, ecdsaAvail bool
+	for _, certType := range certReq.certificateTypes {
+		switch certType {
+		case certTypeRSASign:
+			rsaAvail = true
+		case certTypeECDSASign:
+			ecdsaAvail = true
+		}
+	}
+
+	if c.config.GetClientCertificate != nil {
+		var signatureSchemes []SignatureScheme
+
+		if !certReq.hasSignatureAndHash {
+			// Prior to TLS 1.2, the signature schemes were not
+			// included in the certificate request message. In this
+			// case we use a plausible list based on the acceptable
+			// certificate types.
+			signatureSchemes = tls11SignatureSchemes
+			if !ecdsaAvail {
+				signatureSchemes = signatureSchemes[tls11SignatureSchemesNumECDSA:]
+			}
+			if !rsaAvail {
+				signatureSchemes = signatureSchemes[:len(signatureSchemes)-tls11SignatureSchemesNumRSA]
+			}
+		} else {
+			signatureSchemes = make([]SignatureScheme, 0, len(certReq.signatureAndHashes))
+			for _, sah := range certReq.signatureAndHashes {
+				signatureSchemes = append(signatureSchemes, SignatureScheme(sah.hash)<<8+SignatureScheme(sah.signature))
+			}
+		}
+
+		return c.config.GetClientCertificate(&CertificateRequestInfo{
+			AcceptableCAs:    certReq.certificateAuthorities,
+			SignatureSchemes: signatureSchemes,
+		})
+	}
+
+	// RFC 4346 on the certificateAuthorities field: A list of the
+	// distinguished names of acceptable certificate authorities.
+	// These distinguished names may specify a desired
+	// distinguished name for a root CA or for a subordinate CA;
+	// thus, this message can be used to describe both known roots
+	// and a desired authorization space. If the
+	// certificate_authorities list is empty then the client MAY
+	// send any certificate of the appropriate
+	// ClientCertificateType, unless there is some external
+	// arrangement to the contrary.
+
+	// We need to search our list of client certs for one
+	// where SignatureAlgorithm is acceptable to the server and the
+	// Issuer is in certReq.certificateAuthorities
+findCert:
+	for i, chain := range c.config.Certificates {
+		if !rsaAvail && !ecdsaAvail {
+			continue
+		}
+
+		for j, cert := range chain.Certificate {
+			x509Cert := chain.Leaf
+			// parse the certificate if this isn't the leaf
+			// node, or if chain.Leaf was nil
+			if j != 0 || x509Cert == nil {
+				var err error
+				if x509Cert, err = x509.ParseCertificate(cert); err != nil {
+					c.sendAlert(alertInternalError)
+					return nil, errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error())
+				}
+			}
+
+			switch {
+			case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA:
+			case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA:
+			default:
+				continue findCert
+			}
+
+			if len(certReq.certificateAuthorities) == 0 {
+				// they gave us an empty list, so just take the
+				// first cert from c.config.Certificates
+				return &chain, nil
+			}
+
+			for _, ca := range certReq.certificateAuthorities {
+				if bytes.Equal(x509Cert.RawIssuer, ca) {
+					return &chain, nil
+				}
+			}
+		}
+	}
+
+	// No acceptable certificate found. Don't send a certificate.
+	return new(Certificate), nil
+}
+
 // clientSessionCacheKey returns a key used to cache sessionTickets that could
 // be used to resume previously negotiated TLS sessions with a server.
 func clientSessionCacheKey(serverAddr net.Addr, config *Config) string {
diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go
index 07d31b6..5851f89 100644
--- a/src/crypto/tls/handshake_client_test.go
+++ b/src/crypto/tls/handshake_client_test.go
@@ -15,12 +15,14 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"math/big"
 	"net"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"strconv"
 	"strings"
+	"sync"
 	"testing"
 	"time"
 )
@@ -76,7 +78,7 @@ func newOpensslOutputSink() *opensslOutputSink {
 
 // opensslEndOfHandshake is a message that the “openssl s_server” tool will
 // print when a handshake completes if run with “-state”.
-const opensslEndOfHandshake = "SSL_accept:SSLv3 write finished A"
+const opensslEndOfHandshake = "SSL_accept:SSLv3/TLS write finished"
 
 func (o *opensslOutputSink) Write(data []byte) (n int, err error) {
 	o.line = append(o.line, data...)
@@ -275,6 +277,8 @@ func (test *clientTest) loadData() (flows [][]byte, err error) {
 }
 
 func (test *clientTest) run(t *testing.T, write bool) {
+	checkOpenSSLVersion(t)
+
 	var clientConn, serverConn net.Conn
 	var recordingConn *recordingConn
 	var childProcess *exec.Cmd
@@ -355,7 +359,7 @@ func (test *clientTest) run(t *testing.T, write bool) {
 				}
 
 				if expected := i + 1; client.handshakes != expected {
-					t.Errorf("client should have recorded %d handshakes, but believes that %d have occured", expected, client.handshakes)
+					t.Errorf("client should have recorded %d handshakes, but believes that %d have occurred", expected, client.handshakes)
 				}
 			}()
 
@@ -409,7 +413,7 @@ func (test *clientTest) run(t *testing.T, write bool) {
 		childProcess.Process.Kill()
 		childProcess.Wait()
 		if len(recordingConn.flows) < 3 {
-			childProcess.Stdout.(*bytes.Buffer).WriteTo(os.Stdout)
+			os.Stdout.Write(childProcess.Stdout.(*opensslOutputSink).all)
 			t.Fatalf("Client connection didn't work")
 		}
 		recordingConn.WriteTo(out)
@@ -417,7 +421,26 @@ func (test *clientTest) run(t *testing.T, write bool) {
 	}
 }
 
+var (
+	didParMu sync.Mutex
+	didPar   = map[*testing.T]bool{}
+)
+
+// setParallel calls t.Parallel once. If you call it twice, it would
+// panic.
+func setParallel(t *testing.T) {
+	didParMu.Lock()
+	v := didPar[t]
+	didPar[t] = true
+	didParMu.Unlock()
+	if !v {
+		t.Parallel()
+	}
+}
+
 func runClientTestForVersion(t *testing.T, template *clientTest, prefix, option string) {
+	setParallel(t)
+
 	test := *template
 	test.name = prefix + test.name
 	if len(test.command) == 0 {
@@ -508,14 +531,81 @@ func TestHandshakeClientAES256GCMSHA384(t *testing.T) {
 	runClientTestTLS12(t, test)
 }
 
+func TestHandshakeClientAES128CBCSHA256(t *testing.T) {
+	test := &clientTest{
+		name:    "AES128-SHA256",
+		command: []string{"openssl", "s_server", "-cipher", "AES128-SHA256"},
+	}
+	runClientTestTLS12(t, test)
+}
+
+func TestHandshakeClientECDHERSAAES128CBCSHA256(t *testing.T) {
+	test := &clientTest{
+		name:    "ECDHE-RSA-AES128-SHA256",
+		command: []string{"openssl", "s_server", "-cipher", "ECDHE-RSA-AES128-SHA256"},
+	}
+	runClientTestTLS12(t, test)
+}
+
+func TestHandshakeClientECDHEECDSAAES128CBCSHA256(t *testing.T) {
+	test := &clientTest{
+		name:    "ECDHE-ECDSA-AES128-SHA256",
+		command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA256"},
+		cert:    testECDSACertificate,
+		key:     testECDSAPrivateKey,
+	}
+	runClientTestTLS12(t, test)
+}
+
+func TestHandshakeClientX25519(t *testing.T) {
+	config := testConfig.Clone()
+	config.CurvePreferences = []CurveID{X25519}
+
+	test := &clientTest{
+		name:    "X25519-ECDHE-RSA-AES-GCM",
+		command: []string{"openssl", "s_server", "-cipher", "ECDHE-RSA-AES128-GCM-SHA256"},
+		config:  config,
+	}
+
+	runClientTestTLS12(t, test)
+}
+
+func TestHandshakeClientECDHERSAChaCha20(t *testing.T) {
+	config := testConfig.Clone()
+	config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305}
+
+	test := &clientTest{
+		name:    "ECDHE-RSA-CHACHA20-POLY1305",
+		command: []string{"openssl", "s_server", "-cipher", "ECDHE-RSA-CHACHA20-POLY1305"},
+		config:  config,
+	}
+
+	runClientTestTLS12(t, test)
+}
+
+func TestHandshakeClientECDHEECDSAChaCha20(t *testing.T) {
+	config := testConfig.Clone()
+	config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305}
+
+	test := &clientTest{
+		name:    "ECDHE-ECDSA-CHACHA20-POLY1305",
+		command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-CHACHA20-POLY1305"},
+		config:  config,
+		cert:    testECDSACertificate,
+		key:     testECDSAPrivateKey,
+	}
+
+	runClientTestTLS12(t, test)
+}
+
 func TestHandshakeClientCertRSA(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	cert, _ := X509KeyPair([]byte(clientCertificatePEM), []byte(clientKeyPEM))
 	config.Certificates = []Certificate{cert}
 
 	test := &clientTest{
 		name:    "ClientCert-RSA-RSA",
-		command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"},
+		command: []string{"openssl", "s_server", "-cipher", "AES128", "-verify", "1"},
 		config:  config,
 	}
 
@@ -545,13 +635,13 @@ func TestHandshakeClientCertRSA(t *testing.T) {
 }
 
 func TestHandshakeClientCertECDSA(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	cert, _ := X509KeyPair([]byte(clientECDSACertificatePEM), []byte(clientECDSAKeyPEM))
 	config.Certificates = []Certificate{cert}
 
 	test := &clientTest{
 		name:    "ClientCert-ECDSA-RSA",
-		command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"},
+		command: []string{"openssl", "s_server", "-cipher", "AES128", "-verify", "1"},
 		config:  config,
 	}
 
@@ -622,13 +712,14 @@ func TestClientResumption(t *testing.T) {
 		t.Fatal("first ticket doesn't match ticket after resumption")
 	}
 
-	key2 := randomKey()
-	serverConfig.SetSessionTicketKeys([][32]byte{key2})
+	key1 := randomKey()
+	serverConfig.SetSessionTicketKeys([][32]byte{key1})
 
 	testResumeState("InvalidSessionTicketKey", false)
 	testResumeState("ResumeAfterInvalidSessionTicketKey", true)
 
-	serverConfig.SetSessionTicketKeys([][32]byte{randomKey(), key2})
+	key2 := randomKey()
+	serverConfig.SetSessionTicketKeys([][32]byte{key2, key1})
 	ticket = getTicket()
 	testResumeState("KeyChange", true)
 	if bytes.Equal(ticket, getTicket()) {
@@ -636,6 +727,16 @@ func TestClientResumption(t *testing.T) {
 	}
 	testResumeState("KeyChangeFinish", true)
 
+	// Reset serverConfig to ensure that calling SetSessionTicketKeys
+	// before the serverConfig is used works.
+	serverConfig = &Config{
+		CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
+		Certificates: testConfig.Certificates,
+	}
+	serverConfig.SetSessionTicketKeys([][32]byte{key2})
+
+	testResumeState("FreshConfig", true)
+
 	clientConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
 	testResumeState("DifferentCipherSuite", false)
 	testResumeState("DifferentCipherSuiteRecovers", true)
@@ -690,8 +791,59 @@ func TestLRUClientSessionCache(t *testing.T) {
 	}
 }
 
+func TestKeyLog(t *testing.T) {
+	var serverBuf, clientBuf bytes.Buffer
+
+	clientConfig := testConfig.Clone()
+	clientConfig.KeyLogWriter = &clientBuf
+
+	serverConfig := testConfig.Clone()
+	serverConfig.KeyLogWriter = &serverBuf
+
+	c, s := net.Pipe()
+	done := make(chan bool)
+
+	go func() {
+		defer close(done)
+
+		if err := Server(s, serverConfig).Handshake(); err != nil {
+			t.Errorf("server: %s", err)
+			return
+		}
+		s.Close()
+	}()
+
+	if err := Client(c, clientConfig).Handshake(); err != nil {
+		t.Fatalf("client: %s", err)
+	}
+
+	c.Close()
+	<-done
+
+	checkKeylogLine := func(side, loggedLine string) {
+		if len(loggedLine) == 0 {
+			t.Fatalf("%s: no keylog line was produced", side)
+		}
+		const expectedLen = 13 /* "CLIENT_RANDOM" */ +
+			1 /* space */ +
+			32*2 /* hex client nonce */ +
+			1 /* space */ +
+			48*2 /* hex master secret */ +
+			1 /* new line */
+		if len(loggedLine) != expectedLen {
+			t.Fatalf("%s: keylog line has incorrect length (want %d, got %d): %q", side, expectedLen, len(loggedLine), loggedLine)
+		}
+		if !strings.HasPrefix(loggedLine, "CLIENT_RANDOM "+strings.Repeat("0", 64)+" ") {
+			t.Fatalf("%s: keylog line has incorrect structure or nonce: %q", side, loggedLine)
+		}
+	}
+
+	checkKeylogLine("client", string(clientBuf.Bytes()))
+	checkKeylogLine("server", string(serverBuf.Bytes()))
+}
+
 func TestHandshakeClientALPNMatch(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.NextProtos = []string{"proto2", "proto1"}
 
 	test := &clientTest{
@@ -711,32 +863,11 @@ func TestHandshakeClientALPNMatch(t *testing.T) {
 	runClientTestTLS12(t, test)
 }
 
-func TestHandshakeClientALPNNoMatch(t *testing.T) {
-	config := testConfig.clone()
-	config.NextProtos = []string{"proto3"}
-
-	test := &clientTest{
-		name: "ALPN-NoMatch",
-		// Note that this needs OpenSSL 1.0.2 because that is the first
-		// version that supports the -alpn flag.
-		command: []string{"openssl", "s_server", "-alpn", "proto1,proto2"},
-		config:  config,
-		validate: func(state ConnectionState) error {
-			// There's no overlap so OpenSSL will not select a protocol.
-			if state.NegotiatedProtocol != "" {
-				return fmt.Errorf("Got protocol %q, wanted ''", state.NegotiatedProtocol)
-			}
-			return nil
-		},
-	}
-	runClientTestTLS12(t, test)
-}
-
 // sctsBase64 contains data from `openssl s_client -serverinfo 18 -connect ritter.vg:443`
 const sctsBase64 = "ABIBaQFnAHUApLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BAAAAFHl5nuFgAABAMARjBEAiAcS4JdlW5nW9sElUv2zvQyPoZ6ejKrGGB03gjaBZFMLwIgc1Qbbn+hsH0RvObzhS+XZhr3iuQQJY8S9G85D9KeGPAAdgBo9pj4H2SCvjqM7rkoHUz8cVFdZ5PURNEKZ6y7T0/7xAAAAUeX4bVwAAAEAwBHMEUCIDIhFDgG2HIuADBkGuLobU5a4dlCHoJLliWJ1SYT05z6AiEAjxIoZFFPRNWMGGIjskOTMwXzQ1Wh2e7NxXE1kd1J0QsAdgDuS723dc5guuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAUhcZIqHAAAEAwBHMEUCICmJ1rBT09LpkbzxtUC+Hi7nXLR0J+2PmwLp+sJMuqK+AiEAr0NkUnEVKVhAkccIFpYDqHOlZaBs [...]
 
 func TestHandshakClientSCTs(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 
 	scts, err := base64.StdEncoding.DecodeString(sctsBase64)
 	if err != nil {
@@ -771,7 +902,7 @@ func TestHandshakClientSCTs(t *testing.T) {
 }
 
 func TestRenegotiationRejected(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	test := &clientTest{
 		name:                        "RenegotiationRejected",
 		command:                     []string{"openssl", "s_server", "-state"},
@@ -793,7 +924,7 @@ func TestRenegotiationRejected(t *testing.T) {
 }
 
 func TestRenegotiateOnce(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.Renegotiation = RenegotiateOnceAsClient
 
 	test := &clientTest{
@@ -807,7 +938,7 @@ func TestRenegotiateOnce(t *testing.T) {
 }
 
 func TestRenegotiateTwice(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.Renegotiation = RenegotiateFreelyAsClient
 
 	test := &clientTest{
@@ -821,7 +952,7 @@ func TestRenegotiateTwice(t *testing.T) {
 }
 
 func TestRenegotiateTwiceRejected(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.Renegotiation = RenegotiateOnceAsClient
 
 	test := &clientTest{
@@ -956,6 +1087,160 @@ func TestServerSelectingUnconfiguredCipherSuite(t *testing.T) {
 	}
 }
 
+func TestVerifyPeerCertificate(t *testing.T) {
+	issuer, err := x509.ParseCertificate(testRSACertificateIssuer)
+	if err != nil {
+		panic(err)
+	}
+
+	rootCAs := x509.NewCertPool()
+	rootCAs.AddCert(issuer)
+
+	now := func() time.Time { return time.Unix(1476984729, 0) }
+
+	sentinelErr := errors.New("TestVerifyPeerCertificate")
+
+	verifyCallback := func(called *bool, rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
+		if l := len(rawCerts); l != 1 {
+			return fmt.Errorf("got len(rawCerts) = %d, wanted 1", l)
+		}
+		if len(validatedChains) == 0 {
+			return errors.New("got len(validatedChains) = 0, wanted non-zero")
+		}
+		*called = true
+		return nil
+	}
+
+	tests := []struct {
+		configureServer func(*Config, *bool)
+		configureClient func(*Config, *bool)
+		validate        func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error)
+	}{
+		{
+			configureServer: func(config *Config, called *bool) {
+				config.InsecureSkipVerify = false
+				config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
+					return verifyCallback(called, rawCerts, validatedChains)
+				}
+			},
+			configureClient: func(config *Config, called *bool) {
+				config.InsecureSkipVerify = false
+				config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
+					return verifyCallback(called, rawCerts, validatedChains)
+				}
+			},
+			validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
+				if clientErr != nil {
+					t.Errorf("test[%d]: client handshake failed: %v", testNo, clientErr)
+				}
+				if serverErr != nil {
+					t.Errorf("test[%d]: server handshake failed: %v", testNo, serverErr)
+				}
+				if !clientCalled {
+					t.Errorf("test[%d]: client did not call callback", testNo)
+				}
+				if !serverCalled {
+					t.Errorf("test[%d]: server did not call callback", testNo)
+				}
+			},
+		},
+		{
+			configureServer: func(config *Config, called *bool) {
+				config.InsecureSkipVerify = false
+				config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
+					return sentinelErr
+				}
+			},
+			configureClient: func(config *Config, called *bool) {
+				config.VerifyPeerCertificate = nil
+			},
+			validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
+				if serverErr != sentinelErr {
+					t.Errorf("#%d: got server error %v, wanted sentinelErr", testNo, serverErr)
+				}
+			},
+		},
+		{
+			configureServer: func(config *Config, called *bool) {
+				config.InsecureSkipVerify = false
+			},
+			configureClient: func(config *Config, called *bool) {
+				config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
+					return sentinelErr
+				}
+			},
+			validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
+				if clientErr != sentinelErr {
+					t.Errorf("#%d: got client error %v, wanted sentinelErr", testNo, clientErr)
+				}
+			},
+		},
+		{
+			configureServer: func(config *Config, called *bool) {
+				config.InsecureSkipVerify = false
+			},
+			configureClient: func(config *Config, called *bool) {
+				config.InsecureSkipVerify = true
+				config.VerifyPeerCertificate = func(rawCerts [][]byte, validatedChains [][]*x509.Certificate) error {
+					if l := len(rawCerts); l != 1 {
+						return fmt.Errorf("got len(rawCerts) = %d, wanted 1", l)
+					}
+					// With InsecureSkipVerify set, this
+					// callback should still be called but
+					// validatedChains must be empty.
+					if l := len(validatedChains); l != 0 {
+						return errors.New("got len(validatedChains) = 0, wanted zero")
+					}
+					*called = true
+					return nil
+				}
+			},
+			validate: func(t *testing.T, testNo int, clientCalled, serverCalled bool, clientErr, serverErr error) {
+				if clientErr != nil {
+					t.Errorf("test[%d]: client handshake failed: %v", testNo, clientErr)
+				}
+				if serverErr != nil {
+					t.Errorf("test[%d]: server handshake failed: %v", testNo, serverErr)
+				}
+				if !clientCalled {
+					t.Errorf("test[%d]: client did not call callback", testNo)
+				}
+			},
+		},
+	}
+
+	for i, test := range tests {
+		c, s := net.Pipe()
+		done := make(chan error)
+
+		var clientCalled, serverCalled bool
+
+		go func() {
+			config := testConfig.Clone()
+			config.ServerName = "example.golang"
+			config.ClientAuth = RequireAndVerifyClientCert
+			config.ClientCAs = rootCAs
+			config.Time = now
+			test.configureServer(config, &serverCalled)
+
+			err = Server(s, config).Handshake()
+			s.Close()
+			done <- err
+		}()
+
+		config := testConfig.Clone()
+		config.ServerName = "example.golang"
+		config.RootCAs = rootCAs
+		config.Time = now
+		test.configureClient(config, &clientCalled)
+		clientErr := Client(c, config).Handshake()
+		c.Close()
+		serverErr := <-done
+
+		test.validate(t, i, clientCalled, serverCalled, clientErr, serverErr)
+	}
+}
+
 // brokenConn wraps a net.Conn and causes all Writes after a certain number to
 // fail with brokenConnErr.
 type brokenConn struct {
@@ -1046,7 +1331,52 @@ func TestBuffering(t *testing.T) {
 	}
 }
 
+func TestAlertFlushing(t *testing.T) {
+	c, s := net.Pipe()
+	done := make(chan bool)
+
+	clientWCC := &writeCountingConn{Conn: c}
+	serverWCC := &writeCountingConn{Conn: s}
+
+	serverConfig := testConfig.Clone()
+
+	// Cause a signature-time error
+	brokenKey := rsa.PrivateKey{PublicKey: testRSAPrivateKey.PublicKey}
+	brokenKey.D = big.NewInt(42)
+	serverConfig.Certificates = []Certificate{{
+		Certificate: [][]byte{testRSACertificate},
+		PrivateKey:  &brokenKey,
+	}}
+
+	go func() {
+		Server(serverWCC, serverConfig).Handshake()
+		serverWCC.Close()
+		done <- true
+	}()
+
+	err := Client(clientWCC, testConfig).Handshake()
+	if err == nil {
+		t.Fatal("client unexpectedly returned no error")
+	}
+
+	const expectedError = "remote error: tls: handshake failure"
+	if e := err.Error(); !strings.Contains(e, expectedError) {
+		t.Fatalf("expected to find %q in error but error was %q", expectedError, e)
+	}
+	clientWCC.Close()
+	<-done
+
+	if n := clientWCC.numWrites; n != 1 {
+		t.Errorf("expected client handshake to complete with one write, but saw %d", n)
+	}
+
+	if n := serverWCC.numWrites; n != 1 {
+		t.Errorf("expected server handshake to complete with one write, but saw %d", n)
+	}
+}
+
 func TestHandshakeRace(t *testing.T) {
+	t.Parallel()
 	// This test races a Read and Write to try and complete a handshake in
 	// order to provide some evidence that there are no races or deadlocks
 	// in the handshake locking.
@@ -1099,3 +1429,137 @@ func TestHandshakeRace(t *testing.T) {
 		<-readDone
 	}
 }
+
+func TestTLS11SignatureSchemes(t *testing.T) {
+	expected := tls11SignatureSchemesNumECDSA + tls11SignatureSchemesNumRSA
+	if expected != len(tls11SignatureSchemes) {
+		t.Errorf("expected to find %d TLS 1.1 signature schemes, but found %d", expected, len(tls11SignatureSchemes))
+	}
+}
+
+var getClientCertificateTests = []struct {
+	setup               func(*Config)
+	expectedClientError string
+	verify              func(*testing.T, int, *ConnectionState)
+}{
+	{
+		func(clientConfig *Config) {
+			// Returning a Certificate with no certificate data
+			// should result in an empty message being sent to the
+			// server.
+			clientConfig.GetClientCertificate = func(cri *CertificateRequestInfo) (*Certificate, error) {
+				if len(cri.SignatureSchemes) == 0 {
+					panic("empty SignatureSchemes")
+				}
+				return new(Certificate), nil
+			}
+		},
+		"",
+		func(t *testing.T, testNum int, cs *ConnectionState) {
+			if l := len(cs.PeerCertificates); l != 0 {
+				t.Errorf("#%d: expected no certificates but got %d", testNum, l)
+			}
+		},
+	},
+	{
+		func(clientConfig *Config) {
+			// With TLS 1.1, the SignatureSchemes should be
+			// synthesised from the supported certificate types.
+			clientConfig.MaxVersion = VersionTLS11
+			clientConfig.GetClientCertificate = func(cri *CertificateRequestInfo) (*Certificate, error) {
+				if len(cri.SignatureSchemes) == 0 {
+					panic("empty SignatureSchemes")
+				}
+				return new(Certificate), nil
+			}
+		},
+		"",
+		func(t *testing.T, testNum int, cs *ConnectionState) {
+			if l := len(cs.PeerCertificates); l != 0 {
+				t.Errorf("#%d: expected no certificates but got %d", testNum, l)
+			}
+		},
+	},
+	{
+		func(clientConfig *Config) {
+			// Returning an error should abort the handshake with
+			// that error.
+			clientConfig.GetClientCertificate = func(cri *CertificateRequestInfo) (*Certificate, error) {
+				return nil, errors.New("GetClientCertificate")
+			}
+		},
+		"GetClientCertificate",
+		func(t *testing.T, testNum int, cs *ConnectionState) {
+		},
+	},
+	{
+		func(clientConfig *Config) {
+			clientConfig.GetClientCertificate = func(cri *CertificateRequestInfo) (*Certificate, error) {
+				return &testConfig.Certificates[0], nil
+			}
+		},
+		"",
+		func(t *testing.T, testNum int, cs *ConnectionState) {
+			if l := len(cs.VerifiedChains); l != 0 {
+				t.Errorf("#%d: expected some verified chains, but found none", testNum)
+			}
+		},
+	},
+}
+
+func TestGetClientCertificate(t *testing.T) {
+	issuer, err := x509.ParseCertificate(testRSACertificateIssuer)
+	if err != nil {
+		panic(err)
+	}
+
+	for i, test := range getClientCertificateTests {
+		serverConfig := testConfig.Clone()
+		serverConfig.ClientAuth = RequestClientCert
+		serverConfig.RootCAs = x509.NewCertPool()
+		serverConfig.RootCAs.AddCert(issuer)
+
+		clientConfig := testConfig.Clone()
+
+		test.setup(clientConfig)
+
+		type serverResult struct {
+			cs  ConnectionState
+			err error
+		}
+
+		c, s := net.Pipe()
+		done := make(chan serverResult)
+
+		go func() {
+			defer s.Close()
+			server := Server(s, serverConfig)
+			err := server.Handshake()
+
+			var cs ConnectionState
+			if err == nil {
+				cs = server.ConnectionState()
+			}
+			done <- serverResult{cs, err}
+		}()
+
+		clientErr := Client(c, clientConfig).Handshake()
+		c.Close()
+
+		result := <-done
+
+		if clientErr != nil {
+			if len(test.expectedClientError) == 0 {
+				t.Errorf("#%d: client error: %v", i, clientErr)
+			} else if got := clientErr.Error(); got != test.expectedClientError {
+				t.Errorf("#%d: expected client error %q, but got %q", i, test.expectedClientError, got)
+			}
+		} else if len(test.expectedClientError) > 0 {
+			t.Errorf("#%d: expected client error %q, but got no error", i, test.expectedClientError)
+		} else if err := result.err; err != nil {
+			t.Errorf("#%d: server error: %v", i, err)
+		} else {
+			test.verify(t, i, &result.cs)
+		}
+	}
+}
diff --git a/src/crypto/tls/handshake_messages.go b/src/crypto/tls/handshake_messages.go
index ab8e60a..694bd91 100644
--- a/src/crypto/tls/handshake_messages.go
+++ b/src/crypto/tls/handshake_messages.go
@@ -802,12 +802,9 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
 			}
 			l := int(d[0])<<8 | int(d[1])
 			d = d[2:]
-			if len(d) != l {
+			if len(d) != l || l == 0 {
 				return false
 			}
-			if l == 0 {
-				continue
-			}
 
 			m.scts = make([][]byte, 0, 3)
 			for len(d) != 0 {
@@ -816,7 +813,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
 				}
 				sctLen := int(d[0])<<8 | int(d[1])
 				d = d[2:]
-				if len(d) < sctLen {
+				if sctLen == 0 || len(d) < sctLen {
 					return false
 				}
 				m.scts = append(m.scts, d[:sctLen])
diff --git a/src/crypto/tls/handshake_messages_test.go b/src/crypto/tls/handshake_messages_test.go
index 95d825b..f1154d4 100644
--- a/src/crypto/tls/handshake_messages_test.go
+++ b/src/crypto/tls/handshake_messages_test.go
@@ -5,6 +5,7 @@
 package tls
 
 import (
+	"bytes"
 	"math/rand"
 	"reflect"
 	"testing"
@@ -260,3 +261,65 @@ func (*sessionState) Generate(rand *rand.Rand, size int) reflect.Value {
 	}
 	return reflect.ValueOf(s)
 }
+
+func TestRejectEmptySCTList(t *testing.T) {
+	// https://tools.ietf.org/html/rfc6962#section-3.3.1 specifies that
+	// empty SCT lists are invalid.
+
+	var random [32]byte
+	sct := []byte{0x42, 0x42, 0x42, 0x42}
+	serverHello := serverHelloMsg{
+		vers:   VersionTLS12,
+		random: random[:],
+		scts:   [][]byte{sct},
+	}
+	serverHelloBytes := serverHello.marshal()
+
+	var serverHelloCopy serverHelloMsg
+	if !serverHelloCopy.unmarshal(serverHelloBytes) {
+		t.Fatal("Failed to unmarshal initial message")
+	}
+
+	// Change serverHelloBytes so that the SCT list is empty
+	i := bytes.Index(serverHelloBytes, sct)
+	if i < 0 {
+		t.Fatal("Cannot find SCT in ServerHello")
+	}
+
+	var serverHelloEmptySCT []byte
+	serverHelloEmptySCT = append(serverHelloEmptySCT, serverHelloBytes[:i-6]...)
+	// Append the extension length and SCT list length for an empty list.
+	serverHelloEmptySCT = append(serverHelloEmptySCT, []byte{0, 2, 0, 0}...)
+	serverHelloEmptySCT = append(serverHelloEmptySCT, serverHelloBytes[i+4:]...)
+
+	// Update the handshake message length.
+	serverHelloEmptySCT[1] = byte((len(serverHelloEmptySCT) - 4) >> 16)
+	serverHelloEmptySCT[2] = byte((len(serverHelloEmptySCT) - 4) >> 8)
+	serverHelloEmptySCT[3] = byte(len(serverHelloEmptySCT) - 4)
+
+	// Update the extensions length
+	serverHelloEmptySCT[42] = byte((len(serverHelloEmptySCT) - 44) >> 8)
+	serverHelloEmptySCT[43] = byte((len(serverHelloEmptySCT) - 44))
+
+	if serverHelloCopy.unmarshal(serverHelloEmptySCT) {
+		t.Fatal("Unmarshaled ServerHello with empty SCT list")
+	}
+}
+
+func TestRejectEmptySCT(t *testing.T) {
+	// Not only must the SCT list be non-empty, but the SCT elements must
+	// not be zero length.
+
+	var random [32]byte
+	serverHello := serverHelloMsg{
+		vers:   VersionTLS12,
+		random: random[:],
+		scts:   [][]byte{nil},
+	}
+	serverHelloBytes := serverHello.marshal()
+
+	var serverHelloCopy serverHelloMsg
+	if serverHelloCopy.unmarshal(serverHelloBytes) {
+		t.Fatal("Unmarshaled ServerHello with zero-length SCT")
+	}
+}
diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go
index 1aac729..b786c30 100644
--- a/src/crypto/tls/handshake_server.go
+++ b/src/crypto/tls/handshake_server.go
@@ -19,29 +19,28 @@ import (
 // serverHandshakeState contains details of a server handshake in progress.
 // It's discarded once the handshake has completed.
 type serverHandshakeState struct {
-	c               *Conn
-	clientHello     *clientHelloMsg
-	hello           *serverHelloMsg
-	suite           *cipherSuite
-	ellipticOk      bool
-	ecdsaOk         bool
-	rsaDecryptOk    bool
-	rsaSignOk       bool
-	sessionState    *sessionState
-	finishedHash    finishedHash
-	masterSecret    []byte
-	certsFromClient [][]byte
-	cert            *Certificate
+	c                     *Conn
+	clientHello           *clientHelloMsg
+	hello                 *serverHelloMsg
+	suite                 *cipherSuite
+	ellipticOk            bool
+	ecdsaOk               bool
+	rsaDecryptOk          bool
+	rsaSignOk             bool
+	sessionState          *sessionState
+	finishedHash          finishedHash
+	masterSecret          []byte
+	certsFromClient       [][]byte
+	cert                  *Certificate
+	cachedClientHelloInfo *ClientHelloInfo
 }
 
 // serverHandshake performs a TLS handshake as a server.
 // c.out.Mutex <= L; c.handshakeMutex <= L.
 func (c *Conn) serverHandshake() error {
-	config := c.config
-
 	// If this is the first server handshake, we generate a random key to
 	// encrypt the tickets with.
-	config.serverInitOnce.Do(config.serverInit)
+	c.config.serverInitOnce.Do(c.config.serverInit)
 
 	hs := serverHandshakeState{
 		c: c,
@@ -112,7 +111,6 @@ func (c *Conn) serverHandshake() error {
 // readClientHello reads a ClientHello message from the client and decides
 // whether we will perform session resumption.
 func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
-	config := hs.c.config
 	c := hs.c
 
 	msg, err := c.readHandshake()
@@ -125,7 +123,22 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
 		c.sendAlert(alertUnexpectedMessage)
 		return false, unexpectedMessageError(hs.clientHello, msg)
 	}
-	c.vers, ok = config.mutualVersion(hs.clientHello.vers)
+
+	if c.config.GetConfigForClient != nil {
+		if newConfig, err := c.config.GetConfigForClient(hs.clientHelloInfo()); err != nil {
+			c.sendAlert(alertInternalError)
+			return false, err
+		} else if newConfig != nil {
+			newConfig.mutex.Lock()
+			newConfig.originalConfig = c.config
+			newConfig.mutex.Unlock()
+
+			newConfig.serverInitOnce.Do(newConfig.serverInit)
+			c.config = newConfig
+		}
+	}
+
+	c.vers, ok = c.config.mutualVersion(hs.clientHello.vers)
 	if !ok {
 		c.sendAlert(alertProtocolVersion)
 		return false, fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers)
@@ -135,7 +148,7 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
 	hs.hello = new(serverHelloMsg)
 
 	supportedCurve := false
-	preferredCurves := config.curvePreferences()
+	preferredCurves := c.config.curvePreferences()
 Curves:
 	for _, curve := range hs.clientHello.supportedCurves {
 		for _, supported := range preferredCurves {
@@ -171,7 +184,7 @@ Curves:
 
 	hs.hello.vers = c.vers
 	hs.hello.random = make([]byte, 32)
-	_, err = io.ReadFull(config.rand(), hs.hello.random)
+	_, err = io.ReadFull(c.config.rand(), hs.hello.random)
 	if err != nil {
 		c.sendAlert(alertInternalError)
 		return false, err
@@ -196,20 +209,15 @@ Curves:
 	} else {
 		// Although sending an empty NPN extension is reasonable, Firefox has
 		// had a bug around this. Best to send nothing at all if
-		// config.NextProtos is empty. See
+		// c.config.NextProtos is empty. See
 		// https://golang.org/issue/5445.
-		if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 {
+		if hs.clientHello.nextProtoNeg && len(c.config.NextProtos) > 0 {
 			hs.hello.nextProtoNeg = true
-			hs.hello.nextProtos = config.NextProtos
+			hs.hello.nextProtos = c.config.NextProtos
 		}
 	}
 
-	hs.cert, err = config.getCertificate(&ClientHelloInfo{
-		CipherSuites:    hs.clientHello.cipherSuites,
-		ServerName:      hs.clientHello.serverName,
-		SupportedCurves: hs.clientHello.supportedCurves,
-		SupportedPoints: hs.clientHello.supportedPoints,
-	})
+	hs.cert, err = c.config.getCertificate(hs.clientHelloInfo())
 	if err != nil {
 		c.sendAlert(alertInternalError)
 		return false, err
@@ -354,18 +362,17 @@ func (hs *serverHandshakeState) doResumeHandshake() error {
 }
 
 func (hs *serverHandshakeState) doFullHandshake() error {
-	config := hs.c.config
 	c := hs.c
 
 	if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
 		hs.hello.ocspStapling = true
 	}
 
-	hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled
+	hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
 	hs.hello.cipherSuite = hs.suite.id
 
 	hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite)
-	if config.ClientAuth == NoClientCert {
+	if c.config.ClientAuth == NoClientCert {
 		// No need to keep a full record of the handshake if client
 		// certificates won't be used.
 		hs.finishedHash.discardHandshakeBuffer()
@@ -394,7 +401,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
 	}
 
 	keyAgreement := hs.suite.ka(c.vers)
-	skx, err := keyAgreement.generateServerKeyExchange(config, hs.cert, hs.clientHello, hs.hello)
+	skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello)
 	if err != nil {
 		c.sendAlert(alertHandshakeFailure)
 		return err
@@ -406,7 +413,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
 		}
 	}
 
-	if config.ClientAuth >= RequestClientCert {
+	if c.config.ClientAuth >= RequestClientCert {
 		// Request a client certificate
 		certReq := new(certificateRequestMsg)
 		certReq.certificateTypes = []byte{
@@ -423,8 +430,8 @@ func (hs *serverHandshakeState) doFullHandshake() error {
 		// to our request. When we know the CAs we trust, then
 		// we can send them down, so that the client can choose
 		// an appropriate certificate to give to us.
-		if config.ClientCAs != nil {
-			certReq.certificateAuthorities = config.ClientCAs.Subjects()
+		if c.config.ClientCAs != nil {
+			certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
 		}
 		hs.finishedHash.Write(certReq.marshal())
 		if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
@@ -452,7 +459,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
 	var ok bool
 	// If we requested a client certificate, then the client must send a
 	// certificate message, even if it's empty.
-	if config.ClientAuth >= RequestClientCert {
+	if c.config.ClientAuth >= RequestClientCert {
 		if certMsg, ok = msg.(*certificateMsg); !ok {
 			c.sendAlert(alertUnexpectedMessage)
 			return unexpectedMessageError(certMsg, msg)
@@ -461,7 +468,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
 
 		if len(certMsg.certificates) == 0 {
 			// The client didn't actually send a certificate
-			switch config.ClientAuth {
+			switch c.config.ClientAuth {
 			case RequireAnyClientCert, RequireAndVerifyClientCert:
 				c.sendAlert(alertBadCertificate)
 				return errors.New("tls: client didn't provide a certificate")
@@ -487,12 +494,16 @@ func (hs *serverHandshakeState) doFullHandshake() error {
 	}
 	hs.finishedHash.Write(ckx.marshal())
 
-	preMasterSecret, err := keyAgreement.processClientKeyExchange(config, hs.cert, ckx, c.vers)
+	preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers)
 	if err != nil {
 		c.sendAlert(alertHandshakeFailure)
 		return err
 	}
 	hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
+	if err := c.config.writeKeyLog(hs.clientHello.random, hs.masterSecret); err != nil {
+		c.sendAlert(alertInternalError)
+		return err
+	}
 
 	// If we received a client cert in response to our certificate request message,
 	// the client will send us a certificateVerifyMsg immediately after the
@@ -730,6 +741,13 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (c
 		c.verifiedChains = chains
 	}
 
+	if c.config.VerifyPeerCertificate != nil {
+		if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
+			c.sendAlert(alertBadCertificate)
+			return nil, err
+		}
+	}
+
 	if len(certs) == 0 {
 		return nil, nil
 	}
@@ -788,3 +806,37 @@ func (hs *serverHandshakeState) setCipherSuite(id uint16, supportedCipherSuites
 	}
 	return false
 }
+
+// suppVersArray is the backing array of ClientHelloInfo.SupportedVersions
+var suppVersArray = [...]uint16{VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30}
+
+func (hs *serverHandshakeState) clientHelloInfo() *ClientHelloInfo {
+	if hs.cachedClientHelloInfo != nil {
+		return hs.cachedClientHelloInfo
+	}
+
+	var supportedVersions []uint16
+	if hs.clientHello.vers > VersionTLS12 {
+		supportedVersions = suppVersArray[:]
+	} else if hs.clientHello.vers >= VersionSSL30 {
+		supportedVersions = suppVersArray[VersionTLS12-hs.clientHello.vers:]
+	}
+
+	signatureSchemes := make([]SignatureScheme, 0, len(hs.clientHello.signatureAndHashes))
+	for _, sah := range hs.clientHello.signatureAndHashes {
+		signatureSchemes = append(signatureSchemes, SignatureScheme(sah.hash)<<8+SignatureScheme(sah.signature))
+	}
+
+	hs.cachedClientHelloInfo = &ClientHelloInfo{
+		CipherSuites:      hs.clientHello.cipherSuites,
+		ServerName:        hs.clientHello.serverName,
+		SupportedCurves:   hs.clientHello.supportedCurves,
+		SupportedPoints:   hs.clientHello.supportedPoints,
+		SignatureSchemes:  signatureSchemes,
+		SupportedProtos:   hs.clientHello.alpnProtocols,
+		SupportedVersions: supportedVersions,
+		Conn:              hs.c.conn,
+	}
+
+	return hs.cachedClientHelloInfo
+}
diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go
index 9ae5d11..bcd3d43 100644
--- a/src/crypto/tls/handshake_server_test.go
+++ b/src/crypto/tls/handshake_server_test.go
@@ -130,7 +130,7 @@ func TestNoRC4ByDefault(t *testing.T) {
 		cipherSuites:       []uint16{TLS_RSA_WITH_RC4_128_SHA},
 		compressionMethods: []uint8{compressionNone},
 	}
-	serverConfig := testConfig.clone()
+	serverConfig := testConfig.Clone()
 	// Reset the enabled cipher suites to nil in order to test the
 	// defaults.
 	serverConfig.CipherSuites = nil
@@ -147,7 +147,7 @@ func TestDontSelectECDSAWithRSAKey(t *testing.T) {
 		supportedCurves:    []CurveID{CurveP256},
 		supportedPoints:    []uint8{pointFormatUncompressed},
 	}
-	serverConfig := testConfig.clone()
+	serverConfig := testConfig.Clone()
 	serverConfig.CipherSuites = clientHello.cipherSuites
 	serverConfig.Certificates = make([]Certificate, 1)
 	serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate}
@@ -172,7 +172,7 @@ func TestDontSelectRSAWithECDSAKey(t *testing.T) {
 		supportedCurves:    []CurveID{CurveP256},
 		supportedPoints:    []uint8{pointFormatUncompressed},
 	}
-	serverConfig := testConfig.clone()
+	serverConfig := testConfig.Clone()
 	serverConfig.CipherSuites = clientHello.cipherSuites
 	// First test that it *does* work when the server's key is RSA.
 	testClientHello(t, serverConfig, clientHello)
@@ -206,7 +206,8 @@ func TestRenegotiationExtension(t *testing.T) {
 		buf = make([]byte, 1024)
 		n, err := c.Read(buf)
 		if err != nil {
-			t.Fatalf("Server read returned error: %s", err)
+			t.Errorf("Server read returned error: %s", err)
+			return
 		}
 		buf = buf[:n]
 		c.Close()
@@ -265,7 +266,7 @@ func TestTLS12OnlyCipherSuites(t *testing.T) {
 		reply, clientErr = cli.readHandshake()
 		c.Close()
 	}()
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.CipherSuites = clientHello.cipherSuites
 	Server(s, config).Handshake()
 	s.Close()
@@ -558,6 +559,8 @@ func (test *serverTest) loadData() (flows [][]byte, err error) {
 }
 
 func (test *serverTest) run(t *testing.T, write bool) {
+	checkOpenSSLVersion(t)
+
 	var clientConn, serverConn net.Conn
 	var recordingConn *recordingConn
 	var childProcess *exec.Cmd
@@ -658,6 +661,7 @@ func (test *serverTest) run(t *testing.T, write bool) {
 }
 
 func runServerTestForVersion(t *testing.T, template *serverTest, prefix, option string) {
+	setParallel(t)
 	test := *template
 	test.name = prefix + test.name
 	if len(test.command) == 0 {
@@ -732,7 +736,7 @@ func TestHandshakeServerAES256GCMSHA384(t *testing.T) {
 }
 
 func TestHandshakeServerECDHEECDSAAES(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.Certificates = make([]Certificate, 1)
 	config.Certificates[0].Certificate = [][]byte{testECDSACertificate}
 	config.Certificates[0].PrivateKey = testECDSAPrivateKey
@@ -747,8 +751,20 @@ func TestHandshakeServerECDHEECDSAAES(t *testing.T) {
 	runServerTestTLS12(t, test)
 }
 
+func TestHandshakeServerX25519(t *testing.T) {
+	config := testConfig.Clone()
+	config.CurvePreferences = []CurveID{X25519}
+
+	test := &serverTest{
+		name:    "X25519-ECDHE-RSA-AES-GCM",
+		command: []string{"openssl", "s_client", "-no_ticket", "-cipher", "ECDHE-RSA-AES128-GCM-SHA256"},
+		config:  config,
+	}
+	runServerTestTLS12(t, test)
+}
+
 func TestHandshakeServerALPN(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.NextProtos = []string{"proto1", "proto2"}
 
 	test := &serverTest{
@@ -769,7 +785,7 @@ func TestHandshakeServerALPN(t *testing.T) {
 }
 
 func TestHandshakeServerALPNNoMatch(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.NextProtos = []string{"proto3"}
 
 	test := &serverTest{
@@ -804,7 +820,7 @@ func TestHandshakeServerSNI(t *testing.T) {
 // TestHandshakeServerSNICertForName is similar to TestHandshakeServerSNI, but
 // tests the dynamic GetCertificate method
 func TestHandshakeServerSNIGetCertificate(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 
 	// Replace the NameToCertificate map with a GetCertificate function
 	nameToCert := config.NameToCertificate
@@ -826,7 +842,7 @@ func TestHandshakeServerSNIGetCertificate(t *testing.T) {
 // GetCertificate method doesn't return a cert, we fall back to what's in
 // the NameToCertificate map.
 func TestHandshakeServerSNIGetCertificateNotFound(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 
 	config.GetCertificate = func(clientHello *ClientHelloInfo) (*Certificate, error) {
 		return nil, nil
@@ -844,7 +860,7 @@ func TestHandshakeServerSNIGetCertificateNotFound(t *testing.T) {
 func TestHandshakeServerSNIGetCertificateError(t *testing.T) {
 	const errMsg = "TestHandshakeServerSNIGetCertificateError error"
 
-	serverConfig := testConfig.clone()
+	serverConfig := testConfig.Clone()
 	serverConfig.GetCertificate = func(clientHello *ClientHelloInfo) (*Certificate, error) {
 		return nil, errors.New(errMsg)
 	}
@@ -863,7 +879,7 @@ func TestHandshakeServerSNIGetCertificateError(t *testing.T) {
 func TestHandshakeServerEmptyCertificates(t *testing.T) {
 	const errMsg = "TestHandshakeServerEmptyCertificates error"
 
-	serverConfig := testConfig.clone()
+	serverConfig := testConfig.Clone()
 	serverConfig.GetCertificate = func(clientHello *ClientHelloInfo) (*Certificate, error) {
 		return nil, errors.New(errMsg)
 	}
@@ -891,7 +907,7 @@ func TestHandshakeServerEmptyCertificates(t *testing.T) {
 // TestCipherSuiteCertPreferance ensures that we select an RSA ciphersuite with
 // an RSA certificate and an ECDSA ciphersuite with an ECDSA certificate.
 func TestCipherSuiteCertPreferenceECDSA(t *testing.T) {
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}
 	config.PreferServerCipherSuites = true
 
@@ -901,7 +917,7 @@ func TestCipherSuiteCertPreferenceECDSA(t *testing.T) {
 	}
 	runServerTestTLS12(t, test)
 
-	config = testConfig.clone()
+	config = testConfig.Clone()
 	config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}
 	config.Certificates = []Certificate{
 		{
@@ -925,13 +941,13 @@ func TestResumption(t *testing.T) {
 
 	test := &serverTest{
 		name:    "IssueTicket",
-		command: []string{"openssl", "s_client", "-cipher", "RC4-SHA", "-sess_out", sessionFilePath},
+		command: []string{"openssl", "s_client", "-cipher", "AES128-SHA", "-sess_out", sessionFilePath},
 	}
 	runServerTestTLS12(t, test)
 
 	test = &serverTest{
 		name:    "Resume",
-		command: []string{"openssl", "s_client", "-cipher", "RC4-SHA", "-sess_in", sessionFilePath},
+		command: []string{"openssl", "s_client", "-cipher", "AES128-SHA", "-sess_in", sessionFilePath},
 	}
 	runServerTestTLS12(t, test)
 }
@@ -940,11 +956,11 @@ func TestResumptionDisabled(t *testing.T) {
 	sessionFilePath := tempFile("")
 	defer os.Remove(sessionFilePath)
 
-	config := testConfig.clone()
+	config := testConfig.Clone()
 
 	test := &serverTest{
 		name:    "IssueTicketPreDisable",
-		command: []string{"openssl", "s_client", "-cipher", "RC4-SHA", "-sess_out", sessionFilePath},
+		command: []string{"openssl", "s_client", "-cipher", "AES128-SHA", "-sess_out", sessionFilePath},
 		config:  config,
 	}
 	runServerTestTLS12(t, test)
@@ -953,7 +969,7 @@ func TestResumptionDisabled(t *testing.T) {
 
 	test = &serverTest{
 		name:    "ResumeDisabled",
-		command: []string{"openssl", "s_client", "-cipher", "RC4-SHA", "-sess_in", sessionFilePath},
+		command: []string{"openssl", "s_client", "-cipher", "AES128-SHA", "-sess_in", sessionFilePath},
 		config:  config,
 	}
 	runServerTestTLS12(t, test)
@@ -976,40 +992,40 @@ func TestFallbackSCSV(t *testing.T) {
 	runServerTestTLS11(t, test)
 }
 
-// cert.pem and key.pem were generated with generate_cert.go
-// Thus, they have no ExtKeyUsage fields and trigger an error
-// when verification is turned on.
+// clientCertificatePEM and clientKeyPEM were generated with generate_cert.go
+// Thus, they have no ExtKeyUsage fields and trigger an error when verification
+// is turned on.
 
 const clientCertificatePEM = `
 -----BEGIN CERTIFICATE-----
-MIIB7TCCAVigAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD
-bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTExMTIwODA3NTUxMloXDTEyMTIwNzA4
-MDAxMlowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGc
-MAsGCSqGSIb3DQEBAQOBjAAwgYgCgYBO0Hsx44Jk2VnAwoekXh6LczPHY1PfZpIG
-hPZk1Y/kNqcdK+izIDZFI7Xjla7t4PUgnI2V339aEu+H5Fto5OkOdOwEin/ekyfE
-ARl6vfLcPRSr0FTKIQzQTW6HLlzF0rtNS0/Otiz3fojsfNcCkXSmHgwa2uNKWi7e
-E5xMQIhZkwIDAQABozIwMDAOBgNVHQ8BAf8EBAMCAKAwDQYDVR0OBAYEBAECAwQw
-DwYDVR0jBAgwBoAEAQIDBDALBgkqhkiG9w0BAQUDgYEANh+zegx1yW43RmEr1b3A
-p0vMRpqBWHyFeSnIyMZn3TJWRSt1tukkqVCavh9a+hoV2cxVlXIWg7nCto/9iIw4
-hB2rXZIxE0/9gzvGnfERYraL7KtnvshksBFQRlgXa5kc0x38BvEO5ZaoDPl4ILdE
-GFGNEH5PlGffo05wc46QkYU=
+MIIB7zCCAVigAwIBAgIQXBnBiWWDVW/cC8m5k5/pvDANBgkqhkiG9w0BAQsFADAS
+MRAwDgYDVQQKEwdBY21lIENvMB4XDTE2MDgxNzIxNTIzMVoXDTE3MDgxNzIxNTIz
+MVowEjEQMA4GA1UEChMHQWNtZSBDbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEAum+qhr3Pv5/y71yUYHhv6BPy0ZZvzdkybiI3zkH5yl0prOEn2mGi7oHLEMff
+NFiVhuk9GeZcJ3NgyI14AvQdpJgJoxlwaTwlYmYqqyIjxXuFOE8uCXMyp70+m63K
+hAfmDzr/d8WdQYUAirab7rCkPy1MTOZCPrtRyN1IVPQMjkcCAwEAAaNGMEQwDgYD
+VR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAw
+DwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOBgQBGq0Si+yhU+Fpn+GKU
+8ZqyGJ7ysd4dfm92lam6512oFmyc9wnTN+RLKzZ8Aa1B0jLYw9KT+RBrjpW5LBeK
+o0RIvFkTgxYEiKSBXCUNmAysEbEoVr4dzWFihAm/1oDGRY2CLLTYg5vbySK3KhIR
+e/oCO8HJ/+rJnahJ05XX1Q7lNQ==
 -----END CERTIFICATE-----`
 
 const clientKeyPEM = `
 -----BEGIN RSA PRIVATE KEY-----
-MIICWgIBAAKBgE7QezHjgmTZWcDCh6ReHotzM8djU99mkgaE9mTVj+Q2px0r6LMg
-NkUjteOVru3g9SCcjZXff1oS74fkW2jk6Q507ASKf96TJ8QBGXq98tw9FKvQVMoh
-DNBNbocuXMXSu01LT862LPd+iOx81wKRdKYeDBra40paLt4TnExAiFmTAgMBAAEC
-gYBxvXd8yNteFTns8A/2yomEMC4yeosJJSpp1CsN3BJ7g8/qTnrVPxBy+RU+qr63
-t2WquaOu/cr5P8iEsa6lk20tf8pjKLNXeX0b1RTzK8rJLbS7nGzP3tvOhL096VtQ
-dAo4ROEaro0TzYpHmpciSvxVIeEIAAdFDObDJPKqcJAxyQJBAJizfYgK8Gzx9fsx
-hxp+VteCbVPg2euASH5Yv3K5LukRdKoSzHE2grUVQgN/LafC0eZibRanxHegYSr7
-7qaswKUCQQCEIWor/X4XTMdVj3Oj+vpiw75y/S9gh682+myZL+d/02IEkwnB098P
-RkKVpenBHyrGg0oeN5La7URILWKj7CPXAkBKo6F+d+phNjwIFoN1Xb/RA32w/D1I
-saG9sF+UEhRt9AxUfW/U/tIQ9V0ZHHcSg1XaCM5Nvp934brdKdvTOKnJAkBD5h/3
-Rybatlvg/fzBEaJFyq09zhngkxlZOUtBVTqzl17RVvY2orgH02U4HbCHy4phxOn7
-qTdQRYlHRftgnWK1AkANibn9PRYJ7mJyJ9Dyj2QeNcSkSTzrt0tPvUMf4+meJymN
-1Ntu5+S1DLLzfxlaljWG6ylW6DNxujCyuXIV2rvA
+MIICXQIBAAKBgQC6b6qGvc+/n/LvXJRgeG/oE/LRlm/N2TJuIjfOQfnKXSms4Sfa
+YaLugcsQx980WJWG6T0Z5lwnc2DIjXgC9B2kmAmjGXBpPCViZiqrIiPFe4U4Ty4J
+czKnvT6brcqEB+YPOv93xZ1BhQCKtpvusKQ/LUxM5kI+u1HI3UhU9AyORwIDAQAB
+AoGAEJZ03q4uuMb7b26WSQsOMeDsftdatT747LGgs3pNRkMJvTb/O7/qJjxoG+Mc
+qeSj0TAZXp+PXXc3ikCECAc+R8rVMfWdmp903XgO/qYtmZGCorxAHEmR80SrfMXv
+PJnznLQWc8U9nphQErR+tTESg7xWEzmFcPKwnZd1xg8ERYkCQQDTGtrFczlB2b/Z
+9TjNMqUlMnTLIk/a/rPE2fLLmAYhK5sHnJdvDURaH2mF4nso0EGtENnTsh6LATnY
+dkrxXGm9AkEA4hXHG2q3MnhgK1Z5hjv+Fnqd+8bcbII9WW4flFs15EKoMgS1w/PJ
+zbsySaSy5IVS8XeShmT9+3lrleed4sy+UwJBAJOOAbxhfXP5r4+5R6ql66jES75w
+jUCVJzJA5ORJrn8g64u2eGK28z/LFQbv9wXgCwfc72R468BdawFSLa/m2EECQGbZ
+rWiFla26IVXV0xcD98VWJsTBZMlgPnSOqoMdM1kSEd4fUmlAYI/dFzV1XYSkOmVr
+FhdZnklmpVDeu27P4c0CQQCuCOup0FlJSBpWY1TTfun/KMBkBatMz0VMA3d7FKIU
+csPezl677Yjo8u1r/KzeI6zLg87Z8E6r6ZWNc9wBSZK6
 -----END RSA PRIVATE KEY-----`
 
 const clientECDSACertificatePEM = `
@@ -1040,6 +1056,7 @@ FMBexFe01MNvja5oHt1vzobhfm6ySD6B5U7ixohLZNz1MLvT/2XMW/TdtWo+PtAd
 -----END EC PRIVATE KEY-----`
 
 func TestClientAuth(t *testing.T) {
+	setParallel(t)
 	var certPath, keyPath, ecdsaCertPath, ecdsaKeyPath string
 
 	if *update {
@@ -1053,19 +1070,19 @@ func TestClientAuth(t *testing.T) {
 		defer os.Remove(ecdsaKeyPath)
 	}
 
-	config := testConfig.clone()
+	config := testConfig.Clone()
 	config.ClientAuth = RequestClientCert
 
 	test := &serverTest{
 		name:    "ClientAuthRequestedNotGiven",
-		command: []string{"openssl", "s_client", "-no_ticket", "-cipher", "RC4-SHA"},
+		command: []string{"openssl", "s_client", "-no_ticket", "-cipher", "AES128-SHA"},
 		config:  config,
 	}
 	runServerTestTLS12(t, test)
 
 	test = &serverTest{
 		name:              "ClientAuthRequestedAndGiven",
-		command:           []string{"openssl", "s_client", "-no_ticket", "-cipher", "RC4-SHA", "-cert", certPath, "-key", keyPath},
+		command:           []string{"openssl", "s_client", "-no_ticket", "-cipher", "AES128-SHA", "-cert", certPath, "-key", keyPath},
 		config:            config,
 		expectedPeerCerts: []string{clientCertificatePEM},
 	}
@@ -1073,13 +1090,189 @@ func TestClientAuth(t *testing.T) {
 
 	test = &serverTest{
 		name:              "ClientAuthRequestedAndECDSAGiven",
-		command:           []string{"openssl", "s_client", "-no_ticket", "-cipher", "RC4-SHA", "-cert", ecdsaCertPath, "-key", ecdsaKeyPath},
+		command:           []string{"openssl", "s_client", "-no_ticket", "-cipher", "AES128-SHA", "-cert", ecdsaCertPath, "-key", ecdsaKeyPath},
 		config:            config,
 		expectedPeerCerts: []string{clientECDSACertificatePEM},
 	}
 	runServerTestTLS12(t, test)
 }
 
+func TestSNIGivenOnFailure(t *testing.T) {
+	const expectedServerName = "test.testing"
+
+	clientHello := &clientHelloMsg{
+		vers:               VersionTLS10,
+		cipherSuites:       []uint16{TLS_RSA_WITH_RC4_128_SHA},
+		compressionMethods: []uint8{compressionNone},
+		serverName:         expectedServerName,
+	}
+
+	serverConfig := testConfig.Clone()
+	// Erase the server's cipher suites to ensure the handshake fails.
+	serverConfig.CipherSuites = nil
+
+	c, s := net.Pipe()
+	go func() {
+		cli := Client(c, testConfig)
+		cli.vers = clientHello.vers
+		cli.writeRecord(recordTypeHandshake, clientHello.marshal())
+		c.Close()
+	}()
+	hs := serverHandshakeState{
+		c: Server(s, serverConfig),
+	}
+	_, err := hs.readClientHello()
+	defer s.Close()
+
+	if err == nil {
+		t.Error("No error reported from server")
+	}
+
+	cs := hs.c.ConnectionState()
+	if cs.HandshakeComplete {
+		t.Error("Handshake registered as complete")
+	}
+
+	if cs.ServerName != expectedServerName {
+		t.Errorf("Expected ServerName of %q, but got %q", expectedServerName, cs.ServerName)
+	}
+}
+
+var getConfigForClientTests = []struct {
+	setup          func(config *Config)
+	callback       func(clientHello *ClientHelloInfo) (*Config, error)
+	errorSubstring string
+	verify         func(config *Config) error
+}{
+	{
+		nil,
+		func(clientHello *ClientHelloInfo) (*Config, error) {
+			return nil, nil
+		},
+		"",
+		nil,
+	},
+	{
+		nil,
+		func(clientHello *ClientHelloInfo) (*Config, error) {
+			return nil, errors.New("should bubble up")
+		},
+		"should bubble up",
+		nil,
+	},
+	{
+		nil,
+		func(clientHello *ClientHelloInfo) (*Config, error) {
+			config := testConfig.Clone()
+			// Setting a maximum version of TLS 1.1 should cause
+			// the handshake to fail.
+			config.MaxVersion = VersionTLS11
+			return config, nil
+		},
+		"version 301 when expecting version 302",
+		nil,
+	},
+	{
+		func(config *Config) {
+			for i := range config.SessionTicketKey {
+				config.SessionTicketKey[i] = byte(i)
+			}
+			config.sessionTicketKeys = nil
+		},
+		func(clientHello *ClientHelloInfo) (*Config, error) {
+			config := testConfig.Clone()
+			for i := range config.SessionTicketKey {
+				config.SessionTicketKey[i] = 0
+			}
+			config.sessionTicketKeys = nil
+			return config, nil
+		},
+		"",
+		func(config *Config) error {
+			// The value of SessionTicketKey should have been
+			// duplicated into the per-connection Config.
+			for i := range config.SessionTicketKey {
+				if b := config.SessionTicketKey[i]; b != byte(i) {
+					return fmt.Errorf("SessionTicketKey was not duplicated from original Config: byte %d has value %d", i, b)
+				}
+			}
+			return nil
+		},
+	},
+	{
+		func(config *Config) {
+			var dummyKey [32]byte
+			for i := range dummyKey {
+				dummyKey[i] = byte(i)
+			}
+
+			config.SetSessionTicketKeys([][32]byte{dummyKey})
+		},
+		func(clientHello *ClientHelloInfo) (*Config, error) {
+			config := testConfig.Clone()
+			config.sessionTicketKeys = nil
+			return config, nil
+		},
+		"",
+		func(config *Config) error {
+			// The session ticket keys should have been duplicated
+			// into the per-connection Config.
+			if l := len(config.sessionTicketKeys); l != 1 {
+				return fmt.Errorf("got len(sessionTicketKeys) == %d, wanted 1", l)
+			}
+			return nil
+		},
+	},
+}
+
+func TestGetConfigForClient(t *testing.T) {
+	serverConfig := testConfig.Clone()
+	clientConfig := testConfig.Clone()
+	clientConfig.MinVersion = VersionTLS12
+
+	for i, test := range getConfigForClientTests {
+		if test.setup != nil {
+			test.setup(serverConfig)
+		}
+
+		var configReturned *Config
+		serverConfig.GetConfigForClient = func(clientHello *ClientHelloInfo) (*Config, error) {
+			config, err := test.callback(clientHello)
+			configReturned = config
+			return config, err
+		}
+		c, s := net.Pipe()
+		done := make(chan error)
+
+		go func() {
+			defer s.Close()
+			done <- Server(s, serverConfig).Handshake()
+		}()
+
+		clientErr := Client(c, clientConfig).Handshake()
+		c.Close()
+
+		serverErr := <-done
+
+		if len(test.errorSubstring) == 0 {
+			if serverErr != nil || clientErr != nil {
+				t.Errorf("test[%d]: expected no error but got serverErr: %q, clientErr: %q", i, serverErr, clientErr)
+			}
+			if test.verify != nil {
+				if err := test.verify(configReturned); err != nil {
+					t.Errorf("test[%d]: verify returned error: %v", i, err)
+				}
+			}
+		} else {
+			if serverErr == nil {
+				t.Errorf("test[%d]: expected error containing %q but got no error", i, test.errorSubstring)
+			} else if !strings.Contains(serverErr.Error(), test.errorSubstring) {
+				t.Errorf("test[%d]: expected error to contain %q but it was %q", i, test.errorSubstring, serverErr)
+			}
+		}
+	}
+}
+
 func bigFromString(s string) *big.Int {
 	ret := new(big.Int)
 	ret.SetString(s, 10)
@@ -1091,23 +1284,23 @@ func fromHex(s string) []byte {
 	return b
 }
 
-var testRSACertificate = fromHex("30820263308201cca003020102020900a273000c8100cbf3300d06092a864886f70d01010b0500302b31173015060355040a130e476f6f676c652054455354494e473110300e06035504031307476f20526f6f74301e170d3135303130313030303030305a170d3235303130313030303030305a302631173015060355040a130e476f6f676c652054455354494e47310b300906035504031302476f30819f300d06092a864886f70d010101050003818d0030818902818100af8788f6201b95656c14ab4405af3b4514e3b76dfd00634d957ffe6a623586c04af9187cf6aa255e7a643166 [...]
+var testRSACertificate = fromHex("3082024b308201b4a003020102020900e8f09d3fe25beaa6300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301a310b3009060355040a1302476f310b300906035504031302476f30819f300d06092a864886f70d010101050003818d0030818902818100db467d932e12270648bc062821ab7ec4b6a25dfe1e5245887a3647a5080d92425bc281c0be97799840fb4f6d14fd2b138bc2a52e67d8d4099ed62238b74a0b74732bc234 [...]
 
-var testRSACertificateIssuer = fromHex("3082024d308201b6a003020102020827326bd913b7c43d300d06092a864886f70d01010b0500302b31173015060355040a130e476f6f676c652054455354494e473110300e06035504031307476f20526f6f74301e170d3135303130313030303030305a170d3235303130313030303030305a302b31173015060355040a130e476f6f676c652054455354494e473110300e06035504031307476f20526f6f7430819f300d06092a864886f70d010101050003818d0030818902818100f0429a7b9f66a222c8453800452db355b34c4409fee09af2510a6589bfa35bdb4d453200d1 [...]
+var testRSACertificateIssuer = fromHex("3082021930820182a003020102020900ca5e4e811a965964300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f7430819f300d06092a864886f70d010101050003818d0030818902818100d667b378bb22f34143b6cd2008236abefaf2852adf3ab05e01329e2c14834f5105df3f3073f99dab5442d45ee5f8f57b0111c8cb682fbb719a86944e [...]
 
 var testECDSACertificate = fromHex("3082020030820162020900b8bf2d47a0d2ebf4300906072a8648ce3d04013045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3132313132323135303633325a170d3232313132303135303633325a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819b301006072a8648ce3d020106052b81040023038186 [...]
 
-var testSNICertificate = fromHex("308201f23082015da003020102020100300b06092a864886f70d01010530283110300e060355040a130741636d6520436f311430120603550403130b736e69746573742e636f6d301e170d3132303431313137343033355a170d3133303431313137343533355a30283110300e060355040a130741636d6520436f311430120603550403130b736e69746573742e636f6d30819d300b06092a864886f70d01010103818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a [...]
+var testSNICertificate = fromHex("0441883421114c81480804c430820237308201a0a003020102020900e8f09d3fe25beaa6300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a3023310b3009060355040a1302476f311430120603550403130b736e69746573742e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100db467d932e12270648bc062821ab7ec4b6a25dfe1e5245887a3647a5080d92425bc281c0be97799840fb4f6d14fd2b [...]
 
 var testRSAPrivateKey = &rsa.PrivateKey{
 	PublicKey: rsa.PublicKey{
-		N: bigFromString("123260960069105588390096594560395120585636206567569540256061833976822892593755073841963170165000086278069699238754008398039246547214989242849418349143232951701395321381739566687846006911427966669790845430647688107009232778985142860108863460556510585049041936029324503323373417214453307648498561956908810892027L"),
+		N: bigFromString("153980389784927331788354528594524332344709972855165340650588877572729725338415474372475094155672066328274535240275856844648695200875763869073572078279316458648124537905600131008790701752441155668003033945258023841165089852359980273279085783159654751552359397986180318708491098942831252291841441726305535546071"),
 		E: 65537,
 	},
-	D: bigFromString("73196363031103823625826315929954946106043759818067219550565550066527203472294428548476778865091068522665312037075674791871635825938217363523103946045078950060973913307430314113074463630778799389010335923241901501086246276485964417618981733827707048660375428006201525399194575538037883519254056917253456403553L"),
+	D: bigFromString("7746362285745539358014631136245887418412633787074173796862711588221766398229333338511838891484974940633857861775630560092874987828057333663969469797013996401149696897591265769095952887917296740109742927689053276850469671231961384712725169432413343763989564437170644270643461665184965150423819594083121075825"),
 	Primes: []*big.Int{
-		bigFromString("11157426355495284553529769521954035649776033703833034489026848970480272318436419662860715175517581249375929775774910501512841707465207184924996975125010787L"),
-		bigFromString("11047436580963564307160117670964629323534448585520694947919342920137706075617545637058809770319843170934495909554506529982972972247390145716507031692656521L"),
+		bigFromString("13299275414352936908236095374926261633419699590839189494995965049151460173257838079863316944311313904000258169883815802963543635820059341150014695560313417"),
+		bigFromString("11578103692682951732111718237224894755352163854919244905974423810539077224889290605729035287537520656160688625383765857517518932447378594964220731750802463"),
 	},
 }
 
diff --git a/src/crypto/tls/handshake_test.go b/src/crypto/tls/handshake_test.go
index f95f274..8e5410a 100644
--- a/src/crypto/tls/handshake_test.go
+++ b/src/crypto/tls/handshake_test.go
@@ -13,9 +13,11 @@ import (
 	"io"
 	"io/ioutil"
 	"net"
+	"os/exec"
 	"strconv"
 	"strings"
 	"sync"
+	"testing"
 )
 
 // TLS reference tests run a connection against a reference implementation
@@ -35,7 +37,52 @@ import (
 // generate fresh random numbers, large parts of the reference connection will
 // always change.
 
-var update = flag.Bool("update", false, "update golden files on disk")
+var (
+	update = flag.Bool("update", false, "update golden files on disk")
+
+	opensslVersionTestOnce sync.Once
+	opensslVersionTestErr  error
+)
+
+func checkOpenSSLVersion(t *testing.T) {
+	opensslVersionTestOnce.Do(testOpenSSLVersion)
+	if opensslVersionTestErr != nil {
+		t.Fatal(opensslVersionTestErr)
+	}
+}
+
+func testOpenSSLVersion() {
+	// This test ensures that the version of OpenSSL looks reasonable
+	// before updating the test data.
+
+	if !*update {
+		return
+	}
+
+	openssl := exec.Command("openssl", "version")
+	output, err := openssl.CombinedOutput()
+	if err != nil {
+		opensslVersionTestErr = err
+		return
+	}
+
+	version := string(output)
+	if strings.HasPrefix(version, "OpenSSL 1.1.0") {
+		return
+	}
+
+	println("***********************************************")
+	println("")
+	println("You need to build OpenSSL 1.1.0 from source in order")
+	println("to update the test data.")
+	println("")
+	println("Configure it with:")
+	println("./Configure enable-weak-ssl-ciphers enable-ssl3 enable-ssl3-method -static linux-x86_64")
+	println("and then add the apps/ directory at the front of your PATH.")
+	println("***********************************************")
+
+	opensslVersionTestErr = errors.New("version of OpenSSL does not appear to be suitable for updating test data")
+}
 
 // recordingConn is a net.Conn that records the traffic that passes through it.
 // WriteTo can be used to produce output that can be later be loaded with
@@ -88,21 +135,33 @@ func (r *recordingConn) Write(b []byte) (n int, err error) {
 }
 
 // WriteTo writes Go source code to w that contains the recorded traffic.
-func (r *recordingConn) WriteTo(w io.Writer) {
+func (r *recordingConn) WriteTo(w io.Writer) (int64, error) {
 	// TLS always starts with a client to server flow.
 	clientToServer := true
-
+	var written int64
 	for i, flow := range r.flows {
 		source, dest := "client", "server"
 		if !clientToServer {
 			source, dest = dest, source
 		}
-		fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest)
+		n, err := fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest)
+		written += int64(n)
+		if err != nil {
+			return written, err
+		}
 		dumper := hex.Dumper(w)
-		dumper.Write(flow)
-		dumper.Close()
+		n, err = dumper.Write(flow)
+		written += int64(n)
+		if err != nil {
+			return written, err
+		}
+		err = dumper.Close()
+		if err != nil {
+			return written, err
+		}
 		clientToServer = !clientToServer
 	}
+	return written, nil
 }
 
 func parseTestData(r io.Reader) (flows [][]byte, err error) {
diff --git a/src/crypto/tls/key_agreement.go b/src/crypto/tls/key_agreement.go
index 467efb2..1b27c04 100644
--- a/src/crypto/tls/key_agreement.go
+++ b/src/crypto/tls/key_agreement.go
@@ -16,6 +16,8 @@ import (
 	"errors"
 	"io"
 	"math/big"
+
+	"golang_org/x/crypto/curve25519"
 )
 
 var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
@@ -177,52 +179,71 @@ type ecdheKeyAgreement struct {
 	version    uint16
 	sigType    uint8
 	privateKey []byte
-	curve      elliptic.Curve
-	x, y       *big.Int
+	curveid    CurveID
+
+	// publicKey is used to store the peer's public value when X25519 is
+	// being used.
+	publicKey []byte
+	// x and y are used to store the peer's public value when one of the
+	// NIST curves is being used.
+	x, y *big.Int
 }
 
 func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
-	var curveid CurveID
 	preferredCurves := config.curvePreferences()
 
 NextCandidate:
 	for _, candidate := range preferredCurves {
 		for _, c := range clientHello.supportedCurves {
 			if candidate == c {
-				curveid = c
+				ka.curveid = c
 				break NextCandidate
 			}
 		}
 	}
 
-	if curveid == 0 {
+	if ka.curveid == 0 {
 		return nil, errors.New("tls: no supported elliptic curves offered")
 	}
 
-	var ok bool
-	if ka.curve, ok = curveForCurveID(curveid); !ok {
-		return nil, errors.New("tls: preferredCurves includes unsupported curve")
-	}
+	var ecdhePublic []byte
 
-	var x, y *big.Int
-	var err error
-	ka.privateKey, x, y, err = elliptic.GenerateKey(ka.curve, config.rand())
-	if err != nil {
-		return nil, err
+	if ka.curveid == X25519 {
+		var scalar, public [32]byte
+		if _, err := io.ReadFull(config.rand(), scalar[:]); err != nil {
+			return nil, err
+		}
+
+		curve25519.ScalarBaseMult(&public, &scalar)
+		ka.privateKey = scalar[:]
+		ecdhePublic = public[:]
+	} else {
+		curve, ok := curveForCurveID(ka.curveid)
+		if !ok {
+			return nil, errors.New("tls: preferredCurves includes unsupported curve")
+		}
+
+		var x, y *big.Int
+		var err error
+		ka.privateKey, x, y, err = elliptic.GenerateKey(curve, config.rand())
+		if err != nil {
+			return nil, err
+		}
+		ecdhePublic = elliptic.Marshal(curve, x, y)
 	}
-	ecdhePublic := elliptic.Marshal(ka.curve, x, y)
 
 	// http://tools.ietf.org/html/rfc4492#section-5.4
 	serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic))
 	serverECDHParams[0] = 3 // named curve
-	serverECDHParams[1] = byte(curveid >> 8)
-	serverECDHParams[2] = byte(curveid)
+	serverECDHParams[1] = byte(ka.curveid >> 8)
+	serverECDHParams[2] = byte(ka.curveid)
 	serverECDHParams[3] = byte(len(ecdhePublic))
 	copy(serverECDHParams[4:], ecdhePublic)
 
 	sigAndHash := signatureAndHash{signature: ka.sigType}
 
 	if ka.version >= VersionTLS12 {
+		var err error
 		if sigAndHash.hash, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes); err != nil {
 			return nil, err
 		}
@@ -281,15 +302,32 @@ func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Cert
 	if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
 		return nil, errClientKeyExchange
 	}
-	x, y := elliptic.Unmarshal(ka.curve, ckx.ciphertext[1:])
+
+	if ka.curveid == X25519 {
+		if len(ckx.ciphertext) != 1+32 {
+			return nil, errClientKeyExchange
+		}
+
+		var theirPublic, sharedKey, scalar [32]byte
+		copy(theirPublic[:], ckx.ciphertext[1:])
+		copy(scalar[:], ka.privateKey)
+		curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic)
+		return sharedKey[:], nil
+	}
+
+	curve, ok := curveForCurveID(ka.curveid)
+	if !ok {
+		panic("internal error")
+	}
+	x, y := elliptic.Unmarshal(curve, ckx.ciphertext[1:])
 	if x == nil {
 		return nil, errClientKeyExchange
 	}
-	if !ka.curve.IsOnCurve(x, y) {
+	if !curve.IsOnCurve(x, y) {
 		return nil, errClientKeyExchange
 	}
-	x, _ = ka.curve.ScalarMult(x, y, ka.privateKey)
-	preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3)
+	x, _ = curve.ScalarMult(x, y, ka.privateKey)
+	preMasterSecret := make([]byte, (curve.Params().BitSize+7)>>3)
 	xBytes := x.Bytes()
 	copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
 
@@ -303,31 +341,40 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
 	if skx.key[0] != 3 { // named curve
 		return errors.New("tls: server selected unsupported curve")
 	}
-	curveid := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
-
-	var ok bool
-	if ka.curve, ok = curveForCurveID(curveid); !ok {
-		return errors.New("tls: server selected unsupported curve")
-	}
+	ka.curveid = CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
 
 	publicLen := int(skx.key[3])
 	if publicLen+4 > len(skx.key) {
 		return errServerKeyExchange
 	}
-	ka.x, ka.y = elliptic.Unmarshal(ka.curve, skx.key[4:4+publicLen])
-	if ka.x == nil {
-		return errServerKeyExchange
-	}
-	if !ka.curve.IsOnCurve(ka.x, ka.y) {
-		return errServerKeyExchange
-	}
 	serverECDHParams := skx.key[:4+publicLen]
+	publicKey := serverECDHParams[4:]
 
 	sig := skx.key[4+publicLen:]
 	if len(sig) < 2 {
 		return errServerKeyExchange
 	}
 
+	if ka.curveid == X25519 {
+		if len(publicKey) != 32 {
+			return errors.New("tls: bad X25519 public value")
+		}
+		ka.publicKey = publicKey
+	} else {
+		curve, ok := curveForCurveID(ka.curveid)
+		if !ok {
+			return errors.New("tls: server selected unsupported curve")
+		}
+
+		ka.x, ka.y = elliptic.Unmarshal(curve, publicKey)
+		if ka.x == nil {
+			return errServerKeyExchange
+		}
+		if !curve.IsOnCurve(ka.x, ka.y) {
+			return errServerKeyExchange
+		}
+	}
+
 	sigAndHash := signatureAndHash{signature: ka.sigType}
 	if ka.version >= VersionTLS12 {
 		// handle SignatureAndHashAlgorithm
@@ -382,19 +429,40 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
 }
 
 func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
-	if ka.curve == nil {
+	if ka.curveid == 0 {
 		return nil, nil, errors.New("tls: missing ServerKeyExchange message")
 	}
-	priv, mx, my, err := elliptic.GenerateKey(ka.curve, config.rand())
-	if err != nil {
-		return nil, nil, err
-	}
-	x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv)
-	preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3)
-	xBytes := x.Bytes()
-	copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
 
-	serialized := elliptic.Marshal(ka.curve, mx, my)
+	var serialized, preMasterSecret []byte
+
+	if ka.curveid == X25519 {
+		var ourPublic, theirPublic, sharedKey, scalar [32]byte
+
+		if _, err := io.ReadFull(config.rand(), scalar[:]); err != nil {
+			return nil, nil, err
+		}
+
+		copy(theirPublic[:], ka.publicKey)
+		curve25519.ScalarBaseMult(&ourPublic, &scalar)
+		curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic)
+		serialized = ourPublic[:]
+		preMasterSecret = sharedKey[:]
+	} else {
+		curve, ok := curveForCurveID(ka.curveid)
+		if !ok {
+			panic("internal error")
+		}
+		priv, mx, my, err := elliptic.GenerateKey(curve, config.rand())
+		if err != nil {
+			return nil, nil, err
+		}
+		x, _ := curve.ScalarMult(ka.x, ka.y, priv)
+		preMasterSecret = make([]byte, (curve.Params().BitSize+7)>>3)
+		xBytes := x.Bytes()
+		copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
+
+		serialized = elliptic.Marshal(curve, mx, my)
+	}
 
 	ckx := new(clientKeyExchangeMsg)
 	ckx.ciphertext = make([]byte, 1+len(serialized))
diff --git a/src/crypto/tls/testdata/Client-TLSv10-ClientCert-ECDSA-ECDSA b/src/crypto/tls/testdata/Client-TLSv10-ClientCert-ECDSA-ECDSA
index a62d27d..099cef4 100644
--- a/src/crypto/tls/testdata/Client-TLSv10-ClientCert-ECDSA-ECDSA
+++ b/src/crypto/tls/testdata/Client-TLSv10-ClientCert-ECDSA-ECDSA
@@ -1,19 +1,20 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 59 02 00 00  55 03 01 38 1a 94 8d 84  |....Y...U..8....|
-00000010  d7 a4 29 89 50 ad 07 97  5b c0 2c 7b 8c a6 75 0e  |..).P...[.,{..u.|
-00000020  97 51 62 10 07 87 c5 6f  0a 5f 86 20 1d ac 1d 05  |.Qb....o._. ....|
-00000030  ea 85 48 84 73 d9 07 8d  d0 81 56 99 81 10 7b 18  |..H.s.....V...{.|
-00000040  e8 5e da a9 fe cd f9 91  88 31 9b 6e c0 09 00 00  |.^.......1.n....|
+00000000  16 03 01 00 59 02 00 00  55 03 01 4f 5d 09 43 37  |....Y...U..O].C7|
+00000010  70 c6 d9 8b 07 ca 1a f0  fb a7 05 51 53 67 7a 7e  |p..........QSgz~|
+00000020  c9 c6 68 10 10 2a 69 bd  47 db 8e 20 f2 13 5b 26  |..h..*i.G.. ..[&|
+00000030  e6 8e 19 b0 bc b5 ee 1f  ca 44 5d 32 11 37 b0 78  |.........D]2.7.x|
+00000040  49 16 6e c2 44 86 52 3f  9f 05 15 aa c0 09 00 00  |I.n.D.R?........|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  01 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
@@ -48,21 +49,20 @@
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 01 00 d5 0c 00  00 d1 03 00 17 41 04 68  |*............A.h|
-00000280  37 18 3d 57 d2 5a 39 75  1e 7f 0a 3a 47 65 36 2e  |7.=W.Z9u...:Ge6.|
-00000290  6d cb 8f aa 0f 0d 45 5e  3f 14 82 f5 8c b1 11 0a  |m.....E^?.......|
-000002a0  8f e0 bc e4 07 d3 d5 bf  2d f4 82 ba cf c9 1c 88  |........-.......|
-000002b0  33 72 a8 49 39 48 40 74  c6 96 c3 30 72 31 34 00  |3r.I9H at t...0r14.|
-000002c0  8a 30 81 87 02 41 0e 43  2d 29 81 e9 c3 07 fc 5c  |.0...A.C-).....\|
-000002d0  ad c0 51 9e 0f cf c5 77  e4 bf 00 b6 66 f9 0e c6  |..Q....w....f...|
-000002e0  40 c6 b5 49 a4 04 05 31  2c 7c 1f 24 38 80 1b 3f  |@..I...1,|.$8..?|
-000002f0  16 5f c7 4d a8 7d 98 50  7f 7d 6d ed e9 19 1d 19  |._.M.}.P.}m.....|
-00000300  7b fd ec c5 4d 18 ab 02  42 01 00 db 37 b7 fa 39  |{...M...B...7..9|
-00000310  4b 3f 16 06 eb b8 4a 22  c6 de 00 d8 a7 eb a2 9e  |K?....J"........|
-00000320  e1 6f f4 a4 32 e2 ca d0  72 3a e5 f3 14 27 a0 dd  |.o..2...r:...'..|
-00000330  c4 26 34 b3 6c a3 d0 03  90 7a 2e 0e bf 0b 63 63  |.&4.l....z....cc|
-00000340  77 66 37 dd 1a 0f 7a 90  3f c8 a9 16 03 01 00 0e  |wf7...z.?.......|
-00000350  0d 00 00 06 03 01 02 40  00 00 0e 00 00 00        |....... at ......|
+00000270  2a 16 03 01 00 b5 0c 00  00 b1 03 00 1d 20 56 b4  |*............ V.|
+00000280  39 d4 8f 18 79 87 89 d0  04 ee 12 54 20 2b be c1  |9...y......T +..|
+00000290  94 99 40 a2 73 df 1e 92  66 0b d1 f1 d6 38 00 8b  |.. at .s...f....8..|
+000002a0  30 81 88 02 42 01 38 12  59 bd ea 44 59 f4 6f a9  |0...B.8.Y..DY.o.|
+000002b0  8e 9e a0 85 b5 b3 55 3e  76 49 b7 75 98 6e 81 30  |......U>vI.u.n.0|
+000002c0  c4 73 bd 54 78 39 f7 e2  22 49 4c 93 0d c1 26 89  |.s.Tx9.."IL...&.|
+000002d0  08 b9 9c 8b 86 3e 81 2c  a5 50 7c e9 88 ec c0 ad  |.....>.,.P|.....|
+000002e0  9e e0 40 ac 4e 0a fd 02  42 01 2e 0d 37 73 6a 0d  |.. at .N...B...7sj.|
+000002f0  a4 60 08 a0 2b 32 0f 87  8d f8 9b c7 68 cf 50 79  |.`..+2......h.Py|
+00000300  73 f7 cf 93 aa 75 57 20  58 3d 13 c0 f3 66 7d 59  |s....uW X=...f}Y|
+00000310  15 73 d4 29 03 34 df 33  00 c0 b5 71 bc 2a 90 ef  |.s.).4.3...q.*..|
+00000320  3c 02 5e ea 9d 29 93 1c  18 db 04 16 03 01 00 0a  |<.^..)..........|
+00000330  0d 00 00 06 03 01 02 40  00 00 16 03 01 00 04 0e  |....... at ........|
+00000340  00 00 00                                          |...|
 >>> Flow 3 (client to server)
 00000000  16 03 01 02 0a 0b 00 02  06 00 02 03 00 02 00 30  |...............0|
 00000010  82 01 fc 30 82 01 5e 02  09 00 9a 30 84 6c 26 35  |...0..^....0.l&5|
@@ -97,34 +97,32 @@
 000001e0  be e8 91 b3 da 1a f5 5d  a3 23 f5 26 8b 45 70 8d  |.......].#.&.Ep.|
 000001f0  65 62 9b 7e 01 99 3d 18  f6 10 9a 38 61 9b 2e 57  |eb.~..=....8a..W|
 00000200  e4 fa cc b1 8a ce e2 23  a0 87 f0 e1 67 51 eb 16  |.......#....gQ..|
-00000210  03 01 00 46 10 00 00 42  41 04 1e 18 37 ef 0d 19  |...F...BA...7...|
-00000220  51 88 35 75 71 b5 e5 54  5b 12 2e 8f 09 67 fd a7  |Q.5uq..T[....g..|
-00000230  24 20 3e b2 56 1c ce 97  28 5e f8 2b 2d 4f 9e f1  |$ >.V...(^.+-O..|
-00000240  07 9f 6c 4b 5b 83 56 e2  32 42 e9 58 b6 d7 49 a6  |..lK[.V.2B.X..I.|
-00000250  b5 68 1a 41 03 56 6b dc  5a 89 16 03 01 00 90 0f  |.h.A.Vk.Z.......|
-00000260  00 00 8c 00 8a 30 81 87  02 41 51 c5 53 a8 0f cb  |.....0...AQ.S...|
-00000270  18 79 4a 59 53 62 17 bb  29 39 fa cd 56 6c 5c 29  |.yJYSb..)9..Vl\)|
-00000280  1f e3 bc df fb 9a 29 fa  38 1a 73 aa 4c 79 6b 1c  |......).8.s.Lyk.|
-00000290  9f 1c 8e 95 c7 11 cc df  5d e9 c7 93 ce a3 9b e6  |........].......|
-000002a0  94 17 24 3a 8e f8 9a a9  46 01 f9 02 42 01 a1 df  |..$:....F...B...|
-000002b0  c5 cc fe 8d 5b 34 fb 89  2f f5 b3 3f 75 d7 19 1b  |....[4../..?u...|
-000002c0  5e 0f 1a 2e 8f 2d 62 61  73 85 2c 03 3b 22 07 2f  |^....-bas.,.;"./|
-000002d0  6b f3 5c fb ba b2 87 54  1c ef d2 f8 82 f3 9e f8  |k.\....T........|
-000002e0  ce 1b fa ce b0 6d d0 85  f8 62 6e d6 ba 93 cc 14  |.....m...bn.....|
-000002f0  03 01 00 01 01 16 03 01  00 30 76 90 a8 a2 8d 25  |.........0v....%|
-00000300  c5 c2 ff ef 2b 76 83 2c  7a 0d 44 37 99 67 02 d3  |....+v.,z.D7.g..|
-00000310  6e 3b 28 83 21 cf f5 6a  71 61 2d 5b 24 57 b2 19  |n;(.!..jqa-[$W..|
-00000320  63 d4 e5 96 0c 0c e1 f3  3a 99                    |c.......:.|
+00000210  03 01 00 25 10 00 00 21  20 2f e5 7d a3 47 cd 62  |...%...! /.}.G.b|
+00000220  43 15 28 da ac 5f bb 29  07 30 ff f6 84 af c4 cf  |C.(.._.).0......|
+00000230  c2 ed 90 99 5f 58 cb 3b  74 16 03 01 00 91 0f 00  |...._X.;t.......|
+00000240  00 8d 00 8b 30 81 88 02  42 01 53 2c a8 59 57 d2  |....0...B.S,.YW.|
+00000250  fc 0b 12 27 6f 9a f7 4e  a0 dd 2c af 1b 4c 81 0b  |...'o..N..,..L..|
+00000260  97 79 7e 6f dd a1 cf cb  e2 14 4d af 76 99 d8 06  |.y~o......M.v...|
+00000270  4f 8d 4f 86 d3 25 04 ea  80 02 ae 25 10 9d 2d 59  |O.O..%.....%..-Y|
+00000280  11 39 65 6b 83 d0 16 7d  bf a8 a4 02 42 01 f2 16  |.9ek...}....B...|
+00000290  6c f1 e6 3b b1 af fb 3f  99 f0 8a e3 c8 62 ba 71  |l..;...?.....b.q|
+000002a0  12 a1 2c 1e 15 74 d5 98  b5 ae 9f 50 a2 15 9b 73  |..,..t.....P...s|
+000002b0  9a 5f 2c 90 d4 9d 20 6f  35 b6 32 3e f4 b7 dd 50  |._,... o5.2>...P|
+000002c0  64 42 e3 4e 51 f3 11 4b  b4 9e a3 92 a2 10 59 14  |dB.NQ..K......Y.|
+000002d0  03 01 00 01 01 16 03 01  00 30 78 8c 7c 31 ce 16  |.........0x.|1..|
+000002e0  8f 1f 2a b9 ee cb 72 7f  1e 59 5b ad c2 58 32 77  |..*...r..Y[..X2w|
+000002f0  fa 46 83 b9 67 0c 5f 41  25 6a 38 ec 20 d2 80 e6  |.F..g._A%j8. ...|
+00000300  be 85 ce 94 b1 89 5f 8d  17 9b                    |......_...|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 37 f0 ad 4c 11  |..........07..L.|
-00000010  6d fb 54 90 13 d2 10 93  43 d8 be 3b d0 2b 14 a5  |m.T.....C..;.+..|
-00000020  9d fb a6 5d 38 e0 f5 e9  a6 0a 8e 3d 99 a2 ec 96  |...]8......=....|
-00000030  d8 ff 90 13 03 99 33 d7  15 29 5f                 |......3..)_|
+00000000  14 03 01 00 01 01 16 03  01 00 30 95 d6 f2 a2 75  |..........0....u|
+00000010  0e f8 c7 7c f9 1d 65 b4  82 08 c9 62 aa 93 24 8f  |...|..e....b..$.|
+00000020  4d 11 c7 b0 17 04 f1 0a  8b be 64 06 f9 07 20 0b  |M.........d... .|
+00000030  f0 3b 92 db 62 ba 63 91  a1 58 fe                 |.;..b.c..X.|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 20 f9 59 b0  e2 8b f9 2c dd 30 1b 8f  |.... .Y....,.0..|
-00000010  df 85 0f 17 88 23 5e ca  c9 d3 ca 5f 52 d4 33 e0  |.....#^...._R.3.|
-00000020  d2 62 54 17 f2 17 03 01  00 20 62 2d 28 d2 55 68  |.bT...... b-(.Uh|
-00000030  77 ab 6e c0 ac d9 cd 31  1c 38 aa 07 b3 e8 0d 89  |w.n....1.8......|
-00000040  7e e4 f3 a0 65 84 f6 b8  c8 91 15 03 01 00 20 b5  |~...e......... .|
-00000050  95 69 90 d7 32 d1 5a a5  e0 e2 6c 0a dc 00 1c 5e  |.i..2.Z...l....^|
-00000060  d2 10 2b a2 3e ae a5 b2  63 9f c4 4e 62 56 db     |..+.>...c..NbV.|
+00000000  17 03 01 00 20 3e a4 b5  b5 2f 4f c8 e0 08 cf 8a  |.... >.../O.....|
+00000010  9c f6 69 94 a9 91 0f 5d  c5 06 ee 71 e2 42 11 b4  |..i....]...q.B..|
+00000020  a8 17 54 19 3d 17 03 01  00 20 ce d2 8d 8a 78 e4  |..T.=.... ....x.|
+00000030  15 a4 ab 83 0d 9c fa 47  1c 8f 2d 87 a8 55 65 9d  |.......G..-..Ue.|
+00000040  7f 03 75 11 62 83 0b 44  0b f1 15 03 01 00 20 eb  |..u.b..D...... .|
+00000050  1a 46 95 1e 1b 10 b7 25  a8 c4 5b db 8b 3c 61 c9  |.F.....%..[..<a.|
+00000060  25 38 27 1e 69 11 18 16  0a 25 44 ad 9f 52 64     |%8'.i....%D..Rd|
diff --git a/src/crypto/tls/testdata/Client-TLSv10-ClientCert-ECDSA-RSA b/src/crypto/tls/testdata/Client-TLSv10-ClientCert-ECDSA-RSA
index 298cbe1..7e9c272 100644
--- a/src/crypto/tls/testdata/Client-TLSv10-ClientCert-ECDSA-RSA
+++ b/src/crypto/tls/testdata/Client-TLSv10-ClientCert-ECDSA-RSA
@@ -1,60 +1,72 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 51 02 00 00  4d 03 01 4e 15 d3 06 f6  |....Q...M..N....|
-00000010  ec 13 16 c5 fa 59 cf 5e  2f ad 85 b9 38 e7 7f fb  |.....Y.^/...8...|
-00000020  85 cb da eb f2 2e 17 51  a2 b0 be 20 61 e4 32 c9  |.......Q... a.2.|
-00000030  66 92 36 89 0c 0c f4 00  15 47 86 d9 e9 90 ab 2d  |f.6......G.....-|
-00000040  8f a3 e2 5e f6 44 2c 6a  1d 98 88 5c 00 05 00 00  |...^.D,j...\....|
-00000050  05 ff 01 00 01 00 16 03  01 02 71 0b 00 02 6d 00  |..........q...m.|
-00000060  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000070  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000090  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-000000a0  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-000000b0  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000c0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000d0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000e0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000f0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-00000100  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-00000110  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000120  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000130  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000140  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000150  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000160  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000170  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000180  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000190  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-000001a0  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-000001b0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001c0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001d0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001e0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001f0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-00000200  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-00000210  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000220  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000230  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000240  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000250  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000260  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000270  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000280  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000290  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-000002a0  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-000002b0  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002c0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 01 00  |..A4......9.....|
-000002d0  0e 0d 00 00 06 03 01 02  40 00 00 0e 00 00 00     |........ at ......|
+00000000  16 03 01 00 59 02 00 00  55 03 01 32 7c 5c ac bd  |....Y...U..2|\..|
+00000010  77 70 c2 f8 f0 20 37 e4  e8 45 db be 97 22 e4 f3  |wp... 7..E..."..|
+00000020  24 1c c1 29 8f 02 e1 bc  ba 4a 1e 20 81 6f b5 12  |$..).....J. .o..|
+00000030  c0 9d 9e de 2f b6 04 b2  74 34 da 2b 04 55 2c 4f  |..../...t4.+.U,O|
+00000040  dd 01 8a 30 d9 67 45 9f  f1 31 f1 78 c0 13 00 00  |...0.gE..1.x....|
+00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
+00000060  01 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 01 00  |.=.`.\!.;.......|
+000002c0  aa 0c 00 00 a6 03 00 1d  20 2f f7 3b 44 1a 47 85  |........ /.;D.G.|
+000002d0  d7 db 40 28 4e 6a f1 2f  1e b5 cc b0 58 0d 92 93  |..@(Nj./....X...|
+000002e0  30 41 65 08 05 f7 51 23  57 00 80 87 0d c3 22 ff  |0Ae...Q#W.....".|
+000002f0  aa d1 3f 55 09 cf 98 dc  91 f8 d0 63 58 da dc 52  |..?U.......cX..R|
+00000300  03 f0 06 a6 4e 7e 5b 96  a1 3b d7 8e 1e 68 50 ef  |....N~[..;...hP.|
+00000310  59 3f 78 06 eb 9a 33 c5  01 3c e0 fb c6 f1 b6 bc  |Y?x...3..<......|
+00000320  5a bc 95 e8 43 d9 ab 36  05 26 13 c5 a6 68 9b e2  |Z...C..6.&...h..|
+00000330  b1 42 6e 89 60 5c b3 91  02 c5 8b ab 53 d1 d9 79  |.Bn.`\......S..y|
+00000340  d0 37 b5 5e 2c 16 72 29  f8 9c d0 4a 46 87 46 f4  |.7.^,.r)...JF.F.|
+00000350  01 2b e8 6a 4f 59 d1 2d  3d de 4b 3b 0e c7 cd 42  |.+.jOY.-=.K;...B|
+00000360  ae d2 94 e9 a6 6b 65 ad  3f 77 57 16 03 01 00 0a  |.....ke.?wW.....|
+00000370  0d 00 00 06 03 01 02 40  00 00 16 03 01 00 04 0e  |....... at ........|
+00000380  00 00 00                                          |...|
 >>> Flow 3 (client to server)
 00000000  16 03 01 02 0a 0b 00 02  06 00 02 03 00 02 00 30  |...............0|
 00000010  82 01 fc 30 82 01 5e 02  09 00 9a 30 84 6c 26 35  |...0..^....0.l&5|
@@ -89,33 +101,32 @@
 000001e0  be e8 91 b3 da 1a f5 5d  a3 23 f5 26 8b 45 70 8d  |.......].#.&.Ep.|
 000001f0  65 62 9b 7e 01 99 3d 18  f6 10 9a 38 61 9b 2e 57  |eb.~..=....8a..W|
 00000200  e4 fa cc b1 8a ce e2 23  a0 87 f0 e1 67 51 eb 16  |.......#....gQ..|
-00000210  03 01 00 86 10 00 00 82  00 80 73 bd 73 65 92 86  |..........s.se..|
-00000220  23 41 14 79 7f d5 c1 10  ce 94 4d ad 9c c3 a9 87  |#A.y......M.....|
-00000230  b5 32 52 f8 6b 11 93 2d  9b 98 0b 8b 1d c0 f6 53  |.2R.k..-.......S|
-00000240  17 6d c7 9c 2e ae c9 6f  cc 99 23 38 37 1a 10 fe  |.m.....o..#87...|
-00000250  05 0b b5 55 0a 14 e9 60  7d 70 26 98 e2 54 d9 65  |...U...`}p&..T.e|
-00000260  cf 2e f4 53 5f 1d aa 3a  f6 33 7b eb 4c 0e b3 ff  |...S_..:.3{.L...|
-00000270  5a db 36 2a 47 f3 df f9  fc f5 31 78 83 aa 6b 52  |Z.6*G.....1x..kR|
-00000280  b7 ba 1a 96 bc fa c1 a1  a9 bb 2b f5 38 89 00 4d  |..........+.8..M|
-00000290  e5 78 13 4e a4 38 46 42  dc 16 16 03 01 00 91 0f  |.x.N.8FB........|
-000002a0  00 00 8d 00 8b 30 81 88  02 42 01 45 b9 8f b1 1f  |.....0...B.E....|
-000002b0  72 80 2c 4f 2c 65 58 db  40 7e f1 d5 14 0b cc 4c  |r.,O,eX.@~.....L|
-000002c0  8b 50 5c ee 93 45 95 3d  fe 00 5e 5e ca 13 56 8d  |.P\..E.=..^^..V.|
-000002d0  2b b3 1a 22 70 3f d2 41  cf 74 8f c3 0f 37 ba 97  |+.."p?.A.t...7..|
-000002e0  cb 29 16 77 92 df 19 35  f9 8a a0 8e 02 42 01 00  |.).w...5.....B..|
-000002f0  3f 8b ce b1 2a 01 43 e8  2c b5 27 d1 19 bc 04 b3  |?...*.C.,.'.....|
-00000300  c3 ad bf e8 12 37 57 6f  c9 01 7c 8e f4 4d 88 39  |.....7Wo..|..M.9|
-00000310  4b 00 f6 ff fd 38 39 f8  3e 7f 49 d4 6a 82 94 6a  |K....89.>.I.j..j|
-00000320  d3 f4 17 f2 a9 e0 ef 85  1e 01 85 b6 ca 89 91 ee  |................|
-00000330  14 03 01 00 01 01 16 03  01 00 24 8d 82 24 82 55  |..........$..$.U|
-00000340  c4 0e 45 8c f0 f3 e3 29  4e ff 6c ee 43 4b ca 68  |..E....)N.l.CK.h|
-00000350  2e 12 98 cf ce b6 7e fa  73 07 e1 0f aa 7f 80     |......~.s......|
+00000210  03 01 00 25 10 00 00 21  20 2f e5 7d a3 47 cd 62  |...%...! /.}.G.b|
+00000220  43 15 28 da ac 5f bb 29  07 30 ff f6 84 af c4 cf  |C.(.._.).0......|
+00000230  c2 ed 90 99 5f 58 cb 3b  74 16 03 01 00 91 0f 00  |...._X.;t.......|
+00000240  00 8d 00 8b 30 81 88 02  42 01 b3 df 59 06 71 e6  |....0...B...Y.q.|
+00000250  74 c9 9d d5 2c b0 a7 f8  1e ac bc f3 5a e2 ed 0b  |t...,.......Z...|
+00000260  f2 e9 37 82 c6 fe 7c 23  b9 63 6e 88 1d 63 31 ad  |..7...|#.cn..c1.|
+00000270  d3 29 48 eb f3 5d 52 f5  76 ab fc 16 9e 09 4f 49  |.)H..]R.v.....OI|
+00000280  cf b4 03 6a ed db e5 13  ea 67 74 02 42 01 8e 2f  |...j.....gt.B../|
+00000290  b8 12 38 c9 a6 8c 77 40  85 89 ef d8 ac 08 00 c0  |..8...w at ........|
+000002a0  ee 70 68 a6 88 1f d1 67  0d 1b 7b 1f be e0 a7 b9  |.ph....g..{.....|
+000002b0  c3 7d ff 6a 39 3c b9 aa  f6 78 ac 9a ca 67 55 0c  |.}.j9<...x...gU.|
+000002c0  38 23 cc ab 18 c0 b9 ea  9c 84 61 32 0a 0d f3 14  |8#........a2....|
+000002d0  03 01 00 01 01 16 03 01  00 30 73 12 76 94 30 37  |.........0s.v.07|
+000002e0  e5 e3 30 59 88 2f 5f e9  f2 7b 3d 02 88 65 09 14  |..0Y./_..{=..e..|
+000002f0  68 23 02 d0 ae e5 7f 7f  8d 95 3b 1c 75 f5 1f 24  |h#........;.u..$|
+00000300  43 60 29 bb 0e 69 88 36  a9 68                    |C`)..i.6.h|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 24 21 a3 eb a6 f5  |..........$!....|
-00000010  d0 17 38 9b 89 ec f3 39  23 33 f6 49 51 41 97 92  |..8....9#3.IQA..|
-00000020  a6 64 bd 60 68 9d 0e 45  06 2f dd ff 79 b6 50     |.d.`h..E./..y.P|
+00000000  14 03 01 00 01 01 16 03  01 00 30 a0 5f 7f 59 e0  |..........0._.Y.|
+00000010  b1 7e ed ad de 6a 47 94  21 e5 1b 77 a7 d0 88 fd  |.~...jG.!..w....|
+00000020  9e 4e 48 87 1d cf 40 e4  b9 38 a3 2e e4 00 c3 94  |.NH... at ..8......|
+00000030  95 20 1c 97 d2 a9 3a 11  86 30 5f                 |. ....:..0_|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 1a d2 72 d5  91 9d fc 6c 22 02 cc 32  |......r....l"..2|
-00000010  58 5c 8a f6 75 11 48 e1  3f e4 e5 81 29 63 62 15  |X\..u.H.?...)cb.|
-00000020  03 01 00 16 b6 9a 1f 43  d4 ae b7 16 25 ce ae b7  |.......C....%...|
-00000030  6c 37 f7 35 0a 26 7d ea  1f 80                    |l7.5.&}...|
+00000000  17 03 01 00 20 ca 4c f5  cb 81 66 2f 97 e3 5d 8b  |.... .L...f/..].|
+00000010  dd 7d dd fa fe 8c 98 45  3f 3d 16 17 98 4d b5 15  |.}.....E?=...M..|
+00000020  6c 91 8a 79 7a 17 03 01  00 20 96 ec 30 cb d3 78  |l..yz.... ..0..x|
+00000030  b9 0a a1 ab fd 12 25 d5  82 7b 7a 3c 17 56 7b b7  |......%..{z<.V{.|
+00000040  c4 6e ea a2 5b d7 6b b6  22 a9 15 03 01 00 20 ba  |.n..[.k."..... .|
+00000050  ff fe 2b 60 83 34 ad 45  75 15 d5 95 b3 27 92 46  |..+`.4.Eu....'.F|
+00000060  47 ae f1 d4 a4 9d 63 ef  db d9 b5 37 0f f1 74     |G.....c....7..t|
diff --git a/src/crypto/tls/testdata/Client-TLSv10-ClientCert-RSA-ECDSA b/src/crypto/tls/testdata/Client-TLSv10-ClientCert-RSA-ECDSA
index ba4976d..67772e1 100644
--- a/src/crypto/tls/testdata/Client-TLSv10-ClientCert-RSA-ECDSA
+++ b/src/crypto/tls/testdata/Client-TLSv10-ClientCert-RSA-ECDSA
@@ -1,19 +1,20 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 59 02 00 00  55 03 01 eb ed 76 6a 07  |....Y...U....vj.|
-00000010  65 02 ec 6f 93 a0 38 21  09 0d d7 bf 11 20 51 eb  |e..o..8!..... Q.|
-00000020  cc 00 08 9b 7a 98 c4 c5  02 ff c1 20 f9 1b c7 66  |....z...... ...f|
-00000030  35 40 8c 67 8d 7f d5 c8  28 f0 cb d2 f9 da af 7a  |5 at .g....(......z|
-00000040  ea 4e 42 f2 5d 44 1c cc  92 36 b1 10 c0 09 00 00  |.NB.]D...6......|
+00000000  16 03 01 00 59 02 00 00  55 03 01 ed 13 de 15 cc  |....Y...U.......|
+00000010  90 4f f3 72 5a d4 7a 01  26 fa 7a ae 38 92 a0 d6  |.O.rZ.z.&.z.8...|
+00000020  70 4a 20 f6 7e 11 f7 ac  e6 94 87 20 9f 37 0f 8f  |pJ .~...... .7..|
+00000030  55 a6 6a 97 b8 0f 56 aa  2d 69 c5 79 01 d5 c0 01  |U.j...V.-i.y....|
+00000040  2c 2b 0e 16 d8 79 a3 f3  44 99 7c 01 c0 09 00 00  |,+...y..D.|.....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  01 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
@@ -48,82 +49,79 @@
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 01 00 d5 0c 00  00 d1 03 00 17 41 04 34  |*............A.4|
-00000280  47 57 64 b3 20 6d eb 17  9c 36 d4 aa 78 8b 20 26  |GWd. m...6..x. &|
-00000290  6f 22 10 79 5f 96 69 62  1d ae 9f c7 40 17 1e 30  |o".y_.ib.... at ..0|
-000002a0  10 db d1 13 51 d8 63 61  ef 8e fb 34 d6 02 95 ac  |....Q.ca...4....|
-000002b0  fb 33 72 a9 46 ff 27 b1  15 ca dd 81 8f 5a 58 00  |.3r.F.'......ZX.|
-000002c0  8a 30 81 87 02 41 5c 09  1a 87 40 f3 1a 87 84 31  |.0...A\... at ....1|
-000002d0  62 6c e5 a5 c0 3c cc ba  5d 4a 5e 65 ea e0 60 83  |bl...<..]J^e..`.|
-000002e0  fe fe 99 1d 66 4a bb 6c  0d 5e 25 64 e3 92 ce eb  |....fJ.l.^%d....|
-000002f0  15 39 42 a6 b0 98 a1 d3  79 65 c7 fc e7 c7 64 c7  |.9B.....ye....d.|
-00000300  69 9c 2f 7e 00 c1 a3 02  42 01 f2 61 91 ae 8e f6  |i./~....B..a....|
-00000310  88 99 70 55 32 4a fe 08  31 f0 8d d6 e6 1d fa a1  |..pU2J..1.......|
-00000320  76 b6 16 98 58 8e 46 30  b1 00 b6 dd 5d 70 bb e1  |v...X.F0....]p..|
-00000330  81 89 bd aa ac b5 7f 9b  d3 c0 8b 4b c3 36 00 87  |...........K.6..|
-00000340  47 0c 34 92 27 c3 aa bd  0d 7c 36 16 03 01 00 0e  |G.4.'....|6.....|
-00000350  0d 00 00 06 03 01 02 40  00 00 0e 00 00 00        |....... at ......|
+00000270  2a 16 03 01 00 b4 0c 00  00 b0 03 00 1d 20 ca e8  |*............ ..|
+00000280  ef 79 56 cd aa eb 12 8f  e1 89 d1 3c 63 1f c8 54  |.yV........<c..T|
+00000290  5f 4e cf 6b 72 7d 1c bb  f6 80 ae 17 33 69 00 8a  |_N.kr}......3i..|
+000002a0  30 81 87 02 42 01 d1 45  df fc 46 21 5b 9b 49 f0  |0...B..E..F![.I.|
+000002b0  3c f2 16 65 1e 33 90 d8  be 1d 65 12 2f 46 93 5b  |<..e.3....e./F.[|
+000002c0  e2 14 67 b8 67 9b c1 10  31 a1 96 b8 86 c3 8b 26  |..g.g...1......&|
+000002d0  3f da 5e 86 e7 b1 f9 3f  f1 04 57 ed e6 6f a5 86  |?.^....?..W..o..|
+000002e0  f7 58 38 6e 0d ae 42 02  41 05 1b 07 9b 4c 4d 39  |.X8n..B.A....LM9|
+000002f0  2d 0c 4e d7 94 d6 86 c9  6c b9 4d 54 a2 56 87 12  |-.N.....l.MT.V..|
+00000300  08 ec 4e f1 a4 19 5e 52  69 ed 9f 6c 59 5f 31 0f  |..N...^Ri..lY_1.|
+00000310  8d 33 1f a7 42 e5 56 9d  54 f4 18 9b 33 31 97 b9  |.3..B.V.T...31..|
+00000320  57 55 c9 9f ea 7d f2 9e  24 e0 16 03 01 00 0a 0d  |WU...}..$.......|
+00000330  00 00 06 03 01 02 40 00  00 16 03 01 00 04 0e 00  |...... at .........|
+00000340  00 00                                             |..|
 >>> Flow 3 (client to server)
-00000000  16 03 01 01 fb 0b 00 01  f7 00 01 f4 00 01 f1 30  |...............0|
-00000010  82 01 ed 30 82 01 58 a0  03 02 01 02 02 01 00 30  |...0..X........0|
-00000020  0b 06 09 2a 86 48 86 f7  0d 01 01 05 30 26 31 10  |...*.H......0&1.|
-00000030  30 0e 06 03 55 04 0a 13  07 41 63 6d 65 20 43 6f  |0...U....Acme Co|
-00000040  31 12 30 10 06 03 55 04  03 13 09 31 32 37 2e 30  |1.0...U....127.0|
-00000050  2e 30 2e 31 30 1e 17 0d  31 31 31 32 30 38 30 37  |.0.10...11120807|
-00000060  35 35 31 32 5a 17 0d 31  32 31 32 30 37 30 38 30  |5512Z..121207080|
-00000070  30 31 32 5a 30 26 31 10  30 0e 06 03 55 04 0a 13  |012Z0&1.0...U...|
-00000080  07 41 63 6d 65 20 43 6f  31 12 30 10 06 03 55 04  |.Acme Co1.0...U.|
-00000090  03 13 09 31 32 37 2e 30  2e 30 2e 31 30 81 9c 30  |...127.0.0.10..0|
-000000a0  0b 06 09 2a 86 48 86 f7  0d 01 01 01 03 81 8c 00  |...*.H..........|
-000000b0  30 81 88 02 81 80 4e d0  7b 31 e3 82 64 d9 59 c0  |0.....N.{1..d.Y.|
-000000c0  c2 87 a4 5e 1e 8b 73 33  c7 63 53 df 66 92 06 84  |...^..s3.cS.f...|
-000000d0  f6 64 d5 8f e4 36 a7 1d  2b e8 b3 20 36 45 23 b5  |.d...6..+.. 6E#.|
-000000e0  e3 95 ae ed e0 f5 20 9c  8d 95 df 7f 5a 12 ef 87  |...... .....Z...|
-000000f0  e4 5b 68 e4 e9 0e 74 ec  04 8a 7f de 93 27 c4 01  |.[h...t......'..|
-00000100  19 7a bd f2 dc 3d 14 ab  d0 54 ca 21 0c d0 4d 6e  |.z...=...T.!..Mn|
-00000110  87 2e 5c c5 d2 bb 4d 4b  4f ce b6 2c f7 7e 88 ec  |..\...MKO..,.~..|
-00000120  7c d7 02 91 74 a6 1e 0c  1a da e3 4a 5a 2e de 13  ||...t......JZ...|
-00000130  9c 4c 40 88 59 93 02 03  01 00 01 a3 32 30 30 30  |.L at .Y.......2000|
-00000140  0e 06 03 55 1d 0f 01 01  ff 04 04 03 02 00 a0 30  |...U...........0|
-00000150  0d 06 03 55 1d 0e 04 06  04 04 01 02 03 04 30 0f  |...U..........0.|
-00000160  06 03 55 1d 23 04 08 30  06 80 04 01 02 03 04 30  |..U.#..0.......0|
-00000170  0b 06 09 2a 86 48 86 f7  0d 01 01 05 03 81 81 00  |...*.H..........|
-00000180  36 1f b3 7a 0c 75 c9 6e  37 46 61 2b d5 bd c0 a7  |6..z.u.n7Fa+....|
-00000190  4b cc 46 9a 81 58 7c 85  79 29 c8 c8 c6 67 dd 32  |K.F..X|.y)...g.2|
-000001a0  56 45 2b 75 b6 e9 24 a9  50 9a be 1f 5a fa 1a 15  |VE+u..$.P...Z...|
-000001b0  d9 cc 55 95 72 16 83 b9  c2 b6 8f fd 88 8c 38 84  |..U.r.........8.|
-000001c0  1d ab 5d 92 31 13 4f fd  83 3b c6 9d f1 11 62 b6  |..].1.O..;....b.|
-000001d0  8b ec ab 67 be c8 64 b0  11 50 46 58 17 6b 99 1c  |...g..d..PFX.k..|
-000001e0  d3 1d fc 06 f1 0e e5 96  a8 0c f9 78 20 b7 44 18  |...........x .D.|
-000001f0  51 8d 10 7e 4f 94 67 df  a3 4e 70 73 8e 90 91 85  |Q..~O.g..Nps....|
-00000200  16 03 01 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000210  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000220  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000230  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000240  a6 b5 68 1a 41 03 56 6b  dc 5a 89 16 03 01 00 86  |..h.A.Vk.Z......|
-00000250  0f 00 00 82 00 80 1e 4d  89 4e e2 82 ca 5d 31 8a  |.......M.N...]1.|
-00000260  66 c7 c2 d6 00 4d 2e 1e  94 34 61 6b 86 3d 78 60  |f....M...4ak.=x`|
-00000270  70 e1 71 93 22 df 5d 81  d3 d7 33 10 f5 01 f9 1d  |p.q.".]...3.....|
-00000280  e2 4a 91 22 67 ae 5b 2f  4c d9 43 31 35 c6 01 ad  |.J."g.[/L.C15...|
-00000290  59 86 03 a1 9b c5 ea a5  2d aa ef 46 5a a8 70 57  |Y.......-..FZ.pW|
-000002a0  50 59 ea 7a 07 32 bb a6  a1 11 33 05 d8 88 2e 42  |PY.z.2....3....B|
-000002b0  d8 7b f7 34 be 5e 5f 42  9f 6a 90 ed d7 4b c4 7e  |.{.4.^_B.j...K.~|
-000002c0  f9 5c a5 ff 28 f8 a1 f1  8f 1c e0 7a 37 a0 49 e5  |.\..(......z7.I.|
-000002d0  ce 11 46 ef 5f 06 14 03  01 00 01 01 16 03 01 00  |..F._...........|
-000002e0  30 cb 08 f0 3c d4 21 f2  3a 7d db 59 75 80 48 24  |0...<.!.:}.Yu.H$|
-000002f0  27 6f 2c 26 50 a4 7d 6c  91 d5 5d f7 c9 b4 bd 15  |'o,&P.}l..].....|
-00000300  a8 8a 12 d5 40 8c 9a 0f  56 67 66 89 dd 12 36 d8  |.... at ...Vgf...6.|
-00000310  d3                                                |.|
+00000000  16 03 01 01 fd 0b 00 01  f9 00 01 f6 00 01 f3 30  |...............0|
+00000010  82 01 ef 30 82 01 58 a0  03 02 01 02 02 10 5c 19  |...0..X.......\.|
+00000020  c1 89 65 83 55 6f dc 0b  c9 b9 93 9f e9 bc 30 0d  |..e.Uo........0.|
+00000030  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 30 12 31  |..*.H........0.1|
+00000040  10 30 0e 06 03 55 04 0a  13 07 41 63 6d 65 20 43  |.0...U....Acme C|
+00000050  6f 30 1e 17 0d 31 36 30  38 31 37 32 31 35 32 33  |o0...16081721523|
+00000060  31 5a 17 0d 31 37 30 38  31 37 32 31 35 32 33 31  |1Z..170817215231|
+00000070  5a 30 12 31 10 30 0e 06  03 55 04 0a 13 07 41 63  |Z0.1.0...U....Ac|
+00000080  6d 65 20 43 6f 30 81 9f  30 0d 06 09 2a 86 48 86  |me Co0..0...*.H.|
+00000090  f7 0d 01 01 01 05 00 03  81 8d 00 30 81 89 02 81  |...........0....|
+000000a0  81 00 ba 6f aa 86 bd cf  bf 9f f2 ef 5c 94 60 78  |...o........\.`x|
+000000b0  6f e8 13 f2 d1 96 6f cd  d9 32 6e 22 37 ce 41 f9  |o.....o..2n"7.A.|
+000000c0  ca 5d 29 ac e1 27 da 61  a2 ee 81 cb 10 c7 df 34  |.])..'.a.......4|
+000000d0  58 95 86 e9 3d 19 e6 5c  27 73 60 c8 8d 78 02 f4  |X...=..\'s`..x..|
+000000e0  1d a4 98 09 a3 19 70 69  3c 25 62 66 2a ab 22 23  |......pi<%bf*."#|
+000000f0  c5 7b 85 38 4f 2e 09 73  32 a7 bd 3e 9b ad ca 84  |.{.8O..s2..>....|
+00000100  07 e6 0f 3a ff 77 c5 9d  41 85 00 8a b6 9b ee b0  |...:.w..A.......|
+00000110  a4 3f 2d 4c 4c e6 42 3e  bb 51 c8 dd 48 54 f4 0c  |.?-LL.B>.Q..HT..|
+00000120  8e 47 02 03 01 00 01 a3  46 30 44 30 0e 06 03 55  |.G......F0D0...U|
+00000130  1d 0f 01 01 ff 04 04 03  02 05 a0 30 13 06 03 55  |...........0...U|
+00000140  1d 25 04 0c 30 0a 06 08  2b 06 01 05 05 07 03 01  |.%..0...+.......|
+00000150  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 0f  |0...U.......0.0.|
+00000160  06 03 55 1d 11 04 08 30  06 87 04 7f 00 00 01 30  |..U....0.......0|
+00000170  0d 06 09 2a 86 48 86 f7  0d 01 01 0b 05 00 03 81  |...*.H..........|
+00000180  81 00 46 ab 44 a2 fb 28  54 f8 5a 67 f8 62 94 f1  |..F.D..(T.Zg.b..|
+00000190  9a b2 18 9e f2 b1 de 1d  7e 6f 76 95 a9 ba e7 5d  |........~ov....]|
+000001a0  a8 16 6c 9c f7 09 d3 37  e4 4b 2b 36 7c 01 ad 41  |..l....7.K+6|..A|
+000001b0  d2 32 d8 c3 d2 93 f9 10  6b 8e 95 b9 2c 17 8a a3  |.2......k...,...|
+000001c0  44 48 bc 59 13 83 16 04  88 a4 81 5c 25 0d 98 0c  |DH.Y.......\%...|
+000001d0  ac 11 b1 28 56 be 1d cd  61 62 84 09 bf d6 80 c6  |...(V...ab......|
+000001e0  45 8d 82 2c b4 d8 83 9b  db c9 22 b7 2a 12 11 7b  |E..,......".*..{|
+000001f0  fa 02 3b c1 c9 ff ea c9  9d a8 49 d3 95 d7 d5 0e  |..;.......I.....|
+00000200  e5 35 16 03 01 00 25 10  00 00 21 20 2f e5 7d a3  |.5....%...! /.}.|
+00000210  47 cd 62 43 15 28 da ac  5f bb 29 07 30 ff f6 84  |G.bC.(.._.).0...|
+00000220  af c4 cf c2 ed 90 99 5f  58 cb 3b 74 16 03 01 00  |......._X.;t....|
+00000230  86 0f 00 00 82 00 80 8f  6f 77 5d d5 99 28 0c 7a  |........ow]..(.z|
+00000240  36 f2 50 ec 9a e6 eb 88  ac 45 f7 9b 6f 98 84 ba  |6.P......E..o...|
+00000250  fb 3c b8 d6 54 61 b8 87  25 50 3c 31 5a d2 c1 54  |.<..Ta..%P<1Z..T|
+00000260  e8 ed c3 93 cc 98 b1 c3  d4 84 11 d8 a0 c7 ae 67  |...............g|
+00000270  67 35 6a 0f 93 18 bb 18  52 f8 25 88 1f d2 19 4d  |g5j.....R.%....M|
+00000280  3b 4c f2 0f f7 06 68 57  cf 45 20 e0 57 75 37 e9  |;L....hW.E .Wu7.|
+00000290  cd 86 1f e5 d2 90 1e cf  3a 18 fd 45 bc a1 84 63  |........:..E...c|
+000002a0  36 d8 ac 6b 09 41 da 0a  87 7f ab ce 8e 49 e6 c8  |6..k.A.......I..|
+000002b0  bf fb 2c 3b 7b e9 ae 14  03 01 00 01 01 16 03 01  |..,;{...........|
+000002c0  00 30 7d 65 9c c1 25 e4  85 d7 39 d4 67 cf eb f1  |.0}e..%...9.g...|
+000002d0  b7 c2 4d e6 5d bd 13 74  55 22 f0 8a 7e a6 a2 eb  |..M.]..tU"..~...|
+000002e0  93 cc b7 fa 86 b1 b5 e0  a3 ef ee 56 f0 cd f7 a5  |...........V....|
+000002f0  d8 9e                                             |..|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 02 e3 be 9d 2d  |..........0....-|
-00000010  6f 2c 9a b7 b4 f1 a5 30  ec 3e ae 05 e6 02 19 2f  |o,.....0.>...../|
-00000020  a4 ac d1 6e ac de 75 4e  cc 14 e6 78 5a ea 27 7f  |...n..uN...xZ.'.|
-00000030  4e 45 c4 9d b2 da a6 ea  b7 d2 7e                 |NE........~|
+00000000  14 03 01 00 01 01 16 03  01 00 30 ff 13 14 c5 ad  |..........0.....|
+00000010  88 ec a1 cf cc 0d 3f 7b  ec 50 4a 25 69 1f 18 dc  |......?{.PJ%i...|
+00000020  b1 99 1f 3b 78 60 e0 83  c0 cd 9a b3 0d 59 0b f8  |...;x`.......Y..|
+00000030  8a b7 7c 2c b4 2c e4 d0  49 82 82                 |..|,.,..I..|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 20 e0 71 e9  54 11 6e 48 4b be a2 2a  |.... .q.T.nHK..*|
-00000010  b1 70 d2 2c 74 c0 f4 74  05 f1 d3 d6 84 29 58 f7  |.p.,t..t.....)X.|
-00000020  87 90 84 2b c8 17 03 01  00 20 b6 a2 e9 e0 f0 0d  |...+..... ......|
-00000030  d5 ef d7 32 6d cb 99 5d  a6 37 c2 6e f9 c3 8e 6f  |...2m..].7.n...o|
-00000040  76 71 d8 a6 c5 ae 4e 04  77 06 15 03 01 00 20 f2  |vq....N.w..... .|
-00000050  09 ab dc 37 90 78 3a 2a  41 ab 9b a9 c1 78 2a 64  |...7.x:*A....x*d|
-00000060  a8 3f 21 c4 bb af 76 b3  c6 2f e1 20 a3 b1 1e     |.?!...v../. ...|
+00000000  17 03 01 00 20 51 91 74  f6 31 07 15 6b 9e 0b 28  |.... Q.t.1..k..(|
+00000010  02 b8 ec 9d c6 e3 15 24  d3 ea 4b 27 d0 fa 9f c2  |.......$..K'....|
+00000020  c4 8d 37 b3 d9 17 03 01  00 20 7d 97 75 fe de 3f  |..7...... }.u..?|
+00000030  ae ab e6 a8 1d 76 1c 06  9c 02 61 cc f5 1d fe c8  |.....v....a.....|
+00000040  a2 dc ae 97 7f 1c 05 19  e5 14 15 03 01 00 20 4a  |.............. J|
+00000050  bc 45 97 6b 09 8e 47 5f  d5 a0 97 78 79 67 09 8d  |.E.k..G_...xyg..|
+00000060  d3 80 38 58 5c cc ae 8e  d4 67 1d 93 2b 20 79     |..8X\....g..+ y|
diff --git a/src/crypto/tls/testdata/Client-TLSv10-ClientCert-RSA-RSA b/src/crypto/tls/testdata/Client-TLSv10-ClientCert-RSA-RSA
index e007870..e585894 100644
--- a/src/crypto/tls/testdata/Client-TLSv10-ClientCert-RSA-RSA
+++ b/src/crypto/tls/testdata/Client-TLSv10-ClientCert-RSA-RSA
@@ -1,120 +1,131 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 51 02 00 00  4d 03 01 e5 d7 4b 56 7b  |....Q...M....KV{|
-00000010  a8 2c 07 33 fc 66 d7 79  e9 26 91 56 7d 9d 99 1d  |.,.3.f.y.&.V}...|
-00000020  b2 24 36 2c f6 78 3a e7  63 15 f6 20 9f e1 d4 07  |.$6,.x:.c.. ....|
-00000030  a9 75 3d b9 3b 8c 46 cb  a7 37 36 56 af 4e 99 cf  |.u=.;.F..76V.N..|
-00000040  90 49 e1 e9 69 25 81 0f  fd 22 48 e6 00 05 00 00  |.I..i%..."H.....|
-00000050  05 ff 01 00 01 00 16 03  01 02 71 0b 00 02 6d 00  |..........q...m.|
-00000060  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000070  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000090  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-000000a0  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-000000b0  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000c0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000d0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000e0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000f0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-00000100  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-00000110  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000120  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000130  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000140  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000150  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000160  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000170  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000180  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000190  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-000001a0  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-000001b0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001c0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001d0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001e0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001f0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-00000200  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-00000210  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000220  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000230  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000240  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000250  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000260  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000270  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000280  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000290  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-000002a0  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-000002b0  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002c0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 01 00  |..A4......9.....|
-000002d0  0e 0d 00 00 06 03 01 02  40 00 00 0e 00 00 00     |........ at ......|
+00000000  16 03 01 00 59 02 00 00  55 03 01 61 6b 2e 41 7f  |....Y...U..ak.A.|
+00000010  af 26 6f a2 8b 63 ee e4  b1 76 19 3a 6d a3 c2 30  |.&o..c...v.:m..0|
+00000020  37 e8 47 c2 90 10 7e e8  c5 b2 eb 20 00 1c 8f 70  |7.G...~.... ...p|
+00000030  0d 15 4a c7 7d ab ca 79  a7 d8 c2 01 62 6e 6f aa  |..J.}..y....bno.|
+00000040  df a2 1c 8f 7c 27 d9 e6  fe e9 c8 ab c0 13 00 00  |....|'..........|
+00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
+00000060  01 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 01 00  |.=.`.\!.;.......|
+000002c0  aa 0c 00 00 a6 03 00 1d  20 29 0b ca 37 f3 a1 52  |........ )..7..R|
+000002d0  49 1c 84 9a e4 74 6b 4b  2d 1f e6 e9 83 1d 5d 59  |I....tkK-.....]Y|
+000002e0  5a 2f 09 9f bc a4 30 af  71 00 80 d9 bb 6d 09 a7  |Z/....0.q....m..|
+000002f0  ab 47 6f e8 a6 1a da fb  66 7d a5 f0 c9 c3 24 4c  |.Go.....f}....$L|
+00000300  99 56 c6 29 71 27 08 0b  c1 60 44 cc 6d 42 1b 5e  |.V.)q'...`D.mB.^|
+00000310  cd 9f 82 24 38 23 ec d9  fa 32 49 2f 16 5d d2 9d  |...$8#...2I/.]..|
+00000320  e9 13 4e 66 3d f8 bf 30  2e 8c eb 35 4c e8 81 86  |..Nf=..0...5L...|
+00000330  c0 de c7 0d a9 60 7e 7c  4a c4 1d a0 89 70 de 82  |.....`~|J....p..|
+00000340  1b 37 a0 ea 7f 20 a5 fe  d4 20 1d 6f 1a 84 dd a4  |.7... ... .o....|
+00000350  13 46 18 c6 31 14 81 4b  a4 bb 43 5c c4 49 1c 5a  |.F..1..K..C\.I.Z|
+00000360  8d 12 57 e0 1d 9a b6 cd  f1 39 ff 16 03 01 00 0a  |..W......9......|
+00000370  0d 00 00 06 03 01 02 40  00 00 16 03 01 00 04 0e  |....... at ........|
+00000380  00 00 00                                          |...|
 >>> Flow 3 (client to server)
-00000000  16 03 01 01 fb 0b 00 01  f7 00 01 f4 00 01 f1 30  |...............0|
-00000010  82 01 ed 30 82 01 58 a0  03 02 01 02 02 01 00 30  |...0..X........0|
-00000020  0b 06 09 2a 86 48 86 f7  0d 01 01 05 30 26 31 10  |...*.H......0&1.|
-00000030  30 0e 06 03 55 04 0a 13  07 41 63 6d 65 20 43 6f  |0...U....Acme Co|
-00000040  31 12 30 10 06 03 55 04  03 13 09 31 32 37 2e 30  |1.0...U....127.0|
-00000050  2e 30 2e 31 30 1e 17 0d  31 31 31 32 30 38 30 37  |.0.10...11120807|
-00000060  35 35 31 32 5a 17 0d 31  32 31 32 30 37 30 38 30  |5512Z..121207080|
-00000070  30 31 32 5a 30 26 31 10  30 0e 06 03 55 04 0a 13  |012Z0&1.0...U...|
-00000080  07 41 63 6d 65 20 43 6f  31 12 30 10 06 03 55 04  |.Acme Co1.0...U.|
-00000090  03 13 09 31 32 37 2e 30  2e 30 2e 31 30 81 9c 30  |...127.0.0.10..0|
-000000a0  0b 06 09 2a 86 48 86 f7  0d 01 01 01 03 81 8c 00  |...*.H..........|
-000000b0  30 81 88 02 81 80 4e d0  7b 31 e3 82 64 d9 59 c0  |0.....N.{1..d.Y.|
-000000c0  c2 87 a4 5e 1e 8b 73 33  c7 63 53 df 66 92 06 84  |...^..s3.cS.f...|
-000000d0  f6 64 d5 8f e4 36 a7 1d  2b e8 b3 20 36 45 23 b5  |.d...6..+.. 6E#.|
-000000e0  e3 95 ae ed e0 f5 20 9c  8d 95 df 7f 5a 12 ef 87  |...... .....Z...|
-000000f0  e4 5b 68 e4 e9 0e 74 ec  04 8a 7f de 93 27 c4 01  |.[h...t......'..|
-00000100  19 7a bd f2 dc 3d 14 ab  d0 54 ca 21 0c d0 4d 6e  |.z...=...T.!..Mn|
-00000110  87 2e 5c c5 d2 bb 4d 4b  4f ce b6 2c f7 7e 88 ec  |..\...MKO..,.~..|
-00000120  7c d7 02 91 74 a6 1e 0c  1a da e3 4a 5a 2e de 13  ||...t......JZ...|
-00000130  9c 4c 40 88 59 93 02 03  01 00 01 a3 32 30 30 30  |.L at .Y.......2000|
-00000140  0e 06 03 55 1d 0f 01 01  ff 04 04 03 02 00 a0 30  |...U...........0|
-00000150  0d 06 03 55 1d 0e 04 06  04 04 01 02 03 04 30 0f  |...U..........0.|
-00000160  06 03 55 1d 23 04 08 30  06 80 04 01 02 03 04 30  |..U.#..0.......0|
-00000170  0b 06 09 2a 86 48 86 f7  0d 01 01 05 03 81 81 00  |...*.H..........|
-00000180  36 1f b3 7a 0c 75 c9 6e  37 46 61 2b d5 bd c0 a7  |6..z.u.n7Fa+....|
-00000190  4b cc 46 9a 81 58 7c 85  79 29 c8 c8 c6 67 dd 32  |K.F..X|.y)...g.2|
-000001a0  56 45 2b 75 b6 e9 24 a9  50 9a be 1f 5a fa 1a 15  |VE+u..$.P...Z...|
-000001b0  d9 cc 55 95 72 16 83 b9  c2 b6 8f fd 88 8c 38 84  |..U.r.........8.|
-000001c0  1d ab 5d 92 31 13 4f fd  83 3b c6 9d f1 11 62 b6  |..].1.O..;....b.|
-000001d0  8b ec ab 67 be c8 64 b0  11 50 46 58 17 6b 99 1c  |...g..d..PFX.k..|
-000001e0  d3 1d fc 06 f1 0e e5 96  a8 0c f9 78 20 b7 44 18  |...........x .D.|
-000001f0  51 8d 10 7e 4f 94 67 df  a3 4e 70 73 8e 90 91 85  |Q..~O.g..Nps....|
-00000200  16 03 01 00 86 10 00 00  82 00 80 73 bd 73 65 92  |...........s.se.|
-00000210  86 23 41 14 79 7f d5 c1  10 ce 94 4d ad 9c c3 a9  |.#A.y......M....|
-00000220  87 b5 32 52 f8 6b 11 93  2d 9b 98 0b 8b 1d c0 f6  |..2R.k..-.......|
-00000230  53 17 6d c7 9c 2e ae c9  6f cc 99 23 38 37 1a 10  |S.m.....o..#87..|
-00000240  fe 05 0b b5 55 0a 14 e9  60 7d 70 26 98 e2 54 d9  |....U...`}p&..T.|
-00000250  65 cf 2e f4 53 5f 1d aa  3a f6 33 7b eb 4c 0e b3  |e...S_..:.3{.L..|
-00000260  ff 5a db 36 2a 47 f3 df  f9 fc f5 31 78 83 aa 6b  |.Z.6*G.....1x..k|
-00000270  52 b7 ba 1a 96 bc fa c1  a1 a9 bb 2b f5 38 89 00  |R..........+.8..|
-00000280  4d e5 78 13 4e a4 38 46  42 dc 16 16 03 01 00 86  |M.x.N.8FB.......|
-00000290  0f 00 00 82 00 80 3d f7  ff c1 72 82 b8 90 42 a3  |......=...r...B.|
-000002a0  10 24 b5 01 44 60 98 39  e4 36 86 56 09 55 e5 73  |.$..D`.9.6.V.U.s|
-000002b0  3a d9 9d 00 ae 05 23 6f  78 4e 49 28 c1 cc 7a ff  |:.....#oxNI(..z.|
-000002c0  8f 67 92 cd 94 c0 d2 68  7f 48 ec 10 83 48 9e 02  |.g.....h.H...H..|
-000002d0  b8 10 b2 1b f0 ba 8f 5a  c8 85 d9 19 53 c2 8d 37  |.......Z....S..7|
-000002e0  8e 86 4c ca ee 0f c4 97  20 f9 a5 4e 94 b8 c5 c5  |..L..... ..N....|
-000002f0  53 0c c1 b6 e5 a1 4e d6  15 b3 6b 08 c2 25 c3 de  |S.....N...k..%..|
-00000300  e7 69 85 85 56 31 16 ad  68 7e 00 8f 1b fc f8 9f  |.i..V1..h~......|
-00000310  d7 50 87 08 0d c5 14 03  01 00 01 01 16 03 01 00  |.P..............|
-00000320  24 eb 0c f3 4f 56 04 e3  54 b0 a8 e4 bb af 3a 44  |$...OV..T.....:D|
-00000330  c7 d6 f0 24 2f fc e6 79  93 f4 4e ec c5 1f 5b 99  |...$/..y..N...[.|
-00000340  32 37 c2 f1 ad                                    |27...|
+00000000  16 03 01 01 fd 0b 00 01  f9 00 01 f6 00 01 f3 30  |...............0|
+00000010  82 01 ef 30 82 01 58 a0  03 02 01 02 02 10 5c 19  |...0..X.......\.|
+00000020  c1 89 65 83 55 6f dc 0b  c9 b9 93 9f e9 bc 30 0d  |..e.Uo........0.|
+00000030  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 30 12 31  |..*.H........0.1|
+00000040  10 30 0e 06 03 55 04 0a  13 07 41 63 6d 65 20 43  |.0...U....Acme C|
+00000050  6f 30 1e 17 0d 31 36 30  38 31 37 32 31 35 32 33  |o0...16081721523|
+00000060  31 5a 17 0d 31 37 30 38  31 37 32 31 35 32 33 31  |1Z..170817215231|
+00000070  5a 30 12 31 10 30 0e 06  03 55 04 0a 13 07 41 63  |Z0.1.0...U....Ac|
+00000080  6d 65 20 43 6f 30 81 9f  30 0d 06 09 2a 86 48 86  |me Co0..0...*.H.|
+00000090  f7 0d 01 01 01 05 00 03  81 8d 00 30 81 89 02 81  |...........0....|
+000000a0  81 00 ba 6f aa 86 bd cf  bf 9f f2 ef 5c 94 60 78  |...o........\.`x|
+000000b0  6f e8 13 f2 d1 96 6f cd  d9 32 6e 22 37 ce 41 f9  |o.....o..2n"7.A.|
+000000c0  ca 5d 29 ac e1 27 da 61  a2 ee 81 cb 10 c7 df 34  |.])..'.a.......4|
+000000d0  58 95 86 e9 3d 19 e6 5c  27 73 60 c8 8d 78 02 f4  |X...=..\'s`..x..|
+000000e0  1d a4 98 09 a3 19 70 69  3c 25 62 66 2a ab 22 23  |......pi<%bf*."#|
+000000f0  c5 7b 85 38 4f 2e 09 73  32 a7 bd 3e 9b ad ca 84  |.{.8O..s2..>....|
+00000100  07 e6 0f 3a ff 77 c5 9d  41 85 00 8a b6 9b ee b0  |...:.w..A.......|
+00000110  a4 3f 2d 4c 4c e6 42 3e  bb 51 c8 dd 48 54 f4 0c  |.?-LL.B>.Q..HT..|
+00000120  8e 47 02 03 01 00 01 a3  46 30 44 30 0e 06 03 55  |.G......F0D0...U|
+00000130  1d 0f 01 01 ff 04 04 03  02 05 a0 30 13 06 03 55  |...........0...U|
+00000140  1d 25 04 0c 30 0a 06 08  2b 06 01 05 05 07 03 01  |.%..0...+.......|
+00000150  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 0f  |0...U.......0.0.|
+00000160  06 03 55 1d 11 04 08 30  06 87 04 7f 00 00 01 30  |..U....0.......0|
+00000170  0d 06 09 2a 86 48 86 f7  0d 01 01 0b 05 00 03 81  |...*.H..........|
+00000180  81 00 46 ab 44 a2 fb 28  54 f8 5a 67 f8 62 94 f1  |..F.D..(T.Zg.b..|
+00000190  9a b2 18 9e f2 b1 de 1d  7e 6f 76 95 a9 ba e7 5d  |........~ov....]|
+000001a0  a8 16 6c 9c f7 09 d3 37  e4 4b 2b 36 7c 01 ad 41  |..l....7.K+6|..A|
+000001b0  d2 32 d8 c3 d2 93 f9 10  6b 8e 95 b9 2c 17 8a a3  |.2......k...,...|
+000001c0  44 48 bc 59 13 83 16 04  88 a4 81 5c 25 0d 98 0c  |DH.Y.......\%...|
+000001d0  ac 11 b1 28 56 be 1d cd  61 62 84 09 bf d6 80 c6  |...(V...ab......|
+000001e0  45 8d 82 2c b4 d8 83 9b  db c9 22 b7 2a 12 11 7b  |E..,......".*..{|
+000001f0  fa 02 3b c1 c9 ff ea c9  9d a8 49 d3 95 d7 d5 0e  |..;.......I.....|
+00000200  e5 35 16 03 01 00 25 10  00 00 21 20 2f e5 7d a3  |.5....%...! /.}.|
+00000210  47 cd 62 43 15 28 da ac  5f bb 29 07 30 ff f6 84  |G.bC.(.._.).0...|
+00000220  af c4 cf c2 ed 90 99 5f  58 cb 3b 74 16 03 01 00  |......._X.;t....|
+00000230  86 0f 00 00 82 00 80 12  76 af 25 e4 e7 ff d6 e4  |........v.%.....|
+00000240  27 58 31 0f 6b 1e 84 13  2f d0 60 80 18 c3 f8 c1  |'X1.k.../.`.....|
+00000250  f8 04 39 d4 07 05 d3 96  e2 b2 10 de 1f 68 88 67  |..9..........h.g|
+00000260  1d dd 0a 11 52 9d 16 0e  af 07 de cb f1 7c f4 b4  |....R........|..|
+00000270  5d 0f 4f 43 5b 3c 25 07  32 13 f2 ab 9b 2d d0 a8  |].OC[<%.2....-..|
+00000280  28 90 cb 04 48 c3 43 bd  2b b4 ef b9 7b cd bd d5  |(...H.C.+...{...|
+00000290  bc d1 cc 00 17 46 fa 9b  1f 44 58 b7 6c de 1b 7a  |.....F...DX.l..z|
+000002a0  e0 d7 12 38 a3 09 f8 7a  9b 26 0b ee 37 bc 79 1b  |...8...z.&..7.y.|
+000002b0  51 9f 9a 1f f9 a9 51 14  03 01 00 01 01 16 03 01  |Q.....Q.........|
+000002c0  00 30 97 df fb 79 78 a8  27 fd 2b 68 6b ec 4d 29  |.0...yx.'.+hk.M)|
+000002d0  a1 02 59 ae 18 0b 46 62  af 61 53 2f 95 50 f2 ac  |..Y...Fb.aS/.P..|
+000002e0  c8 c3 5e 78 ca b0 e2 5d  ff d7 1b 9b 00 30 f6 da  |..^x...].....0..|
+000002f0  d7 91                                             |..|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 24 75 ac 09 a6 28  |..........$u...(|
-00000010  60 ce 7f 81 a2 75 86 af  84 95 dc 3f e1 07 1c 02  |`....u.....?....|
-00000020  bc 3c 90 db 1e 1a 35 06  93 60 22 69 b9 05 bb     |.<....5..`"i...|
+00000000  14 03 01 00 01 01 16 03  01 00 30 f9 e9 d7 8c 4a  |..........0....J|
+00000010  6b f4 c9 88 d6 98 70 53  13 fc 51 9c 81 14 cf 71  |k.....pS..Q....q|
+00000020  d9 30 7a d9 2c 34 96 00  a4 a0 2b 1e 7d ff d0 f2  |.0z.,4....+.}...|
+00000030  b7 81 ed 86 c5 e1 09 16  82 47 20                 |.........G |
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 1a f4 67 a7  d8 0a 67 8d 3a 11 53 7e  |......g...g.:.S~|
-00000010  49 91 bf 92 85 e0 35 24  25 72 92 26 63 9b 09 15  |I.....5$%r.&c...|
-00000020  03 01 00 16 98 bb a0 ca  40 70 26 6f 2d 73 35 3d  |........ at p&o-s5=|
-00000030  90 8c ff 01 e0 b1 50 52  e3 57                    |......PR.W|
+00000000  17 03 01 00 20 db b5 66  4e fb b1 47 8a 8e 6b a8  |.... ..fN..G..k.|
+00000010  03 53 1a 51 22 8e 47 a3  3a 74 ed a4 6a aa 79 fd  |.S.Q".G.:t..j.y.|
+00000020  55 0f ac 35 a9 17 03 01  00 20 3e 0b 39 f5 5a 03  |U..5..... >.9.Z.|
+00000030  43 d9 e2 7d 1c dc 3b 42  82 2a 2d d4 04 0a 76 97  |C..}..;B.*-...v.|
+00000040  70 ed ee 99 58 15 40 c1  3a d5 15 03 01 00 20 bf  |p...X. at .:..... .|
+00000050  ea e8 93 67 a4 91 1a b5  f5 03 a5 94 50 95 41 16  |...g........P.A.|
+00000060  b0 2a 74 d9 32 65 94 35  45 b9 0f 2e 80 87 fd     |.*t.2e.5E......|
diff --git a/src/crypto/tls/testdata/Client-TLSv10-ECDHE-ECDSA-AES b/src/crypto/tls/testdata/Client-TLSv10-ECDHE-ECDSA-AES
index 929e1c6..529b7ce 100644
--- a/src/crypto/tls/testdata/Client-TLSv10-ECDHE-ECDSA-AES
+++ b/src/crypto/tls/testdata/Client-TLSv10-ECDHE-ECDSA-AES
@@ -1,19 +1,20 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 59 02 00 00  55 03 01 78 09 57 86 09  |....Y...U..x.W..|
-00000010  64 7e 35 c7 c7 b9 44 9c  09 ae f0 49 cb 1c 1f 58  |d~5...D....I...X|
-00000020  89 ef 65 16 9e 32 73 cd  4d 1b 8f 20 10 4d 5b cf  |..e..2s.M.. .M[.|
-00000030  d0 24 59 dd e8 47 c9 a2  ad 9c 98 b5 eb 16 46 6b  |.$Y..G........Fk|
-00000040  7d 33 6e 53 0a 3d 81 71  a1 bc 43 7a c0 09 00 00  |}3nS.=.q..Cz....|
+00000000  16 03 01 00 59 02 00 00  55 03 01 b0 ec 4b 2e 9e  |....Y...U....K..|
+00000010  19 7d 7b 7e 7c 52 8a d2  2e 8a 97 05 8a c6 ae aa  |.}{~|R..........|
+00000020  c5 62 bd 6f bd 7e fe 6b  c6 9f d4 20 74 db 02 b1  |.b.o.~.k... t...|
+00000030  65 88 41 bb 9a 55 22 f3  01 c4 5c ca 39 86 b1 77  |e.A..U"...\.9..w|
+00000040  c4 b3 45 16 eb 55 d8 15  b8 4d ac 12 c0 09 00 00  |..E..U...M......|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  01 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
@@ -48,41 +49,37 @@
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 01 00 d6 0c 00  00 d2 03 00 17 41 04 51  |*............A.Q|
-00000280  39 70 43 9c 01 de 29 df  3c d0 f8 31 54 70 34 53  |9pC...).<..1Tp4S|
-00000290  0e ab e8 e0 b0 8b 21 66  63 ac a9 68 7c 92 6f f8  |......!fc..h|.o.|
-000002a0  cf a3 ba cb 6d 39 f4 5c  f5 2e ff 1d d7 1b b9 e7  |....m9.\........|
-000002b0  08 13 59 f8 64 7e 23 e0  1d 04 cf 37 47 d6 b7 00  |..Y.d~#....7G...|
-000002c0  8b 30 81 88 02 42 01 cd  1d 01 46 68 da 4c b6 0d  |.0...B....Fh.L..|
-000002d0  67 05 39 0d aa 6c c5 40  e4 5d bf 4f 2a 92 78 8d  |g.9..l. at .].O*.x.|
-000002e0  08 0e c0 07 8c 68 cc 55  4e 54 a9 9d 22 f9 a6 4a  |.....h.UNT.."..J|
-000002f0  e4 38 9f 53 4a 60 e8 eb  81 02 50 75 7e 13 31 2a  |.8.SJ`....Pu~.1*|
-00000300  ff 3e 17 cd b4 d1 d4 75  02 42 01 95 ba b6 a0 12  |.>.....u.B......|
-00000310  23 59 9f ae 1c c0 60 d2  8f 59 6b 35 ee b3 3f ac  |#Y....`..Yk5..?.|
-00000320  e4 42 9a 23 d0 f4 fd a1  3c 36 1b 31 33 76 8d f0  |.B.#....<6.13v..|
-00000330  b6 66 fd 92 9a 2a 27 8b  06 11 72 41 09 bd 27 55  |.f...*'...rA..'U|
-00000340  c7 1b a9 d1 49 5e 8f 85  dc aa 9d be 16 03 01 00  |....I^..........|
-00000350  04 0e 00 00 00                                    |.....|
+00000270  2a 16 03 01 00 b5 0c 00  00 b1 03 00 1d 20 7d 74  |*............ }t|
+00000280  bf aa a8 b6 c0 1f 78 0c  1a ee c5 b7 56 ff 5c aa  |......x.....V.\.|
+00000290  f4 e3 a5 0c f7 64 31 eb  85 8a c9 bd 05 1b 00 8b  |.....d1.........|
+000002a0  30 81 88 02 42 00 f8 5d  e5 bf 2e 70 79 f4 36 90  |0...B..]...py.6.|
+000002b0  fc 6e 9a cc f1 c4 01 50  8c b9 92 4e bd e0 82 2d  |.n.....P...N...-|
+000002c0  1b ab 30 71 d1 db 76 af  50 75 08 fb cb 50 5b 00  |..0q..v.Pu...P[.|
+000002d0  49 72 f5 d7 d9 44 48 94  ac 1d 8d 2e 50 90 ad a3  |Ir...DH.....P...|
+000002e0  42 2b 5f 57 48 5e 9e 02  42 00 bb 0b 9a d7 25 53  |B+_WH^..B.....%S|
+000002f0  04 5c 58 01 07 8e 3d ee  f5 4f 0b 80 bd 02 07 3e  |.\X...=..O.....>|
+00000300  ff b9 01 ac 7a 49 be 94  fa cf 58 5c 59 91 b5 5d  |....zI....X\Y..]|
+00000310  cc 61 b9 e3 2f 53 7d 3c  3f 41 c5 31 1a 90 fc fa  |.a../S}<?A.1....|
+00000320  f7 0b eb e9 01 17 ab 23  ab 28 63 16 03 01 00 04  |.......#.(c.....|
+00000330  0e 00 00 00                                       |....|
 >>> Flow 3 (client to server)
-00000000  16 03 01 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 01 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 01 00 30 64 61  7f ea 98 8e e7 c9 0f ea  |.....0da........|
-00000060  0a b3 52 ba 3d 01 36 a4  47 24 7b 2d 19 b5 7e 92  |..R.=.6.G${-..~.|
-00000070  04 b7 8c 4f fc 02 5d 79  15 3e 50 72 05 3c df d2  |...O..]y.>Pr.<..|
-00000080  c6 a3 b3 c8 7c 48                                 |....|H|
+00000000  16 03 01 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 01 00 01 01  |....._X.;t......|
+00000030  16 03 01 00 30 f5 d0 86  ef 96 7e b9 94 cc 19 62  |....0.....~....b|
+00000040  cc 3a 14 f1 74 a2 0d c8  b9 4c 5d 8a c5 80 60 23  |.:..t....L]...`#|
+00000050  d5 f5 04 06 16 e2 69 ca  4d 99 1b a0 b5 3b 7d 62  |......i.M....;}b|
+00000060  51 62 ee d9 60                                    |Qb..`|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 7d 49 8d e9 da  |..........0}I...|
-00000010  87 77 18 4d 10 63 17 ed  1f 34 7a d4 be e3 dd b6  |.w.M.c...4z.....|
-00000020  8b f3 a7 06 bc de 76 8e  04 be 8a 95 5b 24 19 ec  |......v.....[$..|
-00000030  66 55 8a 1b e0 df 0b a1  57 cb 67                 |fU......W.g|
+00000000  14 03 01 00 01 01 16 03  01 00 30 4d f9 c2 63 4f  |..........0M..cO|
+00000010  98 1b 02 ce df ca d1 15  a2 4f 6f 4c 2c b8 1a 88  |.........OoL,...|
+00000020  11 c9 b3 45 e6 1d cf e8  6b dd 8c 89 c6 1d 0b 66  |...E....k......f|
+00000030  82 d5 1d c6 55 14 1c 56  59 3e 69                 |....U..VY>i|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 20 2d a3 e5  55 13 3f 73 8e ba 41 79  |.... -..U.?s..Ay|
-00000010  65 e0 83 d5 3a ea cd e9  a8 b4 4b 3c d0 0c bf 06  |e...:.....K<....|
-00000020  75 2a 67 f2 f7 17 03 01  00 20 a0 8d 3c a2 ca b3  |u*g...... ..<...|
-00000030  f3 e5 36 dc 44 a4 3b ad  cd 03 be a9 70 a8 75 51  |..6.D.;.....p.uQ|
-00000040  0f 8e 9f 7c a7 3c 03 84  38 88 15 03 01 00 20 75  |...|.<..8..... u|
-00000050  0f db fe 48 b4 7e 04 3b  f5 5b 47 5b 0a ab 69 18  |...H.~.;.[G[..i.|
-00000060  37 bb 89 d3 a8 40 ba 53  3b 5f 6d 8b 06 ff ae     |7.... at .S;_m....|
+00000000  17 03 01 00 20 12 be 42  b4 31 07 55 8e f9 a1 64  |.... ..B.1.U...d|
+00000010  96 70 46 68 3e fd 4e 4f  9c af b3 11 de fc 80 f1  |.pFh>.NO........|
+00000020  c8 11 84 ba ae 17 03 01  00 20 2f f9 ec dd 50 97  |......... /...P.|
+00000030  1e a4 f1 66 fe 28 e3 c1  51 8d c0 f6 c3 d8 b3 ad  |...f.(..Q.......|
+00000040  7d dc a5 98 87 90 34 71  b4 73 15 03 01 00 20 d1  |}.....4q.s.... .|
+00000050  6f 91 91 01 68 c4 11 6a  e5 a2 ed 20 3f 3a 3d b7  |o...h..j... ?:=.|
+00000060  d9 7f c3 b3 29 c3 df 3e  17 69 76 9f 04 f8 58     |....)..>.iv...X|
diff --git a/src/crypto/tls/testdata/Client-TLSv10-ECDHE-RSA-AES b/src/crypto/tls/testdata/Client-TLSv10-ECDHE-RSA-AES
index 0b481ea..78947ac 100644
--- a/src/crypto/tls/testdata/Client-TLSv10-ECDHE-RSA-AES
+++ b/src/crypto/tls/testdata/Client-TLSv10-ECDHE-RSA-AES
@@ -1,93 +1,89 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 59 02 00 00  55 03 01 90 3d f6 98 16  |....Y...U...=...|
-00000010  55 0f 73 94 05 96 4c ab  ad f4 98 7a db c5 ca 26  |U.s...L....z...&|
-00000020  1b c8 d9 15 a8 79 8e 2b  10 67 54 20 b2 8e 45 24  |.....y.+.gT ..E$|
-00000030  6d 82 ec f5 30 41 2e 32  10 fa c0 76 3f 84 81 39  |m...0A.2...v?..9|
-00000040  1e 5d 98 c1 33 d9 0d 4f  21 e1 0d 47 c0 13 00 00  |.]..3..O!..G....|
+00000000  16 03 01 00 59 02 00 00  55 03 01 45 04 14 a2 70  |....Y...U..E...p|
+00000010  3e 1e d9 2c d4 bd f3 e8  9c f3 e0 08 d8 0f 7f 82  |>..,............|
+00000020  2b 07 a0 bd 47 56 a0 e1  06 0d 36 20 fc 0f 5b 85  |+...GV....6 ..[.|
+00000030  8e 17 20 f1 f6 1e 80 c3  79 1a e1 86 c3 ed e9 24  |.. .....y......$|
+00000040  6d bb 24 3c 0c 8d 2c 79  f2 03 27 b0 c0 13 00 00  |m.$<..,y..'.....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
-00000060  01 02 71 0b 00 02 6d 00  02 6a 00 02 67 30 82 02  |..q...m..j..g0..|
-00000070  63 30 82 01 cc a0 03 02  01 02 02 09 00 a2 73 00  |c0............s.|
-00000080  0c 81 00 cb f3 30 0d 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000090  01 0b 05 00 30 2b 31 17  30 15 06 03 55 04 0a 13  |....0+1.0...U...|
-000000a0  0e 47 6f 6f 67 6c 65 20  54 45 53 54 49 4e 47 31  |.Google TESTING1|
-000000b0  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
-000000c0  74 30 1e 17 0d 31 35 30  31 30 31 30 30 30 30 30  |t0...15010100000|
-000000d0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
-000000e0  5a 30 26 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |Z0&1.0...U....Go|
-000000f0  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 0b 30 09  |ogle TESTING1.0.|
-00000100  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
-00000110  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
-00000120  81 89 02 81 81 00 af 87  88 f6 20 1b 95 65 6c 14  |.......... ..el.|
-00000130  ab 44 05 af 3b 45 14 e3  b7 6d fd 00 63 4d 95 7f  |.D..;E...m..cM..|
-00000140  fe 6a 62 35 86 c0 4a f9  18 7c f6 aa 25 5e 7a 64  |.jb5..J..|..%^zd|
-00000150  31 66 00 ba f4 8e 92 af  c7 6b d8 76 d4 f3 5f 41  |1f.......k.v.._A|
-00000160  cb 6e 56 15 97 1b 97 c1  3c 12 39 21 66 3d 2b 16  |.nV.....<.9!f=+.|
-00000170  d1 bc db 1c c0 a7 da b7  ca ad ba da cb d5 21 50  |..............!P|
-00000180  ec de 8d ab d1 6b 81 4b  89 02 f3 c4 be c1 6c 89  |.....k.K......l.|
-00000190  b1 44 84 bd 21 d1 04 7d  9d 16 4d f9 82 15 f6 ef  |.D..!..}..M.....|
-000001a0  fa d6 09 47 f2 fb 02 03  01 00 01 a3 81 93 30 81  |...G..........0.|
-000001b0  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
-000001c0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
-000001d0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
-000001e0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
-000001f0  06 03 55 1d 0e 04 12 04  10 12 50 8d 89 6f 1b d1  |..U.......P..o..|
-00000200  dc 54 4d 6e cb 69 5e 06  f4 30 1b 06 03 55 1d 23  |.TMn.i^..0...U.#|
-00000210  04 14 30 12 80 10 bf 3d  b6 a9 66 f2 b8 40 cf ea  |..0....=..f.. at ..|
-00000220  b4 03 78 48 1a 41 30 19  06 03 55 1d 11 04 12 30  |..xH.A0...U....0|
-00000230  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
-00000240  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
-00000250  03 81 81 00 92 7c af 91  55 12 18 96 59 31 a6 48  |.....|..U...Y1.H|
-00000260  40 d5 2d d5 ee bb 02 a0  f5 c2 1e 7c 9b b3 30 7d  |@.-........|..0}|
-00000270  3c dc 76 da 4f 3d c0 fa  ae 2d 33 24 6b 03 7b 1b  |<.v.O=...-3$k.{.|
-00000280  67 59 11 21 b5 11 bc 77  b9 d9 e0 6e a8 2d 2e 35  |gY.!...w...n.-.5|
-00000290  fa 64 5f 22 3e 63 10 6b  be ff 14 86 6d 0d f0 15  |.d_">c.k....m...|
-000002a0  31 a8 14 38 1e 3b 84 87  2c cb 98 ed 51 76 b9 b1  |1..8.;..,...Qv..|
-000002b0  4f dd db 9b 84 04 86 40  fa 51 dd ba b4 8d eb e3  |O...... at .Q......|
-000002c0  46 de 46 b9 4f 86 c7 f9  a4 c2 41 34 ac cc f6 ea  |F.F.O.....A4....|
-000002d0  b0 ab 39 18 16 03 01 00  cb 0c 00 00 c7 03 00 17  |..9.............|
-000002e0  41 04 d9 ae 3f 05 64 d3  77 d9 1d b8 37 8a d4 ac  |A...?.d.w...7...|
-000002f0  51 f4 af 65 70 da c0 64  76 00 53 50 a2 d4 6c bc  |Q..ep..dv.SP..l.|
-00000300  9c 62 ab 2f 7b 02 48 fe  b2 0d 0b bb be 8f 34 55  |.b./{.H.......4U|
-00000310  fb ce ee 93 43 76 d5 ce  3b b5 79 ab 3d 74 6e 19  |....Cv..;.y.=tn.|
-00000320  a9 7d 00 80 05 cf 57 f2  f7 e0 ad 71 f1 75 d0 8b  |.}....W....q.u..|
-00000330  f5 9d 83 1a 7e 0a 71 10  d7 9e fe bd 9d 47 62 45  |....~.q......GbE|
-00000340  8d 1b 9c 33 fa 2c 5c aa  ce 9e 62 dc ad 56 ac 87  |...3.,\...b..V..|
-00000350  84 54 f5 32 87 d1 bb 8b  d9 d7 6d 3c 6c 6d b7 79  |.T.2......m<lm.y|
-00000360  05 4d 55 f1 7c ef b1 fc  e7 35 5d 41 66 60 44 4f  |.MU.|....5]Af`DO|
-00000370  f3 dd de 25 f4 73 12 c2  b6 cc 61 d5 14 5a ff 88  |...%.s....a..Z..|
-00000380  ae f5 04 62 ac 2d 10 a0  95 c1 8e fa e6 db fe 41  |...b.-.........A|
-00000390  46 98 f1 3d 2e e3 2a 5a  ea 87 26 6e 7a 4f 38 6c  |F..=..*Z..&nzO8l|
-000003a0  4b 1f 1b 56 16 03 01 00  04 0e 00 00 00           |K..V.........|
+00000060  01 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 01 00  |.=.`.\!.;.......|
+000002c0  aa 0c 00 00 a6 03 00 1d  20 85 05 5f e3 a2 b2 12  |........ .._....|
+000002d0  c8 82 53 2b c2 38 e1 a8  08 87 a7 d5 b3 98 6f 81  |..S+.8........o.|
+000002e0  ce 81 6b 78 3e 3a b7 1d  71 00 80 43 81 fb 47 5e  |..kx>:..q..C..G^|
+000002f0  08 16 39 35 d3 c2 f3 ea  bb 2c 7d bc 01 9b 35 5d  |..95.....,}...5]|
+00000300  63 7e c3 38 f3 04 96 eb  d7 3f d1 df 71 97 ec 22  |c~.8.....?..q.."|
+00000310  1b 4a 89 14 4d e5 44 08  87 52 69 ea 28 f8 6a ea  |.J..M.D..Ri.(.j.|
+00000320  3e ff 17 de 4d 20 95 e3  6e 3f af 05 20 9b a3 ac  |>...M ..n?.. ...|
+00000330  70 1b 1c bf f9 52 d6 11  6d d9 85 90 08 4d 64 1f  |p....R..m....Md.|
+00000340  c5 35 34 37 11 b8 44 a3  ef 93 a6 b6 87 58 0b c4  |.547..D......X..|
+00000350  8e 94 d8 67 4d 09 7a 2a  aa 95 db e6 af 29 21 a2  |...gM.z*.....)!.|
+00000360  ee c3 90 ef c6 53 46 12  fb 87 06 16 03 01 00 04  |.....SF.........|
+00000370  0e 00 00 00                                       |....|
 >>> Flow 3 (client to server)
-00000000  16 03 01 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 01 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 01 00 30 73 96  2d 54 e3 9a bc 54 f5 9e  |.....0s.-T...T..|
-00000060  e5 c7 46 35 b8 e1 d6 f6  14 95 92 f1 95 81 5a 9d  |..F5..........Z.|
-00000070  4b df cc 96 77 f2 39 60  5d 5d da 94 b0 bf a0 80  |K...w.9`]]......|
-00000080  bd 28 55 b1 6a c3                                 |.(U.j.|
+00000000  16 03 01 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 01 00 01 01  |....._X.;t......|
+00000030  16 03 01 00 30 50 f5 a9  34 25 ed a2 fb c8 7f 35  |....0P..4%.....5|
+00000040  08 57 59 da 54 c1 8d 92  ec 23 73 af f3 92 8d 19  |.WY.T....#s.....|
+00000050  03 ce ab 5b eb dc 5b 81  3f 51 a1 20 31 3f 33 da  |...[..[.?Q. 1?3.|
+00000060  27 c5 c3 9c fd                                    |'....|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 c9 46 7a 8b be  |..........0.Fz..|
-00000010  cd eb 5c 83 13 9c 9b 9f  70 84 38 3b 48 8c f4 11  |..\.....p.8;H...|
-00000020  b3 ca 10 09 38 d0 8e c8  9f 66 db b9 8a 95 15 6b  |....8....f.....k|
-00000030  5e f8 1d 39 25 75 3d f1  b9 32 a3                 |^..9%u=..2.|
+00000000  14 03 01 00 01 01 16 03  01 00 30 b1 61 9b 63 4e  |..........0.a.cN|
+00000010  43 96 80 49 ac 2d 93 7d  b9 f2 bb 81 79 5e 94 bf  |C..I.-.}....y^..|
+00000020  06 d0 a6 14 46 91 cd 90  b0 8a 85 ee fe 41 a7 4d  |....F........A.M|
+00000030  97 d7 4d 40 5e f4 5b bd  d3 0c db                 |..M@^.[....|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 20 04 69 a9  01 42 f4 1a fd 5a 4e 12  |.... .i..B...ZN.|
-00000010  2b 6d cd 68 6b 94 70 b2  80 07 cf 79 a4 43 69 bf  |+m.hk.p....y.Ci.|
-00000020  27 25 b5 ae e7 17 03 01  00 20 bf 1e cd 83 64 af  |'%....... ....d.|
-00000030  6f cc 89 21 bf 16 e7 e8  86 29 f3 0a 36 ab a4 e3  |o..!.....)..6...|
-00000040  fa c0 7e 7a 78 ca 29 17  11 9c 15 03 01 00 20 94  |..~zx.)....... .|
-00000050  7a dd 17 eb fd 67 b1 cc  58 c9 c3 ae db b6 b0 a4  |z....g..X.......|
-00000060  68 15 36 ca 33 22 ec 03  fb cf 2f f5 70 d6 9d     |h.6.3"..../.p..|
+00000000  17 03 01 00 20 49 21 bc  a5 4c 96 41 3f 22 87 0a  |.... I!..L.A?"..|
+00000010  c0 4e 0e 54 cb c2 27 8a  4f b0 37 fb b4 1f c1 4e  |.N.T..'.O.7....N|
+00000020  77 e1 09 57 23 17 03 01  00 20 f0 f0 3b 78 a8 ae  |w..W#.... ..;x..|
+00000030  ef b1 e0 f4 29 0f 90 4a  0f e5 48 34 84 5e 4f d8  |....)..J..H4.^O.|
+00000040  53 46 f8 29 64 2b 8e 87  79 0a 15 03 01 00 20 71  |SF.)d+..y..... q|
+00000050  32 6c 08 2a f7 18 c8 d5  48 a8 c7 d1 68 7a 65 ec  |2l.*....H...hze.|
+00000060  3e fa 4b fe ff 76 1a 57  64 22 61 27 a0 5d b6     |>.K..v.Wd"a'.].|
diff --git a/src/crypto/tls/testdata/Client-TLSv10-RSA-RC4 b/src/crypto/tls/testdata/Client-TLSv10-RSA-RC4
index 57eb930..7ecfbde 100644
--- a/src/crypto/tls/testdata/Client-TLSv10-RSA-RC4
+++ b/src/crypto/tls/testdata/Client-TLSv10-RSA-RC4
@@ -1,79 +1,78 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 51 02 00 00  4d 03 01 1c 0e e9 7a c6  |....Q...M.....z.|
-00000010  91 fe 7e 8c 6f 0b 8e cf  23 f5 07 29 10 de 05 a6  |..~.o...#..)....|
-00000020  20 72 11 65 4f 2b 45 95  96 02 62 20 43 a8 93 34  | r.eO+E...b C..4|
-00000030  e7 c0 29 d5 fb 26 f9 c2  59 37 94 dc e6 b5 c4 ed  |..)..&..Y7......|
-00000040  ae 7a d7 94 d1 f4 d8 0b  02 ad 20 1b 00 05 00 00  |.z........ .....|
-00000050  05 ff 01 00 01 00 16 03  01 02 71 0b 00 02 6d 00  |..........q...m.|
-00000060  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000070  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000090  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-000000a0  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-000000b0  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000c0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000d0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000e0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000f0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-00000100  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-00000110  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000120  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000130  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000140  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000150  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000160  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000170  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000180  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000190  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-000001a0  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-000001b0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001c0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001d0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001e0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001f0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-00000200  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-00000210  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000220  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000230  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000240  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000250  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000260  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000270  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000280  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000290  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-000002a0  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-000002b0  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002c0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 01 00  |..A4......9.....|
-000002d0  04 0e 00 00 00                                    |.....|
+00000000  16 03 01 00 51 02 00 00  4d 03 01 4c 98 ce a5 80  |....Q...M..L....|
+00000010  84 dc d3 70 de 75 bf 26  5c 15 8b b7 2c 78 30 a7  |...p.u.&\...,x0.|
+00000020  65 1a 0c f7 1a e5 51 91  7c cb ca 20 83 2c 90 3b  |e.....Q.|.. .,.;|
+00000030  cf dd 4e 51 8b 27 98 95  aa d9 1d da 4d 3d e1 18  |..NQ.'......M=..|
+00000040  f5 58 fd 85 c5 ed c9 5f  12 2f 4b b3 00 05 00 00  |.X....._./K.....|
+00000050  05 ff 01 00 01 00 16 03  01 02 59 0b 00 02 55 00  |..........Y...U.|
+00000060  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000070  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000090  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+000000a0  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+000000b0  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000c0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000d0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000e0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000f0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+00000100  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+00000110  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000120  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000130  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000140  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000150  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000160  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000170  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000180  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000190  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+000001a0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+000001b0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001c0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001d0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001e0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001f0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+00000200  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+00000210  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000220  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000230  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000240  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000250  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000260  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000270  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000280  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000290  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+000002a0  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+000002b0  3b e9 fa e7 16 03 01 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 01 00 86 10 00 00  82 00 80 73 bd 73 65 92  |...........s.se.|
-00000010  86 23 41 14 79 7f d5 c1  10 ce 94 4d ad 9c c3 a9  |.#A.y......M....|
-00000020  87 b5 32 52 f8 6b 11 93  2d 9b 98 0b 8b 1d c0 f6  |..2R.k..-.......|
-00000030  53 17 6d c7 9c 2e ae c9  6f cc 99 23 38 37 1a 10  |S.m.....o..#87..|
-00000040  fe 05 0b b5 55 0a 14 e9  60 7d 70 26 98 e2 54 d9  |....U...`}p&..T.|
-00000050  65 cf 2e f4 53 5f 1d aa  3a f6 33 7b eb 4c 0e b3  |e...S_..:.3{.L..|
-00000060  ff 5a db 36 2a 47 f3 df  f9 fc f5 31 78 83 aa 6b  |.Z.6*G.....1x..k|
-00000070  52 b7 ba 1a 96 bc fa c1  a1 a9 bb 2b f5 38 89 00  |R..........+.8..|
-00000080  4d e5 78 13 4e a4 38 46  42 dc 16 14 03 01 00 01  |M.x.N.8FB.......|
-00000090  01 16 03 01 00 24 ae a9  da 45 6b 5e 76 57 02 62  |.....$...Ek^vW.b|
-000000a0  63 d4 1f 40 bf c9 47 27  a9 7a 24 c0 f0 e9 c2 c4  |c.. at ..G'.z$.....|
-000000b0  9c 07 84 df ae c7 66 40  d2 b0                    |......f at ..|
+00000000  16 03 01 00 86 10 00 00  82 00 80 b9 65 8d bf a7  |............e...|
+00000010  c8 4b 79 ce 6f cb 8b 13  1c ac b9 7d 66 5e e9 ba  |.Ky.o......}f^..|
+00000020  1d 71 4e a9 e9 34 ae f6  64 65 90 3b d8 16 52 a2  |.qN..4..de.;..R.|
+00000030  6f f4 cb 8a 13 74 a2 ee  b7 27 69 b4 41 c0 90 68  |o....t...'i.A..h|
+00000040  bc 02 69 e1 c6 48 4f 39  36 30 25 ca 4c 17 ce 83  |..i..HO960%.L...|
+00000050  9e 08 56 e3 05 49 93 9e  2e c4 fb e6 c8 01 f1 0f  |..V..I..........|
+00000060  c5 70 0f 08 83 48 e9 48  ef 6e 50 8b 05 7e e5 84  |.p...H.H.nP..~..|
+00000070  25 fa 55 c7 ae 31 02 27  00 ef 3f 98 86 20 12 89  |%.U..1.'..?.. ..|
+00000080  91 59 28 b4 f7 d7 af d2  69 61 35 14 03 01 00 01  |.Y(.....ia5.....|
+00000090  01 16 03 01 00 24 0e 4d  ff 2c 39 80 ba a5 96 18  |.....$.M.,9.....|
+000000a0  56 15 94 9f e2 1e 7d 13  62 51 d5 e1 05 f8 d8 b3  |V.....}.bQ......|
+000000b0  bd 77 58 38 95 b4 7d 37  66 8a                    |.wX8..}7f.|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 24 e9 84 92 41 c5  |..........$...A.|
-00000010  31 e1 3c a9 78 18 d1 7b  e1 b1 0b 0a ef 18 54 19  |1.<.x..{......T.|
-00000020  7c ba c7 59 ca c8 7b 4d  c9 f4 ad d6 7b 77 fb     ||..Y..{M....{w.|
+00000000  14 03 01 00 01 01 16 03  01 00 24 dc 6f da 57 0d  |..........$.o.W.|
+00000010  f8 b8 aa d5 e5 0a 2e 81  ed 2a b7 f8 0e 2a f1 05  |.........*...*..|
+00000020  76 8d 4f b0 0e db 16 c5  d7 c8 5e f9 fb 9e e0     |v.O.......^....|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 1a 1a dc 95  e2 4f ec f1 f6 68 9d 15  |.........O...h..|
-00000010  56 d5 7b 06 1a f5 be bb  b1 ca b2 a6 d3 9e 28 15  |V.{...........(.|
-00000020  03 01 00 16 64 fe 4a 37  d3 32 a8 55 38 9e 0f 76  |....d.J7.2.U8..v|
-00000030  50 de e2 2e aa 77 15 2b  e5 21                    |P....w.+.!|
+00000000  17 03 01 00 1a 47 97 4d  e6 59 d4 2f bb 60 56 69  |.....G.M.Y./.`Vi|
+00000010  d8 bc 8d 91 44 7c cd 85  7e c5 18 5f 57 8e 08 15  |....D|..~.._W...|
+00000020  03 01 00 16 f7 79 56 72  e6 77 8d af 94 55 d7 0e  |.....yVr.w...U..|
+00000030  96 c8 3b 35 52 ea f7 e7  b8 d6                    |..;5R.....|
diff --git a/src/crypto/tls/testdata/Client-TLSv11-ECDHE-ECDSA-AES b/src/crypto/tls/testdata/Client-TLSv11-ECDHE-ECDSA-AES
index 87fbe3f..5232ad5 100644
--- a/src/crypto/tls/testdata/Client-TLSv11-ECDHE-ECDSA-AES
+++ b/src/crypto/tls/testdata/Client-TLSv11-ECDHE-ECDSA-AES
@@ -1,19 +1,20 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 02 00 59 02 00 00  55 03 02 07 ae a6 e4 1a  |....Y...U.......|
-00000010  f7 7a 0c bc ea 21 0e 86  e3 d0 b4 2c fc d9 97 a3  |.z...!.....,....|
-00000020  8b 29 5f 59 3e a9 06 fb  ca d9 57 20 cd 45 e7 cd  |.)_Y>.....W .E..|
-00000030  6c 4c 56 cd 7c 4c 51 2c  8f 8c 67 a2 05 51 26 f5  |lLV.|LQ,..g..Q&.|
-00000040  17 cc 18 c2 a1 29 94 4b  e2 02 cc 1c c0 09 00 00  |.....).K........|
+00000000  16 03 02 00 59 02 00 00  55 03 02 30 f8 f6 b2 af  |....Y...U..0....|
+00000010  99 b0 e0 f6 9a eb 47 6f  2f ad 2f 45 18 56 b3 dd  |......Go/./E.V..|
+00000020  b6 b9 20 fb af 97 43 1f  0e 51 c0 20 fe 6c 12 64  |.. ...C..Q. .l.d|
+00000030  8e cc a4 24 d6 e6 80 cf  f2 9e 74 3f a7 1e 8b da  |...$......t?....|
+00000040  0e 3c 58 74 f4 8f be 1f  60 86 57 c6 c0 09 00 00  |.<Xt....`.W.....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  02 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
@@ -48,43 +49,39 @@
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 02 00 d6 0c 00  00 d2 03 00 17 41 04 cd  |*............A..|
-00000280  9d 30 75 8d 98 17 b5 1b  2f 4e af ea 69 52 a1 c1  |.0u...../N..iR..|
-00000290  86 73 6a 56 54 f8 ed b6  35 e5 4e 34 a0 6f b1 85  |.sjVT...5.N4.o..|
-000002a0  95 8e be 77 c5 1a 56 9a  59 d1 69 79 ea d6 2b c7  |...w..V.Y.iy..+.|
-000002b0  c1 4a fb bc f8 98 c3 49  1c f3 ce 33 ef 98 20 00  |.J.....I...3.. .|
-000002c0  8b 30 81 88 02 42 00 8b  15 7e 3b 4f 73 b0 8e ca  |.0...B...~;Os...|
-000002d0  67 e0 7c d8 89 70 f1 b2  6b 9c 19 84 fa aa 6e 15  |g.|..p..k.....n.|
-000002e0  8b 46 95 57 d5 ac 79 f3  e8 2a e5 7a a8 1e c3 d7  |.F.W..y..*.z....|
-000002f0  0a b2 02 cd d6 32 34 2f  37 65 41 c8 61 c6 ed e5  |.....24/7eA.a...|
-00000300  d2 6f 0f e8 1a 49 b6 c7  02 42 00 d1 00 f4 05 65  |.o...I...B.....e|
-00000310  dd 43 42 db 8b 0b 95 9d  f5 62 51 e6 58 60 20 9b  |.CB......bQ.X` .|
-00000320  46 84 e6 1f 76 4a 92 42  e4 4d 77 5b 76 a5 78 a0  |F...vJ.B.Mw[v.x.|
-00000330  b0 f0 50 7d f9 4f ca 43  9d c2 50 cb 20 1c 40 52  |..P}.O.C..P. . at R|
-00000340  0f a8 c4 43 7a 9d d5 61  de 26 30 b5 16 03 02 00  |...Cz..a.&0.....|
-00000350  04 0e 00 00 00                                    |.....|
+00000270  2a 16 03 02 00 b3 0c 00  00 af 03 00 1d 20 4b 2d  |*............ K-|
+00000280  60 48 54 b4 48 42 10 de  e1 98 f0 fb 73 d9 49 16  |`HT.HB......s.I.|
+00000290  3e a2 11 b3 84 50 de 26  00 09 d8 36 34 04 00 89  |>....P.&...64...|
+000002a0  30 81 86 02 41 24 90 6f  3e 1a 2c a5 7f 08 dc b2  |0...A$.o>.,.....|
+000002b0  d3 46 27 5e cb 1f 2a 6d  92 ba 1b fe e3 c5 64 79  |.F'^..*m......dy|
+000002c0  31 50 8c 43 4b b1 ee 0d  6f 53 ad 6f e9 db 86 e7  |1P.CK...oS.o....|
+000002d0  1f e3 77 f1 8d a8 ab 81  2a d6 fa e7 98 d5 bc 0d  |..w.....*.......|
+000002e0  ec af ea 84 c4 f8 02 41  6a d2 66 32 e1 d7 46 1a  |.......Aj.f2..F.|
+000002f0  95 5a 91 c3 76 82 20 c2  a3 a2 32 f5 fd eb a2 0e  |.Z..v. ...2.....|
+00000300  0f d8 a9 31 7a ef a8 05  6c 5d bf 27 d0 2d 94 ca  |...1z...l].'.-..|
+00000310  fb d6 62 7a 1c 6a 46 20  fe ed a6 60 a3 db b1 bd  |..bz.jF ...`....|
+00000320  11 82 05 c3 db 0c 4a 2d  6c 16 03 02 00 04 0e 00  |......J-l.......|
+00000330  00 00                                             |..|
 >>> Flow 3 (client to server)
-00000000  16 03 02 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 02 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 02 00 40 00 00  00 00 00 00 00 00 00 00  |..... at ..........|
-00000060  00 00 00 00 00 00 c0 81  e7 e8 40 f3 24 45 ed 74  |.......... at .$E.t|
-00000070  86 31 7b 39 d1 3c a2 67  99 28 06 b1 34 b6 3c a6  |.1{9.<.g.(..4.<.|
-00000080  1d ce 39 aa 56 c9 72 0d  f1 e0 c1 5a 51 a0 5d f2  |..9.V.r....ZQ.].|
-00000090  44 4d e6 d7 0e 84                                 |DM....|
+00000000  16 03 02 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 02 00 01 01  |....._X.;t......|
+00000030  16 03 02 00 40 00 00 00  00 00 00 00 00 00 00 00  |.... at ...........|
+00000040  00 00 00 00 00 22 71 28  3d 07 73 61 5e 84 72 36  |....."q(=.sa^.r6|
+00000050  c0 87 37 4a 5b c2 d9 40  96 a2 01 20 b2 04 23 2f  |..7J[.. at ... ..#/|
+00000060  c1 6f 1e 7c a1 77 20 0f  87 46 98 a2 5c aa 35 37  |.o.|.w ..F..\.57|
+00000070  37 87 5a 75 33                                    |7.Zu3|
 >>> Flow 4 (server to client)
-00000000  14 03 02 00 01 01 16 03  02 00 40 82 8d c7 e3 7b  |.......... at ....{|
-00000010  f8 9d 33 a1 c2 08 8c 24  d9 af 66 64 6e e8 61 8e  |..3....$..fdn.a.|
-00000020  3c 03 65 2d c3 64 a2 26  23 a5 25 3f a2 a4 f9 40  |<.e-.d.&#.%?...@|
-00000030  ec 9f 0e b8 57 b1 5f 84  ea 94 72 1a 3e 60 f1 dd  |....W._...r.>`..|
-00000040  af 2e 81 f7 16 de 43 85  21 51 49                 |......C.!QI|
+00000000  14 03 02 00 01 01 16 03  02 00 40 21 b5 1f 8d 4b  |..........@!...K|
+00000010  1c a7 28 4e 73 3e d7 c5  75 6e eb e4 b3 95 02 4e  |..(Ns>..un.....N|
+00000020  a3 47 03 44 97 69 c9 89  f5 ac e2 29 5e 22 e7 2c  |.G.D.i.....)^".,|
+00000030  a2 2d e3 ac 64 45 ae 9d  07 9e fe f8 c6 85 47 4d  |.-..dE........GM|
+00000040  59 be 72 8d e6 50 da c7  83 91 14                 |Y.r..P.....|
 >>> Flow 5 (client to server)
 00000000  17 03 02 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 43 8f 88  82 c8 e1 55 37 76 d7 a5  |.....C.....U7v..|
-00000020  83 c6 d2 94 26 fe 30 1f  e2 24 ca d7 27 22 33 47  |....&.0..$..'"3G|
-00000030  5f a9 74 9d ad 15 03 02  00 30 00 00 00 00 00 00  |_.t......0......|
-00000040  00 00 00 00 00 00 00 00  00 00 49 8e ee 5c ec 86  |..........I..\..|
-00000050  e7 64 a7 ac 0d 5c c4 43  a6 45 a4 22 b7 3d 21 06  |.d...\.C.E.".=!.|
-00000060  11 67 08 99 9a 08 a1 7c  e0 1e                    |.g.....|..|
+00000010  00 00 00 00 00 57 45 25  4c 1b 90 d3 28 e1 69 43  |.....WE%L...(.iC|
+00000020  c5 28 d9 d5 15 35 cf 41  bb 38 f2 12 c6 18 a5 a2  |.(...5.A.8......|
+00000030  f5 e4 64 1d 59 15 03 02  00 30 00 00 00 00 00 00  |..d.Y....0......|
+00000040  00 00 00 00 00 00 00 00  00 00 35 06 5f e3 ff e7  |..........5._...|
+00000050  f0 f1 0c d5 b1 59 42 80  19 8d 67 1b 18 18 5c 18  |.....YB...g...\.|
+00000060  42 38 67 85 c3 ab e2 dc  60 d4                    |B8g.....`.|
diff --git a/src/crypto/tls/testdata/Client-TLSv11-ECDHE-RSA-AES b/src/crypto/tls/testdata/Client-TLSv11-ECDHE-RSA-AES
index 7581642..48ff7bc 100644
--- a/src/crypto/tls/testdata/Client-TLSv11-ECDHE-RSA-AES
+++ b/src/crypto/tls/testdata/Client-TLSv11-ECDHE-RSA-AES
@@ -1,95 +1,91 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 02 00 59 02 00 00  55 03 02 0c 74 28 d1 02  |....Y...U...t(..|
-00000010  15 8f 15 9c ec 8c 4e 34  97 d8 14 ab 0c ed 1b 38  |......N4.......8|
-00000020  af 7f e6 d3 41 db fd ad  a0 8d 4f 20 03 71 4a d6  |....A.....O .qJ.|
-00000030  32 23 57 6c e1 55 34 1d  48 6f 9d e0 9a db 15 9d  |2#Wl.U4.Ho......|
-00000040  5b 45 a7 3e 4e 98 31 7d  f5 d4 b6 36 c0 13 00 00  |[E.>N.1}...6....|
+00000000  16 03 02 00 59 02 00 00  55 03 02 10 ca d9 1b 83  |....Y...U.......|
+00000010  59 c8 a5 a5 6a 89 65 6e  1c 0a 49 98 69 05 49 27  |Y...j.en..I.i.I'|
+00000020  96 72 74 5f 6e 5b 66 26  3b fd f8 20 23 b5 d6 c5  |.rt_n[f&;.. #...|
+00000030  09 f1 66 02 27 5c 5a 15  17 83 c5 11 a4 32 cf d8  |..f.'\Z......2..|
+00000040  1e a0 e7 93 83 35 6e aa  61 ae 97 77 c0 13 00 00  |.....5n.a..w....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
-00000060  02 02 71 0b 00 02 6d 00  02 6a 00 02 67 30 82 02  |..q...m..j..g0..|
-00000070  63 30 82 01 cc a0 03 02  01 02 02 09 00 a2 73 00  |c0............s.|
-00000080  0c 81 00 cb f3 30 0d 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000090  01 0b 05 00 30 2b 31 17  30 15 06 03 55 04 0a 13  |....0+1.0...U...|
-000000a0  0e 47 6f 6f 67 6c 65 20  54 45 53 54 49 4e 47 31  |.Google TESTING1|
-000000b0  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
-000000c0  74 30 1e 17 0d 31 35 30  31 30 31 30 30 30 30 30  |t0...15010100000|
-000000d0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
-000000e0  5a 30 26 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |Z0&1.0...U....Go|
-000000f0  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 0b 30 09  |ogle TESTING1.0.|
-00000100  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
-00000110  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
-00000120  81 89 02 81 81 00 af 87  88 f6 20 1b 95 65 6c 14  |.......... ..el.|
-00000130  ab 44 05 af 3b 45 14 e3  b7 6d fd 00 63 4d 95 7f  |.D..;E...m..cM..|
-00000140  fe 6a 62 35 86 c0 4a f9  18 7c f6 aa 25 5e 7a 64  |.jb5..J..|..%^zd|
-00000150  31 66 00 ba f4 8e 92 af  c7 6b d8 76 d4 f3 5f 41  |1f.......k.v.._A|
-00000160  cb 6e 56 15 97 1b 97 c1  3c 12 39 21 66 3d 2b 16  |.nV.....<.9!f=+.|
-00000170  d1 bc db 1c c0 a7 da b7  ca ad ba da cb d5 21 50  |..............!P|
-00000180  ec de 8d ab d1 6b 81 4b  89 02 f3 c4 be c1 6c 89  |.....k.K......l.|
-00000190  b1 44 84 bd 21 d1 04 7d  9d 16 4d f9 82 15 f6 ef  |.D..!..}..M.....|
-000001a0  fa d6 09 47 f2 fb 02 03  01 00 01 a3 81 93 30 81  |...G..........0.|
-000001b0  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
-000001c0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
-000001d0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
-000001e0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
-000001f0  06 03 55 1d 0e 04 12 04  10 12 50 8d 89 6f 1b d1  |..U.......P..o..|
-00000200  dc 54 4d 6e cb 69 5e 06  f4 30 1b 06 03 55 1d 23  |.TMn.i^..0...U.#|
-00000210  04 14 30 12 80 10 bf 3d  b6 a9 66 f2 b8 40 cf ea  |..0....=..f.. at ..|
-00000220  b4 03 78 48 1a 41 30 19  06 03 55 1d 11 04 12 30  |..xH.A0...U....0|
-00000230  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
-00000240  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
-00000250  03 81 81 00 92 7c af 91  55 12 18 96 59 31 a6 48  |.....|..U...Y1.H|
-00000260  40 d5 2d d5 ee bb 02 a0  f5 c2 1e 7c 9b b3 30 7d  |@.-........|..0}|
-00000270  3c dc 76 da 4f 3d c0 fa  ae 2d 33 24 6b 03 7b 1b  |<.v.O=...-3$k.{.|
-00000280  67 59 11 21 b5 11 bc 77  b9 d9 e0 6e a8 2d 2e 35  |gY.!...w...n.-.5|
-00000290  fa 64 5f 22 3e 63 10 6b  be ff 14 86 6d 0d f0 15  |.d_">c.k....m...|
-000002a0  31 a8 14 38 1e 3b 84 87  2c cb 98 ed 51 76 b9 b1  |1..8.;..,...Qv..|
-000002b0  4f dd db 9b 84 04 86 40  fa 51 dd ba b4 8d eb e3  |O...... at .Q......|
-000002c0  46 de 46 b9 4f 86 c7 f9  a4 c2 41 34 ac cc f6 ea  |F.F.O.....A4....|
-000002d0  b0 ab 39 18 16 03 02 00  cb 0c 00 00 c7 03 00 17  |..9.............|
-000002e0  41 04 2c e8 55 b8 19 d6  cd e5 c7 96 a4 aa 61 af  |A.,.U.........a.|
-000002f0  aa b2 f1 fc b3 ac 9a 90  02 d0 0a 86 61 9a c1 2e  |............a...|
-00000300  3e fd 42 0b ba 07 95 77  2b 92 a2 5b 1f 44 ad 6b  |>.B....w+..[.D.k|
-00000310  78 7a f4 b3 4b 04 d3 d5  2d eb 20 2d 73 02 4c db  |xz..K...-. -s.L.|
-00000320  7e ac 00 80 79 b0 c6 b9  a8 50 e4 bf de 97 c6 1f  |~...y....P......|
-00000330  ae 5f 89 77 6e e4 23 8c  8d 1a 49 f8 d4 92 cf 0d  |._.wn.#...I.....|
-00000340  f0 08 bd 3a 88 9c 55 46  fc be 9e 7c 70 ff 6f 70  |...:..UF...|p.op|
-00000350  7b 94 b3 7b 82 c3 58 53  f7 20 13 3c 83 6e 10 55  |{..{..XS. .<.n.U|
-00000360  9d 51 cb 53 8c 93 dc 0e  02 06 40 d4 df ce 57 e4  |.Q.S...... at ...W.|
-00000370  e0 9a ba e2 b3 9b 01 98  0e 12 ca e9 96 5b 7a f2  |.............[z.|
-00000380  b1 ac 9c 44 e7 6e 2e c6  51 63 99 68 26 93 ca e2  |...D.n..Qc.h&...|
-00000390  40 31 e5 9a 80 ce 83 8f  ca 80 90 c4 e8 ab 89 b2  |@1..............|
-000003a0  ca d6 30 a5 16 03 02 00  04 0e 00 00 00           |..0..........|
+00000060  02 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 02 00  |.=.`.\!.;.......|
+000002c0  aa 0c 00 00 a6 03 00 1d  20 97 a4 91 6b cd ca 44  |........ ...k..D|
+000002d0  7e ac fd a5 b1 c0 ce 88  07 f3 a2 d9 93 2b a8 d9  |~............+..|
+000002e0  0b 65 0b 47 c0 2e 4f 3b  26 00 80 00 18 01 a1 29  |.e.G..O;&......)|
+000002f0  0b 84 c9 09 5e 8c 58 5f  62 b6 22 8b 94 6e 72 26  |....^.X_b."..nr&|
+00000300  44 27 32 b9 22 12 67 58  34 a1 ce 6f 87 19 a0 5c  |D'2.".gX4..o...\|
+00000310  5d 58 dc 91 fb c7 e6 31  33 76 6d 1f 8e 4f 46 55  |]X.....13vm..OFU|
+00000320  f1 08 57 9b bb fe 8d c7  6c 0b cd 8b ad b7 51 28  |..W.....l.....Q(|
+00000330  f8 5b 75 97 fe a0 d4 a1  2e 9a d3 d5 45 62 f8 19  |.[u.........Eb..|
+00000340  f6 73 d0 f6 6d e8 43 49  a2 f5 71 66 c5 29 1a 99  |.s..m.CI..qf.)..|
+00000350  e6 c0 cc f9 a5 cd a5 b7  58 08 4d cc 17 46 91 4c  |........X.M..F.L|
+00000360  29 99 b4 05 78 af e7 b0  d1 2d 38 16 03 02 00 04  |)...x....-8.....|
+00000370  0e 00 00 00                                       |....|
 >>> Flow 3 (client to server)
-00000000  16 03 02 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 02 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 02 00 40 00 00  00 00 00 00 00 00 00 00  |..... at ..........|
-00000060  00 00 00 00 00 00 7d 87  6f 44 8f b9 92 51 5a b7  |......}.oD...QZ.|
-00000070  d2 6c 22 7f 62 a1 4e 30  61 f8 42 cd b0 05 c0 24  |.l".b.N0a.B....$|
-00000080  1f e0 49 a8 36 ce 8a 68  94 b7 37 c7 e8 d9 d8 05  |..I.6..h..7.....|
-00000090  be fb 5e 48 ba d1                                 |..^H..|
+00000000  16 03 02 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 02 00 01 01  |....._X.;t......|
+00000030  16 03 02 00 40 00 00 00  00 00 00 00 00 00 00 00  |.... at ...........|
+00000040  00 00 00 00 00 f2 fb 8f  ab ae 46 f2 6a 18 1b 77  |..........F.j..w|
+00000050  f3 ce 0c b6 83 9c d6 34  54 82 76 db 5c 79 5d cf  |.......4T.v.\y].|
+00000060  24 f3 26 6d 9d f0 af d3  fc e0 96 69 0a 04 f7 ba  |$.&m.......i....|
+00000070  78 54 af 37 a5                                    |xT.7.|
 >>> Flow 4 (server to client)
-00000000  14 03 02 00 01 01 16 03  02 00 40 7d ed 01 b9 5a  |..........@}...Z|
-00000010  34 f4 e1 63 70 84 13 86  e6 4d 90 92 da 3c 9b 35  |4..cp....M...<.5|
-00000020  77 92 7f 0a fd 69 09 75  30 5b c3 2c 6e 8e d0 59  |w....i.u0[.,n..Y|
-00000030  08 08 5c c9 eb 53 45 f3  a6 12 16 f2 95 06 27 82  |..\..SE.......'.|
-00000040  6d 9b 9e 6a bb 52 79 65  ca 94 9b                 |m..j.Rye...|
+00000000  14 03 02 00 01 01 16 03  02 00 40 e7 d9 0e af 0c  |.......... at .....|
+00000010  06 55 85 ab b0 0a 5e d9  11 81 7a 53 c0 f6 3f 84  |.U....^...zS..?.|
+00000020  06 7d 9c 20 05 e9 0d 1d  df 9b 48 11 d9 df 0c e6  |.}. ......H.....|
+00000030  6b c2 a8 8f f4 d9 e8 8e  f6 1a 3e db 7c e5 97 ac  |k.........>.|...|
+00000040  5d 63 08 b2 3a 54 91 62  fc 2e a5                 |]c..:T.b...|
 >>> Flow 5 (client to server)
 00000000  17 03 02 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 bb 2d 28  50 1f a4 8f be 94 b9 99  |......-(P.......|
-00000020  e6 0b dd cf 50 fc 72 92  ec 1d 72 9b 27 9a 36 18  |....P.r...r.'.6.|
-00000030  3e e3 d7 cc 69 15 03 02  00 30 00 00 00 00 00 00  |>...i....0......|
-00000040  00 00 00 00 00 00 00 00  00 00 61 ca 39 3c 7e 9f  |..........a.9<~.|
-00000050  1c c8 c2 2a 42 4a d0 c4  f3 80 41 04 b4 35 d0 41  |...*BJ....A..5.A|
-00000060  3d 47 1b 16 2c 71 27 04  7c 81                    |=G..,q'.|.|
+00000010  00 00 00 00 00 3f ef e9  bc 10 07 75 99 67 8f 99  |.....?.....u.g..|
+00000020  bb c0 15 94 86 a2 80 cc  15 97 54 f8 4e 1a d1 9a  |..........T.N...|
+00000030  33 80 aa da ec 15 03 02  00 30 00 00 00 00 00 00  |3........0......|
+00000040  00 00 00 00 00 00 00 00  00 00 6f 78 7b d2 80 62  |..........ox{..b|
+00000050  5c cf 34 d6 5a 72 d8 63  95 24 c6 ff 69 d0 6d 90  |\.4.Zr.c.$..i.m.|
+00000060  8d a2 9f 37 e8 7b b1 d4  68 04                    |...7.{..h.|
diff --git a/src/crypto/tls/testdata/Client-TLSv11-RSA-RC4 b/src/crypto/tls/testdata/Client-TLSv11-RSA-RC4
index e5e315e..2e9c49e 100644
--- a/src/crypto/tls/testdata/Client-TLSv11-RSA-RC4
+++ b/src/crypto/tls/testdata/Client-TLSv11-RSA-RC4
@@ -1,79 +1,78 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 02 00 51 02 00 00  4d 03 02 59 22 be 64 85  |....Q...M..Y".d.|
-00000010  71 af 54 70 5f a8 50 ff  68 52 a0 9e a7 79 4d 90  |q.Tp_.P.hR...yM.|
-00000020  cd bc c7 9c 4f 62 bc 4d  a6 b9 0c 20 e1 94 8f 01  |....Ob.M... ....|
-00000030  fa 7f 9e 6f 01 72 82 ef  cc 41 ed 4d 7e 76 ee e1  |...o.r...A.M~v..|
-00000040  21 34 f3 5c e0 b4 4b e2  73 37 a8 40 00 05 00 00  |!4.\..K.s7. at ....|
-00000050  05 ff 01 00 01 00 16 03  02 02 71 0b 00 02 6d 00  |..........q...m.|
-00000060  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000070  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000090  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-000000a0  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-000000b0  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000c0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000d0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000e0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000f0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-00000100  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-00000110  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000120  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000130  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000140  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000150  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000160  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000170  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000180  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000190  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-000001a0  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-000001b0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001c0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001d0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001e0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001f0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-00000200  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-00000210  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000220  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000230  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000240  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000250  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000260  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000270  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000280  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000290  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-000002a0  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-000002b0  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002c0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 02 00  |..A4......9.....|
-000002d0  04 0e 00 00 00                                    |.....|
+00000000  16 03 02 00 51 02 00 00  4d 03 02 36 8f 18 79 0f  |....Q...M..6..y.|
+00000010  99 6b 3d 1d f4 19 aa ff  79 7c 50 15 52 db 9d c5  |.k=.....y|P.R...|
+00000020  62 40 2d 5b de 45 0c 66  b1 cb be 20 9e 00 c3 00  |b at -[.E.f... ....|
+00000030  22 2f b5 c6 79 c1 f7 72  8f 4b 94 f4 ac fe a9 53  |"/..y..r.K.....S|
+00000040  97 4e fb 00 df 34 b6 24  8f ff 89 db 00 05 00 00  |.N...4.$........|
+00000050  05 ff 01 00 01 00 16 03  02 02 59 0b 00 02 55 00  |..........Y...U.|
+00000060  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000070  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000090  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+000000a0  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+000000b0  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000c0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000d0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000e0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000f0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+00000100  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+00000110  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000120  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000130  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000140  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000150  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000160  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000170  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000180  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000190  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+000001a0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+000001b0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001c0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001d0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001e0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001f0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+00000200  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+00000210  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000220  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000230  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000240  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000250  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000260  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000270  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000280  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000290  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+000002a0  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+000002b0  3b e9 fa e7 16 03 02 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 02 00 86 10 00 00  82 00 80 73 bd 73 65 92  |...........s.se.|
-00000010  86 23 41 14 79 7f d5 c1  10 ce 94 4d ad 9c c3 a9  |.#A.y......M....|
-00000020  87 b5 32 52 f8 6b 11 93  2d 9b 98 0b 8b 1d c0 f6  |..2R.k..-.......|
-00000030  53 17 6d c7 9c 2e ae c9  6f cc 99 23 38 37 1a 10  |S.m.....o..#87..|
-00000040  fe 05 0b b5 55 0a 14 e9  60 7d 70 26 98 e2 54 d9  |....U...`}p&..T.|
-00000050  65 cf 2e f4 53 5f 1d aa  3a f6 33 7b eb 4c 0e b3  |e...S_..:.3{.L..|
-00000060  ff 5a db 36 2a 47 f3 df  f9 fc f5 31 78 83 aa 6b  |.Z.6*G.....1x..k|
-00000070  52 b7 ba 1a 96 bc fa c1  a1 a9 bb 2b f5 38 89 00  |R..........+.8..|
-00000080  4d e5 78 13 4e a4 38 46  42 dc 16 14 03 02 00 01  |M.x.N.8FB.......|
-00000090  01 16 03 02 00 24 0b 7b  3e 32 fb 94 95 66 26 a9  |.....$.{>2...f&.|
-000000a0  4c 21 5e 18 59 cb 80 57  1b 9a 89 c8 91 c5 30 1f  |L!^.Y..W......0.|
-000000b0  1a e2 80 9a 0f 03 8e 7b  4c 7d                    |.......{L}|
+00000000  16 03 02 00 86 10 00 00  82 00 80 b9 65 8d bf a7  |............e...|
+00000010  c8 4b 79 ce 6f cb 8b 13  1c ac b9 7d 66 5e e9 ba  |.Ky.o......}f^..|
+00000020  1d 71 4e a9 e9 34 ae f6  64 65 90 3b d8 16 52 a2  |.qN..4..de.;..R.|
+00000030  6f f4 cb 8a 13 74 a2 ee  b7 27 69 b4 41 c0 90 68  |o....t...'i.A..h|
+00000040  bc 02 69 e1 c6 48 4f 39  36 30 25 ca 4c 17 ce 83  |..i..HO960%.L...|
+00000050  9e 08 56 e3 05 49 93 9e  2e c4 fb e6 c8 01 f1 0f  |..V..I..........|
+00000060  c5 70 0f 08 83 48 e9 48  ef 6e 50 8b 05 7e e5 84  |.p...H.H.nP..~..|
+00000070  25 fa 55 c7 ae 31 02 27  00 ef 3f 98 86 20 12 89  |%.U..1.'..?.. ..|
+00000080  91 59 28 b4 f7 d7 af d2  69 61 35 14 03 02 00 01  |.Y(.....ia5.....|
+00000090  01 16 03 02 00 24 92 fa  9a bb 9b a3 39 3f 6b 0c  |.....$......9?k.|
+000000a0  70 b5 48 4a fc cf 79 86  c7 90 e8 db ca 6a ff 95  |p.HJ..y......j..|
+000000b0  11 9b 65 b2 07 61 00 a8  dc 14                    |..e..a....|
 >>> Flow 4 (server to client)
-00000000  14 03 02 00 01 01 16 03  02 00 24 06 7f be 82 45  |..........$....E|
-00000010  79 c6 67 fb d3 1e 3f ca  d9 0f 8f 81 36 cc 80 77  |y.g...?.....6..w|
-00000020  b8 48 f3 88 29 fa f1 3a  b2 d4 fd 10 e5 8c 43     |.H..)..:......C|
+00000000  14 03 02 00 01 01 16 03  02 00 24 47 d5 78 31 32  |..........$G.x12|
+00000010  db db 2b f4 2f e6 a4 fa  fa 04 31 ba c0 bc 86 38  |..+./.....1....8|
+00000020  29 70 53 c6 8c 28 e5 75  90 ed 0f 4f 3e cb 06     |)pS..(.u...O>..|
 >>> Flow 5 (client to server)
-00000000  17 03 02 00 1a 29 4d a2  80 38 2c 9e 96 bb 29 8b  |.....)M..8,...).|
-00000010  22 69 ea 85 3e d8 a9 66  39 b8 58 12 ae 67 db 15  |"i..>..f9.X..g..|
-00000020  03 02 00 16 8c b2 f4 c1  35 5d 28 dc 5c bc 30 95  |........5](.\.0.|
-00000030  99 3e f6 c6 ff 4f 5c f4  85 1a                    |.>...O\...|
+00000000  17 03 02 00 1a f3 e7 6c  01 c5 70 c6 69 dd 4f 40  |.......l..p.i.O@|
+00000010  38 c1 b2 d2 28 69 2f 99  b1 bd 71 d0 c2 00 08 15  |8...(i/...q.....|
+00000020  03 02 00 16 4c 44 14 02  8e 46 f8 84 40 f1 3d 6d  |....LD...F.. at .=m|
+00000030  f2 01 f6 9d 7a 0b 18 ee  9d 41                    |....z....A|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-AES128-GCM-SHA256 b/src/crypto/tls/testdata/Client-TLSv12-AES128-GCM-SHA256
index 9e41089..c20dcc6 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-AES128-GCM-SHA256
+++ b/src/crypto/tls/testdata/Client-TLSv12-AES128-GCM-SHA256
@@ -1,81 +1,80 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 51 02 00 00  4d 03 03 e7 18 39 61 14  |....Q...M....9a.|
-00000010  47 69 40 34 ae 4e 58 4b  32 2d ed 2a 52 09 c5 2f  |Gi at 4.NXK2-.*R../|
-00000020  07 f6 44 0b 2b 9c 43 4b  bb 79 b6 20 48 f4 ff f1  |..D.+.CK.y. H...|
-00000030  c6 72 77 e5 3a e0 8d 08  b9 cd 8b bf e3 9b ec 41  |.rw.:..........A|
-00000040  1d d9 86 1b 35 7b 8c 04  e0 83 0d d3 00 9c 00 00  |....5{..........|
-00000050  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000060  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000070  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000090  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-000000a0  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-000000b0  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000c0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000d0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000e0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000f0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-00000100  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-00000110  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000120  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000130  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000140  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000150  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000160  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000170  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000180  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000190  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-000001a0  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-000001b0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001c0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001d0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001e0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001f0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-00000200  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-00000210  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000220  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000230  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000240  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000250  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000260  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000270  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000280  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000290  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-000002a0  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-000002b0  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002c0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002d0  04 0e 00 00 00                                    |.....|
+00000000  16 03 03 00 51 02 00 00  4d 03 03 65 9c b1 7a 5c  |....Q...M..e..z\|
+00000010  84 e5 a5 12 ba 54 1f 4c  ec 95 0b 8f ea 5c cc 3b  |.....T.L.....\.;|
+00000020  de b8 18 23 8e c4 95 59  d7 7f 8f 20 36 fe ec 27  |...#...Y... 6..'|
+00000030  10 85 43 fb 9c 68 3f 69  d0 08 a6 57 10 a6 29 a4  |..C..h?i...W..).|
+00000040  f6 0c 2e 05 6e 0d e5 44  61 e1 2e 07 00 9c 00 00  |....n..Da.......|
+00000050  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000060  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000070  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000090  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+000000a0  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+000000b0  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000c0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000d0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000e0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000f0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+00000100  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+00000110  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000120  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000130  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000140  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000150  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000160  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000170  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000180  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000190  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+000001a0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+000001b0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001c0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001d0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001e0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001f0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+00000200  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+00000210  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000220  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000230  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000240  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000250  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000260  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000270  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000280  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000290  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+000002a0  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+000002b0  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 73 bd 73 65 92  |...........s.se.|
-00000010  86 23 41 14 79 7f d5 c1  10 ce 94 4d ad 9c c3 a9  |.#A.y......M....|
-00000020  87 b5 32 52 f8 6b 11 93  2d 9b 98 0b 8b 1d c0 f6  |..2R.k..-.......|
-00000030  53 17 6d c7 9c 2e ae c9  6f cc 99 23 38 37 1a 10  |S.m.....o..#87..|
-00000040  fe 05 0b b5 55 0a 14 e9  60 7d 70 26 98 e2 54 d9  |....U...`}p&..T.|
-00000050  65 cf 2e f4 53 5f 1d aa  3a f6 33 7b eb 4c 0e b3  |e...S_..:.3{.L..|
-00000060  ff 5a db 36 2a 47 f3 df  f9 fc f5 31 78 83 aa 6b  |.Z.6*G.....1x..k|
-00000070  52 b7 ba 1a 96 bc fa c1  a1 a9 bb 2b f5 38 89 00  |R..........+.8..|
-00000080  4d e5 78 13 4e a4 38 46  42 dc 16 14 03 03 00 01  |M.x.N.8FB.......|
-00000090  01 16 03 03 00 28 00 00  00 00 00 00 00 00 8a 9b  |.....(..........|
-000000a0  29 1d 64 2e ee 0d 39 d9  c5 86 b9 02 9d c3 bd 74  |).d...9........t|
-000000b0  39 9d 53 9f 1a ee 84 64  82 82 41 81 f8 2f        |9.S....d..A../|
+00000000  16 03 03 00 86 10 00 00  82 00 80 b9 65 8d bf a7  |............e...|
+00000010  c8 4b 79 ce 6f cb 8b 13  1c ac b9 7d 66 5e e9 ba  |.Ky.o......}f^..|
+00000020  1d 71 4e a9 e9 34 ae f6  64 65 90 3b d8 16 52 a2  |.qN..4..de.;..R.|
+00000030  6f f4 cb 8a 13 74 a2 ee  b7 27 69 b4 41 c0 90 68  |o....t...'i.A..h|
+00000040  bc 02 69 e1 c6 48 4f 39  36 30 25 ca 4c 17 ce 83  |..i..HO960%.L...|
+00000050  9e 08 56 e3 05 49 93 9e  2e c4 fb e6 c8 01 f1 0f  |..V..I..........|
+00000060  c5 70 0f 08 83 48 e9 48  ef 6e 50 8b 05 7e e5 84  |.p...H.H.nP..~..|
+00000070  25 fa 55 c7 ae 31 02 27  00 ef 3f 98 86 20 12 89  |%.U..1.'..?.. ..|
+00000080  91 59 28 b4 f7 d7 af d2  69 61 35 14 03 03 00 01  |.Y(.....ia5.....|
+00000090  01 16 03 03 00 28 00 00  00 00 00 00 00 00 97 f1  |.....(..........|
+000000a0  fe 34 f7 de 76 9b 56 27  e6 9f 36 48 30 a6 de 78  |.4..v.V'..6H0..x|
+000000b0  10 6a ef bf 92 8a 6e 99  21 2f 1b 7b 48 80        |.j....n.!/.{H.|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 60 71 25 f9 f9  |..........(`q%..|
-00000010  89 cd f8 6f 00 a6 0e 92  f8 3e 84 08 79 6f 91 cd  |...o.....>..yo..|
-00000020  e2 62 d5 da 96 79 c3 0d  f4 34 26 bd 47 9c 30 aa  |.b...y...4&.G.0.|
-00000030  1b 5f 24                                          |._$|
+00000000  14 03 03 00 01 01 16 03  03 00 28 23 9d a2 ae a1  |..........(#....|
+00000010  7d dd 92 1f 42 18 68 f6  fb 31 56 7b e4 58 a4 e9  |}...B.h..1V{.X..|
+00000020  c2 1c e7 67 1b 40 b1 b9  63 9d 05 fb c7 44 9e f6  |...g. at ..c....D..|
+00000030  7a 14 bb                                          |z..|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 44 05 c9  |.............D..|
-00000010  2b 82 55 26 ab 4b 65 b1  94 e5 8a 81 bf 44 a5 cb  |+.U&.Ke......D..|
-00000020  22 f0 0a 15 03 03 00 1a  00 00 00 00 00 00 00 02  |"...............|
-00000030  59 8d 6f 5d 30 47 4d 3e  ed aa 87 5f ca 39 44 a4  |Y.o]0GM>..._.9D.|
-00000040  9b fc                                             |..|
+00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 d7 31 70  |..............1p|
+00000010  c8 11 3f bd 83 fc 6e f8  3b e0 ee 45 c5 1a c8 41  |..?...n.;..E...A|
+00000020  80 22 d4 15 03 03 00 1a  00 00 00 00 00 00 00 02  |."..............|
+00000030  7a fe 3a 11 7c c0 26 30  55 24 85 0b 43 cb 7c ac  |z.:.|.&0U$..C.|.|
+00000040  ef 2c                                             |.,|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-AES128-SHA256 b/src/crypto/tls/testdata/Client-TLSv12-AES128-SHA256
new file mode 100644
index 0000000..774481e
--- /dev/null
+++ b/src/crypto/tls/testdata/Client-TLSv12-AES128-SHA256
@@ -0,0 +1,89 @@
+>>> Flow 1 (client to server)
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
+00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
+>>> Flow 2 (server to client)
+00000000  16 03 03 00 51 02 00 00  4d 03 03 26 31 ba 4a 56  |....Q...M..&1.JV|
+00000010  16 83 15 47 b9 c4 7e 10  ca 92 31 4d 77 af cc cd  |...G..~...1Mw...|
+00000020  70 f4 cc 82 6e b9 ac 1b  0d 17 25 20 e9 08 ec 95  |p...n.....% ....|
+00000030  ea 84 a4 bd 8f 9d 8e d3  58 a7 5e 72 42 e4 19 8f  |........X.^rB...|
+00000040  46 c3 d9 be 16 3c d4 53  5a 02 8f a1 00 3c 00 00  |F....<.SZ....<..|
+00000050  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000060  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000070  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000090  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+000000a0  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+000000b0  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000c0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000d0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000e0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000f0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+00000100  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+00000110  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000120  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000130  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000140  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000150  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000160  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000170  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000180  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000190  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+000001a0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+000001b0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001c0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001d0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001e0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001f0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+00000200  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+00000210  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000220  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000230  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000240  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000250  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000260  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000270  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000280  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000290  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+000002a0  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+000002b0  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
+>>> Flow 3 (client to server)
+00000000  16 03 03 00 86 10 00 00  82 00 80 b9 65 8d bf a7  |............e...|
+00000010  c8 4b 79 ce 6f cb 8b 13  1c ac b9 7d 66 5e e9 ba  |.Ky.o......}f^..|
+00000020  1d 71 4e a9 e9 34 ae f6  64 65 90 3b d8 16 52 a2  |.qN..4..de.;..R.|
+00000030  6f f4 cb 8a 13 74 a2 ee  b7 27 69 b4 41 c0 90 68  |o....t...'i.A..h|
+00000040  bc 02 69 e1 c6 48 4f 39  36 30 25 ca 4c 17 ce 83  |..i..HO960%.L...|
+00000050  9e 08 56 e3 05 49 93 9e  2e c4 fb e6 c8 01 f1 0f  |..V..I..........|
+00000060  c5 70 0f 08 83 48 e9 48  ef 6e 50 8b 05 7e e5 84  |.p...H.H.nP..~..|
+00000070  25 fa 55 c7 ae 31 02 27  00 ef 3f 98 86 20 12 89  |%.U..1.'..?.. ..|
+00000080  91 59 28 b4 f7 d7 af d2  69 61 35 14 03 03 00 01  |.Y(.....ia5.....|
+00000090  01 16 03 03 00 50 00 00  00 00 00 00 00 00 00 00  |.....P..........|
+000000a0  00 00 00 00 00 00 46 91  40 e2 65 40 fe 42 c3 34  |......F. at .e@.B.4|
+000000b0  98 65 89 d0 96 7e 7b 67  8e c4 d5 e6 37 f5 96 04  |.e...~{g....7...|
+000000c0  b7 c8 63 83 76 5c ca 9d  89 18 d4 97 8b 3f f6 75  |..c.v\.......?.u|
+000000d0  1d 51 0b b9 90 1c 85 8f  83 20 9e 9a 21 d9 db 14  |.Q....... ..!...|
+000000e0  1e 02 d4 ab aa c4                                 |......|
+>>> Flow 4 (server to client)
+00000000  14 03 03 00 01 01 16 03  03 00 50 6d 34 72 de 79  |..........Pm4r.y|
+00000010  ad 96 d0 92 5f d7 01 de  90 f4 5d 0f de 02 ae 19  |...._.....].....|
+00000020  61 a3 ee 29 ab 18 f1 09  2e 5b bc e0 73 9a 68 19  |a..).....[..s.h.|
+00000030  17 dd c8 d9 63 b4 28 c8  da 1a 81 40 ca d3 5a 99  |....c.(.... at ..Z.|
+00000040  17 67 fe e9 dd 1a 52 c4  6e 70 0a 0e cf e8 c0 f8  |.g....R.np......|
+00000050  6c 1f ee d2 70 97 dc ee  b8 95 35                 |l...p.....5|
+>>> Flow 5 (client to server)
+00000000  17 03 03 00 40 00 00 00  00 00 00 00 00 00 00 00  |.... at ...........|
+00000010  00 00 00 00 00 ee 03 cc  04 97 17 f0 04 85 02 b7  |................|
+00000020  5c 24 ca 9f c2 25 e0 76  f4 72 e5 71 2b ac f4 a5  |\$...%.v.r.q+...|
+00000030  c4 62 08 9a da b7 ab 30  2f 34 b0 70 20 a3 b9 b3  |.b.....0/4.p ...|
+00000040  df 90 9b 01 0b 15 03 03  00 40 00 00 00 00 00 00  |......... at ......|
+00000050  00 00 00 00 00 00 00 00  00 00 3b ca fc 0e 08 f2  |..........;.....|
+00000060  c8 b7 22 61 43 24 b3 54  1b ca 58 c6 bd 27 f3 3d  |.."aC$.T..X..'.=|
+00000070  ac a0 d8 fe 0e b5 15 7c  1f 98 32 f0 6b 28 bc 61  |.......|..2.k(.a|
+00000080  6c c7 ba 66 54 19 92 a9  6f 43                    |l..fT...oC|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-AES256-GCM-SHA384 b/src/crypto/tls/testdata/Client-TLSv12-AES256-GCM-SHA384
index 33fb708..6d26746 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-AES256-GCM-SHA384
+++ b/src/crypto/tls/testdata/Client-TLSv12-AES256-GCM-SHA384
@@ -1,81 +1,80 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 51 02 00 00  4d 03 03 08 e2 40 1b 1a  |....Q...M.... at ..|
-00000010  54 dc 66 60 e1 e0 8d 94  c6 dd 2c eb 95 e0 e9 2f  |T.f`......,..../|
-00000020  fb 49 17 d8 34 d7 a2 7a  1b e1 60 20 26 a3 4b 7c  |.I..4..z..` &.K||
-00000030  40 cc df 4b 9c 72 a9 e6  61 89 1e 20 b2 e5 e3 1e  |@..K.r..a.. ....|
-00000040  4e a3 b6 32 ce fc 94 0d  ab 13 74 f8 00 9d 00 00  |N..2......t.....|
-00000050  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000060  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000070  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000090  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-000000a0  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-000000b0  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000c0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000d0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000e0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000f0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-00000100  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-00000110  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000120  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000130  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000140  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000150  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000160  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000170  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000180  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000190  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-000001a0  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-000001b0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001c0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001d0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001e0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001f0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-00000200  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-00000210  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000220  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000230  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000240  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000250  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000260  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000270  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000280  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000290  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-000002a0  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-000002b0  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002c0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002d0  04 0e 00 00 00                                    |.....|
+00000000  16 03 03 00 51 02 00 00  4d 03 03 98 b0 4a 9a c8  |....Q...M....J..|
+00000010  8f f9 1f f9 70 03 d9 1a  ee 7c 29 30 6a 71 7c 6c  |....p....|)0jq|l|
+00000020  ea 2c de 84 f9 ee 4d 2c  d7 58 12 20 a4 e2 1b f3  |.,....M,.X. ....|
+00000030  42 b8 9a 0b 71 8c 27 57  61 98 c5 c5 1b 04 01 5b  |B...q.'Wa......[|
+00000040  a0 bc 88 64 d9 ce 5a a1  b2 7b 6c 4e 00 9d 00 00  |...d..Z..{lN....|
+00000050  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000060  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000070  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000090  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+000000a0  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+000000b0  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000c0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000d0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000e0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000f0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+00000100  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+00000110  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000120  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000130  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000140  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000150  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000160  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000170  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000180  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000190  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+000001a0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+000001b0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001c0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001d0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001e0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001f0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+00000200  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+00000210  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000220  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000230  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000240  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000250  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000260  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000270  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000280  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000290  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+000002a0  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+000002b0  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 73 bd 73 65 92  |...........s.se.|
-00000010  86 23 41 14 79 7f d5 c1  10 ce 94 4d ad 9c c3 a9  |.#A.y......M....|
-00000020  87 b5 32 52 f8 6b 11 93  2d 9b 98 0b 8b 1d c0 f6  |..2R.k..-.......|
-00000030  53 17 6d c7 9c 2e ae c9  6f cc 99 23 38 37 1a 10  |S.m.....o..#87..|
-00000040  fe 05 0b b5 55 0a 14 e9  60 7d 70 26 98 e2 54 d9  |....U...`}p&..T.|
-00000050  65 cf 2e f4 53 5f 1d aa  3a f6 33 7b eb 4c 0e b3  |e...S_..:.3{.L..|
-00000060  ff 5a db 36 2a 47 f3 df  f9 fc f5 31 78 83 aa 6b  |.Z.6*G.....1x..k|
-00000070  52 b7 ba 1a 96 bc fa c1  a1 a9 bb 2b f5 38 89 00  |R..........+.8..|
-00000080  4d e5 78 13 4e a4 38 46  42 dc 16 14 03 03 00 01  |M.x.N.8FB.......|
-00000090  01 16 03 03 00 28 00 00  00 00 00 00 00 00 28 9a  |.....(........(.|
-000000a0  46 23 21 fa a9 ec 6d 57  d1 27 2f 53 58 a9 00 48  |F#!...mW.'/SX..H|
-000000b0  7e 82 82 b8 23 f3 c4 a8  d3 2c a3 99 76 2e        |~...#....,..v.|
+00000000  16 03 03 00 86 10 00 00  82 00 80 b9 65 8d bf a7  |............e...|
+00000010  c8 4b 79 ce 6f cb 8b 13  1c ac b9 7d 66 5e e9 ba  |.Ky.o......}f^..|
+00000020  1d 71 4e a9 e9 34 ae f6  64 65 90 3b d8 16 52 a2  |.qN..4..de.;..R.|
+00000030  6f f4 cb 8a 13 74 a2 ee  b7 27 69 b4 41 c0 90 68  |o....t...'i.A..h|
+00000040  bc 02 69 e1 c6 48 4f 39  36 30 25 ca 4c 17 ce 83  |..i..HO960%.L...|
+00000050  9e 08 56 e3 05 49 93 9e  2e c4 fb e6 c8 01 f1 0f  |..V..I..........|
+00000060  c5 70 0f 08 83 48 e9 48  ef 6e 50 8b 05 7e e5 84  |.p...H.H.nP..~..|
+00000070  25 fa 55 c7 ae 31 02 27  00 ef 3f 98 86 20 12 89  |%.U..1.'..?.. ..|
+00000080  91 59 28 b4 f7 d7 af d2  69 61 35 14 03 03 00 01  |.Y(.....ia5.....|
+00000090  01 16 03 03 00 28 00 00  00 00 00 00 00 00 57 97  |.....(........W.|
+000000a0  64 ef 80 07 b0 31 02 c8  2a a2 b7 1f d6 a3 7c 7a  |d....1..*.....|z|
+000000b0  e9 c4 e1 55 9f 0a ef 0d  8f 0a 57 13 f5 a4        |...U......W...|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 0c c8 0a e1 b8  |..........(.....|
-00000010  95 b9 84 bc 0f 48 eb d4  4a a5 63 c2 92 58 8a 91  |.....H..J.c..X..|
-00000020  27 30 36 28 23 f5 50 bd  d6 a9 e9 61 54 10 f9 72  |'06(#.P....aT..r|
-00000030  98 d1 0e                                          |...|
+00000000  14 03 03 00 01 01 16 03  03 00 28 42 49 9c 67 4f  |..........(BI.gO|
+00000010  36 75 b8 34 0e ee 00 98  1a ba 52 d5 96 7b 91 d7  |6u.4......R..{..|
+00000020  ba ec e4 5e 2e 42 e3 72  a0 ea 60 24 31 30 3d a2  |...^.B.r..`$10=.|
+00000030  c5 6c 8f                                          |.l.|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 b9 55 ce  |..............U.|
-00000010  5a ab 7e 7e 58 4f c9 5a  bc 0e 93 98 4f 87 86 98  |Z.~~XO.Z....O...|
-00000020  a6 40 7e 15 03 03 00 1a  00 00 00 00 00 00 00 02  |.@~.............|
-00000030  57 0c 6f d9 28 87 d4 a6  de 14 91 a7 79 cc 19 e5  |W.o.(.......y...|
-00000040  28 66                                             |(f|
+00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 42 9f da  |.............B..|
+00000010  a1 e6 98 48 a8 6c 78 a0  f7 fd e7 0f bc df 97 ef  |...H.lx.........|
+00000020  b8 62 4c 15 03 03 00 1a  00 00 00 00 00 00 00 02  |.bL.............|
+00000030  99 ac 35 a4 d9 1f 58 26  51 c6 6a b9 1f 53 ec 19  |..5...X&Q.j..S..|
+00000040  90 78                                             |.x|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ALPN b/src/crypto/tls/testdata/Client-TLSv12-ALPN
index d7745dc..9f90ff2 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ALPN
+++ b/src/crypto/tls/testdata/Client-TLSv12-ALPN
@@ -1,93 +1,86 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 9d 01 00 00  99 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 a9 01 00 00  a5 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 4e 33 74 00 00  00 05 00 05 01 00 00 00  |...N3t..........|
-00000060  00 00 0a 00 08 00 06 00  17 00 18 00 19 00 0b 00  |................|
-00000070  02 01 00 00 0d 00 0e 00  0c 04 01 04 03 05 01 05  |................|
-00000080  03 02 01 02 03 ff 01 00  01 00 00 10 00 10 00 0e  |................|
-00000090  06 70 72 6f 74 6f 32 06  70 72 6f 74 6f 31 00 12  |.proto2.proto1..|
-000000a0  00 00                                             |..|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 50 33 74  |.............P3t|
+00000060  00 00 00 05 00 05 01 00  00 00 00 00 0a 00 0a 00  |................|
+00000070  08 00 1d 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
+00000080  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
+00000090  03 ff 01 00 01 00 00 10  00 10 00 0e 06 70 72 6f  |.............pro|
+000000a0  74 6f 32 06 70 72 6f 74  6f 31 00 12 00 00        |to2.proto1....|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 66 02 00 00  62 03 03 a2 7d b9 7c f9  |....f...b...}.|.|
-00000010  bf fb cb b2 d5 11 c0 99  19 73 3d b4 eb 6b 39 f8  |.........s=..k9.|
-00000020  1b 7c 1d 6b 17 4d 66 a3  ed 20 5b 20 ee 87 d7 1f  |.|.k.Mf.. [ ....|
-00000030  cf 60 6c 75 12 8b de 56  f6 ca da 4a 92 76 49 43  |.`lu...V...J.vIC|
-00000040  70 18 0a e7 7b 2a 0c f3  44 a6 d8 dd c0 2f 00 00  |p...{*..D..../..|
+00000000  16 03 03 00 66 02 00 00  62 03 03 8d 70 c6 03 ad  |....f...b...p...|
+00000010  2f 20 b3 c2 ab e0 fc 80  74 c4 23 9e 82 65 61 a1  |/ ......t.#..ea.|
+00000020  26 97 14 a0 9b 9c d5 e0  92 43 ee 20 ec 84 cf 78  |&........C. ...x|
+00000030  44 16 7d f3 ad 94 a9 f8  c3 e0 c6 e1 b6 c5 e3 3d  |D.}............=|
+00000040  77 ea 76 1d 58 cc 94 3a  ad 1a 1a 6c cc a8 00 00  |w.v.X..:...l....|
 00000050  1a ff 01 00 01 00 00 0b  00 04 03 00 01 02 00 10  |................|
-00000060  00 09 00 07 06 70 72 6f  74 6f 31 16 03 03 02 71  |.....proto1....q|
-00000070  0b 00 02 6d 00 02 6a 00  02 67 30 82 02 63 30 82  |...m..j..g0..c0.|
-00000080  01 cc a0 03 02 01 02 02  09 00 a2 73 00 0c 81 00  |...........s....|
-00000090  cb f3 30 0d 06 09 2a 86  48 86 f7 0d 01 01 0b 05  |..0...*.H.......|
-000000a0  00 30 2b 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |.0+1.0...U....Go|
-000000b0  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 10 30 0e  |ogle TESTING1.0.|
-000000c0  06 03 55 04 03 13 07 47  6f 20 52 6f 6f 74 30 1e  |..U....Go Root0.|
-000000d0  17 0d 31 35 30 31 30 31  30 30 30 30 30 30 5a 17  |..150101000000Z.|
-000000e0  0d 32 35 30 31 30 31 30  30 30 30 30 30 5a 30 26  |.250101000000Z0&|
-000000f0  31 17 30 15 06 03 55 04  0a 13 0e 47 6f 6f 67 6c  |1.0...U....Googl|
-00000100  65 20 54 45 53 54 49 4e  47 31 0b 30 09 06 03 55  |e TESTING1.0...U|
-00000110  04 03 13 02 47 6f 30 81  9f 30 0d 06 09 2a 86 48  |....Go0..0...*.H|
-00000120  86 f7 0d 01 01 01 05 00  03 81 8d 00 30 81 89 02  |............0...|
-00000130  81 81 00 af 87 88 f6 20  1b 95 65 6c 14 ab 44 05  |....... ..el..D.|
-00000140  af 3b 45 14 e3 b7 6d fd  00 63 4d 95 7f fe 6a 62  |.;E...m..cM...jb|
-00000150  35 86 c0 4a f9 18 7c f6  aa 25 5e 7a 64 31 66 00  |5..J..|..%^zd1f.|
-00000160  ba f4 8e 92 af c7 6b d8  76 d4 f3 5f 41 cb 6e 56  |......k.v.._A.nV|
-00000170  15 97 1b 97 c1 3c 12 39  21 66 3d 2b 16 d1 bc db  |.....<.9!f=+....|
-00000180  1c c0 a7 da b7 ca ad ba  da cb d5 21 50 ec de 8d  |...........!P...|
-00000190  ab d1 6b 81 4b 89 02 f3  c4 be c1 6c 89 b1 44 84  |..k.K......l..D.|
-000001a0  bd 21 d1 04 7d 9d 16 4d  f9 82 15 f6 ef fa d6 09  |.!..}..M........|
-000001b0  47 f2 fb 02 03 01 00 01  a3 81 93 30 81 90 30 0e  |G..........0..0.|
-000001c0  06 03 55 1d 0f 01 01 ff  04 04 03 02 05 a0 30 1d  |..U...........0.|
-000001d0  06 03 55 1d 25 04 16 30  14 06 08 2b 06 01 05 05  |..U.%..0...+....|
-000001e0  07 03 01 06 08 2b 06 01  05 05 07 03 02 30 0c 06  |.....+.......0..|
-000001f0  03 55 1d 13 01 01 ff 04  02 30 00 30 19 06 03 55  |.U.......0.0...U|
-00000200  1d 0e 04 12 04 10 12 50  8d 89 6f 1b d1 dc 54 4d  |.......P..o...TM|
-00000210  6e cb 69 5e 06 f4 30 1b  06 03 55 1d 23 04 14 30  |n.i^..0...U.#..0|
-00000220  12 80 10 bf 3d b6 a9 66  f2 b8 40 cf ea b4 03 78  |....=..f.. at ....x|
-00000230  48 1a 41 30 19 06 03 55  1d 11 04 12 30 10 82 0e  |H.A0...U....0...|
-00000240  65 78 61 6d 70 6c 65 2e  67 6f 6c 61 6e 67 30 0d  |example.golang0.|
-00000250  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 03 81 81  |..*.H...........|
-00000260  00 92 7c af 91 55 12 18  96 59 31 a6 48 40 d5 2d  |..|..U...Y1.H at .-|
-00000270  d5 ee bb 02 a0 f5 c2 1e  7c 9b b3 30 7d 3c dc 76  |........|..0}<.v|
-00000280  da 4f 3d c0 fa ae 2d 33  24 6b 03 7b 1b 67 59 11  |.O=...-3$k.{.gY.|
-00000290  21 b5 11 bc 77 b9 d9 e0  6e a8 2d 2e 35 fa 64 5f  |!...w...n.-.5.d_|
-000002a0  22 3e 63 10 6b be ff 14  86 6d 0d f0 15 31 a8 14  |">c.k....m...1..|
-000002b0  38 1e 3b 84 87 2c cb 98  ed 51 76 b9 b1 4f dd db  |8.;..,...Qv..O..|
-000002c0  9b 84 04 86 40 fa 51 dd  ba b4 8d eb e3 46 de 46  |.... at .Q......F.F|
-000002d0  b9 4f 86 c7 f9 a4 c2 41  34 ac cc f6 ea b0 ab 39  |.O.....A4......9|
-000002e0  18 16 03 03 00 cd 0c 00  00 c9 03 00 17 41 04 41  |.............A.A|
-000002f0  a4 1f 6f ea 6d 59 68 72  1a 6d 47 c7 b4 a0 08 01  |..o.mYhr.mG.....|
-00000300  b9 b3 d8 7a 95 75 c0 58  2a d9 29 91 e7 d9 78 b2  |...z.u.X*.)...x.|
-00000310  97 1d 52 72 2d 18 cb ce  83 8a 07 f4 bd dd 7e a1  |..Rr-.........~.|
-00000320  d2 45 51 9d bf f1 bf 01  33 3a 10 94 6c 2b 99 04  |.EQ.....3:..l+..|
-00000330  01 00 80 63 8f 03 6d b4  4d f7 27 d0 1f f2 0f ff  |...c..m.M.'.....|
-00000340  af 27 c2 97 21 68 8c 32  8b 14 67 0e b5 75 3a 5b  |.'..!h.2..g..u:[|
-00000350  73 08 9a c7 fd ad 8d 50  2a de e7 d6 c5 87 7a b2  |s......P*.....z.|
-00000360  06 29 0a 09 dd d4 81 d5  a7 2b 4d 20 50 72 6f be  |.).......+M Pro.|
-00000370  35 9b c6 2d b0 1e 8f a2  cf 10 33 d4 53 0b 33 95  |5..-......3.S.3.|
-00000380  b8 a5 34 38 1b db 1e 45  07 36 3a 86 c7 f1 b1 3a  |..48...E.6:....:|
-00000390  2e 5d 82 b2 1d 3e e1 27  8f f2 f4 2c 8c c3 27 e9  |.]...>.'...,..'.|
-000003a0  f0 9a 8f 6d 20 b1 19 8e  23 d5 04 69 e4 eb 0d eb  |...m ...#..i....|
-000003b0  97 fb 71 16 03 03 00 04  0e 00 00 00              |..q.........|
+00000060  00 09 00 07 06 70 72 6f  74 6f 31 16 03 03 02 59  |.....proto1....Y|
+00000070  0b 00 02 55 00 02 52 00  02 4f 30 82 02 4b 30 82  |...U..R..O0..K0.|
+00000080  01 b4 a0 03 02 01 02 02  09 00 e8 f0 9d 3f e2 5b  |.............?.[|
+00000090  ea a6 30 0d 06 09 2a 86  48 86 f7 0d 01 01 0b 05  |..0...*.H.......|
+000000a0  00 30 1f 31 0b 30 09 06  03 55 04 0a 13 02 47 6f  |.0.1.0...U....Go|
+000000b0  31 10 30 0e 06 03 55 04  03 13 07 47 6f 20 52 6f  |1.0...U....Go Ro|
+000000c0  6f 74 30 1e 17 0d 31 36  30 31 30 31 30 30 30 30  |ot0...1601010000|
+000000d0  30 30 5a 17 0d 32 35 30  31 30 31 30 30 30 30 30  |00Z..25010100000|
+000000e0  30 5a 30 1a 31 0b 30 09  06 03 55 04 0a 13 02 47  |0Z0.1.0...U....G|
+000000f0  6f 31 0b 30 09 06 03 55  04 03 13 02 47 6f 30 81  |o1.0...U....Go0.|
+00000100  9f 30 0d 06 09 2a 86 48  86 f7 0d 01 01 01 05 00  |.0...*.H........|
+00000110  03 81 8d 00 30 81 89 02  81 81 00 db 46 7d 93 2e  |....0.......F}..|
+00000120  12 27 06 48 bc 06 28 21  ab 7e c4 b6 a2 5d fe 1e  |.'.H..(!.~...]..|
+00000130  52 45 88 7a 36 47 a5 08  0d 92 42 5b c2 81 c0 be  |RE.z6G....B[....|
+00000140  97 79 98 40 fb 4f 6d 14  fd 2b 13 8b c2 a5 2e 67  |.y. at .Om..+.....g|
+00000150  d8 d4 09 9e d6 22 38 b7  4a 0b 74 73 2b c2 34 f1  |....."8.J.ts+.4.|
+00000160  d1 93 e5 96 d9 74 7b f3  58 9f 6c 61 3c c0 b0 41  |.....t{.X.la<..A|
+00000170  d4 d9 2b 2b 24 23 77 5b  1c 3b bd 75 5d ce 20 54  |..++$#w[.;.u]. T|
+00000180  cf a1 63 87 1d 1e 24 c4  f3 1d 1a 50 8b aa b6 14  |..c...$....P....|
+00000190  43 ed 97 a7 75 62 f4 14  c8 52 d7 02 03 01 00 01  |C...ub...R......|
+000001a0  a3 81 93 30 81 90 30 0e  06 03 55 1d 0f 01 01 ff  |...0..0...U.....|
+000001b0  04 04 03 02 05 a0 30 1d  06 03 55 1d 25 04 16 30  |......0...U.%..0|
+000001c0  14 06 08 2b 06 01 05 05  07 03 01 06 08 2b 06 01  |...+.........+..|
+000001d0  05 05 07 03 02 30 0c 06  03 55 1d 13 01 01 ff 04  |.....0...U......|
+000001e0  02 30 00 30 19 06 03 55  1d 0e 04 12 04 10 9f 91  |.0.0...U........|
+000001f0  16 1f 43 43 3e 49 a6 de  6d b6 80 d7 9f 60 30 1b  |..CC>I..m....`0.|
+00000200  06 03 55 1d 23 04 14 30  12 80 10 48 13 49 4d 13  |..U.#..0...H.IM.|
+00000210  7e 16 31 bb a3 01 d5 ac  ab 6e 7b 30 19 06 03 55  |~.1......n{0...U|
+00000220  1d 11 04 12 30 10 82 0e  65 78 61 6d 70 6c 65 2e  |....0...example.|
+00000230  67 6f 6c 61 6e 67 30 0d  06 09 2a 86 48 86 f7 0d  |golang0...*.H...|
+00000240  01 01 0b 05 00 03 81 81  00 9d 30 cc 40 2b 5b 50  |..........0. at +[P|
+00000250  a0 61 cb ba e5 53 58 e1  ed 83 28 a9 58 1a a9 38  |.a...SX...(.X..8|
+00000260  a4 95 a1 ac 31 5a 1a 84  66 3d 43 d3 2d d9 0b f2  |....1Z..f=C.-...|
+00000270  97 df d3 20 64 38 92 24  3a 00 bc cf 9c 7d b7 40  |... d8.$:....}.@|
+00000280  20 01 5f aa d3 16 61 09  a2 76 fd 13 c3 cc e1 0c  | ._...a..v......|
+00000290  5c ee b1 87 82 f1 6c 04  ed 73 bb b3 43 77 8d 0c  |\.....l..s..Cw..|
+000002a0  1c f1 0f a1 d8 40 83 61  c9 4c 72 2b 9d ae db 46  |..... at .a.Lr+...F|
+000002b0  06 06 4d f4 c1 b3 3e c0  d1 bd 42 d4 db fe 3d 13  |..M...>...B...=.|
+000002c0  60 84 5c 21 d3 3b e9 fa  e7 16 03 03 00 ac 0c 00  |`.\!.;..........|
+000002d0  00 a8 03 00 1d 20 84 de  31 92 b6 a5 d8 a4 88 a2  |..... ..1.......|
+000002e0  54 67 e6 61 40 f2 5a 87  0f ce 15 b1 d6 af f3 5d  |Tg.a at .Z........]|
+000002f0  99 71 d6 04 f5 52 04 01  00 80 a8 1d 8b 8c e9 a3  |.q...R..........|
+00000300  af 2d 31 e4 0f f0 26 74  c2 e5 1b ae ac 47 9c 6e  |.-1...&t.....G.n|
+00000310  6c 5f 45 7d b1 b3 2a af  36 68 42 13 95 0d 33 1c  |l_E}..*.6hB...3.|
+00000320  8d 6c 72 48 4a 94 f0 fb  82 20 cc 76 21 7f 62 e7  |.lrHJ.... .v!.b.|
+00000330  23 a3 c8 4e 3a ce f1 5c  c3 60 73 26 59 4c 94 f3  |#..N:..\.`s&YL..|
+00000340  07 36 f6 a0 b3 60 03 d5  72 1e bf c8 d9 1d 61 01  |.6...`..r.....a.|
+00000350  9a 18 57 a3 b4 de 36 1f  e1 7d dc 69 c0 fb c0 71  |..W...6..}.i...q|
+00000360  45 1f 73 0d 50 69 d3 18  97 23 60 1c 5a 9a 93 b4  |E.s.Pi...#`.Z...|
+00000370  67 cc e5 80 3b 25 d0 6c  50 c8 16 03 03 00 04 0e  |g...;%.lP.......|
+00000380  00 00 00                                          |...|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 15 94  |.....(..........|
-00000060  6f 5b 35 9d eb 14 c8 be  23 a7 05 8c 14 86 35 a7  |o[5.....#.....5.|
-00000070  5c 91 76 4f 85 b1 09 f8  0f 58 9f ec d2 a9        |\.vO.....X....|
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 20 5c c5 3e  7a 14 97 1b 55 88 25 08  |.... \.>z...U.%.|
+00000040  ad 86 48 ac f0 43 8c 17  5b 58 93 6c 7a 95 69 a8  |..H..C..[X.lz.i.|
+00000050  ad 0c b3 61 4d                                    |...aM|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 e7 7f 99 c9 fa  |..........(.....|
-00000010  e0 a3 e3 77 68 74 37 62  26 90 d6 be ec a1 ae 5a  |...wht7b&......Z|
-00000020  de af 10 f1 2e a0 42 f0  88 ed 89 54 04 b2 b9 eb  |......B....T....|
-00000030  b0 91 b8                                          |...|
+00000000  14 03 03 00 01 01 16 03  03 00 20 dd 1b 80 da d9  |.......... .....|
+00000010  73 da 7d 15 9b 92 82 01  a7 8f fe 4a 75 97 8f f4  |s.}........Ju...|
+00000020  64 1b bf cf c3 40 78 f2  52 f5 7a                 |d.... at x.R.z|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 be e7 77  |...............w|
-00000010  f9 92 ac 51 d0 34 25 34  e6 35 9e ea f0 d3 89 45  |...Q.4%4.5.....E|
-00000020  84 1b 93 15 03 03 00 1a  00 00 00 00 00 00 00 02  |................|
-00000030  1a 27 54 01 c9 7c 86 4b  61 c8 98 1b d3 15 1f 93  |.'T..|.Ka.......|
-00000040  f9 42                                             |.B|
+00000000  17 03 03 00 16 4e fa 7c  37 80 48 19 a6 03 25 7c  |.....N.|7.H...%||
+00000010  65 56 43 af 9a e8 e2 aa  e5 79 98 15 03 03 00 12  |eVC......y......|
+00000020  f9 b7 01 e8 2e 85 33 89  60 44 84 93 26 4c ec ac  |......3.`D..&L..|
+00000030  2e 6f                                             |.o|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ALPN-NoMatch b/src/crypto/tls/testdata/Client-TLSv12-ALPN-NoMatch
index 9a34e4a..62e7d11 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ALPN-NoMatch
+++ b/src/crypto/tls/testdata/Client-TLSv12-ALPN-NoMatch
@@ -1,91 +1,91 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 96 01 00 00  92 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 9c 01 00 00  98 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 47 33 74 00 00  00 05 00 05 01 00 00 00  |...G3t..........|
-00000060  00 00 0a 00 08 00 06 00  17 00 18 00 19 00 0b 00  |................|
-00000070  02 01 00 00 0d 00 0e 00  0c 04 01 04 03 05 01 05  |................|
-00000080  03 02 01 02 03 ff 01 00  01 00 00 10 00 09 00 07  |................|
-00000090  06 70 72 6f 74 6f 33 00  12 00 00                 |.proto3....|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 28 c0 2f  |.............(./|
+00000030  c0 2b c0 30 c0 2c c0 27  c0 13 c0 23 c0 09 c0 14  |.+.0.,.'...#....|
+00000040  c0 0a 00 9c 00 9d 00 3c  00 2f 00 35 c0 12 00 0a  |.......<./.5....|
+00000050  00 05 c0 11 c0 07 01 00  00 47 33 74 00 00 00 05  |.........G3t....|
+00000060  00 05 01 00 00 00 00 00  0a 00 08 00 06 00 17 00  |................|
+00000070  18 00 19 00 0b 00 02 01  00 00 0d 00 0e 00 0c 04  |................|
+00000080  01 04 03 05 01 05 03 02  01 02 03 ff 01 00 01 00  |................|
+00000090  00 10 00 09 00 07 06 70  72 6f 74 6f 33 00 12 00  |.......proto3...|
+000000a0  00                                                |.|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 18 3d 15 59 fb  |....Y...U...=.Y.|
-00000010  0a a4 93 d7 43 50 59 7f  6c f9 64 db b5 47 cc 17  |....CPY.l.d..G..|
-00000020  8c cd 91 b5 04 02 3f c0  5d 60 b7 20 75 ed d2 e9  |......?.]`. u...|
-00000030  b6 72 2d f7 66 34 2e 2f  d2 b9 80 66 eb c3 36 f6  |.r-.f4./...f..6.|
-00000040  b2 61 77 79 a9 c2 db cd  57 5a b2 6b c0 2f 00 00  |.awy....WZ.k./..|
+00000000  16 03 03 00 59 02 00 00  55 03 03 36 0e 9f 51 42  |....Y...U..6..QB|
+00000010  82 65 fa b5 17 7a 86 d6  40 33 a9 67 d3 3d aa 2f  |.e...z.. at 3.g.=./|
+00000020  89 a0 39 82 af 16 30 8e  64 80 d4 20 23 a6 d0 12  |..9...0.d.. #...|
+00000030  ff 8c fc b4 b5 47 ec 10  fe ba 73 fb 0f ab e8 1c  |.....G....s.....|
+00000040  15 c1 fb 11 c1 b2 e1 8a  f7 5d 5b ad c0 2f 00 00  |.........][../..|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
-00000060  03 02 71 0b 00 02 6d 00  02 6a 00 02 67 30 82 02  |..q...m..j..g0..|
-00000070  63 30 82 01 cc a0 03 02  01 02 02 09 00 a2 73 00  |c0............s.|
-00000080  0c 81 00 cb f3 30 0d 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000090  01 0b 05 00 30 2b 31 17  30 15 06 03 55 04 0a 13  |....0+1.0...U...|
-000000a0  0e 47 6f 6f 67 6c 65 20  54 45 53 54 49 4e 47 31  |.Google TESTING1|
-000000b0  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
-000000c0  74 30 1e 17 0d 31 35 30  31 30 31 30 30 30 30 30  |t0...15010100000|
-000000d0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
-000000e0  5a 30 26 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |Z0&1.0...U....Go|
-000000f0  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 0b 30 09  |ogle TESTING1.0.|
-00000100  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
-00000110  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
-00000120  81 89 02 81 81 00 af 87  88 f6 20 1b 95 65 6c 14  |.......... ..el.|
-00000130  ab 44 05 af 3b 45 14 e3  b7 6d fd 00 63 4d 95 7f  |.D..;E...m..cM..|
-00000140  fe 6a 62 35 86 c0 4a f9  18 7c f6 aa 25 5e 7a 64  |.jb5..J..|..%^zd|
-00000150  31 66 00 ba f4 8e 92 af  c7 6b d8 76 d4 f3 5f 41  |1f.......k.v.._A|
-00000160  cb 6e 56 15 97 1b 97 c1  3c 12 39 21 66 3d 2b 16  |.nV.....<.9!f=+.|
-00000170  d1 bc db 1c c0 a7 da b7  ca ad ba da cb d5 21 50  |..............!P|
-00000180  ec de 8d ab d1 6b 81 4b  89 02 f3 c4 be c1 6c 89  |.....k.K......l.|
-00000190  b1 44 84 bd 21 d1 04 7d  9d 16 4d f9 82 15 f6 ef  |.D..!..}..M.....|
-000001a0  fa d6 09 47 f2 fb 02 03  01 00 01 a3 81 93 30 81  |...G..........0.|
-000001b0  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
-000001c0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
-000001d0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
-000001e0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
-000001f0  06 03 55 1d 0e 04 12 04  10 12 50 8d 89 6f 1b d1  |..U.......P..o..|
-00000200  dc 54 4d 6e cb 69 5e 06  f4 30 1b 06 03 55 1d 23  |.TMn.i^..0...U.#|
-00000210  04 14 30 12 80 10 bf 3d  b6 a9 66 f2 b8 40 cf ea  |..0....=..f.. at ..|
-00000220  b4 03 78 48 1a 41 30 19  06 03 55 1d 11 04 12 30  |..xH.A0...U....0|
-00000230  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
-00000240  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
-00000250  03 81 81 00 92 7c af 91  55 12 18 96 59 31 a6 48  |.....|..U...Y1.H|
-00000260  40 d5 2d d5 ee bb 02 a0  f5 c2 1e 7c 9b b3 30 7d  |@.-........|..0}|
-00000270  3c dc 76 da 4f 3d c0 fa  ae 2d 33 24 6b 03 7b 1b  |<.v.O=...-3$k.{.|
-00000280  67 59 11 21 b5 11 bc 77  b9 d9 e0 6e a8 2d 2e 35  |gY.!...w...n.-.5|
-00000290  fa 64 5f 22 3e 63 10 6b  be ff 14 86 6d 0d f0 15  |.d_">c.k....m...|
-000002a0  31 a8 14 38 1e 3b 84 87  2c cb 98 ed 51 76 b9 b1  |1..8.;..,...Qv..|
-000002b0  4f dd db 9b 84 04 86 40  fa 51 dd ba b4 8d eb e3  |O...... at .Q......|
-000002c0  46 de 46 b9 4f 86 c7 f9  a4 c2 41 34 ac cc f6 ea  |F.F.O.....A4....|
-000002d0  b0 ab 39 18 16 03 03 00  cd 0c 00 00 c9 03 00 17  |..9.............|
-000002e0  41 04 62 52 78 76 89 36  e7 b9 a6 cc df 8e f8 c3  |A.bRxv.6........|
-000002f0  52 54 b6 42 9b 68 65 65  27 91 bf 1b 0f 21 ab a9  |RT.B.hee'....!..|
-00000300  f4 00 62 dd 70 25 b8 ec  d0 3d 9b 0c 53 16 6e eb  |..b.p%...=..S.n.|
-00000310  a8 c3 1a ad a9 de ec 27  64 07 e8 9b b8 bf 5a 6c  |.......'d.....Zl|
-00000320  87 f4 04 01 00 80 05 ec  2b f7 2e a4 5e 79 85 6f  |........+...^y.o|
-00000330  64 7a b5 fb 9a e9 f1 12  ae 28 93 4b 6d 8e a0 2f  |dz.......(.Km../|
-00000340  94 bc 38 26 01 64 ab fb  03 c8 3d 17 bc b4 43 09  |..8&.d....=...C.|
-00000350  19 c8 e9 ac 60 40 67 57  71 e3 72 22 cf b1 a7 38  |....`@gWq.r"...8|
-00000360  ac 86 88 9d 47 6f 70 c9  43 82 75 b6 bf 42 4e 72  |....Gop.C.u..BNr|
-00000370  12 48 d1 2b ce 74 02 5d  30 56 66 6f 71 8f 9b 82  |.H.+.t.]0Vfoq...|
-00000380  70 3b 92 5d fb 37 d3 cf  d3 23 27 d2 d5 8d 72 22  |p;.].7...#'...r"|
-00000390  d4 b4 92 6d 64 06 d9 0b  e0 bb 34 eb bf 42 ec 6a  |...md.....4..B.j|
-000003a0  ea e3 56 68 85 a0 16 03  03 00 04 0e 00 00 00     |..Vh...........|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  cd 0c 00 00 c9 03 00 17  41 04 11 b4 a9 10 7e 5c  |........A.....~\|
+000002d0  41 5e 39 12 15 a3 ed 5b  3e 5d 68 c8 ad 48 39 ef  |A^9....[>]h..H9.|
+000002e0  09 8b b1 a7 bf db 5f 54  49 cd d5 de 4d b3 47 4c  |......_TI...M.GL|
+000002f0  18 02 84 7c ec 75 4e d0  3e 8a d1 6c 80 83 98 64  |...|.uN.>..l...d|
+00000300  4a 81 bc 8f 84 c7 e5 b4  2d fa 04 01 00 80 72 ee  |J.......-.....r.|
+00000310  41 38 f2 b8 a1 56 81 d8  04 78 75 05 f4 78 5f f2  |A8...V...xu..x_.|
+00000320  2b 5d a2 46 23 9d 48 c8  63 a9 1d de a8 78 6e 99  |+].F#.H.c....xn.|
+00000330  cd 59 6b 19 20 f5 b1 11  e1 f8 1c 5b 40 c3 b8 cd  |.Yk. ......[@...|
+00000340  66 a3 98 37 c5 c2 5c b7  d6 cc 61 b4 5e 97 fa dd  |f..7..\...a.^...|
+00000350  b7 85 5d b6 34 8c 39 4a  60 5a 03 20 47 7f e3 65  |..].4.9J`Z. G..e|
+00000360  01 18 00 2c c3 eb be d4  aa 58 57 a9 5e 69 fb 3c  |...,.....XW.^i.<|
+00000370  fa c6 28 1a 5c f7 00 d5  21 e5 c1 30 db 84 38 c3  |..(.\...!..0..8.|
+00000380  08 aa 08 5f c9 fd a0 b7  8e d0 66 77 bf 13 16 03  |..._......fw....|
+00000390  03 00 04 0e 00 00 00                              |.......|
 >>> Flow 3 (client to server)
 00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
 00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
 00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
 00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
 00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 97 34  |.....(.........4|
-00000060  d5 c2 64 97 f6 a9 f5 60  bc 17 f3 d3 02 3f 42 a8  |..d....`.....?B.|
-00000070  2f ba eb c6 50 3c ec 9b  8d 3b 22 ed ec 35        |/...P<...;"..5|
+00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 4f 7e  |.....(........O~|
+00000060  9a 3a cc 74 a4 91 77 01  0b 0e 28 0a c5 bd 55 b7  |.:.t..w...(...U.|
+00000070  9a 4c 40 4e e9 c9 46 d5  5f c5 e1 77 c3 f2        |.L at N..F._..w..|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 9a e5 f5 51 5c  |..........(...Q\|
-00000010  cb be 5d a1 67 cc 55 aa  ba db e7 0a ab 96 3b 33  |..].g.U.......;3|
-00000020  5f 2c 8c 61 20 f1 0d 6e  ce 90 d8 39 27 d7 fb 68  |_,.a ..n...9'..h|
-00000030  d9 dd da                                          |...|
+00000000  14 03 03 00 01 01 16 03  03 00 28 62 4b 13 ef 22  |..........(bK.."|
+00000010  f9 a8 8d ec 42 3a 36 80  5d a8 5b e9 60 d1 ba 65  |....B:6.].[.`..e|
+00000020  2b d8 37 64 e5 12 b2 ef  84 75 87 0c 0f 3d 35 6e  |+.7d.....u...=5n|
+00000030  59 7c 51                                          |Y|Q|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 a8 be 7c  |...............||
-00000010  05 48 ea df 62 4a 7a 45  68 e4 dc e6 42 ff 06 f2  |.H..bJzEh...B...|
-00000020  02 33 1a 15 03 03 00 1a  00 00 00 00 00 00 00 02  |.3..............|
-00000030  66 68 f4 de da 69 b4 f9  80 9c 80 c6 46 e5 2b ae  |fh...i......F.+.|
-00000040  0e d1                                             |..|
+00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 5f cd 4d  |............._.M|
+00000010  7b a7 c0 f9 6c 1f 80 93  cf 55 3b 12 c7 21 12 86  |{...l....U;..!..|
+00000020  f6 b1 52 15 03 03 00 1a  00 00 00 00 00 00 00 02  |..R.............|
+00000030  fd 31 a4 4b d1 e9 f0 e0  18 b5 96 28 f7 b4 0c 29  |.1.K.......(...)|
+00000040  8c 0c                                             |..|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-ECDSA b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-ECDSA
index 4b88acf..336e10d 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-ECDSA
+++ b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-ECDSA
@@ -1,19 +1,20 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 f0 5c 85 c8 ff  |....Y...U...\...|
-00000010  c5 57 76 99 3d 75 e6 2e  db 31 26 c0 0c 81 c5 6b  |.Wv.=u...1&....k|
-00000020  30 79 e6 72 86 77 48 01  ec 43 1a 20 f8 fd ad b5  |0y.r.wH..C. ....|
-00000030  a0 7b f3 35 27 df ad 95  f9 56 f9 79 6c a2 6c 23  |.{.5'....V.yl.l#|
-00000040  51 4c ef fc 92 fb fa 59  97 e9 63 27 c0 09 00 00  |QL.....Y..c'....|
+00000000  16 03 03 00 59 02 00 00  55 03 03 cf 28 2c 3e 4f  |....Y...U...(,>O|
+00000010  da 6b ae 24 74 a9 91 c3  c5 55 4b ab ec 07 f8 cd  |.k.$t....UK.....|
+00000020  65 f8 fe 08 f6 9a 23 da  99 6c 5d 20 af 4a 1e 32  |e.....#..l] .J.2|
+00000030  7b bd 3c 0b b1 14 66 a3  b7 2f a4 2a c3 43 c4 e0  |{.<...f../.*.C..|
+00000040  c2 ad 78 b1 28 ab 51 06  1b 87 d2 75 c0 09 00 00  |..x.(.Q....u....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
@@ -48,24 +49,22 @@
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 03 00 d8 0c 00  00 d4 03 00 17 41 04 51  |*............A.Q|
-00000280  1e 2e 40 c5 a1 13 15 f0  bc 8a 04 e1 9a 57 74 10  |.. at ..........Wt.|
-00000290  7e b3 17 bf 0c c9 85 9b  5f bd 6b 39 c7 a6 c0 50  |~......._.k9...P|
-000002a0  0e 5e 9b b1 8c cc 57 39  e8 0f 94 02 be 28 19 16  |.^....W9.....(..|
-000002b0  94 73 2b c1 3c a7 0f c9  e7 b0 89 ac 13 53 f9 04  |.s+.<........S..|
-000002c0  03 00 8b 30 81 88 02 42  01 1b e0 ab 94 02 aa 27  |...0...B.......'|
-000002d0  fa 7b 99 9c 68 36 d8 2d  2e e0 92 84 c7 7b 37 74  |.{..h6.-.....{7t|
-000002e0  6a ad a8 f5 50 3f 74 d5  e8 8e 5a db 31 43 c8 98  |j...P?t...Z.1C..|
-000002f0  d3 ee 61 43 80 9a 72 eb  2d 2b 21 b8 33 aa 61 0a  |..aC..r.-+!.3.a.|
-00000300  cd dc 85 88 29 26 83 ee  3c b2 02 42 00 b6 ea 34  |....)&..<..B...4|
-00000310  30 71 5c 0a 9a 6d a2 25  62 1c 3e 13 90 9c a3 b8  |0q\..m.%b.>.....|
-00000320  0d 97 a8 06 26 9e 31 50  88 9a b9 ff 12 63 a8 14  |....&.1P.....c..|
-00000330  18 f3 c2 b0 af d1 27 25  a9 ec ef 69 85 7a 72 c6  |......'%...i.zr.|
-00000340  b0 88 d2 c1 41 43 f4 69  62 25 13 eb f9 f8 16 03  |....AC.ib%......|
-00000350  03 00 2e 0d 00 00 26 03  01 02 40 00 1e 06 01 06  |......&... at .....|
-00000360  02 06 03 05 01 05 02 05  03 04 01 04 02 04 03 03  |................|
-00000370  01 03 02 03 03 02 01 02  02 02 03 00 00 0e 00 00  |................|
-00000380  00                                                |.|
+00000270  2a 16 03 03 00 b7 0c 00  00 b3 03 00 1d 20 18 6f  |*............ .o|
+00000280  77 a5 2b 27 2c 52 fc 6c  8a 34 41 1c a8 c6 4f 90  |w.+',R.l.4A...O.|
+00000290  a9 4b b7 e0 39 8b b1 f5  a6 15 4b 94 e8 2c 04 03  |.K..9.....K..,..|
+000002a0  00 8b 30 81 88 02 42 00  dc 3a 14 a2 38 32 c1 40  |..0...B..:..82.@|
+000002b0  98 83 17 94 e9 2a 0d 95  c3 59 d6 76 94 c2 3e a0  |.....*...Y.v..>.|
+000002c0  f7 e0 5d 64 47 5a d1 d9  ed d2 1c 6b 13 3e e7 83  |..]dGZ.....k.>..|
+000002d0  6e bb 53 33 03 7d 69 c6  8f 9d 98 d7 96 9c 73 e3  |n.S3.}i.......s.|
+000002e0  12 bd 69 1f b1 d3 f4 25  d7 02 42 01 11 6d c8 53  |..i....%..B..m.S|
+000002f0  9b bf f4 db ff 8a 00 82  93 f7 b5 bf c9 bb cd ec  |................|
+00000300  64 f8 d9 6d 36 0d f8 db  ce 9d 65 a0 5e 5a e0 13  |d..m6.....e.^Z..|
+00000310  ec 08 73 2c 3f 8c c6 5b  08 cc 0f 4a 7d 6b 5e 89  |..s,?..[...J}k^.|
+00000320  bf 4a 4e db 51 5a 9f 51  3e 9d 9a c5 84 16 03 03  |.JN.QZ.Q>.......|
+00000330  00 2a 0d 00 00 26 03 01  02 40 00 1e 06 01 06 02  |.*...&... at ......|
+00000340  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000350  03 02 03 03 02 01 02 02  02 03 00 00 16 03 03 00  |................|
+00000360  04 0e 00 00 00                                    |.....|
 >>> Flow 3 (client to server)
 00000000  16 03 03 02 0a 0b 00 02  06 00 02 03 00 02 00 30  |...............0|
 00000010  82 01 fc 30 82 01 5e 02  09 00 9a 30 84 6c 26 35  |...0..^....0.l&5|
@@ -100,36 +99,34 @@
 000001e0  be e8 91 b3 da 1a f5 5d  a3 23 f5 26 8b 45 70 8d  |.......].#.&.Ep.|
 000001f0  65 62 9b 7e 01 99 3d 18  f6 10 9a 38 61 9b 2e 57  |eb.~..=....8a..W|
 00000200  e4 fa cc b1 8a ce e2 23  a0 87 f0 e1 67 51 eb 16  |.......#....gQ..|
-00000210  03 03 00 46 10 00 00 42  41 04 1e 18 37 ef 0d 19  |...F...BA...7...|
-00000220  51 88 35 75 71 b5 e5 54  5b 12 2e 8f 09 67 fd a7  |Q.5uq..T[....g..|
-00000230  24 20 3e b2 56 1c ce 97  28 5e f8 2b 2d 4f 9e f1  |$ >.V...(^.+-O..|
-00000240  07 9f 6c 4b 5b 83 56 e2  32 42 e9 58 b6 d7 49 a6  |..lK[.V.2B.X..I.|
-00000250  b5 68 1a 41 03 56 6b dc  5a 89 16 03 03 00 93 0f  |.h.A.Vk.Z.......|
-00000260  00 00 8f 05 03 00 8b 30  81 88 02 42 00 8a 82 c2  |.......0...B....|
-00000270  c0 30 8c a1 12 c4 4a ed  d1 00 3f 2d ee bd 8e 9c  |.0....J...?-....|
-00000280  a5 a0 d9 6f 44 27 49 60  e9 75 01 ee b4 0d 87 25  |...oD'I`.u.....%|
-00000290  2a 8d 67 f1 e3 d9 49 6f  a0 34 90 76 93 52 f9 17  |*.g...Io.4.v.R..|
-000002a0  fb 1b cc d0 5a f4 50 37  9c 4c 44 b6 61 5f 02 42  |....Z.P7.LD.a_.B|
-000002b0  01 ad 85 38 e9 3a 69 35  ea 74 76 2c 09 6b ab d4  |...8.:i5.tv,.k..|
-000002c0  e0 dc d1 d5 03 41 22 8e  8b 53 98 b7 f1 b6 e9 29  |.....A"..S.....)|
-000002d0  d2 57 34 dc e0 b6 71 77  79 bd 57 61 7c 30 77 00  |.W4...qwy.Wa|0w.|
-000002e0  7a 42 2d 1f ed e8 14 da  16 33 c6 31 e4 3d 53 3a  |zB-......3.1.=S:|
-000002f0  9a 37 14 03 03 00 01 01  16 03 03 00 40 00 00 00  |.7.......... at ...|
-00000300  00 00 00 00 00 00 00 00  00 00 00 00 00 d4 fe c1  |................|
-00000310  a6 fb 21 78 21 80 af 0d  da a1 80 68 e2 9c ec 0b  |..!x!......h....|
-00000320  57 8c 2a 7e f1 11 3b 52  ea 17 00 d1 d4 14 78 c5  |W.*~..;R......x.|
-00000330  81 39 12 ad 30 98 93 1b  29 77 45 7d 00           |.9..0...)wE}.|
+00000210  03 03 00 25 10 00 00 21  20 2f e5 7d a3 47 cd 62  |...%...! /.}.G.b|
+00000220  43 15 28 da ac 5f bb 29  07 30 ff f6 84 af c4 cf  |C.(.._.).0......|
+00000230  c2 ed 90 99 5f 58 cb 3b  74 16 03 03 00 93 0f 00  |...._X.;t.......|
+00000240  00 8f 05 03 00 8b 30 81  88 02 42 01 32 6d 32 38  |......0...B.2m28|
+00000250  d6 bd 1b b6 c5 80 f2 ea  60 b8 bf 3f b6 76 68 1b  |........`..?.vh.|
+00000260  66 fb 5d 69 0b 25 09 7f  2d 73 ad 7e cd 98 cb b5  |f.]i.%..-s.~....|
+00000270  93 4e 4f 1c 4e 3f a1 39  cf a0 70 a6 3d 29 36 27  |.NO.N?.9..p.=)6'|
+00000280  51 e0 55 95 11 df 00 88  6c 38 d6 de 36 02 42 01  |Q.U.....l8..6.B.|
+00000290  67 50 81 90 a7 ae b5 e2  34 75 81 41 c2 71 8d 0c  |gP......4u.A.q..|
+000002a0  9a 20 e7 33 af 0e 61 48  85 51 a1 f7 90 17 d1 ad  |. .3..aH.Q......|
+000002b0  b3 e1 cf 3e 12 fc ce 39  16 a8 78 3b 69 0d 79 76  |...>...9..x;i.yv|
+000002c0  03 17 75 c2 a0 63 5e dc  0a a7 c9 aa 15 2a 83 65  |..u..c^......*.e|
+000002d0  df 14 03 03 00 01 01 16  03 03 00 40 00 00 00 00  |........... at ....|
+000002e0  00 00 00 00 00 00 00 00  00 00 00 00 27 da 48 f6  |............'.H.|
+000002f0  d3 00 98 b9 a6 b7 41 0b  eb e6 d1 d7 82 9a 0c 59  |......A........Y|
+00000300  8a 42 1c 99 59 af da a7  5b 88 ab b6 7d 01 bc 0f  |.B..Y...[...}...|
+00000310  45 08 c4 05 0d 2a 4a 83  bf eb b1 b6              |E....*J.....|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 40 f7 0a 50 d0 87  |.......... at ..P..|
-00000010  fb f9 be b0 6b 8d 9b a5  8b d2 56 27 67 7d 3c 51  |....k.....V'g}<Q|
-00000020  af 53 8c 7d 61 9f 12 a5  54 5d ec 56 36 31 01 73  |.S.}a...T].V61.s|
-00000030  37 cb 5f ff 36 3c 1c 4a  e3 db ec 99 bc 86 15 e4  |7._.6<.J........|
-00000040  cd 5d 87 bd d7 80 c7 b1  fe 42 9f                 |.].......B.|
+00000000  14 03 03 00 01 01 16 03  03 00 40 73 7c e6 43 b9  |.......... at s|.C.|
+00000010  47 85 1c 50 f1 cb a1 29  79 02 dd 13 85 2a d9 a2  |G..P...)y....*..|
+00000020  07 50 e4 80 c4 7e 66 ee  f2 1a 21 1d cd e4 ff 4a  |.P...~f...!....J|
+00000030  a4 61 9d b4 a1 26 88 72  20 2b 06 77 c3 8b 3b 21  |.a...&.r +.w..;!|
+00000040  53 33 02 3d a2 06 77 3b  a5 a6 0b                 |S3.=..w;...|
 >>> Flow 5 (client to server)
 00000000  17 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 ef 81 cf  63 f1 b5 6b b2 30 6f 00  |........c..k.0o.|
-00000020  0e c0 0c 5d d4 85 76 d2  30 db 6b 14 06 e4 75 0b  |...]..v.0.k...u.|
-00000030  cf fc 72 aa 64 15 03 03  00 30 00 00 00 00 00 00  |..r.d....0......|
-00000040  00 00 00 00 00 00 00 00  00 00 ef 28 8a e7 15 51  |...........(...Q|
-00000050  0d 0d 27 4f 36 35 f6 43  28 d2 16 dc a3 35 33 3e  |..'O65.C(....53>|
-00000060  be 80 db 31 a9 89 3d 17  c2 58                    |...1..=..X|
+00000010  00 00 00 00 00 1a 45 68  03 9b f0 42 e4 21 5e d8  |......Eh...B.!^.|
+00000020  98 d6 46 67 2b 93 80 92  1f 91 60 a3 05 04 1c a0  |..Fg+.....`.....|
+00000030  1b a9 ce 45 03 15 03 03  00 30 00 00 00 00 00 00  |...E.....0......|
+00000040  00 00 00 00 00 00 00 00  00 00 6b 23 42 c8 5c 29  |..........k#B.\)|
+00000050  f5 1f 7c d5 80 c4 9f 6f  12 77 95 71 8f 82 f9 63  |..|....o.w.q...c|
+00000060  07 2c 6d ed 6d c6 4f 90  50 a3                    |.,m.m.O.P.|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-RSA b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-RSA
index 53f2f86..fb6c940 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-RSA
+++ b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-RSA
@@ -1,62 +1,74 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 51 02 00 00  4d 03 03 e8 b5 19 bd df  |....Q...M.......|
-00000010  e5 18 78 4b 01 f1 3f 7f  ab 91 05 78 98 77 50 bf  |..xK..?....x.wP.|
-00000020  60 f5 a4 76 7b 3c 40 9f  54 56 68 20 a1 99 57 a7  |`..v{<@.TVh ..W.|
-00000030  a8 46 ca 26 22 d8 bb 8d  93 12 48 ff be 8e d3 d4  |.F.&".....H.....|
-00000040  e0 fd cd ce f5 d9 a9 2e  fe d4 cd 85 00 05 00 00  |................|
-00000050  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000060  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000070  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000090  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-000000a0  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-000000b0  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000c0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000d0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000e0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000f0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-00000100  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-00000110  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000120  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000130  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000140  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000150  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000160  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000170  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000180  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000190  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-000001a0  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-000001b0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001c0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001d0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001e0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001f0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-00000200  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-00000210  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000220  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000230  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000240  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000250  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000260  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000270  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000280  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000290  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-000002a0  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-000002b0  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002c0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002d0  2e 0d 00 00 26 03 01 02  40 00 1e 06 01 06 02 06  |....&... at .......|
-000002e0  03 05 01 05 02 05 03 04  01 04 02 04 03 03 01 03  |................|
-000002f0  02 03 03 02 01 02 02 02  03 00 00 0e 00 00 00     |...............|
+00000000  16 03 03 00 59 02 00 00  55 03 03 2e d2 1c 3f f8  |....Y...U.....?.|
+00000010  3a dc be 78 0b fa 03 00  e0 9a b9 62 34 45 f8 34  |:..x.......b4E.4|
+00000020  54 21 4c c0 76 a6 e1 5a  a1 67 c2 20 1b 98 25 34  |T!L.v..Z.g. ..%4|
+00000030  79 ac 59 b5 39 c8 93 10  a9 ea 9d 25 3d 2c d8 69  |y.Y.9......%=,.i|
+00000040  da d8 33 75 ef 44 4c 76  92 2b 3b b4 c0 2f 00 00  |..3u.DLv.+;../..|
+00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 06 be 1b 0b d8 95 59  |........ ......Y|
+000002d0  b2 13 1c 4a 06 b8 36 3e  4f 98 3f 81 11 3e 7d 21  |...J..6>O.?..>}!|
+000002e0  fa d9 f0 db 1b 41 4a d0  14 04 01 00 80 ca 57 f5  |.....AJ.......W.|
+000002f0  e7 b6 72 7e 3f b0 67 f2  a2 d0 84 d5 7f 7d 83 ff  |..r~?.g......}..|
+00000300  92 73 4f 19 f7 94 b6 d7  95 f4 1b 56 2a fc fa 24  |.sO........V*..$|
+00000310  3e fe 00 65 52 76 c8 30  8a bf ae fe b5 c9 f2 47  |>..eRv.0.......G|
+00000320  0a 71 ad c1 6a 61 8c b5  ab 59 09 12 92 b2 b4 ad  |.q..ja...Y......|
+00000330  cb cc ac c4 30 e9 a4 8a  82 4e 2e d6 1d 16 46 dd  |....0....N....F.|
+00000340  60 37 50 b8 ae 83 c1 e6  1d ba 8c c7 18 f7 5e d7  |`7P...........^.|
+00000350  23 e5 8a 14 ba e4 8e a1  77 8a b6 41 03 61 8a 25  |#.......w..A.a.%|
+00000360  8a 27 f8 cb 2e 4a e0 07  aa bf 03 32 98 16 03 03  |.'...J.....2....|
+00000370  00 2a 0d 00 00 26 03 01  02 40 00 1e 06 01 06 02  |.*...&... at ......|
+00000380  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000390  03 02 03 03 02 01 02 02  02 03 00 00 16 03 03 00  |................|
+000003a0  04 0e 00 00 00                                    |.....|
 >>> Flow 3 (client to server)
 00000000  16 03 03 02 0a 0b 00 02  06 00 02 03 00 02 00 30  |...............0|
 00000010  82 01 fc 30 82 01 5e 02  09 00 9a 30 84 6c 26 35  |...0..^....0.l&5|
@@ -91,33 +103,30 @@
 000001e0  be e8 91 b3 da 1a f5 5d  a3 23 f5 26 8b 45 70 8d  |.......].#.&.Ep.|
 000001f0  65 62 9b 7e 01 99 3d 18  f6 10 9a 38 61 9b 2e 57  |eb.~..=....8a..W|
 00000200  e4 fa cc b1 8a ce e2 23  a0 87 f0 e1 67 51 eb 16  |.......#....gQ..|
-00000210  03 03 00 86 10 00 00 82  00 80 73 bd 73 65 92 86  |..........s.se..|
-00000220  23 41 14 79 7f d5 c1 10  ce 94 4d ad 9c c3 a9 87  |#A.y......M.....|
-00000230  b5 32 52 f8 6b 11 93 2d  9b 98 0b 8b 1d c0 f6 53  |.2R.k..-.......S|
-00000240  17 6d c7 9c 2e ae c9 6f  cc 99 23 38 37 1a 10 fe  |.m.....o..#87...|
-00000250  05 0b b5 55 0a 14 e9 60  7d 70 26 98 e2 54 d9 65  |...U...`}p&..T.e|
-00000260  cf 2e f4 53 5f 1d aa 3a  f6 33 7b eb 4c 0e b3 ff  |...S_..:.3{.L...|
-00000270  5a db 36 2a 47 f3 df f9  fc f5 31 78 83 aa 6b 52  |Z.6*G.....1x..kR|
-00000280  b7 ba 1a 96 bc fa c1 a1  a9 bb 2b f5 38 89 00 4d  |..........+.8..M|
-00000290  e5 78 13 4e a4 38 46 42  dc 16 16 03 03 00 92 0f  |.x.N.8FB........|
-000002a0  00 00 8e 05 03 00 8a 30  81 87 02 42 01 4c f6 31  |.......0...B.L.1|
-000002b0  4f ec 64 bb ce d0 96 4d  66 f3 8d 64 78 c9 2d 47  |O.d....Mf..dx.-G|
-000002c0  39 02 88 31 49 84 7f cc  a8 af c1 17 35 fb 46 b1  |9..1I.......5.F.|
-000002d0  dc 07 58 71 13 6b 8e 71  2b 94 fd 41 7c 26 45 39  |..Xq.k.q+..A|&E9|
-000002e0  28 b1 aa f7 5b 89 04 de  84 d1 b5 d9 9f f3 02 41  |(...[..........A|
-000002f0  4e f6 2a ed 39 ee 63 68  da f5 ae 1b 4d f5 01 0f  |N.*.9.ch....M...|
-00000300  bc f7 05 d2 96 42 67 e3  8f ff 27 d5 bf c4 53 bf  |.....Bg...'...S.|
-00000310  8a d7 46 58 05 54 94 d8  73 a9 d9 38 40 5f cb 8c  |..FX.T..s..8 at _..|
-00000320  c7 d1 94 56 2a e1 61 32  29 f7 c9 c1 e8 95 30 e3  |...V*.a2).....0.|
-00000330  33 14 03 03 00 01 01 16  03 03 00 24 b1 86 d2 50  |3..........$...P|
-00000340  fc ea 68 b1 d9 3d b7 2c  fd 2c 87 f0 d4 44 2b 22  |..h..=.,.,...D+"|
-00000350  b8 47 74 77 46 14 6d 18  b3 08 9c 3a d4 a1 ba cb  |.GtwF.m....:....|
+00000210  03 03 00 25 10 00 00 21  20 2f e5 7d a3 47 cd 62  |...%...! /.}.G.b|
+00000220  43 15 28 da ac 5f bb 29  07 30 ff f6 84 af c4 cf  |C.(.._.).0......|
+00000230  c2 ed 90 99 5f 58 cb 3b  74 16 03 03 00 92 0f 00  |...._X.;t.......|
+00000240  00 8e 05 03 00 8a 30 81  87 02 41 19 c6 1e f0 f4  |......0...A.....|
+00000250  ca 79 d7 8c 36 0f 56 9a  9d 07 55 31 fe 63 f1 ec  |.y..6.V...U1.c..|
+00000260  20 80 6f 12 ed 7f bb c0  87 0a 0d 68 81 89 bd 8b  | .o........h....|
+00000270  19 04 5e c0 19 8a d2 0f  6d 71 83 59 ee a7 be be  |..^.....mq.Y....|
+00000280  1d bb 2f 12 53 9b ca 58  0e a6 8d ae 02 42 00 d0  |../.S..X.....B..|
+00000290  4c 69 75 30 86 d1 da 73  1b 8e 3e e1 82 9b f3 58  |Liu0...s..>....X|
+000002a0  8f 6d 0a 10 86 72 5f 90  17 d1 ac 34 8a b5 60 d0  |.m...r_....4..`.|
+000002b0  b8 54 0f 05 7f cd 6a c0  62 b5 04 d9 3a 98 95 b6  |.T....j.b...:...|
+000002c0  b3 00 1d 94 6e 79 35 57  d2 78 a4 7a 4a 45 89 d1  |....ny5W.x.zJE..|
+000002d0  14 03 03 00 01 01 16 03  03 00 28 00 00 00 00 00  |..........(.....|
+000002e0  00 00 00 cf b5 3c cf 0a  b7 6b 51 cb fe 06 4c df  |.....<...kQ...L.|
+000002f0  2c 79 a6 5e a8 75 8b 4c  44 7b ae ff 64 d7 67 dc  |,y.^.u.LD{..d.g.|
+00000300  af ef 54                                          |..T|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 70 c7 ee d4 d3  |..........$p....|
-00000010  d3 ad dc 5a d1 a3 01 89  4d ae 0f b9 7b 97 91 4a  |...Z....M...{..J|
-00000020  c0 5b e2 94 ef 5f 2f e0  90 1a 18 8a e8 50 9d     |.[..._/......P.|
+00000000  14 03 03 00 01 01 16 03  03 00 28 1c 12 5e 29 ba  |..........(..^).|
+00000010  34 b3 d8 ae f7 2a 83 0d  3e 21 ec 91 c9 fa 7f d1  |4....*..>!......|
+00000020  42 7e 8d d9 e5 ed 4e f9  ae 95 66 27 85 cc 44 2d  |B~....N...f'..D-|
+00000030  cd a3 26                                          |..&|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1a e8 e8 00  30 71 09 61 65 55 90 c8  |........0q.aeU..|
-00000010  d6 fd 8d 5d a9 fb e6 2b  d4 45 a9 8c ea 2f 0b 15  |...]...+.E.../..|
-00000020  03 03 00 16 f2 d3 36 ce  26 42 59 1b d7 15 c5 c4  |......6.&BY.....|
-00000030  8b 0b 06 0a d0 fd 78 62  3d 39                    |......xb=9|
+00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 8b 4c 36  |..............L6|
+00000010  6f b8 69 16 0a 40 67 05  5e a8 e6 48 cc ad 7b 29  |o.i.. at g.^..H..{)|
+00000020  95 3d 02 15 03 03 00 1a  00 00 00 00 00 00 00 02  |.=..............|
+00000030  58 e3 b5 8e 30 e7 5d 02  cd e5 c0 11 95 3a ef a9  |X...0.]......:..|
+00000040  d7 86                                             |..|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-AES256-GCM-SHA384 b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-AES256-GCM-SHA384
index 9ba51f5..17fc8f8 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-AES256-GCM-SHA384
+++ b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-AES256-GCM-SHA384
@@ -1,134 +1,130 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 f5 6d a6 9a 3d  |....Y...U...m..=|
-00000010  b4 32 c7 59 b9 f7 09 bb  56 7e 06 26 02 ac eb dd  |.2.Y....V~.&....|
-00000020  78 91 e4 cd f9 f4 e7 98  7f 13 f0 20 6d d5 42 4a  |x.......... m.BJ|
-00000030  85 ac 86 9a a6 78 6d 5c  d7 ef 9d 16 dc ff 5a 41  |.....xm\......ZA|
-00000040  91 5a 54 ff ba f6 90 f4  2a 4f fd 37 c0 30 00 00  |.ZT.....*O.7.0..|
+00000000  16 03 03 00 59 02 00 00  55 03 03 31 df 35 e4 36  |....Y...U..1.5.6|
+00000010  c5 f1 b4 9f e7 5d fa e1  e0 23 04 54 bd 2b fb ab  |.....]...#.T.+..|
+00000020  a2 37 8f 35 eb 79 47 e6  f8 2b cb 20 ba d8 db 26  |.7.5.yG..+. ...&|
+00000030  ce 6b 4a e9 1e 0c 46 9f  4d 85 cb d7 b0 e2 3d 20  |.kJ...F.M.....= |
+00000040  58 43 83 37 e1 53 ac 3b  d9 b3 fd 0a c0 30 00 00  |XC.7.S.;.....0..|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
-00000060  03 02 71 0b 00 02 6d 00  02 6a 00 02 67 30 82 02  |..q...m..j..g0..|
-00000070  63 30 82 01 cc a0 03 02  01 02 02 09 00 a2 73 00  |c0............s.|
-00000080  0c 81 00 cb f3 30 0d 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000090  01 0b 05 00 30 2b 31 17  30 15 06 03 55 04 0a 13  |....0+1.0...U...|
-000000a0  0e 47 6f 6f 67 6c 65 20  54 45 53 54 49 4e 47 31  |.Google TESTING1|
-000000b0  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
-000000c0  74 30 1e 17 0d 31 35 30  31 30 31 30 30 30 30 30  |t0...15010100000|
-000000d0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
-000000e0  5a 30 26 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |Z0&1.0...U....Go|
-000000f0  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 0b 30 09  |ogle TESTING1.0.|
-00000100  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
-00000110  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
-00000120  81 89 02 81 81 00 af 87  88 f6 20 1b 95 65 6c 14  |.......... ..el.|
-00000130  ab 44 05 af 3b 45 14 e3  b7 6d fd 00 63 4d 95 7f  |.D..;E...m..cM..|
-00000140  fe 6a 62 35 86 c0 4a f9  18 7c f6 aa 25 5e 7a 64  |.jb5..J..|..%^zd|
-00000150  31 66 00 ba f4 8e 92 af  c7 6b d8 76 d4 f3 5f 41  |1f.......k.v.._A|
-00000160  cb 6e 56 15 97 1b 97 c1  3c 12 39 21 66 3d 2b 16  |.nV.....<.9!f=+.|
-00000170  d1 bc db 1c c0 a7 da b7  ca ad ba da cb d5 21 50  |..............!P|
-00000180  ec de 8d ab d1 6b 81 4b  89 02 f3 c4 be c1 6c 89  |.....k.K......l.|
-00000190  b1 44 84 bd 21 d1 04 7d  9d 16 4d f9 82 15 f6 ef  |.D..!..}..M.....|
-000001a0  fa d6 09 47 f2 fb 02 03  01 00 01 a3 81 93 30 81  |...G..........0.|
-000001b0  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
-000001c0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
-000001d0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
-000001e0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
-000001f0  06 03 55 1d 0e 04 12 04  10 12 50 8d 89 6f 1b d1  |..U.......P..o..|
-00000200  dc 54 4d 6e cb 69 5e 06  f4 30 1b 06 03 55 1d 23  |.TMn.i^..0...U.#|
-00000210  04 14 30 12 80 10 bf 3d  b6 a9 66 f2 b8 40 cf ea  |..0....=..f.. at ..|
-00000220  b4 03 78 48 1a 41 30 19  06 03 55 1d 11 04 12 30  |..xH.A0...U....0|
-00000230  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
-00000240  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
-00000250  03 81 81 00 92 7c af 91  55 12 18 96 59 31 a6 48  |.....|..U...Y1.H|
-00000260  40 d5 2d d5 ee bb 02 a0  f5 c2 1e 7c 9b b3 30 7d  |@.-........|..0}|
-00000270  3c dc 76 da 4f 3d c0 fa  ae 2d 33 24 6b 03 7b 1b  |<.v.O=...-3$k.{.|
-00000280  67 59 11 21 b5 11 bc 77  b9 d9 e0 6e a8 2d 2e 35  |gY.!...w...n.-.5|
-00000290  fa 64 5f 22 3e 63 10 6b  be ff 14 86 6d 0d f0 15  |.d_">c.k....m...|
-000002a0  31 a8 14 38 1e 3b 84 87  2c cb 98 ed 51 76 b9 b1  |1..8.;..,...Qv..|
-000002b0  4f dd db 9b 84 04 86 40  fa 51 dd ba b4 8d eb e3  |O...... at .Q......|
-000002c0  46 de 46 b9 4f 86 c7 f9  a4 c2 41 34 ac cc f6 ea  |F.F.O.....A4....|
-000002d0  b0 ab 39 18 16 03 03 00  cd 0c 00 00 c9 03 00 17  |..9.............|
-000002e0  41 04 2a 2a b7 63 a8 8e  34 67 32 18 57 6e fe 2a  |A.**.c..4g2.Wn.*|
-000002f0  51 41 41 5f 65 a3 a7 e9  d6 0b 42 7f 77 fb 40 09  |QAA_e.....B.w. at .|
-00000300  c8 7a a2 9b fd 5f 6e 2b  ce 85 f6 24 c2 8d e8 bb  |.z..._n+...$....|
-00000310  69 3e dc 51 15 6f a8 db  a4 fb 11 10 70 04 82 6a  |i>.Q.o......p..j|
-00000320  7b 81 04 01 00 80 7a a3  c9 1b e6 02 33 39 55 36  |{.....z.....39U6|
-00000330  dc f9 2d f7 00 5b 8d f4  de 7a f7 3b 1b 4c 9a 27  |..-..[...z.;.L.'|
-00000340  f6 db 3c d1 6b f8 d6 7a  20 53 33 5f 88 9f f6 73  |..<.k..z S3_...s|
-00000350  90 2f 35 9e f6 05 b5 80  96 4f c8 85 e6 72 95 ba  |./5......O...r..|
-00000360  3b 42 43 94 c3 0b db 91  ff 6b 24 c6 b1 78 de 18  |;BC......k$..x..|
-00000370  9f d5 3b 33 53 22 45 bf  cb b2 d2 77 ce 03 56 7b  |..;3S"E....w..V{|
-00000380  b7 56 b6 ec 04 64 62 04  f7 f8 52 1a 47 49 01 71  |.V...db...R.GI.q|
-00000390  29 9e ee 68 1f e9 c6 36  fb 77 4c 9a 14 90 e1 70  |)..h...6.wL....p|
-000003a0  7d 7e 77 92 a6 18 16 03  03 00 2e 0d 00 00 26 03  |}~w...........&.|
-000003b0  01 02 40 00 1e 06 01 06  02 06 03 05 01 05 02 05  |.. at .............|
-000003c0  03 04 01 04 02 04 03 03  01 03 02 03 03 02 01 02  |................|
-000003d0  02 02 03 00 00 0e 00 00  00                       |.........|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 9a 18 f9 2e 33 f7 bb  |........ ....3..|
+000002d0  ca 60 0b 51 ad 5c 01 e2  61 82 0b 3f 09 8f 78 9d  |.`.Q.\..a..?..x.|
+000002e0  3b 11 8b e0 4a 35 2e d5  54 04 01 00 80 90 94 0e  |;...J5..T.......|
+000002f0  bf 3a b7 95 d3 58 cc 65  c3 79 5e 1e bb d9 21 56  |.:...X.e.y^...!V|
+00000300  06 93 6c 2b 6e 26 55 ee  26 c3 02 44 7e db 35 9b  |..l+n&U.&..D~.5.|
+00000310  d4 d4 1a a0 65 35 41 a4  6c ce de 1f 94 ff b4 1b  |....e5A.l.......|
+00000320  1e 9c 28 0b 4c 8d 55 d0  d8 be f1 df e0 d1 1a b5  |..(.L.U.........|
+00000330  c8 be 2c 5a 2c c3 3f ea  4f e6 d5 b4 6b e1 ff eb  |..,Z,.?.O...k...|
+00000340  f3 f3 40 54 d5 62 1f a0  fc b2 34 66 ee c5 27 a6  |.. at T.b....4f..'.|
+00000350  2b 2a b9 5d 3f 36 28 eb  39 99 25 e5 04 d2 18 13  |+*.]?6(.9.%.....|
+00000360  3c 23 93 d0 04 37 85 b0  4d 6e 9b 32 9a 16 03 03  |<#...7..Mn.2....|
+00000370  00 2a 0d 00 00 26 03 01  02 40 00 1e 06 01 06 02  |.*...&... at ......|
+00000380  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000390  03 02 03 03 02 01 02 02  02 03 00 00 16 03 03 00  |................|
+000003a0  04 0e 00 00 00                                    |.....|
 >>> Flow 3 (client to server)
-00000000  16 03 03 01 fb 0b 00 01  f7 00 01 f4 00 01 f1 30  |...............0|
-00000010  82 01 ed 30 82 01 58 a0  03 02 01 02 02 01 00 30  |...0..X........0|
-00000020  0b 06 09 2a 86 48 86 f7  0d 01 01 05 30 26 31 10  |...*.H......0&1.|
-00000030  30 0e 06 03 55 04 0a 13  07 41 63 6d 65 20 43 6f  |0...U....Acme Co|
-00000040  31 12 30 10 06 03 55 04  03 13 09 31 32 37 2e 30  |1.0...U....127.0|
-00000050  2e 30 2e 31 30 1e 17 0d  31 31 31 32 30 38 30 37  |.0.10...11120807|
-00000060  35 35 31 32 5a 17 0d 31  32 31 32 30 37 30 38 30  |5512Z..121207080|
-00000070  30 31 32 5a 30 26 31 10  30 0e 06 03 55 04 0a 13  |012Z0&1.0...U...|
-00000080  07 41 63 6d 65 20 43 6f  31 12 30 10 06 03 55 04  |.Acme Co1.0...U.|
-00000090  03 13 09 31 32 37 2e 30  2e 30 2e 31 30 81 9c 30  |...127.0.0.10..0|
-000000a0  0b 06 09 2a 86 48 86 f7  0d 01 01 01 03 81 8c 00  |...*.H..........|
-000000b0  30 81 88 02 81 80 4e d0  7b 31 e3 82 64 d9 59 c0  |0.....N.{1..d.Y.|
-000000c0  c2 87 a4 5e 1e 8b 73 33  c7 63 53 df 66 92 06 84  |...^..s3.cS.f...|
-000000d0  f6 64 d5 8f e4 36 a7 1d  2b e8 b3 20 36 45 23 b5  |.d...6..+.. 6E#.|
-000000e0  e3 95 ae ed e0 f5 20 9c  8d 95 df 7f 5a 12 ef 87  |...... .....Z...|
-000000f0  e4 5b 68 e4 e9 0e 74 ec  04 8a 7f de 93 27 c4 01  |.[h...t......'..|
-00000100  19 7a bd f2 dc 3d 14 ab  d0 54 ca 21 0c d0 4d 6e  |.z...=...T.!..Mn|
-00000110  87 2e 5c c5 d2 bb 4d 4b  4f ce b6 2c f7 7e 88 ec  |..\...MKO..,.~..|
-00000120  7c d7 02 91 74 a6 1e 0c  1a da e3 4a 5a 2e de 13  ||...t......JZ...|
-00000130  9c 4c 40 88 59 93 02 03  01 00 01 a3 32 30 30 30  |.L at .Y.......2000|
-00000140  0e 06 03 55 1d 0f 01 01  ff 04 04 03 02 00 a0 30  |...U...........0|
-00000150  0d 06 03 55 1d 0e 04 06  04 04 01 02 03 04 30 0f  |...U..........0.|
-00000160  06 03 55 1d 23 04 08 30  06 80 04 01 02 03 04 30  |..U.#..0.......0|
-00000170  0b 06 09 2a 86 48 86 f7  0d 01 01 05 03 81 81 00  |...*.H..........|
-00000180  36 1f b3 7a 0c 75 c9 6e  37 46 61 2b d5 bd c0 a7  |6..z.u.n7Fa+....|
-00000190  4b cc 46 9a 81 58 7c 85  79 29 c8 c8 c6 67 dd 32  |K.F..X|.y)...g.2|
-000001a0  56 45 2b 75 b6 e9 24 a9  50 9a be 1f 5a fa 1a 15  |VE+u..$.P...Z...|
-000001b0  d9 cc 55 95 72 16 83 b9  c2 b6 8f fd 88 8c 38 84  |..U.r.........8.|
-000001c0  1d ab 5d 92 31 13 4f fd  83 3b c6 9d f1 11 62 b6  |..].1.O..;....b.|
-000001d0  8b ec ab 67 be c8 64 b0  11 50 46 58 17 6b 99 1c  |...g..d..PFX.k..|
-000001e0  d3 1d fc 06 f1 0e e5 96  a8 0c f9 78 20 b7 44 18  |...........x .D.|
-000001f0  51 8d 10 7e 4f 94 67 df  a3 4e 70 73 8e 90 91 85  |Q..~O.g..Nps....|
-00000200  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000210  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000220  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000230  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000240  a6 b5 68 1a 41 03 56 6b  dc 5a 89 16 03 03 00 88  |..h.A.Vk.Z......|
-00000250  0f 00 00 84 05 01 00 80  45 11 dc 3c 2a fc 5f aa  |........E..<*._.|
-00000260  60 09 59 47 45 cc a7 74  e3 9d 0c c3 a4 08 b0 2a  |`.YGE..t.......*|
-00000270  44 47 cd 66 ed 94 54 8f  d7 74 fd 47 a3 90 56 69  |DG.f..T..t.G..Vi|
-00000280  5a b6 c5 b0 bd c2 16 a2  1e af 58 37 88 cb d1 4b  |Z.........X7...K|
-00000290  5c ee e6 0f 16 9b e0 d7  43 b3 e6 0a b2 90 fa 21  |\.......C......!|
-000002a0  78 95 3e 7f fc c1 b3 df  a1 bf fc eb bc e8 37 63  |x.>...........7c|
-000002b0  87 33 3e c3 9a e4 6c 0f  3d 0d 9f e8 db 2d 82 ad  |.3>...l.=....-..|
-000002c0  3c 6d f7 4a 5e 81 21 4f  19 0e 60 2d ef c1 40 8d  |<m.J^.!O..`-.. at .|
-000002d0  cb 97 4f 08 1c c0 66 e7  14 03 03 00 01 01 16 03  |..O...f.........|
-000002e0  03 00 28 00 00 00 00 00  00 00 00 8c ce 5e 94 90  |..(..........^..|
-000002f0  22 2c 8d 64 be 29 99 62  1f 95 6e 3b 51 22 9c eb  |",.d.).b..n;Q"..|
-00000300  f3 0f 24 b8 a5 84 58 70  82 71 a1                 |..$...Xp.q.|
+00000000  16 03 03 01 fd 0b 00 01  f9 00 01 f6 00 01 f3 30  |...............0|
+00000010  82 01 ef 30 82 01 58 a0  03 02 01 02 02 10 5c 19  |...0..X.......\.|
+00000020  c1 89 65 83 55 6f dc 0b  c9 b9 93 9f e9 bc 30 0d  |..e.Uo........0.|
+00000030  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 30 12 31  |..*.H........0.1|
+00000040  10 30 0e 06 03 55 04 0a  13 07 41 63 6d 65 20 43  |.0...U....Acme C|
+00000050  6f 30 1e 17 0d 31 36 30  38 31 37 32 31 35 32 33  |o0...16081721523|
+00000060  31 5a 17 0d 31 37 30 38  31 37 32 31 35 32 33 31  |1Z..170817215231|
+00000070  5a 30 12 31 10 30 0e 06  03 55 04 0a 13 07 41 63  |Z0.1.0...U....Ac|
+00000080  6d 65 20 43 6f 30 81 9f  30 0d 06 09 2a 86 48 86  |me Co0..0...*.H.|
+00000090  f7 0d 01 01 01 05 00 03  81 8d 00 30 81 89 02 81  |...........0....|
+000000a0  81 00 ba 6f aa 86 bd cf  bf 9f f2 ef 5c 94 60 78  |...o........\.`x|
+000000b0  6f e8 13 f2 d1 96 6f cd  d9 32 6e 22 37 ce 41 f9  |o.....o..2n"7.A.|
+000000c0  ca 5d 29 ac e1 27 da 61  a2 ee 81 cb 10 c7 df 34  |.])..'.a.......4|
+000000d0  58 95 86 e9 3d 19 e6 5c  27 73 60 c8 8d 78 02 f4  |X...=..\'s`..x..|
+000000e0  1d a4 98 09 a3 19 70 69  3c 25 62 66 2a ab 22 23  |......pi<%bf*."#|
+000000f0  c5 7b 85 38 4f 2e 09 73  32 a7 bd 3e 9b ad ca 84  |.{.8O..s2..>....|
+00000100  07 e6 0f 3a ff 77 c5 9d  41 85 00 8a b6 9b ee b0  |...:.w..A.......|
+00000110  a4 3f 2d 4c 4c e6 42 3e  bb 51 c8 dd 48 54 f4 0c  |.?-LL.B>.Q..HT..|
+00000120  8e 47 02 03 01 00 01 a3  46 30 44 30 0e 06 03 55  |.G......F0D0...U|
+00000130  1d 0f 01 01 ff 04 04 03  02 05 a0 30 13 06 03 55  |...........0...U|
+00000140  1d 25 04 0c 30 0a 06 08  2b 06 01 05 05 07 03 01  |.%..0...+.......|
+00000150  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 0f  |0...U.......0.0.|
+00000160  06 03 55 1d 11 04 08 30  06 87 04 7f 00 00 01 30  |..U....0.......0|
+00000170  0d 06 09 2a 86 48 86 f7  0d 01 01 0b 05 00 03 81  |...*.H..........|
+00000180  81 00 46 ab 44 a2 fb 28  54 f8 5a 67 f8 62 94 f1  |..F.D..(T.Zg.b..|
+00000190  9a b2 18 9e f2 b1 de 1d  7e 6f 76 95 a9 ba e7 5d  |........~ov....]|
+000001a0  a8 16 6c 9c f7 09 d3 37  e4 4b 2b 36 7c 01 ad 41  |..l....7.K+6|..A|
+000001b0  d2 32 d8 c3 d2 93 f9 10  6b 8e 95 b9 2c 17 8a a3  |.2......k...,...|
+000001c0  44 48 bc 59 13 83 16 04  88 a4 81 5c 25 0d 98 0c  |DH.Y.......\%...|
+000001d0  ac 11 b1 28 56 be 1d cd  61 62 84 09 bf d6 80 c6  |...(V...ab......|
+000001e0  45 8d 82 2c b4 d8 83 9b  db c9 22 b7 2a 12 11 7b  |E..,......".*..{|
+000001f0  fa 02 3b c1 c9 ff ea c9  9d a8 49 d3 95 d7 d5 0e  |..;.......I.....|
+00000200  e5 35 16 03 03 00 25 10  00 00 21 20 2f e5 7d a3  |.5....%...! /.}.|
+00000210  47 cd 62 43 15 28 da ac  5f bb 29 07 30 ff f6 84  |G.bC.(.._.).0...|
+00000220  af c4 cf c2 ed 90 99 5f  58 cb 3b 74 16 03 03 00  |......._X.;t....|
+00000230  88 0f 00 00 84 05 01 00  80 ad f7 ff a0 cb d0 6e  |...............n|
+00000240  8f 19 0c 40 2a 1f bb dd  11 52 81 84 f1 7b 3f cf  |...@*....R...{?.|
+00000250  75 72 83 a4 4c 0a 9c 70  95 98 d5 51 a2 28 0c 8c  |ur..L..p...Q.(..|
+00000260  20 08 d7 2a a5 3e 0c cf  6c a2 1d 32 bd cc a1 b4  | ..*.>..l..2....|
+00000270  61 e0 6d 9a 61 16 03 5c  7a b8 fa 15 ea cd e4 de  |a.m.a..\z.......|
+00000280  d6 16 93 b2 e0 d2 55 b9  03 e0 67 04 27 64 8c e2  |......U...g.'d..|
+00000290  01 ee 8f f7 59 3e 12 16  51 f2 07 20 fe 03 e2 3e  |....Y>..Q.. ...>|
+000002a0  09 1f 96 24 c5 73 0e 69  ac 57 ff 43 2b 6a c6 20  |...$.s.i.W.C+j. |
+000002b0  2f e4 ef 7e bc b3 38 57  06 14 03 03 00 01 01 16  |/..~..8W........|
+000002c0  03 03 00 28 00 00 00 00  00 00 00 00 fd 71 f5 ca  |...(.........q..|
+000002d0  91 26 67 54 a5 e6 f3 06  c8 40 24 9d a9 bd b1 9a  |.&gT.....@$.....|
+000002e0  63 c4 c2 53 56 ba af c0  16 bc 06 5c              |c..SV......\|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 b1 23 11 48 69  |..........(.#.Hi|
-00000010  52 44 34 f1 9a 69 2b 79  fb 68 b4 53 d5 d7 08 08  |RD4..i+y.h.S....|
-00000020  34 95 5f 56 b2 57 eb 91  31 6c 32 25 b5 68 8a 8e  |4._V.W..1l2%.h..|
-00000030  f1 68 6e                                          |.hn|
+00000000  14 03 03 00 01 01 16 03  03 00 28 92 8f bd c5 97  |..........(.....|
+00000010  94 76 70 f4 0a f9 9a 79  69 31 27 0e c0 c5 0b 3c  |.vp....yi1'....<|
+00000020  9f 4f c2 2f cb 6c 56 62  80 3b e5 72 6a 05 9e 4b  |.O./.lVb.;.rj..K|
+00000030  34 b9 66                                          |4.f|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 03 ab 9e  |................|
-00000010  f0 a6 6c f1 ea 23 20 63  42 a3 9d c6 5d 41 96 c1  |..l..# cB...]A..|
-00000020  44 b2 8b 15 03 03 00 1a  00 00 00 00 00 00 00 02  |D...............|
-00000030  e9 73 41 50 28 b0 d3 00  46 81 d6 c9 1a ca ab cd  |.sAP(...F.......|
-00000040  44 9b                                             |D.|
+00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 43 fb 43  |.............C.C|
+00000010  3d 96 63 dd 25 94 9d 7a  fb 9e 15 6f 62 5e ed 34  |=.c.%..z...ob^.4|
+00000020  19 89 b8 15 03 03 00 1a  00 00 00 00 00 00 00 02  |................|
+00000030  53 c2 2d 6c b7 91 6c 62  84 09 a2 1c 9b 3d 9e 89  |S.-l..lb.....=..|
+00000040  6a 3d                                             |j=|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-ECDSA b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-ECDSA
index 4fa5e20..1ff9198 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-ECDSA
+++ b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-ECDSA
@@ -1,19 +1,20 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 2a 01 f8 3e d1  |....Y...U..*..>.|
-00000010  52 41 2e 9a 8d 56 ff 52  3d 6a fe 65 ab 91 bb b7  |RA...V.R=j.e....|
-00000020  82 be f1 60 40 3b 80 a1  f8 dc 95 20 48 87 41 46  |...`@;..... H.AF|
-00000030  6a d2 f3 b8 d8 68 20 40  45 b7 fe 19 21 bc 84 00  |j....h @E...!...|
-00000040  5d 40 40 21 58 3e 7d fb  a7 e3 30 37 c0 09 00 00  |]@@!X>}...07....|
+00000000  16 03 03 00 59 02 00 00  55 03 03 84 4b ff a4 2a  |....Y...U...K..*|
+00000010  a4 76 c0 26 f6 05 72 94  01 15 44 f2 c6 7d b0 4b  |.v.&..r...D..}.K|
+00000020  1b fa da 51 54 45 78 66  e6 0a dd 20 17 df d2 0c  |...QTExf... ....|
+00000030  2f d6 55 b9 ae 82 ce 2f  2f 07 67 54 5e 02 bd 2f  |/.U....//.gT^../|
+00000040  48 f6 fb 3d 9c fa 4f a8  66 15 08 da c0 09 00 00  |H..=..O.f.......|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
@@ -48,87 +49,83 @@
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 03 00 d8 0c 00  00 d4 03 00 17 41 04 00  |*............A..|
-00000280  9e 90 3a 3d 00 37 0a c0  43 92 6e bf b4 23 d9 64  |..:=.7..C.n..#.d|
-00000290  99 d2 90 9e eb 88 b6 d6  6f 15 4a 22 72 f0 bf 5e  |........o.J"r..^|
-000002a0  72 80 93 90 aa f1 d1 9c  45 c6 6e 3a f8 a9 6f fe  |r.......E.n:..o.|
-000002b0  fb 24 dc b1 4d 52 39 91  f5 48 36 06 f6 15 0e 04  |.$..MR9..H6.....|
-000002c0  03 00 8b 30 81 88 02 42  00 a9 54 74 a7 a8 d0 04  |...0...B..Tt....|
-000002d0  ae ef e4 64 38 74 21 e6  18 f0 79 b2 d7 7e 7b 0e  |...d8t!...y..~{.|
-000002e0  f6 74 75 52 f0 b8 15 3c  3d 15 52 75 9f 60 03 63  |.tuR...<=.Ru.`.c|
-000002f0  15 b8 1e b8 0e 5c 58 c7  e7 2f 6d 76 c7 c8 42 7a  |.....\X../mv..Bz|
-00000300  df 15 26 4b dc 9c 3b 4d  b3 b6 02 42 00 a5 fd bf  |..&K..;M...B....|
-00000310  a9 5d fc 87 42 24 f9 0b  7a 17 97 7c ee 45 1c 29  |.]..B$..z..|.E.)|
-00000320  3a 07 5f df 4d f2 d3 cb  fc a6 fd 84 34 2c 40 84  |:._.M.......4, at .|
-00000330  06 76 bf 43 35 d2 f6 9a  7c d6 1b 5e d8 fd 08 35  |.v.C5...|..^...5|
-00000340  1b 90 0e 24 a7 48 9d 71  ab 4a 11 92 d3 6e 16 03  |...$.H.q.J...n..|
-00000350  03 00 2e 0d 00 00 26 03  01 02 40 00 1e 06 01 06  |......&... at .....|
-00000360  02 06 03 05 01 05 02 05  03 04 01 04 02 04 03 03  |................|
-00000370  01 03 02 03 03 02 01 02  02 02 03 00 00 0e 00 00  |................|
-00000380  00                                                |.|
+00000270  2a 16 03 03 00 b7 0c 00  00 b3 03 00 1d 20 5d 4e  |*............ ]N|
+00000280  0c 9e ad 7b f1 48 0c db  03 96 26 2e 16 87 2c e2  |...{.H....&...,.|
+00000290  ce a2 47 5a 57 30 e8 e1  7e b2 53 5b c6 7b 04 03  |..GZW0..~.S[.{..|
+000002a0  00 8b 30 81 88 02 42 00  e8 8e 68 a8 e3 b5 b4 fe  |..0...B...h.....|
+000002b0  b9 91 aa 4f 96 3d 97 8d  b2 ef 23 a4 3d 16 db 2b  |...O.=....#.=..+|
+000002c0  50 6d 52 cd a5 e7 79 ae  65 10 d6 36 e0 ba c3 6b  |PmR...y.e..6...k|
+000002d0  53 61 14 bb 05 47 5a df  26 2f cb 3a 95 c6 6b dc  |Sa...GZ.&/.:..k.|
+000002e0  88 fd 2e 22 b5 ef ff 31  0e 02 42 01 be ce 6e 53  |..."...1..B...nS|
+000002f0  42 43 1c 1c d8 83 7f 45  c4 16 ee d2 7b 66 a0 f4  |BC.....E....{f..|
+00000300  f3 14 da 5c 14 e8 fc bc  86 7d 18 43 b9 7b 90 8c  |...\.....}.C.{..|
+00000310  af f1 05 95 c6 53 0b 0b  0d 10 a1 e9 bb 89 35 c2  |.....S........5.|
+00000320  b2 e1 d7 dd 99 7c bf 85  19 3c 4e 8e 8f 16 03 03  |.....|...<N.....|
+00000330  00 2a 0d 00 00 26 03 01  02 40 00 1e 06 01 06 02  |.*...&... at ......|
+00000340  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000350  03 02 03 03 02 01 02 02  02 03 00 00 16 03 03 00  |................|
+00000360  04 0e 00 00 00                                    |.....|
 >>> Flow 3 (client to server)
-00000000  16 03 03 01 fb 0b 00 01  f7 00 01 f4 00 01 f1 30  |...............0|
-00000010  82 01 ed 30 82 01 58 a0  03 02 01 02 02 01 00 30  |...0..X........0|
-00000020  0b 06 09 2a 86 48 86 f7  0d 01 01 05 30 26 31 10  |...*.H......0&1.|
-00000030  30 0e 06 03 55 04 0a 13  07 41 63 6d 65 20 43 6f  |0...U....Acme Co|
-00000040  31 12 30 10 06 03 55 04  03 13 09 31 32 37 2e 30  |1.0...U....127.0|
-00000050  2e 30 2e 31 30 1e 17 0d  31 31 31 32 30 38 30 37  |.0.10...11120807|
-00000060  35 35 31 32 5a 17 0d 31  32 31 32 30 37 30 38 30  |5512Z..121207080|
-00000070  30 31 32 5a 30 26 31 10  30 0e 06 03 55 04 0a 13  |012Z0&1.0...U...|
-00000080  07 41 63 6d 65 20 43 6f  31 12 30 10 06 03 55 04  |.Acme Co1.0...U.|
-00000090  03 13 09 31 32 37 2e 30  2e 30 2e 31 30 81 9c 30  |...127.0.0.10..0|
-000000a0  0b 06 09 2a 86 48 86 f7  0d 01 01 01 03 81 8c 00  |...*.H..........|
-000000b0  30 81 88 02 81 80 4e d0  7b 31 e3 82 64 d9 59 c0  |0.....N.{1..d.Y.|
-000000c0  c2 87 a4 5e 1e 8b 73 33  c7 63 53 df 66 92 06 84  |...^..s3.cS.f...|
-000000d0  f6 64 d5 8f e4 36 a7 1d  2b e8 b3 20 36 45 23 b5  |.d...6..+.. 6E#.|
-000000e0  e3 95 ae ed e0 f5 20 9c  8d 95 df 7f 5a 12 ef 87  |...... .....Z...|
-000000f0  e4 5b 68 e4 e9 0e 74 ec  04 8a 7f de 93 27 c4 01  |.[h...t......'..|
-00000100  19 7a bd f2 dc 3d 14 ab  d0 54 ca 21 0c d0 4d 6e  |.z...=...T.!..Mn|
-00000110  87 2e 5c c5 d2 bb 4d 4b  4f ce b6 2c f7 7e 88 ec  |..\...MKO..,.~..|
-00000120  7c d7 02 91 74 a6 1e 0c  1a da e3 4a 5a 2e de 13  ||...t......JZ...|
-00000130  9c 4c 40 88 59 93 02 03  01 00 01 a3 32 30 30 30  |.L at .Y.......2000|
-00000140  0e 06 03 55 1d 0f 01 01  ff 04 04 03 02 00 a0 30  |...U...........0|
-00000150  0d 06 03 55 1d 0e 04 06  04 04 01 02 03 04 30 0f  |...U..........0.|
-00000160  06 03 55 1d 23 04 08 30  06 80 04 01 02 03 04 30  |..U.#..0.......0|
-00000170  0b 06 09 2a 86 48 86 f7  0d 01 01 05 03 81 81 00  |...*.H..........|
-00000180  36 1f b3 7a 0c 75 c9 6e  37 46 61 2b d5 bd c0 a7  |6..z.u.n7Fa+....|
-00000190  4b cc 46 9a 81 58 7c 85  79 29 c8 c8 c6 67 dd 32  |K.F..X|.y)...g.2|
-000001a0  56 45 2b 75 b6 e9 24 a9  50 9a be 1f 5a fa 1a 15  |VE+u..$.P...Z...|
-000001b0  d9 cc 55 95 72 16 83 b9  c2 b6 8f fd 88 8c 38 84  |..U.r.........8.|
-000001c0  1d ab 5d 92 31 13 4f fd  83 3b c6 9d f1 11 62 b6  |..].1.O..;....b.|
-000001d0  8b ec ab 67 be c8 64 b0  11 50 46 58 17 6b 99 1c  |...g..d..PFX.k..|
-000001e0  d3 1d fc 06 f1 0e e5 96  a8 0c f9 78 20 b7 44 18  |...........x .D.|
-000001f0  51 8d 10 7e 4f 94 67 df  a3 4e 70 73 8e 90 91 85  |Q..~O.g..Nps....|
-00000200  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000210  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000220  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000230  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000240  a6 b5 68 1a 41 03 56 6b  dc 5a 89 16 03 03 00 88  |..h.A.Vk.Z......|
-00000250  0f 00 00 84 05 01 00 80  20 ef 4b 1c d7 67 37 6e  |........ .K..g7n|
-00000260  24 12 9e e9 59 b1 6d da  e5 3e 6b 11 03 f4 96 e4  |$...Y.m..>k.....|
-00000270  2e fb 03 e1 13 af 73 4d  15 11 c1 80 e2 ed 11 c6  |......sM........|
-00000280  73 6a 96 ce d1 26 e4 bc  fe 71 c9 48 32 fd d8 70  |sj...&...q.H2..p|
-00000290  01 9d 18 7b ed a3 bd 6a  68 df 45 a0 d5 77 79 d2  |...{...jh.E..wy.|
-000002a0  5b e2 8c 96 68 95 46 8d  7d e6 b6 26 fa e1 c4 05  |[...h.F.}..&....|
-000002b0  4c d1 39 4e 35 e3 0c 1b  26 37 2e 0b 9b 0b cf f7  |L.9N5...&7......|
-000002c0  25 c3 da 27 18 70 83 18  49 ff ee ba e3 f8 70 75  |%..'.p..I.....pu|
-000002d0  e8 9b 2d 89 d7 b2 00 a5  14 03 03 00 01 01 16 03  |..-.............|
-000002e0  03 00 40 00 00 00 00 00  00 00 00 00 00 00 00 00  |.. at .............|
-000002f0  00 00 00 d3 33 79 85 64  14 07 a6 93 74 f8 f8 55  |....3y.d....t..U|
-00000300  0f fb fc 8e 1b 4c 38 21  b6 61 c5 4b b2 d4 17 b2  |.....L8!.a.K....|
-00000310  c4 be a6 4b d6 3f a3 5f  3c ff 5f 1d 93 a2 c4 82  |...K.?._<._.....|
-00000320  96 90 eb                                          |...|
+00000000  16 03 03 01 fd 0b 00 01  f9 00 01 f6 00 01 f3 30  |...............0|
+00000010  82 01 ef 30 82 01 58 a0  03 02 01 02 02 10 5c 19  |...0..X.......\.|
+00000020  c1 89 65 83 55 6f dc 0b  c9 b9 93 9f e9 bc 30 0d  |..e.Uo........0.|
+00000030  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 30 12 31  |..*.H........0.1|
+00000040  10 30 0e 06 03 55 04 0a  13 07 41 63 6d 65 20 43  |.0...U....Acme C|
+00000050  6f 30 1e 17 0d 31 36 30  38 31 37 32 31 35 32 33  |o0...16081721523|
+00000060  31 5a 17 0d 31 37 30 38  31 37 32 31 35 32 33 31  |1Z..170817215231|
+00000070  5a 30 12 31 10 30 0e 06  03 55 04 0a 13 07 41 63  |Z0.1.0...U....Ac|
+00000080  6d 65 20 43 6f 30 81 9f  30 0d 06 09 2a 86 48 86  |me Co0..0...*.H.|
+00000090  f7 0d 01 01 01 05 00 03  81 8d 00 30 81 89 02 81  |...........0....|
+000000a0  81 00 ba 6f aa 86 bd cf  bf 9f f2 ef 5c 94 60 78  |...o........\.`x|
+000000b0  6f e8 13 f2 d1 96 6f cd  d9 32 6e 22 37 ce 41 f9  |o.....o..2n"7.A.|
+000000c0  ca 5d 29 ac e1 27 da 61  a2 ee 81 cb 10 c7 df 34  |.])..'.a.......4|
+000000d0  58 95 86 e9 3d 19 e6 5c  27 73 60 c8 8d 78 02 f4  |X...=..\'s`..x..|
+000000e0  1d a4 98 09 a3 19 70 69  3c 25 62 66 2a ab 22 23  |......pi<%bf*."#|
+000000f0  c5 7b 85 38 4f 2e 09 73  32 a7 bd 3e 9b ad ca 84  |.{.8O..s2..>....|
+00000100  07 e6 0f 3a ff 77 c5 9d  41 85 00 8a b6 9b ee b0  |...:.w..A.......|
+00000110  a4 3f 2d 4c 4c e6 42 3e  bb 51 c8 dd 48 54 f4 0c  |.?-LL.B>.Q..HT..|
+00000120  8e 47 02 03 01 00 01 a3  46 30 44 30 0e 06 03 55  |.G......F0D0...U|
+00000130  1d 0f 01 01 ff 04 04 03  02 05 a0 30 13 06 03 55  |...........0...U|
+00000140  1d 25 04 0c 30 0a 06 08  2b 06 01 05 05 07 03 01  |.%..0...+.......|
+00000150  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 0f  |0...U.......0.0.|
+00000160  06 03 55 1d 11 04 08 30  06 87 04 7f 00 00 01 30  |..U....0.......0|
+00000170  0d 06 09 2a 86 48 86 f7  0d 01 01 0b 05 00 03 81  |...*.H..........|
+00000180  81 00 46 ab 44 a2 fb 28  54 f8 5a 67 f8 62 94 f1  |..F.D..(T.Zg.b..|
+00000190  9a b2 18 9e f2 b1 de 1d  7e 6f 76 95 a9 ba e7 5d  |........~ov....]|
+000001a0  a8 16 6c 9c f7 09 d3 37  e4 4b 2b 36 7c 01 ad 41  |..l....7.K+6|..A|
+000001b0  d2 32 d8 c3 d2 93 f9 10  6b 8e 95 b9 2c 17 8a a3  |.2......k...,...|
+000001c0  44 48 bc 59 13 83 16 04  88 a4 81 5c 25 0d 98 0c  |DH.Y.......\%...|
+000001d0  ac 11 b1 28 56 be 1d cd  61 62 84 09 bf d6 80 c6  |...(V...ab......|
+000001e0  45 8d 82 2c b4 d8 83 9b  db c9 22 b7 2a 12 11 7b  |E..,......".*..{|
+000001f0  fa 02 3b c1 c9 ff ea c9  9d a8 49 d3 95 d7 d5 0e  |..;.......I.....|
+00000200  e5 35 16 03 03 00 25 10  00 00 21 20 2f e5 7d a3  |.5....%...! /.}.|
+00000210  47 cd 62 43 15 28 da ac  5f bb 29 07 30 ff f6 84  |G.bC.(.._.).0...|
+00000220  af c4 cf c2 ed 90 99 5f  58 cb 3b 74 16 03 03 00  |......._X.;t....|
+00000230  88 0f 00 00 84 05 01 00  80 98 58 a8 77 3d db 0b  |..........X.w=..|
+00000240  04 36 78 51 8b 23 48 fc  70 2b c9 94 1f ee 32 ae  |.6xQ.#H.p+....2.|
+00000250  41 c0 42 20 19 51 67 e7  fa c0 fd 15 a1 5f 55 4f  |A.B .Qg......_UO|
+00000260  aa be 29 77 f5 47 71 b9  6c 51 89 18 df 25 98 fd  |..)w.Gq.lQ...%..|
+00000270  c8 6e ae e3 fd 99 63 ca  2c d2 fb ca bc 57 b7 7f  |.n....c.,....W..|
+00000280  a2 90 a6 6f b7 2e b7 2a  52 29 e6 75 57 86 cc b1  |...o...*R).uW...|
+00000290  d8 6c f3 4e 49 ab 4b 66  0a 72 aa ec c2 f7 6e 57  |.l.NI.Kf.r....nW|
+000002a0  15 26 79 1a a4 24 c2 ba  76 9e dd b9 f9 d4 da 1b  |.&y..$..v.......|
+000002b0  c9 29 66 eb 64 1b 68 66  66 14 03 03 00 01 01 16  |.)f.d.hff.......|
+000002c0  03 03 00 40 00 00 00 00  00 00 00 00 00 00 00 00  |... at ............|
+000002d0  00 00 00 00 70 2b 69 27  05 9a 96 e6 e8 52 ea 0f  |....p+i'.....R..|
+000002e0  3a d6 40 b5 e2 89 5b bf  aa 95 6c c1 7d 53 09 89  |:. at ...[...l.}S..|
+000002f0  23 38 6b 83 85 84 fa f4  2e fb cd b3 57 4e 79 8a  |#8k.........WNy.|
+00000300  92 74 03 22                                       |.t."|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 40 80 b0 df ff b3  |.......... at .....|
-00000010  34 11 03 f5 2d fb c7 c2  38 15 df 41 97 55 0e 1d  |4...-...8..A.U..|
-00000020  36 f7 a5 35 5b 63 d7 c5  a6 fd fc a1 91 32 9d cd  |6..5[c.......2..|
-00000030  34 66 75 4c 5d 27 ee 89  ed d4 4a ec 67 b0 da e7  |4fuL]'....J.g...|
-00000040  f0 e7 36 eb db b9 22 97  74 30 cd                 |..6...".t0.|
+00000000  14 03 03 00 01 01 16 03  03 00 40 8f 91 f1 f5 6b  |.......... at ....k|
+00000010  cc 52 9d db 35 1f db b4  64 fe 33 a5 83 08 24 2f  |.R..5...d.3...$/|
+00000020  57 18 0e 60 4e 18 54 bb  80 31 37 fe 26 14 b8 c8  |W..`N.T..17.&...|
+00000030  dd c4 8c 07 42 0b 80 0b  41 82 40 f6 9b b8 60 4f  |....B...A. at ...`O|
+00000040  cb 7b 43 ea 1a 6e 31 8d  9f 82 f7                 |.{C..n1....|
 >>> Flow 5 (client to server)
 00000000  17 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 62 24 32  e9 40 38 c8 c3 dd 07 42  |.....b$2. at 8....B|
-00000020  05 c8 7c 3d d1 27 68 00  e4 91 6c 2d 08 c1 a1 b6  |..|=.'h...l-....|
-00000030  8a 89 3d 1d c1 15 03 03  00 30 00 00 00 00 00 00  |..=......0......|
-00000040  00 00 00 00 00 00 00 00  00 00 d1 c8 bc cb cb a5  |................|
-00000050  24 1e ad c5 bf 23 92 4b  81 a6 c0 77 19 e0 46 30  |$....#.K...w..F0|
-00000060  48 51 0c cc 39 cd 4b 8d  e5 a7                    |HQ..9.K...|
+00000010  00 00 00 00 00 70 e7 c8  03 9c e2 58 73 68 ab 9b  |.....p.....Xsh..|
+00000020  5c bf 32 57 f8 f1 13 97  02 59 de 99 d3 3e 16 3d  |\.2W.....Y...>.=|
+00000030  87 11 d4 b4 63 15 03 03  00 30 00 00 00 00 00 00  |....c....0......|
+00000040  00 00 00 00 00 00 00 00  00 00 9b 99 45 f3 0d f1  |............E...|
+00000050  c5 36 07 8c 81 94 b7 0a  dc 7c ee 0c 22 1b 36 fd  |.6.......|..".6.|
+00000060  d4 fc 7d f1 98 8b 87 be  5f c6                    |..}....._.|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-RSA b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-RSA
index a0a9640..76f0c25 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-RSA
+++ b/src/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-RSA
@@ -1,122 +1,130 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 51 02 00 00  4d 03 03 60 8e 1c c9 6d  |....Q...M..`...m|
-00000010  de 9d 2a dc 6a a6 82 71  9a 3f 8f 5b 18 52 44 4e  |..*.j..q.?.[.RDN|
-00000020  4d 72 0d e7 c8 a1 b0 81  64 8c 1f 20 06 a8 17 35  |Mr......d.. ...5|
-00000030  b8 0b 96 52 30 f7 b3 d4  2a 25 94 c0 ba a8 a2 f7  |...R0...*%......|
-00000040  86 5c 18 18 3c 68 3a 71  0f bc 3f 12 00 05 00 00  |.\..<h:q..?.....|
-00000050  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000060  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000070  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000090  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-000000a0  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-000000b0  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000c0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000d0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000e0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000f0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-00000100  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-00000110  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000120  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000130  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000140  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000150  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000160  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000170  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000180  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000190  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-000001a0  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-000001b0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001c0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001d0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001e0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001f0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-00000200  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-00000210  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000220  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000230  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000240  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000250  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000260  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000270  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000280  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000290  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-000002a0  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-000002b0  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002c0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002d0  2e 0d 00 00 26 03 01 02  40 00 1e 06 01 06 02 06  |....&... at .......|
-000002e0  03 05 01 05 02 05 03 04  01 04 02 04 03 03 01 03  |................|
-000002f0  02 03 03 02 01 02 02 02  03 00 00 0e 00 00 00     |...............|
+00000000  16 03 03 00 59 02 00 00  55 03 03 92 93 45 4c f9  |....Y...U....EL.|
+00000010  93 bf ee 78 58 e0 42 b6  df 32 c2 63 6d ec 89 66  |...xX.B..2.cm..f|
+00000020  5a 11 7c 0d 31 2f b5 90  22 ab 3d 20 65 f3 40 c4  |Z.|.1/..".= e. at .|
+00000030  f8 31 fa 80 f3 fb a7 f6  9e dc 0c 94 67 48 d9 2b  |.1..........gH.+|
+00000040  cb 94 82 5f 4e 8b 41 5e  c6 63 27 da c0 2f 00 00  |..._N.A^.c'../..|
+00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 66 49 44 2b 04 fe f5  |........ fID+...|
+000002d0  41 68 60 09 81 0e 24 c4  46 68 33 87 41 dd 48 69  |Ah`...$.Fh3.A.Hi|
+000002e0  4c be c8 22 2d 4e ff 80  20 04 01 00 80 30 85 40  |L.."-N.. ....0.@|
+000002f0  30 56 d5 1d 41 14 9d e8  27 39 a2 18 d5 eb 92 27  |0V..A...'9.....'|
+00000300  63 4b 05 85 1a 9e 5f 60  2c 80 a3 20 9f 9c 57 29  |cK...._`,.. ..W)|
+00000310  ba 5f ac 0a aa 89 98 fc  ca 8e 37 6b 44 bc 0f 33  |._........7kD..3|
+00000320  5d 47 91 46 55 d4 f9 4f  76 73 51 c4 f6 a9 90 e4  |]G.FU..OvsQ.....|
+00000330  95 10 92 94 f1 33 11 3d  83 0a eb 5d ff e6 9d 9c  |.....3.=...]....|
+00000340  19 ec e1 65 11 ad d7 7b  6a a4 f9 d8 b6 0c 53 8a  |...e...{j.....S.|
+00000350  16 d5 1f a7 0b 80 6f c5  d8 6a 57 11 2f b1 84 65  |......o..jW./..e|
+00000360  24 8a 02 de aa 10 40 bd  9b 68 a2 b7 b6 16 03 03  |$..... at ..h......|
+00000370  00 2a 0d 00 00 26 03 01  02 40 00 1e 06 01 06 02  |.*...&... at ......|
+00000380  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000390  03 02 03 03 02 01 02 02  02 03 00 00 16 03 03 00  |................|
+000003a0  04 0e 00 00 00                                    |.....|
 >>> Flow 3 (client to server)
-00000000  16 03 03 01 fb 0b 00 01  f7 00 01 f4 00 01 f1 30  |...............0|
-00000010  82 01 ed 30 82 01 58 a0  03 02 01 02 02 01 00 30  |...0..X........0|
-00000020  0b 06 09 2a 86 48 86 f7  0d 01 01 05 30 26 31 10  |...*.H......0&1.|
-00000030  30 0e 06 03 55 04 0a 13  07 41 63 6d 65 20 43 6f  |0...U....Acme Co|
-00000040  31 12 30 10 06 03 55 04  03 13 09 31 32 37 2e 30  |1.0...U....127.0|
-00000050  2e 30 2e 31 30 1e 17 0d  31 31 31 32 30 38 30 37  |.0.10...11120807|
-00000060  35 35 31 32 5a 17 0d 31  32 31 32 30 37 30 38 30  |5512Z..121207080|
-00000070  30 31 32 5a 30 26 31 10  30 0e 06 03 55 04 0a 13  |012Z0&1.0...U...|
-00000080  07 41 63 6d 65 20 43 6f  31 12 30 10 06 03 55 04  |.Acme Co1.0...U.|
-00000090  03 13 09 31 32 37 2e 30  2e 30 2e 31 30 81 9c 30  |...127.0.0.10..0|
-000000a0  0b 06 09 2a 86 48 86 f7  0d 01 01 01 03 81 8c 00  |...*.H..........|
-000000b0  30 81 88 02 81 80 4e d0  7b 31 e3 82 64 d9 59 c0  |0.....N.{1..d.Y.|
-000000c0  c2 87 a4 5e 1e 8b 73 33  c7 63 53 df 66 92 06 84  |...^..s3.cS.f...|
-000000d0  f6 64 d5 8f e4 36 a7 1d  2b e8 b3 20 36 45 23 b5  |.d...6..+.. 6E#.|
-000000e0  e3 95 ae ed e0 f5 20 9c  8d 95 df 7f 5a 12 ef 87  |...... .....Z...|
-000000f0  e4 5b 68 e4 e9 0e 74 ec  04 8a 7f de 93 27 c4 01  |.[h...t......'..|
-00000100  19 7a bd f2 dc 3d 14 ab  d0 54 ca 21 0c d0 4d 6e  |.z...=...T.!..Mn|
-00000110  87 2e 5c c5 d2 bb 4d 4b  4f ce b6 2c f7 7e 88 ec  |..\...MKO..,.~..|
-00000120  7c d7 02 91 74 a6 1e 0c  1a da e3 4a 5a 2e de 13  ||...t......JZ...|
-00000130  9c 4c 40 88 59 93 02 03  01 00 01 a3 32 30 30 30  |.L at .Y.......2000|
-00000140  0e 06 03 55 1d 0f 01 01  ff 04 04 03 02 00 a0 30  |...U...........0|
-00000150  0d 06 03 55 1d 0e 04 06  04 04 01 02 03 04 30 0f  |...U..........0.|
-00000160  06 03 55 1d 23 04 08 30  06 80 04 01 02 03 04 30  |..U.#..0.......0|
-00000170  0b 06 09 2a 86 48 86 f7  0d 01 01 05 03 81 81 00  |...*.H..........|
-00000180  36 1f b3 7a 0c 75 c9 6e  37 46 61 2b d5 bd c0 a7  |6..z.u.n7Fa+....|
-00000190  4b cc 46 9a 81 58 7c 85  79 29 c8 c8 c6 67 dd 32  |K.F..X|.y)...g.2|
-000001a0  56 45 2b 75 b6 e9 24 a9  50 9a be 1f 5a fa 1a 15  |VE+u..$.P...Z...|
-000001b0  d9 cc 55 95 72 16 83 b9  c2 b6 8f fd 88 8c 38 84  |..U.r.........8.|
-000001c0  1d ab 5d 92 31 13 4f fd  83 3b c6 9d f1 11 62 b6  |..].1.O..;....b.|
-000001d0  8b ec ab 67 be c8 64 b0  11 50 46 58 17 6b 99 1c  |...g..d..PFX.k..|
-000001e0  d3 1d fc 06 f1 0e e5 96  a8 0c f9 78 20 b7 44 18  |...........x .D.|
-000001f0  51 8d 10 7e 4f 94 67 df  a3 4e 70 73 8e 90 91 85  |Q..~O.g..Nps....|
-00000200  16 03 03 00 86 10 00 00  82 00 80 73 bd 73 65 92  |...........s.se.|
-00000210  86 23 41 14 79 7f d5 c1  10 ce 94 4d ad 9c c3 a9  |.#A.y......M....|
-00000220  87 b5 32 52 f8 6b 11 93  2d 9b 98 0b 8b 1d c0 f6  |..2R.k..-.......|
-00000230  53 17 6d c7 9c 2e ae c9  6f cc 99 23 38 37 1a 10  |S.m.....o..#87..|
-00000240  fe 05 0b b5 55 0a 14 e9  60 7d 70 26 98 e2 54 d9  |....U...`}p&..T.|
-00000250  65 cf 2e f4 53 5f 1d aa  3a f6 33 7b eb 4c 0e b3  |e...S_..:.3{.L..|
-00000260  ff 5a db 36 2a 47 f3 df  f9 fc f5 31 78 83 aa 6b  |.Z.6*G.....1x..k|
-00000270  52 b7 ba 1a 96 bc fa c1  a1 a9 bb 2b f5 38 89 00  |R..........+.8..|
-00000280  4d e5 78 13 4e a4 38 46  42 dc 16 16 03 03 00 88  |M.x.N.8FB.......|
-00000290  0f 00 00 84 05 01 00 80  21 58 47 70 c2 2e 1c 4a  |........!XGp...J|
-000002a0  fa 97 b2 cf 8d f8 93 f4  b0 8c b3 e0 e7 33 a6 ea  |.............3..|
-000002b0  d7 fe 8e 25 e7 f3 f5 a1  8d 09 b7 0b 01 ec a1 15  |...%............|
-000002c0  5b 67 06 53 2a 7d 31 e5  a8 16 bc e3 1d ed 5a 77  |[g.S*}1.......Zw|
-000002d0  0b 78 78 c5 fc c5 62 8e  41 49 d3 ea cd 69 10 3f  |.xx...b.AI...i.?|
-000002e0  34 9e 86 df f9 9f f6 02  0c 29 c4 66 a0 45 cf 7b  |4........).f.E.{|
-000002f0  ce 51 ec 0a 26 b4 9d 3d  9e 63 5d 40 1a e8 84 4e  |.Q..&..=.c]@...N|
-00000300  24 f5 42 48 b5 3e f8 92  c4 f2 e6 5d f4 ad 67 01  |$.BH.>.....]..g.|
-00000310  f8 a7 a7 2b b5 fc be e8  14 03 03 00 01 01 16 03  |...+............|
-00000320  03 00 24 f0 ec 1d f5 39  1c d2 d2 c7 f4 1f 3b 0c  |..$....9......;.|
-00000330  cd 25 e4 8e ed c4 bb 02  9d 38 e5 a7 91 e0 ea 00  |.%.......8......|
-00000340  73 a8 9a 63 c9 e7 7d                              |s..c..}|
+00000000  16 03 03 01 fd 0b 00 01  f9 00 01 f6 00 01 f3 30  |...............0|
+00000010  82 01 ef 30 82 01 58 a0  03 02 01 02 02 10 5c 19  |...0..X.......\.|
+00000020  c1 89 65 83 55 6f dc 0b  c9 b9 93 9f e9 bc 30 0d  |..e.Uo........0.|
+00000030  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 30 12 31  |..*.H........0.1|
+00000040  10 30 0e 06 03 55 04 0a  13 07 41 63 6d 65 20 43  |.0...U....Acme C|
+00000050  6f 30 1e 17 0d 31 36 30  38 31 37 32 31 35 32 33  |o0...16081721523|
+00000060  31 5a 17 0d 31 37 30 38  31 37 32 31 35 32 33 31  |1Z..170817215231|
+00000070  5a 30 12 31 10 30 0e 06  03 55 04 0a 13 07 41 63  |Z0.1.0...U....Ac|
+00000080  6d 65 20 43 6f 30 81 9f  30 0d 06 09 2a 86 48 86  |me Co0..0...*.H.|
+00000090  f7 0d 01 01 01 05 00 03  81 8d 00 30 81 89 02 81  |...........0....|
+000000a0  81 00 ba 6f aa 86 bd cf  bf 9f f2 ef 5c 94 60 78  |...o........\.`x|
+000000b0  6f e8 13 f2 d1 96 6f cd  d9 32 6e 22 37 ce 41 f9  |o.....o..2n"7.A.|
+000000c0  ca 5d 29 ac e1 27 da 61  a2 ee 81 cb 10 c7 df 34  |.])..'.a.......4|
+000000d0  58 95 86 e9 3d 19 e6 5c  27 73 60 c8 8d 78 02 f4  |X...=..\'s`..x..|
+000000e0  1d a4 98 09 a3 19 70 69  3c 25 62 66 2a ab 22 23  |......pi<%bf*."#|
+000000f0  c5 7b 85 38 4f 2e 09 73  32 a7 bd 3e 9b ad ca 84  |.{.8O..s2..>....|
+00000100  07 e6 0f 3a ff 77 c5 9d  41 85 00 8a b6 9b ee b0  |...:.w..A.......|
+00000110  a4 3f 2d 4c 4c e6 42 3e  bb 51 c8 dd 48 54 f4 0c  |.?-LL.B>.Q..HT..|
+00000120  8e 47 02 03 01 00 01 a3  46 30 44 30 0e 06 03 55  |.G......F0D0...U|
+00000130  1d 0f 01 01 ff 04 04 03  02 05 a0 30 13 06 03 55  |...........0...U|
+00000140  1d 25 04 0c 30 0a 06 08  2b 06 01 05 05 07 03 01  |.%..0...+.......|
+00000150  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 0f  |0...U.......0.0.|
+00000160  06 03 55 1d 11 04 08 30  06 87 04 7f 00 00 01 30  |..U....0.......0|
+00000170  0d 06 09 2a 86 48 86 f7  0d 01 01 0b 05 00 03 81  |...*.H..........|
+00000180  81 00 46 ab 44 a2 fb 28  54 f8 5a 67 f8 62 94 f1  |..F.D..(T.Zg.b..|
+00000190  9a b2 18 9e f2 b1 de 1d  7e 6f 76 95 a9 ba e7 5d  |........~ov....]|
+000001a0  a8 16 6c 9c f7 09 d3 37  e4 4b 2b 36 7c 01 ad 41  |..l....7.K+6|..A|
+000001b0  d2 32 d8 c3 d2 93 f9 10  6b 8e 95 b9 2c 17 8a a3  |.2......k...,...|
+000001c0  44 48 bc 59 13 83 16 04  88 a4 81 5c 25 0d 98 0c  |DH.Y.......\%...|
+000001d0  ac 11 b1 28 56 be 1d cd  61 62 84 09 bf d6 80 c6  |...(V...ab......|
+000001e0  45 8d 82 2c b4 d8 83 9b  db c9 22 b7 2a 12 11 7b  |E..,......".*..{|
+000001f0  fa 02 3b c1 c9 ff ea c9  9d a8 49 d3 95 d7 d5 0e  |..;.......I.....|
+00000200  e5 35 16 03 03 00 25 10  00 00 21 20 2f e5 7d a3  |.5....%...! /.}.|
+00000210  47 cd 62 43 15 28 da ac  5f bb 29 07 30 ff f6 84  |G.bC.(.._.).0...|
+00000220  af c4 cf c2 ed 90 99 5f  58 cb 3b 74 16 03 03 00  |......._X.;t....|
+00000230  88 0f 00 00 84 05 01 00  80 05 9b 97 90 30 0b 21  |.............0.!|
+00000240  ed 52 16 19 e0 54 7d 59  42 17 94 81 9b 2c b6 5b  |.R...T}YB....,.[|
+00000250  7f 7c 8e a5 bf 27 a9 25  14 74 f0 37 fa 6e 2b 84  |.|...'.%.t.7.n+.|
+00000260  80 a4 cd ae a6 8a 1b 62  2d 5e 03 ff 70 55 d7 99  |.......b-^..pU..|
+00000270  68 3c b3 0e 03 41 ae af  c6 3e 09 d4 16 8e 06 71  |h<...A...>.....q|
+00000280  14 f8 90 97 cd f6 eb 7d  90 3c d1 f3 95 db 35 3c  |.......}.<....5<|
+00000290  c9 7d dc 30 55 e1 a0 66  8e 26 20 4f 43 89 08 6f  |.}.0U..f.& OC..o|
+000002a0  95 58 42 ae e8 6c b6 77  45 c6 8c c7 ad e5 ed ff  |.XB..l.wE.......|
+000002b0  09 6f 2e 7e b0 e4 5c f2  db 14 03 03 00 01 01 16  |.o.~..\.........|
+000002c0  03 03 00 28 00 00 00 00  00 00 00 00 c0 2c cc 32  |...(.........,.2|
+000002d0  78 5e 6c 3e e9 a3 83 65  b4 bb 4e 79 b2 04 08 30  |x^l>...e..Ny...0|
+000002e0  09 e9 04 99 70 48 44 95  26 b0 37 c9              |....pHD.&.7.|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 0b c5 7d ca a6  |..........$..}..|
-00000010  87 7f 50 7b 88 9c d9 8e  ea 78 a0 40 6b 8e 92 0b  |..P{.....x. at k...|
-00000020  78 88 97 46 ec 7c 20 5b  1f fc da 49 d8 6a be     |x..F.| [...I.j.|
+00000000  14 03 03 00 01 01 16 03  03 00 28 5f 80 e2 f1 78  |..........(_...x|
+00000010  0f cb 58 5c 3c 50 4c 1e  33 8a 1f b7 89 92 37 11  |..X\<PL.3.....7.|
+00000020  a3 8a 76 39 4a 3d b0 1f  a3 e9 ba 52 f8 2b e5 a3  |..v9J=.....R.+..|
+00000030  59 7c ac                                          |Y|.|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1a 16 25 97  df 98 e4 d6 8e d1 2c 0c  |......%.......,.|
-00000010  27 74 67 e5 b7 f1 c7 58  41 5f 04 f5 e4 74 dc 15  |'tg....XA_...t..|
-00000020  03 03 00 16 df 55 01 0d  53 5b b4 36 c7 88 8d b0  |.....U..S[.6....|
-00000030  22 53 ec 87 1b 07 c7 9d  af 89                    |"S........|
+00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 75 dc 54  |.............u.T|
+00000010  d9 c5 b1 c2 c9 64 9a ea  20 e5 76 61 6c 05 af 33  |.....d.. .val..3|
+00000020  6b bc d7 15 03 03 00 1a  00 00 00 00 00 00 00 02  |k...............|
+00000030  24 6b 03 76 d3 da d0 ee  a6 32 c3 58 a1 5e a5 21  |$k.v.....2.X.^.!|
+00000040  b8 3a                                             |.:|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES
index c9b5351..5d795e7 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES
@@ -1,19 +1,20 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 8a ae 27 5b 39  |....Y...U....'[9|
-00000010  8b c4 a6 d5 fa 9e 67 9e  6c fe 53 ed ab ec e0 04  |......g.l.S.....|
-00000020  8d 7c f8 1f d0 db 2e cb  22 4d a1 20 ee 80 5f fc  |.|......"M. .._.|
-00000030  f8 77 8a 23 23 c5 95 81  7f a6 12 f5 e0 19 91 50  |.w.##..........P|
-00000040  da 75 42 c2 eb 45 bf e2  a5 54 ed 6e c0 09 00 00  |.uB..E...T.n....|
+00000000  16 03 03 00 59 02 00 00  55 03 03 c3 2f 08 30 ba  |....Y...U.../.0.|
+00000010  5d 9e 55 ef 23 f9 8a 0d  2f b4 25 02 5f c0 d2 c2  |].U.#.../.%._...|
+00000020  50 7c da db a4 ee 7c 18  df af aa 20 f3 a5 02 de  |P|....|.... ....|
+00000030  54 9f ce b9 6d 69 66 5d  57 76 ff 18 91 d3 93 ab  |T...mif]Wv......|
+00000040  39 13 29 4c b9 a7 3c db  7f 4d 97 fc c0 09 00 00  |9.)L..<..M......|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
@@ -48,43 +49,39 @@
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 03 00 d7 0c 00  00 d3 03 00 17 41 04 3e  |*............A.>|
-00000280  e3 d6 d6 7b d7 ec 6f 4b  88 50 53 26 5a 2b a6 69  |...{..oK.PS&Z+.i|
-00000290  25 6f 30 a7 c3 a5 39 5c  e2 ca b6 35 a5 30 35 9a  |%o0...9\...5.05.|
-000002a0  35 8f e3 65 bd 4c 47 30  72 9a 4b 32 c4 6d 01 c9  |5..e.LG0r.K2.m..|
-000002b0  a5 91 d1 cd 0b 79 e2 04  0f a9 9c c2 72 cd 58 04  |.....y......r.X.|
-000002c0  03 00 8a 30 81 87 02 41  70 01 8c dd 0a 32 db e3  |...0...Ap....2..|
-000002d0  0b 04 9c d0 64 a1 08 06  b1 cf e3 66 79 1f c0 c1  |....d......fy...|
-000002e0  14 34 87 a6 e5 52 11 20  12 24 a5 b6 c2 50 63 86  |.4...R. .$...Pc.|
-000002f0  31 6a e3 85 7d 19 2d 3b  68 bf b7 64 20 37 c7 f9  |1j..}.-;h..d 7..|
-00000300  76 38 b5 32 84 0b f9 b6  71 02 42 01 89 e3 93 85  |v8.2....q.B.....|
-00000310  d6 16 8e 44 66 72 d6 9f  b3 b1 e9 22 ad 2e f8 49  |...Dfr....."...I|
-00000320  41 8f 80 f9 0e d4 fd de  35 67 cf 09 ba 65 f7 a1  |A.......5g...e..|
-00000330  17 a8 c0 b5 a3 cc c0 17  83 af 68 92 5b 5c a9 ce  |..........h.[\..|
-00000340  ce 11 92 93 fe 39 b9 80  19 20 f2 b6 3b 16 03 03  |.....9... ..;...|
-00000350  00 04 0e 00 00 00                                 |......|
+00000270  2a 16 03 03 00 b6 0c 00  00 b2 03 00 1d 20 46 07  |*............ F.|
+00000280  78 fd 3b 0c e0 2c 96 f8  44 b9 e9 06 e9 66 17 35  |x.;..,..D....f.5|
+00000290  c0 92 87 51 f6 e3 d7 f5  09 50 56 c6 e9 3b 04 03  |...Q.....PV..;..|
+000002a0  00 8a 30 81 87 02 41 5d  88 4b fe eb 45 ee 5e 9f  |..0...A].K..E.^.|
+000002b0  ec 20 90 1f 41 aa 47 87  f7 ae 46 71 dc 55 8b 2c  |. ..A.G...Fq.U.,|
+000002c0  ce 70 5f ad 3e fa 3c c3  cb 77 d2 69 67 25 27 08  |.p_.>.<..w.ig%'.|
+000002d0  23 de 52 3c 0e 6c ca 8f  86 8f 61 cd 5b cf d8 42  |#.R<.l....a.[..B|
+000002e0  aa 5a 95 aa 4b d4 d9 f3  02 42 01 81 78 53 9c bd  |.Z..K....B..xS..|
+000002f0  af 7e d9 be 26 07 24 11  ca 4b 1d dd 2b 49 ec 35  |.~..&.$..K..+I.5|
+00000300  25 8d 58 87 ad 80 4f 90  c7 f8 a4 b9 c2 75 b5 12  |%.X...O......u..|
+00000310  a7 2c 49 82 76 e8 ce c4  a7 23 68 75 fc 88 82 13  |.,I.v....#hu....|
+00000320  27 55 a7 50 3c d6 d0 ae  e3 88 94 b4 16 03 03 00  |'U.P<...........|
+00000330  04 0e 00 00 00                                    |.....|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 40 00 00  00 00 00 00 00 00 00 00  |..... at ..........|
-00000060  00 00 00 00 00 00 39 19  e6 fb c7 28 b4 c9 f5 ba  |......9....(....|
-00000070  a2 44 0a 74 70 18 86 1f  5f c2 3d 99 f5 d7 17 04  |.D.tp..._.=.....|
-00000080  88 07 a5 06 01 6a 2c 13  55 0b fc 82 75 b5 24 54  |.....j,.U...u.$T|
-00000090  a0 63 9e f0 ce 92                                 |.c....|
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 40 00 00 00  00 00 00 00 00 00 00 00  |.... at ...........|
+00000040  00 00 00 00 00 04 f4 cc  a8 9f 48 44 ca 19 e6 4c  |..........HD...L|
+00000050  3d 51 f2 29 40 0b 70 06  09 f0 69 5c 51 78 51 1e  |=Q.)@.p...i\QxQ.|
+00000060  2b d1 47 22 8d d6 fb f5  41 bd e4 fd 3d f4 1b 48  |+.G"....A...=..H|
+00000070  44 96 2d 97 b9                                    |D.-..|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 40 15 37 89 e6 1f  |.......... at .7...|
-00000010  20 f6 91 b9 1f fb 29 e9  3e 07 ab c2 09 96 06 89  | .....).>.......|
-00000020  69 c2 dd 63 e1 24 5d cd  af 08 e2 ed df 46 45 6b  |i..c.$]......FEk|
-00000030  1e 9f 62 b6 89 27 04 3f  fc f2 77 71 23 04 24 37  |..b..'.?..wq#.$7|
-00000040  74 8a 0a 64 a8 10 e3 1c  dc 53 64                 |t..d.....Sd|
+00000000  14 03 03 00 01 01 16 03  03 00 40 f1 a2 70 ba 50  |.......... at ..p.P|
+00000010  9d 7a 9f 8f c6 fb 7e 83  75 bd 94 cf e6 c1 4a f0  |.z....~.u.....J.|
+00000020  e6 54 e9 2c 30 23 a2 5c  c2 09 32 d4 06 f7 54 e7  |.T.,0#.\..2...T.|
+00000030  ab 27 a6 66 ab 86 e6 2c  20 12 cf 61 4d ef 12 20  |.'.f..., ..aM.. |
+00000040  ba b6 42 39 b7 76 b9 1b  fc f4 44                 |..B9.v....D|
 >>> Flow 5 (client to server)
 00000000  17 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 ae 4e e7  3a 25 9d 8f fa 06 99 49  |......N.:%.....I|
-00000020  2e b8 0f 49 d0 54 2d 06  b4 d7 4c 60 51 f1 13 11  |...I.T-...L`Q...|
-00000030  c1 b3 f5 d0 bc 15 03 03  00 30 00 00 00 00 00 00  |.........0......|
-00000040  00 00 00 00 00 00 00 00  00 00 80 de bf db 10 74  |...............t|
-00000050  da 3f d8 77 ca 37 cc f3  96 bd d3 e1 34 9c f2 0a  |.?.w.7......4...|
-00000060  70 60 5e 7c a4 7e c9 bd  89 5f                    |p`^|.~..._|
+00000010  00 00 00 00 00 0b 5b d0  ab 14 3f ae 1f 4b 12 25  |......[...?..K.%|
+00000020  05 2a 67 11 0c 17 42 1b  b6 d2 af 16 40 26 fd 7b  |.*g...B.....@&.{|
+00000030  d7 57 10 2a f8 15 03 03  00 30 00 00 00 00 00 00  |.W.*.....0......|
+00000040  00 00 00 00 00 00 00 00  00 00 83 54 64 d6 31 32  |...........Td.12|
+00000050  55 62 32 49 b9 54 7b e3  34 02 1c 75 e3 1b 5a 41  |Ub2I.T{.4..u..ZA|
+00000060  a2 cd 47 26 f0 ed c2 d5  41 34                    |..G&....A4|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES-GCM b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES-GCM
index 48d822a..28a9ef7 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES-GCM
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES-GCM
@@ -1,19 +1,20 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 42 ab c5 81 f5  |....Y...U..B....|
-00000010  c0 5b 73 64 f6 1b e0 59  30 b0 fd c5 e6 b3 57 f2  |.[sd...Y0.....W.|
-00000020  28 3f 5c d2 e8 05 7d a8  29 84 2e 20 8e 18 6b 52  |(?\...}.).. ..kR|
-00000030  1b ee 03 02 64 52 fb 24  44 4f 39 f2 d3 0f e6 9d  |....dR.$DO9.....|
-00000040  50 31 31 b3 39 9e c1 3a  b3 67 41 a0 c0 2b 00 00  |P11.9..:.gA..+..|
+00000000  16 03 03 00 59 02 00 00  55 03 03 72 81 c3 91 37  |....Y...U..r...7|
+00000010  54 37 fb 0f 2b 16 3b 7b  bc a6 d9 2e e2 83 23 83  |T7..+.;{......#.|
+00000020  b3 67 cf 36 dc 65 8d a6  3d cb 72 20 ac b9 b9 48  |.g.6.e..=.r ...H|
+00000030  30 9d fe 67 09 39 f5 47  d2 9a c8 3e 22 02 50 5e  |0..g.9.G...>".P^|
+00000040  fd 02 c9 ff c1 84 2e 2e  ab 78 ef c6 c0 2b 00 00  |.........x...+..|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
@@ -48,38 +49,34 @@
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 03 00 d8 0c 00  00 d4 03 00 17 41 04 7a  |*............A.z|
-00000280  01 b4 2c 50 85 34 6e 2c  2c 52 bc fa cf 71 82 e5  |..,P.4n,,R...q..|
-00000290  98 8d b0 f1 65 5f 7d bc  c8 1b 7c 84 3e 46 45 c5  |....e_}...|.>FE.|
-000002a0  43 0e 72 e1 90 63 40 26  1c 22 dc 9a 3b b8 12 26  |C.r..c@&."..;..&|
-000002b0  a9 d6 1c e1 44 cf c7 38  db 9e 1b d0 b9 bb 06 04  |....D..8........|
-000002c0  03 00 8b 30 81 88 02 42  01 6b af f8 34 ae 89 50  |...0...B.k..4..P|
-000002d0  df 44 20 16 0b f9 ef a9  99 63 39 48 39 08 69 2d  |.D ......c9H9.i-|
-000002e0  2d 9d 8b 3a e8 8a 9c 2f  e9 d2 85 f2 d3 54 53 ec  |-..:.../.....TS.|
-000002f0  b7 18 5b b0 76 3c 38 02  85 cc 00 20 45 9d e7 ba  |..[.v<8.... E...|
-00000300  c0 3f c0 b5 1f df 64 42  fd 34 02 42 00 fa e5 dd  |.?....dB.4.B....|
-00000310  04 c4 60 60 ff 9b 95 a2  a4 b4 80 87 9f 59 b4 8e  |..``.........Y..|
-00000320  72 bf 53 8e 61 b6 df 99  9d 81 05 c5 71 a2 00 cb  |r.S.a.......q...|
-00000330  80 bd e5 2a c3 51 d0 45  2f a3 8b 6d 21 6e 6c 80  |...*.Q.E/..m!nl.|
-00000340  4e f1 28 23 6d 76 df 55  77 69 a1 be 39 05 16 03  |N.(#mv.Uwi..9...|
-00000350  03 00 04 0e 00 00 00                              |.......|
+00000270  2a 16 03 03 00 b6 0c 00  00 b2 03 00 1d 20 b7 1c  |*............ ..|
+00000280  2f 05 5e c8 ae 68 6b 54  c5 88 19 cf 7c 04 b2 ed  |/.^..hkT....|...|
+00000290  8d 5a e9 7e 6b 50 8a ee  12 66 2d 6f e4 7a 04 03  |.Z.~kP...f-o.z..|
+000002a0  00 8a 30 81 87 02 42 01  8d 7e 23 bc d7 a7 ad 73  |..0...B..~#....s|
+000002b0  5f 45 9e 04 da 6e c0 34  a8 09 59 e3 bc ab 80 e1  |_E...n.4..Y.....|
+000002c0  d4 84 79 7d de 90 c1 f2  ea 95 ed fc 7e d3 f0 31  |..y}........~..1|
+000002d0  4c 9b da 82 a0 97 ed e6  c9 f2 b9 2a 0a 1e 0a 2c  |L..........*...,|
+000002e0  7f 1d 62 ea 11 a9 77 5e  2f 02 41 09 88 2b eb 84  |..b...w^/.A..+..|
+000002f0  4f 62 9a c9 8a 0b a2 c6  88 0e 3e d9 29 f0 2b ba  |Ob........>.).+.|
+00000300  08 40 b0 9c 17 70 d9 84  1e d3 39 ad 70 fc df 63  |. at ...p....9.p..c|
+00000310  a0 f6 69 3c 19 ce 0b a5  95 d2 6a b1 46 b1 e5 ba  |..i<......j.F...|
+00000320  fd d2 67 4b 76 e3 eb b9  21 d0 7c 85 16 03 03 00  |..gKv...!.|.....|
+00000330  04 0e 00 00 00                                    |.....|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 94 ba  |.....(..........|
-00000060  0a c6 38 6b 65 60 95 5e  df fc 42 7e ac 9f 5a 25  |..8ke`.^..B~..Z%|
-00000070  39 0e a9 7a 61 b3 17 80  77 82 e5 80 0a af        |9..za...w.....|
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 28 00 00 00  00 00 00 00 00 32 49 7e  |....(........2I~|
+00000040  8f d6 2e 81 d7 03 a6 61  a3 04 98 81 95 84 58 d1  |.......a......X.|
+00000050  a2 33 fe 4a 5d cd 96 76  64 1e 1a 62 03           |.3.J]..vd..b.|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 ef 8d ac 17 6f  |..........(....o|
-00000010  88 03 88 8f f3 d5 a0 60  28 a9 4d e8 20 ae 0c 21  |.......`(.M. ..!|
-00000020  fd d1 50 9b c3 d1 e9 cd  27 ed d7 8b 92 60 49 47  |..P.....'....`IG|
-00000030  ed 9a 74                                          |..t|
+00000000  14 03 03 00 01 01 16 03  03 00 28 28 14 2d ae 7c  |..........((.-.||
+00000010  b8 7d dc 27 b2 18 39 57  c8 be 5c 3d a3 ab fa 5a  |.}.'..9W..\=...Z|
+00000020  3d 55 1b 3d 31 77 95 af  42 86 af 2b e7 5a 98 40  |=U.=1w..B..+.Z.@|
+00000030  18 77 d1                                          |.w.|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 1d 4f 3c  |..............O<|
-00000010  c5 d1 39 01 46 ab 7d d1  75 59 e7 f5 cd fa 02 0b  |..9.F.}.uY......|
-00000020  dd 02 17 15 03 03 00 1a  00 00 00 00 00 00 00 02  |................|
-00000030  e9 a5 d5 0c 05 2a 82 fe  a5 6c 03 6e d0 c4 7d cb  |.....*...l.n..}.|
-00000040  32 f3                                             |2.|
+00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 30 50 2f  |.............0P/|
+00000010  36 4a 7c ee e6 f0 b9 b8  bf 4d e3 63 4d 5e 58 08  |6J|......M.cM^X.|
+00000020  ac ac 82 15 03 03 00 1a  00 00 00 00 00 00 00 02  |................|
+00000030  80 e2 42 ca 91 65 04 4e  ca a8 6f 81 7c 30 c0 1f  |..B..e.N..o.|0..|
+00000040  aa 7b                                             |.{|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES128-SHA256 b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES128-SHA256
new file mode 100644
index 0000000..831fa21
--- /dev/null
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES128-SHA256
@@ -0,0 +1,91 @@
+>>> Flow 1 (client to server)
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
+00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
+>>> Flow 2 (server to client)
+00000000  16 03 03 00 59 02 00 00  55 03 03 be c0 b6 d7 fe  |....Y...U.......|
+00000010  43 9d 21 1f 89 27 bd db  0a 9a 5a 44 dd 79 d1 f2  |C.!..'....ZD.y..|
+00000020  18 9a 2a 04 8c eb e6 a9  93 ef ee 20 35 48 44 08  |..*........ 5HD.|
+00000030  8c 7a 3e f6 0f d7 5f 33  54 60 0b c9 65 4e 17 8d  |.z>..._3T`..eN..|
+00000040  d2 69 b7 20 0b c5 ba 9a  d4 b7 40 39 c0 23 00 00  |.i. ...... at 9.#..|
+00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
+00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
+00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
+00000080  30 09 06 07 2a 86 48 ce  3d 04 01 30 45 31 0b 30  |0...*.H.=..0E1.0|
+00000090  09 06 03 55 04 06 13 02  41 55 31 13 30 11 06 03  |...U....AU1.0...|
+000000a0  55 04 08 13 0a 53 6f 6d  65 2d 53 74 61 74 65 31  |U....Some-State1|
+000000b0  21 30 1f 06 03 55 04 0a  13 18 49 6e 74 65 72 6e  |!0...U....Intern|
+000000c0  65 74 20 57 69 64 67 69  74 73 20 50 74 79 20 4c  |et Widgits Pty L|
+000000d0  74 64 30 1e 17 0d 31 32  31 31 32 32 31 35 30 36  |td0...1211221506|
+000000e0  33 32 5a 17 0d 32 32 31  31 32 30 31 35 30 36 33  |32Z..22112015063|
+000000f0  32 5a 30 45 31 0b 30 09  06 03 55 04 06 13 02 41  |2Z0E1.0...U....A|
+00000100  55 31 13 30 11 06 03 55  04 08 13 0a 53 6f 6d 65  |U1.0...U....Some|
+00000110  2d 53 74 61 74 65 31 21  30 1f 06 03 55 04 0a 13  |-State1!0...U...|
+00000120  18 49 6e 74 65 72 6e 65  74 20 57 69 64 67 69 74  |.Internet Widgit|
+00000130  73 20 50 74 79 20 4c 74  64 30 81 9b 30 10 06 07  |s Pty Ltd0..0...|
+00000140  2a 86 48 ce 3d 02 01 06  05 2b 81 04 00 23 03 81  |*.H.=....+...#..|
+00000150  86 00 04 00 c4 a1 ed be  98 f9 0b 48 73 36 7e c3  |...........Hs6~.|
+00000160  16 56 11 22 f2 3d 53 c3  3b 4d 21 3d cd 6b 75 e6  |.V.".=S.;M!=.ku.|
+00000170  f6 b0 dc 9a df 26 c1 bc  b2 87 f0 72 32 7c b3 64  |.....&.....r2|.d|
+00000180  2f 1c 90 bc ea 68 23 10  7e fe e3 25 c0 48 3a 69  |/....h#.~..%.H:i|
+00000190  e0 28 6d d3 37 00 ef 04  62 dd 0d a0 9c 70 62 83  |.(m.7...b....pb.|
+000001a0  d8 81 d3 64 31 aa 9e 97  31 bd 96 b0 68 c0 9b 23  |...d1...1...h..#|
+000001b0  de 76 64 3f 1a 5c 7f e9  12 0e 58 58 b6 5f 70 dd  |.vd?.\....XX._p.|
+000001c0  9b d8 ea d5 d7 f5 d5 cc  b9 b6 9f 30 66 5b 66 9a  |...........0f[f.|
+000001d0  20 e2 27 e5 bf fe 3b 30  09 06 07 2a 86 48 ce 3d  | .'...;0...*.H.=|
+000001e0  04 01 03 81 8c 00 30 81  88 02 42 01 88 a2 4f eb  |......0...B...O.|
+000001f0  e2 45 c5 48 7d 1b ac f5  ed 98 9d ae 47 70 c0 5e  |.E.H}.......Gp.^|
+00000200  1b b6 2f bd f1 b6 4d b7  61 40 d3 11 a2 ce ee 0b  |../...M.a at ......|
+00000210  7e 92 7e ff 76 9d c3 3b  7e a5 3f ce fa 10 e2 59  |~.~.v..;~.?....Y|
+00000220  ec 47 2d 7c ac da 4e 97  0e 15 a0 6f d0 02 42 01  |.G-|..N....o..B.|
+00000230  4d fc be 67 13 9c 2d 05  0e bd 3f a3 8c 25 c1 33  |M..g..-...?..%.3|
+00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
+00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
+00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
+00000270  2a 16 03 03 00 b7 0c 00  00 b3 03 00 1d 20 0e 53  |*............ .S|
+00000280  82 54 1a ba f8 a4 52 1d  6d b8 70 18 41 e8 67 f9  |.T....R.m.p.A.g.|
+00000290  38 1e fb fa b8 89 2d d6  4d 1b ae 67 fe 75 04 03  |8.....-.M..g.u..|
+000002a0  00 8b 30 81 88 02 42 01  68 0e d4 92 a6 1a d9 66  |..0...B.h......f|
+000002b0  ff 0e ca 4c 32 b8 78 d3  52 d1 ad a2 32 83 f0 3c  |...L2.x.R...2..<|
+000002c0  43 e0 7e 92 94 e9 c6 99  00 d9 f7 06 c0 2c 72 c0  |C.~..........,r.|
+000002d0  9b f7 c0 ec 1f 23 8f b5  e5 74 9d ff 89 17 12 b4  |.....#...t......|
+000002e0  f1 f5 25 f7 2e 0d 78 f6  1c 02 42 01 fc da dd c8  |..%...x...B.....|
+000002f0  65 30 67 a3 ff 42 e3 37  19 ba 7c 04 6b a1 b3 97  |e0g..B.7..|.k...|
+00000300  b0 ca 8c 2d fc b0 40 1c  a1 d8 c9 64 fe df 48 3b  |...-.. at ....d..H;|
+00000310  07 57 1f 81 a2 3e a4 84  96 00 fb 55 29 1c 94 9d  |.W...>.....U)...|
+00000320  f9 0d a4 71 4f 5f fd c3  22 e2 88 07 21 16 03 03  |...qO_.."...!...|
+00000330  00 04 0e 00 00 00                                 |......|
+>>> Flow 3 (client to server)
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 50 00 00 00  00 00 00 00 00 00 00 00  |....P...........|
+00000040  00 00 00 00 00 7c e9 97  4b 98 8a 4c 59 95 3e 31  |.....|..K..LY.>1|
+00000050  c4 b6 e2 79 10 de bc 8e  aa 1e 52 07 b2 e1 52 bc  |...y......R...R.|
+00000060  3b da 8d 5f 12 6a 18 d1  0a 5d 93 1c ad bb f9 b7  |;.._.j...]......|
+00000070  6b 58 49 39 ea 3a 9e 20  47 69 43 b4 b4 d8 16 d0  |kXI9.:. GiC.....|
+00000080  f0 9d 36 74 04                                    |..6t.|
+>>> Flow 4 (server to client)
+00000000  14 03 03 00 01 01 16 03  03 00 50 60 d5 ac 39 b6  |..........P`..9.|
+00000010  58 50 03 90 9f c9 78 f1  45 43 b1 34 bd c8 29 65  |XP....x.EC.4..)e|
+00000020  54 38 7a 88 46 15 e8 a4  fb 9d 80 4e d6 45 e1 8d  |T8z.F......N.E..|
+00000030  27 d6 09 66 1d ee 46 6d  dd 8e 89 34 0f 4a fb fd  |'..f..Fm...4.J..|
+00000040  bc 85 08 07 f0 5b 1c 24  e2 11 1b e2 a4 94 f5 80  |.....[.$........|
+00000050  fa 47 f4 62 0e b9 1c 31  cb 7b bf                 |.G.b...1.{.|
+>>> Flow 5 (client to server)
+00000000  17 03 03 00 40 00 00 00  00 00 00 00 00 00 00 00  |.... at ...........|
+00000010  00 00 00 00 00 37 b7 23  a2 06 97 f3 60 9a f0 7e  |.....7.#....`..~|
+00000020  b7 11 6d 0d 66 0e db 38  1a eb cd 72 80 c8 54 ef  |..m.f..8...r..T.|
+00000030  cf 90 6d 22 68 41 63 03  46 b9 28 4f 2f d6 fe fa  |..m"hAc.F.(O/...|
+00000040  b2 91 07 36 71 15 03 03  00 40 00 00 00 00 00 00  |...6q.... at ......|
+00000050  00 00 00 00 00 00 00 00  00 00 ca 17 d9 fd 1a 0e  |................|
+00000060  21 db a4 92 dc 92 e8 89  9d 14 6b 8a d3 ee a7 95  |!.........k.....|
+00000070  c0 91 8d 3c af 5a 48 d5  c6 2f 66 b8 b8 d4 ce f9  |...<.ZH../f.....|
+00000080  59 e5 e0 e2 df e5 7e ea  94 03                    |Y.....~...|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES256-GCM-SHA384 b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES256-GCM-SHA384
index 2a16651..c22edd0 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES256-GCM-SHA384
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES256-GCM-SHA384
@@ -1,19 +1,20 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 ed 6d 5a 1c 89  |....Y...U...mZ..|
-00000010  a4 f3 35 0b e4 74 7e e2  05 a5 36 4d 4a 55 b3 7c  |..5..t~...6MJU.||
-00000020  a1 a6 42 a3 fc 35 8c e0  97 5b 4b 20 a1 4a 06 28  |..B..5...[K .J.(|
-00000030  4d 40 0b fc 47 d5 4d 9b  d5 43 b0 0d 0d c6 ae 30  |M at ..G.M..C.....0|
-00000040  79 59 00 d4 90 96 98 92  d2 3a 57 07 c0 2c 00 00  |yY.......:W..,..|
+00000000  16 03 03 00 59 02 00 00  55 03 03 e6 0e 27 a7 58  |....Y...U....'.X|
+00000010  1d a3 1d 1d 21 64 31 f6  1e bc 28 b4 46 7e 26 be  |....!d1...(.F~&.|
+00000020  de 0a 65 eb f0 18 dc 7f  3e 1b 55 20 fe 66 50 20  |..e.....>.U .fP |
+00000030  f0 f0 48 a8 db 0a ff ee  60 ea 3d 7f 07 5e b9 65  |..H.....`.=..^.e|
+00000040  c3 e4 2a 19 9c bd 57 36  ca e3 a7 2d c0 2c 00 00  |..*...W6...-.,..|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
@@ -48,38 +49,34 @@
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 03 00 d7 0c 00  00 d3 03 00 17 41 04 89  |*............A..|
-00000280  e6 6d 6a 56 3e e5 4e 72  df 2b 41 11 de a0 c0 3e  |.mjV>.Nr.+A....>|
-00000290  22 04 9a b5 a8 d6 22 30  2a e5 bd 83 1c 7a 8e 6c  |"....."0*....z.l|
-000002a0  93 ab 8f d7 64 9e fe 89  c0 da 9a 45 7d 76 91 69  |....d......E}v.i|
-000002b0  0a 11 c5 59 26 49 ec 69  99 b3 91 a5 4b 2b 89 04  |...Y&I.i....K+..|
-000002c0  03 00 8a 30 81 87 02 42  01 17 1d ff 9a 99 76 20  |...0...B......v |
-000002d0  13 8a e1 5a a8 04 8a 1e  84 57 fd b0 95 c1 6c af  |...Z.....W....l.|
-000002e0  b2 66 13 b5 75 36 ce 86  69 67 3d dc 82 2f 06 57  |.f..u6..ig=../.W|
-000002f0  19 14 56 54 0e 8e 04 74  0b 73 49 61 92 8e d1 9a  |..VT...t.sIa....|
-00000300  b5 60 7f 65 a8 f8 99 eb  ac 56 02 41 57 a3 78 57  |.`.e.....V.AW.xW|
-00000310  8a dd fa 9c 3d 24 a0 f2  0a 74 1a 8a 8f 6c 82 55  |....=$...t...l.U|
-00000320  4c cd d8 5d 79 99 87 93  41 e7 78 f4 28 0d ef 63  |L..]y...A.x.(..c|
-00000330  fb da 8e 93 86 31 6e 3e  ca 6f 6b 1b fd 7a a3 86  |.....1n>.ok..z..|
-00000340  6e bb 17 35 90 d9 a4 df  12 d0 54 5e 25 16 03 03  |n..5......T^%...|
-00000350  00 04 0e 00 00 00                                 |......|
+00000270  2a 16 03 03 00 b7 0c 00  00 b3 03 00 1d 20 17 21  |*............ .!|
+00000280  a9 b8 65 8b aa 41 d2 d1  45 45 e5 ce 39 60 54 b6  |..e..A..EE..9`T.|
+00000290  43 9f c4 19 a4 aa ec 71  08 b0 d1 22 f7 46 04 03  |C......q...".F..|
+000002a0  00 8b 30 81 88 02 42 00  8b a5 d9 d3 8f a1 72 48  |..0...B.......rH|
+000002b0  06 42 25 c3 f6 c8 46 8d  88 30 36 7d d8 18 a9 cc  |.B%...F..06}....|
+000002c0  de e4 c8 3f e9 d2 f0 88  18 cc c6 fb 14 e0 05 b1  |...?............|
+000002d0  ec 50 3d 57 b4 e9 83 57  55 4b 0d 2c 89 69 ff b1  |.P=W...WUK.,.i..|
+000002e0  58 0b 01 89 48 97 ee 88  7e 02 42 01 e1 6f 9c 36  |X...H...~.B..o.6|
+000002f0  6a 6c 86 24 d6 b3 45 f1  6c 03 d8 fd da d8 cc 52  |jl.$..E.l......R|
+00000300  04 41 7a c5 f9 b5 91 a5  6c d8 5a 03 ad de e3 da  |.Az.....l.Z.....|
+00000310  de f8 db b0 bc 75 38 03  ab 84 ac 3f b2 c2 7e 6d  |.....u8....?..~m|
+00000320  a7 2e c0 d9 bd 85 e2 7b  36 11 2b 12 14 16 03 03  |.......{6.+.....|
+00000330  00 04 0e 00 00 00                                 |......|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 9d e7  |.....(..........|
-00000060  31 2a 0a 46 84 fd d9 18  c2 b0 b1 31 eb 63 4d 2d  |1*.F.......1.cM-|
-00000070  ee 17 59 e6 b4 0f c6 d8  3d 8c e9 57 83 a8        |..Y.....=..W..|
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 28 00 00 00  00 00 00 00 00 4b a3 cc  |....(........K..|
+00000040  a1 5b 04 d4 1e 6c 2c 26  56 23 62 50 bc d6 90 0b  |.[...l,&V#bP....|
+00000050  67 41 d9 7c 79 a5 53 54  73 0a 93 e2 73           |gA.|y.STs...s|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 e0 85 25 02 b4  |..........(..%..|
-00000010  86 32 57 70 3c 7e 6b e5  75 e0 3a 43 c8 c2 fe f8  |.2Wp<~k.u.:C....|
-00000020  2e 04 fe 73 e4 7b 2c 9a  e0 65 2e d6 53 ae f1 19  |...s.{,..e..S...|
-00000030  dd 6f 1a                                          |.o.|
+00000000  14 03 03 00 01 01 16 03  03 00 28 e3 19 7b 8c 8a  |..........(..{..|
+00000010  52 35 82 cc 70 50 83 22  88 8b 0a 54 bc eb ff 57  |R5..pP."...T...W|
+00000020  2c df 0d 50 d6 21 2f d2  d9 e8 15 27 b9 d7 01 a3  |,..P.!/....'....|
+00000030  f2 62 0b                                          |.b.|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 29 b2 e6  |.............)..|
-00000010  c3 2e 72 ba cc ac d9 3b  c7 0c 1d 53 b2 30 39 71  |..r....;...S.09q|
-00000020  6e dd 79 15 03 03 00 1a  00 00 00 00 00 00 00 02  |n.y.............|
-00000030  88 c9 92 fe 6c 1f 6c fd  bd 7b fb 0a 8a b5 cc c9  |....l.l..{......|
-00000040  94 90                                             |..|
+00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 cc cf 92  |................|
+00000010  4d 25 58 96 1d dc df fb  d9 1f a5 49 87 45 dd 73  |M%X........I.E.s|
+00000020  1a 17 ae 15 03 03 00 1a  00 00 00 00 00 00 00 02  |................|
+00000030  fb b5 c5 e4 aa ea e7 7e  ff dd f7 11 63 c0 e4 a3  |.......~....c...|
+00000040  86 fc                                             |..|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-CHACHA20-POLY1305 b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-CHACHA20-POLY1305
new file mode 100644
index 0000000..61e6657
--- /dev/null
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-CHACHA20-POLY1305
@@ -0,0 +1,77 @@
+>>> Flow 1 (client to server)
+00000000  16 03 01 00 67 01 00 00  63 03 03 00 00 00 00 00  |....g...c.......|
+00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 02 cc a9  |................|
+00000030  01 00 00 38 00 05 00 05  01 00 00 00 00 00 0a 00  |...8............|
+00000040  0a 00 08 00 1d 00 17 00  18 00 19 00 0b 00 02 01  |................|
+00000050  00 00 0d 00 0e 00 0c 04  01 04 03 05 01 05 03 02  |................|
+00000060  01 02 03 ff 01 00 01 00  00 12 00 00              |............|
+>>> Flow 2 (server to client)
+00000000  16 03 03 00 59 02 00 00  55 03 03 d6 47 27 38 fc  |....Y...U...G'8.|
+00000010  16 92 2c 1f a6 53 a9 31  85 65 a7 83 0a 8f cb 4d  |..,..S.1.e.....M|
+00000020  7d 5b df c1 2e b9 b1 08  e3 b9 96 20 16 0c e5 07  |}[......... ....|
+00000030  27 cc 4f 7d 11 ef 1a 14  c6 42 bf e9 c1 b7 a5 89  |'.O}.....B......|
+00000040  ca 2b 4c 30 4f c7 c8 10  13 b0 b1 6b cc a9 00 00  |.+L0O......k....|
+00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
+00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
+00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
+00000080  30 09 06 07 2a 86 48 ce  3d 04 01 30 45 31 0b 30  |0...*.H.=..0E1.0|
+00000090  09 06 03 55 04 06 13 02  41 55 31 13 30 11 06 03  |...U....AU1.0...|
+000000a0  55 04 08 13 0a 53 6f 6d  65 2d 53 74 61 74 65 31  |U....Some-State1|
+000000b0  21 30 1f 06 03 55 04 0a  13 18 49 6e 74 65 72 6e  |!0...U....Intern|
+000000c0  65 74 20 57 69 64 67 69  74 73 20 50 74 79 20 4c  |et Widgits Pty L|
+000000d0  74 64 30 1e 17 0d 31 32  31 31 32 32 31 35 30 36  |td0...1211221506|
+000000e0  33 32 5a 17 0d 32 32 31  31 32 30 31 35 30 36 33  |32Z..22112015063|
+000000f0  32 5a 30 45 31 0b 30 09  06 03 55 04 06 13 02 41  |2Z0E1.0...U....A|
+00000100  55 31 13 30 11 06 03 55  04 08 13 0a 53 6f 6d 65  |U1.0...U....Some|
+00000110  2d 53 74 61 74 65 31 21  30 1f 06 03 55 04 0a 13  |-State1!0...U...|
+00000120  18 49 6e 74 65 72 6e 65  74 20 57 69 64 67 69 74  |.Internet Widgit|
+00000130  73 20 50 74 79 20 4c 74  64 30 81 9b 30 10 06 07  |s Pty Ltd0..0...|
+00000140  2a 86 48 ce 3d 02 01 06  05 2b 81 04 00 23 03 81  |*.H.=....+...#..|
+00000150  86 00 04 00 c4 a1 ed be  98 f9 0b 48 73 36 7e c3  |...........Hs6~.|
+00000160  16 56 11 22 f2 3d 53 c3  3b 4d 21 3d cd 6b 75 e6  |.V.".=S.;M!=.ku.|
+00000170  f6 b0 dc 9a df 26 c1 bc  b2 87 f0 72 32 7c b3 64  |.....&.....r2|.d|
+00000180  2f 1c 90 bc ea 68 23 10  7e fe e3 25 c0 48 3a 69  |/....h#.~..%.H:i|
+00000190  e0 28 6d d3 37 00 ef 04  62 dd 0d a0 9c 70 62 83  |.(m.7...b....pb.|
+000001a0  d8 81 d3 64 31 aa 9e 97  31 bd 96 b0 68 c0 9b 23  |...d1...1...h..#|
+000001b0  de 76 64 3f 1a 5c 7f e9  12 0e 58 58 b6 5f 70 dd  |.vd?.\....XX._p.|
+000001c0  9b d8 ea d5 d7 f5 d5 cc  b9 b6 9f 30 66 5b 66 9a  |...........0f[f.|
+000001d0  20 e2 27 e5 bf fe 3b 30  09 06 07 2a 86 48 ce 3d  | .'...;0...*.H.=|
+000001e0  04 01 03 81 8c 00 30 81  88 02 42 01 88 a2 4f eb  |......0...B...O.|
+000001f0  e2 45 c5 48 7d 1b ac f5  ed 98 9d ae 47 70 c0 5e  |.E.H}.......Gp.^|
+00000200  1b b6 2f bd f1 b6 4d b7  61 40 d3 11 a2 ce ee 0b  |../...M.a at ......|
+00000210  7e 92 7e ff 76 9d c3 3b  7e a5 3f ce fa 10 e2 59  |~.~.v..;~.?....Y|
+00000220  ec 47 2d 7c ac da 4e 97  0e 15 a0 6f d0 02 42 01  |.G-|..N....o..B.|
+00000230  4d fc be 67 13 9c 2d 05  0e bd 3f a3 8c 25 c1 33  |M..g..-...?..%.3|
+00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
+00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
+00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
+00000270  2a 16 03 03 00 b7 0c 00  00 b3 03 00 1d 20 69 78  |*............ ix|
+00000280  7b e5 14 95 c8 d1 3c 7e  c2 d7 38 33 c3 9f 8f dc  |{.....<~..83....|
+00000290  25 8d 89 8a 99 a4 e4 8b  40 17 fc 80 43 67 04 03  |%....... at ...Cg..|
+000002a0  00 8b 30 81 88 02 42 01  32 a8 dd d9 ec 11 d2 f2  |..0...B.2.......|
+000002b0  6d 86 da 31 00 8c bf ed  81 1d 8c c8 23 87 98 f7  |m..1........#...|
+000002c0  25 0c 1b 3d 9f 07 80 11  bc 07 b1 15 5f 3a 81 0e  |%..=........_:..|
+000002d0  59 04 e8 09 be ea 21 97  34 a9 8a 2f ef 3a 47 ad  |Y.....!.4../.:G.|
+000002e0  3b f9 9d f3 b8 b8 9a 93  03 02 42 01 bc 88 6b 99  |;.........B...k.|
+000002f0  d7 7a df de 5a 75 53 b0  3c 4c 1d 8b 15 c5 a7 9d  |.z..ZuS.<L......|
+00000300  3d 00 c0 f7 19 47 88 30  00 29 24 80 23 45 88 2e  |=....G.0.)$.#E..|
+00000310  11 60 3e 4b 6a 41 ad dc  3d 7d 3f 59 a0 0e fd d6  |.`>KjA..=}?Y....|
+00000320  f7 c7 7f 63 49 2f e4 4e  d9 8f 2d e5 98 16 03 03  |...cI/.N..-.....|
+00000330  00 04 0e 00 00 00                                 |......|
+>>> Flow 3 (client to server)
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 20 7c 89 36  36 77 8c 09 31 e4 48 01  |.... |.66w..1.H.|
+00000040  6f 08 27 a8 bb 1b 1c a6  0c 09 ec 0b f6 a3 be bd  |o.'.............|
+00000050  76 70 fb f8 e5                                    |vp...|
+>>> Flow 4 (server to client)
+00000000  14 03 03 00 01 01 16 03  03 00 20 a0 db 6c df b1  |.......... ..l..|
+00000010  87 77 78 ad 22 b2 98 77  e8 57 aa 13 a8 98 35 63  |.wx."..w.W....5c|
+00000020  00 c5 13 b9 88 5d ca bf  bc c5 c3                 |.....].....|
+>>> Flow 5 (client to server)
+00000000  17 03 03 00 16 16 00 c8  c6 25 ae 11 9d a5 10 75  |.........%.....u|
+00000010  e4 4c e3 69 12 2b d9 9e  8e 40 88 15 03 03 00 12  |.L.i.+... at ......|
+00000020  cf ab ac d4 c4 8e 9c 92  c4 2f 1f c6 96 0b 36 c9  |........./....6.|
+00000030  f5 22                                             |."|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES
index 29767b7..45728cf 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES
@@ -1,95 +1,91 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 2a 6a a8 b3 97  |....Y...U..*j...|
-00000010  d5 c8 5e b4 22 7e d0 a5  c7 46 af 89 60 44 77 5e  |..^."~...F..`Dw^|
-00000020  1a f8 3a 30 08 6d 5f 4c  61 36 c5 20 57 79 91 3e  |..:0.m_La6. Wy.>|
-00000030  1f 40 d1 f1 33 d7 a9 fb  93 eb 16 0d e1 39 e3 a3  |. at ..3........9..|
-00000040  80 e3 4f 58 a6 f8 a4 be  19 dd ef ee c0 13 00 00  |..OX............|
+00000000  16 03 03 00 59 02 00 00  55 03 03 2f 51 e0 81 eb  |....Y...U../Q...|
+00000010  d2 db 4f 22 fa 11 d2 56  f3 06 d6 a0 97 d2 f3 74  |..O"...V.......t|
+00000020  fc a9 a7 73 ba a8 ee f2  05 89 15 20 0f 96 70 60  |...s....... ..p`|
+00000030  6f 78 aa 56 fa 92 5e e3  bc e7 f0 40 00 48 8b 84  |ox.V..^.... at .H..|
+00000040  57 b8 49 e9 f9 00 99 ff  73 29 f6 e7 c0 13 00 00  |W.I.....s)......|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
-00000060  03 02 71 0b 00 02 6d 00  02 6a 00 02 67 30 82 02  |..q...m..j..g0..|
-00000070  63 30 82 01 cc a0 03 02  01 02 02 09 00 a2 73 00  |c0............s.|
-00000080  0c 81 00 cb f3 30 0d 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000090  01 0b 05 00 30 2b 31 17  30 15 06 03 55 04 0a 13  |....0+1.0...U...|
-000000a0  0e 47 6f 6f 67 6c 65 20  54 45 53 54 49 4e 47 31  |.Google TESTING1|
-000000b0  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
-000000c0  74 30 1e 17 0d 31 35 30  31 30 31 30 30 30 30 30  |t0...15010100000|
-000000d0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
-000000e0  5a 30 26 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |Z0&1.0...U....Go|
-000000f0  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 0b 30 09  |ogle TESTING1.0.|
-00000100  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
-00000110  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
-00000120  81 89 02 81 81 00 af 87  88 f6 20 1b 95 65 6c 14  |.......... ..el.|
-00000130  ab 44 05 af 3b 45 14 e3  b7 6d fd 00 63 4d 95 7f  |.D..;E...m..cM..|
-00000140  fe 6a 62 35 86 c0 4a f9  18 7c f6 aa 25 5e 7a 64  |.jb5..J..|..%^zd|
-00000150  31 66 00 ba f4 8e 92 af  c7 6b d8 76 d4 f3 5f 41  |1f.......k.v.._A|
-00000160  cb 6e 56 15 97 1b 97 c1  3c 12 39 21 66 3d 2b 16  |.nV.....<.9!f=+.|
-00000170  d1 bc db 1c c0 a7 da b7  ca ad ba da cb d5 21 50  |..............!P|
-00000180  ec de 8d ab d1 6b 81 4b  89 02 f3 c4 be c1 6c 89  |.....k.K......l.|
-00000190  b1 44 84 bd 21 d1 04 7d  9d 16 4d f9 82 15 f6 ef  |.D..!..}..M.....|
-000001a0  fa d6 09 47 f2 fb 02 03  01 00 01 a3 81 93 30 81  |...G..........0.|
-000001b0  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
-000001c0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
-000001d0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
-000001e0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
-000001f0  06 03 55 1d 0e 04 12 04  10 12 50 8d 89 6f 1b d1  |..U.......P..o..|
-00000200  dc 54 4d 6e cb 69 5e 06  f4 30 1b 06 03 55 1d 23  |.TMn.i^..0...U.#|
-00000210  04 14 30 12 80 10 bf 3d  b6 a9 66 f2 b8 40 cf ea  |..0....=..f.. at ..|
-00000220  b4 03 78 48 1a 41 30 19  06 03 55 1d 11 04 12 30  |..xH.A0...U....0|
-00000230  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
-00000240  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
-00000250  03 81 81 00 92 7c af 91  55 12 18 96 59 31 a6 48  |.....|..U...Y1.H|
-00000260  40 d5 2d d5 ee bb 02 a0  f5 c2 1e 7c 9b b3 30 7d  |@.-........|..0}|
-00000270  3c dc 76 da 4f 3d c0 fa  ae 2d 33 24 6b 03 7b 1b  |<.v.O=...-3$k.{.|
-00000280  67 59 11 21 b5 11 bc 77  b9 d9 e0 6e a8 2d 2e 35  |gY.!...w...n.-.5|
-00000290  fa 64 5f 22 3e 63 10 6b  be ff 14 86 6d 0d f0 15  |.d_">c.k....m...|
-000002a0  31 a8 14 38 1e 3b 84 87  2c cb 98 ed 51 76 b9 b1  |1..8.;..,...Qv..|
-000002b0  4f dd db 9b 84 04 86 40  fa 51 dd ba b4 8d eb e3  |O...... at .Q......|
-000002c0  46 de 46 b9 4f 86 c7 f9  a4 c2 41 34 ac cc f6 ea  |F.F.O.....A4....|
-000002d0  b0 ab 39 18 16 03 03 00  cd 0c 00 00 c9 03 00 17  |..9.............|
-000002e0  41 04 8e c8 e8 42 7c 8a  01 c2 ff 01 cb 0b e1 20  |A....B|........ |
-000002f0  50 42 d4 3a 1b 34 ff 74  59 81 2f a2 0e 29 b2 f9  |PB.:.4.tY./..)..|
-00000300  47 cf 0d 08 97 cf aa 9f  fe f0 7f e8 f4 fd 3a fa  |G.............:.|
-00000310  a6 b6 47 32 d3 25 78 87  bf 77 cc 12 37 02 6a ad  |..G2.%x..w..7.j.|
-00000320  cf 2c 04 01 00 80 13 a1  95 17 7b 21 86 7f f2 02  |.,........{!....|
-00000330  9f ed 88 2d 1f 2c 38 96  bc fa 5a 39 85 4b 9f ff  |...-.,8...Z9.K..|
-00000340  5c 7a 02 1e 5f c9 4a 69  51 d3 83 34 9f dc 8c 39  |\z.._.JiQ..4...9|
-00000350  fe 81 76 fc c3 59 ff e2  a8 81 ca 6f f6 52 c9 44  |..v..Y.....o.R.D|
-00000360  a0 3f 5e 5e 92 20 db d9  2e 0b e3 ab 75 e7 79 f6  |.?^^. ......u.y.|
-00000370  b2 73 17 e1 94 1e 12 62  e9 b0 0f 04 e7 5d 83 ac  |.s.....b.....]..|
-00000380  71 ca a5 62 40 dd 69 b1  3f cf bb 3d c7 3e 51 6c  |q..b at .i.?..=.>Ql|
-00000390  11 f2 cf 39 f1 b5 72 bd  52 d4 3d 3c c0 90 34 c8  |...9..r.R.=<..4.|
-000003a0  4b 22 55 39 1c 2b 16 03  03 00 04 0e 00 00 00     |K"U9.+.........|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 48 77 87 3e 04 c9 14  |........ Hw.>...|
+000002d0  56 9d 1b 41 4b d0 eb 65  8d 56 56 97 fd 73 97 cd  |V..AK..e.VV..s..|
+000002e0  c6 88 8f 8e 79 99 09 65  53 04 01 00 80 98 c2 ff  |....y..eS.......|
+000002f0  49 aa 41 ce 0e 7b 03 99  39 c0 b5 ac 72 16 1c 5e  |I.A..{..9...r..^|
+00000300  a0 92 f1 07 0c 93 dc f6  25 2b 5c be e3 65 41 a9  |........%+\..eA.|
+00000310  1e 57 6d 9f 28 50 ca 87  2f c7 b0 15 2e 15 d2 cc  |.Wm.(P../.......|
+00000320  4d 0e 42 4c 0a 01 4d 1b  9c d1 17 e7 22 9a 6a a9  |M.BL..M.....".j.|
+00000330  27 0b 7a a7 32 e3 c7 5a  d1 7f f2 1c 45 61 91 a8  |'.z.2..Z....Ea..|
+00000340  e0 e0 49 de b7 2f a6 89  63 94 ed 0e 63 15 6b 4f  |..I../..c...c.kO|
+00000350  fb 62 c4 35 cb 98 89 c2  d1 bc f6 e2 2d 8f 9f 72  |.b.5........-..r|
+00000360  56 79 50 5f cd 73 00 f1  65 bf a4 3f 87 16 03 03  |VyP_.s..e..?....|
+00000370  00 04 0e 00 00 00                                 |......|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 40 00 00  00 00 00 00 00 00 00 00  |..... at ..........|
-00000060  00 00 00 00 00 00 a2 6e  de ea 78 0c 4d 20 ad 1f  |.......n..x.M ..|
-00000070  1a f5 6b 15 09 f1 50 bb  cd 40 0e c7 d9 ed 7f e1  |..k...P.. at ......|
-00000080  4b bc d3 26 5d 89 b7 26  c5 6c 0e 59 6f 84 51 5d  |K..&]..&.l.Yo.Q]|
-00000090  2f 75 d8 0f 2e e8                                 |/u....|
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 40 00 00 00  00 00 00 00 00 00 00 00  |.... at ...........|
+00000040  00 00 00 00 00 93 4b 37  8d 57 43 52 77 56 d2 af  |......K7.WCRwV..|
+00000050  7c 56 d0 bf 1e 7b 29 55  3e b7 d0 1c 02 2e 0d de  ||V...{)U>.......|
+00000060  09 66 f2 98 21 57 ab d2  d2 4a 73 c1 c5 fe f1 b8  |.f..!W...Js.....|
+00000070  95 d3 fc 70 ce                                    |...p.|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 40 dd d8 e7 63 89  |.......... at ...c.|
-00000010  8e cc 3e e0 df 6d 5a 42  b3 49 1b 66 e8 79 e9 f0  |..>..mZB.I.f.y..|
-00000020  8a c3 0e 5e d7 01 ac 04  81 6a e1 60 14 60 b9 a6  |...^.....j.`.`..|
-00000030  4c a5 46 43 74 df 30 1e  f8 74 77 4c b5 42 e5 25  |L.FCt.0..twL.B.%|
-00000040  81 9d b1 04 bc 02 46 bd  b1 55 d0                 |......F..U.|
+00000000  14 03 03 00 01 01 16 03  03 00 40 b3 e1 81 3e 0a  |.......... at ...>.|
+00000010  f8 f3 c6 05 c1 09 f5 73  01 eb 18 1a 05 fa 2f 9b  |.......s....../.|
+00000020  b2 bc c7 44 23 38 ed b9  99 a0 56 7d 8b e4 a5 4b  |...D#8....V}...K|
+00000030  f1 89 45 bc 95 ea 06 a8  48 de 07 bf d5 cb 53 bc  |..E.....H.....S.|
+00000040  50 fa 25 fb d5 79 17 ec  4d be 3d                 |P.%..y..M.=|
 >>> Flow 5 (client to server)
 00000000  17 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 35 49 6d  a7 3f a1 39 5d 37 8d 2e  |.....5Im.?.9]7..|
-00000020  c5 1e 90 3b f9 60 58 d3  47 e3 db 73 8b aa 6c 9e  |...;.`X.G..s..l.|
-00000030  b5 82 55 09 62 15 03 03  00 30 00 00 00 00 00 00  |..U.b....0......|
-00000040  00 00 00 00 00 00 00 00  00 00 71 b3 7b c7 d4 27  |..........q.{..'|
-00000050  f9 77 7f d0 80 25 1b 43  d0 0e 92 38 8c f3 2f 50  |.w...%.C...8../P|
-00000060  eb 96 22 fb e6 09 45 ec  7f 16                    |.."...E...|
+00000010  00 00 00 00 00 3e 90 61  a4 f1 53 ac 7b b2 9f 4e  |.....>.a..S.{..N|
+00000020  2c 16 5a 77 8b da 5d 68  5c 8b a8 6d 44 52 f3 ad  |,.Zw..]h\..mDR..|
+00000030  8e ba c8 89 2f 15 03 03  00 30 00 00 00 00 00 00  |..../....0......|
+00000040  00 00 00 00 00 00 00 00  00 00 e5 01 5d ef 4c 0c  |............].L.|
+00000050  07 8f 21 99 60 83 ee 36  13 8e 25 15 32 85 a5 96  |..!.`..6..%.2...|
+00000060  36 90 60 49 4f c7 54 99  dd 76                    |6.`IO.T..v|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES128-SHA256 b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES128-SHA256
new file mode 100644
index 0000000..6b02249
--- /dev/null
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES128-SHA256
@@ -0,0 +1,95 @@
+>>> Flow 1 (client to server)
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
+00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
+>>> Flow 2 (server to client)
+00000000  16 03 03 00 59 02 00 00  55 03 03 ab d3 05 5e d0  |....Y...U.....^.|
+00000010  80 0b 87 e9 43 26 e2 c9  28 04 3f eb 68 05 54 3d  |....C&..(.?.h.T=|
+00000020  9b 28 d0 4e d4 d9 25 e5  b0 27 2b 20 89 27 da d5  |.(.N..%..'+ .'..|
+00000030  3d 19 38 63 01 34 f6 43  1b a9 f7 09 12 7d 27 e1  |=.8c.4.C.....}'.|
+00000040  f6 23 b8 39 24 8b 1e c7  a3 2f 07 16 c0 27 00 00  |.#.9$..../...'..|
+00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 ec 71 cc fb 07 bd 0f  |........ .q.....|
+000002d0  6b e0 e1 27 7f 62 59 06  09 3c 09 bc b1 c9 09 93  |k..'.bY..<......|
+000002e0  e9 b0 a4 5b f3 be 14 d1  3c 04 01 00 80 a9 c7 98  |...[....<.......|
+000002f0  ea ac 6a 9b 49 7c 72 45  4d 5c c8 4c d6 56 64 1b  |..j.I|rEM\.L.Vd.|
+00000300  44 7f 13 4f 2a ed e9 6b  c7 c0 a2 25 3b 7a 99 f4  |D..O*..k...%;z..|
+00000310  93 84 35 78 72 21 ca f6  29 1b 60 d7 f6 bd 31 5b  |..5xr!..).`...1[|
+00000320  7a fb 57 20 30 cc e6 90  07 b2 0e 08 82 86 56 a7  |z.W 0.........V.|
+00000330  55 00 fd f4 ce f4 b1 74  27 e9 0a 28 1c bc 56 47  |U......t'..(..VG|
+00000340  f7 18 3e 9e 9c 45 2d 1d  82 a8 66 51 27 25 be ec  |..>..E-...fQ'%..|
+00000350  cd 9e 83 89 7e e0 e3 0f  3b 7b 32 f2 26 7b 30 c8  |....~...;{2.&{0.|
+00000360  c1 e3 7b 4c f4 14 d5 51  ea b7 45 7a 59 16 03 03  |..{L...Q..EzY...|
+00000370  00 04 0e 00 00 00                                 |......|
+>>> Flow 3 (client to server)
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 50 00 00 00  00 00 00 00 00 00 00 00  |....P...........|
+00000040  00 00 00 00 00 76 d8 c4  58 a1 94 11 ab 19 4c 7b  |.....v..X.....L{|
+00000050  7c 34 d1 b6 8b 7f a2 96  41 e6 e9 98 d8 55 62 2b  ||4......A....Ub+|
+00000060  56 54 2a 65 25 f0 fa 15  ac cb b7 cc 3b 59 8b 99  |VT*e%.......;Y..|
+00000070  e9 be 9e fe 56 97 07 ae  39 38 a7 f4 f0 d0 e9 f5  |....V...98......|
+00000080  33 de 20 a6 04                                    |3. ..|
+>>> Flow 4 (server to client)
+00000000  14 03 03 00 01 01 16 03  03 00 50 8c 0f 2a be cd  |..........P..*..|
+00000010  30 f7 46 cf 58 5b 38 88  86 5e d1 33 b1 61 d6 95  |0.F.X[8..^.3.a..|
+00000020  13 c7 e7 f2 fb bc 37 e5  a3 db ac a7 74 49 00 89  |......7.....tI..|
+00000030  db 94 25 aa 00 b6 b2 34  0a dd 97 bf fa cf 33 6e  |..%....4......3n|
+00000040  6e f7 ab bf 70 a6 85 91  9b 4f f2 86 15 83 60 0d  |n...p....O....`.|
+00000050  79 9e 11 51 17 a6 6f 06  2f 98 bc                 |y..Q..o./..|
+>>> Flow 5 (client to server)
+00000000  17 03 03 00 40 00 00 00  00 00 00 00 00 00 00 00  |.... at ...........|
+00000010  00 00 00 00 00 c3 c9 23  7b bd 57 1a 29 5f ac f6  |.......#{.W.)_..|
+00000020  8d bb 90 bb 48 8a 9a 75  65 3b 5b 52 c0 ee 0e 24  |....H..ue;[R...$|
+00000030  43 f6 62 1f 1e 51 36 4e  3e a3 e4 96 d8 2b d8 a7  |C.b..Q6N>....+..|
+00000040  d0 18 97 d7 1e 15 03 03  00 40 00 00 00 00 00 00  |......... at ......|
+00000050  00 00 00 00 00 00 00 00  00 00 c0 c8 9f 7d df b1  |.............}..|
+00000060  78 72 b5 3d 0d 3e d9 88  38 c2 42 eb 2b 4d e0 b3  |xr.=.>..8.B.+M..|
+00000070  d7 69 19 31 57 16 7c 0a  bb 24 5b 9c 9b c2 4b b9  |.i.1W.|..$[...K.|
+00000080  55 ef ad 2c c1 eb 9b 59  06 5a                    |U..,...Y.Z|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-CHACHA20-POLY1305 b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-CHACHA20-POLY1305
new file mode 100644
index 0000000..64f999a
--- /dev/null
+++ b/src/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-CHACHA20-POLY1305
@@ -0,0 +1,81 @@
+>>> Flow 1 (client to server)
+00000000  16 03 01 00 67 01 00 00  63 03 03 00 00 00 00 00  |....g...c.......|
+00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 02 cc a8  |................|
+00000030  01 00 00 38 00 05 00 05  01 00 00 00 00 00 0a 00  |...8............|
+00000040  0a 00 08 00 1d 00 17 00  18 00 19 00 0b 00 02 01  |................|
+00000050  00 00 0d 00 0e 00 0c 04  01 04 03 05 01 05 03 02  |................|
+00000060  01 02 03 ff 01 00 01 00  00 12 00 00              |............|
+>>> Flow 2 (server to client)
+00000000  16 03 03 00 59 02 00 00  55 03 03 45 f5 61 06 a8  |....Y...U..E.a..|
+00000010  4e ce c0 32 d6 af fb 12  5e c8 6c 06 ac c9 d7 e4  |N..2....^.l.....|
+00000020  02 49 09 b9 42 ee ae fa  e4 52 18 20 12 3a 53 7d  |.I..B....R. .:S}|
+00000030  11 cf 13 13 a3 f8 42 c3  98 bb bc a6 10 3e f4 13  |......B......>..|
+00000040  a5 a2 fd ef aa b3 01 3c  cb 8a 3a 2c cc a8 00 00  |.......<..:,....|
+00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 57 53 06 53 e5 14 06  |........ WS.S...|
+000002d0  df 26 9d 3a 06 dc a9 d5  49 d3 3f 5f 7b c2 ab 77  |.&.:....I.?_{..w|
+000002e0  fd a1 fe 28 dc 54 36 06  22 04 01 00 80 da 23 f5  |...(.T6.".....#.|
+000002f0  19 de e8 d2 a9 79 b8 37  3d c0 8c ae f6 7c d5 d9  |.....y.7=....|..|
+00000300  87 ab 6b 3f 76 7c 5f 94  be 11 55 a3 78 66 1e e3  |..k?v|_...U.xf..|
+00000310  f3 11 3d 1a f7 02 26 a4  a6 cd 7c fe 87 0d 68 a1  |..=...&...|...h.|
+00000320  50 e8 7e 94 41 bd 5b 74  d0 6d 3b 6c ef ee 88 2d  |P.~.A.[t.m;l...-|
+00000330  60 0a a9 53 cf 1f f4 03  a3 54 e5 91 36 50 62 54  |`..S.....T..6PbT|
+00000340  5f e6 e5 36 63 58 ba 7b  bb 3a 79 59 58 08 a8 f2  |_..6cX.{.:yYX...|
+00000350  f5 1e 35 f8 f5 0f 7f 19  e7 7f 5f 56 e2 50 6d 8c  |..5......._V.Pm.|
+00000360  da 45 70 60 0d 58 32 94  e7 a0 f7 da 93 16 03 03  |.Ep`.X2.........|
+00000370  00 04 0e 00 00 00                                 |......|
+>>> Flow 3 (client to server)
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 20 9d 2f a6  b7 21 56 ad 38 a8 31 20  |.... ./..!V.8.1 |
+00000040  0b 2e dc 3f 8a 34 64 de  81 0e d3 a5 b1 c1 fc 05  |...?.4d.........|
+00000050  18 d9 3e 77 35                                    |..>w5|
+>>> Flow 4 (server to client)
+00000000  14 03 03 00 01 01 16 03  03 00 20 a8 82 60 8a ef  |.......... ..`..|
+00000010  31 55 42 e9 1d 33 0e d8  a9 b1 43 85 1c 04 7b 20  |1UB..3....C...{ |
+00000020  81 df 03 e9 fd c0 f7 32  b9 b3 31                 |.......2..1|
+>>> Flow 5 (client to server)
+00000000  17 03 03 00 16 ef 72 f7  1b 26 1a 47 99 f9 4c e7  |......r..&.G..L.|
+00000010  be 8e ab c5 8e ea 8c c6  60 6c 10 15 03 03 00 12  |........`l......|
+00000020  2c f4 39 e3 3a 74 a4 3c  72 63 77 e8 82 cf a9 e2  |,.9.:t.<rcw.....|
+00000030  2b 04                                             |+.|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-RSA-RC4 b/src/crypto/tls/testdata/Client-TLSv12-RSA-RC4
index ffdc328..74282d4 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-RSA-RC4
+++ b/src/crypto/tls/testdata/Client-TLSv12-RSA-RC4
@@ -1,79 +1,78 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 51 02 00 00  4d 03 03 79 8f 56 ac 75  |....Q...M..y.V.u|
-00000010  4f a9 fc 2c b9 53 82 a6  b4 c8 0d 4e 50 9a 9e aa  |O..,.S.....NP...|
-00000020  8d ed 21 21 91 5d a2 cc  99 1b 68 20 0c e7 35 50  |..!!.]....h ..5P|
-00000030  67 02 70 2a 45 0d 6c 4c  46 df 75 dc 5f 6e 2f 79  |g.p*E.lLF.u._n/y|
-00000040  03 26 da 45 53 25 50 23  c0 85 3b 8c 00 05 00 00  |.&.ES%P#..;.....|
-00000050  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000060  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000070  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000090  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-000000a0  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-000000b0  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000c0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000d0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000e0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000f0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-00000100  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-00000110  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000120  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000130  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000140  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000150  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000160  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000170  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000180  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000190  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-000001a0  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-000001b0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001c0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001d0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001e0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001f0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-00000200  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-00000210  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000220  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000230  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000240  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000250  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000260  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000270  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000280  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000290  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-000002a0  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-000002b0  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002c0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002d0  04 0e 00 00 00                                    |.....|
+00000000  16 03 03 00 51 02 00 00  4d 03 03 ac bf 85 b8 5f  |....Q...M......_|
+00000010  56 44 a0 c5 3b 20 77 71  af de 34 bc 79 a0 a4 a7  |VD..; wq..4.y...|
+00000020  fa 2e cf b5 ee c5 a7 a2  5e 11 48 20 05 89 5e a6  |........^.H ..^.|
+00000030  cd ad 91 e4 be c3 c3 6c  6a 0e 1d ab 27 03 5e 0f  |.......lj...'.^.|
+00000040  05 9d ef b0 63 8d 2d b6  29 08 66 e3 00 05 00 00  |....c.-.).f.....|
+00000050  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000060  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000070  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000080  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000090  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+000000a0  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+000000b0  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000c0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000d0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000e0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000f0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+00000100  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+00000110  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000120  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000130  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000140  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000150  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000160  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000170  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000180  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000190  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+000001a0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+000001b0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001c0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001d0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001e0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001f0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+00000200  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+00000210  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000220  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000230  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000240  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000250  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000260  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000270  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000280  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000290  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+000002a0  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+000002b0  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 73 bd 73 65 92  |...........s.se.|
-00000010  86 23 41 14 79 7f d5 c1  10 ce 94 4d ad 9c c3 a9  |.#A.y......M....|
-00000020  87 b5 32 52 f8 6b 11 93  2d 9b 98 0b 8b 1d c0 f6  |..2R.k..-.......|
-00000030  53 17 6d c7 9c 2e ae c9  6f cc 99 23 38 37 1a 10  |S.m.....o..#87..|
-00000040  fe 05 0b b5 55 0a 14 e9  60 7d 70 26 98 e2 54 d9  |....U...`}p&..T.|
-00000050  65 cf 2e f4 53 5f 1d aa  3a f6 33 7b eb 4c 0e b3  |e...S_..:.3{.L..|
-00000060  ff 5a db 36 2a 47 f3 df  f9 fc f5 31 78 83 aa 6b  |.Z.6*G.....1x..k|
-00000070  52 b7 ba 1a 96 bc fa c1  a1 a9 bb 2b f5 38 89 00  |R..........+.8..|
-00000080  4d e5 78 13 4e a4 38 46  42 dc 16 14 03 03 00 01  |M.x.N.8FB.......|
-00000090  01 16 03 03 00 24 72 bd  b1 13 05 73 26 c0 0b ec  |.....$r....s&...|
-000000a0  e6 39 08 6a 2d 87 00 51  58 9d e3 8d da be 60 98  |.9.j-..QX.....`.|
-000000b0  0a ee 0c 96 13 f4 e5 30  90 85                    |.......0..|
+00000000  16 03 03 00 86 10 00 00  82 00 80 b9 65 8d bf a7  |............e...|
+00000010  c8 4b 79 ce 6f cb 8b 13  1c ac b9 7d 66 5e e9 ba  |.Ky.o......}f^..|
+00000020  1d 71 4e a9 e9 34 ae f6  64 65 90 3b d8 16 52 a2  |.qN..4..de.;..R.|
+00000030  6f f4 cb 8a 13 74 a2 ee  b7 27 69 b4 41 c0 90 68  |o....t...'i.A..h|
+00000040  bc 02 69 e1 c6 48 4f 39  36 30 25 ca 4c 17 ce 83  |..i..HO960%.L...|
+00000050  9e 08 56 e3 05 49 93 9e  2e c4 fb e6 c8 01 f1 0f  |..V..I..........|
+00000060  c5 70 0f 08 83 48 e9 48  ef 6e 50 8b 05 7e e5 84  |.p...H.H.nP..~..|
+00000070  25 fa 55 c7 ae 31 02 27  00 ef 3f 98 86 20 12 89  |%.U..1.'..?.. ..|
+00000080  91 59 28 b4 f7 d7 af d2  69 61 35 14 03 03 00 01  |.Y(.....ia5.....|
+00000090  01 16 03 03 00 24 e1 ef  77 60 cf 7a 44 79 74 59  |.....$..w`.zDytY|
+000000a0  ff 81 72 b9 b5 f5 97 af  60 59 78 f5 01 49 2d bb  |..r.....`Yx..I-.|
+000000b0  4a ec 98 1f f5 31 f4 00  a2 f3                    |J....1....|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 d4 ad ab a0 01  |..........$.....|
-00000010  1b 87 9c aa c4 27 08 b5  8c 4a 7f fc 03 df a6 d6  |.....'...J......|
-00000020  f8 6c d1 61 7c d3 1f 6d  18 c3 8d 88 5c 7b cf     |.l.a|..m....\{.|
+00000000  14 03 03 00 01 01 16 03  03 00 24 52 fd a3 51 aa  |..........$R..Q.|
+00000010  ee 9d 4d be 8c 08 32 f6  f7 4a a5 26 26 6c b2 5a  |..M...2..J.&&l.Z|
+00000020  49 7f 31 7d 44 b1 83 67  19 4a e3 07 7d 59 34     |I.1}D..g.J..}Y4|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1a 33 a8 7a  61 46 09 7b 64 e6 aa f8  |.....3.zaF.{d...|
-00000010  8a 43 d3 a9 0c e9 2e c0  89 7c 72 fb 75 50 50 15  |.C.......|r.uPP.|
-00000020  03 03 00 16 2b b9 b5 eb  f8 bd 53 20 ea 67 bc 47  |....+.....S .g.G|
-00000030  83 cf c5 6e f9 4f 9e 12  f5 1a                    |...n.O....|
+00000000  17 03 03 00 1a 61 73 4d  86 b2 a1 36 b2 3e b0 1d  |.....asM...6.>..|
+00000010  6a b9 8a 8b 00 e0 3a d9  7e 23 c7 83 72 97 28 15  |j.....:.~#..r.(.|
+00000020  03 03 00 16 4a 8a 04 00  0a b2 75 80 20 ad 76 2a  |....J.....u. .v*|
+00000030  88 16 56 e6 4a a5 c0 ea  c7 0c                    |..V.J.....|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-RenegotiateOnce b/src/crypto/tls/testdata/Client-TLSv12-RenegotiateOnce
index 00d35f4..8a9ac36 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-RenegotiateOnce
+++ b/src/crypto/tls/testdata/Client-TLSv12-RenegotiateOnce
@@ -1,251 +1,231 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 fa 71 0e 3c 35  |....Y...U...q.<5|
-00000010  33 cc 51 25 19 cf fe c4  ef c8 2d ec 88 75 a9 1c  |3.Q%......-..u..|
-00000020  6a e4 f3 b4 3d fd 74 cc  e1 71 71 20 de 14 e5 21  |j...=.t..qq ...!|
-00000030  84 17 62 62 4f 44 e7 c2  d6 00 07 d2 63 f8 b0 32  |..bbOD......c..2|
-00000040  e0 12 d3 cb 69 1f d8 ed  5d 43 89 86 c0 2f 00 00  |....i...]C.../..|
+00000000  16 03 03 00 59 02 00 00  55 03 03 16 d2 11 2e d6  |....Y...U.......|
+00000010  62 0c 5e 5c 9b 1f d2 31  87 b3 43 3e cd 47 4f f1  |b.^\...1..C>.GO.|
+00000020  0b a9 d1 4f f1 2a 42 5d  35 e0 ce 20 f2 f3 45 4b  |...O.*B]5.. ..EK|
+00000030  98 2f 80 06 49 9a c3 4f  3f 70 0d e5 9a 2a 2e ff  |./..I..O?p...*..|
+00000040  34 1b 0e 30 2c 85 52 e1  84 8c 3c dc cc a8 00 00  |4..0,.R...<.....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
-00000060  03 02 71 0b 00 02 6d 00  02 6a 00 02 67 30 82 02  |..q...m..j..g0..|
-00000070  63 30 82 01 cc a0 03 02  01 02 02 09 00 a2 73 00  |c0............s.|
-00000080  0c 81 00 cb f3 30 0d 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000090  01 0b 05 00 30 2b 31 17  30 15 06 03 55 04 0a 13  |....0+1.0...U...|
-000000a0  0e 47 6f 6f 67 6c 65 20  54 45 53 54 49 4e 47 31  |.Google TESTING1|
-000000b0  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
-000000c0  74 30 1e 17 0d 31 35 30  31 30 31 30 30 30 30 30  |t0...15010100000|
-000000d0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
-000000e0  5a 30 26 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |Z0&1.0...U....Go|
-000000f0  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 0b 30 09  |ogle TESTING1.0.|
-00000100  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
-00000110  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
-00000120  81 89 02 81 81 00 af 87  88 f6 20 1b 95 65 6c 14  |.......... ..el.|
-00000130  ab 44 05 af 3b 45 14 e3  b7 6d fd 00 63 4d 95 7f  |.D..;E...m..cM..|
-00000140  fe 6a 62 35 86 c0 4a f9  18 7c f6 aa 25 5e 7a 64  |.jb5..J..|..%^zd|
-00000150  31 66 00 ba f4 8e 92 af  c7 6b d8 76 d4 f3 5f 41  |1f.......k.v.._A|
-00000160  cb 6e 56 15 97 1b 97 c1  3c 12 39 21 66 3d 2b 16  |.nV.....<.9!f=+.|
-00000170  d1 bc db 1c c0 a7 da b7  ca ad ba da cb d5 21 50  |..............!P|
-00000180  ec de 8d ab d1 6b 81 4b  89 02 f3 c4 be c1 6c 89  |.....k.K......l.|
-00000190  b1 44 84 bd 21 d1 04 7d  9d 16 4d f9 82 15 f6 ef  |.D..!..}..M.....|
-000001a0  fa d6 09 47 f2 fb 02 03  01 00 01 a3 81 93 30 81  |...G..........0.|
-000001b0  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
-000001c0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
-000001d0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
-000001e0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
-000001f0  06 03 55 1d 0e 04 12 04  10 12 50 8d 89 6f 1b d1  |..U.......P..o..|
-00000200  dc 54 4d 6e cb 69 5e 06  f4 30 1b 06 03 55 1d 23  |.TMn.i^..0...U.#|
-00000210  04 14 30 12 80 10 bf 3d  b6 a9 66 f2 b8 40 cf ea  |..0....=..f.. at ..|
-00000220  b4 03 78 48 1a 41 30 19  06 03 55 1d 11 04 12 30  |..xH.A0...U....0|
-00000230  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
-00000240  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
-00000250  03 81 81 00 92 7c af 91  55 12 18 96 59 31 a6 48  |.....|..U...Y1.H|
-00000260  40 d5 2d d5 ee bb 02 a0  f5 c2 1e 7c 9b b3 30 7d  |@.-........|..0}|
-00000270  3c dc 76 da 4f 3d c0 fa  ae 2d 33 24 6b 03 7b 1b  |<.v.O=...-3$k.{.|
-00000280  67 59 11 21 b5 11 bc 77  b9 d9 e0 6e a8 2d 2e 35  |gY.!...w...n.-.5|
-00000290  fa 64 5f 22 3e 63 10 6b  be ff 14 86 6d 0d f0 15  |.d_">c.k....m...|
-000002a0  31 a8 14 38 1e 3b 84 87  2c cb 98 ed 51 76 b9 b1  |1..8.;..,...Qv..|
-000002b0  4f dd db 9b 84 04 86 40  fa 51 dd ba b4 8d eb e3  |O...... at .Q......|
-000002c0  46 de 46 b9 4f 86 c7 f9  a4 c2 41 34 ac cc f6 ea  |F.F.O.....A4....|
-000002d0  b0 ab 39 18 16 03 03 00  cd 0c 00 00 c9 03 00 17  |..9.............|
-000002e0  41 04 71 a0 a9 f0 31 52  0b a2 5f 44 b1 48 a6 dc  |A.q...1R.._D.H..|
-000002f0  b7 b8 bb a3 59 13 06 46  73 37 b1 9d f6 5a 42 49  |....Y..Fs7...ZBI|
-00000300  a7 e4 3c 26 64 ed 26 41  f9 76 d5 88 ad b5 2f 12  |..<&d.&A.v..../.|
-00000310  ce 02 34 b8 85 36 ee cd  a1 dc d9 d7 4b ed d2 81  |..4..6......K...|
-00000320  82 1e 04 01 00 80 86 91  0e 05 48 de 2b 45 0a 9d  |..........H.+E..|
-00000330  72 33 44 73 98 f3 0e 0f  4c 0b aa c0 6b 02 34 83  |r3Ds....L...k.4.|
-00000340  0c e1 53 04 89 47 21 22  de 09 5e d0 b3 d9 8b 53  |..S..G!"..^....S|
-00000350  62 b0 bf c6 dd fe d3 ed  d6 2e ac a0 64 9d a4 07  |b...........d...|
-00000360  1f a9 d5 89 5f 62 7f e0  b1 9b e2 ef 3e 36 89 70  |...._b......>6.p|
-00000370  3e 7c 0a e7 8c cb c3 a8  e0 91 d9 bd 6e 3d be 0e  |>|..........n=..|
-00000380  a2 8c ab 46 1b 07 24 40  da a5 e3 0b b1 6a 9f 28  |...F..$@.....j.(|
-00000390  c7 4f e8 d0 a3 57 e1 5c  f2 34 07 aa 77 28 91 a0  |.O...W.\.4..w(..|
-000003a0  7e d6 36 2c c3 1a 16 03  03 00 04 0e 00 00 00     |~.6,...........|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 63 85 d4 43 2a d6 9f  |........ c..C*..|
+000002d0  2f 1f 0c 73 fe dc 96 1e  51 50 a5 0d 5e fd b0 5b  |/..s....QP..^..[|
+000002e0  a5 88 2a cd 1e bf c1 ec  4d 04 01 00 80 90 fc 48  |..*.....M......H|
+000002f0  53 eb 1b bc ec 39 be ae  60 4d c9 d1 49 eb 97 cf  |S....9..`M..I...|
+00000300  94 53 75 30 84 35 ff 0c  f6 ad 9f 24 98 70 2b d3  |.Su0.5.....$.p+.|
+00000310  45 0a 7f 25 ca a3 eb 37  5a a5 97 f1 78 8b b6 02  |E..%...7Z...x...|
+00000320  92 f9 12 9e 90 52 36 0e  40 15 76 de 37 02 c5 22  |.....R6. at .v.7.."|
+00000330  44 8f a4 fc f9 ac 88 88  ad 0c 9b f6 0e d6 9f f3  |D...............|
+00000340  68 cb f1 41 dd 2d c2 71  b6 43 36 12 d2 35 1c 9a  |h..A.-.q.C6..5..|
+00000350  a9 72 ea af a9 9e 77 19  16 86 be 3e ec 5f 5a 53  |.r....w....>._ZS|
+00000360  f8 38 27 7f 08 2a ae 68  e0 17 31 df 9b 16 03 03  |.8'..*.h..1.....|
+00000370  00 04 0e 00 00 00                                 |......|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 53 c5  |.....(........S.|
-00000060  60 30 29 1d 8a 38 57 f3  6d d1 f4 e1 ec 3e 79 d1  |`0)..8W.m....>y.|
-00000070  79 d3 b8 7f 4e 71 41 d6  72 fa c0 cd 53 92        |y...NqA.r...S.|
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 20 b1 f2 9a  ca 02 d3 ac 26 f5 32 03  |.... .......&.2.|
+00000040  4c b6 de cb f2 a3 11 19  eb c3 e0 e9 3b 8e 99 7d  |L...........;..}|
+00000050  c2 f3 d0 6d 4d                                    |...mM|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 86 be df d2 27  |..........(....'|
-00000010  8b 37 77 eb 0b e4 6e 38  5c 27 56 48 bb b5 f2 be  |.7w...n8\'VH....|
-00000020  43 e5 f7 32 d2 d3 a1 d7  4e 6a 3c 76 17 94 c1 b0  |C..2....Nj<v....|
-00000030  06 af 67                                          |..g|
+00000000  14 03 03 00 01 01 16 03  03 00 20 c5 d4 b1 e2 0f  |.......... .....|
+00000010  37 ad d5 c1 1a 6c 7f da  5f 25 e3 bd 20 1d 6e 58  |7....l.._%.. .nX|
+00000020  27 7a 07 55 76 11 76 72  1b 28 9e                 |'z.Uv.vr.(.|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 17 22 06  |..............".|
-00000010  f7 50 b5 6f 65 e0 dd f9  b6 bc 50 b7 91 c9 54 5c  |.P.oe.....P...T\|
-00000020  4e 2f cc                                          |N/.|
+00000000  17 03 03 00 16 3d 89 e4  a6 38 75 31 c2 08 3d 86  |.....=...8u1..=.|
+00000010  45 ed 8d c4 49 c4 da 54  3b 8f e3                 |E...I..T;..|
 >>> Flow 6 (server to client)
-00000000  16 03 03 00 1c 86 be df  d2 27 8b 37 78 c8 e7 d6  |.........'.7x...|
-00000010  4b e4 60 9e 4c b0 28 79  d9 7a 78 58 d8 27 76 18  |K.`.L.(y.zxX.'v.|
-00000020  a3                                                |.|
+00000000  16 03 03 00 14 aa 85 e4  64 46 2f 8e dc 89 3e ef  |........dF/...>.|
+00000010  6d 9e 1a af 53 3b a0 81  c2                       |m...S;...|
 >>> Flow 7 (client to server)
-00000000  16 03 03 00 a9 00 00 00  00 00 00 00 02 a7 17 56  |...............V|
-00000010  8e ea 2e fc 76 06 40 b2  fa 10 71 62 68 b9 14 e6  |....v. at ...qbh...|
-00000020  09 6d 63 86 d1 6b 87 3a  c4 84 15 77 68 f8 85 ec  |.mc..k.:...wh...|
-00000030  55 49 3c c5 c1 be 24 85  0c 38 4b 66 a8 5f 33 f9  |UI<...$..8Kf._3.|
-00000040  a3 e5 d1 36 fd 25 ba 9d  54 1f 4c df 66 09 a7 08  |...6.%..T.L.f...|
-00000050  8d 7c a4 7e d4 5d c2 11  77 7b 48 7a 32 f7 88 0a  |.|.~.]..w{Hz2...|
-00000060  51 5f 6a 26 e2 11 88 01  5b b6 8e 6a aa 18 79 85  |Q_j&....[..j..y.|
-00000070  6a 0e 31 1f 33 5e 34 fd  e9 1c 84 7c ea 6c 78 5d  |j.1.3^4....|.lx]|
-00000080  0e d2 df c0 8c 92 3d 48  fc 9e 47 18 2a a7 1e e3  |......=H..G.*...|
-00000090  9b 89 6f 30 d0 fd 0a cd  4c b9 d9 89 b6 72 53 54  |..o0....L....rST|
-000000a0  3e 02 c3 d0 68 b0 4e 40  06 86 cd 8e 87 53        |>...h.N at .....S|
+00000000  16 03 03 00 ad c3 12 d1  1a b2 88 da c1 0b 5a 32  |..............Z2|
+00000010  cf 05 35 53 ce 5d d8 42  cd 99 7e e7 9f 62 b8 35  |..5S.].B..~..b.5|
+00000020  9e f5 b5 a6 15 fd 82 26  9b 6a fe 3b 8e c0 43 27  |.......&.j.;..C'|
+00000030  1c 56 37 d1 6f d9 2c a6  a8 e8 b4 50 64 80 ae 5c  |.V7.o.,....Pd..\|
+00000040  ed eb a6 58 58 52 cf 32  de 1d be 80 69 63 38 a6  |...XXR.2....ic8.|
+00000050  12 4e 11 9b 50 aa 4b 10  f7 ad 6f 5b 08 c6 cc bd  |.N..P.K...o[....|
+00000060  94 42 64 90 c7 33 58 65  18 c5 a7 66 ce dd 83 8b  |.Bd..3Xe...f....|
+00000070  b0 15 8a 61 26 c7 eb 15  4b 6c 0b 15 45 33 2a 01  |...a&...Kl..E3*.|
+00000080  ea 13 5a 20 52 16 15 a0  70 8f 86 dc 28 50 bb e4  |..Z R...p...(P..|
+00000090  9d 01 f4 c9 7f 27 5a 54  3f 42 34 c9 5c 04 3f a3  |.....'ZT?B4.\.?.|
+000000a0  6a 5c a1 3f 03 7c fc 57  94 9b 3e 76 65 bf 78 40  |j\.?.|.W..>ve.x@|
+000000b0  b1 4f                                             |.O|
 >>> Flow 8 (server to client)
-00000000  16 03 03 00 89 86 be df  d2 27 8b 37 79 29 01 95  |.........'.7y)..|
-00000010  8c 13 0f f0 6e 8b 00 0c  1e 1a 36 73 b6 96 ad e1  |....n.....6s....|
-00000020  40 80 6d 68 f3 41 a9 a1  85 ca 86 81 73 6c fc 49  |@.mh.A......sl.I|
-00000030  b4 61 76 27 0f cd 22 5f  7e a7 c1 e3 13 f6 2e da  |.av'.."_~.......|
-00000040  1a 15 57 1a f1 b0 be 6d  55 44 78 95 62 82 ff 6e  |..W....mUDx.b..n|
-00000050  bb 70 ea 24 2c bf e2 14  48 3a 07 9a 30 3a a8 88  |.p.$,...H:..0:..|
-00000060  8b d6 b4 62 28 cb 30 94  54 f6 9c 15 34 e9 c4 d2  |...b(.0.T...4...|
-00000070  e3 42 cf 79 1f 96 34 f3  4c 9f f2 df 6e 70 4f cd  |.B.y..4.L...npO.|
-00000080  68 ae e2 2c d5 b7 f3 37  86 0a f5 7c af 32 16 03  |h..,...7...|.2..|
-00000090  03 02 89 86 be df d2 27  8b 37 7a 66 a9 20 cf 95  |.......'.7zf. ..|
-000000a0  d1 c9 3c c6 bc 53 16 01  e2 78 7e 2b 4d 45 20 d8  |..<..S...x~+ME .|
-000000b0  be da 93 9f 61 0b 34 25  f8 42 aa 0e b7 c5 a7 7a  |....a.4%.B.....z|
-000000c0  99 23 b5 a5 0b 39 37 48  2d 66 21 8a bd 41 11 e5  |.#...97H-f!..A..|
-000000d0  79 5f 5d c1 9b 4f c2 0c  fc a4 b9 ad 82 7e 7e 5b  |y_]..O.......~~[|
-000000e0  f6 95 46 eb b2 9e 9c 2d  58 7e c7 90 2c c4 7f 1c  |..F....-X~..,...|
-000000f0  cf 32 86 37 ec ab 60 71  ee 82 2b a2 95 61 8f 31  |.2.7..`q..+..a.1|
-00000100  99 2d c7 f4 5f 29 e8 b6  c3 f4 81 4f 2c b6 2c 67  |.-.._).....O,.,g|
-00000110  70 e5 cf d1 00 77 34 28  dc 61 cf e1 78 10 5e 64  |p....w4(.a..x.^d|
-00000120  17 f7 2b 3e 74 2c 8f 42  d5 a8 c2 4e 11 48 0f 0a  |..+>t,.B...N.H..|
-00000130  3f 8a ea 0f 37 f5 da 8f  7f 7c 61 b3 98 d9 69 80  |?...7....|a...i.|
-00000140  b5 1e c6 5c 01 ff e3 8e  45 a1 7a cb ee ea 12 d3  |...\....E.z.....|
-00000150  d7 56 2e 33 8c 55 a5 94  84 f7 a1 a4 fa f3 71 f4  |.V.3.U........q.|
-00000160  a3 15 f0 7e 44 c7 32 65  86 39 93 b7 df ab 6b 94  |...~D.2e.9....k.|
-00000170  df 6d d8 31 72 ba d9 7b  b6 8a 68 b1 c8 da e1 a0  |.m.1r..{..h.....|
-00000180  4f 0f 06 6a 52 78 6e a1  57 2f 2b 6b 10 5b c1 57  |O..jRxn.W/+k.[.W|
-00000190  d0 92 23 bf dc 95 f1 83  66 ce 6f ef c5 22 22 24  |..#.....f.o..""$|
-000001a0  80 bd 2f 38 ff de ec 86  8b ad 81 4e fe 31 65 54  |../8.......N.1eT|
-000001b0  19 94 ce 99 0f 6d 5b 1b  53 ba ad 65 a6 6a f6 27  |.....m[.S..e.j.'|
-000001c0  ba e0 b7 a9 8b 80 18 71  67 f7 6c 35 5f 69 c2 19  |.......qg.l5_i..|
-000001d0  08 27 03 45 5a 58 49 27  cf ec bf 18 e7 60 64 2b  |.'.EZXI'.....`d+|
-000001e0  47 9e 07 1a 49 ef 90 20  c7 f7 69 5c 46 92 ae 65  |G...I.. ..i\F..e|
-000001f0  fa 45 9f 3b a3 4e ed cb  d9 1f d9 26 18 1e bb 58  |.E.;.N.....&...X|
-00000200  16 cd a5 00 df 65 73 39  82 fd 98 29 de 45 8f 70  |.....es9...).E.p|
-00000210  56 e3 c6 0b 18 71 09 92  0e 69 4e b8 e7 23 4f 70  |V....q...iN..#Op|
-00000220  7a 89 06 c7 78 05 04 31  7f 77 5c 68 74 f0 45 76  |z...x..1.w\ht.Ev|
-00000230  e2 56 b2 de 34 e6 79 64  49 9a a8 3a b7 5b 4a d3  |.V..4.ydI..:.[J.|
-00000240  5e 6d 0b f3 fb 6d 0c 2f  61 d0 71 f4 0d ed 60 2f  |^m...m./a.q...`/|
-00000250  61 80 c9 9b b9 e5 89 f2  64 88 52 d6 d3 aa 72 6b  |a.......d.R...rk|
-00000260  66 18 ae e9 df 20 40 15  b5 73 ba ac 50 b1 27 99  |f.... @..s..P.'.|
-00000270  b3 17 97 56 0b 7d 25 8a  64 80 42 5c c8 b8 d5 98  |...V.}%.d.B\....|
-00000280  28 16 2b ce 45 65 3d fc  d8 c6 91 31 c2 d4 09 a3  |(.+.Ee=....1....|
-00000290  cf 92 85 63 36 cb e2 da  a3 66 fb 08 c9 bc 12 23  |...c6....f.....#|
-000002a0  c8 88 7d 46 22 98 40 01  bf fb 58 84 f2 8f ad 83  |..}F". at ...X.....|
-000002b0  ed 79 b4 a8 3d e5 92 b7  b8 e1 d0 50 aa be 22 9c  |.y..=......P..".|
-000002c0  9c cb dc bd 65 59 41 3e  6f 53 89 02 30 b1 88 ca  |....eYA>oS..0...|
-000002d0  06 6d 8e b2 a6 75 6a d8  5a 19 65 de 27 c3 bf 70  |.m...uj.Z.e.'..p|
-000002e0  49 64 13 2d 19 5d 7a ec  91 a7 f6 82 92 7d e3 7e  |Id.-.]z......}.~|
-000002f0  d6 65 5b d4 eb ed 58 d7  cd 41 a2 b9 d3 9e e4 a0  |.e[...X..A......|
-00000300  92 bf 88 4f 0e 59 74 66  86 db 72 11 18 ad 81 24  |...O.Ytf..r....$|
-00000310  6e 43 38 24 23 fb db af  92 d8 1a 2d 16 03 03 00  |nC8$#......-....|
-00000320  e5 86 be df d2 27 8b 37  7b ce 01 b6 78 47 7d 3a  |.....'.7{...xG}:|
-00000330  ad 2e 03 8e 78 03 61 da  55 0e d4 fa 87 9d 20 25  |....x.a.U..... %|
-00000340  73 1f 3b 87 7b 02 c1 a3  af ce d5 b9 9e 29 91 1b  |s.;.{........)..|
-00000350  58 13 c9 bc 96 95 88 f8  67 43 03 25 a3 be 5e a6  |X.......gC.%..^.|
-00000360  1d ee 6e 70 4c b5 66 48  3d 7d 1a 58 8e 10 c0 68  |..npL.fH=}.X...h|
-00000370  6b d8 f1 dd 83 c5 d3 c8  81 c5 6d 72 68 50 41 6f  |k.........mrhPAo|
-00000380  f6 20 13 f8 72 fa 82 9a  25 e4 07 10 df b7 39 90  |. ..r...%.....9.|
-00000390  6a d7 d2 d7 a1 1c 31 4e  b6 7c 00 bc 4d b1 a1 ff  |j.....1N.|..M...|
-000003a0  d0 ae 42 b1 2d 3e 8b c9  43 f4 fa fc d4 71 8f 74  |..B.->..C....q.t|
-000003b0  37 23 1b bb 34 4e b6 e4  fe f1 1b ea da 08 e4 12  |7#..4N..........|
-000003c0  fd 50 23 f9 8a 2d 92 eb  f5 2b fc b4 e1 35 87 74  |.P#..-...+...5.t|
-000003d0  44 79 0b df 6a 14 eb 20  17 ab 5b 12 a7 19 a4 4e  |Dy..j.. ..[....N|
-000003e0  94 70 93 57 2d bd c2 54  88 fb 19 b7 82 28 ab db  |.p.W-..T.....(..|
-000003f0  ca a9 19 5d 36 1b d6 fc  7d 41 2c 5b 76 ec 90 72  |...]6...}A,[v..r|
-00000400  47 5b c4 ae 59 a6 16 03  03 00 46 86 be df d2 27  |G[..Y.....F....'|
-00000410  8b 37 7c ed db 59 c6 0b  4e 52 c9 bc 7a 81 ed 20  |.7|..Y..NR..z.. |
-00000420  00 55 02 76 15 49 9b 0b  f2 81 c2 f7 25 51 61 9d  |.U.v.I......%Qa.|
-00000430  48 e3 d2 6f 08 ea 0c 9b  26 cc 3b 52 58 ef a0 1f  |H..o....&.;RX...|
-00000440  09 c3 ca e8 c2 6c 13 86  b1 94 04 f1 65 e2 de 4c  |.....l......e..L|
-00000450  7c                                                |||
+00000000  16 03 03 00 81 33 ef 78  c8 2d 94 4b e3 b8 ea eb  |.....3.x.-.K....|
+00000010  67 1e 6c 10 98 25 5f df  ce 46 4c 13 77 ec d1 b1  |g.l..%_..FL.w...|
+00000020  e9 e2 c9 b0 de 9c ce 40  d0 d9 6f a5 fb a6 69 1f  |....... at ..o...i.|
+00000030  9f 53 68 6c ab f8 f0 10  4a c9 43 f0 ad 61 59 01  |.Shl....J.C..aY.|
+00000040  b2 90 97 9e cf 62 64 a5  46 b2 27 2f 1e b8 33 24  |.....bd.F.'/..3$|
+00000050  ed 7e 6b 5a dd 45 4d 00  61 a3 7e 22 5e bc 02 af  |.~kZ.EM.a.~"^...|
+00000060  5a a0 73 fb c5 1c 0f 11  f6 70 5f cc 9e 1c fa 3c  |Z.s......p_....<|
+00000070  13 0d 8b 03 4c 3b d5 5a  02 7b 95 64 ae cb 2f 50  |....L;.Z.{.d../P|
+00000080  e7 e1 32 13 72 96 16 03  03 02 69 f0 60 6a b8 fb  |..2.r.....i.`j..|
+00000090  50 6e f9 f2 65 d0 73 90  f7 55 0d bc 3a 66 72 32  |Pn..e.s..U..:fr2|
+000000a0  b7 32 ad 1d de 18 04 90  55 70 2d b8 c9 3f 4b 2f  |.2......Up-..?K/|
+000000b0  37 98 1c 4e c1 78 c1 ed  1f e2 bf 50 78 40 04 10  |7..N.x.....Px at ..|
+000000c0  b8 55 48 29 26 b0 a4 4d  ea aa 45 65 b4 21 93 ed  |.UH)&..M..Ee.!..|
+000000d0  49 4c 1d d9 77 33 38 2e  14 92 b4 e3 06 ce fe 51  |IL..w38........Q|
+000000e0  6a 19 1c aa e9 a6 7d fa  45 86 66 1a 6e bb 01 01  |j.....}.E.f.n...|
+000000f0  82 86 89 86 81 ce 0a 93  1a b2 f1 90 71 7a 43 fa  |............qzC.|
+00000100  b1 03 24 75 a1 48 f8 ee  a0 b4 c0 18 ff 81 95 2a  |..$u.H.........*|
+00000110  aa 74 87 39 da 23 ba ab  33 6b 63 ee df 2b f1 d1  |.t.9.#..3kc..+..|
+00000120  1a 9a 4a 0d ef de 68 13  28 81 49 d5 c6 08 57 a9  |..J...h.(.I...W.|
+00000130  d7 5e 56 a4 ec 81 42 de  28 39 51 7d 3a 66 cf a7  |.^V...B.(9Q}:f..|
+00000140  f7 81 7a b2 a7 09 b3 24  a6 b0 a5 cc 96 24 30 b2  |..z....$.....$0.|
+00000150  5b 94 1b ef 70 dd 7f bc  63 2f 7b bc 80 70 9e 9f  |[...p...c/{..p..|
+00000160  01 c9 20 ab 35 53 7c 3b  d6 70 d9 1d 9a f6 e8 76  |.. .5S|;.p.....v|
+00000170  f5 46 f8 b1 10 46 a9 eb  da 7b 80 cc 74 18 f9 30  |.F...F...{..t..0|
+00000180  56 1a cb 4e 60 2a b3 9f  35 fe a9 b8 b8 76 02 a7  |V..N`*..5....v..|
+00000190  4e f9 43 c9 52 70 6a fd  9c 3e dd c4 3f 28 08 19  |N.C.Rpj..>..?(..|
+000001a0  28 ed f9 44 e3 d1 b9 53  7e b7 cd 1b e9 11 c8 9f  |(..D...S~.......|
+000001b0  35 ed ab e3 5e 26 e8 49  7a 13 5c 20 9a b7 a0 95  |5...^&.Iz.\ ....|
+000001c0  60 0f 54 68 5c a8 c9 1d  37 0b 9f f6 61 3b fe 4c  |`.Th\...7...a;.L|
+000001d0  dc 4f 11 98 0c 7a b7 32  0b 50 e2 cd a7 59 bf 05  |.O...z.2.P...Y..|
+000001e0  a2 8a 51 33 23 ab 99 49  23 97 42 3b 0f 1c 39 b1  |..Q3#..I#.B;..9.|
+000001f0  43 c4 01 aa f9 f8 54 d7  2c b4 ef 33 f3 05 13 d0  |C.....T.,..3....|
+00000200  8d 81 06 23 d3 38 cb 3a  6b 37 f0 4d 1f be ed 0c  |...#.8.:k7.M....|
+00000210  b7 58 00 3a bd 74 02 a4  f4 b4 fc fd b8 fa 89 15  |.X.:.t..........|
+00000220  01 46 49 52 47 f1 4c 94  ee de 00 a1 25 aa b4 9b  |.FIRG.L.....%...|
+00000230  f6 b4 23 a1 0d fd 00 5a  de 45 38 ee 69 17 6f c3  |..#....Z.E8.i.o.|
+00000240  0b ed c5 3b b1 7d b1 2c  a4 8f ed 30 44 9a 0b 51  |...;.}.,...0D..Q|
+00000250  34 12 cc 6a 09 e4 74 ec  11 94 4b ba ce 72 93 64  |4..j..t...K..r.d|
+00000260  07 c8 ff 78 6e 1a bd 5e  26 15 a7 e8 72 90 71 a9  |...xn..^&...r.q.|
+00000270  0a bb cf 25 40 1d 20 a7  d7 b3 46 4b 53 6c c2 50  |...%@. ...FKSl.P|
+00000280  c7 7b 58 e1 3c df 6d db  28 71 15 f9 84 b7 ad b0  |.{X.<.m.(q......|
+00000290  9f e9 7a 08 5d 85 7a dd  bc c0 62 2e 6a d0 63 6a  |..z.].z...b.j.cj|
+000002a0  e2 46 6b 80 68 cf e5 a7  9e 60 42 8a 17 54 9c ec  |.Fk.h....`B..T..|
+000002b0  80 9b 81 80 7e 6f 33 8c  d1 be 95 30 f2 a9 19 f8  |....~o3....0....|
+000002c0  36 2c 8e 89 c2 5a b4 04  2e 12 05 21 3b 4f 42 26  |6,...Z.....!;OB&|
+000002d0  d1 98 11 f4 17 c2 a3 06  54 37 31 8e ca 9b 07 62  |........T71....b|
+000002e0  79 95 b8 fd 49 aa 60 5b  03 7d 60 50 b6 2f 3b 0a  |y...I.`[.}`P./;.|
+000002f0  5d c2 9f 92 16 03 03 00  bc ba f6 73 85 34 20 c4  |]..........s.4 .|
+00000300  b3 a4 15 01 fe 37 b3 b4  57 a5 b5 26 0c 64 2b 3e  |.....7..W..&.d+>|
+00000310  07 d3 e4 59 a8 64 3f fd  15 24 24 70 61 77 9b 96  |...Y.d?..$$paw..|
+00000320  c6 4b 79 2e a8 a7 c4 ac  5e cd 6e 8f 30 e5 3f f8  |.Ky.....^.n.0.?.|
+00000330  08 22 cb de 5f 8c b8 dc  07 4b 79 ec 41 41 20 20  |.".._....Ky.AA  |
+00000340  02 f6 4e 98 a3 5e 38 e2  5a d9 4a 2d 2e 3b 29 13  |..N..^8.Z.J-.;).|
+00000350  26 dc 4e eb a5 5e a3 b6  6f 16 75 b3 9e 63 4e 8e  |&.N..^..o.u..cN.|
+00000360  00 c1 46 30 fc 25 f9 05  86 ed 00 87 f2 6b 5c 18  |..F0.%.......k\.|
+00000370  69 e5 5c 32 9e 15 d2 47  9e 0e d8 c1 7a 9d 45 7a  |i.\2...G....z.Ez|
+00000380  76 4a ef 8d b5 60 7d 4d  fa 99 8f c5 58 18 ad a2  |vJ...`}M....X...|
+00000390  93 c1 36 85 39 73 e1 7b  46 be 69 de 88 fa 68 8e  |..6.9s.{F.i...h.|
+000003a0  be d1 48 bc 7b 29 2a 21  ba 60 60 58 51 c2 03 66  |..H.{)*!.``XQ..f|
+000003b0  51 9a 4e 70 06 16 03 03  00 3a c5 ed 8d 5d b9 c0  |Q.Np.....:...]..|
+000003c0  a2 07 15 c3 ef 76 ff fb  ca f6 b6 4b ab a5 7a 80  |.....v.....K..z.|
+000003d0  a9 2e 43 d0 d2 f1 d9 96  61 ff 43 59 3d d1 82 57  |..C.....a.CY=..W|
+000003e0  68 d7 c8 3a 5f 86 4a 2e  00 8f 3d 0e 73 49 c6 4a  |h..:_.J...=.sI.J|
+000003f0  81 4e ec e2 16 03 03 00  14 d5 5f c3 d2 9c 13 36  |.N........_....6|
+00000400  cb 22 23 3d e4 03 5b b9  26 66 cf 79 7c           |."#=..[.&f.y||
 >>> Flow 9 (client to server)
-00000000  16 03 03 02 89 00 00 00  00 00 00 00 03 3c 0f 09  |.............<..|
-00000010  9e dc 39 b8 be ab ca 53  74 05 93 12 a4 e7 bb 56  |..9....St......V|
-00000020  9f e1 9f 2a 09 7d e1 74  89 ee b3 99 3c 91 c6 38  |...*.}.t....<..8|
-00000030  7e 0c 5e 2d 1f 7d bd cd  1a d1 16 ab af 94 08 c6  |~.^-.}..........|
-00000040  74 e3 16 12 0e 9b bc 91  95 6d 01 fd 10 00 12 c6  |t........m......|
-00000050  03 96 92 08 df 50 89 ba  5c 25 ce 31 d8 b1 84 8a  |.....P..\%.1....|
-00000060  7d 6c cf 7f e6 9a e4 08  17 cc b8 f2 c9 8f e8 4b  |}l.............K|
-00000070  ab 44 4f e9 63 8c 93 71  b1 70 4a f4 29 5f ef 45  |.DO.c..q.pJ.)_.E|
-00000080  68 e1 0e 31 a0 4c 96 8c  65 03 f3 48 24 48 d4 d7  |h..1.L..e..H$H..|
-00000090  93 d1 17 39 8d 97 e8 d8  59 08 4b 46 82 cf a3 99  |...9....Y.KF....|
-000000a0  55 36 65 a9 d8 df db d5  65 78 52 38 c2 2a 1e ec  |U6e.....exR8.*..|
-000000b0  65 6a f5 d5 4c 81 0c f6  e6 77 b2 68 d4 6c 32 05  |ej..L....w.h.l2.|
-000000c0  ef f4 ee 0b e1 83 d0 3a  cf a0 06 f2 cc 61 62 5e  |.......:.....ab^|
-000000d0  fa b4 19 c7 e2 99 c1 cf  02 a1 01 3d 6a e0 be 9f  |...........=j...|
-000000e0  82 cd e5 c8 ac e2 3e 6d  0f 60 a4 e9 9b ca cf c9  |......>m.`......|
-000000f0  c1 fe 2d ef 29 ed f9 c3  11 03 9f 76 66 71 ef 24  |..-.)......vfq.$|
-00000100  5f d3 29 aa 6a e1 0c b1  58 7a f3 df 92 e8 61 e2  |_.).j...Xz....a.|
-00000110  41 43 ad 9d 55 a0 b0 a3  20 8d 2c 8f 34 e6 ab d3  |AC..U... .,.4...|
-00000120  37 80 9e cb 27 91 69 0a  ba 33 05 a1 7f 4d 7f 63  |7...'.i..3...M.c|
-00000130  ed 6a c1 72 43 ec 6a 6c  ac b7 87 bb 81 6e 06 fa  |.j.rC.jl.....n..|
-00000140  68 7a c9 33 28 59 ed 74  87 a1 6a 24 06 02 c0 21  |hz.3(Y.t..j$...!|
-00000150  71 b0 27 f9 6e b3 7e 30  e9 e0 df c2 5d 63 2a dd  |q.'.n.~0....]c*.|
-00000160  9d e9 9c 4f 47 66 68 7e  e4 8c 87 b7 f0 a8 3d b8  |...OGfh~......=.|
-00000170  36 39 3e 4c 9f 55 e7 bb  c7 3e 34 36 54 19 41 33  |69>L.U...>46T.A3|
-00000180  61 e6 9a ae c6 91 1d fa  2d 8c 45 95 5f 95 36 79  |a.......-.E._.6y|
-00000190  e9 59 7e 81 cd 7e 9e 01  fe 85 eb c8 ed 4e 93 c6  |.Y~..~.......N..|
-000001a0  53 76 2d 5c 72 50 22 16  04 15 c2 cf 19 07 e6 73  |Sv-\rP"........s|
-000001b0  74 d0 7b bb 68 c3 29 39  bc ab 1b 4c c9 5a 36 73  |t.{.h.)9...L.Z6s|
-000001c0  55 47 7a c8 4a a7 45 fe  f3 a9 94 6e ea ea cc 7d  |UGz.J.E....n...}|
-000001d0  d1 de f4 82 4c 14 84 f0  58 09 56 25 83 7a 23 71  |....L...X.V%.z#q|
-000001e0  a1 63 e3 4e 13 78 68 41  a1 9a 55 ec 9e 37 ee c2  |.c.N.xhA..U..7..|
-000001f0  7d 3f 8f 91 00 30 f2 ca  7b 13 b7 e7 fe 85 c5 aa  |}?...0..{.......|
-00000200  5e e3 97 2c cb d5 13 1e  83 3d c9 2a b1 21 f1 58  |^..,.....=.*.!.X|
-00000210  7d 09 32 31 d6 fd 89 26  ff 72 3c f7 c4 fe 99 33  |}.21...&.r<....3|
-00000220  41 82 76 05 b9 14 b1 b0  3c 41 02 74 a8 1d dd 80  |A.v.....<A.t....|
-00000230  38 67 25 01 39 f7 36 fa  e4 1c 7d 2f c9 7e 21 0a  |8g%.9.6...}/.~!.|
-00000240  30 77 1e ff fc 8a 31 ac  ee 91 f0 2c b1 9a b7 5e  |0w....1....,...^|
-00000250  26 d0 7a 9d b4 9e 53 6b  dd a6 5e 7b f0 45 99 9b  |&.z...Sk..^{.E..|
-00000260  2b 69 90 d4 dd 1a d0 b5  13 90 11 ac 01 f0 2f 94  |+i............/.|
-00000270  5b 59 7e 7a 40 22 3a b0  d4 24 92 7d 94 bf 34 91  |[Y~z@":..$.}..4.|
-00000280  f6 b9 cc c9 e5 de d3 67  6d 83 97 ee 8f 48 16 03  |.......gm....H..|
-00000290  03 00 5e 00 00 00 00 00  00 00 04 dc 6f 41 98 23  |..^.........oA.#|
-000002a0  d7 70 80 24 74 46 c8 45  e1 2f 43 1d b8 66 4d 0a  |.p.$tF.E./C..fM.|
-000002b0  03 0e d6 01 8b 92 f7 76  c1 2c 32 6c 65 60 da ab  |.......v.,2le`..|
-000002c0  0b 12 6d 30 1c cf de e7  ec a7 12 f9 df 6c b4 42  |..m0.........l.B|
-000002d0  e7 d9 6e 6e f3 1c 10 ee  39 47 7f ec 7c ec 68 68  |..nn....9G..|.hh|
-000002e0  e8 b2 70 a2 67 61 e0 b3  68 b5 91 9f 1a e0 c5 af  |..p.ga..h.......|
-000002f0  e3 16 03 03 00 a0 00 00  00 00 00 00 00 05 6b 56  |..............kV|
-00000300  3d 5e 4e f1 2c 30 e2 91  24 5c b1 5f d3 7d 3e dc  |=^N.,0..$\._.}>.|
-00000310  ba 98 e1 9f 72 98 2b 0e  11 07 d1 ea 14 d5 73 25  |....r.+.......s%|
-00000320  d2 cf 8e bc a5 ea 93 a7  32 ab 94 83 1e ea c5 62  |........2......b|
-00000330  06 79 bb ab 4c a0 cf fb  51 3b 7b f0 11 5e ae 50  |.y..L...Q;{..^.P|
-00000340  23 cb ff 86 03 3d a5 66  b9 c4 35 c2 12 f2 98 85  |#....=.f..5.....|
-00000350  77 ba af 3b d5 dd f2 cd  58 09 29 26 08 cd 4a ed  |w..;....X.)&..J.|
-00000360  ac af 57 ab 27 1a 40 ef  10 57 d1 ad 06 34 be ed  |..W.'. at ..W...4..|
-00000370  fe 88 1d 09 4a 81 8a da  e7 ef fa 27 71 ab 2b 3f  |....J......'q.+?|
-00000380  21 91 5f 1a dc 50 a4 f0  58 bd aa af 75 4e 25 2a  |!._..P..X...uN%*|
-00000390  2c 55 e5 57 c6 ab 14 03  03 00 19 00 00 00 00 00  |,U.W............|
-000003a0  00 00 06 5d 8a 3e 8e 55  e4 9d c0 6a de 91 c6 96  |...].>.U...j....|
-000003b0  6e 17 54 a5 16 03 03 00  28 00 00 00 00 00 00 00  |n.T.....(.......|
-000003c0  00 d5 45 5d 11 af e2 b6  f1 a8 e5 ed 58 80 54 ce  |..E]........X.T.|
-000003d0  b3 db dc 97 b3 86 c0 83  f9 3b 7c b5 ad 21 f8 cf  |.........;|..!..|
-000003e0  9a                                                |.|
+00000000  16 03 03 02 69 15 0b 29  0e 27 a9 4b 52 4d 0a 77  |....i..).'.KRM.w|
+00000010  b8 3a 40 95 84 a7 7a 8d  b1 6b 90 61 94 3a e4 06  |.:@...z..k.a.:..|
+00000020  20 6f 88 40 8a 8c c2 4e  dc 3a 01 39 c2 11 5a 9b  | o. at ...N.:.9..Z.|
+00000030  28 92 bc 72 04 a3 60 c3  42 c0 b8 dd f3 41 40 be  |(..r..`.B....A at .|
+00000040  6d 51 5b b8 db 75 63 3d  4f 2a cf f5 04 3d 53 be  |mQ[..uc=O*...=S.|
+00000050  47 f1 ae be 0a 97 5d 2c  df 55 5d dd 9a f6 0e 40  |G.....],.U]....@|
+00000060  59 02 91 d8 55 c9 3b e6  84 9c 8d 40 af 29 77 59  |Y...U.;.... at .)wY|
+00000070  15 0c 83 dd ec c7 e2 16  85 d8 6e e7 4e c5 a5 b1  |..........n.N...|
+00000080  8b 4b 46 3e 62 7c ff 27  1b 37 b7 5c 05 32 30 fb  |.KF>b|.'.7.\.20.|
+00000090  c1 cc 1d 13 1f 09 db 57  6a 70 2b 9f a7 25 9b 75  |.......Wjp+..%.u|
+000000a0  d3 62 20 45 d1 2f 28 c0  d7 84 d6 2e b3 6d 4a c3  |.b E./(......mJ.|
+000000b0  46 0d 92 32 87 65 dd b8  98 68 1a 52 0a df be b3  |F..2.e...h.R....|
+000000c0  09 bc 63 bb a3 da f7 52  5a 81 53 9a e0 ff bb 06  |..c....RZ.S.....|
+000000d0  7f 81 f8 ea 02 bb 3b 96  7b 0f 84 a5 4d 17 3a 2a  |......;.{...M.:*|
+000000e0  20 e9 21 70 b2 ab 8a 55  31 4b 1b 60 52 7f a8 39  | .!p...U1K.`R..9|
+000000f0  5a 0f 0b 00 4e eb 01 0c  a6 d8 f0 30 2b a3 6f 7b  |Z...N......0+.o{|
+00000100  99 82 90 9e 4c c8 03 1c  0e 85 55 bc 2d 42 28 66  |....L.....U.-B(f|
+00000110  35 c3 1e 08 70 d0 45 05  5b 2e 00 fc 9a f1 44 0e  |5...p.E.[.....D.|
+00000120  cb 91 ce b8 0f 2a 9f 5a  18 a8 ca 38 ff 2a ab 11  |.....*.Z...8.*..|
+00000130  57 a5 03 2f 3e 92 21 77  df dc 85 e7 fd d4 7e d0  |W../>.!w......~.|
+00000140  d9 6e ef 99 66 5d a8 f5  9a d3 c3 0f 0c 98 cd fe  |.n..f]..........|
+00000150  5a 46 79 77 c9 28 fb 5e  3e c0 d5 b3 db 98 79 9d  |ZFyw.(.^>.....y.|
+00000160  d4 20 a5 ad 25 d8 3b 39  35 60 fd 21 e0 eb 86 be  |. ..%.;95`.!....|
+00000170  8f 65 72 a2 d3 91 4c 25  70 31 b1 02 29 17 da e0  |.er...L%p1..)...|
+00000180  9f 7d 4e 5f 1a 7b 93 09  4c 84 5b 40 f8 3c 98 36  |.}N_.{..L.[@.<.6|
+00000190  9b 14 43 db 43 11 0a e2  9a 8b 73 96 a3 7b 4d 67  |..C.C.....s..{Mg|
+000001a0  d7 35 a6 85 40 6d 45 0e  9d 47 43 96 b8 64 d4 d7  |.5.. at mE..GC..d..|
+000001b0  d1 28 c8 32 7e ab d5 11  ad b4 a7 9c c9 ab c5 96  |.(.2~...........|
+000001c0  72 69 1a db 42 06 8e 03  d0 70 f9 7a 75 56 53 49  |ri..B....p.zuVSI|
+000001d0  29 e1 60 16 86 99 da 9e  d6 c3 95 94 e3 e4 6c 9c  |).`...........l.|
+000001e0  4f d0 5d e7 a6 23 e1 49  a5 b8 3d 41 a4 e0 8c a2  |O.]..#.I..=A....|
+000001f0  f8 35 40 4d 12 f1 0b 70  06 f5 b5 29 f8 5d 74 73  |.5 at M...p...).]ts|
+00000200  32 35 11 7f 50 a3 22 5b  d6 db a5 a8 9f ca db 47  |25..P."[.......G|
+00000210  b9 a8 c7 fc 16 40 ae 94  6e 6c 40 30 7a d6 9c 89  |..... at ..nl@0z...|
+00000220  d7 e9 1b 6b 26 72 1f d7  c9 bc ce 6f 84 03 3d 65  |...k&r.....o..=e|
+00000230  34 f9 7b 32 54 e4 b4 72  8c e1 31 9e e5 13 50 2f  |4.{2T..r..1...P/|
+00000240  ea 16 27 15 cb ec 0f 1b  21 aa dd cb 25 74 b9 4d  |..'.....!...%t.M|
+00000250  36 c0 0d fe a4 99 2f 86  50 52 d0 83 e2 3f fa e7  |6...../.PR...?..|
+00000260  2d 24 b6 7a ca 7f 69 3e  7d 0b 61 df 29 3b 16 03  |-$.z..i>}.a.);..|
+00000270  03 00 35 8e 89 3d 7b 39  aa d2 21 01 6a 3d fe 4f  |..5..={9..!.j=.O|
+00000280  e2 d9 e6 6d 5d 1e d3 a5  1d 3f f8 8e fb 97 3d 06  |...m]....?....=.|
+00000290  9b 68 67 45 15 3b a1 e8  e8 39 77 1a 41 77 2b c5  |.hgE.;...9w.Aw+.|
+000002a0  8c fe bd 28 7a 85 eb 7a  16 03 03 00 98 f0 ed 3c  |...(z..z.......<|
+000002b0  37 3f 34 3b 35 e4 15 a8  f3 b4 b6 76 49 65 e8 26  |7?4;5......vIe.&|
+000002c0  93 4b cc b1 31 7a 4c e7  7d 80 63 60 65 9a ff 11  |.K..1zL.}.c`e...|
+000002d0  a8 c5 c4 4e c2 7a ca 95  cb 08 21 77 42 ce 70 1e  |...N.z....!wB.p.|
+000002e0  bf d9 b5 6d de dc 03 67  2e 11 b5 47 c1 c0 74 6b  |...m...g...G..tk|
+000002f0  b4 9d c4 de 8c d4 80 e4  99 92 31 68 09 85 00 34  |..........1h...4|
+00000300  43 cc 06 09 bc a8 6e 83  a0 fa df 6e a0 04 e9 37  |C.....n....n...7|
+00000310  b3 05 69 b9 f1 85 7f 48  27 73 d0 64 2c 33 48 1f  |..i....H's.d,3H.|
+00000320  f9 7c 0a 21 1f cb 0f 4c  c2 28 b0 3b 8c 9b 23 21  |.|.!...L.(.;..#!|
+00000330  f4 8c 69 b2 1d 55 35 20  b9 92 09 36 01 aa e1 e3  |..i..U5 ...6....|
+00000340  ee b7 3d 83 7b 14 03 03  00 11 40 bb bb 2e 3d 48  |..=.{..... at ...=H|
+00000350  9f fa c6 0c d8 4f 45 cb  11 b3 a5 16 03 03 00 20  |.....OE........ |
+00000360  39 b7 46 30 00 68 12 c5  f5 d7 a0 85 7f ce 49 70  |9.F0.h........Ip|
+00000370  05 83 64 26 7a 0c 43 fb  a9 4d bb 61 3a c3 8a 91  |..d&z.C..M.a:...|
 >>> Flow 10 (server to client)
-00000000  14 03 03 00 19 86 be df  d2 27 8b 37 7d 85 70 b7  |.........'.7}.p.|
-00000010  a7 98 89 36 01 b4 a8 6f  cb 14 0f dd ac 08 16 03  |...6...o........|
-00000020  03 00 28 75 41 a9 ef 1c  88 59 4e 84 15 29 a4 75  |..(uA....YN..).u|
-00000030  e6 66 01 3f a1 b7 ff 69  04 b6 08 99 c9 5e 57 60  |.f.?...i.....^W`|
-00000040  ea 76 21 94 06 e4 32 95  e1 4c d7 17 03 03 00 21  |.v!...2..L.....!|
-00000050  75 41 a9 ef 1c 88 59 4f  c8 5d 4e bd 42 52 ec 50  |uA....YO.]N.BR.P|
-00000060  2f 28 4f 87 da bc f0 df  a8 93 14 b7 6f a0 7f a2  |/(O.........o...|
-00000070  c5                                                |.|
+00000000  14 03 03 00 11 d1 d7 7e  3c 5f 01 cb f8 eb af d5  |.......~<_......|
+00000010  ba 09 32 68 4b cf 16 03  03 00 20 02 f1 23 45 32  |..2hK..... ..#E2|
+00000020  60 9b 49 db 2f 3a cb 5c  e4 f3 64 b1 cb ca 09 b0  |`.I./:.\..d.....|
+00000030  b3 34 a3 75 7d a5 a0 80  44 fd b7 17 03 03 00 19  |.4.u}...D.......|
+00000040  d7 c3 c4 33 e6 f2 73 d2  2c 0b 7e 0e 40 d3 8b f6  |...3..s.,.~. at ...|
+00000050  47 57 13 88 be 4b 12 43  57                       |GW...K.CW|
 >>> Flow 11 (client to server)
-00000000  15 03 03 00 1a 00 00 00  00 00 00 00 01 92 8a f2  |................|
-00000010  9a d1 c9 1e 68 15 2d 6b  9a a7 f8 21 78 87 89     |....h.-k...!x..|
+00000000  15 03 03 00 12 3a 90 85  e8 ce 53 d1 2a 3b d6 9a  |.....:....S.*;..|
+00000010  f3 61 c1 72 81 c0 03                              |.a.r...|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-RenegotiateTwice b/src/crypto/tls/testdata/Client-TLSv12-RenegotiateTwice
index 185dc65..c2f4e4a 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-RenegotiateTwice
+++ b/src/crypto/tls/testdata/Client-TLSv12-RenegotiateTwice
@@ -1,409 +1,376 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 bb b7 b5 ee 8b  |....Y...U.......|
-00000010  b7 92 40 96 01 65 93 09  a0 63 77 b3 35 74 0a 73  |.. at ..e...cw.5t.s|
-00000020  db e8 4a 9c d4 95 4b 2a  f9 43 1e 20 d6 5a ed d1  |..J...K*.C. .Z..|
-00000030  05 f0 61 aa 45 ae 0e 92  03 87 1b a6 0a 1a 83 a1  |..a.E...........|
-00000040  bd 4f c3 81 79 e8 56 10  5d 08 7b 6d c0 2f 00 00  |.O..y.V.].{m./..|
+00000000  16 03 03 00 59 02 00 00  55 03 03 30 5b 03 80 cf  |....Y...U..0[...|
+00000010  ad 5e 69 9b da 4a de 2f  07 f3 6c 42 d7 50 99 10  |.^i..J./..lB.P..|
+00000020  80 15 dc dd d2 ef 3d 20  b9 eb bd 20 51 63 fd 9d  |......= ... Qc..|
+00000030  3d b7 3e ea 4e 18 45 90  40 50 f2 f3 2b b8 00 42  |=.>.N.E. at P..+..B|
+00000040  bf 77 ae d1 ff 29 2d ca  d8 c2 4e c7 cc a8 00 00  |.w...)-...N.....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
-00000060  03 02 71 0b 00 02 6d 00  02 6a 00 02 67 30 82 02  |..q...m..j..g0..|
-00000070  63 30 82 01 cc a0 03 02  01 02 02 09 00 a2 73 00  |c0............s.|
-00000080  0c 81 00 cb f3 30 0d 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000090  01 0b 05 00 30 2b 31 17  30 15 06 03 55 04 0a 13  |....0+1.0...U...|
-000000a0  0e 47 6f 6f 67 6c 65 20  54 45 53 54 49 4e 47 31  |.Google TESTING1|
-000000b0  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
-000000c0  74 30 1e 17 0d 31 35 30  31 30 31 30 30 30 30 30  |t0...15010100000|
-000000d0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
-000000e0  5a 30 26 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |Z0&1.0...U....Go|
-000000f0  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 0b 30 09  |ogle TESTING1.0.|
-00000100  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
-00000110  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
-00000120  81 89 02 81 81 00 af 87  88 f6 20 1b 95 65 6c 14  |.......... ..el.|
-00000130  ab 44 05 af 3b 45 14 e3  b7 6d fd 00 63 4d 95 7f  |.D..;E...m..cM..|
-00000140  fe 6a 62 35 86 c0 4a f9  18 7c f6 aa 25 5e 7a 64  |.jb5..J..|..%^zd|
-00000150  31 66 00 ba f4 8e 92 af  c7 6b d8 76 d4 f3 5f 41  |1f.......k.v.._A|
-00000160  cb 6e 56 15 97 1b 97 c1  3c 12 39 21 66 3d 2b 16  |.nV.....<.9!f=+.|
-00000170  d1 bc db 1c c0 a7 da b7  ca ad ba da cb d5 21 50  |..............!P|
-00000180  ec de 8d ab d1 6b 81 4b  89 02 f3 c4 be c1 6c 89  |.....k.K......l.|
-00000190  b1 44 84 bd 21 d1 04 7d  9d 16 4d f9 82 15 f6 ef  |.D..!..}..M.....|
-000001a0  fa d6 09 47 f2 fb 02 03  01 00 01 a3 81 93 30 81  |...G..........0.|
-000001b0  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
-000001c0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
-000001d0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
-000001e0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
-000001f0  06 03 55 1d 0e 04 12 04  10 12 50 8d 89 6f 1b d1  |..U.......P..o..|
-00000200  dc 54 4d 6e cb 69 5e 06  f4 30 1b 06 03 55 1d 23  |.TMn.i^..0...U.#|
-00000210  04 14 30 12 80 10 bf 3d  b6 a9 66 f2 b8 40 cf ea  |..0....=..f.. at ..|
-00000220  b4 03 78 48 1a 41 30 19  06 03 55 1d 11 04 12 30  |..xH.A0...U....0|
-00000230  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
-00000240  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
-00000250  03 81 81 00 92 7c af 91  55 12 18 96 59 31 a6 48  |.....|..U...Y1.H|
-00000260  40 d5 2d d5 ee bb 02 a0  f5 c2 1e 7c 9b b3 30 7d  |@.-........|..0}|
-00000270  3c dc 76 da 4f 3d c0 fa  ae 2d 33 24 6b 03 7b 1b  |<.v.O=...-3$k.{.|
-00000280  67 59 11 21 b5 11 bc 77  b9 d9 e0 6e a8 2d 2e 35  |gY.!...w...n.-.5|
-00000290  fa 64 5f 22 3e 63 10 6b  be ff 14 86 6d 0d f0 15  |.d_">c.k....m...|
-000002a0  31 a8 14 38 1e 3b 84 87  2c cb 98 ed 51 76 b9 b1  |1..8.;..,...Qv..|
-000002b0  4f dd db 9b 84 04 86 40  fa 51 dd ba b4 8d eb e3  |O...... at .Q......|
-000002c0  46 de 46 b9 4f 86 c7 f9  a4 c2 41 34 ac cc f6 ea  |F.F.O.....A4....|
-000002d0  b0 ab 39 18 16 03 03 00  cd 0c 00 00 c9 03 00 17  |..9.............|
-000002e0  41 04 b5 fe 7d 68 cd 5a  b7 bf 61 10 81 dc 92 23  |A...}h.Z..a....#|
-000002f0  d0 82 13 fb 71 6f 39 48  f9 87 f8 f7 a0 3a cd 18  |....qo9H.....:..|
-00000300  85 d7 4d 66 88 9d 39 8d  6d 53 a1 a3 0d 00 b0 0f  |..Mf..9.mS......|
-00000310  14 64 1b 72 2d 89 5c 93  6f 3c ed c9 82 20 3d 2f  |.d.r-.\.o<... =/|
-00000320  d0 7f 04 01 00 80 42 24  14 6e cf 78 ea 30 90 1e  |......B$.n.x.0..|
-00000330  4e 99 bf ca 98 9c 2f 24  98 c2 a2 b3 f8 34 49 22  |N...../$.....4I"|
-00000340  35 16 11 03 79 3b a8 10  a3 fa d8 5e 17 9d f9 50  |5...y;.....^...P|
-00000350  0a 3b 0b b5 b2 0f 90 18  c1 f5 6f 89 84 04 e2 f0  |.;........o.....|
-00000360  b0 04 2f 3e 78 d3 de 31  9e 6e 3b b8 c7 f5 cc 4f  |../>x..1.n;....O|
-00000370  4e ad fe 76 d2 6d 23 31  94 56 b1 d8 df 0d 9b c5  |N..v.m#1.V......|
-00000380  f7 9e 9c a7 2a 47 e4 c8  20 08 fc 6c d5 29 cd 36  |....*G.. ..l.).6|
-00000390  88 83 c5 59 33 6d 1f 0b  f9 98 65 fa cb f7 89 2d  |...Y3m....e....-|
-000003a0  90 3a 40 8a 31 7e 16 03  03 00 04 0e 00 00 00     |.:@.1~.........|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 6b cd 37 6d a7 76 7a  |........ k.7m.vz|
+000002d0  04 0e 8c 93 db b0 62 67  14 65 26 5e 91 b3 8d 66  |......bg.e&^...f|
+000002e0  20 40 31 c1 84 3c ef d8  67 04 01 00 80 ce 3a f2  | @1..<..g.....:.|
+000002f0  16 01 b2 8a cd d6 1c b3  c4 46 a5 e8 1f 17 85 d9  |.........F......|
+00000300  5b 97 fb dd 43 65 52 82  e3 49 99 e8 49 d9 09 13  |[...CeR..I..I...|
+00000310  05 73 19 d0 d9 66 54 03  de 4b fa 43 2d f1 f8 98  |.s...fT..K.C-...|
+00000320  79 21 3b fa a9 ea 29 78  fa 87 59 8e 9b 2f f2 99  |y!;...)x..Y../..|
+00000330  14 85 21 9c 7e 59 5b 4b  2f e3 33 c4 7c 2c ac 35  |..!.~Y[K/.3.|,.5|
+00000340  4f 68 c8 a3 0b f5 43 7e  72 9a e6 4f 9c 10 4d 4a  |Oh....C~r..O..MJ|
+00000350  d4 b5 84 62 61 4e f4 0f  3f a5 b5 23 89 d9 33 e2  |...baN..?..#..3.|
+00000360  06 22 02 c5 fe db 27 fb  40 87 8c e7 6e 16 03 03  |."....'. at ...n...|
+00000370  00 04 0e 00 00 00                                 |......|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 fa e7  |.....(..........|
-00000060  ff 47 50 7a 68 0d 20 f6  9f 2a b5 bc f4 21 c1 72  |.GPzh. ..*...!.r|
-00000070  07 4c e5 07 2c 07 e5 1e  d7 fa 07 01 83 68        |.L..,........h|
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 20 5a b9 f7  81 81 7f 65 84 c9 87 40  |.... Z.....e...@|
+00000040  a4 66 07 85 38 3b 85 8d  ff c4 7e b7 f6 16 1d c1  |.f..8;....~.....|
+00000050  36 4f 53 1a bb                                    |6OS..|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 b7 93 18 5b 36  |..........(...[6|
-00000010  18 ce 97 17 75 40 15 17  1f 0e 0d 99 fd 66 fa 89  |....u at .......f..|
-00000020  db b7 97 95 a9 45 90 07  6e 82 0e 67 4f 01 58 ec  |.....E..n..gO.X.|
-00000030  94 d7 ad                                          |...|
+00000000  14 03 03 00 01 01 16 03  03 00 20 ff b2 f4 3b af  |.......... ...;.|
+00000010  ba 44 66 0b f9 31 09 18  df 9d d0 04 82 38 11 dd  |.Df..1.......8..|
+00000020  a7 ee 83 ef 03 51 21 08  f9 c4 8a                 |.....Q!....|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 21 2b 7b  |.............!+{|
-00000010  62 ac e4 37 d6 77 19 89  77 1c 6a ce 40 c1 9d 71  |b..7.w..w.j. at ..q|
-00000020  5a 23 f0                                          |Z#.|
+00000000  17 03 03 00 16 b6 45 4e  35 df 21 f4 c7 24 ba e6  |......EN5.!..$..|
+00000010  18 65 1c 75 ba 72 5d 5b  4a fd 78                 |.e.u.r][J.x|
 >>> Flow 6 (server to client)
-00000000  16 03 03 00 1c b7 93 18  5b 36 18 ce 98 4a 49 69  |........[6...JIi|
-00000010  f4 dd 35 f2 93 3b c6 4e  d5 25 51 34 38 23 ea 74  |..5..;.N.%Q48#.t|
-00000020  84                                                |.|
+00000000  16 03 03 00 14 1a ec 37  ab 36 23 80 76 1c 0b d0  |.......7.6#.v...|
+00000010  54 9a 8f 7e 7a c0 bd 09  9a                       |T..~z....|
 >>> Flow 7 (client to server)
-00000000  16 03 03 00 a9 00 00 00  00 00 00 00 02 65 09 7a  |.............e.z|
-00000010  d5 9b 32 0b cd 10 ea 2c  b6 d8 be ce db 3f a4 38  |..2....,.....?.8|
-00000020  a7 37 a3 95 ed 05 a7 c1  28 69 7a 13 50 07 ab 19  |.7......(iz.P...|
-00000030  52 d7 29 fe 49 80 f0 ef  ea 17 ac 20 f9 62 51 72  |R.).I...... .bQr|
-00000040  8d c0 17 62 03 cf bb 80  f8 6f 1b 74 f1 85 45 96  |...b.....o.t..E.|
-00000050  49 55 56 b0 7a dd 9b 5a  f1 3f 1a e7 96 cd 21 ec  |IUV.z..Z.?....!.|
-00000060  85 6f b9 9d 0f e3 f6 55  9b b8 a7 e1 77 ad 53 0b  |.o.....U....w.S.|
-00000070  98 90 ac 5d cf 31 0f 86  69 04 d8 e9 5e fc ea a8  |...].1..i...^...|
-00000080  a8 b7 a2 d8 0f ea 4f e5  ac f2 b2 c0 59 29 ba 53  |......O.....Y).S|
-00000090  af 1e 81 08 be 02 46 a7  b8 6f 2a b4 86 47 5d 8e  |......F..o*..G].|
-000000a0  72 a6 64 84 7e 76 31 9c  31 fb 59 b7 da 15        |r.d.~v1.1.Y...|
+00000000  16 03 03 00 ad 7a d4 bf  4d bf 48 0a 0f 2c d1 38  |.....z..M.H..,.8|
+00000010  9f 20 f7 c7 36 27 82 d9  5e f4 21 44 0e 75 46 ca  |. ..6'..^.!D.uF.|
+00000020  67 f8 87 6c 4f 1e c7 1e  5f 8d f2 88 58 1b 92 12  |g..lO..._...X...|
+00000030  e0 f6 d5 5c d7 4b 21 b9  8c 93 a8 bc a4 e6 e5 cb  |...\.K!.........|
+00000040  d0 33 eb d5 10 ad 53 50  58 e0 94 f0 b8 52 20 8c  |.3....SPX....R .|
+00000050  c2 69 7a c3 43 73 3f 2e  4a 42 2c c1 8d c1 ff 1e  |.iz.Cs?.JB,.....|
+00000060  57 ba 7d 99 9c a2 10 99  19 d6 72 38 96 ba 2b ff  |W.}.......r8..+.|
+00000070  7b a8 42 c0 c0 c4 d4 50  20 39 39 b8 18 23 a1 d3  |{.B....P 99..#..|
+00000080  38 d3 9d 41 81 c2 4b ba  4c 28 c0 14 22 71 df d9  |8..A..K.L(.."q..|
+00000090  e1 57 fc 9c 4f 0e 28 9a  13 c6 a1 e2 de f0 81 3b  |.W..O.(........;|
+000000a0  5f c1 7b 61 f5 fc 74 93  f1 06 7d 9b 67 56 45 a4  |_.{a..t...}.gVE.|
+000000b0  f8 83                                             |..|
 >>> Flow 8 (server to client)
-00000000  16 03 03 00 89 b7 93 18  5b 36 18 ce 99 4a 72 26  |........[6...Jr&|
-00000010  ab cb a4 70 60 0f 7a 02  62 28 f3 10 54 77 a7 33  |...p`.z.b(..Tw.3|
-00000020  32 a6 29 c8 8d 18 48 8f  9d 45 6e 7e 06 07 ca b3  |2.)...H..En~....|
-00000030  b6 45 eb ac f2 41 f1 d9  19 9e 30 1f c0 18 40 1c  |.E...A....0... at .|
-00000040  55 09 4d f2 23 75 2f 2f  c8 b7 46 63 05 d1 73 c0  |U.M.#u//..Fc..s.|
-00000050  02 71 de 5e 4a 84 92 3d  9a b9 68 62 31 91 7d 23  |.q.^J..=..hb1.}#|
-00000060  43 e3 4b 00 98 2e 01 12  f4 1f fa 4c aa 91 a0 ca  |C.K........L....|
-00000070  9c a0 d9 6b 7f 5c b3 f4  8d e2 3a 54 eb e9 82 44  |...k.\....:T...D|
-00000080  21 54 ac 85 86 39 b8 df  23 64 2a 0c 3e 1d 16 03  |!T...9..#d*.>...|
-00000090  03 02 89 b7 93 18 5b 36  18 ce 9a 1c ae 99 12 58  |......[6.......X|
-000000a0  12 fa ef da 77 04 7f b5  42 68 b1 59 64 50 92 2b  |....w...Bh.YdP.+|
-000000b0  a0 21 b7 b3 4c f8 c2 cc  75 5a d0 85 50 95 f4 1b  |.!..L...uZ..P...|
-000000c0  c9 b2 1f 53 94 4c fd 6d  18 ad 1a 0d 24 9f fb 4c  |...S.L.m....$..L|
-000000d0  19 13 5a 74 f2 e2 59 dd  1b d8 67 bc d9 d0 da ab  |..Zt..Y...g.....|
-000000e0  a7 7f 8e ca e0 09 28 59  18 8d a1 8a c9 c3 2e 76  |......(Y.......v|
-000000f0  b9 0d 2f 56 5f c4 77 07  17 ac 62 26 a1 91 50 ee  |../V_.w...b&..P.|
-00000100  60 45 aa a0 8a d9 1a 13  65 68 c8 cf ca 0c 50 3e  |`E......eh....P>|
-00000110  9f 39 62 02 12 ea b4 ed  e2 6c 0e 28 32 d7 fb ec  |.9b......l.(2...|
-00000120  fc 6d e4 0a 14 1d 88 00  a8 c0 57 1e be 78 fd 18  |.m........W..x..|
-00000130  6e 40 70 37 2e f5 3b 52  59 03 02 bf 27 18 c8 00  |n at p7..;RY...'...|
-00000140  58 8f 5e d8 a8 7c 4c 54  83 4a fe f3 dc f8 19 2a  |X.^..|LT.J.....*|
-00000150  00 ed 96 93 0e e4 45 58  8f 41 99 0d 93 f5 6c a4  |......EX.A....l.|
-00000160  4e 62 f2 4b 9a cb 69 30  5a 4b 36 45 f2 d2 c1 62  |Nb.K..i0ZK6E...b|
-00000170  f9 1c c4 c3 b2 94 b3 17  1a ed d8 57 ba b7 79 a1  |...........W..y.|
-00000180  a2 2e 5a 18 79 36 0b 54  ee 2c 2c 3b 62 96 5d e5  |..Z.y6.T.,,;b.].|
-00000190  3c 74 0e be 52 6f 06 7c  93 05 86 0f d6 1d d0 ee  |<t..Ro.|........|
-000001a0  f9 ac 67 50 a6 d3 36 f7  5f 0b 3f 44 3b fc 4b 79  |..gP..6._.?D;.Ky|
-000001b0  b7 29 04 6c 37 18 2a 04  bf f4 3e 1a 53 f3 93 e5  |.).l7.*...>.S...|
-000001c0  f2 b7 b1 4b ed 19 5a 2f  40 d1 f2 91 49 0b 8b f6  |...K..Z/@...I...|
-000001d0  21 0b 20 01 ce 0f a8 f1  44 3f 5e b1 89 1a 15 9f  |!. .....D?^.....|
-000001e0  4c c5 93 6b 68 93 ab 67  b5 1d 10 fa 22 53 e3 0f  |L..kh..g...."S..|
-000001f0  c7 63 d0 32 b7 52 c6 2e  b7 47 a4 1a b4 ab 35 a9  |.c.2.R...G....5.|
-00000200  b0 0e cd f6 8c e7 54 6c  77 7b 5c 6c c2 b3 02 89  |......Tlw{\l....|
-00000210  74 f7 b1 61 91 dc 01 3a  68 d9 81 78 21 95 b1 67  |t..a...:h..x!..g|
-00000220  36 2d 2a d6 c4 96 0d 7b  e0 44 83 cd 52 e4 05 36  |6-*....{.D..R..6|
-00000230  a4 1d 2a 24 e8 cc 76 d7  66 2f 32 ef 8f 70 ef 26  |..*$..v.f/2..p.&|
-00000240  90 73 2e e6 b4 53 91 13  5b 5e 15 51 15 56 e9 43  |.s...S..[^.Q.V.C|
-00000250  22 9a b6 55 3d 94 00 35  73 41 12 fc 8a 0b fd 89  |"..U=..5sA......|
-00000260  7c 00 14 0d b8 f6 76 d0  ac 33 1d e4 73 49 e9 a2  ||.....v..3..sI..|
-00000270  09 69 e1 f1 a7 92 48 ee  2e fc ef 13 09 7c a7 72  |.i....H......|.r|
-00000280  eb 4c 15 39 17 6e cd 71  c0 e9 48 06 43 09 19 39  |.L.9.n.q..H.C..9|
-00000290  72 b0 9c f8 0e 75 af a8  eb 25 96 36 75 68 16 8f  |r....u...%.6uh..|
-000002a0  e8 f6 66 56 66 63 b0 52  47 74 55 af c8 7a 07 dc  |..fVfc.RGtU..z..|
-000002b0  d0 8b bf 51 6e bc 77 fa  8a 03 43 0c 5a 47 fb c7  |...Qn.w...C.ZG..|
-000002c0  be b3 ef b5 ad 24 48 40  6c 4b 03 41 dd 7c 3e 6e  |.....$H at lK.A.|>n|
-000002d0  25 01 4b 45 ce ad d7 23  3a 6c 33 0b f1 7a 44 07  |%.KE...#:l3..zD.|
-000002e0  7e c8 bd 52 a5 a8 30 91  95 3e 4d 42 07 67 57 fb  |~..R..0..>MB.gW.|
-000002f0  c0 4a ed 9f 76 21 8e df  fb f6 a4 0a 08 1e 5b c6  |.J..v!........[.|
-00000300  3e a3 8c 47 a4 4d 41 2b  e6 8f 42 43 cd ef a8 f1  |>..G.MA+..BC....|
-00000310  88 f2 b3 46 eb 8a 24 a3  98 a2 d7 d2 16 03 03 00  |...F..$.........|
-00000320  e5 b7 93 18 5b 36 18 ce  9b 62 57 ae 22 62 34 88  |....[6...bW."b4.|
-00000330  41 e1 7e 2a 4a 07 b4 b8  aa 80 32 f5 93 4c 58 79  |A.~*J.....2..LXy|
-00000340  82 51 d4 b8 c8 5b d2 99  a3 18 43 aa c2 14 bf 65  |.Q...[....C....e|
-00000350  e8 90 8d 46 69 d5 fa 34  e4 1a 47 06 dc 1a ae e9  |...Fi..4..G.....|
-00000360  40 b2 2e 7e 5e 74 f7 72  4d a9 e2 b7 52 b4 bb dc  |@..~^t.rM...R...|
-00000370  06 e6 50 7e ef 42 8f 72  08 63 f9 ec 9e 13 36 0f  |..P~.B.r.c....6.|
-00000380  d4 95 72 2b ff a5 6d 4b  1b db d6 b3 25 50 f0 dd  |..r+..mK....%P..|
-00000390  e3 89 f5 c1 c0 3f aa 6c  f0 a7 30 5d 56 76 77 b6  |.....?.l..0]Vvw.|
-000003a0  24 8f 93 fd 49 8c 73 1e  f7 5c 5c 3a f3 0d 5e 89  |$...I.s..\\:..^.|
-000003b0  a4 bb 48 8a 82 ed 01 a6  2d eb b1 fe d2 6e 4e 88  |..H.....-....nN.|
-000003c0  1d 06 b6 f5 d8 41 86 40  fe 45 3e ef 35 9b 88 df  |.....A. at .E>.5...|
-000003d0  48 af e0 05 33 4e 13 15  8b b6 5a 8e 5c f8 2a 59  |H...3N....Z.\.*Y|
-000003e0  14 6d 4a 79 75 48 e4 9d  16 4f 6f 65 9c c3 40 1e  |.mJyuH...Ooe.. at .|
-000003f0  7c 72 60 ce b9 f8 61 3b  ff 34 81 94 01 aa b3 59  ||r`...a;.4.....Y|
-00000400  72 d2 1e 5f fe 7f 16 03  03 00 46 b7 93 18 5b 36  |r.._......F...[6|
-00000410  18 ce 9c c8 c9 b2 10 f1  39 bb f0 80 a9 0b 68 76  |........9.....hv|
-00000420  2b 60 0b c5 f3 eb 16 72  b5 4c c9 42 96 39 bf c1  |+`.....r.L.B.9..|
-00000430  94 87 f0 47 80 34 11 e2  1c 4c fc 26 d6 4b 00 49  |...G.4...L.&.K.I|
-00000440  ef 73 00 4e ab 61 d6 1f  89 2c 7e f2 5c ea 6b 5c  |.s.N.a...,~.\.k\|
-00000450  50                                                |P|
+00000000  16 03 03 00 81 ec 0e 7c  60 5b ed 0f 29 e6 cd 3a  |.......|`[..)..:|
+00000010  ca ff f7 68 3e 22 30 cf  43 b2 29 71 ca 3a 06 db  |...h>"0.C.)q.:..|
+00000020  e0 df bc 87 51 62 df 37  37 7a 9d b4 2d 35 ab bb  |....Qb.77z..-5..|
+00000030  d2 ed d1 50 e7 61 a4 27  18 43 23 74 4b 1d 14 46  |...P.a.'.C#tK..F|
+00000040  80 5c 10 99 10 47 b2 51  92 11 1f aa f1 5a c3 cf  |.\...G.Q.....Z..|
+00000050  f5 30 69 96 26 f6 0e 7c  e9 e1 7c 4b d5 2f 46 1c  |.0i.&..|..|K./F.|
+00000060  7d 18 a5 20 f1 ff 40 9e  c7 d5 d4 54 33 c4 99 9c  |}.. .. at ....T3...|
+00000070  26 95 d9 ca 82 fc e7 27  37 fc 33 9f f8 c7 cb 0a  |&......'7.3.....|
+00000080  9d 4e bc 66 ca fc 16 03  03 02 69 ab be 55 39 99  |.N.f......i..U9.|
+00000090  ac 66 a6 86 03 51 70 d8  fa 7d e7 89 09 fc eb b5  |.f...Qp..}......|
+000000a0  25 f6 95 0c 40 3d b8 ea  b6 78 41 db a4 f3 92 83  |%...@=...xA.....|
+000000b0  00 c8 8d 1f 19 0c ec ec  e5 60 07 d9 31 2d 3b 91  |.........`..1-;.|
+000000c0  ba ad b3 c1 fb b5 57 52  73 37 43 89 22 e0 45 7c  |......WRs7C.".E||
+000000d0  b0 da 1f 69 76 d3 af fc  ba f0 98 ec d2 7f be f0  |...iv...........|
+000000e0  a4 76 f0 d9 30 1b 78 22  bb 43 fd 45 64 07 e4 64  |.v..0.x".C.Ed..d|
+000000f0  c5 74 2f ed ba 23 8d 3a  5a ff 7f 35 de 25 53 0d  |.t/..#.:Z..5.%S.|
+00000100  ac ae 5b da b1 7d 86 fe  da c8 9e 79 58 6c 4c 9c  |..[..}.....yXlL.|
+00000110  6d f9 e9 1d 31 ff aa fb  8e 2e 98 f4 3a 67 33 9b  |m...1.......:g3.|
+00000120  a3 63 5e fc 74 f5 c5 28  76 30 e6 22 8a 85 79 56  |.c^.t..(v0."..yV|
+00000130  4e cf 94 38 92 61 09 22  00 95 d4 16 b4 e0 80 05  |N..8.a."........|
+00000140  28 35 30 6f 56 7d f8 b9  ed 49 72 b0 9f 47 9f 07  |(50oV}...Ir..G..|
+00000150  7f 1d b9 3b 6d ce c9 09  72 2f 65 b0 88 b4 ec 24  |...;m...r/e....$|
+00000160  29 8d 57 93 a7 51 85 32  26 4c 31 21 24 b1 ab 97  |).W..Q.2&L1!$...|
+00000170  c6 3c 38 44 d4 8f d0 3b  ea 62 39 48 90 ca c4 5c  |.<8D...;.b9H...\|
+00000180  9c 72 08 a4 3f d0 1e 3e  9f 23 02 0e 94 b4 14 cc  |.r..?..>.#......|
+00000190  96 bc 23 22 2f af c5 ed  81 da 49 ca 26 f4 55 6a  |..#"/.....I.&.Uj|
+000001a0  b3 24 3f 13 a5 f7 d6 82  70 04 37 63 dc 92 63 0f  |.$?.....p.7c..c.|
+000001b0  3d f7 4e 42 7c 5a 42 df  53 17 48 25 cb da 31 e1  |=.NB|ZB.S.H%..1.|
+000001c0  67 a7 22 8c ca db 2d 33  49 86 62 b5 ba 38 53 4c  |g."...-3I.b..8SL|
+000001d0  12 f3 c3 f6 b6 01 53 b1  11 43 e1 e8 ba 3f 68 7a  |......S..C...?hz|
+000001e0  9c 10 09 82 d1 90 cd 9e  52 2e 29 15 0b 13 c3 e3  |........R.).....|
+000001f0  34 ef 76 8a 0d f9 f6 17  76 57 c2 55 cb 7d 45 e0  |4.v.....vW.U.}E.|
+00000200  d5 e6 53 93 75 57 c2 ab  26 5a 13 02 05 7a 60 2b  |..S.uW..&Z...z`+|
+00000210  b6 92 1a 88 0c 34 59 81  40 42 70 d5 4e 7b d3 14  |.....4Y. at Bp.N{..|
+00000220  3c e6 70 81 90 ff 53 79  c5 f3 7c d9 02 6f 35 6b  |<.p...Sy..|..o5k|
+00000230  d4 81 ff 32 a2 71 28 a0  4b 22 75 9f 3b 50 c8 03  |...2.q(.K"u.;P..|
+00000240  22 e7 4f 1f c5 c5 99 67  f3 2c 97 5e 43 89 84 39  |".O....g.,.^C..9|
+00000250  04 c9 4c 1e e0 da 39 8f  78 8f 95 3a 64 74 d3 63  |..L...9.x..:dt.c|
+00000260  13 d4 14 9d 98 6c bb 27  81 1e 12 88 8c d4 11 1c  |.....l.'........|
+00000270  4a 71 2f 10 dc 5b 9a 8e  70 e6 bf 0b a2 50 4c d1  |Jq/..[..p....PL.|
+00000280  c4 29 b7 85 9e 0f 86 33  7c 38 e4 ae d4 53 5e 81  |.).....3|8...S^.|
+00000290  95 cf 6c 1a 15 7d af 20  53 21 f4 84 5c 46 5f 10  |..l..}. S!..\F_.|
+000002a0  1f 68 7d 9a 20 0d a8 1f  90 2f ae b8 c0 57 e0 4b  |.h}. ..../...W.K|
+000002b0  ad ec 9d ae 51 52 78 5d  3f bb de 54 54 d4 18 ff  |....QRx]?..TT...|
+000002c0  e6 b4 c9 52 f8 66 63 e3  bf 7a f3 5e c6 3f d9 27  |...R.fc..z.^.?.'|
+000002d0  71 07 e7 69 6d cb 60 24  55 4e f9 34 e7 5f a3 37  |q..im.`$UN.4._.7|
+000002e0  76 90 ec b5 eb ff 49 38  15 71 23 fd 77 30 80 c8  |v.....I8.q#.w0..|
+000002f0  ca 0e 38 3c 16 03 03 00  bc 89 27 c8 13 dd 05 8b  |..8<......'.....|
+00000300  46 97 5f 19 76 db 29 8d  b1 24 52 fe 7a 2e 2c 4d  |F._.v.)..$R.z.,M|
+00000310  e2 f9 59 89 48 82 f5 d7  87 af 99 4b 98 c5 7c 1d  |..Y.H......K..|.|
+00000320  1a e1 4d fd 0c 37 d0 d3  d8 e2 f8 0f 04 d5 15 21  |..M..7.........!|
+00000330  60 09 47 0c d6 bf 09 bf  55 c1 ae 33 11 35 94 29  |`.G.....U..3.5.)|
+00000340  80 96 3a 53 ae a8 29 c4  84 37 e2 80 f9 da 79 18  |..:S..)..7....y.|
+00000350  77 50 26 32 bf f6 b2 6d  3a 73 25 e7 cd 34 6d ea  |wP&2...m:s%..4m.|
+00000360  a0 92 40 7e f8 eb d0 82  06 b3 a1 f5 1c 8e b2 ef  |..@~............|
+00000370  b2 4f 0c bb 04 1b c7 cf  bc dd 42 19 00 3e 45 93  |.O........B..>E.|
+00000380  e6 72 57 ed eb d9 1d b3  71 b7 fe 84 03 57 e4 9c  |.rW.....q....W..|
+00000390  71 78 35 39 63 b1 cf 36  b9 5a d6 95 13 ee 4d 2f  |qx59c..6.Z....M/|
+000003a0  22 ef ed 2e fc 69 7c 12  c2 0e 32 ed 05 17 42 5c  |"....i|...2...B\|
+000003b0  a7 62 ab 6b 46 16 03 03  00 3a b9 af 3d 25 e4 6a  |.b.kF....:..=%.j|
+000003c0  a9 b5 b4 00 79 21 aa 3c  f3 56 50 bd 32 a4 a8 ab  |....y!.<.VP.2...|
+000003d0  8a 60 77 5d b7 3e 89 d3  60 a2 b8 5c a8 99 27 bb  |.`w].>..`..\..'.|
+000003e0  0e dd 93 af 3e 2b 66 8e  56 19 03 29 44 6a 63 a1  |....>+f.V..)Djc.|
+000003f0  c7 17 f3 1e 16 03 03 00  14 1c bd e4 7b c3 88 d9  |............{...|
+00000400  be ba c3 c3 fb b7 07 9d  f1 58 3e b0 61           |.........X>.a|
 >>> Flow 9 (client to server)
-00000000  16 03 03 02 89 00 00 00  00 00 00 00 03 c2 d4 9b  |................|
-00000010  19 c8 b6 76 fb ef e4 b2  f7 97 c7 80 f5 e2 b4 3c  |...v...........<|
-00000020  bd b7 b8 25 da 54 52 a7  f8 38 0d 48 c0 13 19 82  |...%.TR..8.H....|
-00000030  17 3c ff d2 c0 8f bd 76  5d 16 39 db a7 51 3f b1  |.<.....v].9..Q?.|
-00000040  72 b6 59 e4 8c 6c f5 33  de 78 15 8d 64 cf 55 c6  |r.Y..l.3.x..d.U.|
-00000050  47 e3 0b 30 06 e1 6c 2d  e1 e0 7a e1 0a da dd 0d  |G..0..l-..z.....|
-00000060  60 5b 06 28 a8 94 14 a3  cc 91 96 8d 2b 71 af ff  |`[.(........+q..|
-00000070  c2 32 e2 19 77 96 f3 5b  53 3a d3 29 51 c2 54 98  |.2..w..[S:.)Q.T.|
-00000080  f3 00 8e 9a fe ef bb ea  06 27 58 54 3c c8 67 dc  |.........'XT<.g.|
-00000090  f3 41 01 77 de 25 b4 54  53 67 64 41 b3 ae 2b c2  |.A.w.%.TSgdA..+.|
-000000a0  57 cd 74 14 3c 46 a7 70  ec a8 bc 0e 05 46 ce fc  |W.t.<F.p.....F..|
-000000b0  c8 54 4d 23 25 b9 e0 45  fa 1e 1b 2c f1 d0 da 66  |.TM#%..E...,...f|
-000000c0  3c 00 e5 b3 f5 f9 ff 64  75 82 f9 dd c2 3f 42 46  |<......du....?BF|
-000000d0  27 ca 72 a2 f7 6c 4e bf  98 05 e6 99 b5 7b 60 33  |'.r..lN......{`3|
-000000e0  99 e8 7a 7c 91 41 64 cd  96 60 f2 f6 c8 bd 4f 35  |..z|.Ad..`....O5|
-000000f0  5f 6f 43 11 b0 94 3c 98  bc 58 15 7e 52 01 ba cf  |_oC...<..X.~R...|
-00000100  71 f4 0a fb 85 0a 24 13  0c 4a 53 55 77 92 91 cd  |q.....$..JSUw...|
-00000110  ce 39 7e 07 2f 4f ba 47  ca bd 67 5b ce 5a 04 03  |.9~./O.G..g[.Z..|
-00000120  ff 86 0a 82 80 b9 42 b8  4c e3 ce 73 b2 4a 5a 4b  |......B.L..s.JZK|
-00000130  f5 f2 44 d8 e5 01 30 c8  2e ce 4f 62 2d 34 9c d6  |..D...0...Ob-4..|
-00000140  57 20 db 37 20 66 03 b6  4d a7 0f 75 30 d8 ad 2f  |W .7 f..M..u0../|
-00000150  63 f7 4e 24 ec 68 e0 a2  a9 b1 3d 68 e5 c1 8b d8  |c.N$.h....=h....|
-00000160  19 dd 40 33 c6 5c 57 3b  22 5a 9c 24 fe 2f 92 54  |.. at 3.\W;"Z.$./.T|
-00000170  0f e8 85 74 06 72 59 ab  1d b8 5d 31 91 ed 05 51  |...t.rY...]1...Q|
-00000180  61 c6 43 3d 81 f4 47 c3  80 17 4d 1b 08 c4 85 1b  |a.C=..G...M.....|
-00000190  b7 37 b0 cf 5c 73 5f 56  0f 5a b5 21 21 46 e3 df  |.7..\s_V.Z.!!F..|
-000001a0  e6 cb 9d ac ab 16 c0 b1  b8 2a 4a 5b a7 2d 7a 00  |.........*J[.-z.|
-000001b0  9f 9d 76 57 ab 20 ea 80  8a 7a ca 14 45 d7 4e 1b  |..vW. ...z..E.N.|
-000001c0  c8 7c b8 c6 82 fc 40 b2  b4 7d f1 74 7d b5 2a 90  |.|.... at ..}.t}.*.|
-000001d0  01 83 d4 d1 26 63 d7 39  69 b1 33 5f 7e 54 de f7  |....&c.9i.3_~T..|
-000001e0  08 3d 62 3b da 57 0d d4  48 99 9a 3e 99 e5 b0 6b  |.=b;.W..H..>...k|
-000001f0  25 45 38 36 aa 7a bb 81  7d 0b dd 1d 50 c4 17 68  |%E86.z..}...P..h|
-00000200  4b a7 f7 2f d8 cd 97 a6  ea 24 9b 34 69 9e 7d ad  |K../.....$.4i.}.|
-00000210  6a 17 23 d8 36 61 cf 85  74 47 18 5b fd cd 72 ac  |j.#.6a..tG.[..r.|
-00000220  c2 a2 b4 53 e3 5d 25 f7  bb b6 95 99 a0 e5 05 38  |...S.]%........8|
-00000230  0a 52 32 f6 7d a6 30 5c  11 6b 8a 7b af ec a2 9b  |.R2.}.0\.k.{....|
-00000240  b8 f1 85 6d a8 b7 79 61  42 60 4a 35 73 fb d5 2c  |...m..yaB`J5s..,|
-00000250  1f 84 5c d9 c9 23 10 e8  a4 2c 56 fd f4 22 1a 7a  |..\..#...,V..".z|
-00000260  f3 b2 c5 69 8b c9 d1 d5  45 c8 65 59 fc ab d9 d3  |...i....E.eY....|
-00000270  7d ab c3 fe bc da 6d a3  cd 0c 83 32 70 65 c7 7f  |}.....m....2pe..|
-00000280  8c 83 c8 97 3e 7f 89 fc  11 7d 1c a5 fd 99 16 03  |....>....}......|
-00000290  03 00 5e 00 00 00 00 00  00 00 04 64 60 91 c0 fd  |..^........d`...|
-000002a0  3a 96 5a ac 5a 13 a9 9a  41 eb a0 6d 51 98 ee a8  |:.Z.Z...A..mQ...|
-000002b0  4d ee 90 c9 3e a5 15 ac  f3 6a c8 56 f3 20 c3 10  |M...>....j.V. ..|
-000002c0  e3 3a d1 ea b0 7d a7 21  ae 2c b1 fa 5c b8 c1 fa  |.:...}.!.,..\...|
-000002d0  d7 97 6e ea fd 09 53 46  db aa e4 39 31 00 c2 bb  |..n...SF...91...|
-000002e0  ad 36 10 cd e9 cb 46 31  7b 66 ee ce 0c a8 f9 c2  |.6....F1{f......|
-000002f0  0a 16 03 03 00 a0 00 00  00 00 00 00 00 05 6d a0  |..............m.|
-00000300  03 60 12 bb 06 89 0c 03  ad f7 36 f3 5c e4 c1 65  |.`........6.\..e|
-00000310  b2 26 c9 f5 87 85 f9 8f  2d 05 43 35 32 d7 0a a0  |.&......-.C52...|
-00000320  e5 16 7a 94 62 15 ed cc  8e 9f e3 10 8d e7 83 a2  |..z.b...........|
-00000330  ea e4 07 49 c9 df 1d 2b  6f b8 0f 67 31 22 44 9b  |...I...+o..g1"D.|
-00000340  65 77 99 78 f9 3e 14 67  3a 90 e5 5a c2 b5 1b ee  |ew.x.>.g:..Z....|
-00000350  db 20 73 8d 85 22 4d 79  6e e9 17 d0 b1 03 58 f3  |. s.."Myn.....X.|
-00000360  cf 1b f5 03 9a 75 1f 7a  3b 49 ee 67 04 da c4 fc  |.....u.z;I.g....|
-00000370  7a 62 a9 ff 26 4f 71 b2  7e e9 c7 78 96 74 1e 63  |zb..&Oq.~..x.t.c|
-00000380  eb 2b 2f 18 1f 19 cf 1e  89 73 39 9e f6 02 3d 31  |.+/......s9...=1|
-00000390  50 65 1f 80 19 26 14 03  03 00 19 00 00 00 00 00  |Pe...&..........|
-000003a0  00 00 06 82 62 df fd 51  9e be 21 0b 22 b6 c1 6d  |....b..Q..!."..m|
-000003b0  5c 90 ea c5 16 03 03 00  28 00 00 00 00 00 00 00  |\.......(.......|
-000003c0  00 d6 5f a7 05 2b 99 cc  7d fb d7 38 5e e3 31 a7  |.._..+..}..8^.1.|
-000003d0  c9 1c bd 7b c7 89 d0 e5  b5 93 78 d1 63 57 d2 76  |...{......x.cW.v|
-000003e0  38                                                |8|
+00000000  16 03 03 02 69 d3 b2 83  ca 08 61 36 6f fa 66 de  |....i.....a6o.f.|
+00000010  5c 42 5f 35 17 f4 4c b3  5e 17 94 07 c5 2a 89 90  |\B_5..L.^....*..|
+00000020  b8 31 50 af 55 91 b1 28  bc 00 00 da 57 85 cc d5  |.1P.U..(....W...|
+00000030  a5 a7 ec ba 52 d5 1f ab  b5 4f a3 53 9c af 59 20  |....R....O.S..Y |
+00000040  be 13 f6 c5 70 b0 cc f7  59 69 d1 db 80 7f 60 29  |....p...Yi....`)|
+00000050  e1 e8 7a 1e 97 a6 e1 a2  11 70 4f b4 7b 53 7c 9a  |..z......pO.{S|.|
+00000060  dd fa 58 5d 12 a4 fb 3e  46 dd d3 4d ac e3 b6 73  |..X]...>F..M...s|
+00000070  05 42 03 f3 37 79 a0 10  34 ef 01 6f c6 27 52 4b  |.B..7y..4..o.'RK|
+00000080  75 4c b2 86 1b d6 ca 13  b4 31 41 db 93 2f a3 35  |uL.......1A../.5|
+00000090  e1 e9 bc 65 85 c8 1a e2  7b 6e 36 49 e8 ed 04 a9  |...e....{n6I....|
+000000a0  cb a9 70 32 c0 4d e5 f5  94 d8 af 7d 41 b6 dc e5  |..p2.M.....}A...|
+000000b0  fc ff db 9f 91 50 df c6  b2 4a 0c bb 86 4e 50 a5  |.....P...J...NP.|
+000000c0  2a 3b 69 e2 3d fa 10 eb  cc 48 21 9d 98 78 02 0b  |*;i.=....H!..x..|
+000000d0  e7 4d 83 63 9e e5 ad 2c  7b 46 d1 09 28 35 07 6c  |.M.c...,{F..(5.l|
+000000e0  12 c9 ae f0 4c 7c 00 aa  54 ba 7b e8 42 be e0 18  |....L|..T.{.B...|
+000000f0  f4 df a0 9d eb 8f a2 3a  d0 00 b6 e0 eb 90 65 38  |.......:......e8|
+00000100  b2 cc 21 87 57 b7 50 07  41 e2 5a 9b c8 2b ae 64  |..!.W.P.A.Z..+.d|
+00000110  55 6a d7 b5 0d 5d f0 ac  da 0b f0 80 75 8d 6a 0a  |Uj...]......u.j.|
+00000120  5b c5 2a 20 f2 ab 75 76  a3 33 f1 43 24 06 45 75  |[.* ..uv.3.C$.Eu|
+00000130  63 58 60 57 ca 7f d2 8f  e8 a5 b8 e3 72 37 39 bd  |cX`W........r79.|
+00000140  9c dd 10 b0 f8 b5 e8 46  4a 39 ac 94 69 08 51 29  |.......FJ9..i.Q)|
+00000150  27 2d 28 0f 15 8a 91 00  3b cc 99 77 ea 82 0d e1  |'-(.....;..w....|
+00000160  66 ed 34 da 6e 88 47 e5  f4 1b 28 28 cd 38 9f e5  |f.4.n.G...((.8..|
+00000170  1c 26 67 3d 83 d0 b1 88  a3 08 9c 2e 08 a5 b7 af  |.&g=............|
+00000180  15 28 d5 1e aa c9 58 17  48 85 f7 13 17 6f 55 c3  |.(....X.H....oU.|
+00000190  dc 9d 2e 7e 67 45 7a cb  80 a7 e5 77 86 96 36 4d  |...~gEz....w..6M|
+000001a0  f7 df 27 7d f4 5d 53 9b  be e6 b2 b9 44 b9 86 e9  |..'}.]S.....D...|
+000001b0  8f 33 26 4e 20 97 e4 34  66 58 6d d5 28 be e8 84  |.3&N ..4fXm.(...|
+000001c0  de 2f 19 f2 46 52 80 84  e9 ef 0e 6c 55 5c 43 8c  |./..FR.....lU\C.|
+000001d0  51 19 8b 22 30 b7 b3 eb  9f ed 35 a1 fe 09 aa 6a  |Q.."0.....5....j|
+000001e0  f8 b6 37 7e 20 4c e5 76  ae 10 4c dd 67 7c 07 e2  |..7~ L.v..L.g|..|
+000001f0  0c dc 78 77 a1 8e c1 d1  aa fa d3 36 8b 9c 74 ed  |..xw.......6..t.|
+00000200  e2 fe 84 98 40 95 f2 1a  a6 fd 77 cf 47 d3 c3 d6  |.... at .....w.G...|
+00000210  bc 38 45 a9 94 63 13 52  2d 7b 3f 3d 06 1e 0a 31  |.8E..c.R-{?=...1|
+00000220  cb 98 1d 18 fa a7 86 21  65 c7 58 dd 78 13 2d 4d  |.......!e.X.x.-M|
+00000230  76 57 9d 65 15 a3 2d be  7f c9 58 78 b7 89 3c d6  |vW.e..-...Xx..<.|
+00000240  dc 7e 5b c5 1b 93 78 04  7b ca ef 61 77 6c 27 6b  |.~[...x.{..awl'k|
+00000250  a5 30 67 43 64 a7 30 f0  dd 5c 63 92 76 9a e3 a2  |.0gCd.0..\c.v...|
+00000260  31 12 49 8e c4 7e 80 ce  1e ce 94 66 2f 6f 16 03  |1.I..~.....f/o..|
+00000270  03 00 35 39 86 a0 5a 88  07 66 5f 43 59 ed fb b5  |..59..Z..f_CY...|
+00000280  0c 0b ee ff 76 62 28 ea  62 7e 53 72 3d d9 26 9f  |....vb(.b~Sr=.&.|
+00000290  06 64 99 83 a5 3c 59 45  84 4d 79 86 1d b4 21 14  |.d...<YE.My...!.|
+000002a0  29 df f1 99 aa 6a 64 91  16 03 03 00 98 22 32 c2  |)....jd......"2.|
+000002b0  63 2c 82 b9 47 02 65 97  6b 14 fc 35 b8 20 ce 3c  |c,..G.e.k..5. .<|
+000002c0  1b 16 fd 51 55 e8 31 b5  58 e6 64 a8 2b b0 f2 ab  |...QU.1.X.d.+...|
+000002d0  31 7a cd 3e 32 43 64 eb  08 b0 52 d4 81 56 e3 04  |1z.>2Cd...R..V..|
+000002e0  4e d2 1a b8 f6 ae 64 d2  c0 05 9b 18 84 71 a7 ad  |N.....d......q..|
+000002f0  ea 49 f6 b5 ae 91 ad 66  6f cb fa 56 de 1e 40 22  |.I.....fo..V..@"|
+00000300  7f d3 44 c4 a7 bf b1 7b  61 e3 09 1f fe 3b 4b 7b  |..D....{a....;K{|
+00000310  5a 3f ae 4e c4 5c a8 ac  a4 c2 fc 0e 1f 12 4f 9b  |Z?.N.\........O.|
+00000320  f9 e0 a2 89 ab a0 bb e8  f9 97 5a 7c 8d e8 58 87  |..........Z|..X.|
+00000330  3f 4c 1c f3 ff 6e b9 a0  e6 f7 e4 c2 43 cc af 9e  |?L...n......C...|
+00000340  06 3a 1c ee ef 14 03 03  00 11 8b 6d c3 12 83 c4  |.:.........m....|
+00000350  2f e7 67 81 db 18 9a 33  e6 fa 82 16 03 03 00 20  |/.g....3....... |
+00000360  ef c9 6f 3f b7 9d 9d 2d  a0 05 b6 fd 74 8e ff de  |..o?...-....t...|
+00000370  cc 4b ca 4c 5b 4d 61 30  76 b5 f3 7a d1 46 d6 6a  |.K.L[Ma0v..z.F.j|
 >>> Flow 10 (server to client)
-00000000  14 03 03 00 19 b7 93 18  5b 36 18 ce 9d 68 22 e1  |........[6...h".|
-00000010  d0 06 aa e5 87 f8 49 bc  38 d7 b9 38 85 97 16 03  |......I.8..8....|
-00000020  03 00 28 24 71 bf 67 14  d6 5e 29 1b de e6 f4 e0  |..($q.g..^).....|
-00000030  33 76 dc 66 c6 95 c0 3a  15 49 99 09 2f cf 6b 6b  |3v.f...:.I../.kk|
-00000040  a1 8f 1a e4 af 8d 1e 7f  02 b1 87 17 03 03 00 21  |...............!|
-00000050  24 71 bf 67 14 d6 5e 2a  61 7b 98 dd e8 52 b0 1e  |$q.g..^*a{...R..|
-00000060  28 46 28 de e2 22 65 6c  66 85 3a 1d bb 9e 76 a2  |(F(.."elf.:...v.|
-00000070  55 16 03 03 00 1c 24 71  bf 67 14 d6 5e 2b bb 84  |U.....$q.g..^+..|
-00000080  6d f0 1c d0 46 89 bb b2  09 96 dd 95 53 bf ac d7  |m...F.......S...|
-00000090  80 f1                                             |..|
+00000000  14 03 03 00 11 2e d9 0e  1b 6b 4b 9a 60 96 e9 38  |.........kK.`..8|
+00000010  35 a3 72 3d c4 5b 16 03  03 00 20 62 1f 2b 02 e8  |5.r=.[.... b.+..|
+00000020  22 0a 89 a2 f6 60 8a da  f2 09 52 5a 92 12 a8 f6  |"....`....RZ....|
+00000030  c3 ee 25 17 6b 91 25 3e  2b 75 ba 17 03 03 00 19  |..%.k.%>+u......|
+00000040  b9 4e 6b c3 74 30 39 f9  3f a2 aa af 80 49 6b 8a  |.Nk.t09.?....Ik.|
+00000050  f2 a1 9b f5 c7 34 f7 1b  2f 16 03 03 00 14 2d 25  |.....4../.....-%|
+00000060  74 c2 c7 80 a8 5f 65 ff  65 b0 c2 50 c8 bc fe 8c  |t...._e.e..P....|
+00000070  d4 9e                                             |..|
 >>> Flow 11 (client to server)
-00000000  16 03 03 00 a9 00 00 00  00 00 00 00 01 61 12 ee  |.............a..|
-00000010  0a f2 5e e2 3d 3d 36 4c  14 10 20 aa 4d 8a 91 e4  |..^.==6L.. .M...|
-00000020  c2 b0 63 68 9e f5 71 b7  a4 ee 75 27 20 8c 2e 21  |..ch..q...u' ..!|
-00000030  f5 57 3d e9 9a 05 da 7b  a5 af 6a 17 10 8b eb 25  |.W=....{..j....%|
-00000040  8a 79 75 07 dc fe f5 7f  a5 e2 63 31 ee 55 ba c0  |.yu.......c1.U..|
-00000050  e6 3d de 03 36 2b 64 19  b1 1a b8 80 09 25 8c dd  |.=..6+d......%..|
-00000060  dd 59 c7 1d e7 40 20 ae  ca a9 b5 14 a7 57 f0 62  |.Y...@ ......W.b|
-00000070  71 88 a3 2c fc a4 50 dc  8b 85 22 20 38 c5 74 ea  |q..,..P..." 8.t.|
-00000080  ac 33 1d a3 c5 5c cc 10  62 fd c5 70 22 fa e3 73  |.3...\..b..p"..s|
-00000090  f3 bf 24 14 0d cb 7c 25  e4 74 6c fe c0 70 5e a0  |..$...|%.tl..p^.|
-000000a0  63 a7 e5 f2 6e d8 71 bd  7d b9 f0 b6 0b 70        |c...n.q.}....p|
+00000000  16 03 03 00 ad d0 25 9a  38 60 d3 94 8e b8 23 45  |......%.8`....#E|
+00000010  44 67 95 20 79 1a 27 5f  cf bb dc 72 8c 95 2a 02  |Dg. y.'_...r..*.|
+00000020  1d d9 80 c6 61 12 c2 5c  ae d5 62 a9 aa b0 4a d0  |....a..\..b...J.|
+00000030  13 ad 6d ae df c9 63 e2  6b 27 bb ca 88 c3 dc 8b  |..m...c.k'......|
+00000040  e9 bc 0b fb 32 d6 0f 99  b6 d1 03 7e c9 8d 72 ee  |....2......~..r.|
+00000050  09 7b 82 f1 79 11 a7 23  43 8b 77 b8 a9 bd 9e 67  |.{..y..#C.w....g|
+00000060  43 03 79 88 34 ab c2 6b  d1 2a cf c7 c9 b6 14 ee  |C.y.4..k.*......|
+00000070  ee 32 01 10 fb 85 dc 5a  04 cf 9a ea 8d 8c fc f8  |.2.....Z........|
+00000080  b3 b2 49 c0 93 37 93 09  24 1c 26 97 43 5f dd 09  |..I..7..$.&.C_..|
+00000090  7a 0d c0 6c bd 17 df 78  37 3d 23 6b 9d 27 d2 f7  |z..l...x7=#k.'..|
+000000a0  1c 2e 82 5c ba 95 1c 0f  b3 40 af 9f 2f 5e 42 40  |...\..... at ../^B@|
+000000b0  cc b9                                             |..|
 >>> Flow 12 (server to client)
-00000000  16 03 03 00 89 24 71 bf  67 14 d6 5e 2c 55 62 31  |.....$q.g..^,Ub1|
-00000010  5c a3 53 1a c3 2f 89 47  62 33 7e 24 cd ad a9 5b  |\.S../.Gb3~$...[|
-00000020  51 79 d8 08 08 ff 09 3c  41 c7 80 ed ec 5a 7a e4  |Qy.....<A....Zz.|
-00000030  71 e1 17 91 5e c1 80 58  35 c7 27 ca 62 74 cc d8  |q...^..X5.'.bt..|
-00000040  e8 35 86 97 bf 05 73 b9  3f ae 5b af 9a 14 88 4b  |.5....s.?.[....K|
-00000050  f9 6f a4 de 3d 45 c8 7b  0a b1 7a 81 3e 7c 02 b5  |.o..=E.{..z.>|..|
-00000060  e9 43 a5 64 88 59 f6 55  20 d1 09 39 cd 01 46 0f  |.C.d.Y.U ..9..F.|
-00000070  a2 06 f3 2b 45 14 b2 57  21 2c 2f a0 e5 db 02 99  |...+E..W!,/.....|
-00000080  e4 6b 1e 22 99 c9 ae 93  e4 67 89 d1 c6 6d 16 03  |.k.".....g...m..|
-00000090  03 02 89 24 71 bf 67 14  d6 5e 2d ce 6a 42 6b ce  |...$q.g..^-.jBk.|
-000000a0  07 4e ff 40 39 4b 00 c8  14 4c 76 e0 4d 09 41 c3  |.N. at 9K...Lv.M.A.|
-000000b0  41 3a ca ac 28 06 01 80  e4 b8 73 a2 fc ea 8d 92  |A:..(.....s.....|
-000000c0  44 0e 43 3e d8 cb 8a 0c  a0 c1 5e 88 6d 6d 80 be  |D.C>......^.mm..|
-000000d0  9c 9f cc 20 7c fa 6f e4  1a a1 39 c2 a8 7d 04 85  |... |.o...9..}..|
-000000e0  75 5d c4 d3 6f df d7 3a  9d 83 c3 74 aa 49 df 34  |u]..o..:...t.I.4|
-000000f0  e0 41 ad a3 80 80 c3 29  44 b9 5f a1 7b 67 89 30  |.A.....)D._.{g.0|
-00000100  04 b0 90 78 6b 82 fe ae  0c eb e1 5a 64 e2 6f de  |...xk......Zd.o.|
-00000110  de 12 db 4f 1f eb 1d a9  66 a1 62 11 ab 54 1f 5d  |...O....f.b..T.]|
-00000120  c2 ce 1e a8 b3 8b 29 08  76 13 a0 67 5b e6 1b 2c  |......).v..g[..,|
-00000130  bd 1b 42 80 a5 09 b0 03  28 df 77 6f a7 d5 2f 85  |..B.....(.wo../.|
-00000140  2b b1 69 81 5c a0 16 16  1c eb b4 61 f1 f7 70 55  |+.i.\......a..pU|
-00000150  ee 64 9d 8f 1a 0b af af  18 f5 da e6 32 ab b2 28  |.d..........2..(|
-00000160  0d a0 ea b4 44 3d a9 f7  1a 84 c1 8f 30 09 41 13  |....D=......0.A.|
-00000170  a3 34 79 a7 6f da 76 59  62 9f d6 82 0f 48 21 64  |.4y.o.vYb....H!d|
-00000180  11 49 53 cd 3a 44 5a dc  8b 97 8a 84 d2 f9 12 77  |.IS.:DZ........w|
-00000190  b3 5b b0 37 58 7a a3 5a  47 9d c7 e4 83 f5 0a 32  |.[.7Xz.ZG......2|
-000001a0  10 39 aa d6 7c 8e 44 eb  a9 fd 0f c0 6a 80 82 21  |.9..|.D.....j..!|
-000001b0  30 d1 36 31 73 38 c5 bd  16 99 71 b5 49 8e 7f df  |0.61s8....q.I...|
-000001c0  f9 64 7f ff 16 3b 68 7c  b5 7c 1f 41 19 36 dd ef  |.d...;h|.|.A.6..|
-000001d0  65 11 b9 91 c4 d4 40 eb  37 94 69 8b 3b 10 56 45  |e..... at .7.i.;.VE|
-000001e0  ee 56 a8 a7 3d 94 17 5c  fe f2 88 c7 fb 78 8e 51  |.V..=..\.....x.Q|
-000001f0  53 a8 bc b3 88 ee 75 42  1d 41 b8 c5 34 d5 9e bc  |S.....uB.A..4...|
-00000200  b4 b7 1c 97 8b 83 d6 3d  97 4b 43 7a 40 3d 63 6e  |.......=.KCz@=cn|
-00000210  cf 57 9a d3 71 6d 54 fe  38 ec 6f d7 c3 aa 1c a8  |.W..qmT.8.o.....|
-00000220  2b f6 34 96 cb 16 da 3e  2d 74 dd f6 1c 33 3c 4e  |+.4....>-t...3<N|
-00000230  25 d9 e3 c5 85 52 c3 ea  22 ea 86 16 84 31 05 a4  |%....R.."....1..|
-00000240  7d 41 00 bd 4a b3 79 93  18 1c a1 e4 78 1c 90 49  |}A..J.y.....x..I|
-00000250  b4 9f bc d3 2d d0 f9 46  da 13 7c f6 88 5e e1 b2  |....-..F..|..^..|
-00000260  5c 41 12 bf 2f 1f b4 c3  13 8c 2f a6 83 c5 86 ba  |\A../...../.....|
-00000270  20 42 21 57 e1 78 82 0e  4b 55 32 c1 f2 6e 4c a2  | B!W.x..KU2..nL.|
-00000280  a7 c7 63 b3 b5 30 49 9d  7a 51 5e 67 38 52 89 ee  |..c..0I.zQ^g8R..|
-00000290  51 16 34 5c f6 b1 04 30  7b f4 b0 f8 88 6c 9d bc  |Q.4\...0{....l..|
-000002a0  32 5d 8b 73 b0 df f6 a2  dd e7 62 94 d7 b7 68 92  |2].s......b...h.|
-000002b0  d6 a6 6a b2 53 75 d8 a7  43 1f 1e a2 c0 4e 6a 84  |..j.Su..C....Nj.|
-000002c0  e7 6d ae 81 82 dc 43 bd  8c 44 6a db ec 37 34 70  |.m....C..Dj..74p|
-000002d0  a0 e3 39 a1 17 d2 b7 53  bc 06 0e 33 3f 91 b3 a6  |..9....S...3?...|
-000002e0  0a d1 43 b0 94 54 bc b9  07 52 40 6e 49 99 ab 09  |..C..T...R at nI...|
-000002f0  3f dc 5d 5f c9 33 59 03  3f cf 7b 47 54 2d 05 4b  |?.]_.3Y.?.{GT-.K|
-00000300  c2 e6 81 f5 2f 58 5d 84  ad 9d 72 cc 3b 09 70 50  |..../X]...r.;.pP|
-00000310  75 f8 c8 b7 9a 3f b7 3e  aa 6a 75 5d 16 03 03 00  |u....?.>.ju]....|
-00000320  e5 24 71 bf 67 14 d6 5e  2e 0b f5 20 45 e5 51 07  |.$q.g..^... E.Q.|
-00000330  98 f0 75 3c 5c f3 16 88  ba e7 76 fe 10 18 41 38  |..u<\.....v...A8|
-00000340  d5 df 7f 8b d3 2e 1c 0a  4c 83 57 fc e5 63 35 68  |........L.W..c5h|
-00000350  6e 23 5b c3 0c 9d f9 ab  f8 3c 86 b6 ec 54 ec 52  |n#[......<...T.R|
-00000360  a4 45 cf 7b 31 a7 04 ef  5b 0b b1 11 50 8c 95 25  |.E.{1...[...P..%|
-00000370  9a 17 9b 4d 65 9c 0b d3  bb 0d 98 10 d9 34 52 7a  |...Me........4Rz|
-00000380  f8 1e 9e 78 cb 41 27 47  31 cb 25 42 90 e9 3c 02  |...x.A'G1.%B..<.|
-00000390  49 17 01 5f 06 d2 f4 58  35 75 d5 9d 54 65 15 0d  |I.._...X5u..Te..|
-000003a0  02 7e 94 fd c8 ac b8 c4  97 1c 9a 1c 9a 23 d5 d3  |.~...........#..|
-000003b0  44 c6 9a dd f9 b4 d1 48  e9 3d a0 5b d4 66 b3 d9  |D......H.=.[.f..|
-000003c0  11 0c d5 6d 0e 06 9c 00  90 30 d7 97 06 dc 0e e2  |...m.....0......|
-000003d0  59 51 7f b5 2e b8 f7 eb  be 66 56 fa 9d a4 92 db  |YQ.......fV.....|
-000003e0  82 3a d9 fc bd da c5 23  f6 2c 7b 36 2f a8 57 8e  |.:.....#.,{6/.W.|
-000003f0  c6 0a 48 50 e3 f4 e7 07  95 48 9b 45 a9 ba cb e0  |..HP.....H.E....|
-00000400  3e ee 10 f9 0e cc 16 03  03 00 46 24 71 bf 67 14  |>.........F$q.g.|
-00000410  d6 5e 2f 97 87 ae b8 b4  fb f1 67 2b e7 0f f4 be  |.^/.......g+....|
-00000420  24 0a f8 4a c0 42 4b 40  d3 ea e7 e0 f7 2a 9b 80  |$..J.BK at .....*..|
-00000430  bb 62 c0 2d d5 f8 52 19  49 d4 4c 45 1d c2 28 e7  |.b.-..R.I.LE..(.|
-00000440  8f fd b2 47 0e 22 d1 e1  b1 33 c1 26 6a fd 3f 9f  |...G."...3.&j.?.|
-00000450  d8                                                |.|
+00000000  16 03 03 00 81 39 20 22  4a e1 4e 21 2e 78 35 50  |.....9 "J.N!.x5P|
+00000010  8a d4 c4 c6 a9 20 74 8e  10 4b fd 31 4c ba c4 7f  |..... t..K.1L...|
+00000020  14 91 ab 4b 7a b2 7a 86  56 cc 61 ed 12 63 4a d5  |...Kz.z.V.a..cJ.|
+00000030  b5 c9 48 6e ae 10 71 fc  30 f5 e5 36 8b e2 7d 9d  |..Hn..q.0..6..}.|
+00000040  93 e2 34 cd 8e a0 72 c4  19 9b 62 eb 9b c4 5e c3  |..4...r...b...^.|
+00000050  df 22 d4 16 4d 88 b1 d5  e5 74 ac 9d 38 40 7d 1f  |."..M....t..8@}.|
+00000060  40 bd 86 e5 fc 19 88 bb  67 cf 3f 22 0f 39 1f 8f  |@.......g.?".9..|
+00000070  40 5c ee 29 48 00 c1 bf  6a f1 85 51 03 c0 e8 7a  |@\.)H...j..Q...z|
+00000080  2e f4 2a f9 b6 fb 16 03  03 02 69 59 b1 5a b9 98  |..*.......iY.Z..|
+00000090  86 77 af 76 a7 4c 69 0d  83 79 3c 9a b9 3c dd 49  |.w.v.Li..y<..<.I|
+000000a0  f2 0f ef d2 38 ac 31 b0  9b 5d 57 ed a7 ca f9 79  |....8.1..]W....y|
+000000b0  7c ef 58 f7 ca 7e 0c 60  1c 53 09 c5 06 dd b8 31  ||.X..~.`.S.....1|
+000000c0  09 88 af 50 9f c2 2a 2c  24 89 9e 57 ae c8 e6 49  |...P..*,$..W...I|
+000000d0  f4 6f 46 e9 dd 38 f1 52  e9 06 50 38 69 b3 e1 69  |.oF..8.R..P8i..i|
+000000e0  f5 33 28 54 39 43 05 b9  04 04 c2 d9 3d 34 da 48  |.3(T9C......=4.H|
+000000f0  99 7e 8a b2 59 d1 d3 20  da 34 c8 ab 91 f7 66 69  |.~..Y.. .4....fi|
+00000100  3a 4f fc 6d ee e6 ef f5  c0 b0 08 bf 59 c0 f8 e5  |:O.m........Y...|
+00000110  dc c6 dc c6 49 a7 d5 3c  22 0f e3 9f 39 0b 2a 09  |....I..<"...9.*.|
+00000120  7a 56 f8 90 33 05 06 09  21 6f 6a 53 0d 89 63 2a  |zV..3...!ojS..c*|
+00000130  76 28 19 33 73 e5 a0 2a  fa d2 82 bf 4c 43 84 f9  |v(.3s..*....LC..|
+00000140  e1 bb 7f 31 62 04 c5 26  ed 16 40 f5 6a 04 e0 c5  |...1b..&.. at .j...|
+00000150  4e fe bd e3 4c 57 e2 61  41 2f 9a 95 7f 8a e1 34  |N...LW.aA/.....4|
+00000160  64 f4 fc ce 13 e5 0c 68  c1 e5 29 df e7 b1 56 0b  |d......h..)...V.|
+00000170  42 6b fc d6 5d 63 0b 3c  0c ca ce 38 11 50 f2 64  |Bk..]c.<...8.P.d|
+00000180  1a 6e 16 da 3e 69 cf 82  ba a0 03 0f 9d 72 ed 10  |.n..>i.......r..|
+00000190  78 b2 54 c0 be 9c 16 fa  15 4b 88 db dd 85 dd 08  |x.T......K......|
+000001a0  bd f4 50 a0 50 01 77 7a  cf 20 c1 3e 50 55 5f bd  |..P.P.wz. .>PU_.|
+000001b0  4a ba b4 d5 6d 51 38 b2  6d 4f fc b5 af b9 92 ff  |J...mQ8.mO......|
+000001c0  c4 44 f1 0e db 4d 71 09  15 b3 c3 37 47 57 03 35  |.D...Mq....7GW.5|
+000001d0  95 de da 33 31 8c 60 bc  8a 97 2d f8 27 9b 4e dc  |...31.`...-.'.N.|
+000001e0  2a 6d aa 3e 4d eb 8f 97  b8 fa d4 ef f6 27 d9 da  |*m.>M........'..|
+000001f0  a6 fe 38 91 4b 96 ff 75  4b 71 52 9f 37 e4 d9 85  |..8.K..uKqR.7...|
+00000200  a8 d8 ac 21 e9 b2 c0 4f  c0 c0 e3 3a 9f ab e0 93  |...!...O...:....|
+00000210  dc 03 18 30 92 55 33 67  58 f3 47 f3 0a 95 bc 33  |...0.U3gX.G....3|
+00000220  70 73 e1 5b 9d 63 cf f7  c7 9b da 9e 5d 2e 7a 66  |ps.[.c......].zf|
+00000230  03 b1 b8 5c fa b9 f4 fb  4e 0b 38 9a 97 f0 c9 e5  |...\....N.8.....|
+00000240  ce 18 33 ea 66 1c 59 cd  41 3e af 71 7c bf 00 a0  |..3.f.Y.A>.q|...|
+00000250  d9 ee 20 d7 80 a9 5d 55  b8 f8 92 7b 7e 4f bc 66  |.. ...]U...{~O.f|
+00000260  92 98 6a c6 15 5b e3 1d  59 14 d9 d0 5a 30 c0 4d  |..j..[..Y...Z0.M|
+00000270  37 f5 d9 40 a4 f9 f4 ad  b6 cf 55 98 03 1e e7 13  |7.. at ......U.....|
+00000280  5a 23 49 69 36 fa ae 9d  c2 cb 90 16 cc 36 f4 41  |Z#Ii6........6.A|
+00000290  3a c7 56 9f 05 23 be 1d  3f 8f 90 41 09 6b 88 9a  |:.V..#..?..A.k..|
+000002a0  70 91 76 e1 6d f0 5c 86  de 77 a4 83 c7 d5 3c c4  |p.v.m.\..w....<.|
+000002b0  67 ff b1 a2 e2 f5 61 4f  3b 4d 38 5d c9 c2 8c 97  |g.....aO;M8]....|
+000002c0  e2 a7 c0 72 3b 5e 4c d9  0f 18 a8 b9 77 8d 31 8f  |...r;^L.....w.1.|
+000002d0  d9 73 ac 33 a6 7a b5 bd  5e 58 b4 51 22 be d8 d4  |.s.3.z..^X.Q"...|
+000002e0  f5 e1 bc 37 80 d3 cf 3b  58 be 3e ce 33 83 1c 46  |...7...;X.>.3..F|
+000002f0  8b f3 f3 56 16 03 03 00  bc 30 f0 e1 0b eb 80 fe  |...V.....0......|
+00000300  5f fb 94 c9 5a 04 08 82  0f 0b b5 9b f7 f6 f6 d3  |_...Z...........|
+00000310  7e 68 e7 e5 2e cf a0 56  e6 b7 70 0a 63 5e 53 42  |~h.....V..p.c^SB|
+00000320  7f 27 ec 82 a0 5d e3 e8  77 27 40 3c 6d 47 ce dd  |.'...]..w'@<mG..|
+00000330  89 6e ac 0c 48 ab 25 d1  8e 4e 4e b8 99 69 a5 94  |.n..H.%..NN..i..|
+00000340  8d 19 ab f9 82 6b 28 e3  b8 5f f7 75 a8 dd 1f b5  |.....k(.._.u....|
+00000350  0a ff d1 72 4f 82 9b 9e  8e b3 3f f9 73 d6 cd ea  |...rO.....?.s...|
+00000360  c8 5b 51 4e 7f 16 a5 4b  81 5e 9a d7 a3 f2 64 35  |.[QN...K.^....d5|
+00000370  fd 2e dc 11 07 10 04 63  80 77 eb 0d d2 ba 9f 7c  |.......c.w.....||
+00000380  ad 66 eb 0a 2e f7 f2 3d  d3 a4 04 7b 83 8f 84 94  |.f.....=...{....|
+00000390  93 8e 8c 74 2a b0 3a 3e  e1 33 92 f9 59 e5 0a 56  |...t*.:>.3..Y..V|
+000003a0  95 3a 8b dc 1f 24 6c 08  07 c0 f1 20 6d 96 43 71  |.:...$l.... m.Cq|
+000003b0  bf c6 92 81 e3 16 03 03  00 3a 91 c1 13 a0 26 c9  |.........:....&.|
+000003c0  12 6a 09 23 5b 8b e8 da  10 cc 4d 00 ed 9e a6 09  |.j.#[.....M.....|
+000003d0  d8 d4 c0 b7 f0 cd 5f 7a  6e 4d 31 21 f6 6a 22 e7  |......_znM1!.j".|
+000003e0  6e 25 11 94 4a f2 b1 f1  b2 c9 30 4b e4 cd f3 f5  |n%..J.....0K....|
+000003f0  ce 86 e7 5d 16 03 03 00  14 20 c5 91 f0 6e 21 b8  |...]..... ...n!.|
+00000400  e8 a1 bb cf 62 ed 4b 5a  fa 19 40 ea 54           |....b.KZ.. at .T|
 >>> Flow 13 (client to server)
-00000000  16 03 03 02 89 00 00 00  00 00 00 00 02 32 20 c7  |.............2 .|
-00000010  66 2d 3f e5 9b f9 e0 1c  7c 1f 3e 21 d9 51 af 9a  |f-?.....|.>!.Q..|
-00000020  60 65 99 c7 3e 0b 48 f2  a3 8f eb ea 75 da af 60  |`e..>.H.....u..`|
-00000030  2e 5b ac 7f 9f d1 1f 69  86 18 49 3b 18 a2 e5 c5  |.[.....i..I;....|
-00000040  d0 c7 fe 3e c6 15 3d 5d  04 4d aa 7e 28 e3 20 d3  |...>..=].M.~(. .|
-00000050  55 c2 ed 4f 61 5f cc f9  39 5f 7d 3a 0f f2 81 5d  |U..Oa_..9_}:...]|
-00000060  fd 4e 86 92 12 cd 2b b7  e6 46 49 b7 b8 5f 8f e5  |.N....+..FI.._..|
-00000070  b7 5e 64 2f 13 33 65 1c  c8 c4 38 bd 70 94 23 e9  |.^d/.3e...8.p.#.|
-00000080  b6 57 81 c8 23 d1 57 85  91 c5 bc 5b 33 55 eb f5  |.W..#.W....[3U..|
-00000090  2d b3 76 53 44 e2 e8 66  fe 42 de f8 6f 03 37 d4  |-.vSD..f.B..o.7.|
-000000a0  a0 a4 75 7a 03 7f 00 92  eb 45 2f b8 5d 01 d3 4b  |..uz.....E/.]..K|
-000000b0  e7 ca 2f 5b 3b 20 67 dc  32 2a 4c 06 1b 03 97 c1  |../[; g.2*L.....|
-000000c0  38 40 35 79 31 25 b0 fe  d8 f3 b7 ee 6c ad 62 3e  |8 at 5y1%......l.b>|
-000000d0  60 d6 96 6a 10 2b 14 8a  9e 72 f4 c9 63 6a 63 14  |`..j.+...r..cjc.|
-000000e0  d1 b0 e4 1f e9 3d 85 9d  ed 11 3f 85 eb fa ca 46  |.....=....?....F|
-000000f0  17 f8 45 d5 65 28 79 8d  63 8e d7 22 40 9f c7 25  |..E.e(y.c.."@..%|
-00000100  ae e0 72 9f 60 70 95 59  99 25 41 1a e6 e9 45 cb  |..r.`p.Y.%A...E.|
-00000110  3d 5a 2e 2d 4d c2 3c f2  3a 01 61 1f 96 d7 78 1a  |=Z.-M.<.:.a...x.|
-00000120  cd 14 bd 87 75 23 10 7f  67 e4 8e fa 0a 9d 5d e9  |....u#..g.....].|
-00000130  12 f8 c7 35 c1 37 4c a4  91 a1 a5 de 79 9a a7 9c  |...5.7L.....y...|
-00000140  ce d2 c9 72 a8 fa a3 27  24 8d 14 4e d7 11 f3 e9  |...r...'$..N....|
-00000150  07 4d 6d 47 92 4d e2 75  9a 71 d0 1e dd 09 61 0e  |.MmG.M.u.q....a.|
-00000160  16 36 84 3a b1 dd 9b f8  09 dd 73 78 ed f7 29 4e  |.6.:......sx..)N|
-00000170  a6 29 b0 31 54 72 ac 4b  7a 49 13 ba 9b ef b6 8b  |.).1Tr.KzI......|
-00000180  48 dd a1 a7 9d 25 0e b7  37 42 5f 70 27 a7 59 40  |H....%..7B_p'.Y@|
-00000190  fe 72 1a 23 3e 71 b7 56  ef ff 02 c0 c9 07 99 20  |.r.#>q.V....... |
-000001a0  19 d2 9e 65 a5 5e f1 15  d3 ec 6e bb b1 c4 bf c0  |...e.^....n.....|
-000001b0  f8 71 19 bc 77 30 93 72  33 eb 1b c0 62 07 5e ca  |.q..w0.r3...b.^.|
-000001c0  4a bf 89 5d 5d 44 23 fb  58 8e 71 b4 58 41 b9 97  |J..]]D#.X.q.XA..|
-000001d0  8b da b6 a0 b6 40 54 46  01 b9 47 79 21 bc 7c f3  |..... at TF..Gy!.|.|
-000001e0  4c 46 a3 92 ce d6 ec ac  3b 5d 6f 19 65 d1 b0 cd  |LF......;]o.e...|
-000001f0  19 cd 2e 9d 6e 7d d3 57  44 c2 dd c6 56 dd e6 2b  |....n}.WD...V..+|
-00000200  06 c6 f1 46 f1 ba ce e6  d9 c8 1e 03 5d b5 15 37  |...F........]..7|
-00000210  9d 8a d2 01 e7 28 33 30  a2 2b a3 42 d1 05 2f e9  |.....(30.+.B../.|
-00000220  7f 50 bf c8 7f 7b f8 c7  7e 12 3f 97 5e d5 1c 34  |.P...{..~.?.^..4|
-00000230  eb bf 2e c2 f0 6b 36 4e  09 c9 73 0e bb 3a cd f8  |.....k6N..s..:..|
-00000240  5f 2a 13 4d f2 92 b3 ae  4f dd 0e 82 a0 58 a9 be  |_*.M....O....X..|
-00000250  2f c1 20 5c 64 48 11 e3  66 18 22 4d ea aa 76 21  |/. \dH..f."M..v!|
-00000260  07 ac 5a f2 14 38 a7 d8  9a 58 f8 92 62 77 3c 59  |..Z..8...X..bw<Y|
-00000270  1a 31 4e 3f 56 55 2b 9f  87 96 9c 7e c5 f0 10 fa  |.1N?VU+....~....|
-00000280  90 a1 0b 9e e4 66 74 99  80 da 58 85 3d bd 16 03  |.....ft...X.=...|
-00000290  03 00 5e 00 00 00 00 00  00 00 03 42 d9 1d 19 27  |..^........B...'|
-000002a0  98 c0 29 9e bc 35 99 e9  e9 de f5 7c b7 2f ce a1  |..)..5.....|./..|
-000002b0  48 fe a9 79 26 c3 f1 74  63 73 3b 8d b7 4c 47 11  |H..y&..tcs;..LG.|
-000002c0  7c ea 6d 09 4c 1c 10 1d  c9 b4 63 d4 5e c4 f1 34  ||.m.L.....c.^..4|
-000002d0  94 63 1c 04 a1 5f d0 65  7c b6 dd 2b a3 1c 1b 5f  |.c..._.e|..+..._|
-000002e0  5c d6 dc 7f e7 df c4 75  ad df cc ae 71 47 64 cc  |\......u....qGd.|
-000002f0  96 16 03 03 00 a0 00 00  00 00 00 00 00 04 61 37  |..............a7|
-00000300  a3 98 54 d1 7c 5d 14 b9  04 72 6e 02 ab 1a 15 2c  |..T.|]...rn....,|
-00000310  93 07 15 ab 56 b1 ac d5  75 75 2e 25 ae 5e 3f fa  |....V...uu.%.^?.|
-00000320  d0 20 ff 9d e0 ef fd 25  ed 4d 60 56 c7 33 07 d0  |. .....%.M`V.3..|
-00000330  57 09 e4 12 bd aa f0 d2  cc de 0d 45 23 ab b6 67  |W..........E#..g|
-00000340  ea d3 bc e1 4d 3a 75 9f  2d bb 53 b4 70 67 ce 63  |....M:u.-.S.pg.c|
-00000350  83 29 fa 27 2b db ea a3  19 be 79 77 cd 75 fb bf  |.).'+.....yw.u..|
-00000360  c1 27 86 a6 a9 27 06 49  e1 77 13 0d e4 78 0c 07  |.'...'.I.w...x..|
-00000370  d4 1c af 76 f4 7b 05 04  5f 0f ec 66 f9 03 3e 81  |...v.{.._..f..>.|
-00000380  41 be 24 5f 43 2a 99 56  06 a9 d7 be ca 62 46 a2  |A.$_C*.V.....bF.|
-00000390  ba e1 a6 8b 1b 0a 14 03  03 00 19 00 00 00 00 00  |................|
-000003a0  00 00 05 f9 8f d4 80 bf  ed b3 38 3a 12 d9 91 b6  |..........8:....|
-000003b0  cf 87 1a 1b 16 03 03 00  28 00 00 00 00 00 00 00  |........(.......|
-000003c0  00 fb 80 da 9a 59 82 9d  d2 35 57 57 dd 76 a1 b1  |.....Y...5WW.v..|
-000003d0  4a dc a5 cb f6 81 3f e3  4d cc 0e 7f 3a 96 85 f3  |J.....?.M...:...|
-000003e0  ea                                                |.|
+00000000  16 03 03 02 69 1a b5 80  9c c6 42 be a6 d9 d1 c8  |....i.....B.....|
+00000010  0b 2c f7 f8 2a 1a 02 f6  db 48 8b 5c 45 fc 28 41  |.,..*....H.\E.(A|
+00000020  a4 db 35 fb a7 a9 72 07  19 86 9e e9 dc 53 48 fb  |..5...r......SH.|
+00000030  97 94 f3 a0 0d 8d 46 69  cf eb d9 ea de 21 a3 f4  |......Fi.....!..|
+00000040  b9 94 4f 8f 09 c3 de 66  fa 20 83 34 fc 50 ae c6  |..O....f. .4.P..|
+00000050  13 f6 7c c5 80 45 17 51  af a9 da bd 24 dd f6 15  |..|..E.Q....$...|
+00000060  8e c4 b4 e6 10 54 85 23  9d fe 72 b1 4c 2f ab ec  |.....T.#..r.L/..|
+00000070  78 9a 06 60 03 96 f7 d0  af dd 94 26 c9 67 1d e5  |x..`.......&.g..|
+00000080  9f 87 22 c4 11 82 f1 4c  f5 68 a7 fd d8 f3 dc ac  |.."....L.h......|
+00000090  35 90 6b c2 97 1c 78 54  d9 b2 3c d8 9d 13 66 42  |5.k...xT..<...fB|
+000000a0  6e 3b 1b 8f 32 4e 8f b5  a0 60 6e e4 35 fd d5 b4  |n;..2N...`n.5...|
+000000b0  f8 fb a6 a9 c7 b7 31 39  8e 90 0c 42 bc 1a 4f 67  |......19...B..Og|
+000000c0  fa b3 10 a8 45 01 88 8a  dd 41 43 c6 6c 41 9d 28  |....E....AC.lA.(|
+000000d0  bd 4c 43 85 4c 3b 7c 03  c4 55 70 f1 49 a0 d6 52  |.LC.L;|..Up.I..R|
+000000e0  5c 49 3e 2b d7 e8 a9 75  5e 92 bf f3 4c 1c c3 7e  |\I>+...u^...L..~|
+000000f0  69 ca 52 25 07 bb 54 e0  58 2d 28 ba 50 30 81 e8  |i.R%..T.X-(.P0..|
+00000100  93 0d 9d 78 3d c0 f1 c3  34 72 29 f1 da 60 de 84  |...x=...4r)..`..|
+00000110  aa fc d3 0f 2b 1e 1a 9c  7c af 6f 6a 4c cc 88 36  |....+...|.ojL..6|
+00000120  03 03 34 79 f9 89 6d 41  bb 19 61 6a 60 75 d7 0f  |..4y..mA..aj`u..|
+00000130  dc 22 7a e4 36 91 32 2b  99 24 36 19 4b 4c c4 ae  |."z.6.2+.$6.KL..|
+00000140  4f 98 6d f0 97 ce bc b4  ee 97 30 97 0b 54 8c 14  |O.m.......0..T..|
+00000150  8f d5 9d 44 ae 0a a6 07  9f 39 f6 66 80 cb fa 27  |...D.....9.f...'|
+00000160  bb 2f 26 bb c3 1a a4 9b  a0 d3 e8 75 13 49 da 0a  |./&........u.I..|
+00000170  8f cc e8 15 77 f8 05 c2  17 a0 ff e7 2a 59 bc c8  |....w.......*Y..|
+00000180  67 b5 7e b8 78 1e 32 81  22 3e 57 28 ee 51 96 b7  |g.~.x.2.">W(.Q..|
+00000190  0e dc 3f d1 00 16 a5 eb  01 cb c3 e3 5b 01 ad a9  |..?.........[...|
+000001a0  d5 1b ac 9b e2 0d 9a 6f  7a 2f f1 7d 05 57 6d 2d  |.......oz/.}.Wm-|
+000001b0  9d 35 68 88 e7 c5 e0 fb  9d 87 a7 ef 71 d8 47 b8  |.5h.........q.G.|
+000001c0  19 8e 87 d8 b1 36 0f 52  ab 98 8b 97 43 53 86 be  |.....6.R....CS..|
+000001d0  9d 86 2a cd 7c b0 46 c4  48 89 5b 6c 1e 93 9e a2  |..*.|.F.H.[l....|
+000001e0  15 af 60 8f 84 75 99 97  53 11 23 f9 0d ba 78 12  |..`..u..S.#...x.|
+000001f0  9b a9 04 91 d0 3a b3 4d  7b 67 a0 fa 78 5c 19 d6  |.....:.M{g..x\..|
+00000200  88 d2 21 f6 8d e4 91 d1  76 95 67 9d 4e 3b a1 d2  |..!.....v.g.N;..|
+00000210  61 c3 d2 a6 53 fd 82 93  20 7e f6 07 a0 49 3a ea  |a...S... ~...I:.|
+00000220  bc 73 03 0b f2 df 51 ac  35 d8 e4 35 9d 13 56 b5  |.s....Q.5..5..V.|
+00000230  be fc 7c 36 8b 37 a0 71  57 62 bd 89 38 18 90 1e  |..|6.7.qWb..8...|
+00000240  7a 1f b3 8f 73 55 32 94  5a 3a 31 91 b3 95 bd 61  |z...sU2.Z:1....a|
+00000250  ea 93 9d f0 38 33 fb 5b  28 3d a7 29 a4 91 de 85  |....83.[(=.)....|
+00000260  9c 16 63 10 81 c6 e0 11  92 3d 53 db 69 95 16 03  |..c......=S.i...|
+00000270  03 00 35 f7 3a b7 d1 20  aa f7 ed b1 c7 46 52 cb  |..5.:.. .....FR.|
+00000280  6e dd e2 f4 ae 83 20 cd  6c 59 b5 26 f9 81 7e 6c  |n..... .lY.&..~l|
+00000290  ed e9 0b 2a f1 3a dc f8  d6 ed e5 6d 89 14 3d 79  |...*.:.....m..=y|
+000002a0  e7 c8 af 89 30 eb 98 3d  16 03 03 00 98 a0 59 2f  |....0..=......Y/|
+000002b0  cd d9 f6 63 e3 53 47 77  e5 c4 fc 2e 12 d7 24 8a  |...c.SGw......$.|
+000002c0  4c c7 d8 c9 77 4f bc 3c  af 93 6f 57 3d 6f 5d 1c  |L...wO.<..oW=o].|
+000002d0  6a 5a 2c 42 1c e0 92 d5  5e 34 c8 a5 9e 11 21 16  |jZ,B....^4....!.|
+000002e0  01 1a 08 af 4e f6 a1 e3  19 a6 81 41 3d 7a f3 d1  |....N......A=z..|
+000002f0  e0 9e 55 90 42 4b d9 5c  46 b7 eb c8 fb 83 1c 97  |..U.BK.\F.......|
+00000300  9e d9 74 bb 7f 2f 4d 61  89 46 db 32 da 1a 76 95  |..t../Ma.F.2..v.|
+00000310  88 f8 ca 62 14 88 dc 97  b8 58 82 74 16 78 be c5  |...b.....X.t.x..|
+00000320  f9 78 a4 88 c1 d4 6b 36  6e 54 60 a5 21 30 47 07  |.x....k6nT`.!0G.|
+00000330  e8 2d 22 ce a5 17 fb 43  10 9d 74 c9 64 a3 db ac  |.-"....C..t.d...|
+00000340  d9 24 7a a7 5d 14 03 03  00 11 68 20 87 e9 9b 91  |.$z.].....h ....|
+00000350  81 67 2b 31 c4 47 e8 9b  2e 7c c4 16 03 03 00 20  |.g+1.G...|..... |
+00000360  ef 6f 3d 0f 23 fa 77 8c  a9 46 d9 0d b0 d9 f8 16  |.o=.#.w..F......|
+00000370  62 e2 07 21 ec b6 a7 78  ce a6 ea b3 68 c1 c7 af  |b..!...x....h...|
 >>> Flow 14 (server to client)
-00000000  14 03 03 00 19 24 71 bf  67 14 d6 5e 30 cc 1c 3f  |.....$q.g..^0..?|
-00000010  3c 20 07 b3 c3 79 d0 6e  fd 59 e6 0d 47 fd 16 03  |< ...y.n.Y..G...|
-00000020  03 00 28 54 db a5 f7 3d  b3 18 49 39 e5 59 93 bb  |..(T...=..I9.Y..|
-00000030  64 93 1c ed 46 d6 f8 89  94 45 ba 4a 9e 73 2e cb  |d...F....E.J.s..|
-00000040  03 18 e4 26 6d 33 e3 34  73 d6 fc 17 03 03 00 21  |...&m3.4s......!|
-00000050  54 db a5 f7 3d b3 18 4a  aa 45 38 3b 50 02 44 37  |T...=..J.E8;P.D7|
-00000060  6a d1 3e f9 d3 3b 33 33  d5 84 2d 52 33 7d 68 84  |j.>..;33..-R3}h.|
-00000070  ef                                                |.|
+00000000  14 03 03 00 11 bb 45 96  9e 08 cb e4 24 c2 e3 71  |......E.....$..q|
+00000010  40 d1 ef a1 5e 2f 16 03  03 00 20 1b 3f 69 fb ae  |@...^/.... .?i..|
+00000020  cd 98 15 59 16 14 cf a5  16 af 36 6d 6d 3a 49 06  |...Y......6mm:I.|
+00000030  a6 f9 cf 53 ea 9a b7 3b  48 d2 e3 17 03 03 00 19  |...S...;H.......|
+00000040  72 2c 82 a0 8c 6c 8b c3  78 e4 41 1b ff ba 92 6d  |r,...l..x.A....m|
+00000050  3c 4d 9c c3 95 e3 27 b9  82                       |<M....'..|
 >>> Flow 15 (client to server)
-00000000  15 03 03 00 1a 00 00 00  00 00 00 00 01 55 5f 94  |.............U_.|
-00000010  25 d0 89 86 cb 8f 33 6f  b7 b6 35 ec 0f 6a 87     |%.....3o..5..j.|
+00000000  15 03 03 00 12 3d f9 72  53 84 b5 a4 ec 27 39 cc  |.....=.rS....'9.|
+00000010  72 29 c0 e6 37 7b 0f                              |r)..7{.|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-RenegotiateTwiceRejected b/src/crypto/tls/testdata/Client-TLSv12-RenegotiateTwiceRejected
index fe2fa88..737ccc6 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-RenegotiateTwiceRejected
+++ b/src/crypto/tls/testdata/Client-TLSv12-RenegotiateTwiceRejected
@@ -1,255 +1,234 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 51 0e b9 8f 73  |....Y...U..Q...s|
-00000010  ec 20 17 90 80 3a 43 7a  bc 19 19 f5 75 c3 76 a6  |. ...:Cz....u.v.|
-00000020  53 53 4b 77 ce dd ca 4b  1b 1e ed 20 8d e5 a7 6f  |SSKw...K... ...o|
-00000030  53 e9 a4 06 4b 01 a6 08  a1 90 e5 da c9 e3 74 b0  |S...K.........t.|
-00000040  87 1f 17 1a 68 d3 f7 ae  39 b8 3e 80 c0 2f 00 00  |....h...9.>../..|
+00000000  16 03 03 00 59 02 00 00  55 03 03 75 f6 1b 17 2f  |....Y...U..u.../|
+00000010  e0 d4 19 06 0d 93 90 51  46 d9 c3 a0 cd 45 6c 85  |.......QF....El.|
+00000020  94 87 01 21 3e 92 62 1d  e7 d1 39 20 07 26 a1 5b  |...!>.b...9 .&.[|
+00000030  d2 4a 61 40 ba 58 c0 23  0c 3f c3 08 5d 28 04 94  |.Ja at .X.#.?..](..|
+00000040  a9 34 37 28 64 75 6f 9c  ae fa 1f 24 cc a8 00 00  |.47(duo....$....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
-00000060  03 02 71 0b 00 02 6d 00  02 6a 00 02 67 30 82 02  |..q...m..j..g0..|
-00000070  63 30 82 01 cc a0 03 02  01 02 02 09 00 a2 73 00  |c0............s.|
-00000080  0c 81 00 cb f3 30 0d 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000090  01 0b 05 00 30 2b 31 17  30 15 06 03 55 04 0a 13  |....0+1.0...U...|
-000000a0  0e 47 6f 6f 67 6c 65 20  54 45 53 54 49 4e 47 31  |.Google TESTING1|
-000000b0  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
-000000c0  74 30 1e 17 0d 31 35 30  31 30 31 30 30 30 30 30  |t0...15010100000|
-000000d0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
-000000e0  5a 30 26 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |Z0&1.0...U....Go|
-000000f0  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 0b 30 09  |ogle TESTING1.0.|
-00000100  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
-00000110  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
-00000120  81 89 02 81 81 00 af 87  88 f6 20 1b 95 65 6c 14  |.......... ..el.|
-00000130  ab 44 05 af 3b 45 14 e3  b7 6d fd 00 63 4d 95 7f  |.D..;E...m..cM..|
-00000140  fe 6a 62 35 86 c0 4a f9  18 7c f6 aa 25 5e 7a 64  |.jb5..J..|..%^zd|
-00000150  31 66 00 ba f4 8e 92 af  c7 6b d8 76 d4 f3 5f 41  |1f.......k.v.._A|
-00000160  cb 6e 56 15 97 1b 97 c1  3c 12 39 21 66 3d 2b 16  |.nV.....<.9!f=+.|
-00000170  d1 bc db 1c c0 a7 da b7  ca ad ba da cb d5 21 50  |..............!P|
-00000180  ec de 8d ab d1 6b 81 4b  89 02 f3 c4 be c1 6c 89  |.....k.K......l.|
-00000190  b1 44 84 bd 21 d1 04 7d  9d 16 4d f9 82 15 f6 ef  |.D..!..}..M.....|
-000001a0  fa d6 09 47 f2 fb 02 03  01 00 01 a3 81 93 30 81  |...G..........0.|
-000001b0  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
-000001c0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
-000001d0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
-000001e0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
-000001f0  06 03 55 1d 0e 04 12 04  10 12 50 8d 89 6f 1b d1  |..U.......P..o..|
-00000200  dc 54 4d 6e cb 69 5e 06  f4 30 1b 06 03 55 1d 23  |.TMn.i^..0...U.#|
-00000210  04 14 30 12 80 10 bf 3d  b6 a9 66 f2 b8 40 cf ea  |..0....=..f.. at ..|
-00000220  b4 03 78 48 1a 41 30 19  06 03 55 1d 11 04 12 30  |..xH.A0...U....0|
-00000230  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
-00000240  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
-00000250  03 81 81 00 92 7c af 91  55 12 18 96 59 31 a6 48  |.....|..U...Y1.H|
-00000260  40 d5 2d d5 ee bb 02 a0  f5 c2 1e 7c 9b b3 30 7d  |@.-........|..0}|
-00000270  3c dc 76 da 4f 3d c0 fa  ae 2d 33 24 6b 03 7b 1b  |<.v.O=...-3$k.{.|
-00000280  67 59 11 21 b5 11 bc 77  b9 d9 e0 6e a8 2d 2e 35  |gY.!...w...n.-.5|
-00000290  fa 64 5f 22 3e 63 10 6b  be ff 14 86 6d 0d f0 15  |.d_">c.k....m...|
-000002a0  31 a8 14 38 1e 3b 84 87  2c cb 98 ed 51 76 b9 b1  |1..8.;..,...Qv..|
-000002b0  4f dd db 9b 84 04 86 40  fa 51 dd ba b4 8d eb e3  |O...... at .Q......|
-000002c0  46 de 46 b9 4f 86 c7 f9  a4 c2 41 34 ac cc f6 ea  |F.F.O.....A4....|
-000002d0  b0 ab 39 18 16 03 03 00  cd 0c 00 00 c9 03 00 17  |..9.............|
-000002e0  41 04 5e 2e 43 b7 c2 0f  e8 4a 33 aa b8 d6 04 7f  |A.^.C....J3.....|
-000002f0  2b be a2 e3 6f fa 05 1a  d1 64 a7 d1 ec 45 f9 16  |+...o....d...E..|
-00000300  b7 75 ad f2 52 3e a3 60  67 f8 fb 87 a0 c0 d4 2f  |.u..R>.`g....../|
-00000310  f4 66 c9 dd 38 40 79 5b  16 75 0b 16 6a d8 e5 ad  |.f..8 at y[.u..j...|
-00000320  63 f3 04 01 00 80 5e 89  b3 6b f4 a1 35 b3 27 be  |c.....^..k..5.'.|
-00000330  6a d4 39 42 7c ac e2 d4  9f a0 a0 a3 95 22 b5 09  |j.9B|........"..|
-00000340  70 4a 0c 6f cf 7f 69 f9  7d 27 c4 0d e7 b8 9c 82  |pJ.o..i.}'......|
-00000350  c9 0d 1d bb 5c 23 20 eb  ca 09 ca 02 a0 56 27 10  |....\# ......V'.|
-00000360  c5 d6 13 7d cd 05 64 cc  53 20 5d df ac 00 90 7f  |...}..d.S ].....|
-00000370  d7 cd f2 a1 07 9c 06 c2  e6 d1 94 60 d3 c6 97 a6  |...........`....|
-00000380  3c e5 89 67 e7 cc b7 c1  ba 75 dc 17 2b 47 ce 23  |<..g.....u..+G.#|
-00000390  a3 37 3b 3f 32 39 ae 4a  64 17 d2 64 d1 75 23 8a  |.7;?29.Jd..d.u#.|
-000003a0  e3 b4 fa 75 17 72 16 03  03 00 04 0e 00 00 00     |...u.r.........|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 84 74 e5 bc 24 39 39  |........ .t..$99|
+000002d0  e0 6e 35 fb 34 87 76 0d  78 89 35 5c 85 e7 92 da  |.n5.4.v.x.5\....|
+000002e0  e1 39 f4 b2 e7 ec d5 cd  58 04 01 00 80 cf 3a 57  |.9......X.....:W|
+000002f0  6a 8b b7 72 d8 a2 6b 47  87 77 8b 7a bf 63 6c e8  |j..r..kG.w.z.cl.|
+00000300  d4 20 6a 6a 9c 62 b6 ef  4b 9f a7 89 8c a6 fd 02  |. jj.b..K.......|
+00000310  92 2f 8d 07 44 09 f6 d9  03 99 39 49 1d 8d 1b 7f  |./..D.....9I....|
+00000320  eb eb 4b a6 fb 9f 83 3b  3d d3 61 3e e4 d3 22 24  |..K....;=.a>.."$|
+00000330  c1 44 76 e8 75 c7 aa 31  96 e3 50 bb 76 3e 87 02  |.Dv.u..1..P.v>..|
+00000340  b9 1d 82 dd 55 ee 05 b9  b5 1e 65 90 2c 50 c9 87  |....U.....e.,P..|
+00000350  49 dd 35 c8 84 67 6e 52  3a 3b ec 3c 63 f4 0f 95  |I.5..gnR:;.<c...|
+00000360  87 05 8e ec bd d7 97 9f  9a 14 78 d7 97 16 03 03  |..........x.....|
+00000370  00 04 0e 00 00 00                                 |......|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 4f ef  |.....(........O.|
-00000060  08 7c a7 de 53 70 7e 78  fb 08 79 97 1f bd 33 92  |.|..Sp~x..y...3.|
-00000070  c5 46 4d 64 32 bb 94 f0  07 ad 7d 00 86 9e        |.FMd2.....}...|
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 20 3e a2 12  3b a4 83 4a c2 0e 93 d5  |.... >..;..J....|
+00000040  98 d5 2e 11 9e 60 60 23  78 5a 63 49 f1 7a ee c4  |.....``#xZcI.z..|
+00000050  47 00 6c 4e fb                                    |G.lN.|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 59 c3 19 1f ed  |..........(Y....|
-00000010  d1 1b 54 5b 66 81 47 29  9a 77 84 87 a0 bd c5 d4  |..T[f.G).w......|
-00000020  f0 4e e2 11 d3 1d 26 dd  87 7a 55 11 48 37 7f 3a  |.N....&..zU.H7.:|
-00000030  2c fc 62                                          |,.b|
+00000000  14 03 03 00 01 01 16 03  03 00 20 6d 78 5b 5f 1b  |.......... mx[_.|
+00000010  2c 05 ba 92 46 e2 f0 04  48 fa a1 da be 93 ed fd  |,...F...H.......|
+00000020  f5 f8 b8 dd 00 60 09 a6  36 3c c4                 |.....`..6<.|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 02 12 57  |...............W|
-00000010  9d 64 c5 47 13 95 13 7b  2b 3e e0 f7 ae 49 0f c7  |.d.G...{+>...I..|
-00000020  0e 3a 67                                          |.:g|
+00000000  17 03 03 00 16 a4 db 13  bc c1 6e 06 9e 6d 1a c9  |..........n..m..|
+00000010  85 a7 e9 28 b8 27 74 19  8f 1a bc                 |...(.'t....|
 >>> Flow 6 (server to client)
-00000000  16 03 03 00 1c 59 c3 19  1f ed d1 1b 55 ac 23 dc  |.....Y......U.#.|
-00000010  0c 35 65 1e 7a 65 4f 47  13 46 a0 d0 d0 4d 0a 1f  |.5e.zeOG.F...M..|
-00000020  5c                                                |\|
+00000000  16 03 03 00 14 fa a3 77  3c 76 11 5d be 12 4f 6a  |.......w<v.]..Oj|
+00000010  f7 0d 26 73 ab 7c bd 4b  19                       |..&s.|.K.|
 >>> Flow 7 (client to server)
-00000000  16 03 03 00 a9 00 00 00  00 00 00 00 02 c0 05 0c  |................|
-00000010  76 0a 46 19 16 17 a6 75  af 11 bb 73 37 74 a4 26  |v.F....u...s7t.&|
-00000020  d9 16 93 b8 19 5e 2f 17  52 d1 12 9e 36 90 4e c9  |.....^/.R...6.N.|
-00000030  7a f9 89 75 3b d9 d4 e1  2e cf a0 5d 03 7d cc f6  |z..u;......].}..|
-00000040  73 aa a9 52 c7 65 78 d0  89 6f b1 15 6e f9 9e 55  |s..R.ex..o..n..U|
-00000050  42 9e 22 09 df 97 00 31  b8 73 57 1b 93 ff 0c e7  |B."....1.sW.....|
-00000060  46 29 40 79 a7 c0 de b9  44 93 7b 4d 35 a0 35 65  |F)@y....D.{M5.5e|
-00000070  6e 58 07 90 2b 11 49 26  10 f7 c7 32 f7 8e 6e a7  |nX..+.I&...2..n.|
-00000080  9b 75 ba cb 4a ce f7 f0  f1 31 ca 04 a6 02 d0 62  |.u..J....1.....b|
-00000090  da 9b 8b 27 8e 04 b8 4a  49 0d d6 31 10 93 30 37  |...'...JI..1..07|
-000000a0  ad ea d7 c4 49 98 90 f3  a1 45 f4 69 2e 59        |....I....E.i.Y|
+00000000  16 03 03 00 ad 59 80 a4  91 95 56 2a 01 ee 04 84  |.....Y....V*....|
+00000010  f9 d2 dd a5 2c a6 46 a8  69 a3 c7 47 7e eb 54 da  |....,.F.i..G~.T.|
+00000020  ec cc 9d aa e1 0a b1 7c  e9 cf 9f c9 c8 12 62 35  |.......|......b5|
+00000030  d2 4a eb 28 0d 9b aa a4  d5 79 66 f7 72 4c 26 10  |.J.(.....yf.rL&.|
+00000040  b6 71 db 4a 68 8b 47 f9  47 e3 6d a6 4e 99 d5 0b  |.q.Jh.G.G.m.N...|
+00000050  27 b2 2c 23 9b 58 60 8a  37 a1 8e 26 09 26 2a 46  |'.,#.X`.7..&.&*F|
+00000060  e6 24 7f 9b cb 6b d1 9d  b1 c0 48 c9 50 8b ab 06  |.$...k....H.P...|
+00000070  05 57 ef 1a e0 bd ce db  ca 3d e1 59 df 24 4c 02  |.W.......=.Y.$L.|
+00000080  bf 1b 9e 48 52 42 6c dd  8f fa 82 68 56 52 a6 be  |...HRBl....hVR..|
+00000090  d0 93 cb 43 74 e1 2f 86  cc e1 4c fa ba fc 1d f0  |...Ct./...L.....|
+000000a0  d5 20 3a 79 b3 b8 b7 24  b5 cf 4c dd a5 d0 4d 18  |. :y...$..L...M.|
+000000b0  15 55                                             |.U|
 >>> Flow 8 (server to client)
-00000000  16 03 03 00 89 59 c3 19  1f ed d1 1b 56 c8 38 86  |.....Y......V.8.|
-00000010  22 b6 5f 55 cb 0c e1 40  e6 12 f2 71 d5 09 bc 47  |"._U... at ...q...G|
-00000020  ea 83 38 3a 58 f4 34 da  ae 7f 64 fb 8c bc 71 64  |..8:X.4...d...qd|
-00000030  1b aa 84 e4 3e c1 cc c4  a9 05 36 13 5a 9b 1e c0  |....>.....6.Z...|
-00000040  44 cc 86 54 f0 75 b7 d0  aa b0 f0 3a b5 c7 f1 cc  |D..T.u.....:....|
-00000050  1f cd 8e 9e 9e bb 24 23  c3 05 0b a5 1d f3 0b 41  |......$#.......A|
-00000060  41 19 89 1e ee 51 fc b3  e8 e2 6e a8 3f c4 8b ab  |A....Q....n.?...|
-00000070  cb af d9 a1 7e 1c db e7  6c f6 23 71 c6 31 db 40  |....~...l.#q.1.@|
-00000080  a8 a7 08 fb 1a ff 8d 94  53 88 9a 11 73 6a 16 03  |........S...sj..|
-00000090  03 02 89 59 c3 19 1f ed  d1 1b 57 17 dd 9c b5 a5  |...Y......W.....|
-000000a0  89 12 3a 14 61 34 e0 1d  0b 35 d6 3a 6c 09 93 2b  |..:.a4...5.:l..+|
-000000b0  6c 69 ee f4 f3 be fb 42  33 99 fd 9a e6 21 38 68  |li.....B3....!8h|
-000000c0  a6 19 37 43 24 81 ba 35  12 fe ab ed 49 0b 03 54  |..7C$..5....I..T|
-000000d0  11 a7 74 4d bb ba e7 b9  f3 ee 6a 4e 1a 84 2f 03  |..tM......jN../.|
-000000e0  0c d1 28 21 49 84 f4 3c  5b 15 92 07 5c 6a 24 89  |..(!I..<[...\j$.|
-000000f0  00 cf 78 31 76 23 0f 9d  45 3b 93 a5 68 ee 9c 73  |..x1v#..E;..h..s|
-00000100  14 3f 08 30 37 40 4e 8b  a4 02 03 3c 4b 52 74 99  |.?.07 at N....<KRt.|
-00000110  0e 9a ec 40 c6 74 16 ef  c5 48 68 33 86 d7 06 57  |... at .t...Hh3...W|
-00000120  bf 8a 6f 3f 41 fe 4d f2  37 0a 1b fd fb 66 55 bd  |..o?A.M.7....fU.|
-00000130  70 4d b0 8c 4f 78 24 eb  1f 8f 22 c7 aa 07 89 04  |pM..Ox$...".....|
-00000140  6a b3 07 15 37 25 21 63  97 39 b1 c1 9b fa 81 5e  |j...7%!c.9.....^|
-00000150  69 c9 c7 4a 9c 5d b3 6a  41 d0 5e b9 f4 d5 5c a1  |i..J.].jA.^...\.|
-00000160  8e 8a ad 58 6e 5c 4f 73  62 38 1c 5f 8d b1 67 63  |...Xn\Osb8._..gc|
-00000170  49 da 4b 4e 83 54 34 8f  8d 12 de 4e 43 4d dc b9  |I.KN.T4....NCM..|
-00000180  02 ab 08 59 db 0b 45 7e  f5 b8 e2 33 f1 04 de 16  |...Y..E~...3....|
-00000190  05 bf b4 2b 07 a1 11 e4  9e 48 f7 52 ab 20 89 04  |...+.....H.R. ..|
-000001a0  a7 44 28 7a 12 6c 19 ab  2f 68 1a d9 26 ec 72 a0  |.D(z.l../h..&.r.|
-000001b0  62 83 48 6f 4b 70 7d 74  3a 43 4f a6 38 37 fe 59  |b.HoKp}t:CO.87.Y|
-000001c0  6e 72 5d 81 7d 2c c7 e1  6b 06 47 41 56 17 2c 25  |nr].},..k.GAV.,%|
-000001d0  06 b1 7f f5 10 0a 31 a3  12 b1 5c 01 2f e0 a6 e4  |......1...\./...|
-000001e0  fa ab d2 0b 02 77 ad ac  f8 54 db 70 20 0a 1f 04  |.....w...T.p ...|
-000001f0  86 a8 32 05 26 ee 7d e0  e9 03 19 cc 8f 67 f5 b6  |..2.&.}......g..|
-00000200  97 fe 06 5e c1 d5 df 25  f5 39 70 64 57 a8 c9 84  |...^...%.9pdW...|
-00000210  8f 0f 25 f8 c8 f9 17 70  e5 00 3c 4a 9f 4b c1 d9  |..%....p..<J.K..|
-00000220  6e b8 1a e4 6d 85 a4 e2  42 44 71 ba 43 9b 03 70  |n...m...BDq.C..p|
-00000230  14 ff 72 5e 5c 69 24 2e  52 0c 73 8b df 50 99 68  |..r^\i$.R.s..P.h|
-00000240  57 81 c1 ed b6 33 fc 74  15 45 fd a2 c4 8c f8 95  |W....3.t.E......|
-00000250  bf 8d 0e 92 91 42 72 77  03 ec c6 f6 9a 02 ca 7d  |.....Brw.......}|
-00000260  3c 87 72 eb 8d 30 3a 5c  b4 03 4a 6d 2e 83 22 c5  |<.r..0:\..Jm..".|
-00000270  e2 4a 95 83 7a 72 72 f2  2a 11 25 4a bd 04 16 ab  |.J..zrr.*.%J....|
-00000280  6a 48 44 2b 99 fb 6f 61  9a 14 4a 42 1e bf d1 82  |jHD+..oa..JB....|
-00000290  db 62 5f ac 1e 6d 1d 1b  0d 4b 9d 8d 3a 84 94 b4  |.b_..m...K..:...|
-000002a0  aa 08 5b 90 7f d2 46 b0  a7 40 f4 55 76 6b 0d 4c  |..[...F.. at .Uvk.L|
-000002b0  8e e3 8c fd ed 33 7d 93  f8 d8 c3 db 26 2d db a1  |.....3}.....&-..|
-000002c0  24 bc b0 fb 26 5f ec 13  5f 97 05 bb 5c 3c cc a3  |$...&_.._...\<..|
-000002d0  c2 57 58 cd 2e 70 0c a7  77 c5 e5 e8 0c 42 f2 e0  |.WX..p..w....B..|
-000002e0  1c 11 0f 62 4b 84 49 c2  b7 10 83 2e 16 1c 38 d4  |...bK.I.......8.|
-000002f0  10 f7 ca 71 7a 87 c5 a3  66 d2 98 1e c8 f2 c0 37  |...qz...f......7|
-00000300  0e 28 31 fe 8e 3e f4 03  74 6e 91 42 22 cb 5d 7f  |.(1..>..tn.B".].|
-00000310  d2 22 da 3c f2 a0 2d 09  a9 a5 2d 14 16 03 03 00  |.".<..-...-.....|
-00000320  e5 59 c3 19 1f ed d1 1b  58 3f 19 93 55 cb 19 f8  |.Y......X?..U...|
-00000330  02 1a 43 b3 b2 6c 4e 3e  ee 99 b3 df fd 45 24 ac  |..C..lN>.....E$.|
-00000340  63 e7 45 cc a4 44 ca cf  3a e1 81 88 01 9a b3 64  |c.E..D..:......d|
-00000350  fe 6b 36 57 9f 81 fc 40  8d ef 21 af 00 be 43 f7  |.k6W... at ..!...C.|
-00000360  a3 3b a3 fa f0 01 f2 b4  ab 8a d1 a8 14 58 1b 6f  |.;...........X.o|
-00000370  75 01 35 92 54 a7 a6 c1  99 1e 92 d8 87 53 7b 42  |u.5.T........S{B|
-00000380  4a 76 96 5e e9 db bb 4e  f1 d9 bb e6 d2 b0 34 10  |Jv.^...N......4.|
-00000390  1b 4c d5 2c ca af 19 0d  3e 77 ee 77 0e 5f ff e2  |.L.,....>w.w._..|
-000003a0  02 c5 4a f2 ec 0b 7d cf  d1 e7 3c 72 d2 17 4d 6c  |..J...}...<r..Ml|
-000003b0  a7 ca 3a 1b 00 2b 69 17  e5 a9 82 69 49 c2 ff 8a  |..:..+i....iI...|
-000003c0  f1 e8 ab 1b c3 8d da f1  31 ba a6 f4 7c 3c 01 6f  |........1...|<.o|
-000003d0  ed a8 6f e2 4f a3 68 77  b7 54 b5 87 1b 5c 5c fb  |..o.O.hw.T...\\.|
-000003e0  83 bf 48 4d 36 43 d6 f7  0a 48 74 f3 44 9d 43 53  |..HM6C...Ht.D.CS|
-000003f0  f8 54 1b 57 97 24 53 5a  93 e2 e9 33 f0 35 5f 0a  |.T.W.$SZ...3.5_.|
-00000400  0d 4c ce 92 4d c9 16 03  03 00 46 59 c3 19 1f ed  |.L..M.....FY....|
-00000410  d1 1b 59 80 50 fc 3a 56  e0 0b 06 b4 58 39 0c d8  |..Y.P.:V....X9..|
-00000420  4b b1 11 7a bd cf 1c 78  41 62 ee 22 74 61 7d 61  |K..z...xAb."ta}a|
-00000430  91 3d 0a 74 a4 b0 cd 25  70 19 a5 de d8 1b df 12  |.=.t...%p.......|
-00000440  4e b8 71 db ac bc 48 ea  89 32 ec 27 69 02 0d 8b  |N.q...H..2.'i...|
-00000450  83                                                |.|
+00000000  16 03 03 00 81 10 b8 d9  9a 82 21 14 86 6d ef e4  |..........!..m..|
+00000010  b6 bc 10 84 80 a6 72 7b  dc 24 ba e1 e5 d2 bb d2  |......r{.$......|
+00000020  9f 09 d3 d7 37 20 ec 7d  bd 13 e0 bd 40 44 8a 6e  |....7 .}.... at D.n|
+00000030  7f f6 d5 42 03 4b 00 b5  87 99 ac 6d 11 03 38 46  |...B.K.....m..8F|
+00000040  33 71 c4 10 ce da 36 d6  c1 41 9f 96 8e eb 4b 99  |3q....6..A....K.|
+00000050  24 07 8c 6b 88 2c f1 dd  31 35 ba 43 0f 60 bf b0  |$..k.,..15.C.`..|
+00000060  74 77 a9 d4 a7 65 f6 68  e2 70 4a 7e fe db ab 13  |tw...e.h.pJ~....|
+00000070  7f 51 c3 0f b4 93 42 12  d6 29 5d 44 5c df 17 6e  |.Q....B..)]D\..n|
+00000080  73 1d b8 95 fc 8b 16 03  03 02 69 64 5d 4f b4 3a  |s.........id]O.:|
+00000090  23 98 07 51 63 18 09 07  9a 8c dd 8e 51 a8 ca 23  |#..Qc.......Q..#|
+000000a0  37 21 f4 d5 e0 8f 03 1c  6f 6d c4 60 dd 99 8f 4b  |7!......om.`...K|
+000000b0  4c 11 f7 78 f8 aa f6 29  87 cf 4d ba 89 87 2a c9  |L..x...)..M...*.|
+000000c0  48 66 48 d3 a6 19 8a 84  f6 db 17 b4 59 5b e4 8e  |HfH.........Y[..|
+000000d0  8e ef fc 32 10 aa 0d 57  47 68 82 07 72 95 03 f6  |...2...WGh..r...|
+000000e0  e5 c1 c3 01 00 f9 85 4f  0f 85 37 73 f7 c5 af a2  |.......O..7s....|
+000000f0  37 96 ba 06 49 6a 2d a2  23 39 2e 9b f1 fc 01 de  |7...Ij-.#9......|
+00000100  53 75 ee 34 ae 27 ea 49  6a d8 d0 cd 9b e8 60 b7  |Su.4.'.Ij.....`.|
+00000110  f6 b1 8e 26 ec 6c 36 87  d6 70 49 07 e0 96 2a bd  |...&.l6..pI...*.|
+00000120  45 a3 b1 f5 dc b0 a3 4f  dc d8 c3 fd 4f fb 98 13  |E......O....O...|
+00000130  67 55 99 39 b5 16 19 72  9d f1 5a cf 6e 53 d9 03  |gU.9...r..Z.nS..|
+00000140  05 f5 81 07 a1 38 a1 e5  4c 76 51 1a ae f6 4b f6  |.....8..LvQ...K.|
+00000150  b2 a7 84 1e 2a 31 b0 b8  9f 41 e8 e5 32 18 44 4c  |....*1...A..2.DL|
+00000160  0b fb e3 d9 4c dd 45 c5  c4 c4 57 bf f7 5a dc f6  |....L.E...W..Z..|
+00000170  73 98 d4 ea 2f c0 cb 35  97 c1 45 94 37 87 d3 8c  |s.../..5..E.7...|
+00000180  65 3f ee a8 67 a6 00 80  92 02 76 e8 0a 04 ce 7a  |e?..g.....v....z|
+00000190  79 4f cd 70 1a 31 5a 03  83 01 de 1f 4a 46 39 4e  |yO.p.1Z.....JF9N|
+000001a0  d0 80 6e 67 d7 e6 fc ba  74 4b 57 d2 3c 19 7b 03  |..ng....tKW.<.{.|
+000001b0  ab 9a e2 f7 db 58 c2 b7  58 96 55 88 e6 e2 e2 f8  |.....X..X.U.....|
+000001c0  ab e9 b0 12 ef ff e6 53  7b 4e 01 2f 65 0d 05 f0  |.......S{N./e...|
+000001d0  95 9f 46 d2 ae e7 33 5c  37 56 ab 67 95 87 81 59  |..F...3\7V.g...Y|
+000001e0  f2 35 76 78 ed 13 63 a3  58 52 af 46 e6 aa c3 99  |.5vx..c.XR.F....|
+000001f0  37 9d 10 25 cc 7f 7e 63  e1 96 6d 7a 8e ac 9e 00  |7..%..~c..mz....|
+00000200  d1 0e 7a 48 b6 82 77 6a  a0 17 d1 77 70 f8 40 4a  |..zH..wj...wp. at J|
+00000210  c4 90 da b0 3f 25 68 f5  9f dd 5e ec 95 02 19 53  |....?%h...^....S|
+00000220  08 6a 13 11 88 9e 2b 25  b8 28 cd 58 36 d7 d3 95  |.j....+%.(.X6...|
+00000230  f5 91 63 92 ff 3b d2 4f  75 ae 47 6c 64 8a a4 76  |..c..;.Ou.Gld..v|
+00000240  48 96 a7 35 d6 35 22 96  4d 4f ee 45 fb 88 52 68  |H..5.5".MO.E..Rh|
+00000250  4e 38 93 e8 08 6a e6 f3  00 a7 f7 b0 0b 68 41 ab  |N8...j.......hA.|
+00000260  9b 3a 92 2b fd b2 71 14  77 91 48 e7 70 62 b5 b0  |.:.+..q.w.H.pb..|
+00000270  45 90 35 d2 b3 22 f5 70  6c 62 7f 55 6b 56 42 f8  |E.5..".plb.UkVB.|
+00000280  6c 87 a7 60 45 37 f0 41  41 ec 73 f5 f1 d9 d2 84  |l..`E7.AA.s.....|
+00000290  bd 88 bc 9b 43 ec 8b b3  c4 3a 59 2c 30 61 30 98  |....C....:Y,0a0.|
+000002a0  78 d3 e7 85 dd 7e 80 b8  fb b3 bf 7e 11 79 e8 20  |x....~.....~.y. |
+000002b0  aa b8 81 94 10 5c f8 ba  70 4c 1e 7c 35 8f 48 30  |.....\..pL.|5.H0|
+000002c0  17 38 d6 1c 91 ed 00 2c  f7 af 29 d3 cb 9b ab 6b  |.8.....,..)....k|
+000002d0  b3 fa 6e 1a 9b a8 cf 08  8e 03 a5 f7 76 17 74 3a  |..n.........v.t:|
+000002e0  9e 36 ae 19 fd 2c 44 14  f3 2b 1d 01 db e1 96 22  |.6...,D..+....."|
+000002f0  25 14 5b d8 16 03 03 00  bc bb 8c bb cf f9 d7 76  |%.[............v|
+00000300  7a a7 d6 e0 29 cb 45 21  8d 57 0b 81 0c e0 96 05  |z...).E!.W......|
+00000310  c7 96 67 43 0f 41 11 e9  c2 07 2d 62 17 b4 64 01  |..gC.A....-b..d.|
+00000320  c5 75 79 dc 9c 36 3f ea  42 ea 09 a7 bc 0f 6b b1  |.uy..6?.B.....k.|
+00000330  b4 4d ae 38 0a ca 51 d0  97 46 b6 55 12 7c 24 28  |.M.8..Q..F.U.|$(|
+00000340  77 16 64 42 53 70 c4 52  ed e5 aa 20 3a 00 7e d0  |w.dBSp.R... :.~.|
+00000350  9e 99 e4 56 5f ef 30 56  00 8b e7 31 6d 66 6d dc  |...V_.0V...1mfm.|
+00000360  58 8a b0 60 6f 16 a7 b0  14 a5 9d 3d 38 94 6e 16  |X..`o......=8.n.|
+00000370  a3 22 76 e9 59 d8 90 cd  32 83 bb 8c c5 23 cb c2  |."v.Y...2....#..|
+00000380  c5 03 02 de 13 97 ac 4e  99 9f 4d 6d aa ed 72 b7  |.......N..Mm..r.|
+00000390  76 db 8b c9 31 17 b9 1c  4a fa 87 2c 6b dc 07 82  |v...1...J..,k...|
+000003a0  a2 39 e0 09 32 a2 8e 1c  6e 68 e1 14 ba ab 3a d4  |.9..2...nh....:.|
+000003b0  1e 4f f4 39 c7 16 03 03  00 3a cf b2 61 5f 7e ef  |.O.9.....:..a_~.|
+000003c0  04 51 64 f6 3d b3 54 44  bd 8a d0 87 48 64 76 a6  |.Qd.=.TD....Hdv.|
+000003d0  0d bc 7f b7 99 d4 67 8b  cd e3 c4 5d df c5 c8 fc  |......g....]....|
+000003e0  be d1 c4 03 6d 61 2d 10  58 b1 7a 7c 9e 1c 6b 16  |....ma-.X.z|..k.|
+000003f0  ff 31 a8 87 16 03 03 00  14 85 99 57 4b a1 19 33  |.1.........WK..3|
+00000400  bd 9a 86 ed be 5d 24 f0  02 9d ca 1e 26           |.....]$.....&|
 >>> Flow 9 (client to server)
-00000000  16 03 03 02 89 00 00 00  00 00 00 00 03 be 8d 55  |...............U|
-00000010  8a 5b 24 10 db e3 f2 11  28 0d 26 cc 1b bc 38 fa  |.[$.....(.&...8.|
-00000020  1c 8c f8 c9 64 55 ec 43  16 f7 ca af 12 a8 1c 09  |....dU.C........|
-00000030  0d b0 47 bc 9f 19 02 91  ab 9d 33 b4 bc 45 f7 4d  |..G.......3..E.M|
-00000040  53 85 4a 91 7e d3 2d dc  d6 02 6e 4a 34 51 99 db  |S.J.~.-...nJ4Q..|
-00000050  f2 a1 8d 34 60 6f 15 6a  f9 4d 7a 03 0b dc f7 c1  |...4`o.j.Mz.....|
-00000060  99 c2 2c b8 4c a1 63 ce  a2 fb 33 0d d6 dd d4 0a  |..,.L.c...3.....|
-00000070  88 0c 1d 5c ea 06 00 33  3a 06 6e 3d 63 b4 d5 0c  |...\...3:.n=c...|
-00000080  9b 69 f0 86 72 db 47 52  3d 61 0b 66 57 8d 7b 67  |.i..r.GR=a.fW.{g|
-00000090  1e 42 aa b8 ca e6 d3 07  56 cf f5 09 14 25 a2 1d  |.B......V....%..|
-000000a0  3b 3e dd 0c 41 ac 66 05  3b db 59 85 9d e2 9f 8b  |;>..A.f.;.Y.....|
-000000b0  21 c0 9a 3b 0b 8e 5b 4b  af ac 73 87 d3 b4 34 b7  |!..;..[K..s...4.|
-000000c0  2e 26 b0 5d 10 3a 2e 00  cc ac 40 b5 72 40 69 fa  |.&.].:.... at .r@i.|
-000000d0  11 04 b6 37 38 84 59 76  29 08 f0 0f 0f 79 40 7c  |...78.Yv)....y@||
-000000e0  e4 08 15 b7 58 cd 6c f4  d6 77 d6 f8 cb 1d ca 5c  |....X.l..w.....\|
-000000f0  41 d7 f8 64 63 14 a5 a5  3a 13 ce 55 b4 0a d9 b5  |A..dc...:..U....|
-00000100  34 f9 5e 69 f2 9a 62 88  b9 69 2f 93 08 2c 55 c4  |4.^i..b..i/..,U.|
-00000110  5d 0d cb 92 ac 2c 30 27  83 11 68 9f 74 35 5d 3a  |]....,0'..h.t5]:|
-00000120  96 4c 57 91 95 a8 e7 03  fa b7 ae 8b 94 e3 39 38  |.LW...........98|
-00000130  6d e1 ad b1 f7 26 2c 90  d4 3c eb a5 5e df e4 29  |m....&,..<..^..)|
-00000140  39 ff ba d2 04 f4 b4 9c  fa c2 da 34 bc 04 32 07  |9..........4..2.|
-00000150  db 52 38 fd 92 89 4c e9  50 13 e5 90 e7 f1 88 5e  |.R8...L.P......^|
-00000160  c1 7a 9b fa 6e 1f 99 ce  52 77 0c 03 d8 a6 5d 64  |.z..n...Rw....]d|
-00000170  ab 58 82 93 10 a1 4f 35  ea a3 6d af a9 64 17 3d  |.X....O5..m..d.=|
-00000180  fc a8 d8 9e 7e d7 44 af  2a c1 d6 a8 4d 78 b3 0b  |....~.D.*...Mx..|
-00000190  d1 0b 3d 54 e2 c8 df 84  61 cb 92 1a d8 ce 23 a3  |..=T....a.....#.|
-000001a0  68 f7 af 40 53 09 f0 cc  00 7d 39 83 2c 6d f4 44  |h.. at S....}9.,m.D|
-000001b0  d6 95 59 06 0a ef 9c 74  39 b3 70 cb 0a 0c 13 cd  |..Y....t9.p.....|
-000001c0  ec 1f bf 75 93 01 1a 35  68 75 8b 80 15 80 7d a9  |...u...5hu....}.|
-000001d0  d0 25 9a 52 bc 02 bf 71  eb 60 76 2a 74 90 c8 16  |.%.R...q.`v*t...|
-000001e0  80 03 c2 a8 0c be 94 7c  12 b0 ee 45 3a 38 09 5a  |.......|...E:8.Z|
-000001f0  bf 8b ca 78 f3 9e 79 8a  9f 65 57 84 f8 33 79 2a  |...x..y..eW..3y*|
-00000200  f8 8c e0 c8 4b 9e 12 19  b1 3f ba cf 9d db 48 13  |....K....?....H.|
-00000210  b4 b0 53 0e 7a 6b 1d 21  13 45 37 8d 90 75 88 f9  |..S.zk.!.E7..u..|
-00000220  b5 9d 41 d0 ee 95 5f 6a  e5 96 b6 48 ce 3b 43 20  |..A..._j...H.;C |
-00000230  47 15 db eb ba af 6d bf  38 26 e7 ad 86 ba 1e 91  |G.....m.8&......|
-00000240  be 8b df ba 5c 30 6e 3c  13 6a 96 68 13 24 bf 06  |....\0n<.j.h.$..|
-00000250  f1 d2 b0 05 8f 8e 21 7f  6a 09 5f b8 be 0b c5 5f  |......!.j._...._|
-00000260  67 60 94 ec 78 65 6f 70  94 9b 15 82 07 f4 88 fb  |g`..xeop........|
-00000270  a2 94 68 f7 57 0a 9c ec  ab 3f 8f d5 83 ec 6a 24  |..h.W....?....j$|
-00000280  6f 88 4f 22 7f a1 82 cb  ef ec 4c 33 b9 c1 16 03  |o.O"......L3....|
-00000290  03 00 5e 00 00 00 00 00  00 00 04 34 f9 69 a5 83  |..^........4.i..|
-000002a0  c5 86 34 51 f0 07 5b 44  51 36 c1 0d f7 71 c7 1b  |..4Q..[DQ6...q..|
-000002b0  70 27 aa 35 cd c7 10 76  fd 96 27 dc bc 6f 39 ff  |p'.5...v..'..o9.|
-000002c0  f1 a7 de e3 c5 21 70 e9  70 b1 52 d2 f0 be c0 72  |.....!p.p.R....r|
-000002d0  e5 aa 2b 1a 1d a8 8f 10  37 b5 2f c7 b9 32 c8 3c  |..+.....7./..2.<|
-000002e0  7c c8 11 a5 dc aa 84 12  57 f1 ff 3b f9 04 a4 29  ||.......W..;...)|
-000002f0  24 16 03 03 00 a0 00 00  00 00 00 00 00 05 1a 86  |$...............|
-00000300  c7 35 6f 23 c5 38 85 85  0e 31 df 33 1a 42 6e f8  |.5o#.8...1.3.Bn.|
-00000310  c3 f7 81 29 aa 03 85 8c  5a 8a e1 9b 1c d3 6f 7d  |...)....Z.....o}|
-00000320  36 41 45 30 06 2b dd 19  dc 22 9e 9e d4 bc 0e 51  |6AE0.+...".....Q|
-00000330  65 59 e9 7e 1b a1 d1 54  4b 3c 9a 41 de b9 43 98  |eY.~...TK<.A..C.|
-00000340  a5 ef 7a b8 77 69 f7 a5  80 02 d6 46 73 96 89 46  |..z.wi.....Fs..F|
-00000350  43 3a d7 ae 21 64 db 05  b5 7d fc 83 a3 75 ba ad  |C:..!d...}...u..|
-00000360  0d d2 d6 9b 51 3b cb 37  85 46 92 b5 57 eb 2c dc  |....Q;.7.F..W.,.|
-00000370  b2 8f e2 c0 7f 29 bf 5e  bd f0 26 dd 31 e4 31 af  |.....).^..&.1.1.|
-00000380  09 51 e4 26 09 56 a2 f4  5d fc c5 cb c8 da 51 ee  |.Q.&.V..].....Q.|
-00000390  35 2e bb 3e ee bb 14 03  03 00 19 00 00 00 00 00  |5..>............|
-000003a0  00 00 06 b5 3d 07 af c9  3f ad f0 25 b4 5e b9 0f  |....=...?..%.^..|
-000003b0  fa f0 16 48 16 03 03 00  28 00 00 00 00 00 00 00  |...H....(.......|
-000003c0  00 43 54 67 b4 f1 0e 1d  9d 7f ab f7 4c b6 77 3f  |.CTg........L.w?|
-000003d0  d0 17 da 6a 61 75 a8 c8  42 47 fb 2a f7 22 85 02  |...jau..BG.*."..|
-000003e0  b0                                                |.|
+00000000  16 03 03 02 69 8c c7 01  da 38 a5 36 3d 2c 21 1c  |....i....8.6=,!.|
+00000010  64 1b b8 e7 c2 cd 17 06  b6 51 0e e6 d9 d9 18 c5  |d........Q......|
+00000020  a9 c9 ac 5d 2d 23 f8 15  92 b2 e1 62 6e d7 8d 58  |...]-#.....bn..X|
+00000030  5b d9 b8 26 e5 ec 0f 61  15 3e 12 70 89 0d 3f 4e  |[..&...a.>.p..?N|
+00000040  e3 2e 18 42 7c c7 59 7b  e1 48 d9 a8 cf b1 cd 38  |...B|.Y{.H.....8|
+00000050  17 90 97 89 2e 4f 4b df  58 b0 9f 4e 95 d2 e9 70  |.....OK.X..N...p|
+00000060  6d 0b 82 af b7 05 be 11  26 d8 f9 89 e6 d6 44 f5  |m.......&.....D.|
+00000070  db 7c 8c 91 61 78 dc 68  98 9b 10 17 5b 85 42 93  |.|..ax.h....[.B.|
+00000080  31 a2 16 97 72 c5 f2 d0  81 76 a6 9b b7 9c 14 ab  |1...r....v......|
+00000090  a7 bf 19 f7 34 e3 8f 3f  a5 aa 23 c8 49 07 1b 6f  |....4..?..#.I..o|
+000000a0  e5 5d 65 66 a1 dc d2 e7  bb c2 4b 9e a7 9a dc d6  |.]ef......K.....|
+000000b0  72 42 d3 71 d3 51 a4 3a  82 f7 cd 2a 15 34 da 6d  |rB.q.Q.:...*.4.m|
+000000c0  44 3a a9 7d 6e 4c ce a5  ba 6b 5b 3b 88 8c e1 29  |D:.}nL...k[;...)|
+000000d0  ee a8 17 1b 02 36 8f 68  c9 9e e6 f1 bf e3 e3 e0  |.....6.h........|
+000000e0  cd 6d 7f ff c2 4d 3f 88  c7 9b 75 20 e5 cd fa fa  |.m...M?...u ....|
+000000f0  a0 d7 10 6a c1 00 73 f9  df bd 22 60 8c 82 71 e6  |...j..s..."`..q.|
+00000100  56 aa 90 bf c7 a8 82 51  e7 23 42 ec 99 f5 b9 aa  |V......Q.#B.....|
+00000110  3c cc c6 32 11 29 1f a6  ae 89 03 04 e8 de 9f f4  |<..2.)..........|
+00000120  bd 87 ae af 91 ee a2 f3  e2 6d 7b 87 ad 67 16 2d  |.........m{..g.-|
+00000130  ad 92 34 38 52 ed 7c 38  92 45 16 26 9f 65 d2 67  |..48R.|8.E.&.e.g|
+00000140  3e 33 a1 bd b2 f6 d3 c8  76 96 52 11 0d 8d ac a6  |>3......v.R.....|
+00000150  27 10 6a 43 63 5f 82 41  e7 fe 91 24 68 70 bd 2c  |'.jCc_.A...$hp.,|
+00000160  35 fd 0e 49 ec 3a dd f3  c0 af 5c f4 61 9a 2a 00  |5..I.:....\.a.*.|
+00000170  59 b5 28 24 f0 cf d3 25  bc 77 65 74 04 ee 4b 5e  |Y.($...%.wet..K^|
+00000180  2b 9f 1d 27 e2 dd 1a ed  ab e5 ff d6 1a 55 d7 4d  |+..'.........U.M|
+00000190  5c da 14 96 21 43 f6 c3  2d 78 e5 75 60 69 26 ce  |\...!C..-x.u`i&.|
+000001a0  7a 66 5e 42 91 0e ef 41  c2 c4 e6 15 8a 9a 17 a1  |zf^B...A........|
+000001b0  d9 23 2c cc c7 81 00 71  b0 52 ec 4e ea eb f9 75  |.#,....q.R.N...u|
+000001c0  2e 87 16 b4 ba 25 8c 09  f1 23 f9 ee ea db 0e b5  |.....%...#......|
+000001d0  d0 dd 47 9b b6 06 a3 f3  5e 0d 34 5a ba 76 cd 0a  |..G.....^.4Z.v..|
+000001e0  b1 9f 8a 99 aa d3 02 2e  b6 04 7b c5 d3 2f dc d7  |..........{../..|
+000001f0  68 af 6b 88 90 0a 94 a4  29 65 0b ba b3 da f2 cd  |h.k.....)e......|
+00000200  51 93 4f ea b4 f8 54 c7  28 e3 2d 63 d0 62 54 d9  |Q.O...T.(.-c.bT.|
+00000210  27 a0 85 57 7b a2 f2 f5  a5 25 83 1b e2 36 15 06  |'..W{....%...6..|
+00000220  41 ae e1 f9 ca a5 c6 59  2d da 4a ed 10 7b 80 01  |A......Y-.J..{..|
+00000230  06 39 f2 a8 4b 22 37 4d  aa 84 79 85 71 29 1b 4e  |.9..K"7M..y.q).N|
+00000240  c3 79 af 13 f5 4e 3c 6d  fa 8c d7 55 13 2b 48 3d  |.y...N<m...U.+H=|
+00000250  9a 79 e2 b4 3f 59 f8 f9  6c d2 39 51 e0 6e c2 c3  |.y..?Y..l.9Q.n..|
+00000260  09 06 d4 e0 4f 76 a6 54  b8 9d ef 30 ba 80 16 03  |....Ov.T...0....|
+00000270  03 00 35 df a1 cc 0f 77  42 24 fe 38 ac ec 25 cf  |..5....wB$.8..%.|
+00000280  13 85 03 87 73 b4 c0 d9  97 d0 a8 8e 12 f4 13 61  |....s..........a|
+00000290  42 40 03 a6 b6 6c d3 dd  d9 92 f0 e9 bc bc a7 22  |B at ...l........."|
+000002a0  64 cf 4b 00 97 71 ac 3b  16 03 03 00 98 f7 61 85  |d.K..q.;......a.|
+000002b0  47 fc 23 b5 34 4b 1f 10  c7 12 51 07 a2 40 40 48  |G.#.4K....Q..@@H|
+000002c0  1c 79 52 3a a7 9d ca 45  62 a2 53 71 bd 23 0a 77  |.yR:...Eb.Sq.#.w|
+000002d0  0b 7f 8c ff f2 51 da 91  07 d7 67 71 bb ec 02 8d  |.....Q....gq....|
+000002e0  3f 79 dc d9 af f4 4c 78  3e ac 8d 30 ed eb c9 19  |?y....Lx>..0....|
+000002f0  96 89 a7 0f 7c fb 96 18  84 86 e7 bd e2 35 31 0c  |....|........51.|
+00000300  7a 51 d7 94 6b 61 62 7a  6a d8 56 62 e6 cf bf 60  |zQ..kabzj.Vb...`|
+00000310  df 7a c5 ce d3 87 ea 2f  5a ad 90 d4 39 f7 47 8e  |.z...../Z...9.G.|
+00000320  8b d3 6b 8e e0 3f 2f 59  71 e4 e6 bf 3f 4a 29 a8  |..k..?/Yq...?J).|
+00000330  60 df 1b 5c 2d 21 ab 0a  a5 9f 5a a2 a3 d6 08 3c  |`..\-!....Z....<|
+00000340  4a 4b f9 d6 a0 14 03 03  00 11 44 a4 62 7e c1 4a  |JK........D.b~.J|
+00000350  4e 56 dd 08 65 b2 ab 12  cd fa 8d 16 03 03 00 20  |NV..e.......... |
+00000360  b4 52 5a e8 33 b6 23 b1  b4 e6 59 da b0 84 52 94  |.RZ.3.#...Y...R.|
+00000370  70 de dc 02 f6 41 e3 27  7c 27 56 6a 7c 92 e3 48  |p....A.'|'Vj|..H|
 >>> Flow 10 (server to client)
-00000000  14 03 03 00 19 59 c3 19  1f ed d1 1b 5a 9d 51 67  |.....Y......Z.Qg|
-00000010  ff ed 61 fd 01 85 c2 46  f1 26 e1 08 c3 5b 16 03  |..a....F.&...[..|
-00000020  03 00 28 02 08 83 98 20  78 eb a5 8e f5 d3 31 b6  |..(.... x.....1.|
-00000030  6d 4b 3a 9c cd 76 30 ca  92 4b 6c 17 2c d8 c5 d5  |mK:..v0..Kl.,...|
-00000040  7d 59 76 f8 ff 0c 8f f4  f6 fb 81 17 03 03 00 21  |}Yv............!|
-00000050  02 08 83 98 20 78 eb a6  9b c6 07 33 3b 43 e4 5b  |.... x.....3;C.[|
-00000060  c6 d4 31 6e 2b 5b 4a 65  c2 0a df 27 02 a2 3e 3b  |..1n+[Je...'..>;|
-00000070  04 16 03 03 00 1c 02 08  83 98 20 78 eb a7 43 45  |.......... x..CE|
-00000080  df 9b 74 94 81 17 21 b1  7d d5 c0 7a 2b cc 38 a1  |..t...!.}..z+.8.|
-00000090  30 1d                                             |0.|
+00000000  14 03 03 00 11 c7 92 b3  aa a0 91 21 4f 42 96 0c  |...........!OB..|
+00000010  3a 92 c3 53 55 d1 16 03  03 00 20 4b da e5 1c 08  |:..SU..... K....|
+00000020  ce a7 33 f1 a6 c7 47 52  19 68 b4 f5 1d 66 a7 38  |..3...GR.h...f.8|
+00000030  97 45 43 9f ca b5 db 2c  14 fc f4 17 03 03 00 19  |.EC....,........|
+00000040  28 f4 bb bf c1 5a 2d 1e  b8 fc c7 fc 55 16 e9 cc  |(....Z-.....U...|
+00000050  43 a3 63 58 7e 2c 60 77  23 16 03 03 00 14 2c e6  |C.cX~,`w#.....,.|
+00000060  25 0a b7 26 7b 13 55 62  f1 fe 6e fe 0e 57 53 57  |%..&{.Ub..n..WSW|
+00000070  19 1b                                             |..|
 >>> Flow 11 (client to server)
-00000000  15 03 03 00 1a 00 00 00  00 00 00 00 01 d6 2b 5a  |..............+Z|
-00000010  7d c9 ba d3 94 cc 45 26  1c 1e 1e 70 39 6c 4e 15  |}.....E&...p9lN.|
-00000020  03 03 00 1a 00 00 00 00  00 00 00 02 fd 76 21 e8  |.............v!.|
-00000030  b5 16 14 43 36 9f 61 14  6d 40 76 e3 14 11        |...C6.a.m at v...|
+00000000  15 03 03 00 12 dd 2b 00  09 fd 8c 7d 21 3d 7c 06  |......+....}!=|.|
+00000010  93 ca c9 21 b2 3e 20 15  03 03 00 12 90 32 4b 3b  |...!.> ......2K;|
+00000020  33 4d fd 69 55 81 aa 42  16 ae 47 b9 4c 06        |3M.iU..B..G.L.|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-RenegotiationRejected b/src/crypto/tls/testdata/Client-TLSv12-RenegotiationRejected
index 90adc18..cb96432 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-RenegotiationRejected
+++ b/src/crypto/tls/testdata/Client-TLSv12-RenegotiationRejected
@@ -1,97 +1,89 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 b1 7d c5 82 a4  |....Y...U...}...|
-00000010  f7 1d 3a b9 c0 da 13 7c  2f 75 22 a4 5f 2e 58 2a  |..:....|/u"._.X*|
-00000020  39 eb 18 7c bb 0d 98 ba  51 2e 4a 20 41 40 2f 53  |9..|....Q.J A@/S|
-00000030  bc 16 e0 a4 44 07 f0 5e  8f 43 a3 69 87 0b 94 dd  |....D..^.C.i....|
-00000040  60 a0 20 d0 25 e1 a1 a0  b8 0d d8 00 c0 2f 00 00  |`. .%......../..|
+00000000  16 03 03 00 59 02 00 00  55 03 03 91 a6 bc 02 ab  |....Y...U.......|
+00000010  19 62 2c de 45 57 ba 71  c0 b0 4d 78 5e f4 c2 b9  |.b,.EW.q..Mx^...|
+00000020  81 ba 8b d6 b1 9b c8 fb  0c 7c 40 20 dc 66 80 5b  |.........|@ .f.[|
+00000030  20 3c 60 65 7f 9e 0c 67  a8 f3 22 c9 c5 48 80 fa  | <`e...g.."..H..|
+00000040  02 a1 1a 48 6d 1c 46 07  db 6c 8e 85 cc a8 00 00  |...Hm.F..l......|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
-00000060  03 02 71 0b 00 02 6d 00  02 6a 00 02 67 30 82 02  |..q...m..j..g0..|
-00000070  63 30 82 01 cc a0 03 02  01 02 02 09 00 a2 73 00  |c0............s.|
-00000080  0c 81 00 cb f3 30 0d 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000090  01 0b 05 00 30 2b 31 17  30 15 06 03 55 04 0a 13  |....0+1.0...U...|
-000000a0  0e 47 6f 6f 67 6c 65 20  54 45 53 54 49 4e 47 31  |.Google TESTING1|
-000000b0  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
-000000c0  74 30 1e 17 0d 31 35 30  31 30 31 30 30 30 30 30  |t0...15010100000|
-000000d0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
-000000e0  5a 30 26 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |Z0&1.0...U....Go|
-000000f0  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 0b 30 09  |ogle TESTING1.0.|
-00000100  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
-00000110  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
-00000120  81 89 02 81 81 00 af 87  88 f6 20 1b 95 65 6c 14  |.......... ..el.|
-00000130  ab 44 05 af 3b 45 14 e3  b7 6d fd 00 63 4d 95 7f  |.D..;E...m..cM..|
-00000140  fe 6a 62 35 86 c0 4a f9  18 7c f6 aa 25 5e 7a 64  |.jb5..J..|..%^zd|
-00000150  31 66 00 ba f4 8e 92 af  c7 6b d8 76 d4 f3 5f 41  |1f.......k.v.._A|
-00000160  cb 6e 56 15 97 1b 97 c1  3c 12 39 21 66 3d 2b 16  |.nV.....<.9!f=+.|
-00000170  d1 bc db 1c c0 a7 da b7  ca ad ba da cb d5 21 50  |..............!P|
-00000180  ec de 8d ab d1 6b 81 4b  89 02 f3 c4 be c1 6c 89  |.....k.K......l.|
-00000190  b1 44 84 bd 21 d1 04 7d  9d 16 4d f9 82 15 f6 ef  |.D..!..}..M.....|
-000001a0  fa d6 09 47 f2 fb 02 03  01 00 01 a3 81 93 30 81  |...G..........0.|
-000001b0  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
-000001c0  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
-000001d0  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
-000001e0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
-000001f0  06 03 55 1d 0e 04 12 04  10 12 50 8d 89 6f 1b d1  |..U.......P..o..|
-00000200  dc 54 4d 6e cb 69 5e 06  f4 30 1b 06 03 55 1d 23  |.TMn.i^..0...U.#|
-00000210  04 14 30 12 80 10 bf 3d  b6 a9 66 f2 b8 40 cf ea  |..0....=..f.. at ..|
-00000220  b4 03 78 48 1a 41 30 19  06 03 55 1d 11 04 12 30  |..xH.A0...U....0|
-00000230  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
-00000240  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
-00000250  03 81 81 00 92 7c af 91  55 12 18 96 59 31 a6 48  |.....|..U...Y1.H|
-00000260  40 d5 2d d5 ee bb 02 a0  f5 c2 1e 7c 9b b3 30 7d  |@.-........|..0}|
-00000270  3c dc 76 da 4f 3d c0 fa  ae 2d 33 24 6b 03 7b 1b  |<.v.O=...-3$k.{.|
-00000280  67 59 11 21 b5 11 bc 77  b9 d9 e0 6e a8 2d 2e 35  |gY.!...w...n.-.5|
-00000290  fa 64 5f 22 3e 63 10 6b  be ff 14 86 6d 0d f0 15  |.d_">c.k....m...|
-000002a0  31 a8 14 38 1e 3b 84 87  2c cb 98 ed 51 76 b9 b1  |1..8.;..,...Qv..|
-000002b0  4f dd db 9b 84 04 86 40  fa 51 dd ba b4 8d eb e3  |O...... at .Q......|
-000002c0  46 de 46 b9 4f 86 c7 f9  a4 c2 41 34 ac cc f6 ea  |F.F.O.....A4....|
-000002d0  b0 ab 39 18 16 03 03 00  cd 0c 00 00 c9 03 00 17  |..9.............|
-000002e0  41 04 62 2a a7 2d 1f 7a  8d 7e 8a 9e 84 db df e2  |A.b*.-.z.~......|
-000002f0  7c 35 d8 a1 9f ec 23 ef  c7 c2 9a c5 45 02 6f eb  ||5....#.....E.o.|
-00000300  24 ed 77 e1 ca fe 9a be  06 1e ea 30 5a e7 13 00  |$.w........0Z...|
-00000310  47 52 a4 a2 d8 ee 9d 4e  87 f5 48 83 6f 5d 8e 02  |GR.....N..H.o]..|
-00000320  ff f5 04 01 00 80 19 f6  63 a1 47 d1 cf 4d 28 73  |........c.G..M(s|
-00000330  4e 31 03 78 b5 17 ba 53  64 d0 b8 3f 04 77 9d 6b  |N1.x...Sd..?.w.k|
-00000340  85 d0 d4 1e 02 90 b9 ab  10 dc d7 b1 79 1b 12 80  |............y...|
-00000350  e1 5a 4b 69 80 2d 2a 37  4c fd 72 a9 c3 8e 2a 1f  |.ZKi.-*7L.r...*.|
-00000360  1a 3f 74 49 c6 49 ce 2f  02 58 3f 68 f0 f6 b5 8a  |.?tI.I./.X?h....|
-00000370  16 11 8b 63 15 6a f2 91  f1 74 a8 f0 6d dc 91 0a  |...c.j...t..m...|
-00000380  b4 e2 4e 10 14 1d b9 da  05 29 bf 31 30 ee 7d a5  |..N......).10.}.|
-00000390  75 4e da ff db 43 04 a7  55 4b dd 93 4c 5f 32 be  |uN...C..UK..L_2.|
-000003a0  e9 23 c9 a1 23 86 16 03  03 00 04 0e 00 00 00     |.#..#..........|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 f5 fd 54 ea 3e bb 70  |........ ..T.>.p|
+000002d0  fb 8e fb e4 8a 25 1f 9d  3d 9a fb fb 9d ff d1 52  |.....%..=......R|
+000002e0  81 9d b0 ea a1 e6 b0 87  2f 04 01 00 80 77 54 16  |......../....wT.|
+000002f0  98 5d 22 c4 7f 9b 2a 44  dd e4 0d 78 c2 60 6a ad  |.]"...*D...x.`j.|
+00000300  91 9d d9 ed 93 0b 4e b4  c6 26 f1 94 6d e0 cc f4  |......N..&..m...|
+00000310  8d fa 9c ec 70 f5 5b ac  80 d7 5e 4f 49 04 bc 24  |....p.[...^OI..$|
+00000320  8e 0a 7d 44 e1 7e 47 1e  a8 68 d1 fe 6f 41 0d 4a  |..}D.~G..h..oA.J|
+00000330  e5 5b f6 f6 a3 af 76 21  56 1a 25 d2 03 3c f4 dd  |.[....v!V.%..<..|
+00000340  0c 13 ce 56 8a 61 6f 5b  8c a1 04 43 82 87 64 20  |...V.ao[...C..d |
+00000350  4a 3b ec 90 d7 59 aa ca  08 3a 39 57 f1 56 57 6a  |J;...Y...:9W.VWj|
+00000360  18 c9 14 7f e3 8d 83 0f  e2 0b 4d 24 01 16 03 03  |..........M$....|
+00000370  00 04 0e 00 00 00                                 |......|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 d4 cb  |.....(..........|
-00000060  e2 c0 1e fe cb b0 d6 fe  da 7c 8f 8c b2 2f f7 c1  |.........|.../..|
-00000070  3d e9 52 6e 70 c1 13 13  87 ff 12 85 6c 2c        |=.Rnp.......l,|
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 20 6b ce 5c  2f df 85 e7 5e fa 51 48  |.... k.\/...^.QH|
+00000040  f9 31 a5 02 64 c7 1e b1  2e f2 6b 86 30 43 23 91  |.1..d.....k.0C#.|
+00000050  76 6b 40 74 2b                                    |vk at t+|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 0a 86 ff b2 73  |..........(....s|
-00000010  35 40 a1 89 9f 21 1f 0b  2f 79 50 70 eb 74 e1 2f  |5 at ...!../yPp.t./|
-00000020  4d bc 5c 3c 85 0b 60 cc  73 36 e4 08 01 0a 4c 75  |M.\<..`.s6....Lu|
-00000030  0f a2 9c                                          |...|
+00000000  14 03 03 00 01 01 16 03  03 00 20 e7 1e 88 10 2d  |.......... ....-|
+00000010  dc 35 6d 2b 4a 91 39 5d  5c 46 ed 2e 45 6f 41 38  |.5m+J.9]\F..EoA8|
+00000020  66 0f 15 58 f8 af d8 a6  6c 99 61                 |f..X....l.a|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 c9 78 b7  |..............x.|
-00000010  07 d1 a9 95 fc b4 aa 57  16 77 86 fb c7 a9 c6 12  |.......W.w......|
-00000020  bc bd 09                                          |...|
+00000000  17 03 03 00 16 ab 2a df  2f 3c 07 6a 24 98 55 0b  |......*./<.j$.U.|
+00000010  67 20 2d 92 cd 9a 44 74  da fd 6a                 |g -...Dt..j|
 >>> Flow 6 (server to client)
-00000000  16 03 03 00 1c 0a 86 ff  b2 73 35 40 a2 4d b1 9b  |.........s5 at .M..|
-00000010  eb 51 76 71 6b b8 88 fe  21 60 bb 8b 2a cc e3 3e  |.Qvqk...!`..*..>|
-00000020  d5                                                |.|
+00000000  16 03 03 00 14 d6 fb e7  9a 76 2a 6f e1 e9 33 1a  |.........v*o..3.|
+00000010  77 07 fd 7f 98 af 1e 04  43                       |w.......C|
 >>> Flow 7 (client to server)
-00000000  15 03 03 00 1a 00 00 00  00 00 00 00 02 0e da c6  |................|
-00000010  01 09 cc 0f bb 7d de c9  41 8d 30 b5 d5 b7 f2 15  |.....}..A.0.....|
-00000020  03 03 00 1a 00 00 00 00  00 00 00 03 a7 0e 24 98  |..............$.|
-00000030  32 62 1b a9 98 17 b6 b3  71 af 88 7a a3 6b        |2b......q..z.k|
+00000000  15 03 03 00 12 7e e3 20  96 03 31 8c 6a 31 f8 62  |.....~. ..1.j1.b|
+00000010  02 a7 a4 ce 77 83 c1 15  03 03 00 12 b9 91 75 45  |....w.........uE|
+00000020  a5 4a f9 c6 6d b2 5c c3  0a 1a 26 63 00 04        |.J..m.\...&c..|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-SCT b/src/crypto/tls/testdata/Client-TLSv12-SCT
index e6187ba..a0f6a09 100644
--- a/src/crypto/tls/testdata/Client-TLSv12-SCT
+++ b/src/crypto/tls/testdata/Client-TLSv12-SCT
@@ -1,19 +1,20 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 85 01 00 00  81 03 03 00 00 00 00 00  |................|
+00000000  16 03 01 00 91 01 00 00  8d 03 03 00 00 00 00 00  |................|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 22 c0 2f  |............."./|
-00000030  c0 2b c0 30 c0 2c c0 11  c0 07 c0 13 c0 09 c0 14  |.+.0.,..........|
-00000040  c0 0a 00 9c 00 9d 00 05  00 2f 00 35 c0 12 00 0a  |........./.5....|
-00000050  01 00 00 36 00 05 00 05  01 00 00 00 00 00 0a 00  |...6............|
-00000060  08 00 06 00 17 00 18 00  19 00 0b 00 02 01 00 00  |................|
-00000070  0d 00 0e 00 0c 04 01 04  03 05 01 05 03 02 01 02  |................|
-00000080  03 ff 01 00 01 00 00 12  00 00                    |..........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 38 00 05  |.............8..|
+00000060  00 05 01 00 00 00 00 00  0a 00 0a 00 08 00 1d 00  |................|
+00000070  17 00 18 00 19 00 0b 00  02 01 00 00 0d 00 0e 00  |................|
+00000080  0c 04 01 04 03 05 01 05  03 02 01 02 03 ff 01 00  |................|
+00000090  01 00 00 12 00 00                                 |......|
 >>> Flow 2 (server to client)
-00000000  16 03 03 01 c6 02 00 01  c2 03 03 5d d8 84 38 51  |...........]..8Q|
-00000010  c6 51 9e 6c d3 e0 b2 d7  81 2a 9b 1c 06 0b 11 c8  |.Q.l.....*......|
-00000020  54 90 f3 d1 66 83 7a 68  2f 65 8b 20 ac 8b 35 9a  |T...f.zh/e. ..5.|
-00000030  31 25 04 c9 89 31 27 80  8f 10 74 8e 3c 4f 20 bc  |1%...1'...t.<O .|
-00000040  3b 46 9d d0 91 f3 ca 7e  0e 59 b7 72 c0 2f 00 01  |;F.....~.Y.r./..|
+00000000  16 03 03 01 c6 02 00 01  c2 03 03 08 db 5c c4 da  |.............\..|
+00000010  fe 2c a2 21 0d c4 9e c4  14 b9 e3 15 d7 c5 2c 84  |.,.!..........,.|
+00000020  f2 b8 0e 32 67 9e 72 08  9c 17 6b 20 86 09 60 52  |...2g.r...k ..`R|
+00000030  9d 53 ba c8 8a c3 1a 11  c0 e5 c6 a0 59 49 ed cb  |.S..........YI..|
+00000040  e0 6f 0a 56 e9 4f bf 02  2f 23 10 b8 cc a8 00 01  |.o.V.O../#......|
 00000050  7a ff 01 00 01 00 00 0b  00 04 03 00 01 02 00 12  |z...............|
 00000060  01 69 01 67 00 75 00 a4  b9 09 90 b4 18 58 14 87  |.i.g.u.......X..|
 00000070  bb 13 a2 cc 67 70 0a 3c  35 98 04 f9 1b df b8 e3  |....gp.<5.......|
@@ -37,77 +38,70 @@
 00000190  91 bc f1 b5 40 be 1e 2e  e7 5c b4 74 27 ed 8f 9b  |.... at ....\.t'...|
 000001a0  02 e9 fa c2 4c ba a2 be  02 21 00 af 43 64 52 71  |....L....!..CdRq|
 000001b0  15 29 58 40 91 c7 08 16  96 03 a8 73 a5 65 a0 6c  |.)X at .......s.e.l|
-000001c0  b8 48 56 5a b6 29 83 64  6d 2a 9d 16 03 03 02 71  |.HVZ.).dm*.....q|
-000001d0  0b 00 02 6d 00 02 6a 00  02 67 30 82 02 63 30 82  |...m..j..g0..c0.|
-000001e0  01 cc a0 03 02 01 02 02  09 00 a2 73 00 0c 81 00  |...........s....|
-000001f0  cb f3 30 0d 06 09 2a 86  48 86 f7 0d 01 01 0b 05  |..0...*.H.......|
-00000200  00 30 2b 31 17 30 15 06  03 55 04 0a 13 0e 47 6f  |.0+1.0...U....Go|
-00000210  6f 67 6c 65 20 54 45 53  54 49 4e 47 31 10 30 0e  |ogle TESTING1.0.|
-00000220  06 03 55 04 03 13 07 47  6f 20 52 6f 6f 74 30 1e  |..U....Go Root0.|
-00000230  17 0d 31 35 30 31 30 31  30 30 30 30 30 30 5a 17  |..150101000000Z.|
-00000240  0d 32 35 30 31 30 31 30  30 30 30 30 30 5a 30 26  |.250101000000Z0&|
-00000250  31 17 30 15 06 03 55 04  0a 13 0e 47 6f 6f 67 6c  |1.0...U....Googl|
-00000260  65 20 54 45 53 54 49 4e  47 31 0b 30 09 06 03 55  |e TESTING1.0...U|
-00000270  04 03 13 02 47 6f 30 81  9f 30 0d 06 09 2a 86 48  |....Go0..0...*.H|
-00000280  86 f7 0d 01 01 01 05 00  03 81 8d 00 30 81 89 02  |............0...|
-00000290  81 81 00 af 87 88 f6 20  1b 95 65 6c 14 ab 44 05  |....... ..el..D.|
-000002a0  af 3b 45 14 e3 b7 6d fd  00 63 4d 95 7f fe 6a 62  |.;E...m..cM...jb|
-000002b0  35 86 c0 4a f9 18 7c f6  aa 25 5e 7a 64 31 66 00  |5..J..|..%^zd1f.|
-000002c0  ba f4 8e 92 af c7 6b d8  76 d4 f3 5f 41 cb 6e 56  |......k.v.._A.nV|
-000002d0  15 97 1b 97 c1 3c 12 39  21 66 3d 2b 16 d1 bc db  |.....<.9!f=+....|
-000002e0  1c c0 a7 da b7 ca ad ba  da cb d5 21 50 ec de 8d  |...........!P...|
-000002f0  ab d1 6b 81 4b 89 02 f3  c4 be c1 6c 89 b1 44 84  |..k.K......l..D.|
-00000300  bd 21 d1 04 7d 9d 16 4d  f9 82 15 f6 ef fa d6 09  |.!..}..M........|
-00000310  47 f2 fb 02 03 01 00 01  a3 81 93 30 81 90 30 0e  |G..........0..0.|
-00000320  06 03 55 1d 0f 01 01 ff  04 04 03 02 05 a0 30 1d  |..U...........0.|
-00000330  06 03 55 1d 25 04 16 30  14 06 08 2b 06 01 05 05  |..U.%..0...+....|
-00000340  07 03 01 06 08 2b 06 01  05 05 07 03 02 30 0c 06  |.....+.......0..|
-00000350  03 55 1d 13 01 01 ff 04  02 30 00 30 19 06 03 55  |.U.......0.0...U|
-00000360  1d 0e 04 12 04 10 12 50  8d 89 6f 1b d1 dc 54 4d  |.......P..o...TM|
-00000370  6e cb 69 5e 06 f4 30 1b  06 03 55 1d 23 04 14 30  |n.i^..0...U.#..0|
-00000380  12 80 10 bf 3d b6 a9 66  f2 b8 40 cf ea b4 03 78  |....=..f.. at ....x|
-00000390  48 1a 41 30 19 06 03 55  1d 11 04 12 30 10 82 0e  |H.A0...U....0...|
-000003a0  65 78 61 6d 70 6c 65 2e  67 6f 6c 61 6e 67 30 0d  |example.golang0.|
-000003b0  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 03 81 81  |..*.H...........|
-000003c0  00 92 7c af 91 55 12 18  96 59 31 a6 48 40 d5 2d  |..|..U...Y1.H at .-|
-000003d0  d5 ee bb 02 a0 f5 c2 1e  7c 9b b3 30 7d 3c dc 76  |........|..0}<.v|
-000003e0  da 4f 3d c0 fa ae 2d 33  24 6b 03 7b 1b 67 59 11  |.O=...-3$k.{.gY.|
-000003f0  21 b5 11 bc 77 b9 d9 e0  6e a8 2d 2e 35 fa 64 5f  |!...w...n.-.5.d_|
-00000400  22 3e 63 10 6b be ff 14  86 6d 0d f0 15 31 a8 14  |">c.k....m...1..|
-00000410  38 1e 3b 84 87 2c cb 98  ed 51 76 b9 b1 4f dd db  |8.;..,...Qv..O..|
-00000420  9b 84 04 86 40 fa 51 dd  ba b4 8d eb e3 46 de 46  |.... at .Q......F.F|
-00000430  b9 4f 86 c7 f9 a4 c2 41  34 ac cc f6 ea b0 ab 39  |.O.....A4......9|
-00000440  18 16 03 03 00 cd 0c 00  00 c9 03 00 17 41 04 1e  |.............A..|
-00000450  d1 1c 5c d3 00 41 84 cd  f7 e2 78 ad b5 7d 5b f2  |..\..A....x..}[.|
-00000460  23 5b 1a 18 44 3f 86 8e  3e 52 f2 4b b6 7d 84 b4  |#[..D?..>R.K.}..|
-00000470  1d 98 83 8f 2f 58 07 92  1f 58 2a 8d 8c e3 fa b7  |..../X...X*.....|
-00000480  aa 78 7e 33 9a 64 b9 b6  cb 78 94 be 2b c3 ac 04  |.x~3.d...x..+...|
-00000490  01 00 80 65 9f 42 e3 24  5c cd 18 aa 08 8e 6b bf  |...e.B.$\.....k.|
-000004a0  39 15 2a a3 e6 42 1c 9d  6b 34 39 a2 2c 58 f5 5f  |9.*..B..k49.,X._|
-000004b0  3e fb 2a 4c 01 2b e5 20  4e f5 69 77 c1 62 8f 68  |>.*L.+. N.iw.b.h|
-000004c0  be b4 c4 77 27 c9 4a 97  6d 18 7f 45 fd c9 9e 24  |...w'.J.m..E...$|
-000004d0  19 6b d9 00 c5 52 1a 34  a3 c9 cb eb 92 fc f6 48  |.k...R.4.......H|
-000004e0  3d 89 8a ff 82 be 55 c9  92 e2 24 86 b0 99 c6 e8  |=.....U...$.....|
-000004f0  a5 4c b7 bc 5a e5 f3 81  94 ee 15 47 e7 5e 8c 66  |.L..Z......G.^.f|
-00000500  32 72 7d 81 78 61 fe 25  98 dd 07 a2 92 4c eb ed  |2r}.xa.%.....L..|
-00000510  f1 a7 17 16 03 03 00 04  0e 00 00 00              |............|
+000001c0  b8 48 56 5a b6 29 83 64  6d 2a 9d 16 03 03 02 59  |.HVZ.).dm*.....Y|
+000001d0  0b 00 02 55 00 02 52 00  02 4f 30 82 02 4b 30 82  |...U..R..O0..K0.|
+000001e0  01 b4 a0 03 02 01 02 02  09 00 e8 f0 9d 3f e2 5b  |.............?.[|
+000001f0  ea a6 30 0d 06 09 2a 86  48 86 f7 0d 01 01 0b 05  |..0...*.H.......|
+00000200  00 30 1f 31 0b 30 09 06  03 55 04 0a 13 02 47 6f  |.0.1.0...U....Go|
+00000210  31 10 30 0e 06 03 55 04  03 13 07 47 6f 20 52 6f  |1.0...U....Go Ro|
+00000220  6f 74 30 1e 17 0d 31 36  30 31 30 31 30 30 30 30  |ot0...1601010000|
+00000230  30 30 5a 17 0d 32 35 30  31 30 31 30 30 30 30 30  |00Z..25010100000|
+00000240  30 5a 30 1a 31 0b 30 09  06 03 55 04 0a 13 02 47  |0Z0.1.0...U....G|
+00000250  6f 31 0b 30 09 06 03 55  04 03 13 02 47 6f 30 81  |o1.0...U....Go0.|
+00000260  9f 30 0d 06 09 2a 86 48  86 f7 0d 01 01 01 05 00  |.0...*.H........|
+00000270  03 81 8d 00 30 81 89 02  81 81 00 db 46 7d 93 2e  |....0.......F}..|
+00000280  12 27 06 48 bc 06 28 21  ab 7e c4 b6 a2 5d fe 1e  |.'.H..(!.~...]..|
+00000290  52 45 88 7a 36 47 a5 08  0d 92 42 5b c2 81 c0 be  |RE.z6G....B[....|
+000002a0  97 79 98 40 fb 4f 6d 14  fd 2b 13 8b c2 a5 2e 67  |.y. at .Om..+.....g|
+000002b0  d8 d4 09 9e d6 22 38 b7  4a 0b 74 73 2b c2 34 f1  |....."8.J.ts+.4.|
+000002c0  d1 93 e5 96 d9 74 7b f3  58 9f 6c 61 3c c0 b0 41  |.....t{.X.la<..A|
+000002d0  d4 d9 2b 2b 24 23 77 5b  1c 3b bd 75 5d ce 20 54  |..++$#w[.;.u]. T|
+000002e0  cf a1 63 87 1d 1e 24 c4  f3 1d 1a 50 8b aa b6 14  |..c...$....P....|
+000002f0  43 ed 97 a7 75 62 f4 14  c8 52 d7 02 03 01 00 01  |C...ub...R......|
+00000300  a3 81 93 30 81 90 30 0e  06 03 55 1d 0f 01 01 ff  |...0..0...U.....|
+00000310  04 04 03 02 05 a0 30 1d  06 03 55 1d 25 04 16 30  |......0...U.%..0|
+00000320  14 06 08 2b 06 01 05 05  07 03 01 06 08 2b 06 01  |...+.........+..|
+00000330  05 05 07 03 02 30 0c 06  03 55 1d 13 01 01 ff 04  |.....0...U......|
+00000340  02 30 00 30 19 06 03 55  1d 0e 04 12 04 10 9f 91  |.0.0...U........|
+00000350  16 1f 43 43 3e 49 a6 de  6d b6 80 d7 9f 60 30 1b  |..CC>I..m....`0.|
+00000360  06 03 55 1d 23 04 14 30  12 80 10 48 13 49 4d 13  |..U.#..0...H.IM.|
+00000370  7e 16 31 bb a3 01 d5 ac  ab 6e 7b 30 19 06 03 55  |~.1......n{0...U|
+00000380  1d 11 04 12 30 10 82 0e  65 78 61 6d 70 6c 65 2e  |....0...example.|
+00000390  67 6f 6c 61 6e 67 30 0d  06 09 2a 86 48 86 f7 0d  |golang0...*.H...|
+000003a0  01 01 0b 05 00 03 81 81  00 9d 30 cc 40 2b 5b 50  |..........0. at +[P|
+000003b0  a0 61 cb ba e5 53 58 e1  ed 83 28 a9 58 1a a9 38  |.a...SX...(.X..8|
+000003c0  a4 95 a1 ac 31 5a 1a 84  66 3d 43 d3 2d d9 0b f2  |....1Z..f=C.-...|
+000003d0  97 df d3 20 64 38 92 24  3a 00 bc cf 9c 7d b7 40  |... d8.$:....}.@|
+000003e0  20 01 5f aa d3 16 61 09  a2 76 fd 13 c3 cc e1 0c  | ._...a..v......|
+000003f0  5c ee b1 87 82 f1 6c 04  ed 73 bb b3 43 77 8d 0c  |\.....l..s..Cw..|
+00000400  1c f1 0f a1 d8 40 83 61  c9 4c 72 2b 9d ae db 46  |..... at .a.Lr+...F|
+00000410  06 06 4d f4 c1 b3 3e c0  d1 bd 42 d4 db fe 3d 13  |..M...>...B...=.|
+00000420  60 84 5c 21 d3 3b e9 fa  e7 16 03 03 00 ac 0c 00  |`.\!.;..........|
+00000430  00 a8 03 00 1d 20 27 94  d8 62 00 f3 3f 21 e6 e1  |..... '..b..?!..|
+00000440  0f 1f 2d 9b 37 4d cf 72  34 48 72 2e 85 46 dd b6  |..-.7M.r4Hr..F..|
+00000450  32 23 64 b3 4b 63 04 01  00 80 82 34 e5 7f 70 51  |2#d.Kc.....4..pQ|
+00000460  42 3b ab 51 61 73 1f 2c  64 04 1d 66 96 ff f9 95  |B;.Qas.,d..f....|
+00000470  86 09 a8 75 11 16 34 05  17 fb 96 9c fb 78 40 4c  |...u..4......x at L|
+00000480  10 5b ee 0d 31 a0 77 32  a8 0f 19 ef a4 30 cd 08  |.[..1.w2.....0..|
+00000490  cb f5 ec 36 fa 24 0a ca  0b d8 16 02 d1 34 86 a7  |...6.$.......4..|
+000004a0  f8 e3 cb e6 62 1c cd 40  d6 4f 0c 2f 5b 66 12 6f  |....b.. at .O./[f.o|
+000004b0  8a 2f c4 ef ac 46 86 44  90 e0 65 38 94 4d 5e df  |./...F.D..e8.M^.|
+000004c0  51 a3 40 6e 64 b6 00 6b  88 97 7b 43 78 d9 df 70  |Q. at nd..k..{Cx..p|
+000004d0  fe 66 66 56 82 14 ed ab  08 cd 16 03 03 00 04 0e  |.ffV............|
+000004e0  00 00 00                                          |...|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
-00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 f0 4f  |.....(.........O|
-00000060  fe 22 53 9e e1 61 f4 45  4e 41 ff 5e e4 63 25 f7  |."S..a.ENA.^.c%.|
-00000070  b2 f6 0a ea 89 75 7f d4  e7 3a cc e8 c2 2c        |.....u...:...,|
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 20 7a 58 e1  33 d4 ce ca 57 ef ea b9  |.... zX.3...W...|
+00000040  9d f2 4d ec ce 86 4b e9  c2 b5 64 dd 0f 32 f0 66  |..M...K...d..2.f|
+00000050  65 42 74 d8 59                                    |eBt.Y|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 ad 49 0a 66 16  |..........(.I.f.|
-00000010  6d 64 42 c2 ab 38 bf 81  3d d9 14 13 d6 69 27 81  |mdB..8..=....i'.|
-00000020  ea 5c 53 fd 6c bf 81 6c  06 81 a5 67 f2 cd ed a3  |.\S.l..l...g....|
-00000030  d4 c2 08                                          |...|
+00000000  14 03 03 00 01 01 16 03  03 00 20 27 df 9b 14 a1  |.......... '....|
+00000010  cd a5 83 5b 6b 30 60 a3  ae 8d 64 56 fe 8e 87 a2  |...[k0`...dV....|
+00000020  ff 1b 54 72 c8 7c b2 85  9d 8a de                 |..Tr.|.....|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 5c ab e3  |.............\..|
-00000010  f9 61 72 9e 44 46 1a 05  e9 00 eb 5b e0 73 22 03  |.ar.DF.....[.s".|
-00000020  9f 90 f9 15 03 03 00 1a  00 00 00 00 00 00 00 02  |................|
-00000030  04 28 a4 9d 07 79 95 40  0f f0 eb b9 5d 97 bf 87  |.(...y. at ....]...|
-00000040  4a b6                                             |J.|
+00000000  17 03 03 00 16 c7 bf a9  7a 72 07 27 88 9a ec 1b  |........zr.'....|
+00000010  d3 44 f2 20 88 e4 c2 8b  61 86 5c 15 03 03 00 12  |.D. ....a.\.....|
+00000020  35 ab f5 f6 92 f9 db 23  bf f1 8e e8 65 62 cf 48  |5......#....eb.H|
+00000030  91 9d                                             |..|
diff --git a/src/crypto/tls/testdata/Client-TLSv12-X25519-ECDHE-RSA-AES-GCM b/src/crypto/tls/testdata/Client-TLSv12-X25519-ECDHE-RSA-AES-GCM
new file mode 100644
index 0000000..90541fd
--- /dev/null
+++ b/src/crypto/tls/testdata/Client-TLSv12-X25519-ECDHE-RSA-AES-GCM
@@ -0,0 +1,85 @@
+>>> Flow 1 (client to server)
+00000000  16 03 01 00 8b 01 00 00  87 03 03 00 00 00 00 00  |................|
+00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2c cc a8  |.............,..|
+00000030  cc a9 c0 2f c0 2b c0 30  c0 2c c0 27 c0 13 c0 23  |.../.+.0.,.'...#|
+00000040  c0 09 c0 14 c0 0a 00 9c  00 9d 00 3c 00 2f 00 35  |...........<./.5|
+00000050  c0 12 00 0a 00 05 c0 11  c0 07 01 00 00 32 00 05  |.............2..|
+00000060  00 05 01 00 00 00 00 00  0a 00 04 00 02 00 1d 00  |................|
+00000070  0b 00 02 01 00 00 0d 00  0e 00 0c 04 01 04 03 05  |................|
+00000080  01 05 03 02 01 02 03 ff  01 00 01 00 00 12 00 00  |................|
+>>> Flow 2 (server to client)
+00000000  16 03 03 00 59 02 00 00  55 03 03 07 42 b0 44 05  |....Y...U...B.D.|
+00000010  b1 6d 3c f0 60 fe 6a f2  1f 8f 1d 88 de 4b 6a 1b  |.m<.`.j......Kj.|
+00000020  4f 72 60 4d 42 a5 f7 77  eb 86 c2 20 99 35 47 07  |Or`MB..w... .5G.|
+00000030  64 60 32 52 2e 1d 54 d5  b7 e2 26 85 72 c1 ec 8d  |d`2R..T...&.r...|
+00000040  fb 59 86 91 46 7d ad 16  bd b7 38 94 c0 2f 00 00  |.Y..F}....8../..|
+00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
+00000060  03 02 59 0b 00 02 55 00  02 52 00 02 4f 30 82 02  |..Y...U..R..O0..|
+00000070  4b 30 82 01 b4 a0 03 02  01 02 02 09 00 e8 f0 9d  |K0..............|
+00000080  3f e2 5b ea a6 30 0d 06  09 2a 86 48 86 f7 0d 01  |?.[..0...*.H....|
+00000090  01 0b 05 00 30 1f 31 0b  30 09 06 03 55 04 0a 13  |....0.1.0...U...|
+000000a0  02 47 6f 31 10 30 0e 06  03 55 04 03 13 07 47 6f  |.Go1.0...U....Go|
+000000b0  20 52 6f 6f 74 30 1e 17  0d 31 36 30 31 30 31 30  | Root0...1601010|
+000000c0  30 30 30 30 30 5a 17 0d  32 35 30 31 30 31 30 30  |00000Z..25010100|
+000000d0  30 30 30 30 5a 30 1a 31  0b 30 09 06 03 55 04 0a  |0000Z0.1.0...U..|
+000000e0  13 02 47 6f 31 0b 30 09  06 03 55 04 03 13 02 47  |..Go1.0...U....G|
+000000f0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
+00000100  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 db 46  |.......0.......F|
+00000110  7d 93 2e 12 27 06 48 bc  06 28 21 ab 7e c4 b6 a2  |}...'.H..(!.~...|
+00000120  5d fe 1e 52 45 88 7a 36  47 a5 08 0d 92 42 5b c2  |]..RE.z6G....B[.|
+00000130  81 c0 be 97 79 98 40 fb  4f 6d 14 fd 2b 13 8b c2  |....y. at .Om..+...|
+00000140  a5 2e 67 d8 d4 09 9e d6  22 38 b7 4a 0b 74 73 2b  |..g....."8.J.ts+|
+00000150  c2 34 f1 d1 93 e5 96 d9  74 7b f3 58 9f 6c 61 3c  |.4......t{.X.la<|
+00000160  c0 b0 41 d4 d9 2b 2b 24  23 77 5b 1c 3b bd 75 5d  |..A..++$#w[.;.u]|
+00000170  ce 20 54 cf a1 63 87 1d  1e 24 c4 f3 1d 1a 50 8b  |. T..c...$....P.|
+00000180  aa b6 14 43 ed 97 a7 75  62 f4 14 c8 52 d7 02 03  |...C...ub...R...|
+00000190  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
+000001a0  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
+000001b0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
+000001c0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
+000001d0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
+000001e0  10 9f 91 16 1f 43 43 3e  49 a6 de 6d b6 80 d7 9f  |.....CC>I..m....|
+000001f0  60 30 1b 06 03 55 1d 23  04 14 30 12 80 10 48 13  |`0...U.#..0...H.|
+00000200  49 4d 13 7e 16 31 bb a3  01 d5 ac ab 6e 7b 30 19  |IM.~.1......n{0.|
+00000210  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
+00000220  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
+00000230  86 f7 0d 01 01 0b 05 00  03 81 81 00 9d 30 cc 40  |.............0.@|
+00000240  2b 5b 50 a0 61 cb ba e5  53 58 e1 ed 83 28 a9 58  |+[P.a...SX...(.X|
+00000250  1a a9 38 a4 95 a1 ac 31  5a 1a 84 66 3d 43 d3 2d  |..8....1Z..f=C.-|
+00000260  d9 0b f2 97 df d3 20 64  38 92 24 3a 00 bc cf 9c  |...... d8.$:....|
+00000270  7d b7 40 20 01 5f aa d3  16 61 09 a2 76 fd 13 c3  |}.@ ._...a..v...|
+00000280  cc e1 0c 5c ee b1 87 82  f1 6c 04 ed 73 bb b3 43  |...\.....l..s..C|
+00000290  77 8d 0c 1c f1 0f a1 d8  40 83 61 c9 4c 72 2b 9d  |w....... at .a.Lr+.|
+000002a0  ae db 46 06 06 4d f4 c1  b3 3e c0 d1 bd 42 d4 db  |..F..M...>...B..|
+000002b0  fe 3d 13 60 84 5c 21 d3  3b e9 fa e7 16 03 03 00  |.=.`.\!.;.......|
+000002c0  ac 0c 00 00 a8 03 00 1d  20 cc f6 2e 98 6c e0 8b  |........ ....l..|
+000002d0  15 17 63 6f 97 5e 37 6a  a7 3c 4b f2 d4 91 e0 87  |..co.^7j.<K.....|
+000002e0  53 1d d3 9e f3 43 a9 21  40 04 01 00 80 3c 35 db  |S....C.!@....<5.|
+000002f0  b1 ef 58 96 b4 3f eb 6b  d5 0d b7 ab cd 51 8d 57  |..X..?.k.....Q.W|
+00000300  2b fe 3a 7f 72 42 a0 a7  7d 1d db c1 6c cd df de  |+.:.rB..}...l...|
+00000310  7f 98 69 b0 0b c1 56 07  34 51 79 dc 1a 52 d1 11  |..i...V.4Qy..R..|
+00000320  ea b4 dd 0f 9d 9a 8c a3  4f 23 da 0e aa dc 2a e1  |........O#....*.|
+00000330  16 51 a4 33 e2 4f f8 34  2d b0 ba f5 f5 ed 3e 24  |.Q.3.O.4-.....>$|
+00000340  04 f0 b9 ab 81 b8 4e 39  88 8f b7 46 2c 60 b8 5c  |......N9...F,`.\|
+00000350  6f 4d d4 5d 7a 04 f7 1d  82 98 a2 b1 f9 7e f0 1f  |oM.]z........~..|
+00000360  cf a5 e5 28 25 d4 3d b0  32 ea eb 21 c6 16 03 03  |...(%.=.2..!....|
+00000370  00 04 0e 00 00 00                                 |......|
+>>> Flow 3 (client to server)
+00000000  16 03 03 00 25 10 00 00  21 20 2f e5 7d a3 47 cd  |....%...! /.}.G.|
+00000010  62 43 15 28 da ac 5f bb  29 07 30 ff f6 84 af c4  |bC.(.._.).0.....|
+00000020  cf c2 ed 90 99 5f 58 cb  3b 74 14 03 03 00 01 01  |....._X.;t......|
+00000030  16 03 03 00 28 00 00 00  00 00 00 00 00 75 70 c8  |....(........up.|
+00000040  c5 ef ae 60 b5 8d ba 98  1a 7d 8d c3 e4 32 fc 33  |...`.....}...2.3|
+00000050  5e 15 cc e2 d7 5d d5 76  52 1a fe ac 1e           |^....].vR....|
+>>> Flow 4 (server to client)
+00000000  14 03 03 00 01 01 16 03  03 00 28 7f 2b fe 0d 9f  |..........(.+...|
+00000010  93 07 fd ee 48 76 09 fb  8d 4c dd 7b b5 b5 26 36  |....Hv...L.{..&6|
+00000020  3e 05 e1 1b a7 dc 0b 4a  c0 69 a8 22 33 0b 17 fc  |>......J.i."3...|
+00000030  6f ab b8                                          |o..|
+>>> Flow 5 (client to server)
+00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 49 61 5c  |.............Ia\|
+00000010  db f2 e5 63 23 3a f1 dd  12 3e 61 ed d9 4b 5f b5  |...c#:...>a..K_.|
+00000020  d3 f7 38 15 03 03 00 1a  00 00 00 00 00 00 00 02  |..8.............|
+00000030  af 09 a7 f1 e1 d9 1f 54  d1 35 19 16 b7 23 ce 4e  |.......T.5...#.N|
+00000040  3a b1                                             |:.|
diff --git a/src/crypto/tls/testdata/Server-SSLv3-RSA-3DES b/src/crypto/tls/testdata/Server-SSLv3-RSA-3DES
index 10b7f52..11a8a1c 100644
--- a/src/crypto/tls/testdata/Server-SSLv3-RSA-3DES
+++ b/src/crypto/tls/testdata/Server-SSLv3-RSA-3DES
@@ -1,78 +1,76 @@
 >>> Flow 1 (client to server)
-00000000  16 03 00 00 30 01 00 00  2c 03 00 50 32 2f f9 d5  |....0...,..P2/..|
-00000010  8f 83 ac 79 0e 0b e5 65  2c 87 79 01 7d 15 73 00  |...y...e,.y.}.s.|
-00000020  46 7c dc c6 6d 70 0b f3  d2 dc de 00 00 04 00 0a  |F|..mp..........|
-00000030  00 ff 02 01 00                                    |.....|
+00000000  16 03 00 00 2f 01 00 00  2b 03 00 47 b4 bd 36 64  |..../...+..G..6d|
+00000010  0a 7d 37 1d 99 ac fd 1c  7a 3f d5 0f 9d 90 e3 59  |.}7.....z?.....Y|
+00000020  64 e4 fb 59 3a 4a 5f 53  d2 af 88 00 00 04 00 0a  |d..Y:J_S........|
+00000030  00 ff 01 00                                       |....|
 >>> Flow 2 (server to client)
 00000000  16 03 00 00 31 02 00 00  2d 03 00 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 0a 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  00 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 00 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000030  05 ff 01 00 01 00 16 03  00 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 00 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 00 00 84 10 00 00  80 48 96 89 e9 d2 e6 c6  |.........H......|
-00000010  eb 9d f8 46 dd c7 d8 01  95 57 76 1a 59 1c 79 21  |...F.....Wv.Y.y!|
-00000020  94 0b 83 b2 c9 5e c1 5f  4f 12 00 10 63 12 d3 f9  |.....^._O...c...|
-00000030  ae ae 31 18 fa b4 33 37  eb b9 23 15 55 7e cf 62  |..1...37..#.U~.b|
-00000040  20 a7 cb eb 69 35 e0 35  32 e4 0a 4c c0 33 e9 7d  | ...i5.52..L.3.}|
-00000050  f2 a8 4b e2 fe 90 62 7a  09 df c5 46 03 0c 52 7a  |..K...bz...F..Rz|
-00000060  fb 96 dd fd 55 aa e5 be  3c 35 65 03 be e1 51 0f  |....U...<5e...Q.|
-00000070  7b b3 05 6b e9 af 9b 0e  e4 ea d9 34 69 a5 c2 9a  |{..k.......4i...|
-00000080  71 a8 cc 0a 94 ef 91 14  88 14 03 00 00 01 01 16  |q...............|
-00000090  03 00 00 40 0c 34 26 4c  cf f0 d4 a0 08 b9 b7 6b  |... at .4&L.......k|
-000000a0  0a 69 55 48 91 2c 92 4c  9b e7 66 d0 b8 da 2d e7  |.iUH.,.L..f...-.|
-000000b0  89 ca f5 a4 3d 11 ff 87  22 07 c0 ed 72 9c ad 19  |....=..."...r...|
-000000c0  7d 63 2b 67 43 e3 33 76  a1 ac 69 77 55 bb 60 ba  |}c+gC.3v..iwU.`.|
-000000d0  57 00 4e 2a                                       |W.N*|
+00000000  16 03 00 00 84 10 00 00  80 43 4d 76 6b 7f b3 e6  |.........CMvk...|
+00000010  82 18 f9 8a a5 cd 45 ab  8f 1a 1d d4 9a 0a 1d 50  |......E........P|
+00000020  96 f2 08 14 a7 6b e3 ef  d1 31 6b 18 d2 f5 ee e3  |.....k...1k.....|
+00000030  cd df 67 23 3d ec 70 09  07 df 32 c2 cd 60 6c 2b  |..g#=.p...2..`l+|
+00000040  7f 04 cd b3 77 87 78 e5  90 60 41 0c fc 22 1a 3a  |....w.x..`A..".:|
+00000050  82 29 28 92 9c f8 33 3a  72 ee 08 58 55 d5 ea 9c  |.)(...3:r..XU...|
+00000060  37 96 a4 92 75 e0 29 8a  18 ad 5a c1 1f 4c aa c7  |7...u.)...Z..L..|
+00000070  49 89 6e ff 29 32 a3 c8  51 e8 50 3f 41 10 36 27  |I.n.)2..Q.P?A.6'|
+00000080  0b 60 a2 96 4b 82 a9 c6  52 14 03 00 00 01 01 16  |.`..K...R.......|
+00000090  03 00 00 40 b3 59 d0 de  d1 47 8e 9e 1a 27 16 41  |... at .Y...G...'.A|
+000000a0  f7 38 4e 91 12 a0 71 89  1c 68 29 dc 60 7e 2c 39  |.8N...q..h).`~,9|
+000000b0  45 cb e6 98 8d 43 5e 76  34 ca 5b 86 24 9d 77 0a  |E....C^v4.[.$.w.|
+000000c0  90 60 19 75 67 74 3d 95  1d e7 82 ee a8 9f 3a 60  |.`.ugt=.......:`|
+000000d0  8e ac 28 74                                       |..(t|
 >>> Flow 4 (server to client)
-00000000  14 03 00 00 01 01 16 03  00 00 40 dd e1 34 c5 4a  |.......... at ..4.J|
-00000010  96 76 81 49 df 1b 3d 48  cc 6c b0 3b ee 77 a9 62  |.v.I..=H.l.;.w.b|
-00000020  91 b3 16 b0 e1 79 4b 2a  95 d8 54 98 7b 5e ac 0f  |.....yK*..T.{^..|
-00000030  07 3b 06 36 e1 38 dc 75  6a af f7 ce a4 b2 3f 9e  |.;.6.8.uj.....?.|
-00000040  36 b1 44 ce e9 6c 34 ba  ce 97 02 17 03 00 00 18  |6.D..l4.........|
-00000050  5b be 71 2f a1 15 2f e9  9b 83 8e f1 9b e7 5b 4a  |[.q/../.......[J|
-00000060  a1 85 13 03 c0 f2 30 0c  17 03 00 00 28 2c d9 9e  |......0.....(,..|
-00000070  f4 d2 70 2a 37 76 66 e7  f4 5c c7 55 be d8 82 49  |..p*7vf..\.U...I|
-00000080  77 e0 4f 0f 87 4b c0 b1  f3 d2 a3 63 df 62 bc ee  |w.O..K.....c.b..|
-00000090  5c c2 50 2a 96 15 03 00  00 18 8b 0a 68 8a d8 64  |\.P*........h..d|
-000000a0  4e 3f f9 ee c6 b2 21 51  03 10 6b 73 3b 8c a4 bb  |N?....!Q..ks;...|
-000000b0  6d f2                                             |m.|
+00000000  14 03 00 00 01 01 16 03  00 00 40 e8 3e 89 b5 10  |.......... at .>...|
+00000010  e4 c9 eb f7 3f 83 e5 6a  7c 04 fd e6 96 69 25 fb  |....?..j|....i%.|
+00000020  0b 0b 0e f7 13 4e 99 45  d2 0e 13 22 6b d1 0e 32  |.....N.E..."k..2|
+00000030  30 b5 c4 a2 03 cf 22 59  68 5c cc 63 96 f5 01 f3  |0....."Yh\.c....|
+00000040  2c b3 b5 13 e1 9d 19 45  c0 4f 28 17 03 00 00 18  |,......E.O(.....|
+00000050  2e cb 8c b3 d4 d5 c2 18  fd 6e dc 72 7b b3 4b b8  |.........n.r{.K.|
+00000060  10 56 0a 01 af 55 e8 5a  17 03 00 00 28 3f df 74  |.V...U.Z....(?.t|
+00000070  2f b9 5b a4 43 ec 24 68  ad ff 6c 52 b5 6a 91 0c  |/.[.C.$h..lR.j..|
+00000080  be 3b 25 c9 e4 40 59 66  17 cb f0 e7 6b 6e cd 43  |.;%.. at Yf....kn.C|
+00000090  ac be b7 62 d0 15 03 00  00 18 43 4d 3c fd 83 6e  |...b......CM<..n|
+000000a0  e0 3f ae 40 0c 8a a1 08  d2 74 e2 60 7b d0 97 d5  |.?. at .....t.`{...|
+000000b0  e8 a5                                             |..|
diff --git a/src/crypto/tls/testdata/Server-SSLv3-RSA-AES b/src/crypto/tls/testdata/Server-SSLv3-RSA-AES
index e733819..771373c 100644
--- a/src/crypto/tls/testdata/Server-SSLv3-RSA-AES
+++ b/src/crypto/tls/testdata/Server-SSLv3-RSA-AES
@@ -1,79 +1,77 @@
 >>> Flow 1 (client to server)
-00000000  16 03 00 00 30 01 00 00  2c 03 00 36 b0 f3 52 13  |....0...,..6..R.|
-00000010  00 17 16 8f 6e 44 24 06  84 05 5b 03 e6 8a 55 ee  |....nD$...[...U.|
-00000020  75 9c a8 77 9e e0 7b 15  f9 60 6e 00 00 04 00 2f  |u..w..{..`n..../|
-00000030  00 ff 02 01 00                                    |.....|
+00000000  16 03 00 00 2f 01 00 00  2b 03 00 26 1e 06 cd 27  |..../...+..&...'|
+00000010  f5 2a b4 8d 00 07 47 16  02 23 aa 5e 92 02 95 4a  |.*....G..#.^...J|
+00000020  1a 0b a8 51 8a 6f 4a 31  3c e9 a2 00 00 04 00 2f  |...Q.oJ1<....../|
+00000030  00 ff 01 00                                       |....|
 >>> Flow 2 (server to client)
 00000000  16 03 00 00 31 02 00 00  2d 03 00 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
-00000030  05 ff 01 00 01 00 16 03  00 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 00 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000030  05 ff 01 00 01 00 16 03  00 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 00 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 00 00 84 10 00 00  80 2d 0b b1 1c 96 72 65  |.........-....re|
-00000010  e5 3b 5b 48 35 91 b8 2e  18 b5 6c 36 a4 91 10 0e  |.;[H5.....l6....|
-00000020  15 63 de fb 7e ea 44 cd  2e 2f 37 2c 88 96 30 d4  |.c..~.D../7,..0.|
-00000030  07 ff 02 9b af 84 2c 43  6c 3a 1f 75 17 4c 5e 8b  |......,Cl:.u.L^.|
-00000040  4a d9 df 68 fe ad 72 c9  0c f7 a5 0c a1 70 8b 9f  |J..h..r......p..|
-00000050  e7 8e 1d 32 61 8e 80 e5  3a 3a 61 ea 22 1a 67 e5  |...2a...::a.".g.|
-00000060  06 6a 5e 0c 65 bd c7 32  9c 13 c1 53 ad 8e f1 be  |.j^.e..2...S....|
-00000070  4d 6c 53 89 8f 9c 49 d2  85 58 04 b5 e8 53 b4 82  |MlS...I..X...S..|
-00000080  84 46 9d 70 fa 0a 34 15  1d 14 03 00 00 01 01 16  |.F.p..4.........|
-00000090  03 00 00 40 71 c7 4b ef  6b 7a f4 a2 29 dd c0 4b  |... at q.K.kz..)..K|
-000000a0  ef 04 7d ea 1c 31 16 38  ae 85 f9 89 db 2f a8 04  |..}..1.8...../..|
-000000b0  ad 61 b7 33 73 8c 31 9b  72 5a f6 8b 10 71 0c af  |.a.3s.1.rZ...q..|
-000000c0  99 89 14 63 b8 19 f8 0e  2c 0f 14 c6 d6 0a bd 4f  |...c....,......O|
-000000d0  96 59 0d 60                                       |.Y.`|
+00000000  16 03 00 00 84 10 00 00  80 66 67 59 2f 21 b9 e3  |.........fgY/!..|
+00000010  0d a9 78 0c 6b fc dc 6f  69 4e f9 00 8b 40 a2 0f  |..x.k..oiN... at ..|
+00000020  5a d8 8c d2 59 ab 33 78  f6 42 2f fa cf d6 48 7a  |Z...Y.3x.B/...Hz|
+00000030  59 30 94 1c 10 49 30 69  4a 6c a2 e5 ce 59 6d e3  |Y0...I0iJl...Ym.|
+00000040  49 0c a7 0a ab 17 8b c6  48 82 71 44 d5 7d 80 e5  |I.......H.qD.}..|
+00000050  6d 45 6c 10 12 01 85 71  ee dc c5 e3 19 41 ed 22  |mEl....q.....A."|
+00000060  11 5c c4 25 c6 90 ad c8  4c 48 45 8d ad 6c f4 ef  |.\.%....LHE..l..|
+00000070  fb b4 2b 53 90 cc 78 b0  9e 22 e7 2c 1a 64 0e 8b  |..+S..x..".,.d..|
+00000080  d8 57 54 74 c5 33 20 3f  42 14 03 00 00 01 01 16  |.WTt.3 ?B.......|
+00000090  03 00 00 40 18 b6 0a d4  9e 4d fa 8a 67 ce 8e d5  |... at .....M..g...|
+000000a0  51 31 75 65 f1 ff 54 a2  1b 80 c5 c3 a0 fc d2 78  |Q1ue..T........x|
+000000b0  0b 99 3b 65 6c 1d 52 6d  a9 9f 64 13 97 d5 2e b1  |..;el.Rm..d.....|
+000000c0  76 0b a0 fb f6 16 f7 72  28 a5 8a 11 a7 46 d5 59  |v......r(....F.Y|
+000000d0  e1 f4 f3 6f                                       |...o|
 >>> Flow 4 (server to client)
-00000000  14 03 00 00 01 01 16 03  00 00 40 28 76 de 29 3b  |..........@(v.);|
-00000010  48 77 56 f1 e5 97 21 20  88 9c 7d 5e 02 3d bb c9  |HwV...! ..}^.=..|
-00000020  2f b1 ce 2e 65 ac 53 ea  a2 06 0e fb cf 53 28 1d  |/...e.S......S(.|
-00000030  df b3 24 48 52 7a 28 d6  9e 50 83 64 da 34 c1 f4  |..$HRz(..P.d.4..|
-00000040  c9 bf ec 42 33 c4 8a 6f  89 aa 1c 17 03 00 00 20  |...B3..o....... |
-00000050  f2 af bb 38 4f 37 58 0e  c4 2b 28 45 01 45 89 e9  |...8O7X..+(E.E..|
-00000060  31 5a 6d 8d 4d 1b 49 bd  7d 87 8a 62 e6 c8 03 43  |1Zm.M.I.}..b...C|
-00000070  17 03 00 00 30 60 ec e4  6f ec 88 33 d8 89 49 73  |....0`..o..3..Is|
-00000080  3a aa 67 ab 45 9f de c7  3f 0e 39 3d 9a 30 99 9c  |:.g.E...?.9=.0..|
-00000090  2d 10 5f f0 7d 70 10 d5  8e ca 18 91 25 e8 9d d1  |-._.}p......%...|
-000000a0  36 b0 a7 90 9b 15 03 00  00 20 63 e9 92 98 7d b1  |6........ c...}.|
-000000b0  9a 88 07 37 b2 27 99 95  b9 16 17 74 c2 42 9c dc  |...7.'.....t.B..|
-000000c0  80 32 de f4 f6 87 cb f1  87 d8                    |.2........|
+00000000  14 03 00 00 01 01 16 03  00 00 40 6c 5b 64 b5 f9  |.......... at l[d..|
+00000010  76 cc 7e 51 72 46 ab 21  17 b3 fb 2b 48 c5 5a 9f  |v.~QrF.!...+H.Z.|
+00000020  e6 35 14 ff df c7 a7 4b  5e 5a 9b 82 57 b5 bf 4d  |.5.....K^Z..W..M|
+00000030  5f 7c a5 be 67 96 71 3a  63 ad 76 86 66 06 e9 a2  |_|..g.q:c.v.f...|
+00000040  35 39 6f 79 13 21 4b 19  c1 83 0e 17 03 00 00 20  |59oy.!K........ |
+00000050  1a 80 c5 d1 8b 33 79 89  39 fc 11 44 80 33 1a f7  |.....3y.9..D.3..|
+00000060  9f 63 96 5d c9 1a d4 56  2a ee 68 24 68 83 5d ca  |.c.]...V*.h$h.].|
+00000070  17 03 00 00 30 7c d4 88  17 d0 10 66 6a b3 61 ed  |....0|.....fj.a.|
+00000080  0a b5 72 55 ca fb c4 ec  e2 f2 e2 bf 67 dd 3d c9  |..rU........g.=.|
+00000090  01 3b 50 5c 35 ce 28 2d  e6 9c 1f 5c 70 14 46 2a  |.;P\5.(-...\p.F*|
+000000a0  d8 9e ef 6a 66 15 03 00  00 20 c7 af e1 86 10 30  |...jf.... .....0|
+000000b0  41 73 88 b2 86 02 a8 60  38 61 92 32 11 22 2d 47  |As.....`8a.2."-G|
+000000c0  76 fe 22 9c 76 c2 00 ee  e9 03                    |v.".v.....|
diff --git a/src/crypto/tls/testdata/Server-SSLv3-RSA-RC4 b/src/crypto/tls/testdata/Server-SSLv3-RSA-RC4
index dce3ebe..f5674cc 100644
--- a/src/crypto/tls/testdata/Server-SSLv3-RSA-RC4
+++ b/src/crypto/tls/testdata/Server-SSLv3-RSA-RC4
@@ -1,74 +1,72 @@
 >>> Flow 1 (client to server)
-00000000  16 03 00 00 30 01 00 00  2c 03 00 3c 64 40 96 81  |....0...,..<d at ..|
-00000010  b4 90 3d a5 bb 90 8a ba  39 73 4c cd 2d f9 4c 12  |..=.....9sL.-.L.|
-00000020  4c 6e d6 09 43 e3 eb 07  2e 52 1a 00 00 04 00 05  |Ln..C....R......|
-00000030  00 ff 02 01 00                                    |.....|
+00000000  16 03 00 00 2f 01 00 00  2b 03 00 3f cc 8d 3f f0  |..../...+..?..?.|
+00000010  c9 36 6f 43 43 c1 46 45  cd bf e5 ba 02 e6 55 2c  |.6oCC.FE......U,|
+00000020  3a 24 4a db cb a8 f2 1d  26 3e ef 00 00 04 00 05  |:$J.....&>......|
+00000030  00 ff 01 00                                       |....|
 >>> Flow 2 (server to client)
 00000000  16 03 00 00 31 02 00 00  2d 03 00 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 05 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  00 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 00 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000030  05 ff 01 00 01 00 16 03  00 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 00 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 00 00 84 10 00 00  80 00 e0 40 dd e4 0f 54  |........... at ...T|
-00000010  40 66 62 06 72 2a 7a 06  2d a9 0f 16 3b 5c 63 9b  |@fb.r*z.-...;\c.|
-00000020  95 82 9c d4 95 57 c0 37  d1 30 6a 33 e1 5a ec 93  |.....W.7.0j3.Z..|
-00000030  12 ec 2a 94 c6 9c b3 6c  a3 4f ef cd f1 80 25 a7  |..*....l.O....%.|
-00000040  54 ca 6a 6e b9 80 0b fc  f1 e9 60 a0 f5 33 24 3b  |T.jn......`..3$;|
-00000050  13 04 9a f1 8a 37 cd 11  cf 95 ae 71 ba 73 8e 00  |.....7.....q.s..|
-00000060  86 17 6a 3b d5 9e a9 04  87 fd 62 ed 4c b5 01 55  |..j;......b.L..U|
-00000070  65 a2 fb e8 1d 86 a5 58  2a ad e7 fd d3 44 2f 7d  |e......X*....D/}|
-00000080  25 b7 3b c7 75 39 5c 45  f6 14 03 00 00 01 01 16  |%.;.u9\E........|
-00000090  03 00 00 3c e6 58 15 b2  fb 0d 44 ed 43 d5 ff a8  |...<.X....D.C...|
-000000a0  41 25 83 41 46 da f6 8e  70 34 39 c6 6c 2c ea 1b  |A%.AF...p49.l,..|
-000000b0  2a 02 5c 4b e4 87 58 33  6c d0 22 2e ce 85 df 31  |*.\K..X3l."....1|
-000000c0  0d 71 4c 1a f9 9c 64 d7  87 53 eb c9 1a 0a 16 dc  |.qL...d..S......|
+00000000  16 03 00 00 84 10 00 00  80 13 5d 75 f0 6d 24 54  |..........]u.m$T|
+00000010  f5 a1 f0 13 86 61 ce ea  66 86 06 eb c8 27 78 9f  |.....a..f....'x.|
+00000020  10 0d ef 94 3f 1b fb 8c  11 14 67 2a 0e 2a 1b cf  |....?.....g*.*..|
+00000030  ae 5a cb ac b8 b2 ea a8  70 85 ee fd 88 a9 61 a4  |.Z......p.....a.|
+00000040  75 66 86 a5 88 96 a0 0d  6f 77 fe 63 5e 88 60 4d  |uf......ow.c^.`M|
+00000050  f6 b7 93 28 99 72 e8 60  ed 64 9a 3f e6 12 ea ee  |...(.r.`.d.?....|
+00000060  83 58 d4 0c 19 e0 2b ce  b0 b4 fa 73 9f 78 d9 09  |.X....+....s.x..|
+00000070  8c 17 b8 f5 04 e1 de c4  fe a9 1a aa ba 0d be f3  |................|
+00000080  c8 e1 e4 e8 cc 39 4c f0  b9 14 03 00 00 01 01 16  |.....9L.........|
+00000090  03 00 00 3c 1b 70 07 7f  ad 8f a7 78 fd e8 eb b2  |...<.p.....x....|
+000000a0  9a 54 86 a2 dd bc fa b6  0a 52 48 24 79 6a 04 f6  |.T.......RH$yj..|
+000000b0  28 80 1f b7 b1 c6 4e 07  a3 52 60 5a 5a 81 14 11  |(.....N..R`ZZ...|
+000000c0  d2 ee 33 71 e7 d3 ba 3e  4b 31 81 f2 f0 49 ee e4  |..3q...>K1...I..|
 >>> Flow 4 (server to client)
-00000000  14 03 00 00 01 01 16 03  00 00 3c 17 a2 5b 4a 06  |..........<..[J.|
-00000010  63 6a 4b f9 ef 66 ed 31  f6 87 75 20 8b 08 8d 5d  |cjK..f.1..u ...]|
-00000020  0f 72 87 dd 8d db 99 d5  06 42 2b a3 84 77 35 f2  |.r.......B+..w5.|
-00000030  1d 11 ae 0b 0c df ed 10  6e 23 27 93 29 65 25 f6  |........n#'.)e%.|
-00000040  60 b9 76 c8 95 2b 0c 17  03 00 00 21 df 08 e8 1f  |`.v..+.....!....|
-00000050  2f ea 5a 61 d6 d4 4a c0  c1 b5 59 bc e1 89 6e 88  |/.Za..J...Y...n.|
-00000060  bb 8d 16 db 64 87 31 6c  2d d6 c7 d2 ed 15 03 00  |....d.1l-.......|
-00000070  00 16 a9 53 32 af 7a a4  88 02 93 6b aa 95 84 4f  |...S2.z....k...O|
-00000080  17 5a 97 93 67 87 3b 07                           |.Z..g.;.|
+00000000  14 03 00 00 01 01 16 03  00 00 3c 47 20 7c b9 0d  |..........<G |..|
+00000010  f8 59 c0 79 25 ae 8a f3  f5 7d 0c f5 62 d4 a5 5b  |.Y.y%....}..b..[|
+00000020  f6 08 36 cc 99 21 ac 80  04 48 49 2c 04 7c 87 08  |..6..!...HI,.|..|
+00000030  d3 10 43 a9 6a ec 99 96  99 0a fa cb db 95 6f fe  |..C.j.........o.|
+00000040  b1 75 77 1e b7 a9 9d 17  03 00 00 21 1a 2f bc 70  |.uw........!./.p|
+00000050  2c 00 6b 55 e1 e5 81 17  1c b7 a1 11 d7 21 c1 2f  |,.kU.........!./|
+00000060  3e 95 f8 48 74 a4 97 0a  9d a2 0b bc d4 15 03 00  |>..Ht...........|
+00000070  00 16 67 0d 6d 69 53 87  92 23 21 51 72 f6 31 73  |..g.miS..#!Qr.1s|
+00000080  db bd 3c e6 f4 12 4c 69                           |..<...Li|
diff --git a/src/crypto/tls/testdata/Server-TLSv10-ECDHE-ECDSA-AES b/src/crypto/tls/testdata/Server-TLSv10-ECDHE-ECDSA-AES
index 9314b90..3e17081 100644
--- a/src/crypto/tls/testdata/Server-TLSv10-ECDHE-ECDSA-AES
+++ b/src/crypto/tls/testdata/Server-TLSv10-ECDHE-ECDSA-AES
@@ -1,11 +1,10 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 5f 01 00 00  5b 03 01 ad 87 94 6b 8a  |...._...[.....k.|
-00000010  38 9e 70 d6 94 8a 73 a9  39 d8 d7 25 ab 47 92 4c  |8.p...s.9..%.G.L|
-00000020  b1 20 8e 4d f3 7b cd 84  5e 13 c1 00 00 04 c0 0a  |. .M.{..^.......|
-00000030  00 ff 02 01 00 00 2d 00  0b 00 04 03 00 01 02 00  |......-.........|
-00000040  0a 00 1c 00 1a 00 17 00  19 00 1c 00 1b 00 18 00  |................|
-00000050  1a 00 16 00 0e 00 0d 00  0b 00 0c 00 09 00 0a 00  |................|
-00000060  0f 00 01 01                                       |....|
+00000000  16 03 01 00 4f 01 00 00  4b 03 01 f1 86 d0 c8 69  |....O...K......i|
+00000010  46 0b 0b 89 08 c0 82 c0  f7 f1 9a b6 d2 2b e1 46  |F............+.F|
+00000020  e6 e1 44 65 de 39 0a 68  a8 d5 1c 00 00 04 c0 0a  |..De.9.h........|
+00000030  00 ff 01 00 00 1e 00 0b  00 04 03 00 01 02 00 0a  |................|
+00000040  00 0a 00 08 00 1d 00 17  00 19 00 18 00 16 00 00  |................|
+00000050  00 17 00 00                                       |....|
 >>> Flow 2 (server to client)
 00000000  16 03 01 00 31 02 00 00  2d 03 01 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
@@ -43,41 +42,37 @@
 00000210  0e bd 3f a3 8c 25 c1 33  13 83 0d 94 06 bb d4 37  |..?..%.3.......7|
 00000220  7a f6 ec 7a c9 86 2e dd  d7 11 69 7f 85 7c 56 de  |z..z......i..|V.|
 00000230  fb 31 78 2b e4 c7 78 0d  ae cb be 9e 4e 36 24 31  |.1x+..x.....N6$1|
-00000240  7b 6a 0f 39 95 12 07 8f  2a 16 03 01 00 d6 0c 00  |{j.9....*.......|
-00000250  00 d2 03 00 17 41 04 1e  18 37 ef 0d 19 51 88 35  |.....A...7...Q.5|
-00000260  75 71 b5 e5 54 5b 12 2e  8f 09 67 fd a7 24 20 3e  |uq..T[....g..$ >|
-00000270  b2 56 1c ce 97 28 5e f8  2b 2d 4f 9e f1 07 9f 6c  |.V...(^.+-O....l|
-00000280  4b 5b 83 56 e2 32 42 e9  58 b6 d7 49 a6 b5 68 1a  |K[.V.2B.X..I..h.|
-00000290  41 03 56 6b dc 5a 89 00  8b 30 81 88 02 42 00 8b  |A.Vk.Z...0...B..|
-000002a0  48 d5 a3 a0 35 5c 31 f5  0b e8 72 7c 87 31 79 af  |H...5\1...r|.1y.|
-000002b0  7f 12 93 9a f9 df d5 44  bf 08 5a 6b 1c 68 dd 73  |.......D..Zk.h.s|
-000002c0  67 0f 32 41 45 53 bf 74  cf 91 54 e7 7a 88 41 7a  |g.2AES.t..T.z.Az|
-000002d0  15 ea 3d e3 b8 93 c0 3f  24 4c fb ee 25 f1 20 80  |..=....?$L..%. .|
-000002e0  02 42 01 ab 97 5f 8b 8d  22 71 f9 f5 a3 59 69 42  |.B..._.."q...YiB|
-000002f0  06 bd 12 f5 61 53 cb c8  a1 b4 90 87 12 94 9b f8  |....aS..........|
-00000300  b3 1d 34 d9 cd 64 20 9c  92 ec b5 72 35 01 44 3a  |..4..d ....r5.D:|
-00000310  86 e4 54 46 0d 74 1d 4e  d8 41 16 eb ac c3 8a 2f  |..TF.t.N.A...../|
-00000320  20 11 ad bc 16 03 01 00  04 0e 00 00 00           | ............|
+00000240  7b 6a 0f 39 95 12 07 8f  2a 16 03 01 00 b5 0c 00  |{j.9....*.......|
+00000250  00 b1 03 00 1d 20 2f e5  7d a3 47 cd 62 43 15 28  |..... /.}.G.bC.(|
+00000260  da ac 5f bb 29 07 30 ff  f6 84 af c4 cf c2 ed 90  |.._.).0.........|
+00000270  99 5f 58 cb 3b 74 00 8b  30 81 88 02 42 00 ad 93  |._X.;t..0...B...|
+00000280  e2 c2 3d 7e 95 63 17 5d  45 cf cd 27 af d2 db b3  |..=~.c.]E..'....|
+00000290  d0 bc 13 1e 6f 0a 61 3a  fb 3c b3 03 61 2c 36 ae  |....o.a:.<..a,6.|
+000002a0  4f be 27 e9 43 3c cf 57  9b 82 5e 7d 54 36 ed 7e  |O.'.C<.W..^}T6.~|
+000002b0  0b 34 68 26 90 00 20 02  0f c1 18 bc 79 1b 90 02  |.4h&.. .....y...|
+000002c0  42 01 6b 66 9d 56 48 8e  5e 38 93 48 03 6b b9 d7  |B.kf.VH.^8.H.k..|
+000002d0  bd 14 a0 3e 8a 27 81 7f  fe 4d e5 8a 12 4d 95 16  |...>.'...M...M..|
+000002e0  ef c7 8d 60 07 1d 22 f8  5d 72 0d cc be c3 51 69  |...`..".]r....Qi|
+000002f0  7a 04 e3 84 e5 ba dd 04  1d d4 4c 6f 9f 6b 12 e0  |z.........Lo.k..|
+00000300  2f 83 3c 16 03 01 00 04  0e 00 00 00              |/.<.........|
 >>> Flow 3 (client to server)
-00000000  16 03 01 00 46 10 00 00  42 41 04 38 ca 59 61 cd  |....F...BA.8.Ya.|
-00000010  17 4a cf a8 0b 81 c6 b7  7f 52 dd 95 d7 57 9d 24  |.J.......R...W.$|
-00000020  bb b1 02 af 57 ee b9 f9  c5 a0 c3 20 44 e1 9a e4  |....W...... D...|
-00000030  83 64 7d a1 fa 9d 2e 3b  5e be 0f af ed 96 f3 09  |.d}....;^.......|
-00000040  62 a2 22 21 72 f8 84 89  8a fd 10 14 03 01 00 01  |b."!r...........|
-00000050  01 16 03 01 00 30 bd e6  23 e0 32 b8 4c ef ce 9e  |.....0..#.2.L...|
-00000060  22 a5 77 2c f1 7e 2f 8d  8b 9e a5 92 42 f9 0f 02  |".w,.~/.....B...|
-00000070  eb 2e 94 f1 6d a3 24 3f  c0 ae bb c0 c4 99 08 51  |....m.$?.......Q|
-00000080  47 28 8b 4e f9 02                                 |G(.N..|
+00000000  16 03 01 00 25 10 00 00  21 20 18 40 ea d1 e1 17  |....%...! . at ....|
+00000010  b6 a2 a5 db 20 13 70 81  90 fc ac e8 96 7c b1 e1  |.... .p......|..|
+00000020  ff 6f 57 1f c1 64 72 94  f7 05 14 03 01 00 01 01  |.oW..dr.........|
+00000030  16 03 01 00 30 05 33 48  f0 2a 3a df df 1d c4 3d  |....0.3H.*:....=|
+00000040  87 ea 9d 04 04 eb 84 bf  a0 ed bc 56 2f ab 36 52  |...........V/.6R|
+00000050  d5 b2 2c 6f 8c 58 49 51  33 d5 fc df 5d 09 df e9  |..,o.XIQ3...]...|
+00000060  be 20 30 9a 37                                    |. 0.7|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 11 a9 f0 95 27  |..........0....'|
-00000010  ac 0a b7 8e 0d 42 0a 2a  f8 f8 e2 4f 4f 4a 79 d1  |.....B.*...OOJy.|
-00000020  73 e6 4d 42 90 3c 06 f8  7b da 26 cc 58 be 97 f6  |s.MB.<..{.&.X...|
-00000030  41 32 fb 39 2f fa e1 bc  59 2b 45 17 03 01 00 20  |A2.9/...Y+E.... |
-00000040  93 6a a1 a6 a2 e6 be bb  be 2f 8f 0c 52 39 1c 6a  |.j......./..R9.j|
-00000050  6d 4c af 38 f7 60 8b ad  0e c7 62 0c 8b a4 42 14  |mL.8.`....b...B.|
-00000060  17 03 01 00 30 da b0 1b  ef cf 45 86 09 e9 be aa  |....0.....E.....|
-00000070  0f 71 af a3 86 d0 0f 2d  e8 76 39 9a c4 1f f5 c2  |.q.....-.v9.....|
-00000080  82 0a ee 34 0e a6 3b 19  b8 2c 10 ad fc 03 33 31  |...4..;..,....31|
-00000090  10 42 9b 6e 7b 15 03 01  00 20 ac 73 4d 4b 92 30  |.B.n{.... .sMK.0|
-000000a0  bf 4c bc 77 c1 87 d7 20  ad 82 bd 75 31 82 0d 34  |.L.w... ...u1..4|
-000000b0  cb b2 86 fd 4f 9c 84 a3  80 af                    |....O.....|
+00000000  14 03 01 00 01 01 16 03  01 00 30 8c b6 5b 83 03  |..........0..[..|
+00000010  c0 d8 83 f7 1d 24 2e ec  39 68 00 91 73 d2 5a 15  |.....$..9h..s.Z.|
+00000020  3f 83 aa e3 6d fd cc 31  58 90 e9 a9 e3 e4 78 5d  |?...m..1X.....x]|
+00000030  ce 8e b3 ba cd 71 aa a2  fd f4 7c 17 03 01 00 20  |.....q....|.... |
+00000040  62 98 34 9d 01 13 13 2d  1b 27 3a 4f 10 28 48 d6  |b.4....-.':O.(H.|
+00000050  32 8c 99 2a c8 64 14 6e  dc f5 7c 6d 16 59 45 8e  |2..*.d.n..|m.YE.|
+00000060  17 03 01 00 30 1e ed f9  40 ad 5c 5d f6 94 c9 fd  |....0... at .\]....|
+00000070  a1 ac fc 00 7b 48 9a 59  6d f5 b7 06 a4 66 25 04  |....{H.Ym....f%.|
+00000080  61 33 08 f3 66 86 21 00  fb f3 03 78 83 4c b6 c8  |a3..f.!....x.L..|
+00000090  9d 5e ea f5 7e 15 03 01  00 20 98 d8 f6 2a 79 60  |.^..~.... ...*y`|
+000000a0  8d fb c9 45 2f 27 59 17  a9 79 eb e7 b9 46 f1 57  |...E/'Y..y...F.W|
+000000b0  a6 fa ea e1 d0 23 8c 03  4f 72                    |.....#..Or|
diff --git a/src/crypto/tls/testdata/Server-TLSv10-RSA-3DES b/src/crypto/tls/testdata/Server-TLSv10-RSA-3DES
index 33e8063..9590b0d 100644
--- a/src/crypto/tls/testdata/Server-TLSv10-RSA-3DES
+++ b/src/crypto/tls/testdata/Server-TLSv10-RSA-3DES
@@ -1,74 +1,72 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 37 01 00 00  33 03 01 8d f4 8c 3d bd  |....7...3.....=.|
-00000010  d8 81 53 bb f5 bc 18 69  07 09 0d 05 93 4f 6f eb  |..S....i.....Oo.|
-00000020  fa fb 03 65 d4 49 a3 df  9f c3 a5 00 00 04 00 0a  |...e.I..........|
-00000030  00 ff 02 01 00 00 05 00  0f 00 01 01              |............|
+00000000  16 03 01 00 39 01 00 00  35 03 01 58 71 a3 0c c4  |....9...5..Xq...|
+00000010  b6 b0 33 0a 66 3c eb c6  f4 d9 0e 99 75 d4 9e b6  |..3.f<......u...|
+00000020  03 b4 ae ae ad bc a8 ab  64 a0 27 00 00 04 00 0a  |........d.'.....|
+00000030  00 ff 01 00 00 08 00 16  00 00 00 17 00 00        |..............|
 >>> Flow 2 (server to client)
 00000000  16 03 01 00 31 02 00 00  2d 03 01 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 0a 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  01 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 01 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000030  05 ff 01 00 01 00 16 03  01 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 01 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 01 00 86 10 00 00  82 00 80 6b 00 e3 47 e3  |...........k..G.|
-00000010  0d 22 44 53 7a b9 d1 14  4e e4 47 17 a1 e2 f5 d2  |."DSz...N.G.....|
-00000020  f7 82 2f 1b e2 3a 60 aa  79 36 fa 74 05 72 66 88  |../..:`.y6.t.rf.|
-00000030  3f 6a 57 8d 10 8a a1 80  3c 74 5b 29 c3 a1 b8 57  |?jW.....<t[)...W|
-00000040  20 cc 75 fc 0e 3c 09 06  46 52 b2 ca b2 cd bf 4c  | .u..<..FR.....L|
-00000050  b3 12 2b 59 f1 41 a2 c7  4c 62 c7 61 26 2b 89 fe  |..+Y.A..Lb.a&+..|
-00000060  01 9a b6 2b b4 15 75 05  4b f8 5b 04 9a 64 cc 06  |...+..u.K.[..d..|
-00000070  6b 8c 98 6d 51 37 50 b4  69 03 5c 9a ed e3 9a 23  |k..mQ7P.i.\....#|
-00000080  a9 68 e0 56 58 f7 f4 a0  d6 b4 55 14 03 01 00 01  |.h.VX.....U.....|
-00000090  01 16 03 01 00 28 9f ac  be d9 6f ab cb 0e 45 8a  |.....(....o...E.|
-000000a0  96 71 fd 23 39 b0 02 cc  a6 5a 7a 64 e2 29 9f 18  |.q.#9....Zzd.)..|
-000000b0  dc 25 84 ee 76 56 3c cc  d9 15 34 16 67 7e        |.%..vV<...4.g~|
+00000000  16 03 01 00 86 10 00 00  82 00 80 ab 50 cd 04 9e  |............P...|
+00000010  db 19 e4 18 26 ff 59 41  20 02 a5 a2 20 a3 1c 44  |....&.YA ... ..D|
+00000020  02 bc 9a 1c d9 d7 5d 5b  55 fc 2a 4d 2b 03 22 b1  |......][U.*M+.".|
+00000030  de 96 10 84 6f e3 f2 22  2d 6f cb 29 07 43 a6 6e  |....o.."-o.).C.n|
+00000040  ce 23 64 f7 72 2b dc 9b  c0 6f 7f bd 8e cf e2 7f  |.#d.r+...o......|
+00000050  75 12 24 72 23 6b 26 08  69 76 17 c0 21 91 c0 7d  |u.$r#k&.iv..!..}|
+00000060  8c 8f 20 83 08 02 0d 73  27 23 91 35 5f 3f e6 56  |.. ....s'#.5_?.V|
+00000070  1d 69 d3 1d 3b 0e fa 60  86 8b 40 ad c0 48 59 60  |.i..;..`.. at ..HY`|
+00000080  45 eb b0 77 2c 91 94 75  fd 6a d3 14 03 01 00 01  |E..w,..u.j......|
+00000090  01 16 03 01 00 28 8b 25  c1 8f 25 32 b5 cb 74 6d  |.....(.%..%2..tm|
+000000a0  08 67 59 a3 ae ae 16 f9  fa 03 f6 54 42 f4 56 3f  |.gY........TB.V?|
+000000b0  c4 12 66 f3 1a b0 48 95  24 79 fe 41 a5 d1        |..f...H.$y.A..|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 28 f4 cf 23 f8 86  |..........(..#..|
-00000010  83 df 44 af 1c 25 b1 51  84 5b 6a f3 0e 6b 47 5c  |..D..%.Q.[j..kG\|
-00000020  2a 59 67 db 42 11 f9 53  58 4e db 6f 00 b2 20 5b  |*Yg.B..SXN.o.. [|
-00000030  ae a3 43 17 03 01 00 18  df e0 22 d6 05 ab 79 c7  |..C......."...y.|
-00000040  87 8a 82 83 01 bc 06 45  36 74 4d 1c 40 96 97 5f  |.......E6tM. at .._|
-00000050  17 03 01 00 28 49 bd b7  e9 41 6b eb b1 aa 89 60  |....(I...Ak....`|
-00000060  21 91 df bf f4 7a 49 9d  54 04 4a 16 1a d1 44 9a  |!....zI.T.J...D.|
-00000070  09 6c 4f 01 3d c0 2f d5  a3 72 a3 b2 fe 15 03 01  |.lO.=./..r......|
-00000080  00 18 5c 7a de a0 ef ed  56 99 99 01 5f b4 32 b3  |..\z....V..._.2.|
-00000090  00 be c6 cc 7e bb 6f 82  7d f7                    |....~.o.}.|
+00000000  14 03 01 00 01 01 16 03  01 00 28 ff 69 ed 0f 20  |..........(.i.. |
+00000010  ff e1 42 78 b9 bc a8 61  48 82 08 a0 01 a5 98 91  |..Bx...aH.......|
+00000020  3e 39 d4 6d 17 38 a2 04  18 ed 90 3c f0 cf 6a 9a  |>9.m.8.....<..j.|
+00000030  ea c5 45 17 03 01 00 18  b5 76 2c 0e f1 34 51 e5  |..E......v,..4Q.|
+00000040  f5 38 d3 9f c9 c5 d5 19  35 c3 2e ec 18 df 8e c8  |.8......5.......|
+00000050  17 03 01 00 28 47 6f e9  c0 fa b3 21 ec 6c 16 e7  |....(Go....!.l..|
+00000060  71 a8 09 15 17 86 68 1c  cf fa ea 37 68 d3 33 ef  |q.....h....7h.3.|
+00000070  4a b1 95 46 5b 16 d7 95  f8 13 65 2f 93 15 03 01  |J..F[.....e/....|
+00000080  00 18 1b 0c 09 81 ff fc  6d 82 84 ab 83 98 fc 72  |........m......r|
+00000090  f5 4a a0 eb 08 96 79 01  76 26                    |.J....y.v&|
diff --git a/src/crypto/tls/testdata/Server-TLSv10-RSA-AES b/src/crypto/tls/testdata/Server-TLSv10-RSA-AES
index e51bc17..c175029 100644
--- a/src/crypto/tls/testdata/Server-TLSv10-RSA-AES
+++ b/src/crypto/tls/testdata/Server-TLSv10-RSA-AES
@@ -1,77 +1,75 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 37 01 00 00  33 03 01 39 72 8e 06 ed  |....7...3..9r...|
-00000010  d2 74 5c 02 74 0e 2b 7a  bd 54 ce be 17 a0 4f 1a  |.t\.t.+z.T....O.|
-00000020  c5 72 b1 e8 3e 2e 90 68  ff fc 6e 00 00 04 00 2f  |.r..>..h..n..../|
-00000030  00 ff 02 01 00 00 05 00  0f 00 01 01              |............|
+00000000  16 03 01 00 39 01 00 00  35 03 01 82 f3 04 d5 71  |....9...5......q|
+00000010  d8 65 69 36 46 cb 45 77  b2 ef 00 75 98 e4 16 d2  |.ei6F.Ew...u....|
+00000020  70 f7 3c 97 84 49 ef da  5d cd 64 00 00 04 00 2f  |p.<..I..].d..../|
+00000030  00 ff 01 00 00 08 00 16  00 00 00 17 00 00        |..............|
 >>> Flow 2 (server to client)
 00000000  16 03 01 00 31 02 00 00  2d 03 01 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
-00000030  05 ff 01 00 01 00 16 03  01 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 01 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000030  05 ff 01 00 01 00 16 03  01 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 01 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 01 00 86 10 00 00  82 00 80 0e a2 23 fe 89  |.............#..|
-00000010  46 08 20 98 da 4d 91 a4  48 40 ab 03 df 1f 00 67  |F. ..M..H at .....g|
-00000020  f3 fb fb 22 f7 8e d6 65  2c 43 a7 f4 9c 0e 25 cc  |..."...e,C....%.|
-00000030  d9 3b b5 58 df bd 93 27  1c df 69 37 27 01 cb 0d  |.;.X...'..i7'...|
-00000040  b4 f4 a6 8d 91 fe ef dc  9a e2 09 7c 53 1a 73 6d  |...........|S.sm|
-00000050  b9 f6 89 0a 1f 94 f0 26  25 ef 73 54 20 d5 8d 77  |.......&%.sT ..w|
-00000060  36 2e e7 4c 9a f1 4a be  ae 6e b6 be 16 10 31 42  |6..L..J..n....1B|
-00000070  9e d2 49 41 2c 32 52 11  bc 85 2d fa 39 80 9b f9  |..IA,2R...-.9...|
-00000080  95 fe e8 88 2a a2 57 65  7e 38 b2 14 03 01 00 01  |....*.We~8......|
-00000090  01 16 03 01 00 30 1c 6f  91 45 16 ed 25 82 ee 5f  |.....0.o.E..%.._|
-000000a0  f9 f0 09 0c a4 ad 56 61  e5 b7 a2 05 50 02 b8 80  |......Va....P...|
-000000b0  ef 73 d1 11 3c 25 50 44  0d ba b5 7c fd 5d 7a df  |.s..<%PD...|.]z.|
-000000c0  14 62 1b 29 be 29                                 |.b.).)|
+00000000  16 03 01 00 86 10 00 00  82 00 80 9c a1 18 77 22  |..............w"|
+00000010  f5 a1 cf 4d cc df 27 7c  c5 7e 98 24 24 be 2f b2  |...M..'|.~.$$./.|
+00000020  1d d7 b8 2f fe 90 73 d0  fc f6 88 3c 91 a4 bc dc  |.../..s....<....|
+00000030  b9 0b 48 0d 55 e5 9f c1  8a 6c 1c 7d 4d a9 12 d5  |..H.U....l.}M...|
+00000040  87 4b 9a 77 74 3d 33 8c  c7 17 fb 32 09 df 86 f1  |.K.wt=3....2....|
+00000050  93 cc 17 f9 08 bd bc 0e  38 df 9d 82 ad cc 70 0c  |........8.....p.|
+00000060  f5 8b 8d 99 e8 5f 3e e5  a6 c7 c2 6a 67 02 90 82  |....._>....jg...|
+00000070  28 9a 72 e1 3e 77 51 10  84 29 21 09 56 36 f2 6a  |(.r.>wQ..)!.V6.j|
+00000080  1d 15 08 7b 44 41 43 59  55 8d 52 14 03 01 00 01  |...{DACYU.R.....|
+00000090  01 16 03 01 00 30 06 5b  20 42 7e 7b 1f 4b 7c 36  |.....0.[ B~{.K|6|
+000000a0  99 bb c6 b4 ea a1 19 3e  02 0c 3b 3a 38 be 80 11  |.......>..;:8...|
+000000b0  29 72 a8 12 92 ad 24 9d  bf 01 3e ef 9a f1 db 33  |)r....$...>....3|
+000000c0  3e c1 dc d2 51 b1                                 |>...Q.|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 5e 8c b1 dc 1f  |..........0^....|
-00000010  b3 18 85 4a 46 02 fb 34  e4 05 56 78 4c e3 34 63  |...JF..4..VxL.4c|
-00000020  06 08 b4 ee 36 e2 28 ab  c9 98 ee 26 4e 5b 5d 42  |....6.(....&N[]B|
-00000030  5f f8 e1 d1 2f 8b c8 ef  5a 65 40 17 03 01 00 20  |_.../...Ze at .... |
-00000040  e7 92 6e b1 60 b9 f8 cd  53 d3 37 5b 44 74 1c af  |..n.`...S.7[Dt..|
-00000050  90 93 13 8e 55 25 cc 9f  57 8c f3 06 f7 ba e0 f9  |....U%..W.......|
-00000060  17 03 01 00 30 fc 83 e6  4e 8c 65 8f d1 7c c7 f4  |....0...N.e..|..|
-00000070  8b 68 0d 5d da 8e 49 45  68 ea 4c e3 7f 7d 84 87  |.h.]..IEh.L..}..|
-00000080  2f 63 e0 fb 43 24 04 cd  e2 38 32 50 0a 4c 43 ce  |/c..C$...82P.LC.|
-00000090  3b 12 a5 6b 99 15 03 01  00 20 2a 42 d8 57 26 79  |;..k..... *B.W&y|
-000000a0  51 ee 79 9d b2 83 b8 49  a4 e9 a2 08 34 73 c4 f5  |Q.y....I....4s..|
-000000b0  53 21 4b 78 ec 5b ce b4  4e a0                    |S!Kx.[..N.|
+00000000  14 03 01 00 01 01 16 03  01 00 30 2e d5 04 91 6d  |..........0....m|
+00000010  32 12 8b 41 4a 46 2c f3  7f d4 16 0a 21 c2 ac 88  |2..AJF,.....!...|
+00000020  09 a0 b5 0d 65 4e 44 e1  92 5a ae b8 3f 61 1f 35  |....eND..Z..?a.5|
+00000030  ab 3a fe bd f8 3c 2c 42  dd 68 0f 17 03 01 00 20  |.:...<,B.h..... |
+00000040  6e d4 08 98 bf b7 18 84  ee 68 f8 17 88 c5 13 7a  |n........h.....z|
+00000050  73 e0 c6 ca 0d 21 4d 6b  44 dc 94 36 6c e4 a0 2f  |s....!MkD..6l../|
+00000060  17 03 01 00 30 a0 45 d0  88 5d 96 48 26 46 37 33  |....0.E..].H&F73|
+00000070  f6 48 f3 38 2e 38 d7 b6  ef d5 25 bf f3 1b b6 78  |.H.8.8....%....x|
+00000080  32 a7 9c fe be 55 35 f2  07 5b b7 14 87 89 80 f2  |2....U5..[......|
+00000090  cc d5 cb c8 57 15 03 01  00 20 80 2a 8e 6c b8 5a  |....W.... .*.l.Z|
+000000a0  41 b4 ae 56 ca 3f 8b a2  e1 ea a0 55 64 b5 60 44  |A..V.?.....Ud.`D|
+000000b0  8f de 33 c6 37 f7 df b5  d9 c3                    |..3.7.....|
diff --git a/src/crypto/tls/testdata/Server-TLSv10-RSA-RC4 b/src/crypto/tls/testdata/Server-TLSv10-RSA-RC4
index 8d54463..3d788c3 100644
--- a/src/crypto/tls/testdata/Server-TLSv10-RSA-RC4
+++ b/src/crypto/tls/testdata/Server-TLSv10-RSA-RC4
@@ -1,71 +1,69 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 37 01 00 00  33 03 01 b3 82 99 50 b0  |....7...3.....P.|
-00000010  1e 7a 46 48 9d 8e 93 32  3b 01 bc 50 e9 5c eb 91  |.zFH...2;..P.\..|
-00000020  25 4b c1 ea 0a 91 c9 b3  2b 54 90 00 00 04 00 05  |%K......+T......|
-00000030  00 ff 02 01 00 00 05 00  0f 00 01 01              |............|
+00000000  16 03 01 00 39 01 00 00  35 03 01 71 34 00 f7 c4  |....9...5..q4...|
+00000010  e6 94 b4 ca f2 af d5 0a  82 ce d4 f6 b7 4a a7 d1  |.............J..|
+00000020  1a 88 65 b2 3c b2 6c ec  f7 eb 4a 00 00 04 00 05  |..e.<.l...J.....|
+00000030  00 ff 01 00 00 08 00 16  00 00 00 17 00 00        |..............|
 >>> Flow 2 (server to client)
 00000000  16 03 01 00 31 02 00 00  2d 03 01 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 05 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  01 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 01 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000030  05 ff 01 00 01 00 16 03  01 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 01 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 01 00 86 10 00 00  82 00 80 05 1c 93 2a 69  |..............*i|
-00000010  f7 0b 1d 59 ea ca c2 4a  b1 7c ef 22 4c 7b 31 5f  |...Y...J.|."L{1_|
-00000020  18 8d 32 b6 db 75 8c f8  45 07 27 e1 9f 3f 9d 0b  |..2..u..E.'..?..|
-00000030  02 ac 2c 3f aa bf 79 fb  d4 af 98 0c b2 c0 03 4b  |..,?..y........K|
-00000040  86 26 c3 30 f3 ea 2b 1a  ab 70 90 8d 01 2b 0e ff  |.&.0..+..p...+..|
-00000050  4c 10 9a 29 75 cb 14 bb  85 80 98 35 fb 82 e8 b5  |L..)u......5....|
-00000060  80 6f 9d e6 3b b6 14 36  bb 61 8e 18 f2 6b da 09  |.o..;..6.a...k..|
-00000070  71 9c 6d 1e c3 60 a9 c5  8b 4e 77 41 db ec 12 49  |q.m..`...NwA...I|
-00000080  a4 c2 e2 10 ce 7f 18 05  b9 74 aa 14 03 01 00 01  |.........t......|
-00000090  01 16 03 01 00 24 3d 90  d0 f6 6f 15 94 05 a0 fb  |.....$=...o.....|
-000000a0  43 83 55 b5 b1 ef e5 fd  fc 00 d3 d5 25 b4 7c 9f  |C.U.........%.|.|
-000000b0  e0 82 99 2a 6d 5a 26 7c  05 21                    |...*mZ&|.!|
+00000000  16 03 01 00 86 10 00 00  82 00 80 a5 75 5a 20 2c  |............uZ ,|
+00000010  31 f7 61 dc 73 c7 f6 4c  06 d2 b9 c0 e8 5f cc 0c  |1.a.s..L....._..|
+00000020  51 70 0a 30 b2 8a bb 3b  4c 37 f6 d3 38 da 13 48  |Qp.0...;L7..8..H|
+00000030  90 4f fe 41 ec 53 3c fb  07 26 77 68 07 a0 fb 71  |.O.A.S<..&wh...q|
+00000040  b6 cc 3c cd b4 64 03 08  3a 76 97 6e 6c f1 b4 a9  |..<..d..:v.nl...|
+00000050  af f4 e0 ce bf 36 b9 8e  37 12 de 5b ac 24 06 63  |.....6..7..[.$.c|
+00000060  e2 fb 13 33 be 3b 8d 93  e3 10 95 29 21 b2 22 77  |...3.;.....)!."w|
+00000070  cb 95 b2 13 b3 76 47 98  13 1b a8 cc 50 47 ed 50  |.....vG.....PG.P|
+00000080  f0 cc ca 5a c6 a0 1e c9  9c 97 58 14 03 01 00 01  |...Z......X.....|
+00000090  01 16 03 01 00 24 e7 fd  a2 7e fd 6f 53 da 29 68  |.....$...~.oS.)h|
+000000a0  c3 49 2e e9 69 a1 94 b9  e4 a0 cb a2 94 14 a6 42  |.I..i..........B|
+000000b0  df 75 1e da 95 e5 60 e3  35 f1                    |.u....`.5.|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 24 28 d0 1f ec 1d  |..........$(....|
-00000010  9e 1d e3 80 6b 6d 3e 8b  c5 f7 9c 14 a9 0b c3 53  |....km>........S|
-00000020  fd 48 d0 b3 eb d1 49 97  97 71 e9 36 b9 e6 3a 17  |.H....I..q.6..:.|
-00000030  03 01 00 21 c3 b6 2e 02  05 86 0f 57 04 dd 88 33  |...!.......W...3|
-00000040  0a ed 1d d5 a8 0f 55 54  c5 8c 87 5b 11 b7 80 7f  |......UT...[....|
-00000050  30 79 e0 64 ee 15 03 01  00 16 b1 50 de 3d 18 05  |0y.d.......P.=..|
-00000060  2f db 37 4c db 62 f1 c8  d5 19 ca c2 fb a5 8a 37  |/.7L.b.........7|
+00000000  14 03 01 00 01 01 16 03  01 00 24 44 a6 c8 7b 5f  |..........$D..{_|
+00000010  b9 4e c2 62 2d e0 c3 9f  76 0f b3 e5 f5 07 b7 c0  |.N.b-...v.......|
+00000020  93 cd 1f 32 3c 0a 7a 83  57 4a 24 59 ac 95 f9 17  |...2<.z.WJ$Y....|
+00000030  03 01 00 21 6f 02 76 2e  70 82 a0 6c 11 ce 3c b8  |...!o.v.p..l..<.|
+00000040  dd d3 9e 2a ee ce d7 7f  63 1a 5b 35 d0 46 68 7d  |...*....c.[5.Fh}|
+00000050  21 6e 5b 64 fc 15 03 01  00 16 81 56 32 7d 51 e4  |!n[d.......V2}Q.|
+00000060  08 53 85 45 65 c3 87 ac  b0 58 70 4f 6f f7 64 4e  |.S.Ee....XpOo.dN|
diff --git a/src/crypto/tls/testdata/Server-TLSv11-FallbackSCSV b/src/crypto/tls/testdata/Server-TLSv11-FallbackSCSV
index a6e2137..209e621 100644
--- a/src/crypto/tls/testdata/Server-TLSv11-FallbackSCSV
+++ b/src/crypto/tls/testdata/Server-TLSv11-FallbackSCSV
@@ -1,17 +1,10 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 cf 01 00 00  cb 03 02 ee 33 c1 3f a6  |............3.?.|
-00000010  62 ba a6 4f c7 32 25 0f  15 66 f7 35 a2 cf c0 cd  |b..O.2%..f.5....|
-00000020  48 93 77 1c 04 1f fb 65  41 37 ca 00 00 70 c0 14  |H.w....eA7...p..|
-00000030  c0 0a 00 39 00 38 00 37  00 36 00 88 00 87 00 86  |...9.8.7.6......|
-00000040  00 85 c0 0f c0 05 00 35  00 84 c0 13 c0 09 00 33  |.......5.......3|
-00000050  00 32 00 31 00 30 00 9a  00 99 00 98 00 97 00 45  |.2.1.0.........E|
-00000060  00 44 00 43 00 42 c0 0e  c0 04 00 2f 00 96 00 41  |.D.C.B...../...A|
-00000070  00 07 c0 11 c0 07 c0 0c  c0 02 00 05 00 04 c0 12  |................|
-00000080  c0 08 00 16 00 13 00 10  00 0d c0 0d c0 03 00 0a  |................|
-00000090  00 15 00 12 00 0f 00 0c  00 09 00 ff 56 00 02 01  |............V...|
-000000a0  00 00 31 00 0b 00 04 03  00 01 02 00 0a 00 1c 00  |..1.............|
-000000b0  1a 00 17 00 19 00 1c 00  1b 00 18 00 1a 00 16 00  |................|
-000000c0  0e 00 0d 00 0b 00 0c 00  09 00 0a 00 23 00 00 00  |............#...|
-000000d0  0f 00 01 01                                       |....|
+00000000  16 03 01 00 63 01 00 00  5f 03 02 6e 78 cc 6a ea  |....c..._..nx.j.|
+00000010  13 aa a8 20 76 7d 32 ca  c7 3f be 88 36 ae fb c3  |... v}2..?..6...|
+00000020  ca 95 35 70 54 20 3b 18  3b ba 82 00 00 14 c0 0a  |..5pT ;.;.......|
+00000030  c0 14 00 39 c0 09 c0 13  00 33 00 35 00 2f 00 ff  |...9.....3.5./..|
+00000040  56 00 01 00 00 22 00 0b  00 04 03 00 01 02 00 0a  |V...."..........|
+00000050  00 0a 00 08 00 1d 00 17  00 19 00 18 00 23 00 00  |.............#..|
+00000060  00 16 00 00 00 17 00 00                           |........|
 >>> Flow 2 (server to client)
 00000000  15 03 02 00 02 02 56                              |......V|
diff --git a/src/crypto/tls/testdata/Server-TLSv11-RSA-RC4 b/src/crypto/tls/testdata/Server-TLSv11-RSA-RC4
index b3916f9..18debc4 100644
--- a/src/crypto/tls/testdata/Server-TLSv11-RSA-RC4
+++ b/src/crypto/tls/testdata/Server-TLSv11-RSA-RC4
@@ -1,71 +1,69 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 37 01 00 00  33 03 02 95 4a cf f5 14  |....7...3...J...|
-00000010  56 9b d6 be 0c ba 0d b2  ad 65 47 d2 c6 ce 84 c9  |V........eG.....|
-00000020  f4 80 2a 4e 75 df ff 48  cf 48 9b 00 00 04 00 05  |..*Nu..H.H......|
-00000030  00 ff 02 01 00 00 05 00  0f 00 01 01              |............|
+00000000  16 03 01 00 39 01 00 00  35 03 02 15 67 73 bf 3f  |....9...5...gs.?|
+00000010  6f 15 30 c2 34 2e c6 1b  23 3a 42 45 4d d9 87 a2  |o.0.4...#:BEM...|
+00000020  e7 b8 de 1c b8 2b cc 21  7a 0b a1 00 00 04 00 05  |.....+.!z.......|
+00000030  00 ff 01 00 00 08 00 16  00 00 00 17 00 00        |..............|
 >>> Flow 2 (server to client)
 00000000  16 03 02 00 31 02 00 00  2d 03 02 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 05 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  02 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 02 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000030  05 ff 01 00 01 00 16 03  02 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 02 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 02 00 86 10 00 00  82 00 80 19 c3 d4 f2 e4  |................|
-00000010  bf 4a 52 90 a4 65 f6 e7  29 1a 7f ef 0e a4 1e b4  |.JR..e..).......|
-00000020  c2 df b2 83 43 4a 1a f4  b6 cd 51 a5 24 62 3f e1  |....CJ....Q.$b?.|
-00000030  fb 5f 7e b1 10 08 3b 8a  ab eb 3a a3 72 ba 31 1c  |._~...;...:.r.1.|
-00000040  23 bd ef 2e 3d 13 dc 61  88 a6 af ca 80 82 5d e4  |#...=..a......].|
-00000050  d6 a2 d3 13 80 87 c6 ad  a5 13 4e f1 b6 a0 5d fa  |..........N...].|
-00000060  ed a7 0d 37 58 f1 54 38  18 f5 be db 90 9f 07 4a  |...7X.T8.......J|
-00000070  67 25 c9 8d 9d 5e 07 62  ca db 72 74 b5 bd a0 ed  |g%...^.b..rt....|
-00000080  d0 95 9f 3e cd 37 b8 96  df df 3b 14 03 02 00 01  |...>.7....;.....|
-00000090  01 16 03 02 00 24 80 53  7a 8f 23 06 a7 6b e6 be  |.....$.Sz.#..k..|
-000000a0  61 c2 1a c8 35 30 6b e2  2f 82 f3 46 ff e3 1d bd  |a...50k./..F....|
-000000b0  68 e9 a2 78 49 33 05 ca  d9 41                    |h..xI3...A|
+00000000  16 03 02 00 86 10 00 00  82 00 80 75 8e 85 93 be  |...........u....|
+00000010  53 df e0 4f 65 92 ed 3d  58 34 f8 06 fd 36 e4 5a  |S..Oe..=X4...6.Z|
+00000020  f7 7a 59 88 f6 ac bd de  21 ed c4 04 0d 35 19 cd  |.zY.....!....5..|
+00000030  ff 3b 9f c4 bc 93 4f 21  2a 36 a3 99 a4 6f eb 1e  |.;....O!*6...o..|
+00000040  7b b4 a8 a7 6d 69 a5 93  b6 e3 d2 2d be 7a c8 f3  |{...mi.....-.z..|
+00000050  9f 25 9e f9 51 75 d9 4f  05 41 0e 17 56 31 4e 3f  |.%..Qu.O.A..V1N?|
+00000060  c0 15 d8 c4 29 4d e5 92  f9 ed 50 b6 88 f1 41 ea  |....)M....P...A.|
+00000070  cb 5a 8c 50 12 78 16 e7  21 b6 11 ca 2c 49 cf b6  |.Z.P.x..!...,I..|
+00000080  d2 1a 16 28 f7 08 b5 c9  61 e0 18 14 03 02 00 01  |...(....a.......|
+00000090  01 16 03 02 00 24 a1 cf  1b 5d dc 4c 9c 2c d7 39  |.....$...].L.,.9|
+000000a0  af 13 e9 04 48 c0 2a aa  6f 3a 9c fb 9e 0a 25 55  |....H.*.o:....%U|
+000000b0  7e 82 3d 1b 78 d1 e3 e0  f5 30                    |~.=.x....0|
 >>> Flow 4 (server to client)
-00000000  14 03 02 00 01 01 16 03  02 00 24 8f 06 3e 7b 8c  |..........$..>{.|
-00000010  fb f2 3d 9e 5c a9 46 56  79 2a 3a ba ad 25 30 57  |..=.\.FVy*:..%0W|
-00000020  f9 f1 16 70 51 5d 73 7e  47 74 8d c0 84 b0 31 17  |...pQ]s~Gt....1.|
-00000030  03 02 00 21 76 09 88 df  7e f7 2f c9 3d 86 b9 30  |...!v...~./.=..0|
-00000040  b0 5c ac ea db c6 d0 d5  69 55 91 7b a1 72 0b 4d  |.\......iU.{.r.M|
-00000050  7d ae 6f aa 50 15 03 02  00 16 8c 31 73 86 1a c7  |}.o.P......1s...|
-00000060  ef 08 52 8a 7d 30 b8 00  3b 62 4d 21 7b 81 2c 76  |..R.}0..;bM!{.,v|
+00000000  14 03 02 00 01 01 16 03  02 00 24 7b 68 71 56 0f  |..........${hqV.|
+00000010  a5 46 1c 13 34 81 b5 b6  ba 29 fb 41 46 dc fe 78  |.F..4....).AF..x|
+00000020  cc 0b 2d 75 bd fe c1 55  45 b1 fc 04 28 5e b1 17  |..-u...UE...(^..|
+00000030  03 02 00 21 0b fa a9 2f  9e 82 5b 77 30 c2 27 88  |...!.../..[w0.'.|
+00000040  f5 f3 50 47 7b 62 4c 7a  d4 07 71 74 46 da 24 de  |..PG{bLz..qtF.$.|
+00000050  bf 3f 56 a7 9b 15 03 02  00 16 85 26 8a 89 33 21  |.?V........&..3!|
+00000060  36 ce 69 83 84 50 fc 8f  99 b3 43 ad 6b 14 1e b2  |6.i..P....C.k...|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ALPN b/src/crypto/tls/testdata/Server-TLSv12-ALPN
index df832d7..3e90ebd 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ALPN
+++ b/src/crypto/tls/testdata/Server-TLSv12-ALPN
@@ -1,109 +1,94 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 01 4d 01 00 01  49 03 03 ac 76 61 d8 20  |....M...I...va. |
-00000010  b3 c3 1d c2 3d c2 a4 b9  e2 46 a2 a1 0a 5e 08 56  |....=....F...^.V|
-00000020  4a aa 59 43 42 d6 21 9c  46 0c 21 00 00 b6 c0 30  |J.YCB.!.F.!....0|
-00000030  c0 2c c0 28 c0 24 c0 14  c0 0a 00 a5 00 a3 00 a1  |.,.(.$..........|
-00000040  00 9f 00 6b 00 6a 00 69  00 68 00 39 00 38 00 37  |...k.j.i.h.9.8.7|
-00000050  00 36 00 88 00 87 00 86  00 85 c0 32 c0 2e c0 2a  |.6.........2...*|
-00000060  c0 26 c0 0f c0 05 00 9d  00 3d 00 35 00 84 c0 2f  |.&.......=.5.../|
-00000070  c0 2b c0 27 c0 23 c0 13  c0 09 00 a4 00 a2 00 a0  |.+.'.#..........|
-00000080  00 9e 00 67 00 40 00 3f  00 3e 00 33 00 32 00 31  |...g. at .?.>.3.2.1|
-00000090  00 30 00 9a 00 99 00 98  00 97 00 45 00 44 00 43  |.0.........E.D.C|
-000000a0  00 42 c0 31 c0 2d c0 29  c0 25 c0 0e c0 04 00 9c  |.B.1.-.).%......|
-000000b0  00 3c 00 2f 00 96 00 41  00 07 c0 11 c0 07 c0 0c  |.<./...A........|
-000000c0  c0 02 00 05 00 04 c0 12  c0 08 00 16 00 13 00 10  |................|
-000000d0  00 0d c0 0d c0 03 00 0a  00 15 00 12 00 0f 00 0c  |................|
-000000e0  00 09 00 ff 02 01 00 00  69 00 0b 00 04 03 00 01  |........i.......|
-000000f0  02 00 0a 00 1c 00 1a 00  17 00 19 00 1c 00 1b 00  |................|
-00000100  18 00 1a 00 16 00 0e 00  0d 00 0b 00 0c 00 09 00  |................|
-00000110  0a 00 23 00 00 00 0d 00  20 00 1e 06 01 06 02 06  |..#..... .......|
-00000120  03 05 01 05 02 05 03 04  01 04 02 04 03 03 01 03  |................|
-00000130  02 03 03 02 01 02 02 02  03 00 0f 00 01 01 00 10  |................|
-00000140  00 10 00 0e 06 70 72 6f  74 6f 32 06 70 72 6f 74  |.....proto2.prot|
-00000150  6f 31                                             |o1|
+00000000  16 03 01 00 bf 01 00 00  bb 03 03 18 c9 32 10 f3  |.............2..|
+00000010  be ff a8 60 c5 2a 03 cb  25 8a b3 54 8d 70 27 90  |...`.*..%..T.p'.|
+00000020  74 1e 15 3e 61 48 9b be  f0 77 1f 00 00 38 c0 2c  |t..>aH...w...8.,|
+00000030  c0 30 00 9f cc a9 cc a8  cc aa c0 2b c0 2f 00 9e  |.0.........+./..|
+00000040  c0 24 c0 28 00 6b c0 23  c0 27 00 67 c0 0a c0 14  |.$.(.k.#.'.g....|
+00000050  00 39 c0 09 c0 13 00 33  00 9d 00 9c 00 3d 00 3c  |.9.....3.....=.<|
+00000060  00 35 00 2f 00 ff 01 00  00 5a 00 0b 00 04 03 00  |.5./.....Z......|
+00000070  01 02 00 0a 00 0a 00 08  00 1d 00 17 00 19 00 18  |................|
+00000080  00 23 00 00 00 0d 00 20  00 1e 06 01 06 02 06 03  |.#..... ........|
+00000090  05 01 05 02 05 03 04 01  04 02 04 03 03 01 03 02  |................|
+000000a0  03 03 02 01 02 02 02 03  00 10 00 10 00 0e 06 70  |...............p|
+000000b0  72 6f 74 6f 32 06 70 72  6f 74 6f 31 00 16 00 00  |roto2.proto1....|
+000000c0  00 17 00 00                                       |....|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 42 02 00 00  3e 03 03 00 00 00 00 00  |....B...>.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 c0 30 00 00  |.............0..|
 00000030  16 00 23 00 00 ff 01 00  01 00 00 10 00 09 00 07  |..#.............|
-00000040  06 70 72 6f 74 6f 31 16  03 03 02 71 0b 00 02 6d  |.proto1....q...m|
-00000050  00 02 6a 00 02 67 30 82  02 63 30 82 01 cc a0 03  |..j..g0..c0.....|
-00000060  02 01 02 02 09 00 a2 73  00 0c 81 00 cb f3 30 0d  |.......s......0.|
-00000070  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 30 2b 31  |..*.H........0+1|
-00000080  17 30 15 06 03 55 04 0a  13 0e 47 6f 6f 67 6c 65  |.0...U....Google|
-00000090  20 54 45 53 54 49 4e 47  31 10 30 0e 06 03 55 04  | TESTING1.0...U.|
-000000a0  03 13 07 47 6f 20 52 6f  6f 74 30 1e 17 0d 31 35  |...Go Root0...15|
-000000b0  30 31 30 31 30 30 30 30  30 30 5a 17 0d 32 35 30  |0101000000Z..250|
-000000c0  31 30 31 30 30 30 30 30  30 5a 30 26 31 17 30 15  |101000000Z0&1.0.|
-000000d0  06 03 55 04 0a 13 0e 47  6f 6f 67 6c 65 20 54 45  |..U....Google TE|
-000000e0  53 54 49 4e 47 31 0b 30  09 06 03 55 04 03 13 02  |STING1.0...U....|
-000000f0  47 6f 30 81 9f 30 0d 06  09 2a 86 48 86 f7 0d 01  |Go0..0...*.H....|
-00000100  01 01 05 00 03 81 8d 00  30 81 89 02 81 81 00 af  |........0.......|
-00000110  87 88 f6 20 1b 95 65 6c  14 ab 44 05 af 3b 45 14  |... ..el..D..;E.|
-00000120  e3 b7 6d fd 00 63 4d 95  7f fe 6a 62 35 86 c0 4a  |..m..cM...jb5..J|
-00000130  f9 18 7c f6 aa 25 5e 7a  64 31 66 00 ba f4 8e 92  |..|..%^zd1f.....|
-00000140  af c7 6b d8 76 d4 f3 5f  41 cb 6e 56 15 97 1b 97  |..k.v.._A.nV....|
-00000150  c1 3c 12 39 21 66 3d 2b  16 d1 bc db 1c c0 a7 da  |.<.9!f=+........|
-00000160  b7 ca ad ba da cb d5 21  50 ec de 8d ab d1 6b 81  |.......!P.....k.|
-00000170  4b 89 02 f3 c4 be c1 6c  89 b1 44 84 bd 21 d1 04  |K......l..D..!..|
-00000180  7d 9d 16 4d f9 82 15 f6  ef fa d6 09 47 f2 fb 02  |}..M........G...|
-00000190  03 01 00 01 a3 81 93 30  81 90 30 0e 06 03 55 1d  |.......0..0...U.|
-000001a0  0f 01 01 ff 04 04 03 02  05 a0 30 1d 06 03 55 1d  |..........0...U.|
-000001b0  25 04 16 30 14 06 08 2b  06 01 05 05 07 03 01 06  |%..0...+........|
-000001c0  08 2b 06 01 05 05 07 03  02 30 0c 06 03 55 1d 13  |.+.......0...U..|
-000001d0  01 01 ff 04 02 30 00 30  19 06 03 55 1d 0e 04 12  |.....0.0...U....|
-000001e0  04 10 12 50 8d 89 6f 1b  d1 dc 54 4d 6e cb 69 5e  |...P..o...TMn.i^|
-000001f0  06 f4 30 1b 06 03 55 1d  23 04 14 30 12 80 10 bf  |..0...U.#..0....|
-00000200  3d b6 a9 66 f2 b8 40 cf  ea b4 03 78 48 1a 41 30  |=..f.. at ....xH.A0|
-00000210  19 06 03 55 1d 11 04 12  30 10 82 0e 65 78 61 6d  |...U....0...exam|
-00000220  70 6c 65 2e 67 6f 6c 61  6e 67 30 0d 06 09 2a 86  |ple.golang0...*.|
-00000230  48 86 f7 0d 01 01 0b 05  00 03 81 81 00 92 7c af  |H.............|.|
-00000240  91 55 12 18 96 59 31 a6  48 40 d5 2d d5 ee bb 02  |.U...Y1.H at .-....|
-00000250  a0 f5 c2 1e 7c 9b b3 30  7d 3c dc 76 da 4f 3d c0  |....|..0}<.v.O=.|
-00000260  fa ae 2d 33 24 6b 03 7b  1b 67 59 11 21 b5 11 bc  |..-3$k.{.gY.!...|
-00000270  77 b9 d9 e0 6e a8 2d 2e  35 fa 64 5f 22 3e 63 10  |w...n.-.5.d_">c.|
-00000280  6b be ff 14 86 6d 0d f0  15 31 a8 14 38 1e 3b 84  |k....m...1..8.;.|
-00000290  87 2c cb 98 ed 51 76 b9  b1 4f dd db 9b 84 04 86  |.,...Qv..O......|
-000002a0  40 fa 51 dd ba b4 8d eb  e3 46 de 46 b9 4f 86 c7  |@.Q......F.F.O..|
-000002b0  f9 a4 c2 41 34 ac cc f6  ea b0 ab 39 18 16 03 03  |...A4......9....|
-000002c0  00 cd 0c 00 00 c9 03 00  17 41 04 1e 18 37 ef 0d  |.........A...7..|
-000002d0  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
-000002e0  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
-000002f0  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
-00000300  a6 b5 68 1a 41 03 56 6b  dc 5a 89 05 01 00 80 aa  |..h.A.Vk.Z......|
-00000310  8c a6 e1 51 65 fc 99 37  cf 63 d8 fd 04 52 d5 50  |...Qe..7.c...R.P|
-00000320  1f 0a f5 90 58 48 19 8d  d8 0b 64 23 e4 24 56 b4  |....XH....d#.$V.|
-00000330  e5 87 0f 88 a1 7a 29 fa  88 79 99 75 6d 53 a9 50  |.....z)..y.umS.P|
-00000340  a4 9c b9 47 c2 51 87 10  b9 a5 e3 6f a9 38 b8 83  |...G.Q.....o.8..|
-00000350  0d 39 b5 28 27 5f ec 9d  a3 2d 1c 53 6b da 93 0d  |.9.('_...-.Sk...|
-00000360  cc cf 0c 27 7e d2 f0 05  d5 c0 04 dc 6d d4 2e 03  |...'~.......m...|
-00000370  a7 16 98 58 e4 8d fd 14  6b bb 0c 09 b0 16 35 9e  |...X....k.....5.|
-00000380  78 3a 29 21 b5 2f 13 37  94 ae f7 fe 54 89 c0 16  |x:)!./.7....T...|
-00000390  03 03 00 04 0e 00 00 00                           |........|
+00000040  06 70 72 6f 74 6f 31 16  03 03 02 59 0b 00 02 55  |.proto1....Y...U|
+00000050  00 02 52 00 02 4f 30 82  02 4b 30 82 01 b4 a0 03  |..R..O0..K0.....|
+00000060  02 01 02 02 09 00 e8 f0  9d 3f e2 5b ea a6 30 0d  |.........?.[..0.|
+00000070  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 30 1f 31  |..*.H........0.1|
+00000080  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 10 30 0e  |.0...U....Go1.0.|
+00000090  06 03 55 04 03 13 07 47  6f 20 52 6f 6f 74 30 1e  |..U....Go Root0.|
+000000a0  17 0d 31 36 30 31 30 31  30 30 30 30 30 30 5a 17  |..160101000000Z.|
+000000b0  0d 32 35 30 31 30 31 30  30 30 30 30 30 5a 30 1a  |.250101000000Z0.|
+000000c0  31 0b 30 09 06 03 55 04  0a 13 02 47 6f 31 0b 30  |1.0...U....Go1.0|
+000000d0  09 06 03 55 04 03 13 02  47 6f 30 81 9f 30 0d 06  |...U....Go0..0..|
+000000e0  09 2a 86 48 86 f7 0d 01  01 01 05 00 03 81 8d 00  |.*.H............|
+000000f0  30 81 89 02 81 81 00 db  46 7d 93 2e 12 27 06 48  |0.......F}...'.H|
+00000100  bc 06 28 21 ab 7e c4 b6  a2 5d fe 1e 52 45 88 7a  |..(!.~...]..RE.z|
+00000110  36 47 a5 08 0d 92 42 5b  c2 81 c0 be 97 79 98 40  |6G....B[.....y.@|
+00000120  fb 4f 6d 14 fd 2b 13 8b  c2 a5 2e 67 d8 d4 09 9e  |.Om..+.....g....|
+00000130  d6 22 38 b7 4a 0b 74 73  2b c2 34 f1 d1 93 e5 96  |."8.J.ts+.4.....|
+00000140  d9 74 7b f3 58 9f 6c 61  3c c0 b0 41 d4 d9 2b 2b  |.t{.X.la<..A..++|
+00000150  24 23 77 5b 1c 3b bd 75  5d ce 20 54 cf a1 63 87  |$#w[.;.u]. T..c.|
+00000160  1d 1e 24 c4 f3 1d 1a 50  8b aa b6 14 43 ed 97 a7  |..$....P....C...|
+00000170  75 62 f4 14 c8 52 d7 02  03 01 00 01 a3 81 93 30  |ub...R.........0|
+00000180  81 90 30 0e 06 03 55 1d  0f 01 01 ff 04 04 03 02  |..0...U.........|
+00000190  05 a0 30 1d 06 03 55 1d  25 04 16 30 14 06 08 2b  |..0...U.%..0...+|
+000001a0  06 01 05 05 07 03 01 06  08 2b 06 01 05 05 07 03  |.........+......|
+000001b0  02 30 0c 06 03 55 1d 13  01 01 ff 04 02 30 00 30  |.0...U.......0.0|
+000001c0  19 06 03 55 1d 0e 04 12  04 10 9f 91 16 1f 43 43  |...U..........CC|
+000001d0  3e 49 a6 de 6d b6 80 d7  9f 60 30 1b 06 03 55 1d  |>I..m....`0...U.|
+000001e0  23 04 14 30 12 80 10 48  13 49 4d 13 7e 16 31 bb  |#..0...H.IM.~.1.|
+000001f0  a3 01 d5 ac ab 6e 7b 30  19 06 03 55 1d 11 04 12  |.....n{0...U....|
+00000200  30 10 82 0e 65 78 61 6d  70 6c 65 2e 67 6f 6c 61  |0...example.gola|
+00000210  6e 67 30 0d 06 09 2a 86  48 86 f7 0d 01 01 0b 05  |ng0...*.H.......|
+00000220  00 03 81 81 00 9d 30 cc  40 2b 5b 50 a0 61 cb ba  |......0. at +[P.a..|
+00000230  e5 53 58 e1 ed 83 28 a9  58 1a a9 38 a4 95 a1 ac  |.SX...(.X..8....|
+00000240  31 5a 1a 84 66 3d 43 d3  2d d9 0b f2 97 df d3 20  |1Z..f=C.-...... |
+00000250  64 38 92 24 3a 00 bc cf  9c 7d b7 40 20 01 5f aa  |d8.$:....}.@ ._.|
+00000260  d3 16 61 09 a2 76 fd 13  c3 cc e1 0c 5c ee b1 87  |..a..v......\...|
+00000270  82 f1 6c 04 ed 73 bb b3  43 77 8d 0c 1c f1 0f a1  |..l..s..Cw......|
+00000280  d8 40 83 61 c9 4c 72 2b  9d ae db 46 06 06 4d f4  |. at .a.Lr+...F..M.|
+00000290  c1 b3 3e c0 d1 bd 42 d4  db fe 3d 13 60 84 5c 21  |..>...B...=.`.\!|
+000002a0  d3 3b e9 fa e7 16 03 03  00 ac 0c 00 00 a8 03 00  |.;..............|
+000002b0  1d 20 2f e5 7d a3 47 cd  62 43 15 28 da ac 5f bb  |. /.}.G.bC.(.._.|
+000002c0  29 07 30 ff f6 84 af c4  cf c2 ed 90 99 5f 58 cb  |).0.........._X.|
+000002d0  3b 74 05 01 00 80 2e c1  51 a1 e8 92 a6 bb ad 1e  |;t......Q.......|
+000002e0  4d f1 22 c5 e7 10 e6 31  1d 78 61 8a 22 a3 93 84  |M."....1.xa."...|
+000002f0  58 d6 5a c6 94 d0 da 6c  6a 35 d1 31 ea 1b 7e 55  |X.Z....lj5.1..~U|
+00000300  d6 35 a3 b7 42 e4 04 f8  31 15 15 88 5f 91 a8 7e  |.5..B...1..._..~|
+00000310  3e 73 52 8f 32 50 2e ad  95 44 83 b6 88 d6 18 99  |>sR.2P...D......|
+00000320  cf 86 57 97 c0 b2 a0 91  ee a7 ac f8 38 4b 1c 8e  |..W.........8K..|
+00000330  a4 58 59 4a f6 fc 88 a4  02 ed c8 04 1a 8b 7b 9e  |.XYJ..........{.|
+00000340  83 91 72 ca 1e 1c e0 76  58 73 89 3a 7d 12 c5 ef  |..r....vXs.:}...|
+00000350  f8 f7 45 dc ca c4 16 03  03 00 04 0e 00 00 00     |..E............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 35 ca 56 91 15  |....F...BA.5.V..|
-00000010  4f dd af 97 f2 2d fb df  54 2b 80 98 18 bb 33 54  |O....-..T+....3T|
-00000020  3f 7e 66 21 d3 81 38 f9  a4 b5 b9 a6 46 9a 52 8b  |?~f!..8.....F.R.|
-00000030  98 f7 81 1f 77 81 78 38  01 c5 3b fb 7a b7 53 e7  |....w.x8..;.z.S.|
-00000040  ae c3 4c 2e 73 f4 8e 3a  36 0d 43 14 03 03 00 01  |..L.s..:6.C.....|
-00000050  01 16 03 03 00 28 38 26  8e 03 ad 81 9b a0 41 d9  |.....(8&......A.|
-00000060  c0 11 3f 36 dc 6b ab 6c  29 dc df 02 a3 fe b0 0f  |..?6.k.l).......|
-00000070  2e b1 c6 44 39 42 d5 ef  29 30 d8 e0 f1 f9        |...D9B..)0....|
+00000000  16 03 03 00 25 10 00 00  21 20 be 4e 0d d5 31 aa  |....%...! .N..1.|
+00000010  27 13 df 73 d3 8d 17 8c  b3 5f 44 61 7b 01 b6 99  |'..s....._Da{...|
+00000020  7b ba b3 5d bf d4 be 3c  87 26 14 03 03 00 01 01  |{..]...<.&......|
+00000030  16 03 03 00 28 9c 86 e0  30 d4 a5 ec 0c 9e a6 08  |....(...0.......|
+00000040  ce 8a 7a ff ef be 52 0c  56 86 62 de 49 09 a1 18  |..z...R.V.b.I...|
+00000050  aa 62 e5 e3 d3 2e 4a 24  c9 ef 44 c9 67           |.b....J$..D.g|
 >>> Flow 4 (server to client)
 00000000  16 03 03 00 82 04 00 00  7e 00 00 00 00 00 78 50  |........~.....xP|
 00000010  46 ad c1 db a8 38 86 7b  2b bb fd d0 c3 42 3e 00  |F....8.{+....B>.|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 94  |................|
-00000030  6f ec 80 83 61 fb fb 41  b1 31 e9 71 75 43 c3 74  |o...a..A.1.quC.t|
-00000040  a1 a0 ac fb 97 b7 69 ee  a6 2f e3 a3 dd 9f de e4  |......i../......|
-00000050  80 9d d7 69 1a 2c 0b b4  02 bd ef e2 6a c1 ca 30  |...i.,......j..0|
-00000060  8b 9d 60 f9 fe 33 94 53  3a 14 a3 1a aa 5a ba ff  |..`..3.S:....Z..|
-00000070  1e 94 fd 4f e7 90 0b 09  ee 80 f3 d6 d5 c0 48 83  |...O..........H.|
-00000080  98 20 d7 a4 07 99 e0 14  03 03 00 01 01 16 03 03  |. ..............|
-00000090  00 28 00 00 00 00 00 00  00 00 0d 66 de 91 4a 97  |.(.........f..J.|
-000000a0  21 c6 d2 d7 df 68 9b 7e  f6 43 73 02 66 b3 5a d6  |!....h.~.Cs.f.Z.|
-000000b0  92 48 c2 c1 11 fc cd 1e  2e 4b 17 03 03 00 25 00  |.H.......K....%.|
-000000c0  00 00 00 00 00 00 01 72  0c 48 75 fa b2 8b 23 09  |.......r.Hu...#.|
-000000d0  be 76 36 a4 bc e0 62 ef  bd 79 8e de 6b 39 4b 55  |.v6...b..y..k9KU|
-000000e0  8d 3c ca 14 15 03 03 00  1a 00 00 00 00 00 00 00  |.<..............|
-000000f0  02 74 5f 79 31 41 4f f5  4d 02 96 bc c3 9a 85 92  |.t_y1AO.M.......|
-00000100  44 e1 76                                          |D.v|
+00000030  6f ec 80 83 61 da 3d a1  df 0d 11 25 4b 66 55 09  |o...a.=....%KfU.|
+00000040  af 7a c1 82 b9 ea 2f 9f  5c f4 0a 62 15 62 c2 32  |.z..../.\..b.b.2|
+00000050  c6 37 51 5b bb 19 14 f8  73 f8 fb 82 00 ef 19 21  |.7Q[....s......!|
+00000060  e2 52 7d ab 0a 33 94 df  78 54 ba 6c 5e 94 eb 16  |.R}..3..xT.l^...|
+00000070  ad 85 01 ca d5 98 8f 8e  b7 04 7e 9a 3c 35 c0 e5  |..........~.<5..|
+00000080  8f cf 27 6d b4 12 c2 14  03 03 00 01 01 16 03 03  |..'m............|
+00000090  00 28 00 00 00 00 00 00  00 00 75 da b5 10 2e 7c  |.(........u....||
+000000a0  39 ec 3d 98 12 fb 5d 15  81 79 f3 c7 b1 e4 e0 54  |9.=...]..y.....T|
+000000b0  ed 27 6e bc c3 81 a0 74  7e 38 17 03 03 00 25 00  |.'n....t~8....%.|
+000000c0  00 00 00 00 00 00 01 bf  81 cc 93 49 4f b2 59 8b  |...........IO.Y.|
+000000d0  53 4a 61 96 04 00 4b ac  34 d5 bd 5a 94 44 18 5b  |SJa...K.4..Z.D.[|
+000000e0  7d 81 dc 05 15 03 03 00  1a 00 00 00 00 00 00 00  |}...............|
+000000f0  02 bd 32 d5 cf 4d 13 61  6a 77 8b 3e 51 b3 13 84  |..2..M.ajw.>Q...|
+00000100  e6 1a 23                                          |..#|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ALPN-NoMatch b/src/crypto/tls/testdata/Server-TLSv12-ALPN-NoMatch
index 35bfdc1..d40300e 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ALPN-NoMatch
+++ b/src/crypto/tls/testdata/Server-TLSv12-ALPN-NoMatch
@@ -1,108 +1,94 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 01 4d 01 00 01  49 03 03 73 f2 f2 44 4f  |....M...I..s..DO|
-00000010  87 05 77 e2 e7 07 ca c7  d4 36 37 4e d9 17 ba ff  |..w......67N....|
-00000020  b0 e1 47 65 f8 7f fd 7a  b4 85 39 00 00 b6 c0 30  |..Ge...z..9....0|
-00000030  c0 2c c0 28 c0 24 c0 14  c0 0a 00 a5 00 a3 00 a1  |.,.(.$..........|
-00000040  00 9f 00 6b 00 6a 00 69  00 68 00 39 00 38 00 37  |...k.j.i.h.9.8.7|
-00000050  00 36 00 88 00 87 00 86  00 85 c0 32 c0 2e c0 2a  |.6.........2...*|
-00000060  c0 26 c0 0f c0 05 00 9d  00 3d 00 35 00 84 c0 2f  |.&.......=.5.../|
-00000070  c0 2b c0 27 c0 23 c0 13  c0 09 00 a4 00 a2 00 a0  |.+.'.#..........|
-00000080  00 9e 00 67 00 40 00 3f  00 3e 00 33 00 32 00 31  |...g. at .?.>.3.2.1|
-00000090  00 30 00 9a 00 99 00 98  00 97 00 45 00 44 00 43  |.0.........E.D.C|
-000000a0  00 42 c0 31 c0 2d c0 29  c0 25 c0 0e c0 04 00 9c  |.B.1.-.).%......|
-000000b0  00 3c 00 2f 00 96 00 41  00 07 c0 11 c0 07 c0 0c  |.<./...A........|
-000000c0  c0 02 00 05 00 04 c0 12  c0 08 00 16 00 13 00 10  |................|
-000000d0  00 0d c0 0d c0 03 00 0a  00 15 00 12 00 0f 00 0c  |................|
-000000e0  00 09 00 ff 02 01 00 00  69 00 0b 00 04 03 00 01  |........i.......|
-000000f0  02 00 0a 00 1c 00 1a 00  17 00 19 00 1c 00 1b 00  |................|
-00000100  18 00 1a 00 16 00 0e 00  0d 00 0b 00 0c 00 09 00  |................|
-00000110  0a 00 23 00 00 00 0d 00  20 00 1e 06 01 06 02 06  |..#..... .......|
-00000120  03 05 01 05 02 05 03 04  01 04 02 04 03 03 01 03  |................|
-00000130  02 03 03 02 01 02 02 02  03 00 0f 00 01 01 00 10  |................|
-00000140  00 10 00 0e 06 70 72 6f  74 6f 32 06 70 72 6f 74  |.....proto2.prot|
-00000150  6f 31                                             |o1|
+00000000  16 03 01 00 bf 01 00 00  bb 03 03 82 57 14 fc ab  |............W...|
+00000010  56 21 ff 72 39 72 3f fe  f1 9b 0f 22 00 ff ef 44  |V!.r9r?...."...D|
+00000020  da db e0 83 d2 c0 a7 1c  fb f0 6c 00 00 38 c0 2c  |..........l..8.,|
+00000030  c0 30 00 9f cc a9 cc a8  cc aa c0 2b c0 2f 00 9e  |.0.........+./..|
+00000040  c0 24 c0 28 00 6b c0 23  c0 27 00 67 c0 0a c0 14  |.$.(.k.#.'.g....|
+00000050  00 39 c0 09 c0 13 00 33  00 9d 00 9c 00 3d 00 3c  |.9.....3.....=.<|
+00000060  00 35 00 2f 00 ff 01 00  00 5a 00 0b 00 04 03 00  |.5./.....Z......|
+00000070  01 02 00 0a 00 0a 00 08  00 1d 00 17 00 19 00 18  |................|
+00000080  00 23 00 00 00 0d 00 20  00 1e 06 01 06 02 06 03  |.#..... ........|
+00000090  05 01 05 02 05 03 04 01  04 02 04 03 03 01 03 02  |................|
+000000a0  03 03 02 01 02 02 02 03  00 10 00 10 00 0e 06 70  |...............p|
+000000b0  72 6f 74 6f 32 06 70 72  6f 74 6f 31 00 16 00 00  |roto2.proto1....|
+000000c0  00 17 00 00                                       |....|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 35 02 00 00  31 03 03 00 00 00 00 00  |....5...1.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 c0 30 00 00  |.............0..|
-00000030  09 00 23 00 00 ff 01 00  01 00 16 03 03 02 71 0b  |..#...........q.|
-00000040  00 02 6d 00 02 6a 00 02  67 30 82 02 63 30 82 01  |..m..j..g0..c0..|
-00000050  cc a0 03 02 01 02 02 09  00 a2 73 00 0c 81 00 cb  |..........s.....|
-00000060  f3 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |.0...*.H........|
-00000070  30 2b 31 17 30 15 06 03  55 04 0a 13 0e 47 6f 6f  |0+1.0...U....Goo|
-00000080  67 6c 65 20 54 45 53 54  49 4e 47 31 10 30 0e 06  |gle TESTING1.0..|
-00000090  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
-000000a0  0d 31 35 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.150101000000Z..|
-000000b0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 26 31  |250101000000Z0&1|
-000000c0  17 30 15 06 03 55 04 0a  13 0e 47 6f 6f 67 6c 65  |.0...U....Google|
-000000d0  20 54 45 53 54 49 4e 47  31 0b 30 09 06 03 55 04  | TESTING1.0...U.|
-000000e0  03 13 02 47 6f 30 81 9f  30 0d 06 09 2a 86 48 86  |...Go0..0...*.H.|
-000000f0  f7 0d 01 01 01 05 00 03  81 8d 00 30 81 89 02 81  |...........0....|
-00000100  81 00 af 87 88 f6 20 1b  95 65 6c 14 ab 44 05 af  |...... ..el..D..|
-00000110  3b 45 14 e3 b7 6d fd 00  63 4d 95 7f fe 6a 62 35  |;E...m..cM...jb5|
-00000120  86 c0 4a f9 18 7c f6 aa  25 5e 7a 64 31 66 00 ba  |..J..|..%^zd1f..|
-00000130  f4 8e 92 af c7 6b d8 76  d4 f3 5f 41 cb 6e 56 15  |.....k.v.._A.nV.|
-00000140  97 1b 97 c1 3c 12 39 21  66 3d 2b 16 d1 bc db 1c  |....<.9!f=+.....|
-00000150  c0 a7 da b7 ca ad ba da  cb d5 21 50 ec de 8d ab  |..........!P....|
-00000160  d1 6b 81 4b 89 02 f3 c4  be c1 6c 89 b1 44 84 bd  |.k.K......l..D..|
-00000170  21 d1 04 7d 9d 16 4d f9  82 15 f6 ef fa d6 09 47  |!..}..M........G|
-00000180  f2 fb 02 03 01 00 01 a3  81 93 30 81 90 30 0e 06  |..........0..0..|
-00000190  03 55 1d 0f 01 01 ff 04  04 03 02 05 a0 30 1d 06  |.U...........0..|
-000001a0  03 55 1d 25 04 16 30 14  06 08 2b 06 01 05 05 07  |.U.%..0...+.....|
-000001b0  03 01 06 08 2b 06 01 05  05 07 03 02 30 0c 06 03  |....+.......0...|
-000001c0  55 1d 13 01 01 ff 04 02  30 00 30 19 06 03 55 1d  |U.......0.0...U.|
-000001d0  0e 04 12 04 10 12 50 8d  89 6f 1b d1 dc 54 4d 6e  |......P..o...TMn|
-000001e0  cb 69 5e 06 f4 30 1b 06  03 55 1d 23 04 14 30 12  |.i^..0...U.#..0.|
-000001f0  80 10 bf 3d b6 a9 66 f2  b8 40 cf ea b4 03 78 48  |...=..f.. at ....xH|
-00000200  1a 41 30 19 06 03 55 1d  11 04 12 30 10 82 0e 65  |.A0...U....0...e|
-00000210  78 61 6d 70 6c 65 2e 67  6f 6c 61 6e 67 30 0d 06  |xample.golang0..|
-00000220  09 2a 86 48 86 f7 0d 01  01 0b 05 00 03 81 81 00  |.*.H............|
-00000230  92 7c af 91 55 12 18 96  59 31 a6 48 40 d5 2d d5  |.|..U...Y1.H at .-.|
-00000240  ee bb 02 a0 f5 c2 1e 7c  9b b3 30 7d 3c dc 76 da  |.......|..0}<.v.|
-00000250  4f 3d c0 fa ae 2d 33 24  6b 03 7b 1b 67 59 11 21  |O=...-3$k.{.gY.!|
-00000260  b5 11 bc 77 b9 d9 e0 6e  a8 2d 2e 35 fa 64 5f 22  |...w...n.-.5.d_"|
-00000270  3e 63 10 6b be ff 14 86  6d 0d f0 15 31 a8 14 38  |>c.k....m...1..8|
-00000280  1e 3b 84 87 2c cb 98 ed  51 76 b9 b1 4f dd db 9b  |.;..,...Qv..O...|
-00000290  84 04 86 40 fa 51 dd ba  b4 8d eb e3 46 de 46 b9  |... at .Q......F.F.|
-000002a0  4f 86 c7 f9 a4 c2 41 34  ac cc f6 ea b0 ab 39 18  |O.....A4......9.|
-000002b0  16 03 03 00 cd 0c 00 00  c9 03 00 17 41 04 1e 18  |............A...|
-000002c0  37 ef 0d 19 51 88 35 75  71 b5 e5 54 5b 12 2e 8f  |7...Q.5uq..T[...|
-000002d0  09 67 fd a7 24 20 3e b2  56 1c ce 97 28 5e f8 2b  |.g..$ >.V...(^.+|
-000002e0  2d 4f 9e f1 07 9f 6c 4b  5b 83 56 e2 32 42 e9 58  |-O....lK[.V.2B.X|
-000002f0  b6 d7 49 a6 b5 68 1a 41  03 56 6b dc 5a 89 05 01  |..I..h.A.Vk.Z...|
-00000300  00 80 97 89 a3 7f 30 d1  7b 70 26 3d a4 d5 66 2e  |......0.{p&=..f.|
-00000310  cd fc 02 f5 37 a5 cd 09  69 7a c6 2f b2 62 e8 a6  |....7...iz./.b..|
-00000320  88 e2 3a c4 0a 8c 77 ad  d3 c9 29 49 84 81 9c cd  |..:...w...)I....|
-00000330  33 44 59 2d b5 2e e7 ce  12 c5 3b 46 13 6d 4a c8  |3DY-......;F.mJ.|
-00000340  6d f6 1f e7 f1 99 13 01  ca 43 79 fa b5 78 c7 1a  |m........Cy..x..|
-00000350  7d 8f 85 dd 3b ca 56 22  c3 d0 41 11 1b 13 8c 07  |}...;.V"..A.....|
-00000360  02 75 87 7a ea 68 43 30  0b 2a 38 52 b2 8f cc ea  |.u.z.hC0.*8R....|
-00000370  a3 a3 cb 71 fb 97 cd 3e  74 d0 5b 9b bd 17 13 f0  |...q...>t.[.....|
-00000380  d9 fe 16 03 03 00 04 0e  00 00 00                 |...........|
+00000030  09 00 23 00 00 ff 01 00  01 00 16 03 03 02 59 0b  |..#...........Y.|
+00000040  00 02 55 00 02 52 00 02  4f 30 82 02 4b 30 82 01  |..U..R..O0..K0..|
+00000050  b4 a0 03 02 01 02 02 09  00 e8 f0 9d 3f e2 5b ea  |............?.[.|
+00000060  a6 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |.0...*.H........|
+00000070  30 1f 31 0b 30 09 06 03  55 04 0a 13 02 47 6f 31  |0.1.0...U....Go1|
+00000080  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
+00000090  74 30 1e 17 0d 31 36 30  31 30 31 30 30 30 30 30  |t0...16010100000|
+000000a0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
+000000b0  5a 30 1a 31 0b 30 09 06  03 55 04 0a 13 02 47 6f  |Z0.1.0...U....Go|
+000000c0  31 0b 30 09 06 03 55 04  03 13 02 47 6f 30 81 9f  |1.0...U....Go0..|
+000000d0  30 0d 06 09 2a 86 48 86  f7 0d 01 01 01 05 00 03  |0...*.H.........|
+000000e0  81 8d 00 30 81 89 02 81  81 00 db 46 7d 93 2e 12  |...0.......F}...|
+000000f0  27 06 48 bc 06 28 21 ab  7e c4 b6 a2 5d fe 1e 52  |'.H..(!.~...]..R|
+00000100  45 88 7a 36 47 a5 08 0d  92 42 5b c2 81 c0 be 97  |E.z6G....B[.....|
+00000110  79 98 40 fb 4f 6d 14 fd  2b 13 8b c2 a5 2e 67 d8  |y. at .Om..+.....g.|
+00000120  d4 09 9e d6 22 38 b7 4a  0b 74 73 2b c2 34 f1 d1  |...."8.J.ts+.4..|
+00000130  93 e5 96 d9 74 7b f3 58  9f 6c 61 3c c0 b0 41 d4  |....t{.X.la<..A.|
+00000140  d9 2b 2b 24 23 77 5b 1c  3b bd 75 5d ce 20 54 cf  |.++$#w[.;.u]. T.|
+00000150  a1 63 87 1d 1e 24 c4 f3  1d 1a 50 8b aa b6 14 43  |.c...$....P....C|
+00000160  ed 97 a7 75 62 f4 14 c8  52 d7 02 03 01 00 01 a3  |...ub...R.......|
+00000170  81 93 30 81 90 30 0e 06  03 55 1d 0f 01 01 ff 04  |..0..0...U......|
+00000180  04 03 02 05 a0 30 1d 06  03 55 1d 25 04 16 30 14  |.....0...U.%..0.|
+00000190  06 08 2b 06 01 05 05 07  03 01 06 08 2b 06 01 05  |..+.........+...|
+000001a0  05 07 03 02 30 0c 06 03  55 1d 13 01 01 ff 04 02  |....0...U.......|
+000001b0  30 00 30 19 06 03 55 1d  0e 04 12 04 10 9f 91 16  |0.0...U.........|
+000001c0  1f 43 43 3e 49 a6 de 6d  b6 80 d7 9f 60 30 1b 06  |.CC>I..m....`0..|
+000001d0  03 55 1d 23 04 14 30 12  80 10 48 13 49 4d 13 7e  |.U.#..0...H.IM.~|
+000001e0  16 31 bb a3 01 d5 ac ab  6e 7b 30 19 06 03 55 1d  |.1......n{0...U.|
+000001f0  11 04 12 30 10 82 0e 65  78 61 6d 70 6c 65 2e 67  |...0...example.g|
+00000200  6f 6c 61 6e 67 30 0d 06  09 2a 86 48 86 f7 0d 01  |olang0...*.H....|
+00000210  01 0b 05 00 03 81 81 00  9d 30 cc 40 2b 5b 50 a0  |.........0. at +[P.|
+00000220  61 cb ba e5 53 58 e1 ed  83 28 a9 58 1a a9 38 a4  |a...SX...(.X..8.|
+00000230  95 a1 ac 31 5a 1a 84 66  3d 43 d3 2d d9 0b f2 97  |...1Z..f=C.-....|
+00000240  df d3 20 64 38 92 24 3a  00 bc cf 9c 7d b7 40 20  |.. d8.$:....}.@ |
+00000250  01 5f aa d3 16 61 09 a2  76 fd 13 c3 cc e1 0c 5c  |._...a..v......\|
+00000260  ee b1 87 82 f1 6c 04 ed  73 bb b3 43 77 8d 0c 1c  |.....l..s..Cw...|
+00000270  f1 0f a1 d8 40 83 61 c9  4c 72 2b 9d ae db 46 06  |.... at .a.Lr+...F.|
+00000280  06 4d f4 c1 b3 3e c0 d1  bd 42 d4 db fe 3d 13 60  |.M...>...B...=.`|
+00000290  84 5c 21 d3 3b e9 fa e7  16 03 03 00 ac 0c 00 00  |.\!.;...........|
+000002a0  a8 03 00 1d 20 2f e5 7d  a3 47 cd 62 43 15 28 da  |.... /.}.G.bC.(.|
+000002b0  ac 5f bb 29 07 30 ff f6  84 af c4 cf c2 ed 90 99  |._.).0..........|
+000002c0  5f 58 cb 3b 74 05 01 00  80 bf 6d 57 f5 0c 78 c4  |_X.;t.....mW..x.|
+000002d0  77 48 0e 60 67 7a 3a 1b  3e 9e d2 88 a4 89 07 ef  |wH.`gz:.>.......|
+000002e0  d1 45 1a 66 7e 8c ec cb  da 71 ea ec ba ed 81 9e  |.E.f~....q......|
+000002f0  21 4d 2e ba d4 8f c2 0a  67 ea 3a a1 d1 67 09 66  |!M......g.:..g.f|
+00000300  dc a8 ad 16 a2 23 2a db  4f 31 65 b1 54 13 73 d1  |.....#*.O1e.T.s.|
+00000310  f6 7b 75 d9 f1 07 19 b8  67 21 87 d2 3b cf a5 6c  |.{u.....g!..;..l|
+00000320  61 8e af ed 60 7f f2 56  9f 0d 0f 19 88 98 30 3a  |a...`..V......0:|
+00000330  61 8c 21 e7 8b 5d ab 6f  cf 93 73 33 63 cd 50 bb  |a.!..].o..s3c.P.|
+00000340  dd 0e ab 4f 6a fb a3 f9  68 16 03 03 00 04 0e 00  |...Oj...h.......|
+00000350  00 00                                             |..|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 ba 5b 0f e7 ec  |....F...BA..[...|
-00000010  8e c8 ad 51 8c c0 50 f1  8a 2a 68 32 74 d0 95 03  |...Q..P..*h2t...|
-00000020  0c 61 f1 1c 89 ed 95 5d  9a 4a 14 ee cc 14 9a 73  |.a.....].J.....s|
-00000030  f6 db 46 dd b7 47 8a 82  3d 7a b8 9f 45 d1 a2 3f  |..F..G..=z..E..?|
-00000040  f4 34 9b b6 6d 7d 41 87  c9 d5 cd 14 03 03 00 01  |.4..m}A.........|
-00000050  01 16 03 03 00 28 1e ae  f6 90 a9 91 eb 4b ca 23  |.....(.......K.#|
-00000060  6e bf 9e 67 5b 38 ab f6  d6 ee 12 aa b9 b6 d0 6e  |n..g[8.........n|
-00000070  a7 dd 45 91 34 45 78 a0  04 9e d8 85 48 48        |..E.4Ex.....HH|
+00000000  16 03 03 00 25 10 00 00  21 20 05 0c 3b 8b 22 36  |....%...! ..;."6|
+00000010  61 79 58 28 b0 82 65 44  39 67 93 c5 2c 3b d1 40  |ayX(..eD9g..,;.@|
+00000020  88 af 9f 38 c1 fa e0 81  a0 19 14 03 03 00 01 01  |...8............|
+00000030  16 03 03 00 28 87 2e d2  c2 ce 65 6d e8 d9 da a0  |....(.....em....|
+00000040  9d dc f5 51 b0 84 88 8d  c6 a3 0a 5d 08 10 ca c6  |...Q.......]....|
+00000050  e3 83 0c 0a cb 6d ec 09  b8 9f a5 45 99           |.....m.....E.|
 >>> Flow 4 (server to client)
 00000000  16 03 03 00 82 04 00 00  7e 00 00 00 00 00 78 50  |........~.....xP|
 00000010  46 ad c1 db a8 38 86 7b  2b bb fd d0 c3 42 3e 00  |F....8.{+....B>.|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 94  |................|
-00000030  6f ec 80 83 61 4f 7f 09  64 32 96 26 b5 71 46 6a  |o...aO..d2.&.qFj|
-00000040  29 7d fd 0b bb 49 13 0e  c8 c5 de 06 ed 47 e8 cb  |)}...I.......G..|
-00000050  d8 9f 18 82 69 af ab 24  d2 78 90 ba 9a c8 24 95  |....i..$.x....$.|
-00000060  46 53 19 2e e8 33 94 3c  22 73 26 d6 86 4e 01 a4  |FS...3.<"s&..N..|
-00000070  34 ea a8 bf f2 ca b5 0d  fc f6 08 b9 31 b3 42 e7  |4...........1.B.|
-00000080  c1 92 96 f9 bf 9a 00 14  03 03 00 01 01 16 03 03  |................|
-00000090  00 28 00 00 00 00 00 00  00 00 bd 51 1d 0e bd 51  |.(.........Q...Q|
-000000a0  a3 b1 03 f2 df f4 ba 9b  1e a5 a8 22 e7 ce 7c 19  |..........."..|.|
-000000b0  1a bf 37 3d 42 f4 4d 6f  63 75 17 03 03 00 25 00  |..7=B.Mocu....%.|
-000000c0  00 00 00 00 00 00 01 52  8a d2 34 52 70 f1 cf 87  |.......R..4Rp...|
-000000d0  54 4e fd e6 11 a7 76 1a  f4 7b 70 e8 34 ef 01 c8  |TN....v..{p.4...|
-000000e0  6c 4a f8 6d 15 03 03 00  1a 00 00 00 00 00 00 00  |lJ.m............|
-000000f0  02 8a 4c f9 7c d1 61 a6  cd 2a e6 3a 5b b0 cb aa  |..L.|.a..*.:[...|
-00000100  91 2e 8b                                          |...|
+00000030  6f ec 80 83 61 34 53 e2  a4 e2 ff 73 4f 1b 15 8f  |o...a4S....sO...|
+00000040  3b 43 47 ac 20 c6 2d 5e  52 7a 61 6f af 40 c3 5a  |;CG. .-^Rzao. at .Z|
+00000050  cb 3f 7d 10 a9 90 ca cf  8d c4 c4 d4 a3 b8 1d 62  |.?}............b|
+00000060  7d a9 68 32 01 33 94 65  8b 67 73 aa 51 d4 08 1d  |}.h2.3.e.gs.Q...|
+00000070  ce 76 6b ef 3d e6 ce d3  42 fe 24 cf f3 82 5b 17  |.vk.=...B.$...[.|
+00000080  5c 03 e9 50 94 8e 8b 14  03 03 00 01 01 16 03 03  |\..P............|
+00000090  00 28 00 00 00 00 00 00  00 00 c2 7c e6 69 c9 ec  |.(.........|.i..|
+000000a0  b5 55 57 34 8e 86 38 e6  28 85 b0 c8 2e c8 0f a6  |.UW4..8.(.......|
+000000b0  a9 07 f4 91 47 46 dd fe  c8 57 17 03 03 00 25 00  |....GF...W....%.|
+000000c0  00 00 00 00 00 00 01 39  9a a2 da d8 3d 7f 25 0e  |.......9....=.%.|
+000000d0  83 a8 cd 57 d8 a4 7e 9f  e1 e2 fe 3f 5a ed b9 99  |...W..~....?Z...|
+000000e0  b6 4d 97 3a 15 03 03 00  1a 00 00 00 00 00 00 00  |.M.:............|
+000000f0  02 d5 2a aa 1e 7a 60 b8  79 56 c6 56 75 11 b7 4c  |..*..z`.yV.Vu..L|
+00000100  83 19 9c                                          |...|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceECDSA b/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceECDSA
index ae3748f..e286407 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceECDSA
+++ b/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceECDSA
@@ -1,24 +1,15 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 01 35 01 00 01  31 03 03 00 02 67 8e 1d  |....5...1....g..|
-00000010  3b d2 26 20 63 c5 6a b6  68 25 02 72 ce 86 6b c7  |;.& c.j.h%.r..k.|
-00000020  97 1a 9f 4d be 02 98 ac  24 5e 82 00 00 b6 c0 30  |...M....$^.....0|
-00000030  c0 2c c0 28 c0 24 c0 14  c0 0a 00 a5 00 a3 00 a1  |.,.(.$..........|
-00000040  00 9f 00 6b 00 6a 00 69  00 68 00 39 00 38 00 37  |...k.j.i.h.9.8.7|
-00000050  00 36 00 88 00 87 00 86  00 85 c0 32 c0 2e c0 2a  |.6.........2...*|
-00000060  c0 26 c0 0f c0 05 00 9d  00 3d 00 35 00 84 c0 2f  |.&.......=.5.../|
-00000070  c0 2b c0 27 c0 23 c0 13  c0 09 00 a4 00 a2 00 a0  |.+.'.#..........|
-00000080  00 9e 00 67 00 40 00 3f  00 3e 00 33 00 32 00 31  |...g. at .?.>.3.2.1|
-00000090  00 30 00 9a 00 99 00 98  00 97 00 45 00 44 00 43  |.0.........E.D.C|
-000000a0  00 42 c0 31 c0 2d c0 29  c0 25 c0 0e c0 04 00 9c  |.B.1.-.).%......|
-000000b0  00 3c 00 2f 00 96 00 41  00 07 c0 11 c0 07 c0 0c  |.<./...A........|
-000000c0  c0 02 00 05 00 04 c0 12  c0 08 00 16 00 13 00 10  |................|
-000000d0  00 0d c0 0d c0 03 00 0a  00 15 00 12 00 0f 00 0c  |................|
-000000e0  00 09 00 ff 02 01 00 00  51 00 0b 00 04 03 00 01  |........Q.......|
-000000f0  02 00 0a 00 1c 00 1a 00  17 00 19 00 1c 00 1b 00  |................|
-00000100  18 00 1a 00 16 00 0e 00  0d 00 0b 00 0c 00 09 00  |................|
-00000110  0a 00 0d 00 20 00 1e 06  01 06 02 06 03 05 01 05  |.... ...........|
-00000120  02 05 03 04 01 04 02 04  03 03 01 03 02 03 03 02  |................|
-00000130  01 02 02 02 03 00 0f 00  01 01                    |..........|
+00000000  16 03 01 00 a7 01 00 00  a3 03 03 27 01 f3 21 98  |...........'..!.|
+00000010  ff 55 7f 78 32 44 b7 9d  88 6b 82 43 26 52 00 74  |.U.x2D...k.C&R.t|
+00000020  fb 05 ca be 23 1f d0 18  1f 74 c2 00 00 38 c0 2c  |....#....t...8.,|
+00000030  c0 30 00 9f cc a9 cc a8  cc aa c0 2b c0 2f 00 9e  |.0.........+./..|
+00000040  c0 24 c0 28 00 6b c0 23  c0 27 00 67 c0 0a c0 14  |.$.(.k.#.'.g....|
+00000050  00 39 c0 09 c0 13 00 33  00 9d 00 9c 00 3d 00 3c  |.9.....3.....=.<|
+00000060  00 35 00 2f 00 ff 01 00  00 42 00 0b 00 04 03 00  |.5./.....B......|
+00000070  01 02 00 0a 00 0a 00 08  00 1d 00 17 00 19 00 18  |................|
+00000080  00 0d 00 20 00 1e 06 01  06 02 06 03 05 01 05 02  |... ............|
+00000090  05 03 04 01 04 02 04 03  03 01 03 02 03 03 02 01  |................|
+000000a0  02 02 02 03 00 16 00 00  00 17 00 00              |............|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
@@ -56,43 +47,39 @@
 00000210  0e bd 3f a3 8c 25 c1 33  13 83 0d 94 06 bb d4 37  |..?..%.3.......7|
 00000220  7a f6 ec 7a c9 86 2e dd  d7 11 69 7f 85 7c 56 de  |z..z......i..|V.|
 00000230  fb 31 78 2b e4 c7 78 0d  ae cb be 9e 4e 36 24 31  |.1x+..x.....N6$1|
-00000240  7b 6a 0f 39 95 12 07 8f  2a 16 03 03 00 d8 0c 00  |{j.9....*.......|
-00000250  00 d4 03 00 17 41 04 1e  18 37 ef 0d 19 51 88 35  |.....A...7...Q.5|
-00000260  75 71 b5 e5 54 5b 12 2e  8f 09 67 fd a7 24 20 3e  |uq..T[....g..$ >|
-00000270  b2 56 1c ce 97 28 5e f8  2b 2d 4f 9e f1 07 9f 6c  |.V...(^.+-O....l|
-00000280  4b 5b 83 56 e2 32 42 e9  58 b6 d7 49 a6 b5 68 1a  |K[.V.2B.X..I..h.|
-00000290  41 03 56 6b dc 5a 89 05  03 00 8b 30 81 88 02 42  |A.Vk.Z.....0...B|
-000002a0  01 bc 56 16 22 ad fd e7  ac ba c8 f5 3f c0 d7 f8  |..V.".......?...|
-000002b0  8c 64 e0 ba 09 30 c3 66  49 90 7e d2 68 86 07 72  |.d...0.fI.~.h..r|
-000002c0  20 87 a1 e1 36 92 a7 68  e2 c3 6e 34 93 a9 ca e8  | ...6..h..n4....|
-000002d0  68 3d 9e 42 c4 1e 8e 2d  95 05 ee a6 a4 2c 8d be  |h=.B...-.....,..|
-000002e0  e3 88 02 42 01 16 18 77  b9 99 0e f8 46 90 46 07  |...B...w....F.F.|
-000002f0  f9 67 a9 26 68 d7 da c8  a1 d9 67 55 ec 37 11 2d  |.g.&h.....gU.7.-|
-00000300  4b f3 52 f4 96 6a 0e 8a  6a 14 21 94 63 ea f9 70  |K.R..j..j.!.c..p|
-00000310  2d 57 05 8a 72 29 6e d2  60 a1 97 af 08 5b c3 cf  |-W..r)n.`....[..|
-00000320  3a 82 a3 81 11 cf 16 03  03 00 04 0e 00 00 00     |:..............|
+00000240  7b 6a 0f 39 95 12 07 8f  2a 16 03 03 00 b7 0c 00  |{j.9....*.......|
+00000250  00 b3 03 00 1d 20 2f e5  7d a3 47 cd 62 43 15 28  |..... /.}.G.bC.(|
+00000260  da ac 5f bb 29 07 30 ff  f6 84 af c4 cf c2 ed 90  |.._.).0.........|
+00000270  99 5f 58 cb 3b 74 05 03  00 8b 30 81 88 02 42 01  |._X.;t....0...B.|
+00000280  4f 30 aa d0 4d e5 61 db  ba fc 95 15 52 ef 2a 41  |O0..M.a.....R.*A|
+00000290  b4 d6 59 ac 39 61 b6 38  08 1e 87 b3 ca 9b 49 d3  |..Y.9a.8......I.|
+000002a0  95 5a c5 29 84 cd 10 73  4a cc 09 df 1a b0 54 6d  |.Z.)...sJ.....Tm|
+000002b0  b8 61 28 80 2e ec cf 95  9d 6f c3 d9 ed 80 53 63  |.a(......o....Sc|
+000002c0  d9 02 42 00 af 71 2f 91  80 ff a1 79 82 c7 d9 79  |..B..q/....y...y|
+000002d0  fa 12 a9 88 7b 93 47 be  6a dc 80 42 17 9d 85 7a  |....{.G.j..B...z|
+000002e0  b8 1b fe 85 7f 5c 10 9c  9e 0e e1 71 a7 b0 12 02  |.....\.....q....|
+000002f0  e2 a4 79 c4 8d d8 02 09  01 9c 6f 7a 27 7c 1f f4  |..y.......oz'|..|
+00000300  38 46 59 46 94 16 03 03  00 04 0e 00 00 00        |8FYF..........|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 8c 80 0c da 24  |....F...BA.....$|
-00000010  d6 66 ff cc 1b 26 d5 3f  37 37 16 8f 16 ee 0d 5f  |.f...&.?77....._|
-00000020  c3 0e 62 7c e4 52 2d 43  29 e9 6b da 49 bc 99 16  |..b|.R-C).k.I...|
-00000030  28 46 8e 43 20 7f 12 66  1c 94 1c 03 55 6f 05 53  |(F.C ..f....Uo.S|
-00000040  6f b7 dc 8b 70 9d 9d c5  1f da 5b 14 03 03 00 01  |o...p.....[.....|
-00000050  01 16 03 03 00 40 17 60  dd e5 b2 58 fd 74 10 38  |..... at .`...X.t.8|
-00000060  95 b1 73 7e 8f 7a 2b d0  f5 65 80 0c dc b1 ca 29  |..s~.z+..e.....)|
-00000070  06 25 e1 f9 c3 c0 7c 88  e4 ad d3 16 0a 8a dd 1f  |.%....|.........|
-00000080  a7 86 86 0f ac c7 ea f5  0f 1f 2b 97 85 b3 81 f7  |..........+.....|
-00000090  5d 42 2f 3b 72 80                                 |]B/;r.|
+00000000  16 03 03 00 25 10 00 00  21 20 8c 80 e4 c7 bd d7  |....%...! ......|
+00000010  ea ea 42 f7 53 24 50 28  6a e9 f3 ff 4f 4a 28 22  |..B.S$P(j...OJ("|
+00000020  a2 95 09 fc f0 d9 3e fc  cc 6e 14 03 03 00 01 01  |......>..n......|
+00000030  16 03 03 00 40 79 56 60  f5 45 e7 48 9e 97 1d 49  |.... at yV`.E.H...I|
+00000040  de 59 dd b0 f0 0a d2 cc  10 f0 98 3c c2 d5 67 d6  |.Y.........<..g.|
+00000050  2c 18 2b 21 ae a3 2f ea  2d 0b ff fd e6 c2 73 25  |,.+!../.-.....s%|
+00000060  1c 01 3e 94 3a cc 1d 58  6b fb 7f 85 e4 50 ec 10  |..>.:..Xk....P..|
+00000070  b9 d7 71 cb be                                    |..q..|
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
-00000010  00 00 00 00 00 00 00 00  00 00 00 82 01 fd 38 ae  |..............8.|
-00000020  a4 07 8f bd 72 0a a2 b5  c5 78 09 89 65 1b 6d 1e  |....r....x..e.m.|
-00000030  56 52 9d 4f de 02 15 2d  93 d8 8f d7 1f bb 07 3b  |VR.O...-.......;|
-00000040  e9 62 3c 19 3e 19 65 ac  10 aa e5 17 03 03 00 40  |.b<.>.e........@|
+00000010  00 00 00 00 00 00 00 00  00 00 00 83 5c 5c e3 c0  |............\\..|
+00000020  20 56 8c 92 4b 75 f0 30  bd 67 74 52 f1 af 9c 14  | V..Ku.0.gtR....|
+00000030  29 1e e4 b2 5b c0 2c e6  48 6f 94 42 7b 21 92 96  |)...[.,.Ho.B{!..|
+00000040  0a 83 ce 1c 91 36 95 8c  14 38 57 17 03 03 00 40  |.....6...8W....@|
 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000060  18 61 1d 26 f3 b9 34 20  00 6c 27 75 fc 35 f5 c2  |.a.&..4 .l'u.5..|
-00000070  6f 71 ca 9b 0d 70 30 46  57 7c 07 86 7d 52 a9 d6  |oq...p0FW|..}R..|
-00000080  ab fc 89 a5 48 79 ae 60  03 05 4b 17 b2 d9 6b 39  |....Hy.`..K...k9|
+00000060  73 a4 40 cf ad 86 cc 05  9e 47 5f 83 50 ae 68 d5  |s. at ......G_.P.h.|
+00000070  d1 6a a9 8c ba 74 fe c0  cc 4a 1a e3 b0 14 0d 31  |.j...t...J.....1|
+00000080  9f 06 54 e3 95 3a 89 6d  34 54 0c e4 b4 34 38 21  |..T..:.m4T...48!|
 00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-000000a0  00 00 00 00 00 8f 8d 88  88 c5 1e f5 bf 06 f2 45  |...............E|
-000000b0  e7 fe f0 24 c7 4c 92 5a  80 a7 89 c8 2b ac 49 d9  |...$.L.Z....+.I.|
-000000c0  39 00 ca 57 ec                                    |9..W.|
+000000a0  00 00 00 00 00 e6 dd b2  11 ab a7 34 61 00 d4 09  |...........4a...|
+000000b0  bc ea c1 5f c4 e2 52 60  63 96 f0 fd 44 4e f9 0e  |..._..R`c...DN..|
+000000c0  af 32 99 e4 12                                    |.2...|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceRSA b/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceRSA
index 144ef42..1f9fbc1 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceRSA
+++ b/src/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceRSA
@@ -1,104 +1,89 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 01 35 01 00 01  31 03 03 ed 84 e2 1a c1  |....5...1.......|
-00000010  d9 3f a5 ba 70 0b 5f 3f  3b 87 79 18 27 03 92 ee  |.?..p._?;.y.'...|
-00000020  b1 9f c7 36 26 e3 0b 6d  fc d5 ed 00 00 b6 c0 30  |...6&..m.......0|
-00000030  c0 2c c0 28 c0 24 c0 14  c0 0a 00 a5 00 a3 00 a1  |.,.(.$..........|
-00000040  00 9f 00 6b 00 6a 00 69  00 68 00 39 00 38 00 37  |...k.j.i.h.9.8.7|
-00000050  00 36 00 88 00 87 00 86  00 85 c0 32 c0 2e c0 2a  |.6.........2...*|
-00000060  c0 26 c0 0f c0 05 00 9d  00 3d 00 35 00 84 c0 2f  |.&.......=.5.../|
-00000070  c0 2b c0 27 c0 23 c0 13  c0 09 00 a4 00 a2 00 a0  |.+.'.#..........|
-00000080  00 9e 00 67 00 40 00 3f  00 3e 00 33 00 32 00 31  |...g. at .?.>.3.2.1|
-00000090  00 30 00 9a 00 99 00 98  00 97 00 45 00 44 00 43  |.0.........E.D.C|
-000000a0  00 42 c0 31 c0 2d c0 29  c0 25 c0 0e c0 04 00 9c  |.B.1.-.).%......|
-000000b0  00 3c 00 2f 00 96 00 41  00 07 c0 11 c0 07 c0 0c  |.<./...A........|
-000000c0  c0 02 00 05 00 04 c0 12  c0 08 00 16 00 13 00 10  |................|
-000000d0  00 0d c0 0d c0 03 00 0a  00 15 00 12 00 0f 00 0c  |................|
-000000e0  00 09 00 ff 02 01 00 00  51 00 0b 00 04 03 00 01  |........Q.......|
-000000f0  02 00 0a 00 1c 00 1a 00  17 00 19 00 1c 00 1b 00  |................|
-00000100  18 00 1a 00 16 00 0e 00  0d 00 0b 00 0c 00 09 00  |................|
-00000110  0a 00 0d 00 20 00 1e 06  01 06 02 06 03 05 01 05  |.... ...........|
-00000120  02 05 03 04 01 04 02 04  03 03 01 03 02 03 03 02  |................|
-00000130  01 02 02 02 03 00 0f 00  01 01                    |..........|
+00000000  16 03 01 00 a7 01 00 00  a3 03 03 1d 39 c9 33 73  |............9.3s|
+00000010  c2 b9 71 d8 66 23 63 a7  5c 9e 50 b6 3e a5 f9 bb  |..q.f#c.\.P.>...|
+00000020  34 1b 71 e1 09 4f ae d5  53 8a e8 00 00 38 c0 2c  |4.q..O..S....8.,|
+00000030  c0 30 00 9f cc a9 cc a8  cc aa c0 2b c0 2f 00 9e  |.0.........+./..|
+00000040  c0 24 c0 28 00 6b c0 23  c0 27 00 67 c0 0a c0 14  |.$.(.k.#.'.g....|
+00000050  00 39 c0 09 c0 13 00 33  00 9d 00 9c 00 3d 00 3c  |.9.....3.....=.<|
+00000060  00 35 00 2f 00 ff 01 00  00 42 00 0b 00 04 03 00  |.5./.....B......|
+00000070  01 02 00 0a 00 0a 00 08  00 1d 00 17 00 19 00 18  |................|
+00000080  00 0d 00 20 00 1e 06 01  06 02 06 03 05 01 05 02  |... ............|
+00000090  05 03 04 01 04 02 04 03  03 01 03 02 03 03 02 01  |................|
+000000a0  02 02 02 03 00 16 00 00  00 17 00 00              |............|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 c0 14 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002b0  cd 0c 00 00 c9 03 00 17  41 04 1e 18 37 ef 0d 19  |........A...7...|
-000002c0  51 88 35 75 71 b5 e5 54  5b 12 2e 8f 09 67 fd a7  |Q.5uq..T[....g..|
-000002d0  24 20 3e b2 56 1c ce 97  28 5e f8 2b 2d 4f 9e f1  |$ >.V...(^.+-O..|
-000002e0  07 9f 6c 4b 5b 83 56 e2  32 42 e9 58 b6 d7 49 a6  |..lK[.V.2B.X..I.|
-000002f0  b5 68 1a 41 03 56 6b dc  5a 89 05 01 00 80 7c 5c  |.h.A.Vk.Z.....|\|
-00000300  f6 68 cc 07 f0 bd ec 30  07 d0 70 1b c6 95 a4 14  |.h.....0..p.....|
-00000310  67 3a 83 a1 43 ff 0a c3  f0 b7 ee 59 f8 c7 09 65  |g:..C......Y...e|
-00000320  08 ac 18 34 d4 8f 46 c4  2c 91 7b 57 95 e0 54 03  |...4..F.,.{W..T.|
-00000330  d8 8e b6 53 61 74 77 8b  a3 5f 23 f0 06 dc 3a 56  |...Satw.._#...:V|
-00000340  61 80 5e 31 d5 75 c3 05  9f d0 06 1f c5 32 ba 79  |a.^1.u.......2.y|
-00000350  fd 14 a9 54 5a 18 b4 2b  09 0e 19 ab 76 0b 12 5d  |...TZ..+....v..]|
-00000360  52 27 ce b8 dd 4c f8 f2  d2 70 56 43 19 53 b3 13  |R'...L...pVC.S..|
-00000370  b9 b7 65 ce cd 50 ed 4a  9f 42 96 c7 3c b9 16 03  |..e..P.J.B..<...|
-00000380  03 00 04 0e 00 00 00                              |.......|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  ac 0c 00 00 a8 03 00 1d  |;...............|
+000002a0  20 2f e5 7d a3 47 cd 62  43 15 28 da ac 5f bb 29  | /.}.G.bC.(.._.)|
+000002b0  07 30 ff f6 84 af c4 cf  c2 ed 90 99 5f 58 cb 3b  |.0.........._X.;|
+000002c0  74 05 01 00 80 06 d4 bd  e4 7b 10 77 89 d7 d4 d6  |t........{.w....|
+000002d0  4e f6 3e 46 49 db ee 5c  4e bc ee fe cb 8b a6 9b  |N.>FI..\N.......|
+000002e0  5c f6 99 fb 31 96 60 a8  23 09 f6 31 65 53 f0 6e  |\...1.`.#..1eS.n|
+000002f0  07 5c 32 f9 59 5d 8b c0  b4 74 c8 01 85 8a b7 19  |.\2.Y]...t......|
+00000300  ab 19 08 68 6a e8 2f 81  bd 04 9b 38 ab d9 27 66  |...hj./....8..'f|
+00000310  d7 a5 3f 75 9c 4f 81 5b  9e 69 10 20 2b f2 1d a2  |..?u.O.[.i. +...|
+00000320  8f fc 7f ba ee 5b 76 8b  19 3f 46 60 01 25 99 72  |.....[v..?F`.%.r|
+00000330  78 24 02 8e 28 d5 24 f1  2e 6b 70 53 75 ec e2 8d  |x$..(.$..kpSu...|
+00000340  76 ab e0 8e e8 16 03 03  00 04 0e 00 00 00        |v.............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 36 1c 6c f5 0a  |....F...BA.6.l..|
-00000010  7f 52 84 ac 5a 27 45 76  79 a6 89 f1 1d d9 30 30  |.R..Z'Evy.....00|
-00000020  b6 64 af c7 34 11 12 b3  b9 72 83 e6 78 bc 06 74  |.d..4....r..x..t|
-00000030  a7 a4 10 01 34 77 5c 05  88 82 0f a9 cf 8d e8 68  |....4w\........h|
-00000040  09 80 c7 79 b6 e9 5a 2a  5f 80 5e 14 03 03 00 01  |...y..Z*_.^.....|
-00000050  01 16 03 03 00 40 ef f9  3c 34 cd 26 70 c9 7b 60  |..... at ..<4.&p.{`|
-00000060  a7 27 0a 2b 86 18 2f 10  ad 48 3f 2e 9e 88 13 d6  |.'.+../..H?.....|
-00000070  d8 c6 fd 35 99 be 09 e6  dd ae 02 06 ea df 60 62  |...5..........`b|
-00000080  e0 f8 67 ea 9d c8 8c 11  d8 5a e7 6a a6 b2 eb 62  |..g......Z.j...b|
-00000090  23 b2 d2 be 75 58                                 |#...uX|
+00000000  16 03 03 00 25 10 00 00  21 20 21 75 22 84 bc b7  |....%...! !u"...|
+00000010  82 b3 03 d2 42 ff b6 ce  76 26 88 bf 8f 72 fc dd  |....B...v&...r..|
+00000020  63 9b f1 4c 22 6d 12 cc  d3 57 14 03 03 00 01 01  |c..L"m...W......|
+00000030  16 03 03 00 40 20 2b 26  bd 60 1b 27 a1 32 cb ab  |....@ +&.`.'.2..|
+00000040  30 83 9c 47 59 7d f5 bb  d9 45 8a d9 3e 29 86 4d  |0..GY}...E..>).M|
+00000050  54 86 48 38 25 d9 b9 af  36 7c 7a f0 ae f6 b6 4e  |T.H8%...6|z....N|
+00000060  a1 76 93 91 26 f3 c9 49  b5 6d 49 cf 22 97 bf c7  |.v..&..I.mI."...|
+00000070  db 44 c8 7a e3                                    |.D.z.|
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
-00000010  00 00 00 00 00 00 00 00  00 00 00 a6 52 02 4f 20  |............R.O |
-00000020  f6 d7 2d 2d 7c 65 4e 7b  43 33 32 50 9b c6 68 2c  |..--|eN{C32P..h,|
-00000030  c0 6a 02 6f c6 bc 38 d8  06 c0 42 ba c1 41 ce 5c  |.j.o..8...B..A.\|
-00000040  d0 a0 5f fc 8a 31 33 26  a2 79 9a 17 03 03 00 40  |.._..13&.y.....@|
+00000010  00 00 00 00 00 00 00 00  00 00 00 43 7c 61 f2 30  |...........C|a.0|
+00000020  c6 4a c2 76 da 7d 84 c6  ed 5d ee 2e 9c 33 e4 3b  |.J.v.}...]...3.;|
+00000030  e3 a1 ea ee 44 02 4b f7  90 f6 0c 8b 45 d7 26 2e  |....D.K.....E.&.|
+00000040  4a 37 43 1d 93 44 79 e6  5d c5 8c 17 03 03 00 40  |J7C..Dy.]......@|
 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000060  f2 42 8f e8 79 0d f3 c0  a0 b7 8a 5e de b8 52 c4  |.B..y......^..R.|
-00000070  b6 9d b2 10 00 e8 a3 19  27 12 ac 38 e7 d8 ec 89  |........'..8....|
-00000080  af 7d 68 15 03 e8 c4 c8  08 34 ad ad 15 7b 69 bb  |.}h......4...{i.|
+00000060  b2 e4 62 17 e2 d4 d8 73  2b ea 77 39 78 51 1a 86  |..b....s+.w9xQ..|
+00000070  64 54 1f 36 9a cc a1 c0  d2 6d df b7 8a 2e 68 b0  |dT.6.....m....h.|
+00000080  79 9a 9f a1 15 b1 78 fa  db 2e 5a 43 0d fe 45 71  |y.....x...ZC..Eq|
 00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-000000a0  00 00 00 00 00 a0 a5 02  ff b1 77 9a 8f e0 fc ca  |..........w.....|
-000000b0  86 ee ca 9c 7c 3b ca 61  33 7f f9 12 54 79 41 97  |....|;.a3...TyA.|
-000000c0  b0 7d bd 9b 93                                    |.}...|
+000000a0  00 00 00 00 00 04 8c d0  bf 15 9f b4 55 22 b8 8e  |............U"..|
+000000b0  a5 a7 df ed bd b2 ab 88  71 38 bd b2 5d b4 5e 8e  |........q8..].^.|
+000000c0  54 fc e4 63 5a                                    |T..cZ|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndECDSAGiven b/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndECDSAGiven
index 626024c..7a950db 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndECDSAGiven
+++ b/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndECDSAGiven
@@ -1,57 +1,56 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 5b 01 00 00  57 03 03 55 46 9e 3b 51  |....[...W..UF.;Q|
-00000010  e7 cd bf df bc fe 0d 5a  5a de a6 09 6c 72 cb ea  |.......ZZ...lr..|
-00000020  ab f8 a6 fd 9a 5b be 77  7d 25 20 00 00 04 00 05  |.....[.w}% .....|
-00000030  00 ff 02 01 00 00 29 00  0d 00 20 00 1e 06 01 06  |......)... .....|
-00000040  02 06 03 05 01 05 02 05  03 04 01 04 02 04 03 03  |................|
-00000050  01 03 02 03 03 02 01 02  02 02 03 00 0f 00 01 01  |................|
+00000000  16 03 01 00 5d 01 00 00  59 03 03 8e 50 ff 02 c4  |....]...Y...P...|
+00000010  3b 5e dd ee 59 d1 3a e1  db f1 30 f4 bb a7 8a 8c  |;^..Y.:...0.....|
+00000020  b2 d2 1a fd f8 a4 c9 e4  5f 41 e1 00 00 04 00 2f  |........_A...../|
+00000030  00 ff 01 00 00 2c 00 0d  00 20 00 1e 06 01 06 02  |.....,... ......|
+00000040  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000050  03 02 03 03 02 01 02 02  02 03 00 16 00 00 00 17  |................|
+00000060  00 00                                             |..|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 05 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002b0  17 0d 00 00 13 02 01 40  00 0c 04 01 04 03 05 01  |....... at ........|
-000002c0  05 03 02 01 02 03 00 00  16 03 03 00 04 0e 00 00  |................|
-000002d0  00                                                |.|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  17 0d 00 00 13 02 01 40  |;..............@|
+000002a0  00 0c 04 01 04 03 05 01  05 03 02 01 02 03 00 00  |................|
+000002b0  16 03 03 00 04 0e 00 00  00                       |.........|
 >>> Flow 3 (client to server)
 00000000  16 03 03 02 0a 0b 00 02  06 00 02 03 00 02 00 30  |...............0|
 00000010  82 01 fc 30 82 01 5e 02  09 00 9a 30 84 6c 26 35  |...0..^....0.l&5|
@@ -86,32 +85,40 @@
 000001e0  be e8 91 b3 da 1a f5 5d  a3 23 f5 26 8b 45 70 8d  |.......].#.&.Ep.|
 000001f0  65 62 9b 7e 01 99 3d 18  f6 10 9a 38 61 9b 2e 57  |eb.~..=....8a..W|
 00000200  e4 fa cc b1 8a ce e2 23  a0 87 f0 e1 67 51 eb 16  |.......#....gQ..|
-00000210  03 03 00 86 10 00 00 82  00 80 03 64 6f 74 1b 0e  |...........dot..|
-00000220  df 6b a4 8e f8 ec b5 02  c2 d6 7a 9a f3 bf 3e 32  |.k........z...>2|
-00000230  ba 41 dd 61 33 8a 63 fb  71 e6 87 68 32 9c 41 d5  |.A.a3.c.q..h2.A.|
-00000240  59 ee 93 55 16 e9 0a 01  72 14 93 23 82 73 91 3a  |Y..U....r..#.s.:|
-00000250  6d 3c e6 e0 a8 33 34 84  80 59 65 6b c1 6d 01 19  |m<...34..Yek.m..|
-00000260  cc d5 4f 1d f6 88 4f cc  b5 c6 3c 9c 68 4a be 47  |..O...O...<.hJ.G|
-00000270  c2 67 61 a4 e3 c3 00 c0  9c d4 83 ed b5 65 25 a4  |.ga..........e%.|
-00000280  2e 1c 8d 47 3f 80 b8 1d  5b 74 a2 bf fa b9 b7 e2  |...G?...[t......|
-00000290  58 94 ba ec a9 cf 1c 56  ef 0a 16 03 03 00 92 0f  |X......V........|
-000002a0  00 00 8e 04 03 00 8a 30  81 87 02 41 75 cf 19 3a  |.......0...Au..:|
-000002b0  a1 9e e9 69 c7 f3 63 0b  46 c2 60 35 e1 cc 95 0d  |...i..c.F.`5....|
-000002c0  ee 0f ad 28 17 b4 b2 09  ea 38 18 c7 08 84 b6 ac  |...(.....8......|
-000002d0  65 03 b9 49 c3 ea ff e4  45 d3 15 14 3a 94 14 0c  |e..I....E...:...|
-000002e0  cb 48 ce 75 c2 a4 4a 0e  7d d8 f0 c5 5f 02 42 01  |.H.u..J.}..._.B.|
-000002f0  99 dd c7 54 ce ee 38 bb  18 16 eb 92 0a 53 0b 92  |...T..8......S..|
-00000300  d8 73 73 48 b3 0a 3b ea  12 ea 62 d3 88 99 00 54  |.ssH..;...b....T|
-00000310  bc 92 28 7d 66 b3 17 7f  e7 5f 69 50 d1 a1 4c 6a  |..(}f...._iP..Lj|
-00000320  99 60 00 59 0a 4d 6c 97  05 54 ee 82 5a e1 c5 88  |.`.Y.Ml..T..Z...|
-00000330  1b 14 03 03 00 01 01 16  03 03 00 24 80 64 11 aa  |...........$.d..|
-00000340  cc 9d 1c 83 b6 2f 56 dc  48 cb 33 e5 0f 25 a2 42  |...../V.H.3..%.B|
-00000350  df b8 a6 cc 64 93 10 63  ad 76 91 27 3f c7 8f d4  |....d..c.v.'?...|
+00000210  03 03 00 86 10 00 00 82  00 80 2b 80 6e 49 b8 ec  |..........+.nI..|
+00000220  12 7a 7c f3 2a d3 7e 16  a0 39 e5 77 61 7a 56 15  |.z|.*.~..9.wazV.|
+00000230  97 c6 64 63 13 cf 09 d0  1b f5 b6 78 1d cb 86 4f  |..dc.......x...O|
+00000240  14 84 c9 e6 5d 3c 6b 61  5e 46 83 7e ef 1d 74 d4  |....]<ka^F.~..t.|
+00000250  3b 8c 78 be 26 92 24 04  b4 6f 21 88 03 8d 92 a8  |;.x.&.$..o!.....|
+00000260  60 c6 08 b5 75 5d 2f 2c  71 60 5f 54 27 a0 fa 83  |`...u]/,q`_T'...|
+00000270  4d 39 1e 22 1e 1e 60 92  51 ac 2d 35 c7 cf fc 5e  |M9."..`.Q.-5...^|
+00000280  db e3 60 37 6b 4e 7c d8  04 f3 09 54 de 38 af 57  |..`7kN|....T.8.W|
+00000290  20 d0 f5 08 5a a8 6f 65  03 55 16 03 03 00 93 0f  | ...Z.oe.U......|
+000002a0  00 00 8f 04 03 00 8b 30  81 88 02 42 00 97 84 ac  |.......0...B....|
+000002b0  cf 9b df b0 3a c8 9d a6  da 8c 11 87 35 2a d7 d0  |....:.......5*..|
+000002c0  15 df e1 02 ca 85 3f 1c  a5 21 17 8c 8a 73 1b 76  |......?..!...s.v|
+000002d0  8d 0f af 26 ea b5 7f 87  a6 b6 c8 61 32 27 fc f4  |...&.......a2'..|
+000002e0  b7 c5 c3 2c 53 61 59 5a  5d 12 c6 dd 9e 54 02 42  |...,SaYZ]....T.B|
+000002f0  01 04 3f 82 bb a4 5c ea  ea c9 9c 2a 75 96 c2 88  |..?...\....*u...|
+00000300  5a ae f8 2e 29 01 cf 7b  a4 20 83 df ec c8 9d 37  |Z...)..{. .....7|
+00000310  0c 33 fb 20 73 51 47 6d  81 d0 75 b1 19 ed 02 00  |.3. sQGm..u.....|
+00000320  b8 40 67 75 b9 72 63 9c  1e e1 c9 44 93 d6 ec e7  |. at gu.rc....D....|
+00000330  16 5c 14 03 03 00 01 01  16 03 03 00 40 43 51 cf  |.\.......... at CQ.|
+00000340  07 b2 de 6e 40 10 eb dd  3f 84 6a 54 a3 7f b2 48  |...n at ...?.jT...H|
+00000350  b3 aa 3c d4 e7 69 32 7c  77 ba e9 0b 99 b3 c9 e8  |..<..i2|w.......|
+00000360  c5 53 29 9a 6b 82 ee 7d  5e a9 ae 63 fa a9 af 21  |.S).k..}^..c...!|
+00000370  f2 04 b1 a1 bf f1 10 4c  65 6c 49 34 a0           |.......LelI4.|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 24 8d e5 5f d9  |..........$$.._.|
-00000010  99 7d d4 f2 5f f4 4b e3  b4 8e 33 84 7a c3 cb bf  |.}.._.K...3.z...|
-00000020  21 00 94 db 7b 7f 6c fa  a0 f2 9f 0e e9 3b 27 17  |!...{.l......;'.|
-00000030  03 03 00 21 67 f8 3a ff  c1 3b cb de 04 bf 49 a6  |...!g.:..;....I.|
-00000040  9a 45 56 ab 64 99 06 7e  40 cc a7 f6 4e 1e ca cb  |.EV.d..~@...N...|
-00000050  11 87 da 58 b7 15 03 03  00 16 10 1b 62 97 25 bf  |...X........b.%.|
-00000060  84 c1 23 d6 76 4a a1 da  07 c7 25 68 f6 6e 63 55  |..#.vJ....%h.ncU|
+00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
+00000010  00 00 00 00 00 00 00 00  00 00 00 93 a2 18 a9 e2  |................|
+00000020  51 be f7 bd c0 05 64 51  0a 17 9d 58 11 d1 a6 b9  |Q.....dQ...X....|
+00000030  6d 1e 42 16 e4 bc bf 09  f2 b9 29 20 74 8a cd 8a  |m.B.......) t...|
+00000040  b6 31 04 64 fb 5b 1f 83  c3 19 78 17 03 03 00 40  |.1.d.[....x....@|
+00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000060  ee c4 d0 5d 69 37 2b fc  dc 9c f1 77 df 44 6f da  |...]i7+....w.Do.|
+00000070  4e 22 05 05 3a 6c 32 a8  6c c2 fb ce ca a7 1b 54  |N"..:l2.l......T|
+00000080  2a 25 ae cf 77 e4 47 21  33 b6 29 54 62 00 dd 30  |*%..w.G!3.)Tb..0|
+00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
+000000a0  00 00 00 00 00 cf e1 fd  e3 5f d3 19 cd 05 70 79  |........._....py|
+000000b0  be 16 a5 26 18 f1 92 bc  73 bd 6f 4d 33 3d 6f 8a  |...&....s.oM3=o.|
+000000c0  13 51 7c 57 c7                                    |.Q|W.|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndGiven b/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndGiven
index 819825c..c81acc8 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndGiven
+++ b/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedAndGiven
@@ -1,116 +1,123 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 5b 01 00 00  57 03 03 87 41 6f c5 67  |....[...W...Ao.g|
-00000010  07 3b 12 46 ad aa d2 be  0d 08 98 e3 c7 4b ac 48  |.;.F.........K.H|
-00000020  67 02 6b 3b dc 84 79 c5  57 e9 89 00 00 04 00 05  |g.k;..y.W.......|
-00000030  00 ff 02 01 00 00 29 00  0d 00 20 00 1e 06 01 06  |......)... .....|
-00000040  02 06 03 05 01 05 02 05  03 04 01 04 02 04 03 03  |................|
-00000050  01 03 02 03 03 02 01 02  02 02 03 00 0f 00 01 01  |................|
+00000000  16 03 01 00 5d 01 00 00  59 03 03 f3 3a db 98 ff  |....]...Y...:...|
+00000010  29 a2 30 75 53 87 b3 5f  00 b5 9f 77 4d 88 38 ea  |).0uS.._...wM.8.|
+00000020  e9 87 f4 a4 e4 da dd 73  00 47 d1 00 00 04 00 2f  |.......s.G...../|
+00000030  00 ff 01 00 00 2c 00 0d  00 20 00 1e 06 01 06 02  |.....,... ......|
+00000040  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000050  03 02 03 03 02 01 02 02  02 03 00 16 00 00 00 17  |................|
+00000060  00 00                                             |..|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 05 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002b0  17 0d 00 00 13 02 01 40  00 0c 04 01 04 03 05 01  |....... at ........|
-000002c0  05 03 02 01 02 03 00 00  16 03 03 00 04 0e 00 00  |................|
-000002d0  00                                                |.|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  17 0d 00 00 13 02 01 40  |;..............@|
+000002a0  00 0c 04 01 04 03 05 01  05 03 02 01 02 03 00 00  |................|
+000002b0  16 03 03 00 04 0e 00 00  00                       |.........|
 >>> Flow 3 (client to server)
-00000000  16 03 03 01 fb 0b 00 01  f7 00 01 f4 00 01 f1 30  |...............0|
-00000010  82 01 ed 30 82 01 58 a0  03 02 01 02 02 01 00 30  |...0..X........0|
-00000020  0b 06 09 2a 86 48 86 f7  0d 01 01 05 30 26 31 10  |...*.H......0&1.|
-00000030  30 0e 06 03 55 04 0a 13  07 41 63 6d 65 20 43 6f  |0...U....Acme Co|
-00000040  31 12 30 10 06 03 55 04  03 13 09 31 32 37 2e 30  |1.0...U....127.0|
-00000050  2e 30 2e 31 30 1e 17 0d  31 31 31 32 30 38 30 37  |.0.10...11120807|
-00000060  35 35 31 32 5a 17 0d 31  32 31 32 30 37 30 38 30  |5512Z..121207080|
-00000070  30 31 32 5a 30 26 31 10  30 0e 06 03 55 04 0a 13  |012Z0&1.0...U...|
-00000080  07 41 63 6d 65 20 43 6f  31 12 30 10 06 03 55 04  |.Acme Co1.0...U.|
-00000090  03 13 09 31 32 37 2e 30  2e 30 2e 31 30 81 9c 30  |...127.0.0.10..0|
-000000a0  0b 06 09 2a 86 48 86 f7  0d 01 01 01 03 81 8c 00  |...*.H..........|
-000000b0  30 81 88 02 81 80 4e d0  7b 31 e3 82 64 d9 59 c0  |0.....N.{1..d.Y.|
-000000c0  c2 87 a4 5e 1e 8b 73 33  c7 63 53 df 66 92 06 84  |...^..s3.cS.f...|
-000000d0  f6 64 d5 8f e4 36 a7 1d  2b e8 b3 20 36 45 23 b5  |.d...6..+.. 6E#.|
-000000e0  e3 95 ae ed e0 f5 20 9c  8d 95 df 7f 5a 12 ef 87  |...... .....Z...|
-000000f0  e4 5b 68 e4 e9 0e 74 ec  04 8a 7f de 93 27 c4 01  |.[h...t......'..|
-00000100  19 7a bd f2 dc 3d 14 ab  d0 54 ca 21 0c d0 4d 6e  |.z...=...T.!..Mn|
-00000110  87 2e 5c c5 d2 bb 4d 4b  4f ce b6 2c f7 7e 88 ec  |..\...MKO..,.~..|
-00000120  7c d7 02 91 74 a6 1e 0c  1a da e3 4a 5a 2e de 13  ||...t......JZ...|
-00000130  9c 4c 40 88 59 93 02 03  01 00 01 a3 32 30 30 30  |.L at .Y.......2000|
-00000140  0e 06 03 55 1d 0f 01 01  ff 04 04 03 02 00 a0 30  |...U...........0|
-00000150  0d 06 03 55 1d 0e 04 06  04 04 01 02 03 04 30 0f  |...U..........0.|
-00000160  06 03 55 1d 23 04 08 30  06 80 04 01 02 03 04 30  |..U.#..0.......0|
-00000170  0b 06 09 2a 86 48 86 f7  0d 01 01 05 03 81 81 00  |...*.H..........|
-00000180  36 1f b3 7a 0c 75 c9 6e  37 46 61 2b d5 bd c0 a7  |6..z.u.n7Fa+....|
-00000190  4b cc 46 9a 81 58 7c 85  79 29 c8 c8 c6 67 dd 32  |K.F..X|.y)...g.2|
-000001a0  56 45 2b 75 b6 e9 24 a9  50 9a be 1f 5a fa 1a 15  |VE+u..$.P...Z...|
-000001b0  d9 cc 55 95 72 16 83 b9  c2 b6 8f fd 88 8c 38 84  |..U.r.........8.|
-000001c0  1d ab 5d 92 31 13 4f fd  83 3b c6 9d f1 11 62 b6  |..].1.O..;....b.|
-000001d0  8b ec ab 67 be c8 64 b0  11 50 46 58 17 6b 99 1c  |...g..d..PFX.k..|
-000001e0  d3 1d fc 06 f1 0e e5 96  a8 0c f9 78 20 b7 44 18  |...........x .D.|
-000001f0  51 8d 10 7e 4f 94 67 df  a3 4e 70 73 8e 90 91 85  |Q..~O.g..Nps....|
-00000200  16 03 03 00 86 10 00 00  82 00 80 79 a7 23 10 fc  |...........y.#..|
-00000210  64 a7 ab 17 ce d6 8b ab  ff c2 44 40 3b ba b4 c6  |d.........D@;...|
-00000220  86 b7 66 7d be 9b fa 66  f9 f6 bb e4 f7 02 16 ea  |..f}...f........|
-00000230  0f 13 9c 8a 98 3a 34 e6  58 82 dc dc 27 3a 3d 5c  |.....:4.X...':=\|
-00000240  99 09 db 48 54 a5 5a a2  16 7f ba 99 d9 0d ca fb  |...HT.Z.........|
-00000250  4a 9e b7 f6 3a ab 26 ef  f9 df a2 0c 4c 45 19 3b  |J...:.&.....LE.;|
-00000260  b2 9f 21 cd ff fc cc c7  fb 91 fa 54 93 a9 42 a9  |..!........T..B.|
-00000270  4c 48 4a 8c 7b 9a d7 90  97 f6 21 89 03 f6 a5 86  |LHJ.{.....!.....|
-00000280  83 6f 21 19 2f 5b f8 ec  a6 36 e9 16 03 03 00 88  |.o!./[...6......|
-00000290  0f 00 00 84 04 01 00 80  0f 9d 15 cc c0 0b 71 8a  |..............q.|
-000002a0  b9 95 ca 9a 86 ff bf 93  8d da 64 ce 99 28 e2 6e  |..........d..(.n|
-000002b0  6d 6f 34 c9 03 fa 87 96  b0 1d 4f b2 3c 9e 4d 2c  |mo4.......O.<.M,|
-000002c0  df be 7d fb 53 fe 90 6f  45 f3 f0 d9 ab 70 d4 df  |..}.S..oE....p..|
-000002d0  5a 95 a4 53 12 02 c1 45  15 c2 2b 69 7e 5f 6f cd  |Z..S...E..+i~_o.|
-000002e0  b3 eb 5d ff 48 36 94 ad  28 29 fe 47 40 ab 9c eb  |..].H6..().G at ...|
-000002f0  02 f9 ca 7d e0 48 9f 6e  a4 9f 1e c2 d7 fd 16 18  |...}.H.n........|
-00000300  db ad d9 35 27 89 96 c8  c4 70 10 be a4 5d 6b b4  |...5'....p...]k.|
-00000310  d8 61 70 93 08 00 0f c9  14 03 03 00 01 01 16 03  |.ap.............|
-00000320  03 00 24 7b ee b7 23 12  63 f0 80 ca b3 6f d3 b8  |..${..#.c....o..|
-00000330  ca cc 4a 54 06 ea e5 3e  73 f2 de 1d d6 16 7e 61  |..JT...>s.....~a|
-00000340  32 76 eb f8 8a 66 74                              |2v...ft|
+00000000  16 03 03 01 fd 0b 00 01  f9 00 01 f6 00 01 f3 30  |...............0|
+00000010  82 01 ef 30 82 01 58 a0  03 02 01 02 02 10 5c 19  |...0..X.......\.|
+00000020  c1 89 65 83 55 6f dc 0b  c9 b9 93 9f e9 bc 30 0d  |..e.Uo........0.|
+00000030  06 09 2a 86 48 86 f7 0d  01 01 0b 05 00 30 12 31  |..*.H........0.1|
+00000040  10 30 0e 06 03 55 04 0a  13 07 41 63 6d 65 20 43  |.0...U....Acme C|
+00000050  6f 30 1e 17 0d 31 36 30  38 31 37 32 31 35 32 33  |o0...16081721523|
+00000060  31 5a 17 0d 31 37 30 38  31 37 32 31 35 32 33 31  |1Z..170817215231|
+00000070  5a 30 12 31 10 30 0e 06  03 55 04 0a 13 07 41 63  |Z0.1.0...U....Ac|
+00000080  6d 65 20 43 6f 30 81 9f  30 0d 06 09 2a 86 48 86  |me Co0..0...*.H.|
+00000090  f7 0d 01 01 01 05 00 03  81 8d 00 30 81 89 02 81  |...........0....|
+000000a0  81 00 ba 6f aa 86 bd cf  bf 9f f2 ef 5c 94 60 78  |...o........\.`x|
+000000b0  6f e8 13 f2 d1 96 6f cd  d9 32 6e 22 37 ce 41 f9  |o.....o..2n"7.A.|
+000000c0  ca 5d 29 ac e1 27 da 61  a2 ee 81 cb 10 c7 df 34  |.])..'.a.......4|
+000000d0  58 95 86 e9 3d 19 e6 5c  27 73 60 c8 8d 78 02 f4  |X...=..\'s`..x..|
+000000e0  1d a4 98 09 a3 19 70 69  3c 25 62 66 2a ab 22 23  |......pi<%bf*."#|
+000000f0  c5 7b 85 38 4f 2e 09 73  32 a7 bd 3e 9b ad ca 84  |.{.8O..s2..>....|
+00000100  07 e6 0f 3a ff 77 c5 9d  41 85 00 8a b6 9b ee b0  |...:.w..A.......|
+00000110  a4 3f 2d 4c 4c e6 42 3e  bb 51 c8 dd 48 54 f4 0c  |.?-LL.B>.Q..HT..|
+00000120  8e 47 02 03 01 00 01 a3  46 30 44 30 0e 06 03 55  |.G......F0D0...U|
+00000130  1d 0f 01 01 ff 04 04 03  02 05 a0 30 13 06 03 55  |...........0...U|
+00000140  1d 25 04 0c 30 0a 06 08  2b 06 01 05 05 07 03 01  |.%..0...+.......|
+00000150  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 0f  |0...U.......0.0.|
+00000160  06 03 55 1d 11 04 08 30  06 87 04 7f 00 00 01 30  |..U....0.......0|
+00000170  0d 06 09 2a 86 48 86 f7  0d 01 01 0b 05 00 03 81  |...*.H..........|
+00000180  81 00 46 ab 44 a2 fb 28  54 f8 5a 67 f8 62 94 f1  |..F.D..(T.Zg.b..|
+00000190  9a b2 18 9e f2 b1 de 1d  7e 6f 76 95 a9 ba e7 5d  |........~ov....]|
+000001a0  a8 16 6c 9c f7 09 d3 37  e4 4b 2b 36 7c 01 ad 41  |..l....7.K+6|..A|
+000001b0  d2 32 d8 c3 d2 93 f9 10  6b 8e 95 b9 2c 17 8a a3  |.2......k...,...|
+000001c0  44 48 bc 59 13 83 16 04  88 a4 81 5c 25 0d 98 0c  |DH.Y.......\%...|
+000001d0  ac 11 b1 28 56 be 1d cd  61 62 84 09 bf d6 80 c6  |...(V...ab......|
+000001e0  45 8d 82 2c b4 d8 83 9b  db c9 22 b7 2a 12 11 7b  |E..,......".*..{|
+000001f0  fa 02 3b c1 c9 ff ea c9  9d a8 49 d3 95 d7 d5 0e  |..;.......I.....|
+00000200  e5 35 16 03 03 00 86 10  00 00 82 00 80 47 31 82  |.5...........G1.|
+00000210  ae c1 d2 74 3f a4 74 5a  57 16 ae e2 d0 46 72 53  |...t?.tZW....FrS|
+00000220  e1 5e 6a e8 e4 d5 8c 84  2b d9 82 c1 4a da 9e 1d  |.^j.....+...J...|
+00000230  a0 da 60 08 0d 35 0c 55  6d 6a 68 04 09 ee 94 39  |..`..5.Umjh....9|
+00000240  c7 a3 49 7f 2c ee 6a cf  09 01 bd 08 d3 59 0a bd  |..I.,.j......Y..|
+00000250  7f 6c d3 26 eb be 7b fd  9b 17 fd e2 6e 82 d1 c7  |.l.&..{.....n...|
+00000260  dd c3 64 8c 87 f0 41 f2  71 75 f1 0a 01 26 5b 97  |..d...A.qu...&[.|
+00000270  94 ba ac 50 df 19 32 39  80 ae 14 ea 4a d2 e5 9f  |...P..29....J...|
+00000280  5d 07 9f 2d 89 ac 83 33  40 aa 8e cc 2c 16 03 03  |]..-...3 at ...,...|
+00000290  00 88 0f 00 00 84 04 01  00 80 7d 37 8b 6f be e9  |..........}7.o..|
+000002a0  e7 fa 4c 28 cf 16 0d 28  40 e9 f2 9a 11 22 fc 8a  |..L(...(@...."..|
+000002b0  2c 52 f7 36 af 1a cf d7  8a f8 17 19 9f ed 9d 1d  |,R.6............|
+000002c0  43 f9 e2 fb 0f dd ca d6  1d 4c 03 4e 25 8d 5c 4c  |C........L.N%.\L|
+000002d0  95 98 02 db cf ea 44 2a  ad 36 74 e3 08 07 e3 9a  |......D*.6t.....|
+000002e0  50 6c dc 46 a1 f5 84 9b  65 7f 48 94 b5 de cc a9  |Pl.F....e.H.....|
+000002f0  cf ee 0e 31 f2 f8 6f 8f  19 4b 29 14 b4 32 1d 21  |...1..o..K)..2.!|
+00000300  02 d2 da 64 68 e8 a1 72  cc ee 64 48 d8 74 e5 64  |...dh..r..dH.t.d|
+00000310  90 b3 50 cc 3e 25 0e b1  88 53 14 03 03 00 01 01  |..P.>%...S......|
+00000320  16 03 03 00 40 6a 61 6b  3e ea 63 2c b8 26 95 e2  |.... at jak>.c,.&..|
+00000330  5f 83 e3 c3 cd c3 b7 a8  0b 76 81 8a 5b 46 ff 41  |_........v..[F.A|
+00000340  c2 02 eb 21 85 31 b9 ba  2e 30 e7 6e 8d 1c 49 15  |...!.1...0.n..I.|
+00000350  af a0 a7 67 62 b7 42 8c  fa a8 04 8c 23 7a 3d 39  |...gb.B.....#z=9|
+00000360  74 18 70 2b 99                                    |t.p+.|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 64 d5 a4 78 e9  |..........$d..x.|
-00000010  f1 1d d1 34 f7 b3 95 87  18 f6 cf 65 c6 f0 02 08  |...4.......e....|
-00000020  69 f5 6d aa f2 da fc 2c  ac fc aa f8 25 aa 50 17  |i.m....,....%.P.|
-00000030  03 03 00 21 9f 94 f8 78  46 58 2c 21 0d 30 04 89  |...!...xFX,!.0..|
-00000040  bd 35 03 dc 04 b6 0f 6f  22 65 db 3d 8d 96 00 0c  |.5.....o"e.=....|
-00000050  db bf e5 b3 59 15 03 03  00 16 a6 35 f2 07 5e 32  |....Y......5..^2|
-00000060  4e 09 e4 31 3a f6 4a 83  c2 03 db b9 bf b0 eb 6d  |N..1:.J........m|
+00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
+00000010  00 00 00 00 00 00 00 00  00 00 00 a6 5b 7c b0 91  |............[|..|
+00000020  53 f6 d5 e4 34 71 4f 64  2a 03 9d 75 62 d9 8d a8  |S...4qOd*..ub...|
+00000030  39 7b e1 d8 31 80 26 db  14 f3 3a 52 66 7d 12 31  |9{..1.&...:Rf}.1|
+00000040  29 14 7f a1 39 b6 1c e0  c9 55 6e 17 03 03 00 40  |)...9....Un....@|
+00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000060  5e 00 64 9b 25 cd 74 94  b7 65 6e 83 8e 5b 68 e8  |^.d.%.t..en..[h.|
+00000070  59 4c f0 31 8b f2 0c 59  2a ff 11 8e 43 d4 73 fd  |YL.1...Y*...C.s.|
+00000080  b3 2a 76 59 25 52 32 76  bd 2e 1d 4d 0a 53 d7 c2  |.*vY%R2v...M.S..|
+00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
+000000a0  00 00 00 00 00 23 96 6e  7d 41 bb 51 4f 40 52 07  |.....#.n}A.QO at R.|
+000000b0  90 cc 6c bd c0 bb 99 d4  8a 91 7b 8a f3 24 ef 71  |..l.......{..$.q|
+000000c0  20 d4 98 b0 14                                    | ....|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedNotGiven b/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedNotGiven
index 903272b..091de90 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedNotGiven
+++ b/src/crypto/tls/testdata/Server-TLSv12-ClientAuthRequestedNotGiven
@@ -1,76 +1,83 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 5b 01 00 00  57 03 03 9a 56 e0 d9 b8  |....[...W...V...|
-00000010  ac d5 88 c7 2f d1 87 1b  44 c6 ff 7b 4f 6f f0 2a  |..../...D..{Oo.*|
-00000020  56 a1 9e 46 86 4b 6f 91  29 29 3b 00 00 04 00 05  |V..F.Ko.));.....|
-00000030  00 ff 02 01 00 00 29 00  0d 00 20 00 1e 06 01 06  |......)... .....|
-00000040  02 06 03 05 01 05 02 05  03 04 01 04 02 04 03 03  |................|
-00000050  01 03 02 03 03 02 01 02  02 02 03 00 0f 00 01 01  |................|
+00000000  16 03 01 00 5d 01 00 00  59 03 03 41 24 db 27 37  |....]...Y..A$.'7|
+00000010  2e 1e a8 61 b7 7f 08 f1  83 84 fb d5 a2 96 e0 53  |...a...........S|
+00000020  1a 2b cd 8e 50 38 7b a5  64 d8 92 00 00 04 00 2f  |.+..P8{.d....../|
+00000030  00 ff 01 00 00 2c 00 0d  00 20 00 1e 06 01 06 02  |.....,... ......|
+00000040  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000050  03 02 03 03 02 01 02 02  02 03 00 16 00 00 00 17  |................|
+00000060  00 00                                             |..|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 05 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002b0  17 0d 00 00 13 02 01 40  00 0c 04 01 04 03 05 01  |....... at ........|
-000002c0  05 03 02 01 02 03 00 00  16 03 03 00 04 0e 00 00  |................|
-000002d0  00                                                |.|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  17 0d 00 00 13 02 01 40  |;..............@|
+000002a0  00 0c 04 01 04 03 05 01  05 03 02 01 02 03 00 00  |................|
+000002b0  16 03 03 00 04 0e 00 00  00                       |.........|
 >>> Flow 3 (client to server)
 00000000  16 03 03 00 07 0b 00 00  03 00 00 00 16 03 03 00  |................|
-00000010  86 10 00 00 82 00 80 3a  72 91 a2 c3 ba 83 75 1b  |.......:r.....u.|
-00000020  d3 f6 1c 07 7f 92 a8 b0  1f 47 42 cc 8d 4e 7e 1e  |.........GB..N~.|
-00000030  23 49 44 29 53 19 9f 3b  5c bb 5d ed 6c d9 49 5d  |#ID)S..;\.].l.I]|
-00000040  6e f9 d1 59 9d 40 67 b3  0c ee 41 85 6c 4a 4d 3b  |n..Y. at g...A.lJM;|
-00000050  c1 e6 c8 7f 93 15 cb 2a  17 64 da 70 f3 2a c3 7c  |.......*.d.p.*.||
-00000060  a2 02 48 19 fb 74 5a dc  52 0d 80 6b ed c0 8c 15  |..H..tZ.R..k....|
-00000070  3e 3b 34 7c 55 6e 95 e0  d1 4a 7f b0 bc 33 67 a7  |>;4|Un...J...3g.|
-00000080  3b 40 bb eb 83 58 4a fb  fb 01 9b 0d fa ef 83 c4  |;@...XJ.........|
-00000090  87 10 75 0c a7 ad 91 14  03 03 00 01 01 16 03 03  |..u.............|
-000000a0  00 24 18 ce de 8d ab c1  6e 3b 0b 51 fe 94 ae 0a  |.$......n;.Q....|
-000000b0  39 9c 4d a2 90 53 d4 1e  5f f6 96 5a 51 f2 39 c1  |9.M..S.._..ZQ.9.|
-000000c0  d6 06 c0 4e 58 99                                 |...NX.|
+00000010  86 10 00 00 82 00 80 61  2e 6b ad 77 48 8d 2f 0e  |.......a.k.wH./.|
+00000020  e6 27 64 fd 95 22 72 68  80 8e 2b 0e b2 0f cc be  |.'d.."rh..+.....|
+00000030  19 31 93 1d d3 0a fb 00  da 50 26 66 17 59 6b e9  |.1.......P&f.Yk.|
+00000040  7e 16 4e 24 ba 68 0a 0c  69 2f 03 74 34 50 12 85  |~.N$.h..i/.t4P..|
+00000050  4a f0 6c 52 d4 dd 13 18  c7 a4 9d ea f0 c6 94 d9  |J.lR............|
+00000060  ae fe 19 6c 83 dd ed 38  e3 d2 21 18 e6 76 11 e7  |...l...8..!..v..|
+00000070  62 13 8b 56 65 e0 f6 61  d9 db 7c 7b 5b 57 13 3d  |b..Ve..a..|{[W.=|
+00000080  58 64 67 8d 9f 3f 6a a0  70 c5 c6 d0 db eb 17 3f  |Xdg..?j.p......?|
+00000090  6c 58 d7 c3 ba ec 4c 14  03 03 00 01 01 16 03 03  |lX....L.........|
+000000a0  00 40 6e 24 1b a4 b3 e7  d4 c2 7e cb bd 82 2d 4c  |. at n$......~...-L|
+000000b0  a8 f1 5e 81 c8 61 a6 2c  6a e3 6d 30 7f fa c7 f3  |..^..a.,j.m0....|
+000000c0  f6 b1 b1 4f 0b 23 9a fd  66 81 48 97 4b 0f 88 07  |...O.#..f.H.K...|
+000000d0  37 7b 97 b0 62 6c 2a e1  47 65 e9 f9 cd c1 79 99  |7{..bl*.Ge....y.|
+000000e0  6d 84                                             |m.|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 8b 7e 57 f3 7d  |..........$.~W.}|
-00000010  ab 44 f0 c7 53 2d 39 08  14 32 12 4e 4b 45 9a e3  |.D..S-9..2.NKE..|
-00000020  1c 43 36 16 59 a0 4b e4  78 43 d2 a5 dc 96 b1 17  |.C6.Y.K.xC......|
-00000030  03 03 00 21 54 89 75 23  de 7d c7 c6 80 a6 a6 69  |...!T.u#.}.....i|
-00000040  d0 a8 95 77 71 a0 89 34  f4 c3 31 73 bb b0 ac d7  |...wq..4..1s....|
-00000050  e5 e4 83 4b 10 15 03 03  00 16 0d 44 43 67 21 cc  |...K.......DCg!.|
-00000060  6c c1 7e 72 99 aa 7f a1  de 10 0b 36 ae 05 d9 9e  |l.~r.......6....|
+00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
+00000010  00 00 00 00 00 00 00 00  00 00 00 40 c6 62 4e 26  |........... at .bN&|
+00000020  e7 32 09 ba d6 3b 71 79  8e 75 ee fb af 09 db c5  |.2...;qy.u......|
+00000030  a5 8b cd 1f 90 f3 65 86  4a b4 b1 9a e8 1e 80 f6  |......e.J.......|
+00000040  ad db bd c2 9f ec 98 42  0b 37 30 17 03 03 00 40  |.......B.70....@|
+00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000060  a9 74 3c 13 87 f4 cf 77  be 07 c2 a1 e0 47 4e 52  |.t<....w.....GNR|
+00000070  c2 86 da 08 c2 93 21 80  4c 19 51 cc 0d 76 49 75  |......!.L.Q..vIu|
+00000080  0b 48 3d e0 e2 01 93 4b  f1 73 91 17 aa 00 b5 71  |.H=....K.s.....q|
+00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
+000000a0  00 00 00 00 00 1f 2f 2d  d7 39 06 c4 59 49 80 66  |....../-.9..YI.f|
+000000b0  6c 35 2e a7 45 ee 0a 05  4b 1e 7f 78 5d cd 24 2c  |l5..E...K..x].$,|
+000000c0  0a 3e 55 1c 7d                                    |.>U.}|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ECDHE-ECDSA-AES b/src/crypto/tls/testdata/Server-TLSv12-ECDHE-ECDSA-AES
index 8b04a5a..b412980 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ECDHE-ECDSA-AES
+++ b/src/crypto/tls/testdata/Server-TLSv12-ECDHE-ECDSA-AES
@@ -1,13 +1,12 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 83 01 00 00  7f 03 03 ec 8e d0 43 01  |..............C.|
-00000010  8e 81 3f d8 1f 7e 96 f1  de 4c 94 18 09 1d c5 8c  |..?..~...L......|
-00000020  3a 58 68 5b 3e 7d 46 66  fe 04 74 00 00 04 c0 0a  |:Xh[>}Ff..t.....|
-00000030  00 ff 02 01 00 00 51 00  0b 00 04 03 00 01 02 00  |......Q.........|
-00000040  0a 00 1c 00 1a 00 17 00  19 00 1c 00 1b 00 18 00  |................|
-00000050  1a 00 16 00 0e 00 0d 00  0b 00 0c 00 09 00 0a 00  |................|
-00000060  0d 00 20 00 1e 06 01 06  02 06 03 05 01 05 02 05  |.. .............|
-00000070  03 04 01 04 02 04 03 03  01 03 02 03 03 02 01 02  |................|
-00000080  02 02 03 00 0f 00 01 01                           |........|
+00000000  16 03 01 00 73 01 00 00  6f 03 03 f0 4f 82 cc 2a  |....s...o...O..*|
+00000010  27 0b 7f 2e e4 af 6d ba  4e fe 61 99 fc 0a 44 ee  |'.....m.N.a...D.|
+00000020  c0 4e 7b 3a 7c f0 6d 12  b7 7d 9e 00 00 04 c0 0a  |.N{:|.m..}......|
+00000030  00 ff 01 00 00 42 00 0b  00 04 03 00 01 02 00 0a  |.....B..........|
+00000040  00 0a 00 08 00 1d 00 17  00 19 00 18 00 0d 00 20  |............... |
+00000050  00 1e 06 01 06 02 06 03  05 01 05 02 05 03 04 01  |................|
+00000060  04 02 04 03 03 01 03 02  03 03 02 01 02 02 02 03  |................|
+00000070  00 16 00 00 00 17 00 00                           |........|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
@@ -45,43 +44,39 @@
 00000210  0e bd 3f a3 8c 25 c1 33  13 83 0d 94 06 bb d4 37  |..?..%.3.......7|
 00000220  7a f6 ec 7a c9 86 2e dd  d7 11 69 7f 85 7c 56 de  |z..z......i..|V.|
 00000230  fb 31 78 2b e4 c7 78 0d  ae cb be 9e 4e 36 24 31  |.1x+..x.....N6$1|
-00000240  7b 6a 0f 39 95 12 07 8f  2a 16 03 03 00 d8 0c 00  |{j.9....*.......|
-00000250  00 d4 03 00 17 41 04 1e  18 37 ef 0d 19 51 88 35  |.....A...7...Q.5|
-00000260  75 71 b5 e5 54 5b 12 2e  8f 09 67 fd a7 24 20 3e  |uq..T[....g..$ >|
-00000270  b2 56 1c ce 97 28 5e f8  2b 2d 4f 9e f1 07 9f 6c  |.V...(^.+-O....l|
-00000280  4b 5b 83 56 e2 32 42 e9  58 b6 d7 49 a6 b5 68 1a  |K[.V.2B.X..I..h.|
-00000290  41 03 56 6b dc 5a 89 05  03 00 8b 30 81 88 02 42  |A.Vk.Z.....0...B|
-000002a0  01 08 89 99 1c 91 97 fb  e8 5b 69 5f f5 36 66 d6  |.........[i_.6f.|
-000002b0  dd 53 04 09 c8 7f c1 25  28 8c 28 57 55 3a 95 3f  |.S.....%(.(WU:.?|
-000002c0  ab 09 47 9a 27 74 83 84  44 cf 86 b7 5e 7f fe db  |..G.'t..D...^...|
-000002d0  05 33 3c 1a b7 f6 bc ff  0d 33 e4 ec 3c e2 1d e2  |.3<......3..<...|
-000002e0  6e ab 02 42 00 92 4e 45  a7 86 e4 bd 40 82 b7 04  |n..B..NE.... at ...|
-000002f0  12 fe 34 ab e3 c9 4a 05  1f 4e 58 79 67 58 94 53  |..4...J..NXygX.S|
-00000300  e8 1b ba 60 76 92 00 99  a7 5f 0a 98 cb e3 1e de  |...`v...._......|
-00000310  0c df 18 76 58 d5 e1 f1  ef a5 da 9a a3 62 77 50  |...vX........bwP|
-00000320  37 d0 22 d0 31 90 16 03  03 00 04 0e 00 00 00     |7.".1..........|
+00000240  7b 6a 0f 39 95 12 07 8f  2a 16 03 03 00 b7 0c 00  |{j.9....*.......|
+00000250  00 b3 03 00 1d 20 2f e5  7d a3 47 cd 62 43 15 28  |..... /.}.G.bC.(|
+00000260  da ac 5f bb 29 07 30 ff  f6 84 af c4 cf c2 ed 90  |.._.).0.........|
+00000270  99 5f 58 cb 3b 74 05 03  00 8b 30 81 88 02 42 01  |._X.;t....0...B.|
+00000280  2d d4 82 80 01 6b e6 8c  6a 2a b3 09 1b 0d 86 e6  |-....k..j*......|
+00000290  62 92 85 46 d9 e3 b2 e9  f1 5e 77 c2 27 fd 2b 68  |b..F.....^w.'.+h|
+000002a0  6a e1 3d e2 42 d2 86 96  42 b1 3b 50 7b e2 2c 34  |j.=.B...B.;P{.,4|
+000002b0  d3 e7 f6 14 89 48 eb 5c  9a 98 98 ab f3 db 85 06  |.....H.\........|
+000002c0  cb 02 42 00 df 42 94 63  a5 ff 43 a5 20 5d 83 09  |..B..B.c..C. ]..|
+000002d0  88 7d 10 ff ec 32 33 28  1d 43 b2 d2 bf 39 0c 63  |.}...23(.C...9.c|
+000002e0  9a c0 f8 0e 9f 71 a7 9a  5d 27 1a 5c f2 36 80 b3  |.....q..]'.\.6..|
+000002f0  71 0f d3 c0 fd 0d 5d 02  90 c4 9d 90 db 74 ad f6  |q.....]......t..|
+00000300  22 8f 6b 9d 55 16 03 03  00 04 0e 00 00 00        |".k.U.........|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 9e 94 25 4f 70  |....F...BA...%Op|
-00000010  a8 e0 87 3a 09 6c 58 4f  5e 76 d9 63 dc c3 d5 63  |...:.lXO^v.c...c|
-00000020  be f2 75 ff 23 23 79 6b  82 fe 56 f5 b9 7a 55 55  |..u.##yk..V..zUU|
-00000030  32 3b ee c5 f0 1f 7b e9  82 01 21 8d 06 03 48 95  |2;....{...!...H.|
-00000040  21 b8 fa 9d 18 2a 08 9c  71 a8 4d 14 03 03 00 01  |!....*..q.M.....|
-00000050  01 16 03 03 00 40 31 f0  7b 5f e8 94 a3 7f b0 12  |..... at 1.{_......|
-00000060  a9 80 87 26 eb cf b6 87  61 e7 5b 9b 36 3d 11 bb  |...&....a.[.6=..|
-00000070  21 55 5c f7 e8 f3 b7 1e  f2 06 0d c5 a9 8d f8 48  |!U\............H|
-00000080  c2 2b 8f 83 be 17 4f ec  ff 8e 24 44 74 25 09 40  |.+....O...$Dt%.@|
-00000090  90 fd 70 4d fb bb                                 |..pM..|
+00000000  16 03 03 00 25 10 00 00  21 20 af 52 73 d4 46 4d  |....%...! .Rs.FM|
+00000010  bc 0e dd 56 1f 7f 72 ce  6c 99 b9 64 53 7d 53 8d  |...V..r.l..dS}S.|
+00000020  0c a4 75 8c 83 3b 4b 76  2d 4f 14 03 03 00 01 01  |..u..;Kv-O......|
+00000030  16 03 03 00 40 a0 ef 9f  54 a8 ab 7c 5b 4a a1 b2  |.... at ...T..|[J..|
+00000040  5d 5b 6a d7 a7 32 35 46  58 d0 ba 38 6f 94 6e 9a  |][j..25FX..8o.n.|
+00000050  41 16 82 ed 4d 39 c4 ff  06 bf 2c 67 47 70 56 4e  |A...M9....,gGpVN|
+00000060  c5 ac 7f a0 5d d9 89 82  7a d9 36 07 55 b3 20 f4  |....]...z.6.U. .|
+00000070  b2 73 cf c3 7d                                    |.s..}|
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
-00000010  00 00 00 00 00 00 00 00  00 00 00 13 eb 4e 56 3d  |.............NV=|
-00000020  1b 10 2e e8 08 65 b9 53  9e 56 49 b7 e9 25 35 94  |.....e.S.VI..%5.|
-00000030  c7 df 7d f7 78 2e f3 8b  9c 2b 9d 42 90 91 5c 97  |..}.x....+.B..\.|
-00000040  22 20 ca 6d a2 83 b3 d8  b3 71 64 17 03 03 00 40  |" .m.....qd....@|
+00000010  00 00 00 00 00 00 00 00  00 00 00 73 5f db 9e 08  |...........s_...|
+00000020  10 38 3b c0 95 6b dd fc  16 b2 d1 db 63 13 ca d5  |.8;..k......c...|
+00000030  b5 be 5a 1d 74 b5 75 f3  a2 63 59 be a7 d0 ab 0d  |..Z.t.u..cY.....|
+00000040  d3 43 83 8a 1d 59 ed fd  ea f0 b9 17 03 03 00 40  |.C...Y.........@|
 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000060  97 f1 c4 2e bf 6d 85 d5  3d 4b 4a 8b ee 53 08 5a  |.....m..=KJ..S.Z|
-00000070  db 8b 75 49 d9 cb db e3  86 90 ac 93 ce e7 9a 70  |..uI...........p|
-00000080  4c dc 4a f4 c9 f6 b5 fd  f0 3f 9f e9 f9 c3 b3 c6  |L.J......?......|
+00000060  cf 20 4f 4f bf c4 00 05  1e ca 7f 6f 69 77 e9 52  |. OO.......oiw.R|
+00000070  14 61 02 6d f1 c0 ad 7c  1a 34 cf b2 7a 58 4a 70  |.a.m...|.4..zXJp|
+00000080  11 36 5f e9 21 62 cb eb  8f e7 11 04 bf 66 03 69  |.6_.!b.......f.i|
 00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-000000a0  00 00 00 00 00 5e b1 b7  21 7d 89 65 66 17 d8 79  |.....^..!}.ef..y|
-000000b0  26 db ad 08 28 2c e7 7a  c4 ec 93 19 4f c8 bb 5c  |&...(,.z....O..\|
-000000c0  c2 9e 09 56 07                                    |...V.|
+000000a0  00 00 00 00 00 f5 35 92  09 6c 45 c0 27 95 98 a9  |......5..lE.'...|
+000000b0  86 56 53 1f a8 01 d5 0b  79 0e 91 15 3b 9a 07 21  |.VS.....y...;..!|
+000000c0  cb ce f0 2b 6a                                    |...+j|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-IssueTicket b/src/crypto/tls/testdata/Server-TLSv12-IssueTicket
index 2760508..feced4b 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-IssueTicket
+++ b/src/crypto/tls/testdata/Server-TLSv12-IssueTicket
@@ -1,83 +1,89 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 5f 01 00 00  5b 03 03 6e cc 37 81 0a  |...._...[..n.7..|
-00000010  b9 fe 58 30 8e 32 61 3c  b1 38 1e 2b f6 ab 44 ee  |..X0.2a<.8.+..D.|
-00000020  f2 cc fe 6e fe 40 65 49  d9 ba aa 00 00 04 00 05  |...n. at eI........|
-00000030  00 ff 02 01 00 00 2d 00  23 00 00 00 0d 00 20 00  |......-.#..... .|
-00000040  1e 06 01 06 02 06 03 05  01 05 02 05 03 04 01 04  |................|
-00000050  02 04 03 03 01 03 02 03  03 02 01 02 02 02 03 00  |................|
-00000060  0f 00 01 01                                       |....|
+00000000  16 03 01 00 61 01 00 00  5d 03 03 b1 be 1f 18 b6  |....a...].......|
+00000010  a2 5d 4f 2f a0 e5 3b c4  4a 2d 76 bd 98 92 32 85  |.]O/..;.J-v...2.|
+00000020  9d 6b 9e 10 4b fc 03 7b  fb bc e4 00 00 04 00 2f  |.k..K..{......./|
+00000030  00 ff 01 00 00 30 00 23  00 00 00 0d 00 20 00 1e  |.....0.#..... ..|
+00000040  06 01 06 02 06 03 05 01  05 02 05 03 04 01 04 02  |................|
+00000050  04 03 03 01 03 02 03 03  02 01 02 02 02 03 00 16  |................|
+00000060  00 00 00 17 00 00                                 |......|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 35 02 00 00  31 03 03 00 00 00 00 00  |....5...1.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 05 00 00  |................|
-00000030  09 00 23 00 00 ff 01 00  01 00 16 03 03 02 71 0b  |..#...........q.|
-00000040  00 02 6d 00 02 6a 00 02  67 30 82 02 63 30 82 01  |..m..j..g0..c0..|
-00000050  cc a0 03 02 01 02 02 09  00 a2 73 00 0c 81 00 cb  |..........s.....|
-00000060  f3 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |.0...*.H........|
-00000070  30 2b 31 17 30 15 06 03  55 04 0a 13 0e 47 6f 6f  |0+1.0...U....Goo|
-00000080  67 6c 65 20 54 45 53 54  49 4e 47 31 10 30 0e 06  |gle TESTING1.0..|
-00000090  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
-000000a0  0d 31 35 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.150101000000Z..|
-000000b0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 26 31  |250101000000Z0&1|
-000000c0  17 30 15 06 03 55 04 0a  13 0e 47 6f 6f 67 6c 65  |.0...U....Google|
-000000d0  20 54 45 53 54 49 4e 47  31 0b 30 09 06 03 55 04  | TESTING1.0...U.|
-000000e0  03 13 02 47 6f 30 81 9f  30 0d 06 09 2a 86 48 86  |...Go0..0...*.H.|
-000000f0  f7 0d 01 01 01 05 00 03  81 8d 00 30 81 89 02 81  |...........0....|
-00000100  81 00 af 87 88 f6 20 1b  95 65 6c 14 ab 44 05 af  |...... ..el..D..|
-00000110  3b 45 14 e3 b7 6d fd 00  63 4d 95 7f fe 6a 62 35  |;E...m..cM...jb5|
-00000120  86 c0 4a f9 18 7c f6 aa  25 5e 7a 64 31 66 00 ba  |..J..|..%^zd1f..|
-00000130  f4 8e 92 af c7 6b d8 76  d4 f3 5f 41 cb 6e 56 15  |.....k.v.._A.nV.|
-00000140  97 1b 97 c1 3c 12 39 21  66 3d 2b 16 d1 bc db 1c  |....<.9!f=+.....|
-00000150  c0 a7 da b7 ca ad ba da  cb d5 21 50 ec de 8d ab  |..........!P....|
-00000160  d1 6b 81 4b 89 02 f3 c4  be c1 6c 89 b1 44 84 bd  |.k.K......l..D..|
-00000170  21 d1 04 7d 9d 16 4d f9  82 15 f6 ef fa d6 09 47  |!..}..M........G|
-00000180  f2 fb 02 03 01 00 01 a3  81 93 30 81 90 30 0e 06  |..........0..0..|
-00000190  03 55 1d 0f 01 01 ff 04  04 03 02 05 a0 30 1d 06  |.U...........0..|
-000001a0  03 55 1d 25 04 16 30 14  06 08 2b 06 01 05 05 07  |.U.%..0...+.....|
-000001b0  03 01 06 08 2b 06 01 05  05 07 03 02 30 0c 06 03  |....+.......0...|
-000001c0  55 1d 13 01 01 ff 04 02  30 00 30 19 06 03 55 1d  |U.......0.0...U.|
-000001d0  0e 04 12 04 10 12 50 8d  89 6f 1b d1 dc 54 4d 6e  |......P..o...TMn|
-000001e0  cb 69 5e 06 f4 30 1b 06  03 55 1d 23 04 14 30 12  |.i^..0...U.#..0.|
-000001f0  80 10 bf 3d b6 a9 66 f2  b8 40 cf ea b4 03 78 48  |...=..f.. at ....xH|
-00000200  1a 41 30 19 06 03 55 1d  11 04 12 30 10 82 0e 65  |.A0...U....0...e|
-00000210  78 61 6d 70 6c 65 2e 67  6f 6c 61 6e 67 30 0d 06  |xample.golang0..|
-00000220  09 2a 86 48 86 f7 0d 01  01 0b 05 00 03 81 81 00  |.*.H............|
-00000230  92 7c af 91 55 12 18 96  59 31 a6 48 40 d5 2d d5  |.|..U...Y1.H at .-.|
-00000240  ee bb 02 a0 f5 c2 1e 7c  9b b3 30 7d 3c dc 76 da  |.......|..0}<.v.|
-00000250  4f 3d c0 fa ae 2d 33 24  6b 03 7b 1b 67 59 11 21  |O=...-3$k.{.gY.!|
-00000260  b5 11 bc 77 b9 d9 e0 6e  a8 2d 2e 35 fa 64 5f 22  |...w...n.-.5.d_"|
-00000270  3e 63 10 6b be ff 14 86  6d 0d f0 15 31 a8 14 38  |>c.k....m...1..8|
-00000280  1e 3b 84 87 2c cb 98 ed  51 76 b9 b1 4f dd db 9b  |.;..,...Qv..O...|
-00000290  84 04 86 40 fa 51 dd ba  b4 8d eb e3 46 de 46 b9  |... at .Q......F.F.|
-000002a0  4f 86 c7 f9 a4 c2 41 34  ac cc f6 ea b0 ab 39 18  |O.....A4......9.|
-000002b0  16 03 03 00 04 0e 00 00  00                       |.........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
+00000030  09 00 23 00 00 ff 01 00  01 00 16 03 03 02 59 0b  |..#...........Y.|
+00000040  00 02 55 00 02 52 00 02  4f 30 82 02 4b 30 82 01  |..U..R..O0..K0..|
+00000050  b4 a0 03 02 01 02 02 09  00 e8 f0 9d 3f e2 5b ea  |............?.[.|
+00000060  a6 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |.0...*.H........|
+00000070  30 1f 31 0b 30 09 06 03  55 04 0a 13 02 47 6f 31  |0.1.0...U....Go1|
+00000080  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
+00000090  74 30 1e 17 0d 31 36 30  31 30 31 30 30 30 30 30  |t0...16010100000|
+000000a0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
+000000b0  5a 30 1a 31 0b 30 09 06  03 55 04 0a 13 02 47 6f  |Z0.1.0...U....Go|
+000000c0  31 0b 30 09 06 03 55 04  03 13 02 47 6f 30 81 9f  |1.0...U....Go0..|
+000000d0  30 0d 06 09 2a 86 48 86  f7 0d 01 01 01 05 00 03  |0...*.H.........|
+000000e0  81 8d 00 30 81 89 02 81  81 00 db 46 7d 93 2e 12  |...0.......F}...|
+000000f0  27 06 48 bc 06 28 21 ab  7e c4 b6 a2 5d fe 1e 52  |'.H..(!.~...]..R|
+00000100  45 88 7a 36 47 a5 08 0d  92 42 5b c2 81 c0 be 97  |E.z6G....B[.....|
+00000110  79 98 40 fb 4f 6d 14 fd  2b 13 8b c2 a5 2e 67 d8  |y. at .Om..+.....g.|
+00000120  d4 09 9e d6 22 38 b7 4a  0b 74 73 2b c2 34 f1 d1  |...."8.J.ts+.4..|
+00000130  93 e5 96 d9 74 7b f3 58  9f 6c 61 3c c0 b0 41 d4  |....t{.X.la<..A.|
+00000140  d9 2b 2b 24 23 77 5b 1c  3b bd 75 5d ce 20 54 cf  |.++$#w[.;.u]. T.|
+00000150  a1 63 87 1d 1e 24 c4 f3  1d 1a 50 8b aa b6 14 43  |.c...$....P....C|
+00000160  ed 97 a7 75 62 f4 14 c8  52 d7 02 03 01 00 01 a3  |...ub...R.......|
+00000170  81 93 30 81 90 30 0e 06  03 55 1d 0f 01 01 ff 04  |..0..0...U......|
+00000180  04 03 02 05 a0 30 1d 06  03 55 1d 25 04 16 30 14  |.....0...U.%..0.|
+00000190  06 08 2b 06 01 05 05 07  03 01 06 08 2b 06 01 05  |..+.........+...|
+000001a0  05 07 03 02 30 0c 06 03  55 1d 13 01 01 ff 04 02  |....0...U.......|
+000001b0  30 00 30 19 06 03 55 1d  0e 04 12 04 10 9f 91 16  |0.0...U.........|
+000001c0  1f 43 43 3e 49 a6 de 6d  b6 80 d7 9f 60 30 1b 06  |.CC>I..m....`0..|
+000001d0  03 55 1d 23 04 14 30 12  80 10 48 13 49 4d 13 7e  |.U.#..0...H.IM.~|
+000001e0  16 31 bb a3 01 d5 ac ab  6e 7b 30 19 06 03 55 1d  |.1......n{0...U.|
+000001f0  11 04 12 30 10 82 0e 65  78 61 6d 70 6c 65 2e 67  |...0...example.g|
+00000200  6f 6c 61 6e 67 30 0d 06  09 2a 86 48 86 f7 0d 01  |olang0...*.H....|
+00000210  01 0b 05 00 03 81 81 00  9d 30 cc 40 2b 5b 50 a0  |.........0. at +[P.|
+00000220  61 cb ba e5 53 58 e1 ed  83 28 a9 58 1a a9 38 a4  |a...SX...(.X..8.|
+00000230  95 a1 ac 31 5a 1a 84 66  3d 43 d3 2d d9 0b f2 97  |...1Z..f=C.-....|
+00000240  df d3 20 64 38 92 24 3a  00 bc cf 9c 7d b7 40 20  |.. d8.$:....}.@ |
+00000250  01 5f aa d3 16 61 09 a2  76 fd 13 c3 cc e1 0c 5c  |._...a..v......\|
+00000260  ee b1 87 82 f1 6c 04 ed  73 bb b3 43 77 8d 0c 1c  |.....l..s..Cw...|
+00000270  f1 0f a1 d8 40 83 61 c9  4c 72 2b 9d ae db 46 06  |.... at .a.Lr+...F.|
+00000280  06 4d f4 c1 b3 3e c0 d1  bd 42 d4 db fe 3d 13 60  |.M...>...B...=.`|
+00000290  84 5c 21 d3 3b e9 fa e7  16 03 03 00 04 0e 00 00  |.\!.;...........|
+000002a0  00                                                |.|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 5d f3 3b c6 24  |...........].;.$|
-00000010  34 17 eb e1 6c de fa cd  ed 6f 42 74 01 5f 4b 22  |4...l....oBt._K"|
-00000020  9e 79 da 68 9f e9 f8 af  84 6b b7 38 52 f3 5e a1  |.y.h.....k.8R.^.|
-00000030  e2 aa d1 48 15 1e 39 6e  18 59 3e dc 57 4a fb b1  |...H..9n.Y>.WJ..|
-00000040  18 18 40 ae 84 da d8 76  50 65 3b a5 d9 7a 72 b1  |.. at ....vPe;..zr.|
-00000050  51 07 65 08 0e 1d 05 f5  47 a8 7d 79 89 1e fe 00  |Q.e.....G.}y....|
-00000060  89 af 01 7f 4d 0c 11 d7  02 cf 88 7b be 03 c5 65  |....M......{...e|
-00000070  44 77 32 56 5c da 01 53  d1 dd d9 b4 5f 42 85 da  |Dw2V\..S...._B..|
-00000080  82 0b 95 59 45 a3 7a 48  d4 00 22 14 03 03 00 01  |...YE.zH..".....|
-00000090  01 16 03 03 00 24 dd 06  a2 4b a0 8e 8b 31 f2 26  |.....$...K...1.&|
-000000a0  b2 6f d4 5d ff 34 eb 31  42 16 e7 c2 26 3d f7 16  |.o.].4.1B...&=..|
-000000b0  ed bd 41 4b 6f d4 03 fb  b7 83                    |..AKo.....|
+00000000  16 03 03 00 86 10 00 00  82 00 80 8f f0 5a 2f 01  |.............Z/.|
+00000010  99 79 e6 f2 a0 31 a4 02  d8 c0 1e 70 e8 67 58 bd  |.y...1.....p.gX.|
+00000020  a0 2a 37 3a 3c 2d 45 53  e7 d2 7d 94 16 ea 10 5c  |.*7:<-ES..}....\|
+00000030  07 91 36 87 ab f6 d1 7a  c7 40 a7 7f 23 1b ef 33  |..6....z. at ..#..3|
+00000040  80 ea 7d 75 d3 62 de 7d  d2 6b cf 90 54 0f e7 02  |..}u.b.}.k..T...|
+00000050  03 85 ef 38 f4 e9 88 8f  e4 7c 8c ac 95 e6 88 f4  |...8.....|......|
+00000060  05 f7 c7 89 4a 64 de 34  5f 09 c2 84 19 36 c1 42  |....Jd.4_....6.B|
+00000070  ea 03 69 38 7e 32 10 8a  b5 cf c7 2f 8e c6 5f 29  |..i8~2...../.._)|
+00000080  4e 8a 8e d4 17 6c 9c 18  7b ea df 14 03 03 00 01  |N....l..{.......|
+00000090  01 16 03 03 00 40 5f 50  47 5a 97 52 9d 11 b5 db  |..... at _PGZ.R....|
+000000a0  ab 7b b9 e3 74 52 c5 cd  f4 73 18 cf 12 c4 fe 07  |.{..tR...s......|
+000000b0  88 5f a9 18 7a 12 23 67  ec 72 07 9f 19 b5 bf 52  |._..z.#g.r.....R|
+000000c0  2f dd 26 66 25 98 8c 5a  07 0f 26 c1 b0 38 6c 01  |/.&f%..Z..&..8l.|
+000000d0  e4 f4 ee dd b3 72                                 |.....r|
 >>> Flow 4 (server to client)
 00000000  16 03 03 00 82 04 00 00  7e 00 00 00 00 00 78 50  |........~.....xP|
 00000010  46 ad c1 db a8 38 86 7b  2b bb fd d0 c3 42 3e 00  |F....8.{+....B>.|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 94  |................|
-00000030  6f 2c b5 83 61 4d 51 5f  33 46 48 fe 9e e9 83 f4  |o,..aMQ_3FH.....|
-00000040  b1 aa c9 b1 61 2a b2 9d  0f 17 c4 09 f6 26 7a 75  |....a*.......&zu|
-00000050  f1 19 99 db 36 d8 32 f0  94 61 4f 8f ed 80 33 51  |....6.2..aO...3Q|
-00000060  f3 c6 15 84 6b 33 94 c8  4f 84 b7 7c 27 6d 8f fe  |....k3..O..|'m..|
-00000070  41 8e b2 92 ca 80 e8 6c  ba 75 77 f5 3a 87 17 ae  |A......l.uw.:...|
-00000080  f8 b9 6b 10 f9 85 da 14  03 03 00 01 01 16 03 03  |..k.............|
-00000090  00 24 70 bd b9 24 02 ce  69 8a 07 c7 c8 7e cf b7  |.$p..$..i....~..|
-000000a0  4e 2b e2 dc 47 fc f7 3a  c8 2d ab a0 9a ed 27 d9  |N+..G..:.-....'.|
-000000b0  71 ea 45 29 d6 25 17 03  03 00 21 d9 28 ee 99 04  |q.E).%....!.(...|
-000000c0  35 ff ca 3d 30 3f 76 fb  08 1a 56 73 f5 72 c3 fa  |5..=0?v...Vs.r..|
-000000d0  cd 9e 3c 1b 3f 43 4d 56  92 38 9e a6 15 03 03 00  |..<.?CMV.8......|
-000000e0  16 6f 55 57 7b 81 6f 7d  fa 90 76 0b 5b 6d 95 35  |.oUW{.o}..v.[m.5|
-000000f0  39 9f a8 c9 dc b7 80                              |9......|
+00000030  6f 2c 9f 83 61 0b b1 b7  9e 10 2d 0c 56 e8 70 66  |o,..a.....-.V.pf|
+00000040  ad de b1 15 74 2f 8b 08  8c 96 bb 4b 1b 4e dd 81  |....t/.....K.N..|
+00000050  0e bf 84 4d 43 8f c0 7e  a0 7f be c0 59 bf 83 26  |...MC..~....Y..&|
+00000060  0f a2 22 52 2c 33 94 5a  77 54 f3 b5 f2 22 51 d5  |.."R,3.ZwT..."Q.|
+00000070  24 c2 60 c3 2e 0f 9c 5e  33 3b e8 7c 52 2a 76 08  |$.`....^3;.|R*v.|
+00000080  58 ac 47 98 bc 36 b6 14  03 03 00 01 01 16 03 03  |X.G..6..........|
+00000090  00 40 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |. at ..............|
+000000a0  00 00 31 fa c3 6c 95 c0  86 a5 55 30 41 c3 2d 6b  |..1..l....U0A.-k|
+000000b0  a5 00 0b af 33 63 de 80  01 3d 7a 38 8e a7 f4 b1  |....3c...=z8....|
+000000c0  2d bb e3 1d 1a b4 61 18  b5 d9 d1 7f d1 9a e7 e8  |-.....a.........|
+000000d0  49 ee 17 03 03 00 40 00  00 00 00 00 00 00 00 00  |I..... at .........|
+000000e0  00 00 00 00 00 00 00 a6  d5 e4 a8 9b d3 7d 72 1c  |.............}r.|
+000000f0  ff 14 03 68 34 c9 ca 0d  2e 80 a1 09 f7 92 f6 86  |...h4...........|
+00000100  44 22 e8 1c ea e9 dd cc  a7 92 9a 72 ec 22 5b 82  |D".........r."[.|
+00000110  7b 43 02 f7 fa 59 7b 15  03 03 00 30 00 00 00 00  |{C...Y{....0....|
+00000120  00 00 00 00 00 00 00 00  00 00 00 00 5f ab 03 1d  |............_...|
+00000130  08 72 07 6d 78 66 5b 18  ec 3a b7 ea 75 96 ce 95  |.r.mxf[..:..u...|
+00000140  0c c9 6f 86 91 14 30 d6  2e 5d b1 b4              |..o...0..]..|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable b/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable
index b5eb6c7..467e332 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable
+++ b/src/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable
@@ -1,83 +1,89 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 5f 01 00 00  5b 03 03 54 25 f9 0f b8  |...._...[..T%...|
-00000010  2d 52 a0 17 b6 62 1c 60  38 31 30 67 f1 55 9c c8  |-R...b.`810g.U..|
-00000020  d3 74 65 bf cd 34 fb 6f  f2 60 7c 00 00 04 00 05  |.te..4.o.`|.....|
-00000030  00 ff 02 01 00 00 2d 00  23 00 00 00 0d 00 20 00  |......-.#..... .|
-00000040  1e 06 01 06 02 06 03 05  01 05 02 05 03 04 01 04  |................|
-00000050  02 04 03 03 01 03 02 03  03 02 01 02 02 02 03 00  |................|
-00000060  0f 00 01 01                                       |....|
+00000000  16 03 01 00 61 01 00 00  5d 03 03 91 2f b7 db 1e  |....a...].../...|
+00000010  41 ac c6 17 1d 0f 0c 8e  86 15 e0 de e9 c8 6b f5  |A.............k.|
+00000020  69 c7 bf ad ff 63 58 2b  b1 79 a6 00 00 04 00 2f  |i....cX+.y...../|
+00000030  00 ff 01 00 00 30 00 23  00 00 00 0d 00 20 00 1e  |.....0.#..... ..|
+00000040  06 01 06 02 06 03 05 01  05 02 05 03 04 01 04 02  |................|
+00000050  04 03 03 01 03 02 03 03  02 01 02 02 02 03 00 16  |................|
+00000060  00 00 00 17 00 00                                 |......|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 35 02 00 00  31 03 03 00 00 00 00 00  |....5...1.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 05 00 00  |................|
-00000030  09 00 23 00 00 ff 01 00  01 00 16 03 03 02 71 0b  |..#...........q.|
-00000040  00 02 6d 00 02 6a 00 02  67 30 82 02 63 30 82 01  |..m..j..g0..c0..|
-00000050  cc a0 03 02 01 02 02 09  00 a2 73 00 0c 81 00 cb  |..........s.....|
-00000060  f3 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |.0...*.H........|
-00000070  30 2b 31 17 30 15 06 03  55 04 0a 13 0e 47 6f 6f  |0+1.0...U....Goo|
-00000080  67 6c 65 20 54 45 53 54  49 4e 47 31 10 30 0e 06  |gle TESTING1.0..|
-00000090  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
-000000a0  0d 31 35 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.150101000000Z..|
-000000b0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 26 31  |250101000000Z0&1|
-000000c0  17 30 15 06 03 55 04 0a  13 0e 47 6f 6f 67 6c 65  |.0...U....Google|
-000000d0  20 54 45 53 54 49 4e 47  31 0b 30 09 06 03 55 04  | TESTING1.0...U.|
-000000e0  03 13 02 47 6f 30 81 9f  30 0d 06 09 2a 86 48 86  |...Go0..0...*.H.|
-000000f0  f7 0d 01 01 01 05 00 03  81 8d 00 30 81 89 02 81  |...........0....|
-00000100  81 00 af 87 88 f6 20 1b  95 65 6c 14 ab 44 05 af  |...... ..el..D..|
-00000110  3b 45 14 e3 b7 6d fd 00  63 4d 95 7f fe 6a 62 35  |;E...m..cM...jb5|
-00000120  86 c0 4a f9 18 7c f6 aa  25 5e 7a 64 31 66 00 ba  |..J..|..%^zd1f..|
-00000130  f4 8e 92 af c7 6b d8 76  d4 f3 5f 41 cb 6e 56 15  |.....k.v.._A.nV.|
-00000140  97 1b 97 c1 3c 12 39 21  66 3d 2b 16 d1 bc db 1c  |....<.9!f=+.....|
-00000150  c0 a7 da b7 ca ad ba da  cb d5 21 50 ec de 8d ab  |..........!P....|
-00000160  d1 6b 81 4b 89 02 f3 c4  be c1 6c 89 b1 44 84 bd  |.k.K......l..D..|
-00000170  21 d1 04 7d 9d 16 4d f9  82 15 f6 ef fa d6 09 47  |!..}..M........G|
-00000180  f2 fb 02 03 01 00 01 a3  81 93 30 81 90 30 0e 06  |..........0..0..|
-00000190  03 55 1d 0f 01 01 ff 04  04 03 02 05 a0 30 1d 06  |.U...........0..|
-000001a0  03 55 1d 25 04 16 30 14  06 08 2b 06 01 05 05 07  |.U.%..0...+.....|
-000001b0  03 01 06 08 2b 06 01 05  05 07 03 02 30 0c 06 03  |....+.......0...|
-000001c0  55 1d 13 01 01 ff 04 02  30 00 30 19 06 03 55 1d  |U.......0.0...U.|
-000001d0  0e 04 12 04 10 12 50 8d  89 6f 1b d1 dc 54 4d 6e  |......P..o...TMn|
-000001e0  cb 69 5e 06 f4 30 1b 06  03 55 1d 23 04 14 30 12  |.i^..0...U.#..0.|
-000001f0  80 10 bf 3d b6 a9 66 f2  b8 40 cf ea b4 03 78 48  |...=..f.. at ....xH|
-00000200  1a 41 30 19 06 03 55 1d  11 04 12 30 10 82 0e 65  |.A0...U....0...e|
-00000210  78 61 6d 70 6c 65 2e 67  6f 6c 61 6e 67 30 0d 06  |xample.golang0..|
-00000220  09 2a 86 48 86 f7 0d 01  01 0b 05 00 03 81 81 00  |.*.H............|
-00000230  92 7c af 91 55 12 18 96  59 31 a6 48 40 d5 2d d5  |.|..U...Y1.H at .-.|
-00000240  ee bb 02 a0 f5 c2 1e 7c  9b b3 30 7d 3c dc 76 da  |.......|..0}<.v.|
-00000250  4f 3d c0 fa ae 2d 33 24  6b 03 7b 1b 67 59 11 21  |O=...-3$k.{.gY.!|
-00000260  b5 11 bc 77 b9 d9 e0 6e  a8 2d 2e 35 fa 64 5f 22  |...w...n.-.5.d_"|
-00000270  3e 63 10 6b be ff 14 86  6d 0d f0 15 31 a8 14 38  |>c.k....m...1..8|
-00000280  1e 3b 84 87 2c cb 98 ed  51 76 b9 b1 4f dd db 9b  |.;..,...Qv..O...|
-00000290  84 04 86 40 fa 51 dd ba  b4 8d eb e3 46 de 46 b9  |... at .Q......F.F.|
-000002a0  4f 86 c7 f9 a4 c2 41 34  ac cc f6 ea b0 ab 39 18  |O.....A4......9.|
-000002b0  16 03 03 00 04 0e 00 00  00                       |.........|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
+00000030  09 00 23 00 00 ff 01 00  01 00 16 03 03 02 59 0b  |..#...........Y.|
+00000040  00 02 55 00 02 52 00 02  4f 30 82 02 4b 30 82 01  |..U..R..O0..K0..|
+00000050  b4 a0 03 02 01 02 02 09  00 e8 f0 9d 3f e2 5b ea  |............?.[.|
+00000060  a6 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |.0...*.H........|
+00000070  30 1f 31 0b 30 09 06 03  55 04 0a 13 02 47 6f 31  |0.1.0...U....Go1|
+00000080  10 30 0e 06 03 55 04 03  13 07 47 6f 20 52 6f 6f  |.0...U....Go Roo|
+00000090  74 30 1e 17 0d 31 36 30  31 30 31 30 30 30 30 30  |t0...16010100000|
+000000a0  30 5a 17 0d 32 35 30 31  30 31 30 30 30 30 30 30  |0Z..250101000000|
+000000b0  5a 30 1a 31 0b 30 09 06  03 55 04 0a 13 02 47 6f  |Z0.1.0...U....Go|
+000000c0  31 0b 30 09 06 03 55 04  03 13 02 47 6f 30 81 9f  |1.0...U....Go0..|
+000000d0  30 0d 06 09 2a 86 48 86  f7 0d 01 01 01 05 00 03  |0...*.H.........|
+000000e0  81 8d 00 30 81 89 02 81  81 00 db 46 7d 93 2e 12  |...0.......F}...|
+000000f0  27 06 48 bc 06 28 21 ab  7e c4 b6 a2 5d fe 1e 52  |'.H..(!.~...]..R|
+00000100  45 88 7a 36 47 a5 08 0d  92 42 5b c2 81 c0 be 97  |E.z6G....B[.....|
+00000110  79 98 40 fb 4f 6d 14 fd  2b 13 8b c2 a5 2e 67 d8  |y. at .Om..+.....g.|
+00000120  d4 09 9e d6 22 38 b7 4a  0b 74 73 2b c2 34 f1 d1  |...."8.J.ts+.4..|
+00000130  93 e5 96 d9 74 7b f3 58  9f 6c 61 3c c0 b0 41 d4  |....t{.X.la<..A.|
+00000140  d9 2b 2b 24 23 77 5b 1c  3b bd 75 5d ce 20 54 cf  |.++$#w[.;.u]. T.|
+00000150  a1 63 87 1d 1e 24 c4 f3  1d 1a 50 8b aa b6 14 43  |.c...$....P....C|
+00000160  ed 97 a7 75 62 f4 14 c8  52 d7 02 03 01 00 01 a3  |...ub...R.......|
+00000170  81 93 30 81 90 30 0e 06  03 55 1d 0f 01 01 ff 04  |..0..0...U......|
+00000180  04 03 02 05 a0 30 1d 06  03 55 1d 25 04 16 30 14  |.....0...U.%..0.|
+00000190  06 08 2b 06 01 05 05 07  03 01 06 08 2b 06 01 05  |..+.........+...|
+000001a0  05 07 03 02 30 0c 06 03  55 1d 13 01 01 ff 04 02  |....0...U.......|
+000001b0  30 00 30 19 06 03 55 1d  0e 04 12 04 10 9f 91 16  |0.0...U.........|
+000001c0  1f 43 43 3e 49 a6 de 6d  b6 80 d7 9f 60 30 1b 06  |.CC>I..m....`0..|
+000001d0  03 55 1d 23 04 14 30 12  80 10 48 13 49 4d 13 7e  |.U.#..0...H.IM.~|
+000001e0  16 31 bb a3 01 d5 ac ab  6e 7b 30 19 06 03 55 1d  |.1......n{0...U.|
+000001f0  11 04 12 30 10 82 0e 65  78 61 6d 70 6c 65 2e 67  |...0...example.g|
+00000200  6f 6c 61 6e 67 30 0d 06  09 2a 86 48 86 f7 0d 01  |olang0...*.H....|
+00000210  01 0b 05 00 03 81 81 00  9d 30 cc 40 2b 5b 50 a0  |.........0. at +[P.|
+00000220  61 cb ba e5 53 58 e1 ed  83 28 a9 58 1a a9 38 a4  |a...SX...(.X..8.|
+00000230  95 a1 ac 31 5a 1a 84 66  3d 43 d3 2d d9 0b f2 97  |...1Z..f=C.-....|
+00000240  df d3 20 64 38 92 24 3a  00 bc cf 9c 7d b7 40 20  |.. d8.$:....}.@ |
+00000250  01 5f aa d3 16 61 09 a2  76 fd 13 c3 cc e1 0c 5c  |._...a..v......\|
+00000260  ee b1 87 82 f1 6c 04 ed  73 bb b3 43 77 8d 0c 1c  |.....l..s..Cw...|
+00000270  f1 0f a1 d8 40 83 61 c9  4c 72 2b 9d ae db 46 06  |.... at .a.Lr+...F.|
+00000280  06 4d f4 c1 b3 3e c0 d1  bd 42 d4 db fe 3d 13 60  |.M...>...B...=.`|
+00000290  84 5c 21 d3 3b e9 fa e7  16 03 03 00 04 0e 00 00  |.\!.;...........|
+000002a0  00                                                |.|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 19 db c8 25 14  |..............%.|
-00000010  e0 7b 6e 87 7b 59 2d 85  8b 47 ce 31 d7 3a 53 06  |.{n.{Y-..G.1.:S.|
-00000020  ff cf 89 ae 45 fd 59 d2  50 c2 31 33 48 81 a8 d7  |....E.Y.P.13H...|
-00000030  47 36 b9 bd 8d f3 f9 f8  c2 6d 6a 8a 6b c4 e5 53  |G6.......mj.k..S|
-00000040  24 52 40 66 49 a9 56 74  4c 94 bc 85 5b 79 5a e1  |$R at fI.VtL...[yZ.|
-00000050  66 3c 42 d8 ca e1 3f c5  36 b8 b5 8c b2 ea 87 68  |f<B...?.6......h|
-00000060  70 eb e3 da 27 fe ed f5  d0 4a c7 fe 46 0b 0f 29  |p...'....J..F..)|
-00000070  19 41 ef dd a9 85 8a 67  02 41 04 30 20 07 09 55  |.A.....g.A.0 ..U|
-00000080  ff 92 44 f1 59 49 39 dd  fa d7 a0 14 03 03 00 01  |..D.YI9.........|
-00000090  01 16 03 03 00 24 82 b5  7b d1 7c 03 93 88 fd 97  |.....$..{.|.....|
-000000a0  54 b7 ff 39 a7 11 c3 cd  53 f3 1c 6c ed ab b6 a0  |T..9....S..l....|
-000000b0  1c b9 89 f0 1a f8 5f 15  7f 85                    |......_...|
+00000000  16 03 03 00 86 10 00 00  82 00 80 c5 0c 17 b1 b2  |................|
+00000010  65 0b b7 7b 45 6f cb 7d  b4 9c 5c 82 3a 1a 75 11  |e..{Eo.}..\.:.u.|
+00000020  22 6f 41 3a 81 e2 81 2e  74 f8 70 61 fd e1 7c ce  |"oA:....t.pa..|.|
+00000030  bf 06 d7 29 77 07 b3 9d  cc 33 25 53 17 12 43 ae  |...)w....3%S..C.|
+00000040  4f df ad a4 3e 49 6e 97  50 b6 23 d0 fa 3d a6 bc  |O...>In.P.#..=..|
+00000050  38 d8 5f 2b 45 a7 d0 aa  cd b1 39 03 8f 62 9e 46  |8._+E.....9..b.F|
+00000060  50 d4 83 1d b8 76 41 29  d4 40 9a 65 41 8d 1c f0  |P....vA). at .eA...|
+00000070  d4 4d 88 d2 5e 42 ec c8  86 d6 fd df 65 d8 f1 82  |.M..^B......e...|
+00000080  8f 6a 80 31 1a 0e fc 13  2b 90 a8 14 03 03 00 01  |.j.1....+.......|
+00000090  01 16 03 03 00 40 50 ad  ed 91 c4 6a ed f8 aa 06  |..... at P....j....|
+000000a0  9e 13 03 38 bf 83 ef 4b  8e d5 89 d4 a3 f8 d9 8d  |...8...K........|
+000000b0  bb 88 72 a6 16 f6 5d d5  ca 55 bb e4 76 47 08 35  |..r...]..U..vG.5|
+000000c0  b9 fb 92 a4 0a b9 36 d7  62 44 81 e8 cf db ad 9a  |......6.bD......|
+000000d0  6d 72 c0 af 70 bd                                 |mr..p.|
 >>> Flow 4 (server to client)
 00000000  16 03 03 00 82 04 00 00  7e 00 00 00 00 00 78 50  |........~.....xP|
 00000010  46 ad c1 db a8 38 86 7b  2b bb fd d0 c3 42 3e 00  |F....8.{+....B>.|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 94  |................|
-00000030  6f 2c b5 83 61 01 55 65  2a 95 38 d4 d5 5f 41 c1  |o,..a.Ue*.8.._A.|
-00000040  45 e4 f8 4b 3b 08 44 df  0b 72 11 93 cd d4 ff 36  |E..K;.D..r.....6|
-00000050  0f 4f 3a a9 4c 9f ab c7  ae 88 97 bc 1e ff 2d 27  |.O:.L.........-'|
-00000060  39 a8 82 84 ae 33 94 48  8b 1c 58 9d 60 65 3c 3f  |9....3.H..X.`e<?|
-00000070  17 9d 6a eb 50 cd 65 04  bb c7 28 c8 0d 57 44 52  |..j.P.e...(..WDR|
-00000080  e0 17 de df f3 13 b1 14  03 03 00 01 01 16 03 03  |................|
-00000090  00 24 5a 41 90 0a eb d9  6b 02 68 3d 98 12 1d fa  |.$ZA....k.h=....|
-000000a0  46 7d 73 ea 8e 49 72 a5  2f 04 40 5c 7d 03 c7 3a  |F}s..Ir./.@\}..:|
-000000b0  6e 50 7c 87 bb 13 17 03  03 00 21 46 da ec ad 52  |nP|.......!F...R|
-000000c0  ea 5a 01 89 15 77 79 af  86 02 b5 89 c8 97 dc f7  |.Z...wy.........|
-000000d0  ac 73 09 87 7a 61 57 d6  9b 17 10 af 15 03 03 00  |.s..zaW.........|
-000000e0  16 bb 20 22 ad 6e 65 66  8c d6 07 e3 82 5f ac 1e  |.. ".nef....._..|
-000000f0  ec 54 72 eb 2d c5 af                              |.Tr.-..|
+00000030  6f 2c 9f 83 61 2e fe 48  fe f6 bb 98 a0 6f b0 be  |o,..a..H.....o..|
+00000040  9e 86 d7 b2 f2 67 c7 44  c7 3d e4 2b de d0 f4 d2  |.....g.D.=.+....|
+00000050  17 51 84 8e 7a a7 80 c4  65 14 f7 49 09 68 15 56  |.Q..z...e..I.h.V|
+00000060  68 32 41 d1 6f 33 94 a1  3a c9 37 20 5d e6 b0 6f  |h2A.o3..:.7 ]..o|
+00000070  37 0a 10 e3 28 e1 34 b6  6d e6 7a 44 24 7f 2f cf  |7...(.4.m.zD$./.|
+00000080  1b ae dd 4c d0 11 75 14  03 03 00 01 01 16 03 03  |...L..u.........|
+00000090  00 40 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |. at ..............|
+000000a0  00 00 7e 4a 31 e8 7d c6  eb 34 56 3b 62 0c 11 a2  |..~J1.}..4V;b...|
+000000b0  f0 bd 9b 9a 4c c9 39 2d  ed 21 dd 0c 72 3a 92 e1  |....L.9-.!..r:..|
+000000c0  0f b3 7f 71 c5 cf 2a 6f  68 bc 8e 84 7e d5 10 2e  |...q..*oh...~...|
+000000d0  c3 d4 17 03 03 00 40 00  00 00 00 00 00 00 00 00  |...... at .........|
+000000e0  00 00 00 00 00 00 00 43  76 cc 74 b3 1c 89 c0 6b  |.......Cv.t....k|
+000000f0  96 f7 2c 84 c1 0a 6e d6  7f b4 76 76 2c 2f 74 6a  |..,...n...vv,/tj|
+00000100  c7 4e 18 69 1c 97 cd ca  f2 7a 33 01 3e 6f bb 54  |.N.i.....z3.>o.T|
+00000110  49 4e 8e 1d f4 13 74 15  03 03 00 30 00 00 00 00  |IN....t....0....|
+00000120  00 00 00 00 00 00 00 00  00 00 00 00 2d 70 b1 13  |............-p..|
+00000130  a9 e3 72 ca 05 8e 8d b7  f4 97 de 58 46 aa 2a 9c  |..r........XF.*.|
+00000140  2f 8c 3e 59 7b 64 e5 51  61 7f a6 39              |/.>Y{d.Qa..9|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-RSA-3DES b/src/crypto/tls/testdata/Server-TLSv12-RSA-3DES
index 23b6c5b..af50381 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-RSA-3DES
+++ b/src/crypto/tls/testdata/Server-TLSv12-RSA-3DES
@@ -1,77 +1,76 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 5b 01 00 00  57 03 03 1e c8 d0 4c b2  |....[...W.....L.|
-00000010  8d 37 e1 88 c7 0f e0 6a  21 3c 5e 8a bf fa 97 1f  |.7.....j!<^.....|
-00000020  5b 28 bc 6d 47 32 0a 6b  f7 11 f5 00 00 04 00 0a  |[(.mG2.k........|
-00000030  00 ff 02 01 00 00 29 00  0d 00 20 00 1e 06 01 06  |......)... .....|
-00000040  02 06 03 05 01 05 02 05  03 04 01 04 02 04 03 03  |................|
-00000050  01 03 02 03 03 02 01 02  02 02 03 00 0f 00 01 01  |................|
+00000000  16 03 01 00 5d 01 00 00  59 03 03 0c fb 72 82 e5  |....]...Y....r..|
+00000010  9a 04 90 c8 0d 73 25 9a  3f 88 e3 48 71 a2 33 3e  |.....s%.?..Hq.3>|
+00000020  90 32 74 bc 12 38 d6 3a  d3 11 1d 00 00 04 00 0a  |.2t..8.:........|
+00000030  00 ff 01 00 00 2c 00 0d  00 20 00 1e 06 01 06 02  |.....,... ......|
+00000040  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000050  03 02 03 03 02 01 02 02  02 03 00 16 00 00 00 17  |................|
+00000060  00 00                                             |..|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 0a 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 4d c2 e0 9b 40  |...........M...@|
-00000010  44 52 aa 55 06 71 0b bc  17 89 3a 94 d8 d0 1d ed  |DR.U.q....:.....|
-00000020  70 d3 21 30 1b be 97 e0  72 30 60 05 de 9a a9 dd  |p.!0....r0`.....|
-00000030  8c 0c 81 78 3a 15 9c 1c  c6 22 81 0a 10 57 d1 9a  |...x:...."...W..|
-00000040  17 5c 74 9e 58 79 4b f1  70 d9 d9 21 d8 79 64 fa  |.\t.XyK.p..!.yd.|
-00000050  aa a5 e6 93 2a 16 57 23  a7 17 fb 71 b6 c2 d3 5b  |....*.W#...q...[|
-00000060  3d 22 50 16 47 17 5f 15  e8 f1 30 da 10 69 84 25  |="P.G._...0..i.%|
-00000070  05 d0 b5 f0 e8 69 72 4e  93 d3 7c 1a 01 6d 37 fb  |.....irN..|..m7.|
-00000080  cf e1 af f9 da dd 71 56  9b 08 24 14 03 03 00 01  |......qV..$.....|
-00000090  01 16 03 03 00 30 53 ab  b5 09 5a 36 36 df b1 ed  |.....0S...Z66...|
-000000a0  d2 69 5c 45 0b a9 02 8f  6d 25 d4 01 da 5f 27 ab  |.i\E....m%..._'.|
-000000b0  ba 89 6e ee d8 91 24 f8  5e ca 6e 4d 51 41 88 3c  |..n...$.^.nMQA.<|
-000000c0  f8 67 b4 fb d3 cb                                 |.g....|
+00000000  16 03 03 00 86 10 00 00  82 00 80 04 90 54 41 b9  |.............TA.|
+00000010  22 12 39 d9 1d 0b b8 6c  d4 b3 8a ec 78 42 80 a5  |".9....l....xB..|
+00000020  03 c9 2a 9e 95 6f a0 28  3a 5c e9 59 28 ba 49 9b  |..*..o.(:\.Y(.I.|
+00000030  37 63 61 3f c4 ac ba 55  6b 85 a5 27 ed 37 b9 25  |7ca?...Uk..'.7.%|
+00000040  04 cf 84 ad 43 6b ab 13  fa 72 29 b8 01 d9 aa 0c  |....Ck...r).....|
+00000050  be b1 9a c4 5a 05 3d 2d  71 b4 72 f5 3a 77 fb 6b  |....Z.=-q.r.:w.k|
+00000060  45 b0 5b 00 f8 1e f9 70  7f a4 64 c9 1e 35 56 0b  |E.[....p..d..5V.|
+00000070  68 07 4c 04 95 f4 ca b1  0a b3 25 2b 93 2d be 80  |h.L.......%+.-..|
+00000080  76 15 75 07 23 ee 25 f3  1b a8 2f 14 03 03 00 01  |v.u.#.%.../.....|
+00000090  01 16 03 03 00 30 e5 cd  56 75 e6 a4 58 e5 33 cc  |.....0..Vu..X.3.|
+000000a0  95 23 e0 7f 01 f2 45 21  bb 7d 7c 17 1f 59 7c f9  |.#....E!.}|..Y|.|
+000000b0  38 05 a3 95 4d 9b f2 3f  9d 84 2c 31 15 8b 4d d4  |8...M..?..,1..M.|
+000000c0  17 3c 62 2b f6 71                                 |.<b+.q|
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 30 00 00 00 00 00  |..........0.....|
-00000010  00 00 00 50 83 52 65 2d  6e 76 aa 8d 2d 46 06 12  |...P.Re-nv..-F..|
-00000020  1a e7 25 79 28 61 9e 2d  07 0b fb 3c 77 38 d8 b0  |..%y(a.-...<w8..|
-00000030  af ca 86 8a 51 07 4d 83  39 81 9b 17 03 03 00 30  |....Q.M.9......0|
-00000040  00 00 00 00 00 00 00 00  a1 ea 74 b2 7b fc 3f 9d  |..........t.{.?.|
-00000050  bc eb 9d 09 a2 56 4a ff  d4 fd 00 23 0b e6 69 62  |.....VJ....#..ib|
-00000060  0e 4c 82 43 3f 21 8f b8  fd 5c ce 37 6c 57 d2 98  |.L.C?!...\.7lW..|
-00000070  15 03 03 00 20 00 00 00  00 00 00 00 00 dc 47 cc  |.... .........G.|
-00000080  34 eb 9e 7f d0 8f 5a 32  e6 6d 76 15 18 cc 8d 21  |4.....Z2.mv....!|
-00000090  43 91 81 31 81                                    |C..1.|
+00000010  00 00 00 b3 85 c2 1b ac  9e c2 01 f7 0f 76 6d 09  |.............vm.|
+00000020  5c 4f 9f a6 89 1b 56 e3  05 0b 7e 0d 9d 6b 36 35  |\O....V...~..k65|
+00000030  49 99 aa 4c 14 3b 69 2a  87 71 7d 17 03 03 00 30  |I..L.;i*.q}....0|
+00000040  00 00 00 00 00 00 00 00  15 65 d4 be e5 1b c9 29  |.........e.....)|
+00000050  e9 3a c4 22 72 f8 0c 40  c7 f5 45 a1 a3 c8 a8 64  |.:."r.. at ..E....d|
+00000060  22 4c 6c 79 3f 32 66 d4  05 09 a8 d4 d8 a8 f3 c7  |"Lly?2f.........|
+00000070  15 03 03 00 20 00 00 00  00 00 00 00 00 fc 8d c6  |.... ...........|
+00000080  3d b1 c4 9f 30 26 e3 b9  46 8f ce 9f 7e 5b 1e a3  |=...0&..F...~[..|
+00000090  d0 98 64 3c 0d                                    |..d<.|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-RSA-AES b/src/crypto/tls/testdata/Server-TLSv12-RSA-AES
index 66155e7..813f748 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-RSA-AES
+++ b/src/crypto/tls/testdata/Server-TLSv12-RSA-AES
@@ -1,81 +1,80 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 5b 01 00 00  57 03 03 5a ba 29 44 35  |....[...W..Z.)D5|
-00000010  c4 48 64 61 06 84 70 5c  b5 65 ad 01 9b b2 29 0d  |.Hda..p\.e....).|
-00000020  d1 46 17 3a 27 fb 92 d8  aa 21 aa 00 00 04 00 2f  |.F.:'....!...../|
-00000030  00 ff 02 01 00 00 29 00  0d 00 20 00 1e 06 01 06  |......)... .....|
-00000040  02 06 03 05 01 05 02 05  03 04 01 04 02 04 03 03  |................|
-00000050  01 03 02 03 03 02 01 02  02 02 03 00 0f 00 01 01  |................|
+00000000  16 03 01 00 5d 01 00 00  59 03 03 7a e5 86 e2 0a  |....]...Y..z....|
+00000010  53 e7 ba 32 d1 57 47 ed  45 29 1b 33 2c 58 33 8f  |S..2.WG.E).3,X3.|
+00000020  36 2c 50 6f f9 c7 3b 12  40 23 e2 00 00 04 00 2f  |6,Po..;.@#...../|
+00000030  00 ff 01 00 00 2c 00 0d  00 20 00 1e 06 01 06 02  |.....,... ......|
+00000040  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000050  03 02 03 03 02 01 02 02  02 03 00 16 00 00 00 17  |................|
+00000060  00 00                                             |..|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
-00000030  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 ad e8 09 aa 07  |................|
-00000010  c0 3c 8b 39 d2 a8 bd ca  59 eb cf 0a de 33 3e d2  |.<.9....Y....3>.|
-00000020  4f 76 1f 7a 96 50 b3 52  6b 04 9e 6f f1 06 2b 4a  |Ov.z.P.Rk..o..+J|
-00000030  7f 01 f2 51 a3 a7 1e f6  20 a7 27 4e 97 68 61 98  |...Q.... .'N.ha.|
-00000040  9f fd bd aa e8 e6 80 4d  9a 65 51 35 11 44 e4 2c  |.......M.eQ5.D.,|
-00000050  a2 47 33 d1 b6 b7 d5 40  c0 17 34 ff e2 12 8e 00  |.G3.... at ..4.....|
-00000060  41 e6 4f 3e 56 2f d9 30  6b d9 99 e3 9f ce 10 ba  |A.O>V/.0k.......|
-00000070  7c 95 3b 49 c9 5f 1e 97  37 90 e4 da 9a e0 01 5f  ||.;I._..7......_|
-00000080  b8 6f 95 19 40 0f ca 63  76 6b 2a 14 03 03 00 01  |.o.. at ..cvk*.....|
-00000090  01 16 03 03 00 40 86 cb  f5 69 1b ee 67 6e 3b be  |..... at ...i..gn;.|
-000000a0  e0 de 22 06 8c d4 f4 98  a6 45 1c 2f e5 f0 b4 25  |.."......E./...%|
-000000b0  f4 c0 87 7f e8 1c 2c 1d  20 52 50 fe dc a3 0c 22  |......,. RP...."|
-000000c0  b7 7f d3 9c 42 b8 23 d0  3e fd 93 be a2 50 28 dd  |....B.#.>....P(.|
-000000d0  79 f3 c6 90 c7 bb                                 |y.....|
+00000000  16 03 03 00 86 10 00 00  82 00 80 8f 13 d1 23 1b  |..............#.|
+00000010  8d 28 c7 a3 97 66 9f 8a  c1 13 a1 c9 3b 25 93 7a  |.(...f......;%.z|
+00000020  ea 54 58 fc 57 41 ca 92  77 99 13 01 61 e4 73 90  |.TX.WA..w...a.s.|
+00000030  c7 f1 2b 5e 5e 79 cf 69  7d 6b 3f 6e 5f 2e b0 f5  |..+^^y.i}k?n_...|
+00000040  f7 53 2b 46 15 92 6c 20  95 6b 44 6a 0a 3d 0b 56  |.S+F..l .kDj.=.V|
+00000050  66 53 ff 55 ec 38 10 cf  76 2c 0e ab 45 7a 02 6a  |fS.U.8..v,..Ez.j|
+00000060  75 07 11 80 6c d0 57 79  ed d6 4b b8 a0 04 91 a0  |u...l.Wy..K.....|
+00000070  d4 4b 76 38 9c b3 a6 2e  0c 3e 63 a8 18 15 c9 ab  |.Kv8.....>c.....|
+00000080  54 69 cd e5 6f 3c 56 a6  5f a7 e0 14 03 03 00 01  |Ti..o<V._.......|
+00000090  01 16 03 03 00 40 06 07  16 b4 7f fa 1a 8f 9b 1a  |..... at ..........|
+000000a0  a3 23 ba 5f ce ff 0a 70  b1 11 2b 84 77 7a 78 1a  |.#._...p..+.wzx.|
+000000b0  6c 32 93 6c 31 e8 e5 26  12 97 14 33 a9 77 71 19  |l2.l1..&...3.wq.|
+000000c0  0b 20 5d 8f 3c 71 0d a4  b9 94 aa ff 42 8e d3 2d  |. ].<q......B..-|
+000000d0  7e 57 3b 35 41 cc                                 |~W;5A.|
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
-00000010  00 00 00 00 00 00 00 00  00 00 00 70 e5 19 ef 25  |...........p...%|
-00000020  05 0b 02 79 2b 79 49 e6  2c ad c0 e7 03 b3 40 68  |...y+yI.,..... at h|
-00000030  67 98 31 7c 7e 85 86 a8  5c de 72 3f d1 59 12 20  |g.1|~...\.r?.Y. |
-00000040  87 95 44 57 64 35 03 f5  68 61 20 17 03 03 00 40  |..DWd5..ha ....@|
+00000010  00 00 00 00 00 00 00 00  00 00 00 51 27 cd 07 6e  |...........Q'..n|
+00000020  72 c8 17 ba e7 62 7c d0  49 55 e7 e6 c5 2c 93 39  |r....b|.IU...,.9|
+00000030  55 02 f5 fa 9a 7a 6f c5  79 6f ff 0f 4b b9 3d ad  |U....zo.yo..K.=.|
+00000040  23 c7 53 ad 13 2d d6 da  83 d0 67 17 03 03 00 40  |#.S..-....g....@|
 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000060  1b 17 7c bb 04 4f 31 7b  da 40 5e 93 64 97 4a 8d  |..|..O1{.@^.d.J.|
-00000070  98 cf 77 2d 01 53 37 53  2c 59 8f ca ac 65 ae f3  |..w-.S7S,Y...e..|
-00000080  f8 d4 ae 67 74 c8 72 21  67 51 9a 1b 71 f2 0e 04  |...gt.r!gQ..q...|
+00000060  f5 09 3b 69 c2 1f f8 03  78 1b 13 57 ca 92 96 eb  |..;i....x..W....|
+00000070  f8 71 30 09 5a 68 01 47  96 b1 5b 7d b7 57 5e 70  |.q0.Zh.G..[}.W^p|
+00000080  00 77 bb 55 32 7b d9 a5  f7 e2 a8 6d 4b d6 be c6  |.w.U2{.....mK...|
 00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-000000a0  00 00 00 00 00 fd 36 99  3d c7 44 1b 30 39 4a a7  |......6.=.D.09J.|
-000000b0  40 43 e3 01 2b 22 3d c6  8c a1 0d 73 d5 16 d2 25  |@C..+"=....s...%|
-000000c0  19 c8 d7 76 ee                                    |...v.|
+000000a0  00 00 00 00 00 58 1e a0  14 82 8d e4 c5 92 35 79  |.....X........5y|
+000000b0  3b 5e 3a fe 97 18 db 27  19 7e b5 14 8c 01 fb 6a  |;^:....'.~.....j|
+000000c0  e4 26 96 e6 de                                    |.&...|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-RSA-AES-GCM b/src/crypto/tls/testdata/Server-TLSv12-RSA-AES-GCM
index a6e7a07..4e52915 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-RSA-AES-GCM
+++ b/src/crypto/tls/testdata/Server-TLSv12-RSA-AES-GCM
@@ -1,86 +1,79 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 83 01 00 00  7f 03 03 19 c7 02 a0 bf  |................|
-00000010  5a fb c2 d4 f5 68 0a 19  0f 5e 3a 6b c5 88 17 0b  |Z....h...^:k....|
-00000020  35 ff df ee 06 32 ad 32  99 0e c9 00 00 04 c0 2f  |5....2.2......./|
-00000030  00 ff 02 01 00 00 51 00  0b 00 04 03 00 01 02 00  |......Q.........|
-00000040  0a 00 1c 00 1a 00 17 00  19 00 1c 00 1b 00 18 00  |................|
-00000050  1a 00 16 00 0e 00 0d 00  0b 00 0c 00 09 00 0a 00  |................|
-00000060  0d 00 20 00 1e 06 01 06  02 06 03 05 01 05 02 05  |.. .............|
-00000070  03 04 01 04 02 04 03 03  01 03 02 03 03 02 01 02  |................|
-00000080  02 02 03 00 0f 00 01 01                           |........|
+00000000  16 03 01 00 73 01 00 00  6f 03 03 4e 1a d7 67 e4  |....s...o..N..g.|
+00000010  d1 11 85 bc 62 59 da 8f  ea d0 a0 2b 9b d3 47 aa  |....bY.....+..G.|
+00000020  d0 39 6f 3f 42 dc 7c 16  bb 25 ef 00 00 04 c0 2f  |.9o?B.|..%...../|
+00000030  00 ff 01 00 00 42 00 0b  00 04 03 00 01 02 00 0a  |.....B..........|
+00000040  00 0a 00 08 00 1d 00 17  00 19 00 18 00 0d 00 20  |............... |
+00000050  00 1e 06 01 06 02 06 03  05 01 05 02 05 03 04 01  |................|
+00000060  04 02 04 03 03 01 03 02  03 03 02 01 02 02 02 03  |................|
+00000070  00 16 00 00 00 17 00 00                           |........|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 c0 2f 00 00  |............./..|
-00000030  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002b0  cd 0c 00 00 c9 03 00 17  41 04 1e 18 37 ef 0d 19  |........A...7...|
-000002c0  51 88 35 75 71 b5 e5 54  5b 12 2e 8f 09 67 fd a7  |Q.5uq..T[....g..|
-000002d0  24 20 3e b2 56 1c ce 97  28 5e f8 2b 2d 4f 9e f1  |$ >.V...(^.+-O..|
-000002e0  07 9f 6c 4b 5b 83 56 e2  32 42 e9 58 b6 d7 49 a6  |..lK[.V.2B.X..I.|
-000002f0  b5 68 1a 41 03 56 6b dc  5a 89 05 01 00 80 97 53  |.h.A.Vk.Z......S|
-00000300  cc 1f a2 55 e2 52 69 a6  b3 78 4f 7e 34 3e 37 e4  |...U.Ri..xO~4>7.|
-00000310  e0 bb 15 ff 96 f8 1d 9c  11 03 2c 68 ca 6d 2b 3c  |..........,h.m+<|
-00000320  b3 96 64 21 d6 3f 81 42  07 c0 1b 85 7e a9 65 54  |..d!.?.B....~.eT|
-00000330  23 89 33 c1 71 b9 29 72  47 8a 0e 71 75 20 d7 b6  |#.3.q.)rG..qu ..|
-00000340  9d c2 ac c1 a8 dc 6c 0e  7e 29 93 fc b2 68 83 2e  |......l.~)...h..|
-00000350  e1 fe e5 eb 54 d7 c3 30  f2 8f 9d 91 49 48 4f 84  |....T..0....IHO.|
-00000360  1a d5 47 75 27 bf c8 09  65 4a a8 7c 65 a0 d0 23  |..Gu'...eJ.|e..#|
-00000370  9f 26 d6 57 62 cb e1 06  64 90 16 73 1b d4 16 03  |.&.Wb...d..s....|
-00000380  03 00 04 0e 00 00 00                              |.......|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  ac 0c 00 00 a8 03 00 1d  |;...............|
+000002a0  20 2f e5 7d a3 47 cd 62  43 15 28 da ac 5f bb 29  | /.}.G.bC.(.._.)|
+000002b0  07 30 ff f6 84 af c4 cf  c2 ed 90 99 5f 58 cb 3b  |.0.........._X.;|
+000002c0  74 05 01 00 80 b2 49 30  60 b7 0c 48 cb 9f 1c 75  |t.....I0`..H...u|
+000002d0  a6 b0 b0 7b 5e e6 f9 bc  5a 49 d4 51 e2 76 4c 01  |...{^...ZI.Q.vL.|
+000002e0  55 bd 37 cf 86 75 4f 33  9b fd 3c fc bb da 81 a9  |U.7..uO3..<.....|
+000002f0  26 7b 82 31 c5 51 0f d4  e8 fa a3 16 45 19 c8 40  |&{.1.Q......E..@|
+00000300  23 fa 32 bc 05 36 fb a7  a2 d9 6f e7 bc b8 27 0b  |#.2..6....o...'.|
+00000310  2a 9e 7b 95 fd b4 c0 2e  f0 73 fe fb a2 ea 20 a2  |*.{......s.... .|
+00000320  73 73 96 c8 bc 82 58 09  84 fc f4 09 2a c8 68 cb  |ss....X.....*.h.|
+00000330  66 b0 de 2c 78 7a d4 ec  06 f1 1c 52 03 5a 69 24  |f..,xz.....R.Zi$|
+00000340  c4 e6 bb 68 f4 16 03 03  00 04 0e 00 00 00        |...h..........|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 1a 94 a7 1a 36  |....F...BA.....6|
-00000010  d1 ca ad d7 e8 64 03 84  b4 a6 9f dc 30 a2 a3 60  |.....d......0..`|
-00000020  a0 5a 1f a0 3d 8e d1 b8  96 75 37 ee a6 3f d6 ad  |.Z..=....u7..?..|
-00000030  93 b6 7d 58 99 53 04 4b  6e c6 7f 04 bf 60 f9 ba  |..}X.S.Kn....`..|
-00000040  e7 b8 04 73 10 77 ff 22  93 b2 7b 14 03 03 00 01  |...s.w."..{.....|
-00000050  01 16 03 03 00 28 29 6b  2b 14 21 a7 e4 84 c0 9d  |.....()k+.!.....|
-00000060  92 07 cd dd 0b eb c1 b0  76 06 71 48 46 93 b8 05  |........v.qHF...|
-00000070  1a 2b 53 14 da 34 ac 05  4c cc 4d 47 12 28        |.+S..4..L.MG.(|
+00000000  16 03 03 00 25 10 00 00  21 20 41 d4 8d 53 2e 47  |....%...! A..S.G|
+00000010  b8 35 ba 86 3c 41 07 2e  c1 a0 9d c2 e9 11 d8 20  |.5..<A......... |
+00000020  a1 fa 0a ff 28 64 5b af  c3 67 14 03 03 00 01 01  |....(d[..g......|
+00000030  16 03 03 00 28 78 fd 19  36 4d a7 ca ab ba 06 6b  |....(x..6M.....k|
+00000040  3f a5 79 17 2f 2b ec f6  19 db 17 1a 52 ea 72 0b  |?.y./+......R.r.|
+00000050  01 af 56 8b 14 8f 8a 04  f3 ff ea fe 33           |..V.........3|
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 28 00 00 00 00 00  |..........(.....|
-00000010  00 00 00 b9 c9 6f cb 58  df 1c a1 0a 79 4e fa 8f  |.....o.X....yN..|
-00000020  41 55 8a 0a f8 d1 83 88  28 fb 44 00 8a a5 11 39  |AU......(.D....9|
-00000030  5b d4 83 17 03 03 00 25  00 00 00 00 00 00 00 01  |[......%........|
-00000040  85 4f 2a 54 aa c0 ce 7b  1e 4e e4 64 56 57 68 5e  |.O*T...{.N.dVWh^|
-00000050  fa 41 67 8a da 9d f4 78  a6 c6 13 76 7c 15 03 03  |.Ag....x...v|...|
-00000060  00 1a 00 00 00 00 00 00  00 02 38 71 21 c6 82 bc  |..........8q!...|
-00000070  2e 37 14 1d 15 2f 74 9d  7c 99 d8 66              |.7.../t.|..f|
+00000010  00 00 00 ec 99 e0 9a 83  28 94 e6 72 4f be 28 24  |........(..rO.($|
+00000020  64 bd 9d 86 79 cc ab 05  15 39 06 6e da 0c b8 4e  |d...y....9.n...N|
+00000030  6c a9 f3 17 03 03 00 25  00 00 00 00 00 00 00 01  |l......%........|
+00000040  9a d7 b0 54 dd 3c ae 8e  3f 1f 41 68 a5 01 a0 da  |...T.<..?.Ah....|
+00000050  e8 8e 90 55 1a 11 f0 70  8d a3 af a4 29 15 03 03  |...U...p....)...|
+00000060  00 1a 00 00 00 00 00 00  00 02 a8 96 cb 16 d7 b1  |................|
+00000070  41 7e bc 0e 01 8f cc 47  40 e5 c7 2a              |A~.....G at ..*|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-RSA-AES256-GCM-SHA384 b/src/crypto/tls/testdata/Server-TLSv12-RSA-AES256-GCM-SHA384
index cc94ac7..36be9da 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-RSA-AES256-GCM-SHA384
+++ b/src/crypto/tls/testdata/Server-TLSv12-RSA-AES256-GCM-SHA384
@@ -1,86 +1,79 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 83 01 00 00  7f 03 03 55 9b 71 e2 46  |...........U.q.F|
-00000010  88 58 c4 16 6a 6e 14 3d  3a 5a f9 fe ec 68 71 24  |.X..jn.=:Z...hq$|
-00000020  d0 06 6f a1 56 8f d6 15  42 6b ba 00 00 04 c0 30  |..o.V...Bk.....0|
-00000030  00 ff 02 01 00 00 51 00  0b 00 04 03 00 01 02 00  |......Q.........|
-00000040  0a 00 1c 00 1a 00 17 00  19 00 1c 00 1b 00 18 00  |................|
-00000050  1a 00 16 00 0e 00 0d 00  0b 00 0c 00 09 00 0a 00  |................|
-00000060  0d 00 20 00 1e 06 01 06  02 06 03 05 01 05 02 05  |.. .............|
-00000070  03 04 01 04 02 04 03 03  01 03 02 03 03 02 01 02  |................|
-00000080  02 02 03 00 0f 00 01 01                           |........|
+00000000  16 03 01 00 73 01 00 00  6f 03 03 b7 d2 dc fe 53  |....s...o......S|
+00000010  d6 13 08 19 be 30 22 17  db a7 06 9b 62 82 14 38  |.....0".....b..8|
+00000020  2e 68 70 08 02 7d 22 64  13 75 f5 00 00 04 c0 30  |.hp..}"d.u.....0|
+00000030  00 ff 01 00 00 42 00 0b  00 04 03 00 01 02 00 0a  |.....B..........|
+00000040  00 0a 00 08 00 1d 00 17  00 19 00 18 00 0d 00 20  |............... |
+00000050  00 1e 06 01 06 02 06 03  05 01 05 02 05 03 04 01  |................|
+00000060  04 02 04 03 03 01 03 02  03 03 02 01 02 02 02 03  |................|
+00000070  00 16 00 00 00 17 00 00                           |........|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 c0 30 00 00  |.............0..|
-00000030  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002b0  cd 0c 00 00 c9 03 00 17  41 04 1e 18 37 ef 0d 19  |........A...7...|
-000002c0  51 88 35 75 71 b5 e5 54  5b 12 2e 8f 09 67 fd a7  |Q.5uq..T[....g..|
-000002d0  24 20 3e b2 56 1c ce 97  28 5e f8 2b 2d 4f 9e f1  |$ >.V...(^.+-O..|
-000002e0  07 9f 6c 4b 5b 83 56 e2  32 42 e9 58 b6 d7 49 a6  |..lK[.V.2B.X..I.|
-000002f0  b5 68 1a 41 03 56 6b dc  5a 89 05 01 00 80 7f 44  |.h.A.Vk.Z......D|
-00000300  af 7b 21 01 6b f0 1c 75  d3 6b 28 99 68 e1 0e d3  |.{!.k..u.k(.h...|
-00000310  a8 cb 5a 2e 23 ad d7 92  73 46 5b 66 88 bd f1 d6  |..Z.#...sF[f....|
-00000320  5d 52 d1 07 53 88 9c 64  e3 ce 80 b3 39 7f 9e 2b  |]R..S..d....9..+|
-00000330  0a 02 a7 e1 3e 00 70 51  3b b4 52 d1 3c 4a e9 f7  |....>.pQ;.R.<J..|
-00000340  0f 85 fa ff d7 ba 96 fc  77 8e 66 8d c6 4c b8 c2  |........w.f..L..|
-00000350  a5 d3 ad 72 f0 8c ba d2  bf 1c 81 7b 4e d5 9e 80  |...r.......{N...|
-00000360  7e b2 90 a0 2f d6 ad c2  33 43 da 46 b0 22 40 ff  |~.../...3C.F."@.|
-00000370  df 95 b3 1e f1 97 b9 7b  61 3c 78 d9 ae cb 16 03  |.......{a<x.....|
-00000380  03 00 04 0e 00 00 00                              |.......|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  ac 0c 00 00 a8 03 00 1d  |;...............|
+000002a0  20 2f e5 7d a3 47 cd 62  43 15 28 da ac 5f bb 29  | /.}.G.bC.(.._.)|
+000002b0  07 30 ff f6 84 af c4 cf  c2 ed 90 99 5f 58 cb 3b  |.0.........._X.;|
+000002c0  74 05 01 00 80 b8 c4 6a  be 2a dd 47 03 7b 84 72  |t......j.*.G.{.r|
+000002d0  0b a4 c0 a7 2e b5 a4 be  c7 6a 2a 8b d0 23 6f b5  |.........j*..#o.|
+000002e0  bc 0e ba 3c f5 9d a3 90  b0 af 80 11 bd 22 b5 7b  |...<.........".{|
+000002f0  3c 53 f8 54 d0 b4 b0 53  28 75 0d 15 58 88 c2 90  |<S.T...S(u..X...|
+00000300  69 ea e0 08 aa 07 93 15  ae ed e5 a8 3d 2d 9c 62  |i...........=-.b|
+00000310  1d 40 26 7d 0e d3 23 52  71 71 ff ea 18 f9 0a eb  |.@&}..#Rqq......|
+00000320  78 8a 8f 9e 12 8c 0b 33  a8 ee 42 76 16 29 58 ec  |x......3..Bv.)X.|
+00000330  ea 6d 22 48 0d d0 68 c3  97 8d e9 ec cd 10 f6 47  |.m"H..h........G|
+00000340  c9 9d 42 12 54 16 03 03  00 04 0e 00 00 00        |..B.T.........|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 46 10 00 00  42 41 04 d8 85 85 d2 78  |....F...BA.....x|
-00000010  27 a5 0a bb 10 67 ec a9  d8 11 f0 ba b9 d7 21 39  |'....g........!9|
-00000020  ed c7 0a a0 a2 69 ab fb  9b 15 e0 d7 ec ca 97 c8  |.....i..........|
-00000030  c0 b2 66 0b 2c 68 37 ac  f0 34 fa 3a 07 dd f2 ae  |..f.,h7..4.:....|
-00000040  8e f6 e3 eb de 08 1f 56  e5 66 eb 14 03 03 00 01  |.......V.f......|
-00000050  01 16 03 03 00 28 f5 2d  89 00 0c 9d d9 0e 54 1b  |.....(.-......T.|
-00000060  71 84 4d c7 bb 98 36 8c  29 b6 06 d8 7b df d9 92  |q.M...6.)...{...|
-00000070  01 00 16 44 5e e3 db f3  8f b7 fa 43 0c f7        |...D^......C..|
+00000000  16 03 03 00 25 10 00 00  21 20 20 df 4a b8 02 4f  |....%...!  .J..O|
+00000010  31 db 22 90 59 57 20 23  e1 72 8d 28 60 b3 f2 77  |1.".YW #.r.(`..w|
+00000020  db 3a ce 64 5a a5 63 94  be 09 14 03 03 00 01 01  |.:.dZ.c.........|
+00000030  16 03 03 00 28 de 72 f3  c3 b2 aa b4 9b b7 fe 35  |....(.r........5|
+00000040  3b 25 af 74 47 d3 49 39  07 d9 70 37 30 d0 b7 47  |;%.tG.I9..p70..G|
+00000050  bf ad 97 08 44 59 a7 3c  12 f2 4a 2d 7c           |....DY.<..J-||
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 28 00 00 00 00 00  |..........(.....|
-00000010  00 00 00 a4 82 dc d1 67  ed 17 ae 22 13 0d ac d2  |.......g..."....|
-00000020  f4 58 44 5b b4 c6 25 29  80 b6 bc 63 0e 67 22 6e  |.XD[..%)...c.g"n|
-00000030  18 92 48 17 03 03 00 25  00 00 00 00 00 00 00 01  |..H....%........|
-00000040  a0 fe 37 25 fb 4d 29 96  f9 01 67 19 d8 83 26 68  |..7%.M)...g...&h|
-00000050  d0 e8 58 2c ef 90 a3 b5  26 51 26 a9 28 15 03 03  |..X,....&Q&.(...|
-00000060  00 1a 00 00 00 00 00 00  00 02 91 4b d5 54 4a ef  |...........K.TJ.|
-00000070  22 88 ab b7 a2 bb 20 5a  b2 3e 7b 36              |"..... Z.>{6|
+00000010  00 00 00 16 18 e1 e8 d4  c0 d1 19 3a 50 10 85 fc  |...........:P...|
+00000020  fc 3e 27 54 e4 57 b6 e7  c4 25 d5 4e 10 ad 0f ff  |.>'T.W...%.N....|
+00000030  ad 45 8c 17 03 03 00 25  00 00 00 00 00 00 00 01  |.E.....%........|
+00000040  50 b8 af 5f a2 3e 0f f7  f0 81 1f 32 69 39 2f f2  |P.._.>.....2i9/.|
+00000050  47 28 80 fb d0 46 d4 b7  a2 ba e3 71 ea 15 03 03  |G(...F.....q....|
+00000060  00 1a 00 00 00 00 00 00  00 02 c4 64 7a 81 b3 3a  |...........dz..:|
+00000070  2c 71 35 ec f7 0c 52 36  20 2c eb fe              |,q5...R6 ,..|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-RSA-RC4 b/src/crypto/tls/testdata/Server-TLSv12-RSA-RC4
index c55f891..e49d1bc 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-RSA-RC4
+++ b/src/crypto/tls/testdata/Server-TLSv12-RSA-RC4
@@ -1,73 +1,72 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 5b 01 00 00  57 03 03 ec 96 78 51 74  |....[...W....xQt|
-00000010  1b f2 21 ad f2 4f 50 aa  67 f1 20 e2 4f d3 4d 0e  |..!..OP.g. .O.M.|
-00000020  54 91 df 91 d8 81 e3 75  bb 20 c2 00 00 04 00 05  |T......u. ......|
-00000030  00 ff 02 01 00 00 29 00  0d 00 20 00 1e 06 01 06  |......)... .....|
-00000040  02 06 03 05 01 05 02 05  03 04 01 04 02 04 03 03  |................|
-00000050  01 03 02 03 03 02 01 02  02 02 03 00 0f 00 01 01  |................|
+00000000  16 03 01 00 5d 01 00 00  59 03 03 55 3e 1a 3f cc  |....]...Y..U>.?.|
+00000010  14 18 07 db 5e 97 15 33  62 9d de 56 7b ea 52 bf  |....^..3b..V{.R.|
+00000020  a3 ce c2 75 3f 52 0a 2f  3e 99 07 00 00 04 00 05  |...u?R./>.......|
+00000030  00 ff 01 00 00 2c 00 0d  00 20 00 1e 06 01 06 02  |.....,... ......|
+00000040  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000050  03 02 03 03 02 01 02 02  02 03 00 16 00 00 00 17  |................|
+00000060  00 00                                             |..|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 05 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 05 4b 04 74 76  |............K.tv|
-00000010  73 3a 92 04 4d 8b 3b 59  c2 43 c5 f4 07 e8 bc a3  |s:..M.;Y.C......|
-00000020  62 44 b7 80 9f 8f bc 43  8a 67 09 64 a7 93 9f f9  |bD.....C.g.d....|
-00000030  2c 55 03 4b e5 87 9d 18  a2 c3 48 4f 02 6e e0 23  |,U.K......HO.n.#|
-00000040  0b 2f 57 81 e4 38 50 11  d6 b1 71 4f c2 e5 a4 03  |./W..8P...qO....|
-00000050  34 a4 eb a1 42 47 79 05  bc 7b b8 26 5b c1 f9 82  |4...BGy..{.&[...|
-00000060  fc 58 49 eb 04 52 fe 57  3c ed 5c 2b d8 fe 49 d7  |.XI..R.W<.\+..I.|
-00000070  d2 2e 6c e8 74 74 0d 87  b3 f6 2d f0 ff 03 f0 2d  |..l.tt....-....-|
-00000080  c8 a2 20 89 3f 3f 11 e1  fb 93 85 14 03 03 00 01  |.. .??..........|
-00000090  01 16 03 03 00 24 9a 81  c0 9e 76 b6 3d 78 37 8e  |.....$....v.=x7.|
-000000a0  ab 33 48 93 bb 0d f4 86  3c ff 72 28 10 35 c2 10  |.3H.....<.r(.5..|
-000000b0  f0 a0 ff 0c 20 f3 c4 29  83 a6                    |.... ..)..|
+00000000  16 03 03 00 86 10 00 00  82 00 80 a7 55 0a e7 33  |............U..3|
+00000010  8e be 5a 3a b4 f4 06 6e  fc 0e 42 6e f3 0c 01 5a  |..Z:...n..Bn...Z|
+00000020  65 73 36 bd cd be 0f 65  2f d2 88 1a f0 5e f8 07  |es6....e/....^..|
+00000030  c1 fe 5f 5f d6 f5 fa 79  24 44 0d 33 4f e6 74 88  |..__...y$D.3O.t.|
+00000040  86 f1 76 84 29 b4 f2 ae  eb 9b 00 a2 6a e4 97 58  |..v.).......j..X|
+00000050  8b 2e 04 8f 8f 5e fe b4  9d 38 1d 8d 40 a4 9b a2  |.....^...8.. at ...|
+00000060  17 50 8a e5 39 c9 e9 41  3e 0d 9c 42 2c 7a 88 bf  |.P..9..A>..B,z..|
+00000070  f7 09 4e 27 0b fe cc 53  13 07 d5 7e 0e e6 02 3c  |..N'...S...~...<|
+00000080  8a 3f f9 03 df b6 65 a0  77 ee 50 14 03 03 00 01  |.?....e.w.P.....|
+00000090  01 16 03 03 00 24 5f 41  3e 38 05 08 74 62 5b 4e  |.....$_A>8..tb[N|
+000000a0  94 55 98 74 5c 65 1a 4c  49 08 1d 77 d7 f0 12 47  |.U.t\e.LI..w...G|
+000000b0  d2 ef a6 31 5c 36 03 b5  b5 9d                    |...1\6....|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 93 15 62 c5 2b  |..........$..b.+|
-00000010  4f 8a d7 0f 70 1f 9d 11  fc 8f 9a a9 b7 d7 44 50  |O...p.........DP|
-00000020  6e 0e 5b d7 3b de 15 7d  17 35 31 42 1f a4 40 17  |n.[.;..}.51B.. at .|
-00000030  03 03 00 21 a9 ca 73 e9  ce 2d 21 ef 7d bc 40 91  |...!..s..-!.}. at .|
-00000040  41 c9 53 62 af 09 8e b4  37 0f fa ab b7 76 8f 5b  |A.Sb....7....v.[|
-00000050  7d 0f 04 48 49 15 03 03  00 16 76 b1 d7 91 88 6f  |}..HI.....v....o|
-00000060  b4 e7 a4 f1 d2 c2 ac 50  db 31 ae 5c f7 53 a1 68  |.......P.1.\.S.h|
+00000000  14 03 03 00 01 01 16 03  03 00 24 6f 68 a2 c0 4d  |..........$oh..M|
+00000010  f4 cb c0 e5 8b 19 f9 2e  46 c3 3b 92 eb a9 42 8b  |........F.;...B.|
+00000020  03 4a e2 62 9d f1 c0 39  b1 63 61 08 15 b0 ca 17  |.J.b...9.ca.....|
+00000030  03 03 00 21 50 9e 16 ce  7e af 8f 43 d1 1c 30 37  |...!P...~..C..07|
+00000040  85 e9 68 3a 9c 7e 26 90  dc 14 b1 ec 91 20 2b 4a  |..h:.~&...... +J|
+00000050  24 b4 fa b1 50 15 03 03  00 16 59 74 08 41 73 01  |$...P.....Yt.As.|
+00000060  22 19 0b 35 6b 4d ee d2  15 50 42 de cc cf cc 09  |"..5kM...PB.....|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-Resume b/src/crypto/tls/testdata/Server-TLSv12-Resume
index 521376c..366ca8f 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-Resume
+++ b/src/crypto/tls/testdata/Server-TLSv12-Resume
@@ -1,37 +1,41 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 f7 01 00 00  f3 03 03 35 70 0b ed 1c  |...........5p...|
-00000010  83 57 b1 7b 0a 47 ce d4  07 3a 49 96 93 4c a1 83  |.W.{.G...:I..L..|
-00000020  4b ab b7 ab 7d b0 14 be  dd 91 92 20 09 34 b7 de  |K...}...... .4..|
-00000030  bd 43 e8 cd 8e f3 43 aa  04 64 8f 0a 55 a6 58 ed  |.C....C..d..U.X.|
-00000040  31 8b 3d 4c 84 4f 2c 6d  b0 b1 75 6f 00 04 00 05  |1.=L.O,m..uo....|
-00000050  00 ff 02 01 00 00 a5 00  23 00 78 50 46 ad c1 db  |........#.xPF...|
-00000060  a8 38 86 7b 2b bb fd d0  c3 42 3e 00 00 00 00 00  |.8.{+....B>.....|
-00000070  00 00 00 00 00 00 00 00  00 00 00 94 6f 2c b5 83  |............o,..|
-00000080  61 4d 51 5f 33 46 48 fe  9e e9 83 f4 b1 aa c9 b1  |aMQ_3FH.........|
-00000090  61 2a b2 9d 0f 17 c4 09  f6 26 7a 75 f1 19 99 db  |a*.......&zu....|
-000000a0  36 d8 32 f0 94 61 4f 8f  ed 80 33 51 f3 c6 15 84  |6.2..aO...3Q....|
-000000b0  6b 33 94 c8 4f 84 b7 7c  27 6d 8f fe 41 8e b2 92  |k3..O..|'m..A...|
-000000c0  ca 80 e8 6c ba 75 77 f5  3a 87 17 ae f8 b9 6b 10  |...l.uw.:.....k.|
-000000d0  f9 85 da 00 0d 00 20 00  1e 06 01 06 02 06 03 05  |...... .........|
-000000e0  01 05 02 05 03 04 01 04  02 04 03 03 01 03 02 03  |................|
-000000f0  03 02 01 02 02 02 03 00  0f 00 01 01              |............|
+00000000  16 03 01 00 f9 01 00 00  f5 03 03 23 77 58 99 0e  |...........#wX..|
+00000010  44 ed 63 44 e4 e4 eb d1  83 c3 9c d0 24 12 a3 b9  |D.cD........$...|
+00000020  55 6b 4d da bf 84 9d 35  de 43 a0 20 7b 93 cb d3  |UkM....5.C. {...|
+00000030  c5 ce 5e d5 aa 48 91 a4  b2 c2 d7 72 09 0d 21 78  |..^..H.....r..!x|
+00000040  f0 ac 7a ed 9a a9 ad dd  51 8b b2 1c 00 04 00 2f  |..z.....Q....../|
+00000050  00 ff 01 00 00 a8 00 23  00 78 50 46 ad c1 db a8  |.......#.xPF....|
+00000060  38 86 7b 2b bb fd d0 c3  42 3e 00 00 00 00 00 00  |8.{+....B>......|
+00000070  00 00 00 00 00 00 00 00  00 00 94 6f 2c 9f 83 61  |...........o,..a|
+00000080  0b b1 b7 9e 10 2d 0c 56  e8 70 66 ad de b1 15 74  |.....-.V.pf....t|
+00000090  2f 8b 08 8c 96 bb 4b 1b  4e dd 81 0e bf 84 4d 43  |/.....K.N.....MC|
+000000a0  8f c0 7e a0 7f be c0 59  bf 83 26 0f a2 22 52 2c  |..~....Y..&.."R,|
+000000b0  33 94 5a 77 54 f3 b5 f2  22 51 d5 24 c2 60 c3 2e  |3.ZwT..."Q.$.`..|
+000000c0  0f 9c 5e 33 3b e8 7c 52  2a 76 08 58 ac 47 98 bc  |..^3;.|R*v.X.G..|
+000000d0  36 b6 00 0d 00 20 00 1e  06 01 06 02 06 03 05 01  |6.... ..........|
+000000e0  05 02 05 03 04 01 04 02  04 03 03 01 03 02 03 03  |................|
+000000f0  02 01 02 02 02 03 00 16  00 00 00 17 00 00        |..............|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 51 02 00 00  4d 03 03 00 00 00 00 00  |....Q...M.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 20 09 34 b7 de  |........... .4..|
-00000030  bd 43 e8 cd 8e f3 43 aa  04 64 8f 0a 55 a6 58 ed  |.C....C..d..U.X.|
-00000040  31 8b 3d 4c 84 4f 2c 6d  b0 b1 75 6f 00 05 00 00  |1.=L.O,m..uo....|
+00000020  00 00 00 00 00 00 00 00  00 00 00 20 7b 93 cb d3  |........... {...|
+00000030  c5 ce 5e d5 aa 48 91 a4  b2 c2 d7 72 09 0d 21 78  |..^..H.....r..!x|
+00000040  f0 ac 7a ed 9a a9 ad dd  51 8b b2 1c 00 2f 00 00  |..z.....Q..../..|
 00000050  05 ff 01 00 01 00 14 03  03 00 01 01 16 03 03 00  |................|
-00000060  24 18 67 37 5a c6 ea 3f  5f 06 2d c3 f1 2a ff d3  |$.g7Z..?_.-..*..|
-00000070  45 ce fe 38 1a e6 39 25  e7 e5 01 4d 6e fd 23 af  |E..8..9%...Mn.#.|
-00000080  dc 67 1b 1d e2                                    |.g...|
+00000060  40 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |@...............|
+00000070  00 ac d9 95 88 c6 37 e8  3c 24 d8 d9 15 46 25 c6  |......7.<$...F%.|
+00000080  32 0c 75 80 11 3d 89 53  1c 7a b1 78 6a c1 1a d7  |2.u..=.S.z.xj...|
+00000090  91 6e c2 55 99 84 11 43  cd 62 99 3b 28 1b 2e 08  |.n.U...C.b.;(...|
+000000a0  a8                                                |.|
 >>> Flow 3 (client to server)
-00000000  14 03 03 00 01 01 16 03  03 00 24 b5 75 e6 1d 1d  |..........$.u...|
-00000010  cb 2c 5d 9f d1 07 de 23  11 84 c2 59 50 55 72 27  |.,]....#...YPUr'|
-00000020  f2 5e 34 e2 c1 53 bf 21  5f f4 c4 2c f1 e1 7a     |.^4..S.!_..,..z|
+00000000  14 03 03 00 01 01 16 03  03 00 40 67 fd 43 2a 0b  |.......... at g.C*.|
+00000010  14 6b 89 53 84 a8 04 62  d6 30 af 68 eb 8e 2a de  |.k.S...b.0.h..*.|
+00000020  67 c9 40 af 8b ac dd 29  a4 20 e4 da b0 dd c3 05  |g. at ....). ......|
+00000030  82 83 8f 75 77 db 6c fe  e7 20 54 e3 eb 51 31 68  |...uw.l.. T..Q1h|
+00000040  da 11 a3 6d a1 34 d9 f5  d1 ef c9                 |...m.4.....|
 >>> Flow 4 (server to client)
-00000000  17 03 03 00 21 93 92 dd  07 a3 8f 3d 34 6d b8 94  |....!......=4m..|
-00000010  a7 6f 18 27 e7 cd 30 0a  08 4f b6 9b cb 43 93 27  |.o.'..0..O...C.'|
-00000020  b6 8b 83 ae d6 8a 15 03  03 00 16 68 a1 a4 f8 66  |...........h...f|
-00000030  8a c0 e7 d3 97 83 cb 35  94 84 7a e6 47 3c 97 8c  |.......5..z.G<..|
-00000040  69                                                |i|
+00000000  17 03 03 00 40 00 00 00  00 00 00 00 00 00 00 00  |.... at ...........|
+00000010  00 00 00 00 00 ee e2 75  6f 78 b0 88 1a 8b 9b 91  |.......uox......|
+00000020  c9 8c 3b ae a5 93 71 12  55 66 f8 09 a5 1f 4b 1b  |..;...q.Uf....K.|
+00000030  c2 fe 65 8b 3d d9 dc fa  af dc 29 1b 83 da e0 6a  |..e.=.....)....j|
+00000040  4b cd d0 dc 27                                    |K...'|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-ResumeDisabled b/src/crypto/tls/testdata/Server-TLSv12-ResumeDisabled
index c3cd2ef..3474837 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-ResumeDisabled
+++ b/src/crypto/tls/testdata/Server-TLSv12-ResumeDisabled
@@ -1,83 +1,89 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 f7 01 00 00  f3 03 03 2b 89 3f 02 47  |...........+.?.G|
-00000010  f6 14 64 3b 64 08 84 6e  9c e1 c9 4d 4f 30 92 06  |..d;d..n...MO0..|
-00000020  d1 19 58 5d 93 30 41 4d  a6 2c f6 20 53 3c e7 f4  |..X].0AM.,. S<..|
-00000030  23 7e 59 b1 32 c4 2d db  0b 6f ab 64 4a 72 c9 31  |#~Y.2.-..o.dJr.1|
-00000040  d9 b9 38 a8 b4 4e 0c 39  f8 f4 61 7a 00 04 00 05  |..8..N.9..az....|
-00000050  00 ff 02 01 00 00 a5 00  23 00 78 50 46 ad c1 db  |........#.xPF...|
-00000060  a8 38 86 7b 2b bb fd d0  c3 42 3e 00 00 00 00 00  |.8.{+....B>.....|
-00000070  00 00 00 00 00 00 00 00  00 00 00 94 6f 2c b5 83  |............o,..|
-00000080  61 01 55 65 2a 95 38 d4  d5 5f 41 c1 45 e4 f8 4b  |a.Ue*.8.._A.E..K|
-00000090  3b 08 44 df 0b 72 11 93  cd d4 ff 36 0f 4f 3a a9  |;.D..r.....6.O:.|
-000000a0  4c 9f ab c7 ae 88 97 bc  1e ff 2d 27 39 a8 82 84  |L.........-'9...|
-000000b0  ae 33 94 48 8b 1c 58 9d  60 65 3c 3f 17 9d 6a eb  |.3.H..X.`e<?..j.|
-000000c0  50 cd 65 04 bb c7 28 c8  0d 57 44 52 e0 17 de df  |P.e...(..WDR....|
-000000d0  f3 13 b1 00 0d 00 20 00  1e 06 01 06 02 06 03 05  |...... .........|
-000000e0  01 05 02 05 03 04 01 04  02 04 03 03 01 03 02 03  |................|
-000000f0  03 02 01 02 02 02 03 00  0f 00 01 01              |............|
+00000000  16 03 01 00 f9 01 00 00  f5 03 03 e8 59 b4 a7 b2  |............Y...|
+00000010  77 86 57 47 0d d7 7b 2b  c1 a2 04 fd 8d 4d e4 f5  |w.WG..{+.....M..|
+00000020  be e2 65 8e 28 9a fe c3  19 fc 43 20 40 38 fb 60  |..e.(.....C @8.`|
+00000030  f8 2f 36 f4 85 1d ee f1  53 f2 90 cf 3c 58 36 cd  |./6.....S...<X6.|
+00000040  bd 22 b4 0c 92 a9 17 56  f9 b4 dd 9b 00 04 00 2f  |.".....V......./|
+00000050  00 ff 01 00 00 a8 00 23  00 78 50 46 ad c1 db a8  |.......#.xPF....|
+00000060  38 86 7b 2b bb fd d0 c3  42 3e 00 00 00 00 00 00  |8.{+....B>......|
+00000070  00 00 00 00 00 00 00 00  00 00 94 6f 2c 9f 83 61  |...........o,..a|
+00000080  2e fe 48 fe f6 bb 98 a0  6f b0 be 9e 86 d7 b2 f2  |..H.....o.......|
+00000090  67 c7 44 c7 3d e4 2b de  d0 f4 d2 17 51 84 8e 7a  |g.D.=.+.....Q..z|
+000000a0  a7 80 c4 65 14 f7 49 09  68 15 56 68 32 41 d1 6f  |...e..I.h.Vh2A.o|
+000000b0  33 94 a1 3a c9 37 20 5d  e6 b0 6f 37 0a 10 e3 28  |3..:.7 ]..o7...(|
+000000c0  e1 34 b6 6d e6 7a 44 24  7f 2f cf 1b ae dd 4c d0  |.4.m.zD$./....L.|
+000000d0  11 75 00 0d 00 20 00 1e  06 01 06 02 06 03 05 01  |.u... ..........|
+000000e0  05 02 05 03 04 01 04 02  04 03 03 01 03 02 03 03  |................|
+000000f0  02 01 02 02 02 03 00 16  00 00 00 17 00 00        |..............|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 05 00 00  |................|
-00000030  05 ff 01 00 01 00 16 03  03 02 71 0b 00 02 6d 00  |..........q...m.|
-00000040  02 6a 00 02 67 30 82 02  63 30 82 01 cc a0 03 02  |.j..g0..c0......|
-00000050  01 02 02 09 00 a2 73 00  0c 81 00 cb f3 30 0d 06  |......s......0..|
-00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 2b 31 17  |.*.H........0+1.|
-00000070  30 15 06 03 55 04 0a 13  0e 47 6f 6f 67 6c 65 20  |0...U....Google |
-00000080  54 45 53 54 49 4e 47 31  10 30 0e 06 03 55 04 03  |TESTING1.0...U..|
-00000090  13 07 47 6f 20 52 6f 6f  74 30 1e 17 0d 31 35 30  |..Go Root0...150|
-000000a0  31 30 31 30 30 30 30 30  30 5a 17 0d 32 35 30 31  |101000000Z..2501|
-000000b0  30 31 30 30 30 30 30 30  5a 30 26 31 17 30 15 06  |01000000Z0&1.0..|
-000000c0  03 55 04 0a 13 0e 47 6f  6f 67 6c 65 20 54 45 53  |.U....Google TES|
-000000d0  54 49 4e 47 31 0b 30 09  06 03 55 04 03 13 02 47  |TING1.0...U....G|
-000000e0  6f 30 81 9f 30 0d 06 09  2a 86 48 86 f7 0d 01 01  |o0..0...*.H.....|
-000000f0  01 05 00 03 81 8d 00 30  81 89 02 81 81 00 af 87  |.......0........|
-00000100  88 f6 20 1b 95 65 6c 14  ab 44 05 af 3b 45 14 e3  |.. ..el..D..;E..|
-00000110  b7 6d fd 00 63 4d 95 7f  fe 6a 62 35 86 c0 4a f9  |.m..cM...jb5..J.|
-00000120  18 7c f6 aa 25 5e 7a 64  31 66 00 ba f4 8e 92 af  |.|..%^zd1f......|
-00000130  c7 6b d8 76 d4 f3 5f 41  cb 6e 56 15 97 1b 97 c1  |.k.v.._A.nV.....|
-00000140  3c 12 39 21 66 3d 2b 16  d1 bc db 1c c0 a7 da b7  |<.9!f=+.........|
-00000150  ca ad ba da cb d5 21 50  ec de 8d ab d1 6b 81 4b  |......!P.....k.K|
-00000160  89 02 f3 c4 be c1 6c 89  b1 44 84 bd 21 d1 04 7d  |......l..D..!..}|
-00000170  9d 16 4d f9 82 15 f6 ef  fa d6 09 47 f2 fb 02 03  |..M........G....|
-00000180  01 00 01 a3 81 93 30 81  90 30 0e 06 03 55 1d 0f  |......0..0...U..|
-00000190  01 01 ff 04 04 03 02 05  a0 30 1d 06 03 55 1d 25  |.........0...U.%|
-000001a0  04 16 30 14 06 08 2b 06  01 05 05 07 03 01 06 08  |..0...+.........|
-000001b0  2b 06 01 05 05 07 03 02  30 0c 06 03 55 1d 13 01  |+.......0...U...|
-000001c0  01 ff 04 02 30 00 30 19  06 03 55 1d 0e 04 12 04  |....0.0...U.....|
-000001d0  10 12 50 8d 89 6f 1b d1  dc 54 4d 6e cb 69 5e 06  |..P..o...TMn.i^.|
-000001e0  f4 30 1b 06 03 55 1d 23  04 14 30 12 80 10 bf 3d  |.0...U.#..0....=|
-000001f0  b6 a9 66 f2 b8 40 cf ea  b4 03 78 48 1a 41 30 19  |..f.. at ....xH.A0.|
-00000200  06 03 55 1d 11 04 12 30  10 82 0e 65 78 61 6d 70  |..U....0...examp|
-00000210  6c 65 2e 67 6f 6c 61 6e  67 30 0d 06 09 2a 86 48  |le.golang0...*.H|
-00000220  86 f7 0d 01 01 0b 05 00  03 81 81 00 92 7c af 91  |.............|..|
-00000230  55 12 18 96 59 31 a6 48  40 d5 2d d5 ee bb 02 a0  |U...Y1.H at .-.....|
-00000240  f5 c2 1e 7c 9b b3 30 7d  3c dc 76 da 4f 3d c0 fa  |...|..0}<.v.O=..|
-00000250  ae 2d 33 24 6b 03 7b 1b  67 59 11 21 b5 11 bc 77  |.-3$k.{.gY.!...w|
-00000260  b9 d9 e0 6e a8 2d 2e 35  fa 64 5f 22 3e 63 10 6b  |...n.-.5.d_">c.k|
-00000270  be ff 14 86 6d 0d f0 15  31 a8 14 38 1e 3b 84 87  |....m...1..8.;..|
-00000280  2c cb 98 ed 51 76 b9 b1  4f dd db 9b 84 04 86 40  |,...Qv..O......@|
-00000290  fa 51 dd ba b4 8d eb e3  46 de 46 b9 4f 86 c7 f9  |.Q......F.F.O...|
-000002a0  a4 c2 41 34 ac cc f6 ea  b0 ab 39 18 16 03 03 00  |..A4......9.....|
-000002b0  04 0e 00 00 00                                    |.....|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 ab d9 61 5e d3  |.............a^.|
-00000010  87 d7 eb 21 12 6f f9 61  dd 8b de 76 d7 14 70 2f  |...!.o.a...v..p/|
-00000020  9c 0f 3c d4 4c 77 41 e2  ac 73 18 c3 0f 66 f2 b1  |..<.LwA..s...f..|
-00000030  fd 4b 1e d9 cb 5c 94 16  4d c2 98 f9 0d 55 f7 79  |.K...\..M....U.y|
-00000040  e2 8d 2c 87 96 e7 10 fb  78 fb ce 27 5d 9f ac 97  |..,.....x..']...|
-00000050  d6 54 6f 0c 78 dc 7b 2e  49 4c e2 42 24 b9 3d de  |.To.x.{.IL.B$.=.|
-00000060  89 5f 1a 40 54 33 11 96  89 6f 59 25 5e 89 60 40  |._. at T3...oY%^.`@|
-00000070  83 8c 0e 92 0e 7d 68 9d  17 74 ba 39 e8 6f e3 43  |.....}h..t.9.o.C|
-00000080  44 80 e6 62 4b 35 43 21  5b eb 32 14 03 03 00 01  |D..bK5C![.2.....|
-00000090  01 16 03 03 00 24 77 1a  b5 2c 88 d7 a6 83 f5 30  |.....$w..,.....0|
-000000a0  a0 c3 b4 45 a6 af 9b c2  ac 55 cf 73 4f a0 ba e5  |...E.....U.sO...|
-000000b0  2a be 9c 97 d2 d2 0b e9  95 0e                    |*.........|
+00000000  16 03 03 00 86 10 00 00  82 00 80 5e 04 66 f2 27  |...........^.f.'|
+00000010  99 3b f8 15 9f b8 4a ab  8c 32 10 0d 5b c9 5b 0b  |.;....J..2..[.[.|
+00000020  04 69 dc 2b 9e bb 28 38  b6 a0 0f 32 ae 8c 96 64  |.i.+..(8...2...d|
+00000030  63 97 6b b6 63 94 45 84  03 28 d1 d8 85 2f a7 bb  |c.k.c.E..(.../..|
+00000040  be ca 3e f5 30 27 e1 fd  e5 cc bc b5 61 3d 26 8d  |..>.0'......a=&.|
+00000050  0e 93 dd 78 07 5c fe 1b  a9 57 c7 ce e6 df eb 28  |...x.\...W.....(|
+00000060  74 ce 12 f3 df 3f c0 9e  54 b6 e0 b0 ea f7 08 c6  |t....?..T.......|
+00000070  e1 9b cb e7 e9 41 b0 b4  68 2f f2 9b 1a 0a e3 17  |.....A..h/......|
+00000080  df d7 18 ff 95 ca 36 07  32 ff f9 14 03 03 00 01  |......6.2.......|
+00000090  01 16 03 03 00 40 cb c3  74 05 82 ab 93 07 a2 8b  |..... at ..t.......|
+000000a0  24 27 c0 21 3e d1 15 12  9a 85 20 5b f5 7e 7e 0a  |$'.!>..... [.~~.|
+000000b0  a0 8e b2 de aa 25 2a b3  3d 12 1b 01 45 ec 36 53  |.....%*.=...E.6S|
+000000c0  32 1d 81 c7 1d a6 96 c2  a9 2e af fa 90 6e 76 bb  |2............nv.|
+000000d0  a2 bc 43 91 c9 ca                                 |..C...|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 a9 ae 0c a5 ed  |..........$.....|
-00000010  51 10 d9 14 71 41 40 bd  be f5 44 98 11 2f d8 0f  |Q...qA at ...D../..|
-00000020  4d 42 bf 28 53 bf 02 7e  d6 16 92 7f dd 03 ec 17  |MB.(S..~........|
-00000030  03 03 00 21 46 c2 68 a1  6a 35 67 e7 7d 62 71 43  |...!F.h.j5g.}bqC|
-00000040  6b ea d6 fc 22 fc 2d ad  45 d6 6d 98 9e e5 88 a0  |k...".-.E.m.....|
-00000050  ac 80 f7 6f 05 15 03 03  00 16 0c ae f4 fc 0e 09  |...o............|
-00000060  0b 09 73 69 83 0d 00 89  39 ff f6 20 4e 2b 88 3d  |..si....9.. N+.=|
+00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
+00000010  00 00 00 00 00 00 00 00  00 00 00 7f 39 4c 83 d4  |............9L..|
+00000020  ca a2 7a a8 eb 3e 45 18  6e 33 3d 6f eb 2d 4f 72  |..z..>E.n3=o.-Or|
+00000030  35 ee c3 f8 22 fd 39 28  47 23 55 16 6c 47 80 b7  |5...".9(G#U.lG..|
+00000040  65 31 15 f6 89 79 96 bd  6a df 1d 17 03 03 00 40  |e1...y..j......@|
+00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000060  0c ea 0d 87 9a 24 d5 cc  26 9a a2 32 df 04 24 7d  |.....$..&..2..$}|
+00000070  45 ed 35 4e 5b a0 57 c1  c7 f1 0f 8b b0 f9 49 85  |E.5N[.W.......I.|
+00000080  d6 e6 36 26 d5 f3 e4 00  76 d0 d6 20 be b3 31 e5  |..6&....v.. ..1.|
+00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
+000000a0  00 00 00 00 00 6c 51 e9  c2 e8 4f 43 e2 ce 01 9d  |.....lQ...OC....|
+000000b0  d9 6f d7 c7 bf 16 d9 28  ca 8a ea 5e d5 84 ba 55  |.o.....(...^...U|
+000000c0  b7 23 9d 79 28                                    |.#.y(|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-SNI b/src/crypto/tls/testdata/Server-TLSv12-SNI
index ae52ac2..852cc63 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-SNI
+++ b/src/crypto/tls/testdata/Server-TLSv12-SNI
@@ -1,64 +1,81 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 6f 01 00 00  6b 03 03 07 0f b6 b9 cc  |....o...k.......|
-00000010  db 23 57 92 d0 9b 37 72  9d ad 9a 0d 17 6b dd b8  |.#W...7r.....k..|
-00000020  81 b7 7c 54 dd 68 fe 4e  28 00 39 00 00 04 00 2f  |..|T.h.N(.9..../|
-00000030  00 ff 02 01 00 00 3d 00  00 00 10 00 0e 00 00 0b  |......=.........|
-00000040  73 6e 69 74 65 73 74 2e  63 6f 6d 00 0d 00 20 00  |snitest.com... .|
-00000050  1e 06 01 06 02 06 03 05  01 05 02 05 03 04 01 04  |................|
-00000060  02 04 03 03 01 03 02 03  03 02 01 02 02 02 03 00  |................|
-00000070  0f 00 01 01                                       |....|
+00000000  16 03 01 00 71 01 00 00  6d 03 03 35 8f 03 0b f4  |....q...m..5....|
+00000010  81 dd d7 ec 8b cc 85 bd  07 5b 83 16 cc 6e b2 67  |.........[...n.g|
+00000020  fd 33 69 81 14 9a 14 9d  37 43 5a 00 00 04 00 2f  |.3i.....7CZ..../|
+00000030  00 ff 01 00 00 40 00 00  00 10 00 0e 00 00 0b 73  |..... at .........s|
+00000040  6e 69 74 65 73 74 2e 63  6f 6d 00 0d 00 20 00 1e  |nitest.com... ..|
+00000050  06 01 06 02 06 03 05 01  05 02 05 03 04 01 04 02  |................|
+00000060  04 03 03 01 03 02 03 03  02 01 02 02 02 03 00 16  |................|
+00000070  00 00 00 17 00 00                                 |......|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
-00000030  05 ff 01 00 01 00 16 03  03 02 00 0b 00 01 fc 00  |................|
-00000040  01 f9 00 01 f6 30 82 01  f2 30 82 01 5d a0 03 02  |.....0...0..]...|
-00000050  01 02 02 01 00 30 0b 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000060  01 05 30 28 31 10 30 0e  06 03 55 04 0a 13 07 41  |..0(1.0...U....A|
-00000070  63 6d 65 20 43 6f 31 14  30 12 06 03 55 04 03 13  |cme Co1.0...U...|
-00000080  0b 73 6e 69 74 65 73 74  2e 63 6f 6d 30 1e 17 0d  |.snitest.com0...|
-00000090  31 32 30 34 31 31 31 37  34 30 33 35 5a 17 0d 31  |120411174035Z..1|
-000000a0  33 30 34 31 31 31 37 34  35 33 35 5a 30 28 31 10  |30411174535Z0(1.|
-000000b0  30 0e 06 03 55 04 0a 13  07 41 63 6d 65 20 43 6f  |0...U....Acme Co|
-000000c0  31 14 30 12 06 03 55 04  03 13 0b 73 6e 69 74 65  |1.0...U....snite|
-000000d0  73 74 2e 63 6f 6d 30 81  9d 30 0b 06 09 2a 86 48  |st.com0..0...*.H|
-000000e0  86 f7 0d 01 01 01 03 81  8d 00 30 81 89 02 81 81  |..........0.....|
-000000f0  00 bb 79 d6 f5 17 b5 e5  bf 46 10 d0 dc 69 be e6  |..y......F...i..|
-00000100  2b 07 43 5a d0 03 2d 8a  7a 43 85 b7 14 52 e7 a5  |+.CZ..-.zC...R..|
-00000110  65 4c 2c 78 b8 23 8c b5  b4 82 e5 de 1f 95 3b 7e  |eL,x.#........;~|
-00000120  62 a5 2c a5 33 d6 fe 12  5c 7a 56 fc f5 06 bf fa  |b.,.3...\zV.....|
-00000130  58 7b 26 3f b5 cd 04 d3  d0 c9 21 96 4a c7 f4 54  |X{&?......!.J..T|
-00000140  9f 5a bf ef 42 71 00 fe  18 99 07 7f 7e 88 7d 7d  |.Z..Bq......~.}}|
-00000150  f1 04 39 c4 a2 2e db 51  c9 7c e3 c0 4c 3b 32 66  |..9....Q.|..L;2f|
-00000160  01 cf af b1 1d b8 71 9a  1d db db 89 6b ae da 2d  |......q.....k..-|
-00000170  79 02 03 01 00 01 a3 32  30 30 30 0e 06 03 55 1d  |y......2000...U.|
-00000180  0f 01 01 ff 04 04 03 02  00 a0 30 0d 06 03 55 1d  |..........0...U.|
-00000190  0e 04 06 04 04 01 02 03  04 30 0f 06 03 55 1d 23  |.........0...U.#|
-000001a0  04 08 30 06 80 04 01 02  03 04 30 0b 06 09 2a 86  |..0.......0...*.|
-000001b0  48 86 f7 0d 01 01 05 03  81 81 00 89 c6 45 5f 1c  |H............E_.|
-000001c0  1f 5e f8 eb 1a b1 74 ee  24 39 05 9f 5c 42 59 bb  |.^....t.$9..\BY.|
-000001d0  1a 8d 86 cd b1 d0 56 f5  6a 71 7d a4 0e 95 ab 90  |......V.jq}.....|
-000001e0  f5 9e 8d ea f6 27 c1 57  99 50 94 db 08 02 26 6e  |.....'.W.P....&n|
-000001f0  b3 4f c6 84 2d ea 8a 4b  68 d9 c1 38 91 03 ab 84  |.O..-..Kh..8....|
-00000200  fb 9e 1f 85 d9 b5 d2 3f  f2 31 2c 86 70 fb b5 40  |.......?.1,.p..@|
-00000210  14 82 45 a4 eb af e2 64  d9 0c 8a 4c f4 f8 5b 0f  |..E....d...L..[.|
-00000220  ac 12 ac 2f c4 a3 15 4b  ad 52 46 28 68 af 96 c6  |.../...K.RF(h...|
-00000230  2c 65 25 d6 52 b6 e3 18  45 bd cc 16 03 03 00 04  |,e%.R...E.......|
-00000240  0e 00 00 00                                       |....|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 72 6a 6f a8 c9  |...........rjo..|
-00000010  3e 4c 33 da 92 23 97 68  fc 4e ca 74 77 98 f3 88  |>L3..#.h.N.tw...|
-00000020  ba a0 55 b6 a0 6f ff c8  db 2b 90 17 1f 45 bc 26  |..U..o...+...E.&|
-00000030  62 6e b9 91 96 b9 03 5d  eb f2 a2 59 87 7b 81 4a  |bn.....]...Y.{.J|
-00000040  0c f9 e2 23 60 e3 c7 4d  53 4f 3a 1f c5 5f dd 15  |...#`..MSO:.._..|
-00000050  cc 78 c5 21 fd 33 02 68  77 7c 8d 5f e8 80 a7 84  |.x.!.3.hw|._....|
-00000060  a7 2c b3 1f 64 df 8a 63  e9 b3 24 02 c1 6a 94 bd  |.,..d..c..$..j..|
-00000070  a1 62 e5 32 e5 d9 83 25  0d 0f 1a 02 90 8a cd 79  |.b.2...%.......y|
-00000080  1c bd 4a c2 f4 5d a0 24  c6 c1 ae 14 03 03 00 01  |..J..].$........|
-00000090  01 16 03 03 00 40 1f 1a  17 47 15 25 5b 3d 5f e5  |..... at ...G.%[=_.|
-000000a0  f4 d9 64 47 5f 80 09 f7  dd 5a ff 58 19 08 b5 db  |..dG_....Z.X....|
-000000b0  38 88 a4 44 07 01 fb 80  1d 89 b2 d4 af 95 80 b3  |8..D............|
-000000c0  75 13 82 0e 24 12 1d 5c  29 72 1d 21 d4 69 16 e0  |u...$..\)r.!.i..|
-000000d0  b5 4b 46 62 fe f7                                 |.KFb..|
+00000000  16 03 03 00 86 10 00 00  82 00 80 4c 15 46 23 91  |...........L.F#.|
+00000010  a0 d8 6c 45 f0 49 7e 70  84 9f bf 53 3d 68 2c cc  |..lE.I~p...S=h,.|
+00000020  20 3f 28 bd cf e6 6e fd  e6 90 ff 87 14 82 65 00  | ?(...n.......e.|
+00000030  d6 b6 ef 5a 0c d6 30 76  88 d2 37 33 39 de 00 b4  |...Z..0v..739...|
+00000040  ec dd 30 3b f6 88 ff 4c  b2 98 75 77 fd c3 61 38  |..0;...L..uw..a8|
+00000050  2d 00 f7 14 d8 a4 37 22  c0 db 8a bd 12 0b b8 cc  |-.....7"........|
+00000060  37 82 78 d3 0e f2 0b 9b  51 c5 26 c5 e2 ce 3e 0e  |7.x.....Q.&...>.|
+00000070  04 34 39 83 a8 f5 65 ff  40 d9 9b 4a 11 6b b3 d2  |.49...e. at ..J.k..|
+00000080  f7 02 78 a9 7c f4 69 56  3a a4 98 14 03 03 00 01  |..x.|.iV:.......|
+00000090  01 16 03 03 00 40 d6 90  b3 07 d1 a1 c1 12 35 07  |..... at ........5.|
+000000a0  4e c0 df 4b 17 cc fa 49  47 c9 22 c3 6f 70 fa ee  |N..K...IG.".op..|
+000000b0  cf b3 61 d6 06 54 cd ce  c2 15 17 8a a0 f6 5c 43  |..a..T........\C|
+000000c0  7c 92 ce 89 d4 96 53 d0  c7 e6 9a 24 bc 5a 83 e5  ||.....S....$.Z..|
+000000d0  9c 65 72 e7 80 a4                                 |.er...|
 >>> Flow 4 (server to client)
-00000000  15 03 03 00 02 02 14                              |.......|
+00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
+00000010  00 00 00 00 00 00 00 00  00 00 00 a0 61 69 4c 9d  |............aiL.|
+00000020  68 f3 f8 f6 a0 ef 1b f4  a2 f5 83 fa 03 87 ad 67  |h..............g|
+00000030  7e 9f df c6 ce 9f 69 ce  22 fc de 91 0d 18 00 fb  |~.....i.".......|
+00000040  c1 5d a1 2d bb 89 29 4f  f6 de 57 17 03 03 00 40  |.].-..)O..W....@|
+00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000060  bb 54 f4 80 69 1d 3b 9c  e7 9c 1a fb 4e 3d c1 02  |.T..i.;.....N=..|
+00000070  d3 05 86 35 47 61 59 aa  45 54 ae a2 59 4c 75 8c  |...5GaY.ET..YLu.|
+00000080  8d a9 7d 7f a0 4b d9 65  7a 53 ef 7e ed a3 fa 9e  |..}..K.ezS.~....|
+00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
+000000a0  00 00 00 00 00 cc fd 0f  cb 74 a5 36 ce c1 cd 54  |.........t.6...T|
+000000b0  6f 66 81 c0 ab ff 72 ea  f3 1f a6 b7 ef 46 45 68  |of....r......FEh|
+000000c0  9b 0b 7f 4f 46                                    |...OF|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificate b/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificate
index 7ac8dc9..b35cd8d 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificate
+++ b/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificate
@@ -1,64 +1,81 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 6f 01 00 00  6b 03 03 fe d6 ce b5 1b  |....o...k.......|
-00000010  1c 50 0c db 9c 35 5d 0e  f2 ee 57 3f 65 83 9f 28  |.P...5]...W?e..(|
-00000020  96 50 1c e0 7f 95 f9 17  39 4f c3 00 00 04 00 2f  |.P......9O...../|
-00000030  00 ff 02 01 00 00 3d 00  00 00 10 00 0e 00 00 0b  |......=.........|
-00000040  73 6e 69 74 65 73 74 2e  63 6f 6d 00 0d 00 20 00  |snitest.com... .|
-00000050  1e 06 01 06 02 06 03 05  01 05 02 05 03 04 01 04  |................|
-00000060  02 04 03 03 01 03 02 03  03 02 01 02 02 02 03 00  |................|
-00000070  0f 00 01 01                                       |....|
+00000000  16 03 01 00 71 01 00 00  6d 03 03 31 c7 3f 2b 99  |....q...m..1.?+.|
+00000010  95 d8 d5 b7 91 ab 95 c6  09 35 0c 2b bd b6 94 1e  |.........5.+....|
+00000020  64 4a 2d b6 43 23 a0 01  e7 93 22 00 00 04 00 2f  |dJ-.C#...."..../|
+00000030  00 ff 01 00 00 40 00 00  00 10 00 0e 00 00 0b 73  |..... at .........s|
+00000040  6e 69 74 65 73 74 2e 63  6f 6d 00 0d 00 20 00 1e  |nitest.com... ..|
+00000050  06 01 06 02 06 03 05 01  05 02 05 03 04 01 04 02  |................|
+00000060  04 03 03 01 03 02 03 03  02 01 02 02 02 03 00 16  |................|
+00000070  00 00 00 17 00 00                                 |......|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
-00000030  05 ff 01 00 01 00 16 03  03 02 00 0b 00 01 fc 00  |................|
-00000040  01 f9 00 01 f6 30 82 01  f2 30 82 01 5d a0 03 02  |.....0...0..]...|
-00000050  01 02 02 01 00 30 0b 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000060  01 05 30 28 31 10 30 0e  06 03 55 04 0a 13 07 41  |..0(1.0...U....A|
-00000070  63 6d 65 20 43 6f 31 14  30 12 06 03 55 04 03 13  |cme Co1.0...U...|
-00000080  0b 73 6e 69 74 65 73 74  2e 63 6f 6d 30 1e 17 0d  |.snitest.com0...|
-00000090  31 32 30 34 31 31 31 37  34 30 33 35 5a 17 0d 31  |120411174035Z..1|
-000000a0  33 30 34 31 31 31 37 34  35 33 35 5a 30 28 31 10  |30411174535Z0(1.|
-000000b0  30 0e 06 03 55 04 0a 13  07 41 63 6d 65 20 43 6f  |0...U....Acme Co|
-000000c0  31 14 30 12 06 03 55 04  03 13 0b 73 6e 69 74 65  |1.0...U....snite|
-000000d0  73 74 2e 63 6f 6d 30 81  9d 30 0b 06 09 2a 86 48  |st.com0..0...*.H|
-000000e0  86 f7 0d 01 01 01 03 81  8d 00 30 81 89 02 81 81  |..........0.....|
-000000f0  00 bb 79 d6 f5 17 b5 e5  bf 46 10 d0 dc 69 be e6  |..y......F...i..|
-00000100  2b 07 43 5a d0 03 2d 8a  7a 43 85 b7 14 52 e7 a5  |+.CZ..-.zC...R..|
-00000110  65 4c 2c 78 b8 23 8c b5  b4 82 e5 de 1f 95 3b 7e  |eL,x.#........;~|
-00000120  62 a5 2c a5 33 d6 fe 12  5c 7a 56 fc f5 06 bf fa  |b.,.3...\zV.....|
-00000130  58 7b 26 3f b5 cd 04 d3  d0 c9 21 96 4a c7 f4 54  |X{&?......!.J..T|
-00000140  9f 5a bf ef 42 71 00 fe  18 99 07 7f 7e 88 7d 7d  |.Z..Bq......~.}}|
-00000150  f1 04 39 c4 a2 2e db 51  c9 7c e3 c0 4c 3b 32 66  |..9....Q.|..L;2f|
-00000160  01 cf af b1 1d b8 71 9a  1d db db 89 6b ae da 2d  |......q.....k..-|
-00000170  79 02 03 01 00 01 a3 32  30 30 30 0e 06 03 55 1d  |y......2000...U.|
-00000180  0f 01 01 ff 04 04 03 02  00 a0 30 0d 06 03 55 1d  |..........0...U.|
-00000190  0e 04 06 04 04 01 02 03  04 30 0f 06 03 55 1d 23  |.........0...U.#|
-000001a0  04 08 30 06 80 04 01 02  03 04 30 0b 06 09 2a 86  |..0.......0...*.|
-000001b0  48 86 f7 0d 01 01 05 03  81 81 00 89 c6 45 5f 1c  |H............E_.|
-000001c0  1f 5e f8 eb 1a b1 74 ee  24 39 05 9f 5c 42 59 bb  |.^....t.$9..\BY.|
-000001d0  1a 8d 86 cd b1 d0 56 f5  6a 71 7d a4 0e 95 ab 90  |......V.jq}.....|
-000001e0  f5 9e 8d ea f6 27 c1 57  99 50 94 db 08 02 26 6e  |.....'.W.P....&n|
-000001f0  b3 4f c6 84 2d ea 8a 4b  68 d9 c1 38 91 03 ab 84  |.O..-..Kh..8....|
-00000200  fb 9e 1f 85 d9 b5 d2 3f  f2 31 2c 86 70 fb b5 40  |.......?.1,.p..@|
-00000210  14 82 45 a4 eb af e2 64  d9 0c 8a 4c f4 f8 5b 0f  |..E....d...L..[.|
-00000220  ac 12 ac 2f c4 a3 15 4b  ad 52 46 28 68 af 96 c6  |.../...K.RF(h...|
-00000230  2c 65 25 d6 52 b6 e3 18  45 bd cc 16 03 03 00 04  |,e%.R...E.......|
-00000240  0e 00 00 00                                       |....|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 6f bc 33 c8 2d  |...........o.3.-|
-00000010  5a 50 65 2f 06 1a 45 16  9e 5b ab 97 43 b1 9e 96  |ZPe/..E..[..C...|
-00000020  b5 4a fe c3 7c e8 e4 07  ea 00 47 d2 51 23 11 ae  |.J..|.....G.Q#..|
-00000030  11 a8 79 71 b9 c6 82 17  40 ae c7 fc cd 05 4d 6d  |..yq.... at .....Mm|
-00000040  cb bc f1 ce 30 d0 10 37  4a e6 aa d3 14 fb bc 62  |....0..7J......b|
-00000050  eb 6c fa ec e1 e1 dd 63  39 95 0b 87 71 a0 83 ba  |.l.....c9...q...|
-00000060  bf 25 f8 a9 d3 c4 35 e1  88 23 85 56 c6 f4 0b 10  |.%....5..#.V....|
-00000070  d1 70 f8 0a 3d a1 08 17  ce 27 2d 4c a7 c5 b5 0d  |.p..=....'-L....|
-00000080  f2 43 b7 b9 02 21 7a eb  40 d2 66 14 03 03 00 01  |.C...!z. at .f.....|
-00000090  01 16 03 03 00 40 0a 93  47 0b 6e 40 ff cd 09 a5  |..... at ..G.n@....|
-000000a0  bc a4 c5 a8 c3 1c 0a fb  d4 2e 8a 2f 0c f3 d6 e1  |.........../....|
-000000b0  de 1f 56 2f 09 61 2d 31  d5 b1 29 6b f2 18 8b f2  |..V/.a-1..)k....|
-000000c0  0c 0e 09 5f ec 11 56 af  44 8b e5 2d 09 68 eb c2  |..._..V.D..-.h..|
-000000d0  91 4d 04 b2 10 02                                 |.M....|
+00000000  16 03 03 00 86 10 00 00  82 00 80 c9 3a 9d ea e3  |............:...|
+00000010  19 f1 07 77 61 ef 5a aa  ed 0f 26 b4 7a 45 db 05  |...wa.Z...&.zE..|
+00000020  bd 51 77 f5 ee 7b c1 83  9c 95 49 7b 70 5e 5b fe  |.Qw..{....I{p^[.|
+00000030  25 d2 3d 64 74 b8 a4 97  fd cb b9 75 7b 8f b0 59  |%.=dt......u{..Y|
+00000040  30 bf b3 41 ce 54 83 0a  ca 29 49 5a fe 29 4c 53  |0..A.T...)IZ.)LS|
+00000050  fb d6 6e 46 d9 f7 31 17  d6 ee f9 ac 41 82 22 11  |..nF..1.....A.".|
+00000060  a7 34 07 41 50 43 2f 83  f6 1f c6 c0 9d 4a 67 5a  |.4.APC/......JgZ|
+00000070  af 44 59 c0 00 33 be 24  f7 0a a4 fe 76 6b 03 05  |.DY..3.$....vk..|
+00000080  2e ec 4d 49 db 6e e5 0a  5f af 09 14 03 03 00 01  |..MI.n.._.......|
+00000090  01 16 03 03 00 40 ad 89  4d 25 a2 ce 98 8c cf b6  |..... at ..M%......|
+000000a0  f5 f4 76 6b e7 71 66 4a  f9 a7 67 fb 1d 6c a7 83  |..vk.qfJ..g..l..|
+000000b0  3b 1d 6a af 65 f2 c1 1d  97 03 5b c2 34 ee 3b 8e  |;.j.e.....[.4.;.|
+000000c0  cc bd 8f 3a b8 9b 4f 90  3f de 1e 97 1e 8e 61 37  |...:..O.?.....a7|
+000000d0  2d 30 35 84 3b 26                                 |-05.;&|
 >>> Flow 4 (server to client)
-00000000  15 03 03 00 02 02 14                              |.......|
+00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
+00000010  00 00 00 00 00 00 00 00  00 00 00 fe 23 de 33 4b  |............#.3K|
+00000020  55 f2 8e 73 09 ba ae f1  12 bd f7 15 75 90 8f 19  |U..s........u...|
+00000030  1b 19 b6 3f 2c 19 47 87  a9 43 d5 1e 85 fb 0c 90  |...?,.G..C......|
+00000040  c8 18 72 8f 08 6f 48 43  3c 5c 5a 17 03 03 00 40  |..r..oHC<\Z....@|
+00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000060  4d 44 d7 eb a3 94 00 74  90 9d c0 bd 8e 11 eb b6  |MD.....t........|
+00000070  93 43 c6 14 0d ba c2 aa  f0 f5 2d 85 9a 7c 27 44  |.C........-..|'D|
+00000080  fc d8 46 76 b2 21 4f 70  1a 9a df 9e 3a 8f a3 58  |..Fv.!Op....:..X|
+00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
+000000a0  00 00 00 00 00 91 0f c3  2a 98 79 57 39 3c 68 98  |........*.yW9<h.|
+000000b0  df 36 12 de e5 15 ee cb  80 ce 33 d9 20 95 33 38  |.6........3. .38|
+000000c0  8b d8 ed 8f 9b                                    |.....|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificateNotFound b/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificateNotFound
index 32c0d4a..8ba207b 100644
--- a/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificateNotFound
+++ b/src/crypto/tls/testdata/Server-TLSv12-SNI-GetCertificateNotFound
@@ -1,64 +1,81 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 6f 01 00 00  6b 03 03 64 29 6e 67 3d  |....o...k..d)ng=|
-00000010  5a 13 48 8a ae a5 67 6b  2f 5b 76 d2 c1 df 23 c6  |Z.H...gk/[v...#.|
-00000020  f9 52 26 33 ce 0b 03 f6  53 a7 db 00 00 04 00 2f  |.R&3....S....../|
-00000030  00 ff 02 01 00 00 3d 00  00 00 10 00 0e 00 00 0b  |......=.........|
-00000040  73 6e 69 74 65 73 74 2e  63 6f 6d 00 0d 00 20 00  |snitest.com... .|
-00000050  1e 06 01 06 02 06 03 05  01 05 02 05 03 04 01 04  |................|
-00000060  02 04 03 03 01 03 02 03  03 02 01 02 02 02 03 00  |................|
-00000070  0f 00 01 01                                       |....|
+00000000  16 03 01 00 71 01 00 00  6d 03 03 f8 16 6b 20 c3  |....q...m....k .|
+00000010  a4 cf fc ca 04 47 7a f9  cc d9 cf 4a 15 ff 6e 82  |.....Gz....J..n.|
+00000020  14 6a 91 91 7f f1 f4 42  e6 7c d4 00 00 04 00 2f  |.j.....B.|...../|
+00000030  00 ff 01 00 00 40 00 00  00 10 00 0e 00 00 0b 73  |..... at .........s|
+00000040  6e 69 74 65 73 74 2e 63  6f 6d 00 0d 00 20 00 1e  |nitest.com... ..|
+00000050  06 01 06 02 06 03 05 01  05 02 05 03 04 01 04 02  |................|
+00000060  04 03 03 01 03 02 03 03  02 01 02 02 02 03 00 16  |................|
+00000070  00 00 00 17 00 00                                 |......|
 >>> Flow 2 (server to client)
 00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 2f 00 00  |............./..|
-00000030  05 ff 01 00 01 00 16 03  03 02 00 0b 00 01 fc 00  |................|
-00000040  01 f9 00 01 f6 30 82 01  f2 30 82 01 5d a0 03 02  |.....0...0..]...|
-00000050  01 02 02 01 00 30 0b 06  09 2a 86 48 86 f7 0d 01  |.....0...*.H....|
-00000060  01 05 30 28 31 10 30 0e  06 03 55 04 0a 13 07 41  |..0(1.0...U....A|
-00000070  63 6d 65 20 43 6f 31 14  30 12 06 03 55 04 03 13  |cme Co1.0...U...|
-00000080  0b 73 6e 69 74 65 73 74  2e 63 6f 6d 30 1e 17 0d  |.snitest.com0...|
-00000090  31 32 30 34 31 31 31 37  34 30 33 35 5a 17 0d 31  |120411174035Z..1|
-000000a0  33 30 34 31 31 31 37 34  35 33 35 5a 30 28 31 10  |30411174535Z0(1.|
-000000b0  30 0e 06 03 55 04 0a 13  07 41 63 6d 65 20 43 6f  |0...U....Acme Co|
-000000c0  31 14 30 12 06 03 55 04  03 13 0b 73 6e 69 74 65  |1.0...U....snite|
-000000d0  73 74 2e 63 6f 6d 30 81  9d 30 0b 06 09 2a 86 48  |st.com0..0...*.H|
-000000e0  86 f7 0d 01 01 01 03 81  8d 00 30 81 89 02 81 81  |..........0.....|
-000000f0  00 bb 79 d6 f5 17 b5 e5  bf 46 10 d0 dc 69 be e6  |..y......F...i..|
-00000100  2b 07 43 5a d0 03 2d 8a  7a 43 85 b7 14 52 e7 a5  |+.CZ..-.zC...R..|
-00000110  65 4c 2c 78 b8 23 8c b5  b4 82 e5 de 1f 95 3b 7e  |eL,x.#........;~|
-00000120  62 a5 2c a5 33 d6 fe 12  5c 7a 56 fc f5 06 bf fa  |b.,.3...\zV.....|
-00000130  58 7b 26 3f b5 cd 04 d3  d0 c9 21 96 4a c7 f4 54  |X{&?......!.J..T|
-00000140  9f 5a bf ef 42 71 00 fe  18 99 07 7f 7e 88 7d 7d  |.Z..Bq......~.}}|
-00000150  f1 04 39 c4 a2 2e db 51  c9 7c e3 c0 4c 3b 32 66  |..9....Q.|..L;2f|
-00000160  01 cf af b1 1d b8 71 9a  1d db db 89 6b ae da 2d  |......q.....k..-|
-00000170  79 02 03 01 00 01 a3 32  30 30 30 0e 06 03 55 1d  |y......2000...U.|
-00000180  0f 01 01 ff 04 04 03 02  00 a0 30 0d 06 03 55 1d  |..........0...U.|
-00000190  0e 04 06 04 04 01 02 03  04 30 0f 06 03 55 1d 23  |.........0...U.#|
-000001a0  04 08 30 06 80 04 01 02  03 04 30 0b 06 09 2a 86  |..0.......0...*.|
-000001b0  48 86 f7 0d 01 01 05 03  81 81 00 89 c6 45 5f 1c  |H............E_.|
-000001c0  1f 5e f8 eb 1a b1 74 ee  24 39 05 9f 5c 42 59 bb  |.^....t.$9..\BY.|
-000001d0  1a 8d 86 cd b1 d0 56 f5  6a 71 7d a4 0e 95 ab 90  |......V.jq}.....|
-000001e0  f5 9e 8d ea f6 27 c1 57  99 50 94 db 08 02 26 6e  |.....'.W.P....&n|
-000001f0  b3 4f c6 84 2d ea 8a 4b  68 d9 c1 38 91 03 ab 84  |.O..-..Kh..8....|
-00000200  fb 9e 1f 85 d9 b5 d2 3f  f2 31 2c 86 70 fb b5 40  |.......?.1,.p..@|
-00000210  14 82 45 a4 eb af e2 64  d9 0c 8a 4c f4 f8 5b 0f  |..E....d...L..[.|
-00000220  ac 12 ac 2f c4 a3 15 4b  ad 52 46 28 68 af 96 c6  |.../...K.RF(h...|
-00000230  2c 65 25 d6 52 b6 e3 18  45 bd cc 16 03 03 00 04  |,e%.R...E.......|
-00000240  0e 00 00 00                                       |....|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  04 0e 00 00 00           |;............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 86 10 00 00  82 00 80 7b 3b b8 73 f0  |...........{;.s.|
-00000010  78 2c 75 91 ee 4a ac 6e  9d 08 8e ef dd 52 fb 38  |x,u..J.n.....R.8|
-00000020  d7 6f b3 39 8a 8c 3c dc  4b e0 a9 2b 0b de 9a d6  |.o.9..<.K..+....|
-00000030  38 72 ae 0f 76 87 4b f6  45 17 f6 fa b2 5a 07 30  |8r..v.K.E....Z.0|
-00000040  5b ef e7 40 e0 95 98 bf  8d 8d 5e 7a 6a ea 2d 2e  |[.. at ......^zj.-.|
-00000050  9c 99 e4 47 6b 4f 16 32  fb 0d a7 01 36 2e 06 f2  |...GkO.2....6...|
-00000060  65 74 b6 ed 07 51 60 7a  98 d6 77 36 f4 c7 f6 b1  |et...Q`z..w6....|
-00000070  f1 6a 3e 38 7c 79 5c c6  b4 53 5c 85 fb 0b 2c f9  |.j>8|y\..S\...,.|
-00000080  2c 60 97 c8 73 f7 22 ef  52 4c 10 14 03 03 00 01  |,`..s.".RL......|
-00000090  01 16 03 03 00 40 4c 73  6e 5e 66 9d 53 2e fa b7  |..... at Lsn^f.S...|
-000000a0  90 2d 52 55 13 d2 67 28  aa 1a 6f 62 42 ef 2f 4d  |.-RU..g(..obB./M|
-000000b0  04 1d ef 64 8f 1a 70 c0  d0 bf 06 72 ee db 3e 43  |...d..p....r..>C|
-000000c0  7f ed 37 bb a1 62 0c c5  c8 a9 c0 51 8f 77 95 b3  |..7..b.....Q.w..|
-000000d0  72 7e 01 89 c4 32                                 |r~...2|
+00000000  16 03 03 00 86 10 00 00  82 00 80 80 e8 00 cc 09  |................|
+00000010  fc 87 20 9f 2a 38 33 6f  cb 61 71 86 6d 55 6a 87  |.. .*83o.aq.mUj.|
+00000020  e0 22 78 62 4e 3b 98 5c  87 fd 3b 1c 73 d3 77 7e  |."xbN;.\..;.s.w~|
+00000030  a4 c3 6f d4 6d 82 65 40  0e 70 2f 24 e9 7d ff 49  |..o.m.e at .p/$.}.I|
+00000040  c7 bd 45 44 af ae a5 7a  06 06 5e 1e ce 31 73 4b  |..ED...z..^..1sK|
+00000050  4a 38 f0 11 ba 32 58 ab  a5 94 12 13 30 83 95 85  |J8...2X.....0...|
+00000060  f5 7e 8d a7 cc 6d 19 14  f9 b0 dc 64 e5 4d b1 7d  |.~...m.....d.M.}|
+00000070  e6 95 d4 4a 7f 85 11 5b  a7 c9 32 84 c2 ec 2e c3  |...J...[..2.....|
+00000080  40 fe 5c e2 cf 5b 96 8a  72 9f 9f 14 03 03 00 01  |@.\..[..r.......|
+00000090  01 16 03 03 00 40 a8 d2  5b 24 28 2b 86 1e c1 2e  |..... at ..[$(+....|
+000000a0  6f da 7a ac 6b bf 02 ea  10 5d 9c 71 fb 19 eb 17  |o.z.k....].q....|
+000000b0  19 b2 07 7c b9 df d0 6d  9f 80 cf 37 a0 2a 18 c9  |...|...m...7.*..|
+000000c0  e9 b5 9f 94 42 6a 6b 33  55 fb 6d 94 3b 79 ed 26  |....Bjk3U.m.;y.&|
+000000d0  5c 5a 7f 68 2c d8                                 |\Z.h,.|
 >>> Flow 4 (server to client)
-00000000  15 03 03 00 02 02 14                              |.......|
+00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |.......... at .....|
+00000010  00 00 00 00 00 00 00 00  00 00 00 18 e9 b5 96 14  |................|
+00000020  38 98 d4 23 cd e5 32 0e  09 ae b3 3b 90 a4 4d c2  |8..#..2....;..M.|
+00000030  e5 a8 df 72 e8 97 0b 67  cb 87 f4 d0 3e 52 ca d1  |...r...g....>R..|
+00000040  28 94 ed 88 6c cb 62 53  b2 a1 04 17 03 03 00 40  |(...l.bS.......@|
+00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000060  0e e3 b0 da 4b 19 ca 29  7b 1d c8 e3 0d d7 f2 97  |....K..){.......|
+00000070  b0 0b 6e f0 d2 4b f0 c4  ca 87 75 3c ae 66 e1 b3  |..n..K....u<.f..|
+00000080  06 e3 e6 90 54 fd 31 f7  5d 3b 6f de 0f d5 e4 09  |....T.1.];o.....|
+00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
+000000a0  00 00 00 00 00 ee a1 b0  94 b5 86 71 73 66 14 ac  |...........qsf..|
+000000b0  5c 4e 1b 67 27 af db b6  e3 44 15 38 b1 f5 e0 13  |\N.g'....D.8....|
+000000c0  a5 e1 82 c0 6a                                    |....j|
diff --git a/src/crypto/tls/testdata/Server-TLSv12-X25519-ECDHE-RSA-AES-GCM b/src/crypto/tls/testdata/Server-TLSv12-X25519-ECDHE-RSA-AES-GCM
new file mode 100644
index 0000000..89587e9
--- /dev/null
+++ b/src/crypto/tls/testdata/Server-TLSv12-X25519-ECDHE-RSA-AES-GCM
@@ -0,0 +1,79 @@
+>>> Flow 1 (client to server)
+00000000  16 03 01 00 73 01 00 00  6f 03 03 33 6d f4 4a 4b  |....s...o..3m.JK|
+00000010  48 35 ef 62 e9 bd 66 90  7e 73 62 bf 93 51 3d 90  |H5.b..f.~sb..Q=.|
+00000020  9e f1 17 ae bd 24 28 54  44 50 8e 00 00 04 c0 2f  |.....$(TDP...../|
+00000030  00 ff 01 00 00 42 00 0b  00 04 03 00 01 02 00 0a  |.....B..........|
+00000040  00 0a 00 08 00 1d 00 17  00 19 00 18 00 0d 00 20  |............... |
+00000050  00 1e 06 01 06 02 06 03  05 01 05 02 05 03 04 01  |................|
+00000060  04 02 04 03 03 01 03 02  03 03 02 01 02 02 02 03  |................|
+00000070  00 16 00 00 00 17 00 00                           |........|
+>>> Flow 2 (server to client)
+00000000  16 03 03 00 31 02 00 00  2d 03 03 00 00 00 00 00  |....1...-.......|
+00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 c0 2f 00 00  |............./..|
+00000030  05 ff 01 00 01 00 16 03  03 02 59 0b 00 02 55 00  |..........Y...U.|
+00000040  02 52 00 02 4f 30 82 02  4b 30 82 01 b4 a0 03 02  |.R..O0..K0......|
+00000050  01 02 02 09 00 e8 f0 9d  3f e2 5b ea a6 30 0d 06  |........?.[..0..|
+00000060  09 2a 86 48 86 f7 0d 01  01 0b 05 00 30 1f 31 0b  |.*.H........0.1.|
+00000070  30 09 06 03 55 04 0a 13  02 47 6f 31 10 30 0e 06  |0...U....Go1.0..|
+00000080  03 55 04 03 13 07 47 6f  20 52 6f 6f 74 30 1e 17  |.U....Go Root0..|
+00000090  0d 31 36 30 31 30 31 30  30 30 30 30 30 5a 17 0d  |.160101000000Z..|
+000000a0  32 35 30 31 30 31 30 30  30 30 30 30 5a 30 1a 31  |250101000000Z0.1|
+000000b0  0b 30 09 06 03 55 04 0a  13 02 47 6f 31 0b 30 09  |.0...U....Go1.0.|
+000000c0  06 03 55 04 03 13 02 47  6f 30 81 9f 30 0d 06 09  |..U....Go0..0...|
+000000d0  2a 86 48 86 f7 0d 01 01  01 05 00 03 81 8d 00 30  |*.H............0|
+000000e0  81 89 02 81 81 00 db 46  7d 93 2e 12 27 06 48 bc  |.......F}...'.H.|
+000000f0  06 28 21 ab 7e c4 b6 a2  5d fe 1e 52 45 88 7a 36  |.(!.~...]..RE.z6|
+00000100  47 a5 08 0d 92 42 5b c2  81 c0 be 97 79 98 40 fb  |G....B[.....y. at .|
+00000110  4f 6d 14 fd 2b 13 8b c2  a5 2e 67 d8 d4 09 9e d6  |Om..+.....g.....|
+00000120  22 38 b7 4a 0b 74 73 2b  c2 34 f1 d1 93 e5 96 d9  |"8.J.ts+.4......|
+00000130  74 7b f3 58 9f 6c 61 3c  c0 b0 41 d4 d9 2b 2b 24  |t{.X.la<..A..++$|
+00000140  23 77 5b 1c 3b bd 75 5d  ce 20 54 cf a1 63 87 1d  |#w[.;.u]. T..c..|
+00000150  1e 24 c4 f3 1d 1a 50 8b  aa b6 14 43 ed 97 a7 75  |.$....P....C...u|
+00000160  62 f4 14 c8 52 d7 02 03  01 00 01 a3 81 93 30 81  |b...R.........0.|
+00000170  90 30 0e 06 03 55 1d 0f  01 01 ff 04 04 03 02 05  |.0...U..........|
+00000180  a0 30 1d 06 03 55 1d 25  04 16 30 14 06 08 2b 06  |.0...U.%..0...+.|
+00000190  01 05 05 07 03 01 06 08  2b 06 01 05 05 07 03 02  |........+.......|
+000001a0  30 0c 06 03 55 1d 13 01  01 ff 04 02 30 00 30 19  |0...U.......0.0.|
+000001b0  06 03 55 1d 0e 04 12 04  10 9f 91 16 1f 43 43 3e  |..U..........CC>|
+000001c0  49 a6 de 6d b6 80 d7 9f  60 30 1b 06 03 55 1d 23  |I..m....`0...U.#|
+000001d0  04 14 30 12 80 10 48 13  49 4d 13 7e 16 31 bb a3  |..0...H.IM.~.1..|
+000001e0  01 d5 ac ab 6e 7b 30 19  06 03 55 1d 11 04 12 30  |....n{0...U....0|
+000001f0  10 82 0e 65 78 61 6d 70  6c 65 2e 67 6f 6c 61 6e  |...example.golan|
+00000200  67 30 0d 06 09 2a 86 48  86 f7 0d 01 01 0b 05 00  |g0...*.H........|
+00000210  03 81 81 00 9d 30 cc 40  2b 5b 50 a0 61 cb ba e5  |.....0. at +[P.a...|
+00000220  53 58 e1 ed 83 28 a9 58  1a a9 38 a4 95 a1 ac 31  |SX...(.X..8....1|
+00000230  5a 1a 84 66 3d 43 d3 2d  d9 0b f2 97 df d3 20 64  |Z..f=C.-...... d|
+00000240  38 92 24 3a 00 bc cf 9c  7d b7 40 20 01 5f aa d3  |8.$:....}.@ ._..|
+00000250  16 61 09 a2 76 fd 13 c3  cc e1 0c 5c ee b1 87 82  |.a..v......\....|
+00000260  f1 6c 04 ed 73 bb b3 43  77 8d 0c 1c f1 0f a1 d8  |.l..s..Cw.......|
+00000270  40 83 61 c9 4c 72 2b 9d  ae db 46 06 06 4d f4 c1  |@.a.Lr+...F..M..|
+00000280  b3 3e c0 d1 bd 42 d4 db  fe 3d 13 60 84 5c 21 d3  |.>...B...=.`.\!.|
+00000290  3b e9 fa e7 16 03 03 00  ac 0c 00 00 a8 03 00 1d  |;...............|
+000002a0  20 2f e5 7d a3 47 cd 62  43 15 28 da ac 5f bb 29  | /.}.G.bC.(.._.)|
+000002b0  07 30 ff f6 84 af c4 cf  c2 ed 90 99 5f 58 cb 3b  |.0.........._X.;|
+000002c0  74 05 01 00 80 61 aa 96  74 97 9f 2a 81 df 73 4d  |t....a..t..*..sM|
+000002d0  58 fb 8b 34 d9 51 02 1d  30 45 98 11 fa 20 cc 48  |X..4.Q..0E... .H|
+000002e0  18 8d 92 4a bc bf 34 c2  52 cc 7b 7d 93 32 f9 98  |...J..4.R.{}.2..|
+000002f0  eb d0 6d 58 4c 24 71 f1  78 cc ee 4d f8 26 26 d3  |..mXL$q.x..M.&&.|
+00000300  b0 1c 46 67 ff 75 fc b5  b3 75 31 f3 9d d6 51 07  |..Fg.u...u1...Q.|
+00000310  7a c1 2f 52 3f 88 23 f2  90 74 d0 77 6d 2b c7 31  |z./R?.#..t.wm+.1|
+00000320  3d 81 a8 b9 84 a6 8f 96  25 91 e8 31 3b e9 20 b8  |=.......%..1;. .|
+00000330  c4 11 68 da 58 0a ee 79  de fe 32 29 d6 24 b0 56  |..h.X..y..2).$.V|
+00000340  ab e8 b5 57 fc 16 03 03  00 04 0e 00 00 00        |...W..........|
+>>> Flow 3 (client to server)
+00000000  16 03 03 00 25 10 00 00  21 20 eb 0e 38 40 3f 32  |....%...! ..8@?2|
+00000010  a4 95 fb c4 de e5 82 9a  4b 46 37 de 29 e5 6b e6  |........KF7.).k.|
+00000020  44 bf f0 af 0c 62 19 bd  5c 0e 14 03 03 00 01 01  |D....b..\.......|
+00000030  16 03 03 00 28 67 ad 91  f6 8d 8a 39 f7 f2 a6 42  |....(g.....9...B|
+00000040  f2 8c 2f 1d b3 1d dd f1  88 65 7e 66 d2 d9 70 09  |../......e~f..p.|
+00000050  4e 12 90 0d 0b d5 a5 a6  20 bc 32 63 05           |N....... .2c.|
+>>> Flow 4 (server to client)
+00000000  14 03 03 00 01 01 16 03  03 00 28 00 00 00 00 00  |..........(.....|
+00000010  00 00 00 56 b1 b8 16 9a  27 c6 ee d4 7f b7 68 83  |...V....'.....h.|
+00000020  43 3b 04 92 ec cc c7 db  82 f8 7d 04 64 1d 55 cf  |C;........}.d.U.|
+00000030  02 69 ac 17 03 03 00 25  00 00 00 00 00 00 00 01  |.i.....%........|
+00000040  d6 69 51 5d 3b 00 93 c2  a6 19 97 7d bf a9 d9 96  |.iQ];......}....|
+00000050  43 1d ae 32 c3 52 1a f0  18 ba 10 4c e0 15 03 03  |C..2.R.....L....|
+00000060  00 1a 00 00 00 00 00 00  00 02 1e 8a 5e 37 c0 b1  |............^7..|
+00000070  0d 1e c9 6a 90 23 d6 4c  5c 47 5b bf              |...j.#.L\G[.|
diff --git a/src/crypto/tls/tls.go b/src/crypto/tls/tls.go
index 8eef884..f2e5aea 100644
--- a/src/crypto/tls/tls.go
+++ b/src/crypto/tls/tls.go
@@ -5,7 +5,7 @@
 // Package tls partially implements TLS 1.2, as specified in RFC 5246.
 package tls
 
-// BUG(agl): The crypto/tls package does not implement countermeasures
+// BUG(agl): The crypto/tls package only implements some countermeasures
 // against Lucky13 attacks on CBC-mode encryption. See
 // http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
 // https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
@@ -102,7 +102,7 @@ func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*
 	timeout := dialer.Timeout
 
 	if !dialer.Deadline.IsZero() {
-		deadlineTimeout := dialer.Deadline.Sub(time.Now())
+		deadlineTimeout := time.Until(dialer.Deadline)
 		if timeout == 0 || deadlineTimeout < timeout {
 			timeout = deadlineTimeout
 		}
@@ -135,7 +135,7 @@ func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*
 	// from the hostname we're connecting to.
 	if config.ServerName == "" {
 		// Make a copy to avoid polluting argument or default.
-		c := config.clone()
+		c := config.Clone()
 		c.ServerName = hostname
 		config = c
 	}
diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go
index 48b46a0..8933f4f 100644
--- a/src/crypto/tls/tls_test.go
+++ b/src/crypto/tls/tls_test.go
@@ -11,6 +11,7 @@ import (
 	"fmt"
 	"internal/testenv"
 	"io"
+	"io/ioutil"
 	"math"
 	"math/rand"
 	"net"
@@ -98,6 +99,7 @@ var keyPairTests = []struct {
 }
 
 func TestX509KeyPair(t *testing.T) {
+	t.Parallel()
 	var pem []byte
 	for _, test := range keyPairTests {
 		pem = []byte(test.cert + test.key)
@@ -241,7 +243,7 @@ func testConnReadNonzeroAndEOF(t *testing.T, delay time.Duration) error {
 			srvCh <- nil
 			return
 		}
-		serverConfig := testConfig.clone()
+		serverConfig := testConfig.Clone()
 		srv := Server(sconn, serverConfig)
 		if err := srv.Handshake(); err != nil {
 			serr = fmt.Errorf("handshake: %v", err)
@@ -251,7 +253,7 @@ func testConnReadNonzeroAndEOF(t *testing.T, delay time.Duration) error {
 		srvCh <- srv
 	}()
 
-	clientConfig := testConfig.clone()
+	clientConfig := testConfig.Clone()
 	conn, err := Dial("tcp", ln.Addr().String(), clientConfig)
 	if err != nil {
 		t.Fatal(err)
@@ -293,18 +295,20 @@ func TestTLSUniqueMatches(t *testing.T) {
 		for i := 0; i < 2; i++ {
 			sconn, err := ln.Accept()
 			if err != nil {
-				t.Fatal(err)
+				t.Error(err)
+				return
 			}
-			serverConfig := testConfig.clone()
+			serverConfig := testConfig.Clone()
 			srv := Server(sconn, serverConfig)
 			if err := srv.Handshake(); err != nil {
-				t.Fatal(err)
+				t.Error(err)
+				return
 			}
 			serverTLSUniques <- srv.ConnectionState().TLSUnique
 		}
 	}()
 
-	clientConfig := testConfig.clone()
+	clientConfig := testConfig.Clone()
 	clientConfig.ClientSessionCache = NewLRUClientSessionCache(1)
 	conn, err := Dial("tcp", ln.Addr().String(), clientConfig)
 	if err != nil {
@@ -394,7 +398,7 @@ func TestConnCloseBreakingWrite(t *testing.T) {
 			srvCh <- nil
 			return
 		}
-		serverConfig := testConfig.clone()
+		serverConfig := testConfig.Clone()
 		srv := Server(sconn, serverConfig)
 		if err := srv.Handshake(); err != nil {
 			serr = fmt.Errorf("handshake: %v", err)
@@ -414,7 +418,7 @@ func TestConnCloseBreakingWrite(t *testing.T) {
 		Conn: cconn,
 	}
 
-	clientConfig := testConfig.clone()
+	clientConfig := testConfig.Clone()
 	tconn := Client(conn, clientConfig)
 	if err := tconn.Handshake(); err != nil {
 		t.Fatal(err)
@@ -458,6 +462,112 @@ func TestConnCloseBreakingWrite(t *testing.T) {
 	}
 }
 
+func TestConnCloseWrite(t *testing.T) {
+	ln := newLocalListener(t)
+	defer ln.Close()
+
+	clientDoneChan := make(chan struct{})
+
+	serverCloseWrite := func() error {
+		sconn, err := ln.Accept()
+		if err != nil {
+			return fmt.Errorf("accept: %v", err)
+		}
+		defer sconn.Close()
+
+		serverConfig := testConfig.Clone()
+		srv := Server(sconn, serverConfig)
+		if err := srv.Handshake(); err != nil {
+			return fmt.Errorf("handshake: %v", err)
+		}
+		defer srv.Close()
+
+		data, err := ioutil.ReadAll(srv)
+		if err != nil {
+			return err
+		}
+		if len(data) > 0 {
+			return fmt.Errorf("Read data = %q; want nothing", data)
+		}
+
+		if err := srv.CloseWrite(); err != nil {
+			return fmt.Errorf("server CloseWrite: %v", err)
+		}
+
+		// Wait for clientCloseWrite to finish, so we know we
+		// tested the CloseWrite before we defer the
+		// sconn.Close above, which would also cause the
+		// client to unblock like CloseWrite.
+		<-clientDoneChan
+		return nil
+	}
+
+	clientCloseWrite := func() error {
+		defer close(clientDoneChan)
+
+		clientConfig := testConfig.Clone()
+		conn, err := Dial("tcp", ln.Addr().String(), clientConfig)
+		if err != nil {
+			return err
+		}
+		if err := conn.Handshake(); err != nil {
+			return err
+		}
+		defer conn.Close()
+
+		if err := conn.CloseWrite(); err != nil {
+			return fmt.Errorf("client CloseWrite: %v", err)
+		}
+
+		if _, err := conn.Write([]byte{0}); err != errShutdown {
+			return fmt.Errorf("CloseWrite error = %v; want errShutdown", err)
+		}
+
+		data, err := ioutil.ReadAll(conn)
+		if err != nil {
+			return err
+		}
+		if len(data) > 0 {
+			return fmt.Errorf("Read data = %q; want nothing", data)
+		}
+		return nil
+	}
+
+	errChan := make(chan error, 2)
+
+	go func() { errChan <- serverCloseWrite() }()
+	go func() { errChan <- clientCloseWrite() }()
+
+	for i := 0; i < 2; i++ {
+		select {
+		case err := <-errChan:
+			if err != nil {
+				t.Fatal(err)
+			}
+		case <-time.After(10 * time.Second):
+			t.Fatal("deadlock")
+		}
+	}
+
+	// Also test CloseWrite being called before the handshake is
+	// finished:
+	{
+		ln2 := newLocalListener(t)
+		defer ln2.Close()
+
+		netConn, err := net.Dial("tcp", ln2.Addr().String())
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer netConn.Close()
+		conn := Client(netConn, testConfig.Clone())
+
+		if err := conn.CloseWrite(); err != errEarlyCloseWrite {
+			t.Errorf("CloseWrite error = %v; want errEarlyCloseWrite", err)
+		}
+	}
+}
+
 func TestClone(t *testing.T) {
 	var c1 Config
 	v := reflect.ValueOf(&c1).Elem()
@@ -477,12 +587,12 @@ func TestClone(t *testing.T) {
 		case "Rand":
 			f.Set(reflect.ValueOf(io.Reader(os.Stdin)))
 			continue
-		case "Time", "GetCertificate":
+		case "Time", "GetCertificate", "GetConfigForClient", "VerifyPeerCertificate", "GetClientCertificate":
 			// DeepEqual can't compare functions.
 			continue
 		case "Certificates":
 			f.Set(reflect.ValueOf([]Certificate{
-				{Certificate: [][]byte{[]byte{'b'}}},
+				{Certificate: [][]byte{{'b'}}},
 			}))
 			continue
 		case "NameToCertificate":
@@ -494,6 +604,10 @@ func TestClone(t *testing.T) {
 		case "ClientSessionCache":
 			f.Set(reflect.ValueOf(NewLRUClientSessionCache(10)))
 			continue
+		case "KeyLogWriter":
+			f.Set(reflect.ValueOf(io.Writer(os.Stdout)))
+			continue
+
 		}
 
 		q, ok := quick.Value(f.Type(), rnd)
@@ -503,7 +617,11 @@ func TestClone(t *testing.T) {
 		f.Set(q)
 	}
 
-	c2 := c1.clone()
+	c2 := c1.Clone()
+	// DeepEqual also compares unexported fields, thus c2 needs to have run
+	// serverInit in order to be DeepEqual to c1. Cloning it and discarding
+	// the result is sufficient.
+	c2.Clone()
 
 	if !reflect.DeepEqual(&c1, c2) {
 		t.Errorf("clone failed to copy a field")
@@ -551,7 +669,8 @@ func throughput(b *testing.B, totalBytes int64, dynamicRecordSizingDisabled bool
 				// (cannot call b.Fatal in goroutine)
 				panic(fmt.Errorf("accept: %v", err))
 			}
-			serverConfig := testConfig.clone()
+			serverConfig := testConfig.Clone()
+			serverConfig.CipherSuites = nil // the defaults may prefer faster ciphers
 			serverConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled
 			srv := Server(sconn, serverConfig)
 			if err := srv.Handshake(); err != nil {
@@ -564,7 +683,8 @@ func throughput(b *testing.B, totalBytes int64, dynamicRecordSizingDisabled bool
 	}()
 
 	b.SetBytes(totalBytes)
-	clientConfig := testConfig.clone()
+	clientConfig := testConfig.Clone()
+	clientConfig.CipherSuites = nil // the defaults may prefer faster ciphers
 	clientConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled
 
 	buf := make([]byte, bufsize)
@@ -641,7 +761,7 @@ func latency(b *testing.B, bps int, dynamicRecordSizingDisabled bool) {
 				// (cannot call b.Fatal in goroutine)
 				panic(fmt.Errorf("accept: %v", err))
 			}
-			serverConfig := testConfig.clone()
+			serverConfig := testConfig.Clone()
 			serverConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled
 			srv := Server(&slowConn{sconn, bps}, serverConfig)
 			if err := srv.Handshake(); err != nil {
@@ -651,7 +771,7 @@ func latency(b *testing.B, bps int, dynamicRecordSizingDisabled bool) {
 		}
 	}()
 
-	clientConfig := testConfig.clone()
+	clientConfig := testConfig.Clone()
 	clientConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled
 
 	buf := make([]byte, 16384)
diff --git a/src/crypto/x509/cert_pool.go b/src/crypto/x509/cert_pool.go
index 8438bf6..fea33df 100644
--- a/src/crypto/x509/cert_pool.go
+++ b/src/crypto/x509/cert_pool.go
@@ -4,11 +4,7 @@
 
 package x509
 
-import (
-	"encoding/pem"
-	"errors"
-	"runtime"
-)
+import "encoding/pem"
 
 // CertPool is a set of certificates.
 type CertPool struct {
@@ -30,9 +26,6 @@ func NewCertPool() *CertPool {
 // Any mutations to the returned pool are not written to disk and do
 // not affect any other pool.
 func SystemCertPool() (*CertPool, error) {
-	if runtime.GOOS == "windows" {
-		return nil, errors.New("crypto/x509: system root pool is not available on Windows")
-	}
 	return loadSystemRoots()
 }
 
@@ -86,10 +79,8 @@ func (s *CertPool) AddCert(cert *Certificate) {
 	}
 
 	// Check that the certificate isn't being added twice.
-	for _, c := range s.certs {
-		if c.Equal(cert) {
-			return
-		}
+	if s.contains(cert) {
+		return
 	}
 
 	n := len(s.certs)
diff --git a/src/crypto/x509/pkix/pkix.go b/src/crypto/x509/pkix/pkix.go
index faad406..39fd78d 100644
--- a/src/crypto/x509/pkix/pkix.go
+++ b/src/crypto/x509/pkix/pkix.go
@@ -64,34 +64,36 @@ func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
 		if len(rdn) == 0 {
 			continue
 		}
-		atv := rdn[0]
-		n.Names = append(n.Names, atv)
-		value, ok := atv.Value.(string)
-		if !ok {
-			continue
-		}
 
-		t := atv.Type
-		if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
-			switch t[3] {
-			case 3:
-				n.CommonName = value
-			case 5:
-				n.SerialNumber = value
-			case 6:
-				n.Country = append(n.Country, value)
-			case 7:
-				n.Locality = append(n.Locality, value)
-			case 8:
-				n.Province = append(n.Province, value)
-			case 9:
-				n.StreetAddress = append(n.StreetAddress, value)
-			case 10:
-				n.Organization = append(n.Organization, value)
-			case 11:
-				n.OrganizationalUnit = append(n.OrganizationalUnit, value)
-			case 17:
-				n.PostalCode = append(n.PostalCode, value)
+		for _, atv := range rdn {
+			n.Names = append(n.Names, atv)
+			value, ok := atv.Value.(string)
+			if !ok {
+				continue
+			}
+
+			t := atv.Type
+			if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
+				switch t[3] {
+				case 3:
+					n.CommonName = value
+				case 5:
+					n.SerialNumber = value
+				case 6:
+					n.Country = append(n.Country, value)
+				case 7:
+					n.Locality = append(n.Locality, value)
+				case 8:
+					n.Province = append(n.Province, value)
+				case 9:
+					n.StreetAddress = append(n.StreetAddress, value)
+				case 10:
+					n.Organization = append(n.Organization, value)
+				case 11:
+					n.OrganizationalUnit = append(n.OrganizationalUnit, value)
+				case 17:
+					n.PostalCode = append(n.PostalCode, value)
+				}
 			}
 		}
 	}
diff --git a/src/crypto/x509/root_cgo_darwin.go b/src/crypto/x509/root_cgo_darwin.go
index d599174..ea86b60 100644
--- a/src/crypto/x509/root_cgo_darwin.go
+++ b/src/crypto/x509/root_cgo_darwin.go
@@ -7,7 +7,7 @@
 package x509
 
 /*
-#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060
+#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080
 #cgo LDFLAGS: -framework CoreFoundation -framework Security
 
 #include <errno.h>
@@ -73,11 +73,10 @@ int useOldCode() {
 //
 // On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
 // certificates of the system. On failure, the function returns -1.
-// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
 //
-// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
-// be released (using CFRelease) after we've consumed its content.
-int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
+// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after
+// we've consumed its content.
+int FetchPEMRoots(CFDataRef *pemRoots) {
 	if (useOldCode()) {
 		return FetchPEMRoots_MountainLion(pemRoots);
 	}
@@ -94,69 +93,23 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
 		return -1;
 	}
 
-	// kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
-	// but the Go linker's internal linking mode can't handle CFSTR relocations.
-	// Create our own dynamic string instead and release it below.
-	CFStringRef policy = CFStringCreateWithCString(NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
-
 	CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
-	CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
 	for (int i = 0; i < numDomains; i++) {
 		CFArrayRef certs = NULL;
+		// Only get certificates from domain that are trusted
 		OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
 		if (err != noErr) {
 			continue;
 		}
 
-		CFIndex numCerts = CFArrayGetCount(certs);
+		int numCerts = CFArrayGetCount(certs);
 		for (int j = 0; j < numCerts; j++) {
 			CFDataRef data = NULL;
 			CFErrorRef errRef = NULL;
-			CFArrayRef trustSettings = NULL;
 			SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
 			if (cert == NULL) {
 				continue;
 			}
-			// We only want trusted certs.
-			int untrusted = 0;
-			if (i != 0) {
-				// Certs found in the system domain are always trusted. If the user
-				// configures "Never Trust" on such a cert, it will also be found in the
-				// admin or user domain, causing it to be added to untrustedPemRoots. The
-				// Go code will then clean this up.
-
-				// Trust may be stored in any of the domains. According to Apple's
-				// SecTrustServer.c, "user trust settings overrule admin trust settings",
-				// so take the last trust settings array we find.
-				// Skip the system domain since it is always trusted.
-				for (int k = 1; k < numDomains; k++) {
-					CFArrayRef domainTrustSettings = NULL;
-					err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings);
-					if (err == errSecSuccess && domainTrustSettings != NULL) {
-						if (trustSettings) {
-							CFRelease(trustSettings);
-						}
-						trustSettings = domainTrustSettings;
-					}
-				}
-				if (trustSettings == NULL) {
-					// "this certificate must be verified to a known trusted certificate"; aka not a root.
-					continue;
-				}
-				for (CFIndex k = 0; k < CFArrayGetCount(trustSettings); k++) {
-					CFNumberRef cfNum;
-					CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, k);
-					if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){
-						SInt32 result = 0;
-						CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
-						// TODO: The rest of the dictionary specifies conditions for evaluation.
-						if (result == kSecTrustSettingsResultDeny) {
-							untrusted = 1;
-						}
-					}
-				}
-				CFRelease(trustSettings);
-			}
 			// We only want to add Root CAs, so make sure Subject and Issuer Name match
 			CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
 			if (errRef != NULL) {
@@ -185,16 +138,13 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
 			}
 
 			if (data != NULL) {
-				CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData;
-				CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
+				CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data));
 				CFRelease(data);
 			}
 		}
 		CFRelease(certs);
 	}
-	CFRelease(policy);
 	*pemRoots = combinedData;
-	*untrustedPemRoots = combinedUntrustedData;
 	return 0;
 }
 */
@@ -208,8 +158,7 @@ func loadSystemRoots() (*CertPool, error) {
 	roots := NewCertPool()
 
 	var data C.CFDataRef = nil
-	var untrustedData C.CFDataRef = nil
-	err := C.FetchPEMRoots(&data, &untrustedData)
+	err := C.FetchPEMRoots(&data)
 	if err == -1 {
 		// TODO: better error message
 		return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
@@ -218,19 +167,5 @@ func loadSystemRoots() (*CertPool, error) {
 	defer C.CFRelease(C.CFTypeRef(data))
 	buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
 	roots.AppendCertsFromPEM(buf)
-	if untrustedData == nil {
-		return roots, nil
-	}
-	defer C.CFRelease(C.CFTypeRef(untrustedData))
-	buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
-	untrustedRoots := NewCertPool()
-	untrustedRoots.AppendCertsFromPEM(buf)
-
-	trustedRoots := NewCertPool()
-	for _, c := range roots.certs {
-		if !untrustedRoots.contains(c) {
-			trustedRoots.AddCert(c)
-		}
-	}
-	return trustedRoots, nil
+	return roots, nil
 }
diff --git a/src/crypto/x509/root_darwin.go b/src/crypto/x509/root_darwin.go
index 59b303d..78de56c 100644
--- a/src/crypto/x509/root_darwin.go
+++ b/src/crypto/x509/root_darwin.go
@@ -6,27 +6,12 @@
 
 package x509
 
-import (
-	"bytes"
-	"encoding/pem"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"os/exec"
-	"strconv"
-	"sync"
-	"syscall"
-)
+import "os/exec"
 
 func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
 	return nil, nil
 }
 
-// This code is only used when compiling without cgo.
-// It is here, instead of root_nocgo_darwin.go, so that tests can check it
-// even if the tests are run with cgo enabled.
-// The linker will not include these unused functions in binaries built with cgo enabled.
-
 func execSecurityRoots() (*CertPool, error) {
 	cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
 	data, err := cmd.Output()
@@ -34,100 +19,7 @@ func execSecurityRoots() (*CertPool, error) {
 		return nil, err
 	}
 
-	var (
-		mu    sync.Mutex
-		roots = NewCertPool()
-	)
-	add := func(cert *Certificate) {
-		mu.Lock()
-		defer mu.Unlock()
-		roots.AddCert(cert)
-	}
-	blockCh := make(chan *pem.Block)
-	var wg sync.WaitGroup
-	for i := 0; i < 4; i++ {
-		wg.Add(1)
-		go func() {
-			defer wg.Done()
-			for block := range blockCh {
-				verifyCertWithSystem(block, add)
-			}
-		}()
-	}
-	for len(data) > 0 {
-		var block *pem.Block
-		block, data = pem.Decode(data)
-		if block == nil {
-			break
-		}
-		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
-			continue
-		}
-		blockCh <- block
-	}
-	close(blockCh)
-	wg.Wait()
+	roots := NewCertPool()
+	roots.AppendCertsFromPEM(data)
 	return roots, nil
 }
-
-func verifyCertWithSystem(block *pem.Block, add func(*Certificate)) {
-	data := pem.EncodeToMemory(block)
-	var cmd *exec.Cmd
-	if needsTmpFiles() {
-		f, err := ioutil.TempFile("", "cert")
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err)
-			return
-		}
-		defer os.Remove(f.Name())
-		if _, err := f.Write(data); err != nil {
-			fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
-			return
-		}
-		if err := f.Close(); err != nil {
-			fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
-			return
-		}
-		cmd = exec.Command("/usr/bin/security", "verify-cert", "-c", f.Name(), "-l")
-	} else {
-		cmd = exec.Command("/usr/bin/security", "verify-cert", "-c", "/dev/stdin", "-l")
-		cmd.Stdin = bytes.NewReader(data)
-	}
-	if cmd.Run() == nil {
-		// Non-zero exit means untrusted
-		cert, err := ParseCertificate(block.Bytes)
-		if err != nil {
-			return
-		}
-
-		add(cert)
-	}
-}
-
-var versionCache struct {
-	sync.Once
-	major int
-}
-
-// needsTmpFiles reports whether the OS is <= 10.11 (which requires real
-// files as arguments to the security command).
-func needsTmpFiles() bool {
-	versionCache.Do(func() {
-		release, err := syscall.Sysctl("kern.osrelease")
-		if err != nil {
-			return
-		}
-		for i, c := range release {
-			if c == '.' {
-				release = release[:i]
-				break
-			}
-		}
-		major, err := strconv.Atoi(release)
-		if err != nil {
-			return
-		}
-		versionCache.major = major
-	})
-	return versionCache.major <= 15
-}
diff --git a/src/crypto/x509/root_darwin_test.go b/src/crypto/x509/root_darwin_test.go
index c8ca3ea..8b6b151 100644
--- a/src/crypto/x509/root_darwin_test.go
+++ b/src/crypto/x509/root_darwin_test.go
@@ -29,7 +29,6 @@ func TestSystemRoots(t *testing.T) {
 		// On Mavericks, there are 212 bundled certs; require only
 		// 150 here, since this is just a sanity check, and the
 		// exact number will vary over time.
-		t.Logf("got %d roots", len(tt.certs))
 		if want, have := 150, len(tt.certs); have < want {
 			t.Fatalf("want at least %d system roots, have %d", want, have)
 		}
diff --git a/src/crypto/x509/root_linux.go b/src/crypto/x509/root_linux.go
index cfeca69..38dd72d 100644
--- a/src/crypto/x509/root_linux.go
+++ b/src/crypto/x509/root_linux.go
@@ -6,8 +6,9 @@ package x509
 
 // Possible certificate files; stop after finding one.
 var certFiles = []string{
-	"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
-	"/etc/pki/tls/certs/ca-bundle.crt",   // Fedora/RHEL
-	"/etc/ssl/ca-bundle.pem",             // OpenSUSE
-	"/etc/pki/tls/cacert.pem",            // OpenELEC
+	"/etc/ssl/certs/ca-certificates.crt",                // Debian/Ubuntu/Gentoo etc.
+	"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
+	"/etc/pki/tls/certs/ca-bundle.crt",                  // Fedora/RHEL 6
+	"/etc/ssl/ca-bundle.pem",                            // OpenSUSE
+	"/etc/pki/tls/cacert.pem",                           // OpenELEC
 }
diff --git a/src/crypto/x509/root_windows.go b/src/crypto/x509/root_windows.go
index 392c869..ca2fba5 100644
--- a/src/crypto/x509/root_windows.go
+++ b/src/crypto/x509/root_windows.go
@@ -225,4 +225,37 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
 	return chains, nil
 }
 
-func loadSystemRoots() (*CertPool, error) { return nil, nil }
+func loadSystemRoots() (*CertPool, error) {
+	const CRYPT_E_NOT_FOUND = 0x80092004
+
+	store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
+	if err != nil {
+		return nil, err
+	}
+	defer syscall.CertCloseStore(store, 0)
+
+	roots := NewCertPool()
+	var cert *syscall.CertContext
+	for {
+		cert, err = syscall.CertEnumCertificatesInStore(store, cert)
+		if err != nil {
+			if errno, ok := err.(syscall.Errno); ok {
+				if errno == CRYPT_E_NOT_FOUND {
+					break
+				}
+			}
+			return nil, err
+		}
+		if cert == nil {
+			break
+		}
+		// Copy the buf, since ParseCertificate does not create its own copy.
+		buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
+		buf2 := make([]byte, cert.Length)
+		copy(buf2, buf)
+		if c, err := ParseCertificate(buf2); err == nil {
+			roots.AddCert(c)
+		}
+	}
+	return roots, nil
+}
diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
index 85c083f..0d3de30 100644
--- a/src/crypto/x509/verify.go
+++ b/src/crypto/x509/verify.go
@@ -5,6 +5,7 @@
 package x509
 
 import (
+	"bytes"
 	"errors"
 	"fmt"
 	"net"
@@ -33,6 +34,9 @@ const (
 	// IncompatibleUsage results when the certificate's key usage indicates
 	// that it may only be used for a different purpose.
 	IncompatibleUsage
+	// NameMismatch results when the subject name of a parent certificate
+	// does not match the issuer name in the child.
+	NameMismatch
 )
 
 // CertificateInvalidError results when an odd error occurs. Users of this
@@ -54,6 +58,8 @@ func (e CertificateInvalidError) Error() string {
 		return "x509: too many intermediates for path length constraint"
 	case IncompatibleUsage:
 		return "x509: certificate specifies an incompatible key usage"
+	case NameMismatch:
+		return "x509: issuer name does not match subject from issuing certificate"
 	}
 	return "x509: unknown error"
 }
@@ -87,12 +93,16 @@ func (h HostnameError) Error() string {
 			valid = c.Subject.CommonName
 		}
 	}
+
+	if len(valid) == 0 {
+		return "x509: certificate is not valid for any names, but wanted to match " + h.Host
+	}
 	return "x509: certificate is valid for " + valid + ", not " + h.Host
 }
 
 // UnknownAuthorityError results when the certificate issuer is unknown
 type UnknownAuthorityError struct {
-	cert *Certificate
+	Cert *Certificate
 	// hintErr contains an error that may be helpful in determining why an
 	// authority wasn't found.
 	hintErr error
@@ -108,8 +118,9 @@ func (e UnknownAuthorityError) Error() string {
 		if len(certName) == 0 {
 			if len(e.hintCert.Subject.Organization) > 0 {
 				certName = e.hintCert.Subject.Organization[0]
+			} else {
+				certName = "serial:" + e.hintCert.SerialNumber.String()
 			}
-			certName = "serial:" + e.hintCert.SerialNumber.String()
 		}
 		s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName)
 	}
@@ -153,8 +164,40 @@ const (
 	rootCertificate
 )
 
+func matchNameConstraint(domain, constraint string) bool {
+	// The meaning of zero length constraints is not specified, but this
+	// code follows NSS and accepts them as valid for everything.
+	if len(constraint) == 0 {
+		return true
+	}
+
+	if len(domain) < len(constraint) {
+		return false
+	}
+
+	prefixLen := len(domain) - len(constraint)
+	if !strings.EqualFold(domain[prefixLen:], constraint) {
+		return false
+	}
+
+	if prefixLen == 0 {
+		return true
+	}
+
+	isSubdomain := domain[prefixLen-1] == '.'
+	constraintHasLeadingDot := constraint[0] == '.'
+	return isSubdomain != constraintHasLeadingDot
+}
+
 // isValid performs validity checks on the c.
 func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error {
+	if len(currentChain) > 0 {
+		child := currentChain[len(currentChain)-1]
+		if !bytes.Equal(child.RawIssuer, c.RawSubject) {
+			return CertificateInvalidError{c, NameMismatch}
+		}
+	}
+
 	now := opts.CurrentTime
 	if now.IsZero() {
 		now = time.Now()
@@ -165,12 +208,9 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
 
 	if len(c.PermittedDNSDomains) > 0 {
 		ok := false
-		for _, domain := range c.PermittedDNSDomains {
-			if opts.DNSName == domain ||
-				(strings.HasSuffix(opts.DNSName, domain) &&
-					len(opts.DNSName) >= 1+len(domain) &&
-					opts.DNSName[len(opts.DNSName)-len(domain)-1] == '.') {
-				ok = true
+		for _, constraint := range c.PermittedDNSDomains {
+			ok = matchNameConstraint(opts.DNSName, constraint)
+			if ok {
 				break
 			}
 		}
@@ -262,9 +302,13 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
 		}
 	}
 
-	candidateChains, err := c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts)
-	if err != nil {
-		return
+	var candidateChains [][]*Certificate
+	if opts.Roots.contains(c) {
+		candidateChains = append(candidateChains, []*Certificate{c})
+	} else {
+		if candidateChains, err = c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts); err != nil {
+			return nil, err
+		}
 	}
 
 	keyUsages := opts.KeyUsages
@@ -302,8 +346,16 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate
 
 func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) {
 	possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c)
+nextRoot:
 	for _, rootNum := range possibleRoots {
 		root := opts.Roots.certs[rootNum]
+
+		for _, cert := range currentChain {
+			if cert.Equal(root) {
+				continue nextRoot
+			}
+		}
+
 		err = root.isValid(rootCertificate, currentChain, opts)
 		if err != nil {
 			continue
@@ -316,7 +368,7 @@ nextIntermediate:
 	for _, intermediateNum := range possibleIntermediates {
 		intermediate := opts.Intermediates.certs[intermediateNum]
 		for _, cert := range currentChain {
-			if cert == intermediate {
+			if cert.Equal(intermediate) {
 				continue nextIntermediate
 			}
 		}
diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go
index bacf7de..15c4091 100644
--- a/src/crypto/x509/verify_test.go
+++ b/src/crypto/x509/verify_test.go
@@ -8,6 +8,7 @@ import (
 	"crypto/x509/pkix"
 	"encoding/pem"
 	"errors"
+	"fmt"
 	"runtime"
 	"strings"
 	"testing"
@@ -103,10 +104,6 @@ var verifyTests = []verifyTest{
 
 		expectedChains: [][]string{
 			{"Google", "Google Internet Authority", "GeoTrust"},
-			// TODO(agl): this is ok, but it would be nice if the
-			//            chain building didn't visit the same SPKI
-			//            twice.
-			{"Google", "Google Internet Authority", "GeoTrust", "GeoTrust"},
 		},
 		// CAPI doesn't build the chain with the duplicated GeoTrust
 		// entry so the results don't match. Thus we skip this test
@@ -129,12 +126,8 @@ var verifyTests = []verifyTest{
 		roots:         []string{startComRoot},
 		currentTime:   1302726541,
 
-		// Skip when using systemVerify, since Windows
-		// can only return a single chain to us (for now).
-		systemSkip: true,
 		expectedChains: [][]string{
 			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
-			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
 		},
 	},
 	{
@@ -235,6 +228,41 @@ var verifyTests = []verifyTest{
 			},
 		},
 	},
+	{
+		// Putting a certificate as a root directly should work as a
+		// way of saying “exactly this”.
+		leaf:        selfSigned,
+		roots:       []string{selfSigned},
+		currentTime: 1471624472,
+		dnsName:     "foo.example",
+		systemSkip:  true,
+
+		expectedChains: [][]string{
+			{"Acme Co"},
+		},
+	},
+	{
+		// Putting a certificate as a root directly should not skip
+		// other checks however.
+		leaf:        selfSigned,
+		roots:       []string{selfSigned},
+		currentTime: 1471624472,
+		dnsName:     "notfoo.example",
+		systemSkip:  true,
+
+		errorCallback: expectHostnameError,
+	},
+	{
+		// The issuer name in the leaf doesn't exactly match the
+		// subject name in the root. Go does not perform
+		// canonicalization and so should reject this. See issue 14955.
+		leaf:        issuerSubjectMatchLeaf,
+		roots:       []string{issuerSubjectMatchRoot},
+		currentTime: 1475787715,
+		systemSkip:  true,
+
+		errorCallback: expectSubjectIssuerMismatcthError,
+	},
 }
 
 func expectHostnameError(t *testing.T, i int, err error) (ok bool) {
@@ -262,10 +290,15 @@ func expectUsageError(t *testing.T, i int, err error) (ok bool) {
 }
 
 func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) {
-	if _, ok := err.(UnknownAuthorityError); !ok {
+	e, ok := err.(UnknownAuthorityError)
+	if !ok {
 		t.Errorf("#%d: error was not UnknownAuthorityError: %s", i, err)
 		return false
 	}
+	if e.Cert == nil {
+		t.Errorf("#%d: error was UnknownAuthorityError, but missing Cert: %s", i, err)
+		return false
+	}
 	return true
 }
 
@@ -289,6 +322,14 @@ func expectHashError(t *testing.T, i int, err error) bool {
 	return true
 }
 
+func expectSubjectIssuerMismatcthError(t *testing.T, i int, err error) (ok bool) {
+	if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NameMismatch {
+		t.Errorf("#%d: error was not a NameMismatch: %s", i, err)
+		return false
+	}
+	return true
+}
+
 func certificateFromPEM(pemBytes string) (*Certificate, error) {
 	block, _ := pem.Decode([]byte(pemBytes))
 	if block == nil {
@@ -1088,3 +1129,253 @@ Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
 c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
 mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
 -----END CERTIFICATE-----`
+
+const selfSigned = `-----BEGIN CERTIFICATE-----
+MIIC/DCCAeSgAwIBAgIRAK0SWRVmi67xU3z0gkgY+PkwDQYJKoZIhvcNAQELBQAw
+EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjA4MTkxNjMzNDdaFw0xNzA4MTkxNjMz
+NDdaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDWkm1kdCwxyKEt6OTmZitkmLGH8cQu9z7rUdrhW8lWNm4kh2SuaUWP
+pscBjda5iqg51aoKuWJR2rw6ElDne+X5eit2FT8zJgAU8v39lMFjbaVZfS9TFOYF
+w0Tk0Luo/PyKJpZnwhsP++iiGQiteJbndy8aLKmJ2MpLfpDGIgxEIyNb5dgoDi0D
+WReDCpE6K9WDYqvKVGnQ2Jvqqra6Gfx0tFkuqJxQuqA8aUOlPHcCH4KBZdNEoXdY
+YL3E4dCAh0YiDs80wNZx4cHqEM3L8gTEFqW2Tn1TSuPZO6gjJ9QPsuUZVjaMZuuO
+NVxqLGujZkDzARhC3fBpptMuaAfi20+BAgMBAAGjTTBLMA4GA1UdDwEB/wQEAwIF
+oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBYGA1UdEQQPMA2C
+C2Zvby5leGFtcGxlMA0GCSqGSIb3DQEBCwUAA4IBAQBPvvfnDhsHWt+/cfwdAVim
+4EDn+hYOMkTQwU0pouYIvY8QXYkZ8MBxpBtBMK4JhFU+ewSWoBAEH2dCCvx/BDxN
+UGTSJHMbsvJHcFvdmsvvRxOqQ/cJz7behx0cfoeHMwcs0/vWv8ms5wHesb5Ek7L0
+pl01FCBGTcncVqr6RK1r4fTpeCCfRIERD+YRJz8TtPH6ydesfLL8jIV40H8NiDfG
+vRAvOtNiKtPzFeQVdbRPOskC4rcHyPeiDAMAMixeLi63+CFty4da3r5lRezeedCE
+cw3ESZzThBwWqvPOtJdpXdm+r57pDW8qD+/0lY8wfImMNkQAyCUCLg/1Lxt/hrBj
+-----END CERTIFICATE-----`
+
+const issuerSubjectMatchRoot = `
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 161640039802297062 (0x23e42c281e55ae6)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: O=Golang, CN=Root ca
+        Validity
+            Not Before: Jan  1 00:00:00 2015 GMT
+            Not After : Jan  1 00:00:00 2025 GMT
+        Subject: O=Golang, CN=Root ca
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:e9:0e:7f:11:0c:e6:5a:e6:86:83:70:f6:51:07:
+                    2e:02:78:11:f5:b2:24:92:38:ee:26:62:02:c7:94:
+                    f1:3e:a1:77:6a:c0:8f:d5:22:68:b6:5d:e2:4c:da:
+                    e0:85:11:35:c2:92:72:49:8d:81:b4:88:97:6b:b7:
+                    fc:b2:44:5b:d9:4d:06:70:f9:0c:c6:8f:e9:b3:df:
+                    a3:6a:84:6c:43:59:be:9d:b2:d0:76:9b:c3:d7:fa:
+                    99:59:c3:b8:e5:f3:53:03:bd:49:d6:b3:cc:a2:43:
+                    fe:ad:c2:0b:b9:01:b8:56:29:94:03:24:a7:0d:28:
+                    21:29:a9:ae:94:5b:4a:f9:9f
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Certificate Sign
+            X509v3 Extended Key Usage:
+                TLS Web Server Authentication, TLS Web Client Authentication
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+            X509v3 Subject Key Identifier:
+                40:37:D7:01:FB:40:2F:B8:1C:7E:54:04:27:8C:59:01
+    Signature Algorithm: sha256WithRSAEncryption
+         6f:84:df:49:e0:99:d4:71:66:1d:32:86:56:cb:ea:5a:6b:0e:
+         00:6a:d1:5a:6e:1f:06:23:07:ff:cb:d1:1a:74:e4:24:43:0b:
+         aa:2a:a0:73:75:25:82:bc:bf:3f:a9:f8:48:88:ac:ed:3a:94:
+         3b:0d:d3:88:c8:67:44:61:33:df:71:6c:c5:af:ed:16:8c:bf:
+         82:f9:49:bb:e3:2a:07:53:36:37:25:77:de:91:a4:77:09:7f:
+         6f:b2:91:58:c4:05:89:ea:8e:fa:e1:3b:19:ef:f8:f6:94:b7:
+         7b:27:e6:e4:84:dd:2b:f5:93:f5:3c:d8:86:c5:38:01:56:5c:
+         9f:6d
+-----BEGIN CERTIFICATE-----
+MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE
+ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNhMB4XDTE1MDEwMTAwMDAwMFoXDTI1
+MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNh
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpDn8RDOZa5oaDcPZRBy4CeBH1
+siSSOO4mYgLHlPE+oXdqwI/VImi2XeJM2uCFETXCknJJjYG0iJdrt/yyRFvZTQZw
++QzGj+mz36NqhGxDWb6dstB2m8PX+plZw7jl81MDvUnWs8yiQ/6twgu5AbhWKZQD
+JKcNKCEpqa6UW0r5nwIDAQABo10wWzAOBgNVHQ8BAf8EBAMCAgQwHQYDVR0lBBYw
+FAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wGQYDVR0OBBIE
+EEA31wH7QC+4HH5UBCeMWQEwDQYJKoZIhvcNAQELBQADgYEAb4TfSeCZ1HFmHTKG
+VsvqWmsOAGrRWm4fBiMH/8vRGnTkJEMLqiqgc3Ulgry/P6n4SIis7TqUOw3TiMhn
+RGEz33Fsxa/tFoy/gvlJu+MqB1M2NyV33pGkdwl/b7KRWMQFieqO+uE7Ge/49pS3
+eyfm5ITdK/WT9TzYhsU4AVZcn20=
+-----END CERTIFICATE-----`
+
+const issuerSubjectMatchLeaf = `
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 16785088708916013734 (0xe8f09d3fe25beaa6)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: O=Golang, CN=Root CA
+        Validity
+            Not Before: Jan  1 00:00:00 2015 GMT
+            Not After : Jan  1 00:00:00 2025 GMT
+        Subject: O=Golang, CN=Leaf
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:db:46:7d:93:2e:12:27:06:48:bc:06:28:21:ab:
+                    7e:c4:b6:a2:5d:fe:1e:52:45:88:7a:36:47:a5:08:
+                    0d:92:42:5b:c2:81:c0:be:97:79:98:40:fb:4f:6d:
+                    14:fd:2b:13:8b:c2:a5:2e:67:d8:d4:09:9e:d6:22:
+                    38:b7:4a:0b:74:73:2b:c2:34:f1:d1:93:e5:96:d9:
+                    74:7b:f3:58:9f:6c:61:3c:c0:b0:41:d4:d9:2b:2b:
+                    24:23:77:5b:1c:3b:bd:75:5d:ce:20:54:cf:a1:63:
+                    87:1d:1e:24:c4:f3:1d:1a:50:8b:aa:b6:14:43:ed:
+                    97:a7:75:62:f4:14:c8:52:d7
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Key Usage: critical
+                Digital Signature, Key Encipherment
+            X509v3 Extended Key Usage:
+                TLS Web Server Authentication, TLS Web Client Authentication
+            X509v3 Basic Constraints: critical
+                CA:FALSE
+            X509v3 Subject Key Identifier:
+                9F:91:16:1F:43:43:3E:49:A6:DE:6D:B6:80:D7:9F:60
+            X509v3 Authority Key Identifier:
+                keyid:40:37:D7:01:FB:40:2F:B8:1C:7E:54:04:27:8C:59:01
+
+    Signature Algorithm: sha256WithRSAEncryption
+         8d:86:05:da:89:f5:1d:c5:16:14:41:b9:34:87:2b:5c:38:99:
+         e3:d9:5a:5b:7a:5b:de:0b:5c:08:45:09:6f:1c:9d:31:5f:08:
+         ca:7a:a3:99:da:83:0b:22:be:4f:02:35:91:4e:5d:5c:37:bf:
+         89:22:58:7d:30:76:d2:2f:d0:a0:ee:77:9e:77:c0:d6:19:eb:
+         ec:a0:63:35:6a:80:9b:80:1a:80:de:64:bc:40:38:3c:22:69:
+         ad:46:26:a2:3d:ea:f4:c2:92:49:16:03:96:ae:64:21:b9:7c:
+         ee:64:91:47:81:aa:b4:0c:09:2b:12:1a:b2:f3:af:50:b3:b1:
+         ce:24
+-----BEGIN CERTIFICATE-----
+MIICODCCAaGgAwIBAgIJAOjwnT/iW+qmMA0GCSqGSIb3DQEBCwUAMCMxDzANBgNV
+BAoTBkdvbGFuZzEQMA4GA1UEAxMHUm9vdCBDQTAeFw0xNTAxMDEwMDAwMDBaFw0y
+NTAxMDEwMDAwMDBaMCAxDzANBgNVBAoTBkdvbGFuZzENMAsGA1UEAxMETGVhZjCB
+nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA20Z9ky4SJwZIvAYoIat+xLaiXf4e
+UkWIejZHpQgNkkJbwoHAvpd5mED7T20U/SsTi8KlLmfY1Ame1iI4t0oLdHMrwjTx
+0ZPlltl0e/NYn2xhPMCwQdTZKyskI3dbHDu9dV3OIFTPoWOHHR4kxPMdGlCLqrYU
+Q+2Xp3Vi9BTIUtcCAwEAAaN3MHUwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG
+CCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMBkGA1UdDgQSBBCfkRYf
+Q0M+SabebbaA159gMBsGA1UdIwQUMBKAEEA31wH7QC+4HH5UBCeMWQEwDQYJKoZI
+hvcNAQELBQADgYEAjYYF2on1HcUWFEG5NIcrXDiZ49laW3pb3gtcCEUJbxydMV8I
+ynqjmdqDCyK+TwI1kU5dXDe/iSJYfTB20i/QoO53nnfA1hnr7KBjNWqAm4AagN5k
+vEA4PCJprUYmoj3q9MKSSRYDlq5kIbl87mSRR4GqtAwJKxIasvOvULOxziQ=
+-----END CERTIFICATE-----
+`
+
+var unknownAuthorityErrorTests = []struct {
+	cert     string
+	expected string
+}{
+	{selfSignedWithCommonName, "x509: certificate signed by unknown authority (possibly because of \"empty\" while trying to verify candidate authority certificate \"test\")"},
+	{selfSignedNoCommonNameWithOrgName, "x509: certificate signed by unknown authority (possibly because of \"empty\" while trying to verify candidate authority certificate \"ca\")"},
+	{selfSignedNoCommonNameNoOrgName, "x509: certificate signed by unknown authority (possibly because of \"empty\" while trying to verify candidate authority certificate \"serial:0\")"},
+}
+
+func TestUnknownAuthorityError(t *testing.T) {
+	for i, tt := range unknownAuthorityErrorTests {
+		der, _ := pem.Decode([]byte(tt.cert))
+		if der == nil {
+			t.Errorf("#%d: Unable to decode PEM block", i)
+		}
+		c, err := ParseCertificate(der.Bytes)
+		if err != nil {
+			t.Errorf("#%d: Unable to parse certificate -> %s", i, err)
+		}
+		uae := &UnknownAuthorityError{
+			Cert:     c,
+			hintErr:  fmt.Errorf("empty"),
+			hintCert: c,
+		}
+		actual := uae.Error()
+		if strings.Compare(actual, tt.expected) != 0 {
+			t.Errorf("#%d: UnknownAuthorityError.Error() response invalid actual: %s expected: %s", i, actual, tt.expected)
+		}
+	}
+}
+
+var nameConstraintTests = []struct {
+	constraint, domain string
+	shouldMatch        bool
+}{
+	{"", "anything.com", true},
+	{"example.com", "example.com", true},
+	{"example.com", "ExAmPle.coM", true},
+	{"example.com", "exampl1.com", false},
+	{"example.com", "www.ExAmPle.coM", true},
+	{"example.com", "notexample.com", false},
+	{".example.com", "example.com", false},
+	{".example.com", "www.example.com", true},
+	{".example.com", "www..example.com", false},
+}
+
+func TestNameConstraints(t *testing.T) {
+	for i, test := range nameConstraintTests {
+		result := matchNameConstraint(test.domain, test.constraint)
+		if result != test.shouldMatch {
+			t.Errorf("unexpected result for test #%d: domain=%s, constraint=%s, result=%t", i, test.domain, test.constraint, result)
+		}
+	}
+}
+
+const selfSignedWithCommonName = `-----BEGIN CERTIFICATE-----
+MIIDCjCCAfKgAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQKEwJjYTEL
+MAkGA1UEAxMCY2EwHhcNMTYwODI4MTcwOTE4WhcNMjEwODI3MTcwOTE4WjAcMQsw
+CQYDVQQKEwJjYTENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAOH55PfRsbvmcabfLLko1w/yuapY/hk13Cgmc3WE/Z1ZStxGiVxY
+gQVH9n4W/TbUsrep/TmcC4MV7xEm5252ArcgaH6BeQ4QOTFj/6Jx0RT7U/ix+79x
+8RRysf7OlzNpGIctwZEM7i/G+0ZfqX9ULxL/EW9tppSxMX1jlXZQarnU7BERL5cH
++G2jcbU9H28FXYishqpVYE9L7xrXMm61BAwvGKB0jcVW6JdhoAOSfQbbgp7JjIlq
+czXqUsv1UdORO/horIoJptynTvuARjZzyWatya6as7wyOgEBllE6BjPK9zpn+lp3
+tQ8dwKVqm/qBPhIrVqYG/Ec7pIv8mJfYabMCAwEAAaNZMFcwDgYDVR0PAQH/BAQD
+AgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAA
+MAoGA1UdDgQDBAEAMAwGA1UdIwQFMAOAAQAwDQYJKoZIhvcNAQELBQADggEBAAAM
+XMFphzq4S5FBcRdB2fRrmcoz+jEROBWvIH/1QUJeBEBz3ZqBaJYfBtQTvqCA5Rjw
+dxyIwVd1W3q3aSulM0tO62UCU6L6YeeY/eq8FmpD7nMJo7kCrXUUAMjxbYvS3zkT
+v/NErK6SgWnkQiPJBZNX1Q9+aSbLT/sbaCTdbWqcGNRuLGJkmqfIyoxRt0Hhpqsx
+jP5cBaVl50t4qoCuVIE9cOucnxYXnI7X5HpXWvu8Pfxo4SwVjb1az8Fk5s8ZnxGe
+fPB6Q3L/pKBe0SEe5GywpwtokPLB3lAygcuHbxp/1FlQ1NQZqq+vgXRIla26bNJf
+IuYkJwt6w+LH/9HZgf8=
+-----END CERTIFICATE-----`
+const selfSignedNoCommonNameWithOrgName = `-----BEGIN CERTIFICATE-----
+MIIC+zCCAeOgAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQKEwJjYTEL
+MAkGA1UEAxMCY2EwHhcNMTYwODI4MTgxMzQ4WhcNMjEwODI3MTgxMzQ4WjANMQsw
+CQYDVQQKEwJjYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL5EjrUa
+7EtOMxWiIgTzp2FlQvncPsG329O3l3uNGnbigb8TmNMw2M8UhoDjd84pnU5RAfqd
+8t5TJyw/ybnIKBN131Q2xX+gPQ0dFyMvcO+i1CUgCxmYZomKVA2MXO1RD1hLTYGS
+gOVjc3no3MBwd8uVQp0NStqJ1QvLtNG4Uy+B28qe+ZFGGbjGqx8/CU4A8Szlpf7/
+xAZR8w5qFUUlpA2LQYeHHJ5fQVXw7kyL1diNrKNi0G3qcY0IrBh++hT+hnEEXyXu
+g8a0Ux18hoE8D6rAr34rCZl6AWfqW5wjwm+N5Ns2ugr9U4N8uCKJYMPHb2CtdubU
+46IzVucpTfGLdaMCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQG
+CCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMAoGA1UdDgQDBAEAMAwG
+A1UdIwQFMAOAAQAwDQYJKoZIhvcNAQELBQADggEBAEn5SgVpJ3zjsdzPqK7Qd/sB
+bYd1qtPHlrszjhbHBg35C6mDgKhcv4o6N+fuC+FojZb8lIxWzJtvT9pQbfy/V6u3
+wOb816Hm71uiP89sioIOKCvSAstj/p9doKDOUaKOcZBTw0PS2m9eja8bnleZzBvK
+rD8cNkHf74v98KvBhcwBlDifVzmkWzMG6TL1EkRXUyLKiWgoTUFSkCDV927oXXMR
+DKnszq+AVw+K8hbeV2A7GqT7YfeqOAvSbatTDnDtKOPmlCnQui8A149VgZzXv7eU
+29ssJSqjUPyp58dlV6ZuynxPho1QVZUOQgnJToXIQ3/5vIvJRXy52GJCs4/Gh/w=
+-----END CERTIFICATE-----`
+const selfSignedNoCommonNameNoOrgName = `-----BEGIN CERTIFICATE-----
+MIIC7jCCAdagAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQKEwJjYTEL
+MAkGA1UEAxMCY2EwHhcNMTYwODI4MTgxOTQ1WhcNMjEwODI3MTgxOTQ1WjAAMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp3E+Jl6DpgzogHUW/i/AAcCM
+fnNJLOamNVKFGmmxhb4XTHxRaWoTzrlsyzIMS0WzivvJeZVe6mWbvuP2kZanKgIz
+35YXRTR9HbqkNTMuvnpUESzWxbGWE2jmt2+a/Jnz89FS4WIYRhF7nI2z8PvZOfrI
+2gETTT2tEpoF2S4soaYfm0DBeT8K0/rogAaf+oeUS6V+v3miRcAooJgpNJGu9kqm
+S0xKPn1RCFVjpiRd6YNS0xZirjYQIBMFBvoSoHjaOdgJptNRBprYPOxVJ/ItzGf0
+kPmzPFCx2tKfxV9HLYBPgxi+fP3IIx8aIYuJn8yReWtYEMYU11hDPeAFN5Gm+wID
+AQABo1kwVzAOBgNVHQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsG
+AQUFBwMBMAwGA1UdEwEB/wQCMAAwCgYDVR0OBAMEAQAwDAYDVR0jBAUwA4ABADAN
+BgkqhkiG9w0BAQsFAAOCAQEATZVOFeiCpPM5QysToLv+8k7Rjoqt6L5IxMUJGEpq
+4ENmldmwkhEKr9VnYEJY3njydnnTm97d9vOfnLj9nA9wMBODeOO3KL2uJR2oDnmM
+9z1NSe2aQKnyBb++DM3ZdikpHn/xEpGV19pYKFQVn35x3lpPh2XijqRDO/erKemb
+w67CoNRb81dy+4Q1lGpA8ORoLWh5fIq2t2eNGc4qB8vlTIKiESzAwu7u3sRfuWQi
+4R+gnfLd37FWflMHwztFbVTuNtPOljCX0LN7KcuoXYlr05RhQrmoN7fQHsrZMNLs
+8FVjHdKKu+uPstwd04Uy4BR/H2y1yerN9j/L6ZkMl98iiA==
+-----END CERTIFICATE-----`
diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go
index 9ad3cf2..d9077db 100644
--- a/src/crypto/x509/x509.go
+++ b/src/crypto/x509/x509.go
@@ -67,9 +67,8 @@ func marshalPublicKey(pub interface{}) (publicKeyBytes []byte, publicKeyAlgorith
 			return nil, pkix.AlgorithmIdentifier{}, err
 		}
 		publicKeyAlgorithm.Algorithm = oidPublicKeyRSA
-		// This is a NULL parameters value which is technically
-		// superfluous, but most other code includes it and, by
-		// doing this, we match their public key hashes.
+		// This is a NULL parameters value which is required by
+		// https://tools.ietf.org/html/rfc3279#section-2.3.1.
 		publicKeyAlgorithm.Parameters = asn1.RawValue{
 			Tag: 5,
 		}
@@ -179,21 +178,36 @@ const (
 	ECDSAWithSHA256
 	ECDSAWithSHA384
 	ECDSAWithSHA512
+	SHA256WithRSAPSS
+	SHA384WithRSAPSS
+	SHA512WithRSAPSS
 )
 
+func (algo SignatureAlgorithm) isRSAPSS() bool {
+	switch algo {
+	case SHA256WithRSAPSS, SHA384WithRSAPSS, SHA512WithRSAPSS:
+		return true
+	default:
+		return false
+	}
+}
+
 var algoName = [...]string{
-	MD2WithRSA:      "MD2-RSA",
-	MD5WithRSA:      "MD5-RSA",
-	SHA1WithRSA:     "SHA1-RSA",
-	SHA256WithRSA:   "SHA256-RSA",
-	SHA384WithRSA:   "SHA384-RSA",
-	SHA512WithRSA:   "SHA512-RSA",
-	DSAWithSHA1:     "DSA-SHA1",
-	DSAWithSHA256:   "DSA-SHA256",
-	ECDSAWithSHA1:   "ECDSA-SHA1",
-	ECDSAWithSHA256: "ECDSA-SHA256",
-	ECDSAWithSHA384: "ECDSA-SHA384",
-	ECDSAWithSHA512: "ECDSA-SHA512",
+	MD2WithRSA:       "MD2-RSA",
+	MD5WithRSA:       "MD5-RSA",
+	SHA1WithRSA:      "SHA1-RSA",
+	SHA256WithRSA:    "SHA256-RSA",
+	SHA384WithRSA:    "SHA384-RSA",
+	SHA512WithRSA:    "SHA512-RSA",
+	SHA256WithRSAPSS: "SHA256-RSAPSS",
+	SHA384WithRSAPSS: "SHA384-RSAPSS",
+	SHA512WithRSAPSS: "SHA512-RSAPSS",
+	DSAWithSHA1:      "DSA-SHA1",
+	DSAWithSHA256:    "DSA-SHA256",
+	ECDSAWithSHA1:    "ECDSA-SHA1",
+	ECDSAWithSHA256:  "ECDSA-SHA256",
+	ECDSAWithSHA384:  "ECDSA-SHA384",
+	ECDSAWithSHA512:  "ECDSA-SHA512",
 }
 
 func (algo SignatureAlgorithm) String() string {
@@ -269,12 +283,24 @@ var (
 	oidSignatureSHA256WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
 	oidSignatureSHA384WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
 	oidSignatureSHA512WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
+	oidSignatureRSAPSS          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
 	oidSignatureDSAWithSHA1     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
 	oidSignatureDSAWithSHA256   = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
 	oidSignatureECDSAWithSHA1   = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
 	oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
 	oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
 	oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
+
+	oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
+	oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
+	oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
+
+	oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
+
+	// oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA
+	// but it's specified by ISO. Microsoft's makecert.exe has been known
+	// to produce certificates with this OID.
+	oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29}
 )
 
 var signatureAlgorithmDetails = []struct {
@@ -286,9 +312,13 @@ var signatureAlgorithmDetails = []struct {
 	{MD2WithRSA, oidSignatureMD2WithRSA, RSA, crypto.Hash(0) /* no value for MD2 */},
 	{MD5WithRSA, oidSignatureMD5WithRSA, RSA, crypto.MD5},
 	{SHA1WithRSA, oidSignatureSHA1WithRSA, RSA, crypto.SHA1},
+	{SHA1WithRSA, oidISOSignatureSHA1WithRSA, RSA, crypto.SHA1},
 	{SHA256WithRSA, oidSignatureSHA256WithRSA, RSA, crypto.SHA256},
 	{SHA384WithRSA, oidSignatureSHA384WithRSA, RSA, crypto.SHA384},
 	{SHA512WithRSA, oidSignatureSHA512WithRSA, RSA, crypto.SHA512},
+	{SHA256WithRSAPSS, oidSignatureRSAPSS, RSA, crypto.SHA256},
+	{SHA384WithRSAPSS, oidSignatureRSAPSS, RSA, crypto.SHA384},
+	{SHA512WithRSAPSS, oidSignatureRSAPSS, RSA, crypto.SHA512},
 	{DSAWithSHA1, oidSignatureDSAWithSHA1, DSA, crypto.SHA1},
 	{DSAWithSHA256, oidSignatureDSAWithSHA256, DSA, crypto.SHA256},
 	{ECDSAWithSHA1, oidSignatureECDSAWithSHA1, ECDSA, crypto.SHA1},
@@ -297,12 +327,115 @@ var signatureAlgorithmDetails = []struct {
 	{ECDSAWithSHA512, oidSignatureECDSAWithSHA512, ECDSA, crypto.SHA512},
 }
 
-func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm {
-	for _, details := range signatureAlgorithmDetails {
-		if oid.Equal(details.oid) {
-			return details.algo
+// pssParameters reflects the parameters in an AlgorithmIdentifier that
+// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3
+type pssParameters struct {
+	// The following three fields are not marked as
+	// optional because the default values specify SHA-1,
+	// which is no longer suitable for use in signatures.
+	Hash         pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"`
+	MGF          pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"`
+	SaltLength   int                      `asn1:"explicit,tag:2"`
+	TrailerField int                      `asn1:"optional,explicit,tag:3,default:1"`
+}
+
+// rsaPSSParameters returns an asn1.RawValue suitable for use as the Parameters
+// in an AlgorithmIdentifier that specifies RSA PSS.
+func rsaPSSParameters(hashFunc crypto.Hash) asn1.RawValue {
+	var hashOID asn1.ObjectIdentifier
+
+	switch hashFunc {
+	case crypto.SHA256:
+		hashOID = oidSHA256
+	case crypto.SHA384:
+		hashOID = oidSHA384
+	case crypto.SHA512:
+		hashOID = oidSHA512
+	}
+
+	params := pssParameters{
+		Hash: pkix.AlgorithmIdentifier{
+			Algorithm: hashOID,
+			Parameters: asn1.RawValue{
+				Tag: 5, /* ASN.1 NULL */
+			},
+		},
+		MGF: pkix.AlgorithmIdentifier{
+			Algorithm: oidMGF1,
+		},
+		SaltLength:   hashFunc.Size(),
+		TrailerField: 1,
+	}
+
+	mgf1Params := pkix.AlgorithmIdentifier{
+		Algorithm: hashOID,
+		Parameters: asn1.RawValue{
+			Tag: 5, /* ASN.1 NULL */
+		},
+	}
+
+	var err error
+	params.MGF.Parameters.FullBytes, err = asn1.Marshal(mgf1Params)
+	if err != nil {
+		panic(err)
+	}
+
+	serialized, err := asn1.Marshal(params)
+	if err != nil {
+		panic(err)
+	}
+
+	return asn1.RawValue{FullBytes: serialized}
+}
+
+func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) SignatureAlgorithm {
+	if !ai.Algorithm.Equal(oidSignatureRSAPSS) {
+		for _, details := range signatureAlgorithmDetails {
+			if ai.Algorithm.Equal(details.oid) {
+				return details.algo
+			}
 		}
+		return UnknownSignatureAlgorithm
+	}
+
+	// RSA PSS is special because it encodes important parameters
+	// in the Parameters.
+
+	var params pssParameters
+	if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, &params); err != nil {
+		return UnknownSignatureAlgorithm
+	}
+
+	var mgf1HashFunc pkix.AlgorithmIdentifier
+	if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil {
+		return UnknownSignatureAlgorithm
 	}
+
+	// PSS is greatly overburdened with options. This code forces
+	// them into three buckets by requiring that the MGF1 hash
+	// function always match the message hash function (as
+	// recommended in
+	// https://tools.ietf.org/html/rfc3447#section-8.1), that the
+	// salt length matches the hash length, and that the trailer
+	// field has the default value.
+	asn1NULL := []byte{0x05, 0x00}
+	if !bytes.Equal(params.Hash.Parameters.FullBytes, asn1NULL) ||
+		!params.MGF.Algorithm.Equal(oidMGF1) ||
+		!mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) ||
+		!bytes.Equal(mgf1HashFunc.Parameters.FullBytes, asn1NULL) ||
+		params.TrailerField != 1 {
+		return UnknownSignatureAlgorithm
+	}
+
+	switch {
+	case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32:
+		return SHA256WithRSAPSS
+	case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48:
+		return SHA384WithRSAPSS
+	case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64:
+		return SHA512WithRSAPSS
+	}
+
 	return UnknownSignatureAlgorithm
 }
 
@@ -681,11 +814,11 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
 	switch algo {
 	case SHA1WithRSA, DSAWithSHA1, ECDSAWithSHA1:
 		hashType = crypto.SHA1
-	case SHA256WithRSA, DSAWithSHA256, ECDSAWithSHA256:
+	case SHA256WithRSA, SHA256WithRSAPSS, DSAWithSHA256, ECDSAWithSHA256:
 		hashType = crypto.SHA256
-	case SHA384WithRSA, ECDSAWithSHA384:
+	case SHA384WithRSA, SHA384WithRSAPSS, ECDSAWithSHA384:
 		hashType = crypto.SHA384
-	case SHA512WithRSA, ECDSAWithSHA512:
+	case SHA512WithRSA, SHA512WithRSAPSS, ECDSAWithSHA512:
 		hashType = crypto.SHA512
 	case MD2WithRSA, MD5WithRSA:
 		return InsecureAlgorithmError(algo)
@@ -703,7 +836,11 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
 
 	switch pub := publicKey.(type) {
 	case *rsa.PublicKey:
-		return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
+		if algo.isRSAPSS() {
+			return rsa.VerifyPSS(pub, hashType, digest, signature, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
+		} else {
+			return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
+		}
 	case *dsa.PublicKey:
 		dsaSig := new(dsaSignature)
 		if rest, err := asn1.Unmarshal(signature, dsaSig); err != nil {
@@ -738,7 +875,7 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
 
 // CheckCRLSignature checks that the signature in crl is from c.
 func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) error {
-	algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algorithm)
+	algo := getSignatureAlgorithmFromAI(crl.SignatureAlgorithm)
 	return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign())
 }
 
@@ -787,10 +924,19 @@ type distributionPointName struct {
 	RelativeName pkix.RDNSequence `asn1:"optional,tag:1"`
 }
 
+// asn1Null is the ASN.1 encoding of a NULL value.
+var asn1Null = []byte{5, 0}
+
 func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) {
 	asn1Data := keyData.PublicKey.RightAlign()
 	switch algo {
 	case RSA:
+		// RSA public keys must have a NULL in the parameters
+		// (https://tools.ietf.org/html/rfc3279#section-2.3.1).
+		if !bytes.Equal(keyData.Algorithm.Parameters.FullBytes, asn1Null) {
+			return nil, errors.New("x509: RSA key missing NULL parameters")
+		}
+
 		p := new(rsaPublicKey)
 		rest, err := asn1.Unmarshal(asn1Data, p)
 		if err != nil {
@@ -937,7 +1083,7 @@ func parseCertificate(in *certificate) (*Certificate, error) {
 
 	out.Signature = in.SignatureValue.RightAlign()
 	out.SignatureAlgorithm =
-		getSignatureAlgorithmFromOID(in.TBSCertificate.SignatureAlgorithm.Algorithm)
+		getSignatureAlgorithmFromAI(in.TBSCertificate.SignatureAlgorithm)
 
 	out.PublicKeyAlgorithm =
 		getPublicKeyAlgorithmFromOID(in.TBSCertificate.PublicKey.Algorithm.Algorithm)
@@ -1545,6 +1691,9 @@ func signingParamsForPublicKey(pub interface{}, requestedSigAlgo SignatureAlgori
 				err = errors.New("x509: cannot sign with hash function requested")
 				return
 			}
+			if requestedSigAlgo.isRSAPSS() {
+				sigAlgo.Parameters = rsaPSSParameters(hashFunc)
+			}
 			found = true
 			break
 		}
@@ -1577,6 +1726,10 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
 		return nil, errors.New("x509: certificate private key does not implement crypto.Signer")
 	}
 
+	if template.SerialNumber == nil {
+		return nil, errors.New("x509: no SerialNumber given")
+	}
+
 	hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(key.Public(), template.SignatureAlgorithm)
 	if err != nil {
 		return nil, err
@@ -1629,8 +1782,17 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
 	h.Write(tbsCertContents)
 	digest := h.Sum(nil)
 
+	var signerOpts crypto.SignerOpts
+	signerOpts = hashFunc
+	if template.SignatureAlgorithm != 0 && template.SignatureAlgorithm.isRSAPSS() {
+		signerOpts = &rsa.PSSOptions{
+			SaltLength: rsa.PSSSaltLengthEqualsHash,
+			Hash:       hashFunc,
+		}
+	}
+
 	var signature []byte
-	signature, err = key.Sign(rand, digest, hashFunc)
+	signature, err = key.Sign(rand, digest, signerOpts)
 	if err != nil {
 		return
 	}
@@ -1804,7 +1966,7 @@ func newRawAttributes(attributes []pkix.AttributeTypeAndValueSET) ([]asn1.RawVal
 		return nil, err
 	}
 	if len(rest) != 0 {
-		return nil, errors.New("x509: failed to unmarshall raw CSR Attributes")
+		return nil, errors.New("x509: failed to unmarshal raw CSR Attributes")
 	}
 	return rawAttributes, nil
 }
@@ -2035,7 +2197,7 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error
 		RawSubject:               in.TBSCSR.Subject.FullBytes,
 
 		Signature:          in.SignatureValue.RightAlign(),
-		SignatureAlgorithm: getSignatureAlgorithmFromOID(in.SignatureAlgorithm.Algorithm),
+		SignatureAlgorithm: getSignatureAlgorithmFromAI(in.SignatureAlgorithm),
 
 		PublicKeyAlgorithm: getPublicKeyAlgorithmFromOID(in.TBSCSR.PublicKey.Algorithm.Algorithm),
 
diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go
index c6448d3..354545c 100644
--- a/src/crypto/x509/x509_test.go
+++ b/src/crypto/x509/x509_test.go
@@ -24,6 +24,7 @@ import (
 	"net"
 	"os/exec"
 	"reflect"
+	"strings"
 	"testing"
 	"time"
 )
@@ -85,14 +86,21 @@ FF53oIpvxe/SCOymfWq/LW849Ytv3Xwod0+wzAP8STXG4HSELS4UedPYeHJJJYcZ
 -----END PUBLIC KEY-----
 `
 
-var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
-MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
-fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
-/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
-RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
-EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
-IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
-tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
+var pemPrivateKey = `
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCxoeCUW5KJxNPxMp+KmCxKLc1Zv9Ny+4CFqcUXVUYH69L3mQ7v
+IWrJ9GBfcaA7BPQqUlWxWM+OCEQZH1EZNIuqRMNQVuIGCbz5UQ8w6tS0gcgdeGX7
+J7jgCQ4RK3F/PuCM38QBLaHx988qG8NMc6VKErBjctCXFHQt14lerd5KpQIDAQAB
+AoGAYrf6Hbk+mT5AI33k2Jt1kcweodBP7UkExkPxeuQzRVe0KVJw0EkcFhywKpr1
+V5eLMrILWcJnpyHE5slWwtFHBG6a5fLaNtsBBtcAIfqTQ0Vfj5c6SzVaJv0Z5rOd
+7gQF6isy3t3w9IF3We9wXQKzT6q5ypPGdm6fciKQ8RnzREkCQQDZwppKATqQ41/R
+vhSj90fFifrGE6aVKC1hgSpxGQa4oIdsYYHwMzyhBmWW9Xv/R+fPyr8ZwPxp2c12
+33QwOLPLAkEA0NNUb+z4ebVVHyvSwF5jhfJxigim+s49KuzJ1+A2RaSApGyBZiwS
+rWvWkB471POAKUYt5ykIWVZ83zcceQiNTwJBAMJUFQZX5GDqWFc/zwGoKkeR49Yi
+MTXIvf7Wmv6E++eFcnT461FlGAUHRV+bQQXGsItR/opIG7mGogIkVXa3E1MCQARX
+AAA7eoZ9AEHflUeuLn9QJI/r0hyQQLEtrpwv6rDT1GCWaLII5HJ6NUFVf4TTcqxo
+6vdM4QGKTJoO+SaCyP0CQFdpcxSAuzpFcKv0IlJ8XzS/cy+mweCMwyJ1PFEc4FX6
+wg/HcAJWY60xZTJDFN+Qfx8ZQvBEin6c2/h+zZi5IVY=
 -----END RSA PRIVATE KEY-----
 `
 
@@ -127,13 +135,13 @@ func bigFromHexString(s string) *big.Int {
 
 var rsaPrivateKey = &rsa.PrivateKey{
 	PublicKey: rsa.PublicKey{
-		N: bigFromString("9353930466774385905609975137998169297361893554149986716853295022578535724979677252958524466350471210367835187480748268864277464700638583474144061408845077"),
+		N: bigFromString("124737666279038955318614287965056875799409043964547386061640914307192830334599556034328900586693254156136128122194531292927142396093148164407300419162827624945636708870992355233833321488652786796134504707628792159725681555822420087112284637501705261187690946267527866880072856272532711620639179596808018872997"),
 		E: 65537,
 	},
-	D: bigFromString("7266398431328116344057699379749222532279343923819063639497049039389899328538543087657733766554155839834519529439851673014800261285757759040931985506583861"),
+	D: bigFromString("69322600686866301945688231018559005300304807960033948687567105312977055197015197977971637657636780793670599180105424702854759606794705928621125408040473426339714144598640466128488132656829419518221592374964225347786430566310906679585739468938549035854760501049443920822523780156843263434219450229353270690889"),
 	Primes: []*big.Int{
-		bigFromString("98920366548084643601728869055592650835572950932266967461790948584315647051443"),
-		bigFromString("94560208308847015747498523884063394671606671904944666360068158221458669711639"),
+		bigFromString("11405025354575369741595561190164746858706645478381139288033759331174478411254205003127028642766986913445391069745480057674348716675323735886284176682955723"),
+		bigFromString("10937079261204603443118731009201819560867324167189758120988909645641782263430128449826989846631183550578761324239709121189827307416350485191350050332642639"),
 	},
 }
 
@@ -340,6 +348,9 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
 		{"RSA/ECDSA", &testPrivateKey.PublicKey, ecdsaPriv, false, ECDSAWithSHA384},
 		{"ECDSA/RSA", &ecdsaPriv.PublicKey, testPrivateKey, false, SHA256WithRSA},
 		{"ECDSA/ECDSA", &ecdsaPriv.PublicKey, ecdsaPriv, true, ECDSAWithSHA1},
+		{"RSAPSS/RSAPSS", &testPrivateKey.PublicKey, testPrivateKey, true, SHA256WithRSAPSS},
+		{"ECDSA/RSAPSS", &ecdsaPriv.PublicKey, testPrivateKey, false, SHA256WithRSAPSS},
+		{"RSAPSS/ECDSA", &testPrivateKey.PublicKey, ecdsaPriv, false, ECDSAWithSHA384},
 	}
 
 	testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
@@ -761,16 +772,76 @@ func TestVerifyCertificateWithDSASignature(t *testing.T) {
 	}
 }
 
+var rsaPSSSelfSignedPEM = `-----BEGIN CERTIFICATE-----
+MIIGHjCCA9KgAwIBAgIBdjBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAQUA
+oRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAQUAogMCASAwbjELMAkGA1UEBhMC
+SlAxHDAaBgNVBAoME0phcGFuZXNlIEdvdmVybm1lbnQxKDAmBgNVBAsMH1RoZSBN
+aW5pc3RyeSBvZiBGb3JlaWduIEFmZmFpcnMxFzAVBgNVBAMMDmUtcGFzc3BvcnRD
+U0NBMB4XDTEzMDUxNDA1MDczMFoXDTI5MDUxNDA1MDczMFowbjELMAkGA1UEBhMC
+SlAxHDAaBgNVBAoME0phcGFuZXNlIEdvdmVybm1lbnQxKDAmBgNVBAsMH1RoZSBN
+aW5pc3RyeSBvZiBGb3JlaWduIEFmZmFpcnMxFzAVBgNVBAMMDmUtcGFzc3BvcnRD
+U0NBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx/E3WRVxcCDXhoST
+8nVSLjW6hwM4Ni99AegWzcGtfGFo0zjFA1Cl5URqxauvYu3gQgQHBGA1CovWeGrl
+yVSRzOL1imcYsSgLOcnhVYB3Xcrof4ebv9+W+TwNdc9YzAwcj8rNd5nP6PKXIQ+W
+PCkEOXdyb80YEnxuT+NPjkVfFSPBS7QYZpvT2fwy4fZ0eh48253+7VleSmTO0mqj
+7TlzaG56q150SLZbhpOd8jD8bM/wACnLCPR88wj4hCcDLEwoLyY85HJCTIQQMnoT
+UpqyzEeupPREIm6yi4d8C9YqIWFn2YTnRcWcmMaJLzq+kYwKoudfnoC6RW2vzZXn
+defQs68IZuK+uALu9G3JWGPgu0CQGj0JNDT8zkiDV++4eNrZczWKjr1YnAL+VbLK
+bApwL2u19l2WDpfUklimhWfraqHNIUKU6CjZOG31RzXcplIj0mtqs0E1r7r357Es
+yFoB28iNo4cz1lCulh0E4WJzWzLZcT4ZspHHRCFyvYnXoibXEV1nULq8ByKKG0FS
+7nn4SseoV+8PvjHLPhmHGMvi4mxkbcXdV3wthHT1/HXdqY84A4xHWt1+sB/TpTek
+tDhFlEfcUygvTu58UtOnysomOVVeERmi7WSujfzKsGJAJYeetiA5R+zX7BxeyFVE
+qW0zh1Tkwh0S8LRe5diJh4+6FG0CAwEAAaNfMF0wHQYDVR0OBBYEFD+oahaikBTV
+Urk81Uz7kRS2sx0aMA4GA1UdDwEB/wQEAwIBBjAYBgNVHSAEETAPMA0GCyqDCIaP
+fgYFAQEBMBIGA1UdEwEB/wQIMAYBAf8CAQAwQQYJKoZIhvcNAQEKMDSgDzANBglg
+hkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEFAKIDAgEgA4IC
+AQAaxWBQn5CZuNBfyzL57mn31ukHUFd61OMROSX3PT7oCv1Dy+C2AdRlxOcbN3/n
+li0yfXUUqiY3COlLAHKRlkr97mLtxEFoJ0R8nVN2IQdChNQM/XSCzSGyY8NVa1OR
+TTpEWLnexJ9kvIdbFXwUqdTnAkOI0m7Rg8j+E+lRRHg1xDAA1qKttrtUj3HRQWf3
+kNTu628SiMvap6aIdncburaK56MP7gkR1Wr/ichOfjIA3Jgw2PapI31i0GqeMd66
+U1+lC9FeyMAJpuSVp/SoiYzYo+79SFcVoM2yw3yAnIKg7q9GLYYqzncdykT6C06c
+15gWFI6igmReAsD9ITSvYh0jLrLHfEYcPTOD3ZXJ4EwwHtWSoO3gq1EAtOYKu/Lv
+C8zfBsZcFdsHvsSiYeBU8Oioe42mguky3Ax9O7D805Ek6R68ra07MW/G4YxvV7IN
+2BfSaYy8MX9IG0ZMIOcoc0FeF5xkFmJ7kdrlTaJzC0IE9PNxNaH5QnOAFB8vxHcO
+FioUxb6UKdHcPLR1VZtAdTdTMjSJxUqD/35Cdfqs7oDJXz8f6TXO2Tdy6G++YUs9
+qsGZWxzFvvkXUkQSl0dQQ5jO/FtUJcAVXVVp20LxPemfatAHpW31WdJYeWSQWky2
++f9b5TXKXVyjlUL7uHxowWrT2AtTchDH22wTEtqLEF9Z3Q==
+-----END CERTIFICATE-----`
+
+func TestRSAPSSSelfSigned(t *testing.T) {
+	der, _ := pem.Decode([]byte(rsaPSSSelfSignedPEM))
+	if der == nil {
+		t.Fatal("Failed to find PEM block")
+	}
+
+	cert, err := ParseCertificate(der.Bytes)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err = cert.CheckSignatureFrom(cert); err != nil {
+		t.Fatal(err)
+	}
+}
+
 const pemCertificate = `-----BEGIN CERTIFICATE-----
-MIIB5DCCAZCgAwIBAgIBATALBgkqhkiG9w0BAQUwLTEQMA4GA1UEChMHQWNtZSBDbzEZMBcGA1UE
-AxMQdGVzdC5leGFtcGxlLmNvbTAeFw03MDAxMDEwMDE2NDBaFw03MDAxMDIwMzQ2NDBaMC0xEDAO
-BgNVBAoTB0FjbWUgQ28xGTAXBgNVBAMTEHRlc3QuZXhhbXBsZS5jb20wWjALBgkqhkiG9w0BAQED
-SwAwSAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0fd7Ai2KW5ToIwzFo
-fvJcS/STa6HA5gQenRUCAwEAAaOBnjCBmzAOBgNVHQ8BAf8EBAMCAAQwDwYDVR0TAQH/BAUwAwEB
-/zANBgNVHQ4EBgQEAQIDBDAPBgNVHSMECDAGgAQBAgMEMBsGA1UdEQQUMBKCEHRlc3QuZXhhbXBs
-ZS5jb20wDwYDVR0gBAgwBjAEBgIqAzAqBgNVHR4EIzAhoB8wDoIMLmV4YW1wbGUuY29tMA2CC2V4
-YW1wbGUuY29tMAsGCSqGSIb3DQEBBQNBAHKZKoS1wEQOGhgklx4+/yFYQlnqwKXvar/ZecQvJwui
-0seMQnwBhwdBkHfVIU2Fu5VUMRyxlf0ZNaDXcpU581k=
+MIIDATCCAemgAwIBAgIRAKQkkrFx1T/dgB/Go/xBM5swDQYJKoZIhvcNAQELBQAw
+EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjA4MTcyMDM2MDdaFw0xNzA4MTcyMDM2
+MDdaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDAoJtjG7M6InsWwIo+l3qq9u+g2rKFXNu9/mZ24XQ8XhV6PUR+5HQ4
+jUFWC58ExYhottqK5zQtKGkw5NuhjowFUgWB/VlNGAUBHtJcWR/062wYrHBYRxJH
+qVXOpYKbIWwFKoXu3hcpg/CkdOlDWGKoZKBCwQwUBhWE7MDhpVdQ+ZljUJWL+FlK
+yQK5iRsJd5TGJ6VUzLzdT4fmN2DzeK6GLeyMpVpU3sWV90JJbxWQ4YrzkKzYhMmB
+EcpXTG2wm+ujiHU/k2p8zlf8Sm7VBM/scmnMFt0ynNXop4FWvJzEm1G0xD2t+e2I
+5Utr04dOZPCgkm++QJgYhtZvgW7ZZiGTAgMBAAGjUjBQMA4GA1UdDwEB/wQEAwIF
+oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBsGA1UdEQQUMBKC
+EHRlc3QuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBADpqKQxrthH5InC7
+X96UP0OJCu/lLEMkrjoEWYIQaFl7uLPxKH5AmQPH4lYwF7u7gksR7owVG9QU9fs6
+1fK7II9CVgCd/4tZ0zm98FmU4D0lHGtPARrrzoZaqVZcAvRnFTlPX5pFkPhVjjai
+/mkxX9LpD8oK1445DFHxK5UjLMmPIIWd8EOi+v5a+hgGwnJpoW7hntSl8kHMtTmy
+fnnktsblSUV4lRCit0ymC7Ojhe+gzCCwkgs5kDzVVag+tnl/0e2DloIjASwOhpbH
+KVcg7fBd484ht/sS+l0dsB4KDOSpd8JzVDMF8OZqlaydizoJO0yWr9GbCN1+OKq5
+EhLrEqU=
 -----END CERTIFICATE-----`
 
 func TestCRLCreation(t *testing.T) {
@@ -867,7 +938,7 @@ func TestParsePEMCRL(t *testing.T) {
 func TestImports(t *testing.T) {
 	testenv.MustHaveGoRun(t)
 
-	if err := exec.Command("go", "run", "x509_test_import.go").Run(); err != nil {
+	if err := exec.Command(testenv.GoToolPath(t), "run", "x509_test_import.go").Run(); err != nil {
 		t.Errorf("failed to run x509_test_import.go: %s", err)
 	}
 }
@@ -1284,3 +1355,112 @@ func TestMD5(t *testing.T) {
 		t.Fatalf("certificate verification returned %v (%T), wanted InsecureAlgorithmError", err, err)
 	}
 }
+
+// certMissingRSANULL contains an RSA public key where the AlgorithmIdentifer
+// parameters are omitted rather than being an ASN.1 NULL.
+const certMissingRSANULL = `
+-----BEGIN CERTIFICATE-----
+MIIB7TCCAVigAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD
+bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTExMTIwODA3NTUxMloXDTEyMTIwNzA4
+MDAxMlowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGc
+MAsGCSqGSIb3DQEBAQOBjAAwgYgCgYBO0Hsx44Jk2VnAwoekXh6LczPHY1PfZpIG
+hPZk1Y/kNqcdK+izIDZFI7Xjla7t4PUgnI2V339aEu+H5Fto5OkOdOwEin/ekyfE
+ARl6vfLcPRSr0FTKIQzQTW6HLlzF0rtNS0/Otiz3fojsfNcCkXSmHgwa2uNKWi7e
+E5xMQIhZkwIDAQABozIwMDAOBgNVHQ8BAf8EBAMCAKAwDQYDVR0OBAYEBAECAwQw
+DwYDVR0jBAgwBoAEAQIDBDALBgkqhkiG9w0BAQUDgYEANh+zegx1yW43RmEr1b3A
+p0vMRpqBWHyFeSnIyMZn3TJWRSt1tukkqVCavh9a+hoV2cxVlXIWg7nCto/9iIw4
+hB2rXZIxE0/9gzvGnfERYraL7KtnvshksBFQRlgXa5kc0x38BvEO5ZaoDPl4ILdE
+GFGNEH5PlGffo05wc46QkYU=
+-----END CERTIFICATE-----`
+
+func TestRSAMissingNULLParameters(t *testing.T) {
+	block, _ := pem.Decode([]byte(certMissingRSANULL))
+	if _, err := ParseCertificate(block.Bytes); err == nil {
+		t.Error("unexpected success when parsing certificate with missing RSA NULL parameter")
+	} else if !strings.Contains(err.Error(), "missing NULL") {
+		t.Errorf("unrecognised error when parsing certificate with missing RSA NULL parameter: %s", err)
+	}
+}
+
+const certISOOID = `
+-----BEGIN CERTIFICATE-----
+MIIB5TCCAVKgAwIBAgIQtwyL3RPWV7dJQp34HwZG9DAJBgUrDgMCHQUAMBExDzAN
+BgNVBAMTBm15dGVzdDAeFw0xNjA4MDkyMjExMDVaFw0zOTEyMzEyMzU5NTlaMBEx
+DzANBgNVBAMTBm15dGVzdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEArzIH
+GsyDB3ohIGkkvijF2PTRUX1bvOtY1eUUpjwHyu0twpAKSuaQv2Ha+/63+aHe8O86
+BT+98wjXFX6RFSagtAujo80rIF2dSm33BGt18pDN8v6zp93dnAm0jRaSQrHJ75xw
+5O+S1oEYR1LtUoFJy6qB104j6aINBAgOiLIKiMkCAwEAAaNGMEQwQgYDVR0BBDsw
+OYAQVuYVQ/WDjdGSkZRlTtJDNKETMBExDzANBgNVBAMTBm15dGVzdIIQtwyL3RPW
+V7dJQp34HwZG9DAJBgUrDgMCHQUAA4GBABngrSkH7vG5lY4sa4AZF59lAAXqBVJE
+J4TBiKC62hCdZv18rBleP6ETfhbPg7pTs8p4ebQbpmtNxRS9Lw3MzQ8Ya5Ybwzj2
+NwBSyCtCQl7mrEg4nJqJl4A2EUhnET/oVxU0oTV/SZ3ziGXcY1oG1s6vidV7TZTu
+MCRtdSdaM7g3
+-----END CERTIFICATE-----`
+
+func TestISOOIDInCertificate(t *testing.T) {
+	block, _ := pem.Decode([]byte(certISOOID))
+	if cert, err := ParseCertificate(block.Bytes); err != nil {
+		t.Errorf("certificate with ISO OID failed to parse: %s", err)
+	} else if cert.SignatureAlgorithm == UnknownSignatureAlgorithm {
+		t.Errorf("ISO OID not recognised in certificate")
+	}
+}
+
+// certMultipleRDN contains a RelativeDistinguishedName with two elements (the
+// common name and serial number). This particular certificate was the first
+// such certificate in the “Pilot” Certificate Transparency log.
+const certMultipleRDN = `
+-----BEGIN CERTIFICATE-----
+MIIFRzCCBC+gAwIBAgIEOl59NTANBgkqhkiG9w0BAQUFADA9MQswCQYDVQQGEwJz
+aTEbMBkGA1UEChMSc3RhdGUtaW5zdGl0dXRpb25zMREwDwYDVQQLEwhzaWdvdi1j
+YTAeFw0xMjExMTYxMDUyNTdaFw0xNzExMTYxMjQ5MDVaMIGLMQswCQYDVQQGEwJz
+aTEbMBkGA1UEChMSc3RhdGUtaW5zdGl0dXRpb25zMRkwFwYDVQQLExB3ZWItY2Vy
+dGlmaWNhdGVzMRAwDgYDVQQLEwdTZXJ2ZXJzMTIwFAYDVQQFEw0xMjM2NDg0MDEw
+MDEwMBoGA1UEAxMTZXBvcnRhbC5tc3MuZWR1cy5zaTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAMrNkZH9MPuBTjMGNk3sJX8V+CkFx/4ru7RTlLS6dlYM
+098dtSfJ3s2w0p/1NB9UmR8j0yS0Kg6yoZ3ShsSO4DWBtcQD8820a6BYwqxxQTNf
+HSRZOc+N/4TQrvmK6t4k9Aw+YEYTMrWOU4UTeyhDeCcUsBdh7HjfWsVaqNky+2sv
+oic3zP5gF+2QfPkvOoHT3FLR8olNhViIE6Kk3eFIEs4dkq/ZzlYdLb8pHQoj/sGI
+zFmA5AFvm1HURqOmJriFjBwaCtn8AVEYOtQrnUCzJYu1ex8azyS2ZgYMX0u8A5Z/
+y2aMS/B2W+H79WcgLpK28vPwe7vam0oFrVytAd+u65ECAwEAAaOCAf4wggH6MA4G
+A1UdDwEB/wQEAwIFoDBABgNVHSAEOTA3MDUGCisGAQQBr1kBAwMwJzAlBggrBgEF
+BQcCARYZaHR0cDovL3d3dy5jYS5nb3Yuc2kvY3BzLzAfBgNVHREEGDAWgRRwb2Rw
+b3JhLm1pemtzQGdvdi5zaTCB8QYDVR0fBIHpMIHmMFWgU6BRpE8wTTELMAkGA1UE
+BhMCc2kxGzAZBgNVBAoTEnN0YXRlLWluc3RpdHV0aW9uczERMA8GA1UECxMIc2ln
+b3YtY2ExDjAMBgNVBAMTBUNSTDM5MIGMoIGJoIGGhldsZGFwOi8veDUwMC5nb3Yu
+c2kvb3U9c2lnb3YtY2Esbz1zdGF0ZS1pbnN0aXR1dGlvbnMsYz1zaT9jZXJ0aWZp
+Y2F0ZVJldm9jYXRpb25MaXN0P2Jhc2WGK2h0dHA6Ly93d3cuc2lnb3YtY2EuZ292
+LnNpL2NybC9zaWdvdi1jYS5jcmwwKwYDVR0QBCQwIoAPMjAxMjExMTYxMDUyNTda
+gQ8yMDE3MTExNjEyNDkwNVowHwYDVR0jBBgwFoAUHvjUU2uzgwbpBAZXAvmlv8ZY
+PHIwHQYDVR0OBBYEFGI1Duuu+wTGDZka/xHNbwcbM69ZMAkGA1UdEwQCMAAwGQYJ
+KoZIhvZ9B0EABAwwChsEVjcuMQMCA6gwDQYJKoZIhvcNAQEFBQADggEBAHny0K1y
+BQznrzDu3DDpBcGYguKU0dvU9rqsV1ua4nxkriSMWjgsX6XJFDdDW60I3P4VWab5
+ag5fZzbGqi8kva/CzGgZh+CES0aWCPy+4Gb8lwOTt+854/laaJvd6kgKTER7z7U9
+9C86Ch2y4sXNwwwPJ1A9dmrZJZOcJjS/WYZgwaafY2Hdxub5jqPE5nehwYUPVu9R
+uH6/skk4OEKcfOtN0hCnISOVuKYyS4ANARWRG5VGHIH06z3lGUVARFRJ61gtAprd
+La+fgSS+LVZ+kU2TkeoWAKvGq8MAgDq4D4Xqwekg7WKFeuyusi/NI5rm40XgjBMF
+DF72IUofoVt7wo0=
+-----END CERTIFICATE-----`
+
+func TestMultipleRDN(t *testing.T) {
+	block, _ := pem.Decode([]byte(certMultipleRDN))
+	cert, err := ParseCertificate(block.Bytes)
+	if err != nil {
+		t.Fatalf("certificate with two elements in an RDN failed to parse: %v", err)
+	}
+
+	if want := "eportal.mss.edus.si"; cert.Subject.CommonName != want {
+		t.Errorf("got common name of %q, but want %q", cert.Subject.CommonName, want)
+	}
+
+	if want := "1236484010010"; cert.Subject.SerialNumber != want {
+		t.Errorf("got serial number of %q, but want %q", cert.Subject.SerialNumber, want)
+	}
+}
+
+func TestSystemCertPool(t *testing.T) {
+	_, err := SystemCertPool()
+	if err != nil {
+		t.Fatal(err)
+	}
+}
diff --git a/src/database/sql/convert.go b/src/database/sql/convert.go
index 99aed23..4b4dfc4 100644
--- a/src/database/sql/convert.go
+++ b/src/database/sql/convert.go
@@ -17,12 +17,19 @@ import (
 
 var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
 
+func describeNamedValue(nv *driver.NamedValue) string {
+	if len(nv.Name) == 0 {
+		return fmt.Sprintf("$%d", nv.Ordinal)
+	}
+	return fmt.Sprintf("with name %q", nv.Name)
+}
+
 // driverArgs converts arguments from callers of Stmt.Exec and
 // Stmt.Query into driver Values.
 //
 // The statement ds may be nil, if no statement is available.
-func driverArgs(ds *driverStmt, args []interface{}) ([]driver.Value, error) {
-	dargs := make([]driver.Value, len(args))
+func driverArgs(ds *driverStmt, args []interface{}) ([]driver.NamedValue, error) {
+	nvargs := make([]driver.NamedValue, len(args))
 	var si driver.Stmt
 	if ds != nil {
 		si = ds.si
@@ -33,26 +40,39 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.Value, error) {
 	if !ok {
 		for n, arg := range args {
 			var err error
-			dargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
+			nv := &nvargs[n]
+			nv.Ordinal = n + 1
+			if np, ok := arg.(NamedArg); ok {
+				arg = np.Value
+				nvargs[n].Name = np.Name
+			}
+			nv.Value, err = driver.DefaultParameterConverter.ConvertValue(arg)
+
 			if err != nil {
-				return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
+				return nil, fmt.Errorf("sql: converting Exec argument %s type: %v", describeNamedValue(nv), err)
 			}
 		}
-		return dargs, nil
+		return nvargs, nil
 	}
 
 	// Let the Stmt convert its own arguments.
 	for n, arg := range args {
+		nv := &nvargs[n]
+		nv.Ordinal = n + 1
+		if np, ok := arg.(NamedArg); ok {
+			arg = np.Value
+			nv.Name = np.Name
+		}
 		// First, see if the value itself knows how to convert
 		// itself to a driver type. For example, a NullString
 		// struct changing into a string or nil.
-		if svi, ok := arg.(driver.Valuer); ok {
-			sv, err := svi.Value()
+		if vr, ok := arg.(driver.Valuer); ok {
+			sv, err := callValuerValue(vr)
 			if err != nil {
-				return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err)
+				return nil, fmt.Errorf("sql: argument %s from Value: %v", describeNamedValue(nv), err)
 			}
 			if !driver.IsValue(sv) {
-				return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv)
+				return nil, fmt.Errorf("sql: argument %s: non-subset type %T returned from Value", describeNamedValue(nv), sv)
 			}
 			arg = sv
 		}
@@ -66,18 +86,18 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.Value, error) {
 		// same error.
 		var err error
 		ds.Lock()
-		dargs[n], err = cc.ColumnConverter(n).ConvertValue(arg)
+		nv.Value, err = cc.ColumnConverter(n).ConvertValue(arg)
 		ds.Unlock()
 		if err != nil {
-			return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n, err)
+			return nil, fmt.Errorf("sql: converting argument %s type: %v", describeNamedValue(nv), err)
 		}
-		if !driver.IsValue(dargs[n]) {
-			return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
-				arg, dargs[n])
+		if !driver.IsValue(nv.Value) {
+			return nil, fmt.Errorf("sql: for argument %s, driver ColumnConverter error converted %T to unsupported type %T",
+				describeNamedValue(nv), arg, nv.Value)
 		}
 	}
 
-	return dargs, nil
+	return nvargs, nil
 }
 
 // convertAssign copies to dest the value in src, converting it if possible.
@@ -330,3 +350,25 @@ func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) {
 	}
 	return
 }
+
+var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
+
+// callValuerValue returns vr.Value(), with one exception:
+// If vr.Value is an auto-generated method on a pointer type and the
+// pointer is nil, it would panic at runtime in the panicwrap
+// method. Treat it like nil instead.
+// Issue 8415.
+//
+// This is so people can implement driver.Value on value types and
+// still use nil pointers to those types to mean nil/NULL, just like
+// string/*string.
+//
+// This function is mirrored in the database/sql/driver package.
+func callValuerValue(vr driver.Valuer) (v driver.Value, err error) {
+	if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr &&
+		rv.IsNil() &&
+		rv.Type().Elem().Implements(valuerReflectType) {
+		return nil, nil
+	}
+	return vr.Value()
+}
diff --git a/src/database/sql/convert_test.go b/src/database/sql/convert_test.go
index ab81f2f..4dfab1f 100644
--- a/src/database/sql/convert_test.go
+++ b/src/database/sql/convert_test.go
@@ -9,6 +9,7 @@ import (
 	"fmt"
 	"reflect"
 	"runtime"
+	"strings"
 	"testing"
 	"time"
 )
@@ -389,3 +390,85 @@ func TestUserDefinedBytes(t *testing.T) {
 		t.Fatal("userDefinedBytes got potentially dirty driver memory")
 	}
 }
+
+type Valuer_V string
+
+func (v Valuer_V) Value() (driver.Value, error) {
+	return strings.ToUpper(string(v)), nil
+}
+
+type Valuer_P string
+
+func (p *Valuer_P) Value() (driver.Value, error) {
+	if p == nil {
+		return "nil-to-str", nil
+	}
+	return strings.ToUpper(string(*p)), nil
+}
+
+func TestDriverArgs(t *testing.T) {
+	var nilValuerVPtr *Valuer_V
+	var nilValuerPPtr *Valuer_P
+	var nilStrPtr *string
+	tests := []struct {
+		args []interface{}
+		want []driver.NamedValue
+	}{
+		0: {
+			args: []interface{}{Valuer_V("foo")},
+			want: []driver.NamedValue{
+				driver.NamedValue{
+					Ordinal: 1,
+					Value:   "FOO",
+				},
+			},
+		},
+		1: {
+			args: []interface{}{nilValuerVPtr},
+			want: []driver.NamedValue{
+				driver.NamedValue{
+					Ordinal: 1,
+					Value:   nil,
+				},
+			},
+		},
+		2: {
+			args: []interface{}{nilValuerPPtr},
+			want: []driver.NamedValue{
+				driver.NamedValue{
+					Ordinal: 1,
+					Value:   "nil-to-str",
+				},
+			},
+		},
+		3: {
+			args: []interface{}{"plain-str"},
+			want: []driver.NamedValue{
+				driver.NamedValue{
+					Ordinal: 1,
+					Value:   "plain-str",
+				},
+			},
+		},
+		4: {
+			args: []interface{}{nilStrPtr},
+			want: []driver.NamedValue{
+				driver.NamedValue{
+					Ordinal: 1,
+					Value:   nil,
+				},
+			},
+		},
+	}
+	for i, tt := range tests {
+		ds := new(driverStmt)
+		got, err := driverArgs(ds, tt.args)
+		if err != nil {
+			t.Errorf("test[%d]: %v", i, err)
+			continue
+		}
+		if !reflect.DeepEqual(got, tt.want) {
+			t.Errorf("test[%d]: got %v, want %v", i, got, tt.want)
+		}
+	}
+}
diff --git a/src/database/sql/ctxutil.go b/src/database/sql/ctxutil.go
new file mode 100644
index 0000000..7c05ce2
--- /dev/null
+++ b/src/database/sql/ctxutil.go
@@ -0,0 +1,156 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sql
+
+import (
+	"context"
+	"database/sql/driver"
+	"errors"
+)
+
+func ctxDriverPrepare(ctx context.Context, ci driver.Conn, query string) (driver.Stmt, error) {
+	if ciCtx, is := ci.(driver.ConnPrepareContext); is {
+		return ciCtx.PrepareContext(ctx, query)
+	}
+	si, err := ci.Prepare(query)
+	if err == nil {
+		select {
+		default:
+		case <-ctx.Done():
+			si.Close()
+			return nil, ctx.Err()
+		}
+	}
+	return si, err
+}
+
+func ctxDriverExec(ctx context.Context, execer driver.Execer, query string, nvdargs []driver.NamedValue) (driver.Result, error) {
+	if execerCtx, is := execer.(driver.ExecerContext); is {
+		return execerCtx.ExecContext(ctx, query, nvdargs)
+	}
+	dargs, err := namedValueToValue(nvdargs)
+	if err != nil {
+		return nil, err
+	}
+
+	resi, err := execer.Exec(query, dargs)
+	if err == nil {
+		select {
+		default:
+		case <-ctx.Done():
+			return resi, ctx.Err()
+		}
+	}
+	return resi, err
+}
+
+func ctxDriverQuery(ctx context.Context, queryer driver.Queryer, query string, nvdargs []driver.NamedValue) (driver.Rows, error) {
+	if queryerCtx, is := queryer.(driver.QueryerContext); is {
+		ret, err := queryerCtx.QueryContext(ctx, query, nvdargs)
+		return ret, err
+	}
+	dargs, err := namedValueToValue(nvdargs)
+	if err != nil {
+		return nil, err
+	}
+
+	rowsi, err := queryer.Query(query, dargs)
+	if err == nil {
+		select {
+		default:
+		case <-ctx.Done():
+			rowsi.Close()
+			return nil, ctx.Err()
+		}
+	}
+	return rowsi, err
+}
+
+func ctxDriverStmtExec(ctx context.Context, si driver.Stmt, nvdargs []driver.NamedValue) (driver.Result, error) {
+	if siCtx, is := si.(driver.StmtExecContext); is {
+		return siCtx.ExecContext(ctx, nvdargs)
+	}
+	dargs, err := namedValueToValue(nvdargs)
+	if err != nil {
+		return nil, err
+	}
+
+	resi, err := si.Exec(dargs)
+	if err == nil {
+		select {
+		default:
+		case <-ctx.Done():
+			return resi, ctx.Err()
+		}
+	}
+	return resi, err
+}
+
+func ctxDriverStmtQuery(ctx context.Context, si driver.Stmt, nvdargs []driver.NamedValue) (driver.Rows, error) {
+	if siCtx, is := si.(driver.StmtQueryContext); is {
+		return siCtx.QueryContext(ctx, nvdargs)
+	}
+	dargs, err := namedValueToValue(nvdargs)
+	if err != nil {
+		return nil, err
+	}
+
+	rowsi, err := si.Query(dargs)
+	if err == nil {
+		select {
+		default:
+		case <-ctx.Done():
+			rowsi.Close()
+			return nil, ctx.Err()
+		}
+	}
+	return rowsi, err
+}
+
+var errLevelNotSupported = errors.New("sql: selected isolation level is not supported")
+
+func ctxDriverBegin(ctx context.Context, ci driver.Conn) (driver.Tx, error) {
+	if ciCtx, is := ci.(driver.ConnBeginContext); is {
+		return ciCtx.BeginContext(ctx)
+	}
+
+	if ctx.Done() == context.Background().Done() {
+		return ci.Begin()
+	}
+
+	// Check the transaction level in ctx. If set and non-default
+	// then return an error here as the BeginContext driver value is not supported.
+	if level, ok := driver.IsolationFromContext(ctx); ok && level != driver.IsolationLevel(LevelDefault) {
+		return nil, errors.New("sql: driver does not support non-default isolation level")
+	}
+
+	// Check for a read-only parameter in ctx. If a read-only transaction is
+	// requested return an error as the BeginContext driver value is not supported.
+	if ro := driver.ReadOnlyFromContext(ctx); ro {
+		return nil, errors.New("sql: driver does not support read-only transactions")
+	}
+
+	txi, err := ci.Begin()
+	if err == nil {
+		select {
+		default:
+		case <-ctx.Done():
+			txi.Rollback()
+			return nil, ctx.Err()
+		}
+	}
+	return txi, err
+}
+
+func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
+	dargs := make([]driver.Value, len(named))
+	for n, param := range named {
+		if len(param.Name) > 0 {
+			return nil, errors.New("sql: driver does not support the use of Named Parameters")
+		}
+		dargs[n] = param.Value
+	}
+	return dargs, nil
+}
diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go
index 4dba85a..c8cbbf0 100644
--- a/src/database/sql/driver/driver.go
+++ b/src/database/sql/driver/driver.go
@@ -8,7 +8,12 @@
 // Most code should use package sql.
 package driver
 
-import "errors"
+import (
+	"context"
+	"database/sql/internal"
+	"errors"
+	"reflect"
+)
 
 // Value is a value that drivers must be able to handle.
 // It is either nil or an instance of one of these types:
@@ -21,6 +26,16 @@ import "errors"
 //   time.Time
 type Value interface{}
 
+// NamedValue holds both the value name and value.
+// The Ordinal is the position of the parameter starting from one and is always set.
+// If the Name is not empty it should be used for the parameter identifier and
+// not the ordinal position.
+type NamedValue struct {
+	Name    string
+	Ordinal int
+	Value   Value
+}
+
 // Driver is the interface that must be implemented by a database
 // driver.
 type Driver interface {
@@ -54,6 +69,17 @@ var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented")
 // you shouldn't return ErrBadConn.
 var ErrBadConn = errors.New("driver: bad connection")
 
+// Pinger is an optional interface that may be implemented by a Conn.
+//
+// If a Conn does not implement Pinger, the sql package's DB.Ping and
+// DB.PingContext will check if there is at least one Conn available.
+//
+// If Conn.Ping returns ErrBadConn, DB.Ping and DB.PingContext will remove
+// the Conn from pool.
+type Pinger interface {
+	Ping(ctx context.Context) error
+}
+
 // Execer is an optional interface that may be implemented by a Conn.
 //
 // If a Conn does not implement Execer, the sql package's DB.Exec will
@@ -61,10 +87,25 @@ var ErrBadConn = errors.New("driver: bad connection")
 // statement.
 //
 // Exec may return ErrSkip.
+//
+// Deprecated: Drivers should implement ExecerContext instead (or additionally).
 type Execer interface {
 	Exec(query string, args []Value) (Result, error)
 }
 
+// ExecerContext is an optional interface that may be implemented by a Conn.
+//
+// If a Conn does not implement ExecerContext, the sql package's DB.Exec will
+// first prepare a query, execute the statement, and then close the
+// statement.
+//
+// ExecerContext may return ErrSkip.
+//
+// ExecerContext must honor the context timeout and return when the context is canceled.
+type ExecerContext interface {
+	ExecContext(ctx context.Context, query string, args []NamedValue) (Result, error)
+}
+
 // Queryer is an optional interface that may be implemented by a Conn.
 //
 // If a Conn does not implement Queryer, the sql package's DB.Query will
@@ -72,10 +113,25 @@ type Execer interface {
 // statement.
 //
 // Query may return ErrSkip.
+//
+// Deprecated: Drivers should implement QueryerContext instead (or additionally).
 type Queryer interface {
 	Query(query string, args []Value) (Rows, error)
 }
 
+// QueryerContext is an optional interface that may be implemented by a Conn.
+//
+// If a Conn does not implement QueryerContext, the sql package's DB.Query will
+// first prepare a query, execute the statement, and then close the
+// statement.
+//
+// QueryerContext may return ErrSkip.
+//
+// QueryerContext must honor the context timeout and return when the context is canceled.
+type QueryerContext interface {
+	QueryContext(ctx context.Context, query string, args []NamedValue) (Rows, error)
+}
+
 // Conn is a connection to a database. It is not used concurrently
 // by multiple goroutines.
 //
@@ -95,9 +151,56 @@ type Conn interface {
 	Close() error
 
 	// Begin starts and returns a new transaction.
+	//
+	// Deprecated: Drivers should implement ConnBeginContext instead (or additionally).
 	Begin() (Tx, error)
 }
 
+// ConnPrepareContext enhances the Conn interface with context.
+type ConnPrepareContext interface {
+	// PrepareContext returns a prepared statement, bound to this connection.
+	// context is for the preparation of the statement,
+	// it must not store the context within the statement itself.
+	PrepareContext(ctx context.Context, query string) (Stmt, error)
+}
+
+// IsolationLevel is the transaction isolation level stored in Context.
+//
+// This type should be considered identical to sql.IsolationLevel along
+// with any values defined on it.
+type IsolationLevel int
+
+// IsolationFromContext extracts the isolation level from a Context.
+func IsolationFromContext(ctx context.Context) (level IsolationLevel, ok bool) {
+	level, ok = ctx.Value(internal.IsolationLevelKey{}).(IsolationLevel)
+	return level, ok
+}
+
+// ReadOnlyFromContext extracts the read-only property from a Context.
+// When readonly is true the transaction must be set to read-only
+// or return an error.
+func ReadOnlyFromContext(ctx context.Context) (readonly bool) {
+	readonly, _ = ctx.Value(internal.ReadOnlyKey{}).(bool)
+	return readonly
+}
+
+// ConnBeginContext enhances the Conn interface with context.
+type ConnBeginContext interface {
+	// BeginContext starts and returns a new transaction.
+	// If the context is canceled by the user the sql package will
+	// call Tx.Rollback before discarding and closing the connection.
+	//
+	// This must call IsolationFromContext to determine if there is a set
+	// isolation level. If the driver does not support setting the isolation
+	// level and one is set or if there is a set isolation level
+	// but the set level is not supported, an error must be returned.
+	//
+	// This must also call ReadOnlyFromContext to determine if the read-only
+	// value is true to either set the read-only transaction property if supported
+	// or return an error if it is not supported.
+	BeginContext(ctx context.Context) (Tx, error)
+}
+
 // Result is the result of a query execution.
 type Result interface {
 	// LastInsertId returns the database's auto-generated ID
@@ -132,13 +235,35 @@ type Stmt interface {
 
 	// Exec executes a query that doesn't return rows, such
 	// as an INSERT or UPDATE.
+	//
+	// Deprecated: Drivers should implement StmtExecContext instead (or additionally).
 	Exec(args []Value) (Result, error)
 
 	// Query executes a query that may return rows, such as a
 	// SELECT.
+	//
+	// Deprecated: Drivers should implement StmtQueryContext instead (or additionally).
 	Query(args []Value) (Rows, error)
 }
 
+// StmtExecContext enhances the Stmt interface by providing Exec with context.
+type StmtExecContext interface {
+	// ExecContext executes a query that doesn't return rows, such
+	// as an INSERT or UPDATE.
+	//
+	// ExecContext must honor the context timeout and return when it is canceled.
+	ExecContext(ctx context.Context, args []NamedValue) (Result, error)
+}
+
+// StmtQueryContext enhances the Stmt interface by providing Query with context.
+type StmtQueryContext interface {
+	// QueryContext executes a query that may return rows, such as a
+	// SELECT.
+	//
+	// QueryContext must honor the context timeout and return when it is canceled.
+	QueryContext(ctx context.Context, args []NamedValue) (Rows, error)
+}
+
 // ColumnConverter may be optionally implemented by Stmt if the
 // statement is aware of its own columns' types and can convert from
 // any type to a driver Value.
@@ -169,6 +294,76 @@ type Rows interface {
 	Next(dest []Value) error
 }
 
+// RowsNextResultSet extends the Rows interface by providing a way to signal
+// the driver to advance to the next result set.
+type RowsNextResultSet interface {
+	Rows
+
+	// HasNextResultSet is called at the end of the current result set and
+	// reports whether there is another result set after the current one.
+	HasNextResultSet() bool
+
+	// NextResultSet advances the driver to the next result set even
+	// if there are remaining rows in the current result set.
+	//
+	// NextResultSet should return io.EOF when there are no more result sets.
+	NextResultSet() error
+}
+
+// RowsColumnTypeScanType may be implemented by Rows. It should return
+// the value type that can be used to scan types into. For example, the database
+// column type "bigint" this should return "reflect.TypeOf(int64(0))".
+type RowsColumnTypeScanType interface {
+	Rows
+	ColumnTypeScanType(index int) reflect.Type
+}
+
+// RowsColumnTypeDatabaseTypeName may be implemented by Rows. It should return the
+// database system type name without the length. Type names should be uppercase.
+// Examples of returned types: "VARCHAR", "NVARCHAR", "VARCHAR2", "CHAR", "TEXT",
+// "DECIMAL", "SMALLINT", "INT", "BIGINT", "BOOL", "[]BIGINT", "JSONB", "XML",
+// "TIMESTAMP".
+type RowsColumnTypeDatabaseTypeName interface {
+	Rows
+	ColumnTypeDatabaseTypeName(index int) string
+}
+
+// RowsColumnTypeLength may be implemented by Rows. It should return the length
+// of the column type if the column is a variable length type. If the column is
+// not a variable length type ok should return false.
+// If length is not limited other than system limits, it should return math.MaxInt64.
+// The following are examples of returned values for various types:
+//   TEXT          (math.MaxInt64, true)
+//   varchar(10)   (10, true)
+//   nvarchar(10)  (10, true)
+//   decimal       (0, false)
+//   int           (0, false)
+//   bytea(30)     (30, true)
+type RowsColumnTypeLength interface {
+	Rows
+	ColumnTypeLength(index int) (length int64, ok bool)
+}
+
+// RowsColumnTypeNullable may be implemented by Rows. The nullable value should
+// be true if it is known the column may be null, or false if the column is known
+// to be not nullable.
+// If the column nullability is unknown, ok should be false.
+type RowsColumnTypeNullable interface {
+	Rows
+	ColumnTypeNullable(index int) (nullable, ok bool)
+}
+
+// RowsColumnTypePrecisionScale may be implemented by Rows. It should return
+// the precision and scale for decimal types. If not applicable, ok should be false.
+// The following are examples of returned values for various types:
+//   decimal(38, 4)    (38, 4, true)
+//   int               (0, 0, false)
+//   decimal           (math.MaxInt64, math.MaxInt64, true)
+type RowsColumnTypePrecisionScale interface {
+	Rows
+	ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool)
+}
+
 // Tx is a transaction.
 type Tx interface {
 	Commit() error
diff --git a/src/database/sql/driver/types.go b/src/database/sql/driver/types.go
index e480e70..8b3cb6c 100644
--- a/src/database/sql/driver/types.go
+++ b/src/database/sql/driver/types.go
@@ -198,9 +198,9 @@ func IsScanValue(v interface{}) bool {
 // Value method is used to return a Value. As a fallback, the provided
 // argument's underlying type is used to convert it to a Value:
 // underlying integer types are converted to int64, floats to float64,
-// and strings to []byte. If the argument is a nil pointer,
-// ConvertValue returns a nil Value. If the argument is a non-nil
-// pointer, it is dereferenced and ConvertValue is called
+// bool, string, and []byte to themselves. If the argument is a nil
+// pointer, ConvertValue returns a nil Value. If the argument is a
+// non-nil pointer, it is dereferenced and ConvertValue is called
 // recursively. Other types are an error.
 var DefaultParameterConverter defaultConverter
 
@@ -208,13 +208,35 @@ type defaultConverter struct{}
 
 var _ ValueConverter = defaultConverter{}
 
+var valuerReflectType = reflect.TypeOf((*Valuer)(nil)).Elem()
+
+// callValuerValue returns vr.Value(), with one exception:
+// If vr.Value is an auto-generated method on a pointer type and the
+// pointer is nil, it would panic at runtime in the panicwrap
+// method. Treat it like nil instead.
+// Issue 8415.
+//
+// This is so people can implement driver.Value on value types and
+// still use nil pointers to those types to mean nil/NULL, just like
+// string/*string.
+//
+// This function is mirrored in the database/sql package.
+func callValuerValue(vr Valuer) (v Value, err error) {
+	if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr &&
+		rv.IsNil() &&
+		rv.Type().Elem().Implements(valuerReflectType) {
+		return nil, nil
+	}
+	return vr.Value()
+}
+
 func (defaultConverter) ConvertValue(v interface{}) (Value, error) {
 	if IsValue(v) {
 		return v, nil
 	}
 
-	if svi, ok := v.(Valuer); ok {
-		sv, err := svi.Value()
+	if vr, ok := v.(Valuer); ok {
+		sv, err := callValuerValue(vr)
 		if err != nil {
 			return nil, err
 		}
@@ -245,6 +267,16 @@ func (defaultConverter) ConvertValue(v interface{}) (Value, error) {
 		return int64(u64), nil
 	case reflect.Float32, reflect.Float64:
 		return rv.Float(), nil
+	case reflect.Bool:
+		return rv.Bool(), nil
+	case reflect.Slice:
+		ek := rv.Type().Elem().Kind()
+		if ek == reflect.Uint8 {
+			return rv.Bytes(), nil
+		}
+		return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, ek)
+	case reflect.String:
+		return rv.String(), nil
 	}
 	return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
 }
diff --git a/src/database/sql/driver/types_test.go b/src/database/sql/driver/types_test.go
index 1ce0ff0..0379bf8 100644
--- a/src/database/sql/driver/types_test.go
+++ b/src/database/sql/driver/types_test.go
@@ -20,6 +20,16 @@ type valueConverterTest struct {
 var now = time.Now()
 var answer int64 = 42
 
+type (
+	i  int64
+	f  float64
+	b  bool
+	bs []byte
+	s  string
+	t  time.Time
+	is []int
+)
+
 var valueConverterTests = []valueConverterTest{
 	{Bool, "true", true, ""},
 	{Bool, "True", true, ""},
@@ -41,6 +51,12 @@ var valueConverterTests = []valueConverterTest{
 	{DefaultParameterConverter, (*int64)(nil), nil, ""},
 	{DefaultParameterConverter, &answer, answer, ""},
 	{DefaultParameterConverter, &now, now, ""},
+	{DefaultParameterConverter, i(9), int64(9), ""},
+	{DefaultParameterConverter, f(0.1), float64(0.1), ""},
+	{DefaultParameterConverter, b(true), true, ""},
+	{DefaultParameterConverter, bs{1}, []byte{1}, ""},
+	{DefaultParameterConverter, s("a"), "a", ""},
+	{DefaultParameterConverter, is{1}, nil, "unsupported type driver.is, a slice of int"},
 }
 
 func TestValueConverters(t *testing.T) {
diff --git a/src/database/sql/example_test.go b/src/database/sql/example_test.go
index dcb74e0..ce56ca4 100644
--- a/src/database/sql/example_test.go
+++ b/src/database/sql/example_test.go
@@ -44,3 +44,65 @@ func ExampleDB_QueryRow() {
 		fmt.Printf("Username is %s\n", username)
 	}
 }
+
+func ExampleDB_Query_multipleResultSets() {
+	age := 27
+	q := `
+create temp table uid (id bigint); -- Create temp table for queries.
+insert into uid
+select id from users where age < ?; -- Populate temp table.
+
+-- First result set.
+select
+	users.id, name
+from
+	users
+	join uid on users.id = uid.id
+;
+
+-- Second result set.
+select 
+	ur.user, ur.role
+from
+	user_roles as ur
+	join uid on uid.id = ur.user
+;
+	`
+	rows, err := db.Query(q, age)
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		var (
+			id   int64
+			name string
+		)
+		if err := rows.Scan(&id, &name); err != nil {
+			log.Fatal(err)
+		}
+		fmt.Printf("id %d name is %s\n", id, name)
+	}
+	if !rows.NextResultSet() {
+		log.Fatal("expected more result sets", rows.Err())
+	}
+	var roleMap = map[int64]string{
+		1: "user",
+		2: "admin",
+		3: "gopher",
+	}
+	for rows.Next() {
+		var (
+			id   int64
+			role int64
+		)
+		if err := rows.Scan(&id, &role); err != nil {
+			log.Fatal(err)
+		}
+		fmt.Printf("id %d has role %s\n", id, roleMap[role])
+	}
+	if err := rows.Err(); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go
index 5b238bf..416b97d 100644
--- a/src/database/sql/fakedb_test.go
+++ b/src/database/sql/fakedb_test.go
@@ -5,11 +5,13 @@
 package sql
 
 import (
+	"context"
 	"database/sql/driver"
 	"errors"
 	"fmt"
 	"io"
 	"log"
+	"reflect"
 	"sort"
 	"strconv"
 	"strings"
@@ -32,10 +34,16 @@ var _ = log.Printf
 //     where types are: "string", [u]int{8,16,32,64}, "bool"
 //   INSERT|<tablename>|col=val,col2=val2,col3=?
 //   SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
+//   SELECT|<tablename>|projectcol1,projectcol2|filtercol=?param1,filtercol2=?param2
 //
 // Any of these can be preceded by PANIC|<method>|, to cause the
 // named method on fakeStmt to panic.
 //
+// Any of these can be proceeded by WAIT|<duration>|, to cause the
+// named method on fakeStmt to sleep for the specified duration.
+//
+// Multiple of these can be combined when separated with a semicolon.
+//
 // When opening a fakeDriver's database, it starts empty with no
 // tables. All tables and data are stored in memory only.
 type fakeDriver struct {
@@ -101,6 +109,12 @@ type fakeTx struct {
 	c *fakeConn
 }
 
+type boundCol struct {
+	Column      string
+	Placeholder string
+	Ordinal     int
+}
+
 type fakeStmt struct {
 	c *fakeConn
 	q string // just for debugging
@@ -108,6 +122,9 @@ type fakeStmt struct {
 	cmd   string
 	table string
 	panic string
+	wait  time.Duration
+
+	next *fakeStmt // used for returning multiple results.
 
 	closed bool
 
@@ -116,7 +133,7 @@ type fakeStmt struct {
 	colValue     []interface{} // used by INSERT (mix of strings and "?" for bound params)
 	placeholders int           // used by INSERT/SELECT: number of ? params
 
-	whereCol []string // used by SELECT (all placeholders)
+	whereCol []boundCol // used by SELECT (all placeholders)
 
 	placeholderConverter []driver.ValueConverter // used by INSERT
 }
@@ -335,18 +352,23 @@ func (c *fakeConn) Close() (err error) {
 	return nil
 }
 
-func checkSubsetTypes(args []driver.Value) error {
-	for n, arg := range args {
-		switch arg.(type) {
+func checkSubsetTypes(args []driver.NamedValue) error {
+	for _, arg := range args {
+		switch arg.Value.(type) {
 		case int64, float64, bool, nil, []byte, string, time.Time:
 		default:
-			return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg)
+			return fmt.Errorf("fakedb_test: invalid argument ordinal %[1]d: %[2]v, type %[2]T", arg.Ordinal, arg.Value)
 		}
 	}
 	return nil
 }
 
 func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) {
+	// Ensure that ExecContext is called if available.
+	panic("ExecContext was not called.")
+}
+
+func (c *fakeConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
 	// This is an optional interface, but it's implemented here
 	// just to check that all the args are of the proper types.
 	// ErrSkip is returned so the caller acts as if we didn't
@@ -359,6 +381,11 @@ func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error
 }
 
 func (c *fakeConn) Query(query string, args []driver.Value) (driver.Rows, error) {
+	// Ensure that ExecContext is called if available.
+	panic("QueryContext was not called.")
+}
+
+func (c *fakeConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
 	// This is an optional interface, but it's implemented here
 	// just to check that all the args are of the proper types.
 	// ErrSkip is returned so the caller acts as if we didn't
@@ -377,12 +404,13 @@ func errf(msg string, args ...interface{}) error {
 // parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=?
 // (note that where columns must always contain ? marks,
 //  just a limitation for fakedb)
-func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
+func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (*fakeStmt, error) {
 	if len(parts) != 3 {
 		stmt.Close()
 		return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts))
 	}
 	stmt.table = parts[0]
+
 	stmt.colName = strings.Split(parts[1], ",")
 	for n, colspec := range strings.Split(parts[2], ",") {
 		if colspec == "" {
@@ -399,19 +427,19 @@ func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, e
 			stmt.Close()
 			return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column)
 		}
-		if value != "?" {
+		if !strings.HasPrefix(value, "?") {
 			stmt.Close()
 			return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark",
 				stmt.table, column)
 		}
-		stmt.whereCol = append(stmt.whereCol, column)
 		stmt.placeholders++
+		stmt.whereCol = append(stmt.whereCol, boundCol{Column: column, Placeholder: value, Ordinal: stmt.placeholders})
 	}
 	return stmt, nil
 }
 
 // parts are table|col=type,col2=type2
-func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
+func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (*fakeStmt, error) {
 	if len(parts) != 2 {
 		stmt.Close()
 		return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts))
@@ -430,7 +458,7 @@ func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, e
 }
 
 // parts are table|col=?,col2=val
-func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, error) {
+func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (*fakeStmt, error) {
 	if len(parts) != 2 {
 		stmt.Close()
 		return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts))
@@ -450,7 +478,7 @@ func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, e
 		}
 		stmt.colName = append(stmt.colName, column)
 
-		if value != "?" {
+		if !strings.HasPrefix(value, "?") {
 			var subsetVal interface{}
 			// Convert to driver subset type
 			switch ctype {
@@ -473,7 +501,7 @@ func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, e
 		} else {
 			stmt.placeholders++
 			stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype))
-			stmt.colValue = append(stmt.colValue, "?")
+			stmt.colValue = append(stmt.colValue, value)
 		}
 	}
 	return stmt, nil
@@ -483,6 +511,10 @@ func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, e
 var hookPrepareBadConn func() bool
 
 func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
+	panic("use PrepareContext")
+}
+
+func (c *fakeConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
 	c.numPrepare++
 	if c.db == nil {
 		panic("nil c.db; conn = " + fmt.Sprintf("%#v", c))
@@ -492,38 +524,72 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
 		return nil, driver.ErrBadConn
 	}
 
-	parts := strings.Split(query, "|")
-	if len(parts) < 1 {
-		return nil, errf("empty query")
-	}
-	stmt := &fakeStmt{q: query, c: c}
-	if len(parts) >= 3 && parts[0] == "PANIC" {
-		stmt.panic = parts[1]
-		parts = parts[2:]
-	}
-	cmd := parts[0]
-	stmt.cmd = cmd
-	parts = parts[1:]
+	var firstStmt, prev *fakeStmt
+	for _, query := range strings.Split(query, ";") {
+		parts := strings.Split(query, "|")
+		if len(parts) < 1 {
+			return nil, errf("empty query")
+		}
+		stmt := &fakeStmt{q: query, c: c}
+		if firstStmt == nil {
+			firstStmt = stmt
+		}
+		if len(parts) >= 3 {
+			switch parts[0] {
+			case "PANIC":
+				stmt.panic = parts[1]
+				parts = parts[2:]
+			case "WAIT":
+				wait, err := time.ParseDuration(parts[1])
+				if err != nil {
+					return nil, errf("expected section after WAIT to be a duration, got %q %v", parts[1], err)
+				}
+				parts = parts[2:]
+				stmt.wait = wait
+			}
+		}
+		cmd := parts[0]
+		stmt.cmd = cmd
+		parts = parts[1:]
+
+		if stmt.wait > 0 {
+			wait := time.NewTimer(stmt.wait)
+			select {
+			case <-wait.C:
+			case <-ctx.Done():
+				wait.Stop()
+				return nil, ctx.Err()
+			}
+		}
 
-	c.incrStat(&c.stmtsMade)
-	switch cmd {
-	case "WIPE":
-		// Nothing
-	case "SELECT":
-		return c.prepareSelect(stmt, parts)
-	case "CREATE":
-		return c.prepareCreate(stmt, parts)
-	case "INSERT":
-		return c.prepareInsert(stmt, parts)
-	case "NOSERT":
-		// Do all the prep-work like for an INSERT but don't actually insert the row.
-		// Used for some of the concurrent tests.
-		return c.prepareInsert(stmt, parts)
-	default:
-		stmt.Close()
-		return nil, errf("unsupported command type %q", cmd)
+		c.incrStat(&c.stmtsMade)
+		var err error
+		switch cmd {
+		case "WIPE":
+			// Nothing
+		case "SELECT":
+			stmt, err = c.prepareSelect(stmt, parts)
+		case "CREATE":
+			stmt, err = c.prepareCreate(stmt, parts)
+		case "INSERT":
+			stmt, err = c.prepareInsert(stmt, parts)
+		case "NOSERT":
+			// Do all the prep-work like for an INSERT but don't actually insert the row.
+			// Used for some of the concurrent tests.
+			stmt, err = c.prepareInsert(stmt, parts)
+		default:
+			stmt.Close()
+			return nil, errf("unsupported command type %q", cmd)
+		}
+		if err != nil {
+			return nil, err
+		}
+		if prev != nil {
+			prev.next = stmt
+		}
+		prev = stmt
 	}
-	return stmt, nil
+	return firstStmt, nil
 }
 
 func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
@@ -550,6 +616,9 @@ func (s *fakeStmt) Close() error {
 		s.c.incrStat(&s.c.stmtsClosed)
 		s.closed = true
 	}
+	if s.next != nil {
+		s.next.Close()
+	}
 	return nil
 }
 
@@ -559,6 +628,9 @@ var errClosed = errors.New("fakedb: statement has been closed")
 var hookExecBadConn func() bool
 
 func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
+	panic("Using ExecContext")
+}
+func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
 	if s.panic == "Exec" {
 		panic(s.panic)
 	}
@@ -575,6 +647,16 @@ func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
 		return nil, err
 	}
 
+	if s.wait > 0 {
+		time.Sleep(s.wait)
+	}
+
+	select {
+	default:
+	case <-ctx.Done():
+		return nil, ctx.Err()
+	}
+
 	db := s.c.db
 	switch s.cmd {
 	case "WIPE":
@@ -599,7 +681,7 @@ func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
 // When doInsert is true, add the row to the table.
 // When doInsert is false do prep-work and error checking, but don't
 // actually add the row to the table.
-func (s *fakeStmt) execInsert(args []driver.Value, doInsert bool) (driver.Result, error) {
+func (s *fakeStmt) execInsert(args []driver.NamedValue, doInsert bool) (driver.Result, error) {
 	db := s.c.db
 	if len(args) != s.placeholders {
 		panic("error in pkg db; should only get here if size is correct")
@@ -625,8 +707,18 @@ func (s *fakeStmt) execInsert(args []driver.Value, doInsert bool) (driver.Result
 			return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname)
 		}
 		var val interface{}
-		if strvalue, ok := s.colValue[n].(string); ok && strvalue == "?" {
-			val = args[argPos]
+		if strvalue, ok := s.colValue[n].(string); ok && strings.HasPrefix(strvalue, "?") {
+			if strvalue == "?" {
+				val = args[argPos].Value
+			} else {
+				// Assign value from argument placeholder name.
+				for _, a := range args {
+					if a.Name == strvalue {
+						val = a.Value
+						break
+					}
+				}
+			}
 			argPos++
 		} else {
 			val = s.colValue[n]
@@ -646,6 +738,10 @@ func (s *fakeStmt) execInsert(args []driver.Value, doInsert bool) (driver.Result
 var hookQueryBadConn func() bool
 
 func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) {
+	panic("Use QueryContext")
+}
+
+func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
 	if s.panic == "Query" {
 		panic(s.panic)
 	}
@@ -667,65 +763,101 @@ func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) {
 		panic("error in pkg db; should only get here if size is correct")
 	}
 
-	db.mu.Lock()
-	t, ok := db.table(s.table)
-	db.mu.Unlock()
-	if !ok {
-		return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
-	}
+	setMRows := make([][]*row, 0, 1)
+	setColumns := make([][]string, 0, 1)
+	setColType := make([][]string, 0, 1)
 
-	if s.table == "magicquery" {
-		if len(s.whereCol) == 2 && s.whereCol[0] == "op" && s.whereCol[1] == "millis" {
-			if args[0] == "sleep" {
-				time.Sleep(time.Duration(args[1].(int64)) * time.Millisecond)
-			}
+	for {
+		db.mu.Lock()
+		t, ok := db.table(s.table)
+		db.mu.Unlock()
+		if !ok {
+			return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table)
 		}
-	}
-
-	t.mu.Lock()
-	defer t.mu.Unlock()
 
-	colIdx := make(map[string]int) // select column name -> column index in table
-	for _, name := range s.colName {
-		idx := t.columnIndex(name)
-		if idx == -1 {
-			return nil, fmt.Errorf("fakedb: unknown column name %q", name)
+		if s.table == "magicquery" {
+			if len(s.whereCol) == 2 && s.whereCol[0].Column == "op" && s.whereCol[1].Column == "millis" {
+				if args[0].Value == "sleep" {
+					time.Sleep(time.Duration(args[1].Value.(int64)) * time.Millisecond)
+				}
+			}
 		}
-		colIdx[name] = idx
-	}
 
-	mrows := []*row{}
-rows:
-	for _, trow := range t.rows {
-		// Process the where clause, skipping non-match rows. This is lazy
-		// and just uses fmt.Sprintf("%v") to test equality. Good enough
-		// for test code.
-		for widx, wcol := range s.whereCol {
-			idx := t.columnIndex(wcol)
+		t.mu.Lock()
+
+		colIdx := make(map[string]int) // select column name -> column index in table
+		for _, name := range s.colName {
+			idx := t.columnIndex(name)
 			if idx == -1 {
-				return nil, fmt.Errorf("db: invalid where clause column %q", wcol)
+				t.mu.Unlock()
+				return nil, fmt.Errorf("fakedb: unknown column name %q", name)
 			}
-			tcol := trow.cols[idx]
-			if bs, ok := tcol.([]byte); ok {
-				// lazy hack to avoid sprintf %v on a []byte
-				tcol = string(bs)
+			colIdx[name] = idx
+		}
+
+		mrows := []*row{}
+	rows:
+		for _, trow := range t.rows {
+			// Process the where clause, skipping non-match rows. This is lazy
+			// and just uses fmt.Sprintf("%v") to test equality. Good enough
+			// for test code.
+			for _, wcol := range s.whereCol {
+				idx := t.columnIndex(wcol.Column)
+				if idx == -1 {
+					t.mu.Unlock()
+					return nil, fmt.Errorf("db: invalid where clause column %q", wcol)
+				}
+				tcol := trow.cols[idx]
+				if bs, ok := tcol.([]byte); ok {
+					// lazy hack to avoid sprintf %v on a []byte
+					tcol = string(bs)
+				}
+				var argValue interface{}
+				if wcol.Placeholder == "?" {
+					argValue = args[wcol.Ordinal-1].Value
+				} else {
+					// Assign arg value from placeholder name.
+					for _, a := range args {
+						if a.Name == wcol.Placeholder {
+							argValue = a.Value
+							break
+						}
+					}
+				}
+				if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", argValue) {
+					continue rows
+				}
 			}
-			if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", args[widx]) {
-				continue rows
+			mrow := &row{cols: make([]interface{}, len(s.colName))}
+			for seli, name := range s.colName {
+				mrow.cols[seli] = trow.cols[colIdx[name]]
 			}
+			mrows = append(mrows, mrow)
 		}
-		mrow := &row{cols: make([]interface{}, len(s.colName))}
-		for seli, name := range s.colName {
-			mrow.cols[seli] = trow.cols[colIdx[name]]
+
+		var colType []string
+		for _, column := range s.colName {
+			colType = append(colType, t.coltype[t.columnIndex(column)])
 		}
-		mrows = append(mrows, mrow)
+
+		t.mu.Unlock()
+
+		setMRows = append(setMRows, mrows)
+		setColumns = append(setColumns, s.colName)
+		setColType = append(setColType, colType)
+
+		if s.next == nil {
+			break
+		}
+		s = s.next
 	}
 
 	cursor := &rowsCursor{
-		pos:    -1,
-		rows:   mrows,
-		cols:   s.colName,
-		errPos: -1,
+		posRow:  -1,
+		rows:    setMRows,
+		cols:    setColumns,
+		colType: setColType,
+		errPos:  -1,
 	}
 	return cursor, nil
 }
@@ -760,10 +892,12 @@ func (tx *fakeTx) Rollback() error {
 }
 
 type rowsCursor struct {
-	cols   []string
-	pos    int
-	rows   []*row
-	closed bool
+	cols    [][]string
+	colType [][]string
+	posSet  int
+	posRow  int
+	rows    [][]*row
+	closed  bool
 
 	// errPos and err are for making Next return early with error.
 	errPos int
@@ -786,7 +920,11 @@ func (rc *rowsCursor) Close() error {
 }
 
 func (rc *rowsCursor) Columns() []string {
-	return rc.cols
+	return rc.cols[rc.posSet]
+}
+
+func (rc *rowsCursor) ColumnTypeScanType(index int) reflect.Type {
+	return colTypeToReflectType(rc.colType[rc.posSet][index])
 }
 
 var rowsCursorNextHook func(dest []driver.Value) error
@@ -799,14 +937,14 @@ func (rc *rowsCursor) Next(dest []driver.Value) error {
 	if rc.closed {
 		return errors.New("fakedb: cursor is closed")
 	}
-	rc.pos++
-	if rc.pos == rc.errPos {
+	rc.posRow++
+	if rc.posRow == rc.errPos {
 		return rc.err
 	}
-	if rc.pos >= len(rc.rows) {
+	if rc.posRow >= len(rc.rows[rc.posSet]) {
 		return io.EOF // per interface spec
 	}
-	for i, v := range rc.rows[rc.pos].cols {
+	for i, v := range rc.rows[rc.posSet][rc.posRow].cols {
 		// TODO(bradfitz): convert to subset types? naah, I
 		// think the subset types should only be input to
 		// driver, but the sql package should be able to handle
@@ -831,6 +969,19 @@ func (rc *rowsCursor) Next(dest []driver.Value) error {
 	return nil
 }
 
+func (rc *rowsCursor) HasNextResultSet() bool {
+	return rc.posSet < len(rc.rows)-1
+}
+
+func (rc *rowsCursor) NextResultSet() error {
+	if rc.HasNextResultSet() {
+		rc.posSet++
+		rc.posRow = -1
+		return nil
+	}
+	return io.EOF // Per interface spec.
+}
+
 // fakeDriverString is like driver.String, but indirects pointers like
 // DefaultValueConverter.
 //
@@ -882,3 +1033,29 @@ func converterForType(typ string) driver.ValueConverter {
 	}
 	panic("invalid fakedb column type of " + typ)
 }
+
+func colTypeToReflectType(typ string) reflect.Type {
+	switch typ {
+	case "bool":
+		return reflect.TypeOf(false)
+	case "nullbool":
+		return reflect.TypeOf(NullBool{})
+	case "int32":
+		return reflect.TypeOf(int32(0))
+	case "string":
+		return reflect.TypeOf("")
+	case "nullstring":
+		return reflect.TypeOf(NullString{})
+	case "int64":
+		return reflect.TypeOf(int64(0))
+	case "nullint64":
+		return reflect.TypeOf(NullInt64{})
+	case "float64":
+		return reflect.TypeOf(float64(0))
+	case "nullfloat64":
+		return reflect.TypeOf(NullFloat64{})
+	case "datetime":
+		return reflect.TypeOf(time.Time{})
+	}
+	panic("invalid fakedb column type of " + typ)
+}
diff --git a/src/database/sql/internal/types.go b/src/database/sql/internal/types.go
new file mode 100644
index 0000000..1895144
--- /dev/null
+++ b/src/database/sql/internal/types.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+// Context keys that set transaction properties for sql.BeginContext.
+type (
+	IsolationLevelKey struct{} // context value is driver.IsolationLevel
+	ReadOnlyKey       struct{} // context value is bool
+)
diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go
index 09de1c3..a620707 100644
--- a/src/database/sql/sql.go
+++ b/src/database/sql/sql.go
@@ -13,10 +13,13 @@
 package sql
 
 import (
+	"context"
 	"database/sql/driver"
+	"database/sql/internal"
 	"errors"
 	"fmt"
 	"io"
+	"reflect"
 	"runtime"
 	"sort"
 	"sync"
@@ -66,6 +69,74 @@ func Drivers() []string {
 	return list
 }
 
+// A NamedArg used as an argument to Query or Exec
+// binds to the corresponding named parameter in the SQL statement.
+type NamedArg struct {
+	_Named_Fields_Required struct{}
+
+	// Name of the parameter placeholder. If empty the ordinal position in the
+	// argument list will be used.
+	Name string
+
+	// Value of the parameter. It may be assigned the same value types as
+	// the query arguments.
+	Value interface{}
+}
+
+// Named provides a more concise way to create NamedArg values.
+//
+// Example usage:
+//
+//     db.ExecContext(ctx, `
+//         delete from Invoice
+//         where
+//             TimeCreated < @end
+//             and TimeCreated >= @start;`,
+//         sql.Named("start", startTime),
+//         sql.Named("end", endTime),
+//     )
+func Named(name string, value interface{}) NamedArg {
+	// This method exists because the go1compat promise
+	// doesn't guarantee that structs don't grow more fields,
+	// so unkeyed struct literals are a vet error. Thus, we don't
+	// want to allow sql.NamedArg{name, value}.
+	return NamedArg{Name: name, Value: value}
+}
+
+// IsolationLevel is the transaction isolation level stored in Context.
+// The IsolationLevel is set with IsolationContext and the context
+// should be passed to BeginContext.
+type IsolationLevel int
+
+// Various isolation levels that drivers may support in BeginContext.
+// If a driver does not support a given isolation level an error may be returned.
+//
+// See https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels.
+const (
+	LevelDefault IsolationLevel = iota
+	LevelReadUncommitted
+	LevelReadCommitted
+	LevelWriteCommitted
+	LevelRepeatableRead
+	LevelSnapshot
+	LevelSerializable
+	LevelLinearizable
+)
+
+// IsolationContext returns a new Context that carries the provided isolation level.
+// The context must contain the isolation level before beginning the transaction
+// with BeginContext.
+func IsolationContext(ctx context.Context, level IsolationLevel) context.Context {
+	return context.WithValue(ctx, internal.IsolationLevelKey{}, driver.IsolationLevel(level))
+}
+
+// ReadOnlyWithContext returns a new Context that carries the provided
+// read-only transaction property. The context must contain the read-only property
+// before beginning the transaction with BeginContext.
+func ReadOnlyContext(ctx context.Context) context.Context {
+	return context.WithValue(ctx, internal.ReadOnlyKey{}, true)
+}
+
 // RawBytes is a byte slice that holds a reference to memory owned by
 // the database itself. After a Scan into a RawBytes, the slice is only
 // valid until the next call to Next, Scan, or Close.
@@ -272,7 +343,7 @@ type driverConn struct {
 	ci          driver.Conn
 	closed      bool
 	finalClosed bool // ci.Close has been called
-	openStmt    map[driver.Stmt]bool
+	openStmt    map[*driverStmt]bool
 
 	// guarded by db.mu
 	inUse      bool
@@ -284,10 +355,10 @@ func (dc *driverConn) releaseConn(err error) {
 	dc.db.putConn(dc, err)
 }
 
-func (dc *driverConn) removeOpenStmt(si driver.Stmt) {
+func (dc *driverConn) removeOpenStmt(ds *driverStmt) {
 	dc.Lock()
 	defer dc.Unlock()
-	delete(dc.openStmt, si)
+	delete(dc.openStmt, ds)
 }
 
 func (dc *driverConn) expired(timeout time.Duration) bool {
@@ -297,28 +368,23 @@ func (dc *driverConn) expired(timeout time.Duration) bool {
 	return dc.createdAt.Add(timeout).Before(nowFunc())
 }
 
-func (dc *driverConn) prepareLocked(query string) (driver.Stmt, error) {
-	si, err := dc.ci.Prepare(query)
-	if err == nil {
-		// Track each driverConn's open statements, so we can close them
-		// before closing the conn.
-		//
-		// TODO(bradfitz): let drivers opt out of caring about
-		// stmt closes if the conn is about to close anyway? For now
-		// do the safe thing, in case stmts need to be closed.
-		//
-		// TODO(bradfitz): after Go 1.2, closing driver.Stmts
-		// should be moved to driverStmt, using unique
-		// *driverStmts everywhere (including from
-		// *Stmt.connStmt, instead of returning a
-		// driver.Stmt), using driverStmt as a pointer
-		// everywhere, and making it a finalCloser.
-		if dc.openStmt == nil {
-			dc.openStmt = make(map[driver.Stmt]bool)
-		}
-		dc.openStmt[si] = true
+func (dc *driverConn) prepareLocked(ctx context.Context, query string) (*driverStmt, error) {
+	si, err := ctxDriverPrepare(ctx, dc.ci, query)
+	if err != nil {
+		return nil, err
 	}
-	return si, err
+
+	// Track each driverConn's open statements, so we can close them
+	// before closing the conn.
+	//
+	// Wrap all driver.Stmt is *driverStmt to ensure they are only closed once.
+	if dc.openStmt == nil {
+		dc.openStmt = make(map[*driverStmt]bool)
+	}
+	ds := &driverStmt{Locker: dc, si: si}
+	dc.openStmt[ds] = true
+
+	return ds, nil
 }
 
 // the dc.db's Mutex is held.
@@ -350,17 +416,26 @@ func (dc *driverConn) Close() error {
 }
 
 func (dc *driverConn) finalClose() error {
-	dc.Lock()
+	var err error
 
-	for si := range dc.openStmt {
-		si.Close()
+	// Each *driverStmt has a lock to the dc. Copy the list out of the dc
+	// before calling close on each stmt.
+	var openStmt []*driverStmt
+	withLock(dc, func() {
+		openStmt = make([]*driverStmt, 0, len(dc.openStmt))
+		for ds := range dc.openStmt {
+			openStmt = append(openStmt, ds)
+		}
+		dc.openStmt = nil
+	})
+	for _, ds := range openStmt {
+		ds.Close()
 	}
-	dc.openStmt = nil
-
-	err := dc.ci.Close()
-	dc.ci = nil
-	dc.finalClosed = true
-	dc.Unlock()
+	withLock(dc, func() {
+		dc.finalClosed = true
+		err = dc.ci.Close()
+		dc.ci = nil
+	})
 
 	dc.db.mu.Lock()
 	dc.db.numOpen--
@@ -377,12 +452,21 @@ func (dc *driverConn) finalClose() error {
 type driverStmt struct {
 	sync.Locker // the *driverConn
 	si          driver.Stmt
+	closed      bool
+	closeErr    error // return value of previous Close call
 }
 
+// Close ensures dirver.Stmt is only closed once any always returns the same
+// result.
 func (ds *driverStmt) Close() error {
 	ds.Lock()
 	defer ds.Unlock()
-	return ds.si.Close()
+	if ds.closed {
+		return ds.closeErr
+	}
+	ds.closed = true
+	ds.closeErr = ds.si.Close()
+	return ds.closeErr
 }
 
 // depSet is a finalCloser's outstanding dependencies
@@ -494,18 +578,36 @@ func Open(driverName, dataSourceName string) (*DB, error) {
 	return db, nil
 }
 
-// Ping verifies a connection to the database is still alive,
+// PingContext verifies a connection to the database is still alive,
 // establishing a connection if necessary.
-func (db *DB) Ping() error {
-	// TODO(bradfitz): give drivers an optional hook to implement
-	// this in a more efficient or more reliable way, if they
-	// have one.
-	dc, err := db.conn(cachedOrNewConn)
+func (db *DB) PingContext(ctx context.Context) error {
+	var dc *driverConn
+	var err error
+
+	for i := 0; i < maxBadConnRetries; i++ {
+		dc, err = db.conn(ctx, cachedOrNewConn)
+		if err != driver.ErrBadConn {
+			break
+		}
+	}
+	if err == driver.ErrBadConn {
+		dc, err = db.conn(ctx, alwaysNewConn)
+	}
 	if err != nil {
 		return err
 	}
-	db.putConn(dc, nil)
-	return nil
+
+	if pinger, ok := dc.ci.(driver.Pinger); ok {
+		err = pinger.Ping(ctx)
+	}
+	db.putConn(dc, err)
+	return err
+}
+
+// Ping verifies a connection to the database is still alive,
+// establishing a connection if necessary.
+func (db *DB) Ping() error {
+	return db.PingContext(context.Background())
 }
 
 // Close closes the database, releasing any open resources.
@@ -777,12 +879,19 @@ type connRequest struct {
 var errDBClosed = errors.New("sql: database is closed")
 
 // conn returns a newly-opened or cached *driverConn.
-func (db *DB) conn(strategy connReuseStrategy) (*driverConn, error) {
+func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn, error) {
 	db.mu.Lock()
 	if db.closed {
 		db.mu.Unlock()
 		return nil, errDBClosed
 	}
+	// Check if the context is expired.
+	select {
+	default:
+	case <-ctx.Done():
+		db.mu.Unlock()
+		return nil, ctx.Err()
+	}
 	lifetime := db.maxLifetime
 
 	// Prefer a free connection, if possible.
@@ -808,15 +917,21 @@ func (db *DB) conn(strategy connReuseStrategy) (*driverConn, error) {
 		req := make(chan connRequest, 1)
 		db.connRequests = append(db.connRequests, req)
 		db.mu.Unlock()
-		ret, ok := <-req
-		if !ok {
-			return nil, errDBClosed
-		}
-		if ret.err == nil && ret.conn.expired(lifetime) {
-			ret.conn.Close()
-			return nil, driver.ErrBadConn
+
+		// Timeout the connection request with the context.
+		select {
+		case <-ctx.Done():
+			return nil, ctx.Err()
+		case ret, ok := <-req:
+			if !ok {
+				return nil, errDBClosed
+			}
+			if ret.err == nil && ret.conn.expired(lifetime) {
+				ret.conn.Close()
+				return nil, driver.ErrBadConn
+			}
+			return ret.conn, ret.err
 		}
-		return ret.conn, ret.err
 	}
 
 	db.numOpen++ // optimistically
@@ -844,21 +959,22 @@ func (db *DB) conn(strategy connReuseStrategy) (*driverConn, error) {
 // putConnHook is a hook for testing.
 var putConnHook func(*DB, *driverConn)
 
-// noteUnusedDriverStatement notes that si is no longer used and should
+// noteUnusedDriverStatement notes that ds is no longer used and should
 // be closed whenever possible (when c is next not in use), unless c is
 // already closed.
-func (db *DB) noteUnusedDriverStatement(c *driverConn, si driver.Stmt) {
+func (db *DB) noteUnusedDriverStatement(c *driverConn, ds *driverStmt) {
 	db.mu.Lock()
 	defer db.mu.Unlock()
 	if c.inUse {
 		c.onPut = append(c.onPut, func() {
-			si.Close()
+			ds.Close()
 		})
 	} else {
 		c.Lock()
-		defer c.Unlock()
-		if !c.finalClosed {
-			si.Close()
+		fc := c.finalClosed
+		c.Unlock()
+		if !fc {
+			ds.Close()
 		}
 	}
 }
@@ -952,40 +1068,53 @@ func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
 // connection to be opened.
 const maxBadConnRetries = 2
 
-// Prepare creates a prepared statement for later queries or executions.
+// PrepareContext creates a prepared statement for later queries or executions.
 // Multiple queries or executions may be run concurrently from the
 // returned statement.
 // The caller must call the statement's Close method
 // when the statement is no longer needed.
-func (db *DB) Prepare(query string) (*Stmt, error) {
+//
+// The provided context is used for the preparation of the statement, not for the
+// execution of the statement.
+func (db *DB) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
 	var stmt *Stmt
 	var err error
 	for i := 0; i < maxBadConnRetries; i++ {
-		stmt, err = db.prepare(query, cachedOrNewConn)
+		stmt, err = db.prepare(ctx, query, cachedOrNewConn)
 		if err != driver.ErrBadConn {
 			break
 		}
 	}
 	if err == driver.ErrBadConn {
-		return db.prepare(query, alwaysNewConn)
+		return db.prepare(ctx, query, alwaysNewConn)
 	}
 	return stmt, err
 }
 
-func (db *DB) prepare(query string, strategy connReuseStrategy) (*Stmt, error) {
+// Prepare creates a prepared statement for later queries or executions.
+// Multiple queries or executions may be run concurrently from the
+// returned statement.
+// The caller must call the statement's Close method
+// when the statement is no longer needed.
+func (db *DB) Prepare(query string) (*Stmt, error) {
+	return db.PrepareContext(context.Background(), query)
+}
+
+func (db *DB) prepare(ctx context.Context, query string, strategy connReuseStrategy) (*Stmt, error) {
 	// TODO: check if db.driver supports an optional
 	// driver.Preparer interface and call that instead, if so,
 	// otherwise we make a prepared statement that's bound
 	// to a connection, and to execute this prepared statement
 	// we either need to use this connection (if it's free), else
 	// get a new connection + re-prepare + execute on that one.
-	dc, err := db.conn(strategy)
+	dc, err := db.conn(ctx, strategy)
 	if err != nil {
 		return nil, err
 	}
-	dc.Lock()
-	si, err := dc.prepareLocked(query)
-	dc.Unlock()
+	var ds *driverStmt
+	withLock(dc, func() {
+		ds, err = dc.prepareLocked(ctx, query)
+	})
 	if err != nil {
 		db.putConn(dc, err)
 		return nil, err
@@ -993,7 +1122,7 @@ func (db *DB) prepare(query string, strategy connReuseStrategy) (*Stmt, error) {
 	stmt := &Stmt{
 		db:            db,
 		query:         query,
-		css:           []connStmt{{dc, si}},
+		css:           []connStmt{{dc, ds}},
 		lastNumClosed: atomic.LoadUint64(&db.numClosed),
 	}
 	db.addDep(stmt, stmt)
@@ -1001,25 +1130,31 @@ func (db *DB) prepare(query string, strategy connReuseStrategy) (*Stmt, error) {
 	return stmt, nil
 }
 
-// Exec executes a query without returning any rows.
+// ExecContext executes a query without returning any rows.
 // The args are for any placeholder parameters in the query.
-func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
+func (db *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) {
 	var res Result
 	var err error
 	for i := 0; i < maxBadConnRetries; i++ {
-		res, err = db.exec(query, args, cachedOrNewConn)
+		res, err = db.exec(ctx, query, args, cachedOrNewConn)
 		if err != driver.ErrBadConn {
 			break
 		}
 	}
 	if err == driver.ErrBadConn {
-		return db.exec(query, args, alwaysNewConn)
+		return db.exec(ctx, query, args, alwaysNewConn)
 	}
 	return res, err
 }
 
-func (db *DB) exec(query string, args []interface{}, strategy connReuseStrategy) (res Result, err error) {
-	dc, err := db.conn(strategy)
+// Exec executes a query without returning any rows.
+// The args are for any placeholder parameters in the query.
+func (db *DB) Exec(query string, args ...interface{}) (Result, error) {
+	return db.ExecContext(context.Background(), query, args...)
+}
+
+func (db *DB) exec(ctx context.Context, query string, args []interface{}, strategy connReuseStrategy) (res Result, err error) {
+	dc, err := db.conn(ctx, strategy)
 	if err != nil {
 		return nil, err
 	}
@@ -1028,13 +1163,15 @@ func (db *DB) exec(query string, args []interface{}, strategy connReuseStrategy)
 	}()
 
 	if execer, ok := dc.ci.(driver.Execer); ok {
-		dargs, err := driverArgs(nil, args)
+		var dargs []driver.NamedValue
+		dargs, err = driverArgs(nil, args)
 		if err != nil {
 			return nil, err
 		}
-		dc.Lock()
-		resi, err := execer.Exec(query, dargs)
-		dc.Unlock()
+		var resi driver.Result
+		withLock(dc, func() {
+			resi, err = ctxDriverExec(ctx, execer, query, dargs)
+		})
 		if err != driver.ErrSkip {
 			if err != nil {
 				return nil, err
@@ -1043,54 +1180,63 @@ func (db *DB) exec(query string, args []interface{}, strategy connReuseStrategy)
 		}
 	}
 
-	dc.Lock()
-	si, err := dc.ci.Prepare(query)
-	dc.Unlock()
+	var si driver.Stmt
+	withLock(dc, func() {
+		si, err = ctxDriverPrepare(ctx, dc.ci, query)
+	})
 	if err != nil {
 		return nil, err
 	}
-	defer withLock(dc, func() { si.Close() })
-	return resultFromStatement(driverStmt{dc, si}, args...)
+	ds := &driverStmt{Locker: dc, si: si}
+	defer ds.Close()
+	return resultFromStatement(ctx, ds, args...)
 }
 
-// Query executes a query that returns rows, typically a SELECT.
+// QueryContext executes a query that returns rows, typically a SELECT.
 // The args are for any placeholder parameters in the query.
-func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
+func (db *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
 	var rows *Rows
 	var err error
 	for i := 0; i < maxBadConnRetries; i++ {
-		rows, err = db.query(query, args, cachedOrNewConn)
+		rows, err = db.query(ctx, query, args, cachedOrNewConn)
 		if err != driver.ErrBadConn {
 			break
 		}
 	}
 	if err == driver.ErrBadConn {
-		return db.query(query, args, alwaysNewConn)
+		return db.query(ctx, query, args, alwaysNewConn)
 	}
 	return rows, err
 }
 
-func (db *DB) query(query string, args []interface{}, strategy connReuseStrategy) (*Rows, error) {
-	ci, err := db.conn(strategy)
+// Query executes a query that returns rows, typically a SELECT.
+// The args are for any placeholder parameters in the query.
+func (db *DB) Query(query string, args ...interface{}) (*Rows, error) {
+	return db.QueryContext(context.Background(), query, args...)
+}
+
+func (db *DB) query(ctx context.Context, query string, args []interface{}, strategy connReuseStrategy) (*Rows, error) {
+	ci, err := db.conn(ctx, strategy)
 	if err != nil {
 		return nil, err
 	}
 
-	return db.queryConn(ci, ci.releaseConn, query, args)
+	return db.queryConn(ctx, ci, ci.releaseConn, query, args)
 }
 
 // queryConn executes a query on the given connection.
 // The connection gets released by the releaseConn function.
-func (db *DB) queryConn(dc *driverConn, releaseConn func(error), query string, args []interface{}) (*Rows, error) {
+func (db *DB) queryConn(ctx context.Context, dc *driverConn, releaseConn func(error), query string, args []interface{}) (*Rows, error) {
 	if queryer, ok := dc.ci.(driver.Queryer); ok {
 		dargs, err := driverArgs(nil, args)
 		if err != nil {
 			releaseConn(err)
 			return nil, err
 		}
-		dc.Lock()
-		rowsi, err := queryer.Query(query, dargs)
-		dc.Unlock()
+		var rowsi driver.Rows
+		withLock(dc, func() {
+			rowsi, err = ctxDriverQuery(ctx, queryer, query, dargs)
+		})
 		if err != driver.ErrSkip {
 			if err != nil {
 				releaseConn(err)
@@ -1103,24 +1249,25 @@ func (db *DB) queryConn(dc *driverConn, releaseConn func(error), query string, a
 				releaseConn: releaseConn,
 				rowsi:       rowsi,
 			}
+			rows.initContextClose(ctx)
 			return rows, nil
 		}
 	}
 
-	dc.Lock()
-	si, err := dc.ci.Prepare(query)
-	dc.Unlock()
+	var si driver.Stmt
+	var err error
+	withLock(dc, func() {
+		si, err = ctxDriverPrepare(ctx, dc.ci, query)
+	})
 	if err != nil {
 		releaseConn(err)
 		return nil, err
 	}
 
-	ds := driverStmt{dc, si}
-	rowsi, err := rowsiFromStatement(ds, args...)
+	ds := &driverStmt{Locker: dc, si: si}
+	rowsi, err := rowsiFromStatement(ctx, ds, args...)
 	if err != nil {
-		dc.Lock()
-		si.Close()
-		dc.Unlock()
+		ds.Close()
 		releaseConn(err)
 		return nil, err
 	}
@@ -1131,53 +1278,94 @@ func (db *DB) queryConn(dc *driverConn, releaseConn func(error), query string, a
 		dc:          dc,
 		releaseConn: releaseConn,
 		rowsi:       rowsi,
-		closeStmt:   si,
+		closeStmt:   ds,
 	}
+	rows.initContextClose(ctx)
 	return rows, nil
 }
 
+// QueryRowContext executes a query that is expected to return at most one row.
+// QueryRowContext always returns a non-nil value. Errors are deferred until
+// Row's Scan method is called.
+func (db *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row {
+	rows, err := db.QueryContext(ctx, query, args...)
+	return &Row{rows: rows, err: err}
+}
+
 // QueryRow executes a query that is expected to return at most one row.
 // QueryRow always returns a non-nil value. Errors are deferred until
 // Row's Scan method is called.
 func (db *DB) QueryRow(query string, args ...interface{}) *Row {
-	rows, err := db.Query(query, args...)
-	return &Row{rows: rows, err: err}
+	return db.QueryRowContext(context.Background(), query, args...)
 }
 
-// Begin starts a transaction. The isolation level is dependent on
-// the driver.
-func (db *DB) Begin() (*Tx, error) {
+// BeginContext starts a transaction.
+//
+// The provided context is used until the transaction is committed or rolled back.
+// If the context is canceled, the sql package will roll back
+// the transaction. Tx.Commit will return an error if the context provided to
+// BeginContext is canceled.
+//
+// An isolation level may be set by setting the value in the context
+// before calling this. If a non-default isolation level is used
+// that the driver doesn't support an error will be returned. Different drivers
+// may have slightly different meanings for the same isolation level.
+func (db *DB) BeginContext(ctx context.Context) (*Tx, error) {
 	var tx *Tx
 	var err error
 	for i := 0; i < maxBadConnRetries; i++ {
-		tx, err = db.begin(cachedOrNewConn)
+		tx, err = db.begin(ctx, cachedOrNewConn)
 		if err != driver.ErrBadConn {
 			break
 		}
 	}
 	if err == driver.ErrBadConn {
-		return db.begin(alwaysNewConn)
+		return db.begin(ctx, alwaysNewConn)
 	}
 	return tx, err
 }
 
-func (db *DB) begin(strategy connReuseStrategy) (tx *Tx, err error) {
-	dc, err := db.conn(strategy)
+// Begin starts a transaction. The default isolation level is dependent on
+// the driver.
+func (db *DB) Begin() (*Tx, error) {
+	return db.BeginContext(context.Background())
+}
+
+func (db *DB) begin(ctx context.Context, strategy connReuseStrategy) (tx *Tx, err error) {
+	dc, err := db.conn(ctx, strategy)
 	if err != nil {
 		return nil, err
 	}
-	dc.Lock()
-	txi, err := dc.ci.Begin()
-	dc.Unlock()
+	var txi driver.Tx
+	withLock(dc, func() {
+		txi, err = ctxDriverBegin(ctx, dc.ci)
+	})
 	if err != nil {
 		db.putConn(dc, err)
 		return nil, err
 	}
-	return &Tx{
-		db:  db,
-		dc:  dc,
-		txi: txi,
-	}, nil
+
+	// Schedule the transaction to rollback when the context is cancelled.
+	// The cancel function in Tx will be called after done is set to true.
+	ctx, cancel := context.WithCancel(ctx)
+	tx = &Tx{
+		db:     db,
+		dc:     dc,
+		txi:    txi,
+		cancel: cancel,
+		ctx:    ctx,
+	}
+	go func(tx *Tx) {
+		select {
+		case <-tx.ctx.Done():
+			if !tx.isDone() {
+				// Discard and close the connection used to ensure the transaction
+				// is closed and the resources are released.
+				tx.rollback(true)
+			}
+		}
+	}(tx)
+	return tx, nil
 }
 
 // Driver returns the database's underlying driver.
@@ -1203,10 +1391,11 @@ type Tx struct {
 	dc  *driverConn
 	txi driver.Tx
 
-	// done transitions from false to true exactly once, on Commit
+	// done transitions from 0 to 1 exactly once, on Commit
 	// or Rollback. once done, all operations fail with
 	// ErrTxDone.
-	done bool
+	// Use atomic operations on value when checking value.
+	done int32
 
 	// All Stmts prepared for this transaction. These will be closed after the
 	// transaction has been committed or rolled back.
@@ -1214,22 +1403,34 @@ type Tx struct {
 		sync.Mutex
 		v []*Stmt
 	}
+
+	// cancel is called after done transitions from false to true.
+	cancel func()
+
+	// ctx lives for the life of the transaction.
+	ctx context.Context
+}
+
+func (tx *Tx) isDone() bool {
+	return atomic.LoadInt32(&tx.done) != 0
 }
 
+// ErrTxDone is returned by any operation that is performed on a transaction
+// that has already been committed or rolled back.
 var ErrTxDone = errors.New("sql: Transaction has already been committed or rolled back")
 
 func (tx *Tx) close(err error) {
-	if tx.done {
+	if !atomic.CompareAndSwapInt32(&tx.done, 0, 1) {
 		panic("double close") // internal error
 	}
-	tx.done = true
 	tx.db.putConn(tx.dc, err)
+	tx.cancel()
 	tx.dc = nil
 	tx.txi = nil
 }
 
-func (tx *Tx) grabConn() (*driverConn, error) {
-	if tx.done {
+func (tx *Tx) grabConn(ctx context.Context) (*driverConn, error) {
+	if tx.isDone() {
 		return nil, ErrTxDone
 	}
 	return tx.dc, nil
@@ -1238,20 +1439,26 @@ func (tx *Tx) grabConn() (*driverConn, error) {
 // Closes all Stmts prepared for this transaction.
 func (tx *Tx) closePrepared() {
 	tx.stmts.Lock()
+	defer tx.stmts.Unlock()
 	for _, stmt := range tx.stmts.v {
 		stmt.Close()
 	}
-	tx.stmts.Unlock()
 }
 
 // Commit commits the transaction.
 func (tx *Tx) Commit() error {
-	if tx.done {
+	select {
+	default:
+	case <-tx.ctx.Done():
+		return tx.ctx.Err()
+	}
+	if tx.isDone() {
 		return ErrTxDone
 	}
-	tx.dc.Lock()
-	err := tx.txi.Commit()
-	tx.dc.Unlock()
+	var err error
+	withLock(tx.dc, func() {
+		err = tx.txi.Commit()
+	})
 	if err != driver.ErrBadConn {
 		tx.closePrepared()
 	}
@@ -1259,28 +1466,42 @@ func (tx *Tx) Commit() error {
 	return err
 }
 
-// Rollback aborts the transaction.
-func (tx *Tx) Rollback() error {
-	if tx.done {
+// rollback aborts the transaction and optionally forces the pool to discard
+// the connection.
+func (tx *Tx) rollback(discardConn bool) error {
+	if tx.isDone() {
 		return ErrTxDone
 	}
-	tx.dc.Lock()
-	err := tx.txi.Rollback()
-	tx.dc.Unlock()
+	var err error
+	withLock(tx.dc, func() {
+		err = tx.txi.Rollback()
+	})
 	if err != driver.ErrBadConn {
 		tx.closePrepared()
 	}
+	if discardConn {
+		err = driver.ErrBadConn
+	}
 	tx.close(err)
 	return err
 }
 
+// Rollback aborts the transaction.
+func (tx *Tx) Rollback() error {
+	return tx.rollback(false)
+}
+
 // Prepare creates a prepared statement for use within a transaction.
 //
-// The returned statement operates within the transaction and can no longer
-// be used once the transaction has been committed or rolled back.
+// The returned statement operates within the transaction and will be closed
+// when the transaction has been committed or rolled back.
 //
 // To use an existing prepared statement on this transaction, see Tx.Stmt.
-func (tx *Tx) Prepare(query string) (*Stmt, error) {
+//
+// The provided context will be used for the preparation of the context, not
+// for the execution of the returned statement. The returned statement
+// will run in the transaction context.
+func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
 	// TODO(bradfitz): We could be more efficient here and either
 	// provide a method to take an existing Stmt (created on
 	// perhaps a different Conn), and re-create it on this Conn if
@@ -1294,14 +1515,15 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
 	// Perhaps just looking at the reference count (by noting
 	// Stmt.Close) would be enough. We might also want a finalizer
 	// on Stmt to drop the reference count.
-	dc, err := tx.grabConn()
+	dc, err := tx.grabConn(ctx)
 	if err != nil {
 		return nil, err
 	}
 
-	dc.Lock()
-	si, err := dc.ci.Prepare(query)
-	dc.Unlock()
+	var si driver.Stmt
+	withLock(dc, func() {
+		si, err = ctxDriverPrepare(ctx, dc.ci, query)
+	})
 	if err != nil {
 		return nil, err
 	}
@@ -1309,7 +1531,7 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
 	stmt := &Stmt{
 		db: tx.db,
 		tx: tx,
-		txsi: &driverStmt{
+		txds: &driverStmt{
 			Locker: dc,
 			si:     si,
 		},
@@ -1321,7 +1543,17 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
 	return stmt, nil
 }
 
-// Stmt returns a transaction-specific prepared statement from
+// Prepare creates a prepared statement for use within a transaction.
+//
+// The returned statement operates within the transaction and can no longer
+// be used once the transaction has been committed or rolled back.
+//
+// To use an existing prepared statement on this transaction, see Tx.Stmt.
+func (tx *Tx) Prepare(query string) (*Stmt, error) {
+	return tx.PrepareContext(context.Background(), query)
+}
+
+// StmtContext returns a transaction-specific prepared statement from
 // an existing statement.
 //
 // Example:
@@ -1329,11 +1561,11 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
 //  ...
 //  tx, err := db.Begin()
 //  ...
-//  res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203)
+//  res, err := tx.StmtContext(ctx, updateMoney).Exec(123.45, 98293203)
 //
-// The returned statement operates within the transaction and can no longer
-// be used once the transaction has been committed or rolled back.
-func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
+// The returned statement operates within the transaction and will be closed
+// when the transaction has been committed or rolled back.
+func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt {
 	// TODO(bradfitz): optimize this. Currently this re-prepares
 	// each time. This is fine for now to illustrate the API but
 	// we should really cache already-prepared statements
@@ -1342,17 +1574,18 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
 	if tx.db != stmt.db {
 		return &Stmt{stickyErr: errors.New("sql: Tx.Stmt: statement from different database used")}
 	}
-	dc, err := tx.grabConn()
+	dc, err := tx.grabConn(ctx)
 	if err != nil {
 		return &Stmt{stickyErr: err}
 	}
-	dc.Lock()
-	si, err := dc.ci.Prepare(stmt.query)
-	dc.Unlock()
+	var si driver.Stmt
+	withLock(dc, func() {
+		si, err = ctxDriverPrepare(ctx, dc.ci, stmt.query)
+	})
 	txs := &Stmt{
 		db: tx.db,
 		tx: tx,
-		txsi: &driverStmt{
+		txds: &driverStmt{
 			Locker: dc,
 			si:     si,
 		},
@@ -1365,10 +1598,26 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
 	return txs
 }
 
-// Exec executes a query that doesn't return rows.
+// Stmt returns a transaction-specific prepared statement from
+// an existing statement.
+//
+// Example:
+//  updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?")
+//  ...
+//  tx, err := db.Begin()
+//  ...
+//  res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203)
+//
+// The returned statement operates within the transaction and will be closed
+// when the transaction has been committed or rolled back.
+func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
+	return tx.StmtContext(context.Background(), stmt)
+}
+
+// ExecContext executes a query that doesn't return rows.
 // For example: an INSERT and UPDATE.
-func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
-	dc, err := tx.grabConn()
+func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) {
+	dc, err := tx.grabConn(ctx)
 	if err != nil {
 		return nil, err
 	}
@@ -1378,9 +1627,10 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
 		if err != nil {
 			return nil, err
 		}
-		dc.Lock()
-		resi, err := execer.Exec(query, dargs)
-		dc.Unlock()
+		var resi driver.Result
+		withLock(dc, func() {
+			resi, err = ctxDriverExec(ctx, execer, query, dargs)
+		})
 		if err == nil {
 			return driverResult{dc, resi}, nil
 		}
@@ -1389,39 +1639,59 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
 		}
 	}
 
-	dc.Lock()
-	si, err := dc.ci.Prepare(query)
-	dc.Unlock()
+	var si driver.Stmt
+	withLock(dc, func() {
+		si, err = ctxDriverPrepare(ctx, dc.ci, query)
+	})
 	if err != nil {
 		return nil, err
 	}
-	defer withLock(dc, func() { si.Close() })
+	ds := &driverStmt{Locker: dc, si: si}
+	defer ds.Close()
 
-	return resultFromStatement(driverStmt{dc, si}, args...)
+	return resultFromStatement(ctx, ds, args...)
 }
 
-// Query executes a query that returns rows, typically a SELECT.
-func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
-	dc, err := tx.grabConn()
+// Exec executes a query that doesn't return rows.
+// For example: an INSERT and UPDATE.
+func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
+	return tx.ExecContext(context.Background(), query, args...)
+}
+
+// QueryContext executes a query that returns rows, typically a SELECT.
+func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
+	dc, err := tx.grabConn(ctx)
 	if err != nil {
 		return nil, err
 	}
 	releaseConn := func(error) {}
-	return tx.db.queryConn(dc, releaseConn, query, args)
+	return tx.db.queryConn(ctx, dc, releaseConn, query, args)
+}
+
+// Query executes a query that returns rows, typically a SELECT.
+func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) {
+	return tx.QueryContext(context.Background(), query, args...)
+}
+
+// QueryRowContext executes a query that is expected to return at most one row.
+// QueryRowContext always returns a non-nil value. Errors are deferred until
+// Row's Scan method is called.
+func (tx *Tx) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row {
+	rows, err := tx.QueryContext(ctx, query, args...)
+	return &Row{rows: rows, err: err}
 }
 
 // QueryRow executes a query that is expected to return at most one row.
 // QueryRow always returns a non-nil value. Errors are deferred until
 // Row's Scan method is called.
 func (tx *Tx) QueryRow(query string, args ...interface{}) *Row {
-	rows, err := tx.Query(query, args...)
-	return &Row{rows: rows, err: err}
+	return tx.QueryRowContext(context.Background(), query, args...)
 }
 
 // connStmt is a prepared statement on a particular connection.
 type connStmt struct {
 	dc *driverConn
-	si driver.Stmt
+	ds *driverStmt
 }
 
 // Stmt is a prepared statement.
@@ -1436,7 +1706,7 @@ type Stmt struct {
 
 	// If in a transaction, else both nil:
 	tx   *Tx
-	txsi *driverStmt
+	txds *driverStmt
 
 	mu     sync.Mutex // protects the rest of the fields
 	closed bool
@@ -1452,15 +1722,15 @@ type Stmt struct {
 	lastNumClosed uint64
 }
 
-// Exec executes a prepared statement with the given arguments and
+// ExecContext executes a prepared statement with the given arguments and
 // returns a Result summarizing the effect of the statement.
-func (s *Stmt) Exec(args ...interface{}) (Result, error) {
+func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (Result, error) {
 	s.closemu.RLock()
 	defer s.closemu.RUnlock()
 
 	var res Result
 	for i := 0; i < maxBadConnRetries; i++ {
-		dc, releaseConn, si, err := s.connStmt()
+		_, releaseConn, ds, err := s.connStmt(ctx)
 		if err != nil {
 			if err == driver.ErrBadConn {
 				continue
@@ -1468,7 +1738,7 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
 			return nil, err
 		}
 
-		res, err = resultFromStatement(driverStmt{dc, si}, args...)
+		res, err = resultFromStatement(ctx, ds, args...)
 		releaseConn(err)
 		if err != driver.ErrBadConn {
 			return res, err
@@ -1477,13 +1747,19 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
 	return nil, driver.ErrBadConn
 }
 
-func driverNumInput(ds driverStmt) int {
+// Exec executes a prepared statement with the given arguments and
+// returns a Result summarizing the effect of the statement.
+func (s *Stmt) Exec(args ...interface{}) (Result, error) {
+	return s.ExecContext(context.Background(), args...)
+}
+
+func driverNumInput(ds *driverStmt) int {
 	ds.Lock()
 	defer ds.Unlock() // in case NumInput panics
 	return ds.si.NumInput()
 }
 
-func resultFromStatement(ds driverStmt, args ...interface{}) (Result, error) {
+func resultFromStatement(ctx context.Context, ds *driverStmt, args ...interface{}) (Result, error) {
 	want := driverNumInput(ds)
 
 	// -1 means the driver doesn't know how to count the number of
@@ -1493,14 +1769,15 @@ func resultFromStatement(ds driverStmt, args ...interface{}) (Result, error) {
 		return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args))
 	}
 
-	dargs, err := driverArgs(&ds, args)
+	dargs, err := driverArgs(ds, args)
 	if err != nil {
 		return nil, err
 	}
 
 	ds.Lock()
 	defer ds.Unlock()
-	resi, err := ds.si.Exec(dargs)
+
+	resi, err := ctxDriverStmtExec(ctx, ds.si, dargs)
 	if err != nil {
 		return nil, err
 	}
@@ -1536,7 +1813,7 @@ func (s *Stmt) removeClosedStmtLocked() {
 // connStmt returns a free driver connection on which to execute the
 // statement, a function to call to release the connection, and a
 // statement bound to that connection.
-func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.Stmt, err error) {
+func (s *Stmt) connStmt(ctx context.Context) (ci *driverConn, releaseConn func(error), ds *driverStmt, err error) {
 	if err = s.stickyErr; err != nil {
 		return
 	}
@@ -1551,19 +1828,18 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
 	// transaction was created on.
 	if s.tx != nil {
 		s.mu.Unlock()
-		ci, err = s.tx.grabConn() // blocks, waiting for the connection.
+		ci, err = s.tx.grabConn(ctx) // blocks, waiting for the connection.
 		if err != nil {
 			return
 		}
 		releaseConn = func(error) {}
-		return ci, releaseConn, s.txsi.si, nil
+		return ci, releaseConn, s.txds, nil
 	}
 
 	s.removeClosedStmtLocked()
 	s.mu.Unlock()
 
-	// TODO(bradfitz): or always wait for one? make configurable later?
-	dc, err := s.db.conn(cachedOrNewConn)
+	dc, err := s.db.conn(ctx, cachedOrNewConn)
 	if err != nil {
 		return nil, nil, nil, err
 	}
@@ -1572,36 +1848,36 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
 	for _, v := range s.css {
 		if v.dc == dc {
 			s.mu.Unlock()
-			return dc, dc.releaseConn, v.si, nil
+			return dc, dc.releaseConn, v.ds, nil
 		}
 	}
 	s.mu.Unlock()
 
 	// No luck; we need to prepare the statement on this connection
-	dc.Lock()
-	si, err = dc.prepareLocked(s.query)
-	dc.Unlock()
+	withLock(dc, func() {
+		ds, err = dc.prepareLocked(ctx, s.query)
+	})
 	if err != nil {
 		s.db.putConn(dc, err)
 		return nil, nil, nil, err
 	}
 	s.mu.Lock()
-	cs := connStmt{dc, si}
+	cs := connStmt{dc, ds}
 	s.css = append(s.css, cs)
 	s.mu.Unlock()
 
-	return dc, dc.releaseConn, si, nil
+	return dc, dc.releaseConn, ds, nil
 }
 
-// Query executes a prepared query statement with the given arguments
+// QueryContext executes a prepared query statement with the given arguments
 // and returns the query results as a *Rows.
-func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
+func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*Rows, error) {
 	s.closemu.RLock()
 	defer s.closemu.RUnlock()
 
 	var rowsi driver.Rows
 	for i := 0; i < maxBadConnRetries; i++ {
-		dc, releaseConn, si, err := s.connStmt()
+		dc, releaseConn, ds, err := s.connStmt(ctx)
 		if err != nil {
 			if err == driver.ErrBadConn {
 				continue
@@ -1609,7 +1885,7 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
 			return nil, err
 		}
 
-		rowsi, err = rowsiFromStatement(driverStmt{dc, si}, args...)
+		rowsi, err = rowsiFromStatement(ctx, ds, args...)
 		if err == nil {
 			// Note: ownership of ci passes to the *Rows, to be freed
 			// with releaseConn.
@@ -1618,6 +1894,7 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
 				rowsi: rowsi,
 				// releaseConn set below
 			}
+			rows.initContextClose(ctx)
 			s.db.addDep(s, rows)
 			rows.releaseConn = func(err error) {
 				releaseConn(err)
@@ -1634,10 +1911,17 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
 	return nil, driver.ErrBadConn
 }
 
-func rowsiFromStatement(ds driverStmt, args ...interface{}) (driver.Rows, error) {
-	ds.Lock()
-	want := ds.si.NumInput()
-	ds.Unlock()
+// Query executes a prepared query statement with the given arguments
+// and returns the query results as a *Rows.
+func (s *Stmt) Query(args ...interface{}) (*Rows, error) {
+	return s.QueryContext(context.Background(), args...)
+}
+
+func rowsiFromStatement(ctx context.Context, ds *driverStmt, args ...interface{}) (driver.Rows, error) {
+	var want int
+	withLock(ds, func() {
+		want = ds.si.NumInput()
+	})
 
 	// -1 means the driver doesn't know how to count the number of
 	// placeholders, so we won't sanity check input here and instead let the
@@ -1646,21 +1930,22 @@ func rowsiFromStatement(ds driverStmt, args ...interface{}) (driver.Rows, error)
 		return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", want, len(args))
 	}
 
-	dargs, err := driverArgs(&ds, args)
+	dargs, err := driverArgs(ds, args)
 	if err != nil {
 		return nil, err
 	}
 
 	ds.Lock()
-	rowsi, err := ds.si.Query(dargs)
-	ds.Unlock()
+	defer ds.Unlock()
+
+	rowsi, err := ctxDriverStmtQuery(ctx, ds.si, dargs)
 	if err != nil {
 		return nil, err
 	}
 	return rowsi, nil
 }
 
-// QueryRow executes a prepared query statement with the given arguments.
+// QueryRowContext executes a prepared query statement with the given arguments.
 // If an error occurs during the execution of the statement, that error will
 // be returned by a call to Scan on the returned *Row, which is always non-nil.
 // If the query selects no rows, the *Row's Scan will return ErrNoRows.
@@ -1670,15 +1955,30 @@ func rowsiFromStatement(ds driverStmt, args ...interface{}) (driver.Rows, error)
 // Example usage:
 //
 //  var name string
-//  err := nameByUseridStmt.QueryRow(id).Scan(&name)
-func (s *Stmt) QueryRow(args ...interface{}) *Row {
-	rows, err := s.Query(args...)
+//  err := nameByUseridStmt.QueryRowContext(ctx, id).Scan(&name)
+func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *Row {
+	rows, err := s.QueryContext(ctx, args...)
 	if err != nil {
 		return &Row{err: err}
 	}
 	return &Row{rows: rows}
 }
 
+// QueryRow executes a prepared query statement with the given arguments.
+// If an error occurs during the execution of the statement, that error will
+// be returned by a call to Scan on the returned *Row, which is always non-nil.
+// If the query selects no rows, the *Row's Scan will return ErrNoRows.
+// Otherwise, the *Row's Scan scans the first selected row and discards
+// the rest.
+//
+// Example usage:
+//
+//  var name string
+//  err := nameByUseridStmt.QueryRow(id).Scan(&name)
+func (s *Stmt) QueryRow(args ...interface{}) *Row {
+	return s.QueryRowContext(context.Background(), args...)
+}
+
 // Close closes the statement.
 func (s *Stmt) Close() error {
 	s.closemu.Lock()
@@ -1693,13 +1993,11 @@ func (s *Stmt) Close() error {
 		return nil
 	}
 	s.closed = true
+	s.mu.Unlock()
 
 	if s.tx != nil {
-		err := s.txsi.Close()
-		s.mu.Unlock()
-		return err
+		return s.txds.Close()
 	}
-	s.mu.Unlock()
 
 	return s.db.removeDep(s, s)
 }
@@ -1709,8 +2007,8 @@ func (s *Stmt) finalClose() error {
 	defer s.mu.Unlock()
 	if s.css != nil {
 		for _, v := range s.css {
-			s.db.noteUnusedDriverStatement(v.dc, v.si)
-			v.dc.removeOpenStmt(v.si)
+			s.db.noteUnusedDriverStatement(v.dc, v.ds)
+			v.dc.removeOpenStmt(v.ds)
 		}
 		s.css = nil
 	}
@@ -1736,10 +2034,28 @@ type Rows struct {
 	releaseConn func(error)
 	rowsi       driver.Rows
 
-	closed    bool
+	// closed value is 1 when the Rows is closed.
+	// Use atomic operations on value when checking value.
+	closed    int32
+	ctxClose  chan struct{} // closed when Rows is closed, may be null.
 	lastcols  []driver.Value
 	lasterr   error       // non-nil only if closed is true
-	closeStmt driver.Stmt // if non-nil, statement to Close on close
+	closeStmt *driverStmt // if non-nil, statement to Close on close
+}
+
+func (rs *Rows) initContextClose(ctx context.Context) {
+	if ctx.Done() == context.Background().Done() {
+		return
+	}
+
+	rs.ctxClose = make(chan struct{})
+	go func() {
+		select {
+		case <-ctx.Done():
+			rs.Close()
+		case <-rs.ctxClose:
+		}
+	}()
 }
 
 // Next prepares the next result row for reading with the Scan method. It
@@ -1749,7 +2065,7 @@ type Rows struct {
 //
 // Every call to Scan, even the first one, must be preceded by a call to Next.
 func (rs *Rows) Next() bool {
-	if rs.closed {
+	if rs.isClosed() {
 		return false
 	}
 	if rs.lastcols == nil {
@@ -1757,6 +2073,47 @@ func (rs *Rows) Next() bool {
 	}
 	rs.lasterr = rs.rowsi.Next(rs.lastcols)
 	if rs.lasterr != nil {
+		// Close the connection if there is a driver error.
+		if rs.lasterr != io.EOF {
+			rs.Close()
+			return false
+		}
+		nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet)
+		if !ok {
+			rs.Close()
+			return false
+		}
+		// The driver is at the end of the current result set.
+		// Test to see if there is another result set after the current one.
+		// Only close Rows if there is no futher result sets to read.
+		if !nextResultSet.HasNextResultSet() {
+			rs.Close()
+		}
+		return false
+	}
+	return true
+}
+
+// NextResultSet prepares the next result set for reading. It returns true if
+// there is further result sets, or false if there is no further result set
+// or if there is an error advancing to it. The Err method should be consulted
+// to distinguish between the two cases.
+//
+// After calling NextResultSet, the Next method should always be called before
+// scanning. If there are further result sets they may not have rows in the result
+// set.
+func (rs *Rows) NextResultSet() bool {
+	if rs.isClosed() {
+		return false
+	}
+	rs.lastcols = nil
+	nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet)
+	if !ok {
+		rs.Close()
+		return false
+	}
+	rs.lasterr = nextResultSet.NextResultSet()
+	if rs.lasterr != nil {
 		rs.Close()
 		return false
 	}
@@ -1776,7 +2133,7 @@ func (rs *Rows) Err() error {
 // Columns returns an error if the rows are closed, or if the rows
 // are from QueryRow and there was a deferred error.
 func (rs *Rows) Columns() ([]string, error) {
-	if rs.closed {
+	if rs.isClosed() {
 		return nil, errors.New("sql: Rows are closed")
 	}
 	if rs.rowsi == nil {
@@ -1785,6 +2142,107 @@ func (rs *Rows) Columns() ([]string, error) {
 	return rs.rowsi.Columns(), nil
 }
 
+// ColumnTypes returns column information such as column type, length,
+// and nullable. Some information may not be available from some drivers.
+func (rs *Rows) ColumnTypes() ([]*ColumnType, error) {
+	if rs.isClosed() {
+		return nil, errors.New("sql: Rows are closed")
+	}
+	if rs.rowsi == nil {
+		return nil, errors.New("sql: no Rows available")
+	}
+	return rowsColumnInfoSetup(rs.rowsi), nil
+}
+
+// ColumnType contains the name and type of a column.
+type ColumnType struct {
+	name string
+
+	hasNullable       bool
+	hasLength         bool
+	hasPrecisionScale bool
+
+	nullable     bool
+	length       int64
+	databaseType string
+	precision    int64
+	scale        int64
+	scanType     reflect.Type
+}
+
+// Name returns the name or alias of the column.
+func (ci *ColumnType) Name() string {
+	return ci.name
+}
+
+// Length returns the column type length for variable length column types such
+// as text and binary field types. If the type length is unbounded the value will
+// be math.MaxInt64 (any database limits will still apply).
+// If the column type is not variable length, such as an int, or if not supported
+// by the driver ok is false.
+func (ci *ColumnType) Length() (length int64, ok bool) {
+	return ci.length, ci.hasLength
+}
+
+// DecimalSize returns the scale and precision of a decimal type.
+// If not applicable or if not supported ok is false.
+func (ci *ColumnType) DecimalSize() (precision, scale int64, ok bool) {
+	return ci.precision, ci.scale, ci.hasPrecisionScale
+}
+
+// ScanType returns a Go type suitable for scanning into using Rows.Scan.
+// If a driver does not support this property ScanType will return
+// the type of an empty interface.
+func (ci *ColumnType) ScanType() reflect.Type {
+	return ci.scanType
+}
+
+// Nullable returns whether the column may be null.
+// If a driver does not support this property ok will be false.
+func (ci *ColumnType) Nullable() (nullable, ok bool) {
+	return ci.nullable, ci.hasNullable
+}
+
+// DatabaseTypeName returns the database system name of the column type. If an empty
+// string is returned the driver type name is not supported.
+// Consult your driver documentation for a list of driver data types. Length specifiers
+// are not included.
+// Common type include "VARCHAR", "TEXT", "NVARCHAR", "DECIMAL", "BOOL", "INT", "BIGINT".
+func (ci *ColumnType) DatabaseTypeName() string {
+	return ci.databaseType
+}
+
+func rowsColumnInfoSetup(rowsi driver.Rows) []*ColumnType {
+	names := rowsi.Columns()
+
+	list := make([]*ColumnType, len(names))
+	for i := range list {
+		ci := &ColumnType{
+			name: names[i],
+		}
+		list[i] = ci
+
+		if prop, ok := rowsi.(driver.RowsColumnTypeScanType); ok {
+			ci.scanType = prop.ColumnTypeScanType(i)
+		} else {
+			ci.scanType = reflect.TypeOf(new(interface{})).Elem()
+		}
+		if prop, ok := rowsi.(driver.RowsColumnTypeDatabaseTypeName); ok {
+			ci.databaseType = prop.ColumnTypeDatabaseTypeName(i)
+		}
+		if prop, ok := rowsi.(driver.RowsColumnTypeLength); ok {
+			ci.length, ci.hasLength = prop.ColumnTypeLength(i)
+		}
+		if prop, ok := rowsi.(driver.RowsColumnTypeNullable); ok {
+			ci.nullable, ci.hasNullable = prop.ColumnTypeNullable(i)
+		}
+		if prop, ok := rowsi.(driver.RowsColumnTypePrecisionScale); ok {
+			ci.precision, ci.scale, ci.hasPrecisionScale = prop.ColumnTypePrecisionScale(i)
+		}
+	}
+	return list
+}
+
 // Scan copies the columns in the current row into the values pointed
 // at by dest. The number of values in dest must be the same as the
 // number of columns in Rows.
@@ -1837,7 +2295,7 @@ func (rs *Rows) Columns() ([]string, error) {
 // For scanning into *bool, the source may be true, false, 1, 0, or
 // string inputs parseable by strconv.ParseBool.
 func (rs *Rows) Scan(dest ...interface{}) error {
-	if rs.closed {
+	if rs.isClosed() {
 		return errors.New("sql: Rows are closed")
 	}
 	if rs.lastcols == nil {
@@ -1857,14 +2315,21 @@ func (rs *Rows) Scan(dest ...interface{}) error {
 
 var rowsCloseHook func(*Rows, *error)
 
-// Close closes the Rows, preventing further enumeration. If Next returns
-// false, the Rows are closed automatically and it will suffice to check the
+func (rs *Rows) isClosed() bool {
+	return atomic.LoadInt32(&rs.closed) != 0
+}
+
+// Close closes the Rows, preventing further enumeration. If Next is called
+// and returns false and there are no further result sets,
+// the Rows are closed automatically and it will suffice to check the
 // result of Err. Close is idempotent and does not affect the result of Err.
 func (rs *Rows) Close() error {
-	if rs.closed {
+	if !atomic.CompareAndSwapInt32(&rs.closed, 0, 1) {
 		return nil
 	}
-	rs.closed = true
+	if rs.ctxClose != nil {
+		close(rs.ctxClose)
+	}
 	err := rs.rowsi.Close()
 	if fn := rowsCloseHook; fn != nil {
 		fn(rs, &err)
diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go
index 08df0c7..27fb765 100644
--- a/src/database/sql/sql_test.go
+++ b/src/database/sql/sql_test.go
@@ -5,6 +5,7 @@
 package sql
 
 import (
+	"context"
 	"database/sql/driver"
 	"errors"
 	"fmt"
@@ -23,6 +24,17 @@ func init() {
 		c  *driverConn
 	}
 	freedFrom := make(map[dbConn]string)
+	var mu sync.Mutex
+	getFreedFrom := func(c dbConn) string {
+		mu.Lock()
+		defer mu.Unlock()
+		return freedFrom[c]
+	}
+	setFreedFrom := func(c dbConn, s string) {
+		mu.Lock()
+		defer mu.Unlock()
+		freedFrom[c] = s
+	}
 	putConnHook = func(db *DB, c *driverConn) {
 		idx := -1
 		for i, v := range db.freeConn {
@@ -35,10 +47,10 @@ func init() {
 			// print before panic, as panic may get lost due to conflicting panic
 			// (all goroutines asleep) elsewhere, since we might not unlock
 			// the mutex in freeConn here.
-			println("double free of conn. conflicts are:\nA) " + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack())
+			println("double free of conn. conflicts are:\nA) " + getFreedFrom(dbConn{db, c}) + "\n\nand\nB) " + stack())
 			panic("double free of conn.")
 		}
-		freedFrom[dbConn{db, c}] = stack()
+		setFreedFrom(dbConn{db, c}, stack())
 	}
 }
 
@@ -140,10 +152,7 @@ func closeDB(t testing.TB, db *DB) {
 	if err != nil {
 		t.Fatalf("error closing DB: %v", err)
 	}
-	db.mu.Lock()
-	count := db.numOpen
-	db.mu.Unlock()
-	if count != 0 {
+	if count := db.numOpenConns(); count != 0 {
 		t.Fatalf("%d connections still open after closing DB", count)
 	}
 }
@@ -182,6 +191,12 @@ func (db *DB) numFreeConns() int {
 	return len(db.freeConn)
 }
 
+func (db *DB) numOpenConns() int {
+	db.mu.Lock()
+	defer db.mu.Unlock()
+	return db.numOpen
+}
+
 // clearAllConns closes all connections in db.
 func (db *DB) clearAllConns(t *testing.T) {
 	db.SetMaxIdleConns(0)
@@ -260,6 +275,257 @@ func TestQuery(t *testing.T) {
 	}
 }
 
+func TestQueryContext(t *testing.T) {
+	db := newTestDB(t, "people")
+	defer closeDB(t, db)
+	prepares0 := numPrepares(t, db)
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	rows, err := db.QueryContext(ctx, "SELECT|people|age,name|")
+	if err != nil {
+		t.Fatalf("Query: %v", err)
+	}
+	type row struct {
+		age  int
+		name string
+	}
+	got := []row{}
+	index := 0
+	for rows.Next() {
+		if index == 2 {
+			cancel()
+			time.Sleep(10 * time.Millisecond)
+		}
+		var r row
+		err = rows.Scan(&r.age, &r.name)
+		if err != nil {
+			if index == 2 {
+				break
+			}
+			t.Fatalf("Scan: %v", err)
+		}
+		if index == 2 && err == nil {
+			t.Fatal("expected an error on last scan")
+		}
+		got = append(got, r)
+		index++
+	}
+	err = rows.Err()
+	if err != nil {
+		t.Fatalf("Err: %v", err)
+	}
+	want := []row{
+		{age: 1, name: "Alice"},
+		{age: 2, name: "Bob"},
+	}
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want)
+	}
+
+	// And verify that the final rows.Next() call, which hit EOF,
+	// also closed the rows connection.
+	if n := db.numFreeConns(); n != 1 {
+		t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
+	}
+	if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
+		t.Errorf("executed %d Prepare statements; want 1", prepares)
+	}
+}
+
+func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
+	deadline := time.Now().Add(waitFor)
+	for time.Now().Before(deadline) {
+		if fn() {
+			return true
+		}
+		time.Sleep(checkEvery)
+	}
+	return false
+}
+
+func TestQueryContextWait(t *testing.T) {
+	db := newTestDB(t, "people")
+	defer closeDB(t, db)
+	prepares0 := numPrepares(t, db)
+
+	ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*15)
+
+	// This will trigger the *fakeConn.Prepare method which will take time
+	// performing the query. The ctxDriverPrepare func will check the context
+	// after this and close the rows and return an error.
+	_, err := db.QueryContext(ctx, "WAIT|1s|SELECT|people|age,name|")
+	if err != context.DeadlineExceeded {
+		t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
+	}
+
+	// Verify closed rows connection after error condition.
+	if n := db.numFreeConns(); n != 1 {
+		t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
+	}
+	if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
+		t.Errorf("executed %d Prepare statements; want 1", prepares)
+	}
+}
+
+func TestTxContextWait(t *testing.T) {
+	db := newTestDB(t, "people")
+	defer closeDB(t, db)
+
+	ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*15)
+
+	tx, err := db.BeginContext(ctx)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// This will trigger the *fakeConn.Prepare method which will take time
+	// performing the query. The ctxDriverPrepare func will check the context
+	// after this and close the rows and return an error.
+	_, err = tx.QueryContext(ctx, "WAIT|1s|SELECT|people|age,name|")
+	if err != context.DeadlineExceeded {
+		t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
+	}
+
+	var numFree int
+	if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+		numFree = db.numFreeConns()
+		return numFree == 0
+	}) {
+		t.Fatalf("free conns after hitting EOF = %d; want 0", numFree)
+	}
+
+	// Ensure the dropped connection allows more connections to be made.
+	// Checked on DB Close.
+	waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
+		return db.numOpenConns() == 0
+	})
+}
+
+func TestMultiResultSetQuery(t *testing.T) {
+	db := newTestDB(t, "people")
+	defer closeDB(t, db)
+	prepares0 := numPrepares(t, db)
+	rows, err := db.Query("SELECT|people|age,name|;SELECT|people|name|")
+	if err != nil {
+		t.Fatalf("Query: %v", err)
+	}
+	type row1 struct {
+		age  int
+		name string
+	}
+	type row2 struct {
+		name string
+	}
+	got1 := []row1{}
+	for rows.Next() {
+		var r row1
+		err = rows.Scan(&r.age, &r.name)
+		if err != nil {
+			t.Fatalf("Scan: %v", err)
+		}
+		got1 = append(got1, r)
+	}
+	err = rows.Err()
+	if err != nil {
+		t.Fatalf("Err: %v", err)
+	}
+	want1 := []row1{
+		{age: 1, name: "Alice"},
+		{age: 2, name: "Bob"},
+		{age: 3, name: "Chris"},
+	}
+	if !reflect.DeepEqual(got1, want1) {
+		t.Errorf("mismatch.\n got1: %#v\nwant: %#v", got1, want1)
+	}
+
+	if !rows.NextResultSet() {
+		t.Errorf("expected another result set")
+	}
+
+	got2 := []row2{}
+	for rows.Next() {
+		var r row2
+		err = rows.Scan(&r.name)
+		if err != nil {
+			t.Fatalf("Scan: %v", err)
+		}
+		got2 = append(got2, r)
+	}
+	err = rows.Err()
+	if err != nil {
+		t.Fatalf("Err: %v", err)
+	}
+	want2 := []row2{
+		{name: "Alice"},
+		{name: "Bob"},
+		{name: "Chris"},
+	}
+	if !reflect.DeepEqual(got2, want2) {
+		t.Errorf("mismatch.\n got: %#v\nwant: %#v", got2, want2)
+	}
+	if rows.NextResultSet() {
+		t.Errorf("expected no more result sets")
+	}
+
+	// And verify that the final rows.Next() call, which hit EOF,
+	// also closed the rows connection.
+	if n := db.numFreeConns(); n != 1 {
+		t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
+	}
+	if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
+		t.Errorf("executed %d Prepare statements; want 1", prepares)
+	}
+}
+
+func TestQueryNamedArg(t *testing.T) {
+	db := newTestDB(t, "people")
+	defer closeDB(t, db)
+	prepares0 := numPrepares(t, db)
+	rows, err := db.Query(
+		// Ensure the name and age parameters only match on placeholder name, not position.
+		"SELECT|people|age,name|name=?name,age=?age",
+		Named("?age", 2),
+		Named("?name", "Bob"),
+	)
+	if err != nil {
+		t.Fatalf("Query: %v", err)
+	}
+	type row struct {
+		age  int
+		name string
+	}
+	got := []row{}
+	for rows.Next() {
+		var r row
+		err = rows.Scan(&r.age, &r.name)
+		if err != nil {
+			t.Fatalf("Scan: %v", err)
+		}
+		got = append(got, r)
+	}
+	err = rows.Err()
+	if err != nil {
+		t.Fatalf("Err: %v", err)
+	}
+	want := []row{
+		{age: 2, name: "Bob"},
+	}
+	if !reflect.DeepEqual(got, want) {
+		t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want)
+	}
+
+	// And verify that the final rows.Next() call, which hit EOF,
+	// also closed the rows connection.
+	if n := db.numFreeConns(); n != 1 {
+		t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
+	}
+	if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
+		t.Errorf("executed %d Prepare statements; want 1", prepares)
+	}
+}
+
 func TestByteOwnership(t *testing.T) {
 	db := newTestDB(t, "people")
 	defer closeDB(t, db)
@@ -317,6 +583,56 @@ func TestRowsColumns(t *testing.T) {
 	}
 }
 
+func TestRowsColumnTypes(t *testing.T) {
+	db := newTestDB(t, "people")
+	defer closeDB(t, db)
+	rows, err := db.Query("SELECT|people|age,name|")
+	if err != nil {
+		t.Fatalf("Query: %v", err)
+	}
+	tt, err := rows.ColumnTypes()
+	if err != nil {
+		t.Fatalf("ColumnTypes: %v", err)
+	}
+
+	types := make([]reflect.Type, len(tt))
+	for i, tp := range tt {
+		st := tp.ScanType()
+		if st == nil {
+			t.Errorf("scantype is null for column %q", tp.Name())
+			continue
+		}
+		types[i] = st
+	}
+	values := make([]interface{}, len(tt))
+	for i := range values {
+		values[i] = reflect.New(types[i]).Interface()
+	}
+	ct := 0
+	for rows.Next() {
+		err = rows.Scan(values...)
+		if err != nil {
+			t.Fatalf("failed to scan values in %v", err)
+		}
+		ct++
+		if ct == 0 {
+			if values[0].(string) != "Bob" {
+				t.Errorf("Expected Bob, got %v", values[0])
+			}
+			if values[1].(int) != 2 {
+				t.Errorf("Expected 2, got %v", values[1])
+			}
+		}
+	}
+	if ct != 3 {
+		t.Errorf("expected 3 rows, got %d", ct)
+	}
+
+	if err := rows.Close(); err != nil {
+		t.Errorf("error closing rows: %s", err)
+	}
+}
+
 func TestQueryRow(t *testing.T) {
 	db := newTestDB(t, "people")
 	defer closeDB(t, db)
@@ -439,7 +755,7 @@ func TestStatementClose(t *testing.T) {
 		msg  string
 	}{
 		{&Stmt{stickyErr: want}, "stickyErr not propagated"},
-		{&Stmt{tx: &Tx{}, txsi: &driverStmt{&sync.Mutex{}, stubDriverStmt{want}}}, "driverStmt.Close() error not propagated"},
+		{&Stmt{tx: &Tx{}, txds: &driverStmt{Locker: &sync.Mutex{}, si: stubDriverStmt{want}}}, "driverStmt.Close() error not propagated"},
 	}
 	for _, test := range tests {
 		if err := test.stmt.Close(); err != want {
@@ -513,8 +829,8 @@ func TestExec(t *testing.T) {
 		{[]interface{}{7, 9}, ""},
 
 		// Invalid conversions:
-		{[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting argument #1's type: sql/driver: value 4294967295 overflows int32"},
-		{[]interface{}{"Brad", "strconv fail"}, "sql: converting argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
+		{[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting argument $2 type: sql/driver: value 4294967295 overflows int32"},
+		{[]interface{}{"Brad", "strconv fail"}, `sql: converting argument $2 type: sql/driver: value "strconv fail" can't be converted to int32`},
 
 		// Wrong number of args:
 		{[]interface{}{}, "sql: expected 2 arguments, got 0"},
@@ -1159,17 +1475,19 @@ func TestMaxOpenConnsOnBusy(t *testing.T) {
 
 	db.SetMaxOpenConns(3)
 
-	conn0, err := db.conn(cachedOrNewConn)
+	ctx := context.Background()
+
+	conn0, err := db.conn(ctx, cachedOrNewConn)
 	if err != nil {
 		t.Fatalf("db open conn fail: %v", err)
 	}
 
-	conn1, err := db.conn(cachedOrNewConn)
+	conn1, err := db.conn(ctx, cachedOrNewConn)
 	if err != nil {
 		t.Fatalf("db open conn fail: %v", err)
 	}
 
-	conn2, err := db.conn(cachedOrNewConn)
+	conn2, err := db.conn(ctx, cachedOrNewConn)
 	if err != nil {
 		t.Fatalf("db open conn fail: %v", err)
 	}
@@ -1203,7 +1521,11 @@ func TestPendingConnsAfterErr(t *testing.T) {
 		tryOpen = maxOpen*2 + 2
 	)
 
-	db := newTestDB(t, "people")
+	// No queries will be run.
+	db, err := Open("test", fakeDBName)
+	if err != nil {
+		t.Fatalf("Open: %v", err)
+	}
 	defer closeDB(t, db)
 	defer func() {
 		for k, v := range db.lastPut {
@@ -1215,29 +1537,29 @@ func TestPendingConnsAfterErr(t *testing.T) {
 	db.SetMaxIdleConns(0)
 
 	errOffline := errors.New("db offline")
+
 	defer func() { setHookOpenErr(nil) }()
 
 	errs := make(chan error, tryOpen)
 
-	unblock := make(chan struct{})
+	var opening sync.WaitGroup
+	opening.Add(tryOpen)
+
 	setHookOpenErr(func() error {
-		<-unblock // block until all connections are in flight
+		// Wait for all connections to enqueue.
+		opening.Wait()
 		return errOffline
 	})
 
-	var opening sync.WaitGroup
-	opening.Add(tryOpen)
 	for i := 0; i < tryOpen; i++ {
 		go func() {
 			opening.Done() // signal one connection is in flight
-			_, err := db.Exec("INSERT|people|name=Julia,age=19")
+			_, err := db.Exec("will never run")
 			errs <- err
 		}()
 	}
 
-	opening.Wait()                    // wait for all workers to begin running
-	time.Sleep(10 * time.Millisecond) // make extra sure all workers are blocked
-	close(unblock)                    // let all workers proceed
+	opening.Wait() // wait for all workers to begin running
 
 	const timeout = 5 * time.Second
 	to := time.NewTimer(timeout)
@@ -1254,6 +1576,24 @@ func TestPendingConnsAfterErr(t *testing.T) {
 			t.Fatalf("orphaned connection request(s), still waiting after %v", timeout)
 		}
 	}
+
+	// Wait a reasonable time for the database to close all connections.
+	tick := time.NewTicker(3 * time.Millisecond)
+	defer tick.Stop()
+	for {
+		select {
+		case <-tick.C:
+			db.mu.Lock()
+			if db.numOpen == 0 {
+				db.mu.Unlock()
+				return
+			}
+			db.mu.Unlock()
+		case <-to.C:
+			// Closing the database will check for numOpen and fail the test.
+			return
+		}
+	}
 }
 
 func TestSingleOpenConn(t *testing.T) {
@@ -2279,7 +2619,8 @@ func TestConnectionLeak(t *testing.T) {
 	go func() {
 		r, err := db.Query("SELECT|people|name|")
 		if err != nil {
-			t.Fatal(err)
+			t.Error(err)
+			return
 		}
 		r.Close()
 		wg.Done()
@@ -2299,6 +2640,97 @@ func TestConnectionLeak(t *testing.T) {
 	wg.Wait()
 }
 
+// badConn implements a bad driver.Conn, for TestBadDriver.
+// The Exec method panics.
+type badConn struct{}
+
+func (bc badConn) Prepare(query string) (driver.Stmt, error) {
+	return nil, errors.New("badConn Prepare")
+}
+
+func (bc badConn) Close() error {
+	return nil
+}
+
+func (bc badConn) Begin() (driver.Tx, error) {
+	return nil, errors.New("badConn Begin")
+}
+
+func (bc badConn) Exec(query string, args []driver.Value) (driver.Result, error) {
+	panic("badConn.Exec")
+}
+
+// badDriver is a driver.Driver that uses badConn.
+type badDriver struct{}
+
+func (bd badDriver) Open(name string) (driver.Conn, error) {
+	return badConn{}, nil
+}
+
+// Issue 15901.
+func TestBadDriver(t *testing.T) {
+	Register("bad", badDriver{})
+	db, err := Open("bad", "ignored")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		if r := recover(); r == nil {
+			t.Error("expected panic")
+		} else {
+			if want := "badConn.Exec"; r.(string) != want {
+				t.Errorf("panic was %v, expected %v", r, want)
+			}
+		}
+	}()
+	defer db.Close()
+	db.Exec("ignored")
+}
+
+type pingDriver struct {
+	fails bool
+}
+
+type pingConn struct {
+	badConn
+	driver *pingDriver
+}
+
+var pingError = errors.New("Ping failed")
+
+func (pc pingConn) Ping(ctx context.Context) error {
+	if pc.driver.fails {
+		return pingError
+	}
+	return nil
+}
+
+var _ driver.Pinger = pingConn{}
+
+func (pd *pingDriver) Open(name string) (driver.Conn, error) {
+	return pingConn{driver: pd}, nil
+}
+
+func TestPing(t *testing.T) {
+	driver := &pingDriver{}
+	Register("ping", driver)
+
+	db, err := Open("ping", "ignored")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err := db.Ping(); err != nil {
+		t.Errorf("err was %#v, expected nil", err)
+		return
+	}
+
+	driver.fails = true
+	if err := db.Ping(); err != pingError {
+		t.Errorf("err was %#v, expected pingError", err)
+	}
+}
+
 func BenchmarkConcurrentDBExec(b *testing.B) {
 	b.ReportAllocs()
 	ct := new(concurrentDBExecTest)
diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go
index c173ea9..8eeab65 100644
--- a/src/debug/elf/file.go
+++ b/src/debug/elf/file.go
@@ -579,7 +579,7 @@ func (f *File) Section(name string) *Section {
 }
 
 // applyRelocations applies relocations to dst. rels is a relocations section
-// in RELA format.
+// in REL or RELA format.
 func (f *File) applyRelocations(dst []byte, rels []byte) error {
 	switch {
 	case f.Class == ELFCLASS64 && f.Machine == EM_X86_64:
@@ -594,10 +594,14 @@ func (f *File) applyRelocations(dst []byte, rels []byte) error {
 		return f.applyRelocationsPPC(dst, rels)
 	case f.Class == ELFCLASS64 && f.Machine == EM_PPC64:
 		return f.applyRelocationsPPC64(dst, rels)
+	case f.Class == ELFCLASS32 && f.Machine == EM_MIPS:
+		return f.applyRelocationsMIPS(dst, rels)
 	case f.Class == ELFCLASS64 && f.Machine == EM_MIPS:
 		return f.applyRelocationsMIPS64(dst, rels)
 	case f.Class == ELFCLASS64 && f.Machine == EM_S390:
 		return f.applyRelocationss390x(dst, rels)
+	case f.Class == ELFCLASS64 && f.Machine == EM_SPARCV9:
+		return f.applyRelocationsSPARC64(dst, rels)
 	default:
 		return errors.New("applyRelocations: not implemented")
 	}
@@ -861,6 +865,44 @@ func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {
 	return nil
 }
 
+func (f *File) applyRelocationsMIPS(dst []byte, rels []byte) error {
+	// 8 is the size of Rel32.
+	if len(rels)%8 != 0 {
+		return errors.New("length of relocation section is not a multiple of 8")
+	}
+
+	symbols, _, err := f.getSymbols(SHT_SYMTAB)
+	if err != nil {
+		return err
+	}
+
+	b := bytes.NewReader(rels)
+	var rel Rel32
+
+	for b.Len() > 0 {
+		binary.Read(b, f.ByteOrder, &rel)
+		symNo := rel.Info >> 8
+		t := R_MIPS(rel.Info & 0xff)
+
+		if symNo == 0 || symNo > uint32(len(symbols)) {
+			continue
+		}
+		sym := &symbols[symNo-1]
+
+		switch t {
+		case R_MIPS_32:
+			if rel.Off+4 >= uint32(len(dst)) {
+				continue
+			}
+			val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
+			val += uint32(sym.Value)
+			f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
+		}
+	}
+
+	return nil
+}
+
 func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error {
 	// 24 is the size of Rela64.
 	if len(rels)%24 != 0 {
@@ -962,6 +1004,51 @@ func (f *File) applyRelocationss390x(dst []byte, rels []byte) error {
 	return nil
 }
 
+func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error {
+	// 24 is the size of Rela64.
+	if len(rels)%24 != 0 {
+		return errors.New("length of relocation section is not a multiple of 24")
+	}
+
+	symbols, _, err := f.getSymbols(SHT_SYMTAB)
+	if err != nil {
+		return err
+	}
+
+	b := bytes.NewReader(rels)
+	var rela Rela64
+
+	for b.Len() > 0 {
+		binary.Read(b, f.ByteOrder, &rela)
+		symNo := rela.Info >> 32
+		t := R_SPARC(rela.Info & 0xff)
+
+		if symNo == 0 || symNo > uint64(len(symbols)) {
+			continue
+		}
+		sym := &symbols[symNo-1]
+		if SymType(sym.Info&0xf) != STT_SECTION {
+			// We don't handle non-section relocations for now.
+			continue
+		}
+
+		switch t {
+		case R_SPARC_64, R_SPARC_UA64:
+			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
+				continue
+			}
+			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
+		case R_SPARC_32, R_SPARC_UA32:
+			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
+				continue
+			}
+			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
+		}
+	}
+
+	return nil
+}
+
 func (f *File) DWARF() (*dwarf.Data, error) {
 	// sectionData gets the data for s, checks its size, and
 	// applies any applicable relations.
diff --git a/src/debug/elf/file_test.go b/src/debug/elf/file_test.go
index b189219..58bdf27 100644
--- a/src/debug/elf/file_test.go
+++ b/src/debug/elf/file_test.go
@@ -492,6 +492,63 @@ var relocationTests = []relocationTest{
 		},
 	},
 	{
+		"testdata/go-relocation-test-gcc620-sparc64.obj",
+		[]relocationTestEntry{
+			{0, &dwarf.Entry{
+				Offset:   0xb,
+				Tag:      dwarf.TagCompileUnit,
+				Children: true,
+				Field: []dwarf.Field{
+					{Attr: dwarf.AttrProducer, Val: "GNU C11 6.2.0 20160914 -mcpu=v9 -g -fstack-protector-strong", Class: dwarf.ClassString},
+					{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
+					{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
+					{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
+					{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
+					{Attr: dwarf.AttrHighpc, Val: int64(0x2c), Class: dwarf.ClassConstant},
+					{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
+				},
+			}},
+		},
+	},
+	{
+		"testdata/go-relocation-test-gcc492-mipsle.obj",
+		[]relocationTestEntry{
+			{0, &dwarf.Entry{
+				Offset:   0xb,
+				Tag:      dwarf.TagCompileUnit,
+				Children: true,
+				Field: []dwarf.Field{
+					{Attr: dwarf.AttrProducer, Val: "GNU C 4.9.2 -mel -march=mips2 -mtune=mips32 -mllsc -mno-shared -mabi=32 -g", Class: dwarf.ClassString},
+					{Attr: dwarf.AttrLanguage, Val: int64(1), Class: dwarf.ClassConstant},
+					{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
+					{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
+					{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
+					{Attr: dwarf.AttrHighpc, Val: int64(0x58), Class: dwarf.ClassConstant},
+					{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
+				},
+			}},
+		},
+	},
+	{
+		"testdata/go-relocation-test-gcc540-mips.obj",
+		[]relocationTestEntry{
+			{0, &dwarf.Entry{
+				Offset:   0xb,
+				Tag:      dwarf.TagCompileUnit,
+				Children: true,
+				Field: []dwarf.Field{
+					{Attr: dwarf.AttrProducer, Val: "GNU C11 5.4.0 20160609 -meb -mips32 -mtune=mips32r2 -mfpxx -mllsc -mno-shared -mabi=32 -g -gdwarf-2", Class: dwarf.ClassString},
+					{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
+					{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
+					{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
+					{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
+					{Attr: dwarf.AttrHighpc, Val: uint64(0x5c), Class: dwarf.ClassAddress},
+					{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
+				},
+			}},
+		},
+	},
+	{
 		"testdata/go-relocation-test-gcc493-mips64le.obj",
 		[]relocationTestEntry{
 			{0, &dwarf.Entry{
diff --git a/src/debug/elf/testdata/go-relocation-test-gcc492-mipsle.obj b/src/debug/elf/testdata/go-relocation-test-gcc492-mipsle.obj
new file mode 100644
index 0000000..a5fbcfb
Binary files /dev/null and b/src/debug/elf/testdata/go-relocation-test-gcc492-mipsle.obj differ
diff --git a/src/debug/elf/testdata/go-relocation-test-gcc540-mips.obj b/src/debug/elf/testdata/go-relocation-test-gcc540-mips.obj
new file mode 100644
index 0000000..270c777
Binary files /dev/null and b/src/debug/elf/testdata/go-relocation-test-gcc540-mips.obj differ
diff --git a/src/debug/elf/testdata/go-relocation-test-gcc620-sparc64.obj b/src/debug/elf/testdata/go-relocation-test-gcc620-sparc64.obj
new file mode 100644
index 0000000..d65c23e
Binary files /dev/null and b/src/debug/elf/testdata/go-relocation-test-gcc620-sparc64.obj differ
diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go
index e859d5a..e94ed19 100644
--- a/src/debug/gosym/pclntab.go
+++ b/src/debug/gosym/pclntab.go
@@ -291,13 +291,17 @@ func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
 	return true
 }
 
+// PCValue looks up the given PC in a pc value table. target is the
+// offset of the pc from the entry point.
+func PCValue(tab []byte, target uint64, quantum int) int {
+	t := LineTable{Data: tab, quantum: uint32(quantum)}
+	return int(t.pcvalue(0, 0, target))
+}
+
 // pcvalue reports the value associated with the target pc.
 // off is the offset to the beginning of the pc-value table,
 // and entry is the start PC for the corresponding function.
 func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
-	if off == 0 {
-		return -1
-	}
 	p := t.Data[off:]
 
 	val := int32(-1)
diff --git a/src/debug/gosym/pclntab_test.go b/src/debug/gosym/pclntab_test.go
index 9f82e31..7e7cee6 100644
--- a/src/debug/gosym/pclntab_test.go
+++ b/src/debug/gosym/pclntab_test.go
@@ -37,7 +37,7 @@ func dotest(t *testing.T) {
 	// the resulting binary looks like it was built from pclinetest.s,
 	// but we have renamed it to keep it away from the go tool.
 	pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
-	cmd := exec.Command("go", "tool", "asm", "-o", pclinetestBinary+".o", "pclinetest.asm")
+	cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", pclinetestBinary+".o", "pclinetest.asm")
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
 	if err := cmd.Run(); err != nil {
@@ -58,7 +58,7 @@ func dotest(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	cmd = exec.Command("go", "tool", "link", "-H", "linux",
+	cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-H", "linux",
 		"-o", pclinetestBinary, pclinetestBinary+".o")
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
diff --git a/src/debug/macho/macho.go b/src/debug/macho/macho.go
index 3164753..40ac74e 100644
--- a/src/debug/macho/macho.go
+++ b/src/debug/macho/macho.go
@@ -145,7 +145,7 @@ type Section32 struct {
 	Reserve2 uint32
 }
 
-// A Section32 is a 64-bit Mach-O section header.
+// A Section64 is a 64-bit Mach-O section header.
 type Section64 struct {
 	Name     [16]byte
 	Seg      [16]byte
diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go
index 3074ba0..87f225c 100644
--- a/src/debug/pe/file.go
+++ b/src/debug/pe/file.go
@@ -13,14 +13,17 @@ import (
 	"os"
 )
 
+// Avoid use of post-Go 1.4 io features, to make safe for toolchain bootstrap.
+const seekStart = 0
+
 // A File represents an open PE file.
 type File struct {
 	FileHeader
 	OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
 	Sections       []*Section
 	Symbols        []*Symbol    // COFF symbols with auxiliary symbol records removed
-	_COFFSymbols   []COFFSymbol // all COFF symbols (including auxiliary symbol records)
-	_StringTable   _StringTable
+	COFFSymbols    []COFFSymbol // all COFF symbols (including auxiliary symbol records)
+	StringTable    StringTable
 
 	closer io.Closer
 }
@@ -80,7 +83,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 	} else {
 		base = int64(0)
 	}
-	sr.Seek(base, io.SeekStart)
+	sr.Seek(base, seekStart)
 	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
 		return nil, err
 	}
@@ -93,23 +96,23 @@ func NewFile(r io.ReaderAt) (*File, error) {
 	var err error
 
 	// Read string table.
-	f._StringTable, err = readStringTable(&f.FileHeader, sr)
+	f.StringTable, err = readStringTable(&f.FileHeader, sr)
 	if err != nil {
 		return nil, err
 	}
 
 	// Read symbol table.
-	f._COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)
+	f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)
 	if err != nil {
 		return nil, err
 	}
-	f.Symbols, err = removeAuxSymbols(f._COFFSymbols, f._StringTable)
+	f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable)
 	if err != nil {
 		return nil, err
 	}
 
 	// Read optional header.
-	sr.Seek(base, io.SeekStart)
+	sr.Seek(base, seekStart)
 	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
 		return nil, err
 	}
@@ -141,7 +144,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 		if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
 			return nil, err
 		}
-		name, err := sh.fullName(f._StringTable)
+		name, err := sh.fullName(f.StringTable)
 		if err != nil {
 			return nil, err
 		}
@@ -168,7 +171,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 	}
 	for i := range f.Sections {
 		var err error
-		f.Sections[i]._Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
+		f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
 		if err != nil {
 			return nil, err
 		}
diff --git a/src/debug/pe/file_test.go b/src/debug/pe/file_test.go
index 964caf5..5a740c8 100644
--- a/src/debug/pe/file_test.go
+++ b/src/debug/pe/file_test.go
@@ -307,7 +307,7 @@ func main() {
 	src := filepath.Join(tmpdir, "a.go")
 	exe := filepath.Join(tmpdir, "a.exe")
 	err = ioutil.WriteFile(src, []byte(prog), 0644)
-	output, err := exec.Command("go", "build", "-o", exe, src).CombinedOutput()
+	output, err := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, src).CombinedOutput()
 	if err != nil {
 		t.Fatalf("building test executable failed: %s %s", err, output)
 	}
diff --git a/src/debug/pe/section.go b/src/debug/pe/section.go
index 8e6690f..b641158 100644
--- a/src/debug/pe/section.go
+++ b/src/debug/pe/section.go
@@ -28,7 +28,7 @@ type SectionHeader32 struct {
 // fullName finds real name of section sh. Normally name is stored
 // in sh.Name, but if it is longer then 8 characters, it is stored
 // in COFF string table st instead.
-func (sh *SectionHeader32) fullName(st _StringTable) (string, error) {
+func (sh *SectionHeader32) fullName(st StringTable) (string, error) {
 	if sh.Name[0] != '/' {
 		return cstring(sh.Name[:]), nil
 	}
@@ -41,23 +41,23 @@ func (sh *SectionHeader32) fullName(st _StringTable) (string, error) {
 
 // TODO(brainman): copy all IMAGE_REL_* consts from ldpe.go here
 
-// _Reloc represents a PE COFF relocation.
+// Reloc represents a PE COFF relocation.
 // Each section contains its own relocation list.
-type _Reloc struct {
+type Reloc struct {
 	VirtualAddress   uint32
 	SymbolTableIndex uint32
 	Type             uint16
 }
 
-func readRelocs(sh *SectionHeader, r io.ReadSeeker) ([]_Reloc, error) {
+func readRelocs(sh *SectionHeader, r io.ReadSeeker) ([]Reloc, error) {
 	if sh.NumberOfRelocations <= 0 {
 		return nil, nil
 	}
-	_, err := r.Seek(int64(sh.PointerToRelocations), io.SeekStart)
+	_, err := r.Seek(int64(sh.PointerToRelocations), seekStart)
 	if err != nil {
 		return nil, fmt.Errorf("fail to seek to %q section relocations: %v", sh.Name, err)
 	}
-	relocs := make([]_Reloc, sh.NumberOfRelocations)
+	relocs := make([]Reloc, sh.NumberOfRelocations)
 	err = binary.Read(r, binary.LittleEndian, relocs)
 	if err != nil {
 		return nil, fmt.Errorf("fail to read section relocations: %v", err)
@@ -83,7 +83,7 @@ type SectionHeader struct {
 // Section provides access to PE COFF section.
 type Section struct {
 	SectionHeader
-	_Relocs []_Reloc
+	Relocs []Reloc
 
 	// Embed ReaderAt for ReadAt method.
 	// Do not embed SectionReader directly
diff --git a/src/debug/pe/string.go b/src/debug/pe/string.go
index 69837f6..c30255f 100644
--- a/src/debug/pe/string.go
+++ b/src/debug/pe/string.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The Go Authors.  All rights reserved.
+// Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
@@ -19,16 +19,16 @@ func cstring(b []byte) string {
 	return string(b[:i])
 }
 
-// _StringTable is a COFF string table.
-type _StringTable []byte
+// StringTable is a COFF string table.
+type StringTable []byte
 
-func readStringTable(fh *FileHeader, r io.ReadSeeker) (_StringTable, error) {
+func readStringTable(fh *FileHeader, r io.ReadSeeker) (StringTable, error) {
 	// COFF string table is located right after COFF symbol table.
 	if fh.PointerToSymbolTable <= 0 {
 		return nil, nil
 	}
 	offset := fh.PointerToSymbolTable + COFFSymbolSize*fh.NumberOfSymbols
-	_, err := r.Seek(int64(offset), io.SeekStart)
+	_, err := r.Seek(int64(offset), seekStart)
 	if err != nil {
 		return nil, fmt.Errorf("fail to seek to string table: %v", err)
 	}
@@ -47,13 +47,13 @@ func readStringTable(fh *FileHeader, r io.ReadSeeker) (_StringTable, error) {
 	if err != nil {
 		return nil, fmt.Errorf("fail to read string table: %v", err)
 	}
-	return _StringTable(buf), nil
+	return StringTable(buf), nil
 }
 
 // TODO(brainman): decide if start parameter should be int instead of uint32
 
 // String extracts string from COFF string table st at offset start.
-func (st _StringTable) String(start uint32) (string, error) {
+func (st StringTable) String(start uint32) (string, error) {
 	// start includes 4 bytes of string table length
 	if start < 4 {
 		return "", fmt.Errorf("offset %d is before the start of string table", start)
diff --git a/src/debug/pe/symbol.go b/src/debug/pe/symbol.go
index 7b8cbf2..7fa5948 100644
--- a/src/debug/pe/symbol.go
+++ b/src/debug/pe/symbol.go
@@ -23,10 +23,13 @@ type COFFSymbol struct {
 }
 
 func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) {
+	if fh.PointerToSymbolTable == 0 {
+		return nil, nil
+	}
 	if fh.NumberOfSymbols <= 0 {
 		return nil, nil
 	}
-	_, err := r.Seek(int64(fh.PointerToSymbolTable), io.SeekStart)
+	_, err := r.Seek(int64(fh.PointerToSymbolTable), seekStart)
 	if err != nil {
 		return nil, fmt.Errorf("fail to seek to symbol table: %v", err)
 	}
@@ -46,17 +49,17 @@ func isSymNameOffset(name [8]byte) (bool, uint32) {
 	return false, 0
 }
 
-// _FullName finds real name of symbol sym. Normally name is stored
+// FullName finds real name of symbol sym. Normally name is stored
 // in sym.Name, but if it is longer then 8 characters, it is stored
 // in COFF string table st instead.
-func (sym *COFFSymbol) _FullName(st _StringTable) (string, error) {
+func (sym *COFFSymbol) FullName(st StringTable) (string, error) {
 	if ok, offset := isSymNameOffset(sym.Name); ok {
 		return st.String(offset)
 	}
 	return cstring(sym.Name[:]), nil
 }
 
-func removeAuxSymbols(allsyms []COFFSymbol, st _StringTable) ([]*Symbol, error) {
+func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) {
 	if len(allsyms) == 0 {
 		return nil, nil
 	}
@@ -67,7 +70,7 @@ func removeAuxSymbols(allsyms []COFFSymbol, st _StringTable) ([]*Symbol, error)
 			aux--
 			continue
 		}
-		name, err := sym._FullName(st)
+		name, err := sym.FullName(st)
 		if err != nil {
 			return nil, err
 		}
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
index 2b5ad08..044f74a 100644
--- a/src/encoding/asn1/asn1.go
+++ b/src/encoding/asn1/asn1.go
@@ -841,6 +841,13 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
 	case reflect.Struct:
 		structType := fieldType
 
+		for i := 0; i < structType.NumField(); i++ {
+			if structType.Field(i).PkgPath != "" {
+				err = StructuralError{"struct contains unexported fields"}
+				return
+			}
+		}
+
 		if structType.NumField() > 0 &&
 			structType.Field(0).Type == rawContentsType {
 			bytes := bytes[initOffset:offset]
@@ -969,7 +976,7 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
 // The following tags on struct fields have special meaning to Unmarshal:
 //
 //	application	specifies that a APPLICATION tag is used
-//	default:x	sets the default value for optional integer fields
+//	default:x	sets the default value for optional integer fields (only used if optional is also present)
 //	explicit	specifies that an additional, explicit tag wraps the implicit one
 //	optional	marks the field as ASN.1 OPTIONAL
 //	set		causes a SET, rather than a SEQUENCE type to be expected
diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go
index f8623fa..9976656 100644
--- a/src/encoding/asn1/asn1_test.go
+++ b/src/encoding/asn1/asn1_test.go
@@ -132,9 +132,13 @@ func TestParseBigInt(t *testing.T) {
 			if ret.String() != test.base10 {
 				t.Errorf("#%d: bad result from %x, got %s want %s", i, test.in, ret.String(), test.base10)
 			}
-			fw := newForkableWriter()
-			marshalBigInt(fw, ret)
-			result := fw.Bytes()
+			e, err := makeBigInt(ret)
+			if err != nil {
+				t.Errorf("%d: err=%q", i, err)
+				continue
+			}
+			result := make([]byte, e.Len())
+			e.Encode(result)
 			if !bytes.Equal(result, test.in) {
 				t.Errorf("#%d: got %x from marshaling %s, want %x", i, result, ret, test.in)
 			}
@@ -963,7 +967,7 @@ func TestUnmarshalInvalidUTF8(t *testing.T) {
 func TestMarshalNilValue(t *testing.T) {
 	nilValueTestData := []interface{}{
 		nil,
-		struct{ v interface{} }{},
+		struct{ V interface{} }{},
 	}
 	for i, test := range nilValueTestData {
 		if _, err := Marshal(test); err == nil {
@@ -971,3 +975,32 @@ func TestMarshalNilValue(t *testing.T) {
 		}
 	}
 }
+
+type unexported struct {
+	X int
+	y int
+}
+
+type exported struct {
+	X int
+	Y int
+}
+
+func TestUnexportedStructField(t *testing.T) {
+	want := StructuralError{"struct contains unexported fields"}
+
+	_, err := Marshal(unexported{X: 5, y: 1})
+	if err != want {
+		t.Errorf("got %v, want %v", err, want)
+	}
+
+	bs, err := Marshal(exported{X: 5, Y: 1})
+	if err != nil {
+		t.Fatal(err)
+	}
+	var u unexported
+	_, err = Unmarshal(bs, &u)
+	if err != want {
+		t.Errorf("got %v, want %v", err, want)
+	}
+}
diff --git a/src/encoding/asn1/marshal.go b/src/encoding/asn1/marshal.go
index 30797ef..76d0b0c 100644
--- a/src/encoding/asn1/marshal.go
+++ b/src/encoding/asn1/marshal.go
@@ -5,77 +5,125 @@
 package asn1
 
 import (
-	"bytes"
 	"errors"
 	"fmt"
-	"io"
 	"math/big"
 	"reflect"
 	"time"
 	"unicode/utf8"
 )
 
-// A forkableWriter is an in-memory buffer that can be
-// 'forked' to create new forkableWriters that bracket the
-// original. After
-//    pre, post := w.fork()
-// the overall sequence of bytes represented is logically w+pre+post.
-type forkableWriter struct {
-	*bytes.Buffer
-	pre, post *forkableWriter
+var (
+	byte00Encoder encoder = byteEncoder(0x00)
+	byteFFEncoder encoder = byteEncoder(0xff)
+)
+
+// encoder represents a ASN.1 element that is waiting to be marshaled.
+type encoder interface {
+	// Len returns the number of bytes needed to marshal this element.
+	Len() int
+	// Encode encodes this element by writing Len() bytes to dst.
+	Encode(dst []byte)
+}
+
+type byteEncoder byte
+
+func (c byteEncoder) Len() int {
+	return 1
 }
 
-func newForkableWriter() *forkableWriter {
-	return &forkableWriter{new(bytes.Buffer), nil, nil}
+func (c byteEncoder) Encode(dst []byte) {
+	dst[0] = byte(c)
 }
 
-func (f *forkableWriter) fork() (pre, post *forkableWriter) {
-	if f.pre != nil || f.post != nil {
-		panic("have already forked")
+type bytesEncoder []byte
+
+func (b bytesEncoder) Len() int {
+	return len(b)
+}
+
+func (b bytesEncoder) Encode(dst []byte) {
+	if copy(dst, b) != len(b) {
+		panic("internal error")
 	}
-	f.pre = newForkableWriter()
-	f.post = newForkableWriter()
-	return f.pre, f.post
 }
 
-func (f *forkableWriter) Len() (l int) {
-	l += f.Buffer.Len()
-	if f.pre != nil {
-		l += f.pre.Len()
+type stringEncoder string
+
+func (s stringEncoder) Len() int {
+	return len(s)
+}
+
+func (s stringEncoder) Encode(dst []byte) {
+	if copy(dst, s) != len(s) {
+		panic("internal error")
 	}
-	if f.post != nil {
-		l += f.post.Len()
+}
+
+type multiEncoder []encoder
+
+func (m multiEncoder) Len() int {
+	var size int
+	for _, e := range m {
+		size += e.Len()
 	}
-	return
+	return size
 }
 
-func (f *forkableWriter) writeTo(out io.Writer) (n int, err error) {
-	n, err = out.Write(f.Bytes())
-	if err != nil {
-		return
+func (m multiEncoder) Encode(dst []byte) {
+	var off int
+	for _, e := range m {
+		e.Encode(dst[off:])
+		off += e.Len()
 	}
+}
 
-	var nn int
+type taggedEncoder struct {
+	// scratch contains temporary space for encoding the tag and length of
+	// an element in order to avoid extra allocations.
+	scratch [8]byte
+	tag     encoder
+	body    encoder
+}
 
-	if f.pre != nil {
-		nn, err = f.pre.writeTo(out)
-		n += nn
-		if err != nil {
-			return
-		}
+func (t *taggedEncoder) Len() int {
+	return t.tag.Len() + t.body.Len()
+}
+
+func (t *taggedEncoder) Encode(dst []byte) {
+	t.tag.Encode(dst)
+	t.body.Encode(dst[t.tag.Len():])
+}
+
+type int64Encoder int64
+
+func (i int64Encoder) Len() int {
+	n := 1
+
+	for i > 127 {
+		n++
+		i >>= 8
 	}
 
-	if f.post != nil {
-		nn, err = f.post.writeTo(out)
-		n += nn
+	for i < -128 {
+		n++
+		i >>= 8
 	}
-	return
+
+	return n
 }
 
-func marshalBase128Int(out *forkableWriter, n int64) (err error) {
+func (i int64Encoder) Encode(dst []byte) {
+	n := i.Len()
+
+	for j := 0; j < n; j++ {
+		dst[j] = byte(i >> uint((n-1-j)*8))
+	}
+}
+
+func base128IntLength(n int64) int {
 	if n == 0 {
-		err = out.WriteByte(0)
-		return
+		return 1
 	}
 
 	l := 0
@@ -83,54 +131,33 @@ func marshalBase128Int(out *forkableWriter, n int64) (err error) {
 		l++
 	}
 
+	return l
+}
+
+func appendBase128Int(dst []byte, n int64) []byte {
+	l := base128IntLength(n)
+
 	for i := l - 1; i >= 0; i-- {
 		o := byte(n >> uint(i*7))
 		o &= 0x7f
 		if i != 0 {
 			o |= 0x80
 		}
-		err = out.WriteByte(o)
-		if err != nil {
-			return
-		}
-	}
-
-	return nil
-}
 
-func marshalInt64(out *forkableWriter, i int64) (err error) {
-	n := int64Length(i)
-
-	for ; n > 0; n-- {
-		err = out.WriteByte(byte(i >> uint((n-1)*8)))
-		if err != nil {
-			return
-		}
+		dst = append(dst, o)
 	}
 
-	return nil
+	return dst
 }
 
-func int64Length(i int64) (numBytes int) {
-	numBytes = 1
-
-	for i > 127 {
-		numBytes++
-		i >>= 8
+func makeBigInt(n *big.Int) (encoder, error) {
+	if n == nil {
+		return nil, StructuralError{"empty integer"}
 	}
 
-	for i < -128 {
-		numBytes++
-		i >>= 8
-	}
-
-	return
-}
-
-func marshalBigInt(out *forkableWriter, n *big.Int) (err error) {
 	if n.Sign() < 0 {
 		// A negative number has to be converted to two's-complement
-		// form. So we'll subtract 1 and invert. If the
+		// form. So we'll invert and subtract 1. If the
 		// most-significant-bit isn't set then we'll need to pad the
 		// beginning with 0xff in order to keep the number negative.
 		nMinus1 := new(big.Int).Neg(n)
@@ -140,41 +167,31 @@ func marshalBigInt(out *forkableWriter, n *big.Int) (err error) {
 			bytes[i] ^= 0xff
 		}
 		if len(bytes) == 0 || bytes[0]&0x80 == 0 {
-			err = out.WriteByte(0xff)
-			if err != nil {
-				return
-			}
+			return multiEncoder([]encoder{byteFFEncoder, bytesEncoder(bytes)}), nil
 		}
-		_, err = out.Write(bytes)
+		return bytesEncoder(bytes), nil
 	} else if n.Sign() == 0 {
 		// Zero is written as a single 0 zero rather than no bytes.
-		err = out.WriteByte(0x00)
+		return byte00Encoder, nil
 	} else {
 		bytes := n.Bytes()
 		if len(bytes) > 0 && bytes[0]&0x80 != 0 {
 			// We'll have to pad this with 0x00 in order to stop it
 			// looking like a negative number.
-			err = out.WriteByte(0)
-			if err != nil {
-				return
-			}
+			return multiEncoder([]encoder{byte00Encoder, bytesEncoder(bytes)}), nil
 		}
-		_, err = out.Write(bytes)
+		return bytesEncoder(bytes), nil
 	}
-	return
 }
 
-func marshalLength(out *forkableWriter, i int) (err error) {
+func appendLength(dst []byte, i int) []byte {
 	n := lengthLength(i)
 
 	for ; n > 0; n-- {
-		err = out.WriteByte(byte(i >> uint((n-1)*8)))
-		if err != nil {
-			return
-		}
+		dst = append(dst, byte(i>>uint((n-1)*8)))
 	}
 
-	return nil
+	return dst
 }
 
 func lengthLength(i int) (numBytes int) {
@@ -186,123 +203,104 @@ func lengthLength(i int) (numBytes int) {
 	return
 }
 
-func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err error) {
+func appendTagAndLength(dst []byte, t tagAndLength) []byte {
 	b := uint8(t.class) << 6
 	if t.isCompound {
 		b |= 0x20
 	}
 	if t.tag >= 31 {
 		b |= 0x1f
-		err = out.WriteByte(b)
-		if err != nil {
-			return
-		}
-		err = marshalBase128Int(out, int64(t.tag))
-		if err != nil {
-			return
-		}
+		dst = append(dst, b)
+		dst = appendBase128Int(dst, int64(t.tag))
 	} else {
 		b |= uint8(t.tag)
-		err = out.WriteByte(b)
-		if err != nil {
-			return
-		}
+		dst = append(dst, b)
 	}
 
 	if t.length >= 128 {
 		l := lengthLength(t.length)
-		err = out.WriteByte(0x80 | byte(l))
-		if err != nil {
-			return
-		}
-		err = marshalLength(out, t.length)
-		if err != nil {
-			return
-		}
+		dst = append(dst, 0x80|byte(l))
+		dst = appendLength(dst, t.length)
 	} else {
-		err = out.WriteByte(byte(t.length))
-		if err != nil {
-			return
-		}
+		dst = append(dst, byte(t.length))
 	}
 
-	return nil
+	return dst
 }
 
-func marshalBitString(out *forkableWriter, b BitString) (err error) {
-	paddingBits := byte((8 - b.BitLength%8) % 8)
-	err = out.WriteByte(paddingBits)
-	if err != nil {
-		return
-	}
-	_, err = out.Write(b.Bytes)
-	return
+type bitStringEncoder BitString
+
+func (b bitStringEncoder) Len() int {
+	return len(b.Bytes) + 1
 }
 
-func marshalObjectIdentifier(out *forkableWriter, oid []int) (err error) {
-	if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
-		return StructuralError{"invalid object identifier"}
+func (b bitStringEncoder) Encode(dst []byte) {
+	dst[0] = byte((8 - b.BitLength%8) % 8)
+	if copy(dst[1:], b.Bytes) != len(b.Bytes) {
+		panic("internal error")
 	}
+}
 
-	err = marshalBase128Int(out, int64(oid[0]*40+oid[1]))
-	if err != nil {
-		return
+type oidEncoder []int
+
+func (oid oidEncoder) Len() int {
+	l := base128IntLength(int64(oid[0]*40 + oid[1]))
+	for i := 2; i < len(oid); i++ {
+		l += base128IntLength(int64(oid[i]))
 	}
+	return l
+}
+
+func (oid oidEncoder) Encode(dst []byte) {
+	dst = appendBase128Int(dst[:0], int64(oid[0]*40+oid[1]))
 	for i := 2; i < len(oid); i++ {
-		err = marshalBase128Int(out, int64(oid[i]))
-		if err != nil {
-			return
-		}
+		dst = appendBase128Int(dst, int64(oid[i]))
 	}
+}
 
-	return
+func makeObjectIdentifier(oid []int) (e encoder, err error) {
+	if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
+		return nil, StructuralError{"invalid object identifier"}
+	}
+
+	return oidEncoder(oid), nil
 }
 
-func marshalPrintableString(out *forkableWriter, s string) (err error) {
-	b := []byte(s)
-	for _, c := range b {
-		if !isPrintable(c) {
-			return StructuralError{"PrintableString contains invalid character"}
+func makePrintableString(s string) (e encoder, err error) {
+	for i := 0; i < len(s); i++ {
+		if !isPrintable(s[i]) {
+			return nil, StructuralError{"PrintableString contains invalid character"}
 		}
 	}
 
-	_, err = out.Write(b)
-	return
+	return stringEncoder(s), nil
 }
 
-func marshalIA5String(out *forkableWriter, s string) (err error) {
-	b := []byte(s)
-	for _, c := range b {
-		if c > 127 {
-			return StructuralError{"IA5String contains invalid character"}
+func makeIA5String(s string) (e encoder, err error) {
+	for i := 0; i < len(s); i++ {
+		if s[i] > 127 {
+			return nil, StructuralError{"IA5String contains invalid character"}
 		}
 	}
 
-	_, err = out.Write(b)
-	return
+	return stringEncoder(s), nil
 }
 
-func marshalUTF8String(out *forkableWriter, s string) (err error) {
-	_, err = out.Write([]byte(s))
-	return
+func makeUTF8String(s string) encoder {
+	return stringEncoder(s)
 }
 
-func marshalTwoDigits(out *forkableWriter, v int) (err error) {
-	err = out.WriteByte(byte('0' + (v/10)%10))
-	if err != nil {
-		return
-	}
-	return out.WriteByte(byte('0' + v%10))
+func appendTwoDigits(dst []byte, v int) []byte {
+	return append(dst, byte('0'+(v/10)%10), byte('0'+v%10))
 }
 
-func marshalFourDigits(out *forkableWriter, v int) (err error) {
+func appendFourDigits(dst []byte, v int) []byte {
 	var bytes [4]byte
 	for i := range bytes {
 		bytes[3-i] = '0' + byte(v%10)
 		v /= 10
 	}
-	_, err = out.Write(bytes[:])
-	return
+	return append(dst, bytes[:]...)
 }
 
 func outsideUTCRange(t time.Time) bool {
@@ -310,80 +308,75 @@ func outsideUTCRange(t time.Time) bool {
 	return year < 1950 || year >= 2050
 }
 
-func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
+func makeUTCTime(t time.Time) (e encoder, err error) {
+	dst := make([]byte, 0, 18)
+
+	dst, err = appendUTCTime(dst, t)
+	if err != nil {
+		return nil, err
+	}
+
+	return bytesEncoder(dst), nil
+}
+
+func makeGeneralizedTime(t time.Time) (e encoder, err error) {
+	dst := make([]byte, 0, 20)
+
+	dst, err = appendGeneralizedTime(dst, t)
+	if err != nil {
+		return nil, err
+	}
+
+	return bytesEncoder(dst), nil
+}
+
+func appendUTCTime(dst []byte, t time.Time) (ret []byte, err error) {
 	year := t.Year()
 
 	switch {
 	case 1950 <= year && year < 2000:
-		err = marshalTwoDigits(out, year-1900)
+		dst = appendTwoDigits(dst, year-1900)
 	case 2000 <= year && year < 2050:
-		err = marshalTwoDigits(out, year-2000)
+		dst = appendTwoDigits(dst, year-2000)
 	default:
-		return StructuralError{"cannot represent time as UTCTime"}
-	}
-	if err != nil {
-		return
+		return nil, StructuralError{"cannot represent time as UTCTime"}
 	}
 
-	return marshalTimeCommon(out, t)
+	return appendTimeCommon(dst, t), nil
 }
 
-func marshalGeneralizedTime(out *forkableWriter, t time.Time) (err error) {
+func appendGeneralizedTime(dst []byte, t time.Time) (ret []byte, err error) {
 	year := t.Year()
 	if year < 0 || year > 9999 {
-		return StructuralError{"cannot represent time as GeneralizedTime"}
-	}
-	if err = marshalFourDigits(out, year); err != nil {
-		return
+		return nil, StructuralError{"cannot represent time as GeneralizedTime"}
 	}
 
-	return marshalTimeCommon(out, t)
+	dst = appendFourDigits(dst, year)
+
+	return appendTimeCommon(dst, t), nil
 }
 
-func marshalTimeCommon(out *forkableWriter, t time.Time) (err error) {
+func appendTimeCommon(dst []byte, t time.Time) []byte {
 	_, month, day := t.Date()
 
-	err = marshalTwoDigits(out, int(month))
-	if err != nil {
-		return
-	}
-
-	err = marshalTwoDigits(out, day)
-	if err != nil {
-		return
-	}
+	dst = appendTwoDigits(dst, int(month))
+	dst = appendTwoDigits(dst, day)
 
 	hour, min, sec := t.Clock()
 
-	err = marshalTwoDigits(out, hour)
-	if err != nil {
-		return
-	}
-
-	err = marshalTwoDigits(out, min)
-	if err != nil {
-		return
-	}
-
-	err = marshalTwoDigits(out, sec)
-	if err != nil {
-		return
-	}
+	dst = appendTwoDigits(dst, hour)
+	dst = appendTwoDigits(dst, min)
+	dst = appendTwoDigits(dst, sec)
 
 	_, offset := t.Zone()
 
 	switch {
 	case offset/60 == 0:
-		err = out.WriteByte('Z')
-		return
+		return append(dst, 'Z')
 	case offset > 0:
-		err = out.WriteByte('+')
+		dst = append(dst, '+')
 	case offset < 0:
-		err = out.WriteByte('-')
-	}
-
-	if err != nil {
-		return
+		dst = append(dst, '-')
 	}
 
 	offsetMinutes := offset / 60
@@ -391,13 +384,10 @@ func marshalTimeCommon(out *forkableWriter, t time.Time) (err error) {
 		offsetMinutes = -offsetMinutes
 	}
 
-	err = marshalTwoDigits(out, offsetMinutes/60)
-	if err != nil {
-		return
-	}
+	dst = appendTwoDigits(dst, offsetMinutes/60)
+	dst = appendTwoDigits(dst, offsetMinutes%60)
 
-	err = marshalTwoDigits(out, offsetMinutes%60)
-	return
+	return dst
 }
 
 func stripTagAndLength(in []byte) []byte {
@@ -408,114 +398,130 @@ func stripTagAndLength(in []byte) []byte {
 	return in[offset:]
 }
 
-func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) {
+func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error) {
 	switch value.Type() {
 	case flagType:
-		return nil
+		return bytesEncoder(nil), nil
 	case timeType:
 		t := value.Interface().(time.Time)
 		if params.timeType == TagGeneralizedTime || outsideUTCRange(t) {
-			return marshalGeneralizedTime(out, t)
-		} else {
-			return marshalUTCTime(out, t)
+			return makeGeneralizedTime(t)
 		}
+		return makeUTCTime(t)
 	case bitStringType:
-		return marshalBitString(out, value.Interface().(BitString))
+		return bitStringEncoder(value.Interface().(BitString)), nil
 	case objectIdentifierType:
-		return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier))
+		return makeObjectIdentifier(value.Interface().(ObjectIdentifier))
 	case bigIntType:
-		return marshalBigInt(out, value.Interface().(*big.Int))
+		return makeBigInt(value.Interface().(*big.Int))
 	}
 
 	switch v := value; v.Kind() {
 	case reflect.Bool:
 		if v.Bool() {
-			return out.WriteByte(255)
-		} else {
-			return out.WriteByte(0)
+			return byteFFEncoder, nil
 		}
+		return byte00Encoder, nil
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		return marshalInt64(out, v.Int())
+		return int64Encoder(v.Int()), nil
 	case reflect.Struct:
 		t := v.Type()
 
+		for i := 0; i < t.NumField(); i++ {
+			if t.Field(i).PkgPath != "" {
+				return nil, StructuralError{"struct contains unexported fields"}
+			}
+		}
+
 		startingField := 0
 
+		n := t.NumField()
+		if n == 0 {
+			return bytesEncoder(nil), nil
+		}
+
 		// If the first element of the structure is a non-empty
 		// RawContents, then we don't bother serializing the rest.
-		if t.NumField() > 0 && t.Field(0).Type == rawContentsType {
+		if t.Field(0).Type == rawContentsType {
 			s := v.Field(0)
 			if s.Len() > 0 {
-				bytes := make([]byte, s.Len())
-				for i := 0; i < s.Len(); i++ {
-					bytes[i] = uint8(s.Index(i).Uint())
-				}
+				bytes := s.Bytes()
 				/* The RawContents will contain the tag and
 				 * length fields but we'll also be writing
 				 * those ourselves, so we strip them out of
 				 * bytes */
-				_, err = out.Write(stripTagAndLength(bytes))
-				return
-			} else {
-				startingField = 1
+				return bytesEncoder(stripTagAndLength(bytes)), nil
 			}
+
+			startingField = 1
 		}
 
-		for i := startingField; i < t.NumField(); i++ {
-			var pre *forkableWriter
-			pre, out = out.fork()
-			err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1")))
-			if err != nil {
-				return
+		switch n1 := n - startingField; n1 {
+		case 0:
+			return bytesEncoder(nil), nil
+		case 1:
+			return makeField(v.Field(startingField), parseFieldParameters(t.Field(startingField).Tag.Get("asn1")))
+		default:
+			m := make([]encoder, n1)
+			for i := 0; i < n1; i++ {
+				m[i], err = makeField(v.Field(i+startingField), parseFieldParameters(t.Field(i+startingField).Tag.Get("asn1")))
+				if err != nil {
+					return nil, err
+				}
 			}
+
+			return multiEncoder(m), nil
 		}
-		return
 	case reflect.Slice:
 		sliceType := v.Type()
 		if sliceType.Elem().Kind() == reflect.Uint8 {
-			bytes := make([]byte, v.Len())
-			for i := 0; i < v.Len(); i++ {
-				bytes[i] = uint8(v.Index(i).Uint())
-			}
-			_, err = out.Write(bytes)
-			return
+			return bytesEncoder(v.Bytes()), nil
 		}
 
 		var fp fieldParameters
-		for i := 0; i < v.Len(); i++ {
-			var pre *forkableWriter
-			pre, out = out.fork()
-			err = marshalField(pre, v.Index(i), fp)
-			if err != nil {
-				return
+
+		switch l := v.Len(); l {
+		case 0:
+			return bytesEncoder(nil), nil
+		case 1:
+			return makeField(v.Index(0), fp)
+		default:
+			m := make([]encoder, l)
+
+			for i := 0; i < l; i++ {
+				m[i], err = makeField(v.Index(i), fp)
+				if err != nil {
+					return nil, err
+				}
 			}
+
+			return multiEncoder(m), nil
 		}
-		return
 	case reflect.String:
 		switch params.stringType {
 		case TagIA5String:
-			return marshalIA5String(out, v.String())
+			return makeIA5String(v.String())
 		case TagPrintableString:
-			return marshalPrintableString(out, v.String())
+			return makePrintableString(v.String())
 		default:
-			return marshalUTF8String(out, v.String())
+			return makeUTF8String(v.String()), nil
 		}
 	}
 
-	return StructuralError{"unknown Go type"}
+	return nil, StructuralError{"unknown Go type"}
 }
 
-func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err error) {
+func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) {
 	if !v.IsValid() {
-		return fmt.Errorf("asn1: cannot marshal nil value")
+		return nil, fmt.Errorf("asn1: cannot marshal nil value")
 	}
 	// If the field is an interface{} then recurse into it.
 	if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 {
-		return marshalField(out, v.Elem(), params)
+		return makeField(v.Elem(), params)
 	}
 
 	if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty {
-		return
+		return bytesEncoder(nil), nil
 	}
 
 	if params.optional && params.defaultValue != nil && canHaveDefaultValue(v.Kind()) {
@@ -523,7 +529,7 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
 		defaultValue.SetInt(*params.defaultValue)
 
 		if reflect.DeepEqual(v.Interface(), defaultValue.Interface()) {
-			return
+			return bytesEncoder(nil), nil
 		}
 	}
 
@@ -532,37 +538,36 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
 	// behaviour, but it's what Go has traditionally done.
 	if params.optional && params.defaultValue == nil {
 		if reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) {
-			return
+			return bytesEncoder(nil), nil
 		}
 	}
 
 	if v.Type() == rawValueType {
 		rv := v.Interface().(RawValue)
 		if len(rv.FullBytes) != 0 {
-			_, err = out.Write(rv.FullBytes)
-		} else {
-			err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
-			if err != nil {
-				return
-			}
-			_, err = out.Write(rv.Bytes)
+			return bytesEncoder(rv.FullBytes), nil
 		}
-		return
+
+		t := new(taggedEncoder)
+
+		t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound}))
+		t.body = bytesEncoder(rv.Bytes)
+
+		return t, nil
 	}
 
 	tag, isCompound, ok := getUniversalType(v.Type())
 	if !ok {
-		err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())}
-		return
+		return nil, StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())}
 	}
 	class := ClassUniversal
 
 	if params.timeType != 0 && tag != TagUTCTime {
-		return StructuralError{"explicit time type given to non-time member"}
+		return nil, StructuralError{"explicit time type given to non-time member"}
 	}
 
 	if params.stringType != 0 && tag != TagPrintableString {
-		return StructuralError{"explicit string type given to non-string member"}
+		return nil, StructuralError{"explicit string type given to non-string member"}
 	}
 
 	switch tag {
@@ -574,7 +579,7 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
 			for _, r := range v.String() {
 				if r >= utf8.RuneSelf || !isPrintable(byte(r)) {
 					if !utf8.ValidString(v.String()) {
-						return errors.New("asn1: string not valid UTF-8")
+						return nil, errors.New("asn1: string not valid UTF-8")
 					}
 					tag = TagUTF8String
 					break
@@ -591,46 +596,46 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
 
 	if params.set {
 		if tag != TagSequence {
-			return StructuralError{"non sequence tagged as set"}
+			return nil, StructuralError{"non sequence tagged as set"}
 		}
 		tag = TagSet
 	}
 
-	tags, body := out.fork()
+	t := new(taggedEncoder)
 
-	err = marshalBody(body, v, params)
+	t.body, err = makeBody(v, params)
 	if err != nil {
-		return
+		return nil, err
 	}
 
-	bodyLen := body.Len()
+	bodyLen := t.body.Len()
 
-	var explicitTag *forkableWriter
 	if params.explicit {
-		explicitTag, tags = tags.fork()
-	}
+		t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{class, tag, bodyLen, isCompound}))
 
-	if !params.explicit && params.tag != nil {
-		// implicit tag.
-		tag = *params.tag
-		class = ClassContextSpecific
-	}
+		tt := new(taggedEncoder)
 
-	err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound})
-	if err != nil {
-		return
-	}
+		tt.body = t
 
-	if params.explicit {
-		err = marshalTagAndLength(explicitTag, tagAndLength{
+		tt.tag = bytesEncoder(appendTagAndLength(tt.scratch[:0], tagAndLength{
 			class:      ClassContextSpecific,
 			tag:        *params.tag,
-			length:     bodyLen + tags.Len(),
+			length:     bodyLen + t.tag.Len(),
 			isCompound: true,
-		})
+		}))
+
+		return tt, nil
+	}
+
+	if params.tag != nil {
+		// implicit tag.
+		tag = *params.tag
+		class = ClassContextSpecific
 	}
 
-	return err
+	t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{class, tag, bodyLen, isCompound}))
+
+	return t, nil
 }
 
 // Marshal returns the ASN.1 encoding of val.
@@ -643,13 +648,11 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
 //	printable:	causes strings to be marshaled as ASN.1, PrintableString strings.
 //	utf8:		causes strings to be marshaled as ASN.1, UTF8 strings
 func Marshal(val interface{}) ([]byte, error) {
-	var out bytes.Buffer
-	v := reflect.ValueOf(val)
-	f := newForkableWriter()
-	err := marshalField(f, v, fieldParameters{})
+	e, err := makeField(reflect.ValueOf(val), fieldParameters{})
 	if err != nil {
 		return nil, err
 	}
-	_, err = f.writeTo(&out)
-	return out.Bytes(), err
+	b := make([]byte, e.Len())
+	e.Encode(b)
+	return b, nil
 }
diff --git a/src/encoding/asn1/marshal_test.go b/src/encoding/asn1/marshal_test.go
index cdca8aa..10db1aa 100644
--- a/src/encoding/asn1/marshal_test.go
+++ b/src/encoding/asn1/marshal_test.go
@@ -8,6 +8,7 @@ import (
 	"bytes"
 	"encoding/hex"
 	"math/big"
+	"strings"
 	"testing"
 	"time"
 )
@@ -167,9 +168,42 @@ func TestMarshal(t *testing.T) {
 	}
 }
 
+type marshalErrTest struct {
+	in  interface{}
+	err string
+}
+
+var marshalErrTests = []marshalErrTest{
+	{bigIntStruct{nil}, "empty integer"},
+}
+
+func TestMarshalError(t *testing.T) {
+	for i, test := range marshalErrTests {
+		_, err := Marshal(test.in)
+		if err == nil {
+			t.Errorf("#%d should fail, but success", i)
+			continue
+		}
+
+		if !strings.Contains(err.Error(), test.err) {
+			t.Errorf("#%d got: %v want %v", i, err, test.err)
+		}
+	}
+}
+
 func TestInvalidUTF8(t *testing.T) {
 	_, err := Marshal(string([]byte{0xff, 0xff}))
 	if err == nil {
 		t.Errorf("invalid UTF8 string was accepted")
 	}
 }
+
+func BenchmarkMarshal(b *testing.B) {
+	b.ReportAllocs()
+
+	for i := 0; i < b.N; i++ {
+		for _, test := range marshalTests {
+			Marshal(test.in)
+		}
+	}
+}
diff --git a/src/encoding/base64/base64.go b/src/encoding/base64/base64.go
index c2116d8..d2efad4 100644
--- a/src/encoding/base64/base64.go
+++ b/src/encoding/base64/base64.go
@@ -23,6 +23,7 @@ type Encoding struct {
 	encode    [64]byte
 	decodeMap [256]byte
 	padChar   rune
+	strict    bool
 }
 
 const (
@@ -62,6 +63,14 @@ func (enc Encoding) WithPadding(padding rune) *Encoding {
 	return &enc
 }
 
+// Strict creates a new encoding identical to enc except with
+// strict decoding enabled. In this mode, the decoder requires that
+// trailing padding bits are zero, as described in RFC 4648 section 3.5.
+func (enc Encoding) Strict() *Encoding {
+	enc.strict = true
+	return &enc
+}
+
 // StdEncoding is the standard base64 encoding, as defined in
 // RFC 4648.
 var StdEncoding = NewEncoding(encodeStd)
@@ -311,15 +320,24 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 
 		// Convert 4x 6bit source bytes into 3 bytes
 		val := uint(dbuf[0])<<18 | uint(dbuf[1])<<12 | uint(dbuf[2])<<6 | uint(dbuf[3])
+		dbuf[2], dbuf[1], dbuf[0] = byte(val>>0), byte(val>>8), byte(val>>16)
 		switch dlen {
 		case 4:
-			dst[2] = byte(val >> 0)
+			dst[2] = dbuf[2]
+			dbuf[2] = 0
 			fallthrough
 		case 3:
-			dst[1] = byte(val >> 8)
+			dst[1] = dbuf[1]
+			if enc.strict && dbuf[2] != 0 {
+				return n, end, CorruptInputError(si - 1)
+			}
+			dbuf[1] = 0
 			fallthrough
 		case 2:
-			dst[0] = byte(val >> 16)
+			dst[0] = dbuf[0]
+			if enc.strict && (dbuf[1] != 0 || dbuf[2] != 0) {
+				return n, end, CorruptInputError(si - 2)
+			}
 		}
 		dst = dst[dinc:]
 		n += dlen - 1
diff --git a/src/encoding/base64/base64_test.go b/src/encoding/base64/base64_test.go
index 19ddb92..e2e1d59 100644
--- a/src/encoding/base64/base64_test.go
+++ b/src/encoding/base64/base64_test.go
@@ -85,6 +85,11 @@ var encodingTests = []encodingTest{
 	{RawStdEncoding, rawRef},
 	{RawURLEncoding, rawUrlRef},
 	{funnyEncoding, funnyRef},
+	{StdEncoding.Strict(), stdRef},
+	{URLEncoding.Strict(), urlRef},
+	{RawStdEncoding.Strict(), rawRef},
+	{RawURLEncoding.Strict(), rawUrlRef},
+	{funnyEncoding.Strict(), funnyRef},
 }
 
 var bigtest = testpair{
@@ -436,6 +441,22 @@ func TestDecoderIssue7733(t *testing.T) {
 	}
 }
 
+func TestDecoderIssue15656(t *testing.T) {
+	_, err := StdEncoding.Strict().DecodeString("WvLTlMrX9NpYDQlEIFlnDB==")
+	want := CorruptInputError(22)
+	if !reflect.DeepEqual(want, err) {
+		t.Errorf("Error = %v; want CorruptInputError(22)", err)
+	}
+	_, err = StdEncoding.Strict().DecodeString("WvLTlMrX9NpYDQlEIFlnDA==")
+	if err != nil {
+		t.Errorf("Error = %v; want nil", err)
+	}
+	_, err = StdEncoding.DecodeString("WvLTlMrX9NpYDQlEIFlnDB==")
+	if err != nil {
+		t.Errorf("Error = %v; want nil", err)
+	}
+}
+
 func BenchmarkEncodeToString(b *testing.B) {
 	data := make([]byte, 8192)
 	b.SetBytes(int64(len(data)))
diff --git a/src/encoding/binary/binary.go b/src/encoding/binary/binary.go
index 46c6add..3834254 100644
--- a/src/encoding/binary/binary.go
+++ b/src/encoding/binary/binary.go
@@ -7,7 +7,7 @@
 //
 // Numbers are translated by reading and writing fixed-size values.
 // A fixed-size value is either a fixed-size arithmetic
-// type (int8, uint8, int16, float32, complex64, ...)
+// type (bool, int8, uint8, int16, float32, complex64, ...)
 // or an array or struct containing only fixed-size values.
 //
 // The varint functions encode and decode single integer values using
@@ -147,6 +147,8 @@ func (bigEndian) GoString() string { return "binary.BigEndian" }
 // of fixed-size values.
 // Bytes read from r are decoded using the specified byte order
 // and written to successive fields of the data.
+// When decoding boolean values, a zero byte is decoded as false, and
+// any other non-zero byte is decoded as true.
 // When reading into structs, the field data for fields with
 // blank (_) field names is skipped; i.e., blank field names
 // may be used for padding.
@@ -169,6 +171,8 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
 			return err
 		}
 		switch data := data.(type) {
+		case *bool:
+			*data = b[0] != 0
 		case *int8:
 			*data = int8(b[0])
 		case *uint8:
@@ -185,8 +189,12 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
 			*data = int64(order.Uint64(bs))
 		case *uint64:
 			*data = order.Uint64(bs)
-		case []int8:
+		case []bool:
 			for i, x := range bs { // Easier to loop over the input for 8-bit values.
+				data[i] = x != 0
+			}
+		case []int8:
+			for i, x := range bs {
 				data[i] = int8(x)
 			}
 		case []uint8:
@@ -243,6 +251,7 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
 // Write writes the binary representation of data into w.
 // Data must be a fixed-size value or a slice of fixed-size
 // values, or a pointer to such data.
+// Boolean values encode as one byte: 1 for true, and 0 for false.
 // Bytes written to w are encoded using the specified byte order
 // and read from successive fields of the data.
 // When writing structs, zero values are written for fields
@@ -258,6 +267,26 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error {
 			bs = b[:n]
 		}
 		switch v := data.(type) {
+		case *bool:
+			if *v {
+				b[0] = 1
+			} else {
+				b[0] = 0
+			}
+		case bool:
+			if v {
+				b[0] = 1
+			} else {
+				b[0] = 0
+			}
+		case []bool:
+			for i, x := range v {
+				if x {
+					bs[i] = 1
+				} else {
+					bs[i] = 0
+				}
+			}
 		case *int8:
 			b[0] = byte(*v)
 		case int8:
@@ -378,7 +407,8 @@ func sizeof(t reflect.Type) int {
 		}
 		return sum
 
-	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
+	case reflect.Bool,
+		reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
 		reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
 		reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
 		return int(t.Size())
@@ -395,6 +425,21 @@ type coder struct {
 type decoder coder
 type encoder coder
 
+func (d *decoder) bool() bool {
+	x := d.buf[0]
+	d.buf = d.buf[1:]
+	return x != 0
+}
+
+func (e *encoder) bool(x bool) {
+	if x {
+		e.buf[0] = 1
+	} else {
+		e.buf[0] = 0
+	}
+	e.buf = e.buf[1:]
+}
+
 func (d *decoder) uint8() uint8 {
 	x := d.buf[0]
 	d.buf = d.buf[1:]
@@ -485,6 +530,9 @@ func (d *decoder) value(v reflect.Value) {
 			d.value(v.Index(i))
 		}
 
+	case reflect.Bool:
+		v.SetBool(d.bool())
+
 	case reflect.Int8:
 		v.SetInt(int64(d.int8()))
 	case reflect.Int16:
@@ -547,6 +595,9 @@ func (e *encoder) value(v reflect.Value) {
 			e.value(v.Index(i))
 		}
 
+	case reflect.Bool:
+		e.bool(v.Bool())
+
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 		switch v.Type().Kind() {
 		case reflect.Int8:
@@ -609,7 +660,7 @@ func (e *encoder) skip(v reflect.Value) {
 // It returns zero if the type cannot be implemented by the fast path in Read or Write.
 func intDataSize(data interface{}) int {
 	switch data := data.(type) {
-	case int8, uint8, *int8, *uint8:
+	case bool, int8, uint8, *bool, *int8, *uint8:
 		return 1
 	case []int8:
 		return len(data)
diff --git a/src/encoding/binary/binary_test.go b/src/encoding/binary/binary_test.go
index c0728e9..fc7f276 100644
--- a/src/encoding/binary/binary_test.go
+++ b/src/encoding/binary/binary_test.go
@@ -27,6 +27,8 @@ type Struct struct {
 	Complex64  complex64
 	Complex128 complex128
 	Array      [4]uint8
+	Bool       bool
+	BoolArray  [4]bool
 }
 
 type T struct {
@@ -58,6 +60,9 @@ var s = Struct{
 	),
 
 	[4]uint8{0x43, 0x44, 0x45, 0x46},
+
+	true,
+	[4]bool{true, false, true, false},
 }
 
 var big = []byte{
@@ -76,6 +81,9 @@ var big = []byte{
 	51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
 
 	67, 68, 69, 70,
+
+	1,
+	1, 0, 1, 0,
 }
 
 var little = []byte{
@@ -94,6 +102,9 @@ var little = []byte{
 	58, 57, 56, 55, 54, 53, 52, 51, 66, 65, 64, 63, 62, 61, 60, 59,
 
 	67, 68, 69, 70,
+
+	1,
+	1, 0, 1, 0,
 }
 
 var src = []byte{1, 2, 3, 4, 5, 6, 7, 8}
@@ -141,6 +152,25 @@ func TestWriteSlice(t *testing.T) {
 	checkResult(t, "WriteSlice", BigEndian, err, buf.Bytes(), src)
 }
 
+func TestReadBool(t *testing.T) {
+	var res bool
+	var err error
+	err = Read(bytes.NewReader([]byte{0}), BigEndian, &res)
+	checkResult(t, "ReadBool", BigEndian, err, res, false)
+	res = false
+	err = Read(bytes.NewReader([]byte{1}), BigEndian, &res)
+	checkResult(t, "ReadBool", BigEndian, err, res, true)
+	res = false
+	err = Read(bytes.NewReader([]byte{2}), BigEndian, &res)
+	checkResult(t, "ReadBool", BigEndian, err, res, true)
+}
+
+func TestReadBoolSlice(t *testing.T) {
+	slice := make([]bool, 4)
+	err := Read(bytes.NewReader([]byte{0, 1, 2, 255}), BigEndian, slice)
+	checkResult(t, "ReadBoolSlice", BigEndian, err, slice, []bool{false, true, true, true})
+}
+
 // Addresses of arrays are easier to manipulate with reflection than are slices.
 var intArrays = []interface{}{
 	&[100]int8{},
@@ -422,16 +452,15 @@ func BenchmarkReadInts(b *testing.B) {
 		Read(r, BigEndian, &ls.Uint32)
 		Read(r, BigEndian, &ls.Uint64)
 	}
-
+	b.StopTimer()
 	want := s
 	want.Float32 = 0
 	want.Float64 = 0
 	want.Complex64 = 0
 	want.Complex128 = 0
-	for i := range want.Array {
-		want.Array[i] = 0
-	}
-	b.StopTimer()
+	want.Array = [4]uint8{0, 0, 0, 0}
+	want.Bool = false
+	want.BoolArray = [4]bool{false, false, false, false}
 	if b.N > 0 && !reflect.DeepEqual(ls, want) {
 		b.Fatalf("struct doesn't match:\ngot  %v;\nwant %v", ls, want)
 	}
diff --git a/src/encoding/csv/reader.go b/src/encoding/csv/reader.go
index a5e03a9..c8c4ca7 100644
--- a/src/encoding/csv/reader.go
+++ b/src/encoding/csv/reader.go
@@ -114,7 +114,14 @@ type Reader struct {
 	line   int
 	column int
 	r      *bufio.Reader
-	field  bytes.Buffer
+	// lineBuffer holds the unescaped fields read by readField, one after another.
+	// The fields can be accessed by using the indexes in fieldIndexes.
+	// Example: for the row `a,"b","c""d",e` lineBuffer will contain `abc"de` and
+	// fieldIndexes will contain the indexes 0, 1, 2, 5.
+	lineBuffer bytes.Buffer
+	// Indexes of fields inside lineBuffer
+	// The i'th field starts at offset fieldIndexes[i] in lineBuffer.
+	fieldIndexes []int
 }
 
 // NewReader returns a new Reader that reads from r.
@@ -134,8 +141,12 @@ func (r *Reader) error(err error) error {
 	}
 }
 
-// Read reads one record from r. The record is a slice of strings with each
-// string representing one field.
+// Read reads one record (a slice of fields) from r.
+// If the record has an unexpected number of fields,
+// Read returns the record along with the error ErrFieldCount.
+// Except for that case, Read always returns either a non-nil
+// record or a non-nil error, but not both.
+// If there is no data left to be read, Read returns nil, io.EOF.
 func (r *Reader) Read() (record []string, err error) {
 	for {
 		record, err = r.parseRecord()
@@ -233,31 +244,54 @@ func (r *Reader) parseRecord() (fields []string, err error) {
 	}
 	r.r.UnreadRune()
 
+	r.lineBuffer.Reset()
+	r.fieldIndexes = r.fieldIndexes[:0]
+
 	// At this point we have at least one field.
 	for {
+		idx := r.lineBuffer.Len()
+
 		haveField, delim, err := r.parseField()
 		if haveField {
-			// If FieldsPerRecord is greater than 0 we can assume the final
-			// length of fields to be equal to FieldsPerRecord.
-			if r.FieldsPerRecord > 0 && fields == nil {
-				fields = make([]string, 0, r.FieldsPerRecord)
-			}
-			fields = append(fields, r.field.String())
+			r.fieldIndexes = append(r.fieldIndexes, idx)
 		}
+
 		if delim == '\n' || err == io.EOF {
-			return fields, err
-		} else if err != nil {
+			if len(r.fieldIndexes) == 0 {
+				return nil, err
+			}
+			break
+		}
+
+		if err != nil {
 			return nil, err
 		}
 	}
+
+	fieldCount := len(r.fieldIndexes)
+	// Using this approach (creating a single string and taking slices of it)
+	// means that a single reference to any of the fields will retain the whole
+	// string. The risk of a nontrivial space leak caused by this is considered
+	// minimal and a tradeoff for better performance through the combined
+	// allocations.
+	line := r.lineBuffer.String()
+	fields = make([]string, fieldCount)
+
+	for i, idx := range r.fieldIndexes {
+		if i == fieldCount-1 {
+			fields[i] = line[idx:]
+		} else {
+			fields[i] = line[idx:r.fieldIndexes[i+1]]
+		}
+	}
+
+	return fields, nil
 }
 
 // parseField parses the next field in the record. The read field is
-// located in r.field. Delim is the first character not part of the field
+// appended to r.lineBuffer. Delim is the first character not part of the field
 // (r.Comma or '\n').
 func (r *Reader) parseField() (haveField bool, delim rune, err error) {
-	r.field.Reset()
-
 	r1, err := r.readRune()
 	for err == nil && r.TrimLeadingSpace && r1 != '\n' && unicode.IsSpace(r1) {
 		r1, err = r.readRune()
@@ -310,19 +344,19 @@ func (r *Reader) parseField() (haveField bool, delim rune, err error) {
 						return false, 0, r.error(ErrQuote)
 					}
 					// accept the bare quote
-					r.field.WriteRune('"')
+					r.lineBuffer.WriteRune('"')
 				}
 			case '\n':
 				r.line++
 				r.column = -1
 			}
-			r.field.WriteRune(r1)
+			r.lineBuffer.WriteRune(r1)
 		}
 
 	default:
 		// unquoted field
 		for {
-			r.field.WriteRune(r1)
+			r.lineBuffer.WriteRune(r1)
 			r1, err = r.readRune()
 			if err != nil || r1 == r.Comma {
 				break
diff --git a/src/encoding/csv/reader_test.go b/src/encoding/csv/reader_test.go
index be1002d..7b3aca4 100644
--- a/src/encoding/csv/reader_test.go
+++ b/src/encoding/csv/reader_test.go
@@ -5,6 +5,7 @@
 package csv
 
 import (
+	"io"
 	"reflect"
 	"strings"
 	"testing"
@@ -292,8 +293,52 @@ func TestRead(t *testing.T) {
 	}
 }
 
-func BenchmarkRead(b *testing.B) {
-	data := `x,y,z,w
+// nTimes is an io.Reader which yields the string s n times.
+type nTimes struct {
+	s   string
+	n   int
+	off int
+}
+
+func (r *nTimes) Read(p []byte) (n int, err error) {
+	for {
+		if r.n <= 0 || r.s == "" {
+			return n, io.EOF
+		}
+		n0 := copy(p, r.s[r.off:])
+		p = p[n0:]
+		n += n0
+		r.off += n0
+		if r.off == len(r.s) {
+			r.off = 0
+			r.n--
+		}
+		if len(p) == 0 {
+			return
+		}
+	}
+}
+
+// benchmarkRead measures reading the provided CSV rows data.
+// initReader, if non-nil, modifies the Reader before it's used.
+func benchmarkRead(b *testing.B, initReader func(*Reader), rows string) {
+	b.ReportAllocs()
+	r := NewReader(&nTimes{s: rows, n: b.N})
+	if initReader != nil {
+		initReader(r)
+	}
+	for {
+		_, err := r.Read()
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
+const benchmarkCSVData = `x,y,z,w
 x,y,z,
 x,y,,
 x,,,
@@ -305,11 +350,22 @@ x,,,
 "","","",""
 `
 
-	for i := 0; i < b.N; i++ {
-		_, err := NewReader(strings.NewReader(data)).ReadAll()
+func BenchmarkRead(b *testing.B) {
+	benchmarkRead(b, nil, benchmarkCSVData)
+}
 
-		if err != nil {
-			b.Fatalf("could not read data: %s", err)
-		}
-	}
+func BenchmarkReadWithFieldsPerRecord(b *testing.B) {
+	benchmarkRead(b, func(r *Reader) { r.FieldsPerRecord = 4 }, benchmarkCSVData)
+}
+
+func BenchmarkReadWithoutFieldsPerRecord(b *testing.B) {
+	benchmarkRead(b, func(r *Reader) { r.FieldsPerRecord = -1 }, benchmarkCSVData)
+}
+
+func BenchmarkReadLargeFields(b *testing.B) {
+	benchmarkRead(b, nil, strings.Repeat(`xxxxxxxxxxxxxxxx,yyyyyyyyyyyyyyyy,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+xxxxxxxxxxxxxxxxxxxxxxxx,yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,vvvv
+,,zzzz,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy,zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+`, 3))
 }
diff --git a/src/encoding/gob/encoder.go b/src/encoding/gob/encoder.go
index d6c8fdd..40ec81b 100644
--- a/src/encoding/gob/encoder.go
+++ b/src/encoding/gob/encoder.go
@@ -215,6 +215,9 @@ func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) {
 // guaranteeing that all necessary type information has been transmitted first.
 // Passing a nil pointer to EncodeValue will panic, as they cannot be transmitted by gob.
 func (enc *Encoder) EncodeValue(value reflect.Value) error {
+	if value.Kind() == reflect.Invalid {
+		return errors.New("gob: cannot encode nil value")
+	}
 	if value.Kind() == reflect.Ptr && value.IsNil() {
 		panic("gob: cannot encode nil pointer of type " + value.Type().String())
 	}
diff --git a/src/encoding/gob/encoder_test.go b/src/encoding/gob/encoder_test.go
index 22090a1..9256848 100644
--- a/src/encoding/gob/encoder_test.go
+++ b/src/encoding/gob/encoder_test.go
@@ -830,6 +830,20 @@ func TestPtrToMapOfMap(t *testing.T) {
 	}
 }
 
+// Test that untyped nils generate an error, not a panic.
+// See Issue 16204.
+func TestCatchInvalidNilValue(t *testing.T) {
+	encodeErr, panicErr := encodeAndRecover(nil)
+	if panicErr != nil {
+		t.Fatalf("panicErr=%v, should not panic encoding untyped nil", panicErr)
+	}
+	if encodeErr == nil {
+		t.Errorf("got err=nil, want non-nil error when encoding untyped nil value")
+	} else if !strings.Contains(encodeErr.Error(), "nil value") {
+		t.Errorf("expected 'nil value' error; got err=%v", encodeErr)
+	}
+}
+
 // A top-level nil pointer generates a panic with a helpful string-valued message.
 func TestTopLevelNilPointer(t *testing.T) {
 	var ip *int
diff --git a/src/encoding/hex/example_test.go b/src/encoding/hex/example_test.go
new file mode 100644
index 0000000..3580757
--- /dev/null
+++ b/src/encoding/hex/example_test.go
@@ -0,0 +1,98 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package hex_test
+
+import (
+	"encoding/hex"
+	"fmt"
+	"log"
+	"os"
+)
+
+func ExampleEncode() {
+	src := []byte("Hello Gopher!")
+
+	dst := make([]byte, hex.EncodedLen(len(src)))
+	hex.Encode(dst, src)
+
+	fmt.Printf("%s\n", dst)
+
+	// Output:
+	// 48656c6c6f20476f7068657221
+}
+
+func ExampleDecode() {
+	src := []byte("48656c6c6f20476f7068657221")
+
+	dst := make([]byte, hex.DecodedLen(len(src)))
+	n, err := hex.Decode(dst, src)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("%s\n", dst[:n])
+
+	// Output:
+	// Hello Gopher!
+}
+
+func ExampleDecodeString() {
+	const s = "48656c6c6f20476f7068657221"
+	decoded, err := hex.DecodeString(s)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("%s\n", decoded)
+
+	// Output:
+	// Hello Gopher!
+}
+
+func ExampleDump() {
+	content := []byte("Go is an open source programming language.")
+
+	fmt.Printf("%s", hex.Dump(content))
+
+	// Output:
+	// 00000000  47 6f 20 69 73 20 61 6e  20 6f 70 65 6e 20 73 6f  |Go is an open so|
+	// 00000010  75 72 63 65 20 70 72 6f  67 72 61 6d 6d 69 6e 67  |urce programming|
+	// 00000020  20 6c 61 6e 67 75 61 67  65 2e                    | language.|
+}
+
+func ExampleDumper() {
+	lines := []string{
+		"Go is an open source programming language.",
+		"\n",
+		"We encourage all Go users to subscribe to golang-announce.",
+	}
+
+	stdoutDumper := hex.Dumper(os.Stdout)
+
+	defer stdoutDumper.Close()
+
+	for _, line := range lines {
+		stdoutDumper.Write([]byte(line))
+	}
+
+	// Output:
+	// 00000000  47 6f 20 69 73 20 61 6e  20 6f 70 65 6e 20 73 6f  |Go is an open so|
+	// 00000010  75 72 63 65 20 70 72 6f  67 72 61 6d 6d 69 6e 67  |urce programming|
+	// 00000020  20 6c 61 6e 67 75 61 67  65 2e 0a 57 65 20 65 6e  | language..We en|
+	// 00000030  63 6f 75 72 61 67 65 20  61 6c 6c 20 47 6f 20 75  |courage all Go u|
+	// 00000040  73 65 72 73 20 74 6f 20  73 75 62 73 63 72 69 62  |sers to subscrib|
+	// 00000050  65 20 74 6f 20 67 6f 6c  61 6e 67 2d 61 6e 6e 6f  |e to golang-anno|
+	// 00000060  75 6e 63 65 2e                                    |unce.|
+}
+
+func ExampleEncodeToString() {
+	src := []byte("Hello")
+	encodedStr := hex.EncodeToString(src)
+
+	fmt.Printf("%s\n", encodedStr)
+
+	// Output:
+	// 48656c6c6f
+}
diff --git a/src/encoding/hex/hex.go b/src/encoding/hex/hex.go
index 73a2503..b43c1c4 100644
--- a/src/encoding/hex/hex.go
+++ b/src/encoding/hex/hex.go
@@ -12,9 +12,13 @@ import (
 	"io"
 )
 
-const hextable = "0123456789abcdef"
+var hextable = [16]byte{
+	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+	'a', 'b', 'c', 'd', 'e', 'f',
+}
 
 // EncodedLen returns the length of an encoding of n source bytes.
+// Specifically, it returns n * 2.
 func EncodedLen(n int) int { return n * 2 }
 
 // Encode encodes src into EncodedLen(len(src))
@@ -40,12 +44,15 @@ func (e InvalidByteError) Error() string {
 	return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e))
 }
 
+// DecodedLen returns the length of a decoding of x source bytes.
+// Specifically, it returns x / 2.
 func DecodedLen(x int) int { return x / 2 }
 
-// Decode decodes src into DecodedLen(len(src)) bytes, returning the actual
-// number of bytes written to dst.
+// Decode decodes src into DecodedLen(len(src)) bytes,
+// returning the actual number of bytes written to dst.
 //
-// If Decode encounters invalid input, it returns an error describing the failure.
+// Decode expects that src contain only hexadecimal
+// characters and that src should have an even length.
 func Decode(dst, src []byte) (int, error) {
 	if len(src)%2 == 1 {
 		return 0, ErrLength
diff --git a/src/encoding/hex/hex_test.go b/src/encoding/hex/hex_test.go
index b969636..64dabbd 100644
--- a/src/encoding/hex/hex_test.go
+++ b/src/encoding/hex/hex_test.go
@@ -6,6 +6,7 @@ package hex
 
 import (
 	"bytes"
+	"fmt"
 	"testing"
 )
 
@@ -151,3 +152,18 @@ var expectedHexDump = []byte(`00000000  1e 1f 20 21 22 23 24 25  26 27 28 29 2a
 00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
 00000020  3e 3f 40 41 42 43 44 45                           |>?@ABCDE|
 `)
+
+var sink []byte
+
+func BenchmarkEncode(b *testing.B) {
+	for _, size := range []int{256, 1024, 4096, 16384} {
+		src := bytes.Repeat([]byte{2, 3, 5, 7, 9, 11, 13, 17}, size/8)
+		sink = make([]byte, 2*size)
+
+		b.Run(fmt.Sprintf("%v", size), func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				Encode(sink, src)
+			}
+		})
+	}
+}
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index 2eda875..77fc460 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -34,6 +34,13 @@ import (
 // the value pointed at by the pointer. If the pointer is nil, Unmarshal
 // allocates a new value for it to point to.
 //
+// To unmarshal JSON into a value implementing the Unmarshaler interface,
+// Unmarshal calls that value's UnmarshalJSON method, including
+// when the input is a JSON null.
+// Otherwise, if the value implements encoding.TextUnmarshaler
+// and the input is a JSON quoted string, Unmarshal calls that value's
+// UnmarshalText method with the unquoted form of the string.
+//
 // To unmarshal JSON into a struct, Unmarshal matches incoming object
 // keys to the keys used by Marshal (either the struct field name or its tag),
 // preferring an exact match but also accepting a case-insensitive match.
@@ -63,8 +70,8 @@ import (
 //
 // To unmarshal a JSON object into a map, Unmarshal first establishes a map to
 // use. If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal
-// reuses the existing map, keeping existing entries. Unmarshal then stores key-
-// value pairs from the JSON object into the map. The map's key type must
+// reuses the existing map, keeping existing entries. Unmarshal then stores
+// key-value pairs from the JSON object into the map. The map's key type must
 // either be a string, an integer, or implement encoding.TextUnmarshaler.
 //
 // If a JSON value is not appropriate for a given target type,
@@ -102,6 +109,9 @@ func Unmarshal(data []byte, v interface{}) error {
 // The input can be assumed to be a valid encoding of
 // a JSON value. UnmarshalJSON must copy the JSON data
 // if it wishes to retain the data after returning.
+//
+// By convention, to approximate the behavior of Unmarshal itself,
+// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
 type Unmarshaler interface {
 	UnmarshalJSON([]byte) error
 }
@@ -112,9 +122,14 @@ type UnmarshalTypeError struct {
 	Value  string       // description of JSON value - "bool", "array", "number -5"
 	Type   reflect.Type // type of Go value it could not be assigned to
 	Offset int64        // error occurred after reading Offset bytes
+	Struct string       // name of the struct type containing the field
+	Field  string       // name of the field holding the Go value
 }
 
 func (e *UnmarshalTypeError) Error() string {
+	if e.Struct != "" || e.Field != "" {
+		return "json: cannot unmarshal " + e.Value + " into Go struct field " + e.Struct + "." + e.Field + " of type " + e.Type.String()
+	}
 	return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
 }
 
@@ -248,10 +263,14 @@ func isValidNumber(s string) bool {
 
 // decodeState represents the state while decoding a JSON value.
 type decodeState struct {
-	data       []byte
-	off        int // read offset in data
-	scan       scanner
-	nextscan   scanner // for calls to nextValue
+	data         []byte
+	off          int // read offset in data
+	scan         scanner
+	nextscan     scanner  // for calls to nextValue
+	errorContext struct { // provides context for type errors
+		Struct string
+		Field  string
+	}
 	savedError error
 	useNumber  bool
 }
@@ -265,22 +284,37 @@ func (d *decodeState) init(data []byte) *decodeState {
 	d.data = data
 	d.off = 0
 	d.savedError = nil
+	d.errorContext.Struct = ""
+	d.errorContext.Field = ""
 	return d
 }
 
 // error aborts the decoding by panicking with err.
 func (d *decodeState) error(err error) {
-	panic(err)
+	panic(d.addErrorContext(err))
 }
 
 // saveError saves the first err it is called with,
 // for reporting at the end of the unmarshal.
 func (d *decodeState) saveError(err error) {
 	if d.savedError == nil {
-		d.savedError = err
+		d.savedError = d.addErrorContext(err)
 	}
 }
 
+// addErrorContext returns a new error enhanced with information from d.errorContext
+func (d *decodeState) addErrorContext(err error) error {
+	if d.errorContext.Struct != "" || d.errorContext.Field != "" {
+		switch err := err.(type) {
+		case *UnmarshalTypeError:
+			err.Struct = d.errorContext.Struct
+			err.Field = d.errorContext.Field
+			return err
+		}
+	}
+	return err
+}
+
 // next cuts off and returns the next full JSON value in d.data[d.off:].
 // The next value is known to be an object or array, not a literal.
 func (d *decodeState) next() []byte {
@@ -434,8 +468,10 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
 			if u, ok := v.Interface().(Unmarshaler); ok {
 				return u, nil, reflect.Value{}
 			}
-			if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
-				return nil, u, reflect.Value{}
+			if !decodingNull {
+				if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
+					return nil, u, reflect.Value{}
+				}
 			}
 		}
 		v = v.Elem()
@@ -457,7 +493,7 @@ func (d *decodeState) array(v reflect.Value) {
 		return
 	}
 	if ut != nil {
-		d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)})
+		d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
 		d.off--
 		d.next()
 		return
@@ -476,7 +512,7 @@ func (d *decodeState) array(v reflect.Value) {
 		// Otherwise it's invalid.
 		fallthrough
 	default:
-		d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)})
+		d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
 		d.off--
 		d.next()
 		return
@@ -566,7 +602,7 @@ func (d *decodeState) object(v reflect.Value) {
 		return
 	}
 	if ut != nil {
-		d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
+		d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
 		d.off--
 		d.next() // skip over { } in input
 		return
@@ -594,7 +630,7 @@ func (d *decodeState) object(v reflect.Value) {
 			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 		default:
 			if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) {
-				d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
+				d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
 				d.off--
 				d.next() // skip over { } in input
 				return
@@ -604,9 +640,9 @@ func (d *decodeState) object(v reflect.Value) {
 			v.Set(reflect.MakeMap(t))
 		}
 	case reflect.Struct:
-
+		// ok
 	default:
-		d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
+		d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
 		d.off--
 		d.next() // skip over { } in input
 		return
@@ -671,6 +707,8 @@ func (d *decodeState) object(v reflect.Value) {
 					}
 					subv = subv.Field(i)
 				}
+				d.errorContext.Field = f.name
+				d.errorContext.Struct = v.Type().Name()
 			}
 		}
 
@@ -682,7 +720,6 @@ func (d *decodeState) object(v reflect.Value) {
 			d.error(errPhase)
 		}
 
-		// Read value.
 		if destring {
 			switch qv := d.valueQuoted().(type) {
 			case nil:
@@ -714,7 +751,7 @@ func (d *decodeState) object(v reflect.Value) {
 					s := string(key)
 					n, err := strconv.ParseInt(s, 10, 64)
 					if err != nil || reflect.Zero(kt).OverflowInt(n) {
-						d.saveError(&UnmarshalTypeError{"number " + s, kt, int64(start + 1)})
+						d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)})
 						return
 					}
 					kv = reflect.ValueOf(n).Convert(kt)
@@ -722,7 +759,7 @@ func (d *decodeState) object(v reflect.Value) {
 					s := string(key)
 					n, err := strconv.ParseUint(s, 10, 64)
 					if err != nil || reflect.Zero(kt).OverflowUint(n) {
-						d.saveError(&UnmarshalTypeError{"number " + s, kt, int64(start + 1)})
+						d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)})
 						return
 					}
 					kv = reflect.ValueOf(n).Convert(kt)
@@ -741,6 +778,9 @@ func (d *decodeState) object(v reflect.Value) {
 		if op != scanObjectValue {
 			d.error(errPhase)
 		}
+
+		d.errorContext.Struct = ""
+		d.errorContext.Field = ""
 	}
 }
 
@@ -767,7 +807,7 @@ func (d *decodeState) convertNumber(s string) (interface{}, error) {
 	}
 	f, err := strconv.ParseFloat(s, 64)
 	if err != nil {
-		return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)}
+		return nil, &UnmarshalTypeError{Value: "number " + s, Type: reflect.TypeOf(0.0), Offset: int64(d.off)}
 	}
 	return f, nil
 }
@@ -786,8 +826,8 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 		d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
 		return
 	}
-	wantptr := item[0] == 'n' // null
-	u, ut, pv := d.indirect(v, wantptr)
+	isNull := item[0] == 'n' // null
+	u, ut, pv := d.indirect(v, isNull)
 	if u != nil {
 		err := u.UnmarshalJSON(item)
 		if err != nil {
@@ -800,7 +840,16 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 			if fromQuoted {
 				d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
 			} else {
-				d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
+				var val string
+				switch item[0] {
+				case 'n':
+					val = "null"
+				case 't', 'f':
+					val = "bool"
+				default:
+					val = "number"
+				}
+				d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.off)})
 			}
 			return
 		}
@@ -823,19 +872,31 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 
 	switch c := item[0]; c {
 	case 'n': // null
+		// The main parser checks that only true and false can reach here,
+		// but if this was a quoted string input, it could be anything.
+		if fromQuoted && string(item) != "null" {
+			d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+			break
+		}
 		switch v.Kind() {
 		case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
 			v.Set(reflect.Zero(v.Type()))
 			// otherwise, ignore null for primitives/string
 		}
 	case 't', 'f': // true, false
-		value := c == 't'
+		value := item[0] == 't'
+		// The main parser checks that only true and false can reach here,
+		// but if this was a quoted string input, it could be anything.
+		if fromQuoted && string(item) != "true" && string(item) != "false" {
+			d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+			break
+		}
 		switch v.Kind() {
 		default:
 			if fromQuoted {
 				d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
 			} else {
-				d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)})
+				d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off)})
 			}
 		case reflect.Bool:
 			v.SetBool(value)
@@ -843,7 +904,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 			if v.NumMethod() == 0 {
 				v.Set(reflect.ValueOf(value))
 			} else {
-				d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)})
+				d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off)})
 			}
 		}
 
@@ -858,10 +919,10 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 		}
 		switch v.Kind() {
 		default:
-			d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
+			d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)})
 		case reflect.Slice:
 			if v.Type().Elem().Kind() != reflect.Uint8 {
-				d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
+				d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)})
 				break
 			}
 			b := make([]byte, base64.StdEncoding.DecodedLen(len(s)))
@@ -877,7 +938,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 			if v.NumMethod() == 0 {
 				v.Set(reflect.ValueOf(string(s)))
 			} else {
-				d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
+				d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)})
 			}
 		}
 
@@ -902,7 +963,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 			if fromQuoted {
 				d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
 			} else {
-				d.error(&UnmarshalTypeError{"number", v.Type(), int64(d.off)})
+				d.error(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off)})
 			}
 		case reflect.Interface:
 			n, err := d.convertNumber(s)
@@ -911,7 +972,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 				break
 			}
 			if v.NumMethod() != 0 {
-				d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)})
+				d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off)})
 				break
 			}
 			v.Set(reflect.ValueOf(n))
@@ -919,7 +980,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 			n, err := strconv.ParseInt(s, 10, 64)
 			if err != nil || v.OverflowInt(n) {
-				d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
+				d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)})
 				break
 			}
 			v.SetInt(n)
@@ -927,7 +988,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 			n, err := strconv.ParseUint(s, 10, 64)
 			if err != nil || v.OverflowUint(n) {
-				d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
+				d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)})
 				break
 			}
 			v.SetUint(n)
@@ -935,7 +996,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 		case reflect.Float32, reflect.Float64:
 			n, err := strconv.ParseFloat(s, v.Type().Bits())
 			if err != nil || v.OverflowFloat(n) {
-				d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
+				d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)})
 				break
 			}
 			v.SetFloat(n)
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index 255ff5c..bd38ddd 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -11,6 +11,7 @@ import (
 	"fmt"
 	"image"
 	"math"
+	"math/big"
 	"net"
 	"reflect"
 	"strconv"
@@ -33,6 +34,11 @@ type V struct {
 	F1 interface{}
 	F2 int32
 	F3 Number
+	F4 *VOuter
+}
+
+type VOuter struct {
+	V V
 }
 
 // ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and
@@ -374,6 +380,10 @@ type unmarshalTest struct {
 	golden    bool
 }
 
+type B struct {
+	B bool `json:",string"`
+}
+
 var unmarshalTests = []unmarshalTest{
 	// basic types
 	{in: `true`, ptr: new(bool), out: true},
@@ -389,7 +399,7 @@ var unmarshalTests = []unmarshalTest{
 	{in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
 	{in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
 	{in: "null", ptr: new(interface{}), out: nil},
-	{in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7}},
+	{in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7, "T", "X"}},
 	{in: `{"x": 1}`, ptr: new(tx), out: tx{}},
 	{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
 	{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
@@ -504,22 +514,22 @@ var unmarshalTests = []unmarshalTest{
 	{
 		in:  `{"abc":"abc"}`,
 		ptr: new(map[int]string),
-		err: &UnmarshalTypeError{"number abc", reflect.TypeOf(0), 2},
+		err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeOf(0), Offset: 2},
 	},
 	{
 		in:  `{"256":"abc"}`,
 		ptr: new(map[uint8]string),
-		err: &UnmarshalTypeError{"number 256", reflect.TypeOf(uint8(0)), 2},
+		err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeOf(uint8(0)), Offset: 2},
 	},
 	{
 		in:  `{"128":"abc"}`,
 		ptr: new(map[int8]string),
-		err: &UnmarshalTypeError{"number 128", reflect.TypeOf(int8(0)), 2},
+		err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeOf(int8(0)), Offset: 2},
 	},
 	{
 		in:  `{"-1":"abc"}`,
 		ptr: new(map[uint8]string),
-		err: &UnmarshalTypeError{"number -1", reflect.TypeOf(uint8(0)), 2},
+		err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeOf(uint8(0)), Offset: 2},
 	},
 
 	// Map keys can be encoding.TextUnmarshalers.
@@ -653,12 +663,12 @@ var unmarshalTests = []unmarshalTest{
 	{
 		in:  `{"2009-11-10T23:00:00Z": "hello world"}`,
 		ptr: &map[Point]string{},
-		err: &UnmarshalTypeError{"object", reflect.TypeOf(map[Point]string{}), 1},
+		err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[Point]string{}), Offset: 1},
 	},
 	{
 		in:  `{"asdf": "hello world"}`,
 		ptr: &map[unmarshaler]string{},
-		err: &UnmarshalTypeError{"object", reflect.TypeOf(map[unmarshaler]string{}), 1},
+		err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[unmarshaler]string{}), Offset: 1},
 	},
 
 	// related to issue 13783.
@@ -738,6 +748,51 @@ var unmarshalTests = []unmarshalTest{
 		out:    []intWithPtrMarshalText{1, 2, 3},
 		golden: true,
 	},
+
+	{in: `0.000001`, ptr: new(float64), out: 0.000001, golden: true},
+	{in: `1e-7`, ptr: new(float64), out: 1e-7, golden: true},
+	{in: `100000000000000000000`, ptr: new(float64), out: 100000000000000000000.0, golden: true},
+	{in: `1e+21`, ptr: new(float64), out: 1e21, golden: true},
+	{in: `-0.000001`, ptr: new(float64), out: -0.000001, golden: true},
+	{in: `-1e-7`, ptr: new(float64), out: -1e-7, golden: true},
+	{in: `-100000000000000000000`, ptr: new(float64), out: -100000000000000000000.0, golden: true},
+	{in: `-1e+21`, ptr: new(float64), out: -1e21, golden: true},
+	{in: `999999999999999900000`, ptr: new(float64), out: 999999999999999900000.0, golden: true},
+	{in: `9007199254740992`, ptr: new(float64), out: 9007199254740992.0, golden: true},
+	{in: `9007199254740993`, ptr: new(float64), out: 9007199254740992.0, golden: false},
+
+	{
+		in:  `{"V": {"F2": "hello"}}`,
+		ptr: new(VOuter),
+		err: &UnmarshalTypeError{
+			Value:  "string",
+			Struct: "V",
+			Field:  "F2",
+			Type:   reflect.TypeOf(int32(0)),
+			Offset: 20,
+		},
+	},
+	{
+		in:  `{"V": {"F4": {}, "F2": "hello"}}`,
+		ptr: new(VOuter),
+		err: &UnmarshalTypeError{
+			Value:  "string",
+			Struct: "V",
+			Field:  "F2",
+			Type:   reflect.TypeOf(int32(0)),
+			Offset: 30,
+		},
+	},
+
+	// issue 15146.
+	// invalid inputs in wrongStringTests below.
+	{in: `{"B":"true"}`, ptr: new(B), out: B{true}, golden: true},
+	{in: `{"B":"false"}`, ptr: new(B), out: B{false}, golden: true},
+	{in: `{"B": "maybe"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "maybe" into bool`)},
+	{in: `{"B": "tru"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "tru" into bool`)},
+	{in: `{"B": "False"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "False" into bool`)},
+	{in: `{"B": "null"}`, ptr: new(B), out: B{false}},
+	{in: `{"B": "nul"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "nul" into bool`)},
 }
 
 func TestMarshal(t *testing.T) {
@@ -1470,40 +1525,148 @@ func TestInterfaceSet(t *testing.T) {
 	}
 }
 
+type NullTest struct {
+	Bool      bool
+	Int       int
+	Int8      int8
+	Int16     int16
+	Int32     int32
+	Int64     int64
+	Uint      uint
+	Uint8     uint8
+	Uint16    uint16
+	Uint32    uint32
+	Uint64    uint64
+	Float32   float32
+	Float64   float64
+	String    string
+	PBool     *bool
+	Map       map[string]string
+	Slice     []string
+	Interface interface{}
+
+	PRaw    *RawMessage
+	PTime   *time.Time
+	PBigInt *big.Int
+	PText   *MustNotUnmarshalText
+	PBuffer *bytes.Buffer // has methods, just not relevant ones
+	PStruct *struct{}
+
+	Raw    RawMessage
+	Time   time.Time
+	BigInt big.Int
+	Text   MustNotUnmarshalText
+	Buffer bytes.Buffer
+	Struct struct{}
+}
+
+type NullTestStrings struct {
+	Bool      bool              `json:",string"`
+	Int       int               `json:",string"`
+	Int8      int8              `json:",string"`
+	Int16     int16             `json:",string"`
+	Int32     int32             `json:",string"`
+	Int64     int64             `json:",string"`
+	Uint      uint              `json:",string"`
+	Uint8     uint8             `json:",string"`
+	Uint16    uint16            `json:",string"`
+	Uint32    uint32            `json:",string"`
+	Uint64    uint64            `json:",string"`
+	Float32   float32           `json:",string"`
+	Float64   float64           `json:",string"`
+	String    string            `json:",string"`
+	PBool     *bool             `json:",string"`
+	Map       map[string]string `json:",string"`
+	Slice     []string          `json:",string"`
+	Interface interface{}       `json:",string"`
+
+	PRaw    *RawMessage           `json:",string"`
+	PTime   *time.Time            `json:",string"`
+	PBigInt *big.Int              `json:",string"`
+	PText   *MustNotUnmarshalText `json:",string"`
+	PBuffer *bytes.Buffer         `json:",string"`
+	PStruct *struct{}             `json:",string"`
+
+	Raw    RawMessage           `json:",string"`
+	Time   time.Time            `json:",string"`
+	BigInt big.Int              `json:",string"`
+	Text   MustNotUnmarshalText `json:",string"`
+	Buffer bytes.Buffer         `json:",string"`
+	Struct struct{}             `json:",string"`
+}
+
 // JSON null values should be ignored for primitives and string values instead of resulting in an error.
 // Issue 2540
 func TestUnmarshalNulls(t *testing.T) {
+	// Unmarshal docs:
+	// The JSON null value unmarshals into an interface, map, pointer, or slice
+	// by setting that Go value to nil. Because null is often used in JSON to mean
+	// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
+	// on the value and produces no error.
+
 	jsonData := []byte(`{
-		"Bool"    : null,
-		"Int"     : null,
-		"Int8"    : null,
-		"Int16"   : null,
-		"Int32"   : null,
-		"Int64"   : null,
-		"Uint"    : null,
-		"Uint8"   : null,
-		"Uint16"  : null,
-		"Uint32"  : null,
-		"Uint64"  : null,
-		"Float32" : null,
-		"Float64" : null,
-		"String"  : null}`)
-
-	nulls := All{
-		Bool:    true,
-		Int:     2,
-		Int8:    3,
-		Int16:   4,
-		Int32:   5,
-		Int64:   6,
-		Uint:    7,
-		Uint8:   8,
-		Uint16:  9,
-		Uint32:  10,
-		Uint64:  11,
-		Float32: 12.1,
-		Float64: 13.1,
-		String:  "14"}
+				"Bool"    : null,
+				"Int"     : null,
+				"Int8"    : null,
+				"Int16"   : null,
+				"Int32"   : null,
+				"Int64"   : null,
+				"Uint"    : null,
+				"Uint8"   : null,
+				"Uint16"  : null,
+				"Uint32"  : null,
+				"Uint64"  : null,
+				"Float32" : null,
+				"Float64" : null,
+				"String"  : null,
+				"PBool": null,
+				"Map": null,
+				"Slice": null,
+				"Interface": null,
+				"PRaw": null,
+				"PTime": null,
+				"PBigInt": null,
+				"PText": null,
+				"PBuffer": null,
+				"PStruct": null,
+				"Raw": null,
+				"Time": null,
+				"BigInt": null,
+				"Text": null,
+				"Buffer": null,
+				"Struct": null
+			}`)
+	nulls := NullTest{
+		Bool:      true,
+		Int:       2,
+		Int8:      3,
+		Int16:     4,
+		Int32:     5,
+		Int64:     6,
+		Uint:      7,
+		Uint8:     8,
+		Uint16:    9,
+		Uint32:    10,
+		Uint64:    11,
+		Float32:   12.1,
+		Float64:   13.1,
+		String:    "14",
+		PBool:     new(bool),
+		Map:       map[string]string{},
+		Slice:     []string{},
+		Interface: new(MustNotUnmarshalJSON),
+		PRaw:      new(RawMessage),
+		PTime:     new(time.Time),
+		PBigInt:   new(big.Int),
+		PText:     new(MustNotUnmarshalText),
+		PStruct:   new(struct{}),
+		PBuffer:   new(bytes.Buffer),
+		Raw:       RawMessage("123"),
+		Time:      time.Unix(123456789, 0),
+		BigInt:    *big.NewInt(123),
+	}
+
+	before := nulls.Time.String()
 
 	err := Unmarshal(jsonData, &nulls)
 	if err != nil {
@@ -1512,9 +1675,61 @@ func TestUnmarshalNulls(t *testing.T) {
 	if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 ||
 		nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 ||
 		nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" {
-
 		t.Errorf("Unmarshal of null values affected primitives")
 	}
+
+	if nulls.PBool != nil {
+		t.Errorf("Unmarshal of null did not clear nulls.PBool")
+	}
+	if nulls.Map != nil {
+		t.Errorf("Unmarshal of null did not clear nulls.Map")
+	}
+	if nulls.Slice != nil {
+		t.Errorf("Unmarshal of null did not clear nulls.Slice")
+	}
+	if nulls.Interface != nil {
+		t.Errorf("Unmarshal of null did not clear nulls.Interface")
+	}
+	if nulls.PRaw != nil {
+		t.Errorf("Unmarshal of null did not clear nulls.PRaw")
+	}
+	if nulls.PTime != nil {
+		t.Errorf("Unmarshal of null did not clear nulls.PTime")
+	}
+	if nulls.PBigInt != nil {
+		t.Errorf("Unmarshal of null did not clear nulls.PBigInt")
+	}
+	if nulls.PText != nil {
+		t.Errorf("Unmarshal of null did not clear nulls.PText")
+	}
+	if nulls.PBuffer != nil {
+		t.Errorf("Unmarshal of null did not clear nulls.PBuffer")
+	}
+	if nulls.PStruct != nil {
+		t.Errorf("Unmarshal of null did not clear nulls.PStruct")
+	}
+
+	if string(nulls.Raw) != "null" {
+		t.Errorf("Unmarshal of RawMessage null did not record null: %v", string(nulls.Raw))
+	}
+	if nulls.Time.String() != before {
+		t.Errorf("Unmarshal of time.Time null set time to %v", nulls.Time.String())
+	}
+	if nulls.BigInt.String() != "123" {
+		t.Errorf("Unmarshal of big.Int null set int to %v", nulls.BigInt.String())
+	}
+}
+
+type MustNotUnmarshalJSON struct{}
+
+func (x MustNotUnmarshalJSON) UnmarshalJSON(data []byte) error {
+	return errors.New("MustNotUnmarshalJSON was used")
+}
+
+type MustNotUnmarshalText struct{}
+
+func (x MustNotUnmarshalText) UnmarshalText(text []byte) error {
+	return errors.New("MustNotUnmarshalText was used")
 }
 
 func TestStringKind(t *testing.T) {
@@ -1540,8 +1755,8 @@ func TestStringKind(t *testing.T) {
 	}
 }
 
-// Custom types with []byte as underlying type could not be marshalled
-// and then unmarshalled.
+// Custom types with []byte as underlying type could not be marshaled
+// and then unmarshaled.
 // Issue 8962.
 func TestByteKind(t *testing.T) {
 	type byteKind []byte
@@ -1753,7 +1968,7 @@ var invalidUnmarshalTextTests = []struct {
 	{nil, "json: Unmarshal(nil)"},
 	{struct{}{}, "json: Unmarshal(non-pointer struct {})"},
 	{(*int)(nil), "json: Unmarshal(nil *int)"},
-	{new(net.IP), "json: cannot unmarshal string into Go value of type *net.IP"},
+	{new(net.IP), "json: cannot unmarshal number into Go value of type *net.IP"},
 }
 
 func TestInvalidUnmarshalText(t *testing.T) {
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index 6bb6de8..8f21dda 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -34,7 +34,7 @@ import (
 // and is not a nil pointer, Marshal calls its MarshalJSON method
 // to produce JSON. If no MarshalJSON method is present but the
 // value implements encoding.TextMarshaler instead, Marshal calls
-// its MarshalText method.
+// its MarshalText method and encodes the result as a JSON string.
 // The nil pointer exception is not strictly necessary
 // but mimics a similar, necessary exception in the behavior of
 // UnmarshalJSON.
@@ -50,25 +50,33 @@ import (
 // The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e"
 // to keep some browsers from misinterpreting JSON output as HTML.
 // Ampersand "&" is also escaped to "\u0026" for the same reason.
-// This escaping can be disabled using an Encoder with DisableHTMLEscaping.
+// This escaping can be disabled using an Encoder that had SetEscapeHTML(false)
+// called on it.
 //
 // Array and slice values encode as JSON arrays, except that
 // []byte encodes as a base64-encoded string, and a nil slice
 // encodes as the null JSON value.
 //
-// Struct values encode as JSON objects. Each exported struct field
-// becomes a member of the object unless
-//   - the field's tag is "-", or
-//   - the field is empty and its tag specifies the "omitempty" option.
-// The empty values are false, 0, any
-// nil pointer or interface value, and any array, slice, map, or string of
-// length zero. The object's default key string is the struct field name
-// but can be specified in the struct field's tag value. The "json" key in
-// the struct field's tag value is the key name, followed by an optional comma
-// and options. Examples:
+// Struct values encode as JSON objects.
+// Each exported struct field becomes a member of the object, using the
+// field name as the object key, unless the field is omitted for one of the
+// reasons given below.
 //
-//   // Field is ignored by this package.
-//   Field int `json:"-"`
+// The encoding of each struct field can be customized by the format string
+// stored under the "json" key in the struct field's tag.
+// The format string gives the name of the field, possibly followed by a
+// comma-separated list of options. The name may be empty in order to
+// specify options without overriding the default field name.
+//
+// The "omitempty" option specifies that the field should be omitted
+// from the encoding if the field has an empty value, defined as
+// false, 0, a nil pointer, a nil interface value, and any empty array,
+// slice, map, or string.
+//
+// As a special case, if the field tag is "-", the field is always omitted.
+// Note that a field with name "-" can still be generated using the tag "-,".
+//
+// Examples of struct field tags and their meanings:
 //
 //   // Field appears in JSON as key "myName".
 //   Field int `json:"myName"`
@@ -83,6 +91,12 @@ import (
 //   // Note the leading comma.
 //   Field int `json:",omitempty"`
 //
+//   // Field is ignored by this package.
+//   Field int `json:"-"`
+//
+//   // Field appears in JSON as key "-".
+//   Field int `json:"-,"`
+//
 // The "string" option signals that a field is stored as JSON inside a
 // JSON-encoded string. It applies only to fields of string, floating point,
 // integer, or boolean types. This extra level of encoding is sometimes used
@@ -110,7 +124,9 @@ import (
 //
 // 1) Of those fields, if any are JSON-tagged, only tagged fields are considered,
 // even if there are multiple untagged fields that would otherwise conflict.
+//
 // 2) If there is exactly one field (tagged or not according to the first rule), that is selected.
+//
 // 3) Otherwise there are multiple fields, and all are ignored; no error occurs.
 //
 // Handling of anonymous struct fields is new in Go 1.1.
@@ -427,7 +443,11 @@ func marshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
 		e.WriteString("null")
 		return
 	}
-	m := v.Interface().(Marshaler)
+	m, ok := v.Interface().(Marshaler)
+	if !ok {
+		e.WriteString("null")
+		return
+	}
 	b, err := m.MarshalJSON()
 	if err == nil {
 		// copy JSON into buffer, checking validity.
@@ -525,7 +545,31 @@ func (bits floatEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
 	if math.IsInf(f, 0) || math.IsNaN(f) {
 		e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, int(bits))})
 	}
-	b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, int(bits))
+
+	// Convert as if by ES6 number to string conversion.
+	// This matches most other JSON generators.
+	// See golang.org/issue/6384 and golang.org/issue/14135.
+	// Like fmt %g, but the exponent cutoffs are different
+	// and exponents themselves are not padded to two digits.
+	b := e.scratch[:0]
+	abs := math.Abs(f)
+	fmt := byte('f')
+	// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
+	if abs != 0 {
+		if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
+			fmt = 'e'
+		}
+	}
+	b = strconv.AppendFloat(b, f, fmt, -1, int(bits))
+	if fmt == 'e' {
+		// clean up e-09 to e-9
+		n := len(b)
+		if n >= 4 && b[n-4] == 'e' && b[n-3] == '-' && b[n-2] == '0' {
+			b[n-2] = b[n-1]
+			b = b[:n-1]
+		}
+	}
+
 	if opts.quoted {
 		e.WriteByte('"')
 	}
@@ -635,7 +679,7 @@ func (me *mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
 			e.error(&MarshalerError{v.Type(), err})
 		}
 	}
-	sort.Sort(byString(sv))
+	sort.Slice(sv, func(i, j int) bool { return sv[i].s < sv[j].s })
 
 	for i, kv := range sv {
 		if i > 0 {
@@ -834,15 +878,6 @@ func (w *reflectWithString) resolve() error {
 	panic("unexpected map key type")
 }
 
-// byString is a slice of reflectWithString where the reflect.Value is either
-// a string or an encoding.TextMarshaler.
-// It implements the methods to sort by string.
-type byString []reflectWithString
-
-func (sv byString) Len() int           { return len(sv) }
-func (sv byString) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
-func (sv byString) Less(i, j int) bool { return sv[i].s < sv[j].s }
-
 // NOTE: keep in sync with stringBytes below.
 func (e *encodeState) string(s string, escapeHTML bool) int {
 	len0 := e.Len()
@@ -850,8 +885,7 @@ func (e *encodeState) string(s string, escapeHTML bool) int {
 	start := 0
 	for i := 0; i < len(s); {
 		if b := s[i]; b < utf8.RuneSelf {
-			if 0x20 <= b && b != '\\' && b != '"' &&
-				(!escapeHTML || b != '<' && b != '>' && b != '&') {
+			if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {
 				i++
 				continue
 			}
@@ -928,8 +962,7 @@ func (e *encodeState) stringBytes(s []byte, escapeHTML bool) int {
 	start := 0
 	for i := 0; i < len(s); {
 		if b := s[i]; b < utf8.RuneSelf {
-			if 0x20 <= b && b != '\\' && b != '"' &&
-				(!escapeHTML || b != '<' && b != '>' && b != '&') {
+			if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {
 				i++
 				continue
 			}
@@ -1018,28 +1051,6 @@ func fillField(f field) field {
 	return f
 }
 
-// byName sorts field by name, breaking ties with depth,
-// then breaking ties with "name came from json tag", then
-// breaking ties with index sequence.
-type byName []field
-
-func (x byName) Len() int { return len(x) }
-
-func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-
-func (x byName) Less(i, j int) bool {
-	if x[i].name != x[j].name {
-		return x[i].name < x[j].name
-	}
-	if len(x[i].index) != len(x[j].index) {
-		return len(x[i].index) < len(x[j].index)
-	}
-	if x[i].tag != x[j].tag {
-		return x[i].tag
-	}
-	return byIndex(x).Less(i, j)
-}
-
 // byIndex sorts field by index sequence.
 type byIndex []field
 
@@ -1157,7 +1168,22 @@ func typeFields(t reflect.Type) []field {
 		}
 	}
 
-	sort.Sort(byName(fields))
+	sort.Slice(fields, func(i, j int) bool {
+		x := fields
+		// sort field by name, breaking ties with depth, then
+		// breaking ties with "name came from json tag", then
+		// breaking ties with index sequence.
+		if x[i].name != x[j].name {
+			return x[i].name < x[j].name
+		}
+		if len(x[i].index) != len(x[j].index) {
+			return len(x[i].index) < len(x[j].index)
+		}
+		if x[i].tag != x[j].tag {
+			return x[i].tag
+		}
+		return byIndex(x).Less(i, j)
+	})
 
 	// Delete all fields that are hidden by the Go rules for embedded fields,
 	// except that fields with JSON tags are promoted.
diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go
index b484022..6d574cf 100644
--- a/src/encoding/json/encode_test.go
+++ b/src/encoding/json/encode_test.go
@@ -7,8 +7,11 @@ package json
 import (
 	"bytes"
 	"fmt"
+	"log"
 	"math"
 	"reflect"
+	"regexp"
+	"strconv"
 	"testing"
 	"unicode"
 )
@@ -290,6 +293,44 @@ type BugX struct {
 	BugB
 }
 
+// Issue 16042. Even if a nil interface value is passed in
+// as long as it implements MarshalJSON, it should be marshaled.
+type nilMarshaler string
+
+func (nm *nilMarshaler) MarshalJSON() ([]byte, error) {
+	if nm == nil {
+		return Marshal("0zenil0")
+	}
+	return Marshal("zenil:" + string(*nm))
+}
+
+// Issue 16042.
+func TestNilMarshal(t *testing.T) {
+	testCases := []struct {
+		v    interface{}
+		want string
+	}{
+		{v: nil, want: `null`},
+		{v: new(float64), want: `0`},
+		{v: []interface{}(nil), want: `null`},
+		{v: []string(nil), want: `null`},
+		{v: map[string]string(nil), want: `null`},
+		{v: []byte(nil), want: `null`},
+		{v: struct{ M string }{"gopher"}, want: `{"M":"gopher"}`},
+		{v: struct{ M Marshaler }{}, want: `{"M":null}`},
+		{v: struct{ M Marshaler }{(*nilMarshaler)(nil)}, want: `{"M":"0zenil0"}`},
+		{v: struct{ M interface{} }{(*nilMarshaler)(nil)}, want: `{"M":null}`},
+	}
+
+	for _, tt := range testCases {
+		out, err := Marshal(tt.v)
+		if err != nil || string(out) != tt.want {
+			t.Errorf("Marshal(%#v) = %#q, %#v, want %#q, nil", tt.v, out, err, tt.want)
+			continue
+		}
+	}
+}
+
 // Issue 5245.
 func TestEmbeddedBug(t *testing.T) {
 	v := BugB{
@@ -375,6 +416,7 @@ func TestDuplicatedFieldDisappears(t *testing.T) {
 }
 
 func TestStringBytes(t *testing.T) {
+	t.Parallel()
 	// Test that encodeState.stringBytes and encodeState.string use the same encoding.
 	var r []rune
 	for i := '\u0000'; i <= unicode.MaxRune; i++ {
@@ -418,30 +460,6 @@ func TestStringBytes(t *testing.T) {
 	}
 }
 
-func TestIssue6458(t *testing.T) {
-	type Foo struct {
-		M RawMessage
-	}
-	x := Foo{RawMessage(`"foo"`)}
-
-	b, err := Marshal(&x)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if want := `{"M":"foo"}`; string(b) != want {
-		t.Errorf("Marshal(&x) = %#q; want %#q", b, want)
-	}
-
-	b, err = Marshal(x)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if want := `{"M":"ImZvbyI="}`; string(b) != want {
-		t.Errorf("Marshal(x) = %#q; want %#q", b, want)
-	}
-}
-
 func TestIssue10281(t *testing.T) {
 	type Foo struct {
 		N Number
@@ -483,7 +501,7 @@ func TestEncodePointerString(t *testing.T) {
 		t.Fatalf("Unmarshal: %v", err)
 	}
 	if back.N == nil {
-		t.Fatalf("Unmarshalled nil N field")
+		t.Fatalf("Unmarshaled nil N field")
 	}
 	if *back.N != 42 {
 		t.Fatalf("*N = %d; want 42", *back.N)
@@ -611,3 +629,208 @@ func TestTextMarshalerMapKeysAreSorted(t *testing.T) {
 		t.Errorf("Marshal map with text.Marshaler keys: got %#q, want %#q", b, want)
 	}
 }
+
+var re = regexp.MustCompile
+
+// syntactic checks on form of marshaled floating point numbers.
+var badFloatREs = []*regexp.Regexp{
+	re(`p`),                     // no binary exponential notation
+	re(`^\+`),                   // no leading + sign
+	re(`^-?0[^.]`),              // no unnecessary leading zeros
+	re(`^-?\.`),                 // leading zero required before decimal point
+	re(`\.(e|$)`),               // no trailing decimal
+	re(`\.[0-9]+0(e|$)`),        // no trailing zero in fraction
+	re(`^-?(0|[0-9]{2,})\..*e`), // exponential notation must have normalized mantissa
+	re(`e[0-9]`),                // positive exponent must be signed
+	re(`e[+-]0`),                // exponent must not have leading zeros
+	re(`e-[1-6]$`),              // not tiny enough for exponential notation
+	re(`e+(.|1.|20)$`),          // not big enough for exponential notation
+	re(`^-?0\.0000000`),         // too tiny, should use exponential notation
+	re(`^-?[0-9]{22}`),          // too big, should use exponential notation
+	re(`[1-9][0-9]{16}[1-9]`),   // too many significant digits in integer
+	re(`[1-9][0-9.]{17}[1-9]`),  // too many significant digits in decimal
+	// below here for float32 only
+	re(`[1-9][0-9]{8}[1-9]`),  // too many significant digits in integer
+	re(`[1-9][0-9.]{9}[1-9]`), // too many significant digits in decimal
+}
+
+func TestMarshalFloat(t *testing.T) {
+	t.Parallel()
+	nfail := 0
+	test := func(f float64, bits int) {
+		vf := interface{}(f)
+		if bits == 32 {
+			f = float64(float32(f)) // round
+			vf = float32(f)
+		}
+		bout, err := Marshal(vf)
+		if err != nil {
+			t.Errorf("Marshal(%T(%g)): %v", vf, vf, err)
+			nfail++
+			return
+		}
+		out := string(bout)
+
+		// result must convert back to the same float
+		g, err := strconv.ParseFloat(out, bits)
+		if err != nil {
+			t.Errorf("Marshal(%T(%g)) = %q, cannot parse back: %v", vf, vf, out, err)
+			nfail++
+			return
+		}
+		if f != g || fmt.Sprint(f) != fmt.Sprint(g) { // fmt.Sprint handles ±0
+			t.Errorf("Marshal(%T(%g)) = %q (is %g, not %g)", vf, vf, out, float32(g), vf)
+			nfail++
+			return
+		}
+
+		bad := badFloatREs
+		if bits == 64 {
+			bad = bad[:len(bad)-2]
+		}
+		for _, re := range bad {
+			if re.MatchString(out) {
+				t.Errorf("Marshal(%T(%g)) = %q, must not match /%s/", vf, vf, out, re)
+				nfail++
+				return
+			}
+		}
+	}
+
+	var (
+		bigger  = math.Inf(+1)
+		smaller = math.Inf(-1)
+	)
+
+	var digits = "1.2345678901234567890123"
+	for i := len(digits); i >= 2; i-- {
+		for exp := -30; exp <= 30; exp++ {
+			for _, sign := range "+-" {
+				for bits := 32; bits <= 64; bits += 32 {
+					s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp)
+					f, err := strconv.ParseFloat(s, bits)
+					if err != nil {
+						log.Fatal(err)
+					}
+					next := math.Nextafter
+					if bits == 32 {
+						next = func(g, h float64) float64 {
+							return float64(math.Nextafter32(float32(g), float32(h)))
+						}
+					}
+					test(f, bits)
+					test(next(f, bigger), bits)
+					test(next(f, smaller), bits)
+					if nfail > 50 {
+						t.Fatalf("stopping test early")
+					}
+				}
+			}
+		}
+	}
+	test(0, 64)
+	test(math.Copysign(0, -1), 64)
+	test(0, 32)
+	test(math.Copysign(0, -1), 32)
+}
+
+func TestMarshalRawMessageValue(t *testing.T) {
+	type (
+		T1 struct {
+			M RawMessage `json:",omitempty"`
+		}
+		T2 struct {
+			M *RawMessage `json:",omitempty"`
+		}
+	)
+
+	var (
+		rawNil   = RawMessage(nil)
+		rawEmpty = RawMessage([]byte{})
+		rawText  = RawMessage([]byte(`"foo"`))
+	)
+
+	tests := []struct {
+		in   interface{}
+		want string
+		ok   bool
+	}{
+		// Test with nil RawMessage.
+		{rawNil, "null", true},
+		{&rawNil, "null", true},
+		{[]interface{}{rawNil}, "[null]", true},
+		{&[]interface{}{rawNil}, "[null]", true},
+		{[]interface{}{&rawNil}, "[null]", true},
+		{&[]interface{}{&rawNil}, "[null]", true},
+		{struct{ M RawMessage }{rawNil}, `{"M":null}`, true},
+		{&struct{ M RawMessage }{rawNil}, `{"M":null}`, true},
+		{struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true},
+		{&struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true},
+		{map[string]interface{}{"M": rawNil}, `{"M":null}`, true},
+		{&map[string]interface{}{"M": rawNil}, `{"M":null}`, true},
+		{map[string]interface{}{"M": &rawNil}, `{"M":null}`, true},
+		{&map[string]interface{}{"M": &rawNil}, `{"M":null}`, true},
+		{T1{rawNil}, "{}", true},
+		{T2{&rawNil}, `{"M":null}`, true},
+		{&T1{rawNil}, "{}", true},
+		{&T2{&rawNil}, `{"M":null}`, true},
+
+		// Test with empty, but non-nil, RawMessage.
+		{rawEmpty, "", false},
+		{&rawEmpty, "", false},
+		{[]interface{}{rawEmpty}, "", false},
+		{&[]interface{}{rawEmpty}, "", false},
+		{[]interface{}{&rawEmpty}, "", false},
+		{&[]interface{}{&rawEmpty}, "", false},
+		{struct{ X RawMessage }{rawEmpty}, "", false},
+		{&struct{ X RawMessage }{rawEmpty}, "", false},
+		{struct{ X *RawMessage }{&rawEmpty}, "", false},
+		{&struct{ X *RawMessage }{&rawEmpty}, "", false},
+		{map[string]interface{}{"nil": rawEmpty}, "", false},
+		{&map[string]interface{}{"nil": rawEmpty}, "", false},
+		{map[string]interface{}{"nil": &rawEmpty}, "", false},
+		{&map[string]interface{}{"nil": &rawEmpty}, "", false},
+		{T1{rawEmpty}, "{}", true},
+		{T2{&rawEmpty}, "", false},
+		{&T1{rawEmpty}, "{}", true},
+		{&T2{&rawEmpty}, "", false},
+
+		// Test with RawMessage with some text.
+		//
+		// The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo".
+		// This behavior was intentionally changed in Go 1.8.
+		// See https://github.com/golang/go/issues/14493#issuecomment-255857318
+		{rawText, `"foo"`, true}, // Issue6458
+		{&rawText, `"foo"`, true},
+		{[]interface{}{rawText}, `["foo"]`, true},  // Issue6458
+		{&[]interface{}{rawText}, `["foo"]`, true}, // Issue6458
+		{[]interface{}{&rawText}, `["foo"]`, true},
+		{&[]interface{}{&rawText}, `["foo"]`, true},
+		{struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458
+		{&struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true},
+		{struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true},
+		{&struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true},
+		{map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true},  // Issue6458
+		{&map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458
+		{map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true},
+		{&map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true},
+		{T1{rawText}, `{"M":"foo"}`, true}, // Issue6458
+		{T2{&rawText}, `{"M":"foo"}`, true},
+		{&T1{rawText}, `{"M":"foo"}`, true},
+		{&T2{&rawText}, `{"M":"foo"}`, true},
+	}
+
+	for i, tt := range tests {
+		b, err := Marshal(tt.in)
+		if ok := (err == nil); ok != tt.ok {
+			if err != nil {
+				t.Errorf("test %d, unexpected failure: %v", i, err)
+			} else {
+				t.Errorf("test %d, unexpected success", i)
+			}
+		}
+		if got := string(b); got != tt.want {
+			t.Errorf("test %d, Marshal(%#v) = %q, want %q", i, tt.in, got, tt.want)
+		}
+	}
+}
diff --git a/src/encoding/json/example_marshaling_test.go b/src/encoding/json/example_marshaling_test.go
new file mode 100644
index 0000000..7f15c74
--- /dev/null
+++ b/src/encoding/json/example_marshaling_test.go
@@ -0,0 +1,73 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package json_test
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+	"strings"
+)
+
+type Animal int
+
+const (
+	Unknown Animal = iota
+	Gopher
+	Zebra
+)
+
+func (a *Animal) UnmarshalJSON(b []byte) error {
+	var s string
+	if err := json.Unmarshal(b, &s); err != nil {
+		return err
+	}
+	switch strings.ToLower(s) {
+	default:
+		*a = Unknown
+	case "gopher":
+		*a = Gopher
+	case "zebra":
+		*a = Zebra
+	}
+
+	return nil
+}
+
+func (a Animal) MarshalJSON() ([]byte, error) {
+	var s string
+	switch a {
+	default:
+		s = "unknown"
+	case Gopher:
+		s = "gopher"
+	case Zebra:
+		s = "zebra"
+	}
+
+	return json.Marshal(s)
+}
+
+func Example_customMarshalJSON() {
+	blob := `["gopher","armadillo","zebra","unknown","gopher","bee","gopher","zebra"]`
+	var zoo []Animal
+	if err := json.Unmarshal([]byte(blob), &zoo); err != nil {
+		log.Fatal(err)
+	}
+
+	census := make(map[Animal]int)
+	for _, animal := range zoo {
+		census[animal] += 1
+	}
+
+	fmt.Printf("Zoo Census:\n* Gophers: %d\n* Zebras:  %d\n* Unknown: %d\n",
+		census[Gopher], census[Zebra], census[Unknown])
+
+	// Output:
+	// Zoo Census:
+	// * Gophers: 3
+	// * Zebras:  2
+	// * Unknown: 3
+}
diff --git a/src/encoding/json/example_test.go b/src/encoding/json/example_test.go
index 555eff9..e4dffd9 100644
--- a/src/encoding/json/example_test.go
+++ b/src/encoding/json/example_test.go
@@ -174,7 +174,7 @@ func ExampleDecoder_Decode_stream() {
 }
 
 // This example uses RawMessage to delay parsing part of a JSON message.
-func ExampleRawMessage() {
+func ExampleRawMessage_unmarshal() {
 	type Color struct {
 		Space string
 		Point json.RawMessage // delay parsing until we know the color space
@@ -219,6 +219,30 @@ func ExampleRawMessage() {
 	// RGB &{98 218 255}
 }
 
+// This example uses RawMessage to use a precomputed JSON during marshal.
+func ExampleRawMessage_marshal() {
+	h := json.RawMessage(`{"precomputed": true}`)
+
+	c := struct {
+		Header *json.RawMessage `json:"header"`
+		Body   string           `json:"body"`
+	}{Header: &h, Body: "Hello Gophers!"}
+
+	b, err := json.MarshalIndent(&c, "", "\t")
+	if err != nil {
+		fmt.Println("error:", err)
+	}
+	os.Stdout.Write(b)
+
+	// Output:
+	// {
+	// 	"header": {
+	// 		"precomputed": true
+	// 	},
+	// 	"body": "Hello Gophers!"
+	// }
+}
+
 func ExampleIndent() {
 	type Road struct {
 		Name   string
diff --git a/src/encoding/json/scanner_test.go b/src/encoding/json/scanner_test.go
index 70a2897..c5c1be3 100644
--- a/src/encoding/json/scanner_test.go
+++ b/src/encoding/json/scanner_test.go
@@ -119,6 +119,7 @@ func TestCompactBig(t *testing.T) {
 }
 
 func TestIndentBig(t *testing.T) {
+	t.Parallel()
 	initBig()
 	var buf bytes.Buffer
 	if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
diff --git a/src/encoding/json/stream.go b/src/encoding/json/stream.go
index 87f0e57..95e30ce 100644
--- a/src/encoding/json/stream.go
+++ b/src/encoding/json/stream.go
@@ -246,9 +246,12 @@ func (enc *Encoder) SetEscapeHTML(on bool) {
 // be used to delay JSON decoding or precompute a JSON encoding.
 type RawMessage []byte
 
-// MarshalJSON returns *m as the JSON encoding of m.
-func (m *RawMessage) MarshalJSON() ([]byte, error) {
-	return *m, nil
+// MarshalJSON returns m as the JSON encoding of m.
+func (m RawMessage) MarshalJSON() ([]byte, error) {
+	if m == nil {
+		return []byte("null"), nil
+	}
+	return m, nil
 }
 
 // UnmarshalJSON sets *m to a copy of data.
diff --git a/src/encoding/json/tables.go b/src/encoding/json/tables.go
new file mode 100644
index 0000000..10acdc1
--- /dev/null
+++ b/src/encoding/json/tables.go
@@ -0,0 +1,218 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package json
+
+import "unicode/utf8"
+
+// safeSet holds the value true if the ASCII character with the given array
+// position can be represented inside a JSON string without any further
+// escaping.
+//
+// All values are true except for the ASCII control characters (0-31), the
+// double quote ("), and the backslash character ("\").
+var safeSet = [utf8.RuneSelf]bool{
+	' ':      true,
+	'!':      true,
+	'"':      false,
+	'#':      true,
+	'$':      true,
+	'%':      true,
+	'&':      true,
+	'\'':     true,
+	'(':      true,
+	')':      true,
+	'*':      true,
+	'+':      true,
+	',':      true,
+	'-':      true,
+	'.':      true,
+	'/':      true,
+	'0':      true,
+	'1':      true,
+	'2':      true,
+	'3':      true,
+	'4':      true,
+	'5':      true,
+	'6':      true,
+	'7':      true,
+	'8':      true,
+	'9':      true,
+	':':      true,
+	';':      true,
+	'<':      true,
+	'=':      true,
+	'>':      true,
+	'?':      true,
+	'@':      true,
+	'A':      true,
+	'B':      true,
+	'C':      true,
+	'D':      true,
+	'E':      true,
+	'F':      true,
+	'G':      true,
+	'H':      true,
+	'I':      true,
+	'J':      true,
+	'K':      true,
+	'L':      true,
+	'M':      true,
+	'N':      true,
+	'O':      true,
+	'P':      true,
+	'Q':      true,
+	'R':      true,
+	'S':      true,
+	'T':      true,
+	'U':      true,
+	'V':      true,
+	'W':      true,
+	'X':      true,
+	'Y':      true,
+	'Z':      true,
+	'[':      true,
+	'\\':     false,
+	']':      true,
+	'^':      true,
+	'_':      true,
+	'`':      true,
+	'a':      true,
+	'b':      true,
+	'c':      true,
+	'd':      true,
+	'e':      true,
+	'f':      true,
+	'g':      true,
+	'h':      true,
+	'i':      true,
+	'j':      true,
+	'k':      true,
+	'l':      true,
+	'm':      true,
+	'n':      true,
+	'o':      true,
+	'p':      true,
+	'q':      true,
+	'r':      true,
+	's':      true,
+	't':      true,
+	'u':      true,
+	'v':      true,
+	'w':      true,
+	'x':      true,
+	'y':      true,
+	'z':      true,
+	'{':      true,
+	'|':      true,
+	'}':      true,
+	'~':      true,
+	'\u007f': true,
+}
+
+// htmlSafeSet holds the value true if the ASCII character with the given
+// array position can be safely represented inside a JSON string, embedded
+// inside of HTML <script> tags, without any additional escaping.
+//
+// All values are true except for the ASCII control characters (0-31), the
+// double quote ("), the backslash character ("\"), HTML opening and closing
+// tags ("<" and ">"), and the ampersand ("&").
+var htmlSafeSet = [utf8.RuneSelf]bool{
+	' ':      true,
+	'!':      true,
+	'"':      false,
+	'#':      true,
+	'$':      true,
+	'%':      true,
+	'&':      false,
+	'\'':     true,
+	'(':      true,
+	')':      true,
+	'*':      true,
+	'+':      true,
+	',':      true,
+	'-':      true,
+	'.':      true,
+	'/':      true,
+	'0':      true,
+	'1':      true,
+	'2':      true,
+	'3':      true,
+	'4':      true,
+	'5':      true,
+	'6':      true,
+	'7':      true,
+	'8':      true,
+	'9':      true,
+	':':      true,
+	';':      true,
+	'<':      false,
+	'=':      true,
+	'>':      false,
+	'?':      true,
+	'@':      true,
+	'A':      true,
+	'B':      true,
+	'C':      true,
+	'D':      true,
+	'E':      true,
+	'F':      true,
+	'G':      true,
+	'H':      true,
+	'I':      true,
+	'J':      true,
+	'K':      true,
+	'L':      true,
+	'M':      true,
+	'N':      true,
+	'O':      true,
+	'P':      true,
+	'Q':      true,
+	'R':      true,
+	'S':      true,
+	'T':      true,
+	'U':      true,
+	'V':      true,
+	'W':      true,
+	'X':      true,
+	'Y':      true,
+	'Z':      true,
+	'[':      true,
+	'\\':     false,
+	']':      true,
+	'^':      true,
+	'_':      true,
+	'`':      true,
+	'a':      true,
+	'b':      true,
+	'c':      true,
+	'd':      true,
+	'e':      true,
+	'f':      true,
+	'g':      true,
+	'h':      true,
+	'i':      true,
+	'j':      true,
+	'k':      true,
+	'l':      true,
+	'm':      true,
+	'n':      true,
+	'o':      true,
+	'p':      true,
+	'q':      true,
+	'r':      true,
+	's':      true,
+	't':      true,
+	'u':      true,
+	'v':      true,
+	'w':      true,
+	'x':      true,
+	'y':      true,
+	'z':      true,
+	'{':      true,
+	'|':      true,
+	'}':      true,
+	'~':      true,
+	'\u007f': true,
+}
diff --git a/src/encoding/json/tagkey_test.go b/src/encoding/json/tagkey_test.go
index c1739ea..f77c49c 100644
--- a/src/encoding/json/tagkey_test.go
+++ b/src/encoding/json/tagkey_test.go
@@ -44,6 +44,10 @@ type punctuationTag struct {
 	V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // https://golang.org/issue/3546
 }
 
+type dashTag struct {
+	V string `json:"-,"`
+}
+
 type emptyTag struct {
 	W string
 }
@@ -80,6 +84,7 @@ var structTagObjectKeyTests = []struct {
 	{basicLatin6xTag{"6x"}, "6x", "abcdefghijklmno"},
 	{basicLatin7xTag{"7x"}, "7x", "pqrstuvwxyz"},
 	{miscPlaneTag{"いろはにほへと"}, "いろはにほへと", "色は匂へど"},
+	{dashTag{"foo"}, "foo", "-"},
 	{emptyTag{"Pour Moi"}, "Pour Moi", "W"},
 	{misnamedTag{"Animal Kingdom"}, "Animal Kingdom", "X"},
 	{badFormatTag{"Orfevre"}, "Orfevre", "Y"},
diff --git a/src/encoding/pem/example_test.go b/src/encoding/pem/example_test.go
new file mode 100644
index 0000000..900b31c
--- /dev/null
+++ b/src/encoding/pem/example_test.go
@@ -0,0 +1,44 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pem_test
+
+import (
+	"crypto/x509"
+	"encoding/pem"
+	"fmt"
+	"log"
+)
+
+func ExampleDecode() {
+	var pubPEMData = []byte(`
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlRuRnThUjU8/prwYxbty
+WPT9pURI3lbsKMiB6Fn/VHOKE13p4D8xgOCADpdRagdT6n4etr9atzDKUSvpMtR3
+CP5noNc97WiNCggBjVWhs7szEe8ugyqF23XwpHQ6uV1LKH50m92MbOWfCtjU9p/x
+qhNpQQ1AZhqNy5Gevap5k8XzRmjSldNAFZMY7Yv3Gi+nyCwGwpVtBUwhuLzgNFK/
+yDtw2WcWmUU7NuC8Q6MWvPebxVtCfVp/iQU6q60yyt6aGOBkhAX0LpKAEhKidixY
+nP9PNVBvxgu3XZ4P36gZV6+ummKdBVnc3NqwBLu5+CcdRdusmHPHd5pHf4/38Z3/
+6qU2a/fPvWzceVTEgZ47QjFMTCTmCwNt29cvi7zZeQzjtwQgn4ipN9NibRH/Ax/q
+TbIzHfrJ1xa2RteWSdFjwtxi9C20HUkjXSeI4YlzQMH0fPX6KCE7aVePTOnB69I/
+a9/q96DiXZajwlpq3wFctrs1oXqBp5DVrCIj8hU2wNgB7LtQ1mCtsYz//heai0K9
+PhE4X6hiE0YmeAZjR0uHl8M/5aW9xCoJ72+12kKpWAa0SFRWLy6FejNYCYpkupVJ
+yecLk/4L1W0l6jQQZnWErXZYe0PNFcmwGXy1Rep83kfBRNKRy5tvocalLlwXLdUk
+AIU+2GKjyT3iMuzZxxFxPFMCAwEAAQ==
+-----END PUBLIC KEY-----
+and some more`)
+
+	block, rest := pem.Decode(pubPEMData)
+	if block == nil || block.Type != "PUBLIC KEY" {
+		log.Fatal("failed to decode PEM block containing public key")
+	}
+
+	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("Got a %T, with remaining data: %q", pub, rest)
+	// Output: Got a *rsa.PublicKey, with remaining data: "and some more"
+}
diff --git a/src/encoding/pem/pem.go b/src/encoding/pem/pem.go
index ff2bed1..fbf4999 100644
--- a/src/encoding/pem/pem.go
+++ b/src/encoding/pem/pem.go
@@ -119,19 +119,36 @@ func Decode(data []byte) (p *Block, rest []byte) {
 		rest = next
 	}
 
-	var endIndex int
+	var endIndex, endTrailerIndex int
+
 	// If there were no headers, the END line might occur
 	// immediately, without a leading newline.
 	if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
 		endIndex = 0
+		endTrailerIndex = len(pemEnd) - 1
 	} else {
 		endIndex = bytes.Index(rest, pemEnd)
+		endTrailerIndex = endIndex + len(pemEnd)
 	}
 
 	if endIndex < 0 {
 		return decodeError(data, rest)
 	}
 
+	// After the "-----" of the ending line should be the same type and a
+	// final five dashes.
+	endTrailer := rest[endTrailerIndex:]
+	endTrailerLen := len(typeLine) + len(pemEndOfLine)
+	if len(endTrailer) < endTrailerLen {
+		return decodeError(data, rest)
+	}
+
+	endTrailer = endTrailer[:endTrailerLen]
+	if !bytes.HasPrefix(endTrailer, typeLine) ||
+		!bytes.HasSuffix(endTrailer, pemEndOfLine) {
+		return decodeError(data, rest)
+	}
+
 	base64Data := removeWhitespace(rest[:endIndex])
 	p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
 	n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
diff --git a/src/encoding/pem/pem_test.go b/src/encoding/pem/pem_test.go
index 958dbc1..6321dec 100644
--- a/src/encoding/pem/pem_test.go
+++ b/src/encoding/pem/pem_test.go
@@ -78,6 +78,48 @@ func TestDecode(t *testing.T) {
 	}
 }
 
+const pemTooFewEndingDashes = `
+-----BEGIN FOO-----
+dGVzdA==
+-----END FOO----`
+
+const pemWrongEndingType = `
+-----BEGIN FOO-----
+dGVzdA==
+-----END BAR-----`
+
+const pemMissingEndingSpace = `
+-----BEGIN FOO-----
+dGVzdA==
+-----ENDBAR-----`
+
+var badPEMTests = []struct {
+	name  string
+	input string
+}{
+	{
+		"too few trailing dashes",
+		pemTooFewEndingDashes,
+	},
+	{
+		"incorrect ending type",
+		pemWrongEndingType,
+	},
+	{
+		"missing ending space",
+		pemMissingEndingSpace,
+	},
+}
+
+func TestBadDecode(t *testing.T) {
+	for _, test := range badPEMTests {
+		result, _ := Decode([]byte(test.input))
+		if result != nil {
+			t.Errorf("unexpected success while parsing %q", test.name)
+		}
+	}
+}
+
 func TestEncode(t *testing.T) {
 	r := EncodeToMemory(privateKey2)
 	if string(r) != pemPrivateKey2 {
diff --git a/src/encoding/xml/marshal.go b/src/encoding/xml/marshal.go
index abb078c..1176f5d 100644
--- a/src/encoding/xml/marshal.go
+++ b/src/encoding/xml/marshal.go
@@ -24,10 +24,10 @@ const (
 
 // Marshal returns the XML encoding of v.
 //
-// Marshal handles an array or slice by marshalling each of the elements.
-// Marshal handles a pointer by marshalling the value it points at or, if the
+// Marshal handles an array or slice by marshaling each of the elements.
+// Marshal handles a pointer by marshaling the value it points at or, if the
 // pointer is nil, by writing nothing. Marshal handles an interface value by
-// marshalling the value it contains or, if the interface value is nil, by
+// marshaling the value it contains or, if the interface value is nil, by
 // writing nothing. Marshal handles all other data by writing one or more XML
 // elements containing the data.
 //
@@ -36,9 +36,9 @@ const (
 //     - the value of the XMLName field of type Name
 //     - the tag of the struct field used to obtain the data
 //     - the name of the struct field used to obtain the data
-//     - the name of the marshalled type
+//     - the name of the marshaled type
 //
-// The XML element for a struct contains marshalled elements for each of the
+// The XML element for a struct contains marshaled elements for each of the
 // exported fields of the struct, with these exceptions:
 //     - the XMLName field, described above, is omitted.
 //     - a field with tag "-" is omitted.
@@ -51,9 +51,9 @@ const (
 //     - a field with tag ",cdata" is written as character data
 //       wrapped in one or more <![CDATA[ ... ]]> tags, not as an XML element.
 //     - a field with tag ",innerxml" is written verbatim, not subject
-//       to the usual marshalling procedure.
+//       to the usual marshaling procedure.
 //     - a field with tag ",comment" is written as an XML comment, not
-//       subject to the usual marshalling procedure. It must not contain
+//       subject to the usual marshaling procedure. It must not contain
 //       the "--" string within it.
 //     - a field with a tag including the "omitempty" option is omitted
 //       if the field value is empty. The empty values are false, 0, any
@@ -494,7 +494,6 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
 			continue
 		}
 		fv := finfo.value(val)
-		name := Name{Space: finfo.xmlns, Local: finfo.name}
 
 		if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
 			continue
@@ -504,69 +503,10 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
 			continue
 		}
 
-		if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
-			attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
-			if err != nil {
-				return err
-			}
-			if attr.Name.Local != "" {
-				start.Attr = append(start.Attr, attr)
-			}
-			continue
-		}
-
-		if fv.CanAddr() {
-			pv := fv.Addr()
-			if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
-				attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
-				if err != nil {
-					return err
-				}
-				if attr.Name.Local != "" {
-					start.Attr = append(start.Attr, attr)
-				}
-				continue
-			}
-		}
-
-		if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
-			text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
-			if err != nil {
-				return err
-			}
-			start.Attr = append(start.Attr, Attr{name, string(text)})
-			continue
-		}
-
-		if fv.CanAddr() {
-			pv := fv.Addr()
-			if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
-				text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
-				if err != nil {
-					return err
-				}
-				start.Attr = append(start.Attr, Attr{name, string(text)})
-				continue
-			}
-		}
-
-		// Dereference or skip nil pointer, interface values.
-		switch fv.Kind() {
-		case reflect.Ptr, reflect.Interface:
-			if fv.IsNil() {
-				continue
-			}
-			fv = fv.Elem()
-		}
-
-		s, b, err := p.marshalSimple(fv.Type(), fv)
-		if err != nil {
+		name := Name{Space: finfo.xmlns, Local: finfo.name}
+		if err := p.marshalAttr(&start, name, fv); err != nil {
 			return err
 		}
-		if b != nil {
-			s = string(b)
-		}
-		start.Attr = append(start.Attr, Attr{name, s})
 	}
 
 	if err := p.writeStart(&start); err != nil {
@@ -596,6 +536,90 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
 	return p.cachedWriteError()
 }
 
+// marshalAttr marshals an attribute with the given name and value, adding to start.Attr.
+func (p *printer) marshalAttr(start *StartElement, name Name, val reflect.Value) error {
+	if val.CanInterface() && val.Type().Implements(marshalerAttrType) {
+		attr, err := val.Interface().(MarshalerAttr).MarshalXMLAttr(name)
+		if err != nil {
+			return err
+		}
+		if attr.Name.Local != "" {
+			start.Attr = append(start.Attr, attr)
+		}
+		return nil
+	}
+
+	if val.CanAddr() {
+		pv := val.Addr()
+		if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
+			attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
+			if err != nil {
+				return err
+			}
+			if attr.Name.Local != "" {
+				start.Attr = append(start.Attr, attr)
+			}
+			return nil
+		}
+	}
+
+	if val.CanInterface() && val.Type().Implements(textMarshalerType) {
+		text, err := val.Interface().(encoding.TextMarshaler).MarshalText()
+		if err != nil {
+			return err
+		}
+		start.Attr = append(start.Attr, Attr{name, string(text)})
+		return nil
+	}
+
+	if val.CanAddr() {
+		pv := val.Addr()
+		if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+			text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
+			if err != nil {
+				return err
+			}
+			start.Attr = append(start.Attr, Attr{name, string(text)})
+			return nil
+		}
+	}
+
+	// Dereference or skip nil pointer, interface values.
+	switch val.Kind() {
+	case reflect.Ptr, reflect.Interface:
+		if val.IsNil() {
+			return nil
+		}
+		val = val.Elem()
+	}
+
+	// Walk slices.
+	if val.Kind() == reflect.Slice && val.Type().Elem().Kind() != reflect.Uint8 {
+		n := val.Len()
+		for i := 0; i < n; i++ {
+			if err := p.marshalAttr(start, name, val.Index(i)); err != nil {
+				return err
+			}
+		}
+		return nil
+	}
+
+	if val.Type() == attrType {
+		start.Attr = append(start.Attr, val.Interface().(Attr))
+		return nil
+	}
+
+	s, b, err := p.marshalSimple(val.Type(), val)
+	if err != nil {
+		return err
+	}
+	if b != nil {
+		s = string(b)
+	}
+	start.Attr = append(start.Attr, Attr{name, s})
+	return nil
+}
+
 // defaultStart returns the default start element to use,
 // given the reflect type, field info, and start template.
 func defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement {
@@ -760,14 +784,6 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
 		}
 		vf := finfo.value(val)
 
-		// Dereference or skip nil pointer, interface values.
-		switch vf.Kind() {
-		case reflect.Ptr, reflect.Interface:
-			if !vf.IsNil() {
-				vf = vf.Elem()
-			}
-		}
-
 		switch finfo.flags & fMode {
 		case fCDATA, fCharData:
 			emit := EscapeText
@@ -800,6 +816,16 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
 					continue
 				}
 			}
+			// Drill into interfaces and pointers.
+			// This can turn into an infinite loop given a cyclic chain,
+			// but it matches the Go 1 behavior.
+			for vf.Kind() == reflect.Interface || vf.Kind() == reflect.Ptr {
+				if vf.IsNil() {
+					return nil
+				}
+				vf = vf.Elem()
+			}
+
 			var scratch [64]byte
 			switch vf.Kind() {
 			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
diff --git a/src/encoding/xml/marshal_test.go b/src/encoding/xml/marshal_test.go
index fe8b16f..d79b99a 100644
--- a/src/encoding/xml/marshal_test.go
+++ b/src/encoding/xml/marshal_test.go
@@ -199,6 +199,17 @@ type AttrTest struct {
 	Bytes []byte  `xml:",attr"`
 }
 
+type AttrsTest struct {
+	Attrs []Attr  `xml:",any,attr"`
+	Int   int     `xml:",attr"`
+	Named int     `xml:"int,attr"`
+	Float float64 `xml:",attr"`
+	Uint8 uint8   `xml:",attr"`
+	Bool  bool    `xml:",attr"`
+	Str   string  `xml:",attr"`
+	Bytes []byte  `xml:",attr"`
+}
+
 type OmitAttrTest struct {
 	Int   int     `xml:",attr,omitempty"`
 	Named int     `xml:"int,attr,omitempty"`
@@ -207,6 +218,7 @@ type OmitAttrTest struct {
 	Bool  bool    `xml:",attr,omitempty"`
 	Str   string  `xml:",attr,omitempty"`
 	Bytes []byte  `xml:",attr,omitempty"`
+	PStr  *string `xml:",attr,omitempty"`
 }
 
 type OmitFieldTest struct {
@@ -217,6 +229,7 @@ type OmitFieldTest struct {
 	Bool  bool          `xml:",omitempty"`
 	Str   string        `xml:",omitempty"`
 	Bytes []byte        `xml:",omitempty"`
+	PStr  *string       `xml:",omitempty"`
 	Ptr   *PresenceTest `xml:",omitempty"`
 }
 
@@ -317,6 +330,10 @@ func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
 	return Attr{name, "hello world"}, nil
 }
 
+func (m *MyMarshalerAttrTest) UnmarshalXMLAttr(attr Attr) error {
+	return nil
+}
+
 type MarshalerStruct struct {
 	Foo MyMarshalerAttrTest `xml:",attr"`
 }
@@ -373,12 +390,13 @@ var (
 	nameAttr     = "Sarah"
 	ageAttr      = uint(12)
 	contentsAttr = "lorem ipsum"
+	empty        = ""
 )
 
 // Unless explicitly stated as such (or *Plain), all of the
 // tests below are two-way tests. When introducing new tests,
 // please try to make them two-way as well to ensure that
-// marshalling and unmarshalling are as symmetrical as feasible.
+// marshaling and unmarshaling are as symmetrical as feasible.
 var marshalTests = []struct {
 	Value         interface{}
 	ExpectXML     string
@@ -823,6 +841,53 @@ var marshalTests = []struct {
 			` Bool="false" Str="" Bytes=""></AttrTest>`,
 	},
 	{
+		Value: &AttrsTest{
+			Attrs: []Attr{
+				{Name: Name{Local: "Answer"}, Value: "42"},
+				{Name: Name{Local: "Int"}, Value: "8"},
+				{Name: Name{Local: "int"}, Value: "9"},
+				{Name: Name{Local: "Float"}, Value: "23.5"},
+				{Name: Name{Local: "Uint8"}, Value: "255"},
+				{Name: Name{Local: "Bool"}, Value: "true"},
+				{Name: Name{Local: "Str"}, Value: "str"},
+				{Name: Name{Local: "Bytes"}, Value: "byt"},
+			},
+		},
+		ExpectXML:   `<AttrsTest Answer="42" Int="8" int="9" Float="23.5" Uint8="255" Bool="true" Str="str" Bytes="byt" Int="0" int="0" Float="0" Uint8="0" Bool="false" Str="" Bytes=""></AttrsTest>`,
+		MarshalOnly: true,
+	},
+	{
+		Value: &AttrsTest{
+			Attrs: []Attr{
+				{Name: Name{Local: "Answer"}, Value: "42"},
+			},
+			Int:   8,
+			Named: 9,
+			Float: 23.5,
+			Uint8: 255,
+			Bool:  true,
+			Str:   "str",
+			Bytes: []byte("byt"),
+		},
+		ExpectXML: `<AttrsTest Answer="42" Int="8" int="9" Float="23.5" Uint8="255" Bool="true" Str="str" Bytes="byt"></AttrsTest>`,
+	},
+	{
+		Value: &AttrsTest{
+			Attrs: []Attr{
+				{Name: Name{Local: "Int"}, Value: "0"},
+				{Name: Name{Local: "int"}, Value: "0"},
+				{Name: Name{Local: "Float"}, Value: "0"},
+				{Name: Name{Local: "Uint8"}, Value: "0"},
+				{Name: Name{Local: "Bool"}, Value: "false"},
+				{Name: Name{Local: "Str"}},
+				{Name: Name{Local: "Bytes"}},
+			},
+			Bytes: []byte{},
+		},
+		ExpectXML:   `<AttrsTest Int="0" int="0" Float="0" Uint8="0" Bool="false" Str="" Bytes="" Int="0" int="0" Float="0" Uint8="0" Bool="false" Str="" Bytes=""></AttrsTest>`,
+		MarshalOnly: true,
+	},
+	{
 		Value: &OmitAttrTest{
 			Int:   8,
 			Named: 9,
@@ -831,9 +896,10 @@ var marshalTests = []struct {
 			Bool:  true,
 			Str:   "str",
 			Bytes: []byte("byt"),
+			PStr:  &empty,
 		},
 		ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
-			` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
+			` Bool="true" Str="str" Bytes="byt" PStr=""></OmitAttrTest>`,
 	},
 	{
 		Value:     &OmitAttrTest{},
@@ -864,6 +930,7 @@ var marshalTests = []struct {
 			Bool:  true,
 			Str:   "str",
 			Bytes: []byte("byt"),
+			PStr:  &empty,
 			Ptr:   &PresenceTest{},
 		},
 		ExpectXML: `<OmitFieldTest>` +
@@ -874,6 +941,7 @@ var marshalTests = []struct {
 			`<Bool>true</Bool>` +
 			`<Str>str</Str>` +
 			`<Bytes>byt</Bytes>` +
+			`<PStr></PStr>` +
 			`<Ptr></Ptr>` +
 			`</OmitFieldTest>`,
 	},
@@ -1092,7 +1160,7 @@ type AttrParent struct {
 }
 
 type BadAttr struct {
-	Name []string `xml:"name,attr"`
+	Name map[string]string `xml:"name,attr"`
 }
 
 var marshalErrorTests = []struct {
@@ -1128,8 +1196,8 @@ var marshalErrorTests = []struct {
 		Err:   `xml: X>Y chain not valid with attr flag`,
 	},
 	{
-		Value: BadAttr{[]string{"X", "Y"}},
-		Err:   `xml: unsupported type: []string`,
+		Value: BadAttr{map[string]string{"X": "Y"}},
+		Err:   `xml: unsupported type: map[string]string`,
 	},
 }
 
@@ -1732,7 +1800,7 @@ func TestDecodeEncode(t *testing.T) {
 	in.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
 <?Target Instruction?>
 <root>
-</root>	
+</root>
 `)
 	dec := NewDecoder(&in)
 	enc := NewEncoder(&out)
@@ -1823,3 +1891,14 @@ func TestSimpleUseOfEncodeToken(t *testing.T) {
 		t.Errorf("enc.EncodeToken: expected %q; got %q", want, buf.String())
 	}
 }
+
+// Issue 16158. Decoder.unmarshalAttr ignores the return value of copyValue.
+func TestIssue16158(t *testing.T) {
+	const data = `<foo b="HELLOWORLD"></foo>`
+	err := Unmarshal([]byte(data), &struct {
+		B byte `xml:"b,attr,omitempty"`
+	}{})
+	if err == nil {
+		t.Errorf("Unmarshal: expected error, got nil")
+	}
+}
diff --git a/src/encoding/xml/read.go b/src/encoding/xml/read.go
index 937432e..5a89d5f 100644
--- a/src/encoding/xml/read.go
+++ b/src/encoding/xml/read.go
@@ -52,6 +52,11 @@ import (
 //      the explicit name in a struct field tag of the form "name,attr",
 //      Unmarshal records the attribute value in that field.
 //
+//   * If the XML element has an attribute not handled by the previous
+//      rule and the struct has a field with an associated tag containing
+//      ",any,attr", Unmarshal records the attribute value in the first
+//      such field.
+//
 //   * If the XML element contains character data, that data is
 //      accumulated in the first struct field that has tag ",chardata".
 //      The struct field may have type []byte or string.
@@ -85,7 +90,7 @@ import (
 //   * An anonymous struct field is handled as if the fields of its
 //      value were part of the outer struct.
 //
-//   * A struct field with tag "-" is never unmarshalled into.
+//   * A struct field with tag "-" is never unmarshaled into.
 //
 // Unmarshal maps an XML element to a string or []byte by saving the
 // concatenation of that element's character data in the string or
@@ -94,8 +99,12 @@ import (
 // Unmarshal maps an attribute value to a string or []byte by saving
 // the value in the string or slice.
 //
-// Unmarshal maps an XML element to a slice by extending the length of
-// the slice and mapping the element to the newly created value.
+// Unmarshal maps an attribute value to an Attr by saving the attribute,
+// including its name, in the Attr.
+//
+// Unmarshal maps an XML element or attribute value to a slice by
+// extending the length of the slice and mapping the element or attribute
+// to the newly created value.
 //
 // Unmarshal maps an XML element or attribute value to a bool by
 // setting it to the boolean value represented by the string.
@@ -133,7 +142,7 @@ func (d *Decoder) DecodeElement(v interface{}, start *StartElement) error {
 	return d.unmarshal(val.Elem(), start)
 }
 
-// An UnmarshalError represents an error in the unmarshalling process.
+// An UnmarshalError represents an error in the unmarshaling process.
 type UnmarshalError string
 
 func (e UnmarshalError) Error() string { return string(e) }
@@ -232,7 +241,6 @@ func (p *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error {
 		}
 		val = val.Elem()
 	}
-
 	if val.CanInterface() && val.Type().Implements(unmarshalerAttrType) {
 		// This is an unmarshaler with a non-pointer receiver,
 		// so it's likely to be incorrect, but we do what we're told.
@@ -258,11 +266,30 @@ func (p *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error {
 		}
 	}
 
-	copyValue(val, []byte(attr.Value))
-	return nil
+	if val.Type().Kind() == reflect.Slice && val.Type().Elem().Kind() != reflect.Uint8 {
+		// Slice of element values.
+		// Grow slice.
+		n := val.Len()
+		val.Set(reflect.Append(val, reflect.Zero(val.Type().Elem())))
+
+		// Recur to read element into slice.
+		if err := p.unmarshalAttr(val.Index(n), attr); err != nil {
+			val.SetLen(n)
+			return err
+		}
+		return nil
+	}
+
+	if val.Type() == attrType {
+		val.Set(reflect.ValueOf(attr))
+		return nil
+	}
+
+	return copyValue(val, []byte(attr.Value))
 }
 
 var (
+	attrType            = reflect.TypeOf(Attr{})
 	unmarshalerType     = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
 	unmarshalerAttrType = reflect.TypeOf((*UnmarshalerAttr)(nil)).Elem()
 	textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
@@ -359,16 +386,7 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
 		// Slice of element values.
 		// Grow slice.
 		n := v.Len()
-		if n >= v.Cap() {
-			ncap := 2 * n
-			if ncap < 4 {
-				ncap = 4
-			}
-			new := reflect.MakeSlice(typ, n, ncap)
-			reflect.Copy(new, v)
-			v.Set(new)
-		}
-		v.SetLen(n + 1)
+		v.Set(reflect.Append(val, reflect.Zero(v.Type().Elem())))
 
 		// Recur to read element into slice.
 		if err := p.unmarshal(v.Index(n), start); err != nil {
@@ -415,22 +433,40 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
 		}
 
 		// Assign attributes.
-		// Also, determine whether we need to save character data or comments.
-		for i := range tinfo.fields {
-			finfo := &tinfo.fields[i]
-			switch finfo.flags & fMode {
-			case fAttr:
-				strv := finfo.value(sv)
-				// Look for attribute.
-				for _, a := range start.Attr {
+		for _, a := range start.Attr {
+			handled := false
+			any := -1
+			for i := range tinfo.fields {
+				finfo := &tinfo.fields[i]
+				switch finfo.flags & fMode {
+				case fAttr:
+					strv := finfo.value(sv)
 					if a.Name.Local == finfo.name && (finfo.xmlns == "" || finfo.xmlns == a.Name.Space) {
 						if err := p.unmarshalAttr(strv, a); err != nil {
 							return err
 						}
-						break
+						handled = true
+					}
+
+				case fAny | fAttr:
+					if any == -1 {
+						any = i
 					}
 				}
+			}
+			if !handled && any >= 0 {
+				finfo := &tinfo.fields[any]
+				strv := finfo.value(sv)
+				if err := p.unmarshalAttr(strv, a); err != nil {
+					return err
+				}
+			}
+		}
 
+		// Determine whether we need to save character data or comments.
+		for i := range tinfo.fields {
+			finfo := &tinfo.fields[i]
+			switch finfo.flags & fMode {
 			case fCDATA, fCharData:
 				if !saveData.IsValid() {
 					saveData = finfo.value(sv)
@@ -546,7 +582,9 @@ Loop:
 	case reflect.String:
 		t.SetString(string(saveXMLData))
 	case reflect.Slice:
-		t.Set(reflect.ValueOf(saveXMLData))
+		if t.Type().Elem().Kind() == reflect.Uint8 {
+			t.Set(reflect.ValueOf(saveXMLData))
+		}
 	}
 
 	return nil
diff --git a/src/encoding/xml/read_test.go b/src/encoding/xml/read_test.go
index 7a98092..273c303 100644
--- a/src/encoding/xml/read_test.go
+++ b/src/encoding/xml/read_test.go
@@ -705,7 +705,7 @@ func TestUnmarshalIntoInterface(t *testing.T) {
 	}
 	pea, ok := pod.Pea.(*Pea)
 	if !ok {
-		t.Fatalf("unmarshalled into wrong type: have %T want *Pea", pod.Pea)
+		t.Fatalf("unmarshaled into wrong type: have %T want *Pea", pod.Pea)
 	}
 	have, want := pea.Cotelydon, "Green stuff"
 	if have != want {
@@ -733,3 +733,22 @@ func TestMalformedComment(t *testing.T) {
 		}
 	}
 }
+
+type IXField struct {
+	Five        int      `xml:"five"`
+	NotInnerXML []string `xml:",innerxml"`
+}
+
+// Issue 15600. ",innerxml" on a field that can't hold it.
+func TestInvalidInnerXMLType(t *testing.T) {
+	v := new(IXField)
+	if err := Unmarshal([]byte(`<tag><five>5</five><innertag/></tag>`), v); err != nil {
+		t.Errorf("Unmarshal failed: got %v", err)
+	}
+	if v.Five != 5 {
+		t.Errorf("Five = %v, want 5", v.Five)
+	}
+	if v.NotInnerXML != nil {
+		t.Errorf("NotInnerXML = %v, want nil", v.NotInnerXML)
+	}
+}
diff --git a/src/encoding/xml/typeinfo.go b/src/encoding/xml/typeinfo.go
index 70da962..6623c78 100644
--- a/src/encoding/xml/typeinfo.go
+++ b/src/encoding/xml/typeinfo.go
@@ -48,7 +48,7 @@ var tinfoLock sync.RWMutex
 var nameType = reflect.TypeOf(Name{})
 
 // getTypeInfo returns the typeInfo structure with details necessary
-// for marshalling and unmarshalling typ.
+// for marshaling and unmarshaling typ.
 func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
 	tinfoLock.RLock()
 	tinfo, ok := tinfoMap[typ]
@@ -151,7 +151,7 @@ func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, erro
 		switch mode := finfo.flags & fMode; mode {
 		case 0:
 			finfo.flags |= fElement
-		case fAttr, fCDATA, fCharData, fInnerXml, fComment, fAny:
+		case fAttr, fCDATA, fCharData, fInnerXml, fComment, fAny, fAny | fAttr:
 			if f.Name == "XMLName" || tag != "" && mode != fAttr {
 				valid = false
 			}
@@ -214,7 +214,7 @@ func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, erro
 	}
 
 	// If the field type has an XMLName field, the names must match
-	// so that the behavior of both marshalling and unmarshalling
+	// so that the behavior of both marshaling and unmarshaling
 	// is straightforward and unambiguous.
 	if finfo.flags&fElement != 0 {
 		ftyp := f.Type
@@ -334,7 +334,7 @@ Loop:
 	return nil
 }
 
-// A TagPathError represents an error in the unmarshalling process
+// A TagPathError represents an error in the unmarshaling process
 // caused by the use of field tags with conflicting paths.
 type TagPathError struct {
 	Struct       reflect.Type
diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go
index d5465c5..7339fa0 100644
--- a/src/expvar/expvar.go
+++ b/src/expvar/expvar.go
@@ -49,6 +49,10 @@ type Int struct {
 	i int64
 }
 
+func (v *Int) Value() int64 {
+	return atomic.LoadInt64(&v.i)
+}
+
 func (v *Int) String() string {
 	return strconv.FormatInt(atomic.LoadInt64(&v.i), 10)
 }
@@ -66,6 +70,10 @@ type Float struct {
 	f uint64
 }
 
+func (v *Float) Value() float64 {
+	return math.Float64frombits(atomic.LoadUint64(&v.f))
+}
+
 func (v *Float) String() string {
 	return strconv.FormatFloat(
 		math.Float64frombits(atomic.LoadUint64(&v.f)), 'g', -1, 64)
@@ -219,6 +227,14 @@ type String struct {
 	s  string
 }
 
+func (v *String) Value() string {
+	v.mu.RLock()
+	defer v.mu.RUnlock()
+	return v.s
+}
+
+// String implements the Val interface. To get the unquoted string
+// use Value.
 func (v *String) String() string {
 	v.mu.RLock()
 	s := v.s
@@ -237,6 +253,10 @@ func (v *String) Set(value string) {
 // and formatting the returned value using JSON.
 type Func func() interface{}
 
+func (f Func) Value() interface{} {
+	return f()
+}
+
 func (f Func) String() string {
 	v, _ := json.Marshal(f())
 	return string(v)
@@ -322,6 +342,13 @@ func expvarHandler(w http.ResponseWriter, r *http.Request) {
 	fmt.Fprintf(w, "\n}\n")
 }
 
+// Handler returns the expvar HTTP Handler.
+//
+// This is only needed to install the handler in a non-standard location.
+func Handler() http.Handler {
+	return http.HandlerFunc(expvarHandler)
+}
+
 func cmdline() interface{} {
 	return os.Args
 }
diff --git a/src/expvar/expvar_test.go b/src/expvar/expvar_test.go
index 7b1c9df..0efa864 100644
--- a/src/expvar/expvar_test.go
+++ b/src/expvar/expvar_test.go
@@ -7,13 +7,12 @@ package expvar
 import (
 	"bytes"
 	"encoding/json"
-	"math"
 	"net"
 	"net/http/httptest"
+	"reflect"
 	"runtime"
 	"strconv"
 	"sync"
-	"sync/atomic"
 	"testing"
 )
 
@@ -58,6 +57,10 @@ func TestInt(t *testing.T) {
 	if reqs.i != -2 {
 		t.Errorf("reqs.i = %v, want -2", reqs.i)
 	}
+
+	if v, want := reqs.Value(), int64(-2); v != want {
+		t.Errorf("reqs.Value() = %q, want %q", v, want)
+	}
 }
 
 func BenchmarkIntAdd(b *testing.B) {
@@ -80,10 +83,6 @@ func BenchmarkIntSet(b *testing.B) {
 	})
 }
 
-func (v *Float) val() float64 {
-	return math.Float64frombits(atomic.LoadUint64(&v.f))
-}
-
 func TestFloat(t *testing.T) {
 	RemoveAll()
 	reqs := NewFloat("requests-float")
@@ -96,8 +95,8 @@ func TestFloat(t *testing.T) {
 
 	reqs.Add(1.5)
 	reqs.Add(1.25)
-	if v := reqs.val(); v != 2.75 {
-		t.Errorf("reqs.val() = %v, want 2.75", v)
+	if v := reqs.Value(); v != 2.75 {
+		t.Errorf("reqs.Value() = %v, want 2.75", v)
 	}
 
 	if s := reqs.String(); s != "2.75" {
@@ -105,8 +104,8 @@ func TestFloat(t *testing.T) {
 	}
 
 	reqs.Add(-2)
-	if v := reqs.val(); v != 0.75 {
-		t.Errorf("reqs.val() = %v, want 0.75", v)
+	if v := reqs.Value(); v != 0.75 {
+		t.Errorf("reqs.Value() = %v, want 0.75", v)
 	}
 }
 
@@ -146,6 +145,10 @@ func TestString(t *testing.T) {
 		t.Errorf("from %q, name.String() = %q, want %q", name.s, s, want)
 	}
 
+	if s, want := name.Value(), "Mike"; s != want {
+		t.Errorf("from %q, name.Value() = %q, want %q", name.s, s, want)
+	}
+
 	// Make sure we produce safe JSON output.
 	name.Set(`<`)
 	if s, want := name.String(), "\"\\u003c\""; s != want {
@@ -177,7 +180,7 @@ func TestMapCounter(t *testing.T) {
 	if x := colors.m["blue"].(*Int).i; x != 4 {
 		t.Errorf("colors.m[\"blue\"] = %v, want 4", x)
 	}
-	if x := colors.m[`green "midori"`].(*Float).val(); x != 4.125 {
+	if x := colors.m[`green "midori"`].(*Float).Value(); x != 4.125 {
 		t.Errorf("colors.m[`green \"midori\"] = %v, want 4.125", x)
 	}
 
@@ -242,6 +245,9 @@ func TestFunc(t *testing.T) {
 	if s, exp := f.String(), `["a","b"]`; s != exp {
 		t.Errorf(`f.String() = %q, want %q`, s, exp)
 	}
+	if v := f.Value(); !reflect.DeepEqual(v, x) {
+		t.Errorf(`f.Value() = %q, want %q`, v, x)
+	}
 
 	x = 17
 	if s, exp := f.String(), `17`; s != exp {
diff --git a/src/flag/export_test.go b/src/flag/export_test.go
index 12d3dc7..edbe83c 100644
--- a/src/flag/export_test.go
+++ b/src/flag/export_test.go
@@ -13,5 +13,6 @@ import "os"
 // exit the program.
 func ResetForTesting(usage func()) {
 	CommandLine = NewFlagSet(os.Args[0], ContinueOnError)
+	CommandLine.Usage = commandLineUsage
 	Usage = usage
 }
diff --git a/src/flag/flag.go b/src/flag/flag.go
index fa0f05e..bbbc55a 100644
--- a/src/flag/flag.go
+++ b/src/flag/flag.go
@@ -94,7 +94,7 @@ func (b *boolValue) Set(s string) error {
 
 func (b *boolValue) Get() interface{} { return bool(*b) }
 
-func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) }
+func (b *boolValue) String() string { return strconv.FormatBool(bool(*b)) }
 
 func (b *boolValue) IsBoolFlag() bool { return true }
 
@@ -121,7 +121,7 @@ func (i *intValue) Set(s string) error {
 
 func (i *intValue) Get() interface{} { return int(*i) }
 
-func (i *intValue) String() string { return fmt.Sprintf("%v", *i) }
+func (i *intValue) String() string { return strconv.Itoa(int(*i)) }
 
 // -- int64 Value
 type int64Value int64
@@ -139,7 +139,7 @@ func (i *int64Value) Set(s string) error {
 
 func (i *int64Value) Get() interface{} { return int64(*i) }
 
-func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) }
+func (i *int64Value) String() string { return strconv.FormatInt(int64(*i), 10) }
 
 // -- uint Value
 type uintValue uint
@@ -157,7 +157,7 @@ func (i *uintValue) Set(s string) error {
 
 func (i *uintValue) Get() interface{} { return uint(*i) }
 
-func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) }
+func (i *uintValue) String() string { return strconv.FormatUint(uint64(*i), 10) }
 
 // -- uint64 Value
 type uint64Value uint64
@@ -175,7 +175,7 @@ func (i *uint64Value) Set(s string) error {
 
 func (i *uint64Value) Get() interface{} { return uint64(*i) }
 
-func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) }
+func (i *uint64Value) String() string { return strconv.FormatUint(uint64(*i), 10) }
 
 // -- string Value
 type stringValue string
@@ -192,7 +192,7 @@ func (s *stringValue) Set(val string) error {
 
 func (s *stringValue) Get() interface{} { return string(*s) }
 
-func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) }
+func (s *stringValue) String() string { return string(*s) }
 
 // -- float64 Value
 type float64Value float64
@@ -210,7 +210,7 @@ func (f *float64Value) Set(s string) error {
 
 func (f *float64Value) Get() interface{} { return float64(*f) }
 
-func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
+func (f *float64Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 64) }
 
 // -- time.Duration Value
 type durationValue time.Duration
@@ -238,6 +238,8 @@ func (d *durationValue) String() string { return (*time.Duration)(d).String() }
 // rather than using the next command-line argument.
 //
 // Set is called once, in command line order, for each flag present.
+// The flag package may call the String method with a zero-valued receiver,
+// such as a nil pointer.
 type Value interface {
 	String() string
 	Set(string) error
@@ -500,7 +502,7 @@ func PrintDefaults() {
 }
 
 // defaultUsage is the default function to print a usage message.
-func defaultUsage(f *FlagSet) {
+func (f *FlagSet) defaultUsage() {
 	if f.name == "" {
 		fmt.Fprintf(f.out(), "Usage:\n")
 	} else {
@@ -819,11 +821,7 @@ func (f *FlagSet) failf(format string, a ...interface{}) error {
 // or the appropriate default usage function otherwise.
 func (f *FlagSet) usage() {
 	if f.Usage == nil {
-		if f == CommandLine {
-			Usage()
-		} else {
-			defaultUsage(f)
-		}
+		f.defaultUsage()
 	} else {
 		f.Usage()
 	}
@@ -953,6 +951,18 @@ func Parsed() bool {
 // methods of CommandLine.
 var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
 
+func init() {
+	// Override generic FlagSet default Usage with call to global Usage.
+	// Note: This is not CommandLine.Usage = Usage,
+	// because we want any eventual call to use any updated value of Usage,
+	// not the value it has when this line is run.
+	CommandLine.Usage = commandLineUsage
+}
+
+func commandLineUsage() {
+	Usage()
+}
+
 // NewFlagSet returns a new, empty flag set with the specified name and
 // error handling property.
 func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
@@ -960,6 +970,7 @@ func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
 		name:          name,
 		errorHandling: errorHandling,
 	}
+	f.Usage = f.defaultUsage
 	return f
 }
 
diff --git a/src/fmt/doc.go b/src/fmt/doc.go
index c312914..a2faecb 100644
--- a/src/fmt/doc.go
+++ b/src/fmt/doc.go
@@ -48,13 +48,10 @@
 	Pointer:
 		%p	base 16 notation, with leading 0x
 
-	There is no 'u' flag.  Integers are printed unsigned if they have unsigned type.
-	Similarly, there is no need to specify the size of the operand (int8, int64).
-
 	The default format for %v is:
 		bool:                    %t
 		int, int8 etc.:          %d
-		uint, uint8 etc.:        %d, %x if printed with %#v
+		uint, uint8 etc.:        %d, %#x if printed with %#v
 		float32, complex64, etc: %g
 		string:                  %s
 		chan:                    %p
@@ -177,6 +174,9 @@
 	that type has a String method. Such pathologies are rare, however,
 	and the package does not protect against them.
 
+	When printing a struct, fmt cannot and therefore does not invoke
+	formatting methods such as Error or String on unexported fields.
+
 	Explicit argument indexes:
 
 	In Printf, Sprintf, and Fprintf, the default behavior is for each
@@ -247,31 +247,42 @@
 	Scanln, Fscanln and Sscanln stop scanning at a newline and
 	require that the items be followed by a newline or EOF.
 
-	Scanf, Fscanf and Sscanf require that (after skipping spaces)
-	newlines in the format are matched by newlines in the input
-	and vice versa.  This behavior differs from the corresponding
-	routines in C, which uniformly treat newlines as spaces.
-
-	When scanning with Scanf, Fscanf, and Sscanf, all non-empty
-	runs of space characters (except newline) are equivalent
-	to a single space in both the format and the input.  With
-	that proviso, text in the format string must match the input
-	text; scanning stops if it does not, with the return value
-	of the function indicating the number of arguments scanned.
-
 	Scanf, Fscanf, and Sscanf parse the arguments according to a
-	format string, analogous to that of Printf.  For example, %x
-	will scan an integer as a hexadecimal number, and %v will scan
-	the default representation format for the value.
-
-	The formats behave analogously to those of Printf with the
-	following exceptions:
-
-		%p is not implemented
-		%T is not implemented
-		%e %E %f %F %g %G are all equivalent and scan any floating point or complex value
-		%s and %v on strings scan a space-delimited token
-		Flags # and + are not implemented.
+	format string, analogous to that of Printf. In the text that
+	follows, 'space' means any Unicode whitespace character
+	except newline.
+
+	In the format string, a verb introduced by the % character
+	consumes and parses input; these verbs are described in more
+	detail below. A character other than %, space, or newline in
+	the format consumes exactly that input character, which must
+	be present. A newline with zero or more spaces before it in
+	the format string consumes zero or more spaces in the input
+	followed by a single newline or the end of the input. A space
+	following a newline in the format string consumes zero or more
+	spaces in the input. Otherwise, any run of one or more spaces
+	in the format string consumes as many spaces as possible in
+	the input. Unless the run of spaces in the format string
+	appears adjacent to a newline, the run must consume at least
+	one space from the input or find the end of the input.
+
+	The handling of spaces and newlines differs from that of C's
+	scanf family: in C, newlines are treated as any other space,
+	and it is never an error when a run of spaces in the format
+	string finds no spaces to consume in the input.
+
+	The verbs behave analogously to those of Printf.
+	For example, %x will scan an integer as a hexadecimal number,
+	and %v will scan the default representation format for the value.
+	The Printf verbs %p and %T and the flags # and + are not implemented,
+	and the verbs %e %E %f %F %g and %G are all equivalent and scan any
+	floating-point or complex value.
+
+	Input processed by verbs is implicitly space-delimited: the
+	implementation of every verb except %c starts by discarding
+	leading spaces from the remaining input, and the %s verb
+	(and %v reading into a string) stops consuming input at the first
+	space or newline character.
 
 	The familiar base-setting prefixes 0 (octal) and 0x
 	(hexadecimal) are accepted when scanning integers without
@@ -300,6 +311,9 @@
 	All arguments to be scanned must be either pointers to basic
 	types or implementations of the Scanner interface.
 
+	Like Scanf and Fscanf, Sscanf need not consume its entire input.
+	There is no way to recover how much of the input string Sscanf used.
+
 	Note: Fscan etc. can read one character (rune) past the input
 	they return, which means that a loop calling a scan routine
 	may skip some of the input.  This is usually a problem only
diff --git a/src/fmt/export_test.go b/src/fmt/export_test.go
index 12d5a11..14163a2 100644
--- a/src/fmt/export_test.go
+++ b/src/fmt/export_test.go
@@ -5,3 +5,4 @@
 package fmt
 
 var IsSpace = isSpace
+var Parsenum = parsenum
diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go
index 5fb2a63..6f8c155 100644
--- a/src/fmt/fmt_test.go
+++ b/src/fmt/fmt_test.go
@@ -605,7 +605,10 @@ var fmtTests = []struct {
 	{"%x", I(23), `3c32333e`},
 	{"%#x", I(23), `0x3c32333e`},
 	{"%# x", I(23), `0x3c 0x32 0x33 0x3e`},
-	{"%d", I(23), `23`}, // Stringer applies only to string formats.
+	// Stringer applies only to string formats.
+	{"%d", I(23), `23`},
+	// Stringer applies to the extracted value.
+	{"%s", reflect.ValueOf(I(23)), `<23>`},
 
 	// go syntax
 	{"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`},
@@ -1737,3 +1740,26 @@ func TestFormatterFlags(t *testing.T) {
 		}
 	}
 }
+
+func TestParsenum(t *testing.T) {
+	testCases := []struct {
+		s          string
+		start, end int
+		num        int
+		isnum      bool
+		newi       int
+	}{
+		{"a123", 0, 4, 0, false, 0},
+		{"1234", 1, 1, 0, false, 1},
+		{"123a", 0, 4, 123, true, 3},
+		{"12a3", 0, 4, 12, true, 2},
+		{"1234", 0, 4, 1234, true, 4},
+		{"1a234", 1, 3, 0, false, 1},
+	}
+	for _, tt := range testCases {
+		num, isnum, newi := Parsenum(tt.s, tt.start, tt.end)
+		if num != tt.num || isnum != tt.isnum || newi != tt.newi {
+			t.Errorf("parsenum(%q, %d, %d) = %d, %v, %d, want %d, %v, %d", tt.s, tt.start, tt.end, num, isnum, newi, tt.num, tt.isnum, tt.newi)
+		}
+	}
+}
diff --git a/src/fmt/format.go b/src/fmt/format.go
index 0236475..f770483 100644
--- a/src/fmt/format.go
+++ b/src/fmt/format.go
@@ -46,7 +46,7 @@ type fmt struct {
 	wid  int // width
 	prec int // precision
 
-	// intbuf is large enought to store %b of an int64 with a sign and
+	// intbuf is large enough to store %b of an int64 with a sign and
 	// avoids padding at the end of the struct on 32 bit architectures.
 	intbuf [68]byte
 }
diff --git a/src/fmt/print.go b/src/fmt/print.go
index f8c7316..75301a2 100644
--- a/src/fmt/print.go
+++ b/src/fmt/print.go
@@ -659,6 +659,14 @@ func (p *pp) printArg(arg interface{}, verb rune) {
 	case []byte:
 		p.fmtBytes(f, verb, "[]byte")
 	case reflect.Value:
+		// Handle extractable values with special methods
+		// since printValue does not handle them at depth 0.
+		if f.IsValid() && f.CanInterface() {
+			p.arg = f.Interface()
+			if p.handleMethods(verb) {
+				return
+			}
+		}
 		p.printValue(f, verb, 0)
 	default:
 		// If the type is not simple, it might have methods.
diff --git a/src/fmt/scan.go b/src/fmt/scan.go
index fdf4197..cd7232c 100644
--- a/src/fmt/scan.go
+++ b/src/fmt/scan.go
@@ -1075,6 +1075,58 @@ func (s *ss) doScan(a []interface{}) (numProcessed int, err error) {
 func (s *ss) advance(format string) (i int) {
 	for i < len(format) {
 		fmtc, w := utf8.DecodeRuneInString(format[i:])
+
+		// Space processing.
+		// In the rest of this comment "space" means spaces other than newline.
+		// Newline in the format matches input of zero or more spaces and then newline or end-of-input.
+		// Spaces in the format before the newline are collapsed into the newline.
+		// Spaces in the format after the newline match zero or more spaces after the corresponding input newline.
+		// Other spaces in the format match input of one or more spaces or end-of-input.
+		if isSpace(fmtc) {
+			newlines := 0
+			trailingSpace := false
+			for isSpace(fmtc) && i < len(format) {
+				if fmtc == '\n' {
+					newlines++
+					trailingSpace = false
+				} else {
+					trailingSpace = true
+				}
+				i += w
+				fmtc, w = utf8.DecodeRuneInString(format[i:])
+			}
+			for j := 0; j < newlines; j++ {
+				inputc := s.getRune()
+				for isSpace(inputc) && inputc != '\n' {
+					inputc = s.getRune()
+				}
+				if inputc != '\n' && inputc != eof {
+					s.errorString("newline in format does not match input")
+				}
+			}
+			if trailingSpace {
+				inputc := s.getRune()
+				if newlines == 0 {
+					// If the trailing space stood alone (did not follow a newline),
+					// it must find at least one space to consume.
+					if !isSpace(inputc) && inputc != eof {
+						s.errorString("expected space in input to match format")
+					}
+					if inputc == '\n' {
+						s.errorString("newline in input does not match format")
+					}
+				}
+				for isSpace(inputc) && inputc != '\n' {
+					inputc = s.getRune()
+				}
+				if inputc != eof {
+					s.UnreadRune()
+				}
+			}
+			continue
+		}
+
+		// Verbs.
 		if fmtc == '%' {
 			// % at end of string is an error.
 			if i+w == len(format) {
@@ -1087,48 +1139,8 @@ func (s *ss) advance(format string) (i int) {
 			}
 			i += w // skip the first %
 		}
-		sawSpace := false
-		wasNewline := false
-		// Skip spaces in format but absorb at most one newline.
-		for isSpace(fmtc) && i < len(format) {
-			if fmtc == '\n' {
-				if wasNewline { // Already saw one; stop here.
-					break
-				}
-				wasNewline = true
-			}
-			sawSpace = true
-			i += w
-			fmtc, w = utf8.DecodeRuneInString(format[i:])
-		}
-		if sawSpace {
-			// There was space in the format, so there should be space
-			// in the input.
-			inputc := s.getRune()
-			if inputc == eof {
-				return
-			}
-			if !isSpace(inputc) {
-				// Space in format but not in input.
-				s.errorString("expected space in input to match format")
-			}
-			// Skip spaces but stop at newline.
-			for inputc != '\n' && isSpace(inputc) {
-				inputc = s.getRune()
-			}
-			if inputc == '\n' {
-				if !wasNewline {
-					s.errorString("newline in input does not match format")
-				}
-				// We've reached a newline, stop now; don't read further.
-				return
-			}
-			s.UnreadRune()
-			if wasNewline {
-				s.errorString("newline in format does not match input")
-			}
-			continue
-		}
+
+		// Literals.
 		inputc := s.mustReadRune()
 		if fmtc != inputc {
 			s.UnreadRune()
diff --git a/src/fmt/scan_test.go b/src/fmt/scan_test.go
index e36b62e..d7019d9 100644
--- a/src/fmt/scan_test.go
+++ b/src/fmt/scan_test.go
@@ -291,6 +291,97 @@ var scanfTests = []ScanfTest{
 	{"%c", " ", &uintVal, uint(' ')},   // %c must accept a blank.
 	{"%c", "\t", &uintVal, uint('\t')}, // %c must accept any space.
 	{"%c", "\n", &uintVal, uint('\n')}, // %c must accept any space.
+
+	// space handling
+	{"%d", "27", &intVal, 27},
+	{"%d", "27 ", &intVal, 27},
+	{"%d", " 27", &intVal, 27},
+	{"%d", " 27 ", &intVal, 27},
+
+	{"X%d", "X27", &intVal, 27},
+	{"X%d", "X27 ", &intVal, 27},
+	{"X%d", "X 27", &intVal, 27},
+	{"X%d", "X 27 ", &intVal, 27},
+
+	{"X %d", "X27", &intVal, nil},  // expected space in input to match format
+	{"X %d", "X27 ", &intVal, nil}, // expected space in input to match format
+	{"X %d", "X 27", &intVal, 27},
+	{"X %d", "X 27 ", &intVal, 27},
+
+	{"%dX", "27X", &intVal, 27},
+	{"%dX", "27 X", &intVal, nil}, // input does not match format
+	{"%dX", " 27X", &intVal, 27},
+	{"%dX", " 27 X", &intVal, nil}, // input does not match format
+
+	{"%d X", "27X", &intVal, nil}, // expected space in input to match format
+	{"%d X", "27 X", &intVal, 27},
+	{"%d X", " 27X", &intVal, nil}, // expected space in input to match format
+	{"%d X", " 27 X", &intVal, 27},
+
+	{"X %d X", "X27X", &intVal, nil},  // expected space in input to match format
+	{"X %d X", "X27 X", &intVal, nil}, // expected space in input to match format
+	{"X %d X", "X 27X", &intVal, nil}, // expected space in input to match format
+	{"X %d X", "X 27 X", &intVal, 27},
+
+	{"X %s X", "X27X", &stringVal, nil},  // expected space in input to match format
+	{"X %s X", "X27 X", &stringVal, nil}, // expected space in input to match format
+	{"X %s X", "X 27X", &stringVal, nil}, // unexpected EOF
+	{"X %s X", "X 27 X", &stringVal, "27"},
+
+	{"X%sX", "X27X", &stringVal, nil},   // unexpected EOF
+	{"X%sX", "X27 X", &stringVal, nil},  // input does not match format
+	{"X%sX", "X 27X", &stringVal, nil},  // unexpected EOF
+	{"X%sX", "X 27 X", &stringVal, nil}, // input does not match format
+
+	{"X%s", "X27", &stringVal, "27"},
+	{"X%s", "X27 ", &stringVal, "27"},
+	{"X%s", "X 27", &stringVal, "27"},
+	{"X%s", "X 27 ", &stringVal, "27"},
+
+	{"X%dX", "X27X", &intVal, 27},
+	{"X%dX", "X27 X", &intVal, nil}, // input does not match format
+	{"X%dX", "X 27X", &intVal, 27},
+	{"X%dX", "X 27 X", &intVal, nil}, // input does not match format
+
+	{"X%dX", "X27X", &intVal, 27},
+	{"X%dX", "X27X ", &intVal, 27},
+	{"X%dX", " X27X", &intVal, nil},  // input does not match format
+	{"X%dX", " X27X ", &intVal, nil}, // input does not match format
+
+	{"X%dX\n", "X27X", &intVal, 27},
+	{"X%dX \n", "X27X ", &intVal, 27},
+	{"X%dX\n", "X27X\n", &intVal, 27},
+	{"X%dX\n", "X27X \n", &intVal, 27},
+
+	{"X%dX \n", "X27X", &intVal, 27},
+	{"X%dX \n", "X27X ", &intVal, 27},
+	{"X%dX \n", "X27X\n", &intVal, 27},
+	{"X%dX \n", "X27X \n", &intVal, 27},
+
+	{"X%c", "X\n", &runeVal, '\n'},
+	{"X%c", "X \n", &runeVal, ' '},
+	{"X %c", "X!", &runeVal, nil},  // expected space in input to match format
+	{"X %c", "X\n", &runeVal, nil}, // newline in input does not match format
+	{"X %c", "X !", &runeVal, '!'},
+	{"X %c", "X \n", &runeVal, '\n'},
+
+	{" X%dX", "X27X", &intVal, nil},  // expected space in input to match format
+	{" X%dX", "X27X ", &intVal, nil}, // expected space in input to match format
+	{" X%dX", " X27X", &intVal, 27},
+	{" X%dX", " X27X ", &intVal, 27},
+
+	{"X%dX ", "X27X", &intVal, 27},
+	{"X%dX ", "X27X ", &intVal, 27},
+	{"X%dX ", " X27X", &intVal, nil},  // input does not match format
+	{"X%dX ", " X27X ", &intVal, nil}, // input does not match format
+
+	{" X%dX ", "X27X", &intVal, nil},  // expected space in input to match format
+	{" X%dX ", "X27X ", &intVal, nil}, // expected space in input to match format
+	{" X%dX ", " X27X", &intVal, 27},
+	{" X%dX ", " X27X ", &intVal, 27},
+
+	{"%d\nX", "27\nX", &intVal, 27},
+	{"%dX\n X", "27X\n X", &intVal, 27},
 }
 
 var overflowTests = []ScanTest{
@@ -416,11 +507,17 @@ func TestScanf(t *testing.T) {
 	for _, test := range scanfTests {
 		n, err := Sscanf(test.text, test.format, test.in)
 		if err != nil {
-			t.Errorf("got error scanning (%q, %q): %s", test.format, test.text, err)
+			if test.out != nil {
+				t.Errorf("Sscanf(%q, %q): unexpected error: %v", test.text, test.format, err)
+			}
+			continue
+		}
+		if test.out == nil {
+			t.Errorf("Sscanf(%q, %q): unexpected success", test.text, test.format)
 			continue
 		}
 		if n != 1 {
-			t.Errorf("count error on entry (%q, %q): got %d", test.format, test.text, n)
+			t.Errorf("Sscanf(%q, %q): parsed %d field, want 1", test.text, test.format, n)
 			continue
 		}
 		// The incoming value may be a pointer
@@ -430,7 +527,7 @@ func TestScanf(t *testing.T) {
 		}
 		val := v.Interface()
 		if !reflect.DeepEqual(val, test.out) {
-			t.Errorf("scanning (%q, %q): expected %#v got %#v, type %T", test.format, test.text, test.out, val, val)
+			t.Errorf("Sscanf(%q, %q): parsed value %T(%#v), want %T(%#v)", test.text, test.format, val, val, test.out, test.out)
 		}
 	}
 }
@@ -1113,9 +1210,47 @@ func TestScanfNewlineMatchFormat(t *testing.T) {
 		{"space-newline in both", "1 \n2", "%d \n%d", 2, true},
 		{"extra space in format", "1\n2", "%d\n %d", 2, true},
 		{"two extra spaces in format", "1\n2", "%d \n %d", 2, true},
+		{"space vs newline 0000", "1\n2", "%d\n%d", 2, true},
+		{"space vs newline 0001", "1\n2", "%d\n %d", 2, true},
+		{"space vs newline 0010", "1\n2", "%d \n%d", 2, true},
+		{"space vs newline 0011", "1\n2", "%d \n %d", 2, true},
+		{"space vs newline 0100", "1\n 2", "%d\n%d", 2, true},
+		{"space vs newline 0101", "1\n 2", "%d\n%d ", 2, true},
+		{"space vs newline 0110", "1\n 2", "%d \n%d", 2, true},
+		{"space vs newline 0111", "1\n 2", "%d \n %d", 2, true},
+		{"space vs newline 1000", "1 \n2", "%d\n%d", 2, true},
+		{"space vs newline 1001", "1 \n2", "%d\n %d", 2, true},
+		{"space vs newline 1010", "1 \n2", "%d \n%d", 2, true},
+		{"space vs newline 1011", "1 \n2", "%d \n %d", 2, true},
+		{"space vs newline 1100", "1 \n 2", "%d\n%d", 2, true},
+		{"space vs newline 1101", "1 \n 2", "%d\n %d", 2, true},
+		{"space vs newline 1110", "1 \n 2", "%d \n%d", 2, true},
+		{"space vs newline 1111", "1 \n 2", "%d \n %d", 2, true},
+		{"space vs newline no-percent 0000", "1\n2", "1\n2", 0, true},
+		{"space vs newline no-percent 0001", "1\n2", "1\n 2", 0, true},
+		{"space vs newline no-percent 0010", "1\n2", "1 \n2", 0, true},
+		{"space vs newline no-percent 0011", "1\n2", "1 \n 2", 0, true},
+		{"space vs newline no-percent 0100", "1\n 2", "1\n2", 0, false},  // fails: space after nl in input but not pattern
+		{"space vs newline no-percent 0101", "1\n 2", "1\n2 ", 0, false}, // fails: space after nl in input but not pattern
+		{"space vs newline no-percent 0110", "1\n 2", "1 \n2", 0, false}, // fails: space after nl in input but not pattern
+		{"space vs newline no-percent 0111", "1\n 2", "1 \n 2", 0, true},
+		{"space vs newline no-percent 1000", "1 \n2", "1\n2", 0, true},
+		{"space vs newline no-percent 1001", "1 \n2", "1\n 2", 0, true},
+		{"space vs newline no-percent 1010", "1 \n2", "1 \n2", 0, true},
+		{"space vs newline no-percent 1011", "1 \n2", "1 \n 2", 0, true},
+		{"space vs newline no-percent 1100", "1 \n 2", "1\n2", 0, false}, // fails: space after nl in input but not pattern
+		{"space vs newline no-percent 1101", "1 \n 2", "1\n 2", 0, true},
+		{"space vs newline no-percent 1110", "1 \n 2", "1 \n2", 0, false}, // fails: space after nl in input but not pattern
+		{"space vs newline no-percent 1111", "1 \n 2", "1 \n 2", 0, true},
 	}
 	for _, test := range tests {
-		n, err := Sscanf(test.text, test.format, &a, &b)
+		var n int
+		var err error
+		if strings.Contains(test.format, "%") {
+			n, err = Sscanf(test.text, test.format, &a, &b)
+		} else {
+			n, err = Sscanf(test.text, test.format)
+		}
 		if n != test.count {
 			t.Errorf("%s: expected to scan %d item(s), scanned %d", test.name, test.count, n)
 		}
diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go
index d3dcd79..a197b5a 100644
--- a/src/go/ast/ast.go
+++ b/src/go/ast/ast.go
@@ -317,7 +317,7 @@ type (
 		Fun      Expr      // function expression
 		Lparen   token.Pos // position of "("
 		Args     []Expr    // function arguments; or nil
-		Ellipsis token.Pos // position of "...", if any
+		Ellipsis token.Pos // position of "..." (token.NoPos if there is no "...")
 		Rparen   token.Pos // position of ")"
 	}
 
@@ -902,7 +902,7 @@ type (
 
 	// A GenDecl node (generic declaration node) represents an import,
 	// constant, type or variable declaration. A valid Lparen position
-	// (Lparen.Line > 0) indicates a parenthesized declaration.
+	// (Lparen.IsValid()) indicates a parenthesized declaration.
 	//
 	// Relationship between Tok value and Specs element type:
 	//
diff --git a/src/go/build/build.go b/src/go/build/build.go
index 9706b8b..f6aabcb 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -256,13 +256,32 @@ func (ctxt *Context) SrcDirs() []string {
 // if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
 var Default Context = defaultContext()
 
+func defaultGOPATH() string {
+	env := "HOME"
+	if runtime.GOOS == "windows" {
+		env = "USERPROFILE"
+	} else if runtime.GOOS == "plan9" {
+		env = "home"
+	}
+	if home := os.Getenv(env); home != "" {
+		def := filepath.Join(home, "go")
+		if def == runtime.GOROOT() {
+			// Don't set the default GOPATH to GOROOT,
+			// as that will trigger warnings from the go tool.
+			return ""
+		}
+		return def
+	}
+	return ""
+}
+
 func defaultContext() Context {
 	var c Context
 
 	c.GOARCH = envOr("GOARCH", runtime.GOARCH)
 	c.GOOS = envOr("GOOS", runtime.GOOS)
 	c.GOROOT = pathpkg.Clean(runtime.GOROOT())
-	c.GOPATH = envOr("GOPATH", "")
+	c.GOPATH = envOr("GOPATH", defaultGOPATH())
 	c.Compiler = runtime.Compiler
 
 	// Each major Go release in the Go 1.x series should add a tag here.
@@ -270,9 +289,13 @@ func defaultContext() Context {
 	// in all releases >= Go 1.x. Code that requires Go 1.x or later should
 	// say "+build go1.x", and code that should only be built before Go 1.x
 	// (perhaps it is the stub to use in that case) should say "+build !go1.x".
-	c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7"}
+	c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7", "go1.8"}
 
-	switch os.Getenv("CGO_ENABLED") {
+	env := os.Getenv("CGO_ENABLED")
+	if env == "" {
+		env = defaultCGO_ENABLED
+	}
+	switch env {
 	case "1":
 		c.CgoEnabled = true
 	case "0":
@@ -336,6 +359,11 @@ const (
 	// See golang.org/s/go15vendor for more information.
 	//
 	// Setting IgnoreVendor ignores vendor directories.
+	//
+	// In contrast to the package's ImportPath,
+	// the returned package's Imports, TestImports, and XTestImports
+	// are always the exact import paths from the source files:
+	// Import makes no attempt to resolve or check those paths.
 	IgnoreVendor
 )
 
@@ -381,15 +409,15 @@ type Package struct {
 	CgoPkgConfig []string // Cgo pkg-config directives
 
 	// Dependency information
-	Imports   []string                    // imports from GoFiles, CgoFiles
+	Imports   []string                    // import paths from GoFiles, CgoFiles
 	ImportPos map[string][]token.Position // line information for Imports
 
 	// Test information
 	TestGoFiles    []string                    // _test.go files in package
-	TestImports    []string                    // imports from TestGoFiles
+	TestImports    []string                    // import paths from TestGoFiles
 	TestImportPos  map[string][]token.Position // line information for TestImports
 	XTestGoFiles   []string                    // _test.go files outside package
-	XTestImports   []string                    // imports from XTestGoFiles
+	XTestImports   []string                    // import paths from XTestGoFiles
 	XTestImportPos map[string][]token.Position // line information for XTestImports
 }
 
@@ -410,11 +438,16 @@ func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
 // containing no buildable Go source files. (It may still contain
 // test files, files hidden by build tags, and so on.)
 type NoGoError struct {
-	Dir string
+	Dir     string
+	Ignored bool // whether any Go files were ignored due to build tags
 }
 
 func (e *NoGoError) Error() string {
-	return "no buildable Go source files in " + e.Dir
+	msg := "no buildable Go source files in " + e.Dir
+	if e.Ignored {
+		msg += " (.go files ignored due to build tags)"
+	}
+	return msg
 }
 
 // MultiplePackageError describes a directory containing
@@ -636,7 +669,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
 			format = "\t%s"
 		}
 		if len(tried.gopath) == 0 {
-			paths = append(paths, "\t($GOPATH not set)")
+			paths = append(paths, "\t($GOPATH not set. For more details see: 'go help gopath')")
 		}
 		return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n"))
 	}
@@ -846,7 +879,7 @@ Found:
 		return p, badGoError
 	}
 	if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
-		return p, &NoGoError{p.Dir}
+		return p, &NoGoError{Dir: p.Dir, Ignored: len(p.IgnoredGoFiles) > 0}
 	}
 
 	for tag := range allTags {
@@ -1063,10 +1096,14 @@ func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map
 	}
 
 	// Look for +build comments to accept or reject the file.
-	if !ctxt.shouldBuild(data, allTags, binaryOnly) && !ctxt.UseAllFiles {
+	var sawBinaryOnly bool
+	if !ctxt.shouldBuild(data, allTags, &sawBinaryOnly) && !ctxt.UseAllFiles {
 		return
 	}
 
+	if binaryOnly != nil && sawBinaryOnly {
+		*binaryOnly = true
+	}
 	match = true
 	return
 }
@@ -1110,9 +1147,8 @@ var binaryOnlyComment = []byte("//go:binary-only-package")
 //
 // marks the file as applicable only on Windows and Linux.
 //
-// If shouldBuild finds a //go:binary-only-package comment in a file that
-// should be built, it sets *binaryOnly to true. Otherwise it does
-// not change *binaryOnly.
+// If shouldBuild finds a //go:binary-only-package comment in the file,
+// it sets *binaryOnly to true. Otherwise it does not change *binaryOnly.
 //
 func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool, binaryOnly *bool) bool {
 	sawBinaryOnly := false
@@ -1282,7 +1318,8 @@ func expandSrcDir(str string, srcdir string) (string, bool) {
 // We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
 // See golang.org/issue/6038.
 // The @ is for OS X. See golang.org/issue/13720.
-const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@"
+// The % is for Jenkins. See golang.org/issue/16959.
+const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%"
 const safeSpaces = " "
 
 var safeBytes = []byte(safeSpaces + safeString)
diff --git a/src/go/build/build_test.go b/src/go/build/build_test.go
index 198a649..8ca8e5e 100644
--- a/src/go/build/build_test.go
+++ b/src/go/build/build_test.go
@@ -93,6 +93,17 @@ func TestEmptyFolderImport(t *testing.T) {
 	}
 }
 
+func TestIgnoredGoFilesImport(t *testing.T) {
+	_, err := Import(".", "testdata/ignored", 0)
+	e, ok := err.(*NoGoError)
+	if !ok {
+		t.Fatal(`Import("testdata/ignored") did not return NoGoError.`)
+	}
+	if !e.Ignored {
+		t.Fatal(`Import("testdata/ignored") should have ignored Go files.`)
+	}
+}
+
 func TestMultiplePackageImport(t *testing.T) {
 	_, err := Import(".", "testdata/multi", 0)
 	mpe, ok := err.(*MultiplePackageError)
@@ -283,6 +294,7 @@ func TestShellSafety(t *testing.T) {
 		result                  bool
 	}{
 		{"-I${SRCDIR}/../include", "/projects/src/issue 11868", "-I/projects/src/issue 11868/../include", true},
+		{"-I${SRCDIR}", "wtf$@%", "-Iwtf$@%", true},
 		{"-X${SRCDIR}/1,${SRCDIR}/2", "/projects/src/issue 11868", "-X/projects/src/issue 11868/1,/projects/src/issue 11868/2", true},
 		{"-I/tmp -I/tmp", "/tmp2", "-I/tmp -I/tmp", false},
 		{"-I/tmp", "/tmp/[0]", "-I/tmp", true},
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index 5b25291..e6f2288 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -59,7 +59,6 @@ var pkgDeps = map[string][]string{
 	"math":          {"unsafe"},
 	"math/cmplx":    {"math"},
 	"math/rand":     {"L0", "math"},
-	"sort":          {},
 	"strconv":       {"L0", "unicode/utf8", "math"},
 	"unicode/utf16": {},
 	"unicode/utf8":  {},
@@ -94,8 +93,8 @@ var pkgDeps = map[string][]string{
 	// L3 adds reflection and some basic utility packages
 	// and interface definitions, but nothing that makes
 	// system calls.
-	"crypto":              {"L2", "hash"},          // interfaces
-	"crypto/cipher":       {"L2", "crypto/subtle"}, // interfaces
+	"crypto":              {"L2", "hash"}, // interfaces
+	"crypto/cipher":       {"L2", "crypto/subtle"},
 	"crypto/subtle":       {},
 	"encoding/base32":     {"L2"},
 	"encoding/base64":     {"L2"},
@@ -109,11 +108,13 @@ var pkgDeps = map[string][]string{
 	"image/color":         {"L2"},                // interfaces
 	"image/color/palette": {"L2", "image/color"},
 	"reflect":             {"L2"},
+	"sort":                {"reflect"},
 
 	"L3": {
 		"L2",
 		"crypto",
 		"crypto/cipher",
+		"crypto/internal/cipherhw",
 		"crypto/subtle",
 		"encoding/base32",
 		"encoding/base64",
@@ -170,17 +171,18 @@ var pkgDeps = map[string][]string{
 	"log": {"L1", "os", "fmt", "time"},
 
 	// Packages used by testing must be low-level (L2+fmt).
-	"regexp":         {"L2", "regexp/syntax"},
-	"regexp/syntax":  {"L2"},
-	"runtime/debug":  {"L2", "fmt", "io/ioutil", "os", "time"},
-	"runtime/pprof":  {"L2", "fmt", "os", "text/tabwriter"},
-	"runtime/trace":  {"L0"},
-	"text/tabwriter": {"L2"},
-
-	"testing":          {"L2", "flag", "fmt", "os", "runtime/debug", "runtime/pprof", "runtime/trace", "time"},
+	"regexp":                            {"L2", "regexp/syntax"},
+	"regexp/syntax":                     {"L2"},
+	"runtime/debug":                     {"L2", "fmt", "io/ioutil", "os", "time"},
+	"runtime/pprof/internal/protopprof": {"L2", "fmt", "internal/pprof/profile", "os", "time"},
+	"runtime/pprof":                     {"L2", "fmt", "internal/pprof/profile", "os", "runtime/pprof/internal/protopprof", "text/tabwriter", "time"},
+	"runtime/trace":                     {"L0"},
+	"text/tabwriter":                    {"L2"},
+
+	"testing":          {"L2", "context", "flag", "fmt", "internal/race", "os", "runtime/debug", "runtime/pprof", "runtime/trace", "time"},
 	"testing/iotest":   {"L2", "log"},
 	"testing/quick":    {"L2", "flag", "fmt", "reflect"},
-	"internal/testenv": {"L2", "OS", "flag", "testing"},
+	"internal/testenv": {"L2", "OS", "flag", "testing", "syscall"},
 
 	// L4 is defined as L3+fmt+log+time, because in general once
 	// you're using L3 packages, use of fmt, log, or time is not a big deal.
@@ -219,50 +221,53 @@ var pkgDeps = map[string][]string{
 	"go/types":                  {"L4", "GOPARSER", "container/heap", "go/constant"},
 
 	// One of a kind.
-	"archive/tar":              {"L4", "OS", "syscall"},
-	"archive/zip":              {"L4", "OS", "compress/flate"},
-	"container/heap":           {"sort"},
-	"compress/bzip2":           {"L4"},
-	"compress/flate":           {"L4"},
-	"compress/gzip":            {"L4", "compress/flate"},
-	"compress/lzw":             {"L4"},
-	"compress/zlib":            {"L4", "compress/flate"},
-	"context":                  {"errors", "fmt", "reflect", "sync", "time"},
-	"database/sql":             {"L4", "container/list", "database/sql/driver"},
-	"database/sql/driver":      {"L4", "time"},
-	"debug/dwarf":              {"L4"},
-	"debug/elf":                {"L4", "OS", "debug/dwarf", "compress/zlib"},
-	"debug/gosym":              {"L4"},
-	"debug/macho":              {"L4", "OS", "debug/dwarf"},
-	"debug/pe":                 {"L4", "OS", "debug/dwarf"},
-	"debug/plan9obj":           {"L4", "OS"},
-	"encoding":                 {"L4"},
-	"encoding/ascii85":         {"L4"},
-	"encoding/asn1":            {"L4", "math/big"},
-	"encoding/csv":             {"L4"},
-	"encoding/gob":             {"L4", "OS", "encoding"},
-	"encoding/hex":             {"L4"},
-	"encoding/json":            {"L4", "encoding"},
-	"encoding/pem":             {"L4"},
-	"encoding/xml":             {"L4", "encoding"},
-	"flag":                     {"L4", "OS"},
-	"go/build":                 {"L4", "OS", "GOPARSER"},
-	"html":                     {"L4"},
-	"image/draw":               {"L4", "image/internal/imageutil"},
-	"image/gif":                {"L4", "compress/lzw", "image/color/palette", "image/draw"},
-	"image/internal/imageutil": {"L4"},
-	"image/jpeg":               {"L4", "image/internal/imageutil"},
-	"image/png":                {"L4", "compress/zlib"},
-	"index/suffixarray":        {"L4", "regexp"},
-	"internal/singleflight":    {"sync"},
-	"internal/trace":           {"L4", "OS"},
-	"math/big":                 {"L4"},
-	"mime":                     {"L4", "OS", "syscall", "internal/syscall/windows/registry"},
-	"mime/quotedprintable":     {"L4"},
-	"net/internal/socktest":    {"L4", "OS", "syscall"},
-	"net/url":                  {"L4"},
-	"text/scanner":             {"L4", "OS"},
-	"text/template/parse":      {"L4"},
+	"archive/tar":               {"L4", "OS", "syscall"},
+	"archive/zip":               {"L4", "OS", "compress/flate"},
+	"container/heap":            {"sort"},
+	"compress/bzip2":            {"L4"},
+	"compress/flate":            {"L4"},
+	"compress/gzip":             {"L4", "compress/flate"},
+	"compress/lzw":              {"L4"},
+	"compress/zlib":             {"L4", "compress/flate"},
+	"context":                   {"errors", "fmt", "reflect", "sync", "time"},
+	"database/sql":              {"L4", "container/list", "context", "database/sql/driver", "database/sql/internal"},
+	"database/sql/driver":       {"L4", "context", "time", "database/sql/internal"},
+	"debug/dwarf":               {"L4"},
+	"debug/elf":                 {"L4", "OS", "debug/dwarf", "compress/zlib"},
+	"debug/gosym":               {"L4"},
+	"debug/macho":               {"L4", "OS", "debug/dwarf"},
+	"debug/pe":                  {"L4", "OS", "debug/dwarf"},
+	"debug/plan9obj":            {"L4", "OS"},
+	"encoding":                  {"L4"},
+	"encoding/ascii85":          {"L4"},
+	"encoding/asn1":             {"L4", "math/big"},
+	"encoding/csv":              {"L4"},
+	"encoding/gob":              {"L4", "OS", "encoding"},
+	"encoding/hex":              {"L4"},
+	"encoding/json":             {"L4", "encoding"},
+	"encoding/pem":              {"L4"},
+	"encoding/xml":              {"L4", "encoding"},
+	"flag":                      {"L4", "OS"},
+	"go/build":                  {"L4", "OS", "GOPARSER"},
+	"html":                      {"L4"},
+	"image/draw":                {"L4", "image/internal/imageutil"},
+	"image/gif":                 {"L4", "compress/lzw", "image/color/palette", "image/draw"},
+	"image/internal/imageutil":  {"L4"},
+	"image/jpeg":                {"L4", "image/internal/imageutil"},
+	"image/png":                 {"L4", "compress/zlib"},
+	"index/suffixarray":         {"L4", "regexp"},
+	"internal/singleflight":     {"sync"},
+	"internal/trace":            {"L4", "OS"},
+	"internal/pprof/profile":    {"L4", "OS", "compress/gzip", "regexp"},
+	"math/big":                  {"L4"},
+	"mime":                      {"L4", "OS", "syscall", "internal/syscall/windows/registry"},
+	"mime/quotedprintable":      {"L4"},
+	"net/internal/socktest":     {"L4", "OS", "syscall"},
+	"net/url":                   {"L4"},
+	"plugin":                    {"L0", "OS", "CGO"},
+	"testing/internal/testdeps": {"L4", "runtime/pprof", "regexp"},
+	"text/scanner":              {"L4", "OS"},
+	"text/template/parse":       {"L4"},
 
 	"html/template": {
 		"L4", "OS", "encoding/json", "html", "text/template",
@@ -297,7 +302,7 @@ var pkgDeps = map[string][]string{
 		"context", "math/rand", "os", "sort", "syscall", "time",
 		"internal/nettrace",
 		"internal/syscall/windows", "internal/singleflight", "internal/race",
-		"golang_org/x/net/route",
+		"golang_org/x/net/lif", "golang_org/x/net/route",
 	},
 
 	// NET enables use of basic network-related packages.
@@ -332,6 +337,9 @@ var pkgDeps = map[string][]string{
 		"crypto/sha1",
 		"crypto/sha256",
 		"crypto/sha512",
+		"golang_org/x/crypto/chacha20poly1305",
+		"golang_org/x/crypto/curve25519",
+		"golang_org/x/crypto/poly1305",
 	},
 
 	// Random byte, number generation.
@@ -375,16 +383,24 @@ var pkgDeps = map[string][]string{
 	// HTTP, kingpin of dependencies.
 	"net/http": {
 		"L4", "NET", "OS",
-		"context", "compress/gzip", "container/list", "crypto/tls",
-		"mime/multipart", "runtime/debug",
-		"net/http/internal",
+		"compress/gzip",
+		"container/list",
+		"context",
+		"crypto/rand",
+		"crypto/tls",
 		"golang_org/x/net/http2/hpack",
+		"golang_org/x/net/idna",
 		"golang_org/x/net/lex/httplex",
+		"golang_org/x/text/unicode/norm",
+		"golang_org/x/text/width",
 		"internal/nettrace",
+		"mime/multipart",
 		"net/http/httptrace",
+		"net/http/internal",
+		"runtime/debug",
 	},
 	"net/http/internal":  {"L4"},
-	"net/http/httptrace": {"context", "internal/nettrace", "net", "reflect", "time"},
+	"net/http/httptrace": {"context", "crypto/tls", "internal/nettrace", "net", "reflect", "time"},
 
 	// HTTP-using packages.
 	"expvar":             {"L4", "OS", "encoding/json", "net/http"},
@@ -392,7 +408,7 @@ var pkgDeps = map[string][]string{
 	"net/http/cookiejar": {"L4", "NET", "net/http"},
 	"net/http/fcgi":      {"L4", "NET", "OS", "net/http", "net/http/cgi"},
 	"net/http/httptest":  {"L4", "NET", "OS", "crypto/tls", "flag", "net/http", "net/http/internal"},
-	"net/http/httputil":  {"L4", "NET", "OS", "net/http", "net/http/internal"},
+	"net/http/httputil":  {"L4", "NET", "OS", "context", "net/http", "net/http/internal"},
 	"net/http/pprof":     {"L4", "OS", "html/template", "net/http", "runtime/pprof", "runtime/trace"},
 	"net/rpc":            {"L4", "NET", "encoding/gob", "html/template", "net/http"},
 	"net/rpc/jsonrpc":    {"L4", "NET", "encoding/json", "net/rpc"},
diff --git a/src/go/build/doc.go b/src/go/build/doc.go
index 9f7ac8f..979d047 100644
--- a/src/go/build/doc.go
+++ b/src/go/build/doc.go
@@ -104,6 +104,7 @@
 //	- "go1.5", from Go version 1.5 onward
 //	- "go1.6", from Go version 1.6 onward
 //	- "go1.7", from Go version 1.7 onward
+//	- "go1.8", from Go version 1.8 onward
 //	- any additional words listed in ctxt.BuildTags
 //
 // If a file's name, after stripping the extension and a possible _test suffix,
diff --git a/src/go/build/syslist.go b/src/go/build/syslist.go
index c83622b..73fdbe6 100644
--- a/src/go/build/syslist.go
+++ b/src/go/build/syslist.go
@@ -4,5 +4,5 @@
 
 package build
 
-const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows "
+const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows zos "
 const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc s390 s390x sparc sparc64 "
diff --git a/src/go/build/testdata/ignored/ignored.go b/src/go/build/testdata/ignored/ignored.go
new file mode 100644
index 0000000..48a2ae8
--- /dev/null
+++ b/src/go/build/testdata/ignored/ignored.go
@@ -0,0 +1,3 @@
+// +build alwaysignore
+
+package ignored
diff --git a/src/go/constant/value.go b/src/go/constant/value.go
index ab10ae3..7c32473 100644
--- a/src/go/constant/value.go
+++ b/src/go/constant/value.go
@@ -43,13 +43,14 @@ type Value interface {
 	// Kind returns the value kind.
 	Kind() Kind
 
-	// String returns a short, human-readable form of the value.
+	// String returns a short, quoted (human-readable) form of the value.
 	// For numeric values, the result may be an approximation;
 	// for String values the result may be a shortened string.
 	// Use ExactString for a string representing a value exactly.
 	String() string
 
-	// ExactString returns an exact, printable form of the value.
+	// ExactString returns an exact, quoted (human-readable) form of the value.
+	// If the Value is of Kind String, use StringVal to obtain the unquoted string.
 	ExactString() string
 
 	// Prevent external implementations.
@@ -847,6 +848,10 @@ Error:
 
 func ord(x Value) int {
 	switch x.(type) {
+	default:
+		// force invalid value into "x position" in match
+		// (don't panic here so that callers can provide a better error message)
+		return -1
 	case unknownVal:
 		return 0
 	case boolVal, stringVal:
@@ -861,15 +866,13 @@ func ord(x Value) int {
 		return 5
 	case complexVal:
 		return 6
-	default:
-		panic("unreachable")
 	}
 }
 
 // match returns the matching representation (same type) with the
 // smallest complexity for two values x and y. If one of them is
-// numeric, both of them must be numeric. If one of them is Unknown,
-// both results are Unknown.
+// numeric, both of them must be numeric. If one of them is Unknown
+// or invalid (say, nil) both results are that value.
 //
 func match(x, y Value) (_, _ Value) {
 	if ord(x) > ord(y) {
@@ -879,9 +882,6 @@ func match(x, y Value) (_, _ Value) {
 	// ord(x) <= ord(y)
 
 	switch x := x.(type) {
-	case unknownVal:
-		return x, x
-
 	case boolVal, stringVal, complexVal:
 		return x, y
 
@@ -920,6 +920,7 @@ func match(x, y Value) (_, _ Value) {
 		case complexVal:
 			return vtoc(x), y
 		}
+
 	case floatVal:
 		switch y := y.(type) {
 		case floatVal:
@@ -929,18 +930,23 @@ func match(x, y Value) (_, _ Value) {
 		}
 	}
 
-	panic("unreachable")
+	// force unknown and invalid values into "x position" in callers of match
+	// (don't panic here so that callers can provide a better error message)
+	return x, x
 }
 
 // BinaryOp returns the result of the binary expression x op y.
 // The operation must be defined for the operands. If one of the
 // operands is Unknown, the result is Unknown.
+// BinaryOp doesn't handle comparisons or shifts; use Compare
+// or Shift instead.
+//
 // To force integer division of Int operands, use op == token.QUO_ASSIGN
 // instead of token.QUO; the result is guaranteed to be Int in this case.
 // Division by zero leads to a run-time panic.
 //
-func BinaryOp(x Value, op token.Token, y Value) Value {
-	x, y = match(x, y)
+func BinaryOp(x_ Value, op token.Token, y_ Value) Value {
+	x, y := match(x_, y_)
 
 	switch x := x.(type) {
 	case unknownVal:
@@ -1107,7 +1113,7 @@ func BinaryOp(x Value, op token.Token, y Value) Value {
 	}
 
 Error:
-	panic(fmt.Sprintf("invalid binary operation %v %s %v", x, op, y))
+	panic(fmt.Sprintf("invalid binary operation %v %s %v", x_, op, y_))
 }
 
 func add(x, y Value) Value { return BinaryOp(x, token.ADD, y) }
@@ -1167,7 +1173,7 @@ func cmpZero(x int, op token.Token) bool {
 	case token.GEQ:
 		return x >= 0
 	}
-	panic("unreachable")
+	panic(fmt.Sprintf("invalid comparison %v %s 0", x, op))
 }
 
 // Compare returns the result of the comparison x op y.
@@ -1175,8 +1181,8 @@ func cmpZero(x int, op token.Token) bool {
 // If one of the operands is Unknown, the result is
 // false.
 //
-func Compare(x Value, op token.Token, y Value) bool {
-	x, y = match(x, y)
+func Compare(x_ Value, op token.Token, y_ Value) bool {
+	x, y := match(x_, y_)
 
 	switch x := x.(type) {
 	case unknownVal:
@@ -1246,5 +1252,5 @@ func Compare(x Value, op token.Token, y Value) bool {
 		}
 	}
 
-	panic(fmt.Sprintf("invalid comparison %v %s %v", x, op, y))
+	panic(fmt.Sprintf("invalid comparison %v %s %v", x_, op, y_))
 }
diff --git a/src/go/doc/comment.go b/src/go/doc/comment.go
index ed8eef4..15e034b 100644
--- a/src/go/doc/comment.go
+++ b/src/go/doc/comment.go
@@ -53,7 +53,7 @@ const (
 	filePart = `[a-zA-Z0-9_?%#~&/\-+=()]+` // parentheses may not be matching; see pairedParensPrefixLen
 	urlRx    = `(` + protocol + `)://` +   // http://
 		hostPart + `([.:]` + hostPart + `)*/?` + // //www.google.com:8080/
-		filePart + `([:.,]` + filePart + `)*`
+		filePart + `([:.,;]` + filePart + `)*`
 )
 
 var matchRx = regexp.MustCompile(`(` + urlRx + `)|(` + identRx + `)`)
diff --git a/src/go/doc/comment_test.go b/src/go/doc/comment_test.go
index ad65c2a..76dfbea 100644
--- a/src/go/doc/comment_test.go
+++ b/src/go/doc/comment_test.go
@@ -162,6 +162,7 @@ var emphasizeTests = []struct {
 	{"Hello http://example.com/%2f/ /world.", `Hello <a href="http://example.com/%2f/">http://example.com/%2f/</a> /world.`},
 	{"Lorem http: ipsum //host/path", "Lorem http: ipsum //host/path"},
 	{"javascript://is/not/linked", "javascript://is/not/linked"},
+	{"http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD", `<a href="http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD">http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD</a>`},
 }
 
 func TestEmphasize(t *testing.T) {
diff --git a/src/go/doc/reader.go b/src/go/doc/reader.go
index e4e7b7c..8e82353 100644
--- a/src/go/doc/reader.go
+++ b/src/go/doc/reader.go
@@ -362,6 +362,11 @@ func (r *reader) readFunc(fun *ast.FuncDecl) {
 	// associate methods with the receiver type, if any
 	if fun.Recv != nil {
 		// method
+		if len(fun.Recv.List) == 0 {
+			// should not happen (incorrect AST); (See issue 17788)
+			// don't show this method
+			return
+		}
 		recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type)
 		if imp {
 			// should not happen (incorrect AST);
@@ -645,7 +650,9 @@ func (r *reader) computeMethodSets() {
 func (r *reader) cleanupTypes() {
 	for _, t := range r.types {
 		visible := r.isVisible(t.name)
-		if t.decl == nil && (predeclaredTypes[t.name] || visible && (t.isEmbedded || r.hasDotImp)) {
+		predeclared := predeclaredTypes[t.name]
+
+		if t.decl == nil && (predeclared || visible && (t.isEmbedded || r.hasDotImp)) {
 			// t.name is a predeclared type (and was not redeclared in this package),
 			// or it was embedded somewhere but its declaration is missing (because
 			// the AST is incomplete), or we have a dot-import (and all bets are off):
@@ -660,10 +667,12 @@ func (r *reader) cleanupTypes() {
 				r.funcs[name] = f
 			}
 			// 3) move methods
-			for name, m := range t.methods {
-				// don't overwrite functions with the same name - drop them
-				if _, found := r.funcs[name]; !found {
-					r.funcs[name] = m
+			if !predeclared {
+				for name, m := range t.methods {
+					// don't overwrite functions with the same name - drop them
+					if _, found := r.funcs[name]; !found {
+						r.funcs[name] = m
+					}
 				}
 			}
 		}
@@ -809,6 +818,11 @@ func noteBodies(notes []*Note) []string {
 // ----------------------------------------------------------------------------
 // Predeclared identifiers
 
+// IsPredeclared reports whether s is a predeclared identifier.
+func IsPredeclared(s string) bool {
+	return predeclaredTypes[s] || predeclaredFuncs[s] || predeclaredConstants[s]
+}
+
 var predeclaredTypes = map[string]bool{
 	"bool":       true,
 	"byte":       true,
diff --git a/src/go/doc/testdata/issue17788.0.golden b/src/go/doc/testdata/issue17788.0.golden
new file mode 100644
index 0000000..42c00da
--- /dev/null
+++ b/src/go/doc/testdata/issue17788.0.golden
@@ -0,0 +1,8 @@
+// 
+PACKAGE issue17788
+
+IMPORTPATH
+	testdata/issue17788
+
+FILENAMES
+	testdata/issue17788.go
diff --git a/src/go/doc/testdata/issue17788.1.golden b/src/go/doc/testdata/issue17788.1.golden
new file mode 100644
index 0000000..42c00da
--- /dev/null
+++ b/src/go/doc/testdata/issue17788.1.golden
@@ -0,0 +1,8 @@
+// 
+PACKAGE issue17788
+
+IMPORTPATH
+	testdata/issue17788
+
+FILENAMES
+	testdata/issue17788.go
diff --git a/src/go/doc/testdata/issue17788.2.golden b/src/go/doc/testdata/issue17788.2.golden
new file mode 100644
index 0000000..42c00da
--- /dev/null
+++ b/src/go/doc/testdata/issue17788.2.golden
@@ -0,0 +1,8 @@
+// 
+PACKAGE issue17788
+
+IMPORTPATH
+	testdata/issue17788
+
+FILENAMES
+	testdata/issue17788.go
diff --git a/src/go/doc/testdata/issue17788.go b/src/go/doc/testdata/issue17788.go
new file mode 100644
index 0000000..883ad5f
--- /dev/null
+++ b/src/go/doc/testdata/issue17788.go
@@ -0,0 +1,8 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue17788
+
+func ( /* receiver type */ ) f0() {
+}
diff --git a/src/go/doc/testdata/predeclared.0.golden b/src/go/doc/testdata/predeclared.0.golden
new file mode 100644
index 0000000..9f37b06
--- /dev/null
+++ b/src/go/doc/testdata/predeclared.0.golden
@@ -0,0 +1,8 @@
+// Package predeclared is a go/doc test for handling of exported ...
+PACKAGE predeclared
+
+IMPORTPATH
+	testdata/predeclared
+
+FILENAMES
+	testdata/predeclared.go
diff --git a/src/go/doc/testdata/predeclared.1.golden b/src/go/doc/testdata/predeclared.1.golden
new file mode 100644
index 0000000..2ff8ee6
--- /dev/null
+++ b/src/go/doc/testdata/predeclared.1.golden
@@ -0,0 +1,22 @@
+// Package predeclared is a go/doc test for handling of exported ...
+PACKAGE predeclared
+
+IMPORTPATH
+	testdata/predeclared
+
+FILENAMES
+	testdata/predeclared.go
+
+TYPES
+	// 
+	type bool int
+
+	// Must not be visible. 
+	func (b bool) String() string
+
+	// 
+	type error struct{}
+
+	// Must not be visible. 
+	func (e error) Error() string
+
diff --git a/src/go/doc/testdata/predeclared.2.golden b/src/go/doc/testdata/predeclared.2.golden
new file mode 100644
index 0000000..9f37b06
--- /dev/null
+++ b/src/go/doc/testdata/predeclared.2.golden
@@ -0,0 +1,8 @@
+// Package predeclared is a go/doc test for handling of exported ...
+PACKAGE predeclared
+
+IMPORTPATH
+	testdata/predeclared
+
+FILENAMES
+	testdata/predeclared.go
diff --git a/src/go/doc/testdata/predeclared.go b/src/go/doc/testdata/predeclared.go
new file mode 100644
index 0000000..c6dd806
--- /dev/null
+++ b/src/go/doc/testdata/predeclared.go
@@ -0,0 +1,22 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package predeclared is a go/doc test for handling of
+// exported methods on locally-defined predeclared types.
+// See issue 9860.
+package predeclared
+
+type error struct{}
+
+// Must not be visible.
+func (e error) Error() string {
+	return ""
+}
+
+type bool int
+
+// Must not be visible.
+func (b bool) String() string {
+	return ""
+}
diff --git a/src/go/format/format_test.go b/src/go/format/format_test.go
index b5817a5..72b8d5a 100644
--- a/src/go/format/format_test.go
+++ b/src/go/format/format_test.go
@@ -6,9 +6,11 @@ package format
 
 import (
 	"bytes"
+	"fmt"
 	"go/parser"
 	"go/token"
 	"io/ioutil"
+	"log"
 	"strings"
 	"testing"
 )
@@ -143,3 +145,28 @@ func TestPartial(t *testing.T) {
 		}
 	}
 }
+
+func ExampleNode() {
+	const expr = "(6+2*3)/4"
+
+	// parser.ParseExpr parses the argument and returns the
+	// corresponding ast.Node.
+	node, err := parser.ParseExpr(expr)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Create a FileSet for node. Since the node does not come
+	// from a real source file, fset will be empty.
+	fset := token.NewFileSet()
+
+	var buf bytes.Buffer
+	err = Node(&buf, fset, node)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Println(buf.String())
+
+	// Output: (6 + 2*3) / 4
+}
diff --git a/src/go/internal/gccgoimporter/importer.go b/src/go/internal/gccgoimporter/importer.go
index 19b9c73..a22d8fe 100644
--- a/src/go/internal/gccgoimporter/importer.go
+++ b/src/go/internal/gccgoimporter/importer.go
@@ -63,6 +63,7 @@ func findExportFile(searchpaths []string, pkgpath string) (string, error) {
 
 const (
 	gccgov1Magic    = "v1;\n"
+	gccgov2Magic    = "v2;\n"
 	goimporterMagic = "\n$$ "
 	archiveMagic    = "!<ar"
 )
@@ -91,7 +92,7 @@ func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err e
 
 	var elfreader io.ReaderAt
 	switch string(magic[:]) {
-	case gccgov1Magic, goimporterMagic:
+	case gccgov1Magic, gccgov2Magic, goimporterMagic:
 		// Raw export data.
 		reader = f
 		return
@@ -168,7 +169,7 @@ func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Impo
 		}
 
 		switch string(magic[:]) {
-		case gccgov1Magic:
+		case gccgov1Magic, gccgov2Magic:
 			var p parser
 			p.init(fpath, reader, imports)
 			pkg = p.parsePackage()
diff --git a/src/go/internal/gccgoimporter/importer_test.go b/src/go/internal/gccgoimporter/importer_test.go
index c10fa48..58abbba 100644
--- a/src/go/internal/gccgoimporter/importer_test.go
+++ b/src/go/internal/gccgoimporter/importer_test.go
@@ -95,6 +95,7 @@ var importerTests = [...]importerTest{
 	{pkgpath: "complexnums", name: "NP", want: "const NP untyped complex", wantval: "(-1 + 1i)"},
 	{pkgpath: "complexnums", name: "PN", want: "const PN untyped complex", wantval: "(1 + -1i)"},
 	{pkgpath: "complexnums", name: "PP", want: "const PP untyped complex", wantval: "(1 + 1i)"},
+	{pkgpath: "conversions", name: "Bits", want: "const Bits Units", wantval: `"bits"`},
 	// TODO: enable this entry once bug has been tracked down
 	//{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
 }
diff --git a/src/go/internal/gccgoimporter/parser.go b/src/go/internal/gccgoimporter/parser.go
index c06cce4..7312cb4 100644
--- a/src/go/internal/gccgoimporter/parser.go
+++ b/src/go/internal/gccgoimporter/parser.go
@@ -19,6 +19,7 @@ import (
 
 type parser struct {
 	scanner  scanner.Scanner
+	version  string                    // format version
 	tok      rune                      // current token
 	lit      string                    // literal string; only valid for Ident, Int, String tokens
 	pkgpath  string                    // package path of imported package
@@ -245,9 +246,20 @@ func (p *parser) parseVar(pkg *types.Package) *types.Var {
 	return types.NewVar(token.NoPos, pkg, name, p.parseType(pkg))
 }
 
-// ConstValue     = string | "false" | "true" | ["-"] (int ["'"] | FloatOrComplex) .
+// Conversion = "convert" "(" Type "," ConstValue ")" .
+func (p *parser) parseConversion(pkg *types.Package) (val constant.Value, typ types.Type) {
+	p.expectKeyword("convert")
+	p.expect('(')
+	typ = p.parseType(pkg)
+	p.expect(',')
+	val, _ = p.parseConstValue(pkg)
+	p.expect(')')
+	return
+}
+
+// ConstValue     = string | "false" | "true" | ["-"] (int ["'"] | FloatOrComplex) | Conversion .
 // FloatOrComplex = float ["i" | ("+"|"-") float "i"] .
-func (p *parser) parseConstValue() (val constant.Value, typ types.Type) {
+func (p *parser) parseConstValue(pkg *types.Package) (val constant.Value, typ types.Type) {
 	switch p.tok {
 	case scanner.String:
 		str := p.parseString()
@@ -262,6 +274,9 @@ func (p *parser) parseConstValue() (val constant.Value, typ types.Type) {
 		case "true":
 			b = true
 
+		case "convert":
+			return p.parseConversion(pkg)
+
 		default:
 			p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit)
 		}
@@ -348,7 +363,7 @@ func (p *parser) parseConst(pkg *types.Package) *types.Const {
 		typ = p.parseType(pkg)
 	}
 	p.expect('=')
-	val, vtyp := p.parseConstValue()
+	val, vtyp := p.parseConstValue(pkg)
 	if typ == nil {
 		typ = vtyp
 	}
@@ -723,7 +738,7 @@ func (p *parser) maybeCreatePackage() {
 	}
 }
 
-// InitDataDirective = "v1" ";" |
+// InitDataDirective = ( "v1" | "v2" ) ";" |
 //                     "priority" int ";" |
 //                     "init" { PackageInit } ";" |
 //                     "checksum" unquotedString ";" .
@@ -734,7 +749,8 @@ func (p *parser) parseInitDataDirective() {
 	}
 
 	switch p.lit {
-	case "v1":
+	case "v1", "v2":
+		p.version = p.lit
 		p.next()
 		p.expect(';')
 
@@ -766,8 +782,9 @@ func (p *parser) parseInitDataDirective() {
 }
 
 // Directive = InitDataDirective |
-//             "package" unquotedString ";" |
+//             "package" unquotedString [ unquotedString ] [ unquotedString ] ";" |
 //             "pkgpath" unquotedString ";" |
+//             "prefix" unquotedString ";" |
 //             "import" unquotedString unquotedString string ";" |
 //             "func" Func ";" |
 //             "type" Type ";" |
@@ -780,13 +797,17 @@ func (p *parser) parseDirective() {
 	}
 
 	switch p.lit {
-	case "v1", "priority", "init", "checksum":
+	case "v1", "v2", "priority", "init", "checksum":
 		p.parseInitDataDirective()
 
 	case "package":
 		p.next()
 		p.pkgname = p.parseUnquotedString()
 		p.maybeCreatePackage()
+		if p.version == "v2" && p.tok != ';' {
+			p.parseUnquotedString()
+			p.parseUnquotedString()
+		}
 		p.expect(';')
 
 	case "pkgpath":
@@ -795,6 +816,11 @@ func (p *parser) parseDirective() {
 		p.maybeCreatePackage()
 		p.expect(';')
 
+	case "prefix":
+		p.next()
+		p.pkgpath = p.parseUnquotedString()
+		p.expect(';')
+
 	case "import":
 		p.next()
 		pkgname := p.parseUnquotedString()
diff --git a/src/go/internal/gccgoimporter/testdata/conversions.go b/src/go/internal/gccgoimporter/testdata/conversions.go
new file mode 100644
index 0000000..653927a
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/conversions.go
@@ -0,0 +1,5 @@
+package conversions
+
+type Units string
+
+const Bits = Units("bits")
diff --git a/src/go/internal/gccgoimporter/testdata/conversions.gox b/src/go/internal/gccgoimporter/testdata/conversions.gox
new file mode 100644
index 0000000..7de6cda
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/conversions.gox
@@ -0,0 +1,6 @@
+v2;
+package conversions;
+prefix go;
+package conversions go.conversions go.conversions;
+const Bits <type 1 "Units" <type -16>> = convert(<type 1>, "bits");
+type <type 1>;
diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go
index 75c2d91..a8f3490 100644
--- a/src/go/internal/gcimporter/bimport.go
+++ b/src/go/internal/gcimporter/bimport.go
@@ -11,7 +11,9 @@ import (
 	"go/token"
 	"go/types"
 	"sort"
+	"strconv"
 	"strings"
+	"sync"
 	"unicode"
 	"unicode/utf8"
 )
@@ -21,7 +23,7 @@ type importer struct {
 	data    []byte
 	path    string
 	buf     []byte // for reading strings
-	version string
+	version int    // export format version
 
 	// object lists
 	strList       []string         // in order of appearance
@@ -33,6 +35,8 @@ type importer struct {
 	posInfoFormat bool
 	prevFile      string
 	prevLine      int
+	fset          *token.FileSet
+	files         map[string]*token.File
 
 	// debugging support
 	debugFormat bool
@@ -41,37 +45,74 @@ type importer struct {
 
 // BImportData imports a package from the serialized package data
 // and returns the number of bytes consumed and a reference to the package.
-// If data is obviously malformed, an error is returned but in
-// general it is not recommended to call BImportData on untrusted data.
-func BImportData(imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
+// If the export data version is not recognized or the format is otherwise
+// compromised, an error is returned.
+func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, _ *types.Package, err error) {
+	// catch panics and return them as errors
+	defer func() {
+		if e := recover(); e != nil {
+			// The package (filename) causing the problem is added to this
+			// error by a wrapper in the caller (Import in gcimporter.go).
+			err = fmt.Errorf("cannot import, possibly version skew (%v) - reinstall package", e)
+		}
+	}()
+
 	p := importer{
 		imports: imports,
 		data:    data,
 		path:    path,
+		version: -1,           // unknown version
 		strList: []string{""}, // empty string is mapped to 0
+		fset:    fset,
+		files:   make(map[string]*token.File),
+	}
+
+	// read version info
+	var versionstr string
+	if b := p.rawByte(); b == 'c' || b == 'd' {
+		// Go1.7 encoding; first byte encodes low-level
+		// encoding format (compact vs debug).
+		// For backward-compatibility only (avoid problems with
+		// old installed packages). Newly compiled packages use
+		// the extensible format string.
+		// TODO(gri) Remove this support eventually; after Go1.8.
+		if b == 'd' {
+			p.debugFormat = true
+		}
+		p.trackAllTypes = p.rawByte() == 'a'
+		p.posInfoFormat = p.int() != 0
+		versionstr = p.string()
+		if versionstr == "v1" {
+			p.version = 0
+		}
+	} else {
+		// Go1.8 extensible encoding
+		// read version string and extract version number (ignore anything after the version number)
+		versionstr = p.rawStringln(b)
+		if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" {
+			if v, err := strconv.Atoi(s[1]); err == nil && v > 0 {
+				p.version = v
+			}
+		}
 	}
 
-	// read low-level encoding format
-	switch format := p.rawByte(); format {
-	case 'c':
-		// compact format - nothing to do
-	case 'd':
-		p.debugFormat = true
+	// read version specific flags - extend as necessary
+	switch p.version {
+	// case 4:
+	// 	...
+	//	fallthrough
+	case 3, 2, 1:
+		p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
+		p.trackAllTypes = p.int() != 0
+		p.posInfoFormat = p.int() != 0
+	case 0:
+		// Go1.7 encoding format - nothing to do here
 	default:
-		return p.read, nil, fmt.Errorf("invalid encoding format in export data: got %q; want 'c' or 'd'", format)
+		errorf("unknown export format version %d (%q)", p.version, versionstr)
 	}
 
-	p.trackAllTypes = p.rawByte() == 'a'
-
-	p.posInfoFormat = p.int() != 0
-
 	// --- generic export data ---
 
-	p.version = p.string()
-	if p.version != "v0" && p.version != "v1" {
-		return p.read, nil, fmt.Errorf("unknown export data version: %s", p.version)
-	}
-
 	// populate typList with predeclared "known" types
 	p.typList = append(p.typList, predeclared...)
 
@@ -91,7 +132,7 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
 
 	// self-verification
 	if count := p.int(); count != objcount {
-		panic(fmt.Sprintf("got %d objects; want %d", objcount, count))
+		errorf("got %d objects; want %d", objcount, count)
 	}
 
 	// ignore compiler-specific import data
@@ -119,6 +160,10 @@ func BImportData(imports map[string]*types.Package, data []byte, path string) (i
 	return p.read, pkg, nil
 }
 
+func errorf(format string, args ...interface{}) {
+	panic(fmt.Sprintf(format, args...))
+}
+
 func (p *importer) pkg() *types.Package {
 	// if the package was seen before, i is its index (>= 0)
 	i := p.tagOrIndex()
@@ -128,7 +173,7 @@ func (p *importer) pkg() *types.Package {
 
 	// otherwise, i is the package tag (< 0)
 	if i != packageTag {
-		panic(fmt.Sprintf("unexpected package tag %d", i))
+		errorf("unexpected package tag %d", i)
 	}
 
 	// read package data
@@ -137,13 +182,13 @@ func (p *importer) pkg() *types.Package {
 
 	// we should never see an empty package name
 	if name == "" {
-		panic("empty package name in import")
+		errorf("empty package name in import")
 	}
 
 	// an empty path denotes the package we are currently importing;
 	// it must be the first package we see
 	if (path == "") != (len(p.pkgList) == 0) {
-		panic(fmt.Sprintf("package path %q for pkg index %d", path, len(p.pkgList)))
+		errorf("package path %q for pkg index %d", path, len(p.pkgList))
 	}
 
 	// if the package was imported before, use that one; otherwise create a new one
@@ -155,61 +200,104 @@ func (p *importer) pkg() *types.Package {
 		pkg = types.NewPackage(path, name)
 		p.imports[path] = pkg
 	} else if pkg.Name() != name {
-		panic(fmt.Sprintf("conflicting names %s and %s for package %q", pkg.Name(), name, path))
+		errorf("conflicting names %s and %s for package %q", pkg.Name(), name, path)
 	}
 	p.pkgList = append(p.pkgList, pkg)
 
 	return pkg
 }
 
+// objTag returns the tag value for each object kind.
+// obj must not be a *types.Alias.
+func objTag(obj types.Object) int {
+	switch obj.(type) {
+	case *types.Const:
+		return constTag
+	case *types.TypeName:
+		return typeTag
+	case *types.Var:
+		return varTag
+	case *types.Func:
+		return funcTag
+	// Aliases are not exported multiple times, thus we should not see them here.
+	default:
+		errorf("unexpected object: %v (%T)", obj, obj) // panics
+		panic("unreachable")
+	}
+}
+
+func sameObj(a, b types.Object) bool {
+	// Because unnamed types are not canonicalized, we cannot simply compare types for
+	// (pointer) identity.
+	// Ideally we'd check equality of constant values as well, but this is good enough.
+	return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type())
+}
+
 func (p *importer) declare(obj types.Object) {
 	pkg := obj.Pkg()
 	if alt := pkg.Scope().Insert(obj); alt != nil {
-		// This could only trigger if we import a (non-type) object a second time.
-		// This should never happen because 1) we only import a package once; and
-		// b) we ignore compiler-specific export data which may contain functions
-		// whose inlined function bodies refer to other functions that were already
-		// imported.
-		// (See also the comment in cmd/compile/internal/gc/bimport.go importer.obj,
-		// switch case importing functions).
-		panic(fmt.Sprintf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", alt, obj))
+		// This can only trigger if we import a (non-type) object a second time.
+		// Excluding aliases, this cannot happen because 1) we only import a package
+		// once; and b) we ignore compiler-specific export data which may contain
+		// functions whose inlined function bodies refer to other functions that
+		// were already imported.
+		// However, aliases require reexporting the original object, so we need
+		// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
+		// method importer.obj, switch case importing functions).
+		// Note that the original itself cannot be an alias.
+		if !sameObj(obj, alt) {
+			errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt)
+		}
 	}
 }
 
 func (p *importer) obj(tag int) {
 	switch tag {
 	case constTag:
-		p.pos()
+		pos := p.pos()
 		pkg, name := p.qualifiedName()
 		typ := p.typ(nil)
 		val := p.value()
-		p.declare(types.NewConst(token.NoPos, pkg, name, typ, val))
+		p.declare(types.NewConst(pos, pkg, name, typ, val))
 
 	case typeTag:
-		_ = p.typ(nil)
+		p.typ(nil)
 
 	case varTag:
-		p.pos()
+		pos := p.pos()
 		pkg, name := p.qualifiedName()
 		typ := p.typ(nil)
-		p.declare(types.NewVar(token.NoPos, pkg, name, typ))
+		p.declare(types.NewVar(pos, pkg, name, typ))
 
 	case funcTag:
-		p.pos()
+		pos := p.pos()
 		pkg, name := p.qualifiedName()
 		params, isddd := p.paramList()
 		result, _ := p.paramList()
 		sig := types.NewSignature(nil, params, result, isddd)
-		p.declare(types.NewFunc(token.NoPos, pkg, name, sig))
+		p.declare(types.NewFunc(pos, pkg, name, sig))
+
+	case aliasTag:
+		pos := p.pos()
+		name := p.string()
+		var orig types.Object
+		if pkg, name := p.qualifiedName(); pkg != nil {
+			orig = pkg.Scope().Lookup(name)
+		}
+		// Alias-related code. Keep for now.
+		_ = pos
+		_ = name
+		_ = orig
+		// p.declare(types.NewAlias(pos, p.pkgList[0], name, orig))
 
 	default:
-		panic(fmt.Sprintf("unexpected object tag %d", tag))
+		errorf("unexpected object tag %d", tag)
 	}
 }
 
-func (p *importer) pos() {
+func (p *importer) pos() token.Pos {
 	if !p.posInfoFormat {
-		return
+		return token.NoPos
 	}
 
 	file := p.prevFile
@@ -225,12 +313,45 @@ func (p *importer) pos() {
 	}
 	p.prevLine = line
 
-	// TODO(gri) register new position
+	// Synthesize a token.Pos
+
+	// Since we don't know the set of needed file positions, we
+	// reserve maxlines positions per file.
+	const maxlines = 64 * 1024
+	f := p.files[file]
+	if f == nil {
+		f = p.fset.AddFile(file, -1, maxlines)
+		p.files[file] = f
+		// Allocate the fake linebreak indices on first use.
+		// TODO(adonovan): opt: save ~512KB using a more complex scheme?
+		fakeLinesOnce.Do(func() {
+			fakeLines = make([]int, maxlines)
+			for i := range fakeLines {
+				fakeLines[i] = i
+			}
+		})
+		f.SetLines(fakeLines)
+	}
+
+	if line > maxlines {
+		line = 1
+	}
+
+	// Treat the file as if it contained only newlines
+	// and column=1: use the line number as the offset.
+	return f.Pos(line - 1)
 }
 
+var (
+	fakeLines     []int
+	fakeLinesOnce sync.Once
+)
+
 func (p *importer) qualifiedName() (pkg *types.Package, name string) {
 	name = p.string()
-	pkg = p.pkg()
+	if name != "" {
+		pkg = p.pkg()
+	}
 	return
 }
 
@@ -263,19 +384,19 @@ func (p *importer) typ(parent *types.Package) types.Type {
 	switch i {
 	case namedTag:
 		// read type object
-		p.pos()
+		pos := p.pos()
 		parent, name := p.qualifiedName()
 		scope := parent.Scope()
 		obj := scope.Lookup(name)
 
 		// if the object doesn't exist yet, create and insert it
 		if obj == nil {
-			obj = types.NewTypeName(token.NoPos, parent, name, nil)
+			obj = types.NewTypeName(pos, parent, name, nil)
 			scope.Insert(obj)
 		}
 
 		if _, ok := obj.(*types.TypeName); !ok {
-			panic(fmt.Sprintf("pkg = %s, name = %s => %s", parent, name, obj))
+			errorf("pkg = %s, name = %s => %s", parent, name, obj)
 		}
 
 		// associate new named type with obj if it doesn't exist yet
@@ -296,7 +417,7 @@ func (p *importer) typ(parent *types.Package) types.Type {
 		// read associated methods
 		for i := p.int(); i > 0; i-- {
 			// TODO(gri) replace this with something closer to fieldName
-			p.pos()
+			pos := p.pos()
 			name := p.string()
 			if !exported(name) {
 				p.pkg()
@@ -305,13 +426,10 @@ func (p *importer) typ(parent *types.Package) types.Type {
 			recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver?
 			params, isddd := p.paramList()
 			result, _ := p.paramList()
-
-			if p.version == "v1" {
-				p.int() // nointerface flag - discarded
-			}
+			p.int() // go:nointerface pragma - discarded
 
 			sig := types.NewSignature(recv.At(0), params, result, isddd)
-			t0.AddMethod(types.NewFunc(token.NoPos, parent, name, sig))
+			t0.AddMethod(types.NewFunc(pos, parent, name, sig))
 		}
 
 		return t
@@ -385,7 +503,7 @@ func (p *importer) typ(parent *types.Package) types.Type {
 
 		// no embedded interfaces with gc compiler
 		if p.int() != 0 {
-			panic("unexpected embedded interface")
+			errorf("unexpected embedded interface")
 		}
 
 		t := types.NewInterface(p.methodList(parent), nil)
@@ -421,14 +539,15 @@ func (p *importer) typ(parent *types.Package) types.Type {
 		case 3 /* Cboth */ :
 			dir = types.SendRecv
 		default:
-			panic(fmt.Sprintf("unexpected channel dir %d", d))
+			errorf("unexpected channel dir %d", d)
 		}
 		val := p.typ(parent)
 		*t = *types.NewChan(dir, val)
 		return t
 
 	default:
-		panic(fmt.Sprintf("unexpected type tag %d", i))
+		errorf("unexpected type tag %d", i) // panics
+		panic("unreachable")
 	}
 }
 
@@ -445,7 +564,7 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [
 }
 
 func (p *importer) field(parent *types.Package) *types.Var {
-	p.pos()
+	pos := p.pos()
 	pkg, name := p.fieldName(parent)
 	typ := p.typ(parent)
 
@@ -459,12 +578,12 @@ func (p *importer) field(parent *types.Package) *types.Var {
 		case *types.Named:
 			name = typ.Obj().Name()
 		default:
-			panic("anonymous field expected")
+			errorf("anonymous field expected")
 		}
 		anonymous = true
 	}
 
-	return types.NewField(token.NoPos, pkg, name, typ, anonymous)
+	return types.NewField(pos, pkg, name, typ, anonymous)
 }
 
 func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
@@ -478,28 +597,28 @@ func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
 }
 
 func (p *importer) method(parent *types.Package) *types.Func {
-	p.pos()
+	pos := p.pos()
 	pkg, name := p.fieldName(parent)
 	params, isddd := p.paramList()
 	result, _ := p.paramList()
 	sig := types.NewSignature(nil, params, result, isddd)
-	return types.NewFunc(token.NoPos, pkg, name, sig)
+	return types.NewFunc(pos, pkg, name, sig)
 }
 
 func (p *importer) fieldName(parent *types.Package) (*types.Package, string) {
+	name := p.string()
 	pkg := parent
 	if pkg == nil {
 		// use the imported package instead
 		pkg = p.pkgList[0]
 	}
-	name := p.string()
-	if name == "" {
-		return pkg, "" // anonymous
+	if p.version == 0 && name == "_" {
+		// version 0 didn't export a package for _ fields
+		return pkg, name
 	}
-	if name == "?" || name != "_" && !exported(name) {
-		// explicitly qualified field
+	if name != "" && !exported(name) {
 		if name == "?" {
-			name = "" // anonymous
+			name = ""
 		}
 		pkg = p.pkg()
 	}
@@ -538,7 +657,7 @@ func (p *importer) param(named bool) (*types.Var, bool) {
 	if named {
 		name = p.string()
 		if name == "" {
-			panic("expected named parameter")
+			errorf("expected named parameter")
 		}
 		if name != "_" {
 			pkg = p.pkg()
@@ -575,8 +694,11 @@ func (p *importer) value() constant.Value {
 		return constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
 	case stringTag:
 		return constant.MakeString(p.string())
+	case unknownTag:
+		return constant.MakeUnknown()
 	default:
-		panic(fmt.Sprintf("unexpected value tag %d", tag))
+		errorf("unexpected value tag %d", tag) // panics
+		panic("unreachable")
 	}
 }
 
@@ -639,7 +761,7 @@ func (p *importer) tagOrIndex() int {
 func (p *importer) int() int {
 	x := p.int64()
 	if int64(int(x)) != x {
-		panic("exported integer too large")
+		errorf("exported integer too large")
 	}
 	return int(x)
 }
@@ -678,24 +800,34 @@ func (p *importer) string() string {
 
 func (p *importer) marker(want byte) {
 	if got := p.rawByte(); got != want {
-		panic(fmt.Sprintf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read))
+		errorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read)
 	}
 
 	pos := p.read
 	if n := int(p.rawInt64()); n != pos {
-		panic(fmt.Sprintf("incorrect position: got %d; want %d", n, pos))
+		errorf("incorrect position: got %d; want %d", n, pos)
 	}
 }
 
-// rawInt64 should only be used by low-level decoders
+// rawInt64 should only be used by low-level decoders.
 func (p *importer) rawInt64() int64 {
 	i, err := binary.ReadVarint(p)
 	if err != nil {
-		panic(fmt.Sprintf("read error: %v", err))
+		errorf("read error: %v", err)
 	}
 	return i
 }
 
+// rawStringln should only be used to read the initial version string.
+func (p *importer) rawStringln(b byte) string {
+	p.buf = p.buf[:0]
+	for b != '\n' {
+		p.buf = append(p.buf, b)
+		b = p.rawByte()
+	}
+	return string(p.buf)
+}
+
 // needed for binary.ReadVarint in rawInt64
 func (p *importer) ReadByte() (byte, error) {
 	return p.rawByte(), nil
@@ -716,7 +848,7 @@ func (p *importer) rawByte() byte {
 		case '|':
 			// nothing to do
 		default:
-			panic("unexpected escape sequence in export data")
+			errorf("unexpected escape sequence in export data")
 		}
 	}
 	p.data = p.data[r:]
@@ -758,7 +890,11 @@ const (
 	fractionTag // not used by gc
 	complexTag
 	stringTag
+	nilTag     // only used by gc (appears in exported inlined function bodies)
 	unknownTag // not used by gc (only appears in packages with errors)
+
+	// Aliases
+	aliasTag
 )
 
 var predeclared = []types.Type{
diff --git a/src/go/internal/gcimporter/exportdata.go b/src/go/internal/gcimporter/exportdata.go
index 4c0d2fe..c12e459 100644
--- a/src/go/internal/gcimporter/exportdata.go
+++ b/src/go/internal/gcimporter/exportdata.go
@@ -8,7 +8,6 @@ package gcimporter
 
 import (
 	"bufio"
-	"errors"
 	"fmt"
 	"io"
 	"strconv"
@@ -29,7 +28,7 @@ func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
 	s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
 	size, err = strconv.Atoi(s)
 	if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
-		err = errors.New("invalid archive header")
+		err = fmt.Errorf("invalid archive header")
 		return
 	}
 	name = strings.TrimSpace(string(hdr[:16]))
@@ -46,6 +45,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) {
 	// Read first line to make sure this is an object file.
 	line, err := r.ReadSlice('\n')
 	if err != nil {
+		err = fmt.Errorf("can't find export data (%v)", err)
 		return
 	}
 
@@ -58,13 +58,14 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) {
 
 		// First entry should be __.PKGDEF.
 		if name != "__.PKGDEF" {
-			err = errors.New("go archive is missing __.PKGDEF")
+			err = fmt.Errorf("go archive is missing __.PKGDEF")
 			return
 		}
 
 		// Read first line of __.PKGDEF data, so that line
 		// is once again the first line of the input.
 		if line, err = r.ReadSlice('\n'); err != nil {
+			err = fmt.Errorf("can't find export data (%v)", err)
 			return
 		}
 	}
@@ -72,7 +73,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) {
 	// Now at __.PKGDEF in archive or still at beginning of file.
 	// Either way, line should begin with "go object ".
 	if !strings.HasPrefix(string(line), "go object ") {
-		err = errors.New("not a go object file")
+		err = fmt.Errorf("not a Go object file")
 		return
 	}
 
@@ -80,6 +81,7 @@ func FindExportData(r *bufio.Reader) (hdr string, err error) {
 	// Begins after first line starting with $$.
 	for line[0] != '$' {
 		if line, err = r.ReadSlice('\n'); err != nil {
+			err = fmt.Errorf("can't find export data (%v)", err)
 			return
 		}
 	}
diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go
index 2c6e676..f99f0f8 100644
--- a/src/go/internal/gcimporter/gcimporter.go
+++ b/src/go/internal/gcimporter/gcimporter.go
@@ -7,21 +7,14 @@ package gcimporter // import "go/internal/gcimporter"
 
 import (
 	"bufio"
-	"errors"
 	"fmt"
 	"go/build"
 	"go/token"
-	"io"
+	"go/types"
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"sort"
-	"strconv"
 	"strings"
-	"text/scanner"
-
-	exact "go/constant"
-	"go/types"
 )
 
 // debugging/development support
@@ -86,38 +79,6 @@ func FindPkg(path, srcDir string) (filename, id string) {
 	return
 }
 
-// ImportData imports a package by reading the gc-generated export data,
-// adds the corresponding package object to the packages map indexed by id,
-// and returns the object.
-//
-// The packages map must contains all packages already imported. The data
-// reader position must be the beginning of the export data section. The
-// filename is only used in error messages.
-//
-// If packages[id] contains the completely imported package, that package
-// can be used directly, and there is no need to call this function (but
-// there is also no harm but for extra time used).
-//
-func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) {
-	// support for parser error handling
-	defer func() {
-		switch r := recover().(type) {
-		case nil:
-			// nothing to do
-		case importError:
-			err = r
-		default:
-			panic(r) // internal error
-		}
-	}()
-
-	var p parser
-	p.init(filename, id, data, packages)
-	pkg = p.parseExport()
-
-	return
-}
-
 // Import imports a gc-generated package given its import path and srcDir, adds
 // the corresponding package object to the packages map, and returns the object.
 // The packages map must contain all packages already imported.
@@ -146,7 +107,7 @@ func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types
 		f.Close()
 		if err != nil {
 			// add file name to error
-			err = fmt.Errorf("reading export data: %s: %v", filename, err)
+			err = fmt.Errorf("%s: %v", filename, err)
 		}
 	}()
 
@@ -158,12 +119,15 @@ func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types
 
 	switch hdr {
 	case "$$\n":
-		return ImportData(packages, filename, id, buf)
+		err = fmt.Errorf("import %q: old export format no longer supported (recompile library)", path)
 	case "$$B\n":
 		var data []byte
 		data, err = ioutil.ReadAll(buf)
 		if err == nil {
-			_, pkg, err = BImportData(packages, data, id)
+			// TODO(gri): allow clients of go/importer to provide a FileSet.
+			// Or, define a new standard go/types/gcexportdata package.
+			fset := token.NewFileSet()
+			_, pkg, err = BImportData(fset, packages, data, id)
 			return
 		}
 	default:
@@ -173,312 +137,6 @@ func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types
 	return
 }
 
-// ----------------------------------------------------------------------------
-// Parser
-
-// TODO(gri) Imported objects don't have position information.
-//           Ideally use the debug table line info; alternatively
-//           create some fake position (or the position of the
-//           import). That way error messages referring to imported
-//           objects can print meaningful information.
-
-// parser parses the exports inside a gc compiler-produced
-// object/archive file and populates its scope with the results.
-type parser struct {
-	scanner    scanner.Scanner
-	tok        rune                      // current token
-	lit        string                    // literal string; only valid for Ident, Int, String tokens
-	id         string                    // package id of imported package
-	sharedPkgs map[string]*types.Package // package id -> package object (across importer)
-	localPkgs  map[string]*types.Package // package id -> package object (just this package)
-}
-
-func (p *parser) init(filename, id string, src io.Reader, packages map[string]*types.Package) {
-	p.scanner.Init(src)
-	p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
-	p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
-	p.scanner.Whitespace = 1<<'\t' | 1<<' '
-	p.scanner.Filename = filename // for good error messages
-	p.next()
-	p.id = id
-	p.sharedPkgs = packages
-	if debug {
-		// check consistency of packages map
-		for _, pkg := range packages {
-			if pkg.Name() == "" {
-				fmt.Printf("no package name for %s\n", pkg.Path())
-			}
-		}
-	}
-}
-
-func (p *parser) next() {
-	p.tok = p.scanner.Scan()
-	switch p.tok {
-	case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·':
-		p.lit = p.scanner.TokenText()
-	default:
-		p.lit = ""
-	}
-	if debug {
-		fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
-	}
-}
-
-func declTypeName(pkg *types.Package, name string) *types.TypeName {
-	scope := pkg.Scope()
-	if obj := scope.Lookup(name); obj != nil {
-		return obj.(*types.TypeName)
-	}
-	obj := types.NewTypeName(token.NoPos, pkg, name, nil)
-	// a named type may be referred to before the underlying type
-	// is known - set it up
-	types.NewNamed(obj, nil, nil)
-	scope.Insert(obj)
-	return obj
-}
-
-// ----------------------------------------------------------------------------
-// Error handling
-
-// Internal errors are boxed as importErrors.
-type importError struct {
-	pos scanner.Position
-	err error
-}
-
-func (e importError) Error() string {
-	return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
-}
-
-func (p *parser) error(err interface{}) {
-	if s, ok := err.(string); ok {
-		err = errors.New(s)
-	}
-	// panic with a runtime.Error if err is not an error
-	panic(importError{p.scanner.Pos(), err.(error)})
-}
-
-func (p *parser) errorf(format string, args ...interface{}) {
-	p.error(fmt.Sprintf(format, args...))
-}
-
-func (p *parser) expect(tok rune) string {
-	lit := p.lit
-	if p.tok != tok {
-		p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
-	}
-	p.next()
-	return lit
-}
-
-func (p *parser) expectSpecial(tok string) {
-	sep := 'x' // not white space
-	i := 0
-	for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' {
-		sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
-		p.next()
-		i++
-	}
-	if i < len(tok) {
-		p.errorf("expected %q, got %q", tok, tok[0:i])
-	}
-}
-
-func (p *parser) expectKeyword(keyword string) {
-	lit := p.expect(scanner.Ident)
-	if lit != keyword {
-		p.errorf("expected keyword %s, got %q", keyword, lit)
-	}
-}
-
-// ----------------------------------------------------------------------------
-// Qualified and unqualified names
-
-// PackageId = string_lit .
-//
-func (p *parser) parsePackageId() string {
-	id, err := strconv.Unquote(p.expect(scanner.String))
-	if err != nil {
-		p.error(err)
-	}
-	// id == "" stands for the imported package id
-	// (only known at time of package installation)
-	if id == "" {
-		id = p.id
-	}
-	return id
-}
-
-// PackageName = ident .
-//
-func (p *parser) parsePackageName() string {
-	return p.expect(scanner.Ident)
-}
-
-// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
-func (p *parser) parseDotIdent() string {
-	ident := ""
-	if p.tok != scanner.Int {
-		sep := 'x' // not white space
-		for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' {
-			ident += p.lit
-			sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
-			p.next()
-		}
-	}
-	if ident == "" {
-		p.expect(scanner.Ident) // use expect() for error handling
-	}
-	return ident
-}
-
-// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) .
-//
-func (p *parser) parseQualifiedName() (id, name string) {
-	p.expect('@')
-	id = p.parsePackageId()
-	p.expect('.')
-	// Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields.
-	if p.tok == '?' {
-		p.next()
-	} else {
-		name = p.parseDotIdent()
-	}
-	return
-}
-
-// getPkg returns the package for a given id. If the package is
-// not found, create the package and add it to the p.localPkgs
-// and p.sharedPkgs maps. name is the (expected) name of the
-// package. If name == "", the package name is expected to be
-// set later via an import clause in the export data.
-//
-// id identifies a package, usually by a canonical package path like
-// "encoding/json" but possibly by a non-canonical import path like
-// "./json".
-//
-func (p *parser) getPkg(id, name string) *types.Package {
-	// package unsafe is not in the packages maps - handle explicitly
-	if id == "unsafe" {
-		return types.Unsafe
-	}
-
-	pkg := p.localPkgs[id]
-	if pkg == nil {
-		// first import of id from this package
-		pkg = p.sharedPkgs[id]
-		if pkg == nil {
-			// first import of id by this importer;
-			// add (possibly unnamed) pkg to shared packages
-			pkg = types.NewPackage(id, name)
-			p.sharedPkgs[id] = pkg
-		}
-		// add (possibly unnamed) pkg to local packages
-		if p.localPkgs == nil {
-			p.localPkgs = make(map[string]*types.Package)
-		}
-		p.localPkgs[id] = pkg
-	} else if name != "" {
-		// package exists already and we have an expected package name;
-		// make sure names match or set package name if necessary
-		if pname := pkg.Name(); pname == "" {
-			pkg.SetName(name)
-		} else if pname != name {
-			p.errorf("%s package name mismatch: %s (given) vs %s (expected)", id, pname, name)
-		}
-	}
-	return pkg
-}
-
-// parseExportedName is like parseQualifiedName, but
-// the package id is resolved to an imported *types.Package.
-//
-func (p *parser) parseExportedName() (pkg *types.Package, name string) {
-	id, name := p.parseQualifiedName()
-	pkg = p.getPkg(id, "")
-	return
-}
-
-// ----------------------------------------------------------------------------
-// Types
-
-// BasicType = identifier .
-//
-func (p *parser) parseBasicType() types.Type {
-	id := p.expect(scanner.Ident)
-	obj := types.Universe.Lookup(id)
-	if obj, ok := obj.(*types.TypeName); ok {
-		return obj.Type()
-	}
-	p.errorf("not a basic type: %s", id)
-	return nil
-}
-
-// ArrayType = "[" int_lit "]" Type .
-//
-func (p *parser) parseArrayType(parent *types.Package) types.Type {
-	// "[" already consumed and lookahead known not to be "]"
-	lit := p.expect(scanner.Int)
-	p.expect(']')
-	elem := p.parseType(parent)
-	n, err := strconv.ParseInt(lit, 10, 64)
-	if err != nil {
-		p.error(err)
-	}
-	return types.NewArray(elem, n)
-}
-
-// MapType = "map" "[" Type "]" Type .
-//
-func (p *parser) parseMapType(parent *types.Package) types.Type {
-	p.expectKeyword("map")
-	p.expect('[')
-	key := p.parseType(parent)
-	p.expect(']')
-	elem := p.parseType(parent)
-	return types.NewMap(key, elem)
-}
-
-// Name = identifier | "?" | QualifiedName .
-//
-// For unqualified and anonymous names, the returned package is the parent
-// package unless parent == nil, in which case the returned package is the
-// package being imported. (The parent package is not nil if the the name
-// is an unqualified struct field or interface method name belonging to a
-// type declared in another package.)
-//
-// For qualified names, the returned package is nil (and not created if
-// it doesn't exist yet) unless materializePkg is set (which creates an
-// unnamed package with valid package path). In the latter case, a
-// subsequent import clause is expected to provide a name for the package.
-//
-func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) {
-	pkg = parent
-	if pkg == nil {
-		pkg = p.sharedPkgs[p.id]
-	}
-	switch p.tok {
-	case scanner.Ident:
-		name = p.lit
-		p.next()
-	case '?':
-		// anonymous
-		p.next()
-	case '@':
-		// exported name prefixed with package path
-		pkg = nil
-		var id string
-		id, name = p.parseQualifiedName()
-		if materializePkg {
-			pkg = p.getPkg(id, "")
-		}
-	default:
-		p.error("name expected")
-	}
-	return
-}
-
 func deref(typ types.Type) types.Type {
 	if p, _ := typ.(*types.Pointer); p != nil {
 		return p.Elem()
@@ -486,531 +144,6 @@ func deref(typ types.Type) types.Type {
 	return typ
 }
 
-// Field = Name Type [ string_lit ] .
-//
-func (p *parser) parseField(parent *types.Package) (*types.Var, string) {
-	pkg, name := p.parseName(parent, true)
-	typ := p.parseType(parent)
-	anonymous := false
-	if name == "" {
-		// anonymous field - typ must be T or *T and T must be a type name
-		switch typ := deref(typ).(type) {
-		case *types.Basic: // basic types are named types
-			pkg = nil // objects defined in Universe scope have no package
-			name = typ.Name()
-		case *types.Named:
-			name = typ.Obj().Name()
-		default:
-			p.errorf("anonymous field expected")
-		}
-		anonymous = true
-	}
-	tag := ""
-	if p.tok == scanner.String {
-		s := p.expect(scanner.String)
-		var err error
-		tag, err = strconv.Unquote(s)
-		if err != nil {
-			p.errorf("invalid struct tag %s: %s", s, err)
-		}
-	}
-	return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag
-}
-
-// StructType = "struct" "{" [ FieldList ] "}" .
-// FieldList  = Field { ";" Field } .
-//
-func (p *parser) parseStructType(parent *types.Package) types.Type {
-	var fields []*types.Var
-	var tags []string
-
-	p.expectKeyword("struct")
-	p.expect('{')
-	for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
-		if i > 0 {
-			p.expect(';')
-		}
-		fld, tag := p.parseField(parent)
-		if tag != "" && tags == nil {
-			tags = make([]string, i)
-		}
-		if tags != nil {
-			tags = append(tags, tag)
-		}
-		fields = append(fields, fld)
-	}
-	p.expect('}')
-
-	return types.NewStruct(fields, tags)
-}
-
-// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
-//
-func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
-	_, name := p.parseName(nil, false)
-	// remove gc-specific parameter numbering
-	if i := strings.Index(name, "·"); i >= 0 {
-		name = name[:i]
-	}
-	if p.tok == '.' {
-		p.expectSpecial("...")
-		isVariadic = true
-	}
-	typ := p.parseType(nil)
-	if isVariadic {
-		typ = types.NewSlice(typ)
-	}
-	// ignore argument tag (e.g. "noescape")
-	if p.tok == scanner.String {
-		p.next()
-	}
-	// TODO(gri) should we provide a package?
-	par = types.NewVar(token.NoPos, nil, name, typ)
-	return
-}
-
-// Parameters    = "(" [ ParameterList ] ")" .
-// ParameterList = { Parameter "," } Parameter .
-//
-func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) {
-	p.expect('(')
-	for p.tok != ')' && p.tok != scanner.EOF {
-		if len(list) > 0 {
-			p.expect(',')
-		}
-		par, variadic := p.parseParameter()
-		list = append(list, par)
-		if variadic {
-			if isVariadic {
-				p.error("... not on final argument")
-			}
-			isVariadic = true
-		}
-	}
-	p.expect(')')
-
-	return
-}
-
-// Signature = Parameters [ Result ] .
-// Result    = Type | Parameters .
-//
-func (p *parser) parseSignature(recv *types.Var) *types.Signature {
-	params, isVariadic := p.parseParameters()
-
-	// optional result type
-	var results []*types.Var
-	if p.tok == '(' {
-		var variadic bool
-		results, variadic = p.parseParameters()
-		if variadic {
-			p.error("... not permitted on result type")
-		}
-	}
-
-	return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic)
-}
-
-// InterfaceType = "interface" "{" [ MethodList ] "}" .
-// MethodList    = Method { ";" Method } .
-// Method        = Name Signature .
-//
-// The methods of embedded interfaces are always "inlined"
-// by the compiler and thus embedded interfaces are never
-// visible in the export data.
-//
-func (p *parser) parseInterfaceType(parent *types.Package) types.Type {
-	var methods []*types.Func
-
-	p.expectKeyword("interface")
-	p.expect('{')
-	for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
-		if i > 0 {
-			p.expect(';')
-		}
-		pkg, name := p.parseName(parent, true)
-		sig := p.parseSignature(nil)
-		methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
-	}
-	p.expect('}')
-
-	// Complete requires the type's embedded interfaces to be fully defined,
-	// but we do not define any
-	return types.NewInterface(methods, nil).Complete()
-}
-
-// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
-//
-func (p *parser) parseChanType(parent *types.Package) types.Type {
-	dir := types.SendRecv
-	if p.tok == scanner.Ident {
-		p.expectKeyword("chan")
-		if p.tok == '<' {
-			p.expectSpecial("<-")
-			dir = types.SendOnly
-		}
-	} else {
-		p.expectSpecial("<-")
-		p.expectKeyword("chan")
-		dir = types.RecvOnly
-	}
-	elem := p.parseType(parent)
-	return types.NewChan(dir, elem)
-}
-
-// Type =
-//	BasicType | TypeName | ArrayType | SliceType | StructType |
-//      PointerType | FuncType | InterfaceType | MapType | ChanType |
-//      "(" Type ")" .
-//
-// BasicType   = ident .
-// TypeName    = ExportedName .
-// SliceType   = "[" "]" Type .
-// PointerType = "*" Type .
-// FuncType    = "func" Signature .
-//
-func (p *parser) parseType(parent *types.Package) types.Type {
-	switch p.tok {
-	case scanner.Ident:
-		switch p.lit {
-		default:
-			return p.parseBasicType()
-		case "struct":
-			return p.parseStructType(parent)
-		case "func":
-			// FuncType
-			p.next()
-			return p.parseSignature(nil)
-		case "interface":
-			return p.parseInterfaceType(parent)
-		case "map":
-			return p.parseMapType(parent)
-		case "chan":
-			return p.parseChanType(parent)
-		}
-	case '@':
-		// TypeName
-		pkg, name := p.parseExportedName()
-		return declTypeName(pkg, name).Type()
-	case '[':
-		p.next() // look ahead
-		if p.tok == ']' {
-			// SliceType
-			p.next()
-			return types.NewSlice(p.parseType(parent))
-		}
-		return p.parseArrayType(parent)
-	case '*':
-		// PointerType
-		p.next()
-		return types.NewPointer(p.parseType(parent))
-	case '<':
-		return p.parseChanType(parent)
-	case '(':
-		// "(" Type ")"
-		p.next()
-		typ := p.parseType(parent)
-		p.expect(')')
-		return typ
-	}
-	p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
-	return nil
-}
-
-// ----------------------------------------------------------------------------
-// Declarations
-
-// ImportDecl = "import" PackageName PackageId .
-//
-func (p *parser) parseImportDecl() {
-	p.expectKeyword("import")
-	name := p.parsePackageName()
-	p.getPkg(p.parsePackageId(), name)
-}
-
-// int_lit = [ "+" | "-" ] { "0" ... "9" } .
-//
-func (p *parser) parseInt() string {
-	s := ""
-	switch p.tok {
-	case '-':
-		s = "-"
-		p.next()
-	case '+':
-		p.next()
-	}
-	return s + p.expect(scanner.Int)
-}
-
-// number = int_lit [ "p" int_lit ] .
-//
-func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) {
-	// mantissa
-	mant := exact.MakeFromLiteral(p.parseInt(), token.INT, 0)
-	if mant == nil {
-		panic("invalid mantissa")
-	}
-
-	if p.lit == "p" {
-		// exponent (base 2)
-		p.next()
-		exp, err := strconv.ParseInt(p.parseInt(), 10, 0)
-		if err != nil {
-			p.error(err)
-		}
-		if exp < 0 {
-			denom := exact.MakeInt64(1)
-			denom = exact.Shift(denom, token.SHL, uint(-exp))
-			typ = types.Typ[types.UntypedFloat]
-			val = exact.BinaryOp(mant, token.QUO, denom)
-			return
-		}
-		if exp > 0 {
-			mant = exact.Shift(mant, token.SHL, uint(exp))
-		}
-		typ = types.Typ[types.UntypedFloat]
-		val = mant
-		return
-	}
-
-	typ = types.Typ[types.UntypedInt]
-	val = mant
-	return
-}
-
-// ConstDecl   = "const" ExportedName [ Type ] "=" Literal .
-// Literal     = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit .
-// bool_lit    = "true" | "false" .
-// complex_lit = "(" float_lit "+" float_lit "i" ")" .
-// rune_lit    = "(" int_lit "+" int_lit ")" .
-// string_lit  = `"` { unicode_char } `"` .
-//
-func (p *parser) parseConstDecl() {
-	p.expectKeyword("const")
-	pkg, name := p.parseExportedName()
-
-	var typ0 types.Type
-	if p.tok != '=' {
-		// constant types are never structured - no need for parent type
-		typ0 = p.parseType(nil)
-	}
-
-	p.expect('=')
-	var typ types.Type
-	var val exact.Value
-	switch p.tok {
-	case scanner.Ident:
-		// bool_lit
-		if p.lit != "true" && p.lit != "false" {
-			p.error("expected true or false")
-		}
-		typ = types.Typ[types.UntypedBool]
-		val = exact.MakeBool(p.lit == "true")
-		p.next()
-
-	case '-', scanner.Int:
-		// int_lit
-		typ, val = p.parseNumber()
-
-	case '(':
-		// complex_lit or rune_lit
-		p.next()
-		if p.tok == scanner.Char {
-			p.next()
-			p.expect('+')
-			typ = types.Typ[types.UntypedRune]
-			_, val = p.parseNumber()
-			p.expect(')')
-			break
-		}
-		_, re := p.parseNumber()
-		p.expect('+')
-		_, im := p.parseNumber()
-		p.expectKeyword("i")
-		p.expect(')')
-		typ = types.Typ[types.UntypedComplex]
-		val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
-
-	case scanner.Char:
-		// rune_lit
-		typ = types.Typ[types.UntypedRune]
-		val = exact.MakeFromLiteral(p.lit, token.CHAR, 0)
-		p.next()
-
-	case scanner.String:
-		// string_lit
-		typ = types.Typ[types.UntypedString]
-		val = exact.MakeFromLiteral(p.lit, token.STRING, 0)
-		p.next()
-
-	default:
-		p.errorf("expected literal got %s", scanner.TokenString(p.tok))
-	}
-
-	if typ0 == nil {
-		typ0 = typ
-	}
-
-	pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val))
-}
-
-// TypeDecl = "type" ExportedName Type .
-//
-func (p *parser) parseTypeDecl() {
-	p.expectKeyword("type")
-	pkg, name := p.parseExportedName()
-	obj := declTypeName(pkg, name)
-
-	// The type object may have been imported before and thus already
-	// have a type associated with it. We still need to parse the type
-	// structure, but throw it away if the object already has a type.
-	// This ensures that all imports refer to the same type object for
-	// a given type declaration.
-	typ := p.parseType(pkg)
-
-	if name := obj.Type().(*types.Named); name.Underlying() == nil {
-		name.SetUnderlying(typ)
-	}
-}
-
-// VarDecl = "var" ExportedName Type .
-//
-func (p *parser) parseVarDecl() {
-	p.expectKeyword("var")
-	pkg, name := p.parseExportedName()
-	typ := p.parseType(pkg)
-	pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
-}
-
-// Func = Signature [ Body ] .
-// Body = "{" ... "}" .
-//
-func (p *parser) parseFunc(recv *types.Var) *types.Signature {
-	sig := p.parseSignature(recv)
-	if p.tok == '{' {
-		p.next()
-		for i := 1; i > 0; p.next() {
-			switch p.tok {
-			case '{':
-				i++
-			case '}':
-				i--
-			}
-		}
-	}
-	return sig
-}
-
-// MethodDecl = "func" Receiver Name Func .
-// Receiver   = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
-//
-func (p *parser) parseMethodDecl() {
-	// "func" already consumed
-	p.expect('(')
-	recv, _ := p.parseParameter() // receiver
-	p.expect(')')
-
-	// determine receiver base type object
-	base := deref(recv.Type()).(*types.Named)
-
-	// parse method name, signature, and possibly inlined body
-	_, name := p.parseName(nil, false)
-	sig := p.parseFunc(recv)
-
-	// methods always belong to the same package as the base type object
-	pkg := base.Obj().Pkg()
-
-	// add method to type unless type was imported before
-	// and method exists already
-	// TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small.
-	base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
-}
-
-// FuncDecl = "func" ExportedName Func .
-//
-func (p *parser) parseFuncDecl() {
-	// "func" already consumed
-	pkg, name := p.parseExportedName()
-	typ := p.parseFunc(nil)
-	pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ))
-}
-
-// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
-//
-func (p *parser) parseDecl() {
-	if p.tok == scanner.Ident {
-		switch p.lit {
-		case "import":
-			p.parseImportDecl()
-		case "const":
-			p.parseConstDecl()
-		case "type":
-			p.parseTypeDecl()
-		case "var":
-			p.parseVarDecl()
-		case "func":
-			p.next() // look ahead
-			if p.tok == '(' {
-				p.parseMethodDecl()
-			} else {
-				p.parseFuncDecl()
-			}
-		}
-	}
-	p.expect('\n')
-}
-
-// ----------------------------------------------------------------------------
-// Export
-
-// Export        = "PackageClause { Decl } "$$" .
-// PackageClause = "package" PackageName [ "safe" ] "\n" .
-//
-func (p *parser) parseExport() *types.Package {
-	p.expectKeyword("package")
-	name := p.parsePackageName()
-	if p.tok == scanner.Ident && p.lit == "safe" {
-		// package was compiled with -u option - ignore
-		p.next()
-	}
-	p.expect('\n')
-
-	pkg := p.getPkg(p.id, name)
-
-	for p.tok != '$' && p.tok != scanner.EOF {
-		p.parseDecl()
-	}
-
-	if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
-		// don't call next()/expect() since reading past the
-		// export data may cause scanner errors (e.g. NUL chars)
-		p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
-	}
-
-	if n := p.scanner.ErrorCount; n != 0 {
-		p.errorf("expected no scanner errors, got %d", n)
-	}
-
-	// Record all locally referenced packages as imports.
-	var imports []*types.Package
-	for id, pkg2 := range p.localPkgs {
-		if pkg2.Name() == "" {
-			p.errorf("%s package has no name", id)
-		}
-		if id == p.id {
-			continue // avoid self-edge
-		}
-		imports = append(imports, pkg2)
-	}
-	sort.Sort(byPath(imports))
-	pkg.SetImports(imports)
-
-	// package was imported completely and without errors
-	pkg.MarkComplete()
-
-	return pkg
-}
-
 type byPath []*types.Package
 
 func (a byPath) Len() int           { return len(a) }
diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go
index d8c5bcf..a0697fa 100644
--- a/src/go/internal/gcimporter/gcimporter_test.go
+++ b/src/go/internal/gcimporter/gcimporter_test.go
@@ -5,6 +5,7 @@
 package gcimporter
 
 import (
+	"bytes"
 	"fmt"
 	"internal/testenv"
 	"io/ioutil"
@@ -34,22 +35,7 @@ func skipSpecialPlatforms(t *testing.T) {
 }
 
 func compile(t *testing.T, dirname, filename string) string {
-	testenv.MustHaveGoBuild(t)
-	cmd := exec.Command("go", "tool", "compile", filename)
-	cmd.Dir = dirname
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		t.Logf("%s", out)
-		t.Fatalf("go tool compile %s failed: %s", filename, err)
-	}
-	// filename should end with ".go"
-	return filepath.Join(dirname, filename[:len(filename)-2]+"o")
-}
-
-// TODO(gri) Remove this function once we switched to new export format by default.
-func compileNewExport(t *testing.T, dirname, filename string) string {
-	testenv.MustHaveGoBuild(t)
-	cmd := exec.Command("go", "tool", "compile", "-newexport", filename)
+	cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", filename)
 	cmd.Dir = dirname
 	out, err := cmd.CombinedOutput()
 	if err != nil {
@@ -121,6 +107,8 @@ func TestImportTestdata(t *testing.T) {
 		// additional packages that are not strictly required for
 		// import processing alone (they are exported to err "on
 		// the safe side").
+		// TODO(gri) update the want list to be precise, now that
+		// the textual export data is gone.
 		got := fmt.Sprint(pkg.Imports())
 		for _, want := range []string{"go/ast", "go/token"} {
 			if !strings.Contains(got, want) {
@@ -130,27 +118,66 @@ func TestImportTestdata(t *testing.T) {
 	}
 }
 
-// TODO(gri) Remove this function once we switched to new export format by default
-//           (and update the comment and want list in TestImportTestdata).
-func TestImportTestdataNewExport(t *testing.T) {
+func TestVersionHandling(t *testing.T) {
+	skipSpecialPlatforms(t) // we really only need to exclude nacl platforms, but this is fine
+
 	// This package only handles gc export data.
 	if runtime.Compiler != "gc" {
 		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
 		return
 	}
 
-	if outFn := compileNewExport(t, "testdata", "exports.go"); outFn != "" {
-		defer os.Remove(outFn)
+	const dir = "./testdata/versions"
+	list, err := ioutil.ReadDir(dir)
+	if err != nil {
+		t.Fatal(err)
 	}
 
-	if pkg := testPath(t, "./testdata/exports", "."); pkg != nil {
-		// The package's Imports list must include all packages
-		// explicitly imported by exports.go, plus all packages
-		// referenced indirectly via exported objects in exports.go.
-		want := `[package ast ("go/ast") package token ("go/token")]`
-		got := fmt.Sprint(pkg.Imports())
-		if got != want {
-			t.Errorf(`Package("exports").Imports() = %s, want %s`, got, want)
+	for _, f := range list {
+		name := f.Name()
+		if !strings.HasSuffix(name, ".a") {
+			continue // not a package file
+		}
+		if strings.Contains(name, "corrupted") {
+			continue // don't process a leftover corrupted file
+		}
+		pkgpath := "./" + name[:len(name)-2]
+
+		// test that export data can be imported
+		_, err := Import(make(map[string]*types.Package), pkgpath, dir)
+		if err != nil {
+			t.Errorf("import %q failed: %v", pkgpath, err)
+			continue
+		}
+
+		// create file with corrupted export data
+		// 1) read file
+		data, err := ioutil.ReadFile(filepath.Join(dir, name))
+		if err != nil {
+			t.Fatal(err)
+		}
+		// 2) find export data
+		i := bytes.Index(data, []byte("\n$$B\n")) + 5
+		j := bytes.Index(data[i:], []byte("\n$$\n")) + i
+		if i < 0 || j < 0 || i > j {
+			t.Fatalf("export data section not found (i = %d, j = %d)", i, j)
+		}
+		// 3) corrupt the data (increment every 7th byte)
+		for k := j - 13; k >= i; k -= 7 {
+			data[k]++
+		}
+		// 4) write the file
+		pkgpath += "_corrupted"
+		filename := filepath.Join(dir, pkgpath) + ".a"
+		ioutil.WriteFile(filename, data, 0666)
+		defer os.Remove(filename)
+
+		// test that importing the corrupted file results in an error
+		_, err = Import(make(map[string]*types.Package), pkgpath, dir)
+		if err == nil {
+			t.Errorf("import corrupted %q succeeded", pkgpath)
+		} else if msg := err.Error(); !strings.Contains(msg, "version skew") {
+			t.Errorf("import %q error incorrect (%s)", pkgpath, msg)
 		}
 	}
 }
diff --git a/src/go/internal/gcimporter/testdata/exports.go b/src/go/internal/gcimporter/testdata/exports.go
index 8ee28b0..9a0273b 100644
--- a/src/go/internal/gcimporter/testdata/exports.go
+++ b/src/go/internal/gcimporter/testdata/exports.go
@@ -7,9 +7,7 @@
 
 package exports
 
-import (
-	"go/ast"
-)
+import "go/ast"
 
 // Issue 3682: Correctly read dotted identifiers from export data.
 const init1 = 0
@@ -77,7 +75,8 @@ type (
 
 var (
 	V0 int
-	V1 = -991.0
+	V1         = -991.0
+	V2 float32 = 1.2
 )
 
 func F1()         {}
diff --git a/src/go/internal/gcimporter/testdata/versions/test.go b/src/go/internal/gcimporter/testdata/versions/test.go
new file mode 100644
index 0000000..ac9c968
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/versions/test.go
@@ -0,0 +1,25 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// To create a test case for a new export format version,
+// build this package with the latest compiler and store
+// the resulting .a file appropriately named in the versions
+// directory. The VersionHandling test will pick it up.
+//
+// In the testdata/versions:
+//
+// go build -o test_go1.$X_$Y.a test.go
+//
+// with $X = Go version and $Y = export format version.
+//
+// Make sure this source is extended such that it exercises
+// whatever export format change has taken place.
+
+package test
+
+// Any release before and including Go 1.7 didn't encode
+// the package for a blank struct field.
+type BlankField struct {
+	_ int
+}
diff --git a/src/go/internal/gcimporter/testdata/versions/test_go1.7_0.a b/src/go/internal/gcimporter/testdata/versions/test_go1.7_0.a
new file mode 100644
index 0000000..edb6c3f
Binary files /dev/null and b/src/go/internal/gcimporter/testdata/versions/test_go1.7_0.a differ
diff --git a/src/go/internal/gcimporter/testdata/versions/test_go1.7_1.a b/src/go/internal/gcimporter/testdata/versions/test_go1.7_1.a
new file mode 100644
index 0000000..554d04a
Binary files /dev/null and b/src/go/internal/gcimporter/testdata/versions/test_go1.7_1.a differ
diff --git a/src/go/printer/printer.go b/src/go/printer/printer.go
index a3eaa66..eabf23e 100644
--- a/src/go/printer/printer.go
+++ b/src/go/printer/printer.go
@@ -712,6 +712,16 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, dropped
 	return
 }
 
+// containsLinebreak reports whether the whitespace buffer contains any line breaks.
+func (p *printer) containsLinebreak() bool {
+	for _, ch := range p.wsbuf {
+		if ch == newline || ch == formfeed {
+			return true
+		}
+	}
+	return false
+}
+
 // intersperseComments consumes all comments that appear before the next token
 // tok and prints it together with the buffered whitespace (i.e., the whitespace
 // that needs to be written before the next token). A heuristic is used to mix
@@ -730,23 +740,31 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
 	}
 
 	if last != nil {
-		// if the last comment is a /*-style comment and the next item
+		// If the last comment is a /*-style comment and the next item
 		// follows on the same line but is not a comma, and not a "closing"
 		// token immediately following its corresponding "opening" token,
-		// add an extra blank for separation unless explicitly disabled
+		// add an extra separator unless explicitly disabled. Use a blank
+		// as separator unless we have pending linebreaks and they are not
+		// disabled, in which case we want a linebreak (issue 15137).
+		needsLinebreak := false
 		if p.mode&noExtraBlank == 0 &&
 			last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line &&
 			tok != token.COMMA &&
 			(tok != token.RPAREN || p.prevOpen == token.LPAREN) &&
 			(tok != token.RBRACK || p.prevOpen == token.LBRACK) {
-			p.writeByte(' ', 1)
+			if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 {
+				needsLinebreak = true
+			} else {
+				p.writeByte(' ', 1)
+			}
+		}
+		// Ensure that there is a line break after a //-style comment,
+		// before EOF, and before a closing '}' unless explicitly disabled.
+		if last.Text[1] == '/' ||
+			tok == token.EOF ||
+			tok == token.RBRACE && p.mode&noExtraLinebreak == 0 {
+			needsLinebreak = true
 		}
-		// ensure that there is a line break after a //-style comment,
-		// before a closing '}' unless explicitly disabled, or at eof
-		needsLinebreak :=
-			last.Text[1] == '/' ||
-				tok == token.RBRACE && p.mode&noExtraLinebreak == 0 ||
-				tok == token.EOF
 		return p.writeCommentSuffix(needsLinebreak)
 	}
 
@@ -1292,6 +1310,8 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{
 
 // Fprint "pretty-prints" an AST node to output.
 // It calls Config.Fprint with default settings.
+// Note that gofmt uses tabs for indentation but spaces for alignent;
+// use format.Node (package go/format) for output that matches gofmt.
 //
 func Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
 	return (&Config{Tabwidth: 8}).Fprint(output, fset, node)
diff --git a/src/go/printer/printer_test.go b/src/go/printer/printer_test.go
index 73f9ead..0badbfb 100644
--- a/src/go/printer/printer_test.go
+++ b/src/go/printer/printer_test.go
@@ -197,12 +197,17 @@ var data = []entry{
 }
 
 func TestFiles(t *testing.T) {
+	t.Parallel()
 	for _, e := range data {
 		source := filepath.Join(dataDir, e.source)
 		golden := filepath.Join(dataDir, e.golden)
-		check(t, source, golden, e.mode)
-		// TODO(gri) check that golden is idempotent
-		//check(t, golden, golden, e.mode)
+		mode := e.mode
+		t.Run(e.source, func(t *testing.T) {
+			t.Parallel()
+			check(t, source, golden, mode)
+			// TODO(gri) check that golden is idempotent
+			//check(t, golden, golden, e.mode)
+		})
 	}
 }
 
@@ -295,6 +300,7 @@ func testComment(t *testing.T, f *ast.File, srclen int, comment *ast.Comment) {
 // even if the position information of comments introducing newlines
 // is incorrect.
 func TestBadComments(t *testing.T) {
+	t.Parallel()
 	const src = `
 // first comment - text and position changed by test
 package p
@@ -481,6 +487,7 @@ func TestStmtLists(t *testing.T) {
 }
 
 func TestBaseIndent(t *testing.T) {
+	t.Parallel()
 	// The testfile must not contain multi-line raw strings since those
 	// are not indented (because their values must not change) and make
 	// this test fail.
@@ -495,28 +502,31 @@ func TestBaseIndent(t *testing.T) {
 		panic(err) // error in test
 	}
 
-	var buf bytes.Buffer
 	for indent := 0; indent < 4; indent++ {
-		buf.Reset()
-		(&Config{Tabwidth: tabwidth, Indent: indent}).Fprint(&buf, fset, file)
-		// all code must be indented by at least 'indent' tabs
-		lines := bytes.Split(buf.Bytes(), []byte{'\n'})
-		for i, line := range lines {
-			if len(line) == 0 {
-				continue // empty lines don't have indentation
-			}
-			n := 0
-			for j, b := range line {
-				if b != '\t' {
-					// end of indentation
-					n = j
-					break
+		indent := indent
+		t.Run(fmt.Sprint(indent), func(t *testing.T) {
+			t.Parallel()
+			var buf bytes.Buffer
+			(&Config{Tabwidth: tabwidth, Indent: indent}).Fprint(&buf, fset, file)
+			// all code must be indented by at least 'indent' tabs
+			lines := bytes.Split(buf.Bytes(), []byte{'\n'})
+			for i, line := range lines {
+				if len(line) == 0 {
+					continue // empty lines don't have indentation
+				}
+				n := 0
+				for j, b := range line {
+					if b != '\t' {
+						// end of indentation
+						n = j
+						break
+					}
+				}
+				if n < indent {
+					t.Errorf("line %d: got only %d tabs; want at least %d: %q", i, n, indent, line)
 				}
 			}
-			if n < indent {
-				t.Errorf("line %d: got only %d tabs; want at least %d: %q", i, n, indent, line)
-			}
-		}
+		})
 	}
 }
 
@@ -567,6 +577,7 @@ func (l *limitWriter) Write(buf []byte) (n int, err error) {
 
 // Test whether the printer stops writing after the first error
 func TestWriteErrors(t *testing.T) {
+	t.Parallel()
 	const filename = "printer.go"
 	src, err := ioutil.ReadFile(filename)
 	if err != nil {
diff --git a/src/go/printer/testdata/comments.golden b/src/go/printer/testdata/comments.golden
index 849fa62..4d92e65 100644
--- a/src/go/printer/testdata/comments.golden
+++ b/src/go/printer/testdata/comments.golden
@@ -601,6 +601,32 @@ func _() {
 	_ = a
 }
 
+// Test cases from issues 11274, 15137:
+// Semicolon must not be lost when multiple statements are on the same line with a comment.
+func _() {
+	x := 0 /**/
+	y := 1
+}
+
+func _() {
+	f()
+	f()
+	f() /* comment */
+	f()
+	f() /* comment */
+	f()
+	f() /* a */ /* b */
+	f()
+	f() /* a */ /* b */
+	f()
+	f() /* a */ /* b */
+	f()
+}
+
+func _() {
+	f() /* a */ /* b */
+}
+
 // Comments immediately adjacent to punctuation followed by a newline
 // remain after the punctuation (looks better and permits alignment of
 // comments).
diff --git a/src/go/printer/testdata/comments.input b/src/go/printer/testdata/comments.input
index 30cd23c..40351ee 100644
--- a/src/go/printer/testdata/comments.input
+++ b/src/go/printer/testdata/comments.input
@@ -607,6 +607,24 @@ func _() {
 	_ = a
 }
 
+// Test cases from issues 11274, 15137:
+// Semicolon must not be lost when multiple statements are on the same line with a comment.
+func _() {
+    x := 0 /**/; y := 1
+}
+
+func _() {
+	f(); f()
+	f(); /* comment */ f()
+	f() /* comment */; f()	
+	f(); /* a */ /* b */ f()
+	f() /* a */ /* b */; f()
+	f() /* a */; /* b */ f()
+}
+
+func _() {
+	f() /* a */ /* b */ }
+
 // Comments immediately adjacent to punctuation followed by a newline
 // remain after the punctuation (looks better and permits alignment of
 // comments).
diff --git a/src/go/scanner/scanner.go b/src/go/scanner/scanner.go
index ce660c7..a86e4eb 100644
--- a/src/go/scanner/scanner.go
+++ b/src/go/scanner/scanner.go
@@ -349,7 +349,11 @@ exponent:
 		if s.ch == '-' || s.ch == '+' {
 			s.next()
 		}
-		s.scanMantissa(10)
+		if digitVal(s.ch) < 10 {
+			s.scanMantissa(10)
+		} else {
+			s.error(offs, "illegal floating-point exponent")
+		}
 	}
 
 	if s.ch == 'i' {
diff --git a/src/go/scanner/scanner_test.go b/src/go/scanner/scanner_test.go
index 0d21905..ff41c03 100644
--- a/src/go/scanner/scanner_test.go
+++ b/src/go/scanner/scanner_test.go
@@ -716,6 +716,7 @@ var errors = []struct {
 	{"078.", token.FLOAT, 0, "078.", ""},
 	{"07801234567.", token.FLOAT, 0, "07801234567.", ""},
 	{"078e0", token.FLOAT, 0, "078e0", ""},
+	{"0E", token.FLOAT, 0, "0E", "illegal floating-point exponent"}, // issue 17621
 	{"078", token.INT, 0, "078", "illegal octal number"},
 	{"07800000009", token.INT, 0, "07800000009", "illegal octal number"},
 	{"0x", token.INT, 0, "0x", "illegal hexadecimal number"},
diff --git a/src/go/token/position.go b/src/go/token/position.go
index 7306083..d4171d8 100644
--- a/src/go/token/position.go
+++ b/src/go/token/position.go
@@ -446,7 +446,9 @@ func (s *FileSet) File(p Pos) (f *File) {
 func (s *FileSet) PositionFor(p Pos, adjusted bool) (pos Position) {
 	if p != NoPos {
 		if f := s.file(p); f != nil {
+			s.mutex.RLock()
 			pos = f.position(p, adjusted)
+			s.mutex.RUnlock()
 		}
 	}
 	return
diff --git a/src/go/token/position_test.go b/src/go/token/position_test.go
index d26939c..63984bc 100644
--- a/src/go/token/position_test.go
+++ b/src/go/token/position_test.go
@@ -214,7 +214,7 @@ func TestFileSetCacheUnlikely(t *testing.T) {
 	}
 }
 
-// issue 4345. Test concurrent use of FileSet.Pos does not trigger a
+// issue 4345. Test that concurrent use of FileSet.Pos does not trigger a
 // race in the FileSet position cache.
 func TestFileSetRace(t *testing.T) {
 	fset := NewFileSet()
@@ -237,6 +237,35 @@ func TestFileSetRace(t *testing.T) {
 	stop.Wait()
 }
 
+// issue 16548. Test that concurrent use of File.AddLine and FileSet.PositionFor
+// does not trigger a race in the FileSet position cache.
+func TestFileSetRace2(t *testing.T) {
+	const N = 1e3
+	var (
+		fset = NewFileSet()
+		file = fset.AddFile("", -1, N)
+		ch   = make(chan int, 2)
+	)
+
+	go func() {
+		for i := 0; i < N; i++ {
+			file.AddLine(i)
+		}
+		ch <- 1
+	}()
+
+	go func() {
+		pos := file.Pos(0)
+		for i := 0; i < N; i++ {
+			fset.PositionFor(pos, false)
+		}
+		ch <- 1
+	}()
+
+	<-ch
+	<-ch
+}
+
 func TestPositionFor(t *testing.T) {
 	src := []byte(`
 foo
diff --git a/src/go/types/api.go b/src/go/types/api.go
index ca109f0..4494989 100644
--- a/src/go/types/api.go
+++ b/src/go/types/api.go
@@ -135,7 +135,8 @@ type Config struct {
 // be incomplete.
 type Info struct {
 	// Types maps expressions to their types, and for constant
-	// expressions, their values. Invalid expressions are omitted.
+	// expressions, also their values. Invalid expressions are
+	// omitted.
 	//
 	// For (possibly parenthesized) identifiers denoting built-in
 	// functions, the recorded signatures are call-site specific:
@@ -143,9 +144,13 @@ type Info struct {
 	// an argument-specific signature. Otherwise, the recorded type
 	// is invalid.
 	//
-	// Identifiers on the lhs of declarations (i.e., the identifiers
-	// which are being declared) are collected in the Defs map.
-	// Identifiers denoting packages are collected in the Uses maps.
+	// The Types map does not record the type of every identifier,
+	// only those that appear where an arbitrary expression is
+	// permitted. For instance, the identifier f in a selector
+	// expression x.f is found only in the Selections map, the
+	// identifier z in a variable declaration 'var z int' is found
+	// only in the Defs map, and identifiers denoting packages in
+	// qualified identifiers are collected in the Uses map.
 	Types map[ast.Expr]TypeAndValue
 
 	// Defs maps identifiers to the objects they define (including
diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go
index 035ffd6..1208eb8 100644
--- a/src/go/types/api_test.go
+++ b/src/go/types/api_test.go
@@ -171,13 +171,11 @@ func TestTypesInfo(t *testing.T) {
 			`x.(int)`,
 			`(int, bool)`,
 		},
-		// TODO(gri): uncomment if we accept issue 8189.
-		// {`package p2; type mybool bool; var m map[string]complex128; var b mybool; func _() { _, b = m["foo"] }`,
-		// 	`m["foo"]`,
-		// 	`(complex128, p2.mybool)`,
-		// },
-		// TODO(gri): remove if we accept issue 8189.
-		{`package p2; var m map[string]complex128; var b bool; func _() { _, b = m["foo"] }`,
+		{`package p2a; type mybool bool; var m map[string]complex128; var b mybool; func _() { _, b = m["foo"] }`,
+			`m["foo"]`,
+			`(complex128, p2a.mybool)`,
+		},
+		{`package p2b; var m map[string]complex128; var b bool; func _() { _, b = m["foo"] }`,
 			`m["foo"]`,
 			`(complex128, bool)`,
 		},
@@ -573,26 +571,25 @@ func TestInitOrderInfo(t *testing.T) {
 			"a = next()", "b = next()", "c = next()", "d = next()", "e = next()", "f = next()", "_ = makeOrder()",
 		}},
 		// test case for issue 10709
-		// TODO(gri) enable once the issue is fixed
-		// {`package p13
-
-		// var (
-		//     v = t.m()
-		//     t = makeT(0)
-		// )
-
-		// type T struct{}
-
-		// func (T) m() int { return 0 }
-
-		// func makeT(n int) T {
-		//     if n > 0 {
-		//         return makeT(n-1)
-		//     }
-		//     return T{}
-		// }`, []string{
-		// 	"t = makeT(0)", "v = t.m()",
-		// }},
+		{`package p13
+
+		var (
+		    v = t.m()
+		    t = makeT(0)
+		)
+
+		type T struct{}
+
+		func (T) m() int { return 0 }
+
+		func makeT(n int) T {
+		    if n > 0 {
+		        return makeT(n-1)
+		    }
+		    return T{}
+		}`, []string{
+			"t = makeT(0)", "v = t.m()",
+		}},
 		// test case for issue 10709: same as test before, but variable decls swapped
 		{`package p14
 
@@ -613,6 +610,24 @@ func TestInitOrderInfo(t *testing.T) {
 		}`, []string{
 			"t = makeT(0)", "v = t.m()",
 		}},
+		// another candidate possibly causing problems with issue 10709
+		{`package p15
+
+		var y1 = f1()
+
+		func f1() int { return g1() }
+		func g1() int { f1(); return x1 }
+
+		var x1 = 0
+
+		var y2 = f2()
+
+		func f2() int { return g2() }
+		func g2() int { return x2 }
+
+		var x2 = 0`, []string{
+			"x1 = 0", "y1 = f1()", "x2 = 0", "y2 = f2()",
+		}},
 	}
 
 	for _, test := range tests {
@@ -1001,7 +1016,11 @@ func TestScopeLookupParent(t *testing.T) {
 	}
 	var info Info
 	makePkg := func(path string, files ...*ast.File) {
-		imports[path], _ = conf.Check(path, fset, files, &info)
+		var err error
+		imports[path], err = conf.Check(path, fset, files, &info)
+		if err != nil {
+			t.Fatal(err)
+		}
 	}
 
 	makePkg("lib", mustParse("package lib; var X int"))
@@ -1009,17 +1028,44 @@ func TestScopeLookupParent(t *testing.T) {
 	// name at that point and checks that it resolves to a decl of
 	// the specified kind and line number.  "undef" means undefined.
 	mainSrc := `
+/*lib=pkgname:5*/ /*X=var:1*/ /*Pi=const:8*/ /*T=typename:9*/ /*Y=var:10*/ /*F=func:12*/
 package main
+
 import "lib"
-var Y = lib.X
-func f() {
-	print(Y) /*Y=var:4*/
-	z /*z=undef*/ := /*z=undef*/ 1 /*z=var:7*/
-	print(z)
-	/*f=func:5*/ /*lib=pkgname:3*/
-	type /*T=undef*/ T /*T=typename:10*/ *T
+import . "lib"
+
+const Pi = 3.1415
+type T struct{}
+var Y, _ = lib.X, X
+
+func F(){
+	const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/
+	type /*t=undef*/ t /*t=typename:14*/ *t
+	print(Y) /*Y=var:10*/
+	x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y
+	var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F
+
+	var a []int
+	for i, x := range /*i=undef*/ /*x=var:16*/ a /*i=var:20*/ /*x=var:20*/ { _ = i; _ = x }
+
+	var i interface{}
+	switch y := i.(type) { /*y=undef*/
+	case /*y=undef*/ int /*y=var:23*/ :
+	case float32, /*y=undef*/ float64 /*y=var:23*/ :
+	default /*y=var:23*/:
+		println(y)
+	}
+	/*y=undef*/
+
+        switch int := i.(type) {
+        case /*int=typename:0*/ int /*int=var:31*/ :
+        	println(int)
+        default /*int=var:31*/ :
+        }
 }
+/*main=undef*/
 `
+
 	info.Uses = make(map[*ast.Ident]Object)
 	f := mustParse(mainSrc)
 	makePkg("main", f)
@@ -1125,3 +1171,279 @@ func TestIssue15305(t *testing.T) {
 	}
 	t.Errorf("CallExpr has no type")
 }
+
+// TestCompositeLitTypes verifies that Info.Types registers the correct
+// types for composite literal expressions and composite literal type
+// expressions.
+func TestCompositeLitTypes(t *testing.T) {
+	for _, test := range []struct {
+		lit, typ string
+	}{
+		{`[16]byte{}`, `[16]byte`},
+		{`[...]byte{}`, `[0]byte`},                // test for issue #14092
+		{`[...]int{1, 2, 3}`, `[3]int`},           // test for issue #14092
+		{`[...]int{90: 0, 98: 1, 2}`, `[100]int`}, // test for issue #14092
+		{`[]int{}`, `[]int`},
+		{`map[string]bool{"foo": true}`, `map[string]bool`},
+		{`struct{}{}`, `struct{}`},
+		{`struct{x, y int; z complex128}{}`, `struct{x int; y int; z complex128}`},
+	} {
+		fset := token.NewFileSet()
+		f, err := parser.ParseFile(fset, test.lit, "package p; var _ = "+test.lit, 0)
+		if err != nil {
+			t.Fatalf("%s: %v", test.lit, err)
+		}
+
+		info := &Info{
+			Types: make(map[ast.Expr]TypeAndValue),
+		}
+		if _, err = new(Config).Check("p", fset, []*ast.File{f}, info); err != nil {
+			t.Fatalf("%s: %v", test.lit, err)
+		}
+
+		cmptype := func(x ast.Expr, want string) {
+			tv, ok := info.Types[x]
+			if !ok {
+				t.Errorf("%s: no Types entry found", test.lit)
+				return
+			}
+			if tv.Type == nil {
+				t.Errorf("%s: type is nil", test.lit)
+				return
+			}
+			if got := tv.Type.String(); got != want {
+				t.Errorf("%s: got %v, want %s", test.lit, got, want)
+			}
+		}
+
+		// test type of composite literal expression
+		rhs := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec).Values[0]
+		cmptype(rhs, test.typ)
+
+		// test type of composite literal type expression
+		cmptype(rhs.(*ast.CompositeLit).Type, test.typ)
+	}
+}
+
+// TestObjectParents verifies that objects have parent scopes or not
+// as specified by the Object interface.
+func TestObjectParents(t *testing.T) {
+	const src = `
+package p
+
+const C = 0
+
+type T1 struct {
+	a, b int
+	T2
+}
+
+type T2 interface {
+	im1()
+	im2()
+}
+
+func (T1) m1() {}
+func (*T1) m2() {}
+
+func f(x int) { y := x; print(y) }
+`
+
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "src", src, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	info := &Info{
+		Defs: make(map[*ast.Ident]Object),
+	}
+	if _, err = new(Config).Check("p", fset, []*ast.File{f}, info); err != nil {
+		t.Fatal(err)
+	}
+
+	for ident, obj := range info.Defs {
+		if obj == nil {
+			// only package names and implicit vars have a nil object
+			// (in this test we only need to handle the package name)
+			if ident.Name != "p" {
+				t.Errorf("%v has nil object", ident)
+			}
+			continue
+		}
+
+		// struct fields, type-associated and interface methods
+		// have no parent scope
+		wantParent := true
+		switch obj := obj.(type) {
+		case *Var:
+			if obj.IsField() {
+				wantParent = false
+			}
+		case *Func:
+			if obj.Type().(*Signature).Recv() != nil { // method
+				wantParent = false
+			}
+		}
+
+		gotParent := obj.Parent() != nil
+		switch {
+		case gotParent && !wantParent:
+			t.Errorf("%v: want no parent, got %s", ident, obj.Parent())
+		case !gotParent && wantParent:
+			t.Errorf("%v: no parent found", ident)
+		}
+	}
+}
+
+// Alias-related code. Keep for now.
+/*
+func TestAliases(t *testing.T) {
+	testenv.MustHaveGoBuild(t)
+
+	const src = `
+package b
+
+import (
+	"./testdata/alias"
+	a "./testdata/alias"
+	"math"
+)
+
+const (
+	c1 = alias.Pi1
+	c2 => a.Pi1
+	c3 => a.Pi2
+	c4 => math.Pi
+)
+
+var (
+	v1 => alias.Default
+	v2 => a.Default
+	v3 = f1
+)
+
+type (
+	t1 => alias.Context
+	t2 => a.Context
+)
+
+func f1 => alias.Sin
+func f2 => a.Sin
+
+func _() {
+	assert(c1 == alias.Pi1 && c2 == a.Pi1 && c3 == a.Pi2 && c4 == math.Pi)
+	assert(c2 == c2 && c2 == c3 && c3 == c4)
+	v1 = v2 // must be assignable
+	var _ *t1 = new(t2) // must be assignable
+	var _ t2 = alias.Default
+	f1(1) // must be callable
+	f2(1)
+	_ = alias.Sin(1)
+	_ = a.Sin(1)
+}
+`
+
+	if out := compile(t, "testdata", "alias.go"); out != "" {
+		defer os.Remove(out)
+	}
+
+	DefPredeclaredTestFuncs() // declare assert built-in for testing
+	mustTypecheck(t, "Aliases", src, nil)
+}
+
+func compile(t *testing.T, dirname, filename string) string {
+	cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", filename)
+	cmd.Dir = dirname
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Logf("%s", out)
+		t.Fatalf("go tool compile %s failed: %s", filename, err)
+	}
+	// filename should end with ".go"
+	return filepath.Join(dirname, filename[:len(filename)-2]+"o")
+}
+
+func TestAliasDefUses(t *testing.T) {
+	testenv.MustHaveGoBuild(t)
+
+	const src = `
+package p
+
+import(
+	"go/build"
+	"go/types"
+)
+
+// Defs
+const Invalid => types.Invalid
+type Struct => types.Struct
+var Default => build.Default
+func Implements => types.Implements
+
+// Uses
+const _ = Invalid
+var _ types.Struct = Struct{} // types must be identical
+var _ build.Context = Default
+var _ = Implements(nil, nil)
+`
+
+	info := Info{
+		Defs: make(map[*ast.Ident]Object),
+		Uses: make(map[*ast.Ident]Object),
+	}
+	mustTypecheck(t, "TestAliasDefUses", src, &info)
+
+	// verify Defs
+	defs := map[string]string{
+		"Invalid":    "types.Invalid",
+		"Struct":     "types.Struct",
+		"Default":    "build.Default",
+		"Implements": "types.Implements",
+	}
+
+	for ident, obj := range info.Defs {
+		if alias, ok := obj.(*Alias); ok {
+			if want := defs[ident.Name]; want != "" {
+				orig := alias.Orig()
+				if got := orig.Pkg().Name() + "." + orig.Name(); got != want {
+					t.Errorf("%v: got %v, want %v", ident, got, want)
+				}
+				delete(defs, ident.Name) // mark as found
+			} else {
+				t.Errorf("unexpected alias def of %v", ident)
+			}
+		}
+	}
+
+	if len(defs) != 0 {
+		t.Errorf("missing aliases: %v", defs)
+	}
+
+	// verify Uses
+	uses := map[string]string{
+		"Invalid":    "types.Invalid",
+		"Struct":     "types.Struct",
+		"Default":    "build.Default",
+		"Implements": "types.Implements",
+	}
+
+	for ident, obj := range info.Uses {
+		if alias, ok := obj.(*Alias); ok {
+			if want := uses[ident.Name]; want != "" {
+				orig := alias.Orig()
+				if got := orig.Pkg().Name() + "." + orig.Name(); got != want {
+					t.Errorf("%v: got %v, want %v", ident, got, want)
+				}
+				delete(uses, ident.Name) // mark as found
+			} else {
+				t.Errorf("unexpected alias use of %v", ident)
+			}
+		}
+	}
+
+	if len(uses) != 0 {
+		t.Errorf("missing aliases: %v", defs)
+	}
+}
+*/
diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go
index 6ebf3b5..18f893d 100644
--- a/src/go/types/assignments.go
+++ b/src/go/types/assignments.go
@@ -41,7 +41,7 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
 				x.mode = invalid
 				return
 			}
-			target = defaultType(x.typ)
+			target = Default(x.typ)
 		}
 		check.convertUntyped(x, target)
 		if x.mode == invalid {
@@ -116,7 +116,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
 				lhs.typ = Typ[Invalid]
 				return nil
 			}
-			typ = defaultType(typ)
+			typ = Default(typ)
 		}
 		lhs.typ = typ
 	}
diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go
index 0082be9..596a989 100644
--- a/src/go/types/builtins.go
+++ b/src/go/types/builtins.go
@@ -434,7 +434,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
 			return
 		}
 		if nargs < min || min+1 < nargs {
-			check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs)
+			check.errorf(call.Pos(), "%v expects %d or %d arguments; found %d", call, min, min+1, nargs)
 			return
 		}
 		var sizes []int64 // constant integer arguments, if any
@@ -595,7 +595,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
 			return
 		}
 		if !constant.BoolVal(x.val) {
-			check.errorf(call.Pos(), "%s failed", call)
+			check.errorf(call.Pos(), "%v failed", call)
 			// compile-time assertion failure - safe to continue
 		}
 		// result is constant - no need to record signature
@@ -632,7 +632,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
 func makeSig(res Type, args ...Type) *Signature {
 	list := make([]*Var, len(args))
 	for i, param := range args {
-		list[i] = NewVar(token.NoPos, nil, "", defaultType(param))
+		list[i] = NewVar(token.NoPos, nil, "", Default(param))
 	}
 	params := NewTuple(list...)
 	var result *Tuple
diff --git a/src/go/types/call.go b/src/go/types/call.go
index 45f3e9a..8e5c537 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -275,24 +275,34 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
 	// so we don't need a "package" mode for operands: package names
 	// can only appear in qualified identifiers which are mapped to
 	// selector expressions.
+	// (see also decl.go: checker.aliasDecl)
+	// TODO(gri) factor this code out and share with checker.aliasDecl
 	if ident, ok := e.X.(*ast.Ident); ok {
 		_, obj := check.scope.LookupParent(ident.Name, check.pos)
-		if pkg, _ := obj.(*PkgName); pkg != nil {
-			assert(pkg.pkg == check.pkg)
-			check.recordUse(ident, pkg)
-			pkg.used = true
-			exp := pkg.imported.scope.Lookup(sel)
+		if pname, _ := obj.(*PkgName); pname != nil {
+			assert(pname.pkg == check.pkg)
+			check.recordUse(ident, pname)
+			pname.used = true
+			pkg := pname.imported
+			exp := pkg.scope.Lookup(sel)
 			if exp == nil {
-				if !pkg.imported.fake {
-					check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
+				if !pkg.fake {
+					check.errorf(e.Pos(), "%s not declared by package %s", sel, pkg.name)
 				}
 				goto Error
 			}
 			if !exp.Exported() {
-				check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
+				check.errorf(e.Pos(), "%s not exported by package %s", sel, pkg.name)
 				// ok to continue
 			}
 			check.recordUse(e.Sel, exp)
+			exp = original(exp)
+
+			// avoid further errors if the imported object is an alias that's broken
+			if exp == nil {
+				goto Error
+			}
+
 			// Simplified version of the code for *ast.Idents:
 			// - imported objects are always fully initialized
 			switch exp := exp.(type) {
@@ -315,6 +325,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
 				x.typ = exp.typ
 				x.id = exp.id
 			default:
+				check.dump("unexpected object %v", exp)
 				unreachable()
 			}
 			x.expr = e
diff --git a/src/go/types/check.go b/src/go/types/check.go
index 0279be0..28e94f1 100644
--- a/src/go/types/check.go
+++ b/src/go/types/check.go
@@ -345,7 +345,6 @@ func (check *Checker) recordImplicit(node ast.Node, obj Object) {
 func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
 	assert(obj != nil && (recv == nil || len(index) > 0))
 	check.recordUse(x.Sel, obj)
-	// TODO(gri) Should we also call recordTypeAndValue?
 	if m := check.Selections; m != nil {
 		m[x] = &Selection{kind, recv, obj, index, indirect}
 	}
diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go
index 5e2043b..f844575 100644
--- a/src/go/types/check_test.go
+++ b/src/go/types/check_test.go
@@ -72,6 +72,7 @@ var tests = [][]string{
 	{"testdata/const1.src"},
 	{"testdata/constdecl.src"},
 	{"testdata/vardecl.src"},
+	//{"testdata/aliasdecl.src"},
 	{"testdata/expr0.src"},
 	{"testdata/expr1.src"},
 	{"testdata/expr2.src"},
@@ -80,6 +81,7 @@ var tests = [][]string{
 	{"testdata/shifts.src"},
 	{"testdata/builtins.src"},
 	{"testdata/conversions.src"},
+	{"testdata/conversions2.src"},
 	{"testdata/stmt0.src"},
 	{"testdata/stmt1.src"},
 	{"testdata/gotos.src"},
diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go
index f98cc8d..2bf1e2d 100644
--- a/src/go/types/conversions.go
+++ b/src/go/types/conversions.go
@@ -55,7 +55,7 @@ func (check *Checker) conversion(x *operand, T Type) {
 		//   not []byte as type for the constant "foo").
 		// - Keep untyped nil for untyped nil arguments.
 		if IsInterface(T) || constArg && !isConstType(T) {
-			final = defaultType(x.typ)
+			final = Default(x.typ)
 		}
 		check.updateExprType(x.expr, final, true)
 	}
@@ -69,18 +69,19 @@ func (x *operand) convertibleTo(conf *Config, T Type) bool {
 		return true
 	}
 
-	// "x's type and T have identical underlying types"
+	// "x's type and T have identical underlying types if tags are ignored"
 	V := x.typ
 	Vu := V.Underlying()
 	Tu := T.Underlying()
-	if Identical(Vu, Tu) {
+	if IdenticalIgnoreTags(Vu, Tu) {
 		return true
 	}
 
-	// "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
+	// "x's type and T are unnamed pointer types and their pointer base types
+	// have identical underlying types if tags are ignored"
 	if V, ok := V.(*Pointer); ok {
 		if T, ok := T.(*Pointer); ok {
-			if Identical(V.base.Underlying(), T.base.Underlying()) {
+			if IdenticalIgnoreTags(V.base.Underlying(), T.base.Underlying()) {
 				return true
 			}
 		}
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index 1ecfb35..dced7a6 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -85,6 +85,10 @@ func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) {
 	case *Func:
 		// functions may be recursive - no need to track dependencies
 		check.funcDecl(obj, d)
+	// Alias-related code. Keep for now.
+	// case *Alias:
+	// 	// aliases cannot be recursive - no need to track dependencies
+	// 	check.aliasDecl(obj, d)
 	default:
 		unreachable()
 	}
@@ -329,6 +333,106 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
 	}
 }
 
+// original returns the original Object if obj is an Alias;
+// otherwise it returns obj. The result is never an Alias,
+// but it may be nil.
+func original(obj Object) Object {
+	// an alias stands for the original object; use that one instead
+	if alias, _ := obj.(*disabledAlias); alias != nil {
+		obj = alias.orig
+		// aliases always refer to non-alias originals
+		if _, ok := obj.(*disabledAlias); ok {
+			panic("original is an alias")
+		}
+	}
+	return obj
+}
+
+func (check *Checker) aliasDecl(obj *disabledAlias, decl *declInfo) {
+	assert(obj.typ == nil)
+
+	// alias declarations cannot use iota
+	assert(check.iota == nil)
+
+	// assume alias is invalid to start with
+	obj.typ = Typ[Invalid]
+
+	// rhs must be package-qualified identifer pkg.sel (see also call.go: checker.selector)
+	// TODO(gri) factor this code out and share with checker.selector
+	rhs := decl.init
+	var pkg *Package
+	var sel *ast.Ident
+	if sexpr, ok := rhs.(*ast.SelectorExpr); ok {
+		if ident, ok := sexpr.X.(*ast.Ident); ok {
+			_, obj := check.scope.LookupParent(ident.Name, check.pos)
+			if pname, _ := obj.(*PkgName); pname != nil {
+				assert(pname.pkg == check.pkg)
+				check.recordUse(ident, pname)
+				pname.used = true
+				pkg = pname.imported
+				sel = sexpr.Sel
+			}
+		}
+	}
+	if pkg == nil {
+		check.errorf(rhs.Pos(), "invalid alias: %v is not a package-qualified identifier", rhs)
+		return
+	}
+
+	// qualified identifier must denote an exported object
+	orig := pkg.scope.Lookup(sel.Name)
+	if orig == nil || !orig.Exported() {
+		if !pkg.fake {
+			check.errorf(rhs.Pos(), "%s is not exported by package %s", sel.Name, pkg.name)
+		}
+		return
+	}
+	check.recordUse(sel, orig)
+	orig = original(orig)
+
+	// avoid further errors if the imported object is an alias that's broken
+	if orig == nil {
+		return
+	}
+
+	// An alias declaration must not refer to package unsafe.
+	if orig.Pkg() == Unsafe {
+		check.errorf(rhs.Pos(), "invalid alias: %s refers to package unsafe (%v)", obj.Name(), orig)
+		return
+	}
+
+	// The original must be of the same kind as the alias declaration.
+	var why string
+	switch obj.kind {
+	case token.CONST:
+		if _, ok := orig.(*Const); !ok {
+			why = "constant"
+		}
+	case token.TYPE:
+		if _, ok := orig.(*TypeName); !ok {
+			why = "type"
+		}
+	case token.VAR:
+		if _, ok := orig.(*Var); !ok {
+			why = "variable"
+		}
+	case token.FUNC:
+		if _, ok := orig.(*Func); !ok {
+			why = "function"
+		}
+	default:
+		unreachable()
+	}
+	if why != "" {
+		check.errorf(rhs.Pos(), "invalid alias: %v is not a %s", orig, why)
+		return
+	}
+
+	// alias is valid
+	obj.typ = orig.Type()
+	obj.orig = orig
+}
+
 func (check *Checker) declStmt(decl ast.Decl) {
 	pkg := check.pkg
 
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index 4430c45..f76da17 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -541,7 +541,7 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
 			if !t.Empty() {
 				goto Error
 			}
-			target = defaultType(x.typ)
+			target = Default(x.typ)
 		}
 	case *Pointer, *Signature, *Slice, *Map, *Chan:
 		if !x.isNil() {
@@ -605,8 +605,8 @@ func (check *Checker) comparison(x, y *operand, op token.Token) {
 		// time will be materialized. Update the expression trees.
 		// If the current types are untyped, the materialized type
 		// is the respective default type.
-		check.updateExprType(x.expr, defaultType(x.typ), true)
-		check.updateExprType(y.expr, defaultType(y.typ), true)
+		check.updateExprType(x.expr, Default(x.typ), true)
+		check.updateExprType(y.expr, Default(y.typ), true)
 	}
 
 	// spec: "Comparison operators compare two operands and yield
@@ -1015,32 +1015,38 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
 		}
 
 	case *ast.CompositeLit:
-		typ := hint
-		openArray := false
-		if e.Type != nil {
+		var typ, base Type
+
+		switch {
+		case e.Type != nil:
+			// composite literal type present - use it
 			// [...]T array types may only appear with composite literals.
 			// Check for them here so we don't have to handle ... in general.
-			typ = nil
 			if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil {
 				if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil {
 					// We have an "open" [...]T array type.
 					// Create a new ArrayType with unknown length (-1)
 					// and finish setting it up after analyzing the literal.
 					typ = &Array{len: -1, elem: check.typ(atyp.Elt)}
-					openArray = true
+					base = typ
+					break
 				}
 			}
-			if typ == nil {
-				typ = check.typ(e.Type)
-			}
-		}
-		if typ == nil {
+			typ = check.typ(e.Type)
+			base = typ
+
+		case hint != nil:
+			// no composite literal type present - use hint (element type of enclosing type)
+			typ = hint
+			base, _ = deref(typ.Underlying()) // *T implies &T{}
+
+		default:
 			// TODO(gri) provide better error messages depending on context
 			check.error(e.Pos(), "missing type in composite literal")
 			goto Error
 		}
 
-		switch typ, _ := deref(typ); utyp := typ.Underlying().(type) {
+		switch utyp := base.Underlying().(type) {
 		case *Struct:
 			if len(e.Elts) == 0 {
 				break
@@ -1106,9 +1112,12 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
 
 		case *Array:
 			n := check.indexedElts(e.Elts, utyp.elem, utyp.len)
-			// if we have an "open" [...]T array, set the length now that we know it
-			if openArray {
+			// If we have an "open" [...]T array, set the length now that we know it
+			// and record the type for [...] (usually done by check.typExpr which is
+			// not called for [...]).
+			if utyp.len < 0 {
 				utyp.len = n
+				check.recordTypeAndValue(e.Type, typexpr, utyp, nil)
 			}
 
 		case *Slice:
diff --git a/src/go/types/initorder.go b/src/go/types/initorder.go
index cf9b870..966dccb 100644
--- a/src/go/types/initorder.go
+++ b/src/go/types/initorder.go
@@ -15,7 +15,7 @@ func (check *Checker) initOrder() {
 	// built from several calls to (*Checker).Files. Clear it.
 	check.Info.InitOrder = check.Info.InitOrder[:0]
 
-	// Compute the transposed object dependency graph and initialize
+	// Compute the object dependency graph and initialize
 	// a priority queue with the list of graph nodes.
 	pq := nodeQueue(dependencyGraph(check.objMap))
 	heap.Init(&pq)
@@ -25,22 +25,25 @@ func (check *Checker) initOrder() {
 		fmt.Printf("Computing initialization order for %s\n\n", check.pkg)
 		fmt.Println("Object dependency graph:")
 		for obj, d := range check.objMap {
-			if len(d.deps) > 0 {
-				fmt.Printf("\t%s depends on\n", obj.Name())
-				for dep := range d.deps {
-					fmt.Printf("\t\t%s\n", dep.Name())
+			// only print objects that may appear in the dependency graph
+			if obj, _ := obj.(dependency); obj != nil {
+				if len(d.deps) > 0 {
+					fmt.Printf("\t%s depends on\n", obj.Name())
+					for dep := range d.deps {
+						fmt.Printf("\t\t%s\n", dep.Name())
+					}
+				} else {
+					fmt.Printf("\t%s has no dependencies\n", obj.Name())
 				}
-			} else {
-				fmt.Printf("\t%s has no dependencies\n", obj.Name())
 			}
 		}
 		fmt.Println()
 
-		fmt.Println("Transposed object dependency graph:")
+		fmt.Println("Transposed object dependency graph (functions eliminated):")
 		for _, n := range pq {
-			fmt.Printf("\t%s depends on %d nodes\n", n.obj.Name(), n.in)
-			for _, out := range n.out {
-				fmt.Printf("\t\t%s is dependent\n", out.obj.Name())
+			fmt.Printf("\t%s depends on %d nodes\n", n.obj.Name(), n.ndeps)
+			for p := range n.pred {
+				fmt.Printf("\t\t%s is dependent\n", p.obj.Name())
 			}
 		}
 		fmt.Println()
@@ -54,34 +57,40 @@ func (check *Checker) initOrder() {
 	// In a valid Go program, those nodes always have zero dependencies (after
 	// removing all incoming dependencies), otherwise there are initialization
 	// cycles.
-	mark := 0
 	emitted := make(map[*declInfo]bool)
 	for len(pq) > 0 {
 		// get the next node
-		n := heap.Pop(&pq).(*objNode)
+		n := heap.Pop(&pq).(*graphNode)
 
 		if debug {
 			fmt.Printf("\t%s (src pos %d) depends on %d nodes now\n",
-				n.obj.Name(), n.obj.order(), n.in)
+				n.obj.Name(), n.obj.order(), n.ndeps)
 		}
 
 		// if n still depends on other nodes, we have a cycle
-		if n.in > 0 {
-			mark++ // mark nodes using a different value each time
-			cycle := findPath(n, n, mark)
-			if i := valIndex(cycle); i >= 0 {
-				check.reportCycle(cycle, i)
+		if n.ndeps > 0 {
+			cycle := findPath(check.objMap, n.obj, n.obj, make(objSet))
+			// If n.obj is not part of the cycle (e.g., n.obj->b->c->d->c),
+			// cycle will be nil. Don't report anything in that case since
+			// the cycle is reported when the algorithm gets to an object
+			// in the cycle.
+			// Furthermore, once an object in the cycle is encountered,
+			// the cycle will be broken (dependency count will be reduced
+			// below), and so the remaining nodes in the cycle don't trigger
+			// another error (unless they are part of multiple cycles).
+			if cycle != nil {
+				check.reportCycle(cycle)
 			}
-			// ok to continue, but the variable initialization order
+			// Ok to continue, but the variable initialization order
 			// will be incorrect at this point since it assumes no
-			// cycle errors
+			// cycle errors.
 		}
 
 		// reduce dependency count of all dependent nodes
 		// and update priority queue
-		for _, out := range n.out {
-			out.in--
-			heap.Fix(&pq, out.index)
+		for p := range n.pred {
+			p.ndeps--
+			heap.Fix(&pq, p.index)
 		}
 
 		// record the init order for variables with initializers only
@@ -118,102 +127,147 @@ func (check *Checker) initOrder() {
 	}
 }
 
-// findPath returns the (reversed) list of nodes z, ... c, b, a,
-// such that there is a path (list of edges) from a to z.
+// findPath returns the (reversed) list of objects []Object{to, ... from}
+// such that there is a path of object dependencies from 'from' to 'to'.
 // If there is no such path, the result is nil.
-// Nodes marked with the value mark are considered "visited";
-// unvisited nodes are marked during the graph search.
-func findPath(a, z *objNode, mark int) []*objNode {
-	if a.mark == mark {
+func findPath(objMap map[Object]*declInfo, from, to Object, visited objSet) []Object {
+	if visited[from] {
 		return nil // node already seen
 	}
-	a.mark = mark
+	visited[from] = true
 
-	for _, n := range a.out {
-		if n == z {
-			return []*objNode{z}
+	for d := range objMap[from].deps {
+		if d == to {
+			return []Object{d}
 		}
-		if P := findPath(n, z, mark); P != nil {
-			return append(P, n)
+		if P := findPath(objMap, d, to, visited); P != nil {
+			return append(P, d)
 		}
 	}
 
 	return nil
 }
 
-// valIndex returns the index of the first constant or variable in a,
-// if any; or a value < 0.
-func valIndex(a []*objNode) int {
-	for i, n := range a {
-		switch n.obj.(type) {
-		case *Const, *Var:
-			return i
-		}
-	}
-	return -1
-}
-
-// reportCycle reports an error for the cycle starting at i.
-func (check *Checker) reportCycle(cycle []*objNode, i int) {
-	obj := cycle[i].obj
+// reportCycle reports an error for the given cycle.
+func (check *Checker) reportCycle(cycle []Object) {
+	obj := cycle[0]
 	check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name())
-	// print cycle
-	for _ = range cycle {
+	// subtle loop: print cycle[i] for i = 0, n-1, n-2, ... 1 for len(cycle) = n
+	for i := len(cycle) - 1; i >= 0; i-- {
 		check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
-		i++
-		if i >= len(cycle) {
-			i = 0
-		}
-		obj = cycle[i].obj
+		obj = cycle[i]
 	}
+	// print cycle[0] again to close the cycle
 	check.errorf(obj.Pos(), "\t%s", obj.Name())
 }
 
-// An objNode represents a node in the object dependency graph.
-// Each node b in a.out represents an edge a->b indicating that
-// b depends on a.
-// Nodes may be marked for cycle detection. A node n is marked
-// if n.mark corresponds to the current mark value.
-type objNode struct {
-	obj   Object     // object represented by this node
-	in    int        // number of nodes this node depends on
-	out   []*objNode // list of nodes that depend on this node
-	index int        // node index in list of nodes
-	mark  int        // for cycle detection
+// ----------------------------------------------------------------------------
+// Object dependency graph
+
+// A dependency is an object that may be a dependency in an initialization
+// expression. Only constants, variables, and functions can be dependencies.
+// Constants are here because constant expression cycles are reported during
+// initialization order computation.
+type dependency interface {
+	Object
+	isDependency()
+}
+
+// A graphNode represents a node in the object dependency graph.
+// Each node p in n.pred represents an edge p->n, and each node
+// s in n.succ represents an edge n->s; with a->b indicating that
+// a depends on b.
+type graphNode struct {
+	obj        dependency // object represented by this node
+	pred, succ nodeSet    // consumers and dependencies of this node (lazily initialized)
+	index      int        // node index in graph slice/priority queue
+	ndeps      int        // number of outstanding dependencies before this object can be initialized
+}
+
+type nodeSet map[*graphNode]bool
+
+func (s *nodeSet) add(p *graphNode) {
+	if *s == nil {
+		*s = make(nodeSet)
+	}
+	(*s)[p] = true
 }
 
-// dependencyGraph computes the transposed object dependency graph
-// from the given objMap. The transposed graph is returned as a list
-// of nodes; an edge d->n indicates that node n depends on node d.
-func dependencyGraph(objMap map[Object]*declInfo) []*objNode {
-	// M maps each object to its corresponding node
-	M := make(map[Object]*objNode, len(objMap))
+// dependencyGraph computes the object dependency graph from the given objMap,
+// with any function nodes removed. The resulting graph contains only constants
+// and variables.
+func dependencyGraph(objMap map[Object]*declInfo) []*graphNode {
+	// M is the dependency (Object) -> graphNode mapping
+	M := make(map[dependency]*graphNode)
 	for obj := range objMap {
-		M[obj] = &objNode{obj: obj}
+		// only consider nodes that may be an initialization dependency
+		if obj, _ := obj.(dependency); obj != nil {
+			M[obj] = &graphNode{obj: obj}
+		}
 	}
 
-	// G is the graph of nodes n
-	G := make([]*objNode, len(M))
-	i := 0
+	// compute edges for graph M
+	// (We need to include all nodes, even isolated ones, because they still need
+	// to be scheduled for initialization in correct order relative to other nodes.)
 	for obj, n := range M {
-		deps := objMap[obj].deps
-		n.in = len(deps)
-		for d := range deps {
-			d := M[d]                // node n depends on node d
-			d.out = append(d.out, n) // add edge d->n
+		// for each dependency obj -> d (= deps[i]), create graph edges n->s and s->n
+		for d := range objMap[obj].deps {
+			// only consider nodes that may be an initialization dependency
+			if d, _ := d.(dependency); d != nil {
+				d := M[d]
+				n.succ.add(d)
+				d.pred.add(n)
+			}
 		}
+	}
 
-		G[i] = n
+	// remove function nodes and collect remaining graph nodes in G
+	// (Mutually recursive functions may introduce cycles among themselves
+	// which are permitted. Yet such cycles may incorrectly inflate the dependency
+	// count for variables which in turn may not get scheduled for initialization
+	// in correct order.)
+	var G []*graphNode
+	for obj, n := range M {
+		if _, ok := obj.(*Func); ok {
+			// connect each predecessor p of n with each successor s
+			// and drop the function node (don't collect it in G)
+			for p := range n.pred {
+				// ignore self-cycles
+				if p != n {
+					// Each successor s of n becomes a successor of p, and
+					// each predecessor p of n becomes a predecessor of s.
+					for s := range n.succ {
+						// ignore self-cycles
+						if s != n {
+							p.succ.add(s)
+							s.pred.add(p)
+							delete(s.pred, n) // remove edge to n
+						}
+					}
+					delete(p.succ, n) // remove edge to n
+				}
+			}
+		} else {
+			// collect non-function nodes
+			G = append(G, n)
+		}
+	}
+
+	// fill in index and ndeps fields
+	for i, n := range G {
 		n.index = i
-		i++
+		n.ndeps = len(n.succ)
 	}
 
 	return G
 }
 
+// ----------------------------------------------------------------------------
+// Priority queue
+
 // nodeQueue implements the container/heap interface;
 // a nodeQueue may be used as a priority queue.
-type nodeQueue []*objNode
+type nodeQueue []*graphNode
 
 func (a nodeQueue) Len() int { return len(a) }
 
@@ -227,7 +281,7 @@ func (a nodeQueue) Less(i, j int) bool {
 	x, y := a[i], a[j]
 	// nodes are prioritized by number of incoming dependencies (1st key)
 	// and source order (2nd key)
-	return x.in < y.in || x.in == y.in && x.obj.order() < y.obj.order()
+	return x.ndeps < y.ndeps || x.ndeps == y.ndeps && x.obj.order() < y.obj.order()
 }
 
 func (a *nodeQueue) Push(x interface{}) {
diff --git a/src/go/types/object.go b/src/go/types/object.go
index 707b806..6c0c5c4 100644
--- a/src/go/types/object.go
+++ b/src/go/types/object.go
@@ -19,7 +19,7 @@ import (
 // All objects implement the Object interface.
 //
 type Object interface {
-	Parent() *Scope // scope in which this object is declared
+	Parent() *Scope // scope in which this object is declared; nil for methods and struct fields
 	Pos() token.Pos // position of object identifier in declaration
 	Pkg() *Package  // nil for objects in the Universe scope and labels
 	Name() string   // package local object name
@@ -152,6 +152,7 @@ func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.V
 }
 
 func (obj *Const) Val() constant.Value { return obj.val }
+func (*Const) isDependency()           {} // a constant may be a dependency of an initialization expression
 
 // A TypeName represents a declared type.
 type TypeName struct {
@@ -184,8 +185,8 @@ func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool
 }
 
 func (obj *Var) Anonymous() bool { return obj.anonymous }
-
-func (obj *Var) IsField() bool { return obj.isField }
+func (obj *Var) IsField() bool   { return obj.isField }
+func (*Var) isDependency()       {} // a variable may be a dependency of an initialization expression
 
 // A Func represents a declared function, concrete method, or abstract
 // (interface) method. Its Type() is always a *Signature.
@@ -211,10 +212,31 @@ func (obj *Func) FullName() string {
 	return buf.String()
 }
 
-func (obj *Func) Scope() *Scope {
-	return obj.typ.(*Signature).scope
+func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope }
+func (*Func) isDependency()     {} // a function may be a dependency of an initialization expression
+
+// An Alias represents a declared alias.
+type disabledAlias struct {
+	object
+	orig Object      // aliased constant, type, variable, or function; never an alias
+	kind token.Token // token.CONST, token.TYPE, token.VAR, or token.FUNC (only needed during resolve phase)
+}
+
+func disabledNewAlias(pos token.Pos, pkg *Package, name string, orig Object) *disabledAlias {
+	var typ Type = Typ[Invalid]
+	if orig != nil {
+		typ = orig.Type()
+	}
+	// No need to set a valid Alias.kind - that field is only used during identifier
+	// resolution (1st type-checker pass). We could store the field outside but it's
+	// easier to keep it here.
+	return &disabledAlias{object{nil, pos, pkg, name, typ, 0, token.NoPos}, orig, token.ILLEGAL}
 }
 
+// Orig returns the aliased object, or nil if there was an error.
+// The returned object is never an Alias.
+func (obj *disabledAlias) disabledOrig() Object { return obj.orig }
+
 // A Label represents a declared label.
 type Label struct {
 	object
@@ -273,6 +295,10 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
 		}
 		return
 
+	// Alias-related code. Keep for now.
+	// case *Alias:
+	// 	buf.WriteString("alias")
+
 	case *Label:
 		buf.WriteString("label")
 		typ = nil
@@ -327,14 +353,15 @@ func ObjectString(obj Object, qf Qualifier) string {
 	return buf.String()
 }
 
-func (obj *PkgName) String() string  { return ObjectString(obj, nil) }
-func (obj *Const) String() string    { return ObjectString(obj, nil) }
-func (obj *TypeName) String() string { return ObjectString(obj, nil) }
-func (obj *Var) String() string      { return ObjectString(obj, nil) }
-func (obj *Func) String() string     { return ObjectString(obj, nil) }
-func (obj *Label) String() string    { return ObjectString(obj, nil) }
-func (obj *Builtin) String() string  { return ObjectString(obj, nil) }
-func (obj *Nil) String() string      { return ObjectString(obj, nil) }
+func (obj *PkgName) String() string       { return ObjectString(obj, nil) }
+func (obj *Const) String() string         { return ObjectString(obj, nil) }
+func (obj *TypeName) String() string      { return ObjectString(obj, nil) }
+func (obj *Var) String() string           { return ObjectString(obj, nil) }
+func (obj *Func) String() string          { return ObjectString(obj, nil) }
+func (obj *disabledAlias) String() string { return ObjectString(obj, nil) }
+func (obj *Label) String() string         { return ObjectString(obj, nil) }
+func (obj *Builtin) String() string       { return ObjectString(obj, nil) }
+func (obj *Nil) String() string           { return ObjectString(obj, nil) }
 
 func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) {
 	if f.typ != nil {
diff --git a/src/go/types/ordering.go b/src/go/types/ordering.go
index 6bb98f2..3579abf 100644
--- a/src/go/types/ordering.go
+++ b/src/go/types/ordering.go
@@ -56,13 +56,9 @@ func (check *Checker) resolveOrder() []Object {
 	// sort interface types topologically by dependencies,
 	// and in source order if there are no dependencies
 	sort.Sort(inSourceOrder(ifaces))
-	if debug {
-		for _, obj := range ifaces {
-			assert(check.objMap[obj].mark == 0)
-		}
-	}
+	visited := make(objSet)
 	for _, obj := range ifaces {
-		check.appendInPostOrder(&order, obj)
+		check.appendInPostOrder(&order, obj, visited)
 	}
 
 	// sort everything else in source order
@@ -89,25 +85,25 @@ func (check *Checker) interfaceFor(obj Object) *ast.InterfaceType {
 	return ityp
 }
 
-func (check *Checker) appendInPostOrder(order *[]Object, obj Object) {
-	d := check.objMap[obj]
-	if d.mark != 0 {
+func (check *Checker) appendInPostOrder(order *[]Object, obj Object, visited objSet) {
+	if visited[obj] {
 		// We've already seen this object; either because it's
 		// already added to order, or because we have a cycle.
 		// In both cases we stop. Cycle errors are reported
 		// when type-checking types.
 		return
 	}
-	d.mark = 1
+	visited[obj] = true
 
+	d := check.objMap[obj]
 	for _, obj := range orderedSetObjects(d.deps) {
-		check.appendInPostOrder(order, obj)
+		check.appendInPostOrder(order, obj, visited)
 	}
 
 	*order = append(*order, obj)
 }
 
-func orderedSetObjects(set map[Object]bool) []Object {
+func orderedSetObjects(set objSet) []Object {
 	list := make([]Object, len(set))
 	i := 0
 	for obj := range set {
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
index 5509069..21fd81e 100644
--- a/src/go/types/predicates.go
+++ b/src/go/types/predicates.go
@@ -112,7 +112,12 @@ func hasNil(typ Type) bool {
 
 // Identical reports whether x and y are identical.
 func Identical(x, y Type) bool {
-	return identical(x, y, nil)
+	return identical(x, y, true, nil)
+}
+
+// IdenticalIgnoreTags reports whether x and y are identical if tags are ignored.
+func IdenticalIgnoreTags(x, y Type) bool {
+	return identical(x, y, false, nil)
 }
 
 // An ifacePair is a node in a stack of interface type pairs compared for identity.
@@ -125,7 +130,7 @@ func (p *ifacePair) identical(q *ifacePair) bool {
 	return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
 }
 
-func identical(x, y Type, p *ifacePair) bool {
+func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
 	if x == y {
 		return true
 	}
@@ -143,13 +148,13 @@ func identical(x, y Type, p *ifacePair) bool {
 		// Two array types are identical if they have identical element types
 		// and the same array length.
 		if y, ok := y.(*Array); ok {
-			return x.len == y.len && identical(x.elem, y.elem, p)
+			return x.len == y.len && identical(x.elem, y.elem, cmpTags, p)
 		}
 
 	case *Slice:
 		// Two slice types are identical if they have identical element types.
 		if y, ok := y.(*Slice); ok {
-			return identical(x.elem, y.elem, p)
+			return identical(x.elem, y.elem, cmpTags, p)
 		}
 
 	case *Struct:
@@ -162,9 +167,9 @@ func identical(x, y Type, p *ifacePair) bool {
 				for i, f := range x.fields {
 					g := y.fields[i]
 					if f.anonymous != g.anonymous ||
-						x.Tag(i) != y.Tag(i) ||
+						cmpTags && x.Tag(i) != y.Tag(i) ||
 						!f.sameId(g.pkg, g.name) ||
-						!identical(f.typ, g.typ, p) {
+						!identical(f.typ, g.typ, cmpTags, p) {
 						return false
 					}
 				}
@@ -175,7 +180,7 @@ func identical(x, y Type, p *ifacePair) bool {
 	case *Pointer:
 		// Two pointer types are identical if they have identical base types.
 		if y, ok := y.(*Pointer); ok {
-			return identical(x.base, y.base, p)
+			return identical(x.base, y.base, cmpTags, p)
 		}
 
 	case *Tuple:
@@ -186,7 +191,7 @@ func identical(x, y Type, p *ifacePair) bool {
 				if x != nil {
 					for i, v := range x.vars {
 						w := y.vars[i]
-						if !identical(v.typ, w.typ, p) {
+						if !identical(v.typ, w.typ, cmpTags, p) {
 							return false
 						}
 					}
@@ -202,8 +207,8 @@ func identical(x, y Type, p *ifacePair) bool {
 		// names are not required to match.
 		if y, ok := y.(*Signature); ok {
 			return x.variadic == y.variadic &&
-				identical(x.params, y.params, p) &&
-				identical(x.results, y.results, p)
+				identical(x.params, y.params, cmpTags, p) &&
+				identical(x.results, y.results, cmpTags, p)
 		}
 
 	case *Interface:
@@ -249,7 +254,7 @@ func identical(x, y Type, p *ifacePair) bool {
 				}
 				for i, f := range a {
 					g := b[i]
-					if f.Id() != g.Id() || !identical(f.typ, g.typ, q) {
+					if f.Id() != g.Id() || !identical(f.typ, g.typ, cmpTags, q) {
 						return false
 					}
 				}
@@ -260,14 +265,14 @@ func identical(x, y Type, p *ifacePair) bool {
 	case *Map:
 		// Two map types are identical if they have identical key and value types.
 		if y, ok := y.(*Map); ok {
-			return identical(x.key, y.key, p) && identical(x.elem, y.elem, p)
+			return identical(x.key, y.key, cmpTags, p) && identical(x.elem, y.elem, cmpTags, p)
 		}
 
 	case *Chan:
 		// Two channel types are identical if they have identical value types
 		// and the same direction.
 		if y, ok := y.(*Chan); ok {
-			return x.dir == y.dir && identical(x.elem, y.elem, p)
+			return x.dir == y.dir && identical(x.elem, y.elem, cmpTags, p)
 		}
 
 	case *Named:
@@ -286,11 +291,11 @@ func identical(x, y Type, p *ifacePair) bool {
 	return false
 }
 
-// defaultType returns the default "typed" type for an "untyped" type;
+// Default returns the default "typed" type for an "untyped" type;
 // it returns the incoming type for all other types. The default type
 // for untyped nil is untyped nil.
 //
-func defaultType(typ Type) Type {
+func Default(typ Type) Type {
 	if t, ok := typ.(*Basic); ok {
 		switch t.kind {
 		case UntypedBool:
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index cb8e72e..046e147 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -14,18 +14,23 @@ import (
 	"unicode"
 )
 
-// A declInfo describes a package-level const, type, var, or func declaration.
+// A declInfo describes a package-level const, type, var, func, or alias declaration.
 type declInfo struct {
 	file  *Scope        // scope of file containing this declaration
 	lhs   []*Var        // lhs of n:1 variable declarations, or nil
 	typ   ast.Expr      // type, or nil
-	init  ast.Expr      // init expression, or nil
+	init  ast.Expr      // init/orig expression, or nil
 	fdecl *ast.FuncDecl // func declaration, or nil
 
-	deps map[Object]bool // type and init dependencies; lazily allocated
-	mark int             // for dependency analysis
+	// The deps field tracks initialization expression dependencies.
+	// As a special (overloaded) case, it also tracks dependencies of
+	// interface types on embedded interfaces (see ordering.go).
+	deps objSet // lazily initialized
 }
 
+// An objSet is simply a set of objects.
+type objSet map[Object]bool
+
 // hasInitializer reports whether the declared object has an initialization
 // expression or function body.
 func (d *declInfo) hasInitializer() bool {
@@ -36,7 +41,7 @@ func (d *declInfo) hasInitializer() bool {
 func (d *declInfo) addDep(obj Object) {
 	m := d.deps
 	if m == nil {
-		m = make(map[Object]bool)
+		m = make(objSet)
 		d.deps = m
 	}
 	m[obj] = true
@@ -269,6 +274,13 @@ func (check *Checker) collectObjects() {
 							check.declare(fileScope, nil, obj, token.NoPos)
 						}
 
+					// Alias-related code. Keep for now.
+					// case *ast.AliasSpec:
+					// 	obj := NewAlias(s.Name.Pos(), pkg, s.Name.Name, nil)
+					// 	obj.typ = nil // unresolved
+					// 	obj.kind = d.Tok
+					// 	check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, init: s.Orig})
+
 					case *ast.ValueSpec:
 						switch d.Tok {
 						case token.CONST:
diff --git a/src/go/types/sizes.go b/src/go/types/sizes.go
index 87c3ce4..3bbe5ae 100644
--- a/src/go/types/sizes.go
+++ b/src/go/types/sizes.go
@@ -64,12 +64,25 @@ func (s *StdSizes) Alignof(T Type) int64 {
 			}
 		}
 		return max
+	case *Slice, *Interface:
+		// Multiword data structures are effectively structs
+		// in which each element has size WordSize.
+		return s.WordSize
+	case *Basic:
+		// Strings are like slices and interfaces.
+		if t.Info()&IsString != 0 {
+			return s.WordSize
+		}
 	}
 	a := s.Sizeof(T) // may be 0
 	// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
 	if a < 1 {
 		return 1
 	}
+	// complex{64,128} are aligned like [2]float{32,64}.
+	if isComplex(T) {
+		a /= 2
+	}
 	if a > s.MaxAlign {
 		return s.MaxAlign
 	}
@@ -132,8 +145,8 @@ func (s *StdSizes) Sizeof(T Type) int64 {
 		if n == 0 {
 			return 0
 		}
-		setOffsets(t, s)
-		return t.offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
+		offsets := s.Offsetsof(t.fields)
+		return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
 	case *Interface:
 		return s.WordSize * 2
 	}
@@ -158,22 +171,18 @@ func (conf *Config) offsetsof(T *Struct) []int64 {
 	if T.NumFields() > 0 {
 		// compute offsets on demand
 		if s := conf.Sizes; s != nil {
-			calculated := setOffsets(T, s)
-			offsets = T.offsets
-			if calculated {
-				// sanity checks
-				if len(offsets) != T.NumFields() {
-					panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
-				}
-				for _, o := range offsets {
-					if o < 0 {
-						panic("Config.Sizes.Offsetsof returned an offset < 0")
-					}
+			offsets = s.Offsetsof(T.fields)
+			// sanity checks
+			if len(offsets) != T.NumFields() {
+				panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
+			}
+			for _, o := range offsets {
+				if o < 0 {
+					panic("Config.Sizes.Offsetsof returned an offset < 0")
 				}
 			}
 		} else {
-			setOffsets(T, &stdSizes)
-			offsets = T.offsets
+			offsets = stdSizes.Offsetsof(T.fields)
 		}
 	}
 	return offsets
@@ -207,15 +216,3 @@ func align(x, a int64) int64 {
 	y := x + a - 1
 	return y - y%a
 }
-
-// setOffsets sets the offsets of s for the given sizes if necessary.
-// The result is true if the offsets were not set before; otherwise it
-// is false.
-func setOffsets(s *Struct, sizes Sizes) bool {
-	var calculated bool
-	s.offsetsOnce.Do(func() {
-		calculated = true
-		s.offsets = sizes.Offsetsof(s.fields)
-	})
-	return calculated
-}
diff --git a/src/go/types/sizes_test.go b/src/go/types/sizes_test.go
new file mode 100644
index 0000000..539b4e3
--- /dev/null
+++ b/src/go/types/sizes_test.go
@@ -0,0 +1,112 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains tests for sizes.
+
+package types_test
+
+import (
+	"go/ast"
+	"go/importer"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"testing"
+)
+
+// findStructType typechecks src and returns the first struct type encountered.
+func findStructType(t *testing.T, src string) *types.Struct {
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "x.go", src, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	info := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
+	var conf types.Config
+	_, err = conf.Check("x", fset, []*ast.File{f}, &info)
+	if err != nil {
+		t.Fatal(err)
+	}
+	for _, tv := range info.Types {
+		if ts, ok := tv.Type.(*types.Struct); ok {
+			return ts
+		}
+	}
+	t.Fatalf("failed to find a struct type in src:\n%s\n", src)
+	return nil
+}
+
+// Issue 16316
+func TestMultipleSizeUse(t *testing.T) {
+	const src = `
+package main
+
+type S struct {
+    i int
+    b bool
+    s string
+    n int
+}
+`
+	ts := findStructType(t, src)
+	sizes := types.StdSizes{WordSize: 4, MaxAlign: 4}
+	if got := sizes.Sizeof(ts); got != 20 {
+		t.Errorf("Sizeof(%v) with WordSize 4 = %d want 20", ts, got)
+	}
+	sizes = types.StdSizes{WordSize: 8, MaxAlign: 8}
+	if got := sizes.Sizeof(ts); got != 40 {
+		t.Errorf("Sizeof(%v) with WordSize 8 = %d want 40", ts, got)
+	}
+}
+
+// Issue 16464
+func TestAlignofNaclSlice(t *testing.T) {
+	const src = `
+package main
+
+var s struct {
+	x *int
+	y []byte
+}
+`
+	ts := findStructType(t, src)
+	sizes := &types.StdSizes{WordSize: 4, MaxAlign: 8}
+	var fields []*types.Var
+	// Make a copy manually :(
+	for i := 0; i < ts.NumFields(); i++ {
+		fields = append(fields, ts.Field(i))
+	}
+	offsets := sizes.Offsetsof(fields)
+	if offsets[0] != 0 || offsets[1] != 4 {
+		t.Errorf("OffsetsOf(%v) = %v want %v", ts, offsets, []int{0, 4})
+	}
+}
+
+func TestIssue16902(t *testing.T) {
+	const src = `
+package a
+
+import "unsafe"
+
+const _ = unsafe.Offsetof(struct{ x int64 }{}.x)
+`
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "x.go", src, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	info := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
+	conf := types.Config{
+		Importer: importer.Default(),
+		Sizes:    &types.StdSizes{WordSize: 8, MaxAlign: 8},
+	}
+	_, err = conf.Check("x", fset, []*ast.File{f}, &info)
+	if err != nil {
+		t.Fatal(err)
+	}
+	for _, tv := range info.Types {
+		_ = conf.Sizes.Sizeof(tv.Type)
+		_ = conf.Sizes.Alignof(tv.Type)
+	}
+}
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
index bd5afaf..1c6d7b5 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -156,6 +156,7 @@ func TestStdFixed(t *testing.T) {
 		"issue7746.go",  // large constants - consumes too much memory
 		"issue11362.go", // canonical import path check
 		"issue15002.go", // uses Mmap; testTestDir should consult build tags
+		"issue16369.go", // go/types handles this correctly - not an issue
 	)
 }
 
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go
index 5764430..4e423bd 100644
--- a/src/go/types/stmt.go
+++ b/src/go/types/stmt.go
@@ -68,13 +68,19 @@ func (check *Checker) usage(scope *Scope) {
 }
 
 // stmtContext is a bitset describing which
-// control-flow statements are permissible.
+// control-flow statements are permissible,
+// and provides additional context information
+// for better error messages.
 type stmtContext uint
 
 const (
+	// permissible control-flow statements
 	breakOk stmtContext = 1 << iota
 	continueOk
 	fallthroughOk
+
+	// additional context information
+	finalSwitchCase
 )
 
 func (check *Checker) simpleStmt(s ast.Stmt) {
@@ -292,7 +298,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
 		}(check.scope)
 	}
 
-	inner := ctxt &^ fallthroughOk
+	inner := ctxt &^ (fallthroughOk | finalSwitchCase)
 	switch s := s.(type) {
 	case *ast.BadStmt, *ast.EmptyStmt:
 		// ignore
@@ -454,7 +460,11 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
 			}
 		case token.FALLTHROUGH:
 			if ctxt&fallthroughOk == 0 {
-				check.error(s.Pos(), "fallthrough statement out of place")
+				msg := "fallthrough statement out of place"
+				if ctxt&finalSwitchCase != 0 {
+					msg = "cannot fallthrough final case in switch"
+				}
+				check.error(s.Pos(), msg)
 			}
 		default:
 			check.invalidAST(s.Pos(), "branch statement: %s", s.Tok)
@@ -523,6 +533,8 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
 			inner := inner
 			if i+1 < len(s.Body.List) {
 				inner |= fallthroughOk
+			} else {
+				inner |= finalSwitchCase
 			}
 			check.stmtList(inner, clause.Body)
 			check.closeScope()
@@ -616,9 +628,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
 					T = x.typ
 				}
 				obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
-				scopePos := clause.End()
-				if len(clause.Body) > 0 {
-					scopePos = clause.Body[0].Pos()
+				scopePos := clause.Pos() + token.Pos(len("default")) // for default clause (len(List) == 0)
+				if n := len(clause.List); n > 0 {
+					scopePos = clause.List[n-1].End()
 				}
 				check.declare(check.scope, nil, obj, scopePos)
 				check.recordImplicit(clause, obj)
@@ -810,12 +822,12 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
 
 			// declare variables
 			if len(vars) > 0 {
+				scopePos := s.X.End()
 				for _, obj := range vars {
 					// spec: "The scope of a constant or variable identifier declared inside
 					// a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
 					// for short variable declarations) and ends at the end of the innermost
 					// containing block."
-					scopePos := s.End()
 					check.declare(check.scope, nil /* recordDef already called */, obj, scopePos)
 				}
 			} else {
diff --git a/src/go/types/testdata/conversions2.src b/src/go/types/testdata/conversions2.src
new file mode 100644
index 0000000..93a5f18
--- /dev/null
+++ b/src/go/types/testdata/conversions2.src
@@ -0,0 +1,313 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test various valid and invalid struct assignments and conversions.
+// Does not compile.
+
+package conversions2
+
+type I interface {
+	m()
+}
+
+// conversions between structs
+
+func _() {
+	type S struct{}
+	type T struct{}
+	var s S
+	var t T
+	var u struct{}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u
+	s = S(s)
+	s = S(t)
+	s = S(u)
+	t = u
+	t = T(u)
+}
+
+func _() {
+	type S struct{ x int }
+	type T struct {
+		x int "foo"
+	}
+	var s S
+	var t T
+	var u struct {
+		x int "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = S(s)
+	s = S(t)
+	s = S(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = T(u)
+}
+
+func _() {
+	type E struct{ x int }
+	type S struct{ x E }
+	type T struct {
+		x E "foo"
+	}
+	var s S
+	var t T
+	var u struct {
+		x E "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = S(s)
+	s = S(t)
+	s = S(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = T(u)
+}
+
+func _() {
+	type S struct {
+		x struct {
+			x int "foo"
+		}
+	}
+	type T struct {
+		x struct {
+			x int "bar"
+		} "foo"
+	}
+	var s S
+	var t T
+	var u struct {
+		x struct {
+			x int "bar"
+		} "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = S(s)
+	s = S(t)
+	s = S(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = T(u)
+}
+
+func _() {
+	type E1 struct {
+		x int "foo"
+	}
+	type E2 struct {
+		x int "bar"
+	}
+	type S struct{ x E1 }
+	type T struct {
+		x E2 "foo"
+	}
+	var s S
+	var t T
+	var u struct {
+		x E2 "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = S(s)
+	s = S(t /* ERROR "cannot convert" */ )
+	s = S(u /* ERROR "cannot convert" */ )
+	t = u   // ERROR "cannot use .* in assignment"
+	t = T(u)
+}
+
+func _() {
+	type E struct{ x int }
+	type S struct {
+		f func(struct {
+			x int "foo"
+		})
+	}
+	type T struct {
+		f func(struct {
+			x int "bar"
+		})
+	}
+	var s S
+	var t T
+	var u struct{ f func(E) }
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = S(s)
+	s = S(t)
+	s = S(u /* ERROR "cannot convert" */ )
+	t = u   // ERROR "cannot use .* in assignment"
+	t = T(u /* ERROR "cannot convert" */ )
+}
+
+// conversions between pointers to structs
+
+func _() {
+	type S struct{}
+	type T struct{}
+	var s *S
+	var t *T
+	var u *struct{}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = (*T)(u)
+}
+
+func _() {
+	type S struct{ x int }
+	type T struct {
+		x int "foo"
+	}
+	var s *S
+	var t *T
+	var u *struct {
+		x int "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = (*T)(u)
+}
+
+func _() {
+	type E struct{ x int }
+	type S struct{ x E }
+	type T struct {
+		x E "foo"
+	}
+	var s *S
+	var t *T
+	var u *struct {
+		x E "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = (*T)(u)
+}
+
+func _() {
+	type S struct {
+		x struct {
+			x int "foo"
+		}
+	}
+	type T struct {
+		x struct {
+			x int "bar"
+		} "foo"
+	}
+	var s *S
+	var t *T
+	var u *struct {
+		x struct {
+			x int "bar"
+		} "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = (*T)(u)
+}
+
+func _() {
+	type E1 struct {
+		x int "foo"
+	}
+	type E2 struct {
+		x int "bar"
+	}
+	type S struct{ x E1 }
+	type T struct {
+		x E2 "foo"
+	}
+	var s *S
+	var t *T
+	var u *struct {
+		x E2 "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t /* ERROR "cannot convert" */ )
+	s = (*S)(u /* ERROR "cannot convert" */ )
+	t = u      // ERROR "cannot use .* in assignment"
+	t = (*T)(u)
+}
+
+func _() {
+	type E struct{ x int }
+	type S struct {
+		f func(struct {
+			x int "foo"
+		})
+	}
+	type T struct {
+		f func(struct {
+			x int "bar"
+		})
+	}
+	var s *S
+	var t *T
+	var u *struct{ f func(E) }
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u /* ERROR "cannot convert" */ )
+	t = u      // ERROR "cannot use .* in assignment"
+	t = (*T)(u /* ERROR "cannot convert" */ )
+}
+
+func _() {
+	type E struct{ x int }
+	type S struct {
+		f func(*struct {
+			x int "foo"
+		})
+	}
+	type T struct {
+		f func(*struct {
+			x int "bar"
+		})
+	}
+	var s *S
+	var t *T
+	var u *struct{ f func(E) }
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u /* ERROR "cannot convert" */ )
+	t = u      // ERROR "cannot use .* in assignment"
+	t = (*T)(u /* ERROR "cannot convert" */ )
+}
diff --git a/src/go/types/testdata/expr3.src b/src/go/types/testdata/expr3.src
index 53c03e7..ab1a9f6 100644
--- a/src/go/types/testdata/expr3.src
+++ b/src/go/types/testdata/expr3.src
@@ -324,6 +324,22 @@ func slice_literals() {
 
 	// recursively so
 	_ = [][]T{{}, []T{{}}, {{1, 2, 3}}}
+
+	// issue 17954
+	type T0 *struct { s string }
+	_ = []T0{{}}
+	_ = []T0{{"foo"}}
+
+	type T1 *struct{ int }
+	_ = []T1{}
+	_ = []T1{{0}, {1}, {2}}
+
+	type T2 T1
+	_ = []T2{}
+	_ = []T2{{0}, {1}, {2}}
+
+	_ = map[T0]T2{}
+	_ = map[T0]T2{{}: {}}
 }
 
 const index2 int = 2
@@ -393,6 +409,14 @@ func map_literals() {
 	type Point struct { x, y float32 }
 	_ = map[string]Point{"orig": {0, 0}}
 	_ = map[*Point]string{{0, 0}: "orig"}
+
+	// issue 17954
+	type T0 *struct{ s string }
+	type T1 *struct{ int }
+	type T2 T1
+
+	_ = map[T0]T2{}
+	_ = map[T0]T2{{}: {}}
 }
 
 var key2 string = "bar"
diff --git a/src/go/types/testdata/stmt0.src b/src/go/types/testdata/stmt0.src
index 0c727c3..87f08e4 100644
--- a/src/go/types/testdata/stmt0.src
+++ b/src/go/types/testdata/stmt0.src
@@ -536,7 +536,7 @@ func switches1() {
 	default:
 		fallthrough; ;
 	case 4:
-		fallthrough /* ERROR "fallthrough statement out of place" */
+		fallthrough /* ERROR "cannot fallthrough final case in switch" */
 	}
 
 	var y interface{}
@@ -573,7 +573,7 @@ func switches1() {
 		goto L6
 		goto L7
 		goto L8
-		L6: L7: L8: fallthrough /* ERROR "fallthrough statement out of place" */
+		L6: L7: L8: fallthrough /* ERROR "cannot fallthrough final case in switch" */
 	}
 
 	switch x {
@@ -589,6 +589,14 @@ func switches1() {
 		fallthrough /* ERROR "fallthrough statement out of place" */
 		{ /* empty block is not an empty statement */ }; ;
 	default:
+		fallthrough /* ERROR "cannot fallthrough final case in switch" */
+	}
+
+	switch x {
+	case 0:
+		{
+			fallthrough /* ERROR "fallthrough statement out of place" */
+		}
 	}
 }
 
diff --git a/src/go/types/type.go b/src/go/types/type.go
index 4e00da3..01adee8 100644
--- a/src/go/types/type.go
+++ b/src/go/types/type.go
@@ -4,10 +4,7 @@
 
 package types
 
-import (
-	"sort"
-	"sync"
-)
+import "sort"
 
 // A Type represents a type of Go.
 // All types implement the Type interface.
@@ -121,10 +118,8 @@ func (s *Slice) Elem() Type { return s.elem }
 
 // A Struct represents a struct type.
 type Struct struct {
-	fields      []*Var
-	tags        []string  // field tags; nil if there are no tags
-	offsets     []int64   // field offsets in bytes, lazily initialized
-	offsetsOnce sync.Once // for threadsafe lazy initialization of offsets
+	fields []*Var
+	tags   []string // field tags; nil if there are no tags
 }
 
 // NewStruct returns a new struct with the given fields and corresponding field tags.
diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go
index 931b924..ecc0a7d 100644
--- a/src/go/types/typexpr.go
+++ b/src/go/types/typexpr.go
@@ -45,6 +45,17 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa
 		delete(check.unusedDotImports[scope], pkg)
 	}
 
+	// Alias-related code. Keep for now.
+	// An alias stands for the original object; use that one instead.
+	// TODO(gri) We should be able to factor out the Typ[Invalid] test.
+	// if alias, _ := obj.(*Alias); alias != nil {
+	// 	obj = original(obj)
+	// 	if obj == nil || typ == Typ[Invalid] {
+	// 		return
+	// 	}
+	// 	assert(typ == obj.Type())
+	// }
+
 	switch obj := obj.(type) {
 	case *PkgName:
 		check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
@@ -623,8 +634,7 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
 	// current field typ and tag
 	var typ Type
 	var tag string
-	// anonymous != nil indicates an anonymous field.
-	add := func(field *ast.Field, ident *ast.Ident, anonymous *TypeName, pos token.Pos) {
+	add := func(field *ast.Field, ident *ast.Ident, anonymous bool, pos token.Pos) {
 		if tag != "" && tags == nil {
 			tags = make([]string, len(fields))
 		}
@@ -633,15 +643,12 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
 		}
 
 		name := ident.Name
-		fld := NewField(pos, check.pkg, name, typ, anonymous != nil)
+		fld := NewField(pos, check.pkg, name, typ, anonymous)
 		// spec: "Within a struct, non-blank field names must be unique."
 		if name == "_" || check.declareInSet(&fset, pos, fld) {
 			fields = append(fields, fld)
 			check.recordDef(ident, fld)
 		}
-		if anonymous != nil {
-			check.recordUse(ident, anonymous)
-		}
 	}
 
 	for _, f := range list.List {
@@ -650,7 +657,7 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
 		if len(f.Names) > 0 {
 			// named fields
 			for _, name := range f.Names {
-				add(f, name, nil, name.Pos())
+				add(f, name, false, name.Pos())
 			}
 		} else {
 			// anonymous field
@@ -668,7 +675,7 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
 					check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
 					continue
 				}
-				add(f, name, Universe.Lookup(t.name).(*TypeName), pos)
+				add(f, name, true, pos)
 
 			case *Named:
 				// spec: "An embedded type must be specified as a type name
@@ -690,7 +697,7 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
 						continue
 					}
 				}
-				add(f, name, t.obj, pos)
+				add(f, name, true, pos)
 
 			default:
 				check.invalidAST(pos, "anonymous field type %s must be named", typ)
diff --git a/src/hash/crc32/crc32.go b/src/hash/crc32/crc32.go
index c3ac7b8..8aa91b1 100644
--- a/src/hash/crc32/crc32.go
+++ b/src/hash/crc32/crc32.go
@@ -20,9 +20,6 @@ import (
 // The size of a CRC-32 checksum in bytes.
 const Size = 4
 
-// Use "slice by 8" when payload >= this value.
-const sliceBy8Cutoff = 16
-
 // Predefined polynomials.
 const (
 	// IEEE is by far and away the most common CRC-32 polynomial.
@@ -43,71 +40,96 @@ const (
 // Table is a 256-word table representing the polynomial for efficient processing.
 type Table [256]uint32
 
+// This file makes use of functions implemented in architecture-specific files.
+// The interface that they implement is as follows:
+//
+//    // archAvailableIEEE reports whether an architecture-specific CRC32-IEEE
+//    // algorithm is available.
+//    archAvailableIEEE() bool
+//
+//    // archInitIEEE initializes the architecture-specific CRC3-IEEE algorithm.
+//    // It can only be called if archAvailableIEEE() returns true.
+//    archInitIEEE()
+//
+//    // archUpdateIEEE updates the given CRC32-IEEE. It can only be called if
+//    // archInitIEEE() was previously called.
+//    archUpdateIEEE(crc uint32, p []byte) uint32
+//
+//    // archAvailableCastagnoli reports whether an architecture-specific
+//    // CRC32-C algorithm is available.
+//    archAvailableCastagnoli() bool
+//
+//    // archInitCastagnoli initializes the architecture-specific CRC32-C
+//    // algorithm. It can only be called if archAvailableCastagnoli() returns
+//    // true.
+//    archInitCastagnoli()
+//
+//    // archUpdateCastagnoli updates the given CRC32-C. It can only be called
+//    // if archInitCastagnoli() was previously called.
+//    archUpdateCastagnoli(crc uint32, p []byte) uint32
+
 // castagnoliTable points to a lazily initialized Table for the Castagnoli
 // polynomial. MakeTable will always return this value when asked to make a
 // Castagnoli table so we can compare against it to find when the caller is
 // using this polynomial.
 var castagnoliTable *Table
 var castagnoliTable8 *slicing8Table
+var castagnoliArchImpl bool
+var updateCastagnoli func(crc uint32, p []byte) uint32
 var castagnoliOnce sync.Once
 
 func castagnoliInit() {
-	castagnoliTable = makeTable(Castagnoli)
-	castagnoliTable8 = makeTable8(Castagnoli)
+	castagnoliTable = simpleMakeTable(Castagnoli)
+	castagnoliArchImpl = archAvailableCastagnoli()
+
+	if castagnoliArchImpl {
+		archInitCastagnoli()
+		updateCastagnoli = archUpdateCastagnoli
+	} else {
+		// Initialize the slicing-by-8 table.
+		castagnoliTable8 = slicingMakeTable(Castagnoli)
+		updateCastagnoli = func(crc uint32, p []byte) uint32 {
+			return slicingUpdate(crc, castagnoliTable8, p)
+		}
+	}
 }
 
 // IEEETable is the table for the IEEE polynomial.
-var IEEETable = makeTable(IEEE)
-
-// slicing8Table is array of 8 Tables
-type slicing8Table [8]Table
+var IEEETable = simpleMakeTable(IEEE)
 
 // ieeeTable8 is the slicing8Table for IEEE
 var ieeeTable8 *slicing8Table
-var ieeeTable8Once sync.Once
+var ieeeArchImpl bool
+var updateIEEE func(crc uint32, p []byte) uint32
+var ieeeOnce sync.Once
+
+func ieeeInit() {
+	ieeeArchImpl = archAvailableIEEE()
+
+	if ieeeArchImpl {
+		archInitIEEE()
+		updateIEEE = archUpdateIEEE
+	} else {
+		// Initialize the slicing-by-8 table.
+		ieeeTable8 = slicingMakeTable(IEEE)
+		updateIEEE = func(crc uint32, p []byte) uint32 {
+			return slicingUpdate(crc, ieeeTable8, p)
+		}
+	}
+}
 
 // MakeTable returns a Table constructed from the specified polynomial.
 // The contents of this Table must not be modified.
 func MakeTable(poly uint32) *Table {
 	switch poly {
 	case IEEE:
+		ieeeOnce.Do(ieeeInit)
 		return IEEETable
 	case Castagnoli:
 		castagnoliOnce.Do(castagnoliInit)
 		return castagnoliTable
 	}
-	return makeTable(poly)
-}
-
-// makeTable returns the Table constructed from the specified polynomial.
-func makeTable(poly uint32) *Table {
-	t := new(Table)
-	for i := 0; i < 256; i++ {
-		crc := uint32(i)
-		for j := 0; j < 8; j++ {
-			if crc&1 == 1 {
-				crc = (crc >> 1) ^ poly
-			} else {
-				crc >>= 1
-			}
-		}
-		t[i] = crc
-	}
-	return t
-}
-
-// makeTable8 returns slicing8Table constructed from the specified polynomial.
-func makeTable8(poly uint32) *slicing8Table {
-	t := new(slicing8Table)
-	t[0] = *makeTable(poly)
-	for i := 0; i < 256; i++ {
-		crc := t[0][i]
-		for j := 1; j < 8; j++ {
-			crc = t[0][crc&0xFF] ^ (crc >> 8)
-			t[j][i] = crc
-		}
-	}
-	return t
+	return simpleMakeTable(poly)
 }
 
 // digest represents the partial evaluation of a checksum.
@@ -119,7 +141,12 @@ type digest struct {
 // New creates a new hash.Hash32 computing the CRC-32 checksum
 // using the polynomial represented by the Table.
 // Its Sum method will lay the value out in big-endian byte order.
-func New(tab *Table) hash.Hash32 { return &digest{0, tab} }
+func New(tab *Table) hash.Hash32 {
+	if tab == IEEETable {
+		ieeeOnce.Do(ieeeInit)
+	}
+	return &digest{0, tab}
+}
 
 // NewIEEE creates a new hash.Hash32 computing the CRC-32 checksum
 // using the IEEE polynomial.
@@ -132,44 +159,32 @@ func (d *digest) BlockSize() int { return 1 }
 
 func (d *digest) Reset() { d.crc = 0 }
 
-func update(crc uint32, tab *Table, p []byte) uint32 {
-	crc = ^crc
-	for _, v := range p {
-		crc = tab[byte(crc)^v] ^ (crc >> 8)
-	}
-	return ^crc
-}
-
-// updateSlicingBy8 updates CRC using Slicing-by-8
-func updateSlicingBy8(crc uint32, tab *slicing8Table, p []byte) uint32 {
-	crc = ^crc
-	for len(p) > 8 {
-		crc ^= uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24
-		crc = tab[0][p[7]] ^ tab[1][p[6]] ^ tab[2][p[5]] ^ tab[3][p[4]] ^
-			tab[4][crc>>24] ^ tab[5][(crc>>16)&0xFF] ^
-			tab[6][(crc>>8)&0xFF] ^ tab[7][crc&0xFF]
-		p = p[8:]
-	}
-	crc = ^crc
-	if len(p) == 0 {
-		return crc
-	}
-	return update(crc, &tab[0], p)
-}
-
 // Update returns the result of adding the bytes in p to the crc.
 func Update(crc uint32, tab *Table, p []byte) uint32 {
 	switch tab {
 	case castagnoliTable:
 		return updateCastagnoli(crc, p)
 	case IEEETable:
+		// Unfortunately, because IEEETable is exported, IEEE may be used without a
+		// call to MakeTable. We have to make sure it gets initialized in that case.
+		ieeeOnce.Do(ieeeInit)
 		return updateIEEE(crc, p)
+	default:
+		return simpleUpdate(crc, tab, p)
 	}
-	return update(crc, tab, p)
 }
 
 func (d *digest) Write(p []byte) (n int, err error) {
-	d.crc = Update(d.crc, d.tab, p)
+	switch d.tab {
+	case castagnoliTable:
+		d.crc = updateCastagnoli(d.crc, p)
+	case IEEETable:
+		// We only create digest objects through New() which takes care of
+		// initialization in this case.
+		d.crc = updateIEEE(d.crc, p)
+	default:
+		d.crc = simpleUpdate(d.crc, d.tab, p)
+	}
 	return len(p), nil
 }
 
@@ -186,4 +201,7 @@ func Checksum(data []byte, tab *Table) uint32 { return Update(0, tab, data) }
 
 // ChecksumIEEE returns the CRC-32 checksum of data
 // using the IEEE polynomial.
-func ChecksumIEEE(data []byte) uint32 { return updateIEEE(0, data) }
+func ChecksumIEEE(data []byte) uint32 {
+	ieeeOnce.Do(ieeeInit)
+	return updateIEEE(0, data)
+}
diff --git a/src/hash/crc32/crc32_amd64.go b/src/hash/crc32/crc32_amd64.go
index a0180a1..369a436 100644
--- a/src/hash/crc32/crc32_amd64.go
+++ b/src/hash/crc32/crc32_amd64.go
@@ -2,8 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// AMD64-specific hardware-assisted CRC32 algorithms. See crc32.go for a
+// description of the interface that each architecture-specific file
+// implements.
+
 package crc32
 
+import "unsafe"
+
 // This file contains the code to call the SSE 4.2 version of the Castagnoli
 // and IEEE CRC.
 
@@ -13,11 +19,20 @@ func haveSSE41() bool
 func haveSSE42() bool
 func haveCLMUL() bool
 
-// castagnoliSSE42 is defined in crc_amd64.s and uses the SSE4.2 CRC32
+// castagnoliSSE42 is defined in crc32_amd64.s and uses the SSE4.2 CRC32
 // instruction.
 //go:noescape
 func castagnoliSSE42(crc uint32, p []byte) uint32
 
+// castagnoliSSE42Triple is defined in crc32_amd64.s and uses the SSE4.2 CRC32
+// instruction.
+//go:noescape
+func castagnoliSSE42Triple(
+	crcA, crcB, crcC uint32,
+	a, b, c []byte,
+	rounds uint32,
+) (retA uint32, retB uint32, retC uint32)
+
 // ieeeCLMUL is defined in crc_amd64.s and uses the PCLMULQDQ
 // instruction as well as SSE 4.1.
 //go:noescape
@@ -26,35 +41,188 @@ func ieeeCLMUL(crc uint32, p []byte) uint32
 var sse42 = haveSSE42()
 var useFastIEEE = haveCLMUL() && haveSSE41()
 
-func updateCastagnoli(crc uint32, p []byte) uint32 {
-	if sse42 {
-		return castagnoliSSE42(crc, p)
+const castagnoliK1 = 168
+const castagnoliK2 = 1344
+
+type sse42Table [4]Table
+
+var castagnoliSSE42TableK1 *sse42Table
+var castagnoliSSE42TableK2 *sse42Table
+
+func archAvailableCastagnoli() bool {
+	return sse42
+}
+
+func archInitCastagnoli() {
+	if !sse42 {
+		panic("arch-specific Castagnoli not available")
 	}
-	// Use slicing-by-8 on larger inputs.
-	if len(p) >= sliceBy8Cutoff {
-		return updateSlicingBy8(crc, castagnoliTable8, p)
+	castagnoliSSE42TableK1 = new(sse42Table)
+	castagnoliSSE42TableK2 = new(sse42Table)
+	// See description in updateCastagnoli.
+	//    t[0][i] = CRC(i000, O)
+	//    t[1][i] = CRC(0i00, O)
+	//    t[2][i] = CRC(00i0, O)
+	//    t[3][i] = CRC(000i, O)
+	// where O is a sequence of K zeros.
+	var tmp [castagnoliK2]byte
+	for b := 0; b < 4; b++ {
+		for i := 0; i < 256; i++ {
+			val := uint32(i) << uint32(b*8)
+			castagnoliSSE42TableK1[b][i] = castagnoliSSE42(val, tmp[:castagnoliK1])
+			castagnoliSSE42TableK2[b][i] = castagnoliSSE42(val, tmp[:])
+		}
 	}
-	return update(crc, castagnoliTable, p)
 }
 
-func updateIEEE(crc uint32, p []byte) uint32 {
-	if useFastIEEE && len(p) >= 64 {
-		left := len(p) & 15
-		do := len(p) - left
-		crc = ^ieeeCLMUL(^crc, p[:do])
-		if left > 0 {
-			crc = update(crc, IEEETable, p[do:])
+// castagnoliShift computes the CRC32-C of K1 or K2 zeroes (depending on the
+// table given) with the given initial crc value. This corresponds to
+// CRC(crc, O) in the description in updateCastagnoli.
+func castagnoliShift(table *sse42Table, crc uint32) uint32 {
+	return table[3][crc>>24] ^
+		table[2][(crc>>16)&0xFF] ^
+		table[1][(crc>>8)&0xFF] ^
+		table[0][crc&0xFF]
+}
+
+func archUpdateCastagnoli(crc uint32, p []byte) uint32 {
+	if !sse42 {
+		panic("not available")
+	}
+
+	// This method is inspired from the algorithm in Intel's white paper:
+	//    "Fast CRC Computation for iSCSI Polynomial Using CRC32 Instruction"
+	// The same strategy of splitting the buffer in three is used but the
+	// combining calculation is different; the complete derivation is explained
+	// below.
+	//
+	// -- The basic idea --
+	//
+	// The CRC32 instruction (available in SSE4.2) can process 8 bytes at a
+	// time. In recent Intel architectures the instruction takes 3 cycles;
+	// however the processor can pipeline up to three instructions if they
+	// don't depend on each other.
+	//
+	// Roughly this means that we can process three buffers in about the same
+	// time we can process one buffer.
+	//
+	// The idea is then to split the buffer in three, CRC the three pieces
+	// separately and then combine the results.
+	//
+	// Combining the results requires precomputed tables, so we must choose a
+	// fixed buffer length to optimize. The longer the length, the faster; but
+	// only buffers longer than this length will use the optimization. We choose
+	// two cutoffs and compute tables for both:
+	//  - one around 512: 168*3=504
+	//  - one around 4KB: 1344*3=4032
+	//
+	// -- The nitty gritty --
+	//
+	// Let CRC(I, X) be the non-inverted CRC32-C of the sequence X (with
+	// initial non-inverted CRC I). This function has the following properties:
+	//   (a) CRC(I, AB) = CRC(CRC(I, A), B)
+	//   (b) CRC(I, A xor B) = CRC(I, A) xor CRC(0, B)
+	//
+	// Say we want to compute CRC(I, ABC) where A, B, C are three sequences of
+	// K bytes each, where K is a fixed constant. Let O be the sequence of K zero
+	// bytes.
+	//
+	// CRC(I, ABC) = CRC(I, ABO xor C)
+	//             = CRC(I, ABO) xor CRC(0, C)
+	//             = CRC(CRC(I, AB), O) xor CRC(0, C)
+	//             = CRC(CRC(I, AO xor B), O) xor CRC(0, C)
+	//             = CRC(CRC(I, AO) xor CRC(0, B), O) xor CRC(0, C)
+	//             = CRC(CRC(CRC(I, A), O) xor CRC(0, B), O) xor CRC(0, C)
+	//
+	// The castagnoliSSE42Triple function can compute CRC(I, A), CRC(0, B),
+	// and CRC(0, C) efficiently.  We just need to find a way to quickly compute
+	// CRC(uvwx, O) given a 4-byte initial value uvwx. We can precompute these
+	// values; since we can't have a 32-bit table, we break it up into four
+	// 8-bit tables:
+	//
+	//    CRC(uvwx, O) = CRC(u000, O) xor
+	//                   CRC(0v00, O) xor
+	//                   CRC(00w0, O) xor
+	//                   CRC(000x, O)
+	//
+	// We can compute tables corresponding to the four terms for all 8-bit
+	// values.
+
+	crc = ^crc
+
+	// If a buffer is long enough to use the optimization, process the first few
+	// bytes to align the buffer to an 8 byte boundary (if necessary).
+	if len(p) >= castagnoliK1*3 {
+		delta := int(uintptr(unsafe.Pointer(&p[0])) & 7)
+		if delta != 0 {
+			delta = 8 - delta
+			crc = castagnoliSSE42(crc, p[:delta])
+			p = p[delta:]
 		}
-		return crc
 	}
 
-	// Use slicing-by-8 on larger inputs.
-	if len(p) >= sliceBy8Cutoff {
-		ieeeTable8Once.Do(func() {
-			ieeeTable8 = makeTable8(IEEE)
-		})
-		return updateSlicingBy8(crc, ieeeTable8, p)
+	// Process 3*K2 at a time.
+	for len(p) >= castagnoliK2*3 {
+		// Compute CRC(I, A), CRC(0, B), and CRC(0, C).
+		crcA, crcB, crcC := castagnoliSSE42Triple(
+			crc, 0, 0,
+			p, p[castagnoliK2:], p[castagnoliK2*2:],
+			castagnoliK2/24)
+
+		// CRC(I, AB) = CRC(CRC(I, A), O) xor CRC(0, B)
+		crcAB := castagnoliShift(castagnoliSSE42TableK2, crcA) ^ crcB
+		// CRC(I, ABC) = CRC(CRC(I, AB), O) xor CRC(0, C)
+		crc = castagnoliShift(castagnoliSSE42TableK2, crcAB) ^ crcC
+		p = p[castagnoliK2*3:]
+	}
+
+	// Process 3*K1 at a time.
+	for len(p) >= castagnoliK1*3 {
+		// Compute CRC(I, A), CRC(0, B), and CRC(0, C).
+		crcA, crcB, crcC := castagnoliSSE42Triple(
+			crc, 0, 0,
+			p, p[castagnoliK1:], p[castagnoliK1*2:],
+			castagnoliK1/24)
+
+		// CRC(I, AB) = CRC(CRC(I, A), O) xor CRC(0, B)
+		crcAB := castagnoliShift(castagnoliSSE42TableK1, crcA) ^ crcB
+		// CRC(I, ABC) = CRC(CRC(I, AB), O) xor CRC(0, C)
+		crc = castagnoliShift(castagnoliSSE42TableK1, crcAB) ^ crcC
+		p = p[castagnoliK1*3:]
+	}
+
+	// Use the simple implementation for what's left.
+	crc = castagnoliSSE42(crc, p)
+	return ^crc
+}
+
+func archAvailableIEEE() bool {
+	return useFastIEEE
+}
+
+var archIeeeTable8 *slicing8Table
+
+func archInitIEEE() {
+	if !useFastIEEE {
+		panic("not available")
 	}
+	// We still use slicing-by-8 for small buffers.
+	archIeeeTable8 = slicingMakeTable(IEEE)
+}
 
-	return update(crc, IEEETable, p)
+func archUpdateIEEE(crc uint32, p []byte) uint32 {
+	if !useFastIEEE {
+		panic("not available")
+	}
+
+	if len(p) >= 64 {
+		left := len(p) & 15
+		do := len(p) - left
+		crc = ^ieeeCLMUL(^crc, p[:do])
+		p = p[do:]
+	}
+	if len(p) == 0 {
+		return crc
+	}
+	return slicingUpdate(crc, archIeeeTable8, p)
 }
diff --git a/src/hash/crc32/crc32_amd64.s b/src/hash/crc32/crc32_amd64.s
index caacfae..50c0ec8 100644
--- a/src/hash/crc32/crc32_amd64.s
+++ b/src/hash/crc32/crc32_amd64.s
@@ -4,54 +4,136 @@
 
 #include "textflag.h"
 
+// castagnoliSSE42 updates the (non-inverted) crc with the given buffer.
+//
 // func castagnoliSSE42(crc uint32, p []byte) uint32
 TEXT ·castagnoliSSE42(SB),NOSPLIT,$0
 	MOVL crc+0(FP), AX  // CRC value
 	MOVQ p+8(FP), SI  // data pointer
 	MOVQ p_len+16(FP), CX  // len(p)
 
-	NOTL AX
-
-	/* If there's less than 8 bytes to process, we do it byte-by-byte. */
+	// If there are fewer than 8 bytes to process, skip alignment.
 	CMPQ CX, $8
-	JL cleanup
+	JL less_than_8
 
-	/* Process individual bytes until the input is 8-byte aligned. */
-startup:
 	MOVQ SI, BX
 	ANDQ $7, BX
 	JZ aligned
 
+	// Process the first few bytes to 8-byte align the input.
+
+	// BX = 8 - BX. We need to process this many bytes to align.
+	SUBQ $1, BX
+	XORQ $7, BX
+
+	BTQ $0, BX
+	JNC align_2
+
 	CRC32B (SI), AX
 	DECQ CX
 	INCQ SI
-	JMP startup
+
+align_2:
+	BTQ $1, BX
+	JNC align_4
+
+	// CRC32W (SI), AX
+	BYTE $0x66; BYTE $0xf2; BYTE $0x0f; BYTE $0x38; BYTE $0xf1; BYTE $0x06
+
+	SUBQ $2, CX
+	ADDQ $2, SI
+
+align_4:
+	BTQ $2, BX
+	JNC aligned
+
+	// CRC32L (SI), AX
+	BYTE $0xf2; BYTE $0x0f; BYTE $0x38; BYTE $0xf1; BYTE $0x06
+
+	SUBQ $4, CX
+	ADDQ $4, SI
 
 aligned:
-	/* The input is now 8-byte aligned and we can process 8-byte chunks. */
+	// The input is now 8-byte aligned and we can process 8-byte chunks.
 	CMPQ CX, $8
-	JL cleanup
+	JL less_than_8
 
 	CRC32Q (SI), AX
 	ADDQ $8, SI
 	SUBQ $8, CX
 	JMP aligned
 
-cleanup:
-	/* We may have some bytes left over that we process one at a time. */
-	CMPQ CX, $0
-	JE done
+less_than_8:
+	// We may have some bytes left over; process 4 bytes, then 2, then 1.
+	BTQ $2, CX
+	JNC less_than_4
+
+	// CRC32L (SI), AX
+	BYTE $0xf2; BYTE $0x0f; BYTE $0x38; BYTE $0xf1; BYTE $0x06
+	ADDQ $4, SI
+
+less_than_4:
+	BTQ $1, CX
+	JNC less_than_2
+
+	// CRC32W (SI), AX
+	BYTE $0x66; BYTE $0xf2; BYTE $0x0f; BYTE $0x38; BYTE $0xf1; BYTE $0x06
+	ADDQ $2, SI
+
+less_than_2:
+	BTQ $0, CX
+	JNC done
 
 	CRC32B (SI), AX
-	INCQ SI
-	DECQ CX
-	JMP cleanup
 
 done:
-	NOTL AX
 	MOVL AX, ret+32(FP)
 	RET
 
+// castagnoliSSE42Triple updates three (non-inverted) crcs with (24*rounds)
+// bytes from each buffer.
+//
+// func castagnoliSSE42Triple(
+//     crc1, crc2, crc3 uint32,
+//     a, b, c []byte,
+//     rounds uint32,
+// ) (retA uint32, retB uint32, retC uint32)
+TEXT ·castagnoliSSE42Triple(SB),NOSPLIT,$0
+	MOVL crcA+0(FP), AX
+	MOVL crcB+4(FP), CX
+	MOVL crcC+8(FP), DX
+
+	MOVQ a+16(FP), R8   // data pointer
+	MOVQ b+40(FP), R9   // data pointer
+	MOVQ c+64(FP), R10  // data pointer
+
+	MOVL rounds+88(FP), R11
+
+loop:
+	CRC32Q (R8), AX
+	CRC32Q (R9), CX
+	CRC32Q (R10), DX
+
+	CRC32Q 8(R8), AX
+	CRC32Q 8(R9), CX
+	CRC32Q 8(R10), DX
+
+	CRC32Q 16(R8), AX
+	CRC32Q 16(R9), CX
+	CRC32Q 16(R10), DX
+
+	ADDQ $24, R8
+	ADDQ $24, R9
+	ADDQ $24, R10
+
+	DECQ R11
+	JNZ loop
+
+	MOVL AX, retA+96(FP)
+	MOVL CX, retB+100(FP)
+	MOVL DX, retC+104(FP)
+	RET
+
 // func haveSSE42() bool
 TEXT ·haveSSE42(SB),NOSPLIT,$0
 	XORQ AX, AX
diff --git a/src/hash/crc32/crc32_amd64p32.go b/src/hash/crc32/crc32_amd64p32.go
index 1f6cd34..9d728fc 100644
--- a/src/hash/crc32/crc32_amd64p32.go
+++ b/src/hash/crc32/crc32_amd64p32.go
@@ -7,36 +7,35 @@ package crc32
 // This file contains the code to call the SSE 4.2 version of the Castagnoli
 // CRC.
 
-// haveSSE42 is defined in crc_amd64p32.s and uses CPUID to test for SSE 4.2
+// haveSSE42 is defined in crc32_amd64p32.s and uses CPUID to test for SSE 4.2
 // support.
 func haveSSE42() bool
 
-// castagnoliSSE42 is defined in crc_amd64.s and uses the SSE4.2 CRC32
+// castagnoliSSE42 is defined in crc32_amd64p32.s and uses the SSE4.2 CRC32
 // instruction.
 //go:noescape
 func castagnoliSSE42(crc uint32, p []byte) uint32
 
 var sse42 = haveSSE42()
 
-func updateCastagnoli(crc uint32, p []byte) uint32 {
-	if sse42 {
-		return castagnoliSSE42(crc, p)
-	}
-	// Use slicing-by-8 on larger inputs.
-	if len(p) >= sliceBy8Cutoff {
-		return updateSlicingBy8(crc, castagnoliTable8, p)
-	}
-	return update(crc, castagnoliTable, p)
+func archAvailableCastagnoli() bool {
+	return sse42
 }
 
-func updateIEEE(crc uint32, p []byte) uint32 {
-	// Use slicing-by-8 on larger inputs.
-	if len(p) >= sliceBy8Cutoff {
-		ieeeTable8Once.Do(func() {
-			ieeeTable8 = makeTable8(IEEE)
-		})
-		return updateSlicingBy8(crc, ieeeTable8, p)
+func archInitCastagnoli() {
+	if !sse42 {
+		panic("not available")
 	}
+	// No initialization necessary.
+}
 
-	return update(crc, IEEETable, p)
+func archUpdateCastagnoli(crc uint32, p []byte) uint32 {
+	if !sse42 {
+		panic("not available")
+	}
+	return castagnoliSSE42(crc, p)
 }
+
+func archAvailableIEEE() bool                    { return false }
+func archInitIEEE()                              { panic("not available") }
+func archUpdateIEEE(crc uint32, p []byte) uint32 { panic("not available") }
diff --git a/src/hash/crc32/crc32_generic.go b/src/hash/crc32/crc32_generic.go
index 10a6367..abacbb6 100644
--- a/src/hash/crc32/crc32_generic.go
+++ b/src/hash/crc32/crc32_generic.go
@@ -2,28 +2,88 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !amd64,!amd64p32,!s390x
+// This file contains CRC32 algorithms that are not specific to any architecture
+// and don't use hardware acceleration.
+//
+// The simple (and slow) CRC32 implementation only uses a 256*4 bytes table.
+//
+// The slicing-by-8 algorithm is a faster implementation that uses a bigger
+// table (8*256*4 bytes).
 
 package crc32
 
-// This file contains the generic version of updateCastagnoli which does
-// slicing-by-8, or uses the fallback for very small sizes.
+// simpleMakeTable allocates and constructs a Table for the specified
+// polynomial. The table is suitable for use with the simple algorithm
+// (simpleUpdate).
+func simpleMakeTable(poly uint32) *Table {
+	t := new(Table)
+	simplePopulateTable(poly, t)
+	return t
+}
+
+// simplePopulateTable constructs a Table for the specified polynomial, suitable
+// for use with simpleUpdate.
+func simplePopulateTable(poly uint32, t *Table) {
+	for i := 0; i < 256; i++ {
+		crc := uint32(i)
+		for j := 0; j < 8; j++ {
+			if crc&1 == 1 {
+				crc = (crc >> 1) ^ poly
+			} else {
+				crc >>= 1
+			}
+		}
+		t[i] = crc
+	}
+}
+
+// simpleUpdate uses the simple algorithm to update the CRC, given a table that
+// was previously computed using simpleMakeTable.
+func simpleUpdate(crc uint32, tab *Table, p []byte) uint32 {
+	crc = ^crc
+	for _, v := range p {
+		crc = tab[byte(crc)^v] ^ (crc >> 8)
+	}
+	return ^crc
+}
+
+// Use slicing-by-8 when payload >= this value.
+const slicing8Cutoff = 16
 
-func updateCastagnoli(crc uint32, p []byte) uint32 {
-	// Use slicing-by-8 on larger inputs.
-	if len(p) >= sliceBy8Cutoff {
-		return updateSlicingBy8(crc, castagnoliTable8, p)
+// slicing8Table is array of 8 Tables, used by the slicing-by-8 algorithm.
+type slicing8Table [8]Table
+
+// slicingMakeTable constructs a slicing8Table for the specified polynomial. The
+// table is suitable for use with the slicing-by-8 algorithm (slicingUpdate).
+func slicingMakeTable(poly uint32) *slicing8Table {
+	t := new(slicing8Table)
+	simplePopulateTable(poly, &t[0])
+	for i := 0; i < 256; i++ {
+		crc := t[0][i]
+		for j := 1; j < 8; j++ {
+			crc = t[0][crc&0xFF] ^ (crc >> 8)
+			t[j][i] = crc
+		}
 	}
-	return update(crc, castagnoliTable, p)
+	return t
 }
 
-func updateIEEE(crc uint32, p []byte) uint32 {
-	// Use slicing-by-8 on larger inputs.
-	if len(p) >= sliceBy8Cutoff {
-		ieeeTable8Once.Do(func() {
-			ieeeTable8 = makeTable8(IEEE)
-		})
-		return updateSlicingBy8(crc, ieeeTable8, p)
+// slicingUpdate uses the slicing-by-8 algorithm to update the CRC, given a
+// table that was previously computed using slicingMakeTable.
+func slicingUpdate(crc uint32, tab *slicing8Table, p []byte) uint32 {
+	if len(p) >= slicing8Cutoff {
+		crc = ^crc
+		for len(p) > 8 {
+			crc ^= uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24
+			crc = tab[0][p[7]] ^ tab[1][p[6]] ^ tab[2][p[5]] ^ tab[3][p[4]] ^
+				tab[4][crc>>24] ^ tab[5][(crc>>16)&0xFF] ^
+				tab[6][(crc>>8)&0xFF] ^ tab[7][crc&0xFF]
+			p = p[8:]
+		}
+		crc = ^crc
+	}
+	if len(p) == 0 {
+		return crc
 	}
-	return update(crc, IEEETable, p)
+	return simpleUpdate(crc, &tab[0], p)
 }
diff --git a/src/hash/crc32/crc32_otherarch.go b/src/hash/crc32/crc32_otherarch.go
new file mode 100644
index 0000000..cc96076
--- /dev/null
+++ b/src/hash/crc32/crc32_otherarch.go
@@ -0,0 +1,15 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64,!amd64p32,!s390x
+
+package crc32
+
+func archAvailableIEEE() bool                    { return false }
+func archInitIEEE()                              { panic("not available") }
+func archUpdateIEEE(crc uint32, p []byte) uint32 { panic("not available") }
+
+func archAvailableCastagnoli() bool                    { return false }
+func archInitCastagnoli()                              { panic("not available") }
+func archUpdateCastagnoli(crc uint32, p []byte) uint32 { panic("not available") }
diff --git a/src/hash/crc32/crc32_s390x.go b/src/hash/crc32/crc32_s390x.go
index befb58f..d13000d 100644
--- a/src/hash/crc32/crc32_s390x.go
+++ b/src/hash/crc32/crc32_s390x.go
@@ -25,58 +25,65 @@ func vectorizedCastagnoli(crc uint32, p []byte) uint32
 //go:noescape
 func vectorizedIEEE(crc uint32, p []byte) uint32
 
-func genericCastagnoli(crc uint32, p []byte) uint32 {
-	// Use slicing-by-8 on larger inputs.
-	if len(p) >= sliceBy8Cutoff {
-		return updateSlicingBy8(crc, castagnoliTable8, p)
-	}
-	return update(crc, castagnoliTable, p)
+func archAvailableCastagnoli() bool {
+	return hasVX
 }
 
-func genericIEEE(crc uint32, p []byte) uint32 {
-	// Use slicing-by-8 on larger inputs.
-	if len(p) >= sliceBy8Cutoff {
-		ieeeTable8Once.Do(func() {
-			ieeeTable8 = makeTable8(IEEE)
-		})
-		return updateSlicingBy8(crc, ieeeTable8, p)
+var archCastagnoliTable8 *slicing8Table
+
+func archInitCastagnoli() {
+	if !hasVX {
+		panic("not available")
 	}
-	return update(crc, IEEETable, p)
+	// We still use slicing-by-8 for small buffers.
+	archCastagnoliTable8 = slicingMakeTable(Castagnoli)
 }
 
-// updateCastagnoli calculates the checksum of p using
-// vectorizedCastagnoli if possible and falling back onto
-// genericCastagnoli as needed.
-func updateCastagnoli(crc uint32, p []byte) uint32 {
-	// Use vectorized function if vector facility is available and
-	// data length is above threshold.
-	if hasVX && len(p) >= vxMinLen {
+// archUpdateCastagnoli calculates the checksum of p using
+// vectorizedCastagnoli.
+func archUpdateCastagnoli(crc uint32, p []byte) uint32 {
+	if !hasVX {
+		panic("not available")
+	}
+	// Use vectorized function if data length is above threshold.
+	if len(p) >= vxMinLen {
 		aligned := len(p) & ^vxAlignMask
 		crc = vectorizedCastagnoli(crc, p[:aligned])
 		p = p[aligned:]
-		// process remaining data
-		if len(p) > 0 {
-			crc = genericCastagnoli(crc, p)
-		}
+	}
+	if len(p) == 0 {
 		return crc
 	}
-	return genericCastagnoli(crc, p)
+	return slicingUpdate(crc, archCastagnoliTable8, p)
+}
+
+func archAvailableIEEE() bool {
+	return hasVX
+}
+
+var archIeeeTable8 *slicing8Table
+
+func archInitIEEE() {
+	if !hasVX {
+		panic("not available")
+	}
+	// We still use slicing-by-8 for small buffers.
+	archIeeeTable8 = slicingMakeTable(IEEE)
 }
 
-// updateIEEE calculates the checksum of p using vectorizedIEEE if
-// possible and falling back onto genericIEEE as needed.
-func updateIEEE(crc uint32, p []byte) uint32 {
-	// Use vectorized function if vector facility is available and
-	// data length is above threshold.
-	if hasVX && len(p) >= vxMinLen {
+// archUpdateIEEE calculates the checksum of p using vectorizedIEEE.
+func archUpdateIEEE(crc uint32, p []byte) uint32 {
+	if !hasVX {
+		panic("not available")
+	}
+	// Use vectorized function if data length is above threshold.
+	if len(p) >= vxMinLen {
 		aligned := len(p) & ^vxAlignMask
 		crc = vectorizedIEEE(crc, p[:aligned])
 		p = p[aligned:]
-		// process remaining data
-		if len(p) > 0 {
-			crc = genericIEEE(crc, p)
-		}
+	}
+	if len(p) == 0 {
 		return crc
 	}
-	return genericIEEE(crc, p)
+	return slicingUpdate(crc, archIeeeTable8, p)
 }
diff --git a/src/hash/crc32/crc32_test.go b/src/hash/crc32/crc32_test.go
index e2b3557..1356734 100644
--- a/src/hash/crc32/crc32_test.go
+++ b/src/hash/crc32/crc32_test.go
@@ -6,7 +6,7 @@ package crc32
 
 import (
 	"hash"
-	"io"
+	"math/rand"
 	"testing"
 )
 
@@ -49,74 +49,221 @@ var golden = []test{
 	{0x8e0bb443, 0xdcded527, "How can you write a big system without C++?  -Paul Glick"},
 }
 
-func TestGolden(t *testing.T) {
-	castagnoliTab := MakeTable(Castagnoli)
+// testGoldenIEEE verifies that the given function returns
+// correct IEEE checksums.
+func testGoldenIEEE(t *testing.T, crcFunc func(b []byte) uint32) {
+	for _, g := range golden {
+		if crc := crcFunc([]byte(g.in)); crc != g.ieee {
+			t.Errorf("IEEE(%s) = 0x%x want 0x%x", g.in, crc, g.ieee)
+		}
+	}
+}
 
+// testGoldenCastagnoli verifies that the given function returns
+// correct IEEE checksums.
+func testGoldenCastagnoli(t *testing.T, crcFunc func(b []byte) uint32) {
 	for _, g := range golden {
-		ieee := NewIEEE()
-		io.WriteString(ieee, g.in)
-		s := ieee.Sum32()
-		if s != g.ieee {
-			t.Errorf("IEEE(%s) = 0x%x want 0x%x", g.in, s, g.ieee)
+		if crc := crcFunc([]byte(g.in)); crc != g.castagnoli {
+			t.Errorf("Castagnoli(%s) = 0x%x want 0x%x", g.in, crc, g.castagnoli)
 		}
+	}
+}
 
-		castagnoli := New(castagnoliTab)
-		io.WriteString(castagnoli, g.in)
-		s = castagnoli.Sum32()
-		if s != g.castagnoli {
-			t.Errorf("Castagnoli(%s) = 0x%x want 0x%x", g.in, s, g.castagnoli)
+// testCrossCheck generates random buffers of various lengths and verifies that
+// the two "update" functions return the same result.
+func testCrossCheck(t *testing.T, crcFunc1, crcFunc2 func(crc uint32, b []byte) uint32) {
+	// The AMD64 implementation has some cutoffs at lengths 168*3=504 and
+	// 1344*3=4032. We should make sure lengths around these values are in the
+	// list.
+	lengths := []int{0, 1, 2, 3, 4, 5, 10, 16, 50, 100, 128,
+		500, 501, 502, 503, 504, 505, 512, 1000, 1024, 2000,
+		4030, 4031, 4032, 4033, 4036, 4040, 4048, 4096, 5000, 10000}
+	for _, length := range lengths {
+		p := make([]byte, length)
+		_, _ = rand.Read(p)
+		crcInit := uint32(rand.Int63())
+		crc1 := crcFunc1(crcInit, p)
+		crc2 := crcFunc2(crcInit, p)
+		if crc1 != crc2 {
+			t.Errorf("mismatch: 0x%x vs 0x%x (buffer length %d)", crc1, crc2, length)
 		}
+	}
+}
 
-		if len(g.in) > 0 {
-			// The SSE4.2 implementation of this has code to deal
-			// with misaligned data so we ensure that we test that
-			// too.
-			castagnoli = New(castagnoliTab)
-			io.WriteString(castagnoli, g.in[:1])
-			io.WriteString(castagnoli, g.in[1:])
-			s = castagnoli.Sum32()
-			if s != g.castagnoli {
-				t.Errorf("Castagnoli[misaligned](%s) = 0x%x want 0x%x", g.in, s, g.castagnoli)
-			}
+// TestSimple tests the simple generic algorithm.
+func TestSimple(t *testing.T) {
+	tab := simpleMakeTable(IEEE)
+	testGoldenIEEE(t, func(b []byte) uint32 {
+		return simpleUpdate(0, tab, b)
+	})
+
+	tab = simpleMakeTable(Castagnoli)
+	testGoldenCastagnoli(t, func(b []byte) uint32 {
+		return simpleUpdate(0, tab, b)
+	})
+}
+
+// TestSimple tests the slicing-by-8 algorithm.
+func TestSlicing(t *testing.T) {
+	tab := slicingMakeTable(IEEE)
+	testGoldenIEEE(t, func(b []byte) uint32 {
+		return slicingUpdate(0, tab, b)
+	})
+
+	tab = slicingMakeTable(Castagnoli)
+	testGoldenCastagnoli(t, func(b []byte) uint32 {
+		return slicingUpdate(0, tab, b)
+	})
+
+	// Cross-check various polys against the simple algorithm.
+	for _, poly := range []uint32{IEEE, Castagnoli, Koopman, 0xD5828281} {
+		t1 := simpleMakeTable(poly)
+		f1 := func(crc uint32, b []byte) uint32 {
+			return simpleUpdate(crc, t1, b)
+		}
+		t2 := slicingMakeTable(poly)
+		f2 := func(crc uint32, b []byte) uint32 {
+			return slicingUpdate(crc, t2, b)
 		}
+		testCrossCheck(t, f1, f2)
+	}
+}
+
+func TestArchIEEE(t *testing.T) {
+	if !archAvailableIEEE() {
+		t.Skip("Arch-specific IEEE not available.")
+	}
+	archInitIEEE()
+	slicingTable := slicingMakeTable(IEEE)
+	testCrossCheck(t, archUpdateIEEE, func(crc uint32, b []byte) uint32 {
+		return slicingUpdate(crc, slicingTable, b)
+	})
+}
+
+func TestArchCastagnoli(t *testing.T) {
+	if !archAvailableCastagnoli() {
+		t.Skip("Arch-specific Castagnoli not available.")
+	}
+	archInitCastagnoli()
+	slicingTable := slicingMakeTable(Castagnoli)
+	testCrossCheck(t, archUpdateCastagnoli, func(crc uint32, b []byte) uint32 {
+		return slicingUpdate(crc, slicingTable, b)
+	})
+}
+
+func TestGolden(t *testing.T) {
+	testGoldenIEEE(t, ChecksumIEEE)
+
+	// Some implementations have special code to deal with misaligned
+	// data; test that as well.
+	for delta := 1; delta <= 7; delta++ {
+		testGoldenIEEE(t, func(b []byte) uint32 {
+			ieee := NewIEEE()
+			d := delta
+			if d >= len(b) {
+				d = len(b)
+			}
+			ieee.Write(b[:d])
+			ieee.Write(b[d:])
+			return ieee.Sum32()
+		})
+	}
+
+	castagnoliTab := MakeTable(Castagnoli)
+	if castagnoliTab == nil {
+		t.Errorf("nil Castagnoli Table")
+	}
+
+	testGoldenCastagnoli(t, func(b []byte) uint32 {
+		castagnoli := New(castagnoliTab)
+		castagnoli.Write(b)
+		return castagnoli.Sum32()
+	})
+
+	// Some implementations have special code to deal with misaligned
+	// data; test that as well.
+	for delta := 1; delta <= 7; delta++ {
+		testGoldenCastagnoli(t, func(b []byte) uint32 {
+			castagnoli := New(castagnoliTab)
+			d := delta
+			if d >= len(b) {
+				d = len(b)
+			}
+			castagnoli.Write(b[:d])
+			castagnoli.Write(b[d:])
+			return castagnoli.Sum32()
+		})
 	}
 }
 
 func BenchmarkIEEECrc40B(b *testing.B) {
-	benchmark(b, NewIEEE(), 40)
+	benchmark(b, NewIEEE(), 40, 0)
 }
 
 func BenchmarkIEEECrc1KB(b *testing.B) {
-	benchmark(b, NewIEEE(), 1<<10)
+	benchmark(b, NewIEEE(), 1<<10, 0)
 }
 
 func BenchmarkIEEECrc4KB(b *testing.B) {
-	benchmark(b, NewIEEE(), 4<<10)
+	benchmark(b, NewIEEE(), 4<<10, 0)
 }
 
 func BenchmarkIEEECrc32KB(b *testing.B) {
-	benchmark(b, NewIEEE(), 32<<10)
+	benchmark(b, NewIEEE(), 32<<10, 0)
+}
+
+func BenchmarkCastagnoliCrc15B(b *testing.B) {
+	benchmark(b, New(MakeTable(Castagnoli)), 15, 0)
+}
+
+func BenchmarkCastagnoliCrc15BMisaligned(b *testing.B) {
+	benchmark(b, New(MakeTable(Castagnoli)), 15, 1)
 }
 
 func BenchmarkCastagnoliCrc40B(b *testing.B) {
-	benchmark(b, New(MakeTable(Castagnoli)), 40)
+	benchmark(b, New(MakeTable(Castagnoli)), 40, 0)
+}
+
+func BenchmarkCastagnoliCrc40BMisaligned(b *testing.B) {
+	benchmark(b, New(MakeTable(Castagnoli)), 40, 1)
+}
+
+func BenchmarkCastagnoliCrc512(b *testing.B) {
+	benchmark(b, New(MakeTable(Castagnoli)), 512, 0)
+}
+
+func BenchmarkCastagnoliCrc512Misaligned(b *testing.B) {
+	benchmark(b, New(MakeTable(Castagnoli)), 512, 1)
 }
 
 func BenchmarkCastagnoliCrc1KB(b *testing.B) {
-	benchmark(b, New(MakeTable(Castagnoli)), 1<<10)
+	benchmark(b, New(MakeTable(Castagnoli)), 1<<10, 0)
+}
+
+func BenchmarkCastagnoliCrc1KBMisaligned(b *testing.B) {
+	benchmark(b, New(MakeTable(Castagnoli)), 1<<10, 1)
 }
 
 func BenchmarkCastagnoliCrc4KB(b *testing.B) {
-	benchmark(b, New(MakeTable(Castagnoli)), 4<<10)
+	benchmark(b, New(MakeTable(Castagnoli)), 4<<10, 0)
+}
+
+func BenchmarkCastagnoliCrc4KBMisaligned(b *testing.B) {
+	benchmark(b, New(MakeTable(Castagnoli)), 4<<10, 1)
 }
 
 func BenchmarkCastagnoliCrc32KB(b *testing.B) {
-	benchmark(b, New(MakeTable(Castagnoli)), 32<<10)
+	benchmark(b, New(MakeTable(Castagnoli)), 32<<10, 0)
+}
+
+func BenchmarkCastagnoliCrc32KBMisaligned(b *testing.B) {
+	benchmark(b, New(MakeTable(Castagnoli)), 32<<10, 1)
 }
 
-func benchmark(b *testing.B, h hash.Hash32, n int64) {
+func benchmark(b *testing.B, h hash.Hash32, n, alignment int64) {
 	b.SetBytes(n)
-	data := make([]byte, n)
+	data := make([]byte, n+alignment)
+	data = data[alignment:]
 	for i := range data {
 		data[i] = byte(i)
 	}
diff --git a/src/html/template/clone_test.go b/src/html/template/clone_test.go
index d7c62fa..b500715 100644
--- a/src/html/template/clone_test.go
+++ b/src/html/template/clone_test.go
@@ -7,7 +7,9 @@ package template
 import (
 	"bytes"
 	"errors"
+	"fmt"
 	"io/ioutil"
+	"sync"
 	"testing"
 	"text/template/parse"
 )
@@ -194,3 +196,69 @@ func TestFuncMapWorksAfterClone(t *testing.T) {
 		t.Errorf("clone error message mismatch want %q got %q", wantErr, gotErr)
 	}
 }
+
+// https://golang.org/issue/16101
+func TestTemplateCloneExecuteRace(t *testing.T) {
+	const (
+		input   = `<title>{{block "a" .}}a{{end}}</title><body>{{block "b" .}}b{{end}}<body>`
+		overlay = `{{define "b"}}A{{end}}`
+	)
+	outer := Must(New("outer").Parse(input))
+	tmpl := Must(Must(outer.Clone()).Parse(overlay))
+
+	var wg sync.WaitGroup
+	for i := 0; i < 10; i++ {
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			for i := 0; i < 100; i++ {
+				if err := tmpl.Execute(ioutil.Discard, "data"); err != nil {
+					panic(err)
+				}
+			}
+		}()
+	}
+	wg.Wait()
+}
+
+func TestTemplateCloneLookup(t *testing.T) {
+	// Template.escape makes an assumption that the template associated
+	// with t.Name() is t. Check that this holds.
+	tmpl := Must(New("x").Parse("a"))
+	tmpl = Must(tmpl.Clone())
+	if tmpl.Lookup(tmpl.Name()) != tmpl {
+		t.Error("after Clone, tmpl.Lookup(tmpl.Name()) != tmpl")
+	}
+}
+
+func TestCloneGrowth(t *testing.T) {
+	tmpl := Must(New("root").Parse(`<title>{{block "B". }}Arg{{end}}</title>`))
+	tmpl = Must(tmpl.Clone())
+	Must(tmpl.Parse(`{{define "B"}}Text{{end}}`))
+	for i := 0; i < 10; i++ {
+		tmpl.Execute(ioutil.Discard, nil)
+	}
+	if len(tmpl.DefinedTemplates()) > 200 {
+		t.Fatalf("too many templates: %v", len(tmpl.DefinedTemplates()))
+	}
+}
+
+// https://golang.org/issue/17735
+func TestCloneRedefinedName(t *testing.T) {
+	const base = `
+{{ define "a" -}}<title>{{ template "b" . -}}</title>{{ end -}}
+{{ define "b" }}{{ end -}}
+`
+	const page = `{{ template "a" . }}`
+
+	t1 := Must(New("a").Parse(base))
+
+	for i := 0; i < 2; i++ {
+		t2 := Must(t1.Clone())
+		t2 = Must(t2.New(fmt.Sprintf("%d", i)).Parse(page))
+		err := t2.Execute(ioutil.Discard, nil)
+		if err != nil {
+			t.Fatal(err)
+		}
+	}
+}
diff --git a/src/html/template/content_test.go b/src/html/template/content_test.go
index e698328..0b4365c 100644
--- a/src/html/template/content_test.go
+++ b/src/html/template/content_test.go
@@ -162,6 +162,47 @@ func TestTypedContent(t *testing.T) {
 			},
 		},
 		{
+			`<script type="text/javascript">alert("{{.}}")</script>`,
+			[]string{
+				`\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
+				`a[href =~ \x22\/\/example.com\x22]#foo`,
+				`Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`,
+				` dir=\x22ltr\x22`,
+				`c \x26\x26 alert(\x22Hello, World!\x22);`,
+				// Escape sequence not over-escaped.
+				`Hello, World \x26 O\x27Reilly\x21`,
+				`greeting=H%69\x26addressee=(World)`,
+			},
+		},
+		{
+			`<script type="text/javascript">alert({{.}})</script>`,
+			[]string{
+				`"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
+				`"a[href =~ \"//example.com\"]#foo"`,
+				`"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
+				`" dir=\"ltr\""`,
+				// Not escaped.
+				`c && alert("Hello, World!");`,
+				// Escape sequence not over-escaped.
+				`"Hello, World & O'Reilly\x21"`,
+				`"greeting=H%69\u0026addressee=(World)"`,
+			},
+		},
+		{
+			// Not treated as JS. The output is same as for <div>{{.}}</div>
+			`<script type="text/template">{{.}}</script>`,
+			[]string{
+				`<b> "foo%" O'Reilly &bar;`,
+				`a[href =~ "//example.com"]#foo`,
+				// Not escaped.
+				`Hello, <b>World</b> &tc!`,
+				` dir="ltr"`,
+				`c && alert("Hello, World!");`,
+				`Hello, World & O'Reilly\x21`,
+				`greeting=H%69&addressee=(World)`,
+			},
+		},
+		{
 			`<button onclick='alert("{{.}}")'>`,
 			[]string{
 				`\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
diff --git a/src/html/template/context.go b/src/html/template/context.go
index c90fc1f..37a3faf 100644
--- a/src/html/template/context.go
+++ b/src/html/template/context.go
@@ -285,7 +285,8 @@ type element uint8
 const (
 	// elementNone occurs outside a special tag or special element body.
 	elementNone element = iota
-	// elementScript corresponds to the raw text <script> element.
+	// elementScript corresponds to the raw text <script> element
+	// with JS MIME type or no type attribute.
 	elementScript
 	// elementStyle corresponds to the raw text <style> element.
 	elementStyle
@@ -319,6 +320,8 @@ const (
 	attrNone attr = iota
 	// attrScript corresponds to an event handler attribute.
 	attrScript
+	// attrScriptType corresponds to the type attribute in script HTML element
+	attrScriptType
 	// attrStyle corresponds to the style attribute whose value is CSS.
 	attrStyle
 	// attrURL corresponds to an attribute whose value is a URL.
@@ -326,10 +329,11 @@ const (
 )
 
 var attrNames = [...]string{
-	attrNone:   "attrNone",
-	attrScript: "attrScript",
-	attrStyle:  "attrStyle",
-	attrURL:    "attrURL",
+	attrNone:       "attrNone",
+	attrScript:     "attrScript",
+	attrScriptType: "attrScriptType",
+	attrStyle:      "attrStyle",
+	attrURL:        "attrURL",
 }
 
 func (a attr) String() string {
diff --git a/src/html/template/doc.go b/src/html/template/doc.go
index e1e9cad..cb89812 100644
--- a/src/html/template/doc.go
+++ b/src/html/template/doc.go
@@ -129,7 +129,7 @@ then the template output is
 
   <script>var pair = {"A": "foo", "B": "bar"};</script>
 
-See package json to understand how non-string content is marshalled for
+See package json to understand how non-string content is marshaled for
 embedding in JavaScript contexts.
 
 
diff --git a/src/html/template/error.go b/src/html/template/error.go
index 5637384..cbcaf92 100644
--- a/src/html/template/error.go
+++ b/src/html/template/error.go
@@ -44,7 +44,7 @@ const (
 	// OK indicates the lack of an error.
 	OK ErrorCode = iota
 
-	// ErrAmbigContext: "... appears in an ambiguous URL context"
+	// ErrAmbigContext: "... appears in an ambiguous context within a URL"
 	// Example:
 	//   <a href="
 	//      {{if .C}}
diff --git a/src/html/template/escape.go b/src/html/template/escape.go
index 8f2fe46..0e7d2be 100644
--- a/src/html/template/escape.go
+++ b/src/html/template/escape.go
@@ -161,7 +161,7 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
 		case urlPartUnknown:
 			return context{
 				state: stateError,
-				err:   errorf(ErrAmbigContext, n, n.Line, "%s appears in an ambiguous URL context", n),
+				err:   errorf(ErrAmbigContext, n, n.Line, "%s appears in an ambiguous context within a URL", n),
 			}
 		default:
 			panic(c.urlPart.String())
@@ -673,6 +673,8 @@ func contextAfterText(c context, s []byte) (context, int) {
 		return transitionFunc[c.state](c, s[:i])
 	}
 
+	// We are at the beginning of an attribute value.
+
 	i := bytes.IndexAny(s, delimEnds[c.delim])
 	if i == -1 {
 		i = len(s)
@@ -703,13 +705,21 @@ func contextAfterText(c context, s []byte) (context, int) {
 		}
 		return c, len(s)
 	}
+
+	element := c.element
+
+	// If this is a non-JS "type" attribute inside "script" tag, do not treat the contents as JS.
+	if c.state == stateAttr && c.element == elementScript && c.attr == attrScriptType && !isJSType(string(s[:i])) {
+		element = elementNone
+	}
+
 	if c.delim != delimSpaceOrTagEnd {
 		// Consume any quote.
 		i++
 	}
 	// On exiting an attribute, we discard all state information
 	// except the state and element.
-	return context{state: stateTag, element: c.element}, i
+	return context{state: stateTag, element: element}, i
 }
 
 // editActionNode records a change to an action pipeline for later commit.
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
index 023ee57..f6ace49 100644
--- a/src/html/template/escape_test.go
+++ b/src/html/template/escape_test.go
@@ -903,7 +903,7 @@ func TestErrors(t *testing.T) {
 		},
 		{
 			`<a href="{{if .F}}/foo?a={{else}}/bar/{{end}}{{.H}}">`,
-			"z:1:47: {{.H}} appears in an ambiguous URL context",
+			"z:1:47: {{.H}} appears in an ambiguous context within a URL",
 		},
 		{
 			`<a onclick="alert('Hello \`,
@@ -1365,6 +1365,10 @@ func TestEscapeText(t *testing.T) {
 			context{state: stateTag, element: elementScript},
 		},
 		{
+			`<script>`,
+			context{state: stateJS, jsCtx: jsCtxRegexp, element: elementScript},
+		},
+		{
 			`<script>foo`,
 			context{state: stateJS, jsCtx: jsCtxDivOp, element: elementScript},
 		},
@@ -1389,6 +1393,14 @@ func TestEscapeText(t *testing.T) {
 			context{state: stateText},
 		},
 		{
+			`<script type="text/template">`,
+			context{state: stateText},
+		},
+		{
+			`<script type="notjs">`,
+			context{state: stateText},
+		},
+		{
 			`<Script>`,
 			context{state: stateJS, element: elementScript},
 		},
diff --git a/src/html/template/js.go b/src/html/template/js.go
index f6d166b..8f1185c 100644
--- a/src/html/template/js.go
+++ b/src/html/template/js.go
@@ -162,14 +162,14 @@ func jsValEscaper(args ...interface{}) string {
 		// a division operator it is not turned into a line comment:
 		//     x/{{y}}
 		// turning into
-		//     x//* error marshalling y:
+		//     x//* error marshaling y:
 		//          second line of error message */null
 		return fmt.Sprintf(" /* %s */null ", strings.Replace(err.Error(), "*/", "* /", -1))
 	}
 
 	// TODO: maybe post-process output to prevent it from containing
 	// "<!--", "-->", "<![CDATA[", "]]>", or "</script"
-	// in case custom marshallers produce output containing those.
+	// in case custom marshalers produce output containing those.
 
 	// TODO: Maybe abbreviate \u00ab to \xab to produce more compact output.
 	if len(b) == 0 {
@@ -362,3 +362,41 @@ func isJSIdentPart(r rune) bool {
 	}
 	return false
 }
+
+// isJSType returns true if the given MIME type should be considered JavaScript.
+//
+// It is used to determine whether a script tag with a type attribute is a javascript container.
+func isJSType(mimeType string) bool {
+	// per
+	//   http://www.w3.org/TR/html5/scripting-1.html#attr-script-type
+	//   https://tools.ietf.org/html/rfc7231#section-3.1.1
+	//   http://tools.ietf.org/html/rfc4329#section-3
+
+	// discard parameters
+	if i := strings.Index(mimeType, ";"); i >= 0 {
+		mimeType = mimeType[:i]
+	}
+	mimeType = strings.TrimSpace(mimeType)
+	switch mimeType {
+	case
+		"application/ecmascript",
+		"application/javascript",
+		"application/x-ecmascript",
+		"application/x-javascript",
+		"text/ecmascript",
+		"text/javascript",
+		"text/javascript1.0",
+		"text/javascript1.1",
+		"text/javascript1.2",
+		"text/javascript1.3",
+		"text/javascript1.4",
+		"text/javascript1.5",
+		"text/jscript",
+		"text/livescript",
+		"text/x-ecmascript",
+		"text/x-javascript":
+		return true
+	default:
+		return false
+	}
+}
diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go
index 7af7997..58fc37a 100644
--- a/src/html/template/js_test.go
+++ b/src/html/template/js_test.go
@@ -332,6 +332,24 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
 	}
 }
 
+func TestIsJsMimeType(t *testing.T) {
+	tests := []struct {
+		in  string
+		out bool
+	}{
+		{"application/javascript;version=1.8", true},
+		{"application/javascript;version=1.8;foo=bar", true},
+		{"application/javascript/version=1.8", false},
+		{"text/javascript", true},
+	}
+
+	for _, test := range tests {
+		if isJSType(test.in) != test.out {
+			t.Errorf("isJSType(%q) = %v, want %v", test.in, !test.out, test.out)
+		}
+	}
+}
+
 func BenchmarkJSValEscaperWithNum(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		jsValEscaper(3.141592654)
diff --git a/src/html/template/template.go b/src/html/template/template.go
index 063e46d..b313a6b 100644
--- a/src/html/template/template.go
+++ b/src/html/template/template.go
@@ -33,8 +33,9 @@ var escapeOK = fmt.Errorf("template escaped correctly")
 
 // nameSpace is the data structure shared by all templates in an association.
 type nameSpace struct {
-	mu  sync.Mutex
-	set map[string]*Template
+	mu      sync.Mutex
+	set     map[string]*Template
+	escaped bool
 }
 
 // Templates returns a slice of the templates associated with t, including t
@@ -74,13 +75,28 @@ func (t *Template) Option(opt ...string) *Template {
 	return t
 }
 
+// checkCanParse checks whether it is OK to parse templates.
+// If not, it returns an error.
+func (t *Template) checkCanParse() error {
+	if t == nil {
+		return nil
+	}
+	t.nameSpace.mu.Lock()
+	defer t.nameSpace.mu.Unlock()
+	if t.nameSpace.escaped {
+		return fmt.Errorf("html/template: cannot Parse after Execute")
+	}
+	return nil
+}
+
 // escape escapes all associated templates.
 func (t *Template) escape() error {
 	t.nameSpace.mu.Lock()
 	defer t.nameSpace.mu.Unlock()
+	t.nameSpace.escaped = true
 	if t.escapeErr == nil {
 		if t.Tree == nil {
-			return fmt.Errorf("template: %q is an incomplete or empty template%s", t.Name(), t.DefinedTemplates())
+			return fmt.Errorf("template: %q is an incomplete or empty template", t.Name())
 		}
 		if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil {
 			return err
@@ -124,6 +140,7 @@ func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{})
 func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
 	t.nameSpace.mu.Lock()
 	defer t.nameSpace.mu.Unlock()
+	t.nameSpace.escaped = true
 	tmpl = t.set[name]
 	if tmpl == nil {
 		return nil, fmt.Errorf("html/template: %q is undefined", name)
@@ -150,22 +167,27 @@ func (t *Template) DefinedTemplates() string {
 	return t.text.DefinedTemplates()
 }
 
-// Parse parses a string into a template. Nested template definitions
-// will be associated with the top-level template t. Parse may be
-// called multiple times to parse definitions of templates to associate
-// with t. It is an error if a resulting template is non-empty (contains
-// content other than template definitions) and would replace a
-// non-empty template with the same name.  (In multiple calls to Parse
-// with the same receiver template, only one call can contain text
-// other than space, comments, and template definitions.)
-func (t *Template) Parse(src string) (*Template, error) {
-	t.nameSpace.mu.Lock()
-	t.escapeErr = nil
-	t.nameSpace.mu.Unlock()
-	ret, err := t.text.Parse(src)
+// Parse parses text as a template body for t.
+// Named template definitions ({{define ...}} or {{block ...}} statements) in text
+// define additional templates associated with t and are removed from the
+// definition of t itself.
+//
+// Templates can be redefined in successive calls to Parse,
+// before the first use of Execute on t or any associated template.
+// A template definition with a body containing only white space and comments
+// is considered empty and will not replace an existing template's body.
+// This allows using Parse to add new named template definitions without
+// overwriting the main template body.
+func (t *Template) Parse(text string) (*Template, error) {
+	if err := t.checkCanParse(); err != nil {
+		return nil, err
+	}
+
+	ret, err := t.text.Parse(text)
 	if err != nil {
 		return nil, err
 	}
+
 	// In general, all the named templates might have changed underfoot.
 	// Regardless, some new ones may have been defined.
 	// The template.Template set has been updated; update ours.
@@ -176,11 +198,7 @@ func (t *Template) Parse(src string) (*Template, error) {
 		tmpl := t.set[name]
 		if tmpl == nil {
 			tmpl = t.new(name)
-		} else if tmpl.escapeErr != nil {
-			return nil, fmt.Errorf("html/template: cannot redefine %q after it has executed", name)
 		}
-		// Restore our record of this text/template to its unescaped original state.
-		tmpl.escapeErr = nil
 		tmpl.text = v
 		tmpl.Tree = v.Tree
 	}
@@ -190,13 +208,14 @@ func (t *Template) Parse(src string) (*Template, error) {
 // AddParseTree creates a new template with the name and parse tree
 // and associates it with t.
 //
-// It returns an error if t has already been executed.
+// It returns an error if t or any associated template has already been executed.
 func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
+	if err := t.checkCanParse(); err != nil {
+		return nil, err
+	}
+
 	t.nameSpace.mu.Lock()
 	defer t.nameSpace.mu.Unlock()
-	if t.escapeErr != nil {
-		return nil, fmt.Errorf("html/template: cannot AddParseTree to %q after it has executed", t.Name())
-	}
 	text, err := t.text.AddParseTree(name, tree)
 	if err != nil {
 		return nil, err
@@ -252,7 +271,8 @@ func (t *Template) Clone() (*Template, error) {
 			ret.nameSpace,
 		}
 	}
-	return ret, nil
+	// Return the template associated with the name of this template.
+	return ret.set[ret.Name()], nil
 }
 
 // New allocates a new HTML template with the given name.
@@ -361,6 +381,8 @@ func ParseFiles(filenames ...string) (*Template, error) {
 //
 // When parsing multiple files with the same name in different directories,
 // the last one mentioned will be the one that results.
+//
+// ParseFiles returns an error if t or any associated template has already been executed.
 func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
 	return parseFiles(t, filenames...)
 }
@@ -368,6 +390,10 @@ func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
 // parseFiles is the helper for the method and function. If the argument
 // template is nil, it is created from the first file.
 func parseFiles(t *Template, filenames ...string) (*Template, error) {
+	if err := t.checkCanParse(); err != nil {
+		return nil, err
+	}
+
 	if len(filenames) == 0 {
 		// Not really a problem, but be consistent.
 		return nil, fmt.Errorf("html/template: no files named in call to ParseFiles")
@@ -422,12 +448,17 @@ func ParseGlob(pattern string) (*Template, error) {
 //
 // When parsing multiple files with the same name in different directories,
 // the last one mentioned will be the one that results.
+//
+// ParseGlob returns an error if t or any associated template has already been executed.
 func (t *Template) ParseGlob(pattern string) (*Template, error) {
 	return parseGlob(t, pattern)
 }
 
 // parseGlob is the implementation of the function and method ParseGlob.
 func parseGlob(t *Template, pattern string) (*Template, error) {
+	if err := t.checkCanParse(); err != nil {
+		return nil, err
+	}
 	filenames, err := filepath.Glob(pattern)
 	if err != nil {
 		return nil, err
diff --git a/src/html/template/template_test.go b/src/html/template/template_test.go
index 46df1f8..90c5a73 100644
--- a/src/html/template/template_test.go
+++ b/src/html/template/template_test.go
@@ -1,7 +1,13 @@
-package template
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package template_test
 
 import (
 	"bytes"
+	. "html/template"
+	"strings"
 	"testing"
 )
 
@@ -27,3 +33,125 @@ func TestTemplateClone(t *testing.T) {
 		t.Fatalf("got %q; want %q", got, want)
 	}
 }
+
+func TestRedefineNonEmptyAfterExecution(t *testing.T) {
+	c := newTestCase(t)
+	c.mustParse(c.root, `foo`)
+	c.mustExecute(c.root, nil, "foo")
+	c.mustNotParse(c.root, `bar`)
+}
+
+func TestRedefineEmptyAfterExecution(t *testing.T) {
+	c := newTestCase(t)
+	c.mustParse(c.root, ``)
+	c.mustExecute(c.root, nil, "")
+	c.mustNotParse(c.root, `foo`)
+	c.mustExecute(c.root, nil, "")
+}
+
+func TestRedefineAfterNonExecution(t *testing.T) {
+	c := newTestCase(t)
+	c.mustParse(c.root, `{{if .}}<{{template "X"}}>{{end}}{{define "X"}}foo{{end}}`)
+	c.mustExecute(c.root, 0, "")
+	c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
+	c.mustExecute(c.root, 1, "<foo>")
+}
+
+func TestRedefineAfterNamedExecution(t *testing.T) {
+	c := newTestCase(t)
+	c.mustParse(c.root, `<{{template "X" .}}>{{define "X"}}foo{{end}}`)
+	c.mustExecute(c.root, nil, "<foo>")
+	c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
+	c.mustExecute(c.root, nil, "<foo>")
+}
+
+func TestRedefineNestedByNameAfterExecution(t *testing.T) {
+	c := newTestCase(t)
+	c.mustParse(c.root, `{{define "X"}}foo{{end}}`)
+	c.mustExecute(c.lookup("X"), nil, "foo")
+	c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
+	c.mustExecute(c.lookup("X"), nil, "foo")
+}
+
+func TestRedefineNestedByTemplateAfterExecution(t *testing.T) {
+	c := newTestCase(t)
+	c.mustParse(c.root, `{{define "X"}}foo{{end}}`)
+	c.mustExecute(c.lookup("X"), nil, "foo")
+	c.mustNotParse(c.lookup("X"), `bar`)
+	c.mustExecute(c.lookup("X"), nil, "foo")
+}
+
+func TestRedefineSafety(t *testing.T) {
+	c := newTestCase(t)
+	c.mustParse(c.root, `<html><a href="{{template "X"}}">{{define "X"}}{{end}}`)
+	c.mustExecute(c.root, nil, `<html><a href="">`)
+	// Note: Every version of Go prior to Go 1.8 accepted the redefinition of "X"
+	// on the next line, but luckily kept it from being used in the outer template.
+	// Now we reject it, which makes clearer that we're not going to use it.
+	c.mustNotParse(c.root, `{{define "X"}}" bar="baz{{end}}`)
+	c.mustExecute(c.root, nil, `<html><a href="">`)
+}
+
+func TestRedefineTopUse(t *testing.T) {
+	c := newTestCase(t)
+	c.mustParse(c.root, `{{template "X"}}{{.}}{{define "X"}}{{end}}`)
+	c.mustExecute(c.root, 42, `42`)
+	c.mustNotParse(c.root, `{{define "X"}}<script>{{end}}`)
+	c.mustExecute(c.root, 42, `42`)
+}
+
+func TestRedefineOtherParsers(t *testing.T) {
+	c := newTestCase(t)
+	c.mustParse(c.root, ``)
+	c.mustExecute(c.root, nil, ``)
+	if _, err := c.root.ParseFiles("no.template"); err == nil || !strings.Contains(err.Error(), "Execute") {
+		t.Errorf("ParseFiles: %v\nwanted error about already having Executed", err)
+	}
+	if _, err := c.root.ParseGlob("*.no.template"); err == nil || !strings.Contains(err.Error(), "Execute") {
+		t.Errorf("ParseGlob: %v\nwanted error about already having Executed", err)
+	}
+	if _, err := c.root.AddParseTree("t1", c.root.Tree); err == nil || !strings.Contains(err.Error(), "Execute") {
+		t.Errorf("AddParseTree: %v\nwanted error about already having Executed", err)
+	}
+}
+
+type testCase struct {
+	t    *testing.T
+	root *Template
+}
+
+func newTestCase(t *testing.T) *testCase {
+	return &testCase{
+		t:    t,
+		root: New("root"),
+	}
+}
+
+func (c *testCase) lookup(name string) *Template {
+	return c.root.Lookup(name)
+}
+
+func (c *testCase) mustParse(t *Template, text string) {
+	_, err := t.Parse(text)
+	if err != nil {
+		c.t.Fatalf("parse: %v", err)
+	}
+}
+
+func (c *testCase) mustNotParse(t *Template, text string) {
+	_, err := t.Parse(text)
+	if err == nil {
+		c.t.Fatalf("parse: unexpected success")
+	}
+}
+
+func (c *testCase) mustExecute(t *Template, val interface{}, want string) {
+	var buf bytes.Buffer
+	err := t.Execute(&buf, val)
+	if err != nil {
+		c.t.Fatalf("execute: %v", err)
+	}
+	if buf.String() != want {
+		c.t.Fatalf("template output:\n%s\nwant:\n%s", buf.String(), want)
+	}
+}
diff --git a/src/html/template/transition.go b/src/html/template/transition.go
index aefe035..4a4716d 100644
--- a/src/html/template/transition.go
+++ b/src/html/template/transition.go
@@ -105,14 +105,21 @@ func tTag(c context, s []byte) (context, int) {
 			err:   errorf(ErrBadHTML, nil, 0, "expected space, attr name, or end of tag, but got %q", s[i:]),
 		}, len(s)
 	}
-	switch attrType(string(s[i:j])) {
-	case contentTypeURL:
-		attr = attrURL
-	case contentTypeCSS:
-		attr = attrStyle
-	case contentTypeJS:
-		attr = attrScript
+
+	attrName := string(s[i:j])
+	if c.element == elementScript && attrName == "type" {
+		attr = attrScriptType
+	} else {
+		switch attrType(attrName) {
+		case contentTypeURL:
+			attr = attrURL
+		case contentTypeCSS:
+			attr = attrStyle
+		case contentTypeJS:
+			attr = attrScript
+		}
 	}
+
 	if j == len(s) {
 		state = stateAttrName
 	} else {
@@ -149,10 +156,11 @@ func tAfterName(c context, s []byte) (context, int) {
 }
 
 var attrStartStates = [...]state{
-	attrNone:   stateAttr,
-	attrScript: stateJS,
-	attrStyle:  stateCSS,
-	attrURL:    stateURL,
+	attrNone:       stateAttr,
+	attrScript:     stateJS,
+	attrScriptType: stateAttr,
+	attrStyle:      stateCSS,
+	attrURL:        stateURL,
 }
 
 // tBeforeValue is the context transition function for stateBeforeValue.
diff --git a/src/html/template/url.go b/src/html/template/url.go
index 246bfd3..02123b2 100644
--- a/src/html/template/url.go
+++ b/src/html/template/url.go
@@ -32,7 +32,7 @@ func urlEscaper(args ...interface{}) string {
 	return urlProcessor(false, args...)
 }
 
-// urlEscaper normalizes URL content so it can be embedded in a quote-delimited
+// urlNormalizer normalizes URL content so it can be embedded in a quote-delimited
 // string or parenthesis delimited url(...).
 // The normalizer does not encode all HTML specials. Specifically, it does not
 // encode '&' so correct embedding in an HTML attribute requires escaping of
diff --git a/src/image/color/color.go b/src/image/color/color.go
index 1044339..0832c59 100644
--- a/src/image/color/color.go
+++ b/src/image/color/color.go
@@ -246,8 +246,18 @@ func grayModel(c Color) Color {
 		return c
 	}
 	r, g, b, _ := c.RGBA()
-	y := (299*r + 587*g + 114*b + 500) / 1000
-	return Gray{uint8(y >> 8)}
+
+	// These coefficients (the fractions 0.299, 0.587 and 0.114) are the same
+	// as those given by the JFIF specification and used by func RGBToYCbCr in
+	// ycbcr.go.
+	//
+	// Note that 19595 + 38470 + 7471 equals 65536.
+	//
+	// The 24 is 16 + 8. The 16 is the same as used in RGBToYCbCr. The 8 is
+	// because the return value is 8 bit color, not 16 bit color.
+	y := (19595*r + 38470*g + 7471*b + 1<<15) >> 24
+
+	return Gray{uint8(y)}
 }
 
 func gray16Model(c Color) Color {
@@ -255,7 +265,14 @@ func gray16Model(c Color) Color {
 		return c
 	}
 	r, g, b, _ := c.RGBA()
-	y := (299*r + 587*g + 114*b + 500) / 1000
+
+	// These coefficients (the fractions 0.299, 0.587 and 0.114) are the same
+	// as those given by the JFIF specification and used by func RGBToYCbCr in
+	// ycbcr.go.
+	//
+	// Note that 19595 + 38470 + 7471 equals 65536.
+	y := (19595*r + 38470*g + 7471*b + 1<<15) >> 16
+
 	return Gray16{uint16(y)}
 }
 
diff --git a/src/image/color/ycbcr.go b/src/image/color/ycbcr.go
index 3df5d36..18d1a56 100644
--- a/src/image/color/ycbcr.go
+++ b/src/image/color/ycbcr.go
@@ -17,6 +17,8 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
 	b1 := int32(b)
 
 	// yy is in range [0,0xff].
+	//
+	// Note that 19595 + 38470 + 7471 equals 65536.
 	yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16
 
 	// The bit twiddling below is equivalent to
@@ -32,6 +34,8 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
 	// Note that the uint8 type conversion in the return
 	// statement will convert ^int32(0) to 0xff.
 	// The code below to compute cr uses a similar pattern.
+	//
+	// Note that -11056 - 21712 + 32768 equals 0.
 	cb := -11056*r1 - 21712*g1 + 32768*b1 + 257<<15
 	if uint32(cb)&0xff000000 == 0 {
 		cb >>= 16
@@ -39,6 +43,7 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
 		cb = ^(cb >> 31)
 	}
 
+	// Note that 32768 - 27440 - 5328 equals 0.
 	cr := 32768*r1 - 27440*g1 - 5328*b1 + 257<<15
 	if uint32(cr)&0xff000000 == 0 {
 		cr >>= 16
@@ -134,24 +139,39 @@ func (c YCbCr) RGBA() (uint32, uint32, uint32, uint32) {
 	yy1 := int32(c.Y) * 0x10100 // Convert 0x12 to 0x121200.
 	cb1 := int32(c.Cb) - 128
 	cr1 := int32(c.Cr) - 128
-	r := (yy1 + 91881*cr1) >> 8
-	g := (yy1 - 22554*cb1 - 46802*cr1) >> 8
-	b := (yy1 + 116130*cb1) >> 8
-	if r < 0 {
-		r = 0
-	} else if r > 0xffff {
-		r = 0xffff
+
+	// The bit twiddling below is equivalent to
+	//
+	// r := (yy1 + 91881*cr1) >> 8
+	// if r < 0 {
+	//     r = 0
+	// } else if r > 0xff {
+	//     r = 0xffff
+	// }
+	//
+	// but uses fewer branches and is faster.
+	// The code below to compute g and b uses a similar pattern.
+	r := yy1 + 91881*cr1
+	if uint32(r)&0xff000000 == 0 {
+		r >>= 8
+	} else {
+		r = ^(r >> 31) & 0xffff
 	}
-	if g < 0 {
-		g = 0
-	} else if g > 0xffff {
-		g = 0xffff
+
+	g := yy1 - 22554*cb1 - 46802*cr1
+	if uint32(g)&0xff000000 == 0 {
+		g >>= 8
+	} else {
+		g = ^(g >> 31) & 0xffff
 	}
-	if b < 0 {
-		b = 0
-	} else if b > 0xffff {
-		b = 0xffff
+
+	b := yy1 + 116130*cb1
+	if uint32(b)&0xff000000 == 0 {
+		b >>= 8
+	} else {
+		b = ^(b >> 31) & 0xffff
 	}
+
 	return uint32(r), uint32(g), uint32(b), 0xffff
 }
 
@@ -179,23 +199,37 @@ func (c NYCbCrA) RGBA() (uint32, uint32, uint32, uint32) {
 	yy1 := int32(c.Y) * 0x10100 // Convert 0x12 to 0x121200.
 	cb1 := int32(c.Cb) - 128
 	cr1 := int32(c.Cr) - 128
-	r := (yy1 + 91881*cr1) >> 8
-	g := (yy1 - 22554*cb1 - 46802*cr1) >> 8
-	b := (yy1 + 116130*cb1) >> 8
-	if r < 0 {
-		r = 0
-	} else if r > 0xffff {
-		r = 0xffff
+
+	// The bit twiddling below is equivalent to
+	//
+	// r := (yy1 + 91881*cr1) >> 8
+	// if r < 0 {
+	//     r = 0
+	// } else if r > 0xff {
+	//     r = 0xffff
+	// }
+	//
+	// but uses fewer branches and is faster.
+	// The code below to compute g and b uses a similar pattern.
+	r := yy1 + 91881*cr1
+	if uint32(r)&0xff000000 == 0 {
+		r >>= 8
+	} else {
+		r = ^(r >> 31) & 0xffff
 	}
-	if g < 0 {
-		g = 0
-	} else if g > 0xffff {
-		g = 0xffff
+
+	g := yy1 - 22554*cb1 - 46802*cr1
+	if uint32(g)&0xff000000 == 0 {
+		g >>= 8
+	} else {
+		g = ^(g >> 31) & 0xffff
 	}
-	if b < 0 {
-		b = 0
-	} else if b > 0xffff {
-		b = 0xffff
+
+	b := yy1 + 116130*cb1
+	if uint32(b)&0xff000000 == 0 {
+		b >>= 8
+	} else {
+		b = ^(b >> 31) & 0xffff
 	}
 
 	// The second part of this method applies the alpha.
diff --git a/src/image/color/ycbcr_test.go b/src/image/color/ycbcr_test.go
index 561699f..85c1b98 100644
--- a/src/image/color/ycbcr_test.go
+++ b/src/image/color/ycbcr_test.go
@@ -172,7 +172,8 @@ func TestPalette(t *testing.T) {
 	}
 }
 
-var sink uint8
+var sink8 uint8
+var sink32 uint32
 
 func BenchmarkYCbCrToRGB(b *testing.B) {
 	// YCbCrToRGB does saturating arithmetic.
@@ -180,17 +181,17 @@ func BenchmarkYCbCrToRGB(b *testing.B) {
 	// different paths through the generated code.
 	b.Run("0", func(b *testing.B) {
 		for i := 0; i < b.N; i++ {
-			sink, sink, sink = YCbCrToRGB(0, 0, 0)
+			sink8, sink8, sink8 = YCbCrToRGB(0, 0, 0)
 		}
 	})
 	b.Run("128", func(b *testing.B) {
 		for i := 0; i < b.N; i++ {
-			sink, sink, sink = YCbCrToRGB(128, 128, 128)
+			sink8, sink8, sink8 = YCbCrToRGB(128, 128, 128)
 		}
 	})
 	b.Run("255", func(b *testing.B) {
 		for i := 0; i < b.N; i++ {
-			sink, sink, sink = YCbCrToRGB(255, 255, 255)
+			sink8, sink8, sink8 = YCbCrToRGB(255, 255, 255)
 		}
 	})
 }
@@ -201,17 +202,65 @@ func BenchmarkRGBToYCbCr(b *testing.B) {
 	// through the generated code.
 	b.Run("0", func(b *testing.B) {
 		for i := 0; i < b.N; i++ {
-			sink, sink, sink = RGBToYCbCr(0, 0, 0)
+			sink8, sink8, sink8 = RGBToYCbCr(0, 0, 0)
 		}
 	})
 	b.Run("Cb", func(b *testing.B) {
 		for i := 0; i < b.N; i++ {
-			sink, sink, sink = RGBToYCbCr(0, 0, 255)
+			sink8, sink8, sink8 = RGBToYCbCr(0, 0, 255)
 		}
 	})
 	b.Run("Cr", func(b *testing.B) {
 		for i := 0; i < b.N; i++ {
-			sink, sink, sink = RGBToYCbCr(255, 0, 0)
+			sink8, sink8, sink8 = RGBToYCbCr(255, 0, 0)
+		}
+	})
+}
+
+func BenchmarkYCbCrToRGBA(b *testing.B) {
+	// RGB does saturating arithmetic.
+	// Low, middle, and high values can take
+	// different paths through the generated code.
+	b.Run("0", func(b *testing.B) {
+		c := YCbCr{0, 0, 0}
+		for i := 0; i < b.N; i++ {
+			sink32, sink32, sink32, sink32 = c.RGBA()
+		}
+	})
+	b.Run("128", func(b *testing.B) {
+		c := YCbCr{128, 128, 128}
+		for i := 0; i < b.N; i++ {
+			sink32, sink32, sink32, sink32 = c.RGBA()
+		}
+	})
+	b.Run("255", func(b *testing.B) {
+		c := YCbCr{255, 255, 255}
+		for i := 0; i < b.N; i++ {
+			sink32, sink32, sink32, sink32 = c.RGBA()
+		}
+	})
+}
+
+func BenchmarkNYCbCrAToRGBA(b *testing.B) {
+	// RGBA does saturating arithmetic.
+	// Low, middle, and high values can take
+	// different paths through the generated code.
+	b.Run("0", func(b *testing.B) {
+		c := NYCbCrA{YCbCr{0, 0, 0}, 0xff}
+		for i := 0; i < b.N; i++ {
+			sink32, sink32, sink32, sink32 = c.RGBA()
+		}
+	})
+	b.Run("128", func(b *testing.B) {
+		c := NYCbCrA{YCbCr{128, 128, 128}, 0xff}
+		for i := 0; i < b.N; i++ {
+			sink32, sink32, sink32, sink32 = c.RGBA()
+		}
+	})
+	b.Run("255", func(b *testing.B) {
+		c := NYCbCrA{YCbCr{255, 255, 255}, 0xff}
+		for i := 0; i < b.N; i++ {
+			sink32, sink32, sink32, sink32 = c.RGBA()
 		}
 	})
 }
diff --git a/src/image/draw/bench_test.go b/src/image/draw/bench_test.go
index 7b89f95..a41d7e7 100644
--- a/src/image/draw/bench_test.go
+++ b/src/image/draw/bench_test.go
@@ -74,7 +74,7 @@ func bench(b *testing.B, dcm, scm, mcm color.Model, op Op) {
 	var src image.Image
 	switch scm {
 	case nil:
-		src = &image.Uniform{C: color.RGBA{0x11, 0x22, 0x33, 0xff}}
+		src = &image.Uniform{C: color.RGBA{0x11, 0x22, 0x33, 0x44}}
 	case color.CMYKModel:
 		src1 := image.NewCMYK(image.Rect(0, 0, srcw, srch))
 		for y := 0; y < srch; y++ {
diff --git a/src/image/draw/draw.go b/src/image/draw/draw.go
index 6a16cd3..a31dd42 100644
--- a/src/image/draw/draw.go
+++ b/src/image/draw/draw.go
@@ -116,7 +116,12 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
 			if mask == nil {
 				switch src0 := src.(type) {
 				case *image.Uniform:
-					drawFillOver(dst0, r, src0)
+					sr, sg, sb, sa := src0.RGBA()
+					if sa == 0xffff {
+						drawFillSrc(dst0, r, sr, sg, sb, sa)
+					} else {
+						drawFillOver(dst0, r, sr, sg, sb, sa)
+					}
 					return
 				case *image.RGBA:
 					drawCopyOver(dst0, r, src0, sp)
@@ -150,7 +155,8 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
 			if mask == nil {
 				switch src0 := src.(type) {
 				case *image.Uniform:
-					drawFillSrc(dst0, r, src0)
+					sr, sg, sb, sa := src0.RGBA()
+					drawFillSrc(dst0, r, sr, sg, sb, sa)
 					return
 				case *image.RGBA:
 					drawCopySrc(dst0, r, src0, sp)
@@ -232,8 +238,7 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
 	}
 }
 
-func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
-	sr, sg, sb, sa := src.RGBA()
+func drawFillOver(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) {
 	// The 0x101 is here for the same reason as in drawRGBA.
 	a := (m - sa) * 0x101
 	i0 := dst.PixOffset(r.Min.X, r.Min.Y)
@@ -255,8 +260,7 @@ func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
 	}
 }
 
-func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) {
-	sr, sg, sb, sa := src.RGBA()
+func drawFillSrc(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) {
 	sr8 := uint8(sr >> 8)
 	sg8 := uint8(sg >> 8)
 	sb8 := uint8(sb >> 8)
diff --git a/src/image/draw/example_test.go b/src/image/draw/example_test.go
new file mode 100644
index 0000000..2ccc2f4
--- /dev/null
+++ b/src/image/draw/example_test.go
@@ -0,0 +1,48 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package draw_test
+
+import (
+	"fmt"
+	"image"
+	"image/color"
+	"image/draw"
+	"math"
+)
+
+func ExampleDrawer_floydSteinberg() {
+	const width = 130
+	const height = 50
+
+	im := image.NewGray(image.Rectangle{Max: image.Point{X: width, Y: height}})
+	for x := 0; x < width; x++ {
+		for y := 0; y < height; y++ {
+			dist := math.Sqrt(math.Pow(float64(x-width/2), 2)/3+math.Pow(float64(y-height/2), 2)) / (height / 1.5) * 255
+			var gray uint8
+			if dist > 255 {
+				gray = 255
+			} else {
+				gray = uint8(dist)
+			}
+			im.SetGray(x, y, color.Gray{Y: 255 - gray})
+		}
+	}
+	pi := image.NewPaletted(im.Bounds(), []color.Color{
+		color.Gray{Y: 255},
+		color.Gray{Y: 160},
+		color.Gray{Y: 70},
+		color.Gray{Y: 35},
+		color.Gray{Y: 0},
+	})
+
+	draw.FloydSteinberg.Draw(pi, im.Bounds(), im, image.ZP)
+	shade := []string{" ", "░", "▒", "▓", "█"}
+	for i, p := range pi.Pix {
+		fmt.Print(shade[p])
+		if (i+1)%width == 0 {
+			fmt.Print("\n")
+		}
+	}
+}
diff --git a/src/image/gif/reader.go b/src/image/gif/reader.go
index 6181a94..e611128 100644
--- a/src/image/gif/reader.go
+++ b/src/image/gif/reader.go
@@ -63,6 +63,22 @@ const (
 	eApplication    = 0xFF // Application
 )
 
+func readFull(r io.Reader, b []byte) error {
+	_, err := io.ReadFull(r, b)
+	if err == io.EOF {
+		err = io.ErrUnexpectedEOF
+	}
+	return err
+}
+
+func readByte(r io.ByteReader) (byte, error) {
+	b, err := r.ReadByte()
+	if err == io.EOF {
+		err = io.ErrUnexpectedEOF
+	}
+	return b, err
+}
+
 // decoder is the type used to decode a GIF file.
 type decoder struct {
 	r reader
@@ -124,7 +140,7 @@ func (b *blockReader) Read(p []byte) (int, error) {
 			return 0, b.err
 		}
 		b.slice = b.tmp[:blockLen]
-		if _, b.err = io.ReadFull(b.r, b.slice); b.err != nil {
+		if b.err = readFull(b.r, b.slice); b.err != nil {
 			return 0, b.err
 		}
 	}
@@ -151,9 +167,9 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
 	}
 
 	for {
-		c, err := d.r.ReadByte()
+		c, err := readByte(d.r)
 		if err != nil {
-			return err
+			return fmt.Errorf("gif: reading frames: %v", err)
 		}
 		switch c {
 		case sExtension:
@@ -198,9 +214,9 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
 					m.Palette = p
 				}
 			}
-			litWidth, err := d.r.ReadByte()
+			litWidth, err := readByte(d.r)
 			if err != nil {
-				return err
+				return fmt.Errorf("gif: reading image data: %v", err)
 			}
 			if litWidth < 2 || litWidth > 8 {
 				return fmt.Errorf("gif: pixel size in decode out of range: %d", litWidth)
@@ -209,9 +225,9 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
 			br := &blockReader{r: d.r}
 			lzwr := lzw.NewReader(br, lzw.LSB, int(litWidth))
 			defer lzwr.Close()
-			if _, err = io.ReadFull(lzwr, m.Pix); err != nil {
+			if err = readFull(lzwr, m.Pix); err != nil {
 				if err != io.ErrUnexpectedEOF {
-					return err
+					return fmt.Errorf("gif: reading image data: %v", err)
 				}
 				return errNotEnough
 			}
@@ -228,13 +244,13 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
 			// See https://golang.org/issue/9856 for an example GIF.
 			if n, err := lzwr.Read(d.tmp[:1]); n != 0 || (err != io.EOF && err != io.ErrUnexpectedEOF) {
 				if err != nil {
-					return err
+					return fmt.Errorf("gif: reading image data: %v", err)
 				}
 				return errTooMuch
 			}
 			if n, err := br.Read(d.tmp[:1]); n != 0 || err != io.EOF {
 				if err != nil {
-					return err
+					return fmt.Errorf("gif: reading image data: %v", err)
 				}
 				return errTooMuch
 			}
@@ -264,7 +280,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
 
 		case sTrailer:
 			if len(d.image) == 0 {
-				return io.ErrUnexpectedEOF
+				return fmt.Errorf("gif: missing image data")
 			}
 			return nil
 
@@ -275,13 +291,13 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error {
 }
 
 func (d *decoder) readHeaderAndScreenDescriptor() error {
-	_, err := io.ReadFull(d.r, d.tmp[:13])
+	err := readFull(d.r, d.tmp[:13])
 	if err != nil {
-		return err
+		return fmt.Errorf("gif: reading header: %v", err)
 	}
 	d.vers = string(d.tmp[:6])
 	if d.vers != "GIF87a" && d.vers != "GIF89a" {
-		return fmt.Errorf("gif: can't recognize format %s", d.vers)
+		return fmt.Errorf("gif: can't recognize format %q", d.vers)
 	}
 	d.width = int(d.tmp[6]) + int(d.tmp[7])<<8
 	d.height = int(d.tmp[8]) + int(d.tmp[9])<<8
@@ -298,9 +314,9 @@ func (d *decoder) readHeaderAndScreenDescriptor() error {
 
 func (d *decoder) readColorTable(fields byte) (color.Palette, error) {
 	n := 1 << (1 + uint(fields&fColorTableBitsMask))
-	_, err := io.ReadFull(d.r, d.tmp[:3*n])
+	err := readFull(d.r, d.tmp[:3*n])
 	if err != nil {
-		return nil, fmt.Errorf("gif: short read on color table: %s", err)
+		return nil, fmt.Errorf("gif: reading color table: %s", err)
 	}
 	j, p := 0, make(color.Palette, n)
 	for i := range p {
@@ -311,9 +327,9 @@ func (d *decoder) readColorTable(fields byte) (color.Palette, error) {
 }
 
 func (d *decoder) readExtension() error {
-	extension, err := d.r.ReadByte()
+	extension, err := readByte(d.r)
 	if err != nil {
-		return err
+		return fmt.Errorf("gif: reading extension: %v", err)
 	}
 	size := 0
 	switch extension {
@@ -324,9 +340,9 @@ func (d *decoder) readExtension() error {
 	case eComment:
 		// nothing to do but read the data.
 	case eApplication:
-		b, err := d.r.ReadByte()
+		b, err := readByte(d.r)
 		if err != nil {
-			return err
+			return fmt.Errorf("gif: reading extension: %v", err)
 		}
 		// The spec requires size be 11, but Adobe sometimes uses 10.
 		size = int(b)
@@ -334,8 +350,8 @@ func (d *decoder) readExtension() error {
 		return fmt.Errorf("gif: unknown extension 0x%.2x", extension)
 	}
 	if size > 0 {
-		if _, err := io.ReadFull(d.r, d.tmp[:size]); err != nil {
-			return err
+		if err := readFull(d.r, d.tmp[:size]); err != nil {
+			return fmt.Errorf("gif: reading extension: %v", err)
 		}
 	}
 
@@ -343,8 +359,11 @@ func (d *decoder) readExtension() error {
 	// this extension defines a loop count.
 	if extension == eApplication && string(d.tmp[:size]) == "NETSCAPE2.0" {
 		n, err := d.readBlock()
-		if n == 0 || err != nil {
-			return err
+		if err != nil {
+			return fmt.Errorf("gif: reading extension: %v", err)
+		}
+		if n == 0 {
+			return nil
 		}
 		if n == 3 && d.tmp[0] == 1 {
 			d.loopCount = int(d.tmp[1]) | int(d.tmp[2])<<8
@@ -352,14 +371,17 @@ func (d *decoder) readExtension() error {
 	}
 	for {
 		n, err := d.readBlock()
-		if n == 0 || err != nil {
-			return err
+		if err != nil {
+			return fmt.Errorf("gif: reading extension: %v", err)
+		}
+		if n == 0 {
+			return nil
 		}
 	}
 }
 
 func (d *decoder) readGraphicControl() error {
-	if _, err := io.ReadFull(d.r, d.tmp[:6]); err != nil {
+	if err := readFull(d.r, d.tmp[:6]); err != nil {
 		return fmt.Errorf("gif: can't read graphic control: %s", err)
 	}
 	if d.tmp[0] != 4 {
@@ -379,7 +401,7 @@ func (d *decoder) readGraphicControl() error {
 }
 
 func (d *decoder) newImageFromDescriptor() (*image.Paletted, error) {
-	if _, err := io.ReadFull(d.r, d.tmp[:9]); err != nil {
+	if err := readFull(d.r, d.tmp[:9]); err != nil {
 		return nil, fmt.Errorf("gif: can't read image descriptor: %s", err)
 	}
 	left := int(d.tmp[0]) + int(d.tmp[1])<<8
@@ -399,11 +421,14 @@ func (d *decoder) newImageFromDescriptor() (*image.Paletted, error) {
 }
 
 func (d *decoder) readBlock() (int, error) {
-	n, err := d.r.ReadByte()
+	n, err := readByte(d.r)
 	if n == 0 || err != nil {
 		return 0, err
 	}
-	return io.ReadFull(d.r, d.tmp[:n])
+	if err := readFull(d.r, d.tmp[:n]); err != nil {
+		return 0, err
+	}
+	return int(n), nil
 }
 
 // interlaceScan defines the ordering for a pass of the interlace algorithm.
diff --git a/src/image/gif/reader_test.go b/src/image/gif/reader_test.go
index 90c8149..1267ba0 100644
--- a/src/image/gif/reader_test.go
+++ b/src/image/gif/reader_test.go
@@ -10,6 +10,7 @@ import (
 	"image"
 	"image/color"
 	"reflect"
+	"strings"
 	"testing"
 )
 
@@ -292,3 +293,19 @@ func TestLoopCount(t *testing.T) {
 		t.Errorf("loop count mismatch: %d vs %d", img.LoopCount, img1.LoopCount)
 	}
 }
+
+func TestUnexpectedEOF(t *testing.T) {
+	for i := len(testGIF) - 1; i >= 0; i-- {
+		_, err := Decode(bytes.NewReader(testGIF[:i]))
+		if err == errNotEnough {
+			continue
+		}
+		text := ""
+		if err != nil {
+			text = err.Error()
+		}
+		if !strings.HasPrefix(text, "gif:") || !strings.HasSuffix(text, ": unexpected EOF") {
+			t.Errorf("Decode(testGIF[:%d]) = %v, want gif: ...: unexpected EOF", i, err)
+		}
+	}
+}
diff --git a/src/image/png/example_test.go b/src/image/png/example_test.go
new file mode 100644
index 0000000..c437632
--- /dev/null
+++ b/src/image/png/example_test.go
@@ -0,0 +1,77 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package png_test
+
+import (
+	"encoding/base64"
+	"fmt"
+	"image"
+	"image/color"
+	"image/png"
+	"io"
+	"log"
+	"os"
+	"strings"
+)
+
+const gopher = `iVBORw0KGgoAAAANSUhEUgAAAEsAAAA8CAAAAAALAhhPAAAFfUlEQVRYw62XeWwUVRzHf2+OPbo9d7tsWyiyaZti6eWGAhISoIGKECEKCAiJJkYTiUgTMYSIosYYBBIUIxoSPIINEBDi2VhwkQrVsj1ESgu9doHWdrul7ba73WNm3vOPtsseM9MdwvvrzTs+8/t95ze/33sI5BqiabU6m9En8oNjduLnAEDLUsQXFF8tQ5oxK3vmnNmDSMtrncks9Hhtt/qeWZapHb1ha3UqYSWVl2ZmpWgaXMXGohQAvmeop3bjTRtv6SgaK/Pb9/bFzUrYslbFAmHPp+3WhAYdr+7GN/YnpN46Opv55VDsJkoEpMrY/vO2BIYQ6LLvm0ThY3MzDzzeSJeeWNyTkgnIE5ePKsvKlcg/0T9QMzXalwXMlj54z4c0rh/mzEfr+FgWEz2w6uk8dkzFAgcARAgNp1ZYef8b [...]
+
+// gopherPNG creates an io.Reader by decoding the base64 encoded image data string in the gopher constant.
+func gopherPNG() io.Reader { return base64.NewDecoder(base64.StdEncoding, strings.NewReader(gopher)) }
+
+func ExampleDecode() {
+	// This example uses png.Decode which can only decode PNG images.
+	// Consider using the general image.Decode as it can sniff and decode any registered image format.
+	img, err := png.Decode(gopherPNG())
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	levels := []string{" ", "░", "▒", "▓", "█"}
+
+	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
+		for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
+			c := color.GrayModel.Convert(img.At(x, y)).(color.Gray)
+			level := c.Y / 51 // 51 * 5 = 255
+			if level == 5 {
+				level--
+			}
+			fmt.Print(levels[level])
+		}
+		fmt.Print("\n")
+	}
+}
+
+func ExampleEncode() {
+	const width, height = 256, 256
+
+	// Create a colored image of the given width and height.
+	img := image.NewNRGBA(image.Rect(0, 0, width, height))
+
+	for y := 0; y < height; y++ {
+		for x := 0; x < width; x++ {
+			img.Set(x, y, color.NRGBA{
+				R: uint8((x + y) & 255),
+				G: uint8((x + y) << 1 & 255),
+				B: uint8((x + y) << 2 & 255),
+				A: 255,
+			})
+		}
+	}
+
+	f, err := os.Create("image.png")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if err := png.Encode(f, img); err != nil {
+		f.Close()
+		log.Fatal(err)
+	}
+
+	if err := f.Close(); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/src/image/png/reader.go b/src/image/png/reader.go
index 2dd5ed8..32f78f0 100644
--- a/src/image/png/reader.go
+++ b/src/image/png/reader.go
@@ -113,6 +113,11 @@ type decoder struct {
 	idatLength    uint32
 	tmp           [3 * 256]byte
 	interlace     int
+
+	// useTransparent and transparent are used for grayscale and truecolor
+	// transparency, as opposed to palette transparency.
+	useTransparent bool
+	transparent    [6]byte
 }
 
 // A FormatError reports that the input is not a valid PNG.
@@ -252,20 +257,51 @@ func (d *decoder) parsePLTE(length uint32) error {
 }
 
 func (d *decoder) parsetRNS(length uint32) error {
-	if length > 256 {
-		return FormatError("bad tRNS length")
-	}
-	n, err := io.ReadFull(d.r, d.tmp[:length])
-	if err != nil {
-		return err
-	}
-	d.crc.Write(d.tmp[:n])
 	switch d.cb {
-	case cbG8, cbG16:
-		return UnsupportedError("grayscale transparency")
+	case cbG1, cbG2, cbG4, cbG8, cbG16:
+		if length != 2 {
+			return FormatError("bad tRNS length")
+		}
+		n, err := io.ReadFull(d.r, d.tmp[:length])
+		if err != nil {
+			return err
+		}
+		d.crc.Write(d.tmp[:n])
+
+		copy(d.transparent[:], d.tmp[:length])
+		switch d.cb {
+		case cbG1:
+			d.transparent[1] *= 0xff
+		case cbG2:
+			d.transparent[1] *= 0x55
+		case cbG4:
+			d.transparent[1] *= 0x11
+		}
+		d.useTransparent = true
+
 	case cbTC8, cbTC16:
-		return UnsupportedError("truecolor transparency")
+		if length != 6 {
+			return FormatError("bad tRNS length")
+		}
+		n, err := io.ReadFull(d.r, d.tmp[:length])
+		if err != nil {
+			return err
+		}
+		d.crc.Write(d.tmp[:n])
+
+		copy(d.transparent[:], d.tmp[:length])
+		d.useTransparent = true
+
 	case cbP1, cbP2, cbP4, cbP8:
+		if length > 256 {
+			return FormatError("bad tRNS length")
+		}
+		n, err := io.ReadFull(d.r, d.tmp[:length])
+		if err != nil {
+			return err
+		}
+		d.crc.Write(d.tmp[:n])
+
 		if len(d.palette) < n {
 			d.palette = d.palette[:n]
 		}
@@ -273,7 +309,8 @@ func (d *decoder) parsetRNS(length uint32) error {
 			rgba := d.palette[i].(color.RGBA)
 			d.palette[i] = color.NRGBA{rgba.R, rgba.G, rgba.B, d.tmp[i]}
 		}
-	case cbGA8, cbGA16, cbTCA8, cbTCA16:
+
+	default:
 		return FormatError("tRNS, color type mismatch")
 	}
 	return d.verifyChecksum()
@@ -366,7 +403,7 @@ func (d *decoder) decode() (image.Image, error) {
 
 // readImagePass reads a single image pass, sized according to the pass number.
 func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image.Image, error) {
-	var bitsPerPixel int = 0
+	bitsPerPixel := 0
 	pixOffset := 0
 	var (
 		gray     *image.Gray
@@ -394,16 +431,26 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
 	switch d.cb {
 	case cbG1, cbG2, cbG4, cbG8:
 		bitsPerPixel = d.depth
-		gray = image.NewGray(image.Rect(0, 0, width, height))
-		img = gray
+		if d.useTransparent {
+			nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))
+			img = nrgba
+		} else {
+			gray = image.NewGray(image.Rect(0, 0, width, height))
+			img = gray
+		}
 	case cbGA8:
 		bitsPerPixel = 16
 		nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))
 		img = nrgba
 	case cbTC8:
 		bitsPerPixel = 24
-		rgba = image.NewRGBA(image.Rect(0, 0, width, height))
-		img = rgba
+		if d.useTransparent {
+			nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))
+			img = nrgba
+		} else {
+			rgba = image.NewRGBA(image.Rect(0, 0, width, height))
+			img = rgba
+		}
 	case cbP1, cbP2, cbP4, cbP8:
 		bitsPerPixel = d.depth
 		paletted = image.NewPaletted(image.Rect(0, 0, width, height), d.palette)
@@ -414,16 +461,26 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
 		img = nrgba
 	case cbG16:
 		bitsPerPixel = 16
-		gray16 = image.NewGray16(image.Rect(0, 0, width, height))
-		img = gray16
+		if d.useTransparent {
+			nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
+			img = nrgba64
+		} else {
+			gray16 = image.NewGray16(image.Rect(0, 0, width, height))
+			img = gray16
+		}
 	case cbGA16:
 		bitsPerPixel = 32
 		nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
 		img = nrgba64
 	case cbTC16:
 		bitsPerPixel = 48
-		rgba64 = image.NewRGBA64(image.Rect(0, 0, width, height))
-		img = rgba64
+		if d.useTransparent {
+			nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
+			img = nrgba64
+		} else {
+			rgba64 = image.NewRGBA64(image.Rect(0, 0, width, height))
+			img = rgba64
+		}
 	case cbTCA16:
 		bitsPerPixel = 64
 		nrgba64 = image.NewNRGBA64(image.Rect(0, 0, width, height))
@@ -483,27 +540,75 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
 		// Convert from bytes to colors.
 		switch d.cb {
 		case cbG1:
-			for x := 0; x < width; x += 8 {
-				b := cdat[x/8]
-				for x2 := 0; x2 < 8 && x+x2 < width; x2++ {
-					gray.SetGray(x+x2, y, color.Gray{(b >> 7) * 0xff})
-					b <<= 1
+			if d.useTransparent {
+				ty := d.transparent[1]
+				for x := 0; x < width; x += 8 {
+					b := cdat[x/8]
+					for x2 := 0; x2 < 8 && x+x2 < width; x2++ {
+						ycol := (b >> 7) * 0xff
+						acol := uint8(0xff)
+						if ycol == ty {
+							acol = 0x00
+						}
+						nrgba.SetNRGBA(x+x2, y, color.NRGBA{ycol, ycol, ycol, acol})
+						b <<= 1
+					}
+				}
+			} else {
+				for x := 0; x < width; x += 8 {
+					b := cdat[x/8]
+					for x2 := 0; x2 < 8 && x+x2 < width; x2++ {
+						gray.SetGray(x+x2, y, color.Gray{(b >> 7) * 0xff})
+						b <<= 1
+					}
 				}
 			}
 		case cbG2:
-			for x := 0; x < width; x += 4 {
-				b := cdat[x/4]
-				for x2 := 0; x2 < 4 && x+x2 < width; x2++ {
-					gray.SetGray(x+x2, y, color.Gray{(b >> 6) * 0x55})
-					b <<= 2
+			if d.useTransparent {
+				ty := d.transparent[1]
+				for x := 0; x < width; x += 4 {
+					b := cdat[x/4]
+					for x2 := 0; x2 < 4 && x+x2 < width; x2++ {
+						ycol := (b >> 6) * 0x55
+						acol := uint8(0xff)
+						if ycol == ty {
+							acol = 0x00
+						}
+						nrgba.SetNRGBA(x+x2, y, color.NRGBA{ycol, ycol, ycol, acol})
+						b <<= 2
+					}
+				}
+			} else {
+				for x := 0; x < width; x += 4 {
+					b := cdat[x/4]
+					for x2 := 0; x2 < 4 && x+x2 < width; x2++ {
+						gray.SetGray(x+x2, y, color.Gray{(b >> 6) * 0x55})
+						b <<= 2
+					}
 				}
 			}
 		case cbG4:
-			for x := 0; x < width; x += 2 {
-				b := cdat[x/2]
-				for x2 := 0; x2 < 2 && x+x2 < width; x2++ {
-					gray.SetGray(x+x2, y, color.Gray{(b >> 4) * 0x11})
-					b <<= 4
+			if d.useTransparent {
+				ty := d.transparent[1]
+				for x := 0; x < width; x += 2 {
+					b := cdat[x/2]
+					for x2 := 0; x2 < 2 && x+x2 < width; x2++ {
+						ycol := (b >> 4) * 0x11
+						acol := uint8(0xff)
+						if ycol == ty {
+							acol = 0x00
+						}
+						nrgba.SetNRGBA(x+x2, y, color.NRGBA{ycol, ycol, ycol, acol})
+						b <<= 4
+					}
+				}
+			} else {
+				for x := 0; x < width; x += 2 {
+					b := cdat[x/2]
+					for x2 := 0; x2 < 2 && x+x2 < width; x2++ {
+						gray.SetGray(x+x2, y, color.Gray{(b >> 4) * 0x11})
+						b <<= 4
+					}
 				}
 			}
 		case cbG8:
@@ -515,16 +620,37 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
 				nrgba.SetNRGBA(x, y, color.NRGBA{ycol, ycol, ycol, cdat[2*x+1]})
 			}
 		case cbTC8:
-			pix, i, j := rgba.Pix, pixOffset, 0
-			for x := 0; x < width; x++ {
-				pix[i+0] = cdat[j+0]
-				pix[i+1] = cdat[j+1]
-				pix[i+2] = cdat[j+2]
-				pix[i+3] = 0xff
-				i += 4
-				j += 3
+			if d.useTransparent {
+				pix, i, j := nrgba.Pix, pixOffset, 0
+				tr, tg, tb := d.transparent[1], d.transparent[3], d.transparent[5]
+				for x := 0; x < width; x++ {
+					r := cdat[j+0]
+					g := cdat[j+1]
+					b := cdat[j+2]
+					a := uint8(0xff)
+					if r == tr && g == tg && b == tb {
+						a = 0x00
+					}
+					pix[i+0] = r
+					pix[i+1] = g
+					pix[i+2] = b
+					pix[i+3] = a
+					i += 4
+					j += 3
+				}
+				pixOffset += nrgba.Stride
+			} else {
+				pix, i, j := rgba.Pix, pixOffset, 0
+				for x := 0; x < width; x++ {
+					pix[i+0] = cdat[j+0]
+					pix[i+1] = cdat[j+1]
+					pix[i+2] = cdat[j+2]
+					pix[i+3] = 0xff
+					i += 4
+					j += 3
+				}
+				pixOffset += rgba.Stride
 			}
-			pixOffset += rgba.Stride
 		case cbP1:
 			for x := 0; x < width; x += 8 {
 				b := cdat[x/8]
@@ -575,9 +701,21 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
 			copy(nrgba.Pix[pixOffset:], cdat)
 			pixOffset += nrgba.Stride
 		case cbG16:
-			for x := 0; x < width; x++ {
-				ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1])
-				gray16.SetGray16(x, y, color.Gray16{ycol})
+			if d.useTransparent {
+				ty := uint16(d.transparent[0])<<8 | uint16(d.transparent[1])
+				for x := 0; x < width; x++ {
+					ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1])
+					acol := uint16(0xffff)
+					if ycol == ty {
+						acol = 0x0000
+					}
+					nrgba64.SetNRGBA64(x, y, color.NRGBA64{ycol, ycol, ycol, acol})
+				}
+			} else {
+				for x := 0; x < width; x++ {
+					ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1])
+					gray16.SetGray16(x, y, color.Gray16{ycol})
+				}
 			}
 		case cbGA16:
 			for x := 0; x < width; x++ {
@@ -586,11 +724,27 @@ func (d *decoder) readImagePass(r io.Reader, pass int, allocateOnly bool) (image
 				nrgba64.SetNRGBA64(x, y, color.NRGBA64{ycol, ycol, ycol, acol})
 			}
 		case cbTC16:
-			for x := 0; x < width; x++ {
-				rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1])
-				gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3])
-				bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5])
-				rgba64.SetRGBA64(x, y, color.RGBA64{rcol, gcol, bcol, 0xffff})
+			if d.useTransparent {
+				tr := uint16(d.transparent[0])<<8 | uint16(d.transparent[1])
+				tg := uint16(d.transparent[2])<<8 | uint16(d.transparent[3])
+				tb := uint16(d.transparent[4])<<8 | uint16(d.transparent[5])
+				for x := 0; x < width; x++ {
+					rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1])
+					gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3])
+					bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5])
+					acol := uint16(0xffff)
+					if rcol == tr && gcol == tg && bcol == tb {
+						acol = 0x0000
+					}
+					nrgba64.SetNRGBA64(x, y, color.NRGBA64{rcol, gcol, bcol, acol})
+				}
+			} else {
+				for x := 0; x < width; x++ {
+					rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1])
+					gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3])
+					bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5])
+					rgba64.SetRGBA64(x, y, color.RGBA64{rcol, gcol, bcol, 0xffff})
+				}
 			}
 		case cbTCA16:
 			for x := 0; x < width; x++ {
@@ -709,7 +863,11 @@ func (d *decoder) parseChunk() error {
 		d.stage = dsSeenPLTE
 		return d.parsePLTE(length)
 	case "tRNS":
-		if d.stage != dsSeenPLTE {
+		if cbPaletted(d.cb) {
+			if d.stage != dsSeenPLTE {
+				return chunkOrderError
+			}
+		} else if d.stage != dsSeenIHDR {
 			return chunkOrderError
 		}
 		d.stage = dsSeentRNS
diff --git a/src/image/png/reader_test.go b/src/image/png/reader_test.go
index 0bc4203..b9e9f4d 100644
--- a/src/image/png/reader_test.go
+++ b/src/image/png/reader_test.go
@@ -39,6 +39,21 @@ var filenames = []string{
 	"basn4a16",
 	"basn6a08",
 	"basn6a16",
+	"ftbbn0g01",
+	"ftbbn0g02",
+	"ftbbn0g04",
+	"ftbbn2c16",
+	"ftbbn3p08",
+	"ftbgn2c16",
+	"ftbgn3p08",
+	"ftbrn2c08",
+	"ftbwn0g16",
+	"ftbwn3p08",
+	"ftbyn3p08",
+	"ftp0n0g08",
+	"ftp0n2c08",
+	"ftp0n3p08",
+	"ftp1n3p08",
 }
 
 var filenamesPaletted = []string{
@@ -64,6 +79,50 @@ func readPNG(filename string) (image.Image, error) {
 	return Decode(f)
 }
 
+// fakebKGDs maps from filenames to fake bKGD chunks for our approximation to
+// the sng command-line tool. Package png doesn't keep that metadata when
+// png.Decode returns an image.Image.
+var fakebKGDs = map[string]string{
+	"ftbbn0g01": "bKGD {gray: 0;}\n",
+	"ftbbn0g02": "bKGD {gray: 0;}\n",
+	"ftbbn0g04": "bKGD {gray: 0;}\n",
+	"ftbbn2c16": "bKGD {red: 0;  green: 0;  blue: 65535;}\n",
+	"ftbbn3p08": "bKGD {index: 245}\n",
+	"ftbgn2c16": "bKGD {red: 0;  green: 65535;  blue: 0;}\n",
+	"ftbgn3p08": "bKGD {index: 245}\n",
+	"ftbrn2c08": "bKGD {red: 255;  green: 0;  blue: 0;}\n",
+	"ftbwn0g16": "bKGD {gray: 65535;}\n",
+	"ftbwn3p08": "bKGD {index: 0}\n",
+	"ftbyn3p08": "bKGD {index: 245}\n",
+}
+
+// fakegAMAs maps from filenames to fake gAMA chunks for our approximation to
+// the sng command-line tool. Package png doesn't keep that metadata when
+// png.Decode returns an image.Image.
+var fakegAMAs = map[string]string{
+	"ftbbn0g01": "",
+	"ftbbn0g02": "gAMA {0.45455}\n",
+}
+
+// fakeIHDRUsings maps from filenames to fake IHDR "using" lines for our
+// approximation to the sng command-line tool. The PNG model is that
+// transparency (in the tRNS chunk) is separate to the color/grayscale/palette
+// color model (in the IHDR chunk). The Go model is that the concrete
+// image.Image type returned by png.Decode, such as image.RGBA (with all pixels
+// having 100% alpha) or image.NRGBA, encapsulates whether or not the image has
+// transparency. This map is a hack to work around the fact that the Go model
+// can't otherwise discriminate PNG's "IHDR says color (with no alpha) but tRNS
+// says alpha" and "IHDR says color with alpha".
+var fakeIHDRUsings = map[string]string{
+	"ftbbn0g01": "    using grayscale;\n",
+	"ftbbn0g02": "    using grayscale;\n",
+	"ftbbn0g04": "    using grayscale;\n",
+	"ftbbn2c16": "    using color;\n",
+	"ftbgn2c16": "    using color;\n",
+	"ftbrn2c08": "    using color;\n",
+	"ftbwn0g16": "    using grayscale;\n",
+}
+
 // An approximation of the sng command-line tool.
 func sng(w io.WriteCloser, filename string, png image.Image) {
 	defer w.Close()
@@ -95,25 +154,35 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
 	// Write the filename and IHDR.
 	io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n")
 	fmt.Fprintf(w, "    width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth)
-	switch {
-	case cm == color.RGBAModel, cm == color.RGBA64Model:
-		io.WriteString(w, "    using color;\n")
-	case cm == color.NRGBAModel, cm == color.NRGBA64Model:
-		io.WriteString(w, "    using color alpha;\n")
-	case cm == color.GrayModel, cm == color.Gray16Model:
-		io.WriteString(w, "    using grayscale;\n")
-	case cpm != nil:
-		io.WriteString(w, "    using color palette;\n")
-	default:
-		io.WriteString(w, "unknown PNG decoder color model\n")
+	if s, ok := fakeIHDRUsings[filename]; ok {
+		io.WriteString(w, s)
+	} else {
+		switch {
+		case cm == color.RGBAModel, cm == color.RGBA64Model:
+			io.WriteString(w, "    using color;\n")
+		case cm == color.NRGBAModel, cm == color.NRGBA64Model:
+			io.WriteString(w, "    using color alpha;\n")
+		case cm == color.GrayModel, cm == color.Gray16Model:
+			io.WriteString(w, "    using grayscale;\n")
+		case cpm != nil:
+			io.WriteString(w, "    using color palette;\n")
+		default:
+			io.WriteString(w, "unknown PNG decoder color model\n")
+		}
 	}
 	io.WriteString(w, "}\n")
 
-	// We fake a gAMA output. The test files have a gAMA chunk but the go PNG parser ignores it
-	// (the PNG spec section 11.3 says "Ancillary chunks may be ignored by a decoder").
-	io.WriteString(w, "gAMA {1.0000}\n")
+	// We fake a gAMA chunk. The test files have a gAMA chunk but the go PNG
+	// parser ignores it (the PNG spec section 11.3 says "Ancillary chunks may
+	// be ignored by a decoder").
+	if s, ok := fakegAMAs[filename]; ok {
+		io.WriteString(w, s)
+	} else {
+		io.WriteString(w, "gAMA {1.0000}\n")
+	}
 
 	// Write the PLTE and tRNS (if applicable).
+	useTransparent := false
 	if cpm != nil {
 		lastAlpha := -1
 		io.WriteString(w, "PLTE {\n")
@@ -133,6 +202,9 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
 			fmt.Fprintf(w, "    (%3d,%3d,%3d)     # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b)
 		}
 		io.WriteString(w, "}\n")
+		if s, ok := fakebKGDs[filename]; ok {
+			io.WriteString(w, s)
+		}
 		if lastAlpha != -1 {
 			io.WriteString(w, "tRNS {\n")
 			for i := 0; i <= lastAlpha; i++ {
@@ -142,6 +214,42 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
 			}
 			io.WriteString(w, "}\n")
 		}
+	} else if strings.HasPrefix(filename, "ft") {
+		if s, ok := fakebKGDs[filename]; ok {
+			io.WriteString(w, s)
+		}
+		// We fake a tRNS chunk. The test files' grayscale and truecolor
+		// transparent images all have their top left corner transparent.
+		switch c := png.At(0, 0).(type) {
+		case color.NRGBA:
+			if c.A == 0 {
+				useTransparent = true
+				io.WriteString(w, "tRNS {\n")
+				switch filename {
+				case "ftbbn0g01", "ftbbn0g02", "ftbbn0g04":
+					// The standard image package doesn't have a "gray with
+					// alpha" type. Instead, we use an image.NRGBA.
+					fmt.Fprintf(w, "    gray: %d;\n", c.R)
+				default:
+					fmt.Fprintf(w, "    red: %d; green: %d; blue: %d;\n", c.R, c.G, c.B)
+				}
+				io.WriteString(w, "}\n")
+			}
+		case color.NRGBA64:
+			if c.A == 0 {
+				useTransparent = true
+				io.WriteString(w, "tRNS {\n")
+				switch filename {
+				case "ftbwn0g16":
+					// The standard image package doesn't have a "gray16 with
+					// alpha" type. Instead, we use an image.NRGBA64.
+					fmt.Fprintf(w, "    gray: %d;\n", c.R)
+				default:
+					fmt.Fprintf(w, "    red: %d; green: %d; blue: %d;\n", c.R, c.G, c.B)
+				}
+				io.WriteString(w, "}\n")
+			}
+		}
 	}
 
 	// Write the IMAGE.
@@ -171,12 +279,30 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
 		case cm == color.NRGBAModel:
 			for x := bounds.Min.X; x < bounds.Max.X; x++ {
 				nrgba := png.At(x, y).(color.NRGBA)
-				fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A)
+				switch filename {
+				case "ftbbn0g01", "ftbbn0g02", "ftbbn0g04":
+					fmt.Fprintf(w, "%02x", nrgba.R)
+				default:
+					if useTransparent {
+						fmt.Fprintf(w, "%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B)
+					} else {
+						fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A)
+					}
+				}
 			}
 		case cm == color.NRGBA64Model:
 			for x := bounds.Min.X; x < bounds.Max.X; x++ {
 				nrgba64 := png.At(x, y).(color.NRGBA64)
-				fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A)
+				switch filename {
+				case "ftbwn0g16":
+					fmt.Fprintf(w, "%04x ", nrgba64.R)
+				default:
+					if useTransparent {
+						fmt.Fprintf(w, "%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B)
+					} else {
+						fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A)
+					}
+				}
 			}
 		case cpm != nil:
 			var b, c int
@@ -256,8 +382,23 @@ func TestReader(t *testing.T) {
 			}
 			ps := pb.Text()
 			ss := sb.Text()
+
+			// Newer versions of the sng command line tool append an optional
+			// color name to the RGB tuple. For example:
+			//	# rgb = (0xff,0xff,0xff) grey100
+			//	# rgb = (0x00,0x00,0xff) blue1
+			// instead of the older version's plainer:
+			//	# rgb = (0xff,0xff,0xff)
+			//	# rgb = (0x00,0x00,0xff)
+			// We strip any such name.
+			if strings.Contains(ss, "# rgb = (") && !strings.HasSuffix(ss, ")") {
+				if i := strings.LastIndex(ss, ") "); i >= 0 {
+					ss = ss[:i+1]
+				}
+			}
+
 			if ps != ss {
-				t.Errorf("%s: Mismatch\n%sversus\n%s\n", fn, ps, ss)
+				t.Errorf("%s: Mismatch\n%s\nversus\n%s\n", fn, ps, ss)
 				break
 			}
 		}
diff --git a/src/image/png/testdata/pngsuite/README b/src/image/png/testdata/pngsuite/README
index c0f78bd..01d1d89 100644
--- a/src/image/png/testdata/pngsuite/README
+++ b/src/image/png/testdata/pngsuite/README
@@ -1,21 +1,20 @@
 The *.png and README.original files in this directory are copied from
-libpng.org, specifically contrib/pngsuite/* in libpng-1.2.40.tar.gz.
+libpng.org, specifically contrib/pngsuite/* in libpng 1.6.26.
+
 README.original gives the following license for those files:
 
 	Permission to use, copy, and distribute these images for any purpose
 	and without fee is hereby granted.
 
-
-The files basn0g01-30.png, basn0g02-29.png and basn0g04-31.png are in fact
-not part of pngsuite but were created from files in pngsuite. Their non-power-
-of-two sizes makes them useful for testing bit-depths smaller than a byte.
+The files basn0g01-30.png, basn0g02-29.png and basn0g04-31.png are in fact not
+part of pngsuite but were created from files in pngsuite. Their non-power-of-2
+sizes makes them useful for testing bit-depths smaller than a byte.
 
 basn3a08.png was generated from basn6a08.png using the pngnq tool, which
 converted it to the 8-bit paletted image with alpha values in tRNS chunk.
 
-The *.sng files in this directory were generated from the *.png files
-by the sng command-line tool and some hand editing. The files
-basn0g0{1,2,4}.sng were actually generated by first converting the PNG
-to a bitdepth of 8 and then running sng on them. basn4a08.sng was generated
-by from a 16-bit rgba version of basn4a08.png rather than the original
-gray + alpha.
+The *.sng files in this directory were generated from the *.png files by the
+sng command-line tool and some hand editing. The files basn0g0{1,2,4}.sng and
+ftbbn0g0{1,2,4}.sng were actually generated by first converting the PNG to a
+bitdepth of 8 and then running sng on them. basn4a08.sng was generated from a
+16-bit rgba version of basn4a08.png rather than the original gray + alpha.
diff --git a/src/image/png/testdata/pngsuite/ftbbn0g01.png b/src/image/png/testdata/pngsuite/ftbbn0g01.png
new file mode 100644
index 0000000..ba746ff
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbbn0g01.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbbn0g01.sng b/src/image/png/testdata/pngsuite/ftbbn0g01.sng
new file mode 100644
index 0000000..c5347a4
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbbn0g01.sng
@@ -0,0 +1,44 @@
+#SNG: from ftbbn0g01.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using grayscale;
+}
+bKGD {gray: 0;}
+tRNS {
+    gray: 0;
+}
+IMAGE {
+    pixels hex
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+00ffffffffffffff000000000000000000000000000000000000000000000000
+00ffffffffffffffff0000000000000000000000000000000000000000000000
+00ffffffffffffffffff00000000000000000000000000000000000000000000
+00ffffff0000ffffffff00000000000000000000000000000000000000000000
+00ffffff000000ffffff00000000000000000000000000000000000000000000
+00ffffff000000ffffff00ffffff000000ffffff000000000000000000000000
+00ffffff00ffffffffff00ffffff000000ffffff000000000000000000000000
+00ffffffffffffffffff00ffffffff0000ffffff000000000000000000000000
+00ffffffffffffffff0000ffffffff0000ffffff000000000000000000000000
+00ffffffffff0000000000ffffffffff00ffffff000000000000000000000000
+00ffffff00000000000000ffffffffff00ffffff0000000000ffffffffff0000
+00ffffff00000000000000ffffffffffffffffff000000ffffffffffffffff00
+00ffffff00000000000000ffffffffffffffffff000000ffffffffffffffff00
+00ffffff00000000000000ffffffffffffffffff0000ffffffffff00ffffff00
+0000000000000000000000ffffff00ffffffffff0000ffffffff000000000000
+0000000000000000000000ffffff00ffffffffff0000ffffff00000000000000
+0000000000000000000000ffffff0000ffffffff0000ffffff0000ffffff0000
+0000000000000000000000ffffff000000ffffff0000ffffff00ffffffffff00
+0000000000000000000000ffffff000000ffffff0000ffffff0000ffffffff00
+00000000000000000000000000000000000000000000ffffff00000000ffff00
+00000000000000000000000000000000000000000000ffffffff0000ffffff00
+00000000000000000000000000000000000000000000ffffffffffffffffff00
+0000000000000000000000000000000000000000000000ffffffffffffffff00
+000000000000000000000000000000000000000000000000ffffffffffff0000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+}
diff --git a/src/image/png/testdata/pngsuite/ftbbn0g02.png b/src/image/png/testdata/pngsuite/ftbbn0g02.png
new file mode 100644
index 0000000..3d83bd6
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbbn0g02.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbbn0g02.sng b/src/image/png/testdata/pngsuite/ftbbn0g02.sng
new file mode 100644
index 0000000..9686a6a
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbbn0g02.sng
@@ -0,0 +1,45 @@
+#SNG: from ftbbn0g02.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using grayscale;
+}
+gAMA {0.45455}
+bKGD {gray: 0;}
+tRNS {
+    gray: 0;
+}
+IMAGE {
+    pixels hex
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+00aaaaaaaaaaaaaa000000000000000000000000000000000000000000000000
+00aaaaaaaaaaaaaaaa0000000000000000000000000000000000000000000000
+00aaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000000000
+00aaaaaa0000aaaaaaaa00000000000000000000000000000000000000000000
+00aaaaaa000000aaaaaa00000000000000000000000000000000000000000000
+00aaaaaa000000aaaaaa00aaaaaa000000aaaaaa000000000000000000000000
+00aaaaaa00aaaaaaaaaa00aaaaaa000000aaaaaa000000000000000000000000
+00aaaaaaaaaaaaaaaaaa00aaaaaaaa0000aaaaaa000000000000000000000000
+00aaaaaaaaaaaaaaaa0000aaaaaaaa0000aaaaaa000000000000000000000000
+00aaaaaaaaaa0000000000aaaaaaaaaa00aaaaaa000000000000000000000000
+00aaaaaa00000000000000aaaaaaaaaa00aaaaaa0000000000aaaaaaaaaa0000
+00aaaaaa00000000000000aaaaaaaaaaaaaaaaaa000000aaaaaaaaaaaaaaaa00
+00aaaaaa00000000000000aaaaaaaaaaaaaaaaaa000000aaaaaaaaaaaaaaaa00
+00aaaaaa00000000000000aaaaaaaaaaaaaaaaaa0000aaaaaaaaaa00aaaaaa00
+0000000000000000000000aaaaaa00aaaaaaaaaa0000aaaaaaaa000000000000
+0000000000000000000000aaaaaa00aaaaaaaaaa0000aaaaaa00000000000000
+0000000000000000000000aaaaaa0000aaaaaaaa0000aaaaaa0000aaaaaa0000
+0000000000000000000000aaaaaa000000aaaaaa0000aaaaaa00aaaaaaaaaa00
+0000000000000000000000aaaaaa000000aaaaaa0000aaaaaa0000aaaaaaaa00
+00000000000000000000000000000000000000000000aaaaaa00000000aaaa00
+00000000000000000000000000000000000000000000aaaaaaaa0000aaaaaa00
+00000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaa00
+0000000000000000000000000000000000000000000000aaaaaaaaaaaaaaaa00
+000000000000000000000000000000000000000000000000aaaaaaaaaaaa0000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+}
diff --git a/src/image/png/testdata/pngsuite/ftbbn0g04.png b/src/image/png/testdata/pngsuite/ftbbn0g04.png
new file mode 100644
index 0000000..39a7050
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbbn0g04.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbbn0g04.sng b/src/image/png/testdata/pngsuite/ftbbn0g04.sng
new file mode 100644
index 0000000..518ba6c
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbbn0g04.sng
@@ -0,0 +1,45 @@
+#SNG: from ftbbn0g04.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using grayscale;
+}
+gAMA {1.0000}
+bKGD {gray: 0;}
+tRNS {
+    gray: 255;
+}
+IMAGE {
+    pixels hex
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffddcceeffffffffffffffffffffffffffffff
+ffffffffffffffffffffffeebb776655446699ddffffffffffffffffffffffff
+ffffffffffffffffeebb886666553322222222335599ccffffffffffffffffff
+ffffffffffeecc997766554433333322334422112222336699ccffffffffffff
+ffffffcc997777664433333333444433334444332233335566777799cceeffff
+ffffcc777777775533333344556655444444444444332266777777776699ffff
+ffffdd8888887766444466777777777766555555445566777777775555bbffff
+ffffee8888888888777777777777777777777777777777777766555544eeffff
+ffffff8866667788998888777777777777777777777777665555444455ffffff
+ffffff8866778888999999998877777777777777777755331111334488ffffff
+ffffff99667788889999999999998877777777776655221111111133aaffffff
+ffffff99666688888899997777999999887766555533221111001122ddffffff
+ffffffaa666677888899886666669999997755554422111122111144ffffffff
+ffffffbb666666888888777755669999997755552222113344223377ffffffff
+ffffffcc666655778877777755779999996655332211334422111199ffffffff
+ffffffdd6666446688557777557799999966552222113311111111ccffffffff
+ffffffee6666555588666677557799999966442211222211111122eeffffffff
+ffffffff6666555577775577557799999955332211332211111155ffffffffff
+ffffffff6666665566775577557799999955331111443311111188ffffffffff
+ffffffff88666655667755665577999988552211114433111111ccffffffffff
+ffffffffffaa66666666666655779999885522111133111122bbffffffffffff
+ffffffffffffcc6666666666557788998855221111111122ccffffffffffffff
+ffffffffffffffee886666665577888877553311111133ddffffffffffffffff
+ffffffffffffffffffaa666655778888775544221144eeffffffffffffffffff
+ffffffffffffffffffffcc77557788886655553377ffffffffffffffffffffff
+ffffffffffffffffffffffee9988888866555599ffffffffffffffffffffffff
+ffffffffffffffffffffffffffbb88886655bbffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffdd8866ccffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffeeddffffffffffffffffffffffffffffff
+ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+}
diff --git a/src/image/png/testdata/pngsuite/ftbbn2c16.png b/src/image/png/testdata/pngsuite/ftbbn2c16.png
new file mode 100644
index 0000000..dd3168e
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbbn2c16.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbbn2c16.sng b/src/image/png/testdata/pngsuite/ftbbn2c16.sng
new file mode 100644
index 0000000..76989fa
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbbn2c16.sng
@@ -0,0 +1,45 @@
+#SNG: from ftbbn2c16.png
+IHDR {
+    width: 32; height: 32; bitdepth: 16;
+    using color;
+}
+gAMA {1.0000}
+bKGD {red: 0;  green: 0;  blue: 65535;}
+tRNS {
+    red: 65535; green: 65535; blue: 65535;
+}
+IMAGE {
+    pixels hex
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff e3e3e3e3e3e3 c9c9c9c9c9c9 f1f1f1f1f1f1 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff e8e8e8e8e8e8 b5b5b5b5b5b5 7e7e7e7e7e7e 656565656565 6e6e52525252 7e7e2e2e2e2e a6a643434343 c7c790909090 ebebdddddddd ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff eeeeeeeeeeee bfbfbfbfbfbf 898989898989 676767676767 6b6b5d5d5d5d 7a7a39393939 8a8a12121212 8d8d00010000 858500000000 777700000000 848400000000 9a9a01010101 a2a22d2d2d2d bfbf7d7d7d7d ddddd0d0d0d0 fcfcfcfcfcfc ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff f2f2f2f2f2f2 c4c4c4c4c4c4 959595959595 727272727272 6f6f6b6b6b6b 777744444444 87871e1e1e1e 959501010101 9f9f00010000 919100000000 808000010000 72720c0c0c0c 61612d2d2d2d 53530e0e0e0e 505000000000 595900010000 858500000000 929206060606 7a7a66666666 a0a0a0a0a0a0 cfcfcfcfcfcf f8f8f8f8f8f8 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff f7f7f7f7f7f7 cacacacacaca 9a9a9a9a9a9a 767676767676 737373737373 7c7c5d5d5d5d 87872e2e2e2e 939307070707 9e9e00010000 a9a900000000 b0b000000000 c9c900000000 cfcf00000000 b9b900010000 a2a201010101 8c8c19191919 85852a2a2a2a 7f7f13131313 818100010000 969600000000 8f8f00000000 6b6b53535353 6e6e6e6e6e6e 737373737373 767676767676 9b9b9b9b9b9b c4c4c4c4c4c4 eeeeeeeeeeee ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff cccccccccccc 7f7f7f7f7f7f 767676767676 757575757575 757575757575 96962f2f2f2f b8b800010000 b4b400000000 b6b600010000 adad0c0c0c0c 94943a3a3a3a 929250505050 b9b923232323 d6d602020202 e2e200010000 efef00000000 e7e700000000 dada00000000 cfcf00010000 baba00000000 7d7d01010101 6f6f6b6b6b6b 757575757575 757575757575 757575757575 757575757575 6a6a6a6a6a6a 9a9a9a9a9a9a ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff dcdcdcdcdcdc 858585858585 888888888888 848484848484 7b7b7b7b7b7b 858554545454 b7b713131313 a9a91d1d1d1d 8d8d4f4f4f4f 787875757575 777777777777 777777777777 777777777777 81816b6b6b6b aaaa41414141 d6d620202020 ecec10101010 e9e90c0c0c0c d0d012121212 a5a528282828 7b7b58585858 777777777777 777777777777 777777777777 707070707070 5c5c5c5c5c5c 525252525252 bdbdbdbdbdbd ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff eaeaeaeaeaea 848484848484 818181818181 858588888585 8e8e8e8e8e8e 898989898989 7f7f7f7f7f7f 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 767676767676 636363636363 545454545454 505050505050 4c4c4c4c4c4c e6e6e6e6e6e6 ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff f8f8f8f8f8f8 7f7f84847f7f 252597972525 0404a5a50404 3939a4a43939 8b8b94948b8b 939393939393 8f8f8f8f8f8f 838383838383 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7a7a7a7a7a7a 7a7a7a7a7a7a 797979797979 6a6a6a6a6a6a 575757575757 505050505050 4c4c4c4c4c4c 494949494949 595959595959 ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff 8a8a8a8a8a8a 0101b3b30101 0000c6c60001 0000f2f20000 5959b6b65959 929292929292 959595959595 979797979797 949494949494 878787878787 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 717171717171 5a5a5a5a6060 282828288585 040404049393 0c0c0c0c7878 282828285858 464646464a4a 828282828282 ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff 929292929292 0c0cabab0c0c 0000bdbd0001 0000f4f40000 2020dddd2020 919191919191 949494949494 979797979797 999999999999 9b9b9b9b9b9b 999999999999 8b8b8b8b8b8b 7f7f7f7f7f7f 7e7e7e7e7e7e 7e7e7e7e7e7e 7d7d7d7d7d7d 777777777777 626262626262 535353536060 12121212bebe 00010000cccc 000000009292 000000016969 000000006767 2a2a2a2a5555 acacacacacac ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff 949494949494 1616a1a11616 0000b4b40001 0000e2e20000 0000f4f40000 7676a2a27676 939393939393 8d8d97978d8d 46469e9e4646 4646a7a74646 8e8e9e9e8e8e 9e9e9e9e9e9e 9c9c9c9c9c9c 8e8e8e8e8e8e 7e7e7e7e7e7e 6a6a6a6a6a6a 5a5a5a5a5a5a 575757575a5a 18181818cdcd 00010000f0f0 00000000a0a0 020202026060 010101013d3d 000100006161 1d1d1d1d5959 d6d6d6d6d6d6 ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff a4a4a4a4a4a4 212198982121 0000aaaa0001 0000c8c80000 0000f4f40000 3b3bcaca3b3b 929292929292 4a4aacac4a4a 0001bcbc0000 0000a9a90000 2f2f9a9a2f2f 9d9d9d9d9d9d 9f9f9f9f9f9f a0a0a0a0a0a0 7a7a7a7a7a7a 5a5a5a5a5a5a 595959595959 31313131a1a1 00010000ffff 00000000c6c6 030303035b5b 191919192424 0c0c0c0c1515 0c0c0c0c5555 3b3b3b3b5353 fbfbfbfbfbfb ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff b6b6b6b6b6b6 2b2b8f8f2b2b 0000a2a20001 0000adad0000 0000ebeb0000 0707eded0707 898995958989 4343a7a74343 0001c9c90000 000099990000 383895953838 9c9c9c9c9c9c 9e9e9e9e9e9e 9f9f9f9f9f9f 747474747474 595959595959 505050506767 05050505f5f5 00010000f0f0 030303037070 383838384646 484848484848 161616163939 2b2b2b2b5555 727272727272 ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff c7c7c7c7c7c7 343486863434 0000b1b10001 00008d8d0000 0000d2d20000 0000f3f30000 4c4c9b9b4c4c 3b3b9e9e3b3b 0001c7c70000 000098980000 3d3d94943d3d 9b9b9b9b9b9b 9d9d9d9d9d9d 9e9e9e9e9e9e 6e6e6e6e6e6e 595959595959 2b2b2b2badad 00000001ffff 00000000a6a6 252525255959 434343434f4f 161616167e7e 000000019f9f 010101018e8e 9c9c9c9ca1a1 ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff d8d8d8d8d8d8 3e3e7d7d3e3e 0000b1b10001 00007b7b0000 0000b8b80000 0000f1f10000 17178b8b1717 3b3b9c9c3b3b 0001c6c60000 000097970000 3d3d93933d3d 9a9a9a9a9a9a 9b9b9b9b9b9b 9d9d9d9d9d9d 676767676767 575757575959 09090909eeee 00000001f0f0 040404046b6b 333333335a5a 070707079090 000000009e9e 000000017c7c 0d0d0d0d5d5d c7c7c7c7c7c7 ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff eaeaeaeaeaea 474774744747 0000adad0001 000085850000 090998980909 0000dcdc0000 0000a7a70000 232398982323 0001c3c30000 000096960000 3f3f92923f3f 989898989898 9a9a9a9a9a9a 9c9c9c9c9c9c 616161616161 424242427f7f 00010000ffff 00000001b9b9 1a1a1a1a5d5d 161616164949 000000007b7b 000000006b6b 000000016b6b 1c1c1c1c5656 f4f4f4f4f4f4 ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff fcfcfcfcfcfc 50506c6c5050 0000a9a90001 000095950000 2d2d77772d2d 0000c1c10000 0000c5c50000 010193930101 0001c1c10000 000090900000 4b4b91914b4b 979797979797 999999999999 9a9a9a9a9a9a 5a5a5a5a5a5a 2b2b2b2ba4a4 00010000f6f6 000000018686 2f2f2f2f5353 191919193030 020202026363 000000007373 000000019b9b 4d4d4d4d7070 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff 686873736868 0000a4a40001 0000a4a40000 3e3e65653e3e 1414a5a51414 0000d4d40000 00008b8b0001 0000bfbf0000 00008e8e0000 4a4a90904a4a 959595959595 979797979797 969696969696 575757575757 1a1a1a1ab5b5 00010000dede 000000016868 3f3f3f3f4b4b 2b2b2b2b2b2b 0c0c0c0c6d6d 00000000b3b3 000000016b6b 868686869292 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff 8c8c8c8c8c8c 05059e9e0505 0001b0b00000 343466663434 404085854040 0000caca0000 000097970001 0000bcbc0000 00008c8c0000 49498e8e4949 939393939393 959595959595 8f8f8f8f8f8f 565656565656 0f0f0f0fb7b7 00010000b9b9 030303036666 474747474747 2f2f2f2f6464 00010000a2a2 000000009d9d 090909095858 c5c5c5c5c5c5 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff fafafafafafa 9090b0b09090 343485853434 616164646161 63636a6a6363 0606afaf0606 0000aeae0001 0000b9b90000 00008b8b0000 53538d8d5353 919191919191 939393939393 898989898989 555555555555 0a0a0a0aa8a8 000100009d9d 070707076363 343434345c5c 040404049b9b 00010000b1b1 1a1a1a1a4d4d b5b5b5b5bbbb ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff d0d0d0d0d0d0 6d6d6d6d6d6d 656565656565 2d2d8f8f2d2d 0000b2b20001 0000b6b60000 000089890000 55558b8b5555 8f8f8f8f8f8f 919191919191 818181818181 555555555555 151515157e7e 000100008484 010101016565 010101018484 000100009191 1c1c1c1c6e6e cecececed0d0 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ecececececec 868686868686 585870705858 0000afaf0001 0000b3b30000 000088880000 535389895353 8d8d8d8d8d8d 8f8f8f8f8f8f 7a7a7a7a7a7a 545454545454 2c2c2c2c4949 020202026b6b 000000016464 000000006363 292929297474 dfdfdfdfe5e5 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff fcfcfcfcfcfc aaaaaaaaaaaa 212198982121 0001b0b00000 000086860000 575787875757 8b8b8b8b8b8b 8d8d8d8d8d8d 747474747474 535353535353 3d3d3d3d3d3d 1a1a1a1a2323 0d0d0d0d4343 474747477272 ededededefef ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff d1d1d6d6d1d1 38389b9b3838 2d2d77772d2d 7d7d81817d7d 888888888888 8b8b8b8b8b8b 6d6d6d6d6d6d 525252525252 4f4f4f4f4f4f 373737373737 777777777777 fafafafafafa ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff efefefefefef a0a0a0a0a0a0 838383838383 868686868686 888888888888 676767676767 515151515151 505050505050 a0a0a0a0a0a0 fdfdfdfdfdfd ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff fefefefefefe c0c0c0c0c0c0 858585858585 868686868686 616161616161 525252525252 b7b7b7b7b7b7 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff dededededede 909090909090 656565656565 cccccccccccc ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff f5f5f5f5f5f5 e3e3e3e3e3e3 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+}
diff --git a/src/image/png/testdata/pngsuite/ftbbn3p08.png b/src/image/png/testdata/pngsuite/ftbbn3p08.png
new file mode 100644
index 0000000..0ede357
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbbn3p08.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbbn3p08.sng b/src/image/png/testdata/pngsuite/ftbbn3p08.sng
new file mode 100644
index 0000000..429d99b
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbbn3p08.sng
@@ -0,0 +1,292 @@
+#SNG: from ftbbn3p08.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using color palette;
+}
+gAMA {1.0000}
+PLTE {
+    (255,255,255)     # rgb = (0xff,0xff,0xff) grey100
+    (128, 86, 86)     # rgb = (0x80,0x56,0x56)
+    (181,181,184)     # rgb = (0xb5,0xb5,0xb8)
+    (168, 66, 66)     # rgb = (0xa8,0x42,0x42)
+    (159,159,159)     # rgb = (0x9f,0x9f,0x9f)
+    (177, 32, 32)     # rgb = (0xb1,0x20,0x20)
+    (139, 21, 21)     # rgb = (0x8b,0x15,0x15)
+    (157,157,157)     # rgb = (0x9d,0x9d,0x9d)
+    ( 27, 27, 89)     # rgb = (0x1b,0x1b,0x59)
+    (155,155,155)     # rgb = (0x9b,0x9b,0x9b)
+    (  0,  0,132)     # rgb = (0x00,0x00,0x84)
+    (153,153,153)     # rgb = (0x99,0x99,0x99) grey60
+    (143,167,143)     # rgb = (0x8f,0xa7,0x8f)
+    (151,151,151)     # rgb = (0x97,0x97,0x97)
+    (149,149,149)     # rgb = (0x95,0x95,0x95)
+    (147,147,147)     # rgb = (0x93,0x93,0x93)
+    ( 41, 41, 86)     # rgb = (0x29,0x29,0x56)
+    (145,145,145)     # rgb = (0x91,0x91,0x91) grey57
+    (  0,  0,155)     # rgb = (0x00,0x00,0x9b)
+    (143,143,143)     # rgb = (0x8f,0x8f,0x8f) grey56
+    (139,149,139)     # rgb = (0x8b,0x95,0x8b)
+    ( 46, 46,167)     # rgb = (0x2e,0x2e,0xa7)
+    (141,141,141)     # rgb = (0x8d,0x8d,0x8d)
+    (128,  0,  0)     # rgb = (0x80,0x00,0x00)
+    (139,139,139)     # rgb = (0x8b,0x8b,0x8b)
+    (185,  0,  0)     # rgb = (0xb9,0x00,0x00)
+    (137,137,137)     # rgb = (0x89,0x89,0x89)
+    ( 12, 12,213)     # rgb = (0x0c,0x0c,0xd5)
+    (120,117,117)     # rgb = (0x78,0x75,0x75)
+    (135,135,135)     # rgb = (0x87,0x87,0x87) grey53
+    (  0,  0,178)     # rgb = (0x00,0x00,0xb2)
+    (133,133,133)     # rgb = (0x85,0x85,0x85) grey52
+    (165,  0,  0)     # rgb = (0xa5,0x00,0x00)
+    (222,  0,  0)     # rgb = (0xde,0x00,0x00)
+    (129,129,129)     # rgb = (0x81,0x81,0x81)
+    (127,127,127)     # rgb = (0x7f,0x7f,0x7f) grey50
+    (  0,  0,158)     # rgb = (0x00,0x00,0x9e)
+    (125,125,125)     # rgb = (0x7d,0x7d,0x7d) grey49
+    (  0,  0,201)     # rgb = (0x00,0x00,0xc9)
+    (123,123,123)     # rgb = (0x7b,0x7b,0x7b)
+    (121,121,121)     # rgb = (0x79,0x79,0x79)
+    ( 55, 55, 86)     # rgb = (0x37,0x37,0x56)
+    (119,119,119)     # rgb = (0x77,0x77,0x77)
+    (117,117,117)     # rgb = (0x75,0x75,0x75) grey46
+    (115,115,115)     # rgb = (0x73,0x73,0x73) grey45
+    ( 72,169, 72)     # rgb = (0x48,0xa9,0x48)
+    (142,  0,  0)     # rgb = (0x8e,0x00,0x00)
+    (  2,  2,100)     # rgb = (0x02,0x02,0x64)
+    (  0,  0, 98)     # rgb = (0x00,0x00,0x62)
+    ( 86,137, 86)     # rgb = (0x56,0x89,0x56)
+    ( 40, 40,124)     # rgb = (0x28,0x28,0x7c)
+    ( 83,139, 83)     # rgb = (0x53,0x8b,0x53)
+    (137,137,143)     # rgb = (0x89,0x89,0x8f)
+    (103,103,103)     # rgb = (0x67,0x67,0x67)
+    (101,101,101)     # rgb = (0x65,0x65,0x65)
+    ( 93,109, 93)     # rgb = (0x5d,0x6d,0x5d)
+    ( 19,229, 19)     # rgb = (0x13,0xe5,0x13)
+    (134, 38, 38)     # rgb = (0x86,0x26,0x26)
+    (111, 45, 45)     # rgb = (0x6f,0x2d,0x2d)
+    ( 68,145, 68)     # rgb = (0x44,0x91,0x44)
+    ( 97, 97, 97)     # rgb = (0x61,0x61,0x61) grey38
+    ( 59,157, 59)     # rgb = (0x3b,0x9d,0x3b)
+    ( 68,137, 68)     # rgb = (0x44,0x89,0x44)
+    ( 61,147, 61)     # rgb = (0x3d,0x93,0x3d)
+    (  0,  0,164)     # rgb = (0x00,0x00,0xa4)
+    (  0,243,  0)     # rgb = (0x00,0xf3,0x00)
+    (  0,241,  0)     # rgb = (0x00,0xf1,0x00)
+    ( 89, 89, 89)     # rgb = (0x59,0x59,0x59) grey35
+    ( 87, 87, 87)     # rgb = (0x57,0x57,0x57) grey34
+    ( 85, 85, 85)     # rgb = (0x55,0x55,0x55)
+    ( 83, 83, 83)     # rgb = (0x53,0x53,0x53)
+    ( 52,133, 52)     # rgb = (0x34,0x85,0x34)
+    ( 81, 81, 81)     # rgb = (0x51,0x51,0x51)
+    ( 36,151, 36)     # rgb = (0x24,0x97,0x24)
+    ( 79, 79, 79)     # rgb = (0x4f,0x4f,0x4f) grey31
+    ( 58, 58, 65)     # rgb = (0x3a,0x3a,0x41)
+    ( 16, 16,186)     # rgb = (0x10,0x10,0xba)
+    (178, 15, 15)     # rgb = (0xb2,0x0f,0x0f)
+    (  0,199,  0)     # rgb = (0x00,0xc7,0x00)
+    (  0,197,  0)     # rgb = (0x00,0xc5,0x00)
+    (252,252,252)     # rgb = (0xfc,0xfc,0xfc) grey99
+    (  0,195,  0)     # rgb = (0x00,0xc3,0x00)
+    (  4,  4,151)     # rgb = (0x04,0x04,0x97)
+    (  0,193,  0)     # rgb = (0x00,0xc1,0x00)
+    ( 45,119, 45)     # rgb = (0x2d,0x77,0x2d)
+    (250,250,250)     # rgb = (0xfa,0xfa,0xfa) grey98
+    (  0,191,  0)     # rgb = (0x00,0xbf,0x00)
+    (  0,  0,104)     # rgb = (0x00,0x00,0x68)
+    (  0,189,  0)     # rgb = (0x00,0xbd,0x00)
+    (218,212,212)     # rgb = (0xda,0xd4,0xd4)
+    ( 16, 16,123)     # rgb = (0x10,0x10,0x7b)
+    (  9,173,  9)     # rgb = (0x09,0xad,0x09)
+    (248,248,248)     # rgb = (0xf8,0xf8,0xf8)
+    (  0,185,  0)     # rgb = (0x00,0xb9,0x00)
+    (  0,183,  0)     # rgb = (0x00,0xb7,0x00)
+    (156,156,161)     # rgb = (0x9c,0x9c,0xa1)
+    (246,246,246)     # rgb = (0xf6,0xf6,0xf6)
+    ( 12,161, 12)     # rgb = (0x0c,0xa1,0x0c)
+    (  0,179,  0)     # rgb = (0x00,0xb3,0x00)
+    (  0,177,  0)     # rgb = (0x00,0xb1,0x00)
+    ( 16,145, 16)     # rgb = (0x10,0x91,0x10)
+    (  0,171,  0)     # rgb = (0x00,0xab,0x00)
+    (242,242,242)     # rgb = (0xf2,0xf2,0xf2) grey95
+    (  0,169,  0)     # rgb = (0x00,0xa9,0x00)
+    (  0,167,  0)     # rgb = (0x00,0xa7,0x00)
+    (238,238,238)     # rgb = (0xee,0xee,0xee)
+    (236,236,236)     # rgb = (0xec,0xec,0xec)
+    (  0,151,  0)     # rgb = (0x00,0x97,0x00)
+    (234,234,234)     # rgb = (0xea,0xea,0xea)
+    (  0,  0,107)     # rgb = (0x00,0x00,0x6b)
+    (  0,141,  0)     # rgb = (0x00,0x8d,0x00)
+    (  0,139,  0)     # rgb = (0x00,0x8b,0x00) green4
+    (  0,137,  0)     # rgb = (0x00,0x89,0x00)
+    (  0,135,  0)     # rgb = (0x00,0x87,0x00)
+    ( 49, 49, 49)     # rgb = (0x31,0x31,0x31)
+    ( 25, 25, 42)     # rgb = (0x19,0x19,0x2a)
+    (  7,  7, 64)     # rgb = (0x07,0x07,0x40)
+    ( 18, 18,174)     # rgb = (0x12,0x12,0xae)
+    (  9,  9,238)     # rgb = (0x09,0x09,0xee)
+    (211,214,211)     # rgb = (0xd3,0xd6,0xd3)
+    (204,204,204)     # rgb = (0xcc,0xcc,0xcc) grey80
+    (147,  0,  0)     # rgb = (0x93,0x00,0x00)
+    (163, 42, 42)     # rgb = (0xa3,0x2a,0x2a)
+    (198,198,198)     # rgb = (0xc6,0xc6,0xc6)
+    (196,196,196)     # rgb = (0xc4,0xc4,0xc4) grey77
+    (204,  0,  0)     # rgb = (0xcc,0x00,0x00)
+    (211, 10, 10)     # rgb = (0xd3,0x0a,0x0a)
+    (129,107,107)     # rgb = (0x81,0x6b,0x6b)
+    (120, 62, 62)     # rgb = (0x78,0x3e,0x3e)
+    (  3,  3,109)     # rgb = (0x03,0x03,0x6d)
+    (  0,  0,159)     # rgb = (0x00,0x00,0x9f)
+    ( 10, 10, 86)     # rgb = (0x0a,0x0a,0x56)
+    ( 70, 70, 72)     # rgb = (0x46,0x46,0x48)
+    ( 65, 65, 77)     # rgb = (0x41,0x41,0x4d)
+    (115, 93, 93)     # rgb = (0x73,0x5d,0x5d)
+    ( 81,  7,  7)     # rgb = (0x51,0x07,0x07)
+    (168,168,168)     # rgb = (0xa8,0xa8,0xa8) grey66
+    (237,237,239)     # rgb = (0xed,0xed,0xef)
+    (160,160,160)     # rgb = (0xa0,0xa0,0xa0)
+    (158,158,158)     # rgb = (0x9e,0x9e,0x9e) grey62
+    (156,156,156)     # rgb = (0x9c,0x9c,0x9c) grey61
+    (  0,  0,185)     # rgb = (0x00,0x00,0xb9)
+    (154,154,154)     # rgb = (0x9a,0x9a,0x9a)
+    (178,  0,  0)     # rgb = (0xb2,0x00,0x00)
+    (152,152,152)     # rgb = (0x98,0x98,0x98)
+    (235,  0,  0)     # rgb = (0xeb,0x00,0x00)
+    (150,150,150)     # rgb = (0x96,0x96,0x96) grey59
+    (158,  0,  0)     # rgb = (0x9e,0x00,0x00)
+    (148,148,148)     # rgb = (0x94,0x94,0x94) grey58
+    ( 19, 19, 28)     # rgb = (0x13,0x13,0x1c)
+    (146,146,146)     # rgb = (0x92,0x92,0x92)
+    (144,144,144)     # rgb = (0x90,0x90,0x90)
+    (142,142,142)     # rgb = (0x8e,0x8e,0x8e)
+    (  0,  0,145)     # rgb = (0x00,0x00,0x91)
+    (138,138,138)     # rgb = (0x8a,0x8a,0x8a) grey54
+    (136,136,136)     # rgb = (0x88,0x88,0x88)
+    (118,162,118)     # rgb = (0x76,0xa2,0x76)
+    (133,136,133)     # rgb = (0x85,0x88,0x85)
+    (134,134,134)     # rgb = (0x86,0x86,0x86)
+    (132,132,132)     # rgb = (0x84,0x84,0x84)
+    (120, 15, 15)     # rgb = (0x78,0x0f,0x0f)
+    (130,130,130)     # rgb = (0x82,0x82,0x82) grey51
+    (126,130,126)     # rgb = (0x7e,0x82,0x7e)
+    (126,126,126)     # rgb = (0x7e,0x7e,0x7e)
+    (124,124,124)     # rgb = (0x7c,0x7c,0x7c)
+    (122,122,122)     # rgb = (0x7a,0x7a,0x7a) grey48
+    ( 74,192, 74)     # rgb = (0x4a,0xc0,0x4a)
+    (118,118,118)     # rgb = (0x76,0x76,0x76)
+    (116,116,116)     # rgb = (0x74,0x74,0x74)
+    (114,114,114)     # rgb = (0x72,0x72,0x72)
+    (112,112,112)     # rgb = (0x70,0x70,0x70) grey44
+    (152,  0,  0)     # rgb = (0x98,0x00,0x00)
+    (110,110,110)     # rgb = (0x6e,0x6e,0x6e) grey43
+    (106,112,106)     # rgb = (0x6a,0x70,0x6a)
+    (122,102,102)     # rgb = (0x7a,0x66,0x66)
+    (106,106,106)     # rgb = (0x6a,0x6a,0x6a)
+    (132,  0,  0)     # rgb = (0x84,0x00,0x00)
+    ( 68,162, 68)     # rgb = (0x44,0xa2,0x44)
+    ( 75,150, 75)     # rgb = (0x4b,0x96,0x4b)
+    ( 97,100, 97)     # rgb = (0x61,0x64,0x61)
+    ( 98, 98, 98)     # rgb = (0x62,0x62,0x62)
+    (  0,244,  0)     # rgb = (0x00,0xf4,0x00)
+    ( 56,152, 56)     # rgb = (0x38,0x98,0x38)
+    ( 92, 92, 92)     # rgb = (0x5c,0x5c,0x5c) grey36
+    ( 90, 90, 90)     # rgb = (0x5a,0x5a,0x5a)
+    (  0,230,  0)     # rgb = (0x00,0xe6,0x00)
+    (  2,  2, 93)     # rgb = (0x02,0x02,0x5d)
+    ( 66,120, 66)     # rgb = (0x42,0x78,0x42)
+    ( 86, 86, 86)     # rgb = (0x56,0x56,0x56)
+    (  0,  0,240)     # rgb = (0x00,0x00,0xf0)
+    ( 46,148, 46)     # rgb = (0x2e,0x94,0x2e)
+    ( 71,104, 71)     # rgb = (0x47,0x68,0x47)
+    ( 49, 49, 96)     # rgb = (0x31,0x31,0x60)
+    (  0,216,  0)     # rgb = (0x00,0xd8,0x00)
+    ( 82, 82, 82)     # rgb = (0x52,0x52,0x52) grey32
+    ( 80, 80, 80)     # rgb = (0x50,0x50,0x50)
+    (  0,206,  0)     # rgb = (0x00,0xce,0x00)
+    ( 33,152, 33)     # rgb = (0x21,0x98,0x21)
+    ( 20, 20,109)     # rgb = (0x14,0x14,0x6d)
+    (  0,200,  0)     # rgb = (0x00,0xc8,0x00)
+    ( 76, 76, 76)     # rgb = (0x4c,0x4c,0x4c)
+    (253,253,253)     # rgb = (0xfd,0xfd,0xfd)
+    (  0,198,  0)     # rgb = (0x00,0xc6,0x00)
+    (  0,  0,157)     # rgb = (0x00,0x00,0x9d)
+    (111,107,107)     # rgb = (0x6f,0x6b,0x6b)
+    (234, 14, 14)     # rgb = (0xea,0x0e,0x0e)
+    ( 72, 72, 72)     # rgb = (0x48,0x48,0x48)
+    (  0,188,  0)     # rgb = (0x00,0xbc,0x00)
+    ( 52,102, 52)     # rgb = (0x34,0x66,0x34)
+    (  2,  2,245)     # rgb = (0x02,0x02,0xf5)
+    ( 83, 83, 96)     # rgb = (0x53,0x53,0x60)
+    (  0,176,  0)     # rgb = (0x00,0xb0,0x00)
+    (  0,174,  0)     # rgb = (0x00,0xae,0x00)
+    (183,  0,  0)     # rgb = (0xb7,0x00,0x00)
+    (  0,164,  0)     # rgb = (0x00,0xa4,0x00)
+    (239,239,239)     # rgb = (0xef,0xef,0xef)
+    (  0,162,  0)     # rgb = (0x00,0xa2,0x00)
+    (143, 79, 79)     # rgb = (0x8f,0x4f,0x4f)
+    (149, 52, 52)     # rgb = (0x95,0x34,0x34)
+    (  0,152,  0)     # rgb = (0x00,0x98,0x00)
+    (  0,150,  0)     # rgb = (0x00,0x96,0x00)
+    (  0,146,  0)     # rgb = (0x00,0x92,0x00)
+    (231,231,231)     # rgb = (0xe7,0xe7,0xe7)
+    (  0,140,  0)     # rgb = (0x00,0x8c,0x00)
+    (227,227,227)     # rgb = (0xe3,0xe3,0xe3) grey89
+    (  0,128,  0)     # rgb = (0x00,0x80,0x00)
+    (146,  6,  6)     # rgb = (0x92,0x06,0x06)
+    (  1,  1,111)     # rgb = (0x01,0x01,0x6f)
+    (100, 86, 89)     # rgb = (0x64,0x56,0x59)
+    (  0,  0,100)     # rgb = (0x00,0x00,0x64)
+    ( 78, 78,107)     # rgb = (0x4e,0x4e,0x6b)
+    (207,207,207)     # rgb = (0xcf,0xcf,0xcf) grey81
+    (221,221,224)     # rgb = (0xdd,0xdd,0xe0)
+    (  0,  0,123)     # rgb = (0x00,0x00,0x7b)
+    (201,201,201)     # rgb = (0xc9,0xc9,0xc9) grey79
+    ( 22, 22, 65)     # rgb = (0x16,0x16,0x41)
+    ( 33, 33, 89)     # rgb = (0x21,0x21,0x59)
+    ( 87, 87, 89)     # rgb = (0x57,0x57,0x59)
+    ( 68, 68,120)     # rgb = (0x44,0x44,0x78)
+    (191,191,191)     # rgb = (0xbf,0xbf,0xbf) grey75
+    (235,221,221)     # rgb = (0xeb,0xdd,0xdd)
+    ( 45, 45, 84)     # rgb = (0x2d,0x2d,0x54)
+    ( 10, 10, 96)     # rgb = (0x0a,0x0a,0x60)
+    (  0,  0,255)     # rgb = (0x00,0x00,0xff) blue1
+    (191,125,125)     # rgb = (0xbf,0x7d,0x7d)
+    (  0,  0,  0)     # rgb = (0x00,0x00,0x00) grey0
+}
+bKGD {index: 245}
+tRNS {
+ 0}
+IMAGE {
+    pixels hex
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000e0ea66000000000000000000000000000000
+0000000000000000000000de02a336e43903f4f0000000000000000000000000
+000000000000000069ef1a358680062eb017b0ab7af459500000000000000000
+0000000000667c0ea9cc803979937917a03a878787b0e2ae8ae75c0000000000
+00005cea8ea72c8639e293208f7d7d19200639a017ab2ee4ac2ca7097c690000
+00007823a72b2bda198fd54ddad90521219191217d1917cc2b2b2b2baf8e0000
+0000e81f9b9f27014d05d91c2a2a2a7f037ecdcd7e7a012a2a2aaab7c2ef0000
+00006c9f229d981a23282828282828282828282828282828a7b445c3c8de0000
+00005ca249d63d140f139f272727272727272727a5a528af44c3c8ce43000000
+0000009a62ca41a6960e0d941da4a4a4a4a4a4a4a4a9b732525a1084a1000000
+000000965b58b53811940d0b090b1823a3a3252ab4d24c269957571088000000
+000000946162b9b59c0f14b12d0c8b8c98a3afb8ed1bbd82ba74300877000000
+00000088c565c7b5a6962dcf67be07048aa5b84315f326ba7395832950000000
+00000002bed8d4b94214b1c7dbb68c8b04a843e6d1bd814bceeb10a900000000
+0000007b47636ec441b23d4edb3f09078bac4315f340ec855a82995f00000000
+00000059bb63e15d42643dca6b3f8e090735ed76bd81c05224e9f27b00000000
+0000006cbbd47161c1684951dc3f908e8c3ceef38d08ebe96d6d086000000000
+00000050bf67dc54534fdd53ddb20d0b8eb815d10af1732fe312e60000000000
+00000000add6d6bf61c16f566eb20e0d924475bd578572c61e6d340000000000
+0000000016d8d3d03ec76bcfdf3b0f0e13bc4c8d2f84c040cb837b0000000000
+00000000550c47b3365bd45d6f33110f1a4575cbf2c0521e0802000000000000
+000000000000e7ac36be625e7031131122455a0a2f0a99c6e700000000000000
+000000000000006a9e37d36270331613a545f181e53032e80000000000000000
+00000000000000005088c5d371311816a8464b7374ee89000000000000000000
+0000000000000000000077b654a29b18acc24a722a5500000000000000000000
+0000000000000000000000d78a9f9e9b3548c38ac90000000000000000000000
+00000000000000000000000000ef1f9e3cc20200000000000000000000000000
+0000000000000000000000000000e89736780000000000000000000000000000
+00000000000000000000000000000060e0000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+}
diff --git a/src/image/png/testdata/pngsuite/ftbgn2c16.png b/src/image/png/testdata/pngsuite/ftbgn2c16.png
new file mode 100644
index 0000000..85cec39
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbgn2c16.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbgn2c16.sng b/src/image/png/testdata/pngsuite/ftbgn2c16.sng
new file mode 100644
index 0000000..0f5621d
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbgn2c16.sng
@@ -0,0 +1,45 @@
+#SNG: from ftbgn2c16.png
+IHDR {
+    width: 32; height: 32; bitdepth: 16;
+    using color;
+}
+gAMA {1.0000}
+bKGD {red: 0;  green: 65535;  blue: 0;}
+tRNS {
+    red: 65535; green: 65535; blue: 65535;
+}
+IMAGE {
+    pixels hex
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff e3e3e3e3e3e3 c9c9c9c9c9c9 f1f1f1f1f1f1 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff e8e8e8e8e8e8 b5b5b5b5b5b5 7e7e7e7e7e7e 656565656565 6e6e52525252 7e7e2e2e2e2e a6a643434343 c7c790909090 ebebdddddddd ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff eeeeeeeeeeee bfbfbfbfbfbf 898989898989 676767676767 6b6b5d5d5d5d 7a7a39393939 8a8a12121212 8d8d00010000 858500000000 777700000000 848400000000 9a9a01010101 a2a22d2d2d2d bfbf7d7d7d7d ddddd0d0d0d0 fcfcfcfcfcfc ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff f2f2f2f2f2f2 c4c4c4c4c4c4 959595959595 727272727272 6f6f6b6b6b6b 777744444444 87871e1e1e1e 959501010101 9f9f00010000 919100000000 808000010000 72720c0c0c0c 61612d2d2d2d 53530e0e0e0e 505000000000 595900010000 858500000000 929206060606 7a7a66666666 a0a0a0a0a0a0 cfcfcfcfcfcf f8f8f8f8f8f8 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff f7f7f7f7f7f7 cacacacacaca 9a9a9a9a9a9a 767676767676 737373737373 7c7c5d5d5d5d 87872e2e2e2e 939307070707 9e9e00010000 a9a900000000 b0b000000000 c9c900000000 cfcf00000000 b9b900010000 a2a201010101 8c8c19191919 85852a2a2a2a 7f7f13131313 818100010000 969600000000 8f8f00000000 6b6b53535353 6e6e6e6e6e6e 737373737373 767676767676 9b9b9b9b9b9b c4c4c4c4c4c4 eeeeeeeeeeee ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff cccccccccccc 7f7f7f7f7f7f 767676767676 757575757575 757575757575 96962f2f2f2f b8b800010000 b4b400000000 b6b600010000 adad0c0c0c0c 94943a3a3a3a 929250505050 b9b923232323 d6d602020202 e2e200010000 efef00000000 e7e700000000 dada00000000 cfcf00010000 baba00000000 7d7d01010101 6f6f6b6b6b6b 757575757575 757575757575 757575757575 757575757575 6a6a6a6a6a6a 9a9a9a9a9a9a ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff dcdcdcdcdcdc 858585858585 888888888888 848484848484 7b7b7b7b7b7b 858554545454 b7b713131313 a9a91d1d1d1d 8d8d4f4f4f4f 787875757575 777777777777 777777777777 777777777777 81816b6b6b6b aaaa41414141 d6d620202020 ecec10101010 e9e90c0c0c0c d0d012121212 a5a528282828 7b7b58585858 777777777777 777777777777 777777777777 707070707070 5c5c5c5c5c5c 525252525252 bdbdbdbdbdbd ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff eaeaeaeaeaea 848484848484 818181818181 858588888585 8e8e8e8e8e8e 898989898989 7f7f7f7f7f7f 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 797979797979 767676767676 636363636363 545454545454 505050505050 4c4c4c4c4c4c e6e6e6e6e6e6 ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff f8f8f8f8f8f8 7f7f84847f7f 252597972525 0404a5a50404 3939a4a43939 8b8b94948b8b 939393939393 8f8f8f8f8f8f 838383838383 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7b7b7b7b7b7b 7a7a7a7a7a7a 7a7a7a7a7a7a 797979797979 6a6a6a6a6a6a 575757575757 505050505050 4c4c4c4c4c4c 494949494949 595959595959 ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff 8a8a8a8a8a8a 0101b3b30101 0000c6c60001 0000f2f20000 5959b6b65959 929292929292 959595959595 979797979797 949494949494 878787878787 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 7c7c7c7c7c7c 717171717171 5a5a5a5a6060 282828288585 040404049393 0c0c0c0c7878 282828285858 464646464a4a 828282828282 ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff 929292929292 0c0cabab0c0c 0000bdbd0001 0000f4f40000 2020dddd2020 919191919191 949494949494 979797979797 999999999999 9b9b9b9b9b9b 999999999999 8b8b8b8b8b8b 7f7f7f7f7f7f 7e7e7e7e7e7e 7e7e7e7e7e7e 7d7d7d7d7d7d 777777777777 626262626262 535353536060 12121212bebe 00010000cccc 000000009292 000000016969 000000006767 2a2a2a2a5555 acacacacacac ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff 949494949494 1616a1a11616 0000b4b40001 0000e2e20000 0000f4f40000 7676a2a27676 939393939393 8d8d97978d8d 46469e9e4646 4646a7a74646 8e8e9e9e8e8e 9e9e9e9e9e9e 9c9c9c9c9c9c 8e8e8e8e8e8e 7e7e7e7e7e7e 6a6a6a6a6a6a 5a5a5a5a5a5a 575757575a5a 18181818cdcd 00010000f0f0 00000000a0a0 020202026060 010101013d3d 000100006161 1d1d1d1d5959 d6d6d6d6d6d6 ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff a4a4a4a4a4a4 212198982121 0000aaaa0001 0000c8c80000 0000f4f40000 3b3bcaca3b3b 929292929292 4a4aacac4a4a 0001bcbc0000 0000a9a90000 2f2f9a9a2f2f 9d9d9d9d9d9d 9f9f9f9f9f9f a0a0a0a0a0a0 7a7a7a7a7a7a 5a5a5a5a5a5a 595959595959 31313131a1a1 00010000ffff 00000000c6c6 030303035b5b 191919192424 0c0c0c0c1515 0c0c0c0c5555 3b3b3b3b5353 fbfbfbfbfbfb ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff b6b6b6b6b6b6 2b2b8f8f2b2b 0000a2a20001 0000adad0000 0000ebeb0000 0707eded0707 898995958989 4343a7a74343 0001c9c90000 000099990000 383895953838 9c9c9c9c9c9c 9e9e9e9e9e9e 9f9f9f9f9f9f 747474747474 595959595959 505050506767 05050505f5f5 00010000f0f0 030303037070 383838384646 484848484848 161616163939 2b2b2b2b5555 727272727272 ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff c7c7c7c7c7c7 343486863434 0000b1b10001 00008d8d0000 0000d2d20000 0000f3f30000 4c4c9b9b4c4c 3b3b9e9e3b3b 0001c7c70000 000098980000 3d3d94943d3d 9b9b9b9b9b9b 9d9d9d9d9d9d 9e9e9e9e9e9e 6e6e6e6e6e6e 595959595959 2b2b2b2badad 00000001ffff 00000000a6a6 252525255959 434343434f4f 161616167e7e 000000019f9f 010101018e8e 9c9c9c9ca1a1 ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff d8d8d8d8d8d8 3e3e7d7d3e3e 0000b1b10001 00007b7b0000 0000b8b80000 0000f1f10000 17178b8b1717 3b3b9c9c3b3b 0001c6c60000 000097970000 3d3d93933d3d 9a9a9a9a9a9a 9b9b9b9b9b9b 9d9d9d9d9d9d 676767676767 575757575959 09090909eeee 00000001f0f0 040404046b6b 333333335a5a 070707079090 000000009e9e 000000017c7c 0d0d0d0d5d5d c7c7c7c7c7c7 ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff eaeaeaeaeaea 474774744747 0000adad0001 000085850000 090998980909 0000dcdc0000 0000a7a70000 232398982323 0001c3c30000 000096960000 3f3f92923f3f 989898989898 9a9a9a9a9a9a 9c9c9c9c9c9c 616161616161 424242427f7f 00010000ffff 00000001b9b9 1a1a1a1a5d5d 161616164949 000000007b7b 000000006b6b 000000016b6b 1c1c1c1c5656 f4f4f4f4f4f4 ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff fcfcfcfcfcfc 50506c6c5050 0000a9a90001 000095950000 2d2d77772d2d 0000c1c10000 0000c5c50000 010193930101 0001c1c10000 000090900000 4b4b91914b4b 979797979797 999999999999 9a9a9a9a9a9a 5a5a5a5a5a5a 2b2b2b2ba4a4 00010000f6f6 000000018686 2f2f2f2f5353 191919193030 020202026363 000000007373 000000019b9b 4d4d4d4d7070 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff 686873736868 0000a4a40001 0000a4a40000 3e3e65653e3e 1414a5a51414 0000d4d40000 00008b8b0001 0000bfbf0000 00008e8e0000 4a4a90904a4a 959595959595 979797979797 969696969696 575757575757 1a1a1a1ab5b5 00010000dede 000000016868 3f3f3f3f4b4b 2b2b2b2b2b2b 0c0c0c0c6d6d 00000000b3b3 000000016b6b 868686869292 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff 8c8c8c8c8c8c 05059e9e0505 0001b0b00000 343466663434 404085854040 0000caca0000 000097970001 0000bcbc0000 00008c8c0000 49498e8e4949 939393939393 959595959595 8f8f8f8f8f8f 565656565656 0f0f0f0fb7b7 00010000b9b9 030303036666 474747474747 2f2f2f2f6464 00010000a2a2 000000009d9d 090909095858 c5c5c5c5c5c5 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff fafafafafafa 9090b0b09090 343485853434 616164646161 63636a6a6363 0606afaf0606 0000aeae0001 0000b9b90000 00008b8b0000 53538d8d5353 919191919191 939393939393 898989898989 555555555555 0a0a0a0aa8a8 000100009d9d 070707076363 343434345c5c 040404049b9b 00010000b1b1 1a1a1a1a4d4d b5b5b5b5bbbb ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff d0d0d0d0d0d0 6d6d6d6d6d6d 656565656565 2d2d8f8f2d2d 0000b2b20001 0000b6b60000 000089890000 55558b8b5555 8f8f8f8f8f8f 919191919191 818181818181 555555555555 151515157e7e 000100008484 010101016565 010101018484 000100009191 1c1c1c1c6e6e cecececed0d0 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ecececececec 868686868686 585870705858 0000afaf0001 0000b3b30000 000088880000 535389895353 8d8d8d8d8d8d 8f8f8f8f8f8f 7a7a7a7a7a7a 545454545454 2c2c2c2c4949 020202026b6b 000000016464 000000006363 292929297474 dfdfdfdfe5e5 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff fcfcfcfcfcfc aaaaaaaaaaaa 212198982121 0001b0b00000 000086860000 575787875757 8b8b8b8b8b8b 8d8d8d8d8d8d 747474747474 535353535353 3d3d3d3d3d3d 1a1a1a1a2323 0d0d0d0d4343 474747477272 ededededefef ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff d1d1d6d6d1d1 38389b9b3838 2d2d77772d2d 7d7d81817d7d 888888888888 8b8b8b8b8b8b 6d6d6d6d6d6d 525252525252 4f4f4f4f4f4f 373737373737 777777777777 fafafafafafa ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff efefefefefef a0a0a0a0a0a0 838383838383 868686868686 888888888888 676767676767 515151515151 505050505050 a0a0a0a0a0a0 fdfdfdfdfdfd ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff fefefefefefe c0c0c0c0c0c0 858585858585 868686868686 616161616161 525252525252 b7b7b7b7b7b7 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff dededededede 909090909090 656565656565 cccccccccccc ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff f5f5f5f5f5f5 e3e3e3e3e3e3 ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff ffffffffffff 
+}
diff --git a/src/image/png/testdata/pngsuite/ftbgn3p08.png b/src/image/png/testdata/pngsuite/ftbgn3p08.png
new file mode 100644
index 0000000..8cf2e6f
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbgn3p08.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbgn3p08.sng b/src/image/png/testdata/pngsuite/ftbgn3p08.sng
new file mode 100644
index 0000000..0e3b7bd
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbgn3p08.sng
@@ -0,0 +1,292 @@
+#SNG: from ftbgn3p08.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using color palette;
+}
+gAMA {1.0000}
+PLTE {
+    (255,255,255)     # rgb = (0xff,0xff,0xff) grey100
+    (128, 86, 86)     # rgb = (0x80,0x56,0x56)
+    (181,181,184)     # rgb = (0xb5,0xb5,0xb8)
+    (168, 66, 66)     # rgb = (0xa8,0x42,0x42)
+    (159,159,159)     # rgb = (0x9f,0x9f,0x9f)
+    (177, 32, 32)     # rgb = (0xb1,0x20,0x20)
+    (139, 21, 21)     # rgb = (0x8b,0x15,0x15)
+    (157,157,157)     # rgb = (0x9d,0x9d,0x9d)
+    ( 27, 27, 89)     # rgb = (0x1b,0x1b,0x59)
+    (155,155,155)     # rgb = (0x9b,0x9b,0x9b)
+    (  0,  0,132)     # rgb = (0x00,0x00,0x84)
+    (153,153,153)     # rgb = (0x99,0x99,0x99) grey60
+    (143,167,143)     # rgb = (0x8f,0xa7,0x8f)
+    (151,151,151)     # rgb = (0x97,0x97,0x97)
+    (149,149,149)     # rgb = (0x95,0x95,0x95)
+    (147,147,147)     # rgb = (0x93,0x93,0x93)
+    ( 41, 41, 86)     # rgb = (0x29,0x29,0x56)
+    (145,145,145)     # rgb = (0x91,0x91,0x91) grey57
+    (  0,  0,155)     # rgb = (0x00,0x00,0x9b)
+    (143,143,143)     # rgb = (0x8f,0x8f,0x8f) grey56
+    (139,149,139)     # rgb = (0x8b,0x95,0x8b)
+    ( 46, 46,167)     # rgb = (0x2e,0x2e,0xa7)
+    (141,141,141)     # rgb = (0x8d,0x8d,0x8d)
+    (128,  0,  0)     # rgb = (0x80,0x00,0x00)
+    (139,139,139)     # rgb = (0x8b,0x8b,0x8b)
+    (185,  0,  0)     # rgb = (0xb9,0x00,0x00)
+    (137,137,137)     # rgb = (0x89,0x89,0x89)
+    ( 12, 12,213)     # rgb = (0x0c,0x0c,0xd5)
+    (120,117,117)     # rgb = (0x78,0x75,0x75)
+    (135,135,135)     # rgb = (0x87,0x87,0x87) grey53
+    (  0,  0,178)     # rgb = (0x00,0x00,0xb2)
+    (133,133,133)     # rgb = (0x85,0x85,0x85) grey52
+    (165,  0,  0)     # rgb = (0xa5,0x00,0x00)
+    (222,  0,  0)     # rgb = (0xde,0x00,0x00)
+    (129,129,129)     # rgb = (0x81,0x81,0x81)
+    (127,127,127)     # rgb = (0x7f,0x7f,0x7f) grey50
+    (  0,  0,158)     # rgb = (0x00,0x00,0x9e)
+    (125,125,125)     # rgb = (0x7d,0x7d,0x7d) grey49
+    (  0,  0,201)     # rgb = (0x00,0x00,0xc9)
+    (123,123,123)     # rgb = (0x7b,0x7b,0x7b)
+    (121,121,121)     # rgb = (0x79,0x79,0x79)
+    ( 55, 55, 86)     # rgb = (0x37,0x37,0x56)
+    (119,119,119)     # rgb = (0x77,0x77,0x77)
+    (117,117,117)     # rgb = (0x75,0x75,0x75) grey46
+    (115,115,115)     # rgb = (0x73,0x73,0x73) grey45
+    ( 72,169, 72)     # rgb = (0x48,0xa9,0x48)
+    (142,  0,  0)     # rgb = (0x8e,0x00,0x00)
+    (  2,  2,100)     # rgb = (0x02,0x02,0x64)
+    (  0,  0, 98)     # rgb = (0x00,0x00,0x62)
+    ( 86,137, 86)     # rgb = (0x56,0x89,0x56)
+    ( 40, 40,124)     # rgb = (0x28,0x28,0x7c)
+    ( 83,139, 83)     # rgb = (0x53,0x8b,0x53)
+    (137,137,143)     # rgb = (0x89,0x89,0x8f)
+    (103,103,103)     # rgb = (0x67,0x67,0x67)
+    (101,101,101)     # rgb = (0x65,0x65,0x65)
+    ( 93,109, 93)     # rgb = (0x5d,0x6d,0x5d)
+    ( 19,229, 19)     # rgb = (0x13,0xe5,0x13)
+    (134, 38, 38)     # rgb = (0x86,0x26,0x26)
+    (111, 45, 45)     # rgb = (0x6f,0x2d,0x2d)
+    ( 68,145, 68)     # rgb = (0x44,0x91,0x44)
+    ( 97, 97, 97)     # rgb = (0x61,0x61,0x61) grey38
+    ( 59,157, 59)     # rgb = (0x3b,0x9d,0x3b)
+    ( 68,137, 68)     # rgb = (0x44,0x89,0x44)
+    ( 61,147, 61)     # rgb = (0x3d,0x93,0x3d)
+    (  0,  0,164)     # rgb = (0x00,0x00,0xa4)
+    (  0,243,  0)     # rgb = (0x00,0xf3,0x00)
+    (  0,241,  0)     # rgb = (0x00,0xf1,0x00)
+    ( 89, 89, 89)     # rgb = (0x59,0x59,0x59) grey35
+    ( 87, 87, 87)     # rgb = (0x57,0x57,0x57) grey34
+    ( 85, 85, 85)     # rgb = (0x55,0x55,0x55)
+    ( 83, 83, 83)     # rgb = (0x53,0x53,0x53)
+    ( 52,133, 52)     # rgb = (0x34,0x85,0x34)
+    ( 81, 81, 81)     # rgb = (0x51,0x51,0x51)
+    ( 36,151, 36)     # rgb = (0x24,0x97,0x24)
+    ( 79, 79, 79)     # rgb = (0x4f,0x4f,0x4f) grey31
+    ( 58, 58, 65)     # rgb = (0x3a,0x3a,0x41)
+    ( 16, 16,186)     # rgb = (0x10,0x10,0xba)
+    (178, 15, 15)     # rgb = (0xb2,0x0f,0x0f)
+    (  0,199,  0)     # rgb = (0x00,0xc7,0x00)
+    (  0,197,  0)     # rgb = (0x00,0xc5,0x00)
+    (252,252,252)     # rgb = (0xfc,0xfc,0xfc) grey99
+    (  0,195,  0)     # rgb = (0x00,0xc3,0x00)
+    (  4,  4,151)     # rgb = (0x04,0x04,0x97)
+    (  0,193,  0)     # rgb = (0x00,0xc1,0x00)
+    ( 45,119, 45)     # rgb = (0x2d,0x77,0x2d)
+    (250,250,250)     # rgb = (0xfa,0xfa,0xfa) grey98
+    (  0,191,  0)     # rgb = (0x00,0xbf,0x00)
+    (  0,  0,104)     # rgb = (0x00,0x00,0x68)
+    (  0,189,  0)     # rgb = (0x00,0xbd,0x00)
+    (218,212,212)     # rgb = (0xda,0xd4,0xd4)
+    ( 16, 16,123)     # rgb = (0x10,0x10,0x7b)
+    (  9,173,  9)     # rgb = (0x09,0xad,0x09)
+    (248,248,248)     # rgb = (0xf8,0xf8,0xf8)
+    (  0,185,  0)     # rgb = (0x00,0xb9,0x00)
+    (  0,183,  0)     # rgb = (0x00,0xb7,0x00)
+    (156,156,161)     # rgb = (0x9c,0x9c,0xa1)
+    (246,246,246)     # rgb = (0xf6,0xf6,0xf6)
+    ( 12,161, 12)     # rgb = (0x0c,0xa1,0x0c)
+    (  0,179,  0)     # rgb = (0x00,0xb3,0x00)
+    (  0,177,  0)     # rgb = (0x00,0xb1,0x00)
+    ( 16,145, 16)     # rgb = (0x10,0x91,0x10)
+    (  0,171,  0)     # rgb = (0x00,0xab,0x00)
+    (242,242,242)     # rgb = (0xf2,0xf2,0xf2) grey95
+    (  0,169,  0)     # rgb = (0x00,0xa9,0x00)
+    (  0,167,  0)     # rgb = (0x00,0xa7,0x00)
+    (238,238,238)     # rgb = (0xee,0xee,0xee)
+    (236,236,236)     # rgb = (0xec,0xec,0xec)
+    (  0,151,  0)     # rgb = (0x00,0x97,0x00)
+    (234,234,234)     # rgb = (0xea,0xea,0xea)
+    (  0,  0,107)     # rgb = (0x00,0x00,0x6b)
+    (  0,141,  0)     # rgb = (0x00,0x8d,0x00)
+    (  0,139,  0)     # rgb = (0x00,0x8b,0x00) green4
+    (  0,137,  0)     # rgb = (0x00,0x89,0x00)
+    (  0,135,  0)     # rgb = (0x00,0x87,0x00)
+    ( 49, 49, 49)     # rgb = (0x31,0x31,0x31)
+    ( 25, 25, 42)     # rgb = (0x19,0x19,0x2a)
+    (  7,  7, 64)     # rgb = (0x07,0x07,0x40)
+    ( 18, 18,174)     # rgb = (0x12,0x12,0xae)
+    (  9,  9,238)     # rgb = (0x09,0x09,0xee)
+    (211,214,211)     # rgb = (0xd3,0xd6,0xd3)
+    (204,204,204)     # rgb = (0xcc,0xcc,0xcc) grey80
+    (147,  0,  0)     # rgb = (0x93,0x00,0x00)
+    (163, 42, 42)     # rgb = (0xa3,0x2a,0x2a)
+    (198,198,198)     # rgb = (0xc6,0xc6,0xc6)
+    (196,196,196)     # rgb = (0xc4,0xc4,0xc4) grey77
+    (204,  0,  0)     # rgb = (0xcc,0x00,0x00)
+    (211, 10, 10)     # rgb = (0xd3,0x0a,0x0a)
+    (129,107,107)     # rgb = (0x81,0x6b,0x6b)
+    (120, 62, 62)     # rgb = (0x78,0x3e,0x3e)
+    (  3,  3,109)     # rgb = (0x03,0x03,0x6d)
+    (  0,  0,159)     # rgb = (0x00,0x00,0x9f)
+    ( 10, 10, 86)     # rgb = (0x0a,0x0a,0x56)
+    ( 70, 70, 72)     # rgb = (0x46,0x46,0x48)
+    ( 65, 65, 77)     # rgb = (0x41,0x41,0x4d)
+    (115, 93, 93)     # rgb = (0x73,0x5d,0x5d)
+    ( 81,  7,  7)     # rgb = (0x51,0x07,0x07)
+    (168,168,168)     # rgb = (0xa8,0xa8,0xa8) grey66
+    (237,237,239)     # rgb = (0xed,0xed,0xef)
+    (160,160,160)     # rgb = (0xa0,0xa0,0xa0)
+    (158,158,158)     # rgb = (0x9e,0x9e,0x9e) grey62
+    (156,156,156)     # rgb = (0x9c,0x9c,0x9c) grey61
+    (  0,  0,185)     # rgb = (0x00,0x00,0xb9)
+    (154,154,154)     # rgb = (0x9a,0x9a,0x9a)
+    (178,  0,  0)     # rgb = (0xb2,0x00,0x00)
+    (152,152,152)     # rgb = (0x98,0x98,0x98)
+    (235,  0,  0)     # rgb = (0xeb,0x00,0x00)
+    (150,150,150)     # rgb = (0x96,0x96,0x96) grey59
+    (158,  0,  0)     # rgb = (0x9e,0x00,0x00)
+    (148,148,148)     # rgb = (0x94,0x94,0x94) grey58
+    ( 19, 19, 28)     # rgb = (0x13,0x13,0x1c)
+    (146,146,146)     # rgb = (0x92,0x92,0x92)
+    (144,144,144)     # rgb = (0x90,0x90,0x90)
+    (142,142,142)     # rgb = (0x8e,0x8e,0x8e)
+    (  0,  0,145)     # rgb = (0x00,0x00,0x91)
+    (138,138,138)     # rgb = (0x8a,0x8a,0x8a) grey54
+    (136,136,136)     # rgb = (0x88,0x88,0x88)
+    (118,162,118)     # rgb = (0x76,0xa2,0x76)
+    (133,136,133)     # rgb = (0x85,0x88,0x85)
+    (134,134,134)     # rgb = (0x86,0x86,0x86)
+    (132,132,132)     # rgb = (0x84,0x84,0x84)
+    (120, 15, 15)     # rgb = (0x78,0x0f,0x0f)
+    (130,130,130)     # rgb = (0x82,0x82,0x82) grey51
+    (126,130,126)     # rgb = (0x7e,0x82,0x7e)
+    (126,126,126)     # rgb = (0x7e,0x7e,0x7e)
+    (124,124,124)     # rgb = (0x7c,0x7c,0x7c)
+    (122,122,122)     # rgb = (0x7a,0x7a,0x7a) grey48
+    ( 74,192, 74)     # rgb = (0x4a,0xc0,0x4a)
+    (118,118,118)     # rgb = (0x76,0x76,0x76)
+    (116,116,116)     # rgb = (0x74,0x74,0x74)
+    (114,114,114)     # rgb = (0x72,0x72,0x72)
+    (112,112,112)     # rgb = (0x70,0x70,0x70) grey44
+    (152,  0,  0)     # rgb = (0x98,0x00,0x00)
+    (110,110,110)     # rgb = (0x6e,0x6e,0x6e) grey43
+    (106,112,106)     # rgb = (0x6a,0x70,0x6a)
+    (122,102,102)     # rgb = (0x7a,0x66,0x66)
+    (106,106,106)     # rgb = (0x6a,0x6a,0x6a)
+    (132,  0,  0)     # rgb = (0x84,0x00,0x00)
+    ( 68,162, 68)     # rgb = (0x44,0xa2,0x44)
+    ( 75,150, 75)     # rgb = (0x4b,0x96,0x4b)
+    ( 97,100, 97)     # rgb = (0x61,0x64,0x61)
+    ( 98, 98, 98)     # rgb = (0x62,0x62,0x62)
+    (  0,244,  0)     # rgb = (0x00,0xf4,0x00)
+    ( 56,152, 56)     # rgb = (0x38,0x98,0x38)
+    ( 92, 92, 92)     # rgb = (0x5c,0x5c,0x5c) grey36
+    ( 90, 90, 90)     # rgb = (0x5a,0x5a,0x5a)
+    (  0,230,  0)     # rgb = (0x00,0xe6,0x00)
+    (  2,  2, 93)     # rgb = (0x02,0x02,0x5d)
+    ( 66,120, 66)     # rgb = (0x42,0x78,0x42)
+    ( 86, 86, 86)     # rgb = (0x56,0x56,0x56)
+    (  0,  0,240)     # rgb = (0x00,0x00,0xf0)
+    ( 46,148, 46)     # rgb = (0x2e,0x94,0x2e)
+    ( 71,104, 71)     # rgb = (0x47,0x68,0x47)
+    ( 49, 49, 96)     # rgb = (0x31,0x31,0x60)
+    (  0,216,  0)     # rgb = (0x00,0xd8,0x00)
+    ( 82, 82, 82)     # rgb = (0x52,0x52,0x52) grey32
+    ( 80, 80, 80)     # rgb = (0x50,0x50,0x50)
+    (  0,206,  0)     # rgb = (0x00,0xce,0x00)
+    ( 33,152, 33)     # rgb = (0x21,0x98,0x21)
+    ( 20, 20,109)     # rgb = (0x14,0x14,0x6d)
+    (  0,200,  0)     # rgb = (0x00,0xc8,0x00)
+    ( 76, 76, 76)     # rgb = (0x4c,0x4c,0x4c)
+    (253,253,253)     # rgb = (0xfd,0xfd,0xfd)
+    (  0,198,  0)     # rgb = (0x00,0xc6,0x00)
+    (  0,  0,157)     # rgb = (0x00,0x00,0x9d)
+    (111,107,107)     # rgb = (0x6f,0x6b,0x6b)
+    (234, 14, 14)     # rgb = (0xea,0x0e,0x0e)
+    ( 72, 72, 72)     # rgb = (0x48,0x48,0x48)
+    (  0,188,  0)     # rgb = (0x00,0xbc,0x00)
+    ( 52,102, 52)     # rgb = (0x34,0x66,0x34)
+    (  2,  2,245)     # rgb = (0x02,0x02,0xf5)
+    ( 83, 83, 96)     # rgb = (0x53,0x53,0x60)
+    (  0,176,  0)     # rgb = (0x00,0xb0,0x00)
+    (  0,174,  0)     # rgb = (0x00,0xae,0x00)
+    (183,  0,  0)     # rgb = (0xb7,0x00,0x00)
+    (  0,164,  0)     # rgb = (0x00,0xa4,0x00)
+    (239,239,239)     # rgb = (0xef,0xef,0xef)
+    (  0,162,  0)     # rgb = (0x00,0xa2,0x00)
+    (143, 79, 79)     # rgb = (0x8f,0x4f,0x4f)
+    (149, 52, 52)     # rgb = (0x95,0x34,0x34)
+    (  0,152,  0)     # rgb = (0x00,0x98,0x00)
+    (  0,150,  0)     # rgb = (0x00,0x96,0x00)
+    (  0,146,  0)     # rgb = (0x00,0x92,0x00)
+    (231,231,231)     # rgb = (0xe7,0xe7,0xe7)
+    (  0,140,  0)     # rgb = (0x00,0x8c,0x00)
+    (227,227,227)     # rgb = (0xe3,0xe3,0xe3) grey89
+    (  0,128,  0)     # rgb = (0x00,0x80,0x00)
+    (146,  6,  6)     # rgb = (0x92,0x06,0x06)
+    (  1,  1,111)     # rgb = (0x01,0x01,0x6f)
+    (100, 86, 89)     # rgb = (0x64,0x56,0x59)
+    (  0,  0,100)     # rgb = (0x00,0x00,0x64)
+    ( 78, 78,107)     # rgb = (0x4e,0x4e,0x6b)
+    (207,207,207)     # rgb = (0xcf,0xcf,0xcf) grey81
+    (221,221,224)     # rgb = (0xdd,0xdd,0xe0)
+    (  0,  0,123)     # rgb = (0x00,0x00,0x7b)
+    (201,201,201)     # rgb = (0xc9,0xc9,0xc9) grey79
+    ( 22, 22, 65)     # rgb = (0x16,0x16,0x41)
+    ( 33, 33, 89)     # rgb = (0x21,0x21,0x59)
+    ( 87, 87, 89)     # rgb = (0x57,0x57,0x59)
+    ( 68, 68,120)     # rgb = (0x44,0x44,0x78)
+    (191,191,191)     # rgb = (0xbf,0xbf,0xbf) grey75
+    (235,221,221)     # rgb = (0xeb,0xdd,0xdd)
+    ( 45, 45, 84)     # rgb = (0x2d,0x2d,0x54)
+    ( 10, 10, 96)     # rgb = (0x0a,0x0a,0x60)
+    (  0,  0,255)     # rgb = (0x00,0x00,0xff) blue1
+    (191,125,125)     # rgb = (0xbf,0x7d,0x7d)
+    (170,170,170)     # rgb = (0xaa,0xaa,0xaa)
+}
+bKGD {index: 245}
+tRNS {
+ 0}
+IMAGE {
+    pixels hex
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000e0ea66000000000000000000000000000000
+0000000000000000000000de02a336e43903f4f0000000000000000000000000
+000000000000000069ef1a358680062eb017b0ab7af459500000000000000000
+0000000000667c0ea9cc803979937917a03a878787b0e2ae8ae75c0000000000
+00005cea8ea72c8639e293208f7d7d19200639a017ab2ee4ac2ca7097c690000
+00007823a72b2bda198fd54ddad90521219191217d1917cc2b2b2b2baf8e0000
+0000e81f9b9f27014d05d91c2a2a2a7f037ecdcd7e7a012a2a2aaab7c2ef0000
+00006c9f229d981a23282828282828282828282828282828a7b445c3c8de0000
+00005ca249d63d140f139f272727272727272727a5a528af44c3c8ce43000000
+0000009a62ca41a6960e0d941da4a4a4a4a4a4a4a4a9b732525a1084a1000000
+000000965b58b53811940d0b090b1823a3a3252ab4d24c269957571088000000
+000000946162b9b59c0f14b12d0c8b8c98a3afb8ed1bbd82ba74300877000000
+00000088c565c7b5a6962dcf67be07048aa5b84315f326ba7395832950000000
+00000002bed8d4b94214b1c7dbb68c8b04a843e6d1bd814bceeb10a900000000
+0000007b47636ec441b23d4edb3f09078bac4315f340ec855a82995f00000000
+00000059bb63e15d42643dca6b3f8e090735ed76bd81c05224e9f27b00000000
+0000006cbbd47161c1684951dc3f908e8c3ceef38d08ebe96d6d086000000000
+00000050bf67dc54534fdd53ddb20d0b8eb815d10af1732fe312e60000000000
+00000000add6d6bf61c16f566eb20e0d924475bd578572c61e6d340000000000
+0000000016d8d3d03ec76bcfdf3b0f0e13bc4c8d2f84c040cb837b0000000000
+00000000550c47b3365bd45d6f33110f1a4575cbf2c0521e0802000000000000
+000000000000e7ac36be625e7031131122455a0a2f0a99c6e700000000000000
+000000000000006a9e37d36270331613a545f181e53032e80000000000000000
+00000000000000005088c5d371311816a8464b7374ee89000000000000000000
+0000000000000000000077b654a29b18acc24a722a5500000000000000000000
+0000000000000000000000d78a9f9e9b3548c38ac90000000000000000000000
+00000000000000000000000000ef1f9e3cc20200000000000000000000000000
+0000000000000000000000000000e89736780000000000000000000000000000
+00000000000000000000000000000060e0000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+}
diff --git a/src/image/png/testdata/pngsuite/ftbrn2c08.png b/src/image/png/testdata/pngsuite/ftbrn2c08.png
new file mode 100644
index 0000000..5cca0d6
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbrn2c08.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbrn2c08.sng b/src/image/png/testdata/pngsuite/ftbrn2c08.sng
new file mode 100644
index 0000000..9569bda
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbrn2c08.sng
@@ -0,0 +1,45 @@
+#SNG: from ftbrn2c08.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using color;
+}
+gAMA {1.0000}
+bKGD {red: 255;  green: 0;  blue: 0;}
+tRNS {
+    red: 255; green: 255; blue: 255;
+}
+IMAGE {
+    pixels hex
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff e3e3e3 c9c9c9 f1f1f1 ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff e8e8e8 b5b5b5 7e7e7e 656565 6e5252 7e2e2e a64343 c79090 ebdddd ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff eeeeee bfbfbf 898989 676767 6b5d5d 7a3939 8a1212 8d0000 850000 770000 840000 9a0101 a22d2d bf7d7d ddd0d0 fcfcfc ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff f2f2f2 c4c4c4 959595 727272 6f6b6b 774444 871e1e 950101 9f0000 910000 800000 720c0c 612d2d 530e0e 500000 590000 850000 920606 7a6666 a0a0a0 cfcfcf f8f8f8 ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff f7f7f7 cacaca 9a9a9a 767676 737373 7c5d5d 872e2e 930707 9e0000 a90000 b00000 c90000 cf0000 b90000 a20101 8c1919 852a2a 7f1313 810000 960000 8f0000 6b5353 6e6e6e 737373 767676 9b9b9b c4c4c4 eeeeee ffffff ffffff 
+ffffff ffffff cccccc 7f7f7f 767676 757575 757575 962f2f b80000 b40000 b60000 ad0c0c 943a3a 925050 b92323 d60202 e20000 ef0000 e70000 da0000 cf0000 ba0000 7d0101 6f6b6b 757575 757575 757575 757575 6a6a6a 9a9a9a ffffff ffffff 
+ffffff ffffff dcdcdc 858585 888888 848484 7b7b7b 855454 b71313 a91d1d 8d4f4f 787575 777777 777777 777777 816b6b aa4141 d62020 ec1010 e90c0c d01212 a52828 7b5858 777777 777777 777777 707070 5c5c5c 525252 bdbdbd ffffff ffffff 
+ffffff ffffff eaeaea 848484 818181 858885 8e8e8e 898989 7f7f7f 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 767676 636363 545454 505050 4c4c4c e6e6e6 ffffff ffffff 
+ffffff ffffff f8f8f8 7f847f 259725 04a504 39a439 8b948b 939393 8f8f8f 838383 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7a7a7a 7a7a7a 797979 6a6a6a 575757 505050 4c4c4c 494949 595959 ffffff ffffff ffffff 
+ffffff ffffff ffffff 8a8a8a 01b301 00c600 00f200 59b659 929292 959595 979797 949494 878787 7c7c7c 7c7c7c 7c7c7c 7c7c7c 7c7c7c 7c7c7c 7c7c7c 7c7c7c 717171 5a5a60 282885 040493 0c0c78 282858 46464a 828282 ffffff ffffff ffffff 
+ffffff ffffff ffffff 929292 0cab0c 00bd00 00f400 20dd20 919191 949494 979797 999999 9b9b9b 999999 8b8b8b 7f7f7f 7e7e7e 7e7e7e 7d7d7d 777777 626262 535360 1212be 0000cc 000092 000069 000067 2a2a55 acacac ffffff ffffff ffffff 
+ffffff ffffff ffffff 949494 16a116 00b400 00e200 00f400 76a276 939393 8d978d 469e46 46a746 8e9e8e 9e9e9e 9c9c9c 8e8e8e 7e7e7e 6a6a6a 5a5a5a 57575a 1818cd 0000f0 0000a0 020260 01013d 000061 1d1d59 d6d6d6 ffffff ffffff ffffff 
+ffffff ffffff ffffff a4a4a4 219821 00aa00 00c800 00f400 3bca3b 929292 4aac4a 00bc00 00a900 2f9a2f 9d9d9d 9f9f9f a0a0a0 7a7a7a 5a5a5a 595959 3131a1 0000ff 0000c6 03035b 191924 0c0c15 0c0c55 3b3b53 fbfbfb ffffff ffffff ffffff 
+ffffff ffffff ffffff b6b6b6 2b8f2b 00a200 00ad00 00eb00 07ed07 899589 43a743 00c900 009900 389538 9c9c9c 9e9e9e 9f9f9f 747474 595959 505067 0505f5 0000f0 030370 383846 484848 161639 2b2b55 727272 ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff c7c7c7 348634 00b100 008d00 00d200 00f300 4c9b4c 3b9e3b 00c700 009800 3d943d 9b9b9b 9d9d9d 9e9e9e 6e6e6e 595959 2b2bad 0000ff 0000a6 252559 43434f 16167e 00009f 01018e 9c9ca1 ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff d8d8d8 3e7d3e 00b100 007b00 00b800 00f100 178b17 3b9c3b 00c600 009700 3d933d 9a9a9a 9b9b9b 9d9d9d 676767 575759 0909ee 0000f0 04046b 33335a 070790 00009e 00007c 0d0d5d c7c7c7 ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff eaeaea 477447 00ad00 008500 099809 00dc00 00a700 239823 00c300 009600 3f923f 989898 9a9a9a 9c9c9c 616161 42427f 0000ff 0000b9 1a1a5d 161649 00007b 00006b 00006b 1c1c56 f4f4f4 ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff fcfcfc 506c50 00a900 009500 2d772d 00c100 00c500 019301 00c100 009000 4b914b 979797 999999 9a9a9a 5a5a5a 2b2ba4 0000f6 000086 2f2f53 191930 020263 000073 00009b 4d4d70 ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff 687368 00a400 00a400 3e653e 14a514 00d400 008b00 00bf00 008e00 4a904a 959595 979797 969696 575757 1a1ab5 0000de 000068 3f3f4b 2b2b2b 0c0c6d 0000b3 00006b 868692 ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff 8c8c8c 059e05 00b000 346634 408540 00ca00 009700 00bc00 008c00 498e49 939393 959595 8f8f8f 565656 0f0fb7 0000b9 030366 474747 2f2f64 0000a2 00009d 090958 c5c5c5 ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff fafafa 90b090 348534 616461 636a63 06af06 00ae00 00b900 008b00 538d53 919191 939393 898989 555555 0a0aa8 00009d 070763 34345c 04049b 0000b1 1a1a4d b5b5bb ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff d0d0d0 6d6d6d 656565 2d8f2d 00b200 00b600 008900 558b55 8f8f8f 919191 818181 555555 15157e 000084 010165 010184 000091 1c1c6e ceced0 ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ececec 868686 587058 00af00 00b300 008800 538953 8d8d8d 8f8f8f 7a7a7a 545454 2c2c49 02026b 000064 000063 292974 dfdfe5 ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff fcfcfc aaaaaa 219821 00b000 008600 578757 8b8b8b 8d8d8d 747474 535353 3d3d3d 1a1a23 0d0d43 474772 ededef ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff d1d6d1 389b38 2d772d 7d817d 888888 8b8b8b 6d6d6d 525252 4f4f4f 373737 777777 fafafa ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff efefef a0a0a0 838383 868686 888888 676767 515151 505050 a0a0a0 fdfdfd ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff fefefe c0c0c0 858585 868686 616161 525252 b7b7b7 ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff dedede 909090 656565 cccccc ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff f5f5f5 e3e3e3 ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff ffffff 
+}
diff --git a/src/image/png/testdata/pngsuite/ftbwn0g16.png b/src/image/png/testdata/pngsuite/ftbwn0g16.png
new file mode 100644
index 0000000..99bdeed
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbwn0g16.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbwn0g16.sng b/src/image/png/testdata/pngsuite/ftbwn0g16.sng
new file mode 100644
index 0000000..3fca307
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbwn0g16.sng
@@ -0,0 +1,45 @@
+#SNG: from ftbwn0g16.png
+IHDR {
+    width: 32; height: 32; bitdepth: 16;
+    using grayscale;
+}
+gAMA {1.0000}
+bKGD {gray: 65535;}
+tRNS {
+    gray: 65535;
+}
+IMAGE {
+    pixels hex
+ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff e3e3 c9c9 f1f1 ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff e8e8 b5b5 7e7e 6565 5ab9 462f 60f8 a111 e210 ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff eeee bfbf 8989 6767 6190 4cba 3614 2a50 27e9 23b5 279c 2eea 5049 914b d4b7 fcfc ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff f2f2 c4c4 9595 7272 6c9e 5392 3da0 2d6a 2fb7 2b83 2669 2aa7 3cc7 22c2 1801 1ab5 27e9 3008 6c66 a0a0 cfcf f8f8 ffff ffff ffff ffff ffff 
+ffff ffff f7f7 caca 9a9a 7676 7373 66aa 48e3 3109 2f6a 32b6 34d0 3c50 3e1d 3784 3151 3b9b 4578 337b 26b6 2d03 2ae9 5a87 6e6e 7373 7676 9b9b c4c4 eeee ffff ffff 
+ffff ffff cccc 7f7f 7676 7575 7575 4e17 3737 3603 369d 3c5c 553c 641e 5026 419f 43d1 47b7 4551 416a 3e1e 37d0 2636 6c9e 7575 7575 7575 7575 6a6a 9a9a ffff ffff 
+ffff ffff dcdc 8585 8888 8484 7b7b 6308 4449 471f 61ea 765b 7777 7777 7777 7205 60c3 56bd 5214 4e5d 4b15 4daa 62d9 7777 7777 7777 7070 5c5c 5252 bdbd ffff ffff 
+ffff ffff eaea 8484 8181 8749 8e8e 8989 7f7f 7979 7979 7979 7979 7979 7979 7979 7979 7979 7979 7979 7979 7979 7979 7979 7676 6363 5454 5050 4c4c e6e6 ffff ffff 
+ffff ffff f8f8 8271 6847 62d4 783c 90d8 9393 8f8f 8383 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7a7a 7a7a 7979 6a6a 5757 5050 4c4c 4949 5959 ffff ffff ffff 
+ffff ffff ffff 8a8a 69d4 749a 8e83 901d 9292 9595 9797 9494 8787 7c7c 7c7c 7c7c 7c7c 7c7c 7c7c 7c7c 7c7c 7171 5b0b 32d9 1474 1876 2dac 46bc 8282 ffff ffff ffff 
+ffff ffff ffff 9292 69ae 6f4d 8fb1 8f6d 9191 9494 9797 9999 9b9b 9999 8b8b 7f7f 7e7e 7e7e 7d7d 7777 6262 54d2 25d7 1773 10c8 0c12 0bd7 2f1b acac ffff ffff ffff 
+ffff ffff ffff 9494 67f1 6a00 8517 8fb1 905f 9393 9371 7a19 7f65 97fa 9e9e 9c9c 8e8e 7e7e 6a6a 5a5a 57af 2ce6 1b97 1264 0cd0 07e7 0b27 2403 d6d6 ffff ffff ffff 
+ffff ffff ffff a4a4 6735 641d 75c7 8fb1 8f71 9292 8400 6eb6 6386 6e32 9d9d 9f9f a0a0 7a7a 5a5a 5959 3e11 1d50 16c2 0d21 1a5d 0d15 1470 3dfd fbfb ffff ffff ffff 
+ffff ffff ffff b6b6 660f 5f67 65e1 8a64 8e79 909a 7e27 765e 5a1a 6efc 9c9c 9e9e 9f9f 7474 5959 52f5 209b 1b97 0f8a 39d4 4848 1a1c 2fff 7272 ffff ffff ffff ffff 
+ffff ffff ffff c7c7 647e 683c 5309 7bab 8f1a 7ad2 7588 7531 5983 7079 9b9b 9d9d 9e9e 6e6e 5959 3a1c 1d50 1315 2b1f 44a4 220a 1247 1136 9d2f ffff ffff ffff ffff 
+ffff ffff ffff d8d8 6358 683c 486f 6c5b 8dec 5b67 745a 749a 58ec 6fe2 9a9a 9b9b 9d9d 6767 5792 235c 1b97 0fdb 37af 16c6 1229 0e41 163f c7c7 ffff ffff ffff ffff 
+ffff ffff ffff eaea 61c7 65e1 4e53 5d3f 818e 6258 6809 72d6 5855 7020 9898 9a9a 9c9c 6161 4945 1d50 1544 21ce 1bf3 0e23 0c4d 0c4d 22c7 f4f4 ffff ffff ffff ffff 
+ffff ffff ffff fcfc 60cd 6386 57bf 58c1 71a8 7403 56fb 71a8 54cd 7484 9797 9999 9a9a 5a5a 3914 1c47 0f68 3352 1bbe 0d28 0d38 11d2 5153 ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff 6ee2 6094 6094 5535 6978 7cd8 51db 707a 539f 7383 9595 9797 9696 5757 2beb 1985 0bf5 40a0 2b2b 1732 1493 0c4d 87e7 ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff 8c8c 5f1f 67a5 51a6 68e2 76f5 58ec 6eb6 5272 71eb 9393 9595 8f8f 5656 225f 1544 0e64 4747 3547 129f 120c 121e c5c5 ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff fafa a368 63e7 6325 6782 698c 6678 6cf2 51db 757b 9191 9393 8989 5555 1c33 120c 119a 38cd 155f 1459 1ff7 b666 ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff d0d0 6d6d 6565 66e3 68d3 6b2e 50ae 7522 8f8f 9191 8181 5555 2127 0f2d 0c80 1010 10ab 2589 cf09 ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ecec 8686 667a 670e 6969 5017 7320 8d8d 8f8f 7a7a 5454 2f81 0e14 0b7f 0b61 31c8 e090 ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff fcfc aaaa 6735 67a5 4ee9 739b 8b8b 8d8d 7474 5353 3d3d 1b23 1342 4c38 ee28 ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff d4c3 7285 58c1 7fd8 8888 8b8b 6d6d 5252 4f4f 3737 7777 fafa ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff efef a0a0 8383 8686 8888 6767 5151 5050 a0a0 fdfd ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff fefe c0c0 8585 8686 6161 5252 b7b7 ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff dede 9090 6565 cccc ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff f5f5 e3e3 ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff 
+}
diff --git a/src/image/png/testdata/pngsuite/ftbwn3p08.png b/src/image/png/testdata/pngsuite/ftbwn3p08.png
new file mode 100644
index 0000000..eacab7a
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbwn3p08.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbwn3p08.sng b/src/image/png/testdata/pngsuite/ftbwn3p08.sng
new file mode 100644
index 0000000..7b5aff6
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbwn3p08.sng
@@ -0,0 +1,291 @@
+#SNG: from ftbwn3p08.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using color palette;
+}
+gAMA {1.0000}
+PLTE {
+    (255,255,255)     # rgb = (0xff,0xff,0xff) grey100
+    (128, 86, 86)     # rgb = (0x80,0x56,0x56)
+    (181,181,184)     # rgb = (0xb5,0xb5,0xb8)
+    (168, 66, 66)     # rgb = (0xa8,0x42,0x42)
+    (159,159,159)     # rgb = (0x9f,0x9f,0x9f)
+    (177, 32, 32)     # rgb = (0xb1,0x20,0x20)
+    (139, 21, 21)     # rgb = (0x8b,0x15,0x15)
+    (157,157,157)     # rgb = (0x9d,0x9d,0x9d)
+    ( 27, 27, 89)     # rgb = (0x1b,0x1b,0x59)
+    (155,155,155)     # rgb = (0x9b,0x9b,0x9b)
+    (  0,  0,132)     # rgb = (0x00,0x00,0x84)
+    (153,153,153)     # rgb = (0x99,0x99,0x99) grey60
+    (143,167,143)     # rgb = (0x8f,0xa7,0x8f)
+    (151,151,151)     # rgb = (0x97,0x97,0x97)
+    (149,149,149)     # rgb = (0x95,0x95,0x95)
+    (147,147,147)     # rgb = (0x93,0x93,0x93)
+    ( 41, 41, 86)     # rgb = (0x29,0x29,0x56)
+    (145,145,145)     # rgb = (0x91,0x91,0x91) grey57
+    (  0,  0,155)     # rgb = (0x00,0x00,0x9b)
+    (143,143,143)     # rgb = (0x8f,0x8f,0x8f) grey56
+    (139,149,139)     # rgb = (0x8b,0x95,0x8b)
+    ( 46, 46,167)     # rgb = (0x2e,0x2e,0xa7)
+    (141,141,141)     # rgb = (0x8d,0x8d,0x8d)
+    (128,  0,  0)     # rgb = (0x80,0x00,0x00)
+    (139,139,139)     # rgb = (0x8b,0x8b,0x8b)
+    (185,  0,  0)     # rgb = (0xb9,0x00,0x00)
+    (137,137,137)     # rgb = (0x89,0x89,0x89)
+    ( 12, 12,213)     # rgb = (0x0c,0x0c,0xd5)
+    (120,117,117)     # rgb = (0x78,0x75,0x75)
+    (135,135,135)     # rgb = (0x87,0x87,0x87) grey53
+    (  0,  0,178)     # rgb = (0x00,0x00,0xb2)
+    (133,133,133)     # rgb = (0x85,0x85,0x85) grey52
+    (165,  0,  0)     # rgb = (0xa5,0x00,0x00)
+    (222,  0,  0)     # rgb = (0xde,0x00,0x00)
+    (129,129,129)     # rgb = (0x81,0x81,0x81)
+    (127,127,127)     # rgb = (0x7f,0x7f,0x7f) grey50
+    (  0,  0,158)     # rgb = (0x00,0x00,0x9e)
+    (125,125,125)     # rgb = (0x7d,0x7d,0x7d) grey49
+    (  0,  0,201)     # rgb = (0x00,0x00,0xc9)
+    (123,123,123)     # rgb = (0x7b,0x7b,0x7b)
+    (121,121,121)     # rgb = (0x79,0x79,0x79)
+    ( 55, 55, 86)     # rgb = (0x37,0x37,0x56)
+    (119,119,119)     # rgb = (0x77,0x77,0x77)
+    (117,117,117)     # rgb = (0x75,0x75,0x75) grey46
+    (115,115,115)     # rgb = (0x73,0x73,0x73) grey45
+    ( 72,169, 72)     # rgb = (0x48,0xa9,0x48)
+    (142,  0,  0)     # rgb = (0x8e,0x00,0x00)
+    (  2,  2,100)     # rgb = (0x02,0x02,0x64)
+    (  0,  0, 98)     # rgb = (0x00,0x00,0x62)
+    ( 86,137, 86)     # rgb = (0x56,0x89,0x56)
+    ( 40, 40,124)     # rgb = (0x28,0x28,0x7c)
+    ( 83,139, 83)     # rgb = (0x53,0x8b,0x53)
+    (137,137,143)     # rgb = (0x89,0x89,0x8f)
+    (103,103,103)     # rgb = (0x67,0x67,0x67)
+    (101,101,101)     # rgb = (0x65,0x65,0x65)
+    ( 93,109, 93)     # rgb = (0x5d,0x6d,0x5d)
+    ( 19,229, 19)     # rgb = (0x13,0xe5,0x13)
+    (134, 38, 38)     # rgb = (0x86,0x26,0x26)
+    (111, 45, 45)     # rgb = (0x6f,0x2d,0x2d)
+    ( 68,145, 68)     # rgb = (0x44,0x91,0x44)
+    ( 97, 97, 97)     # rgb = (0x61,0x61,0x61) grey38
+    ( 59,157, 59)     # rgb = (0x3b,0x9d,0x3b)
+    ( 68,137, 68)     # rgb = (0x44,0x89,0x44)
+    ( 61,147, 61)     # rgb = (0x3d,0x93,0x3d)
+    (  0,  0,164)     # rgb = (0x00,0x00,0xa4)
+    (  0,243,  0)     # rgb = (0x00,0xf3,0x00)
+    (  0,241,  0)     # rgb = (0x00,0xf1,0x00)
+    ( 89, 89, 89)     # rgb = (0x59,0x59,0x59) grey35
+    ( 87, 87, 87)     # rgb = (0x57,0x57,0x57) grey34
+    ( 85, 85, 85)     # rgb = (0x55,0x55,0x55)
+    ( 83, 83, 83)     # rgb = (0x53,0x53,0x53)
+    ( 52,133, 52)     # rgb = (0x34,0x85,0x34)
+    ( 81, 81, 81)     # rgb = (0x51,0x51,0x51)
+    ( 36,151, 36)     # rgb = (0x24,0x97,0x24)
+    ( 79, 79, 79)     # rgb = (0x4f,0x4f,0x4f) grey31
+    ( 58, 58, 65)     # rgb = (0x3a,0x3a,0x41)
+    ( 16, 16,186)     # rgb = (0x10,0x10,0xba)
+    (178, 15, 15)     # rgb = (0xb2,0x0f,0x0f)
+    (  0,199,  0)     # rgb = (0x00,0xc7,0x00)
+    (  0,197,  0)     # rgb = (0x00,0xc5,0x00)
+    (252,252,252)     # rgb = (0xfc,0xfc,0xfc) grey99
+    (  0,195,  0)     # rgb = (0x00,0xc3,0x00)
+    (  4,  4,151)     # rgb = (0x04,0x04,0x97)
+    (  0,193,  0)     # rgb = (0x00,0xc1,0x00)
+    ( 45,119, 45)     # rgb = (0x2d,0x77,0x2d)
+    (250,250,250)     # rgb = (0xfa,0xfa,0xfa) grey98
+    (  0,191,  0)     # rgb = (0x00,0xbf,0x00)
+    (  0,  0,104)     # rgb = (0x00,0x00,0x68)
+    (  0,189,  0)     # rgb = (0x00,0xbd,0x00)
+    (218,212,212)     # rgb = (0xda,0xd4,0xd4)
+    ( 16, 16,123)     # rgb = (0x10,0x10,0x7b)
+    (  9,173,  9)     # rgb = (0x09,0xad,0x09)
+    (248,248,248)     # rgb = (0xf8,0xf8,0xf8)
+    (  0,185,  0)     # rgb = (0x00,0xb9,0x00)
+    (  0,183,  0)     # rgb = (0x00,0xb7,0x00)
+    (156,156,161)     # rgb = (0x9c,0x9c,0xa1)
+    (246,246,246)     # rgb = (0xf6,0xf6,0xf6)
+    ( 12,161, 12)     # rgb = (0x0c,0xa1,0x0c)
+    (  0,179,  0)     # rgb = (0x00,0xb3,0x00)
+    (  0,177,  0)     # rgb = (0x00,0xb1,0x00)
+    ( 16,145, 16)     # rgb = (0x10,0x91,0x10)
+    (  0,171,  0)     # rgb = (0x00,0xab,0x00)
+    (242,242,242)     # rgb = (0xf2,0xf2,0xf2) grey95
+    (  0,169,  0)     # rgb = (0x00,0xa9,0x00)
+    (  0,167,  0)     # rgb = (0x00,0xa7,0x00)
+    (238,238,238)     # rgb = (0xee,0xee,0xee)
+    (236,236,236)     # rgb = (0xec,0xec,0xec)
+    (  0,151,  0)     # rgb = (0x00,0x97,0x00)
+    (234,234,234)     # rgb = (0xea,0xea,0xea)
+    (  0,  0,107)     # rgb = (0x00,0x00,0x6b)
+    (  0,141,  0)     # rgb = (0x00,0x8d,0x00)
+    (  0,139,  0)     # rgb = (0x00,0x8b,0x00) green4
+    (  0,137,  0)     # rgb = (0x00,0x89,0x00)
+    (  0,135,  0)     # rgb = (0x00,0x87,0x00)
+    ( 49, 49, 49)     # rgb = (0x31,0x31,0x31)
+    ( 25, 25, 42)     # rgb = (0x19,0x19,0x2a)
+    (  7,  7, 64)     # rgb = (0x07,0x07,0x40)
+    ( 18, 18,174)     # rgb = (0x12,0x12,0xae)
+    (  9,  9,238)     # rgb = (0x09,0x09,0xee)
+    (211,214,211)     # rgb = (0xd3,0xd6,0xd3)
+    (204,204,204)     # rgb = (0xcc,0xcc,0xcc) grey80
+    (147,  0,  0)     # rgb = (0x93,0x00,0x00)
+    (163, 42, 42)     # rgb = (0xa3,0x2a,0x2a)
+    (198,198,198)     # rgb = (0xc6,0xc6,0xc6)
+    (196,196,196)     # rgb = (0xc4,0xc4,0xc4) grey77
+    (204,  0,  0)     # rgb = (0xcc,0x00,0x00)
+    (211, 10, 10)     # rgb = (0xd3,0x0a,0x0a)
+    (129,107,107)     # rgb = (0x81,0x6b,0x6b)
+    (120, 62, 62)     # rgb = (0x78,0x3e,0x3e)
+    (  3,  3,109)     # rgb = (0x03,0x03,0x6d)
+    (  0,  0,159)     # rgb = (0x00,0x00,0x9f)
+    ( 10, 10, 86)     # rgb = (0x0a,0x0a,0x56)
+    ( 70, 70, 72)     # rgb = (0x46,0x46,0x48)
+    ( 65, 65, 77)     # rgb = (0x41,0x41,0x4d)
+    (115, 93, 93)     # rgb = (0x73,0x5d,0x5d)
+    ( 81,  7,  7)     # rgb = (0x51,0x07,0x07)
+    (168,168,168)     # rgb = (0xa8,0xa8,0xa8) grey66
+    (237,237,239)     # rgb = (0xed,0xed,0xef)
+    (160,160,160)     # rgb = (0xa0,0xa0,0xa0)
+    (158,158,158)     # rgb = (0x9e,0x9e,0x9e) grey62
+    (156,156,156)     # rgb = (0x9c,0x9c,0x9c) grey61
+    (  0,  0,185)     # rgb = (0x00,0x00,0xb9)
+    (154,154,154)     # rgb = (0x9a,0x9a,0x9a)
+    (178,  0,  0)     # rgb = (0xb2,0x00,0x00)
+    (152,152,152)     # rgb = (0x98,0x98,0x98)
+    (235,  0,  0)     # rgb = (0xeb,0x00,0x00)
+    (150,150,150)     # rgb = (0x96,0x96,0x96) grey59
+    (158,  0,  0)     # rgb = (0x9e,0x00,0x00)
+    (148,148,148)     # rgb = (0x94,0x94,0x94) grey58
+    ( 19, 19, 28)     # rgb = (0x13,0x13,0x1c)
+    (146,146,146)     # rgb = (0x92,0x92,0x92)
+    (144,144,144)     # rgb = (0x90,0x90,0x90)
+    (142,142,142)     # rgb = (0x8e,0x8e,0x8e)
+    (  0,  0,145)     # rgb = (0x00,0x00,0x91)
+    (138,138,138)     # rgb = (0x8a,0x8a,0x8a) grey54
+    (136,136,136)     # rgb = (0x88,0x88,0x88)
+    (118,162,118)     # rgb = (0x76,0xa2,0x76)
+    (133,136,133)     # rgb = (0x85,0x88,0x85)
+    (134,134,134)     # rgb = (0x86,0x86,0x86)
+    (132,132,132)     # rgb = (0x84,0x84,0x84)
+    (120, 15, 15)     # rgb = (0x78,0x0f,0x0f)
+    (130,130,130)     # rgb = (0x82,0x82,0x82) grey51
+    (126,130,126)     # rgb = (0x7e,0x82,0x7e)
+    (126,126,126)     # rgb = (0x7e,0x7e,0x7e)
+    (124,124,124)     # rgb = (0x7c,0x7c,0x7c)
+    (122,122,122)     # rgb = (0x7a,0x7a,0x7a) grey48
+    ( 74,192, 74)     # rgb = (0x4a,0xc0,0x4a)
+    (118,118,118)     # rgb = (0x76,0x76,0x76)
+    (116,116,116)     # rgb = (0x74,0x74,0x74)
+    (114,114,114)     # rgb = (0x72,0x72,0x72)
+    (112,112,112)     # rgb = (0x70,0x70,0x70) grey44
+    (152,  0,  0)     # rgb = (0x98,0x00,0x00)
+    (110,110,110)     # rgb = (0x6e,0x6e,0x6e) grey43
+    (106,112,106)     # rgb = (0x6a,0x70,0x6a)
+    (122,102,102)     # rgb = (0x7a,0x66,0x66)
+    (106,106,106)     # rgb = (0x6a,0x6a,0x6a)
+    (132,  0,  0)     # rgb = (0x84,0x00,0x00)
+    ( 68,162, 68)     # rgb = (0x44,0xa2,0x44)
+    ( 75,150, 75)     # rgb = (0x4b,0x96,0x4b)
+    ( 97,100, 97)     # rgb = (0x61,0x64,0x61)
+    ( 98, 98, 98)     # rgb = (0x62,0x62,0x62)
+    (  0,244,  0)     # rgb = (0x00,0xf4,0x00)
+    ( 56,152, 56)     # rgb = (0x38,0x98,0x38)
+    ( 92, 92, 92)     # rgb = (0x5c,0x5c,0x5c) grey36
+    ( 90, 90, 90)     # rgb = (0x5a,0x5a,0x5a)
+    (  0,230,  0)     # rgb = (0x00,0xe6,0x00)
+    (  2,  2, 93)     # rgb = (0x02,0x02,0x5d)
+    ( 66,120, 66)     # rgb = (0x42,0x78,0x42)
+    ( 86, 86, 86)     # rgb = (0x56,0x56,0x56)
+    (  0,  0,240)     # rgb = (0x00,0x00,0xf0)
+    ( 46,148, 46)     # rgb = (0x2e,0x94,0x2e)
+    ( 71,104, 71)     # rgb = (0x47,0x68,0x47)
+    ( 49, 49, 96)     # rgb = (0x31,0x31,0x60)
+    (  0,216,  0)     # rgb = (0x00,0xd8,0x00)
+    ( 82, 82, 82)     # rgb = (0x52,0x52,0x52) grey32
+    ( 80, 80, 80)     # rgb = (0x50,0x50,0x50)
+    (  0,206,  0)     # rgb = (0x00,0xce,0x00)
+    ( 33,152, 33)     # rgb = (0x21,0x98,0x21)
+    ( 20, 20,109)     # rgb = (0x14,0x14,0x6d)
+    (  0,200,  0)     # rgb = (0x00,0xc8,0x00)
+    ( 76, 76, 76)     # rgb = (0x4c,0x4c,0x4c)
+    (253,253,253)     # rgb = (0xfd,0xfd,0xfd)
+    (  0,198,  0)     # rgb = (0x00,0xc6,0x00)
+    (  0,  0,157)     # rgb = (0x00,0x00,0x9d)
+    (111,107,107)     # rgb = (0x6f,0x6b,0x6b)
+    (234, 14, 14)     # rgb = (0xea,0x0e,0x0e)
+    ( 72, 72, 72)     # rgb = (0x48,0x48,0x48)
+    (  0,188,  0)     # rgb = (0x00,0xbc,0x00)
+    ( 52,102, 52)     # rgb = (0x34,0x66,0x34)
+    (  2,  2,245)     # rgb = (0x02,0x02,0xf5)
+    ( 83, 83, 96)     # rgb = (0x53,0x53,0x60)
+    (  0,176,  0)     # rgb = (0x00,0xb0,0x00)
+    (  0,174,  0)     # rgb = (0x00,0xae,0x00)
+    (183,  0,  0)     # rgb = (0xb7,0x00,0x00)
+    (  0,164,  0)     # rgb = (0x00,0xa4,0x00)
+    (239,239,239)     # rgb = (0xef,0xef,0xef)
+    (  0,162,  0)     # rgb = (0x00,0xa2,0x00)
+    (143, 79, 79)     # rgb = (0x8f,0x4f,0x4f)
+    (149, 52, 52)     # rgb = (0x95,0x34,0x34)
+    (  0,152,  0)     # rgb = (0x00,0x98,0x00)
+    (  0,150,  0)     # rgb = (0x00,0x96,0x00)
+    (  0,146,  0)     # rgb = (0x00,0x92,0x00)
+    (231,231,231)     # rgb = (0xe7,0xe7,0xe7)
+    (  0,140,  0)     # rgb = (0x00,0x8c,0x00)
+    (227,227,227)     # rgb = (0xe3,0xe3,0xe3) grey89
+    (  0,128,  0)     # rgb = (0x00,0x80,0x00)
+    (146,  6,  6)     # rgb = (0x92,0x06,0x06)
+    (  1,  1,111)     # rgb = (0x01,0x01,0x6f)
+    (100, 86, 89)     # rgb = (0x64,0x56,0x59)
+    (  0,  0,100)     # rgb = (0x00,0x00,0x64)
+    ( 78, 78,107)     # rgb = (0x4e,0x4e,0x6b)
+    (207,207,207)     # rgb = (0xcf,0xcf,0xcf) grey81
+    (221,221,224)     # rgb = (0xdd,0xdd,0xe0)
+    (  0,  0,123)     # rgb = (0x00,0x00,0x7b)
+    (201,201,201)     # rgb = (0xc9,0xc9,0xc9) grey79
+    ( 22, 22, 65)     # rgb = (0x16,0x16,0x41)
+    ( 33, 33, 89)     # rgb = (0x21,0x21,0x59)
+    ( 87, 87, 89)     # rgb = (0x57,0x57,0x59)
+    ( 68, 68,120)     # rgb = (0x44,0x44,0x78)
+    (191,191,191)     # rgb = (0xbf,0xbf,0xbf) grey75
+    (235,221,221)     # rgb = (0xeb,0xdd,0xdd)
+    ( 45, 45, 84)     # rgb = (0x2d,0x2d,0x54)
+    ( 10, 10, 96)     # rgb = (0x0a,0x0a,0x60)
+    (  0,  0,255)     # rgb = (0x00,0x00,0xff) blue1
+    (191,125,125)     # rgb = (0xbf,0x7d,0x7d)
+}
+bKGD {index: 0}
+tRNS {
+ 0}
+IMAGE {
+    pixels hex
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000e0ea66000000000000000000000000000000
+0000000000000000000000de02a336e43903f4f0000000000000000000000000
+000000000000000069ef1a358680062eb017b0ab7af459500000000000000000
+0000000000667c0ea9cc803979937917a03a878787b0e2ae8ae75c0000000000
+00005cea8ea72c8639e293208f7d7d19200639a017ab2ee4ac2ca7097c690000
+00007823a72b2bda198fd54ddad90521219191217d1917cc2b2b2b2baf8e0000
+0000e81f9b9f27014d05d91c2a2a2a7f037ecdcd7e7a012a2a2aaab7c2ef0000
+00006c9f229d981a23282828282828282828282828282828a7b445c3c8de0000
+00005ca249d63d140f139f272727272727272727a5a528af44c3c8ce43000000
+0000009a62ca41a6960e0d941da4a4a4a4a4a4a4a4a9b732525a1084a1000000
+000000965b58b53811940d0b090b1823a3a3252ab4d24c269957571088000000
+000000946162b9b59c0f14b12d0c8b8c98a3afb8ed1bbd82ba74300877000000
+00000088c565c7b5a6962dcf67be07048aa5b84315f326ba7395832950000000
+00000002bed8d4b94214b1c7dbb68c8b04a843e6d1bd814bceeb10a900000000
+0000007b47636ec441b23d4edb3f09078bac4315f340ec855a82995f00000000
+00000059bb63e15d42643dca6b3f8e090735ed76bd81c05224e9f27b00000000
+0000006cbbd47161c1684951dc3f908e8c3ceef38d08ebe96d6d086000000000
+00000050bf67dc54534fdd53ddb20d0b8eb815d10af1732fe312e60000000000
+00000000add6d6bf61c16f566eb20e0d924475bd578572c61e6d340000000000
+0000000016d8d3d03ec76bcfdf3b0f0e13bc4c8d2f84c040cb837b0000000000
+00000000550c47b3365bd45d6f33110f1a4575cbf2c0521e0802000000000000
+000000000000e7ac36be625e7031131122455a0a2f0a99c6e700000000000000
+000000000000006a9e37d36270331613a545f181e53032e80000000000000000
+00000000000000005088c5d371311816a8464b7374ee89000000000000000000
+0000000000000000000077b654a29b18acc24a722a5500000000000000000000
+0000000000000000000000d78a9f9e9b3548c38ac90000000000000000000000
+00000000000000000000000000ef1f9e3cc20200000000000000000000000000
+0000000000000000000000000000e89736780000000000000000000000000000
+00000000000000000000000000000060e0000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+}
diff --git a/src/image/png/testdata/pngsuite/ftbyn3p08.png b/src/image/png/testdata/pngsuite/ftbyn3p08.png
new file mode 100644
index 0000000..656db09
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftbyn3p08.png differ
diff --git a/src/image/png/testdata/pngsuite/ftbyn3p08.sng b/src/image/png/testdata/pngsuite/ftbyn3p08.sng
new file mode 100644
index 0000000..5d61987
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftbyn3p08.sng
@@ -0,0 +1,292 @@
+#SNG: from ftbyn3p08.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using color palette;
+}
+gAMA {1.0000}
+PLTE {
+    (255,255,255)     # rgb = (0xff,0xff,0xff) grey100
+    (128, 86, 86)     # rgb = (0x80,0x56,0x56)
+    (181,181,184)     # rgb = (0xb5,0xb5,0xb8)
+    (168, 66, 66)     # rgb = (0xa8,0x42,0x42)
+    (159,159,159)     # rgb = (0x9f,0x9f,0x9f)
+    (177, 32, 32)     # rgb = (0xb1,0x20,0x20)
+    (139, 21, 21)     # rgb = (0x8b,0x15,0x15)
+    (157,157,157)     # rgb = (0x9d,0x9d,0x9d)
+    ( 27, 27, 89)     # rgb = (0x1b,0x1b,0x59)
+    (155,155,155)     # rgb = (0x9b,0x9b,0x9b)
+    (  0,  0,132)     # rgb = (0x00,0x00,0x84)
+    (153,153,153)     # rgb = (0x99,0x99,0x99) grey60
+    (143,167,143)     # rgb = (0x8f,0xa7,0x8f)
+    (151,151,151)     # rgb = (0x97,0x97,0x97)
+    (149,149,149)     # rgb = (0x95,0x95,0x95)
+    (147,147,147)     # rgb = (0x93,0x93,0x93)
+    ( 41, 41, 86)     # rgb = (0x29,0x29,0x56)
+    (145,145,145)     # rgb = (0x91,0x91,0x91) grey57
+    (  0,  0,155)     # rgb = (0x00,0x00,0x9b)
+    (143,143,143)     # rgb = (0x8f,0x8f,0x8f) grey56
+    (139,149,139)     # rgb = (0x8b,0x95,0x8b)
+    ( 46, 46,167)     # rgb = (0x2e,0x2e,0xa7)
+    (141,141,141)     # rgb = (0x8d,0x8d,0x8d)
+    (128,  0,  0)     # rgb = (0x80,0x00,0x00)
+    (139,139,139)     # rgb = (0x8b,0x8b,0x8b)
+    (185,  0,  0)     # rgb = (0xb9,0x00,0x00)
+    (137,137,137)     # rgb = (0x89,0x89,0x89)
+    ( 12, 12,213)     # rgb = (0x0c,0x0c,0xd5)
+    (120,117,117)     # rgb = (0x78,0x75,0x75)
+    (135,135,135)     # rgb = (0x87,0x87,0x87) grey53
+    (  0,  0,178)     # rgb = (0x00,0x00,0xb2)
+    (133,133,133)     # rgb = (0x85,0x85,0x85) grey52
+    (165,  0,  0)     # rgb = (0xa5,0x00,0x00)
+    (222,  0,  0)     # rgb = (0xde,0x00,0x00)
+    (129,129,129)     # rgb = (0x81,0x81,0x81)
+    (127,127,127)     # rgb = (0x7f,0x7f,0x7f) grey50
+    (  0,  0,158)     # rgb = (0x00,0x00,0x9e)
+    (125,125,125)     # rgb = (0x7d,0x7d,0x7d) grey49
+    (  0,  0,201)     # rgb = (0x00,0x00,0xc9)
+    (123,123,123)     # rgb = (0x7b,0x7b,0x7b)
+    (121,121,121)     # rgb = (0x79,0x79,0x79)
+    ( 55, 55, 86)     # rgb = (0x37,0x37,0x56)
+    (119,119,119)     # rgb = (0x77,0x77,0x77)
+    (117,117,117)     # rgb = (0x75,0x75,0x75) grey46
+    (115,115,115)     # rgb = (0x73,0x73,0x73) grey45
+    ( 72,169, 72)     # rgb = (0x48,0xa9,0x48)
+    (142,  0,  0)     # rgb = (0x8e,0x00,0x00)
+    (  2,  2,100)     # rgb = (0x02,0x02,0x64)
+    (  0,  0, 98)     # rgb = (0x00,0x00,0x62)
+    ( 86,137, 86)     # rgb = (0x56,0x89,0x56)
+    ( 40, 40,124)     # rgb = (0x28,0x28,0x7c)
+    ( 83,139, 83)     # rgb = (0x53,0x8b,0x53)
+    (137,137,143)     # rgb = (0x89,0x89,0x8f)
+    (103,103,103)     # rgb = (0x67,0x67,0x67)
+    (101,101,101)     # rgb = (0x65,0x65,0x65)
+    ( 93,109, 93)     # rgb = (0x5d,0x6d,0x5d)
+    ( 19,229, 19)     # rgb = (0x13,0xe5,0x13)
+    (134, 38, 38)     # rgb = (0x86,0x26,0x26)
+    (111, 45, 45)     # rgb = (0x6f,0x2d,0x2d)
+    ( 68,145, 68)     # rgb = (0x44,0x91,0x44)
+    ( 97, 97, 97)     # rgb = (0x61,0x61,0x61) grey38
+    ( 59,157, 59)     # rgb = (0x3b,0x9d,0x3b)
+    ( 68,137, 68)     # rgb = (0x44,0x89,0x44)
+    ( 61,147, 61)     # rgb = (0x3d,0x93,0x3d)
+    (  0,  0,164)     # rgb = (0x00,0x00,0xa4)
+    (  0,243,  0)     # rgb = (0x00,0xf3,0x00)
+    (  0,241,  0)     # rgb = (0x00,0xf1,0x00)
+    ( 89, 89, 89)     # rgb = (0x59,0x59,0x59) grey35
+    ( 87, 87, 87)     # rgb = (0x57,0x57,0x57) grey34
+    ( 85, 85, 85)     # rgb = (0x55,0x55,0x55)
+    ( 83, 83, 83)     # rgb = (0x53,0x53,0x53)
+    ( 52,133, 52)     # rgb = (0x34,0x85,0x34)
+    ( 81, 81, 81)     # rgb = (0x51,0x51,0x51)
+    ( 36,151, 36)     # rgb = (0x24,0x97,0x24)
+    ( 79, 79, 79)     # rgb = (0x4f,0x4f,0x4f) grey31
+    ( 58, 58, 65)     # rgb = (0x3a,0x3a,0x41)
+    ( 16, 16,186)     # rgb = (0x10,0x10,0xba)
+    (178, 15, 15)     # rgb = (0xb2,0x0f,0x0f)
+    (  0,199,  0)     # rgb = (0x00,0xc7,0x00)
+    (  0,197,  0)     # rgb = (0x00,0xc5,0x00)
+    (252,252,252)     # rgb = (0xfc,0xfc,0xfc) grey99
+    (  0,195,  0)     # rgb = (0x00,0xc3,0x00)
+    (  4,  4,151)     # rgb = (0x04,0x04,0x97)
+    (  0,193,  0)     # rgb = (0x00,0xc1,0x00)
+    ( 45,119, 45)     # rgb = (0x2d,0x77,0x2d)
+    (250,250,250)     # rgb = (0xfa,0xfa,0xfa) grey98
+    (  0,191,  0)     # rgb = (0x00,0xbf,0x00)
+    (  0,  0,104)     # rgb = (0x00,0x00,0x68)
+    (  0,189,  0)     # rgb = (0x00,0xbd,0x00)
+    (218,212,212)     # rgb = (0xda,0xd4,0xd4)
+    ( 16, 16,123)     # rgb = (0x10,0x10,0x7b)
+    (  9,173,  9)     # rgb = (0x09,0xad,0x09)
+    (248,248,248)     # rgb = (0xf8,0xf8,0xf8)
+    (  0,185,  0)     # rgb = (0x00,0xb9,0x00)
+    (  0,183,  0)     # rgb = (0x00,0xb7,0x00)
+    (156,156,161)     # rgb = (0x9c,0x9c,0xa1)
+    (246,246,246)     # rgb = (0xf6,0xf6,0xf6)
+    ( 12,161, 12)     # rgb = (0x0c,0xa1,0x0c)
+    (  0,179,  0)     # rgb = (0x00,0xb3,0x00)
+    (  0,177,  0)     # rgb = (0x00,0xb1,0x00)
+    ( 16,145, 16)     # rgb = (0x10,0x91,0x10)
+    (  0,171,  0)     # rgb = (0x00,0xab,0x00)
+    (242,242,242)     # rgb = (0xf2,0xf2,0xf2) grey95
+    (  0,169,  0)     # rgb = (0x00,0xa9,0x00)
+    (  0,167,  0)     # rgb = (0x00,0xa7,0x00)
+    (238,238,238)     # rgb = (0xee,0xee,0xee)
+    (236,236,236)     # rgb = (0xec,0xec,0xec)
+    (  0,151,  0)     # rgb = (0x00,0x97,0x00)
+    (234,234,234)     # rgb = (0xea,0xea,0xea)
+    (  0,  0,107)     # rgb = (0x00,0x00,0x6b)
+    (  0,141,  0)     # rgb = (0x00,0x8d,0x00)
+    (  0,139,  0)     # rgb = (0x00,0x8b,0x00) green4
+    (  0,137,  0)     # rgb = (0x00,0x89,0x00)
+    (  0,135,  0)     # rgb = (0x00,0x87,0x00)
+    ( 49, 49, 49)     # rgb = (0x31,0x31,0x31)
+    ( 25, 25, 42)     # rgb = (0x19,0x19,0x2a)
+    (  7,  7, 64)     # rgb = (0x07,0x07,0x40)
+    ( 18, 18,174)     # rgb = (0x12,0x12,0xae)
+    (  9,  9,238)     # rgb = (0x09,0x09,0xee)
+    (211,214,211)     # rgb = (0xd3,0xd6,0xd3)
+    (204,204,204)     # rgb = (0xcc,0xcc,0xcc) grey80
+    (147,  0,  0)     # rgb = (0x93,0x00,0x00)
+    (163, 42, 42)     # rgb = (0xa3,0x2a,0x2a)
+    (198,198,198)     # rgb = (0xc6,0xc6,0xc6)
+    (196,196,196)     # rgb = (0xc4,0xc4,0xc4) grey77
+    (204,  0,  0)     # rgb = (0xcc,0x00,0x00)
+    (211, 10, 10)     # rgb = (0xd3,0x0a,0x0a)
+    (129,107,107)     # rgb = (0x81,0x6b,0x6b)
+    (120, 62, 62)     # rgb = (0x78,0x3e,0x3e)
+    (  3,  3,109)     # rgb = (0x03,0x03,0x6d)
+    (  0,  0,159)     # rgb = (0x00,0x00,0x9f)
+    ( 10, 10, 86)     # rgb = (0x0a,0x0a,0x56)
+    ( 70, 70, 72)     # rgb = (0x46,0x46,0x48)
+    ( 65, 65, 77)     # rgb = (0x41,0x41,0x4d)
+    (115, 93, 93)     # rgb = (0x73,0x5d,0x5d)
+    ( 81,  7,  7)     # rgb = (0x51,0x07,0x07)
+    (168,168,168)     # rgb = (0xa8,0xa8,0xa8) grey66
+    (237,237,239)     # rgb = (0xed,0xed,0xef)
+    (160,160,160)     # rgb = (0xa0,0xa0,0xa0)
+    (158,158,158)     # rgb = (0x9e,0x9e,0x9e) grey62
+    (156,156,156)     # rgb = (0x9c,0x9c,0x9c) grey61
+    (  0,  0,185)     # rgb = (0x00,0x00,0xb9)
+    (154,154,154)     # rgb = (0x9a,0x9a,0x9a)
+    (178,  0,  0)     # rgb = (0xb2,0x00,0x00)
+    (152,152,152)     # rgb = (0x98,0x98,0x98)
+    (235,  0,  0)     # rgb = (0xeb,0x00,0x00)
+    (150,150,150)     # rgb = (0x96,0x96,0x96) grey59
+    (158,  0,  0)     # rgb = (0x9e,0x00,0x00)
+    (148,148,148)     # rgb = (0x94,0x94,0x94) grey58
+    ( 19, 19, 28)     # rgb = (0x13,0x13,0x1c)
+    (146,146,146)     # rgb = (0x92,0x92,0x92)
+    (144,144,144)     # rgb = (0x90,0x90,0x90)
+    (142,142,142)     # rgb = (0x8e,0x8e,0x8e)
+    (  0,  0,145)     # rgb = (0x00,0x00,0x91)
+    (138,138,138)     # rgb = (0x8a,0x8a,0x8a) grey54
+    (136,136,136)     # rgb = (0x88,0x88,0x88)
+    (118,162,118)     # rgb = (0x76,0xa2,0x76)
+    (133,136,133)     # rgb = (0x85,0x88,0x85)
+    (134,134,134)     # rgb = (0x86,0x86,0x86)
+    (132,132,132)     # rgb = (0x84,0x84,0x84)
+    (120, 15, 15)     # rgb = (0x78,0x0f,0x0f)
+    (130,130,130)     # rgb = (0x82,0x82,0x82) grey51
+    (126,130,126)     # rgb = (0x7e,0x82,0x7e)
+    (126,126,126)     # rgb = (0x7e,0x7e,0x7e)
+    (124,124,124)     # rgb = (0x7c,0x7c,0x7c)
+    (122,122,122)     # rgb = (0x7a,0x7a,0x7a) grey48
+    ( 74,192, 74)     # rgb = (0x4a,0xc0,0x4a)
+    (118,118,118)     # rgb = (0x76,0x76,0x76)
+    (116,116,116)     # rgb = (0x74,0x74,0x74)
+    (114,114,114)     # rgb = (0x72,0x72,0x72)
+    (112,112,112)     # rgb = (0x70,0x70,0x70) grey44
+    (152,  0,  0)     # rgb = (0x98,0x00,0x00)
+    (110,110,110)     # rgb = (0x6e,0x6e,0x6e) grey43
+    (106,112,106)     # rgb = (0x6a,0x70,0x6a)
+    (122,102,102)     # rgb = (0x7a,0x66,0x66)
+    (106,106,106)     # rgb = (0x6a,0x6a,0x6a)
+    (132,  0,  0)     # rgb = (0x84,0x00,0x00)
+    ( 68,162, 68)     # rgb = (0x44,0xa2,0x44)
+    ( 75,150, 75)     # rgb = (0x4b,0x96,0x4b)
+    ( 97,100, 97)     # rgb = (0x61,0x64,0x61)
+    ( 98, 98, 98)     # rgb = (0x62,0x62,0x62)
+    (  0,244,  0)     # rgb = (0x00,0xf4,0x00)
+    ( 56,152, 56)     # rgb = (0x38,0x98,0x38)
+    ( 92, 92, 92)     # rgb = (0x5c,0x5c,0x5c) grey36
+    ( 90, 90, 90)     # rgb = (0x5a,0x5a,0x5a)
+    (  0,230,  0)     # rgb = (0x00,0xe6,0x00)
+    (  2,  2, 93)     # rgb = (0x02,0x02,0x5d)
+    ( 66,120, 66)     # rgb = (0x42,0x78,0x42)
+    ( 86, 86, 86)     # rgb = (0x56,0x56,0x56)
+    (  0,  0,240)     # rgb = (0x00,0x00,0xf0)
+    ( 46,148, 46)     # rgb = (0x2e,0x94,0x2e)
+    ( 71,104, 71)     # rgb = (0x47,0x68,0x47)
+    ( 49, 49, 96)     # rgb = (0x31,0x31,0x60)
+    (  0,216,  0)     # rgb = (0x00,0xd8,0x00)
+    ( 82, 82, 82)     # rgb = (0x52,0x52,0x52) grey32
+    ( 80, 80, 80)     # rgb = (0x50,0x50,0x50)
+    (  0,206,  0)     # rgb = (0x00,0xce,0x00)
+    ( 33,152, 33)     # rgb = (0x21,0x98,0x21)
+    ( 20, 20,109)     # rgb = (0x14,0x14,0x6d)
+    (  0,200,  0)     # rgb = (0x00,0xc8,0x00)
+    ( 76, 76, 76)     # rgb = (0x4c,0x4c,0x4c)
+    (253,253,253)     # rgb = (0xfd,0xfd,0xfd)
+    (  0,198,  0)     # rgb = (0x00,0xc6,0x00)
+    (  0,  0,157)     # rgb = (0x00,0x00,0x9d)
+    (111,107,107)     # rgb = (0x6f,0x6b,0x6b)
+    (234, 14, 14)     # rgb = (0xea,0x0e,0x0e)
+    ( 72, 72, 72)     # rgb = (0x48,0x48,0x48)
+    (  0,188,  0)     # rgb = (0x00,0xbc,0x00)
+    ( 52,102, 52)     # rgb = (0x34,0x66,0x34)
+    (  2,  2,245)     # rgb = (0x02,0x02,0xf5)
+    ( 83, 83, 96)     # rgb = (0x53,0x53,0x60)
+    (  0,176,  0)     # rgb = (0x00,0xb0,0x00)
+    (  0,174,  0)     # rgb = (0x00,0xae,0x00)
+    (183,  0,  0)     # rgb = (0xb7,0x00,0x00)
+    (  0,164,  0)     # rgb = (0x00,0xa4,0x00)
+    (239,239,239)     # rgb = (0xef,0xef,0xef)
+    (  0,162,  0)     # rgb = (0x00,0xa2,0x00)
+    (143, 79, 79)     # rgb = (0x8f,0x4f,0x4f)
+    (149, 52, 52)     # rgb = (0x95,0x34,0x34)
+    (  0,152,  0)     # rgb = (0x00,0x98,0x00)
+    (  0,150,  0)     # rgb = (0x00,0x96,0x00)
+    (  0,146,  0)     # rgb = (0x00,0x92,0x00)
+    (231,231,231)     # rgb = (0xe7,0xe7,0xe7)
+    (  0,140,  0)     # rgb = (0x00,0x8c,0x00)
+    (227,227,227)     # rgb = (0xe3,0xe3,0xe3) grey89
+    (  0,128,  0)     # rgb = (0x00,0x80,0x00)
+    (146,  6,  6)     # rgb = (0x92,0x06,0x06)
+    (  1,  1,111)     # rgb = (0x01,0x01,0x6f)
+    (100, 86, 89)     # rgb = (0x64,0x56,0x59)
+    (  0,  0,100)     # rgb = (0x00,0x00,0x64)
+    ( 78, 78,107)     # rgb = (0x4e,0x4e,0x6b)
+    (207,207,207)     # rgb = (0xcf,0xcf,0xcf) grey81
+    (221,221,224)     # rgb = (0xdd,0xdd,0xe0)
+    (  0,  0,123)     # rgb = (0x00,0x00,0x7b)
+    (201,201,201)     # rgb = (0xc9,0xc9,0xc9) grey79
+    ( 22, 22, 65)     # rgb = (0x16,0x16,0x41)
+    ( 33, 33, 89)     # rgb = (0x21,0x21,0x59)
+    ( 87, 87, 89)     # rgb = (0x57,0x57,0x59)
+    ( 68, 68,120)     # rgb = (0x44,0x44,0x78)
+    (191,191,191)     # rgb = (0xbf,0xbf,0xbf) grey75
+    (235,221,221)     # rgb = (0xeb,0xdd,0xdd)
+    ( 45, 45, 84)     # rgb = (0x2d,0x2d,0x54)
+    ( 10, 10, 96)     # rgb = (0x0a,0x0a,0x60)
+    (  0,  0,255)     # rgb = (0x00,0x00,0xff) blue1
+    (191,125,125)     # rgb = (0xbf,0x7d,0x7d)
+    (255,255,  0)     # rgb = (0xff,0xff,0x00) yellow1
+}
+bKGD {index: 245}
+tRNS {
+ 0}
+IMAGE {
+    pixels hex
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000e0ea66000000000000000000000000000000
+0000000000000000000000de02a336e43903f4f0000000000000000000000000
+000000000000000069ef1a358680062eb017b0ab7af459500000000000000000
+0000000000667c0ea9cc803979937917a03a878787b0e2ae8ae75c0000000000
+00005cea8ea72c8639e293208f7d7d19200639a017ab2ee4ac2ca7097c690000
+00007823a72b2bda198fd54ddad90521219191217d1917cc2b2b2b2baf8e0000
+0000e81f9b9f27014d05d91c2a2a2a7f037ecdcd7e7a012a2a2aaab7c2ef0000
+00006c9f229d981a23282828282828282828282828282828a7b445c3c8de0000
+00005ca249d63d140f139f272727272727272727a5a528af44c3c8ce43000000
+0000009a62ca41a6960e0d941da4a4a4a4a4a4a4a4a9b732525a1084a1000000
+000000965b58b53811940d0b090b1823a3a3252ab4d24c269957571088000000
+000000946162b9b59c0f14b12d0c8b8c98a3afb8ed1bbd82ba74300877000000
+00000088c565c7b5a6962dcf67be07048aa5b84315f326ba7395832950000000
+00000002bed8d4b94214b1c7dbb68c8b04a843e6d1bd814bceeb10a900000000
+0000007b47636ec441b23d4edb3f09078bac4315f340ec855a82995f00000000
+00000059bb63e15d42643dca6b3f8e090735ed76bd81c05224e9f27b00000000
+0000006cbbd47161c1684951dc3f908e8c3ceef38d08ebe96d6d086000000000
+00000050bf67dc54534fdd53ddb20d0b8eb815d10af1732fe312e60000000000
+00000000add6d6bf61c16f566eb20e0d924475bd578572c61e6d340000000000
+0000000016d8d3d03ec76bcfdf3b0f0e13bc4c8d2f84c040cb837b0000000000
+00000000550c47b3365bd45d6f33110f1a4575cbf2c0521e0802000000000000
+000000000000e7ac36be625e7031131122455a0a2f0a99c6e700000000000000
+000000000000006a9e37d36270331613a545f181e53032e80000000000000000
+00000000000000005088c5d371311816a8464b7374ee89000000000000000000
+0000000000000000000077b654a29b18acc24a722a5500000000000000000000
+0000000000000000000000d78a9f9e9b3548c38ac90000000000000000000000
+00000000000000000000000000ef1f9e3cc20200000000000000000000000000
+0000000000000000000000000000e89736780000000000000000000000000000
+00000000000000000000000000000060e0000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+}
diff --git a/src/image/png/testdata/pngsuite/ftp0n0g08.png b/src/image/png/testdata/pngsuite/ftp0n0g08.png
new file mode 100644
index 0000000..333465f
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftp0n0g08.png differ
diff --git a/src/image/png/testdata/pngsuite/ftp0n0g08.sng b/src/image/png/testdata/pngsuite/ftp0n0g08.sng
new file mode 100644
index 0000000..c8abd33
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftp0n0g08.sng
@@ -0,0 +1,41 @@
+#SNG: from ftp0n0g08.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using grayscale;
+}
+gAMA {1.0000}
+IMAGE {
+    pixels hex
+7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7f7f7f7f7f7f7fe3c9f17f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7f7f7f7fe8b57e655a4661a1e17f7f7f7f7f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7feebf8967614d362a2824282f5091d4fc7f7f7f7f7f7f7f7f
+7f7f7f7f7ff2c495726c533e2e302c272b3d23181b28306ca0cff87f7f7f7f7f
+7f7ff7ca9a76736649313033353c3e38313c4533272d2b5a6e73769bc4ee7f7f
+7f7fcc7f7675754e3736373c55645042444845423e38266c757575756a9a7f7f
+7f7fdc8588847b6344476276777777726157524e4b4e63777777705c52bd7f7f
+7f7fea8481878e897f797979797979797979797979797979766354504ce67f7f
+7f7ff88268627890938f837b7b7b7b7b7b7b7b7b7a7a796a57504c49597f7f7f
+7f7f7f8a69748e8f92959794877c7c7c7c7c7c7c7c715b3314182d46827f7f7f
+7f7f7f92696f8f8f919497999b998b7f7e7e7d7762542517110c0c2fac7f7f7f
+7f7f7f946769848f9093937a7f979e9c8e7e6a5a572d1b120d080b24d67f7f7f
+7f7f7fa46764758f8f92836e636e9d9fa07a5a593e1d160d1a0d143efb7f7f7f
+7f7f7fb6665f658a8e907e765a6e9c9e9f745953201b0f3a481a30727f7f7f7f
+7f7f7fc76468537b8e7a757559709b9d9e6e593a1d132b442212119d7f7f7f7f
+7f7f7fd86368486c8d5b7474586f9a9b9d6757231b103717120e16c77f7f7f7f
+7f7f7fea61654e5d816268725870989a9c61491d15221c0e0c0c23f47f7f7f7f
+7f7f7ffc6063575871735771547497999a5a391c0f331c0d0d12517f7f7f7f7f
+7f7f7f7f6e606055697c51705373959796572c190c402b17140c877f7f7f7f7f
+7f7f7f7f8c5f67516876586e527193958f5622150e4735121212c57f7f7f7f7f
+7f7f7f7ffaa363636769666c5175919389551c121139151420b67f7f7f7f7f7f
+7f7f7f7f7f7fd06d6566686b50758f918155210f0c101025ce7f7f7f7f7f7f7f
+7f7f7f7f7f7f7fec8666676950738d8f7a542f0e0b0b31e07f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7ffcaa67674f738b8d74533d1b134ced7f7f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7f7f7fd472587f888b6d524f3777fa7f7f7f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7f7f7f7fefa0838688675150a0fd7f7f7f7f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7f7f7f7f7ffec085866152b77f7f7f7f7f7f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7f7f7f7f7f7f7fde9065cc7f7f7f7f7f7f7f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ff5e37f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
+7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
+}
diff --git a/src/image/png/testdata/pngsuite/ftp0n2c08.png b/src/image/png/testdata/pngsuite/ftp0n2c08.png
new file mode 100644
index 0000000..fc6e42c
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftp0n2c08.png differ
diff --git a/src/image/png/testdata/pngsuite/ftp0n2c08.sng b/src/image/png/testdata/pngsuite/ftp0n2c08.sng
new file mode 100644
index 0000000..d41c7eb
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftp0n2c08.sng
@@ -0,0 +1,41 @@
+#SNG: from ftp0n2c08.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using color;
+}
+gAMA {1.0000}
+IMAGE {
+    pixels hex
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f e3e3e3 c9c9c9 f1f1f1 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f e8e8e8 b5b5b5 7e7e7e 656565 6e5252 7e2e2e a64343 c79090 ebdddd 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f eeeeee bfbfbf 898989 676767 6b5d5d 7a3939 8a1212 8d0000 850000 770000 840000 9a0101 a22d2d bf7d7d ddd0d0 fcfcfc 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f f2f2f2 c4c4c4 959595 727272 6f6b6b 774444 871e1e 950101 9f0000 910000 800000 720c0c 612d2d 530e0e 500000 590000 850000 920606 7a6666 a0a0a0 cfcfcf f8f8f8 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f f7f7f7 cacaca 9a9a9a 767676 737373 7c5d5d 872e2e 930707 9e0000 a90000 b00000 c90000 cf0000 b90000 a20101 8c1919 852a2a 7f1313 810000 960000 8f0000 6b5353 6e6e6e 737373 767676 9b9b9b c4c4c4 eeeeee 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f cccccc 7f7f7f 767676 757575 757575 962f2f b80000 b40000 b60000 ad0c0c 943a3a 925050 b92323 d60202 e20000 ef0000 e70000 da0000 cf0000 ba0000 7d0101 6f6b6b 757575 757575 757575 757575 6a6a6a 9a9a9a 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f dcdcdc 858585 888888 848484 7b7b7b 855454 b71313 a91d1d 8d4f4f 787575 777777 777777 777777 816b6b aa4141 d62020 ec1010 e90c0c d01212 a52828 7b5858 777777 777777 777777 707070 5c5c5c 525252 bdbdbd 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f eaeaea 848484 818181 858885 8e8e8e 898989 7f7f7f 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 797979 767676 636363 545454 505050 4c4c4c e6e6e6 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f f8f8f8 7f847f 259725 04a504 39a439 8b948b 939393 8f8f8f 838383 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7b7b7b 7a7a7a 7a7a7a 797979 6a6a6a 575757 505050 4c4c4c 494949 595959 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 8a8a8a 01b301 00c600 00f200 59b659 929292 959595 979797 949494 878787 7c7c7c 7c7c7c 7c7c7c 7c7c7c 7c7c7c 7c7c7c 7c7c7c 7c7c7c 717171 5a5a60 282885 040493 0c0c78 282858 46464a 828282 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 929292 0cab0c 00bd00 00f400 20dd20 919191 949494 979797 999999 9b9b9b 999999 8b8b8b 7f7f7f 7e7e7e 7e7e7e 7d7d7d 777777 626262 535360 1212be 0000cc 000092 000069 000067 2a2a55 acacac 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 949494 16a116 00b400 00e200 00f400 76a276 939393 8d978d 469e46 46a746 8e9e8e 9e9e9e 9c9c9c 8e8e8e 7e7e7e 6a6a6a 5a5a5a 57575a 1818cd 0000f0 0000a0 020260 01013d 000061 1d1d59 d6d6d6 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f a4a4a4 219821 00aa00 00c800 00f400 3bca3b 929292 4aac4a 00bc00 00a900 2f9a2f 9d9d9d 9f9f9f a0a0a0 7a7a7a 5a5a5a 595959 3131a1 0000ff 0000c6 03035b 191924 0c0c15 0c0c55 3b3b53 fbfbfb 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f b6b6b6 2b8f2b 00a200 00ad00 00eb00 07ed07 899589 43a743 00c900 009900 389538 9c9c9c 9e9e9e 9f9f9f 747474 595959 505067 0505f5 0000f0 030370 383846 484848 161639 2b2b55 727272 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f c7c7c7 348634 00b100 008d00 00d200 00f300 4c9b4c 3b9e3b 00c700 009800 3d943d 9b9b9b 9d9d9d 9e9e9e 6e6e6e 595959 2b2bad 0000ff 0000a6 252559 43434f 16167e 00009f 01018e 9c9ca1 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f d8d8d8 3e7d3e 00b100 007b00 00b800 00f100 178b17 3b9c3b 00c600 009700 3d933d 9a9a9a 9b9b9b 9d9d9d 676767 575759 0909ee 0000f0 04046b 33335a 070790 00009e 00007c 0d0d5d c7c7c7 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f eaeaea 477447 00ad00 008500 099809 00dc00 00a700 239823 00c300 009600 3f923f 989898 9a9a9a 9c9c9c 616161 42427f 0000ff 0000b9 1a1a5d 161649 00007b 00006b 00006b 1c1c56 f4f4f4 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f fcfcfc 506c50 00a900 009500 2d772d 00c100 00c500 019301 00c100 009000 4b914b 979797 999999 9a9a9a 5a5a5a 2b2ba4 0000f6 000086 2f2f53 191930 020263 000073 00009b 4d4d70 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 687368 00a400 00a400 3e653e 14a514 00d400 008b00 00bf00 008e00 4a904a 959595 979797 969696 575757 1a1ab5 0000de 000068 3f3f4b 2b2b2b 0c0c6d 0000b3 00006b 868692 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 8c8c8c 059e05 00b000 346634 408540 00ca00 009700 00bc00 008c00 498e49 939393 959595 8f8f8f 565656 0f0fb7 0000b9 030366 474747 2f2f64 0000a2 00009d 090958 c5c5c5 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f fafafa 90b090 348534 616461 636a63 06af06 00ae00 00b900 008b00 538d53 919191 939393 898989 555555 0a0aa8 00009d 070763 34345c 04049b 0000b1 1a1a4d b5b5bb 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f d0d0d0 6d6d6d 656565 2d8f2d 00b200 00b600 008900 558b55 8f8f8f 919191 818181 555555 15157e 000084 010165 010184 000091 1c1c6e ceced0 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f ececec 868686 587058 00af00 00b300 008800 538953 8d8d8d 8f8f8f 7a7a7a 545454 2c2c49 02026b 000064 000063 292974 dfdfe5 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f fcfcfc aaaaaa 219821 00b000 008600 578757 8b8b8b 8d8d8d 747474 535353 3d3d3d 1a1a23 0d0d43 474772 ededef 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f d1d6d1 389b38 2d772d 7d817d 888888 8b8b8b 6d6d6d 525252 4f4f4f 373737 777777 fafafa 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f efefef a0a0a0 838383 868686 888888 676767 515151 505050 a0a0a0 fdfdfd 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f fefefe c0c0c0 858585 868686 616161 525252 b7b7b7 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f dedede 909090 656565 cccccc 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f f5f5f5 e3e3e3 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 7f7f7f 
+}
diff --git a/src/image/png/testdata/pngsuite/ftp0n3p08.png b/src/image/png/testdata/pngsuite/ftp0n3p08.png
new file mode 100644
index 0000000..69a69e5
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftp0n3p08.png differ
diff --git a/src/image/png/testdata/pngsuite/ftp0n3p08.sng b/src/image/png/testdata/pngsuite/ftp0n3p08.sng
new file mode 100644
index 0000000..f1f8448
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftp0n3p08.sng
@@ -0,0 +1,288 @@
+#SNG: from ftp0n3p08.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using color palette;
+}
+gAMA {1.0000}
+PLTE {
+    ( 20, 20,109)     # rgb = (0x14,0x14,0x6d)
+    (128, 86, 86)     # rgb = (0x80,0x56,0x56)
+    (181,181,184)     # rgb = (0xb5,0xb5,0xb8)
+    (168, 66, 66)     # rgb = (0xa8,0x42,0x42)
+    (159,159,159)     # rgb = (0x9f,0x9f,0x9f)
+    (177, 32, 32)     # rgb = (0xb1,0x20,0x20)
+    (139, 21, 21)     # rgb = (0x8b,0x15,0x15)
+    (157,157,157)     # rgb = (0x9d,0x9d,0x9d)
+    ( 27, 27, 89)     # rgb = (0x1b,0x1b,0x59)
+    (155,155,155)     # rgb = (0x9b,0x9b,0x9b)
+    (  0,  0,132)     # rgb = (0x00,0x00,0x84)
+    (153,153,153)     # rgb = (0x99,0x99,0x99) grey60
+    (143,167,143)     # rgb = (0x8f,0xa7,0x8f)
+    (151,151,151)     # rgb = (0x97,0x97,0x97)
+    (149,149,149)     # rgb = (0x95,0x95,0x95)
+    (147,147,147)     # rgb = (0x93,0x93,0x93)
+    ( 41, 41, 86)     # rgb = (0x29,0x29,0x56)
+    (145,145,145)     # rgb = (0x91,0x91,0x91) grey57
+    (  0,  0,155)     # rgb = (0x00,0x00,0x9b)
+    (143,143,143)     # rgb = (0x8f,0x8f,0x8f) grey56
+    (139,149,139)     # rgb = (0x8b,0x95,0x8b)
+    ( 46, 46,167)     # rgb = (0x2e,0x2e,0xa7)
+    (141,141,141)     # rgb = (0x8d,0x8d,0x8d)
+    (128,  0,  0)     # rgb = (0x80,0x00,0x00)
+    (139,139,139)     # rgb = (0x8b,0x8b,0x8b)
+    (185,  0,  0)     # rgb = (0xb9,0x00,0x00)
+    (137,137,137)     # rgb = (0x89,0x89,0x89)
+    ( 12, 12,213)     # rgb = (0x0c,0x0c,0xd5)
+    (120,117,117)     # rgb = (0x78,0x75,0x75)
+    (135,135,135)     # rgb = (0x87,0x87,0x87) grey53
+    (  0,  0,178)     # rgb = (0x00,0x00,0xb2)
+    (133,133,133)     # rgb = (0x85,0x85,0x85) grey52
+    (165,  0,  0)     # rgb = (0xa5,0x00,0x00)
+    (222,  0,  0)     # rgb = (0xde,0x00,0x00)
+    (129,129,129)     # rgb = (0x81,0x81,0x81)
+    (127,127,127)     # rgb = (0x7f,0x7f,0x7f) grey50
+    (  0,  0,158)     # rgb = (0x00,0x00,0x9e)
+    (125,125,125)     # rgb = (0x7d,0x7d,0x7d) grey49
+    (  0,  0,201)     # rgb = (0x00,0x00,0xc9)
+    (123,123,123)     # rgb = (0x7b,0x7b,0x7b)
+    (121,121,121)     # rgb = (0x79,0x79,0x79)
+    ( 55, 55, 86)     # rgb = (0x37,0x37,0x56)
+    (119,119,119)     # rgb = (0x77,0x77,0x77)
+    (117,117,117)     # rgb = (0x75,0x75,0x75) grey46
+    (115,115,115)     # rgb = (0x73,0x73,0x73) grey45
+    ( 72,169, 72)     # rgb = (0x48,0xa9,0x48)
+    (142,  0,  0)     # rgb = (0x8e,0x00,0x00)
+    (  2,  2,100)     # rgb = (0x02,0x02,0x64)
+    (  0,  0, 98)     # rgb = (0x00,0x00,0x62)
+    ( 86,137, 86)     # rgb = (0x56,0x89,0x56)
+    ( 40, 40,124)     # rgb = (0x28,0x28,0x7c)
+    ( 83,139, 83)     # rgb = (0x53,0x8b,0x53)
+    (137,137,143)     # rgb = (0x89,0x89,0x8f)
+    (103,103,103)     # rgb = (0x67,0x67,0x67)
+    (101,101,101)     # rgb = (0x65,0x65,0x65)
+    ( 93,109, 93)     # rgb = (0x5d,0x6d,0x5d)
+    ( 19,229, 19)     # rgb = (0x13,0xe5,0x13)
+    (134, 38, 38)     # rgb = (0x86,0x26,0x26)
+    (111, 45, 45)     # rgb = (0x6f,0x2d,0x2d)
+    ( 68,145, 68)     # rgb = (0x44,0x91,0x44)
+    ( 97, 97, 97)     # rgb = (0x61,0x61,0x61) grey38
+    ( 59,157, 59)     # rgb = (0x3b,0x9d,0x3b)
+    ( 68,137, 68)     # rgb = (0x44,0x89,0x44)
+    ( 61,147, 61)     # rgb = (0x3d,0x93,0x3d)
+    (  0,  0,164)     # rgb = (0x00,0x00,0xa4)
+    (  0,243,  0)     # rgb = (0x00,0xf3,0x00)
+    (  0,241,  0)     # rgb = (0x00,0xf1,0x00)
+    ( 89, 89, 89)     # rgb = (0x59,0x59,0x59) grey35
+    ( 87, 87, 87)     # rgb = (0x57,0x57,0x57) grey34
+    ( 85, 85, 85)     # rgb = (0x55,0x55,0x55)
+    ( 83, 83, 83)     # rgb = (0x53,0x53,0x53)
+    ( 52,133, 52)     # rgb = (0x34,0x85,0x34)
+    ( 81, 81, 81)     # rgb = (0x51,0x51,0x51)
+    ( 36,151, 36)     # rgb = (0x24,0x97,0x24)
+    ( 79, 79, 79)     # rgb = (0x4f,0x4f,0x4f) grey31
+    ( 58, 58, 65)     # rgb = (0x3a,0x3a,0x41)
+    ( 16, 16,186)     # rgb = (0x10,0x10,0xba)
+    (178, 15, 15)     # rgb = (0xb2,0x0f,0x0f)
+    (  0,199,  0)     # rgb = (0x00,0xc7,0x00)
+    (  0,197,  0)     # rgb = (0x00,0xc5,0x00)
+    (252,252,252)     # rgb = (0xfc,0xfc,0xfc) grey99
+    (  0,195,  0)     # rgb = (0x00,0xc3,0x00)
+    (  4,  4,151)     # rgb = (0x04,0x04,0x97)
+    (  0,193,  0)     # rgb = (0x00,0xc1,0x00)
+    ( 45,119, 45)     # rgb = (0x2d,0x77,0x2d)
+    (250,250,250)     # rgb = (0xfa,0xfa,0xfa) grey98
+    (  0,191,  0)     # rgb = (0x00,0xbf,0x00)
+    (  0,  0,104)     # rgb = (0x00,0x00,0x68)
+    (  0,189,  0)     # rgb = (0x00,0xbd,0x00)
+    (218,212,212)     # rgb = (0xda,0xd4,0xd4)
+    ( 16, 16,123)     # rgb = (0x10,0x10,0x7b)
+    (  9,173,  9)     # rgb = (0x09,0xad,0x09)
+    (248,248,248)     # rgb = (0xf8,0xf8,0xf8)
+    (  0,185,  0)     # rgb = (0x00,0xb9,0x00)
+    (  0,183,  0)     # rgb = (0x00,0xb7,0x00)
+    (156,156,161)     # rgb = (0x9c,0x9c,0xa1)
+    (246,246,246)     # rgb = (0xf6,0xf6,0xf6)
+    ( 12,161, 12)     # rgb = (0x0c,0xa1,0x0c)
+    (  0,179,  0)     # rgb = (0x00,0xb3,0x00)
+    (  0,177,  0)     # rgb = (0x00,0xb1,0x00)
+    ( 16,145, 16)     # rgb = (0x10,0x91,0x10)
+    (  0,171,  0)     # rgb = (0x00,0xab,0x00)
+    (242,242,242)     # rgb = (0xf2,0xf2,0xf2) grey95
+    (  0,169,  0)     # rgb = (0x00,0xa9,0x00)
+    (  0,167,  0)     # rgb = (0x00,0xa7,0x00)
+    (238,238,238)     # rgb = (0xee,0xee,0xee)
+    (236,236,236)     # rgb = (0xec,0xec,0xec)
+    (  0,151,  0)     # rgb = (0x00,0x97,0x00)
+    (234,234,234)     # rgb = (0xea,0xea,0xea)
+    (  0,  0,107)     # rgb = (0x00,0x00,0x6b)
+    (  0,141,  0)     # rgb = (0x00,0x8d,0x00)
+    (  0,139,  0)     # rgb = (0x00,0x8b,0x00) green4
+    (  0,137,  0)     # rgb = (0x00,0x89,0x00)
+    (  0,135,  0)     # rgb = (0x00,0x87,0x00)
+    ( 49, 49, 49)     # rgb = (0x31,0x31,0x31)
+    ( 25, 25, 42)     # rgb = (0x19,0x19,0x2a)
+    (  7,  7, 64)     # rgb = (0x07,0x07,0x40)
+    ( 18, 18,174)     # rgb = (0x12,0x12,0xae)
+    (  9,  9,238)     # rgb = (0x09,0x09,0xee)
+    (211,214,211)     # rgb = (0xd3,0xd6,0xd3)
+    (204,204,204)     # rgb = (0xcc,0xcc,0xcc) grey80
+    (147,  0,  0)     # rgb = (0x93,0x00,0x00)
+    (163, 42, 42)     # rgb = (0xa3,0x2a,0x2a)
+    (198,198,198)     # rgb = (0xc6,0xc6,0xc6)
+    (196,196,196)     # rgb = (0xc4,0xc4,0xc4) grey77
+    (204,  0,  0)     # rgb = (0xcc,0x00,0x00)
+    (211, 10, 10)     # rgb = (0xd3,0x0a,0x0a)
+    (129,107,107)     # rgb = (0x81,0x6b,0x6b)
+    (120, 62, 62)     # rgb = (0x78,0x3e,0x3e)
+    (  3,  3,109)     # rgb = (0x03,0x03,0x6d)
+    (  0,  0,159)     # rgb = (0x00,0x00,0x9f)
+    ( 10, 10, 86)     # rgb = (0x0a,0x0a,0x56)
+    ( 70, 70, 72)     # rgb = (0x46,0x46,0x48)
+    ( 65, 65, 77)     # rgb = (0x41,0x41,0x4d)
+    (115, 93, 93)     # rgb = (0x73,0x5d,0x5d)
+    ( 81,  7,  7)     # rgb = (0x51,0x07,0x07)
+    (168,168,168)     # rgb = (0xa8,0xa8,0xa8) grey66
+    (237,237,239)     # rgb = (0xed,0xed,0xef)
+    (160,160,160)     # rgb = (0xa0,0xa0,0xa0)
+    (158,158,158)     # rgb = (0x9e,0x9e,0x9e) grey62
+    (156,156,156)     # rgb = (0x9c,0x9c,0x9c) grey61
+    (  0,  0,185)     # rgb = (0x00,0x00,0xb9)
+    (154,154,154)     # rgb = (0x9a,0x9a,0x9a)
+    (178,  0,  0)     # rgb = (0xb2,0x00,0x00)
+    (152,152,152)     # rgb = (0x98,0x98,0x98)
+    (235,  0,  0)     # rgb = (0xeb,0x00,0x00)
+    (150,150,150)     # rgb = (0x96,0x96,0x96) grey59
+    (158,  0,  0)     # rgb = (0x9e,0x00,0x00)
+    (148,148,148)     # rgb = (0x94,0x94,0x94) grey58
+    ( 19, 19, 28)     # rgb = (0x13,0x13,0x1c)
+    (146,146,146)     # rgb = (0x92,0x92,0x92)
+    (144,144,144)     # rgb = (0x90,0x90,0x90)
+    (142,142,142)     # rgb = (0x8e,0x8e,0x8e)
+    (  0,  0,145)     # rgb = (0x00,0x00,0x91)
+    (138,138,138)     # rgb = (0x8a,0x8a,0x8a) grey54
+    (136,136,136)     # rgb = (0x88,0x88,0x88)
+    (118,162,118)     # rgb = (0x76,0xa2,0x76)
+    (133,136,133)     # rgb = (0x85,0x88,0x85)
+    (134,134,134)     # rgb = (0x86,0x86,0x86)
+    (132,132,132)     # rgb = (0x84,0x84,0x84)
+    (120, 15, 15)     # rgb = (0x78,0x0f,0x0f)
+    (130,130,130)     # rgb = (0x82,0x82,0x82) grey51
+    (126,130,126)     # rgb = (0x7e,0x82,0x7e)
+    (126,126,126)     # rgb = (0x7e,0x7e,0x7e)
+    (124,124,124)     # rgb = (0x7c,0x7c,0x7c)
+    (122,122,122)     # rgb = (0x7a,0x7a,0x7a) grey48
+    ( 74,192, 74)     # rgb = (0x4a,0xc0,0x4a)
+    (118,118,118)     # rgb = (0x76,0x76,0x76)
+    (116,116,116)     # rgb = (0x74,0x74,0x74)
+    (114,114,114)     # rgb = (0x72,0x72,0x72)
+    (112,112,112)     # rgb = (0x70,0x70,0x70) grey44
+    (152,  0,  0)     # rgb = (0x98,0x00,0x00)
+    (110,110,110)     # rgb = (0x6e,0x6e,0x6e) grey43
+    (106,112,106)     # rgb = (0x6a,0x70,0x6a)
+    (122,102,102)     # rgb = (0x7a,0x66,0x66)
+    (106,106,106)     # rgb = (0x6a,0x6a,0x6a)
+    (132,  0,  0)     # rgb = (0x84,0x00,0x00)
+    ( 68,162, 68)     # rgb = (0x44,0xa2,0x44)
+    ( 75,150, 75)     # rgb = (0x4b,0x96,0x4b)
+    ( 97,100, 97)     # rgb = (0x61,0x64,0x61)
+    ( 98, 98, 98)     # rgb = (0x62,0x62,0x62)
+    (  0,244,  0)     # rgb = (0x00,0xf4,0x00)
+    ( 56,152, 56)     # rgb = (0x38,0x98,0x38)
+    ( 92, 92, 92)     # rgb = (0x5c,0x5c,0x5c) grey36
+    ( 90, 90, 90)     # rgb = (0x5a,0x5a,0x5a)
+    (  0,230,  0)     # rgb = (0x00,0xe6,0x00)
+    (  2,  2, 93)     # rgb = (0x02,0x02,0x5d)
+    ( 66,120, 66)     # rgb = (0x42,0x78,0x42)
+    ( 86, 86, 86)     # rgb = (0x56,0x56,0x56)
+    (  0,  0,240)     # rgb = (0x00,0x00,0xf0)
+    ( 46,148, 46)     # rgb = (0x2e,0x94,0x2e)
+    ( 71,104, 71)     # rgb = (0x47,0x68,0x47)
+    ( 49, 49, 96)     # rgb = (0x31,0x31,0x60)
+    (  0,216,  0)     # rgb = (0x00,0xd8,0x00)
+    ( 82, 82, 82)     # rgb = (0x52,0x52,0x52) grey32
+    ( 80, 80, 80)     # rgb = (0x50,0x50,0x50)
+    (  0,206,  0)     # rgb = (0x00,0xce,0x00)
+    ( 33,152, 33)     # rgb = (0x21,0x98,0x21)
+    (255,255,255)     # rgb = (0xff,0xff,0xff) grey100
+    (  0,200,  0)     # rgb = (0x00,0xc8,0x00)
+    ( 76, 76, 76)     # rgb = (0x4c,0x4c,0x4c)
+    (253,253,253)     # rgb = (0xfd,0xfd,0xfd)
+    (  0,198,  0)     # rgb = (0x00,0xc6,0x00)
+    (  0,  0,157)     # rgb = (0x00,0x00,0x9d)
+    (111,107,107)     # rgb = (0x6f,0x6b,0x6b)
+    (234, 14, 14)     # rgb = (0xea,0x0e,0x0e)
+    ( 72, 72, 72)     # rgb = (0x48,0x48,0x48)
+    (  0,188,  0)     # rgb = (0x00,0xbc,0x00)
+    ( 52,102, 52)     # rgb = (0x34,0x66,0x34)
+    (  2,  2,245)     # rgb = (0x02,0x02,0xf5)
+    ( 83, 83, 96)     # rgb = (0x53,0x53,0x60)
+    (  0,176,  0)     # rgb = (0x00,0xb0,0x00)
+    (  0,174,  0)     # rgb = (0x00,0xae,0x00)
+    (183,  0,  0)     # rgb = (0xb7,0x00,0x00)
+    (  0,164,  0)     # rgb = (0x00,0xa4,0x00)
+    (239,239,239)     # rgb = (0xef,0xef,0xef)
+    (  0,162,  0)     # rgb = (0x00,0xa2,0x00)
+    (143, 79, 79)     # rgb = (0x8f,0x4f,0x4f)
+    (149, 52, 52)     # rgb = (0x95,0x34,0x34)
+    (  0,152,  0)     # rgb = (0x00,0x98,0x00)
+    (  0,150,  0)     # rgb = (0x00,0x96,0x00)
+    (  0,146,  0)     # rgb = (0x00,0x92,0x00)
+    (231,231,231)     # rgb = (0xe7,0xe7,0xe7)
+    (  0,140,  0)     # rgb = (0x00,0x8c,0x00)
+    (227,227,227)     # rgb = (0xe3,0xe3,0xe3) grey89
+    (  0,128,  0)     # rgb = (0x00,0x80,0x00)
+    (146,  6,  6)     # rgb = (0x92,0x06,0x06)
+    (  1,  1,111)     # rgb = (0x01,0x01,0x6f)
+    (100, 86, 89)     # rgb = (0x64,0x56,0x59)
+    (  0,  0,100)     # rgb = (0x00,0x00,0x64)
+    ( 78, 78,107)     # rgb = (0x4e,0x4e,0x6b)
+    (207,207,207)     # rgb = (0xcf,0xcf,0xcf) grey81
+    (221,221,224)     # rgb = (0xdd,0xdd,0xe0)
+    (  0,  0,123)     # rgb = (0x00,0x00,0x7b)
+    (201,201,201)     # rgb = (0xc9,0xc9,0xc9) grey79
+    ( 22, 22, 65)     # rgb = (0x16,0x16,0x41)
+    ( 33, 33, 89)     # rgb = (0x21,0x21,0x59)
+    ( 87, 87, 89)     # rgb = (0x57,0x57,0x59)
+    ( 68, 68,120)     # rgb = (0x44,0x44,0x78)
+    (191,191,191)     # rgb = (0xbf,0xbf,0xbf) grey75
+    (235,221,221)     # rgb = (0xeb,0xdd,0xdd)
+    ( 45, 45, 84)     # rgb = (0x2d,0x2d,0x54)
+    ( 10, 10, 96)     # rgb = (0x0a,0x0a,0x60)
+    (  0,  0,255)     # rgb = (0x00,0x00,0xff) blue1
+    (191,125,125)     # rgb = (0xbf,0x7d,0x7d)
+}
+IMAGE {
+    pixels hex
+2323232323232323232323232323232323232323232323232323232323232323
+2323232323232323232323232323232323232323232323232323232323232323
+2323232323232323232323232323e0ea66232323232323232323232323232323
+2323232323232323232323de02a336e43903f4f0232323232323232323232323
+232323232323232369ef1a358680062eb017b0ab7af459502323232323232323
+2323232323667c0ea9cc803979937917a03a878787b0e2ae8ae75c2323232323
+23235cea8ea72c8639e293208f7d7d19200639a017ab2ee4ac2ca7097c692323
+23237823a72b2bda198fd54ddad90521219191217d1917cc2b2b2b2baf8e2323
+2323e81f9b9f27014d05d91c2a2a2a7f037ecdcd7e7a012a2a2aaab7c2ef2323
+23236c9f229d981a23282828282828282828282828282828a7b445c3c8de2323
+23235ca249d63d140f139f272727272727272727a5a528af44c3c8ce43232323
+2323239a62ca41a6960e0d941da4a4a4a4a4a4a4a4a9b732525a1084a1232323
+232323965b58b53811940d0b090b1823a3a3252ab4d24c269957571088232323
+232323946162b9b59c0f14b12d0c8b8c98a3afb8ed1bbd82ba74300877232323
+23232388c565c7b5a6962dcf67be07048aa5b84315f326ba7395832950232323
+23232302bed8d4b94214b1c7dbb68c8b04a843e6d1bd814bceeb10a923232323
+2323237b47636ec441b23d4edb3f09078bac4315f340ec855a82995f23232323
+23232359bb63e15d42643dca6b3f8e090735ed76bd81c05224e9f27b23232323
+2323236cbbd47161c1684951dc3f908e8c3ceef38d08ebe96d6d086023232323
+23232350bf67dc54534fdd53ddb20d0b8eb815d10af1732fe312e62323232323
+23232323add6d6bf61c16f566eb20e0d924475bd578572001e6d342323232323
+2323232316d8d3d03ec76bcfdf3b0f0e13bc4c8d2f84c040cb837b2323232323
+23232323550c47b3365bd45d6f33110f1a4575cbf2c0521e0802232323232323
+232323232323e7ac36be625e7031131122455a0a2f0a9900e723232323232323
+232323232323236a9e37d36270331613a545f181e53032e82323232323232323
+23232323232323235088c5d371311816a8464b7374ee89232323232323232323
+2323232323232323232377b654a29b18acc24a722a5523232323232323232323
+2323232323232323232323d78a9f9e9b3548c38ac92323232323232323232323
+232323232323232323232323c6ef1f9e3cc20223232323232323232323232323
+2323232323232323232323232323e89736782323232323232323232323232323
+23232323232323232323232323232360e0232323232323232323232323232323
+2323232323232323232323232323232323232323232323232323232323232323
+}
diff --git a/src/image/png/testdata/pngsuite/ftp1n3p08.png b/src/image/png/testdata/pngsuite/ftp1n3p08.png
new file mode 100644
index 0000000..a6c9f35
Binary files /dev/null and b/src/image/png/testdata/pngsuite/ftp1n3p08.png differ
diff --git a/src/image/png/testdata/pngsuite/ftp1n3p08.sng b/src/image/png/testdata/pngsuite/ftp1n3p08.sng
new file mode 100644
index 0000000..2d179e2
--- /dev/null
+++ b/src/image/png/testdata/pngsuite/ftp1n3p08.sng
@@ -0,0 +1,290 @@
+#SNG: from ftp1n3p08.png
+IHDR {
+    width: 32; height: 32; bitdepth: 8;
+    using color palette;
+}
+gAMA {1.0000}
+PLTE {
+    (255,255,255)     # rgb = (0xff,0xff,0xff) grey100
+    (128, 86, 86)     # rgb = (0x80,0x56,0x56)
+    (181,181,184)     # rgb = (0xb5,0xb5,0xb8)
+    (168, 66, 66)     # rgb = (0xa8,0x42,0x42)
+    (159,159,159)     # rgb = (0x9f,0x9f,0x9f)
+    (177, 32, 32)     # rgb = (0xb1,0x20,0x20)
+    (139, 21, 21)     # rgb = (0x8b,0x15,0x15)
+    (157,157,157)     # rgb = (0x9d,0x9d,0x9d)
+    ( 27, 27, 89)     # rgb = (0x1b,0x1b,0x59)
+    (155,155,155)     # rgb = (0x9b,0x9b,0x9b)
+    (  0,  0,132)     # rgb = (0x00,0x00,0x84)
+    (153,153,153)     # rgb = (0x99,0x99,0x99) grey60
+    (143,167,143)     # rgb = (0x8f,0xa7,0x8f)
+    (151,151,151)     # rgb = (0x97,0x97,0x97)
+    (149,149,149)     # rgb = (0x95,0x95,0x95)
+    (147,147,147)     # rgb = (0x93,0x93,0x93)
+    ( 41, 41, 86)     # rgb = (0x29,0x29,0x56)
+    (145,145,145)     # rgb = (0x91,0x91,0x91) grey57
+    (  0,  0,155)     # rgb = (0x00,0x00,0x9b)
+    (143,143,143)     # rgb = (0x8f,0x8f,0x8f) grey56
+    (139,149,139)     # rgb = (0x8b,0x95,0x8b)
+    ( 46, 46,167)     # rgb = (0x2e,0x2e,0xa7)
+    (141,141,141)     # rgb = (0x8d,0x8d,0x8d)
+    (128,  0,  0)     # rgb = (0x80,0x00,0x00)
+    (139,139,139)     # rgb = (0x8b,0x8b,0x8b)
+    (185,  0,  0)     # rgb = (0xb9,0x00,0x00)
+    (137,137,137)     # rgb = (0x89,0x89,0x89)
+    ( 12, 12,213)     # rgb = (0x0c,0x0c,0xd5)
+    (120,117,117)     # rgb = (0x78,0x75,0x75)
+    (135,135,135)     # rgb = (0x87,0x87,0x87) grey53
+    (  0,  0,178)     # rgb = (0x00,0x00,0xb2)
+    (133,133,133)     # rgb = (0x85,0x85,0x85) grey52
+    (165,  0,  0)     # rgb = (0xa5,0x00,0x00)
+    (222,  0,  0)     # rgb = (0xde,0x00,0x00)
+    (129,129,129)     # rgb = (0x81,0x81,0x81)
+    (127,127,127)     # rgb = (0x7f,0x7f,0x7f) grey50
+    (  0,  0,158)     # rgb = (0x00,0x00,0x9e)
+    (125,125,125)     # rgb = (0x7d,0x7d,0x7d) grey49
+    (  0,  0,201)     # rgb = (0x00,0x00,0xc9)
+    (123,123,123)     # rgb = (0x7b,0x7b,0x7b)
+    (121,121,121)     # rgb = (0x79,0x79,0x79)
+    ( 55, 55, 86)     # rgb = (0x37,0x37,0x56)
+    (119,119,119)     # rgb = (0x77,0x77,0x77)
+    (117,117,117)     # rgb = (0x75,0x75,0x75) grey46
+    (115,115,115)     # rgb = (0x73,0x73,0x73) grey45
+    ( 72,169, 72)     # rgb = (0x48,0xa9,0x48)
+    (142,  0,  0)     # rgb = (0x8e,0x00,0x00)
+    (  2,  2,100)     # rgb = (0x02,0x02,0x64)
+    (  0,  0, 98)     # rgb = (0x00,0x00,0x62)
+    ( 86,137, 86)     # rgb = (0x56,0x89,0x56)
+    ( 40, 40,124)     # rgb = (0x28,0x28,0x7c)
+    ( 83,139, 83)     # rgb = (0x53,0x8b,0x53)
+    (137,137,143)     # rgb = (0x89,0x89,0x8f)
+    (103,103,103)     # rgb = (0x67,0x67,0x67)
+    (101,101,101)     # rgb = (0x65,0x65,0x65)
+    ( 93,109, 93)     # rgb = (0x5d,0x6d,0x5d)
+    ( 19,229, 19)     # rgb = (0x13,0xe5,0x13)
+    (134, 38, 38)     # rgb = (0x86,0x26,0x26)
+    (111, 45, 45)     # rgb = (0x6f,0x2d,0x2d)
+    ( 68,145, 68)     # rgb = (0x44,0x91,0x44)
+    ( 97, 97, 97)     # rgb = (0x61,0x61,0x61) grey38
+    ( 59,157, 59)     # rgb = (0x3b,0x9d,0x3b)
+    ( 68,137, 68)     # rgb = (0x44,0x89,0x44)
+    ( 61,147, 61)     # rgb = (0x3d,0x93,0x3d)
+    (  0,  0,164)     # rgb = (0x00,0x00,0xa4)
+    (  0,243,  0)     # rgb = (0x00,0xf3,0x00)
+    (  0,241,  0)     # rgb = (0x00,0xf1,0x00)
+    ( 89, 89, 89)     # rgb = (0x59,0x59,0x59) grey35
+    ( 87, 87, 87)     # rgb = (0x57,0x57,0x57) grey34
+    ( 85, 85, 85)     # rgb = (0x55,0x55,0x55)
+    ( 83, 83, 83)     # rgb = (0x53,0x53,0x53)
+    ( 52,133, 52)     # rgb = (0x34,0x85,0x34)
+    ( 81, 81, 81)     # rgb = (0x51,0x51,0x51)
+    ( 36,151, 36)     # rgb = (0x24,0x97,0x24)
+    ( 79, 79, 79)     # rgb = (0x4f,0x4f,0x4f) grey31
+    ( 58, 58, 65)     # rgb = (0x3a,0x3a,0x41)
+    ( 16, 16,186)     # rgb = (0x10,0x10,0xba)
+    (178, 15, 15)     # rgb = (0xb2,0x0f,0x0f)
+    (  0,199,  0)     # rgb = (0x00,0xc7,0x00)
+    (  0,197,  0)     # rgb = (0x00,0xc5,0x00)
+    (252,252,252)     # rgb = (0xfc,0xfc,0xfc) grey99
+    (  0,195,  0)     # rgb = (0x00,0xc3,0x00)
+    (  4,  4,151)     # rgb = (0x04,0x04,0x97)
+    (  0,193,  0)     # rgb = (0x00,0xc1,0x00)
+    ( 45,119, 45)     # rgb = (0x2d,0x77,0x2d)
+    (250,250,250)     # rgb = (0xfa,0xfa,0xfa) grey98
+    (  0,191,  0)     # rgb = (0x00,0xbf,0x00)
+    (  0,  0,104)     # rgb = (0x00,0x00,0x68)
+    (  0,189,  0)     # rgb = (0x00,0xbd,0x00)
+    (218,212,212)     # rgb = (0xda,0xd4,0xd4)
+    ( 16, 16,123)     # rgb = (0x10,0x10,0x7b)
+    (  9,173,  9)     # rgb = (0x09,0xad,0x09)
+    (248,248,248)     # rgb = (0xf8,0xf8,0xf8)
+    (  0,185,  0)     # rgb = (0x00,0xb9,0x00)
+    (  0,183,  0)     # rgb = (0x00,0xb7,0x00)
+    (156,156,161)     # rgb = (0x9c,0x9c,0xa1)
+    (246,246,246)     # rgb = (0xf6,0xf6,0xf6)
+    ( 12,161, 12)     # rgb = (0x0c,0xa1,0x0c)
+    (  0,179,  0)     # rgb = (0x00,0xb3,0x00)
+    (  0,177,  0)     # rgb = (0x00,0xb1,0x00)
+    ( 16,145, 16)     # rgb = (0x10,0x91,0x10)
+    (  0,171,  0)     # rgb = (0x00,0xab,0x00)
+    (242,242,242)     # rgb = (0xf2,0xf2,0xf2) grey95
+    (  0,169,  0)     # rgb = (0x00,0xa9,0x00)
+    (  0,167,  0)     # rgb = (0x00,0xa7,0x00)
+    (238,238,238)     # rgb = (0xee,0xee,0xee)
+    (236,236,236)     # rgb = (0xec,0xec,0xec)
+    (  0,151,  0)     # rgb = (0x00,0x97,0x00)
+    (234,234,234)     # rgb = (0xea,0xea,0xea)
+    (  0,  0,107)     # rgb = (0x00,0x00,0x6b)
+    (  0,141,  0)     # rgb = (0x00,0x8d,0x00)
+    (  0,139,  0)     # rgb = (0x00,0x8b,0x00) green4
+    (  0,137,  0)     # rgb = (0x00,0x89,0x00)
+    (  0,135,  0)     # rgb = (0x00,0x87,0x00)
+    ( 49, 49, 49)     # rgb = (0x31,0x31,0x31)
+    ( 25, 25, 42)     # rgb = (0x19,0x19,0x2a)
+    (  7,  7, 64)     # rgb = (0x07,0x07,0x40)
+    ( 18, 18,174)     # rgb = (0x12,0x12,0xae)
+    (  9,  9,238)     # rgb = (0x09,0x09,0xee)
+    (211,214,211)     # rgb = (0xd3,0xd6,0xd3)
+    (204,204,204)     # rgb = (0xcc,0xcc,0xcc) grey80
+    (147,  0,  0)     # rgb = (0x93,0x00,0x00)
+    (163, 42, 42)     # rgb = (0xa3,0x2a,0x2a)
+    (198,198,198)     # rgb = (0xc6,0xc6,0xc6)
+    (196,196,196)     # rgb = (0xc4,0xc4,0xc4) grey77
+    (204,  0,  0)     # rgb = (0xcc,0x00,0x00)
+    (211, 10, 10)     # rgb = (0xd3,0x0a,0x0a)
+    (129,107,107)     # rgb = (0x81,0x6b,0x6b)
+    (120, 62, 62)     # rgb = (0x78,0x3e,0x3e)
+    (  3,  3,109)     # rgb = (0x03,0x03,0x6d)
+    (  0,  0,159)     # rgb = (0x00,0x00,0x9f)
+    ( 10, 10, 86)     # rgb = (0x0a,0x0a,0x56)
+    ( 70, 70, 72)     # rgb = (0x46,0x46,0x48)
+    ( 65, 65, 77)     # rgb = (0x41,0x41,0x4d)
+    (115, 93, 93)     # rgb = (0x73,0x5d,0x5d)
+    ( 81,  7,  7)     # rgb = (0x51,0x07,0x07)
+    (168,168,168)     # rgb = (0xa8,0xa8,0xa8) grey66
+    (237,237,239)     # rgb = (0xed,0xed,0xef)
+    (160,160,160)     # rgb = (0xa0,0xa0,0xa0)
+    (158,158,158)     # rgb = (0x9e,0x9e,0x9e) grey62
+    (156,156,156)     # rgb = (0x9c,0x9c,0x9c) grey61
+    (  0,  0,185)     # rgb = (0x00,0x00,0xb9)
+    (154,154,154)     # rgb = (0x9a,0x9a,0x9a)
+    (178,  0,  0)     # rgb = (0xb2,0x00,0x00)
+    (152,152,152)     # rgb = (0x98,0x98,0x98)
+    (235,  0,  0)     # rgb = (0xeb,0x00,0x00)
+    (150,150,150)     # rgb = (0x96,0x96,0x96) grey59
+    (158,  0,  0)     # rgb = (0x9e,0x00,0x00)
+    (148,148,148)     # rgb = (0x94,0x94,0x94) grey58
+    ( 19, 19, 28)     # rgb = (0x13,0x13,0x1c)
+    (146,146,146)     # rgb = (0x92,0x92,0x92)
+    (144,144,144)     # rgb = (0x90,0x90,0x90)
+    (142,142,142)     # rgb = (0x8e,0x8e,0x8e)
+    (  0,  0,145)     # rgb = (0x00,0x00,0x91)
+    (138,138,138)     # rgb = (0x8a,0x8a,0x8a) grey54
+    (136,136,136)     # rgb = (0x88,0x88,0x88)
+    (118,162,118)     # rgb = (0x76,0xa2,0x76)
+    (133,136,133)     # rgb = (0x85,0x88,0x85)
+    (134,134,134)     # rgb = (0x86,0x86,0x86)
+    (132,132,132)     # rgb = (0x84,0x84,0x84)
+    (120, 15, 15)     # rgb = (0x78,0x0f,0x0f)
+    (130,130,130)     # rgb = (0x82,0x82,0x82) grey51
+    (126,130,126)     # rgb = (0x7e,0x82,0x7e)
+    (126,126,126)     # rgb = (0x7e,0x7e,0x7e)
+    (124,124,124)     # rgb = (0x7c,0x7c,0x7c)
+    (122,122,122)     # rgb = (0x7a,0x7a,0x7a) grey48
+    ( 74,192, 74)     # rgb = (0x4a,0xc0,0x4a)
+    (118,118,118)     # rgb = (0x76,0x76,0x76)
+    (116,116,116)     # rgb = (0x74,0x74,0x74)
+    (114,114,114)     # rgb = (0x72,0x72,0x72)
+    (112,112,112)     # rgb = (0x70,0x70,0x70) grey44
+    (152,  0,  0)     # rgb = (0x98,0x00,0x00)
+    (110,110,110)     # rgb = (0x6e,0x6e,0x6e) grey43
+    (106,112,106)     # rgb = (0x6a,0x70,0x6a)
+    (122,102,102)     # rgb = (0x7a,0x66,0x66)
+    (106,106,106)     # rgb = (0x6a,0x6a,0x6a)
+    (132,  0,  0)     # rgb = (0x84,0x00,0x00)
+    ( 68,162, 68)     # rgb = (0x44,0xa2,0x44)
+    ( 75,150, 75)     # rgb = (0x4b,0x96,0x4b)
+    ( 97,100, 97)     # rgb = (0x61,0x64,0x61)
+    ( 98, 98, 98)     # rgb = (0x62,0x62,0x62)
+    (  0,244,  0)     # rgb = (0x00,0xf4,0x00)
+    ( 56,152, 56)     # rgb = (0x38,0x98,0x38)
+    ( 92, 92, 92)     # rgb = (0x5c,0x5c,0x5c) grey36
+    ( 90, 90, 90)     # rgb = (0x5a,0x5a,0x5a)
+    (  0,230,  0)     # rgb = (0x00,0xe6,0x00)
+    (  2,  2, 93)     # rgb = (0x02,0x02,0x5d)
+    ( 66,120, 66)     # rgb = (0x42,0x78,0x42)
+    ( 86, 86, 86)     # rgb = (0x56,0x56,0x56)
+    (  0,  0,240)     # rgb = (0x00,0x00,0xf0)
+    ( 46,148, 46)     # rgb = (0x2e,0x94,0x2e)
+    ( 71,104, 71)     # rgb = (0x47,0x68,0x47)
+    ( 49, 49, 96)     # rgb = (0x31,0x31,0x60)
+    (  0,216,  0)     # rgb = (0x00,0xd8,0x00)
+    ( 82, 82, 82)     # rgb = (0x52,0x52,0x52) grey32
+    ( 80, 80, 80)     # rgb = (0x50,0x50,0x50)
+    (  0,206,  0)     # rgb = (0x00,0xce,0x00)
+    ( 33,152, 33)     # rgb = (0x21,0x98,0x21)
+    ( 20, 20,109)     # rgb = (0x14,0x14,0x6d)
+    (  0,200,  0)     # rgb = (0x00,0xc8,0x00)
+    ( 76, 76, 76)     # rgb = (0x4c,0x4c,0x4c)
+    (253,253,253)     # rgb = (0xfd,0xfd,0xfd)
+    (  0,198,  0)     # rgb = (0x00,0xc6,0x00)
+    (  0,  0,157)     # rgb = (0x00,0x00,0x9d)
+    (111,107,107)     # rgb = (0x6f,0x6b,0x6b)
+    (234, 14, 14)     # rgb = (0xea,0x0e,0x0e)
+    ( 72, 72, 72)     # rgb = (0x48,0x48,0x48)
+    (  0,188,  0)     # rgb = (0x00,0xbc,0x00)
+    ( 52,102, 52)     # rgb = (0x34,0x66,0x34)
+    (  2,  2,245)     # rgb = (0x02,0x02,0xf5)
+    ( 83, 83, 96)     # rgb = (0x53,0x53,0x60)
+    (  0,176,  0)     # rgb = (0x00,0xb0,0x00)
+    (  0,174,  0)     # rgb = (0x00,0xae,0x00)
+    (183,  0,  0)     # rgb = (0xb7,0x00,0x00)
+    (  0,164,  0)     # rgb = (0x00,0xa4,0x00)
+    (239,239,239)     # rgb = (0xef,0xef,0xef)
+    (  0,162,  0)     # rgb = (0x00,0xa2,0x00)
+    (143, 79, 79)     # rgb = (0x8f,0x4f,0x4f)
+    (149, 52, 52)     # rgb = (0x95,0x34,0x34)
+    (  0,152,  0)     # rgb = (0x00,0x98,0x00)
+    (  0,150,  0)     # rgb = (0x00,0x96,0x00)
+    (  0,146,  0)     # rgb = (0x00,0x92,0x00)
+    (231,231,231)     # rgb = (0xe7,0xe7,0xe7)
+    (  0,140,  0)     # rgb = (0x00,0x8c,0x00)
+    (227,227,227)     # rgb = (0xe3,0xe3,0xe3) grey89
+    (  0,128,  0)     # rgb = (0x00,0x80,0x00)
+    (146,  6,  6)     # rgb = (0x92,0x06,0x06)
+    (  1,  1,111)     # rgb = (0x01,0x01,0x6f)
+    (100, 86, 89)     # rgb = (0x64,0x56,0x59)
+    (  0,  0,100)     # rgb = (0x00,0x00,0x64)
+    ( 78, 78,107)     # rgb = (0x4e,0x4e,0x6b)
+    (207,207,207)     # rgb = (0xcf,0xcf,0xcf) grey81
+    (221,221,224)     # rgb = (0xdd,0xdd,0xe0)
+    (  0,  0,123)     # rgb = (0x00,0x00,0x7b)
+    (201,201,201)     # rgb = (0xc9,0xc9,0xc9) grey79
+    ( 22, 22, 65)     # rgb = (0x16,0x16,0x41)
+    ( 33, 33, 89)     # rgb = (0x21,0x21,0x59)
+    ( 87, 87, 89)     # rgb = (0x57,0x57,0x59)
+    ( 68, 68,120)     # rgb = (0x44,0x44,0x78)
+    (191,191,191)     # rgb = (0xbf,0xbf,0xbf) grey75
+    (235,221,221)     # rgb = (0xeb,0xdd,0xdd)
+    ( 45, 45, 84)     # rgb = (0x2d,0x2d,0x54)
+    ( 10, 10, 96)     # rgb = (0x0a,0x0a,0x60)
+    (  0,  0,255)     # rgb = (0x00,0x00,0xff) blue1
+    (191,125,125)     # rgb = (0xbf,0x7d,0x7d)
+}
+tRNS {
+ 0}
+IMAGE {
+    pixels hex
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000e0ea66000000000000000000000000000000
+0000000000000000000000de02a336e43903f4f0000000000000000000000000
+000000000000000069ef1a358680062eb017b0ab7af459500000000000000000
+0000000000667c0ea9cc803979937917a03a878787b0e2ae8ae75c0000000000
+00005cea8ea72c8639e293208f7d7d19200639a017ab2ee4ac2ca7097c690000
+00007823a72b2bda198fd54ddad90521219191217d1917cc2b2b2b2baf8e0000
+0000e81f9b9f27014d05d91c2a2a2a7f037ecdcd7e7a012a2a2aaab7c2ef0000
+00006c9f229d981a23282828282828282828282828282828a7b445c3c8de0000
+00005ca249d63d140f139f272727272727272727a5a528af44c3c8ce43000000
+0000009a62ca41a6960e0d941da4a4a4a4a4a4a4a4a9b732525a1084a1000000
+000000965b58b53811940d0b090b1823a3a3252ab4d24c269957571088000000
+000000946162b9b59c0f14b12d0c8b8c98a3afb8ed1bbd82ba74300877000000
+00000088c565c7b5a6962dcf67be07048aa5b84315f326ba7395832950000000
+00000002bed8d4b94214b1c7dbb68c8b04a843e6d1bd814bceeb10a900000000
+0000007b47636ec441b23d4edb3f09078bac4315f340ec855a82995f00000000
+00000059bb63e15d42643dca6b3f8e090735ed76bd81c05224e9f27b00000000
+0000006cbbd47161c1684951dc3f908e8c3ceef38d08ebe96d6d086000000000
+00000050bf67dc54534fdd53ddb20d0b8eb815d10af1732fe312e60000000000
+00000000add6d6bf61c16f566eb20e0d924475bd578572c61e6d340000000000
+0000000016d8d3d03ec76bcfdf3b0f0e13bc4c8d2f84c040cb837b0000000000
+00000000550c47b3365bd45d6f33110f1a4575cbf2c0521e0802000000000000
+000000000000e7ac36be625e7031131122455a0a2f0a99c6e700000000000000
+000000000000006a9e37d36270331613a545f181e53032e80000000000000000
+00000000000000005088c5d371311816a8464b7374ee89000000000000000000
+0000000000000000000077b654a29b18acc24a722a5500000000000000000000
+0000000000000000000000d78a9f9e9b3548c38ac90000000000000000000000
+00000000000000000000000000ef1f9e3cc20200000000000000000000000000
+0000000000000000000000000000e89736780000000000000000000000000000
+00000000000000000000000000000060e0000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+}
diff --git a/src/image/png/writer.go b/src/image/png/writer.go
index df23270..dd87d81 100644
--- a/src/image/png/writer.go
+++ b/src/image/png/writer.go
@@ -420,8 +420,11 @@ func writeImage(w io.Writer, m image.Image, cb int, level int) error {
 		}
 
 		// Apply the filter.
+		// Skip filter for NoCompression and paletted images (cbP8) as
+		// "filters are rarely useful on palette images" and will result
+		// in larger files (see http://www.libpng.org/pub/png/book/chapter09.html).
 		f := ftNone
-		if level != zlib.NoCompression {
+		if level != zlib.NoCompression && cb != cbP8 {
 			f = filter(&cr, pr, bpp)
 		}
 
diff --git a/src/index/suffixarray/example_test.go b/src/index/suffixarray/example_test.go
new file mode 100644
index 0000000..ea10bfd
--- /dev/null
+++ b/src/index/suffixarray/example_test.go
@@ -0,0 +1,22 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package suffixarray_test
+
+import (
+	"fmt"
+	"index/suffixarray"
+)
+
+func ExampleIndex_Lookup() {
+	index := suffixarray.New([]byte("banana"))
+	offsets := index.Lookup([]byte("ana"), -1)
+	for _, off := range offsets {
+		fmt.Println(off)
+	}
+
+	// Unordered output:
+	// 1
+	// 3
+}
diff --git a/src/cmd/internal/pprof/profile/encode.go b/src/internal/pprof/profile/encode.go
similarity index 100%
rename from src/cmd/internal/pprof/profile/encode.go
rename to src/internal/pprof/profile/encode.go
diff --git a/src/cmd/internal/pprof/profile/filter.go b/src/internal/pprof/profile/filter.go
similarity index 100%
rename from src/cmd/internal/pprof/profile/filter.go
rename to src/internal/pprof/profile/filter.go
diff --git a/src/internal/pprof/profile/legacy_profile.go b/src/internal/pprof/profile/legacy_profile.go
new file mode 100644
index 0000000..d69f8de
--- /dev/null
+++ b/src/internal/pprof/profile/legacy_profile.go
@@ -0,0 +1,1266 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements parsers to convert legacy profiles into the
+// profile.proto format.
+
+package profile
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"math"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+var (
+	countStartRE = regexp.MustCompile(`\A(\w+) profile: total \d+\n\z`)
+	countRE      = regexp.MustCompile(`\A(\d+) @(( 0x[0-9a-f]+)+)\n\z`)
+
+	heapHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] *@ *(heap[_a-z0-9]*)/?(\d*)`)
+	heapSampleRE = regexp.MustCompile(`(-?\d+): *(-?\d+) *\[ *(\d+): *(\d+) *] @([ x0-9a-f]*)`)
+
+	contentionSampleRE = regexp.MustCompile(`(\d+) *(\d+) @([ x0-9a-f]*)`)
+
+	hexNumberRE = regexp.MustCompile(`0x[0-9a-f]+`)
+
+	growthHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ growthz`)
+
+	fragmentationHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ fragmentationz`)
+
+	threadzStartRE = regexp.MustCompile(`--- threadz \d+ ---`)
+	threadStartRE  = regexp.MustCompile(`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---`)
+
+	procMapsRE = regexp.MustCompile(`([[:xdigit:]]+)-([[:xdigit:]]+)\s+([-rwxp]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+):([[:xdigit:]]+)\s+([[:digit:]]+)\s*(\S+)?`)
+
+	briefMapsRE = regexp.MustCompile(`\s*([[:xdigit:]]+)-([[:xdigit:]]+):\s*(\S+)(\s.*@)?([[:xdigit:]]+)?`)
+
+	// LegacyHeapAllocated instructs the heapz parsers to use the
+	// allocated memory stats instead of the default in-use memory. Note
+	// that tcmalloc doesn't provide all allocated memory, only in-use
+	// stats.
+	LegacyHeapAllocated bool
+)
+
+func isSpaceOrComment(line string) bool {
+	trimmed := strings.TrimSpace(line)
+	return len(trimmed) == 0 || trimmed[0] == '#'
+}
+
+// parseGoCount parses a Go count profile (e.g., threadcreate or
+// goroutine) and returns a new Profile.
+func parseGoCount(b []byte) (*Profile, error) {
+	r := bytes.NewBuffer(b)
+
+	var line string
+	var err error
+	for {
+		// Skip past comments and empty lines seeking a real header.
+		line, err = r.ReadString('\n')
+		if err != nil {
+			return nil, err
+		}
+		if !isSpaceOrComment(line) {
+			break
+		}
+	}
+
+	m := countStartRE.FindStringSubmatch(line)
+	if m == nil {
+		return nil, errUnrecognized
+	}
+	profileType := m[1]
+	p := &Profile{
+		PeriodType: &ValueType{Type: profileType, Unit: "count"},
+		Period:     1,
+		SampleType: []*ValueType{{Type: profileType, Unit: "count"}},
+	}
+	locations := make(map[uint64]*Location)
+	for {
+		line, err = r.ReadString('\n')
+		if err != nil {
+			if err == io.EOF {
+				break
+			}
+			return nil, err
+		}
+		if isSpaceOrComment(line) {
+			continue
+		}
+		if strings.HasPrefix(line, "---") {
+			break
+		}
+		m := countRE.FindStringSubmatch(line)
+		if m == nil {
+			return nil, errMalformed
+		}
+		n, err := strconv.ParseInt(m[1], 0, 64)
+		if err != nil {
+			return nil, errMalformed
+		}
+		fields := strings.Fields(m[2])
+		locs := make([]*Location, 0, len(fields))
+		for _, stk := range fields {
+			addr, err := strconv.ParseUint(stk, 0, 64)
+			if err != nil {
+				return nil, errMalformed
+			}
+			// Adjust all frames by -1 to land on the call instruction.
+			addr--
+			loc := locations[addr]
+			if loc == nil {
+				loc = &Location{
+					Address: addr,
+				}
+				locations[addr] = loc
+				p.Location = append(p.Location, loc)
+			}
+			locs = append(locs, loc)
+		}
+		p.Sample = append(p.Sample, &Sample{
+			Location: locs,
+			Value:    []int64{n},
+		})
+	}
+
+	if err = parseAdditionalSections(strings.TrimSpace(line), r, p); err != nil {
+		return nil, err
+	}
+	return p, nil
+}
+
+// remapLocationIDs ensures there is a location for each address
+// referenced by a sample, and remaps the samples to point to the new
+// location ids.
+func (p *Profile) remapLocationIDs() {
+	seen := make(map[*Location]bool, len(p.Location))
+	var locs []*Location
+
+	for _, s := range p.Sample {
+		for _, l := range s.Location {
+			if seen[l] {
+				continue
+			}
+			l.ID = uint64(len(locs) + 1)
+			locs = append(locs, l)
+			seen[l] = true
+		}
+	}
+	p.Location = locs
+}
+
+func (p *Profile) remapFunctionIDs() {
+	seen := make(map[*Function]bool, len(p.Function))
+	var fns []*Function
+
+	for _, l := range p.Location {
+		for _, ln := range l.Line {
+			fn := ln.Function
+			if fn == nil || seen[fn] {
+				continue
+			}
+			fn.ID = uint64(len(fns) + 1)
+			fns = append(fns, fn)
+			seen[fn] = true
+		}
+	}
+	p.Function = fns
+}
+
+// remapMappingIDs matches location addresses with existing mappings
+// and updates them appropriately. This is O(N*M), if this ever shows
+// up as a bottleneck, evaluate sorting the mappings and doing a
+// binary search, which would make it O(N*log(M)).
+func (p *Profile) remapMappingIDs() {
+	if len(p.Mapping) == 0 {
+		return
+	}
+
+	// Some profile handlers will incorrectly set regions for the main
+	// executable if its section is remapped. Fix them through heuristics.
+
+	// Remove the initial mapping if named '/anon_hugepage' and has a
+	// consecutive adjacent mapping.
+	if m := p.Mapping[0]; strings.HasPrefix(m.File, "/anon_hugepage") {
+		if len(p.Mapping) > 1 && m.Limit == p.Mapping[1].Start {
+			p.Mapping = p.Mapping[1:]
+		}
+	}
+
+	// Subtract the offset from the start of the main mapping if it
+	// ends up at a recognizable start address.
+	const expectedStart = 0x400000
+	if m := p.Mapping[0]; m.Start-m.Offset == expectedStart {
+		m.Start = expectedStart
+		m.Offset = 0
+	}
+
+	for _, l := range p.Location {
+		if a := l.Address; a != 0 {
+			for _, m := range p.Mapping {
+				if m.Start <= a && a < m.Limit {
+					l.Mapping = m
+					break
+				}
+			}
+		}
+	}
+
+	// Reset all mapping IDs.
+	for i, m := range p.Mapping {
+		m.ID = uint64(i + 1)
+	}
+}
+
+var cpuInts = []func([]byte) (uint64, []byte){
+	get32l,
+	get32b,
+	get64l,
+	get64b,
+}
+
+func get32l(b []byte) (uint64, []byte) {
+	if len(b) < 4 {
+		return 0, nil
+	}
+	return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24, b[4:]
+}
+
+func get32b(b []byte) (uint64, []byte) {
+	if len(b) < 4 {
+		return 0, nil
+	}
+	return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24, b[4:]
+}
+
+func get64l(b []byte) (uint64, []byte) {
+	if len(b) < 8 {
+		return 0, nil
+	}
+	return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56, b[8:]
+}
+
+func get64b(b []byte) (uint64, []byte) {
+	if len(b) < 8 {
+		return 0, nil
+	}
+	return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56, b[8:]
+}
+
+// ParseTracebacks parses a set of tracebacks and returns a newly
+// populated profile. It will accept any text file and generate a
+// Profile out of it with any hex addresses it can identify, including
+// a process map if it can recognize one. Each sample will include a
+// tag "source" with the addresses recognized in string format.
+func ParseTracebacks(b []byte) (*Profile, error) {
+	r := bytes.NewBuffer(b)
+
+	p := &Profile{
+		PeriodType: &ValueType{Type: "trace", Unit: "count"},
+		Period:     1,
+		SampleType: []*ValueType{
+			{Type: "trace", Unit: "count"},
+		},
+	}
+
+	var sources []string
+	var sloc []*Location
+
+	locs := make(map[uint64]*Location)
+	for {
+		l, err := r.ReadString('\n')
+		if err != nil {
+			if err != io.EOF {
+				return nil, err
+			}
+			if l == "" {
+				break
+			}
+		}
+		if sectionTrigger(l) == memoryMapSection {
+			break
+		}
+		if s, addrs := extractHexAddresses(l); len(s) > 0 {
+			for _, addr := range addrs {
+				// Addresses from stack traces point to the next instruction after
+				// each call. Adjust by -1 to land somewhere on the actual call.
+				addr--
+				loc := locs[addr]
+				if locs[addr] == nil {
+					loc = &Location{
+						Address: addr,
+					}
+					p.Location = append(p.Location, loc)
+					locs[addr] = loc
+				}
+				sloc = append(sloc, loc)
+			}
+
+			sources = append(sources, s...)
+		} else {
+			if len(sources) > 0 || len(sloc) > 0 {
+				addTracebackSample(sloc, sources, p)
+				sloc, sources = nil, nil
+			}
+		}
+	}
+
+	// Add final sample to save any leftover data.
+	if len(sources) > 0 || len(sloc) > 0 {
+		addTracebackSample(sloc, sources, p)
+	}
+
+	if err := p.ParseMemoryMap(r); err != nil {
+		return nil, err
+	}
+	return p, nil
+}
+
+func addTracebackSample(l []*Location, s []string, p *Profile) {
+	p.Sample = append(p.Sample,
+		&Sample{
+			Value:    []int64{1},
+			Location: l,
+			Label:    map[string][]string{"source": s},
+		})
+}
+
+// parseCPU parses a profilez legacy profile and returns a newly
+// populated Profile.
+//
+// The general format for profilez samples is a sequence of words in
+// binary format. The first words are a header with the following data:
+//   1st word -- 0
+//   2nd word -- 3
+//   3rd word -- 0 if a c++ application, 1 if a java application.
+//   4th word -- Sampling period (in microseconds).
+//   5th word -- Padding.
+func parseCPU(b []byte) (*Profile, error) {
+	var parse func([]byte) (uint64, []byte)
+	var n1, n2, n3, n4, n5 uint64
+	for _, parse = range cpuInts {
+		var tmp []byte
+		n1, tmp = parse(b)
+		n2, tmp = parse(tmp)
+		n3, tmp = parse(tmp)
+		n4, tmp = parse(tmp)
+		n5, tmp = parse(tmp)
+
+		if tmp != nil && n1 == 0 && n2 == 3 && n3 == 0 && n4 > 0 && n5 == 0 {
+			b = tmp
+			return cpuProfile(b, int64(n4), parse)
+		}
+	}
+	return nil, errUnrecognized
+}
+
+// cpuProfile returns a new Profile from C++ profilez data.
+// b is the profile bytes after the header, period is the profiling
+// period, and parse is a function to parse 8-byte chunks from the
+// profile in its native endianness.
+func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) {
+	p := &Profile{
+		Period:     period * 1000,
+		PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"},
+		SampleType: []*ValueType{
+			{Type: "samples", Unit: "count"},
+			{Type: "cpu", Unit: "nanoseconds"},
+		},
+	}
+	var err error
+	if b, _, err = parseCPUSamples(b, parse, true, p); err != nil {
+		return nil, err
+	}
+
+	// If all samples have the same second-to-the-bottom frame, it
+	// strongly suggests that it is an uninteresting artifact of
+	// measurement -- a stack frame pushed by the signal handler. The
+	// bottom frame is always correct as it is picked up from the signal
+	// structure, not the stack. Check if this is the case and if so,
+	// remove.
+	if len(p.Sample) > 1 && len(p.Sample[0].Location) > 1 {
+		allSame := true
+		id1 := p.Sample[0].Location[1].Address
+		for _, s := range p.Sample {
+			if len(s.Location) < 2 || id1 != s.Location[1].Address {
+				allSame = false
+				break
+			}
+		}
+		if allSame {
+			for _, s := range p.Sample {
+				s.Location = append(s.Location[:1], s.Location[2:]...)
+			}
+		}
+	}
+
+	if err := p.ParseMemoryMap(bytes.NewBuffer(b)); err != nil {
+		return nil, err
+	}
+	return p, nil
+}
+
+// parseCPUSamples parses a collection of profilez samples from a
+// profile.
+//
+// profilez samples are a repeated sequence of stack frames of the
+// form:
+//    1st word -- The number of times this stack was encountered.
+//    2nd word -- The size of the stack (StackSize).
+//    3rd word -- The first address on the stack.
+//    ...
+//    StackSize + 2 -- The last address on the stack
+// The last stack trace is of the form:
+//   1st word -- 0
+//   2nd word -- 1
+//   3rd word -- 0
+//
+// Addresses from stack traces may point to the next instruction after
+// each call. Optionally adjust by -1 to land somewhere on the actual
+// call (except for the leaf, which is not a call).
+func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), adjust bool, p *Profile) ([]byte, map[uint64]*Location, error) {
+	locs := make(map[uint64]*Location)
+	for len(b) > 0 {
+		var count, nstk uint64
+		count, b = parse(b)
+		nstk, b = parse(b)
+		if b == nil || nstk > uint64(len(b)/4) {
+			return nil, nil, errUnrecognized
+		}
+		var sloc []*Location
+		addrs := make([]uint64, nstk)
+		for i := 0; i < int(nstk); i++ {
+			addrs[i], b = parse(b)
+		}
+
+		if count == 0 && nstk == 1 && addrs[0] == 0 {
+			// End of data marker
+			break
+		}
+		for i, addr := range addrs {
+			if adjust && i > 0 {
+				addr--
+			}
+			loc := locs[addr]
+			if loc == nil {
+				loc = &Location{
+					Address: addr,
+				}
+				locs[addr] = loc
+				p.Location = append(p.Location, loc)
+			}
+			sloc = append(sloc, loc)
+		}
+		p.Sample = append(p.Sample,
+			&Sample{
+				Value:    []int64{int64(count), int64(count) * p.Period},
+				Location: sloc,
+			})
+	}
+	// Reached the end without finding the EOD marker.
+	return b, locs, nil
+}
+
+// parseHeap parses a heapz legacy or a growthz profile and
+// returns a newly populated Profile.
+func parseHeap(b []byte) (p *Profile, err error) {
+	r := bytes.NewBuffer(b)
+	l, err := r.ReadString('\n')
+	if err != nil {
+		return nil, errUnrecognized
+	}
+
+	sampling := ""
+
+	if header := heapHeaderRE.FindStringSubmatch(l); header != nil {
+		p = &Profile{
+			SampleType: []*ValueType{
+				{Type: "objects", Unit: "count"},
+				{Type: "space", Unit: "bytes"},
+			},
+			PeriodType: &ValueType{Type: "objects", Unit: "bytes"},
+		}
+
+		var period int64
+		if len(header[6]) > 0 {
+			if period, err = strconv.ParseInt(header[6], 10, 64); err != nil {
+				return nil, errUnrecognized
+			}
+		}
+
+		switch header[5] {
+		case "heapz_v2", "heap_v2":
+			sampling, p.Period = "v2", period
+		case "heapprofile":
+			sampling, p.Period = "", 1
+		case "heap":
+			sampling, p.Period = "v2", period/2
+		default:
+			return nil, errUnrecognized
+		}
+	} else if header = growthHeaderRE.FindStringSubmatch(l); header != nil {
+		p = &Profile{
+			SampleType: []*ValueType{
+				{Type: "objects", Unit: "count"},
+				{Type: "space", Unit: "bytes"},
+			},
+			PeriodType: &ValueType{Type: "heapgrowth", Unit: "count"},
+			Period:     1,
+		}
+	} else if header = fragmentationHeaderRE.FindStringSubmatch(l); header != nil {
+		p = &Profile{
+			SampleType: []*ValueType{
+				{Type: "objects", Unit: "count"},
+				{Type: "space", Unit: "bytes"},
+			},
+			PeriodType: &ValueType{Type: "allocations", Unit: "count"},
+			Period:     1,
+		}
+	} else {
+		return nil, errUnrecognized
+	}
+
+	if LegacyHeapAllocated {
+		for _, st := range p.SampleType {
+			st.Type = "alloc_" + st.Type
+		}
+	} else {
+		for _, st := range p.SampleType {
+			st.Type = "inuse_" + st.Type
+		}
+	}
+
+	locs := make(map[uint64]*Location)
+	for {
+		l, err = r.ReadString('\n')
+		if err != nil {
+			if err != io.EOF {
+				return nil, err
+			}
+
+			if l == "" {
+				break
+			}
+		}
+
+		if isSpaceOrComment(l) {
+			continue
+		}
+		l = strings.TrimSpace(l)
+
+		if sectionTrigger(l) != unrecognizedSection {
+			break
+		}
+
+		value, blocksize, addrs, err := parseHeapSample(l, p.Period, sampling)
+		if err != nil {
+			return nil, err
+		}
+		var sloc []*Location
+		for _, addr := range addrs {
+			// Addresses from stack traces point to the next instruction after
+			// each call. Adjust by -1 to land somewhere on the actual call.
+			addr--
+			loc := locs[addr]
+			if locs[addr] == nil {
+				loc = &Location{
+					Address: addr,
+				}
+				p.Location = append(p.Location, loc)
+				locs[addr] = loc
+			}
+			sloc = append(sloc, loc)
+		}
+
+		p.Sample = append(p.Sample, &Sample{
+			Value:    value,
+			Location: sloc,
+			NumLabel: map[string][]int64{"bytes": {blocksize}},
+		})
+	}
+
+	if err = parseAdditionalSections(l, r, p); err != nil {
+		return nil, err
+	}
+	return p, nil
+}
+
+// parseHeapSample parses a single row from a heap profile into a new Sample.
+func parseHeapSample(line string, rate int64, sampling string) (value []int64, blocksize int64, addrs []uint64, err error) {
+	sampleData := heapSampleRE.FindStringSubmatch(line)
+	if len(sampleData) != 6 {
+		return value, blocksize, addrs, fmt.Errorf("unexpected number of sample values: got %d, want 6", len(sampleData))
+	}
+
+	// Use first two values by default; tcmalloc sampling generates the
+	// same value for both, only the older heap-profile collect separate
+	// stats for in-use and allocated objects.
+	valueIndex := 1
+	if LegacyHeapAllocated {
+		valueIndex = 3
+	}
+
+	var v1, v2 int64
+	if v1, err = strconv.ParseInt(sampleData[valueIndex], 10, 64); err != nil {
+		return value, blocksize, addrs, fmt.Errorf("malformed sample: %s: %v", line, err)
+	}
+	if v2, err = strconv.ParseInt(sampleData[valueIndex+1], 10, 64); err != nil {
+		return value, blocksize, addrs, fmt.Errorf("malformed sample: %s: %v", line, err)
+	}
+
+	if v1 == 0 {
+		if v2 != 0 {
+			return value, blocksize, addrs, fmt.Errorf("allocation count was 0 but allocation bytes was %d", v2)
+		}
+	} else {
+		blocksize = v2 / v1
+		if sampling == "v2" {
+			v1, v2 = scaleHeapSample(v1, v2, rate)
+		}
+	}
+
+	value = []int64{v1, v2}
+	addrs = parseHexAddresses(sampleData[5])
+
+	return value, blocksize, addrs, nil
+}
+
+// extractHexAddresses extracts hex numbers from a string and returns
+// them, together with their numeric value, in a slice.
+func extractHexAddresses(s string) ([]string, []uint64) {
+	hexStrings := hexNumberRE.FindAllString(s, -1)
+	var ids []uint64
+	for _, s := range hexStrings {
+		if id, err := strconv.ParseUint(s, 0, 64); err == nil {
+			ids = append(ids, id)
+		} else {
+			// Do not expect any parsing failures due to the regexp matching.
+			panic("failed to parse hex value:" + s)
+		}
+	}
+	return hexStrings, ids
+}
+
+// parseHexAddresses parses hex numbers from a string and returns them
+// in a slice.
+func parseHexAddresses(s string) []uint64 {
+	_, ids := extractHexAddresses(s)
+	return ids
+}
+
+// scaleHeapSample adjusts the data from a heapz Sample to
+// account for its probability of appearing in the collected
+// data. heapz profiles are a sampling of the memory allocations
+// requests in a program. We estimate the unsampled value by dividing
+// each collected sample by its probability of appearing in the
+// profile. heapz v2 profiles rely on a poisson process to determine
+// which samples to collect, based on the desired average collection
+// rate R. The probability of a sample of size S to appear in that
+// profile is 1-exp(-S/R).
+func scaleHeapSample(count, size, rate int64) (int64, int64) {
+	if count == 0 || size == 0 {
+		return 0, 0
+	}
+
+	if rate <= 1 {
+		// if rate==1 all samples were collected so no adjustment is needed.
+		// if rate<1 treat as unknown and skip scaling.
+		return count, size
+	}
+
+	avgSize := float64(size) / float64(count)
+	scale := 1 / (1 - math.Exp(-avgSize/float64(rate)))
+
+	return int64(float64(count) * scale), int64(float64(size) * scale)
+}
+
+// parseContention parses a mutex or contention profile. There are 2 cases:
+// "--- contentionz " for legacy C++ profiles (and backwards compatibility)
+// "--- mutex:" or "--- contention:" for profiles generated by the Go runtime.
+// This code converts the text output from runtime into a *Profile. (In the future
+// the runtime might write a serialized Profile directly making this unnecessary.)
+func parseContention(b []byte) (*Profile, error) {
+	r := bytes.NewBuffer(b)
+	var l string
+	var err error
+	for {
+		// Skip past comments and empty lines seeking a real header.
+		l, err = r.ReadString('\n')
+		if err != nil {
+			return nil, err
+		}
+		if !isSpaceOrComment(l) {
+			break
+		}
+	}
+
+	if strings.HasPrefix(l, "--- contentionz ") {
+		return parseCppContention(r)
+	} else if strings.HasPrefix(l, "--- mutex:") {
+		return parseCppContention(r)
+	} else if strings.HasPrefix(l, "--- contention:") {
+		return parseCppContention(r)
+	}
+	return nil, errUnrecognized
+}
+
+// parseCppContention parses the output from synchronization_profiling.cc
+// for backward compatibility, and the compatible (non-debug) block profile
+// output from the Go runtime.
+func parseCppContention(r *bytes.Buffer) (*Profile, error) {
+	p := &Profile{
+		PeriodType: &ValueType{Type: "contentions", Unit: "count"},
+		Period:     1,
+		SampleType: []*ValueType{
+			{Type: "contentions", Unit: "count"},
+			{Type: "delay", Unit: "nanoseconds"},
+		},
+	}
+
+	var cpuHz int64
+	var l string
+	var err error
+	// Parse text of the form "attribute = value" before the samples.
+	const delimiter = "="
+	for {
+		l, err = r.ReadString('\n')
+		if err != nil {
+			if err != io.EOF {
+				return nil, err
+			}
+
+			if l == "" {
+				break
+			}
+		}
+		if isSpaceOrComment(l) {
+			continue
+		}
+
+		if l = strings.TrimSpace(l); l == "" {
+			continue
+		}
+
+		if strings.HasPrefix(l, "---") {
+			break
+		}
+
+		attr := strings.SplitN(l, delimiter, 2)
+		if len(attr) != 2 {
+			break
+		}
+		key, val := strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1])
+		var err error
+		switch key {
+		case "cycles/second":
+			if cpuHz, err = strconv.ParseInt(val, 0, 64); err != nil {
+				return nil, errUnrecognized
+			}
+		case "sampling period":
+			if p.Period, err = strconv.ParseInt(val, 0, 64); err != nil {
+				return nil, errUnrecognized
+			}
+		case "ms since reset":
+			ms, err := strconv.ParseInt(val, 0, 64)
+			if err != nil {
+				return nil, errUnrecognized
+			}
+			p.DurationNanos = ms * 1000 * 1000
+		case "format":
+			// CPP contentionz profiles don't have format.
+			return nil, errUnrecognized
+		case "resolution":
+			// CPP contentionz profiles don't have resolution.
+			return nil, errUnrecognized
+		case "discarded samples":
+		default:
+			return nil, errUnrecognized
+		}
+	}
+
+	locs := make(map[uint64]*Location)
+	for {
+		if !isSpaceOrComment(l) {
+			if l = strings.TrimSpace(l); strings.HasPrefix(l, "---") {
+				break
+			}
+			value, addrs, err := parseContentionSample(l, p.Period, cpuHz)
+			if err != nil {
+				return nil, err
+			}
+			var sloc []*Location
+			for _, addr := range addrs {
+				// Addresses from stack traces point to the next instruction after
+				// each call. Adjust by -1 to land somewhere on the actual call.
+				addr--
+				loc := locs[addr]
+				if locs[addr] == nil {
+					loc = &Location{
+						Address: addr,
+					}
+					p.Location = append(p.Location, loc)
+					locs[addr] = loc
+				}
+				sloc = append(sloc, loc)
+			}
+			p.Sample = append(p.Sample, &Sample{
+				Value:    value,
+				Location: sloc,
+			})
+		}
+
+		if l, err = r.ReadString('\n'); err != nil {
+			if err != io.EOF {
+				return nil, err
+			}
+			if l == "" {
+				break
+			}
+		}
+	}
+
+	if err = parseAdditionalSections(l, r, p); err != nil {
+		return nil, err
+	}
+
+	return p, nil
+}
+
+// parseContentionSample parses a single row from a contention profile
+// into a new Sample.
+func parseContentionSample(line string, period, cpuHz int64) (value []int64, addrs []uint64, err error) {
+	sampleData := contentionSampleRE.FindStringSubmatch(line)
+	if sampleData == nil {
+		return value, addrs, errUnrecognized
+	}
+
+	v1, err := strconv.ParseInt(sampleData[1], 10, 64)
+	if err != nil {
+		return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err)
+	}
+	v2, err := strconv.ParseInt(sampleData[2], 10, 64)
+	if err != nil {
+		return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err)
+	}
+
+	// Unsample values if period and cpuHz are available.
+	// - Delays are scaled to cycles and then to nanoseconds.
+	// - Contentions are scaled to cycles.
+	if period > 0 {
+		if cpuHz > 0 {
+			cpuGHz := float64(cpuHz) / 1e9
+			v1 = int64(float64(v1) * float64(period) / cpuGHz)
+		}
+		v2 = v2 * period
+	}
+
+	value = []int64{v2, v1}
+	addrs = parseHexAddresses(sampleData[3])
+
+	return value, addrs, nil
+}
+
+// parseThread parses a Threadz profile and returns a new Profile.
+func parseThread(b []byte) (*Profile, error) {
+	r := bytes.NewBuffer(b)
+
+	var line string
+	var err error
+	for {
+		// Skip past comments and empty lines seeking a real header.
+		line, err = r.ReadString('\n')
+		if err != nil {
+			return nil, err
+		}
+		if !isSpaceOrComment(line) {
+			break
+		}
+	}
+
+	if m := threadzStartRE.FindStringSubmatch(line); m != nil {
+		// Advance over initial comments until first stack trace.
+		for {
+			line, err = r.ReadString('\n')
+			if err != nil {
+				if err != io.EOF {
+					return nil, err
+				}
+
+				if line == "" {
+					break
+				}
+			}
+			if sectionTrigger(line) != unrecognizedSection || line[0] == '-' {
+				break
+			}
+		}
+	} else if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 {
+		return nil, errUnrecognized
+	}
+
+	p := &Profile{
+		SampleType: []*ValueType{{Type: "thread", Unit: "count"}},
+		PeriodType: &ValueType{Type: "thread", Unit: "count"},
+		Period:     1,
+	}
+
+	locs := make(map[uint64]*Location)
+	// Recognize each thread and populate profile samples.
+	for sectionTrigger(line) == unrecognizedSection {
+		if strings.HasPrefix(line, "---- no stack trace for") {
+			line = ""
+			break
+		}
+		if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 {
+			return nil, errUnrecognized
+		}
+
+		var addrs []uint64
+		line, addrs, err = parseThreadSample(r)
+		if err != nil {
+			return nil, errUnrecognized
+		}
+		if len(addrs) == 0 {
+			// We got a --same as previous threads--. Bump counters.
+			if len(p.Sample) > 0 {
+				s := p.Sample[len(p.Sample)-1]
+				s.Value[0]++
+			}
+			continue
+		}
+
+		var sloc []*Location
+		for _, addr := range addrs {
+			// Addresses from stack traces point to the next instruction after
+			// each call. Adjust by -1 to land somewhere on the actual call.
+			addr--
+			loc := locs[addr]
+			if locs[addr] == nil {
+				loc = &Location{
+					Address: addr,
+				}
+				p.Location = append(p.Location, loc)
+				locs[addr] = loc
+			}
+			sloc = append(sloc, loc)
+		}
+
+		p.Sample = append(p.Sample, &Sample{
+			Value:    []int64{1},
+			Location: sloc,
+		})
+	}
+
+	if err = parseAdditionalSections(line, r, p); err != nil {
+		return nil, err
+	}
+
+	return p, nil
+}
+
+// parseThreadSample parses a symbolized or unsymbolized stack trace.
+// Returns the first line after the traceback, the sample (or nil if
+// it hits a 'same-as-previous' marker) and an error.
+func parseThreadSample(b *bytes.Buffer) (nextl string, addrs []uint64, err error) {
+	var l string
+	sameAsPrevious := false
+	for {
+		if l, err = b.ReadString('\n'); err != nil {
+			if err != io.EOF {
+				return "", nil, err
+			}
+			if l == "" {
+				break
+			}
+		}
+		if l = strings.TrimSpace(l); l == "" {
+			continue
+		}
+
+		if strings.HasPrefix(l, "---") {
+			break
+		}
+		if strings.Contains(l, "same as previous thread") {
+			sameAsPrevious = true
+			continue
+		}
+
+		addrs = append(addrs, parseHexAddresses(l)...)
+	}
+
+	if sameAsPrevious {
+		return l, nil, nil
+	}
+	return l, addrs, nil
+}
+
+// parseAdditionalSections parses any additional sections in the
+// profile, ignoring any unrecognized sections.
+func parseAdditionalSections(l string, b *bytes.Buffer, p *Profile) (err error) {
+	for {
+		if sectionTrigger(l) == memoryMapSection {
+			break
+		}
+		// Ignore any unrecognized sections.
+		if l, err := b.ReadString('\n'); err != nil {
+			if err != io.EOF {
+				return err
+			}
+			if l == "" {
+				break
+			}
+		}
+	}
+	return p.ParseMemoryMap(b)
+}
+
+// ParseMemoryMap parses a memory map in the format of
+// /proc/self/maps, and overrides the mappings in the current profile.
+// It renumbers the samples and locations in the profile correspondingly.
+func (p *Profile) ParseMemoryMap(rd io.Reader) error {
+	b := bufio.NewReader(rd)
+
+	var attrs []string
+	var r *strings.Replacer
+	const delimiter = "="
+	for {
+		l, err := b.ReadString('\n')
+		if err != nil {
+			if err != io.EOF {
+				return err
+			}
+			if l == "" {
+				break
+			}
+		}
+		if l = strings.TrimSpace(l); l == "" {
+			continue
+		}
+
+		if r != nil {
+			l = r.Replace(l)
+		}
+		m, err := parseMappingEntry(l)
+		if err != nil {
+			if err == errUnrecognized {
+				// Recognize assignments of the form: attr=value, and replace
+				// $attr with value on subsequent mappings.
+				if attr := strings.SplitN(l, delimiter, 2); len(attr) == 2 {
+					attrs = append(attrs, "$"+strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1]))
+					r = strings.NewReplacer(attrs...)
+				}
+				// Ignore any unrecognized entries
+				continue
+			}
+			return err
+		}
+		if m == nil || (m.File == "" && len(p.Mapping) != 0) {
+			// In some cases the first entry may include the address range
+			// but not the name of the file. It should be followed by
+			// another entry with the name.
+			continue
+		}
+		if len(p.Mapping) == 1 && p.Mapping[0].File == "" {
+			// Update the name if this is the entry following that empty one.
+			p.Mapping[0].File = m.File
+			continue
+		}
+		p.Mapping = append(p.Mapping, m)
+	}
+	p.remapLocationIDs()
+	p.remapFunctionIDs()
+	p.remapMappingIDs()
+	return nil
+}
+
+func parseMappingEntry(l string) (*Mapping, error) {
+	mapping := &Mapping{}
+	var err error
+	if me := procMapsRE.FindStringSubmatch(l); len(me) == 9 {
+		if !strings.Contains(me[3], "x") {
+			// Skip non-executable entries.
+			return nil, nil
+		}
+		if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil {
+			return nil, errUnrecognized
+		}
+		if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil {
+			return nil, errUnrecognized
+		}
+		if me[4] != "" {
+			if mapping.Offset, err = strconv.ParseUint(me[4], 16, 64); err != nil {
+				return nil, errUnrecognized
+			}
+		}
+		mapping.File = me[8]
+		return mapping, nil
+	}
+
+	if me := briefMapsRE.FindStringSubmatch(l); len(me) == 6 {
+		if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil {
+			return nil, errUnrecognized
+		}
+		if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil {
+			return nil, errUnrecognized
+		}
+		mapping.File = me[3]
+		if me[5] != "" {
+			if mapping.Offset, err = strconv.ParseUint(me[5], 16, 64); err != nil {
+				return nil, errUnrecognized
+			}
+		}
+		return mapping, nil
+	}
+
+	return nil, errUnrecognized
+}
+
+type sectionType int
+
+const (
+	unrecognizedSection sectionType = iota
+	memoryMapSection
+)
+
+var memoryMapTriggers = []string{
+	"--- Memory map: ---",
+	"MAPPED_LIBRARIES:",
+}
+
+func sectionTrigger(line string) sectionType {
+	for _, trigger := range memoryMapTriggers {
+		if strings.Contains(line, trigger) {
+			return memoryMapSection
+		}
+	}
+	return unrecognizedSection
+}
+
+func (p *Profile) addLegacyFrameInfo() {
+	switch {
+	case isProfileType(p, heapzSampleTypes) ||
+		isProfileType(p, heapzInUseSampleTypes) ||
+		isProfileType(p, heapzAllocSampleTypes):
+		p.DropFrames, p.KeepFrames = allocRxStr, allocSkipRxStr
+	case isProfileType(p, contentionzSampleTypes):
+		p.DropFrames, p.KeepFrames = lockRxStr, ""
+	default:
+		p.DropFrames, p.KeepFrames = cpuProfilerRxStr, ""
+	}
+}
+
+var heapzSampleTypes = []string{"allocations", "size"} // early Go pprof profiles
+var heapzInUseSampleTypes = []string{"inuse_objects", "inuse_space"}
+var heapzAllocSampleTypes = []string{"alloc_objects", "alloc_space"}
+var contentionzSampleTypes = []string{"contentions", "delay"}
+
+func isProfileType(p *Profile, t []string) bool {
+	st := p.SampleType
+	if len(st) != len(t) {
+		return false
+	}
+
+	for i := range st {
+		if st[i].Type != t[i] {
+			return false
+		}
+	}
+	return true
+}
+
+var allocRxStr = strings.Join([]string{
+	// POSIX entry points.
+	`calloc`,
+	`cfree`,
+	`malloc`,
+	`free`,
+	`memalign`,
+	`do_memalign`,
+	`(__)?posix_memalign`,
+	`pvalloc`,
+	`valloc`,
+	`realloc`,
+
+	// TC malloc.
+	`tcmalloc::.*`,
+	`tc_calloc`,
+	`tc_cfree`,
+	`tc_malloc`,
+	`tc_free`,
+	`tc_memalign`,
+	`tc_posix_memalign`,
+	`tc_pvalloc`,
+	`tc_valloc`,
+	`tc_realloc`,
+	`tc_new`,
+	`tc_delete`,
+	`tc_newarray`,
+	`tc_deletearray`,
+	`tc_new_nothrow`,
+	`tc_newarray_nothrow`,
+
+	// Memory-allocation routines on OS X.
+	`malloc_zone_malloc`,
+	`malloc_zone_calloc`,
+	`malloc_zone_valloc`,
+	`malloc_zone_realloc`,
+	`malloc_zone_memalign`,
+	`malloc_zone_free`,
+
+	// Go runtime
+	`runtime\..*`,
+
+	// Other misc. memory allocation routines
+	`BaseArena::.*`,
+	`(::)?do_malloc_no_errno`,
+	`(::)?do_malloc_pages`,
+	`(::)?do_malloc`,
+	`DoSampledAllocation`,
+	`MallocedMemBlock::MallocedMemBlock`,
+	`_M_allocate`,
+	`__builtin_(vec_)?delete`,
+	`__builtin_(vec_)?new`,
+	`__gnu_cxx::new_allocator::allocate`,
+	`__libc_malloc`,
+	`__malloc_alloc_template::allocate`,
+	`allocate`,
+	`cpp_alloc`,
+	`operator new(\[\])?`,
+	`simple_alloc::allocate`,
+}, `|`)
+
+var allocSkipRxStr = strings.Join([]string{
+	// Preserve Go runtime frames that appear in the middle/bottom of
+	// the stack.
+	`runtime\.panic`,
+	`runtime\.reflectcall`,
+	`runtime\.call[0-9]*`,
+}, `|`)
+
+var cpuProfilerRxStr = strings.Join([]string{
+	`ProfileData::Add`,
+	`ProfileData::prof_handler`,
+	`CpuProfiler::prof_handler`,
+	`__pthread_sighandler`,
+	`__restore`,
+}, `|`)
+
+var lockRxStr = strings.Join([]string{
+	`RecordLockProfileData`,
+	`(base::)?RecordLockProfileData.*`,
+	`(base::)?SubmitMutexProfileData.*`,
+	`(base::)?SubmitSpinLockProfileData.*`,
+	`(Mutex::)?AwaitCommon.*`,
+	`(Mutex::)?Unlock.*`,
+	`(Mutex::)?UnlockSlow.*`,
+	`(Mutex::)?ReaderUnlock.*`,
+	`(MutexLock::)?~MutexLock.*`,
+	`(SpinLock::)?Unlock.*`,
+	`(SpinLock::)?SlowUnlock.*`,
+	`(SpinLockHolder::)?~SpinLockHolder.*`,
+}, `|`)
diff --git a/src/cmd/internal/pprof/profile/profile.go b/src/internal/pprof/profile/profile.go
similarity index 100%
rename from src/cmd/internal/pprof/profile/profile.go
rename to src/internal/pprof/profile/profile.go
diff --git a/src/internal/pprof/profile/profile_test.go b/src/internal/pprof/profile/profile_test.go
new file mode 100644
index 0000000..e1963f3
--- /dev/null
+++ b/src/internal/pprof/profile/profile_test.go
@@ -0,0 +1,79 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package profile
+
+import (
+	"bytes"
+	"testing"
+)
+
+func TestEmptyProfile(t *testing.T) {
+	var buf bytes.Buffer
+	p, err := Parse(&buf)
+	if err != nil {
+		t.Error("Want no error, got", err)
+	}
+	if p == nil {
+		t.Fatal("Want a valid profile, got <nil>")
+	}
+	if !p.Empty() {
+		t.Errorf("Profile should be empty, got %#v", p)
+	}
+}
+
+func TestParseContention(t *testing.T) {
+	tests := []struct {
+		name    string
+		in      string
+		wantErr bool
+	}{
+		{
+			name: "valid",
+			in: `--- mutex:
+cycles/second=3491920901
+sampling period=1
+43227965305 1659640 @ 0x45e851 0x45f764 0x4a2be1 0x44ea31
+34035731690 15760 @ 0x45e851 0x45f764 0x4a2b17 0x44ea31
+`,
+		},
+		{
+			name: "valid with comment",
+			in: `--- mutex:
+cycles/second=3491920901
+sampling period=1
+43227965305 1659640 @ 0x45e851 0x45f764 0x4a2be1 0x44ea31
+#	0x45e850	sync.(*Mutex).Unlock+0x80	/go/src/sync/mutex.go:126
+#	0x45f763	sync.(*RWMutex).Unlock+0x83	/go/src/sync/rwmutex.go:125
+#	0x4a2be0	main.main.func3+0x70		/go/src/internal/pprof/profile/a_binary.go:58
+
+34035731690 15760 @ 0x45e851 0x45f764 0x4a2b17 0x44ea31
+#	0x45e850	sync.(*Mutex).Unlock+0x80	/go/src/sync/mutex.go:126
+#	0x45f763	sync.(*RWMutex).Unlock+0x83	/go/src/sync/rwmutex.go:125
+#	0x4a2b16	main.main.func2+0xd6		/go/src/internal/pprof/profile/a_binary.go:48
+`,
+		},
+		{
+			name:    "empty",
+			in:      `--- mutex:`,
+			wantErr: true,
+		},
+		{
+			name: "invalid header",
+			in: `--- channel:
+43227965305 1659640 @ 0x45e851 0x45f764 0x4a2be1 0x44ea31`,
+			wantErr: true,
+		},
+	}
+	for _, tc := range tests {
+		_, err := parseContention([]byte(tc.in))
+		if tc.wantErr && err == nil {
+			t.Errorf("parseContention(%q) succeeded unexpectedly", tc.name)
+		}
+		if !tc.wantErr && err != nil {
+			t.Errorf("parseContention(%q) failed unexpectedly: %v", tc.name, err)
+		}
+	}
+
+}
diff --git a/src/cmd/internal/pprof/profile/proto.go b/src/internal/pprof/profile/proto.go
similarity index 100%
rename from src/cmd/internal/pprof/profile/proto.go
rename to src/internal/pprof/profile/proto.go
diff --git a/src/cmd/internal/pprof/profile/proto_test.go b/src/internal/pprof/profile/proto_test.go
similarity index 100%
rename from src/cmd/internal/pprof/profile/proto_test.go
rename to src/internal/pprof/profile/proto_test.go
diff --git a/src/cmd/internal/pprof/profile/prune.go b/src/internal/pprof/profile/prune.go
similarity index 100%
rename from src/cmd/internal/pprof/profile/prune.go
rename to src/internal/pprof/profile/prune.go
diff --git a/src/internal/race/norace.go b/src/internal/race/norace.go
index 7ef5912..d83c016 100644
--- a/src/internal/race/norace.go
+++ b/src/internal/race/norace.go
@@ -38,3 +38,5 @@ func ReadRange(addr unsafe.Pointer, len int) {
 
 func WriteRange(addr unsafe.Pointer, len int) {
 }
+
+func Errors() int { return 0 }
diff --git a/src/internal/race/race.go b/src/internal/race/race.go
index 6c721f6..2e7d97b 100644
--- a/src/internal/race/race.go
+++ b/src/internal/race/race.go
@@ -48,3 +48,7 @@ func ReadRange(addr unsafe.Pointer, len int) {
 func WriteRange(addr unsafe.Pointer, len int) {
 	runtime.RaceWriteRange(addr, len)
 }
+
+func Errors() int {
+	return runtime.RaceErrors()
+}
diff --git a/src/internal/syscall/unix/getrandom_linux_mipsx.go b/src/internal/syscall/unix/getrandom_linux_mipsx.go
new file mode 100644
index 0000000..af7b722
--- /dev/null
+++ b/src/internal/syscall/unix/getrandom_linux_mipsx.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+package unix
+
+// Linux getrandom system call number.
+// See GetRandom in getrandom_linux.go.
+const randomTrap uintptr = 4353
diff --git a/src/internal/syscall/windows/mksyscall.go b/src/internal/syscall/windows/mksyscall.go
new file mode 100644
index 0000000..91fa2b3
--- /dev/null
+++ b/src/internal/syscall/windows/mksyscall.go
@@ -0,0 +1,7 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package windows
+
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go security_windows.go
diff --git a/src/internal/syscall/windows/registry/mksyscall.go b/src/internal/syscall/windows/registry/mksyscall.go
new file mode 100644
index 0000000..0772153
--- /dev/null
+++ b/src/internal/syscall/windows/registry/mksyscall.go
@@ -0,0 +1,7 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package registry
+
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go
diff --git a/src/internal/syscall/windows/registry/syscall.go b/src/internal/syscall/windows/registry/syscall.go
index 5426cae..a6525da 100644
--- a/src/internal/syscall/windows/registry/syscall.go
+++ b/src/internal/syscall/windows/registry/syscall.go
@@ -8,8 +8,6 @@ package registry
 
 import "syscall"
 
-//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go
-
 const (
 	_REG_OPTION_NON_VOLATILE = 0
 
diff --git a/src/internal/syscall/windows/registry/zsyscall_windows.go b/src/internal/syscall/windows/registry/zsyscall_windows.go
index 62affc0..a3a1f5f 100644
--- a/src/internal/syscall/windows/registry/zsyscall_windows.go
+++ b/src/internal/syscall/windows/registry/zsyscall_windows.go
@@ -10,6 +10,31 @@ import (
 
 var _ unsafe.Pointer
 
+// Do the interface allocations only once for common
+// Errno values.
+const (
+	errnoERROR_IO_PENDING = 997
+)
+
+var (
+	errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e syscall.Errno) error {
+	switch e {
+	case 0:
+		return nil
+	case errnoERROR_IO_PENDING:
+		return errERROR_IO_PENDING
+	}
+	// TODO: add more here, after collecting data on the common
+	// error values see on Windows. (perhaps when running
+	// all.bat?)
+	return e
+}
+
 var (
 	modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
 	modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
@@ -76,7 +101,7 @@ func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32,
 	n = uint32(r0)
 	if n == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = syscall.EINVAL
 		}
diff --git a/src/internal/syscall/windows/reparse_windows.go b/src/internal/syscall/windows/reparse_windows.go
new file mode 100644
index 0000000..7c6ad8f
--- /dev/null
+++ b/src/internal/syscall/windows/reparse_windows.go
@@ -0,0 +1,64 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package windows
+
+const (
+	FSCTL_SET_REPARSE_POINT    = 0x000900A4
+	IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
+
+	SYMLINK_FLAG_RELATIVE = 1
+)
+
+// These structures are described
+// in https://msdn.microsoft.com/en-us/library/cc232007.aspx
+// and https://msdn.microsoft.com/en-us/library/cc232006.aspx.
+
+// REPARSE_DATA_BUFFER_HEADER is a common part of REPARSE_DATA_BUFFER structure.
+type REPARSE_DATA_BUFFER_HEADER struct {
+	ReparseTag uint32
+	// The size, in bytes, of the reparse data that follows
+	// the common portion of the REPARSE_DATA_BUFFER element.
+	// This value is the length of the data starting at the
+	// SubstituteNameOffset field.
+	ReparseDataLength uint16
+	Reserved          uint16
+}
+
+type SymbolicLinkReparseBuffer struct {
+	// The integer that contains the offset, in bytes,
+	// of the substitute name string in the PathBuffer array,
+	// computed as an offset from byte 0 of PathBuffer. Note that
+	// this offset must be divided by 2 to get the array index.
+	SubstituteNameOffset uint16
+	// The integer that contains the length, in bytes, of the
+	// substitute name string. If this string is null-terminated,
+	// SubstituteNameLength does not include the Unicode null character.
+	SubstituteNameLength uint16
+	// PrintNameOffset is similar to SubstituteNameOffset.
+	PrintNameOffset uint16
+	// PrintNameLength is similar to SubstituteNameLength.
+	PrintNameLength uint16
+	// Flags specifies whether the substitute name is a full path name or
+	// a path name relative to the directory containing the symbolic link.
+	Flags      uint32
+	PathBuffer [1]uint16
+}
+
+type MountPointReparseBuffer struct {
+	// The integer that contains the offset, in bytes,
+	// of the substitute name string in the PathBuffer array,
+	// computed as an offset from byte 0 of PathBuffer. Note that
+	// this offset must be divided by 2 to get the array index.
+	SubstituteNameOffset uint16
+	// The integer that contains the length, in bytes, of the
+	// substitute name string. If this string is null-terminated,
+	// SubstituteNameLength does not include the Unicode null character.
+	SubstituteNameLength uint16
+	// PrintNameOffset is similar to SubstituteNameOffset.
+	PrintNameOffset uint16
+	// PrintNameLength is similar to SubstituteNameLength.
+	PrintNameLength uint16
+	PathBuffer      [1]uint16
+}
diff --git a/src/internal/syscall/windows/security_windows.go b/src/internal/syscall/windows/security_windows.go
new file mode 100644
index 0000000..2c145e1
--- /dev/null
+++ b/src/internal/syscall/windows/security_windows.go
@@ -0,0 +1,57 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package windows
+
+import (
+	"syscall"
+)
+
+const (
+	SecurityAnonymous      = 0
+	SecurityIdentification = 1
+	SecurityImpersonation  = 2
+	SecurityDelegation     = 3
+)
+
+//sys	ImpersonateSelf(impersonationlevel uint32) (err error) = advapi32.ImpersonateSelf
+//sys	RevertToSelf() (err error) = advapi32.RevertToSelf
+
+const (
+	TOKEN_ADJUST_PRIVILEGES = 0x0020
+	SE_PRIVILEGE_ENABLED    = 0x00000002
+)
+
+type LUID struct {
+	LowPart  uint32
+	HighPart int32
+}
+
+type LUID_AND_ATTRIBUTES struct {
+	Luid       LUID
+	Attributes uint32
+}
+
+type TOKEN_PRIVILEGES struct {
+	PrivilegeCount uint32
+	Privileges     [1]LUID_AND_ATTRIBUTES
+}
+
+//sys	OpenThreadToken(h syscall.Handle, access uint32, openasself bool, token *syscall.Token) (err error) = advapi32.OpenThreadToken
+//sys	LookupPrivilegeValue(systemname *uint16, name *uint16, luid *LUID) (err error) = advapi32.LookupPrivilegeValueW
+//sys	adjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (ret uint32, err error) [true] = advapi32.AdjustTokenPrivileges
+
+func AdjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) error {
+	ret, err := adjustTokenPrivileges(token, disableAllPrivileges, newstate, buflen, prevstate, returnlen)
+	if ret == 0 {
+		// AdjustTokenPrivileges call failed
+		return err
+	}
+	// AdjustTokenPrivileges call succeeded
+	if err == syscall.EINVAL {
+		// GetLastError returned ERROR_SUCCESS
+		return nil
+	}
+	return err
+}
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
index 7b2bc79..ec08a5a 100644
--- a/src/internal/syscall/windows/syscall_windows.go
+++ b/src/internal/syscall/windows/syscall_windows.go
@@ -6,7 +6,10 @@ package windows
 
 import "syscall"
 
-//go:generate go run ../../../syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
+const (
+	ERROR_SHARING_VIOLATION      syscall.Errno = 32
+	ERROR_NO_UNICODE_TRANSLATION syscall.Errno = 1113
+)
 
 const GAA_FLAG_INCLUDE_PREFIX = 0x00000010
 
@@ -107,6 +110,7 @@ const (
 //sys	GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) = iphlpapi.GetAdaptersAddresses
 //sys	GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
 //sys	MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW
+//sys	GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) = kernel32.GetModuleFileNameW
 
 const (
 	ComputerNameNetBIOS                   = 0
@@ -139,5 +143,25 @@ func Rename(oldpath, newpath string) error {
 	return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING)
 }
 
+const MB_ERR_INVALID_CHARS = 8
+
 //sys	GetACP() (acp uint32) = kernel32.GetACP
+//sys	GetConsoleCP() (ccp uint32) = kernel32.GetConsoleCP
 //sys	MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) = kernel32.MultiByteToWideChar
+//sys	GetCurrentThread() (pseudoHandle syscall.Handle, err error) = kernel32.GetCurrentThread
+
+const STYPE_DISKTREE = 0x00
+
+type SHARE_INFO_2 struct {
+	Netname     *uint16
+	Type        uint32
+	Remark      *uint16
+	Permissions uint32
+	MaxUses     uint32
+	CurrentUses uint32
+	Path        *uint16
+	Passwd      *uint16
+}
+
+//sys  NetShareAdd(serverName *uint16, level uint32, buf *byte, parmErr *uint16) (neterr error) = netapi32.NetShareAdd
+//sys  NetShareDel(serverName *uint16, netName *uint16, reserved uint32) (neterr error) = netapi32.NetShareDel
diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
index 6929acf..7a2ffee 100644
--- a/src/internal/syscall/windows/zsyscall_windows.go
+++ b/src/internal/syscall/windows/zsyscall_windows.go
@@ -10,15 +10,52 @@ import (
 
 var _ unsafe.Pointer
 
+// Do the interface allocations only once for common
+// Errno values.
+const (
+	errnoERROR_IO_PENDING = 997
+)
+
+var (
+	errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e syscall.Errno) error {
+	switch e {
+	case 0:
+		return nil
+	case errnoERROR_IO_PENDING:
+		return errERROR_IO_PENDING
+	}
+	// TODO: add more here, after collecting data on the common
+	// error values see on Windows. (perhaps when running
+	// all.bat?)
+	return e
+}
+
 var (
 	modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
 	modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
+	modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
+	modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
 
-	procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
-	procGetComputerNameExW   = modkernel32.NewProc("GetComputerNameExW")
-	procMoveFileExW          = modkernel32.NewProc("MoveFileExW")
-	procGetACP               = modkernel32.NewProc("GetACP")
-	procMultiByteToWideChar  = modkernel32.NewProc("MultiByteToWideChar")
+	procGetAdaptersAddresses  = modiphlpapi.NewProc("GetAdaptersAddresses")
+	procGetComputerNameExW    = modkernel32.NewProc("GetComputerNameExW")
+	procMoveFileExW           = modkernel32.NewProc("MoveFileExW")
+	procGetModuleFileNameW    = modkernel32.NewProc("GetModuleFileNameW")
+	procGetACP                = modkernel32.NewProc("GetACP")
+	procGetConsoleCP          = modkernel32.NewProc("GetConsoleCP")
+	procMultiByteToWideChar   = modkernel32.NewProc("MultiByteToWideChar")
+	procGetCurrentThread      = modkernel32.NewProc("GetCurrentThread")
+	procNetShareAdd           = modnetapi32.NewProc("NetShareAdd")
+	procNetShareDel           = modnetapi32.NewProc("NetShareDel")
+	procImpersonateSelf       = modadvapi32.NewProc("ImpersonateSelf")
+	procRevertToSelf          = modadvapi32.NewProc("RevertToSelf")
+	procOpenThreadToken       = modadvapi32.NewProc("OpenThreadToken")
+	procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
+	procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
 )
 
 func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) {
@@ -33,7 +70,7 @@ func GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) {
 	r1, _, e1 := syscall.Syscall(procGetComputerNameExW.Addr(), 3, uintptr(nameformat), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)))
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = syscall.EINVAL
 		}
@@ -45,7 +82,20 @@ func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) {
 	r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags))
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
+		} else {
+			err = syscall.EINVAL
+		}
+	}
+	return
+}
+
+func GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) {
+	r0, _, e1 := syscall.Syscall(procGetModuleFileNameW.Addr(), 3, uintptr(module), uintptr(unsafe.Pointer(fn)), uintptr(len))
+	n = uint32(r0)
+	if n == 0 {
+		if e1 != 0 {
+			err = errnoErr(e1)
 		} else {
 			err = syscall.EINVAL
 		}
@@ -59,12 +109,120 @@ func GetACP() (acp uint32) {
 	return
 }
 
+func GetConsoleCP() (ccp uint32) {
+	r0, _, _ := syscall.Syscall(procGetConsoleCP.Addr(), 0, 0, 0, 0)
+	ccp = uint32(r0)
+	return
+}
+
 func MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) {
 	r0, _, e1 := syscall.Syscall6(procMultiByteToWideChar.Addr(), 6, uintptr(codePage), uintptr(dwFlags), uintptr(unsafe.Pointer(str)), uintptr(nstr), uintptr(unsafe.Pointer(wchar)), uintptr(nwchar))
 	nwrite = int32(r0)
 	if nwrite == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
+		} else {
+			err = syscall.EINVAL
+		}
+	}
+	return
+}
+
+func GetCurrentThread() (pseudoHandle syscall.Handle, err error) {
+	r0, _, e1 := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
+	pseudoHandle = syscall.Handle(r0)
+	if pseudoHandle == 0 {
+		if e1 != 0 {
+			err = errnoErr(e1)
+		} else {
+			err = syscall.EINVAL
+		}
+	}
+	return
+}
+
+func NetShareAdd(serverName *uint16, level uint32, buf *byte, parmErr *uint16) (neterr error) {
+	r0, _, _ := syscall.Syscall6(procNetShareAdd.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(parmErr)), 0, 0)
+	if r0 != 0 {
+		neterr = syscall.Errno(r0)
+	}
+	return
+}
+
+func NetShareDel(serverName *uint16, netName *uint16, reserved uint32) (neterr error) {
+	r0, _, _ := syscall.Syscall(procNetShareDel.Addr(), 3, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(netName)), uintptr(reserved))
+	if r0 != 0 {
+		neterr = syscall.Errno(r0)
+	}
+	return
+}
+
+func ImpersonateSelf(impersonationlevel uint32) (err error) {
+	r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(impersonationlevel), 0, 0)
+	if r1 == 0 {
+		if e1 != 0 {
+			err = errnoErr(e1)
+		} else {
+			err = syscall.EINVAL
+		}
+	}
+	return
+}
+
+func RevertToSelf() (err error) {
+	r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
+	if r1 == 0 {
+		if e1 != 0 {
+			err = errnoErr(e1)
+		} else {
+			err = syscall.EINVAL
+		}
+	}
+	return
+}
+
+func OpenThreadToken(h syscall.Handle, access uint32, openasself bool, token *syscall.Token) (err error) {
+	var _p0 uint32
+	if openasself {
+		_p0 = 1
+	} else {
+		_p0 = 0
+	}
+	r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(h), uintptr(access), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
+	if r1 == 0 {
+		if e1 != 0 {
+			err = errnoErr(e1)
+		} else {
+			err = syscall.EINVAL
+		}
+	}
+	return
+}
+
+func LookupPrivilegeValue(systemname *uint16, name *uint16, luid *LUID) (err error) {
+	r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemname)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
+	if r1 == 0 {
+		if e1 != 0 {
+			err = errnoErr(e1)
+		} else {
+			err = syscall.EINVAL
+		}
+	}
+	return
+}
+
+func adjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (ret uint32, err error) {
+	var _p0 uint32
+	if disableAllPrivileges {
+		_p0 = 1
+	} else {
+		_p0 = 0
+	}
+	r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(newstate)), uintptr(buflen), uintptr(unsafe.Pointer(prevstate)), uintptr(unsafe.Pointer(returnlen)))
+	ret = uint32(r0)
+	if true {
+		if e1 != 0 {
+			err = errnoErr(e1)
 		} else {
 			err = syscall.EINVAL
 		}
diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go
index f134f6b..10384b6 100644
--- a/src/internal/testenv/testenv.go
+++ b/src/internal/testenv/testenv.go
@@ -11,6 +11,7 @@
 package testenv
 
 import (
+	"errors"
 	"flag"
 	"os"
 	"os/exec"
@@ -67,26 +68,36 @@ func MustHaveGoRun(t *testing.T) {
 }
 
 // GoToolPath reports the path to the Go tool.
+// It is a convenience wrapper around GoTool.
 // If the tool is unavailable GoToolPath calls t.Skip.
 // If the tool should be available and isn't, GoToolPath calls t.Fatal.
 func GoToolPath(t *testing.T) string {
 	MustHaveGoBuild(t)
+	path, err := GoTool()
+	if err != nil {
+		t.Fatal(err)
+	}
+	return path
+}
 
+// GoTool reports the path to the Go tool.
+func GoTool() (string, error) {
+	if !HasGoBuild() {
+		return "", errors.New("platform cannot run go tool")
+	}
 	var exeSuffix string
 	if runtime.GOOS == "windows" {
 		exeSuffix = ".exe"
 	}
-
 	path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
 	if _, err := os.Stat(path); err == nil {
-		return path
+		return path, nil
 	}
-
 	goBin, err := exec.LookPath("go" + exeSuffix)
 	if err != nil {
-		t.Fatalf("cannot find go tool: %v", err)
+		return "", errors.New("cannot find go tool: " + err.Error())
 	}
-	return goBin
+	return goBin, nil
 }
 
 // HasExec reports whether the current system can start new processes
@@ -127,6 +138,37 @@ func MustHaveExternalNetwork(t *testing.T) {
 	}
 }
 
+// HasSymlink reports whether the current system can use os.Symlink.
+func HasSymlink() bool {
+	ok, _ := hasSymlink()
+	return ok
+}
+
+// MustHaveSymlink reports whether the current system can use os.Symlink.
+// If not, MustHaveSymlink calls t.Skip with an explanation.
+func MustHaveSymlink(t *testing.T) {
+	ok, reason := hasSymlink()
+	if !ok {
+		t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason)
+	}
+}
+
+// HasLink reports whether the current system can use os.Link.
+func HasLink() bool {
+	// From Android release M (Marshmallow), hard linking files is blocked
+	// and an attempt to call link() on a file will return EACCES.
+	// - https://code.google.com/p/android-developer-preview/issues/detail?id=3150
+	return runtime.GOOS != "plan9" && runtime.GOOS != "android"
+}
+
+// MustHaveLink reports whether the current system can use os.Link.
+// If not, MustHaveLink calls t.Skip with an explanation.
+func MustHaveLink(t *testing.T) {
+	if !HasLink() {
+		t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
+	}
+}
+
 var flaky = flag.Bool("flaky", false, "run known-flaky tests too")
 
 func SkipFlaky(t *testing.T, issue int) {
diff --git a/src/internal/testenv/testenv_notwin.go b/src/internal/testenv/testenv_notwin.go
new file mode 100644
index 0000000..d8ce6cd
--- /dev/null
+++ b/src/internal/testenv/testenv_notwin.go
@@ -0,0 +1,20 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !windows
+
+package testenv
+
+import (
+	"runtime"
+)
+
+func hasSymlink() (ok bool, reason string) {
+	switch runtime.GOOS {
+	case "android", "nacl", "plan9":
+		return false, ""
+	}
+
+	return true, ""
+}
diff --git a/src/internal/testenv/testenv_windows.go b/src/internal/testenv/testenv_windows.go
new file mode 100644
index 0000000..e593f64
--- /dev/null
+++ b/src/internal/testenv/testenv_windows.go
@@ -0,0 +1,49 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testenv
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sync"
+	"syscall"
+)
+
+var symlinkOnce sync.Once
+var winSymlinkErr error
+
+func initWinHasSymlink() {
+	tmpdir, err := ioutil.TempDir("", "symtest")
+	if err != nil {
+		panic("failed to create temp directory: " + err.Error())
+	}
+	defer os.RemoveAll(tmpdir)
+
+	err = os.Symlink("target", filepath.Join(tmpdir, "symlink"))
+	if err != nil {
+		err = err.(*os.LinkError).Err
+		switch err {
+		case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD:
+			winSymlinkErr = err
+		}
+	}
+	os.Remove("target")
+}
+
+func hasSymlink() (ok bool, reason string) {
+	symlinkOnce.Do(initWinHasSymlink)
+
+	switch winSymlinkErr {
+	case nil:
+		return true, ""
+	case syscall.EWINDOWS:
+		return false, ": symlinks are not supported on your version of Windows"
+	case syscall.ERROR_PRIVILEGE_NOT_HELD:
+		return false, ": you don't have enough privileges to create symlinks"
+	}
+
+	return false, ""
+}
diff --git a/src/internal/trace/goroutines.go b/src/internal/trace/goroutines.go
index f8673e2..923a157 100644
--- a/src/internal/trace/goroutines.go
+++ b/src/internal/trace/goroutines.go
@@ -48,7 +48,7 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc {
 			g := &GDesc{ID: ev.Args[0], CreationTime: ev.Ts, gdesc: new(gdesc)}
 			g.blockSchedTime = ev.Ts
 			gs[g.ID] = g
-		case EvGoStart:
+		case EvGoStart, EvGoStartLabel:
 			g := gs[ev.G]
 			if g.PC == 0 {
 				g.PC = ev.Stk[0].PC
@@ -83,6 +83,10 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc {
 			g := gs[ev.G]
 			g.ExecTime += ev.Ts - g.lastStartTime
 			g.blockNetTime = ev.Ts
+		case EvGoBlockGC:
+			g := gs[ev.G]
+			g.ExecTime += ev.Ts - g.lastStartTime
+			g.blockGCTime = ev.Ts
 		case EvGoUnblock:
 			g := gs[ev.Args[0]]
 			if g.blockNetTime != 0 {
diff --git a/src/internal/trace/mkcanned.bash b/src/internal/trace/mkcanned.bash
new file mode 100755
index 0000000..78c5572
--- /dev/null
+++ b/src/internal/trace/mkcanned.bash
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# Copyright 2016 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# mkcanned.bash creates canned traces for the trace test suite using
+# the current Go version.
+
+set -e
+
+if [ $# != 1 ]; then
+    echo "usage: $0 <label>" >&2
+    exit 1
+fi
+
+go test -run ClientServerParallel4 -trace "testdata/http_$1_good" net/http
+go test -run 'TraceStress$|TraceStressStartStop$' runtime/trace -savetraces
+mv ../../runtime/trace/TestTraceStress.trace "testdata/stress_$1_good"
+mv ../../runtime/trace/TestTraceStressStartStop.trace "testdata/stress_start_stop_$1_good"
diff --git a/src/internal/trace/order.go b/src/internal/trace/order.go
index 8ca2da5..36ed58d 100644
--- a/src/internal/trace/order.go
+++ b/src/internal/trace/order.go
@@ -150,7 +150,7 @@ func stateTransition(ev *Event) (g uint64, init, next gState) {
 		g = ev.G
 		init = gState{1, gRunnable}
 		next = gState{2, gWaiting}
-	case EvGoStart:
+	case EvGoStart, EvGoStartLabel:
 		g = ev.G
 		init = gState{ev.Args[1], gRunnable}
 		next = gState{ev.Args[1] + 1, gRunning}
@@ -165,7 +165,8 @@ func stateTransition(ev *Event) (g uint64, init, next gState) {
 		init = gState{noseq, gRunnable}
 		next = gState{seqinc, gRunning}
 	case EvGoBlock, EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect,
-		EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoSleep, EvGoSysBlock:
+		EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoSleep,
+		EvGoSysBlock, EvGoBlockGC:
 		g = ev.G
 		init = gState{noseq, gRunning}
 		next = gState{noseq, gWaiting}
diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go
index c31517f..efa8540 100644
--- a/src/internal/trace/parser.go
+++ b/src/internal/trace/parser.go
@@ -28,12 +28,13 @@ type Event struct {
 	StkID uint64    // unique stack ID
 	Stk   []*Frame  // stack trace (can be empty)
 	Args  [3]uint64 // event-type-specific arguments
+	SArgs []string  // event-type-specific string args
 	// linked event (can be nil), depends on event type:
 	// for GCStart: the GCStop
 	// for GCScanStart: the GCScanDone
 	// for GCSweepStart: the GCSweepDone
 	// for GoCreate: first GoStart of the created goroutine
-	// for GoStart: the associated GoEnd, GoBlock or other blocking event
+	// for GoStart/GoStartLabel: the associated GoEnd, GoBlock or other blocking event
 	// for GoSched/GoPreempt: the next GoStart
 	// for GoBlock and other blocking events: the unblock event
 	// for GoUnblock: the associated GoStart
@@ -56,6 +57,7 @@ const (
 	TimerP   // depicts timer unblocks
 	NetpollP // depicts network unblocks
 	SyscallP // depicts returns from syscalls
+	GCP      // depicts GC state
 )
 
 // Parse parses, post-processes and verifies the trace.
@@ -125,7 +127,9 @@ func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]stri
 		return
 	}
 	switch ver {
-	case 1005, 1007:
+	case 1005, 1007, 1008:
+		// Note: When adding a new version, add canned traces
+		// from the old version to the test suite using mkcanned.bash.
 		break
 	default:
 		err = fmt.Errorf("unsupported trace file version %v.%v (update Go toolchain) %v", ver/1000, ver%1000, ver)
@@ -362,15 +366,18 @@ func parseEvents(ver int, rawEvents []rawEvent, strings map[uint64]string) (even
 				}
 			}
 			switch raw.typ {
-			case EvGoStart, EvGoStartLocal:
+			case EvGoStart, EvGoStartLocal, EvGoStartLabel:
 				lastG = e.Args[0]
 				e.G = lastG
+				if raw.typ == EvGoStartLabel {
+					e.SArgs = []string{strings[e.Args[2]]}
+				}
 			case EvGCStart, EvGCDone, EvGCScanStart, EvGCScanDone:
 				e.G = 0
 			case EvGoEnd, EvGoStop, EvGoSched, EvGoPreempt,
 				EvGoSleep, EvGoBlock, EvGoBlockSend, EvGoBlockRecv,
 				EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet,
-				EvGoSysBlock:
+				EvGoSysBlock, EvGoBlockGC:
 				lastG = 0
 			case EvGoSysExit, EvGoWaiting, EvGoInSyscall:
 				e.G = e.Args[0]
@@ -548,6 +555,8 @@ func postProcessTrace(ver int, events []*Event) error {
 				return fmt.Errorf("previous GC is not ended before a new one (offset %v, time %v)", ev.Off, ev.Ts)
 			}
 			evGC = ev
+			// Attribute this to the global GC state.
+			ev.P = GCP
 		case EvGCDone:
 			if evGC == nil {
 				return fmt.Errorf("bogus GC end (offset %v, time %v)", ev.Off, ev.Ts)
@@ -581,11 +590,13 @@ func postProcessTrace(ver int, events []*Event) error {
 				return fmt.Errorf("g %v is not runnable before EvGoWaiting (offset %v, time %v)", ev.G, ev.Off, ev.Ts)
 			}
 			g.state = gWaiting
+			g.ev = ev
 		case EvGoInSyscall:
 			if g.state != gRunnable {
 				return fmt.Errorf("g %v is not runnable before EvGoInSyscall (offset %v, time %v)", ev.G, ev.Off, ev.Ts)
 			}
 			g.state = gWaiting
+			g.ev = ev
 		case EvGoCreate:
 			if err := checkRunning(p, g, ev, true); err != nil {
 				return err
@@ -594,7 +605,7 @@ func postProcessTrace(ver int, events []*Event) error {
 				return fmt.Errorf("g %v already exists (offset %v, time %v)", ev.Args[0], ev.Off, ev.Ts)
 			}
 			gs[ev.Args[0]] = gdesc{state: gRunnable, ev: ev, evCreate: ev}
-		case EvGoStart:
+		case EvGoStart, EvGoStartLabel:
 			if g.state != gRunnable {
 				return fmt.Errorf("g %v is not runnable before start (offset %v, time %v)", ev.G, ev.Off, ev.Ts)
 			}
@@ -678,7 +689,7 @@ func postProcessTrace(ver int, events []*Event) error {
 			g.state = gRunnable
 			g.ev = ev
 		case EvGoSleep, EvGoBlock, EvGoBlockSend, EvGoBlockRecv,
-			EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet:
+			EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoBlockGC:
 			if err := checkRunning(p, g, ev, false); err != nil {
 				return err
 			}
@@ -853,8 +864,8 @@ const (
 	EvProcStop       = 6  // stop of P [timestamp]
 	EvGCStart        = 7  // GC start [timestamp, seq, stack id]
 	EvGCDone         = 8  // GC done [timestamp]
-	EvGCScanStart    = 9  // GC scan start [timestamp]
-	EvGCScanDone     = 10 // GC scan done [timestamp]
+	EvGCScanStart    = 9  // GC mark termination start [timestamp]
+	EvGCScanDone     = 10 // GC mark termination done [timestamp]
 	EvGCSweepStart   = 11 // GC sweep start [timestamp, stack id]
 	EvGCSweepDone    = 12 // GC sweep done [timestamp]
 	EvGoCreate       = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id]
@@ -885,7 +896,9 @@ const (
 	EvGoStartLocal   = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id]
 	EvGoUnblockLocal = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack]
 	EvGoSysExitLocal = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp]
-	EvCount          = 41
+	EvGoStartLabel   = 41 // goroutine starts running with label [timestamp, goroutine id, seq, label string id]
+	EvGoBlockGC      = 42 // goroutine blocks on GC assist [timestamp, stack]
+	EvCount          = 43
 )
 
 var EventDescriptions = [EvCount]struct {
@@ -935,4 +948,6 @@ var EventDescriptions = [EvCount]struct {
 	EvGoStartLocal:   {"GoStartLocal", 1007, false, []string{"g"}},
 	EvGoUnblockLocal: {"GoUnblockLocal", 1007, true, []string{"g"}},
 	EvGoSysExitLocal: {"GoSysExitLocal", 1007, false, []string{"g", "ts"}},
+	EvGoStartLabel:   {"GoStartLabel", 1008, false, []string{"g", "seq", "label"}},
+	EvGoBlockGC:      {"GoBlockGC", 1008, true, []string{}},
 }
diff --git a/src/internal/trace/parser_test.go b/src/internal/trace/parser_test.go
index daad3e3..d6f580a 100644
--- a/src/internal/trace/parser_test.go
+++ b/src/internal/trace/parser_test.go
@@ -90,52 +90,13 @@ func TestParseVersion(t *testing.T) {
 
 func TestTimestampOverflow(t *testing.T) {
 	// Test that parser correctly handles large timestamps (long tracing).
-	w := newWriter()
-	w.emit(EvBatch, 0, 0)
-	w.emit(EvFrequency, 1e9)
+	w := NewWriter()
+	w.Emit(EvBatch, 0, 0)
+	w.Emit(EvFrequency, 1e9)
 	for ts := uint64(1); ts < 1e16; ts *= 2 {
-		w.emit(EvGoCreate, ts, ts, 0, 0)
+		w.Emit(EvGoCreate, ts, ts, 0, 0)
 	}
 	if _, err := Parse(w, ""); err != nil {
 		t.Fatalf("failed to parse: %v", err)
 	}
 }
-
-type writer struct {
-	bytes.Buffer
-}
-
-func newWriter() *writer {
-	w := new(writer)
-	w.Write([]byte("go 1.7 trace\x00\x00\x00\x00"))
-	return w
-}
-
-func (w *writer) emit(typ byte, args ...uint64) {
-	nargs := byte(len(args)) - 1
-	if nargs > 3 {
-		nargs = 3
-	}
-	buf := []byte{typ | nargs<<6}
-	if nargs == 3 {
-		buf = append(buf, 0)
-	}
-	for _, a := range args {
-		buf = appendVarint(buf, a)
-	}
-	if nargs == 3 {
-		buf[1] = byte(len(buf) - 2)
-	}
-	n, err := w.Write(buf)
-	if n != len(buf) || err != nil {
-		panic("failed to write")
-	}
-}
-
-func appendVarint(buf []byte, v uint64) []byte {
-	for ; v >= 0x80; v >>= 7 {
-		buf = append(buf, 0x80|byte(v))
-	}
-	buf = append(buf, byte(v))
-	return buf
-}
diff --git a/src/internal/trace/testdata/http_1_7_good b/src/internal/trace/testdata/http_1_7_good
new file mode 100644
index 0000000..b0e318e
Binary files /dev/null and b/src/internal/trace/testdata/http_1_7_good differ
diff --git a/src/internal/trace/testdata/stress_1_7_good b/src/internal/trace/testdata/stress_1_7_good
new file mode 100644
index 0000000..b4d927d
Binary files /dev/null and b/src/internal/trace/testdata/stress_1_7_good differ
diff --git a/src/internal/trace/testdata/stress_start_stop_1_7_good b/src/internal/trace/testdata/stress_start_stop_1_7_good
new file mode 100644
index 0000000..c23ed7d
Binary files /dev/null and b/src/internal/trace/testdata/stress_start_stop_1_7_good differ
diff --git a/src/internal/trace/writer.go b/src/internal/trace/writer.go
new file mode 100644
index 0000000..a481f50
--- /dev/null
+++ b/src/internal/trace/writer.go
@@ -0,0 +1,45 @@
+package trace
+
+import "bytes"
+
+// Writer is a test trace writer.
+type Writer struct {
+	bytes.Buffer
+}
+
+func NewWriter() *Writer {
+	w := new(Writer)
+	w.Write([]byte("go 1.7 trace\x00\x00\x00\x00"))
+	return w
+}
+
+// Emit writes an event record to the trace.
+// See Event types for valid types and required arguments.
+func (w *Writer) Emit(typ byte, args ...uint64) {
+	nargs := byte(len(args)) - 1
+	if nargs > 3 {
+		nargs = 3
+	}
+	buf := []byte{typ | nargs<<6}
+	if nargs == 3 {
+		buf = append(buf, 0)
+	}
+	for _, a := range args {
+		buf = appendVarint(buf, a)
+	}
+	if nargs == 3 {
+		buf[1] = byte(len(buf) - 2)
+	}
+	n, err := w.Write(buf)
+	if n != len(buf) || err != nil {
+		panic("failed to write")
+	}
+}
+
+func appendVarint(buf []byte, v uint64) []byte {
+	for ; v >= 0x80; v >>= 7 {
+		buf = append(buf, 0x80|byte(v))
+	}
+	buf = append(buf, byte(v))
+	return buf
+}
diff --git a/src/io/io.go b/src/io/io.go
index 19d0ae5..3cab728 100644
--- a/src/io/io.go
+++ b/src/io/io.go
@@ -402,11 +402,10 @@ func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
 				break
 			}
 		}
-		if er == EOF {
-			break
-		}
 		if er != nil {
-			err = er
+			if er != EOF {
+				err = er
+			}
 			break
 		}
 	}
diff --git a/src/io/ioutil/ioutil.go b/src/io/ioutil/ioutil.go
index 8ecbb2d..f0da616 100644
--- a/src/io/ioutil/ioutil.go
+++ b/src/io/ioutil/ioutil.go
@@ -88,13 +88,6 @@ func WriteFile(filename string, data []byte, perm os.FileMode) error {
 	return err
 }
 
-// byName implements sort.Interface.
-type byName []os.FileInfo
-
-func (f byName) Len() int           { return len(f) }
-func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
-func (f byName) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }
-
 // ReadDir reads the directory named by dirname and returns
 // a list of directory entries sorted by filename.
 func ReadDir(dirname string) ([]os.FileInfo, error) {
@@ -107,7 +100,7 @@ func ReadDir(dirname string) ([]os.FileInfo, error) {
 	if err != nil {
 		return nil, err
 	}
-	sort.Sort(byName(list))
+	sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
 	return list, nil
 }
 
diff --git a/src/io/ioutil/tempfile.go b/src/io/ioutil/tempfile.go
index 42718cc..e5e315c 100644
--- a/src/io/ioutil/tempfile.go
+++ b/src/io/ioutil/tempfile.go
@@ -90,6 +90,11 @@ func TempDir(dir, prefix string) (name string, err error) {
 			}
 			continue
 		}
+		if os.IsNotExist(err) {
+			if _, err := os.Stat(dir); os.IsNotExist(err) {
+				return "", err
+			}
+		}
 		if err == nil {
 			name = try
 		}
diff --git a/src/io/ioutil/tempfile_test.go b/src/io/ioutil/tempfile_test.go
index d2a132a..6a70aed 100644
--- a/src/io/ioutil/tempfile_test.go
+++ b/src/io/ioutil/tempfile_test.go
@@ -51,3 +51,19 @@ func TestTempDir(t *testing.T) {
 		}
 	}
 }
+
+// test that we return a nice error message if the dir argument to TempDir doesn't
+// exist (or that it's empty and os.TempDir doesn't exist)
+func TestTempDir_BadDir(t *testing.T) {
+	dir, err := TempDir("", "TestTempDir_BadDir")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+
+	badDir := filepath.Join(dir, "not-exist")
+	_, err = TempDir(badDir, "foo")
+	if pe, ok := err.(*os.PathError); !ok || !os.IsNotExist(err) || pe.Path != badDir {
+		t.Errorf("TempDir error = %#v; want PathError for path %q satisifying os.IsNotExist", err, badDir)
+	}
+}
diff --git a/src/io/multi.go b/src/io/multi.go
index 3a9d036..46e45a6 100644
--- a/src/io/multi.go
+++ b/src/io/multi.go
@@ -19,6 +19,7 @@ func (mr *multiReader) Read(p []byte) (n int, err error) {
 		}
 		n, err = mr.readers[0].Read(p)
 		if err == EOF {
+			mr.readers[0] = nil // permit earlier GC
 			mr.readers = mr.readers[1:]
 		}
 		if n > 0 || err != EOF {
diff --git a/src/io/multi_test.go b/src/io/multi_test.go
index 5c6bb84..16e351a 100644
--- a/src/io/multi_test.go
+++ b/src/io/multi_test.go
@@ -14,6 +14,7 @@ import (
 	"runtime"
 	"strings"
 	"testing"
+	"time"
 )
 
 func TestMultiReader(t *testing.T) {
@@ -211,7 +212,7 @@ func (b byteAndEOFReader) Read(p []byte) (n int, err error) {
 	return 1, EOF
 }
 
-// In Go 1.7, this yielded bytes forever.
+// This used to yield bytes forever; issue 16795.
 func TestMultiReaderSingleByteWithEOF(t *testing.T) {
 	got, err := ioutil.ReadAll(LimitReader(MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10))
 	if err != nil {
@@ -234,3 +235,32 @@ func TestMultiReaderFinalEOF(t *testing.T) {
 		t.Errorf("got %v, %v; want 1, EOF", n, err)
 	}
 }
+
+func TestMultiReaderFreesExhaustedReaders(t *testing.T) {
+	var mr Reader
+	closed := make(chan struct{})
+	{
+		buf1 := bytes.NewReader([]byte("foo"))
+		buf2 := bytes.NewReader([]byte("bar"))
+		mr = MultiReader(buf1, buf2)
+		runtime.SetFinalizer(buf1, func(*bytes.Reader) {
+			close(closed)
+		})
+	}
+
+	buf := make([]byte, 4)
+	if n, err := ReadFull(mr, buf); err != nil || string(buf) != "foob" {
+		t.Fatalf(`ReadFull = %d (%q), %v; want 3, "foo", nil`, n, buf[:n], err)
+	}
+
+	runtime.GC()
+	select {
+	case <-closed:
+	case <-time.After(5 * time.Second):
+		t.Fatal("timeout waiting for collection of buf1")
+	}
+
+	if n, err := ReadFull(mr, buf[:2]); err != nil || string(buf[:2]) != "ar" {
+		t.Fatalf(`ReadFull = %d (%q), %v; want 2, "ar", nil`, n, buf[:n], err)
+	}
+}
diff --git a/src/io/pipe.go b/src/io/pipe.go
index 7e98cd2..6145872 100644
--- a/src/io/pipe.go
+++ b/src/io/pipe.go
@@ -148,7 +148,7 @@ type PipeWriter struct {
 }
 
 // Write implements the standard Write interface:
-// it writes data to the pipe, blocking until readers
+// it writes data to the pipe, blocking until one or more readers
 // have consumed all the data or the read end is closed.
 // If the read end is closed with an error, that err is
 // returned as err; otherwise err is ErrClosedPipe.
@@ -175,11 +175,17 @@ func (w *PipeWriter) CloseWithError(err error) error {
 // Pipe creates a synchronous in-memory pipe.
 // It can be used to connect code expecting an io.Reader
 // with code expecting an io.Writer.
-// Reads on one end are matched with writes on the other,
-// copying data directly between the two; there is no internal buffering.
-// It is safe to call Read and Write in parallel with each other or with
-// Close. Close will complete once pending I/O is done. Parallel calls to
-// Read, and parallel calls to Write, are also safe:
+//
+// Reads and Writes on the pipe are matched one to one
+// except when multiple Reads are needed to consume a single Write.
+// That is, each Write to the PipeWriter blocks until it has satisfied
+// one or more Reads from the PipeReader that fully consume
+// the written data.
+// The data is copied directly from the Write to the corresponding
+// Read (or Reads); there is no internal buffering.
+//
+// It is safe to call Read and Write in parallel with each other or with Close.
+// Parallel calls to Read and parallel calls to Write are also safe:
 // the individual calls will be gated sequentially.
 func Pipe() (*PipeReader, *PipeWriter) {
 	p := new(pipe)
diff --git a/src/log/log.go b/src/log/log.go
index 26cdb53..58b8788 100644
--- a/src/log/log.go
+++ b/src/log/log.go
@@ -8,6 +8,8 @@
 // Panic[f|ln], which are easier to use than creating a Logger manually.
 // That logger writes to standard error and prints the date and time
 // of each logged message.
+// Every log message is output on a separate line: if the message being
+// printed does not end in a newline, the logger will add one.
 // The Fatal functions call os.Exit(1) after writing the log message.
 // The Panic functions call panic after writing the log message.
 package log
diff --git a/src/log/syslog/doc.go b/src/log/syslog/doc.go
index dfcc2dd..5458523 100644
--- a/src/log/syslog/doc.go
+++ b/src/log/syslog/doc.go
@@ -10,7 +10,7 @@
 // the syslog client will attempt to reconnect to the server
 // and write again.
 //
-// The syslog package is frozen and not accepting new features.
+// The syslog package is frozen and is not accepting new features.
 // Some external packages provide more functionality. See:
 //
 //   https://godoc.org/?q=syslog
diff --git a/src/log/syslog/example_test.go b/src/log/syslog/example_test.go
new file mode 100644
index 0000000..3d5b76d
--- /dev/null
+++ b/src/log/syslog/example_test.go
@@ -0,0 +1,23 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !windows,!nacl,!plan9
+
+package syslog_test
+
+import (
+	"fmt"
+	"log"
+	"log/syslog"
+)
+
+func ExampleDial() {
+	sysLog, err := syslog.Dial("tcp", "localhost:1234",
+		syslog.LOG_WARNING|syslog.LOG_DAEMON, "demotag")
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Fprintf(sysLog, "This is a daemon warning with demotag.")
+	sysLog.Emerg("And this is a daemon emergency with demotag.")
+}
diff --git a/src/log/syslog/syslog.go b/src/log/syslog/syslog.go
index 9e888dd..df9ffb8 100644
--- a/src/log/syslog/syslog.go
+++ b/src/log/syslog/syslog.go
@@ -112,6 +112,8 @@ func New(priority Priority, tag string) (*Writer, error) {
 // writer sends a log message with the given facility, severity and
 // tag.
 // If network is empty, Dial will connect to the local syslog server.
+// Otherwise, see the documentation for net.Dial for valid values
+// of network and raddr.
 func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
 	if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG {
 		return nil, errors.New("log/syslog: invalid priority")
diff --git a/src/log/syslog/syslog_test.go b/src/log/syslog/syslog_test.go
index 52363f9..1263be6 100644
--- a/src/log/syslog/syslog_test.go
+++ b/src/log/syslog/syslog_test.go
@@ -134,6 +134,7 @@ func startServer(n, la string, done chan<- string) (addr string, sock io.Closer,
 }
 
 func TestWithSimulated(t *testing.T) {
+	t.Parallel()
 	msg := "Test 123"
 	var transport []string
 	for _, n := range []string{"unix", "unixgram", "udp", "tcp"} {
@@ -262,6 +263,7 @@ func check(t *testing.T, in, out string) {
 }
 
 func TestWrite(t *testing.T) {
+	t.Parallel()
 	tests := []struct {
 		pri Priority
 		pre string
@@ -367,7 +369,8 @@ func TestConcurrentReconnect(t *testing.T) {
 			defer wg.Done()
 			w, err := Dial(net, addr, LOG_USER|LOG_ERR, "tag")
 			if err != nil {
-				t.Fatalf("syslog.Dial() failed: %v", err)
+				t.Errorf("syslog.Dial() failed: %v", err)
+				return
 			}
 			defer w.Close()
 			for i := 0; i < M; i++ {
diff --git a/src/make.bash b/src/make.bash
index 1a1412a..84aaab5 100755
--- a/src/make.bash
+++ b/src/make.bash
@@ -47,6 +47,8 @@
 # FC: Command line to run to compile Fortran code for GOARCH.
 # This is used by cgo. Default is "gfortran".
 #
+# PKG_CONFIG: Path to pkg-config tool. Default is "pkg-config".
+#
 # GO_DISTFLAGS: extra flags to provide to "dist bootstrap".
 
 set -e
diff --git a/src/make.rc b/src/make.rc
index 243f83c..ba3554c 100755
--- a/src/make.rc
+++ b/src/make.rc
@@ -80,7 +80,7 @@ if(~ $sysname vx32)
 
 if(! ~ $GOHOSTARCH $GOARCH || ! ~ $GOHOSTOS $GOOS){
 	echo '##### Building packages and commands for host,' $GOHOSTOS/$GOHOSTARCH^.
-	GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH GOBIN= \
+	GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH GOBIN=() \
 		$GOTOOLDIR/go_bootstrap install -gcflags $"GO_GCFLAGS -ldflags $"GO_LDFLAGS -v $pflag std cmd
 	echo
 }
diff --git a/src/math/all_test.go b/src/math/all_test.go
index d9ea1fd..3d8cd72 100644
--- a/src/math/all_test.go
+++ b/src/math/all_test.go
@@ -1165,21 +1165,88 @@ var frexpSC = []fi{
 	{NaN(), 0},
 }
 
-var vfgammaSC = []float64{
-	Inf(-1),
-	-3,
-	Copysign(0, -1),
-	0,
-	Inf(1),
-	NaN(),
-}
-var gammaSC = []float64{
-	NaN(),
-	NaN(),
-	Inf(-1),
-	Inf(1),
-	Inf(1),
-	NaN(),
+var vfgamma = [][2]float64{
+	{Inf(1), Inf(1)},
+	{Inf(-1), NaN()},
+	{0, Inf(1)},
+	{Copysign(0, -1), Inf(-1)},
+	{NaN(), NaN()},
+	{-1, NaN()},
+	{-2, NaN()},
+	{-3, NaN()},
+	{-1e16, NaN()},
+	{-1e300, NaN()},
+	{1.7e308, Inf(1)},
+
+	// Test inputs inspired by Python test suite.
+	// Outputs computed at high precision by PARI/GP.
+	// If recomputing table entries, be careful to use
+	// high-precision (%.1000g) formatting of the float64 inputs.
+	// For example, -2.0000000000000004 is the float64 with exact value
+	// -2.00000000000000044408920985626161695, and
+	// gamma(-2.0000000000000004) = -1249999999999999.5386078562728167651513, while
+	// gamma(-2.00000000000000044408920985626161695) = -1125899906826907.2044875028130093136826.
+	// Thus the table lists -1.1258999068426235e+15 as the answer.
+	{0.5, 1.772453850905516},
+	{1.5, 0.886226925452758},
+	{2.5, 1.329340388179137},
+	{3.5, 3.3233509704478426},
+	{-0.5, -3.544907701811032},
+	{-1.5, 2.363271801207355},
+	{-2.5, -0.9453087204829419},
+	{-3.5, 0.2700882058522691},
+	{0.1, 9.51350769866873},
+	{0.01, 99.4325851191506},
+	{1e-08, 9.999999942278434e+07},
+	{1e-16, 1e+16},
+	{0.001, 999.4237724845955},
+	{1e-16, 1e+16},
+	{1e-308, 1e+308},
+	{5.6e-309, 1.7857142857142864e+308},
+	{5.5e-309, Inf(1)},
+	{1e-309, Inf(1)},
+	{1e-323, Inf(1)},
+	{5e-324, Inf(1)},
+	{-0.1, -10.686287021193193},
+	{-0.01, -100.58719796441078},
+	{-1e-08, -1.0000000057721567e+08},
+	{-1e-16, -1e+16},
+	{-0.001, -1000.5782056293586},
+	{-1e-16, -1e+16},
+	{-1e-308, -1e+308},
+	{-5.6e-309, -1.7857142857142864e+308},
+	{-5.5e-309, Inf(-1)},
+	{-1e-309, Inf(-1)},
+	{-1e-323, Inf(-1)},
+	{-5e-324, Inf(-1)},
+	{-0.9999999999999999, -9.007199254740992e+15},
+	{-1.0000000000000002, 4.5035996273704955e+15},
+	{-1.9999999999999998, 2.2517998136852485e+15},
+	{-2.0000000000000004, -1.1258999068426235e+15},
+	{-100.00000000000001, -7.540083334883109e-145},
+	{-99.99999999999999, 7.540083334884096e-145},
+	{17, 2.0922789888e+13},
+	{171, 7.257415615307999e+306},
+	{171.6, 1.5858969096672565e+308},
+	{171.624, 1.7942117599248104e+308},
+	{171.625, Inf(1)},
+	{172, Inf(1)},
+	{2000, Inf(1)},
+	{-100.5, -3.3536908198076787e-159},
+	{-160.5, -5.255546447007829e-286},
+	{-170.5, -3.3127395215386074e-308},
+	{-171.5, 1.9316265431712e-310},
+	{-176.5, -1.196e-321},
+	{-177.5, 5e-324},
+	{-178.5, Copysign(0, -1)},
+	{-179.5, 0},
+	{-201.0001, 0},
+	{-202.9999, Copysign(0, -1)},
+	{-1000.5, Copysign(0, -1)},
+	{-1.0000000003e+09, Copysign(0, -1)},
+	{-4.5035996273704955e+15, 0},
+	{-63.349078729022985, 4.177797167776188e-88},
+	{-127.45117632943295, 1.183111089623681e-214},
 }
 
 var vfhypotSC = [][2]float64{
@@ -1735,6 +1802,12 @@ var logbBC = []float64{
 }
 
 func tolerance(a, b, e float64) bool {
+	// Multiplying by e here can underflow denormal values to zero.
+	// Check a==b so that at least if a and b are small and identical
+	// we say they match.
+	if a == b {
+		return true
+	}
 	d := a - b
 	if d < 0 {
 		d = -d
@@ -1974,7 +2047,7 @@ func TestExp(t *testing.T) {
 
 func testExp(t *testing.T, Exp func(float64) float64, name string) {
 	for i := 0; i < len(vf); i++ {
-		if f := Exp(vf[i]); !close(exp[i], f) {
+		if f := Exp(vf[i]); !veryclose(exp[i], f) {
 			t.Errorf("%s(%g) = %g, want %g", name, vf[i], f, exp[i])
 		}
 	}
@@ -2147,9 +2220,18 @@ func TestGamma(t *testing.T) {
 			t.Errorf("Gamma(%g) = %g, want %g", vf[i], f, gamma[i])
 		}
 	}
-	for i := 0; i < len(vfgammaSC); i++ {
-		if f := Gamma(vfgammaSC[i]); !alike(gammaSC[i], f) {
-			t.Errorf("Gamma(%g) = %g, want %g", vfgammaSC[i], f, gammaSC[i])
+	for _, g := range vfgamma {
+		f := Gamma(g[0])
+		var ok bool
+		if IsNaN(g[1]) || IsInf(g[1], 0) || g[1] == 0 || f == 0 {
+			ok = alike(g[1], f)
+		} else if g[0] > -50 && g[0] <= 171 {
+			ok = veryclose(g[1], f)
+		} else {
+			ok = close(g[1], f)
+		}
+		if !ok {
+			t.Errorf("Gamma(%g) = %g, want %g", g[0], f, g[1])
 		}
 	}
 }
@@ -3000,27 +3082,36 @@ func BenchmarkSinh(b *testing.B) {
 
 var Global float64
 
-func BenchmarkSqrt(b *testing.B) {
+func BenchmarkSqrtIndirect(b *testing.B) {
 	x, y := 0.0, 10.0
+	f := Sqrt
 	for i := 0; i < b.N; i++ {
-		x += Sqrt(y)
+		x += f(y)
 	}
 	Global = x
 }
 
-func BenchmarkSqrtIndirect(b *testing.B) {
-	x, y := 0.0, 10.0
+func BenchmarkSqrtLatency(b *testing.B) {
+	x := 10.0
+	for i := 0; i < b.N; i++ {
+		x = Sqrt(x)
+	}
+	Global = x
+}
+
+func BenchmarkSqrtIndirectLatency(b *testing.B) {
+	x := 10.0
 	f := Sqrt
 	for i := 0; i < b.N; i++ {
-		x += f(y)
+		x = f(x)
 	}
 	Global = x
 }
 
-func BenchmarkSqrtGo(b *testing.B) {
-	x, y := 0.0, 10.0
+func BenchmarkSqrtGoLatency(b *testing.B) {
+	x := 10.0
 	for i := 0; i < b.N; i++ {
-		x += SqrtGo(y)
+		x = SqrtGo(x)
 	}
 	Global = x
 }
diff --git a/src/math/arith_s390x.go b/src/math/arith_s390x.go
new file mode 100644
index 0000000..892935a
--- /dev/null
+++ b/src/math/arith_s390x.go
@@ -0,0 +1,29 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package math
+
+func log10TrampolineSetup(x float64) float64
+func log10Asm(x float64) float64
+
+func cosTrampolineSetup(x float64) float64
+func cosAsm(x float64) float64
+
+func coshTrampolineSetup(x float64) float64
+func coshAsm(x float64) float64
+
+func sinTrampolineSetup(x float64) float64
+func sinAsm(x float64) float64
+
+func sinhTrampolineSetup(x float64) float64
+func sinhAsm(x float64) float64
+
+func tanhTrampolineSetup(x float64) float64
+func tanhAsm(x float64) float64
+
+// hasVectorFacility reports whether the machine has the z/Architecture
+// vector facility installed and enabled.
+func hasVectorFacility() bool
+
+var hasVX = hasVectorFacility()
diff --git a/src/math/arith_s390x_test.go b/src/math/arith_s390x_test.go
new file mode 100644
index 0000000..b4f3070
--- /dev/null
+++ b/src/math/arith_s390x_test.go
@@ -0,0 +1,144 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Tests whether the non vector routines are working, even when the tests are run on a
+// vector-capable machine.
+package math_test
+
+import (
+	. "math"
+	"testing"
+)
+
+func TestCosNovec(t *testing.T) {
+	if !HasVX {
+		t.Skipf("no vector support")
+	}
+	for i := 0; i < len(vf); i++ {
+		if f := CosNoVec(vf[i]); !veryclose(cos[i], f) {
+			t.Errorf("Cos(%g) = %g, want %g", vf[i], f, cos[i])
+		}
+	}
+	for i := 0; i < len(vfcosSC); i++ {
+		if f := CosNoVec(vfcosSC[i]); !alike(cosSC[i], f) {
+			t.Errorf("Cos(%g) = %g, want %g", vfcosSC[i], f, cosSC[i])
+		}
+	}
+}
+
+func TestCoshNovec(t *testing.T) {
+	if !HasVX {
+		t.Skipf("no vector support")
+	}
+	for i := 0; i < len(vf); i++ {
+		if f := CoshNoVec(vf[i]); !close(cosh[i], f) {
+			t.Errorf("Cosh(%g) = %g, want %g", vf[i], f, cosh[i])
+		}
+	}
+	for i := 0; i < len(vfcoshSC); i++ {
+		if f := CoshNoVec(vfcoshSC[i]); !alike(coshSC[i], f) {
+			t.Errorf("Cosh(%g) = %g, want %g", vfcoshSC[i], f, coshSC[i])
+		}
+	}
+}
+func TestSinNovec(t *testing.T) {
+	if !HasVX {
+		t.Skipf("no vector support")
+	}
+	for i := 0; i < len(vf); i++ {
+		if f := SinNoVec(vf[i]); !veryclose(sin[i], f) {
+			t.Errorf("Sin(%g) = %g, want %g", vf[i], f, sin[i])
+		}
+	}
+	for i := 0; i < len(vfsinSC); i++ {
+		if f := SinNoVec(vfsinSC[i]); !alike(sinSC[i], f) {
+			t.Errorf("Sin(%g) = %g, want %g", vfsinSC[i], f, sinSC[i])
+		}
+	}
+}
+
+func TestSinhNovec(t *testing.T) {
+	if !HasVX {
+		t.Skipf("no vector support")
+	}
+	for i := 0; i < len(vf); i++ {
+		if f := SinhNoVec(vf[i]); !close(sinh[i], f) {
+			t.Errorf("Sinh(%g) = %g, want %g", vf[i], f, sinh[i])
+		}
+	}
+	for i := 0; i < len(vfsinhSC); i++ {
+		if f := SinhNoVec(vfsinhSC[i]); !alike(sinhSC[i], f) {
+			t.Errorf("Sinh(%g) = %g, want %g", vfsinhSC[i], f, sinhSC[i])
+		}
+	}
+}
+
+// Check that math functions of high angle values
+// return accurate results. [Since (vf[i] + large) - large != vf[i],
+// testing for Trig(vf[i] + large) == Trig(vf[i]), where large is
+// a multiple of 2*Pi, is misleading.]
+func TestLargeCosNovec(t *testing.T) {
+	if !HasVX {
+		t.Skipf("no vector support")
+	}
+	large := float64(100000 * Pi)
+	for i := 0; i < len(vf); i++ {
+		f1 := cosLarge[i]
+		f2 := CosNoVec(vf[i] + large)
+		if !close(f1, f2) {
+			t.Errorf("Cos(%g) = %g, want %g", vf[i]+large, f2, f1)
+		}
+	}
+}
+
+func TestLargeSinNovec(t *testing.T) {
+	if !HasVX {
+		t.Skipf("no vector support")
+	}
+	large := float64(100000 * Pi)
+	for i := 0; i < len(vf); i++ {
+		f1 := sinLarge[i]
+		f2 := SinNoVec(vf[i] + large)
+		if !close(f1, f2) {
+			t.Errorf("Sin(%g) = %g, want %g", vf[i]+large, f2, f1)
+		}
+	}
+}
+
+func TestTanhNovec(t *testing.T) {
+	if !HasVX {
+		t.Skipf("no vector support")
+	}
+	for i := 0; i < len(vf); i++ {
+		if f := TanhNoVec(vf[i]); !veryclose(tanh[i], f) {
+			t.Errorf("Tanh(%g) = %g, want %g", vf[i], f, tanh[i])
+		}
+	}
+	for i := 0; i < len(vftanhSC); i++ {
+		if f := TanhNoVec(vftanhSC[i]); !alike(tanhSC[i], f) {
+			t.Errorf("Tanh(%g) = %g, want %g", vftanhSC[i], f, tanhSC[i])
+		}
+	}
+
+}
+
+func TestLog10Novec(t *testing.T) {
+	if !HasVX {
+		t.Skipf("no vector support")
+	}
+	for i := 0; i < len(vf); i++ {
+		a := Abs(vf[i])
+		if f := Log10NoVec(a); !veryclose(log10[i], f) {
+			t.Errorf("Log10(%g) = %g, want %g", a, f, log10[i])
+		}
+	}
+	if f := Log10NoVec(E); f != Log10E {
+		t.Errorf("Log10(%g) = %g, want %g", E, f, Log10E)
+	}
+	for i := 0; i < len(vflogSC); i++ {
+		if f := Log10NoVec(vflogSC[i]); !alike(logSC[i], f) {
+			t.Errorf("Log10(%g) = %g, want %g", vflogSC[i], f, logSC[i])
+		}
+	}
+}
diff --git a/src/math/big/arith_amd64.s b/src/math/big/arith_amd64.s
index b69a2c6..a7eba67 100644
--- a/src/math/big/arith_amd64.s
+++ b/src/math/big/arith_amd64.s
@@ -326,6 +326,41 @@ TEXT ·mulAddVWW(SB),NOSPLIT,$0
 	MOVQ r+56(FP), CX	// c = r
 	MOVQ z_len+8(FP), R11
 	MOVQ $0, BX		// i = 0
+	
+	CMPQ R11, $4
+	JL E5
+	
+U5:	// i+4 <= n
+	// regular loop body unrolled 4x
+	MOVQ (0*8)(R8)(BX*8), AX
+	MULQ R9
+	ADDQ CX, AX
+	ADCQ $0, DX
+	MOVQ AX, (0*8)(R10)(BX*8)
+	MOVQ DX, CX
+	MOVQ (1*8)(R8)(BX*8), AX
+	MULQ R9
+	ADDQ CX, AX
+	ADCQ $0, DX
+	MOVQ AX, (1*8)(R10)(BX*8)
+	MOVQ DX, CX
+	MOVQ (2*8)(R8)(BX*8), AX
+	MULQ R9
+	ADDQ CX, AX
+	ADCQ $0, DX
+	MOVQ AX, (2*8)(R10)(BX*8)
+	MOVQ DX, CX
+	MOVQ (3*8)(R8)(BX*8), AX
+	MULQ R9
+	ADDQ CX, AX
+	ADCQ $0, DX
+	MOVQ AX, (3*8)(R10)(BX*8)
+	MOVQ DX, CX
+	ADDQ $4, BX		// i += 4
+	
+	LEAQ 4(BX), DX
+	CMPQ DX, R11
+	JLE U5
 	JMP E5
 
 L5:	MOVQ (R8)(BX*8), AX
diff --git a/src/math/big/arith_decl_s390x.go b/src/math/big/arith_decl_s390x.go
new file mode 100644
index 0000000..0f11481
--- /dev/null
+++ b/src/math/big/arith_decl_s390x.go
@@ -0,0 +1,23 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !math_big_pure_go
+
+package big
+
+func addVV_check(z, x, y []Word) (c Word)
+func addVV_vec(z, x, y []Word) (c Word)
+func addVV_novec(z, x, y []Word) (c Word)
+func subVV_check(z, x, y []Word) (c Word)
+func subVV_vec(z, x, y []Word) (c Word)
+func subVV_novec(z, x, y []Word) (c Word)
+func addVW_check(z, x []Word, y Word) (c Word)
+func addVW_vec(z, x []Word, y Word) (c Word)
+func addVW_novec(z, x []Word, y Word) (c Word)
+func subVW_check(z, x []Word, y Word) (c Word)
+func subVW_vec(z, x []Word, y Word) (c Word)
+func subVW_novec(z, x []Word, y Word) (c Word)
+func hasVectorFacility() bool
+
+var hasVX = hasVectorFacility()
diff --git a/src/math/big/arith_mipsx.s b/src/math/big/arith_mipsx.s
new file mode 100644
index 0000000..ac23114
--- /dev/null
+++ b/src/math/big/arith_mipsx.s
@@ -0,0 +1,46 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !math_big_pure_go,mips !math_big_pure_go,mipsle
+
+#include "textflag.h"
+
+// This file provides fast assembly versions for the elementary
+// arithmetic operations on vectors implemented in arith.go.
+
+TEXT ·mulWW(SB),NOSPLIT,$0
+	JMP	·mulWW_g(SB)
+
+TEXT ·divWW(SB),NOSPLIT,$0
+	JMP	·divWW_g(SB)
+
+TEXT ·addVV(SB),NOSPLIT,$0
+	JMP	·addVV_g(SB)
+
+TEXT ·subVV(SB),NOSPLIT,$0
+	JMP	·subVV_g(SB)
+
+TEXT ·addVW(SB),NOSPLIT,$0
+	JMP	·addVW_g(SB)
+
+TEXT ·subVW(SB),NOSPLIT,$0
+	JMP	·subVW_g(SB)
+
+TEXT ·shlVU(SB),NOSPLIT,$0
+	JMP	·shlVU_g(SB)
+
+TEXT ·shrVU(SB),NOSPLIT,$0
+	JMP	·shrVU_g(SB)
+
+TEXT ·mulAddVWW(SB),NOSPLIT,$0
+	JMP	·mulAddVWW_g(SB)
+
+TEXT ·addMulVVW(SB),NOSPLIT,$0
+	JMP	·addMulVVW_g(SB)
+
+TEXT ·divWVW(SB),NOSPLIT,$0
+	JMP	·divWVW_g(SB)
+
+TEXT ·bitLen(SB),NOSPLIT,$0
+	JMP	·bitLen_g(SB)
diff --git a/src/math/big/arith_ppc64.s b/src/math/big/arith_ppc64.s
new file mode 100644
index 0000000..47fe8f1
--- /dev/null
+++ b/src/math/big/arith_ppc64.s
@@ -0,0 +1,14 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !math_big_pure_go,ppc64
+
+#include "textflag.h"
+
+// This file provides fast assembly versions for the elementary
+// arithmetic operations on vectors implemented in arith.go.
+
+TEXT ·divWW(SB), NOSPLIT, $0
+	BR ·divWW_g(SB)
+
diff --git a/src/math/big/arith_ppc64le.s b/src/math/big/arith_ppc64le.s
new file mode 100644
index 0000000..b78cdfe
--- /dev/null
+++ b/src/math/big/arith_ppc64le.s
@@ -0,0 +1,50 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !math_big_pure_go,ppc64le
+
+#include "textflag.h"
+
+// This file provides fast assembly versions for the elementary
+// arithmetic operations on vectors implemented in arith.go.
+
+// func divWW(x1, x0, y Word) (q, r Word)
+TEXT ·divWW(SB), NOSPLIT, $0
+	MOVD x1+0(FP), R4
+	MOVD x0+8(FP), R5
+	MOVD y+16(FP), R6
+
+	CMPU R4, R6
+	BGE  divbigger
+
+	// from the programmer's note in ch. 3 of the ISA manual, p.74
+	DIVDEU R6, R4, R3
+	DIVDU  R6, R5, R7
+	MULLD  R6, R3, R8
+	MULLD  R6, R7, R20
+	SUB    R20, R5, R10
+	ADD    R7, R3, R3
+	SUB    R8, R10, R4
+	CMPU   R4, R10
+	BLT    adjust
+	CMPU   R4, R6
+	BLT    end
+
+adjust:
+	MOVD $1, R21
+	ADD  R21, R3, R3
+	SUB  R6, R4, R4
+
+end:
+	MOVD R3, q+24(FP)
+	MOVD R4, r+32(FP)
+
+	RET
+
+divbigger:
+	MOVD $-1, R7
+	MOVD R7, q+24(FP)
+	MOVD R7, r+32(FP)
+	RET
+
diff --git a/src/math/big/arith_ppc64x.s b/src/math/big/arith_ppc64x.s
index d4d4171..89d1cbf 100644
--- a/src/math/big/arith_ppc64x.s
+++ b/src/math/big/arith_ppc64x.s
@@ -9,38 +9,178 @@
 // This file provides fast assembly versions for the elementary
 // arithmetic operations on vectors implemented in arith.go.
 
-TEXT ·mulWW(SB),NOSPLIT,$0
-	BR ·mulWW_g(SB)
+// func mulWW(x, y Word) (z1, z0 Word)
+TEXT ·mulWW(SB), NOSPLIT, $0
+	MOVD   x+0(FP), R4
+	MOVD   y+8(FP), R5
+	MULHDU R4, R5, R6
+	MULLD  R4, R5, R7
+	MOVD   R6, z1+16(FP)
+	MOVD   R7, z0+24(FP)
+	RET
 
-TEXT ·divWW(SB),NOSPLIT,$0
-	BR ·divWW_g(SB)
-
-TEXT ·addVV(SB),NOSPLIT,$0
+TEXT ·addVV(SB), NOSPLIT, $0
 	BR ·addVV_g(SB)
 
-TEXT ·subVV(SB),NOSPLIT,$0
-	BR ·subVV_g(SB)
+// func subVV(z, x, y []Word) (c Word)
+// z[i] = x[i] - y[i] for all i, carrying
+TEXT ·subVV(SB), NOSPLIT, $0
+	MOVD z_len+8(FP), R7
+	MOVD x+24(FP), R8
+	MOVD y+48(FP), R9
+	MOVD z+0(FP), R10
+
+	MOVD $0, R4  // c = 0
+	MOVD $0, R5  // i = 0
+	MOVD $1, R29 // work around lack of ADDI
+	MOVD $8, R28 // work around lack of scaled addressing
+
+	SUBC R0, R0  // clear CA
+	JMP  sublend
+
+// amd64 saves and restores CF, but I believe they only have to do that because all of
+// their math operations clobber it - we should just be able to recover it at the end.
+subloop:
+	MULLD R5, R28, R6
+	MOVD  (R8)(R6), R11 // x[i]
+	MOVD  (R9)(R6), R12 // y[i]
+
+	SUBE R12, R11, R15
+	MOVD R15, (R10)(R6)
 
-TEXT ·addVW(SB),NOSPLIT,$0
+	ADD R29, R5 // i++
+
+sublend:
+	CMP R5, R7
+	BLT subloop
+
+	ADDZE R4
+	XOR   R29, R4
+	MOVD  R4, c+72(FP)
+	RET
+
+TEXT ·addVW(SB), NOSPLIT, $0
 	BR ·addVW_g(SB)
 
-TEXT ·subVW(SB),NOSPLIT,$0
+TEXT ·subVW(SB), NOSPLIT, $0
 	BR ·subVW_g(SB)
 
-TEXT ·shlVU(SB),NOSPLIT,$0
+TEXT ·shlVU(SB), NOSPLIT, $0
 	BR ·shlVU_g(SB)
 
-TEXT ·shrVU(SB),NOSPLIT,$0
+TEXT ·shrVU(SB), NOSPLIT, $0
 	BR ·shrVU_g(SB)
 
-TEXT ·mulAddVWW(SB),NOSPLIT,$0
-	BR ·mulAddVWW_g(SB)
+// func mulAddVWW(z, x []Word, y, r Word) (c Word)
+TEXT ·mulAddVWW(SB), NOSPLIT, $0
+	MOVD z+0(FP), R10
+	MOVD x+24(FP), R8
+	MOVD y+48(FP), R9
+	MOVD r+56(FP), R4     // c = r
+	MOVD z_len+8(FP), R11
+	MOVD $0, R3           // i = 0
+	MOVD $8, R18
+	MOVD $1, R19
+
+	JMP e5
+
+l5:
+	MULLD  R18, R3, R5
+	MOVD   (R8)(R5), R20
+	MULLD  R9, R20, R6
+	MULHDU R9, R20, R7
+	ADDC   R4, R6
+	ADDZE  R7
+	MOVD   R6, (R10)(R5)
+	MOVD   R7, R4
+	ADD    R19, R3
+
+e5:
+	CMP R3, R11
+	BLT l5
+
+	MOVD R4, c+64(FP)
+	RET
+
+// func addMulVVW(z, x []Word, y Word) (c Word)
+TEXT ·addMulVVW(SB), NOSPLIT, $0
+	MOVD z+0(FP), R10
+	MOVD x+24(FP), R8
+	MOVD y+48(FP), R9
+	MOVD z_len+8(FP), R22
+
+	MOVD $0, R5   // i = 0
+	MOVD $0, R4   // c = 0
+	MOVD $8, R28
+	MOVD $-2, R23
+	AND  R22, R23 // mask the last bit of z.len
+	MOVD $2, R24
+	CMP  R23, R24
+	BGE  unrolled
+	JMP  end
+
+unrolled:
+	MOVD  $8, R19         // no (RA)(RB*8) on power
+	MULLD R5, R19
+	MOVD  (R10)(R19), R11 // R11 = z[i]
+	MOVD  (R8)(R19), R16  // R16 = x[i]
+	ADD   R28, R19, R25
+	MOVD  (R10)(R25), R17
+	MOVD  (R8)(R25), R18
+
+	MULLD  R9, R16, R12
+	MULHDU R9, R16, R14
+	MULLD  R9, R18, R6
+	MULHDU R9, R18, R7
+	ADDC   R4, R12
+	ADDZE  R14
+	ADDC   R11, R12        // z[i] = (x[i]*y) + z[i] + carry
+	ADDZE  R14             // carry = high order bits + add carry
+	MOVD   R12, (R10)(R19)
+	ADDC   R14, R6
+	ADDZE  R7
+	ADDC   R17, R6
+	ADDZE  R7
+	MOVD   R6, (R10)(R25)
+	MOVD   R7, R4
+
+	ADD R24, R5
+	CMP R5, R23
+	BLT unrolled
+	JMP end
+
+loop:
+	MOVD   $8, R19
+	MULLD  R5, R19
+	MOVD   (R10)(R19), R11
+	MOVD   (R8)(R19), R16
+	MULLD  R9, R16, R12
+	MULHDU R9, R16, R14
+	ADDC   R4, R12
+	ADDZE  R14
+	ADDC   R11, R12
+	ADDZE  R14
+	MOVD   R12, (R10)(R19)
+	MOVD   R14, R4
+
+	MOVD $1, R15
+	ADD  R15, R5
+
+end:
+	CMP R5, R22
+	BLT loop
 
-TEXT ·addMulVVW(SB),NOSPLIT,$0
-	BR ·addMulVVW_g(SB)
+	MOVD R4, c+56(FP)
+	RET
 
-TEXT ·divWVW(SB),NOSPLIT,$0
+TEXT ·divWVW(SB), NOSPLIT, $0
 	BR ·divWVW_g(SB)
 
-TEXT ·bitLen(SB),NOSPLIT,$0
-	BR ·bitLen_g(SB)
+// func bitLen(x Word) int
+TEXT ·bitLen(SB), NOSPLIT, $0
+	MOVD   x+0(FP), R4
+	CNTLZD R4, R4
+	MOVD   $64, R5
+	SUB    R4, R5
+	MOVD   R5, n+8(FP)
+	RET
diff --git a/src/math/big/arith_s390x.s b/src/math/big/arith_s390x.s
index a691970..bddfd9e 100644
--- a/src/math/big/arith_s390x.s
+++ b/src/math/big/arith_s390x.s
@@ -9,93 +9,464 @@
 // This file provides fast assembly versions for the elementary
 // arithmetic operations on vectors implemented in arith.go.
 
+TEXT ·hasVectorFacility(SB),NOSPLIT,$24-1
+        MOVD    $x-24(SP), R1
+        XC      $24, 0(R1), 0(R1) // clear the storage
+        MOVD    $2, R0            // R0 is the number of double words stored -1
+        WORD    $0xB2B01000       // STFLE 0(R1)
+        XOR     R0, R0            // reset the value of R0
+        MOVBZ   z-8(SP), R1
+        AND     $0x40, R1
+        BEQ     novector
+vectorinstalled:
+        // check if the vector instruction has been enabled
+        VLEIB   $0, $0xF, V16
+        VLGVB   $0, V16, R1
+        CMPBNE  R1, $0xF, novector
+        MOVB    $1, ret+0(FP) // have vx
+        RET
+novector:
+        MOVB    $0, ret+0(FP) // no vx
+        RET
+
 TEXT ·mulWW(SB),NOSPLIT,$0
-	MOVD x+0(FP), R3
-	MOVD y+8(FP), R4
-	MULHDU R3, R4
-	MOVD R10, z1+16(FP)
-	MOVD R11, z0+24(FP)
+	MOVD	x+0(FP), R3
+	MOVD	y+8(FP), R4
+	MULHDU	R3, R4
+	MOVD	R10, z1+16(FP)
+	MOVD	R11, z0+24(FP)
 	RET
 
 // func divWW(x1, x0, y Word) (q, r Word)
 TEXT ·divWW(SB),NOSPLIT,$0
-	MOVD  x1+0(FP), R10
-	MOVD  x0+8(FP), R11
-	MOVD  y+16(FP), R5
-	WORD  $0xb98700a5 // dlgr r10,r5
-	MOVD  R11, q+24(FP)
-	MOVD  R10, r+32(FP)
+	MOVD	x1+0(FP), R10
+	MOVD	x0+8(FP), R11
+	MOVD	y+16(FP), R5
+	WORD	$0xb98700a5 // dlgr r10,r5
+	MOVD	R11, q+24(FP)
+	MOVD	R10, r+32(FP)
 	RET
 
 // DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11
 // func addVV(z, x, y []Word) (c Word)
+
+
 TEXT ·addVV(SB),NOSPLIT,$0
-	MOVD z_len+8(FP), R3
-	MOVD x+24(FP), R8
-	MOVD y+48(FP), R9
-	MOVD z+0(FP), R2
+	MOVD	addvectorfacility+0x00(SB),R1
+	BR	(R1)
+	
+TEXT ·addVV_check(SB),NOSPLIT, $0
+	MOVB	·hasVX(SB), R1
+	CMPBEQ	R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVD	$addvectorfacility+0x00(SB), R1
+	MOVD	$·addVV_novec(SB), R2
+	MOVD	R2, 0(R1)
+	//MOVD	$·addVV_novec(SB), 0(R1)
+	BR	·addVV_novec(SB)
+vectorimpl:
+	MOVD	$addvectorfacility+0x00(SB), R1
+	MOVD	$·addVV_vec(SB), R2
+	MOVD	R2, 0(R1)
+	//MOVD	$·addVV_vec(SB), 0(R1)
+	BR	·addVV_vec(SB)
+
+GLOBL addvectorfacility+0x00(SB), NOPTR, $8
+DATA addvectorfacility+0x00(SB)/8, $·addVV_check(SB)
+
+TEXT ·addVV_vec(SB),NOSPLIT,$0
+	MOVD	z_len+8(FP), R3
+	MOVD	x+24(FP), R8
+	MOVD	y+48(FP), R9
+	MOVD	z+0(FP), R2
+
+	MOVD	$0, R4		// c = 0
+	MOVD	$0, R0		// make sure it's zero
+	MOVD	$0, R10		// i = 0
 
-	MOVD $0, R4		// c = 0
-	MOVD $0, R0		// make sure it's zero
-	MOVD $0, R10		// i = 0
 
 	// s/JL/JMP/ below to disable the unrolled loop
-	SUB  $4, R3		// n -= 4
-	BLT v1			// if n < 0 goto v1
+	SUB	$4, R3
+	BLT	v1
+	SUB     $12, R3                 // n -= 16
+        BLT     A1                      // if n < 0 goto A1
+       
+	MOVD	R8, R5
+	MOVD	R9, R6
+	MOVD	R2, R7
+	// n >= 0
+	// regular loop body unrolled 16x
+	VZERO	V0			// c = 0
+UU1:	VLM	0(R5), V1, V4		// 64-bytes into V1..V8
+	ADD	$64, R5
+	VPDI	$0x4,V1,V1,V1		// flip the doublewords to big-endian order
+	VPDI	$0x4,V2,V2,V2		// flip the doublewords to big-endian order
+
+
+	VLM	0(R6), V9, V12  	// 64-bytes into V9..V16
+	ADD	$64, R6
+	VPDI	$0x4,V9,V9,V9		// flip the doublewords to big-endian order
+	VPDI	$0x4,V10,V10,V10	// flip the doublewords to big-endian order
+
+	VACCCQ	V1, V9, V0, V25
+	VACQ	V1, V9, V0, V17
+	VACCCQ	V2, V10, V25, V26
+	VACQ	V2, V10, V25, V18
+
+
+	VLM	0(R5), V5, V6		// 32-bytes into V1..V8
+	VLM	0(R6), V13, V14  	// 32-bytes into V9..V16
+	ADD	$32, R5
+	ADD	$32, R6
+
+	VPDI	$0x4,V3,V3,V3		// flip the doublewords to big-endian order
+	VPDI	$0x4,V4,V4,V4		// flip the doublewords to big-endian order
+	VPDI	$0x4,V11,V11,V11	// flip the doublewords to big-endian order
+	VPDI	$0x4,V12,V12,V12	// flip the doublewords to big-endian order
+
+	VACCCQ	V3, V11, V26, V27
+	VACQ	V3, V11, V26, V19
+	VACCCQ	V4, V12, V27, V28
+	VACQ	V4, V12, V27, V20
+
+	VLM	0(R5), V7, V8		// 32-bytes into V1..V8
+	VLM	0(R6), V15, V16  	// 32-bytes into V9..V16
+	ADD	$32, R5
+	ADD	$32, R6
+
+	VPDI	$0x4,V5,V5,V5		// flip the doublewords to big-endian order
+	VPDI	$0x4,V6,V6,V6		// flip the doublewords to big-endian order
+	VPDI	$0x4,V13,V13,V13	// flip the doublewords to big-endian order
+	VPDI	$0x4,V14,V14,V14	// flip the doublewords to big-endian order
+
+	VACCCQ	V5, V13, V28, V29
+	VACQ	V5, V13, V28, V21
+	VACCCQ	V6, V14, V29, V30
+	VACQ	V6, V14, V29, V22
+
+	VPDI	$0x4,V7,V7,V7		// flip the doublewords to big-endian order
+	VPDI	$0x4,V8,V8,V8		// flip the doublewords to big-endian order
+	VPDI	$0x4,V15,V15,V15	// flip the doublewords to big-endian order
+	VPDI	$0x4,V16,V16,V16	// flip the doublewords to big-endian order
+
+	VACCCQ	V7, V15, V30, V31
+	VACQ	V7, V15, V30, V23
+	VACCCQ	V8, V16, V31, V0	//V0 has carry-over
+	VACQ	V8, V16, V31, V24
+
+	VPDI	$0x4,V17,V17,V17	// flip the doublewords to big-endian order
+	VPDI	$0x4,V18,V18,V18	// flip the doublewords to big-endian order
+	VPDI	$0x4,V19,V19,V19	// flip the doublewords to big-endian order
+	VPDI	$0x4,V20,V20,V20	// flip the doublewords to big-endian order
+	VPDI	$0x4,V21,V21,V21	// flip the doublewords to big-endian order
+	VPDI	$0x4,V22,V22,V22	// flip the doublewords to big-endian order
+	VPDI	$0x4,V23,V23,V23	// flip the doublewords to big-endian order
+	VPDI	$0x4,V24,V24,V24	// flip the doublewords to big-endian order
+	VSTM	V17, V24, 0(R7)  	// 128-bytes into z
+	ADD	$128, R7
+	ADD	$128, R10	// i += 16
+	SUB	$16,  R3	// n -= 16
+	BGE	UU1		// if n >= 0 goto U1
+	VLGVG	$1, V0, R4	// put cf into R4
+	NEG	R4, R4		// save cf
+
+A1:	ADD	$12, R3		// n += 16
+
+
+	// s/JL/JMP/ below to disable the unrolled loop
+	BLT	v1		// if n < 0 goto v1
 
 U1:	// n >= 0
 	// regular loop body unrolled 4x
-	MOVD 0(R8)(R10*1), R5
-	MOVD 8(R8)(R10*1), R6
-	MOVD 16(R8)(R10*1), R7
-	MOVD 24(R8)(R10*1), R1
-	ADDC R4, R4		// restore CF
-	MOVD 0(R9)(R10*1), R11
-	ADDE R11, R5
-	MOVD 8(R9)(R10*1), R11
-	ADDE R11, R6
-	MOVD 16(R9)(R10*1), R11
-	ADDE R11, R7
-	MOVD 24(R9)(R10*1), R11
-	ADDE R11, R1
-	MOVD R0, R4
-	ADDE R4, R4		// save CF
-	NEG  R4, R4
-	MOVD R5, 0(R2)(R10*1)
-	MOVD R6, 8(R2)(R10*1)
-	MOVD R7, 16(R2)(R10*1)
-	MOVD R1, 24(R2)(R10*1)
+	MOVD	0(R8)(R10*1), R5
+	MOVD	8(R8)(R10*1), R6
+	MOVD	16(R8)(R10*1), R7
+	MOVD	24(R8)(R10*1), R1
+	ADDC	R4, R4		// restore CF
+	MOVD	0(R9)(R10*1), R11
+	ADDE	R11, R5
+	MOVD	8(R9)(R10*1), R11
+	ADDE	R11, R6
+	MOVD	16(R9)(R10*1), R11
+	ADDE	R11, R7
+	MOVD	24(R9)(R10*1), R11
+	ADDE	R11, R1
+	MOVD	R0, R4
+	ADDE	R4, R4		// save CF
+	NEG	R4, R4
+	MOVD	R5, 0(R2)(R10*1)
+	MOVD	R6, 8(R2)(R10*1)
+	MOVD	R7, 16(R2)(R10*1)
+	MOVD	R1, 24(R2)(R10*1)
+
+
+	ADD	$32, R10	// i += 4
+	SUB	$4,  R3		// n -= 4
+	BGE	U1		// if n >= 0 goto U1
+
+v1:	ADD	$4, R3		// n += 4
+	BLE	E1		// if n <= 0 goto E1
 
+L1:	// n > 0
+	ADDC	R4, R4		// restore CF
+	MOVD	0(R8)(R10*1), R5
+	MOVD	0(R9)(R10*1), R11
+	ADDE	R11, R5
+	MOVD	R5, 0(R2)(R10*1)
+	MOVD	R0, R4
+	ADDE	R4, R4		// save CF
+	NEG 	R4, R4
+
+	ADD	$8, R10		// i++
+	SUB	$1, R3		// n--
+	BGT	L1		// if n > 0 goto L1
+
+E1:	NEG	R4, R4
+	MOVD	R4, c+72(FP)	// return c
+	RET
 
-	ADD  $32, R10		// i += 4
-	SUB  $4,  R3		// n -= 4
-	BGE  U1			// if n >= 0 goto U1
+TEXT ·addVV_novec(SB),NOSPLIT,$0
+novec:
+	MOVD	z_len+8(FP), R3
+	MOVD	x+24(FP), R8
+	MOVD	y+48(FP), R9
+	MOVD	z+0(FP), R2
 
-v1:	ADD  $4, R3		// n += 4
-	BLE E1			// if n <= 0 goto E1
+	MOVD	$0, R4		// c = 0
+	MOVD	$0, R0		// make sure it's zero
+	MOVD	$0, R10		// i = 0
 
-L1:	// n > 0
-	ADDC R4, R4		// restore CF
-	MOVD 0(R8)(R10*1), R5
-	MOVD 0(R9)(R10*1), R11
-	ADDE R11, R5
-	MOVD R5, 0(R2)(R10*1)
-	MOVD R0, R4
-	ADDE R4, R4		// save CF
-	NEG  R4, R4
+	// s/JL/JMP/ below to disable the unrolled loop
+	SUB	$4, R3		// n -= 4
+	BLT	v1n		// if n < 0 goto v1n
+U1n:	// n >= 0
+	// regular loop body unrolled 4x
+	MOVD	0(R8)(R10*1), R5
+	MOVD	8(R8)(R10*1), R6
+	MOVD	16(R8)(R10*1), R7
+	MOVD	24(R8)(R10*1), R1
+	ADDC	R4, R4		// restore CF
+	MOVD	0(R9)(R10*1), R11
+	ADDE	R11, R5
+	MOVD	8(R9)(R10*1), R11
+	ADDE	R11, R6
+	MOVD	16(R9)(R10*1), R11
+	ADDE	R11, R7
+	MOVD	24(R9)(R10*1), R11
+	ADDE	R11, R1
+	MOVD	R0, R4
+	ADDE	R4, R4		// save CF
+	NEG	R4, R4
+	MOVD	R5, 0(R2)(R10*1)
+	MOVD	R6, 8(R2)(R10*1)
+	MOVD	R7, 16(R2)(R10*1)
+	MOVD	R1, 24(R2)(R10*1)
+
+
+	ADD	$32, R10	// i += 4
+	SUB	$4,  R3		// n -= 4
+	BGE	U1n		// if n >= 0 goto U1n
+
+v1n:	ADD	$4, R3		// n += 4
+	BLE	E1n		// if n <= 0 goto E1n
+
+L1n:	// n > 0
+	ADDC	R4, R4		// restore CF
+	MOVD	0(R8)(R10*1), R5
+	MOVD	0(R9)(R10*1), R11
+	ADDE	R11, R5
+	MOVD	R5, 0(R2)(R10*1)
+	MOVD	R0, R4
+	ADDE	R4, R4		// save CF
+	NEG 	R4, R4
+
+	ADD	$8, R10		// i++
+	SUB	$1, R3		// n--
+	BGT L1n			// if n > 0 goto L1n
+
+E1n:	NEG	R4, R4
+	MOVD	R4, c+72(FP)	// return c
+	RET
 
-	ADD  $8, R10		// i++
-	SUB  $1, R3		// n--
-	BGT L1			// if n > 0 goto L1
 
-E1:	NEG  R4, R4
-	MOVD R4, c+72(FP)	// return c
+TEXT ·subVV(SB),NOSPLIT,$0
+	MOVD	subvectorfacility+0x00(SB),R1
+	BR	(R1)
+	
+TEXT ·subVV_check(SB),NOSPLIT,$0
+	MOVB	·hasVX(SB), R1
+	CMPBEQ	R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVD	$subvectorfacility+0x00(SB), R1
+	MOVD	$·subVV_novec(SB), R2
+	MOVD	R2, 0(R1)
+	//MOVD	$·subVV_novec(SB), 0(R1)
+	BR	·subVV_novec(SB)
+vectorimpl:
+	MOVD	$subvectorfacility+0x00(SB), R1
+	MOVD    $·subVV_vec(SB), R2
+        MOVD    R2, 0(R1)
+	//MOVD	$·subVV_vec(SB), 0(R1)
+	BR	·subVV_vec(SB)
+
+GLOBL subvectorfacility+0x00(SB), NOPTR, $8
+DATA subvectorfacility+0x00(SB)/8, $·subVV_check(SB)
+
+// DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11
+// func subVV(z, x, y []Word) (c Word)
+// (same as addVV except for SUBC/SUBE instead of ADDC/ADDE and label names)
+TEXT ·subVV_vec(SB),NOSPLIT,$0
+	MOVD	z_len+8(FP), R3
+	MOVD	x+24(FP), R8
+	MOVD	y+48(FP), R9
+	MOVD	z+0(FP), R2
+	MOVD	$0, R4		// c = 0
+	MOVD	$0, R0		// make sure it's zero
+	MOVD	$0, R10		// i = 0
+	
+	// s/JL/JMP/ below to disable the unrolled loop
+	SUB	$4, R3		// n -= 4
+	BLT	v1		// if n < 0 goto v1
+	SUB     $12, R3         // n -= 16
+        BLT     A1              // if n < 0 goto A1
+
+	MOVD	R8, R5
+	MOVD	R9, R6
+	MOVD	R2, R7
+
+	// n >= 0
+	// regular loop body unrolled 16x
+	VZERO	V0		// cf = 0
+	MOVD	$1, R4		// for 390 subtraction cf starts as 1 (no borrow)
+	VLVGG	$1, R4, V0	//put carry into V0
+
+UU1:	VLM	0(R5), V1, V4		// 64-bytes into V1..V8
+	ADD	$64, R5
+	VPDI	$0x4,V1,V1,V1		// flip the doublewords to big-endian order
+	VPDI	$0x4,V2,V2,V2		// flip the doublewords to big-endian order
+
+
+	VLM	0(R6), V9, V12  	// 64-bytes into V9..V16
+	ADD	$64, R6
+	VPDI	$0x4,V9,V9,V9		// flip the doublewords to big-endian order
+	VPDI	$0x4,V10,V10,V10	// flip the doublewords to big-endian order
+
+	VSBCBIQ	V1, V9, V0, V25
+	VSBIQ	V1, V9, V0, V17
+	VSBCBIQ	V2, V10, V25, V26
+	VSBIQ	V2, V10, V25, V18
+
+
+	VLM	0(R5), V5, V6		// 32-bytes into V1..V8
+	VLM	0(R6), V13, V14  	// 32-bytes into V9..V16
+	ADD	$32, R5
+	ADD	$32, R6
+
+	VPDI	$0x4,V3,V3,V3		// flip the doublewords to big-endian order
+	VPDI	$0x4,V4,V4,V4		// flip the doublewords to big-endian order
+	VPDI	$0x4,V11,V11,V11	// flip the doublewords to big-endian order
+	VPDI	$0x4,V12,V12,V12	// flip the doublewords to big-endian order
+
+	VSBCBIQ	V3, V11, V26, V27
+	VSBIQ	V3, V11, V26, V19
+	VSBCBIQ	V4, V12, V27, V28
+	VSBIQ	V4, V12, V27, V20
+
+	VLM	0(R5), V7, V8		// 32-bytes into V1..V8
+	VLM	0(R6), V15, V16  	// 32-bytes into V9..V16
+	ADD	$32, R5
+	ADD	$32, R6
+
+	VPDI	$0x4,V5,V5,V5		// flip the doublewords to big-endian order
+	VPDI	$0x4,V6,V6,V6		// flip the doublewords to big-endian order
+	VPDI	$0x4,V13,V13,V13	// flip the doublewords to big-endian order
+	VPDI	$0x4,V14,V14,V14	// flip the doublewords to big-endian order
+
+	VSBCBIQ	V5, V13, V28, V29
+	VSBIQ	V5, V13, V28, V21
+	VSBCBIQ	V6, V14, V29, V30
+	VSBIQ	V6, V14, V29, V22
+
+	VPDI	$0x4,V7,V7,V7		// flip the doublewords to big-endian order
+	VPDI	$0x4,V8,V8,V8		// flip the doublewords to big-endian order
+	VPDI	$0x4,V15,V15,V15	// flip the doublewords to big-endian order
+	VPDI	$0x4,V16,V16,V16	// flip the doublewords to big-endian order
+
+	VSBCBIQ	V7, V15, V30, V31
+	VSBIQ	V7, V15, V30, V23
+	VSBCBIQ	V8, V16, V31, V0	//V0 has carry-over
+	VSBIQ	V8, V16, V31, V24
+
+	VPDI	$0x4,V17,V17,V17	// flip the doublewords to big-endian order
+	VPDI	$0x4,V18,V18,V18	// flip the doublewords to big-endian order
+	VPDI	$0x4,V19,V19,V19	// flip the doublewords to big-endian order
+	VPDI	$0x4,V20,V20,V20	// flip the doublewords to big-endian order
+	VPDI	$0x4,V21,V21,V21	// flip the doublewords to big-endian order
+	VPDI	$0x4,V22,V22,V22	// flip the doublewords to big-endian order
+	VPDI	$0x4,V23,V23,V23	// flip the doublewords to big-endian order
+	VPDI	$0x4,V24,V24,V24	// flip the doublewords to big-endian order
+	VSTM	V17, V24, 0(R7)   // 128-bytes into z
+	ADD	$128, R7
+	ADD	$128, R10	// i += 16
+	SUB	$16,  R3	// n -= 16
+	BGE	UU1		// if n >= 0 goto U1
+	VLGVG	$1, V0, R4	// put cf into R4
+	SUB	$1, R4		// save cf
+
+A1:	ADD	$12, R3		// n += 16
+	BLT	v1		// if n < 0 goto v1
+	
+U1:	// n >= 0
+	// regular loop body unrolled 4x
+	MOVD	0(R8)(R10*1), R5
+	MOVD	8(R8)(R10*1), R6
+	MOVD	16(R8)(R10*1), R7
+	MOVD	24(R8)(R10*1), R1
+	MOVD	R0, R11
+	SUBC	R4, R11		// restore CF
+	MOVD	0(R9)(R10*1), R11
+	SUBE	R11, R5
+	MOVD	8(R9)(R10*1), R11
+	SUBE	R11, R6
+	MOVD	16(R9)(R10*1), R11
+	SUBE	R11, R7
+	MOVD	24(R9)(R10*1), R11
+	SUBE	R11, R1
+	MOVD	R0, R4
+	SUBE	R4, R4		// save CF
+	MOVD	R5, 0(R2)(R10*1)
+	MOVD	R6, 8(R2)(R10*1)
+	MOVD	R7, 16(R2)(R10*1)
+	MOVD	R1, 24(R2)(R10*1)
+
+	ADD	$32, R10	// i += 4
+	SUB	$4,  R3		// n -= 4
+	BGE	U1		// if n >= 0 goto U1n
+
+v1:	ADD	$4, R3		// n += 4
+	BLE	E1		// if n <= 0 goto E1
+
+L1:	// n > 0
+	MOVD	R0, R11
+	SUBC	R4, R11		// restore CF
+	MOVD	0(R8)(R10*1), R5
+	MOVD	0(R9)(R10*1), R11
+	SUBE	R11, R5
+	MOVD	R5, 0(R2)(R10*1)
+	MOVD	R0, R4
+	SUBE	R4, R4		// save CF
+
+	ADD	$8, R10		// i++
+	SUB	$1, R3		// n--
+	BGT	L1		// if n > 0 goto L1n
+
+E1:	NEG	R4, R4
+	MOVD	R4, c+72(FP)	// return c
 	RET
 
+
 // DI = R3, CX = R4, SI = r10, r8 = r8, r9=r9, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0) + use R11
 // func subVV(z, x, y []Word) (c Word)
 // (same as addVV except for SUBC/SUBE instead of ADDC/ADDE and label names)
-TEXT ·subVV(SB),NOSPLIT,$0
+TEXT ·subVV_novec(SB),NOSPLIT,$0
 	MOVD z_len+8(FP), R3
 	MOVD x+24(FP), R8
 	MOVD y+48(FP), R9
@@ -158,9 +529,163 @@ E1:	NEG  R4, R4
 	MOVD R4, c+72(FP)	// return c
 	RET
 
-
-// func addVW(z, x []Word, y Word) (c Word)
 TEXT ·addVW(SB),NOSPLIT,$0
+	MOVD	addwvectorfacility+0x00(SB),R1
+	BR	(R1)
+	
+TEXT ·addVW_check(SB),NOSPLIT,$0
+	MOVB	·hasVX(SB), R1
+	CMPBEQ	R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVD	$addwvectorfacility+0x00(SB), R1
+	MOVD    $·addVW_novec(SB), R2
+        MOVD    R2, 0(R1)
+	//MOVD	$·addVW_novec(SB), 0(R1)
+	BR	·addVW_novec(SB)
+vectorimpl:
+	MOVD	$addwvectorfacility+0x00(SB), R1
+	MOVD    $·addVW_vec(SB), R2
+        MOVD    R2, 0(R1)
+	//MOVD	$·addVW_vec(SB), 0(R1)
+	BR	·addVW_vec(SB)
+
+GLOBL addwvectorfacility+0x00(SB), NOPTR, $8
+DATA addwvectorfacility+0x00(SB)/8, $·addVW_check(SB)
+
+
+// func addVW_vec(z, x []Word, y Word) (c Word)
+TEXT ·addVW_vec(SB),NOSPLIT,$0
+	MOVD	z_len+8(FP), R3
+	MOVD	x+24(FP), R8
+	MOVD	y+48(FP), R4	// c = y
+	MOVD	z+0(FP), R2
+
+	MOVD	$0, R0		// make sure it's zero
+	MOVD	$0, R10		// i = 0
+	MOVD	R8, R5
+	MOVD	R2, R7
+
+	// s/JL/JMP/ below to disable the unrolled loop
+	SUB	$4, R3			// n -= 4
+	BLT	v10			// if n < 0 goto v10
+	SUB	$12, R3
+	BLT	A10
+
+	// n >= 0
+	// regular loop body unrolled 16x
+
+	VZERO	V0			// prepare V0 to be final carry register
+	VZERO	V9			// to ensure upper half is zero
+	VLVGG	$1, R4, V9
+UU1:	VLM	0(R5), V1, V4		// 64-bytes into V1..V4
+	ADD	$64, R5
+	VPDI	$0x4,V1,V1,V1		// flip the doublewords to big-endian order
+	VPDI	$0x4,V2,V2,V2		// flip the doublewords to big-endian order
+
+
+	VACCCQ	V1, V9, V0, V25
+	VACQ	V1, V9, V0, V17
+	VZERO	V9
+	VACCCQ	V2, V9, V25, V26
+	VACQ	V2, V9, V25, V18
+
+
+	VLM	0(R5), V5, V6		// 32-bytes into V5..V6
+	ADD	$32, R5
+
+	VPDI	$0x4,V3,V3,V3		// flip the doublewords to big-endian order
+	VPDI	$0x4,V4,V4,V4		// flip the doublewords to big-endian order
+
+	VACCCQ	V3, V9, V26, V27
+	VACQ	V3, V9, V26, V19
+	VACCCQ	V4, V9, V27, V28
+	VACQ	V4, V9, V27, V20
+
+	VLM	0(R5), V7, V8		// 32-bytes into V7..V8
+	ADD	$32, R5
+
+	VPDI	$0x4,V5,V5,V5		// flip the doublewords to big-endian order
+	VPDI	$0x4,V6,V6,V6		// flip the doublewords to big-endian order
+
+	VACCCQ	V5, V9, V28, V29
+	VACQ	V5, V9, V28, V21
+	VACCCQ	V6, V9, V29, V30
+	VACQ	V6, V9, V29, V22
+
+	VPDI	$0x4,V7,V7,V7		// flip the doublewords to big-endian order
+	VPDI	$0x4,V8,V8,V8		// flip the doublewords to big-endian order
+
+	VACCCQ	V7, V9, V30, V31
+	VACQ	V7, V9, V30, V23
+	VACCCQ	V8, V9, V31, V0	//V0 has carry-over
+	VACQ	V8, V9, V31, V24
+
+	VPDI	$0x4,V17,V17,V17	// flip the doublewords to big-endian order
+	VPDI	$0x4,V18,V18,V18	// flip the doublewords to big-endian order
+	VPDI	$0x4,V19,V19,V19	// flip the doublewords to big-endian order
+	VPDI	$0x4,V20,V20,V20	// flip the doublewords to big-endian order
+	VPDI	$0x4,V21,V21,V21	// flip the doublewords to big-endian order
+	VPDI	$0x4,V22,V22,V22	// flip the doublewords to big-endian order
+	VPDI	$0x4,V23,V23,V23	// flip the doublewords to big-endian order
+	VPDI	$0x4,V24,V24,V24	// flip the doublewords to big-endian order
+	VSTM	V17, V24, 0(R7)   	// 128-bytes into z
+	ADD	$128, R7
+	ADD	$128, R10		// i += 16
+	SUB	$16,  R3		// n -= 16
+	BGE	UU1		// if n >= 0 goto U1
+	VLGVG	$1, V0, R4	// put cf into R4 in case we branch to v10
+
+A10:	ADD	$12, R3		// n += 16
+
+
+	// s/JL/JMP/ below to disable the unrolled loop
+
+	BLT	v10		// if n < 0 goto v10
+
+
+U4:	// n >= 0
+	// regular loop body unrolled 4x
+	MOVD 0(R8)(R10*1), R5
+	MOVD 8(R8)(R10*1), R6
+	MOVD 16(R8)(R10*1), R7
+	MOVD 24(R8)(R10*1), R1
+	ADDC R4, R5
+	ADDE R0, R6
+	ADDE R0, R7
+	ADDE R0, R1
+	ADDE R0, R0
+	MOVD R0, R4		// save CF
+	SUB  R0, R0
+	MOVD R5, 0(R2)(R10*1)
+	MOVD R6, 8(R2)(R10*1)
+	MOVD R7, 16(R2)(R10*1)
+	MOVD R1, 24(R2)(R10*1)
+
+	ADD $32, R10		// i += 4 -> i +=32
+	SUB $4, R3		// n -= 4
+	BGE U4			// if n >= 0 goto U4
+
+v10:	ADD $4, R3		// n += 4
+	BLE E10			// if n <= 0 goto E4
+
+
+L4:	// n > 0
+	MOVD	0(R8)(R10*1), R5
+	ADDC	R4, R5
+	ADDE	R0, R0
+	MOVD	R0, R4		// save CF
+	SUB 	R0, R0
+	MOVD	R5, 0(R2)(R10*1)
+
+	ADD	$8, R10		// i++
+	SUB	$1, R3		// n--
+	BGT	L4		// if n > 0 goto L4
+
+E10:	MOVD	R4, c+56(FP)	// return c
+
+	RET
+
+
+TEXT ·addVW_novec(SB),NOSPLIT,$0
 //DI = R3, CX = R4, SI = r10, r8 = r8, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0)
 	MOVD z_len+8(FP), R3
 	MOVD x+24(FP), R8
@@ -214,10 +739,166 @@ E4:	MOVD R4, c+56(FP)	// return c
 
 	RET
 
+TEXT ·subVW(SB),NOSPLIT,$0
+	MOVD	subwvectorfacility+0x00(SB),R1
+	BR	(R1)
+	
+TEXT ·subVW_check(SB),NOSPLIT,$0
+	MOVB	·hasVX(SB), R1
+	CMPBEQ	R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVD	$subwvectorfacility+0x00(SB), R1
+	MOVD    $·subVW_novec(SB), R2
+        MOVD    R2, 0(R1)
+	//MOVD	$·subVW_novec(SB), 0(R1)
+	BR	·subVW_novec(SB)
+vectorimpl:
+	MOVD	$subwvectorfacility+0x00(SB), R1
+	MOVD    $·subVW_vec(SB), R2
+        MOVD    R2, 0(R1)
+	//MOVD	$·subVW_vec(SB), 0(R1)
+	BR	·subVW_vec(SB)
+
+GLOBL subwvectorfacility+0x00(SB), NOPTR, $8
+DATA subwvectorfacility+0x00(SB)/8, $·subVW_check(SB)
+
+// func subVW(z, x []Word, y Word) (c Word)
+TEXT ·subVW_vec(SB),NOSPLIT,$0
+	MOVD	z_len+8(FP), R3
+	MOVD	x+24(FP), R8
+	MOVD	y+48(FP), R4	// c = y
+	MOVD	z+0(FP), R2
+
+	MOVD	$0, R0		// make sure it's zero
+	MOVD	$0, R10		// i = 0
+	MOVD	R8, R5
+	MOVD	R2, R7
+
+	// s/JL/JMP/ below to disable the unrolled loop
+	SUB	$4, R3			// n -= 4
+	BLT	v11			// if n < 0 goto v11
+	SUB	$12, R3
+	BLT	A11
+
+	VZERO	V0
+	MOVD	$1, R6			// prepare V0 to be final carry register
+	VLVGG	$1, R6, V0		// borrow is initially "no borrow"
+	VZERO	V9			// to ensure upper half is zero
+	VLVGG	$1, R4, V9
+
+	// n >= 0
+	// regular loop body unrolled 16x
+
+
+UU1:	VLM	0(R5), V1, V4		// 64-bytes into V1..V4
+	ADD	$64, R5
+	VPDI	$0x4,V1,V1,V1		// flip the doublewords to big-endian order
+	VPDI	$0x4,V2,V2,V2		// flip the doublewords to big-endian order
+
+
+	VSBCBIQ	V1, V9, V0, V25
+	VSBIQ	V1, V9, V0, V17
+	VZERO	V9
+	VSBCBIQ	V2, V9, V25, V26
+	VSBIQ	V2, V9, V25, V18
+
+	VLM	0(R5), V5, V6		// 32-bytes into V5..V6
+	ADD	$32, R5
+
+	VPDI	$0x4,V3,V3,V3		// flip the doublewords to big-endian order
+	VPDI	$0x4,V4,V4,V4		// flip the doublewords to big-endian order
+
+
+	VSBCBIQ	V3, V9, V26, V27
+	VSBIQ	V3, V9, V26, V19
+	VSBCBIQ	V4, V9, V27, V28
+	VSBIQ	V4, V9, V27, V20
+
+	VLM	0(R5), V7, V8		// 32-bytes into V7..V8
+	ADD	$32, R5
+
+	VPDI	$0x4,V5,V5,V5		// flip the doublewords to big-endian order
+	VPDI	$0x4,V6,V6,V6		// flip the doublewords to big-endian order
+
+	VSBCBIQ	V5, V9, V28, V29
+	VSBIQ	V5, V9, V28, V21
+	VSBCBIQ	V6, V9, V29, V30
+	VSBIQ	V6, V9, V29, V22
+
+	VPDI	$0x4,V7,V7,V7		// flip the doublewords to big-endian order
+	VPDI	$0x4,V8,V8,V8		// flip the doublewords to big-endian order
+
+	VSBCBIQ	V7, V9, V30, V31
+	VSBIQ	V7, V9, V30, V23
+	VSBCBIQ	V8, V9, V31, V0	// V0 has carry-over
+	VSBIQ	V8, V9, V31, V24
+
+	VPDI	$0x4,V17,V17,V17	// flip the doublewords to big-endian order
+	VPDI	$0x4,V18,V18,V18	// flip the doublewords to big-endian order
+	VPDI	$0x4,V19,V19,V19	// flip the doublewords to big-endian order
+	VPDI	$0x4,V20,V20,V20	// flip the doublewords to big-endian order
+	VPDI	$0x4,V21,V21,V21	// flip the doublewords to big-endian order
+	VPDI	$0x4,V22,V22,V22	// flip the doublewords to big-endian order
+	VPDI	$0x4,V23,V23,V23	// flip the doublewords to big-endian order
+	VPDI	$0x4,V24,V24,V24	// flip the doublewords to big-endian order
+	VSTM	V17, V24, 0(R7)   	// 128-bytes into z
+	ADD	$128, R7
+	ADD	$128, R10		// i += 16
+	SUB	$16,  R3		// n -= 16
+	BGE	UU1			// if n >= 0 goto U1
+	VLGVG	$1, V0, R4		// put cf into R4 in case we branch to v10
+	SUB	$1, R4			// save cf
+	NEG	R4, R4
+A11:	ADD	$12, R3			// n += 16
+
+	BLT	v11			// if n < 0 goto v11
+
+	// n >= 0
+	// regular loop body unrolled 4x
+
+U4:	// n >= 0
+	// regular loop body unrolled 4x
+	MOVD 0(R8)(R10*1), R5
+	MOVD 8(R8)(R10*1), R6
+	MOVD 16(R8)(R10*1), R7
+	MOVD 24(R8)(R10*1), R1
+	SUBC R4, R5 //SLGR  -> SUBC
+	SUBE R0, R6 //SLBGR -> SUBE
+	SUBE R0, R7
+	SUBE R0, R1
+	SUBE R4, R4		// save CF
+	NEG  R4, R4
+	MOVD R5, 0(R2)(R10*1)
+	MOVD R6, 8(R2)(R10*1)
+	MOVD R7, 16(R2)(R10*1)
+	MOVD R1, 24(R2)(R10*1)
+
+	ADD $32, R10		// i += 4 -> i +=32
+	SUB $4, R3		// n -= 4
+	BGE U4			// if n >= 0 goto U4
+
+v11:	ADD $4, R3		// n += 4
+	BLE E11			// if n <= 0 goto E4
+
+L4:	// n > 0
+
+	MOVD	0(R8)(R10*1), R5
+	SUBC	R4, R5
+	SUBE	R4, R4		// save CF
+	NEG	R4, R4
+	MOVD	R5, 0(R2)(R10*1)
+
+	ADD	$8, R10		// i++
+	SUB	$1, R3		// n--
+	BGT	L4		// if n > 0 goto L4
+
+E11:	MOVD	R4, c+56(FP)	// return c
+
+	RET
+
 //DI = R3, CX = R4, SI = r10, r8 = r8, r10 = r2 , r11 = r5, r12 = r6, r13 = r7, r14 = r1 (R0 set to 0)
 // func subVW(z, x []Word, y Word) (c Word)
 // (same as addVW except for SUBC/SUBE instead of ADDC/ADDE and label names)
-TEXT ·subVW(SB),NOSPLIT,$0
+TEXT ·subVW_novec(SB),NOSPLIT,$0
 	MOVD z_len+8(FP), R3
 	MOVD x+24(FP), R8
 	MOVD y+48(FP), R4	// c = y
@@ -270,296 +951,299 @@ E4:	MOVD R4, c+56(FP)	// return c
 
 // func shlVU(z, x []Word, s uint) (c Word)
 TEXT ·shlVU(SB),NOSPLIT,$0
-	MOVD z_len+8(FP), R5
-	SUB  $1, R5             // n--
-	BLT  X8b                // n < 0        (n <= 0)
+	MOVD	z_len+8(FP), R5
+	MOVD	$0, R0
+	SUB	$1, R5             // n--
+	BLT	X8b                // n < 0        (n <= 0)
 
 	// n > 0
-	MOVD s+48(FP), R4
-	CMPBEQ	R0, R4, Z80	       //handle 0 case beq
-	MOVD $64, R6
-	CMPBEQ  R6, R4, Z864	       //handle 64 case beq
-	MOVD z+0(FP), R2
-	MOVD x+24(FP), R8
-	SLD  $3, R5             // n = n*8
-	SUB  R4, R6, R7
-	MOVD (R8)(R5*1), R10    // w1 = x[i-1]
-	SRD  R7, R10, R3
-	MOVD R3, c+56(FP)
-
-	MOVD $0, R1             // i = 0
-	BR   E8
+	MOVD	s+48(FP), R4
+	CMPBEQ	R0, R4, Z80	   //handle 0 case beq
+	MOVD	$64, R6
+	CMPBEQ	R6, R4, Z864	   //handle 64 case beq
+	MOVD	z+0(FP), R2
+	MOVD	x+24(FP), R8
+	SLD	$3, R5             // n = n*8
+	SUB	R4, R6, R7
+	MOVD	(R8)(R5*1), R10    // w1 = x[i-1]
+	SRD	R7, R10, R3
+	MOVD	R3, c+56(FP)
+
+	MOVD	$0, R1             // i = 0
+	BR	E8
 
 	// i < n-1
-L8:	MOVD R10, R3             // w = w1
-	MOVD -8(R8)(R5*1), R10   // w1 = x[i+1]
+L8:	MOVD	R10, R3             // w = w1
+	MOVD	-8(R8)(R5*1), R10   // w1 = x[i+1]
 
-	SLD  R4,  R3             // w<<s | w1>>ŝ
-	SRD  R7, R10, R6
-	OR   R6, R3
-	MOVD R3, (R2)(R5*1)      // z[i] = w<<s | w1>>ŝ
-	SUB  $8, R5              // i--
+	SLD	R4,  R3             // w<<s | w1>>ŝ
+	SRD	R7, R10, R6
+	OR 	R6, R3
+	MOVD	R3, (R2)(R5*1)      // z[i] = w<<s | w1>>ŝ
+	SUB	$8, R5              // i--
 
-E8:	CMPBGT R5, R0, L8        // i < n-1
+E8:	CMPBGT	R5, R0, L8	    // i < n-1
 
 	// i >= n-1
-X8a:	SLD  R4, R10             // w1<<s
-	MOVD R10, (R2)           // z[0] = w1<<s
+X8a:	SLD	R4, R10             // w1<<s
+	MOVD	R10, (R2)           // z[0] = w1<<s
 	RET
 
-X8b:	MOVD R0, c+56(FP)
+X8b:	MOVD	R0, c+56(FP)
 	RET
 
-Z80:	MOVD z+0(FP), R2
-	MOVD x+24(FP), R8
-	SLD  $3, R5             // n = n*8
+Z80:	MOVD	z+0(FP), R2
+	MOVD	x+24(FP), R8
+	SLD	$3, R5             // n = n*8
 
-	MOVD (R8), R10
-	MOVD $0, R3
-	MOVD R3, c+56(FP)
+	MOVD	(R8), R10
+	MOVD	$0, R3
+	MOVD	R3, c+56(FP)
 
-	MOVD $0, R1             // i = 0
-	BR   E8Z
+	MOVD	$0, R1             // i = 0
+	BR	E8Z
 
 	// i < n-1
-L8Z:	MOVD R10, R3
-	MOVD 8(R8)(R1*1), R10
+L8Z:	MOVD	R10, R3
+	MOVD	8(R8)(R1*1), R10
 
-	MOVD R3, (R2)(R1*1)
-	ADD  $8, R1
+	MOVD	R3, (R2)(R1*1)
+	ADD 	$8, R1
 
-E8Z:	CMPBLT R1, R5, L8Z
+E8Z:	CMPBLT	R1, R5, L8Z
 
 	// i >= n-1
-	MOVD R10, (R2)(R5*1)
+	MOVD	R10, (R2)(R5*1)
 	RET
 
-Z864:	MOVD z+0(FP), R2
-	MOVD x+24(FP), R8
-	SLD  $3, R5             // n = n*8
-	MOVD (R8)(R5*1), R3     // w1 = x[n-1]
-	MOVD R3, c+56(FP)       // z[i] = x[n-1]
+Z864:	MOVD	z+0(FP), R2
+	MOVD	x+24(FP), R8
+	SLD	$3, R5             // n = n*8
+	MOVD	(R8)(R5*1), R3     // w1 = x[n-1]
+	MOVD	R3, c+56(FP)       // z[i] = x[n-1]
 
-	BR   E864
+	BR	E864
 
 	// i < n-1
-L864:	MOVD -8(R8)(R5*1), R3
+L864:	MOVD	-8(R8)(R5*1), R3
 
-	MOVD R3, (R2)(R5*1)     // z[i] = x[n-1]
-	SUB  $8, R5             // i--
+	MOVD	R3, (R2)(R5*1)     // z[i] = x[n-1]
+	SUB	$8, R5             // i--
 
-E864:	CMPBGT R5, R0, L864     // i < n-1
+E864:	CMPBGT	R5, R0, L864       // i < n-1
 
-	MOVD R0, (R2)           // z[n-1] = 0
+	MOVD	R0, (R2)           // z[n-1] = 0
 	RET
 
 
 // CX = R4, r8 = r8, r10 = r2 , r11 = r5, DX = r3, AX = r10 , BX = R1 , 64-count = r7 (R0 set to 0) temp = R6
 // func shrVU(z, x []Word, s uint) (c Word)
 TEXT ·shrVU(SB),NOSPLIT,$0
-	MOVD z_len+8(FP), R5
-	SUB  $1, R5             // n--
-	BLT  X9b                // n < 0        (n <= 0)
+	MOVD	z_len+8(FP), R5
+	MOVD	$0, R0
+	SUB	$1, R5             // n--
+	BLT	X9b                // n < 0        (n <= 0)
 
 	// n > 0
-	MOVD s+48(FP), R4
-	CMPBEQ	R0, R4, ZB0	       //handle 0 case beq
-	MOVD $64, R6
-	CMPBEQ  R6, R4, ZB64	       //handle 64 case beq
-	MOVD z+0(FP), R2
-	MOVD x+24(FP), R8
-	SLD  $3, R5             // n = n*8
-	SUB  R4, R6, R7
-	MOVD (R8), R10          // w1 = x[0]
-	SLD  R7, R10, R3
-	MOVD R3, c+56(FP)
-
-	MOVD $0, R1            // i = 0
-	BR   E9
+	MOVD	s+48(FP), R4
+	CMPBEQ	R0, R4, ZB0	//handle 0 case beq
+	MOVD	$64, R6
+	CMPBEQ 	R6, R4, ZB64	//handle 64 case beq
+	MOVD	z+0(FP), R2
+	MOVD	x+24(FP), R8
+	SLD	$3, R5		// n = n*8
+	SUB	R4, R6, R7
+	MOVD	(R8), R10	// w1 = x[0]
+	SLD	R7, R10, R3
+	MOVD	R3, c+56(FP)
+
+	MOVD	$0, R1		// i = 0
+	BR 	E9
 
 	// i < n-1
-L9:	MOVD R10, R3            // w = w1
-	MOVD 8(R8)(R1*1), R10   // w1 = x[i+1]
+L9:	MOVD	R10, R3		// w = w1
+	MOVD	8(R8)(R1*1), R10	// w1 = x[i+1]
 
-	SRD  R4,  R3            // w>>s | w1<<s
-	SLD  R7, R10, R6
-	OR   R6, R3
-	MOVD R3, (R2)(R1*1)     // z[i] = w>>s | w1<<s
-	ADD  $8, R1             // i++
+	SRD	R4,  R3		// w>>s | w1<<s
+	SLD	R7, R10, R6
+	OR	R6, R3
+	MOVD	R3, (R2)(R1*1)	// z[i] = w>>s | w1<<s
+	ADD	$8, R1		// i++
 
-E9:	CMPBLT R1, R5, L9       // i < n-1
+E9:	CMPBLT	R1, R5, L9	// i < n-1
 
 	// i >= n-1
-X9a:	SRD  R4, R10            // w1>>s
-	MOVD R10, (R2)(R5*1)    // z[n-1] = w1>>s
+X9a:	SRD	R4, R10		// w1>>s
+	MOVD	R10, (R2)(R5*1)	// z[n-1] = w1>>s
 	RET
 
-X9b:	MOVD R0, c+56(FP)
+X9b:	MOVD	R0, c+56(FP)
 	RET
 
-ZB0:	MOVD z+0(FP), R2
-	MOVD x+24(FP), R8
-	SLD  $3, R5             // n = n*8
+ZB0:	MOVD	z+0(FP), R2
+	MOVD	x+24(FP), R8
+	SLD	$3, R5		// n = n*8
 
-	MOVD (R8), R10          // w1 = x[0]
-	MOVD $0, R3             // R10 << 64
-	MOVD R3, c+56(FP)
+	MOVD	(R8), R10	// w1 = x[0]
+	MOVD	$0, R3		// R10 << 64
+	MOVD	R3, c+56(FP)
 
-	MOVD $0, R1             // i = 0
-	BR   E9Z
+	MOVD	$0, R1		// i = 0
+	BR	E9Z
 
 	// i < n-1
-L9Z:	MOVD R10, R3            // w = w1
-	MOVD 8(R8)(R1*1), R10   // w1 = x[i+1]
+L9Z:	MOVD	R10, R3		// w = w1
+	MOVD	8(R8)(R1*1), R10	// w1 = x[i+1]
 
-	MOVD R3, (R2)(R1*1)     // z[i] = w>>s | w1<<s
-	ADD  $8, R1             // i++
+	MOVD	R3, (R2)(R1*1)	// z[i] = w>>s | w1<<s
+	ADD	$8, R1		// i++
 
-E9Z:	CMPBLT R1, R5, L9Z      // i < n-1
+E9Z:	CMPBLT	R1, R5, L9Z	// i < n-1
 
 	// i >= n-1
-	MOVD R10, (R2)(R5*1)    // z[n-1] = w1>>s
+	MOVD	R10, (R2)(R5*1)	// z[n-1] = w1>>s
 	RET
 
-ZB64:	MOVD z+0(FP), R2
-	MOVD x+24(FP), R8
-	SLD  $3, R5             // n = n*8
-	MOVD (R8), R3          // w1 = x[0]
-	MOVD R3, c+56(FP)
+ZB64:	MOVD	z+0(FP), R2
+	MOVD	x+24(FP), R8
+	SLD	$3, R5		// n = n*8
+	MOVD	(R8), R3	// w1 = x[0]
+	MOVD	R3, c+56(FP)
 
-	MOVD $0, R1            // i = 0
-	BR   E964
+	MOVD	$0, R1		// i = 0
+	BR	E964
 
 	// i < n-1
-L964:	MOVD 8(R8)(R1*1), R3   // w1 = x[i+1]
+L964:	MOVD	8(R8)(R1*1), R3	// w1 = x[i+1]
 
-	MOVD R3, (R2)(R1*1)     // z[i] = w>>s | w1<<s
-	ADD  $8, R1             // i++
+	MOVD	R3, (R2)(R1*1)	// z[i] = w>>s | w1<<s
+	ADD	$8, R1		// i++
 
-E964:	CMPBLT R1, R5, L964     // i < n-1
+E964:	CMPBLT	R1, R5, L964	// i < n-1
 
 	// i >= n-1
-	MOVD  $0, R10            // w1>>s
-	MOVD R10, (R2)(R5*1)    // z[n-1] = w1>>s
+	MOVD	$0, R10            // w1>>s
+	MOVD	R10, (R2)(R5*1)    // z[n-1] = w1>>s
 	RET
 
 // CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, DX = r3, AX = r6 , BX = R1 , (R0 set to 0) + use R11 + use R7 for i
 // func mulAddVWW(z, x []Word, y, r Word) (c Word)
 TEXT ·mulAddVWW(SB),NOSPLIT,$0
-	MOVD z+0(FP), R2
-	MOVD x+24(FP), R8
-	MOVD y+48(FP), R9
-	MOVD r+56(FP), R4	// c = r
-	MOVD z_len+8(FP), R5
-	MOVD $0, R1		// i = 0
-	MOVD $0, R7		// i*8 = 0
-	MOVD $0, R0		// make sure it's zero
-	BR E5
-
-L5:	MOVD (R8)(R1*1), R6
-	MULHDU R9, R6
-	ADDC R4, R11 		//add to low order bits
-	ADDE R0, R6
-	MOVD R11, (R2)(R1*1)
-	MOVD R6, R4
-	ADD  $8, R1		// i*8 + 8
-	ADD  $1, R7		// i++
-
-E5:	CMPBLT R7, R5, L5	// i < n
-
-	MOVD R4, c+64(FP)
+	MOVD	z+0(FP), R2
+	MOVD	x+24(FP), R8
+	MOVD	y+48(FP), R9
+	MOVD	r+56(FP), R4	// c = r
+	MOVD	z_len+8(FP), R5
+	MOVD	$0, R1		// i = 0
+	MOVD	$0, R7		// i*8 = 0
+	MOVD	$0, R0		// make sure it's zero
+	BR	E5
+
+L5:	MOVD	(R8)(R1*1), R6
+	MULHDU	R9, R6
+	ADDC	R4, R11 	//add to low order bits
+	ADDE	R0, R6
+	MOVD	R11, (R2)(R1*1)
+	MOVD	R6, R4
+	ADD	$8, R1		// i*8 + 8
+	ADD	$1, R7		// i++
+
+E5:	CMPBLT	R7, R5, L5	// i < n
+
+	MOVD	R4, c+64(FP)
 	RET
 
 // func addMulVVW(z, x []Word, y Word) (c Word)
 // CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, AX = r11, DX = R6, r12=r12, BX = R1 , (R0 set to 0) + use R11 + use R7 for i
 TEXT ·addMulVVW(SB),NOSPLIT,$0
-	MOVD z+0(FP), R2
-	MOVD x+24(FP), R8
-	MOVD y+48(FP), R9
-	MOVD z_len+8(FP), R5
-
-	MOVD $0, R1		// i*8 = 0
-	MOVD $0, R7		// i = 0
-	MOVD $0, R0		// make sure it's zero
-	MOVD $0, R4		// c = 0
-
-	MOVD R5, R12
-	AND  $-2, R12
-	CMPBGE R5, $2, A6
-	BR   E6
-
-A6:	MOVD (R8)(R1*1), R6
-	MULHDU R9, R6
-	MOVD (R2)(R1*1), R10
-	ADDC R10, R11	//add to low order bits
-	ADDE R0, R6
-	ADDC R4, R11
-	ADDE R0, R6
-	MOVD R6, R4
-	MOVD R11, (R2)(R1*1)
-
-	MOVD (8)(R8)(R1*1), R6
-	MULHDU R9, R6
-	MOVD (8)(R2)(R1*1), R10
-	ADDC R10, R11	//add to low order bits
-	ADDE R0, R6
-	ADDC R4, R11
-	ADDE R0, R6
-	MOVD R6, R4
-	MOVD R11, (8)(R2)(R1*1)
-
-	ADD  $16, R1		// i*8 + 8
-	ADD  $2, R7		// i++
-
-	CMPBLT R7, R12, A6
-	BR E6
-
-L6:	MOVD (R8)(R1*1), R6
-	MULHDU R9, R6
-	MOVD (R2)(R1*1), R10
-	ADDC R10, R11	//add to low order bits
-	ADDE R0, R6
-	ADDC R4, R11
-	ADDE R0, R6
-	MOVD R6, R4
-	MOVD R11, (R2)(R1*1)
-
-	ADD  $8, R1		// i*8 + 8
-	ADD  $1, R7		// i++
-
-E6:	CMPBLT R7, R5, L6	// i < n
-
-	MOVD R4, c+56(FP)
+	MOVD	z+0(FP), R2
+	MOVD	x+24(FP), R8
+	MOVD	y+48(FP), R9
+	MOVD	z_len+8(FP), R5
+
+	MOVD	$0, R1		// i*8 = 0
+	MOVD	$0, R7		// i = 0
+	MOVD	$0, R0		// make sure it's zero
+	MOVD	$0, R4		// c = 0
+
+	MOVD	R5, R12
+	AND	$-2, R12
+	CMPBGE	R5, $2, A6
+	BR	E6
+
+A6:	MOVD	(R8)(R1*1), R6
+	MULHDU	R9, R6
+	MOVD	(R2)(R1*1), R10
+	ADDC	R10, R11	//add to low order bits
+	ADDE	R0, R6
+	ADDC	R4, R11
+	ADDE	R0, R6
+	MOVD	R6, R4
+	MOVD	R11, (R2)(R1*1)
+
+	MOVD	(8)(R8)(R1*1), R6
+	MULHDU	R9, R6
+	MOVD	(8)(R2)(R1*1), R10
+	ADDC	R10, R11	//add to low order bits
+	ADDE	R0, R6
+	ADDC	R4, R11
+	ADDE	R0, R6
+	MOVD	R6, R4
+	MOVD	R11, (8)(R2)(R1*1)
+
+	ADD	$16, R1		// i*8 + 8
+	ADD	$2, R7		// i++
+
+	CMPBLT	R7, R12, A6
+	BR	E6
+
+L6:	MOVD	(R8)(R1*1), R6
+	MULHDU	R9, R6
+	MOVD	(R2)(R1*1), R10
+	ADDC	R10, R11	//add to low order bits
+	ADDE	R0, R6
+	ADDC	R4, R11
+	ADDE	R0, R6
+	MOVD	R6, R4
+	MOVD	R11, (R2)(R1*1)
+
+	ADD	$8, R1		// i*8 + 8
+	ADD	$1, R7		// i++
+
+E6:	CMPBLT	R7, R5, L6	// i < n
+
+	MOVD	R4, c+56(FP)
 	RET
 
 // func divWVW(z []Word, xn Word, x []Word, y Word) (r Word)
 // CX = R4, r8 = r8, r9=r9, r10 = r2 , r11 = r5, AX = r11, DX = R6, r12=r12, BX = R1(*8) , (R0 set to 0) + use R11 + use R7 for i
 TEXT ·divWVW(SB),NOSPLIT,$0
-	MOVD z+0(FP), R2
-	MOVD xn+24(FP), R10	// r = xn
-	MOVD x+32(FP), R8
-	MOVD y+56(FP), R9
-	MOVD z_len+8(FP), R7	// i = z
-	SLD  $3, R7, R1		// i*8
-	MOVD $0, R0		// make sure it's zero
-	BR E7
-
-L7:	MOVD (R8)(R1*1), R11
-	WORD $0xB98700A9  //DLGR R10,R9
-	MOVD R11, (R2)(R1*1)
-
-E7:	SUB  $1, R7		// i--
-	SUB  $8, R1
-	BGE L7			// i >= 0
-
-	MOVD R10, r+64(FP)
+	MOVD	z+0(FP), R2
+	MOVD	xn+24(FP), R10	// r = xn
+	MOVD	x+32(FP), R8
+	MOVD	y+56(FP), R9
+	MOVD	z_len+8(FP), R7	// i = z
+	SLD	$3, R7, R1		// i*8
+	MOVD	$0, R0		// make sure it's zero
+	BR	E7
+
+L7:	MOVD	(R8)(R1*1), R11
+	WORD	$0xB98700A9	//DLGR R10,R9
+	MOVD	R11, (R2)(R1*1)
+
+E7:	SUB	$1, R7		// i--
+	SUB	$8, R1
+	BGE	L7		// i >= 0
+
+	MOVD	R10, r+64(FP)
 	RET
 
 // func bitLen(x Word) (n int)
 TEXT ·bitLen(SB),NOSPLIT,$0
-	MOVD x+0(FP), R2
-	WORD $0xb9830022 // FLOGR R2,R2
-	MOVD $64, R3
-	SUB  R2, R3
-	MOVD R3, n+8(FP)
+	MOVD  x+0(FP), R2
+	FLOGR R2, R2 // clobbers R3
+	MOVD  $64, R3
+	SUB   R2, R3
+	MOVD  R3, n+8(FP)
 	RET
+
diff --git a/src/math/big/arith_s390x_test.go b/src/math/big/arith_s390x_test.go
new file mode 100644
index 0000000..31a777e
--- /dev/null
+++ b/src/math/big/arith_s390x_test.go
@@ -0,0 +1,44 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x !math_big_pure_go
+
+package big
+
+import (
+	"testing"
+)
+
+// Tests whether the non vector routines are working, even when the tests are run on a
+// vector-capable machine
+
+func TestFunVVnovec(t *testing.T) {
+	if hasVX == true {
+		for _, a := range sumVV {
+			arg := a
+			testFunVV(t, "addVV_novec", addVV_novec, arg)
+
+			arg = argVV{a.z, a.y, a.x, a.c}
+			testFunVV(t, "addVV_novec symmetric", addVV_novec, arg)
+
+			arg = argVV{a.x, a.z, a.y, a.c}
+			testFunVV(t, "subVV_novec", subVV_novec, arg)
+
+			arg = argVV{a.y, a.z, a.x, a.c}
+			testFunVV(t, "subVV_novec symmetric", subVV_novec, arg)
+		}
+	}
+}
+
+func TestFunVWnovec(t *testing.T) {
+	if hasVX == true {
+		for _, a := range sumVW {
+			arg := a
+			testFunVW(t, "addVW_novec", addVW_novec, arg)
+
+			arg = argVW{a.x, a.z, a.y, a.c}
+			testFunVW(t, "subVW_novec", subVW_novec, arg)
+		}
+	}
+}
diff --git a/src/math/big/arith_test.go b/src/math/big/arith_test.go
index 75862b4..f2b3083 100644
--- a/src/math/big/arith_test.go
+++ b/src/math/big/arith_test.go
@@ -6,10 +6,14 @@ package big
 
 import (
 	"fmt"
+	"internal/testenv"
 	"math/rand"
+	"strings"
 	"testing"
 )
 
+var isRaceBuilder = strings.HasSuffix(testenv.Builder(), "-race")
+
 type funWW func(x, y, c Word) (z1, z0 Word)
 type argWW struct {
 	x, y, c, z1, z0 Word
@@ -123,6 +127,9 @@ var benchSizes = []int{1, 2, 3, 4, 5, 1e1, 1e2, 1e3, 1e4, 1e5}
 
 func BenchmarkAddVV(b *testing.B) {
 	for _, n := range benchSizes {
+		if isRaceBuilder && n > 1e3 {
+			continue
+		}
 		x := rndV(n)
 		y := rndV(n)
 		z := make([]Word, n)
@@ -233,6 +240,9 @@ func TestFunVW(t *testing.T) {
 
 func BenchmarkAddVW(b *testing.B) {
 	for _, n := range benchSizes {
+		if isRaceBuilder && n > 1e3 {
+			continue
+		}
 		x := rndV(n)
 		y := rndW()
 		z := make([]Word, n)
@@ -371,6 +381,9 @@ func TestMulAddWWW(t *testing.T) {
 
 func BenchmarkAddMulVVW(b *testing.B) {
 	for _, n := range benchSizes {
+		if isRaceBuilder && n > 1e3 {
+			continue
+		}
 		x := rndV(n)
 		y := rndW()
 		z := make([]Word, n)
diff --git a/src/math/big/decimal.go b/src/math/big/decimal.go
index 2c0c9da..2dfa032 100644
--- a/src/math/big/decimal.go
+++ b/src/math/big/decimal.go
@@ -125,11 +125,12 @@ func shr(x *decimal, s uint) {
 
 	// read a digit, write a digit
 	w := 0 // write index
+	mask := Word(1)<<s - 1
 	for r < len(x.mant) {
 		ch := Word(x.mant[r])
 		r++
 		d := n >> s
-		n -= d << s
+		n &= mask // n -= d << s
 		x.mant[w] = byte(d + '0')
 		w++
 		n = n*10 + ch - '0'
@@ -138,7 +139,7 @@ func shr(x *decimal, s uint) {
 	// write extra digits that still fit
 	for n > 0 && w < len(x.mant) {
 		d := n >> s
-		n -= d << s
+		n &= mask
 		x.mant[w] = byte(d + '0')
 		w++
 		n = n * 10
@@ -148,7 +149,7 @@ func shr(x *decimal, s uint) {
 	// append additional digits that didn't fit
 	for n > 0 {
 		d := n >> s
-		n -= d << s
+		n &= mask
 		x.mant = append(x.mant, byte(d+'0'))
 		n = n * 10
 	}
diff --git a/src/math/big/decimal_test.go b/src/math/big/decimal_test.go
index 15bdb18..424811e 100644
--- a/src/math/big/decimal_test.go
+++ b/src/math/big/decimal_test.go
@@ -4,7 +4,10 @@
 
 package big
 
-import "testing"
+import (
+	"fmt"
+	"testing"
+)
 
 func TestDecimalString(t *testing.T) {
 	for _, test := range []struct {
@@ -105,12 +108,27 @@ func TestDecimalRounding(t *testing.T) {
 	}
 }
 
+var sink string
+
 func BenchmarkDecimalConversion(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		for shift := -100; shift <= +100; shift++ {
 			var d decimal
 			d.init(natOne, shift)
-			d.String()
+			sink = d.String()
 		}
 	}
 }
+
+func BenchmarkFloatString(b *testing.B) {
+	x := new(Float)
+	for _, prec := range []uint{1e2, 1e3, 1e4, 1e5} {
+		x.SetPrec(prec).SetRat(NewRat(1, 3))
+		b.Run(fmt.Sprintf("%v", prec), func(b *testing.B) {
+			b.ReportAllocs()
+			for i := 0; i < b.N; i++ {
+				sink = x.String()
+			}
+		})
+	}
+}
diff --git a/src/math/big/doc.go b/src/math/big/doc.go
index a3c2375..65ed019 100644
--- a/src/math/big/doc.go
+++ b/src/math/big/doc.go
@@ -31,7 +31,7 @@ setters, for instance:
 
 	var z1 Int
 	z1.SetUint64(123)                 // z1 := 123
-	z2 := new(Rat).SetFloat64(1.2)    // z2 := 6/5
+	z2 := new(Rat).SetFloat64(1.25)   // z2 := 5/4
 	z3 := new(Float).SetInt(z1)       // z3 := 123.0
 
 Setters, numeric operations and predicates are represented as methods of
diff --git a/src/math/big/example_test.go b/src/math/big/example_test.go
index ac79552..cfc7735 100644
--- a/src/math/big/example_test.go
+++ b/src/math/big/example_test.go
@@ -51,6 +51,19 @@ func ExampleInt_Scan() {
 	// Output: 18446744073709551617
 }
 
+func ExampleFloat_Scan() {
+	// The Scan function is rarely used directly;
+	// the fmt package recognizes it as an implementation of fmt.Scanner.
+	f := new(big.Float)
+	_, err := fmt.Sscan("1.19282e99", f)
+	if err != nil {
+		log.Println("error scanning value:", err)
+	} else {
+		fmt.Println(f)
+	}
+	// Output: 1.19282e+99
+}
+
 // This example demonstrates how to use big.Int to compute the smallest
 // Fibonacci number with 100 decimal digits and to test whether it is prime.
 func Example_fibonacci() {
diff --git a/src/math/big/float.go b/src/math/big/float.go
index 7a9c2b3..aabd7b4 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -1210,20 +1210,30 @@ func (z *Float) uadd(x, y *Float) {
 	ex := int64(x.exp) - int64(len(x.mant))*_W
 	ey := int64(y.exp) - int64(len(y.mant))*_W
 
+	al := alias(z.mant, x.mant) || alias(z.mant, y.mant)
+
 	// TODO(gri) having a combined add-and-shift primitive
 	//           could make this code significantly faster
 	switch {
 	case ex < ey:
-		// cannot re-use z.mant w/o testing for aliasing
-		t := nat(nil).shl(y.mant, uint(ey-ex))
-		z.mant = z.mant.add(x.mant, t)
+		if al {
+			t := nat(nil).shl(y.mant, uint(ey-ex))
+			z.mant = z.mant.add(x.mant, t)
+		} else {
+			z.mant = z.mant.shl(y.mant, uint(ey-ex))
+			z.mant = z.mant.add(x.mant, z.mant)
+		}
 	default:
 		// ex == ey, no shift needed
 		z.mant = z.mant.add(x.mant, y.mant)
 	case ex > ey:
-		// cannot re-use z.mant w/o testing for aliasing
-		t := nat(nil).shl(x.mant, uint(ex-ey))
-		z.mant = z.mant.add(t, y.mant)
+		if al {
+			t := nat(nil).shl(x.mant, uint(ex-ey))
+			z.mant = z.mant.add(t, y.mant)
+		} else {
+			z.mant = z.mant.shl(x.mant, uint(ex-ey))
+			z.mant = z.mant.add(z.mant, y.mant)
+		}
 		ex = ey
 	}
 	// len(z.mant) > 0
@@ -1247,18 +1257,28 @@ func (z *Float) usub(x, y *Float) {
 	ex := int64(x.exp) - int64(len(x.mant))*_W
 	ey := int64(y.exp) - int64(len(y.mant))*_W
 
+	al := alias(z.mant, x.mant) || alias(z.mant, y.mant)
+
 	switch {
 	case ex < ey:
-		// cannot re-use z.mant w/o testing for aliasing
-		t := nat(nil).shl(y.mant, uint(ey-ex))
-		z.mant = t.sub(x.mant, t)
+		if al {
+			t := nat(nil).shl(y.mant, uint(ey-ex))
+			z.mant = t.sub(x.mant, t)
+		} else {
+			z.mant = z.mant.shl(y.mant, uint(ey-ex))
+			z.mant = z.mant.sub(x.mant, z.mant)
+		}
 	default:
 		// ex == ey, no shift needed
 		z.mant = z.mant.sub(x.mant, y.mant)
 	case ex > ey:
-		// cannot re-use z.mant w/o testing for aliasing
-		t := nat(nil).shl(x.mant, uint(ex-ey))
-		z.mant = t.sub(t, y.mant)
+		if al {
+			t := nat(nil).shl(x.mant, uint(ex-ey))
+			z.mant = t.sub(t, y.mant)
+		} else {
+			z.mant = z.mant.shl(x.mant, uint(ex-ey))
+			z.mant = z.mant.sub(z.mant, y.mant)
+		}
 		ex = ey
 	}
 
diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go
index 464619b..7d4bd31 100644
--- a/src/math/big/float_test.go
+++ b/src/math/big/float_test.go
@@ -5,6 +5,7 @@
 package big
 
 import (
+	"flag"
 	"fmt"
 	"math"
 	"strconv"
@@ -1495,12 +1496,14 @@ func TestFloatQuo(t *testing.T) {
 	}
 }
 
+var long = flag.Bool("long", false, "run very long tests")
+
 // TestFloatQuoSmoke tests all divisions x/y for values x, y in the range [-n, +n];
 // it serves as a smoke test for basic correctness of division.
 func TestFloatQuoSmoke(t *testing.T) {
-	n := 1000
-	if testing.Short() {
-		n = 10
+	n := 10
+	if *long {
+		n = 1000
 	}
 
 	const dprec = 3         // max. precision variation
@@ -1762,3 +1765,41 @@ func TestFloatCmpSpecialValues(t *testing.T) {
 		}
 	}
 }
+
+func BenchmarkFloatAdd(b *testing.B) {
+	x := new(Float)
+	y := new(Float)
+	z := new(Float)
+
+	for _, prec := range []uint{10, 1e2, 1e3, 1e4, 1e5} {
+		x.SetPrec(prec).SetRat(NewRat(1, 3))
+		y.SetPrec(prec).SetRat(NewRat(1, 6))
+		z.SetPrec(prec)
+
+		b.Run(fmt.Sprintf("%v", prec), func(b *testing.B) {
+			b.ReportAllocs()
+			for i := 0; i < b.N; i++ {
+				z.Add(x, y)
+			}
+		})
+	}
+}
+
+func BenchmarkFloatSub(b *testing.B) {
+	x := new(Float)
+	y := new(Float)
+	z := new(Float)
+
+	for _, prec := range []uint{10, 1e2, 1e3, 1e4, 1e5} {
+		x.SetPrec(prec).SetRat(NewRat(1, 3))
+		y.SetPrec(prec).SetRat(NewRat(1, 6))
+		z.SetPrec(prec)
+
+		b.Run(fmt.Sprintf("%v", prec), func(b *testing.B) {
+			b.ReportAllocs()
+			for i := 0; i < b.N; i++ {
+				z.Sub(x, y)
+			}
+		})
+	}
+}
diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go
index a884df6..95d1bf8 100644
--- a/src/math/big/floatconv.go
+++ b/src/math/big/floatconv.go
@@ -12,9 +12,13 @@ import (
 	"strings"
 )
 
+var floatZero Float
+
 // SetString sets z to the value of s and returns z and a boolean indicating
 // success. s must be a floating-point number of the same format as accepted
-// by Parse, with base argument 0.
+// by Parse, with base argument 0. The entire string (not just a prefix) must
+// be valid for success. If the operation failed, the value of z is undefined
+// but the returned value is nil.
 func (z *Float) SetString(s string) (*Float, bool) {
 	if f, _, err := z.Parse(s, 0); err == nil {
 		return f, true
@@ -212,17 +216,18 @@ func (z *Float) pow5(n uint64) *Float {
 //
 // It sets z to the (possibly rounded) value of the corresponding floating-
 // point value, and returns z, the actual base b, and an error err, if any.
+// The entire string (not just a prefix) must be consumed for success.
 // If z's precision is 0, it is changed to 64 before rounding takes effect.
 // The number must be of the form:
 //
 //	number   = [ sign ] [ prefix ] mantissa [ exponent ] | infinity .
 //	sign     = "+" | "-" .
-//      prefix   = "0" ( "x" | "X" | "b" | "B" ) .
+//	prefix   = "0" ( "x" | "X" | "b" | "B" ) .
 //	mantissa = digits | digits "." [ digits ] | "." digits .
 //	exponent = ( "E" | "e" | "p" ) [ sign ] digits .
 //	digits   = digit { digit } .
 //	digit    = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
-//      infinity = [ sign ] ( "inf" | "Inf" ) .
+//	infinity = [ sign ] ( "inf" | "Inf" ) .
 //
 // The base argument must be 0, 2, 10, or 16. Providing an invalid base
 // argument will lead to a run-time panic.
@@ -273,3 +278,16 @@ func (z *Float) Parse(s string, base int) (f *Float, b int, err error) {
 func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) {
 	return new(Float).SetPrec(prec).SetMode(mode).Parse(s, base)
 }
+
+var _ fmt.Scanner = &floatZero // *Float must implement fmt.Scanner
+
+// Scan is a support routine for fmt.Scanner; it sets z to the value of
+// the scanned number. It accepts formats whose verbs are supported by
+// fmt.Scan for floating point values, which are:
+// 'b' (binary), 'e', 'E', 'f', 'F', 'g' and 'G'.
+// Scan doesn't handle ±Inf.
+func (z *Float) Scan(s fmt.ScanState, ch rune) error {
+	s.SkipSpace()
+	_, _, err := z.scan(byteReader{s}, 0)
+	return err
+}
diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go
index b2a1ab0..edcb2eb 100644
--- a/src/math/big/floatconv_test.go
+++ b/src/math/big/floatconv_test.go
@@ -5,6 +5,7 @@
 package big
 
 import (
+	"bytes"
 	"fmt"
 	"math"
 	"strconv"
@@ -665,3 +666,54 @@ func BenchmarkParseFloatLargeExp(b *testing.B) {
 		}
 	}
 }
+
+func TestFloatScan(t *testing.T) {
+	var floatScanTests = []struct {
+		input     string
+		format    string
+		output    string
+		remaining int
+		wantErr   bool
+	}{
+		0: {"10.0", "%f", "10", 0, false},
+		1: {"23.98+2.0", "%v", "23.98", 4, false},
+		2: {"-1+1", "%v", "-1", 2, false},
+		3: {" 00000", "%v", "0", 0, false},
+		4: {"-123456p-78", "%b", "-4.084816388e-19", 0, false},
+		5: {"+123", "%b", "123", 0, false},
+		6: {"-1.234e+56", "%e", "-1.234e+56", 0, false},
+		7: {"-1.234E-56", "%E", "-1.234e-56", 0, false},
+		8: {"-1.234e+567", "%g", "-1.234e+567", 0, false},
+		9: {"+1234567891011.234", "%G", "1.234567891e+12", 0, false},
+
+		// Scan doesn't handle ±Inf.
+		10: {"Inf", "%v", "", 3, true},
+		11: {"-Inf", "%v", "", 3, true},
+		12: {"-Inf", "%v", "", 3, true},
+	}
+
+	var buf bytes.Buffer
+	for i, test := range floatScanTests {
+		x := new(Float)
+		buf.Reset()
+		buf.WriteString(test.input)
+		_, err := fmt.Fscanf(&buf, test.format, x)
+		if test.wantErr {
+			if err == nil {
+				t.Errorf("#%d want non-nil err", i)
+			}
+			continue
+		}
+
+		if err != nil {
+			t.Errorf("#%d error: %s", i, err)
+		}
+
+		if x.String() != test.output {
+			t.Errorf("#%d got %s; want %s", i, x.String(), test.output)
+		}
+		if buf.Len() != test.remaining {
+			t.Errorf("#%d got %d bytes remaining; want %d", i, buf.Len(), test.remaining)
+		}
+	}
+}
diff --git a/src/math/big/floatexample_test.go b/src/math/big/floatexample_test.go
index fb799d5..0c6668c 100644
--- a/src/math/big/floatexample_test.go
+++ b/src/math/big/floatexample_test.go
@@ -11,7 +11,7 @@ import (
 )
 
 func ExampleFloat_Add() {
-	// Operating on numbers of different precision.
+	// Operate on numbers of different precision.
 	var x, y, z big.Float
 	x.SetInt64(1000)          // x is automatically set to 64bit precision
 	y.SetFloat64(2.718281828) // y is automatically set to 53bit precision
@@ -26,8 +26,8 @@ func ExampleFloat_Add() {
 	// z = 1002.718282 (0x.faadf854p+10, prec = 32, acc = Below)
 }
 
-func Example_Shift() {
-	// Implementing Float "shift" by modifying the (binary) exponents directly.
+func ExampleFloat_shift() {
+	// Implement Float "shift" by modifying the (binary) exponents directly.
 	for s := -5; s <= 5; s++ {
 		x := big.NewFloat(0.5)
 		x.SetMantExp(x, x.MantExp(nil)+s) // shift x by s
diff --git a/src/math/big/floatmarsh.go b/src/math/big/floatmarsh.go
index 3725d4b..d1c1dab 100644
--- a/src/math/big/floatmarsh.go
+++ b/src/math/big/floatmarsh.go
@@ -16,7 +16,7 @@ const floatGobVersion byte = 1
 
 // GobEncode implements the gob.GobEncoder interface.
 // The Float value and all its attributes (precision,
-// rounding mode, accuracy) are marshalled.
+// rounding mode, accuracy) are marshaled.
 func (x *Float) GobEncode() ([]byte, error) {
 	if x == nil {
 		return nil, nil
diff --git a/src/math/big/ftoa.go b/src/math/big/ftoa.go
index 57b16e1..d2a8588 100644
--- a/src/math/big/ftoa.go
+++ b/src/math/big/ftoa.go
@@ -376,6 +376,8 @@ func min(x, y int) int {
 	return y
 }
 
+var _ fmt.Formatter = &floatZero // *Float must implement fmt.Formatter
+
 // Format implements fmt.Formatter. It accepts all the regular
 // formats for floating-point numbers ('b', 'e', 'E', 'f', 'F',
 // 'g', 'G') as well as 'p' and 'v'. See (*Float).Text for the
diff --git a/src/math/big/gcd_test.go b/src/math/big/gcd_test.go
index a929bf5..3cca2ec 100644
--- a/src/math/big/gcd_test.go
+++ b/src/math/big/gcd_test.go
@@ -20,6 +20,9 @@ func randInt(r *rand.Rand, size uint) *Int {
 }
 
 func runGCD(b *testing.B, aSize, bSize uint) {
+	if isRaceBuilder && (aSize > 1000 || bSize > 1000) {
+		b.Skip("skipping on race builder")
+	}
 	b.Run("WithoutXY", func(b *testing.B) {
 		runGCDExt(b, aSize, bSize, false)
 	})
diff --git a/src/math/big/int.go b/src/math/big/int.go
index f2a75d1..a2c1b58 100644
--- a/src/math/big/int.go
+++ b/src/math/big/int.go
@@ -361,7 +361,8 @@ func (x *Int) Uint64() uint64 {
 }
 
 // SetString sets z to the value of s, interpreted in the given base,
-// and returns z and a boolean indicating success. If SetString fails,
+// and returns z and a boolean indicating success. The entire string
+// (not just a prefix) must be valid for success. If SetString fails,
 // the value of z is undefined but the returned value is nil.
 //
 // The base argument must be 0 or a value between 2 and MaxBase. If the base
@@ -371,12 +372,11 @@ func (x *Int) Uint64() uint64 {
 //
 func (z *Int) SetString(s string, base int) (*Int, bool) {
 	r := strings.NewReader(s)
-	_, _, err := z.scan(r, base)
-	if err != nil {
+	if _, _, err := z.scan(r, base); err != nil {
 		return nil, false
 	}
-	_, err = r.ReadByte()
-	if err != io.EOF {
+	// entire string must have been consumed
+	if _, err := r.ReadByte(); err != io.EOF {
 		return nil, false
 	}
 	return z, true // err == io.EOF => scan consumed all of s
@@ -550,19 +550,6 @@ func (z *Int) binaryGCD(a, b *Int) *Int {
 	return z.Lsh(u, k)
 }
 
-// ProbablyPrime performs n Miller-Rabin tests to check whether x is prime.
-// If x is prime, it returns true.
-// If x is not prime, it returns false with probability at least 1 - ¼ⁿ.
-//
-// It is not suitable for judging primes that an adversary may have crafted
-// to fool this test.
-func (x *Int) ProbablyPrime(n int) bool {
-	if n <= 0 {
-		panic("non-positive n for ProbablyPrime")
-	}
-	return !x.neg && x.abs.probablyPrime(n)
-}
-
 // Rand sets z to a pseudo-random number in [0, n) and returns z.
 func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int {
 	z.neg = false
@@ -577,6 +564,11 @@ func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int {
 // ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ
 // and returns z. If g and n are not relatively prime, the result is undefined.
 func (z *Int) ModInverse(g, n *Int) *Int {
+	if g.neg {
+		// GCD expects parameters a and b to be > 0.
+		var g2 Int
+		g = g2.Mod(g, n)
+	}
 	var d Int
 	d.GCD(z, nil, g, n)
 	// x and y are such that g*x + n*y = d. Since g and n are
@@ -932,3 +924,14 @@ func (z *Int) Not(x *Int) *Int {
 	z.neg = true // z cannot be zero if x is positive
 	return z
 }
+
+// Sqrt sets z to ⌊√x⌋, the largest integer such that z² ≤ x, and returns z.
+// It panics if x is negative.
+func (z *Int) Sqrt(x *Int) *Int {
+	if x.neg {
+		panic("square root of negative number")
+	}
+	z.neg = false
+	z.abs = z.abs.sqrt(x.abs)
+	return z
+}
diff --git a/src/math/big/int_test.go b/src/math/big/int_test.go
index 45a3765..b8e0778 100644
--- a/src/math/big/int_test.go
+++ b/src/math/big/int_test.go
@@ -9,6 +9,7 @@ import (
 	"encoding/hex"
 	"fmt"
 	"math/rand"
+	"strings"
 	"testing"
 	"testing/quick"
 )
@@ -478,6 +479,18 @@ func TestQuoStepD6(t *testing.T) {
 	}
 }
 
+func BenchmarkQuoRem(b *testing.B) {
+	x, _ := new(Int).SetString("153980389784927331788354528594524332344709972855165340650588877572729725338415474372475094155672066328274535240275856844648695200875763869073572078279316458648124537905600131008790701752441155668003033945258023841165089852359980273279085783159654751552359397986180318708491098942831252291841441726305535546071", 0)
+	y, _ := new(Int).SetString("7746362281539803897849273317883545285945243323447099728551653406505888775727297253384154743724750941556720663282745352402758568446486952008757638690735720782793164586481245379056001310087907017524411556680030339452580238411650898523599802732790857831596547515523593979861803187084910989428312522918414417263055355460715745539358014631136245887418412633787074173796862711588221766398229333338511838891484974940633857861775630560092874987828057333663969469797013996 [...]
+	q := new(Int)
+	r := new(Int)
+
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		q.QuoRem(y, x, r)
+	}
+}
+
 var bitLenTests = []struct {
 	in  string
 	out int
@@ -572,6 +585,19 @@ var expTests = []struct {
 	{"0xffffffffffffffff00000001", "0xffffffffffffffff00000001", "0xffffffffffffffff00000001", "0"},
 	{"0xffffffffffffffffffffffff00000001", "0xffffffffffffffffffffffff00000001", "0xffffffffffffffffffffffff00000001", "0"},
 	{"0xffffffffffffffffffffffffffffffff00000001", "0xffffffffffffffffffffffffffffffff00000001", "0xffffffffffffffffffffffffffffffff00000001", "0"},
+
+	{
+		"2",
+		"0xB08FFB20760FFED58FADA86DFEF71AD72AA0FA763219618FE022C197E54708BB1191C66470250FCE8879487507CEE41381CA4D932F81C2B3F1AB20B539D50DCD",
+		"0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E37 [...]
+		"0x6AADD3E3E424D5B713FCAA8D8945B1E055166132038C57BBD2D51C833F0C5EA2007A2324CE514F8E8C2F008A2F36F44005A4039CB55830986F734C93DAF0EB4BAB54A6A8C7081864F44346E9BC6F0A3EB9F2C0146A00C6A05187D0C101E1F2D038CDB70CB5E9E05A2D188AB6CBB46286624D4415E7D4DBFAD3BCC6009D915C406EED38F468B940F41E6BEDC0430DD78E6F19A7DA3A27498A4181E24D738B0072D8F6ADB8C9809A5B033A09785814FD9919F6EF9F83EEA519BEC593855C4C10CBEEC582D4AE0792158823B0275E6AEC35242740468FAF3D5C60FD1E376362B6322F78B7ED0CA1C5BBCD2B49734A56C0967A1D01A [...]
+	},
+	{
+		"2",
+		"0xB08FFB20760FFED58FADA86DFEF71AD72AA0FA763219618FE022C197E54708BB1191C66470250FCE8879487507CEE41381CA4D932F81C2B3F1AB20B539D50DCD",
+		"0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E37 [...]
+		"0x7858794B5897C29F4ED0B40913416AB6C48588484E6A45F2ED3E26C941D878E923575AAC434EE2750E6439A6976F9BB4D64CEDB2A53CE8D04DD48CADCDF8E46F22747C6B81C6CEA86C0D873FBF7CEF262BAAC43A522BD7F32F3CDAC52B9337C77B3DCFB3DB3EDD80476331E82F4B1DF8EFDC1220C92656DFC9197BDC1877804E28D928A2A284B8DED506CBA304435C9D0133C246C98A7D890D1DE60CBC53A024361DA83A9B8775019083D22AC6820ED7C3C68F8E801DD4EC779EE0A05C6EB682EF9840D285B838369BA7E148FA27691D524FAEAF7C6ECE2A4B99A294B9F2C241857B5B90CC8BFFCFCF18DFA7D676131D5CD3855 [...]
+	},
 }
 
 func TestExp(t *testing.T) {
@@ -614,6 +640,26 @@ func TestExp(t *testing.T) {
 	}
 }
 
+func BenchmarkExp(b *testing.B) {
+	x, _ := new(Int).SetString("1100128911836308964601735937211796349925054637526904754277792800610324687668875673576090568060464662435319686957275262328514040875542037404931764642818527007955537276350311564605460286759366292389414094083747950719493426753283169456551646676502543490234831452562741851564658816095586283902205135365305294707313608478074272972787480345764384819749954829757002692692750250563429707952729900426776978076856569545994523558689262705917888499877298939750506120639545559 [...]
+	y, _ := new(Int).SetString("0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7 [...]
+	n, _ := new(Int).SetString("0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7 [...]
+	out := new(Int)
+	for i := 0; i < b.N; i++ {
+		out.Exp(x, y, n)
+	}
+}
+
+func BenchmarkExp2(b *testing.B) {
+	x, _ := new(Int).SetString("2", 0)
+	y, _ := new(Int).SetString("0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7 [...]
+	n, _ := new(Int).SetString("0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7 [...]
+	out := new(Int)
+	for i := 0; i < b.N; i++ {
+		out.Exp(x, y, n)
+	}
+}
+
 func checkGcd(aBytes, bBytes []byte) bool {
 	x := new(Int)
 	y := new(Int)
@@ -715,85 +761,6 @@ func TestGcd(t *testing.T) {
 	}
 }
 
-var primes = []string{
-	"2",
-	"3",
-	"5",
-	"7",
-	"11",
-
-	"13756265695458089029",
-	"13496181268022124907",
-	"10953742525620032441",
-	"17908251027575790097",
-
-	// https://golang.org/issue/638
-	"18699199384836356663",
-
-	"98920366548084643601728869055592650835572950932266967461790948584315647051443",
-	"94560208308847015747498523884063394671606671904944666360068158221458669711639",
-
-	// http://primes.utm.edu/lists/small/small3.html
-	"449417999055441493994709297093108513015373787049558499205492347871729927573118262811508386655998299074566974373711472560655026288668094291699357843464363003144674940345912431129144354948751003607115263071543163",
-	"230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593",
-	"5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993",
-	"203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123",
-
-	// ECC primes: http://tools.ietf.org/html/draft-ladd-safecurves-02
-	"3618502788666131106986593281521497120414687020801267626233049500247285301239",                                                                                  // Curve1174: 2^251-9
-	"57896044618658097711785492504343953926634992332820282019728792003956564819949",                                                                                 // Curve25519: 2^255-19
-	"9850501549098619803069760025035903451269934817616361666987073351061430442874302652853566563721228910201656997576599",                                           // E-382: 2^382-105
-	"42307582002575910332922579714097346549017899709713998034217522897561970639123926132812109468141778230245837569601494931472367",                                 // Curve41417: 2^414-17
-	"6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", // E-521: 2^521-1
-}
-
-var composites = []string{
-	"0",
-	"1",
-	"21284175091214687912771199898307297748211672914763848041968395774954376176754",
-	"6084766654921918907427900243509372380954290099172559290432744450051395395951",
-	"84594350493221918389213352992032324280367711247940675652888030554255915464401",
-	"82793403787388584738507275144194252681",
-}
-
-func TestProbablyPrime(t *testing.T) {
-	nreps := 20
-	if testing.Short() {
-		nreps = 1
-	}
-	for i, s := range primes {
-		p, _ := new(Int).SetString(s, 10)
-		if !p.ProbablyPrime(nreps) {
-			t.Errorf("#%d prime found to be non-prime (%s)", i, s)
-		}
-	}
-
-	for i, s := range composites {
-		c, _ := new(Int).SetString(s, 10)
-		if c.ProbablyPrime(nreps) {
-			t.Errorf("#%d composite found to be prime (%s)", i, s)
-		}
-		if testing.Short() {
-			break
-		}
-	}
-
-	// check that ProbablyPrime panics if n <= 0
-	c := NewInt(11) // a prime
-	for _, n := range []int{-1, 0, 1} {
-		func() {
-			defer func() {
-				if n <= 0 && recover() == nil {
-					t.Fatalf("expected panic from ProbablyPrime(%d)", n)
-				}
-			}()
-			if !c.ProbablyPrime(n) {
-				t.Fatalf("%v should be a prime", c)
-			}
-		}()
-	}
-}
-
 type intShiftTest struct {
 	in    string
 	shift uint
@@ -1229,6 +1196,9 @@ func BenchmarkModSqrt224_3Mod4(b *testing.B) {
 }
 
 func BenchmarkModSqrt5430_Tonelli(b *testing.B) {
+	if isRaceBuilder {
+		b.Skip("skipping on race builder")
+	}
 	p := tri(5430)
 	x := new(Int).SetUint64(2)
 	for i := 0; i < b.N; i++ {
@@ -1238,6 +1208,9 @@ func BenchmarkModSqrt5430_Tonelli(b *testing.B) {
 }
 
 func BenchmarkModSqrt5430_3Mod4(b *testing.B) {
+	if isRaceBuilder {
+		b.Skip("skipping on race builder")
+	}
 	p := tri(5430)
 	x := new(Int).SetUint64(2)
 	for i := 0; i < b.N; i++ {
@@ -1303,6 +1276,7 @@ var modInverseTests = []struct {
 }{
 	{"1234567", "458948883992"},
 	{"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"},
+	{"-10", "13"}, // issue #16984
 }
 
 func TestModInverse(t *testing.T) {
@@ -1480,3 +1454,44 @@ func TestIssue2607(t *testing.T) {
 	n := NewInt(10)
 	n.Rand(rand.New(rand.NewSource(9)), n)
 }
+
+func TestSqrt(t *testing.T) {
+	root := 0
+	r := new(Int)
+	for i := 0; i < 10000; i++ {
+		if (root+1)*(root+1) <= i {
+			root++
+		}
+		n := NewInt(int64(i))
+		r.SetInt64(-2)
+		r.Sqrt(n)
+		if r.Cmp(NewInt(int64(root))) != 0 {
+			t.Errorf("Sqrt(%v) = %v, want %v", n, r, root)
+		}
+	}
+
+	for i := 0; i < 1000; i += 10 {
+		n, _ := new(Int).SetString("1"+strings.Repeat("0", i), 10)
+		r := new(Int).Sqrt(n)
+		root, _ := new(Int).SetString("1"+strings.Repeat("0", i/2), 10)
+		if r.Cmp(root) != 0 {
+			t.Errorf("Sqrt(1e%d) = %v, want 1e%d", i, r, i/2)
+		}
+	}
+
+	// Test aliasing.
+	r.SetInt64(100)
+	r.Sqrt(r)
+	if r.Int64() != 10 {
+		t.Errorf("Sqrt(100) = %v, want 10 (aliased output)", r.Int64())
+	}
+}
+
+func BenchmarkSqrt(b *testing.B) {
+	n, _ := new(Int).SetString("1"+strings.Repeat("0", 1001), 10)
+	b.ResetTimer()
+	t := new(Int)
+	for i := 0; i < b.N; i++ {
+		t.Sqrt(n)
+	}
+}
diff --git a/src/math/big/intconv.go b/src/math/big/intconv.go
index daf674a..91a62ce 100644
--- a/src/math/big/intconv.go
+++ b/src/math/big/intconv.go
@@ -52,6 +52,8 @@ func writeMultiple(s fmt.State, text string, count int) {
 	}
 }
 
+var _ fmt.Formatter = intOne // *Int must implement fmt.Formatter
+
 // Format implements fmt.Formatter. It accepts the formats
 // 'b' (binary), 'o' (octal), 'd' (decimal), 'x' (lowercase
 // hexadecimal), and 'X' (uppercase hexadecimal).
@@ -223,6 +225,8 @@ func (r byteReader) UnreadByte() error {
 	return r.UnreadRune()
 }
 
+var _ fmt.Scanner = intOne // *Int must implement fmt.Scanner
+
 // Scan is a support routine for fmt.Scanner; it sets z to the value of
 // the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
 // 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
diff --git a/src/math/big/intmarsh.go b/src/math/big/intmarsh.go
index 4ff57b6..ee1e414 100644
--- a/src/math/big/intmarsh.go
+++ b/src/math/big/intmarsh.go
@@ -59,7 +59,7 @@ func (z *Int) UnmarshalText(text []byte) error {
 	return nil
 }
 
-// The JSON marshallers are only here for API backward compatibility
+// The JSON marshalers are only here for API backward compatibility
 // (programs that explicitly look for these two methods). JSON works
 // fine with the TextMarshaler only.
 
@@ -70,5 +70,9 @@ func (x *Int) MarshalJSON() ([]byte, error) {
 
 // UnmarshalJSON implements the json.Unmarshaler interface.
 func (z *Int) UnmarshalJSON(text []byte) error {
+	// Ignore null, like in the main JSON package.
+	if string(text) == "null" {
+		return nil
+	}
 	return z.UnmarshalText(text)
 }
diff --git a/src/math/big/nat.go b/src/math/big/nat.go
index 2e65d2a..9b1a626 100644
--- a/src/math/big/nat.go
+++ b/src/math/big/nat.go
@@ -542,16 +542,21 @@ func (z nat) div(z2, u, v nat) (q, r nat) {
 	return
 }
 
-// getNat returns a nat of len n. The contents may not be zero.
-func getNat(n int) nat {
-	var z nat
+// getNat returns a *nat of len n. The contents may not be zero.
+// The pool holds *nat to avoid allocation when converting to interface{}.
+func getNat(n int) *nat {
+	var z *nat
 	if v := natPool.Get(); v != nil {
-		z = v.(nat)
+		z = v.(*nat)
 	}
-	return z.make(n)
+	if z == nil {
+		z = new(nat)
+	}
+	*z = z.make(n)
+	return z
 }
 
-func putNat(x nat) {
+func putNat(x *nat) {
 	natPool.Put(x)
 }
 
@@ -575,7 +580,8 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
 	}
 	q = z.make(m + 1)
 
-	qhatv := getNat(n + 1)
+	qhatvp := getNat(n + 1)
+	qhatv := *qhatvp
 	if alias(u, uIn) || alias(u, v) {
 		u = nil // u is an alias for uIn or v - cannot reuse
 	}
@@ -583,36 +589,40 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
 	u.clear() // TODO(gri) no need to clear if we allocated a new u
 
 	// D1.
-	var v1 nat
+	var v1p *nat
 	shift := nlz(v[n-1])
 	if shift > 0 {
 		// do not modify v, it may be used by another goroutine simultaneously
-		v1 = getNat(n)
+		v1p = getNat(n)
+		v1 := *v1p
 		shlVU(v1, v, shift)
 		v = v1
 	}
 	u[len(uIn)] = shlVU(u[0:len(uIn)], uIn, shift)
 
 	// D2.
+	vn1 := v[n-1]
 	for j := m; j >= 0; j-- {
 		// D3.
 		qhat := Word(_M)
-		if u[j+n] != v[n-1] {
+		if ujn := u[j+n]; ujn != vn1 {
 			var rhat Word
-			qhat, rhat = divWW(u[j+n], u[j+n-1], v[n-1])
+			qhat, rhat = divWW(ujn, u[j+n-1], vn1)
 
 			// x1 | x2 = q̂v_{n-2}
-			x1, x2 := mulWW(qhat, v[n-2])
+			vn2 := v[n-2]
+			x1, x2 := mulWW(qhat, vn2)
 			// test if q̂v_{n-2} > br̂ + u_{j+n-2}
-			for greaterThan(x1, x2, rhat, u[j+n-2]) {
+			ujn2 := u[j+n-2]
+			for greaterThan(x1, x2, rhat, ujn2) {
 				qhat--
 				prevRhat := rhat
-				rhat += v[n-1]
+				rhat += vn1
 				// v[n-1] >= 0, so this tests for overflow.
 				if rhat < prevRhat {
 					break
 				}
-				x1, x2 = mulWW(qhat, v[n-2])
+				x1, x2 = mulWW(qhat, vn2)
 			}
 		}
 
@@ -628,10 +638,10 @@ func (z nat) divLarge(u, uIn, v nat) (q, r nat) {
 
 		q[j] = qhat
 	}
-	if v1 != nil {
-		putNat(v1)
+	if v1p != nil {
+		putNat(v1p)
 	}
-	putNat(qhatv)
+	putNat(qhatvp)
 
 	q = q.norm()
 	shrVU(u, u, shift)
@@ -650,14 +660,14 @@ func (x nat) bitLen() int {
 
 const deBruijn32 = 0x077CB531
 
-var deBruijn32Lookup = []byte{
+var deBruijn32Lookup = [...]byte{
 	0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
 	31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9,
 }
 
 const deBruijn64 = 0x03f79d71b4ca8b09
 
-var deBruijn64Lookup = []byte{
+var deBruijn64Lookup = [...]byte{
 	0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
 	62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
 	63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
@@ -950,7 +960,7 @@ func (z nat) expNN(x, y, m nat) nat {
 	// (x^2...x^15) but then reduces the number of multiply-reduces by a
 	// third. Even for a 32-bit exponent, this reduces the number of
 	// operations. Uses Montgomery method for odd moduli.
-	if len(x) > 1 && len(y) > 1 && len(m) > 0 {
+	if x.cmp(natOne) > 0 && len(y) > 1 && len(m) > 0 {
 		if m[0]&1 == 1 {
 			return z.expNNMontgomery(x, y, m)
 		}
@@ -1169,96 +1179,6 @@ func (z nat) expNNMontgomery(x, y, m nat) nat {
 	return zz.norm()
 }
 
-// probablyPrime performs n Miller-Rabin tests to check whether x is prime.
-// If x is prime, it returns true.
-// If x is not prime, it returns false with probability at least 1 - ¼ⁿ.
-//
-// It is not suitable for judging primes that an adversary may have crafted
-// to fool this test.
-func (n nat) probablyPrime(reps int) bool {
-	if len(n) == 0 {
-		return false
-	}
-
-	if len(n) == 1 {
-		if n[0] < 2 {
-			return false
-		}
-
-		if n[0]%2 == 0 {
-			return n[0] == 2
-		}
-
-		// We have to exclude these cases because we reject all
-		// multiples of these numbers below.
-		switch n[0] {
-		case 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53:
-			return true
-		}
-	}
-
-	if n[0]&1 == 0 {
-		return false // n is even
-	}
-
-	const primesProduct32 = 0xC0CFD797         // Π {p ∈ primes, 2 < p <= 29}
-	const primesProduct64 = 0xE221F97C30E94E1D // Π {p ∈ primes, 2 < p <= 53}
-
-	var r Word
-	switch _W {
-	case 32:
-		r = n.modW(primesProduct32)
-	case 64:
-		r = n.modW(primesProduct64 & _M)
-	default:
-		panic("Unknown word size")
-	}
-
-	if r%3 == 0 || r%5 == 0 || r%7 == 0 || r%11 == 0 ||
-		r%13 == 0 || r%17 == 0 || r%19 == 0 || r%23 == 0 || r%29 == 0 {
-		return false
-	}
-
-	if _W == 64 && (r%31 == 0 || r%37 == 0 || r%41 == 0 ||
-		r%43 == 0 || r%47 == 0 || r%53 == 0) {
-		return false
-	}
-
-	nm1 := nat(nil).sub(n, natOne)
-	// determine q, k such that nm1 = q << k
-	k := nm1.trailingZeroBits()
-	q := nat(nil).shr(nm1, k)
-
-	nm3 := nat(nil).sub(nm1, natTwo)
-	rand := rand.New(rand.NewSource(int64(n[0])))
-
-	var x, y, quotient nat
-	nm3Len := nm3.bitLen()
-
-NextRandom:
-	for i := 0; i < reps; i++ {
-		x = x.random(rand, nm3, nm3Len)
-		x = x.add(x, natTwo)
-		y = y.expNN(x, q, n)
-		if y.cmp(natOne) == 0 || y.cmp(nm1) == 0 {
-			continue
-		}
-		for j := uint(1); j < k; j++ {
-			y = y.mul(y, y)
-			quotient, y = quotient.div(y, y, n)
-			if y.cmp(nm1) == 0 {
-				continue NextRandom
-			}
-			if y.cmp(natOne) == 0 {
-				return false
-			}
-		}
-		return false
-	}
-
-	return true
-}
-
 // bytes writes the value of z into buf using big-endian encoding.
 // len(buf) must be >= len(z)*_S. The value of z is encoded in the
 // slice buf[i:]. The number i of unused bytes at the beginning of
@@ -1303,3 +1223,37 @@ func (z nat) setBytes(buf []byte) nat {
 
 	return z.norm()
 }
+
+// sqrt sets z = ⌊√x⌋
+func (z nat) sqrt(x nat) nat {
+	if x.cmp(natOne) <= 0 {
+		return z.set(x)
+	}
+	if alias(z, x) {
+		z = nil
+	}
+
+	// Start with value known to be too large and repeat "z = ⌊(z + ⌊x/z⌋)/2⌋" until it stops getting smaller.
+	// See Brent and Zimmermann, Modern Computer Arithmetic, Algorithm 1.13 (SqrtInt).
+	// https://members.loria.fr/PZimmermann/mca/pub226.html
+	// If x is one less than a perfect square, the sequence oscillates between the correct z and z+1;
+	// otherwise it converges to the correct z and stays there.
+	var z1, z2 nat
+	z1 = z
+	z1 = z1.setUint64(1)
+	z1 = z1.shl(z1, uint(x.bitLen()/2+1)) // must be ≥ √x
+	for n := 0; ; n++ {
+		z2, _ = z2.div(nil, x, z1)
+		z2 = z2.add(z2, z1)
+		z2 = z2.shr(z2, 1)
+		if z2.cmp(z1) >= 0 {
+			// z1 is answer.
+			// Figure out whether z1 or z2 is currently aliased to z by looking at loop count.
+			if n&1 == 0 {
+				return z1
+			}
+			return z.set(z1)
+		}
+		z1, z2 = z2, z1
+	}
+}
diff --git a/src/math/big/natconv_test.go b/src/math/big/natconv_test.go
index 79901d1..bdb60e6 100644
--- a/src/math/big/natconv_test.go
+++ b/src/math/big/natconv_test.go
@@ -278,6 +278,9 @@ func BenchmarkScan(b *testing.B) {
 	const x = 10
 	for _, base := range []int{2, 8, 10, 16} {
 		for _, y := range []Word{10, 100, 1000, 10000, 100000} {
+			if isRaceBuilder && y > 1000 {
+				continue
+			}
 			b.Run(fmt.Sprintf("%d/Base%d", y, base), func(b *testing.B) {
 				b.StopTimer()
 				var z nat
@@ -301,6 +304,9 @@ func BenchmarkString(b *testing.B) {
 	const x = 10
 	for _, base := range []int{2, 8, 10, 16} {
 		for _, y := range []Word{10, 100, 1000, 10000, 100000} {
+			if isRaceBuilder && y > 1000 {
+				continue
+			}
 			b.Run(fmt.Sprintf("%d/Base%d", y, base), func(b *testing.B) {
 				b.StopTimer()
 				var z nat
diff --git a/src/math/big/prime.go b/src/math/big/prime.go
new file mode 100644
index 0000000..3e9690e
--- /dev/null
+++ b/src/math/big/prime.go
@@ -0,0 +1,320 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package big
+
+import "math/rand"
+
+// ProbablyPrime reports whether x is probably prime,
+// applying the Miller-Rabin test with n pseudorandomly chosen bases
+// as well as a Baillie-PSW test.
+//
+// If x is prime, ProbablyPrime returns true.
+// If x is chosen randomly and not prime, ProbablyPrime probably returns false.
+// The probability of returning true for a randomly chosen non-prime is at most ¼ⁿ.
+//
+// ProbablyPrime is 100% accurate for inputs less than 2⁶⁴.
+// See Menezes et al., Handbook of Applied Cryptography, 1997, pp. 145-149,
+// and FIPS 186-4 Appendix F for further discussion of the error probabilities.
+//
+// ProbablyPrime is not suitable for judging primes that an adversary may
+// have crafted to fool the test.
+//
+// As of Go 1.8, ProbablyPrime(0) is allowed and applies only a Baillie-PSW test.
+// Before Go 1.8, ProbablyPrime applied only the Miller-Rabin tests, and ProbablyPrime(0) panicked.
+func (x *Int) ProbablyPrime(n int) bool {
+	// Note regarding the doc comment above:
+	// It would be more precise to say that the Baillie-PSW test uses the
+	// extra strong Lucas test as its Lucas test, but since no one knows
+	// how to tell any of the Lucas tests apart inside a Baillie-PSW test
+	// (they all work equally well empirically), that detail need not be
+	// documented or implicitly guaranteed.
+	// The comment does avoid saying "the" Baillie-PSW test
+	// because of this general ambiguity.
+
+	if n < 0 {
+		panic("negative n for ProbablyPrime")
+	}
+	if x.neg || len(x.abs) == 0 {
+		return false
+	}
+
+	// primeBitMask records the primes < 64.
+	const primeBitMask uint64 = 1<<2 | 1<<3 | 1<<5 | 1<<7 |
+		1<<11 | 1<<13 | 1<<17 | 1<<19 | 1<<23 | 1<<29 | 1<<31 |
+		1<<37 | 1<<41 | 1<<43 | 1<<47 | 1<<53 | 1<<59 | 1<<61
+
+	w := x.abs[0]
+	if len(x.abs) == 1 && w < 64 {
+		return primeBitMask&(1<<w) != 0
+	}
+
+	if w&1 == 0 {
+		return false // n is even
+	}
+
+	const primesA = 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23 * 37
+	const primesB = 29 * 31 * 41 * 43 * 47 * 53
+
+	var rA, rB uint32
+	switch _W {
+	case 32:
+		rA = uint32(x.abs.modW(primesA))
+		rB = uint32(x.abs.modW(primesB))
+	case 64:
+		r := x.abs.modW((primesA * primesB) & _M)
+		rA = uint32(r % primesA)
+		rB = uint32(r % primesB)
+	default:
+		panic("math/big: invalid word size")
+	}
+
+	if rA%3 == 0 || rA%5 == 0 || rA%7 == 0 || rA%11 == 0 || rA%13 == 0 || rA%17 == 0 || rA%19 == 0 || rA%23 == 0 || rA%37 == 0 ||
+		rB%29 == 0 || rB%31 == 0 || rB%41 == 0 || rB%43 == 0 || rB%47 == 0 || rB%53 == 0 {
+		return false
+	}
+
+	return x.abs.probablyPrimeMillerRabin(n+1, true) && x.abs.probablyPrimeLucas()
+}
+
+// probablyPrimeMillerRabin reports whether n passes reps rounds of the
+// Miller-Rabin primality test, using pseudo-randomly chosen bases.
+// If force2 is true, one of the rounds is forced to use base 2.
+// See Handbook of Applied Cryptography, p. 139, Algorithm 4.24.
+// The number n is known to be non-zero.
+func (n nat) probablyPrimeMillerRabin(reps int, force2 bool) bool {
+	nm1 := nat(nil).sub(n, natOne)
+	// determine q, k such that nm1 = q << k
+	k := nm1.trailingZeroBits()
+	q := nat(nil).shr(nm1, k)
+
+	nm3 := nat(nil).sub(nm1, natTwo)
+	rand := rand.New(rand.NewSource(int64(n[0])))
+
+	var x, y, quotient nat
+	nm3Len := nm3.bitLen()
+
+NextRandom:
+	for i := 0; i < reps; i++ {
+		if i == reps-1 && force2 {
+			x = x.set(natTwo)
+		} else {
+			x = x.random(rand, nm3, nm3Len)
+			x = x.add(x, natTwo)
+		}
+		y = y.expNN(x, q, n)
+		if y.cmp(natOne) == 0 || y.cmp(nm1) == 0 {
+			continue
+		}
+		for j := uint(1); j < k; j++ {
+			y = y.mul(y, y)
+			quotient, y = quotient.div(y, y, n)
+			if y.cmp(nm1) == 0 {
+				continue NextRandom
+			}
+			if y.cmp(natOne) == 0 {
+				return false
+			}
+		}
+		return false
+	}
+
+	return true
+}
+
+// probablyPrimeLucas reports whether n passes the "almost extra strong" Lucas probable prime test,
+// using Baillie-OEIS parameter selection. This corresponds to "AESLPSP" on Jacobsen's tables (link below).
+// The combination of this test and a Miller-Rabin/Fermat test with base 2 gives a Baillie-PSW test.
+//
+// References:
+//
+// Baillie and Wagstaff, "Lucas Pseudoprimes", Mathematics of Computation 35(152),
+// October 1980, pp. 1391-1417, especially page 1401.
+// http://www.ams.org/journals/mcom/1980-35-152/S0025-5718-1980-0583518-6/S0025-5718-1980-0583518-6.pdf
+//
+// Grantham, "Frobenius Pseudoprimes", Mathematics of Computation 70(234),
+// March 2000, pp. 873-891.
+// http://www.ams.org/journals/mcom/2001-70-234/S0025-5718-00-01197-2/S0025-5718-00-01197-2.pdf
+//
+// Baillie, "Extra strong Lucas pseudoprimes", OEIS A217719, https://oeis.org/A217719.
+//
+// Jacobsen, "Pseudoprime Statistics, Tables, and Data", http://ntheory.org/pseudoprimes.html.
+//
+// Nicely, "The Baillie-PSW Primality Test", http://www.trnicely.net/misc/bpsw.html.
+// (Note that Nicely's definition of the "extra strong" test gives the wrong Jacobi condition,
+// as pointed out by Jacobsen.)
+//
+// Crandall and Pomerance, Prime Numbers: A Computational Perspective, 2nd ed.
+// Springer, 2005.
+func (n nat) probablyPrimeLucas() bool {
+	// Discard 0, 1.
+	if len(n) == 0 || n.cmp(natOne) == 0 {
+		return false
+	}
+	// Two is the only even prime.
+	// Already checked by caller, but here to allow testing in isolation.
+	if n[0]&1 == 0 {
+		return n.cmp(natTwo) == 0
+	}
+
+	// Baillie-OEIS "method C" for choosing D, P, Q,
+	// as in https://oeis.org/A217719/a217719.txt:
+	// try increasing P ≥ 3 such that D = P² - 4 (so Q = 1)
+	// until Jacobi(D, n) = -1.
+	// The search is expected to succeed for non-square n after just a few trials.
+	// After more than expected failures, check whether n is square
+	// (which would cause Jacobi(D, n) = 1 for all D not dividing n).
+	p := Word(3)
+	d := nat{1}
+	t1 := nat(nil) // temp
+	intD := &Int{abs: d}
+	intN := &Int{abs: n}
+	for ; ; p++ {
+		if p > 10000 {
+			// This is widely believed to be impossible.
+			// If we get a report, we'll want the exact number n.
+			panic("math/big: internal error: cannot find (D/n) = -1 for " + intN.String())
+		}
+		d[0] = p*p - 4
+		j := Jacobi(intD, intN)
+		if j == -1 {
+			break
+		}
+		if j == 0 {
+			// d = p²-4 = (p-2)(p+2).
+			// If (d/n) == 0 then d shares a prime factor with n.
+			// Since the loop proceeds in increasing p and starts with p-2==1,
+			// the shared prime factor must be p+2.
+			// If p+2 == n, then n is prime; otherwise p+2 is a proper factor of n.
+			return len(n) == 1 && n[0] == p+2
+		}
+		if p == 40 {
+			// We'll never find (d/n) = -1 if n is a square.
+			// If n is a non-square we expect to find a d in just a few attempts on average.
+			// After 40 attempts, take a moment to check if n is indeed a square.
+			t1 = t1.sqrt(n)
+			t1 = t1.mul(t1, t1)
+			if t1.cmp(n) == 0 {
+				return false
+			}
+		}
+	}
+
+	// Grantham definition of "extra strong Lucas pseudoprime", after Thm 2.3 on p. 876
+	// (D, P, Q above have become Δ, b, 1):
+	//
+	// Let U_n = U_n(b, 1), V_n = V_n(b, 1), and Δ = b²-4.
+	// An extra strong Lucas pseudoprime to base b is a composite n = 2^r s + Jacobi(Δ, n),
+	// where s is odd and gcd(n, 2*Δ) = 1, such that either (i) U_s ≡ 0 mod n and V_s ≡ ±2 mod n,
+	// or (ii) V_{2^t s} ≡ 0 mod n for some 0 ≤ t < r-1.
+	//
+	// We know gcd(n, Δ) = 1 or else we'd have found Jacobi(d, n) == 0 above.
+	// We know gcd(n, 2) = 1 because n is odd.
+	//
+	// Arrange s = (n - Jacobi(Δ, n)) / 2^r = (n+1) / 2^r.
+	s := nat(nil).add(n, natOne)
+	r := int(s.trailingZeroBits())
+	s = s.shr(s, uint(r))
+	nm2 := nat(nil).sub(n, natTwo) // n-2
+
+	// We apply the "almost extra strong" test, which checks the above conditions
+	// except for U_s ≡ 0 mod n, which allows us to avoid computing any U_k values.
+	// Jacobsen points out that maybe we should just do the full extra strong test:
+	// "It is also possible to recover U_n using Crandall and Pomerance equation 3.13:
+	// U_n = D^-1 (2V_{n+1} - PV_n) allowing us to run the full extra-strong test
+	// at the cost of a single modular inversion. This computation is easy and fast in GMP,
+	// so we can get the full extra-strong test at essentially the same performance as the
+	// almost extra strong test."
+
+	// Compute Lucas sequence V_s(b, 1), where:
+	//
+	//	V(0) = 2
+	//	V(1) = P
+	//	V(k) = P V(k-1) - Q V(k-2).
+	//
+	// (Remember that due to method C above, P = b, Q = 1.)
+	//
+	// In general V(k) = α^k + β^k, where α and β are roots of x² - Px + Q.
+	// Crandall and Pomerance (p.147) observe that for 0 ≤ j ≤ k,
+	//
+	//	V(j+k) = V(j)V(k) - V(k-j).
+	//
+	// So in particular, to quickly double the subscript:
+	//
+	//	V(2k) = V(k)² - 2
+	//	V(2k+1) = V(k) V(k+1) - P
+	//
+	// We can therefore start with k=0 and build up to k=s in log₂(s) steps.
+	natP := nat(nil).setWord(p)
+	vk := nat(nil).setWord(2)
+	vk1 := nat(nil).setWord(p)
+	t2 := nat(nil) // temp
+	for i := int(s.bitLen()); i >= 0; i-- {
+		if s.bit(uint(i)) != 0 {
+			// k' = 2k+1
+			// V(k') = V(2k+1) = V(k) V(k+1) - P.
+			t1 = t1.mul(vk, vk1)
+			t1 = t1.add(t1, n)
+			t1 = t1.sub(t1, natP)
+			t2, vk = t2.div(vk, t1, n)
+			// V(k'+1) = V(2k+2) = V(k+1)² - 2.
+			t1 = t1.mul(vk1, vk1)
+			t1 = t1.add(t1, nm2)
+			t2, vk1 = t2.div(vk1, t1, n)
+		} else {
+			// k' = 2k
+			// V(k'+1) = V(2k+1) = V(k) V(k+1) - P.
+			t1 = t1.mul(vk, vk1)
+			t1 = t1.add(t1, n)
+			t1 = t1.sub(t1, natP)
+			t2, vk1 = t2.div(vk1, t1, n)
+			// V(k') = V(2k) = V(k)² - 2
+			t1 = t1.mul(vk, vk)
+			t1 = t1.add(t1, nm2)
+			t2, vk = t2.div(vk, t1, n)
+		}
+	}
+
+	// Now k=s, so vk = V(s). Check V(s) ≡ ±2 (mod n).
+	if vk.cmp(natTwo) == 0 || vk.cmp(nm2) == 0 {
+		// Check U(s) ≡ 0.
+		// As suggested by Jacobsen, apply Crandall and Pomerance equation 3.13:
+		//
+		//	U(k) = D⁻¹ (2 V(k+1) - P V(k))
+		//
+		// Since we are checking for U(k) == 0 it suffices to check 2 V(k+1) == P V(k) mod n,
+		// or P V(k) - 2 V(k+1) == 0 mod n.
+		t1 := t1.mul(vk, natP)
+		t2 := t2.shl(vk1, 1)
+		if t1.cmp(t2) < 0 {
+			t1, t2 = t2, t1
+		}
+		t1 = t1.sub(t1, t2)
+		t3 := vk1 // steal vk1, no longer needed below
+		vk1 = nil
+		_ = vk1
+		t2, t3 = t2.div(t3, t1, n)
+		if len(t3) == 0 {
+			return true
+		}
+	}
+
+	// Check V(2^t s) ≡ 0 mod n for some 0 ≤ t < r-1.
+	for t := 0; t < r-1; t++ {
+		if len(vk) == 0 { // vk == 0
+			return true
+		}
+		// Optimization: V(k) = 2 is a fixed point for V(k') = V(k)² - 2,
+		// so if V(k) = 2, we can stop: we will never find a future V(k) == 0.
+		if len(vk) == 1 && vk[0] == 2 { // vk == 2
+			return false
+		}
+		// k' = 2k
+		// V(k') = V(2k) = V(k)² - 2
+		t1 = t1.mul(vk, vk)
+		t1 = t1.sub(t1, natTwo)
+		t2, vk = t2.div(vk, t1, n)
+	}
+	return false
+}
diff --git a/src/math/big/prime_test.go b/src/math/big/prime_test.go
new file mode 100644
index 0000000..a2d3d18
--- /dev/null
+++ b/src/math/big/prime_test.go
@@ -0,0 +1,214 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package big
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+	"unicode"
+)
+
+var primes = []string{
+	"2",
+	"3",
+	"5",
+	"7",
+	"11",
+
+	"13756265695458089029",
+	"13496181268022124907",
+	"10953742525620032441",
+	"17908251027575790097",
+
+	// https://golang.org/issue/638
+	"18699199384836356663",
+
+	"98920366548084643601728869055592650835572950932266967461790948584315647051443",
+	"94560208308847015747498523884063394671606671904944666360068158221458669711639",
+
+	// http://primes.utm.edu/lists/small/small3.html
+	"449417999055441493994709297093108513015373787049558499205492347871729927573118262811508386655998299074566974373711472560655026288668094291699357843464363003144674940345912431129144354948751003607115263071543163",
+	"230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593",
+	"5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993",
+	"203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123",
+
+	// ECC primes: http://tools.ietf.org/html/draft-ladd-safecurves-02
+	"3618502788666131106986593281521497120414687020801267626233049500247285301239",                                                                                  // Curve1174: 2^251-9
+	"57896044618658097711785492504343953926634992332820282019728792003956564819949",                                                                                 // Curve25519: 2^255-19
+	"9850501549098619803069760025035903451269934817616361666987073351061430442874302652853566563721228910201656997576599",                                           // E-382: 2^382-105
+	"42307582002575910332922579714097346549017899709713998034217522897561970639123926132812109468141778230245837569601494931472367",                                 // Curve41417: 2^414-17
+	"6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", // E-521: 2^521-1
+}
+
+var composites = []string{
+	"0",
+	"1",
+	"21284175091214687912771199898307297748211672914763848041968395774954376176754",
+	"6084766654921918907427900243509372380954290099172559290432744450051395395951",
+	"84594350493221918389213352992032324280367711247940675652888030554255915464401",
+	"82793403787388584738507275144194252681",
+
+	// Arnault, "Rabin-Miller Primality Test: Composite Numbers Which Pass It",
+	// Mathematics of Computation, 64(209) (January 1995), pp. 335-361.
+	"1195068768795265792518361315725116351898245581", // strong pseudoprime to prime bases 2 through 29
+	// strong pseudoprime to all prime bases up to 200
+	`
+     80383745745363949125707961434194210813883768828755814583748891752229
+      74273765333652186502336163960045457915042023603208766569966760987284
+       0439654082329287387918508691668573282677617710293896977394701670823
+        0428687109997439976544144845341155872450633409279022275296229414984
+         2306881685404326457534018329786111298960644845216191652872597534901`,
+
+	// Extra-strong Lucas pseudoprimes. https://oeis.org/A217719
+	"989",
+	"3239",
+	"5777",
+	"10877",
+	"27971",
+	"29681",
+	"30739",
+	"31631",
+	"39059",
+	"72389",
+	"73919",
+	"75077",
+	"100127",
+	"113573",
+	"125249",
+	"137549",
+	"137801",
+	"153931",
+	"155819",
+	"161027",
+	"162133",
+	"189419",
+	"218321",
+	"231703",
+	"249331",
+	"370229",
+	"429479",
+	"430127",
+	"459191",
+	"473891",
+	"480689",
+	"600059",
+	"621781",
+	"632249",
+	"635627",
+
+	"3673744903",
+	"3281593591",
+	"2385076987",
+	"2738053141",
+	"2009621503",
+	"1502682721",
+	"255866131",
+	"117987841",
+	"587861",
+
+	"6368689",
+	"8725753",
+	"80579735209",
+	"105919633",
+}
+
+func cutSpace(r rune) rune {
+	if unicode.IsSpace(r) {
+		return -1
+	}
+	return r
+}
+
+func TestProbablyPrime(t *testing.T) {
+	nreps := 20
+	if testing.Short() {
+		nreps = 3
+	}
+	for i, s := range primes {
+		p, _ := new(Int).SetString(s, 10)
+		if !p.ProbablyPrime(nreps) || !p.ProbablyPrime(1) || !p.ProbablyPrime(0) {
+			t.Errorf("#%d prime found to be non-prime (%s)", i, s)
+		}
+	}
+
+	for i, s := range composites {
+		s = strings.Map(cutSpace, s)
+		c, _ := new(Int).SetString(s, 10)
+		if c.ProbablyPrime(nreps) || c.ProbablyPrime(1) || c.ProbablyPrime(0) {
+			t.Errorf("#%d composite found to be prime (%s)", i, s)
+		}
+	}
+
+	// check that ProbablyPrime panics if n <= 0
+	c := NewInt(11) // a prime
+	for _, n := range []int{-1, 0, 1} {
+		func() {
+			defer func() {
+				if n < 0 && recover() == nil {
+					t.Fatalf("expected panic from ProbablyPrime(%d)", n)
+				}
+			}()
+			if !c.ProbablyPrime(n) {
+				t.Fatalf("%v should be a prime", c)
+			}
+		}()
+	}
+}
+
+func BenchmarkProbablyPrime(b *testing.B) {
+	p, _ := new(Int).SetString("203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123", 10)
+	for _, n := range []int{0, 1, 5, 10, 20} {
+		b.Run(fmt.Sprintf("n=%d", n), func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				p.ProbablyPrime(n)
+			}
+		})
+	}
+
+	b.Run("Lucas", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			p.abs.probablyPrimeLucas()
+		}
+	})
+	b.Run("MillerRabinBase2", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			p.abs.probablyPrimeMillerRabin(1, true)
+		}
+	})
+}
+
+func TestMillerRabinPseudoprimes(t *testing.T) {
+	testPseudoprimes(t, "probablyPrimeMillerRabin",
+		func(n nat) bool { return n.probablyPrimeMillerRabin(1, true) && !n.probablyPrimeLucas() },
+		// https://oeis.org/A001262
+		[]int{2047, 3277, 4033, 4681, 8321, 15841, 29341, 42799, 49141, 52633, 65281, 74665, 80581, 85489, 88357, 90751})
+}
+
+func TestLucasPseudoprimes(t *testing.T) {
+	testPseudoprimes(t, "probablyPrimeLucas",
+		func(n nat) bool { return n.probablyPrimeLucas() && !n.probablyPrimeMillerRabin(1, true) },
+		// https://oeis.org/A217719
+		[]int{989, 3239, 5777, 10877, 27971, 29681, 30739, 31631, 39059, 72389, 73919, 75077})
+}
+
+func testPseudoprimes(t *testing.T, name string, cond func(nat) bool, want []int) {
+	n := nat{1}
+	for i := 3; i < 100000; i += 2 {
+		n[0] = Word(i)
+		pseudo := cond(n)
+		if pseudo && (len(want) == 0 || i != want[0]) {
+			t.Errorf("%s(%v, base=2) = %v, want false", name, i)
+		} else if !pseudo && len(want) >= 1 && i == want[0] {
+			t.Errorf("%s(%v, base=2) = false, want true", name, i)
+		}
+		if len(want) > 0 && i == want[0] {
+			want = want[1:]
+		}
+	}
+	if len(want) > 0 {
+		t.Fatalf("forgot to test %v", want)
+	}
+}
diff --git a/src/math/big/rat_test.go b/src/math/big/rat_test.go
index 3a06fca..afda686 100644
--- a/src/math/big/rat_test.go
+++ b/src/math/big/rat_test.go
@@ -382,9 +382,9 @@ func TestFloat32Distribution(t *testing.T) {
 		9,
 		11,
 	}
-	var winc, einc = uint64(1), 1 // soak test (~1.5s on x86-64)
-	if testing.Short() {
-		winc, einc = 5, 15 // quick test (~60ms on x86-64)
+	var winc, einc = uint64(5), 15 // quick test (~60ms on x86-64)
+	if *long {
+		winc, einc = uint64(1), 1 // soak test (~1.5s on x86-64)
 	}
 
 	for _, sign := range "+-" {
@@ -430,9 +430,9 @@ func TestFloat64Distribution(t *testing.T) {
 		9,
 		11,
 	}
-	var winc, einc = uint64(1), 1 // soak test (~75s on x86-64)
-	if testing.Short() {
-		winc, einc = 10, 500 // quick test (~12ms on x86-64)
+	var winc, einc = uint64(10), 500 // quick test (~12ms on x86-64)
+	if *long {
+		winc, einc = uint64(1), 1 // soak test (~75s on x86-64)
 	}
 
 	for _, sign := range "+-" {
diff --git a/src/math/big/ratconv.go b/src/math/big/ratconv.go
index ef2b675..a6a401c 100644
--- a/src/math/big/ratconv.go
+++ b/src/math/big/ratconv.go
@@ -18,6 +18,9 @@ func ratTok(ch rune) bool {
 	return strings.ContainsRune("+-/0123456789.eE", ch)
 }
 
+var ratZero Rat
+var _ fmt.Scanner = &ratZero // *Rat must implement fmt.Scanner
+
 // Scan is a support routine for fmt.Scanner. It accepts the formats
 // 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent.
 func (z *Rat) Scan(s fmt.ScanState, ch rune) error {
@@ -36,8 +39,9 @@ func (z *Rat) Scan(s fmt.ScanState, ch rune) error {
 
 // SetString sets z to the value of s and returns z and a boolean indicating
 // success. s can be given as a fraction "a/b" or as a floating-point number
-// optionally followed by an exponent. If the operation failed, the value of
-// z is undefined but the returned value is nil.
+// optionally followed by an exponent. The entire string (not just a prefix)
+// must be valid for success. If the operation failed, the value of z is un-
+// defined but the returned value is nil.
 func (z *Rat) SetString(s string) (*Rat, bool) {
 	if len(s) == 0 {
 		return nil, false
@@ -49,9 +53,13 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
 		if _, ok := z.a.SetString(s[:sep], 0); !ok {
 			return nil, false
 		}
-		s = s[sep+1:]
+		r := strings.NewReader(s[sep+1:])
 		var err error
-		if z.b.abs, _, _, err = z.b.abs.scan(strings.NewReader(s), 0, false); err != nil {
+		if z.b.abs, _, _, err = z.b.abs.scan(r, 0, false); err != nil {
+			return nil, false
+		}
+		// entire string must have been consumed
+		if _, err = r.ReadByte(); err != io.EOF {
 			return nil, false
 		}
 		if len(z.b.abs) == 0 {
diff --git a/src/math/big/ratconv_test.go b/src/math/big/ratconv_test.go
index 35ad6cc..56ac8d7 100644
--- a/src/math/big/ratconv_test.go
+++ b/src/math/big/ratconv_test.go
@@ -50,6 +50,10 @@ var setStringTests = []StringTest{
 	{"204211327800791583.81095", "4084226556015831676219/20000", true},
 	{"0e9999999999", "0", true}, // issue #16176
 	{in: "1/0"},
+	{in: "4/3/2"}, // issue 17001
+	{in: "4/3/"},
+	{in: "4/3."},
+	{in: "4/"},
 }
 
 // These are not supported by fmt.Fscanf.
@@ -59,6 +63,7 @@ var setStringTests2 = []StringTest{
 	{"-010.", "-10", true},
 	{"0x10/0x20", "1/2", true},
 	{"0b1000/3", "8/3", true},
+	{in: "4/3x"},
 	// TODO(gri) add more tests
 }
 
@@ -139,7 +144,7 @@ func TestFloatString(t *testing.T) {
 }
 
 // Test inputs to Rat.SetString. The prefix "long:" causes the test
-// to be skipped in --test.short mode.  (The threshold is about 500us.)
+// to be skipped except in -long mode.  (The threshold is about 500us.)
 var float64inputs = []string{
 	// Constants plundered from strconv/testfp.txt.
 
@@ -345,7 +350,7 @@ func isFinite(f float64) bool {
 func TestFloat32SpecialCases(t *testing.T) {
 	for _, input := range float64inputs {
 		if strings.HasPrefix(input, "long:") {
-			if testing.Short() {
+			if !*long {
 				continue
 			}
 			input = input[len("long:"):]
@@ -401,7 +406,7 @@ func TestFloat32SpecialCases(t *testing.T) {
 func TestFloat64SpecialCases(t *testing.T) {
 	for _, input := range float64inputs {
 		if strings.HasPrefix(input, "long:") {
-			if testing.Short() {
+			if !*long {
 				continue
 			}
 			input = input[len("long:"):]
diff --git a/src/math/cmplx/cmath_test.go b/src/math/cmplx/cmath_test.go
index d904be8..7a5c485 100644
--- a/src/math/cmplx/cmath_test.go
+++ b/src/math/cmplx/cmath_test.go
@@ -759,6 +759,14 @@ func TestTanh(t *testing.T) {
 	}
 }
 
+// See issue 17577
+func TestInfiniteLoopIntanSeries(t *testing.T) {
+	want := Inf()
+	if got := Cot(0); got != want {
+		t.Errorf("Cot(0): got %g, want %g", got, want)
+	}
+}
+
 func BenchmarkAbs(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		Abs(complex(2.5, 3.5))
diff --git a/src/math/cmplx/example_test.go b/src/math/cmplx/example_test.go
new file mode 100644
index 0000000..f0ed963
--- /dev/null
+++ b/src/math/cmplx/example_test.go
@@ -0,0 +1,28 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cmplx_test
+
+import (
+	"fmt"
+	"math"
+	"math/cmplx"
+)
+
+func ExampleAbs() {
+	fmt.Printf("%.1f", cmplx.Abs(3+4i))
+	// Output: 5.0
+}
+
+// ExampleExp computes Euler's identity.
+func ExampleExp() {
+	fmt.Printf("%.1f", cmplx.Exp(1i*math.Pi)+1)
+	// Output: (0.0+0.0i)
+}
+
+func ExamplePolar() {
+	r, theta := cmplx.Polar(2i)
+	fmt.Printf("r: %.1f, θ: %.1f*π", r, theta/math.Pi)
+	// Output: r: 2.0, θ: 0.5*π
+}
diff --git a/src/math/cmplx/tan.go b/src/math/cmplx/tan.go
index 9485315..2990552 100644
--- a/src/math/cmplx/tan.go
+++ b/src/math/cmplx/tan.go
@@ -120,9 +120,9 @@ func tanSeries(z complex128) float64 {
 	rn := 0.0
 	d := 0.0
 	for {
-		rn += 1
+		rn++
 		f *= rn
-		rn += 1
+		rn++
 		f *= rn
 		x2 *= x
 		y2 *= y
@@ -130,16 +130,18 @@ func tanSeries(z complex128) float64 {
 		t /= f
 		d += t
 
-		rn += 1
+		rn++
 		f *= rn
-		rn += 1
+		rn++
 		f *= rn
 		x2 *= x
 		y2 *= y
 		t = y2 - x2
 		t /= f
 		d += t
-		if math.Abs(t/d) <= MACHEP {
+		if !(math.Abs(t/d) > MACHEP) {
+			// Caution: Use ! and > instead of <= for correct behavior if t/d is NaN.
+			// See issue 17577.
 			break
 		}
 	}
diff --git a/src/math/cosh_s390x.s b/src/math/cosh_s390x.s
new file mode 100644
index 0000000..d061bd0
--- /dev/null
+++ b/src/math/cosh_s390x.s
@@ -0,0 +1,227 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// Constants
+DATA coshrodataL23<>+0(SB)/8, $0.231904681384629956E-16
+DATA coshrodataL23<>+8(SB)/8, $0.693147180559945286E+00
+DATA coshrodataL23<>+16(SB)/8, $0.144269504088896339E+01
+DATA coshrodataL23<>+24(SB)/8, $704.E0
+GLOBL coshrodataL23<>+0(SB), RODATA, $32
+DATA coshxinf<>+0(SB)/8, $0x7FF0000000000000
+GLOBL coshxinf<>+0(SB), RODATA, $8
+DATA coshxlim1<>+0(SB)/8, $800.E0
+GLOBL coshxlim1<>+0(SB), RODATA, $8
+DATA coshxaddhy<>+0(SB)/8, $0xc2f0000100003fdf
+GLOBL coshxaddhy<>+0(SB), RODATA, $8
+DATA coshx4ff<>+0(SB)/8, $0x4ff0000000000000
+GLOBL coshx4ff<>+0(SB), RODATA, $8
+DATA coshe1<>+0(SB)/8, $0x3ff000000000000a
+GLOBL coshe1<>+0(SB), RODATA, $8
+
+// Log multiplier table
+DATA coshtab<>+0(SB)/8, $0.442737824274138381E-01
+DATA coshtab<>+8(SB)/8, $0.263602189790660309E-01
+DATA coshtab<>+16(SB)/8, $0.122565642281703586E-01
+DATA coshtab<>+24(SB)/8, $0.143757052860721398E-02
+DATA coshtab<>+32(SB)/8, $-.651375034121276075E-02
+DATA coshtab<>+40(SB)/8, $-.119317678849450159E-01
+DATA coshtab<>+48(SB)/8, $-.150868749549871069E-01
+DATA coshtab<>+56(SB)/8, $-.161992609578469234E-01
+DATA coshtab<>+64(SB)/8, $-.154492360403337917E-01
+DATA coshtab<>+72(SB)/8, $-.129850717389178721E-01
+DATA coshtab<>+80(SB)/8, $-.892902649276657891E-02
+DATA coshtab<>+88(SB)/8, $-.338202636596794887E-02
+DATA coshtab<>+96(SB)/8, $0.357266307045684762E-02
+DATA coshtab<>+104(SB)/8, $0.118665304327406698E-01
+DATA coshtab<>+112(SB)/8, $0.214434994118118914E-01
+DATA coshtab<>+120(SB)/8, $0.322580645161290314E-01
+GLOBL coshtab<>+0(SB), RODATA, $128
+
+// Minimax polynomial approximations
+DATA coshe2<>+0(SB)/8, $0.500000000000004237e+00
+GLOBL coshe2<>+0(SB), RODATA, $8
+DATA coshe3<>+0(SB)/8, $0.166666666630345592e+00
+GLOBL coshe3<>+0(SB), RODATA, $8
+DATA coshe4<>+0(SB)/8, $0.416666664838056960e-01
+GLOBL coshe4<>+0(SB), RODATA, $8
+DATA coshe5<>+0(SB)/8, $0.833349307718286047e-02
+GLOBL coshe5<>+0(SB), RODATA, $8
+DATA coshe6<>+0(SB)/8, $0.138926439368309441e-02
+GLOBL coshe6<>+0(SB), RODATA, $8
+
+// Cosh returns the hyperbolic cosine of x.
+//
+// Special cases are:
+//      Cosh(±0) = 1
+//      Cosh(±Inf) = +Inf
+//      Cosh(NaN) = NaN
+// The algorithm used is minimax polynomial approximation
+// with coefficients determined with a Remez exchange algorithm.
+
+TEXT ·coshAsm(SB),NOSPLIT,$0-16
+	FMOVD   x+0(FP), F0
+	MOVD    $coshrodataL23<>+0(SB), R9
+	WORD    $0xB3120000     //ltdbr %f0,%f0
+	MOVD    $0x4086000000000000, R2
+	MOVD    $0x4086000000000000, R3
+	BLTU    L19
+	FMOVD   F0, F4
+L2:
+	WORD    $0xED409018     //cdb %f4,.L24-.L23(%r9)
+	BYTE    $0x00
+	BYTE    $0x19
+	BGE     L14     //jnl   .L14
+	BVS     L14
+	WFCEDBS V4, V4, V2
+	BEQ     L20
+L1:
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L14:
+	WFCEDBS V4, V4, V2
+	BVS     L1
+	MOVD    $coshxlim1<>+0(SB), R1
+	FMOVD   0(R1), F2
+	WFCHEDBS        V4, V2, V2
+	BEQ     L21
+	MOVD    $coshxaddhy<>+0(SB), R1
+	FMOVD   coshrodataL23<>+16(SB), F5
+	FMOVD   0(R1), F2
+	WFMSDB  V0, V5, V2, V5
+	FMOVD   coshrodataL23<>+8(SB), F3
+	FADD    F5, F2
+	MOVD    $coshe6<>+0(SB), R1
+	WFMSDB  V2, V3, V0, V3
+	FMOVD   0(R1), F6
+	WFMDB   V3, V3, V1
+	MOVD    $coshe4<>+0(SB), R1
+	FMOVD   coshrodataL23<>+0(SB), F7
+	WFMADB  V2, V7, V3, V2
+	FMOVD   0(R1), F3
+	MOVD    $coshe5<>+0(SB), R1
+	WFMADB  V1, V6, V3, V6
+	FMOVD   0(R1), F7
+	MOVD    $coshe3<>+0(SB), R1
+	FMOVD   0(R1), F3
+	WFMADB  V1, V7, V3, V7
+	FNEG    F2, F3
+	WORD    $0xB3CD0015     //lgdr %r1,%f5
+	MOVD    $coshe2<>+0(SB), R3
+	WFCEDBS V4, V0, V0
+	FMOVD   0(R3), F5
+	MOVD    $coshe1<>+0(SB), R3
+	WFMADB  V1, V6, V5, V6
+	FMOVD   0(R3), F5
+	WORD    $0xEC21000F     //risbgn %r2,%r1,64-64+0,64-64+0+16-1,64-0-16
+	BYTE    $0x30
+	BYTE    $0x59
+	WFMADB  V1, V7, V5, V1
+	BVS     L22
+	WORD    $0xEC4139BC     //risbg %r4,%r1,57,128+60,3
+	BYTE    $0x03
+	BYTE    $0x55
+	MOVD    $coshtab<>+0(SB), R3
+	WFMADB  V3, V6, V1, V6
+	WORD    $0x68043000     //ld    %f0,0(%r4,%r3)
+	FMSUB   F0, F3, F2, F2
+	WORD    $0xA71AF000     //ahi   %r1,-4096
+	WFMADB  V2, V6, V0, V6
+L17:
+	WORD    $0xEC21000F     //risbgn %r2,%r1,64-64+0,64-64+0+16-1,64-0-16
+	BYTE    $0x30
+	BYTE    $0x59
+	WORD    $0xB3C10022     //ldgr %f2,%r2
+	FMADD   F2, F6, F2, F2
+	MOVD    $coshx4ff<>+0(SB), R1
+	FMOVD   0(R1), F0
+	FMUL    F2, F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L19:
+	FNEG    F0, F4
+	BR      L2
+L20:
+	MOVD    $coshxaddhy<>+0(SB), R1
+	FMOVD   coshrodataL23<>+16(SB), F3
+	FMOVD   0(R1), F2
+	WFMSDB  V0, V3, V2, V3
+	FMOVD   coshrodataL23<>+8(SB), F4
+	FADD    F3, F2
+	MOVD    $coshe6<>+0(SB), R1
+	FMSUB   F4, F2, F0, F0
+	FMOVD   0(R1), F6
+	WFMDB   V0, V0, V1
+	MOVD    $coshe4<>+0(SB), R1
+	FMOVD   0(R1), F4
+	MOVD    $coshe5<>+0(SB), R1
+	FMOVD   coshrodataL23<>+0(SB), F5
+	WFMADB  V1, V6, V4, V6
+	FMADD   F5, F2, F0, F0
+	FMOVD   0(R1), F2
+	MOVD    $coshe3<>+0(SB), R1
+	FMOVD   0(R1), F4
+	WFMADB  V1, V2, V4, V2
+	MOVD    $coshe2<>+0(SB), R1
+	FMOVD   0(R1), F5
+	FNEG    F0, F4
+	WFMADB  V1, V6, V5, V6
+	MOVD    $coshe1<>+0(SB), R1
+	FMOVD   0(R1), F5
+	WFMADB  V1, V2, V5, V1
+	WORD    $0xB3CD0013     //lgdr  %r1,%f3
+	MOVD    $coshtab<>+0(SB), R5
+	WFMADB  V4, V6, V1, V3
+	WORD    $0xEC4139BC     //risbg %r4,%r1,57,128+60,3
+	BYTE    $0x03
+	BYTE    $0x55
+	WFMSDB  V4, V6, V1, V6
+	WORD    $0x68145000     //ld %f1,0(%r4,%r5)
+	WFMSDB  V4, V1, V0, V2
+	WORD    $0xA7487FBE     //lhi %r4,32702
+	FMADD   F3, F2, F1, F1
+	SUBW    R1, R4
+	WORD    $0xECC439BC     //risbg %r12,%r4,57,128+60,3
+	BYTE    $0x03
+	BYTE    $0x55
+	WORD    $0x682C5000     //ld %f2,0(%r12,%r5)
+	FMSUB   F2, F4, F0, F0
+	WORD    $0xEC21000F     //risbgn %r2,%r1,64-64+0,64-64+0+16-1,64-0-16
+	BYTE    $0x30
+	BYTE    $0x59
+	WFMADB  V0, V6, V2, V6
+	WORD    $0xEC34000F     //risbgn %r3,%r4,64-64+0,64-64+0+16-1,64-0-16
+	BYTE    $0x30
+	BYTE    $0x59
+	WORD    $0xB3C10022     //ldgr %f2,%r2
+	WORD    $0xB3C10003     //ldgr %f0,%r3
+	FMADD   F2, F1, F2, F2
+	FMADD   F0, F6, F0, F0
+	FADD    F2, F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L22:
+	WORD    $0xA7387FBE     //lhi %r3,32702
+	MOVD    $coshtab<>+0(SB), R4
+	SUBW    R1, R3
+	WFMSDB  V3, V6, V1, V6
+	WORD    $0xEC3339BC     //risbg %r3,%r3,57,128+60,3
+	BYTE    $0x03
+	BYTE    $0x55
+	WORD    $0x68034000     //ld %f0,0(%r3,%r4)
+	FMSUB   F0, F3, F2, F2
+	WORD    $0xA7386FBE     //lhi %r3,28606
+	WFMADB  V2, V6, V0, V6
+	SUBW    R1, R3, R1
+	BR      L17
+L21:
+	MOVD    $coshxinf<>+0(SB), R1
+	FMOVD   0(R1), F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
diff --git a/src/math/dim_arm64.s b/src/math/dim_arm64.s
new file mode 100644
index 0000000..4b6b592
--- /dev/null
+++ b/src/math/dim_arm64.s
@@ -0,0 +1,78 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+#define PosInf 0x7FF0000000000000
+#define NaN    0x7FF8000000000001
+#define NegInf 0xFFF0000000000000
+
+// func Dim(x, y float64) float64
+TEXT ·Dim(SB),NOSPLIT,$0
+	// (+Inf, +Inf) special case
+	MOVD	$PosInf, R0
+	MOVD	x+0(FP), R1
+	MOVD	y+8(FP), R2
+	CMP	R0, R1
+	BNE	dim2
+	CMP	R0, R2
+	BEQ	bothInf
+dim2:	// (-Inf, -Inf) special case
+	MOVD	$NegInf, R0
+	CMP	R0, R1
+	BNE	dim3
+	CMP	R0, R2
+	BEQ	bothInf
+dim3:	// normal case
+	FMOVD	R1, F0
+	FMOVD	R2, F1
+	FMOVD	$0.0, F2
+	FSUBD	F1, F0
+	FMAXD	F0, F2, F0
+	FMOVD	F0, ret+16(FP)
+	RET
+bothInf:
+	MOVD	$NaN, R0
+	MOVD	R0, ret+16(FP)
+	RET
+
+// func ·Max(x, y float64) float64
+TEXT ·Max(SB),NOSPLIT,$0
+	// +Inf special cases
+	MOVD	$PosInf, R0
+	MOVD	x+0(FP), R1
+	CMP	R0, R1
+	BEQ	isPosInf
+	MOVD	y+8(FP), R2
+	CMP	R0, R2
+	BEQ	isPosInf
+	// normal case
+	FMOVD	R1, F0
+	FMOVD	R2, F1
+	FMAXD	F0, F1, F0
+	FMOVD	F0, ret+16(FP)
+	RET
+isPosInf: // return +Inf
+	MOVD	R0, ret+16(FP)
+	RET
+
+// func Min(x, y float64) float64
+TEXT ·Min(SB),NOSPLIT,$0
+	// -Inf special cases
+	MOVD	$NegInf, R0
+	MOVD	x+0(FP), R1
+	CMP	R0, R1
+	BEQ	isNegInf
+	MOVD	y+8(FP), R2
+	CMP	R0, R2
+	BEQ	isNegInf
+	// normal case
+	FMOVD	R1, F0
+	FMOVD	R2, F1
+	FMIND	F0, F1, F0
+	FMOVD	F0, ret+16(FP)
+	RET
+isNegInf: // return -Inf
+	MOVD	R0, ret+16(FP)
+	RET
diff --git a/src/math/exp_386.s b/src/math/exp_386.s
index 18a92ef..9d63295 100644
--- a/src/math/exp_386.s
+++ b/src/math/exp_386.s
@@ -6,36 +6,6 @@
 
 // func Exp(x float64) float64
 TEXT ·Exp(SB),NOSPLIT,$0
-// test bits for not-finite
-	MOVL    x_hi+4(FP), AX
-	ANDL    $0x7ff00000, AX
-	CMPL    AX, $0x7ff00000
-	JEQ     not_finite
-	FLDL2E                // F0=log2(e)
-	FMULD   x+0(FP), F0   // F0=x*log2(e)
-	FMOVD   F0, F1        // F0=x*log2(e), F1=x*log2(e)
-	FRNDINT               // F0=int(x*log2(e)), F1=x*log2(e)
-	FSUBD   F0, F1        // F0=int(x*log2(e)), F1=x*log2(e)-int(x*log2(e))
-	FXCHD   F0, F1        // F0=x*log2(e)-int(x*log2(e)), F1=int(x*log2(e))
-	F2XM1                 // F0=2**(x*log2(e)-int(x*log2(e)))-1, F1=int(x*log2(e))
-	FLD1                  // F0=1, F1=2**(x*log2(e)-int(x*log2(e)))-1, F2=int(x*log2(e))
-	FADDDP  F0, F1        // F0=2**(x*log2(e)-int(x*log2(e))), F1=int(x*log2(e))
-	FSCALE                // F0=e**x, F1=int(x*log2(e))
-	FMOVDP  F0, F1        // F0=e**x
-	FMOVDP  F0, ret+8(FP)
-	RET
-not_finite:
-// test bits for -Inf
-	MOVL    x_hi+4(FP), BX
-	MOVL    x_lo+0(FP), CX
-	CMPL    BX, $0xfff00000
-	JNE     not_neginf
-	CMPL    CX, $0
-	JNE     not_neginf
-	FLDZ                  // F0=0
-	FMOVDP  F0, ret+8(FP)
-	RET
-not_neginf:
-	MOVL    CX, ret_lo+8(FP)
-	MOVL    BX, ret_hi+12(FP)
-	RET
+	// Used to use 387 assembly (FLDL2E+F2XM1) here,
+	// but it was both slower and less accurate than the portable Go code.
+	JMP ·exp(SB)
diff --git a/src/math/expm1.go b/src/math/expm1.go
index 8ce67e5..7dd75a8 100644
--- a/src/math/expm1.go
+++ b/src/math/expm1.go
@@ -229,7 +229,7 @@ func expm1(x float64) float64 {
 		}
 		t := Float64frombits(uint64(0x3ff-k) << 52) // 2**-k
 		y := x - (e + t)
-		y += 1
+		y++
 		y = Float64frombits(Float64bits(y) + uint64(k)<<52) // add k to y's exponent
 		return y
 	}
diff --git a/src/math/export_s390x_test.go b/src/math/export_s390x_test.go
new file mode 100644
index 0000000..3fdbd86
--- /dev/null
+++ b/src/math/export_s390x_test.go
@@ -0,0 +1,14 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package math
+
+// Export internal functions and variable for testing.
+var Log10NoVec = log10
+var CosNoVec = cos
+var CoshNoVec = cosh
+var SinNoVec = sin
+var SinhNoVec = sinh
+var TanhNoVec = tanh
+var HasVX = hasVX
diff --git a/src/math/floor_arm64.s b/src/math/floor_arm64.s
new file mode 100644
index 0000000..6d240d4
--- /dev/null
+++ b/src/math/floor_arm64.s
@@ -0,0 +1,26 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// func Floor(x float64) float64
+TEXT ·Floor(SB),NOSPLIT,$0
+	FMOVD	x+0(FP), F0
+	FRINTMD	F0, F0
+	FMOVD	F0, ret+8(FP)
+	RET
+
+// func Ceil(x float64) float64
+TEXT ·Ceil(SB),NOSPLIT,$0
+	FMOVD	x+0(FP), F0
+	FRINTPD	F0, F0
+	FMOVD	F0, ret+8(FP)
+	RET
+
+// func Trunc(x float64) float64
+TEXT ·Trunc(SB),NOSPLIT,$0
+	FMOVD	x+0(FP), F0
+	FRINTZD	F0, F0
+	FMOVD	F0, ret+8(FP)
+	RET
diff --git a/src/math/floor_ppc64x.s b/src/math/floor_ppc64x.s
new file mode 100644
index 0000000..2ab011d
--- /dev/null
+++ b/src/math/floor_ppc64x.s
@@ -0,0 +1,25 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ppc64 ppc64le
+
+#include "textflag.h"
+
+TEXT ·Floor(SB),NOSPLIT,$0
+	FMOVD   x+0(FP), F0
+	FRIM	F0, F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+TEXT ·Ceil(SB),NOSPLIT,$0
+	FMOVD   x+0(FP), F0
+	FRIP    F0, F0
+	FMOVD	F0, ret+8(FP)
+	RET
+
+TEXT ·Trunc(SB),NOSPLIT,$0
+	FMOVD   x+0(FP), F0
+	FRIZ    F0, F0
+	FMOVD   F0, ret+8(FP)
+	RET
diff --git a/src/math/floor_s390x.s b/src/math/floor_s390x.s
new file mode 100644
index 0000000..896e79b
--- /dev/null
+++ b/src/math/floor_s390x.s
@@ -0,0 +1,26 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// func Floor(x float64) float64
+TEXT ·Floor(SB),NOSPLIT,$0
+	FMOVD	x+0(FP), F0
+	FIDBR	$7, F0, F0
+	FMOVD	F0, ret+8(FP)
+	RET
+
+// func Ceil(x float64) float64
+TEXT ·Ceil(SB),NOSPLIT,$0
+	FMOVD	x+0(FP), F0
+	FIDBR	$6, F0, F0
+	FMOVD	F0, ret+8(FP)
+	RET
+
+// func Trunc(x float64) float64
+TEXT ·Trunc(SB),NOSPLIT,$0
+	FMOVD	x+0(FP), F0
+	FIDBR	$5, F0, F0
+	FMOVD	F0, ret+8(FP)
+	RET
diff --git a/src/math/gamma.go b/src/math/gamma.go
index 841ec11..cc9e869 100644
--- a/src/math/gamma.go
+++ b/src/math/gamma.go
@@ -91,23 +91,31 @@ var _gamS = [...]float64{
 }
 
 // Gamma function computed by Stirling's formula.
-// The polynomial is valid for 33 <= x <= 172.
-func stirling(x float64) float64 {
+// The pair of results must be multiplied together to get the actual answer.
+// The multiplication is left to the caller so that, if careful, the caller can avoid
+// infinity for 172 <= x <= 180.
+// The polynomial is valid for 33 <= x <= 172; larger values are only used
+// in reciprocal and produce denormalized floats. The lower precision there
+// masks any imprecision in the polynomial.
+func stirling(x float64) (float64, float64) {
+	if x > 200 {
+		return Inf(1), 1
+	}
 	const (
 		SqrtTwoPi   = 2.506628274631000502417
 		MaxStirling = 143.01608
 	)
 	w := 1 / x
 	w = 1 + w*((((_gamS[0]*w+_gamS[1])*w+_gamS[2])*w+_gamS[3])*w+_gamS[4])
-	y := Exp(x)
+	y1 := Exp(x)
+	y2 := 1.0
 	if x > MaxStirling { // avoid Pow() overflow
 		v := Pow(x, 0.5*x-0.25)
-		y = v * (v / y)
+		y1, y2 = v, v/y1
 	} else {
-		y = Pow(x, x-0.5) / y
+		y1 = Pow(x, x-0.5) / y1
 	}
-	y = SqrtTwoPi * y * w
-	return y
+	return y1, SqrtTwoPi * w * y2
 }
 
 // Gamma returns the Gamma function of x.
@@ -125,22 +133,26 @@ func Gamma(x float64) float64 {
 	switch {
 	case isNegInt(x) || IsInf(x, -1) || IsNaN(x):
 		return NaN()
+	case IsInf(x, 1):
+		return Inf(1)
 	case x == 0:
 		if Signbit(x) {
 			return Inf(-1)
 		}
 		return Inf(1)
-	case x < -170.5674972726612 || x > 171.61447887182298:
-		return Inf(1)
 	}
 	q := Abs(x)
 	p := Floor(q)
 	if q > 33 {
 		if x >= 0 {
-			return stirling(x)
+			y1, y2 := stirling(x)
+			return y1 * y2
 		}
+		// Note: x is negative but (checked above) not a negative integer,
+		// so x must be small enough to be in range for conversion to int64.
+		// If |x| were >= 2⁶³ it would have to be an integer.
 		signgam := 1
-		if ip := int(p); ip&1 == 0 {
+		if ip := int64(p); ip&1 == 0 {
 			signgam = -1
 		}
 		z := q - p
@@ -152,7 +164,14 @@ func Gamma(x float64) float64 {
 		if z == 0 {
 			return Inf(signgam)
 		}
-		z = Pi / (Abs(z) * stirling(q))
+		sq1, sq2 := stirling(q)
+		absz := Abs(z)
+		d := absz * sq1 * sq2
+		if IsInf(d, 0) {
+			z = Pi / absz / sq1 / sq2
+		} else {
+			z = Pi / d
+		}
 		return float64(signgam) * z
 	}
 
diff --git a/src/math/j0.go b/src/math/j0.go
index cbef7aa..fe26791 100644
--- a/src/math/j0.go
+++ b/src/math/j0.go
@@ -305,20 +305,20 @@ var p0S2 = [5]float64{
 }
 
 func pzero(x float64) float64 {
-	var p [6]float64
-	var q [5]float64
+	var p *[6]float64
+	var q *[5]float64
 	if x >= 8 {
-		p = p0R8
-		q = p0S8
+		p = &p0R8
+		q = &p0S8
 	} else if x >= 4.5454 {
-		p = p0R5
-		q = p0S5
+		p = &p0R5
+		q = &p0S5
 	} else if x >= 2.8571 {
-		p = p0R3
-		q = p0S3
+		p = &p0R3
+		q = &p0S3
 	} else if x >= 2 {
-		p = p0R2
-		q = p0S2
+		p = &p0R2
+		q = &p0S2
 	}
 	z := 1 / (x * x)
 	r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))))
@@ -408,19 +408,19 @@ var q0S2 = [6]float64{
 }
 
 func qzero(x float64) float64 {
-	var p, q [6]float64
+	var p, q *[6]float64
 	if x >= 8 {
-		p = q0R8
-		q = q0S8
+		p = &q0R8
+		q = &q0S8
 	} else if x >= 4.5454 {
-		p = q0R5
-		q = q0S5
+		p = &q0R5
+		q = &q0S5
 	} else if x >= 2.8571 {
-		p = q0R3
-		q = q0S3
+		p = &q0R3
+		q = &q0S3
 	} else if x >= 2 {
-		p = q0R2
-		q = q0S2
+		p = &q0R2
+		q = &q0S2
 	}
 	z := 1 / (x * x)
 	r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))))
diff --git a/src/math/j1.go b/src/math/j1.go
index d359d90..f1adcb6 100644
--- a/src/math/j1.go
+++ b/src/math/j1.go
@@ -298,20 +298,20 @@ var p1S2 = [5]float64{
 }
 
 func pone(x float64) float64 {
-	var p [6]float64
-	var q [5]float64
+	var p *[6]float64
+	var q *[5]float64
 	if x >= 8 {
-		p = p1R8
-		q = p1S8
+		p = &p1R8
+		q = &p1S8
 	} else if x >= 4.5454 {
-		p = p1R5
-		q = p1S5
+		p = &p1R5
+		q = &p1S5
 	} else if x >= 2.8571 {
-		p = p1R3
-		q = p1S3
+		p = &p1R3
+		q = &p1S3
 	} else if x >= 2 {
-		p = p1R2
-		q = p1S2
+		p = &p1R2
+		q = &p1S2
 	}
 	z := 1 / (x * x)
 	r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))))
@@ -401,19 +401,19 @@ var q1S2 = [6]float64{
 }
 
 func qone(x float64) float64 {
-	var p, q [6]float64
+	var p, q *[6]float64
 	if x >= 8 {
-		p = q1R8
-		q = q1S8
+		p = &q1R8
+		q = &q1S8
 	} else if x >= 4.5454 {
-		p = q1R5
-		q = q1S5
+		p = &q1R5
+		q = &q1S5
 	} else if x >= 2.8571 {
-		p = q1R3
-		q = q1S3
+		p = &q1R3
+		q = &q1S3
 	} else if x >= 2 {
-		p = q1R2
-		q = q1S2
+		p = &q1R2
+		q = &q1S2
 	}
 	z := 1 / (x * x)
 	r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5]))))
diff --git a/src/math/jn.go b/src/math/jn.go
index 721112f..3422782 100644
--- a/src/math/jn.go
+++ b/src/math/jn.go
@@ -174,7 +174,7 @@ func Jn(n int, x float64) float64 {
 			q1 := w*z - 1
 			k := 1
 			for q1 < 1e9 {
-				k += 1
+				k++
 				z += h
 				q0, q1 = q1, z*q1-q0
 			}
diff --git a/src/math/log10_s390x.s b/src/math/log10_s390x.s
new file mode 100644
index 0000000..460bcd9
--- /dev/null
+++ b/src/math/log10_s390x.s
@@ -0,0 +1,170 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// Minimax polynomial coefficients and other constants
+DATA log10rodataL19<>+0(SB)/8, $0.000000000000000000E+00
+DATA log10rodataL19<>+8(SB)/8, $-1.0
+DATA log10rodataL19<>+16(SB)/8, $0x7FF8000000000000   //+NanN
+DATA log10rodataL19<>+24(SB)/8, $.15375570329280596749
+DATA log10rodataL19<>+32(SB)/8, $.60171950900703668594E+04
+DATA log10rodataL19<>+40(SB)/8, $-1.9578460454940795898
+DATA log10rodataL19<>+48(SB)/8, $0.78962633073318517310E-01
+DATA log10rodataL19<>+56(SB)/8, $-.71784211884836937993E-02
+DATA log10rodataL19<>+64(SB)/8, $0.87011165920689940661E-03
+DATA log10rodataL19<>+72(SB)/8, $-.11865158981621437541E-03
+DATA log10rodataL19<>+80(SB)/8, $0.17258413403018680410E-04
+DATA log10rodataL19<>+88(SB)/8, $0.40752932047883484315E-06
+DATA log10rodataL19<>+96(SB)/8, $-.26149194688832680410E-05
+DATA log10rodataL19<>+104(SB)/8, $0.92453396963875026759E-08
+DATA log10rodataL19<>+112(SB)/8, $-.64572084905921579630E-07
+DATA log10rodataL19<>+120(SB)/8, $-5.5
+DATA log10rodataL19<>+128(SB)/8, $18446744073709551616.
+GLOBL log10rodataL19<>+0(SB), RODATA, $136
+
+// Table of log10 correction terms
+DATA log10tab2074<>+0(SB)/8, $0.254164497922885069E-01
+DATA log10tab2074<>+8(SB)/8, $0.179018857989381839E-01
+DATA log10tab2074<>+16(SB)/8, $0.118926768029048674E-01
+DATA log10tab2074<>+24(SB)/8, $0.722595568238080033E-02
+DATA log10tab2074<>+32(SB)/8, $0.376393570022739135E-02
+DATA log10tab2074<>+40(SB)/8, $0.138901135928814326E-02
+DATA log10tab2074<>+48(SB)/8, $0
+DATA log10tab2074<>+56(SB)/8, $-0.490780466387818203E-03
+DATA log10tab2074<>+64(SB)/8, $-0.159811431402137571E-03
+DATA log10tab2074<>+72(SB)/8, $0.925796337165100494E-03
+DATA log10tab2074<>+80(SB)/8, $0.270683176738357035E-02
+DATA log10tab2074<>+88(SB)/8, $0.513079030821304758E-02
+DATA log10tab2074<>+96(SB)/8, $0.815089785397996303E-02
+DATA log10tab2074<>+104(SB)/8, $0.117253060262419215E-01
+DATA log10tab2074<>+112(SB)/8, $0.158164239345343963E-01
+DATA log10tab2074<>+120(SB)/8, $0.203903595489229786E-01
+GLOBL log10tab2074<>+0(SB), RODATA, $128
+
+// Log10 returns the decimal logarithm of the argument.
+//
+// Special cases are:
+//      Log(+Inf) = +Inf
+//      Log(0) = -Inf
+//      Log(x < 0) = NaN
+//      Log(NaN) = NaN
+// The algorithm used is minimax polynomial approximation
+// with coefficients determined with a Remez exchange algorithm.
+
+TEXT ·log10Asm(SB),NOSPLIT,$8-16
+	FMOVD   x+0(FP), F0
+	MOVD    $log10rodataL19<>+0(SB), R9
+	FMOVD   F0, x-8(SP)
+	WORD    $0xC0298006     //iilf %r2,2147909631
+	BYTE    $0x7F
+	BYTE    $0xFF
+	WORD    $0x5840F008     //l %r4, 8(%r15)
+	SUBW    R4, R2, R3
+	WORD    $0xEC5320AF     //risbg %r5,%r3,32,128+47,0
+	BYTE    $0x00
+	BYTE    $0x55
+	MOVH    $0x0, R1
+	WORD    $0xEC15001F     //risbgn %r1,%r5,64-64+0,64-64+0+32-1,64-0-32
+	BYTE    $0x20
+	BYTE    $0x59
+	WORD    $0xC0590016     //iilf %r5,1507327
+	BYTE    $0xFF
+	BYTE    $0xFF
+	MOVW    R4, R10
+	MOVW    R5, R11
+	CMPBLE  R10, R11, L2
+	WORD    $0xC0297FEF     //iilf %r2,2146435071
+	BYTE    $0xFF
+	BYTE    $0xFF
+	MOVW    R4, R10
+	MOVW    R2, R11
+	CMPBLE  R10, R11, L16
+L3:
+L1:
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L2:
+	WORD    $0xB3120000     //ltdbr %f0,%f0
+	BLEU    L13
+	WORD    $0xED009080     //mdb %f0,.L20-.L19(%r9)
+	BYTE    $0x00
+	BYTE    $0x1C
+	FMOVD   F0, x-8(SP)
+	WORD    $0x5B20F008     //s %r2, 8(%r15)
+	WORD    $0xEC3239BC     //risbg %r3,%r2,57,128+60,64-13
+	BYTE    $0x33
+	BYTE    $0x55
+	ANDW    $0xFFFF0000, R2
+	WORD    $0xEC12001F     //risbgn %r1,%r2,64-64+0,64-64+0+32-1,64-0-32
+	BYTE    $0x20
+	BYTE    $0x59
+	ADDW    $0x4000000, R2
+	BLEU    L17
+L8:
+	SRW     $8, R2, R2
+	ORW     $0x45000000, R2
+L4:
+	FMOVD   log10rodataL19<>+120(SB), F2
+	WORD    $0xB3C10041     //ldgr  %f4,%r1
+	WFMADB  V4, V0, V2, V0
+	FMOVD   log10rodataL19<>+112(SB), F4
+	FMOVD   log10rodataL19<>+104(SB), F6
+	WFMADB  V0, V6, V4, V6
+	FMOVD   log10rodataL19<>+96(SB), F4
+	FMOVD   log10rodataL19<>+88(SB), F1
+	WFMADB  V0, V1, V4, V1
+	WFMDB   V0, V0, V4
+	FMOVD   log10rodataL19<>+80(SB), F2
+	WFMADB  V6, V4, V1, V6
+	FMOVD   log10rodataL19<>+72(SB), F1
+	WFMADB  V0, V2, V1, V2
+	FMOVD   log10rodataL19<>+64(SB), F1
+	WORD    $0xEC3339BC     //risbg %r3,%r3,57,128+60,0
+	BYTE    $0x00
+	BYTE    $0x55
+	WFMADB  V4, V6, V2, V6
+	FMOVD   log10rodataL19<>+56(SB), F2
+	WFMADB  V0, V1, V2, V1
+	VLVGF   $0, R2, V2
+	WFMADB  V4, V6, V1, V4
+	LDEBR   F2, F2
+	FMOVD   log10rodataL19<>+48(SB), F6
+	WFMADB  V0, V4, V6, V4
+	FMOVD   log10rodataL19<>+40(SB), F1
+	FMOVD   log10rodataL19<>+32(SB), F6
+	MOVD    $log10tab2074<>+0(SB), R1
+	WFMADB  V2, V1, V6, V2
+	WORD    $0x68331000     //ld %f3,0(%r3,%r1)
+	WFMADB  V0, V4, V3, V0
+	FMOVD   log10rodataL19<>+24(SB), F4
+	FMADD   F4, F2, F0, F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L16:
+	WORD    $0xEC2328B7     //risbg %r2,%r3,40,128+55,64-8
+	BYTE    $0x38
+	BYTE    $0x55
+	WORD    $0xEC3339BC     //risbg %r3,%r3,57,128+60,64-13
+	BYTE    $0x33
+	BYTE    $0x55
+	ORW     $0x45000000, R2
+	BR      L4
+L13:
+	BGE     L18     //jnl .L18
+	BVS     L18
+	FMOVD   log10rodataL19<>+16(SB), F0
+	BR      L1
+L17:
+	SRAW    $1, R2, R2
+	SUBW    $0x40000000, R2
+	BR      L8
+L18:
+	FMOVD   log10rodataL19<>+8(SB), F0
+	WORD    $0xED009000     //ddb %f0,.L36-.L19(%r9)
+	BYTE    $0x00
+	BYTE    $0x1D
+	BR      L1
diff --git a/src/math/log1p.go b/src/math/log1p.go
index d1bddfb..b128a16 100644
--- a/src/math/log1p.go
+++ b/src/math/log1p.go
@@ -167,7 +167,7 @@ func log1p(x float64) float64 {
 		if iu < 0x0006a09e667f3bcd { // mantissa of Sqrt(2)
 			u = Float64frombits(iu | 0x3ff0000000000000) // normalize u
 		} else {
-			k += 1
+			k++
 			u = Float64frombits(iu | 0x3fe0000000000000) // normalize u/2
 			iu = (0x0010000000000000 - iu) >> 2
 		}
@@ -179,10 +179,9 @@ func log1p(x float64) float64 {
 		if f == 0 {
 			if k == 0 {
 				return 0
-			} else {
-				c += float64(k) * Ln2Lo
-				return float64(k)*Ln2Hi + c
 			}
+			c += float64(k) * Ln2Lo
+			return float64(k)*Ln2Hi + c
 		}
 		R = hfsq * (1.0 - 0.66666666666666666*f) // avoid division
 		if k == 0 {
diff --git a/src/math/modf_386.s b/src/math/modf_386.s
index d9b1eeb..e916073 100644
--- a/src/math/modf_386.s
+++ b/src/math/modf_386.s
@@ -7,16 +7,16 @@
 // func Modf(f float64) (int float64, frac float64)
 TEXT ·Modf(SB),NOSPLIT,$0
 	// special case for f == -0.0
-	MOVL f+4(FP), DX	// high word
-	MOVL f+0(FP), AX	// low word
+	MOVL f_hi+4(FP), DX	// high word
+	MOVL f_lo+0(FP), AX	// low word
 	CMPL DX, $(1<<31)	// beginning of -0.0
 	JNE notNegativeZero
 	CMPL AX, $0			// could be denormalized
 	JNE notNegativeZero
-	MOVL AX, int+8(FP)
-	MOVL DX, int+12(FP)
-	MOVL AX, frac+16(FP)
-	MOVL DX, frac+20(FP)
+	MOVL AX, int_lo+8(FP)
+	MOVL DX, int_hi+12(FP)
+	MOVL AX, frac_lo+16(FP)
+	MOVL DX, frac_hi+20(FP)
 	RET
 notNegativeZero:
 	FMOVD   f+0(FP), F0  // F0=f
diff --git a/src/math/modf_arm64.s b/src/math/modf_arm64.s
new file mode 100644
index 0000000..7c70ef3
--- /dev/null
+++ b/src/math/modf_arm64.s
@@ -0,0 +1,18 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// func Modf(f float64) (int float64, frac float64)
+TEXT ·Modf(SB),NOSPLIT,$0
+	MOVD	f+0(FP), R0
+	FMOVD	R0, F0
+	FRINTZD	F0, F1
+	FMOVD	F1, int+8(FP)
+	FSUBD	F1, F0
+	FMOVD	F0, R1
+	AND	$(1<<63), R0
+	ORR	R0, R1 // must have same sign
+	MOVD	R1, frac+16(FP)
+	RET
diff --git a/src/math/rand/gen_cooked.go b/src/math/rand/gen_cooked.go
new file mode 100644
index 0000000..567b7a8
--- /dev/null
+++ b/src/math/rand/gen_cooked.go
@@ -0,0 +1,89 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// This program computes the value of rng_cooked in rng.go,
+// which is used for seeding all instances of rand.Source.
+// a 64bit and a 63bit version of the array is printed to
+// the standard output.
+
+package main
+
+import "fmt"
+
+const (
+	length = 607
+	tap    = 273
+	mask   = (1 << 63) - 1
+	a      = 48271
+	m      = (1 << 31) - 1
+	q      = 44488
+	r      = 3399
+)
+
+var (
+	rngVec          [length]int64
+	rngTap, rngFeed int
+)
+
+func seedrand(x int32) int32 {
+	hi := x / q
+	lo := x % q
+	x = a*lo - r*hi
+	if x < 0 {
+		x += m
+	}
+	return x
+}
+
+func srand(seed int32) {
+	rngTap = 0
+	rngFeed = length - tap
+	seed %= m
+	if seed < 0 {
+		seed += m
+	} else if seed == 0 {
+		seed = 89482311
+	}
+	x := seed
+	for i := -20; i < length; i++ {
+		x = seedrand(x)
+		if i >= 0 {
+			var u int64
+			u = int64(x) << 20
+			x = seedrand(x)
+			u ^= int64(x) << 10
+			x = seedrand(x)
+			u ^= int64(x)
+			rngVec[i] = u
+		}
+	}
+}
+
+func vrand() int64 {
+	rngTap--
+	if rngTap < 0 {
+		rngTap += length
+	}
+	rngFeed--
+	if rngFeed < 0 {
+		rngFeed += length
+	}
+	x := (rngVec[rngFeed] + rngVec[rngTap])
+	rngVec[rngFeed] = x
+	return x
+}
+
+func main() {
+	srand(1)
+	for i := uint64(0); i < 7.8e12; i++ {
+		vrand()
+	}
+	fmt.Printf("rngVec after 7.8e12 calls to vrand:\n%#v\n", rngVec)
+	for i := range rngVec {
+		rngVec[i] &= mask
+	}
+	fmt.Printf("lower 63bit of rngVec after 7.8e12 calls to vrand:\n%#v\n", rngVec)
+}
diff --git a/src/math/rand/race_test.go b/src/math/rand/race_test.go
index 48f6c29..186c716 100644
--- a/src/math/rand/race_test.go
+++ b/src/math/rand/race_test.go
@@ -33,6 +33,7 @@ func TestConcurrent(t *testing.T) {
 				seed += int64(Int63n(Int63()))
 				seed += int64(NormFloat64())
 				seed += int64(Uint32())
+				seed += int64(Uint64())
 				for _, p := range Perm(10) {
 					seed += int64(p)
 				}
diff --git a/src/math/rand/rand.go b/src/math/rand/rand.go
index dd8d43c..9fe1cbd 100644
--- a/src/math/rand/rand.go
+++ b/src/math/rand/rand.go
@@ -23,7 +23,20 @@ type Source interface {
 	Seed(seed int64)
 }
 
+// A Source64 is a Source that can also generate
+// uniformly-distributed pseudo-random uint64 values in
+// the range [0, 1<<64) directly.
+// If a Rand r's underlying Source s implements Source64,
+// then r.Uint64 returns the result of one call to s.Uint64
+// instead of making two calls to s.Int63.
+type Source64 interface {
+	Source
+	Uint64() uint64
+}
+
 // NewSource returns a new pseudo-random Source seeded with the given value.
+// Unlike the default Source used by top-level functions, this source is not
+// safe for concurrent use by multiple goroutines.
 func NewSource(seed int64) Source {
 	var rng rngSource
 	rng.Seed(seed)
@@ -33,6 +46,7 @@ func NewSource(seed int64) Source {
 // A Rand is a source of random numbers.
 type Rand struct {
 	src Source
+	s64 Source64 // non-nil if src is source64
 
 	// readVal contains remainder of 63-bit integer used for bytes
 	// generation during most recent Read call.
@@ -46,7 +60,10 @@ type Rand struct {
 
 // New returns a new Rand that uses random values from src
 // to generate other random values.
-func New(src Source) *Rand { return &Rand{src: src} }
+func New(src Source) *Rand {
+	s64, _ := src.(Source64)
+	return &Rand{src: src, s64: s64}
+}
 
 // Seed uses the provided seed value to initialize the generator to a deterministic state.
 // Seed should not be called concurrently with any other Rand method.
@@ -66,6 +83,14 @@ func (r *Rand) Int63() int64 { return r.src.Int63() }
 // Uint32 returns a pseudo-random 32-bit value as a uint32.
 func (r *Rand) Uint32() uint32 { return uint32(r.Int63() >> 31) }
 
+// Uint64 returns a pseudo-random 64-bit value as a uint64.
+func (r *Rand) Uint64() uint64 {
+	if r.s64 != nil {
+		return r.s64.Uint64()
+	}
+	return uint64(r.Int63())>>31 | uint64(r.Int63())<<32
+}
+
 // Int31 returns a non-negative pseudo-random 31-bit integer as an int32.
 func (r *Rand) Int31() int32 { return int32(r.Int63() >> 32) }
 
@@ -207,7 +232,7 @@ func read(p []byte, int63 func() int64, readVal *int64, readPos *int8) (n int, e
  * Top-level convenience functions
  */
 
-var globalRand = New(&lockedSource{src: NewSource(1)})
+var globalRand = New(&lockedSource{src: NewSource(1).(Source64)})
 
 // Seed uses the provided seed value to initialize the default Source to a
 // deterministic state. If Seed is not called, the generator behaves as
@@ -224,6 +249,10 @@ func Int63() int64 { return globalRand.Int63() }
 // from the default Source.
 func Uint32() uint32 { return globalRand.Uint32() }
 
+// Uint64 returns a pseudo-random 64-bit value as a uint64
+// from the default Source.
+func Uint64() uint64 { return globalRand.Uint64() }
+
 // Int31 returns a non-negative pseudo-random 31-bit integer as an int32
 // from the default Source.
 func Int31() int32 { return globalRand.Int31() }
@@ -286,7 +315,7 @@ func ExpFloat64() float64 { return globalRand.ExpFloat64() }
 
 type lockedSource struct {
 	lk  sync.Mutex
-	src Source
+	src Source64
 }
 
 func (r *lockedSource) Int63() (n int64) {
@@ -296,6 +325,13 @@ func (r *lockedSource) Int63() (n int64) {
 	return
 }
 
+func (r *lockedSource) Uint64() (n uint64) {
+	r.lk.Lock()
+	n = r.src.Uint64()
+	r.lk.Unlock()
+	return
+}
+
 func (r *lockedSource) Seed(seed int64) {
 	r.lk.Lock()
 	r.src.Seed(seed)
diff --git a/src/math/rand/rand_test.go b/src/math/rand/rand_test.go
index 6f31279..bf509e0 100644
--- a/src/math/rand/rand_test.go
+++ b/src/math/rand/rand_test.go
@@ -328,13 +328,26 @@ func TestExpTables(t *testing.T) {
 	}
 }
 
+func hasSlowFloatingPoint() bool {
+	switch runtime.GOARCH {
+	case "arm":
+		return os.Getenv("GOARM") == "5"
+	case "mips", "mipsle", "mips64", "mips64le":
+		// Be conservative and assume that all mips boards
+		// have emulated floating point.
+		// TODO: detect what it actually has.
+		return true
+	}
+	return false
+}
+
 func TestFloat32(t *testing.T) {
 	// For issue 6721, the problem came after 7533753 calls, so check 10e6.
 	num := int(10e6)
 	// But do the full amount only on builders (not locally).
 	// But ARM5 floating point emulation is slow (Issue 10749), so
 	// do less for that builder:
-	if testing.Short() && (testenv.Builder() == "" || runtime.GOARCH == "arm" && os.Getenv("GOARM") == "5") {
+	if testing.Short() && (testenv.Builder() == "" || hasSlowFloatingPoint()) {
 		num /= 100 // 1.72 seconds instead of 172 seconds
 	}
 
diff --git a/src/math/rand/regress_test.go b/src/math/rand/regress_test.go
index 4dd965c..e31e6c5 100644
--- a/src/math/rand/regress_test.go
+++ b/src/math/rand/regress_test.go
@@ -381,4 +381,24 @@ var regressGolden = []interface{}{
 	uint32(75079301),                                                   // Uint32()
 	uint32(3380456901),                                                 // Uint32()
 	uint32(3433369789),                                                 // Uint32()
+	uint64(8717895732742165505),                                        // Uint64()
+	uint64(2259404117704393152),                                        // Uint64()
+	uint64(6050128673802995827),                                        // Uint64()
+	uint64(9724605487393973602),                                        // Uint64()
+	uint64(12613765599614152010),                                       // Uint64()
+	uint64(11893357769247901871),                                       // Uint64()
+	uint64(1774932891286980153),                                        // Uint64()
+	uint64(15267744271532198264),                                       // Uint64()
+	uint64(17498302081433670737),                                       // Uint64()
+	uint64(1543572285742637646),                                        // Uint64()
+	uint64(11885104867954719224),                                       // Uint64()
+	uint64(17548432336275752516),                                       // Uint64()
+	uint64(7837839688282259259),                                        // Uint64()
+	uint64(2518412263346885298),                                        // Uint64()
+	uint64(5617773211005988520),                                        // Uint64()
+	uint64(11562935753659892057),                                       // Uint64()
+	uint64(16368296284793757383),                                       // Uint64()
+	uint64(161231572858529631),                                         // Uint64()
+	uint64(16482847956365694147),                                       // Uint64()
+	uint64(16596477517051940556),                                       // Uint64()
 }
diff --git a/src/math/rand/rng.go b/src/math/rand/rng.go
index 947c49f..f922417 100644
--- a/src/math/rand/rng.go
+++ b/src/math/rand/rng.go
@@ -23,161 +23,159 @@ const (
 )
 
 var (
-	// cooked random numbers
-	// the state of the rng
-	// after 780e10 iterations
+	// Used for seeding. See gen_cooked.go for details.
 	rng_cooked [_LEN]int64 = [...]int64{
-		5041579894721019882, 4646389086726545243, 1395769623340756751, 5333664234075297259,
-		2875692520355975054, 9033628115061424579, 7143218595135194537, 4812947590706362721,
-		7937252194349799378, 5307299880338848416, 8209348851763925077, 2115741599318814044,
-		4593015457530856296, 8140875735541888011, 3319429241265089026, 8619815648190321034,
-		1727074043483619500, 113108499721038619, 4569519971459345583, 5062833859075314731,
-		2387618771259064424, 2716131344356686112, 6559392774825876886, 7650093201692370310,
-		7684323884043752161, 257867835996031390, 6593456519409015164, 271327514973697897,
-		2789386447340118284, 1065192797246149621, 3344507881999356393, 4459797941780066633,
-		7465081662728599889, 1014950805555097187, 4449440729345990775, 3481109366438502643,
+		-4181792142133755926, -4576982950128230565, 1395769623340756751, 5333664234075297259,
+		-6347679516498800754, 9033628115061424579, 7143218595135194537, 4812947590706362721,
+		7937252194349799378, 5307299880338848416, 8209348851763925077, -7107630437535961764,
+		4593015457530856296, 8140875735541888011, -5903942795589686782, -603556388664454774,
+		-7496297993371156308, 113108499721038619, 4569519971459345583, -4160538177779461077,
+		-6835753265595711384, -6507240692498089696, 6559392774825876886, 7650093201692370310,
+		7684323884043752161, -8965504200858744418, -2629915517445760644, 271327514973697897,
+		-6433985589514657524, 1065192797246149621, 3344507881999356393, -4763574095074709175,
+		7465081662728599889, 1014950805555097187, -4773931307508785033, -5742262670416273165,
 		2418672789110888383, 5796562887576294778, 4484266064449540171, 3738982361971787048,
-		4523597184512354423, 10530508058128498, 8633833783282346118, 2625309929628791628,
-		8660405965245884302, 10162832508971942, 6540714680961817391, 7031802312784620857,
-		6240911277345944669, 831864355460801054, 8004434137542152891, 2116287251661052151,
+		-4699774852342421385, 10530508058128498, -589538253572429690, -6598062107225984180,
+		8660405965245884302, 10162832508971942, -2682657355892958417, 7031802312784620857,
+		6240911277345944669, 831864355460801054, -1218937899312622917, 2116287251661052151,
 		2202309800992166967, 9161020366945053561, 4069299552407763864, 4936383537992622449,
-		457351505131524928, 342195045928179354, 2847771682816600509, 2068020115986376518,
-		4368649989588021065, 887231587095185257, 5563591506886576496, 6816225200251950296,
-		5616972787034086048, 8471809303394836566, 1686575021641186857, 4045484338074262002,
-		4244156215201778923, 7848217333783577387, 5632136521049761902, 833283142057835272,
-		9029726508369077193, 3243583134664087292, 4316371101804477087, 8937849979965997980,
-		6446940406810434101, 1679342092332374735, 6050638460742422078, 6993520719509581582,
-		7640877852514293609, 5881353426285907985, 812786550756860885, 4541845584483343330,
-		2725470216277009086, 4980675660146853729, 5210769080603236061, 8894283318990530821,
-		6326442804750084282, 1495812843684243920, 7069751578799128019, 7370257291860230865,
-		6756929275356942261, 4706794511633873654, 7824520467827898663, 8549875090542453214,
-		33650829478596156, 1328918435751322643, 7297902601803624459, 1011190183918857495,
-		2238025036817854944, 5147159997473910359, 896512091560522982, 2659470849286379941,
-		6097729358393448602, 1731725986304753684, 4106255841983812711, 8327155210721535508,
-		8477511620686074402, 5803876044675762232, 8435417780860221662, 5988852856651071244,
-		4715837297103951910, 7566171971264485114, 505808562678895611, 5070098180695063370,
-		842110666775871513, 572156825025677802, 1791881013492340891, 3393267094866038768,
-		3778721850472236509, 2352769483186201278, 1292459583847367458, 8897907043675088419,
-		5781809037144163536, 2733958794029492513, 5092019688680754699, 8996124554772526841,
-		4234737173186232084, 5027558287275472836, 4635198586344772304, 8687338893267139351,
-		5907508150730407386, 784756255473944452, 972392927514829904, 5422057694808175112,
-		5158420642969283891, 9048531678558643225, 2407211146698877100, 7583282216521099569,
-		3940796514530962282, 3341174631045206375, 3095313889586102949, 7405321895688238710,
-		5832080132947175283, 7890064875145919662, 8184139210799583195, 1149859861409226130,
-		1464597243840211302, 4641648007187991873, 3516491885471466898, 956288521791657692,
+		457351505131524928, -8881176990926596454, -6375600354038175299, -7155351920868399290,
+		4368649989588021065, 887231587095185257, -3659780529968199312, -2407146836602825512,
+		5616972787034086048, -751562733459939242, 1686575021641186857, -5177887698780513806,
+		-4979215821652996885, -1375154703071198421, 5632136521049761902, -8390088894796940536,
+		-193645528485698615, -5979788902190688516, -4907000935050298721, -285522056888777828,
+		-2776431630044341707, 1679342092332374735, 6050638460742422078, -2229851317345194226,
+		-1582494184340482199, 5881353426285907985, 812786550756860885, 4541845584483343330,
+		-6497901820577766722, 4980675660146853729, -4012602956251539747, -329088717864244987,
+		-2896929232104691526, 1495812843684243920, -2153620458055647789, 7370257291860230865,
+		-2466442761497833547, 4706794511633873654, -1398851569026877145, 8549875090542453214,
+		-9189721207376179652, -7894453601103453165, 7297902601803624459, 1011190183918857495,
+		-6985347000036920864, 5147159997473910359, -8326859945294252826, 2659470849286379941,
+		6097729358393448602, -7491646050550022124, -5117116194870963097, -896216826133240300,
+		-745860416168701406, 5803876044675762232, -787954255994554146, -3234519180203704564,
+		-4507534739750823898, -1657200065590290694, 505808562678895611, -4153273856159712438,
+		-8381261370078904295, 572156825025677802, 1791881013492340891, 3393267094866038768,
+		-5444650186382539299, 2352769483186201278, -7930912453007408350, -325464993179687389,
+		-3441562999710612272, -6489413242825283295, 5092019688680754699, -227247482082248967,
+		4234737173186232084, 5027558287275472836, 4635198586344772304, -536033143587636457,
+		5907508150730407386, -8438615781380831356, 972392927514829904, -3801314342046600696,
+		-4064951393885491917, -174840358296132583, 2407211146698877100, -1640089820333676239,
+		3940796514530962282, -5882197405809569433, 3095313889586102949, -1818050141166537098,
+		5832080132947175283, 7890064875145919662, 8184139210799583195, -8073512175445549678,
+		-7758774793014564506, -4581724029666783935, 3516491885471466898, -8267083515063118116,
 		6657089965014657519, 5220884358887979358, 1796677326474620641, 5340761970648932916,
 		1147977171614181568, 5066037465548252321, 2574765911837859848, 1085848279845204775,
-		3350107529868390359, 6116438694366558490, 2107701075971293812, 1803294065921269267,
-		2469478054175558874, 7368243281019965984, 3791908367843677526, 185046971116456637,
-		2257095756513439648, 7217693971077460129, 909049953079504259, 7196649268545224266,
-		5637660345400869599, 3955544945427965183, 8057528650917418961, 4139268440301127643,
-		6621926588513568059, 1373361136802681441, 6527366231383600011, 3507654575162700890,
-		9202058512774729859, 1954818376891585542, 6640380907130175705, 8299563319178235687,
-		3901867355218954373, 7046310742295574065, 6847195391333990232, 1572638100518868053,
-		8850422670118399721, 3631909142291992901, 5158881091950831288, 2882958317343121593,
-		4763258931815816403, 6280052734341785344, 4243789408204964850, 2043464728020827976,
-		6545300466022085465, 4562580375758598164, 5495451168795427352, 1738312861590151095,
-		553004618757816492, 6895160632757959823, 8233623922264685171, 7139506338801360852,
-		8550891222387991669, 5535668688139305547, 2430933853350256242, 5401941257863201076,
-		8159640039107728799, 6157493831600770366, 7632066283658143750, 6308328381617103346,
+		-5873264506986385449, 6116438694366558490, 2107701075971293812, -7420077970933506541,
+		2469478054175558874, -1855128755834809824, -5431463669011098282, -9038325065738319171,
+		-6966276280341336160, 7217693971077460129, -8314322083775271549, 7196649268545224266,
+		-3585711691453906209, -5267827091426810625, 8057528650917418961, -5084103596553648165,
+		-2601445448341207749, -7850010900052094367, 6527366231383600011, 3507654575162700890,
+		9202058512774729859, 1954818376891585542, -2582991129724600103, 8299563319178235687,
+		-5321504681635821435, 7046310742295574065, -2376176645520785576, -7650733936335907755,
+		8850422670118399721, 3631909142291992901, 5158881091950831288, -6340413719511654215,
+		4763258931815816403, 6280052734341785344, -4979582628649810958, 2043464728020827976,
+		-2678071570832690343, 4562580375758598164, 5495451168795427352, -7485059175264624713,
+		553004618757816492, 6895160632757959823, -989748114590090637, 7139506338801360852,
+		-672480814466784139, 5535668688139305547, 2430933853350256242, -3821430778991574732,
+		-1063731997747047009, -3065878205254005442, 7632066283658143750, 6308328381617103346,
 		3681878764086140361, 3289686137190109749, 6587997200611086848, 244714774258135476,
-		4079788377417136100, 8090302575944624335, 2945117363431356361, 864324395848741045,
-		3009039260312620700, 8430027460082534031, 401084700045993341, 7254622446438694921,
-		4707864159563588614, 5640248530963493951, 5982507712689997893, 3315098242282210105,
-		5503847578771918426, 3941971367175193882, 8118566580304798074, 3839261274019871296,
-		7062410411742090847, 741381002980207668, 6027994129690250817, 2497829994150063930,
-		6251390334426228834, 1368930247903518833, 8809096399316380241, 6492004350391900708,
-		2462145737463489636, 404828418920299174, 4153026434231690595, 261785715255475940,
-		5464715384600071357, 592710404378763017, 6764129236657751224, 8513655718539357449,
-		5820343663801914208, 385298524683789911, 5224135003438199467, 6303131641338802145,
-		7150122561309371392, 368107899140673753, 3115186834558311558, 2915636353584281051,
+		-5143583659437639708, 8090302575944624335, 2945117363431356361, -8359047641006034763,
+		3009039260312620700, -793344576772241777, 401084700045993341, -1968749590416080887,
+		4707864159563588614, -3583123505891281857, -3240864324164777915, -5908273794572565703,
+		-3719524458082857382, -5281400669679581926, 8118566580304798074, 3839261274019871296,
+		7062410411742090847, -8481991033874568140, 6027994129690250817, -6725542042704711878,
+		-2971981702428546974, -7854441788951256975, 8809096399316380241, 6492004350391900708,
+		2462145737463489636, -8818543617934476634, -5070345602623085213, -8961586321599299868,
+		-3758656652254704451, -8630661632476012791, 6764129236657751224, -709716318315418359,
+		-3403028373052861600, -8838073512170985897, -3999237033416576341, -2920240395515973663,
+		-2073249475545404416, 368107899140673753, -6108185202296464250, -6307735683270494757,
 		4782583894627718279, 6718292300699989587, 8387085186914375220, 3387513132024756289,
-		4654329375432538231, 8930667561363381602, 5374373436876319273, 7623042350483453954,
-		7725442901813263321, 9186225467561587250, 4091027289597503355, 2357631606492579800,
-		2530936820058611833, 1636551876240043639, 5564664674334965799, 1452244145334316253,
-		2061642381019690829, 1279580266495294036, 9108481583171221009, 6023278686734049809,
-		5007630032676973346, 2153168792952589781, 6720334534964750538, 6041546491134794105,
-		3433922409283786309, 2285479922797300912, 3110614940896576130, 6366559590722842893,
-		5418791419666136509, 7163298419643543757, 4891138053923696990, 580618510277907015,
-		1684034065251686769, 4429514767357295841, 330346578555450005, 1119637995812174675,
-		7177515271653460134, 4589042248470800257, 7693288629059004563, 143607045258444228,
-		246994305896273627, 866417324803099287, 6473547110565816071, 3092379936208876896,
-		2058427839513754051, 5133784708526867938, 8785882556301281247, 6149332666841167611,
-		8585842181454472135, 6137678347805511274, 2070447184436970006, 5708223427705576541,
-		5999657892458244504, 4358391411789012426, 325123008708389849, 6837621693887290924,
-		4843721905315627004, 6010651222149276415, 5398352198963874652, 4602025990114250980,
-		1044646352569048800, 9106614159853161675, 829256115228593269, 4919284369102997000,
-		2681532557646850893, 3681559472488511871, 5307999518958214035, 6334130388442829274,
-		2658708232916537604, 1163313865052186287, 581945337509520675, 3648778920718647903,
-		4423673246306544414, 1620799783996955743, 220828013409515943, 8150384699999389761,
-		4287360518296753003, 4590000184845883843, 5513660857261085186, 6964829100392774275,
-		478991688350776035, 8746140185685648781, 228500091334420247, 1356187007457302238,
-		3019253992034194581, 3152601605678500003, 430152752706002213, 5559581553696971176,
-		4916432985369275664, 663574931734554391, 3420773838927732076, 2868348622579915573,
-		1999319134044418520, 3328689518636282723, 2587672709781371173, 1517255313529399333,
-		3092343956317362483, 3662252519007064108, 972445599196498113, 7664865435875959367,
-		1708913533482282562, 6917817162668868494, 3217629022545312900, 2570043027221707107,
-		8739788839543624613, 2488075924621352812, 4694002395387436668, 4559628481798514356,
+		4654329375432538231, -292704475491394206, -3848998599978456535, 7623042350483453954,
+		7725442901813263321, 9186225467561587250, -5132344747257272453, -6865740430362196008,
+		2530936820058611833, 1636551876240043639, -3658707362519810009, 1452244145334316253,
+		-7161729655835084979, -7943791770359481772, 9108481583171221009, -3200093350120725999,
+		5007630032676973346, 2153168792952589781, 6720334534964750538, -3181825545719981703,
+		3433922409283786309, 2285479922797300912, 3110614940896576130, -2856812446131932915,
+		-3804580617188639299, 7163298419643543757, 4891138053923696990, 580618510277907015,
+		1684034065251686769, 4429514767357295841, -8893025458299325803, -8103734041042601133,
+		7177515271653460134, 4589042248470800257, -1530083407795771245, 143607045258444228,
+		246994305896273627, -8356954712051676521, 6473547110565816071, 3092379936208876896,
+		2058427839513754051, -4089587328327907870, 8785882556301281247, -3074039370013608197,
+		-637529855400303673, 6137678347805511274, -7152924852417805802, 5708223427705576541,
+		-3223714144396531304, 4358391411789012426, 325123008708389849, 6837621693887290924,
+		4843721905315627004, -3212720814705499393, -3825019837890901156, 4602025990114250980,
+		1044646352569048800, 9106614159853161675, -8394115921626182539, -4304087667751778808,
+		2681532557646850893, 3681559472488511871, -3915372517896561773, -2889241648411946534,
+		-6564663803938238204, -8060058171802589521, 581945337509520675, 3648778920718647903,
+		-4799698790548231394, -7602572252857820065, 220828013409515943, -1072987336855386047,
+		4287360518296753003, -4633371852008891965, 5513660857261085186, -2258542936462001533,
+		-8744380348503999773, 8746140185685648781, 228500091334420247, 1356187007457302238,
+		3019253992034194581, 3152601605678500003, -8793219284148773595, 5559581553696971176,
+		4916432985369275664, -8559797105120221417, -5802598197927043732, 2868348622579915573,
+		-7224052902810357288, -5894682518218493085, 2587672709781371173, -7706116723325376475,
+		3092343956317362483, -5561119517847711700, 972445599196498113, -1558506600978816441,
+		1708913533482282562, -2305554874185907314, -6005743014309462908, -6653329009633068701,
+		-483583197311151195, 2488075924621352812, -4529369641467339140, -4663743555056261452,
 		2997203966153298104, 1282559373026354493, 240113143146674385, 8665713329246516443,
-		628141331766346752, 4571950817186770476, 1472811188152235408, 7596648026010355826,
-		6091219417754424743, 7834161864828164065, 7103445518877254909, 4390861237357459201,
-		4442653864240571734, 8903482404847331368, 622261699494173647, 6037261250297213248,
-		504404948065709118, 7275215526217113061, 1011176780856001400, 2194750105623461063,
-		2623071828615234808, 5157313728073836108, 3738405111966602044, 2539767524076729570,
-		2467284396349269342, 5256026990536851868, 7841086888628396109, 6640857538655893162,
-		1202087339038317498, 2113514992440715978, 7534350895342931403, 4925284734898484745,
-		5145623771477493805, 8225140880134972332, 2719520354384050532, 9132346697815513771,
-		4332154495710163773, 7137789594094346916, 6994721091344268833, 6667228574869048934,
-		655440045726677499, 59934747298466858, 6124974028078036405, 8957774780655365418,
-		2332206071942466437, 1701056712286369627, 3154897383618636503, 1637766181387607527,
-		2460521277767576533, 197309393502684135, 643677854385267315, 2543179307861934850,
-		4350769010207485119, 4754652089410667672, 2015595502641514512, 7999059458976458608,
-		4287946071480840813, 8362686366770308971, 6486469209321732151, 3617727845841796026,
-		7554353525834302244, 4450022655153542367, 1605195740213535749, 5327014565305508387,
-		4626575813550328320, 2692222020597705149, 241045573717249868, 5098046974627094010,
-		7916882295460730264, 884817090297530579, 5329160409530630596, 7790979528857726136,
-		4955070238059373407, 4918537275422674302, 3008076183950404629, 3007769226071157901,
-		2470346235617803020, 8928702772696731736, 7856187920214445904, 4474874585391974885,
-		7900176660600710914, 2140571127916226672, 2425445057265199971, 2486055153341847830,
-		4186670094382025798, 1883939007446035042, 8808666044074867985, 3734134241178479257,
-		4065968871360089196, 6953124200385847784, 1305686814738899057, 1637739099014457647,
-		3656125660947993209, 3966759634633167020, 3106378204088556331, 6328899822778449810,
-		4565385105440252958, 1979884289539493806, 2331793186920865425, 3783206694208922581,
-		8464961209802336085, 2843963751609577687, 3030678195484896323, 4793717574095772604,
+		628141331766346752, -4651421219668005332, -7750560848702540400, 7596648026010355826,
+		-3132152619100351065, 7834161864828164065, 7103445518877254909, 4390861237357459201,
+		-4780718172614204074, -319889632007444440, 622261699494173647, -3186110786557562560,
+		-8718967088789066690, -1948156510637662747, -8212195255998774408, -7028621931231314745,
+		2623071828615234808, -4066058308780939700, -5484966924888173764, -6683604512778046238,
+		-6756087640505506466, 5256026990536851868, 7841086888628396109, 6640857538655893162,
+		-8021284697816458310, -7109857044414059830, -1689021141511844405, -4298087301956291063,
+		-4077748265377282003, -998231156719803476, 2719520354384050532, 9132346697815513771,
+		4332154495710163773, -2085582442760428892, 6994721091344268833, -2556143461985726874,
+		-8567931991128098309, 59934747298466858, -3098398008776739403, -265597256199410390,
+		2332206071942466437, -7522315324568406181, 3154897383618636503, -7585605855467168281,
+		-6762850759087199275, 197309393502684135, -8579694182469508493, 2543179307861934850,
+		4350769010207485119, -4468719947444108136, -7207776534213261296, -1224312577878317200,
+		4287946071480840813, 8362686366770308971, 6486469209321732151, -5605644191012979782,
+		-1669018511020473564, 4450022655153542367, -7618176296641240059, -3896357471549267421,
+		-4596796223304447488, -6531150016257070659, -8982326463137525940, -4125325062227681798,
+		-1306489741394045544, -8338554946557245229, 5329160409530630596, 7790979528857726136,
+		4955070238059373407, -4304834761432101506, -6215295852904371179, 3007769226071157901,
+		-6753025801236972788, 8928702772696731736, 7856187920214445904, -4748497451462800923,
+		7900176660600710914, -7082800908938549136, -6797926979589575837, -6737316883512927978,
+		4186670094382025798, 1883939007446035042, -414705992779907823, 3734134241178479257,
+		4065968871360089196, 6953124200385847784, -7917685222115876751, -7585632937840318161,
+		-5567246375906782599, -5256612402221608788, 3106378204088556331, -2894472214076325998,
+		4565385105440252958, 1979884289539493806, -6891578849933910383, 3783206694208922581,
+		8464961209802336085, 2843963751609577687, 3030678195484896323, -4429654462759003204,
 		4459239494808162889, 402587895800087237, 8057891408711167515, 4541888170938985079,
-		1042662272908816815, 5557303057122568958, 2647678726283249984, 2144477441549833761,
-		5806352215355387087, 7117771003473903623, 5916597177708541638, 462597715452321361,
-		8833658097025758785, 5970273481425315300, 563813119381731307, 2768349550652697015,
-		1598828206250873866, 5206393647403558110, 6235043485709261823, 3152217402014639496,
-		8469693267274066490, 125672920241807416, 5311079624024060938, 6663754932310491587,
-		8736848295048751716, 4488039774992061878, 5923302823487327109, 140891791083103236,
-		7414942793393574290, 7990420780896957397, 4317817392807076702, 3625184369705367340,
-		2740722765288122703, 5743100009702758344, 5997898640509039159, 8854493341352484163,
-		5242208035432907801, 701338899890987198, 7609280429197514109, 3020985755112334161,
-		6651322707055512866, 2635195723621160615, 5144520864246028816, 1035086515727829828,
-		1567242097116389047, 8172389260191636581, 6337820351429292273, 2163012566996458925,
-		2743190902890262681, 1906367633221323427, 6011544915663598137, 5932255307352610768,
-		2241128460406315459, 895504896216695588, 3094483003111372717, 4583857460292963101,
-		9079887171656594975, 8839289181930711403, 5762740387243057873, 4225072055348026230,
-		1838220598389033063, 3801620336801580414, 8823526620080073856, 1776617605585100335,
-		7899055018877642622, 5421679761463003041, 5521102963086275121, 4248279443559365898,
-		8735487530905098534, 1760527091573692978, 7142485049657745894, 8222656872927218123,
-		4969531564923704323, 3394475942196872480, 6424174453260338141, 359248545074932887,
-		3273651282831730598, 6797106199797138596, 3030918217665093212, 145600834617314036,
-		6036575856065626233, 740416251634527158, 7080427635449935582, 6951781370868335478,
-		399922722363687927, 294902314447253185, 7844950936339178523, 880320858634709042,
-		6192655680808675579, 411604686384710388, 9026808440365124461, 6440783557497587732,
+		1042662272908816815, -3666068979732206850, 2647678726283249984, 2144477441549833761,
+		-3417019821499388721, -2105601033380872185, 5916597177708541638, -8760774321402454447,
+		8833658097025758785, 5970273481425315300, 563813119381731307, -6455022486202078793,
+		1598828206250873866, -4016978389451217698, -2988328551145513985, -6071154634840136312,
+		8469693267274066490, 125672920241807416, -3912292412830714870, -2559617104544284221,
+		-486523741806024092, -4735332261862713930, 5923302823487327109, -9082480245771672572,
+		-1808429243461201518, 7990420780896957397, 4317817392807076702, 3625184369705367340,
+		-6482649271566653105, -3480272027152017464, -3225473396345736649, -368878695502291645,
+		-3981164001421868007, -8522033136963788610, 7609280429197514109, 3020985755112334161,
+		-2572049329799262942, 2635195723621160615, 5144520864246028816, -8188285521126945980,
+		1567242097116389047, 8172389260191636581, -2885551685425483535, -7060359469858316883,
+		-6480181133964513127, -7317004403633452381, 6011544915663598137, 5932255307352610768,
+		2241128460406315459, -8327867140638080220, 3094483003111372717, 4583857460292963101,
+		9079887171656594975, -384082854924064405, -3460631649611717935, 4225072055348026230,
+		-7385151438465742745, 3801620336801580414, -399845416774701952, -7446754431269675473,
+		7899055018877642622, 5421679761463003041, 5521102963086275121, -4975092593295409910,
+		8735487530905098534, -7462844945281082830, -2080886987197029914, -1000715163927557685,
+		-4253840471931071485, -5828896094657903328, 6424174453260338141, 359248545074932887,
+		-5949720754023045210, -2426265837057637212, 3030918217665093212, -9077771202237461772,
+		-3186796180789149575, 740416251634527158, -2142944401404840226, 6951781370868335478,
+		399922722363687927, -8928469722407522623, -1378421100515597285, -8343051178220066766,
+		-3030716356046100229, -8811767350470065420, 9026808440365124461, 6440783557497587732,
 		4615674634722404292, 539897290441580544, 2096238225866883852, 8751955639408182687,
-		1907224908052289603, 7381039757301768559, 6157238513393239656, 7749994231914157575,
+		-7316147128802486205, 7381039757301768559, 6157238513393239656, -1473377804940618233,
 		8629571604380892756, 5280433031239081479, 7101611890139813254, 2479018537985767835,
-		7169176924412769570, 7942066497793203302, 1357759729055557688, 2278447439451174845,
-		3625338785743880657, 6477479539006708521, 8976185375579272206, 5511371554711836120,
+		7169176924412769570, -1281305539061572506, -7865612307799218120, 2278447439451174845,
+		3625338785743880657, 6477479539006708521, 8976185375579272206, -3712000482142939688,
 		1326024180520890843, 7537449876596048829, 5464680203499696154, 3189671183162196045,
-		6346751753565857109, 241159987320630307, 3095793449658682053, 8978332846736310159,
-		2902794662273147216, 7208698530190629697, 7276901792339343736, 1732385229314443140,
-		4133292154170828382, 2918308698224194548, 1519461397937144458, 5293934712616591764,
-		4922828954023452664, 2879211533496425641, 5896236396443472108, 8465043815351752425,
-		7329020396871624740, 8915471717014488588, 2944902635677463047, 7052079073493465134,
+		6346751753565857109, -8982212049534145501, -6127578587196093755, -245039190118465649,
+		-6320577374581628592, 7208698530190629697, 7276901792339343736, -7490986807540332668,
+		4133292154170828382, 2918308698224194548, -7703910638917631350, -3929437324238184044,
+		-4300543082831323144, -6344160503358350167, 5896236396443472108, -758328221503023383,
+		-1894351639983151068, -307900319840287220, -6278469401177312761, -2171292963361310674,
 		8382142935188824023, 9103922860780351547, 4152330101494654406,
 	}
 )
@@ -223,13 +221,18 @@ func (rng *rngSource) Seed(seed int64) {
 			x = seedrand(x)
 			u ^= int64(x)
 			u ^= rng_cooked[i]
-			rng.vec[i] = u & _MASK
+			rng.vec[i] = u
 		}
 	}
 }
 
 // Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
 func (rng *rngSource) Int63() int64 {
+	return int64(rng.Uint64() & _MASK)
+}
+
+// Uint64 returns a non-negative pseudo-random 64-bit integer as an uint64.
+func (rng *rngSource) Uint64() uint64 {
 	rng.tap--
 	if rng.tap < 0 {
 		rng.tap += _LEN
@@ -240,7 +243,7 @@ func (rng *rngSource) Int63() int64 {
 		rng.feed += _LEN
 	}
 
-	x := (rng.vec[rng.feed] + rng.vec[rng.tap]) & _MASK
+	x := rng.vec[rng.feed] + rng.vec[rng.tap]
 	rng.vec[rng.feed] = x
-	return x
+	return uint64(x)
 }
diff --git a/src/math/sin.go b/src/math/sin.go
index ed85f21..7a75a5f 100644
--- a/src/math/sin.go
+++ b/src/math/sin.go
@@ -140,8 +140,8 @@ func cos(x float64) float64 {
 
 	// map zeros to origin
 	if j&1 == 1 {
-		j += 1
-		y += 1
+		j++
+		y++
 	}
 	j &= 7 // octant modulo 2Pi radians (360 degrees)
 	if j > 3 {
@@ -200,8 +200,8 @@ func sin(x float64) float64 {
 
 	// map zeros to origin
 	if j&1 == 1 {
-		j += 1
-		y += 1
+		j++
+		y++
 	}
 	j &= 7 // octant modulo 2Pi radians (360 degrees)
 	// reflect in x axis
diff --git a/src/math/sin_s390x.s b/src/math/sin_s390x.s
new file mode 100644
index 0000000..5dc823c
--- /dev/null
+++ b/src/math/sin_s390x.s
@@ -0,0 +1,356 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// Various constants
+DATA sincosxnan<>+0(SB)/8, $0x7ff8000000000000
+GLOBL sincosxnan<>+0(SB), RODATA, $8
+DATA sincosxlim<>+0(SB)/8, $0x432921fb54442d19
+GLOBL sincosxlim<>+0(SB), RODATA, $8
+DATA sincosxadd<>+0(SB)/8, $0xc338000000000000
+GLOBL sincosxadd<>+0(SB), RODATA, $8
+DATA sincosxpi2l<>+0(SB)/8, $0.108285667392191389e-31
+GLOBL sincosxpi2l<>+0(SB), RODATA, $8
+DATA sincosxpi2m<>+0(SB)/8, $0.612323399573676480e-16
+GLOBL sincosxpi2m<>+0(SB), RODATA, $8
+DATA sincosxpi2h<>+0(SB)/8, $0.157079632679489656e+01
+GLOBL sincosxpi2h<>+0(SB), RODATA, $8
+DATA sincosrpi2<>+0(SB)/8, $0.636619772367581341e+00
+GLOBL sincosrpi2<>+0(SB), RODATA, $8
+
+// Minimax polynomial approximations
+DATA sincosc0<>+0(SB)/8, $0.100000000000000000E+01
+GLOBL sincosc0<>+0(SB), RODATA, $8
+DATA sincosc1<>+0(SB)/8, $-.499999999999999833E+00
+GLOBL sincosc1<>+0(SB), RODATA, $8
+DATA sincosc2<>+0(SB)/8, $0.416666666666625843E-01
+GLOBL sincosc2<>+0(SB), RODATA, $8
+DATA sincosc3<>+0(SB)/8, $-.138888888885498984E-02
+GLOBL sincosc3<>+0(SB), RODATA, $8
+DATA sincosc4<>+0(SB)/8, $0.248015871681607202E-04
+GLOBL sincosc4<>+0(SB), RODATA, $8
+DATA sincosc5<>+0(SB)/8, $-.275572911309937875E-06
+GLOBL sincosc5<>+0(SB), RODATA, $8
+DATA sincosc6<>+0(SB)/8, $0.208735047247632818E-08
+GLOBL sincosc6<>+0(SB), RODATA, $8
+DATA sincosc7<>+0(SB)/8, $-.112753632738365317E-10
+GLOBL sincosc7<>+0(SB), RODATA, $8
+DATA sincoss0<>+0(SB)/8, $0.100000000000000000E+01
+GLOBL sincoss0<>+0(SB), RODATA, $8
+DATA sincoss1<>+0(SB)/8, $-.166666666666666657E+00
+GLOBL sincoss1<>+0(SB), RODATA, $8
+DATA sincoss2<>+0(SB)/8, $0.833333333333309209E-02
+GLOBL sincoss2<>+0(SB), RODATA, $8
+DATA sincoss3<>+0(SB)/8, $-.198412698410701448E-03
+GLOBL sincoss3<>+0(SB), RODATA, $8
+DATA sincoss4<>+0(SB)/8, $0.275573191453906794E-05
+GLOBL sincoss4<>+0(SB), RODATA, $8
+DATA sincoss5<>+0(SB)/8, $-.250520918387633290E-07
+GLOBL sincoss5<>+0(SB), RODATA, $8
+DATA sincoss6<>+0(SB)/8, $0.160571285514715856E-09
+GLOBL sincoss6<>+0(SB), RODATA, $8
+DATA sincoss7<>+0(SB)/8, $-.753213484933210972E-12
+GLOBL sincoss7<>+0(SB), RODATA, $8
+
+// Sin returns the sine of the radian argument x.
+//
+// Special cases are:
+//      Sin(±0) = ±0
+//      Sin(±Inf) = NaN
+//      Sin(NaN) = NaN
+// The algorithm used is minimax polynomial approximation.
+// with coefficients determined with a Remez exchange algorithm.
+
+TEXT ·sinAsm(SB),NOSPLIT,$0-16
+	FMOVD   x+0(FP), F0
+	//special case Sin(±0) = ±0
+	FMOVD   $(0.0), F1
+	FCMPU   F0, F1
+	BEQ     sinIsZero
+	WORD    $0xB3120000     //ltdbr %f0,%f0
+	BLTU    L17
+	FMOVD   F0, F5
+L2:
+	MOVD    $sincoss7<>+0(SB), R1
+	FMOVD   0(R1), F4
+	MOVD    $sincoss6<>+0(SB), R1
+	FMOVD   0(R1), F1
+	MOVD    $sincoss5<>+0(SB), R1
+	VLEG    $0, 0(R1), V18
+	MOVD    $sincoss4<>+0(SB), R1
+	FMOVD   0(R1), F6
+	MOVD    $sincoss2<>+0(SB), R1
+	VLEG    $0, 0(R1), V16
+	MOVD    $sincoss3<>+0(SB), R1
+	FMOVD   0(R1), F7
+	MOVD    $sincoss1<>+0(SB), R1
+	FMOVD   0(R1), F3
+	MOVD    $sincoss0<>+0(SB), R1
+	FMOVD   0(R1), F2
+	WFCHDBS V2, V5, V2
+	BEQ     L18
+	MOVD    $sincosrpi2<>+0(SB), R1
+	FMOVD   0(R1), F3
+	MOVD    $sincosxadd<>+0(SB), R1
+	FMOVD   0(R1), F2
+	WFMSDB  V0, V3, V2, V3
+	FMOVD   0(R1), F6
+	FADD    F3, F6
+	MOVD    $sincosxpi2h<>+0(SB), R1
+	FMOVD   0(R1), F2
+	FMSUB   F2, F6, F0, F0
+	MOVD    $sincosxpi2m<>+0(SB), R1
+	FMOVD   0(R1), F4
+	FMADD   F4, F6, F0, F0
+	MOVD    $sincosxpi2l<>+0(SB), R1
+	WFMDB   V0, V0, V1
+	FMOVD   0(R1), F7
+	WFMDB   V1, V1, V2
+	WORD    $0xB3CD0013     //lgdr  %r1,%f3
+	MOVD    $sincosxlim<>+0(SB), R2
+	WORD    $0xA7110001     //tmll  %r1,1
+	BEQ     L6
+	FMOVD   0(R2), F0
+	WFCHDBS V0, V5, V0
+	BNE     L14
+	MOVD    $sincosc7<>+0(SB), R2
+	FMOVD   0(R2), F0
+	MOVD    $sincosc6<>+0(SB), R2
+	FMOVD   0(R2), F4
+	MOVD    $sincosc5<>+0(SB), R2
+	WFMADB  V1, V0, V4, V0
+	FMOVD   0(R2), F6
+	MOVD    $sincosc4<>+0(SB), R2
+	WFMADB  V1, V0, V6, V0
+	FMOVD   0(R2), F4
+	MOVD    $sincosc2<>+0(SB), R2
+	FMOVD   0(R2), F6
+	WFMADB  V2, V4, V6, V4
+	MOVD    $sincosc3<>+0(SB), R2
+	FMOVD   0(R2), F3
+	MOVD    $sincosc1<>+0(SB), R2
+	WFMADB  V2, V0, V3, V0
+	FMOVD   0(R2), F6
+	WFMADB  V1, V4, V6, V4
+	WORD    $0xA7110002     //tmll  %r1,2
+	WFMADB  V2, V0, V4, V0
+	MOVD    $sincosc0<>+0(SB), R1
+	FMOVD   0(R1), F2
+	WFMADB  V1, V0, V2, V0
+	BNE     L15
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L6:
+	FMOVD   0(R2), F4
+	WFCHDBS V4, V5, V4
+	BNE     L14
+	MOVD    $sincoss7<>+0(SB), R2
+	FMOVD   0(R2), F4
+	MOVD    $sincoss6<>+0(SB), R2
+	FMOVD   0(R2), F3
+	MOVD    $sincoss5<>+0(SB), R2
+	WFMADB  V1, V4, V3, V4
+	WFMADB  V6, V7, V0, V6
+	FMOVD   0(R2), F0
+	MOVD    $sincoss4<>+0(SB), R2
+	FMADD   F4, F1, F0, F0
+	FMOVD   0(R2), F3
+	MOVD    $sincoss2<>+0(SB), R2
+	FMOVD   0(R2), F4
+	MOVD    $sincoss3<>+0(SB), R2
+	WFMADB  V2, V3, V4, V3
+	FMOVD   0(R2), F4
+	MOVD    $sincoss1<>+0(SB), R2
+	WFMADB  V2, V0, V4, V0
+	FMOVD   0(R2), F4
+	WFMADB  V1, V3, V4, V3
+	FNEG    F6, F4
+	WFMADB  V2, V0, V3, V2
+	WFMDB   V4, V1, V0
+	WORD    $0xA7110002     //tmll  %r1,2
+	WFMSDB  V0, V2, V6, V0
+	BNE     L15
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L14:
+	MOVD    $sincosxnan<>+0(SB), R1
+	FMOVD   0(R1), F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L18:
+	WFMDB   V0, V0, V2
+	WFMADB  V2, V4, V1, V4
+	WFMDB   V2, V2, V1
+	WFMADB  V2, V4, V18, V4
+	WFMADB  V1, V6, V16, V6
+	WFMADB  V1, V4, V7, V4
+	WFMADB  V2, V6, V3, V6
+	FMUL    F0, F2
+	WFMADB  V1, V4, V6, V4
+	FMADD   F4, F2, F0, F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L17:
+	FNEG    F0, F5
+	BR      L2
+L15:
+	FNEG    F0, F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+
+sinIsZero:
+	FMOVD   F0, ret+8(FP)
+	RET
+
+// Cos returns the cosine of the radian argument.
+//
+// Special cases are:
+//      Cos(±Inf) = NaN
+//      Cos(NaN) = NaN
+// The algorithm used is minimax polynomial approximation.
+// with coefficients determined with a Remez exchange algorithm.
+
+TEXT ·cosAsm(SB),NOSPLIT,$0-16
+	FMOVD   x+0(FP), F0
+	WORD    $0xB3120000     //ltdbr %f0,%f0
+	BLTU    L35
+	FMOVD   F0, F1
+L21:
+	MOVD    $sincosc7<>+0(SB), R1
+	FMOVD   0(R1), F4
+	MOVD    $sincosc6<>+0(SB), R1
+	VLEG    $0, 0(R1), V20
+	MOVD    $sincosc5<>+0(SB), R1
+	VLEG    $0, 0(R1), V18
+	MOVD    $sincosc4<>+0(SB), R1
+	FMOVD   0(R1), F6
+	MOVD    $sincosc2<>+0(SB), R1
+	VLEG    $0, 0(R1), V16
+	MOVD    $sincosc3<>+0(SB), R1
+	FMOVD   0(R1), F7
+	MOVD    $sincosc1<>+0(SB), R1
+	FMOVD   0(R1), F5
+	MOVD    $sincosrpi2<>+0(SB), R1
+	FMOVD   0(R1), F2
+	MOVD    $sincosxadd<>+0(SB), R1
+	FMOVD   0(R1), F3
+	MOVD    $sincoss0<>+0(SB), R1
+	WFMSDB  V0, V2, V3, V2
+	FMOVD   0(R1), F3
+	WFCHDBS V3, V1, V3
+	WORD    $0xB3CD0012     //lgdr %r1,%f2
+	BEQ     L36
+	MOVD    $sincosxadd<>+0(SB), R2
+	FMOVD   0(R2), F4
+	FADD    F2, F4
+	MOVD    $sincosxpi2h<>+0(SB), R2
+	FMOVD   0(R2), F2
+	WFMSDB  V4, V2, V0, V2
+	MOVD    $sincosxpi2m<>+0(SB), R2
+	FMOVD   0(R2), F0
+	WFMADB  V4, V0, V2, V0
+	MOVD    $sincosxpi2l<>+0(SB), R2
+	WFMDB   V0, V0, V2
+	FMOVD   0(R2), F5
+	WFMDB   V2, V2, V6
+	MOVD    $sincosxlim<>+0(SB), R2
+	WORD    $0xA7110001     //tmll %r1,1
+	BNE     L25
+	FMOVD   0(R2), F0
+	WFCHDBS V0, V1, V0
+	BNE     L33
+	MOVD    $sincosc7<>+0(SB), R2
+	FMOVD   0(R2), F0
+	MOVD    $sincosc6<>+0(SB), R2
+	FMOVD   0(R2), F4
+	MOVD    $sincosc5<>+0(SB), R2
+	WFMADB  V2, V0, V4, V0
+	FMOVD   0(R2), F1
+	MOVD    $sincosc4<>+0(SB), R2
+	WFMADB  V2, V0, V1, V0
+	FMOVD   0(R2), F4
+	MOVD    $sincosc2<>+0(SB), R2
+	FMOVD   0(R2), F1
+	WFMADB  V6, V4, V1, V4
+	MOVD    $sincosc3<>+0(SB), R2
+	FMOVD   0(R2), F3
+	MOVD    $sincosc1<>+0(SB), R2
+	WFMADB  V6, V0, V3, V0
+	FMOVD   0(R2), F1
+	WFMADB  V2, V4, V1, V4
+	WORD    $0xA7110002     //tmll %r1,2
+	WFMADB  V6, V0, V4, V0
+	MOVD    $sincosc0<>+0(SB), R1
+	FMOVD   0(R1), F4
+	WFMADB  V2, V0, V4, V0
+	BNE     L34
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L25:
+	FMOVD   0(R2), F3
+	WFCHDBS V3, V1, V1
+	BNE     L33
+	MOVD    $sincoss7<>+0(SB), R2
+	FMOVD   0(R2), F1
+	MOVD    $sincoss6<>+0(SB), R2
+	FMOVD   0(R2), F3
+	MOVD    $sincoss5<>+0(SB), R2
+	WFMADB  V2, V1, V3, V1
+	FMOVD   0(R2), F3
+	MOVD    $sincoss4<>+0(SB), R2
+	WFMADB  V2, V1, V3, V1
+	FMOVD   0(R2), F3
+	MOVD    $sincoss2<>+0(SB), R2
+	FMOVD   0(R2), F7
+	WFMADB  V6, V3, V7, V3
+	MOVD    $sincoss3<>+0(SB), R2
+	FMADD   F5, F4, F0, F0
+	FMOVD   0(R2), F4
+	MOVD    $sincoss1<>+0(SB), R2
+	FMADD   F1, F6, F4, F4
+	FMOVD   0(R2), F1
+	FMADD   F3, F2, F1, F1
+	FMUL    F0, F2
+	WFMADB  V6, V4, V1, V6
+	WORD    $0xA7110002     //tmll  %r1,2
+	FMADD   F6, F2, F0, F0
+	BNE     L34
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L33:
+	MOVD    $sincosxnan<>+0(SB), R1
+	FMOVD   0(R1), F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L36:
+	FMUL    F0, F0
+	MOVD    $sincosc0<>+0(SB), R1
+	WFMDB   V0, V0, V1
+	WFMADB  V0, V4, V20, V4
+	WFMADB  V1, V6, V16, V6
+	WFMADB  V0, V4, V18, V4
+	WFMADB  V0, V6, V5, V6
+	WFMADB  V1, V4, V7, V4
+	FMOVD   0(R1), F2
+	WFMADB  V1, V4, V6, V4
+	WFMADB  V0, V4, V2, V0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L35:
+	FNEG    F0, F1
+	BR      L21
+L34:
+	FNEG    F0, F0
+	FMOVD   F0, ret+8(FP)
+	RET
diff --git a/src/math/sincos.go b/src/math/sincos.go
index 7180303..6e663d0 100644
--- a/src/math/sincos.go
+++ b/src/math/sincos.go
@@ -40,8 +40,8 @@ func sincos(x float64) (sin, cos float64) {
 	y := float64(j)      // integer part of x/(Pi/4), as float
 
 	if j&1 == 1 { // map zeros to origin
-		j += 1
-		y += 1
+		j++
+		y++
 	}
 	j &= 7     // octant modulo 2Pi radians (360 degrees)
 	if j > 3 { // reflect in x axis
diff --git a/src/math/sinh.go b/src/math/sinh.go
index 139b911..2bdd7b1 100644
--- a/src/math/sinh.go
+++ b/src/math/sinh.go
@@ -22,7 +22,9 @@ package math
 //	Sinh(±0) = ±0
 //	Sinh(±Inf) = ±Inf
 //	Sinh(NaN) = NaN
-func Sinh(x float64) float64 {
+func Sinh(x float64) float64
+
+func sinh(x float64) float64 {
 	// The coefficients are #2029 from Hart & Cheney. (20.36D)
 	const (
 		P0 = -0.6307673640497716991184787251e+6
@@ -66,7 +68,9 @@ func Sinh(x float64) float64 {
 //	Cosh(±0) = 1
 //	Cosh(±Inf) = +Inf
 //	Cosh(NaN) = NaN
-func Cosh(x float64) float64 {
+func Cosh(x float64) float64
+
+func cosh(x float64) float64 {
 	if x < 0 {
 		x = -x
 	}
diff --git a/src/math/sinh_s390x.s b/src/math/sinh_s390x.s
new file mode 100644
index 0000000..e492415
--- /dev/null
+++ b/src/math/sinh_s390x.s
@@ -0,0 +1,261 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+
+#include "textflag.h"
+
+// Constants
+DATA sinhrodataL21<>+0(SB)/8, $0.231904681384629956E-16
+DATA sinhrodataL21<>+8(SB)/8, $0.693147180559945286E+00
+DATA sinhrodataL21<>+16(SB)/8, $704.E0
+GLOBL sinhrodataL21<>+0(SB), RODATA, $24
+DATA sinhrlog2<>+0(SB)/8, $0x3ff7154760000000
+GLOBL sinhrlog2<>+0(SB), RODATA, $8
+DATA sinhxinf<>+0(SB)/8, $0x7ff0000000000000
+GLOBL sinhxinf<>+0(SB), RODATA, $8
+DATA sinhxinit<>+0(SB)/8, $0x3ffb504f333f9de6
+GLOBL sinhxinit<>+0(SB), RODATA, $8
+DATA sinhxlim1<>+0(SB)/8, $800.E0
+GLOBL sinhxlim1<>+0(SB), RODATA, $8
+DATA sinhxadd<>+0(SB)/8, $0xc3200001610007fb
+GLOBL sinhxadd<>+0(SB), RODATA, $8
+DATA sinhx4ff<>+0(SB)/8, $0x4ff0000000000000
+GLOBL sinhx4ff<>+0(SB), RODATA, $8
+
+// Minimax polynomial approximations
+DATA sinhe0<>+0(SB)/8, $0.11715728752538099300E+01
+GLOBL sinhe0<>+0(SB), RODATA, $8
+DATA sinhe1<>+0(SB)/8, $0.11715728752538099300E+01
+GLOBL sinhe1<>+0(SB), RODATA, $8
+DATA sinhe2<>+0(SB)/8, $0.58578643762688526692E+00
+GLOBL sinhe2<>+0(SB), RODATA, $8
+DATA sinhe3<>+0(SB)/8, $0.19526214587563004497E+00
+GLOBL sinhe3<>+0(SB), RODATA, $8
+DATA sinhe4<>+0(SB)/8, $0.48815536475176217404E-01
+GLOBL sinhe4<>+0(SB), RODATA, $8
+DATA sinhe5<>+0(SB)/8, $0.97631072948627397816E-02
+GLOBL sinhe5<>+0(SB), RODATA, $8
+DATA sinhe6<>+0(SB)/8, $0.16271839297756073153E-02
+GLOBL sinhe6<>+0(SB), RODATA, $8
+DATA sinhe7<>+0(SB)/8, $0.23245485387271142509E-03
+GLOBL sinhe7<>+0(SB), RODATA, $8
+DATA sinhe8<>+0(SB)/8, $0.29080955860869629131E-04
+GLOBL sinhe8<>+0(SB), RODATA, $8
+DATA sinhe9<>+0(SB)/8, $0.32311267157667725278E-05
+GLOBL sinhe9<>+0(SB), RODATA, $8
+
+// Sinh returns the hyperbolic sine of the argument.
+//
+// Special cases are:
+//      Sinh(±0) = ±0
+//      Sinh(±Inf) = ±Inf
+//      Sinh(NaN) = NaN
+// The algorithm used is minimax polynomial approximation
+// with coefficients determined with a Remez exchange algorithm.
+
+TEXT ·sinhAsm(SB),NOSPLIT,$0-16
+	FMOVD   x+0(FP), F0
+	//specail case Sinh(±0) = ±0
+	FMOVD   $(0.0), F1
+	FCMPU   F0, F1
+	BEQ     sinhIsZero
+	//specail case Sinh(±Inf = ±Inf
+	FMOVD   $1.797693134862315708145274237317043567981e+308, F1
+	FCMPU   F1, F0
+	BLEU    sinhIsInf
+	FMOVD   $-1.797693134862315708145274237317043567981e+308, F1
+	FCMPU   F1, F0
+	BGT             sinhIsInf
+
+	MOVD    $sinhrodataL21<>+0(SB), R5
+	WORD    $0xB3120000     //ltdbr %f0,%f0
+	MOVD    sinhxinit<>+0(SB), R1
+	FMOVD   F0, F4
+	MOVD    R1, R3
+	BLTU    L19
+	FMOVD   F0, F2
+L2:
+	WORD    $0xED205010     //cdb %f2,.L22-.L21(%r5)
+	BYTE    $0x00
+	BYTE    $0x19
+	BGE     L15     //jnl   .L15
+	BVS     L15
+	WFCEDBS V2, V2, V0
+	BEQ     L20
+L12:
+	FMOVD   F4, F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L15:
+	WFCEDBS V2, V2, V0
+	BVS     L12
+	MOVD    $sinhxlim1<>+0(SB), R2
+	FMOVD   0(R2), F0
+	WFCHDBS V0, V2, V0
+	BEQ     L6
+	WFCHEDBS        V4, V2, V6
+	MOVD    $sinhxinf<>+0(SB), R1
+	FMOVD   0(R1), F0
+	BNE     LEXITTAGsinh
+	WFCHDBS V2, V4, V2
+	BNE     L16
+	FNEG    F0, F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L19:
+	FNEG    F0, F2
+	BR      L2
+L6:
+	MOVD    $sinhxadd<>+0(SB), R2
+	FMOVD   0(R2), F0
+	MOVD    sinhrlog2<>+0(SB), R2
+	WORD    $0xB3C10062     //ldgr  %f6,%r2
+	WFMSDB  V4, V6, V0, V16
+	FMOVD   sinhrodataL21<>+8(SB), F6
+	WFADB   V0, V16, V0
+	FMOVD   sinhrodataL21<>+0(SB), F3
+	WFMSDB  V0, V6, V4, V6
+	MOVD    $sinhe9<>+0(SB), R2
+	WFMADB  V0, V3, V6, V0
+	FMOVD   0(R2), F1
+	MOVD    $sinhe7<>+0(SB), R2
+	WFMDB   V0, V0, V6
+	FMOVD   0(R2), F5
+	MOVD    $sinhe8<>+0(SB), R2
+	FMOVD   0(R2), F3
+	MOVD    $sinhe6<>+0(SB), R2
+	WFMADB  V6, V1, V5, V1
+	FMOVD   0(R2), F5
+	MOVD    $sinhe5<>+0(SB), R2
+	FMOVD   0(R2), F7
+	MOVD    $sinhe3<>+0(SB), R2
+	WFMADB  V6, V3, V5, V3
+	FMOVD   0(R2), F5
+	MOVD    $sinhe4<>+0(SB), R2
+	WFMADB  V6, V7, V5, V7
+	FMOVD   0(R2), F5
+	MOVD    $sinhe2<>+0(SB), R2
+	VLEG    $0, 0(R2), V20
+	WFMDB   V6, V6, V18
+	WFMADB  V6, V5, V20, V5
+	WFMADB  V1, V18, V7, V1
+	FNEG    F0, F0
+	WFMADB  V3, V18, V5, V3
+	MOVD    $sinhe1<>+0(SB), R3
+	WFCEDBS V2, V4, V2
+	FMOVD   0(R3), F5
+	MOVD    $sinhe0<>+0(SB), R3
+	WFMADB  V6, V1, V5, V1
+	FMOVD   0(R3), F5
+	VLGVG   $0, V16, R2
+	WFMADB  V6, V3, V5, V6
+	RLL     $3, R2, R2
+	WORD    $0xEC12000F     //risbgn %r1,%r2,64-64+0,64-64+0+16-1,64-0-16
+	BYTE    $0x30
+	BYTE    $0x59
+	BEQ     L9
+	WFMSDB  V0, V1, V6, V0
+	MOVD    $sinhx4ff<>+0(SB), R3
+	FNEG    F0, F0
+	FMOVD   0(R3), F2
+	FMUL    F2, F0
+	ANDW    $0xFFFF, R2
+	WORD    $0xA53FEFB6     //llill %r3,61366
+	SUBW    R2, R3, R2
+	WORD    $0xEC12000F     //risbgn %r1,%r2,64-64+0,64-64+0+16-1,64-0-16
+	BYTE    $0x30
+	BYTE    $0x59
+	WORD    $0xB3C10021     //ldgr %f2,%r1
+	FMUL    F2, F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L20:
+	MOVD    $sinhxadd<>+0(SB), R2
+	FMOVD   0(R2), F2
+	MOVD    sinhrlog2<>+0(SB), R2
+	WORD    $0xB3C10002     //ldgr  %f0,%r2
+	WFMSDB  V4, V0, V2, V6
+	FMOVD   sinhrodataL21<>+8(SB), F0
+	FADD    F6, F2
+	MOVD    $sinhe9<>+0(SB), R2
+	FMSUB   F0, F2, F4, F4
+	FMOVD   0(R2), F1
+	FMOVD   sinhrodataL21<>+0(SB), F3
+	MOVD    $sinhe7<>+0(SB), R2
+	FMADD   F3, F2, F4, F4
+	FMOVD   0(R2), F0
+	MOVD    $sinhe8<>+0(SB), R2
+	WFMDB   V4, V4, V2
+	FMOVD   0(R2), F3
+	MOVD    $sinhe6<>+0(SB), R2
+	FMOVD   0(R2), F5
+	WORD    $0xB3CD0026     //lgdr %r2,%f6
+	RLL     $3, R2, R2
+	WORD    $0xEC12000F     //risbgn %r1,%r2,64-64+0,64-64+0+16-1,64-0-16
+	BYTE    $0x30
+	BYTE    $0x59
+	WFMADB  V2, V1, V0, V1
+	WORD    $0xB3C10001     //ldgr  %f0,%r1
+	MOVD    $sinhe5<>+0(SB), R1
+	WFMADB  V2, V3, V5, V3
+	FMOVD   0(R1), F5
+	MOVD    $sinhe3<>+0(SB), R1
+	FMOVD   0(R1), F6
+	WFMDB   V2, V2, V7
+	WFMADB  V2, V5, V6, V5
+	WORD    $0xA7487FB6     //lhi %r4,32694
+	FNEG    F4, F4
+	ANDW    $0xFFFF, R2
+	SUBW    R2, R4, R2
+	WORD    $0xEC32000F     //risbgn %r3,%r2,64-64+0,64-64+0+16-1,64-0-16
+	BYTE    $0x30
+	BYTE    $0x59
+	WORD    $0xB3C10063     //ldgr  %f6,%r3
+	WFADB   V0, V6, V16
+	MOVD    $sinhe4<>+0(SB), R1
+	WFMADB  V1, V7, V5, V1
+	WFMDB   V4, V16, V4
+	FMOVD   0(R1), F5
+	MOVD    $sinhe2<>+0(SB), R1
+	VLEG    $0, 0(R1), V16
+	MOVD    $sinhe1<>+0(SB), R1
+	WFMADB  V2, V5, V16, V5
+	VLEG    $0, 0(R1), V16
+	WFMADB  V3, V7, V5, V3
+	WFMADB  V2, V1, V16, V1
+	FSUB    F6, F0
+	FMUL    F1, F4
+	MOVD    $sinhe0<>+0(SB), R1
+	FMOVD   0(R1), F6
+	WFMADB  V2, V3, V6, V2
+	WFMADB  V0, V2, V4, V0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L9:
+	WFMADB  V0, V1, V6, V0
+	MOVD    $sinhx4ff<>+0(SB), R3
+	FMOVD   0(R3), F2
+	FMUL    F2, F0
+	WORD    $0xA72AF000     //ahi   %r2,-4096
+	WORD    $0xEC12000F     //risbgn %r1,%r2,64-64+0,64-64+0+16-1,64-0-16
+	BYTE    $0x30
+	BYTE    $0x59
+	WORD    $0xB3C10021     //ldgr %f2,%r1
+	FMUL    F2, F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L16:
+	FMOVD   F0, ret+8(FP)
+	RET
+
+LEXITTAGsinh:
+sinhIsInf:
+sinhIsZero:
+	FMOVD   F0, ret+8(FP)
+	RET
diff --git a/src/math/sinh_stub.s b/src/math/sinh_stub.s
new file mode 100644
index 0000000..4caaa0c
--- /dev/null
+++ b/src/math/sinh_stub.s
@@ -0,0 +1,17 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build 386 amd64 amd64p32 arm
+
+#include "textflag.h"
+
+TEXT ·Sinh(SB),NOSPLIT,$0
+	JMP ·sinh(SB)
+
+TEXT ·Cosh(SB),NOSPLIT,$0
+	JMP ·cosh(SB)
+
+TEXT ·Tanh(SB),NOSPLIT,$0
+	JMP ·tanh(SB)
+
diff --git a/src/math/sqrt_amd64.s b/src/math/sqrt_amd64.s
index f8d825d..1102903 100644
--- a/src/math/sqrt_amd64.s
+++ b/src/math/sqrt_amd64.s
@@ -5,7 +5,8 @@
 #include "textflag.h"
 
 // func Sqrt(x float64) float64
-TEXT ·Sqrt(SB),NOSPLIT,$0
+TEXT ·Sqrt(SB), NOSPLIT, $0
+	XORPS  X0, X0 // break dependency
 	SQRTSD x+0(FP), X0
-	MOVSD X0, ret+8(FP)
+	MOVSD  X0, ret+8(FP)
 	RET
diff --git a/src/math/sqrt_mipsx.s b/src/math/sqrt_mipsx.s
new file mode 100644
index 0000000..1b27d49
--- /dev/null
+++ b/src/math/sqrt_mipsx.s
@@ -0,0 +1,14 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "textflag.h"
+
+// func Sqrt(x float64) float64
+TEXT ·Sqrt(SB),NOSPLIT,$0
+	MOVD	x+0(FP), F0
+	SQRTD	F0, F0
+	MOVD	F0, ret+8(FP)
+	RET
diff --git a/src/math/stubs_arm64.s b/src/math/stubs_arm64.s
index 04de911..d8c9aa8 100644
--- a/src/math/stubs_arm64.s
+++ b/src/math/stubs_arm64.s
@@ -18,33 +18,18 @@ TEXT ·Atan2(SB),NOSPLIT,$0
 TEXT ·Atan(SB),NOSPLIT,$0
 	B ·atan(SB)
 
-TEXT ·Dim(SB),NOSPLIT,$0
-	B ·dim(SB)
-
-TEXT ·Min(SB),NOSPLIT,$0
-	B ·min(SB)
-
-TEXT ·Max(SB),NOSPLIT,$0
-	B ·max(SB)
-
 TEXT ·Exp2(SB),NOSPLIT,$0
 	B ·exp2(SB)
 
+TEXT ·Cosh(SB),NOSPLIT,$0
+	B ·cosh(SB)
+
 TEXT ·Expm1(SB),NOSPLIT,$0
 	B ·expm1(SB)
 
 TEXT ·Exp(SB),NOSPLIT,$0
 	B ·exp(SB)
 
-TEXT ·Floor(SB),NOSPLIT,$0
-	B ·floor(SB)
-
-TEXT ·Ceil(SB),NOSPLIT,$0
-	B ·ceil(SB)
-
-TEXT ·Trunc(SB),NOSPLIT,$0
-	B ·trunc(SB)
-
 TEXT ·Frexp(SB),NOSPLIT,$0
 	B ·frexp(SB)
 
@@ -66,9 +51,6 @@ TEXT ·Log1p(SB),NOSPLIT,$0
 TEXT ·Log(SB),NOSPLIT,$0
 	B ·log(SB)
 
-TEXT ·Modf(SB),NOSPLIT,$0
-	B ·modf(SB)
-
 TEXT ·Mod(SB),NOSPLIT,$0
 	B ·mod(SB)
 
@@ -81,8 +63,14 @@ TEXT ·Sincos(SB),NOSPLIT,$0
 TEXT ·Sin(SB),NOSPLIT,$0
 	B ·sin(SB)
 
+TEXT ·Sinh(SB),NOSPLIT,$0
+	B ·sinh(SB)
+
 TEXT ·Cos(SB),NOSPLIT,$0
 	B ·cos(SB)
 
 TEXT ·Tan(SB),NOSPLIT,$0
 	B ·tan(SB)
+
+TEXT ·Tanh(SB),NOSPLIT,$0
+	B ·tanh(SB)
diff --git a/src/math/stubs_mips64x.s b/src/math/stubs_mips64x.s
index 97e6e4c..21df5cc 100644
--- a/src/math/stubs_mips64x.s
+++ b/src/math/stubs_mips64x.s
@@ -81,11 +81,20 @@ TEXT ·Sincos(SB),NOSPLIT,$0
 TEXT ·Sin(SB),NOSPLIT,$0
 	JMP ·sin(SB)
 
+TEXT ·Sinh(SB),NOSPLIT,$0
+	JMP ·sinh(SB)
+
 TEXT ·Cos(SB),NOSPLIT,$0
 	JMP ·cos(SB)
 
+TEXT ·Cosh(SB),NOSPLIT,$0
+	JMP ·cosh(SB)
+
 TEXT ·Sqrt(SB),NOSPLIT,$0
 	JMP ·sqrt(SB)
 
 TEXT ·Tan(SB),NOSPLIT,$0
 	JMP ·tan(SB)
+
+TEXT ·Tanh(SB),NOSPLIT,$0
+	JMP ·tanh(SB)
diff --git a/src/math/stubs_mipsx.s b/src/math/stubs_mipsx.s
new file mode 100644
index 0000000..b869768
--- /dev/null
+++ b/src/math/stubs_mipsx.s
@@ -0,0 +1,98 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "textflag.h"
+
+TEXT ·Asin(SB),NOSPLIT,$0
+	JMP	·asin(SB)
+
+TEXT ·Acos(SB),NOSPLIT,$0
+	JMP	·acos(SB)
+
+TEXT ·Atan2(SB),NOSPLIT,$0
+	JMP	·atan2(SB)
+
+TEXT ·Atan(SB),NOSPLIT,$0
+	JMP	·atan(SB)
+
+TEXT ·Dim(SB),NOSPLIT,$0
+	JMP	·dim(SB)
+
+TEXT ·Min(SB),NOSPLIT,$0
+	JMP	·min(SB)
+
+TEXT ·Max(SB),NOSPLIT,$0
+	JMP	·max(SB)
+
+TEXT ·Exp2(SB),NOSPLIT,$0
+	JMP	·exp2(SB)
+
+TEXT ·Expm1(SB),NOSPLIT,$0
+	JMP	·expm1(SB)
+
+TEXT ·Exp(SB),NOSPLIT,$0
+	JMP	·exp(SB)
+
+TEXT ·Floor(SB),NOSPLIT,$0
+	JMP	·floor(SB)
+
+TEXT ·Ceil(SB),NOSPLIT,$0
+	JMP	·ceil(SB)
+
+TEXT ·Trunc(SB),NOSPLIT,$0
+	JMP	·trunc(SB)
+
+TEXT ·Frexp(SB),NOSPLIT,$0
+	JMP	·frexp(SB)
+
+TEXT ·Hypot(SB),NOSPLIT,$0
+	JMP	·hypot(SB)
+
+TEXT ·Ldexp(SB),NOSPLIT,$0
+	JMP	·ldexp(SB)
+
+TEXT ·Log10(SB),NOSPLIT,$0
+	JMP	·log10(SB)
+
+TEXT ·Log2(SB),NOSPLIT,$0
+	JMP	·log2(SB)
+
+TEXT ·Log1p(SB),NOSPLIT,$0
+	JMP	·log1p(SB)
+
+TEXT ·Log(SB),NOSPLIT,$0
+	JMP	·log(SB)
+
+TEXT ·Modf(SB),NOSPLIT,$0
+	JMP	·modf(SB)
+
+TEXT ·Mod(SB),NOSPLIT,$0
+	JMP	·mod(SB)
+
+TEXT ·Remainder(SB),NOSPLIT,$0
+	JMP	·remainder(SB)
+
+TEXT ·Sincos(SB),NOSPLIT,$0
+	JMP	·sincos(SB)
+
+TEXT ·Sin(SB),NOSPLIT,$0
+	JMP	·sin(SB)
+
+TEXT ·Sinh(SB),NOSPLIT,$0
+        JMP ·sinh(SB)
+
+TEXT ·Cos(SB),NOSPLIT,$0
+	JMP	·cos(SB)
+
+TEXT ·Cosh(SB),NOSPLIT,$0
+        JMP ·cosh(SB)
+
+TEXT ·Tan(SB),NOSPLIT,$0
+	JMP	·tan(SB)
+
+TEXT ·Tanh(SB),NOSPLIT,$0
+        JMP ·tanh(SB)
+
diff --git a/src/math/stubs_ppc64x.s b/src/math/stubs_ppc64x.s
index a57357e..b622016 100644
--- a/src/math/stubs_ppc64x.s
+++ b/src/math/stubs_ppc64x.s
@@ -36,15 +36,6 @@ TEXT ·Expm1(SB),NOSPLIT,$0
 TEXT ·Exp(SB),NOSPLIT,$0
 	BR ·exp(SB)
 
-TEXT ·Floor(SB),NOSPLIT,$0
-	BR ·floor(SB)
-
-TEXT ·Ceil(SB),NOSPLIT,$0
-	BR ·ceil(SB)
-
-TEXT ·Trunc(SB),NOSPLIT,$0
-	BR ·trunc(SB)
-
 TEXT ·Frexp(SB),NOSPLIT,$0
 	BR ·frexp(SB)
 
@@ -81,8 +72,17 @@ TEXT ·Sincos(SB),NOSPLIT,$0
 TEXT ·Sin(SB),NOSPLIT,$0
 	BR ·sin(SB)
 
+TEXT ·Sinh(SB),NOSPLIT,$0
+	BR ·sinh(SB)
+
 TEXT ·Cos(SB),NOSPLIT,$0
 	BR ·cos(SB)
 
+TEXT ·Cosh(SB),NOSPLIT,$0
+	BR ·cosh(SB)
+
 TEXT ·Tan(SB),NOSPLIT,$0
 	BR ·tan(SB)
+
+TEXT ·Tanh(SB),NOSPLIT,$0
+	BR ·tanh(SB)
diff --git a/src/math/stubs_s390x.s b/src/math/stubs_s390x.s
index 7686844..8da55c5 100644
--- a/src/math/stubs_s390x.s
+++ b/src/math/stubs_s390x.s
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "../runtime/textflag.h"
+#include "textflag.h"
 
 TEXT ·Asin(SB),NOSPLIT,$0
 	BR ·asin(SB)
@@ -25,15 +25,6 @@ TEXT ·Expm1(SB),NOSPLIT,$0
 TEXT ·Exp(SB),NOSPLIT,$0
 	BR ·exp(SB)
 
-TEXT ·Floor(SB),NOSPLIT,$0
-	BR ·floor(SB)
-
-TEXT ·Ceil(SB),NOSPLIT,$0
-	BR ·ceil(SB)
-
-TEXT ·Trunc(SB),NOSPLIT,$0
-	BR ·trunc(SB)
-
 TEXT ·Frexp(SB),NOSPLIT,$0
 	BR ·frexp(SB)
 
@@ -43,9 +34,6 @@ TEXT ·Hypot(SB),NOSPLIT,$0
 TEXT ·Ldexp(SB),NOSPLIT,$0
 	BR ·ldexp(SB)
 
-TEXT ·Log10(SB),NOSPLIT,$0
-	BR ·log10(SB)
-
 TEXT ·Log2(SB),NOSPLIT,$0
 	BR ·log2(SB)
 
@@ -67,11 +55,154 @@ TEXT ·Remainder(SB),NOSPLIT,$0
 TEXT ·Sincos(SB),NOSPLIT,$0
 	BR ·sincos(SB)
 
-TEXT ·Sin(SB),NOSPLIT,$0
-	BR ·sin(SB)
+TEXT ·Tan(SB),NOSPLIT,$0
+	BR ·tan(SB)
+
+//if go assembly use vector instruction
+TEXT ·hasVectorFacility(SB),NOSPLIT,$24-1
+	MOVD    $x-24(SP), R1
+	XC      $24, 0(R1), 0(R1) // clear the storage
+	MOVD    $2, R0            // R0 is the number of double words stored -1
+	WORD    $0xB2B01000       // STFLE 0(R1)
+	XOR     R0, R0            // reset the value of R0
+	MOVBZ   z-8(SP), R1
+	AND     $0x40, R1
+	BEQ     novector
+vectorinstalled:
+	// check if the vector instruction has been enabled
+	VLEIB   $0, $0xF, V16
+	VLGVB   $0, V16, R1
+	CMPBNE  R1, $0xF, novector
+	MOVB    $1, ret+0(FP) // have vx
+	RET
+novector:
+	MOVB    $0, ret+0(FP) // no vx
+	RET
+
+TEXT ·Log10(SB),NOSPLIT,$0
+	MOVD    log10vectorfacility+0x00(SB),R1
+	BR      (R1)
+
+TEXT ·log10TrampolineSetup(SB),NOSPLIT, $0
+	MOVB    ·hasVX(SB), R1
+	CMPBEQ  R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVD    $log10vectorfacility+0x00(SB), R1
+	MOVD    $·log10(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·log10(SB)
+vectorimpl:
+	MOVD    $log10vectorfacility+0x00(SB), R1
+	MOVD    $·log10Asm(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·log10Asm(SB)
+
+GLOBL log10vectorfacility+0x00(SB), NOPTR, $8
+DATA log10vectorfacility+0x00(SB)/8, $·log10TrampolineSetup(SB)
+
 
 TEXT ·Cos(SB),NOSPLIT,$0
-	BR ·cos(SB)
+	MOVD    cosvectorfacility+0x00(SB),R1
+	BR      (R1)
+
+TEXT ·cosTrampolineSetup(SB),NOSPLIT, $0
+	MOVB    ·hasVX(SB), R1
+	CMPBEQ  R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVD    $cosvectorfacility+0x00(SB), R1
+	MOVD    $·cos(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·cos(SB)
+vectorimpl:
+	MOVD    $cosvectorfacility+0x00(SB), R1
+	MOVD    $·cosAsm(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·cosAsm(SB)
+
+GLOBL cosvectorfacility+0x00(SB), NOPTR, $8
+DATA cosvectorfacility+0x00(SB)/8, $·cosTrampolineSetup(SB)
+
+
+TEXT ·Cosh(SB),NOSPLIT,$0
+	MOVD    coshvectorfacility+0x00(SB),R1
+	BR      (R1)
+
+TEXT ·coshTrampolineSetup(SB),NOSPLIT, $0
+	MOVB    ·hasVX(SB), R1
+	CMPBEQ  R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVD    $coshvectorfacility+0x00(SB), R1
+	MOVD    $·cosh(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·cosh(SB)
+vectorimpl:
+	MOVD    $coshvectorfacility+0x00(SB), R1
+	MOVD    $·coshAsm(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·coshAsm(SB)
+
+GLOBL coshvectorfacility+0x00(SB), NOPTR, $8
+DATA coshvectorfacility+0x00(SB)/8, $·coshTrampolineSetup(SB)
+
+
+TEXT ·Sin(SB),NOSPLIT,$0
+	MOVD    sinvectorfacility+0x00(SB),R1
+	BR      (R1)
+
+TEXT ·sinTrampolineSetup(SB),NOSPLIT, $0
+	MOVB    ·hasVX(SB), R1
+	CMPBEQ  R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVD    $sinvectorfacility+0x00(SB), R1
+	MOVD    $·sin(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·sin(SB)
+vectorimpl:
+	MOVD    $sinvectorfacility+0x00(SB), R1
+	MOVD    $·sinAsm(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·sinAsm(SB)
+
+GLOBL sinvectorfacility+0x00(SB), NOPTR, $8
+DATA sinvectorfacility+0x00(SB)/8, $·sinTrampolineSetup(SB)
+
+
+TEXT ·Sinh(SB),NOSPLIT,$0
+	MOVD    sinhvectorfacility+0x00(SB),R1
+	BR      (R1)
+
+TEXT ·sinhTrampolineSetup(SB),NOSPLIT, $0
+	MOVB    ·hasVX(SB), R1
+	CMPBEQ  R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVD    $sinhvectorfacility+0x00(SB), R1
+	MOVD    $·sinh(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·sinh(SB)
+vectorimpl:
+	MOVD    $sinhvectorfacility+0x00(SB), R1
+	MOVD    $·sinhAsm(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·sinhAsm(SB)
+
+GLOBL sinhvectorfacility+0x00(SB), NOPTR, $8
+DATA sinhvectorfacility+0x00(SB)/8, $·sinhTrampolineSetup(SB)
+
+
+
+TEXT ·Tanh(SB),NOSPLIT,$0
+	MOVD    tanhvectorfacility+0x00(SB),R1
+	BR      (R1)
+
+TEXT ·tanhTrampolineSetup(SB),NOSPLIT, $0
+	MOVB    ·hasVX(SB), R1
+	CMPBEQ  R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVD    $tanhvectorfacility+0x00(SB), R1
+	MOVD    $·tanh(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·tanh(SB)
+vectorimpl:
+	MOVD    $tanhvectorfacility+0x00(SB), R1
+	MOVD    $·tanhAsm(SB), R2
+	MOVD    R2, 0(R1)
+	BR      ·tanhAsm(SB)
+
+GLOBL tanhvectorfacility+0x00(SB), NOPTR, $8
+DATA tanhvectorfacility+0x00(SB)/8, $·tanhTrampolineSetup(SB)
+
 
-TEXT ·Tan(SB),NOSPLIT,$0
-	BR ·tan(SB)
diff --git a/src/math/tan.go b/src/math/tan.go
index 285eff1..aa2fb37 100644
--- a/src/math/tan.go
+++ b/src/math/tan.go
@@ -108,8 +108,8 @@ func tan(x float64) float64 {
 
 	/* map zeros and singularities to origin */
 	if j&1 == 1 {
-		j += 1
-		y += 1
+		j++
+		y++
 	}
 
 	z := ((x - y*PI4A) - y*PI4B) - y*PI4C
diff --git a/src/math/tanh.go b/src/math/tanh.go
index cf0ffa1..eaa0e4c 100644
--- a/src/math/tanh.go
+++ b/src/math/tanh.go
@@ -71,7 +71,9 @@ var tanhQ = [...]float64{
 //	Tanh(±0) = ±0
 //	Tanh(±Inf) = ±1
 //	Tanh(NaN) = NaN
-func Tanh(x float64) float64 {
+func Tanh(x float64) float64
+
+func tanh(x float64) float64 {
 	const MAXLOG = 8.8029691931113054295988e+01 // log(2**127)
 	z := Abs(x)
 	switch {
diff --git a/src/math/tanh_s390x.s b/src/math/tanh_s390x.s
new file mode 100644
index 0000000..1b76c14
--- /dev/null
+++ b/src/math/tanh_s390x.s
@@ -0,0 +1,173 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+// Minimax polynomial approximations
+DATA tanhrodataL18<>+0(SB)/8, $-1.0
+DATA tanhrodataL18<>+8(SB)/8, $-2.0
+DATA tanhrodataL18<>+16(SB)/8, $1.0
+DATA tanhrodataL18<>+24(SB)/8, $2.0
+DATA tanhrodataL18<>+32(SB)/8, $0.20000000000000011868E+01
+DATA tanhrodataL18<>+40(SB)/8, $0.13333333333333341256E+01
+DATA tanhrodataL18<>+48(SB)/8, $0.26666666663549111502E+00
+DATA tanhrodataL18<>+56(SB)/8, $0.66666666658721844678E+00
+DATA tanhrodataL18<>+64(SB)/8, $0.88890217768964374821E-01
+DATA tanhrodataL18<>+72(SB)/8, $0.25397199429103821138E-01
+DATA tanhrodataL18<>+80(SB)/8, $-.346573590279972643E+00
+DATA tanhrodataL18<>+88(SB)/8, $20.E0
+GLOBL tanhrodataL18<>+0(SB), RODATA, $96
+
+// Constants
+DATA tanhrlog2<>+0(SB)/8, $0x4007154760000000
+GLOBL tanhrlog2<>+0(SB), RODATA, $8
+DATA tanhxadd<>+0(SB)/8, $0xc2f0000100003ff0
+GLOBL tanhxadd<>+0(SB), RODATA, $8
+DATA tanhxmone<>+0(SB)/8, $-1.0
+GLOBL tanhxmone<>+0(SB), RODATA, $8
+DATA tanhxzero<>+0(SB)/8, $0
+GLOBL tanhxzero<>+0(SB), RODATA, $8
+
+// Polynomial coefficients
+DATA tanhtab<>+0(SB)/8, $0.000000000000000000E+00
+DATA tanhtab<>+8(SB)/8, $-.171540871271399150E-01
+DATA tanhtab<>+16(SB)/8, $-.306597931864376363E-01
+DATA tanhtab<>+24(SB)/8, $-.410200970469965021E-01
+DATA tanhtab<>+32(SB)/8, $-.486343079978231466E-01
+DATA tanhtab<>+40(SB)/8, $-.538226193725835820E-01
+DATA tanhtab<>+48(SB)/8, $-.568439602538111520E-01
+DATA tanhtab<>+56(SB)/8, $-.579091847395528847E-01
+DATA tanhtab<>+64(SB)/8, $-.571909584179366341E-01
+DATA tanhtab<>+72(SB)/8, $-.548312665987204407E-01
+DATA tanhtab<>+80(SB)/8, $-.509471843643441085E-01
+DATA tanhtab<>+88(SB)/8, $-.456353588448863359E-01
+DATA tanhtab<>+96(SB)/8, $-.389755254243262365E-01
+DATA tanhtab<>+104(SB)/8, $-.310332908285244231E-01
+DATA tanhtab<>+112(SB)/8, $-.218623539150173528E-01
+DATA tanhtab<>+120(SB)/8, $-.115062908917949451E-01
+GLOBL tanhtab<>+0(SB), RODATA, $128
+
+// Tanh returns the hyperbolic tangent of the argument.
+//
+// Special cases are:
+//      Tanh(±0) = ±0
+//      Tanh(±Inf) = ±1
+//      Tanh(NaN) = NaN
+// The algorithm used is minimax polynomial approximation using a table of
+// polynomial coefficients determined with a Remez exchange algorithm.
+
+TEXT ·tanhAsm(SB),NOSPLIT,$0-16
+	FMOVD   x+0(FP), F0
+	//specail case Tanh(±0) = ±0
+	FMOVD   $(0.0), F1
+	FCMPU   F0, F1
+	BEQ     tanhIsZero
+	MOVD    $tanhrodataL18<>+0(SB), R5
+	WORD    $0xB3120000     //ltdbr %f0,%f0
+	MOVD    $0x4034000000000000, R1
+	BLTU    L15
+	FMOVD   F0, F1
+L2:
+	MOVD    $tanhxadd<>+0(SB), R2
+	FMOVD   0(R2), F2
+	MOVD    tanhrlog2<>+0(SB), R2
+	WORD    $0xB3C10042     //ldgr %f4,%r2
+	WFMSDB  V0, V4, V2, V4
+	MOVD    $tanhtab<>+0(SB), R3
+	WORD    $0xB3CD0024     //lgdr %r2,%f4
+	WORD    $0xEC4239BC     //risbg %r4,%r2,57,128+60,3
+	BYTE    $0x03
+	BYTE    $0x55
+	WORD    $0xED105058     //cdb %f1,.L19-.L18(%r5)
+	BYTE    $0x00
+	BYTE    $0x19
+	WORD    $0xEC12000F     //risbgn %r1,%r2,64-64+0,64-64+0+16-1,64-0-16
+	BYTE    $0x30
+	BYTE    $0x59
+	WORD    $0x68543000     //ld %f5,0(%r4,%r3)
+	WORD    $0xB3C10061     //ldgr %f6,%r1
+	BLT     L3
+	MOVD    $tanhxzero<>+0(SB), R1
+	FMOVD   0(R1), F2
+	WFCHDBS V0, V2, V4
+	BEQ     L9
+	WFCHDBS V2, V0, V2
+	BNE     L1
+	MOVD    $tanhxmone<>+0(SB), R1
+	FMOVD   0(R1), F0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L3:
+	FADD    F4, F2
+	FMOVD   tanhrodataL18<>+80(SB), F4
+	FMADD   F4, F2, F0, F0
+	FMOVD   tanhrodataL18<>+72(SB), F1
+	WFMDB   V0, V0, V3
+	FMOVD   tanhrodataL18<>+64(SB), F2
+	WFMADB  V0, V1, V2, V1
+	FMOVD   tanhrodataL18<>+56(SB), F4
+	FMOVD   tanhrodataL18<>+48(SB), F2
+	WFMADB  V1, V3, V4, V1
+	FMOVD   tanhrodataL18<>+40(SB), F4
+	WFMADB  V3, V2, V4, V2
+	FMOVD   tanhrodataL18<>+32(SB), F4
+	WORD    $0xB9270022     //lhr %r2,%r2
+	WFMADB  V3, V1, V4, V1
+	FMOVD   tanhrodataL18<>+24(SB), F4
+	WFMADB  V3, V2, V4, V3
+	WFMADB  V0, V5, V0, V2
+	WFMADB  V0, V1, V3, V0
+	WORD    $0xA7183ECF     //lhi %r1,16079
+	WFMADB  V0, V2, V5, V2
+	FMUL    F6, F2
+	MOVW    R2, R10
+	MOVW    R1, R11
+	CMPBLE  R10, R11, L16
+	FMOVD   F6, F0
+	WORD    $0xED005010     //adb %f0,.L28-.L18(%r5)
+	BYTE    $0x00
+	BYTE    $0x1A
+	WORD    $0xA7184330     //lhi %r1,17200
+	FADD    F2, F0
+	MOVW    R2, R10
+	MOVW    R1, R11
+	CMPBGT  R10, R11, L17
+	WORD    $0xED605010     //sdb %f6,.L28-.L18(%r5)
+	BYTE    $0x00
+	BYTE    $0x1B
+	FADD    F6, F2
+	WFDDB   V0, V2, V0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L9:
+	FMOVD   tanhrodataL18<>+16(SB), F0
+L1:
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L15:
+	FNEG    F0, F1
+	BR      L2
+L16:
+	FADD    F6, F2
+	FMOVD   tanhrodataL18<>+8(SB), F0
+	FMADD   F4, F2, F0, F0
+	FMOVD   tanhrodataL18<>+0(SB), F4
+	FNEG    F0, F0
+	WFMADB  V0, V2, V4, V0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+L17:
+	WFDDB   V0, V4, V0
+	FMOVD   tanhrodataL18<>+16(SB), F2
+	WFSDB   V0, V2, V0
+	FMOVD   F0, ret+8(FP)
+	RET
+
+tanhIsZero:      //return ±0
+	FMOVD   F0, ret+8(FP)
+	RET
diff --git a/src/mime/mediatype.go b/src/mime/mediatype.go
index 1845401..75cc903 100644
--- a/src/mime/mediatype.go
+++ b/src/mime/mediatype.go
@@ -248,24 +248,33 @@ func consumeValue(v string) (value, rest string) {
 	}
 
 	// parse a quoted-string
-	rest = v[1:] // consume the leading quote
 	buffer := new(bytes.Buffer)
-	var nextIsLiteral bool
-	for idx, r := range rest {
-		switch {
-		case nextIsLiteral:
-			buffer.WriteRune(r)
-			nextIsLiteral = false
-		case r == '"':
-			return buffer.String(), rest[idx+1:]
-		case r == '\\':
-			nextIsLiteral = true
-		case r != '\r' && r != '\n':
-			buffer.WriteRune(r)
-		default:
+	for i := 1; i < len(v); i++ {
+		r := v[i]
+		if r == '"' {
+			return buffer.String(), v[i+1:]
+		}
+		// When MSIE sends a full file path (in "intranet mode"), it does not
+		// escape backslashes: "C:\dev\go\foo.txt", not "C:\\dev\\go\\foo.txt".
+		//
+		// No known MIME generators emit unnecessary backslash escapes
+		// for simple token characters like numbers and letters.
+		//
+		// If we see an unnecessary backslash escape, assume it is from MSIE
+		// and intended as a literal backslash. This makes Go servers deal better
+		// with MSIE without affecting the way they handle conforming MIME
+		// generators.
+		if r == '\\' && i+1 < len(v) && !isTokenChar(rune(v[i+1])) {
+			buffer.WriteByte(v[i+1])
+			i++
+			continue
+		}
+		if r == '\r' || r == '\n' {
 			return "", v
 		}
+		buffer.WriteByte(v[i])
 	}
+	// Did not find end quote.
 	return "", v
 }
 
diff --git a/src/mime/mediatype_test.go b/src/mime/mediatype_test.go
index 9afa558..c5fc906 100644
--- a/src/mime/mediatype_test.go
+++ b/src/mime/mediatype_test.go
@@ -138,10 +138,11 @@ func TestParseMediaType(t *testing.T) {
 			m("title", "This is even more ***fun*** isn't it!")},
 
 		// Tests from http://greenbytes.de/tech/tc2231/
+		// Note: Backslash escape handling is a bit loose, like MSIE.
 		// TODO(bradfitz): add the rest of the tests from that site.
 		{`attachment; filename="f\oo.html"`,
 			"attachment",
-			m("filename", "foo.html")},
+			m("filename", "f\\oo.html")},
 		{`attachment; filename="\"quoting\" tested.html"`,
 			"attachment",
 			m("filename", `"quoting" tested.html`)},
@@ -165,7 +166,7 @@ func TestParseMediaType(t *testing.T) {
 			m("filename", "foo-%41.html")},
 		{`attachment; filename="foo-%\41.html"`,
 			"attachment",
-			m("filename", "foo-%41.html")},
+			m("filename", "foo-%\\41.html")},
 		{`filename=foo.html`,
 			"", m()},
 		{`x=y; filename=foo.html`,
@@ -220,18 +221,21 @@ func TestParseMediaType(t *testing.T) {
 
 		// Empty string used to be mishandled.
 		{`foo; bar=""`, "foo", m("bar", "")},
+
+		// Microsoft browers in intranet mode do not think they need to escape \ in file name.
+		{`form-data; name="file"; filename="C:\dev\go\robots.txt"`, "form-data", m("name", "file", "filename", `C:\dev\go\robots.txt`)},
 	}
 	for _, test := range tests {
 		mt, params, err := ParseMediaType(test.in)
 		if err != nil {
 			if test.t != "" {
-				t.Errorf("for input %q, unexpected error: %v", test.in, err)
+				t.Errorf("for input %#q, unexpected error: %v", test.in, err)
 				continue
 			}
 			continue
 		}
 		if g, e := mt, test.t; g != e {
-			t.Errorf("for input %q, expected type %q, got %q",
+			t.Errorf("for input %#q, expected type %q, got %q",
 				test.in, e, g)
 			continue
 		}
@@ -239,7 +243,7 @@ func TestParseMediaType(t *testing.T) {
 			continue
 		}
 		if !reflect.DeepEqual(params, test.p) {
-			t.Errorf("for input %q, wrong params.\n"+
+			t.Errorf("for input %#q, wrong params.\n"+
 				"expected: %#v\n"+
 				"     got: %#v",
 				test.in, test.p, params)
diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go
index 205348c..1954808 100644
--- a/src/mime/multipart/multipart.go
+++ b/src/mime/multipart/multipart.go
@@ -42,9 +42,7 @@ type Part struct {
 	// during Read calls.
 	Header textproto.MIMEHeader
 
-	buffer    *bytes.Buffer
-	mr        *Reader
-	bytesRead int
+	mr *Reader
 
 	disposition       string
 	dispositionParams map[string]string
@@ -53,6 +51,11 @@ type Part struct {
 	// wrapper around such a reader, decoding the
 	// Content-Transfer-Encoding
 	r io.Reader
+
+	n       int   // known data bytes waiting in mr.bufReader
+	total   int64 // total data bytes read already
+	err     error // error to return when n == 0
+	readErr error // read error observed from mr.bufReader
 }
 
 // FormName returns the name parameter if p has a Content-Disposition
@@ -126,7 +129,6 @@ func newPart(mr *Reader) (*Part, error) {
 	bp := &Part{
 		Header: make(map[string][]string),
 		mr:     mr,
-		buffer: new(bytes.Buffer),
 	}
 	if err := bp.populateHeaders(); err != nil {
 		return nil, err
@@ -161,65 +163,118 @@ type partReader struct {
 	p *Part
 }
 
-func (pr partReader) Read(d []byte) (n int, err error) {
+func (pr partReader) Read(d []byte) (int, error) {
 	p := pr.p
-	defer func() {
-		p.bytesRead += n
-	}()
-	if p.buffer.Len() >= len(d) {
-		// Internal buffer of unconsumed data is large enough for
-		// the read request. No need to parse more at the moment.
-		return p.buffer.Read(d)
+	br := p.mr.bufReader
+
+	// Read into buffer until we identify some data to return,
+	// or we find a reason to stop (boundary or read error).
+	for p.n == 0 && p.err == nil {
+		peek, _ := br.Peek(br.Buffered())
+		p.n, p.err = scanUntilBoundary(peek, p.mr.dashBoundary, p.mr.nlDashBoundary, p.total, p.readErr)
+		if p.n == 0 && p.err == nil {
+			// Force buffered I/O to read more into buffer.
+			_, p.readErr = br.Peek(len(peek) + 1)
+			if p.readErr == io.EOF {
+				p.readErr = io.ErrUnexpectedEOF
+			}
+		}
 	}
-	peek, err := p.mr.bufReader.Peek(peekBufferSize) // TODO(bradfitz): add buffer size accessor
-
-	// Look for an immediate empty part without a leading \r\n
-	// before the boundary separator. Some MIME code makes empty
-	// parts like this. Most browsers, however, write the \r\n
-	// before the subsequent boundary even for empty parts and
-	// won't hit this path.
-	if p.bytesRead == 0 && p.mr.peekBufferIsEmptyPart(peek) {
-		return 0, io.EOF
+
+	// Read out from "data to return" part of buffer.
+	if p.n == 0 {
+		return 0, p.err
 	}
-	unexpectedEOF := err == io.EOF
-	if err != nil && !unexpectedEOF {
-		return 0, fmt.Errorf("multipart: Part Read: %v", err)
+	n := len(d)
+	if n > p.n {
+		n = p.n
 	}
-	if peek == nil {
-		panic("nil peek buf")
+	n, _ = br.Read(d[:n])
+	p.total += int64(n)
+	p.n -= n
+	if p.n == 0 {
+		return n, p.err
 	}
-	// Search the peek buffer for "\r\n--boundary". If found,
-	// consume everything up to the boundary. If not, consume only
-	// as much of the peek buffer as cannot hold the boundary
-	// string.
-	nCopy := 0
-	foundBoundary := false
-	if idx, isEnd := p.mr.peekBufferSeparatorIndex(peek); idx != -1 {
-		nCopy = idx
-		foundBoundary = isEnd
-		if !isEnd && nCopy == 0 {
-			nCopy = 1 // make some progress.
+	return n, nil
+}
+
+// scanUntilBoundary scans buf to identify how much of it can be safely
+// returned as part of the Part body.
+// dashBoundary is "--boundary".
+// nlDashBoundary is "\r\n--boundary" or "\n--boundary", depending on what mode we are in.
+// The comments below (and the name) assume "\n--boundary", but either is accepted.
+// total is the number of bytes read out so far. If total == 0, then a leading "--boundary" is recognized.
+// readErr is the read error, if any, that followed reading the bytes in buf.
+// scanUntilBoundary returns the number of data bytes from buf that can be
+// returned as part of the Part body and also the error to return (if any)
+// once those data bytes are done.
+func scanUntilBoundary(buf, dashBoundary, nlDashBoundary []byte, total int64, readErr error) (int, error) {
+	if total == 0 {
+		// At beginning of body, allow dashBoundary.
+		if bytes.HasPrefix(buf, dashBoundary) {
+			switch matchAfterPrefix(buf, dashBoundary, readErr) {
+			case -1:
+				return len(dashBoundary), nil
+			case 0:
+				return 0, nil
+			case +1:
+				return 0, io.EOF
+			}
+		}
+		if bytes.HasPrefix(dashBoundary, buf) {
+			return 0, readErr
 		}
-	} else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 {
-		nCopy = safeCount
-	} else if unexpectedEOF {
-		// If we've run out of peek buffer and the boundary
-		// wasn't found (and can't possibly fit), we must have
-		// hit the end of the file unexpectedly.
-		return 0, io.ErrUnexpectedEOF
 	}
-	if nCopy > 0 {
-		if _, err := io.CopyN(p.buffer, p.mr.bufReader, int64(nCopy)); err != nil {
-			return 0, err
+
+	// Search for "\n--boundary".
+	if i := bytes.Index(buf, nlDashBoundary); i >= 0 {
+		switch matchAfterPrefix(buf[i:], nlDashBoundary, readErr) {
+		case -1:
+			return i + len(nlDashBoundary), nil
+		case 0:
+			return i, nil
+		case +1:
+			return i, io.EOF
+		}
+	}
+	if bytes.HasPrefix(nlDashBoundary, buf) {
+		return 0, readErr
+	}
+
+	// Otherwise, anything up to the final \n is not part of the boundary
+	// and so must be part of the body.
+	// Also if the section from the final \n onward is not a prefix of the boundary,
+	// it too must be part of the body.
+	i := bytes.LastIndexByte(buf, nlDashBoundary[0])
+	if i >= 0 && bytes.HasPrefix(nlDashBoundary, buf[i:]) {
+		return i, nil
+	}
+	return len(buf), readErr
+}
+
+// matchAfterPrefix checks whether buf should be considered to match the boundary.
+// The prefix is "--boundary" or "\r\n--boundary" or "\n--boundary",
+// and the caller has verified already that bytes.HasPrefix(buf, prefix) is true.
+//
+// matchAfterPrefix returns +1 if the buffer does match the boundary,
+// meaning the prefix is followed by a dash, space, tab, cr, nl, or end of input.
+// It returns -1 if the buffer definitely does NOT match the boundary,
+// meaning the prefix is followed by some other character.
+// For example, "--foobar" does not match "--foo".
+// It returns 0 more input needs to be read to make the decision,
+// meaning that len(buf) == len(prefix) and readErr == nil.
+func matchAfterPrefix(buf, prefix []byte, readErr error) int {
+	if len(buf) == len(prefix) {
+		if readErr != nil {
+			return +1
 		}
+		return 0
 	}
-	n, err = p.buffer.Read(d)
-	if err == io.EOF && !foundBoundary {
-		// If the boundary hasn't been reached there's more to
-		// read, so don't pass through an EOF from the buffer
-		err = nil
+	c := buf[len(prefix)]
+	if c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '-' {
+		return +1
 	}
-	return
+	return -1
 }
 
 func (p *Part) Close() error {
@@ -337,64 +392,6 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) {
 	return bytes.Equal(rest, mr.nl)
 }
 
-// peekBufferIsEmptyPart reports whether the provided peek-ahead
-// buffer represents an empty part. It is called only if we've not
-// already read any bytes in this part and checks for the case of MIME
-// software not writing the \r\n on empty parts. Some does, some
-// doesn't.
-//
-// This checks that what follows the "--boundary" is actually the end
-// ("--boundary--" with optional whitespace) or optional whitespace
-// and then a newline, so we don't catch "--boundaryFAKE", in which
-// case the whole line is part of the data.
-func (mr *Reader) peekBufferIsEmptyPart(peek []byte) bool {
-	// End of parts case.
-	// Test whether peek matches `^--boundary--[ \t]*(?:\r\n|$)`
-	if bytes.HasPrefix(peek, mr.dashBoundaryDash) {
-		rest := peek[len(mr.dashBoundaryDash):]
-		rest = skipLWSPChar(rest)
-		return bytes.HasPrefix(rest, mr.nl) || len(rest) == 0
-	}
-	if !bytes.HasPrefix(peek, mr.dashBoundary) {
-		return false
-	}
-	// Test whether rest matches `^[ \t]*\r\n`)
-	rest := peek[len(mr.dashBoundary):]
-	rest = skipLWSPChar(rest)
-	return bytes.HasPrefix(rest, mr.nl)
-}
-
-// peekBufferSeparatorIndex returns the index of mr.nlDashBoundary in
-// peek and whether it is a real boundary (and not a prefix of an
-// unrelated separator). To be the end, the peek buffer must contain a
-// newline after the boundary or contain the ending boundary (--separator--).
-func (mr *Reader) peekBufferSeparatorIndex(peek []byte) (idx int, isEnd bool) {
-	idx = bytes.Index(peek, mr.nlDashBoundary)
-	if idx == -1 {
-		return
-	}
-
-	peek = peek[idx+len(mr.nlDashBoundary):]
-	if len(peek) == 0 || len(peek) == 1 && peek[0] == '-' {
-		return idx, false
-	}
-	if len(peek) > 1 && peek[0] == '-' && peek[1] == '-' {
-		return idx, true
-	}
-	peek = skipLWSPChar(peek)
-	// Don't have a complete line after the peek.
-	if bytes.IndexByte(peek, '\n') == -1 {
-		return idx, false
-	}
-	if len(peek) > 0 && peek[0] == '\n' {
-		return idx, true
-	}
-	if len(peek) > 1 && peek[0] == '\r' && peek[1] == '\n' {
-		return idx, true
-	}
-	return idx, false
-}
-
 // skipLWSPChar returns b with leading spaces and tabs removed.
 // RFC 822 defines:
 //    LWSP-char = SPACE / HTAB
diff --git a/src/mime/multipart/multipart_test.go b/src/mime/multipart/multipart_test.go
index 82a7f86..7fbee90 100644
--- a/src/mime/multipart/multipart_test.go
+++ b/src/mime/multipart/multipart_test.go
@@ -125,6 +125,7 @@ func TestMultipartSlowInput(t *testing.T) {
 }
 
 func testMultipart(t *testing.T, r io.Reader, onlyNewlines bool) {
+	t.Parallel()
 	reader := NewReader(r, "MyBoundary")
 	buf := new(bytes.Buffer)
 
@@ -323,6 +324,73 @@ func (s *slowReader) Read(p []byte) (int, error) {
 	return s.r.Read(p[:1])
 }
 
+type sentinelReader struct {
+	// done is closed when this reader is read from.
+	done chan struct{}
+}
+
+func (s *sentinelReader) Read([]byte) (int, error) {
+	if s.done != nil {
+		close(s.done)
+		s.done = nil
+	}
+	return 0, io.EOF
+}
+
+// TestMultipartStreamReadahead tests that PartReader does not block
+// on reading past the end of a part, ensuring that it can be used on
+// a stream like multipart/x-mixed-replace. See golang.org/issue/15431
+func TestMultipartStreamReadahead(t *testing.T) {
+	testBody1 := `
+This is a multi-part message.  This line is ignored.
+--MyBoundary
+foo-bar: baz
+
+Body
+--MyBoundary
+`
+	testBody2 := `foo-bar: bop
+
+Body 2
+--MyBoundary--
+`
+	done1 := make(chan struct{})
+	reader := NewReader(
+		io.MultiReader(
+			strings.NewReader(testBody1),
+			&sentinelReader{done1},
+			strings.NewReader(testBody2)),
+		"MyBoundary")
+
+	var i int
+	readPart := func(hdr textproto.MIMEHeader, body string) {
+		part, err := reader.NextPart()
+		if part == nil || err != nil {
+			t.Fatalf("Part %d: NextPart failed: %v", i, err)
+		}
+
+		if !reflect.DeepEqual(part.Header, hdr) {
+			t.Errorf("Part %d: part.Header = %v, want %v", i, part.Header, hdr)
+		}
+		data, err := ioutil.ReadAll(part)
+		expectEq(t, body, string(data), fmt.Sprintf("Part %d body", i))
+		if err != nil {
+			t.Fatalf("Part %d: ReadAll failed: %v", i, err)
+		}
+		i++
+	}
+
+	readPart(textproto.MIMEHeader{"Foo-Bar": {"baz"}}, "Body")
+
+	select {
+	case <-done1:
+		t.Errorf("Reader read past second boundary")
+	default:
+	}
+
+	readPart(textproto.MIMEHeader{"Foo-Bar": {"bop"}}, "Body 2")
+}
+
 func TestLineContinuation(t *testing.T) {
 	// This body, extracted from an email, contains headers that span multiple
 	// lines.
@@ -755,6 +823,7 @@ func partsFromReader(r *Reader) ([]headerBody, error) {
 }
 
 func TestParseAllSizes(t *testing.T) {
+	t.Parallel()
 	const maxSize = 5 << 10
 	var buf bytes.Buffer
 	body := strings.Repeat("a", maxSize)
diff --git a/src/mime/quotedprintable/example_test.go b/src/mime/quotedprintable/example_test.go
new file mode 100644
index 0000000..5a9ab45
--- /dev/null
+++ b/src/mime/quotedprintable/example_test.go
@@ -0,0 +1,37 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quotedprintable_test
+
+import (
+	"fmt"
+	"io/ioutil"
+	"mime/quotedprintable"
+	"os"
+	"strings"
+)
+
+func ExampleNewReader() {
+	for _, s := range []string{
+		`=48=65=6C=6C=6F=2C=20=47=6F=70=68=65=72=73=21`,
+		`invalid escape: <b style="font-size: 200%">hello</b>`,
+		"Hello, Gophers! This symbol will be unescaped: =3D and this will be written in =\r\none line.",
+	} {
+		b, err := ioutil.ReadAll(quotedprintable.NewReader(strings.NewReader(s)))
+		fmt.Printf("%s %v\n", b, err)
+	}
+	// Output:
+	// Hello, Gophers! <nil>
+	// invalid escape: <b style="font-size: 200%">hello</b> <nil>
+	// Hello, Gophers! This symbol will be unescaped: = and this will be written in one line. <nil>
+}
+
+func ExampleNewWriter() {
+	w := quotedprintable.NewWriter(os.Stdout)
+	w.Write([]byte("These symbols will be escaped: = \t"))
+	w.Close()
+
+	// Output:
+	// These symbols will be escaped: =3D =09
+}
diff --git a/src/mime/quotedprintable/reader.go b/src/mime/quotedprintable/reader.go
index 3bd6833..b142240 100644
--- a/src/mime/quotedprintable/reader.go
+++ b/src/mime/quotedprintable/reader.go
@@ -74,6 +74,11 @@ func (r *Reader) Read(p []byte) (n int, err error) {
 	// 1. in addition to "=\r\n", "=\n" is also treated as soft line break.
 	// 2. it will pass through a '\r' or '\n' not preceded by '=', consistent
 	//    with other broken QP encoders & decoders.
+	// 3. it accepts soft line-break (=) at end of message (issue 15486); i.e.
+	//    the final byte read from the underlying reader is allowed to be '=',
+	//    and it will be silently ignored.
+	// 4. it takes = as literal = if not followed by two hex digits
+	//    but not at end of line (issue 13219).
 	for len(p) > 0 {
 		if len(r.line) == 0 {
 			if r.rerr != nil {
@@ -89,7 +94,8 @@ func (r *Reader) Read(p []byte) (n int, err error) {
 			if bytes.HasSuffix(r.line, softSuffix) {
 				rightStripped := wholeLine[len(r.line):]
 				r.line = r.line[:len(r.line)-1]
-				if !bytes.HasPrefix(rightStripped, lf) && !bytes.HasPrefix(rightStripped, crlf) {
+				if !bytes.HasPrefix(rightStripped, lf) && !bytes.HasPrefix(rightStripped, crlf) &&
+					!(len(rightStripped) == 0 && len(r.line) > 0 && r.rerr == io.EOF) {
 					r.rerr = fmt.Errorf("quotedprintable: invalid bytes after =: %q", rightStripped)
 				}
 			} else if hasLF {
@@ -107,6 +113,11 @@ func (r *Reader) Read(p []byte) (n int, err error) {
 		case b == '=':
 			b, err = readHexByte(r.line[1:])
 			if err != nil {
+				if len(r.line) >= 2 && r.line[1] != '\r' && r.line[1] != '\n' {
+					// Take the = as a literal =.
+					b = '='
+					break
+				}
 				return n, err
 			}
 			r.line = r.line[2:] // 2 of the 3; other 1 is done below
diff --git a/src/mime/quotedprintable/reader_test.go b/src/mime/quotedprintable/reader_test.go
index e77b261..ca016f9 100644
--- a/src/mime/quotedprintable/reader_test.go
+++ b/src/mime/quotedprintable/reader_test.go
@@ -30,7 +30,7 @@ func TestReader(t *testing.T) {
 		{in: "foo bar=3d", want: "foo bar="}, // lax.
 		{in: "foo bar=\n", want: "foo bar"},
 		{in: "foo bar\n", want: "foo bar\n"}, // somewhat lax.
-		{in: "foo bar=0", want: "foo bar", err: io.ErrUnexpectedEOF},
+		{in: "foo bar=0", want: "foo bar=0"}, // lax
 		{in: "foo bar=0D=0A", want: "foo bar\r\n"},
 		{in: " A B        \r\n C ", want: " A B\r\n C"},
 		{in: " A B =\r\n C ", want: " A B  C"},
@@ -58,6 +58,9 @@ func TestReader(t *testing.T) {
 		{in: "foo=\nbar", want: "foobar"},
 		{in: "foo=\rbar", want: "foo", err: "quotedprintable: invalid hex byte 0x0d"},
 		{in: "foo=\r\r\r \nbar", want: "foo", err: `quotedprintable: invalid bytes after =: "\r\r\r \n"`},
+		// Issue 15486, accept trailing soft line-break at end of input.
+		{in: "foo=", want: "foo"},
+		{in: "=", want: "", err: `quotedprintable: invalid bytes after =: ""`},
 
 		// Example from RFC 2045:
 		{in: "Now's the time =\n" + "for all folk to come=\n" + " to the aid of their country.",
@@ -191,13 +194,10 @@ func TestExhaustive(t *testing.T) {
 	}
 	sort.Strings(outcomes)
 	got := strings.Join(outcomes, "\n")
-	want := `OK: 21576
-invalid bytes after =: 3397
-quotedprintable: invalid hex byte 0x0a: 1400
-quotedprintable: invalid hex byte 0x0d: 2700
-quotedprintable: invalid hex byte 0x20: 2490
-quotedprintable: invalid hex byte 0x3d: 440
-unexpected EOF: 3122`
+	want := `OK: 28934
+invalid bytes after =: 3949
+quotedprintable: invalid hex byte 0x0d: 2048
+unexpected EOF: 194`
 	if got != want {
 		t.Errorf("Got:\n%s\nWant:\n%s", got, want)
 	}
diff --git a/src/net/cgo_unix.go b/src/net/cgo_unix.go
index 5a1eed8..56d34b6 100644
--- a/src/net/cgo_unix.go
+++ b/src/net/cgo_unix.go
@@ -96,6 +96,11 @@ func cgoLookupPort(ctx context.Context, network, service string) (port int, err
 
 func cgoLookupServicePort(hints *C.struct_addrinfo, network, service string) (port int, err error) {
 	s := C.CString(service)
+	// Lowercase the service name in the C-allocated memory.
+	for i := 0; i < len(service); i++ {
+		bp := (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(s)) + uintptr(i)))
+		*bp = lowerASCII(*bp)
+	}
 	var res *C.struct_addrinfo
 	defer C.free(unsafe.Pointer(s))
 	gerrno, err := C.getaddrinfo(nil, s, hints, &res)
diff --git a/src/net/conf.go b/src/net/conf.go
index eb72916..c10aafe 100644
--- a/src/net/conf.go
+++ b/src/net/conf.go
@@ -179,8 +179,6 @@ func (c *conf) hostLookupOrder(hostname string) (ret hostLookupOrder) {
 		}
 	}
 
-	hasDot := byteIndex(hostname, '.') != -1
-
 	// Canonicalize the hostname by removing any trailing dot.
 	if stringsHasSuffix(hostname, ".") {
 		hostname = hostname[:len(hostname)-1]
@@ -220,10 +218,14 @@ func (c *conf) hostLookupOrder(hostname string) (ret hostLookupOrder) {
 	var first string
 	for _, src := range srcs {
 		if src.source == "myhostname" {
-			if hostname == "" || hasDot {
-				continue
+			if isLocalhost(hostname) || isGateway(hostname) {
+				return fallbackOrder
 			}
-			return fallbackOrder
+			hn, err := getHostname()
+			if err != nil || stringsEqualFold(hostname, hn) {
+				return fallbackOrder
+			}
+			continue
 		}
 		if src.source == "files" || src.source == "dns" {
 			if !src.standardCriteria() {
@@ -293,7 +295,7 @@ func goDebugNetDNS() (dnsMode string, debugLevel int) {
 			return
 		}
 		if '0' <= s[0] && s[0] <= '9' {
-			debugLevel, _, _ = dtoi(s, 0)
+			debugLevel, _, _ = dtoi(s)
 		} else {
 			dnsMode = s
 		}
@@ -306,3 +308,15 @@ func goDebugNetDNS() (dnsMode string, debugLevel int) {
 	parsePart(goDebug)
 	return
 }
+
+// isLocalhost reports whether h should be considered a "localhost"
+// name for the myhostname NSS module.
+func isLocalhost(h string) bool {
+	return stringsEqualFold(h, "localhost") || stringsEqualFold(h, "localhost.localdomain") || stringsHasSuffixFold(h, ".localhost") || stringsHasSuffixFold(h, ".localhost.localdomain")
+}
+
+// isGateway reports whether h should be considered a "gateway"
+// name for the myhostname NSS module.
+func isGateway(h string) bool {
+	return stringsEqualFold(h, "gateway")
+}
diff --git a/src/net/conf_test.go b/src/net/conf_test.go
index ec8814b..17d03f4 100644
--- a/src/net/conf_test.go
+++ b/src/net/conf_test.go
@@ -13,8 +13,9 @@ import (
 )
 
 type nssHostTest struct {
-	host string
-	want hostLookupOrder
+	host      string
+	localhost string
+	want      hostLookupOrder
 }
 
 func nssStr(s string) *nssConf { return parseNSSConf(strings.NewReader(s)) }
@@ -42,8 +43,8 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv:             defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"foo.local", hostLookupCgo},
-				{"google.com", hostLookupCgo},
+				{"foo.local", "myhostname", hostLookupCgo},
+				{"google.com", "myhostname", hostLookupCgo},
 			},
 		},
 		{
@@ -54,7 +55,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupDNSFiles},
+				{"x.com", "myhostname", hostLookupDNSFiles},
 			},
 		},
 		{
@@ -65,7 +66,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupFilesDNS},
+				{"x.com", "myhostname", hostLookupFilesDNS},
 			},
 		},
 		{
@@ -75,11 +76,11 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"foo.local", hostLookupCgo},
-				{"foo.local.", hostLookupCgo},
-				{"foo.LOCAL", hostLookupCgo},
-				{"foo.LOCAL.", hostLookupCgo},
-				{"google.com", hostLookupFilesDNS},
+				{"foo.local", "myhostname", hostLookupCgo},
+				{"foo.local.", "myhostname", hostLookupCgo},
+				{"foo.LOCAL", "myhostname", hostLookupCgo},
+				{"foo.LOCAL.", "myhostname", hostLookupCgo},
+				{"google.com", "myhostname", hostLookupFilesDNS},
 			},
 		},
 		{
@@ -89,7 +90,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				nss:    nssStr("foo: bar"),
 				resolv: defaultResolvConf,
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupFilesDNS}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFilesDNS}},
 		},
 		// On OpenBSD, no resolv.conf means no DNS.
 		{
@@ -98,7 +99,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				goos:   "openbsd",
 				resolv: defaultResolvConf,
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupFiles}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFiles}},
 		},
 		{
 			name: "solaris_no_nsswitch",
@@ -107,7 +108,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				nss:    &nssConf{err: os.ErrNotExist},
 				resolv: defaultResolvConf,
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupCgo}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupCgo}},
 		},
 		{
 			name: "openbsd_lookup_bind_file",
@@ -116,8 +117,8 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: &dnsConfig{lookup: []string{"bind", "file"}},
 			},
 			hostTests: []nssHostTest{
-				{"google.com", hostLookupDNSFiles},
-				{"foo.local", hostLookupDNSFiles},
+				{"google.com", "myhostname", hostLookupDNSFiles},
+				{"foo.local", "myhostname", hostLookupDNSFiles},
 			},
 		},
 		{
@@ -126,7 +127,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				goos:   "openbsd",
 				resolv: &dnsConfig{lookup: []string{"file", "bind"}},
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupFilesDNS}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFilesDNS}},
 		},
 		{
 			name: "openbsd_lookup_bind",
@@ -134,7 +135,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				goos:   "openbsd",
 				resolv: &dnsConfig{lookup: []string{"bind"}},
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupDNS}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupDNS}},
 		},
 		{
 			name: "openbsd_lookup_file",
@@ -142,7 +143,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				goos:   "openbsd",
 				resolv: &dnsConfig{lookup: []string{"file"}},
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupFiles}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFiles}},
 		},
 		{
 			name: "openbsd_lookup_yp",
@@ -150,7 +151,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				goos:   "openbsd",
 				resolv: &dnsConfig{lookup: []string{"file", "bind", "yp"}},
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupCgo}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupCgo}},
 		},
 		{
 			name: "openbsd_lookup_two",
@@ -158,7 +159,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				goos:   "openbsd",
 				resolv: &dnsConfig{lookup: []string{"file", "foo"}},
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupCgo}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupCgo}},
 		},
 		{
 			name: "openbsd_lookup_empty",
@@ -166,7 +167,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				goos:   "openbsd",
 				resolv: &dnsConfig{lookup: nil},
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupDNSFiles}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupDNSFiles}},
 		},
 		// glibc lacking an nsswitch.conf, per
 		// http://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html
@@ -177,7 +178,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				nss:    &nssConf{err: os.ErrNotExist},
 				resolv: defaultResolvConf,
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupDNSFiles}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupDNSFiles}},
 		},
 		{
 			name: "files_mdns_dns",
@@ -186,8 +187,8 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupFilesDNS},
-				{"x.local", hostLookupCgo},
+				{"x.com", "myhostname", hostLookupFilesDNS},
+				{"x.local", "myhostname", hostLookupCgo},
 			},
 		},
 		{
@@ -197,9 +198,9 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupDNS},
-				{"x\\.com", hostLookupCgo},     // punt on weird glibc escape
-				{"foo.com%en0", hostLookupCgo}, // and IPv6 zones
+				{"x.com", "myhostname", hostLookupDNS},
+				{"x\\.com", "myhostname", hostLookupCgo},     // punt on weird glibc escape
+				{"foo.com%en0", "myhostname", hostLookupCgo}, // and IPv6 zones
 			},
 		},
 		{
@@ -210,8 +211,8 @@ func TestConfHostLookupOrder(t *testing.T) {
 				hasMDNSAllow: true,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupCgo},
-				{"x.local", hostLookupCgo},
+				{"x.com", "myhostname", hostLookupCgo},
+				{"x.local", "myhostname", hostLookupCgo},
 			},
 		},
 		{
@@ -221,9 +222,9 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupFilesDNS},
-				{"x", hostLookupFilesDNS},
-				{"x.local", hostLookupCgo},
+				{"x.com", "myhostname", hostLookupFilesDNS},
+				{"x", "myhostname", hostLookupFilesDNS},
+				{"x.local", "myhostname", hostLookupCgo},
 			},
 		},
 		{
@@ -233,9 +234,9 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupDNSFiles},
-				{"x", hostLookupDNSFiles},
-				{"x.local", hostLookupCgo},
+				{"x.com", "myhostname", hostLookupDNSFiles},
+				{"x", "myhostname", hostLookupDNSFiles},
+				{"x.local", "myhostname", hostLookupCgo},
 			},
 		},
 		{
@@ -245,7 +246,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupCgo},
+				{"x.com", "myhostname", hostLookupCgo},
 			},
 		},
 		{
@@ -255,9 +256,23 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupFilesDNS},
-				{"somehostname", hostLookupCgo},
-				{"", hostLookupFilesDNS}, // Issue 13623
+				{"x.com", "myhostname", hostLookupFilesDNS},
+				{"myhostname", "myhostname", hostLookupCgo},
+				{"myHostname", "myhostname", hostLookupCgo},
+				{"myhostname.dot", "myhostname.dot", hostLookupCgo},
+				{"myHostname.dot", "myhostname.dot", hostLookupCgo},
+				{"gateway", "myhostname", hostLookupCgo},
+				{"Gateway", "myhostname", hostLookupCgo},
+				{"localhost", "myhostname", hostLookupCgo},
+				{"Localhost", "myhostname", hostLookupCgo},
+				{"anything.localhost", "myhostname", hostLookupCgo},
+				{"Anything.localhost", "myhostname", hostLookupCgo},
+				{"localhost.localdomain", "myhostname", hostLookupCgo},
+				{"Localhost.Localdomain", "myhostname", hostLookupCgo},
+				{"anything.localhost.localdomain", "myhostname", hostLookupCgo},
+				{"Anything.Localhost.Localdomain", "myhostname", hostLookupCgo},
+				{"somehostname", "myhostname", hostLookupFilesDNS},
+				{"", "myhostname", hostLookupFilesDNS}, // Issue 13623
 			},
 		},
 		{
@@ -267,8 +282,9 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupFilesDNS},
-				{"somehostname", hostLookupCgo},
+				{"x.com", "myhostname", hostLookupFilesDNS},
+				{"somehostname", "myhostname", hostLookupFilesDNS},
+				{"myhostname", "myhostname", hostLookupCgo},
 			},
 		},
 		// Debian Squeeze is just "dns,files", but lists all
@@ -282,8 +298,8 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupDNSFiles},
-				{"somehostname", hostLookupDNSFiles},
+				{"x.com", "myhostname", hostLookupDNSFiles},
+				{"somehostname", "myhostname", hostLookupDNSFiles},
 			},
 		},
 		{
@@ -292,7 +308,7 @@ func TestConfHostLookupOrder(t *testing.T) {
 				nss:    nssStr("foo: bar"),
 				resolv: &dnsConfig{servers: defaultNS, ndots: 1, timeout: 5, attempts: 2, unknownOpt: true},
 			},
-			hostTests: []nssHostTest{{"google.com", hostLookupCgo}},
+			hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupCgo}},
 		},
 		// Android should always use cgo.
 		{
@@ -303,12 +319,18 @@ func TestConfHostLookupOrder(t *testing.T) {
 				resolv: defaultResolvConf,
 			},
 			hostTests: []nssHostTest{
-				{"x.com", hostLookupCgo},
+				{"x.com", "myhostname", hostLookupCgo},
 			},
 		},
 	}
+
+	origGetHostname := getHostname
+	defer func() { getHostname = origGetHostname }()
+
 	for _, tt := range tests {
 		for _, ht := range tt.hostTests {
+			getHostname = func() (string, error) { return ht.localhost, nil }
+
 			gotOrder := tt.c.hostLookupOrder(ht.host)
 			if gotOrder != ht.want {
 				t.Errorf("%s: hostLookupOrder(%q) = %v; want %v", tt.name, ht.host, gotOrder, ht.want)
diff --git a/src/net/dial.go b/src/net/dial.go
index 55edb43..5db3585 100644
--- a/src/net/dial.go
+++ b/src/net/dial.go
@@ -59,6 +59,9 @@ type Dialer struct {
 	// that do not support keep-alives ignore this field.
 	KeepAlive time.Duration
 
+	// Resolver optionally specifies an alternate resolver to use.
+	Resolver *Resolver
+
 	// Cancel is an optional channel whose closure indicates that
 	// the dial should be canceled. Not all types of dials support
 	// cancelation.
@@ -92,6 +95,13 @@ func (d *Dialer) deadline(ctx context.Context, now time.Time) (earliest time.Tim
 	return minNonzeroTime(earliest, d.Deadline)
 }
 
+func (d *Dialer) resolver() *Resolver {
+	if d.Resolver != nil {
+		return d.Resolver
+	}
+	return DefaultResolver
+}
+
 // partialDeadline returns the deadline to use for a single address,
 // when multiple addresses are pending.
 func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, error) {
@@ -141,7 +151,7 @@ func parseNetwork(ctx context.Context, net string) (afnet string, proto int, err
 	switch afnet {
 	case "ip", "ip4", "ip6":
 		protostr := net[i+1:]
-		proto, i, ok := dtoi(protostr, 0)
+		proto, i, ok := dtoi(protostr)
 		if !ok || i != len(protostr) {
 			proto, err = lookupProtocol(ctx, protostr)
 			if err != nil {
@@ -156,7 +166,7 @@ func parseNetwork(ctx context.Context, net string) (afnet string, proto int, err
 // resolverAddrList resolves addr using hint and returns a list of
 // addresses. The result contains at least one address when error is
 // nil.
-func resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (addrList, error) {
+func (r *Resolver) resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (addrList, error) {
 	afnet, _, err := parseNetwork(ctx, network)
 	if err != nil {
 		return nil, err
@@ -166,7 +176,6 @@ func resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (
 	}
 	switch afnet {
 	case "unix", "unixgram", "unixpacket":
-		// TODO(bradfitz): push down context
 		addr, err := ResolveUnixAddr(afnet, addr)
 		if err != nil {
 			return nil, err
@@ -176,7 +185,7 @@ func resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (
 		}
 		return addrList{addr}, nil
 	}
-	addrs, err := internetAddrList(ctx, afnet, addr)
+	addrs, err := r.internetAddrList(ctx, afnet, addr)
 	if err != nil || op != "dial" || hint == nil {
 		return addrs, err
 	}
@@ -221,7 +230,7 @@ func resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (
 		}
 	}
 	if len(naddrs) == 0 {
-		return nil, errNoSuitableAddress
+		return nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: hint.String()}
 	}
 	return naddrs, nil
 }
@@ -326,7 +335,7 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn
 		resolveCtx = context.WithValue(resolveCtx, nettrace.TraceKey{}, &shadow)
 	}
 
-	addrs, err := resolveAddrList(resolveCtx, "dial", network, address, d.LocalAddr)
+	addrs, err := d.resolver().resolveAddrList(resolveCtx, "dial", network, address, d.LocalAddr)
 	if err != nil {
 		return nil, &OpError{Op: "dial", Net: network, Source: nil, Addr: nil, Err: err}
 	}
@@ -524,8 +533,11 @@ func dialSingle(ctx context.Context, dp *dialParam, ra Addr) (c Conn, err error)
 // If host is omitted, as in ":8080", Listen listens on all available interfaces
 // instead of just the interface with the given host address.
 // See Dial for more details about address syntax.
+//
+// Listening on a hostname is not recommended because this creates a socket
+// for at most one of its IP addresses.
 func Listen(net, laddr string) (Listener, error) {
-	addrs, err := resolveAddrList(context.Background(), "listen", net, laddr, nil)
+	addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", net, laddr, nil)
 	if err != nil {
 		return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: nil, Err: err}
 	}
@@ -551,8 +563,11 @@ func Listen(net, laddr string) (Listener, error) {
 // If host is omitted, as in ":8080", ListenPacket listens on all available interfaces
 // instead of just the interface with the given host address.
 // See Dial for the syntax of laddr.
+//
+// Listening on a hostname is not recommended because this creates a socket
+// for at most one of its IP addresses.
 func ListenPacket(net, laddr string) (PacketConn, error) {
-	addrs, err := resolveAddrList(context.Background(), "listen", net, laddr, nil)
+	addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", net, laddr, nil)
 	if err != nil {
 		return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: nil, Err: err}
 	}
diff --git a/src/net/dial_test.go b/src/net/dial_test.go
index 8b21e6b..6ba8e95 100644
--- a/src/net/dial_test.go
+++ b/src/net/dial_test.go
@@ -55,6 +55,23 @@ func TestProhibitionaryDialArg(t *testing.T) {
 	}
 }
 
+func TestDialLocal(t *testing.T) {
+	ln, err := newLocalListener("tcp")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer ln.Close()
+	_, port, err := SplitHostPort(ln.Addr().String())
+	if err != nil {
+		t.Fatal(err)
+	}
+	c, err := Dial("tcp", JoinHostPort("", port))
+	if err != nil {
+		t.Fatal(err)
+	}
+	c.Close()
+}
+
 func TestDialTimeoutFDLeak(t *testing.T) {
 	switch runtime.GOOS {
 	case "plan9":
diff --git a/src/net/dnsclient.go b/src/net/dnsclient.go
index f1835b8..2ab5639 100644
--- a/src/net/dnsclient.go
+++ b/src/net/dnsclient.go
@@ -113,12 +113,20 @@ func equalASCIILabel(x, y string) bool {
 	return true
 }
 
+// isDomainName checks if a string is a presentation-format domain name
+// (currently restricted to hostname-compatible "preferred name" LDH labels and
+// SRV-like "underscore labels"; see golang.org/issue/12421).
 func isDomainName(s string) bool {
 	// See RFC 1035, RFC 3696.
-	if len(s) == 0 {
-		return false
-	}
-	if len(s) > 255 {
+	// Presentation format has dots before every label except the first, and the
+	// terminal empty label is optional here because we assume fully-qualified
+	// (absolute) input. We must therefore reserve space for the first and last
+	// labels' length octets in wire format, where they are necessary and the
+	// maximum total length is 255.
+	// So our _effective_ maximum is 253, but 254 is not rejected if the last
+	// character is a dot.
+	l := len(s)
+	if l == 0 || l > 254 || l == 254 && s[l-1] != '.' {
 		return false
 	}
 
diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go
index b5b6ffb..2980302 100644
--- a/src/net/dnsclient_unix.go
+++ b/src/net/dnsclient_unix.go
@@ -125,7 +125,7 @@ func (d *Dialer) dialDNS(ctx context.Context, network, server string) (dnsConn,
 	// Calling Dial here is scary -- we have to be sure not to
 	// dial a name that will require a DNS lookup, or Dial will
 	// call back here to translate it. The DNS config parser has
-	// already checked that all the cfg.servers[i] are IP
+	// already checked that all the cfg.servers are IP
 	// addresses, which Dial will use without a DNS lookup.
 	c, err := d.DialContext(ctx, network, server)
 	if err != nil {
@@ -182,13 +182,14 @@ func exchange(ctx context.Context, server, name string, qtype uint16, timeout ti
 // Do a lookup for a single name, which must be rooted
 // (otherwise answer will not find the answers).
 func tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) {
-	if len(cfg.servers) == 0 {
-		return "", nil, &DNSError{Err: "no DNS servers", Name: name}
-	}
-
 	var lastErr error
+	serverOffset := cfg.serverOffset()
+	sLen := uint32(len(cfg.servers))
+
 	for i := 0; i < cfg.attempts; i++ {
-		for _, server := range cfg.servers {
+		for j := uint32(0); j < sLen; j++ {
+			server := cfg.servers[(serverOffset+j)%sLen]
+
 			msg, err := exchange(ctx, server, name, qtype, cfg.timeout)
 			if err != nil {
 				lastErr = &DNSError{
@@ -315,7 +316,12 @@ func (conf *resolverConfig) releaseSema() {
 
 func lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
 	if !isDomainName(name) {
-		return "", nil, &DNSError{Err: "invalid domain name", Name: name}
+		// We used to use "invalid domain name" as the error,
+		// but that is a detail of the specific lookup mechanism.
+		// Other lookups might allow broader name syntax
+		// (for example Multicast DNS allows UTF-8; see RFC 6762).
+		// For consistency with libc resolvers, report no such host.
+		return "", nil, &DNSError{Err: errNoSuchHost.Error(), Name: name}
 	}
 	resolvConf.tryUpdate("/etc/resolv.conf")
 	resolvConf.mu.RLock()
@@ -356,14 +362,21 @@ func (conf *dnsConfig) nameList(name string) []string {
 		return nil
 	}
 
+	// Check name length (see isDomainName).
+	l := len(name)
+	rooted := l > 0 && name[l-1] == '.'
+	if l > 254 || l == 254 && rooted {
+		return nil
+	}
+
 	// If name is rooted (trailing dot), try only that name.
-	rooted := len(name) > 0 && name[len(name)-1] == '.'
 	if rooted {
 		return []string{name}
 	}
 
 	hasNdots := count(name, '.') >= conf.ndots
 	name += "."
+	l++
 
 	// Build list of search choices.
 	names := make([]string, 0, 1+len(conf.search))
@@ -371,9 +384,11 @@ func (conf *dnsConfig) nameList(name string) []string {
 	if hasNdots {
 		names = append(names, name)
 	}
-	// Try suffixes.
+	// Try suffixes that are not too long (see isDomainName).
 	for _, suffix := range conf.search {
-		names = append(names, name+suffix)
+		if l+len(suffix) <= 254 {
+			names = append(names, name+suffix)
+		}
 	}
 	// Try unsuffixed, if not tried first above.
 	if !hasNdots {
@@ -455,8 +470,9 @@ func goLookupIPFiles(name string) (addrs []IPAddr) {
 
 // goLookupIP is the native Go implementation of LookupIP.
 // The libc versions are in cgo_*.go.
-func goLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error) {
-	return goLookupIPOrder(ctx, name, hostLookupFilesDNS)
+func goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
+	order := systemConf().hostLookupOrder(host)
+	return goLookupIPOrder(ctx, host, order)
 }
 
 func goLookupIPOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, err error) {
@@ -467,7 +483,8 @@ func goLookupIPOrder(ctx context.Context, name string, order hostLookupOrder) (a
 		}
 	}
 	if !isDomainName(name) {
-		return nil, &DNSError{Err: "invalid domain name", Name: name}
+		// See comment in func lookup above about use of errNoSuchHost.
+		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: name}
 	}
 	resolvConf.tryUpdate("/etc/resolv.conf")
 	resolvConf.mu.RLock()
diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go
index 6ebeeae..7dc364d 100644
--- a/src/net/dnsclient_unix_test.go
+++ b/src/net/dnsclient_unix_test.go
@@ -411,7 +411,7 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) {
 			// We need to take care with errors on both
 			// DNS message exchange layer and DNS
 			// transport layer because goLookupIP may fail
-			// when the IP connectivty on node under test
+			// when the IP connectivity on node under test
 			// gets lost during its run.
 			if err, ok := err.(*DNSError); !ok || tt.error != nil && (err.Name != tt.error.(*DNSError).Name || err.Server != tt.error.(*DNSError).Server || err.IsTimeout != tt.error.(*DNSError).IsTimeout) {
 				t.Errorf("got %v; want %v", err, tt.error)
@@ -668,12 +668,14 @@ func TestIgnoreDNSForgeries(t *testing.T) {
 		b := make([]byte, 512)
 		n, err := s.Read(b)
 		if err != nil {
-			t.Fatal(err)
+			t.Error(err)
+			return
 		}
 
 		msg := &dnsMsg{}
 		if !msg.Unpack(b[:n]) {
-			t.Fatal("invalid DNS query")
+			t.Error("invalid DNS query")
+			return
 		}
 
 		s.Write([]byte("garbage DNS response packet"))
@@ -682,7 +684,8 @@ func TestIgnoreDNSForgeries(t *testing.T) {
 		msg.id++ // make invalid ID
 		b, ok := msg.Pack()
 		if !ok {
-			t.Fatal("failed to pack DNS response")
+			t.Error("failed to pack DNS response")
+			return
 		}
 		s.Write(b)
 
@@ -701,7 +704,8 @@ func TestIgnoreDNSForgeries(t *testing.T) {
 
 		b, ok = msg.Pack()
 		if !ok {
-			t.Fatal("failed to pack DNS response")
+			t.Error("failed to pack DNS response")
+			return
 		}
 		s.Write(b)
 	}()
@@ -797,3 +801,73 @@ func TestRetryTimeout(t *testing.T) {
 		t.Error("deadline0 still zero", deadline0)
 	}
 }
+
+func TestRotate(t *testing.T) {
+	// without rotation, always uses the first server
+	testRotate(t, false, []string{"192.0.2.1", "192.0.2.2"}, []string{"192.0.2.1:53", "192.0.2.1:53", "192.0.2.1:53"})
+
+	// with rotation, rotates through back to first
+	testRotate(t, true, []string{"192.0.2.1", "192.0.2.2"}, []string{"192.0.2.1:53", "192.0.2.2:53", "192.0.2.1:53"})
+}
+
+func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) {
+	origTestHookDNSDialer := testHookDNSDialer
+	defer func() { testHookDNSDialer = origTestHookDNSDialer }()
+
+	conf, err := newResolvConfTest()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer conf.teardown()
+
+	var confLines []string
+	for _, ns := range nameservers {
+		confLines = append(confLines, "nameserver "+ns)
+	}
+	if rotate {
+		confLines = append(confLines, "options rotate")
+	}
+
+	if err := conf.writeAndUpdate(confLines); err != nil {
+		t.Fatal(err)
+	}
+
+	d := &fakeDNSDialer{}
+	testHookDNSDialer = func() dnsDialer { return d }
+
+	var usedServers []string
+	d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
+		usedServers = append(usedServers, s)
+
+		r := &dnsMsg{
+			dnsMsgHdr: dnsMsgHdr{
+				id:                  q.id,
+				response:            true,
+				recursion_available: true,
+			},
+			question: q.question,
+			answer: []dnsRR{
+				&dnsRR_CNAME{
+					Hdr: dnsRR_Header{
+						Name:   q.question[0].Name,
+						Rrtype: dnsTypeCNAME,
+						Class:  dnsClassINET,
+					},
+					Cname: "golang.org",
+				},
+			},
+		}
+		return r, nil
+	}
+
+	// len(nameservers) + 1 to allow rotation to get back to start
+	for i := 0; i < len(nameservers)+1; i++ {
+		if _, err := goLookupCNAME(context.Background(), "www.golang.org"); err != nil {
+			t.Fatal(err)
+		}
+	}
+
+	if !reflect.DeepEqual(usedServers, wantServers) {
+		t.Fatalf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate, usedServers, wantServers)
+	}
+}
diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go
index aec575e..9c8108d 100644
--- a/src/net/dnsconfig_unix.go
+++ b/src/net/dnsconfig_unix.go
@@ -10,6 +10,7 @@ package net
 
 import (
 	"os"
+	"sync/atomic"
 	"time"
 )
 
@@ -29,6 +30,7 @@ type dnsConfig struct {
 	lookup     []string      // OpenBSD top-level database "lookup" order
 	err        error         // any error that occurs during open of resolv.conf
 	mtime      time.Time     // time of resolv.conf modification
+	soffset    uint32        // used by serverOffset
 }
 
 // See resolv.conf(5) on a Linux machine.
@@ -91,19 +93,21 @@ func dnsReadConfig(filename string) *dnsConfig {
 			for _, s := range f[1:] {
 				switch {
 				case hasPrefix(s, "ndots:"):
-					n, _, _ := dtoi(s, 6)
-					if n < 1 {
-						n = 1
+					n, _, _ := dtoi(s[6:])
+					if n < 0 {
+						n = 0
+					} else if n > 15 {
+						n = 15
 					}
 					conf.ndots = n
 				case hasPrefix(s, "timeout:"):
-					n, _, _ := dtoi(s, 8)
+					n, _, _ := dtoi(s[8:])
 					if n < 1 {
 						n = 1
 					}
 					conf.timeout = time.Duration(n) * time.Second
 				case hasPrefix(s, "attempts:"):
-					n, _, _ := dtoi(s, 9)
+					n, _, _ := dtoi(s[9:])
 					if n < 1 {
 						n = 1
 					}
@@ -134,6 +138,17 @@ func dnsReadConfig(filename string) *dnsConfig {
 	return conf
 }
 
+// serverOffset returns an offset that can be used to determine
+// indices of servers in c.servers when making queries.
+// When the rotate option is enabled, this offset increases.
+// Otherwise it is always 0.
+func (c *dnsConfig) serverOffset() uint32 {
+	if c.rotate {
+		return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start
+	}
+	return 0
+}
+
 func dnsDefaultSearch() []string {
 	hn, err := getHostname()
 	if err != nil {
diff --git a/src/net/dnsconfig_unix_test.go b/src/net/dnsconfig_unix_test.go
index 9fd6dbf..37bdeb0 100644
--- a/src/net/dnsconfig_unix_test.go
+++ b/src/net/dnsconfig_unix_test.go
@@ -10,6 +10,7 @@ import (
 	"errors"
 	"os"
 	"reflect"
+	"strings"
 	"testing"
 	"time"
 )
@@ -61,6 +62,36 @@ var dnsReadConfigTests = []struct {
 		},
 	},
 	{
+		name: "testdata/invalid-ndots-resolv.conf",
+		want: &dnsConfig{
+			servers:  defaultNS,
+			ndots:    0,
+			timeout:  5 * time.Second,
+			attempts: 2,
+			search:   []string{"domain.local."},
+		},
+	},
+	{
+		name: "testdata/large-ndots-resolv.conf",
+		want: &dnsConfig{
+			servers:  defaultNS,
+			ndots:    15,
+			timeout:  5 * time.Second,
+			attempts: 2,
+			search:   []string{"domain.local."},
+		},
+	},
+	{
+		name: "testdata/negative-ndots-resolv.conf",
+		want: &dnsConfig{
+			servers:  defaultNS,
+			ndots:    0,
+			timeout:  5 * time.Second,
+			attempts: 2,
+			search:   []string{"domain.local."},
+		},
+	},
+	{
 		name: "testdata/openbsd-resolv.conf",
 		want: &dnsConfig{
 			ndots:    1,
@@ -154,3 +185,55 @@ func TestDNSDefaultSearch(t *testing.T) {
 		}
 	}
 }
+
+func TestDNSNameLength(t *testing.T) {
+	origGetHostname := getHostname
+	defer func() { getHostname = origGetHostname }()
+	getHostname = func() (string, error) { return "host.domain.local", nil }
+
+	var char63 = ""
+	for i := 0; i < 63; i++ {
+		char63 += "a"
+	}
+	longDomain := strings.Repeat(char63+".", 5) + "example"
+
+	for _, tt := range dnsReadConfigTests {
+		conf := dnsReadConfig(tt.name)
+		if conf.err != nil {
+			t.Fatal(conf.err)
+		}
+
+		var shortestSuffix int
+		for _, suffix := range tt.want.search {
+			if shortestSuffix == 0 || len(suffix) < shortestSuffix {
+				shortestSuffix = len(suffix)
+			}
+		}
+
+		// Test a name that will be maximally long when prefixing the shortest
+		// suffix (accounting for the intervening dot).
+		longName := longDomain[len(longDomain)-254+1+shortestSuffix:]
+		if longName[0] == '.' || longName[1] == '.' {
+			longName = "aa." + longName[3:]
+		}
+		for _, fqdn := range conf.nameList(longName) {
+			if len(fqdn) > 254 {
+				t.Errorf("got %d; want less than or equal to 254", len(fqdn))
+			}
+		}
+
+		// Now test a name that's too long for suffixing.
+		unsuffixable := "a." + longName[1:]
+		unsuffixableResults := conf.nameList(unsuffixable)
+		if len(unsuffixableResults) != 1 {
+			t.Errorf("suffixed names %v; want []", unsuffixableResults[1:])
+		}
+
+		// Now test a name that's too long for DNS.
+		tooLong := "a." + longDomain
+		tooLongResults := conf.nameList(tooLong)
+		if tooLongResults != nil {
+			t.Errorf("suffixed names %v; want nil", tooLongResults)
+		}
+	}
+}
diff --git a/src/net/dnsmsg.go b/src/net/dnsmsg.go
index afdb44c..8f6c7b6 100644
--- a/src/net/dnsmsg.go
+++ b/src/net/dnsmsg.go
@@ -69,7 +69,7 @@ const (
 )
 
 // A dnsStruct describes how to iterate over its fields to emulate
-// reflective marshalling.
+// reflective marshaling.
 type dnsStruct interface {
 	// Walk iterates over fields of a structure and calls f
 	// with a reference to that field, the name of the field
diff --git a/src/net/dnsmsg_test.go b/src/net/dnsmsg_test.go
index 25bd98c..2a25a21 100644
--- a/src/net/dnsmsg_test.go
+++ b/src/net/dnsmsg_test.go
@@ -117,7 +117,7 @@ func TestDNSParseSRVReply(t *testing.T) {
 	if !ok {
 		t.Fatal("unpacking packet failed")
 	}
-	msg.String() // exercise this code path
+	_ = msg.String() // exercise this code path
 	if g, e := len(msg.answer), 5; g != e {
 		t.Errorf("len(msg.answer) = %d; want %d", g, e)
 	}
@@ -165,7 +165,7 @@ func TestDNSParseCorruptSRVReply(t *testing.T) {
 	if !ok {
 		t.Fatal("unpacking packet failed")
 	}
-	msg.String() // exercise this code path
+	_ = msg.String() // exercise this code path
 	if g, e := len(msg.answer), 5; g != e {
 		t.Errorf("len(msg.answer) = %d; want %d", g, e)
 	}
@@ -393,7 +393,7 @@ func TestIsResponseTo(t *testing.T) {
 
 	for i := range badResponses {
 		if badResponses[i].IsResponseTo(&query) {
-			t.Error("%v: got true, want false", i)
+			t.Errorf("%v: got true, want false", i)
 		}
 	}
 }
diff --git a/src/net/dnsname_test.go b/src/net/dnsname_test.go
index bc777b8..e0f786d 100644
--- a/src/net/dnsname_test.go
+++ b/src/net/dnsname_test.go
@@ -32,14 +32,12 @@ var dnsNameTests = []dnsNameTest{
 
 func emitDNSNameTest(ch chan<- dnsNameTest) {
 	defer close(ch)
-	var char59 = ""
 	var char63 = ""
-	var char64 = ""
-	for i := 0; i < 59; i++ {
-		char59 += "a"
+	for i := 0; i < 63; i++ {
+		char63 += "a"
 	}
-	char63 = char59 + "aaaa"
-	char64 = char63 + "a"
+	char64 := char63 + "a"
+	longDomain := strings.Repeat(char63+".", 5) + "example"
 
 	for _, tc := range dnsNameTests {
 		ch <- tc
@@ -47,14 +45,15 @@ func emitDNSNameTest(ch chan<- dnsNameTest) {
 
 	ch <- dnsNameTest{char63 + ".com", true}
 	ch <- dnsNameTest{char64 + ".com", false}
-	// 255 char name is fine:
-	ch <- dnsNameTest{char59 + "." + char63 + "." + char63 + "." +
-		char63 + ".com",
-		true}
-	// 256 char name is bad:
-	ch <- dnsNameTest{char59 + "a." + char63 + "." + char63 + "." +
-		char63 + ".com",
-		false}
+
+	// Remember: wire format is two octets longer than presentation
+	// (length octets for the first and [root] last labels).
+	// 253 is fine:
+	ch <- dnsNameTest{longDomain[len(longDomain)-253:], true}
+	// A terminal dot doesn't contribute to length:
+	ch <- dnsNameTest{longDomain[len(longDomain)-253:] + ".", true}
+	// 254 is bad:
+	ch <- dnsNameTest{longDomain[len(longDomain)-254:], false}
 }
 
 func TestDNSName(t *testing.T) {
diff --git a/src/net/error_test.go b/src/net/error_test.go
index d6de5a3..c23da49 100644
--- a/src/net/error_test.go
+++ b/src/net/error_test.go
@@ -97,7 +97,8 @@ second:
 		goto third
 	}
 	switch nestedErr {
-	case errCanceled, errClosing, errMissingAddress, errNoSuitableAddress:
+	case errCanceled, errClosing, errMissingAddress, errNoSuitableAddress,
+		context.DeadlineExceeded, context.Canceled:
 		return nil
 	}
 	return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
@@ -230,23 +231,27 @@ func TestDialAddrError(t *testing.T) {
 	} {
 		var err error
 		var c Conn
+		var op string
 		if tt.lit != "" {
 			c, err = Dial(tt.network, JoinHostPort(tt.lit, "0"))
+			op = fmt.Sprintf("Dial(%q, %q)", tt.network, JoinHostPort(tt.lit, "0"))
 		} else {
 			c, err = DialTCP(tt.network, nil, tt.addr)
+			op = fmt.Sprintf("DialTCP(%q, %q)", tt.network, tt.addr)
 		}
 		if err == nil {
 			c.Close()
-			t.Errorf("%s %q/%v: should fail", tt.network, tt.lit, tt.addr)
+			t.Errorf("%s succeeded, want error", op)
 			continue
 		}
 		if perr := parseDialError(err); perr != nil {
-			t.Error(perr)
+			t.Errorf("%s: %v", op, perr)
 			continue
 		}
-		aerr, ok := err.(*OpError).Err.(*AddrError)
+		operr := err.(*OpError).Err
+		aerr, ok := operr.(*AddrError)
 		if !ok {
-			t.Errorf("%s %q/%v: should be AddrError: %v", tt.network, tt.lit, tt.addr, err)
+			t.Errorf("%s: %v is %T, want *AddrError", op, err, operr)
 			continue
 		}
 		want := tt.lit
@@ -254,7 +259,7 @@ func TestDialAddrError(t *testing.T) {
 			want = tt.addr.IP.String()
 		}
 		if aerr.Addr != want {
-			t.Fatalf("%s: got %q; want %q", tt.network, aerr.Addr, want)
+			t.Errorf("%s: %v, error Addr=%q, want %q", op, err, aerr.Addr, want)
 		}
 	}
 }
@@ -521,6 +526,10 @@ third:
 	if isPlatformError(nestedErr) {
 		return nil
 	}
+	switch nestedErr {
+	case os.ErrClosed: // for Plan 9
+		return nil
+	}
 	return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
 }
 
diff --git a/src/net/example_test.go b/src/net/example_test.go
index 6f2f907..9dd1732 100644
--- a/src/net/example_test.go
+++ b/src/net/example_test.go
@@ -5,6 +5,7 @@
 package net_test
 
 import (
+	"fmt"
 	"io"
 	"log"
 	"net"
@@ -34,3 +35,15 @@ func ExampleListener() {
 		}(conn)
 	}
 }
+
+func ExampleCIDRMask() {
+	// This mask corresponds to a /31 subnet for IPv4.
+	fmt.Println(net.CIDRMask(31, 32))
+
+	// This mask corresponds to a /64 subnet for IPv6.
+	fmt.Println(net.CIDRMask(64, 128))
+
+	// Output:
+	// fffffffe
+	// ffffffffffffffff0000000000000000
+}
diff --git a/src/net/fd_io_plan9.go b/src/net/fd_io_plan9.go
new file mode 100644
index 0000000..76da0c5
--- /dev/null
+++ b/src/net/fd_io_plan9.go
@@ -0,0 +1,93 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+	"os"
+	"runtime"
+	"sync"
+	"syscall"
+)
+
+// asyncIO implements asynchronous cancelable I/O.
+// An asyncIO represents a single asynchronous Read or Write
+// operation. The result is returned on the result channel.
+// The undergoing I/O system call can either complete or be
+// interrupted by a note.
+type asyncIO struct {
+	res chan result
+
+	// mu guards the pid field.
+	mu sync.Mutex
+
+	// pid holds the process id of
+	// the process running the IO operation.
+	pid int
+}
+
+// result is the return value of a Read or Write operation.
+type result struct {
+	n   int
+	err error
+}
+
+// newAsyncIO returns a new asyncIO that performs an I/O
+// operation by calling fn, which must do one and only one
+// interruptible system call.
+func newAsyncIO(fn func([]byte) (int, error), b []byte) *asyncIO {
+	aio := &asyncIO{
+		res: make(chan result, 0),
+	}
+	aio.mu.Lock()
+	go func() {
+		// Lock the current goroutine to its process
+		// and store the pid in io so that Cancel can
+		// interrupt it. We ignore the "hangup" signal,
+		// so the signal does not take down the entire
+		// Go runtime.
+		runtime.LockOSThread()
+		runtime_ignoreHangup()
+		aio.pid = os.Getpid()
+		aio.mu.Unlock()
+
+		n, err := fn(b)
+
+		aio.mu.Lock()
+		aio.pid = -1
+		runtime_unignoreHangup()
+		aio.mu.Unlock()
+
+		aio.res <- result{n, err}
+	}()
+	return aio
+}
+
+var hangupNote os.Signal = syscall.Note("hangup")
+
+// Cancel interrupts the I/O operation, causing
+// the Wait function to return.
+func (aio *asyncIO) Cancel() {
+	aio.mu.Lock()
+	defer aio.mu.Unlock()
+	if aio.pid == -1 {
+		return
+	}
+	proc, err := os.FindProcess(aio.pid)
+	if err != nil {
+		return
+	}
+	proc.Signal(hangupNote)
+}
+
+// Wait for the I/O operation to complete.
+func (aio *asyncIO) Wait() (int, error) {
+	res := <-aio.res
+	return res.n, res.err
+}
+
+// The following functions, provided by the runtime, are used to
+// ignore and unignore the "hangup" signal received by the process.
+func runtime_ignoreHangup()
+func runtime_unignoreHangup()
diff --git a/src/net/fd_plan9.go b/src/net/fd_plan9.go
index 7533232..300d8c4 100644
--- a/src/net/fd_plan9.go
+++ b/src/net/fd_plan9.go
@@ -7,21 +7,37 @@ package net
 import (
 	"io"
 	"os"
+	"sync/atomic"
 	"syscall"
 	"time"
 )
 
+type atomicBool int32
+
+func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
+func (b *atomicBool) setFalse()   { atomic.StoreInt32((*int32)(b), 0) }
+func (b *atomicBool) setTrue()    { atomic.StoreInt32((*int32)(b), 1) }
+
 // Network file descriptor.
 type netFD struct {
 	// locking/lifetime of sysfd + serialize access to Read and Write methods
 	fdmu fdMutex
 
 	// immutable until Close
-	net          string
-	n            string
-	dir          string
-	ctl, data    *os.File
-	laddr, raddr Addr
+	net               string
+	n                 string
+	dir               string
+	listen, ctl, data *os.File
+	laddr, raddr      Addr
+	isStream          bool
+
+	// deadlines
+	raio      *asyncIO
+	waio      *asyncIO
+	rtimer    *time.Timer
+	wtimer    *time.Timer
+	rtimedout atomicBool // set true when read deadline has been reached
+	wtimedout atomicBool // set true when write deadline has been reached
 }
 
 var (
@@ -32,8 +48,16 @@ func sysInit() {
 	netdir = "/net"
 }
 
-func newFD(net, name string, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) {
-	return &netFD{net: net, n: name, dir: netdir + "/" + net + "/" + name, ctl: ctl, data: data, laddr: laddr, raddr: raddr}, nil
+func newFD(net, name string, listen, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) {
+	return &netFD{
+		net:    net,
+		n:      name,
+		dir:    netdir + "/" + net + "/" + name,
+		listen: listen,
+		ctl:    ctl, data: data,
+		laddr: laddr,
+		raddr: raddr,
+	}, nil
 }
 
 func (fd *netFD) init() error {
@@ -64,11 +88,20 @@ func (fd *netFD) destroy() {
 			err = err1
 		}
 	}
+	if fd.listen != nil {
+		if err1 := fd.listen.Close(); err1 != nil && err == nil {
+			err = err1
+		}
+	}
 	fd.ctl = nil
 	fd.data = nil
+	fd.listen = nil
 }
 
 func (fd *netFD) Read(b []byte) (n int, err error) {
+	if fd.rtimedout.isSet() {
+		return 0, errTimeout
+	}
 	if !fd.ok() || fd.data == nil {
 		return 0, syscall.EINVAL
 	}
@@ -79,10 +112,15 @@ func (fd *netFD) Read(b []byte) (n int, err error) {
 	if len(b) == 0 {
 		return 0, nil
 	}
-	n, err = fd.data.Read(b)
+	fd.raio = newAsyncIO(fd.data.Read, b)
+	n, err = fd.raio.Wait()
+	fd.raio = nil
 	if isHangup(err) {
 		err = io.EOF
 	}
+	if isInterrupted(err) {
+		err = errTimeout
+	}
 	if fd.net == "udp" && err == io.EOF {
 		n = 0
 		err = nil
@@ -91,6 +129,9 @@ func (fd *netFD) Read(b []byte) (n int, err error) {
 }
 
 func (fd *netFD) Write(b []byte) (n int, err error) {
+	if fd.wtimedout.isSet() {
+		return 0, errTimeout
+	}
 	if !fd.ok() || fd.data == nil {
 		return 0, syscall.EINVAL
 	}
@@ -98,7 +139,13 @@ func (fd *netFD) Write(b []byte) (n int, err error) {
 		return 0, err
 	}
 	defer fd.writeUnlock()
-	return fd.data.Write(b)
+	fd.waio = newAsyncIO(fd.data.Write, b)
+	n, err = fd.waio.Wait()
+	fd.waio = nil
+	if isInterrupted(err) {
+		err = errTimeout
+	}
+	return
 }
 
 func (fd *netFD) closeRead() error {
@@ -124,11 +171,10 @@ func (fd *netFD) Close() error {
 	}
 	if fd.net == "tcp" {
 		// The following line is required to unblock Reads.
-		// For some reason, WriteString returns an error:
-		// "write /net/tcp/39/listen: inappropriate use of fd"
-		// But without it, Reads on dead conns hang forever.
-		// See Issue 9554.
-		fd.ctl.WriteString("hangup")
+		_, err := fd.ctl.WriteString("close")
+		if err != nil {
+			return err
+		}
 	}
 	err := fd.ctl.Close()
 	if fd.data != nil {
@@ -136,8 +182,14 @@ func (fd *netFD) Close() error {
 			err = err1
 		}
 	}
+	if fd.listen != nil {
+		if err1 := fd.listen.Close(); err1 != nil && err == nil {
+			err = err1
+		}
+	}
 	fd.ctl = nil
 	fd.data = nil
+	fd.listen = nil
 	return err
 }
 
@@ -165,15 +217,74 @@ func (fd *netFD) file(f *os.File, s string) (*os.File, error) {
 }
 
 func (fd *netFD) setDeadline(t time.Time) error {
-	return syscall.EPLAN9
+	return setDeadlineImpl(fd, t, 'r'+'w')
 }
 
 func (fd *netFD) setReadDeadline(t time.Time) error {
-	return syscall.EPLAN9
+	return setDeadlineImpl(fd, t, 'r')
 }
 
 func (fd *netFD) setWriteDeadline(t time.Time) error {
-	return syscall.EPLAN9
+	return setDeadlineImpl(fd, t, 'w')
+}
+
+func setDeadlineImpl(fd *netFD, t time.Time, mode int) error {
+	d := t.Sub(time.Now())
+	if mode == 'r' || mode == 'r'+'w' {
+		fd.rtimedout.setFalse()
+	}
+	if mode == 'w' || mode == 'r'+'w' {
+		fd.wtimedout.setFalse()
+	}
+	if t.IsZero() || d < 0 {
+		// Stop timer
+		if mode == 'r' || mode == 'r'+'w' {
+			if fd.rtimer != nil {
+				fd.rtimer.Stop()
+			}
+			fd.rtimer = nil
+		}
+		if mode == 'w' || mode == 'r'+'w' {
+			if fd.wtimer != nil {
+				fd.wtimer.Stop()
+			}
+			fd.wtimer = nil
+		}
+	} else {
+		// Interrupt I/O operation once timer has expired
+		if mode == 'r' || mode == 'r'+'w' {
+			fd.rtimer = time.AfterFunc(d, func() {
+				fd.rtimedout.setTrue()
+				if fd.raio != nil {
+					fd.raio.Cancel()
+				}
+			})
+		}
+		if mode == 'w' || mode == 'r'+'w' {
+			fd.wtimer = time.AfterFunc(d, func() {
+				fd.wtimedout.setTrue()
+				if fd.waio != nil {
+					fd.waio.Cancel()
+				}
+			})
+		}
+	}
+	if !t.IsZero() && d < 0 {
+		// Interrupt current I/O operation
+		if mode == 'r' || mode == 'r'+'w' {
+			fd.rtimedout.setTrue()
+			if fd.raio != nil {
+				fd.raio.Cancel()
+			}
+		}
+		if mode == 'w' || mode == 'r'+'w' {
+			fd.wtimedout.setTrue()
+			if fd.waio != nil {
+				fd.waio.Cancel()
+			}
+		}
+	}
+	return nil
 }
 
 func setReadBuffer(fd *netFD, bytes int) error {
@@ -187,3 +298,7 @@ func setWriteBuffer(fd *netFD, bytes int) error {
 func isHangup(err error) bool {
 	return err != nil && stringsHasSuffix(err.Error(), "Hangup")
 }
+
+func isInterrupted(err error) bool {
+	return err != nil && stringsHasSuffix(err.Error(), "interrupted")
+}
diff --git a/src/net/fd_poll_nacl.go b/src/net/fd_poll_nacl.go
index cda8b82..8398760 100644
--- a/src/net/fd_poll_nacl.go
+++ b/src/net/fd_poll_nacl.go
@@ -5,6 +5,7 @@
 package net
 
 import (
+	"runtime"
 	"syscall"
 	"time"
 )
@@ -22,6 +23,7 @@ func (pd *pollDesc) evict() {
 	pd.closing = true
 	if pd.fd != nil {
 		syscall.StopIO(pd.fd.sysfd)
+		runtime.KeepAlive(pd.fd)
 	}
 }
 
diff --git a/src/net/fd_poll_runtime.go b/src/net/fd_poll_runtime.go
index 6c1d095..62b69fc 100644
--- a/src/net/fd_poll_runtime.go
+++ b/src/net/fd_poll_runtime.go
@@ -7,6 +7,7 @@
 package net
 
 import (
+	"runtime"
 	"sync"
 	"syscall"
 	"time"
@@ -33,6 +34,7 @@ var serverInit sync.Once
 func (pd *pollDesc) init(fd *netFD) error {
 	serverInit.Do(runtime_pollServerInit)
 	ctx, errno := runtime_pollOpen(uintptr(fd.sysfd))
+	runtime.KeepAlive(fd)
 	if errno != 0 {
 		return syscall.Errno(errno)
 	}
@@ -120,7 +122,7 @@ func (fd *netFD) setWriteDeadline(t time.Time) error {
 }
 
 func setDeadlineImpl(fd *netFD, t time.Time, mode int) error {
-	diff := int64(t.Sub(time.Now()))
+	diff := int64(time.Until(t))
 	d := runtimeNano() + diff
 	if d <= 0 && diff > 0 {
 		// If the user has a deadline in the future, but the delay calculation
diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go
index 11dde76..3c95fc0 100644
--- a/src/net/fd_unix.go
+++ b/src/net/fd_unix.go
@@ -24,11 +24,15 @@ type netFD struct {
 	sysfd       int
 	family      int
 	sotype      int
+	isStream    bool
 	isConnected bool
 	net         string
 	laddr       Addr
 	raddr       Addr
 
+	// writev cache.
+	iovecs *[]syscall.Iovec
+
 	// wait server
 	pd pollDesc
 }
@@ -37,7 +41,7 @@ func sysInit() {
 }
 
 func newFD(sysfd, family, sotype int, net string) (*netFD, error) {
-	return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net}, nil
+	return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net, isStream: sotype == syscall.SOCK_STREAM}, nil
 }
 
 func (fd *netFD) init() error {
@@ -235,6 +239,9 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
 	if err := fd.pd.prepareRead(); err != nil {
 		return 0, err
 	}
+	if fd.isStream && len(p) > 1<<30 {
+		p = p[:1<<30]
+	}
 	for {
 		n, err = syscall.Read(fd.sysfd, p)
 		if err != nil {
@@ -318,7 +325,11 @@ func (fd *netFD) Write(p []byte) (nn int, err error) {
 	}
 	for {
 		var n int
-		n, err = syscall.Write(fd.sysfd, p[nn:])
+		max := len(p)
+		if fd.isStream && max-nn > 1<<30 {
+			max = nn + 1<<30
+		}
+		n, err = syscall.Write(fd.sysfd, p[nn:max])
 		if n > 0 {
 			nn += n
 		}
diff --git a/src/net/fd_windows.go b/src/net/fd_windows.go
index b0b6769..a976f2a 100644
--- a/src/net/fd_windows.go
+++ b/src/net/fd_windows.go
@@ -96,6 +96,7 @@ type operation struct {
 	rsan   int32
 	handle syscall.Handle
 	flags  uint32
+	bufs   []syscall.WSABuf
 }
 
 func (o *operation) InitBuf(buf []byte) {
@@ -106,6 +107,30 @@ func (o *operation) InitBuf(buf []byte) {
 	}
 }
 
+func (o *operation) InitBufs(buf *Buffers) {
+	if o.bufs == nil {
+		o.bufs = make([]syscall.WSABuf, 0, len(*buf))
+	} else {
+		o.bufs = o.bufs[:0]
+	}
+	for _, b := range *buf {
+		var p *byte
+		if len(b) > 0 {
+			p = &b[0]
+		}
+		o.bufs = append(o.bufs, syscall.WSABuf{Len: uint32(len(b)), Buf: p})
+	}
+}
+
+// ClearBufs clears all pointers to Buffers parameter captured
+// by InitBufs, so it can be released by garbage collector.
+func (o *operation) ClearBufs() {
+	for i := range o.bufs {
+		o.bufs[i].Buf = nil
+	}
+	o.bufs = o.bufs[:0]
+}
+
 // ioSrv executes net IO requests.
 type ioSrv struct {
 	req chan ioSrvReq
@@ -239,6 +264,7 @@ type netFD struct {
 	sysfd         syscall.Handle
 	family        int
 	sotype        int
+	isStream      bool
 	isConnected   bool
 	skipSyncNotif bool
 	net           string
@@ -257,7 +283,7 @@ func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error)
 		return nil, initErr
 	}
 	onceStartServer.Do(startServer)
-	return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net}, nil
+	return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net, isStream: sotype == syscall.SOCK_STREAM}, nil
 }
 
 func (fd *netFD) init() error {
@@ -483,6 +509,42 @@ func (fd *netFD) Write(buf []byte) (int, error) {
 	return n, err
 }
 
+func (c *conn) writeBuffers(v *Buffers) (int64, error) {
+	if !c.ok() {
+		return 0, syscall.EINVAL
+	}
+	n, err := c.fd.writeBuffers(v)
+	if err != nil {
+		return n, &OpError{Op: "WSASend", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+	}
+	return n, nil
+}
+
+func (fd *netFD) writeBuffers(buf *Buffers) (int64, error) {
+	if len(*buf) == 0 {
+		return 0, nil
+	}
+	if err := fd.writeLock(); err != nil {
+		return 0, err
+	}
+	defer fd.writeUnlock()
+	if race.Enabled {
+		race.ReleaseMerge(unsafe.Pointer(&ioSync))
+	}
+	o := &fd.wop
+	o.InitBufs(buf)
+	n, err := wsrv.ExecIO(o, "WSASend", func(o *operation) error {
+		return syscall.WSASend(o.fd.sysfd, &o.bufs[0], uint32(len(*buf)), &o.qty, 0, &o.o, nil)
+	})
+	o.ClearBufs()
+	if _, ok := err.(syscall.Errno); ok {
+		err = os.NewSyscallError("wsasend", err)
+	}
+	testHookDidWritev(n)
+	buf.consume(int64(n))
+	return int64(n), err
+}
+
 func (fd *netFD) writeTo(buf []byte, sa syscall.Sockaddr) (int, error) {
 	if len(buf) == 0 {
 		return 0, nil
@@ -541,7 +603,7 @@ func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD
 		netfd.Close()
 		return nil, os.NewSyscallError("setsockopt", err)
 	}
-
+	runtime.KeepAlive(fd)
 	return netfd, nil
 }
 
diff --git a/src/net/file.go b/src/net/file.go
index 1aad477..0709985 100644
--- a/src/net/file.go
+++ b/src/net/file.go
@@ -6,6 +6,9 @@ package net
 
 import "os"
 
+// BUG(mikio): On NaCl and Windows, the FileConn, FileListener and
+// FilePacketConn functions are not implemented.
+
 type fileAddr string
 
 func (fileAddr) Network() string  { return "file+net" }
diff --git a/src/net/file_plan9.go b/src/net/file_plan9.go
index 2939c09..d16e5a1 100644
--- a/src/net/file_plan9.go
+++ b/src/net/file_plan9.go
@@ -81,7 +81,7 @@ func newFileFD(f *os.File) (net *netFD, err error) {
 	if err != nil {
 		return nil, err
 	}
-	return newFD(comp[1], name, ctl, nil, laddr, nil)
+	return newFD(comp[1], name, nil, ctl, nil, laddr, nil)
 }
 
 func fileConn(f *os.File) (Conn, error) {
diff --git a/src/net/http/client.go b/src/net/http/client.go
index 993c247..fe2b019 100644
--- a/src/net/http/client.go
+++ b/src/net/http/client.go
@@ -18,6 +18,7 @@ import (
 	"io/ioutil"
 	"log"
 	"net/url"
+	"sort"
 	"strings"
 	"sync"
 	"time"
@@ -33,6 +34,25 @@ import (
 // A Client is higher-level than a RoundTripper (such as Transport)
 // and additionally handles HTTP details such as cookies and
 // redirects.
+//
+// When following redirects, the Client will forward all headers set on the
+// initial Request except:
+//
+//	* when forwarding sensitive headers like "Authorization",
+//	  "WWW-Authenticate", and "Cookie" to untrusted targets.
+//	  These headers will be ignored when following a redirect to a domain
+//	  that is not a subdomain match or exact match of the initial domain.
+//	  For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com"
+//	  will forward the sensitive headers, but a redirect to "bar.com" will not.
+//
+//	* when forwarding the "Cookie" header with a non-nil cookie Jar.
+//	  Since each redirect may mutate the state of the cookie jar,
+//	  a redirect may possibly alter a cookie set in the initial request.
+//	  When forwarding the "Cookie" header, any mutated cookies will be omitted,
+//	  with the expectation that the Jar will insert those mutated cookies
+//	  with the updated values (assuming the origin matches).
+//	  If Jar is nil, the initial cookies are forwarded without change.
+//
 type Client struct {
 	// Transport specifies the mechanism by which individual
 	// HTTP requests are made.
@@ -56,8 +76,14 @@ type Client struct {
 	CheckRedirect func(req *Request, via []*Request) error
 
 	// Jar specifies the cookie jar.
-	// If Jar is nil, cookies are not sent in requests and ignored
-	// in responses.
+	//
+	// The Jar is used to insert relevant cookies into every
+	// outbound Request and is updated with the cookie values
+	// of every inbound Response. The Jar is consulted for every
+	// redirect that the Client follows.
+	//
+	// If Jar is nil, cookies are only sent if they are explicitly
+	// set on the Request.
 	Jar CookieJar
 
 	// Timeout specifies a time limit for requests made by this
@@ -155,40 +181,6 @@ func (c *Client) send(req *Request, deadline time.Time) (*Response, error) {
 	return resp, nil
 }
 
-// Do sends an HTTP request and returns an HTTP response, following
-// policy (such as redirects, cookies, auth) as configured on the
-// client.
-//
-// An error is returned if caused by client policy (such as
-// CheckRedirect), or failure to speak HTTP (such as a network
-// connectivity problem). A non-2xx status code doesn't cause an
-// error.
-//
-// If the returned error is nil, the Response will contain a non-nil
-// Body which the user is expected to close. If the Body is not
-// closed, the Client's underlying RoundTripper (typically Transport)
-// may not be able to re-use a persistent TCP connection to the server
-// for a subsequent "keep-alive" request.
-//
-// The request Body, if non-nil, will be closed by the underlying
-// Transport, even on errors.
-//
-// On error, any Response can be ignored. A non-nil Response with a
-// non-nil error only occurs when CheckRedirect fails, and even then
-// the returned Response.Body is already closed.
-//
-// Generally Get, Post, or PostForm will be used instead of Do.
-func (c *Client) Do(req *Request) (*Response, error) {
-	method := valueOrDefault(req.Method, "GET")
-	if method == "GET" || method == "HEAD" {
-		return c.doFollowingRedirects(req, shouldRedirectGet)
-	}
-	if method == "POST" || method == "PUT" {
-		return c.doFollowingRedirects(req, shouldRedirectPost)
-	}
-	return c.send(req, c.deadline())
-}
-
 func (c *Client) deadline() time.Time {
 	if c.Timeout > 0 {
 		return time.Now().Add(c.Timeout)
@@ -251,7 +243,7 @@ func send(ireq *Request, rt RoundTripper, deadline time.Time) (*Response, error)
 	if !deadline.IsZero() {
 		forkReq()
 	}
-	stopTimer, wasCanceled := setRequestCancel(req, rt, deadline)
+	stopTimer, didTimeout := setRequestCancel(req, rt, deadline)
 
 	resp, err := rt.RoundTrip(req)
 	if err != nil {
@@ -271,9 +263,9 @@ func send(ireq *Request, rt RoundTripper, deadline time.Time) (*Response, error)
 	}
 	if !deadline.IsZero() {
 		resp.Body = &cancelTimerBody{
-			stop:           stopTimer,
-			rc:             resp.Body,
-			reqWasCanceled: wasCanceled,
+			stop:          stopTimer,
+			rc:            resp.Body,
+			reqDidTimeout: didTimeout,
 		}
 	}
 	return resp, nil
@@ -282,7 +274,7 @@ func send(ireq *Request, rt RoundTripper, deadline time.Time) (*Response, error)
 // setRequestCancel sets the Cancel field of req, if deadline is
 // non-zero. The RoundTripper's type is used to determine whether the legacy
 // CancelRequest behavior should be used.
-func setRequestCancel(req *Request, rt RoundTripper, deadline time.Time) (stopTimer func(), wasCanceled func() bool) {
+func setRequestCancel(req *Request, rt RoundTripper, deadline time.Time) (stopTimer func(), didTimeout func() bool) {
 	if deadline.IsZero() {
 		return nop, alwaysFalse
 	}
@@ -292,15 +284,6 @@ func setRequestCancel(req *Request, rt RoundTripper, deadline time.Time) (stopTi
 	cancel := make(chan struct{})
 	req.Cancel = cancel
 
-	wasCanceled = func() bool {
-		select {
-		case <-cancel:
-			return true
-		default:
-			return false
-		}
-	}
-
 	doCancel := func() {
 		// The new way:
 		close(cancel)
@@ -324,19 +307,23 @@ func setRequestCancel(req *Request, rt RoundTripper, deadline time.Time) (stopTi
 	var once sync.Once
 	stopTimer = func() { once.Do(func() { close(stopTimerCh) }) }
 
-	timer := time.NewTimer(deadline.Sub(time.Now()))
+	timer := time.NewTimer(time.Until(deadline))
+	var timedOut atomicBool
+
 	go func() {
 		select {
 		case <-initialReqCancel:
 			doCancel()
+			timer.Stop()
 		case <-timer.C:
+			timedOut.setTrue()
 			doCancel()
 		case <-stopTimerCh:
 			timer.Stop()
 		}
 	}()
 
-	return stopTimer, wasCanceled
+	return stopTimer, timedOut.isSet
 }
 
 // See 2 (end of page 4) http://www.ietf.org/rfc/rfc2617.txt
@@ -349,26 +336,6 @@ func basicAuth(username, password string) string {
 	return base64.StdEncoding.EncodeToString([]byte(auth))
 }
 
-// True if the specified HTTP status code is one for which the Get utility should
-// automatically redirect.
-func shouldRedirectGet(statusCode int) bool {
-	switch statusCode {
-	case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
-		return true
-	}
-	return false
-}
-
-// True if the specified HTTP status code is one for which the Post utility should
-// automatically redirect.
-func shouldRedirectPost(statusCode int) bool {
-	switch statusCode {
-	case StatusFound, StatusSeeOther:
-		return true
-	}
-	return false
-}
-
 // Get issues a GET to the specified URL. If the response is one of
 // the following redirect codes, Get follows the redirect, up to a
 // maximum of 10 redirects:
@@ -377,6 +344,7 @@ func shouldRedirectPost(statusCode int) bool {
 //    302 (Found)
 //    303 (See Other)
 //    307 (Temporary Redirect)
+//    308 (Permanent Redirect)
 //
 // An error is returned if there were too many redirects or if there
 // was an HTTP protocol error. A non-2xx response doesn't cause an
@@ -401,6 +369,7 @@ func Get(url string) (resp *Response, err error) {
 //    302 (Found)
 //    303 (See Other)
 //    307 (Temporary Redirect)
+//    308 (Permanent Redirect)
 //
 // An error is returned if the Client's CheckRedirect function fails
 // or if there was an HTTP protocol error. A non-2xx response doesn't
@@ -415,7 +384,7 @@ func (c *Client) Get(url string) (resp *Response, err error) {
 	if err != nil {
 		return nil, err
 	}
-	return c.doFollowingRedirects(req, shouldRedirectGet)
+	return c.Do(req)
 }
 
 func alwaysFalse() bool { return false }
@@ -436,16 +405,77 @@ func (c *Client) checkRedirect(req *Request, via []*Request) error {
 	return fn(req, via)
 }
 
-func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) bool) (*Response, error) {
+// redirectBehavior describes what should happen when the
+// client encounters a 3xx status code from the server
+func redirectBehavior(reqMethod string, resp *Response, via []*Request) (redirectMethod string, shouldRedirect bool) {
+	switch resp.StatusCode {
+	case 301, 302, 303:
+		redirectMethod = "GET"
+		shouldRedirect = true
+	case 307, 308:
+		redirectMethod = reqMethod
+		shouldRedirect = true
+
+		// Treat 307 and 308 specially, since they're new in
+		// Go 1.8, and they also require re-sending the request body.
+		loc := resp.Header.Get("Location")
+		if loc == "" {
+			// 308s have been observed in the wild being served
+			// without Location headers. Since Go 1.7 and earlier
+			// didn't follow these codes, just stop here instead
+			// of returning an error.
+			// See Issue 17773.
+			shouldRedirect = false
+			break
+		}
+		ireq := via[0]
+		if ireq.GetBody == nil && ireq.outgoingLength() != 0 {
+			// We had a request body, and 307/308 require
+			// re-sending it, but GetBody is not defined. So just
+			// return this response to the user instead of an
+			// error, like we did in Go 1.7 and earlier.
+			shouldRedirect = false
+		}
+	}
+
+	return redirectMethod, shouldRedirect
+}
+
+// Do sends an HTTP request and returns an HTTP response, following
+// policy (such as redirects, cookies, auth) as configured on the
+// client.
+//
+// An error is returned if caused by client policy (such as
+// CheckRedirect), or failure to speak HTTP (such as a network
+// connectivity problem). A non-2xx status code doesn't cause an
+// error.
+//
+// If the returned error is nil, the Response will contain a non-nil
+// Body which the user is expected to close. If the Body is not
+// closed, the Client's underlying RoundTripper (typically Transport)
+// may not be able to re-use a persistent TCP connection to the server
+// for a subsequent "keep-alive" request.
+//
+// The request Body, if non-nil, will be closed by the underlying
+// Transport, even on errors.
+//
+// On error, any Response can be ignored. A non-nil Response with a
+// non-nil error only occurs when CheckRedirect fails, and even then
+// the returned Response.Body is already closed.
+//
+// Generally Get, Post, or PostForm will be used instead of Do.
+func (c *Client) Do(req *Request) (*Response, error) {
 	if req.URL == nil {
 		req.closeBody()
 		return nil, errors.New("http: nil Request.URL")
 	}
 
 	var (
-		deadline = c.deadline()
-		reqs     []*Request
-		resp     *Response
+		deadline       = c.deadline()
+		reqs           []*Request
+		resp           *Response
+		copyHeaders    = c.makeHeadersCopier(req)
+		redirectMethod string
 	)
 	uerr := func(err error) error {
 		req.closeBody()
@@ -476,16 +506,27 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo
 			}
 			ireq := reqs[0]
 			req = &Request{
-				Method:   ireq.Method,
+				Method:   redirectMethod,
 				Response: resp,
 				URL:      u,
 				Header:   make(Header),
 				Cancel:   ireq.Cancel,
 				ctx:      ireq.ctx,
 			}
-			if ireq.Method == "POST" || ireq.Method == "PUT" {
-				req.Method = "GET"
+			if ireq.GetBody != nil {
+				req.Body, err = ireq.GetBody()
+				if err != nil {
+					return nil, uerr(err)
+				}
+				req.ContentLength = ireq.ContentLength
 			}
+
+			// Copy original headers before setting the Referer,
+			// in case the user set Referer on their first request.
+			// If they really want to override, they can do it in
+			// their CheckRedirect func.
+			copyHeaders(req)
+
 			// Add the Referer header from the most recent
 			// request URL to the new one, if it's not https->http:
 			if ref := refererForURL(reqs[len(reqs)-1].URL, req.URL); ref != "" {
@@ -523,7 +564,6 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo
 		}
 
 		reqs = append(reqs, req)
-
 		var err error
 		if resp, err = c.send(req, deadline); err != nil {
 			if !deadline.IsZero() && !time.Now().Before(deadline) {
@@ -535,9 +575,77 @@ func (c *Client) doFollowingRedirects(req *Request, shouldRedirect func(int) boo
 			return nil, uerr(err)
 		}
 
-		if !shouldRedirect(resp.StatusCode) {
+		var shouldRedirect bool
+		redirectMethod, shouldRedirect = redirectBehavior(req.Method, resp, reqs)
+		if !shouldRedirect {
 			return resp, nil
 		}
+
+		req.closeBody()
+	}
+}
+
+// makeHeadersCopier makes a function that copies headers from the
+// initial Request, ireq. For every redirect, this function must be called
+// so that it can copy headers into the upcoming Request.
+func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
+	// The headers to copy are from the very initial request.
+	// We use a closured callback to keep a reference to these original headers.
+	var (
+		ireqhdr  = ireq.Header.clone()
+		icookies map[string][]*Cookie
+	)
+	if c.Jar != nil && ireq.Header.Get("Cookie") != "" {
+		icookies = make(map[string][]*Cookie)
+		for _, c := range ireq.Cookies() {
+			icookies[c.Name] = append(icookies[c.Name], c)
+		}
+	}
+
+	preq := ireq // The previous request
+	return func(req *Request) {
+		// If Jar is present and there was some initial cookies provided
+		// via the request header, then we may need to alter the initial
+		// cookies as we follow redirects since each redirect may end up
+		// modifying a pre-existing cookie.
+		//
+		// Since cookies already set in the request header do not contain
+		// information about the original domain and path, the logic below
+		// assumes any new set cookies override the original cookie
+		// regardless of domain or path.
+		//
+		// See https://golang.org/issue/17494
+		if c.Jar != nil && icookies != nil {
+			var changed bool
+			resp := req.Response // The response that caused the upcoming redirect
+			for _, c := range resp.Cookies() {
+				if _, ok := icookies[c.Name]; ok {
+					delete(icookies, c.Name)
+					changed = true
+				}
+			}
+			if changed {
+				ireqhdr.Del("Cookie")
+				var ss []string
+				for _, cs := range icookies {
+					for _, c := range cs {
+						ss = append(ss, c.Name+"="+c.Value)
+					}
+				}
+				sort.Strings(ss) // Ensure deterministic headers
+				ireqhdr.Set("Cookie", strings.Join(ss, "; "))
+			}
+		}
+
+		// Copy the initial request's Header values
+		// (at least the safe ones).
+		for k, vv := range ireqhdr {
+			if shouldCopyHeaderOnRedirect(k, preq.URL, req.URL) {
+				req.Header[k] = vv
+			}
+		}
+
+		preq = req // Update previous Request with the current request
 	}
 }
 
@@ -558,8 +666,8 @@ func defaultCheckRedirect(req *Request, via []*Request) error {
 // Post is a wrapper around DefaultClient.Post.
 //
 // To set custom headers, use NewRequest and DefaultClient.Do.
-func Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
-	return DefaultClient.Post(url, bodyType, body)
+func Post(url string, contentType string, body io.Reader) (resp *Response, err error) {
+	return DefaultClient.Post(url, contentType, body)
 }
 
 // Post issues a POST to the specified URL.
@@ -570,13 +678,13 @@ func Post(url string, bodyType string, body io.Reader) (resp *Response, err erro
 // request.
 //
 // To set custom headers, use NewRequest and Client.Do.
-func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
+func (c *Client) Post(url string, contentType string, body io.Reader) (resp *Response, err error) {
 	req, err := NewRequest("POST", url, body)
 	if err != nil {
 		return nil, err
 	}
-	req.Header.Set("Content-Type", bodyType)
-	return c.doFollowingRedirects(req, shouldRedirectPost)
+	req.Header.Set("Content-Type", contentType)
+	return c.Do(req)
 }
 
 // PostForm issues a POST to the specified URL, with data's keys and
@@ -605,7 +713,7 @@ func (c *Client) PostForm(url string, data url.Values) (resp *Response, err erro
 	return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
 }
 
-// Head issues a HEAD to the specified URL.  If the response is one of
+// Head issues a HEAD to the specified URL. If the response is one of
 // the following redirect codes, Head follows the redirect, up to a
 // maximum of 10 redirects:
 //
@@ -613,13 +721,14 @@ func (c *Client) PostForm(url string, data url.Values) (resp *Response, err erro
 //    302 (Found)
 //    303 (See Other)
 //    307 (Temporary Redirect)
+//    308 (Permanent Redirect)
 //
 // Head is a wrapper around DefaultClient.Head
 func Head(url string) (resp *Response, err error) {
 	return DefaultClient.Head(url)
 }
 
-// Head issues a HEAD to the specified URL.  If the response is one of the
+// Head issues a HEAD to the specified URL. If the response is one of the
 // following redirect codes, Head follows the redirect after calling the
 // Client's CheckRedirect function:
 //
@@ -627,22 +736,23 @@ func Head(url string) (resp *Response, err error) {
 //    302 (Found)
 //    303 (See Other)
 //    307 (Temporary Redirect)
+//    308 (Permanent Redirect)
 func (c *Client) Head(url string) (resp *Response, err error) {
 	req, err := NewRequest("HEAD", url, nil)
 	if err != nil {
 		return nil, err
 	}
-	return c.doFollowingRedirects(req, shouldRedirectGet)
+	return c.Do(req)
 }
 
 // cancelTimerBody is an io.ReadCloser that wraps rc with two features:
 // 1) on Read error or close, the stop func is called.
-// 2) On Read failure, if reqWasCanceled is true, the error is wrapped and
+// 2) On Read failure, if reqDidTimeout is true, the error is wrapped and
 //    marked as net.Error that hit its timeout.
 type cancelTimerBody struct {
-	stop           func() // stops the time.Timer waiting to cancel the request
-	rc             io.ReadCloser
-	reqWasCanceled func() bool
+	stop          func() // stops the time.Timer waiting to cancel the request
+	rc            io.ReadCloser
+	reqDidTimeout func() bool
 }
 
 func (b *cancelTimerBody) Read(p []byte) (n int, err error) {
@@ -654,7 +764,7 @@ func (b *cancelTimerBody) Read(p []byte) (n int, err error) {
 	if err == io.EOF {
 		return n, err
 	}
-	if b.reqWasCanceled() {
+	if b.reqDidTimeout() {
 		err = &httpError{
 			err:     err.Error() + " (Client.Timeout exceeded while reading body)",
 			timeout: true,
@@ -668,3 +778,52 @@ func (b *cancelTimerBody) Close() error {
 	b.stop()
 	return err
 }
+
+func shouldCopyHeaderOnRedirect(headerKey string, initial, dest *url.URL) bool {
+	switch CanonicalHeaderKey(headerKey) {
+	case "Authorization", "Www-Authenticate", "Cookie", "Cookie2":
+		// Permit sending auth/cookie headers from "foo.com"
+		// to "sub.foo.com".
+
+		// Note that we don't send all cookies to subdomains
+		// automatically. This function is only used for
+		// Cookies set explicitly on the initial outgoing
+		// client request. Cookies automatically added via the
+		// CookieJar mechanism continue to follow each
+		// cookie's scope as set by Set-Cookie. But for
+		// outgoing requests with the Cookie header set
+		// directly, we don't know their scope, so we assume
+		// it's for *.domain.com.
+
+		// TODO(bradfitz): once issue 16142 is fixed, make
+		// this code use those URL accessors, and consider
+		// "http://foo.com" and "http://foo.com:80" as
+		// equivalent?
+
+		// TODO(bradfitz): better hostname canonicalization,
+		// at least once we figure out IDNA/Punycode (issue
+		// 13835).
+		ihost := strings.ToLower(initial.Host)
+		dhost := strings.ToLower(dest.Host)
+		return isDomainOrSubdomain(dhost, ihost)
+	}
+	// All other headers are copied:
+	return true
+}
+
+// isDomainOrSubdomain reports whether sub is a subdomain (or exact
+// match) of the parent domain.
+//
+// Both domains must already be in canonical form.
+func isDomainOrSubdomain(sub, parent string) bool {
+	if sub == parent {
+		return true
+	}
+	// If sub is "foo.example.com" and parent is "example.com",
+	// that means sub must end in "."+parent.
+	// Do it without allocating.
+	if !strings.HasSuffix(sub, parent) {
+		return false
+	}
+	return sub[len(sub)-len(parent)-1] == '.'
+}
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go
index a9b1948..a5f58cb 100644
--- a/src/net/http/client_test.go
+++ b/src/net/http/client_test.go
@@ -19,8 +19,10 @@ import (
 	"log"
 	"net"
 	. "net/http"
+	"net/http/cookiejar"
 	"net/http/httptest"
 	"net/url"
+	"reflect"
 	"strconv"
 	"strings"
 	"sync"
@@ -65,11 +67,13 @@ func (w chanWriter) Write(p []byte) (n int, err error) {
 }
 
 func TestClient(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewServer(robotsTxtHandler)
 	defer ts.Close()
 
-	r, err := Get(ts.URL)
+	c := &Client{Transport: &Transport{DisableKeepAlives: true}}
+	r, err := c.Get(ts.URL)
 	var b []byte
 	if err == nil {
 		b, err = pedanticReadAll(r.Body)
@@ -109,6 +113,7 @@ func (t *recordingTransport) RoundTrip(req *Request) (resp *Response, err error)
 }
 
 func TestGetRequestFormat(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	tr := &recordingTransport{}
 	client := &Client{Transport: tr}
@@ -195,6 +200,7 @@ func TestPostFormRequestFormat(t *testing.T) {
 }
 
 func TestClientRedirects(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	var ts *httptest.Server
 	ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -206,14 +212,17 @@ func TestClientRedirects(t *testing.T) {
 			}
 		}
 		if n < 15 {
-			Redirect(w, r, fmt.Sprintf("/?n=%d", n+1), StatusFound)
+			Redirect(w, r, fmt.Sprintf("/?n=%d", n+1), StatusTemporaryRedirect)
 			return
 		}
 		fmt.Fprintf(w, "n=%d", n)
 	}))
 	defer ts.Close()
 
-	c := &Client{}
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+
+	c := &Client{Transport: tr}
 	_, err := c.Get(ts.URL)
 	if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
 		t.Errorf("with default client Get, expected error %q, got %q", e, g)
@@ -242,11 +251,14 @@ func TestClientRedirects(t *testing.T) {
 	var checkErr error
 	var lastVia []*Request
 	var lastReq *Request
-	c = &Client{CheckRedirect: func(req *Request, via []*Request) error {
-		lastReq = req
-		lastVia = via
-		return checkErr
-	}}
+	c = &Client{
+		Transport: tr,
+		CheckRedirect: func(req *Request, via []*Request) error {
+			lastReq = req
+			lastVia = via
+			return checkErr
+		},
+	}
 	res, err := c.Get(ts.URL)
 	if err != nil {
 		t.Fatalf("Get error: %v", err)
@@ -292,20 +304,27 @@ func TestClientRedirects(t *testing.T) {
 }
 
 func TestClientRedirectContext(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
-		Redirect(w, r, "/", StatusFound)
+		Redirect(w, r, "/", StatusTemporaryRedirect)
 	}))
 	defer ts.Close()
 
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+
 	ctx, cancel := context.WithCancel(context.Background())
-	c := &Client{CheckRedirect: func(req *Request, via []*Request) error {
-		cancel()
-		if len(via) > 2 {
-			return errors.New("too many redirects")
-		}
-		return nil
-	}}
+	c := &Client{
+		Transport: tr,
+		CheckRedirect: func(req *Request, via []*Request) error {
+			cancel()
+			if len(via) > 2 {
+				return errors.New("too many redirects")
+			}
+			return nil
+		},
+	}
 	req, _ := NewRequest("GET", ts.URL, nil)
 	req = req.WithContext(ctx)
 	_, err := c.Do(req)
@@ -313,12 +332,96 @@ func TestClientRedirectContext(t *testing.T) {
 	if !ok {
 		t.Fatalf("got error %T; want *url.Error", err)
 	}
-	if ue.Err != ExportErrRequestCanceled && ue.Err != ExportErrRequestCanceledConn {
-		t.Errorf("url.Error.Err = %v; want errRequestCanceled or errRequestCanceledConn", ue.Err)
+	if ue.Err != context.Canceled {
+		t.Errorf("url.Error.Err = %v; want %v", ue.Err, context.Canceled)
 	}
 }
 
+type redirectTest struct {
+	suffix       string
+	want         int // response code
+	redirectBody string
+}
+
 func TestPostRedirects(t *testing.T) {
+	postRedirectTests := []redirectTest{
+		{"/", 200, "first"},
+		{"/?code=301&next=302", 200, "c301"},
+		{"/?code=302&next=302", 200, "c302"},
+		{"/?code=303&next=301", 200, "c303wc301"}, // Issue 9348
+		{"/?code=304", 304, "c304"},
+		{"/?code=305", 305, "c305"},
+		{"/?code=307&next=303,308,302", 200, "c307"},
+		{"/?code=308&next=302,301", 200, "c308"},
+		{"/?code=404", 404, "c404"},
+	}
+
+	wantSegments := []string{
+		`POST / "first"`,
+		`POST /?code=301&next=302 "c301"`,
+		`GET /?code=302 "c301"`,
+		`GET / "c301"`,
+		`POST /?code=302&next=302 "c302"`,
+		`GET /?code=302 "c302"`,
+		`GET / "c302"`,
+		`POST /?code=303&next=301 "c303wc301"`,
+		`GET /?code=301 "c303wc301"`,
+		`GET / "c303wc301"`,
+		`POST /?code=304 "c304"`,
+		`POST /?code=305 "c305"`,
+		`POST /?code=307&next=303,308,302 "c307"`,
+		`POST /?code=303&next=308,302 "c307"`,
+		`GET /?code=308&next=302 "c307"`,
+		`GET /?code=302 "c307"`,
+		`GET / "c307"`,
+		`POST /?code=308&next=302,301 "c308"`,
+		`POST /?code=302&next=301 "c308"`,
+		`GET /?code=301 "c308"`,
+		`GET / "c308"`,
+		`POST /?code=404 "c404"`,
+	}
+	want := strings.Join(wantSegments, "\n")
+	testRedirectsByMethod(t, "POST", postRedirectTests, want)
+}
+
+func TestDeleteRedirects(t *testing.T) {
+	deleteRedirectTests := []redirectTest{
+		{"/", 200, "first"},
+		{"/?code=301&next=302,308", 200, "c301"},
+		{"/?code=302&next=302", 200, "c302"},
+		{"/?code=303", 200, "c303"},
+		{"/?code=307&next=301,308,303,302,304", 304, "c307"},
+		{"/?code=308&next=307", 200, "c308"},
+		{"/?code=404", 404, "c404"},
+	}
+
+	wantSegments := []string{
+		`DELETE / "first"`,
+		`DELETE /?code=301&next=302,308 "c301"`,
+		`GET /?code=302&next=308 "c301"`,
+		`GET /?code=308 "c301"`,
+		`GET / "c301"`,
+		`DELETE /?code=302&next=302 "c302"`,
+		`GET /?code=302 "c302"`,
+		`GET / "c302"`,
+		`DELETE /?code=303 "c303"`,
+		`GET / "c303"`,
+		`DELETE /?code=307&next=301,308,303,302,304 "c307"`,
+		`DELETE /?code=301&next=308,303,302,304 "c307"`,
+		`GET /?code=308&next=303,302,304 "c307"`,
+		`GET /?code=303&next=302,304 "c307"`,
+		`GET /?code=302&next=304 "c307"`,
+		`GET /?code=304 "c307"`,
+		`DELETE /?code=308&next=307 "c308"`,
+		`DELETE /?code=307 "c308"`,
+		`DELETE / "c308"`,
+		`DELETE /?code=404 "c404"`,
+	}
+	want := strings.Join(wantSegments, "\n")
+	testRedirectsByMethod(t, "DELETE", deleteRedirectTests, want)
+}
+
+func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, want string) {
 	defer afterTest(t)
 	var log struct {
 		sync.Mutex
@@ -327,29 +430,35 @@ func TestPostRedirects(t *testing.T) {
 	var ts *httptest.Server
 	ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		log.Lock()
-		fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI)
+		slurp, _ := ioutil.ReadAll(r.Body)
+		fmt.Fprintf(&log.Buffer, "%s %s %q\n", r.Method, r.RequestURI, slurp)
 		log.Unlock()
-		if v := r.URL.Query().Get("code"); v != "" {
+		urlQuery := r.URL.Query()
+		if v := urlQuery.Get("code"); v != "" {
+			location := ts.URL
+			if final := urlQuery.Get("next"); final != "" {
+				splits := strings.Split(final, ",")
+				first, rest := splits[0], splits[1:]
+				location = fmt.Sprintf("%s?code=%s", location, first)
+				if len(rest) > 0 {
+					location = fmt.Sprintf("%s&next=%s", location, strings.Join(rest, ","))
+				}
+			}
 			code, _ := strconv.Atoi(v)
 			if code/100 == 3 {
-				w.Header().Set("Location", ts.URL)
+				w.Header().Set("Location", location)
 			}
 			w.WriteHeader(code)
 		}
 	}))
 	defer ts.Close()
-	tests := []struct {
-		suffix string
-		want   int // response code
-	}{
-		{"/", 200},
-		{"/?code=301", 301},
-		{"/?code=302", 200},
-		{"/?code=303", 200},
-		{"/?code=404", 404},
-	}
-	for _, tt := range tests {
-		res, err := Post(ts.URL+tt.suffix, "text/plain", strings.NewReader("Some content"))
+
+	for _, tt := range table {
+		content := tt.redirectBody
+		req, _ := NewRequest(method, ts.URL+tt.suffix, strings.NewReader(content))
+		req.GetBody = func() (io.ReadCloser, error) { return ioutil.NopCloser(strings.NewReader(content)), nil }
+		res, err := DefaultClient.Do(req)
+
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -360,13 +469,17 @@ func TestPostRedirects(t *testing.T) {
 	log.Lock()
 	got := log.String()
 	log.Unlock()
-	want := "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 "
+
+	got = strings.TrimSpace(got)
+	want = strings.TrimSpace(want)
+
 	if got != want {
-		t.Errorf("Log differs.\n Got: %q\nWant: %q", got, want)
+		t.Errorf("Log differs.\n Got:\n%s\nWant:\n%s\n", got, want)
 	}
 }
 
 func TestClientRedirectUseResponse(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	const body = "Hello, world."
 	var ts *httptest.Server
@@ -381,12 +494,18 @@ func TestClientRedirectUseResponse(t *testing.T) {
 	}))
 	defer ts.Close()
 
-	c := &Client{CheckRedirect: func(req *Request, via []*Request) error {
-		if req.Response == nil {
-			t.Error("expected non-nil Request.Response")
-		}
-		return ErrUseLastResponse
-	}}
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+
+	c := &Client{
+		Transport: tr,
+		CheckRedirect: func(req *Request, via []*Request) error {
+			if req.Response == nil {
+				t.Error("expected non-nil Request.Response")
+			}
+			return ErrUseLastResponse
+		},
+	}
 	res, err := c.Get(ts.URL)
 	if err != nil {
 		t.Fatal(err)
@@ -404,6 +523,57 @@ func TestClientRedirectUseResponse(t *testing.T) {
 	}
 }
 
+// Issue 17773: don't follow a 308 (or 307) if the response doesn't
+// have a Location header.
+func TestClientRedirect308NoLocation(t *testing.T) {
+	setParallel(t)
+	defer afterTest(t)
+	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+		w.Header().Set("Foo", "Bar")
+		w.WriteHeader(308)
+	}))
+	defer ts.Close()
+	res, err := Get(ts.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	res.Body.Close()
+	if res.StatusCode != 308 {
+		t.Errorf("status = %d; want %d", res.StatusCode, 308)
+	}
+	if got := res.Header.Get("Foo"); got != "Bar" {
+		t.Errorf("Foo header = %q; want Bar", got)
+	}
+}
+
+// Don't follow a 307/308 if we can't resent the request body.
+func TestClientRedirect308NoGetBody(t *testing.T) {
+	setParallel(t)
+	defer afterTest(t)
+	const fakeURL = "https://localhost:1234/" // won't be hit
+	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+		w.Header().Set("Location", fakeURL)
+		w.WriteHeader(308)
+	}))
+	defer ts.Close()
+	req, err := NewRequest("POST", ts.URL, strings.NewReader("some body"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	req.GetBody = nil // so it can't rewind.
+	res, err := DefaultClient.Do(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+	res.Body.Close()
+	if res.StatusCode != 308 {
+		t.Errorf("status = %d; want %d", res.StatusCode, 308)
+	}
+	if got := res.Header.Get("Location"); got != fakeURL {
+		t.Errorf("Location header = %q; want %q", got, fakeURL)
+	}
+}
+
 var expectedCookies = []*Cookie{
 	{Name: "ChocolateChip", Value: "tasty"},
 	{Name: "First", Value: "Hit"},
@@ -476,12 +646,16 @@ func (j *TestJar) Cookies(u *url.URL) []*Cookie {
 }
 
 func TestRedirectCookiesJar(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	var ts *httptest.Server
 	ts = httptest.NewServer(echoCookiesRedirectHandler)
 	defer ts.Close()
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
 	c := &Client{
-		Jar: new(TestJar),
+		Transport: tr,
+		Jar:       new(TestJar),
 	}
 	u, _ := url.Parse(ts.URL)
 	c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]})
@@ -665,6 +839,7 @@ func TestClientWrites(t *testing.T) {
 }
 
 func TestClientInsecureTransport(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		w.Write([]byte("Hello"))
@@ -842,6 +1017,7 @@ func TestResponseSetsTLSConnectionState(t *testing.T) {
 func TestHTTPSClientDetectsHTTPServer(t *testing.T) {
 	defer afterTest(t)
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
+	ts.Config.ErrorLog = quietLog
 	defer ts.Close()
 
 	_, err := Get(strings.Replace(ts.URL, "http", "https", 1))
@@ -895,6 +1071,7 @@ func testClientHeadContentLength(t *testing.T, h2 bool) {
 }
 
 func TestEmptyPasswordAuth(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	gopher := "gopher"
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -915,7 +1092,9 @@ func TestEmptyPasswordAuth(t *testing.T) {
 		}
 	}))
 	defer ts.Close()
-	c := &Client{}
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+	c := &Client{Transport: tr}
 	req, err := NewRequest("GET", ts.URL, nil)
 	if err != nil {
 		t.Fatal(err)
@@ -1007,10 +1186,10 @@ func TestClientTimeout_h1(t *testing.T) { testClientTimeout(t, h1Mode) }
 func TestClientTimeout_h2(t *testing.T) { testClientTimeout(t, h2Mode) }
 
 func testClientTimeout(t *testing.T, h2 bool) {
-	if testing.Short() {
-		t.Skip("skipping in short mode")
-	}
+	setParallel(t)
 	defer afterTest(t)
+	testDone := make(chan struct{}) // closed in defer below
+
 	sawRoot := make(chan bool, 1)
 	sawSlow := make(chan bool, 1)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -1020,19 +1199,26 @@ func testClientTimeout(t *testing.T, h2 bool) {
 			return
 		}
 		if r.URL.Path == "/slow" {
+			sawSlow <- true
 			w.Write([]byte("Hello"))
 			w.(Flusher).Flush()
-			sawSlow <- true
-			time.Sleep(2 * time.Second)
+			<-testDone
 			return
 		}
 	}))
 	defer cst.close()
-	const timeout = 500 * time.Millisecond
+	defer close(testDone) // before cst.close, to unblock /slow handler
+
+	// 200ms should be long enough to get a normal request (the /
+	// handler), but not so long that it makes the test slow.
+	const timeout = 200 * time.Millisecond
 	cst.c.Timeout = timeout
 
 	res, err := cst.c.Get(cst.ts.URL)
 	if err != nil {
+		if strings.Contains(err.Error(), "Client.Timeout") {
+			t.Skipf("host too slow to get fast resource in %v", timeout)
+		}
 		t.Fatal(err)
 	}
 
@@ -1057,7 +1243,7 @@ func testClientTimeout(t *testing.T, h2 bool) {
 		res.Body.Close()
 	}()
 
-	const failTime = timeout * 2
+	const failTime = 5 * time.Second
 	select {
 	case err := <-errc:
 		if err == nil {
@@ -1082,11 +1268,9 @@ func TestClientTimeout_Headers_h2(t *testing.T) { testClientTimeout_Headers(t, h
 
 // Client.Timeout firing before getting to the body
 func testClientTimeout_Headers(t *testing.T, h2 bool) {
-	if testing.Short() {
-		t.Skip("skipping in short mode")
-	}
+	setParallel(t)
 	defer afterTest(t)
-	donec := make(chan bool)
+	donec := make(chan bool, 1)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		<-donec
 	}))
@@ -1100,9 +1284,10 @@ func testClientTimeout_Headers(t *testing.T, h2 bool) {
 	// doesn't know this, so synchronize explicitly.
 	defer func() { donec <- true }()
 
-	cst.c.Timeout = 500 * time.Millisecond
-	_, err := cst.c.Get(cst.ts.URL)
+	cst.c.Timeout = 5 * time.Millisecond
+	res, err := cst.c.Get(cst.ts.URL)
 	if err == nil {
+		res.Body.Close()
 		t.Fatal("got response from Get; expected error")
 	}
 	if _, ok := err.(*url.Error); !ok {
@@ -1120,9 +1305,40 @@ func testClientTimeout_Headers(t *testing.T, h2 bool) {
 	}
 }
 
+// Issue 16094: if Client.Timeout is set but not hit, a Timeout error shouldn't be
+// returned.
+func TestClientTimeoutCancel(t *testing.T) {
+	setParallel(t)
+	defer afterTest(t)
+
+	testDone := make(chan struct{})
+	ctx, cancel := context.WithCancel(context.Background())
+
+	cst := newClientServerTest(t, h1Mode, HandlerFunc(func(w ResponseWriter, r *Request) {
+		w.(Flusher).Flush()
+		<-testDone
+	}))
+	defer cst.close()
+	defer close(testDone)
+
+	cst.c.Timeout = 1 * time.Hour
+	req, _ := NewRequest("GET", cst.ts.URL, nil)
+	req.Cancel = ctx.Done()
+	res, err := cst.c.Do(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+	cancel()
+	_, err = io.Copy(ioutil.Discard, res.Body)
+	if err != ExportErrRequestCanceled {
+		t.Fatalf("error = %v; want errRequestCanceled", err)
+	}
+}
+
 func TestClientRedirectEatsBody_h1(t *testing.T) { testClientRedirectEatsBody(t, h1Mode) }
 func TestClientRedirectEatsBody_h2(t *testing.T) { testClientRedirectEatsBody(t, h2Mode) }
 func testClientRedirectEatsBody(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	saw := make(chan string, 2)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -1138,10 +1354,10 @@ func testClientRedirectEatsBody(t *testing.T, h2 bool) {
 		t.Fatal(err)
 	}
 	_, err = ioutil.ReadAll(res.Body)
+	res.Body.Close()
 	if err != nil {
 		t.Fatal(err)
 	}
-	res.Body.Close()
 
 	var first string
 	select {
@@ -1229,3 +1445,296 @@ func TestClientRedirectResponseWithoutRequest(t *testing.T) {
 	// Check that this doesn't crash:
 	c.Get("http://dummy.tld")
 }
+
+// Issue 4800: copy (some) headers when Client follows a redirect
+func TestClientCopyHeadersOnRedirect(t *testing.T) {
+	const (
+		ua   = "some-agent/1.2"
+		xfoo = "foo-val"
+	)
+	var ts2URL string
+	ts1 := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+		want := Header{
+			"User-Agent":      []string{ua},
+			"X-Foo":           []string{xfoo},
+			"Referer":         []string{ts2URL},
+			"Accept-Encoding": []string{"gzip"},
+		}
+		if !reflect.DeepEqual(r.Header, want) {
+			t.Errorf("Request.Header = %#v; want %#v", r.Header, want)
+		}
+		if t.Failed() {
+			w.Header().Set("Result", "got errors")
+		} else {
+			w.Header().Set("Result", "ok")
+		}
+	}))
+	defer ts1.Close()
+	ts2 := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+		Redirect(w, r, ts1.URL, StatusFound)
+	}))
+	defer ts2.Close()
+	ts2URL = ts2.URL
+
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+	c := &Client{
+		Transport: tr,
+		CheckRedirect: func(r *Request, via []*Request) error {
+			want := Header{
+				"User-Agent": []string{ua},
+				"X-Foo":      []string{xfoo},
+				"Referer":    []string{ts2URL},
+			}
+			if !reflect.DeepEqual(r.Header, want) {
+				t.Errorf("CheckRedirect Request.Header = %#v; want %#v", r.Header, want)
+			}
+			return nil
+		},
+	}
+
+	req, _ := NewRequest("GET", ts2.URL, nil)
+	req.Header.Add("User-Agent", ua)
+	req.Header.Add("X-Foo", xfoo)
+	req.Header.Add("Cookie", "foo=bar")
+	req.Header.Add("Authorization", "secretpassword")
+	res, err := c.Do(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer res.Body.Close()
+	if res.StatusCode != 200 {
+		t.Fatal(res.Status)
+	}
+	if got := res.Header.Get("Result"); got != "ok" {
+		t.Errorf("result = %q; want ok", got)
+	}
+}
+
+// Issue 17494: cookies should be altered when Client follows redirects.
+func TestClientAltersCookiesOnRedirect(t *testing.T) {
+	cookieMap := func(cs []*Cookie) map[string][]string {
+		m := make(map[string][]string)
+		for _, c := range cs {
+			m[c.Name] = append(m[c.Name], c.Value)
+		}
+		return m
+	}
+
+	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+		var want map[string][]string
+		got := cookieMap(r.Cookies())
+
+		c, _ := r.Cookie("Cycle")
+		switch c.Value {
+		case "0":
+			want = map[string][]string{
+				"Cookie1": {"OldValue1a", "OldValue1b"},
+				"Cookie2": {"OldValue2"},
+				"Cookie3": {"OldValue3a", "OldValue3b"},
+				"Cookie4": {"OldValue4"},
+				"Cycle":   {"0"},
+			}
+			SetCookie(w, &Cookie{Name: "Cycle", Value: "1", Path: "/"})
+			SetCookie(w, &Cookie{Name: "Cookie2", Path: "/", MaxAge: -1}) // Delete cookie from Header
+			Redirect(w, r, "/", StatusFound)
+		case "1":
+			want = map[string][]string{
+				"Cookie1": {"OldValue1a", "OldValue1b"},
+				"Cookie3": {"OldValue3a", "OldValue3b"},
+				"Cookie4": {"OldValue4"},
+				"Cycle":   {"1"},
+			}
+			SetCookie(w, &Cookie{Name: "Cycle", Value: "2", Path: "/"})
+			SetCookie(w, &Cookie{Name: "Cookie3", Value: "NewValue3", Path: "/"}) // Modify cookie in Header
+			SetCookie(w, &Cookie{Name: "Cookie4", Value: "NewValue4", Path: "/"}) // Modify cookie in Jar
+			Redirect(w, r, "/", StatusFound)
+		case "2":
+			want = map[string][]string{
+				"Cookie1": {"OldValue1a", "OldValue1b"},
+				"Cookie3": {"NewValue3"},
+				"Cookie4": {"NewValue4"},
+				"Cycle":   {"2"},
+			}
+			SetCookie(w, &Cookie{Name: "Cycle", Value: "3", Path: "/"})
+			SetCookie(w, &Cookie{Name: "Cookie5", Value: "NewValue5", Path: "/"}) // Insert cookie into Jar
+			Redirect(w, r, "/", StatusFound)
+		case "3":
+			want = map[string][]string{
+				"Cookie1": {"OldValue1a", "OldValue1b"},
+				"Cookie3": {"NewValue3"},
+				"Cookie4": {"NewValue4"},
+				"Cookie5": {"NewValue5"},
+				"Cycle":   {"3"},
+			}
+			// Don't redirect to ensure the loop ends.
+		default:
+			t.Errorf("unexpected redirect cycle")
+			return
+		}
+
+		if !reflect.DeepEqual(got, want) {
+			t.Errorf("redirect %s, Cookie = %v, want %v", c.Value, got, want)
+		}
+	}))
+	defer ts.Close()
+
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+	jar, _ := cookiejar.New(nil)
+	c := &Client{
+		Transport: tr,
+		Jar:       jar,
+	}
+
+	u, _ := url.Parse(ts.URL)
+	req, _ := NewRequest("GET", ts.URL, nil)
+	req.AddCookie(&Cookie{Name: "Cookie1", Value: "OldValue1a"})
+	req.AddCookie(&Cookie{Name: "Cookie1", Value: "OldValue1b"})
+	req.AddCookie(&Cookie{Name: "Cookie2", Value: "OldValue2"})
+	req.AddCookie(&Cookie{Name: "Cookie3", Value: "OldValue3a"})
+	req.AddCookie(&Cookie{Name: "Cookie3", Value: "OldValue3b"})
+	jar.SetCookies(u, []*Cookie{{Name: "Cookie4", Value: "OldValue4", Path: "/"}})
+	jar.SetCookies(u, []*Cookie{{Name: "Cycle", Value: "0", Path: "/"}})
+	res, err := c.Do(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer res.Body.Close()
+	if res.StatusCode != 200 {
+		t.Fatal(res.Status)
+	}
+}
+
+// Part of Issue 4800
+func TestShouldCopyHeaderOnRedirect(t *testing.T) {
+	tests := []struct {
+		header     string
+		initialURL string
+		destURL    string
+		want       bool
+	}{
+		{"User-Agent", "http://foo.com/", "http://bar.com/", true},
+		{"X-Foo", "http://foo.com/", "http://bar.com/", true},
+
+		// Sensitive headers:
+		{"cookie", "http://foo.com/", "http://bar.com/", false},
+		{"cookie2", "http://foo.com/", "http://bar.com/", false},
+		{"authorization", "http://foo.com/", "http://bar.com/", false},
+		{"www-authenticate", "http://foo.com/", "http://bar.com/", false},
+
+		// But subdomains should work:
+		{"www-authenticate", "http://foo.com/", "http://foo.com/", true},
+		{"www-authenticate", "http://foo.com/", "http://sub.foo.com/", true},
+		{"www-authenticate", "http://foo.com/", "http://notfoo.com/", false},
+		// TODO(bradfitz): make this test work, once issue 16142 is fixed:
+		// {"www-authenticate", "http://foo.com:80/", "http://foo.com/", true},
+	}
+	for i, tt := range tests {
+		u0, err := url.Parse(tt.initialURL)
+		if err != nil {
+			t.Errorf("%d. initial URL %q parse error: %v", i, tt.initialURL, err)
+			continue
+		}
+		u1, err := url.Parse(tt.destURL)
+		if err != nil {
+			t.Errorf("%d. dest URL %q parse error: %v", i, tt.destURL, err)
+			continue
+		}
+		got := Export_shouldCopyHeaderOnRedirect(tt.header, u0, u1)
+		if got != tt.want {
+			t.Errorf("%d. shouldCopyHeaderOnRedirect(%q, %q => %q) = %v; want %v",
+				i, tt.header, tt.initialURL, tt.destURL, got, tt.want)
+		}
+	}
+}
+
+func TestClientRedirectTypes(t *testing.T) {
+	setParallel(t)
+	defer afterTest(t)
+
+	tests := [...]struct {
+		method       string
+		serverStatus int
+		wantMethod   string // desired subsequent client method
+	}{
+		0: {method: "POST", serverStatus: 301, wantMethod: "GET"},
+		1: {method: "POST", serverStatus: 302, wantMethod: "GET"},
+		2: {method: "POST", serverStatus: 303, wantMethod: "GET"},
+		3: {method: "POST", serverStatus: 307, wantMethod: "POST"},
+		4: {method: "POST", serverStatus: 308, wantMethod: "POST"},
+
+		5: {method: "HEAD", serverStatus: 301, wantMethod: "GET"},
+		6: {method: "HEAD", serverStatus: 302, wantMethod: "GET"},
+		7: {method: "HEAD", serverStatus: 303, wantMethod: "GET"},
+		8: {method: "HEAD", serverStatus: 307, wantMethod: "HEAD"},
+		9: {method: "HEAD", serverStatus: 308, wantMethod: "HEAD"},
+
+		10: {method: "GET", serverStatus: 301, wantMethod: "GET"},
+		11: {method: "GET", serverStatus: 302, wantMethod: "GET"},
+		12: {method: "GET", serverStatus: 303, wantMethod: "GET"},
+		13: {method: "GET", serverStatus: 307, wantMethod: "GET"},
+		14: {method: "GET", serverStatus: 308, wantMethod: "GET"},
+
+		15: {method: "DELETE", serverStatus: 301, wantMethod: "GET"},
+		16: {method: "DELETE", serverStatus: 302, wantMethod: "GET"},
+		17: {method: "DELETE", serverStatus: 303, wantMethod: "GET"},
+		18: {method: "DELETE", serverStatus: 307, wantMethod: "DELETE"},
+		19: {method: "DELETE", serverStatus: 308, wantMethod: "DELETE"},
+
+		20: {method: "PUT", serverStatus: 301, wantMethod: "GET"},
+		21: {method: "PUT", serverStatus: 302, wantMethod: "GET"},
+		22: {method: "PUT", serverStatus: 303, wantMethod: "GET"},
+		23: {method: "PUT", serverStatus: 307, wantMethod: "PUT"},
+		24: {method: "PUT", serverStatus: 308, wantMethod: "PUT"},
+
+		25: {method: "MADEUPMETHOD", serverStatus: 301, wantMethod: "GET"},
+		26: {method: "MADEUPMETHOD", serverStatus: 302, wantMethod: "GET"},
+		27: {method: "MADEUPMETHOD", serverStatus: 303, wantMethod: "GET"},
+		28: {method: "MADEUPMETHOD", serverStatus: 307, wantMethod: "MADEUPMETHOD"},
+		29: {method: "MADEUPMETHOD", serverStatus: 308, wantMethod: "MADEUPMETHOD"},
+	}
+
+	handlerc := make(chan HandlerFunc, 1)
+
+	ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
+		h := <-handlerc
+		h(rw, req)
+	}))
+	defer ts.Close()
+
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+
+	for i, tt := range tests {
+		handlerc <- func(w ResponseWriter, r *Request) {
+			w.Header().Set("Location", ts.URL)
+			w.WriteHeader(tt.serverStatus)
+		}
+
+		req, err := NewRequest(tt.method, ts.URL, nil)
+		if err != nil {
+			t.Errorf("#%d: NewRequest: %v", i, err)
+			continue
+		}
+
+		c := &Client{Transport: tr}
+		c.CheckRedirect = func(req *Request, via []*Request) error {
+			if got, want := req.Method, tt.wantMethod; got != want {
+				return fmt.Errorf("#%d: got next method %q; want %q", i, got, want)
+			}
+			handlerc <- func(rw ResponseWriter, req *Request) {
+				// TODO: Check that the body is valid when we do 307 and 308 support
+			}
+			return nil
+		}
+
+		res, err := c.Do(req)
+		if err != nil {
+			t.Errorf("#%d: Response: %v", i, err)
+			continue
+		}
+
+		res.Body.Close()
+	}
+}
diff --git a/src/net/http/clientserver_test.go b/src/net/http/clientserver_test.go
index 8caba28..53556a1 100644
--- a/src/net/http/clientserver_test.go
+++ b/src/net/http/clientserver_test.go
@@ -44,6 +44,19 @@ func (t *clientServerTest) close() {
 	t.ts.Close()
 }
 
+func (t *clientServerTest) getURL(u string) string {
+	res, err := t.c.Get(u)
+	if err != nil {
+		t.t.Fatal(err)
+	}
+	defer res.Body.Close()
+	slurp, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		t.t.Fatal(err)
+	}
+	return string(slurp)
+}
+
 func (t *clientServerTest) scheme() string {
 	if t.h2 {
 		return "https"
@@ -56,6 +69,10 @@ const (
 	h2Mode = true
 )
 
+var optQuietLog = func(ts *httptest.Server) {
+	ts.Config.ErrorLog = quietLog
+}
+
 func newClientServerTest(t *testing.T, h2 bool, h Handler, opts ...interface{}) *clientServerTest {
 	cst := &clientServerTest{
 		t:  t,
@@ -64,21 +81,23 @@ func newClientServerTest(t *testing.T, h2 bool, h Handler, opts ...interface{})
 		tr: &Transport{},
 	}
 	cst.c = &Client{Transport: cst.tr}
+	cst.ts = httptest.NewUnstartedServer(h)
 
 	for _, opt := range opts {
 		switch opt := opt.(type) {
 		case func(*Transport):
 			opt(cst.tr)
+		case func(*httptest.Server):
+			opt(cst.ts)
 		default:
 			t.Fatalf("unhandled option type %T", opt)
 		}
 	}
 
 	if !h2 {
-		cst.ts = httptest.NewServer(h)
+		cst.ts.Start()
 		return cst
 	}
-	cst.ts = httptest.NewUnstartedServer(h)
 	ExportHttp2ConfigureServer(cst.ts.Config, nil)
 	cst.ts.TLS = cst.ts.Config.TLSConfig
 	cst.ts.StartTLS()
@@ -170,6 +189,7 @@ func (tt h12Compare) reqFunc() reqFunc {
 }
 
 func (tt h12Compare) run(t *testing.T) {
+	setParallel(t)
 	cst1 := newClientServerTest(t, false, HandlerFunc(tt.Handler), tt.Opts...)
 	defer cst1.close()
 	cst2 := newClientServerTest(t, true, HandlerFunc(tt.Handler), tt.Opts...)
@@ -938,6 +958,7 @@ func testStarRequest(t *testing.T, method string, h2 bool) {
 
 // Issue 13957
 func TestTransportDiscardsUnneededConns(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2Mode, HandlerFunc(func(w ResponseWriter, r *Request) {
 		fmt.Fprintf(w, "Hello, %v", r.RemoteAddr)
@@ -1022,6 +1043,7 @@ func TestTransportGCRequest_Body_h2(t *testing.T)   { testTransportGCRequest(t,
 func TestTransportGCRequest_NoBody_h1(t *testing.T) { testTransportGCRequest(t, h1Mode, false) }
 func TestTransportGCRequest_NoBody_h2(t *testing.T) { testTransportGCRequest(t, h2Mode, false) }
 func testTransportGCRequest(t *testing.T, h2, body bool) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		ioutil.ReadAll(r.Body)
@@ -1068,10 +1090,11 @@ func TestTransportRejectsInvalidHeaders_h2(t *testing.T) {
 	testTransportRejectsInvalidHeaders(t, h2Mode)
 }
 func testTransportRejectsInvalidHeaders(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		fmt.Fprintf(w, "Handler saw headers: %q", r.Header)
-	}))
+	}), optQuietLog)
 	defer cst.close()
 	cst.tr.DisableKeepAlives = true
 
@@ -1139,24 +1162,44 @@ func testBogusStatusWorks(t *testing.T, h2 bool) {
 	}
 }
 
-func TestInterruptWithPanic_h1(t *testing.T) { testInterruptWithPanic(t, h1Mode) }
-func TestInterruptWithPanic_h2(t *testing.T) { testInterruptWithPanic(t, h2Mode) }
-func testInterruptWithPanic(t *testing.T, h2 bool) {
-	log.SetOutput(ioutil.Discard) // is noisy otherwise
-	defer log.SetOutput(os.Stderr)
-
+func TestInterruptWithPanic_h1(t *testing.T)     { testInterruptWithPanic(t, h1Mode, "boom") }
+func TestInterruptWithPanic_h2(t *testing.T)     { testInterruptWithPanic(t, h2Mode, "boom") }
+func TestInterruptWithPanic_nil_h1(t *testing.T) { testInterruptWithPanic(t, h1Mode, nil) }
+func TestInterruptWithPanic_nil_h2(t *testing.T) { testInterruptWithPanic(t, h2Mode, nil) }
+func TestInterruptWithPanic_ErrAbortHandler_h1(t *testing.T) {
+	testInterruptWithPanic(t, h1Mode, ErrAbortHandler)
+}
+func TestInterruptWithPanic_ErrAbortHandler_h2(t *testing.T) {
+	testInterruptWithPanic(t, h2Mode, ErrAbortHandler)
+}
+func testInterruptWithPanic(t *testing.T, h2 bool, panicValue interface{}) {
+	setParallel(t)
 	const msg = "hello"
 	defer afterTest(t)
+
+	testDone := make(chan struct{})
+	defer close(testDone)
+
+	var errorLog lockedBytesBuffer
+	gotHeaders := make(chan bool, 1)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		io.WriteString(w, msg)
 		w.(Flusher).Flush()
-		panic("no more")
-	}))
+
+		select {
+		case <-gotHeaders:
+		case <-testDone:
+		}
+		panic(panicValue)
+	}), func(ts *httptest.Server) {
+		ts.Config.ErrorLog = log.New(&errorLog, "", 0)
+	})
 	defer cst.close()
 	res, err := cst.c.Get(cst.ts.URL)
 	if err != nil {
 		t.Fatal(err)
 	}
+	gotHeaders <- true
 	defer res.Body.Close()
 	slurp, err := ioutil.ReadAll(res.Body)
 	if string(slurp) != msg {
@@ -1165,6 +1208,42 @@ func testInterruptWithPanic(t *testing.T, h2 bool) {
 	if err == nil {
 		t.Errorf("client read all successfully; want some error")
 	}
+	logOutput := func() string {
+		errorLog.Lock()
+		defer errorLog.Unlock()
+		return errorLog.String()
+	}
+	wantStackLogged := panicValue != nil && panicValue != ErrAbortHandler
+
+	if err := waitErrCondition(5*time.Second, 10*time.Millisecond, func() error {
+		gotLog := logOutput()
+		if !wantStackLogged {
+			if gotLog == "" {
+				return nil
+			}
+			return fmt.Errorf("want no log output; got: %s", gotLog)
+		}
+		if gotLog == "" {
+			return fmt.Errorf("wanted a stack trace logged; got nothing")
+		}
+		if !strings.Contains(gotLog, "created by ") && strings.Count(gotLog, "\n") < 6 {
+			return fmt.Errorf("output doesn't look like a panic stack trace. Got: %s", gotLog)
+		}
+		return nil
+	}); err != nil {
+		t.Fatal(err)
+	}
+}
+
+type lockedBytesBuffer struct {
+	sync.Mutex
+	bytes.Buffer
+}
+
+func (b *lockedBytesBuffer) Write(p []byte) (int, error) {
+	b.Lock()
+	defer b.Unlock()
+	return b.Buffer.Write(p)
 }
 
 // Issue 15366
@@ -1200,6 +1279,7 @@ func TestH12_AutoGzipWithDumpResponse(t *testing.T) {
 func TestCloseIdleConnections_h1(t *testing.T) { testCloseIdleConnections(t, h1Mode) }
 func TestCloseIdleConnections_h2(t *testing.T) { testCloseIdleConnections(t, h2Mode) }
 func testCloseIdleConnections(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		w.Header().Set("X-Addr", r.RemoteAddr)
@@ -1234,3 +1314,70 @@ func (x noteCloseConn) Close() error {
 	x.closeFunc()
 	return x.Conn.Close()
 }
+
+type testErrorReader struct{ t *testing.T }
+
+func (r testErrorReader) Read(p []byte) (n int, err error) {
+	r.t.Error("unexpected Read call")
+	return 0, io.EOF
+}
+
+func TestNoSniffExpectRequestBody_h1(t *testing.T) { testNoSniffExpectRequestBody(t, h1Mode) }
+func TestNoSniffExpectRequestBody_h2(t *testing.T) { testNoSniffExpectRequestBody(t, h2Mode) }
+
+func testNoSniffExpectRequestBody(t *testing.T, h2 bool) {
+	defer afterTest(t)
+	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
+		w.WriteHeader(StatusUnauthorized)
+	}))
+	defer cst.close()
+
+	// Set ExpectContinueTimeout non-zero so RoundTrip won't try to write it.
+	cst.tr.ExpectContinueTimeout = 10 * time.Second
+
+	req, err := NewRequest("POST", cst.ts.URL, testErrorReader{t})
+	if err != nil {
+		t.Fatal(err)
+	}
+	req.ContentLength = 0 // so transport is tempted to sniff it
+	req.Header.Set("Expect", "100-continue")
+	res, err := cst.tr.RoundTrip(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer res.Body.Close()
+	if res.StatusCode != StatusUnauthorized {
+		t.Errorf("status code = %v; want %v", res.StatusCode, StatusUnauthorized)
+	}
+}
+
+func TestServerUndeclaredTrailers_h1(t *testing.T) { testServerUndeclaredTrailers(t, h1Mode) }
+func TestServerUndeclaredTrailers_h2(t *testing.T) { testServerUndeclaredTrailers(t, h2Mode) }
+func testServerUndeclaredTrailers(t *testing.T, h2 bool) {
+	defer afterTest(t)
+	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
+		w.Header().Set("Foo", "Bar")
+		w.Header().Set("Trailer:Foo", "Baz")
+		w.(Flusher).Flush()
+		w.Header().Add("Trailer:Foo", "Baz2")
+		w.Header().Set("Trailer:Bar", "Quux")
+	}))
+	defer cst.close()
+	res, err := cst.c.Get(cst.ts.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if _, err := io.Copy(ioutil.Discard, res.Body); err != nil {
+		t.Fatal(err)
+	}
+	res.Body.Close()
+	delete(res.Header, "Date")
+	delete(res.Header, "Content-Type")
+
+	if want := (Header{"Foo": {"Bar"}}); !reflect.DeepEqual(res.Header, want) {
+		t.Errorf("Header = %#v; want %#v", res.Header, want)
+	}
+	if want := (Header{"Foo": {"Baz", "Baz2"}, "Bar": {"Quux"}}); !reflect.DeepEqual(res.Trailer, want) {
+		t.Errorf("Trailer = %#v; want %#v", res.Trailer, want)
+	}
+}
diff --git a/src/net/http/cookie.go b/src/net/http/cookie.go
index 1ea0e93..5a67476 100644
--- a/src/net/http/cookie.go
+++ b/src/net/http/cookie.go
@@ -6,7 +6,6 @@ package http
 
 import (
 	"bytes"
-	"fmt"
 	"log"
 	"net"
 	"strconv"
@@ -40,7 +39,11 @@ type Cookie struct {
 // readSetCookies parses all "Set-Cookie" values from
 // the header h and returns the successfully parsed Cookies.
 func readSetCookies(h Header) []*Cookie {
-	cookies := []*Cookie{}
+	cookieCount := len(h["Set-Cookie"])
+	if cookieCount == 0 {
+		return []*Cookie{}
+	}
+	cookies := make([]*Cookie, 0, cookieCount)
 	for _, line := range h["Set-Cookie"] {
 		parts := strings.Split(strings.TrimSpace(line), ";")
 		if len(parts) == 1 && parts[0] == "" {
@@ -55,8 +58,8 @@ func readSetCookies(h Header) []*Cookie {
 		if !isCookieNameValid(name) {
 			continue
 		}
-		value, success := parseCookieValue(value, true)
-		if !success {
+		value, ok := parseCookieValue(value, true)
+		if !ok {
 			continue
 		}
 		c := &Cookie{
@@ -75,8 +78,8 @@ func readSetCookies(h Header) []*Cookie {
 				attr, val = attr[:j], attr[j+1:]
 			}
 			lowerAttr := strings.ToLower(attr)
-			val, success = parseCookieValue(val, false)
-			if !success {
+			val, ok = parseCookieValue(val, false)
+			if !ok {
 				c.Unparsed = append(c.Unparsed, parts[i])
 				continue
 			}
@@ -96,10 +99,9 @@ func readSetCookies(h Header) []*Cookie {
 					break
 				}
 				if secs <= 0 {
-					c.MaxAge = -1
-				} else {
-					c.MaxAge = secs
+					secs = -1
 				}
+				c.MaxAge = secs
 				continue
 			case "expires":
 				c.RawExpires = val
@@ -142,9 +144,13 @@ func (c *Cookie) String() string {
 		return ""
 	}
 	var b bytes.Buffer
-	fmt.Fprintf(&b, "%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))
+	b.WriteString(sanitizeCookieName(c.Name))
+	b.WriteRune('=')
+	b.WriteString(sanitizeCookieValue(c.Value))
+
 	if len(c.Path) > 0 {
-		fmt.Fprintf(&b, "; Path=%s", sanitizeCookiePath(c.Path))
+		b.WriteString("; Path=")
+		b.WriteString(sanitizeCookiePath(c.Path))
 	}
 	if len(c.Domain) > 0 {
 		if validCookieDomain(c.Domain) {
@@ -156,25 +162,31 @@ func (c *Cookie) String() string {
 			if d[0] == '.' {
 				d = d[1:]
 			}
-			fmt.Fprintf(&b, "; Domain=%s", d)
+			b.WriteString("; Domain=")
+			b.WriteString(d)
 		} else {
-			log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute",
-				c.Domain)
+			log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute", c.Domain)
 		}
 	}
-	if c.Expires.Unix() > 0 {
-		fmt.Fprintf(&b, "; Expires=%s", c.Expires.UTC().Format(TimeFormat))
+	if validCookieExpires(c.Expires) {
+		b.WriteString("; Expires=")
+		b2 := b.Bytes()
+		b.Reset()
+		b.Write(c.Expires.UTC().AppendFormat(b2, TimeFormat))
 	}
 	if c.MaxAge > 0 {
-		fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge)
+		b.WriteString("; Max-Age=")
+		b2 := b.Bytes()
+		b.Reset()
+		b.Write(strconv.AppendInt(b2, int64(c.MaxAge), 10))
 	} else if c.MaxAge < 0 {
-		fmt.Fprintf(&b, "; Max-Age=0")
+		b.WriteString("; Max-Age=0")
 	}
 	if c.HttpOnly {
-		fmt.Fprintf(&b, "; HttpOnly")
+		b.WriteString("; HttpOnly")
 	}
 	if c.Secure {
-		fmt.Fprintf(&b, "; Secure")
+		b.WriteString("; Secure")
 	}
 	return b.String()
 }
@@ -184,12 +196,12 @@ func (c *Cookie) String() string {
 //
 // if filter isn't empty, only cookies of that name are returned
 func readCookies(h Header, filter string) []*Cookie {
-	cookies := []*Cookie{}
 	lines, ok := h["Cookie"]
 	if !ok {
-		return cookies
+		return []*Cookie{}
 	}
 
+	cookies := []*Cookie{}
 	for _, line := range lines {
 		parts := strings.Split(strings.TrimSpace(line), ";")
 		if len(parts) == 1 && parts[0] == "" {
@@ -212,8 +224,8 @@ func readCookies(h Header, filter string) []*Cookie {
 			if filter != "" && filter != name {
 				continue
 			}
-			val, success := parseCookieValue(val, true)
-			if !success {
+			val, ok := parseCookieValue(val, true)
+			if !ok {
 				continue
 			}
 			cookies = append(cookies, &Cookie{Name: name, Value: val})
@@ -234,6 +246,12 @@ func validCookieDomain(v string) bool {
 	return false
 }
 
+// validCookieExpires returns whether v is a valid cookie expires-value.
+func validCookieExpires(t time.Time) bool {
+	// IETF RFC 6265 Section 5.1.1.5, the year must not be less than 1601
+	return t.Year() >= 1601
+}
+
 // isCookieDomainName returns whether s is a valid domain name or a valid
 // domain name with a leading dot '.'.  It is almost a direct copy of
 // package net's isDomainName.
diff --git a/src/net/http/cookie_test.go b/src/net/http/cookie_test.go
index 95e6147..b3e54f8 100644
--- a/src/net/http/cookie_test.go
+++ b/src/net/http/cookie_test.go
@@ -56,6 +56,15 @@ var writeSetCookiesTests = []struct {
 		&Cookie{Name: "cookie-9", Value: "expiring", Expires: time.Unix(1257894000, 0)},
 		"cookie-9=expiring; Expires=Tue, 10 Nov 2009 23:00:00 GMT",
 	},
+	// According to IETF 6265 Section 5.1.1.5, the year cannot be less than 1601
+	{
+		&Cookie{Name: "cookie-10", Value: "expiring-1601", Expires: time.Date(1601, 1, 1, 1, 1, 1, 1, time.UTC)},
+		"cookie-10=expiring-1601; Expires=Mon, 01 Jan 1601 01:01:01 GMT",
+	},
+	{
+		&Cookie{Name: "cookie-11", Value: "invalid-expiry", Expires: time.Date(1600, 1, 1, 1, 1, 1, 1, time.UTC)},
+		"cookie-11=invalid-expiry",
+	},
 	// The "special" cookies have values containing commas or spaces which
 	// are disallowed by RFC 6265 but are common in the wild.
 	{
@@ -426,3 +435,92 @@ func TestCookieSanitizePath(t *testing.T) {
 		t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
 	}
 }
+
+func BenchmarkCookieString(b *testing.B) {
+	const wantCookieString = `cookie-9=i3e01nf61b6t23bvfmplnanol3; Path=/restricted/; Domain=example.com; Expires=Tue, 10 Nov 2009 23:00:00 GMT; Max-Age=3600`
+	c := &Cookie{
+		Name:    "cookie-9",
+		Value:   "i3e01nf61b6t23bvfmplnanol3",
+		Expires: time.Unix(1257894000, 0),
+		Path:    "/restricted/",
+		Domain:  ".example.com",
+		MaxAge:  3600,
+	}
+	var benchmarkCookieString string
+	b.ReportAllocs()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		benchmarkCookieString = c.String()
+	}
+	if have, want := benchmarkCookieString, wantCookieString; have != want {
+		b.Fatalf("Have: %v Want: %v", have, want)
+	}
+}
+
+func BenchmarkReadSetCookies(b *testing.B) {
+	header := Header{
+		"Set-Cookie": {
+			"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
+			".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
+		},
+	}
+	wantCookies := []*Cookie{
+		{
+			Name:       "NID",
+			Value:      "99=YsDT5i3E-CXax-",
+			Path:       "/",
+			Domain:     ".google.ch",
+			HttpOnly:   true,
+			Expires:    time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
+			RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
+			Raw:        "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
+		},
+		{
+			Name:       ".ASPXAUTH",
+			Value:      "7E3AA",
+			Path:       "/",
+			Expires:    time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
+			RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
+			HttpOnly:   true,
+			Raw:        ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
+		},
+	}
+	var c []*Cookie
+	b.ReportAllocs()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		c = readSetCookies(header)
+	}
+	if !reflect.DeepEqual(c, wantCookies) {
+		b.Fatalf("readSetCookies:\nhave: %s\nwant: %s\n", toJSON(c), toJSON(wantCookies))
+	}
+}
+
+func BenchmarkReadCookies(b *testing.B) {
+	header := Header{
+		"Cookie": {
+			`de=; client_region=0; rpld1=0:hispeed.ch|20:che|21:zh|22:zurich|23:47.36|24:8.53|; rpld0=1:08|; backplane-channel=newspaper.com:1471; devicetype=0; osfam=0; rplmct=2; s_pers=%20s_vmonthnum%3D1472680800496%2526vn%253D1%7C1472680800496%3B%20s_nr%3D1471686767664-New%7C1474278767664%3B%20s_lv%3D1471686767669%7C1566294767669%3B%20s_lv_s%3DFirst%2520Visit%7C1471688567669%3B%20s_monthinvisit%3Dtrue%7C1471688567677%3B%20gvp_p5%3Dsports%253Ablog%253Aearly-lead%2520-%2520184693%2520-%252020160 [...]
+		},
+	}
+	wantCookies := []*Cookie{
+		{Name: "de", Value: ""},
+		{Name: "client_region", Value: "0"},
+		{Name: "rpld1", Value: "0:hispeed.ch|20:che|21:zh|22:zurich|23:47.36|24:8.53|"},
+		{Name: "rpld0", Value: "1:08|"},
+		{Name: "backplane-channel", Value: "newspaper.com:1471"},
+		{Name: "devicetype", Value: "0"},
+		{Name: "osfam", Value: "0"},
+		{Name: "rplmct", Value: "2"},
+		{Name: "s_pers", Value: "%20s_vmonthnum%3D1472680800496%2526vn%253D1%7C1472680800496%3B%20s_nr%3D1471686767664-New%7C1474278767664%3B%20s_lv%3D1471686767669%7C1566294767669%3B%20s_lv_s%3DFirst%2520Visit%7C1471688567669%3B%20s_monthinvisit%3Dtrue%7C1471688567677%3B%20gvp_p5%3Dsports%253Ablog%253Aearly-lead%2520-%2520184693%2520-%252020160820%2520-%2520u-s%7C1471688567681%3B%20gvp_p51%3Dwp%2520-%2520sports%7C1471688567684%3B"},
+		{Name: "s_sess", Value: "%20s_wp_ep%3Dhomepage%3B%20s._ref%3Dhttps%253A%252F%252Fwww.google.ch%252F%3B%20s_cc%3Dtrue%3B%20s_ppvl%3Dsports%25253Ablog%25253Aearly-lead%252520-%252520184693%252520-%25252020160820%252520-%252520u-lawyer%252C12%252C12%252C502%252C1231%252C502%252C1680%252C1050%252C2%252CP%3B%20s_ppv%3Dsports%25253Ablog%25253Aearly-lead%252520-%252520184693%252520-%25252020160820%252520-%252520u-s-lawyer%252C12%252C12%252C502%252C1231%252C502%252C1680%252C1050%252C2%252CP%3B [...]
+	}
+	var c []*Cookie
+	b.ReportAllocs()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		c = readCookies(header, "")
+	}
+	if !reflect.DeepEqual(c, wantCookies) {
+		b.Fatalf("readCookies:\nhave: %s\nwant: %s\n", toJSON(c), toJSON(wantCookies))
+	}
+}
diff --git a/src/net/http/cookiejar/dummy_publicsuffix_test.go b/src/net/http/cookiejar/dummy_publicsuffix_test.go
new file mode 100644
index 0000000..9b31173
--- /dev/null
+++ b/src/net/http/cookiejar/dummy_publicsuffix_test.go
@@ -0,0 +1,21 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cookiejar_test
+
+import "net/http/cookiejar"
+
+type dummypsl struct {
+	List cookiejar.PublicSuffixList
+}
+
+func (dummypsl) PublicSuffix(domain string) string {
+	return domain
+}
+
+func (dummypsl) String() string {
+	return "dummy"
+}
+
+var publicsuffix = dummypsl{}
diff --git a/src/net/http/cookiejar/example_test.go b/src/net/http/cookiejar/example_test.go
new file mode 100644
index 0000000..91728ca
--- /dev/null
+++ b/src/net/http/cookiejar/example_test.go
@@ -0,0 +1,65 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cookiejar_test
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+	"net/http/cookiejar"
+	"net/http/httptest"
+	"net/url"
+)
+
+func ExampleNew() {
+	// Start a server to give us cookies.
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if cookie, err := r.Cookie("Flavor"); err != nil {
+			http.SetCookie(w, &http.Cookie{Name: "Flavor", Value: "Chocolate Chip"})
+		} else {
+			cookie.Value = "Oatmeal Raisin"
+			http.SetCookie(w, cookie)
+		}
+	}))
+	defer ts.Close()
+
+	u, err := url.Parse(ts.URL)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// All users of cookiejar should import "golang.org/x/net/publicsuffix"
+	jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	client := &http.Client{
+		Jar: jar,
+	}
+
+	if _, err = client.Get(u.String()); err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Println("After 1st request:")
+	for _, cookie := range jar.Cookies(u) {
+		fmt.Printf("  %s: %s\n", cookie.Name, cookie.Value)
+	}
+
+	if _, err = client.Get(u.String()); err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Println("After 2nd request:")
+	for _, cookie := range jar.Cookies(u) {
+		fmt.Printf("  %s: %s\n", cookie.Name, cookie.Value)
+	}
+	// Output:
+	// After 1st request:
+	//   Flavor: Chocolate Chip
+	// After 2nd request:
+	//   Flavor: Oatmeal Raisin
+}
diff --git a/src/net/http/cookiejar/jar.go b/src/net/http/cookiejar/jar.go
index 0e0fac9..f89abbc 100644
--- a/src/net/http/cookiejar/jar.go
+++ b/src/net/http/cookiejar/jar.go
@@ -107,7 +107,7 @@ type entry struct {
 	seqNum uint64
 }
 
-// Id returns the domain;path;name triple of e as an id.
+// id returns the domain;path;name triple of e as an id.
 func (e *entry) id() string {
 	return fmt.Sprintf("%s;%s;%s", e.Domain, e.Path, e.Name)
 }
@@ -147,24 +147,6 @@ func hasDotSuffix(s, suffix string) bool {
 	return len(s) > len(suffix) && s[len(s)-len(suffix)-1] == '.' && s[len(s)-len(suffix):] == suffix
 }
 
-// byPathLength is a []entry sort.Interface that sorts according to RFC 6265
-// section 5.4 point 2: by longest path and then by earliest creation time.
-type byPathLength []entry
-
-func (s byPathLength) Len() int { return len(s) }
-
-func (s byPathLength) Less(i, j int) bool {
-	if len(s[i].Path) != len(s[j].Path) {
-		return len(s[i].Path) > len(s[j].Path)
-	}
-	if !s[i].Creation.Equal(s[j].Creation) {
-		return s[i].Creation.Before(s[j].Creation)
-	}
-	return s[i].seqNum < s[j].seqNum
-}
-
-func (s byPathLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
 // Cookies implements the Cookies method of the http.CookieJar interface.
 //
 // It returns an empty slice if the URL's scheme is not HTTP or HTTPS.
@@ -221,7 +203,18 @@ func (j *Jar) cookies(u *url.URL, now time.Time) (cookies []*http.Cookie) {
 		}
 	}
 
-	sort.Sort(byPathLength(selected))
+	// sort according to RFC 6265 section 5.4 point 2: by longest
+	// path and then by earliest creation time.
+	sort.Slice(selected, func(i, j int) bool {
+		s := selected
+		if len(s[i].Path) != len(s[j].Path) {
+			return len(s[i].Path) > len(s[j].Path)
+		}
+		if !s[i].Creation.Equal(s[j].Creation) {
+			return s[i].Creation.Before(s[j].Creation)
+		}
+		return s[i].seqNum < s[j].seqNum
+	})
 	for _, e := range selected {
 		cookies = append(cookies, &http.Cookie{Name: e.Name, Value: e.Value})
 	}
diff --git a/src/net/http/doc.go b/src/net/http/doc.go
index 4ec8272..7855fea 100644
--- a/src/net/http/doc.go
+++ b/src/net/http/doc.go
@@ -44,7 +44,8 @@ For control over proxies, TLS configuration, keep-alives,
 compression, and other settings, create a Transport:
 
 	tr := &http.Transport{
-		TLSClientConfig:    &tls.Config{RootCAs: pool},
+		MaxIdleConns:       10,
+		IdleConnTimeout:    30 * time.Second,
 		DisableCompression: true,
 	}
 	client := &http.Client{Transport: tr}
@@ -77,19 +78,30 @@ custom Server:
 	}
 	log.Fatal(s.ListenAndServe())
 
-The http package has transparent support for the HTTP/2 protocol when
-using HTTPS. Programs that must disable HTTP/2 can do so by setting
-Transport.TLSNextProto (for clients) or Server.TLSNextProto (for
-servers) to a non-nil, empty map. Alternatively, the following GODEBUG
-environment variables are currently supported:
+Starting with Go 1.6, the http package has transparent support for the
+HTTP/2 protocol when using HTTPS. Programs that must disable HTTP/2
+can do so by setting Transport.TLSNextProto (for clients) or
+Server.TLSNextProto (for servers) to a non-nil, empty
+map. Alternatively, the following GODEBUG environment variables are
+currently supported:
 
 	GODEBUG=http2client=0  # disable HTTP/2 client support
 	GODEBUG=http2server=0  # disable HTTP/2 server support
 	GODEBUG=http2debug=1   # enable verbose HTTP/2 debug logs
 	GODEBUG=http2debug=2   # ... even more verbose, with frame dumps
 
-The GODEBUG variables are not covered by Go's API compatibility promise.
-HTTP/2 support was added in Go 1.6. Please report any issues instead of
-disabling HTTP/2 support: https://golang.org/s/http2bug
+The GODEBUG variables are not covered by Go's API compatibility
+promise. Please report any issues before disabling HTTP/2
+support: https://golang.org/s/http2bug
+
+The http package's Transport and Server both automatically enable
+HTTP/2 support for simple configurations. To enable HTTP/2 for more
+complex configurations, to use lower-level HTTP/2 features, or to use
+a newer version of Go's http2 package, import "golang.org/x/net/http2"
+directly and use its ConfigureTransport and/or ConfigureServer
+functions. Manually configuring HTTP/2 via the golang.org/x/net/http2
+package takes precedence over the net/http package's built-in HTTP/2
+support.
+
 */
 package http
diff --git a/src/net/http/export_test.go b/src/net/http/export_test.go
index 9c5ba08..b61f58b 100644
--- a/src/net/http/export_test.go
+++ b/src/net/http/export_test.go
@@ -24,6 +24,7 @@ var (
 	ExportErrRequestCanceled     = errRequestCanceled
 	ExportErrRequestCanceledConn = errRequestCanceledConn
 	ExportServeFile              = serveFile
+	ExportScanETag               = scanETag
 	ExportHttp2ConfigureServer   = http2ConfigureServer
 )
 
@@ -87,6 +88,12 @@ func (t *Transport) IdleConnKeysForTesting() (keys []string) {
 	return
 }
 
+func (t *Transport) IdleConnKeyCountForTesting() int {
+	t.idleMu.Lock()
+	defer t.idleMu.Unlock()
+	return len(t.idleConn)
+}
+
 func (t *Transport) IdleConnStrsForTesting() []string {
 	var ret []string
 	t.idleMu.Lock()
@@ -100,6 +107,24 @@ func (t *Transport) IdleConnStrsForTesting() []string {
 	return ret
 }
 
+func (t *Transport) IdleConnStrsForTesting_h2() []string {
+	var ret []string
+	noDialPool := t.h2transport.ConnPool.(http2noDialClientConnPool)
+	pool := noDialPool.http2clientConnPool
+
+	pool.mu.Lock()
+	defer pool.mu.Unlock()
+
+	for k, cc := range pool.conns {
+		for range cc {
+			ret = append(ret, k)
+		}
+	}
+
+	sort.Strings(ret)
+	return ret
+}
+
 func (t *Transport) IdleConnCountForTesting(cacheKey string) int {
 	t.idleMu.Lock()
 	defer t.idleMu.Unlock()
@@ -160,3 +185,17 @@ func ExportHttp2ConfigureTransport(t *Transport) error {
 	t.h2transport = t2
 	return nil
 }
+
+var Export_shouldCopyHeaderOnRedirect = shouldCopyHeaderOnRedirect
+
+func (s *Server) ExportAllConnsIdle() bool {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	for c := range s.activeConn {
+		st, ok := c.curState.Load().(ConnState)
+		if !ok || st != StateIdle {
+			return false
+		}
+	}
+	return true
+}
diff --git a/src/net/http/fcgi/fcgi.go b/src/net/http/fcgi/fcgi.go
index 3374841..5057d70 100644
--- a/src/net/http/fcgi/fcgi.go
+++ b/src/net/http/fcgi/fcgi.go
@@ -3,8 +3,12 @@
 // license that can be found in the LICENSE file.
 
 // Package fcgi implements the FastCGI protocol.
+//
+// The protocol is not an official standard and the original
+// documentation is no longer online. See the Internet Archive's
+// mirror at: https://web.archive.org/web/20150420080736/http://www.fastcgi.com/drupal/node/6?q=node/22
+//
 // Currently only the responder role is supported.
-// The protocol is defined at http://www.fastcgi.com/drupal/node/6?q=node/22
 package fcgi
 
 // This file defines the raw protocol and some utilities used by the child and
diff --git a/src/net/http/fs.go b/src/net/http/fs.go
index c7a58a6..bf63bb5 100644
--- a/src/net/http/fs.go
+++ b/src/net/http/fs.go
@@ -77,7 +77,7 @@ func dirList(w ResponseWriter, f File) {
 		Error(w, "Error reading directory", StatusInternalServerError)
 		return
 	}
-	sort.Sort(byName(dirs))
+	sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })
 
 	w.Header().Set("Content-Type", "text/html; charset=utf-8")
 	fmt.Fprintf(w, "<pre>\n")
@@ -98,7 +98,8 @@ func dirList(w ResponseWriter, f File) {
 // ServeContent replies to the request using the content in the
 // provided ReadSeeker. The main benefit of ServeContent over io.Copy
 // is that it handles Range requests properly, sets the MIME type, and
-// handles If-Modified-Since requests.
+// handles If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since,
+// and If-Range requests.
 //
 // If the response's Content-Type header is not set, ServeContent
 // first tries to deduce the type from name's file extension and,
@@ -115,8 +116,8 @@ func dirList(w ResponseWriter, f File) {
 // The content's Seek method must work: ServeContent uses
 // a seek to the end of the content to determine its size.
 //
-// If the caller has set w's ETag header, ServeContent uses it to
-// handle requests using If-Range and If-None-Match.
+// If the caller has set w's ETag header formatted per RFC 7232, section 2.3,
+// ServeContent uses it to handle requests using If-Match, If-None-Match, or If-Range.
 //
 // Note that *os.File implements the io.ReadSeeker interface.
 func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) {
@@ -140,15 +141,17 @@ func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time
 // users.
 var errSeeker = errors.New("seeker can't seek")
 
+// errNoOverlap is returned by serveContent's parseRange if first-byte-pos of
+// all of the byte-range-spec values is greater than the content size.
+var errNoOverlap = errors.New("invalid range: failed to overlap")
+
 // if name is empty, filename is unknown. (used for mime type, before sniffing)
 // if modtime.IsZero(), modtime is unknown.
 // content must be seeked to the beginning of the file.
 // The sizeFunc is called at most once. Its error, if any, is sent in the HTTP response.
 func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, sizeFunc func() (int64, error), content io.ReadSeeker) {
-	if checkLastModified(w, r, modtime) {
-		return
-	}
-	rangeReq, done := checkETag(w, r, modtime)
+	setLastModified(w, modtime)
+	done, rangeReq := checkPreconditions(w, r, modtime)
 	if done {
 		return
 	}
@@ -189,6 +192,9 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
 	if size >= 0 {
 		ranges, err := parseRange(rangeReq, size)
 		if err != nil {
+			if err == errNoOverlap {
+				w.Header().Set("Content-Range", fmt.Sprintf("bytes */%d", size))
+			}
 			Error(w, err.Error(), StatusRequestedRangeNotSatisfiable)
 			return
 		}
@@ -263,90 +269,245 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time,
 	}
 }
 
-var unixEpochTime = time.Unix(0, 0)
-
-// modtime is the modification time of the resource to be served, or IsZero().
-// return value is whether this request is now complete.
-func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool {
-	if modtime.IsZero() || modtime.Equal(unixEpochTime) {
-		// If the file doesn't have a modtime (IsZero), or the modtime
-		// is obviously garbage (Unix time == 0), then ignore modtimes
-		// and don't process the If-Modified-Since header.
-		return false
+// scanETag determines if a syntactically valid ETag is present at s. If so,
+// the ETag and remaining text after consuming ETag is returned. Otherwise,
+// it returns "", "".
+func scanETag(s string) (etag string, remain string) {
+	s = textproto.TrimString(s)
+	start := 0
+	if strings.HasPrefix(s, "W/") {
+		start = 2
+	}
+	if len(s[start:]) < 2 || s[start] != '"' {
+		return "", ""
+	}
+	// ETag is either W/"text" or "text".
+	// See RFC 7232 2.3.
+	for i := start + 1; i < len(s); i++ {
+		c := s[i]
+		switch {
+		// Character values allowed in ETags.
+		case c == 0x21 || c >= 0x23 && c <= 0x7E || c >= 0x80:
+		case c == '"':
+			return string(s[:i+1]), s[i+1:]
+		default:
+			break
+		}
 	}
+	return "", ""
+}
 
-	// The Date-Modified header truncates sub-second precision, so
-	// use mtime < t+1s instead of mtime <= t to check for unmodified.
-	if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) {
-		h := w.Header()
-		delete(h, "Content-Type")
-		delete(h, "Content-Length")
-		w.WriteHeader(StatusNotModified)
-		return true
-	}
-	w.Header().Set("Last-Modified", modtime.UTC().Format(TimeFormat))
-	return false
+// etagStrongMatch reports whether a and b match using strong ETag comparison.
+// Assumes a and b are valid ETags.
+func etagStrongMatch(a, b string) bool {
+	return a == b && a != "" && a[0] == '"'
 }
 
-// checkETag implements If-None-Match and If-Range checks.
-//
-// The ETag or modtime must have been previously set in the
-// ResponseWriter's headers. The modtime is only compared at second
-// granularity and may be the zero value to mean unknown.
-//
-// The return value is the effective request "Range" header to use and
-// whether this request is now considered done.
-func checkETag(w ResponseWriter, r *Request, modtime time.Time) (rangeReq string, done bool) {
-	etag := w.Header().get("Etag")
-	rangeReq = r.Header.get("Range")
-
-	// Invalidate the range request if the entity doesn't match the one
-	// the client was expecting.
-	// "If-Range: version" means "ignore the Range: header unless version matches the
-	// current file."
-	// We only support ETag versions.
-	// The caller must have set the ETag on the response already.
-	if ir := r.Header.get("If-Range"); ir != "" && ir != etag {
-		// The If-Range value is typically the ETag value, but it may also be
-		// the modtime date. See golang.org/issue/8367.
-		timeMatches := false
-		if !modtime.IsZero() {
-			if t, err := ParseTime(ir); err == nil && t.Unix() == modtime.Unix() {
-				timeMatches = true
-			}
+// etagWeakMatch reports whether a and b match using weak ETag comparison.
+// Assumes a and b are valid ETags.
+func etagWeakMatch(a, b string) bool {
+	return strings.TrimPrefix(a, "W/") == strings.TrimPrefix(b, "W/")
+}
+
+// condResult is the result of an HTTP request precondition check.
+// See https://tools.ietf.org/html/rfc7232 section 3.
+type condResult int
+
+const (
+	condNone condResult = iota
+	condTrue
+	condFalse
+)
+
+func checkIfMatch(w ResponseWriter, r *Request) condResult {
+	im := r.Header.Get("If-Match")
+	if im == "" {
+		return condNone
+	}
+	for {
+		im = textproto.TrimString(im)
+		if len(im) == 0 {
+			break
+		}
+		if im[0] == ',' {
+			im = im[1:]
+			continue
+		}
+		if im[0] == '*' {
+			return condTrue
 		}
-		if !timeMatches {
-			rangeReq = ""
+		etag, remain := scanETag(im)
+		if etag == "" {
+			break
+		}
+		if etagStrongMatch(etag, w.Header().get("Etag")) {
+			return condTrue
 		}
+		im = remain
 	}
 
-	if inm := r.Header.get("If-None-Match"); inm != "" {
-		// Must know ETag.
+	return condFalse
+}
+
+func checkIfUnmodifiedSince(w ResponseWriter, r *Request, modtime time.Time) condResult {
+	ius := r.Header.Get("If-Unmodified-Since")
+	if ius == "" || isZeroTime(modtime) {
+		return condNone
+	}
+	if t, err := ParseTime(ius); err == nil {
+		// The Date-Modified header truncates sub-second precision, so
+		// use mtime < t+1s instead of mtime <= t to check for unmodified.
+		if modtime.Before(t.Add(1 * time.Second)) {
+			return condTrue
+		}
+		return condFalse
+	}
+	return condNone
+}
+
+func checkIfNoneMatch(w ResponseWriter, r *Request) condResult {
+	inm := r.Header.get("If-None-Match")
+	if inm == "" {
+		return condNone
+	}
+	buf := inm
+	for {
+		buf = textproto.TrimString(buf)
+		if len(buf) == 0 {
+			break
+		}
+		if buf[0] == ',' {
+			buf = buf[1:]
+		}
+		if buf[0] == '*' {
+			return condFalse
+		}
+		etag, remain := scanETag(buf)
 		if etag == "" {
-			return rangeReq, false
+			break
+		}
+		if etagWeakMatch(etag, w.Header().get("Etag")) {
+			return condFalse
 		}
+		buf = remain
+	}
+	return condTrue
+}
+
+func checkIfModifiedSince(w ResponseWriter, r *Request, modtime time.Time) condResult {
+	if r.Method != "GET" && r.Method != "HEAD" {
+		return condNone
+	}
+	ims := r.Header.Get("If-Modified-Since")
+	if ims == "" || isZeroTime(modtime) {
+		return condNone
+	}
+	t, err := ParseTime(ims)
+	if err != nil {
+		return condNone
+	}
+	// The Date-Modified header truncates sub-second precision, so
+	// use mtime < t+1s instead of mtime <= t to check for unmodified.
+	if modtime.Before(t.Add(1 * time.Second)) {
+		return condFalse
+	}
+	return condTrue
+}
+
+func checkIfRange(w ResponseWriter, r *Request, modtime time.Time) condResult {
+	if r.Method != "GET" {
+		return condNone
+	}
+	ir := r.Header.get("If-Range")
+	if ir == "" {
+		return condNone
+	}
+	etag, _ := scanETag(ir)
+	if etag != "" {
+		if etagStrongMatch(etag, w.Header().Get("Etag")) {
+			return condTrue
+		} else {
+			return condFalse
+		}
+	}
+	// The If-Range value is typically the ETag value, but it may also be
+	// the modtime date. See golang.org/issue/8367.
+	if modtime.IsZero() {
+		return condFalse
+	}
+	t, err := ParseTime(ir)
+	if err != nil {
+		return condFalse
+	}
+	if t.Unix() == modtime.Unix() {
+		return condTrue
+	}
+	return condFalse
+}
+
+var unixEpochTime = time.Unix(0, 0)
+
+// isZeroTime reports whether t is obviously unspecified (either zero or Unix()=0).
+func isZeroTime(t time.Time) bool {
+	return t.IsZero() || t.Equal(unixEpochTime)
+}
+
+func setLastModified(w ResponseWriter, modtime time.Time) {
+	if !isZeroTime(modtime) {
+		w.Header().Set("Last-Modified", modtime.UTC().Format(TimeFormat))
+	}
+}
 
-		// TODO(bradfitz): non-GET/HEAD requests require more work:
-		// sending a different status code on matches, and
-		// also can't use weak cache validators (those with a "W/
-		// prefix).  But most users of ServeContent will be using
-		// it on GET or HEAD, so only support those for now.
-		if r.Method != "GET" && r.Method != "HEAD" {
-			return rangeReq, false
+func writeNotModified(w ResponseWriter) {
+	// RFC 7232 section 4.1:
+	// a sender SHOULD NOT generate representation metadata other than the
+	// above listed fields unless said metadata exists for the purpose of
+	// guiding cache updates (e.g., Last-Modified might be useful if the
+	// response does not have an ETag field).
+	h := w.Header()
+	delete(h, "Content-Type")
+	delete(h, "Content-Length")
+	if h.Get("Etag") != "" {
+		delete(h, "Last-Modified")
+	}
+	w.WriteHeader(StatusNotModified)
+}
+
+// checkPreconditions evaluates request preconditions and reports whether a precondition
+// resulted in sending StatusNotModified or StatusPreconditionFailed.
+func checkPreconditions(w ResponseWriter, r *Request, modtime time.Time) (done bool, rangeHeader string) {
+	// This function carefully follows RFC 7232 section 6.
+	ch := checkIfMatch(w, r)
+	if ch == condNone {
+		ch = checkIfUnmodifiedSince(w, r, modtime)
+	}
+	if ch == condFalse {
+		w.WriteHeader(StatusPreconditionFailed)
+		return true, ""
+	}
+	switch checkIfNoneMatch(w, r) {
+	case condFalse:
+		if r.Method == "GET" || r.Method == "HEAD" {
+			writeNotModified(w)
+			return true, ""
+		} else {
+			w.WriteHeader(StatusPreconditionFailed)
+			return true, ""
 		}
+	case condNone:
+		if checkIfModifiedSince(w, r, modtime) == condFalse {
+			writeNotModified(w)
+			return true, ""
+		}
+	}
 
-		// TODO(bradfitz): deal with comma-separated or multiple-valued
-		// list of If-None-match values. For now just handle the common
-		// case of a single item.
-		if inm == etag || inm == "*" {
-			h := w.Header()
-			delete(h, "Content-Type")
-			delete(h, "Content-Length")
-			w.WriteHeader(StatusNotModified)
-			return "", true
+	rangeHeader = r.Header.get("Range")
+	if rangeHeader != "" {
+		if checkIfRange(w, r, modtime) == condFalse {
+			rangeHeader = ""
 		}
 	}
-	return rangeReq, false
+	return false, rangeHeader
 }
 
 // name is '/'-separated, not filepath.Separator.
@@ -419,9 +580,11 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
 
 	// Still a directory? (we didn't find an index.html file)
 	if d.IsDir() {
-		if checkLastModified(w, r, d.ModTime()) {
+		if checkIfModifiedSince(w, r, d.ModTime()) == condFalse {
+			writeNotModified(w)
 			return
 		}
+		w.Header().Set("Last-Modified", d.ModTime().UTC().Format(TimeFormat))
 		dirList(w, f)
 		return
 	}
@@ -543,6 +706,7 @@ func (r httpRange) mimeHeader(contentType string, size int64) textproto.MIMEHead
 }
 
 // parseRange parses a Range header string as per RFC 2616.
+// errNoOverlap is returned if none of the ranges overlap.
 func parseRange(s string, size int64) ([]httpRange, error) {
 	if s == "" {
 		return nil, nil // header not present
@@ -552,6 +716,7 @@ func parseRange(s string, size int64) ([]httpRange, error) {
 		return nil, errors.New("invalid range")
 	}
 	var ranges []httpRange
+	noOverlap := false
 	for _, ra := range strings.Split(s[len(b):], ",") {
 		ra = strings.TrimSpace(ra)
 		if ra == "" {
@@ -577,9 +742,15 @@ func parseRange(s string, size int64) ([]httpRange, error) {
 			r.length = size - r.start
 		} else {
 			i, err := strconv.ParseInt(start, 10, 64)
-			if err != nil || i >= size || i < 0 {
+			if err != nil || i < 0 {
 				return nil, errors.New("invalid range")
 			}
+			if i >= size {
+				// If the range begins after the size of the content,
+				// then it does not overlap.
+				noOverlap = true
+				continue
+			}
 			r.start = i
 			if end == "" {
 				// If no end is specified, range extends to end of the file.
@@ -597,6 +768,10 @@ func parseRange(s string, size int64) ([]httpRange, error) {
 		}
 		ranges = append(ranges, r)
 	}
+	if noOverlap && len(ranges) == 0 {
+		// The specified ranges did not overlap with the content.
+		return nil, errNoOverlap
+	}
 	return ranges, nil
 }
 
@@ -628,9 +803,3 @@ func sumRangesSize(ranges []httpRange) (size int64) {
 	}
 	return
 }
-
-type byName []os.FileInfo
-
-func (s byName) Len() int           { return len(s) }
-func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
-func (s byName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
diff --git a/src/net/http/fs_test.go b/src/net/http/fs_test.go
index c811891..17a0e4a 100644
--- a/src/net/http/fs_test.go
+++ b/src/net/http/fs_test.go
@@ -68,6 +68,7 @@ var ServeFileRangeTests = []struct {
 }
 
 func TestServeFile(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		ServeFile(w, r, "testdata/file")
@@ -274,6 +275,7 @@ func TestFileServerEscapesNames(t *testing.T) {
 		{`"'<>&`, `<a href="%22%27%3C%3E&">"'<>&</a>`},
 		{`?foo=bar#baz`, `<a href="%3Ffoo=bar%23baz">?foo=bar#baz</a>`},
 		{`<combo>?foo`, `<a href="%3Ccombo%3E%3Ffoo"><combo>?foo</a>`},
+		{`foo:bar`, `<a href="./foo:bar">foo:bar</a>`},
 	}
 
 	// We put each test file in its own directory in the fakeFS so we can look at it in isolation.
@@ -765,6 +767,7 @@ func TestServeContent(t *testing.T) {
 		reqHeader        map[string]string
 		wantLastMod      string
 		wantContentType  string
+		wantContentRange string
 		wantStatus       int
 	}
 	htmlModTime := mustStat(t, "testdata/index.html").ModTime()
@@ -782,8 +785,9 @@ func TestServeContent(t *testing.T) {
 			wantStatus:      200,
 		},
 		"not_modified_modtime": {
-			file:    "testdata/style.css",
-			modtime: htmlModTime,
+			file:      "testdata/style.css",
+			serveETag: `"foo"`, // Last-Modified sent only when no ETag
+			modtime:   htmlModTime,
 			reqHeader: map[string]string{
 				"If-Modified-Since": htmlModTime.UTC().Format(TimeFormat),
 			},
@@ -792,6 +796,7 @@ func TestServeContent(t *testing.T) {
 		"not_modified_modtime_with_contenttype": {
 			file:             "testdata/style.css",
 			serveContentType: "text/css", // explicit content type
+			serveETag:        `"foo"`,    // Last-Modified sent only when no ETag
 			modtime:          htmlModTime,
 			reqHeader: map[string]string{
 				"If-Modified-Since": htmlModTime.UTC().Format(TimeFormat),
@@ -808,21 +813,62 @@ func TestServeContent(t *testing.T) {
 		},
 		"not_modified_etag_no_seek": {
 			content:   panicOnSeek{nil}, // should never be called
-			serveETag: `"foo"`,
+			serveETag: `W/"foo"`,        // If-None-Match uses weak ETag comparison
 			reqHeader: map[string]string{
-				"If-None-Match": `"foo"`,
+				"If-None-Match": `"baz", W/"foo"`,
 			},
 			wantStatus: 304,
 		},
+		"if_none_match_mismatch": {
+			file:      "testdata/style.css",
+			serveETag: `"foo"`,
+			reqHeader: map[string]string{
+				"If-None-Match": `"Foo"`,
+			},
+			wantStatus:      200,
+			wantContentType: "text/css; charset=utf-8",
+		},
 		"range_good": {
 			file:      "testdata/style.css",
 			serveETag: `"A"`,
 			reqHeader: map[string]string{
 				"Range": "bytes=0-4",
 			},
-			wantStatus:      StatusPartialContent,
+			wantStatus:       StatusPartialContent,
+			wantContentType:  "text/css; charset=utf-8",
+			wantContentRange: "bytes 0-4/8",
+		},
+		"range_match": {
+			file:      "testdata/style.css",
+			serveETag: `"A"`,
+			reqHeader: map[string]string{
+				"Range":    "bytes=0-4",
+				"If-Range": `"A"`,
+			},
+			wantStatus:       StatusPartialContent,
+			wantContentType:  "text/css; charset=utf-8",
+			wantContentRange: "bytes 0-4/8",
+		},
+		"range_match_weak_etag": {
+			file:      "testdata/style.css",
+			serveETag: `W/"A"`,
+			reqHeader: map[string]string{
+				"Range":    "bytes=0-4",
+				"If-Range": `W/"A"`,
+			},
+			wantStatus:      200,
 			wantContentType: "text/css; charset=utf-8",
 		},
+		"range_no_overlap": {
+			file:      "testdata/style.css",
+			serveETag: `"A"`,
+			reqHeader: map[string]string{
+				"Range": "bytes=10-20",
+			},
+			wantStatus:       StatusRequestedRangeNotSatisfiable,
+			wantContentType:  "text/plain; charset=utf-8",
+			wantContentRange: "bytes */8",
+		},
 		// An If-Range resource for entity "A", but entity "B" is now current.
 		// The Range request should be ignored.
 		"range_no_match": {
@@ -842,9 +888,10 @@ func TestServeContent(t *testing.T) {
 				"Range":    "bytes=0-4",
 				"If-Range": "Wed, 25 Jun 2014 17:12:18 GMT",
 			},
-			wantStatus:      StatusPartialContent,
-			wantContentType: "text/css; charset=utf-8",
-			wantLastMod:     "Wed, 25 Jun 2014 17:12:18 GMT",
+			wantStatus:       StatusPartialContent,
+			wantContentType:  "text/css; charset=utf-8",
+			wantContentRange: "bytes 0-4/8",
+			wantLastMod:      "Wed, 25 Jun 2014 17:12:18 GMT",
 		},
 		"range_with_modtime_nanos": {
 			file:    "testdata/style.css",
@@ -853,9 +900,10 @@ func TestServeContent(t *testing.T) {
 				"Range":    "bytes=0-4",
 				"If-Range": "Wed, 25 Jun 2014 17:12:18 GMT",
 			},
-			wantStatus:      StatusPartialContent,
-			wantContentType: "text/css; charset=utf-8",
-			wantLastMod:     "Wed, 25 Jun 2014 17:12:18 GMT",
+			wantStatus:       StatusPartialContent,
+			wantContentType:  "text/css; charset=utf-8",
+			wantContentRange: "bytes 0-4/8",
+			wantLastMod:      "Wed, 25 Jun 2014 17:12:18 GMT",
 		},
 		"unix_zero_modtime": {
 			content:         strings.NewReader("<html>foo"),
@@ -863,6 +911,62 @@ func TestServeContent(t *testing.T) {
 			wantStatus:      StatusOK,
 			wantContentType: "text/html; charset=utf-8",
 		},
+		"ifmatch_matches": {
+			file:      "testdata/style.css",
+			serveETag: `"A"`,
+			reqHeader: map[string]string{
+				"If-Match": `"Z", "A"`,
+			},
+			wantStatus:      200,
+			wantContentType: "text/css; charset=utf-8",
+		},
+		"ifmatch_star": {
+			file:      "testdata/style.css",
+			serveETag: `"A"`,
+			reqHeader: map[string]string{
+				"If-Match": `*`,
+			},
+			wantStatus:      200,
+			wantContentType: "text/css; charset=utf-8",
+		},
+		"ifmatch_failed": {
+			file:      "testdata/style.css",
+			serveETag: `"A"`,
+			reqHeader: map[string]string{
+				"If-Match": `"B"`,
+			},
+			wantStatus:      412,
+			wantContentType: "text/plain; charset=utf-8",
+		},
+		"ifmatch_fails_on_weak_etag": {
+			file:      "testdata/style.css",
+			serveETag: `W/"A"`,
+			reqHeader: map[string]string{
+				"If-Match": `W/"A"`,
+			},
+			wantStatus:      412,
+			wantContentType: "text/plain; charset=utf-8",
+		},
+		"if_unmodified_since_true": {
+			file:    "testdata/style.css",
+			modtime: htmlModTime,
+			reqHeader: map[string]string{
+				"If-Unmodified-Since": htmlModTime.UTC().Format(TimeFormat),
+			},
+			wantStatus:      200,
+			wantContentType: "text/css; charset=utf-8",
+			wantLastMod:     htmlModTime.UTC().Format(TimeFormat),
+		},
+		"if_unmodified_since_false": {
+			file:    "testdata/style.css",
+			modtime: htmlModTime,
+			reqHeader: map[string]string{
+				"If-Unmodified-Since": htmlModTime.Add(-2 * time.Second).UTC().Format(TimeFormat),
+			},
+			wantStatus:      412,
+			wantContentType: "text/plain; charset=utf-8",
+			wantLastMod:     htmlModTime.UTC().Format(TimeFormat),
+		},
 	}
 	for testName, tt := range tests {
 		var content io.ReadSeeker
@@ -903,6 +1007,9 @@ func TestServeContent(t *testing.T) {
 		if g, e := res.Header.Get("Content-Type"), tt.wantContentType; g != e {
 			t.Errorf("test %q: content-type = %q, want %q", testName, g, e)
 		}
+		if g, e := res.Header.Get("Content-Range"), tt.wantContentRange; g != e {
+			t.Errorf("test %q: content-range = %q, want %q", testName, g, e)
+		}
 		if g, e := res.Header.Get("Last-Modified"), tt.wantLastMod; g != e {
 			t.Errorf("test %q: last-modified = %q, want %q", testName, g, e)
 		}
@@ -958,6 +1065,7 @@ func TestServeContentErrorMessages(t *testing.T) {
 
 // verifies that sendfile is being used on Linux
 func TestLinuxSendfile(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	if runtime.GOOS != "linux" {
 		t.Skip("skipping; linux-only test")
@@ -978,10 +1086,12 @@ func TestLinuxSendfile(t *testing.T) {
 
 	syscalls := "sendfile,sendfile64"
 	switch runtime.GOARCH {
-	case "mips64", "mips64le", "s390x":
+	case "mips64le", "s390x":
 		// strace on the above platforms doesn't support sendfile64
 		// and will error out if we specify that with `-e trace='.
 		syscalls = "sendfile"
+	case "mips64":
+		t.Skip("TODO: update this test to be robust against various versions of strace on mips64. See golang.org/issue/33430")
 	}
 
 	var buf bytes.Buffer
@@ -1008,10 +1118,9 @@ func TestLinuxSendfile(t *testing.T) {
 	Post(fmt.Sprintf("http://%s/quit", ln.Addr()), "", nil)
 	child.Wait()
 
-	rx := regexp.MustCompile(`sendfile(64)?\(\d+,\s*\d+,\s*NULL,\s*\d+\)\s*=\s*\d+\s*\n`)
-	rxResume := regexp.MustCompile(`<\.\.\. sendfile(64)? resumed> \)\s*=\s*\d+\s*\n`)
+	rx := regexp.MustCompile(`sendfile(64)?\(\d+,\s*\d+,\s*NULL,\s*\d+`)
 	out := buf.String()
-	if !rx.MatchString(out) && !rxResume.MatchString(out) {
+	if !rx.MatchString(out) {
 		t.Errorf("no sendfile system call found in:\n%s", out)
 	}
 }
@@ -1090,3 +1199,26 @@ func (d fileServerCleanPathDir) Open(path string) (File, error) {
 }
 
 type panicOnSeek struct{ io.ReadSeeker }
+
+func Test_scanETag(t *testing.T) {
+	tests := []struct {
+		in         string
+		wantETag   string
+		wantRemain string
+	}{
+		{`W/"etag-1"`, `W/"etag-1"`, ""},
+		{`"etag-2"`, `"etag-2"`, ""},
+		{`"etag-1", "etag-2"`, `"etag-1"`, `, "etag-2"`},
+		{"", "", ""},
+		{"", "", ""},
+		{"W/", "", ""},
+		{`W/"truc`, "", ""},
+		{`w/"case-sensitive"`, "", ""},
+	}
+	for _, test := range tests {
+		etag, remain := ExportScanETag(test.in)
+		if etag != test.wantETag || remain != test.wantRemain {
+			t.Errorf("scanETag(%q)=%q %q, want %q %q", test.in, etag, remain, test.wantETag, test.wantRemain)
+		}
+	}
+}
diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go
index 063043a..bb7f05d 100644
--- a/src/net/http/h2_bundle.go
+++ b/src/net/http/h2_bundle.go
@@ -21,6 +21,7 @@ import (
 	"bytes"
 	"compress/gzip"
 	"context"
+	"crypto/rand"
 	"crypto/tls"
 	"encoding/binary"
 	"errors"
@@ -43,6 +44,7 @@ import (
 	"time"
 
 	"golang_org/x/net/http2/hpack"
+	"golang_org/x/net/idna"
 	"golang_org/x/net/lex/httplex"
 )
 
@@ -1254,7 +1256,7 @@ func (f *http2Framer) WriteSettings(settings ...http2Setting) error {
 	return f.endWrite()
 }
 
-// WriteSettings writes an empty SETTINGS frame with the ACK bit set.
+// WriteSettingsAck writes an empty SETTINGS frame with the ACK bit set.
 //
 // It will perform exactly one Write to the underlying Writer.
 // It is the caller's responsibility to not call other Write methods concurrently.
@@ -2091,6 +2093,13 @@ type http2clientTrace httptrace.ClientTrace
 
 func http2reqContext(r *Request) context.Context { return r.Context() }
 
+func (t *http2Transport) idleConnTimeout() time.Duration {
+	if t.t1 != nil {
+		return t.t1.IdleConnTimeout
+	}
+	return 0
+}
+
 func http2setResponseUncompressed(res *Response) { res.Uncompressed = true }
 
 func http2traceGotConn(req *Request, cc *http2ClientConn) {
@@ -2145,6 +2154,40 @@ func http2requestTrace(req *Request) *http2clientTrace {
 	return (*http2clientTrace)(trace)
 }
 
+// Ping sends a PING frame to the server and waits for the ack.
+func (cc *http2ClientConn) Ping(ctx context.Context) error {
+	return cc.ping(ctx)
+}
+
+func http2cloneTLSConfig(c *tls.Config) *tls.Config { return c.Clone() }
+
+var _ Pusher = (*http2responseWriter)(nil)
+
+// Push implements http.Pusher.
+func (w *http2responseWriter) Push(target string, opts *PushOptions) error {
+	internalOpts := http2pushOptions{}
+	if opts != nil {
+		internalOpts.Method = opts.Method
+		internalOpts.Header = opts.Header
+	}
+	return w.push(target, internalOpts)
+}
+
+func http2configureServer18(h1 *Server, h2 *http2Server) error {
+	if h2.IdleTimeout == 0 {
+		if h1.IdleTimeout != 0 {
+			h2.IdleTimeout = h1.IdleTimeout
+		} else {
+			h2.IdleTimeout = h1.ReadTimeout
+		}
+	}
+	return nil
+}
+
+func http2shouldLogPanic(panicValue interface{}) bool {
+	return panicValue != nil && panicValue != ErrAbortHandler
+}
+
 var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
 
 type http2goroutineLock uint64
@@ -2368,6 +2411,7 @@ var (
 	http2VerboseLogs    bool
 	http2logFrameWrites bool
 	http2logFrameReads  bool
+	http2inTests        bool
 )
 
 func init() {
@@ -2409,13 +2453,23 @@ var (
 
 type http2streamState int
 
+// HTTP/2 stream states.
+//
+// See http://tools.ietf.org/html/rfc7540#section-5.1.
+//
+// For simplicity, the server code merges "reserved (local)" into
+// "half-closed (remote)". This is one less state transition to track.
+// The only downside is that we send PUSH_PROMISEs slightly less
+// liberally than allowable. More discussion here:
+// https://lists.w3.org/Archives/Public/ietf-http-wg/2016JulSep/0599.html
+//
+// "reserved (remote)" is omitted since the client code does not
+// support server push.
 const (
 	http2stateIdle http2streamState = iota
 	http2stateOpen
 	http2stateHalfClosedLocal
 	http2stateHalfClosedRemote
-	http2stateResvLocal
-	http2stateResvRemote
 	http2stateClosed
 )
 
@@ -2424,8 +2478,6 @@ var http2stateName = [...]string{
 	http2stateOpen:             "Open",
 	http2stateHalfClosedLocal:  "HalfClosedLocal",
 	http2stateHalfClosedRemote: "HalfClosedRemote",
-	http2stateResvLocal:        "ResvLocal",
-	http2stateResvRemote:       "ResvRemote",
 	http2stateClosed:           "Closed",
 }
 
@@ -2586,13 +2638,27 @@ func http2newBufferedWriter(w io.Writer) *http2bufferedWriter {
 	return &http2bufferedWriter{w: w}
 }
 
+// bufWriterPoolBufferSize is the size of bufio.Writer's
+// buffers created using bufWriterPool.
+//
+// TODO: pick a less arbitrary value? this is a bit under
+// (3 x typical 1500 byte MTU) at least. Other than that,
+// not much thought went into it.
+const http2bufWriterPoolBufferSize = 4 << 10
+
 var http2bufWriterPool = sync.Pool{
 	New: func() interface{} {
-
-		return bufio.NewWriterSize(nil, 4<<10)
+		return bufio.NewWriterSize(nil, http2bufWriterPoolBufferSize)
 	},
 }
 
+func (w *http2bufferedWriter) Available() int {
+	if w.bw == nil {
+		return http2bufWriterPoolBufferSize
+	}
+	return w.bw.Available()
+}
+
 func (w *http2bufferedWriter) Write(p []byte) (n int, err error) {
 	if w.bw == nil {
 		bw := http2bufWriterPool.Get().(*bufio.Writer)
@@ -2686,6 +2752,19 @@ func (s *http2sorter) SortStrings(ss []string) {
 	s.v = save
 }
 
+// validPseudoPath reports whether v is a valid :path pseudo-header
+// value. It must be either:
+//
+//     *) a non-empty string starting with '/', but not with with "//",
+//     *) the string '*', for OPTIONS requests.
+//
+// For now this is only used a quick check for deciding when to clean
+// up Opaque URLs before sending requests from the Transport.
+// See golang.org/issue/16847
+func http2validPseudoPath(v string) bool {
+	return (len(v) > 0 && v[0] == '/' && (len(v) == 1 || v[1] != '/')) || v == "*"
+}
+
 // pipe is a goroutine-safe io.Reader/io.Writer pair.  It's like
 // io.Pipe except there are no PipeReader/PipeWriter halves, and the
 // underlying buffer is an interface. (io.Pipe is always unbuffered)
@@ -2882,6 +2961,15 @@ type http2Server struct {
 	// PermitProhibitedCipherSuites, if true, permits the use of
 	// cipher suites prohibited by the HTTP/2 spec.
 	PermitProhibitedCipherSuites bool
+
+	// IdleTimeout specifies how long until idle clients should be
+	// closed with a GOAWAY frame. PING frames are not considered
+	// activity for the purposes of IdleTimeout.
+	IdleTimeout time.Duration
+
+	// NewWriteScheduler constructs a write scheduler for a connection.
+	// If nil, a default scheduler is chosen.
+	NewWriteScheduler func() http2WriteScheduler
 }
 
 func (s *http2Server) maxReadFrameSize() uint32 {
@@ -2904,9 +2992,15 @@ func (s *http2Server) maxConcurrentStreams() uint32 {
 //
 // ConfigureServer must be called before s begins serving.
 func http2ConfigureServer(s *Server, conf *http2Server) error {
+	if s == nil {
+		panic("nil *http.Server")
+	}
 	if conf == nil {
 		conf = new(http2Server)
 	}
+	if err := http2configureServer18(s, conf); err != nil {
+		return err
+	}
 
 	if s.TLSConfig == nil {
 		s.TLSConfig = new(tls.Config)
@@ -2945,8 +3039,6 @@ func http2ConfigureServer(s *Server, conf *http2Server) error {
 		s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, http2NextProtoTLS)
 	}
 
-	s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2-14")
-
 	if s.TLSNextProto == nil {
 		s.TLSNextProto = map[string]func(*Server, *tls.Conn, Handler){}
 	}
@@ -2960,7 +3052,6 @@ func http2ConfigureServer(s *Server, conf *http2Server) error {
 		})
 	}
 	s.TLSNextProto[http2NextProtoTLS] = protoHandler
-	s.TLSNextProto["h2-14"] = protoHandler
 	return nil
 }
 
@@ -3014,29 +3105,35 @@ func (s *http2Server) ServeConn(c net.Conn, opts *http2ServeConnOpts) {
 	defer cancel()
 
 	sc := &http2serverConn{
-		srv:              s,
-		hs:               opts.baseConfig(),
-		conn:             c,
-		baseCtx:          baseCtx,
-		remoteAddrStr:    c.RemoteAddr().String(),
-		bw:               http2newBufferedWriter(c),
-		handler:          opts.handler(),
-		streams:          make(map[uint32]*http2stream),
-		readFrameCh:      make(chan http2readFrameResult),
-		wantWriteFrameCh: make(chan http2frameWriteMsg, 8),
-		wroteFrameCh:     make(chan http2frameWriteResult, 1),
-		bodyReadCh:       make(chan http2bodyReadMsg),
-		doneServing:      make(chan struct{}),
-		advMaxStreams:    s.maxConcurrentStreams(),
-		writeSched: http2writeScheduler{
-			maxFrameSize: http2initialMaxFrameSize,
-		},
+		srv:               s,
+		hs:                opts.baseConfig(),
+		conn:              c,
+		baseCtx:           baseCtx,
+		remoteAddrStr:     c.RemoteAddr().String(),
+		bw:                http2newBufferedWriter(c),
+		handler:           opts.handler(),
+		streams:           make(map[uint32]*http2stream),
+		readFrameCh:       make(chan http2readFrameResult),
+		wantWriteFrameCh:  make(chan http2FrameWriteRequest, 8),
+		wantStartPushCh:   make(chan http2startPushRequest, 8),
+		wroteFrameCh:      make(chan http2frameWriteResult, 1),
+		bodyReadCh:        make(chan http2bodyReadMsg),
+		doneServing:       make(chan struct{}),
+		clientMaxStreams:  math.MaxUint32,
+		advMaxStreams:     s.maxConcurrentStreams(),
 		initialWindowSize: http2initialWindowSize,
+		maxFrameSize:      http2initialMaxFrameSize,
 		headerTableSize:   http2initialHeaderTableSize,
 		serveG:            http2newGoroutineLock(),
 		pushEnabled:       true,
 	}
 
+	if s.NewWriteScheduler != nil {
+		sc.writeSched = s.NewWriteScheduler()
+	} else {
+		sc.writeSched = http2NewRandomWriteScheduler()
+	}
+
 	sc.flow.add(http2initialWindowSize)
 	sc.inflow.add(http2initialWindowSize)
 	sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
@@ -3090,16 +3187,18 @@ type http2serverConn struct {
 	handler          Handler
 	baseCtx          http2contextContext
 	framer           *http2Framer
-	doneServing      chan struct{}              // closed when serverConn.serve ends
-	readFrameCh      chan http2readFrameResult  // written by serverConn.readFrames
-	wantWriteFrameCh chan http2frameWriteMsg    // from handlers -> serve
-	wroteFrameCh     chan http2frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes
-	bodyReadCh       chan http2bodyReadMsg      // from handlers -> serve
-	testHookCh       chan func(int)             // code to run on the serve loop
-	flow             http2flow                  // conn-wide (not stream-specific) outbound flow control
-	inflow           http2flow                  // conn-wide inbound flow control
-	tlsState         *tls.ConnectionState       // shared by all handlers, like net/http
+	doneServing      chan struct{}               // closed when serverConn.serve ends
+	readFrameCh      chan http2readFrameResult   // written by serverConn.readFrames
+	wantWriteFrameCh chan http2FrameWriteRequest // from handlers -> serve
+	wantStartPushCh  chan http2startPushRequest  // from handlers -> serve
+	wroteFrameCh     chan http2frameWriteResult  // from writeFrameAsync -> serve, tickles more frame writes
+	bodyReadCh       chan http2bodyReadMsg       // from handlers -> serve
+	testHookCh       chan func(int)              // code to run on the serve loop
+	flow             http2flow                   // conn-wide (not stream-specific) outbound flow control
+	inflow           http2flow                   // conn-wide inbound flow control
+	tlsState         *tls.ConnectionState        // shared by all handlers, like net/http
 	remoteAddrStr    string
+	writeSched       http2WriteScheduler
 
 	// Everything following is owned by the serve loop; use serveG.check():
 	serveG                http2goroutineLock // used to verify funcs are on serve()
@@ -3109,22 +3208,27 @@ type http2serverConn struct {
 	unackedSettings       int    // how many SETTINGS have we sent without ACKs?
 	clientMaxStreams      uint32 // SETTINGS_MAX_CONCURRENT_STREAMS from client (our PUSH_PROMISE limit)
 	advMaxStreams         uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
-	curOpenStreams        uint32 // client's number of open streams
-	maxStreamID           uint32 // max ever seen
+	curClientStreams      uint32 // number of open streams initiated by the client
+	curPushedStreams      uint32 // number of open streams initiated by server push
+	maxClientStreamID     uint32 // max ever seen from client (odd), or 0 if there have been no client requests
+	maxPushPromiseID      uint32 // ID of the last push promise (even), or 0 if there have been no pushes
 	streams               map[uint32]*http2stream
 	initialWindowSize     int32
+	maxFrameSize          int32
 	headerTableSize       uint32
 	peerMaxHeaderListSize uint32            // zero means unknown (default)
 	canonHeader           map[string]string // http2-lower-case -> Go-Canonical-Case
-	writingFrame          bool              // started write goroutine but haven't heard back on wroteFrameCh
+	writingFrame          bool              // started writing a frame (on serve goroutine or separate)
+	writingFrameAsync     bool              // started a frame on its own goroutine but haven't heard back on wroteFrameCh
 	needsFrameFlush       bool              // last frame write wasn't a flush
-	writeSched            http2writeScheduler
-	inGoAway              bool // we've started to or sent GOAWAY
-	needToSendGoAway      bool // we need to schedule a GOAWAY frame write
+	inGoAway              bool              // we've started to or sent GOAWAY
+	inFrameScheduleLoop   bool              // whether we're in the scheduleFrameWrite loop
+	needToSendGoAway      bool              // we need to schedule a GOAWAY frame write
 	goAwayCode            http2ErrCode
 	shutdownTimerCh       <-chan time.Time // nil until used
 	shutdownTimer         *time.Timer      // nil until used
-	freeRequestBodyBuf    []byte           // if non-nil, a free initialWindowSize buffer for getRequestBodyBuf
+	idleTimer             *time.Timer      // nil if unused
+	idleTimerCh           <-chan time.Time // nil if unused
 
 	// Owned by the writeFrameAsync goroutine:
 	headerWriteBuf bytes.Buffer
@@ -3168,11 +3272,11 @@ type http2stream struct {
 	numTrailerValues int64
 	weight           uint8
 	state            http2streamState
-	sentReset        bool // only true once detached from streams map
-	gotReset         bool // only true once detacted from streams map
-	gotTrailerHeader bool // HEADER frame for trailers was seen
-	wroteHeaders     bool // whether we wrote headers (not status 100)
-	reqBuf           []byte
+	sentReset        bool   // only true once detached from streams map
+	gotReset         bool   // only true once detacted from streams map
+	gotTrailerHeader bool   // HEADER frame for trailers was seen
+	wroteHeaders     bool   // whether we wrote headers (not status 100)
+	reqBuf           []byte // if non-nil, body pipe buffer to return later at EOF
 
 	trailer    Header // accumulated trailers
 	reqTrailer Header // handler's Request.Trailer
@@ -3195,8 +3299,14 @@ func (sc *http2serverConn) state(streamID uint32) (http2streamState, *http2strea
 		return st.state, st
 	}
 
-	if streamID <= sc.maxStreamID {
-		return http2stateClosed, nil
+	if streamID%2 == 1 {
+		if streamID <= sc.maxClientStreamID {
+			return http2stateClosed, nil
+		}
+	} else {
+		if streamID <= sc.maxPushPromiseID {
+			return http2stateClosed, nil
+		}
 	}
 	return http2stateIdle, nil
 }
@@ -3328,17 +3438,17 @@ func (sc *http2serverConn) readFrames() {
 
 // frameWriteResult is the message passed from writeFrameAsync to the serve goroutine.
 type http2frameWriteResult struct {
-	wm  http2frameWriteMsg // what was written (or attempted)
-	err error              // result of the writeFrame call
+	wr  http2FrameWriteRequest // what was written (or attempted)
+	err error                  // result of the writeFrame call
 }
 
 // writeFrameAsync runs in its own goroutine and writes a single frame
 // and then reports when it's done.
 // At most one goroutine can be running writeFrameAsync at a time per
 // serverConn.
-func (sc *http2serverConn) writeFrameAsync(wm http2frameWriteMsg) {
-	err := wm.write.writeFrame(sc)
-	sc.wroteFrameCh <- http2frameWriteResult{wm, err}
+func (sc *http2serverConn) writeFrameAsync(wr http2FrameWriteRequest) {
+	err := wr.write.writeFrame(sc)
+	sc.wroteFrameCh <- http2frameWriteResult{wr, err}
 }
 
 func (sc *http2serverConn) closeAllStreamsOnConnClose() {
@@ -3382,7 +3492,7 @@ func (sc *http2serverConn) serve() {
 		sc.vlogf("http2: server connection from %v on %p", sc.conn.RemoteAddr(), sc.hs)
 	}
 
-	sc.writeFrame(http2frameWriteMsg{
+	sc.writeFrame(http2FrameWriteRequest{
 		write: http2writeSettings{
 			{http2SettingMaxFrameSize, sc.srv.maxReadFrameSize()},
 			{http2SettingMaxConcurrentStreams, sc.advMaxStreams},
@@ -3399,6 +3509,17 @@ func (sc *http2serverConn) serve() {
 	sc.setConnState(StateActive)
 	sc.setConnState(StateIdle)
 
+	if sc.srv.IdleTimeout != 0 {
+		sc.idleTimer = time.NewTimer(sc.srv.IdleTimeout)
+		defer sc.idleTimer.Stop()
+		sc.idleTimerCh = sc.idleTimer.C
+	}
+
+	var gracefulShutdownCh <-chan struct{}
+	if sc.hs != nil {
+		gracefulShutdownCh = http2h1ServerShutdownChan(sc.hs)
+	}
+
 	go sc.readFrames()
 
 	settingsTimer := time.NewTimer(http2firstSettingsTimeout)
@@ -3406,8 +3527,10 @@ func (sc *http2serverConn) serve() {
 	for {
 		loopNum++
 		select {
-		case wm := <-sc.wantWriteFrameCh:
-			sc.writeFrame(wm)
+		case wr := <-sc.wantWriteFrameCh:
+			sc.writeFrame(wr)
+		case spr := <-sc.wantStartPushCh:
+			sc.startPush(spr)
 		case res := <-sc.wroteFrameCh:
 			sc.wroteFrame(res)
 		case res := <-sc.readFrameCh:
@@ -3424,12 +3547,22 @@ func (sc *http2serverConn) serve() {
 		case <-settingsTimer.C:
 			sc.logf("timeout waiting for SETTINGS frames from %v", sc.conn.RemoteAddr())
 			return
+		case <-gracefulShutdownCh:
+			gracefulShutdownCh = nil
+			sc.startGracefulShutdown()
 		case <-sc.shutdownTimerCh:
 			sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
 			return
+		case <-sc.idleTimerCh:
+			sc.vlogf("connection is idle")
+			sc.goAway(http2ErrCodeNo)
 		case fn := <-sc.testHookCh:
 			fn(loopNum)
 		}
+
+		if sc.inGoAway && sc.curClientStreams == 0 && !sc.needToSendGoAway && !sc.writingFrame {
+			return
+		}
 	}
 }
 
@@ -3477,7 +3610,7 @@ func (sc *http2serverConn) writeDataFromHandler(stream *http2stream, data []byte
 	ch := http2errChanPool.Get().(chan error)
 	writeArg := http2writeDataPool.Get().(*http2writeData)
 	*writeArg = http2writeData{stream.id, data, endStream}
-	err := sc.writeFrameFromHandler(http2frameWriteMsg{
+	err := sc.writeFrameFromHandler(http2FrameWriteRequest{
 		write:  writeArg,
 		stream: stream,
 		done:   ch,
@@ -3507,17 +3640,17 @@ func (sc *http2serverConn) writeDataFromHandler(stream *http2stream, data []byte
 	return err
 }
 
-// writeFrameFromHandler sends wm to sc.wantWriteFrameCh, but aborts
+// writeFrameFromHandler sends wr to sc.wantWriteFrameCh, but aborts
 // if the connection has gone away.
 //
 // This must not be run from the serve goroutine itself, else it might
 // deadlock writing to sc.wantWriteFrameCh (which is only mildly
 // buffered and is read by serve itself). If you're on the serve
 // goroutine, call writeFrame instead.
-func (sc *http2serverConn) writeFrameFromHandler(wm http2frameWriteMsg) error {
+func (sc *http2serverConn) writeFrameFromHandler(wr http2FrameWriteRequest) error {
 	sc.serveG.checkNotOn()
 	select {
-	case sc.wantWriteFrameCh <- wm:
+	case sc.wantWriteFrameCh <- wr:
 		return nil
 	case <-sc.doneServing:
 
@@ -3533,36 +3666,36 @@ func (sc *http2serverConn) writeFrameFromHandler(wm http2frameWriteMsg) error {
 // make it onto the wire
 //
 // If you're not on the serve goroutine, use writeFrameFromHandler instead.
-func (sc *http2serverConn) writeFrame(wm http2frameWriteMsg) {
+func (sc *http2serverConn) writeFrame(wr http2FrameWriteRequest) {
 	sc.serveG.check()
 
 	var ignoreWrite bool
 
-	switch wm.write.(type) {
+	switch wr.write.(type) {
 	case *http2writeResHeaders:
-		wm.stream.wroteHeaders = true
+		wr.stream.wroteHeaders = true
 	case http2write100ContinueHeadersFrame:
-		if wm.stream.wroteHeaders {
+		if wr.stream.wroteHeaders {
 			ignoreWrite = true
 		}
 	}
 
 	if !ignoreWrite {
-		sc.writeSched.add(wm)
+		sc.writeSched.Push(wr)
 	}
 	sc.scheduleFrameWrite()
 }
 
-// startFrameWrite starts a goroutine to write wm (in a separate
+// startFrameWrite starts a goroutine to write wr (in a separate
 // goroutine since that might block on the network), and updates the
-// serve goroutine's state about the world, updated from info in wm.
-func (sc *http2serverConn) startFrameWrite(wm http2frameWriteMsg) {
+// serve goroutine's state about the world, updated from info in wr.
+func (sc *http2serverConn) startFrameWrite(wr http2FrameWriteRequest) {
 	sc.serveG.check()
 	if sc.writingFrame {
 		panic("internal error: can only be writing one frame at a time")
 	}
 
-	st := wm.stream
+	st := wr.stream
 	if st != nil {
 		switch st.state {
 		case http2stateHalfClosedLocal:
@@ -3573,13 +3706,31 @@ func (sc *http2serverConn) startFrameWrite(wm http2frameWriteMsg) {
 				sc.scheduleFrameWrite()
 				return
 			}
-			panic(fmt.Sprintf("internal error: attempt to send a write %v on a closed stream", wm))
+			panic(fmt.Sprintf("internal error: attempt to send a write %v on a closed stream", wr))
+		}
+	}
+	if wpp, ok := wr.write.(*http2writePushPromise); ok {
+		var err error
+		wpp.promisedID, err = wpp.allocatePromisedID()
+		if err != nil {
+			sc.writingFrameAsync = false
+			if wr.done != nil {
+				wr.done <- err
+			}
+			return
 		}
 	}
 
 	sc.writingFrame = true
 	sc.needsFrameFlush = true
-	go sc.writeFrameAsync(wm)
+	if wr.write.staysWithinBuffer(sc.bw.Available()) {
+		sc.writingFrameAsync = false
+		err := wr.write.writeFrame(sc)
+		sc.wroteFrame(http2frameWriteResult{wr, err})
+	} else {
+		sc.writingFrameAsync = true
+		go sc.writeFrameAsync(wr)
+	}
 }
 
 // errHandlerPanicked is the error given to any callers blocked in a read from
@@ -3595,24 +3746,25 @@ func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) {
 		panic("internal error: expected to be already writing a frame")
 	}
 	sc.writingFrame = false
+	sc.writingFrameAsync = false
 
-	wm := res.wm
-	st := wm.stream
+	wr := res.wr
+	st := wr.stream
 
-	closeStream := http2endsStream(wm.write)
+	closeStream := http2endsStream(wr.write)
 
-	if _, ok := wm.write.(http2handlerPanicRST); ok {
+	if _, ok := wr.write.(http2handlerPanicRST); ok {
 		sc.closeStream(st, http2errHandlerPanicked)
 	}
 
-	if ch := wm.done; ch != nil {
+	if ch := wr.done; ch != nil {
 		select {
 		case ch <- res.err:
 		default:
-			panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wm.write))
+			panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wr.write))
 		}
 	}
-	wm.write = nil
+	wr.write = nil
 
 	if closeStream {
 		if st == nil {
@@ -3646,47 +3798,68 @@ func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) {
 // flush the write buffer.
 func (sc *http2serverConn) scheduleFrameWrite() {
 	sc.serveG.check()
-	if sc.writingFrame {
-		return
-	}
-	if sc.needToSendGoAway {
-		sc.needToSendGoAway = false
-		sc.startFrameWrite(http2frameWriteMsg{
-			write: &http2writeGoAway{
-				maxStreamID: sc.maxStreamID,
-				code:        sc.goAwayCode,
-			},
-		})
-		return
-	}
-	if sc.needToSendSettingsAck {
-		sc.needToSendSettingsAck = false
-		sc.startFrameWrite(http2frameWriteMsg{write: http2writeSettingsAck{}})
+	if sc.writingFrame || sc.inFrameScheduleLoop {
 		return
 	}
-	if !sc.inGoAway {
-		if wm, ok := sc.writeSched.take(); ok {
-			sc.startFrameWrite(wm)
-			return
+	sc.inFrameScheduleLoop = true
+	for !sc.writingFrameAsync {
+		if sc.needToSendGoAway {
+			sc.needToSendGoAway = false
+			sc.startFrameWrite(http2FrameWriteRequest{
+				write: &http2writeGoAway{
+					maxStreamID: sc.maxClientStreamID,
+					code:        sc.goAwayCode,
+				},
+			})
+			continue
 		}
+		if sc.needToSendSettingsAck {
+			sc.needToSendSettingsAck = false
+			sc.startFrameWrite(http2FrameWriteRequest{write: http2writeSettingsAck{}})
+			continue
+		}
+		if !sc.inGoAway || sc.goAwayCode == http2ErrCodeNo {
+			if wr, ok := sc.writeSched.Pop(); ok {
+				sc.startFrameWrite(wr)
+				continue
+			}
+		}
+		if sc.needsFrameFlush {
+			sc.startFrameWrite(http2FrameWriteRequest{write: http2flushFrameWriter{}})
+			sc.needsFrameFlush = false
+			continue
+		}
+		break
 	}
-	if sc.needsFrameFlush {
-		sc.startFrameWrite(http2frameWriteMsg{write: http2flushFrameWriter{}})
-		sc.needsFrameFlush = false
-		return
-	}
+	sc.inFrameScheduleLoop = false
+}
+
+// startGracefulShutdown sends a GOAWAY with ErrCodeNo to tell the
+// client we're gracefully shutting down. The connection isn't closed
+// until all current streams are done.
+func (sc *http2serverConn) startGracefulShutdown() {
+	sc.goAwayIn(http2ErrCodeNo, 0)
 }
 
 func (sc *http2serverConn) goAway(code http2ErrCode) {
 	sc.serveG.check()
-	if sc.inGoAway {
-		return
-	}
+	var forceCloseIn time.Duration
 	if code != http2ErrCodeNo {
-		sc.shutDownIn(250 * time.Millisecond)
+		forceCloseIn = 250 * time.Millisecond
 	} else {
 
-		sc.shutDownIn(1 * time.Second)
+		forceCloseIn = 1 * time.Second
+	}
+	sc.goAwayIn(code, forceCloseIn)
+}
+
+func (sc *http2serverConn) goAwayIn(code http2ErrCode, forceCloseIn time.Duration) {
+	sc.serveG.check()
+	if sc.inGoAway {
+		return
+	}
+	if forceCloseIn != 0 {
+		sc.shutDownIn(forceCloseIn)
 	}
 	sc.inGoAway = true
 	sc.needToSendGoAway = true
@@ -3702,7 +3875,7 @@ func (sc *http2serverConn) shutDownIn(d time.Duration) {
 
 func (sc *http2serverConn) resetStream(se http2StreamError) {
 	sc.serveG.check()
-	sc.writeFrame(http2frameWriteMsg{write: se})
+	sc.writeFrame(http2FrameWriteRequest{write: se})
 	if st, ok := sc.streams[se.StreamID]; ok {
 		st.sentReset = true
 		sc.closeStream(st, se)
@@ -3782,6 +3955,8 @@ func (sc *http2serverConn) processFrame(f http2Frame) error {
 		return sc.processResetStream(f)
 	case *http2PriorityFrame:
 		return sc.processPriority(f)
+	case *http2GoAwayFrame:
+		return sc.processGoAway(f)
 	case *http2PushPromiseFrame:
 
 		return http2ConnectionError(http2ErrCodeProtocol)
@@ -3801,7 +3976,10 @@ func (sc *http2serverConn) processPing(f *http2PingFrame) error {
 
 		return http2ConnectionError(http2ErrCodeProtocol)
 	}
-	sc.writeFrame(http2frameWriteMsg{write: http2writePingAck{f}})
+	if sc.inGoAway && sc.goAwayCode != http2ErrCodeNo {
+		return nil
+	}
+	sc.writeFrame(http2FrameWriteRequest{write: http2writePingAck{f}})
 	return nil
 }
 
@@ -3809,7 +3987,11 @@ func (sc *http2serverConn) processWindowUpdate(f *http2WindowUpdateFrame) error
 	sc.serveG.check()
 	switch {
 	case f.StreamID != 0:
-		st := sc.streams[f.StreamID]
+		state, st := sc.state(f.StreamID)
+		if state == http2stateIdle {
+
+			return http2ConnectionError(http2ErrCodeProtocol)
+		}
 		if st == nil {
 
 			return nil
@@ -3848,11 +4030,21 @@ func (sc *http2serverConn) closeStream(st *http2stream, err error) {
 		panic(fmt.Sprintf("invariant; can't close stream in state %v", st.state))
 	}
 	st.state = http2stateClosed
-	sc.curOpenStreams--
-	if sc.curOpenStreams == 0 {
-		sc.setConnState(StateIdle)
+	if st.isPushed() {
+		sc.curPushedStreams--
+	} else {
+		sc.curClientStreams--
 	}
 	delete(sc.streams, st.id)
+	if len(sc.streams) == 0 {
+		sc.setConnState(StateIdle)
+		if sc.srv.IdleTimeout != 0 {
+			sc.idleTimer.Reset(sc.srv.IdleTimeout)
+		}
+		if http2h1ServerKeepAlivesDisabled(sc.hs) {
+			sc.startGracefulShutdown()
+		}
+	}
 	if p := st.body; p != nil {
 
 		sc.sendWindowUpdate(nil, p.Len())
@@ -3860,11 +4052,7 @@ func (sc *http2serverConn) closeStream(st *http2stream, err error) {
 		p.CloseWithError(err)
 	}
 	st.cw.Close()
-	sc.writeSched.forgetStream(st.id)
-	if st.reqBuf != nil {
-
-		sc.freeRequestBodyBuf = st.reqBuf
-	}
+	sc.writeSched.CloseStream(st.id)
 }
 
 func (sc *http2serverConn) processSettings(f *http2SettingsFrame) error {
@@ -3904,7 +4092,7 @@ func (sc *http2serverConn) processSetting(s http2Setting) error {
 	case http2SettingInitialWindowSize:
 		return sc.processSettingInitialWindowSize(s.Val)
 	case http2SettingMaxFrameSize:
-		sc.writeSched.maxFrameSize = s.Val
+		sc.maxFrameSize = int32(s.Val)
 	case http2SettingMaxHeaderListSize:
 		sc.peerMaxHeaderListSize = s.Val
 	default:
@@ -3933,11 +4121,18 @@ func (sc *http2serverConn) processSettingInitialWindowSize(val uint32) error {
 
 func (sc *http2serverConn) processData(f *http2DataFrame) error {
 	sc.serveG.check()
+	if sc.inGoAway && sc.goAwayCode != http2ErrCodeNo {
+		return nil
+	}
 	data := f.Data()
 
 	id := f.Header().StreamID
-	st, ok := sc.streams[id]
-	if !ok || st.state != http2stateOpen || st.gotTrailerHeader {
+	state, st := sc.state(id)
+	if id == 0 || state == http2stateIdle {
+
+		return http2ConnectionError(http2ErrCodeProtocol)
+	}
+	if st == nil || state != http2stateOpen || st.gotTrailerHeader {
 
 		if sc.inflow.available() < int32(f.Length) {
 			return http2streamError(id, http2ErrCodeFlowControl)
@@ -3985,6 +4180,24 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error {
 	return nil
 }
 
+func (sc *http2serverConn) processGoAway(f *http2GoAwayFrame) error {
+	sc.serveG.check()
+	if f.ErrCode != http2ErrCodeNo {
+		sc.logf("http2: received GOAWAY %+v, starting graceful shutdown", f)
+	} else {
+		sc.vlogf("http2: received GOAWAY %+v, starting graceful shutdown", f)
+	}
+	sc.startGracefulShutdown()
+
+	sc.pushEnabled = false
+	return nil
+}
+
+// isPushed reports whether the stream is server-initiated.
+func (st *http2stream) isPushed() bool {
+	return st.id%2 == 0
+}
+
 // endStream closes a Request.Body's pipe. It is called when a DATA
 // frame says a request body is over (or after trailers).
 func (st *http2stream) endStream() {
@@ -4014,7 +4227,7 @@ func (st *http2stream) copyTrailersToHandlerRequest() {
 
 func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error {
 	sc.serveG.check()
-	id := f.Header().StreamID
+	id := f.StreamID
 	if sc.inGoAway {
 
 		return nil
@@ -4024,50 +4237,39 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error {
 		return http2ConnectionError(http2ErrCodeProtocol)
 	}
 
-	st := sc.streams[f.Header().StreamID]
-	if st != nil {
+	if st := sc.streams[f.StreamID]; st != nil {
 		return st.processTrailerHeaders(f)
 	}
 
-	if id <= sc.maxStreamID {
+	if id <= sc.maxClientStreamID {
 		return http2ConnectionError(http2ErrCodeProtocol)
 	}
-	sc.maxStreamID = id
+	sc.maxClientStreamID = id
 
-	ctx, cancelCtx := http2contextWithCancel(sc.baseCtx)
-	st = &http2stream{
-		sc:        sc,
-		id:        id,
-		state:     http2stateOpen,
-		ctx:       ctx,
-		cancelCtx: cancelCtx,
+	if sc.idleTimer != nil {
+		sc.idleTimer.Stop()
 	}
-	if f.StreamEnded() {
-		st.state = http2stateHalfClosedRemote
-	}
-	st.cw.Init()
 
-	st.flow.conn = &sc.flow
-	st.flow.add(sc.initialWindowSize)
-	st.inflow.conn = &sc.inflow
-	st.inflow.add(http2initialWindowSize)
+	if sc.curClientStreams+1 > sc.advMaxStreams {
+		if sc.unackedSettings == 0 {
 
-	sc.streams[id] = st
-	if f.HasPriority() {
-		http2adjustStreamPriority(sc.streams, st.id, f.Priority)
-	}
-	sc.curOpenStreams++
-	if sc.curOpenStreams == 1 {
-		sc.setConnState(StateActive)
+			return http2streamError(id, http2ErrCodeProtocol)
+		}
+
+		return http2streamError(id, http2ErrCodeRefusedStream)
 	}
-	if sc.curOpenStreams > sc.advMaxStreams {
 
-		if sc.unackedSettings == 0 {
+	initialState := http2stateOpen
+	if f.StreamEnded() {
+		initialState = http2stateHalfClosedRemote
+	}
+	st := sc.newStream(id, 0, initialState)
 
-			return http2streamError(st.id, http2ErrCodeProtocol)
+	if f.HasPriority() {
+		if err := http2checkPriority(f.StreamID, f.Priority); err != nil {
+			return err
 		}
-
-		return http2streamError(st.id, http2ErrCodeRefusedStream)
+		sc.writeSched.AdjustStream(st.id, f.Priority)
 	}
 
 	rw, req, err := sc.newWriterAndRequest(st, f)
@@ -4085,10 +4287,14 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error {
 	if f.Truncated {
 
 		handler = http2handleHeaderListTooLong
-	} else if err := http2checkValidHTTP2Request(req); err != nil {
+	} else if err := http2checkValidHTTP2RequestHeaders(req.Header); err != nil {
 		handler = http2new400Handler(err)
 	}
 
+	if sc.hs.ReadTimeout != 0 {
+		sc.conn.SetReadDeadline(time.Time{})
+	}
+
 	go sc.runHandler(rw, req, handler)
 	return nil
 }
@@ -4121,90 +4327,138 @@ func (st *http2stream) processTrailerHeaders(f *http2MetaHeadersFrame) error {
 	return nil
 }
 
-func (sc *http2serverConn) processPriority(f *http2PriorityFrame) error {
-	http2adjustStreamPriority(sc.streams, f.StreamID, f.http2PriorityParam)
+func http2checkPriority(streamID uint32, p http2PriorityParam) error {
+	if streamID == p.StreamDep {
+
+		return http2streamError(streamID, http2ErrCodeProtocol)
+	}
 	return nil
 }
 
-func http2adjustStreamPriority(streams map[uint32]*http2stream, streamID uint32, priority http2PriorityParam) {
-	st, ok := streams[streamID]
-	if !ok {
+func (sc *http2serverConn) processPriority(f *http2PriorityFrame) error {
+	if sc.inGoAway {
+		return nil
+	}
+	if err := http2checkPriority(f.StreamID, f.http2PriorityParam); err != nil {
+		return err
+	}
+	sc.writeSched.AdjustStream(f.StreamID, f.http2PriorityParam)
+	return nil
+}
 
-		return
+func (sc *http2serverConn) newStream(id, pusherID uint32, state http2streamState) *http2stream {
+	sc.serveG.check()
+	if id == 0 {
+		panic("internal error: cannot create stream with id 0")
 	}
-	st.weight = priority.Weight
-	parent := streams[priority.StreamDep]
-	if parent == st {
 
-		return
+	ctx, cancelCtx := http2contextWithCancel(sc.baseCtx)
+	st := &http2stream{
+		sc:        sc,
+		id:        id,
+		state:     state,
+		ctx:       ctx,
+		cancelCtx: cancelCtx,
 	}
+	st.cw.Init()
+	st.flow.conn = &sc.flow
+	st.flow.add(sc.initialWindowSize)
+	st.inflow.conn = &sc.inflow
+	st.inflow.add(http2initialWindowSize)
 
-	for piter := parent; piter != nil; piter = piter.parent {
-		if piter == st {
-			parent.parent = st.parent
-			break
-		}
+	sc.streams[id] = st
+	sc.writeSched.OpenStream(st.id, http2OpenStreamOptions{PusherID: pusherID})
+	if st.isPushed() {
+		sc.curPushedStreams++
+	} else {
+		sc.curClientStreams++
 	}
-	st.parent = parent
-	if priority.Exclusive && (st.parent != nil || priority.StreamDep == 0) {
-		for _, openStream := range streams {
-			if openStream != st && openStream.parent == st.parent {
-				openStream.parent = st
-			}
-		}
+	if sc.curClientStreams+sc.curPushedStreams == 1 {
+		sc.setConnState(StateActive)
 	}
+
+	return st
 }
 
 func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHeadersFrame) (*http2responseWriter, *Request, error) {
 	sc.serveG.check()
 
-	method := f.PseudoValue("method")
-	path := f.PseudoValue("path")
-	scheme := f.PseudoValue("scheme")
-	authority := f.PseudoValue("authority")
+	rp := http2requestParam{
+		method:    f.PseudoValue("method"),
+		scheme:    f.PseudoValue("scheme"),
+		authority: f.PseudoValue("authority"),
+		path:      f.PseudoValue("path"),
+	}
 
-	isConnect := method == "CONNECT"
+	isConnect := rp.method == "CONNECT"
 	if isConnect {
-		if path != "" || scheme != "" || authority == "" {
+		if rp.path != "" || rp.scheme != "" || rp.authority == "" {
 			return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol)
 		}
-	} else if method == "" || path == "" ||
-		(scheme != "https" && scheme != "http") {
+	} else if rp.method == "" || rp.path == "" || (rp.scheme != "https" && rp.scheme != "http") {
 
 		return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol)
 	}
 
 	bodyOpen := !f.StreamEnded()
-	if method == "HEAD" && bodyOpen {
+	if rp.method == "HEAD" && bodyOpen {
 
 		return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol)
 	}
-	var tlsState *tls.ConnectionState // nil if not scheme https
 
-	if scheme == "https" {
-		tlsState = sc.tlsState
+	rp.header = make(Header)
+	for _, hf := range f.RegularFields() {
+		rp.header.Add(sc.canonicalHeader(hf.Name), hf.Value)
+	}
+	if rp.authority == "" {
+		rp.authority = rp.header.Get("Host")
 	}
 
-	header := make(Header)
-	for _, hf := range f.RegularFields() {
-		header.Add(sc.canonicalHeader(hf.Name), hf.Value)
+	rw, req, err := sc.newWriterAndRequestNoBody(st, rp)
+	if err != nil {
+		return nil, nil, err
+	}
+	if bodyOpen {
+		st.reqBuf = http2getRequestBodyBuf()
+		req.Body.(*http2requestBody).pipe = &http2pipe{
+			b: &http2fixedBuffer{buf: st.reqBuf},
+		}
+
+		if vv, ok := rp.header["Content-Length"]; ok {
+			req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
+		} else {
+			req.ContentLength = -1
+		}
 	}
+	return rw, req, nil
+}
+
+type http2requestParam struct {
+	method                  string
+	scheme, authority, path string
+	header                  Header
+}
+
+func (sc *http2serverConn) newWriterAndRequestNoBody(st *http2stream, rp http2requestParam) (*http2responseWriter, *Request, error) {
+	sc.serveG.check()
 
-	if authority == "" {
-		authority = header.Get("Host")
+	var tlsState *tls.ConnectionState // nil if not scheme https
+	if rp.scheme == "https" {
+		tlsState = sc.tlsState
 	}
-	needsContinue := header.Get("Expect") == "100-continue"
+
+	needsContinue := rp.header.Get("Expect") == "100-continue"
 	if needsContinue {
-		header.Del("Expect")
+		rp.header.Del("Expect")
 	}
 
-	if cookies := header["Cookie"]; len(cookies) > 1 {
-		header.Set("Cookie", strings.Join(cookies, "; "))
+	if cookies := rp.header["Cookie"]; len(cookies) > 1 {
+		rp.header.Set("Cookie", strings.Join(cookies, "; "))
 	}
 
 	// Setup Trailers
 	var trailer Header
-	for _, v := range header["Trailer"] {
+	for _, v := range rp.header["Trailer"] {
 		for _, key := range strings.Split(v, ",") {
 			key = CanonicalHeaderKey(strings.TrimSpace(key))
 			switch key {
@@ -4218,55 +4472,42 @@ func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHead
 			}
 		}
 	}
-	delete(header, "Trailer")
+	delete(rp.header, "Trailer")
 
-	body := &http2requestBody{
-		conn:          sc,
-		stream:        st,
-		needsContinue: needsContinue,
-	}
 	var url_ *url.URL
 	var requestURI string
-	if isConnect {
-		url_ = &url.URL{Host: authority}
-		requestURI = authority
+	if rp.method == "CONNECT" {
+		url_ = &url.URL{Host: rp.authority}
+		requestURI = rp.authority
 	} else {
 		var err error
-		url_, err = url.ParseRequestURI(path)
+		url_, err = url.ParseRequestURI(rp.path)
 		if err != nil {
-			return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol)
+			return nil, nil, http2streamError(st.id, http2ErrCodeProtocol)
 		}
-		requestURI = path
+		requestURI = rp.path
+	}
+
+	body := &http2requestBody{
+		conn:          sc,
+		stream:        st,
+		needsContinue: needsContinue,
 	}
 	req := &Request{
-		Method:     method,
+		Method:     rp.method,
 		URL:        url_,
 		RemoteAddr: sc.remoteAddrStr,
-		Header:     header,
+		Header:     rp.header,
 		RequestURI: requestURI,
 		Proto:      "HTTP/2.0",
 		ProtoMajor: 2,
 		ProtoMinor: 0,
 		TLS:        tlsState,
-		Host:       authority,
+		Host:       rp.authority,
 		Body:       body,
 		Trailer:    trailer,
 	}
 	req = http2requestWithContext(req, st.ctx)
-	if bodyOpen {
-
-		buf := make([]byte, http2initialWindowSize)
-
-		body.pipe = &http2pipe{
-			b: &http2fixedBuffer{buf: buf},
-		}
-
-		if vv, ok := header["Content-Length"]; ok {
-			req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
-		} else {
-			req.ContentLength = -1
-		}
-	}
 
 	rws := http2responseWriterStatePool.Get().(*http2responseWriterState)
 	bwSave := rws.bw
@@ -4282,13 +4523,22 @@ func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHead
 	return rw, req, nil
 }
 
-func (sc *http2serverConn) getRequestBodyBuf() []byte {
-	sc.serveG.check()
-	if buf := sc.freeRequestBodyBuf; buf != nil {
-		sc.freeRequestBodyBuf = nil
-		return buf
+var http2reqBodyCache = make(chan []byte, 8)
+
+func http2getRequestBodyBuf() []byte {
+	select {
+	case b := <-http2reqBodyCache:
+		return b
+	default:
+		return make([]byte, http2initialWindowSize)
+	}
+}
+
+func http2putRequestBodyBuf(b []byte) {
+	select {
+	case http2reqBodyCache <- b:
+	default:
 	}
-	return make([]byte, http2initialWindowSize)
 }
 
 // Run on its own goroutine.
@@ -4298,15 +4548,17 @@ func (sc *http2serverConn) runHandler(rw *http2responseWriter, req *Request, han
 		rw.rws.stream.cancelCtx()
 		if didPanic {
 			e := recover()
-			// Same as net/http:
-			const size = 64 << 10
-			buf := make([]byte, size)
-			buf = buf[:runtime.Stack(buf, false)]
-			sc.writeFrameFromHandler(http2frameWriteMsg{
+			sc.writeFrameFromHandler(http2FrameWriteRequest{
 				write:  http2handlerPanicRST{rw.rws.stream.id},
 				stream: rw.rws.stream,
 			})
-			sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
+
+			if http2shouldLogPanic(e) {
+				const size = 64 << 10
+				buf := make([]byte, size)
+				buf = buf[:runtime.Stack(buf, false)]
+				sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
+			}
 			return
 		}
 		rw.handlerDone()
@@ -4334,7 +4586,7 @@ func (sc *http2serverConn) writeHeaders(st *http2stream, headerData *http2writeR
 
 		errc = http2errChanPool.Get().(chan error)
 	}
-	if err := sc.writeFrameFromHandler(http2frameWriteMsg{
+	if err := sc.writeFrameFromHandler(http2FrameWriteRequest{
 		write:  headerData,
 		stream: st,
 		done:   errc,
@@ -4357,7 +4609,7 @@ func (sc *http2serverConn) writeHeaders(st *http2stream, headerData *http2writeR
 
 // called from handler goroutines.
 func (sc *http2serverConn) write100ContinueHeaders(st *http2stream) {
-	sc.writeFrameFromHandler(http2frameWriteMsg{
+	sc.writeFrameFromHandler(http2FrameWriteRequest{
 		write:  http2write100ContinueHeadersFrame{st.id},
 		stream: st,
 	})
@@ -4373,11 +4625,19 @@ type http2bodyReadMsg struct {
 // called from handler goroutines.
 // Notes that the handler for the given stream ID read n bytes of its body
 // and schedules flow control tokens to be sent.
-func (sc *http2serverConn) noteBodyReadFromHandler(st *http2stream, n int) {
+func (sc *http2serverConn) noteBodyReadFromHandler(st *http2stream, n int, err error) {
 	sc.serveG.checkNotOn()
-	select {
-	case sc.bodyReadCh <- http2bodyReadMsg{st, n}:
-	case <-sc.doneServing:
+	if n > 0 {
+		select {
+		case sc.bodyReadCh <- http2bodyReadMsg{st, n}:
+		case <-sc.doneServing:
+		}
+	}
+	if err == io.EOF {
+		if buf := st.reqBuf; buf != nil {
+			st.reqBuf = nil
+			http2putRequestBodyBuf(buf)
+		}
 	}
 }
 
@@ -4419,7 +4679,7 @@ func (sc *http2serverConn) sendWindowUpdate32(st *http2stream, n int32) {
 	if st != nil {
 		streamID = st.id
 	}
-	sc.writeFrame(http2frameWriteMsg{
+	sc.writeFrame(http2FrameWriteRequest{
 		write:  http2writeWindowUpdate{streamID: streamID, n: uint32(n)},
 		stream: st,
 	})
@@ -4434,16 +4694,19 @@ func (sc *http2serverConn) sendWindowUpdate32(st *http2stream, n int32) {
 	}
 }
 
+// requestBody is the Handler's Request.Body type.
+// Read and Close may be called concurrently.
 type http2requestBody struct {
 	stream        *http2stream
 	conn          *http2serverConn
-	closed        bool
+	closed        bool       // for use by Close only
+	sawEOF        bool       // for use by Read only
 	pipe          *http2pipe // non-nil if we have a HTTP entity message body
 	needsContinue bool       // need to send a 100-continue
 }
 
 func (b *http2requestBody) Close() error {
-	if b.pipe != nil {
+	if b.pipe != nil && !b.closed {
 		b.pipe.BreakWithError(http2errClosedBody)
 	}
 	b.closed = true
@@ -4455,13 +4718,17 @@ func (b *http2requestBody) Read(p []byte) (n int, err error) {
 		b.needsContinue = false
 		b.conn.write100ContinueHeaders(b.stream)
 	}
-	if b.pipe == nil {
+	if b.pipe == nil || b.sawEOF {
 		return 0, io.EOF
 	}
 	n, err = b.pipe.Read(p)
-	if n > 0 {
-		b.conn.noteBodyReadFromHandler(b.stream, n)
+	if err == io.EOF {
+		b.sawEOF = true
+	}
+	if b.conn == nil && http2inTests {
+		return
 	}
+	b.conn.noteBodyReadFromHandler(b.stream, n, err)
 	return
 }
 
@@ -4696,8 +4963,9 @@ func (w *http2responseWriter) CloseNotify() <-chan bool {
 	if ch == nil {
 		ch = make(chan bool, 1)
 		rws.closeNotifierCh = ch
+		cw := rws.stream.cw
 		go func() {
-			rws.stream.cw.Wait()
+			cw.Wait()
 			ch <- true
 		}()
 	}
@@ -4793,6 +5061,172 @@ func (w *http2responseWriter) handlerDone() {
 	http2responseWriterStatePool.Put(rws)
 }
 
+// Push errors.
+var (
+	http2ErrRecursivePush    = errors.New("http2: recursive push not allowed")
+	http2ErrPushLimitReached = errors.New("http2: push would exceed peer's SETTINGS_MAX_CONCURRENT_STREAMS")
+)
+
+// pushOptions is the internal version of http.PushOptions, which we
+// cannot include here because it's only defined in Go 1.8 and later.
+type http2pushOptions struct {
+	Method string
+	Header Header
+}
+
+func (w *http2responseWriter) push(target string, opts http2pushOptions) error {
+	st := w.rws.stream
+	sc := st.sc
+	sc.serveG.checkNotOn()
+
+	if st.isPushed() {
+		return http2ErrRecursivePush
+	}
+
+	if opts.Method == "" {
+		opts.Method = "GET"
+	}
+	if opts.Header == nil {
+		opts.Header = Header{}
+	}
+	wantScheme := "http"
+	if w.rws.req.TLS != nil {
+		wantScheme = "https"
+	}
+
+	u, err := url.Parse(target)
+	if err != nil {
+		return err
+	}
+	if u.Scheme == "" {
+		if !strings.HasPrefix(target, "/") {
+			return fmt.Errorf("target must be an absolute URL or an absolute path: %q", target)
+		}
+		u.Scheme = wantScheme
+		u.Host = w.rws.req.Host
+	} else {
+		if u.Scheme != wantScheme {
+			return fmt.Errorf("cannot push URL with scheme %q from request with scheme %q", u.Scheme, wantScheme)
+		}
+		if u.Host == "" {
+			return errors.New("URL must have a host")
+		}
+	}
+	for k := range opts.Header {
+		if strings.HasPrefix(k, ":") {
+			return fmt.Errorf("promised request headers cannot include psuedo header %q", k)
+		}
+
+		switch strings.ToLower(k) {
+		case "content-length", "content-encoding", "trailer", "te", "expect", "host":
+			return fmt.Errorf("promised request headers cannot include %q", k)
+		}
+	}
+	if err := http2checkValidHTTP2RequestHeaders(opts.Header); err != nil {
+		return err
+	}
+
+	if opts.Method != "GET" && opts.Method != "HEAD" {
+		return fmt.Errorf("method %q must be GET or HEAD", opts.Method)
+	}
+
+	msg := http2startPushRequest{
+		parent: st,
+		method: opts.Method,
+		url:    u,
+		header: http2cloneHeader(opts.Header),
+		done:   http2errChanPool.Get().(chan error),
+	}
+
+	select {
+	case <-sc.doneServing:
+		return http2errClientDisconnected
+	case <-st.cw:
+		return http2errStreamClosed
+	case sc.wantStartPushCh <- msg:
+	}
+
+	select {
+	case <-sc.doneServing:
+		return http2errClientDisconnected
+	case <-st.cw:
+		return http2errStreamClosed
+	case err := <-msg.done:
+		http2errChanPool.Put(msg.done)
+		return err
+	}
+}
+
+type http2startPushRequest struct {
+	parent *http2stream
+	method string
+	url    *url.URL
+	header Header
+	done   chan error
+}
+
+func (sc *http2serverConn) startPush(msg http2startPushRequest) {
+	sc.serveG.check()
+
+	if msg.parent.state != http2stateOpen && msg.parent.state != http2stateHalfClosedRemote {
+
+		msg.done <- http2errStreamClosed
+		return
+	}
+
+	if !sc.pushEnabled {
+		msg.done <- ErrNotSupported
+		return
+	}
+
+	allocatePromisedID := func() (uint32, error) {
+		sc.serveG.check()
+
+		if !sc.pushEnabled {
+			return 0, ErrNotSupported
+		}
+
+		if sc.curPushedStreams+1 > sc.clientMaxStreams {
+			return 0, http2ErrPushLimitReached
+		}
+
+		if sc.maxPushPromiseID+2 >= 1<<31 {
+			sc.startGracefulShutdown()
+			return 0, http2ErrPushLimitReached
+		}
+		sc.maxPushPromiseID += 2
+		promisedID := sc.maxPushPromiseID
+
+		promised := sc.newStream(promisedID, msg.parent.id, http2stateHalfClosedRemote)
+		rw, req, err := sc.newWriterAndRequestNoBody(promised, http2requestParam{
+			method:    msg.method,
+			scheme:    msg.url.Scheme,
+			authority: msg.url.Host,
+			path:      msg.url.RequestURI(),
+			header:    msg.header,
+		})
+		if err != nil {
+
+			panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err))
+		}
+
+		go sc.runHandler(rw, req, sc.handler.ServeHTTP)
+		return promisedID, nil
+	}
+
+	sc.writeFrame(http2FrameWriteRequest{
+		write: &http2writePushPromise{
+			streamID:           msg.parent.id,
+			method:             msg.method,
+			url:                msg.url,
+			h:                  msg.header,
+			allocatePromisedID: allocatePromisedID,
+		},
+		stream: msg.parent,
+		done:   msg.done,
+	})
+}
+
 // foreachHeaderElement splits v according to the "#rule" construction
 // in RFC 2616 section 2.1 and calls fn for each non-empty element.
 func http2foreachHeaderElement(v string, fn func(string)) {
@@ -4820,16 +5254,16 @@ var http2connHeaders = []string{
 	"Upgrade",
 }
 
-// checkValidHTTP2Request checks whether req is a valid HTTP/2 request,
+// checkValidHTTP2RequestHeaders checks whether h is a valid HTTP/2 request,
 // per RFC 7540 Section 8.1.2.2.
 // The returned error is reported to users.
-func http2checkValidHTTP2Request(req *Request) error {
-	for _, h := range http2connHeaders {
-		if _, ok := req.Header[h]; ok {
-			return fmt.Errorf("request header %q is not valid in HTTP/2", h)
+func http2checkValidHTTP2RequestHeaders(h Header) error {
+	for _, k := range http2connHeaders {
+		if _, ok := h[k]; ok {
+			return fmt.Errorf("request header %q is not valid in HTTP/2", k)
 		}
 	}
-	te := req.Header["Te"]
+	te := h["Te"]
 	if len(te) > 0 && (len(te) > 1 || (te[0] != "trailers" && te[0] != "")) {
 		return errors.New(`request header "TE" may only be "trailers" in HTTP/2`)
 	}
@@ -4877,6 +5311,45 @@ var http2badTrailer = map[string]bool{
 	"Www-Authenticate":    true,
 }
 
+// h1ServerShutdownChan returns a channel that will be closed when the
+// provided *http.Server wants to shut down.
+//
+// This is a somewhat hacky way to get at http1 innards. It works
+// when the http2 code is bundled into the net/http package in the
+// standard library. The alternatives ended up making the cmd/go tool
+// depend on http Servers. This is the lightest option for now.
+// This is tested via the TestServeShutdown* tests in net/http.
+func http2h1ServerShutdownChan(hs *Server) <-chan struct{} {
+	if fn := http2testh1ServerShutdownChan; fn != nil {
+		return fn(hs)
+	}
+	var x interface{} = hs
+	type I interface {
+		getDoneChan() <-chan struct{}
+	}
+	if hs, ok := x.(I); ok {
+		return hs.getDoneChan()
+	}
+	return nil
+}
+
+// optional test hook for h1ServerShutdownChan.
+var http2testh1ServerShutdownChan func(hs *Server) <-chan struct{}
+
+// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
+// disabled. See comments on h1ServerShutdownChan above for why
+// the code is written this way.
+func http2h1ServerKeepAlivesDisabled(hs *Server) bool {
+	var x interface{} = hs
+	type I interface {
+		doKeepAlives() bool
+	}
+	if hs, ok := x.(I); ok {
+		return !hs.doKeepAlives()
+	}
+	return false
+}
+
 const (
 	// transportDefaultConnFlow is how many connection-level flow control
 	// tokens we give the server at start-up, past the default 64k.
@@ -4997,6 +5470,9 @@ type http2ClientConn struct {
 	readerDone chan struct{} // closed on error
 	readerErr  error         // set before readerDone is closed
 
+	idleTimeout time.Duration // or 0 for never
+	idleTimer   *time.Timer
+
 	mu              sync.Mutex // guards following
 	cond            *sync.Cond // hold mu; broadcast on flow/closed changes
 	flow            http2flow  // our conn-level flow control quota (cs.flow is per stream)
@@ -5007,6 +5483,7 @@ type http2ClientConn struct {
 	goAwayDebug     string                        // goAway frame's debug data, retained as a string
 	streams         map[uint32]*http2clientStream // client-initiated
 	nextStreamID    uint32
+	pings           map[[8]byte]chan struct{} // in flight ping data to notification channel
 	bw              *bufio.Writer
 	br              *bufio.Reader
 	fr              *http2Framer
@@ -5041,6 +5518,7 @@ type http2clientStream struct {
 	bytesRemain int64     // -1 means unknown; owned by transportResponseBody.Read
 	readErr     error     // sticky read error; owned by transportResponseBody.Read
 	stopReqBody error     // if non-nil, stop writing req body; guarded by cc.mu
+	didReset    bool      // whether we sent a RST_STREAM to the server; guarded by cc.mu
 
 	peerReset chan struct{} // closed on peer reset
 	resetErr  error         // populated before peerReset is closed
@@ -5068,15 +5546,26 @@ func (cs *http2clientStream) awaitRequestCancel(req *Request) {
 	}
 	select {
 	case <-req.Cancel:
+		cs.cancelStream()
 		cs.bufPipe.CloseWithError(http2errRequestCanceled)
-		cs.cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil)
 	case <-ctx.Done():
+		cs.cancelStream()
 		cs.bufPipe.CloseWithError(ctx.Err())
-		cs.cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil)
 	case <-cs.done:
 	}
 }
 
+func (cs *http2clientStream) cancelStream() {
+	cs.cc.mu.Lock()
+	didReset := cs.didReset
+	cs.didReset = true
+	cs.cc.mu.Unlock()
+
+	if !didReset {
+		cs.cc.writeStreamReset(cs.ID, http2ErrCodeCancel, nil)
+	}
+}
+
 // checkResetOrDone reports any error sent in a RST_STREAM frame by the
 // server, or errStreamClosed if the stream is complete.
 func (cs *http2clientStream) checkResetOrDone() error {
@@ -5133,14 +5622,18 @@ func (t *http2Transport) RoundTrip(req *Request) (*Response, error) {
 // authorityAddr returns a given authority (a host/IP, or host:port / ip:port)
 // and returns a host:port. The port 443 is added if needed.
 func http2authorityAddr(scheme string, authority string) (addr string) {
-	if _, _, err := net.SplitHostPort(authority); err == nil {
-		return authority
+	host, port, err := net.SplitHostPort(authority)
+	if err != nil {
+		port = "443"
+		if scheme == "http" {
+			port = "80"
+		}
+		host = authority
 	}
-	port := "443"
-	if scheme == "http" {
-		port = "80"
+	if a, err := idna.ToASCII(host); err == nil {
+		host = a
 	}
-	return net.JoinHostPort(authority, port)
+	return net.JoinHostPort(host, port)
 }
 
 // RoundTripOpt is like RoundTrip, but takes options.
@@ -5203,7 +5696,7 @@ func (t *http2Transport) dialClientConn(addr string, singleUse bool) (*http2Clie
 func (t *http2Transport) newTLSConfig(host string) *tls.Config {
 	cfg := new(tls.Config)
 	if t.TLSClientConfig != nil {
-		*cfg = *t.TLSClientConfig
+		*cfg = *http2cloneTLSConfig(t.TLSClientConfig)
 	}
 	if !http2strSliceContains(cfg.NextProtos, http2NextProtoTLS) {
 		cfg.NextProtos = append([]string{http2NextProtoTLS}, cfg.NextProtos...)
@@ -5273,6 +5766,11 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client
 		streams:              make(map[uint32]*http2clientStream),
 		singleUse:            singleUse,
 		wantSettingsAck:      true,
+		pings:                make(map[[8]byte]chan struct{}),
+	}
+	if d := t.idleConnTimeout(); d != 0 {
+		cc.idleTimeout = d
+		cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout)
 	}
 	if http2VerboseLogs {
 		t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr())
@@ -5345,6 +5843,16 @@ func (cc *http2ClientConn) canTakeNewRequestLocked() bool {
 		cc.nextStreamID < math.MaxInt32
 }
 
+// onIdleTimeout is called from a time.AfterFunc goroutine.  It will
+// only be called when we're idle, but because we're coming from a new
+// goroutine, there could be a new request coming in at the same time,
+// so this simply calls the synchronized closeIfIdle to shut down this
+// connection. The timer could just call closeIfIdle, but this is more
+// clear.
+func (cc *http2ClientConn) onIdleTimeout() {
+	cc.closeIfIdle()
+}
+
 func (cc *http2ClientConn) closeIfIdle() {
 	cc.mu.Lock()
 	if len(cc.streams) > 0 {
@@ -5437,13 +5945,13 @@ func (cc *http2ClientConn) responseHeaderTimeout() time.Duration {
 // Certain headers are special-cased as okay but not transmitted later.
 func http2checkConnHeaders(req *Request) error {
 	if v := req.Header.Get("Upgrade"); v != "" {
-		return errors.New("http2: invalid Upgrade request header")
+		return fmt.Errorf("http2: invalid Upgrade request header: %q", req.Header["Upgrade"])
 	}
-	if v := req.Header.Get("Transfer-Encoding"); (v != "" && v != "chunked") || len(req.Header["Transfer-Encoding"]) > 1 {
-		return errors.New("http2: invalid Transfer-Encoding request header")
+	if vv := req.Header["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") {
+		return fmt.Errorf("http2: invalid Transfer-Encoding request header: %q", vv)
 	}
-	if v := req.Header.Get("Connection"); (v != "" && v != "close" && v != "keep-alive") || len(req.Header["Connection"]) > 1 {
-		return errors.New("http2: invalid Connection request header")
+	if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "close" && vv[0] != "keep-alive") {
+		return fmt.Errorf("http2: invalid Connection request header: %q", vv)
 	}
 	return nil
 }
@@ -5465,6 +5973,9 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
 	if err := http2checkConnHeaders(req); err != nil {
 		return nil, err
 	}
+	if cc.idleTimer != nil {
+		cc.idleTimer.Stop()
+	}
 
 	trailers, err := http2commaSeparatedTrailers(req)
 	if err != nil {
@@ -5779,6 +6290,26 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail
 	if host == "" {
 		host = req.URL.Host
 	}
+	host, err := httplex.PunycodeHostPort(host)
+	if err != nil {
+		return nil, err
+	}
+
+	var path string
+	if req.Method != "CONNECT" {
+		path = req.URL.RequestURI()
+		if !http2validPseudoPath(path) {
+			orig := path
+			path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host)
+			if !http2validPseudoPath(path) {
+				if req.URL.Opaque != "" {
+					return nil, fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque)
+				} else {
+					return nil, fmt.Errorf("invalid request :path %q", orig)
+				}
+			}
+		}
+	}
 
 	for k, vv := range req.Header {
 		if !httplex.ValidHeaderFieldName(k) {
@@ -5794,8 +6325,8 @@ func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trail
 	cc.writeHeader(":authority", host)
 	cc.writeHeader(":method", req.Method)
 	if req.Method != "CONNECT" {
-		cc.writeHeader(":path", req.URL.RequestURI())
-		cc.writeHeader(":scheme", "https")
+		cc.writeHeader(":path", path)
+		cc.writeHeader(":scheme", req.URL.Scheme)
 	}
 	if trailers != "" {
 		cc.writeHeader("trailer", trailers)
@@ -5913,6 +6444,9 @@ func (cc *http2ClientConn) streamByID(id uint32, andRemove bool) *http2clientStr
 	if andRemove && cs != nil && !cc.closed {
 		cc.lastActive = time.Now()
 		delete(cc.streams, id)
+		if len(cc.streams) == 0 && cc.idleTimer != nil {
+			cc.idleTimer.Reset(cc.idleTimeout)
+		}
 		close(cs.done)
 		cc.cond.Broadcast()
 	}
@@ -5969,6 +6503,10 @@ func (rl *http2clientConnReadLoop) cleanup() {
 	defer cc.t.connPool().MarkDead(cc)
 	defer close(cc.readerDone)
 
+	if cc.idleTimer != nil {
+		cc.idleTimer.Stop()
+	}
+
 	err := cc.readerErr
 	cc.mu.Lock()
 	if cc.goAway != nil && http2isEOFOrNetReadError(err) {
@@ -6371,9 +6909,10 @@ func (rl *http2clientConnReadLoop) processData(f *http2DataFrame) error {
 			cc.bw.Flush()
 			cc.wmu.Unlock()
 		}
+		didReset := cs.didReset
 		cc.mu.Unlock()
 
-		if len(data) > 0 {
+		if len(data) > 0 && !didReset {
 			if _, err := cs.bufPipe.Write(data); err != nil {
 				rl.endStreamError(cs, err)
 				return err
@@ -6524,9 +7063,56 @@ func (rl *http2clientConnReadLoop) processResetStream(f *http2RSTStreamFrame) er
 	return nil
 }
 
+// Ping sends a PING frame to the server and waits for the ack.
+// Public implementation is in go17.go and not_go17.go
+func (cc *http2ClientConn) ping(ctx http2contextContext) error {
+	c := make(chan struct{})
+	// Generate a random payload
+	var p [8]byte
+	for {
+		if _, err := rand.Read(p[:]); err != nil {
+			return err
+		}
+		cc.mu.Lock()
+
+		if _, found := cc.pings[p]; !found {
+			cc.pings[p] = c
+			cc.mu.Unlock()
+			break
+		}
+		cc.mu.Unlock()
+	}
+	cc.wmu.Lock()
+	if err := cc.fr.WritePing(false, p); err != nil {
+		cc.wmu.Unlock()
+		return err
+	}
+	if err := cc.bw.Flush(); err != nil {
+		cc.wmu.Unlock()
+		return err
+	}
+	cc.wmu.Unlock()
+	select {
+	case <-c:
+		return nil
+	case <-ctx.Done():
+		return ctx.Err()
+	case <-cc.readerDone:
+
+		return cc.readerErr
+	}
+}
+
 func (rl *http2clientConnReadLoop) processPing(f *http2PingFrame) error {
 	if f.IsAck() {
+		cc := rl.cc
+		cc.mu.Lock()
+		defer cc.mu.Unlock()
 
+		if c, ok := cc.pings[f.Data]; ok {
+			close(c)
+			delete(cc.pings, f.Data)
+		}
 		return nil
 	}
 	cc := rl.cc
@@ -6701,6 +7287,11 @@ func http2isConnectionCloseRequest(req *Request) bool {
 // writeFramer is implemented by any type that is used to write frames.
 type http2writeFramer interface {
 	writeFrame(http2writeContext) error
+
+	// staysWithinBuffer reports whether this writer promises that
+	// it will only write less than or equal to size bytes, and it
+	// won't Flush the write context.
+	staysWithinBuffer(size int) bool
 }
 
 // writeContext is the interface needed by the various frame writer
@@ -6743,8 +7334,16 @@ func (http2flushFrameWriter) writeFrame(ctx http2writeContext) error {
 	return ctx.Flush()
 }
 
+func (http2flushFrameWriter) staysWithinBuffer(max int) bool { return false }
+
 type http2writeSettings []http2Setting
 
+func (s http2writeSettings) staysWithinBuffer(max int) bool {
+	const settingSize = 6 // uint16 + uint32
+	return http2frameHeaderLen+settingSize*len(s) <= max
+
+}
+
 func (s http2writeSettings) writeFrame(ctx http2writeContext) error {
 	return ctx.Framer().WriteSettings([]http2Setting(s)...)
 }
@@ -6764,6 +7363,8 @@ func (p *http2writeGoAway) writeFrame(ctx http2writeContext) error {
 	return err
 }
 
+func (*http2writeGoAway) staysWithinBuffer(max int) bool { return false }
+
 type http2writeData struct {
 	streamID  uint32
 	p         []byte
@@ -6778,6 +7379,10 @@ func (w *http2writeData) writeFrame(ctx http2writeContext) error {
 	return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
 }
 
+func (w *http2writeData) staysWithinBuffer(max int) bool {
+	return http2frameHeaderLen+len(w.p) <= max
+}
+
 // handlerPanicRST is the message sent from handler goroutines when
 // the handler panics.
 type http2handlerPanicRST struct {
@@ -6788,22 +7393,59 @@ func (hp http2handlerPanicRST) writeFrame(ctx http2writeContext) error {
 	return ctx.Framer().WriteRSTStream(hp.StreamID, http2ErrCodeInternal)
 }
 
+func (hp http2handlerPanicRST) staysWithinBuffer(max int) bool { return http2frameHeaderLen+4 <= max }
+
 func (se http2StreamError) writeFrame(ctx http2writeContext) error {
 	return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
 }
 
+func (se http2StreamError) staysWithinBuffer(max int) bool { return http2frameHeaderLen+4 <= max }
+
 type http2writePingAck struct{ pf *http2PingFrame }
 
 func (w http2writePingAck) writeFrame(ctx http2writeContext) error {
 	return ctx.Framer().WritePing(true, w.pf.Data)
 }
 
+func (w http2writePingAck) staysWithinBuffer(max int) bool {
+	return http2frameHeaderLen+len(w.pf.Data) <= max
+}
+
 type http2writeSettingsAck struct{}
 
 func (http2writeSettingsAck) writeFrame(ctx http2writeContext) error {
 	return ctx.Framer().WriteSettingsAck()
 }
 
+func (http2writeSettingsAck) staysWithinBuffer(max int) bool { return http2frameHeaderLen <= max }
+
+// splitHeaderBlock splits headerBlock into fragments so that each fragment fits
+// in a single frame, then calls fn for each fragment. firstFrag/lastFrag are true
+// for the first/last fragment, respectively.
+func http2splitHeaderBlock(ctx http2writeContext, headerBlock []byte, fn func(ctx http2writeContext, frag []byte, firstFrag, lastFrag bool) error) error {
+	// For now we're lazy and just pick the minimum MAX_FRAME_SIZE
+	// that all peers must support (16KB). Later we could care
+	// more and send larger frames if the peer advertised it, but
+	// there's little point. Most headers are small anyway (so we
+	// generally won't have CONTINUATION frames), and extra frames
+	// only waste 9 bytes anyway.
+	const maxFrameSize = 16384
+
+	first := true
+	for len(headerBlock) > 0 {
+		frag := headerBlock
+		if len(frag) > maxFrameSize {
+			frag = frag[:maxFrameSize]
+		}
+		headerBlock = headerBlock[len(frag):]
+		if err := fn(ctx, frag, first, len(headerBlock) == 0); err != nil {
+			return err
+		}
+		first = false
+	}
+	return nil
+}
+
 // writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames
 // for HTTP response headers or trailers from a server handler.
 type http2writeResHeaders struct {
@@ -6825,6 +7467,11 @@ func http2encKV(enc *hpack.Encoder, k, v string) {
 	enc.WriteField(hpack.HeaderField{Name: k, Value: v})
 }
 
+func (w *http2writeResHeaders) staysWithinBuffer(max int) bool {
+
+	return false
+}
+
 func (w *http2writeResHeaders) writeFrame(ctx http2writeContext) error {
 	enc, buf := ctx.HeaderEncoder()
 	buf.Reset()
@@ -6850,39 +7497,69 @@ func (w *http2writeResHeaders) writeFrame(ctx http2writeContext) error {
 		panic("unexpected empty hpack")
 	}
 
-	// For now we're lazy and just pick the minimum MAX_FRAME_SIZE
-	// that all peers must support (16KB). Later we could care
-	// more and send larger frames if the peer advertised it, but
-	// there's little point. Most headers are small anyway (so we
-	// generally won't have CONTINUATION frames), and extra frames
-	// only waste 9 bytes anyway.
-	const maxFrameSize = 16384
+	return http2splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
+}
 
-	first := true
-	for len(headerBlock) > 0 {
-		frag := headerBlock
-		if len(frag) > maxFrameSize {
-			frag = frag[:maxFrameSize]
-		}
-		headerBlock = headerBlock[len(frag):]
-		endHeaders := len(headerBlock) == 0
-		var err error
-		if first {
-			first = false
-			err = ctx.Framer().WriteHeaders(http2HeadersFrameParam{
-				StreamID:      w.streamID,
-				BlockFragment: frag,
-				EndStream:     w.endStream,
-				EndHeaders:    endHeaders,
-			})
-		} else {
-			err = ctx.Framer().WriteContinuation(w.streamID, endHeaders, frag)
-		}
-		if err != nil {
-			return err
-		}
+func (w *http2writeResHeaders) writeHeaderBlock(ctx http2writeContext, frag []byte, firstFrag, lastFrag bool) error {
+	if firstFrag {
+		return ctx.Framer().WriteHeaders(http2HeadersFrameParam{
+			StreamID:      w.streamID,
+			BlockFragment: frag,
+			EndStream:     w.endStream,
+			EndHeaders:    lastFrag,
+		})
+	} else {
+		return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
+	}
+}
+
+// writePushPromise is a request to write a PUSH_PROMISE and 0+ CONTINUATION frames.
+type http2writePushPromise struct {
+	streamID uint32   // pusher stream
+	method   string   // for :method
+	url      *url.URL // for :scheme, :authority, :path
+	h        Header
+
+	// Creates an ID for a pushed stream. This runs on serveG just before
+	// the frame is written. The returned ID is copied to promisedID.
+	allocatePromisedID func() (uint32, error)
+	promisedID         uint32
+}
+
+func (w *http2writePushPromise) staysWithinBuffer(max int) bool {
+
+	return false
+}
+
+func (w *http2writePushPromise) writeFrame(ctx http2writeContext) error {
+	enc, buf := ctx.HeaderEncoder()
+	buf.Reset()
+
+	http2encKV(enc, ":method", w.method)
+	http2encKV(enc, ":scheme", w.url.Scheme)
+	http2encKV(enc, ":authority", w.url.Host)
+	http2encKV(enc, ":path", w.url.RequestURI())
+	http2encodeHeaders(enc, w.h, nil)
+
+	headerBlock := buf.Bytes()
+	if len(headerBlock) == 0 {
+		panic("unexpected empty hpack")
+	}
+
+	return http2splitHeaderBlock(ctx, headerBlock, w.writeHeaderBlock)
+}
+
+func (w *http2writePushPromise) writeHeaderBlock(ctx http2writeContext, frag []byte, firstFrag, lastFrag bool) error {
+	if firstFrag {
+		return ctx.Framer().WritePushPromise(http2PushPromiseParam{
+			StreamID:      w.streamID,
+			PromiseID:     w.promisedID,
+			BlockFragment: frag,
+			EndHeaders:    lastFrag,
+		})
+	} else {
+		return ctx.Framer().WriteContinuation(w.streamID, lastFrag, frag)
 	}
-	return nil
 }
 
 type http2write100ContinueHeadersFrame struct {
@@ -6901,15 +7578,24 @@ func (w http2write100ContinueHeadersFrame) writeFrame(ctx http2writeContext) err
 	})
 }
 
+func (w http2write100ContinueHeadersFrame) staysWithinBuffer(max int) bool {
+
+	return 9+2*(len(":status")+len("100")) <= max
+}
+
 type http2writeWindowUpdate struct {
 	streamID uint32 // or 0 for conn-level
 	n        uint32
 }
 
+func (wu http2writeWindowUpdate) staysWithinBuffer(max int) bool { return http2frameHeaderLen+4 <= max }
+
 func (wu http2writeWindowUpdate) writeFrame(ctx http2writeContext) error {
 	return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n)
 }
 
+// encodeHeaders encodes an http.Header. If keys is not nil, then (k, h[k])
+// is encoded only only if k is in keys.
 func http2encodeHeaders(enc *hpack.Encoder, h Header, keys []string) {
 	if keys == nil {
 		sorter := http2sorterPool.Get().(*http2sorter)
@@ -6939,14 +7625,51 @@ func http2encodeHeaders(enc *hpack.Encoder, h Header, keys []string) {
 	}
 }
 
-// frameWriteMsg is a request to write a frame.
-type http2frameWriteMsg struct {
+// WriteScheduler is the interface implemented by HTTP/2 write schedulers.
+// Methods are never called concurrently.
+type http2WriteScheduler interface {
+	// OpenStream opens a new stream in the write scheduler.
+	// It is illegal to call this with streamID=0 or with a streamID that is
+	// already open -- the call may panic.
+	OpenStream(streamID uint32, options http2OpenStreamOptions)
+
+	// CloseStream closes a stream in the write scheduler. Any frames queued on
+	// this stream should be discarded. It is illegal to call this on a stream
+	// that is not open -- the call may panic.
+	CloseStream(streamID uint32)
+
+	// AdjustStream adjusts the priority of the given stream. This may be called
+	// on a stream that has not yet been opened or has been closed. Note that
+	// RFC 7540 allows PRIORITY frames to be sent on streams in any state. See:
+	// https://tools.ietf.org/html/rfc7540#section-5.1
+	AdjustStream(streamID uint32, priority http2PriorityParam)
+
+	// Push queues a frame in the scheduler.
+	Push(wr http2FrameWriteRequest)
+
+	// Pop dequeues the next frame to write. Returns false if no frames can
+	// be written. Frames with a given wr.StreamID() are Pop'd in the same
+	// order they are Push'd.
+	Pop() (wr http2FrameWriteRequest, ok bool)
+}
+
+// OpenStreamOptions specifies extra options for WriteScheduler.OpenStream.
+type http2OpenStreamOptions struct {
+	// PusherID is zero if the stream was initiated by the client. Otherwise,
+	// PusherID names the stream that pushed the newly opened stream.
+	PusherID uint32
+}
+
+// FrameWriteRequest is a request to write a frame.
+type http2FrameWriteRequest struct {
 	// write is the interface value that does the writing, once the
-	// writeScheduler (below) has decided to select this frame
-	// to write. The write functions are all defined in write.go.
+	// WriteScheduler has selected this frame to write. The write
+	// functions are all defined in write.go.
 	write http2writeFramer
 
-	stream *http2stream // used for prioritization. nil for non-stream frames.
+	// stream is the stream on which this frame will be written.
+	// nil for non-stream frames like PING and SETTINGS.
+	stream *http2stream
 
 	// done, if non-nil, must be a buffered channel with space for
 	// 1 message and is sent the return value from write (or an
@@ -6954,247 +7677,626 @@ type http2frameWriteMsg struct {
 	done chan error
 }
 
-// for debugging only:
-func (wm http2frameWriteMsg) String() string {
-	var streamID uint32
-	if wm.stream != nil {
-		streamID = wm.stream.id
+// StreamID returns the id of the stream this frame will be written to.
+// 0 is used for non-stream frames such as PING and SETTINGS.
+func (wr http2FrameWriteRequest) StreamID() uint32 {
+	if wr.stream == nil {
+		if se, ok := wr.write.(http2StreamError); ok {
+
+			return se.StreamID
+		}
+		return 0
+	}
+	return wr.stream.id
+}
+
+// DataSize returns the number of flow control bytes that must be consumed
+// to write this entire frame. This is 0 for non-DATA frames.
+func (wr http2FrameWriteRequest) DataSize() int {
+	if wd, ok := wr.write.(*http2writeData); ok {
+		return len(wd.p)
 	}
+	return 0
+}
+
+// Consume consumes min(n, available) bytes from this frame, where available
+// is the number of flow control bytes available on the stream. Consume returns
+// 0, 1, or 2 frames, where the integer return value gives the number of frames
+// returned.
+//
+// If flow control prevents consuming any bytes, this returns (_, _, 0). If
+// the entire frame was consumed, this returns (wr, _, 1). Otherwise, this
+// returns (consumed, rest, 2), where 'consumed' contains the consumed bytes and
+// 'rest' contains the remaining bytes. The consumed bytes are deducted from the
+// underlying stream's flow control budget.
+func (wr http2FrameWriteRequest) Consume(n int32) (http2FrameWriteRequest, http2FrameWriteRequest, int) {
+	var empty http2FrameWriteRequest
+
+	wd, ok := wr.write.(*http2writeData)
+	if !ok || len(wd.p) == 0 {
+		return wr, empty, 1
+	}
+
+	allowed := wr.stream.flow.available()
+	if n < allowed {
+		allowed = n
+	}
+	if wr.stream.sc.maxFrameSize < allowed {
+		allowed = wr.stream.sc.maxFrameSize
+	}
+	if allowed <= 0 {
+		return empty, empty, 0
+	}
+	if len(wd.p) > int(allowed) {
+		wr.stream.flow.take(allowed)
+		consumed := http2FrameWriteRequest{
+			stream: wr.stream,
+			write: &http2writeData{
+				streamID: wd.streamID,
+				p:        wd.p[:allowed],
+
+				endStream: false,
+			},
+
+			done: nil,
+		}
+		rest := http2FrameWriteRequest{
+			stream: wr.stream,
+			write: &http2writeData{
+				streamID:  wd.streamID,
+				p:         wd.p[allowed:],
+				endStream: wd.endStream,
+			},
+			done: wr.done,
+		}
+		return consumed, rest, 2
+	}
+
+	wr.stream.flow.take(int32(len(wd.p)))
+	return wr, empty, 1
+}
+
+// String is for debugging only.
+func (wr http2FrameWriteRequest) String() string {
 	var des string
-	if s, ok := wm.write.(fmt.Stringer); ok {
+	if s, ok := wr.write.(fmt.Stringer); ok {
 		des = s.String()
 	} else {
-		des = fmt.Sprintf("%T", wm.write)
+		des = fmt.Sprintf("%T", wr.write)
 	}
-	return fmt.Sprintf("[frameWriteMsg stream=%d, ch=%v, type: %v]", streamID, wm.done != nil, des)
+	return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", wr.StreamID(), wr.done != nil, des)
 }
 
-// writeScheduler tracks pending frames to write, priorities, and decides
-// the next one to use. It is not thread-safe.
-type http2writeScheduler struct {
-	// zero are frames not associated with a specific stream.
-	// They're sent before any stream-specific freams.
-	zero http2writeQueue
+// writeQueue is used by implementations of WriteScheduler.
+type http2writeQueue struct {
+	s []http2FrameWriteRequest
+}
 
-	// maxFrameSize is the maximum size of a DATA frame
-	// we'll write. Must be non-zero and between 16K-16M.
-	maxFrameSize uint32
+func (q *http2writeQueue) empty() bool { return len(q.s) == 0 }
 
-	// sq contains the stream-specific queues, keyed by stream ID.
-	// when a stream is idle, it's deleted from the map.
-	sq map[uint32]*http2writeQueue
+func (q *http2writeQueue) push(wr http2FrameWriteRequest) {
+	q.s = append(q.s, wr)
+}
 
-	// canSend is a slice of memory that's reused between frame
-	// scheduling decisions to hold the list of writeQueues (from sq)
-	// which have enough flow control data to send. After canSend is
-	// built, the best is selected.
-	canSend []*http2writeQueue
+func (q *http2writeQueue) shift() http2FrameWriteRequest {
+	if len(q.s) == 0 {
+		panic("invalid use of queue")
+	}
+	wr := q.s[0]
 
-	// pool of empty queues for reuse.
-	queuePool []*http2writeQueue
+	copy(q.s, q.s[1:])
+	q.s[len(q.s)-1] = http2FrameWriteRequest{}
+	q.s = q.s[:len(q.s)-1]
+	return wr
 }
 
-func (ws *http2writeScheduler) putEmptyQueue(q *http2writeQueue) {
-	if len(q.s) != 0 {
-		panic("queue must be empty")
+// consume consumes up to n bytes from q.s[0]. If the frame is
+// entirely consumed, it is removed from the queue. If the frame
+// is partially consumed, the frame is kept with the consumed
+// bytes removed. Returns true iff any bytes were consumed.
+func (q *http2writeQueue) consume(n int32) (http2FrameWriteRequest, bool) {
+	if len(q.s) == 0 {
+		return http2FrameWriteRequest{}, false
 	}
-	ws.queuePool = append(ws.queuePool, q)
+	consumed, rest, numresult := q.s[0].Consume(n)
+	switch numresult {
+	case 0:
+		return http2FrameWriteRequest{}, false
+	case 1:
+		q.shift()
+	case 2:
+		q.s[0] = rest
+	}
+	return consumed, true
 }
 
-func (ws *http2writeScheduler) getEmptyQueue() *http2writeQueue {
-	ln := len(ws.queuePool)
+type http2writeQueuePool []*http2writeQueue
+
+// put inserts an unused writeQueue into the pool.
+func (p *http2writeQueuePool) put(q *http2writeQueue) {
+	for i := range q.s {
+		q.s[i] = http2FrameWriteRequest{}
+	}
+	q.s = q.s[:0]
+	*p = append(*p, q)
+}
+
+// get returns an empty writeQueue.
+func (p *http2writeQueuePool) get() *http2writeQueue {
+	ln := len(*p)
 	if ln == 0 {
 		return new(http2writeQueue)
 	}
-	q := ws.queuePool[ln-1]
-	ws.queuePool = ws.queuePool[:ln-1]
+	x := ln - 1
+	q := (*p)[x]
+	(*p)[x] = nil
+	*p = (*p)[:x]
 	return q
 }
 
-func (ws *http2writeScheduler) empty() bool { return ws.zero.empty() && len(ws.sq) == 0 }
+// RFC 7540, Section 5.3.5: the default weight is 16.
+const http2priorityDefaultWeight = 15 // 16 = 15 + 1
 
-func (ws *http2writeScheduler) add(wm http2frameWriteMsg) {
-	st := wm.stream
-	if st == nil {
-		ws.zero.push(wm)
+// PriorityWriteSchedulerConfig configures a priorityWriteScheduler.
+type http2PriorityWriteSchedulerConfig struct {
+	// MaxClosedNodesInTree controls the maximum number of closed streams to
+	// retain in the priority tree. Setting this to zero saves a small amount
+	// of memory at the cost of performance.
+	//
+	// See RFC 7540, Section 5.3.4:
+	//   "It is possible for a stream to become closed while prioritization
+	//   information ... is in transit. ... This potentially creates suboptimal
+	//   prioritization, since the stream could be given a priority that is
+	//   different from what is intended. To avoid these problems, an endpoint
+	//   SHOULD retain stream prioritization state for a period after streams
+	//   become closed. The longer state is retained, the lower the chance that
+	//   streams are assigned incorrect or default priority values."
+	MaxClosedNodesInTree int
+
+	// MaxIdleNodesInTree controls the maximum number of idle streams to
+	// retain in the priority tree. Setting this to zero saves a small amount
+	// of memory at the cost of performance.
+	//
+	// See RFC 7540, Section 5.3.4:
+	//   Similarly, streams that are in the "idle" state can be assigned
+	//   priority or become a parent of other streams. This allows for the
+	//   creation of a grouping node in the dependency tree, which enables
+	//   more flexible expressions of priority. Idle streams begin with a
+	//   default priority (Section 5.3.5).
+	MaxIdleNodesInTree int
+
+	// ThrottleOutOfOrderWrites enables write throttling to help ensure that
+	// data is delivered in priority order. This works around a race where
+	// stream B depends on stream A and both streams are about to call Write
+	// to queue DATA frames. If B wins the race, a naive scheduler would eagerly
+	// write as much data from B as possible, but this is suboptimal because A
+	// is a higher-priority stream. With throttling enabled, we write a small
+	// amount of data from B to minimize the amount of bandwidth that B can
+	// steal from A.
+	ThrottleOutOfOrderWrites bool
+}
+
+// NewPriorityWriteScheduler constructs a WriteScheduler that schedules
+// frames by following HTTP/2 priorities as described in RFC 7340 Section 5.3.
+// If cfg is nil, default options are used.
+func http2NewPriorityWriteScheduler(cfg *http2PriorityWriteSchedulerConfig) http2WriteScheduler {
+	if cfg == nil {
+
+		cfg = &http2PriorityWriteSchedulerConfig{
+			MaxClosedNodesInTree:     10,
+			MaxIdleNodesInTree:       10,
+			ThrottleOutOfOrderWrites: false,
+		}
+	}
+
+	ws := &http2priorityWriteScheduler{
+		nodes:                make(map[uint32]*http2priorityNode),
+		maxClosedNodesInTree: cfg.MaxClosedNodesInTree,
+		maxIdleNodesInTree:   cfg.MaxIdleNodesInTree,
+		enableWriteThrottle:  cfg.ThrottleOutOfOrderWrites,
+	}
+	ws.nodes[0] = &ws.root
+	if cfg.ThrottleOutOfOrderWrites {
+		ws.writeThrottleLimit = 1024
 	} else {
-		ws.streamQueue(st.id).push(wm)
+		ws.writeThrottleLimit = math.MaxInt32
 	}
+	return ws
+}
+
+type http2priorityNodeState int
+
+const (
+	http2priorityNodeOpen http2priorityNodeState = iota
+	http2priorityNodeClosed
+	http2priorityNodeIdle
+)
+
+// priorityNode is a node in an HTTP/2 priority tree.
+// Each node is associated with a single stream ID.
+// See RFC 7540, Section 5.3.
+type http2priorityNode struct {
+	q            http2writeQueue        // queue of pending frames to write
+	id           uint32                 // id of the stream, or 0 for the root of the tree
+	weight       uint8                  // the actual weight is weight+1, so the value is in [1,256]
+	state        http2priorityNodeState // open | closed | idle
+	bytes        int64                  // number of bytes written by this node, or 0 if closed
+	subtreeBytes int64                  // sum(node.bytes) of all nodes in this subtree
+
+	// These links form the priority tree.
+	parent     *http2priorityNode
+	kids       *http2priorityNode // start of the kids list
+	prev, next *http2priorityNode // doubly-linked list of siblings
 }
 
-func (ws *http2writeScheduler) streamQueue(streamID uint32) *http2writeQueue {
-	if q, ok := ws.sq[streamID]; ok {
-		return q
+func (n *http2priorityNode) setParent(parent *http2priorityNode) {
+	if n == parent {
+		panic("setParent to self")
 	}
-	if ws.sq == nil {
-		ws.sq = make(map[uint32]*http2writeQueue)
+	if n.parent == parent {
+		return
+	}
+
+	if parent := n.parent; parent != nil {
+		if n.prev == nil {
+			parent.kids = n.next
+		} else {
+			n.prev.next = n.next
+		}
+		if n.next != nil {
+			n.next.prev = n.prev
+		}
+	}
+
+	n.parent = parent
+	if parent == nil {
+		n.next = nil
+		n.prev = nil
+	} else {
+		n.next = parent.kids
+		n.prev = nil
+		if n.next != nil {
+			n.next.prev = n
+		}
+		parent.kids = n
 	}
-	q := ws.getEmptyQueue()
-	ws.sq[streamID] = q
-	return q
 }
 
-// take returns the most important frame to write and removes it from the scheduler.
-// It is illegal to call this if the scheduler is empty or if there are no connection-level
-// flow control bytes available.
-func (ws *http2writeScheduler) take() (wm http2frameWriteMsg, ok bool) {
-	if ws.maxFrameSize == 0 {
-		panic("internal error: ws.maxFrameSize not initialized or invalid")
+func (n *http2priorityNode) addBytes(b int64) {
+	n.bytes += b
+	for ; n != nil; n = n.parent {
+		n.subtreeBytes += b
 	}
+}
 
-	if !ws.zero.empty() {
-		return ws.zero.shift(), true
+// walkReadyInOrder iterates over the tree in priority order, calling f for each node
+// with a non-empty write queue. When f returns true, this funcion returns true and the
+// walk halts. tmp is used as scratch space for sorting.
+//
+// f(n, openParent) takes two arguments: the node to visit, n, and a bool that is true
+// if any ancestor p of n is still open (ignoring the root node).
+func (n *http2priorityNode) walkReadyInOrder(openParent bool, tmp *[]*http2priorityNode, f func(*http2priorityNode, bool) bool) bool {
+	if !n.q.empty() && f(n, openParent) {
+		return true
 	}
-	if len(ws.sq) == 0 {
-		return
+	if n.kids == nil {
+		return false
+	}
+
+	if n.id != 0 {
+		openParent = openParent || (n.state == http2priorityNodeOpen)
 	}
 
-	for id, q := range ws.sq {
-		if q.firstIsNoCost() {
-			return ws.takeFrom(id, q)
+	w := n.kids.weight
+	needSort := false
+	for k := n.kids.next; k != nil; k = k.next {
+		if k.weight != w {
+			needSort = true
+			break
 		}
 	}
+	if !needSort {
+		for k := n.kids; k != nil; k = k.next {
+			if k.walkReadyInOrder(openParent, tmp, f) {
+				return true
+			}
+		}
+		return false
+	}
 
-	if len(ws.canSend) != 0 {
-		panic("should be empty")
+	*tmp = (*tmp)[:0]
+	for n.kids != nil {
+		*tmp = append(*tmp, n.kids)
+		n.kids.setParent(nil)
 	}
-	for _, q := range ws.sq {
-		if n := ws.streamWritableBytes(q); n > 0 {
-			ws.canSend = append(ws.canSend, q)
+	sort.Sort(http2sortPriorityNodeSiblings(*tmp))
+	for i := len(*tmp) - 1; i >= 0; i-- {
+		(*tmp)[i].setParent(n)
+	}
+	for k := n.kids; k != nil; k = k.next {
+		if k.walkReadyInOrder(openParent, tmp, f) {
+			return true
 		}
 	}
-	if len(ws.canSend) == 0 {
-		return
+	return false
+}
+
+type http2sortPriorityNodeSiblings []*http2priorityNode
+
+func (z http2sortPriorityNodeSiblings) Len() int { return len(z) }
+
+func (z http2sortPriorityNodeSiblings) Swap(i, k int) { z[i], z[k] = z[k], z[i] }
+
+func (z http2sortPriorityNodeSiblings) Less(i, k int) bool {
+
+	wi, bi := float64(z[i].weight+1), float64(z[i].subtreeBytes)
+	wk, bk := float64(z[k].weight+1), float64(z[k].subtreeBytes)
+	if bi == 0 && bk == 0 {
+		return wi >= wk
+	}
+	if bk == 0 {
+		return false
 	}
-	defer ws.zeroCanSend()
+	return bi/bk <= wi/wk
+}
+
+type http2priorityWriteScheduler struct {
+	// root is the root of the priority tree, where root.id = 0.
+	// The root queues control frames that are not associated with any stream.
+	root http2priorityNode
+
+	// nodes maps stream ids to priority tree nodes.
+	nodes map[uint32]*http2priorityNode
 
-	q := ws.canSend[0]
+	// maxID is the maximum stream id in nodes.
+	maxID uint32
 
-	return ws.takeFrom(q.streamID(), q)
+	// lists of nodes that have been closed or are idle, but are kept in
+	// the tree for improved prioritization. When the lengths exceed either
+	// maxClosedNodesInTree or maxIdleNodesInTree, old nodes are discarded.
+	closedNodes, idleNodes []*http2priorityNode
+
+	// From the config.
+	maxClosedNodesInTree int
+	maxIdleNodesInTree   int
+	writeThrottleLimit   int32
+	enableWriteThrottle  bool
+
+	// tmp is scratch space for priorityNode.walkReadyInOrder to reduce allocations.
+	tmp []*http2priorityNode
+
+	// pool of empty queues for reuse.
+	queuePool http2writeQueuePool
 }
 
-// zeroCanSend is defered from take.
-func (ws *http2writeScheduler) zeroCanSend() {
-	for i := range ws.canSend {
-		ws.canSend[i] = nil
+func (ws *http2priorityWriteScheduler) OpenStream(streamID uint32, options http2OpenStreamOptions) {
+
+	if curr := ws.nodes[streamID]; curr != nil {
+		if curr.state != http2priorityNodeIdle {
+			panic(fmt.Sprintf("stream %d already opened", streamID))
+		}
+		curr.state = http2priorityNodeOpen
+		return
+	}
+
+	parent := ws.nodes[options.PusherID]
+	if parent == nil {
+		parent = &ws.root
+	}
+	n := &http2priorityNode{
+		q:      *ws.queuePool.get(),
+		id:     streamID,
+		weight: http2priorityDefaultWeight,
+		state:  http2priorityNodeOpen,
+	}
+	n.setParent(parent)
+	ws.nodes[streamID] = n
+	if streamID > ws.maxID {
+		ws.maxID = streamID
 	}
-	ws.canSend = ws.canSend[:0]
 }
 
-// streamWritableBytes returns the number of DATA bytes we could write
-// from the given queue's stream, if this stream/queue were
-// selected. It is an error to call this if q's head isn't a
-// *writeData.
-func (ws *http2writeScheduler) streamWritableBytes(q *http2writeQueue) int32 {
-	wm := q.head()
-	ret := wm.stream.flow.available()
-	if ret == 0 {
-		return 0
+func (ws *http2priorityWriteScheduler) CloseStream(streamID uint32) {
+	if streamID == 0 {
+		panic("violation of WriteScheduler interface: cannot close stream 0")
 	}
-	if int32(ws.maxFrameSize) < ret {
-		ret = int32(ws.maxFrameSize)
+	if ws.nodes[streamID] == nil {
+		panic(fmt.Sprintf("violation of WriteScheduler interface: unknown stream %d", streamID))
 	}
-	if ret == 0 {
-		panic("internal error: ws.maxFrameSize not initialized or invalid")
+	if ws.nodes[streamID].state != http2priorityNodeOpen {
+		panic(fmt.Sprintf("violation of WriteScheduler interface: stream %d already closed", streamID))
 	}
-	wd := wm.write.(*http2writeData)
-	if len(wd.p) < int(ret) {
-		ret = int32(len(wd.p))
+
+	n := ws.nodes[streamID]
+	n.state = http2priorityNodeClosed
+	n.addBytes(-n.bytes)
+
+	q := n.q
+	ws.queuePool.put(&q)
+	n.q.s = nil
+	if ws.maxClosedNodesInTree > 0 {
+		ws.addClosedOrIdleNode(&ws.closedNodes, ws.maxClosedNodesInTree, n)
+	} else {
+		ws.removeNode(n)
 	}
-	return ret
 }
 
-func (ws *http2writeScheduler) takeFrom(id uint32, q *http2writeQueue) (wm http2frameWriteMsg, ok bool) {
-	wm = q.head()
-
-	if wd, ok := wm.write.(*http2writeData); ok && len(wd.p) > 0 {
-		allowed := wm.stream.flow.available()
-		if allowed == 0 {
+func (ws *http2priorityWriteScheduler) AdjustStream(streamID uint32, priority http2PriorityParam) {
+	if streamID == 0 {
+		panic("adjustPriority on root")
+	}
 
-			return http2frameWriteMsg{}, false
+	n := ws.nodes[streamID]
+	if n == nil {
+		if streamID <= ws.maxID || ws.maxIdleNodesInTree == 0 {
+			return
 		}
-		if int32(ws.maxFrameSize) < allowed {
-			allowed = int32(ws.maxFrameSize)
+		ws.maxID = streamID
+		n = &http2priorityNode{
+			q:      *ws.queuePool.get(),
+			id:     streamID,
+			weight: http2priorityDefaultWeight,
+			state:  http2priorityNodeIdle,
 		}
+		n.setParent(&ws.root)
+		ws.nodes[streamID] = n
+		ws.addClosedOrIdleNode(&ws.idleNodes, ws.maxIdleNodesInTree, n)
+	}
 
-		if len(wd.p) > int(allowed) {
-			wm.stream.flow.take(allowed)
-			chunk := wd.p[:allowed]
-			wd.p = wd.p[allowed:]
+	parent := ws.nodes[priority.StreamDep]
+	if parent == nil {
+		n.setParent(&ws.root)
+		n.weight = http2priorityDefaultWeight
+		return
+	}
 
-			return http2frameWriteMsg{
-				stream: wm.stream,
-				write: &http2writeData{
-					streamID: wd.streamID,
-					p:        chunk,
+	if n == parent {
+		return
+	}
 
-					endStream: false,
-				},
+	for x := parent.parent; x != nil; x = x.parent {
+		if x == n {
+			parent.setParent(n.parent)
+			break
+		}
+	}
 
-				done: nil,
-			}, true
+	if priority.Exclusive {
+		k := parent.kids
+		for k != nil {
+			next := k.next
+			if k != n {
+				k.setParent(n)
+			}
+			k = next
 		}
-		wm.stream.flow.take(int32(len(wd.p)))
 	}
 
-	q.shift()
-	if q.empty() {
-		ws.putEmptyQueue(q)
-		delete(ws.sq, id)
+	n.setParent(parent)
+	n.weight = priority.Weight
+}
+
+func (ws *http2priorityWriteScheduler) Push(wr http2FrameWriteRequest) {
+	var n *http2priorityNode
+	if id := wr.StreamID(); id == 0 {
+		n = &ws.root
+	} else {
+		n = ws.nodes[id]
+		if n == nil {
+			panic("add on non-open stream")
+		}
 	}
-	return wm, true
+	n.q.push(wr)
 }
 
-func (ws *http2writeScheduler) forgetStream(id uint32) {
-	q, ok := ws.sq[id]
-	if !ok {
+func (ws *http2priorityWriteScheduler) Pop() (wr http2FrameWriteRequest, ok bool) {
+	ws.root.walkReadyInOrder(false, &ws.tmp, func(n *http2priorityNode, openParent bool) bool {
+		limit := int32(math.MaxInt32)
+		if openParent {
+			limit = ws.writeThrottleLimit
+		}
+		wr, ok = n.q.consume(limit)
+		if !ok {
+			return false
+		}
+		n.addBytes(int64(wr.DataSize()))
+
+		if openParent {
+			ws.writeThrottleLimit += 1024
+			if ws.writeThrottleLimit < 0 {
+				ws.writeThrottleLimit = math.MaxInt32
+			}
+		} else if ws.enableWriteThrottle {
+			ws.writeThrottleLimit = 1024
+		}
+		return true
+	})
+	return wr, ok
+}
+
+func (ws *http2priorityWriteScheduler) addClosedOrIdleNode(list *[]*http2priorityNode, maxSize int, n *http2priorityNode) {
+	if maxSize == 0 {
 		return
 	}
-	delete(ws.sq, id)
+	if len(*list) == maxSize {
 
-	for i := range q.s {
-		q.s[i] = http2frameWriteMsg{}
+		ws.removeNode((*list)[0])
+		x := (*list)[1:]
+		copy(*list, x)
+		*list = (*list)[:len(x)]
 	}
-	q.s = q.s[:0]
-	ws.putEmptyQueue(q)
+	*list = append(*list, n)
 }
 
-type http2writeQueue struct {
-	s []http2frameWriteMsg
+func (ws *http2priorityWriteScheduler) removeNode(n *http2priorityNode) {
+	for k := n.kids; k != nil; k = k.next {
+		k.setParent(n.parent)
+	}
+	n.setParent(nil)
+	delete(ws.nodes, n.id)
 }
 
-// streamID returns the stream ID for a non-empty stream-specific queue.
-func (q *http2writeQueue) streamID() uint32 { return q.s[0].stream.id }
+// NewRandomWriteScheduler constructs a WriteScheduler that ignores HTTP/2
+// priorities. Control frames like SETTINGS and PING are written before DATA
+// frames, but if no control frames are queued and multiple streams have queued
+// HEADERS or DATA frames, Pop selects a ready stream arbitrarily.
+func http2NewRandomWriteScheduler() http2WriteScheduler {
+	return &http2randomWriteScheduler{sq: make(map[uint32]*http2writeQueue)}
+}
 
-func (q *http2writeQueue) empty() bool { return len(q.s) == 0 }
+type http2randomWriteScheduler struct {
+	// zero are frames not associated with a specific stream.
+	zero http2writeQueue
+
+	// sq contains the stream-specific queues, keyed by stream ID.
+	// When a stream is idle or closed, it's deleted from the map.
+	sq map[uint32]*http2writeQueue
 
-func (q *http2writeQueue) push(wm http2frameWriteMsg) {
-	q.s = append(q.s, wm)
+	// pool of empty queues for reuse.
+	queuePool http2writeQueuePool
 }
 
-// head returns the next item that would be removed by shift.
-func (q *http2writeQueue) head() http2frameWriteMsg {
-	if len(q.s) == 0 {
-		panic("invalid use of queue")
-	}
-	return q.s[0]
+func (ws *http2randomWriteScheduler) OpenStream(streamID uint32, options http2OpenStreamOptions) {
+
 }
 
-func (q *http2writeQueue) shift() http2frameWriteMsg {
-	if len(q.s) == 0 {
-		panic("invalid use of queue")
+func (ws *http2randomWriteScheduler) CloseStream(streamID uint32) {
+	q, ok := ws.sq[streamID]
+	if !ok {
+		return
 	}
-	wm := q.s[0]
+	delete(ws.sq, streamID)
+	ws.queuePool.put(q)
+}
+
+func (ws *http2randomWriteScheduler) AdjustStream(streamID uint32, priority http2PriorityParam) {
 
-	copy(q.s, q.s[1:])
-	q.s[len(q.s)-1] = http2frameWriteMsg{}
-	q.s = q.s[:len(q.s)-1]
-	return wm
 }
 
-func (q *http2writeQueue) firstIsNoCost() bool {
-	if df, ok := q.s[0].write.(*http2writeData); ok {
-		return len(df.p) == 0
+func (ws *http2randomWriteScheduler) Push(wr http2FrameWriteRequest) {
+	id := wr.StreamID()
+	if id == 0 {
+		ws.zero.push(wr)
+		return
 	}
-	return true
+	q, ok := ws.sq[id]
+	if !ok {
+		q = ws.queuePool.get()
+		ws.sq[id] = q
+	}
+	q.push(wr)
+}
+
+func (ws *http2randomWriteScheduler) Pop() (http2FrameWriteRequest, bool) {
+
+	if !ws.zero.empty() {
+		return ws.zero.shift(), true
+	}
+
+	for _, q := range ws.sq {
+		if wr, ok := q.consume(math.MaxInt32); ok {
+			return wr, true
+		}
+	}
+	return http2FrameWriteRequest{}, false
 }
diff --git a/src/net/http/header.go b/src/net/http/header.go
index 6343165..8321692 100644
--- a/src/net/http/header.go
+++ b/src/net/http/header.go
@@ -32,9 +32,11 @@ func (h Header) Set(key, value string) {
 }
 
 // Get gets the first value associated with the given key.
+// It is case insensitive; textproto.CanonicalMIMEHeaderKey is used
+// to canonicalize the provided key.
 // If there are no values associated with the key, Get returns "".
-// To access multiple values of a key, access the map directly
-// with CanonicalHeaderKey.
+// To access multiple values of a key, or to use non-canonical keys,
+// access the map directly.
 func (h Header) Get(key string) string {
 	return textproto.MIMEHeader(h).Get(key)
 }
diff --git a/src/net/http/http.go b/src/net/http/http.go
index b34ae41..826f7ff 100644
--- a/src/net/http/http.go
+++ b/src/net/http/http.go
@@ -5,7 +5,11 @@
 package http
 
 import (
+	"io"
+	"strconv"
 	"strings"
+	"time"
+	"unicode/utf8"
 
 	"golang_org/x/net/lex/httplex"
 )
@@ -14,6 +18,10 @@ import (
 // Transport's byte-limiting readers.
 const maxInt64 = 1<<63 - 1
 
+// aLongTimeAgo is a non-zero time, far in the past, used for
+// immediate cancelation of network operations.
+var aLongTimeAgo = time.Unix(233431200, 0)
+
 // TODO(bradfitz): move common stuff here. The other files have accumulated
 // generic http stuff in random places.
 
@@ -41,3 +49,93 @@ func removeEmptyPort(host string) string {
 func isNotToken(r rune) bool {
 	return !httplex.IsTokenRune(r)
 }
+
+func isASCII(s string) bool {
+	for i := 0; i < len(s); i++ {
+		if s[i] >= utf8.RuneSelf {
+			return false
+		}
+	}
+	return true
+}
+
+func hexEscapeNonASCII(s string) string {
+	newLen := 0
+	for i := 0; i < len(s); i++ {
+		if s[i] >= utf8.RuneSelf {
+			newLen += 3
+		} else {
+			newLen++
+		}
+	}
+	if newLen == len(s) {
+		return s
+	}
+	b := make([]byte, 0, newLen)
+	for i := 0; i < len(s); i++ {
+		if s[i] >= utf8.RuneSelf {
+			b = append(b, '%')
+			b = strconv.AppendInt(b, int64(s[i]), 16)
+		} else {
+			b = append(b, s[i])
+		}
+	}
+	return string(b)
+}
+
+// NoBody is an io.ReadCloser with no bytes. Read always returns EOF
+// and Close always returns nil. It can be used in an outgoing client
+// request to explicitly signal that a request has zero bytes.
+// An alternative, however, is to simply set Request.Body to nil.
+var NoBody = noBody{}
+
+type noBody struct{}
+
+func (noBody) Read([]byte) (int, error)         { return 0, io.EOF }
+func (noBody) Close() error                     { return nil }
+func (noBody) WriteTo(io.Writer) (int64, error) { return 0, nil }
+
+var (
+	// verify that an io.Copy from NoBody won't require a buffer:
+	_ io.WriterTo   = NoBody
+	_ io.ReadCloser = NoBody
+)
+
+// PushOptions describes options for Pusher.Push.
+type PushOptions struct {
+	// Method specifies the HTTP method for the promised request.
+	// If set, it must be "GET" or "HEAD". Empty means "GET".
+	Method string
+
+	// Header specifies additional promised request headers. This cannot
+	// include HTTP/2 pseudo header fields like ":path" and ":scheme",
+	// which will be added automatically.
+	Header Header
+}
+
+// Pusher is the interface implemented by ResponseWriters that support
+// HTTP/2 server push. For more background, see
+// https://tools.ietf.org/html/rfc7540#section-8.2.
+type Pusher interface {
+	// Push initiates an HTTP/2 server push. This constructs a synthetic
+	// request using the given target and options, serializes that request
+	// into a PUSH_PROMISE frame, then dispatches that request using the
+	// server's request handler. If opts is nil, default options are used.
+	//
+	// The target must either be an absolute path (like "/path") or an absolute
+	// URL that contains a valid host and the same scheme as the parent request.
+	// If the target is a path, it will inherit the scheme and host of the
+	// parent request.
+	//
+	// The HTTP/2 spec disallows recursive pushes and cross-authority pushes.
+	// Push may or may not detect these invalid pushes; however, invalid
+	// pushes will be detected and canceled by conforming clients.
+	//
+	// Handlers that wish to push URL X should call Push before sending any
+	// data that may trigger a request for URL X. This avoids a race where the
+	// client issues requests for X before receiving the PUSH_PROMISE for X.
+	//
+	// Push returns ErrNotSupported if the client has disabled push or if push
+	// is not supported on the underlying connection.
+	Push(target string, opts *PushOptions) error
+}
diff --git a/src/net/http/http_test.go b/src/net/http/http_test.go
index 34da4bb..8f466bb 100644
--- a/src/net/http/http_test.go
+++ b/src/net/http/http_test.go
@@ -12,8 +12,13 @@ import (
 	"os/exec"
 	"reflect"
 	"testing"
+	"time"
 )
 
+func init() {
+	shutdownPollInterval = 5 * time.Millisecond
+}
+
 func TestForeachHeaderElement(t *testing.T) {
 	tests := []struct {
 		in   string
@@ -51,6 +56,18 @@ func TestCleanHost(t *testing.T) {
 		{"www.google.com foo", "www.google.com"},
 		{"www.google.com/foo", "www.google.com"},
 		{" first character is a space", ""},
+		{"[1::6]:8080", "[1::6]:8080"},
+
+		// Punycode:
+		{"гофер.рф/foo", "xn--c1ae0ajs.xn--p1ai"},
+		{"bücher.de", "xn--bcher-kva.de"},
+		{"bücher.de:8080", "xn--bcher-kva.de:8080"},
+		// Verify we convert to lowercase before punycode:
+		{"BÜCHER.de", "xn--bcher-kva.de"},
+		{"BÜCHER.de:8080", "xn--bcher-kva.de:8080"},
+		// Verify we normalize to NFC before punycode:
+		{"gophér.nfc", "xn--gophr-esa.nfc"},            // NFC input; no work needed
+		{"goph\u0065\u0301r.nfd", "xn--gophr-esa.nfd"}, // NFD input
 	}
 	for _, tt := range tests {
 		got := cleanHost(tt.in)
@@ -65,8 +82,9 @@ func TestCleanHost(t *testing.T) {
 // This catches accidental dependencies between the HTTP transport and
 // server code.
 func TestCmdGoNoHTTPServer(t *testing.T) {
+	t.Parallel()
 	goBin := testenv.GoToolPath(t)
-	out, err := exec.Command("go", "tool", "nm", goBin).CombinedOutput()
+	out, err := exec.Command(goBin, "tool", "nm", goBin).CombinedOutput()
 	if err != nil {
 		t.Fatalf("go tool nm: %v: %s", err, out)
 	}
diff --git a/src/net/http/httptest/example_test.go b/src/net/http/httptest/example_test.go
index 124ce75..bd2c496 100644
--- a/src/net/http/httptest/example_test.go
+++ b/src/net/http/httptest/example_test.go
@@ -6,6 +6,7 @@ package httptest_test
 
 import (
 	"fmt"
+	"io"
 	"io/ioutil"
 	"log"
 	"net/http"
@@ -14,15 +15,24 @@ import (
 
 func ExampleResponseRecorder() {
 	handler := func(w http.ResponseWriter, r *http.Request) {
-		http.Error(w, "something failed", http.StatusInternalServerError)
+		io.WriteString(w, "<html><body>Hello World!</body></html>")
 	}
 
 	req := httptest.NewRequest("GET", "http://example.com/foo", nil)
 	w := httptest.NewRecorder()
 	handler(w, req)
 
-	fmt.Printf("%d - %s", w.Code, w.Body.String())
-	// Output: 500 - something failed
+	resp := w.Result()
+	body, _ := ioutil.ReadAll(resp.Body)
+
+	fmt.Println(resp.StatusCode)
+	fmt.Println(resp.Header.Get("Content-Type"))
+	fmt.Println(string(body))
+
+	// Output:
+	// 200
+	// text/html; charset=utf-8
+	// <html><body>Hello World!</body></html>
 }
 
 func ExampleServer() {
diff --git a/src/net/http/httptest/httptest.go b/src/net/http/httptest/httptest.go
index e2148a6..f7202da 100644
--- a/src/net/http/httptest/httptest.go
+++ b/src/net/http/httptest/httptest.go
@@ -35,6 +35,9 @@ import (
 //
 // NewRequest panics on error for ease of use in testing, where a
 // panic is acceptable.
+//
+// To generate a client HTTP request instead of a server request, see
+// the NewRequest function in the net/http package.
 func NewRequest(method, target string, body io.Reader) *http.Request {
 	if method == "" {
 		method = "GET"
diff --git a/src/net/http/httptest/recorder.go b/src/net/http/httptest/recorder.go
index 0ad26a3..5f1aa6a 100644
--- a/src/net/http/httptest/recorder.go
+++ b/src/net/http/httptest/recorder.go
@@ -8,15 +8,33 @@ import (
 	"bytes"
 	"io/ioutil"
 	"net/http"
+	"strconv"
+	"strings"
 )
 
 // ResponseRecorder is an implementation of http.ResponseWriter that
 // records its mutations for later inspection in tests.
 type ResponseRecorder struct {
-	Code      int           // the HTTP response code from WriteHeader
-	HeaderMap http.Header   // the HTTP response headers
-	Body      *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
-	Flushed   bool
+	// Code is the HTTP response code set by WriteHeader.
+	//
+	// Note that if a Handler never calls WriteHeader or Write,
+	// this might end up being 0, rather than the implicit
+	// http.StatusOK. To get the implicit value, use the Result
+	// method.
+	Code int
+
+	// HeaderMap contains the headers explicitly set by the Handler.
+	//
+	// To get the implicit headers set by the server (such as
+	// automatic Content-Type), use the Result method.
+	HeaderMap http.Header
+
+	// Body is the buffer to which the Handler's Write calls are sent.
+	// If nil, the Writes are silently discarded.
+	Body *bytes.Buffer
+
+	// Flushed is whether the Handler called Flush.
+	Flushed bool
 
 	result      *http.Response // cache of Result's return value
 	snapHeader  http.Header    // snapshot of HeaderMap at first Write
@@ -136,6 +154,9 @@ func (rw *ResponseRecorder) Flush() {
 // first write call, or at the time of this call, if the handler never
 // did a write.
 //
+// The Response.Body is guaranteed to be non-nil and Body.Read call is
+// guaranteed to not return any error other than io.EOF.
+//
 // Result must only be called after the handler has finished running.
 func (rw *ResponseRecorder) Result() *http.Response {
 	if rw.result != nil {
@@ -159,6 +180,7 @@ func (rw *ResponseRecorder) Result() *http.Response {
 	if rw.Body != nil {
 		res.Body = ioutil.NopCloser(bytes.NewReader(rw.Body.Bytes()))
 	}
+	res.ContentLength = parseContentLength(res.Header.Get("Content-Length"))
 
 	if trailers, ok := rw.snapHeader["Trailer"]; ok {
 		res.Trailer = make(http.Header, len(trailers))
@@ -181,5 +203,33 @@ func (rw *ResponseRecorder) Result() *http.Response {
 			res.Trailer[k] = vv2
 		}
 	}
+	for k, vv := range rw.HeaderMap {
+		if !strings.HasPrefix(k, http.TrailerPrefix) {
+			continue
+		}
+		if res.Trailer == nil {
+			res.Trailer = make(http.Header)
+		}
+		for _, v := range vv {
+			res.Trailer.Add(strings.TrimPrefix(k, http.TrailerPrefix), v)
+		}
+	}
 	return res
 }
+
+// parseContentLength trims whitespace from s and returns -1 if no value
+// is set, or the value if it's >= 0.
+//
+// This a modified version of same function found in net/http/transfer.go. This
+// one just ignores an invalid header.
+func parseContentLength(cl string) int64 {
+	cl = strings.TrimSpace(cl)
+	if cl == "" {
+		return -1
+	}
+	n, err := strconv.ParseInt(cl, 10, 64)
+	if err != nil {
+		return -1
+	}
+	return n
+}
diff --git a/src/net/http/httptest/recorder_test.go b/src/net/http/httptest/recorder_test.go
index d4e7137..9afba4e 100644
--- a/src/net/http/httptest/recorder_test.go
+++ b/src/net/http/httptest/recorder_test.go
@@ -94,6 +94,14 @@ func TestRecorder(t *testing.T) {
 			return nil
 		}
 	}
+	hasContentLength := func(length int64) checkFunc {
+		return func(rec *ResponseRecorder) error {
+			if got := rec.Result().ContentLength; got != length {
+				return fmt.Errorf("ContentLength = %d; want %d", got, length)
+			}
+			return nil
+		}
+	}
 
 	tests := []struct {
 		name   string
@@ -141,7 +149,7 @@ func TestRecorder(t *testing.T) {
 				w.(http.Flusher).Flush() // also sends a 200
 				w.WriteHeader(201)
 			},
-			check(hasStatus(200), hasFlush(true)),
+			check(hasStatus(200), hasFlush(true), hasContentLength(-1)),
 		},
 		{
 			"Content-Type detection",
@@ -199,6 +207,7 @@ func TestRecorder(t *testing.T) {
 				w.Header().Set("Trailer-A", "valuea")
 				w.Header().Set("Trailer-C", "valuec")
 				w.Header().Set("Trailer-NotDeclared", "should be omitted")
+				w.Header().Set("Trailer:Trailer-D", "with prefix")
 			},
 			check(
 				hasStatus(200),
@@ -208,6 +217,7 @@ func TestRecorder(t *testing.T) {
 				hasTrailer("Trailer-A", "valuea"),
 				hasTrailer("Trailer-C", "valuec"),
 				hasNotTrailers("Non-Trailer", "Trailer-B", "Trailer-NotDeclared"),
+				hasTrailer("Trailer-D", "with prefix"),
 			),
 		},
 		{
@@ -244,6 +254,16 @@ func TestRecorder(t *testing.T) {
 				hasNotHeaders("X-Bar"),
 			),
 		},
+		{
+			"setting Content-Length header",
+			func(w http.ResponseWriter, r *http.Request) {
+				body := "Some body"
+				contentLength := fmt.Sprintf("%d", len(body))
+				w.Header().Set("Content-Length", contentLength)
+				io.WriteString(w, body)
+			},
+			check(hasStatus(200), hasContents("Some body"), hasContentLength(9)),
+		},
 	}
 	r, _ := http.NewRequest("GET", "http://foo.com/", nil)
 	for _, tt := range tests {
diff --git a/src/net/http/httptest/server.go b/src/net/http/httptest/server.go
index 8608077..7118214 100644
--- a/src/net/http/httptest/server.go
+++ b/src/net/http/httptest/server.go
@@ -16,7 +16,6 @@ import (
 	"net/http"
 	"net/http/internal"
 	"os"
-	"runtime"
 	"sync"
 	"time"
 )
@@ -114,9 +113,10 @@ func (s *Server) StartTLS() {
 	}
 
 	existingConfig := s.TLS
-	s.TLS = new(tls.Config)
 	if existingConfig != nil {
-		*s.TLS = *existingConfig
+		s.TLS = existingConfig.Clone()
+	} else {
+		s.TLS = new(tls.Config)
 	}
 	if s.TLS.NextProtos == nil {
 		s.TLS.NextProtos = []string{"http/1.1"}
@@ -293,15 +293,6 @@ func (s *Server) closeConn(c net.Conn) { s.closeConnChan(c, nil) }
 // closeConnChan is like closeConn, but takes an optional channel to receive a value
 // when the goroutine closing c is done.
 func (s *Server) closeConnChan(c net.Conn, done chan<- struct{}) {
-	if runtime.GOOS == "plan9" {
-		// Go's Plan 9 net package isn't great at unblocking reads when
-		// their underlying TCP connections are closed. Don't trust
-		// that that the ConnState state machine will get to
-		// StateClosed. Instead, just go there directly. Plan 9 may leak
-		// resources if the syscall doesn't end up returning. Oh well.
-		s.forgetConn(c)
-	}
-
 	c.Close()
 	if done != nil {
 		done <- struct{}{}
diff --git a/src/net/http/httptrace/example_test.go b/src/net/http/httptrace/example_test.go
new file mode 100644
index 0000000..07fdc0a
--- /dev/null
+++ b/src/net/http/httptrace/example_test.go
@@ -0,0 +1,29 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package httptrace_test
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+	"net/http/httptrace"
+)
+
+func Example() {
+	req, _ := http.NewRequest("GET", "http://example.com", nil)
+	trace := &httptrace.ClientTrace{
+		GotConn: func(connInfo httptrace.GotConnInfo) {
+			fmt.Printf("Got Conn: %+v\n", connInfo)
+		},
+		DNSDone: func(dnsInfo httptrace.DNSDoneInfo) {
+			fmt.Printf("DNS Info: %+v\n", dnsInfo)
+		},
+	}
+	req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
+	_, err := http.DefaultTransport.RoundTrip(req)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/src/net/http/httptrace/trace.go b/src/net/http/httptrace/trace.go
index 6f187a7..3b74179 100644
--- a/src/net/http/httptrace/trace.go
+++ b/src/net/http/httptrace/trace.go
@@ -1,6 +1,6 @@
 // Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.h
+// license that can be found in the LICENSE file.
 
 // Package httptrace provides mechanisms to trace the events within
 // HTTP client requests.
@@ -8,6 +8,7 @@ package httptrace
 
 import (
 	"context"
+	"crypto/tls"
 	"internal/nettrace"
 	"net"
 	"reflect"
@@ -65,11 +66,16 @@ func WithClientTrace(ctx context.Context, trace *ClientTrace) context.Context {
 	return ctx
 }
 
-// ClientTrace is a set of hooks to run at various stages of an HTTP
-// client request. Any particular hook may be nil. Functions may be
-// called concurrently from different goroutines, starting after the
-// call to Transport.RoundTrip and ending either when RoundTrip
-// returns an error, or when the Response.Body is closed.
+// ClientTrace is a set of hooks to run at various stages of an outgoing
+// HTTP request. Any particular hook may be nil. Functions may be
+// called concurrently from different goroutines and some may be called
+// after the request has completed or failed.
+//
+// ClientTrace currently traces a single HTTP request & response
+// during a single round trip and has no hooks that span a series
+// of redirected requests.
+//
+// See https://blog.golang.org/http-tracing for more.
 type ClientTrace struct {
 	// GetConn is called before a connection is created or
 	// retrieved from an idle pool. The hostPort is the
@@ -119,6 +125,16 @@ type ClientTrace struct {
 	// enabled, this may be called multiple times.
 	ConnectDone func(network, addr string, err error)
 
+	// TLSHandshakeStart is called when the TLS handshake is started. When
+	// connecting to a HTTPS site via a HTTP proxy, the handshake happens after
+	// the CONNECT request is processed by the proxy.
+	TLSHandshakeStart func()
+
+	// TLSHandshakeDone is called after the TLS handshake with either the
+	// successful handshake's connection state, or a non-nil error on handshake
+	// failure.
+	TLSHandshakeDone func(tls.ConnectionState, error)
+
 	// WroteHeaders is called after the Transport has written
 	// the request headers.
 	WroteHeaders func()
diff --git a/src/net/http/httptrace/trace_test.go b/src/net/http/httptrace/trace_test.go
index c7eaed8..bb57ada 100644
--- a/src/net/http/httptrace/trace_test.go
+++ b/src/net/http/httptrace/trace_test.go
@@ -1,14 +1,41 @@
 // Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.h
+// license that can be found in the LICENSE file.
 
 package httptrace
 
 import (
 	"bytes"
+	"context"
 	"testing"
 )
 
+func TestWithClientTrace(t *testing.T) {
+	var buf bytes.Buffer
+	connectStart := func(b byte) func(network, addr string) {
+		return func(network, addr string) {
+			buf.WriteByte(b)
+		}
+	}
+
+	ctx := context.Background()
+	oldtrace := &ClientTrace{
+		ConnectStart: connectStart('O'),
+	}
+	ctx = WithClientTrace(ctx, oldtrace)
+	newtrace := &ClientTrace{
+		ConnectStart: connectStart('N'),
+	}
+	ctx = WithClientTrace(ctx, newtrace)
+	trace := ContextClientTrace(ctx)
+
+	buf.Reset()
+	trace.ConnectStart("net", "addr")
+	if got, want := buf.String(), "NO"; got != want {
+		t.Errorf("got %q; want %q", got, want)
+	}
+}
+
 func TestCompose(t *testing.T) {
 	var buf bytes.Buffer
 	var testNum int
diff --git a/src/net/http/httputil/persist.go b/src/net/http/httputil/persist.go
index 87ddd52..cbedf25 100644
--- a/src/net/http/httputil/persist.go
+++ b/src/net/http/httputil/persist.go
@@ -15,9 +15,14 @@ import (
 )
 
 var (
+	// Deprecated: No longer used.
 	ErrPersistEOF = &http.ProtocolError{ErrorString: "persistent connection closed"}
-	ErrClosed     = &http.ProtocolError{ErrorString: "connection closed by user"}
-	ErrPipeline   = &http.ProtocolError{ErrorString: "pipeline error"}
+
+	// Deprecated: No longer used.
+	ErrClosed = &http.ProtocolError{ErrorString: "connection closed by user"}
+
+	// Deprecated: No longer used.
+	ErrPipeline = &http.ProtocolError{ErrorString: "pipeline error"}
 )
 
 // This is an API usage error - the local side is closed.
diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go
index 49c120a..7867505 100644
--- a/src/net/http/httputil/reverseproxy.go
+++ b/src/net/http/httputil/reverseproxy.go
@@ -7,6 +7,7 @@
 package httputil
 
 import (
+	"context"
 	"io"
 	"log"
 	"net"
@@ -51,6 +52,11 @@ type ReverseProxy struct {
 	// get byte slices for use by io.CopyBuffer when
 	// copying HTTP response bodies.
 	BufferPool BufferPool
+
+	// ModifyResponse is an optional function that
+	// modifies the Response from the backend.
+	// If it returns an error, the proxy returns a StatusBadGateway error.
+	ModifyResponse func(*http.Response) error
 }
 
 // A BufferPool is an interface for getting and returning temporary
@@ -120,76 +126,59 @@ var hopHeaders = []string{
 	"Upgrade",
 }
 
-type requestCanceler interface {
-	CancelRequest(*http.Request)
-}
-
-type runOnFirstRead struct {
-	io.Reader // optional; nil means empty body
-
-	fn func() // Run before first Read, then set to nil
-}
-
-func (c *runOnFirstRead) Read(bs []byte) (int, error) {
-	if c.fn != nil {
-		c.fn()
-		c.fn = nil
-	}
-	if c.Reader == nil {
-		return 0, io.EOF
-	}
-	return c.Reader.Read(bs)
-}
-
 func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	transport := p.Transport
 	if transport == nil {
 		transport = http.DefaultTransport
 	}
 
+	ctx := req.Context()
+	if cn, ok := rw.(http.CloseNotifier); ok {
+		var cancel context.CancelFunc
+		ctx, cancel = context.WithCancel(ctx)
+		defer cancel()
+		notifyChan := cn.CloseNotify()
+		go func() {
+			select {
+			case <-notifyChan:
+				cancel()
+			case <-ctx.Done():
+			}
+		}()
+	}
+
 	outreq := new(http.Request)
 	*outreq = *req // includes shallow copies of maps, but okay
-
-	if closeNotifier, ok := rw.(http.CloseNotifier); ok {
-		if requestCanceler, ok := transport.(requestCanceler); ok {
-			reqDone := make(chan struct{})
-			defer close(reqDone)
-
-			clientGone := closeNotifier.CloseNotify()
-
-			outreq.Body = struct {
-				io.Reader
-				io.Closer
-			}{
-				Reader: &runOnFirstRead{
-					Reader: outreq.Body,
-					fn: func() {
-						go func() {
-							select {
-							case <-clientGone:
-								requestCanceler.CancelRequest(outreq)
-							case <-reqDone:
-							}
-						}()
-					},
-				},
-				Closer: outreq.Body,
-			}
-		}
+	if req.ContentLength == 0 {
+		outreq.Body = nil // Issue 16036: nil Body for http.Transport retries
 	}
+	outreq = outreq.WithContext(ctx)
 
 	p.Director(outreq)
-	outreq.Proto = "HTTP/1.1"
-	outreq.ProtoMajor = 1
-	outreq.ProtoMinor = 1
 	outreq.Close = false
 
-	// Remove hop-by-hop headers to the backend. Especially
-	// important is "Connection" because we want a persistent
-	// connection, regardless of what the client sent to us. This
-	// is modifying the same underlying map from req (shallow
+	// We are modifying the same underlying map from req (shallow
 	// copied above) so we only copy it if necessary.
 	copiedHeaders := false
+
+	// Remove hop-by-hop headers listed in the "Connection" header.
+	// See RFC 2616, section 14.10.
+	if c := outreq.Header.Get("Connection"); c != "" {
+		for _, f := range strings.Split(c, ",") {
+			if f = strings.TrimSpace(f); f != "" {
+				if !copiedHeaders {
+					outreq.Header = make(http.Header)
+					copyHeader(outreq.Header, req.Header)
+					copiedHeaders = true
+				}
+				outreq.Header.Del(f)
+			}
+		}
+	}
+
+	// Remove hop-by-hop headers to the backend. Especially
+	// important is "Connection" because we want a persistent
+	// connection, regardless of what the client sent to us.
 	for _, h := range hopHeaders {
 		if outreq.Header.Get(h) != "" {
 			if !copiedHeaders {
@@ -218,16 +207,34 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 		return
 	}
 
+	// Remove hop-by-hop headers listed in the
+	// "Connection" header of the response.
+	if c := res.Header.Get("Connection"); c != "" {
+		for _, f := range strings.Split(c, ",") {
+			if f = strings.TrimSpace(f); f != "" {
+				res.Header.Del(f)
+			}
+		}
+	}
+
 	for _, h := range hopHeaders {
 		res.Header.Del(h)
 	}
 
+	if p.ModifyResponse != nil {
+		if err := p.ModifyResponse(res); err != nil {
+			p.logf("http: proxy error: %v", err)
+			rw.WriteHeader(http.StatusBadGateway)
+			return
+		}
+	}
+
 	copyHeader(rw.Header(), res.Header)
 
 	// The "Trailer" header isn't included in the Transport's response,
 	// at least for *http.Transport. Build it up from Trailer.
 	if len(res.Trailer) > 0 {
-		var trailerKeys []string
+		trailerKeys := make([]string, 0, len(res.Trailer))
 		for k := range res.Trailer {
 			trailerKeys = append(trailerKeys, k)
 		}
@@ -266,12 +273,40 @@ func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
 	if p.BufferPool != nil {
 		buf = p.BufferPool.Get()
 	}
-	io.CopyBuffer(dst, src, buf)
+	p.copyBuffer(dst, src, buf)
 	if p.BufferPool != nil {
 		p.BufferPool.Put(buf)
 	}
 }
 
+func (p *ReverseProxy) copyBuffer(dst io.Writer, src io.Reader, buf []byte) (int64, error) {
+	if len(buf) == 0 {
+		buf = make([]byte, 32*1024)
+	}
+	var written int64
+	for {
+		nr, rerr := src.Read(buf)
+		if rerr != nil && rerr != io.EOF {
+			p.logf("httputil: ReverseProxy read error during body copy: %v", rerr)
+		}
+		if nr > 0 {
+			nw, werr := dst.Write(buf[:nr])
+			if nw > 0 {
+				written += int64(nw)
+			}
+			if werr != nil {
+				return written, werr
+			}
+			if nr != nw {
+				return written, io.ErrShortWrite
+			}
+		}
+		if rerr != nil {
+			return written, rerr
+		}
+	}
+}
+
 func (p *ReverseProxy) logf(format string, args ...interface{}) {
 	if p.ErrorLog != nil {
 		p.ErrorLog.Printf(format, args...)
diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go
index fe7cdb8..20c4e16 100644
--- a/src/net/http/httputil/reverseproxy_test.go
+++ b/src/net/http/httputil/reverseproxy_test.go
@@ -9,6 +9,8 @@ package httputil
 import (
 	"bufio"
 	"bytes"
+	"errors"
+	"fmt"
 	"io"
 	"io/ioutil"
 	"log"
@@ -135,6 +137,61 @@ func TestReverseProxy(t *testing.T) {
 
 }
 
+// Issue 16875: remove any proxied headers mentioned in the "Connection"
+// header value.
+func TestReverseProxyStripHeadersPresentInConnection(t *testing.T) {
+	const fakeConnectionToken = "X-Fake-Connection-Token"
+	const backendResponse = "I am the backend"
+	backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if c := r.Header.Get(fakeConnectionToken); c != "" {
+			t.Errorf("handler got header %q = %q; want empty", fakeConnectionToken, c)
+		}
+		if c := r.Header.Get("Upgrade"); c != "" {
+			t.Errorf("handler got header %q = %q; want empty", "Upgrade", c)
+		}
+		w.Header().Set("Connection", "Upgrade, "+fakeConnectionToken)
+		w.Header().Set("Upgrade", "should be deleted")
+		w.Header().Set(fakeConnectionToken, "should be deleted")
+		io.WriteString(w, backendResponse)
+	}))
+	defer backend.Close()
+	backendURL, err := url.Parse(backend.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	proxyHandler := NewSingleHostReverseProxy(backendURL)
+	frontend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		proxyHandler.ServeHTTP(w, r)
+		if c := r.Header.Get("Upgrade"); c != "original value" {
+			t.Errorf("handler modified header %q = %q; want %q", "Upgrade", c, "original value")
+		}
+	}))
+	defer frontend.Close()
+
+	getReq, _ := http.NewRequest("GET", frontend.URL, nil)
+	getReq.Header.Set("Connection", "Upgrade, "+fakeConnectionToken)
+	getReq.Header.Set("Upgrade", "original value")
+	getReq.Header.Set(fakeConnectionToken, "should be deleted")
+	res, err := http.DefaultClient.Do(getReq)
+	if err != nil {
+		t.Fatalf("Get: %v", err)
+	}
+	defer res.Body.Close()
+	bodyBytes, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		t.Fatalf("reading body: %v", err)
+	}
+	if got, want := string(bodyBytes), backendResponse; got != want {
+		t.Errorf("got body %q; want %q", got, want)
+	}
+	if c := res.Header.Get("Upgrade"); c != "" {
+		t.Errorf("handler got header %q = %q; want empty", "Upgrade", c)
+	}
+	if c := res.Header.Get(fakeConnectionToken); c != "" {
+		t.Errorf("handler got header %q = %q; want empty", fakeConnectionToken, c)
+	}
+}
+
 func TestXForwardedFor(t *testing.T) {
 	const prevForwardedFor = "client ip"
 	const backendResponse = "I am the backend"
@@ -260,14 +317,14 @@ func TestReverseProxyCancelation(t *testing.T) {
 
 	reqInFlight := make(chan struct{})
 	backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		close(reqInFlight)
+		close(reqInFlight) // cause the client to cancel its request
 
 		select {
 		case <-time.After(10 * time.Second):
 			// Note: this should only happen in broken implementations, and the
 			// closenotify case should be instantaneous.
-			t.Log("Failed to close backend connection")
-			t.Fail()
+			t.Error("Handler never saw CloseNotify")
+			return
 		case <-w.(http.CloseNotifier).CloseNotify():
 		}
 
@@ -300,13 +357,13 @@ func TestReverseProxyCancelation(t *testing.T) {
 	}()
 	res, err := http.DefaultClient.Do(getReq)
 	if res != nil {
-		t.Fatal("Non-nil response")
+		t.Errorf("got response %v; want nil", res.Status)
 	}
 	if err == nil {
 		// This should be an error like:
 		// Get http://127.0.0.1:58079: read tcp 127.0.0.1:58079:
 		//    use of closed network connection
-		t.Fatal("DefaultClient.Do() returned nil error")
+		t.Error("DefaultClient.Do() returned nil error; want non-nil error")
 	}
 }
 
@@ -495,3 +552,115 @@ func TestReverseProxy_Post(t *testing.T) {
 		t.Errorf("got body %q; expected %q", g, e)
 	}
 }
+
+type RoundTripperFunc func(*http.Request) (*http.Response, error)
+
+func (fn RoundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
+	return fn(req)
+}
+
+// Issue 16036: send a Request with a nil Body when possible
+func TestReverseProxy_NilBody(t *testing.T) {
+	backendURL, _ := url.Parse("http://fake.tld/")
+	proxyHandler := NewSingleHostReverseProxy(backendURL)
+	proxyHandler.ErrorLog = log.New(ioutil.Discard, "", 0) // quiet for tests
+	proxyHandler.Transport = RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
+		if req.Body != nil {
+			t.Error("Body != nil; want a nil Body")
+		}
+		return nil, errors.New("done testing the interesting part; so force a 502 Gateway error")
+	})
+	frontend := httptest.NewServer(proxyHandler)
+	defer frontend.Close()
+
+	res, err := http.DefaultClient.Get(frontend.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer res.Body.Close()
+	if res.StatusCode != 502 {
+		t.Errorf("status code = %v; want 502 (Gateway Error)", res.Status)
+	}
+}
+
+// Issue 14237. Test ModifyResponse and that an error from it
+// causes the proxy to return StatusBadGateway, or StatusOK otherwise.
+func TestReverseProxyModifyResponse(t *testing.T) {
+	backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("X-Hit-Mod", fmt.Sprintf("%v", r.URL.Path == "/mod"))
+	}))
+	defer backendServer.Close()
+
+	rpURL, _ := url.Parse(backendServer.URL)
+	rproxy := NewSingleHostReverseProxy(rpURL)
+	rproxy.ErrorLog = log.New(ioutil.Discard, "", 0) // quiet for tests
+	rproxy.ModifyResponse = func(resp *http.Response) error {
+		if resp.Header.Get("X-Hit-Mod") != "true" {
+			return fmt.Errorf("tried to by-pass proxy")
+		}
+		return nil
+	}
+
+	frontendProxy := httptest.NewServer(rproxy)
+	defer frontendProxy.Close()
+
+	tests := []struct {
+		url      string
+		wantCode int
+	}{
+		{frontendProxy.URL + "/mod", http.StatusOK},
+		{frontendProxy.URL + "/schedule", http.StatusBadGateway},
+	}
+
+	for i, tt := range tests {
+		resp, err := http.Get(tt.url)
+		if err != nil {
+			t.Fatalf("failed to reach proxy: %v", err)
+		}
+		if g, e := resp.StatusCode, tt.wantCode; g != e {
+			t.Errorf("#%d: got res.StatusCode %d; expected %d", i, g, e)
+		}
+		resp.Body.Close()
+	}
+}
+
+// Issue 16659: log errors from short read
+func TestReverseProxy_CopyBuffer(t *testing.T) {
+	backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		out := "this call was relayed by the reverse proxy"
+		// Coerce a wrong content length to induce io.UnexpectedEOF
+		w.Header().Set("Content-Length", fmt.Sprintf("%d", len(out)*2))
+		fmt.Fprintln(w, out)
+	}))
+	defer backendServer.Close()
+
+	rpURL, err := url.Parse(backendServer.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var proxyLog bytes.Buffer
+	rproxy := NewSingleHostReverseProxy(rpURL)
+	rproxy.ErrorLog = log.New(&proxyLog, "", log.Lshortfile)
+	frontendProxy := httptest.NewServer(rproxy)
+	defer frontendProxy.Close()
+
+	resp, err := http.Get(frontendProxy.URL)
+	if err != nil {
+		t.Fatalf("failed to reach proxy: %v", err)
+	}
+	defer resp.Body.Close()
+
+	if _, err := ioutil.ReadAll(resp.Body); err == nil {
+		t.Fatalf("want non-nil error")
+	}
+	expected := []string{
+		"EOF",
+		"read",
+	}
+	for _, phrase := range expected {
+		if !bytes.Contains(proxyLog.Bytes(), []byte(phrase)) {
+			t.Errorf("expected log to contain phrase %q", phrase)
+		}
+	}
+}
diff --git a/src/net/http/internal/chunked.go b/src/net/http/internal/chunked.go
index 2e62c00..63f321d 100644
--- a/src/net/http/internal/chunked.go
+++ b/src/net/http/internal/chunked.go
@@ -35,10 +35,11 @@ func NewChunkedReader(r io.Reader) io.Reader {
 }
 
 type chunkedReader struct {
-	r   *bufio.Reader
-	n   uint64 // unread bytes in chunk
-	err error
-	buf [2]byte
+	r        *bufio.Reader
+	n        uint64 // unread bytes in chunk
+	err      error
+	buf      [2]byte
+	checkEnd bool // whether need to check for \r\n chunk footer
 }
 
 func (cr *chunkedReader) beginChunk() {
@@ -68,6 +69,21 @@ func (cr *chunkedReader) chunkHeaderAvailable() bool {
 
 func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
 	for cr.err == nil {
+		if cr.checkEnd {
+			if n > 0 && cr.r.Buffered() < 2 {
+				// We have some data. Return early (per the io.Reader
+				// contract) instead of potentially blocking while
+				// reading more.
+				break
+			}
+			if _, cr.err = io.ReadFull(cr.r, cr.buf[:2]); cr.err == nil {
+				if string(cr.buf[:]) != "\r\n" {
+					cr.err = errors.New("malformed chunked encoding")
+					break
+				}
+			}
+			cr.checkEnd = false
+		}
 		if cr.n == 0 {
 			if n > 0 && !cr.chunkHeaderAvailable() {
 				// We've read enough. Don't potentially block
@@ -92,11 +108,7 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
 		// If we're at the end of a chunk, read the next two
 		// bytes to verify they are "\r\n".
 		if cr.n == 0 && cr.err == nil {
-			if _, cr.err = io.ReadFull(cr.r, cr.buf[:2]); cr.err == nil {
-				if cr.buf[0] != '\r' || cr.buf[1] != '\n' {
-					cr.err = errors.New("malformed chunked encoding")
-				}
-			}
+			cr.checkEnd = true
 		}
 	}
 	return n, cr.err
diff --git a/src/net/http/internal/chunked_test.go b/src/net/http/internal/chunked_test.go
index 9abe1ab..d067165 100644
--- a/src/net/http/internal/chunked_test.go
+++ b/src/net/http/internal/chunked_test.go
@@ -185,3 +185,30 @@ func TestChunkReadingIgnoresExtensions(t *testing.T) {
 		t.Errorf("read %q; want %q", g, e)
 	}
 }
+
+// Issue 17355: ChunkedReader shouldn't block waiting for more data
+// if it can return something.
+func TestChunkReadPartial(t *testing.T) {
+	pr, pw := io.Pipe()
+	go func() {
+		pw.Write([]byte("7\r\n1234567"))
+	}()
+	cr := NewChunkedReader(pr)
+	readBuf := make([]byte, 7)
+	n, err := cr.Read(readBuf)
+	if err != nil {
+		t.Fatal(err)
+	}
+	want := "1234567"
+	if n != 7 || string(readBuf) != want {
+		t.Fatalf("Read: %v %q; want %d, %q", n, readBuf[:n], len(want), want)
+	}
+	go func() {
+		pw.Write([]byte("xx"))
+	}()
+	_, err = cr.Read(readBuf)
+	if got := fmt.Sprint(err); !strings.Contains(got, "malformed") {
+		t.Fatalf("second read = %v; want malformed error", err)
+	}
+
+}
diff --git a/src/net/http/main_test.go b/src/net/http/main_test.go
index aea6e12..438bd2e 100644
--- a/src/net/http/main_test.go
+++ b/src/net/http/main_test.go
@@ -6,6 +6,8 @@ package http_test
 
 import (
 	"fmt"
+	"io/ioutil"
+	"log"
 	"net/http"
 	"os"
 	"runtime"
@@ -15,6 +17,8 @@ import (
 	"time"
 )
 
+var quietLog = log.New(ioutil.Discard, "", 0)
+
 func TestMain(m *testing.M) {
 	v := m.Run()
 	if v == 0 && goroutineLeaked() {
@@ -134,3 +138,20 @@ func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
 	}
 	return false
 }
+
+// waitErrCondition is like waitCondition but with errors instead of bools.
+func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error {
+	deadline := time.Now().Add(waitFor)
+	var err error
+	for time.Now().Before(deadline) {
+		if err = fn(); err == nil {
+			return nil
+		}
+		time.Sleep(checkEvery)
+	}
+	return err
+}
+
+func closeClient(c *http.Client) {
+	c.Transport.(*http.Transport).CloseIdleConnections()
+}
diff --git a/src/net/http/npn_test.go b/src/net/http/npn_test.go
index e2e911d..4c1f6b5 100644
--- a/src/net/http/npn_test.go
+++ b/src/net/http/npn_test.go
@@ -18,6 +18,7 @@ import (
 )
 
 func TestNextProtoUpgrade(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		fmt.Fprintf(w, "path=%s,proto=", r.URL.Path)
diff --git a/src/net/http/range_test.go b/src/net/http/range_test.go
index ef911af..114987e 100644
--- a/src/net/http/range_test.go
+++ b/src/net/http/range_test.go
@@ -38,7 +38,7 @@ var ParseRangeTests = []struct {
 	{"bytes=0-", 10, []httpRange{{0, 10}}},
 	{"bytes=5-", 10, []httpRange{{5, 5}}},
 	{"bytes=0-20", 10, []httpRange{{0, 10}}},
-	{"bytes=15-,0-5", 10, nil},
+	{"bytes=15-,0-5", 10, []httpRange{{0, 6}}},
 	{"bytes=1-2,5-", 10, []httpRange{{1, 2}, {5, 5}}},
 	{"bytes=-2 , 7-", 11, []httpRange{{9, 2}, {7, 4}}},
 	{"bytes=0-0 ,2-2, 7-", 11, []httpRange{{0, 1}, {2, 1}, {7, 4}}},
diff --git a/src/net/http/readrequest_test.go b/src/net/http/readrequest_test.go
index 4bf646b..28a148b 100644
--- a/src/net/http/readrequest_test.go
+++ b/src/net/http/readrequest_test.go
@@ -25,7 +25,7 @@ type reqTest struct {
 }
 
 var noError = ""
-var noBody = ""
+var noBodyStr = ""
 var noTrailer Header = nil
 
 var reqTests = []reqTest{
@@ -95,7 +95,7 @@ var reqTests = []reqTest{
 			RequestURI:    "/",
 		},
 
-		noBody,
+		noBodyStr,
 		noTrailer,
 		noError,
 	},
@@ -121,7 +121,7 @@ var reqTests = []reqTest{
 			RequestURI:    "//user at host/is/actually/a/path/",
 		},
 
-		noBody,
+		noBodyStr,
 		noTrailer,
 		noError,
 	},
@@ -131,7 +131,7 @@ var reqTests = []reqTest{
 		"GET ../../../../etc/passwd HTTP/1.1\r\n" +
 			"Host: test\r\n\r\n",
 		nil,
-		noBody,
+		noBodyStr,
 		noTrailer,
 		"parse ../../../../etc/passwd: invalid URI for request",
 	},
@@ -141,7 +141,7 @@ var reqTests = []reqTest{
 		"GET  HTTP/1.1\r\n" +
 			"Host: test\r\n\r\n",
 		nil,
-		noBody,
+		noBodyStr,
 		noTrailer,
 		"parse : empty url",
 	},
@@ -227,7 +227,7 @@ var reqTests = []reqTest{
 			RequestURI:    "www.google.com:443",
 		},
 
-		noBody,
+		noBodyStr,
 		noTrailer,
 		noError,
 	},
@@ -251,7 +251,7 @@ var reqTests = []reqTest{
 			RequestURI:    "127.0.0.1:6060",
 		},
 
-		noBody,
+		noBodyStr,
 		noTrailer,
 		noError,
 	},
@@ -275,7 +275,7 @@ var reqTests = []reqTest{
 			RequestURI:    "/_goRPC_",
 		},
 
-		noBody,
+		noBodyStr,
 		noTrailer,
 		noError,
 	},
@@ -299,7 +299,7 @@ var reqTests = []reqTest{
 			RequestURI:    "*",
 		},
 
-		noBody,
+		noBodyStr,
 		noTrailer,
 		noError,
 	},
@@ -323,7 +323,7 @@ var reqTests = []reqTest{
 			RequestURI:    "*",
 		},
 
-		noBody,
+		noBodyStr,
 		noTrailer,
 		noError,
 	},
@@ -350,7 +350,7 @@ var reqTests = []reqTest{
 			RequestURI: "/",
 		},
 
-		noBody,
+		noBodyStr,
 		noTrailer,
 		noError,
 	},
@@ -376,7 +376,7 @@ var reqTests = []reqTest{
 			RequestURI: "/",
 		},
 
-		noBody,
+		noBodyStr,
 		noTrailer,
 		noError,
 	},
@@ -397,7 +397,7 @@ var reqTests = []reqTest{
 			ContentLength: -1,
 			Close:         true,
 		},
-		noBody,
+		noBodyStr,
 		noTrailer,
 		noError,
 	},
diff --git a/src/net/http/request.go b/src/net/http/request.go
index dc55592..fd9ea54 100644
--- a/src/net/http/request.go
+++ b/src/net/http/request.go
@@ -18,12 +18,17 @@ import (
 	"io/ioutil"
 	"mime"
 	"mime/multipart"
+	"net"
 	"net/http/httptrace"
 	"net/textproto"
 	"net/url"
 	"strconv"
 	"strings"
 	"sync"
+
+	"golang_org/x/net/idna"
+	"golang_org/x/text/unicode/norm"
+	"golang_org/x/text/width"
 )
 
 const (
@@ -34,21 +39,40 @@ const (
 // is either not present in the request or not a file field.
 var ErrMissingFile = errors.New("http: no such file")
 
-// HTTP request parsing errors.
+// ProtocolError represents an HTTP protocol error.
+//
+// Deprecated: Not all errors in the http package related to protocol errors
+// are of type ProtocolError.
 type ProtocolError struct {
 	ErrorString string
 }
 
-func (err *ProtocolError) Error() string { return err.ErrorString }
+func (pe *ProtocolError) Error() string { return pe.ErrorString }
 
 var (
-	ErrHeaderTooLong        = &ProtocolError{"header too long"}
-	ErrShortBody            = &ProtocolError{"entity body too short"}
-	ErrNotSupported         = &ProtocolError{"feature not supported"}
-	ErrUnexpectedTrailer    = &ProtocolError{"trailer header without chunked transfer encoding"}
+	// ErrNotSupported is returned by the Push method of Pusher
+	// implementations to indicate that HTTP/2 Push support is not
+	// available.
+	ErrNotSupported = &ProtocolError{"feature not supported"}
+
+	// ErrUnexpectedTrailer is returned by the Transport when a server
+	// replies with a Trailer header, but without a chunked reply.
+	ErrUnexpectedTrailer = &ProtocolError{"trailer header without chunked transfer encoding"}
+
+	// ErrMissingBoundary is returned by Request.MultipartReader when the
+	// request's Content-Type does not include a "boundary" parameter.
+	ErrMissingBoundary = &ProtocolError{"no multipart boundary param in Content-Type"}
+
+	// ErrNotMultipart is returned by Request.MultipartReader when the
+	// request's Content-Type is not multipart/form-data.
+	ErrNotMultipart = &ProtocolError{"request Content-Type isn't multipart/form-data"}
+
+	// Deprecated: ErrHeaderTooLong is not used.
+	ErrHeaderTooLong = &ProtocolError{"header too long"}
+	// Deprecated: ErrShortBody is not used.
+	ErrShortBody = &ProtocolError{"entity body too short"}
+	// Deprecated: ErrMissingContentLength is not used.
 	ErrMissingContentLength = &ProtocolError{"missing ContentLength in HEAD response"}
-	ErrNotMultipart         = &ProtocolError{"request Content-Type isn't multipart/form-data"}
-	ErrMissingBoundary      = &ProtocolError{"no multipart boundary param in Content-Type"}
 )
 
 type badStringError struct {
@@ -146,11 +170,20 @@ type Request struct {
 	// Handler does not need to.
 	Body io.ReadCloser
 
+	// GetBody defines an optional func to return a new copy of
+	// Body. It used for client requests when a redirect requires
+	// reading the body more than once. Use of GetBody still
+	// requires setting Body.
+	//
+	// For server requests it is unused.
+	GetBody func() (io.ReadCloser, error)
+
 	// ContentLength records the length of the associated content.
 	// The value -1 indicates that the length is unknown.
 	// Values >= 0 indicate that the given number of bytes may
 	// be read from Body.
-	// For client requests, a value of 0 means unknown if Body is not nil.
+	// For client requests, a value of 0 with a non-nil Body is
+	// also treated as unknown.
 	ContentLength int64
 
 	// TransferEncoding lists the transfer encodings from outermost to
@@ -175,11 +208,15 @@ type Request struct {
 	// For server requests Host specifies the host on which the
 	// URL is sought. Per RFC 2616, this is either the value of
 	// the "Host" header or the host name given in the URL itself.
-	// It may be of the form "host:port".
+	// It may be of the form "host:port". For international domain
+	// names, Host may be in Punycode or Unicode form. Use
+	// golang.org/x/net/idna to convert it to either format if
+	// needed.
 	//
 	// For client requests Host optionally overrides the Host
 	// header to send. If empty, the Request.Write method uses
-	// the value of URL.Host.
+	// the value of URL.Host. Host may contain an international
+	// domain name.
 	Host string
 
 	// Form contains the parsed form data, including both the URL
@@ -319,6 +356,8 @@ var ErrNoCookie = errors.New("http: named cookie not present")
 
 // Cookie returns the named cookie provided in the request or
 // ErrNoCookie if not found.
+// If multiple cookies match the given name, only one cookie will
+// be returned.
 func (r *Request) Cookie(name string) (*Cookie, error) {
 	for _, c := range readCookies(r.Header, name) {
 		return c, nil
@@ -573,7 +612,24 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, wai
 	return nil
 }
 
-// cleanHost strips anything after '/' or ' '.
+func idnaASCII(v string) (string, error) {
+	if isASCII(v) {
+		return v, nil
+	}
+	// The idna package doesn't do everything from
+	// https://tools.ietf.org/html/rfc5895 so we do it here.
+	// TODO(bradfitz): should the idna package do this instead?
+	v = strings.ToLower(v)
+	v = width.Fold.String(v)
+	v = norm.NFC.String(v)
+	return idna.ToASCII(v)
+}
+
+// cleanHost cleans up the host sent in request's Host header.
+//
+// It both strips anything after '/' or ' ', and puts the value
+// into Punycode form, if necessary.
+//
 // Ideally we'd clean the Host header according to the spec:
 //   https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]")
 //   https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host)
@@ -584,9 +640,21 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, wai
 // first offending character.
 func cleanHost(in string) string {
 	if i := strings.IndexAny(in, " /"); i != -1 {
-		return in[:i]
+		in = in[:i]
+	}
+	host, port, err := net.SplitHostPort(in)
+	if err != nil { // input was just a host
+		a, err := idnaASCII(in)
+		if err != nil {
+			return in // garbage in, garbage out
+		}
+		return a
 	}
-	return in
+	a, err := idnaASCII(host)
+	if err != nil {
+		return in // garbage in, garbage out
+	}
+	return net.JoinHostPort(a, port)
 }
 
 // removeZone removes IPv6 zone identifier from host.
@@ -658,11 +726,11 @@ func validMethod(method string) bool {
 // methods Do, Post, and PostForm, and Transport.RoundTrip.
 //
 // NewRequest returns a Request suitable for use with Client.Do or
-// Transport.RoundTrip.
-// To create a request for use with testing a Server Handler use either
-// ReadRequest or manually update the Request fields. See the Request
-// type's documentation for the difference between inbound and outbound
-// request fields.
+// Transport.RoundTrip. To create a request for use with testing a
+// Server Handler, either use the NewRequest function in the
+// net/http/httptest package, use ReadRequest, or manually update the
+// Request fields. See the Request type's documentation for the
+// difference between inbound and outbound request fields.
 func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
 	if method == "" {
 		// We document that "" means "GET" for Request.Method, and people have
@@ -697,10 +765,39 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) {
 		switch v := body.(type) {
 		case *bytes.Buffer:
 			req.ContentLength = int64(v.Len())
+			buf := v.Bytes()
+			req.GetBody = func() (io.ReadCloser, error) {
+				r := bytes.NewReader(buf)
+				return ioutil.NopCloser(r), nil
+			}
 		case *bytes.Reader:
 			req.ContentLength = int64(v.Len())
+			snapshot := *v
+			req.GetBody = func() (io.ReadCloser, error) {
+				r := snapshot
+				return ioutil.NopCloser(&r), nil
+			}
 		case *strings.Reader:
 			req.ContentLength = int64(v.Len())
+			snapshot := *v
+			req.GetBody = func() (io.ReadCloser, error) {
+				r := snapshot
+				return ioutil.NopCloser(&r), nil
+			}
+		default:
+			req.ContentLength = -1 // unknown
+		}
+		// For client requests, Request.ContentLength of 0
+		// means either actually 0, or unknown. The only way
+		// to explicitly say that the ContentLength is zero is
+		// to set the Body to nil. But turns out too much code
+		// depends on NewRequest returning a non-nil Body,
+		// so we use a well-known ReadCloser variable instead
+		// and have the http package also treat that sentinel
+		// variable to mean explicitly zero.
+		if req.ContentLength == 0 {
+			req.Body = NoBody
+			req.GetBody = func() (io.ReadCloser, error) { return NoBody, nil }
 		}
 	}
 
@@ -1000,18 +1097,24 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
 	return
 }
 
-// ParseForm parses the raw query from the URL and updates r.Form.
+// ParseForm populates r.Form and r.PostForm.
+//
+// For all requests, ParseForm parses the raw query from the URL and updates
+// r.Form.
 //
-// For POST or PUT requests, it also parses the request body as a form and
-// put the results into both r.PostForm and r.Form.
-// POST and PUT body parameters take precedence over URL query string values
-// in r.Form.
+// For POST, PUT, and PATCH requests, it also parses the request body as a form
+// and puts the results into both r.PostForm and r.Form. Request body parameters
+// take precedence over URL query string values in r.Form.
+//
+// For other HTTP methods, or when the Content-Type is not
+// application/x-www-form-urlencoded, the request Body is not read, and
+// r.PostForm is initialized to a non-nil, empty value.
 //
 // If the request Body's size has not already been limited by MaxBytesReader,
 // the size is capped at 10MB.
 //
 // ParseMultipartForm calls ParseForm automatically.
-// It is idempotent.
+// ParseForm is idempotent.
 func (r *Request) ParseForm() error {
 	var err error
 	if r.PostForm == nil {
@@ -1174,3 +1277,15 @@ func (r *Request) isReplayable() bool {
 	}
 	return false
 }
+
+// outgoingLength reports the Content-Length of this outgoing (Client) request.
+// It maps 0 into -1 (unknown) when the Body is non-nil.
+func (r *Request) outgoingLength() int64 {
+	if r.Body == nil || r.Body == NoBody {
+		return 0
+	}
+	if r.ContentLength != 0 {
+		return r.ContentLength
+	}
+	return -1
+}
diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
index a4c88c0..3c965c1 100644
--- a/src/net/http/request_test.go
+++ b/src/net/http/request_test.go
@@ -29,9 +29,9 @@ func TestQuery(t *testing.T) {
 	}
 }
 
-func TestPostQuery(t *testing.T) {
-	req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not",
-		strings.NewReader("z=post&both=y&prio=2&empty="))
+func TestParseFormQuery(t *testing.T) {
+	req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&orphan=nope&empty=not",
+		strings.NewReader("z=post&both=y&prio=2&=nokey&orphan;empty=&"))
 	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
 
 	if q := req.FormValue("q"); q != "foo" {
@@ -55,39 +55,30 @@ func TestPostQuery(t *testing.T) {
 	if prio := req.FormValue("prio"); prio != "2" {
 		t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio)
 	}
-	if empty := req.FormValue("empty"); empty != "" {
+	if orphan := req.Form["orphan"]; !reflect.DeepEqual(orphan, []string{"", "nope"}) {
+		t.Errorf(`req.FormValue("orphan") = %q, want "" (from body)`, orphan)
+	}
+	if empty := req.Form["empty"]; !reflect.DeepEqual(empty, []string{"", "not"}) {
 		t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty)
 	}
+	if nokey := req.Form[""]; !reflect.DeepEqual(nokey, []string{"nokey"}) {
+		t.Errorf(`req.FormValue("nokey") = %q, want "nokey" (from body)`, nokey)
+	}
 }
 
-func TestPatchQuery(t *testing.T) {
-	req, _ := NewRequest("PATCH", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not",
-		strings.NewReader("z=post&both=y&prio=2&empty="))
-	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
-
-	if q := req.FormValue("q"); q != "foo" {
-		t.Errorf(`req.FormValue("q") = %q, want "foo"`, q)
-	}
-	if z := req.FormValue("z"); z != "post" {
-		t.Errorf(`req.FormValue("z") = %q, want "post"`, z)
-	}
-	if bq, found := req.PostForm["q"]; found {
-		t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq)
-	}
-	if bz := req.PostFormValue("z"); bz != "post" {
-		t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz)
-	}
-	if qs := req.Form["q"]; !reflect.DeepEqual(qs, []string{"foo", "bar"}) {
-		t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs)
-	}
-	if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"y", "x"}) {
-		t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both)
-	}
-	if prio := req.FormValue("prio"); prio != "2" {
-		t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio)
-	}
-	if empty := req.FormValue("empty"); empty != "" {
-		t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty)
+// Tests that we only parse the form automatically for certain methods.
+func TestParseFormQueryMethods(t *testing.T) {
+	for _, method := range []string{"POST", "PATCH", "PUT", "FOO"} {
+		req, _ := NewRequest(method, "http://www.google.com/search",
+			strings.NewReader("foo=bar"))
+		req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
+		want := "bar"
+		if method == "FOO" {
+			want = ""
+		}
+		if got := req.FormValue("foo"); got != want {
+			t.Errorf(`for method %s, FormValue("foo") = %q; want %q`, method, got, want)
+		}
 	}
 }
 
@@ -374,18 +365,68 @@ func TestFormFileOrder(t *testing.T) {
 
 var readRequestErrorTests = []struct {
 	in  string
-	err error
+	err string
+
+	header Header
 }{
-	{"GET / HTTP/1.1\r\nheader:foo\r\n\r\n", nil},
-	{"GET / HTTP/1.1\r\nheader:foo\r\n", io.ErrUnexpectedEOF},
-	{"", io.EOF},
+	0: {"GET / HTTP/1.1\r\nheader:foo\r\n\r\n", "", Header{"Header": {"foo"}}},
+	1: {"GET / HTTP/1.1\r\nheader:foo\r\n", io.ErrUnexpectedEOF.Error(), nil},
+	2: {"", io.EOF.Error(), nil},
+	3: {
+		in:  "HEAD / HTTP/1.1\r\nContent-Length:4\r\n\r\n",
+		err: "http: method cannot contain a Content-Length",
+	},
+	4: {
+		in:     "HEAD / HTTP/1.1\r\n\r\n",
+		header: Header{},
+	},
+
+	// Multiple Content-Length values should either be
+	// deduplicated if same or reject otherwise
+	// See Issue 16490.
+	5: {
+		in:  "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 0\r\n\r\nGopher hey\r\n",
+		err: "cannot contain multiple Content-Length headers",
+	},
+	6: {
+		in:  "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 6\r\n\r\nGopher\r\n",
+		err: "cannot contain multiple Content-Length headers",
+	},
+	7: {
+		in:     "PUT / HTTP/1.1\r\nContent-Length: 6 \r\nContent-Length: 6\r\nContent-Length:6\r\n\r\nGopher\r\n",
+		err:    "",
+		header: Header{"Content-Length": {"6"}},
+	},
+	8: {
+		in:  "PUT / HTTP/1.1\r\nContent-Length: 1\r\nContent-Length: 6 \r\n\r\n",
+		err: "cannot contain multiple Content-Length headers",
+	},
+	9: {
+		in:  "POST / HTTP/1.1\r\nContent-Length:\r\nContent-Length: 3\r\n\r\n",
+		err: "cannot contain multiple Content-Length headers",
+	},
+	10: {
+		in:     "HEAD / HTTP/1.1\r\nContent-Length:0\r\nContent-Length: 0\r\n\r\n",
+		header: Header{"Content-Length": {"0"}},
+	},
 }
 
 func TestReadRequestErrors(t *testing.T) {
 	for i, tt := range readRequestErrorTests {
-		_, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.in)))
-		if err != tt.err {
-			t.Errorf("%d. got error = %v; want %v", i, err, tt.err)
+		req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.in)))
+		if err == nil {
+			if tt.err != "" {
+				t.Errorf("#%d: got nil err; want %q", i, tt.err)
+			}
+
+			if !reflect.DeepEqual(tt.header, req.Header) {
+				t.Errorf("#%d: gotHeader: %q wantHeader: %q", i, req.Header, tt.header)
+			}
+			continue
+		}
+
+		if tt.err == "" || !strings.Contains(err.Error(), tt.err) {
+			t.Errorf("%d: got error = %v; want %v", i, err, tt.err)
 		}
 	}
 }
@@ -456,18 +497,22 @@ func TestNewRequestContentLength(t *testing.T) {
 		{bytes.NewReader([]byte("123")), 3},
 		{bytes.NewBuffer([]byte("1234")), 4},
 		{strings.NewReader("12345"), 5},
+		{strings.NewReader(""), 0},
 		// Not detected:
-		{struct{ io.Reader }{strings.NewReader("xyz")}, 0},
-		{io.NewSectionReader(strings.NewReader("x"), 0, 6), 0},
-		{readByte(io.NewSectionReader(strings.NewReader("xy"), 0, 6)), 0},
+		{struct{ io.Reader }{strings.NewReader("xyz")}, -1},
+		{io.NewSectionReader(strings.NewReader("x"), 0, 6), -1},
+		{readByte(io.NewSectionReader(strings.NewReader("xy"), 0, 6)), -1},
 	}
-	for _, tt := range tests {
+	for i, tt := range tests {
 		req, err := NewRequest("POST", "http://localhost/", tt.r)
 		if err != nil {
 			t.Fatal(err)
 		}
 		if req.ContentLength != tt.want {
-			t.Errorf("ContentLength(%T) = %d; want %d", tt.r, req.ContentLength, tt.want)
+			t.Errorf("test[%d]: ContentLength(%T) = %d; want %d", i, tt.r, req.ContentLength, tt.want)
+		}
+		if (req.ContentLength == 0) != (req.Body == NoBody) {
+			t.Errorf("test[%d]: ContentLength = %d but Body non-nil is %v", i, req.ContentLength, req.Body != nil)
 		}
 	}
 }
@@ -626,11 +671,31 @@ func TestStarRequest(t *testing.T) {
 	if err != nil {
 		return
 	}
+	if req.ContentLength != 0 {
+		t.Errorf("ContentLength = %d; want 0", req.ContentLength)
+	}
+	if req.Body == nil {
+		t.Errorf("Body = nil; want non-nil")
+	}
+
+	// Request.Write has Client semantics for Body/ContentLength,
+	// where ContentLength 0 means unknown if Body is non-nil, and
+	// thus chunking will happen unless we change semantics and
+	// signal that we want to serialize it as exactly zero.  The
+	// only way to do that for outbound requests is with a nil
+	// Body:
+	clientReq := *req
+	clientReq.Body = nil
+
 	var out bytes.Buffer
-	if err := req.Write(&out); err != nil {
+	if err := clientReq.Write(&out); err != nil {
 		t.Fatal(err)
 	}
-	back, err := ReadRequest(bufio.NewReader(&out))
+
+	if strings.Contains(out.String(), "chunked") {
+		t.Error("wrote chunked request; want no body")
+	}
+	back, err := ReadRequest(bufio.NewReader(bytes.NewReader(out.Bytes())))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -719,6 +784,47 @@ func TestMaxBytesReaderStickyError(t *testing.T) {
 	}
 }
 
+// verify that NewRequest sets Request.GetBody and that it works
+func TestNewRequestGetBody(t *testing.T) {
+	tests := []struct {
+		r io.Reader
+	}{
+		{r: strings.NewReader("hello")},
+		{r: bytes.NewReader([]byte("hello"))},
+		{r: bytes.NewBuffer([]byte("hello"))},
+	}
+	for i, tt := range tests {
+		req, err := NewRequest("POST", "http://foo.tld/", tt.r)
+		if err != nil {
+			t.Errorf("test[%d]: %v", i, err)
+			continue
+		}
+		if req.Body == nil {
+			t.Errorf("test[%d]: Body = nil", i)
+			continue
+		}
+		if req.GetBody == nil {
+			t.Errorf("test[%d]: GetBody = nil", i)
+			continue
+		}
+		slurp1, err := ioutil.ReadAll(req.Body)
+		if err != nil {
+			t.Errorf("test[%d]: ReadAll(Body) = %v", i, err)
+		}
+		newBody, err := req.GetBody()
+		if err != nil {
+			t.Errorf("test[%d]: GetBody = %v", i, err)
+		}
+		slurp2, err := ioutil.ReadAll(newBody)
+		if err != nil {
+			t.Errorf("test[%d]: ReadAll(GetBody()) = %v", i, err)
+		}
+		if string(slurp1) != string(slurp2) {
+			t.Errorf("test[%d]: Body %q != GetBody %q", i, slurp1, slurp2)
+		}
+	}
+}
+
 func testMissingFile(t *testing.T, req *Request) {
 	f, fh, err := req.FormFile("missing")
 	if f != nil {
diff --git a/src/net/http/requestwrite_test.go b/src/net/http/requestwrite_test.go
index 2545f6f..d13e37a 100644
--- a/src/net/http/requestwrite_test.go
+++ b/src/net/http/requestwrite_test.go
@@ -28,7 +28,7 @@ type reqWriteTest struct {
 
 var reqWriteTests = []reqWriteTest{
 	// HTTP/1.1 => chunked coding; no body; no trailer
-	{
+	0: {
 		Req: Request{
 			Method: "GET",
 			URL: &url.URL{
@@ -75,7 +75,7 @@ var reqWriteTests = []reqWriteTest{
 			"Proxy-Connection: keep-alive\r\n\r\n",
 	},
 	// HTTP/1.1 => chunked coding; body; empty trailer
-	{
+	1: {
 		Req: Request{
 			Method: "GET",
 			URL: &url.URL{
@@ -104,7 +104,7 @@ var reqWriteTests = []reqWriteTest{
 			chunk("abcdef") + chunk(""),
 	},
 	// HTTP/1.1 POST => chunked coding; body; empty trailer
-	{
+	2: {
 		Req: Request{
 			Method: "POST",
 			URL: &url.URL{
@@ -137,7 +137,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// HTTP/1.1 POST with Content-Length, no chunking
-	{
+	3: {
 		Req: Request{
 			Method: "POST",
 			URL: &url.URL{
@@ -172,7 +172,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// HTTP/1.1 POST with Content-Length in headers
-	{
+	4: {
 		Req: Request{
 			Method: "POST",
 			URL:    mustParseURL("http://example.com/"),
@@ -201,7 +201,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// default to HTTP/1.1
-	{
+	5: {
 		Req: Request{
 			Method: "GET",
 			URL:    mustParseURL("/search"),
@@ -215,7 +215,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Request with a 0 ContentLength and a 0 byte body.
-	{
+	6: {
 		Req: Request{
 			Method:        "POST",
 			URL:           mustParseURL("/"),
@@ -227,9 +227,32 @@ var reqWriteTests = []reqWriteTest{
 
 		Body: func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 0)) },
 
-		// RFC 2616 Section 14.13 says Content-Length should be specified
-		// unless body is prohibited by the request method.
-		// Also, nginx expects it for POST and PUT.
+		WantWrite: "POST / HTTP/1.1\r\n" +
+			"Host: example.com\r\n" +
+			"User-Agent: Go-http-client/1.1\r\n" +
+			"Transfer-Encoding: chunked\r\n" +
+			"\r\n0\r\n\r\n",
+
+		WantProxy: "POST / HTTP/1.1\r\n" +
+			"Host: example.com\r\n" +
+			"User-Agent: Go-http-client/1.1\r\n" +
+			"Transfer-Encoding: chunked\r\n" +
+			"\r\n0\r\n\r\n",
+	},
+
+	// Request with a 0 ContentLength and a nil body.
+	7: {
+		Req: Request{
+			Method:        "POST",
+			URL:           mustParseURL("/"),
+			Host:          "example.com",
+			ProtoMajor:    1,
+			ProtoMinor:    1,
+			ContentLength: 0, // as if unset by user
+		},
+
+		Body: func() io.ReadCloser { return nil },
+
 		WantWrite: "POST / HTTP/1.1\r\n" +
 			"Host: example.com\r\n" +
 			"User-Agent: Go-http-client/1.1\r\n" +
@@ -244,7 +267,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Request with a 0 ContentLength and a 1 byte body.
-	{
+	8: {
 		Req: Request{
 			Method:        "POST",
 			URL:           mustParseURL("/"),
@@ -270,7 +293,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Request with a ContentLength of 10 but a 5 byte body.
-	{
+	9: {
 		Req: Request{
 			Method:        "POST",
 			URL:           mustParseURL("/"),
@@ -284,7 +307,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Request with a ContentLength of 4 but an 8 byte body.
-	{
+	10: {
 		Req: Request{
 			Method:        "POST",
 			URL:           mustParseURL("/"),
@@ -298,7 +321,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Request with a 5 ContentLength and nil body.
-	{
+	11: {
 		Req: Request{
 			Method:        "POST",
 			URL:           mustParseURL("/"),
@@ -311,7 +334,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Request with a 0 ContentLength and a body with 1 byte content and an error.
-	{
+	12: {
 		Req: Request{
 			Method:        "POST",
 			URL:           mustParseURL("/"),
@@ -331,7 +354,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Request with a 0 ContentLength and a body without content and an error.
-	{
+	13: {
 		Req: Request{
 			Method:        "POST",
 			URL:           mustParseURL("/"),
@@ -352,7 +375,7 @@ var reqWriteTests = []reqWriteTest{
 
 	// Verify that DumpRequest preserves the HTTP version number, doesn't add a Host,
 	// and doesn't add a User-Agent.
-	{
+	14: {
 		Req: Request{
 			Method:     "GET",
 			URL:        mustParseURL("/foo"),
@@ -373,7 +396,7 @@ var reqWriteTests = []reqWriteTest{
 	// an empty Host header, and don't use
 	// Request.Header["Host"]. This is just testing that
 	// we don't change Go 1.0 behavior.
-	{
+	15: {
 		Req: Request{
 			Method: "GET",
 			Host:   "",
@@ -395,7 +418,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Opaque test #1 from golang.org/issue/4860
-	{
+	16: {
 		Req: Request{
 			Method: "GET",
 			URL: &url.URL{
@@ -414,7 +437,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Opaque test #2 from golang.org/issue/4860
-	{
+	17: {
 		Req: Request{
 			Method: "GET",
 			URL: &url.URL{
@@ -433,7 +456,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Testing custom case in header keys. Issue 5022.
-	{
+	18: {
 		Req: Request{
 			Method: "GET",
 			URL: &url.URL{
@@ -457,7 +480,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Request with host header field; IPv6 address with zone identifier
-	{
+	19: {
 		Req: Request{
 			Method: "GET",
 			URL: &url.URL{
@@ -472,7 +495,7 @@ var reqWriteTests = []reqWriteTest{
 	},
 
 	// Request with optional host header field; IPv6 address with zone identifier
-	{
+	20: {
 		Req: Request{
 			Method: "GET",
 			URL: &url.URL{
@@ -553,14 +576,14 @@ func (rc *closeChecker) Close() error {
 	return nil
 }
 
-// TestRequestWriteClosesBody tests that Request.Write does close its request.Body.
+// TestRequestWriteClosesBody tests that Request.Write closes its request.Body.
 // It also indirectly tests NewRequest and that it doesn't wrap an existing Closer
 // inside a NopCloser, and that it serializes it correctly.
 func TestRequestWriteClosesBody(t *testing.T) {
 	rc := &closeChecker{Reader: strings.NewReader("my body")}
 	req, _ := NewRequest("POST", "http://foo.com/", rc)
-	if req.ContentLength != 0 {
-		t.Errorf("got req.ContentLength %d, want 0", req.ContentLength)
+	if req.ContentLength != -1 {
+		t.Errorf("got req.ContentLength %d, want -1", req.ContentLength)
 	}
 	buf := new(bytes.Buffer)
 	req.Write(buf)
@@ -571,12 +594,7 @@ func TestRequestWriteClosesBody(t *testing.T) {
 		"Host: foo.com\r\n" +
 		"User-Agent: Go-http-client/1.1\r\n" +
 		"Transfer-Encoding: chunked\r\n\r\n" +
-		// TODO: currently we don't buffer before chunking, so we get a
-		// single "m" chunk before the other chunks, as this was the 1-byte
-		// read from our MultiReader where we stitched the Body back together
-		// after sniffing whether the Body was 0 bytes or not.
-		chunk("m") +
-		chunk("y body") +
+		chunk("my body") +
 		chunk("")
 	if buf.String() != expected {
 		t.Errorf("write:\n got: %s\nwant: %s", buf.String(), expected)
diff --git a/src/net/http/response.go b/src/net/http/response.go
index 5450d50..ae118fb 100644
--- a/src/net/http/response.go
+++ b/src/net/http/response.go
@@ -261,7 +261,7 @@ func (r *Response) Write(w io.Writer) error {
 		if n == 0 {
 			// Reset it to a known zero reader, in case underlying one
 			// is unhappy being read repeatedly.
-			r1.Body = eofReader
+			r1.Body = NoBody
 		} else {
 			r1.ContentLength = -1
 			r1.Body = struct {
@@ -300,7 +300,7 @@ func (r *Response) Write(w io.Writer) error {
 	// contentLengthAlreadySent may have been already sent for
 	// POST/PUT requests, even if zero length. See Issue 8180.
 	contentLengthAlreadySent := tw.shouldSendContentLength()
-	if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) && !contentLengthAlreadySent {
+	if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) && !contentLengthAlreadySent && bodyAllowedForStatus(r.StatusCode) {
 		if _, err := io.WriteString(w, "Content-Length: 0\r\n"); err != nil {
 			return err
 		}
diff --git a/src/net/http/response_test.go b/src/net/http/response_test.go
index 126da92..660d517 100644
--- a/src/net/http/response_test.go
+++ b/src/net/http/response_test.go
@@ -589,6 +589,7 @@ var readResponseCloseInMiddleTests = []struct {
 // reading only part of its contents advances the read to the end of
 // the request, right up until the next request.
 func TestReadResponseCloseInMiddle(t *testing.T) {
+	t.Parallel()
 	for _, test := range readResponseCloseInMiddleTests {
 		fatalf := func(format string, args ...interface{}) {
 			args = append([]interface{}{test.chunked, test.compressed}, args...)
@@ -792,6 +793,7 @@ func TestReadResponseErrors(t *testing.T) {
 	type testCase struct {
 		name    string // optional, defaults to in
 		in      string
+		header  Header
 		wantErr interface{} // nil, err value, or string substring
 	}
 
@@ -817,11 +819,22 @@ func TestReadResponseErrors(t *testing.T) {
 		}
 	}
 
+	contentLength := func(status, body string, wantErr interface{}, header Header) testCase {
+		return testCase{
+			name:    fmt.Sprintf("status %q %q", status, body),
+			in:      fmt.Sprintf("HTTP/1.1 %s\r\n%s", status, body),
+			wantErr: wantErr,
+			header:  header,
+		}
+	}
+
+	errMultiCL := "message cannot contain multiple Content-Length headers"
+
 	tests := []testCase{
-		{"", "", io.ErrUnexpectedEOF},
-		{"", "HTTP/1.1 301 Moved Permanently\r\nFoo: bar", io.ErrUnexpectedEOF},
-		{"", "HTTP/1.1", "malformed HTTP response"},
-		{"", "HTTP/2.0", "malformed HTTP response"},
+		{"", "", nil, io.ErrUnexpectedEOF},
+		{"", "HTTP/1.1 301 Moved Permanently\r\nFoo: bar", nil, io.ErrUnexpectedEOF},
+		{"", "HTTP/1.1", nil, "malformed HTTP response"},
+		{"", "HTTP/2.0", nil, "malformed HTTP response"},
 		status("20X Unknown", true),
 		status("abcd Unknown", true),
 		status("二百/两百 OK", true),
@@ -846,7 +859,21 @@ func TestReadResponseErrors(t *testing.T) {
 		version("HTTP/A.B", true),
 		version("HTTP/1", true),
 		version("http/1.1", true),
+
+		contentLength("200 OK", "Content-Length: 10\r\nContent-Length: 7\r\n\r\nGopher hey\r\n", errMultiCL, nil),
+		contentLength("200 OK", "Content-Length: 7\r\nContent-Length: 7\r\n\r\nGophers\r\n", nil, Header{"Content-Length": {"7"}}),
+		contentLength("201 OK", "Content-Length: 0\r\nContent-Length: 7\r\n\r\nGophers\r\n", errMultiCL, nil),
+		contentLength("300 OK", "Content-Length: 0\r\nContent-Length: 0 \r\n\r\nGophers\r\n", nil, Header{"Content-Length": {"0"}}),
+		contentLength("200 OK", "Content-Length:\r\nContent-Length:\r\n\r\nGophers\r\n", nil, nil),
+		contentLength("206 OK", "Content-Length:\r\nContent-Length: 0 \r\nConnection: close\r\n\r\nGophers\r\n", errMultiCL, nil),
+
+		// multiple content-length headers for 204 and 304 should still be checked
+		contentLength("204 OK", "Content-Length: 7\r\nContent-Length: 8\r\n\r\n", errMultiCL, nil),
+		contentLength("204 OK", "Content-Length: 3\r\nContent-Length: 3\r\n\r\n", nil, nil),
+		contentLength("304 OK", "Content-Length: 880\r\nContent-Length: 1\r\n\r\n", errMultiCL, nil),
+		contentLength("304 OK", "Content-Length: 961\r\nContent-Length: 961\r\n\r\n", nil, nil),
 	}
+
 	for i, tt := range tests {
 		br := bufio.NewReader(strings.NewReader(tt.in))
 		_, rerr := ReadResponse(br, nil)
diff --git a/src/net/http/responsewrite_test.go b/src/net/http/responsewrite_test.go
index 90f6767..d41d898 100644
--- a/src/net/http/responsewrite_test.go
+++ b/src/net/http/responsewrite_test.go
@@ -241,7 +241,8 @@ func TestResponseWrite(t *testing.T) {
 			"HTTP/1.0 007 license to violate specs\r\nContent-Length: 0\r\n\r\n",
 		},
 
-		// No stutter.
+		// No stutter.  Status code in 1xx range response should
+		// not include a Content-Length header.  See issue #16942.
 		{
 			Response{
 				StatusCode: 123,
@@ -253,7 +254,23 @@ func TestResponseWrite(t *testing.T) {
 				Body:       nil,
 			},
 
-			"HTTP/1.0 123 Sesame Street\r\nContent-Length: 0\r\n\r\n",
+			"HTTP/1.0 123 Sesame Street\r\n\r\n",
+		},
+
+		// Status code 204 (No content) response should not include a
+		// Content-Length header.  See issue #16942.
+		{
+			Response{
+				StatusCode: 204,
+				Status:     "No Content",
+				ProtoMajor: 1,
+				ProtoMinor: 0,
+				Request:    dummyReq("GET"),
+				Header:     Header{},
+				Body:       nil,
+			},
+
+			"HTTP/1.0 204 No Content\r\n\r\n",
 		},
 	}
 
diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go
index 13e5f28..593b1f3 100644
--- a/src/net/http/serve_test.go
+++ b/src/net/http/serve_test.go
@@ -156,6 +156,7 @@ func (ht handlerTest) rawResponse(req string) string {
 }
 
 func TestConsumingBodyOnNextConn(t *testing.T) {
+	t.Parallel()
 	defer afterTest(t)
 	conn := new(testConn)
 	for i := 0; i < 2; i++ {
@@ -237,6 +238,7 @@ var vtests = []struct {
 }
 
 func TestHostHandlers(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	mux := NewServeMux()
 	for _, h := range handlers {
@@ -353,6 +355,7 @@ var serveMuxTests = []struct {
 }
 
 func TestServeMuxHandler(t *testing.T) {
+	setParallel(t)
 	mux := NewServeMux()
 	for _, e := range serveMuxRegister {
 		mux.Handle(e.pattern, e.h)
@@ -390,15 +393,16 @@ var serveMuxTests2 = []struct {
 // TestServeMuxHandlerRedirects tests that automatic redirects generated by
 // mux.Handler() shouldn't clear the request's query string.
 func TestServeMuxHandlerRedirects(t *testing.T) {
+	setParallel(t)
 	mux := NewServeMux()
 	for _, e := range serveMuxRegister {
 		mux.Handle(e.pattern, e.h)
 	}
 
 	for _, tt := range serveMuxTests2 {
-		tries := 1
+		tries := 1 // expect at most 1 redirection if redirOk is true.
 		turl := tt.url
-		for tries > 0 {
+		for {
 			u, e := url.Parse(turl)
 			if e != nil {
 				t.Fatal(e)
@@ -432,6 +436,7 @@ func TestServeMuxHandlerRedirects(t *testing.T) {
 
 // Tests for https://golang.org/issue/900
 func TestMuxRedirectLeadingSlashes(t *testing.T) {
+	setParallel(t)
 	paths := []string{"//foo.txt", "///foo.txt", "/../../foo.txt"}
 	for _, path := range paths {
 		req, err := ReadRequest(bufio.NewReader(strings.NewReader("GET " + path + " HTTP/1.1\r\nHost: test\r\n\r\n")))
@@ -456,9 +461,6 @@ func TestMuxRedirectLeadingSlashes(t *testing.T) {
 }
 
 func TestServerTimeouts(t *testing.T) {
-	if runtime.GOOS == "plan9" {
-		t.Skip("skipping test; see https://golang.org/issue/7237")
-	}
 	setParallel(t)
 	defer afterTest(t)
 	reqNum := 0
@@ -536,9 +538,7 @@ func TestServerTimeouts(t *testing.T) {
 // shouldn't cause a handler to block forever on reads (next HTTP
 // request) that will never happen.
 func TestOnlyWriteTimeout(t *testing.T) {
-	if runtime.GOOS == "plan9" {
-		t.Skip("skipping test; see https://golang.org/issue/7237")
-	}
+	setParallel(t)
 	defer afterTest(t)
 	var conn net.Conn
 	var afterTimeoutErrc = make(chan error, 1)
@@ -598,6 +598,7 @@ func (l trackLastConnListener) Accept() (c net.Conn, err error) {
 
 // TestIdentityResponse verifies that a handler can unset
 func TestIdentityResponse(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	handler := HandlerFunc(func(rw ResponseWriter, req *Request) {
 		rw.Header().Set("Content-Length", "3")
@@ -619,13 +620,16 @@ func TestIdentityResponse(t *testing.T) {
 	ts := httptest.NewServer(handler)
 	defer ts.Close()
 
+	c := &Client{Transport: new(Transport)}
+	defer closeClient(c)
+
 	// Note: this relies on the assumption (which is true) that
 	// Get sends HTTP/1.1 or greater requests. Otherwise the
 	// server wouldn't have the choice to send back chunked
 	// responses.
 	for _, te := range []string{"", "identity"} {
 		url := ts.URL + "/?te=" + te
-		res, err := Get(url)
+		res, err := c.Get(url)
 		if err != nil {
 			t.Fatalf("error with Get of %s: %v", url, err)
 		}
@@ -644,7 +648,7 @@ func TestIdentityResponse(t *testing.T) {
 
 	// Verify that ErrContentLength is returned
 	url := ts.URL + "/?overwrite=1"
-	res, err := Get(url)
+	res, err := c.Get(url)
 	if err != nil {
 		t.Fatalf("error with Get of %s: %v", url, err)
 	}
@@ -674,6 +678,7 @@ func TestIdentityResponse(t *testing.T) {
 }
 
 func testTCPConnectionCloses(t *testing.T, req string, h Handler) {
+	setParallel(t)
 	defer afterTest(t)
 	s := httptest.NewServer(h)
 	defer s.Close()
@@ -717,6 +722,7 @@ func testTCPConnectionCloses(t *testing.T, req string, h Handler) {
 }
 
 func testTCPConnectionStaysOpen(t *testing.T, req string, handler Handler) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewServer(handler)
 	defer ts.Close()
@@ -750,7 +756,7 @@ func TestServeHTTP10Close(t *testing.T) {
 
 // TestClientCanClose verifies that clients can also force a connection to close.
 func TestClientCanClose(t *testing.T) {
-	testTCPConnectionCloses(t, "GET / HTTP/1.1\r\nConnection: close\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+	testTCPConnectionCloses(t, "GET / HTTP/1.1\r\nHost: foo\r\nConnection: close\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
 		// Nothing.
 	}))
 }
@@ -758,7 +764,7 @@ func TestClientCanClose(t *testing.T) {
 // TestHandlersCanSetConnectionClose verifies that handlers can force a connection to close,
 // even for HTTP/1.1 requests.
 func TestHandlersCanSetConnectionClose11(t *testing.T) {
-	testTCPConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+	testTCPConnectionCloses(t, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
 		w.Header().Set("Connection", "close")
 	}))
 }
@@ -796,6 +802,7 @@ func TestHTTP10KeepAlive304Response(t *testing.T) {
 
 // Issue 15703
 func TestKeepAliveFinalChunkWithEOF(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, false /* h1 */, HandlerFunc(func(w ResponseWriter, r *Request) {
 		w.(Flusher).Flush() // force chunked encoding
@@ -828,6 +835,7 @@ func TestSetsRemoteAddr_h1(t *testing.T) { testSetsRemoteAddr(t, h1Mode) }
 func TestSetsRemoteAddr_h2(t *testing.T) { testSetsRemoteAddr(t, h2Mode) }
 
 func testSetsRemoteAddr(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		fmt.Fprintf(w, "%s", r.RemoteAddr)
@@ -877,6 +885,7 @@ func (c *blockingRemoteAddrConn) RemoteAddr() net.Addr {
 
 // Issue 12943
 func TestServerAllowsBlockingRemoteAddr(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		fmt.Fprintf(w, "RA:%s", r.RemoteAddr)
@@ -948,7 +957,9 @@ func TestServerAllowsBlockingRemoteAddr(t *testing.T) {
 		t.Fatalf("response 1 addr = %q; want %q", g, e)
 	}
 }
+
 func TestIdentityResponseHeaders(t *testing.T) {
+	// Not parallel; changes log output.
 	defer afterTest(t)
 	log.SetOutput(ioutil.Discard) // is noisy otherwise
 	defer log.SetOutput(os.Stderr)
@@ -960,7 +971,10 @@ func TestIdentityResponseHeaders(t *testing.T) {
 	}))
 	defer ts.Close()
 
-	res, err := Get(ts.URL)
+	c := &Client{Transport: new(Transport)}
+	defer closeClient(c)
+
+	res, err := c.Get(ts.URL)
 	if err != nil {
 		t.Fatalf("Get error: %v", err)
 	}
@@ -983,6 +997,7 @@ func TestHeadResponses_h1(t *testing.T) { testHeadResponses(t, h1Mode) }
 func TestHeadResponses_h2(t *testing.T) { testHeadResponses(t, h2Mode) }
 
 func testHeadResponses(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		_, err := w.Write([]byte("<html>"))
@@ -1020,9 +1035,6 @@ func testHeadResponses(t *testing.T, h2 bool) {
 }
 
 func TestTLSHandshakeTimeout(t *testing.T) {
-	if runtime.GOOS == "plan9" {
-		t.Skip("skipping test; see https://golang.org/issue/7237")
-	}
 	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
@@ -1054,6 +1066,7 @@ func TestTLSHandshakeTimeout(t *testing.T) {
 }
 
 func TestTLSServer(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		if r.TLS != nil {
@@ -1121,6 +1134,7 @@ func TestAutomaticHTTP2_Serve_H2TLSConfig(t *testing.T) {
 }
 
 func testAutomaticHTTP2_Serve(t *testing.T, tlsConf *tls.Config, wantH2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	ln := newLocalListener(t)
 	ln.Close() // immediately (not a defer!)
@@ -1136,6 +1150,7 @@ func testAutomaticHTTP2_Serve(t *testing.T, tlsConf *tls.Config, wantH2 bool) {
 }
 
 func TestAutomaticHTTP2_Serve_WithTLSConfig(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ln := newLocalListener(t)
 	ln.Close() // immediately (not a defer!)
@@ -1177,6 +1192,7 @@ func TestAutomaticHTTP2_ListenAndServe_GetCertificate(t *testing.T) {
 }
 
 func testAutomaticHTTP2_ListenAndServe(t *testing.T, tlsConf *tls.Config) {
+	// Not parallel: uses global test hooks.
 	defer afterTest(t)
 	defer SetTestHookServerServe(nil)
 	var ok bool
@@ -1280,6 +1296,7 @@ var serverExpectTests = []serverExpectTest{
 // correctly.
 // http2 test: TestServer_Response_Automatic100Continue
 func TestServerExpect(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		// Note using r.FormValue("readbody") because for POST
@@ -1373,6 +1390,7 @@ func TestServerExpect(t *testing.T) {
 // Under a ~256KB (maxPostHandlerReadBytes) threshold, the server
 // should consume client request bodies that a handler didn't read.
 func TestServerUnreadRequestBodyLittle(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	conn := new(testConn)
 	body := strings.Repeat("x", 100<<10)
@@ -1413,6 +1431,7 @@ func TestServerUnreadRequestBodyLittle(t *testing.T) {
 // should ignore client request bodies that a handler didn't read
 // and close the connection.
 func TestServerUnreadRequestBodyLarge(t *testing.T) {
+	setParallel(t)
 	if testing.Short() && testenv.Builder() == "" {
 		t.Log("skipping in short mode")
 	}
@@ -1546,6 +1565,7 @@ var handlerBodyCloseTests = [...]handlerBodyCloseTest{
 }
 
 func TestHandlerBodyClose(t *testing.T) {
+	setParallel(t)
 	if testing.Short() && testenv.Builder() == "" {
 		t.Skip("skipping in -short mode")
 	}
@@ -1625,6 +1645,7 @@ var testHandlerBodyConsumers = []testHandlerBodyConsumer{
 }
 
 func TestRequestBodyReadErrorClosesConnection(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	for _, handler := range testHandlerBodyConsumers {
 		conn := new(testConn)
@@ -1655,6 +1676,7 @@ func TestRequestBodyReadErrorClosesConnection(t *testing.T) {
 }
 
 func TestInvalidTrailerClosesConnection(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	for _, handler := range testHandlerBodyConsumers {
 		conn := new(testConn)
@@ -1737,7 +1759,7 @@ restart:
 		if !c.rd.IsZero() {
 			// If the deadline falls in the middle of our sleep window, deduct
 			// part of the sleep, then return a timeout.
-			if remaining := c.rd.Sub(time.Now()); remaining < cue {
+			if remaining := time.Until(c.rd); remaining < cue {
 				c.script[0] = cue - remaining
 				time.Sleep(remaining)
 				return 0, syscall.ETIMEDOUT
@@ -1823,6 +1845,7 @@ func TestRequestBodyTimeoutClosesConnection(t *testing.T) {
 func TestTimeoutHandler_h1(t *testing.T) { testTimeoutHandler(t, h1Mode) }
 func TestTimeoutHandler_h2(t *testing.T) { testTimeoutHandler(t, h2Mode) }
 func testTimeoutHandler(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	sendHi := make(chan bool, 1)
 	writeErrors := make(chan error, 1)
@@ -1876,6 +1899,7 @@ func testTimeoutHandler(t *testing.T, h2 bool) {
 
 // See issues 8209 and 8414.
 func TestTimeoutHandlerRace(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 
 	delayHi := HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -1892,6 +1916,9 @@ func TestTimeoutHandlerRace(t *testing.T) {
 	ts := httptest.NewServer(TimeoutHandler(delayHi, 20*time.Millisecond, ""))
 	defer ts.Close()
 
+	c := &Client{Transport: new(Transport)}
+	defer closeClient(c)
+
 	var wg sync.WaitGroup
 	gate := make(chan bool, 10)
 	n := 50
@@ -1905,7 +1932,7 @@ func TestTimeoutHandlerRace(t *testing.T) {
 		go func() {
 			defer wg.Done()
 			defer func() { <-gate }()
-			res, err := Get(fmt.Sprintf("%s/%d", ts.URL, rand.Intn(50)))
+			res, err := c.Get(fmt.Sprintf("%s/%d", ts.URL, rand.Intn(50)))
 			if err == nil {
 				io.Copy(ioutil.Discard, res.Body)
 				res.Body.Close()
@@ -1917,6 +1944,7 @@ func TestTimeoutHandlerRace(t *testing.T) {
 
 // See issues 8209 and 8414.
 func TestTimeoutHandlerRaceHeader(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 
 	delay204 := HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -1932,13 +1960,15 @@ func TestTimeoutHandlerRaceHeader(t *testing.T) {
 	if testing.Short() {
 		n = 10
 	}
+	c := &Client{Transport: new(Transport)}
+	defer closeClient(c)
 	for i := 0; i < n; i++ {
 		gate <- true
 		wg.Add(1)
 		go func() {
 			defer wg.Done()
 			defer func() { <-gate }()
-			res, err := Get(ts.URL)
+			res, err := c.Get(ts.URL)
 			if err != nil {
 				t.Error(err)
 				return
@@ -1952,6 +1982,7 @@ func TestTimeoutHandlerRaceHeader(t *testing.T) {
 
 // Issue 9162
 func TestTimeoutHandlerRaceHeaderTimeout(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	sendHi := make(chan bool, 1)
 	writeErrors := make(chan error, 1)
@@ -2016,11 +2047,15 @@ func TestTimeoutHandlerStartTimerWhenServing(t *testing.T) {
 	timeout := 300 * time.Millisecond
 	ts := httptest.NewServer(TimeoutHandler(handler, timeout, ""))
 	defer ts.Close()
+
+	c := &Client{Transport: new(Transport)}
+	defer closeClient(c)
+
 	// Issue was caused by the timeout handler starting the timer when
 	// was created, not when the request. So wait for more than the timeout
 	// to ensure that's not the case.
 	time.Sleep(2 * timeout)
-	res, err := Get(ts.URL)
+	res, err := c.Get(ts.URL)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -2032,6 +2067,7 @@ func TestTimeoutHandlerStartTimerWhenServing(t *testing.T) {
 
 // https://golang.org/issue/15948
 func TestTimeoutHandlerEmptyResponse(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	var handler HandlerFunc = func(w ResponseWriter, _ *Request) {
 		// No response.
@@ -2040,7 +2076,10 @@ func TestTimeoutHandlerEmptyResponse(t *testing.T) {
 	ts := httptest.NewServer(TimeoutHandler(handler, timeout, ""))
 	defer ts.Close()
 
-	res, err := Get(ts.URL)
+	c := &Client{Transport: new(Transport)}
+	defer closeClient(c)
+
+	res, err := c.Get(ts.URL)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -2050,23 +2089,6 @@ func TestTimeoutHandlerEmptyResponse(t *testing.T) {
 	}
 }
 
-// Verifies we don't path.Clean() on the wrong parts in redirects.
-func TestRedirectMunging(t *testing.T) {
-	req, _ := NewRequest("GET", "http://example.com/", nil)
-
-	resp := httptest.NewRecorder()
-	Redirect(resp, req, "/foo?next=http://bar.com/", 302)
-	if g, e := resp.Header().Get("Location"), "/foo?next=http://bar.com/"; g != e {
-		t.Errorf("Location header was %q; want %q", g, e)
-	}
-
-	resp = httptest.NewRecorder()
-	Redirect(resp, req, "http://localhost:8080/_ah/login?continue=http://localhost:8080/", 302)
-	if g, e := resp.Header().Get("Location"), "http://localhost:8080/_ah/login?continue=http://localhost:8080/"; g != e {
-		t.Errorf("Location header was %q; want %q", g, e)
-	}
-}
-
 func TestRedirectBadPath(t *testing.T) {
 	// This used to crash. It's not valid input (bad path), but it
 	// shouldn't crash.
@@ -2085,7 +2107,7 @@ func TestRedirectBadPath(t *testing.T) {
 }
 
 // Test different URL formats and schemes
-func TestRedirectURLFormat(t *testing.T) {
+func TestRedirect(t *testing.T) {
 	req, _ := NewRequest("GET", "http://example.com/qux/", nil)
 
 	var tests = []struct {
@@ -2108,6 +2130,14 @@ func TestRedirectURLFormat(t *testing.T) {
 		{"../quux/foobar.com/baz", "/quux/foobar.com/baz"},
 		// incorrect number of slashes
 		{"///foobar.com/baz", "/foobar.com/baz"},
+
+		// Verifies we don't path.Clean() on the wrong parts in redirects:
+		{"/foo?next=http://bar.com/", "/foo?next=http://bar.com/"},
+		{"http://localhost:8080/_ah/login?continue=http://localhost:8080/",
+			"http://localhost:8080/_ah/login?continue=http://localhost:8080/"},
+
+		{"/фубар", "/%d1%84%d1%83%d0%b1%d0%b0%d1%80"},
+		{"http://foo.com/фубар", "http://foo.com/%d1%84%d1%83%d0%b1%d0%b0%d1%80"},
 	}
 
 	for _, tt := range tests {
@@ -2133,6 +2163,7 @@ func TestZeroLengthPostAndResponse_h2(t *testing.T) {
 }
 
 func testZeroLengthPostAndResponse(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(rw ResponseWriter, r *Request) {
 		all, err := ioutil.ReadAll(r.Body)
@@ -2252,12 +2283,58 @@ func testHandlerPanic(t *testing.T, withHijack, h2 bool, panicValue interface{})
 	}
 }
 
+type terrorWriter struct{ t *testing.T }
+
+func (w terrorWriter) Write(p []byte) (int, error) {
+	w.t.Errorf("%s", p)
+	return len(p), nil
+}
+
+// Issue 16456: allow writing 0 bytes on hijacked conn to test hijack
+// without any log spam.
+func TestServerWriteHijackZeroBytes(t *testing.T) {
+	defer afterTest(t)
+	done := make(chan struct{})
+	ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+		defer close(done)
+		w.(Flusher).Flush()
+		conn, _, err := w.(Hijacker).Hijack()
+		if err != nil {
+			t.Errorf("Hijack: %v", err)
+			return
+		}
+		defer conn.Close()
+		_, err = w.Write(nil)
+		if err != ErrHijacked {
+			t.Errorf("Write error = %v; want ErrHijacked", err)
+		}
+	}))
+	ts.Config.ErrorLog = log.New(terrorWriter{t}, "Unexpected write: ", 0)
+	ts.Start()
+	defer ts.Close()
+
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+	c := &Client{Transport: tr}
+	res, err := c.Get(ts.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	res.Body.Close()
+	select {
+	case <-done:
+	case <-time.After(5 * time.Second):
+		t.Fatal("timeout")
+	}
+}
+
 func TestServerNoDate_h1(t *testing.T)        { testServerNoHeader(t, h1Mode, "Date") }
 func TestServerNoDate_h2(t *testing.T)        { testServerNoHeader(t, h2Mode, "Date") }
 func TestServerNoContentType_h1(t *testing.T) { testServerNoHeader(t, h1Mode, "Content-Type") }
 func TestServerNoContentType_h2(t *testing.T) { testServerNoHeader(t, h2Mode, "Content-Type") }
 
 func testServerNoHeader(t *testing.T, h2 bool, header string) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		w.Header()[header] = nil
@@ -2275,6 +2352,7 @@ func testServerNoHeader(t *testing.T, h2 bool, header string) {
 }
 
 func TestStripPrefix(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	h := HandlerFunc(func(w ResponseWriter, r *Request) {
 		w.Header().Set("X-Path", r.URL.Path)
@@ -2282,7 +2360,10 @@ func TestStripPrefix(t *testing.T) {
 	ts := httptest.NewServer(StripPrefix("/foo", h))
 	defer ts.Close()
 
-	res, err := Get(ts.URL + "/foo/bar")
+	c := &Client{Transport: new(Transport)}
+	defer closeClient(c)
+
+	res, err := c.Get(ts.URL + "/foo/bar")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -2304,10 +2385,11 @@ func TestStripPrefix(t *testing.T) {
 func TestRequestLimit_h1(t *testing.T) { testRequestLimit(t, h1Mode) }
 func TestRequestLimit_h2(t *testing.T) { testRequestLimit(t, h2Mode) }
 func testRequestLimit(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		t.Fatalf("didn't expect to get request in Handler")
-	}))
+	}), optQuietLog)
 	defer cst.close()
 	req, _ := NewRequest("GET", cst.ts.URL, nil)
 	var bytesPerHeader = len("header12345: val12345\r\n")
@@ -2350,6 +2432,7 @@ func (cr countReader) Read(p []byte) (n int, err error) {
 func TestRequestBodyLimit_h1(t *testing.T) { testRequestBodyLimit(t, h1Mode) }
 func TestRequestBodyLimit_h2(t *testing.T) { testRequestBodyLimit(t, h2Mode) }
 func testRequestBodyLimit(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	const limit = 1 << 20
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -2399,14 +2482,14 @@ func TestClientWriteShutdown(t *testing.T) {
 	}
 	err = conn.(*net.TCPConn).CloseWrite()
 	if err != nil {
-		t.Fatalf("Dial: %v", err)
+		t.Fatalf("CloseWrite: %v", err)
 	}
 	donec := make(chan bool)
 	go func() {
 		defer close(donec)
 		bs, err := ioutil.ReadAll(conn)
 		if err != nil {
-			t.Fatalf("ReadAll: %v", err)
+			t.Errorf("ReadAll: %v", err)
 		}
 		got := string(bs)
 		if got != "" {
@@ -2445,6 +2528,7 @@ func TestServerBufferedChunking(t *testing.T) {
 // closing the TCP connection, causing the client to get a RST.
 // See https://golang.org/issue/3595
 func TestServerGracefulClose(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		Error(w, "bye", StatusUnauthorized)
@@ -2557,7 +2641,8 @@ func TestCloseNotifier(t *testing.T) {
 	go func() {
 		_, err = fmt.Fprintf(conn, "GET / HTTP/1.1\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n")
 		if err != nil {
-			t.Fatal(err)
+			t.Error(err)
+			return
 		}
 		<-diec
 		conn.Close()
@@ -2599,7 +2684,8 @@ func TestCloseNotifierPipelined(t *testing.T) {
 		const req = "GET / HTTP/1.1\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n"
 		_, err = io.WriteString(conn, req+req) // two requests
 		if err != nil {
-			t.Fatal(err)
+			t.Error(err)
+			return
 		}
 		<-diec
 		conn.Close()
@@ -2707,6 +2793,7 @@ func TestHijackAfterCloseNotifier(t *testing.T) {
 }
 
 func TestHijackBeforeRequestBodyRead(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	var requestBody = bytes.Repeat([]byte("a"), 1<<20)
 	bodyOkay := make(chan bool, 1)
@@ -3028,15 +3115,18 @@ func (l *errorListener) Addr() net.Addr {
 }
 
 func TestAcceptMaxFds(t *testing.T) {
-	log.SetOutput(ioutil.Discard) // is noisy otherwise
-	defer log.SetOutput(os.Stderr)
+	setParallel(t)
 
 	ln := &errorListener{[]error{
 		&net.OpError{
 			Op:  "accept",
 			Err: syscall.EMFILE,
 		}}}
-	err := Serve(ln, HandlerFunc(HandlerFunc(func(ResponseWriter, *Request) {})))
+	server := &Server{
+		Handler:  HandlerFunc(HandlerFunc(func(ResponseWriter, *Request) {})),
+		ErrorLog: log.New(ioutil.Discard, "", 0), // noisy otherwise
+	}
+	err := server.Serve(ln)
 	if err != io.EOF {
 		t.Errorf("got error %v, want EOF", err)
 	}
@@ -3161,6 +3251,7 @@ func TestHTTP10ConnectionHeader(t *testing.T) {
 func TestServerReaderFromOrder_h1(t *testing.T) { testServerReaderFromOrder(t, h1Mode) }
 func TestServerReaderFromOrder_h2(t *testing.T) { testServerReaderFromOrder(t, h2Mode) }
 func testServerReaderFromOrder(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	pr, pw := io.Pipe()
 	const size = 3 << 20
@@ -3265,6 +3356,7 @@ func TestTransportAndServerSharedBodyRace_h2(t *testing.T) {
 	testTransportAndServerSharedBodyRace(t, h2Mode)
 }
 func testTransportAndServerSharedBodyRace(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 
 	const bodySize = 1 << 20
@@ -3453,6 +3545,7 @@ func TestAppendTime(t *testing.T) {
 }
 
 func TestServerConnState(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	handler := map[string]func(w ResponseWriter, r *Request){
 		"/": func(w ResponseWriter, r *Request) {
@@ -3500,14 +3593,39 @@ func TestServerConnState(t *testing.T) {
 	}
 	ts.Start()
 
-	mustGet(t, ts.URL+"/")
-	mustGet(t, ts.URL+"/close")
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+	c := &Client{Transport: tr}
+
+	mustGet := func(url string, headers ...string) {
+		req, err := NewRequest("GET", url, nil)
+		if err != nil {
+			t.Fatal(err)
+		}
+		for len(headers) > 0 {
+			req.Header.Add(headers[0], headers[1])
+			headers = headers[2:]
+		}
+		res, err := c.Do(req)
+		if err != nil {
+			t.Errorf("Error fetching %s: %v", url, err)
+			return
+		}
+		_, err = ioutil.ReadAll(res.Body)
+		defer res.Body.Close()
+		if err != nil {
+			t.Errorf("Error reading %s: %v", url, err)
+		}
+	}
+
+	mustGet(ts.URL + "/")
+	mustGet(ts.URL + "/close")
 
-	mustGet(t, ts.URL+"/")
-	mustGet(t, ts.URL+"/", "Connection", "close")
+	mustGet(ts.URL + "/")
+	mustGet(ts.URL+"/", "Connection", "close")
 
-	mustGet(t, ts.URL+"/hijack")
-	mustGet(t, ts.URL+"/hijack-panic")
+	mustGet(ts.URL + "/hijack")
+	mustGet(ts.URL + "/hijack-panic")
 
 	// New->Closed
 	{
@@ -3587,31 +3705,10 @@ func TestServerConnState(t *testing.T) {
 	}
 
 	mu.Lock()
-	t.Errorf("Unexpected events.\nGot log: %s\n   Want: %s\n", logString(stateLog), logString(want))
+	t.Errorf("Unexpected events.\nGot log:\n%s\n   Want:\n%s\n", logString(stateLog), logString(want))
 	mu.Unlock()
 }
 
-func mustGet(t *testing.T, url string, headers ...string) {
-	req, err := NewRequest("GET", url, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	for len(headers) > 0 {
-		req.Header.Add(headers[0], headers[1])
-		headers = headers[2:]
-	}
-	res, err := DefaultClient.Do(req)
-	if err != nil {
-		t.Errorf("Error fetching %s: %v", url, err)
-		return
-	}
-	_, err = ioutil.ReadAll(res.Body)
-	defer res.Body.Close()
-	if err != nil {
-		t.Errorf("Error reading %s: %v", url, err)
-	}
-}
-
 func TestServerKeepAlivesEnabled(t *testing.T) {
 	defer afterTest(t)
 	ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
@@ -3632,6 +3729,7 @@ func TestServerKeepAlivesEnabled(t *testing.T) {
 func TestServerEmptyBodyRace_h1(t *testing.T) { testServerEmptyBodyRace(t, h1Mode) }
 func TestServerEmptyBodyRace_h2(t *testing.T) { testServerEmptyBodyRace(t, h2Mode) }
 func testServerEmptyBodyRace(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	var n int32
 	cst := newClientServerTest(t, h2, HandlerFunc(func(rw ResponseWriter, req *Request) {
@@ -3695,6 +3793,7 @@ func (c *closeWriteTestConn) CloseWrite() error {
 }
 
 func TestCloseWrite(t *testing.T) {
+	setParallel(t)
 	var srv Server
 	var testConn closeWriteTestConn
 	c := ExportServerNewConn(&srv, &testConn)
@@ -3935,6 +4034,7 @@ Host: foo
 // If a Handler finishes and there's an unread request body,
 // verify the server try to do implicit read on it before replying.
 func TestHandlerFinishSkipBigContentLengthRead(t *testing.T) {
+	setParallel(t)
 	conn := &testConn{closec: make(chan bool)}
 	conn.readBuf.Write([]byte(fmt.Sprintf(
 		"POST / HTTP/1.1\r\n" +
@@ -4033,7 +4133,11 @@ func TestServerValidatesHostHeader(t *testing.T) {
 		io.WriteString(&conn.readBuf, methodTarget+tt.proto+"\r\n"+tt.host+"\r\n")
 
 		ln := &oneConnListener{conn}
-		go Serve(ln, HandlerFunc(func(ResponseWriter, *Request) {}))
+		srv := Server{
+			ErrorLog: quietLog,
+			Handler:  HandlerFunc(func(ResponseWriter, *Request) {}),
+		}
+		go srv.Serve(ln)
 		<-conn.closec
 		res, err := ReadResponse(bufio.NewReader(&conn.writeBuf), nil)
 		if err != nil {
@@ -4088,6 +4192,7 @@ func TestServerHandlersCanHandleH2PRI(t *testing.T) {
 // Test that we validate the valid bytes in HTTP/1 headers.
 // Issue 11207.
 func TestServerValidatesHeaders(t *testing.T) {
+	setParallel(t)
 	tests := []struct {
 		header string
 		want   int
@@ -4097,9 +4202,10 @@ func TestServerValidatesHeaders(t *testing.T) {
 		{"X-Foo: bar\r\n", 200},
 		{"Foo: a space\r\n", 200},
 
-		{"A space: foo\r\n", 400},    // space in header
-		{"foo\xffbar: foo\r\n", 400}, // binary in header
-		{"foo\x00bar: foo\r\n", 400}, // binary in header
+		{"A space: foo\r\n", 400},                            // space in header
+		{"foo\xffbar: foo\r\n", 400},                         // binary in header
+		{"foo\x00bar: foo\r\n", 400},                         // binary in header
+		{"Foo: " + strings.Repeat("x", 1<<21) + "\r\n", 431}, // header too large
 
 		{"foo: foo foo\r\n", 200},    // LWS space is okay
 		{"foo: foo\tfoo\r\n", 200},   // LWS tab is okay
@@ -4112,7 +4218,11 @@ func TestServerValidatesHeaders(t *testing.T) {
 		io.WriteString(&conn.readBuf, "GET / HTTP/1.1\r\nHost: foo\r\n"+tt.header+"\r\n")
 
 		ln := &oneConnListener{conn}
-		go Serve(ln, HandlerFunc(func(ResponseWriter, *Request) {}))
+		srv := Server{
+			ErrorLog: quietLog,
+			Handler:  HandlerFunc(func(ResponseWriter, *Request) {}),
+		}
+		go srv.Serve(ln)
 		<-conn.closec
 		res, err := ReadResponse(bufio.NewReader(&conn.writeBuf), nil)
 		if err != nil {
@@ -4132,6 +4242,7 @@ func TestServerRequestContextCancel_ServeHTTPDone_h2(t *testing.T) {
 	testServerRequestContextCancel_ServeHTTPDone(t, h2Mode)
 }
 func testServerRequestContextCancel_ServeHTTPDone(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	ctxc := make(chan context.Context, 1)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -4157,13 +4268,12 @@ func testServerRequestContextCancel_ServeHTTPDone(t *testing.T, h2 bool) {
 	}
 }
 
+// Tests that the Request.Context available to the Handler is canceled
+// if the peer closes their TCP connection. This requires that the server
+// is always blocked in a Read call so it notices the EOF from the client.
+// See issues 15927 and 15224.
 func TestServerRequestContextCancel_ConnClose(t *testing.T) {
-	// Currently the context is not canceled when the connection
-	// is closed because we're not reading from the connection
-	// until after ServeHTTP for the previous handler is done.
-	// Until the server code is modified to always be in a read
-	// (Issue 15224), this test doesn't work yet.
-	t.Skip("TODO(bradfitz): this test doesn't yet work; golang.org/issue/15224")
+	setParallel(t)
 	defer afterTest(t)
 	inHandler := make(chan struct{})
 	handlerDone := make(chan struct{})
@@ -4192,7 +4302,7 @@ func TestServerRequestContextCancel_ConnClose(t *testing.T) {
 
 	select {
 	case <-handlerDone:
-	case <-time.After(3 * time.Second):
+	case <-time.After(4 * time.Second):
 		t.Fatalf("timeout waiting to see ServeHTTP exit")
 	}
 }
@@ -4204,6 +4314,7 @@ func TestServerContext_ServerContextKey_h2(t *testing.T) {
 	testServerContext_ServerContextKey(t, h2Mode)
 }
 func testServerContext_ServerContextKey(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		ctx := r.Context()
@@ -4229,6 +4340,7 @@ func testServerContext_ServerContextKey(t *testing.T, h2 bool) {
 
 // https://golang.org/issue/15960
 func TestHandlerSetTransferEncodingChunked(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
 		w.Header().Set("Transfer-Encoding", "chunked")
@@ -4243,6 +4355,7 @@ func TestHandlerSetTransferEncodingChunked(t *testing.T) {
 
 // https://golang.org/issue/16063
 func TestHandlerSetTransferEncodingGzip(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
 		w.Header().Set("Transfer-Encoding", "gzip")
@@ -4416,13 +4529,19 @@ func BenchmarkClient(b *testing.B) {
 	b.StopTimer()
 	defer afterTest(b)
 
-	port := os.Getenv("TEST_BENCH_SERVER_PORT") // can be set by user
-	if port == "" {
-		port = "39207"
-	}
 	var data = []byte("Hello world.\n")
 	if server := os.Getenv("TEST_BENCH_SERVER"); server != "" {
 		// Server process mode.
+		port := os.Getenv("TEST_BENCH_SERVER_PORT") // can be set by user
+		if port == "" {
+			port = "0"
+		}
+		ln, err := net.Listen("tcp", "localhost:"+port)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err.Error())
+			os.Exit(1)
+		}
+		fmt.Println(ln.Addr().String())
 		HandleFunc("/", func(w ResponseWriter, r *Request) {
 			r.ParseForm()
 			if r.Form.Get("stop") != "" {
@@ -4431,33 +4550,44 @@ func BenchmarkClient(b *testing.B) {
 			w.Header().Set("Content-Type", "text/html; charset=utf-8")
 			w.Write(data)
 		})
-		log.Fatal(ListenAndServe("localhost:"+port, nil))
+		var srv Server
+		log.Fatal(srv.Serve(ln))
 	}
 
 	// Start server process.
 	cmd := exec.Command(os.Args[0], "-test.run=XXXX", "-test.bench=BenchmarkClient$")
 	cmd.Env = append(os.Environ(), "TEST_BENCH_SERVER=yes")
+	cmd.Stderr = os.Stderr
+	stdout, err := cmd.StdoutPipe()
+	if err != nil {
+		b.Fatal(err)
+	}
 	if err := cmd.Start(); err != nil {
 		b.Fatalf("subprocess failed to start: %v", err)
 	}
 	defer cmd.Process.Kill()
+
+	// Wait for the server in the child process to respond and tell us
+	// its listening address, once it's started listening:
+	timer := time.AfterFunc(10*time.Second, func() {
+		cmd.Process.Kill()
+	})
+	defer timer.Stop()
+	bs := bufio.NewScanner(stdout)
+	if !bs.Scan() {
+		b.Fatalf("failed to read listening URL from child: %v", bs.Err())
+	}
+	url := "http://" + strings.TrimSpace(bs.Text()) + "/"
+	timer.Stop()
+	if _, err := getNoBody(url); err != nil {
+		b.Fatalf("initial probe of child process failed: %v", err)
+	}
+
 	done := make(chan error)
 	go func() {
 		done <- cmd.Wait()
 	}()
 
-	// Wait for the server process to respond.
-	url := "http://localhost:" + port + "/"
-	for i := 0; i < 100; i++ {
-		time.Sleep(100 * time.Millisecond)
-		if _, err := getNoBody(url); err == nil {
-			break
-		}
-		if i == 99 {
-			b.Fatalf("subprocess does not respond")
-		}
-	}
-
 	// Do b.N requests to the server.
 	b.StartTimer()
 	for i := 0; i < b.N; i++ {
@@ -4719,6 +4849,7 @@ func BenchmarkCloseNotifier(b *testing.B) {
 
 // Verify this doesn't race (Issue 16505)
 func TestConcurrentServerServe(t *testing.T) {
+	setParallel(t)
 	for i := 0; i < 100; i++ {
 		ln1 := &oneConnListener{conn: nil}
 		ln2 := &oneConnListener{conn: nil}
@@ -4727,3 +4858,183 @@ func TestConcurrentServerServe(t *testing.T) {
 		go func() { srv.Serve(ln2) }()
 	}
 }
+
+func TestServerIdleTimeout(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping in short mode")
+	}
+	setParallel(t)
+	defer afterTest(t)
+	ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+		io.Copy(ioutil.Discard, r.Body)
+		io.WriteString(w, r.RemoteAddr)
+	}))
+	ts.Config.ReadHeaderTimeout = 1 * time.Second
+	ts.Config.IdleTimeout = 2 * time.Second
+	ts.Start()
+	defer ts.Close()
+
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+	c := &Client{Transport: tr}
+
+	get := func() string {
+		res, err := c.Get(ts.URL)
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer res.Body.Close()
+		slurp, err := ioutil.ReadAll(res.Body)
+		if err != nil {
+			t.Fatal(err)
+		}
+		return string(slurp)
+	}
+
+	a1, a2 := get(), get()
+	if a1 != a2 {
+		t.Fatalf("did requests on different connections")
+	}
+	time.Sleep(3 * time.Second)
+	a3 := get()
+	if a2 == a3 {
+		t.Fatal("request three unexpectedly on same connection")
+	}
+
+	// And test that ReadHeaderTimeout still works:
+	conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer conn.Close()
+	conn.Write([]byte("GET / HTTP/1.1\r\nHost: foo.com\r\n"))
+	time.Sleep(2 * time.Second)
+	if _, err := io.CopyN(ioutil.Discard, conn, 1); err == nil {
+		t.Fatal("copy byte succeeded; want err")
+	}
+}
+
+func get(t *testing.T, c *Client, url string) string {
+	res, err := c.Get(url)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer res.Body.Close()
+	slurp, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return string(slurp)
+}
+
+// Tests that calls to Server.SetKeepAlivesEnabled(false) closes any
+// currently-open connections.
+func TestServerSetKeepAlivesEnabledClosesConns(t *testing.T) {
+	setParallel(t)
+	defer afterTest(t)
+	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+		io.WriteString(w, r.RemoteAddr)
+	}))
+	defer ts.Close()
+
+	tr := &Transport{}
+	defer tr.CloseIdleConnections()
+	c := &Client{Transport: tr}
+
+	get := func() string { return get(t, c, ts.URL) }
+
+	a1, a2 := get(), get()
+	if a1 != a2 {
+		t.Fatal("expected first two requests on same connection")
+	}
+	var idle0 int
+	if !waitCondition(2*time.Second, 10*time.Millisecond, func() bool {
+		idle0 = tr.IdleConnKeyCountForTesting()
+		return idle0 == 1
+	}) {
+		t.Fatalf("idle count before SetKeepAlivesEnabled called = %v; want 1", idle0)
+	}
+
+	ts.Config.SetKeepAlivesEnabled(false)
+
+	var idle1 int
+	if !waitCondition(2*time.Second, 10*time.Millisecond, func() bool {
+		idle1 = tr.IdleConnKeyCountForTesting()
+		return idle1 == 0
+	}) {
+		t.Fatalf("idle count after SetKeepAlivesEnabled called = %v; want 0", idle1)
+	}
+
+	a3 := get()
+	if a3 == a2 {
+		t.Fatal("expected third request on new connection")
+	}
+}
+
+func TestServerShutdown_h1(t *testing.T) { testServerShutdown(t, h1Mode) }
+func TestServerShutdown_h2(t *testing.T) { testServerShutdown(t, h2Mode) }
+
+func testServerShutdown(t *testing.T, h2 bool) {
+	setParallel(t)
+	defer afterTest(t)
+	var doShutdown func() // set later
+	var shutdownRes = make(chan error, 1)
+	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
+		go doShutdown()
+		// Shutdown is graceful, so it should not interrupt
+		// this in-flight response. Add a tiny sleep here to
+		// increase the odds of a failure if shutdown has
+		// bugs.
+		time.Sleep(20 * time.Millisecond)
+		io.WriteString(w, r.RemoteAddr)
+	}))
+	defer cst.close()
+
+	doShutdown = func() {
+		shutdownRes <- cst.ts.Config.Shutdown(context.Background())
+	}
+	get(t, cst.c, cst.ts.URL) // calls t.Fail on failure
+
+	if err := <-shutdownRes; err != nil {
+		t.Fatalf("Shutdown: %v", err)
+	}
+
+	res, err := cst.c.Get(cst.ts.URL)
+	if err == nil {
+		res.Body.Close()
+		t.Fatal("second request should fail. server should be shut down")
+	}
+}
+
+// Issue 17878: tests that we can call Close twice.
+func TestServerCloseDeadlock(t *testing.T) {
+	var s Server
+	s.Close()
+	s.Close()
+}
+
+// Issue 17717: tests that Server.SetKeepAlivesEnabled is respected by
+// both HTTP/1 and HTTP/2.
+func TestServerKeepAlivesEnabled_h1(t *testing.T) { testServerKeepAlivesEnabled(t, h1Mode) }
+func TestServerKeepAlivesEnabled_h2(t *testing.T) { testServerKeepAlivesEnabled(t, h2Mode) }
+func testServerKeepAlivesEnabled(t *testing.T, h2 bool) {
+	setParallel(t)
+	defer afterTest(t)
+	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
+		fmt.Fprintf(w, "%v", r.RemoteAddr)
+	}))
+	defer cst.close()
+	srv := cst.ts.Config
+	srv.SetKeepAlivesEnabled(false)
+	a := cst.getURL(cst.ts.URL)
+	if !waitCondition(2*time.Second, 10*time.Millisecond, srv.ExportAllConnsIdle) {
+		t.Fatalf("test server has active conns")
+	}
+	b := cst.getURL(cst.ts.URL)
+	if a == b {
+		t.Errorf("got same connection between first and second requests")
+	}
+	if !waitCondition(2*time.Second, 10*time.Millisecond, srv.ExportAllConnsIdle) {
+		t.Fatalf("test server has active conns")
+	}
+}
diff --git a/src/net/http/server.go b/src/net/http/server.go
index 89574a8..6df9c26 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -40,7 +40,9 @@ var (
 
 	// ErrHijacked is returned by ResponseWriter.Write calls when
 	// the underlying connection has been hijacked using the
-	// Hijacker interfaced.
+	// Hijacker interface. A zero-byte write on a hijacked
+	// connection will return ErrHijacked without any other side
+	// effects.
 	ErrHijacked = errors.New("http: connection has been hijacked")
 
 	// ErrContentLength is returned by ResponseWriter.Write calls
@@ -73,7 +75,9 @@ var (
 // If ServeHTTP panics, the server (the caller of ServeHTTP) assumes
 // that the effect of the panic was isolated to the active request.
 // It recovers the panic, logs a stack trace to the server error log,
-// and hangs up the connection.
+// and hangs up the connection. To abort a handler so the client sees
+// an interrupted response but the server doesn't log an error, panic
+// with the value ErrAbortHandler.
 type Handler interface {
 	ServeHTTP(ResponseWriter, *Request)
 }
@@ -85,11 +89,25 @@ type Handler interface {
 // has returned.
 type ResponseWriter interface {
 	// Header returns the header map that will be sent by
-	// WriteHeader. Changing the header after a call to
-	// WriteHeader (or Write) has no effect unless the modified
-	// headers were declared as trailers by setting the
-	// "Trailer" header before the call to WriteHeader (see example).
-	// To suppress implicit response headers, set their value to nil.
+	// WriteHeader. The Header map also is the mechanism with which
+	// Handlers can set HTTP trailers.
+	//
+	// Changing the header map after a call to WriteHeader (or
+	// Write) has no effect unless the modified headers are
+	// trailers.
+	//
+	// There are two ways to set Trailers. The preferred way is to
+	// predeclare in the headers which trailers you will later
+	// send by setting the "Trailer" header to the names of the
+	// trailer keys which will come later. In this case, those
+	// keys of the Header map are treated as if they were
+	// trailers. See the example. The second way, for trailer
+	// keys not known to the Handler until after the first Write,
+	// is to prefix the Header map keys with the TrailerPrefix
+	// constant value. See TrailerPrefix.
+	//
+	// To suppress implicit response headers (such as "Date"), set
+	// their value to nil.
 	Header() Header
 
 	// Write writes the data to the connection as part of an HTTP reply.
@@ -206,6 +224,9 @@ type conn struct {
 	// Immutable; never nil.
 	server *Server
 
+	// cancelCtx cancels the connection-level context.
+	cancelCtx context.CancelFunc
+
 	// rwc is the underlying network connection.
 	// This is never wrapped by other types and is the value given out
 	// to CloseNotifier callers. It is usually of type *net.TCPConn or
@@ -232,7 +253,6 @@ type conn struct {
 	r *connReader
 
 	// bufr reads from r.
-	// Users of bufr must hold mu.
 	bufr *bufio.Reader
 
 	// bufw writes to checkConnErrorWriter{c}, which populates werr on error.
@@ -242,7 +262,11 @@ type conn struct {
 	// on this connection, if any.
 	lastMethod string
 
-	// mu guards hijackedv, use of bufr, (*response).closeNotifyCh.
+	curReq atomic.Value // of *response (which has a Request in it)
+
+	curState atomic.Value // of ConnState
+
+	// mu guards hijackedv
 	mu sync.Mutex
 
 	// hijackedv is whether this connection has been hijacked
@@ -262,8 +286,12 @@ func (c *conn) hijackLocked() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
 	if c.hijackedv {
 		return nil, nil, ErrHijacked
 	}
+	c.r.abortPendingRead()
+
 	c.hijackedv = true
 	rwc = c.rwc
+	rwc.SetDeadline(time.Time{})
+
 	buf = bufio.NewReadWriter(c.bufr, bufio.NewWriter(rwc))
 	c.setState(rwc, StateHijacked)
 	return
@@ -346,13 +374,7 @@ func (cw *chunkWriter) close() {
 		bw := cw.res.conn.bufw // conn's bufio writer
 		// zero chunk to mark EOF
 		bw.WriteString("0\r\n")
-		if len(cw.res.trailers) > 0 {
-			trailers := make(Header)
-			for _, h := range cw.res.trailers {
-				if vv := cw.res.handlerHeader[h]; len(vv) > 0 {
-					trailers[h] = vv
-				}
-			}
+		if trailers := cw.res.finalTrailers(); trailers != nil {
 			trailers.Write(bw) // the writer handles noting errors
 		}
 		// final blank line after the trailers (whether
@@ -413,9 +435,48 @@ type response struct {
 	dateBuf [len(TimeFormat)]byte
 	clenBuf [10]byte
 
-	// closeNotifyCh is non-nil once CloseNotify is called.
-	// Guarded by conn.mu
-	closeNotifyCh <-chan bool
+	// closeNotifyCh is the channel returned by CloseNotify.
+	// TODO(bradfitz): this is currently (for Go 1.8) always
+	// non-nil. Make this lazily-created again as it used to be?
+	closeNotifyCh  chan bool
+	didCloseNotify int32 // atomic (only 0->1 winner should send)
+}
+
+// TrailerPrefix is a magic prefix for ResponseWriter.Header map keys
+// that, if present, signals that the map entry is actually for
+// the response trailers, and not the response headers. The prefix
+// is stripped after the ServeHTTP call finishes and the values are
+// sent in the trailers.
+//
+// This mechanism is intended only for trailers that are not known
+// prior to the headers being written. If the set of trailers is fixed
+// or known before the header is written, the normal Go trailers mechanism
+// is preferred:
+//    https://golang.org/pkg/net/http/#ResponseWriter
+//    https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
+const TrailerPrefix = "Trailer:"
+
+// finalTrailers is called after the Handler exits and returns a non-nil
+// value if the Handler set any trailers.
+func (w *response) finalTrailers() Header {
+	var t Header
+	for k, vv := range w.handlerHeader {
+		if strings.HasPrefix(k, TrailerPrefix) {
+			if t == nil {
+				t = make(Header)
+			}
+			t[strings.TrimPrefix(k, TrailerPrefix)] = vv
+		}
+	}
+	for _, k := range w.trailers {
+		if t == nil {
+			t = make(Header)
+		}
+		for _, v := range w.handlerHeader[k] {
+			t.Add(k, v)
+		}
+	}
+	return t
 }
 
 type atomicBool int32
@@ -548,60 +609,148 @@ type readResult struct {
 // call blocked in a background goroutine to wait for activity and
 // trigger a CloseNotifier channel.
 type connReader struct {
-	r      io.Reader
-	remain int64 // bytes remaining
+	conn *conn
+
+	mu      sync.Mutex // guards following
+	hasByte bool
+	byteBuf [1]byte
+	bgErr   error // non-nil means error happened on background read
+	cond    *sync.Cond
+	inRead  bool
+	aborted bool  // set true before conn.rwc deadline is set to past
+	remain  int64 // bytes remaining
+}
+
+func (cr *connReader) lock() {
+	cr.mu.Lock()
+	if cr.cond == nil {
+		cr.cond = sync.NewCond(&cr.mu)
+	}
+}
+
+func (cr *connReader) unlock() { cr.mu.Unlock() }
+
+func (cr *connReader) startBackgroundRead() {
+	cr.lock()
+	defer cr.unlock()
+	if cr.inRead {
+		panic("invalid concurrent Body.Read call")
+	}
+	cr.inRead = true
+	go cr.backgroundRead()
+}
+
+func (cr *connReader) backgroundRead() {
+	n, err := cr.conn.rwc.Read(cr.byteBuf[:])
+	cr.lock()
+	if n == 1 {
+		cr.hasByte = true
+		// We were at EOF already (since we wouldn't be in a
+		// background read otherwise), so this is a pipelined
+		// HTTP request.
+		cr.closeNotifyFromPipelinedRequest()
+	}
+	if ne, ok := err.(net.Error); ok && cr.aborted && ne.Timeout() {
+		// Ignore this error. It's the expected error from
+		// another goroutine calling abortPendingRead.
+	} else if err != nil {
+		cr.handleReadError(err)
+	}
+	cr.aborted = false
+	cr.inRead = false
+	cr.unlock()
+	cr.cond.Broadcast()
+}
 
-	// ch is non-nil if a background read is in progress.
-	// It is guarded by conn.mu.
-	ch chan readResult
+func (cr *connReader) abortPendingRead() {
+	cr.lock()
+	defer cr.unlock()
+	if !cr.inRead {
+		return
+	}
+	cr.aborted = true
+	cr.conn.rwc.SetReadDeadline(aLongTimeAgo)
+	for cr.inRead {
+		cr.cond.Wait()
+	}
+	cr.conn.rwc.SetReadDeadline(time.Time{})
 }
 
 func (cr *connReader) setReadLimit(remain int64) { cr.remain = remain }
 func (cr *connReader) setInfiniteReadLimit()     { cr.remain = maxInt64 }
 func (cr *connReader) hitReadLimit() bool        { return cr.remain <= 0 }
 
+// may be called from multiple goroutines.
+func (cr *connReader) handleReadError(err error) {
+	cr.conn.cancelCtx()
+	cr.closeNotify()
+}
+
+// closeNotifyFromPipelinedRequest simply calls closeNotify.
+//
+// This method wrapper is here for documentation. The callers are the
+// cases where we send on the closenotify channel because of a
+// pipelined HTTP request, per the previous Go behavior and
+// documentation (that this "MAY" happen).
+//
+// TODO: consider changing this behavior and making context
+// cancelation and closenotify work the same.
+func (cr *connReader) closeNotifyFromPipelinedRequest() {
+	cr.closeNotify()
+}
+
+// may be called from multiple goroutines.
+func (cr *connReader) closeNotify() {
+	res, _ := cr.conn.curReq.Load().(*response)
+	if res != nil {
+		if atomic.CompareAndSwapInt32(&res.didCloseNotify, 0, 1) {
+			res.closeNotifyCh <- true
+		}
+	}
+}
+
 func (cr *connReader) Read(p []byte) (n int, err error) {
+	cr.lock()
+	if cr.inRead {
+		cr.unlock()
+		panic("invalid concurrent Body.Read call")
+	}
 	if cr.hitReadLimit() {
+		cr.unlock()
 		return 0, io.EOF
 	}
+	if cr.bgErr != nil {
+		err = cr.bgErr
+		cr.unlock()
+		return 0, err
+	}
 	if len(p) == 0 {
-		return
+		cr.unlock()
+		return 0, nil
 	}
 	if int64(len(p)) > cr.remain {
 		p = p[:cr.remain]
 	}
-
-	// Is a background read (started by CloseNotifier) already in
-	// flight? If so, wait for it and use its result.
-	ch := cr.ch
-	if ch != nil {
-		cr.ch = nil
-		res := <-ch
-		if res.n == 1 {
-			p[0] = res.b
-			cr.remain -= 1
-		}
-		return res.n, res.err
+	if cr.hasByte {
+		p[0] = cr.byteBuf[0]
+		cr.hasByte = false
+		cr.unlock()
+		return 1, nil
 	}
-	n, err = cr.r.Read(p)
-	cr.remain -= int64(n)
-	return
-}
+	cr.inRead = true
+	cr.unlock()
+	n, err = cr.conn.rwc.Read(p)
 
-func (cr *connReader) startBackgroundRead(onReadComplete func()) {
-	if cr.ch != nil {
-		// Background read already started.
-		return
+	cr.lock()
+	cr.inRead = false
+	if err != nil {
+		cr.handleReadError(err)
 	}
-	cr.ch = make(chan readResult, 1)
-	go cr.closeNotifyAwaitActivityRead(cr.ch, onReadComplete)
-}
+	cr.remain -= int64(n)
+	cr.unlock()
 
-func (cr *connReader) closeNotifyAwaitActivityRead(ch chan<- readResult, onReadComplete func()) {
-	var buf [1]byte
-	n, err := cr.r.Read(buf[:1])
-	onReadComplete()
-	ch <- readResult{n, err, buf[0]}
+	cr.cond.Broadcast()
+	return n, err
 }
 
 var (
@@ -633,7 +782,7 @@ func newBufioReader(r io.Reader) *bufio.Reader {
 		br.Reset(r)
 		return br
 	}
-	// Note: if this reader size is every changed, update
+	// Note: if this reader size is ever changed, update
 	// TestHandlerBodyClose's assumptions.
 	return bufio.NewReader(r)
 }
@@ -746,9 +895,18 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
 		return nil, ErrHijacked
 	}
 
+	var (
+		wholeReqDeadline time.Time // or zero if none
+		hdrDeadline      time.Time // or zero if none
+	)
+	t0 := time.Now()
+	if d := c.server.readHeaderTimeout(); d != 0 {
+		hdrDeadline = t0.Add(d)
+	}
 	if d := c.server.ReadTimeout; d != 0 {
-		c.rwc.SetReadDeadline(time.Now().Add(d))
+		wholeReqDeadline = t0.Add(d)
 	}
+	c.rwc.SetReadDeadline(hdrDeadline)
 	if d := c.server.WriteTimeout; d != 0 {
 		defer func() {
 			c.rwc.SetWriteDeadline(time.Now().Add(d))
@@ -756,14 +914,12 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
 	}
 
 	c.r.setReadLimit(c.server.initialReadLimitSize())
-	c.mu.Lock() // while using bufr
 	if c.lastMethod == "POST" {
 		// RFC 2616 section 4.1 tolerance for old buggy clients.
 		peek, _ := c.bufr.Peek(4) // ReadRequest will get err below
 		c.bufr.Discard(numLeadingCRorLF(peek))
 	}
 	req, err := readRequest(c.bufr, keepHostHeader)
-	c.mu.Unlock()
 	if err != nil {
 		if c.r.hitReadLimit() {
 			return nil, errTooLarge
@@ -809,6 +965,11 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
 		body.doEarlyClose = true
 	}
 
+	// Adjust the read deadline if necessary.
+	if !hdrDeadline.Equal(wholeReqDeadline) {
+		c.rwc.SetReadDeadline(wholeReqDeadline)
+	}
+
 	w = &response{
 		conn:          c,
 		cancelCtx:     cancelCtx,
@@ -816,6 +977,7 @@ func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
 		reqBody:       req.Body,
 		handlerHeader: make(Header),
 		contentLength: -1,
+		closeNotifyCh: make(chan bool, 1),
 
 		// We populate these ahead of time so we're not
 		// reading from req.Header after their Handler starts
@@ -990,7 +1152,17 @@ func (cw *chunkWriter) writeHeader(p []byte) {
 	}
 	var setHeader extraHeader
 
+	// Don't write out the fake "Trailer:foo" keys. See TrailerPrefix.
 	trailers := false
+	for k := range cw.header {
+		if strings.HasPrefix(k, TrailerPrefix) {
+			if excludeHeader == nil {
+				excludeHeader = make(map[string]bool)
+			}
+			excludeHeader[k] = true
+			trailers = true
+		}
+	}
 	for _, v := range cw.header["Trailer"] {
 		trailers = true
 		foreachHeaderElement(v, cw.res.declareTrailer)
@@ -1318,7 +1490,9 @@ func (w *response) WriteString(data string) (n int, err error) {
 // either dataB or dataS is non-zero.
 func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err error) {
 	if w.conn.hijacked() {
-		w.conn.server.logf("http: response.Write on hijacked connection")
+		if lenData > 0 {
+			w.conn.server.logf("http: response.Write on hijacked connection")
+		}
 		return 0, ErrHijacked
 	}
 	if !w.wroteHeader {
@@ -1354,6 +1528,8 @@ func (w *response) finishRequest() {
 	w.cw.close()
 	w.conn.bufw.Flush()
 
+	w.conn.r.abortPendingRead()
+
 	// Close the body (regardless of w.closeAfterReply) so we can
 	// re-use its bufio.Reader later safely.
 	w.reqBody.Close()
@@ -1469,11 +1645,30 @@ func validNPN(proto string) bool {
 }
 
 func (c *conn) setState(nc net.Conn, state ConnState) {
-	if hook := c.server.ConnState; hook != nil {
+	srv := c.server
+	switch state {
+	case StateNew:
+		srv.trackConn(c, true)
+	case StateHijacked, StateClosed:
+		srv.trackConn(c, false)
+	}
+	c.curState.Store(connStateInterface[state])
+	if hook := srv.ConnState; hook != nil {
 		hook(nc, state)
 	}
 }
 
+// connStateInterface is an array of the interface{} versions of
+// ConnState values, so we can use them in atomic.Values later without
+// paying the cost of shoving their integers in an interface{}.
+var connStateInterface = [...]interface{}{
+	StateNew:      StateNew,
+	StateActive:   StateActive,
+	StateIdle:     StateIdle,
+	StateHijacked: StateHijacked,
+	StateClosed:   StateClosed,
+}
+
 // badRequestError is a literal string (used by in the server in HTML,
 // unescaped) to tell the user why their request was bad. It should
 // be plain text without user info or other embedded errors.
@@ -1481,11 +1676,34 @@ type badRequestError string
 
 func (e badRequestError) Error() string { return "Bad Request: " + string(e) }
 
+// ErrAbortHandler is a sentinel panic value to abort a handler.
+// While any panic from ServeHTTP aborts the response to the client,
+// panicking with ErrAbortHandler also suppresses logging of a stack
+// trace to the server's error log.
+var ErrAbortHandler = errors.New("net/http: abort Handler")
+
+// isCommonNetReadError reports whether err is a common error
+// encountered during reading a request off the network when the
+// client has gone away or had its read fail somehow. This is used to
+// determine which logs are interesting enough to log about.
+func isCommonNetReadError(err error) bool {
+	if err == io.EOF {
+		return true
+	}
+	if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
+		return true
+	}
+	if oe, ok := err.(*net.OpError); ok && oe.Op == "read" {
+		return true
+	}
+	return false
+}
+
 // Serve a new connection.
 func (c *conn) serve(ctx context.Context) {
 	c.remoteAddr = c.rwc.RemoteAddr().String()
 	defer func() {
-		if err := recover(); err != nil {
+		if err := recover(); err != nil && err != ErrAbortHandler {
 			const size = 64 << 10
 			buf := make([]byte, size)
 			buf = buf[:runtime.Stack(buf, false)]
@@ -1521,13 +1739,14 @@ func (c *conn) serve(ctx context.Context) {
 
 	// HTTP/1.x from here on.
 
-	c.r = &connReader{r: c.rwc}
-	c.bufr = newBufioReader(c.r)
-	c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
-
 	ctx, cancelCtx := context.WithCancel(ctx)
+	c.cancelCtx = cancelCtx
 	defer cancelCtx()
 
+	c.r = &connReader{conn: c}
+	c.bufr = newBufioReader(c.r)
+	c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
+
 	for {
 		w, err := c.readRequest(ctx)
 		if c.r.remain != c.server.initialReadLimitSize() {
@@ -1535,27 +1754,29 @@ func (c *conn) serve(ctx context.Context) {
 			c.setState(c.rwc, StateActive)
 		}
 		if err != nil {
+			const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n"
+
 			if err == errTooLarge {
 				// Their HTTP client may or may not be
 				// able to read this if we're
 				// responding to them and hanging up
 				// while they're still writing their
 				// request. Undefined behavior.
-				io.WriteString(c.rwc, "HTTP/1.1 431 Request Header Fields Too Large\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n431 Request Header Fields Too Large")
+				const publicErr = "431 Request Header Fields Too Large"
+				fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
 				c.closeWriteAndWait()
 				return
 			}
-			if err == io.EOF {
-				return // don't reply
-			}
-			if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
+			if isCommonNetReadError(err) {
 				return // don't reply
 			}
-			var publicErr string
+
+			publicErr := "400 Bad Request"
 			if v, ok := err.(badRequestError); ok {
-				publicErr = ": " + string(v)
+				publicErr = publicErr + ": " + string(v)
 			}
-			io.WriteString(c.rwc, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n400 Bad Request"+publicErr)
+
+			fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
 			return
 		}
 
@@ -1571,11 +1792,24 @@ func (c *conn) serve(ctx context.Context) {
 			return
 		}
 
+		c.curReq.Store(w)
+
+		if requestBodyRemains(req.Body) {
+			registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
+		} else {
+			if w.conn.bufr.Buffered() > 0 {
+				w.conn.r.closeNotifyFromPipelinedRequest()
+			}
+			w.conn.r.startBackgroundRead()
+		}
+
 		// HTTP cannot have multiple simultaneous active requests.[*]
 		// Until the server replies to this request, it can't read another,
 		// so we might as well run the handler in this goroutine.
 		// [*] Not strictly true: HTTP pipelining. We could let them all process
 		// in parallel even if their responses need to be serialized.
+		// But we're not going to implement HTTP pipelining because it
+		// was never deployed in the wild and the answer is HTTP/2.
 		serverHandler{c.server}.ServeHTTP(w, w.req)
 		w.cancelCtx()
 		if c.hijacked() {
@@ -1589,6 +1823,23 @@ func (c *conn) serve(ctx context.Context) {
 			return
 		}
 		c.setState(c.rwc, StateIdle)
+		c.curReq.Store((*response)(nil))
+
+		if !w.conn.server.doKeepAlives() {
+			// We're in shutdown mode. We might've replied
+			// to the user without "Connection: close" and
+			// they might think they can send another
+			// request, but such is life with HTTP/1.1.
+			return
+		}
+
+		if d := c.server.idleTimeout(); d != 0 {
+			c.rwc.SetReadDeadline(time.Now().Add(d))
+			if _, err := c.bufr.Peek(4); err != nil {
+				return
+			}
+		}
+		c.rwc.SetReadDeadline(time.Time{})
 	}
 }
 
@@ -1624,10 +1875,6 @@ func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
-	if w.closeNotifyCh != nil {
-		return nil, nil, errors.New("http: Hijack is incompatible with use of CloseNotifier in same ServeHTTP call")
-	}
-
 	// Release the bufioWriter that writes to the chunk writer, it is not
 	// used after a connection has been hijacked.
 	rwc, buf, err = c.hijackLocked()
@@ -1642,50 +1889,7 @@ func (w *response) CloseNotify() <-chan bool {
 	if w.handlerDone.isSet() {
 		panic("net/http: CloseNotify called after ServeHTTP finished")
 	}
-	c := w.conn
-	c.mu.Lock()
-	defer c.mu.Unlock()
-
-	if w.closeNotifyCh != nil {
-		return w.closeNotifyCh
-	}
-	ch := make(chan bool, 1)
-	w.closeNotifyCh = ch
-
-	if w.conn.hijackedv {
-		// CloseNotify is undefined after a hijack, but we have
-		// no place to return an error, so just return a channel,
-		// even though it'll never receive a value.
-		return ch
-	}
-
-	var once sync.Once
-	notify := func() { once.Do(func() { ch <- true }) }
-
-	if requestBodyRemains(w.reqBody) {
-		// They're still consuming the request body, so we
-		// shouldn't notify yet.
-		registerOnHitEOF(w.reqBody, func() {
-			c.mu.Lock()
-			defer c.mu.Unlock()
-			startCloseNotifyBackgroundRead(c, notify)
-		})
-	} else {
-		startCloseNotifyBackgroundRead(c, notify)
-	}
-	return ch
-}
-
-// c.mu must be held.
-func startCloseNotifyBackgroundRead(c *conn, notify func()) {
-	if c.bufr.Buffered() > 0 {
-		// They've consumed the request body, so anything
-		// remaining is a pipelined request, which we
-		// document as firing on.
-		notify()
-	} else {
-		c.r.startBackgroundRead(notify)
-	}
+	return w.closeNotifyCh
 }
 
 func registerOnHitEOF(rc io.ReadCloser, fn func()) {
@@ -1702,7 +1906,7 @@ func registerOnHitEOF(rc io.ReadCloser, fn func()) {
 // requestBodyRemains reports whether future calls to Read
 // on rc might yield more data.
 func requestBodyRemains(rc io.ReadCloser) bool {
-	if rc == eofReader {
+	if rc == NoBody {
 		return false
 	}
 	switch v := rc.(type) {
@@ -1816,7 +2020,7 @@ func Redirect(w ResponseWriter, r *Request, urlStr string, code int) {
 		}
 	}
 
-	w.Header().Set("Location", urlStr)
+	w.Header().Set("Location", hexEscapeNonASCII(urlStr))
 	w.WriteHeader(code)
 
 	// RFC 2616 recommends that a short note "SHOULD" be included in the
@@ -2094,11 +2298,36 @@ func Serve(l net.Listener, handler Handler) error {
 // A Server defines parameters for running an HTTP server.
 // The zero value for Server is a valid configuration.
 type Server struct {
-	Addr         string        // TCP address to listen on, ":http" if empty
-	Handler      Handler       // handler to invoke, http.DefaultServeMux if nil
-	ReadTimeout  time.Duration // maximum duration before timing out read of the request
-	WriteTimeout time.Duration // maximum duration before timing out write of the response
-	TLSConfig    *tls.Config   // optional TLS config, used by ListenAndServeTLS
+	Addr      string      // TCP address to listen on, ":http" if empty
+	Handler   Handler     // handler to invoke, http.DefaultServeMux if nil
+	TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS
+
+	// ReadTimeout is the maximum duration for reading the entire
+	// request, including the body.
+	//
+	// Because ReadTimeout does not let Handlers make per-request
+	// decisions on each request body's acceptable deadline or
+	// upload rate, most users will prefer to use
+	// ReadHeaderTimeout. It is valid to use them both.
+	ReadTimeout time.Duration
+
+	// ReadHeaderTimeout is the amount of time allowed to read
+	// request headers. The connection's read deadline is reset
+	// after reading the headers and the Handler can decide what
+	// is considered too slow for the body.
+	ReadHeaderTimeout time.Duration
+
+	// WriteTimeout is the maximum duration before timing out
+	// writes of the response. It is reset whenever a new
+	// request's header is read. Like ReadTimeout, it does not
+	// let Handlers make decisions on a per-request basis.
+	WriteTimeout time.Duration
+
+	// IdleTimeout is the maximum amount of time to wait for the
+	// next request when keep-alives are enabled. If IdleTimeout
+	// is zero, the value of ReadTimeout is used. If both are
+	// zero, there is no timeout.
+	IdleTimeout time.Duration
 
 	// MaxHeaderBytes controls the maximum number of bytes the
 	// server will read parsing the request header's keys and
@@ -2114,7 +2343,8 @@ type Server struct {
 	// handle HTTP requests and will initialize the Request's TLS
 	// and RemoteAddr if not already set. The connection is
 	// automatically closed when the function returns.
-	// If TLSNextProto is nil, HTTP/2 support is enabled automatically.
+	// If TLSNextProto is not nil, HTTP/2 support is not enabled
+	// automatically.
 	TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
 
 	// ConnState specifies an optional callback function that is
@@ -2129,8 +2359,132 @@ type Server struct {
 	ErrorLog *log.Logger
 
 	disableKeepAlives int32     // accessed atomically.
+	inShutdown        int32     // accessed atomically (non-zero means we're in Shutdown)
 	nextProtoOnce     sync.Once // guards setupHTTP2_* init
 	nextProtoErr      error     // result of http2.ConfigureServer if used
+
+	mu         sync.Mutex
+	listeners  map[net.Listener]struct{}
+	activeConn map[*conn]struct{}
+	doneChan   chan struct{}
+}
+
+func (s *Server) getDoneChan() <-chan struct{} {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	return s.getDoneChanLocked()
+}
+
+func (s *Server) getDoneChanLocked() chan struct{} {
+	if s.doneChan == nil {
+		s.doneChan = make(chan struct{})
+	}
+	return s.doneChan
+}
+
+func (s *Server) closeDoneChanLocked() {
+	ch := s.getDoneChanLocked()
+	select {
+	case <-ch:
+		// Already closed. Don't close again.
+	default:
+		// Safe to close here. We're the only closer, guarded
+		// by s.mu.
+		close(ch)
+	}
+}
+
+// Close immediately closes all active net.Listeners and any
+// connections in state StateNew, StateActive, or StateIdle. For a
+// graceful shutdown, use Shutdown.
+//
+// Close does not attempt to close (and does not even know about)
+// any hijacked connections, such as WebSockets.
+//
+// Close returns any error returned from closing the Server's
+// underlying Listener(s).
+func (srv *Server) Close() error {
+	srv.mu.Lock()
+	defer srv.mu.Unlock()
+	srv.closeDoneChanLocked()
+	err := srv.closeListenersLocked()
+	for c := range srv.activeConn {
+		c.rwc.Close()
+		delete(srv.activeConn, c)
+	}
+	return err
+}
+
+// shutdownPollInterval is how often we poll for quiescence
+// during Server.Shutdown. This is lower during tests, to
+// speed up tests.
+// Ideally we could find a solution that doesn't involve polling,
+// but which also doesn't have a high runtime cost (and doesn't
+// involve any contentious mutexes), but that is left as an
+// exercise for the reader.
+var shutdownPollInterval = 500 * time.Millisecond
+
+// Shutdown gracefully shuts down the server without interrupting any
+// active connections. Shutdown works by first closing all open
+// listeners, then closing all idle connections, and then waiting
+// indefinitely for connections to return to idle and then shut down.
+// If the provided context expires before the shutdown is complete,
+// then the context's error is returned.
+//
+// Shutdown does not attempt to close nor wait for hijacked
+// connections such as WebSockets. The caller of Shutdown should
+// separately notify such long-lived connections of shutdown and wait
+// for them to close, if desired.
+func (srv *Server) Shutdown(ctx context.Context) error {
+	atomic.AddInt32(&srv.inShutdown, 1)
+	defer atomic.AddInt32(&srv.inShutdown, -1)
+
+	srv.mu.Lock()
+	lnerr := srv.closeListenersLocked()
+	srv.closeDoneChanLocked()
+	srv.mu.Unlock()
+
+	ticker := time.NewTicker(shutdownPollInterval)
+	defer ticker.Stop()
+	for {
+		if srv.closeIdleConns() {
+			return lnerr
+		}
+		select {
+		case <-ctx.Done():
+			return ctx.Err()
+		case <-ticker.C:
+		}
+	}
+}
+
+// closeIdleConns closes all idle connections and reports whether the
+// server is quiescent.
+func (s *Server) closeIdleConns() bool {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	quiescent := true
+	for c := range s.activeConn {
+		st, ok := c.curState.Load().(ConnState)
+		if !ok || st != StateIdle {
+			quiescent = false
+			continue
+		}
+		c.rwc.Close()
+		delete(s.activeConn, c)
+	}
+	return quiescent
+}
+
+func (s *Server) closeListenersLocked() error {
+	var err error
+	for ln := range s.listeners {
+		if cerr := ln.Close(); cerr != nil && err == nil {
+			err = cerr
+		}
+		delete(s.listeners, ln)
+	}
+	return err
 }
 
 // A ConnState represents the state of a client connection to a server.
@@ -2243,6 +2597,8 @@ func (srv *Server) shouldConfigureHTTP2ForServe() bool {
 	return strSliceContains(srv.TLSConfig.NextProtos, http2NextProtoTLS)
 }
 
+var ErrServerClosed = errors.New("http: Server closed")
+
 // Serve accepts incoming connections on the Listener l, creating a
 // new service goroutine for each. The service goroutines read requests and
 // then call srv.Handler to reply to them.
@@ -2252,7 +2608,8 @@ func (srv *Server) shouldConfigureHTTP2ForServe() bool {
 // srv.TLSConfig is non-nil and doesn't include the string "h2" in
 // Config.NextProtos, HTTP/2 support is not enabled.
 //
-// Serve always returns a non-nil error.
+// Serve always returns a non-nil error. After Shutdown or Close, the
+// returned error is ErrServerClosed.
 func (srv *Server) Serve(l net.Listener) error {
 	defer l.Close()
 	if fn := testHookServerServe; fn != nil {
@@ -2264,14 +2621,20 @@ func (srv *Server) Serve(l net.Listener) error {
 		return err
 	}
 
-	// TODO: allow changing base context? can't imagine concrete
-	// use cases yet.
-	baseCtx := context.Background()
+	srv.trackListener(l, true)
+	defer srv.trackListener(l, false)
+
+	baseCtx := context.Background() // base is always background, per Issue 16220
 	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
 	ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
 	for {
 		rw, e := l.Accept()
 		if e != nil {
+			select {
+			case <-srv.getDoneChan():
+				return ErrServerClosed
+			default:
+			}
 			if ne, ok := e.(net.Error); ok && ne.Temporary() {
 				if tempDelay == 0 {
 					tempDelay = 5 * time.Millisecond
@@ -2294,8 +2657,57 @@ func (srv *Server) Serve(l net.Listener) error {
 	}
 }
 
+func (s *Server) trackListener(ln net.Listener, add bool) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	if s.listeners == nil {
+		s.listeners = make(map[net.Listener]struct{})
+	}
+	if add {
+		// If the *Server is being reused after a previous
+		// Close or Shutdown, reset its doneChan:
+		if len(s.listeners) == 0 && len(s.activeConn) == 0 {
+			s.doneChan = nil
+		}
+		s.listeners[ln] = struct{}{}
+	} else {
+		delete(s.listeners, ln)
+	}
+}
+
+func (s *Server) trackConn(c *conn, add bool) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	if s.activeConn == nil {
+		s.activeConn = make(map[*conn]struct{})
+	}
+	if add {
+		s.activeConn[c] = struct{}{}
+	} else {
+		delete(s.activeConn, c)
+	}
+}
+
+func (s *Server) idleTimeout() time.Duration {
+	if s.IdleTimeout != 0 {
+		return s.IdleTimeout
+	}
+	return s.ReadTimeout
+}
+
+func (s *Server) readHeaderTimeout() time.Duration {
+	if s.ReadHeaderTimeout != 0 {
+		return s.ReadHeaderTimeout
+	}
+	return s.ReadTimeout
+}
+
 func (s *Server) doKeepAlives() bool {
-	return atomic.LoadInt32(&s.disableKeepAlives) == 0
+	return atomic.LoadInt32(&s.disableKeepAlives) == 0 && !s.shuttingDown()
+}
+
+func (s *Server) shuttingDown() bool {
+	return atomic.LoadInt32(&s.inShutdown) != 0
 }
 
 // SetKeepAlivesEnabled controls whether HTTP keep-alives are enabled.
@@ -2305,9 +2717,21 @@ func (s *Server) doKeepAlives() bool {
 func (srv *Server) SetKeepAlivesEnabled(v bool) {
 	if v {
 		atomic.StoreInt32(&srv.disableKeepAlives, 0)
-	} else {
-		atomic.StoreInt32(&srv.disableKeepAlives, 1)
+		return
 	}
+	atomic.StoreInt32(&srv.disableKeepAlives, 1)
+
+	// Close idle HTTP/1 conns:
+	srv.closeIdleConns()
+
+	// Close HTTP/2 conns, as soon as they become idle, but reset
+	// the chan so future conns (if the listener is still active)
+	// still work and don't get a GOAWAY immediately, before their
+	// first request:
+	srv.mu.Lock()
+	defer srv.mu.Unlock()
+	srv.closeDoneChanLocked() // closes http2 conns
+	srv.doneChan = nil
 }
 
 func (s *Server) logf(format string, args ...interface{}) {
@@ -2630,24 +3054,6 @@ func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) {
 	}
 }
 
-type eofReaderWithWriteTo struct{}
-
-func (eofReaderWithWriteTo) WriteTo(io.Writer) (int64, error) { return 0, nil }
-func (eofReaderWithWriteTo) Read([]byte) (int, error)         { return 0, io.EOF }
-
-// eofReader is a non-nil io.ReadCloser that always returns EOF.
-// It has a WriteTo method so io.Copy won't need a buffer.
-var eofReader = &struct {
-	eofReaderWithWriteTo
-	io.Closer
-}{
-	eofReaderWithWriteTo{},
-	ioutil.NopCloser(nil),
-}
-
-// Verify that an io.Copy from an eofReader won't require a buffer.
-var _ io.WriterTo = eofReader
-
 // initNPNRequest is an HTTP handler that initializes certain
 // uninitialized fields in its *Request. Such partially-initialized
 // Requests come from NPN protocol handlers.
@@ -2662,7 +3068,7 @@ func (h initNPNRequest) ServeHTTP(rw ResponseWriter, req *Request) {
 		*req.TLS = h.c.ConnectionState()
 	}
 	if req.Body == nil {
-		req.Body = eofReader
+		req.Body = NoBody
 	}
 	if req.RemoteAddr == "" {
 		req.RemoteAddr = h.c.RemoteAddr().String()
@@ -2723,6 +3129,7 @@ func (w checkConnErrorWriter) Write(p []byte) (n int, err error) {
 	n, err = w.c.rwc.Write(p)
 	if err != nil && w.c.werr == nil {
 		w.c.werr = err
+		w.c.cancelCtx()
 	}
 	return
 }
diff --git a/src/net/http/sniff_test.go b/src/net/http/sniff_test.go
index ac404bf..38f3f81 100644
--- a/src/net/http/sniff_test.go
+++ b/src/net/http/sniff_test.go
@@ -66,6 +66,7 @@ func TestServerContentType_h1(t *testing.T) { testServerContentType(t, h1Mode) }
 func TestServerContentType_h2(t *testing.T) { testServerContentType(t, h2Mode) }
 
 func testServerContentType(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		i, _ := strconv.Atoi(r.FormValue("i"))
@@ -160,6 +161,7 @@ func testContentTypeWithCopy(t *testing.T, h2 bool) {
 func TestSniffWriteSize_h1(t *testing.T) { testSniffWriteSize(t, h1Mode) }
 func TestSniffWriteSize_h2(t *testing.T) { testSniffWriteSize(t, h2Mode) }
 func testSniffWriteSize(t *testing.T, h2 bool) {
+	setParallel(t)
 	defer afterTest(t)
 	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		size, _ := strconv.Atoi(r.FormValue("size"))
diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go
index c653467..beafb7a 100644
--- a/src/net/http/transfer.go
+++ b/src/net/http/transfer.go
@@ -59,37 +59,18 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
 			return nil, fmt.Errorf("http: Request.ContentLength=%d with nil Body", rr.ContentLength)
 		}
 		t.Method = valueOrDefault(rr.Method, "GET")
-		t.Body = rr.Body
-		t.BodyCloser = rr.Body
-		t.ContentLength = rr.ContentLength
 		t.Close = rr.Close
 		t.TransferEncoding = rr.TransferEncoding
 		t.Trailer = rr.Trailer
 		atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
-		if t.Body != nil && len(t.TransferEncoding) == 0 && atLeastHTTP11 {
-			if t.ContentLength == 0 {
-				// Test to see if it's actually zero or just unset.
-				var buf [1]byte
-				n, rerr := io.ReadFull(t.Body, buf[:])
-				if rerr != nil && rerr != io.EOF {
-					t.ContentLength = -1
-					t.Body = errorReader{rerr}
-				} else if n == 1 {
-					// Oh, guess there is data in this Body Reader after all.
-					// The ContentLength field just wasn't set.
-					// Stich the Body back together again, re-attaching our
-					// consumed byte.
-					t.ContentLength = -1
-					t.Body = io.MultiReader(bytes.NewReader(buf[:]), t.Body)
-				} else {
-					// Body is actually empty.
-					t.Body = nil
-					t.BodyCloser = nil
-				}
-			}
-			if t.ContentLength < 0 {
-				t.TransferEncoding = []string{"chunked"}
-			}
+
+		t.Body = rr.Body
+		t.ContentLength = rr.outgoingLength()
+		if t.Body != nil {
+			t.BodyCloser = rr.Body
+		}
+		if t.ContentLength < 0 && len(t.TransferEncoding) == 0 && atLeastHTTP11 {
+			t.TransferEncoding = []string{"chunked"}
 		}
 	case *Response:
 		t.IsResponse = true
@@ -214,7 +195,7 @@ func (t *transferWriter) WriteBody(w io.Writer) error {
 	if t.Body != nil {
 		if chunked(t.TransferEncoding) {
 			if bw, ok := w.(*bufio.Writer); ok && !t.IsResponse {
-				w = &internal.FlushAfterChunkWriter{bw}
+				w = &internal.FlushAfterChunkWriter{Writer: bw}
 			}
 			cw := internal.NewChunkedWriter(w)
 			_, err = io.Copy(cw, t.Body)
@@ -386,12 +367,12 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
 	switch {
 	case chunked(t.TransferEncoding):
 		if noBodyExpected(t.RequestMethod) {
-			t.Body = eofReader
+			t.Body = NoBody
 		} else {
 			t.Body = &body{src: internal.NewChunkedReader(r), hdr: msg, r: r, closing: t.Close}
 		}
 	case realLength == 0:
-		t.Body = eofReader
+		t.Body = NoBody
 	case realLength > 0:
 		t.Body = &body{src: io.LimitReader(r, realLength), closing: t.Close}
 	default:
@@ -401,7 +382,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
 			t.Body = &body{src: r, closing: t.Close}
 		} else {
 			// Persistent connection (i.e. HTTP/1.1)
-			t.Body = eofReader
+			t.Body = NoBody
 		}
 	}
 
@@ -493,8 +474,29 @@ func (t *transferReader) fixTransferEncoding() error {
 // function is not a method, because ultimately it should be shared by
 // ReadResponse and ReadRequest.
 func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, error) {
-	contentLens := header["Content-Length"]
 	isRequest := !isResponse
+	contentLens := header["Content-Length"]
+
+	// Hardening against HTTP request smuggling
+	if len(contentLens) > 1 {
+		// Per RFC 7230 Section 3.3.2, prevent multiple
+		// Content-Length headers if they differ in value.
+		// If there are dups of the value, remove the dups.
+		// See Issue 16490.
+		first := strings.TrimSpace(contentLens[0])
+		for _, ct := range contentLens[1:] {
+			if first != strings.TrimSpace(ct) {
+				return 0, fmt.Errorf("http: message cannot contain multiple Content-Length headers; got %q", contentLens)
+			}
+		}
+
+		// deduplicate Content-Length
+		header.Del("Content-Length")
+		header.Add("Content-Length", first)
+
+		contentLens = header["Content-Length"]
+	}
+
 	// Logic based on response type or status
 	if noBodyExpected(requestMethod) {
 		// For HTTP requests, as part of hardening against request
@@ -514,11 +516,6 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
 		return 0, nil
 	}
 
-	if len(contentLens) > 1 {
-		// harden against HTTP request smuggling. See RFC 7230.
-		return 0, errors.New("http: message cannot contain multiple Content-Length headers")
-	}
-
 	// Logic based on Transfer-Encoding
 	if chunked(te) {
 		return -1, nil
@@ -539,7 +536,7 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
 		header.Del("Content-Length")
 	}
 
-	if !isResponse {
+	if isRequest {
 		// RFC 2616 neither explicitly permits nor forbids an
 		// entity-body on a GET request so we permit one if
 		// declared, but we default to 0 here (not -1 below)
diff --git a/src/net/http/transport.go b/src/net/http/transport.go
index 1f07634..e484548 100644
--- a/src/net/http/transport.go
+++ b/src/net/http/transport.go
@@ -25,6 +25,7 @@ import (
 	"os"
 	"strings"
 	"sync"
+	"sync/atomic"
 	"time"
 
 	"golang_org/x/net/lex/httplex"
@@ -40,6 +41,7 @@ var DefaultTransport RoundTripper = &Transport{
 	DialContext: (&net.Dialer{
 		Timeout:   30 * time.Second,
 		KeepAlive: 30 * time.Second,
+		DualStack: true,
 	}).DialContext,
 	MaxIdleConns:          100,
 	IdleConnTimeout:       90 * time.Second,
@@ -66,8 +68,10 @@ const DefaultMaxIdleConnsPerHost = 2
 // For high-level functionality, such as cookies and redirects, see Client.
 //
 // Transport uses HTTP/1.1 for HTTP URLs and either HTTP/1.1 or HTTP/2
-// for HTTPS URLs, depending on whether the server supports HTTP/2.
-// See the package docs for more about HTTP/2.
+// for HTTPS URLs, depending on whether the server supports HTTP/2,
+// and how the Transport is configured. The DefaultTransport supports HTTP/2.
+// To explicitly enable HTTP/2 on a transport, use golang.org/x/net/http2
+// and call ConfigureTransport. See the package docs for more about HTTP/2.
 type Transport struct {
 	idleMu     sync.Mutex
 	wantIdle   bool                                // user has requested to close all idle conns
@@ -76,10 +80,10 @@ type Transport struct {
 	idleLRU    connLRU
 
 	reqMu       sync.Mutex
-	reqCanceler map[*Request]func()
+	reqCanceler map[*Request]func(error)
 
-	altMu    sync.RWMutex
-	altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
+	altMu    sync.Mutex   // guards changing altProto only
+	altProto atomic.Value // of nil or map[string]RoundTripper, key is URI scheme
 
 	// Proxy specifies a function to return a proxy for a given
 	// Request. If the function returns a non-nil error, the
@@ -111,7 +115,9 @@ type Transport struct {
 	DialTLS func(network, addr string) (net.Conn, error)
 
 	// TLSClientConfig specifies the TLS configuration to use with
-	// tls.Client. If nil, the default configuration is used.
+	// tls.Client.
+	// If nil, the default configuration is used.
+	// If non-nil, HTTP/2 support may not be enabled by default.
 	TLSClientConfig *tls.Config
 
 	// TLSHandshakeTimeout specifies the maximum amount of time waiting to
@@ -156,7 +162,9 @@ type Transport struct {
 	// ExpectContinueTimeout, if non-zero, specifies the amount of
 	// time to wait for a server's first response headers after fully
 	// writing the request headers if the request has an
-	// "Expect: 100-continue" header. Zero means no timeout.
+	// "Expect: 100-continue" header. Zero means no timeout and
+	// causes the body to be sent immediately, without
+	// waiting for the server to approve.
 	// This time does not include the time to send the request header.
 	ExpectContinueTimeout time.Duration
 
@@ -168,9 +176,14 @@ type Transport struct {
 	// called with the request's authority (such as "example.com"
 	// or "example.com:1234") and the TLS connection. The function
 	// must return a RoundTripper that then handles the request.
-	// If TLSNextProto is nil, HTTP/2 support is enabled automatically.
+	// If TLSNextProto is not nil, HTTP/2 support is not enabled
+	// automatically.
 	TLSNextProto map[string]func(authority string, c *tls.Conn) RoundTripper
 
+	// ProxyConnectHeader optionally specifies headers to send to
+	// proxies during CONNECT requests.
+	ProxyConnectHeader Header
+
 	// MaxResponseHeaderBytes specifies a limit on how many
 	// response bytes are allowed in the server's response
 	// header.
@@ -330,11 +343,9 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
 			}
 		}
 	}
-	// TODO(bradfitz): switch to atomic.Value for this map instead of RWMutex
-	t.altMu.RLock()
-	altRT := t.altProto[scheme]
-	t.altMu.RUnlock()
-	if altRT != nil {
+
+	altProto, _ := t.altProto.Load().(map[string]RoundTripper)
+	if altRT := altProto[scheme]; altRT != nil {
 		if resp, err := altRT.RoundTrip(req); err != ErrSkipAltProtocol {
 			return resp, err
 		}
@@ -421,19 +432,15 @@ func (pc *persistConn) shouldRetryRequest(req *Request, err error) bool {
 		// our request (as opposed to sending an error).
 		return false
 	}
+	if _, ok := err.(nothingWrittenError); ok {
+		// We never wrote anything, so it's safe to retry.
+		return true
+	}
 	if !req.isReplayable() {
 		// Don't retry non-idempotent requests.
-
-		// TODO: swap the nothingWrittenError and isReplayable checks,
-		// putting the "if nothingWrittenError => return true" case
-		// first, per golang.org/issue/15723
 		return false
 	}
-	switch err.(type) {
-	case nothingWrittenError:
-		// We never wrote anything, so it's safe to retry.
-		return true
-	case transportReadFromServerError:
+	if _, ok := err.(transportReadFromServerError); ok {
 		// We got some non-EOF net.Conn.Read failure reading
 		// the 1st response byte from the server.
 		return true
@@ -463,13 +470,16 @@ var ErrSkipAltProtocol = errors.New("net/http: skip alternate protocol")
 func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) {
 	t.altMu.Lock()
 	defer t.altMu.Unlock()
-	if t.altProto == nil {
-		t.altProto = make(map[string]RoundTripper)
-	}
-	if _, exists := t.altProto[scheme]; exists {
+	oldMap, _ := t.altProto.Load().(map[string]RoundTripper)
+	if _, exists := oldMap[scheme]; exists {
 		panic("protocol " + scheme + " already registered")
 	}
-	t.altProto[scheme] = rt
+	newMap := make(map[string]RoundTripper)
+	for k, v := range oldMap {
+		newMap[k] = v
+	}
+	newMap[scheme] = rt
+	t.altProto.Store(newMap)
 }
 
 // CloseIdleConnections closes any connections which were previously
@@ -502,12 +512,17 @@ func (t *Transport) CloseIdleConnections() {
 // cancelable context instead. CancelRequest cannot cancel HTTP/2
 // requests.
 func (t *Transport) CancelRequest(req *Request) {
+	t.cancelRequest(req, errRequestCanceled)
+}
+
+// Cancel an in-flight request, recording the error value.
+func (t *Transport) cancelRequest(req *Request, err error) {
 	t.reqMu.Lock()
 	cancel := t.reqCanceler[req]
 	delete(t.reqCanceler, req)
 	t.reqMu.Unlock()
 	if cancel != nil {
-		cancel()
+		cancel(err)
 	}
 }
 
@@ -557,10 +572,18 @@ func (e *envOnce) reset() {
 }
 
 func (t *Transport) connectMethodForRequest(treq *transportRequest) (cm connectMethod, err error) {
+	if port := treq.URL.Port(); !validPort(port) {
+		return cm, fmt.Errorf("invalid URL port %q", port)
+	}
 	cm.targetScheme = treq.URL.Scheme
 	cm.targetAddr = canonicalAddr(treq.URL)
 	if t.Proxy != nil {
 		cm.proxyURL, err = t.Proxy(treq.Request)
+		if err == nil && cm.proxyURL != nil {
+			if port := cm.proxyURL.Port(); !validPort(port) {
+				return cm, fmt.Errorf("invalid proxy URL port %q", port)
+			}
+		}
 	}
 	return cm, err
 }
@@ -787,11 +810,11 @@ func (t *Transport) removeIdleConnLocked(pconn *persistConn) {
 	}
 }
 
-func (t *Transport) setReqCanceler(r *Request, fn func()) {
+func (t *Transport) setReqCanceler(r *Request, fn func(error)) {
 	t.reqMu.Lock()
 	defer t.reqMu.Unlock()
 	if t.reqCanceler == nil {
-		t.reqCanceler = make(map[*Request]func())
+		t.reqCanceler = make(map[*Request]func(error))
 	}
 	if fn != nil {
 		t.reqCanceler[r] = fn
@@ -804,7 +827,7 @@ func (t *Transport) setReqCanceler(r *Request, fn func()) {
 // for the request, we don't set the function and return false.
 // Since CancelRequest will clear the canceler, we can use the return value to detect if
 // the request was canceled since the last setReqCancel call.
-func (t *Transport) replaceReqCanceler(r *Request, fn func()) bool {
+func (t *Transport) replaceReqCanceler(r *Request, fn func(error)) bool {
 	t.reqMu.Lock()
 	defer t.reqMu.Unlock()
 	_, ok := t.reqCanceler[r]
@@ -853,7 +876,7 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC
 		// set request canceler to some non-nil function so we
 		// can detect whether it was cleared between now and when
 		// we enter roundTrip
-		t.setReqCanceler(req, func() {})
+		t.setReqCanceler(req, func(error) {})
 		return pc, nil
 	}
 
@@ -878,8 +901,8 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC
 		}()
 	}
 
-	cancelc := make(chan struct{})
-	t.setReqCanceler(req, func() { close(cancelc) })
+	cancelc := make(chan error, 1)
+	t.setReqCanceler(req, func(err error) { cancelc <- err })
 
 	go func() {
 		pc, err := t.dialConn(ctx, cm)
@@ -901,7 +924,12 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC
 		select {
 		case <-req.Cancel:
 		case <-req.Context().Done():
-		case <-cancelc:
+			return nil, req.Context().Err()
+		case err := <-cancelc:
+			if err == errRequestCanceled {
+				err = errRequestCanceledConn
+			}
+			return nil, err
 		default:
 			// It wasn't an error due to cancelation, so
 			// return the original error message:
@@ -926,10 +954,13 @@ func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistC
 		return nil, errRequestCanceledConn
 	case <-req.Context().Done():
 		handlePendingDial()
-		return nil, errRequestCanceledConn
-	case <-cancelc:
+		return nil, req.Context().Err()
+	case err := <-cancelc:
 		handlePendingDial()
-		return nil, errRequestCanceledConn
+		if err == errRequestCanceled {
+			err = errRequestCanceledConn
+		}
+		return nil, err
 	}
 }
 
@@ -943,6 +974,7 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon
 		writeErrCh:    make(chan error, 1),
 		writeLoopDone: make(chan struct{}),
 	}
+	trace := httptrace.ContextClientTrace(ctx)
 	tlsDial := t.DialTLS != nil && cm.targetScheme == "https" && cm.proxyURL == nil
 	if tlsDial {
 		var err error
@@ -956,18 +988,28 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon
 		if tc, ok := pconn.conn.(*tls.Conn); ok {
 			// Handshake here, in case DialTLS didn't. TLSNextProto below
 			// depends on it for knowing the connection state.
+			if trace != nil && trace.TLSHandshakeStart != nil {
+				trace.TLSHandshakeStart()
+			}
 			if err := tc.Handshake(); err != nil {
 				go pconn.conn.Close()
+				if trace != nil && trace.TLSHandshakeDone != nil {
+					trace.TLSHandshakeDone(tls.ConnectionState{}, err)
+				}
 				return nil, err
 			}
 			cs := tc.ConnectionState()
+			if trace != nil && trace.TLSHandshakeDone != nil {
+				trace.TLSHandshakeDone(cs, nil)
+			}
 			pconn.tlsState = &cs
 		}
 	} else {
 		conn, err := t.dial(ctx, "tcp", cm.addr())
 		if err != nil {
 			if cm.proxyURL != nil {
-				err = fmt.Errorf("http: error connecting to proxy %s: %v", cm.proxyURL, err)
+				// Return a typed error, per Issue 16997:
+				err = &net.OpError{Op: "proxyconnect", Net: "tcp", Err: err}
 			}
 			return nil, err
 		}
@@ -987,11 +1029,15 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon
 		}
 	case cm.targetScheme == "https":
 		conn := pconn.conn
+		hdr := t.ProxyConnectHeader
+		if hdr == nil {
+			hdr = make(Header)
+		}
 		connectReq := &Request{
 			Method: "CONNECT",
 			URL:    &url.URL{Opaque: cm.targetAddr},
 			Host:   cm.targetAddr,
-			Header: make(Header),
+			Header: hdr,
 		}
 		if pa := cm.proxyAuth(); pa != "" {
 			connectReq.Header.Set("Proxy-Authorization", pa)
@@ -1016,7 +1062,7 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon
 
 	if cm.targetScheme == "https" && !tlsDial {
 		// Initiate TLS and check remote host name against certificate.
-		cfg := cloneTLSClientConfig(t.TLSClientConfig)
+		cfg := cloneTLSConfig(t.TLSClientConfig)
 		if cfg.ServerName == "" {
 			cfg.ServerName = cm.tlsHost()
 		}
@@ -1030,6 +1076,9 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon
 			})
 		}
 		go func() {
+			if trace != nil && trace.TLSHandshakeStart != nil {
+				trace.TLSHandshakeStart()
+			}
 			err := tlsConn.Handshake()
 			if timer != nil {
 				timer.Stop()
@@ -1038,6 +1087,9 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon
 		}()
 		if err := <-errc; err != nil {
 			plainConn.Close()
+			if trace != nil && trace.TLSHandshakeDone != nil {
+				trace.TLSHandshakeDone(tls.ConnectionState{}, err)
+			}
 			return nil, err
 		}
 		if !cfg.InsecureSkipVerify {
@@ -1047,6 +1099,9 @@ func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistCon
 			}
 		}
 		cs := tlsConn.ConnectionState()
+		if trace != nil && trace.TLSHandshakeDone != nil {
+			trace.TLSHandshakeDone(cs, nil)
+		}
 		pconn.tlsState = &cs
 		pconn.conn = tlsConn
 	}
@@ -1235,8 +1290,8 @@ type persistConn struct {
 	mu                   sync.Mutex // guards following fields
 	numExpectedResponses int
 	closed               error // set non-nil when conn is closed, before closech is closed
+	canceledErr          error // set non-nil if conn is canceled
 	broken               bool  // an error has happened on this connection; marked broken so it's not reused.
-	canceled             bool  // whether this conn was broken due a CancelRequest
 	reused               bool  // whether conn has had successful request/response and is being reused.
 	// mutateHeaderFunc is an optional func to modify extra
 	// headers on each outbound request before it's written. (the
@@ -1274,11 +1329,12 @@ func (pc *persistConn) isBroken() bool {
 	return b
 }
 
-// isCanceled reports whether this connection was closed due to CancelRequest.
-func (pc *persistConn) isCanceled() bool {
+// canceled returns non-nil if the connection was closed due to
+// CancelRequest or due to context cancelation.
+func (pc *persistConn) canceled() error {
 	pc.mu.Lock()
 	defer pc.mu.Unlock()
-	return pc.canceled
+	return pc.canceledErr
 }
 
 // isReused reports whether this connection is in a known broken state.
@@ -1301,10 +1357,10 @@ func (pc *persistConn) gotIdleConnTrace(idleAt time.Time) (t httptrace.GotConnIn
 	return
 }
 
-func (pc *persistConn) cancelRequest() {
+func (pc *persistConn) cancelRequest(err error) {
 	pc.mu.Lock()
 	defer pc.mu.Unlock()
-	pc.canceled = true
+	pc.canceledErr = err
 	pc.closeLocked(errRequestCanceled)
 }
 
@@ -1332,8 +1388,8 @@ func (pc *persistConn) mapRoundTripErrorFromReadLoop(startBytesWritten int64, er
 	if err == nil {
 		return nil
 	}
-	if pc.isCanceled() {
-		return errRequestCanceled
+	if err := pc.canceled(); err != nil {
+		return err
 	}
 	if err == errServerClosedIdle {
 		return err
@@ -1355,8 +1411,8 @@ func (pc *persistConn) mapRoundTripErrorFromReadLoop(startBytesWritten int64, er
 // its pc.closech channel close, indicating the persistConn is dead.
 // (after closech is closed, pc.closed is valid).
 func (pc *persistConn) mapRoundTripErrorAfterClosed(startBytesWritten int64) error {
-	if pc.isCanceled() {
-		return errRequestCanceled
+	if err := pc.canceled(); err != nil {
+		return err
 	}
 	err := pc.closed
 	if err == errServerClosedIdle {
@@ -1513,8 +1569,10 @@ func (pc *persistConn) readLoop() {
 				waitForBodyRead <- isEOF
 				if isEOF {
 					<-eofc // see comment above eofc declaration
-				} else if err != nil && pc.isCanceled() {
-					return errRequestCanceled
+				} else if err != nil {
+					if cerr := pc.canceled(); cerr != nil {
+						return cerr
+					}
 				}
 				return err
 			},
@@ -1554,7 +1612,7 @@ func (pc *persistConn) readLoop() {
 			pc.t.CancelRequest(rc.req)
 		case <-rc.req.Context().Done():
 			alive = false
-			pc.t.CancelRequest(rc.req)
+			pc.t.cancelRequest(rc.req, rc.req.Context().Err())
 		case <-pc.closech:
 			alive = false
 		}
@@ -1840,8 +1898,8 @@ WaitResponse:
 		select {
 		case err := <-writeErrCh:
 			if err != nil {
-				if pc.isCanceled() {
-					err = errRequestCanceled
+				if cerr := pc.canceled(); cerr != nil {
+					err = cerr
 				}
 				re = responseAndError{err: err}
 				pc.close(fmt.Errorf("write error: %v", err))
@@ -1865,9 +1923,8 @@ WaitResponse:
 		case <-cancelChan:
 			pc.t.CancelRequest(req.Request)
 			cancelChan = nil
-			ctxDoneChan = nil
 		case <-ctxDoneChan:
-			pc.t.CancelRequest(req.Request)
+			pc.t.cancelRequest(req.Request, req.Context().Err())
 			cancelChan = nil
 			ctxDoneChan = nil
 		}
@@ -1931,11 +1988,15 @@ var portMap = map[string]string{
 
 // canonicalAddr returns url.Host but always with a ":port" suffix
 func canonicalAddr(url *url.URL) string {
-	addr := url.Host
-	if !hasPort(addr) {
-		return addr + ":" + portMap[url.Scheme]
+	addr := url.Hostname()
+	if v, err := idnaASCII(addr); err == nil {
+		addr = v
+	}
+	port := url.Port()
+	if port == "" {
+		port = portMap[url.Scheme]
 	}
-	return addr
+	return net.JoinHostPort(addr, port)
 }
 
 // bodyEOFSignal is used by the HTTP/1 transport when reading response
@@ -2060,75 +2121,14 @@ type fakeLocker struct{}
 func (fakeLocker) Lock()   {}
 func (fakeLocker) Unlock() {}
 
-// cloneTLSConfig returns a shallow clone of the exported
-// fields of cfg, ignoring the unexported sync.Once, which
-// contains a mutex and must not be copied.
-//
-// The cfg must not be in active use by tls.Server, or else
-// there can still be a race with tls.Server updating SessionTicketKey
-// and our copying it, and also a race with the server setting
-// SessionTicketsDisabled=false on failure to set the random
-// ticket key.
-//
-// If cfg is nil, a new zero tls.Config is returned.
+// clneTLSConfig returns a shallow clone of cfg, or a new zero tls.Config if
+// cfg is nil. This is safe to call even if cfg is in active use by a TLS
+// client or server.
 func cloneTLSConfig(cfg *tls.Config) *tls.Config {
 	if cfg == nil {
 		return &tls.Config{}
 	}
-	return &tls.Config{
-		Rand:                        cfg.Rand,
-		Time:                        cfg.Time,
-		Certificates:                cfg.Certificates,
-		NameToCertificate:           cfg.NameToCertificate,
-		GetCertificate:              cfg.GetCertificate,
-		RootCAs:                     cfg.RootCAs,
-		NextProtos:                  cfg.NextProtos,
-		ServerName:                  cfg.ServerName,
-		ClientAuth:                  cfg.ClientAuth,
-		ClientCAs:                   cfg.ClientCAs,
-		InsecureSkipVerify:          cfg.InsecureSkipVerify,
-		CipherSuites:                cfg.CipherSuites,
-		PreferServerCipherSuites:    cfg.PreferServerCipherSuites,
-		SessionTicketsDisabled:      cfg.SessionTicketsDisabled,
-		SessionTicketKey:            cfg.SessionTicketKey,
-		ClientSessionCache:          cfg.ClientSessionCache,
-		MinVersion:                  cfg.MinVersion,
-		MaxVersion:                  cfg.MaxVersion,
-		CurvePreferences:            cfg.CurvePreferences,
-		DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled,
-		Renegotiation:               cfg.Renegotiation,
-	}
-}
-
-// cloneTLSClientConfig is like cloneTLSConfig but omits
-// the fields SessionTicketsDisabled and SessionTicketKey.
-// This makes it safe to call cloneTLSClientConfig on a config
-// in active use by a server.
-func cloneTLSClientConfig(cfg *tls.Config) *tls.Config {
-	if cfg == nil {
-		return &tls.Config{}
-	}
-	return &tls.Config{
-		Rand:                        cfg.Rand,
-		Time:                        cfg.Time,
-		Certificates:                cfg.Certificates,
-		NameToCertificate:           cfg.NameToCertificate,
-		GetCertificate:              cfg.GetCertificate,
-		RootCAs:                     cfg.RootCAs,
-		NextProtos:                  cfg.NextProtos,
-		ServerName:                  cfg.ServerName,
-		ClientAuth:                  cfg.ClientAuth,
-		ClientCAs:                   cfg.ClientCAs,
-		InsecureSkipVerify:          cfg.InsecureSkipVerify,
-		CipherSuites:                cfg.CipherSuites,
-		PreferServerCipherSuites:    cfg.PreferServerCipherSuites,
-		ClientSessionCache:          cfg.ClientSessionCache,
-		MinVersion:                  cfg.MinVersion,
-		MaxVersion:                  cfg.MaxVersion,
-		CurvePreferences:            cfg.CurvePreferences,
-		DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled,
-		Renegotiation:               cfg.Renegotiation,
-	}
+	return cfg.Clone()
 }
 
 type connLRU struct {
@@ -2169,3 +2169,15 @@ func (cl *connLRU) remove(pc *persistConn) {
 func (cl *connLRU) len() int {
 	return len(cl.m)
 }
+
+// validPort reports whether p (without the colon) is a valid port in
+// a URL, per RFC 3986 Section 3.2.3, which says the port may be
+// empty, or only contain digits.
+func validPort(p string) bool {
+	for _, r := range []byte(p) {
+		if r < '0' || r > '9' {
+			return false
+		}
+	}
+	return true
+}
diff --git a/src/net/http/transport_internal_test.go b/src/net/http/transport_internal_test.go
index a05ca6e..3d24fc1 100644
--- a/src/net/http/transport_internal_test.go
+++ b/src/net/http/transport_internal_test.go
@@ -72,3 +72,70 @@ func newLocalListener(t *testing.T) net.Listener {
 	}
 	return ln
 }
+
+func dummyRequest(method string) *Request {
+	req, err := NewRequest(method, "http://fake.tld/", nil)
+	if err != nil {
+		panic(err)
+	}
+	return req
+}
+
+func TestTransportShouldRetryRequest(t *testing.T) {
+	tests := []struct {
+		pc  *persistConn
+		req *Request
+
+		err  error
+		want bool
+	}{
+		0: {
+			pc:   &persistConn{reused: false},
+			req:  dummyRequest("POST"),
+			err:  nothingWrittenError{},
+			want: false,
+		},
+		1: {
+			pc:   &persistConn{reused: true},
+			req:  dummyRequest("POST"),
+			err:  nothingWrittenError{},
+			want: true,
+		},
+		2: {
+			pc:   &persistConn{reused: true},
+			req:  dummyRequest("POST"),
+			err:  http2ErrNoCachedConn,
+			want: true,
+		},
+		3: {
+			pc:   &persistConn{reused: true},
+			req:  dummyRequest("POST"),
+			err:  errMissingHost,
+			want: false,
+		},
+		4: {
+			pc:   &persistConn{reused: true},
+			req:  dummyRequest("POST"),
+			err:  transportReadFromServerError{},
+			want: false,
+		},
+		5: {
+			pc:   &persistConn{reused: true},
+			req:  dummyRequest("GET"),
+			err:  transportReadFromServerError{},
+			want: true,
+		},
+		6: {
+			pc:   &persistConn{reused: true},
+			req:  dummyRequest("GET"),
+			err:  errServerClosedIdle,
+			want: true,
+		},
+	}
+	for i, tt := range tests {
+		got := tt.pc.shouldRetryRequest(tt.req, tt.err)
+		if got != tt.want {
+			t.Errorf("%d. shouldRetryRequest = %v; want %v", i, got, tt.want)
+		}
+	}
+}
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index 298682d..5a40265 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -441,9 +441,7 @@ func TestTransportMaxPerHostIdleConns(t *testing.T) {
 }
 
 func TestTransportRemovesDeadIdleConnections(t *testing.T) {
-	if runtime.GOOS == "plan9" {
-		t.Skip("skipping test; see https://golang.org/issue/15464")
-	}
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		io.WriteString(w, r.RemoteAddr)
@@ -700,6 +698,7 @@ var roundTripTests = []struct {
 
 // Test that the modification made to the Request by the RoundTripper is cleaned up
 func TestRoundTripGzip(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	const responseBody = "test response body"
 	ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
@@ -758,6 +757,7 @@ func TestRoundTripGzip(t *testing.T) {
 }
 
 func TestTransportGzip(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
 	const nRandBytes = 1024 * 1024
@@ -856,6 +856,7 @@ func TestTransportGzip(t *testing.T) {
 // If a request has Expect:100-continue header, the request blocks sending body until the first response.
 // Premature consumption of the request body should not be occurred.
 func TestTransportExpect100Continue(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 
 	ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
@@ -966,6 +967,48 @@ func TestTransportProxy(t *testing.T) {
 	}
 }
 
+// Issue 16997: test transport dial preserves typed errors
+func TestTransportDialPreservesNetOpProxyError(t *testing.T) {
+	defer afterTest(t)
+
+	var errDial = errors.New("some dial error")
+
+	tr := &Transport{
+		Proxy: func(*Request) (*url.URL, error) {
+			return url.Parse("http://proxy.fake.tld/")
+		},
+		Dial: func(string, string) (net.Conn, error) {
+			return nil, errDial
+		},
+	}
+	defer tr.CloseIdleConnections()
+
+	c := &Client{Transport: tr}
+	req, _ := NewRequest("GET", "http://fake.tld", nil)
+	res, err := c.Do(req)
+	if err == nil {
+		res.Body.Close()
+		t.Fatal("wanted a non-nil error")
+	}
+
+	uerr, ok := err.(*url.Error)
+	if !ok {
+		t.Fatalf("got %T, want *url.Error", err)
+	}
+	oe, ok := uerr.Err.(*net.OpError)
+	if !ok {
+		t.Fatalf("url.Error.Err =  %T; want *net.OpError", uerr.Err)
+	}
+	want := &net.OpError{
+		Op:  "proxyconnect",
+		Net: "tcp",
+		Err: errDial, // original error, unwrapped.
+	}
+	if !reflect.DeepEqual(oe, want) {
+		t.Errorf("Got error %#v; want %#v", oe, want)
+	}
+}
+
 // TestTransportGzipRecursive sends a gzip quine and checks that the
 // client gets the same value back. This is more cute than anything,
 // but checks that we don't recurse forever, and checks that
@@ -1038,7 +1081,7 @@ func waitNumGoroutine(nmax int) int {
 
 // tests that persistent goroutine connections shut down when no longer desired.
 func TestTransportPersistConnLeak(t *testing.T) {
-	setParallel(t)
+	// Not parallel: counts goroutines
 	defer afterTest(t)
 	gotReqCh := make(chan bool)
 	unblockCh := make(chan bool)
@@ -1102,7 +1145,7 @@ func TestTransportPersistConnLeak(t *testing.T) {
 // golang.org/issue/4531: Transport leaks goroutines when
 // request.ContentLength is explicitly short
 func TestTransportPersistConnLeakShortBody(t *testing.T) {
-	setParallel(t)
+	// Not parallel: measures goroutines.
 	defer afterTest(t)
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 	}))
@@ -1198,6 +1241,7 @@ func TestIssue3644(t *testing.T) {
 // Test that a client receives a server's reply, even if the server doesn't read
 // the entire request body.
 func TestIssue3595(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	const deniedMsg = "sorry, denied."
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -1246,6 +1290,7 @@ func TestChunkedNoContent(t *testing.T) {
 }
 
 func TestTransportConcurrency(t *testing.T) {
+	// Not parallel: uses global test hooks.
 	defer afterTest(t)
 	maxProcs, numReqs := 16, 500
 	if testing.Short() {
@@ -1306,9 +1351,7 @@ func TestTransportConcurrency(t *testing.T) {
 }
 
 func TestIssue4191_InfiniteGetTimeout(t *testing.T) {
-	if runtime.GOOS == "plan9" {
-		t.Skip("skipping test; see https://golang.org/issue/7237")
-	}
+	setParallel(t)
 	defer afterTest(t)
 	const debug = false
 	mux := NewServeMux()
@@ -1370,9 +1413,7 @@ func TestIssue4191_InfiniteGetTimeout(t *testing.T) {
 }
 
 func TestIssue4191_InfiniteGetToPutTimeout(t *testing.T) {
-	if runtime.GOOS == "plan9" {
-		t.Skip("skipping test; see https://golang.org/issue/7237")
-	}
+	setParallel(t)
 	defer afterTest(t)
 	const debug = false
 	mux := NewServeMux()
@@ -1696,12 +1737,6 @@ func testCancelRequestWithChannelBeforeDo(t *testing.T, withCtx bool) {
 	defer ts.Close()
 	defer close(unblockc)
 
-	// Don't interfere with the next test on plan9.
-	// Cf. https://golang.org/issues/11476
-	if runtime.GOOS == "plan9" {
-		defer time.Sleep(500 * time.Millisecond)
-	}
-
 	tr := &Transport{}
 	defer tr.CloseIdleConnections()
 	c := &Client{Transport: tr}
@@ -1718,8 +1753,17 @@ func testCancelRequestWithChannelBeforeDo(t *testing.T, withCtx bool) {
 	}
 
 	_, err := c.Do(req)
-	if err == nil || !strings.Contains(err.Error(), "canceled") {
-		t.Errorf("Do error = %v; want cancelation", err)
+	if ue, ok := err.(*url.Error); ok {
+		err = ue.Err
+	}
+	if withCtx {
+		if err != context.Canceled {
+			t.Errorf("Do error = %v; want %v", err, context.Canceled)
+		}
+	} else {
+		if err == nil || !strings.Contains(err.Error(), "canceled") {
+			t.Errorf("Do error = %v; want cancelation", err)
+		}
 	}
 }
 
@@ -1888,6 +1932,7 @@ func TestTransportEmptyMethod(t *testing.T) {
 }
 
 func TestTransportSocketLateBinding(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 
 	mux := NewServeMux()
@@ -2152,6 +2197,7 @@ func TestProxyFromEnvironment(t *testing.T) {
 }
 
 func TestIdleConnChannelLeak(t *testing.T) {
+	// Not parallel: uses global test hooks.
 	var mu sync.Mutex
 	var n int
 
@@ -2383,6 +2429,7 @@ func (c byteFromChanReader) Read(p []byte) (n int, err error) {
 // questionable state.
 // golang.org/issue/7569
 func TestTransportNoReuseAfterEarlyResponse(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	var sconn struct {
 		sync.Mutex
@@ -2485,22 +2532,6 @@ type errorReader struct {
 
 func (e errorReader) Read(p []byte) (int, error) { return 0, e.err }
 
-type plan9SleepReader struct{}
-
-func (plan9SleepReader) Read(p []byte) (int, error) {
-	if runtime.GOOS == "plan9" {
-		// After the fix to unblock TCP Reads in
-		// https://golang.org/cl/15941, this sleep is required
-		// on plan9 to make sure TCP Writes before an
-		// immediate TCP close go out on the wire. On Plan 9,
-		// it seems that a hangup of a TCP connection with
-		// queued data doesn't send the queued data first.
-		// https://golang.org/issue/9554
-		time.Sleep(50 * time.Millisecond)
-	}
-	return 0, io.EOF
-}
-
 type closerFunc func() error
 
 func (f closerFunc) Close() error { return f() }
@@ -2595,7 +2626,7 @@ func TestTransportClosesBodyOnError(t *testing.T) {
 		io.Reader
 		io.Closer
 	}{
-		io.MultiReader(io.LimitReader(neverEnding('x'), 1<<20), plan9SleepReader{}, errorReader{fakeErr}),
+		io.MultiReader(io.LimitReader(neverEnding('x'), 1<<20), errorReader{fakeErr}),
 		closerFunc(func() error {
 			select {
 			case didClose <- true:
@@ -2627,6 +2658,8 @@ func TestTransportClosesBodyOnError(t *testing.T) {
 }
 
 func TestTransportDialTLS(t *testing.T) {
+	setParallel(t)
+	defer afterTest(t)
 	var mu sync.Mutex // guards following
 	var gotReq, didDial bool
 
@@ -2904,14 +2937,8 @@ func TestTransportFlushesBodyChunks(t *testing.T) {
 	defer res.Body.Close()
 
 	want := []string{
-		// Because Request.ContentLength = 0, the body is sniffed for 1 byte to determine whether there's content.
-		// That explains the initial "num0" being split into "n" and "um0".
-		// The first byte is included with the request headers Write. Perhaps in the future
-		// we will want to flush the headers out early if the first byte of the request body is
-		// taking a long time to arrive. But not yet.
 		"POST / HTTP/1.1\r\nHost: localhost:8080\r\nUser-Agent: x\r\nTransfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n" +
-			"1\r\nn\r\n",
-		"4\r\num0\n\r\n",
+			"5\r\nnum0\n\r\n",
 		"5\r\nnum1\n\r\n",
 		"5\r\nnum2\n\r\n",
 		"0\r\n\r\n",
@@ -3150,6 +3177,7 @@ func TestTransportReuseConnection_Gzip_ContentLength(t *testing.T) {
 
 // Make sure we re-use underlying TCP connection for gzipped responses too.
 func testTransportReuseConnection_Gzip(t *testing.T, chunked bool) {
+	setParallel(t)
 	defer afterTest(t)
 	addr := make(chan string, 2)
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -3185,6 +3213,7 @@ func testTransportReuseConnection_Gzip(t *testing.T, chunked bool) {
 }
 
 func TestTransportResponseHeaderLength(t *testing.T) {
+	setParallel(t)
 	defer afterTest(t)
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
 		if r.URL.Path == "/long" {
@@ -3248,7 +3277,7 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) {
 
 	cst.tr.ExpectContinueTimeout = 1 * time.Second
 
-	var mu sync.Mutex
+	var mu sync.Mutex // guards buf
 	var buf bytes.Buffer
 	logf := func(format string, args ...interface{}) {
 		mu.Lock()
@@ -3290,10 +3319,16 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) {
 		Wait100Continue: func() { logf("Wait100Continue") },
 		Got100Continue:  func() { logf("Got100Continue") },
 		WroteRequest: func(e httptrace.WroteRequestInfo) {
-			close(gotWroteReqEvent)
 			logf("WroteRequest: %+v", e)
+			close(gotWroteReqEvent)
 		},
 	}
+	if h2 {
+		trace.TLSHandshakeStart = func() { logf("tls handshake start") }
+		trace.TLSHandshakeDone = func(s tls.ConnectionState, err error) {
+			logf("tls handshake done. ConnectionState = %v \n err = %v", s, err)
+		}
+	}
 	if noHooks {
 		// zero out all func pointers, trying to get some path to crash
 		*trace = httptrace.ClientTrace{}
@@ -3323,7 +3358,10 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) {
 		return
 	}
 
+	mu.Lock()
 	got := buf.String()
+	mu.Unlock()
+
 	wantOnce := func(sub string) {
 		if strings.Count(got, sub) != 1 {
 			t.Errorf("expected substring %q exactly once in output.", sub)
@@ -3342,7 +3380,10 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) {
 	wantOnceOrMore("connected to tcp " + addrStr + " = <nil>")
 	wantOnce("Reused:false WasIdle:false IdleTime:0s")
 	wantOnce("first response byte")
-	if !h2 {
+	if h2 {
+		wantOnce("tls handshake start")
+		wantOnce("tls handshake done")
+	} else {
 		wantOnce("PutIdleConn = <nil>")
 	}
 	wantOnce("Wait100Continue")
@@ -3357,12 +3398,21 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) {
 }
 
 func TestTransportEventTraceRealDNS(t *testing.T) {
+	if testing.Short() && testenv.Builder() == "" {
+		// Skip this test in short mode (the default for
+		// all.bash), in case the user is using a shady/ISP
+		// DNS server hijacking queries.
+		// See issues 16732, 16716.
+		// Our builders use 8.8.8.8, though, which correctly
+		// returns NXDOMAIN, so still run this test there.
+		t.Skip("skipping in short mode")
+	}
 	defer afterTest(t)
 	tr := &Transport{}
 	defer tr.CloseIdleConnections()
 	c := &Client{Transport: tr}
 
-	var mu sync.Mutex
+	var mu sync.Mutex // guards buf
 	var buf bytes.Buffer
 	logf := func(format string, args ...interface{}) {
 		mu.Lock()
@@ -3386,7 +3436,10 @@ func TestTransportEventTraceRealDNS(t *testing.T) {
 		t.Fatal("expected error during DNS lookup")
 	}
 
+	mu.Lock()
 	got := buf.String()
+	mu.Unlock()
+
 	wantSub := func(sub string) {
 		if !strings.Contains(got, sub) {
 			t.Errorf("expected substring %q in output.", sub)
@@ -3402,6 +3455,73 @@ func TestTransportEventTraceRealDNS(t *testing.T) {
 	}
 }
 
+// Issue 14353: port can only contain digits.
+func TestTransportRejectsAlphaPort(t *testing.T) {
+	res, err := Get("http://dummy.tld:123foo/bar")
+	if err == nil {
+		res.Body.Close()
+		t.Fatal("unexpected success")
+	}
+	ue, ok := err.(*url.Error)
+	if !ok {
+		t.Fatalf("got %#v; want *url.Error", err)
+	}
+	got := ue.Err.Error()
+	want := `invalid URL port "123foo"`
+	if got != want {
+		t.Errorf("got error %q; want %q", got, want)
+	}
+}
+
+// Test the httptrace.TLSHandshake{Start,Done} hooks with a https http1
+// connections. The http2 test is done in TestTransportEventTrace_h2
+func TestTLSHandshakeTrace(t *testing.T) {
+	defer afterTest(t)
+	s := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
+	defer s.Close()
+
+	var mu sync.Mutex
+	var start, done bool
+	trace := &httptrace.ClientTrace{
+		TLSHandshakeStart: func() {
+			mu.Lock()
+			defer mu.Unlock()
+			start = true
+		},
+		TLSHandshakeDone: func(s tls.ConnectionState, err error) {
+			mu.Lock()
+			defer mu.Unlock()
+			done = true
+			if err != nil {
+				t.Fatal("Expected error to be nil but was:", err)
+			}
+		},
+	}
+
+	tr := &Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
+	defer tr.CloseIdleConnections()
+	c := &Client{Transport: tr}
+	req, err := NewRequest("GET", s.URL, nil)
+	if err != nil {
+		t.Fatal("Unable to construct test request:", err)
+	}
+	req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
+
+	r, err := c.Do(req)
+	if err != nil {
+		t.Fatal("Unexpected error making request:", err)
+	}
+	r.Body.Close()
+	mu.Lock()
+	defer mu.Unlock()
+	if !start {
+		t.Fatal("Expected TLSHandshakeStart to be called, but wasn't")
+	}
+	if !done {
+		t.Fatal("Expected TLSHandshakeDone to be called, but wasnt't")
+	}
+}
+
 func TestTransportMaxIdleConns(t *testing.T) {
 	defer afterTest(t)
 	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -3457,27 +3577,36 @@ func TestTransportMaxIdleConns(t *testing.T) {
 	}
 }
 
-func TestTransportIdleConnTimeout(t *testing.T) {
+func TestTransportIdleConnTimeout_h1(t *testing.T) { testTransportIdleConnTimeout(t, h1Mode) }
+func TestTransportIdleConnTimeout_h2(t *testing.T) { testTransportIdleConnTimeout(t, h2Mode) }
+func testTransportIdleConnTimeout(t *testing.T, h2 bool) {
 	if testing.Short() {
 		t.Skip("skipping in short mode")
 	}
 	defer afterTest(t)
 
-	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+	const timeout = 1 * time.Second
+
+	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
 		// No body for convenience.
 	}))
-	defer ts.Close()
-
-	const timeout = 1 * time.Second
-	tr := &Transport{
-		IdleConnTimeout: timeout,
-	}
+	defer cst.close()
+	tr := cst.tr
+	tr.IdleConnTimeout = timeout
 	defer tr.CloseIdleConnections()
 	c := &Client{Transport: tr}
 
+	idleConns := func() []string {
+		if h2 {
+			return tr.IdleConnStrsForTesting_h2()
+		} else {
+			return tr.IdleConnStrsForTesting()
+		}
+	}
+
 	var conn string
 	doReq := func(n int) {
-		req, _ := NewRequest("GET", ts.URL, nil)
+		req, _ := NewRequest("GET", cst.ts.URL, nil)
 		req = req.WithContext(httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
 			PutIdleConn: func(err error) {
 				if err != nil {
@@ -3490,7 +3619,7 @@ func TestTransportIdleConnTimeout(t *testing.T) {
 			t.Fatal(err)
 		}
 		res.Body.Close()
-		conns := tr.IdleConnStrsForTesting()
+		conns := idleConns()
 		if len(conns) != 1 {
 			t.Fatalf("req %v: unexpected number of idle conns: %q", n, conns)
 		}
@@ -3506,7 +3635,7 @@ func TestTransportIdleConnTimeout(t *testing.T) {
 		time.Sleep(timeout / 2)
 	}
 	time.Sleep(timeout * 3 / 2)
-	if got := tr.IdleConnStrsForTesting(); len(got) != 0 {
+	if got := idleConns(); len(got) != 0 {
 		t.Errorf("idle conns = %q; want none", got)
 	}
 }
@@ -3523,6 +3652,7 @@ func TestTransportIdleConnTimeout(t *testing.T) {
 // know the successful tls.Dial from DialTLS will need to go into the
 // idle pool. Then we give it a of time to explode.
 func TestIdleConnH2Crash(t *testing.T) {
+	setParallel(t)
 	cst := newClientServerTest(t, h2Mode, HandlerFunc(func(w ResponseWriter, r *Request) {
 		// nothing
 	}))
@@ -3531,12 +3661,12 @@ func TestIdleConnH2Crash(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	gotErr := make(chan bool, 1)
+	sawDoErr := make(chan bool, 1)
+	testDone := make(chan struct{})
+	defer close(testDone)
 
 	cst.tr.IdleConnTimeout = 5 * time.Millisecond
 	cst.tr.DialTLS = func(network, addr string) (net.Conn, error) {
-		cancel()
-		<-gotErr
 		c, err := tls.Dial(network, addr, &tls.Config{
 			InsecureSkipVerify: true,
 			NextProtos:         []string{"h2"},
@@ -3550,6 +3680,17 @@ func TestIdleConnH2Crash(t *testing.T) {
 			c.Close()
 			return nil, errors.New("bogus")
 		}
+
+		cancel()
+
+		failTimer := time.NewTimer(5 * time.Second)
+		defer failTimer.Stop()
+		select {
+		case <-sawDoErr:
+		case <-testDone:
+		case <-failTimer.C:
+			t.Error("timeout in DialTLS, waiting too long for cst.c.Do to fail")
+		}
 		return c, nil
 	}
 
@@ -3560,7 +3701,7 @@ func TestIdleConnH2Crash(t *testing.T) {
 		res.Body.Close()
 		t.Fatal("unexpected success")
 	}
-	gotErr <- true
+	sawDoErr <- true
 
 	// Wait for the explosion.
 	time.Sleep(cst.tr.IdleConnTimeout * 10)
@@ -3605,6 +3746,122 @@ func TestTransportReturnsPeekError(t *testing.T) {
 	}
 }
 
+// Issue 13835: international domain names should work
+func TestTransportIDNA_h1(t *testing.T) { testTransportIDNA(t, h1Mode) }
+func TestTransportIDNA_h2(t *testing.T) { testTransportIDNA(t, h2Mode) }
+func testTransportIDNA(t *testing.T, h2 bool) {
+	defer afterTest(t)
+
+	const uniDomain = "гофер.го"
+	const punyDomain = "xn--c1ae0ajs.xn--c1aw"
+
+	var port string
+	cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
+		want := punyDomain + ":" + port
+		if r.Host != want {
+			t.Errorf("Host header = %q; want %q", r.Host, want)
+		}
+		if h2 {
+			if r.TLS == nil {
+				t.Errorf("r.TLS == nil")
+			} else if r.TLS.ServerName != punyDomain {
+				t.Errorf("TLS.ServerName = %q; want %q", r.TLS.ServerName, punyDomain)
+			}
+		}
+		w.Header().Set("Hit-Handler", "1")
+	}))
+	defer cst.close()
+
+	ip, port, err := net.SplitHostPort(cst.ts.Listener.Addr().String())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Install a fake DNS server.
+	ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, host string) ([]net.IPAddr, error) {
+		if host != punyDomain {
+			t.Errorf("got DNS host lookup for %q; want %q", host, punyDomain)
+			return nil, nil
+		}
+		return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil
+	})
+
+	req, _ := NewRequest("GET", cst.scheme()+"://"+uniDomain+":"+port, nil)
+	trace := &httptrace.ClientTrace{
+		GetConn: func(hostPort string) {
+			want := net.JoinHostPort(punyDomain, port)
+			if hostPort != want {
+				t.Errorf("getting conn for %q; want %q", hostPort, want)
+			}
+		},
+		DNSStart: func(e httptrace.DNSStartInfo) {
+			if e.Host != punyDomain {
+				t.Errorf("DNSStart Host = %q; want %q", e.Host, punyDomain)
+			}
+		},
+	}
+	req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
+
+	res, err := cst.tr.RoundTrip(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer res.Body.Close()
+	if res.Header.Get("Hit-Handler") != "1" {
+		out, err := httputil.DumpResponse(res, true)
+		if err != nil {
+			t.Fatal(err)
+		}
+		t.Errorf("Response body wasn't from Handler. Got:\n%s\n", out)
+	}
+}
+
+// Issue 13290: send User-Agent in proxy CONNECT
+func TestTransportProxyConnectHeader(t *testing.T) {
+	defer afterTest(t)
+	reqc := make(chan *Request, 1)
+	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+		if r.Method != "CONNECT" {
+			t.Errorf("method = %q; want CONNECT", r.Method)
+		}
+		reqc <- r
+		c, _, err := w.(Hijacker).Hijack()
+		if err != nil {
+			t.Errorf("Hijack: %v", err)
+			return
+		}
+		c.Close()
+	}))
+	defer ts.Close()
+	tr := &Transport{
+		ProxyConnectHeader: Header{
+			"User-Agent": {"foo"},
+			"Other":      {"bar"},
+		},
+		Proxy: func(r *Request) (*url.URL, error) {
+			return url.Parse(ts.URL)
+		},
+	}
+	defer tr.CloseIdleConnections()
+	c := &Client{Transport: tr}
+	res, err := c.Get("https://dummy.tld/") // https to force a CONNECT
+	if err == nil {
+		res.Body.Close()
+		t.Errorf("unexpected success")
+	}
+	select {
+	case <-time.After(3 * time.Second):
+		t.Fatal("timeout")
+	case r := <-reqc:
+		if got, want := r.Header.Get("User-Agent"), "foo"; got != want {
+			t.Errorf("CONNECT request User-Agent = %q; want %q", got, want)
+		}
+		if got, want := r.Header.Get("Other"), "bar"; got != want {
+			t.Errorf("CONNECT request Other = %q; want %q", got, want)
+		}
+	}
+}
+
 var errFakeRoundTrip = errors.New("fake roundtrip")
 
 type funcRoundTripper func()
diff --git a/src/net/interface.go b/src/net/interface.go
index 52b857c..301a5cf 100644
--- a/src/net/interface.go
+++ b/src/net/interface.go
@@ -10,6 +10,12 @@ import (
 	"time"
 )
 
+// BUG(mikio): On NaCl, methods and functions related to
+// Interface are not implemented.
+
+// BUG(mikio): On DragonFly BSD, NetBSD, OpenBSD, Plan 9 and Solaris,
+// the MulticastAddrs method of Interface is not implemented.
+
 var (
 	errInvalidInterface         = errors.New("invalid network interface")
 	errInvalidInterfaceIndex    = errors.New("invalid network interface index")
@@ -63,7 +69,8 @@ func (f Flags) String() string {
 	return s
 }
 
-// Addrs returns interface addresses for a specific interface.
+// Addrs returns a list of unicast interface addresses for a specific
+// interface.
 func (ifi *Interface) Addrs() ([]Addr, error) {
 	if ifi == nil {
 		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface}
@@ -75,8 +82,8 @@ func (ifi *Interface) Addrs() ([]Addr, error) {
 	return ifat, err
 }
 
-// MulticastAddrs returns multicast, joined group addresses for
-// a specific interface.
+// MulticastAddrs returns a list of multicast, joined group addresses
+// for a specific interface.
 func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
 	if ifi == nil {
 		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterface}
@@ -100,8 +107,11 @@ func Interfaces() ([]Interface, error) {
 	return ift, nil
 }
 
-// InterfaceAddrs returns a list of the system's network interface
+// InterfaceAddrs returns a list of the system's unicast interface
 // addresses.
+//
+// The returned list does not identify the associated interface; use
+// Interfaces and Interface.Addrs for more detail.
 func InterfaceAddrs() ([]Addr, error) {
 	ifat, err := interfaceAddrTable(nil)
 	if err != nil {
@@ -111,6 +121,10 @@ func InterfaceAddrs() ([]Addr, error) {
 }
 
 // InterfaceByIndex returns the interface specified by index.
+//
+// On Solaris, it returns one of the logical network interfaces
+// sharing the logical data link; for more precision use
+// InterfaceByName.
 func InterfaceByIndex(index int) (*Interface, error) {
 	if index <= 0 {
 		return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errInvalidInterfaceIndex}
@@ -215,7 +229,7 @@ func zoneToInt(zone string) int {
 	defer zoneCache.RUnlock()
 	index, ok := zoneCache.toIndex[zone]
 	if !ok {
-		index, _, _ = dtoi(zone, 0)
+		index, _, _ = dtoi(zone)
 	}
 	return index
 }
diff --git a/src/net/interface_plan9.go b/src/net/interface_plan9.go
new file mode 100644
index 0000000..e5d7739
--- /dev/null
+++ b/src/net/interface_plan9.go
@@ -0,0 +1,198 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+	"errors"
+	"os"
+)
+
+// If the ifindex is zero, interfaceTable returns mappings of all
+// network interfaces. Otherwise it returns a mapping of a specific
+// interface.
+func interfaceTable(ifindex int) ([]Interface, error) {
+	if ifindex == 0 {
+		n, err := interfaceCount()
+		if err != nil {
+			return nil, err
+		}
+		ifcs := make([]Interface, n)
+		for i := range ifcs {
+			ifc, err := readInterface(i)
+			if err != nil {
+				return nil, err
+			}
+			ifcs[i] = *ifc
+		}
+		return ifcs, nil
+	}
+
+	ifc, err := readInterface(ifindex - 1)
+	if err != nil {
+		return nil, err
+	}
+	return []Interface{*ifc}, nil
+}
+
+func readInterface(i int) (*Interface, error) {
+	ifc := &Interface{
+		Index: i + 1,                        // Offset the index by one to suit the contract
+		Name:  netdir + "/ipifc/" + itoa(i), // Name is the full path to the interface path in plan9
+	}
+
+	ifcstat := ifc.Name + "/status"
+	ifcstatf, err := open(ifcstat)
+	if err != nil {
+		return nil, err
+	}
+	defer ifcstatf.close()
+
+	line, ok := ifcstatf.readLine()
+	if !ok {
+		return nil, errors.New("invalid interface status file: " + ifcstat)
+	}
+
+	fields := getFields(line)
+	if len(fields) < 4 {
+		return nil, errors.New("invalid interface status file: " + ifcstat)
+	}
+
+	device := fields[1]
+	mtustr := fields[3]
+
+	mtu, _, ok := dtoi(mtustr)
+	if !ok {
+		return nil, errors.New("invalid status file of interface: " + ifcstat)
+	}
+	ifc.MTU = mtu
+
+	// Not a loopback device
+	if device != "/dev/null" {
+		deviceaddrf, err := open(device + "/addr")
+		if err != nil {
+			return nil, err
+		}
+		defer deviceaddrf.close()
+
+		line, ok = deviceaddrf.readLine()
+		if !ok {
+			return nil, errors.New("invalid address file for interface: " + device + "/addr")
+		}
+
+		if len(line) > 0 && len(line)%2 == 0 {
+			ifc.HardwareAddr = make([]byte, len(line)/2)
+			var ok bool
+			for i := range ifc.HardwareAddr {
+				j := (i + 1) * 2
+				ifc.HardwareAddr[i], ok = xtoi2(line[i*2:j], 0)
+				if !ok {
+					ifc.HardwareAddr = ifc.HardwareAddr[:i]
+					break
+				}
+			}
+		}
+
+		ifc.Flags = FlagUp | FlagBroadcast | FlagMulticast
+	} else {
+		ifc.Flags = FlagUp | FlagMulticast | FlagLoopback
+	}
+
+	return ifc, nil
+}
+
+func interfaceCount() (int, error) {
+	d, err := os.Open(netdir + "/ipifc")
+	if err != nil {
+		return -1, err
+	}
+	defer d.Close()
+
+	names, err := d.Readdirnames(0)
+	if err != nil {
+		return -1, err
+	}
+
+	// Assumes that numbered files in ipifc are strictly
+	// the incrementing numbered directories for the
+	// interfaces
+	c := 0
+	for _, name := range names {
+		if _, _, ok := dtoi(name); !ok {
+			continue
+		}
+		c++
+	}
+
+	return c, nil
+}
+
+// If the ifi is nil, interfaceAddrTable returns addresses for all
+// network interfaces. Otherwise it returns addresses for a specific
+// interface.
+func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
+	var ifcs []Interface
+	if ifi == nil {
+		var err error
+		ifcs, err = interfaceTable(0)
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		ifcs = []Interface{*ifi}
+	}
+
+	addrs := make([]Addr, len(ifcs))
+	for i, ifc := range ifcs {
+		status := ifc.Name + "/status"
+		statusf, err := open(status)
+		if err != nil {
+			return nil, err
+		}
+		defer statusf.close()
+
+		line, ok := statusf.readLine()
+		line, ok = statusf.readLine()
+		if !ok {
+			return nil, errors.New("cannot parse IP address for interface: " + status)
+		}
+
+		// This assumes only a single address for the interface.
+		fields := getFields(line)
+		if len(fields) < 1 {
+			return nil, errors.New("cannot parse IP address for interface: " + status)
+		}
+		addr := fields[0]
+		ip := ParseIP(addr)
+		if ip == nil {
+			return nil, errors.New("cannot parse IP address for interface: " + status)
+		}
+
+		// The mask is represented as CIDR relative to the IPv6 address.
+		// Plan 9 internal representation is always IPv6.
+		maskfld := fields[1]
+		maskfld = maskfld[1:]
+		pfxlen, _, ok := dtoi(maskfld)
+		if !ok {
+			return nil, errors.New("cannot parse network mask for interface: " + status)
+		}
+		var mask IPMask
+		if ip.To4() != nil { // IPv4 or IPv6 IPv4-mapped address
+			mask = CIDRMask(pfxlen-8*len(v4InV6Prefix), 8*IPv4len)
+		}
+		if ip.To16() != nil && ip.To4() == nil { // IPv6 address
+			mask = CIDRMask(pfxlen, 8*IPv6len)
+		}
+
+		addrs[i] = &IPNet{IP: ip, Mask: mask}
+	}
+
+	return addrs, nil
+}
+
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+	return nil, nil
+}
diff --git a/src/net/interface_solaris.go b/src/net/interface_solaris.go
new file mode 100644
index 0000000..dc8ffbf
--- /dev/null
+++ b/src/net/interface_solaris.go
@@ -0,0 +1,107 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+	"syscall"
+
+	"golang_org/x/net/lif"
+)
+
+// If the ifindex is zero, interfaceTable returns mappings of all
+// network interfaces. Otherwise it returns a mapping of a specific
+// interface.
+func interfaceTable(ifindex int) ([]Interface, error) {
+	lls, err := lif.Links(syscall.AF_UNSPEC, "")
+	if err != nil {
+		return nil, err
+	}
+	var ift []Interface
+	for _, ll := range lls {
+		if ifindex != 0 && ifindex != ll.Index {
+			continue
+		}
+		ifi := Interface{Index: ll.Index, MTU: ll.MTU, Name: ll.Name, Flags: linkFlags(ll.Flags)}
+		if len(ll.Addr) > 0 {
+			ifi.HardwareAddr = HardwareAddr(ll.Addr)
+		}
+		ift = append(ift, ifi)
+	}
+	return ift, nil
+}
+
+const (
+	sysIFF_UP          = 0x1
+	sysIFF_BROADCAST   = 0x2
+	sysIFF_DEBUG       = 0x4
+	sysIFF_LOOPBACK    = 0x8
+	sysIFF_POINTOPOINT = 0x10
+	sysIFF_NOTRAILERS  = 0x20
+	sysIFF_RUNNING     = 0x40
+	sysIFF_NOARP       = 0x80
+	sysIFF_PROMISC     = 0x100
+	sysIFF_ALLMULTI    = 0x200
+	sysIFF_INTELLIGENT = 0x400
+	sysIFF_MULTICAST   = 0x800
+	sysIFF_MULTI_BCAST = 0x1000
+	sysIFF_UNNUMBERED  = 0x2000
+	sysIFF_PRIVATE     = 0x8000
+)
+
+func linkFlags(rawFlags int) Flags {
+	var f Flags
+	if rawFlags&sysIFF_UP != 0 {
+		f |= FlagUp
+	}
+	if rawFlags&sysIFF_BROADCAST != 0 {
+		f |= FlagBroadcast
+	}
+	if rawFlags&sysIFF_LOOPBACK != 0 {
+		f |= FlagLoopback
+	}
+	if rawFlags&sysIFF_POINTOPOINT != 0 {
+		f |= FlagPointToPoint
+	}
+	if rawFlags&sysIFF_MULTICAST != 0 {
+		f |= FlagMulticast
+	}
+	return f
+}
+
+// If the ifi is nil, interfaceAddrTable returns addresses for all
+// network interfaces. Otherwise it returns addresses for a specific
+// interface.
+func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
+	var name string
+	if ifi != nil {
+		name = ifi.Name
+	}
+	as, err := lif.Addrs(syscall.AF_UNSPEC, name)
+	if err != nil {
+		return nil, err
+	}
+	var ifat []Addr
+	for _, a := range as {
+		var ip IP
+		var mask IPMask
+		switch a := a.(type) {
+		case *lif.Inet4Addr:
+			ip = IPv4(a.IP[0], a.IP[1], a.IP[2], a.IP[3])
+			mask = CIDRMask(a.PrefixLen, 8*IPv4len)
+		case *lif.Inet6Addr:
+			ip = make(IP, IPv6len)
+			copy(ip, a.IP[:])
+			mask = CIDRMask(a.PrefixLen, 8*IPv6len)
+		}
+		ifat = append(ifat, &IPNet{IP: ip, Mask: mask})
+	}
+	return ifat, nil
+}
+
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+	return nil, nil
+}
diff --git a/src/net/interface_stub.go b/src/net/interface_stub.go
index f64174c..3b0a1ae 100644
--- a/src/net/interface_stub.go
+++ b/src/net/interface_stub.go
@@ -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 nacl plan9 solaris
+// +build nacl
 
 package net
 
diff --git a/src/net/interface_test.go b/src/net/interface_test.go
index 4c695b9..38a2ca4 100644
--- a/src/net/interface_test.go
+++ b/src/net/interface_test.go
@@ -58,8 +58,15 @@ func TestInterfaces(t *testing.T) {
 		if err != nil {
 			t.Fatal(err)
 		}
-		if !reflect.DeepEqual(ifxi, &ifi) {
-			t.Errorf("got %v; want %v", ifxi, ifi)
+		switch runtime.GOOS {
+		case "solaris":
+			if ifxi.Index != ifi.Index {
+				t.Errorf("got %v; want %v", ifxi, ifi)
+			}
+		default:
+			if !reflect.DeepEqual(ifxi, &ifi) {
+				t.Errorf("got %v; want %v", ifxi, ifi)
+			}
 		}
 		ifxn, err := InterfaceByName(ifi.Name)
 		if err != nil {
diff --git a/src/net/ip.go b/src/net/ip.go
index d0c8263..c5b454d 100644
--- a/src/net/ip.go
+++ b/src/net/ip.go
@@ -504,29 +504,25 @@ func (n *IPNet) String() string {
 // Parse IPv4 address (d.d.d.d).
 func parseIPv4(s string) IP {
 	var p [IPv4len]byte
-	i := 0
-	for j := 0; j < IPv4len; j++ {
-		if i >= len(s) {
+	for i := 0; i < IPv4len; i++ {
+		if len(s) == 0 {
 			// Missing octets.
 			return nil
 		}
-		if j > 0 {
-			if s[i] != '.' {
+		if i > 0 {
+			if s[0] != '.' {
 				return nil
 			}
-			i++
+			s = s[1:]
 		}
-		var (
-			n  int
-			ok bool
-		)
-		n, i, ok = dtoi(s, i)
+		n, c, ok := dtoi(s)
 		if !ok || n > 0xFF {
 			return nil
 		}
-		p[j] = byte(n)
+		s = s[c:]
+		p[i] = byte(n)
 	}
-	if i != len(s) {
+	if len(s) != 0 {
 		return nil
 	}
 	return IPv4(p[0], p[1], p[2], p[3])
@@ -538,8 +534,7 @@ func parseIPv4(s string) IP {
 // true.
 func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
 	ip = make(IP, IPv6len)
-	ellipsis := -1 // position of ellipsis in p
-	i := 0         // index in string s
+	ellipsis := -1 // position of ellipsis in ip
 
 	if zoneAllowed {
 		s, zone = splitHostZone(s)
@@ -548,90 +543,91 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
 	// Might have leading ellipsis
 	if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
 		ellipsis = 0
-		i = 2
+		s = s[2:]
 		// Might be only ellipsis
-		if i == len(s) {
+		if len(s) == 0 {
 			return ip, zone
 		}
 	}
 
 	// Loop, parsing hex numbers followed by colon.
-	j := 0
-	for j < IPv6len {
+	i := 0
+	for i < IPv6len {
 		// Hex number.
-		n, i1, ok := xtoi(s, i)
+		n, c, ok := xtoi(s)
 		if !ok || n > 0xFFFF {
 			return nil, zone
 		}
 
 		// If followed by dot, might be in trailing IPv4.
-		if i1 < len(s) && s[i1] == '.' {
-			if ellipsis < 0 && j != IPv6len-IPv4len {
+		if c < len(s) && s[c] == '.' {
+			if ellipsis < 0 && i != IPv6len-IPv4len {
 				// Not the right place.
 				return nil, zone
 			}
-			if j+IPv4len > IPv6len {
+			if i+IPv4len > IPv6len {
 				// Not enough room.
 				return nil, zone
 			}
-			ip4 := parseIPv4(s[i:])
+			ip4 := parseIPv4(s)
 			if ip4 == nil {
 				return nil, zone
 			}
-			ip[j] = ip4[12]
-			ip[j+1] = ip4[13]
-			ip[j+2] = ip4[14]
-			ip[j+3] = ip4[15]
-			i = len(s)
-			j += IPv4len
+			ip[i] = ip4[12]
+			ip[i+1] = ip4[13]
+			ip[i+2] = ip4[14]
+			ip[i+3] = ip4[15]
+			s = ""
+			i += IPv4len
 			break
 		}
 
 		// Save this 16-bit chunk.
-		ip[j] = byte(n >> 8)
-		ip[j+1] = byte(n)
-		j += 2
+		ip[i] = byte(n >> 8)
+		ip[i+1] = byte(n)
+		i += 2
 
 		// Stop at end of string.
-		i = i1
-		if i == len(s) {
+		s = s[c:]
+		if len(s) == 0 {
 			break
 		}
 
 		// Otherwise must be followed by colon and more.
-		if s[i] != ':' || i+1 == len(s) {
+		if s[0] != ':' || len(s) == 1 {
 			return nil, zone
 		}
-		i++
+		s = s[1:]
 
 		// Look for ellipsis.
-		if s[i] == ':' {
+		if s[0] == ':' {
 			if ellipsis >= 0 { // already have one
 				return nil, zone
 			}
-			ellipsis = j
-			if i++; i == len(s) { // can be at end
+			ellipsis = i
+			s = s[1:]
+			if len(s) == 0 { // can be at end
 				break
 			}
 		}
 	}
 
 	// Must have used entire string.
-	if i != len(s) {
+	if len(s) != 0 {
 		return nil, zone
 	}
 
 	// If didn't parse enough, expand ellipsis.
-	if j < IPv6len {
+	if i < IPv6len {
 		if ellipsis < 0 {
 			return nil, zone
 		}
-		n := IPv6len - j
-		for k := j - 1; k >= ellipsis; k-- {
-			ip[k+n] = ip[k]
+		n := IPv6len - i
+		for j := i - 1; j >= ellipsis; j-- {
+			ip[j+n] = ip[j]
 		}
-		for k := ellipsis + n - 1; k >= ellipsis; k-- {
-			ip[k] = 0
+		for j := ellipsis + n - 1; j >= ellipsis; j-- {
+			ip[j] = 0
 		}
 	} else if ellipsis >= 0 {
 		// Ellipsis must represent at least one 0 group.
@@ -677,7 +673,7 @@ func ParseCIDR(s string) (IP, *IPNet, error) {
 		iplen = IPv6len
 		ip, _ = parseIPv6(addr, false)
 	}
-	n, i, ok := dtoi(mask, 0)
+	n, i, ok := dtoi(mask)
 	if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {
 		return nil, nil, &ParseError{Type: "CIDR address", Text: s}
 	}
diff --git a/src/net/ip_test.go b/src/net/ip_test.go
index b6ac26d..4655163 100644
--- a/src/net/ip_test.go
+++ b/src/net/ip_test.go
@@ -28,6 +28,10 @@ var parseIPTests = []struct {
 	{"2001:4860:0:2001::68", IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}},
 	{"2001:4860:0000:2001:0000:0000:0000:0068", IP{0x20, 0x01, 0x48, 0x60, 0, 0, 0x20, 0x01, 0, 0, 0, 0, 0, 0, 0x00, 0x68}},
 
+	{"-0.0.0.0", nil},
+	{"0.-1.0.0", nil},
+	{"0.0.-2.0", nil},
+	{"0.0.0.-3", nil},
 	{"127.0.0.256", nil},
 	{"abc", nil},
 	{"123:", nil},
@@ -242,13 +246,15 @@ func TestIPString(t *testing.T) {
 	}
 }
 
+var sink string
+
 func BenchmarkIPString(b *testing.B) {
 	testHookUninstaller.Do(uninstallTestHooks)
 
 	for i := 0; i < b.N; i++ {
 		for _, tt := range ipStringTests {
 			if tt.in != nil {
-				tt.in.String()
+				sink = tt.in.String()
 			}
 		}
 	}
@@ -299,7 +305,7 @@ func BenchmarkIPMaskString(b *testing.B) {
 
 	for i := 0; i < b.N; i++ {
 		for _, tt := range ipMaskStringTests {
-			tt.in.String()
+			sink = tt.in.String()
 		}
 	}
 }
@@ -330,6 +336,12 @@ var parseCIDRTests = []struct {
 	{"192.168.1.1/255.255.255.0", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/255.255.255.0"}},
 	{"192.168.1.1/35", nil, nil, &ParseError{Type: "CIDR address", Text: "192.168.1.1/35"}},
 	{"2001:db8::1/-1", nil, nil, &ParseError{Type: "CIDR address", Text: "2001:db8::1/-1"}},
+	{"2001:db8::1/-0", nil, nil, &ParseError{Type: "CIDR address", Text: "2001:db8::1/-0"}},
+	{"-0.0.0.0/32", nil, nil, &ParseError{Type: "CIDR address", Text: "-0.0.0.0/32"}},
+	{"0.-1.0.0/32", nil, nil, &ParseError{Type: "CIDR address", Text: "0.-1.0.0/32"}},
+	{"0.0.-2.0/32", nil, nil, &ParseError{Type: "CIDR address", Text: "0.0.-2.0/32"}},
+	{"0.0.0.-3/32", nil, nil, &ParseError{Type: "CIDR address", Text: "0.0.0.-3/32"}},
+	{"0.0.0.0/-0", nil, nil, &ParseError{Type: "CIDR address", Text: "0.0.0.0/-0"}},
 	{"", nil, nil, &ParseError{Type: "CIDR address", Text: ""}},
 }
 
diff --git a/src/net/iprawsock.go b/src/net/iprawsock.go
index 173b3cb..b3cc03e 100644
--- a/src/net/iprawsock.go
+++ b/src/net/iprawsock.go
@@ -9,6 +9,12 @@ import (
 	"syscall"
 )
 
+// BUG(mikio): On NaCl, Plan 9 and Windows, the ReadMsgIP and
+// WriteMsgIP methods of IPConn are not implemented.
+
+// BUG(mikio): On Windows, the File method of IPConn is not
+// implemented.
+
 // IPAddr represents the address of an IP end point.
 type IPAddr struct {
 	IP   IP
@@ -46,6 +52,9 @@ func (a *IPAddr) opAddr() Addr {
 // ResolveIPAddr parses addr as an IP address of the form "host" or
 // "ipv6-host%zone" and resolves the domain name on the network net,
 // which must be "ip", "ip4" or "ip6".
+//
+// Resolving a hostname is not recommended because this returns at most
+// one of its IP addresses.
 func ResolveIPAddr(net, addr string) (*IPAddr, error) {
 	if net == "" { // a hint wildcard for Go 1.0 undocumented behavior
 		net = "ip"
@@ -59,7 +68,7 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) {
 	default:
 		return nil, UnknownNetworkError(net)
 	}
-	addrs, err := internetAddrList(context.Background(), afnet, addr)
+	addrs, err := DefaultResolver.internetAddrList(context.Background(), afnet, addr)
 	if err != nil {
 		return nil, err
 	}
diff --git a/src/net/iprawsock_posix.go b/src/net/iprawsock_posix.go
index 3e0b060..d5e229f 100644
--- a/src/net/iprawsock_posix.go
+++ b/src/net/iprawsock_posix.go
@@ -50,6 +50,10 @@ func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
 	return ipToSockaddr(family, a.IP, 0, a.Zone)
 }
 
+func (a *IPAddr) toLocal(net string) sockaddr {
+	return &IPAddr{loopbackIP(net), a.Zone}
+}
+
 func (c *IPConn) readFrom(b []byte) (int, *IPAddr, error) {
 	// TODO(cw,rsc): consider using readv if we know the family
 	// type to avoid the header trim/copy
diff --git a/src/net/ipsock.go b/src/net/ipsock.go
index 24daf17..c91e201 100644
--- a/src/net/ipsock.go
+++ b/src/net/ipsock.go
@@ -76,7 +76,7 @@ func (addrs addrList) partition(strategy func(Addr) bool) (primaries, fallbacks
 // yielding a list of Addr objects. Known filters are nil, ipv4only,
 // and ipv6only. It returns every address when the filter is nil.
 // The result contains at least one address when error is nil.
-func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr) (addrList, error) {
+func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr, originalAddr string) (addrList, error) {
 	var addrs addrList
 	for _, ip := range ips {
 		if filter == nil || filter(ip) {
@@ -84,21 +84,19 @@ func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr
 		}
 	}
 	if len(addrs) == 0 {
-		return nil, errNoSuitableAddress
+		return nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: originalAddr}
 	}
 	return addrs, nil
 }
 
-// ipv4only reports whether the kernel supports IPv4 addressing mode
-// and addr is an IPv4 address.
+// ipv4only reports whether addr is an IPv4 address.
 func ipv4only(addr IPAddr) bool {
-	return supportsIPv4 && addr.IP.To4() != nil
+	return addr.IP.To4() != nil
 }
 
-// ipv6only reports whether the kernel supports IPv6 addressing mode
-// and addr is an IPv6 address except IPv4-mapped IPv6 address.
+// ipv6only reports whether addr is an IPv6 address except IPv4-mapped IPv6 address.
 func ipv6only(addr IPAddr) bool {
-	return supportsIPv6 && len(addr.IP) == IPv6len && addr.IP.To4() == nil
+	return len(addr.IP) == IPv6len && addr.IP.To4() == nil
 }
 
 // SplitHostPort splits a network address of the form "host:port",
@@ -190,7 +188,7 @@ func JoinHostPort(host, port string) string {
 // address or a DNS name, and returns a list of internet protocol
 // family addresses. The result contains at least one address when
 // error is nil.
-func internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
+func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
 	var (
 		err        error
 		host, port string
@@ -202,7 +200,7 @@ func internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
 			if host, port, err = SplitHostPort(addr); err != nil {
 				return nil, err
 			}
-			if portnum, err = LookupPort(net, port); err != nil {
+			if portnum, err = r.LookupPort(ctx, net, port); err != nil {
 				return nil, err
 			}
 		}
@@ -228,20 +226,21 @@ func internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
 	if host == "" {
 		return addrList{inetaddr(IPAddr{})}, nil
 	}
-	// Try as a literal IP address.
-	var ip IP
-	if ip = parseIPv4(host); ip != nil {
-		return addrList{inetaddr(IPAddr{IP: ip})}, nil
-	}
-	var zone string
-	if ip, zone = parseIPv6(host, true); ip != nil {
-		return addrList{inetaddr(IPAddr{IP: ip, Zone: zone})}, nil
-	}
-	// Try as a DNS name.
-	ips, err := lookupIPContext(ctx, host)
-	if err != nil {
-		return nil, err
+
+	// Try as a literal IP address, then as a DNS name.
+	var ips []IPAddr
+	if ip := parseIPv4(host); ip != nil {
+		ips = []IPAddr{{IP: ip}}
+	} else if ip, zone := parseIPv6(host, true); ip != nil {
+		ips = []IPAddr{{IP: ip, Zone: zone}}
+	} else {
+		// Try as a DNS name.
+		ips, err = r.LookupIPAddr(ctx, host)
+		if err != nil {
+			return nil, err
+		}
 	}
+
 	var filter func(IPAddr) bool
 	if net != "" && net[len(net)-1] == '4' {
 		filter = ipv4only
@@ -249,5 +248,12 @@ func internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
 	if net != "" && net[len(net)-1] == '6' {
 		filter = ipv6only
 	}
-	return filterAddrList(filter, ips, inetaddr)
+	return filterAddrList(filter, ips, inetaddr, host)
+}
+
+func loopbackIP(net string) IP {
+	if net != "" && net[len(net)-1] == '6' {
+		return IPv6loopback
+	}
+	return IP{127, 0, 0, 1}
 }
diff --git a/src/net/ipsock_plan9.go b/src/net/ipsock_plan9.go
index 2b84683..b7fd344 100644
--- a/src/net/ipsock_plan9.go
+++ b/src/net/ipsock_plan9.go
@@ -63,7 +63,7 @@ func parsePlan9Addr(s string) (ip IP, iport int, err error) {
 			return nil, 0, &ParseError{Type: "IP address", Text: s}
 		}
 	}
-	p, _, ok := dtoi(s[i+1:], 0)
+	p, _, ok := dtoi(s[i+1:])
 	if !ok {
 		return nil, 0, &ParseError{Type: "port", Text: s}
 	}
@@ -119,6 +119,11 @@ func startPlan9(ctx context.Context, net string, addr Addr) (ctl *os.File, dest,
 		return
 	}
 
+	if port > 65535 {
+		err = InvalidAddrError("port should be < 65536")
+		return
+	}
+
 	clone, dest, err := queryCS1(ctx, proto, ip, port)
 	if err != nil {
 		return
@@ -193,6 +198,9 @@ func dialPlan9(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, e
 }
 
 func dialPlan9Blocking(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) {
+	if isWildcard(raddr) {
+		raddr = toLocal(raddr, net)
+	}
 	f, dest, proto, name, err := startPlan9(ctx, net, raddr)
 	if err != nil {
 		return nil, err
@@ -213,7 +221,7 @@ func dialPlan9Blocking(ctx context.Context, net string, laddr, raddr Addr) (fd *
 		f.Close()
 		return nil, err
 	}
-	return newFD(proto, name, f, data, laddr, raddr)
+	return newFD(proto, name, nil, f, data, laddr, raddr)
 }
 
 func listenPlan9(ctx context.Context, net string, laddr Addr) (fd *netFD, err error) {
@@ -232,11 +240,11 @@ func listenPlan9(ctx context.Context, net string, laddr Addr) (fd *netFD, err er
 		f.Close()
 		return nil, err
 	}
-	return newFD(proto, name, f, nil, laddr, nil)
+	return newFD(proto, name, nil, f, nil, laddr, nil)
 }
 
 func (fd *netFD) netFD() (*netFD, error) {
-	return newFD(fd.net, fd.n, fd.ctl, fd.data, fd.laddr, fd.raddr)
+	return newFD(fd.net, fd.n, fd.listen, fd.ctl, fd.data, fd.laddr, fd.raddr)
 }
 
 func (fd *netFD) acceptPlan9() (nfd *netFD, err error) {
@@ -245,27 +253,59 @@ func (fd *netFD) acceptPlan9() (nfd *netFD, err error) {
 		return nil, err
 	}
 	defer fd.readUnlock()
-	f, err := os.Open(fd.dir + "/listen")
+	listen, err := os.Open(fd.dir + "/listen")
 	if err != nil {
 		return nil, err
 	}
 	var buf [16]byte
-	n, err := f.Read(buf[:])
+	n, err := listen.Read(buf[:])
 	if err != nil {
-		f.Close()
+		listen.Close()
 		return nil, err
 	}
 	name := string(buf[:n])
+	ctl, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/ctl", os.O_RDWR, 0)
+	if err != nil {
+		listen.Close()
+		return nil, err
+	}
 	data, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/data", os.O_RDWR, 0)
 	if err != nil {
-		f.Close()
+		listen.Close()
+		ctl.Close()
 		return nil, err
 	}
 	raddr, err := readPlan9Addr(fd.net, netdir+"/"+fd.net+"/"+name+"/remote")
 	if err != nil {
+		listen.Close()
+		ctl.Close()
 		data.Close()
-		f.Close()
 		return nil, err
 	}
-	return newFD(fd.net, name, f, data, fd.laddr, raddr)
+	return newFD(fd.net, name, listen, ctl, data, fd.laddr, raddr)
+}
+
+func isWildcard(a Addr) bool {
+	var wildcard bool
+	switch a := a.(type) {
+	case *TCPAddr:
+		wildcard = a.isWildcard()
+	case *UDPAddr:
+		wildcard = a.isWildcard()
+	case *IPAddr:
+		wildcard = a.isWildcard()
+	}
+	return wildcard
+}
+
+func toLocal(a Addr, net string) Addr {
+	switch a := a.(type) {
+	case *TCPAddr:
+		a.IP = loopbackIP(net)
+	case *UDPAddr:
+		a.IP = loopbackIP(net)
+	case *IPAddr:
+		a.IP = loopbackIP(net)
+	}
+	return a
 }
diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go
index abe90ac..f4fab3f 100644
--- a/src/net/ipsock_posix.go
+++ b/src/net/ipsock_posix.go
@@ -154,6 +154,9 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family
 
 // Internet sockets (TCP, UDP, IP)
 func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string) (fd *netFD, err error) {
+	if (runtime.GOOS == "windows" || runtime.GOOS == "openbsd" || runtime.GOOS == "nacl") && mode == "dial" && raddr.isWildcard() {
+		raddr = raddr.toLocal(net)
+	}
 	family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
 	return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr)
 }
diff --git a/src/net/ipsock_test.go b/src/net/ipsock_test.go
index b36557a..1d0f00f 100644
--- a/src/net/ipsock_test.go
+++ b/src/net/ipsock_test.go
@@ -205,13 +205,13 @@ var addrListTests = []struct {
 		nil,
 	},
 
-	{nil, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress},
+	{nil, nil, testInetaddr, nil, nil, nil, &AddrError{errNoSuitableAddress.Error(), "ADDR"}},
 
-	{ipv4only, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress},
-	{ipv4only, []IPAddr{{IP: IPv6loopback}}, testInetaddr, nil, nil, nil, errNoSuitableAddress},
+	{ipv4only, nil, testInetaddr, nil, nil, nil, &AddrError{errNoSuitableAddress.Error(), "ADDR"}},
+	{ipv4only, []IPAddr{{IP: IPv6loopback}}, testInetaddr, nil, nil, nil, &AddrError{errNoSuitableAddress.Error(), "ADDR"}},
 
-	{ipv6only, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress},
-	{ipv6only, []IPAddr{{IP: IPv4(127, 0, 0, 1)}}, testInetaddr, nil, nil, nil, errNoSuitableAddress},
+	{ipv6only, nil, testInetaddr, nil, nil, nil, &AddrError{errNoSuitableAddress.Error(), "ADDR"}},
+	{ipv6only, []IPAddr{{IP: IPv4(127, 0, 0, 1)}}, testInetaddr, nil, nil, nil, &AddrError{errNoSuitableAddress.Error(), "ADDR"}},
 }
 
 func TestAddrList(t *testing.T) {
@@ -220,8 +220,8 @@ func TestAddrList(t *testing.T) {
 	}
 
 	for i, tt := range addrListTests {
-		addrs, err := filterAddrList(tt.filter, tt.ips, tt.inetaddr)
-		if err != tt.err {
+		addrs, err := filterAddrList(tt.filter, tt.ips, tt.inetaddr, "ADDR")
+		if !reflect.DeepEqual(err, tt.err) {
 			t.Errorf("#%v: got %v; want %v", i, err, tt.err)
 		}
 		if tt.err != nil {
diff --git a/src/net/lookup.go b/src/net/lookup.go
index c169e9e..8b5cab0 100644
--- a/src/net/lookup.go
+++ b/src/net/lookup.go
@@ -15,94 +15,137 @@ import (
 // protocol numbers.
 //
 // See http://www.iana.org/assignments/protocol-numbers
+//
+// On Unix, this map is augmented by readProtocols via lookupProtocol.
 var protocols = map[string]int{
-	"icmp": 1, "ICMP": 1,
-	"igmp": 2, "IGMP": 2,
-	"tcp": 6, "TCP": 6,
-	"udp": 17, "UDP": 17,
-	"ipv6-icmp": 58, "IPV6-ICMP": 58, "IPv6-ICMP": 58,
+	"icmp":      1,
+	"igmp":      2,
+	"tcp":       6,
+	"udp":       17,
+	"ipv6-icmp": 58,
 }
 
-// LookupHost looks up the given host using the local resolver.
-// It returns an array of that host's addresses.
-func LookupHost(host string) (addrs []string, err error) {
-	// Make sure that no matter what we do later, host=="" is rejected.
-	// ParseIP, for example, does accept empty strings.
-	if host == "" {
-		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
+// services contains minimal mappings between services names and port
+// numbers for platforms that don't have a complete list of port numbers
+// (some Solaris distros, nacl, etc).
+// On Unix, this map is augmented by readServices via goLookupPort.
+var services = map[string]map[string]int{
+	"udp": {
+		"domain": 53,
+	},
+	"tcp": {
+		"ftp":    21,
+		"ftps":   990,
+		"gopher": 70, // ʕ◔ϖ◔ʔ
+		"http":   80,
+		"https":  443,
+		"imap2":  143,
+		"imap3":  220,
+		"imaps":  993,
+		"pop3":   110,
+		"pop3s":  995,
+		"smtp":   25,
+		"ssh":    22,
+		"telnet": 23,
+	},
+}
+
+const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow
+
+func lookupProtocolMap(name string) (int, error) {
+	var lowerProtocol [maxProtoLength]byte
+	n := copy(lowerProtocol[:], name)
+	lowerASCIIBytes(lowerProtocol[:n])
+	proto, found := protocols[string(lowerProtocol[:n])]
+	if !found || n != len(name) {
+		return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name}
 	}
-	if ip := ParseIP(host); ip != nil {
-		return []string{host}, nil
+	return proto, nil
+}
+
+const maxServiceLength = len("mobility-header") + 10 // with room to grow
+
+func lookupPortMap(network, service string) (port int, error error) {
+	switch network {
+	case "tcp4", "tcp6":
+		network = "tcp"
+	case "udp4", "udp6":
+		network = "udp"
 	}
-	return lookupHost(context.Background(), host)
+
+	if m, ok := services[network]; ok {
+		var lowerService [maxServiceLength]byte
+		n := copy(lowerService[:], service)
+		lowerASCIIBytes(lowerService[:n])
+		if port, ok := m[string(lowerService[:n])]; ok && n == len(service) {
+			return port, nil
+		}
+	}
+	return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service}
 }
 
-// LookupIP looks up host using the local resolver.
-// It returns an array of that host's IPv4 and IPv6 addresses.
-func LookupIP(host string) (ips []IP, err error) {
+// DefaultResolver is the resolver used by the package-level Lookup
+// functions and by Dialers without a specified Resolver.
+var DefaultResolver = &Resolver{}
+
+// A Resolver looks up names and numbers.
+//
+// A nil *Resolver is equivalent to a zero Resolver.
+type Resolver struct {
+	// PreferGo controls whether Go's built-in DNS resolver is preferred
+	// on platforms where it's available. It is equivalent to setting
+	// GODEBUG=netdns=go, but scoped to just this resolver.
+	PreferGo bool
+
+	// TODO(bradfitz): optional interface impl override hook
+	// TODO(bradfitz): Timeout time.Duration?
+}
+
+// LookupHost looks up the given host using the local resolver.
+// It returns a slice of that host's addresses.
+func LookupHost(host string) (addrs []string, err error) {
+	return DefaultResolver.LookupHost(context.Background(), host)
+}
+
+// LookupHost looks up the given host using the local resolver.
+// It returns a slice of that host's addresses.
+func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
 	// Make sure that no matter what we do later, host=="" is rejected.
 	// ParseIP, for example, does accept empty strings.
 	if host == "" {
 		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
 	}
 	if ip := ParseIP(host); ip != nil {
-		return []IP{ip}, nil
-	}
-	addrs, err := lookupIPMerge(context.Background(), host)
-	if err != nil {
-		return
-	}
-	ips = make([]IP, len(addrs))
-	for i, addr := range addrs {
-		ips[i] = addr.IP
+		return []string{host}, nil
 	}
-	return
-}
-
-var lookupGroup singleflight.Group
-
-// lookupIPMerge wraps lookupIP, but makes sure that for any given
-// host, only one lookup is in-flight at a time. The returned memory
-// is always owned by the caller.
-func lookupIPMerge(ctx context.Context, host string) (addrs []IPAddr, err error) {
-	addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) {
-		return testHookLookupIP(ctx, lookupIP, host)
-	})
-	return lookupIPReturn(addrsi, err, shared)
+	return r.lookupHost(ctx, host)
 }
 
-// lookupIPReturn turns the return values from singleflight.Do into
-// the return values from LookupIP.
-func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) {
+// LookupIP looks up host using the local resolver.
+// It returns a slice of that host's IPv4 and IPv6 addresses.
+func LookupIP(host string) ([]IP, error) {
+	addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host)
 	if err != nil {
 		return nil, err
 	}
-	addrs := addrsi.([]IPAddr)
-	if shared {
-		clone := make([]IPAddr, len(addrs))
-		copy(clone, addrs)
-		addrs = clone
+	ips := make([]IP, len(addrs))
+	for i, ia := range addrs {
+		ips[i] = ia.IP
 	}
-	return addrs, nil
+	return ips, nil
 }
 
-// ipAddrsEface returns an empty interface slice of addrs.
-func ipAddrsEface(addrs []IPAddr) []interface{} {
-	s := make([]interface{}, len(addrs))
-	for i, v := range addrs {
-		s[i] = v
+// LookupIPAddr looks up host using the local resolver.
+// It returns a slice of that host's IPv4 and IPv6 addresses.
+func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
+	// Make sure that no matter what we do later, host=="" is rejected.
+	// ParseIP, for example, does accept empty strings.
+	if host == "" {
+		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
+	}
+	if ip := ParseIP(host); ip != nil {
+		return []IPAddr{{IP: ip}}, nil
 	}
-	return s
-}
-
-// lookupIPContext looks up a hostname with a context.
-//
-// TODO(bradfitz): rename this function. All the other
-// build-tag-specific lookupIP funcs also take a context now, so this
-// name is no longer great. Maybe make this lookupIPMerge and ditch
-// the other one, making its callers call this instead with a
-// context.Background().
-func lookupIPContext(ctx context.Context, host string) (addrs []IPAddr, err error) {
 	trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
 	if trace != nil && trace.DNSStart != nil {
 		trace.DNSStart(host)
@@ -110,7 +153,7 @@ func lookupIPContext(ctx context.Context, host string) (addrs []IPAddr, err erro
 	// The underlying resolver func is lookupIP by default but it
 	// can be overridden by tests. This is needed by net/http, so it
 	// uses a context key instead of unexported variables.
-	resolverFunc := lookupIP
+	resolverFunc := r.lookupIP
 	if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string) ([]IPAddr, error)); alt != nil {
 		resolverFunc = alt
 	}
@@ -140,11 +183,46 @@ func lookupIPContext(ctx context.Context, host string) (addrs []IPAddr, err erro
 	}
 }
 
+// lookupGroup merges LookupIPAddr calls together for lookups
+// for the same host. The lookupGroup key is is the LookupIPAddr.host
+// argument.
+// The return values are ([]IPAddr, error).
+var lookupGroup singleflight.Group
+
+// lookupIPReturn turns the return values from singleflight.Do into
+// the return values from LookupIP.
+func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) {
+	if err != nil {
+		return nil, err
+	}
+	addrs := addrsi.([]IPAddr)
+	if shared {
+		clone := make([]IPAddr, len(addrs))
+		copy(clone, addrs)
+		addrs = clone
+	}
+	return addrs, nil
+}
+
+// ipAddrsEface returns an empty interface slice of addrs.
+func ipAddrsEface(addrs []IPAddr) []interface{} {
+	s := make([]interface{}, len(addrs))
+	for i, v := range addrs {
+		s[i] = v
+	}
+	return s
+}
+
 // LookupPort looks up the port for the given network and service.
 func LookupPort(network, service string) (port int, err error) {
+	return DefaultResolver.LookupPort(context.Background(), network, service)
+}
+
+// LookupPort looks up the port for the given network and service.
+func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
 	port, needsLookup := parsePort(service)
 	if needsLookup {
-		port, err = lookupPort(context.Background(), network, service)
+		port, err = r.lookupPort(ctx, network, service)
 		if err != nil {
 			return 0, err
 		}
@@ -160,7 +238,15 @@ func LookupPort(network, service string) (port int, err error) {
 // LookupHost or LookupIP directly; both take care of resolving
 // the canonical name as part of the lookup.
 func LookupCNAME(name string) (cname string, err error) {
-	return lookupCNAME(context.Background(), name)
+	return DefaultResolver.lookupCNAME(context.Background(), name)
+}
+
+// LookupCNAME returns the canonical DNS host for the given name.
+// Callers that do not care about the canonical name can call
+// LookupHost or LookupIP directly; both take care of resolving
+// the canonical name as part of the lookup.
+func (r *Resolver) LookupCNAME(ctx context.Context, name string) (cname string, err error) {
+	return r.lookupCNAME(ctx, name)
 }
 
 // LookupSRV tries to resolve an SRV query of the given service,
@@ -173,26 +259,63 @@ func LookupCNAME(name string) (cname string, err error) {
 // publishing SRV records under non-standard names, if both service
 // and proto are empty strings, LookupSRV looks up name directly.
 func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
-	return lookupSRV(context.Background(), service, proto, name)
+	return DefaultResolver.lookupSRV(context.Background(), service, proto, name)
+}
+
+// LookupSRV tries to resolve an SRV query of the given service,
+// protocol, and domain name. The proto is "tcp" or "udp".
+// The returned records are sorted by priority and randomized
+// by weight within a priority.
+//
+// LookupSRV constructs the DNS name to look up following RFC 2782.
+// That is, it looks up _service._proto.name. To accommodate services
+// publishing SRV records under non-standard names, if both service
+// and proto are empty strings, LookupSRV looks up name directly.
+func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
+	return r.lookupSRV(ctx, service, proto, name)
+}
+
+// LookupMX returns the DNS MX records for the given domain name sorted by preference.
+func LookupMX(name string) ([]*MX, error) {
+	return DefaultResolver.lookupMX(context.Background(), name)
 }
 
 // LookupMX returns the DNS MX records for the given domain name sorted by preference.
-func LookupMX(name string) (mxs []*MX, err error) {
-	return lookupMX(context.Background(), name)
+func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) {
+	return r.lookupMX(ctx, name)
 }
 
 // LookupNS returns the DNS NS records for the given domain name.
-func LookupNS(name string) (nss []*NS, err error) {
-	return lookupNS(context.Background(), name)
+func LookupNS(name string) ([]*NS, error) {
+	return DefaultResolver.lookupNS(context.Background(), name)
+}
+
+// LookupNS returns the DNS NS records for the given domain name.
+func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) {
+	return r.lookupNS(ctx, name)
 }
 
 // LookupTXT returns the DNS TXT records for the given domain name.
-func LookupTXT(name string) (txts []string, err error) {
-	return lookupTXT(context.Background(), name)
+func LookupTXT(name string) ([]string, error) {
+	return DefaultResolver.lookupTXT(context.Background(), name)
+}
+
+// LookupTXT returns the DNS TXT records for the given domain name.
+func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
+	return r.lookupTXT(ctx, name)
 }
 
 // LookupAddr performs a reverse lookup for the given address, returning a list
 // of names mapping to that address.
+//
+// When using the host C library resolver, at most one result will be
+// returned. To bypass the host resolver, use a custom Resolver.
 func LookupAddr(addr string) (names []string, err error) {
-	return lookupAddr(context.Background(), addr)
+	return DefaultResolver.lookupAddr(context.Background(), addr)
+}
+
+// LookupAddr performs a reverse lookup for the given address, returning a list
+// of names mapping to that address.
+func (r *Resolver) LookupAddr(ctx context.Context, addr string) (names []string, err error) {
+	return r.lookupAddr(ctx, addr)
 }
diff --git a/src/net/lookup_nacl.go b/src/net/lookup_nacl.go
new file mode 100644
index 0000000..43cebad
--- /dev/null
+++ b/src/net/lookup_nacl.go
@@ -0,0 +1,52 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build nacl
+
+package net
+
+import (
+	"context"
+	"syscall"
+)
+
+func lookupProtocol(ctx context.Context, name string) (proto int, err error) {
+	return lookupProtocolMap(name)
+}
+
+func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
+	return nil, syscall.ENOPROTOOPT
+}
+
+func (*Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
+	return nil, syscall.ENOPROTOOPT
+}
+
+func (*Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
+	return goLookupPort(network, service)
+}
+
+func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
+	return "", syscall.ENOPROTOOPT
+}
+
+func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, srvs []*SRV, err error) {
+	return "", nil, syscall.ENOPROTOOPT
+}
+
+func (*Resolver) lookupMX(ctx context.Context, name string) (mxs []*MX, err error) {
+	return nil, syscall.ENOPROTOOPT
+}
+
+func (*Resolver) lookupNS(ctx context.Context, name string) (nss []*NS, err error) {
+	return nil, syscall.ENOPROTOOPT
+}
+
+func (*Resolver) lookupTXT(ctx context.Context, name string) (txts []string, err error) {
+	return nil, syscall.ENOPROTOOPT
+}
+
+func (*Resolver) lookupAddr(ctx context.Context, addr string) (ptrs []string, err error) {
+	return nil, syscall.ENOPROTOOPT
+}
diff --git a/src/net/lookup_plan9.go b/src/net/lookup_plan9.go
index 3f7af2a..11f2349 100644
--- a/src/net/lookup_plan9.go
+++ b/src/net/lookup_plan9.go
@@ -111,17 +111,20 @@ func lookupProtocol(ctx context.Context, name string) (proto int, err error) {
 		return 0, UnknownNetworkError(name)
 	}
 	s := f[1]
-	if n, _, ok := dtoi(s, byteIndex(s, '=')+1); ok {
+	if n, _, ok := dtoi(s[byteIndex(s, '=')+1:]); ok {
 		return n, nil
 	}
 	return 0, UnknownNetworkError(name)
 }
 
-func lookupHost(ctx context.Context, host string) (addrs []string, err error) {
+func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
 	// Use netdir/cs instead of netdir/dns because cs knows about
 	// host names in local network (e.g. from /lib/ndb/local)
 	lines, err := queryCS(ctx, "net", host, "1")
 	if err != nil {
+		if stringsHasSuffix(err.Error(), "dns failure") {
+			err = errNoSuchHost
+		}
 		return
 	}
 loop:
@@ -148,8 +151,8 @@ loop:
 	return
 }
 
-func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
-	lits, err := lookupHost(ctx, host)
+func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
+	lits, err := r.lookupHost(ctx, host)
 	if err != nil {
 		return
 	}
@@ -163,14 +166,14 @@ func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
 	return
 }
 
-func lookupPort(ctx context.Context, network, service string) (port int, err error) {
+func (*Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
 	switch network {
 	case "tcp4", "tcp6":
 		network = "tcp"
 	case "udp4", "udp6":
 		network = "udp"
 	}
-	lines, err := queryCS(ctx, network, "127.0.0.1", service)
+	lines, err := queryCS(ctx, network, "127.0.0.1", toLower(service))
 	if err != nil {
 		return
 	}
@@ -186,13 +189,13 @@ func lookupPort(ctx context.Context, network, service string) (port int, err err
 	if i := byteIndex(s, '!'); i >= 0 {
 		s = s[i+1:] // remove address
 	}
-	if n, _, ok := dtoi(s, 0); ok {
+	if n, _, ok := dtoi(s); ok {
 		return n, nil
 	}
 	return 0, unknownPortError
 }
 
-func lookupCNAME(ctx context.Context, name string) (cname string, err error) {
+func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
 	lines, err := queryDNS(ctx, name, "cname")
 	if err != nil {
 		return
@@ -205,7 +208,7 @@ func lookupCNAME(ctx context.Context, name string) (cname string, err error) {
 	return "", errors.New("bad response from ndb/dns")
 }
 
-func lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
+func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
 	var target string
 	if service == "" && proto == "" {
 		target = name
@@ -221,9 +224,9 @@ func lookupSRV(ctx context.Context, service, proto, name string) (cname string,
 		if len(f) < 6 {
 			continue
 		}
-		port, _, portOk := dtoi(f[4], 0)
-		priority, _, priorityOk := dtoi(f[3], 0)
-		weight, _, weightOk := dtoi(f[2], 0)
+		port, _, portOk := dtoi(f[4])
+		priority, _, priorityOk := dtoi(f[3])
+		weight, _, weightOk := dtoi(f[2])
 		if !(portOk && priorityOk && weightOk) {
 			continue
 		}
@@ -234,7 +237,7 @@ func lookupSRV(ctx context.Context, service, proto, name string) (cname string,
 	return
 }
 
-func lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
+func (*Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
 	lines, err := queryDNS(ctx, name, "mx")
 	if err != nil {
 		return
@@ -244,7 +247,7 @@ func lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
 		if len(f) < 4 {
 			continue
 		}
-		if pref, _, ok := dtoi(f[2], 0); ok {
+		if pref, _, ok := dtoi(f[2]); ok {
 			mx = append(mx, &MX{absDomainName([]byte(f[3])), uint16(pref)})
 		}
 	}
@@ -252,7 +255,7 @@ func lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
 	return
 }
 
-func lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
+func (*Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
 	lines, err := queryDNS(ctx, name, "ns")
 	if err != nil {
 		return
@@ -267,7 +270,7 @@ func lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
 	return
 }
 
-func lookupTXT(ctx context.Context, name string) (txt []string, err error) {
+func (*Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) {
 	lines, err := queryDNS(ctx, name, "txt")
 	if err != nil {
 		return
@@ -280,7 +283,7 @@ func lookupTXT(ctx context.Context, name string) (txt []string, err error) {
 	return
 }
 
-func lookupAddr(ctx context.Context, addr string) (name []string, err error) {
+func (*Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) {
 	arpa, err := reverseaddr(addr)
 	if err != nil {
 		return
diff --git a/src/net/lookup_stub.go b/src/net/lookup_stub.go
deleted file mode 100644
index bd096b3..0000000
--- a/src/net/lookup_stub.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build nacl
-
-package net
-
-import (
-	"context"
-	"syscall"
-)
-
-func lookupProtocol(ctx context.Context, name string) (proto int, err error) {
-	return 0, syscall.ENOPROTOOPT
-}
-
-func lookupHost(ctx context.Context, host string) (addrs []string, err error) {
-	return nil, syscall.ENOPROTOOPT
-}
-
-func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
-	return nil, syscall.ENOPROTOOPT
-}
-
-func lookupPort(ctx context.Context, network, service string) (port int, err error) {
-	return 0, syscall.ENOPROTOOPT
-}
-
-func lookupCNAME(ctx context.Context, name string) (cname string, err error) {
-	return "", syscall.ENOPROTOOPT
-}
-
-func lookupSRV(ctx context.Context, service, proto, name string) (cname string, srvs []*SRV, err error) {
-	return "", nil, syscall.ENOPROTOOPT
-}
-
-func lookupMX(ctx context.Context, name string) (mxs []*MX, err error) {
-	return nil, syscall.ENOPROTOOPT
-}
-
-func lookupNS(ctx context.Context, name string) (nss []*NS, err error) {
-	return nil, syscall.ENOPROTOOPT
-}
-
-func lookupTXT(ctx context.Context, name string) (txts []string, err error) {
-	return nil, syscall.ENOPROTOOPT
-}
-
-func lookupAddr(ctx context.Context, addr string) (ptrs []string, err error) {
-	return nil, syscall.ENOPROTOOPT
-}
diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go
index b3aeb85..656bebb 100644
--- a/src/net/lookup_test.go
+++ b/src/net/lookup_test.go
@@ -398,11 +398,11 @@ func TestDNSFlood(t *testing.T) {
 	for i := 0; i < N; i++ {
 		name := fmt.Sprintf("%d.net-test.golang.org", i)
 		go func() {
-			_, err := lookupIPContext(ctxHalfTimeout, name)
+			_, err := DefaultResolver.LookupIPAddr(ctxHalfTimeout, name)
 			c <- err
 		}()
 		go func() {
-			_, err := lookupIPContext(ctxTimeout, name)
+			_, err := DefaultResolver.LookupIPAddr(ctxTimeout, name)
 			c <- err
 		}()
 	}
@@ -616,7 +616,7 @@ func srvString(srvs []*SRV) string {
 func TestLookupPort(t *testing.T) {
 	// See http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
 	//
-	// Please be careful about adding new mappings for testings.
+	// Please be careful about adding new test cases.
 	// There are platforms having incomplete mappings for
 	// restricted resource access and security reasons.
 	type test struct {
@@ -648,8 +648,6 @@ func TestLookupPort(t *testing.T) {
 	}
 
 	switch runtime.GOOS {
-	case "nacl":
-		t.Skipf("not supported on %s", runtime.GOOS)
 	case "android":
 		if netGo {
 			t.Skipf("not supported on %s without cgo; see golang.org/issues/14576", runtime.GOOS)
@@ -670,3 +668,73 @@ func TestLookupPort(t *testing.T) {
 		}
 	}
 }
+
+// Like TestLookupPort but with minimal tests that should always pass
+// because the answers are baked-in to the net package.
+func TestLookupPort_Minimal(t *testing.T) {
+	type test struct {
+		network string
+		name    string
+		port    int
+	}
+	var tests = []test{
+		{"tcp", "http", 80},
+		{"tcp", "HTTP", 80}, // case shouldn't matter
+		{"tcp", "https", 443},
+		{"tcp", "ssh", 22},
+		{"tcp", "gopher", 70},
+		{"tcp4", "http", 80},
+		{"tcp6", "http", 80},
+	}
+
+	for _, tt := range tests {
+		port, err := LookupPort(tt.network, tt.name)
+		if port != tt.port || err != nil {
+			t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=nil", tt.network, tt.name, port, err, tt.port)
+		}
+	}
+}
+
+func TestLookupProtocol_Minimal(t *testing.T) {
+	type test struct {
+		name string
+		want int
+	}
+	var tests = []test{
+		{"tcp", 6},
+		{"TcP", 6}, // case shouldn't matter
+		{"icmp", 1},
+		{"igmp", 2},
+		{"udp", 17},
+		{"ipv6-icmp", 58},
+	}
+
+	for _, tt := range tests {
+		got, err := lookupProtocol(context.Background(), tt.name)
+		if got != tt.want || err != nil {
+			t.Errorf("LookupProtocol(%q) = %d, %v; want %d, error=nil", tt.name, got, err, tt.want)
+		}
+	}
+
+}
+
+func TestLookupNonLDH(t *testing.T) {
+	if runtime.GOOS == "nacl" {
+		t.Skip("skip on nacl")
+	}
+	if fixup := forceGoDNS(); fixup != nil {
+		defer fixup()
+	}
+
+	// "LDH" stands for letters, digits, and hyphens and is the usual
+	// description of standard DNS names.
+	// This test is checking that other kinds of names are reported
+	// as not found, not reported as invalid names.
+	addrs, err := LookupHost("!!!.###.bogus..domain.")
+	if err == nil {
+		t.Fatalf("lookup succeeded: %v", addrs)
+	}
+	if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) {
+		t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost)
+	}
+}
diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go
index 15397e8..35f253c 100644
--- a/src/net/lookup_unix.go
+++ b/src/net/lookup_unix.go
@@ -26,7 +26,7 @@ func readProtocols() {
 			if len(f) < 2 {
 				continue
 			}
-			if proto, _, ok := dtoi(f[1], 0); ok {
+			if proto, _, ok := dtoi(f[1]); ok {
 				if _, ok := protocols[f[0]]; !ok {
 					protocols[f[0]] = proto
 				}
@@ -45,16 +45,12 @@ func readProtocols() {
 // returns correspondent protocol number.
 func lookupProtocol(_ context.Context, name string) (int, error) {
 	onceReadProtocols.Do(readProtocols)
-	proto, found := protocols[name]
-	if !found {
-		return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name}
-	}
-	return proto, nil
+	return lookupProtocolMap(name)
 }
 
-func lookupHost(ctx context.Context, host string) (addrs []string, err error) {
+func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
 	order := systemConf().hostLookupOrder(host)
-	if order == hostLookupCgo {
+	if !r.PreferGo && order == hostLookupCgo {
 		if addrs, err, ok := cgoLookupHost(ctx, host); ok {
 			return addrs, err
 		}
@@ -64,7 +60,10 @@ func lookupHost(ctx context.Context, host string) (addrs []string, err error) {
 	return goLookupHostOrder(ctx, host, order)
 }
 
-func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
+func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
+	if r.PreferGo {
+		return goLookupIP(ctx, host)
+	}
 	order := systemConf().hostLookupOrder(host)
 	if order == hostLookupCgo {
 		if addrs, err, ok := cgoLookupIP(ctx, host); ok {
@@ -76,13 +75,13 @@ func lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
 	return goLookupIPOrder(ctx, host, order)
 }
 
-func lookupPort(ctx context.Context, network, service string) (int, error) {
+func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) {
 	// TODO: use the context if there ever becomes a need. Related
 	// is issue 15321. But port lookup generally just involves
 	// local files, and the os package has no context support. The
 	// files might be on a remote filesystem, though. This should
 	// probably race goroutines if ctx != context.Background().
-	if systemConf().canUseCgo() {
+	if !r.PreferGo && systemConf().canUseCgo() {
 		if port, err, ok := cgoLookupPort(ctx, network, service); ok {
 			return port, err
 		}
@@ -90,8 +89,8 @@ func lookupPort(ctx context.Context, network, service string) (int, error) {
 	return goLookupPort(network, service)
 }
 
-func lookupCNAME(ctx context.Context, name string) (string, error) {
-	if systemConf().canUseCgo() {
+func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) {
+	if !r.PreferGo && systemConf().canUseCgo() {
 		if cname, err, ok := cgoLookupCNAME(ctx, name); ok {
 			return cname, err
 		}
@@ -99,7 +98,7 @@ func lookupCNAME(ctx context.Context, name string) (string, error) {
 	return goLookupCNAME(ctx, name)
 }
 
-func lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
+func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
 	var target string
 	if service == "" && proto == "" {
 		target = name
@@ -119,7 +118,7 @@ func lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV
 	return cname, srvs, nil
 }
 
-func lookupMX(ctx context.Context, name string) ([]*MX, error) {
+func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
 	_, rrs, err := lookup(ctx, name, dnsTypeMX)
 	if err != nil {
 		return nil, err
@@ -133,7 +132,7 @@ func lookupMX(ctx context.Context, name string) ([]*MX, error) {
 	return mxs, nil
 }
 
-func lookupNS(ctx context.Context, name string) ([]*NS, error) {
+func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
 	_, rrs, err := lookup(ctx, name, dnsTypeNS)
 	if err != nil {
 		return nil, err
@@ -145,7 +144,7 @@ func lookupNS(ctx context.Context, name string) ([]*NS, error) {
 	return nss, nil
 }
 
-func lookupTXT(ctx context.Context, name string) ([]string, error) {
+func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
 	_, rrs, err := lookup(ctx, name, dnsTypeTXT)
 	if err != nil {
 		return nil, err
@@ -157,8 +156,8 @@ func lookupTXT(ctx context.Context, name string) ([]string, error) {
 	return txts, nil
 }
 
-func lookupAddr(ctx context.Context, addr string) ([]string, error) {
-	if systemConf().canUseCgo() {
+func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) {
+	if !r.PreferGo && systemConf().canUseCgo() {
 		if ptrs, err, ok := cgoLookupPTR(ctx, addr); ok {
 			return ptrs, err
 		}
diff --git a/src/net/lookup_windows.go b/src/net/lookup_windows.go
index 5f65c2d..5808293 100644
--- a/src/net/lookup_windows.go
+++ b/src/net/lookup_windows.go
@@ -12,10 +12,20 @@ import (
 	"unsafe"
 )
 
+const _WSAHOST_NOT_FOUND = syscall.Errno(11001)
+
+func winError(call string, err error) error {
+	switch err {
+	case _WSAHOST_NOT_FOUND:
+		return errNoSuchHost
+	}
+	return os.NewSyscallError(call, err)
+}
+
 func getprotobyname(name string) (proto int, err error) {
 	p, err := syscall.GetProtoByName(name)
 	if err != nil {
-		return 0, os.NewSyscallError("getprotobyname", err)
+		return 0, winError("getprotobyname", err)
 	}
 	return int(p.Proto), nil
 }
@@ -43,7 +53,7 @@ func lookupProtocol(ctx context.Context, name string) (int, error) {
 	select {
 	case r := <-ch:
 		if r.err != nil {
-			if proto, ok := protocols[name]; ok {
+			if proto, err := lookupProtocolMap(name); err == nil {
 				return proto, nil
 			}
 			r.err = &DNSError{Err: r.err.Error(), Name: name}
@@ -54,8 +64,8 @@ func lookupProtocol(ctx context.Context, name string) (int, error) {
 	}
 }
 
-func lookupHost(ctx context.Context, name string) ([]string, error) {
-	ips, err := lookupIP(ctx, name)
+func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error) {
+	ips, err := r.lookupIP(ctx, name)
 	if err != nil {
 		return nil, err
 	}
@@ -66,8 +76,8 @@ func lookupHost(ctx context.Context, name string) ([]string, error) {
 	return addrs, nil
 }
 
-func lookupIP(ctx context.Context, name string) ([]IPAddr, error) {
-	// TODO(bradfitz,brainman): use ctx?
+func (r *Resolver) lookupIP(ctx context.Context, name string) ([]IPAddr, error) {
+	// TODO(bradfitz,brainman): use ctx more. See TODO below.
 
 	type ret struct {
 		addrs []IPAddr
@@ -85,7 +95,7 @@ func lookupIP(ctx context.Context, name string) ([]IPAddr, error) {
 		var result *syscall.AddrinfoW
 		e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result)
 		if e != nil {
-			ch <- ret{err: &DNSError{Err: os.NewSyscallError("getaddrinfow", e).Error(), Name: name}}
+			ch <- ret{err: &DNSError{Err: winError("getaddrinfow", e).Error(), Name: name}}
 		}
 		defer syscall.FreeAddrInfoW(result)
 		addrs := make([]IPAddr, 0, 5)
@@ -125,7 +135,11 @@ func lookupIP(ctx context.Context, name string) ([]IPAddr, error) {
 	}
 }
 
-func lookupPort(ctx context.Context, network, service string) (int, error) {
+func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) {
+	if r.PreferGo {
+		return lookupPortMap(network, service)
+	}
+
 	// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
 	acquireThread()
 	defer releaseThread()
@@ -144,7 +158,10 @@ func lookupPort(ctx context.Context, network, service string) (int, error) {
 	var result *syscall.AddrinfoW
 	e := syscall.GetAddrInfoW(nil, syscall.StringToUTF16Ptr(service), &hints, &result)
 	if e != nil {
-		return 0, &DNSError{Err: os.NewSyscallError("getaddrinfow", e).Error(), Name: network + "/" + service}
+		if port, err := lookupPortMap(network, service); err == nil {
+			return port, nil
+		}
+		return 0, &DNSError{Err: winError("getaddrinfow", e).Error(), Name: network + "/" + service}
 	}
 	defer syscall.FreeAddrInfoW(result)
 	if result == nil {
@@ -162,7 +179,7 @@ func lookupPort(ctx context.Context, network, service string) (int, error) {
 	return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service}
 }
 
-func lookupCNAME(ctx context.Context, name string) (string, error) {
+func (*Resolver) lookupCNAME(ctx context.Context, name string) (string, error) {
 	// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
 	acquireThread()
 	defer releaseThread()
@@ -174,7 +191,7 @@ func lookupCNAME(ctx context.Context, name string) (string, error) {
 		return absDomainName([]byte(name)), nil
 	}
 	if e != nil {
-		return "", &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: name}
+		return "", &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
 	}
 	defer syscall.DnsRecordListFree(r, 1)
 
@@ -183,7 +200,7 @@ func lookupCNAME(ctx context.Context, name string) (string, error) {
 	return absDomainName([]byte(cname)), nil
 }
 
-func lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
+func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
 	// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
 	acquireThread()
 	defer releaseThread()
@@ -196,7 +213,7 @@ func lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV
 	var r *syscall.DNSRecord
 	e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil)
 	if e != nil {
-		return "", nil, &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: target}
+		return "", nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: target}
 	}
 	defer syscall.DnsRecordListFree(r, 1)
 
@@ -209,14 +226,14 @@ func lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV
 	return absDomainName([]byte(target)), srvs, nil
 }
 
-func lookupMX(ctx context.Context, name string) ([]*MX, error) {
+func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
 	// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
 	acquireThread()
 	defer releaseThread()
 	var r *syscall.DNSRecord
 	e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil)
 	if e != nil {
-		return nil, &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: name}
+		return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
 	}
 	defer syscall.DnsRecordListFree(r, 1)
 
@@ -229,14 +246,14 @@ func lookupMX(ctx context.Context, name string) ([]*MX, error) {
 	return mxs, nil
 }
 
-func lookupNS(ctx context.Context, name string) ([]*NS, error) {
+func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
 	// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
 	acquireThread()
 	defer releaseThread()
 	var r *syscall.DNSRecord
 	e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil)
 	if e != nil {
-		return nil, &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: name}
+		return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
 	}
 	defer syscall.DnsRecordListFree(r, 1)
 
@@ -248,14 +265,14 @@ func lookupNS(ctx context.Context, name string) ([]*NS, error) {
 	return nss, nil
 }
 
-func lookupTXT(ctx context.Context, name string) ([]string, error) {
+func (*Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
 	// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
 	acquireThread()
 	defer releaseThread()
 	var r *syscall.DNSRecord
 	e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
 	if e != nil {
-		return nil, &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: name}
+		return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
 	}
 	defer syscall.DnsRecordListFree(r, 1)
 
@@ -270,7 +287,7 @@ func lookupTXT(ctx context.Context, name string) ([]string, error) {
 	return txts, nil
 }
 
-func lookupAddr(ctx context.Context, addr string) ([]string, error) {
+func (*Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) {
 	// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
 	acquireThread()
 	defer releaseThread()
@@ -281,7 +298,7 @@ func lookupAddr(ctx context.Context, addr string) ([]string, error) {
 	var r *syscall.DNSRecord
 	e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil)
 	if e != nil {
-		return nil, &DNSError{Err: os.NewSyscallError("dnsquery", e).Error(), Name: addr}
+		return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: addr}
 	}
 	defer syscall.DnsRecordListFree(r, 1)
 
diff --git a/src/net/lookup_windows_test.go b/src/net/lookup_windows_test.go
index 9af2c61..bc9ffe1 100644
--- a/src/net/lookup_windows_test.go
+++ b/src/net/lookup_windows_test.go
@@ -169,14 +169,14 @@ func nslookupMX(name string) (mx []*MX, err error) {
 	// golang.org      mail exchanger = 2 alt1.aspmx.l.google.com.
 	rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+mail exchanger\s*=\s*([0-9]+)\s*([a-z0-9.\-]+)$`)
 	for _, ans := range rx.FindAllStringSubmatch(r, -1) {
-		pref, _, _ := dtoi(ans[2], 0)
+		pref, _, _ := dtoi(ans[2])
 		mx = append(mx, &MX{absDomainName([]byte(ans[3])), uint16(pref)})
 	}
 	// windows nslookup syntax
 	// gmail.com       MX preference = 30, mail exchanger = alt3.gmail-smtp-in.l.google.com
 	rx = regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+MX preference\s*=\s*([0-9]+)\s*,\s*mail exchanger\s*=\s*([a-z0-9.\-]+)$`)
 	for _, ans := range rx.FindAllStringSubmatch(r, -1) {
-		pref, _, _ := dtoi(ans[2], 0)
+		pref, _, _ := dtoi(ans[2])
 		mx = append(mx, &MX{absDomainName([]byte(ans[3])), uint16(pref)})
 	}
 	return
diff --git a/src/net/mail/message.go b/src/net/mail/message.go
index 0c00069..702b765 100644
--- a/src/net/mail/message.go
+++ b/src/net/mail/message.go
@@ -92,7 +92,8 @@ func init() {
 	}
 }
 
-func parseDate(date string) (time.Time, error) {
+// ParseDate parses an RFC 5322 date string.
+func ParseDate(date string) (time.Time, error) {
 	for _, layout := range dateLayouts {
 		t, err := time.Parse(layout, date)
 		if err == nil {
@@ -106,7 +107,11 @@ func parseDate(date string) (time.Time, error) {
 type Header map[string][]string
 
 // Get gets the first value associated with the given key.
+// It is case insensitive; CanonicalMIMEHeaderKey is used
+// to canonicalize the provided key.
 // If there are no values associated with the key, Get returns "".
+// To access multiple values of a key, or to use non-canonical keys,
+// access the map directly.
 func (h Header) Get(key string) string {
 	return textproto.MIMEHeader(h).Get(key)
 }
@@ -119,7 +124,7 @@ func (h Header) Date() (time.Time, error) {
 	if hdr == "" {
 		return time.Time{}, ErrHeaderNotPresent
 	}
-	return parseDate(hdr)
+	return ParseDate(hdr)
 }
 
 // AddressList parses the named header field as a list of addresses.
@@ -345,6 +350,9 @@ func (p *addrParser) consumeAddrSpec() (spec string, err error) {
 		// quoted-string
 		debug.Printf("consumeAddrSpec: parsing quoted-string")
 		localPart, err = p.consumeQuotedString()
+		if localPart == "" {
+			err = errors.New("mail: empty quoted string in addr-spec")
+		}
 	} else {
 		// dot-atom
 		debug.Printf("consumeAddrSpec: parsing dot-atom")
@@ -462,9 +470,6 @@ Loop:
 		i += size
 	}
 	p.s = p.s[i+1:]
-	if len(qsb) == 0 {
-		return "", errors.New("mail: empty quoted-string")
-	}
 	return string(qsb), nil
 }
 
diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go
index bbbba6b..f0761ab 100644
--- a/src/net/mail/message_test.go
+++ b/src/net/mail/message_test.go
@@ -110,11 +110,16 @@ func TestDateParsing(t *testing.T) {
 		}
 		date, err := hdr.Date()
 		if err != nil {
-			t.Errorf("Failed parsing %q: %v", test.dateStr, err)
-			continue
+			t.Errorf("Header(Date: %s).Date(): %v", test.dateStr, err)
+		} else if !date.Equal(test.exp) {
+			t.Errorf("Header(Date: %s).Date() = %+v, want %+v", test.dateStr, date, test.exp)
 		}
-		if !date.Equal(test.exp) {
-			t.Errorf("Parse of %q: got %+v, want %+v", test.dateStr, date, test.exp)
+
+		date, err = ParseDate(test.dateStr)
+		if err != nil {
+			t.Errorf("ParseDate(%s): %v", test.dateStr, err)
+		} else if !date.Equal(test.exp) {
+			t.Errorf("ParseDate(%s) = %+v, want %+v", test.dateStr, date, test.exp)
 		}
 	}
 }
@@ -310,6 +315,16 @@ func TestAddressParsing(t *testing.T) {
 				},
 			},
 		},
+		// Issue 14866
+		{
+			`"" <emptystring at example.com>`,
+			[]*Address{
+				{
+					Name:    "",
+					Address: "emptystring at example.com",
+				},
+			},
+		},
 	}
 	for _, test := range tests {
 		if len(test.exp) == 1 {
diff --git a/src/net/main_test.go b/src/net/main_test.go
index 7573ded..28a8ff6 100644
--- a/src/net/main_test.go
+++ b/src/net/main_test.go
@@ -24,6 +24,8 @@ var (
 )
 
 var (
+	testTCPBig = flag.Bool("tcpbig", false, "whether to test massive size of data per read or write call on TCP connection")
+
 	testDNSFlood = flag.Bool("dnsflood", false, "whether to test DNS query flooding")
 
 	// If external IPv4 connectivity exists, we can try dialing
diff --git a/src/net/net.go b/src/net/net.go
index d6812d1..81206ea 100644
--- a/src/net/net.go
+++ b/src/net/net.go
@@ -102,9 +102,13 @@ func init() {
 }
 
 // Addr represents a network end point address.
+//
+// The two methods Network and String conventionally return strings
+// that can be passed as the arguments to Dial, but the exact form
+// and meaning of the strings is up to the implementation.
 type Addr interface {
-	Network() string // name of the network
-	String() string  // string form of address
+	Network() string // name of the network (for example, "tcp", "udp")
+	String() string  // string form of address (for example, "192.0.2.1:25", "[2001:db8::1]:80")
 }
 
 // Conn is a generic stream-oriented network connection.
@@ -112,12 +116,12 @@ type Addr interface {
 // Multiple goroutines may invoke methods on a Conn simultaneously.
 type Conn interface {
 	// Read reads data from the connection.
-	// Read can be made to time out and return a Error with Timeout() == true
+	// Read can be made to time out and return an Error with Timeout() == true
 	// after a fixed time limit; see SetDeadline and SetReadDeadline.
 	Read(b []byte) (n int, err error)
 
 	// Write writes data to the connection.
-	// Write can be made to time out and return a Error with Timeout() == true
+	// Write can be made to time out and return an Error with Timeout() == true
 	// after a fixed time limit; see SetDeadline and SetWriteDeadline.
 	Write(b []byte) (n int, err error)
 
@@ -137,8 +141,10 @@ type Conn interface {
 	//
 	// A deadline is an absolute time after which I/O operations
 	// fail with a timeout (see type Error) instead of
-	// blocking. The deadline applies to all future I/O, not just
-	// the immediately following call to Read or Write.
+	// blocking. The deadline applies to all future and pending
+	// I/O, not just the immediately following call to Read or
+	// Write. After a deadline has been exceeded, the connection
+	// can be refreshed by setting a deadline in the future.
 	//
 	// An idle timeout can be implemented by repeatedly extending
 	// the deadline after successful Read or Write calls.
@@ -146,11 +152,13 @@ type Conn interface {
 	// A zero value for t means I/O operations will not time out.
 	SetDeadline(t time.Time) error
 
-	// SetReadDeadline sets the deadline for future Read calls.
+	// SetReadDeadline sets the deadline for future Read calls
+	// and any currently-blocked Read call.
 	// A zero value for t means Read will not time out.
 	SetReadDeadline(t time.Time) error
 
-	// SetWriteDeadline sets the deadline for future Write calls.
+	// SetWriteDeadline sets the deadline for future Write calls
+	// and any currently-blocked Write call.
 	// Even if write times out, it may return n > 0, indicating that
 	// some of the data was successfully written.
 	// A zero value for t means Write will not time out.
@@ -302,13 +310,13 @@ type PacketConn interface {
 	// bytes copied into b and the return address that
 	// was on the packet.
 	// ReadFrom can be made to time out and return
-	// an error with Timeout() == true after a fixed time limit;
+	// an Error with Timeout() == true after a fixed time limit;
 	// see SetDeadline and SetReadDeadline.
 	ReadFrom(b []byte) (n int, addr Addr, err error)
 
 	// WriteTo writes a packet with payload b to addr.
 	// WriteTo can be made to time out and return
-	// an error with Timeout() == true after a fixed time limit;
+	// an Error with Timeout() == true after a fixed time limit;
 	// see SetDeadline and SetWriteDeadline.
 	// On packet-oriented connections, write timeouts are rare.
 	WriteTo(b []byte, addr Addr) (n int, err error)
@@ -321,21 +329,32 @@ type PacketConn interface {
 	LocalAddr() Addr
 
 	// SetDeadline sets the read and write deadlines associated
-	// with the connection.
+	// with the connection. It is equivalent to calling both
+	// SetReadDeadline and SetWriteDeadline.
+	//
+	// A deadline is an absolute time after which I/O operations
+	// fail with a timeout (see type Error) instead of
+	// blocking. The deadline applies to all future and pending
+	// I/O, not just the immediately following call to ReadFrom or
+	// WriteTo. After a deadline has been exceeded, the connection
+	// can be refreshed by setting a deadline in the future.
+	//
+	// An idle timeout can be implemented by repeatedly extending
+	// the deadline after successful ReadFrom or WriteTo calls.
+	//
+	// A zero value for t means I/O operations will not time out.
 	SetDeadline(t time.Time) error
 
-	// SetReadDeadline sets the deadline for future Read calls.
-	// If the deadline is reached, Read will fail with a timeout
-	// (see type Error) instead of blocking.
-	// A zero value for t means Read will not time out.
+	// SetReadDeadline sets the deadline for future ReadFrom calls
+	// and any currently-blocked ReadFrom call.
+	// A zero value for t means ReadFrom will not time out.
 	SetReadDeadline(t time.Time) error
 
-	// SetWriteDeadline sets the deadline for future Write calls.
-	// If the deadline is reached, Write will fail with a timeout
-	// (see type Error) instead of blocking.
-	// A zero value for t means Write will not time out.
+	// SetWriteDeadline sets the deadline for future WriteTo calls
+	// and any currently-blocked WriteTo call.
 	// Even if write times out, it may return n > 0, indicating that
 	// some of the data was successfully written.
+	// A zero value for t means WriteTo will not time out.
 	SetWriteDeadline(t time.Time) error
 }
 
@@ -512,7 +531,7 @@ func (e *AddrError) Error() string {
 	}
 	s := e.Err
 	if e.Addr != "" {
-		s += " " + e.Addr
+		s = "address " + e.Addr + ": " + s
 	}
 	return s
 }
@@ -604,3 +623,66 @@ func acquireThread() {
 func releaseThread() {
 	<-threadLimit
 }
+
+// buffersWriter is the interface implemented by Conns that support a
+// "writev"-like batch write optimization.
+// writeBuffers should fully consume and write all chunks from the
+// provided Buffers, else it should report a non-nil error.
+type buffersWriter interface {
+	writeBuffers(*Buffers) (int64, error)
+}
+
+var testHookDidWritev = func(wrote int) {}
+
+// Buffers contains zero or more runs of bytes to write.
+//
+// On certain machines, for certain types of connections, this is
+// optimized into an OS-specific batch write operation (such as
+// "writev").
+type Buffers [][]byte
+
+var (
+	_ io.WriterTo = (*Buffers)(nil)
+	_ io.Reader   = (*Buffers)(nil)
+)
+
+func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) {
+	if wv, ok := w.(buffersWriter); ok {
+		return wv.writeBuffers(v)
+	}
+	for _, b := range *v {
+		nb, err := w.Write(b)
+		n += int64(nb)
+		if err != nil {
+			v.consume(n)
+			return n, err
+		}
+	}
+	v.consume(n)
+	return n, nil
+}
+
+func (v *Buffers) Read(p []byte) (n int, err error) {
+	for len(p) > 0 && len(*v) > 0 {
+		n0 := copy(p, (*v)[0])
+		v.consume(int64(n0))
+		p = p[n0:]
+		n += n0
+	}
+	if len(*v) == 0 {
+		err = io.EOF
+	}
+	return
+}
+
+func (v *Buffers) consume(n int64) {
+	for len(*v) > 0 {
+		ln0 := int64(len((*v)[0]))
+		if ln0 > n {
+			(*v)[0] = (*v)[0][n:]
+			return
+		}
+		n -= ln0
+		*v = (*v)[1:]
+	}
+}
diff --git a/src/net/net_test.go b/src/net/net_test.go
index b2f825d..9a9a7e5 100644
--- a/src/net/net_test.go
+++ b/src/net/net_test.go
@@ -5,6 +5,8 @@
 package net
 
 import (
+	"errors"
+	"fmt"
 	"io"
 	"net/internal/socktest"
 	"os"
@@ -15,7 +17,7 @@ import (
 
 func TestCloseRead(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9":
+	case "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 
@@ -414,3 +416,103 @@ func TestZeroByteRead(t *testing.T) {
 		}
 	}
 }
+
+// withTCPConnPair sets up a TCP connection between two peers, then
+// runs peer1 and peer2 concurrently. withTCPConnPair returns when
+// both have completed.
+func withTCPConnPair(t *testing.T, peer1, peer2 func(c *TCPConn) error) {
+	ln, err := newLocalListener("tcp")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer ln.Close()
+	errc := make(chan error, 2)
+	go func() {
+		c1, err := ln.Accept()
+		if err != nil {
+			errc <- err
+			return
+		}
+		defer c1.Close()
+		errc <- peer1(c1.(*TCPConn))
+	}()
+	go func() {
+		c2, err := Dial("tcp", ln.Addr().String())
+		if err != nil {
+			errc <- err
+			return
+		}
+		defer c2.Close()
+		errc <- peer2(c2.(*TCPConn))
+	}()
+	for i := 0; i < 2; i++ {
+		if err := <-errc; err != nil {
+			t.Fatal(err)
+		}
+	}
+}
+
+// Tests that a blocked Read is interrupted by a concurrent SetReadDeadline
+// modifying that Conn's read deadline to the past.
+// See golang.org/cl/30164 which documented this. The net/http package
+// depends on this.
+func TestReadTimeoutUnblocksRead(t *testing.T) {
+	serverDone := make(chan struct{})
+	server := func(cs *TCPConn) error {
+		defer close(serverDone)
+		errc := make(chan error, 1)
+		go func() {
+			defer close(errc)
+			go func() {
+				// TODO: find a better way to wait
+				// until we're blocked in the cs.Read
+				// call below. Sleep is lame.
+				time.Sleep(100 * time.Millisecond)
+
+				// Interrupt the upcoming Read, unblocking it:
+				cs.SetReadDeadline(time.Unix(123, 0)) // time in the past
+			}()
+			var buf [1]byte
+			n, err := cs.Read(buf[:1])
+			if n != 0 || err == nil {
+				errc <- fmt.Errorf("Read = %v, %v; want 0, non-nil", n, err)
+			}
+		}()
+		select {
+		case err := <-errc:
+			return err
+		case <-time.After(5 * time.Second):
+			buf := make([]byte, 2<<20)
+			buf = buf[:runtime.Stack(buf, true)]
+			println("Stacks at timeout:\n", string(buf))
+			return errors.New("timeout waiting for Read to finish")
+		}
+
+	}
+	// Do nothing in the client. Never write. Just wait for the
+	// server's half to be done.
+	client := func(*TCPConn) error {
+		<-serverDone
+		return nil
+	}
+	withTCPConnPair(t, client, server)
+}
+
+// Issue 17695: verify that a blocked Read is woken up by a Close.
+func TestCloseUnblocksRead(t *testing.T) {
+	t.Parallel()
+	server := func(cs *TCPConn) error {
+		// Give the client time to get stuck in a Read:
+		time.Sleep(20 * time.Millisecond)
+		cs.Close()
+		return nil
+	}
+	client := func(ss *TCPConn) error {
+		n, err := ss.Read([]byte{0})
+		if n != 0 || err != io.EOF {
+			return fmt.Errorf("Read = %v, %v; want 0, EOF", n, err)
+		}
+		return nil
+	}
+	withTCPConnPair(t, client, server)
+}
diff --git a/src/net/parse.go b/src/net/parse.go
index ed82a77..5826984 100644
--- a/src/net/parse.go
+++ b/src/net/parse.go
@@ -123,39 +123,27 @@ func getFields(s string) []string { return splitAtBytes(s, " \r\t\n") }
 // Bigger than we need, not too big to worry about overflow
 const big = 0xFFFFFF
 
-// Decimal to integer starting at &s[i0].
-// Returns number, new offset, success.
-func dtoi(s string, i0 int) (n int, i int, ok bool) {
+// Decimal to integer.
+// Returns number, characters consumed, success.
+func dtoi(s string) (n int, i int, ok bool) {
 	n = 0
-	neg := false
-	if len(s) > 0 && s[0] == '-' {
-		neg = true
-		s = s[1:]
-	}
-	for i = i0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
+	for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
 		n = n*10 + int(s[i]-'0')
 		if n >= big {
-			if neg {
-				return -big, i + 1, false
-			}
 			return big, i, false
 		}
 	}
-	if i == i0 {
-		return 0, i, false
-	}
-	if neg {
-		n = -n
-		i++
+	if i == 0 {
+		return 0, 0, false
 	}
 	return n, i, true
 }
 
-// Hexadecimal to integer starting at &s[i0].
-// Returns number, new offset, success.
-func xtoi(s string, i0 int) (n int, i int, ok bool) {
+// Hexadecimal to integer.
+// Returns number, characters consumed, success.
+func xtoi(s string) (n int, i int, ok bool) {
 	n = 0
-	for i = i0; i < len(s); i++ {
+	for i = 0; i < len(s); i++ {
 		if '0' <= s[i] && s[i] <= '9' {
 			n *= 16
 			n += int(s[i] - '0')
@@ -172,7 +160,7 @@ func xtoi(s string, i0 int) (n int, i int, ok bool) {
 			return 0, i, false
 		}
 	}
-	if i == i0 {
+	if i == 0 {
 		return 0, i, false
 	}
 	return n, i, true
@@ -186,7 +174,7 @@ func xtoi2(s string, e byte) (byte, bool) {
 	if len(s) > 2 && s[2] != e {
 		return 0, false
 	}
-	n, ei, ok := xtoi(s[:2], 0)
+	n, ei, ok := xtoi(s[:2])
 	return byte(n), ok && ei == 2
 }
 
@@ -346,22 +334,28 @@ func stringsHasSuffix(s, suffix string) bool {
 // stringsHasSuffixFold reports whether s ends in suffix,
 // ASCII-case-insensitively.
 func stringsHasSuffixFold(s, suffix string) bool {
-	if len(suffix) > len(s) {
+	return len(s) >= len(suffix) && stringsEqualFold(s[len(s)-len(suffix):], suffix)
+}
+
+// stringsHasPrefix is strings.HasPrefix. It reports whether s begins with prefix.
+func stringsHasPrefix(s, prefix string) bool {
+	return len(s) >= len(prefix) && s[:len(prefix)] == prefix
+}
+
+// stringsEqualFold is strings.EqualFold, ASCII only. It reports whether s and t
+// are equal, ASCII-case-insensitively.
+func stringsEqualFold(s, t string) bool {
+	if len(s) != len(t) {
 		return false
 	}
-	for i := 0; i < len(suffix); i++ {
-		if lowerASCII(suffix[i]) != lowerASCII(s[len(s)-len(suffix)+i]) {
+	for i := 0; i < len(s); i++ {
+		if lowerASCII(s[i]) != lowerASCII(t[i]) {
 			return false
 		}
 	}
 	return true
 }
 
-// stringsHasPrefix is strings.HasPrefix. It reports whether s begins with prefix.
-func stringsHasPrefix(s, prefix string) bool {
-	return len(s) >= len(prefix) && s[:len(prefix)] == prefix
-}
-
 func readFull(r io.Reader) (all []byte, err error) {
 	buf := make([]byte, 1024)
 	for {
diff --git a/src/net/parse_test.go b/src/net/parse_test.go
index fec9200..c5f8bfd 100644
--- a/src/net/parse_test.go
+++ b/src/net/parse_test.go
@@ -86,14 +86,13 @@ func TestDtoi(t *testing.T) {
 		ok  bool
 	}{
 		{"", 0, 0, false},
-
-		{"-123456789", -big, 9, false},
-		{"-1", -1, 2, true},
 		{"0", 0, 1, true},
 		{"65536", 65536, 5, true},
 		{"123456789", big, 8, false},
+		{"-0", 0, 0, false},
+		{"-1234", 0, 0, false},
 	} {
-		n, i, ok := dtoi(tt.in, 0)
+		n, i, ok := dtoi(tt.in)
 		if n != tt.out || i != tt.off || ok != tt.ok {
 			t.Errorf("got %d, %d, %v; want %d, %d, %v", n, i, ok, tt.out, tt.off, tt.ok)
 		}
diff --git a/src/net/port_unix.go b/src/net/port_unix.go
index badf8ab..4e04781 100644
--- a/src/net/port_unix.go
+++ b/src/net/port_unix.go
@@ -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 dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris nacl
 
 // Read system port mappings from /etc/services
 
@@ -10,12 +10,6 @@ package net
 
 import "sync"
 
-// services contains minimal mappings between services names and port
-// numbers for platforms that don't have a complete list of port numbers
-// (some Solaris distros).
-var services = map[string]map[string]int{
-	"tcp": {"http": 80},
-}
 var servicesError error
 var onceReadServices sync.Once
 
@@ -27,14 +21,14 @@ func readServices() {
 	for line, ok := file.readLine(); ok; line, ok = file.readLine() {
 		// "http 80/tcp www www-http # World Wide Web HTTP"
 		if i := byteIndex(line, '#'); i >= 0 {
-			line = line[0:i]
+			line = line[:i]
 		}
 		f := getFields(line)
 		if len(f) < 2 {
 			continue
 		}
 		portnet := f[1] // "80/tcp"
-		port, j, ok := dtoi(portnet, 0)
+		port, j, ok := dtoi(portnet)
 		if !ok || port <= 0 || j >= len(portnet) || portnet[j] != '/' {
 			continue
 		}
@@ -56,18 +50,5 @@ func readServices() {
 // goLookupPort is the native Go implementation of LookupPort.
 func goLookupPort(network, service string) (port int, err error) {
 	onceReadServices.Do(readServices)
-
-	switch network {
-	case "tcp4", "tcp6":
-		network = "tcp"
-	case "udp4", "udp6":
-		network = "udp"
-	}
-
-	if m, ok := services[network]; ok {
-		if port, ok = m[service]; ok {
-			return
-		}
-	}
-	return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service}
+	return lookupPortMap(network, service)
 }
diff --git a/src/net/rpc/client.go b/src/net/rpc/client.go
index 862fb1a..fce6a48 100644
--- a/src/net/rpc/client.go
+++ b/src/net/rpc/client.go
@@ -274,6 +274,8 @@ func Dial(network, address string) (*Client, error) {
 	return NewClient(conn), nil
 }
 
+// Close calls the underlying codec's Close method. If the connection is already
+// shutting down, ErrShutdown is returned.
 func (client *Client) Close() error {
 	client.mutex.Lock()
 	if client.closing {
diff --git a/src/net/rpc/client_test.go b/src/net/rpc/client_test.go
index ba11ff8..d116d2a 100644
--- a/src/net/rpc/client_test.go
+++ b/src/net/rpc/client_test.go
@@ -8,7 +8,6 @@ import (
 	"errors"
 	"fmt"
 	"net"
-	"runtime"
 	"strings"
 	"testing"
 )
@@ -53,9 +52,6 @@ func (s *S) Recv(nul *struct{}, reply *R) error {
 }
 
 func TestGobError(t *testing.T) {
-	if runtime.GOOS == "plan9" {
-		t.Skip("skipping test; see https://golang.org/issue/8908")
-	}
 	defer func() {
 		err := recover()
 		if err == nil {
diff --git a/src/net/rpc/server.go b/src/net/rpc/server.go
index cff3241..18ea629 100644
--- a/src/net/rpc/server.go
+++ b/src/net/rpc/server.go
@@ -23,7 +23,7 @@
 
 		func (t *T) MethodName(argType T1, replyType *T2) error
 
-	where T, T1 and T2 can be marshaled by encoding/gob.
+	where T1 and T2 can be marshaled by encoding/gob.
 	These requirements apply even if a different codec is used.
 	(In the future, these requirements may soften for custom codecs.)
 
@@ -55,6 +55,8 @@
 
 		package server
 
+		import "errors"
+
 		type Args struct {
 			A, B int
 		}
@@ -119,6 +121,8 @@
 
 	A server implementation will often provide a simple, type-safe wrapper for the
 	client.
+
+	The net/rpc package is frozen and is not accepting new features.
 */
 package rpc
 
diff --git a/src/net/rpc/server_test.go b/src/net/rpc/server_test.go
index d04271d..8369c9d 100644
--- a/src/net/rpc/server_test.go
+++ b/src/net/rpc/server_test.go
@@ -693,7 +693,8 @@ func benchmarkEndToEndAsync(dial func() (*Client, error), b *testing.B) {
 				B := call.Args.(*Args).B
 				C := call.Reply.(*Reply).C
 				if A+B != C {
-					b.Fatalf("incorrect reply: Add: expected %d got %d", A+B, C)
+					b.Errorf("incorrect reply: Add: expected %d got %d", A+B, C)
+					return
 				}
 				<-gate
 				if atomic.AddInt32(&recv, -1) == 0 {
diff --git a/src/net/smtp/smtp.go b/src/net/smtp/smtp.go
index 9e04dd7..a408fa5 100644
--- a/src/net/smtp/smtp.go
+++ b/src/net/smtp/smtp.go
@@ -9,7 +9,7 @@
 //	STARTTLS  RFC 3207
 // Additional extensions may be handled by clients.
 //
-// The smtp package is frozen and not accepting new features.
+// The smtp package is frozen and is not accepting new features.
 // Some external packages provide more functionality. See:
 //
 //   https://godoc.org/?q=smtp
@@ -19,6 +19,7 @@ import (
 	"crypto/tls"
 	"encoding/base64"
 	"errors"
+	"fmt"
 	"io"
 	"net"
 	"net/textproto"
@@ -200,7 +201,7 @@ func (c *Client) Auth(a Auth) error {
 	}
 	resp64 := make([]byte, encoding.EncodedLen(len(resp)))
 	encoding.Encode(resp64, resp)
-	code, msg64, err := c.cmd(0, "AUTH %s %s", mech, resp64)
+	code, msg64, err := c.cmd(0, strings.TrimSpace(fmt.Sprintf("AUTH %s %s", mech, resp64)))
 	for err == nil {
 		var msg []byte
 		switch code {
diff --git a/src/net/smtp/smtp_test.go b/src/net/smtp/smtp_test.go
index 3ae0d5b..c48fae6 100644
--- a/src/net/smtp/smtp_test.go
+++ b/src/net/smtp/smtp_test.go
@@ -94,6 +94,46 @@ func TestAuthPlain(t *testing.T) {
 	}
 }
 
+// Issue 17794: don't send a trailing space on AUTH command when there's no password.
+func TestClientAuthTrimSpace(t *testing.T) {
+	server := "220 hello world\r\n" +
+		"200 some more"
+	var wrote bytes.Buffer
+	var fake faker
+	fake.ReadWriter = struct {
+		io.Reader
+		io.Writer
+	}{
+		strings.NewReader(server),
+		&wrote,
+	}
+	c, err := NewClient(fake, "fake.host")
+	if err != nil {
+		t.Fatalf("NewClient: %v", err)
+	}
+	c.tls = true
+	c.didHello = true
+	c.Auth(toServerEmptyAuth{})
+	c.Close()
+	if got, want := wrote.String(), "AUTH FOOAUTH\r\n*\r\nQUIT\r\n"; got != want {
+		t.Errorf("wrote %q; want %q", got, want)
+	}
+}
+
+// toServerEmptyAuth is an implementation of Auth that only implements
+// the Start method, and returns "FOOAUTH", nil, nil. Notably, it returns
+// zero bytes for "toServer" so we can test that we don't send spaces at
+// the end of the line. See TestClientAuthTrimSpace.
+type toServerEmptyAuth struct{}
+
+func (toServerEmptyAuth) Start(server *ServerInfo) (proto string, toServer []byte, err error) {
+	return "FOOAUTH", nil, nil
+}
+
+func (toServerEmptyAuth) Next(fromServer []byte, more bool) (toServer []byte, err error) {
+	panic("unexpected call")
+}
+
 type faker struct {
 	io.ReadWriter
 }
@@ -716,23 +756,24 @@ func sendMail(hostPort string) error {
 // generated from src/crypto/tls:
 // go run generate_cert.go  --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
 var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
-MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
-bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
-bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa
-IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA
-AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
-EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
-AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk
-Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA==
+MIIBjjCCATigAwIBAgIQMon9v0s3pDFXvAMnPgelpzANBgkqhkiG9w0BAQsFADAS
+MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
+MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
+AM0u/mNXKkhAzNsFkwKZPSpC4lZZaePQ55IyaJv3ovMM2smvthnlqaUfVKVmz7FF
+wLP9csX6vGtvkZg1uWAtvfkCAwEAAaNoMGYwDgYDVR0PAQH/BAQDAgKkMBMGA1Ud
+JQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhh
+bXBsZS5jb22HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQAD
+QQBOZsFVC7IwX+qibmSbt2IPHkUgXhfbq0a9MYhD6tHcj4gbDcTXh4kZCbgHCz22
+gfSj2/G2wxzopoISVDucuncj
 -----END CERTIFICATE-----`)
 
 // localhostKey is the private key for localhostCert.
 var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
-MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0
-0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV
-NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d
-AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW
-MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD
-EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA
-1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE=
+MIIBOwIBAAJBAM0u/mNXKkhAzNsFkwKZPSpC4lZZaePQ55IyaJv3ovMM2smvthnl
+qaUfVKVmz7FFwLP9csX6vGtvkZg1uWAtvfkCAwEAAQJART2qkxODLUbQ2siSx7m2
+rmBLyR/7X+nLe8aPDrMOxj3heDNl4YlaAYLexbcY8d7VDfCRBKYoAOP0UCP1Vhuf
+UQIhAO6PEI55K3SpNIdc2k5f0xz+9rodJCYzu51EwWX7r8ufAiEA3C9EkLiU2NuK
+3L3DHCN5IlUSN1Nr/lw8NIt50Yorj2cCIQCDw1VbvCV6bDLtSSXzAA51B4ZzScE7
+sHtB5EYF9Dwm9QIhAJuCquuH4mDzVjUntXjXOQPdj7sRqVGCNWdrJwOukat7AiAy
+LXLEwb77DIPoI5ZuaXQC+MnyyJj1ExC9RFcGz+bexA==
 -----END RSA PRIVATE KEY-----`)
diff --git a/src/net/sock_linux.go b/src/net/sock_linux.go
index e2732c5..7bca376 100644
--- a/src/net/sock_linux.go
+++ b/src/net/sock_linux.go
@@ -17,7 +17,7 @@ func maxListenerBacklog() int {
 		return syscall.SOMAXCONN
 	}
 	f := getFields(l)
-	n, _, ok := dtoi(f[0], 0)
+	n, _, ok := dtoi(f[0])
 	if n == 0 || !ok {
 		return syscall.SOMAXCONN
 	}
diff --git a/src/net/sock_posix.go b/src/net/sock_posix.go
index c3af27b..16351e1 100644
--- a/src/net/sock_posix.go
+++ b/src/net/sock_posix.go
@@ -30,6 +30,9 @@ type sockaddr interface {
 	// interface. It returns a nil interface when the address is
 	// nil.
 	sockaddr(family int) (syscall.Sockaddr, error)
+
+	// toLocal maps the zero address to a local system address (127.0.0.1 or ::1)
+	toLocal(net string) sockaddr
 }
 
 // socket returns a network file descriptor that is ready for
diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go
index 7cffcc5..69731eb 100644
--- a/src/net/tcpsock.go
+++ b/src/net/tcpsock.go
@@ -12,6 +12,9 @@ import (
 	"time"
 )
 
+// BUG(mikio): On Windows, the File method of TCPListener is not
+// implemented.
+
 // TCPAddr represents the address of a TCP end point.
 type TCPAddr struct {
 	IP   IP
@@ -53,6 +56,9 @@ func (a *TCPAddr) opAddr() Addr {
 // "tcp6".  A literal address or host name for IPv6 must be enclosed
 // in square brackets, as in "[::1]:80", "[ipv6-host]:http" or
 // "[ipv6-host%zone]:80".
+//
+// Resolving a hostname is not recommended because this returns at most
+// one of its IP addresses.
 func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
 	switch net {
 	case "tcp", "tcp4", "tcp6":
@@ -61,7 +67,7 @@ func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
 	default:
 		return nil, UnknownNetworkError(net)
 	}
-	addrs, err := internetAddrList(context.Background(), net, addr)
+	addrs, err := DefaultResolver.internetAddrList(context.Background(), net, addr)
 	if err != nil {
 		return nil, err
 	}
@@ -81,7 +87,7 @@ func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
 	}
 	n, err := c.readFrom(r)
 	if err != nil && err != io.EOF {
-		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+		err = &OpError{Op: "readfrom", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
 	}
 	return n, err
 }
diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go
index c9a8b68..9641e5c 100644
--- a/src/net/tcpsock_posix.go
+++ b/src/net/tcpsock_posix.go
@@ -40,6 +40,10 @@ func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
 	return ipToSockaddr(family, a.IP, a.Port, a.Zone)
 }
 
+func (a *TCPAddr) toLocal(net string) sockaddr {
+	return &TCPAddr{loopbackIP(net), a.Port, a.Zone}
+}
+
 func (c *TCPConn) readFrom(r io.Reader) (int64, error) {
 	if n, err, handled := sendFile(c.fd, r); handled {
 		return n, err
diff --git a/src/net/tcpsock_test.go b/src/net/tcpsock_test.go
index 4af47fc..573e834 100644
--- a/src/net/tcpsock_test.go
+++ b/src/net/tcpsock_test.go
@@ -5,6 +5,7 @@
 package net
 
 import (
+	"fmt"
 	"internal/testenv"
 	"io"
 	"reflect"
@@ -310,6 +311,16 @@ var resolveTCPAddrTests = []resolveTCPAddrTest{
 	{"tcp", ":12345", &TCPAddr{Port: 12345}, nil},
 
 	{"http", "127.0.0.1:0", nil, UnknownNetworkError("http")},
+
+	{"tcp", "127.0.0.1:http", &TCPAddr{IP: ParseIP("127.0.0.1"), Port: 80}, nil},
+	{"tcp", "[::ffff:127.0.0.1]:http", &TCPAddr{IP: ParseIP("::ffff:127.0.0.1"), Port: 80}, nil},
+	{"tcp", "[2001:db8::1]:http", &TCPAddr{IP: ParseIP("2001:db8::1"), Port: 80}, nil},
+	{"tcp4", "127.0.0.1:http", &TCPAddr{IP: ParseIP("127.0.0.1"), Port: 80}, nil},
+	{"tcp4", "[::ffff:127.0.0.1]:http", &TCPAddr{IP: ParseIP("127.0.0.1"), Port: 80}, nil},
+	{"tcp4", "[2001:db8::1]:http", nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: "2001:db8::1"}},
+	{"tcp6", "127.0.0.1:http", nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: "127.0.0.1"}},
+	{"tcp6", "[::ffff:127.0.0.1]:http", nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: "::ffff:127.0.0.1"}},
+	{"tcp6", "[2001:db8::1]:http", &TCPAddr{IP: ParseIP("2001:db8::1"), Port: 80}, nil},
 }
 
 func TestResolveTCPAddr(t *testing.T) {
@@ -317,21 +328,17 @@ func TestResolveTCPAddr(t *testing.T) {
 	defer func() { testHookLookupIP = origTestHookLookupIP }()
 	testHookLookupIP = lookupLocalhost
 
-	for i, tt := range resolveTCPAddrTests {
+	for _, tt := range resolveTCPAddrTests {
 		addr, err := ResolveTCPAddr(tt.network, tt.litAddrOrName)
-		if err != tt.err {
-			t.Errorf("#%d: %v", i, err)
-		} else if !reflect.DeepEqual(addr, tt.addr) {
-			t.Errorf("#%d: got %#v; want %#v", i, addr, tt.addr)
-		}
-		if err != nil {
+		if !reflect.DeepEqual(addr, tt.addr) || !reflect.DeepEqual(err, tt.err) {
+			t.Errorf("ResolveTCPAddr(%q, %q) = %v, %v, want %v, %v", tt.network, tt.litAddrOrName, addr, err, tt.addr, tt.err)
 			continue
 		}
-		rtaddr, err := ResolveTCPAddr(addr.Network(), addr.String())
-		if err != nil {
-			t.Errorf("#%d: %v", i, err)
-		} else if !reflect.DeepEqual(rtaddr, addr) {
-			t.Errorf("#%d: got %#v; want %#v", i, rtaddr, addr)
+		if err == nil {
+			addr2, err := ResolveTCPAddr(addr.Network(), addr.String())
+			if !reflect.DeepEqual(addr2, tt.addr) || err != tt.err {
+				t.Errorf("(%q, %q): ResolveTCPAddr(%q, %q) = %v, %v, want %v, %v", tt.network, tt.litAddrOrName, addr.Network(), addr.String(), addr2, err, tt.addr, tt.err)
+			}
 		}
 	}
 }
@@ -460,11 +467,14 @@ func TestTCPConcurrentAccept(t *testing.T) {
 
 func TestTCPReadWriteAllocs(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "windows":
+	case "plan9":
+		// The implementation of asynchronous cancelable
+		// I/O on Plan 9 allocates memory.
+		// See net/fd_io_plan9.go.
+		t.Skipf("not supported on %s", runtime.GOOS)
+	case "nacl":
 		// NaCl needs to allocate pseudo file descriptor
 		// stuff. See syscall/fd_nacl.go.
-		// Windows uses closures and channels for IO
-		// completion port-based netpoll. See fd_windows.go.
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 
@@ -474,7 +484,7 @@ func TestTCPReadWriteAllocs(t *testing.T) {
 	}
 	defer ln.Close()
 	var server Conn
-	errc := make(chan error)
+	errc := make(chan error, 1)
 	go func() {
 		var err error
 		server, err = ln.Accept()
@@ -489,6 +499,7 @@ func TestTCPReadWriteAllocs(t *testing.T) {
 		t.Fatal(err)
 	}
 	defer server.Close()
+
 	var buf [128]byte
 	allocs := testing.AllocsPerRun(1000, func() {
 		_, err := server.Write(buf[:])
@@ -503,6 +514,28 @@ func TestTCPReadWriteAllocs(t *testing.T) {
 	if allocs > 0 {
 		t.Fatalf("got %v; want 0", allocs)
 	}
+
+	var bufwrt [128]byte
+	ch := make(chan bool)
+	defer close(ch)
+	go func() {
+		for <-ch {
+			_, err := server.Write(bufwrt[:])
+			errc <- err
+		}
+	}()
+	allocs = testing.AllocsPerRun(1000, func() {
+		ch <- true
+		if _, err = io.ReadFull(client, buf[:]); err != nil {
+			t.Fatal(err)
+		}
+		if err := <-errc; err != nil {
+			t.Fatal(err)
+		}
+	})
+	if allocs > 0 {
+		t.Fatalf("got %v; want 0", allocs)
+	}
 }
 
 func TestTCPStress(t *testing.T) {
@@ -633,3 +666,58 @@ func TestTCPSelfConnect(t *testing.T) {
 		}
 	}
 }
+
+// Test that >32-bit reads work on 64-bit systems.
+// On 32-bit systems this tests that maxint reads work.
+func TestTCPBig(t *testing.T) {
+	if !*testTCPBig {
+		t.Skip("test disabled; use -tcpbig to enable")
+	}
+
+	for _, writev := range []bool{false, true} {
+		t.Run(fmt.Sprintf("writev=%v", writev), func(t *testing.T) {
+			ln, err := newLocalListener("tcp")
+			if err != nil {
+				t.Fatal(err)
+			}
+			defer ln.Close()
+
+			x := int(1 << 30)
+			x = x*5 + 1<<20 // just over 5 GB on 64-bit, just over 1GB on 32-bit
+			done := make(chan int)
+			go func() {
+				defer close(done)
+				c, err := ln.Accept()
+				if err != nil {
+					t.Error(err)
+					return
+				}
+				buf := make([]byte, x)
+				var n int
+				if writev {
+					var n64 int64
+					n64, err = (&Buffers{buf}).WriteTo(c)
+					n = int(n64)
+				} else {
+					n, err = c.Write(buf)
+				}
+				if n != len(buf) || err != nil {
+					t.Errorf("Write(buf) = %d, %v, want %d, nil", n, err, x)
+				}
+				c.Close()
+			}()
+
+			c, err := Dial("tcp", ln.Addr().String())
+			if err != nil {
+				t.Fatal(err)
+			}
+			buf := make([]byte, x)
+			n, err := io.ReadFull(c, buf)
+			if n != len(buf) || err != nil {
+				t.Errorf("Read(buf) = %d, %v, want %d, nil", n, err, x)
+			}
+			c.Close()
+			<-done
+		})
+	}
+}
diff --git a/src/net/tcpsock_unix_test.go b/src/net/tcpsock_unix_test.go
index c07f7d7..2375fe2 100644
--- a/src/net/tcpsock_unix_test.go
+++ b/src/net/tcpsock_unix_test.go
@@ -15,7 +15,7 @@ import (
 )
 
 // See golang.org/issue/14548.
-func TestTCPSupriousConnSetupCompletion(t *testing.T) {
+func TestTCPSpuriousConnSetupCompletion(t *testing.T) {
 	if testing.Short() {
 		t.Skip("skipping in short mode")
 	}
@@ -57,7 +57,7 @@ func TestTCPSupriousConnSetupCompletion(t *testing.T) {
 			c, err := d.Dial(ln.Addr().Network(), ln.Addr().String())
 			if err != nil {
 				if perr := parseDialError(err); perr != nil {
-					t.Errorf("#%d: %v", i, err)
+					t.Errorf("#%d: %v (original error: %v)", i, perr, err)
 				}
 				return
 			}
diff --git a/src/net/testdata/invalid-ndots-resolv.conf b/src/net/testdata/invalid-ndots-resolv.conf
new file mode 100644
index 0000000..084c164
--- /dev/null
+++ b/src/net/testdata/invalid-ndots-resolv.conf
@@ -0,0 +1 @@
+options ndots:invalid
\ No newline at end of file
diff --git a/src/net/testdata/large-ndots-resolv.conf b/src/net/testdata/large-ndots-resolv.conf
new file mode 100644
index 0000000..72968ee
--- /dev/null
+++ b/src/net/testdata/large-ndots-resolv.conf
@@ -0,0 +1 @@
+options ndots:16
\ No newline at end of file
diff --git a/src/net/testdata/negative-ndots-resolv.conf b/src/net/testdata/negative-ndots-resolv.conf
new file mode 100644
index 0000000..c11e0cc
--- /dev/null
+++ b/src/net/testdata/negative-ndots-resolv.conf
@@ -0,0 +1 @@
+options ndots:-1
\ No newline at end of file
diff --git a/src/net/textproto/header.go b/src/net/textproto/header.go
index 2e2752a..ed096d9 100644
--- a/src/net/textproto/header.go
+++ b/src/net/textproto/header.go
@@ -23,8 +23,10 @@ func (h MIMEHeader) Set(key, value string) {
 }
 
 // Get gets the first value associated with the given key.
+// It is case insensitive; CanonicalMIMEHeaderKey is used
+// to canonicalize the provided key.
 // If there are no values associated with the key, Get returns "".
-// Get is a convenience method. For more complex queries,
+// To access multiple values of a key, or to use non-canonical keys,
 // access the map directly.
 func (h MIMEHeader) Get(key string) string {
 	if h == nil {
diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go
index ed26f2a..55bbf44 100644
--- a/src/net/timeout_test.go
+++ b/src/net/timeout_test.go
@@ -5,7 +5,6 @@
 package net
 
 import (
-	"context"
 	"fmt"
 	"internal/testenv"
 	"io"
@@ -152,6 +151,7 @@ var acceptTimeoutTests = []struct {
 }
 
 func TestAcceptTimeout(t *testing.T) {
+	testenv.SkipFlaky(t, 17948)
 	t.Parallel()
 
 	switch runtime.GOOS {
@@ -165,19 +165,18 @@ func TestAcceptTimeout(t *testing.T) {
 	}
 	defer ln.Close()
 
-	ctx, cancel := context.WithCancel(context.Background())
-	defer cancel()
+	var wg sync.WaitGroup
 	for i, tt := range acceptTimeoutTests {
 		if tt.timeout < 0 {
+			wg.Add(1)
 			go func() {
-				var d Dialer
-				c, err := d.DialContext(ctx, ln.Addr().Network(), ln.Addr().String())
+				defer wg.Done()
+				d := Dialer{Timeout: 100 * time.Millisecond}
+				c, err := d.Dial(ln.Addr().Network(), ln.Addr().String())
 				if err != nil {
 					t.Error(err)
 					return
 				}
-				var b [1]byte
-				c.Read(b[:])
 				c.Close()
 			}()
 		}
@@ -198,13 +197,14 @@ func TestAcceptTimeout(t *testing.T) {
 				}
 				if err == nil {
 					c.Close()
-					time.Sleep(tt.timeout / 3)
+					time.Sleep(10 * time.Millisecond)
 					continue
 				}
 				break
 			}
 		}
 	}
+	wg.Wait()
 }
 
 func TestAcceptTimeoutMustReturn(t *testing.T) {
@@ -305,11 +305,6 @@ var readTimeoutTests = []struct {
 }
 
 func TestReadTimeout(t *testing.T) {
-	switch runtime.GOOS {
-	case "plan9":
-		t.Skipf("not supported on %s", runtime.GOOS)
-	}
-
 	handler := func(ls *localServer, ln Listener) {
 		c, err := ln.Accept()
 		if err != nil {
@@ -435,7 +430,7 @@ var readFromTimeoutTests = []struct {
 
 func TestReadFromTimeout(t *testing.T) {
 	switch runtime.GOOS {
-	case "nacl", "plan9":
+	case "nacl":
 		t.Skipf("not supported on %s", runtime.GOOS) // see golang.org/issue/8916
 	}
 
@@ -509,11 +504,6 @@ var writeTimeoutTests = []struct {
 func TestWriteTimeout(t *testing.T) {
 	t.Parallel()
 
-	switch runtime.GOOS {
-	case "plan9":
-		t.Skipf("not supported on %s", runtime.GOOS)
-	}
-
 	ln, err := newLocalListener("tcp")
 	if err != nil {
 		t.Fatal(err)
@@ -629,7 +619,7 @@ func TestWriteToTimeout(t *testing.T) {
 	t.Parallel()
 
 	switch runtime.GOOS {
-	case "nacl", "plan9":
+	case "nacl":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 
@@ -681,11 +671,6 @@ func TestWriteToTimeout(t *testing.T) {
 func TestReadTimeoutFluctuation(t *testing.T) {
 	t.Parallel()
 
-	switch runtime.GOOS {
-	case "plan9":
-		t.Skipf("not supported on %s", runtime.GOOS)
-	}
-
 	ln, err := newLocalListener("tcp")
 	if err != nil {
 		t.Fatal(err)
@@ -719,11 +704,6 @@ func TestReadTimeoutFluctuation(t *testing.T) {
 func TestReadFromTimeoutFluctuation(t *testing.T) {
 	t.Parallel()
 
-	switch runtime.GOOS {
-	case "plan9":
-		t.Skipf("not supported on %s", runtime.GOOS)
-	}
-
 	c1, err := newLocalPacketListener("udp")
 	if err != nil {
 		t.Fatal(err)
@@ -829,11 +809,6 @@ func (b neverEnding) Read(p []byte) (int, error) {
 }
 
 func testVariousDeadlines(t *testing.T) {
-	switch runtime.GOOS {
-	case "plan9":
-		t.Skipf("not supported on %s", runtime.GOOS)
-	}
-
 	type result struct {
 		n   int64
 		err error
@@ -1030,7 +1005,7 @@ func TestReadWriteDeadlineRace(t *testing.T) {
 	t.Parallel()
 
 	switch runtime.GOOS {
-	case "nacl", "plan9":
+	case "nacl":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 
diff --git a/src/net/udpsock.go b/src/net/udpsock.go
index 980f67c..841ef53 100644
--- a/src/net/udpsock.go
+++ b/src/net/udpsock.go
@@ -9,6 +9,15 @@ import (
 	"syscall"
 )
 
+// BUG(mikio): On NaCl, Plan 9 and Windows, the ReadMsgUDP and
+// WriteMsgUDP methods of UDPConn are not implemented.
+
+// BUG(mikio): On Windows, the File method of UDPConn is not
+// implemented.
+
+// BUG(mikio): On NaCl, the ListenMulticastUDP function is not
+// implemented.
+
 // UDPAddr represents the address of a UDP end point.
 type UDPAddr struct {
 	IP   IP
@@ -50,6 +59,9 @@ func (a *UDPAddr) opAddr() Addr {
 // "udp6".  A literal address or host name for IPv6 must be enclosed
 // in square brackets, as in "[::1]:80", "[ipv6-host]:http" or
 // "[ipv6-host%zone]:80".
+//
+// Resolving a hostname is not recommended because this returns at most
+// one of its IP addresses.
 func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
 	switch net {
 	case "udp", "udp4", "udp6":
@@ -58,7 +70,7 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
 	default:
 		return nil, UnknownNetworkError(net)
 	}
-	addrs, err := internetAddrList(context.Background(), net, addr)
+	addrs, err := DefaultResolver.internetAddrList(context.Background(), net, addr)
 	if err != nil {
 		return nil, err
 	}
diff --git a/src/net/udpsock_plan9.go b/src/net/udpsock_plan9.go
index 666f206..1ce7f88 100644
--- a/src/net/udpsock_plan9.go
+++ b/src/net/udpsock_plan9.go
@@ -109,5 +109,41 @@ func listenUDP(ctx context.Context, network string, laddr *UDPAddr) (*UDPConn, e
 }
 
 func listenMulticastUDP(ctx context.Context, network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
-	return nil, syscall.EPLAN9
+	l, err := listenPlan9(ctx, network, gaddr)
+	if err != nil {
+		return nil, err
+	}
+	_, err = l.ctl.WriteString("headers")
+	if err != nil {
+		return nil, err
+	}
+	var addrs []Addr
+	if ifi != nil {
+		addrs, err = ifi.Addrs()
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		addrs, err = InterfaceAddrs()
+		if err != nil {
+			return nil, err
+		}
+	}
+	for _, addr := range addrs {
+		if ipnet, ok := addr.(*IPNet); ok {
+			_, err = l.ctl.WriteString("addmulti " + ipnet.IP.String() + " " + gaddr.IP.String())
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+	l.data, err = os.OpenFile(l.dir+"/data", os.O_RDWR, 0)
+	if err != nil {
+		return nil, err
+	}
+	fd, err := l.netFD()
+	if err != nil {
+		return nil, err
+	}
+	return newUDPConn(fd), nil
 }
diff --git a/src/net/udpsock_plan9_test.go b/src/net/udpsock_plan9_test.go
new file mode 100644
index 0000000..09f5a5d
--- /dev/null
+++ b/src/net/udpsock_plan9_test.go
@@ -0,0 +1,69 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+	"internal/testenv"
+	"runtime"
+	"testing"
+)
+
+func TestListenMulticastUDP(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+
+	ifcs, err := Interfaces()
+	if err != nil {
+		t.Skip(err.Error())
+	}
+	if len(ifcs) == 0 {
+		t.Skip("no network interfaces found")
+	}
+
+	var mifc *Interface
+	for _, ifc := range ifcs {
+		if ifc.Flags&FlagUp|FlagMulticast != FlagUp|FlagMulticast {
+			continue
+		}
+		mifc = &ifc
+		break
+	}
+
+	if mifc == nil {
+		t.Skipf("no multicast interfaces found")
+	}
+
+	c1, err := ListenMulticastUDP("udp4", mifc, &UDPAddr{IP: ParseIP("224.0.0.254")})
+	if err != nil {
+		t.Fatalf("multicast not working on %s", runtime.GOOS)
+	}
+	c1addr := c1.LocalAddr().(*UDPAddr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer c1.Close()
+
+	c2, err := ListenUDP("udp4", &UDPAddr{IP: IPv4zero, Port: 0})
+	c2addr := c2.LocalAddr().(*UDPAddr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer c2.Close()
+
+	n, err := c2.WriteToUDP([]byte("data"), c1addr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if n != 4 {
+		t.Fatalf("got %d; want 4", n)
+	}
+
+	n, err = c1.WriteToUDP([]byte("data"), c2addr)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if n != 4 {
+		t.Fatalf("got %d; want 4", n)
+	}
+}
diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go
index 4924801..72aadca 100644
--- a/src/net/udpsock_posix.go
+++ b/src/net/udpsock_posix.go
@@ -38,6 +38,10 @@ func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
 	return ipToSockaddr(family, a.IP, a.Port, a.Zone)
 }
 
+func (a *UDPAddr) toLocal(net string) sockaddr {
+	return &UDPAddr{loopbackIP(net), a.Port, a.Zone}
+}
+
 func (c *UDPConn) readFrom(b []byte) (int, *UDPAddr, error) {
 	var addr *UDPAddr
 	n, sa, err := c.fd.readFrom(b)
diff --git a/src/net/unixsock.go b/src/net/unixsock.go
index bacdaa4..b25d492 100644
--- a/src/net/unixsock.go
+++ b/src/net/unixsock.go
@@ -7,6 +7,7 @@ package net
 import (
 	"context"
 	"os"
+	"sync"
 	"syscall"
 	"time"
 )
@@ -120,6 +121,9 @@ func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) {
 // the associated out-of-band data into oob. It returns the number of
 // bytes copied into b, the number of bytes copied into oob, the flags
 // that were set on the packet, and the source address of the packet.
+//
+// Note that if len(b) == 0 and len(oob) > 0, this function will still
+// read (and discard) 1 byte from the connection.
 func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
 	if !c.ok() {
 		return 0, 0, 0, nil, syscall.EINVAL
@@ -167,6 +171,9 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (int, error) {
 // WriteMsgUnix writes a packet to addr via c, copying the payload
 // from b and the associated out-of-band data from oob. It returns
 // the number of payload and out-of-band bytes written.
+//
+// Note that if len(b) == 0 and len(oob) > 0, this function will still
+// write 1 byte to the connection.
 func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
 	if !c.ok() {
 		return 0, 0, syscall.EINVAL
@@ -200,9 +207,10 @@ func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
 // typically use variables of type Listener instead of assuming Unix
 // domain sockets.
 type UnixListener struct {
-	fd     *netFD
-	path   string
-	unlink bool
+	fd         *netFD
+	path       string
+	unlink     bool
+	unlinkOnce sync.Once
 }
 
 func (ln *UnixListener) ok() bool { return ln != nil && ln.fd != nil }
diff --git a/src/net/unixsock_posix.go b/src/net/unixsock_posix.go
index 5f0999c..a8f892e 100644
--- a/src/net/unixsock_posix.go
+++ b/src/net/unixsock_posix.go
@@ -94,6 +94,10 @@ func (a *UnixAddr) sockaddr(family int) (syscall.Sockaddr, error) {
 	return &syscall.SockaddrUnix{Name: a.Name}, nil
 }
 
+func (a *UnixAddr) toLocal(net string) sockaddr {
+	return a
+}
+
 func (c *UnixConn) readFrom(b []byte) (int, *UnixAddr, error) {
 	var addr *UnixAddr
 	n, sa, err := c.fd.readFrom(b)
@@ -173,9 +177,12 @@ func (ln *UnixListener) close() error {
 	// is at least compatible with the auto-remove
 	// sequence in ListenUnix. It's only non-Go
 	// programs that can mess us up.
-	if ln.path[0] != '@' && ln.unlink {
-		syscall.Unlink(ln.path)
-	}
+	// Even if there are racy calls to Close, we want to unlink only for the first one.
+	ln.unlinkOnce.Do(func() {
+		if ln.path[0] != '@' && ln.unlink {
+			syscall.Unlink(ln.path)
+		}
+	})
 	return ln.fd.Close()
 }
 
@@ -187,6 +194,18 @@ func (ln *UnixListener) file() (*os.File, error) {
 	return f, nil
 }
 
+// SetUnlinkOnClose sets whether the underlying socket file should be removed
+// from the file system when the listener is closed.
+//
+// The default behavior is to unlink the socket file only when package net created it.
+// That is, when the listener and the underlying socket file were created by a call to
+// Listen or ListenUnix, then by default closing the listener will remove the socket file.
+// but if the listener was created by a call to FileListener to use an already existing
+// socket file, then by default closing the listener will not remove the socket file.
+func (l *UnixListener) SetUnlinkOnClose(unlink bool) {
+	l.unlink = unlink
+}
+
 func listenUnix(ctx context.Context, network string, laddr *UnixAddr) (*UnixListener, error) {
 	fd, err := unixSocket(ctx, network, laddr, nil, "listen")
 	if err != nil {
diff --git a/src/net/unixsock_test.go b/src/net/unixsock_test.go
index f0f88ed..489a29b 100644
--- a/src/net/unixsock_test.go
+++ b/src/net/unixsock_test.go
@@ -9,6 +9,7 @@ package net
 import (
 	"bytes"
 	"internal/testenv"
+	"io/ioutil"
 	"os"
 	"reflect"
 	"runtime"
@@ -414,33 +415,104 @@ func TestUnixUnlink(t *testing.T) {
 		t.Skip("unix test")
 	}
 	name := testUnixAddr()
-	l, err := Listen("unix", name)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if _, err := os.Stat(name); err != nil {
-		t.Fatalf("cannot stat unix socket after ListenUnix: %v", err)
-	}
-	f, _ := l.(*UnixListener).File()
-	l1, err := FileListener(f)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if _, err := os.Stat(name); err != nil {
-		t.Fatalf("cannot stat unix socket after FileListener: %v", err)
-	}
-	if err := l1.Close(); err != nil {
-		t.Fatalf("closing file listener: %v", err)
-	}
-	if _, err := os.Stat(name); err != nil {
-		t.Fatalf("cannot stat unix socket after closing FileListener: %v", err)
+
+	listen := func(t *testing.T) *UnixListener {
+		l, err := Listen("unix", name)
+		if err != nil {
+			t.Fatal(err)
+		}
+		return l.(*UnixListener)
 	}
-	f.Close()
-	if _, err := os.Stat(name); err != nil {
-		t.Fatalf("cannot stat unix socket after closing FileListener and fd: %v", err)
+	checkExists := func(t *testing.T, desc string) {
+		if _, err := os.Stat(name); err != nil {
+			t.Fatalf("unix socket does not exist %s: %v", desc, err)
+		}
 	}
-	l.Close()
-	if _, err := os.Stat(name); err == nil {
-		t.Fatal("closing unix listener did not remove unix socket")
+	checkNotExists := func(t *testing.T, desc string) {
+		if _, err := os.Stat(name); err == nil {
+			t.Fatalf("unix socket does exist %s: %v", desc, err)
+		}
 	}
+
+	// Listener should remove on close.
+	t.Run("Listen", func(t *testing.T) {
+		l := listen(t)
+		checkExists(t, "after Listen")
+		l.Close()
+		checkNotExists(t, "after Listener close")
+	})
+
+	// FileListener should not.
+	t.Run("FileListener", func(t *testing.T) {
+		l := listen(t)
+		f, _ := l.File()
+		l1, _ := FileListener(f)
+		checkExists(t, "after FileListener")
+		f.Close()
+		checkExists(t, "after File close")
+		l1.Close()
+		checkExists(t, "after FileListener close")
+		l.Close()
+		checkNotExists(t, "after Listener close")
+	})
+
+	// Only first call to l.Close should remove.
+	t.Run("SecondClose", func(t *testing.T) {
+		l := listen(t)
+		checkExists(t, "after Listen")
+		l.Close()
+		checkNotExists(t, "after Listener close")
+		if err := ioutil.WriteFile(name, []byte("hello world"), 0666); err != nil {
+			t.Fatalf("cannot recreate socket file: %v", err)
+		}
+		checkExists(t, "after writing temp file")
+		l.Close()
+		checkExists(t, "after second Listener close")
+		os.Remove(name)
+	})
+
+	// SetUnlinkOnClose should do what it says.
+
+	t.Run("Listen/SetUnlinkOnClose(true)", func(t *testing.T) {
+		l := listen(t)
+		checkExists(t, "after Listen")
+		l.SetUnlinkOnClose(true)
+		l.Close()
+		checkNotExists(t, "after Listener close")
+	})
+
+	t.Run("Listen/SetUnlinkOnClose(false)", func(t *testing.T) {
+		l := listen(t)
+		checkExists(t, "after Listen")
+		l.SetUnlinkOnClose(false)
+		l.Close()
+		checkExists(t, "after Listener close")
+		os.Remove(name)
+	})
+
+	t.Run("FileListener/SetUnlinkOnClose(true)", func(t *testing.T) {
+		l := listen(t)
+		f, _ := l.File()
+		l1, _ := FileListener(f)
+		checkExists(t, "after FileListener")
+		l1.(*UnixListener).SetUnlinkOnClose(true)
+		f.Close()
+		checkExists(t, "after File close")
+		l1.Close()
+		checkNotExists(t, "after FileListener close")
+		l.Close()
+	})
+
+	t.Run("FileListener/SetUnlinkOnClose(false)", func(t *testing.T) {
+		l := listen(t)
+		f, _ := l.File()
+		l1, _ := FileListener(f)
+		checkExists(t, "after FileListener")
+		l1.(*UnixListener).SetUnlinkOnClose(false)
+		f.Close()
+		checkExists(t, "after File close")
+		l1.Close()
+		checkExists(t, "after FileListener close")
+		l.Close()
+	})
 }
diff --git a/src/net/url/example_test.go b/src/net/url/example_test.go
index 645de2e..4ae7724 100644
--- a/src/net/url/example_test.go
+++ b/src/net/url/example_test.go
@@ -5,6 +5,7 @@
 package url_test
 
 import (
+	"encoding/json"
 	"fmt"
 	"log"
 	"net/http"
@@ -98,3 +99,21 @@ func ExampleURL_ResolveReference() {
 	// Output:
 	// http://example.com/search?q=dotnet
 }
+
+func ExampleParseQuery() {
+	m, err := url.ParseQuery(`x=1&y=2&y=3;z`)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println(toJSON(m))
+	// Output:
+	// {"x":["1"], "y":["2", "3"], "z":[""]}
+}
+
+func toJSON(m interface{}) string {
+	js, err := json.Marshal(m)
+	if err != nil {
+		log.Fatal(err)
+	}
+	return strings.Replace(string(js), ",", ", ", -1)
+}
diff --git a/src/net/url/url.go b/src/net/url/url.go
index 30e9277..42a514b 100644
--- a/src/net/url/url.go
+++ b/src/net/url/url.go
@@ -74,6 +74,7 @@ type encoding int
 
 const (
 	encodePath encoding = 1 + iota
+	encodePathSegment
 	encodeHost
 	encodeZone
 	encodeUserPassword
@@ -132,9 +133,14 @@ func shouldEscape(c byte, mode encoding) bool {
 			// The RFC allows : @ & = + $ but saves / ; , for assigning
 			// meaning to individual path segments. This package
 			// only manipulates the path as a whole, so we allow those
-			// last two as well. That leaves only ? to escape.
+			// last three as well. That leaves only ? to escape.
 			return c == '?'
 
+		case encodePathSegment: // §3.3
+			// The RFC allows : @ & = + $ but saves / ; , for assigning
+			// meaning to individual path segments.
+			return c == '/' || c == ';' || c == ',' || c == '?'
+
 		case encodeUserPassword: // §3.2.1
 			// The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
 			// userinfo, so we must escape only '@', '/', and '?'.
@@ -164,6 +170,15 @@ func QueryUnescape(s string) (string, error) {
 	return unescape(s, encodeQueryComponent)
 }
 
+// PathUnescape does the inverse transformation of PathEscape, converting
+// %AB into the byte 0xAB. It returns an error if any % is not followed by
+// two hexadecimal digits.
+//
+// PathUnescape is identical to QueryUnescape except that it does not unescape '+' to ' ' (space).
+func PathUnescape(s string) (string, error) {
+	return unescape(s, encodePathSegment)
+}
+
 // unescape unescapes a string; the mode specifies
 // which section of the URL string is being unescaped.
 func unescape(s string, mode encoding) (string, error) {
@@ -250,6 +265,12 @@ func QueryEscape(s string) string {
 	return escape(s, encodeQueryComponent)
 }
 
+// PathEscape escapes the string so it can be safely placed
+// inside a URL path segment.
+func PathEscape(s string) string {
+	return escape(s, encodePathSegment)
+}
+
 func escape(s string, mode encoding) string {
 	spaceCount, hexCount := 0, 0
 	for i := 0; i < len(s); i++ {
@@ -356,10 +377,7 @@ func (u *Userinfo) Username() string {
 
 // Password returns the password in case it is set, and whether it is set.
 func (u *Userinfo) Password() (string, bool) {
-	if u.passwordSet {
-		return u.password, true
-	}
-	return "", false
+	return u.password, u.passwordSet
 }
 
 // String returns the encoded userinfo information in the standard form
@@ -420,7 +438,7 @@ func Parse(rawurl string) (*URL, error) {
 	u, frag := split(rawurl, "#", true)
 	url, err := parse(u, false)
 	if err != nil {
-		return nil, err
+		return nil, &Error{"parse", u, err}
 	}
 	if frag == "" {
 		return url, nil
@@ -437,31 +455,35 @@ func Parse(rawurl string) (*URL, error) {
 // The string rawurl is assumed not to have a #fragment suffix.
 // (Web browsers strip #fragment before sending the URL to a web server.)
 func ParseRequestURI(rawurl string) (*URL, error) {
-	return parse(rawurl, true)
+	url, err := parse(rawurl, true)
+	if err != nil {
+		return nil, &Error{"parse", rawurl, err}
+	}
+	return url, nil
 }
 
 // parse parses a URL from a string in one of two contexts. If
 // viaRequest is true, the URL is assumed to have arrived via an HTTP request,
 // in which case only absolute URLs or path-absolute relative URLs are allowed.
 // If viaRequest is false, all forms of relative URLs are allowed.
-func parse(rawurl string, viaRequest bool) (url *URL, err error) {
+func parse(rawurl string, viaRequest bool) (*URL, error) {
 	var rest string
+	var err error
 
 	if rawurl == "" && viaRequest {
-		err = errors.New("empty url")
-		goto Error
+		return nil, errors.New("empty url")
 	}
-	url = new(URL)
+	url := new(URL)
 
 	if rawurl == "*" {
 		url.Path = "*"
-		return
+		return url, nil
 	}
 
 	// Split off possible leading "http:", "mailto:", etc.
 	// Cannot contain escaped characters.
 	if url.Scheme, rest, err = getscheme(rawurl); err != nil {
-		goto Error
+		return nil, err
 	}
 	url.Scheme = strings.ToLower(url.Scheme)
 
@@ -479,8 +501,20 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
 			return url, nil
 		}
 		if viaRequest {
-			err = errors.New("invalid URI for request")
-			goto Error
+			return nil, errors.New("invalid URI for request")
+		}
+
+		// Avoid confusion with malformed schemes, like cache_object:foo/bar.
+		// See golang.org/issue/16822.
+		//
+		// RFC 3986, §3.3:
+		// In addition, a URI reference (Section 4.1) may be a relative-path reference,
+		// in which case the first path segment cannot contain a colon (":") character.
+		colon := strings.Index(rest, ":")
+		slash := strings.Index(rest, "/")
+		if colon >= 0 && (slash < 0 || colon < slash) {
+			// First path segment has colon. Not allowed in relative URL.
+			return nil, errors.New("first path segment in URL cannot contain colon")
 		}
 	}
 
@@ -489,23 +523,17 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
 		authority, rest = split(rest[2:], "/", false)
 		url.User, url.Host, err = parseAuthority(authority)
 		if err != nil {
-			goto Error
+			return nil, err
 		}
 	}
-	if url.Path, err = unescape(rest, encodePath); err != nil {
-		goto Error
-	}
-	// RawPath is a hint as to the encoding of Path to use
-	// in url.EscapedPath. If that method already gets the
-	// right answer without RawPath, leave it empty.
-	// This will help make sure that people don't rely on it in general.
-	if url.EscapedPath() != rest && validEncodedPath(rest) {
-		url.RawPath = rest
+	// Set Path and, optionally, RawPath.
+	// RawPath is a hint of the encoding of Path. We don't want to set it if
+	// the default escaping of Path is equivalent, to help make sure that people
+	// don't rely on it in general.
+	if err := url.setPath(rest); err != nil {
+		return nil, err
 	}
 	return url, nil
-
-Error:
-	return nil, &Error{"parse", rawurl, err}
 }
 
 func parseAuthority(authority string) (user *Userinfo, host string, err error) {
@@ -586,6 +614,29 @@ func parseHost(host string) (string, error) {
 	return host, nil
 }
 
+// setPath sets the Path and RawPath fields of the URL based on the provided
+// escaped path p. It maintains the invariant that RawPath is only specified
+// when it differs from the default encoding of the path.
+// For example:
+// - setPath("/foo/bar")   will set Path="/foo/bar" and RawPath=""
+// - setPath("/foo%2fbar") will set Path="/foo/bar" and RawPath="/foo%2fbar"
+// setPath will return an error only if the provided path contains an invalid
+// escaping.
+func (u *URL) setPath(p string) error {
+	path, err := unescape(p, encodePath)
+	if err != nil {
+		return err
+	}
+	u.Path = path
+	if escp := escape(path, encodePath); p == escp {
+		// Default encoding is fine.
+		u.RawPath = ""
+	} else {
+		u.RawPath = p
+	}
+	return nil
+}
+
 // EscapedPath returns the escaped form of u.Path.
 // In general there are multiple possible escaped forms of any path.
 // EscapedPath returns u.RawPath when it is a valid escaping of u.Path.
@@ -693,6 +744,17 @@ func (u *URL) String() string {
 		if path != "" && path[0] != '/' && u.Host != "" {
 			buf.WriteByte('/')
 		}
+		if buf.Len() == 0 {
+			// RFC 3986 §4.2
+			// A path segment that contains a colon character (e.g., "this:that")
+			// cannot be used as the first segment of a relative-path reference, as
+			// it would be mistaken for a scheme name. Such a segment must be
+			// preceded by a dot-segment (e.g., "./this:that") to make a relative-
+			// path reference.
+			if i := strings.IndexByte(path, ':'); i > -1 && strings.IndexByte(path[:i], '/') == -1 {
+				buf.WriteString("./")
+			}
+		}
 		buf.WriteString(path)
 	}
 	if u.ForceQuery || u.RawQuery != "" {
@@ -749,6 +811,10 @@ func (v Values) Del(key string) {
 // ParseQuery always returns a non-nil map containing all the
 // valid query parameters found; err describes the first decoding error
 // encountered, if any.
+//
+// Query is expected to be a list of key=value settings separated by
+// ampersands or semicolons. A setting without an equals sign is
+// interpreted as a key set to an empty value.
 func ParseQuery(query string) (Values, error) {
 	m := make(Values)
 	err := parseQuery(m, query)
@@ -852,6 +918,7 @@ func resolvePath(base, ref string) string {
 }
 
 // IsAbs reports whether the URL is absolute.
+// Absolute means that it has a non-empty scheme.
 func (u *URL) IsAbs() bool {
 	return u.Scheme != ""
 }
@@ -880,7 +947,9 @@ func (u *URL) ResolveReference(ref *URL) *URL {
 	}
 	if ref.Scheme != "" || ref.Host != "" || ref.User != nil {
 		// The "absoluteURI" or "net_path" cases.
-		url.Path = resolvePath(ref.Path, "")
+		// We can ignore the error from setPath since we know we provided a
+		// validly-escaped path.
+		url.setPath(resolvePath(ref.EscapedPath(), ""))
 		return &url
 	}
 	if ref.Opaque != "" {
@@ -900,7 +969,7 @@ func (u *URL) ResolveReference(ref *URL) *URL {
 	// The "abs_path" or "rel_path" cases.
 	url.Host = u.Host
 	url.User = u.User
-	url.Path = resolvePath(u.Path, ref.Path)
+	url.setPath(resolvePath(u.EscapedPath(), ref.EscapedPath()))
 	return &url
 }
 
@@ -929,3 +998,59 @@ func (u *URL) RequestURI() string {
 	}
 	return result
 }
+
+// Hostname returns u.Host, without any port number.
+//
+// If Host is an IPv6 literal with a port number, Hostname returns the
+// IPv6 literal without the square brackets. IPv6 literals may include
+// a zone identifier.
+func (u *URL) Hostname() string {
+	return stripPort(u.Host)
+}
+
+// Port returns the port part of u.Host, without the leading colon.
+// If u.Host doesn't contain a port, Port returns an empty string.
+func (u *URL) Port() string {
+	return portOnly(u.Host)
+}
+
+func stripPort(hostport string) string {
+	colon := strings.IndexByte(hostport, ':')
+	if colon == -1 {
+		return hostport
+	}
+	if i := strings.IndexByte(hostport, ']'); i != -1 {
+		return strings.TrimPrefix(hostport[:i], "[")
+	}
+	return hostport[:colon]
+}
+
+func portOnly(hostport string) string {
+	colon := strings.IndexByte(hostport, ':')
+	if colon == -1 {
+		return ""
+	}
+	if i := strings.Index(hostport, "]:"); i != -1 {
+		return hostport[i+len("]:"):]
+	}
+	if strings.Contains(hostport, "]") {
+		return ""
+	}
+	return hostport[colon+len(":"):]
+}
+
+// Marshaling interface implementations.
+// Would like to implement MarshalText/UnmarshalText but that will change the JSON representation of URLs.
+
+func (u *URL) MarshalBinary() (text []byte, err error) {
+	return []byte(u.String()), nil
+}
+
+func (u *URL) UnmarshalBinary(text []byte) error {
+	u1, err := Parse(string(text))
+	if err != nil {
+		return err
+	}
+	*u = *u1
+	return nil
+}
diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go
index 7560f22..6c3bb21 100644
--- a/src/net/url/url_test.go
+++ b/src/net/url/url_test.go
@@ -5,6 +5,10 @@
 package url
 
 import (
+	"bytes"
+	encodingPkg "encoding"
+	"encoding/gob"
+	"encoding/json"
 	"fmt"
 	"io"
 	"net"
@@ -579,20 +583,6 @@ func ufmt(u *URL) string {
 		u.Opaque, u.Scheme, user, pass, u.Host, u.Path, u.RawPath, u.RawQuery, u.Fragment, u.ForceQuery)
 }
 
-func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) {
-	for _, tt := range tests {
-		u, err := parse(tt.in)
-		if err != nil {
-			t.Errorf("%s(%q) returned error %s", name, tt.in, err)
-			continue
-		}
-		if !reflect.DeepEqual(u, tt.out) {
-			t.Errorf("%s(%q):\n\thave %v\n\twant %v\n",
-				name, tt.in, ufmt(u), ufmt(tt.out))
-		}
-	}
-}
-
 func BenchmarkString(b *testing.B) {
 	b.StopTimer()
 	b.ReportAllocs()
@@ -618,7 +608,16 @@ func BenchmarkString(b *testing.B) {
 }
 
 func TestParse(t *testing.T) {
-	DoTest(t, Parse, "Parse", urltests)
+	for _, tt := range urltests {
+		u, err := Parse(tt.in)
+		if err != nil {
+			t.Errorf("Parse(%q) returned error %v", tt.in, err)
+			continue
+		}
+		if !reflect.DeepEqual(u, tt.out) {
+			t.Errorf("Parse(%q):\n\tgot  %v\n\twant %v\n", tt.in, ufmt(u), ufmt(tt.out))
+		}
+	}
 }
 
 const pathThatLooksSchemeRelative = "//not.a.user at not.a.host/just/a/path"
@@ -665,9 +664,10 @@ var parseRequestURLTests = []struct {
 func TestParseRequestURI(t *testing.T) {
 	for _, test := range parseRequestURLTests {
 		_, err := ParseRequestURI(test.url)
-		valid := err == nil
-		if valid != test.expectedValid {
-			t.Errorf("Expected valid=%v for %q; got %v", test.expectedValid, test.url, valid)
+		if test.expectedValid && err != nil {
+			t.Errorf("ParseRequestURI(%q) gave err %v; want no error", test.url, err)
+		} else if !test.expectedValid && err == nil {
+			t.Errorf("ParseRequestURI(%q) gave nil error; want some error", test.url)
 		}
 	}
 
@@ -676,45 +676,69 @@ func TestParseRequestURI(t *testing.T) {
 		t.Fatalf("Unexpected error %v", err)
 	}
 	if url.Path != pathThatLooksSchemeRelative {
-		t.Errorf("Expected path %q; got %q", pathThatLooksSchemeRelative, url.Path)
+		t.Errorf("ParseRequestURI path:\ngot  %q\nwant %q", url.Path, pathThatLooksSchemeRelative)
 	}
 }
 
-func DoTestString(t *testing.T, parse func(string) (*URL, error), name string, tests []URLTest) {
-	for _, tt := range tests {
-		u, err := parse(tt.in)
+var stringURLTests = []struct {
+	url  URL
+	want string
+}{
+	// No leading slash on path should prepend slash on String() call
+	{
+		url: URL{
+			Scheme: "http",
+			Host:   "www.google.com",
+			Path:   "search",
+		},
+		want: "http://www.google.com/search",
+	},
+	// Relative path with first element containing ":" should be prepended with "./", golang.org/issue/17184
+	{
+		url: URL{
+			Path: "this:that",
+		},
+		want: "./this:that",
+	},
+	// Relative path with second element containing ":" should not be prepended with "./"
+	{
+		url: URL{
+			Path: "here/this:that",
+		},
+		want: "here/this:that",
+	},
+	// Non-relative path with first element containing ":" should not be prepended with "./"
+	{
+		url: URL{
+			Scheme: "http",
+			Host:   "www.google.com",
+			Path:   "this:that",
+		},
+		want: "http://www.google.com/this:that",
+	},
+}
+
+func TestURLString(t *testing.T) {
+	for _, tt := range urltests {
+		u, err := Parse(tt.in)
 		if err != nil {
-			t.Errorf("%s(%q) returned error %s", name, tt.in, err)
+			t.Errorf("Parse(%q) returned error %s", tt.in, err)
 			continue
 		}
 		expected := tt.in
-		if len(tt.roundtrip) > 0 {
+		if tt.roundtrip != "" {
 			expected = tt.roundtrip
 		}
 		s := u.String()
 		if s != expected {
-			t.Errorf("%s(%q).String() == %q (expected %q)", name, tt.in, s, expected)
+			t.Errorf("Parse(%q).String() == %q (expected %q)", tt.in, s, expected)
 		}
 	}
-}
 
-func TestURLString(t *testing.T) {
-	DoTestString(t, Parse, "Parse", urltests)
-
-	// no leading slash on path should prepend
-	// slash on String() call
-	noslash := URLTest{
-		"http://www.google.com/search",
-		&URL{
-			Scheme: "http",
-			Host:   "www.google.com",
-			Path:   "search",
-		},
-		"",
-	}
-	s := noslash.out.String()
-	if s != noslash.in {
-		t.Errorf("Expected %s; go %s", noslash.in, s)
+	for _, tt := range stringURLTests {
+		if got := tt.url.String(); got != tt.want {
+			t.Errorf("%+v.String() = %q; want %q", tt.url, got, tt.want)
+		}
 	}
 }
 
@@ -780,6 +804,16 @@ var unescapeTests = []EscapeTest{
 		"",
 		EscapeError("%zz"),
 	},
+	{
+		"a+b",
+		"a b",
+		nil,
+	},
+	{
+		"a%20b",
+		"a b",
+		nil,
+	},
 }
 
 func TestUnescape(t *testing.T) {
@@ -788,10 +822,33 @@ func TestUnescape(t *testing.T) {
 		if actual != tt.out || (err != nil) != (tt.err != nil) {
 			t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", tt.in, actual, err, tt.out, tt.err)
 		}
+
+		in := tt.in
+		out := tt.out
+		if strings.Contains(tt.in, "+") {
+			in = strings.Replace(tt.in, "+", "%20", -1)
+			actual, err := PathUnescape(in)
+			if actual != tt.out || (err != nil) != (tt.err != nil) {
+				t.Errorf("PathUnescape(%q) = %q, %s; want %q, %s", in, actual, err, tt.out, tt.err)
+			}
+			if tt.err == nil {
+				s, err := QueryUnescape(strings.Replace(tt.in, "+", "XXX", -1))
+				if err != nil {
+					continue
+				}
+				in = tt.in
+				out = strings.Replace(s, "XXX", "+", -1)
+			}
+		}
+
+		actual, err = PathUnescape(in)
+		if actual != out || (err != nil) != (tt.err != nil) {
+			t.Errorf("PathUnescape(%q) = %q, %s; want %q, %s", in, actual, err, out, tt.err)
+		}
 	}
 }
 
-var escapeTests = []EscapeTest{
+var queryEscapeTests = []EscapeTest{
 	{
 		"",
 		"",
@@ -819,8 +876,8 @@ var escapeTests = []EscapeTest{
 	},
 }
 
-func TestEscape(t *testing.T) {
-	for _, tt := range escapeTests {
+func TestQueryEscape(t *testing.T) {
+	for _, tt := range queryEscapeTests {
 		actual := QueryEscape(tt.in)
 		if tt.out != actual {
 			t.Errorf("QueryEscape(%q) = %q, want %q", tt.in, actual, tt.out)
@@ -834,6 +891,54 @@ func TestEscape(t *testing.T) {
 	}
 }
 
+var pathEscapeTests = []EscapeTest{
+	{
+		"",
+		"",
+		nil,
+	},
+	{
+		"abc",
+		"abc",
+		nil,
+	},
+	{
+		"abc+def",
+		"abc+def",
+		nil,
+	},
+	{
+		"one two",
+		"one%20two",
+		nil,
+	},
+	{
+		"10%",
+		"10%25",
+		nil,
+	},
+	{
+		" ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;",
+		"%20%3F&=%23+%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09:%2F@$%27%28%29%2A%2C%3B",
+		nil,
+	},
+}
+
+func TestPathEscape(t *testing.T) {
+	for _, tt := range pathEscapeTests {
+		actual := PathEscape(tt.in)
+		if tt.out != actual {
+			t.Errorf("PathEscape(%q) = %q, want %q", tt.in, actual, tt.out)
+		}
+
+		// for bonus points, verify that escape:unescape is an identity.
+		roundtrip, err := PathUnescape(actual)
+		if roundtrip != tt.in || err != nil {
+			t.Errorf("PathUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]")
+		}
+	}
+}
+
 //var userinfoTests = []UserinfoTest{
 //	{"user", "password", "user:password"},
 //	{"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./",
@@ -945,6 +1050,15 @@ var resolveReferenceTests = []struct {
 	// Fragment
 	{"http://foo.com/bar", ".#frag", "http://foo.com/#frag"},
 
+	// Paths with escaping (issue 16947).
+	{"http://foo.com/foo%2fbar/", "../baz", "http://foo.com/baz"},
+	{"http://foo.com/1/2%2f/3%2f4/5", "../../a/b/c", "http://foo.com/1/a/b/c"},
+	{"http://foo.com/1/2/3", "./a%2f../../b/..%2fc", "http://foo.com/1/2/b/..%2fc"},
+	{"http://foo.com/1/2%2f/3%2f4/5", "./a%2f../b/../c", "http://foo.com/1/2%2f/3%2f4/a%2f../c"},
+	{"http://foo.com/foo%20bar/", "../baz", "http://foo.com/baz"},
+	{"http://foo.com/foo", "../bar%2fbaz", "http://foo.com/bar%2fbaz"},
+	{"http://foo.com/foo%2dbar/", "./baz-quux", "http://foo.com/foo%2dbar/baz-quux"},
+
 	// RFC 3986: Normal Examples
 	// http://tools.ietf.org/html/rfc3986#section-5.4.1
 	{"http://a/b/c/d;p?q", "g:h", "g:h"},
@@ -1004,7 +1118,7 @@ func TestResolveReference(t *testing.T) {
 	mustParse := func(url string) *URL {
 		u, err := Parse(url)
 		if err != nil {
-			t.Fatalf("Expected URL to parse: %q, got error: %v", url, err)
+			t.Fatalf("Parse(%q) got err %v", url, err)
 		}
 		return u
 	}
@@ -1013,8 +1127,8 @@ func TestResolveReference(t *testing.T) {
 		base := mustParse(test.base)
 		rel := mustParse(test.rel)
 		url := base.ResolveReference(rel)
-		if url.String() != test.expected {
-			t.Errorf("URL(%q).ResolveReference(%q) == %q, got %q", test.base, test.rel, test.expected, url.String())
+		if got := url.String(); got != test.expected {
+			t.Errorf("URL(%q).ResolveReference(%q)\ngot  %q\nwant %q", test.base, test.rel, got, test.expected)
 		}
 		// Ensure that new instances are returned.
 		if base == url {
@@ -1024,8 +1138,8 @@ func TestResolveReference(t *testing.T) {
 		url, err := base.Parse(test.rel)
 		if err != nil {
 			t.Errorf("URL(%q).Parse(%q) failed: %v", test.base, test.rel, err)
-		} else if url.String() != test.expected {
-			t.Errorf("URL(%q).Parse(%q) == %q, got %q", test.base, test.rel, test.expected, url.String())
+		} else if got := url.String(); got != test.expected {
+			t.Errorf("URL(%q).Parse(%q)\ngot  %q\nwant %q", test.base, test.rel, got, test.expected)
 		} else if base == url {
 			// Ensure that new instances are returned for the wrapper too.
 			t.Errorf("Expected URL.Parse to return new URL instance.")
@@ -1033,14 +1147,14 @@ func TestResolveReference(t *testing.T) {
 		// Ensure Opaque resets the URL.
 		url = base.ResolveReference(opaque)
 		if *url != *opaque {
-			t.Errorf("ResolveReference failed to resolve opaque URL: want %#v, got %#v", url, opaque)
+			t.Errorf("ResolveReference failed to resolve opaque URL:\ngot  %#v\nwant %#v", url, opaque)
 		}
 		// Test the convenience wrapper with an opaque URL too.
 		url, err = base.Parse("scheme:opaque")
 		if err != nil {
 			t.Errorf(`URL(%q).Parse("scheme:opaque") failed: %v`, test.base, err)
 		} else if *url != *opaque {
-			t.Errorf("Parse failed to resolve opaque URL: want %#v, got %#v", url, opaque)
+			t.Errorf("Parse failed to resolve opaque URL:\ngot  %#v\nwant %#v", opaque, url)
 		} else if base == url {
 			// Ensure that new instances are returned, again.
 			t.Errorf("Expected URL.Parse to return new URL instance.")
@@ -1271,7 +1385,7 @@ func TestParseFailure(t *testing.T) {
 	}
 }
 
-func TestParseAuthority(t *testing.T) {
+func TestParseErrors(t *testing.T) {
 	tests := []struct {
 		in      string
 		wantErr bool
@@ -1291,9 +1405,13 @@ func TestParseAuthority(t *testing.T) {
 		{"http://%41:8080/", true},        // not allowed: % encoding only for non-ASCII
 		{"mysql://x@y(z:123)/foo", false}, // golang.org/issue/12023
 		{"mysql://x@y(1.2.3.4:123)/foo", false},
-		{"mysql://x@y([2001:db8::1]:123)/foo", false},
+
 		{"http://[]%20%48%54%54%50%2f%31%2e%31%0a%4d%79%48%65%61%64%65%72%3a%20%31%32%33%0a%0a/", true}, // golang.org/issue/11208
 		{"http://a b.com/", true},                                                                       // no space in host name please
+		{"cache_object://foo", true},                                                                    // scheme cannot have _, relative path cannot have : in first segment
+		{"cache_object:foo", true},
+		{"cache_object:foo/bar", true},
+		{"cache_object/:foo/bar", false},
 	}
 	for _, tt := range tests {
 		u, err := Parse(tt.in)
@@ -1462,11 +1580,106 @@ func TestURLErrorImplementsNetError(t *testing.T) {
 			continue
 		}
 		if err.Timeout() != tt.timeout {
-			t.Errorf("%d: err.Timeout(): want %v, have %v", i+1, tt.timeout, err.Timeout())
+			t.Errorf("%d: err.Timeout(): got %v, want %v", i+1, err.Timeout(), tt.timeout)
 			continue
 		}
 		if err.Temporary() != tt.temporary {
-			t.Errorf("%d: err.Temporary(): want %v, have %v", i+1, tt.temporary, err.Temporary())
+			t.Errorf("%d: err.Temporary(): got %v, want %v", i+1, err.Temporary(), tt.temporary)
+		}
+	}
+}
+
+func TestURLHostname(t *testing.T) {
+	tests := []struct {
+		host string // URL.Host field
+		want string
+	}{
+		{"foo.com:80", "foo.com"},
+		{"foo.com", "foo.com"},
+		{"FOO.COM", "FOO.COM"}, // no canonicalization (yet?)
+		{"1.2.3.4", "1.2.3.4"},
+		{"1.2.3.4:80", "1.2.3.4"},
+		{"[1:2:3:4]", "1:2:3:4"},
+		{"[1:2:3:4]:80", "1:2:3:4"},
+		{"[::1]:80", "::1"},
+	}
+	for _, tt := range tests {
+		u := &URL{Host: tt.host}
+		got := u.Hostname()
+		if got != tt.want {
+			t.Errorf("Hostname for Host %q = %q; want %q", tt.host, got, tt.want)
+		}
+	}
+}
+
+func TestURLPort(t *testing.T) {
+	tests := []struct {
+		host string // URL.Host field
+		want string
+	}{
+		{"foo.com", ""},
+		{"foo.com:80", "80"},
+		{"1.2.3.4", ""},
+		{"1.2.3.4:80", "80"},
+		{"[1:2:3:4]", ""},
+		{"[1:2:3:4]:80", "80"},
+	}
+	for _, tt := range tests {
+		u := &URL{Host: tt.host}
+		got := u.Port()
+		if got != tt.want {
+			t.Errorf("Port for Host %q = %q; want %q", tt.host, got, tt.want)
 		}
 	}
 }
+
+var _ encodingPkg.BinaryMarshaler = (*URL)(nil)
+var _ encodingPkg.BinaryUnmarshaler = (*URL)(nil)
+
+func TestJSON(t *testing.T) {
+	u, err := Parse("https://www.google.com/x?y=z")
+	if err != nil {
+		t.Fatal(err)
+	}
+	js, err := json.Marshal(u)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// If only we could implement TextMarshaler/TextUnmarshaler,
+	// this would work:
+	//
+	// if string(js) != strconv.Quote(u.String()) {
+	// 	t.Errorf("json encoding: %s\nwant: %s\n", js, strconv.Quote(u.String()))
+	// }
+
+	u1 := new(URL)
+	err = json.Unmarshal(js, u1)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if u1.String() != u.String() {
+		t.Errorf("json decoded to: %s\nwant: %s\n", u1, u)
+	}
+}
+
+func TestGob(t *testing.T) {
+	u, err := Parse("https://www.google.com/x?y=z")
+	if err != nil {
+		t.Fatal(err)
+	}
+	var w bytes.Buffer
+	err = gob.NewEncoder(&w).Encode(u)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	u1 := new(URL)
+	err = gob.NewDecoder(&w).Decode(u1)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if u1.String() != u.String() {
+		t.Errorf("json decoded to: %s\nwant: %s\n", u1, u)
+	}
+}
diff --git a/src/net/writev_test.go b/src/net/writev_test.go
new file mode 100644
index 0000000..4d2fc39
--- /dev/null
+++ b/src/net/writev_test.go
@@ -0,0 +1,225 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"reflect"
+	"runtime"
+	"sync"
+	"testing"
+)
+
+func TestBuffers_read(t *testing.T) {
+	const story = "once upon a time in Gopherland ... "
+	buffers := Buffers{
+		[]byte("once "),
+		[]byte("upon "),
+		[]byte("a "),
+		[]byte("time "),
+		[]byte("in "),
+		[]byte("Gopherland ... "),
+	}
+	got, err := ioutil.ReadAll(&buffers)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if string(got) != story {
+		t.Errorf("read %q; want %q", got, story)
+	}
+	if len(buffers) != 0 {
+		t.Errorf("len(buffers) = %d; want 0", len(buffers))
+	}
+}
+
+func TestBuffers_consume(t *testing.T) {
+	tests := []struct {
+		in      Buffers
+		consume int64
+		want    Buffers
+	}{
+		{
+			in:      Buffers{[]byte("foo"), []byte("bar")},
+			consume: 0,
+			want:    Buffers{[]byte("foo"), []byte("bar")},
+		},
+		{
+			in:      Buffers{[]byte("foo"), []byte("bar")},
+			consume: 2,
+			want:    Buffers{[]byte("o"), []byte("bar")},
+		},
+		{
+			in:      Buffers{[]byte("foo"), []byte("bar")},
+			consume: 3,
+			want:    Buffers{[]byte("bar")},
+		},
+		{
+			in:      Buffers{[]byte("foo"), []byte("bar")},
+			consume: 4,
+			want:    Buffers{[]byte("ar")},
+		},
+		{
+			in:      Buffers{nil, nil, nil, []byte("bar")},
+			consume: 1,
+			want:    Buffers{[]byte("ar")},
+		},
+		{
+			in:      Buffers{nil, nil, nil, []byte("foo")},
+			consume: 0,
+			want:    Buffers{[]byte("foo")},
+		},
+		{
+			in:      Buffers{nil, nil, nil},
+			consume: 0,
+			want:    Buffers{},
+		},
+	}
+	for i, tt := range tests {
+		in := tt.in
+		in.consume(tt.consume)
+		if !reflect.DeepEqual(in, tt.want) {
+			t.Errorf("%d. after consume(%d) = %+v, want %+v", i, tt.consume, in, tt.want)
+		}
+	}
+}
+
+func TestBuffers_WriteTo(t *testing.T) {
+	for _, name := range []string{"WriteTo", "Copy"} {
+		for _, size := range []int{0, 10, 1023, 1024, 1025} {
+			t.Run(fmt.Sprintf("%s/%d", name, size), func(t *testing.T) {
+				testBuffer_writeTo(t, size, name == "Copy")
+			})
+		}
+	}
+}
+
+func testBuffer_writeTo(t *testing.T, chunks int, useCopy bool) {
+	oldHook := testHookDidWritev
+	defer func() { testHookDidWritev = oldHook }()
+	var writeLog struct {
+		sync.Mutex
+		log []int
+	}
+	testHookDidWritev = func(size int) {
+		writeLog.Lock()
+		writeLog.log = append(writeLog.log, size)
+		writeLog.Unlock()
+	}
+	var want bytes.Buffer
+	for i := 0; i < chunks; i++ {
+		want.WriteByte(byte(i))
+	}
+
+	withTCPConnPair(t, func(c *TCPConn) error {
+		buffers := make(Buffers, chunks)
+		for i := range buffers {
+			buffers[i] = want.Bytes()[i : i+1]
+		}
+		var n int64
+		var err error
+		if useCopy {
+			n, err = io.Copy(c, &buffers)
+		} else {
+			n, err = buffers.WriteTo(c)
+		}
+		if err != nil {
+			return err
+		}
+		if len(buffers) != 0 {
+			return fmt.Errorf("len(buffers) = %d; want 0", len(buffers))
+		}
+		if n != int64(want.Len()) {
+			return fmt.Errorf("Buffers.WriteTo returned %d; want %d", n, want.Len())
+		}
+		return nil
+	}, func(c *TCPConn) error {
+		all, err := ioutil.ReadAll(c)
+		if !bytes.Equal(all, want.Bytes()) || err != nil {
+			return fmt.Errorf("client read %q, %v; want %q, nil", all, err, want.Bytes())
+		}
+
+		writeLog.Lock() // no need to unlock
+		var gotSum int
+		for _, v := range writeLog.log {
+			gotSum += v
+		}
+
+		var wantSum int
+		switch runtime.GOOS {
+		case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd":
+			var wantMinCalls int
+			wantSum = want.Len()
+			v := chunks
+			for v > 0 {
+				wantMinCalls++
+				v -= 1024
+			}
+			if len(writeLog.log) < wantMinCalls {
+				t.Errorf("write calls = %v < wanted min %v", len(writeLog.log), wantMinCalls)
+			}
+		case "windows":
+			var wantCalls int
+			wantSum = want.Len()
+			if wantSum > 0 {
+				wantCalls = 1 // windows will always do 1 syscall, unless sending empty buffer
+			}
+			if len(writeLog.log) != wantCalls {
+				t.Errorf("write calls = %v; want %v", len(writeLog.log), wantCalls)
+			}
+		}
+		if gotSum != wantSum {
+			t.Errorf("writev call sum  = %v; want %v", gotSum, wantSum)
+		}
+		return nil
+	})
+}
+
+func TestWritevError(t *testing.T) {
+	if runtime.GOOS == "windows" {
+		t.Skipf("skipping the test: windows does not have problem sending large chunks of data")
+	}
+
+	ln, err := newLocalListener("tcp")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer ln.Close()
+
+	ch := make(chan Conn, 1)
+	go func() {
+		defer close(ch)
+		c, err := ln.Accept()
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		ch <- c
+	}()
+	c1, err := Dial("tcp", ln.Addr().String())
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer c1.Close()
+	c2 := <-ch
+	if c2 == nil {
+		t.Fatal("no server side connection")
+	}
+	c2.Close()
+
+	// 1 GB of data should be enough to notice the connection is gone.
+	// Just a few bytes is not enough.
+	// Arrange to reuse the same 1 MB buffer so that we don't allocate much.
+	buf := make([]byte, 1<<20)
+	buffers := make(Buffers, 1<<10)
+	for i := range buffers {
+		buffers[i] = buf
+	}
+	if _, err := buffers.WriteTo(c1); err == nil {
+		t.Fatal("Buffers.WriteTo(closed conn) succeeded, want error")
+	}
+}
diff --git a/src/net/writev_unix.go b/src/net/writev_unix.go
new file mode 100644
index 0000000..174e6bc
--- /dev/null
+++ b/src/net/writev_unix.go
@@ -0,0 +1,95 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package net
+
+import (
+	"io"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+func (c *conn) writeBuffers(v *Buffers) (int64, error) {
+	if !c.ok() {
+		return 0, syscall.EINVAL
+	}
+	n, err := c.fd.writeBuffers(v)
+	if err != nil {
+		return n, &OpError{Op: "writev", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+	}
+	return n, nil
+}
+
+func (fd *netFD) writeBuffers(v *Buffers) (n int64, err error) {
+	if err := fd.writeLock(); err != nil {
+		return 0, err
+	}
+	defer fd.writeUnlock()
+	if err := fd.pd.prepareWrite(); err != nil {
+		return 0, err
+	}
+
+	var iovecs []syscall.Iovec
+	if fd.iovecs != nil {
+		iovecs = *fd.iovecs
+	}
+	// TODO: read from sysconf(_SC_IOV_MAX)? The Linux default is
+	// 1024 and this seems conservative enough for now. Darwin's
+	// UIO_MAXIOV also seems to be 1024.
+	maxVec := 1024
+
+	for len(*v) > 0 {
+		iovecs = iovecs[:0]
+		for _, chunk := range *v {
+			if len(chunk) == 0 {
+				continue
+			}
+			iovecs = append(iovecs, syscall.Iovec{Base: &chunk[0]})
+			if fd.isStream && len(chunk) > 1<<30 {
+				iovecs[len(iovecs)-1].SetLen(1 << 30)
+				break // continue chunk on next writev
+			}
+			iovecs[len(iovecs)-1].SetLen(len(chunk))
+			if len(iovecs) == maxVec {
+				break
+			}
+		}
+		if len(iovecs) == 0 {
+			break
+		}
+		fd.iovecs = &iovecs // cache
+
+		wrote, _, e0 := syscall.Syscall(syscall.SYS_WRITEV,
+			uintptr(fd.sysfd),
+			uintptr(unsafe.Pointer(&iovecs[0])),
+			uintptr(len(iovecs)))
+		if wrote == ^uintptr(0) {
+			wrote = 0
+		}
+		testHookDidWritev(int(wrote))
+		n += int64(wrote)
+		v.consume(int64(wrote))
+		if e0 == syscall.EAGAIN {
+			if err = fd.pd.waitWrite(); err == nil {
+				continue
+			}
+		} else if e0 != 0 {
+			err = syscall.Errno(e0)
+		}
+		if err != nil {
+			break
+		}
+		if n == 0 {
+			err = io.ErrUnexpectedEOF
+			break
+		}
+	}
+	if _, ok := err.(syscall.Errno); ok {
+		err = os.NewSyscallError("writev", err)
+	}
+	return n, err
+}
diff --git a/src/os/dir.go b/src/os/dir.go
new file mode 100644
index 0000000..6c54456
--- /dev/null
+++ b/src/os/dir.go
@@ -0,0 +1,46 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+// Readdir reads the contents of the directory associated with file and
+// returns a slice of up to n FileInfo values, as would be returned
+// by Lstat, in directory order. Subsequent calls on the same file will yield
+// further FileInfos.
+//
+// If n > 0, Readdir returns at most n FileInfo structures. In this case, if
+// Readdir returns an empty slice, it will return a non-nil error
+// explaining why. At the end of a directory, the error is io.EOF.
+//
+// If n <= 0, Readdir returns all the FileInfo from the directory in
+// a single slice. In this case, if Readdir succeeds (reads all
+// the way to the end of the directory), it returns the slice and a
+// nil error. If it encounters an error before the end of the
+// directory, Readdir returns the FileInfo read until that point
+// and a non-nil error.
+func (f *File) Readdir(n int) ([]FileInfo, error) {
+	if f == nil {
+		return nil, ErrInvalid
+	}
+	return f.readdir(n)
+}
+
+// Readdirnames reads and returns a slice of names from the directory f.
+//
+// If n > 0, Readdirnames returns at most n names. In this case, if
+// Readdirnames returns an empty slice, it will return a non-nil error
+// explaining why. At the end of a directory, the error is io.EOF.
+//
+// If n <= 0, Readdirnames returns all the names from the directory in
+// a single slice. In this case, if Readdirnames succeeds (reads all
+// the way to the end of the directory), it returns the slice and a
+// nil error. If it encounters an error before the end of the
+// directory, Readdirnames returns the names read until that point and
+// a non-nil error.
+func (f *File) Readdirnames(n int) (names []string, err error) {
+	if f == nil {
+		return nil, ErrInvalid
+	}
+	return f.readdirnames(n)
+}
diff --git a/src/os/dir_unix.go b/src/os/dir_unix.go
index 589db85..03d949a 100644
--- a/src/os/dir_unix.go
+++ b/src/os/dir_unix.go
@@ -15,6 +15,33 @@ const (
 	blockSize = 4096
 )
 
+func (f *File) readdir(n int) (fi []FileInfo, err error) {
+	dirname := f.name
+	if dirname == "" {
+		dirname = "."
+	}
+	names, err := f.Readdirnames(n)
+	fi = make([]FileInfo, 0, len(names))
+	for _, filename := range names {
+		fip, lerr := lstat(dirname + "/" + filename)
+		if IsNotExist(lerr) {
+			// File disappeared between readdir + stat.
+			// Just treat it as if it didn't exist.
+			continue
+		}
+		if lerr != nil {
+			return fi, lerr
+		}
+		fi = append(fi, fip)
+	}
+	if len(fi) == 0 && err == nil && n > 0 {
+		// Per File.Readdir, the slice must be non-empty or err
+		// must be non-nil if n > 0.
+		err = io.EOF
+	}
+	return fi, err
+}
+
 func (f *File) readdirnames(n int) (names []string, err error) {
 	// If this file has no dirinfo, create one.
 	if f.dirinfo == nil {
diff --git a/src/os/dir_windows.go b/src/os/dir_windows.go
index 9313160..76024fc 100644
--- a/src/os/dir_windows.go
+++ b/src/os/dir_windows.go
@@ -4,6 +4,70 @@
 
 package os
 
+import (
+	"io"
+	"syscall"
+)
+
+func (file *File) readdir(n int) (fi []FileInfo, err error) {
+	if file == nil {
+		return nil, syscall.EINVAL
+	}
+	if !file.isdir() {
+		return nil, &PathError{"Readdir", file.name, syscall.ENOTDIR}
+	}
+	if !file.dirinfo.isempty && file.fd == syscall.InvalidHandle {
+		return nil, syscall.EINVAL
+	}
+	wantAll := n <= 0
+	size := n
+	if wantAll {
+		n = -1
+		size = 100
+	}
+	fi = make([]FileInfo, 0, size) // Empty with room to grow.
+	d := &file.dirinfo.data
+	for n != 0 && !file.dirinfo.isempty {
+		if file.dirinfo.needdata {
+			e := syscall.FindNextFile(file.fd, d)
+			if e != nil {
+				if e == syscall.ERROR_NO_MORE_FILES {
+					break
+				} else {
+					err = &PathError{"FindNextFile", file.name, e}
+					if !wantAll {
+						fi = nil
+					}
+					return
+				}
+			}
+		}
+		file.dirinfo.needdata = true
+		name := syscall.UTF16ToString(d.FileName[0:])
+		if name == "." || name == ".." { // Useless names
+			continue
+		}
+		f := &fileStat{
+			name: name,
+			sys: syscall.Win32FileAttributeData{
+				FileAttributes: d.FileAttributes,
+				CreationTime:   d.CreationTime,
+				LastAccessTime: d.LastAccessTime,
+				LastWriteTime:  d.LastWriteTime,
+				FileSizeHigh:   d.FileSizeHigh,
+				FileSizeLow:    d.FileSizeLow,
+			},
+			path: file.dirinfo.path + `\` + name,
+		}
+		n--
+		fi = append(fi, f)
+	}
+	if !wantAll && len(fi) == 0 {
+		return fi, io.EOF
+	}
+	return fi, nil
+}
+
 func (file *File) readdirnames(n int) (names []string, err error) {
 	fis, err := file.Readdir(n)
 	names = make([]string, len(fis))
diff --git a/src/os/doc.go b/src/os/doc.go
deleted file mode 100644
index 0313eac..0000000
--- a/src/os/doc.go
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package os
-
-import "time"
-
-// FindProcess looks for a running process by its pid.
-//
-// The Process it returns can be used to obtain information
-// about the underlying operating system process.
-//
-// On Unix systems, FindProcess always succeeds and returns a Process
-// for the given pid, regardless of whether the process exists.
-func FindProcess(pid int) (*Process, error) {
-	return findProcess(pid)
-}
-
-// StartProcess starts a new process with the program, arguments and attributes
-// specified by name, argv and attr.
-//
-// StartProcess is a low-level interface. The os/exec package provides
-// higher-level interfaces.
-//
-// If there is an error, it will be of type *PathError.
-func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) {
-	return startProcess(name, argv, attr)
-}
-
-// Release releases any resources associated with the Process p,
-// rendering it unusable in the future.
-// Release only needs to be called if Wait is not.
-func (p *Process) Release() error {
-	return p.release()
-}
-
-// Kill causes the Process to exit immediately.
-func (p *Process) Kill() error {
-	return p.kill()
-}
-
-// Wait waits for the Process to exit, and then returns a
-// ProcessState describing its status and an error, if any.
-// Wait releases any resources associated with the Process.
-// On most operating systems, the Process must be a child
-// of the current process or an error will be returned.
-func (p *Process) Wait() (*ProcessState, error) {
-	return p.wait()
-}
-
-// Signal sends a signal to the Process.
-// Sending Interrupt on Windows is not implemented.
-func (p *Process) Signal(sig Signal) error {
-	return p.signal(sig)
-}
-
-// UserTime returns the user CPU time of the exited process and its children.
-func (p *ProcessState) UserTime() time.Duration {
-	return p.userTime()
-}
-
-// SystemTime returns the system CPU time of the exited process and its children.
-func (p *ProcessState) SystemTime() time.Duration {
-	return p.systemTime()
-}
-
-// Exited reports whether the program has exited.
-func (p *ProcessState) Exited() bool {
-	return p.exited()
-}
-
-// Success reports whether the program exited successfully,
-// such as with exit status 0 on Unix.
-func (p *ProcessState) Success() bool {
-	return p.success()
-}
-
-// Sys returns system-dependent exit information about
-// the process. Convert it to the appropriate underlying
-// type, such as syscall.WaitStatus on Unix, to access its contents.
-func (p *ProcessState) Sys() interface{} {
-	return p.sys()
-}
-
-// SysUsage returns system-dependent resource usage information about
-// the exited process. Convert it to the appropriate underlying
-// type, such as *syscall.Rusage on Unix, to access its contents.
-// (On Unix, *syscall.Rusage matches struct rusage as defined in the
-// getrusage(2) manual page.)
-func (p *ProcessState) SysUsage() interface{} {
-	return p.sysUsage()
-}
-
-// Hostname returns the host name reported by the kernel.
-func Hostname() (name string, err error) {
-	return hostname()
-}
-
-// Readdir reads the contents of the directory associated with file and
-// returns a slice of up to n FileInfo values, as would be returned
-// by Lstat, in directory order. Subsequent calls on the same file will yield
-// further FileInfos.
-//
-// If n > 0, Readdir returns at most n FileInfo structures. In this case, if
-// Readdir returns an empty slice, it will return a non-nil error
-// explaining why. At the end of a directory, the error is io.EOF.
-//
-// If n <= 0, Readdir returns all the FileInfo from the directory in
-// a single slice. In this case, if Readdir succeeds (reads all
-// the way to the end of the directory), it returns the slice and a
-// nil error. If it encounters an error before the end of the
-// directory, Readdir returns the FileInfo read until that point
-// and a non-nil error.
-func (f *File) Readdir(n int) ([]FileInfo, error) {
-	if f == nil {
-		return nil, ErrInvalid
-	}
-	return f.readdir(n)
-}
-
-// Readdirnames reads and returns a slice of names from the directory f.
-//
-// If n > 0, Readdirnames returns at most n names. In this case, if
-// Readdirnames returns an empty slice, it will return a non-nil error
-// explaining why. At the end of a directory, the error is io.EOF.
-//
-// If n <= 0, Readdirnames returns all the names from the directory in
-// a single slice. In this case, if Readdirnames succeeds (reads all
-// the way to the end of the directory), it returns the slice and a
-// nil error. If it encounters an error before the end of the
-// directory, Readdirnames returns the names read until that point and
-// a non-nil error.
-func (f *File) Readdirnames(n int) (names []string, err error) {
-	if f == nil {
-		return nil, ErrInvalid
-	}
-	return f.readdirnames(n)
-}
diff --git a/src/os/env.go b/src/os/env.go
index 4a14714..a03b8f6 100644
--- a/src/os/env.go
+++ b/src/os/env.go
@@ -37,7 +37,7 @@ func ExpandEnv(s string) string {
 // shell variable such as $*.
 func isShellSpecialVar(c uint8) bool {
 	switch c {
-	case '*', '#', '$', '@', '!', '?', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+	case '*', '#', '$', '@', '!', '?', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
 		return true
 	}
 	return false
@@ -76,6 +76,7 @@ func getShellName(s string) (string, int) {
 
 // Getenv retrieves the value of the environment variable named by the key.
 // It returns the value, which will be empty if the variable is not present.
+// To distinguish between an empty value and an unset value, use LookupEnv.
 func Getenv(key string) string {
 	v, _ := syscall.Getenv(key)
 	return v
diff --git a/src/os/env_test.go b/src/os/env_test.go
index d1074cd..e5749f0 100644
--- a/src/os/env_test.go
+++ b/src/os/env_test.go
@@ -95,6 +95,34 @@ func TestUnsetenv(t *testing.T) {
 	}
 }
 
+func TestClearenv(t *testing.T) {
+	const testKey = "GO_TEST_CLEARENV"
+	const testValue = "1"
+
+	// reset env
+	defer func(origEnv []string) {
+		for _, pair := range origEnv {
+			// Environment variables on Windows can begin with =
+			// http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
+			i := strings.Index(pair[1:], "=") + 1
+			if err := Setenv(pair[:i], pair[i+1:]); err != nil {
+				t.Errorf("Setenv(%q, %q) failed during reset: %v", pair[:i], pair[i+1:], err)
+			}
+		}
+	}(Environ())
+
+	if err := Setenv(testKey, testValue); err != nil {
+		t.Fatalf("Setenv(%q, %q) failed: %v", testKey, testValue, err)
+	}
+	if _, ok := LookupEnv(testKey); !ok {
+		t.Errorf("Setenv(%q, %q) didn't set $%s", testKey, testValue, testKey)
+	}
+	Clearenv()
+	if val, ok := LookupEnv(testKey); ok {
+		t.Errorf("Clearenv() didn't clear $%s, remained with value %q", testKey, val)
+	}
+}
+
 func TestLookupEnv(t *testing.T) {
 	const smallpox = "SMALLPOX"      // No one has smallpox.
 	value, ok := LookupEnv(smallpox) // Should not exist.
diff --git a/src/os/env_unix_test.go b/src/os/env_unix_test.go
index 5ec07ee..f7b67eb 100644
--- a/src/os/env_unix_test.go
+++ b/src/os/env_unix_test.go
@@ -7,6 +7,7 @@
 package os_test
 
 import (
+	"fmt"
 	. "os"
 	"testing"
 )
@@ -28,3 +29,28 @@ func TestSetenvUnixEinval(t *testing.T) {
 		}
 	}
 }
+
+var shellSpecialVarTests = []struct {
+	k, v string
+}{
+	{"*", "asterisk"},
+	{"#", "pound"},
+	{"$", "dollar"},
+	{"@", "at"},
+	{"!", "exclamation mark"},
+	{"?", "question mark"},
+	{"-", "dash"},
+}
+
+func TestExpandEnvShellSpecialVar(t *testing.T) {
+	for _, tt := range shellSpecialVarTests {
+		Setenv(tt.k, tt.v)
+		defer Unsetenv(tt.k)
+
+		argRaw := fmt.Sprintf("$%s", tt.k)
+		argWithBrace := fmt.Sprintf("${%s}", tt.k)
+		if gotRaw, gotBrace := ExpandEnv(argRaw), ExpandEnv(argWithBrace); gotRaw != gotBrace {
+			t.Errorf("ExpandEnv(%q) = %q, ExpandEnv(%q) = %q; expect them to be equal", argRaw, gotRaw, argWithBrace, gotBrace)
+		}
+	}
+}
diff --git a/src/os/error.go b/src/os/error.go
index e26ce27..7235bfb 100644
--- a/src/os/error.go
+++ b/src/os/error.go
@@ -14,6 +14,7 @@ var (
 	ErrPermission = errors.New("permission denied")
 	ErrExist      = errors.New("file already exists")
 	ErrNotExist   = errors.New("file does not exist")
+	ErrClosed     = errors.New("file already closed")
 )
 
 // PathError records an error and the operation and file path that caused it.
@@ -63,3 +64,16 @@ func IsNotExist(err error) bool {
 func IsPermission(err error) bool {
 	return isPermission(err)
 }
+
+// underlyingError returns the underlying error for known os error types.
+func underlyingError(err error) error {
+	switch err := err.(type) {
+	case *PathError:
+		return err.Err
+	case *LinkError:
+		return err.Err
+	case *SyscallError:
+		return err.Err
+	}
+	return err
+}
diff --git a/src/os/error_plan9.go b/src/os/error_plan9.go
index 2dc6b39..a673439 100644
--- a/src/os/error_plan9.go
+++ b/src/os/error_plan9.go
@@ -5,46 +5,30 @@
 package os
 
 func isExist(err error) bool {
-	switch pe := err.(type) {
-	case nil:
-		return false
-	case *PathError:
-		err = pe.Err
-	case *LinkError:
-		err = pe.Err
-	case *SyscallError:
-		err = pe.Err
-	}
-	return contains(err.Error(), " exists")
+	return checkErrMessageContent(err, " exists")
 }
 
 func isNotExist(err error) bool {
-	switch pe := err.(type) {
-	case nil:
-		return false
-	case *PathError:
-		err = pe.Err
-	case *LinkError:
-		err = pe.Err
-	case *SyscallError:
-		err = pe.Err
-	}
-	return contains(err.Error(), "does not exist") || contains(err.Error(), "not found") ||
-		contains(err.Error(), "has been removed") || contains(err.Error(), "no parent")
+	return checkErrMessageContent(err, "does not exist", "not found",
+		"has been removed", "no parent")
 }
 
 func isPermission(err error) bool {
-	switch pe := err.(type) {
-	case nil:
+	return checkErrMessageContent(err, "permission denied")
+}
+
+// checkErrMessageContent checks if err message contains one of msgs.
+func checkErrMessageContent(err error, msgs ...string) bool {
+	if err == nil {
 		return false
-	case *PathError:
-		err = pe.Err
-	case *LinkError:
-		err = pe.Err
-	case *SyscallError:
-		err = pe.Err
 	}
-	return contains(err.Error(), "permission denied")
+	err = underlyingError(err)
+	for _, msg := range msgs {
+		if contains(err.Error(), msg) {
+			return true
+		}
+	}
+	return false
 }
 
 // contains is a local version of strings.Contains. It knows len(sep) > 1.
diff --git a/src/os/error_test.go b/src/os/error_test.go
index a47c173..3499cee 100644
--- a/src/os/error_test.go
+++ b/src/os/error_test.go
@@ -91,10 +91,12 @@ var isExistTests = []isExistTest{
 	{&os.PathError{Err: os.ErrPermission}, false, false},
 	{&os.PathError{Err: os.ErrExist}, true, false},
 	{&os.PathError{Err: os.ErrNotExist}, false, true},
+	{&os.PathError{Err: os.ErrClosed}, false, false},
 	{&os.LinkError{Err: os.ErrInvalid}, false, false},
 	{&os.LinkError{Err: os.ErrPermission}, false, false},
 	{&os.LinkError{Err: os.ErrExist}, true, false},
 	{&os.LinkError{Err: os.ErrNotExist}, false, true},
+	{&os.LinkError{Err: os.ErrClosed}, false, false},
 	{&os.SyscallError{Err: os.ErrNotExist}, false, true},
 	{&os.SyscallError{Err: os.ErrExist}, true, false},
 	{nil, false, false},
diff --git a/src/os/error_unix.go b/src/os/error_unix.go
index 3c78eb4..be1440c 100644
--- a/src/os/error_unix.go
+++ b/src/os/error_unix.go
@@ -9,43 +9,16 @@ package os
 import "syscall"
 
 func isExist(err error) bool {
-	switch pe := err.(type) {
-	case nil:
-		return false
-	case *PathError:
-		err = pe.Err
-	case *LinkError:
-		err = pe.Err
-	case *SyscallError:
-		err = pe.Err
-	}
+	err = underlyingError(err)
 	return err == syscall.EEXIST || err == syscall.ENOTEMPTY || err == ErrExist
 }
 
 func isNotExist(err error) bool {
-	switch pe := err.(type) {
-	case nil:
-		return false
-	case *PathError:
-		err = pe.Err
-	case *LinkError:
-		err = pe.Err
-	case *SyscallError:
-		err = pe.Err
-	}
+	err = underlyingError(err)
 	return err == syscall.ENOENT || err == ErrNotExist
 }
 
 func isPermission(err error) bool {
-	switch pe := err.(type) {
-	case nil:
-		return false
-	case *PathError:
-		err = pe.Err
-	case *LinkError:
-		err = pe.Err
-	case *SyscallError:
-		err = pe.Err
-	}
+	err = underlyingError(err)
 	return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
 }
diff --git a/src/os/error_windows.go b/src/os/error_windows.go
index 2c1c39c..02593b5 100644
--- a/src/os/error_windows.go
+++ b/src/os/error_windows.go
@@ -7,48 +7,22 @@ package os
 import "syscall"
 
 func isExist(err error) bool {
-	switch pe := err.(type) {
-	case nil:
-		return false
-	case *PathError:
-		err = pe.Err
-	case *LinkError:
-		err = pe.Err
-	case *SyscallError:
-		err = pe.Err
-	}
+	err = underlyingError(err)
 	return err == syscall.ERROR_ALREADY_EXISTS ||
+		err == syscall.ERROR_DIR_NOT_EMPTY ||
 		err == syscall.ERROR_FILE_EXISTS || err == ErrExist
 }
 
 const _ERROR_BAD_NETPATH = syscall.Errno(53)
 
 func isNotExist(err error) bool {
-	switch pe := err.(type) {
-	case nil:
-		return false
-	case *PathError:
-		err = pe.Err
-	case *LinkError:
-		err = pe.Err
-	case *SyscallError:
-		err = pe.Err
-	}
+	err = underlyingError(err)
 	return err == syscall.ERROR_FILE_NOT_FOUND ||
 		err == _ERROR_BAD_NETPATH ||
 		err == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
 }
 
 func isPermission(err error) bool {
-	switch pe := err.(type) {
-	case nil:
-		return false
-	case *PathError:
-		err = pe.Err
-	case *LinkError:
-		err = pe.Err
-	case *SyscallError:
-		err = pe.Err
-	}
+	err = underlyingError(err)
 	return err == syscall.ERROR_ACCESS_DENIED || err == ErrPermission
 }
diff --git a/src/os/error_windows_test.go b/src/os/error_windows_test.go
index 427dfdb..1635c10 100644
--- a/src/os/error_windows_test.go
+++ b/src/os/error_windows_test.go
@@ -26,6 +26,10 @@ func init() {
 		isExistTest{err: &os.PathError{Err: syscall.ERROR_PATH_NOT_FOUND}, is: false, isnot: true},
 		isExistTest{err: &os.LinkError{Err: syscall.ERROR_PATH_NOT_FOUND}, is: false, isnot: true},
 		isExistTest{err: &os.SyscallError{Err: syscall.ERROR_PATH_NOT_FOUND}, is: false, isnot: true},
+
+		isExistTest{err: &os.PathError{Err: syscall.ERROR_DIR_NOT_EMPTY}, is: true, isnot: false},
+		isExistTest{err: &os.LinkError{Err: syscall.ERROR_DIR_NOT_EMPTY}, is: true, isnot: false},
+		isExistTest{err: &os.SyscallError{Err: syscall.ERROR_DIR_NOT_EMPTY}, is: true, isnot: false},
 	)
 	isPermissionTests = append(isPermissionTests,
 		isPermissionTest{err: &os.PathError{Err: syscall.ERROR_ACCESS_DENIED}, want: true},
diff --git a/src/os/example_test.go b/src/os/example_test.go
new file mode 100644
index 0000000..07f9c76
--- /dev/null
+++ b/src/os/example_test.go
@@ -0,0 +1,106 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os_test
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"time"
+)
+
+func ExampleOpenFile() {
+	f, err := os.OpenFile("notes.txt", os.O_RDWR|os.O_CREATE, 0755)
+	if err != nil {
+		log.Fatal(err)
+	}
+	if err := f.Close(); err != nil {
+		log.Fatal(err)
+	}
+}
+
+func ExampleChmod() {
+	if err := os.Chmod("some-filename", 0644); err != nil {
+		log.Fatal(err)
+	}
+}
+
+func ExampleChtimes() {
+	mtime := time.Date(2006, time.February, 1, 3, 4, 5, 0, time.UTC)
+	atime := time.Date(2007, time.March, 2, 4, 5, 6, 0, time.UTC)
+	if err := os.Chtimes("some-filename", atime, mtime); err != nil {
+		log.Fatal(err)
+	}
+}
+
+func ExampleFileMode() {
+	fi, err := os.Stat("some-filename")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	switch mode := fi.Mode(); {
+	case mode.IsRegular():
+		fmt.Println("regular file")
+	case mode.IsDir():
+		fmt.Println("directory")
+	case mode&os.ModeSymlink != 0:
+		fmt.Println("symbolic link")
+	case mode&os.ModeNamedPipe != 0:
+		fmt.Println("named pipe")
+	}
+}
+
+func ExampleIsNotExist() {
+	filename := "a-nonexistent-file"
+	if _, err := os.Stat(filename); os.IsNotExist(err) {
+		fmt.Printf("file does not exist")
+	}
+	// Output:
+	// file does not exist
+}
+
+func init() {
+	os.Setenv("USER", "gopher")
+	os.Setenv("HOME", "/usr/gopher")
+	os.Unsetenv("GOPATH")
+}
+
+func ExampleExpandEnv() {
+	fmt.Println(os.ExpandEnv("$USER lives in ${HOME}."))
+
+	// Output:
+	// gopher lives in /usr/gopher.
+}
+
+func ExampleLookupEnv() {
+	show := func(key string) {
+		val, ok := os.LookupEnv(key)
+		if !ok {
+			fmt.Printf("%s not set\n", key)
+		} else {
+			fmt.Printf("%s=%s\n", key, val)
+		}
+	}
+
+	show("USER")
+	show("GOPATH")
+
+	// Output:
+	// USER=gopher
+	// GOPATH not set
+}
+
+func ExampleGetenv() {
+	fmt.Printf("%s lives in %s.\n", os.Getenv("USER"), os.Getenv("HOME"))
+
+	// Output:
+	// gopher lives in /usr/gopher.
+}
+
+func ExampleUnsetenv() {
+	os.Setenv("TMPDIR", "/my/tmp")
+	defer os.Unsetenv("TMPDIR")
+}
diff --git a/src/os/exec.go b/src/os/exec.go
index bf32498..8a53e5d 100644
--- a/src/os/exec.go
+++ b/src/os/exec.go
@@ -9,6 +9,7 @@ import (
 	"sync"
 	"sync/atomic"
 	"syscall"
+	"time"
 )
 
 // Process stores the information about a process created by StartProcess.
@@ -70,3 +71,89 @@ func Getpid() int { return syscall.Getpid() }
 
 // Getppid returns the process id of the caller's parent.
 func Getppid() int { return syscall.Getppid() }
+
+// FindProcess looks for a running process by its pid.
+//
+// The Process it returns can be used to obtain information
+// about the underlying operating system process.
+//
+// On Unix systems, FindProcess always succeeds and returns a Process
+// for the given pid, regardless of whether the process exists.
+func FindProcess(pid int) (*Process, error) {
+	return findProcess(pid)
+}
+
+// StartProcess starts a new process with the program, arguments and attributes
+// specified by name, argv and attr.
+//
+// StartProcess is a low-level interface. The os/exec package provides
+// higher-level interfaces.
+//
+// If there is an error, it will be of type *PathError.
+func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) {
+	return startProcess(name, argv, attr)
+}
+
+// Release releases any resources associated with the Process p,
+// rendering it unusable in the future.
+// Release only needs to be called if Wait is not.
+func (p *Process) Release() error {
+	return p.release()
+}
+
+// Kill causes the Process to exit immediately.
+func (p *Process) Kill() error {
+	return p.kill()
+}
+
+// Wait waits for the Process to exit, and then returns a
+// ProcessState describing its status and an error, if any.
+// Wait releases any resources associated with the Process.
+// On most operating systems, the Process must be a child
+// of the current process or an error will be returned.
+func (p *Process) Wait() (*ProcessState, error) {
+	return p.wait()
+}
+
+// Signal sends a signal to the Process.
+// Sending Interrupt on Windows is not implemented.
+func (p *Process) Signal(sig Signal) error {
+	return p.signal(sig)
+}
+
+// UserTime returns the user CPU time of the exited process and its children.
+func (p *ProcessState) UserTime() time.Duration {
+	return p.userTime()
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+func (p *ProcessState) SystemTime() time.Duration {
+	return p.systemTime()
+}
+
+// Exited reports whether the program has exited.
+func (p *ProcessState) Exited() bool {
+	return p.exited()
+}
+
+// Success reports whether the program exited successfully,
+// such as with exit status 0 on Unix.
+func (p *ProcessState) Success() bool {
+	return p.success()
+}
+
+// Sys returns system-dependent exit information about
+// the process. Convert it to the appropriate underlying
+// type, such as syscall.WaitStatus on Unix, to access its contents.
+func (p *ProcessState) Sys() interface{} {
+	return p.sys()
+}
+
+// SysUsage returns system-dependent resource usage information about
+// the exited process. Convert it to the appropriate underlying
+// type, such as *syscall.Rusage on Unix, to access its contents.
+// (On Unix, *syscall.Rusage matches struct rusage as defined in the
+// getrusage(2) manual page.)
+func (p *ProcessState) SysUsage() interface{} {
+	return p.sysUsage()
+}
diff --git a/src/os/exec/example_test.go b/src/os/exec/example_test.go
index 55eaac8..5ccb21a 100644
--- a/src/os/exec/example_test.go
+++ b/src/os/exec/example_test.go
@@ -6,11 +6,15 @@ package exec_test
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"fmt"
+	"io"
+	"io/ioutil"
 	"log"
 	"os/exec"
 	"strings"
+	"time"
 )
 
 func ExampleLookPath() {
@@ -73,3 +77,61 @@ func ExampleCmd_StdoutPipe() {
 	}
 	fmt.Printf("%s is %d years old\n", person.Name, person.Age)
 }
+
+func ExampleCmd_StdinPipe() {
+	cmd := exec.Command("cat")
+	stdin, err := cmd.StdinPipe()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	go func() {
+		defer stdin.Close()
+		io.WriteString(stdin, "values written to stdin are passed to cmd's standard input")
+	}()
+
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	fmt.Printf("%s\n", out)
+}
+
+func ExampleCmd_StderrPipe() {
+	cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr")
+	stderr, err := cmd.StderrPipe()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if err := cmd.Start(); err != nil {
+		log.Fatal(err)
+	}
+
+	slurp, _ := ioutil.ReadAll(stderr)
+	fmt.Printf("%s\n", slurp)
+
+	if err := cmd.Wait(); err != nil {
+		log.Fatal(err)
+	}
+}
+
+func ExampleCmd_CombinedOutput() {
+	cmd := exec.Command("sh", "-c", "echo stdout; echo 1>&2 stderr")
+	stdoutStderr, err := cmd.CombinedOutput()
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Printf("%s\n", stdoutStderr)
+}
+
+func ExampleCommandContext() {
+	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
+	defer cancel()
+
+	if err := exec.CommandContext(ctx, "sleep", "5").Run(); err != nil {
+		// This will fail after 100 milliseconds. The 5 second sleep
+		// will be interrupted.
+	}
+}
diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go
index d2c1b17..c4c5168 100644
--- a/src/os/exec/exec.go
+++ b/src/os/exec/exec.go
@@ -120,12 +120,13 @@ type Cmd struct {
 // It sets only the Path and Args in the returned structure.
 //
 // If name contains no path separators, Command uses LookPath to
-// resolve the path to a complete name if possible. Otherwise it uses
-// name directly.
+// resolve name to a complete path if possible. Otherwise it uses name
+// directly as Path.
 //
 // The returned Cmd's Args field is constructed from the command name
 // followed by the elements of arg, so arg should not include the
-// command name itself. For example, Command("echo", "hello")
+// command name itself. For example, Command("echo", "hello").
+// Args[0] is always name, not the possibly resolved Path.
 func Command(name string, arg ...string) *Cmd {
 	cmd := &Cmd{
 		Path: name,
@@ -515,15 +516,16 @@ func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
 	c.Stdin = pr
 	c.closeAfterStart = append(c.closeAfterStart, pr)
 	wc := &closeOnce{File: pw}
-	c.closeAfterWait = append(c.closeAfterWait, wc)
+	c.closeAfterWait = append(c.closeAfterWait, closerFunc(wc.safeClose))
 	return wc, nil
 }
 
 type closeOnce struct {
 	*os.File
 
-	once sync.Once
-	err  error
+	writers sync.RWMutex // coordinate safeClose and Write
+	once    sync.Once
+	err     error
 }
 
 func (c *closeOnce) Close() error {
@@ -535,6 +537,55 @@ func (c *closeOnce) close() {
 	c.err = c.File.Close()
 }
 
+type closerFunc func() error
+
+func (f closerFunc) Close() error { return f() }
+
+// safeClose closes c being careful not to race with any calls to c.Write.
+// See golang.org/issue/9307 and TestEchoFileRace in exec_test.go.
+// In theory other calls could also be excluded (by writing appropriate
+// wrappers like c.Write's implementation below), but since c is most
+// commonly used as a WriteCloser, Write is the main one to worry about.
+// See also #7970, for which this is a partial fix for this specific instance.
+// The idea is that we return a WriteCloser, and so the caller can be
+// relied upon not to call Write and Close simultaneously, but it's less
+// obvious that cmd.Wait calls Close and that the caller must not call
+// Write and cmd.Wait simultaneously. In fact that seems too onerous.
+// So we change the use of Close in cmd.Wait to use safeClose, which will
+// synchronize with any Write.
+//
+// It's important that we know this won't block forever waiting for the
+// operations being excluded. At the point where this is called,
+// the invoked command has exited and the parent copy of the read side
+// of the pipe has also been closed, so there should really be no read side
+// of the pipe left. Any active writes should return very shortly with an EPIPE,
+// making it reasonable to wait for them.
+// Technically it is possible that the child forked a sub-process or otherwise
+// handed off the read side of the pipe before exiting and the current holder
+// is not reading from the pipe, and the pipe is full, in which case the close here
+// might block waiting for the write to complete. That's probably OK.
+// It's a small enough problem to be outweighed by eliminating the race here.
+func (c *closeOnce) safeClose() error {
+	c.writers.Lock()
+	err := c.Close()
+	c.writers.Unlock()
+	return err
+}
+
+func (c *closeOnce) Write(b []byte) (int, error) {
+	c.writers.RLock()
+	n, err := c.File.Write(b)
+	c.writers.RUnlock()
+	return n, err
+}
+
+func (c *closeOnce) WriteString(s string) (int, error) {
+	c.writers.RLock()
+	n, err := c.File.WriteString(s)
+	c.writers.RUnlock()
+	return n, err
+}
+
 // StdoutPipe returns a pipe that will be connected to the command's
 // standard output when the command starts.
 //
diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go
index 4cc9847..d3ac7ab 100644
--- a/src/os/exec/exec_test.go
+++ b/src/os/exec/exec_test.go
@@ -101,6 +101,26 @@ func TestCatStdin(t *testing.T) {
 	}
 }
 
+func TestEchoFileRace(t *testing.T) {
+	cmd := helperCommand(t, "echo")
+	stdin, err := cmd.StdinPipe()
+	if err != nil {
+		t.Fatalf("StdinPipe: %v", err)
+	}
+	if err := cmd.Start(); err != nil {
+		t.Fatalf("Start: %v", err)
+	}
+	wrote := make(chan bool)
+	go func() {
+		defer close(wrote)
+		fmt.Fprint(stdin, "echo\n")
+	}()
+	if err := cmd.Wait(); err != nil {
+		t.Fatalf("Wait: %v", err)
+	}
+	<-wrote
+}
+
 func TestCatGoodAndBadFile(t *testing.T) {
 	// Testing combined output and error values.
 	bs, err := helperCommand(t, "cat", "/bogus/file.foo", "exec_test.go").CombinedOutput()
@@ -226,6 +246,32 @@ func TestStdinClose(t *testing.T) {
 	check("Wait", cmd.Wait())
 }
 
+// Issue 17647.
+func TestStdinCloseRace(t *testing.T) {
+	cmd := helperCommand(t, "stdinClose")
+	stdin, err := cmd.StdinPipe()
+	if err != nil {
+		t.Fatalf("StdinPipe: %v", err)
+	}
+	if err := cmd.Start(); err != nil {
+		t.Fatalf("Start: %v", err)
+	}
+	go func() {
+		if err := cmd.Process.Kill(); err != nil {
+			t.Errorf("Kill: %v", err)
+		}
+	}()
+	go func() {
+		io.Copy(stdin, strings.NewReader(stdinCloseTestString))
+		if err := stdin.Close(); err != nil {
+			t.Errorf("stdin.Close: %v", err)
+		}
+	}()
+	if err := cmd.Wait(); err == nil {
+		t.Fatalf("Wait: succeeded unexpectedly")
+	}
+}
+
 // Issue 5071
 func TestPipeLookPathLeak(t *testing.T) {
 	fd0, lsof0 := numOpenFDS(t)
@@ -412,7 +458,7 @@ func TestExtraFilesFDShuffle(t *testing.T) {
 		buf := make([]byte, 512)
 		n, err := stderr.Read(buf)
 		if err != nil {
-			t.Fatalf("Read: %s", err)
+			t.Errorf("Read: %s", err)
 			ch <- err.Error()
 		} else {
 			ch <- string(buf[:n])
@@ -938,7 +984,7 @@ func TestContextCancel(t *testing.T) {
 	}
 
 	if err := w.Close(); err != nil {
-		t.Error("error closing write end of pipe: %v", err)
+		t.Errorf("error closing write end of pipe: %v", err)
 	}
 	<-readDone
 
diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go
index 72b5a93..d89db20 100644
--- a/src/os/exec_windows.go
+++ b/src/os/exec_windows.go
@@ -63,7 +63,9 @@ func (p *Process) signal(sig Signal) error {
 		return errors.New("os: process already finished")
 	}
 	if sig == Kill {
-		return terminateProcess(p.Pid, 1)
+		err := terminateProcess(p.Pid, 1)
+		runtime.KeepAlive(p)
+		return err
 	}
 	// TODO(rsc): Handle Interrupt too?
 	return syscall.Errno(syscall.EWINDOWS)
diff --git a/src/os/executable.go b/src/os/executable.go
new file mode 100644
index 0000000..8c21246
--- /dev/null
+++ b/src/os/executable.go
@@ -0,0 +1,23 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+// Executable returns the path name for the executable that started
+// the current process. There is no guarantee that the path is still
+// pointing to the correct executable. If a symlink was used to start
+// the process, depending on the operating system, the result might
+// be the symlink or the path it pointed to. If a stable result is
+// needed, path/filepath.EvalSymlinks might help.
+//
+// Executable returns an absolute path unless an error occurred.
+//
+// The main use case is finding resources located relative to an
+// executable.
+//
+// Executable is not supported on nacl or OpenBSD (unless procfs is
+// mounted.)
+func Executable() (string, error) {
+	return executable()
+}
diff --git a/src/os/executable_darwin.go b/src/os/executable_darwin.go
new file mode 100644
index 0000000..ce5b814
--- /dev/null
+++ b/src/os/executable_darwin.go
@@ -0,0 +1,24 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+var executablePath string // set by ../runtime/os_darwin.go
+
+var initCwd, initCwdErr = Getwd()
+
+func executable() (string, error) {
+	ep := executablePath
+	if ep[0] != '/' {
+		if initCwdErr != nil {
+			return ep, initCwdErr
+		}
+		if len(ep) > 2 && ep[0:2] == "./" {
+			// skip "./"
+			ep = ep[2:]
+		}
+		ep = initCwd + "/" + ep
+	}
+	return ep, nil
+}
diff --git a/src/os/executable_freebsd.go b/src/os/executable_freebsd.go
new file mode 100644
index 0000000..ccaf8e6
--- /dev/null
+++ b/src/os/executable_freebsd.go
@@ -0,0 +1,33 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+func executable() (string, error) {
+	mib := [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
+
+	n := uintptr(0)
+	// get length
+	_, _, err := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
+	if err != 0 {
+		return "", err
+	}
+	if n == 0 { // shouldn't happen
+		return "", nil
+	}
+	buf := make([]byte, n)
+	_, _, err = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
+	if err != 0 {
+		return "", err
+	}
+	if n == 0 { // shouldn't happen
+		return "", nil
+	}
+	return string(buf[:n-1]), nil
+}
diff --git a/src/os/executable_plan9.go b/src/os/executable_plan9.go
new file mode 100644
index 0000000..a5947ea
--- /dev/null
+++ b/src/os/executable_plan9.go
@@ -0,0 +1,19 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build plan9
+
+package os
+
+import "syscall"
+
+func executable() (string, error) {
+	fn := "/proc/" + itoa(Getpid()) + "/text"
+	f, err := Open(fn)
+	if err != nil {
+		return "", err
+	}
+	defer f.Close()
+	return syscall.Fd2path(int(f.Fd()))
+}
diff --git a/src/os/executable_procfs.go b/src/os/executable_procfs.go
new file mode 100644
index 0000000..69a70e1
--- /dev/null
+++ b/src/os/executable_procfs.go
@@ -0,0 +1,36 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux netbsd openbsd dragonfly nacl
+
+package os
+
+import (
+	"errors"
+	"runtime"
+)
+
+// We query the executable path at init time to avoid the problem of
+// readlink returns a path appended with " (deleted)" when the original
+// binary gets deleted.
+var executablePath, executablePathErr = func() (string, error) {
+	var procfn string
+	switch runtime.GOOS {
+	default:
+		return "", errors.New("Executable not implemented for " + runtime.GOOS)
+	case "linux", "android":
+		procfn = "/proc/self/exe"
+	case "netbsd":
+		procfn = "/proc/curproc/exe"
+	case "openbsd":
+		procfn = "/proc/curproc/file"
+	case "dragonfly":
+		procfn = "/proc/curproc/file"
+	}
+	return Readlink(procfn)
+}()
+
+func executable() (string, error) {
+	return executablePath, executablePathErr
+}
diff --git a/src/os/executable_solaris.go b/src/os/executable_solaris.go
new file mode 100644
index 0000000..80f9372
--- /dev/null
+++ b/src/os/executable_solaris.go
@@ -0,0 +1,27 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import "syscall"
+
+var initCwd, initCwdErr = Getwd()
+
+func executable() (string, error) {
+	path, err := syscall.Getexecname()
+	if err != nil {
+		return path, err
+	}
+	if len(path) > 0 && path[0] != '/' {
+		if initCwdErr != nil {
+			return path, initCwdErr
+		}
+		if len(path) > 2 && path[0:2] == "./" {
+			// skip "./"
+			path = path[2:]
+		}
+		return initCwd + "/" + path, nil
+	}
+	return path, nil
+}
diff --git a/src/os/executable_test.go b/src/os/executable_test.go
new file mode 100644
index 0000000..a4d8909
--- /dev/null
+++ b/src/os/executable_test.go
@@ -0,0 +1,87 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os_test
+
+import (
+	"fmt"
+	"internal/testenv"
+	"os"
+	osexec "os/exec"
+	"path/filepath"
+	"runtime"
+	"testing"
+)
+
+const executable_EnvVar = "OSTEST_OUTPUT_EXECPATH"
+
+func TestExecutable(t *testing.T) {
+	testenv.MustHaveExec(t) // will also execlude nacl, which doesn't support Executable anyway
+	ep, err := os.Executable()
+	if err != nil {
+		switch goos := runtime.GOOS; goos {
+		case "openbsd": // procfs is not mounted by default
+			t.Skipf("Executable failed on %s: %v, expected", goos, err)
+		}
+		t.Fatalf("Executable failed: %v", err)
+	}
+	// we want fn to be of the form "dir/prog"
+	dir := filepath.Dir(filepath.Dir(ep))
+	fn, err := filepath.Rel(dir, ep)
+	if err != nil {
+		t.Fatalf("filepath.Rel: %v", err)
+	}
+	cmd := &osexec.Cmd{}
+	// make child start with a relative program path
+	cmd.Dir = dir
+	cmd.Path = fn
+	// forge argv[0] for child, so that we can verify we could correctly
+	// get real path of the executable without influenced by argv[0].
+	cmd.Args = []string{"-", "-test.run=XXXX"}
+	cmd.Env = append(os.Environ(), fmt.Sprintf("%s=1", executable_EnvVar))
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("exec(self) failed: %v", err)
+	}
+	outs := string(out)
+	if !filepath.IsAbs(outs) {
+		t.Fatalf("Child returned %q, want an absolute path", out)
+	}
+	if !sameFile(outs, ep) {
+		t.Fatalf("Child returned %q, not the same file as %q", out, ep)
+	}
+}
+
+func sameFile(fn1, fn2 string) bool {
+	fi1, err := os.Stat(fn1)
+	if err != nil {
+		return false
+	}
+	fi2, err := os.Stat(fn2)
+	if err != nil {
+		return false
+	}
+	return os.SameFile(fi1, fi2)
+}
+
+func init() {
+	if e := os.Getenv(executable_EnvVar); e != "" {
+		// first chdir to another path
+		dir := "/"
+		if runtime.GOOS == "windows" {
+			cwd, err := os.Getwd()
+			if err != nil {
+				panic(err)
+			}
+			dir = filepath.VolumeName(cwd)
+		}
+		os.Chdir(dir)
+		if ep, err := os.Executable(); err != nil {
+			fmt.Fprint(os.Stderr, "ERROR: ", err)
+		} else {
+			fmt.Fprint(os.Stderr, ep)
+		}
+		os.Exit(0)
+	}
+}
diff --git a/src/os/executable_windows.go b/src/os/executable_windows.go
new file mode 100644
index 0000000..fc5cf86
--- /dev/null
+++ b/src/os/executable_windows.go
@@ -0,0 +1,32 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import (
+	"internal/syscall/windows"
+	"syscall"
+)
+
+func getModuleFileName(handle syscall.Handle) (string, error) {
+	n := uint32(1024)
+	var buf []uint16
+	for {
+		buf = make([]uint16, n)
+		r, err := windows.GetModuleFileName(handle, &buf[0], n)
+		if err != nil {
+			return "", err
+		}
+		if r < n {
+			break
+		}
+		// r == n means n not big enough
+		n += 1024
+	}
+	return syscall.UTF16ToString(buf), nil
+}
+
+func executable() (string, error) {
+	return getModuleFileName(0)
+}
diff --git a/src/os/export_windows_test.go b/src/os/export_windows_test.go
new file mode 100644
index 0000000..3bb2d20
--- /dev/null
+++ b/src/os/export_windows_test.go
@@ -0,0 +1,13 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+// Export for testing.
+
+var (
+	FixLongPath     = fixLongPath
+	NewConsoleFile  = newConsoleFile
+	ReadConsoleFunc = &readConsole
+)
diff --git a/src/os/file.go b/src/os/file.go
index e546441..de245c5 100644
--- a/src/os/file.go
+++ b/src/os/file.go
@@ -92,11 +92,11 @@ func (e *LinkError) Error() string {
 }
 
 // Read reads up to len(b) bytes from the File.
-// It returns the number of bytes read and an error, if any.
-// EOF is signaled by a zero count with err set to io.EOF.
+// It returns the number of bytes read and any error encountered.
+// At end of file, Read returns 0, io.EOF.
 func (f *File) Read(b []byte) (n int, err error) {
-	if f == nil {
-		return 0, ErrInvalid
+	if err := f.checkValid("read"); err != nil {
+		return 0, err
 	}
 	n, e := f.read(b)
 	if n == 0 && len(b) > 0 && e == nil {
@@ -113,8 +113,8 @@ func (f *File) Read(b []byte) (n int, err error) {
 // ReadAt always returns a non-nil error when n < len(b).
 // At end of file, that error is io.EOF.
 func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
-	if f == nil {
-		return 0, ErrInvalid
+	if err := f.checkValid("read"); err != nil {
+		return 0, err
 	}
 	for len(b) > 0 {
 		m, e := f.pread(b, off)
@@ -136,8 +136,8 @@ func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
 // It returns the number of bytes written and an error, if any.
 // Write returns a non-nil error when n != len(b).
 func (f *File) Write(b []byte) (n int, err error) {
-	if f == nil {
-		return 0, ErrInvalid
+	if err := f.checkValid("write"); err != nil {
+		return 0, err
 	}
 	n, e := f.write(b)
 	if n < 0 {
@@ -159,8 +159,8 @@ func (f *File) Write(b []byte) (n int, err error) {
 // It returns the number of bytes written and an error, if any.
 // WriteAt returns a non-nil error when n != len(b).
 func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
-	if f == nil {
-		return 0, ErrInvalid
+	if err := f.checkValid("write"); err != nil {
+		return 0, err
 	}
 	for len(b) > 0 {
 		m, e := f.pwrite(b, off)
@@ -181,8 +181,8 @@ func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
 // It returns the new offset and an error, if any.
 // The behavior of Seek on a file opened with O_APPEND is not specified.
 func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
-	if f == nil {
-		return 0, ErrInvalid
+	if err := f.checkValid("seek"); err != nil {
+		return 0, err
 	}
 	r, e := f.seek(offset, whence)
 	if e == nil && f.dirinfo != nil && r != 0 {
@@ -197,16 +197,13 @@ func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
 // WriteString is like Write, but writes the contents of string s rather than
 // a slice of bytes.
 func (f *File) WriteString(s string) (n int, err error) {
-	if f == nil {
-		return 0, ErrInvalid
-	}
 	return f.Write([]byte(s))
 }
 
 // Mkdir creates a new directory with the specified name and permission bits.
 // If there is an error, it will be of type *PathError.
 func Mkdir(name string, perm FileMode) error {
-	e := syscall.Mkdir(name, syscallMode(perm))
+	e := syscall.Mkdir(fixLongPath(name), syscallMode(perm))
 
 	if e != nil {
 		return &PathError{"mkdir", name, e}
@@ -233,8 +230,8 @@ func Chdir(dir string) error {
 // which must be a directory.
 // If there is an error, it will be of type *PathError.
 func (f *File) Chdir() error {
-	if f == nil {
-		return ErrInvalid
+	if err := f.checkValid("chdir"); err != nil {
+		return err
 	}
 	if e := syscall.Fchdir(f.fd); e != nil {
 		return &PathError{"chdir", f.name, e}
@@ -278,3 +275,15 @@ func fixCount(n int, err error) (int, error) {
 	}
 	return n, err
 }
+
+// checkValid checks whether f is valid for use.
+// If not, it returns an appropriate error, perhaps incorporating the operation name op.
+func (f *File) checkValid(op string) error {
+	if f == nil {
+		return ErrInvalid
+	}
+	if f.fd == badFd {
+		return &PathError{op, f.name, ErrClosed}
+	}
+	return nil
+}
diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go
index 9edb6bc..5276a7e 100644
--- a/src/os/file_plan9.go
+++ b/src/os/file_plan9.go
@@ -11,9 +11,9 @@ import (
 	"time"
 )
 
-// File represents an open file descriptor.
-type File struct {
-	*file
+// fixLongPath is a noop on non-Windows platforms.
+func fixLongPath(path string) string {
+	return path
 }
 
 // file is the real representation of *File.
@@ -135,21 +135,21 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) {
 // Close closes the File, rendering it unusable for I/O.
 // It returns an error, if any.
 func (f *File) Close() error {
-	if f == nil {
-		return ErrInvalid
+	if err := f.checkValid("close"); err != nil {
+		return err
 	}
 	return f.file.close()
 }
 
 func (file *file) close() error {
-	if file == nil || file.fd < 0 {
+	if file == nil || file.fd == badFd {
 		return ErrInvalid
 	}
 	var err error
 	if e := syscall.Close(file.fd); e != nil {
 		err = &PathError{"close", file.name, e}
 	}
-	file.fd = -1 // so it can't be closed again
+	file.fd = badFd // so it can't be closed again
 
 	// no need for a finalizer anymore
 	runtime.SetFinalizer(file, nil)
diff --git a/src/os/file_posix.go b/src/os/file_posix.go
index 6d8076f..d817f34 100644
--- a/src/os/file_posix.go
+++ b/src/os/file_posix.go
@@ -18,7 +18,7 @@ func sigpipe() // implemented in package runtime
 func Readlink(name string) (string, error) {
 	for len := 128; ; len *= 2 {
 		b := make([]byte, len)
-		n, e := fixCount(syscall.Readlink(name, b))
+		n, e := fixCount(syscall.Readlink(fixLongPath(name), b))
 		if e != nil {
 			return "", &PathError{"readlink", name, e}
 		}
@@ -57,8 +57,8 @@ func Chmod(name string, mode FileMode) error {
 // Chmod changes the mode of the file to mode.
 // If there is an error, it will be of type *PathError.
 func (f *File) Chmod(mode FileMode) error {
-	if f == nil {
-		return ErrInvalid
+	if err := f.checkValid("chmod"); err != nil {
+		return err
 	}
 	if e := syscall.Fchmod(f.fd, syscallMode(mode)); e != nil {
 		return &PathError{"chmod", f.name, e}
@@ -89,8 +89,8 @@ func Lchown(name string, uid, gid int) error {
 // Chown changes the numeric uid and gid of the named file.
 // If there is an error, it will be of type *PathError.
 func (f *File) Chown(uid, gid int) error {
-	if f == nil {
-		return ErrInvalid
+	if err := f.checkValid("chown"); err != nil {
+		return err
 	}
 	if e := syscall.Fchown(f.fd, uid, gid); e != nil {
 		return &PathError{"chown", f.name, e}
@@ -102,8 +102,8 @@ func (f *File) Chown(uid, gid int) error {
 // It does not change the I/O offset.
 // If there is an error, it will be of type *PathError.
 func (f *File) Truncate(size int64) error {
-	if f == nil {
-		return ErrInvalid
+	if err := f.checkValid("truncate"); err != nil {
+		return err
 	}
 	if e := syscall.Ftruncate(f.fd, size); e != nil {
 		return &PathError{"truncate", f.name, e}
@@ -115,11 +115,11 @@ func (f *File) Truncate(size int64) error {
 // Typically, this means flushing the file system's in-memory copy
 // of recently written data to disk.
 func (f *File) Sync() error {
-	if f == nil {
-		return ErrInvalid
+	if err := f.checkValid("sync"); err != nil {
+		return err
 	}
 	if e := syscall.Fsync(f.fd); e != nil {
-		return NewSyscallError("fsync", e)
+		return &PathError{"sync", f.name, e}
 	}
 	return nil
 }
@@ -134,7 +134,7 @@ func Chtimes(name string, atime time.Time, mtime time.Time) error {
 	var utimes [2]syscall.Timespec
 	utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
 	utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
-	if e := syscall.UtimesNano(name, utimes[0:]); e != nil {
+	if e := syscall.UtimesNano(fixLongPath(name), utimes[0:]); e != nil {
 		return &PathError{"chtimes", name, e}
 	}
 	return nil
diff --git a/src/os/file_unix.go b/src/os/file_unix.go
index 9b64f21..1cff93a 100644
--- a/src/os/file_unix.go
+++ b/src/os/file_unix.go
@@ -11,11 +11,16 @@ import (
 	"syscall"
 )
 
-func sameFile(fs1, fs2 *fileStat) bool {
-	return fs1.sys.Dev == fs2.sys.Dev && fs1.sys.Ino == fs2.sys.Ino
+// fixLongPath is a noop on non-Windows platforms.
+func fixLongPath(path string) string {
+	return path
 }
 
 func rename(oldname, newname string) error {
+	fi, err := Lstat(newname)
+	if err == nil && fi.IsDir() {
+		return &LinkError{"rename", oldname, newname, syscall.EEXIST}
+	}
 	e := syscall.Rename(oldname, newname)
 	if e != nil {
 		return &LinkError{"rename", oldname, newname, e}
@@ -23,11 +28,6 @@ func rename(oldname, newname string) error {
 	return nil
 }
 
-// File represents an open file descriptor.
-type File struct {
-	*file
-}
-
 // file is the real representation of *File.
 // The extra level of indirection ensures that no clients of os
 // can overwrite this data, which could cause the finalizer
@@ -133,7 +133,7 @@ func (f *File) Close() error {
 }
 
 func (file *file) close() error {
-	if file == nil || file.fd < 0 {
+	if file == nil || file.fd == badFd {
 		return syscall.EINVAL
 	}
 	var err error
@@ -147,69 +147,6 @@ func (file *file) close() error {
 	return err
 }
 
-// Stat returns the FileInfo structure describing file.
-// If there is an error, it will be of type *PathError.
-func (f *File) Stat() (FileInfo, error) {
-	if f == nil {
-		return nil, ErrInvalid
-	}
-	var fs fileStat
-	err := syscall.Fstat(f.fd, &fs.sys)
-	if err != nil {
-		return nil, &PathError{"stat", f.name, err}
-	}
-	fillFileStatFromSys(&fs, f.name)
-	return &fs, nil
-}
-
-// Stat returns a FileInfo describing the named file.
-// If there is an error, it will be of type *PathError.
-func Stat(name string) (FileInfo, error) {
-	var fs fileStat
-	err := syscall.Stat(name, &fs.sys)
-	if err != nil {
-		return nil, &PathError{"stat", name, err}
-	}
-	fillFileStatFromSys(&fs, name)
-	return &fs, nil
-}
-
-// Lstat returns a FileInfo describing the named file.
-// If the file is a symbolic link, the returned FileInfo
-// describes the symbolic link. Lstat makes no attempt to follow the link.
-// If there is an error, it will be of type *PathError.
-func Lstat(name string) (FileInfo, error) {
-	var fs fileStat
-	err := syscall.Lstat(name, &fs.sys)
-	if err != nil {
-		return nil, &PathError{"lstat", name, err}
-	}
-	fillFileStatFromSys(&fs, name)
-	return &fs, nil
-}
-
-func (f *File) readdir(n int) (fi []FileInfo, err error) {
-	dirname := f.name
-	if dirname == "" {
-		dirname = "."
-	}
-	names, err := f.Readdirnames(n)
-	fi = make([]FileInfo, 0, len(names))
-	for _, filename := range names {
-		fip, lerr := lstat(dirname + "/" + filename)
-		if IsNotExist(lerr) {
-			// File disappeared between readdir + stat.
-			// Just treat it as if it didn't exist.
-			continue
-		}
-		if lerr != nil {
-			return fi, lerr
-		}
-		fi = append(fi, fip)
-	}
-	return fi, err
-}
-
 // Darwin and FreeBSD can't read or write 2GB+ at a time,
 // even on 64-bit systems. See golang.org/issue/7812.
 // Use 1GB instead of, say, 2GB-1, to keep subsequent
@@ -324,24 +261,6 @@ func Remove(name string) error {
 	return &PathError{"remove", name, e}
 }
 
-// basename removes trailing slashes and the leading directory name from path name
-func basename(name string) string {
-	i := len(name) - 1
-	// Remove trailing slashes
-	for ; i > 0 && name[i] == '/'; i-- {
-		name = name[:i]
-	}
-	// Remove leading directory name
-	for i--; i >= 0; i-- {
-		if name[i] == '/' {
-			name = name[i+1:]
-			break
-		}
-	}
-
-	return name
-}
-
 // TempDir returns the default directory to use for temporary files.
 func TempDir() string {
 	dir := Getenv("TMPDIR")
diff --git a/src/os/file_windows.go b/src/os/file_windows.go
index f470fc4..97be324 100644
--- a/src/os/file_windows.go
+++ b/src/os/file_windows.go
@@ -15,11 +15,6 @@ import (
 	"unsafe"
 )
 
-// File represents an open file descriptor.
-type File struct {
-	*file
-}
-
 // file is the real representation of *File.
 // The extra level of indirection ensures that no clients of os
 // can overwrite this data, which could cause the finalizer
@@ -31,9 +26,11 @@ type file struct {
 	l       sync.Mutex // used to implement windows pread/pwrite
 
 	// only for console io
-	isConsole bool
-	lastbits  []byte // first few bytes of the last incomplete rune in last write
-	readbuf   []rune // input console buffer
+	isConsole      bool
+	lastbits       []byte   // first few bytes of the last incomplete rune in last write
+	readuint16     []uint16 // buffer to hold uint16s obtained with ReadConsole
+	readbyte       []byte   // buffer to hold decoding of readuint16 from utf16 to utf8
+	readbyteOffset int      // readbyte[readOffset:] is yet to be consumed with file.Read
 }
 
 // Fd returns the Windows handle referencing the open file.
@@ -49,20 +46,27 @@ func (file *File) Fd() uintptr {
 // Unlike NewFile, it does not check that h is syscall.InvalidHandle.
 func newFile(h syscall.Handle, name string) *File {
 	f := &File{&file{fd: h, name: name}}
-	var m uint32
-	if syscall.GetConsoleMode(f.fd, &m) == nil {
-		f.isConsole = true
-	}
 	runtime.SetFinalizer(f.file, (*file).close)
 	return f
 }
 
+// newConsoleFile creates new File that will be used as console.
+func newConsoleFile(h syscall.Handle, name string) *File {
+	f := newFile(h, name)
+	f.isConsole = true
+	return f
+}
+
 // NewFile returns a new File with the given file descriptor and name.
 func NewFile(fd uintptr, name string) *File {
 	h := syscall.Handle(fd)
 	if h == syscall.InvalidHandle {
 		return nil
 	}
+	var m uint32
+	if syscall.GetConsoleMode(h, &m) == nil {
+		return newConsoleFile(h, name)
+	}
 	return newFile(h, name)
 }
 
@@ -82,7 +86,7 @@ const DevNull = "NUL"
 func (f *file) isdir() bool { return f != nil && f.dirinfo != nil }
 
 func openFile(name string, flag int, perm FileMode) (file *File, err error) {
-	r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
+	r, e := syscall.Open(fixLongPath(name), flag|syscall.O_CLOEXEC, syscallMode(perm))
 	if e != nil {
 		return nil, e
 	}
@@ -91,10 +95,13 @@ func openFile(name string, flag int, perm FileMode) (file *File, err error) {
 
 func openDir(name string) (file *File, err error) {
 	var mask string
-	if len(name) == 2 && name[1] == ':' { // it is a drive letter, like C:
-		mask = name + `*`
+
+	path := fixLongPath(name)
+
+	if len(path) == 2 && path[1] == ':' || (len(path) > 0 && path[len(path)-1] == '\\') { // it is a drive letter, like C:
+		mask = path + `*`
 	} else {
-		mask = name + `\*`
+		mask = path + `\*`
 	}
 	maskp, e := syscall.UTF16PtrFromString(mask)
 	if e != nil {
@@ -110,11 +117,11 @@ func openDir(name string) (file *File, err error) {
 			return nil, e
 		}
 		var fa syscall.Win32FileAttributeData
-		namep, e := syscall.UTF16PtrFromString(name)
+		pathp, e := syscall.UTF16PtrFromString(path)
 		if e != nil {
 			return nil, e
 		}
-		e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
+		e = syscall.GetFileAttributesEx(pathp, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa)))
 		if e != nil {
 			return nil, e
 		}
@@ -123,7 +130,7 @@ func openDir(name string) (file *File, err error) {
 		}
 		d.isempty = true
 	}
-	d.path = name
+	d.path = path
 	if !isAbs(d.path) {
 		d.path, e = syscall.FullPath(d.path)
 		if e != nil {
@@ -189,71 +196,14 @@ func (file *file) close() error {
 	if e != nil {
 		err = &PathError{"close", file.name, e}
 	}
-	file.fd = syscall.InvalidHandle // so it can't be closed again
+	file.fd = badFd // so it can't be closed again
 
 	// no need for a finalizer anymore
 	runtime.SetFinalizer(file, nil)
 	return err
 }
 
-func (file *File) readdir(n int) (fi []FileInfo, err error) {
-	if file == nil {
-		return nil, syscall.EINVAL
-	}
-	if !file.isdir() {
-		return nil, &PathError{"Readdir", file.name, syscall.ENOTDIR}
-	}
-	if !file.dirinfo.isempty && file.fd == syscall.InvalidHandle {
-		return nil, syscall.EINVAL
-	}
-	wantAll := n <= 0
-	size := n
-	if wantAll {
-		n = -1
-		size = 100
-	}
-	fi = make([]FileInfo, 0, size) // Empty with room to grow.
-	d := &file.dirinfo.data
-	for n != 0 && !file.dirinfo.isempty {
-		if file.dirinfo.needdata {
-			e := syscall.FindNextFile(file.fd, d)
-			if e != nil {
-				if e == syscall.ERROR_NO_MORE_FILES {
-					break
-				} else {
-					err = &PathError{"FindNextFile", file.name, e}
-					if !wantAll {
-						fi = nil
-					}
-					return
-				}
-			}
-		}
-		file.dirinfo.needdata = true
-		name := syscall.UTF16ToString(d.FileName[0:])
-		if name == "." || name == ".." { // Useless names
-			continue
-		}
-		f := &fileStat{
-			name: name,
-			sys: syscall.Win32FileAttributeData{
-				FileAttributes: d.FileAttributes,
-				CreationTime:   d.CreationTime,
-				LastAccessTime: d.LastAccessTime,
-				LastWriteTime:  d.LastWriteTime,
-				FileSizeHigh:   d.FileSizeHigh,
-				FileSizeLow:    d.FileSizeLow,
-			},
-			path: file.dirinfo.path + `\` + name,
-		}
-		n--
-		fi = append(fi, f)
-	}
-	if !wantAll && len(fi) == 0 {
-		return fi, io.EOF
-	}
-	return fi, nil
-}
+var readConsole = syscall.ReadConsole // changed for testing
 
 // readConsole reads utf16 characters from console File,
 // encodes them into utf8 and stores them in buffer b.
@@ -262,50 +212,70 @@ func (f *File) readConsole(b []byte) (n int, err error) {
 	if len(b) == 0 {
 		return 0, nil
 	}
-	if len(f.readbuf) == 0 {
-		numBytes := len(b)
-		// Windows  can't read bytes over max of int16.
-		// Some versions of Windows can read even less.
-		// See golang.org/issue/13697.
-		if numBytes > 10000 {
-			numBytes = 10000
+
+	if f.readuint16 == nil {
+		// Note: syscall.ReadConsole fails for very large buffers.
+		// The limit is somewhere around (but not exactly) 16384.
+		// Stay well below.
+		f.readuint16 = make([]uint16, 0, 10000)
+		f.readbyte = make([]byte, 0, 4*cap(f.readuint16))
+	}
+
+	for f.readbyteOffset >= len(f.readbyte) {
+		n := cap(f.readuint16) - len(f.readuint16)
+		if n > len(b) {
+			n = len(b)
 		}
-		mbytes := make([]byte, numBytes)
-		var nmb uint32
-		err := syscall.ReadFile(f.fd, mbytes, &nmb, nil)
+		var nw uint32
+		err := readConsole(f.fd, &f.readuint16[:len(f.readuint16)+1][len(f.readuint16)], uint32(n), &nw, nil)
 		if err != nil {
 			return 0, err
 		}
-		if nmb > 0 {
-			var pmb *byte
-			if len(b) > 0 {
-				pmb = &mbytes[0]
-			}
-			acp := windows.GetACP()
-			nwc, err := windows.MultiByteToWideChar(acp, 2, pmb, int32(nmb), nil, 0)
-			if err != nil {
-				return 0, err
-			}
-			wchars := make([]uint16, nwc)
-			pwc := &wchars[0]
-			nwc, err = windows.MultiByteToWideChar(acp, 2, pmb, int32(nmb), pwc, nwc)
-			if err != nil {
-				return 0, err
+		uint16s := f.readuint16[:len(f.readuint16)+int(nw)]
+		f.readuint16 = f.readuint16[:0]
+		buf := f.readbyte[:0]
+		for i := 0; i < len(uint16s); i++ {
+			r := rune(uint16s[i])
+			if utf16.IsSurrogate(r) {
+				if i+1 == len(uint16s) {
+					if nw > 0 {
+						// Save half surrogate pair for next time.
+						f.readuint16 = f.readuint16[:1]
+						f.readuint16[0] = uint16(r)
+						break
+					}
+					r = utf8.RuneError
+				} else {
+					r = utf16.DecodeRune(r, rune(uint16s[i+1]))
+					if r != utf8.RuneError {
+						i++
+					}
+				}
 			}
-			f.readbuf = utf16.Decode(wchars[:nwc])
+			n := utf8.EncodeRune(buf[len(buf):cap(buf)], r)
+			buf = buf[:len(buf)+n]
+		}
+		f.readbyte = buf
+		f.readbyteOffset = 0
+		if nw == 0 {
+			break
 		}
 	}
-	for i, r := range f.readbuf {
-		if utf8.RuneLen(r) > len(b) {
-			f.readbuf = f.readbuf[i:]
-			return n, nil
+
+	src := f.readbyte[f.readbyteOffset:]
+	var i int
+	for i = 0; i < len(src) && i < len(b); i++ {
+		x := src[i]
+		if x == 0x1A { // Ctrl-Z
+			if i == 0 {
+				f.readbyteOffset++
+			}
+			break
 		}
-		nr := utf8.EncodeRune(b, r)
-		b = b[nr:]
-		n += nr
+		b[i] = x
 	}
-	f.readbuf = nil
-	return n, nil
+	f.readbyteOffset += i
+	return i, nil
 }
 
 // read reads up to len(b) bytes from the File.
@@ -450,7 +420,7 @@ func Truncate(name string, size int64) error {
 // Remove removes the named file or directory.
 // If there is an error, it will be of type *PathError.
 func Remove(name string) error {
-	p, e := syscall.UTF16PtrFromString(name)
+	p, e := syscall.UTF16PtrFromString(fixLongPath(name))
 	if e != nil {
 		return &PathError{"remove", name, e}
 	}
@@ -487,7 +457,7 @@ func Remove(name string) error {
 }
 
 func rename(oldname, newname string) error {
-	e := windows.Rename(oldname, newname)
+	e := windows.Rename(fixLongPath(oldname), fixLongPath(newname))
 	if e != nil {
 		return &LinkError{"rename", oldname, newname, e}
 	}
@@ -532,11 +502,11 @@ func TempDir() string {
 // Link creates newname as a hard link to the oldname file.
 // If there is an error, it will be of type *LinkError.
 func Link(oldname, newname string) error {
-	n, err := syscall.UTF16PtrFromString(newname)
+	n, err := syscall.UTF16PtrFromString(fixLongPath(newname))
 	if err != nil {
 		return &LinkError{"link", oldname, newname, err}
 	}
-	o, err := syscall.UTF16PtrFromString(oldname)
+	o, err := syscall.UTF16PtrFromString(fixLongPath(oldname))
 	if err != nil {
 		return &LinkError{"link", oldname, newname, err}
 	}
@@ -567,11 +537,11 @@ func Symlink(oldname, newname string) error {
 	fi, err := Lstat(destpath)
 	isdir := err == nil && fi.IsDir()
 
-	n, err := syscall.UTF16PtrFromString(newname)
+	n, err := syscall.UTF16PtrFromString(fixLongPath(newname))
 	if err != nil {
 		return &LinkError{"symlink", oldname, newname, err}
 	}
-	o, err := syscall.UTF16PtrFromString(oldname)
+	o, err := syscall.UTF16PtrFromString(fixLongPath(oldname))
 	if err != nil {
 		return &LinkError{"symlink", oldname, newname, err}
 	}
@@ -587,41 +557,4 @@ func Symlink(oldname, newname string) error {
 	return nil
 }
 
-func fromSlash(path string) string {
-	// Replace each '/' with '\\' if present
-	var pathbuf []byte
-	var lastSlash int
-	for i, b := range path {
-		if b == '/' {
-			if pathbuf == nil {
-				pathbuf = make([]byte, len(path))
-			}
-			copy(pathbuf[lastSlash:], path[lastSlash:i])
-			pathbuf[i] = '\\'
-			lastSlash = i + 1
-		}
-	}
-	if pathbuf == nil {
-		return path
-	}
-
-	copy(pathbuf[lastSlash:], path[lastSlash:])
-	return string(pathbuf)
-}
-
-func dirname(path string) string {
-	vol := volumeName(path)
-	i := len(path) - 1
-	for i >= len(vol) && !IsPathSeparator(path[i]) {
-		i--
-	}
-	dir := path[len(vol) : i+1]
-	last := len(dir) - 1
-	if last > 0 && IsPathSeparator(dir[last]) {
-		dir = dir[:last]
-	}
-	if dir == "" {
-		dir = "."
-	}
-	return vol + dir
-}
+const badFd = syscall.InvalidHandle
diff --git a/src/os/os_test.go b/src/os/os_test.go
index baa2f07..b1e20b7 100644
--- a/src/os/os_test.go
+++ b/src/os/os_test.go
@@ -25,8 +25,6 @@ import (
 	"time"
 )
 
-var supportsSymlinks = true
-
 var dot = []string{
 	"dir_unix.go",
 	"env.go",
@@ -231,6 +229,28 @@ func TestRead0(t *testing.T) {
 	}
 }
 
+// Reading a closed file should should return ErrClosed error
+func TestReadClosed(t *testing.T) {
+	path := sfdir + "/" + sfname
+	file, err := Open(path)
+	if err != nil {
+		t.Fatal("open failed:", err)
+	}
+	file.Close() // close immediately
+
+	b := make([]byte, 100)
+	_, err = file.Read(b)
+
+	e, ok := err.(*PathError)
+	if !ok {
+		t.Fatalf("Read: %T(%v), want PathError", e, e)
+	}
+
+	if e.Err != ErrClosed {
+		t.Errorf("Read: %v, want PathError(ErrClosed)", e)
+	}
+}
+
 func testReaddirnames(dir string, contents []string, t *testing.T) {
 	file, err := Open(dir)
 	if err != nil {
@@ -580,15 +600,8 @@ func TestReaddirOfFile(t *testing.T) {
 }
 
 func TestHardLink(t *testing.T) {
-	if runtime.GOOS == "plan9" {
-		t.Skip("skipping on plan9, hardlinks not supported")
-	}
-	// From Android release M (Marshmallow), hard linking files is blocked
-	// and an attempt to call link() on a file will return EACCES.
-	// - https://code.google.com/p/android-developer-preview/issues/detail?id=3150
-	if runtime.GOOS == "android" {
-		t.Skip("skipping on android, hardlinks not supported")
-	}
+	testenv.MustHaveLink(t)
+
 	defer chtmpdir(t)()
 	from, to := "hardlinktestfrom", "hardlinktestto"
 	Remove(from) // Just in case.
@@ -652,14 +665,8 @@ func chtmpdir(t *testing.T) func() {
 }
 
 func TestSymlink(t *testing.T) {
-	switch runtime.GOOS {
-	case "android", "nacl", "plan9":
-		t.Skipf("skipping on %s", runtime.GOOS)
-	case "windows":
-		if !supportsSymlinks {
-			t.Skipf("skipping on %s", runtime.GOOS)
-		}
-	}
+	testenv.MustHaveSymlink(t)
+
 	defer chtmpdir(t)()
 	from, to := "symlinktestfrom", "symlinktestto"
 	Remove(from) // Just in case.
@@ -719,14 +726,8 @@ func TestSymlink(t *testing.T) {
 }
 
 func TestLongSymlink(t *testing.T) {
-	switch runtime.GOOS {
-	case "android", "plan9", "nacl":
-		t.Skipf("skipping on %s", runtime.GOOS)
-	case "windows":
-		if !supportsSymlinks {
-			t.Skipf("skipping on %s", runtime.GOOS)
-		}
-	}
+	testenv.MustHaveSymlink(t)
+
 	defer chtmpdir(t)()
 	s := "0123456789abcdef"
 	// Long, but not too long: a common limit is 255.
@@ -842,6 +843,39 @@ func TestRenameFailed(t *testing.T) {
 	}
 }
 
+func TestRenameToDirFailed(t *testing.T) {
+	defer chtmpdir(t)()
+	from, to := "renamefrom", "renameto"
+
+	Remove(from)
+	Remove(to)
+	Mkdir(from, 0777)
+	Mkdir(to, 0777)
+	defer Remove(from)
+	defer Remove(to)
+
+	err := Rename(from, to)
+	switch err := err.(type) {
+	case *LinkError:
+		if err.Op != "rename" {
+			t.Errorf("rename %q, %q: err.Op: want %q, got %q", from, to, "rename", err.Op)
+		}
+		if err.Old != from {
+			t.Errorf("rename %q, %q: err.Old: want %q, got %q", from, to, from, err.Old)
+		}
+		if err.New != to {
+			t.Errorf("rename %q, %q: err.New: want %q, got %q", from, to, to, err.New)
+		}
+	case nil:
+		t.Errorf("rename %q, %q: expected error, got nil", from, to)
+
+		// cleanup whatever was placed in "renameto"
+		Remove(to)
+	default:
+		t.Errorf("rename %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err)
+	}
+}
+
 func exec(t *testing.T, dir, cmd string, args []string, expect string) {
 	r, w, err := Pipe()
 	if err != nil {
@@ -1030,7 +1064,7 @@ func testChtimes(t *testing.T, name string) {
 	}
 
 	if !pmt.Before(mt) {
-		t.Errorf("ModTime didn't go backwards; was=%d, after=%d", mt, pmt)
+		t.Errorf("ModTime didn't go backwards; was=%v, after=%v", mt, pmt)
 	}
 }
 
@@ -1667,6 +1701,61 @@ func TestReadAtEOF(t *testing.T) {
 	}
 }
 
+func TestLongPath(t *testing.T) {
+	tmpdir := newDir("TestLongPath", t)
+	defer func(d string) {
+		if err := RemoveAll(d); err != nil {
+			t.Fatalf("RemoveAll failed: %v", err)
+		}
+	}(tmpdir)
+	for len(tmpdir) < 400 {
+		tmpdir += "/dir3456789"
+	}
+	if err := MkdirAll(tmpdir, 0755); err != nil {
+		t.Fatalf("MkdirAll failed: %v", err)
+	}
+	data := []byte("hello world\n")
+	if err := ioutil.WriteFile(tmpdir+"/foo.txt", data, 0644); err != nil {
+		t.Fatalf("ioutil.WriteFile() failed: %v", err)
+	}
+	if err := Rename(tmpdir+"/foo.txt", tmpdir+"/bar.txt"); err != nil {
+		t.Fatalf("Rename failed: %v", err)
+	}
+	mtime := time.Now().Truncate(time.Minute)
+	if err := Chtimes(tmpdir+"/bar.txt", mtime, mtime); err != nil {
+		t.Fatalf("Chtimes failed: %v", err)
+	}
+	names := []string{"bar.txt"}
+	if testenv.HasSymlink() {
+		if err := Symlink(tmpdir+"/bar.txt", tmpdir+"/symlink.txt"); err != nil {
+			t.Fatalf("Symlink failed: %v", err)
+		}
+		names = append(names, "symlink.txt")
+	}
+	if testenv.HasLink() {
+		if err := Link(tmpdir+"/bar.txt", tmpdir+"/link.txt"); err != nil {
+			t.Fatalf("Link failed: %v", err)
+		}
+		names = append(names, "link.txt")
+	}
+	for _, wantSize := range []int64{int64(len(data)), 0} {
+		for _, name := range names {
+			path := tmpdir + "/" + name
+			dir, err := Stat(path)
+			if err != nil {
+				t.Fatalf("Stat(%q) failed: %v", path, err)
+			}
+			filesize := size(path, t)
+			if dir.Size() != filesize || filesize != wantSize {
+				t.Errorf("Size(%q) is %d, len(ReadFile()) is %d, want %d", path, dir.Size(), filesize, wantSize)
+			}
+		}
+		if err := Truncate(tmpdir+"/bar.txt", 0); err != nil {
+			t.Fatalf("Truncate failed: %v", err)
+		}
+	}
+}
+
 func testKillProcess(t *testing.T, processKiller func(p *Process)) {
 	testenv.MustHaveExec(t)
 
diff --git a/src/os/os_unix_test.go b/src/os/os_unix_test.go
index 5c10154..e239835 100644
--- a/src/os/os_unix_test.go
+++ b/src/os/os_unix_test.go
@@ -7,8 +7,12 @@
 package os_test
 
 import (
+	"io"
+	"io/ioutil"
 	. "os"
+	"path/filepath"
 	"runtime"
+	"strings"
 	"syscall"
 	"testing"
 )
@@ -178,3 +182,38 @@ func TestLchown(t *testing.T) {
 		checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid))
 	}
 }
+
+// Issue 16919: Readdir must return a non-empty slice or an error.
+func TestReaddirRemoveRace(t *testing.T) {
+	oldStat := *LstatP
+	defer func() { *LstatP = oldStat }()
+	*LstatP = func(name string) (FileInfo, error) {
+		if strings.HasSuffix(name, "some-file") {
+			// Act like it's been deleted.
+			return nil, ErrNotExist
+		}
+		return oldStat(name)
+	}
+	dir := newDir("TestReaddirRemoveRace", t)
+	defer RemoveAll(dir)
+	if err := ioutil.WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil {
+		t.Fatal(err)
+	}
+	d, err := Open(dir)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer d.Close()
+	fis, err := d.Readdir(2) // notably, greater than zero
+	if len(fis) == 0 && err == nil {
+		// This is what used to happen (Issue 16919)
+		t.Fatal("Readdir = empty slice & err == nil")
+	}
+	if len(fis) != 0 || err != io.EOF {
+		t.Errorf("Readdir = %d entries: %v; want 0, io.EOF", len(fis), err)
+		for i, fi := range fis {
+			t.Errorf("  entry[%d]: %q, %v", i, fi.Name(), fi.Mode())
+		}
+		t.FailNow()
+	}
+}
diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go
index 05d7a8f..54ba99b 100644
--- a/src/os/os_windows_test.go
+++ b/src/os/os_windows_test.go
@@ -5,41 +5,24 @@
 package os_test
 
 import (
+	"fmt"
+	"internal/syscall/windows"
+	"internal/testenv"
+	"io"
 	"io/ioutil"
 	"os"
 	osexec "os/exec"
 	"path/filepath"
+	"reflect"
+	"runtime"
 	"sort"
 	"strings"
 	"syscall"
 	"testing"
+	"unicode/utf16"
+	"unsafe"
 )
 
-var supportJunctionLinks = true
-
-func init() {
-	tmpdir, err := ioutil.TempDir("", "symtest")
-	if err != nil {
-		panic("failed to create temp directory: " + err.Error())
-	}
-	defer os.RemoveAll(tmpdir)
-
-	err = os.Symlink("target", filepath.Join(tmpdir, "symlink"))
-	if err != nil {
-		err = err.(*os.LinkError).Err
-		switch err {
-		case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD:
-			supportsSymlinks = false
-		}
-	}
-	defer os.Remove("target")
-
-	b, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
-	if !strings.Contains(string(b), " /J ") {
-		supportJunctionLinks = false
-	}
-}
-
 func TestSameWindowsFile(t *testing.T) {
 	temp, err := ioutil.TempDir("", "TestSameWindowsFile")
 	if err != nil {
@@ -93,33 +76,423 @@ func TestSameWindowsFile(t *testing.T) {
 	}
 }
 
-func TestStatJunctionLink(t *testing.T) {
-	if !supportJunctionLinks {
-		t.Skip("skipping because junction links are not supported")
+type dirLinkTest struct {
+	name    string
+	mklink  func(link, target string) error
+	issueNo int // correspondent issue number (for broken tests)
+}
+
+func testDirLinks(t *testing.T, tests []dirLinkTest) {
+	tmpdir, err := ioutil.TempDir("", "testDirLinks")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tmpdir)
+
+	oldwd, err := os.Getwd()
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = os.Chdir(tmpdir)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Chdir(oldwd)
+
+	dir := filepath.Join(tmpdir, "dir")
+	err = os.Mkdir(dir, 0777)
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("abc"), 0644)
+	if err != nil {
+		t.Fatal(err)
+	}
+	for _, test := range tests {
+		link := filepath.Join(tmpdir, test.name+"_link")
+		err := test.mklink(link, dir)
+		if err != nil {
+			t.Errorf("creating link for %s test failed: %v", test.name, err)
+			continue
+		}
+
+		data, err := ioutil.ReadFile(filepath.Join(link, "abc"))
+		if err != nil {
+			t.Errorf("failed to read abc file: %v", err)
+			continue
+		}
+		if string(data) != "abc" {
+			t.Errorf(`abc file is expected to have "abc" in it, but has %v`, data)
+			continue
+		}
+
+		if test.issueNo > 0 {
+			t.Logf("skipping broken %q test: see issue %d", test.name, test.issueNo)
+			continue
+		}
+
+		fi, err := os.Stat(link)
+		if err != nil {
+			t.Errorf("failed to stat link %v: %v", link, err)
+			continue
+		}
+		expected := filepath.Base(dir)
+		got := fi.Name()
+		if !fi.IsDir() || expected != got {
+			t.Errorf("link should point to %v but points to %v instead", expected, got)
+			continue
+		}
+	}
+}
+
+// reparseData is used to build reparse buffer data required for tests.
+type reparseData struct {
+	substituteName namePosition
+	printName      namePosition
+	pathBuf        []uint16
+}
+
+type namePosition struct {
+	offset uint16
+	length uint16
+}
+
+func (rd *reparseData) addUTF16s(s []uint16) (offset uint16) {
+	off := len(rd.pathBuf) * 2
+	rd.pathBuf = append(rd.pathBuf, s...)
+	return uint16(off)
+}
+
+func (rd *reparseData) addString(s string) (offset, length uint16) {
+	p := syscall.StringToUTF16(s)
+	return rd.addUTF16s(p), uint16(len(p)-1) * 2 // do not include terminating NUL in the legth (as per PrintNameLength and SubstituteNameLength documentation)
+}
+
+func (rd *reparseData) addSubstituteName(name string) {
+	rd.substituteName.offset, rd.substituteName.length = rd.addString(name)
+}
+
+func (rd *reparseData) addPrintName(name string) {
+	rd.printName.offset, rd.printName.length = rd.addString(name)
+}
+
+func (rd *reparseData) addStringNoNUL(s string) (offset, length uint16) {
+	p := syscall.StringToUTF16(s)
+	p = p[:len(p)-1]
+	return rd.addUTF16s(p), uint16(len(p)) * 2
+}
+
+func (rd *reparseData) addSubstituteNameNoNUL(name string) {
+	rd.substituteName.offset, rd.substituteName.length = rd.addStringNoNUL(name)
+}
+
+func (rd *reparseData) addPrintNameNoNUL(name string) {
+	rd.printName.offset, rd.printName.length = rd.addStringNoNUL(name)
+}
+
+// pathBuffeLen returns length of rd pathBuf in bytes.
+func (rd *reparseData) pathBuffeLen() uint16 {
+	return uint16(len(rd.pathBuf)) * 2
+}
+
+// Windows REPARSE_DATA_BUFFER contains union member, and cannot be
+// translated into Go directly. _REPARSE_DATA_BUFFER type is to help
+// construct alternative versions of Windows REPARSE_DATA_BUFFER with
+// union part of SymbolicLinkReparseBuffer or MountPointReparseBuffer type.
+type _REPARSE_DATA_BUFFER struct {
+	header windows.REPARSE_DATA_BUFFER_HEADER
+	detail [syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
+}
+
+func createDirLink(link string, rdb *_REPARSE_DATA_BUFFER) error {
+	err := os.Mkdir(link, 0777)
+	if err != nil {
+		return err
+	}
+
+	linkp := syscall.StringToUTF16(link)
+	fd, err := syscall.CreateFile(&linkp[0], syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING,
+		syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
+	if err != nil {
+		return err
+	}
+	defer syscall.CloseHandle(fd)
+
+	buflen := uint32(rdb.header.ReparseDataLength) + uint32(unsafe.Sizeof(rdb.header))
+	var bytesReturned uint32
+	return syscall.DeviceIoControl(fd, windows.FSCTL_SET_REPARSE_POINT,
+		(*byte)(unsafe.Pointer(&rdb.header)), buflen, nil, 0, &bytesReturned, nil)
+}
+
+func createMountPoint(link string, target *reparseData) error {
+	var buf *windows.MountPointReparseBuffer
+	buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation
+	byteblob := make([]byte, buflen)
+	buf = (*windows.MountPointReparseBuffer)(unsafe.Pointer(&byteblob[0]))
+	buf.SubstituteNameOffset = target.substituteName.offset
+	buf.SubstituteNameLength = target.substituteName.length
+	buf.PrintNameOffset = target.printName.offset
+	buf.PrintNameLength = target.printName.length
+	copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:], target.pathBuf)
+
+	var rdb _REPARSE_DATA_BUFFER
+	rdb.header.ReparseTag = windows.IO_REPARSE_TAG_MOUNT_POINT
+	rdb.header.ReparseDataLength = buflen
+	copy(rdb.detail[:], byteblob)
+
+	return createDirLink(link, &rdb)
+}
+
+func TestDirectoryJunction(t *testing.T) {
+	var tests = []dirLinkTest{
+		{
+			// Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
+			name: "standard",
+			mklink: func(link, target string) error {
+				var t reparseData
+				t.addSubstituteName(`\??\` + target)
+				t.addPrintName(target)
+				return createMountPoint(link, &t)
+			},
+		},
+		{
+			// Do as junction utility https://technet.microsoft.com/en-au/sysinternals/bb896768.aspx does - set PrintNameLength to 0.
+			name: "have_blank_print_name",
+			mklink: func(link, target string) error {
+				var t reparseData
+				t.addSubstituteName(`\??\` + target)
+				t.addPrintName("")
+				return createMountPoint(link, &t)
+			},
+		},
+	}
+	output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
+	mklinkSupportsJunctionLinks := strings.Contains(string(output), " /J ")
+	if mklinkSupportsJunctionLinks {
+		tests = append(tests,
+			dirLinkTest{
+				name: "use_mklink_cmd",
+				mklink: func(link, target string) error {
+					output, err := osexec.Command("cmd", "/c", "mklink", "/J", link, target).CombinedOutput()
+					if err != nil {
+						t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
+					}
+					return nil
+				},
+			},
+		)
+	} else {
+		t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory junctions`)
+	}
+	testDirLinks(t, tests)
+}
+
+func enableCurrentThreadPrivilege(privilegeName string) error {
+	ct, err := windows.GetCurrentThread()
+	if err != nil {
+		return err
+	}
+	var t syscall.Token
+	err = windows.OpenThreadToken(ct, syscall.TOKEN_QUERY|windows.TOKEN_ADJUST_PRIVILEGES, false, &t)
+	if err != nil {
+		return err
+	}
+	defer syscall.CloseHandle(syscall.Handle(t))
+
+	var tp windows.TOKEN_PRIVILEGES
+
+	privStr, err := syscall.UTF16PtrFromString(privilegeName)
+	if err != nil {
+		return err
+	}
+	err = windows.LookupPrivilegeValue(nil, privStr, &tp.Privileges[0].Luid)
+	if err != nil {
+		return err
+	}
+	tp.PrivilegeCount = 1
+	tp.Privileges[0].Attributes = windows.SE_PRIVILEGE_ENABLED
+	return windows.AdjustTokenPrivileges(t, false, &tp, 0, nil, nil)
+}
+
+func createSymbolicLink(link string, target *reparseData, isrelative bool) error {
+	var buf *windows.SymbolicLinkReparseBuffer
+	buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation
+	byteblob := make([]byte, buflen)
+	buf = (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&byteblob[0]))
+	buf.SubstituteNameOffset = target.substituteName.offset
+	buf.SubstituteNameLength = target.substituteName.length
+	buf.PrintNameOffset = target.printName.offset
+	buf.PrintNameLength = target.printName.length
+	if isrelative {
+		buf.Flags = windows.SYMLINK_FLAG_RELATIVE
+	}
+	copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:], target.pathBuf)
+
+	var rdb _REPARSE_DATA_BUFFER
+	rdb.header.ReparseTag = syscall.IO_REPARSE_TAG_SYMLINK
+	rdb.header.ReparseDataLength = buflen
+	copy(rdb.detail[:], byteblob)
+
+	return createDirLink(link, &rdb)
+}
+
+func TestDirectorySymbolicLink(t *testing.T) {
+	var tests []dirLinkTest
+	output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
+	mklinkSupportsDirectorySymbolicLinks := strings.Contains(string(output), " /D ")
+	if mklinkSupportsDirectorySymbolicLinks {
+		tests = append(tests,
+			dirLinkTest{
+				name: "use_mklink_cmd",
+				mklink: func(link, target string) error {
+					output, err := osexec.Command("cmd", "/c", "mklink", "/D", link, target).CombinedOutput()
+					if err != nil {
+						t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
+					}
+					return nil
+				},
+			},
+		)
+	} else {
+		t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory symbolic links`)
+	}
+
+	// The rest of these test requires SeCreateSymbolicLinkPrivilege to be held.
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	err := windows.ImpersonateSelf(windows.SecurityImpersonation)
+	if err != nil {
+		t.Fatal(err)
 	}
+	defer windows.RevertToSelf()
+
+	err = enableCurrentThreadPrivilege("SeCreateSymbolicLinkPrivilege")
+	if err != nil {
+		t.Skipf(`skipping some tests, could not enable "SeCreateSymbolicLinkPrivilege": %v`, err)
+	}
+	tests = append(tests,
+		dirLinkTest{
+			name: "use_os_pkg",
+			mklink: func(link, target string) error {
+				return os.Symlink(target, link)
+			},
+		},
+		dirLinkTest{
+			// Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
+			name: "standard",
+			mklink: func(link, target string) error {
+				var t reparseData
+				t.addPrintName(target)
+				t.addSubstituteName(`\??\` + target)
+				return createSymbolicLink(link, &t, false)
+			},
+		},
+		dirLinkTest{
+			name: "relative",
+			mklink: func(link, target string) error {
+				var t reparseData
+				t.addSubstituteNameNoNUL(filepath.Base(target))
+				t.addPrintNameNoNUL(filepath.Base(target))
+				return createSymbolicLink(link, &t, true)
+			},
+		},
+	)
+	testDirLinks(t, tests)
+}
+
+func TestNetworkSymbolicLink(t *testing.T) {
+	testenv.MustHaveSymlink(t)
 
-	dir, err := ioutil.TempDir("", "go-build")
+	dir, err := ioutil.TempDir("", "TestNetworkSymbolicLink")
 	if err != nil {
-		t.Fatalf("failed to create temp directory: %v", err)
+		t.Fatal(err)
 	}
 	defer os.RemoveAll(dir)
 
-	link := filepath.Join(filepath.Dir(dir), filepath.Base(dir)+"-link")
+	oldwd, err := os.Getwd()
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = os.Chdir(dir)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Chdir(oldwd)
+
+	shareName := "GoSymbolicLinkTestShare" // hope no conflictions
+	sharePath := filepath.Join(dir, shareName)
+	testDir := "TestDir"
+
+	err = os.MkdirAll(filepath.Join(sharePath, testDir), 0777)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	wShareName, err := syscall.UTF16PtrFromString(shareName)
+	if err != nil {
+		t.Fatal(err)
+	}
+	wSharePath, err := syscall.UTF16PtrFromString(sharePath)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	p := windows.SHARE_INFO_2{
+		Netname:     wShareName,
+		Type:        windows.STYPE_DISKTREE,
+		Remark:      nil,
+		Permissions: 0,
+		MaxUses:     1,
+		CurrentUses: 0,
+		Path:        wSharePath,
+		Passwd:      nil,
+	}
+
+	err = windows.NetShareAdd(nil, 2, (*byte)(unsafe.Pointer(&p)), nil)
+	if err != nil {
+		if err == syscall.ERROR_ACCESS_DENIED {
+			t.Skip("you don't have enough privileges to add network share")
+		}
+		t.Fatal(err)
+	}
+	defer func() {
+		err := windows.NetShareDel(nil, wShareName, 0)
+		if err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	UNCPath := `\\localhost\` + shareName + `\`
 
-	output, err := osexec.Command("cmd", "/c", "mklink", "/J", link, dir).CombinedOutput()
+	fi1, err := os.Stat(sharePath)
 	if err != nil {
-		t.Fatalf("failed to run mklink %v %v: %v %q", link, dir, err, output)
+		t.Fatal(err)
+	}
+	fi2, err := os.Stat(UNCPath)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !os.SameFile(fi1, fi2) {
+		t.Fatalf("%q and %q should be the same directory, but not", sharePath, UNCPath)
+	}
+
+	target := filepath.Join(UNCPath, testDir)
+	link := "link"
+
+	err = os.Symlink(target, link)
+	if err != nil {
+		t.Fatal(err)
 	}
 	defer os.Remove(link)
 
-	fi, err := os.Stat(link)
+	got, err := os.Readlink(link)
 	if err != nil {
-		t.Fatalf("failed to stat link %v: %v", link, err)
+		t.Fatal(err)
 	}
-	expected := filepath.Base(dir)
-	got := fi.Name()
-	if !fi.IsDir() || expected != got {
-		t.Fatalf("link should point to %v but points to %v instead", expected, got)
+
+	if got != target {
+		t.Errorf(`os.Readlink("%s"): got %v, want %v`, link, got, target)
 	}
 }
 
@@ -245,3 +618,107 @@ func TestDeleteReadOnly(t *testing.T) {
 		t.Fatal(err)
 	}
 }
+
+func TestStatSymlinkLoop(t *testing.T) {
+	testenv.MustHaveSymlink(t)
+
+	defer chtmpdir(t)()
+
+	err := os.Symlink("x", "y")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove("y")
+
+	err = os.Symlink("y", "x")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove("x")
+
+	_, err = os.Stat("x")
+	if perr, ok := err.(*os.PathError); !ok || perr.Err != syscall.ELOOP {
+		t.Errorf("expected *PathError with ELOOP, got %T: %v\n", err, err)
+	}
+}
+
+func TestReadStdin(t *testing.T) {
+	old := *os.ReadConsoleFunc
+	defer func() {
+		*os.ReadConsoleFunc = old
+	}()
+
+	testConsole := os.NewConsoleFile(syscall.Stdin, "test")
+
+	var tests = []string{
+		"abc",
+		"äöü",
+		"\u3042",
+		"“hi”™",
+		"hello\x1aworld",
+		"\U0001F648\U0001F649\U0001F64A",
+	}
+
+	for _, consoleSize := range []int{1, 2, 3, 10, 16, 100, 1000} {
+		for _, readSize := range []int{1, 2, 3, 4, 5, 8, 10, 16, 20, 50, 100} {
+			for _, s := range tests {
+				t.Run(fmt.Sprintf("c%d/r%d/%s", consoleSize, readSize, s), func(t *testing.T) {
+					s16 := utf16.Encode([]rune(s))
+					*os.ReadConsoleFunc = func(h syscall.Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) error {
+						if inputControl != nil {
+							t.Fatalf("inputControl not nil")
+						}
+						n := int(toread)
+						if n > consoleSize {
+							n = consoleSize
+						}
+						n = copy((*[10000]uint16)(unsafe.Pointer(buf))[:n], s16)
+						s16 = s16[n:]
+						*read = uint32(n)
+						t.Logf("read %d -> %d", toread, *read)
+						return nil
+					}
+
+					var all []string
+					var buf []byte
+					chunk := make([]byte, readSize)
+					for {
+						n, err := testConsole.Read(chunk)
+						buf = append(buf, chunk[:n]...)
+						if err == io.EOF {
+							all = append(all, string(buf))
+							if len(all) >= 5 {
+								break
+							}
+							buf = buf[:0]
+						} else if err != nil {
+							t.Fatalf("reading %q: error: %v", s, err)
+						}
+						if len(buf) >= 2000 {
+							t.Fatalf("reading %q: stuck in loop: %q", s, buf)
+						}
+					}
+
+					want := strings.Split(s, "\x1a")
+					for len(want) < 5 {
+						want = append(want, "")
+					}
+					if !reflect.DeepEqual(all, want) {
+						t.Errorf("reading %q:\nhave %x\nwant %x", s, all, want)
+					}
+				})
+			}
+		}
+	}
+}
+
+func TestStatPagefile(t *testing.T) {
+	_, err := os.Stat(`c:\pagefile.sys`)
+	if err == nil {
+		return
+	}
+	if os.IsNotExist(err) {
+		t.Skip(`skipping because c:\pagefile.sys is not found`)
+	}
+	t.Fatal(err)
+}
diff --git a/src/os/path_test.go b/src/os/path_test.go
index 51dc25b..6f5bfa5 100644
--- a/src/os/path_test.go
+++ b/src/os/path_test.go
@@ -5,6 +5,7 @@
 package os_test
 
 import (
+	"internal/testenv"
 	"io/ioutil"
 	. "os"
 	"path/filepath"
@@ -169,14 +170,7 @@ func TestRemoveAll(t *testing.T) {
 }
 
 func TestMkdirAllWithSymlink(t *testing.T) {
-	switch runtime.GOOS {
-	case "android", "nacl", "plan9":
-		t.Skipf("skipping on %s", runtime.GOOS)
-	case "windows":
-		if !supportsSymlinks {
-			t.Skipf("skipping on %s", runtime.GOOS)
-		}
-	}
+	testenv.MustHaveSymlink(t)
 
 	tmpDir, err := ioutil.TempDir("", "TestMkdirAllWithSymlink-")
 	if err != nil {
diff --git a/src/os/path_unix.go b/src/os/path_unix.go
index 36f8e61..ecf098c 100644
--- a/src/os/path_unix.go
+++ b/src/os/path_unix.go
@@ -15,3 +15,21 @@ const (
 func IsPathSeparator(c uint8) bool {
 	return PathSeparator == c
 }
+
+// basename removes trailing slashes and the leading directory name from path name
+func basename(name string) string {
+	i := len(name) - 1
+	// Remove trailing slashes
+	for ; i > 0 && name[i] == '/'; i-- {
+		name = name[:i]
+	}
+	// Remove leading directory name
+	for i--; i >= 0; i-- {
+		if name[i] == '/' {
+			name = name[i+1:]
+			break
+		}
+	}
+
+	return name
+}
diff --git a/src/os/path_windows.go b/src/os/path_windows.go
index c96f137..ccac1c0 100644
--- a/src/os/path_windows.go
+++ b/src/os/path_windows.go
@@ -14,3 +14,193 @@ func IsPathSeparator(c uint8) bool {
 	// NOTE: Windows accept / as path separator.
 	return c == '\\' || c == '/'
 }
+
+// basename removes trailing slashes and the leading
+// directory name and drive letter from path name.
+func basename(name string) string {
+	// Remove drive letter
+	if len(name) == 2 && name[1] == ':' {
+		name = "."
+	} else if len(name) > 2 && name[1] == ':' {
+		name = name[2:]
+	}
+	i := len(name) - 1
+	// Remove trailing slashes
+	for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- {
+		name = name[:i]
+	}
+	// Remove leading directory name
+	for i--; i >= 0; i-- {
+		if name[i] == '/' || name[i] == '\\' {
+			name = name[i+1:]
+			break
+		}
+	}
+	return name
+}
+
+func isAbs(path string) (b bool) {
+	v := volumeName(path)
+	if v == "" {
+		return false
+	}
+	path = path[len(v):]
+	if path == "" {
+		return false
+	}
+	return IsPathSeparator(path[0])
+}
+
+func volumeName(path string) (v string) {
+	if len(path) < 2 {
+		return ""
+	}
+	// with drive letter
+	c := path[0]
+	if path[1] == ':' &&
+		('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
+			'A' <= c && c <= 'Z') {
+		return path[:2]
+	}
+	// is it UNC
+	if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) &&
+		!IsPathSeparator(path[2]) && path[2] != '.' {
+		// first, leading `\\` and next shouldn't be `\`. its server name.
+		for n := 3; n < l-1; n++ {
+			// second, next '\' shouldn't be repeated.
+			if IsPathSeparator(path[n]) {
+				n++
+				// third, following something characters. its share name.
+				if !IsPathSeparator(path[n]) {
+					if path[n] == '.' {
+						break
+					}
+					for ; n < l; n++ {
+						if IsPathSeparator(path[n]) {
+							break
+						}
+					}
+					return path[:n]
+				}
+				break
+			}
+		}
+	}
+	return ""
+}
+
+func fromSlash(path string) string {
+	// Replace each '/' with '\\' if present
+	var pathbuf []byte
+	var lastSlash int
+	for i, b := range path {
+		if b == '/' {
+			if pathbuf == nil {
+				pathbuf = make([]byte, len(path))
+			}
+			copy(pathbuf[lastSlash:], path[lastSlash:i])
+			pathbuf[i] = '\\'
+			lastSlash = i + 1
+		}
+	}
+	if pathbuf == nil {
+		return path
+	}
+
+	copy(pathbuf[lastSlash:], path[lastSlash:])
+	return string(pathbuf)
+}
+
+func dirname(path string) string {
+	vol := volumeName(path)
+	i := len(path) - 1
+	for i >= len(vol) && !IsPathSeparator(path[i]) {
+		i--
+	}
+	dir := path[len(vol) : i+1]
+	last := len(dir) - 1
+	if last > 0 && IsPathSeparator(dir[last]) {
+		dir = dir[:last]
+	}
+	if dir == "" {
+		dir = "."
+	}
+	return vol + dir
+}
+
+// fixLongPath returns the extended-length (\\?\-prefixed) form of
+// path when needed, in order to avoid the default 260 character file
+// path limit imposed by Windows. If path is not easily converted to
+// the extended-length form (for example, if path is a relative path
+// or contains .. elements), or is short enough, fixLongPath returns
+// path unmodified.
+//
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
+func fixLongPath(path string) string {
+	// Do nothing (and don't allocate) if the path is "short".
+	// Empirically (at least on the Windows Server 2013 builder),
+	// the kernel is arbitrarily okay with <= 248 bytes. That
+	// matches what the docs above say:
+	// "When using an API to create a directory, the specified
+	// path cannot be so long that you cannot append an 8.3 file
+	// name (that is, the directory name cannot exceed MAX_PATH
+	// minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
+	if len(path) <= 248 {
+		// Don't fix. (This is how Go 1.7 and earlier worked,
+		// not automatically generating the \\?\ form)
+		return path
+	}
+
+	// The extended form begins with \\?\, as in
+	// \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
+	// The extended form disables evaluation of . and .. path
+	// elements and disables the interpretation of / as equivalent
+	// to \. The conversion here rewrites / to \ and elides
+	// . elements as well as trailing or duplicate separators. For
+	// simplicity it avoids the conversion entirely for relative
+	// paths or paths containing .. elements. For now,
+	// \\server\share paths are not converted to
+	// \\?\UNC\server\share paths because the rules for doing so
+	// are less well-specified.
+	if len(path) >= 2 && path[:2] == `\\` {
+		// Don't canonicalize UNC paths.
+		return path
+	}
+	if !isAbs(path) {
+		// Relative path
+		return path
+	}
+
+	const prefix = `\\?`
+
+	pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
+	copy(pathbuf, prefix)
+	n := len(path)
+	r, w := 0, len(prefix)
+	for r < n {
+		switch {
+		case IsPathSeparator(path[r]):
+			// empty block
+			r++
+		case path[r] == '.' && (r+1 == n || IsPathSeparator(path[r+1])):
+			// /./
+			r++
+		case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || IsPathSeparator(path[r+2])):
+			// /../ is currently unhandled
+			return path
+		default:
+			pathbuf[w] = '\\'
+			w++
+			for ; r < n && !IsPathSeparator(path[r]); r++ {
+				pathbuf[w] = path[r]
+				w++
+			}
+		}
+	}
+	// A drive's root directory needs a trailing \
+	if w == len(`\\?\c:`) {
+		pathbuf[w] = '\\'
+		w++
+	}
+	return string(pathbuf[:w])
+}
diff --git a/src/os/path_windows_test.go b/src/os/path_windows_test.go
new file mode 100644
index 0000000..cce0bdd
--- /dev/null
+++ b/src/os/path_windows_test.go
@@ -0,0 +1,46 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os_test
+
+import (
+	"os"
+	"strings"
+	"testing"
+)
+
+func TestFixLongPath(t *testing.T) {
+	// 248 is long enough to trigger the longer-than-248 checks in
+	// fixLongPath, but short enough not to make a path component
+	// longer than 255, which is illegal on Windows. (which
+	// doesn't really matter anyway, since this is purely a string
+	// function we're testing, and it's not actually being used to
+	// do a system call)
+	veryLong := "l" + strings.Repeat("o", 248) + "ng"
+	for _, test := range []struct{ in, want string }{
+		// Short; unchanged:
+		{`C:\short.txt`, `C:\short.txt`},
+		{`C:\`, `C:\`},
+		{`C:`, `C:`},
+		// The "long" substring is replaced by a looooooong
+		// string which triggers the rewriting. Except in the
+		// cases below where it doesn't.
+		{`C:\long\foo.txt`, `\\?\C:\long\foo.txt`},
+		{`C:/long/foo.txt`, `\\?\C:\long\foo.txt`},
+		{`C:\long\foo\\bar\.\baz\\`, `\\?\C:\long\foo\bar\baz`},
+		{`\\unc\path`, `\\unc\path`},
+		{`long.txt`, `long.txt`},
+		{`C:long.txt`, `C:long.txt`},
+		{`c:\long\..\bar\baz`, `c:\long\..\bar\baz`},
+		{`\\?\c:\long\foo.txt`, `\\?\c:\long\foo.txt`},
+		{`\\?\c:\long/foo.txt`, `\\?\c:\long/foo.txt`},
+	} {
+		in := strings.Replace(test.in, "long", veryLong, -1)
+		want := strings.Replace(test.want, "long", veryLong, -1)
+		if got := os.FixLongPath(in); got != want {
+			got = strings.Replace(got, veryLong, "long", -1)
+			t.Errorf("fixLongPath(%q) = %q; want %q", test.in, got, test.want)
+		}
+	}
+}
diff --git a/src/os/signal/doc.go b/src/os/signal/doc.go
index 73b01a2..16f49c7 100644
--- a/src/os/signal/doc.go
+++ b/src/os/signal/doc.go
@@ -181,10 +181,11 @@ If the Go runtime sees an existing signal handler for the SIGCANCEL or
 SIGSETXID signals (which are used only on GNU/Linux), it will turn on
 the SA_ONSTACK flag and otherwise keep the signal handler.
 
-For the synchronous signals, the Go runtime will install a signal
-handler. It will save any existing signal handler. If a synchronous
-signal arrives while executing non-Go code, the Go runtime will invoke
-the existing signal handler instead of the Go signal handler.
+For the synchronous signals and SIGPIPE, the Go runtime will install a
+signal handler. It will save any existing signal handler. If a
+synchronous signal arrives while executing non-Go code, the Go runtime
+will invoke the existing signal handler instead of the Go signal
+handler.
 
 Go code built with -buildmode=c-archive or -buildmode=c-shared will
 not install any other signal handlers by default. If there is an
diff --git a/src/os/signal/signal_windows_test.go b/src/os/signal/signal_windows_test.go
index f3e6706..c2b5901 100644
--- a/src/os/signal/signal_windows_test.go
+++ b/src/os/signal/signal_windows_test.go
@@ -6,6 +6,7 @@ package signal
 
 import (
 	"bytes"
+	"internal/testenv"
 	"io/ioutil"
 	"os"
 	"os/exec"
@@ -75,7 +76,7 @@ func main() {
 	// compile it
 	exe := name + ".exe"
 	defer os.Remove(exe)
-	o, err := exec.Command("go", "build", "-o", exe, src).CombinedOutput()
+	o, err := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, src).CombinedOutput()
 	if err != nil {
 		t.Fatalf("Failed to compile: %v\n%v", err, string(o))
 	}
diff --git a/src/os/stat_plan9.go b/src/os/stat_plan9.go
index 96f056c..274d0d8 100644
--- a/src/os/stat_plan9.go
+++ b/src/os/stat_plan9.go
@@ -11,12 +11,6 @@ import (
 
 const _BIT16SZ = 2
 
-func sameFile(fs1, fs2 *fileStat) bool {
-	a := fs1.sys.(*syscall.Dir)
-	b := fs2.sys.(*syscall.Dir)
-	return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev
-}
-
 func fileInfoFromStat(d *syscall.Dir) FileInfo {
 	fs := &fileStat{
 		name:    d.Name,
@@ -37,6 +31,10 @@ func fileInfoFromStat(d *syscall.Dir) FileInfo {
 	if d.Mode&syscall.DMTMP != 0 {
 		fs.mode |= ModeTemporary
 	}
+	// Consider all files not served by #M as device files.
+	if d.Type != 'M' {
+		fs.mode |= ModeDevice
+	}
 	return fs
 }
 
diff --git a/src/os/stat_unix.go b/src/os/stat_unix.go
new file mode 100644
index 0000000..1733d3f
--- /dev/null
+++ b/src/os/stat_unix.go
@@ -0,0 +1,52 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+
+package os
+
+import (
+	"syscall"
+)
+
+// Stat returns the FileInfo structure describing file.
+// If there is an error, it will be of type *PathError.
+func (f *File) Stat() (FileInfo, error) {
+	if f == nil {
+		return nil, ErrInvalid
+	}
+	var fs fileStat
+	err := syscall.Fstat(f.fd, &fs.sys)
+	if err != nil {
+		return nil, &PathError{"stat", f.name, err}
+	}
+	fillFileStatFromSys(&fs, f.name)
+	return &fs, nil
+}
+
+// Stat returns a FileInfo describing the named file.
+// If there is an error, it will be of type *PathError.
+func Stat(name string) (FileInfo, error) {
+	var fs fileStat
+	err := syscall.Stat(name, &fs.sys)
+	if err != nil {
+		return nil, &PathError{"stat", name, err}
+	}
+	fillFileStatFromSys(&fs, name)
+	return &fs, nil
+}
+
+// Lstat returns a FileInfo describing the named file.
+// If the file is a symbolic link, the returned FileInfo
+// describes the symbolic link. Lstat makes no attempt to follow the link.
+// If there is an error, it will be of type *PathError.
+func Lstat(name string) (FileInfo, error) {
+	var fs fileStat
+	err := syscall.Lstat(name, &fs.sys)
+	if err != nil {
+		return nil, &PathError{"lstat", name, err}
+	}
+	fillFileStatFromSys(&fs, name)
+	return &fs, nil
+}
diff --git a/src/os/stat_windows.go b/src/os/stat_windows.go
index e55eeb0..fdabf73 100644
--- a/src/os/stat_windows.go
+++ b/src/os/stat_windows.go
@@ -5,6 +5,7 @@
 package os
 
 import (
+	"internal/syscall/windows"
 	"syscall"
 	"unsafe"
 )
@@ -61,7 +62,7 @@ func (file *File) Stat() (FileInfo, error) {
 func Stat(name string) (FileInfo, error) {
 	var fi FileInfo
 	var err error
-	for {
+	for i := 0; i < 255; i++ {
 		fi, err = Lstat(name)
 		if err != nil {
 			return fi, err
@@ -74,6 +75,7 @@ func Stat(name string) (FileInfo, error) {
 			return fi, err
 		}
 	}
+	return nil, &PathError{"Stat", name, syscall.ELOOP}
 }
 
 // Lstat returns the FileInfo structure describing the named file.
@@ -88,13 +90,29 @@ func Lstat(name string) (FileInfo, error) {
 		return &devNullStat, nil
 	}
 	fs := &fileStat{name: basename(name)}
-	namep, e := syscall.UTF16PtrFromString(name)
+	namep, e := syscall.UTF16PtrFromString(fixLongPath(name))
 	if e != nil {
 		return nil, &PathError{"Lstat", name, e}
 	}
 	e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys)))
 	if e != nil {
-		return nil, &PathError{"GetFileAttributesEx", name, e}
+		if e != windows.ERROR_SHARING_VIOLATION {
+			return nil, &PathError{"GetFileAttributesEx", name, e}
+		}
+		// try FindFirstFile now that GetFileAttributesEx failed
+		var fd syscall.Win32finddata
+		h, e2 := syscall.FindFirstFile(namep, &fd)
+		if e2 != nil {
+			return nil, &PathError{"FindFirstFile", name, e}
+		}
+		syscall.FindClose(h)
+
+		fs.sys.FileAttributes = fd.FileAttributes
+		fs.sys.CreationTime = fd.CreationTime
+		fs.sys.LastAccessTime = fd.LastAccessTime
+		fs.sys.LastWriteTime = fd.LastWriteTime
+		fs.sys.FileSizeHigh = fd.FileSizeHigh
+		fs.sys.FileSizeLow = fd.FileSizeLow
 	}
 	fs.path = name
 	if !isAbs(fs.path) {
@@ -105,77 +123,3 @@ func Lstat(name string) (FileInfo, error) {
 	}
 	return fs, nil
 }
-
-// basename removes trailing slashes and the leading
-// directory name and drive letter from path name.
-func basename(name string) string {
-	// Remove drive letter
-	if len(name) == 2 && name[1] == ':' {
-		name = "."
-	} else if len(name) > 2 && name[1] == ':' {
-		name = name[2:]
-	}
-	i := len(name) - 1
-	// Remove trailing slashes
-	for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- {
-		name = name[:i]
-	}
-	// Remove leading directory name
-	for i--; i >= 0; i-- {
-		if name[i] == '/' || name[i] == '\\' {
-			name = name[i+1:]
-			break
-		}
-	}
-	return name
-}
-
-func isAbs(path string) (b bool) {
-	v := volumeName(path)
-	if v == "" {
-		return false
-	}
-	path = path[len(v):]
-	if path == "" {
-		return false
-	}
-	return IsPathSeparator(path[0])
-}
-
-func volumeName(path string) (v string) {
-	if len(path) < 2 {
-		return ""
-	}
-	// with drive letter
-	c := path[0]
-	if path[1] == ':' &&
-		('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
-			'A' <= c && c <= 'Z') {
-		return path[:2]
-	}
-	// is it UNC
-	if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) &&
-		!IsPathSeparator(path[2]) && path[2] != '.' {
-		// first, leading `\\` and next shouldn't be `\`. its server name.
-		for n := 3; n < l-1; n++ {
-			// second, next '\' shouldn't be repeated.
-			if IsPathSeparator(path[n]) {
-				n++
-				// third, following something characters. its share name.
-				if !IsPathSeparator(path[n]) {
-					if path[n] == '.' {
-						break
-					}
-					for ; n < l; n++ {
-						if IsPathSeparator(path[n]) {
-							break
-						}
-					}
-					return path[:n]
-				}
-				break
-			}
-		}
-	}
-	return ""
-}
diff --git a/src/os/sys.go b/src/os/sys.go
new file mode 100644
index 0000000..28b0f6b
--- /dev/null
+++ b/src/os/sys.go
@@ -0,0 +1,10 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+// Hostname returns the host name reported by the kernel.
+func Hostname() (name string, err error) {
+	return hostname()
+}
diff --git a/src/os/types.go b/src/os/types.go
index 12b593f..c565483 100644
--- a/src/os/types.go
+++ b/src/os/types.go
@@ -12,6 +12,11 @@ import (
 // Getpagesize returns the underlying system's memory page size.
 func Getpagesize() int { return syscall.Getpagesize() }
 
+// File represents an open file descriptor.
+type File struct {
+	*file // os specific
+}
+
 // A FileInfo describes a file and is returned by Stat and Lstat.
 type FileInfo interface {
 	Name() string       // base name of the file
diff --git a/src/os/types_plan9.go b/src/os/types_plan9.go
index 6d46ca9..125da66 100644
--- a/src/os/types_plan9.go
+++ b/src/os/types_plan9.go
@@ -4,7 +4,10 @@
 
 package os
 
-import "time"
+import (
+	"syscall"
+	"time"
+)
 
 // A fileStat is the implementation of FileInfo returned by Stat and Lstat.
 type fileStat struct {
@@ -19,3 +22,11 @@ func (fs *fileStat) Size() int64        { return fs.size }
 func (fs *fileStat) Mode() FileMode     { return fs.mode }
 func (fs *fileStat) ModTime() time.Time { return fs.modTime }
 func (fs *fileStat) Sys() interface{}   { return fs.sys }
+
+func sameFile(fs1, fs2 *fileStat) bool {
+	a := fs1.sys.(*syscall.Dir)
+	b := fs2.sys.(*syscall.Dir)
+	return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev
+}
+
+const badFd = -1
diff --git a/src/os/types_unix.go b/src/os/types_unix.go
index 056220c..1f61481 100644
--- a/src/os/types_unix.go
+++ b/src/os/types_unix.go
@@ -25,3 +25,9 @@ func (fs *fileStat) Size() int64        { return fs.size }
 func (fs *fileStat) Mode() FileMode     { return fs.mode }
 func (fs *fileStat) ModTime() time.Time { return fs.modTime }
 func (fs *fileStat) Sys() interface{}   { return &fs.sys }
+
+func sameFile(fs1, fs2 *fileStat) bool {
+	return fs1.sys.Dev == fs2.sys.Dev && fs1.sys.Ino == fs2.sys.Ino
+}
+
+const badFd = -1
diff --git a/src/os/wait_wait6.go b/src/os/wait_wait6.go
index 7f4780a..b309811 100644
--- a/src/os/wait_wait6.go
+++ b/src/os/wait_wait6.go
@@ -28,6 +28,7 @@ func (p *Process) blockUntilWaitable() (bool, error) {
 	} else {
 		_, _, errno = syscall.Syscall6(syscall.SYS_WAIT6, _P_PID, uintptr(p.Pid), 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0)
 	}
+	runtime.KeepAlive(p)
 	if errno != 0 {
 		// The wait6 system call is supported only on FreeBSD
 		// 9.3 and above, so it may return an ENOSYS error.
diff --git a/src/os/wait_waitid.go b/src/os/wait_waitid.go
index 74b7494..653fce9 100644
--- a/src/os/wait_waitid.go
+++ b/src/os/wait_waitid.go
@@ -26,7 +26,7 @@ func (p *Process) blockUntilWaitable() (bool, error) {
 	var siginfo [128]byte
 	psig := &siginfo[0]
 	_, _, e := syscall.Syscall6(syscall.SYS_WAITID, _P_PID, uintptr(p.Pid), uintptr(unsafe.Pointer(psig)), syscall.WEXITED|syscall.WNOWAIT, 0, 0)
-	runtime.KeepAlive(psig)
+	runtime.KeepAlive(p)
 	if e != 0 {
 		// waitid has been available since Linux 2.6.9, but
 		// reportedly is not available in Ubuntu on Windows.
diff --git a/src/path/filepath/match.go b/src/path/filepath/match.go
index 9fa68f5..5168e03 100644
--- a/src/path/filepath/match.go
+++ b/src/path/filepath/match.go
@@ -240,13 +240,14 @@ func Glob(pattern string) (matches []string, err error) {
 	}
 
 	dir, file := Split(pattern)
+	volumeLen := 0
 	if runtime.GOOS == "windows" {
-		dir = cleanGlobPathWindows(dir)
+		volumeLen, dir = cleanGlobPathWindows(dir)
 	} else {
 		dir = cleanGlobPath(dir)
 	}
 
-	if !hasMeta(dir) {
+	if !hasMeta(dir[volumeLen:]) {
 		return glob(dir, file, nil)
 	}
 
@@ -283,18 +284,21 @@ func cleanGlobPath(path string) string {
 }
 
 // cleanGlobPathWindows is windows version of cleanGlobPath.
-func cleanGlobPathWindows(path string) string {
+func cleanGlobPathWindows(path string) (prefixLen int, cleaned string) {
 	vollen := volumeNameLen(path)
 	switch {
 	case path == "":
-		return "."
+		return 0, "."
 	case vollen+1 == len(path) && os.IsPathSeparator(path[len(path)-1]): // /, \, C:\ and C:/
 		// do nothing to the path
-		return path
+		return vollen + 1, path
 	case vollen == len(path) && len(path) == 2: // C:
-		return path + "." // convert C: into C:.
+		return vollen, path + "." // convert C: into C:.
 	default:
-		return path[0 : len(path)-1] // chop off trailing separator
+		if vollen >= len(path) {
+			vollen = len(path) - 1
+		}
+		return vollen, path[0 : len(path)-1] // chop off trailing separator
 	}
 }
 
diff --git a/src/path/filepath/match_test.go b/src/path/filepath/match_test.go
index 6b068c7..3bd5598 100644
--- a/src/path/filepath/match_test.go
+++ b/src/path/filepath/match_test.go
@@ -6,6 +6,7 @@ package filepath_test
 
 import (
 	"fmt"
+	"internal/testenv"
 	"io/ioutil"
 	"os"
 	. "path/filepath"
@@ -174,15 +175,7 @@ var globSymlinkTests = []struct {
 }
 
 func TestGlobSymlink(t *testing.T) {
-	switch runtime.GOOS {
-	case "android", "nacl", "plan9":
-		t.Skipf("skipping on %s", runtime.GOOS)
-	case "windows":
-		if !supportsSymlinks {
-			t.Skipf("skipping on %s", runtime.GOOS)
-		}
-
-	}
+	testenv.MustHaveSymlink(t)
 
 	tmpDir, err := ioutil.TempDir("", "globsymlink")
 	if err != nil {
diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go
index 0dc559c..1d8e35c 100644
--- a/src/path/filepath/path.go
+++ b/src/path/filepath/path.go
@@ -177,7 +177,7 @@ func FromSlash(path string) string {
 // SplitList splits a list of paths joined by the OS-specific ListSeparator,
 // usually found in PATH or GOPATH environment variables.
 // Unlike strings.Split, SplitList returns an empty slice when passed an empty
-// string. SplitList does not replace slash characters in the returned paths.
+// string.
 func SplitList(path string) []string {
 	return splitList(path)
 }
@@ -393,9 +393,14 @@ func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
 func Walk(root string, walkFn WalkFunc) error {
 	info, err := os.Lstat(root)
 	if err != nil {
-		return walkFn(root, nil, err)
+		err = walkFn(root, nil, err)
+	} else {
+		err = walk(root, info, walkFn)
 	}
-	return walk(root, info, walkFn)
+	if err == SkipDir {
+		return nil
+	}
+	return err
 }
 
 // readDirNames reads the directory named by dirname and returns
diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go
index a3990e2..921b238 100644
--- a/src/path/filepath/path_test.go
+++ b/src/path/filepath/path_test.go
@@ -6,6 +6,7 @@ package filepath_test
 
 import (
 	"errors"
+	"internal/testenv"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -15,8 +16,6 @@ import (
 	"testing"
 )
 
-var supportsSymlinks = true
-
 type PathTest struct {
 	path, result string
 }
@@ -529,7 +528,7 @@ func TestWalkSkipDirOnFile(t *testing.T) {
 	touch(t, filepath.Join(td, "dir/foo2"))
 
 	sawFoo2 := false
-	filepath.Walk(td, func(path string, info os.FileInfo, err error) error {
+	walker := func(path string, info os.FileInfo, err error) error {
 		if strings.HasSuffix(path, "foo2") {
 			sawFoo2 = true
 		}
@@ -537,8 +536,20 @@ func TestWalkSkipDirOnFile(t *testing.T) {
 			return filepath.SkipDir
 		}
 		return nil
-	})
+	}
+
+	err = filepath.Walk(td, walker)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if sawFoo2 {
+		t.Errorf("SkipDir on file foo1 did not block processing of foo2")
+	}
 
+	err = filepath.Walk(filepath.Join(td, "dir"), walker)
+	if err != nil {
+		t.Fatal(err)
+	}
 	if sawFoo2 {
 		t.Errorf("SkipDir on file foo1 did not block processing of foo2")
 	}
@@ -776,13 +787,7 @@ func simpleJoin(dir, path string) string {
 }
 
 func TestEvalSymlinks(t *testing.T) {
-	switch runtime.GOOS {
-	case "android", "nacl", "plan9":
-		t.Skipf("skipping on %s", runtime.GOOS)
-	}
-	if !supportsSymlinks {
-		t.Skip("skipping because symlinks are not supported")
-	}
+	testenv.MustHaveSymlink(t)
 
 	tmpDir, err := ioutil.TempDir("", "evalsymlink")
 	if err != nil {
@@ -872,6 +877,40 @@ func TestEvalSymlinks(t *testing.T) {
 			t.Errorf(`EvalSymlinks(".") in %q directory returns %q, want "." or %q`, d.path, p, want)
 		}()
 
+		// test EvalSymlinks("C:.") on Windows
+		if runtime.GOOS == "windows" {
+			func() {
+				defer func() {
+					err := os.Chdir(wd)
+					if err != nil {
+						t.Fatal(err)
+					}
+				}()
+
+				err := os.Chdir(path)
+				if err != nil {
+					t.Error(err)
+					return
+				}
+
+				volDot := filepath.VolumeName(tmpDir) + "."
+
+				p, err := filepath.EvalSymlinks(volDot)
+				if err != nil {
+					t.Errorf(`EvalSymlinks("%s") in %q directory error: %v`, volDot, d.path, err)
+					return
+				}
+				if p == volDot {
+					return
+				}
+				want := filepath.Clean(findEvalSymlinksTestDirsDest(t, testdirs, d.path))
+				if p == want {
+					return
+				}
+				t.Errorf(`EvalSymlinks("%s") in %q directory returns %q, want %q or %q`, volDot, d.path, p, volDot, want)
+			}()
+		}
+
 		// test EvalSymlinks(".."+path)
 		func() {
 			defer func() {
@@ -923,14 +962,30 @@ func TestEvalSymlinks(t *testing.T) {
 	}
 }
 
-func TestIssue13582(t *testing.T) {
-	switch runtime.GOOS {
-	case "android", "nacl", "plan9":
-		t.Skipf("skipping on %s", runtime.GOOS)
+func TestEvalSymlinksIsNotExist(t *testing.T) {
+	testenv.MustHaveSymlink(t)
+
+	defer chtmpdir(t)()
+
+	_, err := filepath.EvalSymlinks("notexist")
+	if !os.IsNotExist(err) {
+		t.Errorf("expected the file is not found, got %v\n", err)
+	}
+
+	err = os.Symlink("notexist", "link")
+	if err != nil {
+		t.Fatal(err)
 	}
-	if !supportsSymlinks {
-		t.Skip("skipping because symlinks are not supported")
+	defer os.Remove("link")
+
+	_, err = filepath.EvalSymlinks("link")
+	if !os.IsNotExist(err) {
+		t.Errorf("expected the file is not found, got %v\n", err)
 	}
+}
+
+func TestIssue13582(t *testing.T) {
+	testenv.MustHaveSymlink(t)
 
 	tmpDir, err := ioutil.TempDir("", "issue13582")
 	if err != nil {
@@ -1006,13 +1061,16 @@ var absTestDirs = []string{
 var absTests = []string{
 	".",
 	"b",
+	"b/",
 	"../a",
 	"../a/b",
 	"../a/b/./c/../../.././a",
+	"../a/b/./c/../../.././a/",
 	"$",
 	"$/.",
 	"$/a/../a/b",
 	"$/a/b/c/../../.././a",
+	"$/a/b/c/../../.././a/",
 }
 
 func TestAbs(t *testing.T) {
@@ -1077,7 +1135,7 @@ func TestAbs(t *testing.T) {
 		if !filepath.IsAbs(abspath) {
 			t.Errorf("Abs(%q)=%q, not an absolute path", path, abspath)
 		}
-		if filepath.IsAbs(path) && abspath != filepath.Clean(path) {
+		if filepath.IsAbs(abspath) && abspath != filepath.Clean(abspath) {
 			t.Errorf("Abs(%q)=%q, isn't clean", path, abspath)
 		}
 	}
@@ -1240,11 +1298,11 @@ func TestBug3486(t *testing.T) { // https://golang.org/issue/3486
 	if err != nil {
 		t.Fatal(err)
 	}
-	bugs := filepath.Join(root, "bugs")
+	bugs := filepath.Join(root, "fixedbugs")
 	ken := filepath.Join(root, "ken")
 	seenBugs := false
 	seenKen := false
-	filepath.Walk(root, func(pth string, info os.FileInfo, err error) error {
+	err = filepath.Walk(root, func(pth string, info os.FileInfo, err error) error {
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -1255,12 +1313,15 @@ func TestBug3486(t *testing.T) { // https://golang.org/issue/3486
 			return filepath.SkipDir
 		case ken:
 			if !seenBugs {
-				t.Fatal("filepath.Walk out of order - ken before bugs")
+				t.Fatal("filepath.Walk out of order - ken before fixedbugs")
 			}
 			seenKen = true
 		}
 		return nil
 	})
+	if err != nil {
+		t.Fatal(err)
+	}
 	if !seenKen {
 		t.Fatalf("%q not seen", ken)
 	}
diff --git a/src/path/filepath/path_unix.go b/src/path/filepath/path_unix.go
index 2d242cc..dddcac0 100644
--- a/src/path/filepath/path_unix.go
+++ b/src/path/filepath/path_unix.go
@@ -20,6 +20,8 @@ func volumeNameLen(path string) int {
 }
 
 // HasPrefix exists for historical compatibility and should not be used.
+//
+// Deprecated: Use strings.HasPrefix instead.
 func HasPrefix(p, prefix string) bool {
 	return strings.HasPrefix(p, prefix)
 }
diff --git a/src/path/filepath/path_windows.go b/src/path/filepath/path_windows.go
index 41c57df..359703d 100644
--- a/src/path/filepath/path_windows.go
+++ b/src/path/filepath/path_windows.go
@@ -37,7 +37,7 @@ func volumeNameLen(path string) int {
 	if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
 		return 2
 	}
-	// is it UNC
+	// is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
 	if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
 		!isSlash(path[2]) && path[2] != '.' {
 		// first, leading `\\` and next shouldn't be `\`. its server name.
@@ -106,7 +106,11 @@ func splitList(path string) []string {
 }
 
 func abs(path string) (string, error) {
-	return syscall.FullPath(path)
+	fullPath, err := syscall.FullPath(path)
+	if err != nil {
+		return "", err
+	}
+	return Clean(fullPath), nil
 }
 
 func join(elem []string) string {
diff --git a/src/path/filepath/path_windows_test.go b/src/path/filepath/path_windows_test.go
index 9c82a0b..73e74be 100644
--- a/src/path/filepath/path_windows_test.go
+++ b/src/path/filepath/path_windows_test.go
@@ -12,30 +12,11 @@ import (
 	"os/exec"
 	"path/filepath"
 	"reflect"
+	"runtime/debug"
 	"strings"
-	"syscall"
 	"testing"
 )
 
-func init() {
-	tmpdir, err := ioutil.TempDir("", "symtest")
-	if err != nil {
-		panic("failed to create temp directory: " + err.Error())
-	}
-	defer os.RemoveAll(tmpdir)
-
-	err = os.Symlink("target", filepath.Join(tmpdir, "symlink"))
-	if err == nil {
-		return
-	}
-
-	err = err.(*os.LinkError).Err
-	switch err {
-	case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD:
-		supportsSymlinks = false
-	}
-}
-
 func TestWinSplitListTestsAreValid(t *testing.T) {
 	comspec := os.Getenv("ComSpec")
 	if comspec == "" {
@@ -357,10 +338,10 @@ func TestToNorm(t *testing.T) {
 		{`{{tmp}}\test`, `{{tmpvol}}FOO\BAR`, `{{tmpvol}}foo\bar`},
 
 		// test relative paths begin with '\'
-		{".", `{{tmpnovol}}\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
-		{".", `{{tmpnovol}}\.\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
-		{".", `{{tmpnovol}}\test\..\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
-		{".", `{{tmpnovol}}\TEST\FOO\BAR`, `{{tmpnovol}}\test\foo\bar`},
+		{"{{tmp}}", `{{tmpnovol}}\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
+		{"{{tmp}}", `{{tmpnovol}}\.\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
+		{"{{tmp}}", `{{tmpnovol}}\test\..\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
+		{"{{tmp}}", `{{tmpnovol}}\TEST\FOO\BAR`, `{{tmpnovol}}\test\foo\bar`},
 
 		// test relative paths begin without '\'
 		{`{{tmp}}\test`, ".", `.`},
@@ -371,42 +352,52 @@ func TestToNorm(t *testing.T) {
 		{`{{tmp}}\test`, `FOO\BAR`, `foo\bar`},
 	}
 
-	cwd, err := os.Getwd()
+	tmp, err := ioutil.TempDir("", "testToNorm")
 	if err != nil {
 		t.Fatal(err)
 	}
-
 	defer func() {
-		err := os.Chdir(cwd)
+		err := os.RemoveAll(tmp)
 		if err != nil {
 			t.Fatal(err)
 		}
 	}()
 
-	tmp, err := ioutil.TempDir("", "testToNorm")
+	// ioutil.TempDir might return "non-canonical" name.
+	ctmp, err := filepath.EvalSymlinks(tmp)
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer os.RemoveAll(tmp)
 
-	// ioutil.TempDir might return "non-canonical" name.
-	tmp, err = filepath.EvalSymlinks(tmp)
+	err = os.MkdirAll(strings.Replace(testPath, "{{tmp}}", ctmp, -1), 0777)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	err = os.MkdirAll(strings.Replace(testPath, "{{tmp}}", tmp, -1), 0777)
+	cwd, err := os.Getwd()
 	if err != nil {
 		t.Fatal(err)
 	}
+	defer func() {
+		err := os.Chdir(cwd)
+		if err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	tmpVol := filepath.VolumeName(ctmp)
+	if len(tmpVol) != 2 {
+		t.Fatalf("unexpected temp volume name %q", tmpVol)
+	}
 
-	tmpVol := filepath.VolumeName(tmp)
-	tmpNoVol := tmp[len(tmpVol):]
+	tmpNoVol := ctmp[len(tmpVol):]
+
+	replacer := strings.NewReplacer("{{tmp}}", ctmp, "{{tmpvol}}", tmpVol, "{{tmpnovol}}", tmpNoVol)
 
 	for _, test := range testsDir {
-		wd := strings.Replace(strings.Replace(strings.Replace(test.wd, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
-		arg := strings.Replace(strings.Replace(strings.Replace(test.arg, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
-		want := strings.Replace(strings.Replace(strings.Replace(test.want, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
+		wd := replacer.Replace(test.wd)
+		arg := replacer.Replace(test.arg)
+		want := replacer.Replace(test.want)
 
 		if test.wd == "." {
 			err := os.Chdir(cwd)
@@ -432,3 +423,10 @@ func TestToNorm(t *testing.T) {
 		}
 	}
 }
+
+func TestUNC(t *testing.T) {
+	// Test that this doesn't go into an infinite recursion.
+	// See golang.org/issue/15879.
+	defer debug.SetMaxStack(debug.SetMaxStack(1e6))
+	filepath.Glob(`\\?\c:\*`)
+}
diff --git a/src/path/filepath/symlink.go b/src/path/filepath/symlink.go
index f627a94..824aee4 100644
--- a/src/path/filepath/symlink.go
+++ b/src/path/filepath/symlink.go
@@ -105,8 +105,9 @@ func walkSymlinks(path string) (string, error) {
 			// directory is a symlink. Stop the walk, if symlink
 			// target is not absolute path, and return "."
 			// to the caller (just like unix does).
-			if path == "." && !IsAbs(newpath) {
-				return ".", nil
+			// Same for "C:.".
+			if path[volumeNameLen(path):] == "." && !IsAbs(newpath) {
+				return path, nil
 			}
 		}
 		if i == linksWalked {
diff --git a/src/path/path.go b/src/path/path.go
index c1d4d8a..76c7814 100644
--- a/src/path/path.go
+++ b/src/path/path.go
@@ -4,6 +4,8 @@
 
 // Package path implements utility routines for manipulating slash-separated
 // paths.
+//
+// To manipulate operating system paths, use the path/filepath package.
 package path
 
 import (
diff --git a/src/plugin/plugin.go b/src/plugin/plugin.go
new file mode 100644
index 0000000..5c822bd
--- /dev/null
+++ b/src/plugin/plugin.go
@@ -0,0 +1,73 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package plugin implements loading and symbol resolution of Go plugins.
+//
+// Currently plugins only work on Linux and Darwin.
+//
+// A plugin is a Go main package with exported functions and variables that
+// has been built with:
+//
+//	go build -buildmode=plugin
+//
+// When a plugin is first opened, the init functions of all packages not
+// already part of the program are called. The main function is not run.
+// A plugin is only initialized once, and cannot be closed.
+package plugin
+
+// Plugin is a loaded Go plugin.
+type Plugin struct {
+	pluginpath string
+	loaded     chan struct{} // closed when loaded
+	syms       map[string]interface{}
+}
+
+// Open opens a Go plugin.
+// If a path has already been opened, then the existing *Plugin is returned.
+// It is safe for concurrent use by multiple goroutines.
+func Open(path string) (*Plugin, error) {
+	return open(path)
+}
+
+// Lookup searches for a symbol named symName in plugin p.
+// A symbol is any exported variable or function.
+// It reports an error if the symbol is not found.
+// It is safe for concurrent use by multiple goroutines.
+func (p *Plugin) Lookup(symName string) (Symbol, error) {
+	return lookup(p, symName)
+}
+
+// A Symbol is a pointer to a variable or function.
+//
+// For example, a plugin defined as
+//
+//	package main
+//
+//	// // No C code needed.
+//	import "C"
+//
+//	import "fmt"
+//
+//	var V int
+//
+//	func F() { fmt.Printf("Hello, number %d\n", V) }
+//
+// may be loaded with the Open function and then the exported package
+// symbols V and F can be accessed
+//
+//	p, err := plugin.Open("plugin_name.so")
+//	if err != nil {
+//		panic(err)
+//	}
+//	v, err := p.Lookup("V")
+//	if err != nil {
+//		panic(err)
+//	}
+//	f, err := p.Lookup("F")
+//	if err != nil {
+//		panic(err)
+//	}
+//	*v.(*int) = 7
+//	f.(func())() // prints "Hello, number 7"
+type Symbol interface{}
diff --git a/src/plugin/plugin_dlopen.go b/src/plugin/plugin_dlopen.go
new file mode 100644
index 0000000..c5b0a47
--- /dev/null
+++ b/src/plugin/plugin_dlopen.go
@@ -0,0 +1,138 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,cgo darwin,cgo
+
+package plugin
+
+/*
+#cgo linux LDFLAGS: -ldl
+#include <dlfcn.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <stdio.h>
+
+static uintptr_t pluginOpen(const char* path, char** err) {
+	void* h = dlopen(path, RTLD_NOW|RTLD_GLOBAL);
+	if (h == NULL) {
+		*err = (char*)dlerror();
+	}
+	return (uintptr_t)h;
+}
+
+static void* pluginLookup(uintptr_t h, const char* name, char** err) {
+	void* r = dlsym((void*)h, name);
+	if (r == NULL) {
+		*err = (char*)dlerror();
+	}
+	return r;
+}
+*/
+import "C"
+
+import (
+	"errors"
+	"sync"
+	"unsafe"
+)
+
+func open(name string) (*Plugin, error) {
+	cPath := (*C.char)(C.malloc(C.PATH_MAX + 1))
+	defer C.free(unsafe.Pointer(cPath))
+
+	cRelName := C.CString(name)
+	defer C.free(unsafe.Pointer(cRelName))
+	if C.realpath(cRelName, cPath) == nil {
+		return nil, errors.New("plugin.Open(" + name + "): realpath failed")
+	}
+
+	filepath := C.GoString(cPath)
+
+	pluginsMu.Lock()
+	if p := plugins[filepath]; p != nil {
+		pluginsMu.Unlock()
+		<-p.loaded
+		return p, nil
+	}
+	var cErr *C.char
+	h := C.pluginOpen(cPath, &cErr)
+	if h == 0 {
+		pluginsMu.Unlock()
+		return nil, errors.New("plugin.Open: " + C.GoString(cErr))
+	}
+	// TODO(crawshaw): look for plugin note, confirm it is a Go plugin
+	// and it was built with the correct toolchain.
+	if len(name) > 3 && name[len(name)-3:] == ".so" {
+		name = name[:len(name)-3]
+	}
+
+	pluginpath, syms, mismatchpkg := lastmoduleinit()
+	if mismatchpkg != "" {
+		pluginsMu.Unlock()
+		return nil, errors.New("plugin.Open: plugin was built with a different version of package " + mismatchpkg)
+	}
+	if plugins == nil {
+		plugins = make(map[string]*Plugin)
+	}
+	// This function can be called from the init function of a plugin.
+	// Drop a placeholder in the map so subsequent opens can wait on it.
+	p := &Plugin{
+		pluginpath: pluginpath,
+		loaded:     make(chan struct{}),
+		syms:       syms,
+	}
+	plugins[filepath] = p
+	pluginsMu.Unlock()
+
+	initStr := C.CString(pluginpath + ".init")
+	initFuncPC := C.pluginLookup(h, initStr, &cErr)
+	C.free(unsafe.Pointer(initStr))
+	if initFuncPC != nil {
+		initFuncP := &initFuncPC
+		initFunc := *(*func())(unsafe.Pointer(&initFuncP))
+		initFunc()
+	}
+
+	// Fill out the value of each plugin symbol.
+	for symName, sym := range syms {
+		isFunc := symName[0] == '.'
+		if isFunc {
+			delete(syms, symName)
+			symName = symName[1:]
+		}
+
+		cname := C.CString(pluginpath + "." + symName)
+		p := C.pluginLookup(h, cname, &cErr)
+		C.free(unsafe.Pointer(cname))
+		if p == nil {
+			return nil, errors.New("plugin.Open: could not find symbol " + symName + ": " + C.GoString(cErr))
+		}
+		valp := (*[2]unsafe.Pointer)(unsafe.Pointer(&sym))
+		if isFunc {
+			(*valp)[1] = unsafe.Pointer(&p)
+		} else {
+			(*valp)[1] = p
+		}
+		syms[symName] = sym
+	}
+	close(p.loaded)
+	return p, nil
+}
+
+func lookup(p *Plugin, symName string) (Symbol, error) {
+	if s := p.syms[symName]; s != nil {
+		return s, nil
+	}
+	return nil, errors.New("plugin: symbol " + symName + " not found in plugin " + p.pluginpath)
+}
+
+var (
+	pluginsMu sync.Mutex
+	plugins   map[string]*Plugin
+)
+
+// lastmoduleinit is defined in package runtime
+func lastmoduleinit() (pluginpath string, syms map[string]interface{}, mismatchpkg string)
diff --git a/src/plugin/plugin_stubs.go b/src/plugin/plugin_stubs.go
new file mode 100644
index 0000000..f0bcb4a
--- /dev/null
+++ b/src/plugin/plugin_stubs.go
@@ -0,0 +1,17 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !linux,!darwin !cgo
+
+package plugin
+
+import "errors"
+
+func lookup(p *Plugin, symName string) (interface{}, error) {
+	return nil, errors.New("plugin: not implemented")
+}
+
+func open(name string) (*Plugin, error) {
+	return nil, errors.New("plugin: not implemented")
+}
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 780799c..033a181 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -648,6 +648,20 @@ var (
 
 type self struct{}
 
+type Loop *Loop
+type Loopy interface{}
+
+var loop1, loop2 Loop
+var loopy1, loopy2 Loopy
+
+func init() {
+	loop1 = &loop2
+	loop2 = &loop1
+
+	loopy1 = &loopy2
+	loopy2 = &loopy1
+}
+
 var deepEqualTests = []DeepEqualTest{
 	// Equalities
 	{nil, nil, true},
@@ -706,6 +720,12 @@ var deepEqualTests = []DeepEqualTest{
 	{&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false},
 	{Basic{1, 0.5}, NotBasic{1, 0.5}, false},
 	{map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false},
+
+	// Possible loops.
+	{&loop1, &loop1, true},
+	{&loop1, &loop2, true},
+	{&loopy1, &loopy1, true},
+	{&loopy1, &loopy2, true},
 }
 
 func TestDeepEqual(t *testing.T) {
@@ -1535,6 +1555,34 @@ func BenchmarkCall(b *testing.B) {
 	})
 }
 
+func BenchmarkCallArgCopy(b *testing.B) {
+	byteArray := func(n int) Value {
+		return Zero(ArrayOf(n, TypeOf(byte(0))))
+	}
+	sizes := [...]struct {
+		fv  Value
+		arg Value
+	}{
+		{ValueOf(func(a [128]byte) {}), byteArray(128)},
+		{ValueOf(func(a [256]byte) {}), byteArray(256)},
+		{ValueOf(func(a [1024]byte) {}), byteArray(1024)},
+		{ValueOf(func(a [4096]byte) {}), byteArray(4096)},
+		{ValueOf(func(a [65536]byte) {}), byteArray(65536)},
+	}
+	for _, size := range sizes {
+		bench := func(b *testing.B) {
+			args := []Value{size.arg}
+			b.SetBytes(int64(size.arg.Len()))
+			b.ResetTimer()
+			for i := 0; i < b.N; i++ {
+				size.fv.Call(args)
+			}
+		}
+		name := fmt.Sprintf("size=%v", size.arg.Len())
+		b.Run(name, bench)
+	}
+}
+
 func TestMakeFunc(t *testing.T) {
 	f := dummy
 	fv := MakeFunc(TypeOf(f), func(in []Value) []Value { return in })
@@ -2277,25 +2325,39 @@ func TestFieldPkgPath(t *testing.T) {
 		unexported string
 		OtherPkgFields
 	}{})
-	for _, test := range []struct {
+
+	type pkgpathTest struct {
 		index     []int
 		pkgPath   string
 		anonymous bool
-	}{
+	}
+
+	checkPkgPath := func(name string, s []pkgpathTest) {
+		for _, test := range s {
+			f := typ.FieldByIndex(test.index)
+			if got, want := f.PkgPath, test.pkgPath; got != want {
+				t.Errorf("%s: Field(%d).PkgPath = %q, want %q", name, test.index, got, want)
+			}
+			if got, want := f.Anonymous, test.anonymous; got != want {
+				t.Errorf("%s: Field(%d).Anonymous = %v, want %v", name, test.index, got, want)
+			}
+		}
+	}
+
+	checkPkgPath("testStruct", []pkgpathTest{
 		{[]int{0}, "", false},             // Exported
 		{[]int{1}, "reflect_test", false}, // unexported
 		{[]int{2}, "", true},              // OtherPkgFields
 		{[]int{2, 0}, "", false},          // OtherExported
 		{[]int{2, 1}, "reflect", false},   // otherUnexported
-	} {
-		f := typ.FieldByIndex(test.index)
-		if got, want := f.PkgPath, test.pkgPath; got != want {
-			t.Errorf("Field(%d).PkgPath = %q, want %q", test.index, got, want)
-		}
-		if got, want := f.Anonymous, test.anonymous; got != want {
-			t.Errorf("Field(%d).Anonymous = %v, want %v", test.index, got, want)
-		}
-	}
+	})
+
+	type localOtherPkgFields OtherPkgFields
+	typ = TypeOf(localOtherPkgFields{})
+	checkPkgPath("localOtherPkgFields", []pkgpathTest{
+		{[]int{0}, "", false},        // OtherExported
+		{[]int{1}, "reflect", false}, // otherUnexported
+	})
 }
 
 func TestVariadicType(t *testing.T) {
@@ -3070,6 +3132,9 @@ func ReadWriterV(x io.ReadWriter) Value {
 }
 
 type Empty struct{}
+type MyStruct struct {
+	x int `some:"tag"`
+}
 type MyString string
 type MyBytes []byte
 type MyRunes []int32
@@ -3381,6 +3446,35 @@ var convertTests = []struct {
 	{V((func())(nil)), V(MyFunc(nil))},
 	{V((MyFunc)(nil)), V((func())(nil))},
 
+	// structs with different tags
+	{V(struct {
+		x int `some:"foo"`
+	}{}), V(struct {
+		x int `some:"bar"`
+	}{})},
+
+	{V(struct {
+		x int `some:"bar"`
+	}{}), V(struct {
+		x int `some:"foo"`
+	}{})},
+
+	{V(MyStruct{}), V(struct {
+		x int `some:"foo"`
+	}{})},
+
+	{V(struct {
+		x int `some:"foo"`
+	}{}), V(MyStruct{})},
+
+	{V(MyStruct{}), V(struct {
+		x int `some:"bar"`
+	}{})},
+
+	{V(struct {
+		x int `some:"bar"`
+	}{}), V(MyStruct{})},
+
 	// can convert *byte and *MyByte
 	{V((*byte)(nil)), V((*MyByte)(nil))},
 	{V((*MyByte)(nil)), V((*byte)(nil))},
@@ -3965,6 +4059,38 @@ func TestStructOf(t *testing.T) {
 		}
 	}
 
+	// Check size and alignment with a trailing zero-sized field.
+	st = StructOf([]StructField{
+		{
+			Name: "F1",
+			Type: TypeOf(byte(0)),
+		},
+		{
+			Name: "F2",
+			Type: TypeOf([0]*byte{}),
+		},
+	})
+	stt = TypeOf(struct {
+		G1 byte
+		G2 [0]*byte
+	}{})
+	if st.Size() != stt.Size() {
+		t.Errorf("constructed zero-padded struct size = %v, want %v", st.Size(), stt.Size())
+	}
+	if st.Align() != stt.Align() {
+		t.Errorf("constructed zero-padded struct align = %v, want %v", st.Align(), stt.Align())
+	}
+	if st.FieldAlign() != stt.FieldAlign() {
+		t.Errorf("constructed zero-padded struct field align = %v, want %v", st.FieldAlign(), stt.FieldAlign())
+	}
+	for i := 0; i < st.NumField(); i++ {
+		o1 := st.Field(i).Offset
+		o2 := stt.Field(i).Offset
+		if o1 != o2 {
+			t.Errorf("constructed zero-padded struct field %v offset = %v, want %v", i, o1, o2)
+		}
+	}
+
 	// check duplicate names
 	shouldPanic(func() {
 		StructOf([]StructField{
@@ -5752,3 +5878,101 @@ func BenchmarkNew(b *testing.B) {
 		New(v)
 	}
 }
+
+func TestSwapper(t *testing.T) {
+	type I int
+	var a, b, c I
+	type pair struct {
+		x, y int
+	}
+	type pairPtr struct {
+		x, y int
+		p    *I
+	}
+	type S string
+
+	tests := []struct {
+		in   interface{}
+		i, j int
+		want interface{}
+	}{
+		{
+			in:   []int{1, 20, 300},
+			i:    0,
+			j:    2,
+			want: []int{300, 20, 1},
+		},
+		{
+			in:   []uintptr{1, 20, 300},
+			i:    0,
+			j:    2,
+			want: []uintptr{300, 20, 1},
+		},
+		{
+			in:   []int16{1, 20, 300},
+			i:    0,
+			j:    2,
+			want: []int16{300, 20, 1},
+		},
+		{
+			in:   []int8{1, 20, 100},
+			i:    0,
+			j:    2,
+			want: []int8{100, 20, 1},
+		},
+		{
+			in:   []*I{&a, &b, &c},
+			i:    0,
+			j:    2,
+			want: []*I{&c, &b, &a},
+		},
+		{
+			in:   []string{"eric", "sergey", "larry"},
+			i:    0,
+			j:    2,
+			want: []string{"larry", "sergey", "eric"},
+		},
+		{
+			in:   []S{"eric", "sergey", "larry"},
+			i:    0,
+			j:    2,
+			want: []S{"larry", "sergey", "eric"},
+		},
+		{
+			in:   []pair{{1, 2}, {3, 4}, {5, 6}},
+			i:    0,
+			j:    2,
+			want: []pair{{5, 6}, {3, 4}, {1, 2}},
+		},
+		{
+			in:   []pairPtr{{1, 2, &a}, {3, 4, &b}, {5, 6, &c}},
+			i:    0,
+			j:    2,
+			want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}},
+		},
+	}
+	for i, tt := range tests {
+		inStr := fmt.Sprint(tt.in)
+		Swapper(tt.in)(tt.i, tt.j)
+		if !DeepEqual(tt.in, tt.want) {
+			t.Errorf("%d. swapping %v and %v of %v = %v; want %v", i, tt.i, tt.j, inStr, tt.in, tt.want)
+		}
+	}
+}
+
+// TestUnaddressableField tests that the reflect package will not allow
+// a type from another package to be used as a named type with an
+// unexported field.
+//
+// This ensures that unexported fields cannot be modified by other packages.
+func TestUnaddressableField(t *testing.T) {
+	var b Buffer // type defined in reflect, a different package
+	var localBuffer struct {
+		buf []byte
+	}
+	lv := ValueOf(&localBuffer).Elem()
+	rv := ValueOf(b)
+	shouldPanic(func() {
+		lv.Set(rv)
+	})
+}
diff --git a/src/reflect/asm_mipsx.s b/src/reflect/asm_mipsx.s
new file mode 100644
index 0000000..b6df4e6
--- /dev/null
+++ b/src/reflect/asm_mipsx.s
@@ -0,0 +1,34 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "textflag.h"
+#include "funcdata.h"
+
+#define	REGCTXT	R22
+
+// makeFuncStub is the code half of the function returned by MakeFunc.
+// See the comment on the declaration of makeFuncStub in makefunc.go
+// for more details.
+// No arg size here, runtime pulls arg map out of the func value.
+TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$8
+	NO_LOCAL_POINTERS
+	MOVW	REGCTXT, 4(R29)
+	MOVW	$argframe+0(FP), R1
+	MOVW	R1, 8(R29)
+	JAL	·callReflect(SB)
+	RET
+
+// methodValueCall is the code half of the function returned by makeMethodValue.
+// See the comment on the declaration of methodValueCall in makefunc.go
+// for more details.
+// No arg size here; runtime pulls arg map out of the func value.
+TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$8
+	NO_LOCAL_POINTERS
+	MOVW	REGCTXT, 4(R29)
+	MOVW	$argframe+0(FP), R1
+	MOVW	R1, 8(R29)
+	JAL	·callMethod(SB)
+	RET
diff --git a/src/reflect/deepequal.go b/src/reflect/deepequal.go
index 9770358..f3fd704 100644
--- a/src/reflect/deepequal.go
+++ b/src/reflect/deepequal.go
@@ -30,9 +30,13 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
 	}
 
 	// if depth > 10 { panic("deepValueEqual") }	// for debugging
+
+	// We want to avoid putting more in the visited map than we need to.
+	// For any possible reference cycle that might be encountered,
+	// hard(t) needs to return true for at least one of the types in the cycle.
 	hard := func(k Kind) bool {
 		switch k {
-		case Array, Map, Slice, Struct:
+		case Map, Slice, Ptr, Interface:
 			return true
 		}
 		return false
@@ -142,8 +146,9 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
 //
 // Interface values are deeply equal if they hold deeply equal concrete values.
 //
-// Map values are deeply equal if they are the same map object
-// or if they have the same length and their corresponding keys
+// Map values are deeply equal when all of the following are true:
+// they are both nil or both non-nil, they have the same length,
+// and either they are the same map object or their corresponding keys
 // (matched using Go equality) map to deeply equal values.
 //
 // Pointer values are deeply equal if they are equal using Go's == operator
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index 2cc1530..ffd1104 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -113,3 +113,7 @@ func IsExported(t Type) bool {
 func ResolveReflectName(s string) {
 	resolveReflectName(newName(s, "", "", false))
 }
+
+type Buffer struct {
+	buf []byte
+}
diff --git a/src/reflect/makefunc.go b/src/reflect/makefunc.go
index ad2ebd0..a7efeb8 100644
--- a/src/reflect/makefunc.go
+++ b/src/reflect/makefunc.go
@@ -70,6 +70,8 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
 // word in the passed-in argument frame.
 func makeFuncStub()
 
+// This type is partially duplicated as runtime.reflectMethodValue.
+// Any changes should be reflected in both.
 type methodValue struct {
 	fn     uintptr
 	stack  *bitVector // stack bitmap for args - offset known to runtime
diff --git a/src/reflect/swapper.go b/src/reflect/swapper.go
new file mode 100644
index 0000000..5441cb0
--- /dev/null
+++ b/src/reflect/swapper.go
@@ -0,0 +1,74 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package reflect
+
+import "unsafe"
+
+// Swapper returns a function that swaps the elements in the provided
+// slice.
+//
+// Swapper panics if the provided interface is not a slice.
+func Swapper(slice interface{}) func(i, j int) {
+	v := ValueOf(slice)
+	if v.Kind() != Slice {
+		panic(&ValueError{Method: "Swapper", Kind: v.Kind()})
+	}
+	// Fast path for slices of size 0 and 1. Nothing to swap.
+	switch v.Len() {
+	case 0:
+		return func(i, j int) { panic("reflect: slice index out of range") }
+	case 1:
+		return func(i, j int) {
+			if i != 0 || j != 0 {
+				panic("reflect: slice index out of range")
+			}
+		}
+	}
+
+	typ := v.Type().Elem().(*rtype)
+	size := typ.Size()
+	hasPtr := typ.kind&kindNoPointers == 0
+
+	// Some common & small cases, without using memmove:
+	if hasPtr {
+		if size == ptrSize {
+			ps := *(*[]unsafe.Pointer)(v.ptr)
+			return func(i, j int) { ps[i], ps[j] = ps[j], ps[i] }
+		}
+		if typ.Kind() == String {
+			ss := *(*[]string)(v.ptr)
+			return func(i, j int) { ss[i], ss[j] = ss[j], ss[i] }
+		}
+	} else {
+		switch size {
+		case 8:
+			is := *(*[]int64)(v.ptr)
+			return func(i, j int) { is[i], is[j] = is[j], is[i] }
+		case 4:
+			is := *(*[]int32)(v.ptr)
+			return func(i, j int) { is[i], is[j] = is[j], is[i] }
+		case 2:
+			is := *(*[]int16)(v.ptr)
+			return func(i, j int) { is[i], is[j] = is[j], is[i] }
+		case 1:
+			is := *(*[]int8)(v.ptr)
+			return func(i, j int) { is[i], is[j] = is[j], is[i] }
+		}
+	}
+
+	s := (*sliceHeader)(v.ptr)
+	tmp := unsafe_New(typ) // swap scratch space
+
+	return func(i, j int) {
+		if uint(i) >= uint(s.Len) || uint(j) >= uint(s.Len) {
+			panic("reflect: slice index out of range")
+		}
+		val1 := arrayAt(s.Data, i, size)
+		val2 := arrayAt(s.Data, j, size)
+		typedmemmove(typ, tmp, val1)
+		typedmemmove(typ, val1, val2)
+		typedmemmove(typ, val2, tmp)
+	}
+}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 8916710..9d6e7a6 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -29,6 +29,9 @@ import (
 // Use the Kind method to find out the kind of type before
 // calling kind-specific methods. Calling a method
 // inappropriate to the kind of type causes a run-time panic.
+//
+// Type values are comparable, such as with the == operator.
+// Two Type values are equal if they represent identical types.
 type Type interface {
 	// Methods applicable to all types.
 
@@ -60,7 +63,7 @@ type Type interface {
 	// method signature, without a receiver, and the Func field is nil.
 	MethodByName(string) (Method, bool)
 
-	// NumMethod returns the number of methods in the type's method set.
+	// NumMethod returns the number of exported methods in the type's method set.
 	NumMethod() int
 
 	// Name returns the type's name within its package.
@@ -80,7 +83,7 @@ type Type interface {
 	// String returns a string representation of the type.
 	// The string representation may use shortened package names
 	// (e.g., base64 instead of "encoding/base64") and is not
-	// guaranteed to be unique among types. To test for equality,
+	// guaranteed to be unique among types. To test for type identity,
 	// compare the Types directly.
 	String() string
 
@@ -153,9 +156,18 @@ type Type interface {
 	// and a boolean indicating if the field was found.
 	FieldByName(name string) (StructField, bool)
 
-	// FieldByNameFunc returns the first struct field with a name
+	// FieldByNameFunc returns the struct field with a name
 	// that satisfies the match function and a boolean indicating if
 	// the field was found.
+	//
+	// FieldByNameFunc considers the fields in the struct itself
+	// and then the fields in any anonymous structs, in breadth first order,
+	// stopping at the shallowest nesting depth containing one or more
+	// fields satisfying the match function. If multiple fields at that depth
+	// satisfy the match function, they cancel each other
+	// and FieldByNameFunc returns no match.
+	// This behavior mirrors Go's handling of name lookup in
+	// structs containing anonymous fields.
 	FieldByNameFunc(match func(string) bool) (StructField, bool)
 
 	// In returns the type of a function type's i'th input parameter.
@@ -1144,7 +1156,7 @@ func (tag StructTag) Get(key string) string {
 // the value returned by Lookup is unspecified.
 func (tag StructTag) Lookup(key string) (value string, ok bool) {
 	// When modifying this code, also update the validateStructTag code
-	// in golang.org/x/tools/cmd/vet/structtag.go.
+	// in cmd/vet/structtag.go.
 
 	for tag != "" {
 		// Skip leading space.
@@ -1214,8 +1226,10 @@ func (t *structType) Field(i int) (f StructField) {
 		f.Anonymous = true
 	}
 	if !p.name.isExported() {
-		// Fields never have an import path in their name.
-		f.PkgPath = t.pkgPath.name()
+		f.PkgPath = p.name.pkgPath()
+		if f.PkgPath == "" {
+			f.PkgPath = t.pkgPath.name()
+		}
 	}
 	if tag := p.name.tag(); tag != "" {
 		f.Tag = StructTag(tag)
@@ -1450,25 +1464,24 @@ func (t *rtype) ptrTo() *rtype {
 
 	// Create a new ptrType starting with the description
 	// of an *unsafe.Pointer.
-	p = new(ptrType)
 	var iptr interface{} = (*unsafe.Pointer)(nil)
 	prototype := *(**ptrType)(unsafe.Pointer(&iptr))
-	*p = *prototype
+	pp := *prototype
 
-	p.str = resolveReflectName(newName(s, "", "", false))
+	pp.str = resolveReflectName(newName(s, "", "", false))
 
 	// For the type structures linked into the binary, the
 	// compiler provides a good hash of the string.
 	// Create a good hash for the new string by using
 	// the FNV-1 hash's mixing function to combine the
 	// old hash and the new "*".
-	p.hash = fnv1(t.hash, '*')
+	pp.hash = fnv1(t.hash, '*')
 
-	p.elem = t
+	pp.elem = t
 
-	ptrMap.m[t] = p
+	ptrMap.m[t] = &pp
 	ptrMap.Unlock()
-	return &p.rtype
+	return &pp.rtype
 }
 
 // fnv1 incorporates the list of bytes into the hash x using the FNV-1 hash function.
@@ -1582,10 +1595,22 @@ func directlyAssignable(T, V *rtype) bool {
 	}
 
 	// x's type T and V must  have identical underlying types.
-	return haveIdenticalUnderlyingType(T, V)
+	return haveIdenticalUnderlyingType(T, V, true)
 }
 
-func haveIdenticalUnderlyingType(T, V *rtype) bool {
+func haveIdenticalType(T, V Type, cmpTags bool) bool {
+	if cmpTags {
+		return T == V
+	}
+
+	if T.Name() != V.Name() || T.Kind() != V.Kind() {
+		return false
+	}
+
+	return haveIdenticalUnderlyingType(T.common(), V.common(), false)
+}
+
+func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
 	if T == V {
 		return true
 	}
@@ -1604,18 +1629,18 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool {
 	// Composite types.
 	switch kind {
 	case Array:
-		return T.Elem() == V.Elem() && T.Len() == V.Len()
+		return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
 
 	case Chan:
 		// Special case:
 		// x is a bidirectional channel value, T is a channel type,
 		// and x's type V and T have identical element types.
-		if V.ChanDir() == BothDir && T.Elem() == V.Elem() {
+		if V.ChanDir() == BothDir && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) {
 			return true
 		}
 
 		// Otherwise continue test for identical underlying type.
-		return V.ChanDir() == T.ChanDir() && T.Elem() == V.Elem()
+		return V.ChanDir() == T.ChanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
 
 	case Func:
 		t := (*funcType)(unsafe.Pointer(T))
@@ -1624,12 +1649,12 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool {
 			return false
 		}
 		for i := 0; i < t.NumIn(); i++ {
-			if t.In(i) != v.In(i) {
+			if !haveIdenticalType(t.In(i), v.In(i), cmpTags) {
 				return false
 			}
 		}
 		for i := 0; i < t.NumOut(); i++ {
-			if t.Out(i) != v.Out(i) {
+			if !haveIdenticalType(t.Out(i), v.Out(i), cmpTags) {
 				return false
 			}
 		}
@@ -1646,10 +1671,10 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool {
 		return false
 
 	case Map:
-		return T.Key() == V.Key() && T.Elem() == V.Elem()
+		return haveIdenticalType(T.Key(), V.Key(), cmpTags) && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
 
 	case Ptr, Slice:
-		return T.Elem() == V.Elem()
+		return haveIdenticalType(T.Elem(), V.Elem(), cmpTags)
 
 	case Struct:
 		t := (*structType)(unsafe.Pointer(T))
@@ -1663,15 +1688,28 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool {
 			if tf.name.name() != vf.name.name() {
 				return false
 			}
-			if tf.typ != vf.typ {
+			if !haveIdenticalType(tf.typ, vf.typ, cmpTags) {
 				return false
 			}
-			if tf.name.tag() != vf.name.tag() {
+			if cmpTags && tf.name.tag() != vf.name.tag() {
 				return false
 			}
 			if tf.offset != vf.offset {
 				return false
 			}
+			if !tf.name.isExported() {
+				tp := tf.name.pkgPath()
+				if tp == "" {
+					tp = t.pkgPath.name()
+				}
+				vp := vf.name.pkgPath()
+				if vp == "" {
+					vp = v.pkgPath.name()
+				}
+				if tp != vp {
+					return false
+				}
+			}
 		}
 		return true
 	}
@@ -1846,8 +1884,7 @@ func ChanOf(dir ChanDir, t Type) Type {
 	// Make a channel type.
 	var ichan interface{} = (chan unsafe.Pointer)(nil)
 	prototype := *(**chanType)(unsafe.Pointer(&ichan))
-	ch := new(chanType)
-	*ch = *prototype
+	ch := *prototype
 	ch.tflag = 0
 	ch.dir = uintptr(dir)
 	ch.str = resolveReflectName(newName(s, "", "", false))
@@ -1890,8 +1927,7 @@ func MapOf(key, elem Type) Type {
 
 	// Make a map type.
 	var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil)
-	mt := new(mapType)
-	*mt = **(**mapType)(unsafe.Pointer(&imap))
+	mt := **(**mapType)(unsafe.Pointer(&imap))
 	mt.str = resolveReflectName(newName(s, "", "", false))
 	mt.tflag = 0
 	mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash))
@@ -2024,7 +2060,7 @@ func FuncOf(in, out []Type, variadic bool) Type {
 	// Look in cache.
 	funcLookupCache.RLock()
 	for _, t := range funcLookupCache.m[hash] {
-		if haveIdenticalUnderlyingType(&ft.rtype, t) {
+		if haveIdenticalUnderlyingType(&ft.rtype, t, true) {
 			funcLookupCache.RUnlock()
 			return t
 		}
@@ -2038,7 +2074,7 @@ func FuncOf(in, out []Type, variadic bool) Type {
 		funcLookupCache.m = make(map[uint32][]*rtype)
 	}
 	for _, t := range funcLookupCache.m[hash] {
-		if haveIdenticalUnderlyingType(&ft.rtype, t) {
+		if haveIdenticalUnderlyingType(&ft.rtype, t, true) {
 			return t
 		}
 	}
@@ -2046,7 +2082,7 @@ func FuncOf(in, out []Type, variadic bool) Type {
 	// Look in known types for the same string representation.
 	str := funcStr(ft)
 	for _, tt := range typesByString(str) {
-		if haveIdenticalUnderlyingType(&ft.rtype, tt) {
+		if haveIdenticalUnderlyingType(&ft.rtype, tt, true) {
 			funcLookupCache.m[hash] = append(funcLookupCache.m[hash], tt)
 			return tt
 		}
@@ -2242,15 +2278,16 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
 		}
 	}
 
-	b := new(rtype)
-	b.align = ptrSize
+	b := &rtype{
+		align:   ptrSize,
+		size:    size,
+		kind:    kind,
+		ptrdata: ptrdata,
+		gcdata:  gcdata,
+	}
 	if overflowPad > 0 {
 		b.align = 8
 	}
-	b.size = size
-	b.ptrdata = ptrdata
-	b.kind = kind
-	b.gcdata = gcdata
 	s := "bucket(" + ktyp.String() + "," + etyp.String() + ")"
 	b.str = resolveReflectName(newName(s, "", "", false))
 	return b
@@ -2279,8 +2316,7 @@ func SliceOf(t Type) Type {
 	// Make a slice type.
 	var islice interface{} = ([]unsafe.Pointer)(nil)
 	prototype := *(**sliceType)(unsafe.Pointer(&islice))
-	slice := new(sliceType)
-	*slice = *prototype
+	slice := *prototype
 	slice.tflag = 0
 	slice.str = resolveReflectName(newName(s, "", "", false))
 	slice.hash = fnv1(typ.hash, '[')
@@ -2364,6 +2400,7 @@ func StructOf(fields []StructField) Type {
 		hasGCProg = false // records whether a struct-field type has a GCProg
 	)
 
+	lastzero := uintptr(0)
 	repr = append(repr, "struct {"...)
 	for i, field := range fields {
 		if field.Type == nil {
@@ -2534,9 +2571,22 @@ func StructOf(fields []StructField) Type {
 		}
 		size = f.offset + ft.size
 
+		if ft.size == 0 {
+			lastzero = size
+		}
+
 		fs[i] = f
 	}
 
+	if size > 0 && lastzero == size {
+		// This is a non-zero sized struct that ends in a
+		// zero-sized field. We add an extra byte of padding,
+		// to ensure that taking the address of the final
+		// zero-sized field can't manufacture a pointer to the
+		// next object in the heap. See issue 9401.
+		size++
+	}
+
 	var typ *structType
 	var ut *uncommonType
 	var typPin interface {
@@ -2599,7 +2649,7 @@ func StructOf(fields []StructField) Type {
 	structLookupCache.RLock()
 	for _, st := range structLookupCache.m[hash] {
 		t := st.common()
-		if haveIdenticalUnderlyingType(&typ.rtype, t) {
+		if haveIdenticalUnderlyingType(&typ.rtype, t, true) {
 			structLookupCache.RUnlock()
 			return t
 		}
@@ -2616,14 +2666,14 @@ func StructOf(fields []StructField) Type {
 	}
 	for _, st := range structLookupCache.m[hash] {
 		t := st.common()
-		if haveIdenticalUnderlyingType(&typ.rtype, t) {
+		if haveIdenticalUnderlyingType(&typ.rtype, t, true) {
 			return t
 		}
 	}
 
 	// Look in known types.
 	for _, t := range typesByString(str) {
-		if haveIdenticalUnderlyingType(&typ.rtype, t) {
+		if haveIdenticalUnderlyingType(&typ.rtype, t, true) {
 			// even if 't' wasn't a structType with methods, we should be ok
 			// as the 'u uncommonType' field won't be accessed except when
 			// tflag&tflagUncommon is set.
@@ -2638,6 +2688,7 @@ func StructOf(fields []StructField) Type {
 	typ.size = size
 	typ.align = typalign
 	typ.fieldAlign = typalign
+	typ.ptrToThis = 0
 	if len(methods) > 0 {
 		typ.tflag |= tflagUncommon
 	}
@@ -2824,8 +2875,7 @@ func ArrayOf(count int, elem Type) Type {
 	// Make an array type.
 	var iarray interface{} = [1]unsafe.Pointer{}
 	prototype := *(**arrayType)(unsafe.Pointer(&iarray))
-	array := new(arrayType)
-	*array = *prototype
+	array := *prototype
 	array.str = resolveReflectName(newName(s, "", "", false))
 	array.hash = fnv1(typ.hash, '[')
 	for n := uint32(count); n > 0; n >>= 8 {
@@ -3065,13 +3115,14 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
 	offset += -offset & (ptrSize - 1)
 
 	// build dummy rtype holding gc program
-	x := new(rtype)
-	x.align = ptrSize
+	x := &rtype{
+		align:   ptrSize,
+		size:    offset,
+		ptrdata: uintptr(ptrmap.n) * ptrSize,
+	}
 	if runtime.GOARCH == "amd64p32" {
 		x.align = 8
 	}
-	x.size = offset
-	x.ptrdata = uintptr(ptrmap.n) * ptrSize
 	if ptrmap.n > 0 {
 		x.gcdata = &ptrmap.data[0]
 	} else {
diff --git a/src/reflect/value.go b/src/reflect/value.go
index e6b846e..042414f 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -440,14 +440,16 @@ func (v Value) call(op string, in []Value) []Value {
 
 	var ret []Value
 	if nout == 0 {
-		memclr(args, frametype.size)
+		// This is untyped because the frame is really a
+		// stack, even though it's a heap object.
+		memclrNoHeapPointers(args, frametype.size)
 		framePool.Put(args)
 	} else {
 		// Zero the now unused input area of args,
 		// because the Values returned by this function contain pointers to the args object,
 		// and will thus keep the args object alive indefinitely.
-		memclr(args, retOffset)
-		// Copy return values out of args.
+		memclrNoHeapPointers(args, retOffset)
+		// Wrap Values around return values in args.
 		ret = make([]Value, nout)
 		off = retOffset
 		for i := 0; i < nout; i++ {
@@ -644,7 +646,9 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer) {
 		retOffset,
 		frametype.size-retOffset)
 
-	memclr(args, frametype.size)
+	// This is untyped because the frame is really a stack, even
+	// though it's a heap object.
+	memclrNoHeapPointers(args, frametype.size)
 	framePool.Put(args)
 }
 
@@ -2239,14 +2243,14 @@ func convertOp(dst, src *rtype) func(Value, Type) Value {
 	}
 
 	// dst and src have same underlying type.
-	if haveIdenticalUnderlyingType(dst, src) {
+	if haveIdenticalUnderlyingType(dst, src, false) {
 		return cvtDirect
 	}
 
 	// dst and src are unnamed pointer types with same underlying base type.
 	if dst.Kind() == Ptr && dst.Name() == "" &&
 		src.Kind() == Ptr && src.Name() == "" &&
-		haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common()) {
+		haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common(), false) {
 		return cvtDirect
 	}
 
@@ -2508,7 +2512,7 @@ func typedmemmovepartial(t *rtype, dst, src unsafe.Pointer, off, size uintptr)
 func typedslicecopy(elemType *rtype, dst, src sliceHeader) int
 
 //go:noescape
-func memclr(ptr unsafe.Pointer, n uintptr)
+func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
 
 // Dummy annotation marking that the value x escapes,
 // for use in cases where the reflect code is so clever that
diff --git a/src/regexp/all_test.go b/src/regexp/all_test.go
index 88391ff..beb46e7 100644
--- a/src/regexp/all_test.go
+++ b/src/regexp/all_test.go
@@ -11,7 +11,7 @@ import (
 	"testing"
 )
 
-var good_re = []string{
+var goodRe = []string{
 	``,
 	`.`,
 	`^.$`,
@@ -36,7 +36,7 @@ type stringError struct {
 	err string
 }
 
-var bad_re = []stringError{
+var badRe = []stringError{
 	{`*`, "missing argument to repetition operator: `*`"},
 	{`+`, "missing argument to repetition operator: `+`"},
 	{`?`, "missing argument to repetition operator: `?`"},
@@ -64,14 +64,14 @@ func compileTest(t *testing.T, expr string, error string) *Regexp {
 }
 
 func TestGoodCompile(t *testing.T) {
-	for i := 0; i < len(good_re); i++ {
-		compileTest(t, good_re[i], "")
+	for i := 0; i < len(goodRe); i++ {
+		compileTest(t, goodRe[i], "")
 	}
 }
 
 func TestBadCompile(t *testing.T) {
-	for i := 0; i < len(bad_re); i++ {
-		compileTest(t, bad_re[i].re, bad_re[i].err)
+	for i := 0; i < len(badRe); i++ {
+		compileTest(t, badRe[i].re, badRe[i].err)
 	}
 }
 
@@ -512,6 +512,32 @@ func TestSplit(t *testing.T) {
 	}
 }
 
+// The following sequence of Match calls used to panic. See issue #12980.
+func TestParseAndCompile(t *testing.T) {
+	expr := "a$"
+	s := "a\nb"
+
+	for i, tc := range []struct {
+		reFlags  syntax.Flags
+		expMatch bool
+	}{
+		{syntax.Perl | syntax.OneLine, false},
+		{syntax.Perl &^ syntax.OneLine, true},
+	} {
+		parsed, err := syntax.Parse(expr, tc.reFlags)
+		if err != nil {
+			t.Fatalf("%d: parse: %v", i, err)
+		}
+		re, err := Compile(parsed.String())
+		if err != nil {
+			t.Fatalf("%d: compile: %v", i, err)
+		}
+		if match := re.MatchString(s); match != tc.expMatch {
+			t.Errorf("%d: %q.MatchString(%q)=%t; expected=%t", i, re, s, match, tc.expMatch)
+		}
+	}
+}
+
 // Check that one-pass cutoff does trigger.
 func TestOnePassCutoff(t *testing.T) {
 	re, err := syntax.Parse(`^x{1,1000}y{1,1000}$`, syntax.Perl)
@@ -538,6 +564,72 @@ func TestSwitchBacktrack(t *testing.T) {
 	re.Match(long[:1]) // triggers backtracker
 }
 
+func BenchmarkFind(b *testing.B) {
+	b.StopTimer()
+	re := MustCompile("a+b+")
+	wantSubs := "aaabb"
+	s := []byte("acbb" + wantSubs + "dd")
+	b.StartTimer()
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		subs := re.Find(s)
+		if string(subs) != wantSubs {
+			b.Fatalf("Find(%q) = %q; want %q", s, subs, wantSubs)
+		}
+	}
+}
+
+func BenchmarkFindString(b *testing.B) {
+	b.StopTimer()
+	re := MustCompile("a+b+")
+	wantSubs := "aaabb"
+	s := "acbb" + wantSubs + "dd"
+	b.StartTimer()
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		subs := re.FindString(s)
+		if subs != wantSubs {
+			b.Fatalf("FindString(%q) = %q; want %q", s, subs, wantSubs)
+		}
+	}
+}
+
+func BenchmarkFindSubmatch(b *testing.B) {
+	b.StopTimer()
+	re := MustCompile("a(a+b+)b")
+	wantSubs := "aaabb"
+	s := []byte("acbb" + wantSubs + "dd")
+	b.StartTimer()
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		subs := re.FindSubmatch(s)
+		if string(subs[0]) != wantSubs {
+			b.Fatalf("FindSubmatch(%q)[0] = %q; want %q", s, subs[0], wantSubs)
+		}
+		if string(subs[1]) != "aab" {
+			b.Fatalf("FindSubmatch(%q)[1] = %q; want %q", s, subs[1], "aab")
+		}
+	}
+}
+
+func BenchmarkFindStringSubmatch(b *testing.B) {
+	b.StopTimer()
+	re := MustCompile("a(a+b+)b")
+	wantSubs := "aaabb"
+	s := "acbb" + wantSubs + "dd"
+	b.StartTimer()
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		subs := re.FindStringSubmatch(s)
+		if subs[0] != wantSubs {
+			b.Fatalf("FindStringSubmatch(%q)[0] = %q; want %q", s, subs[0], wantSubs)
+		}
+		if subs[1] != "aab" {
+			b.Fatalf("FindStringSubmatch(%q)[1] = %q; want %q", s, subs[1], "aab")
+		}
+	}
+}
+
 func BenchmarkLiteral(b *testing.B) {
 	x := strings.Repeat("x", 50) + "y"
 	b.StopTimer()
@@ -726,3 +818,23 @@ func BenchmarkMatchParallelCopied(b *testing.B) {
 		}
 	})
 }
+
+var sink string
+
+func BenchmarkQuoteMetaAll(b *testing.B) {
+	s := string(specialBytes)
+	b.SetBytes(int64(len(s)))
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		sink = QuoteMeta(s)
+	}
+}
+
+func BenchmarkQuoteMetaNone(b *testing.B) {
+	s := "abcdefghijklmnopqrstuvwxyz"
+	b.SetBytes(int64(len(s)))
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		sink = QuoteMeta(s)
+	}
+}
diff --git a/src/regexp/exec.go b/src/regexp/exec.go
index 4fd61b5..977619c 100644
--- a/src/regexp/exec.go
+++ b/src/regexp/exec.go
@@ -405,14 +405,16 @@ func (m *machine) onepass(i input, pos int) bool {
 	return m.matched
 }
 
-// empty is a non-nil 0-element slice,
-// so doExecute can avoid an allocation
-// when 0 captures are requested from a successful match.
-var empty = make([]int, 0)
+// doMatch reports whether either r, b or s match the regexp.
+func (re *Regexp) doMatch(r io.RuneReader, b []byte, s string) bool {
+	return re.doExecute(r, b, s, 0, 0, nil) != nil
+}
 
-// doExecute finds the leftmost match in the input and returns
-// the position of its subexpressions.
-func (re *Regexp) doExecute(r io.RuneReader, b []byte, s string, pos int, ncap int) []int {
+// doExecute finds the leftmost match in the input, appends the position
+// of its subexpressions to dstCap and returns dstCap.
+//
+// nil is returned if no matches are found and non-nil if matches are found.
+func (re *Regexp) doExecute(r io.RuneReader, b []byte, s string, pos int, ncap int, dstCap []int) []int {
 	m := re.get()
 	var i input
 	var size int
@@ -445,12 +447,15 @@ func (re *Regexp) doExecute(r io.RuneReader, b []byte, s string, pos int, ncap i
 			return nil
 		}
 	}
-	if ncap == 0 {
-		re.put(m)
-		return empty // empty but not nil
+	dstCap = append(dstCap, m.matchcap...)
+	if dstCap == nil {
+		// Keep the promise of returning non-nil value on match.
+		dstCap = arrayNoInts[:0]
 	}
-	cap := make([]int, len(m.matchcap))
-	copy(cap, m.matchcap)
 	re.put(m)
-	return cap
+	return dstCap
 }
+
+// arrayNoInts is returned by doExecute match if nil dstCap is passed
+// to it with ncap=0.
+var arrayNoInts [0]int
diff --git a/src/regexp/exec_test.go b/src/regexp/exec_test.go
index 69f187e..766394d 100644
--- a/src/regexp/exec_test.go
+++ b/src/regexp/exec_test.go
@@ -8,6 +8,7 @@ import (
 	"bufio"
 	"compress/bzip2"
 	"fmt"
+	"internal/testenv"
 	"io"
 	"os"
 	"path/filepath"
@@ -659,9 +660,14 @@ func makeText(n int) []byte {
 }
 
 func BenchmarkMatch(b *testing.B) {
+	isRaceBuilder := strings.HasSuffix(testenv.Builder(), "-race")
+
 	for _, data := range benchData {
 		r := MustCompile(data.re)
 		for _, size := range benchSizes {
+			if isRaceBuilder && size.n > 1<<10 {
+				continue
+			}
 			t := makeText(size.n)
 			b.Run(data.name+"/"+size.name, func(b *testing.B) {
 				b.SetBytes(int64(size.n))
diff --git a/src/regexp/onepass.go b/src/regexp/onepass.go
index 4991954..1b0564c 100644
--- a/src/regexp/onepass.go
+++ b/src/regexp/onepass.go
@@ -287,11 +287,6 @@ func (p runeSlice) Len() int           { return len(p) }
 func (p runeSlice) Less(i, j int) bool { return p[i] < p[j] }
 func (p runeSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
-// Sort is a convenience method.
-func (p runeSlice) Sort() {
-	sort.Sort(p)
-}
-
 var anyRuneNotNL = []rune{0, '\n' - 1, '\n' + 1, unicode.MaxRune}
 var anyRune = []rune{0, unicode.MaxRune}
 
diff --git a/src/regexp/regexp.go b/src/regexp/regexp.go
index fe3db9f..01093d4 100644
--- a/src/regexp/regexp.go
+++ b/src/regexp/regexp.go
@@ -408,17 +408,17 @@ func (re *Regexp) LiteralPrefix() (prefix string, complete bool) {
 // MatchReader reports whether the Regexp matches the text read by the
 // RuneReader.
 func (re *Regexp) MatchReader(r io.RuneReader) bool {
-	return re.doExecute(r, nil, "", 0, 0) != nil
+	return re.doMatch(r, nil, "")
 }
 
 // MatchString reports whether the Regexp matches the string s.
 func (re *Regexp) MatchString(s string) bool {
-	return re.doExecute(nil, nil, s, 0, 0) != nil
+	return re.doMatch(nil, nil, s)
 }
 
 // Match reports whether the Regexp matches the byte slice b.
 func (re *Regexp) Match(b []byte) bool {
-	return re.doExecute(nil, b, "", 0, 0) != nil
+	return re.doMatch(nil, b, "")
 }
 
 // MatchReader checks whether a textual regular expression matches the text
@@ -502,8 +502,9 @@ func (re *Regexp) replaceAll(bsrc []byte, src string, nmatch int, repl func(dst
 		nmatch = re.prog.NumCap
 	}
 
+	var dstCap [2]int
 	for searchPos <= endPos {
-		a := re.doExecute(nil, bsrc, src, searchPos, nmatch)
+		a := re.doExecute(nil, bsrc, src, searchPos, nmatch, dstCap[:0])
 		if len(a) == 0 {
 			break // no more matches
 		}
@@ -599,11 +600,22 @@ func special(b byte) bool {
 // inside the argument text; the returned string is a regular expression matching
 // the literal text. For example, QuoteMeta(`[foo]`) returns `\[foo\]`.
 func QuoteMeta(s string) string {
-	b := make([]byte, 2*len(s))
-
 	// A byte loop is correct because all metacharacters are ASCII.
-	j := 0
-	for i := 0; i < len(s); i++ {
+	var i int
+	for i = 0; i < len(s); i++ {
+		if special(s[i]) {
+			break
+		}
+	}
+	// No meta characters found, so return original string.
+	if i >= len(s) {
+		return s
+	}
+
+	b := make([]byte, 2*len(s)-i)
+	copy(b, s[:i])
+	j := i
+	for ; i < len(s); i++ {
 		if special(s[i]) {
 			b[j] = '\\'
 			j++
@@ -611,7 +623,7 @@ func QuoteMeta(s string) string {
 		b[j] = s[i]
 		j++
 	}
-	return string(b[0:j])
+	return string(b[:j])
 }
 
 // The number of capture values in the program may correspond
@@ -641,7 +653,7 @@ func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) {
 	}
 
 	for pos, i, prevMatchEnd := 0, 0, -1; i < n && pos <= end; {
-		matches := re.doExecute(nil, b, s, pos, re.prog.NumCap)
+		matches := re.doExecute(nil, b, s, pos, re.prog.NumCap, nil)
 		if len(matches) == 0 {
 			break
 		}
@@ -681,7 +693,8 @@ func (re *Regexp) allMatches(s string, b []byte, n int, deliver func([]int)) {
 // Find returns a slice holding the text of the leftmost match in b of the regular expression.
 // A return value of nil indicates no match.
 func (re *Regexp) Find(b []byte) []byte {
-	a := re.doExecute(nil, b, "", 0, 2)
+	var dstCap [2]int
+	a := re.doExecute(nil, b, "", 0, 2, dstCap[:0])
 	if a == nil {
 		return nil
 	}
@@ -693,7 +706,7 @@ func (re *Regexp) Find(b []byte) []byte {
 // b[loc[0]:loc[1]].
 // A return value of nil indicates no match.
 func (re *Regexp) FindIndex(b []byte) (loc []int) {
-	a := re.doExecute(nil, b, "", 0, 2)
+	a := re.doExecute(nil, b, "", 0, 2, nil)
 	if a == nil {
 		return nil
 	}
@@ -706,7 +719,8 @@ func (re *Regexp) FindIndex(b []byte) (loc []int) {
 // an empty string. Use FindStringIndex or FindStringSubmatch if it is
 // necessary to distinguish these cases.
 func (re *Regexp) FindString(s string) string {
-	a := re.doExecute(nil, nil, s, 0, 2)
+	var dstCap [2]int
+	a := re.doExecute(nil, nil, s, 0, 2, dstCap[:0])
 	if a == nil {
 		return ""
 	}
@@ -718,7 +732,7 @@ func (re *Regexp) FindString(s string) string {
 // itself is at s[loc[0]:loc[1]].
 // A return value of nil indicates no match.
 func (re *Regexp) FindStringIndex(s string) (loc []int) {
-	a := re.doExecute(nil, nil, s, 0, 2)
+	a := re.doExecute(nil, nil, s, 0, 2, nil)
 	if a == nil {
 		return nil
 	}
@@ -731,7 +745,7 @@ func (re *Regexp) FindStringIndex(s string) (loc []int) {
 // byte offset loc[0] through loc[1]-1.
 // A return value of nil indicates no match.
 func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int) {
-	a := re.doExecute(r, nil, "", 0, 2)
+	a := re.doExecute(r, nil, "", 0, 2, nil)
 	if a == nil {
 		return nil
 	}
@@ -744,7 +758,8 @@ func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int) {
 // comment.
 // A return value of nil indicates no match.
 func (re *Regexp) FindSubmatch(b []byte) [][]byte {
-	a := re.doExecute(nil, b, "", 0, re.prog.NumCap)
+	var dstCap [4]int
+	a := re.doExecute(nil, b, "", 0, re.prog.NumCap, dstCap[:0])
 	if a == nil {
 		return nil
 	}
@@ -891,7 +906,7 @@ func extract(str string) (name string, num int, rest string, ok bool) {
 // in the package comment.
 // A return value of nil indicates no match.
 func (re *Regexp) FindSubmatchIndex(b []byte) []int {
-	return re.pad(re.doExecute(nil, b, "", 0, re.prog.NumCap))
+	return re.pad(re.doExecute(nil, b, "", 0, re.prog.NumCap, nil))
 }
 
 // FindStringSubmatch returns a slice of strings holding the text of the
@@ -900,7 +915,8 @@ func (re *Regexp) FindSubmatchIndex(b []byte) []int {
 // package comment.
 // A return value of nil indicates no match.
 func (re *Regexp) FindStringSubmatch(s string) []string {
-	a := re.doExecute(nil, nil, s, 0, re.prog.NumCap)
+	var dstCap [4]int
+	a := re.doExecute(nil, nil, s, 0, re.prog.NumCap, dstCap[:0])
 	if a == nil {
 		return nil
 	}
@@ -919,7 +935,7 @@ func (re *Regexp) FindStringSubmatch(s string) []string {
 // 'Index' descriptions in the package comment.
 // A return value of nil indicates no match.
 func (re *Regexp) FindStringSubmatchIndex(s string) []int {
-	return re.pad(re.doExecute(nil, nil, s, 0, re.prog.NumCap))
+	return re.pad(re.doExecute(nil, nil, s, 0, re.prog.NumCap, nil))
 }
 
 // FindReaderSubmatchIndex returns a slice holding the index pairs
@@ -928,7 +944,7 @@ func (re *Regexp) FindStringSubmatchIndex(s string) []int {
 // by the 'Submatch' and 'Index' descriptions in the package comment. A
 // return value of nil indicates no match.
 func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int {
-	return re.pad(re.doExecute(r, nil, "", 0, re.prog.NumCap))
+	return re.pad(re.doExecute(r, nil, "", 0, re.prog.NumCap, nil))
 }
 
 const startSize = 10 // The size at which to start a slice in the 'All' routines.
diff --git a/src/runtime/HACKING.md b/src/runtime/HACKING.md
new file mode 100644
index 0000000..88fb708
--- /dev/null
+++ b/src/runtime/HACKING.md
@@ -0,0 +1,135 @@
+This is a very incomplete and probably out-of-date guide to
+programming in the Go runtime and how it differs from writing normal
+Go.
+
+Unmanaged memory
+================
+
+In general, the runtime tries to use regular heap allocation. However,
+in some cases the runtime must allocate objects outside of the garbage
+collected heap, in *unmanaged memory*. This is necessary if the
+objects are part of the memory manager itself or if they must be
+allocated in situations where the caller may not have a P.
+
+There are three mechanisms for allocating unmanaged memory:
+
+* sysAlloc obtains memory directly from the OS. This comes in whole
+  multiples of the system page size, but it can be freed with sysFree.
+
+* persistentalloc combines multiple smaller allocations into a single
+  sysAlloc to avoid fragmentation. However, there is no way to free
+  persistentalloced objects (hence the name).
+
+* fixalloc is a SLAB-style allocator that allocates objects of a fixed
+  size. fixalloced objects can be freed, but this memory can only be
+  reused by the same fixalloc pool, so it can only be reused for
+  objects of the same type.
+
+In general, types that are allocated using any of these should be
+marked `//go:notinheap` (see below).
+
+Objects that are allocated in unmanaged memory **must not** contain
+heap pointers unless the following rules are also obeyed:
+
+1. Any pointers from unmanaged memory to the heap must be added as
+   explicit garbage collection roots in `runtime.markroot`.
+
+2. If the memory is reused, the heap pointers must be zero-initialized
+   before they become visible as GC roots. Otherwise, the GC may
+   observe stale heap pointers. See "Zero-initialization versus
+   zeroing".
+
+Zero-initialization versus zeroing
+==================================
+
+There are two types of zeroing in the runtime, depending on whether
+the memory is already initialized to a type-safe state.
+
+If memory is not in a type-safe state, meaning it potentially contains
+"garbage" because it was just allocated and it is being initialized
+for first use, then it must be *zero-initialized* using
+`memclrNoHeapPointers` or non-pointer writes. This does not perform
+write barriers.
+
+If memory is already in a type-safe state and is simply being set to
+the zero value, this must be done using regular writes, `typedmemclr`,
+or `memclrHasPointers`. This performs write barriers.
+
+Runtime-only compiler directives
+================================
+
+In addition to the "//go:" directives documented in "go doc compile",
+the compiler supports additional directives only in the runtime.
+
+go:systemstack
+--------------
+
+`go:systemstack` indicates that a function must run on the system
+stack. This is checked dynamically by a special function prologue.
+
+go:nowritebarrier
+-----------------
+
+`go:nowritebarrier` directs the compiler to emit an error if the
+following function contains any write barriers. (It *does not*
+suppress the generation of write barriers; it is simply an assertion.)
+
+Usually you want `go:nowritebarrierrec`. `go:nowritebarrier` is
+primarily useful in situations where it's "nice" not to have write
+barriers, but not required for correctness.
+
+go:nowritebarrierrec and go:yeswritebarrierrec
+----------------------------------------------
+
+`go:nowritebarrierrec` directs the compiler to emit an error if the
+following function or any function it calls recursively, up to a
+`go:yeswritebarrierrec`, contains a write barrier.
+
+Logically, the compiler floods the call graph starting from each
+`go:nowritebarrierrec` function and produces an error if it encounters
+a function containing a write barrier. This flood stops at
+`go:yeswritebarrierrec` functions.
+
+`go:nowritebarrierrec` is used in the implementation of the write
+barrier to prevent infinite loops.
+
+Both directives are used in the scheduler. The write barrier requires
+an active P (`getg().m.p != nil`) and scheduler code often runs
+without an active P. In this case, `go:nowritebarrierrec` is used on
+functions that release the P or may run without a P and
+`go:yeswritebarrierrec` is used when code re-acquires an active P.
+Since these are function-level annotations, code that releases or
+acquires a P may need to be split across two functions.
+
+go:notinheap
+------------
+
+`go:notinheap` applies to type declarations. It indicates that a type
+must never be heap allocated. Specifically, pointers to this type must
+always fail the `runtime.inheap` check. The type may be used for
+global variables, for stack variables, or for objects in unmanaged
+memory (e.g., allocated with `sysAlloc`, `persistentalloc`, or
+`fixalloc`). Specifically:
+
+1. `new(T)`, `make([]T)`, `append([]T, ...)` and implicit heap
+   allocation of T are disallowed. (Though implicit allocations are
+   disallowed in the runtime anyway.)
+
+2. A pointer to a regular type (other than `unsafe.Pointer`) cannot be
+   converted to a pointer to a `go:notinheap` type, even if they have
+   the same underlying type.
+
+3. Any type that contains a `go:notinheap` type is itself
+   `go:notinheap`. Structs and arrays are `go:notinheap` if their
+   elements are. Maps and channels of `go:notinheap` types are
+   disallowed. To keep things explicit, any type declaration where the
+   type is implicitly `go:notinheap` must be explicitly marked
+   `go:notinheap` as well.
+
+4. Write barriers on pointers to `go:notinheap` types can be omitted.
+
+The last point is the real benefit of `go:notinheap`. The runtime uses
+it for low-level internal structures to avoid memory barriers in the
+scheduler and the memory allocator where they are illegal or simply
+inefficient. This mechanism is reasonably safe and does not compromise
+the readability of the runtime.
diff --git a/src/runtime/alg.go b/src/runtime/alg.go
index 147332e..5c378c6 100644
--- a/src/runtime/alg.go
+++ b/src/runtime/alg.go
@@ -109,7 +109,7 @@ func f32hash(p unsafe.Pointer, h uintptr) uintptr {
 	case f == 0:
 		return c1 * (c0 ^ h) // +0, -0
 	case f != f:
-		return c1 * (c0 ^ h ^ uintptr(fastrand1())) // any kind of NaN
+		return c1 * (c0 ^ h ^ uintptr(fastrand())) // any kind of NaN
 	default:
 		return memhash(p, h, 4)
 	}
@@ -121,7 +121,7 @@ func f64hash(p unsafe.Pointer, h uintptr) uintptr {
 	case f == 0:
 		return c1 * (c0 ^ h) // +0, -0
 	case f != f:
-		return c1 * (c0 ^ h ^ uintptr(fastrand1())) // any kind of NaN
+		return c1 * (c0 ^ h ^ uintptr(fastrand())) // any kind of NaN
 	default:
 		return memhash(p, h, 8)
 	}
@@ -275,12 +275,6 @@ func ifaceHash(i interface {
 	return algarray[alg_INTER].hash(noescape(unsafe.Pointer(&i)), seed)
 }
 
-// Testing adapter for memclr
-func memclrBytes(b []byte) {
-	s := (*slice)(unsafe.Pointer(&b))
-	memclr(s.array, uintptr(s.len))
-}
-
 const hashRandomBytes = sys.PtrSize / 4 * 64
 
 // used in asm_{386,amd64}.s to seed the hash function
diff --git a/src/runtime/append_test.go b/src/runtime/append_test.go
index 6b8968e..6bd8f3b 100644
--- a/src/runtime/append_test.go
+++ b/src/runtime/append_test.go
@@ -100,6 +100,22 @@ func BenchmarkAppendSlice(b *testing.B) {
 	}
 }
 
+var (
+	blackhole []byte
+)
+
+func BenchmarkAppendSliceLarge(b *testing.B) {
+	for _, length := range []int{1 << 10, 4 << 10, 16 << 10, 64 << 10, 256 << 10, 1024 << 10} {
+		y := make([]byte, length)
+		b.Run(fmt.Sprint(length, "Bytes"), func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				blackhole = nil
+				blackhole = append(blackhole, y...)
+			}
+		})
+	}
+}
+
 func BenchmarkAppendStr(b *testing.B) {
 	for _, str := range []string{
 		"1",
diff --git a/src/runtime/asm.s b/src/runtime/asm.s
index 646dc2f..3ddea7c 100644
--- a/src/runtime/asm.s
+++ b/src/runtime/asm.s
@@ -12,8 +12,5 @@ DATA runtime·no_pointers_stackmap+0x00(SB)/4, $2
 DATA runtime·no_pointers_stackmap+0x04(SB)/4, $0
 GLOBL runtime·no_pointers_stackmap(SB),RODATA, $8
 
-TEXT runtime·nop(SB),NOSPLIT,$0-0
-	RET
-
 GLOBL runtime·mheap_(SB), NOPTR, $0
 GLOBL runtime·memstats(SB), NOPTR, $0
diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s
index ea11b2b..3d0b74c 100644
--- a/src/runtime/asm_386.s
+++ b/src/runtime/asm_386.s
@@ -193,9 +193,7 @@ TEXT runtime·asminit(SB),NOSPLIT,$0-0
 	// Other operating systems use double precision.
 	// Change to double precision to match them,
 	// and to match other hardware that only has double.
-	PUSHL $0x27F
-	FLDCW	0(SP)
-	POPL AX
+	FLDCW	runtime·controlWord64(SB)
 	RET
 
 /*
@@ -211,7 +209,11 @@ TEXT runtime·gosave(SB), NOSPLIT, $0-4
 	MOVL	0(SP), BX		// caller's PC
 	MOVL	BX, gobuf_pc(AX)
 	MOVL	$0, gobuf_ret(AX)
-	MOVL	$0, gobuf_ctxt(AX)
+	// Assert ctxt is zero. See func save.
+	MOVL	gobuf_ctxt(AX), BX
+	TESTL	BX, BX
+	JZ	2(PC)
+	CALL	runtime·badctxt(SB)
 	get_tls(CX)
 	MOVL	g(CX), BX
 	MOVL	BX, gobuf_g(AX)
@@ -219,8 +221,20 @@ TEXT runtime·gosave(SB), NOSPLIT, $0-4
 
 // void gogo(Gobuf*)
 // restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), NOSPLIT, $0-4
+TEXT runtime·gogo(SB), NOSPLIT, $8-4
 	MOVL	buf+0(FP), BX		// gobuf
+
+	// If ctxt is not nil, invoke deletion barrier before overwriting.
+	MOVL	gobuf_ctxt(BX), DX
+	TESTL	DX, DX
+	JZ	nilctxt
+	LEAL	gobuf_ctxt(BX), AX
+	MOVL	AX, 0(SP)
+	MOVL	$0, 4(SP)
+	CALL	runtime·writebarrierptr_prewrite(SB)
+	MOVL	buf+0(FP), BX
+
+nilctxt:
 	MOVL	gobuf_g(BX), DX
 	MOVL	0(DX), CX		// make sure g != nil
 	get_tls(CX)
@@ -356,13 +370,15 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
 	MOVL	g_m(BX), BX
 	MOVL	m_g0(BX), SI
 	CMPL	g(CX), SI
-	JNE	2(PC)
+	JNE	3(PC)
+	CALL	runtime·badmorestackg0(SB)
 	INT	$3
 
 	// Cannot grow signal stack.
 	MOVL	m_gsignal(BX), SI
 	CMPL	g(CX), SI
-	JNE	2(PC)
+	JNE	3(PC)
+	CALL	runtime·badmorestackgsignal(SB)
 	INT	$3
 
 	// Called from f.
@@ -381,7 +397,7 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
 	MOVL	SI, (g_sched+gobuf_g)(SI)
 	LEAL	4(SP), AX	// f's SP
 	MOVL	AX, (g_sched+gobuf_sp)(SI)
-	MOVL	DX, (g_sched+gobuf_ctxt)(SI)
+	// newstack will fill gobuf.ctxt.
 
 	// Call newstack on m->g0's stack.
 	MOVL	m_g0(BX), BP
@@ -389,8 +405,10 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
 	MOVL	(g_sched+gobuf_sp)(BP), AX
 	MOVL	-4(AX), BX	// fault if CALL would, before smashing SP
 	MOVL	AX, SP
+	PUSHL	DX	// ctxt argument
 	CALL	runtime·newstack(SB)
 	MOVL	$0, 0x1003	// crash if newstack returns
+	POPL	DX	// keep balance check happy
 	RET
 
 TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0
@@ -475,6 +493,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20;		\
 	PCDATA  $PCDATA_StackMapIndex, $0;	\
 	CALL	AX;				\
 	/* copy return values back */		\
+	MOVL	argtype+0(FP), DX;		\
 	MOVL	argptr+8(FP), DI;		\
 	MOVL	argsize+12(FP), CX;		\
 	MOVL	retoffset+16(FP), BX;		\
@@ -482,17 +501,19 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20;		\
 	ADDL	BX, DI;				\
 	ADDL	BX, SI;				\
 	SUBL	BX, CX;				\
-	REP;MOVSB;				\
-	/* execute write barrier updates */	\
-	MOVL	argtype+0(FP), DX;		\
-	MOVL	argptr+8(FP), DI;		\
-	MOVL	argsize+12(FP), CX;		\
-	MOVL	retoffset+16(FP), BX;		\
-	MOVL	DX, 0(SP);			\
-	MOVL	DI, 4(SP);			\
-	MOVL	CX, 8(SP);			\
-	MOVL	BX, 12(SP);			\
-	CALL	runtime·callwritebarrier(SB);	\
+	CALL	callRet<>(SB);			\
+	RET
+
+// callRet copies return values back at the end of call*. This is a
+// separate function so it can allocate stack space for the arguments
+// to reflectcallmove. It does not follow the Go ABI; it expects its
+// arguments in registers.
+TEXT callRet<>(SB), NOSPLIT, $16-0
+	MOVL	DX, 0(SP)
+	MOVL	DI, 4(SP)
+	MOVL	SI, 8(SP)
+	MOVL	CX, 12(SP)
+	CALL	runtime·reflectcallmove(SB)
 	RET
 
 CALLFN(·call16, 16)
@@ -567,7 +588,11 @@ TEXT gosave<>(SB),NOSPLIT,$0
 	MOVL	-4(AX), AX
 	MOVL	AX, (g_sched+gobuf_pc)(BX)
 	MOVL	$0, (g_sched+gobuf_ret)(BX)
-	MOVL	$0, (g_sched+gobuf_ctxt)(BX)
+	// Assert ctxt is zero. See func save.
+	MOVL	(g_sched+gobuf_ctxt)(BX), AX
+	TESTL	AX, AX
+	JZ	2(PC)
+	CALL	runtime·badctxt(SB)
 	POPL	BX
 	POPL	AX
 	RET
@@ -810,11 +835,6 @@ setbar:
 	CALL	runtime·setNextBarrierPC(SB)
 	RET
 
-TEXT runtime·getcallersp(SB), NOSPLIT, $0-8
-	MOVL	argp+0(FP), AX
-	MOVL	AX, ret+4(FP)
-	RET
-
 // func cputicks() int64
 TEXT runtime·cputicks(SB),NOSPLIT,$0-8
 	TESTL	$0x4000000, runtime·cpuid_edx(SB) // no sse2, no mfence
@@ -845,9 +865,6 @@ TEXT runtime·ldt0setup(SB),NOSPLIT,$16-0
 TEXT runtime·emptyfunc(SB),0,$0-0
 	RET
 
-TEXT runtime·abort(SB),NOSPLIT,$0-0
-	INT $0x3
-
 // memhash_varlen(p unsafe.Pointer, h seed) uintptr
 // redirects to memhash(p, h, size) using the size
 // stored in the closure.
@@ -1292,15 +1309,15 @@ eq:
 // See runtime_test.go:eqstring_generic for
 // equivalent Go code.
 TEXT runtime·eqstring(SB),NOSPLIT,$0-17
-	MOVL	s1str+0(FP), SI
-	MOVL	s2str+8(FP), DI
+	MOVL	s1_base+0(FP), SI
+	MOVL	s2_base+8(FP), DI
 	CMPL	SI, DI
 	JEQ	same
-	MOVL	s1len+4(FP), BX
-	LEAL	v+16(FP), AX
+	MOVL	s1_len+4(FP), BX
+	LEAL	ret+16(FP), AX
 	JMP	runtime·memeqbody(SB)
 same:
-	MOVB	$1, v+16(FP)
+	MOVB	$1, ret+16(FP)
 	RET
 
 TEXT bytes·Equal(SB),NOSPLIT,$0-25
@@ -1578,7 +1595,7 @@ allsame:
 	MOVL	BX, (AX)
 	RET
 
-TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
+TEXT runtime·fastrand(SB), NOSPLIT, $0-4
 	get_tls(CX)
 	MOVL	g(CX), AX
 	MOVL	g_m(AX), AX
@@ -1637,3 +1654,21 @@ TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0
        MOVL    AX, moduledata_next(DX)
        MOVL    AX, runtime·lastmoduledatap(SB)
        RET
+
+TEXT runtime·uint32tofloat64(SB),NOSPLIT,$8-12
+	MOVL	a+0(FP), AX
+	MOVL	AX, 0(SP)
+	MOVL	$0, 4(SP)
+	FMOVV	0(SP), F0
+	FMOVDP	F0, ret+4(FP)
+	RET
+
+TEXT runtime·float64touint32(SB),NOSPLIT,$12-12
+	FMOVD	a+0(FP), F0
+	FSTCW	0(SP)
+	FLDCW	runtime·controlWord64trunc(SB)
+	FMOVVP	F0, 4(SP)
+	FLDCW	0(SP)
+	MOVL	4(SP), AX
+	MOVL	AX, ret+8(FP)
+	RET
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index 6103d54..9ffd297 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -182,8 +182,12 @@ TEXT runtime·gosave(SB), NOSPLIT, $0-8
 	MOVQ	0(SP), BX		// caller's PC
 	MOVQ	BX, gobuf_pc(AX)
 	MOVQ	$0, gobuf_ret(AX)
-	MOVQ	$0, gobuf_ctxt(AX)
 	MOVQ	BP, gobuf_bp(AX)
+	// Assert ctxt is zero. See func save.
+	MOVQ	gobuf_ctxt(AX), BX
+	TESTQ	BX, BX
+	JZ	2(PC)
+	CALL	runtime·badctxt(SB)
 	get_tls(CX)
 	MOVQ	g(CX), BX
 	MOVQ	BX, gobuf_g(AX)
@@ -191,8 +195,20 @@ TEXT runtime·gosave(SB), NOSPLIT, $0-8
 
 // void gogo(Gobuf*)
 // restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), NOSPLIT, $0-8
+TEXT runtime·gogo(SB), NOSPLIT, $16-8
 	MOVQ	buf+0(FP), BX		// gobuf
+
+	// If ctxt is not nil, invoke deletion barrier before overwriting.
+	MOVQ	gobuf_ctxt(BX), AX
+	TESTQ	AX, AX
+	JZ	nilctxt
+	LEAQ	gobuf_ctxt(BX), AX
+	MOVQ	AX, 0(SP)
+	MOVQ	$0, 8(SP)
+	CALL	runtime·writebarrierptr_prewrite(SB)
+	MOVQ	buf+0(FP), BX
+
+nilctxt:
 	MOVQ	gobuf_g(BX), DX
 	MOVQ	0(DX), CX		// make sure g != nil
 	get_tls(CX)
@@ -331,13 +347,15 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
 	MOVQ	g_m(BX), BX
 	MOVQ	m_g0(BX), SI
 	CMPQ	g(CX), SI
-	JNE	2(PC)
+	JNE	3(PC)
+	CALL	runtime·badmorestackg0(SB)
 	INT	$3
 
 	// Cannot grow signal stack (m->gsignal).
 	MOVQ	m_gsignal(BX), SI
 	CMPQ	g(CX), SI
-	JNE	2(PC)
+	JNE	3(PC)
+	CALL	runtime·badmorestackgsignal(SB)
 	INT	$3
 
 	// Called from f.
@@ -356,15 +374,17 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
 	MOVQ	SI, (g_sched+gobuf_g)(SI)
 	LEAQ	8(SP), AX // f's SP
 	MOVQ	AX, (g_sched+gobuf_sp)(SI)
-	MOVQ	DX, (g_sched+gobuf_ctxt)(SI)
 	MOVQ	BP, (g_sched+gobuf_bp)(SI)
+	// newstack will fill gobuf.ctxt.
 
 	// Call newstack on m->g0's stack.
 	MOVQ	m_g0(BX), BX
 	MOVQ	BX, g(CX)
 	MOVQ	(g_sched+gobuf_sp)(BX), SP
+	PUSHQ	DX	// ctxt argument
 	CALL	runtime·newstack(SB)
 	MOVQ	$0, 0x1003	// crash if newstack returns
+	POPQ	DX	// keep balance check happy
 	RET
 
 // morestack but not preserving ctxt.
@@ -412,8 +432,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
 
 TEXT ·reflectcall(SB), NOSPLIT, $0-32
 	MOVLQZX argsize+24(FP), CX
-	// NOTE(rsc): No call16, because CALLFN needs four words
-	// of argument space to invoke callwritebarrier.
 	DISPATCH(runtime·call32, 32)
 	DISPATCH(runtime·call64, 64)
 	DISPATCH(runtime·call128, 128)
@@ -456,24 +474,28 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-32;		\
 	PCDATA  $PCDATA_StackMapIndex, $0;	\
 	CALL	(DX);				\
 	/* copy return values back */		\
+	MOVQ	argtype+0(FP), DX;		\
 	MOVQ	argptr+16(FP), DI;		\
 	MOVLQZX	argsize+24(FP), CX;		\
-	MOVLQZX retoffset+28(FP), BX;		\
+	MOVLQZX	retoffset+28(FP), BX;		\
 	MOVQ	SP, SI;				\
 	ADDQ	BX, DI;				\
 	ADDQ	BX, SI;				\
 	SUBQ	BX, CX;				\
-	REP;MOVSB;				\
-	/* execute write barrier updates */	\
-	MOVQ	argtype+0(FP), DX;		\
-	MOVQ	argptr+16(FP), DI;		\
-	MOVLQZX	argsize+24(FP), CX;		\
-	MOVLQZX retoffset+28(FP), BX;		\
-	MOVQ	DX, 0(SP);			\
-	MOVQ	DI, 8(SP);			\
-	MOVQ	CX, 16(SP);			\
-	MOVQ	BX, 24(SP);			\
-	CALL	runtime·callwritebarrier(SB);	\
+	CALL	callRet<>(SB);			\
+	RET
+
+// callRet copies return values back at the end of call*. This is a
+// separate function so it can allocate stack space for the arguments
+// to reflectcallmove. It does not follow the Go ABI; it expects its
+// arguments in registers.
+TEXT callRet<>(SB), NOSPLIT, $32-0
+	NO_LOCAL_POINTERS
+	MOVQ	DX, 0(SP)
+	MOVQ	DI, 8(SP)
+	MOVQ	SI, 16(SP)
+	MOVQ	CX, 24(SP)
+	CALL	runtime·reflectcallmove(SB)
 	RET
 
 CALLFN(·call32, 32)
@@ -540,8 +562,12 @@ TEXT gosave<>(SB),NOSPLIT,$0
 	LEAQ	8(SP), R9
 	MOVQ	R9, (g_sched+gobuf_sp)(R8)
 	MOVQ	$0, (g_sched+gobuf_ret)(R8)
-	MOVQ	$0, (g_sched+gobuf_ctxt)(R8)
 	MOVQ	BP, (g_sched+gobuf_bp)(R8)
+	// Assert ctxt is zero. See func save.
+	MOVQ	(g_sched+gobuf_ctxt)(R8), R9
+	TESTQ	R9, R9
+	JZ	2(PC)
+	CALL	runtime·badctxt(SB)
 	RET
 
 // func asmcgocall(fn, arg unsafe.Pointer) int32
@@ -825,11 +851,6 @@ setbar:
 	CALL	runtime·setNextBarrierPC(SB)
 	RET
 
-TEXT runtime·getcallersp(SB),NOSPLIT,$0-16
-	MOVQ	argp+0(FP), AX
-	MOVQ	AX, ret+8(FP)
-	RET
-
 // func cputicks() int64
 TEXT runtime·cputicks(SB),NOSPLIT,$0-0
 	CMPB	runtime·lfenceBeforeRdtsc(SB), $1
@@ -1340,15 +1361,15 @@ eq:
 // See runtime_test.go:eqstring_generic for
 // equivalent Go code.
 TEXT runtime·eqstring(SB),NOSPLIT,$0-33
-	MOVQ	s1str+0(FP), SI
-	MOVQ	s2str+16(FP), DI
+	MOVQ	s1_base+0(FP), SI
+	MOVQ	s2_base+16(FP), DI
 	CMPQ	SI, DI
 	JEQ	eq
-	MOVQ	s1len+8(FP), BX
-	LEAQ	v+32(FP), AX
+	MOVQ	s1_len+8(FP), BX
+	LEAQ	ret+32(FP), AX
 	JMP	runtime·memeqbody(SB)
 eq:
-	MOVB	$1, v+32(FP)
+	MOVB	$1, ret+32(FP)
 	RET
 
 // a in SI
@@ -1695,13 +1716,41 @@ big_loop_avx2_exit:
 	JMP loop
 
 
-// TODO: Also use this in bytes.Index
+TEXT strings·supportAVX2(SB),NOSPLIT,$0-1
+	MOVBLZX runtime·support_avx2(SB), AX
+	MOVB AX, ret+0(FP)
+	RET
+
+TEXT bytes·supportAVX2(SB),NOSPLIT,$0-1
+	MOVBLZX runtime·support_avx2(SB), AX
+	MOVB AX, ret+0(FP)
+	RET
+
 TEXT strings·indexShortStr(SB),NOSPLIT,$0-40
 	MOVQ s+0(FP), DI
 	// We want len in DX and AX, because PCMPESTRI implicitly consumes them
 	MOVQ s_len+8(FP), DX
 	MOVQ c+16(FP), BP
 	MOVQ c_len+24(FP), AX
+	MOVQ DI, R10
+	LEAQ ret+32(FP), R11
+	JMP  runtime·indexShortStr(SB)
+
+TEXT bytes·indexShortStr(SB),NOSPLIT,$0-56
+	MOVQ s+0(FP), DI
+	MOVQ s_len+8(FP), DX
+	MOVQ c+24(FP), BP
+	MOVQ c_len+32(FP), AX
+	MOVQ DI, R10
+	LEAQ ret+48(FP), R11
+	JMP  runtime·indexShortStr(SB)
+
+// AX: length of string, that we are searching for
+// DX: length of string, in which we are searching
+// DI: pointer to string, in which we are searching
+// BP: pointer to string, that we are searching for
+// R11: address, where to put return value
+TEXT runtime·indexShortStr(SB),NOSPLIT,$0
 	CMPQ AX, DX
 	JA fail
 	CMPQ DX, $16
@@ -1791,7 +1840,7 @@ loop8:
 	JB loop8
 	JMP fail
 _9_or_more:
-	CMPQ AX, $16
+	CMPQ AX, $15
 	JA   _16_or_more
 	LEAQ 1(DI)(DX*1), DX
 	SUBQ AX, DX
@@ -1815,7 +1864,7 @@ partial_success9to15:
 	JMP fail
 _16_or_more:
 	CMPQ AX, $16
-	JA   _17_to_31
+	JA   _17_or_more
 	MOVOU (BP), X1
 	LEAQ -15(DI)(DX*1), DX
 loop16:
@@ -1828,7 +1877,9 @@ loop16:
 	CMPQ DI,DX
 	JB loop16
 	JMP fail
-_17_to_31:
+_17_or_more:
+	CMPQ AX, $31
+	JA   _32_or_more
 	LEAQ 1(DI)(DX*1), DX
 	SUBQ AX, DX
 	MOVOU -16(BP)(AX*1), X0
@@ -1852,9 +1903,56 @@ partial_success17to31:
 	ADDQ $1,DI
 	CMPQ DI,DX
 	JB loop17to31
+	JMP fail
+// We can get here only when AVX2 is enabled and cutoff for indexShortStr is set to 63
+// So no need to check cpuid
+_32_or_more:
+	CMPQ AX, $32
+	JA   _33_to_63
+	VMOVDQU (BP), Y1
+	LEAQ -31(DI)(DX*1), DX
+loop32:
+	VMOVDQU (DI), Y2
+	VPCMPEQB Y1, Y2, Y3
+	VPMOVMSKB Y3, SI
+	CMPL  SI, $0xffffffff
+	JE   success_avx2
+	ADDQ $1,DI
+	CMPQ DI,DX
+	JB loop32
+	JMP fail_avx2
+_33_to_63:
+	LEAQ 1(DI)(DX*1), DX
+	SUBQ AX, DX
+	VMOVDQU -32(BP)(AX*1), Y0
+	VMOVDQU (BP), Y1
+loop33to63:
+	VMOVDQU (DI), Y2
+	VPCMPEQB Y1, Y2, Y3
+	VPMOVMSKB Y3, SI
+	CMPL  SI, $0xffffffff
+	JE   partial_success33to63
+	ADDQ $1,DI
+	CMPQ DI,DX
+	JB loop33to63
+	JMP fail_avx2
+partial_success33to63:
+	VMOVDQU -32(AX)(DI*1), Y3
+	VPCMPEQB Y0, Y3, Y4
+	VPMOVMSKB Y4, SI
+	CMPL  SI, $0xffffffff
+	JE success_avx2
+	ADDQ $1,DI
+	CMPQ DI,DX
+	JB loop33to63
+fail_avx2:
+	VZEROUPPER
 fail:
-	MOVQ $-1, ret+32(FP)
+	MOVQ $-1, (R11)
 	RET
+success_avx2:
+	VZEROUPPER
+	JMP success
 sse42:
 	MOVL runtime·cpuid_ecx(SB), CX
 	ANDL $0x100000, CX
@@ -1893,8 +1991,8 @@ loop_sse42:
 sse42_success:
 	ADDQ CX, DI
 success:
-	SUBQ s+0(FP), DI
-	MOVQ DI, ret+32(FP)
+	SUBQ R10, DI
+	MOVQ DI, (R11)
 	RET
 
 
@@ -2052,7 +2150,7 @@ eqret:
 	MOVB	$0, ret+48(FP)
 	RET
 
-TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
+TEXT runtime·fastrand(SB), NOSPLIT, $0-4
 	get_tls(CX)
 	MOVQ	g(CX), AX
 	MOVQ	g_m(AX), AX
diff --git a/src/runtime/asm_amd64p32.s b/src/runtime/asm_amd64p32.s
index 452ce04..c3c1c15 100644
--- a/src/runtime/asm_amd64p32.s
+++ b/src/runtime/asm_amd64p32.s
@@ -12,7 +12,7 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$0
 	MOVL	argc+0(FP), AX
 	MOVL	argv+4(FP), BX
 	MOVL	SP, CX
-	SUBL	$128, SP		// plenty of scratch
+	SUBL	$128, CX		// plenty of scratch
 	ANDL	$~15, CX
 	MOVL	CX, SP
 
@@ -107,8 +107,12 @@ TEXT runtime·gosave(SB), NOSPLIT, $0-4
 	MOVL	BX, gobuf_sp(AX)
 	MOVL	0(SP), BX		// caller's PC
 	MOVL	BX, gobuf_pc(AX)
-	MOVL	$0, gobuf_ctxt(AX)
 	MOVQ	$0, gobuf_ret(AX)
+	// Assert ctxt is zero. See func save.
+	MOVL	gobuf_ctxt(AX), BX
+	TESTL	BX, BX
+	JZ	2(PC)
+	CALL	runtime·badctxt(SB)
 	get_tls(CX)
 	MOVL	g(CX), BX
 	MOVL	BX, gobuf_g(AX)
@@ -116,8 +120,20 @@ TEXT runtime·gosave(SB), NOSPLIT, $0-4
 
 // void gogo(Gobuf*)
 // restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), NOSPLIT, $0-4
+TEXT runtime·gogo(SB), NOSPLIT, $8-4
 	MOVL	buf+0(FP), BX		// gobuf
+
+	// If ctxt is not nil, invoke deletion barrier before overwriting.
+	MOVL	gobuf_ctxt(BX), DX
+	TESTL	DX, DX
+	JZ	nilctxt
+	LEAL	gobuf_ctxt(BX), AX
+	MOVL	AX, 0(SP)
+	MOVL	$0, 4(SP)
+	CALL	runtime·writebarrierptr_prewrite(SB)
+	MOVL	buf+0(FP), BX
+
+nilctxt:
 	MOVL	gobuf_g(BX), DX
 	MOVL	0(DX), CX		// make sure g != nil
 	get_tls(CX)
@@ -249,13 +265,15 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
 	// Cannot grow scheduler stack (m->g0).
 	MOVL	m_g0(BX), SI
 	CMPL	g(CX), SI
-	JNE	2(PC)
+	JNE	3(PC)
+	CALL	runtime·badmorestackg0(SB)
 	MOVL	0, AX
 
 	// Cannot grow signal stack (m->gsignal).
 	MOVL	m_gsignal(BX), SI
 	CMPL	g(CX), SI
-	JNE	2(PC)
+	JNE	3(PC)
+	CALL	runtime·badmorestackgsignal(SB)
 	MOVL	0, AX
 
 	// Called from f.
@@ -274,14 +292,16 @@ TEXT runtime·morestack(SB),NOSPLIT,$0-0
 	MOVL	SI, (g_sched+gobuf_g)(SI)
 	LEAL	8(SP), AX // f's SP
 	MOVL	AX, (g_sched+gobuf_sp)(SI)
-	MOVL	DX, (g_sched+gobuf_ctxt)(SI)
+	// newstack will fill gobuf.ctxt.
 
 	// Call newstack on m->g0's stack.
 	MOVL	m_g0(BX), BX
 	MOVL	BX, g(CX)
 	MOVL	(g_sched+gobuf_sp)(BX), SP
+	PUSHQ	DX	// ctxt argument
 	CALL	runtime·newstack(SB)
 	MOVL	$0, 0x1003	// crash if newstack returns
+	POPQ	DX	// keep balance check happy
 	RET
 
 // morestack trampolines
@@ -367,6 +387,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20;		\
 	MOVL	(DX), AX;			\
 	CALL	AX;				\
 	/* copy return values back */		\
+	MOVL	argtype+0(FP), DX;		\
 	MOVL	argptr+8(FP), DI;		\
 	MOVL	argsize+12(FP), CX;		\
 	MOVL	retoffset+16(FP), BX;		\
@@ -374,17 +395,19 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20;		\
 	ADDL	BX, DI;				\
 	ADDL	BX, SI;				\
 	SUBL	BX, CX;				\
-	REP;MOVSB;				\
-	/* execute write barrier updates */	\
-	MOVL	argtype+0(FP), DX;		\
-	MOVL	argptr+8(FP), DI;		\
-	MOVL	argsize+12(FP), CX;		\
-	MOVL	retoffset+16(FP), BX;		\
-	MOVL	DX, 0(SP);			\
-	MOVL	DI, 4(SP);			\
-	MOVL	CX, 8(SP);			\
-	MOVL	BX, 12(SP);			\
-	CALL	runtime·callwritebarrier(SB);	\
+	CALL	callRet<>(SB);			\
+	RET
+
+// callRet copies return values back at the end of call*. This is a
+// separate function so it can allocate stack space for the arguments
+// to reflectcallmove. It does not follow the Go ABI; it expects its
+// arguments in registers.
+TEXT callRet<>(SB), NOSPLIT, $16-0
+	MOVL	DX, 0(SP)
+	MOVL	DI, 4(SP)
+	MOVL	SI, 8(SP)
+	MOVL	CX, 12(SP)
+	CALL	runtime·reflectcallmove(SB)
 	RET
 
 CALLFN(·call16, 16)
@@ -449,13 +472,13 @@ TEXT runtime·asmcgocall(SB),NOSPLIT,$0-12
 
 // cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
 // Not implemented.
-TEXT runtime·cgocallback(SB),NOSPLIT,$0-12
+TEXT runtime·cgocallback(SB),NOSPLIT,$0-16
 	MOVL	0, AX
 	RET
 
 // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
 // Not implemented.
-TEXT ·cgocallback_gofunc(SB),NOSPLIT,$0-12
+TEXT ·cgocallback_gofunc(SB),NOSPLIT,$0-16
 	MOVL	0, AX
 	RET
 
@@ -477,7 +500,7 @@ TEXT runtime·stackcheck(SB), NOSPLIT, $0-0
 	MOVL	0, AX
 	RET
 
-TEXT runtime·memclr(SB),NOSPLIT,$0-8
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-8
 	MOVL	ptr+0(FP), DI
 	MOVL	n+4(FP), CX
 	MOVQ	CX, BX
@@ -521,11 +544,6 @@ setbar:
 	CALL	runtime·setNextBarrierPC(SB)
 	RET
 
-TEXT runtime·getcallersp(SB),NOSPLIT,$0-12
-	MOVL	argp+0(FP), AX
-	MOVL	AX, ret+8(FP)
-	RET
-
 // int64 runtime·cputicks(void)
 TEXT runtime·cputicks(SB),NOSPLIT,$0-0
 	RDTSC
@@ -561,20 +579,20 @@ TEXT runtime·aeshash(SB),NOSPLIT,$0-20
 	MOVL	AX, ret+16(FP)
 	RET
 
-TEXT runtime·aeshashstr(SB),NOSPLIT,$0-20
-	MOVL	AX, ret+16(FP)
+TEXT runtime·aeshashstr(SB),NOSPLIT,$0-12
+	MOVL	AX, ret+8(FP)
 	RET
 
-TEXT runtime·aeshash32(SB),NOSPLIT,$0-20
-	MOVL	AX, ret+16(FP)
+TEXT runtime·aeshash32(SB),NOSPLIT,$0-12
+	MOVL	AX, ret+8(FP)
 	RET
 
-TEXT runtime·aeshash64(SB),NOSPLIT,$0-20
-	MOVL	AX, ret+16(FP)
+TEXT runtime·aeshash64(SB),NOSPLIT,$0-12
+	MOVL	AX, ret+8(FP)
 	RET
 
 // memequal(p, q unsafe.Pointer, size uintptr) bool
-TEXT runtime·memequal(SB),NOSPLIT,$0-13
+TEXT runtime·memequal(SB),NOSPLIT,$0-17
 	MOVL	a+0(FP), SI
 	MOVL	b+4(FP), DI
 	CMPL	SI, DI
@@ -607,16 +625,16 @@ eq:
 // See runtime_test.go:eqstring_generic for
 // equivalent Go code.
 TEXT runtime·eqstring(SB),NOSPLIT,$0-17
-	MOVL	s1str+0(FP), SI
-	MOVL	s2str+8(FP), DI
+	MOVL	s1_base+0(FP), SI
+	MOVL	s2_base+8(FP), DI
 	CMPL	SI, DI
 	JEQ	same
-	MOVL	s1len+4(FP), BX
+	MOVL	s1_len+4(FP), BX
 	CALL	runtime·memeqbody(SB)
-	MOVB	AX, v+16(FP)
+	MOVB	AX, ret+16(FP)
 	RET
 same:
-	MOVB	$1, v+16(FP)
+	MOVB	$1, ret+16(FP)
 	RET
 
 // a in SI
@@ -973,7 +991,7 @@ eqret:
 	MOVB	AX, ret+24(FP)
 	RET
 
-TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
+TEXT runtime·fastrand(SB), NOSPLIT, $0-4
 	get_tls(CX)
 	MOVL	g(CX), AX
 	MOVL	g_m(AX), AX
diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s
index f02297e..79c28a8 100644
--- a/src/runtime/asm_arm.s
+++ b/src/runtime/asm_arm.s
@@ -118,13 +118,30 @@ TEXT runtime·gosave(SB),NOSPLIT,$-4-4
 	MOVW	$0, R11
 	MOVW	R11, gobuf_lr(R0)
 	MOVW	R11, gobuf_ret(R0)
-	MOVW	R11, gobuf_ctxt(R0)
+	// Assert ctxt is zero. See func save.
+	MOVW	gobuf_ctxt(R0), R0
+	CMP	R0, R11
+	B.EQ	2(PC)
+	CALL	runtime·badctxt(SB)
 	RET
 
 // void gogo(Gobuf*)
 // restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB),NOSPLIT,$-4-4
+TEXT runtime·gogo(SB),NOSPLIT,$8-4
+	MOVW	buf+0(FP), R1
+
+	// If ctxt is not nil, invoke deletion barrier before overwriting.
+	MOVW	gobuf_ctxt(R1), R0
+	CMP	$0, R0
+	B.EQ	nilctxt
+	MOVW	$gobuf_ctxt(R1), R0
+	MOVW	R0, 4(R13)
+	MOVW	$0, R0
+	MOVW	R0, 8(R13)
+	BL	runtime·writebarrierptr_prewrite(SB)
 	MOVW	buf+0(FP), R1
+
+nilctxt:
 	MOVW	gobuf_g(R1), R0
 	BL	setg<>(SB)
 
@@ -281,19 +298,23 @@ TEXT runtime·morestack(SB),NOSPLIT,$-4-0
 	MOVW	g_m(g), R8
 	MOVW	m_g0(R8), R4
 	CMP	g, R4
-	BL.EQ	runtime·abort(SB)
+	BNE	3(PC)
+	BL	runtime·badmorestackg0(SB)
+	B	runtime·abort(SB)
 
 	// Cannot grow signal stack (m->gsignal).
 	MOVW	m_gsignal(R8), R4
 	CMP	g, R4
-	BL.EQ	runtime·abort(SB)
+	BNE	3(PC)
+	BL	runtime·badmorestackgsignal(SB)
+	B	runtime·abort(SB)
 
 	// Called from f.
 	// Set g->sched to context in f.
-	MOVW	R7, (g_sched+gobuf_ctxt)(g)
 	MOVW	R13, (g_sched+gobuf_sp)(g)
 	MOVW	LR, (g_sched+gobuf_pc)(g)
 	MOVW	R3, (g_sched+gobuf_lr)(g)
+	// newstack will fill gobuf.ctxt.
 
 	// Called from f.
 	// Set m->morebuf to f's caller.
@@ -306,6 +327,9 @@ TEXT runtime·morestack(SB),NOSPLIT,$-4-0
 	MOVW	m_g0(R8), R0
 	BL	setg<>(SB)
 	MOVW	(g_sched+gobuf_sp)(g), R13
+	MOVW	$0, R0
+	MOVW.W	R0, -8(R13)	// create a call frame on g0
+	MOVW	R7, 4(R13)	// ctxt argument
 	BL	runtime·newstack(SB)
 
 	// Not reached, but make sure the return PC from the call to newstack
@@ -399,6 +423,7 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20;		\
 	PCDATA  $PCDATA_StackMapIndex, $0;	\
 	BL	(R0);				\
 	/* copy return values back */		\
+	MOVW	argtype+0(FP), R4;		\
 	MOVW	argptr+8(FP), R0;		\
 	MOVW	argsize+12(FP), R2;		\
 	MOVW	retoffset+16(FP), R3;		\
@@ -406,24 +431,19 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-20;		\
 	ADD	R3, R1;				\
 	ADD	R3, R0;				\
 	SUB	R3, R2;				\
-loop:						\
-	CMP	$0, R2;				\
-	B.EQ	end;				\
-	MOVBU.P	1(R1), R5;			\
-	MOVBU.P R5, 1(R0);			\
-	SUB	$1, R2, R2;			\
-	B	loop;				\
-end:						\
-	/* execute write barrier updates */	\
-	MOVW	argtype+0(FP), R1;		\
-	MOVW	argptr+8(FP), R0;		\
-	MOVW	argsize+12(FP), R2;		\
-	MOVW	retoffset+16(FP), R3;		\
-	MOVW	R1, 4(R13);			\
-	MOVW	R0, 8(R13);			\
-	MOVW	R2, 12(R13);			\
-	MOVW	R3, 16(R13);			\
-	BL	runtime·callwritebarrier(SB);	\
+	BL	callRet<>(SB);			\
+	RET
+
+// callRet copies return values back at the end of call*. This is a
+// separate function so it can allocate stack space for the arguments
+// to reflectcallmove. It does not follow the Go ABI; it expects its
+// arguments in registers.
+TEXT callRet<>(SB), NOSPLIT, $16-0
+	MOVW	R4, 4(R13)
+	MOVW	R0, 8(R13)
+	MOVW	R1, 12(R13)
+	MOVW	R2, 16(R13)
+	BL	runtime·reflectcallmove(SB)
 	RET	
 
 CALLFN(·call16, 16)
@@ -473,13 +493,18 @@ TEXT runtime·jmpdefer(SB),NOSPLIT,$0-8
 	B	(R1)
 
 // Save state of caller into g->sched. Smashes R11.
-TEXT gosave<>(SB),NOSPLIT,$0
+TEXT gosave<>(SB),NOSPLIT,$-4
 	MOVW	LR, (g_sched+gobuf_pc)(g)
 	MOVW	R13, (g_sched+gobuf_sp)(g)
 	MOVW	$0, R11
 	MOVW	R11, (g_sched+gobuf_lr)(g)
 	MOVW	R11, (g_sched+gobuf_ret)(g)
 	MOVW	R11, (g_sched+gobuf_ctxt)(g)
+	// Assert ctxt is zero. See func save.
+	MOVW	(g_sched+gobuf_ctxt)(g), R11
+	CMP	$0, R11
+	B.EQ	2(PC)
+	CALL	runtime·badctxt(SB)
 	RET
 
 // func asmcgocall(fn, arg unsafe.Pointer) int32
@@ -695,12 +720,6 @@ setbar:
 	BL	runtime·setNextBarrierPC(SB)
 	RET
 
-TEXT runtime·getcallersp(SB),NOSPLIT,$-4-8
-	MOVW	argp+0(FP), R0
-	MOVW	$-4(R0), R0
-	MOVW	R0, ret+4(FP)
-	RET
-
 TEXT runtime·emptyfunc(SB),0,$0-0
 	RET
 
@@ -855,13 +874,13 @@ samebytes:
 // See runtime_test.go:eqstring_generic for
 // equivalent Go code.
 TEXT runtime·eqstring(SB),NOSPLIT,$-4-17
-	MOVW	s1str+0(FP), R2
-	MOVW	s2str+8(FP), R3
+	MOVW	s1_base+0(FP), R2
+	MOVW	s2_base+8(FP), R3
 	MOVW	$1, R8
-	MOVB	R8, v+16(FP)
+	MOVB	R8, ret+16(FP)
 	CMP	R2, R3
 	RET.EQ
-	MOVW	s1len+4(FP), R0
+	MOVW	s1_len+4(FP), R0
 	ADD	R2, R0, R6
 loop:
 	CMP	R2, R6
@@ -871,7 +890,7 @@ loop:
 	CMP	R4, R5
 	BEQ	loop
 	MOVW	$0, R8
-	MOVB	R8, v+16(FP)
+	MOVB	R8, ret+16(FP)
 	RET
 
 // TODO: share code with memequal?
@@ -952,7 +971,7 @@ _sib_notfound:
 	MOVW	R0, ret+12(FP)
 	RET
 
-TEXT runtime·fastrand1(SB),NOSPLIT,$-4-4
+TEXT runtime·fastrand(SB),NOSPLIT,$-4-4
 	MOVW	g_m(g), R1
 	MOVW	m_fastrand(R1), R0
 	ADD.S	R0, R0
@@ -1033,8 +1052,8 @@ TEXT runtime·usplitR0(SB),NOSPLIT,$0
 	SUB	R1, R3, R1
 	RET
 
-TEXT runtime·sigreturn(SB),NOSPLIT,$0-4
-        RET
+TEXT runtime·sigreturn(SB),NOSPLIT,$0-0
+	RET
 
 #ifndef GOOS_nacl
 // This is called from .init_array and follows the platform, not Go, ABI.
diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s
index 7ebd7ba..0e286d4 100644
--- a/src/runtime/asm_arm64.s
+++ b/src/runtime/asm_arm64.s
@@ -11,9 +11,6 @@
 TEXT runtime·rt0_go(SB),NOSPLIT,$0
 	// SP = stack; R0 = argc; R1 = argv
 
-	// initialize essential registers
-	BL	runtime·reginit(SB)
-
 	SUB	$32, RSP
 	MOVW	R0, 8(RSP) // argc
 	MOVD	R1, 16(RSP) // argv
@@ -100,15 +97,6 @@ TEXT runtime·breakpoint(SB),NOSPLIT,$-8-0
 TEXT runtime·asminit(SB),NOSPLIT,$-8-0
 	RET
 
-TEXT runtime·reginit(SB),NOSPLIT,$-8-0
-	// initialize essential FP registers
-	FMOVD	$4503601774854144.0, F27
-	FMOVD	$0.5, F29
-	FSUBD	F29, F29, F28
-	FADDD	F29, F29, F30
-	FADDD	F30, F30, F31
-	RET
-
 /*
  *  go-routine
  */
@@ -123,13 +111,29 @@ TEXT runtime·gosave(SB), NOSPLIT, $-8-8
 	MOVD	g, gobuf_g(R3)
 	MOVD	ZR, gobuf_lr(R3)
 	MOVD	ZR, gobuf_ret(R3)
-	MOVD	ZR, gobuf_ctxt(R3)
+	// Assert ctxt is zero. See func save.
+	MOVD	gobuf_ctxt(R3), R0
+	CMP	$0, R0
+	BEQ	2(PC)
+	CALL	runtime·badctxt(SB)
 	RET
 
 // void gogo(Gobuf*)
 // restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), NOSPLIT, $-8-8
+TEXT runtime·gogo(SB), NOSPLIT, $24-8
 	MOVD	buf+0(FP), R5
+
+	// If ctxt is not nil, invoke deletion barrier before overwriting.
+	MOVD	gobuf_ctxt(R5), R0
+	CMP	$0, R0
+	BEQ	nilctxt
+	MOVD	$gobuf_ctxt(R5), R0
+	MOVD	R0, 8(RSP)
+	MOVD	ZR, 16(RSP)
+	BL	runtime·writebarrierptr_prewrite(SB)
+	MOVD	buf+0(FP), R5
+
+nilctxt:
 	MOVD	gobuf_g(R5), g
 	BL	runtime·save_g(SB)
 
@@ -268,22 +272,24 @@ TEXT runtime·morestack(SB),NOSPLIT,$-8-0
 	MOVD	g_m(g), R8
 	MOVD	m_g0(R8), R4
 	CMP	g, R4
-	BNE	2(PC)
+	BNE	3(PC)
+	BL	runtime·badmorestackg0(SB)
 	B	runtime·abort(SB)
 
 	// Cannot grow signal stack (m->gsignal).
 	MOVD	m_gsignal(R8), R4
 	CMP	g, R4
-	BNE	2(PC)
+	BNE	3(PC)
+	BL	runtime·badmorestackgsignal(SB)
 	B	runtime·abort(SB)
 
 	// Called from f.
 	// Set g->sched to context in f
-	MOVD	R26, (g_sched+gobuf_ctxt)(g)
 	MOVD	RSP, R0
 	MOVD	R0, (g_sched+gobuf_sp)(g)
 	MOVD	LR, (g_sched+gobuf_pc)(g)
 	MOVD	R3, (g_sched+gobuf_lr)(g)
+	// newstack will fill gobuf.ctxt.
 
 	// Called from f.
 	// Set m->morebuf to f's callers.
@@ -297,6 +303,8 @@ TEXT runtime·morestack(SB),NOSPLIT,$-8-0
 	BL	runtime·save_g(SB)
 	MOVD	(g_sched+gobuf_sp)(g), R0
 	MOVD	R0, RSP
+	MOVD.W	$0, -16(RSP)	// create a call frame on g0
+	MOVD	R26, 8(RSP)	// ctxt argument
 	BL	runtime·newstack(SB)
 
 	// Not reached, but make sure the return PC from the call to newstack
@@ -343,8 +351,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
 
 TEXT ·reflectcall(SB), NOSPLIT, $-8-32
 	MOVWU argsize+24(FP), R16
-	// NOTE(rsc): No call16, because CALLFN needs four words
-	// of argument space to invoke callwritebarrier.
 	DISPATCH(runtime·call32, 32)
 	DISPATCH(runtime·call64, 64)
 	DISPATCH(runtime·call128, 128)
@@ -395,33 +401,27 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24;		\
 	PCDATA  $PCDATA_StackMapIndex, $0;	\
 	BL	(R0);				\
 	/* copy return values back */		\
+	MOVD	argtype+0(FP), R7;		\
 	MOVD	arg+16(FP), R3;			\
 	MOVWU	n+24(FP), R4;			\
 	MOVWU	retoffset+28(FP), R6;		\
-	MOVD	RSP, R5;				\
+	ADD	$8, RSP, R5;			\
 	ADD	R6, R5; 			\
 	ADD	R6, R3;				\
 	SUB	R6, R4;				\
-	ADD	$(8-1), R5;			\
-	SUB	$1, R3;				\
-	ADD	R5, R4;				\
-loop:						\
-	CMP	R5, R4;				\
-	BEQ	end;				\
-	MOVBU.W	1(R5), R6;			\
-	MOVBU.W	R6, 1(R3);			\
-	B	loop;				\
-end:						\
-	/* execute write barrier updates */	\
-	MOVD	argtype+0(FP), R7;		\
-	MOVD	arg+16(FP), R3;			\
-	MOVWU	n+24(FP), R4;			\
-	MOVWU	retoffset+28(FP), R6;		\
-	MOVD	R7, 8(RSP);			\
-	MOVD	R3, 16(RSP);			\
-	MOVD	R4, 24(RSP);			\
-	MOVD	R6, 32(RSP);			\
-	BL	runtime·callwritebarrier(SB);	\
+	BL	callRet<>(SB);			\
+	RET
+
+// callRet copies return values back at the end of call*. This is a
+// separate function so it can allocate stack space for the arguments
+// to reflectcallmove. It does not follow the Go ABI; it expects its
+// arguments in registers.
+TEXT callRet<>(SB), NOSPLIT, $40-0
+	MOVD	R7, 8(RSP)
+	MOVD	R3, 16(RSP)
+	MOVD	R5, 24(RSP)
+	MOVD	R4, 32(RSP)
+	BL	runtime·reflectcallmove(SB)
 	RET
 
 // These have 8 added to make the overall frame size a multiple of 16,
@@ -499,7 +499,11 @@ TEXT gosave<>(SB),NOSPLIT,$-8
 	MOVD	R0, (g_sched+gobuf_sp)(g)
 	MOVD	$0, (g_sched+gobuf_lr)(g)
 	MOVD	$0, (g_sched+gobuf_ret)(g)
-	MOVD	$0, (g_sched+gobuf_ctxt)(g)
+	// Assert ctxt is zero. See func save.
+	MOVD	(g_sched+gobuf_ctxt)(g), R0
+	CMP	$0, R0
+	BEQ	2(PC)
+	CALL	runtime·badctxt(SB)
 	RET
 
 // func asmcgocall(fn, arg unsafe.Pointer) int32
@@ -743,12 +747,6 @@ setbar:
 	BL	runtime·setNextBarrierPC(SB)
 	RET
 
-TEXT runtime·getcallersp(SB),NOSPLIT,$0-16
-	MOVD	argp+0(FP), R0
-	SUB	$8, R0
-	MOVD	R0, ret+8(FP)
-	RET
-
 TEXT runtime·abort(SB),NOSPLIT,$-8-0
 	B	(ZR)
 	UNDEF
@@ -869,9 +867,9 @@ samebytes:
 // See runtime_test.go:eqstring_generic for
 // equivalent Go code.
 TEXT runtime·eqstring(SB),NOSPLIT,$0-33
-	MOVD	s1str+0(FP), R0
-	MOVD	s1len+8(FP), R1
-	MOVD	s2str+16(FP), R2
+	MOVD	s1_base+0(FP), R0
+	MOVD	s1_len+8(FP), R1
+	MOVD	s2_base+16(FP), R2
 	ADD	R0, R1		// end
 loop:
 	CMP	R0, R1
@@ -961,7 +959,7 @@ equal:
 	MOVB	R0, ret+48(FP)
 	RET
 
-TEXT runtime·fastrand1(SB),NOSPLIT,$-8-4
+TEXT runtime·fastrand(SB),NOSPLIT,$-8-4
 	MOVD	g_m(g), R1
 	MOVWU	m_fastrand(R1), R0
 	ADD	R0, R0
@@ -996,8 +994,8 @@ TEXT runtime·prefetcht2(SB),NOSPLIT,$0-8
 TEXT runtime·prefetchnta(SB),NOSPLIT,$0-8
 	RET
 
-TEXT runtime·sigreturn(SB),NOSPLIT,$0-8
-        RET
+TEXT runtime·sigreturn(SB),NOSPLIT,$0-0
+	RET
 
 // This is called from .init_array and follows the platform, not Go, ABI.
 TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0
diff --git a/src/runtime/asm_mips64x.s b/src/runtime/asm_mips64x.s
index 7dd35aa..c2d991d 100644
--- a/src/runtime/asm_mips64x.s
+++ b/src/runtime/asm_mips64x.s
@@ -14,9 +14,6 @@
 TEXT runtime·rt0_go(SB),NOSPLIT,$0
 	// R29 = stack; R4 = argc; R5 = argv
 
-	// initialize essential registers
-	JAL	runtime·reginit(SB)
-
 	ADDV	$-24, R29
 	MOVW	R4, 8(R29) // argc
 	MOVV	R5, 16(R29) // argv
@@ -88,19 +85,6 @@ TEXT runtime·breakpoint(SB),NOSPLIT,$-8-0
 TEXT runtime·asminit(SB),NOSPLIT,$-8-0
 	RET
 
-TEXT _cgo_reginit(SB),NOSPLIT,$-8-0
-	// crosscall1 needs to reginit, but can't
-	// get at the 'runtime.reginit' symbol.
-	JMP	runtime·reginit(SB)
-
-TEXT runtime·reginit(SB),NOSPLIT,$-8-0
-	// initialize essential FP registers
-	MOVD	$0.5, F26
-	SUBD	F26, F26, F24
-	ADDD	F26, F26, F28
-	ADDD	F28, F28, F30
-	RET
-
 /*
  *  go-routine
  */
@@ -114,13 +98,27 @@ TEXT runtime·gosave(SB), NOSPLIT, $-8-8
 	MOVV	g, gobuf_g(R1)
 	MOVV	R0, gobuf_lr(R1)
 	MOVV	R0, gobuf_ret(R1)
-	MOVV	R0, gobuf_ctxt(R1)
+	// Assert ctxt is zero. See func save.
+	MOVV	gobuf_ctxt(R1), R1
+	BEQ	R1, 2(PC)
+	JAL	runtime·badctxt(SB)
 	RET
 
 // void gogo(Gobuf*)
 // restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), NOSPLIT, $-8-8
+TEXT runtime·gogo(SB), NOSPLIT, $16-8
+	MOVV	buf+0(FP), R3
+
+	// If ctxt is not nil, invoke deletion barrier before overwriting.
+	MOVV	gobuf_ctxt(R3), R1
+	BEQ	R1, nilctxt
+	MOVV	$gobuf_ctxt(R3), R1
+	MOVV	R1, 8(R29)
+	MOVV	R0, 16(R29)
+	JAL	runtime·writebarrierptr_prewrite(SB)
 	MOVV	buf+0(FP), R3
+
+nilctxt:
 	MOVV	gobuf_g(R3), g	// make sure g is not nil
 	JAL	runtime·save_g(SB)
 
@@ -247,20 +245,22 @@ TEXT runtime·morestack(SB),NOSPLIT,$-8-0
 	// Cannot grow scheduler stack (m->g0).
 	MOVV	g_m(g), R7
 	MOVV	m_g0(R7), R8
-	BNE	g, R8, 2(PC)
+	BNE	g, R8, 3(PC)
+	JAL	runtime·badmorestackg0(SB)
 	JAL	runtime·abort(SB)
 
 	// Cannot grow signal stack (m->gsignal).
 	MOVV	m_gsignal(R7), R8
-	BNE	g, R8, 2(PC)
+	BNE	g, R8, 3(PC)
+	JAL	runtime·badmorestackgsignal(SB)
 	JAL	runtime·abort(SB)
 
 	// Called from f.
 	// Set g->sched to context in f.
-	MOVV	REGCTXT, (g_sched+gobuf_ctxt)(g)
 	MOVV	R29, (g_sched+gobuf_sp)(g)
 	MOVV	R31, (g_sched+gobuf_pc)(g)
 	MOVV	R3, (g_sched+gobuf_lr)(g)
+	// newstack will fill gobuf.ctxt.
 
 	// Called from f.
 	// Set m->morebuf to f's caller.
@@ -272,6 +272,10 @@ TEXT runtime·morestack(SB),NOSPLIT,$-8-0
 	MOVV	m_g0(R7), g
 	JAL	runtime·save_g(SB)
 	MOVV	(g_sched+gobuf_sp)(g), R29
+	// Create a stack frame on g0 to call newstack.
+	MOVV	R0, -16(R29)	// Zero saved LR in frame
+	ADDV	$-16, R29
+	MOVV	REGCTXT, 8(R29)	// ctxt argument
 	JAL	runtime·newstack(SB)
 
 	// Not reached, but make sure the return PC from the call to newstack
@@ -319,8 +323,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
 
 TEXT ·reflectcall(SB), NOSPLIT, $-8-32
 	MOVWU argsize+24(FP), R1
-	// NOTE(rsc): No call16, because CALLFN needs four words
-	// of argument space to invoke callwritebarrier.
 	DISPATCH(runtime·call32, 32)
 	DISPATCH(runtime·call64, 64)
 	DISPATCH(runtime·call128, 128)
@@ -371,33 +373,27 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24;		\
 	PCDATA  $PCDATA_StackMapIndex, $0;	\
 	JAL	(R4);				\
 	/* copy return values back */		\
+	MOVV	argtype+0(FP), R5;		\
 	MOVV	arg+16(FP), R1;			\
 	MOVWU	n+24(FP), R2;			\
 	MOVWU	retoffset+28(FP), R4;		\
-	MOVV	R29, R3;				\
+	ADDV	$8, R29, R3;				\
 	ADDV	R4, R3; 			\
 	ADDV	R4, R1;				\
 	SUBVU	R4, R2;				\
-	ADDV	$8, R3;			\
-	ADDV	R3, R2;				\
-loop:						\
-	BEQ	R3, R2, end;				\
-	MOVBU	(R3), R4;			\
-	ADDV	$1, R3;			\
-	MOVBU	R4, (R1);			\
-	ADDV	$1, R1;			\
-	JMP	loop;				\
-end:						\
-	/* execute write barrier updates */	\
-	MOVV	argtype+0(FP), R5;		\
-	MOVV	arg+16(FP), R1;			\
-	MOVWU	n+24(FP), R2;			\
-	MOVWU	retoffset+28(FP), R4;		\
-	MOVV	R5, 8(R29);			\
-	MOVV	R1, 16(R29);			\
-	MOVV	R2, 24(R29);			\
-	MOVV	R4, 32(R29);			\
-	JAL	runtime·callwritebarrier(SB);	\
+	JAL	callRet<>(SB);			\
+	RET
+
+// callRet copies return values back at the end of call*. This is a
+// separate function so it can allocate stack space for the arguments
+// to reflectcallmove. It does not follow the Go ABI; it expects its
+// arguments in registers.
+TEXT callRet<>(SB), NOSPLIT, $32-0
+	MOVV	R5, 8(R29)
+	MOVV	R1, 16(R29)
+	MOVV	R3, 24(R29)
+	MOVV	R2, 32(R29)
+	JAL	runtime·reflectcallmove(SB)
 	RET
 
 CALLFN(·call16, 16)
@@ -447,13 +443,16 @@ TEXT runtime·jmpdefer(SB), NOSPLIT, $-8-16
 	MOVV	0(REGCTXT), R4
 	JMP	(R4)
 
-// Save state of caller into g->sched. Smashes R31.
+// Save state of caller into g->sched. Smashes R1.
 TEXT gosave<>(SB),NOSPLIT,$-8
 	MOVV	R31, (g_sched+gobuf_pc)(g)
 	MOVV	R29, (g_sched+gobuf_sp)(g)
 	MOVV	R0, (g_sched+gobuf_lr)(g)
 	MOVV	R0, (g_sched+gobuf_ret)(g)
-	MOVV	R0, (g_sched+gobuf_ctxt)(g)
+	// Assert ctxt is zero. See func save.
+	MOVV	(g_sched+gobuf_ctxt)(g), R1
+	BEQ	R1, 2(PC)
+	JAL	runtime·badctxt(SB)
 	RET
 
 // func asmcgocall(fn, arg unsafe.Pointer) int32
@@ -659,12 +658,6 @@ setbar:
 	JAL	runtime·setNextBarrierPC(SB)
 	RET
 
-TEXT runtime·getcallersp(SB),NOSPLIT,$0-16
-	MOVV	argp+0(FP), R1
-	ADDV	$-8, R1
-	MOVV	R1, ret+8(FP)
-	RET
-
 TEXT runtime·abort(SB),NOSPLIT,$-8-0
 	MOVW	(R0), R0
 	UNDEF
@@ -746,13 +739,13 @@ eq:
 // See runtime_test.go:eqstring_generic for
 // equivalent Go code.
 TEXT runtime·eqstring(SB),NOSPLIT,$0-33
-	MOVV	s1str+0(FP), R1
-	MOVV	s2str+16(FP), R2
+	MOVV	s1_base+0(FP), R1
+	MOVV	s2_base+16(FP), R2
 	MOVV	$1, R3
 	MOVB	R3, ret+32(FP)
 	BNE	R1, R2, 2(PC)
 	RET
-	MOVV	s1len+8(FP), R3
+	MOVV	s1_len+8(FP), R3
 	ADDV	R1, R3, R4
 loop:
 	BNE	R1, R4, 2(PC)
@@ -838,7 +831,7 @@ notfound:
 	MOVV	R1, ret+24(FP)
 	RET
 
-TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
+TEXT runtime·fastrand(SB), NOSPLIT, $0-4
 	MOVV	g_m(g), R2
 	MOVWU	m_fastrand(R2), R1
 	ADDU	R1, R1
diff --git a/src/runtime/asm_mipsx.s b/src/runtime/asm_mipsx.s
new file mode 100644
index 0000000..cd855c7
--- /dev/null
+++ b/src/runtime/asm_mipsx.s
@@ -0,0 +1,794 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "go_asm.h"
+#include "go_tls.h"
+#include "funcdata.h"
+#include "textflag.h"
+
+#define	REGCTXT	R22
+
+TEXT runtime·rt0_go(SB),NOSPLIT,$0
+	// R29 = stack; R1 = argc; R2 = argv
+
+	ADDU	$-12, R29
+	MOVW	R1, 4(R29)	// argc
+	MOVW	R2, 8(R29)	// argv
+
+	// create istack out of the given (operating system) stack.
+	// _cgo_init may update stackguard.
+	MOVW	$runtime·g0(SB), g
+	MOVW	$(-64*1024), R23
+	ADD	R23, R29, R1
+	MOVW	R1, g_stackguard0(g)
+	MOVW	R1, g_stackguard1(g)
+	MOVW	R1, (g_stack+stack_lo)(g)
+	MOVW	R29, (g_stack+stack_hi)(g)
+
+// TODO(mips32): cgo
+
+nocgo:
+	// update stackguard after _cgo_init
+	MOVW	(g_stack+stack_lo)(g), R1
+	ADD	$const__StackGuard, R1
+	MOVW	R1, g_stackguard0(g)
+	MOVW	R1, g_stackguard1(g)
+
+	// set the per-goroutine and per-mach "registers"
+	MOVW	$runtime·m0(SB), R1
+
+	// save m->g0 = g0
+	MOVW	g, m_g0(R1)
+	// save m0 to g0->m
+	MOVW	R1, g_m(g)
+
+	JAL	runtime·check(SB)
+
+	// args are already prepared
+	JAL	runtime·args(SB)
+	JAL	runtime·osinit(SB)
+	JAL	runtime·schedinit(SB)
+
+	// create a new goroutine to start program
+	MOVW	$runtime·mainPC(SB), R1	// entry
+	ADDU	$-12, R29
+	MOVW	R1, 8(R29)
+	MOVW	R0, 4(R29)
+	MOVW	R0, 0(R29)
+	JAL	runtime·newproc(SB)
+	ADDU	$12, R29
+
+	// start this M
+	JAL	runtime·mstart(SB)
+
+	UNDEF
+	RET
+
+DATA	runtime·mainPC+0(SB)/4,$runtime·main(SB)
+GLOBL	runtime·mainPC(SB),RODATA,$4
+
+TEXT runtime·breakpoint(SB),NOSPLIT,$0-0
+	BREAK
+	RET
+
+TEXT runtime·asminit(SB),NOSPLIT,$0-0
+	RET
+
+/*
+ *  go-routine
+ */
+
+// void gosave(Gobuf*)
+// save state in Gobuf; setjmp
+TEXT runtime·gosave(SB),NOSPLIT,$-4-4
+	MOVW	buf+0(FP), R1
+	MOVW	R29, gobuf_sp(R1)
+	MOVW	R31, gobuf_pc(R1)
+	MOVW	g, gobuf_g(R1)
+	MOVW	R0, gobuf_lr(R1)
+	MOVW	R0, gobuf_ret(R1)
+	// Assert ctxt is zero. See func save.
+	MOVW	gobuf_ctxt(R1), R1
+	BEQ	R1, 2(PC)
+	JAL	runtime·badctxt(SB)
+	RET
+
+// void gogo(Gobuf*)
+// restore state from Gobuf; longjmp
+TEXT runtime·gogo(SB),NOSPLIT,$8-4
+	MOVW	buf+0(FP), R3
+
+	// If ctxt is not nil, invoke deletion barrier before overwriting.
+	MOVW	gobuf_ctxt(R3), R1
+	BEQ	R1, nilctxt
+	MOVW	$gobuf_ctxt(R3), R1
+	MOVW	R1, 4(R29)
+	MOVW	R0, 8(R29)
+	JAL	runtime·writebarrierptr_prewrite(SB)
+	MOVW	buf+0(FP), R3
+
+nilctxt:
+	MOVW	gobuf_g(R3), g	// make sure g is not nil
+	JAL	runtime·save_g(SB)
+
+	MOVW	0(g), R2
+	MOVW	gobuf_sp(R3), R29
+	MOVW	gobuf_lr(R3), R31
+	MOVW	gobuf_ret(R3), R1
+	MOVW	gobuf_ctxt(R3), REGCTXT
+	MOVW	R0, gobuf_sp(R3)
+	MOVW	R0, gobuf_ret(R3)
+	MOVW	R0, gobuf_lr(R3)
+	MOVW	R0, gobuf_ctxt(R3)
+	MOVW	gobuf_pc(R3), R4
+	JMP	(R4)
+
+// void mcall(fn func(*g))
+// Switch to m->g0's stack, call fn(g).
+// Fn must never return. It should gogo(&g->sched)
+// to keep running g.
+TEXT runtime·mcall(SB),NOSPLIT,$-4-4
+	// Save caller state in g->sched
+	MOVW	R29, (g_sched+gobuf_sp)(g)
+	MOVW	R31, (g_sched+gobuf_pc)(g)
+	MOVW	R0, (g_sched+gobuf_lr)(g)
+	MOVW	g, (g_sched+gobuf_g)(g)
+
+	// Switch to m->g0 & its stack, call fn.
+	MOVW	g, R1
+	MOVW	g_m(g), R3
+	MOVW	m_g0(R3), g
+	JAL	runtime·save_g(SB)
+	BNE	g, R1, 2(PC)
+	JMP	runtime·badmcall(SB)
+	MOVW	fn+0(FP), REGCTXT	// context
+	MOVW	0(REGCTXT), R4	// code pointer
+	MOVW	(g_sched+gobuf_sp)(g), R29	// sp = m->g0->sched.sp
+	ADDU	$-8, R29	// make room for 1 arg and fake LR
+	MOVW	R1, 4(R29)
+	MOVW	R0, 0(R29)
+	JAL	(R4)
+	JMP	runtime·badmcall2(SB)
+
+// systemstack_switch is a dummy routine that systemstack leaves at the bottom
+// of the G stack.  We need to distinguish the routine that
+// lives at the bottom of the G stack from the one that lives
+// at the top of the system stack because the one at the top of
+// the system stack terminates the stack walk (see topofstack()).
+TEXT runtime·systemstack_switch(SB),NOSPLIT,$0-0
+	UNDEF
+	JAL	(R31)	// make sure this function is not leaf
+	RET
+
+// func systemstack(fn func())
+TEXT runtime·systemstack(SB),NOSPLIT,$0-4
+	MOVW	fn+0(FP), R1	// R1 = fn
+	MOVW	R1, REGCTXT	// context
+	MOVW	g_m(g), R2	// R2 = m
+
+	MOVW	m_gsignal(R2), R3	// R3 = gsignal
+	BEQ	g, R3, noswitch
+
+	MOVW	m_g0(R2), R3	// R3 = g0
+	BEQ	g, R3, noswitch
+
+	MOVW	m_curg(R2), R4
+	BEQ	g, R4, switch
+
+	// Bad: g is not gsignal, not g0, not curg. What is it?
+	// Hide call from linker nosplit analysis.
+	MOVW	$runtime·badsystemstack(SB), R4
+	JAL	(R4)
+
+switch:
+	// save our state in g->sched.  Pretend to
+	// be systemstack_switch if the G stack is scanned.
+	MOVW	$runtime·systemstack_switch(SB), R4
+	ADDU	$8, R4	// get past prologue
+	MOVW	R4, (g_sched+gobuf_pc)(g)
+	MOVW	R29, (g_sched+gobuf_sp)(g)
+	MOVW	R0, (g_sched+gobuf_lr)(g)
+	MOVW	g, (g_sched+gobuf_g)(g)
+
+	// switch to g0
+	MOVW	R3, g
+	JAL	runtime·save_g(SB)
+	MOVW	(g_sched+gobuf_sp)(g), R1
+	// make it look like mstart called systemstack on g0, to stop traceback
+	ADDU	$-4, R1
+	MOVW	$runtime·mstart(SB), R2
+	MOVW	R2, 0(R1)
+	MOVW	R1, R29
+
+	// call target function
+	MOVW	0(REGCTXT), R4	// code pointer
+	JAL	(R4)
+
+	// switch back to g
+	MOVW	g_m(g), R1
+	MOVW	m_curg(R1), g
+	JAL	runtime·save_g(SB)
+	MOVW	(g_sched+gobuf_sp)(g), R29
+	MOVW	R0, (g_sched+gobuf_sp)(g)
+	RET
+
+noswitch:
+	// already on m stack, just call directly
+	MOVW	0(REGCTXT), R4	// code pointer
+	JAL	(R4)
+	RET
+
+/*
+ * support for morestack
+ */
+
+// Called during function prolog when more stack is needed.
+// Caller has already loaded:
+// R1: framesize, R2: argsize, R3: LR
+//
+// The traceback routines see morestack on a g0 as being
+// the top of a stack (for example, morestack calling newstack
+// calling the scheduler calling newm calling gc), so we must
+// record an argument size. For that purpose, it has no arguments.
+TEXT runtime·morestack(SB),NOSPLIT,$-4-0
+	// Cannot grow scheduler stack (m->g0).
+	MOVW	g_m(g), R7
+	MOVW	m_g0(R7), R8
+	BNE	g, R8, 3(PC)
+	JAL	runtime·badmorestackg0(SB)
+	JAL	runtime·abort(SB)
+
+	// Cannot grow signal stack (m->gsignal).
+	MOVW	m_gsignal(R7), R8
+	BNE	g, R8, 3(PC)
+	JAL	runtime·badmorestackgsignal(SB)
+	JAL	runtime·abort(SB)
+
+	// Called from f.
+	// Set g->sched to context in f.
+	MOVW	R29, (g_sched+gobuf_sp)(g)
+	MOVW	R31, (g_sched+gobuf_pc)(g)
+	MOVW	R3, (g_sched+gobuf_lr)(g)
+	// newstack will fill gobuf.ctxt.
+
+	// Called from f.
+	// Set m->morebuf to f's caller.
+	MOVW	R3, (m_morebuf+gobuf_pc)(R7)	// f's caller's PC
+	MOVW	R29, (m_morebuf+gobuf_sp)(R7)	// f's caller's SP
+	MOVW	g, (m_morebuf+gobuf_g)(R7)
+
+	// Call newstack on m->g0's stack.
+	MOVW	m_g0(R7), g
+	JAL	runtime·save_g(SB)
+	MOVW	(g_sched+gobuf_sp)(g), R29
+	// Create a stack frame on g0 to call newstack.
+	MOVW	R0, -8(R29)	// Zero saved LR in frame
+	ADDU	$-8, R29
+	MOVW	REGCTXT, 4(R29)	// ctxt argument
+	JAL	runtime·newstack(SB)
+
+	// Not reached, but make sure the return PC from the call to newstack
+	// is still in this function, and not the beginning of the next.
+	UNDEF
+
+TEXT runtime·morestack_noctxt(SB),NOSPLIT,$0-0
+	MOVW	R0, REGCTXT
+	JMP	runtime·morestack(SB)
+
+TEXT runtime·stackBarrier(SB),NOSPLIT,$0
+	// We came here via a RET to an overwritten LR.
+	// R1 may be live. Other registers are available.
+
+	// Get the original return PC, g.stkbar[g.stkbarPos].savedLRVal.
+	MOVW	(g_stkbar+slice_array)(g), R2
+	MOVW	g_stkbarPos(g), R3
+	MOVW	$stkbar__size, R4
+	MULU	R3, R4
+	MOVW	LO, R4
+	ADDU	R2, R4
+	MOVW	stkbar_savedLRVal(R4), R4
+	ADDU	$1, R3
+	MOVW	R3, g_stkbarPos(g)	// Record that this stack barrier was hit.
+	JMP	(R4)	// Jump to the original return PC.
+
+// reflectcall: call a function with the given argument list
+// func call(argtype *_type, f *FuncVal, arg *byte, argsize, retoffset uint32).
+// we don't have variable-sized frames, so we use a small number
+// of constant-sized-frame functions to encode a few bits of size in the pc.
+
+#define DISPATCH(NAME,MAXSIZE)	\
+	MOVW	$MAXSIZE, R23;	\
+	SGTU	R1, R23, R23;	\
+	BNE	R23, 3(PC);	\
+	MOVW	$NAME(SB), R4;	\
+	JMP	(R4)
+
+TEXT reflect·call(SB),NOSPLIT,$0-20
+	JMP	·reflectcall(SB)
+
+TEXT ·reflectcall(SB),NOSPLIT,$-4-20
+	MOVW	argsize+12(FP), R1
+
+	DISPATCH(runtime·call16, 16)
+	DISPATCH(runtime·call32, 32)
+	DISPATCH(runtime·call64, 64)
+	DISPATCH(runtime·call128, 128)
+	DISPATCH(runtime·call256, 256)
+	DISPATCH(runtime·call512, 512)
+	DISPATCH(runtime·call1024, 1024)
+	DISPATCH(runtime·call2048, 2048)
+	DISPATCH(runtime·call4096, 4096)
+	DISPATCH(runtime·call8192, 8192)
+	DISPATCH(runtime·call16384, 16384)
+	DISPATCH(runtime·call32768, 32768)
+	DISPATCH(runtime·call65536, 65536)
+	DISPATCH(runtime·call131072, 131072)
+	DISPATCH(runtime·call262144, 262144)
+	DISPATCH(runtime·call524288, 524288)
+	DISPATCH(runtime·call1048576, 1048576)
+	DISPATCH(runtime·call2097152, 2097152)
+	DISPATCH(runtime·call4194304, 4194304)
+	DISPATCH(runtime·call8388608, 8388608)
+	DISPATCH(runtime·call16777216, 16777216)
+	DISPATCH(runtime·call33554432, 33554432)
+	DISPATCH(runtime·call67108864, 67108864)
+	DISPATCH(runtime·call134217728, 134217728)
+	DISPATCH(runtime·call268435456, 268435456)
+	DISPATCH(runtime·call536870912, 536870912)
+	DISPATCH(runtime·call1073741824, 1073741824)
+	MOVW	$runtime·badreflectcall(SB), R4
+	JMP	(R4)
+
+#define CALLFN(NAME,MAXSIZE)	\
+TEXT NAME(SB),WRAPPER,$MAXSIZE-20;	\
+	NO_LOCAL_POINTERS;	\
+	/* copy arguments to stack */		\
+	MOVW	arg+8(FP), R1;	\
+	MOVW	argsize+12(FP), R2;	\
+	MOVW	R29, R3;	\
+	ADDU	$4, R3;	\
+	ADDU	R3, R2;	\
+	BEQ	R3, R2, 6(PC);	\
+	MOVBU	(R1), R4;	\
+	ADDU	$1, R1;	\
+	MOVBU	R4, (R3);	\
+	ADDU	$1, R3;	\
+	JMP	-5(PC);	\
+	/* call function */			\
+	MOVW	f+4(FP), REGCTXT;	\
+	MOVW	(REGCTXT), R4;	\
+	PCDATA	$PCDATA_StackMapIndex, $0;	\
+	JAL	(R4);	\
+	/* copy return values back */		\
+	MOVW	argtype+0(FP), R5;	\
+	MOVW	arg+8(FP), R1;	\
+	MOVW	n+12(FP), R2;	\
+	MOVW	retoffset+16(FP), R4;	\
+	ADDU	$4, R29, R3;	\
+	ADDU	R4, R3;	\
+	ADDU	R4, R1;	\
+	SUBU	R4, R2;	\
+	JAL	callRet<>(SB);		\
+	RET
+
+// callRet copies return values back at the end of call*. This is a
+// separate function so it can allocate stack space for the arguments
+// to reflectcallmove. It does not follow the Go ABI; it expects its
+// arguments in registers.
+TEXT callRet<>(SB), NOSPLIT, $16-0
+	MOVW	R5, 4(R29)
+	MOVW	R1, 8(R29)
+	MOVW	R3, 12(R29)
+	MOVW	R2, 16(R29)
+	JAL	runtime·reflectcallmove(SB)
+	RET
+
+CALLFN(·call16, 16)
+CALLFN(·call32, 32)
+CALLFN(·call64, 64)
+CALLFN(·call128, 128)
+CALLFN(·call256, 256)
+CALLFN(·call512, 512)
+CALLFN(·call1024, 1024)
+CALLFN(·call2048, 2048)
+CALLFN(·call4096, 4096)
+CALLFN(·call8192, 8192)
+CALLFN(·call16384, 16384)
+CALLFN(·call32768, 32768)
+CALLFN(·call65536, 65536)
+CALLFN(·call131072, 131072)
+CALLFN(·call262144, 262144)
+CALLFN(·call524288, 524288)
+CALLFN(·call1048576, 1048576)
+CALLFN(·call2097152, 2097152)
+CALLFN(·call4194304, 4194304)
+CALLFN(·call8388608, 8388608)
+CALLFN(·call16777216, 16777216)
+CALLFN(·call33554432, 33554432)
+CALLFN(·call67108864, 67108864)
+CALLFN(·call134217728, 134217728)
+CALLFN(·call268435456, 268435456)
+CALLFN(·call536870912, 536870912)
+CALLFN(·call1073741824, 1073741824)
+
+TEXT runtime·procyield(SB),NOSPLIT,$0-4
+	RET
+
+// void jmpdefer(fv, sp);
+// called from deferreturn.
+// 1. grab stored LR for caller
+// 2. sub 8 bytes to get back to JAL deferreturn
+// 3. JMP to fn
+TEXT runtime·jmpdefer(SB),NOSPLIT,$0-8
+	MOVW	0(R29), R31
+	ADDU	$-8, R31
+
+	MOVW	fv+0(FP), REGCTXT
+	MOVW	argp+4(FP), R29
+	ADDU	$-4, R29
+	NOR	R0, R0	// prevent scheduling
+	MOVW	0(REGCTXT), R4
+	JMP	(R4)
+
+// Save state of caller into g->sched. Smashes R1.
+TEXT gosave<>(SB),NOSPLIT,$0
+	MOVW	R31, (g_sched+gobuf_pc)(g)
+	MOVW	R29, (g_sched+gobuf_sp)(g)
+	MOVW	R0, (g_sched+gobuf_lr)(g)
+	MOVW	R0, (g_sched+gobuf_ret)(g)
+	// Assert ctxt is zero. See func save.
+	MOVW	(g_sched+gobuf_ctxt)(g), R1
+	BEQ	R1, 2(PC)
+	JAL	runtime·badctxt(SB)
+	RET
+
+// func asmcgocall(fn, arg unsafe.Pointer) int32
+// Call fn(arg) on the scheduler stack,
+// aligned appropriately for the gcc ABI.
+// See cgocall.go for more details.
+// Not implemented.
+TEXT ·asmcgocall(SB),NOSPLIT,$0-12
+	UNDEF
+
+// cgocallback(void (*fn)(void*), void *frame, uintptr framesize)
+// Turn the fn into a Go func (by taking its address) and call
+// cgocallback_gofunc.
+// Not implemented.
+TEXT runtime·cgocallback(SB),NOSPLIT,$0-16
+	UNDEF
+
+// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
+// See cgocall.go for more details.
+// Not implemented.
+TEXT ·cgocallback_gofunc(SB),NOSPLIT,$0-16
+	UNDEF
+
+// void setg(G*); set g. for use by needm.
+// This only happens if iscgo, so jump straight to save_g
+TEXT runtime·setg(SB),NOSPLIT,$0-4
+	MOVW	gg+0(FP), g
+	JAL	runtime·save_g(SB)
+	RET
+
+// void setg_gcc(G*); set g in C TLS.
+// Must obey the gcc calling convention.
+// Not implemented.
+TEXT setg_gcc<>(SB),NOSPLIT,$0
+	UNDEF
+
+TEXT runtime·getcallerpc(SB),NOSPLIT,$4-8
+	MOVW	8(R29), R1	// LR saved by caller
+	MOVW	runtime·stackBarrierPC(SB), R2
+	BNE	R1, R2, nobar
+	JAL	runtime·nextBarrierPC(SB)	// Get original return PC.
+	MOVW	4(R29), R1
+nobar:
+	MOVW	R1, ret+4(FP)
+	RET
+
+TEXT runtime·setcallerpc(SB),NOSPLIT,$4-8
+	MOVW	pc+4(FP), R1
+	MOVW	8(R29), R2
+	MOVW	runtime·stackBarrierPC(SB), R3
+	BEQ	R2, R3, setbar
+	MOVW	R1, 8(R29)	// set LR in caller
+	RET
+setbar:
+	MOVW	R1, 4(R29)
+	JAL	runtime·setNextBarrierPC(SB)	// Set the stack barrier return PC.
+	RET
+
+TEXT runtime·abort(SB),NOSPLIT,$0-0
+	UNDEF
+
+// memhash_varlen(p unsafe.Pointer, h seed) uintptr
+// redirects to memhash(p, h, size) using the size
+// stored in the closure.
+TEXT runtime·memhash_varlen(SB),NOSPLIT,$16-12
+	GO_ARGS
+	NO_LOCAL_POINTERS
+	MOVW	p+0(FP), R1
+	MOVW	h+4(FP), R2
+	MOVW	4(REGCTXT), R3
+	MOVW	R1, 4(R29)
+	MOVW	R2, 8(R29)
+	MOVW	R3, 12(R29)
+	JAL	runtime·memhash(SB)
+	MOVW	16(R29), R1
+	MOVW	R1, ret+8(FP)
+	RET
+
+// Not implemented.
+TEXT runtime·aeshash(SB),NOSPLIT,$0
+	UNDEF
+
+// Not implemented.
+TEXT runtime·aeshash32(SB),NOSPLIT,$0
+	UNDEF
+
+// Not implemented.
+TEXT runtime·aeshash64(SB),NOSPLIT,$0
+	UNDEF
+
+// Not implemented.
+TEXT runtime·aeshashstr(SB),NOSPLIT,$0
+	UNDEF
+
+// memequal(a, b unsafe.Pointer, size uintptr) bool
+TEXT runtime·memequal(SB),NOSPLIT,$0-13
+	MOVW	a+0(FP), R1
+	MOVW	b+4(FP), R2
+	BEQ	R1, R2, eq
+	MOVW	size+8(FP), R3
+	ADDU	R1, R3, R4
+loop:
+	BNE	R1, R4, test
+	MOVW	$1, R1
+	MOVB	R1, ret+12(FP)
+	RET
+test:
+	MOVBU	(R1), R6
+	ADDU	$1, R1
+	MOVBU	(R2), R7
+	ADDU	$1, R2
+	BEQ	R6, R7, loop
+
+	MOVB	R0, ret+12(FP)
+	RET
+eq:
+	MOVW	$1, R1
+	MOVB	R1, ret+12(FP)
+	RET
+
+// memequal_varlen(a, b unsafe.Pointer) bool
+TEXT runtime·memequal_varlen(SB),NOSPLIT,$0-9
+	MOVW	a+0(FP), R1
+	MOVW	b+4(FP), R2
+	BEQ	R1, R2, eq
+	MOVW	4(REGCTXT), R3	// compiler stores size at offset 4 in the closure
+	ADDU	R1, R3, R4
+loop:
+	BNE	R1, R4, test
+	MOVW	$1, R1
+	MOVB	R1, ret+8(FP)
+	RET
+test:
+	MOVBU	(R1), R6
+	ADDU	$1, R1
+	MOVBU	(R2), R7
+	ADDU	$1, R2
+	BEQ	R6, R7, loop
+
+	MOVB	R0, ret+8(FP)
+	RET
+eq:
+	MOVW	$1, R1
+	MOVB	R1, ret+8(FP)
+	RET
+
+// eqstring tests whether two strings are equal.
+// The compiler guarantees that strings passed
+// to eqstring have equal length.
+// See runtime_test.go:eqstring_generic for
+// equivalent Go code.
+TEXT runtime·eqstring(SB),NOSPLIT,$0-17
+	MOVW	s1_base+0(FP), R1
+	MOVW	s2_base+8(FP), R2
+	MOVW	$1, R3
+	MOVBU	R3, ret+16(FP)
+	BNE	R1, R2, 2(PC)
+	RET
+	MOVW	s1_len+4(FP), R3
+	ADDU	R1, R3, R4
+loop:
+	BNE	R1, R4, 2(PC)
+	RET
+	MOVBU	(R1), R6
+	ADDU	$1, R1
+	MOVBU	(R2), R7
+	ADDU	$1, R2
+	BEQ	R6, R7, loop
+	MOVB	R0, ret+16(FP)
+	RET
+
+TEXT bytes·Equal(SB),NOSPLIT,$0-25
+	MOVW	a_len+4(FP), R3
+	MOVW	b_len+16(FP), R4
+	BNE	R3, R4, noteq	// unequal lengths are not equal
+
+	MOVW	a+0(FP), R1
+	MOVW	b+12(FP), R2
+	ADDU	R1, R3	// end
+
+loop:
+	BEQ	R1, R3, equal	// reached the end
+	MOVBU	(R1), R6
+	ADDU	$1, R1
+	MOVBU	(R2), R7
+	ADDU	$1, R2
+	BEQ	R6, R7, loop
+
+noteq:
+	MOVB	R0, ret+24(FP)
+	RET
+
+equal:
+	MOVW	$1, R1
+	MOVB	R1, ret+24(FP)
+	RET
+
+TEXT bytes·IndexByte(SB),NOSPLIT,$0-20
+	MOVW	s+0(FP), R1
+	MOVW	s_len+4(FP), R2
+	MOVBU	c+12(FP), R3	// byte to find
+	ADDU	$1, R1, R4	// store base+1 for later
+	ADDU	R1, R2	// end
+
+loop:
+	BEQ	R1, R2, notfound
+	MOVBU	(R1), R5
+	ADDU	$1, R1
+	BNE	R3, R5, loop
+
+	SUBU	R4, R1	// R1 will be one beyond the position we want so remove (base+1)
+	MOVW	R1, ret+16(FP)
+	RET
+
+notfound:
+	MOVW	$-1, R1
+	MOVW	R1, ret+16(FP)
+	RET
+
+TEXT strings·IndexByte(SB),NOSPLIT,$0-16
+	MOVW	s_base+0(FP), R1
+	MOVW	s_len+4(FP), R2
+	MOVBU	c+8(FP), R3	// byte to find
+	ADDU	$1, R1, R4	// store base+1 for later
+	ADDU	R1, R2	// end
+
+loop:
+	BEQ	R1, R2, notfound
+	MOVBU	(R1), R5
+	ADDU	$1, R1
+	BNE	R3, R5, loop
+
+	SUBU	R4, R1	// remove (base+1)
+	MOVW	R1, ret+12(FP)
+	RET
+
+notfound:
+	MOVW	$-1, R1
+	MOVW	R1, ret+12(FP)
+	RET
+
+TEXT runtime·cmpstring(SB),NOSPLIT,$0-20
+	MOVW	s1_base+0(FP), R3
+	MOVW	s1_len+4(FP), R1
+	MOVW	s2_base+8(FP), R4
+	MOVW	s2_len+12(FP), R2
+	BEQ	R3, R4, samebytes
+	SGTU	R1, R2, R7
+	MOVW	R1, R8
+	CMOVN	R7, R2, R8	// R8 is min(R1, R2)
+
+	ADDU	R3, R8	// R3 is current byte in s1, R8 is last byte in s1 to compare
+loop:
+	BEQ	R3, R8, samebytes	// all compared bytes were the same; compare lengths
+
+	MOVBU	(R3), R6
+	ADDU	$1, R3
+	MOVBU	(R4), R7
+	ADDU	$1, R4
+	BEQ	R6, R7 , loop
+	// bytes differed
+	SGTU	R6, R7, R8
+	MOVW	$-1, R6
+	CMOVZ	R8, R6, R8
+	JMP	cmp_ret
+samebytes:
+	SGTU	R1, R2, R6
+	SGTU	R2, R1, R7
+	SUBU	R7, R6, R8
+cmp_ret:
+	MOVW	R8, ret+16(FP)
+	RET
+
+TEXT bytes·Compare(SB),NOSPLIT,$0-28
+	MOVW	s1_base+0(FP), R3
+	MOVW	s2_base+12(FP), R4
+	MOVW	s1_len+4(FP), R1
+	MOVW	s2_len+16(FP), R2
+	BEQ	R3, R4, samebytes
+	SGTU	R1, R2, R7
+	MOVW	R1, R8
+	CMOVN	R7, R2, R8	// R8 is min(R1, R2)
+
+	ADDU	R3, R8	// R3 is current byte in s1, R8 is last byte in s1 to compare
+loop:
+	BEQ	R3, R8, samebytes
+
+	MOVBU	(R3), R6
+	ADDU	$1, R3
+	MOVBU	(R4), R7
+	ADDU	$1, R4
+	BEQ	R6, R7 , loop
+
+	SGTU	R6, R7, R8
+	MOVW	$-1, R6
+	CMOVZ	R8, R6, R8
+	JMP	cmp_ret
+samebytes:
+	SGTU	R1, R2, R6
+	SGTU	R2, R1, R7
+	SUBU	R7, R6, R8
+cmp_ret:
+	MOVW	R8, ret+24(FP)
+	RET
+
+TEXT runtime·fastrand(SB),NOSPLIT,$0-4
+	MOVW	g_m(g), R2
+	MOVW	m_fastrand(R2), R1
+	ADDU	R1, R1
+	BGEZ	R1, 2(PC)
+	XOR	$0x88888eef, R1
+	MOVW	R1, m_fastrand(R2)
+	MOVW	R1, ret+0(FP)
+	RET
+
+TEXT runtime·return0(SB),NOSPLIT,$0
+	MOVW	$0, R1
+	RET
+
+// Called from cgo wrappers, this function returns g->m->curg.stack.hi.
+// Must obey the gcc calling convention.
+// Not implemented.
+TEXT _cgo_topofstack(SB),NOSPLIT,$-4
+	UNDEF
+
+// The top-most function running on a goroutine
+// returns to goexit+PCQuantum.
+TEXT runtime·goexit(SB),NOSPLIT,$-4-0
+	NOR	R0, R0	// NOP
+	JAL	runtime·goexit1(SB)	// does not return
+	// traceback from goexit1 must hit code range of goexit
+	NOR	R0, R0	// NOP
+
+TEXT runtime·prefetcht0(SB),NOSPLIT,$0-4
+	RET
+
+TEXT runtime·prefetcht1(SB),NOSPLIT,$0-4
+	RET
+
+TEXT runtime·prefetcht2(SB),NOSPLIT,$0-4
+	RET
+
+TEXT runtime·prefetchnta(SB),NOSPLIT,$0-4
+	RET
+
+TEXT ·checkASM(SB),NOSPLIT,$0-1
+	MOVW	$1, R1
+	MOVB	R1, ret+0(FP)
+	RET
diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s
index 32c63c2..1d6adcc 100644
--- a/src/runtime/asm_ppc64x.s
+++ b/src/runtime/asm_ppc64x.s
@@ -106,12 +106,6 @@ TEXT _cgo_reginit(SB),NOSPLIT|NOFRAME,$0-0
 TEXT runtime·reginit(SB),NOSPLIT|NOFRAME,$0-0
 	// set R0 to zero, it's expected by the toolchain
 	XOR R0, R0
-	// initialize essential FP registers
-	FMOVD	$4503601774854144.0, F27
-	FMOVD	$0.5, F29
-	FSUB	F29, F29, F28
-	FADD	F29, F29, F30
-	FADD	F30, F30, F31
 	RET
 
 /*
@@ -128,13 +122,29 @@ TEXT runtime·gosave(SB), NOSPLIT|NOFRAME, $0-8
 	MOVD	g, gobuf_g(R3)
 	MOVD	R0, gobuf_lr(R3)
 	MOVD	R0, gobuf_ret(R3)
-	MOVD	R0, gobuf_ctxt(R3)
+	// Assert ctxt is zero. See func save.
+	MOVD	gobuf_ctxt(R3), R3
+	CMP	R0, R3
+	BEQ	2(PC)
+	BL	runtime·badctxt(SB)
 	RET
 
 // void gogo(Gobuf*)
 // restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), NOSPLIT|NOFRAME, $0-8
+TEXT runtime·gogo(SB), NOSPLIT, $16-8
 	MOVD	buf+0(FP), R5
+
+	// If ctxt is not nil, invoke deletion barrier before overwriting.
+	MOVD	gobuf_ctxt(R5), R3
+	CMP	R0, R3
+	BEQ	nilctxt
+	MOVD	$gobuf_ctxt(R5), R3
+	MOVD	R3, FIXED_FRAME+0(R1)
+	MOVD	R0, FIXED_FRAME+8(R1)
+	BL	runtime·writebarrierptr_prewrite(SB)
+	MOVD	buf+0(FP), R5
+
+nilctxt:
 	MOVD	gobuf_g(R5), g	// make sure g is not nil
 	BL	runtime·save_g(SB)
 
@@ -290,22 +300,24 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
 	MOVD	g_m(g), R7
 	MOVD	m_g0(R7), R8
 	CMP	g, R8
-	BNE	2(PC)
+	BNE	3(PC)
+	BL	runtime·badmorestackg0(SB)
 	BL	runtime·abort(SB)
 
 	// Cannot grow signal stack (m->gsignal).
 	MOVD	m_gsignal(R7), R8
 	CMP	g, R8
-	BNE	2(PC)
+	BNE	3(PC)
+	BL	runtime·badmorestackgsignal(SB)
 	BL	runtime·abort(SB)
 
 	// Called from f.
 	// Set g->sched to context in f.
-	MOVD	R11, (g_sched+gobuf_ctxt)(g)
 	MOVD	R1, (g_sched+gobuf_sp)(g)
 	MOVD	LR, R8
 	MOVD	R8, (g_sched+gobuf_pc)(g)
 	MOVD	R5, (g_sched+gobuf_lr)(g)
+	// newstack will fill gobuf.ctxt.
 
 	// Called from f.
 	// Set m->morebuf to f's caller.
@@ -317,6 +329,8 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
 	MOVD	m_g0(R7), g
 	BL	runtime·save_g(SB)
 	MOVD	(g_sched+gobuf_sp)(g), R1
+	MOVDU   R0, -(FIXED_FRAME+8)(R1)	// create a call frame on g0
+	MOVD	R11, FIXED_FRAME+0(R1)	// ctxt argument
 	BL	runtime·newstack(SB)
 
 	// Not reached, but make sure the return PC from the call to newstack
@@ -365,8 +379,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
 
 TEXT ·reflectcall(SB), NOSPLIT|NOFRAME, $0-32
 	MOVWZ argsize+24(FP), R3
-	// NOTE(rsc): No call16, because CALLFN needs four words
-	// of argument space to invoke callwritebarrier.
 	DISPATCH(runtime·call32, 32)
 	DISPATCH(runtime·call64, 64)
 	DISPATCH(runtime·call128, 128)
@@ -420,33 +432,27 @@ TEXT NAME(SB), WRAPPER, $MAXSIZE-24;		\
 	BL	(CTR);				\
 	MOVD	24(R1), R2;			\
 	/* copy return values back */		\
+	MOVD	argtype+0(FP), R7;		\
 	MOVD	arg+16(FP), R3;			\
 	MOVWZ	n+24(FP), R4;			\
 	MOVWZ	retoffset+28(FP), R6;		\
-	MOVD	R1, R5;				\
+	ADD	$FIXED_FRAME, R1, R5;		\
 	ADD	R6, R5; 			\
 	ADD	R6, R3;				\
 	SUB	R6, R4;				\
-	ADD	$(FIXED_FRAME-1), R5;			\
-	SUB	$1, R3;				\
-	ADD	R5, R4;				\
-loop:						\
-	CMP	R5, R4;				\
-	BEQ	end;				\
-	MOVBZU	1(R5), R6;			\
-	MOVBZU	R6, 1(R3);			\
-	BR	loop;				\
-end:						\
-	/* execute write barrier updates */	\
-	MOVD	argtype+0(FP), R7;		\
-	MOVD	arg+16(FP), R3;			\
-	MOVWZ	n+24(FP), R4;			\
-	MOVWZ	retoffset+28(FP), R6;		\
-	MOVD	R7, FIXED_FRAME+0(R1);			\
-	MOVD	R3, FIXED_FRAME+8(R1);			\
-	MOVD	R4, FIXED_FRAME+16(R1);			\
-	MOVD	R6, FIXED_FRAME+24(R1);			\
-	BL	runtime·callwritebarrier(SB);	\
+	BL	callRet<>(SB);			\
+	RET
+
+// callRet copies return values back at the end of call*. This is a
+// separate function so it can allocate stack space for the arguments
+// to reflectcallmove. It does not follow the Go ABI; it expects its
+// arguments in registers.
+TEXT callRet<>(SB), NOSPLIT, $32-0
+	MOVD	R7, FIXED_FRAME+0(R1)
+	MOVD	R3, FIXED_FRAME+8(R1)
+	MOVD	R5, FIXED_FRAME+16(R1)
+	MOVD	R4, FIXED_FRAME+24(R1)
+	BL	runtime·reflectcallmove(SB)
 	RET
 
 CALLFN(·call32, 32)
@@ -507,7 +513,11 @@ TEXT gosave<>(SB),NOSPLIT|NOFRAME,$0
 	MOVD	R1, (g_sched+gobuf_sp)(g)
 	MOVD	R0, (g_sched+gobuf_lr)(g)
 	MOVD	R0, (g_sched+gobuf_ret)(g)
-	MOVD	R0, (g_sched+gobuf_ctxt)(g)
+	// Assert ctxt is zero. See func save.
+	MOVD	(g_sched+gobuf_ctxt)(g), R31
+	CMP	R0, R31
+	BEQ	2(PC)
+	BL	runtime·badctxt(SB)
 	RET
 
 // func asmcgocall(fn, arg unsafe.Pointer) int32
@@ -748,12 +758,6 @@ setbar:
 	BL	runtime·setNextBarrierPC(SB)
 	RET
 
-TEXT runtime·getcallersp(SB),NOSPLIT,$0-16
-	MOVD	argp+0(FP), R3
-	SUB	$FIXED_FRAME, R3
-	MOVD	R3, ret+8(FP)
-	RET
-
 TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0
 	MOVW	(R0), R0
 	UNDEF
@@ -824,13 +828,220 @@ eq:
 	MOVB	R3, ret+16(FP)
 	RET
 
-// Do an efficieint memequal for ppc64
-// for reuse where possible.
+// Do an efficient memcmp for ppc64le
+// R3 = s1 len
+// R4 = s2 len
+// R5 = s1 addr
+// R6 = s2 addr
+// R7 = addr of return value
+TEXT cmpbodyLE<>(SB),NOSPLIT|NOFRAME,$0-0
+	MOVD	R3,R8		// set up length
+	CMP	R3,R4,CR2	// unequal?
+	BC	12,8,setuplen	// BLT CR2
+	MOVD	R4,R8		// use R4 for comparison len
+setuplen:
+	MOVD	R8,CTR		// set up loop counter
+	CMP	R8,$8		// only optimize >=8
+	BLT	simplecheck
+	DCBT	(R5)		// cache hint
+	DCBT	(R6)
+	CMP	R8,$32		// optimize >= 32
+	MOVD	R8,R9
+	BLT	setup8a		// 8 byte moves only
+setup32a:
+	SRADCC	$5,R8,R9	// number of 32 byte chunks
+	MOVD	R9,CTR
+
+        // Special processing for 32 bytes or longer.
+        // Loading this way is faster and correct as long as the
+	// doublewords being compared are equal. Once they
+	// are found unequal, reload them in proper byte order
+	// to determine greater or less than.
+loop32a:
+	MOVD	0(R5),R9	// doublewords to compare
+	MOVD	0(R6),R10	// get 4 doublewords
+	MOVD	8(R5),R14
+	MOVD	8(R6),R15
+	CMPU	R9,R10		// bytes equal?
+	MOVD	$0,R16		// set up for cmpne
+	BNE	cmpne		// further compare for LT or GT
+	MOVD	16(R5),R9	// get next pair of doublewords
+	MOVD	16(R6),R10
+	CMPU	R14,R15		// bytes match?
+	MOVD	$8,R16		// set up for cmpne
+	BNE	cmpne		// further compare for LT or GT
+	MOVD	24(R5),R14	// get next pair of doublewords
+	MOVD    24(R6),R15
+	CMPU	R9,R10		// bytes match?
+	MOVD	$16,R16		// set up for cmpne
+	BNE	cmpne		// further compare for LT or GT
+	MOVD	$-8,R16		// for cmpne, R5,R6 already inc by 32
+	ADD	$32,R5		// bump up to next 32
+	ADD	$32,R6
+	CMPU    R14,R15		// bytes match?
+	BC	8,2,loop32a	// br ctr and cr
+	BNE	cmpne
+	ANDCC	$24,R8,R9	// Any 8 byte chunks?
+	BEQ	leftover	// and result is 0
+setup8a:
+	SRADCC	$3,R9,R9	// get the 8 byte count
+	BEQ	leftover	// shifted value is 0
+	MOVD	R9,CTR		// loop count for doublewords
+loop8:
+	MOVDBR	(R5+R0),R9	// doublewords to compare
+	MOVDBR	(R6+R0),R10	// LE compare order
+	ADD	$8,R5
+	ADD	$8,R6
+	CMPU	R9,R10		// match?
+	BC	8,2,loop8	// bt ctr <> 0 && cr
+	BGT	greater
+	BLT	less
+leftover:
+	ANDCC	$7,R8,R9	// check for leftover bytes
+	MOVD	R9,CTR		// save the ctr
+	BNE	simple		// leftover bytes
+	BC	12,10,equal	// test CR2 for length comparison
+	BC	12,8,less
+	BR	greater
+simplecheck:
+	CMP	R8,$0		// remaining compare length 0
+	BNE	simple		// do simple compare
+	BC	12,10,equal	// test CR2 for length comparison
+	BC	12,8,less	// 1st len < 2nd len, result less
+	BR	greater		// 1st len > 2nd len must be greater
+simple:
+	MOVBZ	0(R5), R9	// get byte from 1st operand
+	ADD	$1,R5
+	MOVBZ	0(R6), R10	// get byte from 2nd operand
+	ADD	$1,R6
+	CMPU	R9, R10
+	BC	8,2,simple	// bc ctr <> 0 && cr
+	BGT	greater		// 1st > 2nd
+	BLT	less		// 1st < 2nd
+	BC	12,10,equal	// test CR2 for length comparison
+	BC	12,9,greater	// 2nd len > 1st len
+	BR	less		// must be less
+cmpne:				// only here is not equal
+	MOVDBR	(R5+R16),R8	// reload in reverse order
+	MOVDBR	(R6+R16),R9
+	CMPU	R8,R9		// compare correct endianness
+	BGT	greater		// here only if NE
+less:
+	MOVD	$-1,R3
+	MOVD	R3,(R7)		// return value if A < B
+	RET
+equal:
+	MOVD	$0,(R7)		// return value if A == B
+	RET
+greater:
+	MOVD	$1,R3
+	MOVD	R3,(R7)		// return value if A > B
+	RET
+
+// Do an efficient memcmp for ppc64 (BE)
+// R3 = s1 len
+// R4 = s2 len
+// R5 = s1 addr
+// R6 = s2 addr
+// R7 = addr of return value
+TEXT cmpbodyBE<>(SB),NOSPLIT|NOFRAME,$0-0
+	MOVD	R3,R8		// set up length
+	CMP	R3,R4,CR2	// unequal?
+	BC	12,8,setuplen	// BLT CR2
+	MOVD	R4,R8		// use R4 for comparison len
+setuplen:
+	MOVD	R8,CTR		// set up loop counter
+	CMP	R8,$8		// only optimize >=8
+	BLT	simplecheck
+	DCBT	(R5)		// cache hint
+	DCBT	(R6)
+	CMP	R8,$32		// optimize >= 32
+	MOVD	R8,R9
+	BLT	setup8a		// 8 byte moves only
+
+setup32a:
+	SRADCC	$5,R8,R9	// number of 32 byte chunks
+	MOVD	R9,CTR
+loop32a:
+	MOVD	0(R5),R9	// doublewords to compare
+	MOVD	0(R6),R10	// get 4 doublewords
+	MOVD	8(R5),R14
+	MOVD	8(R6),R15
+	CMPU	R9,R10		// bytes equal?
+	BLT	less		// found to be less
+	BGT	greater		// found to be greater
+	MOVD	16(R5),R9	// get next pair of doublewords
+	MOVD	16(R6),R10
+	CMPU	R14,R15		// bytes match?
+	BLT	less		// found less
+	BGT	greater		// found greater
+	MOVD	24(R5),R14	// get next pair of doublewords
+	MOVD	24(R6),R15
+	CMPU	R9,R10		// bytes match?
+	BLT	less		// found to be less
+	BGT	greater		// found to be greater
+	ADD	$32,R5		// bump up to next 32
+	ADD	$32,R6
+	CMPU	R14,R15		// bytes match?
+	BC	8,2,loop32a	// br ctr and cr
+	BLT	less		// with BE, byte ordering is
+	BGT	greater		// good for compare
+	ANDCC	$24,R8,R9	// Any 8 byte chunks?
+	BEQ	leftover	// and result is 0
+setup8a:
+	SRADCC	$3,R9,R9	// get the 8 byte count
+	BEQ	leftover	// shifted value is 0
+	MOVD	R9,CTR		// loop count for doublewords
+loop8:
+	MOVD	(R5),R9
+	MOVD	(R6),R10
+	ADD	$8,R5
+	ADD	$8,R6
+	CMPU	R9,R10		// match?
+	BC	8,2,loop8	// bt ctr <> 0 && cr
+	BGT	greater
+	BLT	less
+leftover:
+	ANDCC	$7,R8,R9	// check for leftover bytes
+	MOVD	R9,CTR		// save the ctr
+	BNE	simple		// leftover bytes
+	BC	12,10,equal	// test CR2 for length comparison
+	BC	12,8,less
+	BR	greater
+simplecheck:
+	CMP	R8,$0		// remaining compare length 0
+	BNE	simple		// do simple compare
+	BC	12,10,equal	// test CR2 for length comparison
+	BC 	12,8,less	// 1st len < 2nd len, result less
+	BR	greater		// same len, must be equal
+simple:
+	MOVBZ	0(R5),R9	// get byte from 1st operand
+	ADD	$1,R5
+	MOVBZ	0(R6),R10	// get byte from 2nd operand
+	ADD	$1,R6
+	CMPU	R9,R10
+	BC	8,2,simple	// bc ctr <> 0 && cr
+	BGT	greater		// 1st > 2nd
+	BLT	less		// 1st < 2nd
+	BC	12,10,equal	// test CR2 for length comparison
+	BC	12,9,greater	// 2nd len > 1st len
+less:
+	MOVD	$-1,R3
+	MOVD    R3,(R7)		// return value if A < B
+	RET
+equal:
+	MOVD    $0,(R7)		// return value if A == B
+	RET
+greater:
+	MOVD	$1,R3
+	MOVD	R3,(R7)		// return value if A > B
+	RET
+
+// Do an efficient memequal for ppc64
 // R3 = s1
 // R4 = s2
 // R5 = len
 // R9 = return value
-// R6, R7 clobbered
 TEXT runtime·memeqbody(SB),NOSPLIT|NOFRAME,$0-0
 	MOVD    R5,CTR
 	CMP     R5,$8		// only optimize >=8
@@ -908,14 +1119,14 @@ equal:
 // See runtime_test.go:eqstring_generic for
 // equivalent Go code.
 TEXT runtime·eqstring(SB),NOSPLIT,$0-33
-	MOVD    s1str+0(FP), R3
-	MOVD    s2str+16(FP), R4
+	MOVD    s1_base+0(FP), R3
+	MOVD    s2_base+16(FP), R4
 	MOVD    $1, R5
 	MOVB    R5, ret+32(FP)
 	CMP     R3, R4
 	BNE     2(PC)
 	RET
-	MOVD    s1len+8(FP), R5
+	MOVD    s1_len+8(FP), R5
 	BL      runtime·memeqbody(SB)
 	MOVB    R9, ret+32(FP)
 	RET
@@ -995,7 +1206,11 @@ TEXT runtime·cmpstring(SB),NOSPLIT|NOFRAME,$0-40
 	MOVD	s2_base+16(FP), R6
 	MOVD	s2_len+24(FP), R4
 	MOVD	$ret+32(FP), R7
-	BR	runtime·cmpbody<>(SB)
+#ifdef	GOARCH_ppc64le
+	BR	cmpbodyLE<>(SB)
+#else
+	BR      cmpbodyBE<>(SB)
+#endif
 
 TEXT bytes·Compare(SB),NOSPLIT|NOFRAME,$0-56
 	MOVD	s1+0(FP), R5
@@ -1003,52 +1218,13 @@ TEXT bytes·Compare(SB),NOSPLIT|NOFRAME,$0-56
 	MOVD	s2+24(FP), R6
 	MOVD	s2+32(FP), R4
 	MOVD	$ret+48(FP), R7
-	BR	runtime·cmpbody<>(SB)
-
-// On entry:
-// R3 is the length of s1
-// R4 is the length of s2
-// R5 points to the start of s1
-// R6 points to the start of s2
-// R7 points to return value (-1/0/1 will be written here)
-//
-// On exit:
-// R5, R6, R8, R9 and R10 are clobbered
-TEXT runtime·cmpbody<>(SB),NOSPLIT|NOFRAME,$0-0
-	CMP	R5, R6
-	BEQ	samebytes // same starting pointers; compare lengths
-	SUB	$1, R5
-	SUB	$1, R6
-	MOVD	R4, R8
-	CMP	R3, R4
-	BGE	2(PC)
-	MOVD	R3, R8	// R8 is min(R3, R4)
-	ADD	R5, R8	// R5 is current byte in s1, R8 is last byte in s1 to compare
-loop:
-	CMP	R5, R8
-	BEQ	samebytes // all compared bytes were the same; compare lengths
-	MOVBZU	1(R5), R9
-	MOVBZU	1(R6), R10
-	CMP	R9, R10
-	BEQ	loop
-	// bytes differed
-	MOVD	$1, R4
-	BGT	2(PC)
-	NEG	R4
-	MOVD	R4, (R7)
-	RET
-samebytes:
-	MOVD	$1, R8
-	CMP	R3, R4
-	BNE	3(PC)
-	MOVD	R0, (R7)
-	RET
-	BGT	2(PC)
-	NEG	R8
-	MOVD	R8, (R7)
-	RET
+#ifdef	GOARCH_ppc64le
+	BR	cmpbodyLE<>(SB)
+#else
+	BR      cmpbodyBE<>(SB)
+#endif
 
-TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
+TEXT runtime·fastrand(SB), NOSPLIT, $0-4
 	MOVD	g_m(g), R4
 	MOVWZ	m_fastrand(R4), R3
 	ADD	R3, R3
@@ -1109,8 +1285,8 @@ TEXT runtime·prefetcht2(SB),NOSPLIT,$0-8
 TEXT runtime·prefetchnta(SB),NOSPLIT,$0-8
 	RET
 
-TEXT runtime·sigreturn(SB),NOSPLIT,$0-8
-        RET
+TEXT runtime·sigreturn(SB),NOSPLIT,$0-0
+	RET
 
 // prepGoExitFrame saves the current TOC pointer (i.e. the TOC pointer for the
 // module containing runtime) to the frame that goexit will execute in when
diff --git a/src/runtime/asm_s390x.s b/src/runtime/asm_s390x.s
index 97f276c..c2212a5 100644
--- a/src/runtime/asm_s390x.s
+++ b/src/runtime/asm_s390x.s
@@ -7,44 +7,6 @@
 #include "funcdata.h"
 #include "textflag.h"
 
-// Indicate the status of vector facility
-// -1: 	init value
-// 0:	vector not installed
-// 1:	vector installed and enabled
-// 2:	vector installed but not enabled
-
-DATA runtime·vectorfacility+0x00(SB)/4, $-1
-GLOBL runtime·vectorfacility(SB), NOPTR, $4
-
-TEXT runtime·checkvectorfacility(SB),NOSPLIT,$32-0
-	MOVD    $2, R0
-	MOVD	R1, tmp-32(SP)
-	MOVD    $x-24(SP), R1
-	XC	$24, 0(R1), 0(R1)
-//      STFLE   0(R1)
-	WORD    $0xB2B01000
-	MOVBZ   z-8(SP), R1
-	AND     $0x40, R1
-	BNE     vectorinstalled
-	MOVB    $0, runtime·vectorfacility(SB) //Vector not installed
-	MOVD	tmp-32(SP), R1
-	MOVD    $0, R0
-	RET
-vectorinstalled:
-	// check if the vector instruction has been enabled
-	VLEIB   $0, $0xF, V16
-	VLGVB   $0, V16, R0
-	CMPBEQ  R0, $0xF, vectorenabled
-	MOVB    $2, runtime·vectorfacility(SB) //Vector installed but not enabled
-	MOVD    tmp-32(SP), R1
-	MOVD    $0, R0
-	RET
-vectorenabled:
-	MOVB    $1, runtime·vectorfacility(SB) //Vector installed and enabled
-	MOVD    tmp-32(SP), R1
-	MOVD    $0, R0
-	RET
-
 TEXT runtime·rt0_go(SB),NOSPLIT,$0
 	// R2 = argc; R3 = argv; R11 = temp; R13 = g; R15 = stack pointer
 	// C TLS base pointer in AR0:AR1
@@ -110,22 +72,22 @@ nocgo:
 	MOVD	$runtime·mainPC(SB), R2		// entry
 	SUB     $24, R15
 	MOVD 	R2, 16(R15)
-	MOVD 	R0, 8(R15)
-	MOVD 	R0, 0(R15)
+	MOVD 	$0, 8(R15)
+	MOVD 	$0, 0(R15)
 	BL	runtime·newproc(SB)
 	ADD	$24, R15
 
 	// start this M
 	BL	runtime·mstart(SB)
 
-	MOVD	R0, 1(R0)
+	MOVD	$0, 1(R0)
 	RET
 
 DATA	runtime·mainPC+0(SB)/8,$runtime·main(SB)
 GLOBL	runtime·mainPC(SB),RODATA,$8
 
 TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0
-	MOVD	R0, 2(R0)
+	MOVD	$0, 2(R0)
 	RET
 
 TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
@@ -144,13 +106,27 @@ TEXT runtime·gosave(SB), NOSPLIT, $-8-8
 	MOVD	g, gobuf_g(R3)
 	MOVD	$0, gobuf_lr(R3)
 	MOVD	$0, gobuf_ret(R3)
-	MOVD	$0, gobuf_ctxt(R3)
+	// Assert ctxt is zero. See func save.
+	MOVD	gobuf_ctxt(R3), R3
+	CMPBEQ	R3, $0, 2(PC)
+	BL	runtime·badctxt(SB)
 	RET
 
 // void gogo(Gobuf*)
 // restore state from Gobuf; longjmp
-TEXT runtime·gogo(SB), NOSPLIT, $-8-8
+TEXT runtime·gogo(SB), NOSPLIT, $16-8
+	MOVD	buf+0(FP), R5
+
+	// If ctxt is not nil, invoke deletion barrier before overwriting.
+	MOVD	gobuf_ctxt(R5), R1
+	CMPBEQ	R1, $0, nilctxt
+	MOVD	$gobuf_ctxt(R5), R1
+	MOVD	R1, 8(R15)
+	MOVD	R0, 16(R15)
+	BL	runtime·writebarrierptr_prewrite(SB)
 	MOVD	buf+0(FP), R5
+
+nilctxt:
 	MOVD	gobuf_g(R5), g	// make sure g is not nil
 	BL	runtime·save_g(SB)
 
@@ -175,7 +151,7 @@ TEXT runtime·mcall(SB), NOSPLIT, $-8-8
 	// Save caller state in g->sched
 	MOVD	R15, (g_sched+gobuf_sp)(g)
 	MOVD	LR, (g_sched+gobuf_pc)(g)
-	MOVD	R0, (g_sched+gobuf_lr)(g)
+	MOVD	$0, (g_sched+gobuf_lr)(g)
 	MOVD	g, (g_sched+gobuf_g)(g)
 
 	// Switch to m->g0 & its stack, call fn.
@@ -232,7 +208,7 @@ switch:
 	ADD	$16, R6	// get past prologue
 	MOVD	R6, (g_sched+gobuf_pc)(g)
 	MOVD	R15, (g_sched+gobuf_sp)(g)
-	MOVD	R0, (g_sched+gobuf_lr)(g)
+	MOVD	$0, (g_sched+gobuf_lr)(g)
 	MOVD	g, (g_sched+gobuf_g)(g)
 
 	// switch to g0
@@ -279,22 +255,24 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
 	// Cannot grow scheduler stack (m->g0).
 	MOVD	g_m(g), R7
 	MOVD	m_g0(R7), R8
-	CMPBNE	g, R8, 2(PC)
+	CMPBNE	g, R8, 3(PC)
+	BL	runtime·badmorestackg0(SB)
 	BL	runtime·abort(SB)
 
 	// Cannot grow signal stack (m->gsignal).
 	MOVD	m_gsignal(R7), R8
 	CMP	g, R8
-	BNE	2(PC)
+	BNE	3(PC)
+	BL	runtime·badmorestackgsignal(SB)
 	BL	runtime·abort(SB)
 
 	// Called from f.
 	// Set g->sched to context in f.
-	MOVD	R12, (g_sched+gobuf_ctxt)(g)
 	MOVD	R15, (g_sched+gobuf_sp)(g)
 	MOVD	LR, R8
 	MOVD	R8, (g_sched+gobuf_pc)(g)
 	MOVD	R5, (g_sched+gobuf_lr)(g)
+	// newstack will fill gobuf.ctxt.
 
 	// Called from f.
 	// Set m->morebuf to f's caller.
@@ -306,6 +284,10 @@ TEXT runtime·morestack(SB),NOSPLIT|NOFRAME,$0-0
 	MOVD	m_g0(R7), g
 	BL	runtime·save_g(SB)
 	MOVD	(g_sched+gobuf_sp)(g), R15
+	// Create a stack frame on g0 to call newstack.
+	MOVD	$0, -16(R15)	// Zero saved LR in frame
+	SUB	$16, R15
+	MOVD	R12, 8(R15)	// ctxt argument
 	BL	runtime·newstack(SB)
 
 	// Not reached, but make sure the return PC from the call to newstack
@@ -352,8 +334,6 @@ TEXT reflect·call(SB), NOSPLIT, $0-0
 
 TEXT ·reflectcall(SB), NOSPLIT, $-8-32
 	MOVWZ argsize+24(FP), R3
-	// NOTE(rsc): No call16, because CALLFN needs four words
-	// of argument space to invoke callwritebarrier.
 	DISPATCH(runtime·call32, 32)
 	DISPATCH(runtime·call64, 64)
 	DISPATCH(runtime·call128, 128)
@@ -387,54 +367,49 @@ TEXT ·reflectcall(SB), NOSPLIT, $-8-32
 TEXT NAME(SB), WRAPPER, $MAXSIZE-24;		\
 	NO_LOCAL_POINTERS;			\
 	/* copy arguments to stack */		\
-	MOVD	arg+16(FP), R3;			\
-	MOVWZ	argsize+24(FP), R4;			\
-	MOVD	R15, R5;				\
-	ADD	$(8-1), R5;			\
-	SUB	$1, R3;				\
-	ADD	R5, R4;				\
-	CMP	R5, R4;				\
-	BEQ	6(PC);				\
-	ADD	$1, R3;				\
-	ADD	$1, R5;				\
-	MOVBZ	0(R3), R6;			\
-	MOVBZ	R6, 0(R5);			\
-	BR	-6(PC);				\
-	/* call function */			\
+	MOVD	arg+16(FP), R4;			\
+	MOVWZ	argsize+24(FP), R5;		\
+	MOVD	$stack-MAXSIZE(SP), R6;		\
+loopArgs: /* copy 256 bytes at a time */	\
+	CMP	R5, $256;			\
+	BLT	tailArgs;			\
+	SUB	$256, R5;			\
+	MVC	$256, 0(R4), 0(R6);		\
+	MOVD	$256(R4), R4;			\
+	MOVD	$256(R6), R6;			\
+	BR	loopArgs;			\
+tailArgs: /* copy remaining bytes */		\
+	CMP	R5, $0;				\
+	BEQ	callFunction;			\
+	SUB	$1, R5;				\
+	EXRL	$callfnMVC<>(SB), R5;		\
+callFunction:					\
 	MOVD	f+8(FP), R12;			\
 	MOVD	(R12), R8;			\
 	PCDATA  $PCDATA_StackMapIndex, $0;	\
 	BL	(R8);				\
 	/* copy return values back */		\
-	MOVD	arg+16(FP), R3;			\
-	MOVWZ	n+24(FP), R4;			\
-	MOVWZ	retoffset+28(FP), R6;		\
-	MOVD	R15, R5;				\
-	ADD	R6, R5; 			\
-	ADD	R6, R3;				\
-	SUB	R6, R4;				\
-	ADD	$(8-1), R5;			\
-	SUB	$1, R3;				\
-	ADD	R5, R4;				\
-loop:						\
-	CMP	R5, R4;				\
-	BEQ	end;				\
-	ADD	$1, R5;				\
-	ADD	$1, R3;				\
-	MOVBZ	0(R5), R6;			\
-	MOVBZ	R6, 0(R3);			\
-	BR	loop;				\
-end:						\
-	/* execute write barrier updates */	\
 	MOVD	argtype+0(FP), R7;		\
-	MOVD	arg+16(FP), R3;			\
-	MOVWZ	n+24(FP), R4;			\
-	MOVWZ	retoffset+28(FP), R6;		\
-	MOVD	R7, 8(R15);			\
-	MOVD	R3, 16(R15);			\
-	MOVD	R4, 24(R15);			\
-	MOVD	R6, 32(R15);			\
-	BL	runtime·callwritebarrier(SB);	\
+	MOVD	arg+16(FP), R6;			\
+	MOVWZ	n+24(FP), R5;			\
+	MOVD	$stack-MAXSIZE(SP), R4;		\
+	MOVWZ	retoffset+28(FP), R1;		\
+	ADD	R1, R4;				\
+	ADD	R1, R6;				\
+	SUB	R1, R5;				\
+	BL	callRet<>(SB);			\
+	RET
+
+// callRet copies return values back at the end of call*. This is a
+// separate function so it can allocate stack space for the arguments
+// to reflectcallmove. It does not follow the Go ABI; it expects its
+// arguments in registers.
+TEXT callRet<>(SB), NOSPLIT, $32-0
+	MOVD	R7, 8(R15)
+	MOVD	R6, 16(R15)
+	MOVD	R4, 24(R15)
+	MOVD	R5, 32(R15)
+	BL	runtime·reflectcallmove(SB)
 	RET
 
 CALLFN(·call32, 32)
@@ -464,6 +439,10 @@ CALLFN(·call268435456, 268435456)
 CALLFN(·call536870912, 536870912)
 CALLFN(·call1073741824, 1073741824)
 
+// Not a function: target for EXRL (execute relative long) instruction.
+TEXT callfnMVC<>(SB),NOSPLIT|NOFRAME,$0-0
+	MVC	$1, 0(R4), 0(R6)
+
 TEXT runtime·procyield(SB),NOSPLIT,$0-0
 	RET
 
@@ -482,13 +461,16 @@ TEXT runtime·jmpdefer(SB),NOSPLIT|NOFRAME,$0-16
 	MOVD	0(R12), R3
 	BR	(R3)
 
-// Save state of caller into g->sched. Smashes R31.
+// Save state of caller into g->sched. Smashes R1.
 TEXT gosave<>(SB),NOSPLIT|NOFRAME,$0
 	MOVD	LR, (g_sched+gobuf_pc)(g)
 	MOVD	R15, (g_sched+gobuf_sp)(g)
 	MOVD	$0, (g_sched+gobuf_lr)(g)
 	MOVD	$0, (g_sched+gobuf_ret)(g)
-	MOVD	$0, (g_sched+gobuf_ctxt)(g)
+	// Assert ctxt is zero. See func save.
+	MOVD	(g_sched+gobuf_ctxt)(g), R1
+	CMPBEQ	R1, $0, 2(PC)
+	BL	runtime·badctxt(SB)
 	RET
 
 // func asmcgocall(fn, arg unsafe.Pointer) int32
@@ -526,7 +508,7 @@ g0:
 	MOVD	(g_stack+stack_hi)(R5), R5
 	SUB	R2, R5
 	MOVD	R5, 160(R15)             // save depth in old g stack (can't just save SP, as stack might be copied during a callback)
-	MOVD	R0, 0(R15)              // clear back chain pointer (TODO can we give it real back trace information?)
+	MOVD	$0, 0(R15)              // clear back chain pointer (TODO can we give it real back trace information?)
 	MOVD	R4, R2                  // arg in R2
 	BL	R3                      // can clobber: R0-R5, R14, F0-F3, F5, F7-F15
 
@@ -715,12 +697,6 @@ setbar:
 	BL	runtime·setNextBarrierPC(SB)
 	RET
 
-TEXT runtime·getcallersp(SB),NOSPLIT,$0-16
-	MOVD	argp+0(FP), R3
-	SUB	$8, R3
-	MOVD	R3, ret+8(FP)
-	RET
-
 TEXT runtime·abort(SB),NOSPLIT|NOFRAME,$0-0
 	MOVW	(R0), R0
 	UNDEF
@@ -765,10 +741,10 @@ TEXT runtime·aeshash64(SB),NOSPLIT|NOFRAME,$0-0
 TEXT runtime·aeshashstr(SB),NOSPLIT|NOFRAME,$0-0
 	MOVW	(R0), R15
 
-// memequal(p, q unsafe.Pointer, size uintptr) bool
+// memequal(a, b unsafe.Pointer, size uintptr) bool
 TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25
-	MOVD	p+0(FP), R3
-	MOVD	q+8(FP), R5
+	MOVD	a+0(FP), R3
+	MOVD	b+8(FP), R5
 	MOVD	size+16(FP), R6
 	LA	ret+24(FP), R7
 	BR	runtime·memeqbody(SB)
@@ -787,9 +763,9 @@ TEXT runtime·memequal_varlen(SB),NOSPLIT|NOFRAME,$0-17
 // See runtime_test.go:eqstring_generic for
 // equivalent Go code.
 TEXT runtime·eqstring(SB),NOSPLIT|NOFRAME,$0-33
-	MOVD	s1str+0(FP), R3
-	MOVD	s1len+8(FP), R6
-	MOVD	s2str+16(FP), R5
+	MOVD	s1_base+0(FP), R3
+	MOVD	s1_len+8(FP), R6
+	MOVD	s2_base+16(FP), R5
 	LA	ret+32(FP), R7
 	BR	runtime·memeqbody(SB)
 
@@ -875,7 +851,7 @@ TEXT runtime·memeqbodyclc(SB),NOSPLIT|NOFRAME,$0-0
 	CLC	$1, 0(R3), 0(R5)
 	RET
 
-TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
+TEXT runtime·fastrand(SB), NOSPLIT, $0-4
 	MOVD	g_m(g), R4
 	MOVWZ	m_fastrand(R4), R3
 	ADD	R3, R3
@@ -886,14 +862,14 @@ TEXT runtime·fastrand1(SB), NOSPLIT, $0-4
 	MOVW	R3, ret+0(FP)
 	RET
 
-TEXT bytes·IndexByte(SB),NOSPLIT,$0-40
+TEXT bytes·IndexByte(SB),NOSPLIT|NOFRAME,$0-40
 	MOVD	s+0(FP), R3     // s => R3
 	MOVD	s_len+8(FP), R4 // s_len => R4
 	MOVBZ	c+24(FP), R5    // c => R5
 	MOVD	$ret+32(FP), R2 // &ret => R9
 	BR	runtime·indexbytebody(SB)
 
-TEXT strings·IndexByte(SB),NOSPLIT,$0-32
+TEXT strings·IndexByte(SB),NOSPLIT|NOFRAME,$0-32
 	MOVD	s+0(FP), R3     // s => R3
 	MOVD	s_len+8(FP), R4 // s_len => R4
 	MOVBZ	c+16(FP), R5    // c => R5
@@ -905,7 +881,7 @@ TEXT strings·IndexByte(SB),NOSPLIT,$0-32
 // R4: s_len
 // R5: c -- byte sought
 // R2: &ret -- address to put index into
-TEXT runtime·indexbytebody(SB),NOSPLIT,$0
+TEXT runtime·indexbytebody(SB),NOSPLIT|NOFRAME,$0
 	CMPBEQ	R4, $0, notfound
 	MOVD	R3, R6          // store base for later
 	ADD	R3, R4, R8      // the address after the end of the string
@@ -929,12 +905,10 @@ notfound:
 	RET
 
 large:
-	MOVB	runtime·vectorfacility(SB), R1
-	CMPBEQ	R1, $-1, checkvector	// vectorfacility = -1, vector not checked yet
-vectorchecked:
-	CMPBEQ	R1, $1, vectorimpl      // vectorfacility = 1, vector supported
+	MOVBZ	·cpu+facilities_hasVX(SB), R1
+	CMPBNE	R1, $0, vectorimpl
 
-srstimpl:                       // vectorfacility != 1, not support or enable vector
+srstimpl:                       // no vector facility
 	MOVBZ	R5, R0          // c needs to be in R0, leave until last minute as currently R0 is expected to be 0
 srstloop:
 	WORD	$0xB25E0083     // srst %r8, %r3 (search the range [R3, R8))
@@ -952,12 +926,14 @@ notfoundr0:
 
 vectorimpl:
 	//if the address is not 16byte aligned, use loop for the header
-	AND	$15, R3, R8
+	MOVD	R3, R8
+	AND	$15, R8
 	CMPBGT	R8, $0, notaligned
 
 aligned:
 	ADD	R6, R4, R8
-	AND	$-16, R8, R7
+	MOVD	R8, R7
+	AND	$-16, R7
 	// replicate c across V17
 	VLVGB	$0, R5, V19
 	VREPB	$0, V19, V17
@@ -978,7 +954,8 @@ vectorloop:
 	RET
 
 notaligned:
-	AND	$-16, R3, R8
+	MOVD	R3, R8
+	AND	$-16, R8
 	ADD     $16, R8
 notalignedloop:
 	CMPBEQ	R3, R8, aligned
@@ -987,11 +964,6 @@ notalignedloop:
 	CMPBNE	R7, R5, notalignedloop
 	BR	found
 
-checkvector:
-	CALL	runtime·checkvectorfacility(SB)
-	MOVB    runtime·vectorfacility(SB), R1
-	BR	vectorchecked
-
 TEXT runtime·return0(SB), NOSPLIT, $0
 	MOVW	$0, R3
 	RET
@@ -1036,7 +1008,7 @@ TEXT runtime·prefetcht2(SB),NOSPLIT,$0-8
 TEXT runtime·prefetchnta(SB),NOSPLIT,$0-8
 	RET
 
-TEXT runtime·sigreturn(SB),NOSPLIT,$0-8
+TEXT runtime·sigreturn(SB),NOSPLIT,$0-0
 	RET
 
 TEXT ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0
@@ -1104,18 +1076,238 @@ TEXT runtime·cmpbodyclc(SB),NOSPLIT|NOFRAME,$0-0
 	CLC	$1, 0(R3), 0(R5)
 	RET
 
+// func supportsVX() bool
+TEXT strings·supportsVX(SB),NOSPLIT,$0-1
+	MOVBZ	runtime·cpu+facilities_hasVX(SB), R0
+	MOVB	R0, ret+0(FP)
+	RET
+
+// func supportsVX() bool
+TEXT bytes·supportsVX(SB),NOSPLIT,$0-1
+	MOVBZ	runtime·cpu+facilities_hasVX(SB), R0
+	MOVB	R0, ret+0(FP)
+	RET
+
+// func indexShortStr(s, sep string) int
+// Caller must confirm availability of vx facility before calling.
+TEXT strings·indexShortStr(SB),NOSPLIT|NOFRAME,$0-40
+	LMG	s+0(FP), R1, R2   // R1=&s[0],   R2=len(s)
+	LMG	sep+16(FP), R3, R4 // R3=&sep[0], R4=len(sep)
+	MOVD	$ret+32(FP), R5
+	BR	runtime·indexShortStr(SB)
+
+// func indexShortStr(s, sep []byte) int
+// Caller must confirm availability of vx facility before calling.
+TEXT bytes·indexShortStr(SB),NOSPLIT|NOFRAME,$0-56
+	LMG	s+0(FP), R1, R2    // R1=&s[0],   R2=len(s)
+	LMG	sep+24(FP), R3, R4 // R3=&sep[0], R4=len(sep)
+	MOVD	$ret+48(FP), R5
+	BR	runtime·indexShortStr(SB)
+
+// s: string we are searching
+// sep: string to search for
+// R1=&s[0], R2=len(s)
+// R3=&sep[0], R4=len(sep)
+// R5=&ret (int)
+// Caller must confirm availability of vx facility before calling.
+TEXT runtime·indexShortStr(SB),NOSPLIT|NOFRAME,$0
+	CMPBGT	R4, R2, notfound
+	ADD	R1, R2
+	SUB	R4, R2 // R2=&s[len(s)-len(sep)] (last valid index)
+	CMPBEQ	R4, $0, notfound
+	SUB	$1, R4 // R4=len(sep)-1 for use as VLL index
+	VLL	R4, (R3), V0 // contains first 16 bytes of sep
+	MOVD	R1, R7
+index2plus:
+	CMPBNE	R4, $1, index3plus
+	MOVD	$15(R7), R9
+	CMPBGE	R9, R2, index2to16
+	VGBM	$0xaaaa, V31       // 0xff00ff00ff00ff00...
+	VONE	V16
+	VREPH	$0, V0, V1
+	CMPBGE	R9, R2, index2to16
+index2loop:
+	VL	0(R7), V2          // 16 bytes, even indices
+	VL	1(R7), V4          // 16 bytes, odd indices
+	VCEQH	V1, V2, V5         // compare even indices
+	VCEQH	V1, V4, V6         // compare odd indices
+	VSEL	V5, V6, V31, V7    // merge even and odd indices
+	VFEEBS	V16, V7, V17       // find leftmost index, set condition to 1 if found
+	BLT	foundV17
+	MOVD	$16(R7), R7        // R7+=16
+	ADD	$15, R7, R9
+	CMPBLE	R9, R2, index2loop // continue if (R7+15) <= R2 (last index to search)
+	CMPBLE	R7, R2, index2to16
+	BR	notfound
+
+index3plus:
+	CMPBNE	R4, $2, index4plus
+	ADD	$15, R7, R9
+	CMPBGE	R9, R2, index2to16
+	MOVD	$1, R0
+	VGBM	$0xaaaa, V31       // 0xff00ff00ff00ff00...
+	VONE	V16
+	VREPH	$0, V0, V1
+	VREPB	$2, V0, V8
+index3loop:
+	VL	(R7), V2           // load 16-bytes into V2
+	VLL	R0, 16(R7), V3     // load 2-bytes into V3
+	VSLDB	$1, V2, V3, V4     // V4=(V2:V3)<<1
+	VSLDB	$2, V2, V3, V9     // V9=(V2:V3)<<2
+	VCEQH	V1, V2, V5         // compare 2-byte even indices
+	VCEQH	V1, V4, V6         // compare 2-byte odd indices
+	VCEQB	V8, V9, V10        // compare last bytes
+	VSEL	V5, V6, V31, V7    // merge even and odd indices
+	VN	V7, V10, V7        // AND indices with last byte
+	VFEEBS	V16, V7, V17       // find leftmost index, set condition to 1 if found
+	BLT	foundV17
+	MOVD	$16(R7), R7        // R7+=16
+	ADD	$15, R7, R9
+	CMPBLE	R9, R2, index3loop // continue if (R7+15) <= R2 (last index to search)
+	CMPBLE	R7, R2, index2to16
+	BR	notfound
+
+index4plus:
+	CMPBNE	R4, $3, index5plus
+	ADD	$15, R7, R9
+	CMPBGE	R9, R2, index2to16
+	MOVD	$2, R0
+	VGBM	$0x8888, V29       // 0xff000000ff000000...
+	VGBM	$0x2222, V30       // 0x0000ff000000ff00...
+	VGBM	$0xcccc, V31       // 0xffff0000ffff0000...
+	VONE	V16
+	VREPF	$0, V0, V1
+index4loop:
+	VL	(R7), V2           // load 16-bytes into V2
+	VLL	R0, 16(R7), V3     // load 3-bytes into V3
+	VSLDB	$1, V2, V3, V4     // V4=(V2:V3)<<1
+	VSLDB	$2, V2, V3, V9     // V9=(V2:V3)<<1
+	VSLDB	$3, V2, V3, V10    // V10=(V2:V3)<<1
+	VCEQF	V1, V2, V5         // compare index 0, 4, ...
+	VCEQF	V1, V4, V6         // compare index 1, 5, ...
+	VCEQF	V1, V9, V11        // compare index 2, 6, ...
+	VCEQF	V1, V10, V12       // compare index 3, 7, ...
+	VSEL	V5, V6, V29, V13   // merge index 0, 1, 4, 5, ...
+	VSEL	V11, V12, V30, V14 // merge index 2, 3, 6, 7, ...
+	VSEL	V13, V14, V31, V7  // final merge
+	VFEEBS	V16, V7, V17       // find leftmost index, set condition to 1 if found
+	BLT	foundV17
+	MOVD	$16(R7), R7        // R7+=16
+	ADD	$15, R7, R9
+	CMPBLE	R9, R2, index4loop // continue if (R7+15) <= R2 (last index to search)
+	CMPBLE	R7, R2, index2to16
+	BR	notfound
+
+index5plus:
+	CMPBGT	R4, $15, index17plus
+index2to16:
+	CMPBGT	R7, R2, notfound
+	MOVD	$1(R7), R8
+	CMPBGT	R8, R2, index2to16tail
+index2to16loop:
+	// unrolled 2x
+	VLL	R4, (R7), V1
+	VLL	R4, 1(R7), V2
+	VCEQGS	V0, V1, V3
+	BEQ	found
+	MOVD	$1(R7), R7
+	VCEQGS	V0, V2, V4
+	BEQ	found
+	MOVD	$1(R7), R7
+	CMPBLT	R7, R2, index2to16loop
+	CMPBGT	R7, R2, notfound
+index2to16tail:
+	VLL	R4, (R7), V1
+	VCEQGS	V0, V1, V2
+	BEQ	found
+	BR	notfound
+
+index17plus:
+	CMPBGT	R4, $31, index33plus
+	SUB	$16, R4, R0
+	VLL	R0, 16(R3), V1
+	VONE	V7
+index17to32loop:
+	VL	(R7), V2
+	VLL	R0, 16(R7), V3
+	VCEQG	V0, V2, V4
+	VCEQG	V1, V3, V5
+	VN	V4, V5, V6
+	VCEQGS	V6, V7, V8
+	BEQ	found
+	MOVD	$1(R7), R7
+	CMPBLE  R7, R2, index17to32loop
+	BR	notfound
+
+index33plus:
+	CMPBGT	R4, $47, index49plus
+	SUB	$32, R4, R0
+	VL	16(R3), V1
+	VLL	R0, 32(R3), V2
+	VONE	V11
+index33to48loop:
+	VL	(R7), V3
+	VL	16(R7), V4
+	VLL	R0, 32(R7), V5
+	VCEQG	V0, V3, V6
+	VCEQG	V1, V4, V7
+	VCEQG	V2, V5, V8
+	VN	V6, V7, V9
+	VN	V8, V9, V10
+	VCEQGS	V10, V11, V12
+	BEQ	found
+	MOVD	$1(R7), R7
+	CMPBLE  R7, R2, index33to48loop
+	BR	notfound
+
+index49plus:
+	CMPBGT	R4, $63, index65plus
+	SUB	$48, R4, R0
+	VL	16(R3), V1
+	VL	32(R3), V2
+	VLL	R0, 48(R3), V3
+	VONE	V15
+index49to64loop:
+	VL	(R7), V4
+	VL	16(R7), V5
+	VL	32(R7), V6
+	VLL	R0, 48(R7), V7
+	VCEQG	V0, V4, V8
+	VCEQG	V1, V5, V9
+	VCEQG	V2, V6, V10
+	VCEQG	V3, V7, V11
+	VN	V8, V9, V12
+	VN	V10, V11, V13
+	VN	V12, V13, V14
+	VCEQGS	V14, V15, V16
+	BEQ	found
+	MOVD	$1(R7), R7
+	CMPBLE  R7, R2, index49to64loop
+notfound:
+	MOVD	$-1, (R5)
+	RET
+
+index65plus:
+	// not implemented
+	MOVD	$0, (R0)
+	RET
+
+foundV17: // index is in doubleword V17[0]
+	VLGVG	$0, V17, R8
+	ADD	R8, R7
+found:
+	SUB	R1, R7
+	MOVD	R7, (R5)
+	RET
+
 // This is called from .init_array and follows the platform, not Go, ABI.
 // We are overly conservative. We could only save the registers we use.
 // However, since this function is only called once per loaded module
 // performance is unimportant.
 TEXT runtime·addmoduledata(SB),NOSPLIT|NOFRAME,$0-0
-	// Save R6-R15, F0, F2, F4 and F6 in the
-	// register save area of the calling function
+	// Save R6-R15 in the register save area of the calling function.
+	// Don't bother saving F8-F15 as we aren't doing any calls.
 	STMG	R6, R15, 48(R15)
-	FMOVD	F0, 128(R15)
-	FMOVD	F2, 136(R15)
-	FMOVD	F4, 144(R15)
-	FMOVD	F6, 152(R15)
 
 	// append the argument (passed in R2, as per the ELF ABI) to the
 	// moduledata linked list.
@@ -1123,12 +1315,8 @@ TEXT runtime·addmoduledata(SB),NOSPLIT|NOFRAME,$0-0
 	MOVD	R2, moduledata_next(R1)
 	MOVD	R2, runtime·lastmoduledatap(SB)
 
-	// Restore R6-R15, F0, F2, F4 and F6
+	// Restore R6-R15.
 	LMG	48(R15), R6, R15
-	FMOVD	F0, 128(R15)
-	FMOVD	F2, 136(R15)
-	FMOVD	F4, 144(R15)
-	FMOVD	F6, 152(R15)
 	RET
 
 TEXT ·checkASM(SB),NOSPLIT,$0-1
diff --git a/src/runtime/atomic_mipsx.s b/src/runtime/atomic_mipsx.s
new file mode 100644
index 0000000..ed078a2
--- /dev/null
+++ b/src/runtime/atomic_mipsx.s
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "textflag.h"
+
+TEXT ·publicationBarrier(SB),NOSPLIT,$0
+	SYNC
+	RET
diff --git a/src/runtime/atomic_pointer.go b/src/runtime/atomic_pointer.go
index 4fe3340..292b351 100644
--- a/src/runtime/atomic_pointer.go
+++ b/src/runtime/atomic_pointer.go
@@ -20,17 +20,17 @@ import (
 //
 //go:nosplit
 func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) {
+	writebarrierptr_prewrite((*uintptr)(ptr), uintptr(new))
 	atomic.StorepNoWB(noescape(ptr), new)
-	writebarrierptr_nostore((*uintptr)(ptr), uintptr(new))
 }
 
 //go:nosplit
 func casp(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool {
-	if !atomic.Casp1((*unsafe.Pointer)(noescape(unsafe.Pointer(ptr))), noescape(old), new) {
-		return false
-	}
-	writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
-	return true
+	// The write barrier is only necessary if the CAS succeeds,
+	// but since it needs to happen before the write becomes
+	// public, we have to do it conservatively all the time.
+	writebarrierptr_prewrite((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
+	return atomic.Casp1((*unsafe.Pointer)(noescape(unsafe.Pointer(ptr))), noescape(old), new)
 }
 
 // Like above, but implement in terms of sync/atomic's uintptr operations.
@@ -43,8 +43,8 @@ func sync_atomic_StoreUintptr(ptr *uintptr, new uintptr)
 //go:linkname sync_atomic_StorePointer sync/atomic.StorePointer
 //go:nosplit
 func sync_atomic_StorePointer(ptr *unsafe.Pointer, new unsafe.Pointer) {
+	writebarrierptr_prewrite((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
 	sync_atomic_StoreUintptr((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
-	writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
 }
 
 //go:linkname sync_atomic_SwapUintptr sync/atomic.SwapUintptr
@@ -53,8 +53,8 @@ func sync_atomic_SwapUintptr(ptr *uintptr, new uintptr) uintptr
 //go:linkname sync_atomic_SwapPointer sync/atomic.SwapPointer
 //go:nosplit
 func sync_atomic_SwapPointer(ptr *unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer {
+	writebarrierptr_prewrite((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
 	old := unsafe.Pointer(sync_atomic_SwapUintptr((*uintptr)(noescape(unsafe.Pointer(ptr))), uintptr(new)))
-	writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
 	return old
 }
 
@@ -64,9 +64,6 @@ func sync_atomic_CompareAndSwapUintptr(ptr *uintptr, old, new uintptr) bool
 //go:linkname sync_atomic_CompareAndSwapPointer sync/atomic.CompareAndSwapPointer
 //go:nosplit
 func sync_atomic_CompareAndSwapPointer(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool {
-	if !sync_atomic_CompareAndSwapUintptr((*uintptr)(noescape(unsafe.Pointer(ptr))), uintptr(old), uintptr(new)) {
-		return false
-	}
-	writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
-	return true
+	writebarrierptr_prewrite((*uintptr)(unsafe.Pointer(ptr)), uintptr(new))
+	return sync_atomic_CompareAndSwapUintptr((*uintptr)(noescape(unsafe.Pointer(ptr))), uintptr(old), uintptr(new))
 }
diff --git a/src/runtime/cgo/asm_arm64.s b/src/runtime/cgo/asm_arm64.s
index e55a70f..925e930 100644
--- a/src/runtime/cgo/asm_arm64.s
+++ b/src/runtime/cgo/asm_arm64.s
@@ -43,7 +43,6 @@ TEXT crosscall2(SB),NOSPLIT,$-8
 	MOVD	R0, R19
 
 	// Initialize Go ABI environment
-	BL      runtime·reginit(SB)
 	BL	runtime·load_g(SB)
 	BL	(R19)
 
diff --git a/src/runtime/cgo/asm_mips64x.s b/src/runtime/cgo/asm_mips64x.s
index 19e9014..aae2767 100644
--- a/src/runtime/cgo/asm_mips64x.s
+++ b/src/runtime/cgo/asm_mips64x.s
@@ -1,4 +1,4 @@
-// Copyright 2016 The Go Authors.  All rights reserved.
+// Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
@@ -18,9 +18,9 @@ TEXT crosscall2(SB),NOSPLIT,$-8
 	 *  first arg.
 	 */
 	ADDV	$(-8*23), R29
-	MOVV	R5, (8*1)(R29)
-	MOVV	R6, (8*2)(R29)
-	MOVV	R7, (8*3)(R29)
+	MOVV	R5, (8*1)(R29) // void*
+	MOVW	R6, (8*2)(R29) // int32
+	MOVV	R7, (8*3)(R29) // uintptr
 	MOVV	R16, (8*4)(R29)
 	MOVV	R17, (8*5)(R29)
 	MOVV	R18, (8*6)(R29)
@@ -46,13 +46,9 @@ TEXT crosscall2(SB),NOSPLIT,$-8
 	BGEZAL	R0, 1(PC)
 	SRLV	$32, R31, RSB
 	SLLV	$32, RSB
-	JAL	runtime·reginit(SB)
 	JAL	runtime·load_g(SB)
 	JAL	(R4)
 
-	MOVV	(8*1)(R29), R5
-	MOVV	(8*2)(R29), R6
-	MOVV	(8*3)(R29), R7
 	MOVV	(8*4)(R29), R16
 	MOVV	(8*5)(R29), R17
 	MOVV	(8*6)(R29), R18
diff --git a/src/runtime/cgo/asm_s390x.s b/src/runtime/cgo/asm_s390x.s
index ae688b6..7eab8f6 100644
--- a/src/runtime/cgo/asm_s390x.s
+++ b/src/runtime/cgo/asm_s390x.s
@@ -8,36 +8,46 @@
 // func crosscall2(fn func(a unsafe.Pointer, n int32, ctxt uintptr), a unsafe.Pointer, n int32, ctxt uintptr)
 // Saves C callee-saved registers and calls fn with three arguments.
 TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0
-	// Start with standard C stack frame layout and linkage
+	// Start with standard C stack frame layout and linkage.
 
-	// Save R6-R15, F0, F2, F4 and F6 in the
-	// register save area of the calling function
+	// Save R6-R15 in the register save area of the calling function.
 	STMG	R6, R15, 48(R15)
-	FMOVD	F0, 128(R15)
-	FMOVD	F2, 136(R15)
-	FMOVD	F4, 144(R15)
-	FMOVD	F6, 152(R15)
 
-	// Initialize Go ABI environment
-	XOR	R0, R0
-	BL	runtime·load_g(SB)
+	// Allocate 96 bytes on the stack.
+	MOVD	$-96(R15), R15
+
+	// Save F8-F15 in our stack frame.
+	FMOVD	F8, 32(R15)
+	FMOVD	F9, 40(R15)
+	FMOVD	F10, 48(R15)
+	FMOVD	F11, 56(R15)
+	FMOVD	F12, 64(R15)
+	FMOVD	F13, 72(R15)
+	FMOVD	F14, 80(R15)
+	FMOVD	F15, 88(R15)
 
-	// Allocate 32 bytes on the stack
-	SUB	$32, R15
+	// Initialize Go ABI environment.
+	BL	runtime·load_g(SB)
 
 	MOVD	R3, 8(R15)  // arg1
 	MOVW	R4, 16(R15) // arg2
 	MOVD	R5, 24(R15) // arg3
 	BL	(R2)        // fn(arg1, arg2, arg3)
 
-	ADD	$32, R15
+	FMOVD	32(R15), F8
+	FMOVD	40(R15), F9
+	FMOVD	48(R15), F10
+	FMOVD	56(R15), F11
+	FMOVD	64(R15), F12
+	FMOVD	72(R15), F13
+	FMOVD	80(R15), F14
+	FMOVD	88(R15), F15
+
+	// De-allocate stack frame.
+	MOVD	$96(R15), R15
 
-	// Restore R6-R15, F0, F2, F4 and F6
+	// Restore R6-R15.
 	LMG	48(R15), R6, R15
-	FMOVD	F0, 128(R15)
-	FMOVD	F2, 136(R15)
-	FMOVD	F4, 144(R15)
-	FMOVD	F6, 152(R15)
 
 	RET
 
diff --git a/src/runtime/cgo/cgo.go b/src/runtime/cgo/cgo.go
index ce0e6a3..241a821 100644
--- a/src/runtime/cgo/cgo.go
+++ b/src/runtime/cgo/cgo.go
@@ -20,9 +20,6 @@ package cgo
 #cgo !android,linux LDFLAGS: -lpthread
 #cgo netbsd LDFLAGS: -lpthread
 #cgo openbsd LDFLAGS: -lpthread
-// we must explicitly link msvcrt, because runtime needs ntdll, and ntdll
-// exports some incompatible libc functions. See golang.org/issue/12030.
-#cgo windows LDFLAGS: -lmsvcrt -lm -mthreads
 
 #cgo CFLAGS: -Wall -Werror
 
diff --git a/src/runtime/cgo/gcc_context.c b/src/runtime/cgo/gcc_context.c
index 1e6cf7e..b46b604 100644
--- a/src/runtime/cgo/gcc_context.c
+++ b/src/runtime/cgo/gcc_context.c
@@ -1,4 +1,4 @@
-// Copyright 2016 The Go Authors.  All rights reserved.
+// Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
diff --git a/src/runtime/cgo/gcc_dragonfly_amd64.c b/src/runtime/cgo/gcc_dragonfly_amd64.c
index b534dcc..e532ad6 100644
--- a/src/runtime/cgo/gcc_dragonfly_amd64.c
+++ b/src/runtime/cgo/gcc_dragonfly_amd64.c
@@ -56,7 +56,6 @@ static void*
 threadentry(void *v)
 {
 	ThreadStart ts;
-	stack_t ss;
 
 	ts = *(ThreadStart*)v;
 	free(v);
@@ -66,17 +65,6 @@ threadentry(void *v)
 	 */
 	setg_gcc((void*)ts.g);
 
-	// On DragonFly, a new thread inherits the signal stack of the
-	// creating thread. That confuses minit, so we remove that
-	// signal stack here before calling the regular mstart. It's
-	// a bit baroque to remove a signal stack here only to add one
-	// in minit, but it's a simple change that keeps DragonFly
-	// working like other OS's. At this point all signals are
-	// blocked, so there is no race.
-	memset(&ss, 0, sizeof ss);
-	ss.ss_flags = SS_DISABLE;
-	sigaltstack(&ss, nil);
-
 	crosscall_amd64(ts.fn);
 	return nil;
 }
diff --git a/src/runtime/cgo/gcc_libinit_windows.c b/src/runtime/cgo/gcc_libinit_windows.c
index 0824e20..b6f51b3 100644
--- a/src/runtime/cgo/gcc_libinit_windows.c
+++ b/src/runtime/cgo/gcc_libinit_windows.c
@@ -12,8 +12,8 @@
 
 #include "libcgo.h"
 
-static volatile long runtime_init_once_gate = 0;
-static volatile long runtime_init_once_done = 0;
+static volatile LONG runtime_init_once_gate = 0;
+static volatile LONG runtime_init_once_done = 0;
 
 static CRITICAL_SECTION runtime_init_cs;
 
diff --git a/src/runtime/cgo/gcc_linux_mips64x.c b/src/runtime/cgo/gcc_linux_mips64x.c
index 5bf5197..8a95629 100644
--- a/src/runtime/cgo/gcc_linux_mips64x.c
+++ b/src/runtime/cgo/gcc_linux_mips64x.c
@@ -1,4 +1,4 @@
-// Copyright 2016 The Go Authors.  All rights reserved.
+// Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
diff --git a/src/runtime/cgo/gcc_mips64x.S b/src/runtime/cgo/gcc_mips64x.S
index adeb7ae..a7ef006 100644
--- a/src/runtime/cgo/gcc_mips64x.S
+++ b/src/runtime/cgo/gcc_mips64x.S
@@ -1,4 +1,4 @@
-// Copyright 2016 The Go Authors.  All rights reserved.
+// Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
@@ -35,8 +35,6 @@ crosscall1:
 	sdc1	$f30, 136($29)
 	sdc1	$f31, 144($29)
 
-	dla	$23,_cgo_reginit
-
 	// prepare SB register = pc & 0xffffffff00000000
 	bal	1f
 1:
@@ -44,7 +42,6 @@ crosscall1:
 	dsll	$28, $28, 32
 
 	move	$20, $4 // save R4
-	jalr	$23	// call _cgo_reginit, set up Go ABI constant registers
 	move	$1, $6
 	jalr	$5	// call setg_gcc (clobbers R4)
 	jalr	$20	// call fn
diff --git a/src/runtime/cgo/gcc_s390x.S b/src/runtime/cgo/gcc_s390x.S
index 022f82d..614de4b 100644
--- a/src/runtime/cgo/gcc_s390x.S
+++ b/src/runtime/cgo/gcc_s390x.S
@@ -1,4 +1,4 @@
-// Copyright 2016 The Go Authors.  All rights reserved.
+// Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
@@ -6,38 +6,48 @@
  * void crosscall_s390x(void (*fn)(void), void *g)
  *
  * Calling into the go tool chain, where all registers are caller save.
- * Called from standard s390x C ABI, where r6-r13, r15, and f0, f2, f4 and f6 are
+ * Called from standard s390x C ABI, where r6-r13, r15, and f8-f15 are
  * callee-save, so they must be saved explicitly.
  */
 .globl crosscall_s390x
 crosscall_s390x:
-	/*
-	 * save r6-r15, f0, f2, f4 and f6 in the
-	 * register save area of the calling function
-	 */
-	stmg	%r6, %r15, 48(%r15)
-	stdy	%f0, 128(%r15)
-	stdy	%f2, 136(%r15)
-	stdy	%f4, 144(%r15)
-	stdy	%f6, 152(%r15)
-
-	/* set r0 to 0 */
-	xgr	%r0, %r0
+	/* save r6-r15 in the register save area of the calling function */
+	stmg    %r6, %r15, 48(%r15)
+
+	/* allocate 64 bytes of stack space to save f8-f15 */
+	lay     %r15, -64(%r15)
+
+	/* save callee-saved floating point registers */
+	std     %f8, 0(%r15)
+	std     %f9, 8(%r15)
+	std     %f10, 16(%r15)
+	std     %f11, 24(%r15)
+	std     %f12, 32(%r15)
+	std     %f13, 40(%r15)
+	std     %f14, 48(%r15)
+	std     %f15, 56(%r15)
 
 	/* restore g pointer */
-	lgr	%r13, %r3
+	lgr     %r13, %r3
 
-	/* grow stack 8 bytes and call fn */
-	agfi    %r15, -8
+	/* call fn */
 	basr    %r14, %r2
-	agfi	%r15, 8
-
-	/* restore registers */
-	lmg	%r6, %r15, 48(%r15)
-	ldy	%f0, 128(%r15)
-	ldy	%f2, 136(%r15)
-	ldy	%f4, 144(%r15)
-	ldy	%f6, 152(%r15)
+
+	/* restore floating point registers */
+	ld      %f8, 0(%r15)
+	ld      %f9, 8(%r15)
+	ld      %f10, 16(%r15)
+	ld      %f11, 24(%r15)
+	ld      %f12, 32(%r15)
+	ld      %f13, 40(%r15)
+	ld      %f14, 48(%r15)
+	ld      %f15, 56(%r15)
+
+	/* de-allocate stack frame */
+	la      %r15, 64(%r15)
+
+	/* restore general purpose registers */
+	lmg     %r6, %r15, 48(%r15)
 
 	br      %r14 /* restored by lmg */
 
diff --git a/src/runtime/cgo/gcc_setenv.c b/src/runtime/cgo/gcc_setenv.c
index 8708d40..ed5d203 100644
--- a/src/runtime/cgo/gcc_setenv.c
+++ b/src/runtime/cgo/gcc_setenv.c
@@ -13,12 +13,16 @@
 void
 x_cgo_setenv(char **arg)
 {
+	_cgo_tsan_acquire();
 	setenv(arg[0], arg[1], 1);
+	_cgo_tsan_release();
 }
 
 /* Stub for calling unsetenv */
 void
 x_cgo_unsetenv(char *arg)
 {
+	_cgo_tsan_acquire();
 	unsetenv(arg);
+	_cgo_tsan_release();
 }
diff --git a/src/runtime/cgo/gcc_sigaction.c b/src/runtime/cgo/gcc_sigaction.c
new file mode 100644
index 0000000..5aca271
--- /dev/null
+++ b/src/runtime/cgo/gcc_sigaction.c
@@ -0,0 +1,76 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,amd64
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+
+// go_sigaction_t is a C version of the sigactiont struct from
+// defs_linux_amd64.go.  This definition — and its conversion to and from struct
+// sigaction — are specific to linux/amd64.
+typedef struct {
+	uintptr_t handler;
+	uint64_t flags;
+	uintptr_t restorer;
+	uint64_t mask;
+} go_sigaction_t;
+
+// SA_RESTORER is part of the kernel interface.
+// This is GNU/Linux i386/amd64 specific.
+#ifndef SA_RESTORER
+#define SA_RESTORER 0x4000000
+#endif
+
+int32_t
+x_cgo_sigaction(intptr_t signum, const go_sigaction_t *goact, go_sigaction_t *oldgoact) {
+	int32_t ret;
+	struct sigaction act;
+	struct sigaction oldact;
+	int i;
+
+	memset(&act, 0, sizeof act);
+	memset(&oldact, 0, sizeof oldact);
+
+	if (goact) {
+		if (goact->flags & SA_SIGINFO) {
+			act.sa_sigaction = (void(*)(int, siginfo_t*, void*))(goact->handler);
+		} else {
+			act.sa_handler = (void(*)(int))(goact->handler);
+		}
+		sigemptyset(&act.sa_mask);
+		for (i = 0; i < 8 * sizeof(goact->mask); i++) {
+			if (goact->mask & ((uint64_t)(1)<<i)) {
+				sigaddset(&act.sa_mask, i+1);
+			}
+		}
+		act.sa_flags = goact->flags & ~SA_RESTORER;
+	}
+
+	ret = sigaction(signum, goact ? &act : NULL, oldgoact ? &oldact : NULL);
+	if (ret == -1) {
+		/* This is what the Go code expects on failure. */
+		return errno;
+	}
+
+	if (oldgoact) {
+		if (oldact.sa_flags & SA_SIGINFO) {
+			oldgoact->handler = (uintptr_t)(oldact.sa_sigaction);
+		} else {
+			oldgoact->handler = (uintptr_t)(oldact.sa_handler);
+		}
+		oldgoact->mask = 0;
+		for (i = 0; i < 8 * sizeof(oldgoact->mask); i++) {
+			if (sigismember(&act.sa_mask, i+1) == 1) {
+				oldgoact->mask |= (uint64_t)(1)<<i;
+			}
+		}
+		oldgoact->flags = act.sa_flags;
+	}
+
+	return ret;
+}
diff --git a/src/runtime/cgo/sigaction.go b/src/runtime/cgo/sigaction.go
new file mode 100644
index 0000000..30d3f14
--- /dev/null
+++ b/src/runtime/cgo/sigaction.go
@@ -0,0 +1,22 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,amd64
+
+package cgo
+
+// Import "unsafe" because we use go:linkname.
+import _ "unsafe"
+
+// When using cgo, call the C library for sigaction, so that we call into
+// any sanitizer interceptors. This supports using the memory
+// sanitizer with Go programs. The memory sanitizer only applies to
+// C/C++ code; this permits that code to see the Go runtime's existing signal
+// handlers when registering new signal handlers for the process.
+
+//go:cgo_import_static x_cgo_sigaction
+//go:linkname x_cgo_sigaction x_cgo_sigaction
+//go:linkname _cgo_sigaction _cgo_sigaction
+var x_cgo_sigaction byte
+var _cgo_sigaction = &x_cgo_sigaction
diff --git a/src/runtime/cgo_mips64x.go b/src/runtime/cgo_mips64x.go
deleted file mode 100644
index f718e92..0000000
--- a/src/runtime/cgo_mips64x.go
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2016 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build mips64 mips64le
-
-package runtime
-
-// crosscall1 calls into the runtime to set up the registers the
-// Go runtime expects and so the symbol it calls needs to be exported
-// for external linking to work.
-//go:cgo_export_static _cgo_reginit
diff --git a/src/runtime/cgo_mmap.go b/src/runtime/cgo_mmap.go
index a23cc79..5a2a1a2 100644
--- a/src/runtime/cgo_mmap.go
+++ b/src/runtime/cgo_mmap.go
@@ -35,7 +35,6 @@ func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) uns
 // sysMmap calls the mmap system call. It is implemented in assembly.
 func sysMmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) unsafe.Pointer
 
-// cgoMmap calls the mmap function in the runtime/cgo package on the
 // callCgoMmap calls the mmap function in the runtime/cgo package
 // using the GCC calling convention. It is implemented in assembly.
 func callCgoMmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) uintptr
diff --git a/src/runtime/cgo_sigaction.go b/src/runtime/cgo_sigaction.go
new file mode 100644
index 0000000..4da2f40
--- /dev/null
+++ b/src/runtime/cgo_sigaction.go
@@ -0,0 +1,89 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Support for memory sanitizer. See runtime/cgo/sigaction.go.
+
+// +build linux,amd64
+
+package runtime
+
+import "unsafe"
+
+// _cgo_sigaction is filled in by runtime/cgo when it is linked into the
+// program, so it is only non-nil when using cgo.
+//go:linkname _cgo_sigaction _cgo_sigaction
+var _cgo_sigaction unsafe.Pointer
+
+//go:nosplit
+//go:nowritebarrierrec
+func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 {
+	// The runtime package is explicitly blacklisted from sanitizer
+	// instrumentation in racewalk.go, but we might be calling into instrumented C
+	// functions here — so we need the pointer parameters to be properly marked.
+	//
+	// Mark the input as having been written before the call and the output as
+	// read after.
+	if msanenabled && new != nil {
+		msanwrite(unsafe.Pointer(new), unsafe.Sizeof(*new))
+	}
+
+	var ret int32
+
+	if _cgo_sigaction == nil {
+		ret = sysSigaction(sig, new, old, size)
+	} else {
+		// We need to call _cgo_sigaction, which means we need a big enough stack
+		// for C.  To complicate matters, we may be in libpreinit (before the
+		// runtime has been initialized) or in an asynchronous signal handler (with
+		// the current thread in transition between goroutines, or with the g0
+		// system stack already in use).
+
+		g := getg()
+		sp := uintptr(unsafe.Pointer(&sig))
+		switch {
+		case g == nil:
+			// No g: we're on a C stack or a signal stack.
+			ret = callCgoSigaction(sig, new, old)
+		case sp < g.stack.lo || sp >= g.stack.hi:
+			// We're no longer on g's stack, so we must be handling a signal.  It's
+			// possible that we interrupted the thread during a transition between g
+			// and g0, so we should stay on the current stack to avoid corrupting g0.
+			ret = callCgoSigaction(sig, new, old)
+		default:
+			// We're running on g's stack, so either we're not in a signal handler or
+			// the signal handler has set the correct g.  If we're on gsignal or g0,
+			// systemstack will make the call directly; otherwise, it will switch to
+			// g0 to ensure we have enough room to call a libc function.
+			//
+			// The function literal that we pass to systemstack is not nosplit, but
+			// that's ok: we'll be running on a fresh, clean system stack so the stack
+			// check will always succeed anyway.
+			systemstack(func() {
+				ret = callCgoSigaction(sig, new, old)
+			})
+		}
+
+		const EINVAL = 22
+		if ret == EINVAL {
+			// libc reserves certain signals — normally 32-33 — for pthreads, and
+			// returns EINVAL for sigaction calls on those signals.  If we get EINVAL,
+			// fall back to making the syscall directly.
+			ret = sysSigaction(sig, new, old, size)
+		}
+	}
+
+	if msanenabled && old != nil && ret == 0 {
+		msanread(unsafe.Pointer(old), unsafe.Sizeof(*old))
+	}
+	return ret
+}
+
+// sysSigaction calls the rt_sigaction system call. It is implemented in assembly.
+//go:noescape
+func sysSigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
+
+// callCgoSigaction calls the sigaction function in the runtime/cgo package
+// using the GCC calling convention. It is implemented in assembly.
+//go:noescape
+func callCgoSigaction(sig uintptr, new, old *sigactiont) int32
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index f8d6930..007406b 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -104,34 +104,48 @@ func cgocall(fn, arg unsafe.Pointer) int32 {
 		racereleasemerge(unsafe.Pointer(&racecgosync))
 	}
 
-	/*
-	 * Lock g to m to ensure we stay on the same stack if we do a
-	 * cgo callback. Add entry to defer stack in case of panic.
-	 */
+	// Lock g to m to ensure we stay on the same stack if we do a
+	// cgo callback. In case of panic, unwindm calls endcgo.
 	lockOSThread()
 	mp := getg().m
 	mp.ncgocall++
 	mp.ncgo++
-	defer endcgo(mp)
 
 	// Reset traceback.
 	mp.cgoCallers[0] = 0
 
-	/*
-	 * Announce we are entering a system call
-	 * so that the scheduler knows to create another
-	 * M to run goroutines while we are in the
-	 * foreign code.
-	 *
-	 * The call to asmcgocall is guaranteed not to
-	 * split the stack and does not allocate memory,
-	 * so it is safe to call while "in a system call", outside
-	 * the $GOMAXPROCS accounting.
-	 */
+	// Announce we are entering a system call
+	// so that the scheduler knows to create another
+	// M to run goroutines while we are in the
+	// foreign code.
+	//
+	// The call to asmcgocall is guaranteed not to
+	// grow the stack and does not allocate memory,
+	// so it is safe to call while "in a system call", outside
+	// the $GOMAXPROCS accounting.
+	//
+	// fn may call back into Go code, in which case we'll exit the
+	// "system call", run the Go code (which may grow the stack),
+	// and then re-enter the "system call" reusing the PC and SP
+	// saved by entersyscall here.
 	entersyscall(0)
 	errno := asmcgocall(fn, arg)
 	exitsyscall(0)
 
+	// From the garbage collector's perspective, time can move
+	// backwards in the sequence above. If there's a callback into
+	// Go code, GC will see this function at the call to
+	// asmcgocall. When the Go call later returns to C, the
+	// syscall PC/SP is rolled back and the GC sees this function
+	// back at the call to entersyscall. Normally, fn and arg
+	// would be live at entersyscall and dead at asmcgocall, so if
+	// time moved backwards, GC would see these arguments as dead
+	// and then live. Prevent these undead arguments from crashing
+	// GC by forcing them to stay live across this time warp.
+	KeepAlive(fn)
+	KeepAlive(arg)
+
+	endcgo(mp)
 	return errno
 }
 
@@ -314,6 +328,16 @@ func unwindm(restore *bool) {
 	case "arm64":
 		sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16))
 	}
+
+	// Call endcgo to do the accounting that cgocall will not have a
+	// chance to do during an unwind.
+	//
+	// In the case where a a Go call originates from C, ncgo is 0
+	// and there is no matching cgocall to end.
+	if mp.ncgo > 0 {
+		endcgo(mp)
+	}
+
 	releasem(mp)
 }
 
@@ -360,10 +384,10 @@ var racecgosync uint64 // represents possible synchronization in C code
 // pointers.)
 
 // cgoCheckPointer checks if the argument contains a Go pointer that
-// points to a Go pointer, and panics if it does. It returns the pointer.
-func cgoCheckPointer(ptr interface{}, args ...interface{}) interface{} {
+// points to a Go pointer, and panics if it does.
+func cgoCheckPointer(ptr interface{}, args ...interface{}) {
 	if debug.cgocheck == 0 {
-		return ptr
+		return
 	}
 
 	ep := (*eface)(unsafe.Pointer(&ptr))
@@ -376,7 +400,7 @@ func cgoCheckPointer(ptr interface{}, args ...interface{}) interface{} {
 			p = *(*unsafe.Pointer)(p)
 		}
 		if !cgoIsGoPointer(p) {
-			return ptr
+			return
 		}
 		aep := (*eface)(unsafe.Pointer(&args[0]))
 		switch aep._type.kind & kindMask {
@@ -387,7 +411,7 @@ func cgoCheckPointer(ptr interface{}, args ...interface{}) interface{} {
 			}
 			pt := (*ptrtype)(unsafe.Pointer(t))
 			cgoCheckArg(pt.elem, p, true, false, cgoCheckPointerFail)
-			return ptr
+			return
 		case kindSlice:
 			// Check the slice rather than the pointer.
 			ep = aep
@@ -405,7 +429,6 @@ func cgoCheckPointer(ptr interface{}, args ...interface{}) interface{} {
 	}
 
 	cgoCheckArg(t, ep.data, t.kind&kindDirectIface == 0, top, cgoCheckPointerFail)
-	return ptr
 }
 
 const cgoCheckPointerFail = "cgo argument has Go pointer to Go pointer"
@@ -560,7 +583,7 @@ func cgoCheckUnknownPointer(p unsafe.Pointer, msg string) (base, i uintptr) {
 		return
 	}
 
-	for datap := &firstmoduledata; datap != nil; datap = datap.next {
+	for _, datap := range activeModules() {
 		if cgoInRange(p, datap.data, datap.edata) || cgoInRange(p, datap.bss, datap.ebss) {
 			// We have no way to know the size of the object.
 			// We have to assume that it might contain a pointer.
@@ -587,7 +610,7 @@ func cgoIsGoPointer(p unsafe.Pointer) bool {
 		return true
 	}
 
-	for datap := &firstmoduledata; datap != nil; datap = datap.next {
+	for _, datap := range activeModules() {
 		if cgoInRange(p, datap.data, datap.edata) || cgoInRange(p, datap.bss, datap.ebss) {
 			return true
 		}
diff --git a/src/runtime/cgocheck.go b/src/runtime/cgocheck.go
index 2d06414..8cac5d9 100644
--- a/src/runtime/cgocheck.go
+++ b/src/runtime/cgocheck.go
@@ -108,7 +108,7 @@ func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) {
 	}
 
 	// The type has a GC program. Try to find GC bits somewhere else.
-	for datap := &firstmoduledata; datap != nil; datap = datap.next {
+	for _, datap := range activeModules() {
 		if cgoInRange(src, datap.data, datap.edata) {
 			doff := uintptr(src) - datap.data
 			cgoCheckBits(add(src, -doff), datap.gcdatamask.bytedata, off+doff, size)
@@ -123,7 +123,7 @@ func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) {
 
 	aoff := uintptr(src) - mheap_.arena_start
 	idx := aoff >> _PageShift
-	s := h_spans[idx]
+	s := mheap_.spans[idx]
 	if s.state == _MSpanStack {
 		// There are no heap bits for value stored on the stack.
 		// For a channel receive src might be on the stack of some
diff --git a/src/runtime/chan.go b/src/runtime/chan.go
index 712ad8c..b54a46c 100644
--- a/src/runtime/chan.go
+++ b/src/runtime/chan.go
@@ -7,10 +7,16 @@ package runtime
 // This file contains the implementation of Go channels.
 
 // Invariants:
-//  At least one of c.sendq and c.recvq is empty.
+//  At least one of c.sendq and c.recvq is empty,
+//  except for the case of an unbuffered channel with a single goroutine
+//  blocked on it for both sending and receiving using a select statement,
+//  in which case the length of c.sendq and c.recvq is limited only by the
+//  size of the select statement.
+//
 // For buffered channels, also:
 //  c.qcount > 0 implies that c.recvq is empty.
 //  c.qcount < c.dataqsiz implies that c.sendq is empty.
+
 import (
 	"runtime/internal/atomic"
 	"unsafe"
@@ -281,23 +287,34 @@ func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func()) {
 	goready(gp, 4)
 }
 
+// Sends and receives on unbuffered or empty-buffered channels are the
+// only operations where one running goroutine writes to the stack of
+// another running goroutine. The GC assumes that stack writes only
+// happen when the goroutine is running and are only done by that
+// goroutine. Using a write barrier is sufficient to make up for
+// violating that assumption, but the write barrier has to work.
+// typedmemmove will call bulkBarrierPreWrite, but the target bytes
+// are not in the heap, so that will not help. We arrange to call
+// memmove and typeBitsBulkBarrier instead.
+
 func sendDirect(t *_type, sg *sudog, src unsafe.Pointer) {
-	// Send on an unbuffered or empty-buffered channel is the only operation
-	// in the entire runtime where one goroutine
-	// writes to the stack of another goroutine. The GC assumes that
-	// stack writes only happen when the goroutine is running and are
-	// only done by that goroutine. Using a write barrier is sufficient to
-	// make up for violating that assumption, but the write barrier has to work.
-	// typedmemmove will call heapBitsBulkBarrier, but the target bytes
-	// are not in the heap, so that will not help. We arrange to call
-	// memmove and typeBitsBulkBarrier instead.
+	// src is on our stack, dst is a slot on another stack.
 
 	// Once we read sg.elem out of sg, it will no longer
 	// be updated if the destination's stack gets copied (shrunk).
 	// So make sure that no preemption points can happen between read & use.
 	dst := sg.elem
+	typeBitsBulkBarrier(t, uintptr(dst), uintptr(src), t.size)
+	memmove(dst, src, t.size)
+}
+
+func recvDirect(t *_type, sg *sudog, dst unsafe.Pointer) {
+	// dst is on our stack or the heap, src is on another stack.
+	// The channel is locked, so src will not move during this
+	// operation.
+	src := sg.elem
+	typeBitsBulkBarrier(t, uintptr(dst), uintptr(src), t.size)
 	memmove(dst, src, t.size)
-	typeBitsBulkBarrier(t, uintptr(dst), t.size)
 }
 
 func closechan(c *hchan) {
@@ -328,7 +345,7 @@ func closechan(c *hchan) {
 			break
 		}
 		if sg.elem != nil {
-			memclr(sg.elem, uintptr(c.elemsize))
+			typedmemclr(c.elemtype, sg.elem)
 			sg.elem = nil
 		}
 		if sg.releasetime != 0 {
@@ -437,7 +454,7 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r
 		}
 		unlock(&c.lock)
 		if ep != nil {
-			memclr(ep, uintptr(c.elemsize))
+			typedmemclr(c.elemtype, ep)
 		}
 		return true, false
 	}
@@ -461,7 +478,7 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r
 		if ep != nil {
 			typedmemmove(c.elemtype, ep, qp)
 		}
-		memclr(qp, uintptr(c.elemsize))
+		typedmemclr(c.elemtype, qp)
 		c.recvx++
 		if c.recvx == c.dataqsiz {
 			c.recvx = 0
@@ -530,9 +547,7 @@ func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func()) {
 		}
 		if ep != nil {
 			// copy data from sender
-			// ep points to our own stack or heap, so nothing
-			// special (ala sendDirect) needed here.
-			typedmemmove(c.elemtype, ep, sg.elem)
+			recvDirect(c.elemtype, sg, ep)
 		}
 	} else {
 		// Queue is full. Take the item at the
diff --git a/src/runtime/chan_test.go b/src/runtime/chan_test.go
index 8e8c47b..a75fa1b 100644
--- a/src/runtime/chan_test.go
+++ b/src/runtime/chan_test.go
@@ -210,11 +210,14 @@ func TestNonblockRecvRace(t *testing.T) {
 			select {
 			case <-c:
 			default:
-				t.Fatal("chan is not ready")
+				t.Error("chan is not ready")
 			}
 		}()
 		close(c)
 		<-c
+		if t.Failed() {
+			return
+		}
 	}
 }
 
@@ -311,14 +314,16 @@ func TestSelfSelect(t *testing.T) {
 						case c <- p:
 						case v := <-c:
 							if chanCap == 0 && v == p {
-								t.Fatalf("self receive")
+								t.Errorf("self receive")
+								return
 							}
 						}
 					} else {
 						select {
 						case v := <-c:
 							if chanCap == 0 && v == p {
-								t.Fatalf("self receive")
+								t.Errorf("self receive")
+								return
 							}
 						case c <- p:
 						}
diff --git a/src/runtime/cpuflags_amd64.go b/src/runtime/cpuflags_amd64.go
new file mode 100644
index 0000000..026f0cd
--- /dev/null
+++ b/src/runtime/cpuflags_amd64.go
@@ -0,0 +1,75 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+var vendorStringBytes [12]byte
+var maxInputValue uint32
+var featureFlags uint32
+var processorVersionInfo uint32
+
+var useRepMovs = true
+
+func hasFeature(feature uint32) bool {
+	return (featureFlags & feature) != 0
+}
+
+func cpuid_low(arg1, arg2 uint32) (eax, ebx, ecx, edx uint32) // implemented in cpuidlow_amd64.s
+func xgetbv_low(arg1 uint32) (eax, edx uint32)                // implemented in cpuidlow_amd64.s
+
+func init() {
+	const cfOSXSAVE uint32 = 1 << 27
+	const cfAVX uint32 = 1 << 28
+
+	leaf0()
+	leaf1()
+
+	enabledAVX := false
+	// Let's check if OS has set CR4.OSXSAVE[bit 18]
+	// to enable XGETBV instruction.
+	if hasFeature(cfOSXSAVE) {
+		eax, _ := xgetbv_low(0)
+		// Let's check that XCR0[2:1] = ‘11b’
+		// i.e. XMM state and YMM state are enabled by OS.
+		enabledAVX = (eax & 0x6) == 0x6
+	}
+
+	isIntelBridgeFamily := (processorVersionInfo == 0x206A0 ||
+		processorVersionInfo == 0x206D0 ||
+		processorVersionInfo == 0x306A0 ||
+		processorVersionInfo == 0x306E0) &&
+		isIntel()
+
+	useRepMovs = !(hasFeature(cfAVX) && enabledAVX) || isIntelBridgeFamily
+}
+
+func leaf0() {
+	eax, ebx, ecx, edx := cpuid_low(0, 0)
+	maxInputValue = eax
+	int32ToBytes(ebx, vendorStringBytes[0:4])
+	int32ToBytes(edx, vendorStringBytes[4:8])
+	int32ToBytes(ecx, vendorStringBytes[8:12])
+}
+
+func leaf1() {
+	if maxInputValue < 1 {
+		return
+	}
+	eax, _, ecx, _ := cpuid_low(1, 0)
+	// Let's remove stepping and reserved fields
+	processorVersionInfo = eax & 0x0FFF3FF0
+	featureFlags = ecx
+}
+
+func int32ToBytes(arg uint32, buffer []byte) {
+	buffer[3] = byte(arg >> 24)
+	buffer[2] = byte(arg >> 16)
+	buffer[1] = byte(arg >> 8)
+	buffer[0] = byte(arg)
+}
+
+func isIntel() bool {
+	intelSignature := [12]byte{'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l'}
+	return vendorStringBytes == intelSignature
+}
diff --git a/src/runtime/cpuidlow_amd64.s b/src/runtime/cpuidlow_amd64.s
new file mode 100644
index 0000000..64316c9
--- /dev/null
+++ b/src/runtime/cpuidlow_amd64.s
@@ -0,0 +1,22 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// func cpuid_low(arg1, arg2 uint32) (eax, ebx, ecx, edx uint32)
+TEXT ·cpuid_low(SB), 4, $0-24
+    MOVL    arg1+0(FP), AX
+    MOVL    arg2+4(FP), CX
+    CPUID
+    MOVL AX, eax+8(FP)
+    MOVL BX, ebx+12(FP)
+    MOVL CX, ecx+16(FP)
+    MOVL DX, edx+20(FP)
+    RET
+// func xgetbv_low(arg1 uint32) (eax, edx uint32)
+TEXT ·xgetbv_low(SB), 4, $0-16
+    MOVL arg1+0(FP), CX
+    // XGETBV
+    BYTE $0x0F; BYTE $0x01; BYTE $0xD0
+    MOVL AX,eax+8(FP)
+    MOVL DX,edx+12(FP)
+    RET
diff --git a/src/runtime/cpuprof.go b/src/runtime/cpuprof.go
index 5308200..a4b14d3 100644
--- a/src/runtime/cpuprof.go
+++ b/src/runtime/cpuprof.go
@@ -4,7 +4,7 @@
 
 // CPU profiling.
 // Based on algorithms and data structures used in
-// http://code.google.com/p/google-perftools/.
+// https://github.com/google/pprof.
 //
 // The main difference between this code and the google-perftools
 // code is that this code is written to allow copying the profile data
@@ -68,6 +68,7 @@ type cpuprofEntry struct {
 	stack [maxCPUProfStack]uintptr
 }
 
+//go:notinheap
 type cpuProfile struct {
 	on     bool    // profiling is on
 	wait   note    // goroutine waits here
diff --git a/src/runtime/cputicks.go b/src/runtime/cputicks.go
index 9162746..ccc3947 100644
--- a/src/runtime/cputicks.go
+++ b/src/runtime/cputicks.go
@@ -6,6 +6,8 @@
 // +build !arm64
 // +build !mips64
 // +build !mips64le
+// +build !mips
+// +build !mipsle
 
 package runtime
 
diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go
index 2504bd0..7014f11 100644
--- a/src/runtime/crash_cgo_test.go
+++ b/src/runtime/crash_cgo_test.go
@@ -19,10 +19,12 @@ import (
 )
 
 func TestCgoCrashHandler(t *testing.T) {
+	t.Parallel()
 	testCrashHandler(t, true)
 }
 
 func TestCgoSignalDeadlock(t *testing.T) {
+	t.Parallel()
 	if testing.Short() && runtime.GOOS == "windows" {
 		t.Skip("Skipping in short mode") // takes up to 64 seconds
 	}
@@ -34,6 +36,7 @@ func TestCgoSignalDeadlock(t *testing.T) {
 }
 
 func TestCgoTraceback(t *testing.T) {
+	t.Parallel()
 	got := runTestProg(t, "testprogcgo", "CgoTraceback")
 	want := "OK\n"
 	if got != want {
@@ -42,8 +45,12 @@ func TestCgoTraceback(t *testing.T) {
 }
 
 func TestCgoCallbackGC(t *testing.T) {
-	if runtime.GOOS == "plan9" || runtime.GOOS == "windows" {
+	t.Parallel()
+	switch runtime.GOOS {
+	case "plan9", "windows":
 		t.Skipf("no pthreads on %s", runtime.GOOS)
+	case "freebsd":
+		testenv.SkipFlaky(t, 16396)
 	}
 	if testing.Short() {
 		switch {
@@ -63,6 +70,7 @@ func TestCgoCallbackGC(t *testing.T) {
 }
 
 func TestCgoExternalThreadPanic(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS == "plan9" {
 		t.Skipf("no pthreads on %s", runtime.GOOS)
 	}
@@ -74,6 +82,7 @@ func TestCgoExternalThreadPanic(t *testing.T) {
 }
 
 func TestCgoExternalThreadSIGPROF(t *testing.T) {
+	t.Parallel()
 	// issue 9456.
 	switch runtime.GOOS {
 	case "plan9", "windows":
@@ -97,22 +106,42 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) {
 		// ppc64 (issue #8912)
 		t.Skipf("no external linking on ppc64")
 	}
-	got := runTestProg(t, "testprogcgo", "CgoExternalThreadSIGPROF")
-	want := "OK\n"
-	if got != want {
+
+	exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	got, err := testEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
+	if err != nil {
+		t.Fatalf("exit status: %v\n%s", err, got)
+	}
+
+	if want := "OK\n"; string(got) != want {
 		t.Fatalf("expected %q, but got:\n%s", want, got)
 	}
 }
 
 func TestCgoExternalThreadSignal(t *testing.T) {
+	t.Parallel()
 	// issue 10139
 	switch runtime.GOOS {
 	case "plan9", "windows":
 		t.Skipf("no pthreads on %s", runtime.GOOS)
 	}
-	got := runTestProg(t, "testprogcgo", "CgoExternalThreadSignal")
-	want := "OK\n"
-	if got != want {
+
+	exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	got, err := testEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput()
+	if err != nil {
+		t.Fatalf("exit status: %v\n%s", err, got)
+	}
+
+	want := []byte("OK\n")
+	if !bytes.Equal(got, want) {
 		t.Fatalf("expected %q, but got:\n%s", want, got)
 	}
 }
@@ -130,6 +159,7 @@ func TestCgoDLLImports(t *testing.T) {
 }
 
 func TestCgoExecSignalMask(t *testing.T) {
+	t.Parallel()
 	// Test issue 13164.
 	switch runtime.GOOS {
 	case "windows", "plan9":
@@ -143,6 +173,7 @@ func TestCgoExecSignalMask(t *testing.T) {
 }
 
 func TestEnsureDropM(t *testing.T) {
+	t.Parallel()
 	// Test for issue 13881.
 	switch runtime.GOOS {
 	case "windows", "plan9":
@@ -159,6 +190,7 @@ func TestEnsureDropM(t *testing.T) {
 // Test that the program that doesn't need any cgo pointer checking
 // takes about the same amount of time with it as without it.
 func TestCgoCheckBytes(t *testing.T) {
+	t.Parallel()
 	// Make sure we don't count the build time as part of the run time.
 	testenv.MustHaveGoBuild(t)
 	exe, err := buildTestProg(t, "testprogcgo")
@@ -198,6 +230,7 @@ func TestCgoCheckBytes(t *testing.T) {
 }
 
 func TestCgoPanicDeadlock(t *testing.T) {
+	t.Parallel()
 	// test issue 14432
 	got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
 	want := "panic: cgo error\n\n"
@@ -207,6 +240,7 @@ func TestCgoPanicDeadlock(t *testing.T) {
 }
 
 func TestCgoCCodeSIGPROF(t *testing.T) {
+	t.Parallel()
 	got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF")
 	want := "OK\n"
 	if got != want {
@@ -215,6 +249,7 @@ func TestCgoCCodeSIGPROF(t *testing.T) {
 }
 
 func TestCgoCrashTraceback(t *testing.T) {
+	t.Parallel()
 	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
 		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
 	}
@@ -227,6 +262,7 @@ func TestCgoCrashTraceback(t *testing.T) {
 }
 
 func TestCgoTracebackContext(t *testing.T) {
+	t.Parallel()
 	got := runTestProg(t, "testprogcgo", "TracebackContext")
 	want := "OK\n"
 	if got != want {
@@ -235,6 +271,7 @@ func TestCgoTracebackContext(t *testing.T) {
 }
 
 func testCgoPprof(t *testing.T, buildArg, runArg string) {
+	t.Parallel()
 	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
 		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
 	}
@@ -252,7 +289,7 @@ func testCgoPprof(t *testing.T, buildArg, runArg string) {
 	fn := strings.TrimSpace(string(got))
 	defer os.Remove(fn)
 
-	cmd := testEnv(exec.Command("go", "tool", "pprof", "-top", "-nodecount=1", exe, fn))
+	cmd := testEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", exe, fn))
 
 	found := false
 	for i, e := range cmd.Env {
@@ -288,3 +325,65 @@ func TestCgoPprofPIE(t *testing.T) {
 func TestCgoPprofThread(t *testing.T) {
 	testCgoPprof(t, "", "CgoPprofThread")
 }
+
+func TestCgoPprofThreadNoTraceback(t *testing.T) {
+	testCgoPprof(t, "", "CgoPprofThreadNoTraceback")
+}
+
+func TestRaceProf(t *testing.T) {
+	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
+		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
+	}
+
+	testenv.MustHaveGoRun(t)
+
+	// This test requires building various packages with -race, so
+	// it's somewhat slow.
+	if testing.Short() {
+		t.Skip("skipping test in -short mode")
+	}
+
+	exe, err := buildTestProg(t, "testprogcgo", "-race")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	got, err := testEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput()
+	if err != nil {
+		t.Fatal(err)
+	}
+	want := "OK\n"
+	if string(got) != want {
+		t.Errorf("expected %q got %s", want, got)
+	}
+}
+
+func TestRaceSignal(t *testing.T) {
+	t.Parallel()
+	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
+		t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
+	}
+
+	testenv.MustHaveGoRun(t)
+
+	// This test requires building various packages with -race, so
+	// it's somewhat slow.
+	if testing.Short() {
+		t.Skip("skipping test in -short mode")
+	}
+
+	exe, err := buildTestProg(t, "testprogcgo", "-race")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	got, err := testEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput()
+	if err != nil {
+		t.Logf("%s\n", got)
+		t.Fatal(err)
+	}
+	want := "OK\n"
+	if string(got) != want {
+		t.Errorf("expected %q got %s", want, got)
+	}
+}
diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go
index a2f7ff7..9ec0ae4 100644
--- a/src/runtime/crash_test.go
+++ b/src/runtime/crash_test.go
@@ -6,6 +6,7 @@ package runtime_test
 
 import (
 	"bytes"
+	"flag"
 	"fmt"
 	"internal/testenv"
 	"io/ioutil"
@@ -136,11 +137,10 @@ func buildTestProg(t *testing.T, binary string, flags ...string) (string, error)
 	}
 
 	exe := filepath.Join(testprog.dir, name+".exe")
-	cmd := exec.Command("go", append([]string{"build", "-o", exe}, flags...)...)
+	cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
 	cmd.Dir = "testdata/" + binary
 	out, err := testEnv(cmd).CombinedOutput()
 	if err != nil {
-		exe = ""
 		target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
 		testprog.target[name] = target
 		return "", target.err
@@ -158,7 +158,7 @@ var (
 func checkStaleRuntime(t *testing.T) {
 	staleRuntimeOnce.Do(func() {
 		// 'go run' uses the installed copy of runtime.a, which may be out of date.
-		out, err := testEnv(exec.Command("go", "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
+		out, err := testEnv(exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
 		if err != nil {
 			staleRuntimeErr = fmt.Errorf("failed to execute 'go list': %v\n%v", err, string(out))
 			return
@@ -401,6 +401,7 @@ func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
 }
 
 func TestNetpollDeadlock(t *testing.T) {
+	t.Parallel()
 	output := runTestProg(t, "testprognet", "NetpollDeadlock")
 	want := "done\n"
 	if !strings.HasSuffix(output, want) {
@@ -409,6 +410,7 @@ func TestNetpollDeadlock(t *testing.T) {
 }
 
 func TestPanicTraceback(t *testing.T) {
+	t.Parallel()
 	output := runTestProg(t, "testprog", "PanicTraceback")
 	want := "panic: hello"
 	if !strings.HasPrefix(output, want) {
@@ -416,7 +418,7 @@ func TestPanicTraceback(t *testing.T) {
 	}
 
 	// Check functions in the traceback.
-	fns := []string{"panic", "main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
+	fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
 	for _, fn := range fns {
 		re := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`)
 		idx := re.FindStringIndex(output)
@@ -443,6 +445,13 @@ func TestPanicDeadlockSyscall(t *testing.T) {
 	testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n")
 }
 
+func TestPanicLoop(t *testing.T) {
+	output := runTestProg(t, "testprog", "PanicLoop")
+	if want := "panic while printing panic value"; !strings.Contains(output, want) {
+		t.Errorf("output does not contain %q:\n%s", want, output)
+	}
+}
+
 func TestMemPprof(t *testing.T) {
 	testenv.MustHaveGoRun(t)
 
@@ -458,7 +467,7 @@ func TestMemPprof(t *testing.T) {
 	fn := strings.TrimSpace(string(got))
 	defer os.Remove(fn)
 
-	cmd := testEnv(exec.Command("go", "tool", "pprof", "-alloc_space", "-top", exe, fn))
+	cmd := testEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top", exe, fn))
 
 	found := false
 	for i, e := range cmd.Env {
@@ -482,3 +491,39 @@ func TestMemPprof(t *testing.T) {
 		t.Error("missing MemProf in pprof output")
 	}
 }
+
+var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
+
+func TestConcurrentMapWrites(t *testing.T) {
+	if !*concurrentMapTest {
+		t.Skip("skipping without -run_concurrent_map_tests")
+	}
+	testenv.MustHaveGoRun(t)
+	output := runTestProg(t, "testprog", "concurrentMapWrites")
+	want := "fatal error: concurrent map writes"
+	if !strings.HasPrefix(output, want) {
+		t.Fatalf("output does not start with %q:\n%s", want, output)
+	}
+}
+func TestConcurrentMapReadWrite(t *testing.T) {
+	if !*concurrentMapTest {
+		t.Skip("skipping without -run_concurrent_map_tests")
+	}
+	testenv.MustHaveGoRun(t)
+	output := runTestProg(t, "testprog", "concurrentMapReadWrite")
+	want := "fatal error: concurrent map read and map write"
+	if !strings.HasPrefix(output, want) {
+		t.Fatalf("output does not start with %q:\n%s", want, output)
+	}
+}
+func TestConcurrentMapIterateWrite(t *testing.T) {
+	if !*concurrentMapTest {
+		t.Skip("skipping without -run_concurrent_map_tests")
+	}
+	testenv.MustHaveGoRun(t)
+	output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
+	want := "fatal error: concurrent map iteration and map write"
+	if !strings.HasPrefix(output, want) {
+		t.Fatalf("output does not start with %q:\n%s", want, output)
+	}
+}
diff --git a/src/runtime/crash_unix_test.go b/src/runtime/crash_unix_test.go
index 6e4d04b..97deed8 100644
--- a/src/runtime/crash_unix_test.go
+++ b/src/runtime/crash_unix_test.go
@@ -37,6 +37,8 @@ func TestCrashDumpsAllThreads(t *testing.T) {
 
 	checkStaleRuntime(t)
 
+	t.Parallel()
+
 	dir, err := ioutil.TempDir("", "go-build")
 	if err != nil {
 		t.Fatalf("failed to create temp directory: %v", err)
@@ -47,7 +49,7 @@ func TestCrashDumpsAllThreads(t *testing.T) {
 		t.Fatalf("failed to create Go file: %v", err)
 	}
 
-	cmd := exec.Command("go", "build", "-o", "a.exe")
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe")
 	cmd.Dir = dir
 	out, err := testEnv(cmd).CombinedOutput()
 	if err != nil {
diff --git a/src/runtime/debug/garbage.go b/src/runtime/debug/garbage.go
index 8144497..c82c024 100644
--- a/src/runtime/debug/garbage.go
+++ b/src/runtime/debug/garbage.go
@@ -71,7 +71,7 @@ func ReadGCStats(stats *GCStats) {
 			// See the allocation at the top of the function.
 			sorted := stats.Pause[n : n+n]
 			copy(sorted, stats.Pause)
-			sort.Sort(byDuration(sorted))
+			sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] })
 			nq := len(stats.PauseQuantiles) - 1
 			for i := 0; i < nq; i++ {
 				stats.PauseQuantiles[i] = sorted[len(sorted)*i/nq]
@@ -81,12 +81,6 @@ func ReadGCStats(stats *GCStats) {
 	}
 }
 
-type byDuration []time.Duration
-
-func (x byDuration) Len() int           { return len(x) }
-func (x byDuration) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
-func (x byDuration) Less(i, j int) bool { return x[i] < x[j] }
-
 // SetGCPercent sets the garbage collection target percentage:
 // a collection is triggered when the ratio of freshly allocated data
 // to live data remaining after the previous collection reaches this percentage.
diff --git a/src/runtime/debug/garbage_test.go b/src/runtime/debug/garbage_test.go
index 6ec94aa..04e954b 100644
--- a/src/runtime/debug/garbage_test.go
+++ b/src/runtime/debug/garbage_test.go
@@ -80,7 +80,7 @@ func TestReadGCStats(t *testing.T) {
 	for i := 0; i < n; i++ {
 		dt := stats.PauseEnd[i]
 		if dt.UnixNano() != int64(mstats.PauseEnd[off]) {
-			t.Errorf("stats.PauseEnd[%d] = %d, want %d", i, dt, mstats.PauseEnd[off])
+			t.Errorf("stats.PauseEnd[%d] = %d, want %d", i, dt.UnixNano(), mstats.PauseEnd[off])
 		}
 		off = (off + len(mstats.PauseEnd) - 1) % len(mstats.PauseEnd)
 	}
@@ -89,10 +89,6 @@ func TestReadGCStats(t *testing.T) {
 var big = make([]byte, 1<<20)
 
 func TestFreeOSMemory(t *testing.T) {
-	if runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "mips64" || runtime.GOARCH == "mips64le" ||
-		runtime.GOOS == "nacl" {
-		t.Skip("issue 9993; scavenger temporarily disabled on systems with physical pages larger than logical pages")
-	}
 	var ms1, ms2 runtime.MemStats
 
 	if big == nil {
@@ -118,3 +114,16 @@ func TestSetGCPercent(t *testing.T) {
 		t.Errorf("SetGCPercent(123); SetGCPercent(x) = %d, want 123", new)
 	}
 }
+
+func TestSetMaxThreadsOvf(t *testing.T) {
+	// Verify that a big threads count will not overflow the int32
+	// maxmcount variable, causing a panic (see Issue 16076).
+	//
+	// This can only happen when ints are 64 bits, since on platforms
+	// with 32 bit ints SetMaxThreads (which takes an int parameter)
+	// cannot be given anything that will overflow an int32.
+	//
+	// Call SetMaxThreads with 1<<31, but only on 64 bit systems.
+	nt := SetMaxThreads(1 << (30 + ^uint(0)>>63))
+	SetMaxThreads(nt) // restore previous value
+}
diff --git a/src/runtime/defs1_linux.go b/src/runtime/defs1_linux.go
index 87c6e02..e136d96 100644
--- a/src/runtime/defs1_linux.go
+++ b/src/runtime/defs1_linux.go
@@ -33,7 +33,7 @@ type Fpxreg1 C.struct__fpxreg
 type Xmmreg1 C.struct__xmmreg
 type Fpstate1 C.struct__fpstate
 type Fpreg1 C.struct__fpreg
-type SigaltstackT C.struct_sigaltstack
+type StackT C.stack_t
 type Mcontext C.mcontext_t
 type Ucontext C.ucontext_t
 type Sigcontext C.struct_sigcontext
diff --git a/src/runtime/defs1_netbsd_386.go b/src/runtime/defs1_netbsd_386.go
index f222bed..66f07ce 100644
--- a/src/runtime/defs1_netbsd_386.go
+++ b/src/runtime/defs1_netbsd_386.go
@@ -83,12 +83,6 @@ const (
 	_EVFILT_WRITE = 0x1
 )
 
-type sigaltstackt struct {
-	ss_sp    uintptr
-	ss_size  uintptr
-	ss_flags int32
-}
-
 type sigset struct {
 	__bits [4]uint32
 }
diff --git a/src/runtime/defs1_netbsd_amd64.go b/src/runtime/defs1_netbsd_amd64.go
index c2bde4d..9e31471 100644
--- a/src/runtime/defs1_netbsd_amd64.go
+++ b/src/runtime/defs1_netbsd_amd64.go
@@ -83,13 +83,6 @@ const (
 	_EVFILT_WRITE = 0x1
 )
 
-type sigaltstackt struct {
-	ss_sp     uintptr
-	ss_size   uintptr
-	ss_flags  int32
-	pad_cgo_0 [4]byte
-}
-
 type sigset struct {
 	__bits [4]uint32
 }
diff --git a/src/runtime/defs1_netbsd_arm.go b/src/runtime/defs1_netbsd_arm.go
index c976351..db8e4c6 100644
--- a/src/runtime/defs1_netbsd_arm.go
+++ b/src/runtime/defs1_netbsd_arm.go
@@ -83,12 +83,6 @@ const (
 	_EVFILT_WRITE = 0x1
 )
 
-type sigaltstackt struct {
-	ss_sp    uintptr
-	ss_size  uintptr
-	ss_flags int32
-}
-
 type sigset struct {
 	__bits [4]uint32
 }
@@ -110,6 +104,7 @@ type stackt struct {
 type timespec struct {
 	tv_sec  int64
 	tv_nsec int32
+	_       [4]byte // EABI
 }
 
 func (ts *timespec) set_sec(x int32) {
@@ -123,6 +118,7 @@ func (ts *timespec) set_nsec(x int32) {
 type timeval struct {
 	tv_sec  int64
 	tv_usec int32
+	_       [4]byte // EABI
 }
 
 func (tv *timeval) set_usec(x int32) {
@@ -135,10 +131,11 @@ type itimerval struct {
 }
 
 type mcontextt struct {
-	__gregs [17]uint32
-	__fpu   [4 + 8*32 + 4]byte // EABI
-	// __fpu [4+4*33+4]byte // not EABI
+	__gregs     [17]uint32
+	_           [4]byte   // EABI
+	__fpu       [272]byte // EABI
 	_mc_tlsbase uint32
+	_           [4]byte // EABI
 }
 
 type ucontextt struct {
@@ -146,6 +143,7 @@ type ucontextt struct {
 	uc_link     *ucontextt
 	uc_sigmask  sigset
 	uc_stack    stackt
+	_           [4]byte // EABI
 	uc_mcontext mcontextt
 	__uc_pad    [2]int32
 }
@@ -157,6 +155,7 @@ type keventt struct {
 	fflags uint32
 	data   int64
 	udata  *byte
+	_      [4]byte // EABI
 }
 
 // created by cgo -cdefs and then converted to Go
diff --git a/src/runtime/defs1_solaris_amd64.go b/src/runtime/defs1_solaris_amd64.go
index 3bb6f69..5ee3c3f 100644
--- a/src/runtime/defs1_solaris_amd64.go
+++ b/src/runtime/defs1_solaris_amd64.go
@@ -78,6 +78,7 @@ const (
 	_ITIMER_VIRTUAL = 0x1
 	_ITIMER_PROF    = 0x2
 
+	__SC_PAGESIZE         = 0xb
 	__SC_NPROCESSORS_ONLN = 0xf
 
 	_PTHREAD_CREATE_DETACHED = 0x40
@@ -109,20 +110,13 @@ type semt struct {
 	sem_pad2  [2]uint64
 }
 
-type sigaltstackt struct {
-	ss_sp     *byte
-	ss_size   uint64
-	ss_flags  int32
-	pad_cgo_0 [4]byte
-}
-
 type sigset struct {
 	__sigbits [4]uint32
 }
 
 type stackt struct {
 	ss_sp     *byte
-	ss_size   uint64
+	ss_size   uintptr
 	ss_flags  int32
 	pad_cgo_0 [4]byte
 }
diff --git a/src/runtime/defs2_linux.go b/src/runtime/defs2_linux.go
index 9dea6a1..c10dfb8 100644
--- a/src/runtime/defs2_linux.go
+++ b/src/runtime/defs2_linux.go
@@ -139,7 +139,7 @@ type Timespec C.struct_timespec
 type Timeval C.struct_timeval
 type Sigaction C.struct_kernel_sigaction
 type Siginfo C.siginfo_t
-type SigaltstackT C.struct_sigaltstack
+type StackT C.stack_t
 type Sigcontext C.struct_sigcontext
 type Ucontext C.struct_ucontext
 type Itimerval C.struct_itimerval
diff --git a/src/runtime/defs3_linux.go b/src/runtime/defs3_linux.go
index 489c130..6aa3ee4 100644
--- a/src/runtime/defs3_linux.go
+++ b/src/runtime/defs3_linux.go
@@ -35,7 +35,7 @@ type Gregset C.elf_gregset_t
 type FPregset C.elf_fpregset_t
 type Vreg C.elf_vrreg_t
 
-type SigaltstackT C.struct_sigaltstack
+type StackT C.stack_t
 
 // PPC64 uses sigcontext in place of mcontext in ucontext.
 // see http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/arch/powerpc/include/uapi/asm/ucontext.h
diff --git a/src/runtime/defs_arm_linux.go b/src/runtime/defs_arm_linux.go
index afd6897..e51dd32 100644
--- a/src/runtime/defs_arm_linux.go
+++ b/src/runtime/defs_arm_linux.go
@@ -115,7 +115,7 @@ const (
 )
 
 type Timespec C.struct_timespec
-type SigaltstackT C.struct_sigaltstack
+type StackT C.stack_t
 type Sigcontext C.struct_sigcontext
 type Ucontext C.struct_ucontext
 type Timeval C.struct_timeval
diff --git a/src/runtime/defs_dragonfly.go b/src/runtime/defs_dragonfly.go
index c5ebe75..ed00be0 100644
--- a/src/runtime/defs_dragonfly.go
+++ b/src/runtime/defs_dragonfly.go
@@ -109,7 +109,6 @@ const (
 
 type Rtprio C.struct_rtprio
 type Lwpparams C.struct_lwp_params
-type SigaltstackT C.struct_sigaltstack
 type Sigset C.struct___sigset
 type StackT C.stack_t
 
diff --git a/src/runtime/defs_dragonfly_amd64.go b/src/runtime/defs_dragonfly_amd64.go
index 3ac10b0..fc70103 100644
--- a/src/runtime/defs_dragonfly_amd64.go
+++ b/src/runtime/defs_dragonfly_amd64.go
@@ -99,13 +99,6 @@ type lwpparams struct {
 	tid2       unsafe.Pointer // *int32
 }
 
-type sigaltstackt struct {
-	ss_sp     uintptr
-	ss_size   uintptr
-	ss_flags  int32
-	pad_cgo_0 [4]byte
-}
-
 type sigset struct {
 	__bits [4]uint32
 }
diff --git a/src/runtime/defs_freebsd.go b/src/runtime/defs_freebsd.go
index 3f2184d..73422b7 100644
--- a/src/runtime/defs_freebsd.go
+++ b/src/runtime/defs_freebsd.go
@@ -120,7 +120,6 @@ const (
 
 type Rtprio C.struct_rtprio
 type ThrParam C.struct_thr_param
-type SigaltstackT C.struct_sigaltstack
 type Sigset C.struct___sigset
 type StackT C.stack_t
 
diff --git a/src/runtime/defs_freebsd_386.go b/src/runtime/defs_freebsd_386.go
index efcbeb7..0c05d71 100644
--- a/src/runtime/defs_freebsd_386.go
+++ b/src/runtime/defs_freebsd_386.go
@@ -111,12 +111,6 @@ type thrparam struct {
 	spare      [3]uintptr
 }
 
-type sigaltstackt struct {
-	ss_sp    *int8
-	ss_size  uint32
-	ss_flags int32
-}
-
 type sigset struct {
 	__bits [4]uint32
 }
diff --git a/src/runtime/defs_freebsd_amd64.go b/src/runtime/defs_freebsd_amd64.go
index 594f957..b416044 100644
--- a/src/runtime/defs_freebsd_amd64.go
+++ b/src/runtime/defs_freebsd_amd64.go
@@ -112,13 +112,6 @@ type thrparam struct {
 	spare      [3]uintptr
 }
 
-type sigaltstackt struct {
-	ss_sp     *int8
-	ss_size   uint64
-	ss_flags  int32
-	pad_cgo_0 [4]byte
-}
-
 type sigset struct {
 	__bits [4]uint32
 }
diff --git a/src/runtime/defs_freebsd_arm.go b/src/runtime/defs_freebsd_arm.go
index 0e9a2e9..8f85f17 100644
--- a/src/runtime/defs_freebsd_arm.go
+++ b/src/runtime/defs_freebsd_arm.go
@@ -111,12 +111,6 @@ type thrparam struct {
 	spare      [3]uintptr
 }
 
-type sigaltstackt struct {
-	ss_sp    *uint8
-	ss_size  uint32
-	ss_flags int32
-}
-
 type sigset struct {
 	__bits [4]uint32
 }
diff --git a/src/runtime/defs_linux_386.go b/src/runtime/defs_linux_386.go
index 44d2fd1..a7e435f 100644
--- a/src/runtime/defs_linux_386.go
+++ b/src/runtime/defs_linux_386.go
@@ -168,7 +168,7 @@ type siginfo struct {
 	si_addr uint32
 }
 
-type sigaltstackt struct {
+type stackt struct {
 	ss_sp    *byte
 	ss_flags int32
 	ss_size  uintptr
@@ -208,7 +208,7 @@ type sigcontext struct {
 type ucontext struct {
 	uc_flags    uint32
 	uc_link     *ucontext
-	uc_stack    sigaltstackt
+	uc_stack    stackt
 	uc_mcontext sigcontext
 	uc_sigmask  uint32
 }
diff --git a/src/runtime/defs_linux_amd64.go b/src/runtime/defs_linux_amd64.go
index 1936285..e8c6a21 100644
--- a/src/runtime/defs_linux_amd64.go
+++ b/src/runtime/defs_linux_amd64.go
@@ -205,7 +205,7 @@ type fpreg1 struct {
 	exponent    uint16
 }
 
-type sigaltstackt struct {
+type stackt struct {
 	ss_sp     *byte
 	ss_flags  int32
 	pad_cgo_0 [4]byte
@@ -221,7 +221,7 @@ type mcontext struct {
 type ucontext struct {
 	uc_flags     uint64
 	uc_link      *ucontext
-	uc_stack     sigaltstackt
+	uc_stack     stackt
 	uc_mcontext  mcontext
 	uc_sigmask   usigset
 	__fpregs_mem fpstate
diff --git a/src/runtime/defs_linux_arm.go b/src/runtime/defs_linux_arm.go
index b68b964..62ec8fa 100644
--- a/src/runtime/defs_linux_arm.go
+++ b/src/runtime/defs_linux_arm.go
@@ -101,7 +101,7 @@ func (ts *timespec) set_nsec(x int32) {
 	ts.tv_nsec = x
 }
 
-type sigaltstackt struct {
+type stackt struct {
 	ss_sp    *byte
 	ss_flags int32
 	ss_size  uintptr
@@ -134,7 +134,7 @@ type sigcontext struct {
 type ucontext struct {
 	uc_flags    uint32
 	uc_link     *ucontext
-	uc_stack    sigaltstackt
+	uc_stack    stackt
 	uc_mcontext sigcontext
 	uc_sigmask  uint32
 	__unused    [31]int32
diff --git a/src/runtime/defs_linux_arm64.go b/src/runtime/defs_linux_arm64.go
index d1b1a36..c295bc0 100644
--- a/src/runtime/defs_linux_arm64.go
+++ b/src/runtime/defs_linux_arm64.go
@@ -153,7 +153,7 @@ type usigset struct {
 	__val [16]uint64
 }
 
-type sigaltstackt struct {
+type stackt struct {
 	ss_sp     *byte
 	ss_flags  int32
 	pad_cgo_0 [4]byte
@@ -179,7 +179,7 @@ type sockaddr_un struct {
 type ucontext struct {
 	uc_flags    uint64
 	uc_link     *ucontext
-	uc_stack    sigaltstackt
+	uc_stack    stackt
 	uc_sigmask  uint64
 	_pad        [(1024 - 64) / 8]byte
 	_pad2       [8]byte // sigcontext must be aligned to 16-byte
diff --git a/src/runtime/defs_linux_mips64x.go b/src/runtime/defs_linux_mips64x.go
index bb3cd98..df11cb0 100644
--- a/src/runtime/defs_linux_mips64x.go
+++ b/src/runtime/defs_linux_mips64x.go
@@ -150,7 +150,7 @@ const (
 	_SA_RESTORER = 0
 )
 
-type sigaltstackt struct {
+type stackt struct {
 	ss_sp    *byte
 	ss_size  uintptr
 	ss_flags int32
@@ -177,7 +177,7 @@ type sigcontext struct {
 type ucontext struct {
 	uc_flags    uint64
 	uc_link     *ucontext
-	uc_stack    sigaltstackt
+	uc_stack    stackt
 	uc_mcontext sigcontext
 	uc_sigmask  uint64
 }
diff --git a/src/runtime/defs_linux_mipsx.go b/src/runtime/defs_linux_mipsx.go
new file mode 100644
index 0000000..702fbb5
--- /dev/null
+++ b/src/runtime/defs_linux_mipsx.go
@@ -0,0 +1,188 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+// +build linux
+
+package runtime
+
+const (
+	_EINTR  = 0x4
+	_EAGAIN = 0xb
+	_ENOMEM = 0xc
+
+	_PROT_NONE  = 0x0
+	_PROT_READ  = 0x1
+	_PROT_WRITE = 0x2
+	_PROT_EXEC  = 0x4
+
+	_MAP_ANON    = 0x800
+	_MAP_PRIVATE = 0x2
+	_MAP_FIXED   = 0x10
+
+	_MADV_DONTNEED   = 0x4
+	_MADV_HUGEPAGE   = 0xe
+	_MADV_NOHUGEPAGE = 0xf
+
+	_SA_RESTART = 0x10000000
+	_SA_ONSTACK = 0x8000000
+	_SA_SIGINFO = 0x8
+
+	_SIGHUP    = 0x1
+	_SIGINT    = 0x2
+	_SIGQUIT   = 0x3
+	_SIGILL    = 0x4
+	_SIGTRAP   = 0x5
+	_SIGABRT   = 0x6
+	_SIGEMT    = 0x7
+	_SIGFPE    = 0x8
+	_SIGKILL   = 0x9
+	_SIGBUS    = 0xa
+	_SIGSEGV   = 0xb
+	_SIGSYS    = 0xc
+	_SIGPIPE   = 0xd
+	_SIGALRM   = 0xe
+	_SIGUSR1   = 0x10
+	_SIGUSR2   = 0x11
+	_SIGCHLD   = 0x12
+	_SIGPWR    = 0x13
+	_SIGWINCH  = 0x14
+	_SIGURG    = 0x15
+	_SIGIO     = 0x16
+	_SIGSTOP   = 0x17
+	_SIGTSTP   = 0x18
+	_SIGCONT   = 0x19
+	_SIGTTIN   = 0x1a
+	_SIGTTOU   = 0x1b
+	_SIGVTALRM = 0x1c
+	_SIGPROF   = 0x1d
+	_SIGXCPU   = 0x1e
+	_SIGXFSZ   = 0x1f
+
+	_FPE_INTDIV = 0x1
+	_FPE_INTOVF = 0x2
+	_FPE_FLTDIV = 0x3
+	_FPE_FLTOVF = 0x4
+	_FPE_FLTUND = 0x5
+	_FPE_FLTRES = 0x6
+	_FPE_FLTINV = 0x7
+	_FPE_FLTSUB = 0x8
+
+	_BUS_ADRALN = 0x1
+	_BUS_ADRERR = 0x2
+	_BUS_OBJERR = 0x3
+
+	_SEGV_MAPERR = 0x1
+	_SEGV_ACCERR = 0x2
+
+	_ITIMER_REAL    = 0x0
+	_ITIMER_VIRTUAL = 0x1
+	_ITIMER_PROF    = 0x2
+
+	_EPOLLIN       = 0x1
+	_EPOLLOUT      = 0x4
+	_EPOLLERR      = 0x8
+	_EPOLLHUP      = 0x10
+	_EPOLLRDHUP    = 0x2000
+	_EPOLLET       = 0x80000000
+	_EPOLL_CLOEXEC = 0x80000
+	_EPOLL_CTL_ADD = 0x1
+	_EPOLL_CTL_DEL = 0x2
+	_EPOLL_CTL_MOD = 0x3
+)
+
+type timespec struct {
+	tv_sec  int32
+	tv_nsec int32
+}
+
+//go:nosplit
+func (ts *timespec) set_sec(x int64) {
+	ts.tv_sec = int32(x)
+}
+
+//go:nosplit
+func (ts *timespec) set_nsec(x int32) {
+	ts.tv_nsec = x
+}
+
+type timeval struct {
+	tv_sec  int32
+	tv_usec int32
+}
+
+//go:nosplit
+func (tv *timeval) set_usec(x int32) {
+	tv.tv_usec = x
+}
+
+type sigactiont struct {
+	sa_flags   uint32
+	sa_handler uintptr
+	sa_mask    [4]uint32
+	// linux header does not have sa_restorer field,
+	// but it is used in setsig(). it is no harm to put it here
+	sa_restorer uintptr
+}
+
+type siginfo struct {
+	si_signo int32
+	si_code  int32
+	si_errno int32
+	// below here is a union; si_addr is the only field we use
+	si_addr uint32
+}
+
+type itimerval struct {
+	it_interval timeval
+	it_value    timeval
+}
+
+type epollevent struct {
+	events    uint32
+	pad_cgo_0 [4]byte
+	data      uint64
+}
+
+const (
+	_O_RDONLY    = 0x0
+	_O_CLOEXEC   = 0x80000
+	_SA_RESTORER = 0
+)
+
+type stackt struct {
+	ss_sp    *byte
+	ss_size  uintptr
+	ss_flags int32
+}
+
+type sigcontext struct {
+	sc_regmask   uint32
+	sc_status    uint32
+	sc_pc        uint64
+	sc_regs      [32]uint64
+	sc_fpregs    [32]uint64
+	sc_acx       uint32
+	sc_fpc_csr   uint32
+	sc_fpc_eir   uint32
+	sc_used_math uint32
+	sc_dsp       uint32
+	sc_mdhi      uint64
+	sc_mdlo      uint64
+	sc_hi1       uint32
+	sc_lo1       uint32
+	sc_hi2       uint32
+	sc_lo2       uint32
+	sc_hi3       uint32
+	sc_lo3       uint32
+}
+
+type ucontext struct {
+	uc_flags    uint32
+	uc_link     *ucontext
+	uc_stack    stackt
+	Pad_cgo_0   [4]byte
+	uc_mcontext sigcontext
+	uc_sigmask  [4]uint32
+}
diff --git a/src/runtime/defs_linux_ppc64.go b/src/runtime/defs_linux_ppc64.go
index 317a764..45363d1 100644
--- a/src/runtime/defs_linux_ppc64.go
+++ b/src/runtime/defs_linux_ppc64.go
@@ -170,7 +170,7 @@ type vreg struct {
 	u [4]uint32
 }
 
-type sigaltstackt struct {
+type stackt struct {
 	ss_sp     *byte
 	ss_flags  int32
 	pad_cgo_0 [4]byte
@@ -193,7 +193,7 @@ type sigcontext struct {
 type ucontext struct {
 	uc_flags    uint64
 	uc_link     *ucontext
-	uc_stack    sigaltstackt
+	uc_stack    stackt
 	uc_sigmask  uint64
 	__unused    [15]uint64
 	uc_mcontext sigcontext
diff --git a/src/runtime/defs_linux_ppc64le.go b/src/runtime/defs_linux_ppc64le.go
index 317a764..45363d1 100644
--- a/src/runtime/defs_linux_ppc64le.go
+++ b/src/runtime/defs_linux_ppc64le.go
@@ -170,7 +170,7 @@ type vreg struct {
 	u [4]uint32
 }
 
-type sigaltstackt struct {
+type stackt struct {
 	ss_sp     *byte
 	ss_flags  int32
 	pad_cgo_0 [4]byte
@@ -193,7 +193,7 @@ type sigcontext struct {
 type ucontext struct {
 	uc_flags    uint64
 	uc_link     *ucontext
-	uc_stack    sigaltstackt
+	uc_stack    stackt
 	uc_sigmask  uint64
 	__unused    [15]uint64
 	uc_mcontext sigcontext
diff --git a/src/runtime/defs_linux_s390x.go b/src/runtime/defs_linux_s390x.go
index 5f55d5a..ab90723 100644
--- a/src/runtime/defs_linux_s390x.go
+++ b/src/runtime/defs_linux_s390x.go
@@ -143,7 +143,7 @@ const (
 	_SA_RESTORER = 0
 )
 
-type sigaltstackt struct {
+type stackt struct {
 	ss_sp    *byte
 	ss_flags int32
 	ss_size  uintptr
@@ -161,7 +161,7 @@ type sigcontext struct {
 type ucontext struct {
 	uc_flags    uint64
 	uc_link     *ucontext
-	uc_stack    sigaltstackt
+	uc_stack    stackt
 	uc_mcontext sigcontext
 	uc_sigmask  uint64
 }
diff --git a/src/runtime/defs_netbsd.go b/src/runtime/defs_netbsd.go
index b27949e..56db1f0 100644
--- a/src/runtime/defs_netbsd.go
+++ b/src/runtime/defs_netbsd.go
@@ -109,7 +109,6 @@ const (
 	EVFILT_WRITE = C.EVFILT_WRITE
 )
 
-type SigaltstackT C.struct_sigaltstack
 type Sigset C.sigset_t
 type Siginfo C.struct__ksiginfo
 
diff --git a/src/runtime/defs_openbsd.go b/src/runtime/defs_openbsd.go
index 39224c9..7e72150 100644
--- a/src/runtime/defs_openbsd.go
+++ b/src/runtime/defs_openbsd.go
@@ -106,7 +106,6 @@ const (
 
 type TforkT C.struct___tfork
 
-type SigaltstackT C.struct_sigaltstack
 type Sigcontext C.struct_sigcontext
 type Siginfo C.siginfo_t
 type Sigset C.sigset_t
diff --git a/src/runtime/defs_openbsd_386.go b/src/runtime/defs_openbsd_386.go
index 4b60158..ce08111 100644
--- a/src/runtime/defs_openbsd_386.go
+++ b/src/runtime/defs_openbsd_386.go
@@ -90,12 +90,6 @@ type tforkt struct {
 	tf_stack uintptr
 }
 
-type sigaltstackt struct {
-	ss_sp    uintptr
-	ss_size  uintptr
-	ss_flags int32
-}
-
 type sigcontext struct {
 	sc_gs       uint32
 	sc_fs       uint32
diff --git a/src/runtime/defs_openbsd_amd64.go b/src/runtime/defs_openbsd_amd64.go
index 3c27c91..ea07098 100644
--- a/src/runtime/defs_openbsd_amd64.go
+++ b/src/runtime/defs_openbsd_amd64.go
@@ -90,13 +90,6 @@ type tforkt struct {
 	tf_stack uintptr
 }
 
-type sigaltstackt struct {
-	ss_sp     uintptr
-	ss_size   uintptr
-	ss_flags  int32
-	pad_cgo_0 [4]byte
-}
-
 type sigcontext struct {
 	sc_rdi      uint64
 	sc_rsi      uint64
diff --git a/src/runtime/defs_openbsd_arm.go b/src/runtime/defs_openbsd_arm.go
index aab9276..b0fb639 100644
--- a/src/runtime/defs_openbsd_arm.go
+++ b/src/runtime/defs_openbsd_arm.go
@@ -90,12 +90,6 @@ type tforkt struct {
 	tf_stack uintptr
 }
 
-type sigaltstackt struct {
-	ss_sp    uintptr
-	ss_size  uintptr
-	ss_flags int32
-}
-
 type sigcontext struct {
 	__sc_unused int32
 	sc_mask     int32
diff --git a/src/runtime/defs_plan9_386.go b/src/runtime/defs_plan9_386.go
index 54ace48..220169d 100644
--- a/src/runtime/defs_plan9_386.go
+++ b/src/runtime/defs_plan9_386.go
@@ -28,7 +28,10 @@ type sigctxt struct {
 	u *ureg
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) pc() uintptr { return uintptr(c.u.pc) }
+
 func (c *sigctxt) sp() uintptr { return uintptr(c.u.sp) }
 func (c *sigctxt) lr() uintptr { return uintptr(0) }
 
diff --git a/src/runtime/defs_plan9_amd64.go b/src/runtime/defs_plan9_amd64.go
index 1633ec1..29a2643 100644
--- a/src/runtime/defs_plan9_amd64.go
+++ b/src/runtime/defs_plan9_amd64.go
@@ -37,7 +37,10 @@ type sigctxt struct {
 	u *ureg
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) pc() uintptr { return uintptr(c.u.ip) }
+
 func (c *sigctxt) sp() uintptr { return uintptr(c.u.sp) }
 func (c *sigctxt) lr() uintptr { return uintptr(0) }
 
diff --git a/src/runtime/defs_plan9_arm.go b/src/runtime/defs_plan9_arm.go
index 9c700ae..1adc16e 100644
--- a/src/runtime/defs_plan9_arm.go
+++ b/src/runtime/defs_plan9_arm.go
@@ -31,7 +31,10 @@ type sigctxt struct {
 	u *ureg
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) pc() uintptr { return uintptr(c.u.pc) }
+
 func (c *sigctxt) sp() uintptr { return uintptr(c.u.sp) }
 func (c *sigctxt) lr() uintptr { return uintptr(c.u.link) }
 
diff --git a/src/runtime/defs_solaris.go b/src/runtime/defs_solaris.go
index ba44e5f..0638e0b 100644
--- a/src/runtime/defs_solaris.go
+++ b/src/runtime/defs_solaris.go
@@ -133,7 +133,6 @@ const (
 
 type SemT C.sem_t
 
-type SigaltstackT C.struct_sigaltstack
 type Sigset C.sigset_t
 type StackT C.stack_t
 
diff --git a/src/runtime/duff_arm64.s b/src/runtime/duff_arm64.s
index 6d4bb15..5a147fa 100644
--- a/src/runtime/duff_arm64.s
+++ b/src/runtime/duff_arm64.s
@@ -135,4 +135,389 @@ TEXT runtime·duffzero(SB), NOSPLIT, $-8-0
 	MOVD.W	ZR, 8(R16)
 	RET
 
-// TODO: Implement runtime·duffcopy.
+TEXT runtime·duffcopy(SB), NOSPLIT, $0-0
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	MOVD.P	8(R16), R27
+	MOVD.P	R27, 8(R17)
+
+	RET
diff --git a/src/runtime/export_mmap_test.go b/src/runtime/export_mmap_test.go
index bc8191e..f569627 100644
--- a/src/runtime/export_mmap_test.go
+++ b/src/runtime/export_mmap_test.go
@@ -9,7 +9,13 @@
 package runtime
 
 var Mmap = mmap
+var Munmap = munmap
 
 const ENOMEM = _ENOMEM
 const MAP_ANON = _MAP_ANON
 const MAP_PRIVATE = _MAP_PRIVATE
+const MAP_FIXED = _MAP_FIXED
+
+func GetPhysPageSize() uintptr {
+	return physPageSize
+}
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index 199a049..9b76555 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -32,6 +32,9 @@ var FuncPC = funcPC
 
 var Fastlog2 = fastlog2
 
+var Atoi = atoi
+var Atoi32 = atoi32
+
 type LFNode struct {
 	Next    uint64
 	Pushcnt uintptr
@@ -155,7 +158,11 @@ var Int32Hash = int32Hash
 var Int64Hash = int64Hash
 var EfaceHash = efaceHash
 var IfaceHash = ifaceHash
-var MemclrBytes = memclrBytes
+
+func MemclrBytes(b []byte) {
+	s := (*slice)(unsafe.Pointer(&b))
+	memclrNoHeapPointers(s.array, uintptr(s.len))
+}
 
 var HashLoad = &hashLoad
 
@@ -167,9 +174,6 @@ func GostringW(w []uint16) (s string) {
 	return
 }
 
-var Gostringnocopy = gostringnocopy
-var Maxstring = &maxstring
-
 type Uintreg sys.Uintreg
 
 var Open = open
@@ -213,9 +217,6 @@ func BenchSetType(n int, x interface{}) {
 
 const PtrSize = sys.PtrSize
 
-var TestingAssertE2I2GC = &testingAssertE2I2GC
-var TestingAssertE2T2GC = &testingAssertE2T2GC
-
 var ForceGCPeriod = &forcegcperiod
 
 // SetTracebackEnv is like runtime/debug.SetTraceback, but it raises
@@ -234,7 +235,7 @@ func CountPagesInUse() (pagesInUse, counted uintptr) {
 
 	pagesInUse = uintptr(mheap_.pagesInUse)
 
-	for _, s := range h_allspans {
+	for _, s := range mheap_.allspans {
 		if s.state == mSpanInUse {
 			counted += s.npages
 		}
diff --git a/src/runtime/extern.go b/src/runtime/extern.go
index 441dcd9..1b53367 100644
--- a/src/runtime/extern.go
+++ b/src/runtime/extern.go
@@ -57,6 +57,11 @@ It is a comma-separated list of name=val pairs setting these named variables:
 	gcstackbarrierall: setting gcstackbarrierall=1 installs stack barriers
 	in every stack frame, rather than in exponentially-spaced frames.
 
+	gcrescanstacks: setting gcrescanstacks=1 enables stack
+	re-scanning during the STW mark termination phase. This is
+	helpful for debugging if objects are being prematurely
+	garbage collected.
+
 	gcstoptheworld: setting gcstoptheworld=1 disables concurrent garbage collection,
 	making every garbage collection a stop-the-world event. Setting gcstoptheworld=2
 	also disables concurrent sweeping after the garbage collection finishes.
diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go
index d53d3ee..4a32f15 100644
--- a/src/runtime/gc_test.go
+++ b/src/runtime/gc_test.go
@@ -5,7 +5,6 @@
 package runtime_test
 
 import (
-	"io"
 	"os"
 	"reflect"
 	"runtime"
@@ -399,37 +398,6 @@ func TestPrintGC(t *testing.T) {
 	close(done)
 }
 
-// The implicit y, ok := x.(error) for the case error
-// in testTypeSwitch used to not initialize the result y
-// before passing &y to assertE2I2GC.
-// Catch this by making assertE2I2 call runtime.GC,
-// which will force a stack scan and failure if there are
-// bad pointers, and then fill the stack with bad pointers
-// and run the type switch.
-func TestAssertE2I2Liveness(t *testing.T) {
-	// Note that this flag is defined in export_test.go
-	// and is not available to ordinary imports of runtime.
-	*runtime.TestingAssertE2I2GC = true
-	defer func() {
-		*runtime.TestingAssertE2I2GC = false
-	}()
-
-	poisonStack()
-	testTypeSwitch(io.EOF)
-	poisonStack()
-	testAssert(io.EOF)
-	poisonStack()
-	testAssertVar(io.EOF)
-}
-
-func poisonStack() uintptr {
-	var x [1000]uintptr
-	for i := range x {
-		x[i] = 0xff
-	}
-	return x[123]
-}
-
 func testTypeSwitch(x interface{}) error {
 	switch y := x.(type) {
 	case nil:
@@ -455,16 +423,6 @@ func testAssertVar(x interface{}) error {
 	return nil
 }
 
-func TestAssertE2T2Liveness(t *testing.T) {
-	*runtime.TestingAssertE2T2GC = true
-	defer func() {
-		*runtime.TestingAssertE2T2GC = false
-	}()
-
-	poisonStack()
-	testIfaceEqual(io.EOF)
-}
-
 var a bool
 
 //go:noinline
diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go
index 011f005..14f514f 100644
--- a/src/runtime/gcinfo_test.go
+++ b/src/runtime/gcinfo_test.go
@@ -139,7 +139,7 @@ type BigStruct struct {
 
 func infoBigStruct() []byte {
 	switch runtime.GOARCH {
-	case "386", "arm":
+	case "386", "arm", "mips", "mipsle":
 		return []byte{
 			typePointer,                                                // q *int
 			typeScalar, typeScalar, typeScalar, typeScalar, typeScalar, // w byte; e [17]byte
diff --git a/src/runtime/hash32.go b/src/runtime/hash32.go
index 2b7c5c0..be59076 100644
--- a/src/runtime/hash32.go
+++ b/src/runtime/hash32.go
@@ -6,7 +6,7 @@
 //   xxhash: https://code.google.com/p/xxhash/
 // cityhash: https://code.google.com/p/cityhash/
 
-// +build 386 arm
+// +build 386 arm mips mipsle
 
 package runtime
 
diff --git a/src/runtime/hash_test.go b/src/runtime/hash_test.go
index 3108b3b..a6f3cdb 100644
--- a/src/runtime/hash_test.go
+++ b/src/runtime/hash_test.go
@@ -683,6 +683,9 @@ func BenchmarkUnalignedLoad(b *testing.B) {
 }
 
 func TestCollisions(t *testing.T) {
+	if testing.Short() {
+		t.Skip("Skipping in short mode")
+	}
 	for i := 0; i < 16; i++ {
 		for j := 0; j < 16; j++ {
 			if j == i {
diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go
index 509cab2..086d374 100644
--- a/src/runtime/hashmap.go
+++ b/src/runtime/hashmap.go
@@ -93,9 +93,10 @@ const (
 	minTopHash     = 4 // minimum tophash for a normal filled cell.
 
 	// flags
-	iterator    = 1 // there may be an iterator using buckets
-	oldIterator = 2 // there may be an iterator using oldbuckets
-	hashWriting = 4 // a goroutine is writing to the map
+	iterator     = 1 // there may be an iterator using buckets
+	oldIterator  = 2 // there may be an iterator using oldbuckets
+	hashWriting  = 4 // a goroutine is writing to the map
+	sameSizeGrow = 8 // the current map growth is to a new map of the same size
 
 	// sentinel bucket ID for iterator checks
 	noCheck = 1<<(8*sys.PtrSize) - 1
@@ -105,10 +106,11 @@ const (
 type hmap struct {
 	// Note: the format of the Hmap is encoded in ../../cmd/internal/gc/reflect.go and
 	// ../reflect/type.go. Don't change this structure without also changing that code!
-	count int // # live cells == size of map.  Must be first (used by len() builtin)
-	flags uint8
-	B     uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
-	hash0 uint32 // hash seed
+	count     int // # live cells == size of map.  Must be first (used by len() builtin)
+	flags     uint8
+	B         uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
+	noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
+	hash0     uint32 // hash seed
 
 	buckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
 	oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
@@ -128,6 +130,9 @@ type hmap struct {
 
 // A bucket for a Go map.
 type bmap struct {
+	// tophash generally contains the top byte of the hash value
+	// for each key in this bucket. If tophash[0] < minTopHash,
+	// tophash[0] is a bucket evacuation state instead.
 	tophash [bucketCnt]uint8
 	// Followed by bucketCnt keys and then bucketCnt values.
 	// NOTE: packing all the keys together and then all the values together makes the
@@ -165,7 +170,34 @@ func (b *bmap) overflow(t *maptype) *bmap {
 	return *(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-sys.PtrSize))
 }
 
+// incrnoverflow increments h.noverflow.
+// noverflow counts the number of overflow buckets.
+// This is used to trigger same-size map growth.
+// See also tooManyOverflowBuckets.
+// To keep hmap small, noverflow is a uint16.
+// When there are few buckets, noverflow is an exact count.
+// When there are many buckets, noverflow is an approximate count.
+func (h *hmap) incrnoverflow() {
+	// We trigger same-size map growth if there are
+	// as many overflow buckets as buckets.
+	// We need to be able to count to 1<<h.B.
+	if h.B < 16 {
+		h.noverflow++
+		return
+	}
+	// Increment with probability 1/(1<<(h.B-15)).
+	// When we reach 1<<15 - 1, we will have approximately
+	// as many overflow buckets as buckets.
+	mask := uint32(1)<<(h.B-15) - 1
+	// Example: if h.B == 18, then mask == 7,
+	// and fastrand & 7 == 0 with probability 1/8.
+	if fastrand()&mask == 0 {
+		h.noverflow++
+	}
+}
+
 func (h *hmap) setoverflow(t *maptype, b, ovf *bmap) {
+	h.incrnoverflow()
 	if t.bucket.kind&kindNoPointers != 0 {
 		h.createOverflow()
 		*h.overflow[0] = append(*h.overflow[0], ovf)
@@ -238,7 +270,7 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
 
 	// find size parameter which will hold the requested # of elements
 	B := uint8(0)
-	for ; hint > bucketCnt && float32(hint) > loadFactor*float32(uintptr(1)<<B); B++ {
+	for ; overLoadFactor(hint, B); B++ {
 	}
 
 	// allocate initial hash table
@@ -256,10 +288,11 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
 	h.count = 0
 	h.B = B
 	h.flags = 0
-	h.hash0 = fastrand1()
+	h.hash0 = fastrand()
 	h.buckets = buckets
 	h.oldbuckets = nil
 	h.nevacuate = 0
+	h.noverflow = 0
 
 	return h
 }
@@ -290,7 +323,11 @@ func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
 	m := uintptr(1)<<h.B - 1
 	b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
 	if c := h.oldbuckets; c != nil {
-		oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
+		if !h.sameSizeGrow() {
+			// There used to be half as many buckets; mask down one more power of two.
+			m >>= 1
+		}
+		oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize)))
 		if !evacuated(oldb) {
 			b = oldb
 		}
@@ -344,7 +381,11 @@ func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool)
 	m := uintptr(1)<<h.B - 1
 	b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + (hash&m)*uintptr(t.bucketsize)))
 	if c := h.oldbuckets; c != nil {
-		oldb := (*bmap)(unsafe.Pointer(uintptr(c) + (hash&(m>>1))*uintptr(t.bucketsize)))
+		if !h.sameSizeGrow() {
+			// There used to be half as many buckets; mask down one more power of two.
+			m >>= 1
+		}
+		oldb := (*bmap)(unsafe.Pointer(uintptr(c) + (hash&m)*uintptr(t.bucketsize)))
 		if !evacuated(oldb) {
 			b = oldb
 		}
@@ -382,15 +423,16 @@ func mapaccessK(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, unsafe
 	if h == nil || h.count == 0 {
 		return nil, nil
 	}
-	if h.flags&hashWriting != 0 {
-		throw("concurrent map read and map write")
-	}
 	alg := t.key.alg
 	hash := alg.hash(key, uintptr(h.hash0))
 	m := uintptr(1)<<h.B - 1
 	b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + (hash&m)*uintptr(t.bucketsize)))
 	if c := h.oldbuckets; c != nil {
-		oldb := (*bmap)(unsafe.Pointer(uintptr(c) + (hash&(m>>1))*uintptr(t.bucketsize)))
+		if !h.sameSizeGrow() {
+			// There used to be half as many buckets; mask down one more power of two.
+			m >>= 1
+		}
+		oldb := (*bmap)(unsafe.Pointer(uintptr(c) + (hash&m)*uintptr(t.bucketsize)))
 		if !evacuated(oldb) {
 			b = oldb
 		}
@@ -439,20 +481,19 @@ func mapaccess2_fat(t *maptype, h *hmap, key, zero unsafe.Pointer) (unsafe.Point
 	return v, true
 }
 
-func mapassign1(t *maptype, h *hmap, key unsafe.Pointer, val unsafe.Pointer) {
+// Like mapaccess, but allocates a slot for the key if it is not present in the map.
+func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
 	if h == nil {
 		panic(plainError("assignment to entry in nil map"))
 	}
 	if raceenabled {
 		callerpc := getcallerpc(unsafe.Pointer(&t))
-		pc := funcPC(mapassign1)
+		pc := funcPC(mapassign)
 		racewritepc(unsafe.Pointer(h), callerpc, pc)
 		raceReadObjectPC(t.key, key, callerpc, pc)
-		raceReadObjectPC(t.elem, val, callerpc, pc)
 	}
 	if msanenabled {
 		msanread(key, t.key.size)
-		msanread(val, t.elem.size)
 	}
 	if h.flags&hashWriting != 0 {
 		throw("concurrent map writes")
@@ -468,7 +509,7 @@ func mapassign1(t *maptype, h *hmap, key unsafe.Pointer, val unsafe.Pointer) {
 
 again:
 	bucket := hash & (uintptr(1)<<h.B - 1)
-	if h.oldbuckets != nil {
+	if h.growing() {
 		growWork(t, h, bucket)
 	}
 	b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize)))
@@ -479,35 +520,29 @@ again:
 
 	var inserti *uint8
 	var insertk unsafe.Pointer
-	var insertv unsafe.Pointer
+	var val unsafe.Pointer
 	for {
 		for i := uintptr(0); i < bucketCnt; i++ {
 			if b.tophash[i] != top {
 				if b.tophash[i] == empty && inserti == nil {
 					inserti = &b.tophash[i]
 					insertk = add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
-					insertv = add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
+					val = add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
 				}
 				continue
 			}
 			k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
-			k2 := k
 			if t.indirectkey {
-				k2 = *((*unsafe.Pointer)(k2))
+				k = *((*unsafe.Pointer)(k))
 			}
-			if !alg.equal(key, k2) {
+			if !alg.equal(key, k) {
 				continue
 			}
 			// already have a mapping for key. Update it.
 			if t.needkeyupdate {
-				typedmemmove(t.key, k2, key)
+				typedmemmove(t.key, k, key)
 			}
-			v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
-			v2 := v
-			if t.indirectvalue {
-				v2 = *((*unsafe.Pointer)(v2))
-			}
-			typedmemmove(t.elem, v2, val)
+			val = add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
 			goto done
 		}
 		ovf := b.overflow(t)
@@ -517,8 +552,11 @@ again:
 		b = ovf
 	}
 
-	// did not find mapping for key. Allocate new cell & add entry.
-	if float32(h.count) >= loadFactor*float32((uintptr(1)<<h.B)) && h.count >= bucketCnt {
+	// Did not find mapping for key. Allocate new cell & add entry.
+
+	// If we hit the max load factor or we have too many overflow buckets,
+	// and we're not already in the middle of growing, start growing.
+	if !h.growing() && (overLoadFactor(int64(h.count), h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) {
 		hashGrow(t, h)
 		goto again // Growing the table invalidates everything, so try again
 	}
@@ -529,7 +567,7 @@ again:
 		h.setoverflow(t, b, newb)
 		inserti = &newb.tophash[0]
 		insertk = add(unsafe.Pointer(newb), dataOffset)
-		insertv = add(insertk, bucketCnt*uintptr(t.keysize))
+		val = add(insertk, bucketCnt*uintptr(t.keysize))
 	}
 
 	// store new key/value at insert position
@@ -540,11 +578,9 @@ again:
 	}
 	if t.indirectvalue {
 		vmem := newobject(t.elem)
-		*(*unsafe.Pointer)(insertv) = vmem
-		insertv = vmem
+		*(*unsafe.Pointer)(val) = vmem
 	}
 	typedmemmove(t.key, insertk, key)
-	typedmemmove(t.elem, insertv, val)
 	*inserti = top
 	h.count++
 
@@ -553,6 +589,10 @@ done:
 		throw("concurrent map writes")
 	}
 	h.flags &^= hashWriting
+	if t.indirectvalue {
+		val = *((*unsafe.Pointer)(val))
+	}
+	return val
 }
 
 func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
@@ -576,7 +616,7 @@ func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
 	alg := t.key.alg
 	hash := alg.hash(key, uintptr(h.hash0))
 	bucket := hash & (uintptr(1)<<h.B - 1)
-	if h.oldbuckets != nil {
+	if h.growing() {
 		growWork(t, h, bucket)
 	}
 	b := (*bmap)(unsafe.Pointer(uintptr(h.buckets) + bucket*uintptr(t.bucketsize)))
@@ -597,9 +637,17 @@ func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
 			if !alg.equal(key, k2) {
 				continue
 			}
-			memclr(k, uintptr(t.keysize))
+			if t.indirectkey {
+				*(*unsafe.Pointer)(k) = nil
+			} else {
+				typedmemclr(t.key, k)
+			}
 			v := unsafe.Pointer(uintptr(unsafe.Pointer(b)) + dataOffset + bucketCnt*uintptr(t.keysize) + i*uintptr(t.valuesize))
-			memclr(v, uintptr(t.valuesize))
+			if t.indirectvalue {
+				*(*unsafe.Pointer)(v) = nil
+			} else {
+				typedmemclr(t.elem, v)
+			}
 			b.tophash[i] = empty
 			h.count--
 			goto done
@@ -658,9 +706,9 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) {
 	}
 
 	// decide where to start
-	r := uintptr(fastrand1())
+	r := uintptr(fastrand())
 	if h.B > 31-bucketCntBits {
-		r += uintptr(fastrand1()) << 31
+		r += uintptr(fastrand()) << 31
 	}
 	it.startBucket = r & (uintptr(1)<<h.B - 1)
 	it.offset = uint8(r >> h.B & (bucketCnt - 1))
@@ -685,6 +733,9 @@ func mapiternext(it *hiter) {
 		callerpc := getcallerpc(unsafe.Pointer(&it))
 		racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapiternext))
 	}
+	if h.flags&hashWriting != 0 {
+		throw("concurrent map iteration and map write")
+	}
 	t := it.t
 	bucket := it.bucket
 	b := it.bptr
@@ -700,12 +751,12 @@ next:
 			it.value = nil
 			return
 		}
-		if h.oldbuckets != nil && it.B == h.B {
+		if h.growing() && it.B == h.B {
 			// Iterator was started in the middle of a grow, and the grow isn't done yet.
 			// If the bucket we're looking at hasn't been filled in yet (i.e. the old
 			// bucket hasn't been evacuated) then we need to iterate through the old
 			// bucket and only return the ones that will be migrated to this bucket.
-			oldbucket := bucket & (uintptr(1)<<(it.B-1) - 1)
+			oldbucket := bucket & it.h.oldbucketmask()
 			b = (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)))
 			if !evacuated(b) {
 				checkBucket = bucket
@@ -729,9 +780,9 @@ next:
 		k := add(unsafe.Pointer(b), dataOffset+uintptr(offi)*uintptr(t.keysize))
 		v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+uintptr(offi)*uintptr(t.valuesize))
 		if b.tophash[offi] != empty && b.tophash[offi] != evacuatedEmpty {
-			if checkBucket != noCheck {
-				// Special case: iterator was started during a grow and the
-				// grow is not done yet. We're working on a bucket whose
+			if checkBucket != noCheck && !h.sameSizeGrow() {
+				// Special case: iterator was started during a grow to a larger size
+				// and the grow is not done yet. We're working on a bucket whose
 				// oldbucket has not been evacuated yet. Or at least, it wasn't
 				// evacuated when we started the bucket. So we're iterating
 				// through the oldbucket, skipping any keys that will go
@@ -817,21 +868,27 @@ next:
 }
 
 func hashGrow(t *maptype, h *hmap) {
-	if h.oldbuckets != nil {
-		throw("evacuation not done in time")
+	// If we've hit the load factor, get bigger.
+	// Otherwise, there are too many overflow buckets,
+	// so keep the same number of buckets and "grow" laterally.
+	bigger := uint8(1)
+	if !overLoadFactor(int64(h.count), h.B) {
+		bigger = 0
+		h.flags |= sameSizeGrow
 	}
 	oldbuckets := h.buckets
-	newbuckets := newarray(t.bucket, 1<<(h.B+1))
+	newbuckets := newarray(t.bucket, 1<<(h.B+bigger))
 	flags := h.flags &^ (iterator | oldIterator)
 	if h.flags&iterator != 0 {
 		flags |= oldIterator
 	}
 	// commit the grow (atomic wrt gc)
-	h.B++
+	h.B += bigger
 	h.flags = flags
 	h.oldbuckets = oldbuckets
 	h.buckets = newbuckets
 	h.nevacuate = 0
+	h.noverflow = 0
 
 	if h.overflow != nil {
 		// Promote current overflow buckets to the old generation.
@@ -846,35 +903,87 @@ func hashGrow(t *maptype, h *hmap) {
 	// by growWork() and evacuate().
 }
 
-func growWork(t *maptype, h *hmap, bucket uintptr) {
-	noldbuckets := uintptr(1) << (h.B - 1)
+// overLoadFactor reports whether count items placed in 1<<B buckets is over loadFactor.
+func overLoadFactor(count int64, B uint8) bool {
+	// TODO: rewrite to use integer math and comparison?
+	return count >= bucketCnt && float32(count) >= loadFactor*float32((uintptr(1)<<B))
+}
+
+// tooManyOverflowBuckets reports whether noverflow buckets is too many for a map with 1<<B buckets.
+// Note that most of these overflow buckets must be in sparse use;
+// if use was dense, then we'd have already triggered regular map growth.
+func tooManyOverflowBuckets(noverflow uint16, B uint8) bool {
+	// If the threshold is too low, we do extraneous work.
+	// If the threshold is too high, maps that grow and shrink can hold on to lots of unused memory.
+	// "too many" means (approximately) as many overflow buckets as regular buckets.
+	// See incrnoverflow for more details.
+	if B < 16 {
+		return noverflow >= uint16(1)<<B
+	}
+	return noverflow >= 1<<15
+}
+
+// growing reports whether h is growing. The growth may be to the same size or bigger.
+func (h *hmap) growing() bool {
+	return h.oldbuckets != nil
+}
+
+// sameSizeGrow reports whether the current growth is to a map of the same size.
+func (h *hmap) sameSizeGrow() bool {
+	return h.flags&sameSizeGrow != 0
+}
+
+// noldbuckets calculates the number of buckets prior to the current map growth.
+func (h *hmap) noldbuckets() uintptr {
+	oldB := h.B
+	if !h.sameSizeGrow() {
+		oldB--
+	}
+	return uintptr(1) << oldB
+}
+
+// oldbucketmask provides a mask that can be applied to calculate n % noldbuckets().
+func (h *hmap) oldbucketmask() uintptr {
+	return h.noldbuckets() - 1
+}
 
+func growWork(t *maptype, h *hmap, bucket uintptr) {
 	// make sure we evacuate the oldbucket corresponding
 	// to the bucket we're about to use
-	evacuate(t, h, bucket&(noldbuckets-1))
+	evacuate(t, h, bucket&h.oldbucketmask())
 
 	// evacuate one more oldbucket to make progress on growing
-	if h.oldbuckets != nil {
+	if h.growing() {
 		evacuate(t, h, h.nevacuate)
 	}
 }
 
 func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
 	b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)))
-	newbit := uintptr(1) << (h.B - 1)
+	newbit := h.noldbuckets()
 	alg := t.key.alg
 	if !evacuated(b) {
 		// TODO: reuse overflow buckets instead of using new ones, if there
 		// is no iterator using the old buckets.  (If !oldIterator.)
 
-		x := (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize)))
-		y := (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize)))
-		xi := 0
-		yi := 0
-		xk := add(unsafe.Pointer(x), dataOffset)
-		yk := add(unsafe.Pointer(y), dataOffset)
-		xv := add(xk, bucketCnt*uintptr(t.keysize))
-		yv := add(yk, bucketCnt*uintptr(t.keysize))
+		var (
+			x, y   *bmap          // current low/high buckets in new map
+			xi, yi int            // key/val indices into x and y
+			xk, yk unsafe.Pointer // pointers to current x and y key storage
+			xv, yv unsafe.Pointer // pointers to current x and y value storage
+		)
+		x = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize)))
+		xi = 0
+		xk = add(unsafe.Pointer(x), dataOffset)
+		xv = add(xk, bucketCnt*uintptr(t.keysize))
+		if !h.sameSizeGrow() {
+			// Only calculate y pointers if we're growing bigger.
+			// Otherwise GC can see bad pointers.
+			y = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize)))
+			yi = 0
+			yk = add(unsafe.Pointer(y), dataOffset)
+			yv = add(yk, bucketCnt*uintptr(t.keysize))
+		}
 		for ; b != nil; b = b.overflow(t) {
 			k := add(unsafe.Pointer(b), dataOffset)
 			v := add(k, bucketCnt*uintptr(t.keysize))
@@ -891,34 +1000,38 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
 				if t.indirectkey {
 					k2 = *((*unsafe.Pointer)(k2))
 				}
-				// Compute hash to make our evacuation decision (whether we need
-				// to send this key/value to bucket x or bucket y).
-				hash := alg.hash(k2, uintptr(h.hash0))
-				if h.flags&iterator != 0 {
-					if !t.reflexivekey && !alg.equal(k2, k2) {
-						// If key != key (NaNs), then the hash could be (and probably
-						// will be) entirely different from the old hash. Moreover,
-						// it isn't reproducible. Reproducibility is required in the
-						// presence of iterators, as our evacuation decision must
-						// match whatever decision the iterator made.
-						// Fortunately, we have the freedom to send these keys either
-						// way. Also, tophash is meaningless for these kinds of keys.
-						// We let the low bit of tophash drive the evacuation decision.
-						// We recompute a new random tophash for the next level so
-						// these keys will get evenly distributed across all buckets
-						// after multiple grows.
-						if (top & 1) != 0 {
-							hash |= newbit
-						} else {
-							hash &^= newbit
-						}
-						top = uint8(hash >> (sys.PtrSize*8 - 8))
-						if top < minTopHash {
-							top += minTopHash
+				useX := true
+				if !h.sameSizeGrow() {
+					// Compute hash to make our evacuation decision (whether we need
+					// to send this key/value to bucket x or bucket y).
+					hash := alg.hash(k2, uintptr(h.hash0))
+					if h.flags&iterator != 0 {
+						if !t.reflexivekey && !alg.equal(k2, k2) {
+							// If key != key (NaNs), then the hash could be (and probably
+							// will be) entirely different from the old hash. Moreover,
+							// it isn't reproducible. Reproducibility is required in the
+							// presence of iterators, as our evacuation decision must
+							// match whatever decision the iterator made.
+							// Fortunately, we have the freedom to send these keys either
+							// way. Also, tophash is meaningless for these kinds of keys.
+							// We let the low bit of tophash drive the evacuation decision.
+							// We recompute a new random tophash for the next level so
+							// these keys will get evenly distributed across all buckets
+							// after multiple grows.
+							if top&1 != 0 {
+								hash |= newbit
+							} else {
+								hash &^= newbit
+							}
+							top = uint8(hash >> (sys.PtrSize*8 - 8))
+							if top < minTopHash {
+								top += minTopHash
+							}
 						}
 					}
+					useX = hash&newbit == 0
 				}
-				if (hash & newbit) == 0 {
+				if useX {
 					b.tophash[i] = evacuatedX
 					if xi == bucketCnt {
 						newx := (*bmap)(newobject(t.bucket))
@@ -972,7 +1085,13 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
 		// Unlink the overflow buckets & clear key/value to help GC.
 		if h.flags&oldIterator == 0 {
 			b = (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)))
-			memclr(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
+			// Preserve b.tophash because the evacuation
+			// state is maintained there.
+			if t.bucket.kind&kindNoPointers == 0 {
+				memclrHasPointers(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
+			} else {
+				memclrNoHeapPointers(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
+			}
 		}
 	}
 
@@ -988,6 +1107,7 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
 			if h.overflow != nil {
 				h.overflow[1] = nil
 			}
+			h.flags &^= sameSizeGrow
 		}
 	}
 }
@@ -1015,7 +1135,8 @@ func reflect_mapaccess(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
 
 //go:linkname reflect_mapassign reflect.mapassign
 func reflect_mapassign(t *maptype, h *hmap, key unsafe.Pointer, val unsafe.Pointer) {
-	mapassign1(t, h, key, val)
+	p := mapassign(t, h, key)
+	typedmemmove(t.elem, p, val)
 }
 
 //go:linkname reflect_mapdelete reflect.mapdelete
diff --git a/src/runtime/hashmap_fast.go b/src/runtime/hashmap_fast.go
index 8f9bb5a..b5ecc2d 100644
--- a/src/runtime/hashmap_fast.go
+++ b/src/runtime/hashmap_fast.go
@@ -29,7 +29,11 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
 		m := uintptr(1)<<h.B - 1
 		b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
 		if c := h.oldbuckets; c != nil {
-			oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
+			if !h.sameSizeGrow() {
+				// There used to be half as many buckets; mask down one more power of two.
+				m >>= 1
+			}
+			oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize)))
 			if !evacuated(oldb) {
 				b = oldb
 			}
@@ -74,7 +78,11 @@ func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
 		m := uintptr(1)<<h.B - 1
 		b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
 		if c := h.oldbuckets; c != nil {
-			oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
+			if !h.sameSizeGrow() {
+				// There used to be half as many buckets; mask down one more power of two.
+				m >>= 1
+			}
+			oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize)))
 			if !evacuated(oldb) {
 				b = oldb
 			}
@@ -119,7 +127,11 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
 		m := uintptr(1)<<h.B - 1
 		b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
 		if c := h.oldbuckets; c != nil {
-			oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
+			if !h.sameSizeGrow() {
+				// There used to be half as many buckets; mask down one more power of two.
+				m >>= 1
+			}
+			oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize)))
 			if !evacuated(oldb) {
 				b = oldb
 			}
@@ -164,7 +176,11 @@ func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
 		m := uintptr(1)<<h.B - 1
 		b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
 		if c := h.oldbuckets; c != nil {
-			oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
+			if !h.sameSizeGrow() {
+				// There used to be half as many buckets; mask down one more power of two.
+				m >>= 1
+			}
+			oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize)))
 			if !evacuated(oldb) {
 				b = oldb
 			}
@@ -264,7 +280,11 @@ dohash:
 	m := uintptr(1)<<h.B - 1
 	b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
 	if c := h.oldbuckets; c != nil {
-		oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
+		if !h.sameSizeGrow() {
+			// There used to be half as many buckets; mask down one more power of two.
+			m >>= 1
+		}
+		oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize)))
 		if !evacuated(oldb) {
 			b = oldb
 		}
@@ -367,7 +387,11 @@ dohash:
 	m := uintptr(1)<<h.B - 1
 	b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
 	if c := h.oldbuckets; c != nil {
-		oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
+		if !h.sameSizeGrow() {
+			// There used to be half as many buckets; mask down one more power of two.
+			m >>= 1
+		}
+		oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize)))
 		if !evacuated(oldb) {
 			b = oldb
 		}
diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go
index c317b5f..6039417 100644
--- a/src/runtime/heapdump.go
+++ b/src/runtime/heapdump.go
@@ -437,9 +437,7 @@ func dumproots() {
 	dumpfields(firstmoduledata.gcbssmask)
 
 	// MSpan.types
-	allspans := h_allspans
-	for spanidx := uint32(0); spanidx < mheap_.nspan; spanidx++ {
-		s := allspans[spanidx]
+	for _, s := range mheap_.allspans {
 		if s.state == _MSpanInUse {
 			// Finalizers
 			for sp := s.specials; sp != nil; sp = sp.next {
@@ -462,8 +460,7 @@ func dumproots() {
 var freemark [_PageSize / 8]bool
 
 func dumpobjs() {
-	for i := uintptr(0); i < uintptr(mheap_.nspan); i++ {
-		s := h_allspans[i]
+	for _, s := range mheap_.allspans {
 		if s.state != _MSpanInUse {
 			continue
 		}
@@ -474,7 +471,7 @@ func dumpobjs() {
 			throw("freemark array doesn't have enough entries")
 		}
 
-		for freeIndex := s.freeindex; freeIndex < s.nelems; freeIndex++ {
+		for freeIndex := uintptr(0); freeIndex < s.nelems; freeIndex++ {
 			if s.isFree(freeIndex) {
 				freemark[freeIndex] = true
 			}
@@ -608,9 +605,7 @@ func dumpmemprof_callback(b *bucket, nstk uintptr, pstk *uintptr, size, allocs,
 
 func dumpmemprof() {
 	iterate_memprof(dumpmemprof_callback)
-	allspans := h_allspans
-	for spanidx := uint32(0); spanidx < mheap_.nspan; spanidx++ {
-		s := allspans[spanidx]
+	for _, s := range mheap_.allspans {
 		if s.state != _MSpanInUse {
 			continue
 		}
@@ -631,13 +626,12 @@ var dumphdr = []byte("go1.7 heap dump\n")
 
 func mdump() {
 	// make sure we're done sweeping
-	for i := uintptr(0); i < uintptr(mheap_.nspan); i++ {
-		s := h_allspans[i]
+	for _, s := range mheap_.allspans {
 		if s.state == _MSpanInUse {
 			s.ensureSwept()
 		}
 	}
-	memclr(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
+	memclrNoHeapPointers(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
 	dwrite(unsafe.Pointer(&dumphdr[0]), uintptr(len(dumphdr)))
 	dumpparams()
 	dumpitabs()
diff --git a/src/runtime/iface.go b/src/runtime/iface.go
index 1690147..c932e14 100644
--- a/src/runtime/iface.go
+++ b/src/runtime/iface.go
@@ -138,21 +138,53 @@ func additab(m *itab, locked, canfail bool) {
 		throw("invalid itab locking")
 	}
 	h := itabhash(inter, typ)
+	if m == hash[h] {
+		println("duplicate itab for", typ.string(), "and", inter.typ.string())
+		throw("duplicate itabs")
+	}
 	m.link = hash[h]
 	atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m))
 }
 
 func itabsinit() {
 	lock(&ifaceLock)
-	for m := &firstmoduledata; m != nil; m = m.next {
-		for _, i := range m.itablinks {
+	for _, md := range activeModules() {
+		for _, i := range md.itablinks {
 			additab(i, true, false)
 		}
 	}
 	unlock(&ifaceLock)
 }
 
-func convT2E(t *_type, elem unsafe.Pointer, x unsafe.Pointer) (e eface) {
+// panicdottype is called when doing an i.(T) conversion and the conversion fails.
+// have = the dynamic type we have.
+// want = the static type we're trying to convert to.
+// iface = the static type we're converting from.
+func panicdottype(have, want, iface *_type) {
+	haveString := ""
+	if have != nil {
+		haveString = have.string()
+	}
+	panic(&TypeAssertionError{iface.string(), haveString, want.string(), ""})
+}
+
+// panicnildottype is called when doing a i.(T) conversion and the interface i is nil.
+// want = the static type we're trying to convert to.
+func panicnildottype(want *_type) {
+	panic(&TypeAssertionError{"", "", want.string(), ""})
+	// TODO: Add the static type we're converting from as well.
+	// It might generate a better error message.
+	// Just to match other nil conversion errors, we don't for now.
+}
+
+// The conv and assert functions below do very similar things.
+// The convXXX functions are guaranteed by the compiler to succeed.
+// The assertXXX functions may fail (either panicing or returning false,
+// depending on whether they are 1-result or 2-result).
+// The convXXX functions succeed on a nil input, whereas the assertXXX
+// functions fail on a nil input.
+
+func convT2E(t *_type, elem unsafe.Pointer) (e eface) {
 	if raceenabled {
 		raceReadObjectPC(t, elem, getcallerpc(unsafe.Pointer(&t)), funcPC(convT2E))
 	}
@@ -160,20 +192,19 @@ func convT2E(t *_type, elem unsafe.Pointer, x unsafe.Pointer) (e eface) {
 		msanread(elem, t.size)
 	}
 	if isDirectIface(t) {
+		// This case is implemented directly by the compiler.
 		throw("direct convT2E")
 	}
-	if x == nil {
-		x = newobject(t)
-		// TODO: We allocate a zeroed object only to overwrite it with
-		// actual data. Figure out how to avoid zeroing. Also below in convT2I.
-	}
+	x := newobject(t)
+	// TODO: We allocate a zeroed object only to overwrite it with
+	// actual data. Figure out how to avoid zeroing. Also below in convT2I.
 	typedmemmove(t, x, elem)
 	e._type = t
 	e.data = x
 	return
 }
 
-func convT2I(tab *itab, elem unsafe.Pointer, x unsafe.Pointer) (i iface) {
+func convT2I(tab *itab, elem unsafe.Pointer) (i iface) {
 	t := tab._type
 	if raceenabled {
 		raceReadObjectPC(t, elem, getcallerpc(unsafe.Pointer(&tab)), funcPC(convT2I))
@@ -182,127 +213,16 @@ func convT2I(tab *itab, elem unsafe.Pointer, x unsafe.Pointer) (i iface) {
 		msanread(elem, t.size)
 	}
 	if isDirectIface(t) {
+		// This case is implemented directly by the compiler.
 		throw("direct convT2I")
 	}
-	if x == nil {
-		x = newobject(t)
-	}
+	x := newobject(t)
 	typedmemmove(t, x, elem)
 	i.tab = tab
 	i.data = x
 	return
 }
 
-func panicdottype(have, want, iface *_type) {
-	haveString := ""
-	if have != nil {
-		haveString = have.string()
-	}
-	panic(&TypeAssertionError{iface.string(), haveString, want.string(), ""})
-}
-
-func assertI2T(t *_type, i iface, r unsafe.Pointer) {
-	tab := i.tab
-	if tab == nil {
-		panic(&TypeAssertionError{"", "", t.string(), ""})
-	}
-	if tab._type != t {
-		panic(&TypeAssertionError{tab.inter.typ.string(), tab._type.string(), t.string(), ""})
-	}
-	if r != nil {
-		if isDirectIface(t) {
-			writebarrierptr((*uintptr)(r), uintptr(i.data))
-		} else {
-			typedmemmove(t, r, i.data)
-		}
-	}
-}
-
-func assertI2T2(t *_type, i iface, r unsafe.Pointer) bool {
-	tab := i.tab
-	if tab == nil || tab._type != t {
-		if r != nil {
-			memclr(r, t.size)
-		}
-		return false
-	}
-	if r != nil {
-		if isDirectIface(t) {
-			writebarrierptr((*uintptr)(r), uintptr(i.data))
-		} else {
-			typedmemmove(t, r, i.data)
-		}
-	}
-	return true
-}
-
-func assertE2T(t *_type, e eface, r unsafe.Pointer) {
-	if e._type == nil {
-		panic(&TypeAssertionError{"", "", t.string(), ""})
-	}
-	if e._type != t {
-		panic(&TypeAssertionError{"", e._type.string(), t.string(), ""})
-	}
-	if r != nil {
-		if isDirectIface(t) {
-			writebarrierptr((*uintptr)(r), uintptr(e.data))
-		} else {
-			typedmemmove(t, r, e.data)
-		}
-	}
-}
-
-var testingAssertE2T2GC bool
-
-// The compiler ensures that r is non-nil.
-func assertE2T2(t *_type, e eface, r unsafe.Pointer) bool {
-	if testingAssertE2T2GC {
-		GC()
-	}
-	if e._type != t {
-		memclr(r, t.size)
-		return false
-	}
-	if isDirectIface(t) {
-		writebarrierptr((*uintptr)(r), uintptr(e.data))
-	} else {
-		typedmemmove(t, r, e.data)
-	}
-	return true
-}
-
-func convI2E(i iface) (r eface) {
-	tab := i.tab
-	if tab == nil {
-		return
-	}
-	r._type = tab._type
-	r.data = i.data
-	return
-}
-
-func assertI2E(inter *interfacetype, i iface, r *eface) {
-	tab := i.tab
-	if tab == nil {
-		// explicit conversions require non-nil interface value.
-		panic(&TypeAssertionError{"", "", inter.typ.string(), ""})
-	}
-	r._type = tab._type
-	r.data = i.data
-	return
-}
-
-// The compiler ensures that r is non-nil.
-func assertI2E2(inter *interfacetype, i iface, r *eface) bool {
-	tab := i.tab
-	if tab == nil {
-		return false
-	}
-	r._type = tab._type
-	r.data = i.data
-	return true
-}
-
 func convI2I(inter *interfacetype, i iface) (r iface) {
 	tab := i.tab
 	if tab == nil {
@@ -318,7 +238,7 @@ func convI2I(inter *interfacetype, i iface) (r iface) {
 	return
 }
 
-func assertI2I(inter *interfacetype, i iface, r *iface) {
+func assertI2I(inter *interfacetype, i iface) (r iface) {
 	tab := i.tab
 	if tab == nil {
 		// explicit conversions require non-nil interface value.
@@ -331,33 +251,27 @@ func assertI2I(inter *interfacetype, i iface, r *iface) {
 	}
 	r.tab = getitab(inter, tab._type, false)
 	r.data = i.data
+	return
 }
 
-func assertI2I2(inter *interfacetype, i iface, r *iface) bool {
+func assertI2I2(inter *interfacetype, i iface) (r iface, b bool) {
 	tab := i.tab
 	if tab == nil {
-		if r != nil {
-			*r = iface{}
-		}
-		return false
+		return
 	}
 	if tab.inter != inter {
 		tab = getitab(inter, tab._type, true)
 		if tab == nil {
-			if r != nil {
-				*r = iface{}
-			}
-			return false
+			return
 		}
 	}
-	if r != nil {
-		r.tab = tab
-		r.data = i.data
-	}
-	return true
+	r.tab = tab
+	r.data = i.data
+	b = true
+	return
 }
 
-func assertE2I(inter *interfacetype, e eface, r *iface) {
+func assertE2I(inter *interfacetype, e eface) (r iface) {
 	t := e._type
 	if t == nil {
 		// explicit conversions require non-nil interface value.
@@ -365,56 +279,27 @@ func assertE2I(inter *interfacetype, e eface, r *iface) {
 	}
 	r.tab = getitab(inter, t, false)
 	r.data = e.data
+	return
 }
 
-var testingAssertE2I2GC bool
-
-func assertE2I2(inter *interfacetype, e eface, r *iface) bool {
-	if testingAssertE2I2GC {
-		GC()
-	}
+func assertE2I2(inter *interfacetype, e eface) (r iface, b bool) {
 	t := e._type
 	if t == nil {
-		if r != nil {
-			*r = iface{}
-		}
-		return false
+		return
 	}
 	tab := getitab(inter, t, true)
 	if tab == nil {
-		if r != nil {
-			*r = iface{}
-		}
-		return false
-	}
-	if r != nil {
-		r.tab = tab
-		r.data = e.data
+		return
 	}
-	return true
+	r.tab = tab
+	r.data = e.data
+	b = true
+	return
 }
 
 //go:linkname reflect_ifaceE2I reflect.ifaceE2I
 func reflect_ifaceE2I(inter *interfacetype, e eface, dst *iface) {
-	assertE2I(inter, e, dst)
-}
-
-func assertE2E(inter *interfacetype, e eface, r *eface) {
-	if e._type == nil {
-		// explicit conversions require non-nil interface value.
-		panic(&TypeAssertionError{"", "", inter.typ.string(), ""})
-	}
-	*r = e
-}
-
-// The compiler ensures that r is non-nil.
-func assertE2E2(inter *interfacetype, e eface, r *eface) bool {
-	if e._type == nil {
-		*r = eface{}
-		return false
-	}
-	*r = e
-	return true
+	*dst = assertE2I(inter, e)
 }
 
 func iterate_itabs(fn func(*itab)) {
diff --git a/src/runtime/internal/atomic/asm.s b/src/runtime/internal/atomic/asm.s
deleted file mode 100644
index 8488585..0000000
--- a/src/runtime/internal/atomic/asm.s
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "textflag.h"
-
-TEXT runtime∕internal∕atomic·nop(SB),NOSPLIT,$0-0
-	RET
diff --git a/src/runtime/internal/atomic/asm_386.s b/src/runtime/internal/atomic/asm_386.s
index 357d830..882906e 100644
--- a/src/runtime/internal/atomic/asm_386.s
+++ b/src/runtime/internal/atomic/asm_386.s
@@ -52,6 +52,9 @@ TEXT runtime∕internal∕atomic·Xaddint64(SB), NOSPLIT, $0-20
 //	}
 TEXT runtime∕internal∕atomic·Cas64(SB), NOSPLIT, $0-21
 	MOVL	ptr+0(FP), BP
+	TESTL	$7, BP
+	JZ	2(PC)
+	MOVL	0, BP // crash with nil ptr deref
 	MOVL	old_lo+4(FP), AX
 	MOVL	old_hi+8(FP), DX
 	MOVL	new_lo+12(FP), BX
@@ -61,7 +64,7 @@ TEXT runtime∕internal∕atomic·Cas64(SB), NOSPLIT, $0-21
 	SETEQ	ret+20(FP)
 	RET
 
-// bool Casp(void **p, void *old, void *new)
+// bool Casp1(void **p, void *old, void *new)
 // Atomically:
 //	if(*p == old){
 //		*p = new;
diff --git a/src/runtime/internal/atomic/asm_amd64.s b/src/runtime/internal/atomic/asm_amd64.s
index 0001d23..6fb5211 100644
--- a/src/runtime/internal/atomic/asm_amd64.s
+++ b/src/runtime/internal/atomic/asm_amd64.s
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// Note: some of these functions are semantically inlined
+// by the compiler (in src/cmd/compile/internal/gc/ssa.go).
+
 #include "textflag.h"
 
 // bool Cas(int32 *val, int32 old, int32 new)
@@ -55,7 +58,7 @@ TEXT runtime∕internal∕atomic·Loadint64(SB), NOSPLIT, $0-16
 TEXT runtime∕internal∕atomic·Xaddint64(SB), NOSPLIT, $0-24
 	JMP	runtime∕internal∕atomic·Xadd64(SB)
 
-// bool Casp(void **val, void *old, void *new)
+// bool Casp1(void **val, void *old, void *new)
 // Atomically:
 //	if(*val == old){
 //		*val = new;
diff --git a/src/runtime/internal/atomic/asm_amd64p32.s b/src/runtime/internal/atomic/asm_amd64p32.s
index 22c707c..87f7a07 100644
--- a/src/runtime/internal/atomic/asm_amd64p32.s
+++ b/src/runtime/internal/atomic/asm_amd64p32.s
@@ -55,7 +55,7 @@ TEXT runtime∕internal∕atomic·Cas64(SB), NOSPLIT, $0-25
 	SETEQ	ret+24(FP)
 	RET
 
-// bool Casp(void **val, void *old, void *new)
+// bool Casp1(void **val, void *old, void *new)
 // Atomically:
 //	if(*val == old){
 //		*val = new;
diff --git a/src/runtime/internal/atomic/asm_arm.s b/src/runtime/internal/atomic/asm_arm.s
index 12da223..5e2380e 100644
--- a/src/runtime/internal/atomic/asm_arm.s
+++ b/src/runtime/internal/atomic/asm_arm.s
@@ -19,7 +19,7 @@
 //		B	runtime∕internal∕atomic·armcas(SB)
 //
 TEXT runtime∕internal∕atomic·armcas(SB),NOSPLIT,$0-13
-	MOVW	valptr+0(FP), R1
+	MOVW	ptr+0(FP), R1
 	MOVW	old+4(FP), R2
 	MOVW	new+8(FP), R3
 casl:
diff --git a/src/runtime/internal/atomic/asm_arm64.s b/src/runtime/internal/atomic/asm_arm64.s
index 929bf71..b6af632 100644
--- a/src/runtime/internal/atomic/asm_arm64.s
+++ b/src/runtime/internal/atomic/asm_arm64.s
@@ -47,7 +47,7 @@ TEXT runtime∕internal∕atomic·Loadint64(SB), NOSPLIT, $0-16
 TEXT runtime∕internal∕atomic·Xaddint64(SB), NOSPLIT, $0-24
 	B	runtime∕internal∕atomic·Xadd64(SB)
 
-// bool Casp(void **val, void *old, void *new)
+// bool Casp1(void **val, void *old, void *new)
 // Atomically:
 //	if(*val == old){
 //		*val = new;
diff --git a/src/runtime/internal/atomic/asm_mipsx.s b/src/runtime/internal/atomic/asm_mipsx.s
new file mode 100644
index 0000000..30550fd
--- /dev/null
+++ b/src/runtime/internal/atomic/asm_mipsx.s
@@ -0,0 +1,149 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "textflag.h"
+
+TEXT ·Cas(SB),NOSPLIT,$0-13
+	MOVW	ptr+0(FP), R1
+	MOVW	old+4(FP), R2
+	MOVW	new+8(FP), R5
+	SYNC
+try_cas:
+	MOVW	R5, R3
+	LL	(R1), R4	// R4 = *R1
+	BNE	R2, R4, cas_fail
+	SC	R3, (R1)	// *R1 = R3
+	BEQ	R3, try_cas
+	SYNC
+	MOVB	R3, ret+12(FP)
+	RET
+cas_fail:
+	MOVB	R0, ret+12(FP)
+	RET
+
+TEXT ·Store(SB),NOSPLIT,$0-8
+	MOVW	ptr+0(FP), R1
+	MOVW	val+4(FP), R2
+	SYNC
+	MOVW	R2, 0(R1)
+	SYNC
+	RET
+
+TEXT ·Load(SB),NOSPLIT,$0-8
+	MOVW	ptr+0(FP), R1
+	SYNC
+	MOVW	0(R1), R1
+	SYNC
+	MOVW	R1, ret+4(FP)
+	RET
+
+TEXT ·Xadd(SB),NOSPLIT,$0-12
+	MOVW	ptr+0(FP), R2
+	MOVW	delta+4(FP), R3
+	SYNC
+try_xadd:
+	LL	(R2), R1	// R1 = *R2
+	ADDU	R1, R3, R4
+	MOVW	R4, R1
+	SC	R4, (R2)	// *R2 = R4
+	BEQ	R4, try_xadd
+	SYNC
+	MOVW	R1, ret+8(FP)
+	RET
+
+TEXT ·Xchg(SB),NOSPLIT,$0-12
+	MOVW	ptr+0(FP), R2
+	MOVW	new+4(FP), R5
+	SYNC
+try_xchg:
+	MOVW	R5, R3
+	LL	(R2), R1	// R1 = *R2
+	SC	R3, (R2)	// *R2 = R3
+	BEQ	R3, try_xchg
+	SYNC
+	MOVW	R1, ret+8(FP)
+	RET
+
+TEXT ·Casuintptr(SB),NOSPLIT,$0-13
+	JMP	·Cas(SB)
+
+TEXT ·Loaduintptr(SB),NOSPLIT,$0-8
+	JMP	·Load(SB)
+
+TEXT ·Loaduint(SB),NOSPLIT,$0-8
+	JMP	·Load(SB)
+
+TEXT ·Loadp(SB),NOSPLIT,$-0-8
+	JMP	·Load(SB)
+
+TEXT ·Storeuintptr(SB),NOSPLIT,$0-8
+	JMP	·Store(SB)
+
+TEXT ·Xadduintptr(SB),NOSPLIT,$0-12
+	JMP	·Xadd(SB)
+
+TEXT ·Loadint64(SB),NOSPLIT,$0-12
+	JMP	·Load64(SB)
+
+TEXT ·Xaddint64(SB),NOSPLIT,$0-20
+	JMP	·Xadd64(SB)
+
+TEXT ·Casp1(SB),NOSPLIT,$0-13
+	JMP	·Cas(SB)
+
+TEXT ·Xchguintptr(SB),NOSPLIT,$0-12
+	JMP	·Xchg(SB)
+
+TEXT ·StorepNoWB(SB),NOSPLIT,$0-8
+	JMP	·Store(SB)
+
+// void	Or8(byte volatile*, byte);
+TEXT ·Or8(SB),NOSPLIT,$0-5
+	MOVW	ptr+0(FP), R1
+	MOVBU	val+4(FP), R2
+	MOVW	$~3, R3	// Align ptr down to 4 bytes so we can use 32-bit load/store.
+	AND	R1, R3
+#ifdef GOARCH_mips
+	// Big endian.  ptr = ptr ^ 3
+	XOR	$3, R1
+#endif
+	AND	$3, R1, R4	// R4 = ((ptr & 3) * 8)
+	SLL	$3, R4
+	SLL	R4, R2, R2	// Shift val for aligned ptr. R2 = val << R4
+	SYNC
+try_or8:
+	LL	(R3), R4	// R4 = *R3
+	OR	R2, R4
+	SC	R4, (R3)	// *R3 = R4
+	BEQ	R4, try_or8
+	SYNC
+	RET
+
+// void	And8(byte volatile*, byte);
+TEXT ·And8(SB),NOSPLIT,$0-5
+	MOVW	ptr+0(FP), R1
+	MOVBU	val+4(FP), R2
+	MOVW	$~3, R3
+	AND	R1, R3
+#ifdef GOARCH_mips
+	// Big endian.  ptr = ptr ^ 3
+	XOR	$3, R1
+#endif
+	AND	$3, R1, R4	// R4 = ((ptr & 3) * 8)
+	SLL	$3, R4
+	MOVW	$0xFF, R5
+	SLL	R4, R2
+	SLL	R4, R5
+	NOR	R0, R5
+	OR	R5, R2	// Shift val for aligned ptr. R2 = val << R4 | ^(0xFF << R4)
+	SYNC
+try_and8:
+	LL	(R3), R4	// R4 = *R3
+	AND	R2, R4
+	SC	R4, (R3)	// *R3 = R4
+	BEQ	R4, try_and8
+	SYNC
+	RET
diff --git a/src/runtime/internal/atomic/asm_s390x.s b/src/runtime/internal/atomic/asm_s390x.s
index c84718c..e25703e 100644
--- a/src/runtime/internal/atomic/asm_s390x.s
+++ b/src/runtime/internal/atomic/asm_s390x.s
@@ -69,7 +69,7 @@ TEXT ·Xadduintptr(SB), NOSPLIT, $0-24
 	BR	·Xadd64(SB)
 
 // func Xaddint64(ptr *int64, delta int64) int64
-TEXT ·Xaddint64(SB), NOSPLIT, $0-16
+TEXT ·Xaddint64(SB), NOSPLIT, $0-24
 	BR	·Xadd64(SB)
 
 // func Casp1(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool
@@ -141,7 +141,8 @@ TEXT ·Or8(SB), NOSPLIT, $0-9
 	MOVD    ptr+0(FP), R3
 	MOVBZ   val+8(FP), R4
 	// Calculate shift.
-	AND	$3, R3, R5
+	MOVD	R3, R5
+	AND	$3, R5
 	XOR	$3, R5 // big endian - flip direction
 	SLD	$3, R5 // MUL $8, R5
 	SLD	R5, R4
@@ -159,7 +160,8 @@ TEXT ·And8(SB), NOSPLIT, $0-9
 	MOVD    ptr+0(FP), R3
 	MOVBZ   val+8(FP), R4
 	// Calculate shift.
-	AND	$3, R3, R5
+	MOVD	R3, R5
+	AND	$3, R5
 	XOR	$3, R5 // big endian - flip direction
 	SLD	$3, R5 // MUL $8, R5
 	OR	$-256, R4 // create 0xffffffffffffffxx
diff --git a/src/runtime/internal/atomic/atomic_arm.go b/src/runtime/internal/atomic/atomic_arm.go
index 244237d..72af584 100644
--- a/src/runtime/internal/atomic/atomic_arm.go
+++ b/src/runtime/internal/atomic/atomic_arm.go
@@ -106,6 +106,9 @@ func Store(addr *uint32, v uint32) {
 
 //go:nosplit
 func Cas64(addr *uint64, old, new uint64) bool {
+	if uintptr(unsafe.Pointer(addr))&7 != 0 {
+		*(*int)(nil) = 0 // crash on unaligned uint64
+	}
 	var ok bool
 	addrLock(addr).lock()
 	if *addr == old {
@@ -118,6 +121,9 @@ func Cas64(addr *uint64, old, new uint64) bool {
 
 //go:nosplit
 func Xadd64(addr *uint64, delta int64) uint64 {
+	if uintptr(unsafe.Pointer(addr))&7 != 0 {
+		*(*int)(nil) = 0 // crash on unaligned uint64
+	}
 	var r uint64
 	addrLock(addr).lock()
 	r = *addr + uint64(delta)
@@ -128,6 +134,9 @@ func Xadd64(addr *uint64, delta int64) uint64 {
 
 //go:nosplit
 func Xchg64(addr *uint64, v uint64) uint64 {
+	if uintptr(unsafe.Pointer(addr))&7 != 0 {
+		*(*int)(nil) = 0 // crash on unaligned uint64
+	}
 	var r uint64
 	addrLock(addr).lock()
 	r = *addr
@@ -138,6 +147,9 @@ func Xchg64(addr *uint64, v uint64) uint64 {
 
 //go:nosplit
 func Load64(addr *uint64) uint64 {
+	if uintptr(unsafe.Pointer(addr))&7 != 0 {
+		*(*int)(nil) = 0 // crash on unaligned uint64
+	}
 	var r uint64
 	addrLock(addr).lock()
 	r = *addr
@@ -147,6 +159,9 @@ func Load64(addr *uint64) uint64 {
 
 //go:nosplit
 func Store64(addr *uint64, v uint64) {
+	if uintptr(unsafe.Pointer(addr))&7 != 0 {
+		*(*int)(nil) = 0 // crash on unaligned uint64
+	}
 	addrLock(addr).lock()
 	*addr = v
 	addrLock(addr).unlock()
@@ -181,3 +196,6 @@ func And8(addr *uint8, v uint8) {
 		}
 	}
 }
+
+//go:nosplit
+func armcas(ptr *uint32, old, new uint32) bool
diff --git a/src/runtime/internal/atomic/atomic_arm64.go b/src/runtime/internal/atomic/atomic_arm64.go
index dc82c33..3554b7f 100644
--- a/src/runtime/internal/atomic/atomic_arm64.go
+++ b/src/runtime/internal/atomic/atomic_arm64.go
@@ -35,37 +35,11 @@ func Load64(ptr *uint64) uint64
 //go:noescape
 func Loadp(ptr unsafe.Pointer) unsafe.Pointer
 
-//go:nosplit
-func Or8(addr *uint8, v uint8) {
-	// TODO(dfc) implement this in asm.
-	// Align down to 4 bytes and use 32-bit CAS.
-	uaddr := uintptr(unsafe.Pointer(addr))
-	addr32 := (*uint32)(unsafe.Pointer(uaddr &^ 3))
-	word := uint32(v) << ((uaddr & 3) * 8) // little endian
-	for {
-		old := *addr32
-		if Cas(addr32, old, old|word) {
-			return
-		}
-	}
-}
-
-//go:nosplit
-func And8(addr *uint8, v uint8) {
-	// TODO(dfc) implement this in asm.
-	// Align down to 4 bytes and use 32-bit CAS.
-	uaddr := uintptr(unsafe.Pointer(addr))
-	addr32 := (*uint32)(unsafe.Pointer(uaddr &^ 3))
-	word := uint32(v) << ((uaddr & 3) * 8)    // little endian
-	mask := uint32(0xFF) << ((uaddr & 3) * 8) // little endian
-	word |= ^mask
-	for {
-		old := *addr32
-		if Cas(addr32, old, old&word) {
-			return
-		}
-	}
-}
+//go:noescape
+func Or8(ptr *uint8, val uint8)
+
+//go:noescape
+func And8(ptr *uint8, val uint8)
 
 //go:noescape
 func Cas64(ptr *uint64, old, new uint64) bool
diff --git a/src/runtime/internal/atomic/atomic_arm64.s b/src/runtime/internal/atomic/atomic_arm64.s
index eb32f37..6c2031c 100644
--- a/src/runtime/internal/atomic/atomic_arm64.s
+++ b/src/runtime/internal/atomic/atomic_arm64.s
@@ -111,3 +111,22 @@ again:
 
 TEXT runtime∕internal∕atomic·Xchguintptr(SB), NOSPLIT, $0-24
 	B	runtime∕internal∕atomic·Xchg64(SB)
+
+TEXT ·And8(SB), NOSPLIT, $0-9
+	MOVD	ptr+0(FP), R0
+	MOVB	val+8(FP), R1
+	LDAXRB	(R0), R2
+	AND	R1, R2
+	STLXRB	R2, (R0), R3
+	CBNZ	R3, -3(PC)
+	RET
+
+TEXT ·Or8(SB), NOSPLIT, $0-9
+	MOVD	ptr+0(FP), R0
+	MOVB	val+8(FP), R1
+	LDAXRB	(R0), R2
+	ORR	R1, R2
+	STLXRB	R2, (R0), R3
+	CBNZ	R3, -3(PC)
+	RET
+
diff --git a/src/runtime/internal/atomic/atomic_mipsx.go b/src/runtime/internal/atomic/atomic_mipsx.go
new file mode 100644
index 0000000..93a1f1a
--- /dev/null
+++ b/src/runtime/internal/atomic/atomic_mipsx.go
@@ -0,0 +1,132 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+package atomic
+
+import (
+	"runtime/internal/sys"
+	"unsafe"
+)
+
+// TODO implement lock striping
+var lock struct {
+	state uint32
+	pad   [sys.CacheLineSize - 4]byte
+}
+
+//go:noescape
+func spinLock(state *uint32)
+
+//go:noescape
+func spinUnlock(state *uint32)
+
+//go:nosplit
+func lockAndCheck(addr *uint64) {
+	// ensure 8-byte alignement
+	if uintptr(unsafe.Pointer(addr))&7 != 0 {
+		addr = nil
+	}
+	// force dereference before taking lock
+	_ = *addr
+
+	spinLock(&lock.state)
+}
+
+//go:nosplit
+func unlock() {
+	spinUnlock(&lock.state)
+}
+
+//go:nosplit
+func unlockNoFence() {
+	lock.state = 0
+}
+
+//go:nosplit
+func Xadd64(addr *uint64, delta int64) (new uint64) {
+	lockAndCheck(addr)
+
+	new = *addr + uint64(delta)
+	*addr = new
+
+	unlock()
+	return
+}
+
+//go:nosplit
+func Xchg64(addr *uint64, new uint64) (old uint64) {
+	lockAndCheck(addr)
+
+	old = *addr
+	*addr = new
+
+	unlock()
+	return
+}
+
+//go:nosplit
+func Cas64(addr *uint64, old, new uint64) (swapped bool) {
+	lockAndCheck(addr)
+
+	if (*addr) == old {
+		*addr = new
+		unlock()
+		return true
+	}
+
+	unlockNoFence()
+	return false
+}
+
+//go:nosplit
+func Load64(addr *uint64) (val uint64) {
+	lockAndCheck(addr)
+
+	val = *addr
+
+	unlock()
+	return
+}
+
+//go:nosplit
+func Store64(addr *uint64, val uint64) {
+	lockAndCheck(addr)
+
+	*addr = val
+
+	unlock()
+	return
+}
+
+//go:noescape
+func Xadd(ptr *uint32, delta int32) uint32
+
+//go:noescape
+func Xadduintptr(ptr *uintptr, delta uintptr) uintptr
+
+//go:noescape
+func Xchg(ptr *uint32, new uint32) uint32
+
+//go:noescape
+func Xchguintptr(ptr *uintptr, new uintptr) uintptr
+
+//go:noescape
+func Load(ptr *uint32) uint32
+
+//go:noescape
+func Loadp(ptr unsafe.Pointer) unsafe.Pointer
+
+//go:noescape
+func And8(ptr *uint8, val uint8)
+
+//go:noescape
+func Or8(ptr *uint8, val uint8)
+
+//go:noescape
+func Store(ptr *uint32, val uint32)
+
+// NO go:noescape annotation; see atomic_pointer.go.
+func StorepNoWB(ptr unsafe.Pointer, val unsafe.Pointer)
diff --git a/src/runtime/internal/atomic/atomic_mipsx.s b/src/runtime/internal/atomic/atomic_mipsx.s
new file mode 100644
index 0000000..aeebc8f
--- /dev/null
+++ b/src/runtime/internal/atomic/atomic_mipsx.s
@@ -0,0 +1,28 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "textflag.h"
+
+TEXT ·spinLock(SB),NOSPLIT,$0-4
+	MOVW	state+0(FP), R1
+	MOVW	$1, R2
+	SYNC
+try_lock:
+	MOVW	R2, R3
+check_again:
+	LL	(R1), R4
+	BNE	R4, check_again
+	SC	R3, (R1)
+	BEQ	R3, try_lock
+	SYNC
+	RET
+
+TEXT ·spinUnlock(SB),NOSPLIT,$0-4
+	MOVW	state+0(FP), R1
+	SYNC
+	MOVW	R0, (R1)
+	SYNC
+	RET
diff --git a/src/runtime/internal/atomic/atomic_ppc64x.s b/src/runtime/internal/atomic/atomic_ppc64x.s
index 1a7537e..c9c2d1f 100644
--- a/src/runtime/internal/atomic/atomic_ppc64x.s
+++ b/src/runtime/internal/atomic/atomic_ppc64x.s
@@ -6,9 +6,9 @@
 
 #include "textflag.h"
 
-// uint32 runtime∕internal∕atomic·Load(uint32 volatile* addr)
+// uint32 runtime∕internal∕atomic·Load(uint32 volatile* ptr)
 TEXT ·Load(SB),NOSPLIT|NOFRAME,$-8-12
-	MOVD	addr+0(FP), R3
+	MOVD	ptr+0(FP), R3
 	SYNC
 	MOVWZ	0(R3), R3
 	CMPW	R3, R3, CR7
@@ -17,9 +17,9 @@ TEXT ·Load(SB),NOSPLIT|NOFRAME,$-8-12
 	MOVW	R3, ret+8(FP)
 	RET
 
-// uint64 runtime∕internal∕atomic·Load64(uint64 volatile* addr)
+// uint64 runtime∕internal∕atomic·Load64(uint64 volatile* ptr)
 TEXT ·Load64(SB),NOSPLIT|NOFRAME,$-8-16
-	MOVD	addr+0(FP), R3
+	MOVD	ptr+0(FP), R3
 	SYNC
 	MOVD	0(R3), R3
 	CMP	R3, R3, CR7
@@ -28,9 +28,9 @@ TEXT ·Load64(SB),NOSPLIT|NOFRAME,$-8-16
 	MOVD	R3, ret+8(FP)
 	RET
 
-// void *runtime∕internal∕atomic·Loadp(void *volatile *addr)
+// void *runtime∕internal∕atomic·Loadp(void *volatile *ptr)
 TEXT ·Loadp(SB),NOSPLIT|NOFRAME,$-8-16
-	MOVD	addr+0(FP), R3
+	MOVD	ptr+0(FP), R3
 	SYNC
 	MOVD	0(R3), R3
 	CMP	R3, R3, CR7
diff --git a/src/runtime/internal/atomic/atomic_test.go b/src/runtime/internal/atomic/atomic_test.go
index d5dc552..879a82f 100644
--- a/src/runtime/internal/atomic/atomic_test.go
+++ b/src/runtime/internal/atomic/atomic_test.go
@@ -7,6 +7,7 @@ package atomic_test
 import (
 	"runtime"
 	"runtime/internal/atomic"
+	"runtime/internal/sys"
 	"testing"
 	"unsafe"
 )
@@ -51,13 +52,13 @@ func TestXadduintptr(t *testing.T) {
 // Tests that xadduintptr correctly updates 64-bit values. The place where
 // we actually do so is mstats.go, functions mSysStat{Inc,Dec}.
 func TestXadduintptrOnUint64(t *testing.T) {
-	/*	if runtime.BigEndian != 0 {
+	if sys.BigEndian != 0 {
 		// On big endian architectures, we never use xadduintptr to update
 		// 64-bit values and hence we skip the test.  (Note that functions
 		// mSysStat{Inc,Dec} in mstats.go have explicit checks for
 		// big-endianness.)
-		return
-	}*/
+		t.Skip("skip xadduintptr on big endian architecture")
+	}
 	const inc = 100
 	val := uint64(0)
 	atomic.Xadduintptr((*uintptr)(unsafe.Pointer(&val)), inc)
@@ -65,3 +66,40 @@ func TestXadduintptrOnUint64(t *testing.T) {
 		t.Fatalf("xadduintptr should increase lower-order bits, want %d, got %d", inc, val)
 	}
 }
+
+func shouldPanic(t *testing.T, name string, f func()) {
+	defer func() {
+		if recover() == nil {
+			t.Errorf("%s did not panic", name)
+		}
+	}()
+	f()
+}
+
+// Variant of sync/atomic's TestUnaligned64:
+func TestUnaligned64(t *testing.T) {
+	// Unaligned 64-bit atomics on 32-bit systems are
+	// a continual source of pain. Test that on 32-bit systems they crash
+	// instead of failing silently.
+
+	switch runtime.GOARCH {
+	default:
+		if unsafe.Sizeof(int(0)) != 4 {
+			t.Skip("test only runs on 32-bit systems")
+		}
+	case "amd64p32":
+		// amd64p32 can handle unaligned atomics.
+		t.Skipf("test not needed on %v", runtime.GOARCH)
+	}
+
+	x := make([]uint32, 4)
+	up64 := (*uint64)(unsafe.Pointer(&x[1])) // misaligned
+	p64 := (*int64)(unsafe.Pointer(&x[1]))   // misaligned
+
+	shouldPanic(t, "Load64", func() { atomic.Load64(up64) })
+	shouldPanic(t, "Loadint64", func() { atomic.Loadint64(p64) })
+	shouldPanic(t, "Store64", func() { atomic.Store64(up64, 0) })
+	shouldPanic(t, "Xadd64", func() { atomic.Xadd64(up64, 1) })
+	shouldPanic(t, "Xchg64", func() { atomic.Xchg64(up64, 1) })
+	shouldPanic(t, "Cas64", func() { atomic.Cas64(up64, 1, 2) })
+}
diff --git a/src/runtime/internal/atomic/bench_test.go b/src/runtime/internal/atomic/bench_test.go
new file mode 100644
index 0000000..47010e3
--- /dev/null
+++ b/src/runtime/internal/atomic/bench_test.go
@@ -0,0 +1,28 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package atomic_test
+
+import (
+	"runtime/internal/atomic"
+	"testing"
+)
+
+var sink interface{}
+
+func BenchmarkAtomicLoad64(b *testing.B) {
+	var x uint64
+	sink = &x
+	for i := 0; i < b.N; i++ {
+		_ = atomic.Load64(&x)
+	}
+}
+
+func BenchmarkAtomicStore64(b *testing.B) {
+	var x uint64
+	sink = &x
+	for i := 0; i < b.N; i++ {
+		atomic.Store64(&x, 0)
+	}
+}
diff --git a/src/runtime/internal/atomic/sys_nacl_arm.s b/src/runtime/internal/atomic/sys_nacl_arm.s
index efa9604..bdc1dd6 100644
--- a/src/runtime/internal/atomic/sys_nacl_arm.s
+++ b/src/runtime/internal/atomic/sys_nacl_arm.s
@@ -4,9 +4,6 @@
 
 #include "textflag.h"
 
-TEXT runtime∕internal∕atomic·Casp(SB),NOSPLIT,$0
-	B	runtime·cas(SB)
-
 // This is only valid for ARMv6+, however, NaCl/ARM is only defined
 // for ARMv7A anyway.
 TEXT runtime∕internal∕atomic·Cas(SB),NOSPLIT,$0
diff --git a/src/runtime/internal/sys/arch.go b/src/runtime/internal/sys/arch.go
index c175704..148e838 100644
--- a/src/runtime/internal/sys/arch.go
+++ b/src/runtime/internal/sys/arch.go
@@ -11,6 +11,7 @@ const (
 	ARM
 	ARM64
 	I386
+	MIPS
 	MIPS64
 	PPC64
 	S390X
diff --git a/src/runtime/internal/sys/arch_386.go b/src/runtime/internal/sys/arch_386.go
index 48c42f7..61d6722 100644
--- a/src/runtime/internal/sys/arch_386.go
+++ b/src/runtime/internal/sys/arch_386.go
@@ -5,14 +5,14 @@
 package sys
 
 const (
-	ArchFamily    = I386
-	BigEndian     = 0
-	CacheLineSize = 64
-	PhysPageSize  = GoosNacl*65536 + (1-GoosNacl)*4096 // 4k normally; 64k on NaCl
-	PCQuantum     = 1
-	Int64Align    = 4
-	HugePageSize  = 1 << 21
-	MinFrameSize  = 0
+	ArchFamily          = I386
+	BigEndian           = 0
+	CacheLineSize       = 64
+	DefaultPhysPageSize = GoosNacl*65536 + (1-GoosNacl)*4096 // 4k normally; 64k on NaCl
+	PCQuantum           = 1
+	Int64Align          = 4
+	HugePageSize        = 1 << 21
+	MinFrameSize        = 0
 )
 
 type Uintreg uint32
diff --git a/src/runtime/internal/sys/arch_amd64.go b/src/runtime/internal/sys/arch_amd64.go
index 1bbdb99..1f2114a 100644
--- a/src/runtime/internal/sys/arch_amd64.go
+++ b/src/runtime/internal/sys/arch_amd64.go
@@ -5,14 +5,14 @@
 package sys
 
 const (
-	ArchFamily    = AMD64
-	BigEndian     = 0
-	CacheLineSize = 64
-	PhysPageSize  = 4096
-	PCQuantum     = 1
-	Int64Align    = 8
-	HugePageSize  = 1 << 21
-	MinFrameSize  = 0
+	ArchFamily          = AMD64
+	BigEndian           = 0
+	CacheLineSize       = 64
+	DefaultPhysPageSize = 4096
+	PCQuantum           = 1
+	Int64Align          = 8
+	HugePageSize        = 1 << 21
+	MinFrameSize        = 0
 )
 
 type Uintreg uint64
diff --git a/src/runtime/internal/sys/arch_amd64p32.go b/src/runtime/internal/sys/arch_amd64p32.go
index b7011a4..0779855 100644
--- a/src/runtime/internal/sys/arch_amd64p32.go
+++ b/src/runtime/internal/sys/arch_amd64p32.go
@@ -5,14 +5,14 @@
 package sys
 
 const (
-	ArchFamily    = AMD64
-	BigEndian     = 0
-	CacheLineSize = 64
-	PhysPageSize  = 65536*GoosNacl + 4096*(1-GoosNacl)
-	PCQuantum     = 1
-	Int64Align    = 8
-	HugePageSize  = 1 << 21
-	MinFrameSize  = 0
+	ArchFamily          = AMD64
+	BigEndian           = 0
+	CacheLineSize       = 64
+	DefaultPhysPageSize = 65536*GoosNacl + 4096*(1-GoosNacl)
+	PCQuantum           = 1
+	Int64Align          = 8
+	HugePageSize        = 1 << 21
+	MinFrameSize        = 0
 )
 
 type Uintreg uint64
diff --git a/src/runtime/internal/sys/arch_arm.go b/src/runtime/internal/sys/arch_arm.go
index f90f52d..899010b 100644
--- a/src/runtime/internal/sys/arch_arm.go
+++ b/src/runtime/internal/sys/arch_arm.go
@@ -5,14 +5,14 @@
 package sys
 
 const (
-	ArchFamily    = ARM
-	BigEndian     = 0
-	CacheLineSize = 32
-	PhysPageSize  = 65536*GoosNacl + 4096*(1-GoosNacl)
-	PCQuantum     = 4
-	Int64Align    = 4
-	HugePageSize  = 0
-	MinFrameSize  = 4
+	ArchFamily          = ARM
+	BigEndian           = 0
+	CacheLineSize       = 32
+	DefaultPhysPageSize = 65536
+	PCQuantum           = 4
+	Int64Align          = 4
+	HugePageSize        = 0
+	MinFrameSize        = 4
 )
 
 type Uintreg uint32
diff --git a/src/runtime/internal/sys/arch_arm64.go b/src/runtime/internal/sys/arch_arm64.go
index aaaa4b0..2d57dda 100644
--- a/src/runtime/internal/sys/arch_arm64.go
+++ b/src/runtime/internal/sys/arch_arm64.go
@@ -5,14 +5,14 @@
 package sys
 
 const (
-	ArchFamily    = ARM64
-	BigEndian     = 0
-	CacheLineSize = 32
-	PhysPageSize  = 65536
-	PCQuantum     = 4
-	Int64Align    = 8
-	HugePageSize  = 0
-	MinFrameSize  = 8
+	ArchFamily          = ARM64
+	BigEndian           = 0
+	CacheLineSize       = 32
+	DefaultPhysPageSize = 65536
+	PCQuantum           = 4
+	Int64Align          = 8
+	HugePageSize        = 0
+	MinFrameSize        = 8
 )
 
 type Uintreg uint64
diff --git a/src/runtime/internal/sys/arch_mips.go b/src/runtime/internal/sys/arch_mips.go
new file mode 100644
index 0000000..65fc4f8
--- /dev/null
+++ b/src/runtime/internal/sys/arch_mips.go
@@ -0,0 +1,18 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sys
+
+const (
+	ArchFamily          = MIPS
+	BigEndian           = 1
+	CacheLineSize       = 32
+	DefaultPhysPageSize = 65536
+	PCQuantum           = 4
+	Int64Align          = 4
+	HugePageSize        = 0
+	MinFrameSize        = 4
+)
+
+type Uintreg uint32
diff --git a/src/runtime/internal/sys/arch_mips64.go b/src/runtime/internal/sys/arch_mips64.go
index d567259..0f6de74 100644
--- a/src/runtime/internal/sys/arch_mips64.go
+++ b/src/runtime/internal/sys/arch_mips64.go
@@ -5,14 +5,14 @@
 package sys
 
 const (
-	ArchFamily    = MIPS64
-	BigEndian     = 1
-	CacheLineSize = 32
-	PhysPageSize  = 16384
-	PCQuantum     = 4
-	Int64Align    = 8
-	HugePageSize  = 0
-	MinFrameSize  = 8
+	ArchFamily          = MIPS64
+	BigEndian           = 1
+	CacheLineSize       = 32
+	DefaultPhysPageSize = 16384
+	PCQuantum           = 4
+	Int64Align          = 8
+	HugePageSize        = 0
+	MinFrameSize        = 8
 )
 
 type Uintreg uint64
diff --git a/src/runtime/internal/sys/arch_mips64le.go b/src/runtime/internal/sys/arch_mips64le.go
index f8cdf2b..4ced35b 100644
--- a/src/runtime/internal/sys/arch_mips64le.go
+++ b/src/runtime/internal/sys/arch_mips64le.go
@@ -5,14 +5,14 @@
 package sys
 
 const (
-	ArchFamily    = MIPS64
-	BigEndian     = 0
-	CacheLineSize = 32
-	PhysPageSize  = 16384
-	PCQuantum     = 4
-	Int64Align    = 8
-	HugePageSize  = 0
-	MinFrameSize  = 8
+	ArchFamily          = MIPS64
+	BigEndian           = 0
+	CacheLineSize       = 32
+	DefaultPhysPageSize = 16384
+	PCQuantum           = 4
+	Int64Align          = 8
+	HugePageSize        = 0
+	MinFrameSize        = 8
 )
 
 type Uintreg uint64
diff --git a/src/runtime/internal/sys/arch_mipsle.go b/src/runtime/internal/sys/arch_mipsle.go
new file mode 100644
index 0000000..33e9764
--- /dev/null
+++ b/src/runtime/internal/sys/arch_mipsle.go
@@ -0,0 +1,18 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sys
+
+const (
+	ArchFamily          = MIPS
+	BigEndian           = 0
+	CacheLineSize       = 32
+	DefaultPhysPageSize = 65536
+	PCQuantum           = 4
+	Int64Align          = 4
+	HugePageSize        = 0
+	MinFrameSize        = 4
+)
+
+type Uintreg uint32
diff --git a/src/runtime/internal/sys/arch_ppc64.go b/src/runtime/internal/sys/arch_ppc64.go
index cdec63f..80595ee 100644
--- a/src/runtime/internal/sys/arch_ppc64.go
+++ b/src/runtime/internal/sys/arch_ppc64.go
@@ -5,14 +5,14 @@
 package sys
 
 const (
-	ArchFamily    = PPC64
-	BigEndian     = 1
-	CacheLineSize = 64
-	PhysPageSize  = 65536
-	PCQuantum     = 4
-	Int64Align    = 8
-	HugePageSize  = 0
-	MinFrameSize  = 32
+	ArchFamily          = PPC64
+	BigEndian           = 1
+	CacheLineSize       = 128
+	DefaultPhysPageSize = 65536
+	PCQuantum           = 4
+	Int64Align          = 8
+	HugePageSize        = 0
+	MinFrameSize        = 32
 )
 
 type Uintreg uint64
diff --git a/src/runtime/internal/sys/arch_ppc64le.go b/src/runtime/internal/sys/arch_ppc64le.go
index 4fd68f9..f68e777 100644
--- a/src/runtime/internal/sys/arch_ppc64le.go
+++ b/src/runtime/internal/sys/arch_ppc64le.go
@@ -5,14 +5,14 @@
 package sys
 
 const (
-	ArchFamily    = PPC64
-	BigEndian     = 0
-	CacheLineSize = 64
-	PhysPageSize  = 65536
-	PCQuantum     = 4
-	Int64Align    = 8
-	HugePageSize  = 0
-	MinFrameSize  = 32
+	ArchFamily          = PPC64
+	BigEndian           = 0
+	CacheLineSize       = 128
+	DefaultPhysPageSize = 65536
+	PCQuantum           = 4
+	Int64Align          = 8
+	HugePageSize        = 0
+	MinFrameSize        = 32
 )
 
 type Uintreg uint64
diff --git a/src/runtime/internal/sys/arch_s390x.go b/src/runtime/internal/sys/arch_s390x.go
index ca1cb86..4ec4bf8 100644
--- a/src/runtime/internal/sys/arch_s390x.go
+++ b/src/runtime/internal/sys/arch_s390x.go
@@ -5,14 +5,14 @@
 package sys
 
 const (
-	ArchFamily    = S390X
-	BigEndian     = 1
-	CacheLineSize = 256
-	PhysPageSize  = 4096
-	PCQuantum     = 2
-	Int64Align    = 8
-	HugePageSize  = 0
-	MinFrameSize  = 8
+	ArchFamily          = S390X
+	BigEndian           = 1
+	CacheLineSize       = 256
+	DefaultPhysPageSize = 4096
+	PCQuantum           = 2
+	Int64Align          = 8
+	HugePageSize        = 0
+	MinFrameSize        = 8
 )
 
 type Uintreg uint64
diff --git a/src/runtime/internal/sys/intrinsics.go b/src/runtime/internal/sys/intrinsics.go
index 08a062f..db2cbec 100644
--- a/src/runtime/internal/sys/intrinsics.go
+++ b/src/runtime/internal/sys/intrinsics.go
@@ -30,19 +30,6 @@ var deBruijnIdx32 = [32]byte{
 	30, 9, 19, 24, 29, 18, 28, 27,
 }
 
-const deBruijn16 = 0x09af
-
-var deBruijnIdx16 = [16]byte{
-	0, 1, 2, 5, 3, 9, 6, 11,
-	15, 4, 8, 10, 14, 7, 13, 12,
-}
-
-const deBruijn8 = 0x17
-
-var deBruijnIdx8 = [8]byte{
-	0, 1, 2, 4, 7, 3, 6, 5,
-}
-
 // Ctz64 counts trailing (low-order) zeroes,
 // and if all are zero, then 64.
 func Ctz64(x uint64) uint64 {
@@ -63,26 +50,6 @@ func Ctz32(x uint32) uint32 {
 	return y + z
 }
 
-// Ctz16 counts trailing (low-order) zeroes,
-// and if all are zero, then 16.
-func Ctz16(x uint16) uint16 {
-	x &= -x                      // isolate low-order bit
-	y := x * deBruijn16 >> 12    // extract part of deBruijn sequence
-	y = uint16(deBruijnIdx16[y]) // convert to bit index
-	z := (x - 1) >> 11 & 16      // adjustment if zero
-	return y + z
-}
-
-// Ctz8 counts trailing (low-order) zeroes,
-// and if all are zero, then 8.
-func Ctz8(x uint8) uint8 {
-	x &= -x                    // isolate low-order bit
-	y := x * deBruijn8 >> 5    // extract part of deBruijn sequence
-	y = uint8(deBruijnIdx8[y]) // convert to bit index
-	z := (x - 1) >> 4 & 8      // adjustment if zero
-	return y + z
-}
-
 // Bswap64 returns its input with byte order reversed
 // 0x0102030405060708 -> 0x0807060504030201
 func Bswap64(x uint64) uint64 {
diff --git a/src/runtime/internal/sys/intrinsics_386.s b/src/runtime/internal/sys/intrinsics_386.s
index 1f48e26..bc63e5e 100644
--- a/src/runtime/internal/sys/intrinsics_386.s
+++ b/src/runtime/internal/sys/intrinsics_386.s
@@ -36,22 +36,6 @@ TEXT runtime∕internal∕sys·Ctz32(SB), NOSPLIT, $0-8
 	MOVL	AX, ret+4(FP)
 	RET
 
-TEXT runtime∕internal∕sys·Ctz16(SB), NOSPLIT, $0-6
-	MOVW	x+0(FP), AX
-	BSFW	AX, AX
-	JNZ	2(PC)
-	MOVW	$16, AX
-	MOVW	AX, ret+4(FP)
-	RET
-
-TEXT runtime∕internal∕sys·Ctz8(SB), NOSPLIT, $0-5
-	MOVBLZX	x+0(FP), AX
-	BSFL	AX, AX
-	JNZ	2(PC)
-	MOVB	$8, AX
-	MOVB	AX, ret+4(FP)
-	RET
-
 TEXT runtime∕internal∕sys·Bswap64(SB), NOSPLIT, $0-16
 	MOVL	x_lo+0(FP), AX
 	MOVL	x_hi+4(FP), BX
diff --git a/src/runtime/internal/sys/intrinsics_stubs.go b/src/runtime/internal/sys/intrinsics_stubs.go
index 079844f..d351048 100644
--- a/src/runtime/internal/sys/intrinsics_stubs.go
+++ b/src/runtime/internal/sys/intrinsics_stubs.go
@@ -8,7 +8,5 @@ package sys
 
 func Ctz64(x uint64) uint64
 func Ctz32(x uint32) uint32
-func Ctz16(x uint16) uint16
-func Ctz8(x uint8) uint8
 func Bswap64(x uint64) uint64
 func Bswap32(x uint32) uint32
diff --git a/src/runtime/internal/sys/intrinsics_test.go b/src/runtime/internal/sys/intrinsics_test.go
index 097631b..1f2c8da 100644
--- a/src/runtime/internal/sys/intrinsics_test.go
+++ b/src/runtime/internal/sys/intrinsics_test.go
@@ -21,22 +21,6 @@ func TestCtz32(t *testing.T) {
 		}
 	}
 }
-func TestCtz16(t *testing.T) {
-	for i := uint(0); i <= 16; i++ {
-		x := uint16(5) << i
-		if got := sys.Ctz16(x); got != uint16(i) {
-			t.Errorf("Ctz16(%d)=%d, want %d", x, got, i)
-		}
-	}
-}
-func TestCtz8(t *testing.T) {
-	for i := uint(0); i <= 8; i++ {
-		x := uint8(5) << i
-		if got := sys.Ctz8(x); got != uint8(i) {
-			t.Errorf("Ctz8(%d)=%d, want %d", x, got, i)
-		}
-	}
-}
 
 func TestBswap64(t *testing.T) {
 	x := uint64(0x1122334455667788)
diff --git a/src/runtime/internal/sys/zgoarch_mips.go b/src/runtime/internal/sys/zgoarch_mips.go
new file mode 100644
index 0000000..2f733d2
--- /dev/null
+++ b/src/runtime/internal/sys/zgoarch_mips.go
@@ -0,0 +1,26 @@
+// generated by gengoos.go using 'go generate'
+
+package sys
+
+const GOARCH = `mips`
+
+const Goarch386 = 0
+const GoarchAmd64 = 0
+const GoarchAmd64p32 = 0
+const GoarchArm = 0
+const GoarchArmbe = 0
+const GoarchArm64 = 0
+const GoarchArm64be = 0
+const GoarchPpc64 = 0
+const GoarchPpc64le = 0
+const GoarchMips = 1
+const GoarchMipsle = 0
+const GoarchMips64 = 0
+const GoarchMips64le = 0
+const GoarchMips64p32 = 0
+const GoarchMips64p32le = 0
+const GoarchPpc = 0
+const GoarchS390 = 0
+const GoarchS390x = 0
+const GoarchSparc = 0
+const GoarchSparc64 = 0
diff --git a/src/runtime/internal/sys/zgoarch_mipsle.go b/src/runtime/internal/sys/zgoarch_mipsle.go
new file mode 100644
index 0000000..95f3d5a
--- /dev/null
+++ b/src/runtime/internal/sys/zgoarch_mipsle.go
@@ -0,0 +1,26 @@
+// generated by gengoos.go using 'go generate'
+
+package sys
+
+const GOARCH = `mipsle`
+
+const Goarch386 = 0
+const GoarchAmd64 = 0
+const GoarchAmd64p32 = 0
+const GoarchArm = 0
+const GoarchArmbe = 0
+const GoarchArm64 = 0
+const GoarchArm64be = 0
+const GoarchPpc64 = 0
+const GoarchPpc64le = 0
+const GoarchMips = 0
+const GoarchMipsle = 1
+const GoarchMips64 = 0
+const GoarchMips64le = 0
+const GoarchMips64p32 = 0
+const GoarchMips64p32le = 0
+const GoarchPpc = 0
+const GoarchS390 = 0
+const GoarchS390x = 0
+const GoarchSparc = 0
+const GoarchSparc64 = 0
diff --git a/src/runtime/lfstack_32bit.go b/src/runtime/lfstack_32bit.go
index 2f59e02..d36ca50 100644
--- a/src/runtime/lfstack_32bit.go
+++ b/src/runtime/lfstack_32bit.go
@@ -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 386 arm nacl
+// +build 386 arm nacl mips mipsle
 
 package runtime
 
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index b079a07..1c9efc3 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -2,80 +2,81 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Memory allocator, based on tcmalloc.
+// Memory allocator.
+//
+// This was originally based on tcmalloc, but has diverged quite a bit.
 // http://goog-perftools.sourceforge.net/doc/tcmalloc.html
 
 // The main allocator works in runs of pages.
 // Small allocation sizes (up to and including 32 kB) are
-// rounded to one of about 100 size classes, each of which
-// has its own free list of objects of exactly that size.
+// rounded to one of about 70 size classes, each of which
+// has its own free set of objects of exactly that size.
 // Any free page of memory can be split into a set of objects
-// of one size class, which are then managed using free list
-// allocators.
+// of one size class, which are then managed using a free bitmap.
 //
 // The allocator's data structures are:
 //
-//	FixAlloc: a free-list allocator for fixed-size objects,
+//	fixalloc: a free-list allocator for fixed-size off-heap objects,
 //		used to manage storage used by the allocator.
-//	MHeap: the malloc heap, managed at page (4096-byte) granularity.
-//	MSpan: a run of pages managed by the MHeap.
-//	MCentral: a shared free list for a given size class.
-//	MCache: a per-thread (in Go, per-P) cache for small objects.
-//	MStats: allocation statistics.
+//	mheap: the malloc heap, managed at page (8192-byte) granularity.
+//	mspan: a run of pages managed by the mheap.
+//	mcentral: collects all spans of a given size class.
+//	mcache: a per-P cache of mspans with free space.
+//	mstats: allocation statistics.
 //
 // Allocating a small object proceeds up a hierarchy of caches:
 //
 //	1. Round the size up to one of the small size classes
-//	   and look in the corresponding MCache free list.
-//	   If the list is not empty, allocate an object from it.
+//	   and look in the corresponding mspan in this P's mcache.
+//	   Scan the mspan's free bitmap to find a free slot.
+//	   If there is a free slot, allocate it.
 //	   This can all be done without acquiring a lock.
 //
-//	2. If the MCache free list is empty, replenish it by
-//	   taking a bunch of objects from the MCentral free list.
-//	   Moving a bunch amortizes the cost of acquiring the MCentral lock.
+//	2. If the mspan has no free slots, obtain a new mspan
+//	   from the mcentral's list of mspans of the required size
+//	   class that have free space.
+//	   Obtaining a whole span amortizes the cost of locking
+//	   the mcentral.
 //
-//	3. If the MCentral free list is empty, replenish it by
-//	   allocating a run of pages from the MHeap and then
-//	   chopping that memory into objects of the given size.
-//	   Allocating many objects amortizes the cost of locking
-//	   the heap.
+//	3. If the mcentral's mspan list is empty, obtain a run
+//	   of pages from the mheap to use for the mspan.
 //
-//	4. If the MHeap is empty or has no page runs large enough,
+//	4. If the mheap is empty or has no page runs large enough,
 //	   allocate a new group of pages (at least 1MB) from the
-//	   operating system.  Allocating a large run of pages
+//	   operating system. Allocating a large run of pages
 //	   amortizes the cost of talking to the operating system.
 //
-// Freeing a small object proceeds up the same hierarchy:
+// Sweeping an mspan and freeing objects on it proceeds up a similar
+// hierarchy:
+//
+//	1. If the mspan is being swept in response to allocation, it
+//	   is returned to the mcache to satisfy the allocation.
 //
-//	1. Look up the size class for the object and add it to
-//	   the MCache free list.
+//	2. Otherwise, if the mspan still has allocated objects in it,
+//	   it is placed on the mcentral free list for the mspan's size
+//	   class.
 //
-//	2. If the MCache free list is too long or the MCache has
-//	   too much memory, return some to the MCentral free lists.
+//	3. Otherwise, if all objects in the mspan are free, the mspan
+//	   is now "idle", so it is returned to the mheap and no longer
+//	   has a size class.
+//	   This may coalesce it with adjacent idle mspans.
 //
-//	3. If all the objects in a given span have returned to
-//	   the MCentral list, return that span to the page heap.
+//	4. If an mspan remains idle for long enough, return its pages
+//	   to the operating system.
 //
-//	4. If the heap has too much memory, return some to the
-//	   operating system.
+// Allocating and freeing a large object uses the mheap
+// directly, bypassing the mcache and mcentral.
 //
-//	TODO(rsc): Step 4 is not implemented.
+// Free object slots in an mspan are zeroed only if mspan.needzero is
+// false. If needzero is true, objects are zeroed as they are
+// allocated. There are various benefits to delaying zeroing this way:
 //
-// Allocating and freeing a large object uses the page heap
-// directly, bypassing the MCache and MCentral free lists.
+//	1. Stack frame allocation can avoid zeroing altogether.
 //
-// The small objects on the MCache and MCentral free lists
-// may or may not be zeroed. They are zeroed if and only if
-// the second word of the object is zero. A span in the
-// page heap is zeroed unless s->needzero is set. When a span
-// is allocated to break into small objects, it is zeroed if needed
-// and s->needzero is set. There are two main benefits to delaying the
-// zeroing this way:
+//	2. It exhibits better temporal locality, since the program is
+//	   probably about to write to the memory.
 //
-//	1. stack frames allocated from the small object lists
-//	   or the page heap can avoid zeroing altogether.
-//	2. the cost of zeroing when reusing a small object is
-//	   charged to the mutator, not the garbage collector.
+//	3. We don't zero pages that never get reused.
 
 package runtime
 
@@ -101,28 +102,13 @@ const (
 	mSpanInUse = _MSpanInUse
 
 	concurrentSweep = _ConcurrentSweep
-)
 
-const (
-	_PageShift = 13
-	_PageSize  = 1 << _PageShift
-	_PageMask  = _PageSize - 1
-)
+	_PageSize = 1 << _PageShift
+	_PageMask = _PageSize - 1
 
-const (
 	// _64bit = 1 on 64-bit systems, 0 on 32-bit systems
 	_64bit = 1 << (^uintptr(0) >> 63) / 2
 
-	// Computed constant. The definition of MaxSmallSize and the
-	// algorithm in msize.go produces some number of different allocation
-	// size classes. NumSizeClasses is that number. It's needed here
-	// because there are static arrays of this length; when msize runs its
-	// size choosing algorithm it double-checks that NumSizeClasses agrees.
-	_NumSizeClasses = 67
-
-	// Tunable constants.
-	_MaxSmallSize = 32 << 10
-
 	// Tiny allocator parameters, see "Tiny allocator" comment in malloc.go.
 	_TinySize      = 16
 	_TinySizeClass = 2
@@ -155,10 +141,11 @@ const (
 	// See https://golang.org/issue/5402 and https://golang.org/issue/5236.
 	// On other 64-bit platforms, we limit the arena to 512GB, or 39 bits.
 	// On 32-bit, we don't bother limiting anything, so we use the full 32-bit address.
+	// The only exception is mips32 which only has access to low 2GB of virtual memory.
 	// On Darwin/arm64, we cannot reserve more than ~5GB of virtual memory,
 	// but as most devices have less than 4GB of physical memory anyway, we
 	// try to be conservative here, and only ask for a 2GB heap.
-	_MHeapMap_TotalBits = (_64bit*sys.GoosWindows)*35 + (_64bit*(1-sys.GoosWindows)*(1-sys.GoosDarwin*sys.GoarchArm64))*39 + sys.GoosDarwin*sys.GoarchArm64*31 + (1-_64bit)*32
+	_MHeapMap_TotalBits = (_64bit*sys.GoosWindows)*35 + (_64bit*(1-sys.GoosWindows)*(1-sys.GoosDarwin*sys.GoarchArm64))*39 + sys.GoosDarwin*sys.GoarchArm64*31 + (1-_64bit)*(32-(sys.GoarchMips+sys.GoarchMipsle))
 	_MHeapMap_Bits      = _MHeapMap_TotalBits - _PageShift
 
 	_MaxMem = uintptr(1<<_MHeapMap_TotalBits - 1)
@@ -168,9 +155,17 @@ const (
 	// on the hardware details of the machine. The garbage
 	// collector scales well to 32 cpus.
 	_MaxGcproc = 32
+
+	_MaxArena32 = 1<<32 - 1
 )
 
-const _MaxArena32 = 1<<32 - 1
+// physPageSize is the size in bytes of the OS's physical pages.
+// Mapping and unmapping operations must be done at multiples of
+// physPageSize.
+//
+// This must be set by the OS init code (typically in osinit) before
+// mallocinit.
+var physPageSize uintptr
 
 // OS-defined helpers:
 //
@@ -211,12 +206,31 @@ const _MaxArena32 = 1<<32 - 1
 // if accessed. Used only for debugging the runtime.
 
 func mallocinit() {
-	initSizes()
-
 	if class_to_size[_TinySizeClass] != _TinySize {
 		throw("bad TinySizeClass")
 	}
 
+	testdefersizes()
+
+	// Copy class sizes out for statistics table.
+	for i := range class_to_size {
+		memstats.by_size[i].size = uint32(class_to_size[i])
+	}
+
+	// Check physPageSize.
+	if physPageSize == 0 {
+		// The OS init code failed to fetch the physical page size.
+		throw("failed to get system page size")
+	}
+	if physPageSize < minPhysPageSize {
+		print("system page size (", physPageSize, ") is smaller than minimum page size (", minPhysPageSize, ")\n")
+		throw("bad system page size")
+	}
+	if physPageSize&(physPageSize-1) != 0 {
+		print("system page size (", physPageSize, ") must be a power of 2\n")
+		throw("bad system page size")
+	}
+
 	var p, bitmapSize, spansSize, pSize, limit uintptr
 	var reserved bool
 
@@ -337,7 +351,7 @@ func mallocinit() {
 	// To overcome this we ask for PageSize more and round up the pointer.
 	p1 := round(p, _PageSize)
 
-	mheap_.spans = (**mspan)(unsafe.Pointer(p1))
+	spansStart := p1
 	mheap_.bitmap = p1 + spansSize + bitmapSize
 	if sys.PtrSize == 4 {
 		// Set arena_start such that we can accept memory
@@ -356,7 +370,7 @@ func mallocinit() {
 	}
 
 	// Initialize the rest of the allocator.
-	mheap_.init(spansSize)
+	mheap_.init(spansStart, spansSize)
 	_g_ := getg()
 	_g_.m.mcache = allocmcache()
 }
@@ -491,7 +505,7 @@ func nextFreeFast(s *mspan) gclinkptr {
 // weight allocation. If it is a heavy weight allocation the caller must
 // determine whether a new GC cycle needs to be started or if the GC is active
 // whether this goroutine needs to assist the GC.
-func (c *mcache) nextFree(sizeclass int8) (v gclinkptr, s *mspan, shouldhelpgc bool) {
+func (c *mcache) nextFree(sizeclass uint8) (v gclinkptr, s *mspan, shouldhelpgc bool) {
 	s = c.alloc[sizeclass]
 	shouldhelpgc = false
 	freeIndex := s.nextFreeIndex()
@@ -645,11 +659,11 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
 			}
 			size = maxTinySize
 		} else {
-			var sizeclass int8
-			if size <= 1024-8 {
-				sizeclass = size_to_class8[(size+7)>>3]
+			var sizeclass uint8
+			if size <= smallSizeMax-8 {
+				sizeclass = size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv]
 			} else {
-				sizeclass = size_to_class128[(size-1024+127)>>7]
+				sizeclass = size_to_class128[(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv]
 			}
 			size = uintptr(class_to_size[sizeclass])
 			span := c.alloc[sizeclass]
@@ -659,7 +673,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
 			}
 			x = unsafe.Pointer(v)
 			if needzero && span.needzero != 0 {
-				memclr(unsafe.Pointer(v), size)
+				memclrNoHeapPointers(unsafe.Pointer(v), size)
 			}
 		}
 	} else {
@@ -781,6 +795,8 @@ func largeAlloc(size uintptr, needzero bool) *mspan {
 }
 
 // implementation of new builtin
+// compiler (both frontend and SSA backend) knows the signature
+// of this function
 func newobject(typ *_type) unsafe.Pointer {
 	return mallocgc(typ.size, typ, true)
 }
@@ -841,7 +857,7 @@ func nextSample() int32 {
 	// x = -log_e(q) * period
 	// x = log_2(q) * (-log_e(2)) * period    ; Using log_2 for efficiency
 	const randomBitCount = 26
-	q := fastrand1()%(1<<randomBitCount) + 1
+	q := fastrand()%(1<<randomBitCount) + 1
 	qlog := fastlog2(float64(q)) - randomBitCount
 	if qlog > 0 {
 		qlog = 0
@@ -859,7 +875,7 @@ func nextSampleNoFP() int32 {
 		rate = 0x3fffffff
 	}
 	if rate != 0 {
-		return int32(int(fastrand1()) % (2 * rate))
+		return int32(int(fastrand()) % (2 * rate))
 	}
 	return 0
 }
@@ -878,6 +894,9 @@ var globalAlloc struct {
 // There is no associated free operation.
 // Intended for things like function/type/debug-related persistent data.
 // If align is 0, uses default align (currently 8).
+// The returned memory will be zeroed.
+//
+// Consider marking persistentalloc'd types go:notinheap.
 func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer {
 	var p unsafe.Pointer
 	systemstack(func() {
diff --git a/src/runtime/map_test.go b/src/runtime/map_test.go
index 496f8e8..aacd091 100644
--- a/src/runtime/map_test.go
+++ b/src/runtime/map_test.go
@@ -235,6 +235,7 @@ func TestIterGrowWithGC(t *testing.T) {
 }
 
 func testConcurrentReadsAfterGrowth(t *testing.T, useReflect bool) {
+	t.Parallel()
 	if runtime.GOMAXPROCS(-1) == 1 {
 		defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(16))
 	}
diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index 4a8f501..5848b43 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -7,7 +7,7 @@
 // For the concurrent garbage collector, the Go compiler implements
 // updates to pointer-valued fields that may be in heap objects by
 // emitting calls to write barriers. This file contains the actual write barrier
-// implementation, markwb, and the various wrappers called by the
+// implementation, gcmarkwb_m, and the various wrappers called by the
 // compiler to implement pointer assignment, slice assignment,
 // typed memmove, and so on.
 
@@ -18,29 +18,60 @@ import (
 	"unsafe"
 )
 
-// markwb is the mark-phase write barrier, the only barrier we have.
+// gcmarkwb_m is the mark-phase write barrier, the only barrier we have.
 // The rest of this file exists only to make calls to this function.
 //
-// This is the Dijkstra barrier coarsened to always shade the ptr (dst) object.
-// The original Dijkstra barrier only shaded ptrs being placed in black slots.
+// This is a hybrid barrier that combines a Yuasa-style deletion
+// barrier—which shades the object whose reference is being
+// overwritten—with Dijkstra insertion barrier—which shades the object
+// whose reference is being written. The insertion part of the barrier
+// is necessary while the calling goroutine's stack is grey. In
+// pseudocode, the barrier is:
+//
+//     writePointer(slot, ptr):
+//         shade(*slot)
+//         if current stack is grey:
+//             shade(ptr)
+//         *slot = ptr
+//
+// slot is the destination in Go code.
+// ptr is the value that goes into the slot in Go code.
 //
 // Shade indicates that it has seen a white pointer by adding the referent
 // to wbuf as well as marking it.
 //
-// slot is the destination (dst) in go code
-// ptr is the value that goes into the slot (src) in the go code
+// The two shades and the condition work together to prevent a mutator
+// from hiding an object from the garbage collector:
+//
+// 1. shade(*slot) prevents a mutator from hiding an object by moving
+// the sole pointer to it from the heap to its stack. If it attempts
+// to unlink an object from the heap, this will shade it.
+//
+// 2. shade(ptr) prevents a mutator from hiding an object by moving
+// the sole pointer to it from its stack into a black object in the
+// heap. If it attempts to install the pointer into a black object,
+// this will shade it.
+//
+// 3. Once a goroutine's stack is black, the shade(ptr) becomes
+// unnecessary. shade(ptr) prevents hiding an object by moving it from
+// the stack to the heap, but this requires first having a pointer
+// hidden on the stack. Immediately after a stack is scanned, it only
+// points to shaded objects, so it's not hiding anything, and the
+// shade(*slot) prevents it from hiding any other pointers on its
+// stack.
+//
+// For a detailed description of this barrier and proof of
+// correctness, see https://github.com/golang/proposal/blob/master/design/17503-eliminate-rescan.md
+//
 //
 //
 // Dealing with memory ordering:
 //
-// Dijkstra pointed out that maintaining the no black to white
-// pointers means that white to white pointers do not need
-// to be noted by the write barrier. Furthermore if either
-// white object dies before it is reached by the
-// GC then the object can be collected during this GC cycle
-// instead of waiting for the next cycle. Unfortunately the cost of
-// ensuring that the object holding the slot doesn't concurrently
-// change to black without the mutator noticing seems prohibitive.
+// Both the Yuasa and Dijkstra barriers can be made conditional on the
+// color of the object containing the slot. We chose not to make these
+// conditional because the cost of ensuring that the object holding
+// the slot doesn't concurrently change color without the mutator
+// noticing seems prohibitive.
 //
 // Consider the following example where the mutator writes into
 // a slot and then loads the slot's mark bit while the GC thread
@@ -98,15 +129,41 @@ import (
 // barriers for writes to globals so that we don't have to rescan
 // global during mark termination.
 //
+//
+// Publication ordering:
+//
+// The write barrier is *pre-publication*, meaning that the write
+// barrier happens prior to the *slot = ptr write that may make ptr
+// reachable by some goroutine that currently cannot reach it.
+//
+//
 //go:nowritebarrierrec
+//go:systemstack
 func gcmarkwb_m(slot *uintptr, ptr uintptr) {
 	if writeBarrier.needed {
+		// Note: This turns bad pointer writes into bad
+		// pointer reads, which could be confusing. We avoid
+		// reading from obviously bad pointers, which should
+		// take care of the vast majority of these. We could
+		// patch this up in the signal handler, or use XCHG to
+		// combine the read and the write. Checking inheap is
+		// insufficient since we need to track changes to
+		// roots outside the heap.
+		if slot1 := uintptr(unsafe.Pointer(slot)); slot1 >= minPhysPageSize {
+			if optr := *slot; optr != 0 {
+				shade(optr)
+			}
+		}
+		// TODO: Make this conditional on the caller's stack color.
 		if ptr != 0 && inheap(ptr) {
 			shade(ptr)
 		}
 	}
 }
 
+// writebarrierptr_prewrite1 invokes a write barrier for *dst = src
+// prior to the write happening.
+//
 // Write barrier calls must not happen during critical GC and scheduler
 // related operations. In particular there are times when the GC assumes
 // that the world is stopped but scheduler related code is still being
@@ -117,7 +174,7 @@ func gcmarkwb_m(slot *uintptr, ptr uintptr) {
 // that we are in one these critical section and throw if the write is of
 // a pointer to a heap object.
 //go:nosplit
-func writebarrierptr_nostore1(dst *uintptr, src uintptr) {
+func writebarrierptr_prewrite1(dst *uintptr, src uintptr) {
 	mp := acquirem()
 	if mp.inwb || mp.dying > 0 {
 		releasem(mp)
@@ -125,7 +182,7 @@ func writebarrierptr_nostore1(dst *uintptr, src uintptr) {
 	}
 	systemstack(func() {
 		if mp.p == 0 && memstats.enablegc && !mp.inwb && inheap(src) {
-			throw("writebarrierptr_nostore1 called with mp.p == nil")
+			throw("writebarrierptr_prewrite1 called with mp.p == nil")
 		}
 		mp.inwb = true
 		gcmarkwb_m(dst, src)
@@ -138,11 +195,11 @@ func writebarrierptr_nostore1(dst *uintptr, src uintptr) {
 // but if we do that, Go inserts a write barrier on *dst = src.
 //go:nosplit
 func writebarrierptr(dst *uintptr, src uintptr) {
-	*dst = src
 	if writeBarrier.cgo {
 		cgoCheckWriteBarrier(dst, src)
 	}
 	if !writeBarrier.needed {
+		*dst = src
 		return
 	}
 	if src != 0 && src < minPhysPageSize {
@@ -151,13 +208,16 @@ func writebarrierptr(dst *uintptr, src uintptr) {
 			throw("bad pointer in write barrier")
 		})
 	}
-	writebarrierptr_nostore1(dst, src)
+	writebarrierptr_prewrite1(dst, src)
+	*dst = src
 }
 
-// Like writebarrierptr, but the store has already been applied.
-// Do not reapply.
+// writebarrierptr_prewrite is like writebarrierptr, but the store
+// will be performed by the caller after this call. The caller must
+// not allow preemption between this call and the write.
+//
 //go:nosplit
-func writebarrierptr_nostore(dst *uintptr, src uintptr) {
+func writebarrierptr_prewrite(dst *uintptr, src uintptr) {
 	if writeBarrier.cgo {
 		cgoCheckWriteBarrier(dst, src)
 	}
@@ -167,24 +227,38 @@ func writebarrierptr_nostore(dst *uintptr, src uintptr) {
 	if src != 0 && src < minPhysPageSize {
 		systemstack(func() { throw("bad pointer in write barrier") })
 	}
-	writebarrierptr_nostore1(dst, src)
+	writebarrierptr_prewrite1(dst, src)
 }
 
 // typedmemmove copies a value of type t to dst from src.
 //go:nosplit
 func typedmemmove(typ *_type, dst, src unsafe.Pointer) {
+	if typ.kind&kindNoPointers == 0 {
+		bulkBarrierPreWrite(uintptr(dst), uintptr(src), typ.size)
+	}
+	// There's a race here: if some other goroutine can write to
+	// src, it may change some pointer in src after we've
+	// performed the write barrier but before we perform the
+	// memory copy. This safe because the write performed by that
+	// other goroutine must also be accompanied by a write
+	// barrier, so at worst we've unnecessarily greyed the old
+	// pointer that was in src.
 	memmove(dst, src, typ.size)
 	if writeBarrier.cgo {
 		cgoCheckMemmove(typ, dst, src, 0, typ.size)
 	}
-	if typ.kind&kindNoPointers != 0 {
-		return
-	}
-	heapBitsBulkBarrier(uintptr(dst), typ.size)
 }
 
 //go:linkname reflect_typedmemmove reflect.typedmemmove
 func reflect_typedmemmove(typ *_type, dst, src unsafe.Pointer) {
+	if raceenabled {
+		raceWriteObjectPC(typ, dst, getcallerpc(unsafe.Pointer(&typ)), funcPC(reflect_typedmemmove))
+		raceReadObjectPC(typ, src, getcallerpc(unsafe.Pointer(&typ)), funcPC(reflect_typedmemmove))
+	}
+	if msanenabled {
+		msanwrite(dst, typ.size)
+		msanread(src, typ.size)
+	}
 	typedmemmove(typ, dst, src)
 }
 
@@ -192,34 +266,38 @@ func reflect_typedmemmove(typ *_type, dst, src unsafe.Pointer) {
 // dst and src point off bytes into the value and only copies size bytes.
 //go:linkname reflect_typedmemmovepartial reflect.typedmemmovepartial
 func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
+	if writeBarrier.needed && typ.kind&kindNoPointers == 0 && size >= sys.PtrSize {
+		// Pointer-align start address for bulk barrier.
+		adst, asrc, asize := dst, src, size
+		if frag := -off & (sys.PtrSize - 1); frag != 0 {
+			adst = add(dst, frag)
+			asrc = add(src, frag)
+			asize -= frag
+		}
+		bulkBarrierPreWrite(uintptr(adst), uintptr(asrc), asize&^(sys.PtrSize-1))
+	}
+
 	memmove(dst, src, size)
 	if writeBarrier.cgo {
 		cgoCheckMemmove(typ, dst, src, off, size)
 	}
-	if !writeBarrier.needed || typ.kind&kindNoPointers != 0 || size < sys.PtrSize {
-		return
-	}
-
-	if frag := -off & (sys.PtrSize - 1); frag != 0 {
-		dst = add(dst, frag)
-		size -= frag
-	}
-	heapBitsBulkBarrier(uintptr(dst), size&^(sys.PtrSize-1))
 }
 
-// callwritebarrier is invoked at the end of reflectcall, to execute
-// write barrier operations to record the fact that a call's return
-// values have just been copied to frame, starting at retoffset
-// and continuing to framesize. The entire frame (not just the return
-// values) is described by typ. Because the copy has already
-// happened, we call writebarrierptr_nostore, and this is nosplit so
-// the copy and write barrier appear atomic to GC.
+// reflectcallmove is invoked by reflectcall to copy the return values
+// out of the stack and into the heap, invoking the necessary write
+// barriers. dst, src, and size describe the return value area to
+// copy. typ describes the entire frame (not just the return values).
+// typ may be nil, which indicates write barriers are not needed.
+//
+// It must be nosplit and must only call nosplit functions because the
+// stack map of reflectcall is wrong.
+//
 //go:nosplit
-func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uintptr) {
-	if !writeBarrier.needed || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < sys.PtrSize {
-		return
+func reflectcallmove(typ *_type, dst, src unsafe.Pointer, size uintptr) {
+	if writeBarrier.needed && typ != nil && typ.kind&kindNoPointers == 0 && size >= sys.PtrSize {
+		bulkBarrierPreWrite(uintptr(dst), uintptr(src), size)
 	}
-	heapBitsBulkBarrier(uintptr(add(frame, retoffset)), framesize-retoffset)
+	memmove(dst, src, size)
 }
 
 //go:nosplit
@@ -300,8 +378,51 @@ func reflect_typedslicecopy(elemType *_type, dst, src slice) int {
 		if n > src.len {
 			n = src.len
 		}
-		memmove(dst.array, src.array, uintptr(n)*elemType.size)
+		if n == 0 {
+			return 0
+		}
+
+		size := uintptr(n) * elemType.size
+		if raceenabled {
+			callerpc := getcallerpc(unsafe.Pointer(&elemType))
+			pc := funcPC(reflect_typedslicecopy)
+			racewriterangepc(dst.array, size, callerpc, pc)
+			racereadrangepc(src.array, size, callerpc, pc)
+		}
+		if msanenabled {
+			msanwrite(dst.array, size)
+			msanread(src.array, size)
+		}
+
+		memmove(dst.array, src.array, size)
 		return n
 	}
 	return typedslicecopy(elemType, dst, src)
 }
+
+// typedmemclr clears the typed memory at ptr with type typ. The
+// memory at ptr must already be initialized (and hence in type-safe
+// state). If the memory is being initialized for the first time, see
+// memclrNoHeapPointers.
+//
+// If the caller knows that typ has pointers, it can alternatively
+// call memclrHasPointers.
+//
+//go:nosplit
+func typedmemclr(typ *_type, ptr unsafe.Pointer) {
+	if typ.kind&kindNoPointers == 0 {
+		bulkBarrierPreWrite(uintptr(ptr), 0, typ.size)
+	}
+	memclrNoHeapPointers(ptr, typ.size)
+}
+
+// memclrHasPointers clears n bytes of typed memory starting at ptr.
+// The caller must ensure that the type of the object at ptr has
+// pointers, usually by checking typ.kind&kindNoPointers. However, ptr
+// does not have to point to the start of the allocation.
+//
+//go:nosplit
+func memclrHasPointers(ptr unsafe.Pointer, n uintptr) {
+	bulkBarrierPreWrite(uintptr(ptr), 0, n)
+	memclrNoHeapPointers(ptr, n)
+}
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index ccefbcd..89d8a4c 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -78,13 +78,13 @@ import (
 
 const (
 	bitPointer = 1 << 0
-	bitMarked  = 1 << 4 // TODO: Rename bitScan.
+	bitScan    = 1 << 4
 
-	heapBitsShift   = 1                     // shift offset between successive bitPointer or bitMarked entries
+	heapBitsShift   = 1                     // shift offset between successive bitPointer or bitScan entries
 	heapBitmapScale = sys.PtrSize * (8 / 2) // number of data bytes described by one heap bitmap byte
 
-	// all mark/pointer bits in a byte
-	bitMarkedAll  = bitMarked | bitMarked<<heapBitsShift | bitMarked<<(2*heapBitsShift) | bitMarked<<(3*heapBitsShift)
+	// all scan/pointer bits in a byte
+	bitScanAll    = bitScan | bitScan<<heapBitsShift | bitScan<<(2*heapBitsShift) | bitScan<<(3*heapBitsShift)
 	bitPointerAll = bitPointer | bitPointer<<heapBitsShift | bitPointer<<(2*heapBitsShift) | bitPointer<<(3*heapBitsShift)
 )
 
@@ -151,7 +151,7 @@ func (h *mheap) mapBits(arena_used uintptr) {
 
 	n := (arena_used - mheap_.arena_start) / heapBitmapScale
 	n = round(n, bitmapChunk)
-	n = round(n, sys.PhysPageSize)
+	n = round(n, physPageSize)
 	if h.bitmap_mapped >= n {
 		return
 	}
@@ -264,7 +264,11 @@ func (s *mspan) nextFreeIndex() uintptr {
 	return result
 }
 
+// isFree returns whether the index'th object in s is unallocated.
 func (s *mspan) isFree(index uintptr) bool {
+	if index < s.freeindex {
+		return false
+	}
 	whichByte := index / 8
 	whichBit := index % 8
 	byteVal := *addb(s.allocBits, whichByte)
@@ -336,7 +340,7 @@ func (m markBits) clearMarkedNonAtomic() {
 // markBitsForSpan returns the markBits for the span base address base.
 func markBitsForSpan(base uintptr) (mbits markBits) {
 	if base < mheap_.arena_start || base >= mheap_.arena_used {
-		throw("heapBitsForSpan: base out of range")
+		throw("markBitsForSpan: base out of range")
 	}
 	mbits = markBitsForAddr(base)
 	if mbits.mask != 1 {
@@ -394,7 +398,7 @@ func heapBitsForObject(p, refBase, refOff uintptr) (base uintptr, hbits heapBits
 	idx := off >> _PageShift
 	// p points into the heap, but possibly to the middle of an object.
 	// Consult the span table to find the block beginning.
-	s = h_spans[idx]
+	s = mheap_.spans[idx]
 	if s == nil || p < s.base() || p >= s.limit || s.state != mSpanInUse {
 		if s == nil || s.state == _MSpanStack {
 			// If s is nil, the virtual address has never been part of the heap.
@@ -421,7 +425,7 @@ func heapBitsForObject(p, refBase, refOff uintptr) (base uintptr, hbits heapBits
 			} else {
 				print(" to unused region of span")
 			}
-			print("idx=", hex(idx), " span.base()=", hex(s.base()), " span.limit=", hex(s.limit), " span.state=", s.state, "\n")
+			print(" idx=", hex(idx), " span.base()=", hex(s.base()), " span.limit=", hex(s.limit), " span.state=", s.state, "\n")
 			if refBase != 0 {
 				print("runtime: found in object at *(", hex(refBase), "+", hex(refOff), ")\n")
 				gcDumpObject("object", refBase, refOff)
@@ -435,7 +439,7 @@ func heapBitsForObject(p, refBase, refOff uintptr) (base uintptr, hbits heapBits
 	if s.baseMask != 0 {
 		// optimize for power of 2 sized objects.
 		base = s.base()
-		base = base + (p-base)&s.baseMask
+		base = base + (p-base)&uintptr(s.baseMask)
 		objIndex = (base - s.base()) >> s.divShift
 		// base = p & s.baseMask is faster for small spans,
 		// but doesn't work for large spans.
@@ -444,7 +448,7 @@ func heapBitsForObject(p, refBase, refOff uintptr) (base uintptr, hbits heapBits
 		base = s.base()
 		if p-base >= s.elemsize {
 			// n := (p - base) / s.elemsize, using division by multiplication
-			objIndex = uintptr(uint64(p-base) >> s.divShift * uint64(s.divMul) >> s.divShift2)
+			objIndex = uintptr(p-base) >> s.divShift * uintptr(s.divMul) >> s.divShift2
 			base += objIndex * s.elemsize
 		}
 	}
@@ -481,7 +485,7 @@ func (h heapBits) forward(n uintptr) heapBits {
 	return heapBits{subtractb(h.bitp, n/4), uint32(n%4) * heapBitsShift}
 }
 
-// The caller can test isMarked and isPointer by &-ing with bitMarked and bitPointer.
+// The caller can test morePointers and isPointer by &-ing with bitScan and bitPointer.
 // The result includes in its higher bits the bits for subsequent words
 // described by the same bitmap byte.
 func (h heapBits) bits() uint32 {
@@ -494,7 +498,7 @@ func (h heapBits) bits() uint32 {
 // are scalars.
 // h must not describe the second word of the object.
 func (h heapBits) morePointers() bool {
-	return h.bits()&bitMarked != 0
+	return h.bits()&bitScan != 0
 }
 
 // isPointer reports whether the heap bits describe a pointer word.
@@ -512,7 +516,7 @@ func (h heapBits) hasPointers(size uintptr) bool {
 	if size == sys.PtrSize { // 1-word objects are always pointers
 		return true
 	}
-	return (*h.bitp>>h.shift)&bitMarked != 0
+	return (*h.bitp>>h.shift)&bitScan != 0
 }
 
 // isCheckmarked reports whether the heap bits have the checkmarked bit set.
@@ -527,7 +531,7 @@ func (h heapBits) isCheckmarked(size uintptr) bool {
 	// so we know that the initial word's 2-bit pair
 	// and the second word's 2-bit pair are in the
 	// same heap bitmap byte, *h.bitp.
-	return (*h.bitp>>(heapBitsShift+h.shift))&bitMarked != 0
+	return (*h.bitp>>(heapBitsShift+h.shift))&bitScan != 0
 }
 
 // setCheckmarked sets the checkmarked bit.
@@ -539,96 +543,114 @@ func (h heapBits) setCheckmarked(size uintptr) {
 		atomic.Or8(h.bitp, bitPointer<<h.shift)
 		return
 	}
-	atomic.Or8(h.bitp, bitMarked<<(heapBitsShift+h.shift))
+	atomic.Or8(h.bitp, bitScan<<(heapBitsShift+h.shift))
 }
 
-// heapBitsBulkBarrier executes writebarrierptr_nostore
-// for every pointer slot in the memory range [p, p+size),
-// using the heap, data, or BSS bitmap to locate those pointer slots.
-// This executes the write barriers necessary after a memmove.
-// Both p and size must be pointer-aligned.
-// The range [p, p+size) must lie within a single object.
+// bulkBarrierPreWrite executes writebarrierptr_prewrite1
+// for every pointer slot in the memory range [src, src+size),
+// using pointer/scalar information from [dst, dst+size).
+// This executes the write barriers necessary before a memmove.
+// src, dst, and size must be pointer-aligned.
+// The range [dst, dst+size) must lie within a single object.
+//
+// As a special case, src == 0 indicates that this is being used for a
+// memclr. bulkBarrierPreWrite will pass 0 for the src of each write
+// barrier.
 //
-// Callers should call heapBitsBulkBarrier immediately after
-// calling memmove(p, src, size). This function is marked nosplit
+// Callers should call bulkBarrierPreWrite immediately before
+// calling memmove(dst, src, size). This function is marked nosplit
 // to avoid being preempted; the GC must not stop the goroutine
 // between the memmove and the execution of the barriers.
+// The caller is also responsible for cgo pointer checks if this
+// may be writing Go pointers into non-Go memory.
 //
-// The heap bitmap is not maintained for allocations containing
-// no pointers at all; any caller of heapBitsBulkBarrier must first
+// The pointer bitmap is not maintained for allocations containing
+// no pointers at all; any caller of bulkBarrierPreWrite must first
 // make sure the underlying allocation contains pointers, usually
 // by checking typ.kind&kindNoPointers.
 //
 //go:nosplit
-func heapBitsBulkBarrier(p, size uintptr) {
-	if (p|size)&(sys.PtrSize-1) != 0 {
-		throw("heapBitsBulkBarrier: unaligned arguments")
+func bulkBarrierPreWrite(dst, src, size uintptr) {
+	if (dst|src|size)&(sys.PtrSize-1) != 0 {
+		throw("bulkBarrierPreWrite: unaligned arguments")
 	}
 	if !writeBarrier.needed {
 		return
 	}
-	if !inheap(p) {
-		// If p is on the stack and in a higher frame than the
+	if !inheap(dst) {
+		// If dst is on the stack and in a higher frame than the
 		// caller, we either need to execute write barriers on
 		// it (which is what happens for normal stack writes
 		// through pointers to higher frames), or we need to
 		// force the mark termination stack scan to scan the
-		// frame containing p.
+		// frame containing dst.
 		//
-		// Executing write barriers on p is complicated in the
+		// Executing write barriers on dst is complicated in the
 		// general case because we either need to unwind the
 		// stack to get the stack map, or we need the type's
 		// bitmap, which may be a GC program.
 		//
 		// Hence, we opt for forcing the re-scan to scan the
-		// frame containing p, which we can do by simply
+		// frame containing dst, which we can do by simply
 		// unwinding the stack barriers between the current SP
-		// and p's frame.
+		// and dst's frame.
 		gp := getg().m.curg
-		if gp != nil && gp.stack.lo <= p && p < gp.stack.hi {
+		if gp != nil && gp.stack.lo <= dst && dst < gp.stack.hi {
 			// Run on the system stack to give it more
 			// stack space.
 			systemstack(func() {
-				gcUnwindBarriers(gp, p)
+				gcUnwindBarriers(gp, dst)
 			})
 			return
 		}
 
-		// If p is a global, use the data or BSS bitmaps to
+		// If dst is a global, use the data or BSS bitmaps to
 		// execute write barriers.
-		for datap := &firstmoduledata; datap != nil; datap = datap.next {
-			if datap.data <= p && p < datap.edata {
-				bulkBarrierBitmap(p, size, p-datap.data, datap.gcdatamask.bytedata)
+		for _, datap := range activeModules() {
+			if datap.data <= dst && dst < datap.edata {
+				bulkBarrierBitmap(dst, src, size, dst-datap.data, datap.gcdatamask.bytedata)
 				return
 			}
 		}
-		for datap := &firstmoduledata; datap != nil; datap = datap.next {
-			if datap.bss <= p && p < datap.ebss {
-				bulkBarrierBitmap(p, size, p-datap.bss, datap.gcbssmask.bytedata)
+		for _, datap := range activeModules() {
+			if datap.bss <= dst && dst < datap.ebss {
+				bulkBarrierBitmap(dst, src, size, dst-datap.bss, datap.gcbssmask.bytedata)
 				return
 			}
 		}
 		return
 	}
 
-	h := heapBitsForAddr(p)
-	for i := uintptr(0); i < size; i += sys.PtrSize {
-		if h.isPointer() {
-			x := (*uintptr)(unsafe.Pointer(p + i))
-			writebarrierptr_nostore(x, *x)
+	h := heapBitsForAddr(dst)
+	if src == 0 {
+		for i := uintptr(0); i < size; i += sys.PtrSize {
+			if h.isPointer() {
+				dstx := (*uintptr)(unsafe.Pointer(dst + i))
+				writebarrierptr_prewrite1(dstx, 0)
+			}
+			h = h.next()
+		}
+	} else {
+		for i := uintptr(0); i < size; i += sys.PtrSize {
+			if h.isPointer() {
+				dstx := (*uintptr)(unsafe.Pointer(dst + i))
+				srcx := (*uintptr)(unsafe.Pointer(src + i))
+				writebarrierptr_prewrite1(dstx, *srcx)
+			}
+			h = h.next()
 		}
-		h = h.next()
 	}
 }
 
-// bulkBarrierBitmap executes write barriers for [p, p+size) using a
-// 1-bit pointer bitmap. p is assumed to start maskOffset bytes into
-// the data covered by the bitmap in bits.
+// bulkBarrierBitmap executes write barriers for copying from [src,
+// src+size) to [dst, dst+size) using a 1-bit pointer bitmap. src is
+// assumed to start maskOffset bytes into the data covered by the
+// bitmap in bits (which may not be a multiple of 8).
 //
-// This is used by heapBitsBulkBarrier for writes to data and BSS.
+// This is used by bulkBarrierPreWrite for writes to data and BSS.
 //
 //go:nosplit
-func bulkBarrierBitmap(p, size, maskOffset uintptr, bits *uint8) {
+func bulkBarrierBitmap(dst, src, size, maskOffset uintptr, bits *uint8) {
 	word := maskOffset / sys.PtrSize
 	bits = addb(bits, word/8)
 	mask := uint8(1) << (word % 8)
@@ -644,28 +666,34 @@ func bulkBarrierBitmap(p, size, maskOffset uintptr, bits *uint8) {
 			mask = 1
 		}
 		if *bits&mask != 0 {
-			x := (*uintptr)(unsafe.Pointer(p + i))
-			writebarrierptr_nostore(x, *x)
+			dstx := (*uintptr)(unsafe.Pointer(dst + i))
+			if src == 0 {
+				writebarrierptr_prewrite1(dstx, 0)
+			} else {
+				srcx := (*uintptr)(unsafe.Pointer(src + i))
+				writebarrierptr_prewrite1(dstx, *srcx)
+			}
 		}
 		mask <<= 1
 	}
 }
 
-// typeBitsBulkBarrier executes writebarrierptr_nostore
-// for every pointer slot in the memory range [p, p+size),
-// using the type bitmap to locate those pointer slots.
-// The type typ must correspond exactly to [p, p+size).
-// This executes the write barriers necessary after a copy.
-// Both p and size must be pointer-aligned.
+// typeBitsBulkBarrier executes writebarrierptr_prewrite for every
+// pointer that would be copied from [src, src+size) to [dst,
+// dst+size) by a memmove using the type bitmap to locate those
+// pointer slots.
+//
+// The type typ must correspond exactly to [src, src+size) and [dst, dst+size).
+// dst, src, and size must be pointer-aligned.
 // The type typ must have a plain bitmap, not a GC program.
 // The only use of this function is in channel sends, and the
 // 64 kB channel element limit takes care of this for us.
 //
-// Must not be preempted because it typically runs right after memmove,
-// and the GC must not complete between those two.
+// Must not be preempted because it typically runs right before memmove,
+// and the GC must observe them as an atomic action.
 //
 //go:nosplit
-func typeBitsBulkBarrier(typ *_type, p, size uintptr) {
+func typeBitsBulkBarrier(typ *_type, dst, src, size uintptr) {
 	if typ == nil {
 		throw("runtime: typeBitsBulkBarrier without type")
 	}
@@ -690,8 +718,9 @@ func typeBitsBulkBarrier(typ *_type, p, size uintptr) {
 			bits = bits >> 1
 		}
 		if bits&1 != 0 {
-			x := (*uintptr)(unsafe.Pointer(p + i))
-			writebarrierptr_nostore(x, *x)
+			dstx := (*uintptr)(unsafe.Pointer(dst + i))
+			srcx := (*uintptr)(unsafe.Pointer(src + i))
+			writebarrierptr_prewrite(dstx, *srcx)
 		}
 	}
 }
@@ -730,7 +759,7 @@ func (h heapBits) initSpan(s *mspan) {
 		end := h.bitp
 		bitp := subtractb(end, nbyte-1)
 		for {
-			*bitp = bitPointerAll | bitMarkedAll
+			*bitp = bitPointerAll | bitScanAll
 			if bitp == end {
 				break
 			}
@@ -738,7 +767,7 @@ func (h heapBits) initSpan(s *mspan) {
 		}
 		return
 	}
-	memclr(unsafe.Pointer(subtractb(h.bitp, nbyte-1)), nbyte)
+	memclrNoHeapPointers(unsafe.Pointer(subtractb(h.bitp, nbyte-1)), nbyte)
 }
 
 // initCheckmarkSpan initializes a span for being checkmarked.
@@ -758,7 +787,7 @@ func (h heapBits) initCheckmarkSpan(size, n, total uintptr) {
 		return
 	}
 	for i := uintptr(0); i < n; i++ {
-		*h.bitp &^= bitMarked << (heapBitsShift + h.shift)
+		*h.bitp &^= bitScan << (heapBitsShift + h.shift)
 		h = h.forward(size / sys.PtrSize)
 	}
 }
@@ -861,9 +890,6 @@ func (s *mspan) countFree() int {
 // bits that belong to neighboring objects. Also, on weakly-ordered
 // machines, callers must execute a store/store (publication) barrier
 // between calling this function and making the object reachable.
-//
-// TODO: This still has atomic accesses left over from when it could
-// race with GC accessing mark bits in the bitmap. Remove these.
 func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 	const doubleCheck = false // slow but helpful; enable to test modifications to this code
 
@@ -877,11 +903,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 
 	if sys.PtrSize == 8 && size == sys.PtrSize {
 		// It's one word and it has pointers, it must be a pointer.
-		// In general we'd need an atomic update here if the
-		// concurrent GC were marking objects in this span,
-		// because each bitmap byte describes 3 other objects
-		// in addition to the one being allocated.
-		// However, since all allocated one-word objects are pointers
+		// Since all allocated one-word objects are pointers
 		// (non-pointers are aggregated into tinySize allocations),
 		// initSpan sets the pointer bits for us. Nothing to do here.
 		if doubleCheck {
@@ -900,7 +922,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 	ptrmask := typ.gcdata // start of 1-bit pointer mask (or GC program, handled below)
 
 	// Heap bitmap bits for 2-word object are only 4 bits,
-	// so also shared with objects next to it; use atomic updates.
+	// so also shared with objects next to it.
 	// This is called out as a special case primarily for 32-bit systems,
 	// so that on 32-bit systems the code below can assume all objects
 	// are 4-word aligned (because they're all 16-byte aligned).
@@ -917,20 +939,11 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 			if sys.PtrSize == 4 && dataSize == sys.PtrSize {
 				// 1 pointer object. On 32-bit machines clear the bit for the
 				// unused second word.
-				if gcphase == _GCoff {
-					*h.bitp &^= (bitPointer | bitMarked | ((bitPointer | bitMarked) << heapBitsShift)) << h.shift
-					*h.bitp |= (bitPointer | bitMarked) << h.shift
-				} else {
-					atomic.And8(h.bitp, ^uint8((bitPointer|bitMarked|((bitPointer|bitMarked)<<heapBitsShift))<<h.shift))
-					atomic.Or8(h.bitp, (bitPointer|bitMarked)<<h.shift)
-				}
+				*h.bitp &^= (bitPointer | bitScan | ((bitPointer | bitScan) << heapBitsShift)) << h.shift
+				*h.bitp |= (bitPointer | bitScan) << h.shift
 			} else {
 				// 2-element slice of pointer.
-				if gcphase == _GCoff {
-					*h.bitp |= (bitPointer | bitMarked | bitPointer<<heapBitsShift) << h.shift
-				} else {
-					atomic.Or8(h.bitp, (bitPointer|bitMarked|bitPointer<<heapBitsShift)<<h.shift)
-				}
+				*h.bitp |= (bitPointer | bitScan | bitPointer<<heapBitsShift) << h.shift
 			}
 			return
 		}
@@ -943,23 +956,13 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 			}
 		}
 		b := uint32(*ptrmask)
-		hb := (b & 3) | bitMarked
-		if gcphase == _GCoff {
-			// bitPointer == 1, bitMarked is 1 << 4, heapBitsShift is 1.
-			// 110011 is shifted h.shift and complemented.
-			// This clears out the bits that are about to be
-			// ored into *h.hbitp in the next instructions.
-			*h.bitp &^= (bitPointer | bitMarked | ((bitPointer | bitMarked) << heapBitsShift)) << h.shift
-			*h.bitp |= uint8(hb << h.shift)
-		} else {
-			// TODO:(rlh) since the GC is not concurrently setting the
-			// mark bits in the heap map anymore and malloc
-			// owns the span we are allocating in why does this have
-			// to be atomic?
-
-			atomic.And8(h.bitp, ^uint8((bitPointer|bitMarked|((bitPointer|bitMarked)<<heapBitsShift))<<h.shift))
-			atomic.Or8(h.bitp, uint8(hb<<h.shift))
-		}
+		hb := (b & 3) | bitScan
+		// bitPointer == 1, bitScan is 1 << 4, heapBitsShift is 1.
+		// 110011 is shifted h.shift and complemented.
+		// This clears out the bits that are about to be
+		// ored into *h.hbitp in the next instructions.
+		*h.bitp &^= (bitPointer | bitScan | ((bitPointer | bitScan) << heapBitsShift)) << h.shift
+		*h.bitp |= uint8(hb << h.shift)
 		return
 	}
 
@@ -1128,9 +1131,9 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 
 	// Phase 1: Special case for leading byte (shift==0) or half-byte (shift==4).
 	// The leading byte is special because it contains the bits for word 1,
-	// which does not have the marked bits set.
-	// The leading half-byte is special because it's a half a byte and must be
-	// manipulated atomically.
+	// which does not have the scan bit set.
+	// The leading half-byte is special because it's a half a byte,
+	// so we have to be careful with the bits already there.
 	switch {
 	default:
 		throw("heapBitsSetType: unexpected shift")
@@ -1151,7 +1154,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 		// TODO: It doesn't matter if we set the checkmark, so
 		// maybe this case isn't needed any more.
 		hb = b & bitPointerAll
-		hb |= bitMarked | bitMarked<<(2*heapBitsShift) | bitMarked<<(3*heapBitsShift)
+		hb |= bitScan | bitScan<<(2*heapBitsShift) | bitScan<<(3*heapBitsShift)
 		if w += 4; w >= nw {
 			goto Phase3
 		}
@@ -1162,30 +1165,21 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 
 	case sys.PtrSize == 8 && h.shift == 2:
 		// Ptrmask and heap bitmap are misaligned.
-		// The bits for the first two words are in a byte shared with another object
-		// and must be updated atomically.
-		// NOTE(rsc): The atomic here may not be necessary.
+		// The bits for the first two words are in a byte shared
+		// with another object, so we must be careful with the bits
+		// already there.
 		// We took care of 1-word and 2-word objects above,
-		// so this is at least a 6-word object, so our start bits
-		// are shared only with the type bits of another object,
-		// not with its mark bit. Since there is only one allocation
-		// from a given span at a time, we should be able to set
-		// these bits non-atomically. Not worth the risk right now.
+		// so this is at least a 6-word object.
 		hb = (b & (bitPointer | bitPointer<<heapBitsShift)) << (2 * heapBitsShift)
 		// This is not noscan, so set the scan bit in the
 		// first word.
-		hb |= bitMarked << (2 * heapBitsShift)
+		hb |= bitScan << (2 * heapBitsShift)
 		b >>= 2
 		nb -= 2
-		// Note: no bitMarker for second word because that's
+		// Note: no bitScan for second word because that's
 		// the checkmark.
-		if gcphase == _GCoff {
-			*hbitp &^= uint8((bitPointer | bitMarked | (bitPointer << heapBitsShift)) << (2 * heapBitsShift))
-			*hbitp |= uint8(hb)
-		} else {
-			atomic.And8(hbitp, ^(uint8(bitPointer|bitMarked|bitPointer<<heapBitsShift) << (2 * heapBitsShift)))
-			atomic.Or8(hbitp, uint8(hb))
-		}
+		*hbitp &^= uint8((bitPointer | bitScan | (bitPointer << heapBitsShift)) << (2 * heapBitsShift))
+		*hbitp |= uint8(hb)
 		hbitp = subtract1(hbitp)
 		if w += 2; w >= nw {
 			// We know that there is more data, because we handled 2-word objects above.
@@ -1211,7 +1205,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 		// but we'll stop at the break and then truncate
 		// appropriately in Phase 3.
 		hb = b & bitPointerAll
-		hb |= bitMarkedAll
+		hb |= bitScanAll
 		if w += 4; w >= nw {
 			break
 		}
@@ -1259,7 +1253,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 
 		// Emit bitmap byte.
 		hb = b & bitPointerAll
-		hb |= bitMarkedAll
+		hb |= bitScanAll
 		if w += 4; w >= nw {
 			break
 		}
@@ -1275,7 +1269,7 @@ Phase3:
 		// there are more entries than possible pointer slots.
 		// Discard the excess entries (can't be more than 3).
 		mask := uintptr(1)<<(4-(w-nw)) - 1
-		hb &= mask | mask<<4 // apply mask to both pointer bits and mark bits
+		hb &= mask | mask<<4 // apply mask to both pointer bits and scan bits
 	}
 
 	// Change nw from counting possibly-pointer words to total words in allocation.
@@ -1299,14 +1293,10 @@ Phase3:
 	// If w == nw+4 then there's nothing left to do: we wrote all nw entries
 	// and can discard the 4 sitting in hb.
 	// But if w == nw+2, we need to write first two in hb.
-	// The byte is shared with the next object so we may need an atomic.
+	// The byte is shared with the next object, so be careful with
+	// existing bits.
 	if w == nw+2 {
-		if gcphase == _GCoff {
-			*hbitp = *hbitp&^(bitPointer|bitMarked|(bitPointer|bitMarked)<<heapBitsShift) | uint8(hb)
-		} else {
-			atomic.And8(hbitp, ^uint8(bitPointer|bitMarked|(bitPointer|bitMarked)<<heapBitsShift))
-			atomic.Or8(hbitp, uint8(hb))
-		}
+		*hbitp = *hbitp&^(bitPointer|bitScan|(bitPointer|bitScan)<<heapBitsShift) | uint8(hb)
 	}
 
 Phase4:
@@ -1333,20 +1323,20 @@ Phase4:
 		for i := uintptr(0); i < size/sys.PtrSize; i++ {
 			j := i % ndata
 			var have, want uint8
-			have = (*h.bitp >> h.shift) & (bitPointer | bitMarked)
+			have = (*h.bitp >> h.shift) & (bitPointer | bitScan)
 			if i >= totalptr {
 				want = 0 // deadmarker
 				if typ.kind&kindGCProg != 0 && i < (totalptr+3)/4*4 {
-					want = bitMarked
+					want = bitScan
 				}
 			} else {
 				if j < nptr && (*addb(ptrmask, j/8)>>(j%8))&1 != 0 {
 					want |= bitPointer
 				}
 				if i != 1 {
-					want |= bitMarked
+					want |= bitScan
 				} else {
-					have &^= bitMarked
+					have &^= bitScan
 				}
 			}
 			if have != want {
@@ -1377,7 +1367,7 @@ Phase4:
 // of x in the heap bitmap to scalar/dead.
 func heapBitsSetTypeNoScan(x uintptr) {
 	h := heapBitsForAddr(uintptr(x))
-	*h.bitp &^= (bitPointer | bitMarked) << h.shift
+	*h.bitp &^= (bitPointer | bitScan) << h.shift
 }
 
 var debugPtrmask struct {
@@ -1394,7 +1384,7 @@ var debugPtrmask struct {
 // GC programs are only used for large allocations.
 // heapBitsSetType requires that allocSize is a multiple of 4 words,
 // so that the relevant bitmap bytes are not shared with surrounding
-// objects and need not be accessed with atomic instructions.
+// objects.
 func heapBitsSetTypeGCProg(h heapBits, progSize, elemSize, dataSize, allocSize uintptr, prog *byte) {
 	if sys.PtrSize == 8 && allocSize%(4*sys.PtrSize) != 0 {
 		// Alignment will be wrong.
@@ -1468,7 +1458,7 @@ func heapBitsSetTypeGCProg(h heapBits, progSize, elemSize, dataSize, allocSize u
 	}
 	endProg := unsafe.Pointer(subtractb(h.bitp, (totalBits+3)/4))
 	endAlloc := unsafe.Pointer(subtractb(h.bitp, allocSize/heapBitmapScale))
-	memclr(add(endAlloc, 1), uintptr(endProg)-uintptr(endAlloc))
+	memclrNoHeapPointers(add(endAlloc, 1), uintptr(endProg)-uintptr(endAlloc))
 }
 
 // progToPointerMask returns the 1-bit pointer mask output by the GC program prog.
@@ -1525,11 +1515,11 @@ Run:
 				dst = add1(dst)
 				bits >>= 8
 			} else {
-				v := bits&bitPointerAll | bitMarkedAll
+				v := bits&bitPointerAll | bitScanAll
 				*dst = uint8(v)
 				dst = subtract1(dst)
 				bits >>= 4
-				v = bits&bitPointerAll | bitMarkedAll
+				v = bits&bitPointerAll | bitScanAll
 				*dst = uint8(v)
 				dst = subtract1(dst)
 				bits >>= 4
@@ -1563,11 +1553,11 @@ Run:
 					dst = add1(dst)
 					bits >>= 8
 				} else {
-					v := bits&0xf | bitMarkedAll
+					v := bits&0xf | bitScanAll
 					*dst = uint8(v)
 					dst = subtract1(dst)
 					bits >>= 4
-					v = bits&0xf | bitMarkedAll
+					v = bits&0xf | bitScanAll
 					*dst = uint8(v)
 					dst = subtract1(dst)
 					bits >>= 4
@@ -1694,7 +1684,7 @@ Run:
 					}
 				} else {
 					for nbits >= 4 {
-						*dst = uint8(bits&0xf | bitMarkedAll)
+						*dst = uint8(bits&0xf | bitScanAll)
 						dst = subtract1(dst)
 						bits >>= 4
 						nbits -= 4
@@ -1752,7 +1742,7 @@ Run:
 			for i := c / 4; i > 0; i-- {
 				bits |= (uintptr(*src) & 0xf) << nbits
 				src = subtract1(src)
-				*dst = uint8(bits&0xf | bitMarkedAll)
+				*dst = uint8(bits&0xf | bitScanAll)
 				dst = subtract1(dst)
 				bits >>= 4
 			}
@@ -1778,7 +1768,7 @@ Run:
 		totalBits = (uintptr(unsafe.Pointer(dstStart))-uintptr(unsafe.Pointer(dst)))*4 + nbits
 		nbits += -nbits & 3
 		for ; nbits > 0; nbits -= 4 {
-			v := bits&0xf | bitMarkedAll
+			v := bits&0xf | bitScanAll
 			*dst = uint8(v)
 			dst = subtract1(dst)
 			bits >>= 4
@@ -1862,7 +1852,7 @@ func getgcmask(ep interface{}) (mask []byte) {
 	p := e.data
 	t := e._type
 	// data or bss
-	for datap := &firstmoduledata; datap != nil; datap = datap.next {
+	for _, datap := range activeModules() {
 		// data
 		if datap.data <= uintptr(p) && uintptr(p) < datap.edata {
 			bitmap := datap.gcdatamask.bytedata
diff --git a/src/runtime/mcache.go b/src/runtime/mcache.go
index 5938e53..c483310 100644
--- a/src/runtime/mcache.go
+++ b/src/runtime/mcache.go
@@ -11,6 +11,8 @@ import "unsafe"
 //
 // mcaches are allocated from non-GC'd memory, so any heap pointers
 // must be specially handled.
+//
+//go:notinheap
 type mcache struct {
 	// The following members are accessed on every malloc,
 	// so they are grouped here for better caching.
@@ -75,7 +77,6 @@ func allocmcache() *mcache {
 	lock(&mheap_.lock)
 	c := (*mcache)(mheap_.cachealloc.alloc())
 	unlock(&mheap_.lock)
-	memclr(unsafe.Pointer(c), unsafe.Sizeof(*c))
 	for i := 0; i < _NumSizeClasses; i++ {
 		c.alloc[i] = &emptymspan
 	}
diff --git a/src/runtime/mcentral.go b/src/runtime/mcentral.go
index 7b63110..ddcf81e 100644
--- a/src/runtime/mcentral.go
+++ b/src/runtime/mcentral.go
@@ -15,6 +15,8 @@ package runtime
 import "runtime/internal/atomic"
 
 // Central list of free objects of a given size.
+//
+//go:notinheap
 type mcentral struct {
 	lock      mutex
 	sizeclass int32
diff --git a/src/runtime/mem_linux.go b/src/runtime/mem_linux.go
index cd0bf26..094658d 100644
--- a/src/runtime/mem_linux.go
+++ b/src/runtime/mem_linux.go
@@ -22,17 +22,14 @@ const (
 var addrspace_vec [1]byte
 
 func addrspace_free(v unsafe.Pointer, n uintptr) bool {
-	// Step by the minimum possible physical page size. This is
-	// safe even if we have the wrong physical page size; mincore
-	// will just return EINVAL for unaligned addresses.
-	for off := uintptr(0); off < n; off += minPhysPageSize {
+	for off := uintptr(0); off < n; off += physPageSize {
 		// Use a length of 1 byte, which the kernel will round
 		// up to one physical page regardless of the true
 		// physical page size.
 		errval := mincore(unsafe.Pointer(uintptr(v)+off), 1, &addrspace_vec[0])
 		if errval == -_EINVAL {
 			// Address is not a multiple of the physical
-			// page size. That's fine.
+			// page size. Shouldn't happen, but just ignore it.
 			continue
 		}
 		// ENOMEM means unmapped, which is what we want.
@@ -138,7 +135,7 @@ func sysUnused(v unsafe.Pointer, n uintptr) {
 		}
 	}
 
-	if uintptr(v)&(sys.PhysPageSize-1) != 0 || n&(sys.PhysPageSize-1) != 0 {
+	if uintptr(v)&(physPageSize-1) != 0 || n&(physPageSize-1) != 0 {
 		// madvise will round this to any physical page
 		// *covered* by this range, so an unaligned madvise
 		// will release more memory than intended.
diff --git a/src/runtime/mem_plan9.go b/src/runtime/mem_plan9.go
index 3d82a98..98bfc7f 100644
--- a/src/runtime/mem_plan9.go
+++ b/src/runtime/mem_plan9.go
@@ -38,7 +38,7 @@ func memAlloc(n uintptr) unsafe.Pointer {
 				p.size -= n
 				p = (*memHdr)(add(unsafe.Pointer(p), p.size))
 			}
-			memclr(unsafe.Pointer(p), unsafe.Sizeof(memHdr{}))
+			*p = memHdr{}
 			return unsafe.Pointer(p)
 		}
 		prevp = p
@@ -48,7 +48,7 @@ func memAlloc(n uintptr) unsafe.Pointer {
 
 func memFree(ap unsafe.Pointer, n uintptr) {
 	n = memRound(n)
-	memclr(ap, n)
+	memclrNoHeapPointers(ap, n)
 	bp := (*memHdr)(ap)
 	bp.size = n
 	bpn := uintptr(ap)
@@ -63,7 +63,7 @@ func memFree(ap unsafe.Pointer, n uintptr) {
 		if bpn+bp.size == uintptr(unsafe.Pointer(p)) {
 			bp.size += p.size
 			bp.next = p.next
-			memclr(unsafe.Pointer(p), unsafe.Sizeof(memHdr{}))
+			*p = memHdr{}
 		} else {
 			bp.next.set(p)
 		}
@@ -77,14 +77,14 @@ func memFree(ap unsafe.Pointer, n uintptr) {
 	if bpn+bp.size == uintptr(unsafe.Pointer(p.next)) {
 		bp.size += p.next.ptr().size
 		bp.next = p.next.ptr().next
-		memclr(unsafe.Pointer(p.next), unsafe.Sizeof(memHdr{}))
+		*p.next.ptr() = memHdr{}
 	} else {
 		bp.next = p.next
 	}
 	if uintptr(unsafe.Pointer(p))+p.size == bpn {
 		p.size += bp.size
 		p.next = bp.next
-		memclr(unsafe.Pointer(bp), unsafe.Sizeof(memHdr{}))
+		*bp = memHdr{}
 	} else {
 		p.next.set(bp)
 	}
diff --git a/src/runtime/memclr_386.s b/src/runtime/memclr_386.s
index ce962f3..ef6e602 100644
--- a/src/runtime/memclr_386.s
+++ b/src/runtime/memclr_386.s
@@ -8,8 +8,8 @@
 
 // NOTE: Windows externalthreadhandler expects memclr to preserve DX.
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT, $0-8
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-8
 	MOVL	ptr+0(FP), DI
 	MOVL	n+4(FP), BX
 	XORL	AX, AX
diff --git a/src/runtime/memclr_amd64.s b/src/runtime/memclr_amd64.s
index 6f30eca..244f5b4 100644
--- a/src/runtime/memclr_amd64.s
+++ b/src/runtime/memclr_amd64.s
@@ -8,8 +8,8 @@
 
 // NOTE: Windows externalthreadhandler expects memclr to preserve DX.
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT, $0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-16
 	MOVQ	ptr+0(FP), DI
 	MOVQ	n+8(FP), BX
 	XORQ	AX, AX
diff --git a/src/runtime/memclr_arm.s b/src/runtime/memclr_arm.s
index c9b8586..eb37674 100644
--- a/src/runtime/memclr_arm.s
+++ b/src/runtime/memclr_arm.s
@@ -1,5 +1,5 @@
 // Inferno's libkern/memset-arm.s
-// http://code.google.com/p/inferno-os/source/browse/libkern/memset-arm.s
+// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/memset-arm.s
 //
 //         Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
 //         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
@@ -30,7 +30,7 @@
 #define N	R12
 #define TMP	R12				/* N and TMP don't overlap */
 
-TEXT runtime·memclr(SB),NOSPLIT,$0-8
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-8
 	MOVW	ptr+0(FP), TO
 	MOVW	n+4(FP), N
 	MOVW	$0, R0
diff --git a/src/runtime/memclr_arm64.s b/src/runtime/memclr_arm64.s
index 47c6b73..9d756bc 100644
--- a/src/runtime/memclr_arm64.s
+++ b/src/runtime/memclr_arm64.s
@@ -4,8 +4,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
 	MOVD	ptr+0(FP), R3
 	MOVD	n+8(FP), R4
 	// TODO(mwhudson): this is written this way to avoid tickling
diff --git a/src/runtime/memclr_mips64x.s b/src/runtime/memclr_mips64x.s
index 30a4af3..5018d43 100644
--- a/src/runtime/memclr_mips64x.s
+++ b/src/runtime/memclr_mips64x.s
@@ -6,8 +6,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
 	MOVV	ptr+0(FP), R1
 	MOVV	n+8(FP), R2
 	ADDV	R1, R2, R4
diff --git a/src/runtime/memclr_mipsx.s b/src/runtime/memclr_mipsx.s
new file mode 100644
index 0000000..ad013b8
--- /dev/null
+++ b/src/runtime/memclr_mipsx.s
@@ -0,0 +1,71 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "textflag.h"
+
+#ifdef GOARCH_mips
+#define MOVWHI  MOVWL
+#define MOVWLO  MOVWR
+#else
+#define MOVWHI  MOVWR
+#define MOVWLO  MOVWL
+#endif
+
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-8
+	MOVW	n+4(FP), R2
+	MOVW	ptr+0(FP), R1
+
+	SGTU	$4, R2, R3
+	ADDU	R2, R1, R4
+	BNE	R3, small_zero
+
+ptr_align:
+	AND	$3, R1, R3
+	BEQ	R3, setup
+	SUBU	R1, R0, R3
+	AND	$3, R3		// R3 contains number of bytes needed to align ptr
+	MOVWHI	R0, 0(R1)	// MOVWHI will write zeros up to next word boundary
+	SUBU	R3, R2
+	ADDU	R3, R1
+
+setup:
+	AND	$31, R2, R6
+	AND	$3, R2, R5
+	SUBU	R6, R4, R6	// end pointer for 32-byte chunks
+	SUBU	R5, R4, R5	// end pointer for 4-byte chunks
+
+large:
+	BEQ	R1, R6, words
+	MOVW	R0, 0(R1)
+	MOVW	R0, 4(R1)
+	MOVW	R0, 8(R1)
+	MOVW	R0, 12(R1)
+	MOVW	R0, 16(R1)
+	MOVW	R0, 20(R1)
+	MOVW	R0, 24(R1)
+	MOVW	R0, 28(R1)
+	ADDU	$32, R1
+	JMP	large
+
+words:
+	BEQ	R1, R5, tail
+	MOVW	R0, 0(R1)
+	ADDU	$4, R1
+	JMP	words
+
+tail:
+	BEQ	R1, R4, ret
+	MOVWLO	R0, -1(R4)
+
+ret:
+	RET
+
+small_zero:
+	BEQ	R1, R4, ret
+	MOVB	R0, 0(R1)
+	ADDU	$1, R1
+	JMP	small_zero
diff --git a/src/runtime/memclr_plan9_386.s b/src/runtime/memclr_plan9_386.s
index 4707ab2..c3d92a9 100644
--- a/src/runtime/memclr_plan9_386.s
+++ b/src/runtime/memclr_plan9_386.s
@@ -4,8 +4,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT, $0-8
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-8
 	MOVL	ptr+0(FP), DI
 	MOVL	n+4(FP), BX
 	XORL	AX, AX
diff --git a/src/runtime/memclr_plan9_amd64.s b/src/runtime/memclr_plan9_amd64.s
index 37e61df..d4d1a3a 100644
--- a/src/runtime/memclr_plan9_amd64.s
+++ b/src/runtime/memclr_plan9_amd64.s
@@ -4,8 +4,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
 	MOVQ	ptr+0(FP), DI
 	MOVQ	n+8(FP), CX
 	MOVQ	CX, BX
diff --git a/src/runtime/memclr_ppc64x.s b/src/runtime/memclr_ppc64x.s
index 442faa2..e3a4673 100644
--- a/src/runtime/memclr_ppc64x.s
+++ b/src/runtime/memclr_ppc64x.s
@@ -6,26 +6,57 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT|NOFRAME,$0-16
-	MOVD	ptr+0(FP), R3
-	MOVD	n+8(FP), R4
-	SRADCC	$3, R4, R6	// R6 is the number of words to zero
-	BEQ	bytes
-
-	SUB	$8, R3
-	MOVD	R6, CTR
-	MOVDU	R0, 8(R3)
-	BC	25, 0, -1(PC)	// bdnz+ $-4
-	ADD	$8, R3
-
-bytes:
-	ANDCC	$7, R4, R7	// R7 is the number of bytes to zero
-	BEQ	done
-	SUB	$1, R3
-	MOVD	R7, CTR
-	MOVBU	R0, 1(R3)
-	BC	25, 0, -1(PC)	// bdnz+ $-4
-
-done:
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT|NOFRAME, $0-16
+	MOVD ptr+0(FP), R3
+	MOVD n+8(FP), R4
+
+	// Determine if there are doublewords to clear
+check:
+	ANDCC $7, R4, R5  // R5: leftover bytes to clear
+	SRAD  $3, R4, R6  // R6: double words to clear
+	CMP   R6, $0, CR1 // CR1[EQ] set if no double words
+
+	BC     12, 6, nozerolarge // only single bytes
+	MOVD   R6, CTR            // R6 = number of double words
+	SRADCC $2, R6, R7         // 32 byte chunks?
+	BNE    zero32setup
+
+	// Clear double words
+
+zero8:
+	MOVD R0, 0(R3)    // double word
+	ADD  $8, R3
+	BC   16, 0, zero8 // dec ctr, br zero8 if ctr not 0
+	BR   nozerolarge  // handle remainder
+
+	// Prepare to clear 32 bytes at a time.
+
+zero32setup:
+	DCBTST (R3)    // prepare data cache
+	MOVD   R7, CTR // number of 32 byte chunks
+
+zero32:
+	MOVD    R0, 0(R3)       // clear 4 double words
+	MOVD    R0, 8(R3)
+	MOVD    R0, 16(R3)
+	MOVD    R0, 24(R3)
+	ADD     $32, R3
+	BC      16, 0, zero32   // dec ctr, br zero32 if ctr not 0
+	RLDCLCC $61, R4, $3, R6 // remaining doublewords
+	BEQ     nozerolarge
+	MOVD    R6, CTR         // set up the CTR for doublewords
+	BR      zero8
+
+nozerolarge:
+	CMP R5, $0   // any remaining bytes
+	BC  4, 1, LR // ble lr
+
+zerotail:
+	MOVD R5, CTR // set up to clear tail bytes
+
+zerotailloop:
+	MOVB R0, 0(R3)           // clear single bytes
+	ADD  $1, R3
+	BC   16, 0, zerotailloop // dec ctr, br zerotailloop if ctr not 0
 	RET
diff --git a/src/runtime/memclr_s390x.s b/src/runtime/memclr_s390x.s
index 86eafec..43da10d 100644
--- a/src/runtime/memclr_s390x.s
+++ b/src/runtime/memclr_s390x.s
@@ -4,8 +4,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT|NOFRAME,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT|NOFRAME,$0-16
 	MOVD	ptr+0(FP), R4
 	MOVD	n+8(FP), R5
 
@@ -16,8 +16,8 @@ start:
 	CMPBLE	R5, $15, clear12to15
 	CMP	R5, $32
 	BGE	clearmt32
-	MOVD	R0, 0(R4)
-	MOVD	R0, 8(R4)
+	MOVD	$0, 0(R4)
+	MOVD	$0, 8(R4)
 	ADD	$16, R4
 	SUB	$16, R5
 	BR	start
@@ -25,79 +25,79 @@ start:
 clear0to3:
 	CMPBEQ	R5, $0, done
 	CMPBNE	R5, $1, clear2
-	MOVB	R0, 0(R4)
+	MOVB	$0, 0(R4)
 	RET
 clear2:
 	CMPBNE	R5, $2, clear3
-	MOVH	R0, 0(R4)
+	MOVH	$0, 0(R4)
 	RET
 clear3:
-	MOVH	R0, 0(R4)
-	MOVB	R0, 2(R4)
+	MOVH	$0, 0(R4)
+	MOVB	$0, 2(R4)
 	RET
 
 clear4to7:
 	CMPBNE	R5, $4, clear5
-	MOVW	R0, 0(R4)
+	MOVW	$0, 0(R4)
 	RET
 clear5:
 	CMPBNE	R5, $5, clear6
-	MOVW	R0, 0(R4)
-	MOVB	R0, 4(R4)
+	MOVW	$0, 0(R4)
+	MOVB	$0, 4(R4)
 	RET
 clear6:
 	CMPBNE	R5, $6, clear7
-	MOVW	R0, 0(R4)
-	MOVH	R0, 4(R4)
+	MOVW	$0, 0(R4)
+	MOVH	$0, 4(R4)
 	RET
 clear7:
-	MOVW	R0, 0(R4)
-	MOVH	R0, 4(R4)
-	MOVB	R0, 6(R4)
+	MOVW	$0, 0(R4)
+	MOVH	$0, 4(R4)
+	MOVB	$0, 6(R4)
 	RET
 
 clear8to11:
 	CMPBNE	R5, $8, clear9
-	MOVD	R0, 0(R4)
+	MOVD	$0, 0(R4)
 	RET
 clear9:
 	CMPBNE	R5, $9, clear10
-	MOVD	R0, 0(R4)
-	MOVB	R0, 8(R4)
+	MOVD	$0, 0(R4)
+	MOVB	$0, 8(R4)
 	RET
 clear10:
 	CMPBNE	R5, $10, clear11
-	MOVD	R0, 0(R4)
-	MOVH	R0, 8(R4)
+	MOVD	$0, 0(R4)
+	MOVH	$0, 8(R4)
 	RET
 clear11:
-	MOVD	R0, 0(R4)
-	MOVH	R0, 8(R4)
-	MOVB	R0, 10(R4)
+	MOVD	$0, 0(R4)
+	MOVH	$0, 8(R4)
+	MOVB	$0, 10(R4)
 	RET
 
 clear12to15:
 	CMPBNE	R5, $12, clear13
-	MOVD	R0, 0(R4)
-	MOVW	R0, 8(R4)
+	MOVD	$0, 0(R4)
+	MOVW	$0, 8(R4)
 	RET
 clear13:
 	CMPBNE	R5, $13, clear14
-	MOVD	R0, 0(R4)
-	MOVW	R0, 8(R4)
-	MOVB	R0, 12(R4)
+	MOVD	$0, 0(R4)
+	MOVW	$0, 8(R4)
+	MOVB	$0, 12(R4)
 	RET
 clear14:
 	CMPBNE	R5, $14, clear15
-	MOVD	R0, 0(R4)
-	MOVW	R0, 8(R4)
-	MOVH	R0, 12(R4)
+	MOVD	$0, 0(R4)
+	MOVW	$0, 8(R4)
+	MOVH	$0, 12(R4)
 	RET
 clear15:
-	MOVD	R0, 0(R4)
-	MOVW	R0, 8(R4)
-	MOVH	R0, 12(R4)
-	MOVB	R0, 14(R4)
+	MOVD	$0, 0(R4)
+	MOVW	$0, 8(R4)
+	MOVH	$0, 12(R4)
+	MOVB	$0, 14(R4)
 	RET
 
 clearmt32:
@@ -117,6 +117,6 @@ done:
 // DO NOT CALL - target for exrl (execute relative long) instruction.
 TEXT runtime·memclr_s390x_exrl_xc(SB),NOSPLIT|NOFRAME,$0-0
 	XC	$1, 0(R4), 0(R4)
-	MOVD	R0, 0(R0)
+	MOVD	$0, 0(R0)
 	RET
 
diff --git a/src/runtime/memmove_386.s b/src/runtime/memmove_386.s
index 52b35a6..b712ea1 100644
--- a/src/runtime/memmove_386.s
+++ b/src/runtime/memmove_386.s
@@ -1,5 +1,5 @@
 // Inferno's libkern/memmove-386.s
-// http://code.google.com/p/inferno-os/source/browse/libkern/memmove-386.s
+// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/memmove-386.s
 //
 //         Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
 //         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
diff --git a/src/runtime/memmove_amd64.s b/src/runtime/memmove_amd64.s
index 39b4c3a..464f5fd 100644
--- a/src/runtime/memmove_amd64.s
+++ b/src/runtime/memmove_amd64.s
@@ -1,5 +1,5 @@
 // Derived from Inferno's libkern/memmove-386.s (adapted for amd64)
-// http://code.google.com/p/inferno-os/source/browse/libkern/memmove-386.s
+// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/memmove-386.s
 //
 //         Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
 //         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
@@ -64,6 +64,9 @@ tail:
 	JBE	move_129through256
 	// TODO: use branch table and BSR to make this just a single dispatch
 
+	TESTB	$1, runtime·useRepMovs(SB)
+	JZ	avxUnaligned
+
 /*
  * check and set for backwards
  */
@@ -108,7 +111,6 @@ back:
 	ADDQ	BX, CX
 	CMPQ	CX, DI
 	JLS	forward
-	
 /*
  * whole thing backwards has
  * adjusted addresses
@@ -273,3 +275,242 @@ move_256through2048:
 	LEAQ	256(DI), DI
 	JGE	move_256through2048
 	JMP	tail
+
+avxUnaligned:
+	// There are two implementations of move algorithm.
+	// The first one for non-ovelapped memory regions. It uses forward copying.
+	// The second one for overlapped regions. It uses backward copying
+	MOVQ	DI, CX
+	SUBQ	SI, CX
+	// Now CX contains distance between SRC and DEST
+	CMPQ	CX, BX
+	// If the distance lesser than region length it means that regions are overlapped
+	JC	copy_backward
+
+	// Non-temporal copy would be better for big sizes.
+	CMPQ	BX, $0x100000
+	JAE	gobble_big_data_fwd
+
+	// Memory layout on the source side
+	// SI                                       CX
+	// |<---------BX before correction--------->|
+	// |       |<--BX corrected-->|             |
+	// |       |                  |<--- AX  --->|
+	// |<-R11->|                  |<-128 bytes->|
+	// +----------------------------------------+
+	// | Head  | Body             | Tail        |
+	// +-------+------------------+-------------+
+	// ^       ^                  ^
+	// |       |                  |
+	// Save head into Y4          Save tail into X5..X12
+	//         |
+	//         SI+R11, where R11 = ((DI & -32) + 32) - DI
+	// Algorithm:
+	// 1. Unaligned save of the tail's 128 bytes
+	// 2. Unaligned save of the head's 32  bytes
+	// 3. Destination-aligned copying of body (128 bytes per iteration)
+	// 4. Put head on the new place
+	// 5. Put the tail on the new place
+	// It can be important to satisfy processor's pipeline requirements for
+	// small sizes as the cost of unaligned memory region copying is
+	// comparable with the cost of main loop. So code is slightly messed there.
+	// There is more clean implementation of that algorithm for bigger sizes
+	// where the cost of unaligned part copying is negligible.
+	// You can see it after gobble_big_data_fwd label.
+	LEAQ	(SI)(BX*1), CX
+	MOVQ	DI, R10
+	// CX points to the end of buffer so we need go back slightly. We will use negative offsets there.
+	MOVOU	-0x80(CX), X5
+	MOVOU	-0x70(CX), X6
+	MOVQ	$0x80, AX
+	// Align destination address
+	ANDQ	$-32, DI
+	ADDQ	$32, DI
+	// Continue tail saving.
+	MOVOU	-0x60(CX), X7
+	MOVOU	-0x50(CX), X8
+	// Make R11 delta between aligned and unaligned destination addresses.
+	MOVQ	DI, R11
+	SUBQ	R10, R11
+	// Continue tail saving.
+	MOVOU	-0x40(CX), X9
+	MOVOU	-0x30(CX), X10
+	// Let's make bytes-to-copy value adjusted as we've prepared unaligned part for copying.
+	SUBQ	R11, BX
+	// Continue tail saving.
+	MOVOU	-0x20(CX), X11
+	MOVOU	-0x10(CX), X12
+	// The tail will be put on it's place after main body copying.
+	// It's time for the unaligned heading part.
+	VMOVDQU	(SI), Y4
+	// Adjust source address to point past head.
+	ADDQ	R11, SI
+	SUBQ	AX, BX
+	// Aligned memory copying there
+gobble_128_loop:
+	VMOVDQU	(SI), Y0
+	VMOVDQU	0x20(SI), Y1
+	VMOVDQU	0x40(SI), Y2
+	VMOVDQU	0x60(SI), Y3
+	ADDQ	AX, SI
+	VMOVDQA	Y0, (DI)
+	VMOVDQA	Y1, 0x20(DI)
+	VMOVDQA	Y2, 0x40(DI)
+	VMOVDQA	Y3, 0x60(DI)
+	ADDQ	AX, DI
+	SUBQ	AX, BX
+	JA	gobble_128_loop
+	// Now we can store unaligned parts.
+	ADDQ	AX, BX
+	ADDQ	DI, BX
+	VMOVDQU	Y4, (R10)
+	VZEROUPPER
+	MOVOU	X5, -0x80(BX)
+	MOVOU	X6, -0x70(BX)
+	MOVOU	X7, -0x60(BX)
+	MOVOU	X8, -0x50(BX)
+	MOVOU	X9, -0x40(BX)
+	MOVOU	X10, -0x30(BX)
+	MOVOU	X11, -0x20(BX)
+	MOVOU	X12, -0x10(BX)
+	RET
+
+gobble_big_data_fwd:
+	// There is forward copying for big regions.
+	// It uses non-temporal mov instructions.
+	// Details of this algorithm are commented previously for small sizes.
+	LEAQ	(SI)(BX*1), CX
+	MOVOU	-0x80(SI)(BX*1), X5
+	MOVOU	-0x70(CX), X6
+	MOVOU	-0x60(CX), X7
+	MOVOU	-0x50(CX), X8
+	MOVOU	-0x40(CX), X9
+	MOVOU	-0x30(CX), X10
+	MOVOU	-0x20(CX), X11
+	MOVOU	-0x10(CX), X12
+	VMOVDQU	(SI), Y4
+	MOVQ	DI, R8
+	ANDQ	$-32, DI
+	ADDQ	$32, DI
+	MOVQ	DI, R10
+	SUBQ	R8, R10
+	SUBQ	R10, BX
+	ADDQ	R10, SI
+	LEAQ	(DI)(BX*1), CX
+	SUBQ	$0x80, BX
+gobble_mem_fwd_loop:
+	PREFETCHNTA 0x1C0(SI)
+	PREFETCHNTA 0x280(SI)
+	// Prefetch values were choosen empirically.
+	// Approach for prefetch usage as in 7.6.6 of [1]
+	// [1] 64-ia-32-architectures-optimization-manual.pdf
+	// http://www.intel.ru/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
+	VMOVDQU	(SI), Y0
+	VMOVDQU	0x20(SI), Y1
+	VMOVDQU	0x40(SI), Y2
+	VMOVDQU	0x60(SI), Y3
+	ADDQ	$0x80, SI
+	VMOVNTDQ Y0, (DI)
+	VMOVNTDQ Y1, 0x20(DI)
+	VMOVNTDQ Y2, 0x40(DI)
+	VMOVNTDQ Y3, 0x60(DI)
+	ADDQ	$0x80, DI
+	SUBQ	$0x80, BX
+	JA		gobble_mem_fwd_loop
+	// NT instructions don't follow the normal cache-coherency rules.
+	// We need SFENCE there to make copied data available timely.
+	SFENCE
+	VMOVDQU	Y4, (R8)
+	VZEROUPPER
+	MOVOU	X5, -0x80(CX)
+	MOVOU	X6, -0x70(CX)
+	MOVOU	X7, -0x60(CX)
+	MOVOU	X8, -0x50(CX)
+	MOVOU	X9, -0x40(CX)
+	MOVOU	X10, -0x30(CX)
+	MOVOU	X11, -0x20(CX)
+	MOVOU	X12, -0x10(CX)
+	RET
+
+copy_backward:
+	MOVQ	DI, AX
+	// Backward copying is about the same as the forward one.
+	// Firstly we load unaligned tail in the beginning of region.
+	MOVOU	(SI), X5
+	MOVOU	0x10(SI), X6
+	ADDQ	BX, DI
+	MOVOU	0x20(SI), X7
+	MOVOU	0x30(SI), X8
+	LEAQ	-0x20(DI), R10
+	MOVQ	DI, R11
+	MOVOU	0x40(SI), X9
+	MOVOU	0x50(SI), X10
+	ANDQ	$0x1F, R11
+	MOVOU	0x60(SI), X11
+	MOVOU	0x70(SI), X12
+	XORQ	R11, DI
+	// Let's point SI to the end of region
+	ADDQ	BX, SI
+	// and load unaligned head into X4.
+	VMOVDQU	-0x20(SI), Y4
+	SUBQ	R11, SI
+	SUBQ	R11, BX
+	// If there is enough data for non-temporal moves go to special loop
+	CMPQ	BX, $0x100000
+	JA		gobble_big_data_bwd
+	SUBQ	$0x80, BX
+gobble_mem_bwd_loop:
+	VMOVDQU	-0x20(SI), Y0
+	VMOVDQU	-0x40(SI), Y1
+	VMOVDQU	-0x60(SI), Y2
+	VMOVDQU	-0x80(SI), Y3
+	SUBQ	$0x80, SI
+	VMOVDQA	Y0, -0x20(DI)
+	VMOVDQA	Y1, -0x40(DI)
+	VMOVDQA	Y2, -0x60(DI)
+	VMOVDQA	Y3, -0x80(DI)
+	SUBQ	$0x80, DI
+	SUBQ	$0x80, BX
+	JA		gobble_mem_bwd_loop
+	// Let's store unaligned data
+	VMOVDQU	Y4, (R10)
+	VZEROUPPER
+	MOVOU	X5, (AX)
+	MOVOU	X6, 0x10(AX)
+	MOVOU	X7, 0x20(AX)
+	MOVOU	X8, 0x30(AX)
+	MOVOU	X9, 0x40(AX)
+	MOVOU	X10, 0x50(AX)
+	MOVOU	X11, 0x60(AX)
+	MOVOU	X12, 0x70(AX)
+	RET
+
+gobble_big_data_bwd:
+	SUBQ	$0x80, BX
+gobble_big_mem_bwd_loop:
+	PREFETCHNTA -0x1C0(SI)
+	PREFETCHNTA -0x280(SI)
+	VMOVDQU	-0x20(SI), Y0
+	VMOVDQU	-0x40(SI), Y1
+	VMOVDQU	-0x60(SI), Y2
+	VMOVDQU	-0x80(SI), Y3
+	SUBQ	$0x80, SI
+	VMOVNTDQ	Y0, -0x20(DI)
+	VMOVNTDQ	Y1, -0x40(DI)
+	VMOVNTDQ	Y2, -0x60(DI)
+	VMOVNTDQ	Y3, -0x80(DI)
+	SUBQ	$0x80, DI
+	SUBQ	$0x80, BX
+	JA	gobble_big_mem_bwd_loop
+	SFENCE
+	VMOVDQU	Y4, (R10)
+	VZEROUPPER
+	MOVOU	X5, (AX)
+	MOVOU	X6, 0x10(AX)
+	MOVOU	X7, 0x20(AX)
+	MOVOU	X8, 0x30(AX)
+	MOVOU	X9, 0x40(AX)
+	MOVOU	X10, 0x50(AX)
+	MOVOU	X11, 0x60(AX)
+	MOVOU	X12, 0x70(AX)
+	RET
diff --git a/src/runtime/memmove_arm.s b/src/runtime/memmove_arm.s
index 6b880d5..504ae04 100644
--- a/src/runtime/memmove_arm.s
+++ b/src/runtime/memmove_arm.s
@@ -1,5 +1,5 @@
 // Inferno's libkern/memmove-arm.s
-// http://code.google.com/p/inferno-os/source/browse/libkern/memmove-arm.s
+// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/memmove-arm.s
 //
 //         Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
 //         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
diff --git a/src/runtime/memmove_linux_amd64_test.go b/src/runtime/memmove_linux_amd64_test.go
index 1dd5d49..d0e8b42 100644
--- a/src/runtime/memmove_linux_amd64_test.go
+++ b/src/runtime/memmove_linux_amd64_test.go
@@ -16,6 +16,7 @@ import (
 // TestMemmoveOverflow maps 3GB of memory and calls memmove on
 // the corresponding slice.
 func TestMemmoveOverflow(t *testing.T) {
+	t.Parallel()
 	// Create a temporary file.
 	tmp, err := ioutil.TempFile("", "go-memmovetest")
 	if err != nil {
@@ -40,7 +41,7 @@ func TestMemmoveOverflow(t *testing.T) {
 		_, _, errno := syscall.Syscall6(syscall.SYS_MMAP,
 			base+off, 65536, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED|syscall.MAP_FIXED, tmp.Fd(), 0)
 		if errno != 0 {
-			t.Fatalf("could not map a page at requested 0x%x: %s", base+off, errno)
+			t.Skipf("could not map a page at requested 0x%x: %s", base+off, errno)
 		}
 		defer syscall.Syscall(syscall.SYS_MUNMAP, base+off, 65536, 0)
 	}
diff --git a/src/runtime/memmove_mipsx.s b/src/runtime/memmove_mipsx.s
new file mode 100644
index 0000000..e934e4d
--- /dev/null
+++ b/src/runtime/memmove_mipsx.s
@@ -0,0 +1,258 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "textflag.h"
+
+#ifdef GOARCH_mips
+#define MOVWHI  MOVWL
+#define MOVWLO  MOVWR
+#else
+#define MOVWHI  MOVWR
+#define MOVWLO  MOVWL
+#endif
+
+// void runtime·memmove(void*, void*, uintptr)
+TEXT runtime·memmove(SB),NOSPLIT,$-0-12
+	MOVW	n+8(FP), R3
+	MOVW	from+4(FP), R2
+	MOVW	to+0(FP), R1
+
+	ADDU	R3, R2, R4	// end pointer for source
+	ADDU	R3, R1, R5	// end pointer for destination
+
+	// if destination is ahead of source, start at the end of the buffer and go backward.
+	SGTU	R1, R2, R6
+	BNE	R6, backward
+
+	// if less than 4 bytes, use byte by byte copying
+	SGTU	$4, R3, R6
+	BNE	R6, f_small_copy
+
+	// align destination to 4 bytes
+	AND	$3, R1, R6
+	BEQ	R6, f_dest_aligned
+	SUBU	R1, R0, R6
+	AND	$3, R6
+	MOVWHI	0(R2), R7
+	SUBU	R6, R3
+	MOVWLO	3(R2), R7
+	ADDU	R6, R2
+	MOVWHI	R7, 0(R1)
+	ADDU	R6, R1
+
+f_dest_aligned:
+	AND	$31, R3, R7
+	AND	$3, R3, R6
+	SUBU	R7, R5, R7	// end pointer for 32-byte chunks
+	SUBU	R6, R5, R6	// end pointer for 4-byte chunks
+
+	// if source is not aligned, use unaligned reads
+	AND	$3, R2, R8
+	BNE	R8, f_large_ua
+
+f_large:
+	BEQ	R1, R7, f_words
+	ADDU	$32, R1
+	MOVW	0(R2), R8
+	MOVW	4(R2), R9
+	MOVW	8(R2), R10
+	MOVW	12(R2), R11
+	MOVW	16(R2), R12
+	MOVW	20(R2), R13
+	MOVW	24(R2), R14
+	MOVW	28(R2), R15
+	ADDU	$32, R2
+	MOVW	R8, -32(R1)
+	MOVW	R9, -28(R1)
+	MOVW	R10, -24(R1)
+	MOVW	R11, -20(R1)
+	MOVW	R12, -16(R1)
+	MOVW	R13, -12(R1)
+	MOVW	R14, -8(R1)
+	MOVW	R15, -4(R1)
+	JMP	f_large
+
+f_words:
+	BEQ	R1, R6, f_tail
+	ADDU	$4, R1
+	MOVW	0(R2), R8
+	ADDU	$4, R2
+	MOVW	R8, -4(R1)
+	JMP	f_words
+
+f_tail:
+	BEQ	R1, R5, ret
+	MOVWLO	-1(R4), R8
+	MOVWLO	R8, -1(R5)
+
+ret:
+	RET
+
+f_large_ua:
+	BEQ	R1, R7, f_words_ua
+	ADDU	$32, R1
+	MOVWHI	0(R2), R8
+	MOVWHI	4(R2), R9
+	MOVWHI	8(R2), R10
+	MOVWHI	12(R2), R11
+	MOVWHI	16(R2), R12
+	MOVWHI	20(R2), R13
+	MOVWHI	24(R2), R14
+	MOVWHI	28(R2), R15
+	MOVWLO	3(R2), R8
+	MOVWLO	7(R2), R9
+	MOVWLO	11(R2), R10
+	MOVWLO	15(R2), R11
+	MOVWLO	19(R2), R12
+	MOVWLO	23(R2), R13
+	MOVWLO	27(R2), R14
+	MOVWLO	31(R2), R15
+	ADDU	$32, R2
+	MOVW	R8, -32(R1)
+	MOVW	R9, -28(R1)
+	MOVW	R10, -24(R1)
+	MOVW	R11, -20(R1)
+	MOVW	R12, -16(R1)
+	MOVW	R13, -12(R1)
+	MOVW	R14, -8(R1)
+	MOVW	R15, -4(R1)
+	JMP	f_large_ua
+
+f_words_ua:
+	BEQ	R1, R6, f_tail_ua
+	MOVWHI	0(R2), R8
+	ADDU	$4, R1
+	MOVWLO	3(R2), R8
+	ADDU	$4, R2
+	MOVW	R8, -4(R1)
+	JMP	f_words_ua
+
+f_tail_ua:
+	BEQ	R1, R5, ret
+	MOVWHI	-4(R4), R8
+	MOVWLO	-1(R4), R8
+	MOVWLO	R8, -1(R5)
+	JMP	ret
+
+f_small_copy:
+	BEQ	R1, R5, ret
+	ADDU	$1, R1
+	MOVB	0(R2), R6
+	ADDU	$1, R2
+	MOVB	R6, -1(R1)
+	JMP	f_small_copy
+
+backward:
+	SGTU	$4, R3, R6
+	BNE	R6, b_small_copy
+
+	AND	$3, R5, R6
+	BEQ	R6, b_dest_aligned
+	MOVWHI	-4(R4), R7
+	SUBU	R6, R3
+	MOVWLO	-1(R4), R7
+	SUBU	R6, R4
+	MOVWLO	R7, -1(R5)
+	SUBU	R6, R5
+
+b_dest_aligned:
+	AND	$31, R3, R7
+	AND	$3, R3, R6
+	ADDU	R7, R1, R7
+	ADDU	R6, R1, R6
+
+	AND	$3, R4, R8
+	BNE	R8, b_large_ua
+
+b_large:
+	BEQ	R5, R7, b_words
+	ADDU	$-32, R5
+	MOVW	-4(R4), R8
+	MOVW	-8(R4), R9
+	MOVW	-12(R4), R10
+	MOVW	-16(R4), R11
+	MOVW	-20(R4), R12
+	MOVW	-24(R4), R13
+	MOVW	-28(R4), R14
+	MOVW	-32(R4), R15
+	ADDU	$-32, R4
+	MOVW	R8, 28(R5)
+	MOVW	R9, 24(R5)
+	MOVW	R10, 20(R5)
+	MOVW	R11, 16(R5)
+	MOVW	R12, 12(R5)
+	MOVW	R13, 8(R5)
+	MOVW	R14, 4(R5)
+	MOVW	R15, 0(R5)
+	JMP	b_large
+
+b_words:
+	BEQ	R5, R6, b_tail
+	ADDU	$-4, R5
+	MOVW	-4(R4), R8
+	ADDU	$-4, R4
+	MOVW	R8, 0(R5)
+	JMP	b_words
+
+b_tail:
+	BEQ	R5, R1, ret
+	MOVWHI	0(R2), R8	// R2 and R1 have the same alignment so we don't need to load a whole word
+	MOVWHI	R8, 0(R1)
+	JMP	ret
+
+b_large_ua:
+	BEQ	R5, R7, b_words_ua
+	ADDU	$-32, R5
+	MOVWHI	-4(R4), R8
+	MOVWHI	-8(R4), R9
+	MOVWHI	-12(R4), R10
+	MOVWHI	-16(R4), R11
+	MOVWHI	-20(R4), R12
+	MOVWHI	-24(R4), R13
+	MOVWHI	-28(R4), R14
+	MOVWHI	-32(R4), R15
+	MOVWLO	-1(R4), R8
+	MOVWLO	-5(R4), R9
+	MOVWLO	-9(R4), R10
+	MOVWLO	-13(R4), R11
+	MOVWLO	-17(R4), R12
+	MOVWLO	-21(R4), R13
+	MOVWLO	-25(R4), R14
+	MOVWLO	-29(R4), R15
+	ADDU	$-32, R4
+	MOVW	R8, 28(R5)
+	MOVW	R9, 24(R5)
+	MOVW	R10, 20(R5)
+	MOVW	R11, 16(R5)
+	MOVW	R12, 12(R5)
+	MOVW	R13, 8(R5)
+	MOVW	R14, 4(R5)
+	MOVW	R15, 0(R5)
+	JMP	b_large_ua
+
+b_words_ua:
+	BEQ	R5, R6, b_tail_ua
+	MOVWHI	-4(R4), R8
+	ADDU	$-4, R5
+	MOVWLO	-1(R4), R8
+	ADDU	$-4, R4
+	MOVW	R8, 0(R5)
+	JMP	b_words_ua
+
+b_tail_ua:
+	BEQ	R5, R1, ret
+	MOVWHI	(R2), R8
+	MOVWLO	3(R2), R8
+	MOVWHI	R8, 0(R1)
+	JMP ret
+
+b_small_copy:
+	BEQ	R5, R1, ret
+	ADDU	$-1, R5
+	MOVB	-1(R4), R6
+	ADDU	$-1, R4
+	MOVB	R6, 0(R5)
+	JMP	b_small_copy
diff --git a/src/runtime/memmove_plan9_386.s b/src/runtime/memmove_plan9_386.s
index c4d62ec..29d44b2 100644
--- a/src/runtime/memmove_plan9_386.s
+++ b/src/runtime/memmove_plan9_386.s
@@ -1,5 +1,5 @@
 // Inferno's libkern/memmove-386.s
-// http://code.google.com/p/inferno-os/source/browse/libkern/memmove-386.s
+// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/memmove-386.s
 //
 //         Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
 //         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
diff --git a/src/runtime/memmove_plan9_amd64.s b/src/runtime/memmove_plan9_amd64.s
index 9bef31d..a5e8dfa 100644
--- a/src/runtime/memmove_plan9_amd64.s
+++ b/src/runtime/memmove_plan9_amd64.s
@@ -1,5 +1,5 @@
 // Derived from Inferno's libkern/memmove-386.s (adapted for amd64)
-// http://code.google.com/p/inferno-os/source/browse/libkern/memmove-386.s
+// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/memmove-386.s
 //
 //         Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
 //         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
diff --git a/src/runtime/memmove_test.go b/src/runtime/memmove_test.go
index 2124cb9..dbfa284 100644
--- a/src/runtime/memmove_test.go
+++ b/src/runtime/memmove_test.go
@@ -5,12 +5,15 @@
 package runtime_test
 
 import (
+	"crypto/rand"
 	"fmt"
+	"internal/race"
 	. "runtime"
 	"testing"
 )
 
 func TestMemmove(t *testing.T) {
+	t.Parallel()
 	size := 256
 	if testing.Short() {
 		size = 128 + 16
@@ -49,6 +52,7 @@ func TestMemmove(t *testing.T) {
 }
 
 func TestMemmoveAlias(t *testing.T) {
+	t.Parallel()
 	size := 256
 	if testing.Short() {
 		size = 128 + 16
@@ -82,6 +86,110 @@ func TestMemmoveAlias(t *testing.T) {
 	}
 }
 
+func TestMemmoveLarge0x180000(t *testing.T) {
+	t.Parallel()
+	if race.Enabled {
+		t.Skip("skipping large memmove test under race detector")
+	}
+	testSize(t, 0x180000)
+}
+
+func TestMemmoveOverlapLarge0x120000(t *testing.T) {
+	t.Parallel()
+	if race.Enabled {
+		t.Skip("skipping large memmove test under race detector")
+	}
+	testOverlap(t, 0x120000)
+}
+
+func testSize(t *testing.T, size int) {
+	src := make([]byte, size)
+	dst := make([]byte, size)
+	_, _ = rand.Read(src)
+	_, _ = rand.Read(dst)
+
+	ref := make([]byte, size)
+	copyref(ref, dst)
+
+	for n := size - 50; n > 1; n >>= 1 {
+		for x := 0; x <= size-n; x = x*7 + 1 { // offset in src
+			for y := 0; y <= size-n; y = y*9 + 1 { // offset in dst
+				copy(dst[y:y+n], src[x:x+n])
+				copyref(ref[y:y+n], src[x:x+n])
+				p := cmpb(dst, ref)
+				if p >= 0 {
+					t.Fatalf("Copy failed, copying from src[%d:%d] to dst[%d:%d].\nOffset %d is different, %v != %v", x, x+n, y, y+n, p, dst[p], ref[p])
+				}
+			}
+		}
+	}
+}
+
+func testOverlap(t *testing.T, size int) {
+	src := make([]byte, size)
+	test := make([]byte, size)
+	ref := make([]byte, size)
+	_, _ = rand.Read(src)
+
+	for n := size - 50; n > 1; n >>= 1 {
+		for x := 0; x <= size-n; x = x*7 + 1 { // offset in src
+			for y := 0; y <= size-n; y = y*9 + 1 { // offset in dst
+				// Reset input
+				copyref(test, src)
+				copyref(ref, src)
+				copy(test[y:y+n], test[x:x+n])
+				if y <= x {
+					copyref(ref[y:y+n], ref[x:x+n])
+				} else {
+					copybw(ref[y:y+n], ref[x:x+n])
+				}
+				p := cmpb(test, ref)
+				if p >= 0 {
+					t.Fatalf("Copy failed, copying from src[%d:%d] to dst[%d:%d].\nOffset %d is different, %v != %v", x, x+n, y, y+n, p, test[p], ref[p])
+				}
+			}
+		}
+	}
+
+}
+
+// Forward copy.
+func copyref(dst, src []byte) {
+	for i, v := range src {
+		dst[i] = v
+	}
+}
+
+// Backwards copy
+func copybw(dst, src []byte) {
+	if len(src) == 0 {
+		return
+	}
+	for i := len(src) - 1; i >= 0; i-- {
+		dst[i] = src[i]
+	}
+}
+
+// Returns offset of difference
+func matchLen(a, b []byte, max int) int {
+	a = a[:max]
+	b = b[:max]
+	for i, av := range a {
+		if b[i] != av {
+			return i
+		}
+	}
+	return max
+}
+
+func cmpb(a, b []byte) int {
+	l := matchLen(a, b, len(a))
+	if l == len(a) {
+		return -1
+	}
+	return l
+}
+
 func benchmarkSizes(b *testing.B, sizes []int, fn func(b *testing.B, n int)) {
 	for _, n := range sizes {
 		b.Run(fmt.Sprint(n), func(b *testing.B) {
diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go
index 14ebec8..7e191d4 100644
--- a/src/runtime/mfinal.go
+++ b/src/runtime/mfinal.go
@@ -12,10 +12,14 @@ import (
 	"unsafe"
 )
 
+// finblock is allocated from non-GC'd memory, so any heap pointers
+// must be specially handled.
+//
+//go:notinheap
 type finblock struct {
 	alllink *finblock
 	next    *finblock
-	cnt     int32
+	cnt     uint32
 	_       int32
 	fin     [(_FinBlockSize - 2*sys.PtrSize - 2*4) / unsafe.Sizeof(finalizer{})]finalizer
 }
@@ -31,11 +35,11 @@ var allfin *finblock // list of all blocks
 
 // NOTE: Layout known to queuefinalizer.
 type finalizer struct {
-	fn   *funcval       // function to call
-	arg  unsafe.Pointer // ptr to object
+	fn   *funcval       // function to call (may be a heap pointer)
+	arg  unsafe.Pointer // ptr to object (may be a heap pointer)
 	nret uintptr        // bytes of return values from fn
 	fint *_type         // type of first argument of fn
-	ot   *ptrtype       // type of ptr to object
+	ot   *ptrtype       // type of ptr to object (may be a heap pointer)
 }
 
 var finalizer1 = [...]byte{
@@ -68,9 +72,8 @@ var finalizer1 = [...]byte{
 
 func queuefinalizer(p unsafe.Pointer, fn *funcval, nret uintptr, fint *_type, ot *ptrtype) {
 	lock(&finlock)
-	if finq == nil || finq.cnt == int32(len(finq.fin)) {
+	if finq == nil || finq.cnt == uint32(len(finq.fin)) {
 		if finc == nil {
-			// Note: write barrier here, assigning to finc, but should be okay.
 			finc = (*finblock)(persistentalloc(_FinBlockSize, 0, &memstats.gc_sys))
 			finc.alllink = allfin
 			allfin = finc
@@ -96,7 +99,7 @@ func queuefinalizer(p unsafe.Pointer, fn *funcval, nret uintptr, fint *_type, ot
 		finq = block
 	}
 	f := &finq.fin[finq.cnt]
-	finq.cnt++
+	atomic.Xadd(&finq.cnt, +1) // Sync with markroots
 	f.fn = fn
 	f.nret = nret
 	f.fint = fint
@@ -109,7 +112,7 @@ func queuefinalizer(p unsafe.Pointer, fn *funcval, nret uintptr, fint *_type, ot
 //go:nowritebarrier
 func iterate_finq(callback func(*funcval, unsafe.Pointer, uintptr, *_type, *ptrtype)) {
 	for fb := allfin; fb != nil; fb = fb.alllink {
-		for i := int32(0); i < fb.cnt; i++ {
+		for i := uint32(0); i < fb.cnt; i++ {
 			f := &fb.fin[i]
 			callback(f.fn, f.arg, f.nret, f.fint, f.ot)
 		}
@@ -179,6 +182,11 @@ func runfinq() {
 				if f.fint == nil {
 					throw("missing type in runfinq")
 				}
+				// frame is effectively uninitialized
+				// memory. That means we have to clear
+				// it before writing to it to avoid
+				// confusing the write barrier.
+				*(*[2]uintptr)(frame) = [2]uintptr{}
 				switch f.fint.kind & kindMask {
 				case kindPtr:
 					// direct use of pointer
@@ -191,7 +199,7 @@ func runfinq() {
 					if len(ityp.mhdr) != 0 {
 						// convert to interface with methods
 						// this conversion is guaranteed to succeed - we checked in SetFinalizer
-						assertE2I(ityp, *(*eface)(frame), (*iface)(frame))
+						*(*iface)(frame) = assertE2I(ityp, *(*eface)(frame))
 					}
 				default:
 					throw("bad kind in runfinq")
@@ -200,11 +208,14 @@ func runfinq() {
 				reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz))
 				fingRunning = false
 
-				// drop finalizer queue references to finalized object
+				// Drop finalizer queue heap references
+				// before hiding them from markroot.
+				// This also ensures these will be
+				// clear if we reuse the finalizer.
 				f.fn = nil
 				f.arg = nil
 				f.ot = nil
-				fb.cnt = i - 1
+				atomic.Store(&fb.cnt, i-1)
 			}
 			next := fb.next
 			lock(&finlock)
@@ -226,11 +237,12 @@ func runfinq() {
 //
 // SetFinalizer(obj, nil) clears any finalizer associated with obj.
 //
-// The argument obj must be a pointer to an object allocated by
-// calling new or by taking the address of a composite literal.
+// The argument obj must be a pointer to an object allocated by calling
+// new, by taking the address of a composite literal, or by taking the
+// address of a local variable.
 // The argument finalizer must be a function that takes a single argument
 // to which obj's type can be assigned, and can have arbitrary ignored return
-// values. If either of these is not true, SetFinalizer aborts the
+// values. If either of these is not true, SetFinalizer may abort the
 // program.
 //
 // Finalizers are run in dependency order: if A points at B, both have
@@ -352,7 +364,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
 	if ft.dotdotdot() {
 		throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string() + " because dotdotdot")
 	}
-	if ft.dotdotdot() || ft.inCount != 1 {
+	if ft.inCount != 1 {
 		throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string())
 	}
 	fint := ft.in()[0]
@@ -372,7 +384,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
 			// ok - satisfies empty interface
 			goto okarg
 		}
-		if assertE2I2(ityp, *efaceOf(&obj), nil) {
+		if _, ok := assertE2I2(ityp, *efaceOf(&obj)); ok {
 			goto okarg
 		}
 	}
@@ -416,7 +428,7 @@ func findObject(v unsafe.Pointer) (s *mspan, x unsafe.Pointer, n uintptr) {
 	}
 	p := uintptr(v) >> pageShift
 	q := p - arena_start>>pageShift
-	s = *(**mspan)(add(unsafe.Pointer(mheap_.spans), q*sys.PtrSize))
+	s = mheap_.spans[q]
 	if s == nil {
 		return
 	}
diff --git a/src/runtime/mfixalloc.go b/src/runtime/mfixalloc.go
index c4ab648..fe4b0fc 100644
--- a/src/runtime/mfixalloc.go
+++ b/src/runtime/mfixalloc.go
@@ -14,10 +14,16 @@ import "unsafe"
 // Malloc uses a FixAlloc wrapped around sysAlloc to manages its
 // MCache and MSpan objects.
 //
-// Memory returned by FixAlloc_Alloc is not zeroed.
+// Memory returned by fixalloc.alloc is zeroed by default, but the
+// caller may take responsibility for zeroing allocations by setting
+// the zero flag to false. This is only safe if the memory never
+// contains heap pointers.
+//
 // The caller is responsible for locking around FixAlloc calls.
 // Callers can keep state in the object but the first word is
 // smashed by freeing and reallocating.
+//
+// Consider marking fixalloc'd types go:notinheap.
 type fixalloc struct {
 	size   uintptr
 	first  func(arg, p unsafe.Pointer) // called first time p is returned
@@ -27,6 +33,7 @@ type fixalloc struct {
 	nchunk uint32
 	inuse  uintptr // in-use bytes now
 	stat   *uint64
+	zero   bool // zero allocations
 }
 
 // A generic linked list of blocks.  (Typically the block is bigger than sizeof(MLink).)
@@ -34,6 +41,8 @@ type fixalloc struct {
 // this cannot be used by some of the internal GC structures. For example when
 // the sweeper is placing an unmarked object on the free list it does not want the
 // write barrier to be called since that could result in the object being reachable.
+//
+//go:notinheap
 type mlink struct {
 	next *mlink
 }
@@ -49,6 +58,7 @@ func (f *fixalloc) init(size uintptr, first func(arg, p unsafe.Pointer), arg uns
 	f.nchunk = 0
 	f.inuse = 0
 	f.stat = stat
+	f.zero = true
 }
 
 func (f *fixalloc) alloc() unsafe.Pointer {
@@ -61,6 +71,9 @@ func (f *fixalloc) alloc() unsafe.Pointer {
 		v := unsafe.Pointer(f.list)
 		f.list = f.list.next
 		f.inuse += f.size
+		if f.zero {
+			memclrNoHeapPointers(v, f.size)
+		}
 		return v
 	}
 	if uintptr(f.nchunk) < f.size {
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index 3b238cb..cc79d4c 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -122,6 +122,15 @@
 // proportion to the allocation cost. Adjusting GOGC just changes the linear constant
 // (and also the amount of extra memory used).
 
+// Oblets
+//
+// In order to prevent long pauses while scanning large objects and to
+// improve parallelism, the garbage collector breaks up scan jobs for
+// objects larger than maxObletBytes into "oblets" of at most
+// maxObletBytes. When scanning encounters the beginning of a large
+// object, it scans only the first oblet and enqueues the remaining
+// oblets as new scan jobs.
+
 package runtime
 
 import (
@@ -167,24 +176,29 @@ func gcinit() {
 	}
 
 	_ = setGCPercent(readgogc())
-	for datap := &firstmoduledata; datap != nil; datap = datap.next {
-		datap.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(datap.gcdata)), datap.edata-datap.data)
-		datap.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(datap.gcbss)), datap.ebss-datap.bss)
+	memstats.gc_trigger = heapminimum
+	// Compute the goal heap size based on the trigger:
+	//   trigger = marked * (1 + triggerRatio)
+	//   marked = trigger / (1 + triggerRatio)
+	//   goal = marked * (1 + GOGC/100)
+	//        = trigger / (1 + triggerRatio) * (1 + GOGC/100)
+	memstats.next_gc = uint64(float64(memstats.gc_trigger) / (1 + gcController.triggerRatio) * (1 + float64(gcpercent)/100))
+	if gcpercent < 0 {
+		memstats.next_gc = ^uint64(0)
 	}
-	memstats.next_gc = heapminimum
 	work.startSema = 1
 	work.markDoneSema = 1
 }
 
 func readgogc() int32 {
 	p := gogetenv("GOGC")
-	if p == "" {
-		return 100
-	}
 	if p == "off" {
 		return -1
 	}
-	return int32(atoi(p))
+	if n, ok := atoi32(p); ok {
+		return n
+	}
+	return 100
 }
 
 // gcenable is called after the bulk of the runtime initialization,
@@ -209,21 +223,25 @@ func setGCPercent(in int32) (out int32) {
 	if gcController.triggerRatio > float64(gcpercent)/100 {
 		gcController.triggerRatio = float64(gcpercent) / 100
 	}
+	// This is either in gcinit or followed by a STW GC, both of
+	// which will reset other stats like memstats.gc_trigger and
+	// memstats.next_gc to appropriate values.
 	unlock(&mheap_.lock)
 	return out
 }
 
 // Garbage collector phase.
-// Indicates to write barrier and sychronization task to preform.
+// Indicates to write barrier and synchronization task to perform.
 var gcphase uint32
 
 // The compiler knows about this variable.
 // If you change it, you must change the compiler too.
 var writeBarrier struct {
-	enabled bool   // compiler emits a check of this before calling write barrier
-	needed  bool   // whether we need a write barrier for current GC phase
-	cgo     bool   // whether we need a write barrier for a cgo check
-	alignme uint64 // guarantee alignment so that compiler can use a 32 or 64-bit load
+	enabled bool    // compiler emits a check of this before calling write barrier
+	pad     [3]byte // compiler uses 32-bit load for "enabled" field
+	needed  bool    // whether we need a write barrier for current GC phase
+	cgo     bool    // whether we need a write barrier for a cgo check
+	alignme uint64  // guarantee alignment so that compiler can use a 32 or 64-bit load
 }
 
 // gcBlackenEnabled is 1 if mutator assists and background mark
@@ -289,11 +307,19 @@ const (
 	gcMarkWorkerIdleMode
 )
 
+// gcMarkWorkerModeStrings are the strings labels of gcMarkWorkerModes
+// to use in execution traces.
+var gcMarkWorkerModeStrings = [...]string{
+	"GC (dedicated)",
+	"GC (fractional)",
+	"GC (idle)",
+}
+
 // gcController implements the GC pacing controller that determines
 // when to trigger concurrent garbage collection and how much marking
 // work to do in mutator assists and background marking.
 //
-// It uses a feedback control algorithm to adjust the memstats.next_gc
+// It uses a feedback control algorithm to adjust the memstats.gc_trigger
 // trigger based on the heap growth and GC CPU utilization each cycle.
 // This algorithm optimizes for heap growth to match GOGC and for CPU
 // utilization between assist and background marking to be 25% of
@@ -349,10 +375,6 @@ type gcControllerState struct {
 	// that assists and background mark workers started.
 	markStartTime int64
 
-	// heapGoal is the goal memstats.heap_live for when this cycle
-	// ends. This is computed at the beginning of each cycle.
-	heapGoal uint64
-
 	// dedicatedMarkWorkersNeeded is the number of dedicated mark
 	// workers that need to be started. This is computed at the
 	// beginning of each cycle and decremented atomically as
@@ -380,8 +402,9 @@ type gcControllerState struct {
 	// triggerRatio is the heap growth ratio at which the garbage
 	// collection cycle should start. E.g., if this is 0.6, then
 	// GC should start when the live heap has reached 1.6 times
-	// the heap size marked by the previous cycle. This is updated
-	// at the end of of each cycle.
+	// the heap size marked by the previous cycle. This should be
+	// ≤ GOGC/100 so the trigger heap size is less than the goal
+	// heap size. This is updated at the end of of each cycle.
 	triggerRatio float64
 
 	_ [sys.CacheLineSize]byte
@@ -406,28 +429,31 @@ func (c *gcControllerState) startCycle() {
 	c.idleMarkTime = 0
 
 	// If this is the first GC cycle or we're operating on a very
-	// small heap, fake heap_marked so it looks like next_gc is
+	// small heap, fake heap_marked so it looks like gc_trigger is
 	// the appropriate growth from heap_marked, even though the
 	// real heap_marked may not have a meaningful value (on the
 	// first cycle) or may be much smaller (resulting in a large
 	// error response).
-	if memstats.next_gc <= heapminimum {
-		memstats.heap_marked = uint64(float64(memstats.next_gc) / (1 + c.triggerRatio))
-		memstats.heap_reachable = memstats.heap_marked
+	if memstats.gc_trigger <= heapminimum {
+		memstats.heap_marked = uint64(float64(memstats.gc_trigger) / (1 + c.triggerRatio))
 	}
 
-	// Compute the heap goal for this cycle
-	c.heapGoal = memstats.heap_reachable + memstats.heap_reachable*uint64(gcpercent)/100
+	// Re-compute the heap goal for this cycle in case something
+	// changed. This is the same calculation we use elsewhere.
+	memstats.next_gc = memstats.heap_marked + memstats.heap_marked*uint64(gcpercent)/100
+	if gcpercent < 0 {
+		memstats.next_gc = ^uint64(0)
+	}
 
 	// Ensure that the heap goal is at least a little larger than
 	// the current live heap size. This may not be the case if GC
 	// start is delayed or if the allocation that pushed heap_live
-	// over next_gc is large or if the trigger is really close to
+	// over gc_trigger is large or if the trigger is really close to
 	// GOGC. Assist is proportional to this distance, so enforce a
 	// minimum distance, even if it means going over the GOGC goal
 	// by a tiny bit.
-	if c.heapGoal < memstats.heap_live+1024*1024 {
-		c.heapGoal = memstats.heap_live + 1024*1024
+	if memstats.next_gc < memstats.heap_live+1024*1024 {
+		memstats.next_gc = memstats.heap_live + 1024*1024
 	}
 
 	// Compute the total mark utilization goal and divide it among
@@ -457,7 +483,7 @@ func (c *gcControllerState) startCycle() {
 		print("pacer: assist ratio=", c.assistWorkPerByte,
 			" (scan ", memstats.heap_scan>>20, " MB in ",
 			work.initialHeapLive>>20, "->",
-			c.heapGoal>>20, " MB)",
+			memstats.next_gc>>20, " MB)",
 			" workers=", c.dedicatedMarkWorkersNeeded,
 			"+", c.fractionalMarkWorkersNeeded, "\n")
 	}
@@ -506,7 +532,7 @@ func (c *gcControllerState) revise() {
 	}
 
 	// Compute the heap distance remaining.
-	heapDistance := int64(c.heapGoal) - int64(memstats.heap_live)
+	heapDistance := int64(memstats.next_gc) - int64(memstats.heap_live)
 	if heapDistance <= 0 {
 		// This shouldn't happen, but if it does, avoid
 		// dividing by zero or setting the assist negative.
@@ -541,10 +567,6 @@ func (c *gcControllerState) endCycle() {
 	// growth if we had the desired CPU utilization). The
 	// difference between this estimate and the GOGC-based goal
 	// heap growth is the error.
-	//
-	// TODO(austin): next_gc is based on heap_reachable, not
-	// heap_marked, which means the actual growth ratio
-	// technically isn't comparable to the trigger ratio.
 	goalGrowthRatio := float64(gcpercent) / 100
 	actualGrowthRatio := float64(memstats.heap_live)/float64(memstats.heap_marked) - 1
 	assistDuration := nanotime() - c.markStartTime
@@ -575,7 +597,7 @@ func (c *gcControllerState) endCycle() {
 		// Print controller state in terms of the design
 		// document.
 		H_m_prev := memstats.heap_marked
-		H_T := memstats.next_gc
+		H_T := memstats.gc_trigger
 		h_a := actualGrowthRatio
 		H_a := memstats.heap_live
 		h_g := goalGrowthRatio
@@ -602,6 +624,14 @@ func (c *gcControllerState) endCycle() {
 //
 //go:nowritebarrier
 func (c *gcControllerState) enlistWorker() {
+	// If there are idle Ps, wake one so it will run an idle worker.
+	if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 {
+		wakep()
+		return
+	}
+
+	// There are no idle Ps. If we need more dedicated workers,
+	// try to preempt a running P so it will switch to a worker.
 	if c.dedicatedMarkWorkersNeeded <= 0 {
 		return
 	}
@@ -615,7 +645,7 @@ func (c *gcControllerState) enlistWorker() {
 	}
 	myID := gp.m.p.ptr().id
 	for tries := 0; tries < 5; tries++ {
-		id := int32(fastrand1() % uint32(gomaxprocs-1))
+		id := int32(fastrand() % uint32(gomaxprocs-1))
 		if id >= myID {
 			id++
 		}
@@ -751,6 +781,22 @@ var work struct {
 	empty uint64                   // lock-free list of empty blocks workbuf
 	pad0  [sys.CacheLineSize]uint8 // prevents false-sharing between full/empty and nproc/nwait
 
+	// bytesMarked is the number of bytes marked this cycle. This
+	// includes bytes blackened in scanned objects, noscan objects
+	// that go straight to black, and permagrey objects scanned by
+	// markroot during the concurrent scan phase. This is updated
+	// atomically during the cycle. Updates may be batched
+	// arbitrarily, since the value is only read at the end of the
+	// cycle.
+	//
+	// Because of benign races during marking, this number may not
+	// be the exact number of marked bytes, but it should be very
+	// close.
+	//
+	// Put this field here because it needs 64-bit atomic access
+	// (and thus 8-byte alignment even on 32-bit architectures).
+	bytesMarked uint64
+
 	markrootNext uint32 // next markroot job
 	markrootJobs uint32 // number of markroot jobs
 
@@ -760,7 +806,18 @@ var work struct {
 	ndone   uint32
 	alldone note
 
+	// helperDrainBlock indicates that GC mark termination helpers
+	// should pass gcDrainBlock to gcDrain to block in the
+	// getfull() barrier. Otherwise, they should pass gcDrainNoBlock.
+	//
+	// TODO: This is a temporary fallback to support
+	// debug.gcrescanstacks > 0 and to work around some known
+	// races. Remove this when we remove the debug option and fix
+	// the races.
+	helperDrainBlock bool
+
 	// Number of roots of various root types. Set by gcMarkRootPrepare.
+	nFlushCacheRoots                                             int
 	nDataRoots, nBSSRoots, nSpanRoots, nStackRoots, nRescanRoots int
 
 	// markrootDone indicates that roots have been marked at least
@@ -797,26 +854,10 @@ var work struct {
 	// mode is the concurrency mode of the current GC cycle.
 	mode gcMode
 
-	// Copy of mheap.allspans for marker or sweeper.
-	spans []*mspan
-
 	// totaltime is the CPU nanoseconds spent in GC since the
 	// program started if debug.gctrace > 0.
 	totaltime int64
 
-	// bytesMarked is the number of bytes marked this cycle. This
-	// includes bytes blackened in scanned objects, noscan objects
-	// that go straight to black, and permagrey objects scanned by
-	// markroot during the concurrent scan phase. This is updated
-	// atomically during the cycle. Updates may be batched
-	// arbitrarily, since the value is only read at the end of the
-	// cycle.
-	//
-	// Because of benign races during marking, this number may not
-	// be the exact number of marked bytes, but it should be very
-	// close.
-	bytesMarked uint64
-
 	// initialHeapLive is the value of memstats.heap_live at the
 	// beginning of this GC cycle.
 	initialHeapLive uint64
@@ -871,7 +912,7 @@ const (
 // If forceTrigger is true, it ignores the current heap size, but
 // checks all other conditions. In general this should be false.
 func gcShouldStart(forceTrigger bool) bool {
-	return gcphase == _GCoff && (forceTrigger || memstats.heap_live >= memstats.next_gc) && memstats.enablegc && panicking == 0 && gcpercent >= 0
+	return gcphase == _GCoff && (forceTrigger || memstats.heap_live >= memstats.gc_trigger) && memstats.enablegc && panicking == 0 && gcpercent >= 0
 }
 
 // gcStart transitions the GC from _GCoff to _GCmark (if mode ==
@@ -917,7 +958,7 @@ func gcStart(mode gcMode, forceTrigger bool) {
 	// another thread.
 	useStartSema := mode == gcBackgroundMode
 	if useStartSema {
-		semacquire(&work.startSema, false)
+		semacquire(&work.startSema, 0)
 		// Re-check transition condition under transition lock.
 		if !gcShouldStart(forceTrigger) {
 			semrelease(&work.startSema)
@@ -938,7 +979,7 @@ func gcStart(mode gcMode, forceTrigger bool) {
 	}
 
 	// Ok, we're doing it!  Stop everybody else
-	semacquire(&worldsema, false)
+	semacquire(&worldsema, 0)
 
 	if trace.enabled {
 		traceGCStart()
@@ -961,7 +1002,7 @@ func gcStart(mode gcMode, forceTrigger bool) {
 	systemstack(stopTheWorldWithSema)
 	// Finish sweep before we start concurrent scan.
 	systemstack(func() {
-		finishsweep_m(true)
+		finishsweep_m()
 	})
 	// clearpools before we start the GC. If we wait they memory will not be
 	// reclaimed until the next GC cycle.
@@ -969,7 +1010,7 @@ func gcStart(mode gcMode, forceTrigger bool) {
 
 	if mode == gcBackgroundMode { // Do as much work concurrently as possible
 		gcController.startCycle()
-		work.heapGoal = gcController.heapGoal
+		work.heapGoal = memstats.next_gc
 
 		// Enter concurrent mark phase and enable
 		// write barriers.
@@ -998,13 +1039,16 @@ func gcStart(mode gcMode, forceTrigger bool) {
 		// possible.
 		setGCPhase(_GCmark)
 
-		// markrootSpans uses work.spans, so make sure
-		// it is up to date.
-		gcCopySpans()
-
 		gcBgMarkPrepare() // Must happen before assist enable.
 		gcMarkRootPrepare()
 
+		// Mark all active tinyalloc blocks. Since we're
+		// allocating from these, they need to be black like
+		// other allocations. The alternative is to blacken
+		// the tiny block on every allocation from it, which
+		// would slow down the tiny allocator.
+		gcMarkTinyAllocs()
+
 		// At this point all Ps have enabled the write
 		// barrier, thus maintaining the no white to
 		// black invariant. Enable mutator assists to
@@ -1052,7 +1096,7 @@ func gcStart(mode gcMode, forceTrigger bool) {
 // by mark termination.
 func gcMarkDone() {
 top:
-	semacquire(&work.markDoneSema, false)
+	semacquire(&work.markDoneSema, 0)
 
 	// Re-check transition condition under transition lock.
 	if !(gcphase == _GCmark && work.nwait == work.nproc && !gcMarkWorkAvailable(nil)) {
@@ -1072,9 +1116,8 @@ top:
 		// Transition from mark 1 to mark 2.
 		//
 		// The global work list is empty, but there can still be work
-		// sitting in the per-P work caches and there can be more
-		// objects reachable from global roots since they don't have write
-		// barriers. Rescan some roots and flush work caches.
+		// sitting in the per-P work caches.
+		// Flush and disable work caches.
 
 		gcMarkRootCheck()
 
@@ -1094,8 +1137,7 @@ top:
 			// ensure all Ps see gcBlackenPromptly. This
 			// also blocks until any remaining mark 1
 			// workers have exited their loop so we can
-			// start new mark 2 workers that will observe
-			// the new root marking jobs.
+			// start new mark 2 workers.
 			forEachP(func(_p_ *p) {
 				_p_.gcw.dispose()
 			})
@@ -1130,11 +1172,6 @@ top:
 		// this before waking blocked assists.
 		atomic.Store(&gcBlackenEnabled, 0)
 
-		// Flush the gcWork caches. This must be done before
-		// endCycle since endCycle depends on statistics kept
-		// in these caches.
-		gcFlushGCWork()
-
 		// Wake all blocked assists. These will run when we
 		// start the world again.
 		gcWakeAllAssists()
@@ -1144,6 +1181,8 @@ top:
 		// world again.
 		semrelease(&work.markDoneSema)
 
+		// endCycle depends on all gcWork cache stats being
+		// flushed. This is ensured by mark 2.
 		gcController.endCycle()
 
 		// Perform mark termination. This will restart the world.
@@ -1207,7 +1246,7 @@ func gcMarkTermination() {
 			// they have gcscanvalid==true and gcworkdone==true.
 			// Reset these so that all stacks will be rescanned.
 			gcResetMarkState()
-			finishsweep_m(true)
+			finishsweep_m()
 
 			// Still in STW but gcphase is _GCoff, reset to _GCmarktermination
 			// At this point all objects will be found during the gcMark which
@@ -1263,6 +1302,18 @@ func gcMarkTermination() {
 
 	systemstack(startTheWorldWithSema)
 
+	// Update heap profile stats if gcSweep didn't do it. This is
+	// relatively expensive, so we don't want to do it while the
+	// world is stopped, but it needs to happen ASAP after
+	// starting the world to prevent too many allocations from the
+	// next cycle leaking in. It must happen before releasing
+	// worldsema since there are applications that do a
+	// runtime.GC() to update the heap profile and then
+	// immediately collect the profile.
+	if _ConcurrentSweep && work.mode != gcForceBlockMode {
+		mProf_GC()
+	}
+
 	// Free stack spans. This must be done between GC cycles.
 	systemstack(freeStackSpans)
 
@@ -1443,14 +1494,27 @@ func gcBgMarkWorker(_p_ *p) {
 			throw("work.nwait was > work.nproc")
 		}
 
-		switch _p_.gcMarkWorkerMode {
-		default:
-			throw("gcBgMarkWorker: unexpected gcMarkWorkerMode")
-		case gcMarkWorkerDedicatedMode:
-			gcDrain(&_p_.gcw, gcDrainNoBlock|gcDrainFlushBgCredit)
-		case gcMarkWorkerFractionalMode, gcMarkWorkerIdleMode:
-			gcDrain(&_p_.gcw, gcDrainUntilPreempt|gcDrainFlushBgCredit)
-		}
+		systemstack(func() {
+			// Mark our goroutine preemptible so its stack
+			// can be scanned. This lets two mark workers
+			// scan each other (otherwise, they would
+			// deadlock). We must not modify anything on
+			// the G stack. However, stack shrinking is
+			// disabled for mark workers, so it is safe to
+			// read from the G stack.
+			casgstatus(gp, _Grunning, _Gwaiting)
+			switch _p_.gcMarkWorkerMode {
+			default:
+				throw("gcBgMarkWorker: unexpected gcMarkWorkerMode")
+			case gcMarkWorkerDedicatedMode:
+				gcDrain(&_p_.gcw, gcDrainNoBlock|gcDrainFlushBgCredit)
+			case gcMarkWorkerFractionalMode:
+				gcDrain(&_p_.gcw, gcDrainUntilPreempt|gcDrainFlushBgCredit)
+			case gcMarkWorkerIdleMode:
+				gcDrain(&_p_.gcw, gcDrainIdle|gcDrainUntilPreempt|gcDrainFlushBgCredit)
+			}
+			casgstatus(gp, _Gwaiting, _Grunning)
+		})
 
 		// If we are nearing the end of mark, dispose
 		// of the cache promptly. We must do this
@@ -1524,18 +1588,8 @@ func gcMarkWorkAvailable(p *p) bool {
 	return false
 }
 
-// gcFlushGCWork disposes the gcWork caches of all Ps. The world must
-// be stopped.
-//go:nowritebarrier
-func gcFlushGCWork() {
-	// Gather all cached GC work. All other Ps are stopped, so
-	// it's safe to manipulate their GC work caches.
-	for i := 0; i < int(gomaxprocs); i++ {
-		allp[i].gcw.dispose()
-	}
-}
-
 // gcMark runs the mark (or, for concurrent GC, mark termination)
+// All gcWork caches must be empty.
 // STW is in effect at this point.
 //TODO go:nowritebarrier
 func gcMark(start_time int64) {
@@ -1548,13 +1602,6 @@ func gcMark(start_time int64) {
 	}
 	work.tstart = start_time
 
-	gcCopySpans() // TODO(rlh): should this be hoisted and done only once? Right now it is done for normal marking and also for checkmarking.
-
-	// Make sure the per-P gcWork caches are empty. During mark
-	// termination, these caches can still be used temporarily,
-	// but must be disposed to the global lists immediately.
-	gcFlushGCWork()
-
 	// Queue root marking jobs.
 	gcMarkRootPrepare()
 
@@ -1562,6 +1609,31 @@ func gcMark(start_time int64) {
 	work.ndone = 0
 	work.nproc = uint32(gcprocs())
 
+	if debug.gcrescanstacks == 0 && work.full == 0 && work.nDataRoots+work.nBSSRoots+work.nSpanRoots+work.nStackRoots+work.nRescanRoots == 0 {
+		// There's no work on the work queue and no root jobs
+		// that can produce work, so don't bother entering the
+		// getfull() barrier.
+		//
+		// With the hybrid barrier enabled, this will be the
+		// situation the vast majority of the time after
+		// concurrent mark. However, we still need a fallback
+		// for STW GC and because there are some known races
+		// that occasionally leave work around for mark
+		// termination.
+		//
+		// We're still hedging our bets here: if we do
+		// accidentally produce some work, we'll still process
+		// it, just not necessarily in parallel.
+		//
+		// TODO(austin): When we eliminate
+		// debug.gcrescanstacks: fix the races, and remove
+		// work draining from mark termination so we don't
+		// need the fallback path.
+		work.helperDrainBlock = false
+	} else {
+		work.helperDrainBlock = true
+	}
+
 	if trace.enabled {
 		traceGCScanStart()
 	}
@@ -1574,7 +1646,11 @@ func gcMark(start_time int64) {
 	gchelperstart()
 
 	gcw := &getg().m.p.ptr().gcw
-	gcDrain(gcw, gcDrainBlock)
+	if work.helperDrainBlock {
+		gcDrain(gcw, gcDrainBlock)
+	} else {
+		gcDrain(gcw, gcDrainNoBlock)
+	}
 	gcw.dispose()
 
 	if debug.gccheckmark > 0 {
@@ -1593,6 +1669,8 @@ func gcMark(start_time int64) {
 	// Record that at least one root marking pass has completed.
 	work.markrootDone = true
 
+	// Double-check that all gcWork caches are empty. This should
+	// be ensured by mark 2 before we enter mark termination.
 	for i := 0; i < int(gomaxprocs); i++ {
 		gcw := &allp[i].gcw
 		if !gcw.empty() {
@@ -1609,42 +1687,50 @@ func gcMark(start_time int64) {
 
 	cachestats()
 
-	// Update the reachable heap stat.
-	memstats.heap_reachable = work.bytesMarked
+	// Update the marked heap stat.
+	memstats.heap_marked = work.bytesMarked
 
 	// Trigger the next GC cycle when the allocated heap has grown
-	// by triggerRatio over the reachable heap size. Assume that
-	// we're in steady state, so the reachable heap size is the
+	// by triggerRatio over the marked heap size. Assume that
+	// we're in steady state, so the marked heap size is the
 	// same now as it was at the beginning of the GC cycle.
-	memstats.next_gc = uint64(float64(memstats.heap_reachable) * (1 + gcController.triggerRatio))
-	if memstats.next_gc < heapminimum {
-		memstats.next_gc = heapminimum
+	memstats.gc_trigger = uint64(float64(memstats.heap_marked) * (1 + gcController.triggerRatio))
+	if memstats.gc_trigger < heapminimum {
+		memstats.gc_trigger = heapminimum
 	}
-	if int64(memstats.next_gc) < 0 {
+	if int64(memstats.gc_trigger) < 0 {
 		print("next_gc=", memstats.next_gc, " bytesMarked=", work.bytesMarked, " heap_live=", memstats.heap_live, " initialHeapLive=", work.initialHeapLive, "\n")
-		throw("next_gc underflow")
+		throw("gc_trigger underflow")
 	}
 
 	// Update other GC heap size stats. This must happen after
 	// cachestats (which flushes local statistics to these) and
 	// flushallmcaches (which modifies heap_live).
 	memstats.heap_live = work.bytesMarked
-	memstats.heap_marked = work.bytesMarked
 	memstats.heap_scan = uint64(gcController.scanWork)
 
-	minNextGC := memstats.heap_live + sweepMinHeapDistance*uint64(gcpercent)/100
-	if memstats.next_gc < minNextGC {
+	minTrigger := memstats.heap_live + sweepMinHeapDistance*uint64(gcpercent)/100
+	if memstats.gc_trigger < minTrigger {
 		// The allocated heap is already past the trigger.
 		// This can happen if the triggerRatio is very low and
-		// the reachable heap estimate is less than the live
-		// heap size.
+		// the marked heap is less than the live heap size.
 		//
 		// Concurrent sweep happens in the heap growth from
-		// heap_live to next_gc, so bump next_gc up to ensure
+		// heap_live to gc_trigger, so bump gc_trigger up to ensure
 		// that concurrent sweep has some heap growth in which
 		// to perform sweeping before we start the next GC
 		// cycle.
-		memstats.next_gc = minNextGC
+		memstats.gc_trigger = minTrigger
+	}
+
+	// The next GC cycle should finish before the allocated heap
+	// has grown by GOGC/100.
+	memstats.next_gc = memstats.heap_marked + memstats.heap_marked*uint64(gcpercent)/100
+	if gcpercent < 0 {
+		memstats.next_gc = ^uint64(0)
+	}
+	if memstats.next_gc < memstats.gc_trigger {
+		memstats.next_gc = memstats.gc_trigger
 	}
 
 	if trace.enabled {
@@ -1657,12 +1743,16 @@ func gcSweep(mode gcMode) {
 	if gcphase != _GCoff {
 		throw("gcSweep being done but phase is not GCoff")
 	}
-	gcCopySpans()
 
 	lock(&mheap_.lock)
 	mheap_.sweepgen += 2
 	mheap_.sweepdone = 0
-	sweep.spanidx = 0
+	if mheap_.sweepSpans[mheap_.sweepgen/2%2].index != 0 {
+		// We should have drained this list during the last
+		// sweep phase. We certainly need to start this phase
+		// with an empty swept list.
+		throw("non-empty swept list")
+	}
 	unlock(&mheap_.lock)
 
 	if !_ConcurrentSweep || mode == gcForceBlockMode {
@@ -1685,7 +1775,7 @@ func gcSweep(mode gcMode) {
 	// Concurrent sweep needs to sweep all of the in-use pages by
 	// the time the allocated heap reaches the GC trigger. Compute
 	// the ratio of in-use pages to sweep per byte allocated.
-	heapDistance := int64(memstats.next_gc) - int64(memstats.heap_live)
+	heapDistance := int64(memstats.gc_trigger) - int64(memstats.heap_live)
 	// Add a little margin so rounding errors and concurrent
 	// sweep are less likely to leave pages unswept when GC starts.
 	heapDistance -= 1024 * 1024
@@ -1706,26 +1796,6 @@ func gcSweep(mode gcMode) {
 		ready(sweep.g, 0, true)
 	}
 	unlock(&sweep.lock)
-	mProf_GC()
-}
-
-func gcCopySpans() {
-	// Cache runtime.mheap_.allspans in work.spans to avoid conflicts with
-	// resizing/freeing allspans.
-	// New spans can be created while GC progresses, but they are not garbage for
-	// this round:
-	//  - new stack spans can be created even while the world is stopped.
-	//  - new malloc spans can be created during the concurrent sweep
-	// Even if this is stop-the-world, a concurrent exitsyscall can allocate a stack from heap.
-	lock(&mheap_.lock)
-	// Free the old cached mark array if necessary.
-	if work.spans != nil && &work.spans[0] != &h_allspans[0] {
-		sysFree(unsafe.Pointer(&work.spans[0]), uintptr(len(work.spans))*unsafe.Sizeof(work.spans[0]), &memstats.other_sys)
-	}
-	// Cache the current array for sweeping.
-	mheap_.gcspans = mheap_.allspans
-	work.spans = h_allspans
-	unlock(&mheap_.lock)
 }
 
 // gcResetMarkState resets global state prior to marking (concurrent
@@ -1816,7 +1886,11 @@ func gchelper() {
 	// Parallel mark over GC roots and heap
 	if gcphase == _GCmarktermination {
 		gcw := &_g_.m.p.ptr().gcw
-		gcDrain(gcw, gcDrainBlock) // blocks in getfull
+		if work.helperDrainBlock {
+			gcDrain(gcw, gcDrainBlock) // blocks in getfull
+		} else {
+			gcDrain(gcw, gcDrainNoBlock)
+		}
 		gcw.dispose()
 	}
 
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index aa7f7a7..85130bf 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -14,7 +14,6 @@ import (
 
 const (
 	fixedRootFinalizers = iota
-	fixedRootFlushCaches
 	fixedRootFreeGStacks
 	fixedRootCount
 
@@ -25,6 +24,23 @@ const (
 	// rootBlockSpans is the number of spans to scan per span
 	// root.
 	rootBlockSpans = 8 * 1024 // 64MB worth of spans
+
+	// maxObletBytes is the maximum bytes of an object to scan at
+	// once. Larger objects will be split up into "oblets" of at
+	// most this size. Since we can scan 1–2 MB/ms, 128 KB bounds
+	// scan preemption at ~100 µs.
+	//
+	// This must be > _MaxSmallSize so that the object base is the
+	// span base.
+	maxObletBytes = 128 << 10
+
+	// idleCheckThreshold specifies how many units of work to do
+	// between run queue checks in an idle worker. Assuming a scan
+	// rate of 1 MB/ms, this is ~100 µs. Lower values have higher
+	// overhead in the scan loop (the scheduler check may perform
+	// a syscall, so its overhead is nontrivial). Higher values
+	// make the system less responsive to incoming work.
+	idleCheckThreshold = 100000
 )
 
 // gcMarkRootPrepare queues root scanning jobs (stacks, globals, and
@@ -36,6 +52,12 @@ const (
 //
 //go:nowritebarrier
 func gcMarkRootPrepare() {
+	if gcphase == _GCmarktermination {
+		work.nFlushCacheRoots = int(gomaxprocs)
+	} else {
+		work.nFlushCacheRoots = 0
+	}
+
 	// Compute how many data and BSS root blocks there are.
 	nBlocks := func(bytes uintptr) int {
 		return int((bytes + rootBlockBytes - 1) / rootBlockBytes)
@@ -46,14 +68,14 @@ func gcMarkRootPrepare() {
 
 	// Only scan globals once per cycle; preferably concurrently.
 	if !work.markrootDone {
-		for datap := &firstmoduledata; datap != nil; datap = datap.next {
+		for _, datap := range activeModules() {
 			nDataRoots := nBlocks(datap.edata - datap.data)
 			if nDataRoots > work.nDataRoots {
 				work.nDataRoots = nDataRoots
 			}
 		}
 
-		for datap := &firstmoduledata; datap != nil; datap = datap.next {
+		for _, datap := range activeModules() {
 			nBSSRoots := nBlocks(datap.ebss - datap.bss)
 			if nBSSRoots > work.nBSSRoots {
 				work.nBSSRoots = nBSSRoots
@@ -68,7 +90,13 @@ func gcMarkRootPrepare() {
 		// above invariants for objects that get finalizers
 		// after concurrent mark. In STW GC, this will happen
 		// during mark termination.
-		work.nSpanRoots = (len(work.spans) + rootBlockSpans - 1) / rootBlockSpans
+		//
+		// We're only interested in scanning the in-use spans,
+		// which will all be swept at this point. More spans
+		// may be added to this list during concurrent GC, but
+		// we only care about spans that were allocated before
+		// this mark phase.
+		work.nSpanRoots = mheap_.sweepSpans[mheap_.sweepgen/2%2].numBlocks()
 
 		// On the first markroot, we need to scan all Gs. Gs
 		// may be created after this point, but it's okay that
@@ -93,7 +121,7 @@ func gcMarkRootPrepare() {
 	}
 
 	work.markrootNext = 0
-	work.markrootJobs = uint32(fixedRootCount + work.nDataRoots + work.nBSSRoots + work.nSpanRoots + work.nStackRoots + work.nRescanRoots)
+	work.markrootJobs = uint32(fixedRootCount + work.nFlushCacheRoots + work.nDataRoots + work.nBSSRoots + work.nSpanRoots + work.nStackRoots + work.nRescanRoots)
 }
 
 // gcMarkRootCheck checks that all roots have been scanned. It is
@@ -106,26 +134,32 @@ func gcMarkRootCheck() {
 
 	lock(&allglock)
 	// Check that stacks have been scanned.
-	if gcphase == _GCmarktermination {
+	var gp *g
+	if gcphase == _GCmarktermination && debug.gcrescanstacks > 0 {
 		for i := 0; i < len(allgs); i++ {
-			gp := allgs[i]
+			gp = allgs[i]
 			if !(gp.gcscandone && gp.gcscanvalid) && readgstatus(gp) != _Gdead {
-				println("gp", gp, "goid", gp.goid,
-					"status", readgstatus(gp),
-					"gcscandone", gp.gcscandone,
-					"gcscanvalid", gp.gcscanvalid)
-				throw("scan missed a g")
+				goto fail
 			}
 		}
 	} else {
 		for i := 0; i < work.nStackRoots; i++ {
-			gp := allgs[i]
+			gp = allgs[i]
 			if !gp.gcscandone {
-				throw("scan missed a g")
+				goto fail
 			}
 		}
 	}
 	unlock(&allglock)
+	return
+
+fail:
+	println("gp", gp, "goid", gp.goid,
+		"status", readgstatus(gp),
+		"gcscandone", gp.gcscandone,
+		"gcscanvalid", gp.gcscanvalid)
+	unlock(&allglock) // Avoid self-deadlock with traceback.
+	throw("scan missed a g")
 }
 
 // ptrmask for an allocation containing a single pointer.
@@ -141,7 +175,8 @@ var oneptrmask = [...]uint8{1}
 func markroot(gcw *gcWork, i uint32) {
 	// TODO(austin): This is a bit ridiculous. Compute and store
 	// the bases in gcMarkRootPrepare instead of the counts.
-	baseData := uint32(fixedRootCount)
+	baseFlushCache := uint32(fixedRootCount)
+	baseData := baseFlushCache + uint32(work.nFlushCacheRoots)
 	baseBSS := baseData + uint32(work.nDataRoots)
 	baseSpans := baseBSS + uint32(work.nBSSRoots)
 	baseStacks := baseSpans + uint32(work.nSpanRoots)
@@ -150,24 +185,23 @@ func markroot(gcw *gcWork, i uint32) {
 
 	// Note: if you add a case here, please also update heapdump.go:dumproots.
 	switch {
+	case baseFlushCache <= i && i < baseData:
+		flushmcache(int(i - baseFlushCache))
+
 	case baseData <= i && i < baseBSS:
-		for datap := &firstmoduledata; datap != nil; datap = datap.next {
+		for _, datap := range activeModules() {
 			markrootBlock(datap.data, datap.edata-datap.data, datap.gcdatamask.bytedata, gcw, int(i-baseData))
 		}
 
 	case baseBSS <= i && i < baseSpans:
-		for datap := &firstmoduledata; datap != nil; datap = datap.next {
+		for _, datap := range activeModules() {
 			markrootBlock(datap.bss, datap.ebss-datap.bss, datap.gcbssmask.bytedata, gcw, int(i-baseBSS))
 		}
 
 	case i == fixedRootFinalizers:
 		for fb := allfin; fb != nil; fb = fb.alllink {
-			scanblock(uintptr(unsafe.Pointer(&fb.fin[0])), uintptr(fb.cnt)*unsafe.Sizeof(fb.fin[0]), &finptrmask[0], gcw)
-		}
-
-	case i == fixedRootFlushCaches:
-		if gcphase == _GCmarktermination { // Do not flush mcaches during concurrent phase.
-			flushallmcaches()
+			cnt := uintptr(atomic.Load(&fb.cnt))
+			scanblock(uintptr(unsafe.Pointer(&fb.fin[0])), cnt*unsafe.Sizeof(fb.fin[0]), &finptrmask[0], gcw)
 		}
 
 	case i == fixedRootFreeGStacks:
@@ -190,6 +224,11 @@ func markroot(gcw *gcWork, i uint32) {
 			gp = allgs[i-baseStacks]
 		} else if baseRescan <= i && i < end {
 			gp = work.rescan.list[i-baseRescan].ptr()
+			if gp.gcRescan != int32(i-baseRescan) {
+				// Looking for issue #17099.
+				println("runtime: gp", gp, "found at rescan index", i-baseRescan, "but should be at", gp.gcRescan)
+				throw("bad g rescan index")
+			}
 		} else {
 			throw("markroot: bad index")
 		}
@@ -201,24 +240,13 @@ func markroot(gcw *gcWork, i uint32) {
 			gp.waitsince = work.tstart
 		}
 
-		if gcphase != _GCmarktermination && gp.startpc == gcBgMarkWorkerPC && readgstatus(gp) != _Gdead {
-			// GC background workers may be
-			// non-preemptible, so we may deadlock if we
-			// try to scan them during a concurrent phase.
-			// They also have tiny stacks, so just ignore
-			// them until mark termination.
-			gp.gcscandone = true
-			queueRescan(gp)
-			break
-		}
-
 		// scang must be done on the system stack in case
 		// we're trying to scan our own stack.
 		systemstack(func() {
 			// If this is a self-scan, put the user G in
 			// _Gwaiting to prevent self-deadlock. It may
-			// already be in _Gwaiting if this is mark
-			// termination.
+			// already be in _Gwaiting if this is a mark
+			// worker or we're in mark termination.
 			userG := getg().m.curg
 			selfScan := gp == userG && readgstatus(userG) == _Grunning
 			if selfScan {
@@ -318,18 +346,14 @@ func markrootSpans(gcw *gcWork, shard int) {
 	}
 
 	sg := mheap_.sweepgen
-	startSpan := shard * rootBlockSpans
-	endSpan := (shard + 1) * rootBlockSpans
-	if endSpan > len(work.spans) {
-		endSpan = len(work.spans)
-	}
+	spans := mheap_.sweepSpans[mheap_.sweepgen/2%2].block(shard)
 	// Note that work.spans may not include spans that were
 	// allocated between entering the scan phase and now. This is
 	// okay because any objects with finalizers in those spans
 	// must have been allocated and given finalizers after we
 	// entered the scan phase, so addfinalizer will have ensured
 	// the above invariants for them.
-	for _, s := range work.spans[startSpan:endSpan] {
+	for _, s := range spans {
 		if s.state != mSpanInUse {
 			continue
 		}
@@ -381,7 +405,6 @@ func markrootSpans(gcw *gcWork, shard int) {
 // gp must be the calling user gorountine.
 //
 // This must be called with preemption enabled.
-//go:nowritebarrier
 func gcAssistAlloc(gp *g) {
 	// Don't assist in non-preemptible contexts. These are
 	// generally fragile and won't allow the assist to block.
@@ -392,6 +415,7 @@ func gcAssistAlloc(gp *g) {
 		return
 	}
 
+retry:
 	// Compute the amount of scan work we need to do to make the
 	// balance positive. When the required amount of work is low,
 	// we over-assist to build up credit for future allocations
@@ -403,7 +427,6 @@ func gcAssistAlloc(gp *g) {
 		debtBytes = int64(gcController.assistBytesPerWork * float64(scanWork))
 	}
 
-retry:
 	// Steal as much credit as we can from the background GC's
 	// scan credit. This is racy and may drop the background
 	// credit below 0 if two mutators steal at the same time. This
@@ -432,72 +455,14 @@ retry:
 	}
 
 	// Perform assist work
-	completed := false
 	systemstack(func() {
-		if atomic.Load(&gcBlackenEnabled) == 0 {
-			// The gcBlackenEnabled check in malloc races with the
-			// store that clears it but an atomic check in every malloc
-			// would be a performance hit.
-			// Instead we recheck it here on the non-preemptable system
-			// stack to determine if we should preform an assist.
-
-			// GC is done, so ignore any remaining debt.
-			gp.gcAssistBytes = 0
-			return
-		}
-		// Track time spent in this assist. Since we're on the
-		// system stack, this is non-preemptible, so we can
-		// just measure start and end time.
-		startTime := nanotime()
-
-		decnwait := atomic.Xadd(&work.nwait, -1)
-		if decnwait == work.nproc {
-			println("runtime: work.nwait =", decnwait, "work.nproc=", work.nproc)
-			throw("nwait > work.nprocs")
-		}
-
-		// drain own cached work first in the hopes that it
-		// will be more cache friendly.
-		gcw := &getg().m.p.ptr().gcw
-		workDone := gcDrainN(gcw, scanWork)
-		// If we are near the end of the mark phase
-		// dispose of the gcw.
-		if gcBlackenPromptly {
-			gcw.dispose()
-		}
-
-		// Record that we did this much scan work.
-		//
-		// Back out the number of bytes of assist credit that
-		// this scan work counts for. The "1+" is a poor man's
-		// round-up, to ensure this adds credit even if
-		// assistBytesPerWork is very low.
-		gp.gcAssistBytes += 1 + int64(gcController.assistBytesPerWork*float64(workDone))
-
-		// If this is the last worker and we ran out of work,
-		// signal a completion point.
-		incnwait := atomic.Xadd(&work.nwait, +1)
-		if incnwait > work.nproc {
-			println("runtime: work.nwait=", incnwait,
-				"work.nproc=", work.nproc,
-				"gcBlackenPromptly=", gcBlackenPromptly)
-			throw("work.nwait > work.nproc")
-		}
-
-		if incnwait == work.nproc && !gcMarkWorkAvailable(nil) {
-			// This has reached a background completion
-			// point.
-			completed = true
-		}
-		duration := nanotime() - startTime
-		_p_ := gp.m.p.ptr()
-		_p_.gcAssistTime += duration
-		if _p_.gcAssistTime > gcAssistTimeSlack {
-			atomic.Xaddint64(&gcController.assistTime, _p_.gcAssistTime)
-			_p_.gcAssistTime = 0
-		}
+		gcAssistAlloc1(gp, scanWork)
+		// The user stack may have moved, so this can't touch
+		// anything on it until it returns from systemstack.
 	})
 
+	completed := gp.param != nil
+	gp.param = nil
 	if completed {
 		gcMarkDone()
 	}
@@ -524,46 +489,102 @@ retry:
 		// there wasn't enough work to do anyway, so we might
 		// as well let background marking take care of the
 		// work that is available.
-		lock(&work.assistQueue.lock)
-
-		// If the GC cycle is over, just return. This is the
-		// likely path if we completed above. We do this
-		// under the lock to prevent a GC cycle from ending
-		// between this check and queuing the assist.
-		if atomic.Load(&gcBlackenEnabled) == 0 {
-			unlock(&work.assistQueue.lock)
-			return
-		}
-
-		oldHead, oldTail := work.assistQueue.head, work.assistQueue.tail
-		if oldHead == 0 {
-			work.assistQueue.head.set(gp)
-		} else {
-			oldTail.ptr().schedlink.set(gp)
-		}
-		work.assistQueue.tail.set(gp)
-		gp.schedlink.set(nil)
-		// Recheck for background credit now that this G is in
-		// the queue, but can still back out. This avoids a
-		// race in case background marking has flushed more
-		// credit since we checked above.
-		if atomic.Loadint64(&gcController.bgScanCredit) > 0 {
-			work.assistQueue.head = oldHead
-			work.assistQueue.tail = oldTail
-			if oldTail != 0 {
-				oldTail.ptr().schedlink.set(nil)
-			}
-			unlock(&work.assistQueue.lock)
+		if !gcParkAssist() {
 			goto retry
 		}
-		// Park for real.
-		goparkunlock(&work.assistQueue.lock, "GC assist wait", traceEvGoBlock, 2)
 
 		// At this point either background GC has satisfied
 		// this G's assist debt, or the GC cycle is over.
 	}
 }
 
+// gcAssistAlloc1 is the part of gcAssistAlloc that runs on the system
+// stack. This is a separate function to make it easier to see that
+// we're not capturing anything from the user stack, since the user
+// stack may move while we're in this function.
+//
+// gcAssistAlloc1 indicates whether this assist completed the mark
+// phase by setting gp.param to non-nil. This can't be communicated on
+// the stack since it may move.
+//
+//go:systemstack
+func gcAssistAlloc1(gp *g, scanWork int64) {
+	// Clear the flag indicating that this assist completed the
+	// mark phase.
+	gp.param = nil
+
+	if atomic.Load(&gcBlackenEnabled) == 0 {
+		// The gcBlackenEnabled check in malloc races with the
+		// store that clears it but an atomic check in every malloc
+		// would be a performance hit.
+		// Instead we recheck it here on the non-preemptable system
+		// stack to determine if we should preform an assist.
+
+		// GC is done, so ignore any remaining debt.
+		gp.gcAssistBytes = 0
+		return
+	}
+	// Track time spent in this assist. Since we're on the
+	// system stack, this is non-preemptible, so we can
+	// just measure start and end time.
+	startTime := nanotime()
+
+	decnwait := atomic.Xadd(&work.nwait, -1)
+	if decnwait == work.nproc {
+		println("runtime: work.nwait =", decnwait, "work.nproc=", work.nproc)
+		throw("nwait > work.nprocs")
+	}
+
+	// gcDrainN requires the caller to be preemptible.
+	casgstatus(gp, _Grunning, _Gwaiting)
+	gp.waitreason = "GC assist marking"
+
+	// drain own cached work first in the hopes that it
+	// will be more cache friendly.
+	gcw := &getg().m.p.ptr().gcw
+	workDone := gcDrainN(gcw, scanWork)
+	// If we are near the end of the mark phase
+	// dispose of the gcw.
+	if gcBlackenPromptly {
+		gcw.dispose()
+	}
+
+	casgstatus(gp, _Gwaiting, _Grunning)
+
+	// Record that we did this much scan work.
+	//
+	// Back out the number of bytes of assist credit that
+	// this scan work counts for. The "1+" is a poor man's
+	// round-up, to ensure this adds credit even if
+	// assistBytesPerWork is very low.
+	gp.gcAssistBytes += 1 + int64(gcController.assistBytesPerWork*float64(workDone))
+
+	// If this is the last worker and we ran out of work,
+	// signal a completion point.
+	incnwait := atomic.Xadd(&work.nwait, +1)
+	if incnwait > work.nproc {
+		println("runtime: work.nwait=", incnwait,
+			"work.nproc=", work.nproc,
+			"gcBlackenPromptly=", gcBlackenPromptly)
+		throw("work.nwait > work.nproc")
+	}
+
+	if incnwait == work.nproc && !gcMarkWorkAvailable(nil) {
+		// This has reached a background completion point. Set
+		// gp.param to a non-nil value to indicate this. It
+		// doesn't matter what we set it to (it just has to be
+		// a valid pointer).
+		gp.param = unsafe.Pointer(gp)
+	}
+	duration := nanotime() - startTime
+	_p_ := gp.m.p.ptr()
+	_p_.gcAssistTime += duration
+	if _p_.gcAssistTime > gcAssistTimeSlack {
+		atomic.Xaddint64(&gcController.assistTime, _p_.gcAssistTime)
+		_p_.gcAssistTime = 0
+	}
+}
+
 // gcWakeAllAssists wakes all currently blocked assists. This is used
 // at the end of a GC cycle. gcBlackenEnabled must be false to prevent
 // new assists from going to sleep after this point.
@@ -575,6 +596,50 @@ func gcWakeAllAssists() {
 	unlock(&work.assistQueue.lock)
 }
 
+// gcParkAssist puts the current goroutine on the assist queue and parks.
+//
+// gcParkAssist returns whether the assist is now satisfied. If it
+// returns false, the caller must retry the assist.
+//
+//go:nowritebarrier
+func gcParkAssist() bool {
+	lock(&work.assistQueue.lock)
+	// If the GC cycle finished while we were getting the lock,
+	// exit the assist. The cycle can't finish while we hold the
+	// lock.
+	if atomic.Load(&gcBlackenEnabled) == 0 {
+		unlock(&work.assistQueue.lock)
+		return true
+	}
+
+	gp := getg()
+	oldHead, oldTail := work.assistQueue.head, work.assistQueue.tail
+	if oldHead == 0 {
+		work.assistQueue.head.set(gp)
+	} else {
+		oldTail.ptr().schedlink.set(gp)
+	}
+	work.assistQueue.tail.set(gp)
+	gp.schedlink.set(nil)
+
+	// Recheck for background credit now that this G is in
+	// the queue, but can still back out. This avoids a
+	// race in case background marking has flushed more
+	// credit since we checked above.
+	if atomic.Loadint64(&gcController.bgScanCredit) > 0 {
+		work.assistQueue.head = oldHead
+		work.assistQueue.tail = oldTail
+		if oldTail != 0 {
+			oldTail.ptr().schedlink.set(nil)
+		}
+		unlock(&work.assistQueue.lock)
+		return false
+	}
+	// Park.
+	goparkunlock(&work.assistQueue.lock, "GC assist wait", traceEvGoBlockGC, 2)
+	return true
+}
+
 // gcFlushBgCredit flushes scanWork units of background scan work
 // credit. This first satisfies blocked assists on the
 // work.assistQueue and then flushes any remaining credit to
@@ -865,6 +930,15 @@ func scanframeworker(frame *stkframe, cache *pcvalueCache, gcw *gcWork) {
 // gp.gcscanvalid. The caller must own gp and ensure that gp isn't
 // already on the rescan list.
 func queueRescan(gp *g) {
+	if debug.gcrescanstacks == 0 {
+		// Clear gcscanvalid to keep assertions happy.
+		//
+		// TODO: Remove gcscanvalid entirely when we remove
+		// stack rescanning.
+		gp.gcscanvalid = false
+		return
+	}
+
 	if gcphase == _GCoff {
 		gp.gcscanvalid = false
 		return
@@ -894,6 +968,10 @@ func queueRescan(gp *g) {
 // dequeueRescan removes gp from the stack rescan list, if gp is on
 // the rescan list. The caller must own gp.
 func dequeueRescan(gp *g) {
+	if debug.gcrescanstacks == 0 {
+		return
+	}
+
 	if gp.gcRescan == -1 {
 		return
 	}
@@ -921,6 +999,7 @@ const (
 	gcDrainUntilPreempt gcDrainFlags = 1 << iota
 	gcDrainNoBlock
 	gcDrainFlushBgCredit
+	gcDrainIdle
 
 	// gcDrainBlock means neither gcDrainUntilPreempt or
 	// gcDrainNoBlock. It is the default, but callers should use
@@ -934,6 +1013,9 @@ const (
 // If flags&gcDrainUntilPreempt != 0, gcDrain returns when g.preempt
 // is set. This implies gcDrainNoBlock.
 //
+// If flags&gcDrainIdle != 0, gcDrain returns when there is other work
+// to do. This implies gcDrainNoBlock.
+//
 // If flags&gcDrainNoBlock != 0, gcDrain returns as soon as it is
 // unable to get more work. Otherwise, it will block until all
 // blocking calls are blocked in gcDrain.
@@ -948,24 +1030,31 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) {
 		throw("gcDrain phase incorrect")
 	}
 
-	gp := getg()
+	gp := getg().m.curg
 	preemptible := flags&gcDrainUntilPreempt != 0
-	blocking := flags&(gcDrainUntilPreempt|gcDrainNoBlock) == 0
+	blocking := flags&(gcDrainUntilPreempt|gcDrainIdle|gcDrainNoBlock) == 0
 	flushBgCredit := flags&gcDrainFlushBgCredit != 0
+	idle := flags&gcDrainIdle != 0
+
+	initScanWork := gcw.scanWork
+	// idleCheck is the scan work at which to perform the next
+	// idle check with the scheduler.
+	idleCheck := initScanWork + idleCheckThreshold
 
 	// Drain root marking jobs.
 	if work.markrootNext < work.markrootJobs {
-		for blocking || !gp.preempt {
+		for !(preemptible && gp.preempt) {
 			job := atomic.Xadd(&work.markrootNext, +1) - 1
 			if job >= work.markrootJobs {
 				break
 			}
 			markroot(gcw, job)
+			if idle && pollWork() {
+				goto done
+			}
 		}
 	}
 
-	initScanWork := gcw.scanWork
-
 	// Drain heap marking jobs.
 	for !(preemptible && gp.preempt) {
 		// Try to keep work available on the global queue. We used to
@@ -1001,7 +1090,15 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) {
 				gcFlushBgCredit(gcw.scanWork - initScanWork)
 				initScanWork = 0
 			}
+			idleCheck -= gcw.scanWork
 			gcw.scanWork = 0
+
+			if idle && idleCheck <= 0 {
+				idleCheck += idleCheckThreshold
+				if pollWork() {
+					break
+				}
+			}
 		}
 	}
 
@@ -1009,6 +1106,7 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) {
 	// point because we must preserve the condition that the work
 	// buffers are empty.
 
+done:
 	// Flush remaining scan work credit.
 	if gcw.scanWork > 0 {
 		atomic.Xaddint64(&gcController.scanWork, gcw.scanWork)
@@ -1025,7 +1123,13 @@ func gcDrain(gcw *gcWork, flags gcDrainFlags) {
 // buffer. Otherwise, it will perform at least n units of work, but
 // may perform more because scanning is always done in whole object
 // increments. It returns the amount of scan work performed.
+//
+// The caller goroutine must be in a preemptible state (e.g.,
+// _Gwaiting) to prevent deadlocks during stack scanning. As a
+// consequence, this must be called on the system stack.
+//
 //go:nowritebarrier
+//go:systemstack
 func gcDrainN(gcw *gcWork, scanWork int64) int64 {
 	if !writeBarrier.needed {
 		throw("gcDrainN phase incorrect")
@@ -1053,6 +1157,18 @@ func gcDrainN(gcw *gcWork, scanWork int64) int64 {
 		}
 
 		if b == 0 {
+			// Try to do a root job.
+			//
+			// TODO: Assists should get credit for this
+			// work.
+			if work.markrootNext < work.markrootJobs {
+				job := atomic.Xadd(&work.markrootNext, +1) - 1
+				if job < work.markrootJobs {
+					markroot(gcw, job)
+					continue
+				}
+			}
+			// No heap or root jobs.
 			break
 		}
 		scanobject(b, gcw)
@@ -1113,9 +1229,10 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) {
 }
 
 // scanobject scans the object starting at b, adding pointers to gcw.
-// b must point to the beginning of a heap object; scanobject consults
-// the GC bitmap for the pointer mask and the spans for the size of the
-// object.
+// b must point to the beginning of a heap object or an oblet.
+// scanobject consults the GC bitmap for the pointer mask and the
+// spans for the size of the object.
+//
 //go:nowritebarrier
 func scanobject(b uintptr, gcw *gcWork) {
 	// Note that arena_used may change concurrently during
@@ -1130,9 +1247,11 @@ func scanobject(b uintptr, gcw *gcWork) {
 	arena_start := mheap_.arena_start
 	arena_used := mheap_.arena_used
 
-	// Find bits of the beginning of the object.
-	// b must point to the beginning of a heap object, so
-	// we can get its bits and span directly.
+	// Find the bits for b and the size of the object at b.
+	//
+	// b is either the beginning of an object, in which case this
+	// is the size of the object to scan, or it points to an
+	// oblet, in which case we compute the size to scan below.
 	hbits := heapBitsForAddr(b)
 	s := spanOfUnchecked(b)
 	n := s.elemsize
@@ -1140,6 +1259,42 @@ func scanobject(b uintptr, gcw *gcWork) {
 		throw("scanobject n == 0")
 	}
 
+	if n > maxObletBytes {
+		// Large object. Break into oblets for better
+		// parallelism and lower latency.
+		if b == s.base() {
+			// It's possible this is a noscan object (not
+			// from greyobject, but from other code
+			// paths), in which case we must *not* enqueue
+			// oblets since their bitmaps will be
+			// uninitialized.
+			if !hbits.hasPointers(n) {
+				// Bypass the whole scan.
+				gcw.bytesMarked += uint64(n)
+				return
+			}
+
+			// Enqueue the other oblets to scan later.
+			// Some oblets may be in b's scalar tail, but
+			// these will be marked as "no more pointers",
+			// so we'll drop out immediately when we go to
+			// scan those.
+			for oblet := b + maxObletBytes; oblet < s.base()+s.elemsize; oblet += maxObletBytes {
+				if !gcw.putFast(oblet) {
+					gcw.put(oblet)
+				}
+			}
+		}
+
+		// Compute the size of the oblet. Since this object
+		// must be a large object, s.base() is the beginning
+		// of the object.
+		n = s.base() + s.elemsize - b
+		if n > maxObletBytes {
+			n = maxObletBytes
+		}
+	}
+
 	var i uintptr
 	for i = 0; i < n; i += sys.PtrSize {
 		// Find bits for this word.
@@ -1147,14 +1302,16 @@ func scanobject(b uintptr, gcw *gcWork) {
 			// Avoid needless hbits.next() on last iteration.
 			hbits = hbits.next()
 		}
+		// Load bits once. See CL 22712 and issue 16973 for discussion.
+		bits := hbits.bits()
 		// During checkmarking, 1-word objects store the checkmark
 		// in the type bit for the one word. The only one-word objects
 		// are pointers, or else they'd be merged with other non-pointer
 		// data into larger allocations.
-		if i != 1*sys.PtrSize && !hbits.morePointers() {
+		if i != 1*sys.PtrSize && bits&bitScan == 0 {
 			break // no more pointers in this object
 		}
-		if !hbits.isPointer() {
+		if bits&bitPointer == 0 {
 			continue // not a pointer
 		}
 
@@ -1224,6 +1381,13 @@ func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork
 			throw("setCheckmarked and isCheckmarked disagree")
 		}
 	} else {
+		if debug.gccheckmark > 0 && span.isFree(objIndex) {
+			print("runtime: marking free object ", hex(obj), " found at *(", hex(base), "+", hex(off), ")\n")
+			gcDumpObject("base", base, off)
+			gcDumpObject("obj", obj, ^uintptr(0))
+			throw("marking free object")
+		}
+
 		// If marked we have nothing to do.
 		if mbits.isMarked() {
 			return
@@ -1259,15 +1423,28 @@ func gcDumpObject(label string, obj, off uintptr) {
 	k := obj >> _PageShift
 	x := k
 	x -= mheap_.arena_start >> _PageShift
-	s := h_spans[x]
+	s := mheap_.spans[x]
 	print(label, "=", hex(obj), " k=", hex(k))
 	if s == nil {
 		print(" s=nil\n")
 		return
 	}
-	print(" s.base()=", hex(s.base()), " s.limit=", hex(s.limit), " s.sizeclass=", s.sizeclass, " s.elemsize=", s.elemsize, "\n")
+	print(" s.base()=", hex(s.base()), " s.limit=", hex(s.limit), " s.sizeclass=", s.sizeclass, " s.elemsize=", s.elemsize, " s.state=")
+	if 0 <= s.state && int(s.state) < len(mSpanStateNames) {
+		print(mSpanStateNames[s.state], "\n")
+	} else {
+		print("unknown(", s.state, ")\n")
+	}
+
 	skipped := false
-	for i := uintptr(0); i < s.elemsize; i += sys.PtrSize {
+	size := s.elemsize
+	if s.state == _MSpanStack && size == 0 {
+		// We're printing something from a stack frame. We
+		// don't know how big it is, so just show up to an
+		// including off.
+		size = off + sys.PtrSize
+	}
+	for i := uintptr(0); i < size; i += sys.PtrSize {
 		// For big objects, just print the beginning (because
 		// that usually hints at the object's type) and the
 		// fields around off.
@@ -1305,6 +1482,32 @@ func gcmarknewobject(obj, size, scanSize uintptr) {
 	gcw := &getg().m.p.ptr().gcw
 	gcw.bytesMarked += uint64(size)
 	gcw.scanWork += int64(scanSize)
+	if gcBlackenPromptly {
+		// There shouldn't be anything in the work queue, but
+		// we still need to flush stats.
+		gcw.dispose()
+	}
+}
+
+// gcMarkTinyAllocs greys all active tiny alloc blocks.
+//
+// The world must be stopped.
+func gcMarkTinyAllocs() {
+	for _, p := range &allp {
+		if p == nil || p.status == _Pdead {
+			break
+		}
+		c := p.mcache
+		if c == nil || c.tiny == 0 {
+			continue
+		}
+		_, hbits, span, objIndex := heapBitsForObject(c.tiny, 0, 0)
+		gcw := &p.gcw
+		greyobject(c.tiny, 0, 0, hbits, span, gcw, objIndex)
+		if gcBlackenPromptly {
+			gcw.dispose()
+		}
+	}
 }
 
 // Checkmarking
@@ -1333,7 +1536,7 @@ var useCheckmark = false
 //go:nowritebarrier
 func initCheckmarks() {
 	useCheckmark = true
-	for _, s := range work.spans {
+	for _, s := range mheap_.allspans {
 		if s.state == _MSpanInUse {
 			heapBitsForSpan(s.base()).initCheckmarkSpan(s.layout())
 		}
@@ -1342,7 +1545,7 @@ func initCheckmarks() {
 
 func clearCheckmarks() {
 	useCheckmark = false
-	for _, s := range work.spans {
+	for _, s := range mheap_.allspans {
 		if s.state == _MSpanInUse {
 			heapBitsForSpan(s.base()).clearCheckmarkSpan(s.layout())
 		}
diff --git a/src/runtime/mgcsweep.go b/src/runtime/mgcsweep.go
index 947c38e..e74a451 100644
--- a/src/runtime/mgcsweep.go
+++ b/src/runtime/mgcsweep.go
@@ -20,14 +20,21 @@ type sweepdata struct {
 	parked  bool
 	started bool
 
-	spanidx uint32 // background sweeper position
-
 	nbgsweep    uint32
 	npausesweep uint32
+
+	// pacertracegen is the sweepgen at which the last pacer trace
+	// "sweep finished" message was printed.
+	pacertracegen uint32
 }
 
+// finishsweep_m ensures that all spans are swept.
+//
+// The world must be stopped. This ensures there are no sweeps in
+// progress.
+//
 //go:nowritebarrier
-func finishsweep_m(stw bool) {
+func finishsweep_m() {
 	// Sweeping must be complete before marking commences, so
 	// sweep any unswept spans. If this is a concurrent GC, there
 	// shouldn't be any spans left to sweep, so this should finish
@@ -37,20 +44,6 @@ func finishsweep_m(stw bool) {
 		sweep.npausesweep++
 	}
 
-	// There may be some other spans being swept concurrently that
-	// we need to wait for. If finishsweep_m is done with the world stopped
-	// this is not required because the STW must have waited for sweeps.
-	//
-	// TODO(austin): As of this writing, we always pass true for stw.
-	// Consider removing this code.
-	if !stw {
-		sg := mheap_.sweepgen
-		for _, s := range work.spans {
-			if s.sweepgen != sg && s.state == _MSpanInUse {
-				s.ensureSwept()
-			}
-		}
-	}
 	nextMarkBitArenaEpoch()
 }
 
@@ -91,18 +84,23 @@ func sweepone() uintptr {
 	_g_.m.locks++
 	sg := mheap_.sweepgen
 	for {
-		idx := atomic.Xadd(&sweep.spanidx, 1) - 1
-		if idx >= uint32(len(work.spans)) {
+		s := mheap_.sweepSpans[1-sg/2%2].pop()
+		if s == nil {
 			mheap_.sweepdone = 1
 			_g_.m.locks--
-			if debug.gcpacertrace > 0 && idx == uint32(len(work.spans)) {
+			if debug.gcpacertrace > 0 && atomic.Cas(&sweep.pacertracegen, sg-2, sg) {
 				print("pacer: sweep done at heap size ", memstats.heap_live>>20, "MB; allocated ", mheap_.spanBytesAlloc>>20, "MB of spans; swept ", mheap_.pagesSwept, " pages at ", mheap_.sweepPagesPerByte, " pages/byte\n")
 			}
 			return ^uintptr(0)
 		}
-		s := work.spans[idx]
 		if s.state != mSpanInUse {
-			s.sweepgen = sg
+			// This can happen if direct sweeping already
+			// swept this span, but in that case the sweep
+			// generation should always be up-to-date.
+			if s.sweepgen != sg {
+				print("runtime: bad span s.state=", s.state, " s.sweepgen=", s.sweepgen, " sweepgen=", sg, "\n")
+				throw("non in-use span in unswept list")
+			}
 			continue
 		}
 		if s.sweepgen != sg-2 || !atomic.Cas(&s.sweepgen, sg-2, sg-1) {
@@ -110,6 +108,9 @@ func sweepone() uintptr {
 		}
 		npages := s.npages
 		if !s.sweep(false) {
+			// Span is still in-use, so this returned no
+			// pages to the heap and the span needs to
+			// move to the swept in-use list.
 			npages = 0
 		}
 		_g_.m.locks--
@@ -348,6 +349,11 @@ func (s *mspan) sweep(preserve bool) bool {
 		c.local_largefree += size
 		res = true
 	}
+	if !res {
+		// The span has been swept and is still in-use, so put
+		// it on the swept in-use list.
+		mheap_.sweepSpans[sweepgen/2%2].push(s)
+	}
 	if trace.enabled {
 		traceGCSweepDone()
 	}
diff --git a/src/runtime/mgcsweepbuf.go b/src/runtime/mgcsweepbuf.go
new file mode 100644
index 0000000..6c1118e
--- /dev/null
+++ b/src/runtime/mgcsweepbuf.go
@@ -0,0 +1,178 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+import (
+	"runtime/internal/atomic"
+	"runtime/internal/sys"
+	"unsafe"
+)
+
+// A gcSweepBuf is a set of *mspans.
+//
+// gcSweepBuf is safe for concurrent push operations *or* concurrent
+// pop operations, but not both simultaneously.
+type gcSweepBuf struct {
+	// A gcSweepBuf is a two-level data structure consisting of a
+	// growable spine that points to fixed-sized blocks. The spine
+	// can be accessed without locks, but adding a block or
+	// growing it requires taking the spine lock.
+	//
+	// Because each mspan covers at least 8K of heap and takes at
+	// most 8 bytes in the gcSweepBuf, the growth of the spine is
+	// quite limited.
+	//
+	// The spine and all blocks are allocated off-heap, which
+	// allows this to be used in the memory manager and avoids the
+	// need for write barriers on all of these. We never release
+	// this memory because there could be concurrent lock-free
+	// access and we're likely to reuse it anyway. (In principle,
+	// we could do this during STW.)
+
+	spineLock mutex
+	spine     unsafe.Pointer // *[N]*gcSweepBlock, accessed atomically
+	spineLen  uintptr        // Spine array length, accessed atomically
+	spineCap  uintptr        // Spine array cap, accessed under lock
+
+	// index is the first unused slot in the logical concatenation
+	// of all blocks. It is accessed atomically.
+	index uint32
+}
+
+const (
+	gcSweepBlockEntries    = 512 // 4KB on 64-bit
+	gcSweepBufInitSpineCap = 256 // Enough for 1GB heap on 64-bit
+)
+
+type gcSweepBlock struct {
+	spans [gcSweepBlockEntries]*mspan
+}
+
+// push adds span s to buffer b. push is safe to call concurrently
+// with other push operations, but NOT to call concurrently with pop.
+func (b *gcSweepBuf) push(s *mspan) {
+	// Obtain our slot.
+	cursor := uintptr(atomic.Xadd(&b.index, +1) - 1)
+	top, bottom := cursor/gcSweepBlockEntries, cursor%gcSweepBlockEntries
+
+	// Do we need to add a block?
+	spineLen := atomic.Loaduintptr(&b.spineLen)
+	var block *gcSweepBlock
+retry:
+	if top < spineLen {
+		spine := atomic.Loadp(unsafe.Pointer(&b.spine))
+		blockp := add(spine, sys.PtrSize*top)
+		block = (*gcSweepBlock)(atomic.Loadp(blockp))
+	} else {
+		// Add a new block to the spine, potentially growing
+		// the spine.
+		lock(&b.spineLock)
+		// spineLen cannot change until we release the lock,
+		// but may have changed while we were waiting.
+		spineLen = atomic.Loaduintptr(&b.spineLen)
+		if top < spineLen {
+			unlock(&b.spineLock)
+			goto retry
+		}
+
+		if spineLen == b.spineCap {
+			// Grow the spine.
+			newCap := b.spineCap * 2
+			if newCap == 0 {
+				newCap = gcSweepBufInitSpineCap
+			}
+			newSpine := persistentalloc(newCap*sys.PtrSize, sys.CacheLineSize, &memstats.gc_sys)
+			if b.spineCap != 0 {
+				// Blocks are allocated off-heap, so
+				// no write barriers.
+				memmove(newSpine, b.spine, b.spineCap*sys.PtrSize)
+			}
+			// Spine is allocated off-heap, so no write barrier.
+			atomic.StorepNoWB(unsafe.Pointer(&b.spine), newSpine)
+			b.spineCap = newCap
+			// We can't immediately free the old spine
+			// since a concurrent push with a lower index
+			// could still be reading from it. We let it
+			// leak because even a 1TB heap would waste
+			// less than 2MB of memory on old spines. If
+			// this is a problem, we could free old spines
+			// during STW.
+		}
+
+		// Allocate a new block and add it to the spine.
+		block = (*gcSweepBlock)(persistentalloc(unsafe.Sizeof(gcSweepBlock{}), sys.CacheLineSize, &memstats.gc_sys))
+		blockp := add(b.spine, sys.PtrSize*top)
+		// Blocks are allocated off-heap, so no write barrier.
+		atomic.StorepNoWB(blockp, unsafe.Pointer(block))
+		atomic.Storeuintptr(&b.spineLen, spineLen+1)
+		unlock(&b.spineLock)
+	}
+
+	// We have a block. Insert the span.
+	block.spans[bottom] = s
+}
+
+// pop removes and returns a span from buffer b, or nil if b is empty.
+// pop is safe to call concurrently with other pop operations, but NOT
+// to call concurrently with push.
+func (b *gcSweepBuf) pop() *mspan {
+	cursor := atomic.Xadd(&b.index, -1)
+	if int32(cursor) < 0 {
+		atomic.Xadd(&b.index, +1)
+		return nil
+	}
+
+	// There are no concurrent spine or block modifications during
+	// pop, so we can omit the atomics.
+	top, bottom := cursor/gcSweepBlockEntries, cursor%gcSweepBlockEntries
+	blockp := (**gcSweepBlock)(add(b.spine, sys.PtrSize*uintptr(top)))
+	block := *blockp
+	s := block.spans[bottom]
+	// Clear the pointer for block(i).
+	block.spans[bottom] = nil
+	return s
+}
+
+// numBlocks returns the number of blocks in buffer b. numBlocks is
+// safe to call concurrently with any other operation. Spans that have
+// been pushed prior to the call to numBlocks are guaranteed to appear
+// in some block in the range [0, numBlocks()), assuming there are no
+// intervening pops. Spans that are pushed after the call may also
+// appear in these blocks.
+func (b *gcSweepBuf) numBlocks() int {
+	return int((atomic.Load(&b.index) + gcSweepBlockEntries - 1) / gcSweepBlockEntries)
+}
+
+// block returns the spans in the i'th block of buffer b. block is
+// safe to call concurrently with push.
+func (b *gcSweepBuf) block(i int) []*mspan {
+	// Perform bounds check before loading spine address since
+	// push ensures the allocated length is at least spineLen.
+	if i < 0 || uintptr(i) >= atomic.Loaduintptr(&b.spineLen) {
+		throw("block index out of range")
+	}
+
+	// Get block i.
+	spine := atomic.Loadp(unsafe.Pointer(&b.spine))
+	blockp := add(spine, sys.PtrSize*uintptr(i))
+	block := (*gcSweepBlock)(atomic.Loadp(blockp))
+
+	// Slice the block if necessary.
+	cursor := uintptr(atomic.Load(&b.index))
+	top, bottom := cursor/gcSweepBlockEntries, cursor%gcSweepBlockEntries
+	var spans []*mspan
+	if uintptr(i) < top {
+		spans = block.spans[:]
+	} else {
+		spans = block.spans[:bottom]
+	}
+
+	// push may have reserved a slot but not filled it yet, so
+	// trim away unused entries.
+	for len(spans) > 0 && spans[len(spans)-1] == nil {
+		spans = spans[:len(spans)-1]
+	}
+	return spans
+}
diff --git a/src/runtime/mgcwork.go b/src/runtime/mgcwork.go
index d04840b..5eb05a7 100644
--- a/src/runtime/mgcwork.go
+++ b/src/runtime/mgcwork.go
@@ -28,6 +28,8 @@ const (
 // A wbufptr holds a workbuf*, but protects it from write barriers.
 // workbufs never live on the heap, so write barriers are unnecessary.
 // Write barriers on workbuf pointers may also be dangerous in the GC.
+//
+// TODO: Since workbuf is now go:notinheap, this isn't necessary.
 type wbufptr uintptr
 
 func wbufptrOf(w *workbuf) wbufptr {
@@ -94,9 +96,10 @@ func (w *gcWork) init() {
 }
 
 // put enqueues a pointer for the garbage collector to trace.
-// obj must point to the beginning of a heap object.
+// obj must point to the beginning of a heap object or an oblet.
 //go:nowritebarrier
 func (w *gcWork) put(obj uintptr) {
+	flushed := false
 	wbuf := w.wbuf1.ptr()
 	if wbuf == nil {
 		w.init()
@@ -109,11 +112,20 @@ func (w *gcWork) put(obj uintptr) {
 			putfull(wbuf)
 			wbuf = getempty()
 			w.wbuf1 = wbufptrOf(wbuf)
+			flushed = true
 		}
 	}
 
 	wbuf.obj[wbuf.nobj] = obj
 	wbuf.nobj++
+
+	// If we put a buffer on full, let the GC controller know so
+	// it can encourage more workers to run. We delay this until
+	// the end of put so that w is in a consistent state, since
+	// enlistWorker may itself manipulate w.
+	if flushed && gcphase == _GCmark {
+		gcController.enlistWorker()
+	}
 }
 
 // putFast does a put and returns true if it can be done quickly
@@ -261,6 +273,12 @@ func (w *gcWork) balance() {
 		w.wbuf2 = wbufptrOf(getempty())
 	} else if wbuf := w.wbuf1.ptr(); wbuf.nobj > 4 {
 		w.wbuf1 = wbufptrOf(handoff(wbuf))
+	} else {
+		return
+	}
+	// We flushed a buffer to the full list, so wake a worker.
+	if gcphase == _GCmark {
+		gcController.enlistWorker()
 	}
 }
 
@@ -279,6 +297,7 @@ type workbufhdr struct {
 	nobj int
 }
 
+//go:notinheap
 type workbuf struct {
 	workbufhdr
 	// account for the above fields
@@ -334,12 +353,6 @@ func putempty(b *workbuf) {
 func putfull(b *workbuf) {
 	b.checknonempty()
 	lfstackpush(&work.full, &b.node)
-
-	// We just made more work available. Let the GC controller
-	// know so it can encourage more workers to run.
-	if gcphase == _GCmark {
-		gcController.enlistWorker()
-	}
 }
 
 // trygetfull tries to get a full or partially empty workbuffer.
diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go
index 8db2fcc..ef62eff 100644
--- a/src/runtime/mheap.go
+++ b/src/runtime/mheap.go
@@ -22,20 +22,56 @@ const minPhysPageSize = 4096
 // Main malloc heap.
 // The heap itself is the "free[]" and "large" arrays,
 // but all the other global data is here too.
+//
+// mheap must not be heap-allocated because it contains mSpanLists,
+// which must not be heap-allocated.
+//
+//go:notinheap
 type mheap struct {
 	lock      mutex
 	free      [_MaxMHeapList]mSpanList // free lists of given length
 	freelarge mSpanList                // free lists length >= _MaxMHeapList
 	busy      [_MaxMHeapList]mSpanList // busy lists of large objects of given length
 	busylarge mSpanList                // busy lists of large objects length >= _MaxMHeapList
-	allspans  **mspan                  // all spans out there
-	gcspans   **mspan                  // copy of allspans referenced by gc marker or sweeper
-	nspan     uint32
-	sweepgen  uint32 // sweep generation, see comment in mspan
-	sweepdone uint32 // all spans are swept
-	// span lookup
-	spans        **mspan
-	spans_mapped uintptr
+	sweepgen  uint32                   // sweep generation, see comment in mspan
+	sweepdone uint32                   // all spans are swept
+
+	// allspans is a slice of all mspans ever created. Each mspan
+	// appears exactly once.
+	//
+	// The memory for allspans is manually managed and can be
+	// reallocated and move as the heap grows.
+	//
+	// In general, allspans is protected by mheap_.lock, which
+	// prevents concurrent access as well as freeing the backing
+	// store. Accesses during STW might not hold the lock, but
+	// must ensure that allocation cannot happen around the
+	// access (since that may free the backing store).
+	allspans []*mspan // all spans out there
+
+	// spans is a lookup table to map virtual address page IDs to *mspan.
+	// For allocated spans, their pages map to the span itself.
+	// For free spans, only the lowest and highest pages map to the span itself.
+	// Internal pages map to an arbitrary span.
+	// For pages that have never been allocated, spans entries are nil.
+	//
+	// This is backed by a reserved region of the address space so
+	// it can grow without moving. The memory up to len(spans) is
+	// mapped. cap(spans) indicates the total reserved memory.
+	spans []*mspan
+
+	// sweepSpans contains two mspan stacks: one of swept in-use
+	// spans, and one of unswept in-use spans. These two trade
+	// roles on each GC cycle. Since the sweepgen increases by 2
+	// on each cycle, this means the swept spans are in
+	// sweepSpans[sweepgen/2%2] and the unswept spans are in
+	// sweepSpans[1-sweepgen/2%2]. Sweeping pops spans from the
+	// unswept stack and pushes spans that are still in-use on the
+	// swept stack. Likewise, allocating an in-use span pushes it
+	// on the swept stack.
+	sweepSpans [2]gcSweepBuf
+
+	_ uint32 // align uint64 fields on 32-bit for atomics
 
 	// Proportional sweep
 	pagesInUse        uint64  // pages of spans in stats _MSpanInUse; R/W with mheap.lock
@@ -102,24 +138,36 @@ var mheap_ mheap
 // * During GC (gcphase != _GCoff), a span *must not* transition from
 //   stack or in-use to free. Because concurrent GC may read a pointer
 //   and then look up its span, the span state must be monotonic.
+type mSpanState uint8
+
 const (
-	_MSpanInUse = iota // allocated for garbage collected heap
-	_MSpanStack        // allocated for use by stack allocator
+	_MSpanDead  mSpanState = iota
+	_MSpanInUse            // allocated for garbage collected heap
+	_MSpanStack            // allocated for use by stack allocator
 	_MSpanFree
-	_MSpanDead
 )
 
+// mSpanStateNames are the names of the span states, indexed by
+// mSpanState.
+var mSpanStateNames = []string{
+	"_MSpanDead",
+	"_MSpanInUse",
+	"_MSpanStack",
+	"_MSpanFree",
+}
+
 // mSpanList heads a linked list of spans.
 //
-// Linked list structure is based on BSD's "tail queue" data structure.
+//go:notinheap
 type mSpanList struct {
-	first *mspan  // first span in list, or nil if none
-	last  **mspan // last span's next field, or first if none
+	first *mspan // first span in list, or nil if none
+	last  *mspan // last span in list, or nil if none
 }
 
+//go:notinheap
 type mspan struct {
 	next *mspan     // next span in list, or nil if none
-	prev **mspan    // previous span's next field, or list head's first field if none
+	prev *mspan     // previous span in list, or nil if none
 	list *mSpanList // For debugging. TODO: Remove.
 
 	startAddr     uintptr   // address of first byte of span aka s.base()
@@ -186,21 +234,21 @@ type mspan struct {
 	// h->sweepgen is incremented by 2 after every GC
 
 	sweepgen    uint32
-	divMul      uint32   // for divide by elemsize - divMagic.mul
-	allocCount  uint16   // capacity - number of objects in freelist
-	sizeclass   uint8    // size class
-	incache     bool     // being used by an mcache
-	state       uint8    // mspaninuse etc
-	needzero    uint8    // needs to be zeroed before allocation
-	divShift    uint8    // for divide by elemsize - divMagic.shift
-	divShift2   uint8    // for divide by elemsize - divMagic.shift2
-	elemsize    uintptr  // computed from sizeclass or from npages
-	unusedsince int64    // first time spotted by gc in mspanfree state
-	npreleased  uintptr  // number of pages released to the os
-	limit       uintptr  // end of data in span
-	speciallock mutex    // guards specials list
-	specials    *special // linked list of special records sorted by offset.
-	baseMask    uintptr  // if non-0, elemsize is a power of 2, & this will get object allocation base
+	divMul      uint16     // for divide by elemsize - divMagic.mul
+	baseMask    uint16     // if non-0, elemsize is a power of 2, & this will get object allocation base
+	allocCount  uint16     // capacity - number of objects in freelist
+	sizeclass   uint8      // size class
+	incache     bool       // being used by an mcache
+	state       mSpanState // mspaninuse etc
+	needzero    uint8      // needs to be zeroed before allocation
+	divShift    uint8      // for divide by elemsize - divMagic.shift
+	divShift2   uint8      // for divide by elemsize - divMagic.shift2
+	elemsize    uintptr    // computed from sizeclass or from npages
+	unusedsince int64      // first time spotted by gc in mspanfree state
+	npreleased  uintptr    // number of pages released to the os
+	limit       uintptr    // end of data in span
+	speciallock mutex      // guards specials list
+	specials    *special   // linked list of special records sorted by offset.
 }
 
 func (s *mspan) base() uintptr {
@@ -216,22 +264,13 @@ func (s *mspan) layout() (size, n, total uintptr) {
 	return
 }
 
-var h_allspans []*mspan // TODO: make this h.allspans once mheap can be defined in Go
-
-// h_spans is a lookup table to map virtual address page IDs to *mspan.
-// For allocated spans, their pages map to the span itself.
-// For free spans, only the lowest and highest pages map to the span itself. Internal
-// pages map to an arbitrary span.
-// For pages that have never been allocated, h_spans entries are nil.
-var h_spans []*mspan // TODO: make this h.spans once mheap can be defined in Go
-
 func recordspan(vh unsafe.Pointer, p unsafe.Pointer) {
 	h := (*mheap)(vh)
 	s := (*mspan)(p)
-	if len(h_allspans) >= cap(h_allspans) {
+	if len(h.allspans) >= cap(h.allspans) {
 		n := 64 * 1024 / sys.PtrSize
-		if n < cap(h_allspans)*3/2 {
-			n = cap(h_allspans) * 3 / 2
+		if n < cap(h.allspans)*3/2 {
+			n = cap(h.allspans) * 3 / 2
 		}
 		var new []*mspan
 		sp := (*slice)(unsafe.Pointer(&new))
@@ -239,21 +278,18 @@ func recordspan(vh unsafe.Pointer, p unsafe.Pointer) {
 		if sp.array == nil {
 			throw("runtime: cannot allocate memory")
 		}
-		sp.len = len(h_allspans)
+		sp.len = len(h.allspans)
 		sp.cap = n
-		if len(h_allspans) > 0 {
-			copy(new, h_allspans)
-			// Don't free the old array if it's referenced by sweep.
-			// See the comment in mgc.go.
-			if h.allspans != mheap_.gcspans {
-				sysFree(unsafe.Pointer(h.allspans), uintptr(cap(h_allspans))*sys.PtrSize, &memstats.other_sys)
-			}
+		if len(h.allspans) > 0 {
+			copy(new, h.allspans)
+		}
+		oldAllspans := h.allspans
+		h.allspans = new
+		if len(oldAllspans) != 0 {
+			sysFree(unsafe.Pointer(&oldAllspans[0]), uintptr(cap(oldAllspans))*unsafe.Sizeof(oldAllspans[0]), &memstats.other_sys)
 		}
-		h_allspans = new
-		h.allspans = (**mspan)(sp.array)
 	}
-	h_allspans = append(h_allspans, s)
-	h.nspan = uint32(len(h_allspans))
+	h.allspans = append(h.allspans, s)
 }
 
 // inheap reports whether b is a pointer into a (potentially dead) heap object.
@@ -266,7 +302,7 @@ func inheap(b uintptr) bool {
 		return false
 	}
 	// Not a beginning of a block, consult span table to find the block beginning.
-	s := h_spans[(b-mheap_.arena_start)>>_PageShift]
+	s := mheap_.spans[(b-mheap_.arena_start)>>_PageShift]
 	if s == nil || b < s.base() || b >= s.limit || s.state != mSpanInUse {
 		return false
 	}
@@ -281,7 +317,7 @@ func inHeapOrStack(b uintptr) bool {
 		return false
 	}
 	// Not a beginning of a block, consult span table to find the block beginning.
-	s := h_spans[(b-mheap_.arena_start)>>_PageShift]
+	s := mheap_.spans[(b-mheap_.arena_start)>>_PageShift]
 	if s == nil || b < s.base() {
 		return false
 	}
@@ -311,7 +347,7 @@ func spanOf(p uintptr) *mspan {
 // that p points into the heap (that is, mheap_.arena_start <= p <
 // mheap_.arena_used).
 func spanOfUnchecked(p uintptr) *mspan {
-	return h_spans[(p-mheap_.arena_start)>>_PageShift]
+	return mheap_.spans[(p-mheap_.arena_start)>>_PageShift]
 }
 
 func mlookup(v uintptr, base *uintptr, size *uintptr, sp **mspan) int32 {
@@ -364,12 +400,21 @@ func mlookup(v uintptr, base *uintptr, size *uintptr, sp **mspan) int32 {
 }
 
 // Initialize the heap.
-func (h *mheap) init(spans_size uintptr) {
+func (h *mheap) init(spansStart, spansBytes uintptr) {
 	h.spanalloc.init(unsafe.Sizeof(mspan{}), recordspan, unsafe.Pointer(h), &memstats.mspan_sys)
 	h.cachealloc.init(unsafe.Sizeof(mcache{}), nil, nil, &memstats.mcache_sys)
 	h.specialfinalizeralloc.init(unsafe.Sizeof(specialfinalizer{}), nil, nil, &memstats.other_sys)
 	h.specialprofilealloc.init(unsafe.Sizeof(specialprofile{}), nil, nil, &memstats.other_sys)
 
+	// Don't zero mspan allocations. Background sweeping can
+	// inspect a span concurrently with allocating it, so it's
+	// important that the span's sweepgen survive across freeing
+	// and re-allocating a span to prevent background sweeping
+	// from improperly cas'ing it from 0.
+	//
+	// This is safe because mspan contains no heap pointers.
+	h.spanalloc.zero = false
+
 	// h->mapcache needs no init
 	for i := range h.free {
 		h.free[i].init()
@@ -382,10 +427,10 @@ func (h *mheap) init(spans_size uintptr) {
 		h.central[i].mcentral.init(int32(i))
 	}
 
-	sp := (*slice)(unsafe.Pointer(&h_spans))
-	sp.array = unsafe.Pointer(h.spans)
-	sp.len = int(spans_size / sys.PtrSize)
-	sp.cap = int(spans_size / sys.PtrSize)
+	sp := (*slice)(unsafe.Pointer(&h.spans))
+	sp.array = unsafe.Pointer(spansStart)
+	sp.len = 0
+	sp.cap = int(spansBytes / sys.PtrSize)
 }
 
 // mHeap_MapSpans makes sure that the spans are mapped
@@ -401,12 +446,14 @@ func (h *mheap) mapSpans(arena_used uintptr) {
 	n := arena_used
 	n -= h.arena_start
 	n = n / _PageSize * sys.PtrSize
-	n = round(n, sys.PhysPageSize)
-	if h.spans_mapped >= n {
+	n = round(n, physPageSize)
+	need := n / unsafe.Sizeof(h.spans[0])
+	have := uintptr(len(h.spans))
+	if have >= need {
 		return
 	}
-	sysMap(add(unsafe.Pointer(h.spans), h.spans_mapped), n-h.spans_mapped, h.arena_reserved, &memstats.other_sys)
-	h.spans_mapped = n
+	h.spans = h.spans[:need]
+	sysMap(unsafe.Pointer(&h.spans[have]), (need-have)*unsafe.Sizeof(h.spans[0]), h.arena_reserved, &memstats.other_sys)
 }
 
 // Sweeps spans in list until reclaims at least npages into heap.
@@ -517,6 +564,7 @@ func (h *mheap) alloc_m(npage uintptr, sizeclass int32, large bool) *mspan {
 		// Record span info, because gc needs to be
 		// able to map interior pointer to containing span.
 		atomic.Store(&s.sweepgen, h.sweepgen)
+		h.sweepSpans[h.sweepgen/2%2].push(s) // Add to swept in-use list.
 		s.state = _MSpanInUse
 		s.allocCount = 0
 		s.sizeclass = uint8(sizeclass)
@@ -557,15 +605,15 @@ func (h *mheap) alloc_m(npage uintptr, sizeclass int32, large bool) *mspan {
 		traceHeapAlloc()
 	}
 
-	// h_spans is accessed concurrently without synchronization
+	// h.spans is accessed concurrently without synchronization
 	// from other threads. Hence, there must be a store/store
-	// barrier here to ensure the writes to h_spans above happen
+	// barrier here to ensure the writes to h.spans above happen
 	// before the caller can publish a pointer p to an object
 	// allocated from s. As soon as this happens, the garbage
 	// collector running on another processor could read p and
-	// look up s in h_spans. The unlock acts as the barrier to
+	// look up s in h.spans. The unlock acts as the barrier to
 	// order these writes. On the read side, the data dependency
-	// between p and the index in h_spans orders the reads.
+	// between p and the index in h.spans orders the reads.
 	unlock(&h.lock)
 	return s
 }
@@ -581,7 +629,7 @@ func (h *mheap) alloc(npage uintptr, sizeclass int32, large bool, needzero bool)
 
 	if s != nil {
 		if needzero && s.needzero != 0 {
-			memclr(unsafe.Pointer(s.base()), s.npages<<_PageShift)
+			memclrNoHeapPointers(unsafe.Pointer(s.base()), s.npages<<_PageShift)
 		}
 		s.needzero = 0
 	}
@@ -661,10 +709,10 @@ HaveSpan:
 		s.npages = npage
 		p := (t.base() - h.arena_start) >> _PageShift
 		if p > 0 {
-			h_spans[p-1] = s
+			h.spans[p-1] = s
 		}
-		h_spans[p] = t
-		h_spans[p+t.npages-1] = t
+		h.spans[p] = t
+		h.spans[p+t.npages-1] = t
 		t.needzero = s.needzero
 		s.state = _MSpanStack // prevent coalescing with s
 		t.state = _MSpanStack
@@ -675,7 +723,7 @@ HaveSpan:
 
 	p := (s.base() - h.arena_start) >> _PageShift
 	for n := uintptr(0); n < npage; n++ {
-		h_spans[p+n] = s
+		h.spans[p+n] = s
 	}
 
 	memstats.heap_inuse += uint64(npage << _PageShift)
@@ -741,7 +789,7 @@ func (h *mheap) grow(npage uintptr) bool {
 	s.init(uintptr(v), ask>>_PageShift)
 	p := (s.base() - h.arena_start) >> _PageShift
 	for i := p; i < p+s.npages; i++ {
-		h_spans[i] = s
+		h.spans[i] = s
 	}
 	atomic.Store(&s.sweepgen, h.sweepgen)
 	s.state = _MSpanInUse
@@ -756,7 +804,7 @@ func (h *mheap) grow(npage uintptr) bool {
 func (h *mheap) lookup(v unsafe.Pointer) *mspan {
 	p := uintptr(v)
 	p -= h.arena_start
-	return h_spans[p>>_PageShift]
+	return h.spans[p>>_PageShift]
 }
 
 // Look up the span at the given address.
@@ -770,7 +818,7 @@ func (h *mheap) lookupMaybe(v unsafe.Pointer) *mspan {
 	if uintptr(v) < h.arena_start || uintptr(v) >= h.arena_used {
 		return nil
 	}
-	s := h_spans[(uintptr(v)-h.arena_start)>>_PageShift]
+	s := h.spans[(uintptr(v)-h.arena_start)>>_PageShift]
 	if s == nil || uintptr(v) < s.base() || uintptr(v) >= uintptr(unsafe.Pointer(s.limit)) || s.state != _MSpanInUse {
 		return nil
 	}
@@ -855,26 +903,26 @@ func (h *mheap) freeSpanLocked(s *mspan, acctinuse, acctidle bool, unusedsince i
 	// Coalesce with earlier, later spans.
 	p := (s.base() - h.arena_start) >> _PageShift
 	if p > 0 {
-		t := h_spans[p-1]
+		t := h.spans[p-1]
 		if t != nil && t.state == _MSpanFree {
 			s.startAddr = t.startAddr
 			s.npages += t.npages
 			s.npreleased = t.npreleased // absorb released pages
 			s.needzero |= t.needzero
 			p -= t.npages
-			h_spans[p] = s
+			h.spans[p] = s
 			h.freeList(t.npages).remove(t)
 			t.state = _MSpanDead
 			h.spanalloc.free(unsafe.Pointer(t))
 		}
 	}
-	if (p+s.npages)*sys.PtrSize < h.spans_mapped {
-		t := h_spans[p+s.npages]
+	if (p + s.npages) < uintptr(len(h.spans)) {
+		t := h.spans[p+s.npages]
 		if t != nil && t.state == _MSpanFree {
 			s.npages += t.npages
 			s.npreleased += t.npreleased
 			s.needzero |= t.needzero
-			h_spans[p+s.npages-1] = s
+			h.spans[p+s.npages-1] = s
 			h.freeList(t.npages).remove(t)
 			t.state = _MSpanDead
 			h.spanalloc.free(unsafe.Pointer(t))
@@ -909,14 +957,14 @@ func scavengelist(list *mSpanList, now, limit uint64) uintptr {
 		if (now-uint64(s.unusedsince)) > limit && s.npreleased != s.npages {
 			start := s.base()
 			end := start + s.npages<<_PageShift
-			if sys.PhysPageSize > _PageSize {
+			if physPageSize > _PageSize {
 				// We can only release pages in
-				// PhysPageSize blocks, so round start
+				// physPageSize blocks, so round start
 				// and end in. (Otherwise, madvise
 				// will round them *out* and release
 				// more memory than we want.)
-				start = (start + sys.PhysPageSize - 1) &^ (sys.PhysPageSize - 1)
-				end &^= sys.PhysPageSize - 1
+				start = (start + physPageSize - 1) &^ (physPageSize - 1)
+				end &^= physPageSize - 1
 				if end <= start {
 					// start and end don't span a
 					// whole physical page.
@@ -926,7 +974,7 @@ func scavengelist(list *mSpanList, now, limit uint64) uintptr {
 			len := end - start
 
 			released := len - (s.npreleased << _PageShift)
-			if sys.PhysPageSize > _PageSize && released == 0 {
+			if physPageSize > _PageSize && released == 0 {
 				continue
 			}
 			memstats.heap_released += uint64(released)
@@ -965,6 +1013,7 @@ func runtime_debug_freeOSMemory() {
 
 // Initialize a new span with the given start and npages.
 func (span *mspan) init(base uintptr, npages uintptr) {
+	// span is *not* zeroed.
 	span.next = nil
 	span.prev = nil
 	span.list = nil
@@ -986,28 +1035,30 @@ func (span *mspan) init(base uintptr, npages uintptr) {
 }
 
 func (span *mspan) inList() bool {
-	return span.prev != nil
+	return span.list != nil
 }
 
 // Initialize an empty doubly-linked list.
 func (list *mSpanList) init() {
 	list.first = nil
-	list.last = &list.first
+	list.last = nil
 }
 
 func (list *mSpanList) remove(span *mspan) {
-	if span.prev == nil || span.list != list {
+	if span.list != list {
 		println("runtime: failed MSpanList_Remove", span, span.prev, span.list, list)
 		throw("MSpanList_Remove")
 	}
-	if span.next != nil {
-		span.next.prev = span.prev
+	if list.first == span {
+		list.first = span.next
 	} else {
-		// TODO: After we remove the span.list != list check above,
-		// we could at least still check list.last == &span.next here.
+		span.prev.next = span.next
+	}
+	if list.last == span {
 		list.last = span.prev
+	} else {
+		span.next.prev = span.prev
 	}
-	*span.prev = span.next
 	span.next = nil
 	span.prev = nil
 	span.list = nil
@@ -1024,12 +1075,14 @@ func (list *mSpanList) insert(span *mspan) {
 	}
 	span.next = list.first
 	if list.first != nil {
-		list.first.prev = &span.next
+		// The list contains at least one span; link it in.
+		// The last span in the list doesn't change.
+		list.first.prev = span
 	} else {
-		list.last = &span.next
+		// The list contains no spans, so this is also the last span.
+		list.last = span
 	}
 	list.first = span
-	span.prev = &list.first
 	span.list = list
 }
 
@@ -1038,10 +1091,15 @@ func (list *mSpanList) insertBack(span *mspan) {
 		println("failed MSpanList_InsertBack", span, span.next, span.prev, span.list)
 		throw("MSpanList_InsertBack")
 	}
-	span.next = nil
 	span.prev = list.last
-	*list.last = span
-	list.last = &span.next
+	if list.last != nil {
+		// The list contains at least one span.
+		list.last.next = span
+	} else {
+		// The list contains no spans, so this is also the first span.
+		list.first = span
+	}
+	list.last = span
 	span.list = list
 }
 
@@ -1054,6 +1112,7 @@ const (
 	// if that happens.
 )
 
+//go:notinheap
 type special struct {
 	next   *special // linked list in span
 	offset uint16   // span offset of object
@@ -1151,12 +1210,17 @@ func removespecial(p unsafe.Pointer, kind uint8) *special {
 }
 
 // The described object has a finalizer set for it.
+//
+// specialfinalizer is allocated from non-GC'd memory, so any heap
+// pointers must be specially handled.
+//
+//go:notinheap
 type specialfinalizer struct {
 	special special
-	fn      *funcval
+	fn      *funcval // May be a heap pointer.
 	nret    uintptr
-	fint    *_type
-	ot      *ptrtype
+	fint    *_type   // May be a heap pointer, but always live.
+	ot      *ptrtype // May be a heap pointer, but always live.
 }
 
 // Adds a finalizer to the object p. Returns true if it succeeded.
@@ -1211,6 +1275,8 @@ func removefinalizer(p unsafe.Pointer) {
 }
 
 // The described object is being heap profiled.
+//
+//go:notinheap
 type specialprofile struct {
 	special special
 	b       *bucket
@@ -1258,6 +1324,7 @@ type gcBitsHeader struct {
 	next uintptr // *gcBits triggers recursive type bug. (issue 14620)
 }
 
+//go:notinheap
 type gcBits struct {
 	// gcBitsHeader // side step recursive type bug (issue 14620) by including fields by hand.
 	free uintptr // free is the index into bits of the next free byte.
@@ -1351,7 +1418,7 @@ func newArena() *gcBits {
 	} else {
 		result = gcBitsArenas.free
 		gcBitsArenas.free = gcBitsArenas.free.next
-		memclr(unsafe.Pointer(result), gcBitsChunkBytes)
+		memclrNoHeapPointers(unsafe.Pointer(result), gcBitsChunkBytes)
 	}
 	result.next = nil
 	// If result.bits is not 8 byte aligned adjust index so
diff --git a/src/runtime/mkduff.go b/src/runtime/mkduff.go
index 0e7cc66..cf6b37f 100644
--- a/src/runtime/mkduff.go
+++ b/src/runtime/mkduff.go
@@ -8,14 +8,14 @@
 // The compiler jumps to computed addresses within
 // the routine to zero chunks of memory.
 // Do not change duffzero without also
-// changing clearfat in cmd/?g/ggen.go.
+// changing the uses in cmd/compile/internal/*/*.go.
 
 // runtime·duffcopy is a Duff's device for copying memory.
 // The compiler jumps to computed addresses within
 // the routine to copy chunks of memory.
 // Source and destination must not overlap.
 // Do not change duffcopy without also
-// changing blockcopy in cmd/?g/cgen.go.
+// changing the uses in cmd/compile/internal/*/*.go.
 
 // See the zero* and copy* generators below
 // for architecture-specific comments.
@@ -161,7 +161,17 @@ func zeroARM64(w io.Writer) {
 }
 
 func copyARM64(w io.Writer) {
-	fmt.Fprintln(w, "// TODO: Implement runtime·duffcopy.")
+	// R16 (aka REGRT1): ptr to source memory
+	// R17 (aka REGRT2): ptr to destination memory
+	// R27 (aka REGTMP): scratch space
+	// R16 and R17 are updated as a side effect
+	fmt.Fprintln(w, "TEXT runtime·duffcopy(SB), NOSPLIT, $0-0")
+	for i := 0; i < 128; i++ {
+		fmt.Fprintln(w, "\tMOVD.P\t8(R16), R27")
+		fmt.Fprintln(w, "\tMOVD.P\tR27, 8(R17)")
+		fmt.Fprintln(w)
+	}
+	fmt.Fprintln(w, "\tRET")
 }
 
 func tagsPPC64x(w io.Writer) {
diff --git a/src/runtime/mksizeclasses.go b/src/runtime/mksizeclasses.go
new file mode 100644
index 0000000..587d3c7
--- /dev/null
+++ b/src/runtime/mksizeclasses.go
@@ -0,0 +1,309 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Generate tables for small malloc size classes.
+//
+// See malloc.go for overview.
+//
+// The size classes are chosen so that rounding an allocation
+// request up to the next size class wastes at most 12.5% (1.125x).
+//
+// Each size class has its own page count that gets allocated
+// and chopped up when new objects of the size class are needed.
+// That page count is chosen so that chopping up the run of
+// pages into objects of the given size wastes at most 12.5% (1.125x)
+// of the memory. It is not necessary that the cutoff here be
+// the same as above.
+//
+// The two sources of waste multiply, so the worst possible case
+// for the above constraints would be that allocations of some
+// size might have a 26.6% (1.266x) overhead.
+// In practice, only one of the wastes comes into play for a
+// given size (sizes < 512 waste mainly on the round-up,
+// sizes > 512 waste mainly on the page chopping).
+//
+// TODO(rsc): Compute max waste for any given size.
+
+package main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"go/format"
+	"io"
+	"io/ioutil"
+	"log"
+	"os"
+)
+
+// Generate msize.go
+
+var stdout = flag.Bool("stdout", false, "write to stdout instead of sizeclasses.go")
+
+func main() {
+	flag.Parse()
+
+	var b bytes.Buffer
+	fmt.Fprintln(&b, "// AUTO-GENERATED by mksizeclasses.go; DO NOT EDIT")
+	fmt.Fprintln(&b, "//go:generate go run mksizeclasses.go")
+	fmt.Fprintln(&b)
+	fmt.Fprintln(&b, "package runtime")
+	classes := makeClasses()
+
+	printClasses(&b, classes)
+
+	out, err := format.Source(b.Bytes())
+	if err != nil {
+		log.Fatal(err)
+	}
+	if *stdout {
+		_, err = os.Stdout.Write(out)
+	} else {
+		err = ioutil.WriteFile("sizeclasses.go", out, 0666)
+	}
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+const (
+	// Constants that we use and will transfer to the runtime.
+	maxSmallSize = 32 << 10
+	smallSizeDiv = 8
+	smallSizeMax = 1024
+	largeSizeDiv = 128
+	pageShift    = 13
+
+	// Derived constants.
+	pageSize = 1 << pageShift
+)
+
+type class struct {
+	size   int // max size
+	npages int // number of pages
+
+	mul    int
+	shift  uint
+	shift2 uint
+	mask   int
+}
+
+func powerOfTwo(x int) bool {
+	return x != 0 && x&(x-1) == 0
+}
+
+func makeClasses() []class {
+	var classes []class
+
+	classes = append(classes, class{}) // class #0 is a dummy entry
+
+	align := 8
+	for size := align; size <= maxSmallSize; size += align {
+		if powerOfTwo(size) { // bump alignment once in a while
+			if size >= 2048 {
+				align = 256
+			} else if size >= 128 {
+				align = size / 8
+			} else if size >= 16 {
+				align = 16 // required for x86 SSE instructions, if we want to use them
+			}
+		}
+		if !powerOfTwo(align) {
+			panic("incorrect alignment")
+		}
+
+		// Make the allocnpages big enough that
+		// the leftover is less than 1/8 of the total,
+		// so wasted space is at most 12.5%.
+		allocsize := pageSize
+		for allocsize%size > allocsize/8 {
+			allocsize += pageSize
+		}
+		npages := allocsize / pageSize
+
+		// If the previous sizeclass chose the same
+		// allocation size and fit the same number of
+		// objects into the page, we might as well
+		// use just this size instead of having two
+		// different sizes.
+		if len(classes) > 1 && npages == classes[len(classes)-1].npages && allocsize/size == allocsize/classes[len(classes)-1].size {
+			classes[len(classes)-1].size = size
+			continue
+		}
+		classes = append(classes, class{size: size, npages: npages})
+	}
+
+	// Increase object sizes if we can fit the same number of larger objects
+	// into the same number of pages. For example, we choose size 8448 above
+	// with 6 objects in 7 pages. But we can well use object size 9472,
+	// which is also 6 objects in 7 pages but +1024 bytes (+12.12%).
+	// We need to preserve at least largeSizeDiv alignment otherwise
+	// sizeToClass won't work.
+	for i := range classes {
+		if i == 0 {
+			continue
+		}
+		c := &classes[i]
+		psize := c.npages * pageSize
+		new_size := (psize / (psize / c.size)) &^ (largeSizeDiv - 1)
+		if new_size > c.size {
+			c.size = new_size
+		}
+	}
+
+	if len(classes) != 67 {
+		panic("number of size classes has changed")
+	}
+
+	for i := range classes {
+		computeDivMagic(&classes[i])
+	}
+
+	return classes
+}
+
+// computeDivMagic computes some magic constants to implement
+// the division required to compute object number from span offset.
+// n / c.size is implemented as n >> c.shift * c.mul >> c.shift2
+// for all 0 <= n < c.npages * pageSize
+func computeDivMagic(c *class) {
+	// divisor
+	d := c.size
+	if d == 0 {
+		return
+	}
+
+	// maximum input value for which the formula needs to work.
+	max := c.npages*pageSize - 1
+
+	if powerOfTwo(d) {
+		// If the size is a power of two, heapBitsForObject can divide even faster by masking.
+		// Compute this mask.
+		if max >= 1<<16 {
+			panic("max too big for power of two size")
+		}
+		c.mask = 1<<16 - d
+	}
+
+	// Compute pre-shift by factoring power of 2 out of d.
+	for d%2 == 0 {
+		c.shift++
+		d >>= 1
+		max >>= 1
+	}
+
+	// Find the smallest k that works.
+	// A small k allows us to fit the math required into 32 bits
+	// so we can use 32-bit multiplies and shifts on 32-bit platforms.
+nextk:
+	for k := uint(0); ; k++ {
+		mul := (int(1)<<k + d - 1) / d //  ⌈2^k / d⌉
+
+		// Test to see if mul works.
+		for n := 0; n <= max; n++ {
+			if n*mul>>k != n/d {
+				continue nextk
+			}
+		}
+		if mul >= 1<<16 {
+			panic("mul too big")
+		}
+		if uint64(mul)*uint64(max) >= 1<<32 {
+			panic("mul*max too big")
+		}
+		c.mul = mul
+		c.shift2 = k
+		break
+	}
+
+	// double-check.
+	for n := 0; n <= max; n++ {
+		if n*c.mul>>c.shift2 != n/d {
+			fmt.Printf("d=%d max=%d mul=%d shift2=%d n=%d\n", d, max, c.mul, c.shift2, n)
+			panic("bad multiply magic")
+		}
+		// Also check the exact computations that will be done by the runtime,
+		// for both 32 and 64 bit operations.
+		if uint32(n)*uint32(c.mul)>>uint8(c.shift2) != uint32(n/d) {
+			fmt.Printf("d=%d max=%d mul=%d shift2=%d n=%d\n", d, max, c.mul, c.shift2, n)
+			panic("bad 32-bit multiply magic")
+		}
+		if uint64(n)*uint64(c.mul)>>uint8(c.shift2) != uint64(n/d) {
+			fmt.Printf("d=%d max=%d mul=%d shift2=%d n=%d\n", d, max, c.mul, c.shift2, n)
+			panic("bad 64-bit multiply magic")
+		}
+	}
+}
+
+func printClasses(w io.Writer, classes []class) {
+	fmt.Fprintln(w, "const (")
+	fmt.Fprintf(w, "_MaxSmallSize = %d\n", maxSmallSize)
+	fmt.Fprintf(w, "smallSizeDiv = %d\n", smallSizeDiv)
+	fmt.Fprintf(w, "smallSizeMax = %d\n", smallSizeMax)
+	fmt.Fprintf(w, "largeSizeDiv = %d\n", largeSizeDiv)
+	fmt.Fprintf(w, "_NumSizeClasses = %d\n", len(classes))
+	fmt.Fprintf(w, "_PageShift = %d\n", pageShift)
+	fmt.Fprintln(w, ")")
+
+	fmt.Fprint(w, "var class_to_size = [_NumSizeClasses]uint16 {")
+	for _, c := range classes {
+		fmt.Fprintf(w, "%d,", c.size)
+	}
+	fmt.Fprintln(w, "}")
+
+	fmt.Fprint(w, "var class_to_allocnpages = [_NumSizeClasses]uint8 {")
+	for _, c := range classes {
+		fmt.Fprintf(w, "%d,", c.npages)
+	}
+	fmt.Fprintln(w, "}")
+
+	fmt.Fprintln(w, "type divMagic struct {")
+	fmt.Fprintln(w, "  shift uint8")
+	fmt.Fprintln(w, "  shift2 uint8")
+	fmt.Fprintln(w, "  mul uint16")
+	fmt.Fprintln(w, "  baseMask uint16")
+	fmt.Fprintln(w, "}")
+	fmt.Fprint(w, "var class_to_divmagic = [_NumSizeClasses]divMagic {")
+	for _, c := range classes {
+		fmt.Fprintf(w, "{%d,%d,%d,%d},", c.shift, c.shift2, c.mul, c.mask)
+	}
+	fmt.Fprintln(w, "}")
+
+	// map from size to size class, for small sizes.
+	sc := make([]int, smallSizeMax/smallSizeDiv+1)
+	for i := range sc {
+		size := i * smallSizeDiv
+		for j, c := range classes {
+			if c.size >= size {
+				sc[i] = j
+				break
+			}
+		}
+	}
+	fmt.Fprint(w, "var size_to_class8 = [smallSizeMax/smallSizeDiv+1]uint8 {")
+	for _, v := range sc {
+		fmt.Fprintf(w, "%d,", v)
+	}
+	fmt.Fprintln(w, "}")
+
+	// map from size to size class, for large sizes.
+	sc = make([]int, (maxSmallSize-smallSizeMax)/largeSizeDiv+1)
+	for i := range sc {
+		size := smallSizeMax + i*largeSizeDiv
+		for j, c := range classes {
+			if c.size >= size {
+				sc[i] = j
+				break
+			}
+		}
+	}
+	fmt.Fprint(w, "var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv+1]uint8 {")
+	for _, v := range sc {
+		fmt.Fprintf(w, "%d,", v)
+	}
+	fmt.Fprintln(w, "}")
+}
diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go
index c3e4e2c..fc06d8d 100644
--- a/src/runtime/mprof.go
+++ b/src/runtime/mprof.go
@@ -22,6 +22,7 @@ const (
 	// profile types
 	memProfile bucketType = 1 + iota
 	blockProfile
+	mutexProfile
 
 	// size of bucket hash table
 	buckHashSize = 179999
@@ -40,10 +41,14 @@ type bucketType int
 //
 // Per-call-stack profiling information.
 // Lookup by hashing call stack into a linked-list hash table.
+//
+// No heap pointers.
+//
+//go:notinheap
 type bucket struct {
 	next    *bucket
 	allnext *bucket
-	typ     bucketType // memBucket or blockBucket
+	typ     bucketType // memBucket or blockBucket (includes mutexProfile)
 	hash    uintptr
 	size    uintptr
 	nstk    uintptr
@@ -83,7 +88,7 @@ type memRecord struct {
 }
 
 // A blockRecord is the bucket data for a bucket of type blockProfile,
-// part of the blocking profile.
+// which is used in blocking and mutex profiles.
 type blockRecord struct {
 	count  int64
 	cycles int64
@@ -92,6 +97,7 @@ type blockRecord struct {
 var (
 	mbuckets  *bucket // memory profile buckets
 	bbuckets  *bucket // blocking profile buckets
+	xbuckets  *bucket // mutex profile buckets
 	buckhash  *[179999]*bucket
 	bucketmem uintptr
 )
@@ -104,7 +110,7 @@ func newBucket(typ bucketType, nstk int) *bucket {
 		throw("invalid profile bucket type")
 	case memProfile:
 		size += unsafe.Sizeof(memRecord{})
-	case blockProfile:
+	case blockProfile, mutexProfile:
 		size += unsafe.Sizeof(blockRecord{})
 	}
 
@@ -132,7 +138,7 @@ func (b *bucket) mp() *memRecord {
 
 // bp returns the blockRecord associated with the blockProfile bucket b.
 func (b *bucket) bp() *blockRecord {
-	if b.typ != blockProfile {
+	if b.typ != blockProfile && b.typ != mutexProfile {
 		throw("bad use of bucket.bp")
 	}
 	data := add(unsafe.Pointer(b), unsafe.Sizeof(*b)+b.nstk*unsafe.Sizeof(uintptr(0)))
@@ -184,6 +190,9 @@ func stkbucket(typ bucketType, size uintptr, stk []uintptr, alloc bool) *bucket
 	if typ == memProfile {
 		b.allnext = mbuckets
 		mbuckets = b
+	} else if typ == mutexProfile {
+		b.allnext = xbuckets
+		xbuckets = b
 	} else {
 		b.allnext = bbuckets
 		bbuckets = b
@@ -288,10 +297,20 @@ func blockevent(cycles int64, skip int) {
 	if cycles <= 0 {
 		cycles = 1
 	}
+	if blocksampled(cycles) {
+		saveblockevent(cycles, skip+1, blockProfile, &blockprofilerate)
+	}
+}
+
+func blocksampled(cycles int64) bool {
 	rate := int64(atomic.Load64(&blockprofilerate))
-	if rate <= 0 || (rate > cycles && int64(fastrand1())%rate > cycles) {
-		return
+	if rate <= 0 || (rate > cycles && int64(fastrand())%rate > cycles) {
+		return false
 	}
+	return true
+}
+
+func saveblockevent(cycles int64, skip int, which bucketType, ratep *uint64) {
 	gp := getg()
 	var nstk int
 	var stk [maxStack]uintptr
@@ -301,12 +320,43 @@ func blockevent(cycles int64, skip int) {
 		nstk = gcallers(gp.m.curg, skip, stk[:])
 	}
 	lock(&proflock)
-	b := stkbucket(blockProfile, 0, stk[:nstk], true)
+	b := stkbucket(which, 0, stk[:nstk], true)
 	b.bp().count++
 	b.bp().cycles += cycles
 	unlock(&proflock)
 }
 
+var mutexprofilerate uint64 // fraction sampled
+
+// SetMutexProfileFraction controls the fraction of mutex contention events
+// that are reported in the mutex profile. On average 1/rate events are
+// reported. The previous rate is returned.
+//
+// To turn off profiling entirely, pass rate 0.
+// To just read the current rate, pass rate -1.
+// (For n>1 the details of sampling may change.)
+func SetMutexProfileFraction(rate int) int {
+	if rate < 0 {
+		return int(mutexprofilerate)
+	}
+	old := mutexprofilerate
+	atomic.Store64(&mutexprofilerate, uint64(rate))
+	return int(old)
+}
+
+//go:linkname mutexevent sync.event
+func mutexevent(cycles int64, skip int) {
+	if cycles < 0 {
+		cycles = 0
+	}
+	rate := int64(atomic.Load64(&mutexprofilerate))
+	// TODO(pjw): measure impact of always calling fastrand vs using something
+	// like malloc.go:nextSample()
+	if rate > 0 && int64(fastrand())%rate == 0 {
+		saveblockevent(cycles, skip+1, mutexProfile, &mutexprofilerate)
+	}
+}
+
 // Go interface to profile data.
 
 // A StackRecord describes a single execution stack.
@@ -438,6 +488,12 @@ func record(r *MemProfileRecord, b *bucket) {
 	r.FreeBytes = int64(mp.free_bytes)
 	r.AllocObjects = int64(mp.allocs)
 	r.FreeObjects = int64(mp.frees)
+	if raceenabled {
+		racewriterangepc(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0), getcallerpc(unsafe.Pointer(&r)), funcPC(MemProfile))
+	}
+	if msanenabled {
+		msanwrite(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0))
+	}
 	copy(r.Stack0[:], b.stk())
 	for i := int(b.nstk); i < len(r.Stack0); i++ {
 		r.Stack0[i] = 0
@@ -480,6 +536,41 @@ func BlockProfile(p []BlockProfileRecord) (n int, ok bool) {
 			r := &p[0]
 			r.Count = bp.count
 			r.Cycles = bp.cycles
+			if raceenabled {
+				racewriterangepc(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0), getcallerpc(unsafe.Pointer(&p)), funcPC(BlockProfile))
+			}
+			if msanenabled {
+				msanwrite(unsafe.Pointer(&r.Stack0[0]), unsafe.Sizeof(r.Stack0))
+			}
+			i := copy(r.Stack0[:], b.stk())
+			for ; i < len(r.Stack0); i++ {
+				r.Stack0[i] = 0
+			}
+			p = p[1:]
+		}
+	}
+	unlock(&proflock)
+	return
+}
+
+// MutexProfile returns n, the number of records in the current mutex profile.
+// If len(p) >= n, MutexProfile copies the profile into p and returns n, true.
+// Otherwise, MutexProfile does not change p, and returns n, false.
+//
+// Most clients should use the runtime/pprof package
+// instead of calling MutexProfile directly.
+func MutexProfile(p []BlockProfileRecord) (n int, ok bool) {
+	lock(&proflock)
+	for b := xbuckets; b != nil; b = b.allnext {
+		n++
+	}
+	if n <= len(p) {
+		ok = true
+		for b := xbuckets; b != nil; b = b.allnext {
+			bp := b.bp()
+			r := &p[0]
+			r.Count = int64(bp.count)
+			r.Cycles = bp.cycles
 			i := copy(r.Stack0[:], b.stk())
 			for ; i < len(r.Stack0); i++ {
 				r.Stack0[i] = 0
diff --git a/src/runtime/msan_amd64.s b/src/runtime/msan_amd64.s
index 9c59eec..cbe739d 100644
--- a/src/runtime/msan_amd64.s
+++ b/src/runtime/msan_amd64.s
@@ -62,12 +62,16 @@ TEXT	runtime·msanfree(SB), NOSPLIT, $0-16
 TEXT	msancall<>(SB), NOSPLIT, $0-0
 	get_tls(R12)
 	MOVQ	g(R12), R14
+	MOVQ	SP, R12		// callee-saved, preserved across the CALL
+	CMPQ	R14, $0
+	JE	call	// no g; still on a system stack
+
 	MOVQ	g_m(R14), R13
 	// Switch to g0 stack.
-	MOVQ	SP, R12		// callee-saved, preserved across the CALL
 	MOVQ	m_g0(R13), R10
 	CMPQ	R10, R14
 	JE	call	// already on g0
+
 	MOVQ	(g_sched+gobuf_sp)(R10), SP
 call:
 	ANDQ	$~15, SP	// alignment for gcc ABI
diff --git a/src/runtime/msize.go b/src/runtime/msize.go
index 18577b3..438c987 100644
--- a/src/runtime/msize.go
+++ b/src/runtime/msize.go
@@ -5,191 +5,39 @@
 // Malloc small size classes.
 //
 // See malloc.go for overview.
-//
-// The size classes are chosen so that rounding an allocation
-// request up to the next size class wastes at most 12.5% (1.125x).
-//
-// Each size class has its own page count that gets allocated
-// and chopped up when new objects of the size class are needed.
-// That page count is chosen so that chopping up the run of
-// pages into objects of the given size wastes at most 12.5% (1.125x)
-// of the memory. It is not necessary that the cutoff here be
-// the same as above.
-//
-// The two sources of waste multiply, so the worst possible case
-// for the above constraints would be that allocations of some
-// size might have a 26.6% (1.266x) overhead.
-// In practice, only one of the wastes comes into play for a
-// given size (sizes < 512 waste mainly on the round-up,
-// sizes > 512 waste mainly on the page chopping).
-//
-// TODO(rsc): Compute max waste for any given size.
+// See also mksizeclasses.go for how we decide what size classes to use.
 
 package runtime
 
-// Size classes. Computed and initialized by InitSizes.
-//
-// SizeToClass(0 <= n <= MaxSmallSize) returns the size class,
+// sizeToClass(0 <= n <= MaxSmallSize) returns the size class,
 //	1 <= sizeclass < NumSizeClasses, for n.
 //	Size class 0 is reserved to mean "not small".
 //
-// class_to_size[i] = largest size in class i
-// class_to_allocnpages[i] = number of pages to allocate when
-//	making new objects in class i
-
-// The SizeToClass lookup is implemented using two arrays,
+// The sizeToClass lookup is implemented using two arrays,
 // one mapping sizes <= 1024 to their class and one mapping
 // sizes >= 1024 and <= MaxSmallSize to their class.
 // All objects are 8-aligned, so the first array is indexed by
 // the size divided by 8 (rounded up).  Objects >= 1024 bytes
 // are 128-aligned, so the second array is indexed by the
-// size divided by 128 (rounded up).  The arrays are filled in
-// by InitSizes.
-
-var class_to_size [_NumSizeClasses]int32
-var class_to_allocnpages [_NumSizeClasses]int32
-var class_to_divmagic [_NumSizeClasses]divMagic
-
-var size_to_class8 [1024/8 + 1]int8
-var size_to_class128 [(_MaxSmallSize-1024)/128 + 1]int8
-
-func sizeToClass(size int32) int32 {
+// size divided by 128 (rounded up).  The arrays are constants
+// in sizeclass.go generated by mksizeclass.go.
+func sizeToClass(size uint32) uint32 {
 	if size > _MaxSmallSize {
 		throw("invalid size")
 	}
-	if size > 1024-8 {
-		return int32(size_to_class128[(size-1024+127)>>7])
-	}
-	return int32(size_to_class8[(size+7)>>3])
-}
-
-func initSizes() {
-	// Initialize the runtime·class_to_size table (and choose class sizes in the process).
-	class_to_size[0] = 0
-	sizeclass := 1 // 0 means no class
-	align := 8
-	for size := align; size <= _MaxSmallSize; size += align {
-		if size&(size-1) == 0 { // bump alignment once in a while
-			if size >= 2048 {
-				align = 256
-			} else if size >= 128 {
-				align = size / 8
-			} else if size >= 16 {
-				align = 16 // required for x86 SSE instructions, if we want to use them
-			}
-		}
-		if align&(align-1) != 0 {
-			throw("incorrect alignment")
-		}
-
-		// Make the allocnpages big enough that
-		// the leftover is less than 1/8 of the total,
-		// so wasted space is at most 12.5%.
-		allocsize := _PageSize
-		for allocsize%size > allocsize/8 {
-			allocsize += _PageSize
-		}
-		npages := allocsize >> _PageShift
-
-		// If the previous sizeclass chose the same
-		// allocation size and fit the same number of
-		// objects into the page, we might as well
-		// use just this size instead of having two
-		// different sizes.
-		if sizeclass > 1 && npages == int(class_to_allocnpages[sizeclass-1]) && allocsize/size == allocsize/int(class_to_size[sizeclass-1]) {
-			class_to_size[sizeclass-1] = int32(size)
-			continue
-		}
-
-		class_to_allocnpages[sizeclass] = int32(npages)
-		class_to_size[sizeclass] = int32(size)
-		sizeclass++
-	}
-	if sizeclass != _NumSizeClasses {
-		print("runtime: sizeclass=", sizeclass, " NumSizeClasses=", _NumSizeClasses, "\n")
-		throw("bad NumSizeClasses")
-	}
-	// Check maxObjsPerSpan => number of objects invariant.
-	for i, size := range class_to_size {
-		if size != 0 && class_to_allocnpages[i]*pageSize/size > maxObjsPerSpan {
-			throw("span contains too many objects")
-		}
-		if size == 0 && i != 0 {
-			throw("size is 0 but class is not 0")
-		}
-	}
-	// Initialize the size_to_class tables.
-	nextsize := 0
-	for sizeclass = 1; sizeclass < _NumSizeClasses; sizeclass++ {
-		for ; nextsize < 1024 && nextsize <= int(class_to_size[sizeclass]); nextsize += 8 {
-			size_to_class8[nextsize/8] = int8(sizeclass)
-		}
-		if nextsize >= 1024 {
-			for ; nextsize <= int(class_to_size[sizeclass]); nextsize += 128 {
-				size_to_class128[(nextsize-1024)/128] = int8(sizeclass)
-			}
-		}
-	}
-
-	// Double-check SizeToClass.
-	if false {
-		for n := int32(0); n < _MaxSmallSize; n++ {
-			sizeclass := sizeToClass(n)
-			if sizeclass < 1 || sizeclass >= _NumSizeClasses || class_to_size[sizeclass] < n {
-				print("runtime: size=", n, " sizeclass=", sizeclass, " runtime·class_to_size=", class_to_size[sizeclass], "\n")
-				print("incorrect SizeToClass\n")
-				goto dump
-			}
-			if sizeclass > 1 && class_to_size[sizeclass-1] >= n {
-				print("runtime: size=", n, " sizeclass=", sizeclass, " runtime·class_to_size=", class_to_size[sizeclass], "\n")
-				print("SizeToClass too big\n")
-				goto dump
-			}
-		}
-	}
-
-	testdefersizes()
-
-	// Copy out for statistics table.
-	for i := 0; i < len(class_to_size); i++ {
-		memstats.by_size[i].size = uint32(class_to_size[i])
-	}
-
-	for i := 1; i < len(class_to_size); i++ {
-		class_to_divmagic[i] = computeDivMagic(uint32(class_to_size[i]))
+	if size > smallSizeMax-8 {
+		return uint32(size_to_class128[(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv])
 	}
-
-	return
-
-dump:
-	if true {
-		print("runtime: NumSizeClasses=", _NumSizeClasses, "\n")
-		print("runtime·class_to_size:")
-		for sizeclass = 0; sizeclass < _NumSizeClasses; sizeclass++ {
-			print(" ", class_to_size[sizeclass], "")
-		}
-		print("\n\n")
-		print("runtime: size_to_class8:")
-		for i := 0; i < len(size_to_class8); i++ {
-			print(" ", i*8, "=>", size_to_class8[i], "(", class_to_size[size_to_class8[i]], ")\n")
-		}
-		print("\n")
-		print("runtime: size_to_class128:")
-		for i := 0; i < len(size_to_class128); i++ {
-			print(" ", i*128, "=>", size_to_class128[i], "(", class_to_size[size_to_class128[i]], ")\n")
-		}
-		print("\n")
-	}
-	throw("InitSizes failed")
+	return uint32(size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv])
 }
 
 // Returns size of the memory block that mallocgc will allocate if you ask for the size.
 func roundupsize(size uintptr) uintptr {
 	if size < _MaxSmallSize {
-		if size <= 1024-8 {
-			return uintptr(class_to_size[size_to_class8[(size+7)>>3]])
+		if size <= smallSizeMax-8 {
+			return uintptr(class_to_size[size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv]])
 		} else {
-			return uintptr(class_to_size[size_to_class128[(size-1024+127)>>7]])
+			return uintptr(class_to_size[size_to_class128[(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv]])
 		}
 	}
 	if size+_PageSize < size {
@@ -197,66 +45,3 @@ func roundupsize(size uintptr) uintptr {
 	}
 	return round(size, _PageSize)
 }
-
-// divMagic holds magic constants to implement division
-// by a particular constant as a shift, multiply, and shift.
-// That is, given
-//	m = computeMagic(d)
-// then
-//	n/d == ((n>>m.shift) * m.mul) >> m.shift2
-//
-// The magic computation picks m such that
-//	d = d₁*d₂
-//	d₂= 2^m.shift
-//	m.mul = ⌈2^m.shift2 / d₁⌉
-//
-// The magic computation here is tailored for malloc block sizes
-// and does not handle arbitrary d correctly. Malloc block sizes d are
-// always even, so the first shift implements the factors of 2 in d
-// and then the mul and second shift implement the odd factor
-// that remains. Because the first shift divides n by at least 2 (actually 8)
-// before the multiply gets involved, the huge corner cases that
-// require additional adjustment are impossible, so the usual
-// fixup is not needed.
-//
-// For more details see Hacker's Delight, Chapter 10, and
-// http://ridiculousfish.com/blog/posts/labor-of-division-episode-i.html
-// http://ridiculousfish.com/blog/posts/labor-of-division-episode-iii.html
-type divMagic struct {
-	shift    uint8
-	mul      uint32
-	shift2   uint8
-	baseMask uintptr
-}
-
-func computeDivMagic(d uint32) divMagic {
-	var m divMagic
-
-	// If the size is a power of two, heapBitsForObject can divide even faster by masking.
-	// Compute this mask.
-	if d&(d-1) == 0 {
-		// It is a power of 2 (assuming dinptr != 1)
-		m.baseMask = ^(uintptr(d) - 1)
-	} else {
-		m.baseMask = 0
-	}
-
-	// Compute pre-shift by factoring power of 2 out of d.
-	for d&1 == 0 {
-		m.shift++
-		d >>= 1
-	}
-
-	// Compute largest k such that ⌈2^k / d⌉ fits in a 32-bit int.
-	// This is always a good enough approximation.
-	// We could use smaller k for some divisors but there's no point.
-	k := uint8(63)
-	d64 := uint64(d)
-	for ((1<<k)+d64-1)/d64 >= 1<<32 {
-		k--
-	}
-	m.mul = uint32(((1 << k) + d64 - 1) / d64) //  ⌈2^k / d⌉
-	m.shift2 = k
-
-	return m
-}
diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go
index 2d75d2f..b80ab11 100644
--- a/src/runtime/mstats.go
+++ b/src/runtime/mstats.go
@@ -14,6 +14,13 @@ import (
 
 // Statistics.
 // If you edit this structure, also edit type MemStats below.
+// Their layouts must match exactly.
+//
+// For detailed descriptions see the documentation for MemStats.
+// Fields that differ from MemStats are further documented here.
+//
+// Many of these fields are updated on the fly, while others are only
+// updated when updatememstats is called.
 type mstats struct {
 	// General statistics.
 	alloc       uint64 // bytes allocated and not yet freed
@@ -24,18 +31,36 @@ type mstats struct {
 	nfree       uint64 // number of frees
 
 	// Statistics about malloc heap.
-	// protected by mheap.lock
+	// Protected by mheap.lock
+	//
+	// In mstats, heap_sys and heap_inuse includes stack memory,
+	// while in MemStats stack memory is separated out from the
+	// heap stats.
 	heap_alloc    uint64 // bytes allocated and not yet freed (same as alloc above)
-	heap_sys      uint64 // bytes obtained from system
+	heap_sys      uint64 // virtual address space obtained from system
 	heap_idle     uint64 // bytes in idle spans
 	heap_inuse    uint64 // bytes in non-idle spans
 	heap_released uint64 // bytes released to the os
 	heap_objects  uint64 // total number of allocated objects
 
+	// TODO(austin): heap_released is both useless and inaccurate
+	// in its current form. It's useless because, from the user's
+	// and OS's perspectives, there's no difference between a page
+	// that has not yet been faulted in and a page that has been
+	// released back to the OS. We could fix this by considering
+	// newly mapped spans to be "released". It's inaccurate
+	// because when we split a large span for allocation, we
+	// "unrelease" all pages in the large span and not just the
+	// ones we split off for use. This is trickier to fix because
+	// we currently don't know which pages of a span we've
+	// released. We could fix it by separating "free" and
+	// "released" spans, but then we have to allocate from runs of
+	// free and released spans.
+
 	// Statistics about allocation of low-level fixed-size structures.
 	// Protected by FixAlloc locks.
-	stacks_inuse uint64 // this number is included in heap_inuse above
-	stacks_sys   uint64 // always 0 in mstats
+	stacks_inuse uint64 // this number is included in heap_inuse above; differs from MemStats.StackInuse
+	stacks_sys   uint64 // only counts newosproc0 stack in mstats; differs from MemStats.StackSys
 	mspan_inuse  uint64 // mspan structures
 	mspan_sys    uint64
 	mcache_inuse uint64 // mcache structures
@@ -46,7 +71,7 @@ type mstats struct {
 
 	// Statistics about garbage collector.
 	// Protected by mheap or stopping the world during GC.
-	next_gc         uint64 // next gc (in heap_live time)
+	next_gc         uint64 // goal heap_live for when next GC ends; ^0 if disabled
 	last_gc         uint64 // last gc (in absolute time)
 	pause_total_ns  uint64
 	pause_ns        [256]uint64 // circular buffer of recent gc pause lengths
@@ -64,10 +89,19 @@ type mstats struct {
 		nfree   uint64
 	}
 
-	// Statistics below here are not exported to Go directly.
+	// Statistics below here are not exported to MemStats directly.
 
 	tinyallocs uint64 // number of tiny allocations that didn't cause actual allocation; not exported to go directly
 
+	// gc_trigger is the heap size that triggers marking.
+	//
+	// When heap_live ≥ gc_trigger, the mark phase will start.
+	// This is also the heap size by which proportional sweeping
+	// must be complete.
+	gc_trigger uint64
+
+	_ uint32 // force 8-byte alignment of heap_live and prevent an alignment check crash on MIPS32.
+
 	// heap_live is the number of bytes considered live by the GC.
 	// That is: retained by the most recent GC plus allocated
 	// since then. heap_live <= heap_alloc, since heap_alloc
@@ -104,10 +138,6 @@ type mstats struct {
 	// unlike heap_live, heap_marked does not change until the
 	// next mark termination.
 	heap_marked uint64
-
-	// heap_reachable is an estimate of the reachable heap bytes
-	// at the end of the previous GC.
-	heap_reachable uint64
 }
 
 var memstats mstats
@@ -115,47 +145,254 @@ var memstats mstats
 // A MemStats records statistics about the memory allocator.
 type MemStats struct {
 	// General statistics.
-	Alloc      uint64 // bytes allocated and not yet freed
-	TotalAlloc uint64 // bytes allocated (even if freed)
-	Sys        uint64 // bytes obtained from system (sum of XxxSys below)
-	Lookups    uint64 // number of pointer lookups
-	Mallocs    uint64 // number of mallocs
-	Frees      uint64 // number of frees
-
-	// Main allocation heap statistics.
-	HeapAlloc    uint64 // bytes allocated and not yet freed (same as Alloc above)
-	HeapSys      uint64 // bytes obtained from system
-	HeapIdle     uint64 // bytes in idle spans
-	HeapInuse    uint64 // bytes in non-idle span
-	HeapReleased uint64 // bytes released to the OS
-	HeapObjects  uint64 // total number of allocated objects
-
-	// Low-level fixed-size structure allocator statistics.
-	//	Inuse is bytes used now.
-	//	Sys is bytes obtained from system.
-	StackInuse  uint64 // bytes used by stack allocator
-	StackSys    uint64
-	MSpanInuse  uint64 // mspan structures
-	MSpanSys    uint64
-	MCacheInuse uint64 // mcache structures
-	MCacheSys   uint64
-	BuckHashSys uint64 // profiling bucket hash table
-	GCSys       uint64 // GC metadata
-	OtherSys    uint64 // other system allocations
+
+	// Alloc is bytes of allocated heap objects.
+	//
+	// This is the same as HeapAlloc (see below).
+	Alloc uint64
+
+	// TotalAlloc is cumulative bytes allocated for heap objects.
+	//
+	// TotalAlloc increases as heap objects are allocated, but
+	// unlike Alloc and HeapAlloc, it does not decrease when
+	// objects are freed.
+	TotalAlloc uint64
+
+	// Sys is the total bytes of memory obtained from the OS.
+	//
+	// Sys is the sum of the XSys fields below. Sys measures the
+	// virtual address space reserved by the Go runtime for the
+	// heap, stacks, and other internal data structures. It's
+	// likely that not all of the virtual address space is backed
+	// by physical memory at any given moment, though in general
+	// it all was at some point.
+	Sys uint64
+
+	// Lookups is the number of pointer lookups performed by the
+	// runtime.
+	//
+	// This is primarily useful for debugging runtime internals.
+	Lookups uint64
+
+	// Mallocs is the cumulative count of heap objects allocated.
+	Mallocs uint64
+
+	// Frees is the cumulative count of heap objects freed.
+	Frees uint64
+
+	// Heap memory statistics.
+	//
+	// Interpreting the heap statistics requires some knowledge of
+	// how Go organizes memory. Go divides the virtual address
+	// space of the heap into "spans", which are contiguous
+	// regions of memory 8K or larger. A span may be in one of
+	// three states:
+	//
+	// An "idle" span contains no objects or other data. The
+	// physical memory backing an idle span can be released back
+	// to the OS (but the virtual address space never is), or it
+	// can be converted into an "in use" or "stack" span.
+	//
+	// An "in use" span contains at least one heap object and may
+	// have free space available to allocate more heap objects.
+	//
+	// A "stack" span is used for goroutine stacks. Stack spans
+	// are not considered part of the heap. A span can change
+	// between heap and stack memory; it is never used for both
+	// simultaneously.
+
+	// HeapAlloc is bytes of allocated heap objects.
+	//
+	// "Allocated" heap objects include all reachable objects, as
+	// well as unreachable objects that the garbage collector has
+	// not yet freed. Specifically, HeapAlloc increases as heap
+	// objects are allocated and decreases as the heap is swept
+	// and unreachable objects are freed. Sweeping occurs
+	// incrementally between GC cycles, so these two processes
+	// occur simultaneously, and as a result HeapAlloc tends to
+	// change smoothly (in contrast with the sawtooth that is
+	// typical of stop-the-world garbage collectors).
+	HeapAlloc uint64
+
+	// HeapSys is bytes of heap memory obtained from the OS.
+	//
+	// HeapSys measures the amount of virtual address space
+	// reserved for the heap. This includes virtual address space
+	// that has been reserved but not yet used, which consumes no
+	// physical memory, but tends to be small, as well as virtual
+	// address space for which the physical memory has been
+	// returned to the OS after it became unused (see HeapReleased
+	// for a measure of the latter).
+	//
+	// HeapSys estimates the largest size the heap has had.
+	HeapSys uint64
+
+	// HeapIdle is bytes in idle (unused) spans.
+	//
+	// Idle spans have no objects in them. These spans could be
+	// (and may already have been) returned to the OS, or they can
+	// be reused for heap allocations, or they can be reused as
+	// stack memory.
+	//
+	// HeapIdle minus HeapReleased estimates the amount of memory
+	// that could be returned to the OS, but is being retained by
+	// the runtime so it can grow the heap without requesting more
+	// memory from the OS. If this difference is significantly
+	// larger than the heap size, it indicates there was a recent
+	// transient spike in live heap size.
+	HeapIdle uint64
+
+	// HeapInuse is bytes in in-use spans.
+	//
+	// In-use spans have at least one object in them. These spans
+	// can only be used for other objects of roughly the same
+	// size.
+	//
+	// HeapInuse minus HeapAlloc esimates the amount of memory
+	// that has been dedicated to particular size classes, but is
+	// not currently being used. This is an upper bound on
+	// fragmentation, but in general this memory can be reused
+	// efficiently.
+	HeapInuse uint64
+
+	// HeapReleased is bytes of physical memory returned to the OS.
+	//
+	// This counts heap memory from idle spans that was returned
+	// to the OS and has not yet been reacquired for the heap.
+	HeapReleased uint64
+
+	// HeapObjects is the number of allocated heap objects.
+	//
+	// Like HeapAlloc, this increases as objects are allocated and
+	// decreases as the heap is swept and unreachable objects are
+	// freed.
+	HeapObjects uint64
+
+	// Stack memory statistics.
+	//
+	// Stacks are not considered part of the heap, but the runtime
+	// can reuse a span of heap memory for stack memory, and
+	// vice-versa.
+
+	// StackInuse is bytes in stack spans.
+	//
+	// In-use stack spans have at least one stack in them. These
+	// spans can only be used for other stacks of the same size.
+	//
+	// There is no StackIdle because unused stack spans are
+	// returned to the heap (and hence counted toward HeapIdle).
+	StackInuse uint64
+
+	// StackSys is bytes of stack memory obtained from the OS.
+	//
+	// StackSys is StackInuse, plus any memory obtained directly
+	// from the OS for OS thread stacks (which should be minimal).
+	StackSys uint64
+
+	// Off-heap memory statistics.
+	//
+	// The following statistics measure runtime-internal
+	// structures that are not allocated from heap memory (usually
+	// because they are part of implementing the heap). Unlike
+	// heap or stack memory, any memory allocated to these
+	// structures is dedicated to these structures.
+	//
+	// These are primarily useful for debugging runtime memory
+	// overheads.
+
+	// MSpanInuse is bytes of allocated mspan structures.
+	MSpanInuse uint64
+
+	// MSpanSys is bytes of memory obtained from the OS for mspan
+	// structures.
+	MSpanSys uint64
+
+	// MCacheInuse is bytes of allocated mcache structures.
+	MCacheInuse uint64
+
+	// MCacheSys is bytes of memory obtained from the OS for
+	// mcache structures.
+	MCacheSys uint64
+
+	// BuckHashSys is bytes of memory in profiling bucket hash tables.
+	BuckHashSys uint64
+
+	// GCSys is bytes of memory in garbage collection metadata.
+	GCSys uint64
+
+	// OtherSys is bytes of memory in miscellaneous off-heap
+	// runtime allocations.
+	OtherSys uint64
 
 	// Garbage collector statistics.
-	NextGC        uint64 // next collection will happen when HeapAlloc ≥ this amount
-	LastGC        uint64 // end time of last collection (nanoseconds since 1970)
-	PauseTotalNs  uint64
-	PauseNs       [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
-	PauseEnd      [256]uint64 // circular buffer of recent GC pause end times
-	NumGC         uint32
-	GCCPUFraction float64 // fraction of CPU time used by GC
-	EnableGC      bool
-	DebugGC       bool
-
-	// Per-size allocation statistics.
-	// 61 is NumSizeClasses in the C code.
+
+	// NextGC is the target heap size of the next GC cycle.
+	//
+	// The garbage collector's goal is to keep HeapAlloc ≤ NextGC.
+	// At the end of each GC cycle, the target for the next cycle
+	// is computed based on the amount of reachable data and the
+	// value of GOGC.
+	NextGC uint64
+
+	// LastGC is the time the last garbage collection finished, as
+	// nanoseconds since 1970 (the UNIX epoch).
+	LastGC uint64
+
+	// PauseTotalNs is the cumulative nanoseconds in GC
+	// stop-the-world pauses since the program started.
+	//
+	// During a stop-the-world pause, all goroutines are paused
+	// and only the garbage collector can run.
+	PauseTotalNs uint64
+
+	// PauseNs is a circular buffer of recent GC stop-the-world
+	// pause times in nanoseconds.
+	//
+	// The most recent pause is at PauseNs[(NumGC+255)%256]. In
+	// general, PauseNs[N%256] records the time paused in the most
+	// recent N%256th GC cycle. There may be multiple pauses per
+	// GC cycle; this is the sum of all pauses during a cycle.
+	PauseNs [256]uint64
+
+	// PauseEnd is a circular buffer of recent GC pause end times,
+	// as nanoseconds since 1970 (the UNIX epoch).
+	//
+	// This buffer is filled the same way as PauseNs. There may be
+	// multiple pauses per GC cycle; this records the end of the
+	// last pause in a cycle.
+	PauseEnd [256]uint64
+
+	// NumGC is the number of completed GC cycles.
+	NumGC uint32
+
+	// GCCPUFraction is the fraction of this program's available
+	// CPU time used by the GC since the program started.
+	//
+	// GCCPUFraction is expressed as a number between 0 and 1,
+	// where 0 means GC has consumed none of this program's CPU. A
+	// program's available CPU time is defined as the integral of
+	// GOMAXPROCS since the program started. That is, if
+	// GOMAXPROCS is 2 and a program has been running for 10
+	// seconds, its "available CPU" is 20 seconds. GCCPUFraction
+	// does not include CPU time used for write barrier activity.
+	//
+	// This is the same as the fraction of CPU reported by
+	// GODEBUG=gctrace=1.
+	GCCPUFraction float64
+
+	// EnableGC indicates that GC is enabled. It is always true,
+	// even if GOGC=off.
+	EnableGC bool
+
+	// DebugGC is currently unused.
+	DebugGC bool
+
+	// BySize reports per-size class allocation statistics.
+	//
+	// BySize[N] gives statistics for allocations of size S where
+	// BySize[N-1].Size < S ≤ BySize[N].Size.
+	//
+	// This does not report allocations larger than BySize[60].Size.
 	BySize [61]struct {
 		Size    uint32
 		Mallocs uint64
@@ -163,10 +400,11 @@ type MemStats struct {
 	}
 }
 
-// Size of the trailing by_size array differs between Go and C,
+// Size of the trailing by_size array differs between mstats and MemStats,
 // and all data after by_size is local to runtime, not exported.
-// NumSizeClasses was changed, but we cannot change Go struct because of backward compatibility.
-// sizeof_C_MStats is what C thinks about size of Go struct.
+// NumSizeClasses was changed, but we cannot change MemStats because of backward compatibility.
+// sizeof_C_MStats is the size of the prefix of mstats that
+// corresponds to MemStats. It should match Sizeof(MemStats{}).
 var sizeof_C_MStats = unsafe.Offsetof(memstats.by_size) + 61*unsafe.Sizeof(memstats.by_size[0])
 
 func init() {
@@ -175,9 +413,19 @@ func init() {
 		println(sizeof_C_MStats, unsafe.Sizeof(memStats))
 		throw("MStats vs MemStatsType size mismatch")
 	}
+
+	if unsafe.Offsetof(memstats.heap_live)%8 != 0 {
+		println(unsafe.Offsetof(memstats.heap_live))
+		throw("memstats.heap_live not aligned to 8 bytes")
+	}
 }
 
 // ReadMemStats populates m with memory allocator statistics.
+//
+// The returned memory allocator statistics are up to date as of the
+// call to ReadMemStats. This is in contrast with a heap profile,
+// which is a snapshot as of the most recently completed garbage
+// collection cycle.
 func ReadMemStats(m *MemStats) {
 	stopTheWorld("read mem stats")
 
@@ -191,8 +439,9 @@ func ReadMemStats(m *MemStats) {
 func readmemstats_m(stats *MemStats) {
 	updatememstats(nil)
 
-	// Size of the trailing by_size array differs between Go and C,
-	// NumSizeClasses was changed, but we cannot change Go struct because of backward compatibility.
+	// The size of the trailing by_size array differs between
+	// mstats and MemStats. NumSizeClasses was changed, but we
+	// cannot change MemStats because of backward compatibility.
 	memmove(unsafe.Pointer(stats), unsafe.Pointer(&memstats), sizeof_C_MStats)
 
 	// Stack numbers are part of the heap numbers, separate those out for user consumption
@@ -286,8 +535,7 @@ func updatememstats(stats *gcstats) {
 
 	// Scan all spans and count number of alive objects.
 	lock(&mheap_.lock)
-	for i := uint32(0); i < mheap_.nspan; i++ {
-		s := h_allspans[i]
+	for _, s := range mheap_.allspans {
 		if s.state != mSpanInUse {
 			continue
 		}
@@ -335,19 +583,32 @@ func cachestats() {
 	}
 }
 
+// flushmcache flushes the mcache of allp[i].
+//
+// The world must be stopped.
+//
+//go:nowritebarrier
+func flushmcache(i int) {
+	p := allp[i]
+	if p == nil {
+		return
+	}
+	c := p.mcache
+	if c == nil {
+		return
+	}
+	c.releaseAll()
+	stackcache_clear(c)
+}
+
+// flushallmcaches flushes the mcaches of all Ps.
+//
+// The world must be stopped.
+//
 //go:nowritebarrier
 func flushallmcaches() {
-	for i := 0; ; i++ {
-		p := allp[i]
-		if p == nil {
-			break
-		}
-		c := p.mcache
-		if c == nil {
-			continue
-		}
-		c.releaseAll()
-		stackcache_clear(c)
+	for i := 0; i < int(gomaxprocs); i++ {
+		flushmcache(i)
 	}
 }
 
diff --git a/src/runtime/mstkbar.go b/src/runtime/mstkbar.go
index 1bf9d57..4415559 100644
--- a/src/runtime/mstkbar.go
+++ b/src/runtime/mstkbar.go
@@ -148,6 +148,10 @@ var firstStackBarrierOffset = 1024
 // gcMaxStackBarriers returns the maximum number of stack barriers
 // that can be installed in a stack of stackSize bytes.
 func gcMaxStackBarriers(stackSize int) (n int) {
+	if debug.gcstackbarrieroff > 0 {
+		return 0
+	}
+
 	if firstStackBarrierOffset == 0 {
 		// Special debugging case for inserting stack barriers
 		// at every frame. Steal half of the stack for the
diff --git a/src/runtime/net_plan9.go b/src/runtime/net_plan9.go
new file mode 100644
index 0000000..10fd089
--- /dev/null
+++ b/src/runtime/net_plan9.go
@@ -0,0 +1,29 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+import (
+	_ "unsafe"
+)
+
+//go:linkname runtime_ignoreHangup net.runtime_ignoreHangup
+func runtime_ignoreHangup() {
+	getg().m.ignoreHangup = true
+}
+
+//go:linkname runtime_unignoreHangup net.runtime_unignoreHangup
+func runtime_unignoreHangup(sig string) {
+	getg().m.ignoreHangup = false
+}
+
+func ignoredNote(note *byte) bool {
+	if note == nil {
+		return false
+	}
+	if gostringnocopy(note) != "hangup" {
+		return false
+	}
+	return getg().m.ignoreHangup
+}
diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go
index 2ef248d..10a3c88 100644
--- a/src/runtime/netpoll.go
+++ b/src/runtime/netpoll.go
@@ -39,6 +39,10 @@ const (
 const pollBlockSize = 4 * 1024
 
 // Network poller descriptor.
+//
+// No heap pointers.
+//
+//go:notinheap
 type pollDesc struct {
 	link *pollDesc // in pollcache, protected by pollcache.lock
 
diff --git a/src/runtime/noasm.go b/src/runtime/noasm.go
index 0a8f9e6..586836c 100644
--- a/src/runtime/noasm.go
+++ b/src/runtime/noasm.go
@@ -3,6 +3,7 @@
 // license that can be found in the LICENSE file.
 
 // Routines that are implemented in assembly in asm_{amd64,386,arm,arm64,ppc64x,s390x}.s
+// These routines have corresponding stubs in stubs_asm.go.
 
 // +build mips64 mips64le
 
diff --git a/src/runtime/os3_plan9.go b/src/runtime/os3_plan9.go
index aff1d05..26b4acd 100644
--- a/src/runtime/os3_plan9.go
+++ b/src/runtime/os3_plan9.go
@@ -100,6 +100,9 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int {
 		return _NCONT
 	}
 	if flags&_SigNotify != 0 {
+		if ignoredNote(note) {
+			return _NCONT
+		}
 		if sendNote(note) {
 			return _NCONT
 		}
diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go
index 9368e0d..067fb3b 100644
--- a/src/runtime/os3_solaris.go
+++ b/src/runtime/os3_solaris.go
@@ -127,11 +127,20 @@ func getncpu() int32 {
 	return n
 }
 
+func getPageSize() uintptr {
+	n := int32(sysconf(__SC_PAGESIZE))
+	if n <= 0 {
+		return 0
+	}
+	return uintptr(n)
+}
+
 func osinit() {
 	ncpu = getncpu()
+	physPageSize = getPageSize()
 }
 
-func tstart_sysvicall()
+func tstart_sysvicall(newm *m) uint32
 
 // May run with m.p==nil, so write barriers are not allowed.
 //go:nowritebarrier
@@ -195,58 +204,17 @@ func mpreinit(mp *m) {
 
 func miniterrno()
 
-//go:nosplit
-func msigsave(mp *m) {
-	sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
-	sigprocmask(_SIG_SETMASK, &sigmask, nil)
-}
-
-//go:nosplit
-func sigblock() {
-	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
-}
-
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, cannot allocate memory.
 func minit() {
-	_g_ := getg()
 	asmcgocall(unsafe.Pointer(funcPC(miniterrno)), unsafe.Pointer(&libc____errno))
-	// Initialize signal handling
-	var st sigaltstackt
-	sigaltstack(nil, &st)
-	if st.ss_flags&_SS_DISABLE != 0 {
-		signalstack(&_g_.m.gsignal.stack)
-		_g_.m.newSigstack = true
-	} else {
-		// Use existing signal stack.
-		stsp := uintptr(unsafe.Pointer(st.ss_sp))
-		_g_.m.gsignal.stack.lo = stsp
-		_g_.m.gsignal.stack.hi = stsp + uintptr(st.ss_size)
-		_g_.m.gsignal.stackguard0 = stsp + _StackGuard
-		_g_.m.gsignal.stackguard1 = stsp + _StackGuard
-		_g_.m.gsignal.stackAlloc = uintptr(st.ss_size)
-		_g_.m.newSigstack = false
-	}
 
-	// restore signal mask from m.sigmask and unblock essential signals
-	nmask := _g_.m.sigmask
-	for i := range sigtable {
-		if sigtable[i].flags&_SigUnblock != 0 {
-			nmask.__sigbits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
-		}
-	}
-	sigprocmask(_SIG_SETMASK, &nmask, nil)
+	minitSignals()
 }
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
-	if getg().m.newSigstack {
-		signalstack(nil)
-	}
+	unminitSignals()
 }
 
 func memlimit() uintptr {
@@ -284,14 +252,10 @@ func sigtramp()
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
+func setsig(i uint32, fn uintptr) {
 	var sa sigactiont
 
-	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
-	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
-	if restart {
-		sa.sa_flags |= _SA_RESTART
-	}
+	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
 	sa.sa_mask = sigset_all
 	if fn == funcPC(sighandler) {
 		fn = funcPC(sigtramp)
@@ -302,11 +266,10 @@ func setsig(i int32, fn uintptr, restart bool) {
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsigstack(i int32) {
+func setsigstack(i uint32) {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
-	handler := *((*uintptr)(unsafe.Pointer(&sa._funcptr)))
-	if handler == 0 || handler == _SIG_DFL || handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 {
+	if sa.sa_flags&_SA_ONSTACK != 0 {
 		return
 	}
 	sa.sa_flags |= _SA_ONSTACK
@@ -315,40 +278,29 @@ func setsigstack(i int32) {
 
 //go:nosplit
 //go:nowritebarrierrec
-func getsig(i int32) uintptr {
+func getsig(i uint32) uintptr {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
-	if *((*uintptr)(unsafe.Pointer(&sa._funcptr))) == funcPC(sigtramp) {
-		return funcPC(sighandler)
-	}
 	return *((*uintptr)(unsafe.Pointer(&sa._funcptr)))
 }
 
+// setSignaltstackSP sets the ss_sp field of a stackt.
 //go:nosplit
-func signalstack(s *stack) {
-	var st sigaltstackt
-	if s == nil {
-		st.ss_flags = _SS_DISABLE
-	} else {
-		st.ss_sp = (*byte)(unsafe.Pointer(s.lo))
-		st.ss_size = uint64(s.hi - s.lo)
-		st.ss_flags = 0
-	}
-	sigaltstack(&st, nil)
+func setSignalstackSP(s *stackt, sp uintptr) {
+	*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func updatesigmask(m sigmask) {
-	var mask sigset
-	copy(mask.__sigbits[:], m[:])
-	sigprocmask(_SIG_SETMASK, &mask, nil)
+func sigaddset(mask *sigset, i int) {
+	mask.__sigbits[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
+}
+
+func sigdelset(mask *sigset, i int) {
+	mask.__sigbits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
 }
 
-func unblocksig(sig int32) {
-	var mask sigset
-	mask.__sigbits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
-	sigprocmask(_SIG_UNBLOCK, &mask, nil)
+func (c *sigctxt) fixsigcode(sig uint32) {
 }
 
 //go:nosplit
@@ -365,7 +317,7 @@ func semacreate(mp *m) {
 	// here because it could cause a deadlock.
 	_g_.m.libcall.fn = uintptr(unsafe.Pointer(&libc_malloc))
 	_g_.m.libcall.n = 1
-	memclr(unsafe.Pointer(&_g_.m.scratch), uintptr(len(_g_.m.scratch.v)))
+	_g_.m.scratch = mscratch{}
 	_g_.m.scratch.v[0] = unsafe.Sizeof(*sem)
 	_g_.m.libcall.args = uintptr(unsafe.Pointer(&_g_.m.scratch))
 	asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&_g_.m.libcall))
@@ -385,7 +337,7 @@ func semasleep(ns int64) int32 {
 
 		_m_.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_reltimedwait_np))
 		_m_.libcall.n = 2
-		memclr(unsafe.Pointer(&_m_.scratch), uintptr(len(_m_.scratch.v)))
+		_m_.scratch = mscratch{}
 		_m_.scratch.v[0] = _m_.waitsema
 		_m_.scratch.v[1] = uintptr(unsafe.Pointer(&_m_.ts))
 		_m_.libcall.args = uintptr(unsafe.Pointer(&_m_.scratch))
@@ -401,7 +353,7 @@ func semasleep(ns int64) int32 {
 	for {
 		_m_.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_wait))
 		_m_.libcall.n = 1
-		memclr(unsafe.Pointer(&_m_.scratch), uintptr(len(_m_.scratch.v)))
+		_m_.scratch = mscratch{}
 		_m_.scratch.v[0] = _m_.waitsema
 		_m_.libcall.args = uintptr(unsafe.Pointer(&_m_.scratch))
 		asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&_m_.libcall))
@@ -505,11 +457,11 @@ func pthread_create(thread *pthread, attr *pthreadattr, fn uintptr, arg unsafe.P
 
 //go:nosplit
 //go:nowritebarrierrec
-func raise(sig int32) /* int32 */ {
+func raise(sig uint32) /* int32 */ {
 	sysvicall1(&libc_raise, uintptr(sig))
 }
 
-func raiseproc(sig int32) /* int32 */ {
+func raiseproc(sig uint32) /* int32 */ {
 	pid := sysvicall0(&libc_getpid)
 	sysvicall2(&libc_kill, pid, uintptr(sig))
 }
@@ -545,13 +497,13 @@ func setitimer(which int32, value *itimerval, ovalue *itimerval) /* int32 */ {
 
 //go:nosplit
 //go:nowritebarrierrec
-func sigaction(sig int32, act *sigactiont, oact *sigactiont) /* int32 */ {
+func sigaction(sig uint32, act *sigactiont, oact *sigactiont) /* int32 */ {
 	sysvicall3(&libc_sigaction, uintptr(sig), uintptr(unsafe.Pointer(act)), uintptr(unsafe.Pointer(oact)))
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func sigaltstack(ss *sigaltstackt, oss *sigaltstackt) /* int32 */ {
+func sigaltstack(ss *stackt, oss *stackt) /* int32 */ {
 	sysvicall2(&libc_sigaltstack, uintptr(unsafe.Pointer(ss)), uintptr(unsafe.Pointer(oss)))
 }
 
@@ -565,7 +517,7 @@ func sysconf(name int32) int64 {
 	return int64(sysvicall1(&libc_sysconf, uintptr(name)))
 }
 
-func usleep1(uint32)
+func usleep1(usec uint32)
 
 //go:nosplit
 func usleep(µs uint32) {
diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go
index a0e3d8e..1528167 100644
--- a/src/runtime/os_darwin.go
+++ b/src/runtime/os_darwin.go
@@ -50,11 +50,19 @@ func osinit() {
 	// can look at the environment first.
 
 	ncpu = getncpu()
+
+	physPageSize = getPageSize()
 }
 
+const (
+	_CTL_HW      = 6
+	_HW_NCPU     = 3
+	_HW_PAGESIZE = 7
+)
+
 func getncpu() int32 {
 	// Use sysctl to fetch hw.ncpu.
-	mib := [2]uint32{6, 3}
+	mib := [2]uint32{_CTL_HW, _HW_NCPU}
 	out := uint32(0)
 	nout := unsafe.Sizeof(out)
 	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
@@ -64,6 +72,18 @@ func getncpu() int32 {
 	return 1
 }
 
+func getPageSize() uintptr {
+	// Use sysctl to fetch hw.pagesize.
+	mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
+	out := uint32(0)
+	nout := unsafe.Sizeof(out)
+	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
+	if ret >= 0 && int32(out) > 0 {
+		return uintptr(out)
+	}
+	return 0
+}
+
 var urandom_dev = []byte("/dev/urandom\x00")
 
 //go:nosplit
@@ -153,64 +173,22 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
-//go:nosplit
-func msigsave(mp *m) {
-	sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
-	sigprocmask(_SIG_SETMASK, &sigmask, nil)
-}
-
-//go:nosplit
-func sigblock() {
-	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
-}
-
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, cannot allocate memory.
 func minit() {
-	// Initialize signal handling.
-	_g_ := getg()
-
 	// The alternate signal stack is buggy on arm and arm64.
 	// The signal handler handles it directly.
 	// The sigaltstack assembly function does nothing.
 	if GOARCH != "arm" && GOARCH != "arm64" {
-		var st stackt
-		sigaltstack(nil, &st)
-		if st.ss_flags&_SS_DISABLE != 0 {
-			signalstack(&_g_.m.gsignal.stack)
-			_g_.m.newSigstack = true
-		} else {
-			// Use existing signal stack.
-			stsp := uintptr(unsafe.Pointer(st.ss_sp))
-			_g_.m.gsignal.stack.lo = stsp
-			_g_.m.gsignal.stack.hi = stsp + st.ss_size
-			_g_.m.gsignal.stackguard0 = stsp + _StackGuard
-			_g_.m.gsignal.stackguard1 = stsp + _StackGuard
-			_g_.m.gsignal.stackAlloc = st.ss_size
-			_g_.m.newSigstack = false
-		}
+		minitSignalStack()
 	}
-
-	// restore signal mask from m.sigmask and unblock essential signals
-	nmask := _g_.m.sigmask
-	for i := range sigtable {
-		if sigtable[i].flags&_SigUnblock != 0 {
-			nmask &^= 1 << (uint32(i) - 1)
-		}
-	}
-	sigprocmask(_SIG_SETMASK, &nmask, nil)
+	minitSignalMask()
 }
 
 // Called from dropm to undo the effect of an minit.
 //go:nosplit
 func unminit() {
-	if getg().m.newSigstack {
-		signalstack(nil)
-	}
+	unminitSignals()
 }
 
 // Mach IPC, to get at semaphores
@@ -436,7 +414,10 @@ func semasleep1(ns int64) int32 {
 		if r == 0 {
 			break
 		}
-		if r == _KERN_ABORTED { // interrupted
+		// Note: We don't know how this call (with no timeout) can get _KERN_OPERATION_TIMED_OUT,
+		// but it does reliably, though at a very low rate, on OS X 10.8, 10.9, 10.10, and 10.11.
+		// See golang.org/issue/17161.
+		if r == _KERN_ABORTED || r == _KERN_OPERATION_TIMED_OUT { // interrupted
 			continue
 		}
 		macherror(r, "semaphore_wait")
@@ -495,7 +476,7 @@ const (
 )
 
 //go:noescape
-func sigprocmask(how uint32, new, old *sigset)
+func sigprocmask(how int32, new, old *sigset)
 
 //go:noescape
 func sigaction(mode uint32, new *sigactiont, old *usigactiont)
@@ -503,13 +484,15 @@ func sigaction(mode uint32, new *sigactiont, old *usigactiont)
 //go:noescape
 func sigaltstack(new, old *stackt)
 
-func sigtramp()
+// darwin/arm64 uses registers instead of stack-based arguments.
+// TODO: does this matter?
+func sigtramp(fn uintptr, infostyle, sig uint32, info *siginfo, ctx unsafe.Pointer)
 
 //go:noescape
 func setitimer(mode int32, new, old *itimerval)
 
-func raise(sig int32)
-func raiseproc(int32)
+func raise(sig uint32)
+func raiseproc(sig uint32)
 
 //extern SigTabTT runtime·sigtab[];
 
@@ -519,25 +502,22 @@ var sigset_all = ^sigset(0)
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
+func setsig(i uint32, fn uintptr) {
 	var sa sigactiont
-	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
-	if restart {
-		sa.sa_flags |= _SA_RESTART
-	}
+	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
 	sa.sa_mask = ^uint32(0)
 	sa.sa_tramp = unsafe.Pointer(funcPC(sigtramp)) // runtime·sigtramp's job is to call into real handler
 	*(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = fn
-	sigaction(uint32(i), &sa, nil)
+	sigaction(i, &sa, nil)
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsigstack(i int32) {
+func setsigstack(i uint32) {
 	var osa usigactiont
-	sigaction(uint32(i), nil, &osa)
+	sigaction(i, nil, &osa)
 	handler := *(*uintptr)(unsafe.Pointer(&osa.__sigaction_u))
-	if handler == 0 || handler == _SIG_DFL || handler == _SIG_IGN || osa.sa_flags&_SA_ONSTACK != 0 {
+	if osa.sa_flags&_SA_ONSTACK != 0 {
 		return
 	}
 	var sa sigactiont
@@ -545,38 +525,47 @@ func setsigstack(i int32) {
 	sa.sa_tramp = unsafe.Pointer(funcPC(sigtramp))
 	sa.sa_mask = osa.sa_mask
 	sa.sa_flags = osa.sa_flags | _SA_ONSTACK
-	sigaction(uint32(i), &sa, nil)
+	sigaction(i, &sa, nil)
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func getsig(i int32) uintptr {
+func getsig(i uint32) uintptr {
 	var sa usigactiont
-	sigaction(uint32(i), nil, &sa)
+	sigaction(i, nil, &sa)
 	return *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u))
 }
 
+// setSignaltstackSP sets the ss_sp field of a stackt.
 //go:nosplit
-func signalstack(s *stack) {
-	var st stackt
-	if s == nil {
-		st.ss_flags = _SS_DISABLE
-	} else {
-		st.ss_sp = (*byte)(unsafe.Pointer(s.lo))
-		st.ss_size = s.hi - s.lo
-		st.ss_flags = 0
-	}
-	sigaltstack(&st, nil)
+func setSignalstackSP(s *stackt, sp uintptr) {
+	*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func updatesigmask(m sigmask) {
-	s := sigset(m[0])
-	sigprocmask(_SIG_SETMASK, &s, nil)
+func sigaddset(mask *sigset, i int) {
+	*mask |= 1 << (uint32(i) - 1)
+}
+
+func sigdelset(mask *sigset, i int) {
+	*mask &^= 1 << (uint32(i) - 1)
 }
 
-func unblocksig(sig int32) {
-	mask := sigset(1) << (uint32(sig) - 1)
-	sigprocmask(_SIG_UNBLOCK, &mask, nil)
+//go:linkname executablePath os.executablePath
+var executablePath string
+
+func sysargs(argc int32, argv **byte) {
+	// skip over argv, envv and the first string will be the path
+	n := argc + 1
+	for argv_index(argv, n) != nil {
+		n++
+	}
+	executablePath = gostringnocopy(argv_index(argv, n+1))
+
+	// strip "executable_path=" prefix if available, it's added after OS X 10.11.
+	const prefix = "executable_path="
+	if len(executablePath) > len(prefix) && executablePath[:len(prefix)] == prefix {
+		executablePath = executablePath[len(prefix):]
+	}
 }
diff --git a/src/runtime/os_darwin_arm.go b/src/runtime/os_darwin_arm.go
index 1ccc959..ee1bd17 100644
--- a/src/runtime/os_darwin_arm.go
+++ b/src/runtime/os_darwin_arm.go
@@ -17,8 +17,8 @@ func checkgoarm() {
 
 //go:nosplit
 func cputicks() int64 {
-	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
+	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
 	// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-	// TODO: need more entropy to better seed fastrand1.
+	// TODO: need more entropy to better seed fastrand.
 	return nanotime()
 }
diff --git a/src/runtime/os_darwin_arm64.go b/src/runtime/os_darwin_arm64.go
index 4d35af9..8de132d 100644
--- a/src/runtime/os_darwin_arm64.go
+++ b/src/runtime/os_darwin_arm64.go
@@ -6,8 +6,8 @@ package runtime
 
 //go:nosplit
 func cputicks() int64 {
-	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
+	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
 	// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-	// TODO: need more entropy to better seed fastrand1.
+	// TODO: need more entropy to better seed fastrand.
 	return nanotime()
 }
diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go
index 85d4aad..4e50679 100644
--- a/src/runtime/os_dragonfly.go
+++ b/src/runtime/os_dragonfly.go
@@ -22,10 +22,10 @@ type mOS struct{}
 func lwp_create(param *lwpparams) int32
 
 //go:noescape
-func sigaltstack(new, old *sigaltstackt)
+func sigaltstack(new, old *stackt)
 
 //go:noescape
-func sigaction(sig int32, new, old *sigactiont)
+func sigaction(sig uint32, new, old *sigactiont)
 
 //go:noescape
 func sigprocmask(how int32, new, old *sigset)
@@ -39,8 +39,8 @@ func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, nds
 //go:noescape
 func getrlimit(kind int32, limit unsafe.Pointer) int32
 
-func raise(sig int32)
-func raiseproc(sig int32)
+func raise(sig uint32)
+func raiseproc(sig uint32)
 
 //go:noescape
 func sys_umtx_sleep(addr *uint32, val, timeout int32) int32
@@ -54,8 +54,9 @@ const stackSystem = 0
 
 // From DragonFly's <sys/sysctl.h>
 const (
-	_CTL_HW  = 6
-	_HW_NCPU = 3
+	_CTL_HW      = 6
+	_HW_NCPU     = 3
+	_HW_PAGESIZE = 7
 )
 
 var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
@@ -71,6 +72,17 @@ func getncpu() int32 {
 	return 1
 }
 
+func getPageSize() uintptr {
+	mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
+	out := uint32(0)
+	nout := unsafe.Sizeof(out)
+	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
+	if ret >= 0 {
+		return uintptr(out)
+	}
+	return 0
+}
+
 //go:nosplit
 func futexsleep(addr *uint32, val uint32, ns int64) {
 	systemstack(func() {
@@ -141,6 +153,7 @@ func newosproc(mp *m, stk unsafe.Pointer) {
 
 func osinit() {
 	ncpu = getncpu()
+	physPageSize = getPageSize()
 }
 
 var urandom_dev = []byte("/dev/urandom\x00")
@@ -164,57 +177,20 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
-//go:nosplit
-func msigsave(mp *m) {
-	sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
-	sigprocmask(_SIG_SETMASK, &sigmask, nil)
-}
-
-//go:nosplit
-func sigblock() {
-	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
-}
-
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, cannot allocate memory.
 func minit() {
-	_g_ := getg()
-
 	// m.procid is a uint64, but lwp_start writes an int32. Fix it up.
+	_g_ := getg()
 	_g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid)))
 
-	// Initialize signal handling.
-
-	// On DragonFly a thread created by pthread_create inherits
-	// the signal stack of the creating thread. We always create
-	// a new signal stack here, to avoid having two Go threads
-	// using the same signal stack. This breaks the case of a
-	// thread created in C that calls sigaltstack and then calls a
-	// Go function, because we will lose track of the C code's
-	// sigaltstack, but it's the best we can do.
-	signalstack(&_g_.m.gsignal.stack)
-	_g_.m.newSigstack = true
-
-	// restore signal mask from m.sigmask and unblock essential signals
-	nmask := _g_.m.sigmask
-	for i := range sigtable {
-		if sigtable[i].flags&_SigUnblock != 0 {
-			nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
-		}
-	}
-	sigprocmask(_SIG_SETMASK, &nmask, nil)
+	minitSignals()
 }
 
 // Called from dropm to undo the effect of an minit.
 //go:nosplit
 func unminit() {
-	if getg().m.newSigstack {
-		signalstack(nil)
-	}
+	unminitSignals()
 }
 
 func memlimit() uintptr {
@@ -258,12 +234,9 @@ type sigactiont struct {
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
+func setsig(i uint32, fn uintptr) {
 	var sa sigactiont
-	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
-	if restart {
-		sa.sa_flags |= _SA_RESTART
-	}
+	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
 	sa.sa_mask = sigset_all
 	if fn == funcPC(sighandler) {
 		fn = funcPC(sigtramp)
@@ -274,44 +247,33 @@ func setsig(i int32, fn uintptr, restart bool) {
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsigstack(i int32) {
+func setsigstack(i uint32) {
 	throw("setsigstack")
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func getsig(i int32) uintptr {
+func getsig(i uint32) uintptr {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
-	if sa.sa_sigaction == funcPC(sigtramp) {
-		return funcPC(sighandler)
-	}
 	return sa.sa_sigaction
 }
 
+// setSignaltstackSP sets the ss_sp field of a stackt.
 //go:nosplit
-func signalstack(s *stack) {
-	var st sigaltstackt
-	if s == nil {
-		st.ss_flags = _SS_DISABLE
-	} else {
-		st.ss_sp = s.lo
-		st.ss_size = s.hi - s.lo
-		st.ss_flags = 0
-	}
-	sigaltstack(&st, nil)
+func setSignalstackSP(s *stackt, sp uintptr) {
+	s.ss_sp = sp
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func updatesigmask(m sigmask) {
-	var mask sigset
-	copy(mask.__bits[:], m[:])
-	sigprocmask(_SIG_SETMASK, &mask, nil)
+func sigaddset(mask *sigset, i int) {
+	mask.__bits[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
+}
+
+func sigdelset(mask *sigset, i int) {
+	mask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
 }
 
-func unblocksig(sig int32) {
-	var mask sigset
-	mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
-	sigprocmask(_SIG_UNBLOCK, &mask, nil)
+func (c *sigctxt) fixsigcode(sig uint32) {
 }
diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go
index 0e09c60..35ed026 100644
--- a/src/runtime/os_freebsd.go
+++ b/src/runtime/os_freebsd.go
@@ -18,7 +18,7 @@ func thr_new(param *thrparam, size int32)
 func sigaltstack(new, old *stackt)
 
 //go:noescape
-func sigaction(sig int32, new, old *sigactiont)
+func sigaction(sig uint32, new, old *sigactiont)
 
 //go:noescape
 func sigprocmask(how int32, new, old *sigset)
@@ -31,8 +31,8 @@ func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, nds
 
 //go:noescape
 func getrlimit(kind int32, limit unsafe.Pointer) int32
-func raise(sig int32)
-func raiseproc(sig int32)
+func raise(sig uint32)
+func raiseproc(sig uint32)
 
 //go:noescape
 func sys_umtx_op(addr *uint32, mode int32, val uint32, uaddr1 uintptr, ut *umtx_time) int32
@@ -41,8 +41,9 @@ func osyield()
 
 // From FreeBSD's <sys/sysctl.h>
 const (
-	_CTL_HW  = 6
-	_HW_NCPU = 3
+	_CTL_HW      = 6
+	_HW_NCPU     = 3
+	_HW_PAGESIZE = 7
 )
 
 var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
@@ -58,6 +59,17 @@ func getncpu() int32 {
 	return 1
 }
 
+func getPageSize() uintptr {
+	mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
+	out := uint32(0)
+	nout := unsafe.Sizeof(out)
+	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
+	if ret >= 0 {
+		return uintptr(out)
+	}
+	return 0
+}
+
 // FreeBSD's umtx_op syscall is effectively the same as Linux's futex, and
 // thus the code is largely similar. See Linux implementation
 // and lock_futex.go for comments.
@@ -128,6 +140,7 @@ func newosproc(mp *m, stk unsafe.Pointer) {
 
 func osinit() {
 	ncpu = getncpu()
+	physPageSize = getPageSize()
 }
 
 var urandom_dev = []byte("/dev/urandom\x00")
@@ -151,65 +164,23 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
-//go:nosplit
-func msigsave(mp *m) {
-	sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
-	sigprocmask(_SIG_SETMASK, &sigmask, nil)
-}
-
-//go:nosplit
-func sigblock() {
-	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
-}
-
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, cannot allocate memory.
 func minit() {
-	_g_ := getg()
-
 	// m.procid is a uint64, but thr_new writes a uint32 on 32-bit systems.
 	// Fix it up. (Only matters on big-endian, but be clean anyway.)
 	if sys.PtrSize == 4 {
+		_g_ := getg()
 		_g_.m.procid = uint64(*(*uint32)(unsafe.Pointer(&_g_.m.procid)))
 	}
 
-	// Initialize signal handling.
-	var st stackt
-	sigaltstack(nil, &st)
-	if st.ss_flags&_SS_DISABLE != 0 {
-		signalstack(&_g_.m.gsignal.stack)
-		_g_.m.newSigstack = true
-	} else {
-		// Use existing signal stack.
-		stsp := uintptr(unsafe.Pointer(st.ss_sp))
-		_g_.m.gsignal.stack.lo = stsp
-		_g_.m.gsignal.stack.hi = stsp + st.ss_size
-		_g_.m.gsignal.stackguard0 = stsp + _StackGuard
-		_g_.m.gsignal.stackguard1 = stsp + _StackGuard
-		_g_.m.gsignal.stackAlloc = st.ss_size
-		_g_.m.newSigstack = false
-	}
-
-	// restore signal mask from m.sigmask and unblock essential signals
-	nmask := _g_.m.sigmask
-	for i := range sigtable {
-		if sigtable[i].flags&_SigUnblock != 0 {
-			nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
-		}
-	}
-	sigprocmask(_SIG_SETMASK, &nmask, nil)
+	minitSignals()
 }
 
 // Called from dropm to undo the effect of an minit.
 //go:nosplit
 func unminit() {
-	if getg().m.newSigstack {
-		signalstack(nil)
-	}
+	unminitSignals()
 }
 
 func memlimit() uintptr {
@@ -253,12 +224,9 @@ type sigactiont struct {
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
+func setsig(i uint32, fn uintptr) {
 	var sa sigactiont
-	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
-	if restart {
-		sa.sa_flags |= _SA_RESTART
-	}
+	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
 	sa.sa_mask = sigset_all
 	if fn == funcPC(sighandler) {
 		fn = funcPC(sigtramp)
@@ -269,44 +237,33 @@ func setsig(i int32, fn uintptr, restart bool) {
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsigstack(i int32) {
+func setsigstack(i uint32) {
 	throw("setsigstack")
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func getsig(i int32) uintptr {
+func getsig(i uint32) uintptr {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
-	if sa.sa_handler == funcPC(sigtramp) {
-		return funcPC(sighandler)
-	}
 	return sa.sa_handler
 }
 
+// setSignaltstackSP sets the ss_sp field of a stackt.
 //go:nosplit
-func signalstack(s *stack) {
-	var st stackt
-	if s == nil {
-		st.ss_flags = _SS_DISABLE
-	} else {
-		st.ss_sp = s.lo
-		st.ss_size = s.hi - s.lo
-		st.ss_flags = 0
-	}
-	sigaltstack(&st, nil)
+func setSignalstackSP(s *stackt, sp uintptr) {
+	s.ss_sp = sp
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func updatesigmask(m [(_NSIG + 31) / 32]uint32) {
-	var mask sigset
-	copy(mask.__bits[:], m[:])
-	sigprocmask(_SIG_SETMASK, &mask, nil)
+func sigaddset(mask *sigset, i int) {
+	mask.__bits[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
+}
+
+func sigdelset(mask *sigset, i int) {
+	mask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
 }
 
-func unblocksig(sig int32) {
-	var mask sigset
-	mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
-	sigprocmask(_SIG_UNBLOCK, &mask, nil)
+func (c *sigctxt) fixsigcode(sig uint32) {
 }
diff --git a/src/runtime/os_freebsd_arm.go b/src/runtime/os_freebsd_arm.go
index 1f2add2..0399499 100644
--- a/src/runtime/os_freebsd_arm.go
+++ b/src/runtime/os_freebsd_arm.go
@@ -17,8 +17,8 @@ func checkgoarm() {
 
 //go:nosplit
 func cputicks() int64 {
-	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
+	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
 	// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-	// TODO: need more entropy to better seed fastrand1.
+	// TODO: need more entropy to better seed fastrand.
 	return nanotime()
 }
diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go
index 542f214..320c128 100644
--- a/src/runtime/os_linux.go
+++ b/src/runtime/os_linux.go
@@ -133,7 +133,7 @@ const (
 )
 
 //go:noescape
-func clone(flags int32, stk, mm, gg, fn unsafe.Pointer) int32
+func clone(flags int32, stk, mp, gp, fn unsafe.Pointer) int32
 
 // May run with m.p==nil, so write barriers are not allowed.
 //go:nowritebarrier
@@ -148,9 +148,9 @@ func newosproc(mp *m, stk unsafe.Pointer) {
 	// Disable signals during clone, so that the new thread starts
 	// with signals disabled. It will enable them in minit.
 	var oset sigset
-	rtsigprocmask(_SIG_SETMASK, &sigset_all, &oset, int32(unsafe.Sizeof(oset)))
+	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
 	ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart)))
-	rtsigprocmask(_SIG_SETMASK, &oset, nil, int32(unsafe.Sizeof(oset)))
+	sigprocmask(_SIG_SETMASK, &oset, nil)
 
 	if ret < 0 {
 		print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", -ret, ")\n")
@@ -182,9 +182,13 @@ var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
 const (
 	_AT_NULL   = 0  // End of vector
 	_AT_PAGESZ = 6  // System physical page size
+	_AT_HWCAP  = 16 // hardware capability bit vector
 	_AT_RANDOM = 25 // introduced in 2.6.29
+	_AT_HWCAP2 = 26 // hardware capability bit vector 2
 )
 
+var procAuxv = []byte("/proc/self/auxv\x00")
+
 func sysargs(argc int32, argv **byte) {
 	n := argc + 1
 
@@ -198,7 +202,30 @@ func sysargs(argc int32, argv **byte) {
 
 	// now argv+n is auxv
 	auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize))
-	for i := 0; auxv[i] != _AT_NULL; i += 2 {
+	if sysauxv(auxv[:]) == 0 {
+		// In some situations we don't get a loader-provided
+		// auxv, such as when loaded as a library on Android.
+		// Fall back to /proc/self/auxv.
+		fd := open(&procAuxv[0], 0 /* O_RDONLY */, 0)
+		if fd < 0 {
+			return
+		}
+		var buf [128]uintptr
+		n := read(fd, noescape(unsafe.Pointer(&buf[0])), int32(unsafe.Sizeof(buf)))
+		closefd(fd)
+		if n < 0 {
+			return
+		}
+		// Make sure buf is terminated, even if we didn't read
+		// the whole file.
+		buf[len(buf)-2] = _AT_NULL
+		sysauxv(buf[:])
+	}
+}
+
+func sysauxv(auxv []uintptr) int {
+	var i int
+	for ; auxv[i] != _AT_NULL; i += 2 {
 		tag, val := auxv[i], auxv[i+1]
 		switch tag {
 		case _AT_RANDOM:
@@ -207,21 +234,12 @@ func sysargs(argc int32, argv **byte) {
 			startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:]
 
 		case _AT_PAGESZ:
-			// Check that the true physical page size is
-			// compatible with the runtime's assumed
-			// physical page size.
-			if sys.PhysPageSize < val {
-				print("runtime: kernel page size (", val, ") is larger than runtime page size (", sys.PhysPageSize, ")\n")
-				exit(1)
-			}
-			if sys.PhysPageSize%val != 0 {
-				print("runtime: runtime page size (", sys.PhysPageSize, ") is not a multiple of kernel page size (", val, ")\n")
-				exit(1)
-			}
+			physPageSize = val
 		}
 
 		archauxv(tag, val)
 	}
+	return i / 2
 }
 
 func osinit() {
@@ -262,65 +280,21 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
-//go:nosplit
-func msigsave(mp *m) {
-	smask := &mp.sigmask
-	rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask)))
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
-	rtsigprocmask(_SIG_SETMASK, &sigmask, nil, int32(unsafe.Sizeof(sigmask)))
-}
-
-//go:nosplit
-func sigblock() {
-	rtsigprocmask(_SIG_SETMASK, &sigset_all, nil, int32(unsafe.Sizeof(sigset_all)))
-}
-
 func gettid() uint32
 
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, cannot allocate memory.
 func minit() {
-	// Initialize signal handling.
-	_g_ := getg()
-
-	var st sigaltstackt
-	sigaltstack(nil, &st)
-	if st.ss_flags&_SS_DISABLE != 0 {
-		signalstack(&_g_.m.gsignal.stack)
-		_g_.m.newSigstack = true
-	} else {
-		// Use existing signal stack.
-		stsp := uintptr(unsafe.Pointer(st.ss_sp))
-		_g_.m.gsignal.stack.lo = stsp
-		_g_.m.gsignal.stack.hi = stsp + st.ss_size
-		_g_.m.gsignal.stackguard0 = stsp + _StackGuard
-		_g_.m.gsignal.stackguard1 = stsp + _StackGuard
-		_g_.m.gsignal.stackAlloc = st.ss_size
-		_g_.m.newSigstack = false
-	}
+	minitSignals()
 
 	// for debuggers, in case cgo created the thread
-	_g_.m.procid = uint64(gettid())
-
-	// restore signal mask from m.sigmask and unblock essential signals
-	nmask := _g_.m.sigmask
-	for i := range sigtable {
-		if sigtable[i].flags&_SigUnblock != 0 {
-			sigdelset(&nmask, i)
-		}
-	}
-	rtsigprocmask(_SIG_SETMASK, &nmask, nil, int32(unsafe.Sizeof(nmask)))
+	getg().m.procid = uint64(gettid())
 }
 
 // Called from dropm to undo the effect of an minit.
 //go:nosplit
 func unminit() {
-	if getg().m.newSigstack {
-		signalstack(nil)
-	}
+	unminitSignals()
 }
 
 func memlimit() uintptr {
@@ -360,25 +334,28 @@ func memlimit() uintptr {
 //#endif
 
 func sigreturn()
-func sigtramp()
+func sigtramp(sig uint32, info *siginfo, ctx unsafe.Pointer)
 func cgoSigtramp()
 
 //go:noescape
-func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
-
-//go:noescape
-func sigaltstack(new, old *sigaltstackt)
+func sigaltstack(new, old *stackt)
 
 //go:noescape
 func setitimer(mode int32, new, old *itimerval)
 
 //go:noescape
-func rtsigprocmask(sig uint32, new, old *sigset, size int32)
+func rtsigprocmask(how int32, new, old *sigset, size int32)
+
+//go:nosplit
+//go:nowritebarrierrec
+func sigprocmask(how int32, new, old *sigset) {
+	rtsigprocmask(how, new, old, int32(unsafe.Sizeof(*new)))
+}
 
 //go:noescape
 func getrlimit(kind int32, limit unsafe.Pointer) int32
-func raise(sig int32)
-func raiseproc(sig int32)
+func raise(sig uint32)
+func raiseproc(sig uint32)
 
 //go:noescape
 func sched_getaffinity(pid, len uintptr, buf *uintptr) int32
@@ -386,13 +363,9 @@ func osyield()
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
+func setsig(i uint32, fn uintptr) {
 	var sa sigactiont
-	memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa))
-	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTORER
-	if restart {
-		sa.sa_flags |= _SA_RESTART
-	}
+	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTORER | _SA_RESTART
 	sigfillset(&sa.sa_mask)
 	// Although Linux manpage says "sa_restorer element is obsolete and
 	// should not be used". x86_64 kernel requires it. Only use it on
@@ -413,58 +386,31 @@ func setsig(i int32, fn uintptr, restart bool) {
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsigstack(i int32) {
+func setsigstack(i uint32) {
 	var sa sigactiont
-	if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
-		throw("rt_sigaction failure")
-	}
-	if sa.sa_handler == 0 || sa.sa_handler == _SIG_DFL || sa.sa_handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 {
+	rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask))
+	if sa.sa_flags&_SA_ONSTACK != 0 {
 		return
 	}
 	sa.sa_flags |= _SA_ONSTACK
-	if rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) != 0 {
-		throw("rt_sigaction failure")
-	}
+	rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask))
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func getsig(i int32) uintptr {
+func getsig(i uint32) uintptr {
 	var sa sigactiont
-
-	memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa))
 	if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
 		throw("rt_sigaction read failure")
 	}
-	if sa.sa_handler == funcPC(sigtramp) || sa.sa_handler == funcPC(cgoSigtramp) {
-		return funcPC(sighandler)
-	}
 	return sa.sa_handler
 }
 
+// setSignaltstackSP sets the ss_sp field of a stackt.
 //go:nosplit
-func signalstack(s *stack) {
-	var st sigaltstackt
-	if s == nil {
-		st.ss_flags = _SS_DISABLE
-	} else {
-		st.ss_sp = (*byte)(unsafe.Pointer(s.lo))
-		st.ss_size = s.hi - s.lo
-		st.ss_flags = 0
-	}
-	sigaltstack(&st, nil)
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func updatesigmask(m sigmask) {
-	var mask sigset
-	sigcopyset(&mask, m)
-	rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask)))
+func setSignalstackSP(s *stackt, sp uintptr) {
+	*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
 }
 
-func unblocksig(sig int32) {
-	var mask sigset
-	sigaddset(&mask, int(sig))
-	rtsigprocmask(_SIG_UNBLOCK, &mask, nil, int32(unsafe.Sizeof(mask)))
+func (c *sigctxt) fixsigcode(sig uint32) {
 }
diff --git a/src/runtime/os_linux_arm.go b/src/runtime/os_linux_arm.go
index 8e2765a..2b0834a 100644
--- a/src/runtime/os_linux_arm.go
+++ b/src/runtime/os_linux_arm.go
@@ -8,7 +8,6 @@ import "unsafe"
 
 const (
 	_AT_PLATFORM = 15 //  introduced in at least 2.6.11
-	_AT_HWCAP    = 16 // introduced in at least 2.6.11
 
 	_HWCAP_VFP   = 1 << 6  // introduced in at least 2.6.11
 	_HWCAP_VFPv3 = 1 << 13 // introduced in 2.6.30
@@ -53,8 +52,8 @@ func archauxv(tag, val uintptr) {
 
 //go:nosplit
 func cputicks() int64 {
-	// Currently cputicks() is used in blocking profiler and to seed fastrand1().
+	// Currently cputicks() is used in blocking profiler and to seed fastrand().
 	// nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-	// randomNumber provides better seeding of fastrand1.
+	// randomNumber provides better seeding of fastrand.
 	return nanotime() + int64(randomNumber)
 }
diff --git a/src/runtime/os_linux_arm64.go b/src/runtime/os_linux_arm64.go
index 43262ae..bdc341d 100644
--- a/src/runtime/os_linux_arm64.go
+++ b/src/runtime/os_linux_arm64.go
@@ -19,8 +19,8 @@ func archauxv(tag, val uintptr) {
 
 //go:nosplit
 func cputicks() int64 {
-	// Currently cputicks() is used in blocking profiler and to seed fastrand1().
+	// Currently cputicks() is used in blocking profiler and to seed fastrand().
 	// nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-	// randomNumber provides better seeding of fastrand1.
+	// randomNumber provides better seeding of fastrand.
 	return nanotime() + int64(randomNumber)
 }
diff --git a/src/runtime/os_linux_be64.go b/src/runtime/os_linux_be64.go
new file mode 100644
index 0000000..e66dcac
--- /dev/null
+++ b/src/runtime/os_linux_be64.go
@@ -0,0 +1,48 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The standard GNU/Linux sigset type on big-endian 64-bit machines.
+
+// +build ppc64 s390x
+
+package runtime
+
+const (
+	_SS_DISABLE  = 2
+	_NSIG        = 65
+	_SI_USER     = 0
+	_SIG_BLOCK   = 0
+	_SIG_UNBLOCK = 1
+	_SIG_SETMASK = 2
+	_RLIMIT_AS   = 9
+)
+
+type sigset uint64
+
+type rlimit struct {
+	rlim_cur uintptr
+	rlim_max uintptr
+}
+
+var sigset_all = sigset(^uint64(0))
+
+//go:nosplit
+//go:nowritebarrierrec
+func sigaddset(mask *sigset, i int) {
+	if i > 64 {
+		throw("unexpected signal greater than 64")
+	}
+	*mask |= 1 << (uint(i) - 1)
+}
+
+func sigdelset(mask *sigset, i int) {
+	if i > 64 {
+		throw("unexpected signal greater than 64")
+	}
+	*mask &^= 1 << (uint(i) - 1)
+}
+
+func sigfillset(mask *uint64) {
+	*mask = ^uint64(0)
+}
diff --git a/src/runtime/os_linux_generic.go b/src/runtime/os_linux_generic.go
index a16d140..f672162 100644
--- a/src/runtime/os_linux_generic.go
+++ b/src/runtime/os_linux_generic.go
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build !mips
+// +build !mipsle
 // +build !mips64
 // +build !mips64le
 // +build !s390x
+// +build !ppc64
 // +build linux
 
 package runtime
@@ -31,6 +34,8 @@ type rlimit struct {
 
 var sigset_all = sigset{^uint32(0), ^uint32(0)}
 
+//go:nosplit
+//go:nowritebarrierrec
 func sigaddset(mask *sigset, i int) {
 	(*mask)[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
 }
@@ -42,7 +47,3 @@ func sigdelset(mask *sigset, i int) {
 func sigfillset(mask *uint64) {
 	*mask = ^uint64(0)
 }
-
-func sigcopyset(mask *sigset, m sigmask) {
-	copy((*mask)[:], m[:])
-}
diff --git a/src/runtime/os_linux_mips64x.go b/src/runtime/os_linux_mips64x.go
index 8039b2f..be2b719 100644
--- a/src/runtime/os_linux_mips64x.go
+++ b/src/runtime/os_linux_mips64x.go
@@ -22,15 +22,15 @@ func archauxv(tag, val uintptr) {
 
 //go:nosplit
 func cputicks() int64 {
-	// Currently cputicks() is used in blocking profiler and to seed fastrand1().
+	// Currently cputicks() is used in blocking profiler and to seed fastrand().
 	// nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-	// randomNumber provides better seeding of fastrand1.
+	// randomNumber provides better seeding of fastrand.
 	return nanotime() + int64(randomNumber)
 }
 
 const (
 	_SS_DISABLE  = 2
-	_NSIG        = 65
+	_NSIG        = 129
 	_SI_USER     = 0
 	_SIG_BLOCK   = 1
 	_SIG_UNBLOCK = 2
@@ -47,6 +47,8 @@ type rlimit struct {
 
 var sigset_all = sigset{^uint64(0), ^uint64(0)}
 
+//go:nosplit
+//go:nowritebarrierrec
 func sigaddset(mask *sigset, i int) {
 	(*mask)[(i-1)/64] |= 1 << ((uint32(i) - 1) & 63)
 }
@@ -58,7 +60,3 @@ func sigdelset(mask *sigset, i int) {
 func sigfillset(mask *[2]uint64) {
 	(*mask)[0], (*mask)[1] = ^uint64(0), ^uint64(0)
 }
-
-func sigcopyset(mask *sigset, m sigmask) {
-	(*mask)[0] = uint64(m[0]) | uint64(m[1])<<32
-}
diff --git a/src/runtime/os_linux_mipsx.go b/src/runtime/os_linux_mipsx.go
new file mode 100644
index 0000000..313da1b
--- /dev/null
+++ b/src/runtime/os_linux_mipsx.go
@@ -0,0 +1,62 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+// +build mips mipsle
+
+package runtime
+
+var randomNumber uint32
+
+func archauxv(tag, val uintptr) {
+	switch tag {
+	case _AT_RANDOM:
+		// sysargs filled in startupRandomData, but that
+		// pointer may not be word aligned, so we must treat
+		// it as a byte array.
+		randomNumber = uint32(startupRandomData[4]) | uint32(startupRandomData[5])<<8 |
+			uint32(startupRandomData[6])<<16 | uint32(startupRandomData[7])<<24
+	}
+}
+
+//go:nosplit
+func cputicks() int64 {
+	// Currently cputicks() is used in blocking profiler and to seed fastrand().
+	// nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
+	// randomNumber provides better seeding of fastrand1.
+	return nanotime() + int64(randomNumber)
+}
+
+const (
+	_SS_DISABLE  = 2
+	_NSIG        = 128 + 1
+	_SI_USER     = 0
+	_SIG_BLOCK   = 1
+	_SIG_UNBLOCK = 2
+	_SIG_SETMASK = 3
+	_RLIMIT_AS   = 6
+)
+
+type sigset [4]uint32
+
+type rlimit struct {
+	rlim_cur uintptr
+	rlim_max uintptr
+}
+
+var sigset_all = sigset{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}
+
+//go:nosplit
+//go:nowritebarrierrec
+func sigaddset(mask *sigset, i int) {
+	(*mask)[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
+}
+
+func sigdelset(mask *sigset, i int) {
+	(*mask)[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+}
+
+func sigfillset(mask *[4]uint32) {
+	(*mask)[0], (*mask)[1], (*mask)[2], (*mask)[3] = ^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)
+}
diff --git a/src/runtime/os_linux_noauxv.go b/src/runtime/os_linux_noauxv.go
index 22522dd..5e9f031 100644
--- a/src/runtime/os_linux_noauxv.go
+++ b/src/runtime/os_linux_noauxv.go
@@ -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 !amd64,!arm,!arm64,!mips64,!mips64le
+// +build !amd64,!arm,!arm64,!mips,!mipsle,!mips64,!mips64le,!s390x,!ppc64,!ppc64le
 
 package runtime
 
diff --git a/src/runtime/os_linux_ppc64x.go b/src/runtime/os_linux_ppc64x.go
new file mode 100644
index 0000000..b0da98b
--- /dev/null
+++ b/src/runtime/os_linux_ppc64x.go
@@ -0,0 +1,60 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ppc64 ppc64le
+
+package runtime
+
+import (
+	"runtime/internal/sys"
+)
+
+const (
+	// ISA level
+	// Go currently requires POWER5 as a minimum for ppc64, so we need
+	// to check for ISA 2.03 and beyond.
+	_PPC_FEATURE_POWER5_PLUS = 0x00020000 // ISA 2.03 (POWER5+)
+	_PPC_FEATURE_ARCH_2_05   = 0x00001000 // ISA 2.05 (POWER6)
+	_PPC_FEATURE_POWER6_EXT  = 0x00000200 // mffgpr/mftgpr extension (POWER6x)
+	_PPC_FEATURE_ARCH_2_06   = 0x00000100 // ISA 2.06 (POWER7)
+	_PPC_FEATURE2_ARCH_2_07  = 0x80000000 // ISA 2.07 (POWER8)
+
+	// Standalone capabilities
+	_PPC_FEATURE_HAS_ALTIVEC = 0x10000000 // SIMD/Vector unit
+	_PPC_FEATURE_HAS_VSX     = 0x00000080 // Vector scalar unit
+)
+
+type facilities struct {
+	_         [sys.CacheLineSize]byte
+	isPOWER5x bool // ISA 2.03
+	isPOWER6  bool // ISA 2.05
+	isPOWER6x bool // ISA 2.05 + mffgpr/mftgpr extension
+	isPOWER7  bool // ISA 2.06
+	isPOWER8  bool // ISA 2.07
+	hasVMX    bool // Vector unit
+	hasVSX    bool // Vector scalar unit
+	_         [sys.CacheLineSize]byte
+}
+
+// cpu can be tested at runtime in go assembler code to check for
+// a certain ISA level or hardware capability, for example:
+//	  ·cpu+facilities_hasVSX(SB) for checking the availability of VSX
+//	  or
+//	  ·cpu+facilities_isPOWER7(SB) for checking if the processor implements
+//	  ISA 2.06 instructions.
+var cpu facilities
+
+func archauxv(tag, val uintptr) {
+	switch tag {
+	case _AT_HWCAP:
+		cpu.isPOWER5x = val&_PPC_FEATURE_POWER5_PLUS != 0
+		cpu.isPOWER6 = val&_PPC_FEATURE_ARCH_2_05 != 0
+		cpu.isPOWER6x = val&_PPC_FEATURE_POWER6_EXT != 0
+		cpu.isPOWER7 = val&_PPC_FEATURE_ARCH_2_06 != 0
+		cpu.hasVMX = val&_PPC_FEATURE_HAS_ALTIVEC != 0
+		cpu.hasVSX = val&_PPC_FEATURE_HAS_VSX != 0
+	case _AT_HWCAP2:
+		cpu.isPOWER8 = val&_PPC_FEATURE2_ARCH_2_07 != 0
+	}
+}
diff --git a/src/runtime/os_linux_s390x.go b/src/runtime/os_linux_s390x.go
index e659dff..3ca6d4c 100644
--- a/src/runtime/os_linux_s390x.go
+++ b/src/runtime/os_linux_s390x.go
@@ -4,43 +4,29 @@
 
 package runtime
 
-const (
-	_SS_DISABLE  = 2
-	_NSIG        = 65
-	_SI_USER     = 0
-	_SIG_BLOCK   = 0
-	_SIG_UNBLOCK = 1
-	_SIG_SETMASK = 2
-	_RLIMIT_AS   = 9
+import (
+	"runtime/internal/sys"
 )
 
-type sigset uint64
+const (
+	// bit masks taken from bits/hwcap.h
+	_HWCAP_S390_VX = 2048 // vector facility
+)
 
-type rlimit struct {
-	rlim_cur uintptr
-	rlim_max uintptr
+// facilities is padded to avoid false sharing.
+type facilities struct {
+	_     [sys.CacheLineSize]byte
+	hasVX bool // vector facility
+	_     [sys.CacheLineSize]byte
 }
 
-var sigset_all = sigset(^uint64(0))
-
-func sigaddset(mask *sigset, i int) {
-	if i > 64 {
-		throw("unexpected signal greater than 64")
-	}
-	*mask |= 1 << (uint(i) - 1)
-}
+// cpu indicates the availability of s390x facilities that can be used in
+// Go assembly but are optional on models supported by Go.
+var cpu facilities
 
-func sigdelset(mask *sigset, i int) {
-	if i > 64 {
-		throw("unexpected signal greater than 64")
+func archauxv(tag, val uintptr) {
+	switch tag {
+	case _AT_HWCAP: // CPU capability bit flags
+		cpu.hasVX = val&_HWCAP_S390_VX != 0
 	}
-	*mask &^= 1 << (uint(i) - 1)
-}
-
-func sigfillset(mask *uint64) {
-	*mask = ^uint64(0)
-}
-
-func sigcopyset(mask *sigset, m sigmask) {
-	*mask = sigset(uint64(m[0]) | uint64(m[1])<<32)
 }
diff --git a/src/runtime/os_nacl.go b/src/runtime/os_nacl.go
index 1dacc1a..7015316 100644
--- a/src/runtime/os_nacl.go
+++ b/src/runtime/os_nacl.go
@@ -45,7 +45,7 @@ func os_sigpipe() {
 	throw("too many writes on closed pipe")
 }
 
-func dieFromSignal(sig int32) {
+func dieFromSignal(sig uint32) {
 	exit(2)
 }
 
@@ -60,7 +60,7 @@ func sigpanic() {
 	panicmem()
 }
 
-func raiseproc(sig int32) {
+func raiseproc(sig uint32) {
 }
 
 // Stubs so tests can link correctly. These should never be called.
@@ -116,6 +116,7 @@ func osinit() {
 	ncpu = 1
 	getg().m.procid = 2
 	//nacl_exception_handler(funcPC(sigtramp), nil);
+	physPageSize = 65536
 }
 
 func signame(sig uint32) string {
@@ -253,7 +254,7 @@ func badsignalgo(sig uintptr) {
 	if !sigsend(uint32(sig)) {
 		// A foreign thread received the signal sig, and the
 		// Go code does not want to handle it.
-		raisebadsignal(int32(sig))
+		raisebadsignal(uint32(sig))
 	}
 }
 
@@ -266,7 +267,7 @@ func badsignal2() {
 
 var badsignal1 = []byte("runtime: signal received on thread not created by Go.\n")
 
-func raisebadsignal(sig int32) {
+func raisebadsignal(sig uint32) {
 	badsignal2()
 }
 
diff --git a/src/runtime/os_nacl_arm.go b/src/runtime/os_nacl_arm.go
index f94c183..8669ee7 100644
--- a/src/runtime/os_nacl_arm.go
+++ b/src/runtime/os_nacl_arm.go
@@ -16,8 +16,8 @@ func checkgoarm() {
 
 //go:nosplit
 func cputicks() int64 {
-	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
+	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
 	// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-	// TODO: need more entropy to better seed fastrand1.
+	// TODO: need more entropy to better seed fastrand.
 	return nanotime()
 }
diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go
index 4c44b2b..c79b50b 100644
--- a/src/runtime/os_netbsd.go
+++ b/src/runtime/os_netbsd.go
@@ -32,21 +32,21 @@ type mOS struct {
 func setitimer(mode int32, new, old *itimerval)
 
 //go:noescape
-func sigaction(sig int32, new, old *sigactiont)
+func sigaction(sig uint32, new, old *sigactiont)
 
 //go:noescape
-func sigaltstack(new, old *sigaltstackt)
+func sigaltstack(new, old *stackt)
 
 //go:noescape
-func sigprocmask(mode int32, new, old *sigset)
+func sigprocmask(how int32, new, old *sigset)
 
 //go:noescape
 func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
 
 func lwp_tramp()
 
-func raise(sig int32)
-func raiseproc(sig int32)
+func raise(sig uint32)
+func raiseproc(sig uint32)
 
 //go:noescape
 func getcontext(ctxt unsafe.Pointer)
@@ -79,8 +79,9 @@ var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)
 
 // From NetBSD's <sys/sysctl.h>
 const (
-	_CTL_HW  = 6
-	_HW_NCPU = 3
+	_CTL_HW      = 6
+	_HW_NCPU     = 3
+	_HW_PAGESIZE = 7
 )
 
 func getncpu() int32 {
@@ -94,6 +95,17 @@ func getncpu() int32 {
 	return 1
 }
 
+func getPageSize() uintptr {
+	mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
+	out := uint32(0)
+	nout := unsafe.Sizeof(out)
+	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
+	if ret >= 0 {
+		return uintptr(out)
+	}
+	return 0
+}
+
 //go:nosplit
 func semacreate(mp *m) {
 }
@@ -180,12 +192,14 @@ func newosproc(mp *m, stk unsafe.Pointer) {
 // At this point all signals are blocked, so there is no race.
 //go:nosplit
 func netbsdMstart() {
-	signalstack(nil)
+	st := stackt{ss_flags: _SS_DISABLE}
+	sigaltstack(&st, nil)
 	mstart()
 }
 
 func osinit() {
 	ncpu = getncpu()
+	physPageSize = getPageSize()
 }
 
 var urandom_dev = []byte("/dev/urandom\x00")
@@ -209,29 +223,12 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
-//go:nosplit
-func msigsave(mp *m) {
-	sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
-	sigprocmask(_SIG_SETMASK, &sigmask, nil)
-}
-
-//go:nosplit
-func sigblock() {
-	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
-}
-
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, cannot allocate memory.
 func minit() {
 	_g_ := getg()
 	_g_.m.procid = uint64(lwp_self())
 
-	// Initialize signal handling.
-
 	// On NetBSD a thread created by pthread_create inherits the
 	// signal stack of the creating thread. We always create a
 	// new signal stack here, to avoid having two Go threads using
@@ -242,22 +239,13 @@ func minit() {
 	signalstack(&_g_.m.gsignal.stack)
 	_g_.m.newSigstack = true
 
-	// restore signal mask from m.sigmask and unblock essential signals
-	nmask := _g_.m.sigmask
-	for i := range sigtable {
-		if sigtable[i].flags&_SigUnblock != 0 {
-			nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
-		}
-	}
-	sigprocmask(_SIG_SETMASK, &nmask, nil)
+	minitSignalMask()
 }
 
 // Called from dropm to undo the effect of an minit.
 //go:nosplit
 func unminit() {
-	if getg().m.newSigstack {
-		signalstack(nil)
-	}
+	unminitSignals()
 }
 
 func memlimit() uintptr {
@@ -274,12 +262,9 @@ type sigactiont struct {
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
+func setsig(i uint32, fn uintptr) {
 	var sa sigactiont
-	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
-	if restart {
-		sa.sa_flags |= _SA_RESTART
-	}
+	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
 	sa.sa_mask = sigset_all
 	if fn == funcPC(sighandler) {
 		fn = funcPC(sigtramp)
@@ -290,44 +275,33 @@ func setsig(i int32, fn uintptr, restart bool) {
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsigstack(i int32) {
+func setsigstack(i uint32) {
 	throw("setsigstack")
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func getsig(i int32) uintptr {
+func getsig(i uint32) uintptr {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
-	if sa.sa_sigaction == funcPC(sigtramp) {
-		return funcPC(sighandler)
-	}
 	return sa.sa_sigaction
 }
 
+// setSignaltstackSP sets the ss_sp field of a stackt.
 //go:nosplit
-func signalstack(s *stack) {
-	var st sigaltstackt
-	if s == nil {
-		st.ss_flags = _SS_DISABLE
-	} else {
-		st.ss_sp = s.lo
-		st.ss_size = s.hi - s.lo
-		st.ss_flags = 0
-	}
-	sigaltstack(&st, nil)
+func setSignalstackSP(s *stackt, sp uintptr) {
+	s.ss_sp = sp
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func updatesigmask(m sigmask) {
-	var mask sigset
-	copy(mask.__bits[:], m[:])
-	sigprocmask(_SIG_SETMASK, &mask, nil)
+func sigaddset(mask *sigset, i int) {
+	mask.__bits[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
+}
+
+func sigdelset(mask *sigset, i int) {
+	mask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
 }
 
-func unblocksig(sig int32) {
-	var mask sigset
-	mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
-	sigprocmask(_SIG_UNBLOCK, &mask, nil)
+func (c *sigctxt) fixsigcode(sig uint32) {
 }
diff --git a/src/runtime/os_netbsd_arm.go b/src/runtime/os_netbsd_arm.go
index 03032e8..95603da 100644
--- a/src/runtime/os_netbsd_arm.go
+++ b/src/runtime/os_netbsd_arm.go
@@ -28,8 +28,8 @@ func checkgoarm() {
 
 //go:nosplit
 func cputicks() int64 {
-	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
+	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
 	// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-	// TODO: need more entropy to better seed fastrand1.
+	// TODO: need more entropy to better seed fastrand.
 	return nanotime()
 }
diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go
index 9a5c53e..350166d 100644
--- a/src/runtime/os_openbsd.go
+++ b/src/runtime/os_openbsd.go
@@ -17,19 +17,32 @@ type mOS struct {
 func setitimer(mode int32, new, old *itimerval)
 
 //go:noescape
-func sigaction(sig int32, new, old *sigactiont)
+func sigaction(sig uint32, new, old *sigactiont)
 
 //go:noescape
 func sigaltstack(new, old *stackt)
 
 //go:noescape
-func sigprocmask(mode int32, new sigset) sigset
+func obsdsigprocmask(how int32, new sigset) sigset
+
+//go:nosplit
+//go:nowritebarrierrec
+func sigprocmask(how int32, new, old *sigset) {
+	n := sigset(0)
+	if new != nil {
+		n = *new
+	}
+	r := obsdsigprocmask(how, n)
+	if old != nil {
+		*old = r
+	}
+}
 
 //go:noescape
 func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
 
-func raise(sig int32)
-func raiseproc(sig int32)
+func raise(sig uint32)
+func raiseproc(sig uint32)
 
 //go:noescape
 func tfork(param *tforkt, psize uintptr, mm *m, gg *g, fn uintptr) int32
@@ -57,15 +70,13 @@ const (
 
 type sigset uint32
 
-const (
-	sigset_none = sigset(0)
-	sigset_all  = ^sigset(0)
-)
+var sigset_all = ^sigset(0)
 
 // From OpenBSD's <sys/sysctl.h>
 const (
-	_CTL_HW  = 6
-	_HW_NCPU = 3
+	_CTL_HW      = 6
+	_HW_NCPU     = 3
+	_HW_PAGESIZE = 7
 )
 
 func getncpu() int32 {
@@ -81,6 +92,17 @@ func getncpu() int32 {
 	return 1
 }
 
+func getPageSize() uintptr {
+	mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
+	out := uint32(0)
+	nout := unsafe.Sizeof(out)
+	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
+	if ret >= 0 {
+		return uintptr(out)
+	}
+	return 0
+}
+
 //go:nosplit
 func semacreate(mp *m) {
 }
@@ -148,9 +170,10 @@ func newosproc(mp *m, stk unsafe.Pointer) {
 		tf_stack: uintptr(stk),
 	}
 
-	oset := sigprocmask(_SIG_SETMASK, sigset_all)
+	var oset sigset
+	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
 	ret := tfork(&param, unsafe.Sizeof(param), mp, mp.g0, funcPC(mstart))
-	sigprocmask(_SIG_SETMASK, oset)
+	sigprocmask(_SIG_SETMASK, &oset, nil)
 
 	if ret < 0 {
 		print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n")
@@ -163,6 +186,7 @@ func newosproc(mp *m, stk unsafe.Pointer) {
 
 func osinit() {
 	ncpu = getncpu()
+	physPageSize = getPageSize()
 }
 
 var urandom_dev = []byte("/dev/urandom\x00")
@@ -186,62 +210,20 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
-//go:nosplit
-func msigsave(mp *m) {
-	mp.sigmask = sigprocmask(_SIG_BLOCK, 0)
-}
-
-//go:nosplit
-func msigrestore(sigmask sigset) {
-	sigprocmask(_SIG_SETMASK, sigmask)
-}
-
-//go:nosplit
-func sigblock() {
-	sigprocmask(_SIG_SETMASK, sigset_all)
-}
-
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
-	_g_ := getg()
-
 	// m.procid is a uint64, but tfork writes an int32. Fix it up.
+	_g_ := getg()
 	_g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid)))
 
-	// Initialize signal handling
-	var st stackt
-	sigaltstack(nil, &st)
-	if st.ss_flags&_SS_DISABLE != 0 {
-		signalstack(&_g_.m.gsignal.stack)
-		_g_.m.newSigstack = true
-	} else {
-		// Use existing signal stack.
-		stsp := uintptr(unsafe.Pointer(st.ss_sp))
-		_g_.m.gsignal.stack.lo = stsp
-		_g_.m.gsignal.stack.hi = stsp + st.ss_size
-		_g_.m.gsignal.stackguard0 = stsp + _StackGuard
-		_g_.m.gsignal.stackguard1 = stsp + _StackGuard
-		_g_.m.gsignal.stackAlloc = st.ss_size
-		_g_.m.newSigstack = false
-	}
-
-	// restore signal mask from m.sigmask and unblock essential signals
-	nmask := _g_.m.sigmask
-	for i := range sigtable {
-		if sigtable[i].flags&_SigUnblock != 0 {
-			nmask &^= 1 << (uint32(i) - 1)
-		}
-	}
-	sigprocmask(_SIG_SETMASK, nmask)
+	minitSignals()
 }
 
 // Called from dropm to undo the effect of an minit.
 //go:nosplit
 func unminit() {
-	if getg().m.newSigstack {
-		signalstack(nil)
-	}
+	unminitSignals()
 }
 
 func memlimit() uintptr {
@@ -258,12 +240,9 @@ type sigactiont struct {
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsig(i int32, fn uintptr, restart bool) {
+func setsig(i uint32, fn uintptr) {
 	var sa sigactiont
-	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
-	if restart {
-		sa.sa_flags |= _SA_RESTART
-	}
+	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
 	sa.sa_mask = uint32(sigset_all)
 	if fn == funcPC(sighandler) {
 		fn = funcPC(sigtramp)
@@ -274,41 +253,33 @@ func setsig(i int32, fn uintptr, restart bool) {
 
 //go:nosplit
 //go:nowritebarrierrec
-func setsigstack(i int32) {
+func setsigstack(i uint32) {
 	throw("setsigstack")
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func getsig(i int32) uintptr {
+func getsig(i uint32) uintptr {
 	var sa sigactiont
 	sigaction(i, nil, &sa)
-	if sa.sa_sigaction == funcPC(sigtramp) {
-		return funcPC(sighandler)
-	}
 	return sa.sa_sigaction
 }
 
+// setSignaltstackSP sets the ss_sp field of a stackt.
 //go:nosplit
-func signalstack(s *stack) {
-	var st stackt
-	if s == nil {
-		st.ss_flags = _SS_DISABLE
-	} else {
-		st.ss_sp = s.lo
-		st.ss_size = s.hi - s.lo
-		st.ss_flags = 0
-	}
-	sigaltstack(&st, nil)
+func setSignalstackSP(s *stackt, sp uintptr) {
+	s.ss_sp = sp
 }
 
 //go:nosplit
 //go:nowritebarrierrec
-func updatesigmask(m sigmask) {
-	sigprocmask(_SIG_SETMASK, sigset(m[0]))
+func sigaddset(mask *sigset, i int) {
+	*mask |= 1 << (uint32(i) - 1)
+}
+
+func sigdelset(mask *sigset, i int) {
+	*mask &^= 1 << (uint32(i) - 1)
 }
 
-func unblocksig(sig int32) {
-	mask := sigset(1) << (uint32(sig) - 1)
-	sigprocmask(_SIG_UNBLOCK, mask)
+func (c *sigctxt) fixsigcode(sig uint32) {
 }
diff --git a/src/runtime/os_openbsd_arm.go b/src/runtime/os_openbsd_arm.go
index b46fef0..be2e1e9 100644
--- a/src/runtime/os_openbsd_arm.go
+++ b/src/runtime/os_openbsd_arm.go
@@ -17,8 +17,8 @@ func checkgoarm() {
 
 //go:nosplit
 func cputicks() int64 {
-	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
+	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
 	// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-	// TODO: need more entropy to better seed fastrand1.
+	// TODO: need more entropy to better seed fastrand.
 	return nanotime()
 }
diff --git a/src/runtime/os_plan9.go b/src/runtime/os_plan9.go
index 2f3a0d1..ba2d5c5 100644
--- a/src/runtime/os_plan9.go
+++ b/src/runtime/os_plan9.go
@@ -13,6 +13,7 @@ type mOS struct {
 	waitsemacount uint32
 	notesig       *int8
 	errstr        *byte
+	ignoreHangup  bool
 }
 
 func closefd(fd int32) int32
@@ -56,7 +57,7 @@ func noted(mode int32) int32
 func nsec(*int64) int64
 
 //go:noescape
-func sigtramp(ureg, msg unsafe.Pointer)
+func sigtramp(ureg, note unsafe.Pointer)
 
 func setfpmasks()
 
@@ -217,6 +218,55 @@ func getproccount() int32 {
 	return ncpu
 }
 
+var devswap = []byte("/dev/swap\x00")
+var pagesize = []byte(" pagesize\n")
+
+func getPageSize() uintptr {
+	var buf [2048]byte
+	var pos int
+	fd := open(&devswap[0], _OREAD, 0)
+	if fd < 0 {
+		// There's not much we can do if /dev/swap doesn't
+		// exist. However, nothing in the memory manager uses
+		// this on Plan 9, so it also doesn't really matter.
+		return minPhysPageSize
+	}
+	for pos < len(buf) {
+		n := read(fd, unsafe.Pointer(&buf[pos]), int32(len(buf)-pos))
+		if n <= 0 {
+			break
+		}
+		pos += int(n)
+	}
+	closefd(fd)
+	text := buf[:pos]
+	// Find "<n> pagesize" line.
+	bol := 0
+	for i, c := range text {
+		if c == '\n' {
+			bol = i + 1
+		}
+		if bytesHasPrefix(text[i:], pagesize) {
+			// Parse number at the beginning of this line.
+			return uintptr(_atoi(text[bol:]))
+		}
+	}
+	// Again, the page size doesn't really matter, so use a fallback.
+	return minPhysPageSize
+}
+
+func bytesHasPrefix(s, prefix []byte) bool {
+	if len(s) < len(prefix) {
+		return false
+	}
+	for i, p := range prefix {
+		if s[i] != p {
+			return false
+		}
+	}
+	return true
+}
+
 var pid = []byte("#c/pid\x00")
 
 func getpid() uint64 {
@@ -236,6 +286,7 @@ func getpid() uint64 {
 func osinit() {
 	initBloc()
 	ncpu = getproccount()
+	physPageSize = getPageSize()
 	getg().m.procid = getpid()
 	notify(unsafe.Pointer(funcPC(sigtramp)))
 }
@@ -417,7 +468,7 @@ func badsignal2() {
 	exits(&_badsignal[0])
 }
 
-func raisebadsignal(sig int32) {
+func raisebadsignal(sig uint32) {
 	badsignal2()
 }
 
diff --git a/src/runtime/os_plan9_arm.go b/src/runtime/os_plan9_arm.go
index 30cde8f..fdce1e7 100644
--- a/src/runtime/os_plan9_arm.go
+++ b/src/runtime/os_plan9_arm.go
@@ -10,8 +10,8 @@ func checkgoarm() {
 
 //go:nosplit
 func cputicks() int64 {
-	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1().
+	// Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
 	// runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-	// TODO: need more entropy to better seed fastrand1.
+	// TODO: need more entropy to better seed fastrand.
 	return nanotime()
 }
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
index 9147091..0db57f8 100644
--- a/src/runtime/os_windows.go
+++ b/src/runtime/os_windows.go
@@ -20,9 +20,6 @@ const (
 //go:cgo_import_dynamic runtime._CreateIoCompletionPort CreateIoCompletionPort%4 "kernel32.dll"
 //go:cgo_import_dynamic runtime._CreateThread CreateThread%6 "kernel32.dll"
 //go:cgo_import_dynamic runtime._CreateWaitableTimerA CreateWaitableTimerA%3 "kernel32.dll"
-//go:cgo_import_dynamic runtime._CryptAcquireContextW CryptAcquireContextW%5 "advapi32.dll"
-//go:cgo_import_dynamic runtime._CryptGenRandom CryptGenRandom%3 "advapi32.dll"
-//go:cgo_import_dynamic runtime._CryptReleaseContext CryptReleaseContext%2 "advapi32.dll"
 //go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle%7 "kernel32.dll"
 //go:cgo_import_dynamic runtime._ExitProcess ExitProcess%1 "kernel32.dll"
 //go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW%1 "kernel32.dll"
@@ -36,7 +33,6 @@ const (
 //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 "kernel32.dll"
 //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
 //go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 "kernel32.dll"
-//go:cgo_import_dynamic runtime._NtWaitForSingleObject NtWaitForSingleObject%3 "ntdll.dll"
 //go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
 //go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
 //go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll"
@@ -67,9 +63,6 @@ var (
 	_CreateIoCompletionPort,
 	_CreateThread,
 	_CreateWaitableTimerA,
-	_CryptAcquireContextW,
-	_CryptGenRandom,
-	_CryptReleaseContext,
 	_DuplicateHandle,
 	_ExitProcess,
 	_FreeEnvironmentStringsW,
@@ -83,7 +76,6 @@ var (
 	_GetThreadContext,
 	_LoadLibraryW,
 	_LoadLibraryA,
-	_NtWaitForSingleObject,
 	_ResumeThread,
 	_SetConsoleCtrlHandler,
 	_SetErrorMode,
@@ -110,6 +102,21 @@ var (
 	_GetQueuedCompletionStatusEx,
 	_LoadLibraryExW,
 	_ stdFunction
+
+	// Use RtlGenRandom to generate cryptographically random data.
+	// This approach has been recommended by Microsoft (see issue
+	// 15589 for details).
+	// The RtlGenRandom is not listed in advapi32.dll, instead
+	// RtlGenRandom function can be found by searching for SystemFunction036.
+	// Also some versions of Mingw cannot link to SystemFunction036
+	// when building executable as Cgo. So load SystemFunction036
+	// manually during runtime startup.
+	_RtlGenRandom stdFunction
+
+	// Load ntdll.dll manually during startup, otherwise Mingw
+	// links wrong printf function to cgo executable (see issue
+	// 12030 for details).
+	_NtWaitForSingleObject stdFunction
 )
 
 // Function to be called by windows CreateThread
@@ -167,6 +174,20 @@ func loadOptionalSyscalls() {
 	_AddVectoredContinueHandler = windowsFindfunc(k32, []byte("AddVectoredContinueHandler\000"))
 	_GetQueuedCompletionStatusEx = windowsFindfunc(k32, []byte("GetQueuedCompletionStatusEx\000"))
 	_LoadLibraryExW = windowsFindfunc(k32, []byte("LoadLibraryExW\000"))
+
+	var advapi32dll = []byte("advapi32.dll\000")
+	a32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&advapi32dll[0])))
+	if a32 == 0 {
+		throw("advapi32.dll not found")
+	}
+	_RtlGenRandom = windowsFindfunc(a32, []byte("SystemFunction036\000"))
+
+	var ntdll = []byte("ntdll.dll\000")
+	n32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&ntdll[0])))
+	if n32 == 0 {
+		throw("ntdll.dll not found")
+	}
+	_NtWaitForSingleObject = windowsFindfunc(n32, []byte("NtWaitForSingleObject\000"))
 }
 
 //go:nosplit
@@ -205,6 +226,12 @@ func getproccount() int32 {
 	return int32(info.dwnumberofprocessors)
 }
 
+func getPageSize() uintptr {
+	var info systeminfo
+	stdcall1(_GetSystemInfo, uintptr(unsafe.Pointer(&info)))
+	return uintptr(info.dwpagesize)
+}
+
 const (
 	currentProcess = ^uintptr(0) // -1 = current process
 	currentThread  = ^uintptr(1) // -2 = current thread
@@ -256,6 +283,8 @@ func osinit() {
 
 	ncpu = getproccount()
 
+	physPageSize = getPageSize()
+
 	// Windows dynamic priority boosting assumes that a process has different types
 	// of dedicated threads -- GUI, IO, computational, etc. Go processes use
 	// equivalent threads that all do a mix of GUI, IO, computations, etc.
@@ -265,17 +294,9 @@ func osinit() {
 
 //go:nosplit
 func getRandomData(r []byte) {
-	const (
-		prov_rsa_full       = 1
-		crypt_verifycontext = 0xF0000000
-	)
-	var handle uintptr
 	n := 0
-	if stdcall5(_CryptAcquireContextW, uintptr(unsafe.Pointer(&handle)), 0, 0, prov_rsa_full, crypt_verifycontext) != 0 {
-		if stdcall3(_CryptGenRandom, handle, uintptr(len(r)), uintptr(unsafe.Pointer(&r[0]))) != 0 {
-			n = len(r)
-		}
-		stdcall2(_CryptReleaseContext, handle, 0)
+	if stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {
+		n = len(r)
 	}
 	extendRandom(r, n)
 }
@@ -375,13 +396,11 @@ func writeConsole(handle uintptr, buf unsafe.Pointer, bufLen int32) int {
 
 	total := len(s)
 	w := 0
-	for len(s) > 0 {
+	for _, r := range s {
 		if w >= len(utf16tmp)-2 {
 			writeConsoleUTF16(handle, utf16tmp[:w])
 			w = 0
 		}
-		r, n := charntorune(s)
-		s = s[n:]
 		if r < 0x10000 {
 			utf16tmp[w] = uint16(r)
 			w++
@@ -418,6 +437,13 @@ func writeConsoleUTF16(handle uintptr, b []uint16) {
 
 //go:nosplit
 func semasleep(ns int64) int32 {
+	const (
+		_WAIT_ABANDONED = 0x00000080
+		_WAIT_OBJECT_0  = 0x00000000
+		_WAIT_TIMEOUT   = 0x00000102
+		_WAIT_FAILED    = 0xFFFFFFFF
+	)
+
 	// store ms in ns to save stack space
 	if ns < 0 {
 		ns = _INFINITE
@@ -427,15 +453,44 @@ func semasleep(ns int64) int32 {
 			ns = 1
 		}
 	}
-	if stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(ns)) != 0 {
-		return -1 // timeout
+
+	result := stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(ns))
+	switch result {
+	case _WAIT_OBJECT_0: //signaled
+		return 0
+
+	case _WAIT_TIMEOUT:
+		return -1
+
+	case _WAIT_ABANDONED:
+		systemstack(func() {
+			throw("runtime.semasleep wait_abandoned")
+		})
+
+	case _WAIT_FAILED:
+		systemstack(func() {
+			print("runtime: waitforsingleobject wait_failed; errno=", getlasterror(), "\n")
+			throw("runtime.semasleep wait_failed")
+		})
+
+	default:
+		systemstack(func() {
+			print("runtime: waitforsingleobject unexpected; result=", result, "\n")
+			throw("runtime.semasleep unexpected")
+		})
 	}
-	return 0
+
+	return -1 // unreachable
 }
 
 //go:nosplit
 func semawakeup(mp *m) {
-	stdcall1(_SetEvent, mp.waitsema)
+	if stdcall1(_SetEvent, mp.waitsema) == 0 {
+		systemstack(func() {
+			print("runtime: setevent failed; errno=", getlasterror(), "\n")
+			throw("runtime.semawakeup")
+		})
+	}
 }
 
 //go:nosplit
@@ -444,6 +499,12 @@ func semacreate(mp *m) {
 		return
 	}
 	mp.waitsema = stdcall4(_CreateEventA, 0, 0, 0, 0)
+	if mp.waitsema == 0 {
+		systemstack(func() {
+			print("runtime: createevent failed; errno=", getlasterror(), "\n")
+			throw("runtime.semacreate")
+		})
+	}
 }
 
 // May run with m.p==nil, so write barriers are not allowed. This
@@ -456,6 +517,7 @@ func newosproc(mp *m, stk unsafe.Pointer) {
 	thandle := stdcall6(_CreateThread, 0, 0x20000,
 		funcPC(tstart_stdcall), uintptr(unsafe.Pointer(mp)),
 		_STACK_SIZE_PARAM_IS_A_RESERVATION, 0)
+
 	if thandle == 0 {
 		print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", getlasterror(), ")\n")
 		throw("runtime.newosproc")
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index 60b277d..7392436 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -6,6 +6,7 @@ package runtime
 
 import (
 	"runtime/internal/atomic"
+	"runtime/internal/sys"
 	"unsafe"
 )
 
@@ -62,10 +63,6 @@ func panicmem() {
 	panic(memoryError)
 }
 
-func throwreturn() {
-	throw("no return at end of a typed function - compiler is broken")
-}
-
 func throwinit() {
 	throw("recursive call during initialization - linker skew")
 }
@@ -88,16 +85,21 @@ func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
 	argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn)
 	callerpc := getcallerpc(unsafe.Pointer(&siz))
 
-	systemstack(func() {
-		d := newdefer(siz)
-		if d._panic != nil {
-			throw("deferproc: d.panic != nil after newdefer")
-		}
-		d.fn = fn
-		d.pc = callerpc
-		d.sp = sp
-		memmove(add(unsafe.Pointer(d), unsafe.Sizeof(*d)), unsafe.Pointer(argp), uintptr(siz))
-	})
+	d := newdefer(siz)
+	if d._panic != nil {
+		throw("deferproc: d.panic != nil after newdefer")
+	}
+	d.fn = fn
+	d.pc = callerpc
+	d.sp = sp
+	switch siz {
+	case 0:
+		// Do nothing.
+	case sys.PtrSize:
+		*(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp))
+	default:
+		memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz))
+	}
 
 	// deferproc returns 0 normally.
 	// a deferred func that stops a panic
@@ -166,6 +168,10 @@ func testdefersizes() {
 // immediately after the _defer header in memory.
 //go:nosplit
 func deferArgs(d *_defer) unsafe.Pointer {
+	if d.siz == 0 {
+		// Avoid pointer past the defer allocation.
+		return nil
+	}
 	return add(unsafe.Pointer(d), unsafe.Sizeof(*d))
 }
 
@@ -179,22 +185,30 @@ func init() {
 
 // Allocate a Defer, usually using per-P pool.
 // Each defer must be released with freedefer.
-// Note: runs on g0 stack
+//
+// This must not grow the stack because there may be a frame without
+// stack map information when this is called.
+//
+//go:nosplit
 func newdefer(siz int32) *_defer {
 	var d *_defer
 	sc := deferclass(uintptr(siz))
-	mp := acquirem()
+	gp := getg()
 	if sc < uintptr(len(p{}.deferpool)) {
-		pp := mp.p.ptr()
+		pp := gp.m.p.ptr()
 		if len(pp.deferpool[sc]) == 0 && sched.deferpool[sc] != nil {
-			lock(&sched.deferlock)
-			for len(pp.deferpool[sc]) < cap(pp.deferpool[sc])/2 && sched.deferpool[sc] != nil {
-				d := sched.deferpool[sc]
-				sched.deferpool[sc] = d.link
-				d.link = nil
-				pp.deferpool[sc] = append(pp.deferpool[sc], d)
-			}
-			unlock(&sched.deferlock)
+			// Take the slow path on the system stack so
+			// we don't grow newdefer's stack.
+			systemstack(func() {
+				lock(&sched.deferlock)
+				for len(pp.deferpool[sc]) < cap(pp.deferpool[sc])/2 && sched.deferpool[sc] != nil {
+					d := sched.deferpool[sc]
+					sched.deferpool[sc] = d.link
+					d.link = nil
+					pp.deferpool[sc] = append(pp.deferpool[sc], d)
+				}
+				unlock(&sched.deferlock)
+			})
 		}
 		if n := len(pp.deferpool[sc]); n > 0 {
 			d = pp.deferpool[sc][n-1]
@@ -204,19 +218,24 @@ func newdefer(siz int32) *_defer {
 	}
 	if d == nil {
 		// Allocate new defer+args.
-		total := roundupsize(totaldefersize(uintptr(siz)))
-		d = (*_defer)(mallocgc(total, deferType, true))
+		systemstack(func() {
+			total := roundupsize(totaldefersize(uintptr(siz)))
+			d = (*_defer)(mallocgc(total, deferType, true))
+		})
 	}
 	d.siz = siz
-	gp := mp.curg
 	d.link = gp._defer
 	gp._defer = d
-	releasem(mp)
 	return d
 }
 
 // Free the given defer.
 // The defer cannot be used after this call.
+//
+// This must not grow the stack because there may be a frame without a
+// stack map when this is called.
+//
+//go:nosplit
 func freedefer(d *_defer) {
 	if d._panic != nil {
 		freedeferpanic()
@@ -226,31 +245,34 @@ func freedefer(d *_defer) {
 	}
 	sc := deferclass(uintptr(d.siz))
 	if sc < uintptr(len(p{}.deferpool)) {
-		mp := acquirem()
-		pp := mp.p.ptr()
+		pp := getg().m.p.ptr()
 		if len(pp.deferpool[sc]) == cap(pp.deferpool[sc]) {
 			// Transfer half of local cache to the central cache.
-			var first, last *_defer
-			for len(pp.deferpool[sc]) > cap(pp.deferpool[sc])/2 {
-				n := len(pp.deferpool[sc])
-				d := pp.deferpool[sc][n-1]
-				pp.deferpool[sc][n-1] = nil
-				pp.deferpool[sc] = pp.deferpool[sc][:n-1]
-				if first == nil {
-					first = d
-				} else {
-					last.link = d
+			//
+			// Take this slow path on the system stack so
+			// we don't grow freedefer's stack.
+			systemstack(func() {
+				var first, last *_defer
+				for len(pp.deferpool[sc]) > cap(pp.deferpool[sc])/2 {
+					n := len(pp.deferpool[sc])
+					d := pp.deferpool[sc][n-1]
+					pp.deferpool[sc][n-1] = nil
+					pp.deferpool[sc] = pp.deferpool[sc][:n-1]
+					if first == nil {
+						first = d
+					} else {
+						last.link = d
+					}
+					last = d
 				}
-				last = d
-			}
-			lock(&sched.deferlock)
-			last.link = sched.deferpool[sc]
-			sched.deferpool[sc] = first
-			unlock(&sched.deferlock)
+				lock(&sched.deferlock)
+				last.link = sched.deferpool[sc]
+				sched.deferpool[sc] = first
+				unlock(&sched.deferlock)
+			})
 		}
 		*d = _defer{}
 		pp.deferpool[sc] = append(pp.deferpool[sc], d)
-		releasem(mp)
 	}
 }
 
@@ -292,19 +314,23 @@ func deferreturn(arg0 uintptr) {
 	}
 
 	// Moving arguments around.
-	// Do not allow preemption here, because the garbage collector
-	// won't know the form of the arguments until the jmpdefer can
-	// flip the PC over to fn.
-	mp := acquirem()
-	memmove(unsafe.Pointer(&arg0), deferArgs(d), uintptr(d.siz))
+	//
+	// Everything called after this point must be recursively
+	// nosplit because the garbage collector won't know the form
+	// of the arguments until the jmpdefer can flip the PC over to
+	// fn.
+	switch d.siz {
+	case 0:
+		// Do nothing.
+	case sys.PtrSize:
+		*(*uintptr)(unsafe.Pointer(&arg0)) = *(*uintptr)(deferArgs(d))
+	default:
+		memmove(unsafe.Pointer(&arg0), deferArgs(d), uintptr(d.siz))
+	}
 	fn := d.fn
 	d.fn = nil
 	gp._defer = d.link
-	// Switch to systemstack merely to save nosplit stack space.
-	systemstack(func() {
-		freedefer(d)
-	})
-	releasem(mp)
+	freedefer(d)
 	jmpdefer(fn, uintptr(unsafe.Pointer(&arg0)))
 }
 
@@ -354,6 +380,11 @@ func Goexit() {
 // Used when crashing with panicking.
 // This must match types handled by printany.
 func preprintpanics(p *_panic) {
+	defer func() {
+		if recover() != nil {
+			throw("panic while printing panic value")
+		}
+	}()
 	for p != nil {
 		switch v := p.arg.(type) {
 		case error:
@@ -504,15 +535,9 @@ func gopanic(e interface{}) {
 // getargp returns the location where the caller
 // writes outgoing function call arguments.
 //go:nosplit
+//go:noinline
 func getargp(x int) uintptr {
 	// x is an argument mainly so that we can return its address.
-	// However, we need to make the function complex enough
-	// that it won't be inlined. We always pass x = 0, so this code
-	// does nothing other than keep the compiler from thinking
-	// the function is simple enough to inline.
-	if x > 0 {
-		return getcallersp(unsafe.Pointer(&x)) * 0
-	}
 	return uintptr(noescape(unsafe.Pointer(&x)))
 }
 
@@ -555,6 +580,11 @@ func dopanic(unused int) {
 	*(*int)(nil) = 0
 }
 
+//go:linkname sync_throw sync.throw
+func sync_throw(s string) {
+	throw(s)
+}
+
 //go:nosplit
 func throw(s string) {
 	print("fatal error: ", s, "\n")
diff --git a/src/runtime/plugin.go b/src/runtime/plugin.go
new file mode 100644
index 0000000..845bf76
--- /dev/null
+++ b/src/runtime/plugin.go
@@ -0,0 +1,96 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+import "unsafe"
+
+//go:linkname plugin_lastmoduleinit plugin.lastmoduleinit
+func plugin_lastmoduleinit() (path string, syms map[string]interface{}, mismatchpkg string) {
+	md := firstmoduledata.next
+	if md == nil {
+		throw("runtime: no plugin module data")
+	}
+	for md.next != nil {
+		md = md.next
+	}
+	if md.typemap != nil {
+		throw("runtime: plugin already initialized")
+	}
+
+	for _, pmd := range activeModules() {
+		if pmd.pluginpath == md.pluginpath {
+			println("plugin: plugin", md.pluginpath, "already loaded")
+			throw("plugin: plugin already loaded")
+		}
+
+		if inRange(pmd.text, pmd.etext, md.text, md.etext) ||
+			inRange(pmd.bss, pmd.ebss, md.bss, md.ebss) ||
+			inRange(pmd.data, pmd.edata, md.data, md.edata) ||
+			inRange(pmd.types, pmd.etypes, md.types, md.etypes) {
+			println("plugin: new module data overlaps with previous moduledata")
+			println("\tpmd.text-etext=", hex(pmd.text), "-", hex(pmd.etext))
+			println("\tpmd.bss-ebss=", hex(pmd.bss), "-", hex(pmd.ebss))
+			println("\tpmd.data-edata=", hex(pmd.data), "-", hex(pmd.edata))
+			println("\tpmd.types-etypes=", hex(pmd.types), "-", hex(pmd.etypes))
+			println("\tmd.text-etext=", hex(md.text), "-", hex(md.etext))
+			println("\tmd.bss-ebss=", hex(md.bss), "-", hex(md.ebss))
+			println("\tmd.data-edata=", hex(md.data), "-", hex(md.edata))
+			println("\tmd.types-etypes=", hex(md.types), "-", hex(md.etypes))
+			throw("plugin: new module data overlaps with previous moduledata")
+		}
+	}
+	for _, pkghash := range md.pkghashes {
+		if pkghash.linktimehash != *pkghash.runtimehash {
+			return "", nil, pkghash.modulename
+		}
+	}
+
+	// Initialize the freshly loaded module.
+	modulesinit()
+	typelinksinit()
+
+	lock(&ifaceLock)
+	for _, i := range md.itablinks {
+		additab(i, true, false)
+	}
+	unlock(&ifaceLock)
+
+	// Build a map of symbol names to symbols. Here in the runtime
+	// we fill out the first word of the interface, the type. We
+	// pass these zero value interfaces to the plugin package,
+	// where the symbol value is filled in (usually via cgo).
+	//
+	// Because functions are handled specially in the plugin package,
+	// function symbol names are prefixed here with '.' to avoid
+	// a dependency on the reflect package.
+	syms = make(map[string]interface{}, len(md.ptab))
+	for _, ptab := range md.ptab {
+		symName := resolveNameOff(unsafe.Pointer(md.types), ptab.name)
+		t := (*_type)(unsafe.Pointer(md.types)).typeOff(ptab.typ)
+		var val interface{}
+		valp := (*[2]unsafe.Pointer)(unsafe.Pointer(&val))
+		(*valp)[0] = unsafe.Pointer(t)
+
+		name := symName.name()
+		if t.kind&kindMask == kindFunc {
+			name = "." + name
+		}
+		syms[name] = val
+	}
+	return md.pluginpath, syms, ""
+}
+
+// inRange reports whether v0 or v1 are in the range [r0, r1].
+func inRange(r0, r1, v0, v1 uintptr) bool {
+	return (v0 >= r0 && v0 <= r1) || (v1 >= r0 && v1 <= r1)
+}
+
+// A ptabEntry is generated by the compiler for each exported function
+// and global variable in the main package of a plugin. It is used to
+// initialize the plugin module's symbol map.
+type ptabEntry struct {
+	name nameOff
+	typ  typeOff
+}
diff --git a/src/runtime/pprof/internal/protopprof/protomemprofile.go b/src/runtime/pprof/internal/protopprof/protomemprofile.go
new file mode 100644
index 0000000..c2ab5b5
--- /dev/null
+++ b/src/runtime/pprof/internal/protopprof/protomemprofile.go
@@ -0,0 +1,83 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package protopprof
+
+import (
+	"internal/pprof/profile"
+	"math"
+	"runtime"
+	"time"
+)
+
+// EncodeMemProfile converts MemProfileRecords to a Profile.
+func EncodeMemProfile(mr []runtime.MemProfileRecord, rate int64, t time.Time) *profile.Profile {
+	p := &profile.Profile{
+		Period:     rate,
+		PeriodType: &profile.ValueType{Type: "space", Unit: "bytes"},
+		SampleType: []*profile.ValueType{
+			{Type: "alloc_objects", Unit: "count"},
+			{Type: "alloc_space", Unit: "bytes"},
+			{Type: "inuse_objects", Unit: "count"},
+			{Type: "inuse_space", Unit: "bytes"},
+		},
+		TimeNanos: int64(t.UnixNano()),
+	}
+
+	locs := make(map[uintptr]*profile.Location)
+	for _, r := range mr {
+		stack := r.Stack()
+		sloc := make([]*profile.Location, len(stack))
+		for i, addr := range stack {
+			loc := locs[addr]
+			if loc == nil {
+				loc = &profile.Location{
+					ID:      uint64(len(p.Location) + 1),
+					Address: uint64(addr),
+				}
+				locs[addr] = loc
+				p.Location = append(p.Location, loc)
+			}
+			sloc[i] = loc
+		}
+
+		ao, ab := scaleHeapSample(r.AllocObjects, r.AllocBytes, rate)
+		uo, ub := scaleHeapSample(r.InUseObjects(), r.InUseBytes(), rate)
+
+		p.Sample = append(p.Sample, &profile.Sample{
+			Value:    []int64{ao, ab, uo, ub},
+			Location: sloc,
+		})
+	}
+	if runtime.GOOS == "linux" {
+		addMappings(p)
+	}
+	return p
+}
+
+// scaleHeapSample adjusts the data from a heap Sample to
+// account for its probability of appearing in the collected
+// data. heap profiles are a sampling of the memory allocations
+// requests in a program. We estimate the unsampled value by dividing
+// each collected sample by its probability of appearing in the
+// profile. heap profiles rely on a poisson process to determine
+// which samples to collect, based on the desired average collection
+// rate R. The probability of a sample of size S to appear in that
+// profile is 1-exp(-S/R).
+func scaleHeapSample(count, size, rate int64) (int64, int64) {
+	if count == 0 || size == 0 {
+		return 0, 0
+	}
+
+	if rate <= 1 {
+		// if rate==1 all samples were collected so no adjustment is needed.
+		// if rate<1 treat as unknown and skip scaling.
+		return count, size
+	}
+
+	avgSize := float64(size) / float64(count)
+	scale := 1 / (1 - math.Exp(-avgSize/float64(rate)))
+
+	return int64(float64(count) * scale), int64(float64(size) * scale)
+}
diff --git a/src/runtime/pprof/internal/protopprof/protomemprofile_test.go b/src/runtime/pprof/internal/protopprof/protomemprofile_test.go
new file mode 100644
index 0000000..a10fe77
--- /dev/null
+++ b/src/runtime/pprof/internal/protopprof/protomemprofile_test.go
@@ -0,0 +1,104 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package protopprof
+
+import (
+	"bytes"
+	"internal/pprof/profile"
+	"io/ioutil"
+	"reflect"
+	"runtime"
+	"testing"
+	"time"
+)
+
+// TestSampledHeapAllocProfile tests encoding of a memory profile from
+// runtime.MemProfileRecord data.
+func TestSampledHeapAllocProfile(t *testing.T) {
+	if runtime.GOOS != "linux" {
+		t.Skip("Test requires a system with /proc/self/maps")
+	}
+
+	// Figure out two addresses from /proc/self/maps.
+	mmap, err := ioutil.ReadFile("/proc/self/maps")
+	if err != nil {
+		t.Fatal("Cannot read /proc/self/maps")
+	}
+	rd := bytes.NewReader(mmap)
+	mprof := &profile.Profile{}
+	if err = mprof.ParseMemoryMap(rd); err != nil {
+		t.Fatalf("Cannot parse /proc/self/maps")
+	}
+	if len(mprof.Mapping) < 2 {
+		// It is possible for a binary to only have 1 executable
+		// region of memory.
+		t.Skipf("need 2 or more mappings, got %v", len(mprof.Mapping))
+	}
+	address1 := mprof.Mapping[0].Start
+	address2 := mprof.Mapping[1].Start
+
+	var buf bytes.Buffer
+
+	rec, rate := testMemRecords(address1, address2)
+	p := EncodeMemProfile(rec, rate, time.Now())
+	if err := p.Write(&buf); err != nil {
+		t.Fatalf("Failed to write profile: %v", err)
+	}
+
+	p, err = profile.Parse(&buf)
+	if err != nil {
+		t.Fatalf("Could not parse Profile profile: %v", err)
+	}
+
+	// Expected PeriodType, SampleType and Sample.
+	expectedPeriodType := &profile.ValueType{Type: "space", Unit: "bytes"}
+	expectedSampleType := []*profile.ValueType{
+		{Type: "alloc_objects", Unit: "count"},
+		{Type: "alloc_space", Unit: "bytes"},
+		{Type: "inuse_objects", Unit: "count"},
+		{Type: "inuse_space", Unit: "bytes"},
+	}
+	// Expected samples, with values unsampled according to the profiling rate.
+	expectedSample := []*profile.Sample{
+		{Value: []int64{2050, 2099200, 1537, 1574400}, Location: []*profile.Location{
+			{ID: 1, Mapping: mprof.Mapping[0], Address: address1},
+			{ID: 2, Mapping: mprof.Mapping[1], Address: address2},
+		}},
+		{Value: []int64{1, 829411, 1, 829411}, Location: []*profile.Location{
+			{ID: 3, Mapping: mprof.Mapping[1], Address: address2 + 1},
+			{ID: 4, Mapping: mprof.Mapping[1], Address: address2 + 2},
+		}},
+		{Value: []int64{1, 829411, 0, 0}, Location: []*profile.Location{
+			{ID: 5, Mapping: mprof.Mapping[0], Address: address1 + 1},
+			{ID: 6, Mapping: mprof.Mapping[0], Address: address1 + 2},
+			{ID: 7, Mapping: mprof.Mapping[1], Address: address2 + 3},
+		}},
+	}
+
+	if p.Period != 512*1024 {
+		t.Fatalf("Sampling periods do not match")
+	}
+	if !reflect.DeepEqual(p.PeriodType, expectedPeriodType) {
+		t.Fatalf("Period types do not match")
+	}
+	if !reflect.DeepEqual(p.SampleType, expectedSampleType) {
+		t.Fatalf("Sample types do not match")
+	}
+	if !reflect.DeepEqual(p.Sample, expectedSample) {
+		t.Fatalf("Samples do not match: Expected: %v, Got:%v", getSampleAsString(expectedSample),
+			getSampleAsString(p.Sample))
+	}
+}
+
+func testMemRecords(a1, a2 uint64) ([]runtime.MemProfileRecord, int64) {
+	addr1, addr2 := uintptr(a1), uintptr(a2)
+	rate := int64(512 * 1024)
+	rec := []runtime.MemProfileRecord{
+		{4096, 1024, 4, 1, [32]uintptr{addr1, addr2}},
+		{512 * 1024, 0, 1, 0, [32]uintptr{addr2 + 1, addr2 + 2}},
+		{512 * 1024, 512 * 1024, 1, 1, [32]uintptr{addr1 + 1, addr1 + 2, addr2 + 3}},
+	}
+	return rec, rate
+}
diff --git a/src/runtime/pprof/internal/protopprof/protopprof.go b/src/runtime/pprof/internal/protopprof/protopprof.go
new file mode 100644
index 0000000..5d269c4
--- /dev/null
+++ b/src/runtime/pprof/internal/protopprof/protopprof.go
@@ -0,0 +1,105 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package protopprof converts the runtime's raw profile logs
+// to Profile structs containing a representation of the pprof
+// protocol buffer profile format.
+package protopprof
+
+import (
+	"fmt"
+	"os"
+	"runtime"
+	"time"
+	"unsafe"
+
+	"internal/pprof/profile"
+)
+
+// TranslateCPUProfile parses binary CPU profiling stack trace data
+// generated by runtime.CPUProfile() into a profile struct.
+func TranslateCPUProfile(b []byte, startTime time.Time) (*profile.Profile, error) {
+	const wordSize = unsafe.Sizeof(uintptr(0))
+	const minRawProfile = 5 * wordSize // Need a minimum of 5 words.
+	if uintptr(len(b)) < minRawProfile {
+		return nil, fmt.Errorf("truncated profile")
+	}
+	n := int(uintptr(len(b)) / wordSize)
+	data := ((*[1 << 28]uintptr)(unsafe.Pointer(&b[0])))[:n:n]
+	period := data[3]
+	data = data[5:] // skip header
+
+	// profile initialization taken from pprof tool
+	p := &profile.Profile{
+		Period:     int64(period) * 1000,
+		PeriodType: &profile.ValueType{Type: "cpu", Unit: "nanoseconds"},
+		SampleType: []*profile.ValueType{
+			{Type: "samples", Unit: "count"},
+			{Type: "cpu", Unit: "nanoseconds"},
+		},
+		TimeNanos:     int64(startTime.UnixNano()),
+		DurationNanos: time.Since(startTime).Nanoseconds(),
+	}
+	// Parse CPU samples from the profile.
+	locs := make(map[uint64]*profile.Location)
+	for len(b) > 0 {
+		if len(data) < 2 || uintptr(len(data)) < 2+data[1] {
+			return nil, fmt.Errorf("truncated profile")
+		}
+		count := data[0]
+		nstk := data[1]
+		if uintptr(len(data)) < 2+nstk {
+			return nil, fmt.Errorf("truncated profile")
+		}
+		stk := data[2 : 2+nstk]
+		data = data[2+nstk:]
+
+		if count == 0 && nstk == 1 && stk[0] == 0 {
+			// end of data marker
+			break
+		}
+
+		sloc := make([]*profile.Location, len(stk))
+		for i, addr := range stk {
+			addr := uint64(addr)
+			// Addresses from stack traces point to the next instruction after
+			// each call.  Adjust by -1 to land somewhere on the actual call
+			// (except for the leaf, which is not a call).
+			if i > 0 {
+				addr--
+			}
+			loc := locs[addr]
+			if loc == nil {
+				loc = &profile.Location{
+					ID:      uint64(len(p.Location) + 1),
+					Address: addr,
+				}
+				locs[addr] = loc
+				p.Location = append(p.Location, loc)
+			}
+			sloc[i] = loc
+		}
+		p.Sample = append(p.Sample, &profile.Sample{
+			Value:    []int64{int64(count), int64(count) * int64(p.Period)},
+			Location: sloc,
+		})
+	}
+
+	if runtime.GOOS == "linux" {
+		if err := addMappings(p); err != nil {
+			return nil, err
+		}
+	}
+	return p, nil
+}
+
+func addMappings(p *profile.Profile) error {
+	// Parse memory map from /proc/self/maps
+	f, err := os.Open("/proc/self/maps")
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	return p.ParseMemoryMap(f)
+}
diff --git a/src/runtime/pprof/internal/protopprof/protopprof_test.go b/src/runtime/pprof/internal/protopprof/protopprof_test.go
new file mode 100644
index 0000000..f1937b5
--- /dev/null
+++ b/src/runtime/pprof/internal/protopprof/protopprof_test.go
@@ -0,0 +1,171 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package protopprof
+
+import (
+	"bytes"
+	"fmt"
+	"internal/pprof/profile"
+	"io/ioutil"
+	"reflect"
+	"runtime"
+	"testing"
+	"time"
+	"unsafe"
+)
+
+// Helper function to initialize empty cpu profile with sampling period provided.
+func createEmptyProfileWithPeriod(t *testing.T, periodMs uint64) bytes.Buffer {
+	// Mock the sample header produced by cpu profiler. Write a sample
+	// period of 2000 microseconds, followed by no samples.
+	buf := new(bytes.Buffer)
+	// Profile header is as follows:
+	// The first, third and fifth words are 0. The second word is 3.
+	// The fourth word is the period.
+	// EOD marker:
+	// The sixth word -- count is initialized to 0 above.
+	// The code below sets the seventh word -- nstk to 1
+	// The eighth word -- addr is initialized to 0 above.
+	words := []int{0, 3, 0, int(periodMs), 0, 0, 1, 0}
+	n := int(unsafe.Sizeof(0)) * len(words)
+	data := ((*[1 << 29]byte)(unsafe.Pointer(&words[0])))[:n:n]
+	if _, err := buf.Write(data); err != nil {
+		t.Fatalf("createEmptyProfileWithPeriod failed: %v", err)
+	}
+	return *buf
+}
+
+// Helper function to initialize cpu profile with two sample values.
+func createProfileWithTwoSamples(t *testing.T, periodMs uintptr, count1 uintptr, count2 uintptr,
+	address1 uintptr, address2 uintptr) bytes.Buffer {
+	// Mock the sample header produced by cpu profiler. Write a sample
+	// period of 2000 microseconds, followed by no samples.
+	buf := new(bytes.Buffer)
+	words := []uintptr{0, 3, 0, uintptr(periodMs), 0, uintptr(count1), 2,
+		uintptr(address1), uintptr(address1 + 2),
+		uintptr(count2), 2, uintptr(address2), uintptr(address2 + 2),
+		0, 1, 0}
+	for _, n := range words {
+		var err error
+		switch unsafe.Sizeof(int(0)) {
+		case 8:
+			_, err = buf.Write((*[8]byte)(unsafe.Pointer(&n))[:8:8])
+		case 4:
+			_, err = buf.Write((*[4]byte)(unsafe.Pointer(&n))[:4:4])
+		}
+		if err != nil {
+			t.Fatalf("createProfileWithTwoSamples failed: %v", err)
+		}
+	}
+	return *buf
+}
+
+// Tests TranslateCPUProfile parses correct sampling period in an otherwise empty cpu profile.
+func TestTranlateCPUProfileSamplingPeriod(t *testing.T) {
+	// A test server with mock cpu profile data.
+	var buf bytes.Buffer
+
+	startTime := time.Now()
+	b := createEmptyProfileWithPeriod(t, 2000)
+	p, err := TranslateCPUProfile(b.Bytes(), startTime)
+	if err != nil {
+		t.Fatalf("translate failed: %v", err)
+	}
+	if err := p.Write(&buf); err != nil {
+		t.Fatalf("write failed: %v", err)
+	}
+
+	p, err = profile.Parse(&buf)
+	if err != nil {
+		t.Fatalf("Could not parse Profile profile: %v", err)
+	}
+
+	// Expected PeriodType and SampleType.
+	expectedPeriodType := &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}
+	expectedSampleType := []*profile.ValueType{
+		{Type: "samples", Unit: "count"},
+		{Type: "cpu", Unit: "nanoseconds"},
+	}
+	if p.Period != 2000*1000 || !reflect.DeepEqual(p.PeriodType, expectedPeriodType) ||
+		!reflect.DeepEqual(p.SampleType, expectedSampleType) || p.Sample != nil {
+		t.Fatalf("Unexpected Profile fields")
+	}
+}
+
+func getSampleAsString(sample []*profile.Sample) string {
+	var str string
+	for _, x := range sample {
+		for _, y := range x.Location {
+			if y.Mapping != nil {
+				str += fmt.Sprintf("Mapping:%v\n", *y.Mapping)
+			}
+			str += fmt.Sprintf("Location:%v\n", y)
+		}
+		str += fmt.Sprintf("Sample:%v\n", *x)
+	}
+	return str
+}
+
+// Tests TranslateCPUProfile parses a cpu profile with sample values present.
+func TestTranslateCPUProfileWithSamples(t *testing.T) {
+	if runtime.GOOS != "linux" {
+		t.Skip("test requires a system with /proc/self/maps")
+	}
+	// Figure out two addresses from /proc/self/maps.
+	mmap, err := ioutil.ReadFile("/proc/self/maps")
+	if err != nil {
+		t.Fatal("Cannot read /proc/self/maps")
+	}
+	rd := bytes.NewReader(mmap)
+	mprof := &profile.Profile{}
+	if err = mprof.ParseMemoryMap(rd); err != nil {
+		t.Fatalf("Cannot parse /proc/self/maps")
+	}
+	if len(mprof.Mapping) < 2 {
+		// It is possible for a binary to only have 1 executable
+		// region of memory.
+		t.Skipf("need 2 or more mappings, got %v", len(mprof.Mapping))
+	}
+	address1 := mprof.Mapping[0].Start
+	address2 := mprof.Mapping[1].Start
+	// A test server with mock cpu profile data.
+
+	startTime := time.Now()
+	b := createProfileWithTwoSamples(t, 2000, 20, 40, uintptr(address1), uintptr(address2))
+	p, err := TranslateCPUProfile(b.Bytes(), startTime)
+
+	if err != nil {
+		t.Fatalf("Could not parse Profile profile: %v", err)
+	}
+	// Expected PeriodType, SampleType and Sample.
+	expectedPeriodType := &profile.ValueType{Type: "cpu", Unit: "nanoseconds"}
+	expectedSampleType := []*profile.ValueType{
+		{Type: "samples", Unit: "count"},
+		{Type: "cpu", Unit: "nanoseconds"},
+	}
+	expectedSample := []*profile.Sample{
+		{Value: []int64{20, 20 * 2000 * 1000}, Location: []*profile.Location{
+			{ID: 1, Mapping: mprof.Mapping[0], Address: address1},
+			{ID: 2, Mapping: mprof.Mapping[0], Address: address1 + 1},
+		}},
+		{Value: []int64{40, 40 * 2000 * 1000}, Location: []*profile.Location{
+			{ID: 3, Mapping: mprof.Mapping[1], Address: address2},
+			{ID: 4, Mapping: mprof.Mapping[1], Address: address2 + 1},
+		}},
+	}
+	if p.Period != 2000*1000 {
+		t.Fatalf("Sampling periods do not match")
+	}
+	if !reflect.DeepEqual(p.PeriodType, expectedPeriodType) {
+		t.Fatalf("Period types do not match")
+	}
+	if !reflect.DeepEqual(p.SampleType, expectedSampleType) {
+		t.Fatalf("Sample types do not match")
+	}
+	if !reflect.DeepEqual(p.Sample, expectedSample) {
+		t.Fatalf("Samples do not match: Expected: %v, Got:%v", getSampleAsString(expectedSample),
+			getSampleAsString(p.Sample))
+	}
+}
diff --git a/src/runtime/pprof/mprof_test.go b/src/runtime/pprof/mprof_test.go
index 0fff9d4..df4f6f8 100644
--- a/src/runtime/pprof/mprof_test.go
+++ b/src/runtime/pprof/mprof_test.go
@@ -7,6 +7,7 @@ package pprof_test
 import (
 	"bytes"
 	"fmt"
+	"reflect"
 	"regexp"
 	"runtime"
 	. "runtime/pprof"
@@ -42,6 +43,17 @@ func allocatePersistent1K() {
 	}
 }
 
+// Allocate transient memory using reflect.Call.
+
+func allocateReflectTransient() {
+	memSink = make([]byte, 2<<20)
+}
+
+func allocateReflect() {
+	rv := reflect.ValueOf(allocateReflectTransient)
+	rv.Call(nil)
+}
+
 var memoryProfilerRun = 0
 
 func TestMemoryProfiler(t *testing.T) {
@@ -61,6 +73,7 @@ func TestMemoryProfiler(t *testing.T) {
 	allocateTransient1M()
 	allocateTransient2M()
 	allocatePersistent1K()
+	allocateReflect()
 	memSink = nil
 
 	runtime.GC() // materialize stats
@@ -73,18 +86,22 @@ func TestMemoryProfiler(t *testing.T) {
 
 	tests := []string{
 		fmt.Sprintf(`%v: %v \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
-#	0x[0-9,a-f]+	runtime/pprof_test\.allocatePersistent1K\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test\.go:40
-#	0x[0-9,a-f]+	runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test\.go:63
+#	0x[0-9,a-f]+	runtime/pprof_test\.allocatePersistent1K\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test\.go:41
+#	0x[0-9,a-f]+	runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test\.go:75
 `, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun),
 
 		fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
-#	0x[0-9,a-f]+	runtime/pprof_test\.allocateTransient1M\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test.go:21
-#	0x[0-9,a-f]+	runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test.go:61
+#	0x[0-9,a-f]+	runtime/pprof_test\.allocateTransient1M\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test.go:22
+#	0x[0-9,a-f]+	runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test.go:73
 `, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun),
 
 		fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
-#	0x[0-9,a-f]+	runtime/pprof_test\.allocateTransient2M\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test.go:27
-#	0x[0-9,a-f]+	runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test.go:62
+#	0x[0-9,a-f]+	runtime/pprof_test\.allocateTransient2M\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test.go:28
+#	0x[0-9,a-f]+	runtime/pprof_test\.TestMemoryProfiler\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test.go:74
+`, memoryProfilerRun, (2<<20)*memoryProfilerRun),
+
+		fmt.Sprintf(`0: 0 \[%v: %v\] @( 0x[0-9,a-f]+)+
+#	0x[0-9,a-f]+	runtime/pprof_test\.allocateReflectTransient\+0x[0-9,a-f]+	.*/runtime/pprof/mprof_test.go:49
 `, memoryProfilerRun, (2<<20)*memoryProfilerRun),
 	}
 
diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go
index 25f7ed6..aed5b8d 100644
--- a/src/runtime/pprof/pprof.go
+++ b/src/runtime/pprof/pprof.go
@@ -73,13 +73,15 @@ import (
 	"bufio"
 	"bytes"
 	"fmt"
+	"internal/pprof/profile"
 	"io"
-	"os"
 	"runtime"
+	"runtime/pprof/internal/protopprof"
 	"sort"
 	"strings"
 	"sync"
 	"text/tabwriter"
+	"time"
 )
 
 // BUG(rsc): Profiles are only as good as the kernel support used to generate them.
@@ -99,6 +101,7 @@ import (
 //	heap         - a sampling of all heap allocations
 //	threadcreate - stack traces that led to the creation of new OS threads
 //	block        - stack traces that led to blocking on synchronization primitives
+//	mutex        - stack traces of holders of contended mutexes
 //
 // These predefined profiles maintain themselves and panic on an explicit
 // Add or Remove method call.
@@ -152,6 +155,12 @@ var blockProfile = &Profile{
 	write: writeBlock,
 }
 
+var mutexProfile = &Profile{
+	name:  "mutex",
+	count: countMutex,
+	write: writeMutex,
+}
+
 func lockProfiles() {
 	profiles.mu.Lock()
 	if profiles.m == nil {
@@ -161,6 +170,7 @@ func lockProfiles() {
 			"threadcreate": threadcreateProfile,
 			"heap":         heapProfile,
 			"block":        blockProfile,
+			"mutex":        mutexProfile,
 		}
 	}
 }
@@ -202,21 +212,15 @@ func Profiles() []*Profile {
 	lockProfiles()
 	defer unlockProfiles()
 
-	var all []*Profile
+	all := make([]*Profile, 0, len(profiles.m))
 	for _, p := range profiles.m {
 		all = append(all, p)
 	}
 
-	sort.Sort(byName(all))
+	sort.Slice(all, func(i, j int) bool { return all[i].name < all[j].name })
 	return all
 }
 
-type byName []*Profile
-
-func (x byName) Len() int           { return len(x) }
-func (x byName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
-func (x byName) Less(i, j int) bool { return x[i].name < x[j].name }
-
 // Name returns this profile's name, which can be passed to Lookup to reobtain the profile.
 func (p *Profile) Name() string {
 	return p.name
@@ -299,7 +303,7 @@ func (p *Profile) WriteTo(w io.Writer, debug int) error {
 	}
 
 	// Obtain consistent snapshot under lock; then process without lock.
-	var all [][]uintptr
+	all := make([][]uintptr, 0, len(p.m))
 	p.mu.Lock()
 	for _, stk := range p.m {
 		all = append(all, stk)
@@ -337,17 +341,8 @@ type countProfile interface {
 }
 
 // printCountProfile prints a countProfile at the specified debug level.
+// The profile will be in compressed proto format unless debug is nonzero.
 func printCountProfile(w io.Writer, debug int, name string, p countProfile) error {
-	b := bufio.NewWriter(w)
-	var tw *tabwriter.Writer
-	w = b
-	if debug > 0 {
-		tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
-		w = tw
-	}
-
-	fmt.Fprintf(w, "%s profile: total %d\n", name, p.Len())
-
 	// Build count of each stack.
 	var buf bytes.Buffer
 	key := func(stk []uintptr) string {
@@ -373,17 +368,37 @@ func printCountProfile(w io.Writer, debug int, name string, p countProfile) erro
 
 	sort.Sort(&keysByCount{keys, count})
 
-	for _, k := range keys {
-		fmt.Fprintf(w, "%d %s\n", count[k], k)
-		if debug > 0 {
-			printStackRecord(w, p.Stack(index[k]), false)
+	if debug > 0 {
+		// Print debug profile in legacy format
+		tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
+		fmt.Fprintf(tw, "%s profile: total %d\n", name, p.Len())
+		for _, k := range keys {
+			fmt.Fprintf(tw, "%d %s\n", count[k], k)
+			printStackRecord(tw, p.Stack(index[k]), false)
 		}
+		return tw.Flush()
 	}
 
-	if tw != nil {
-		tw.Flush()
+	// Output profile in protobuf form.
+	prof := &profile.Profile{
+		PeriodType: &profile.ValueType{Type: name, Unit: "count"},
+		Period:     1,
+		Sample:     make([]*profile.Sample, 0, len(keys)),
+		SampleType: []*profile.ValueType{{Type: name, Unit: "count"}},
 	}
-	return b.Flush()
+	for _, k := range keys {
+		stk := p.Stack(index[k])
+		c := count[k]
+		locs := make([]*profile.Location, len(stk))
+		for i, addr := range stk {
+			locs[i] = &profile.Location{Address: uint64(addr) - 1}
+		}
+		prof.Sample = append(prof.Sample, &profile.Sample{
+			Location: locs,
+			Value:    []int64{int64(c)},
+		})
+	}
+	return prof.Write(w)
 }
 
 // keysByCount sorts keys with higher counts first, breaking ties by key string order.
@@ -435,12 +450,6 @@ func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) {
 
 // Interface to system profiles.
 
-type byInUseBytes []runtime.MemProfileRecord
-
-func (x byInUseBytes) Len() int           { return len(x) }
-func (x byInUseBytes) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
-func (x byInUseBytes) Less(i, j int) bool { return x[i].InUseBytes() > x[j].InUseBytes() }
-
 // WriteHeapProfile is shorthand for Lookup("heap").WriteTo(w, 0).
 // It is preserved for backwards compatibility.
 func WriteHeapProfile(w io.Writer) error {
@@ -476,15 +485,16 @@ func writeHeap(w io.Writer, debug int) error {
 		// Profile grew; try again.
 	}
 
-	sort.Sort(byInUseBytes(p))
+	if debug == 0 {
+		pp := protopprof.EncodeMemProfile(p, int64(runtime.MemProfileRate), time.Now())
+		return pp.Write(w)
+	}
+
+	sort.Slice(p, func(i, j int) bool { return p[i].InUseBytes() > p[j].InUseBytes() })
 
 	b := bufio.NewWriter(w)
-	var tw *tabwriter.Writer
-	w = b
-	if debug > 0 {
-		tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
-		w = tw
-	}
+	tw := tabwriter.NewWriter(b, 1, 8, 1, '\t', 0)
+	w = tw
 
 	var total runtime.MemProfileRecord
 	for i := range p {
@@ -512,9 +522,7 @@ func writeHeap(w io.Writer, debug int) error {
 			fmt.Fprintf(w, " %#x", pc)
 		}
 		fmt.Fprintf(w, "\n")
-		if debug > 0 {
-			printStackRecord(w, r.Stack(), false)
-		}
+		printStackRecord(w, r.Stack(), false)
 	}
 
 	// Print memstats information too.
@@ -540,15 +548,15 @@ func writeHeap(w io.Writer, debug int) error {
 	fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
 	fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys)
 	fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys)
+	fmt.Fprintf(w, "# GCSys = %d\n", s.GCSys)
+	fmt.Fprintf(w, "# OtherSys = %d\n", s.OtherSys)
 
 	fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC)
 	fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs)
 	fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC)
 	fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC)
 
-	if tw != nil {
-		tw.Flush()
-	}
+	tw.Flush()
 	return b.Flush()
 }
 
@@ -672,49 +680,29 @@ func StartCPUProfile(w io.Writer) error {
 }
 
 func profileWriter(w io.Writer) {
+	startTime := time.Now()
+	// This will buffer the entire profile into buf and then
+	// translate it into a profile.Profile structure. This will
+	// create two copies of all the data in the profile in memory.
+	// TODO(matloob): Convert each chunk of the proto output and
+	// stream it out instead of converting the entire profile.
+	var buf bytes.Buffer
 	for {
 		data := runtime.CPUProfile()
 		if data == nil {
 			break
 		}
-		w.Write(data)
-	}
-
-	// We are emitting the legacy profiling format, which permits
-	// a memory map following the CPU samples. The memory map is
-	// simply a copy of the GNU/Linux /proc/self/maps file. The
-	// profiler uses the memory map to map PC values in shared
-	// libraries to a shared library in the filesystem, in order
-	// to report the correct function and, if the shared library
-	// has debug info, file/line. This is particularly useful for
-	// PIE (position independent executables) as on ELF systems a
-	// PIE is simply an executable shared library.
-	//
-	// Because the profiling format expects the memory map in
-	// GNU/Linux format, we only do this on GNU/Linux for now. To
-	// add support for profiling PIE on other ELF-based systems,
-	// it may be necessary to map the system-specific mapping
-	// information to the GNU/Linux format. For a reasonably
-	// portable C++ version, see the FillProcSelfMaps function in
-	// https://github.com/gperftools/gperftools/blob/master/src/base/sysinfo.cc
-	//
-	// The code that parses this mapping for the pprof tool is
-	// ParseMemoryMap in cmd/internal/pprof/legacy_profile.go, but
-	// don't change that code, as similar code exists in other
-	// (non-Go) pprof readers. Change this code so that that code works.
-	//
-	// We ignore errors reading or copying the memory map; the
-	// profile is likely usable without it, and we have no good way
-	// to report errors.
-	if runtime.GOOS == "linux" {
-		f, err := os.Open("/proc/self/maps")
-		if err == nil {
-			io.WriteString(w, "\nMAPPED_LIBRARIES:\n")
-			io.Copy(w, f)
-			f.Close()
-		}
+		buf.Write(data)
 	}
 
+	profile, err := protopprof.TranslateCPUProfile(buf.Bytes(), startTime)
+	if err != nil {
+		// The runtime should never produce an invalid or truncated profile.
+		// It drops records that can't fit into its log buffers.
+		panic(fmt.Errorf("could not translate binary profile to proto format: %v", err))
+	}
+
+	profile.Write(w)
 	cpu.done <- true
 }
 
@@ -733,18 +721,18 @@ func StopCPUProfile() {
 	<-cpu.done
 }
 
-type byCycles []runtime.BlockProfileRecord
-
-func (x byCycles) Len() int           { return len(x) }
-func (x byCycles) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
-func (x byCycles) Less(i, j int) bool { return x[i].Cycles > x[j].Cycles }
-
 // countBlock returns the number of records in the blocking profile.
 func countBlock() int {
 	n, _ := runtime.BlockProfile(nil)
 	return n
 }
 
+// countMutex returns the number of records in the mutex profile.
+func countMutex() int {
+	n, _ := runtime.MutexProfile(nil)
+	return n
+}
+
 // writeBlock writes the current blocking profile to w.
 func writeBlock(w io.Writer, debug int) error {
 	var p []runtime.BlockProfileRecord
@@ -758,7 +746,7 @@ func writeBlock(w io.Writer, debug int) error {
 		}
 	}
 
-	sort.Sort(byCycles(p))
+	sort.Slice(p, func(i, j int) bool { return p[i].Cycles > p[j].Cycles })
 
 	b := bufio.NewWriter(w)
 	var tw *tabwriter.Writer
@@ -788,4 +776,49 @@ func writeBlock(w io.Writer, debug int) error {
 	return b.Flush()
 }
 
+// writeMutex writes the current mutex profile to w.
+func writeMutex(w io.Writer, debug int) error {
+	// TODO(pjw): too much common code with writeBlock. FIX!
+	var p []runtime.BlockProfileRecord
+	n, ok := runtime.MutexProfile(nil)
+	for {
+		p = make([]runtime.BlockProfileRecord, n+50)
+		n, ok = runtime.MutexProfile(p)
+		if ok {
+			p = p[:n]
+			break
+		}
+	}
+
+	sort.Slice(p, func(i, j int) bool { return p[i].Cycles > p[j].Cycles })
+
+	b := bufio.NewWriter(w)
+	var tw *tabwriter.Writer
+	w = b
+	if debug > 0 {
+		tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
+		w = tw
+	}
+
+	fmt.Fprintf(w, "--- mutex:\n")
+	fmt.Fprintf(w, "cycles/second=%v\n", runtime_cyclesPerSecond())
+	fmt.Fprintf(w, "sampling period=%d\n", runtime.SetMutexProfileFraction(-1))
+	for i := range p {
+		r := &p[i]
+		fmt.Fprintf(w, "%v %v @", r.Cycles, r.Count)
+		for _, pc := range r.Stack() {
+			fmt.Fprintf(w, " %#x", pc)
+		}
+		fmt.Fprint(w, "\n")
+		if debug > 0 {
+			printStackRecord(w, r.Stack(), true)
+		}
+	}
+
+	if tw != nil {
+		tw.Flush()
+	}
+	return b.Flush()
+}
+
 func runtime_cyclesPerSecond() int64
diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go
index a093015..fd06607 100644
--- a/src/runtime/pprof/pprof_test.go
+++ b/src/runtime/pprof/pprof_test.go
@@ -8,8 +8,12 @@ package pprof_test
 
 import (
 	"bytes"
+	"compress/gzip"
 	"fmt"
+	"internal/pprof/profile"
 	"internal/testenv"
+	"io"
+	"io/ioutil"
 	"math/big"
 	"os"
 	"os/exec"
@@ -20,7 +24,6 @@ import (
 	"sync"
 	"testing"
 	"time"
-	"unsafe"
 )
 
 func cpuHogger(f func(), dur time.Duration) {
@@ -87,40 +90,17 @@ func TestCPUProfileMultithreaded(t *testing.T) {
 }
 
 func parseProfile(t *testing.T, valBytes []byte, f func(uintptr, []uintptr)) {
-	// Convert []byte to []uintptr.
-	l := len(valBytes)
-	if i := bytes.Index(valBytes, []byte("\nMAPPED_LIBRARIES:\n")); i >= 0 {
-		l = i
-	}
-	l /= int(unsafe.Sizeof(uintptr(0)))
-	val := *(*[]uintptr)(unsafe.Pointer(&valBytes))
-	val = val[:l]
-
-	// 5 for the header, 3 for the trailer.
-	if l < 5+3 {
-		t.Logf("profile too short: %#x", val)
-		if badOS[runtime.GOOS] {
-			t.Skipf("ignoring failure on %s; see golang.org/issue/13841", runtime.GOOS)
-			return
-		}
-		t.FailNow()
-	}
-
-	hd, val, tl := val[:5], val[5:l-3], val[l-3:]
-	if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 {
-		t.Fatalf("unexpected header %#x", hd)
-	}
-
-	if tl[0] != 0 || tl[1] != 1 || tl[2] != 0 {
-		t.Fatalf("malformed end-of-data marker %#x", tl)
+	p, err := profile.Parse(bytes.NewReader(valBytes))
+	if err != nil {
+		t.Fatal(err)
 	}
-
-	for len(val) > 0 {
-		if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] {
-			t.Fatalf("malformed profile.  leftover: %#x", val)
+	for _, sample := range p.Sample {
+		count := uintptr(sample.Value[0])
+		stk := make([]uintptr, len(sample.Location))
+		for i := range sample.Location {
+			stk[i] = uintptr(sample.Location[i].Address)
 		}
-		f(val[0], val[2:2+val[1]])
-		val = val[2+val[1]:]
+		f(count, stk)
 	}
 }
 
@@ -366,8 +346,49 @@ func TestMathBigDivide(t *testing.T) {
 	})
 }
 
+func slurpString(r io.Reader) string {
+	slurp, _ := ioutil.ReadAll(r)
+	return string(slurp)
+}
+
+func getLinuxKernelConfig() string {
+	if f, err := os.Open("/proc/config"); err == nil {
+		defer f.Close()
+		return slurpString(f)
+	}
+	if f, err := os.Open("/proc/config.gz"); err == nil {
+		defer f.Close()
+		r, err := gzip.NewReader(f)
+		if err != nil {
+			return ""
+		}
+		return slurpString(r)
+	}
+	if f, err := os.Open("/boot/config"); err == nil {
+		defer f.Close()
+		return slurpString(f)
+	}
+	uname, _ := exec.Command("uname", "-r").Output()
+	if len(uname) > 0 {
+		if f, err := os.Open("/boot/config-" + strings.TrimSpace(string(uname))); err == nil {
+			defer f.Close()
+			return slurpString(f)
+		}
+	}
+	return ""
+}
+
+func haveLinuxHiresTimers() bool {
+	config := getLinuxKernelConfig()
+	return strings.Contains(config, "CONFIG_HIGH_RES_TIMERS=y")
+}
+
 func TestStackBarrierProfiling(t *testing.T) {
-	if (runtime.GOOS == "linux" && runtime.GOARCH == "arm") || runtime.GOOS == "openbsd" || runtime.GOOS == "solaris" || runtime.GOOS == "dragonfly" || runtime.GOOS == "freebsd" {
+	if (runtime.GOOS == "linux" && runtime.GOARCH == "arm") ||
+		runtime.GOOS == "openbsd" ||
+		runtime.GOOS == "solaris" ||
+		runtime.GOOS == "dragonfly" ||
+		runtime.GOOS == "freebsd" {
 		// This test currently triggers a large number of
 		// usleep(100)s. These kernels/arches have poor
 		// resolution timers, so this gives up a whole
@@ -380,6 +401,12 @@ func TestStackBarrierProfiling(t *testing.T) {
 		return
 	}
 
+	if runtime.GOOS == "linux" && strings.HasPrefix(runtime.GOARCH, "mips") {
+		if !haveLinuxHiresTimers() {
+			t.Skipf("low resolution timers inhibit profiling signals (golang.org/issue/13405, golang.org/issue/17936)")
+		}
+	}
+
 	if !strings.Contains(os.Getenv("GODEBUG"), "gcstackbarrierall=1") {
 		// Re-execute this test with constant GC and stack
 		// barriers at every frame.
@@ -592,6 +619,42 @@ func blockCond() {
 	mu.Unlock()
 }
 
+func TestMutexProfile(t *testing.T) {
+	old := runtime.SetMutexProfileFraction(1)
+	defer runtime.SetMutexProfileFraction(old)
+	if old != 0 {
+		t.Fatalf("need MutexProfileRate 0, got %d", old)
+	}
+
+	blockMutex()
+
+	var w bytes.Buffer
+	Lookup("mutex").WriteTo(&w, 1)
+	prof := w.String()
+
+	if !strings.HasPrefix(prof, "--- mutex:\ncycles/second=") {
+		t.Errorf("Bad profile header:\n%v", prof)
+	}
+	prof = strings.Trim(prof, "\n")
+	lines := strings.Split(prof, "\n")
+	if len(lines) != 6 {
+		t.Errorf("expected 6 lines, got %d %q\n%s", len(lines), prof, prof)
+	}
+	if len(lines) < 6 {
+		return
+	}
+	// checking that the line is like "35258904 1 @ 0x48288d 0x47cd28 0x458931"
+	r2 := `^\d+ 1 @(?: 0x[[:xdigit:]]+)+`
+	//r2 := "^[0-9]+ 1 @ 0x[0-9a-f x]+$"
+	if ok, err := regexp.MatchString(r2, lines[3]); err != nil || !ok {
+		t.Errorf("%q didn't match %q", lines[3], r2)
+	}
+	r3 := "^#.*runtime/pprof_test.blockMutex.*$"
+	if ok, err := regexp.MatchString(r3, lines[5]); err != nil || !ok {
+		t.Errorf("%q didn't match %q", lines[5], r3)
+	}
+}
+
 func func1(c chan int) { <-c }
 func func2(c chan int) { <-c }
 func func3(c chan int) { <-c }
@@ -616,13 +679,31 @@ func TestGoroutineCounts(t *testing.T) {
 	time.Sleep(10 * time.Millisecond) // let goroutines block on channel
 
 	var w bytes.Buffer
-	Lookup("goroutine").WriteTo(&w, 1)
+	goroutineProf := Lookup("goroutine")
+
+	// Check debug profile
+	goroutineProf.WriteTo(&w, 1)
 	prof := w.String()
 
 	if !containsInOrder(prof, "\n50 @ ", "\n40 @", "\n10 @", "\n1 @") {
 		t.Errorf("expected sorted goroutine counts:\n%s", prof)
 	}
 
+	// Check proto profile
+	w.Reset()
+	goroutineProf.WriteTo(&w, 0)
+	p, err := profile.Parse(&w)
+	if err != nil {
+		t.Errorf("error parsing protobuf profile: %v", err)
+	}
+	if err := p.CheckValid(); err != nil {
+		t.Errorf("protobuf profile is invalid: %v", err)
+	}
+	if !containsCounts(p, []int64{50, 40, 10, 1}) {
+		t.Errorf("expected count profile to contain goroutines with counts %v, got %v",
+			[]int64{50, 40, 10, 1}, p)
+	}
+
 	close(c)
 
 	time.Sleep(10 * time.Millisecond) // let goroutines exit
@@ -638,3 +719,23 @@ func containsInOrder(s string, all ...string) bool {
 	}
 	return true
 }
+
+func containsCounts(prof *profile.Profile, counts []int64) bool {
+	m := make(map[int64]int)
+	for _, c := range counts {
+		m[c]++
+	}
+	for _, s := range prof.Sample {
+		// The count is the single value in the sample
+		if len(s.Value) != 1 {
+			return false
+		}
+		m[s.Value[0]]--
+	}
+	for _, n := range m {
+		if n > 0 {
+			return false
+		}
+	}
+	return true
+}
diff --git a/src/runtime/print.go b/src/runtime/print.go
index 32626c1..8fa3d39 100644
--- a/src/runtime/print.go
+++ b/src/runtime/print.go
@@ -4,7 +4,10 @@
 
 package runtime
 
-import "unsafe"
+import (
+	"runtime/internal/atomic"
+	"unsafe"
+)
 
 // The compiler knows that a print of a value of this type
 // should use printhex instead of printuint (decimal).
@@ -19,6 +22,36 @@ func bytes(s string) (ret []byte) {
 	return
 }
 
+var (
+	// printBacklog is a circular buffer of messages written with the builtin
+	// print* functions, for use in postmortem analysis of core dumps.
+	printBacklog      [512]byte
+	printBacklogIndex int
+)
+
+// recordForPanic maintains a circular buffer of messages written by the
+// runtime leading up to a process crash, allowing the messages to be
+// extracted from a core dump.
+//
+// The text written during a process crash (following "panic" or "fatal
+// error") is not saved, since the goroutine stacks will generally be readable
+// from the runtime datastructures in the core file.
+func recordForPanic(b []byte) {
+	printlock()
+
+	if atomic.Load(&panicking) == 0 {
+		// Not actively crashing: maintain circular buffer of print output.
+		for i := 0; i < len(b); {
+			n := copy(printBacklog[printBacklogIndex:], b[i:])
+			i += n
+			printBacklogIndex += n
+			printBacklogIndex %= len(printBacklog)
+		}
+	}
+
+	printunlock()
+}
+
 var debuglock mutex
 
 // The compiler emits calls to printlock and printunlock around
@@ -53,6 +86,7 @@ func gwrite(b []byte) {
 	if len(b) == 0 {
 		return
 	}
+	recordForPanic(b)
 	gp := getg()
 	if gp == nil || gp.writebuf == nil {
 		writeErr(b)
@@ -199,10 +233,6 @@ func printpointer(p unsafe.Pointer) {
 }
 
 func printstring(s string) {
-	if uintptr(len(s)) > maxstring {
-		gwrite(bytes("[string too long]"))
-		return
-	}
 	gwrite(bytes(s))
 }
 
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index e693f7e..cad1b1c 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -169,7 +169,8 @@ func main() {
 		cgocall(_cgo_notify_runtime_init_done, nil)
 	}
 
-	main_init()
+	fn := main_init // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
+	fn()
 	close(main_init_done)
 
 	needUnlock = false
@@ -180,7 +181,8 @@ func main() {
 		// has a main, but it is not executed.
 		return
 	}
-	main_main()
+	fn = main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
+	fn()
 	if raceenabled {
 		racefini()
 	}
@@ -379,6 +381,29 @@ func badreflectcall() {
 	panic(plainError("arg size to reflect.call more than 1GB"))
 }
 
+var badmorestackg0Msg = "fatal: morestack on g0\n"
+
+//go:nosplit
+//go:nowritebarrierrec
+func badmorestackg0() {
+	sp := stringStructOf(&badmorestackg0Msg)
+	write(2, sp.str, int32(sp.len))
+}
+
+var badmorestackgsignalMsg = "fatal: morestack on gsignal\n"
+
+//go:nosplit
+//go:nowritebarrierrec
+func badmorestackgsignal() {
+	sp := stringStructOf(&badmorestackgsignalMsg)
+	write(2, sp.str, int32(sp.len))
+}
+
+//go:nosplit
+func badctxt() {
+	throw("ctxt != 0")
+}
+
 func lockedOSThread() bool {
 	gp := getg()
 	return gp.lockedm != nil && gp.m.lockedg != nil
@@ -440,8 +465,9 @@ func schedinit() {
 	mallocinit()
 	mcommoninit(_g_.m)
 	alginit()       // maps must not be used before this call
-	typelinksinit() // uses maps
-	itabsinit()
+	modulesinit()   // provides activeModules
+	typelinksinit() // uses maps, activeModules
+	itabsinit()     // uses activeModules
 
 	msigsave(_g_.m)
 	initSigmask = _g_.m.sigmask
@@ -452,17 +478,14 @@ func schedinit() {
 	gcinit()
 
 	sched.lastpoll = uint64(nanotime())
-	procs := int(ncpu)
+	procs := ncpu
+	if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
+		procs = n
+	}
 	if procs > _MaxGomaxprocs {
 		procs = _MaxGomaxprocs
 	}
-	if n := atoi(gogetenv("GOMAXPROCS")); n > 0 {
-		if n > _MaxGomaxprocs {
-			n = _MaxGomaxprocs
-		}
-		procs = n
-	}
-	if procresize(int32(procs)) != nil {
+	if procresize(procs) != nil {
 		throw("unknown runnable goroutine during bootstrap")
 	}
 
@@ -543,7 +566,7 @@ func ready(gp *g, traceskip int, next bool) {
 	// status is Gwaiting or Gscanwaiting, make Grunnable and put on runq
 	casgstatus(gp, _Gwaiting, _Grunnable)
 	runqput(_g_.m.p.ptr(), gp, next)
-	if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 { // TODO: fast atomic
+	if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 {
 		wakep()
 	}
 	_g_.m.locks--
@@ -903,7 +926,7 @@ func restartg(gp *g) {
 // in panic or being exited, this may not reliably stop all
 // goroutines.
 func stopTheWorld(reason string) {
-	semacquire(&worldsema, false)
+	semacquire(&worldsema, 0)
 	getg().m.preemptoff = reason
 	systemstack(stopTheWorldWithSema)
 }
@@ -926,7 +949,7 @@ var worldsema uint32 = 1
 // preemption first and then should stopTheWorldWithSema on the system
 // stack:
 //
-//	semacquire(&worldsema, false)
+//	semacquire(&worldsema, 0)
 //	m.preemptoff = "reason"
 //	systemstack(stopTheWorldWithSema)
 //
@@ -1273,8 +1296,10 @@ type cgothreadstart struct {
 // Can use p for allocation context if needed.
 // fn is recorded as the new m's m.mstartfn.
 //
-// This function it known to the compiler to inhibit the
-// go:nowritebarrierrec annotation because it uses P for allocation.
+// This function is allowed to have write barriers even if the caller
+// isn't because it borrows _p_.
+//
+//go:yeswritebarrierrec
 func allocm(_p_ *p, fn func()) *m {
 	_g_ := getg()
 	_g_.m.locks++ // disable GC because it can be called from sysmon
@@ -1427,6 +1452,7 @@ func oneNewExtraM() {
 	gp.syscallsp = gp.sched.sp
 	gp.stktopsp = gp.sched.sp
 	gp.gcscanvalid = true // fresh G, so no dequeueRescan necessary
+	gp.gcscandone = true
 	gp.gcRescan = -1
 	// malg returns status as Gidle, change to Gsyscall before adding to allg
 	// where GC will see it.
@@ -1438,7 +1464,7 @@ func oneNewExtraM() {
 	gp.lockedm = mp
 	gp.goid = int64(atomic.Xadd64(&sched.goidgen, 1))
 	if raceenabled {
-		gp.racectx = racegostart(funcPC(newextram))
+		gp.racectx = racegostart(funcPC(newextram) + sys.PCQuantum)
 	}
 	// put on allg for garbage collector
 	allgadd(gp)
@@ -1550,7 +1576,7 @@ func unlockextra(mp *m) {
 // Create a new m. It will start off with a call to fn, or else the scheduler.
 // fn needs to be static and not a heap allocated closure.
 // May run with m.p==nil, so write barriers are not allowed.
-//go:nowritebarrier
+//go:nowritebarrierrec
 func newm(fn func(), _p_ *p) {
 	mp := allocm(_p_, fn)
 	mp.nextp.set(_p_)
@@ -1614,7 +1640,7 @@ func mspinning() {
 // May run with m.p==nil, so write barriers are not allowed.
 // If spinning is set, the caller has incremented nmspinning and startm will
 // either decrement nmspinning or set m.spinning in the newly started M.
-//go:nowritebarrier
+//go:nowritebarrierrec
 func startm(_p_ *p, spinning bool) {
 	lock(&sched.lock)
 	if _p_ == nil {
@@ -1659,7 +1685,7 @@ func startm(_p_ *p, spinning bool) {
 
 // Hands off P from syscall or locked M.
 // Always runs without a P, so write barriers are not allowed.
-//go:nowritebarrier
+//go:nowritebarrierrec
 func handoffp(_p_ *p) {
 	// handoffp must start an M in any situation where
 	// findrunnable would return a G to run on _p_.
@@ -1752,7 +1778,7 @@ func stoplockedm() {
 
 // Schedules the locked m to run the locked gp.
 // May run during STW, so write barriers are not allowed.
-//go:nowritebarrier
+//go:nowritebarrierrec
 func startlockedm(gp *g) {
 	_g_ := getg()
 
@@ -1802,6 +1828,11 @@ func gcstopm() {
 // If inheritTime is true, gp inherits the remaining time in the
 // current time slice. Otherwise, it starts a new time slice.
 // Never returns.
+//
+// Write barriers are allowed because this is called immediately after
+// acquiring a P in several places.
+//
+//go:yeswritebarrierrec
 func execute(gp *g, inheritTime bool) {
 	_g_ := getg()
 
@@ -1901,7 +1932,7 @@ top:
 	// If number of spinning M's >= number of busy P's, block.
 	// This is necessary to prevent excessive CPU consumption
 	// when GOMAXPROCS>>1 but the program parallelism is low.
-	if !_g_.m.spinning && 2*atomic.Load(&sched.nmspinning) >= procs-atomic.Load(&sched.npidle) { // TODO: fast atomic
+	if !_g_.m.spinning && 2*atomic.Load(&sched.nmspinning) >= procs-atomic.Load(&sched.npidle) {
 		goto stop
 	}
 	if !_g_.m.spinning {
@@ -1909,7 +1940,7 @@ top:
 		atomic.Xadd(&sched.nmspinning, 1)
 	}
 	for i := 0; i < 4; i++ {
-		for enum := stealOrder.start(fastrand1()); !enum.done(); enum.next() {
+		for enum := stealOrder.start(fastrand()); !enum.done(); enum.next() {
 			if sched.gcwaiting != 0 {
 				goto top
 			}
@@ -1992,6 +2023,26 @@ stop:
 		}
 	}
 
+	// Check for idle-priority GC work again.
+	if gcBlackenEnabled != 0 && gcMarkWorkAvailable(nil) {
+		lock(&sched.lock)
+		_p_ = pidleget()
+		if _p_ != nil && _p_.gcBgMarkWorker == 0 {
+			pidleput(_p_)
+			_p_ = nil
+		}
+		unlock(&sched.lock)
+		if _p_ != nil {
+			acquirep(_p_)
+			if wasSpinning {
+				_g_.m.spinning = true
+				atomic.Xadd(&sched.nmspinning, 1)
+			}
+			// Go back to idle GC check.
+			goto stop
+		}
+	}
+
 	// poll network
 	if netpollinited() && atomic.Xchg64(&sched.lastpoll, 0) != 0 {
 		if _g_.m.p != 0 {
@@ -2022,6 +2073,27 @@ stop:
 	goto top
 }
 
+// pollWork returns true if there is non-background work this P could
+// be doing. This is a fairly lightweight check to be used for
+// background work loops, like idle GC. It checks a subset of the
+// conditions checked by the actual scheduler.
+func pollWork() bool {
+	if sched.runqsize != 0 {
+		return true
+	}
+	p := getg().m.p.ptr()
+	if !runqempty(p) {
+		return true
+	}
+	if netpollinited() && sched.lastpoll != 0 {
+		if gp := netpoll(false); gp != nil {
+			injectglist(gp)
+			return true
+		}
+	}
+	return false
+}
+
 func resetspinning() {
 	_g_ := getg()
 	if !_g_.m.spinning {
@@ -2147,8 +2219,8 @@ top:
 func dropg() {
 	_g_ := getg()
 
-	_g_.m.curg.m = nil
-	_g_.m.curg = nil
+	setMNoWB(&_g_.m.curg.m, nil)
+	setGNoWB(&_g_.m.curg, nil)
 }
 
 func parkunlock_c(gp *g, lock unsafe.Pointer) bool {
@@ -2257,8 +2329,14 @@ func goexit0(gp *g) {
 	schedule()
 }
 
+// save updates getg().sched to refer to pc and sp so that a following
+// gogo will restore pc and sp.
+//
+// save must not have write barriers because invoking a write barrier
+// can clobber getg().sched.
+//
 //go:nosplit
-//go:nowritebarrier
+//go:nowritebarrierrec
 func save(pc, sp uintptr) {
 	_g_ := getg()
 
@@ -2266,8 +2344,13 @@ func save(pc, sp uintptr) {
 	_g_.sched.sp = sp
 	_g_.sched.lr = 0
 	_g_.sched.ret = 0
-	_g_.sched.ctxt = nil
 	_g_.sched.g = guintptr(unsafe.Pointer(_g_))
+	// We need to ensure ctxt is zero, but can't have a write
+	// barrier here. However, it should always already be zero.
+	// Assert that.
+	if _g_.sched.ctxt != nil {
+		badctxt()
+	}
 }
 
 // The goroutine g is about to enter a system call.
@@ -2341,7 +2424,7 @@ func reentersyscall(pc, sp uintptr) {
 		save(pc, sp)
 	}
 
-	if atomic.Load(&sched.sysmonwait) != 0 { // TODO: fast atomic
+	if atomic.Load(&sched.sysmonwait) != 0 {
 		systemstack(entersyscall_sysmon)
 		save(pc, sp)
 	}
@@ -2457,7 +2540,11 @@ func entersyscallblock_handoff() {
 // Arrange for it to run on a cpu again.
 // This is called only from the go syscall library, not
 // from the low-level system calls used by the runtime.
+//
+// Write barriers are not allowed because our P may have been stolen.
+//
 //go:nosplit
+//go:nowritebarrierrec
 func exitsyscall(dummy int32) {
 	_g_ := getg()
 
@@ -2550,22 +2637,7 @@ func exitsyscallfast() bool {
 	// Try to re-acquire the last P.
 	if _g_.m.p != 0 && _g_.m.p.ptr().status == _Psyscall && atomic.Cas(&_g_.m.p.ptr().status, _Psyscall, _Prunning) {
 		// There's a cpu for us, so we can run.
-		_g_.m.mcache = _g_.m.p.ptr().mcache
-		_g_.m.p.ptr().m.set(_g_.m)
-		if _g_.m.syscalltick != _g_.m.p.ptr().syscalltick {
-			if trace.enabled {
-				// The p was retaken and then enter into syscall again (since _g_.m.syscalltick has changed).
-				// traceGoSysBlock for this syscall was already emitted,
-				// but here we effectively retake the p from the new syscall running on the same p.
-				systemstack(func() {
-					// Denote blocking of the new syscall.
-					traceGoSysBlock(_g_.m.p.ptr())
-					// Denote completion of the current syscall.
-					traceGoSysExit(0)
-				})
-			}
-			_g_.m.p.ptr().syscalltick++
-		}
+		exitsyscallfast_reacquired()
 		return true
 	}
 
@@ -2595,6 +2667,35 @@ func exitsyscallfast() bool {
 	return false
 }
 
+// exitsyscallfast_reacquired is the exitsyscall path on which this G
+// has successfully reacquired the P it was running on before the
+// syscall.
+//
+// This function is allowed to have write barriers because exitsyscall
+// has acquired a P at this point.
+//
+//go:yeswritebarrierrec
+//go:nosplit
+func exitsyscallfast_reacquired() {
+	_g_ := getg()
+	_g_.m.mcache = _g_.m.p.ptr().mcache
+	_g_.m.p.ptr().m.set(_g_.m)
+	if _g_.m.syscalltick != _g_.m.p.ptr().syscalltick {
+		if trace.enabled {
+			// The p was retaken and then enter into syscall again (since _g_.m.syscalltick has changed).
+			// traceGoSysBlock for this syscall was already emitted,
+			// but here we effectively retake the p from the new syscall running on the same p.
+			systemstack(func() {
+				// Denote blocking of the new syscall.
+				traceGoSysBlock(_g_.m.p.ptr())
+				// Denote completion of the current syscall.
+				traceGoSysExit(0)
+			})
+		}
+		_g_.m.p.ptr().syscalltick++
+	}
+}
+
 func exitsyscallfast_pidle() bool {
 	lock(&sched.lock)
 	_p_ := pidleget()
@@ -2612,6 +2713,8 @@ func exitsyscallfast_pidle() bool {
 
 // exitsyscall slow path on g0.
 // Failed to acquire P, enqueue gp as runnable.
+//
+//go:nowritebarrierrec
 func exitsyscall0(gp *g) {
 	_g_ := getg()
 
@@ -2758,13 +2861,28 @@ func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr
 	spArg := sp
 	if usesLR {
 		// caller's LR
-		*(*unsafe.Pointer)(unsafe.Pointer(sp)) = nil
+		*(*uintptr)(unsafe.Pointer(sp)) = 0
 		prepGoExitFrame(sp)
 		spArg += sys.MinFrameSize
 	}
-	memmove(unsafe.Pointer(spArg), unsafe.Pointer(argp), uintptr(narg))
+	if narg > 0 {
+		memmove(unsafe.Pointer(spArg), unsafe.Pointer(argp), uintptr(narg))
+		// This is a stack-to-stack copy. If write barriers
+		// are enabled and the source stack is grey (the
+		// destination is always black), then perform a
+		// barrier copy. We do this *after* the memmove
+		// because the destination stack may have garbage on
+		// it.
+		if writeBarrier.needed && !_g_.m.curg.gcscandone {
+			f := findfunc(fn.fn)
+			stkmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
+			// We're in the prologue, so it's always stack map index 0.
+			bv := stackmapdata(stkmap, 0)
+			bulkBarrierBitmap(spArg, spArg, uintptr(narg), 0, bv.bytedata)
+		}
+	}
 
-	memclr(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))
+	memclrNoHeapPointers(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))
 	newg.sched.sp = sp
 	newg.stktopsp = sp
 	newg.sched.pc = funcPC(goexit) + sys.PCQuantum // +PCQuantum so that previous instruction is in same function
@@ -2806,7 +2924,7 @@ func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr
 	}
 	runqput(_p_, newg, true)
 
-	if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 && unsafe.Pointer(fn.fn) != unsafe.Pointer(funcPC(main)) { // TODO: fast atomic
+	if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 && runtimeInitTime != 0 {
 		wakep()
 	}
 	_g_.m.locks--
@@ -3035,7 +3153,12 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
 	}
 
 	// Profiling runs concurrently with GC, so it must not allocate.
-	mp.mallocing++
+	// Set a trap in case the code does allocate.
+	// Note that on windows, one thread takes profiles of all the
+	// other threads, so mp is usually not getg().m.
+	// In fact mp may not even be stopped.
+	// See golang.org/issue/17165.
+	getg().m.mallocing++
 
 	// Define that a "user g" is a user-created goroutine, and a "system g"
 	// is one that is m->g0 or m->gsignal.
@@ -3185,7 +3308,7 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
 		}
 		atomic.Store(&prof.lock, 0)
 	}
-	mp.mallocing--
+	getg().m.mallocing--
 }
 
 // If the signal handler receives a SIGPROF signal on a non-Go thread,
@@ -3194,7 +3317,8 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
 var sigprofCallers cgoCallers
 var sigprofCallersUse uint32
 
-// Called if we receive a SIGPROF signal on a non-Go thread.
+// sigprofNonGo is called if we receive a SIGPROF signal on a non-Go thread,
+// and the signal handler collected a stack trace in sigprofCallers.
 // When this is called, sigprofCallersUse will be non-zero.
 // g is nil, and what we can do is very limited.
 //go:nosplit
@@ -3207,17 +3331,41 @@ func sigprofNonGo() {
 		}
 
 		// Simple cas-lock to coordinate with setcpuprofilerate.
-		if atomic.Cas(&prof.lock, 0, 1) {
-			if prof.hz != 0 {
-				cpuprof.addNonGo(sigprofCallers[:n])
-			}
-			atomic.Store(&prof.lock, 0)
+		for !atomic.Cas(&prof.lock, 0, 1) {
+			osyield()
 		}
+		if prof.hz != 0 {
+			cpuprof.addNonGo(sigprofCallers[:n])
+		}
+		atomic.Store(&prof.lock, 0)
 	}
 
 	atomic.Store(&sigprofCallersUse, 0)
 }
 
+// sigprofNonGoPC is called when a profiling signal arrived on a
+// non-Go thread and we have a single PC value, not a stack trace.
+// g is nil, and what we can do is very limited.
+//go:nosplit
+//go:nowritebarrierrec
+func sigprofNonGoPC(pc uintptr) {
+	if prof.hz != 0 {
+		pc := []uintptr{
+			pc,
+			funcPC(_ExternalCode) + sys.PCQuantum,
+		}
+
+		// Simple cas-lock to coordinate with setcpuprofilerate.
+		for !atomic.Cas(&prof.lock, 0, 1) {
+			osyield()
+		}
+		if prof.hz != 0 {
+			cpuprof.addNonGo(pc)
+		}
+		atomic.Store(&prof.lock, 0)
+	}
+}
+
 // Reports whether a function will set the SP
 // to an absolute value. Important that
 // we don't traceback when these are at the bottom
@@ -3427,7 +3575,13 @@ func procresize(nprocs int32) *p {
 }
 
 // Associate p and the current m.
+//
+// This function is allowed to have write barriers even if the caller
+// isn't because it immediately acquires _p_.
+//
+//go:yeswritebarrierrec
 func acquirep(_p_ *p) {
+	// Do the part that isn't allowed to have write barriers.
 	acquirep1(_p_)
 
 	// have p; write barriers now allowed
@@ -3439,8 +3593,11 @@ func acquirep(_p_ *p) {
 	}
 }
 
-// May run during STW, so write barriers are not allowed.
-//go:nowritebarrier
+// acquirep1 is the first step of acquirep, which actually acquires
+// _p_. This is broken out so we can disallow write barriers for this
+// part, since we don't yet have a P.
+//
+//go:nowritebarrierrec
 func acquirep1(_p_ *p) {
 	_g_ := getg()
 
@@ -3604,7 +3761,7 @@ func sysmon() {
 			delay = 10 * 1000
 		}
 		usleep(delay)
-		if debug.schedtrace <= 0 && (sched.gcwaiting != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs)) { // TODO: fast atomic
+		if debug.schedtrace <= 0 && (sched.gcwaiting != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs)) {
 			lock(&sched.lock)
 			if atomic.Load(&sched.gcwaiting) != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs) {
 				atomic.Store(&sched.sysmonwait, 1)
@@ -3878,7 +4035,7 @@ func schedtrace(detailed bool) {
 // Put mp on midle list.
 // Sched must be locked.
 // May run during STW, so write barriers are not allowed.
-//go:nowritebarrier
+//go:nowritebarrierrec
 func mput(mp *m) {
 	mp.schedlink = sched.midle
 	sched.midle.set(mp)
@@ -3889,7 +4046,7 @@ func mput(mp *m) {
 // Try to get an m from midle list.
 // Sched must be locked.
 // May run during STW, so write barriers are not allowed.
-//go:nowritebarrier
+//go:nowritebarrierrec
 func mget() *m {
 	mp := sched.midle.ptr()
 	if mp != nil {
@@ -3902,7 +4059,7 @@ func mget() *m {
 // Put gp on the global runnable queue.
 // Sched must be locked.
 // May run during STW, so write barriers are not allowed.
-//go:nowritebarrier
+//go:nowritebarrierrec
 func globrunqput(gp *g) {
 	gp.schedlink = 0
 	if sched.runqtail != 0 {
@@ -3917,7 +4074,7 @@ func globrunqput(gp *g) {
 // Put gp at the head of the global runnable queue.
 // Sched must be locked.
 // May run during STW, so write barriers are not allowed.
-//go:nowritebarrier
+//go:nowritebarrierrec
 func globrunqputhead(gp *g) {
 	gp.schedlink = sched.runqhead
 	sched.runqhead.set(gp)
@@ -3977,7 +4134,7 @@ func globrunqget(_p_ *p, max int32) *g {
 // Put p to on _Pidle list.
 // Sched must be locked.
 // May run during STW, so write barriers are not allowed.
-//go:nowritebarrier
+//go:nowritebarrierrec
 func pidleput(_p_ *p) {
 	if !runqempty(_p_) {
 		throw("pidleput: P has non-empty run queue")
@@ -3990,7 +4147,7 @@ func pidleput(_p_ *p) {
 // Try get a p from _Pidle list.
 // Sched must be locked.
 // May run during STW, so write barriers are not allowed.
-//go:nowritebarrier
+//go:nowritebarrierrec
 func pidleget() *p {
 	_p_ := sched.pidle.ptr()
 	if _p_ != nil {
@@ -4034,7 +4191,7 @@ const randomizeScheduler = raceenabled
 // If the run queue is full, runnext puts g on the global queue.
 // Executed only by the owner P.
 func runqput(_p_ *p, gp *g, next bool) {
-	if randomizeScheduler && next && fastrand1()%2 == 0 {
+	if randomizeScheduler && next && fastrand()%2 == 0 {
 		next = false
 	}
 
@@ -4087,7 +4244,7 @@ func runqputslow(_p_ *p, gp *g, h, t uint32) bool {
 
 	if randomizeScheduler {
 		for i := uint32(1); i <= n; i++ {
-			j := fastrand1() % (i + 1)
+			j := fastrand() % (i + 1)
 			batch[i], batch[j] = batch[j], batch[i]
 		}
 	}
@@ -4212,7 +4369,11 @@ func runqsteal(_p_, p2 *p, stealRunNextG bool) *g {
 func setMaxThreads(in int) (out int) {
 	lock(&sched.lock)
 	out = int(sched.maxmcount)
-	sched.maxmcount = int32(in)
+	if in > 0x7fffffff { // MaxInt32
+		sched.maxmcount = 0x7fffffff
+	} else {
+		sched.maxmcount = int32(in)
+	}
 	checkmcount()
 	unlock(&sched.lock)
 	return
diff --git a/src/runtime/race.go b/src/runtime/race.go
index 42da936..d8483c0 100644
--- a/src/runtime/race.go
+++ b/src/runtime/race.go
@@ -20,6 +20,12 @@ func RaceWriteRange(addr unsafe.Pointer, len int)
 func RaceSemacquire(s *uint32)
 func RaceSemrelease(s *uint32)
 
+func RaceErrors() int {
+	var n uint64
+	racecall(&__tsan_report_count, uintptr(unsafe.Pointer(&n)), 0, 0, 0)
+	return int(n)
+}
+
 // private interface for the runtime
 const raceenabled = true
 
@@ -91,23 +97,23 @@ func racecallback(cmd uintptr, ctx unsafe.Pointer) {
 }
 
 func raceSymbolizeCode(ctx *symbolizeCodeContext) {
-	f := findfunc(ctx.pc)
-	if f == nil {
-		ctx.fn = &qq[0]
-		ctx.file = &dash[0]
-		ctx.line = 0
-		ctx.off = ctx.pc
-		ctx.res = 1
-		return
+	f := FuncForPC(ctx.pc)
+	if f != nil {
+		file, line := f.FileLine(ctx.pc)
+		if line != 0 {
+			ctx.fn = cfuncname(f.raw())
+			ctx.line = uintptr(line)
+			ctx.file = &bytes(file)[0] // assume NUL-terminated
+			ctx.off = ctx.pc - f.Entry()
+			ctx.res = 1
+			return
+		}
 	}
-
-	ctx.fn = cfuncname(f)
-	file, line := funcline(f, ctx.pc)
-	ctx.line = uintptr(line)
-	ctx.file = &bytes(file)[0] // assume NUL-terminated
-	ctx.off = ctx.pc - f.entry
+	ctx.fn = &qq[0]
+	ctx.file = &dash[0]
+	ctx.line = 0
+	ctx.off = ctx.pc
 	ctx.res = 1
-	return
 }
 
 type symbolizeDataContext struct {
@@ -176,6 +182,9 @@ var __tsan_go_ignore_sync_begin byte
 //go:linkname __tsan_go_ignore_sync_end __tsan_go_ignore_sync_end
 var __tsan_go_ignore_sync_end byte
 
+//go:linkname __tsan_report_count __tsan_report_count
+var __tsan_report_count byte
+
 // Mimic what cmd/cgo would do.
 //go:cgo_import_static __tsan_init
 //go:cgo_import_static __tsan_fini
@@ -192,6 +201,7 @@ var __tsan_go_ignore_sync_end byte
 //go:cgo_import_static __tsan_release_merge
 //go:cgo_import_static __tsan_go_ignore_sync_begin
 //go:cgo_import_static __tsan_go_ignore_sync_end
+//go:cgo_import_static __tsan_report_count
 
 // These are called from race_amd64.s.
 //go:cgo_import_static __tsan_read
diff --git a/src/runtime/race/README b/src/runtime/race/README
index 95e241c..398b22f 100644
--- a/src/runtime/race/README
+++ b/src/runtime/race/README
@@ -4,4 +4,4 @@ the LLVM project (http://llvm.org/git/compiler-rt.git).
 
 To update the .syso files use golang.org/x/build/cmd/racebuild.
 
-Current runtime is built on rev e35e7c00b5c7e7ee5e24d537b80cb0d34cebb038.
+Current runtime is built on rev 68e1532492f9b3fce0e9024f3c31411105965b11.
diff --git a/src/runtime/race/output_test.go b/src/runtime/race/output_test.go
index 5157f7e..587540f 100644
--- a/src/runtime/race/output_test.go
+++ b/src/runtime/race/output_test.go
@@ -7,17 +7,23 @@
 package race_test
 
 import (
+	"internal/testenv"
 	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"regexp"
+	"runtime"
 	"strings"
 	"testing"
 )
 
 func TestOutput(t *testing.T) {
 	for _, test := range tests {
+		if test.goos != "" && test.goos != runtime.GOOS {
+			t.Logf("test %v runs only on %v, skipping: ", test.name, test.goos)
+			continue
+		}
 		dir, err := ioutil.TempDir("", "go-build")
 		if err != nil {
 			t.Fatalf("failed to create temp directory: %v", err)
@@ -41,7 +47,7 @@ func TestOutput(t *testing.T) {
 			t.Fatalf("failed to close file: %v", err)
 		}
 		// Pass -l to the compiler to test stack traces.
-		cmd := exec.Command("go", test.run, "-race", "-gcflags=-l", src)
+		cmd := exec.Command(testenv.GoToolPath(t), test.run, "-race", "-gcflags=-l", src)
 		// GODEBUG spoils program output, GOMAXPROCS makes it flaky.
 		for _, env := range os.Environ() {
 			if strings.HasPrefix(env, "GODEBUG=") ||
@@ -66,11 +72,12 @@ func TestOutput(t *testing.T) {
 var tests = []struct {
 	name   string
 	run    string
+	goos   string
 	gorace string
 	source string
 	re     string
 }{
-	{"simple", "run", "atexit_sleep_ms=0", `
+	{"simple", "run", "", "atexit_sleep_ms=0", `
 package main
 import "time"
 func main() {
@@ -115,7 +122,7 @@ Found 1 data race\(s\)
 exit status 66
 `},
 
-	{"exitcode", "run", "atexit_sleep_ms=0 exitcode=13", `
+	{"exitcode", "run", "", "atexit_sleep_ms=0 exitcode=13", `
 package main
 func main() {
 	done := make(chan bool)
@@ -129,7 +136,7 @@ func main() {
 }
 `, `exit status 13`},
 
-	{"strip_path_prefix", "run", "atexit_sleep_ms=0 strip_path_prefix=/main.", `
+	{"strip_path_prefix", "run", "", "atexit_sleep_ms=0 strip_path_prefix=/main.", `
 package main
 func main() {
 	done := make(chan bool)
@@ -145,7 +152,7 @@ func main() {
       go:7 \+0x[0-9,a-f]+
 `},
 
-	{"halt_on_error", "run", "atexit_sleep_ms=0 halt_on_error=1", `
+	{"halt_on_error", "run", "", "atexit_sleep_ms=0 halt_on_error=1", `
 package main
 func main() {
 	done := make(chan bool)
@@ -162,7 +169,7 @@ func main() {
 exit status 66
 `},
 
-	{"test_fails_on_race", "test", "atexit_sleep_ms=0", `
+	{"test_fails_on_race", "test", "", "atexit_sleep_ms=0", `
 package main_test
 import "testing"
 func TestFail(t *testing.T) {
@@ -177,11 +184,11 @@ func TestFail(t *testing.T) {
 }
 `, `
 ==================
-PASS
-Found 1 data race\(s\)
+--- FAIL: TestFail \(0...s\)
+.*testing.go:.*: race detected during execution of test
 FAIL`},
 
-	{"slicebytetostring_pc", "run", "atexit_sleep_ms=0", `
+	{"slicebytetostring_pc", "run", "", "atexit_sleep_ms=0", `
 package main
 func main() {
 	done := make(chan string)
@@ -197,4 +204,57 @@ func main() {
       .*/runtime/string\.go:.*
   main\.main\.func1\(\)
       .*/main.go:7`},
+
+	// Test for http://golang.org/issue/17190
+	{"external_cgo_thread", "run", "linux", "atexit_sleep_ms=0", `
+package main
+
+/*
+#include <pthread.h>
+typedef struct cb {
+        int foo;
+} cb;
+extern void goCallback();
+static inline void *threadFunc(void *p) {
+	goCallback();
+	return 0;
+}
+static inline void startThread(cb* c) {
+	pthread_t th;
+	pthread_create(&th, 0, threadFunc, 0);
+}
+*/
+import "C"
+
+import "time"
+
+var racy int
+
+//export goCallback
+func goCallback() {
+	racy++
+}
+
+func main() {
+	var c C.cb
+	C.startThread(&c)
+	time.Sleep(time.Second)
+	racy++
+}
+`, `==================
+WARNING: DATA RACE
+Read at 0x[0-9,a-f]+ by main goroutine:
+  main\.main\(\)
+      .*/main\.go:34 \+0x[0-9,a-f]+
+
+Previous write at 0x[0-9,a-f]+ by goroutine [0-9]:
+  main\.goCallback\(\)
+      .*/main\.go:27 \+0x[0-9,a-f]+
+  main._cgoexpwrap_[0-9a-z]+_goCallback\(\)
+      .*/_cgo_gotypes\.go:[0-9]+ \+0x[0-9,a-f]+
+
+Goroutine [0-9] \(running\) created at:
+  runtime\.newextram\(\)
+      .*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
+==================`},
 }
diff --git a/src/runtime/race/race_darwin_amd64.syso b/src/runtime/race/race_darwin_amd64.syso
index c19740f..89c7f57 100644
Binary files a/src/runtime/race/race_darwin_amd64.syso and b/src/runtime/race/race_darwin_amd64.syso differ
diff --git a/src/runtime/race/race_freebsd_amd64.syso b/src/runtime/race/race_freebsd_amd64.syso
index df1bc26..6312ce8 100644
Binary files a/src/runtime/race/race_freebsd_amd64.syso and b/src/runtime/race/race_freebsd_amd64.syso differ
diff --git a/src/runtime/race/race_linux_amd64.syso b/src/runtime/race/race_linux_amd64.syso
index 1740330..3795520 100644
Binary files a/src/runtime/race/race_linux_amd64.syso and b/src/runtime/race/race_linux_amd64.syso differ
diff --git a/src/runtime/race/race_test.go b/src/runtime/race/race_test.go
index 53ec74c..8cdf52d 100644
--- a/src/runtime/race/race_test.go
+++ b/src/runtime/race/race_test.go
@@ -15,6 +15,7 @@ import (
 	"bufio"
 	"bytes"
 	"fmt"
+	"internal/testenv"
 	"io"
 	"log"
 	"math/rand"
@@ -43,7 +44,7 @@ const (
 )
 
 func TestRace(t *testing.T) {
-	testOutput, err := runTests()
+	testOutput, err := runTests(t)
 	if err != nil {
 		t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput))
 	}
@@ -141,19 +142,21 @@ func processLog(testName string, tsanLog []string) string {
 // runTests assures that the package and its dependencies is
 // built with instrumentation enabled and returns the output of 'go test'
 // which includes possible data race reports from ThreadSanitizer.
-func runTests() ([]byte, error) {
+func runTests(t *testing.T) ([]byte, error) {
 	tests, err := filepath.Glob("./testdata/*_test.go")
 	if err != nil {
 		return nil, err
 	}
 	args := []string{"test", "-race", "-v"}
 	args = append(args, tests...)
-	cmd := exec.Command("go", args...)
+	cmd := exec.Command(testenv.GoToolPath(t), args...)
 	// The following flags turn off heuristics that suppress seemingly identical reports.
 	// It is required because the tests contain a lot of data races on the same addresses
 	// (the tests are simple and the memory is constantly reused).
 	for _, env := range os.Environ() {
-		if strings.HasPrefix(env, "GOMAXPROCS=") || strings.HasPrefix(env, "GODEBUG=") {
+		if strings.HasPrefix(env, "GOMAXPROCS=") ||
+			strings.HasPrefix(env, "GODEBUG=") ||
+			strings.HasPrefix(env, "GORACE=") {
 			continue
 		}
 		cmd.Env = append(cmd.Env, env)
@@ -170,9 +173,11 @@ func runTests() ([]byte, error) {
 	// (that's what is done for C++ ThreadSanitizer tests). This is issue #14119.
 	cmd.Env = append(cmd.Env,
 		"GOMAXPROCS=1",
-		"GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0",
+		"GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0",
 	)
-	return cmd.CombinedOutput()
+	// There are races: we expect tests to fail and the exit code to be non-zero.
+	out, _ := cmd.CombinedOutput()
+	return out, nil
 }
 
 func TestIssue8102(t *testing.T) {
diff --git a/src/runtime/race/race_windows_amd64.syso b/src/runtime/race/race_windows_amd64.syso
index fd93959..b85f5d6 100644
Binary files a/src/runtime/race/race_windows_amd64.syso and b/src/runtime/race/race_windows_amd64.syso differ
diff --git a/src/runtime/race/testdata/cgo_test.go b/src/runtime/race/testdata/cgo_test.go
index ba7e7b5..211ef7d 100644
--- a/src/runtime/race/testdata/cgo_test.go
+++ b/src/runtime/race/testdata/cgo_test.go
@@ -5,13 +5,14 @@
 package race_test
 
 import (
+	"internal/testenv"
 	"os"
 	"os/exec"
 	"testing"
 )
 
 func TestNoRaceCgoSync(t *testing.T) {
-	cmd := exec.Command("go", "run", "-race", "cgo_test_main.go")
+	cmd := exec.Command(testenv.GoToolPath(t), "run", "-race", "cgo_test_main.go")
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
 	if err := cmd.Run(); err != nil {
diff --git a/src/runtime/race/testdata/pool_test.go b/src/runtime/race/testdata/pool_test.go
new file mode 100644
index 0000000..161f4b7
--- /dev/null
+++ b/src/runtime/race/testdata/pool_test.go
@@ -0,0 +1,47 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+	"sync"
+	"testing"
+	"time"
+)
+
+func TestRacePool(t *testing.T) {
+	// Pool randomly drops the argument on the floor during Put.
+	// Repeat so that at least one iteration gets reuse.
+	for i := 0; i < 10; i++ {
+		c := make(chan int)
+		p := &sync.Pool{New: func() interface{} { return make([]byte, 10) }}
+		x := p.Get().([]byte)
+		x[0] = 1
+		p.Put(x)
+		go func() {
+			y := p.Get().([]byte)
+			y[0] = 2
+			c <- 1
+		}()
+		x[0] = 3
+		<-c
+	}
+}
+
+func TestNoRacePool(t *testing.T) {
+	for i := 0; i < 10; i++ {
+		p := &sync.Pool{New: func() interface{} { return make([]byte, 10) }}
+		x := p.Get().([]byte)
+		x[0] = 1
+		p.Put(x)
+		go func() {
+			y := p.Get().([]byte)
+			y[0] = 2
+			p.Put(y)
+		}()
+		time.Sleep(100 * time.Millisecond)
+		x = p.Get().([]byte)
+		x[0] = 3
+	}
+}
diff --git a/src/runtime/race/testdata/reflect_test.go b/src/runtime/race/testdata/reflect_test.go
new file mode 100644
index 0000000..b567400
--- /dev/null
+++ b/src/runtime/race/testdata/reflect_test.go
@@ -0,0 +1,46 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package race_test
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestRaceReflectRW(t *testing.T) {
+	ch := make(chan bool, 1)
+	i := 0
+	v := reflect.ValueOf(&i)
+	go func() {
+		v.Elem().Set(reflect.ValueOf(1))
+		ch <- true
+	}()
+	_ = v.Elem().Int()
+	<-ch
+}
+
+func TestRaceReflectWW(t *testing.T) {
+	ch := make(chan bool, 1)
+	i := 0
+	v := reflect.ValueOf(&i)
+	go func() {
+		v.Elem().Set(reflect.ValueOf(1))
+		ch <- true
+	}()
+	v.Elem().Set(reflect.ValueOf(2))
+	<-ch
+}
+
+func TestRaceReflectCopyWW(t *testing.T) {
+	ch := make(chan bool, 1)
+	a := make([]byte, 2)
+	v := reflect.ValueOf(a)
+	go func() {
+		reflect.Copy(v, v)
+		ch <- true
+	}()
+	reflect.Copy(v, v)
+	<-ch
+}
diff --git a/src/runtime/rt0_android_amd64.s b/src/runtime/rt0_android_amd64.s
index 9af6cab..6420c9f 100644
--- a/src/runtime/rt0_android_amd64.s
+++ b/src/runtime/rt0_android_amd64.s
@@ -17,17 +17,10 @@ TEXT _rt0_amd64_android_lib(SB),NOSPLIT,$0
 	JMP	AX
 
 DATA _rt0_amd64_android_argv+0x00(SB)/8,$_rt0_amd64_android_argv0(SB)
-DATA _rt0_amd64_android_argv+0x08(SB)/8,$0
-DATA _rt0_amd64_android_argv+0x10(SB)/8,$0
-DATA _rt0_amd64_android_argv+0x18(SB)/8,$15  // AT_PLATFORM
-DATA _rt0_amd64_android_argv+0x20(SB)/8,$_rt0_amd64_android_auxv0(SB)
-DATA _rt0_amd64_android_argv+0x28(SB)/8,$0
-GLOBL _rt0_amd64_android_argv(SB),NOPTR,$0x30
-
-// TODO: AT_HWCAP necessary? If so, what value?
+DATA _rt0_amd64_android_argv+0x08(SB)/8,$0 // end argv
+DATA _rt0_amd64_android_argv+0x10(SB)/8,$0 // end envv
+DATA _rt0_amd64_android_argv+0x18(SB)/8,$0 // end auxv
+GLOBL _rt0_amd64_android_argv(SB),NOPTR,$0x20
 
 DATA _rt0_amd64_android_argv0(SB)/8, $"gojni"
 GLOBL _rt0_amd64_android_argv0(SB),RODATA,$8
-
-DATA _rt0_amd64_android_auxv0(SB)/8, $"x86_64"
-GLOBL _rt0_amd64_android_auxv0(SB),RODATA,$8
diff --git a/src/runtime/rt0_android_arm.s b/src/runtime/rt0_android_arm.s
index 8571253..189e290 100644
--- a/src/runtime/rt0_android_arm.s
+++ b/src/runtime/rt0_android_arm.s
@@ -19,17 +19,10 @@ TEXT _rt0_arm_android_lib(SB),NOSPLIT,$0
 	RET
 
 DATA _rt0_arm_android_argv+0x00(SB)/4,$_rt0_arm_android_argv0(SB)
-DATA _rt0_arm_android_argv+0x04(SB)/4,$0
-DATA _rt0_arm_android_argv+0x08(SB)/4,$0
-DATA _rt0_arm_android_argv+0x0C(SB)/4,$15      // AT_PLATFORM
-DATA _rt0_arm_android_argv+0x10(SB)/4,$_rt0_arm_android_auxv0(SB)
-DATA _rt0_arm_android_argv+0x14(SB)/4,$16      // AT_HWCAP
-DATA _rt0_arm_android_argv+0x18(SB)/4,$0x2040  // HWCAP_VFP | HWCAP_VFPv3
-DATA _rt0_arm_android_argv+0x1C(SB)/4,$0
-GLOBL _rt0_arm_android_argv(SB),NOPTR,$0x20
+DATA _rt0_arm_android_argv+0x04(SB)/4,$0 // end argv
+DATA _rt0_arm_android_argv+0x08(SB)/4,$0 // end envv
+DATA _rt0_arm_android_argv+0x0c(SB)/4,$0 // end auxv
+GLOBL _rt0_arm_android_argv(SB),NOPTR,$0x10
 
 DATA _rt0_arm_android_argv0(SB)/8, $"gojni"
 GLOBL _rt0_arm_android_argv0(SB),RODATA,$8
-
-DATA _rt0_arm_android_auxv0(SB)/4, $"v7l"
-GLOBL _rt0_arm_android_auxv0(SB),RODATA,$4
diff --git a/src/runtime/rt0_android_arm64.s b/src/runtime/rt0_android_arm64.s
index 582fc5a..9378213 100644
--- a/src/runtime/rt0_android_arm64.s
+++ b/src/runtime/rt0_android_arm64.s
@@ -17,9 +17,10 @@ TEXT _rt0_arm64_android_lib(SB),NOSPLIT,$-8
 	B	(R4)
 
 DATA _rt0_arm64_android_argv+0x00(SB)/8,$_rt0_arm64_android_argv0(SB)
-DATA _rt0_arm64_android_argv+0x08(SB)/8,$0
-DATA _rt0_arm64_android_argv+0x10(SB)/8,$0
-GLOBL _rt0_arm64_android_argv(SB),NOPTR,$0x18
+DATA _rt0_arm64_android_argv+0x08(SB)/8,$0 // end argv
+DATA _rt0_arm64_android_argv+0x10(SB)/8,$0 // end envv
+DATA _rt0_arm64_android_argv+0x18(SB)/8,$0 // end auxv
+GLOBL _rt0_arm64_android_argv(SB),NOPTR,$0x20
 
 DATA _rt0_arm64_android_argv0(SB)/8, $"gojni"
 GLOBL _rt0_arm64_android_argv0(SB),RODATA,$8
diff --git a/src/runtime/rt0_linux_mipsx.s b/src/runtime/rt0_linux_mipsx.s
new file mode 100644
index 0000000..5e8c5c3
--- /dev/null
+++ b/src/runtime/rt0_linux_mipsx.s
@@ -0,0 +1,27 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+// +build mips mipsle
+
+#include "textflag.h"
+
+TEXT _rt0_mips_linux(SB),NOSPLIT,$0
+	JMP	_main<>(SB)
+
+TEXT _rt0_mipsle_linux(SB),NOSPLIT,$0
+	JMP	_main<>(SB)
+
+TEXT _main<>(SB),NOSPLIT,$-4
+	// In a statically linked binary, the stack contains argc,
+	// argv as argc string pointers followed by a NULL, envv as a
+	// sequence of string pointers followed by a NULL, and auxv.
+	// There is no TLS base pointer.
+	MOVW	0(R29), R1	// argc
+	ADD	$4, R29, R2	// argv
+	JMP	main(SB)
+
+TEXT main(SB),NOSPLIT,$-4
+	MOVW	$runtime·rt0_go(SB), R4
+	JMP	(R4)
diff --git a/src/runtime/rune.go b/src/runtime/rune.go
deleted file mode 100644
index 99c38e0..0000000
--- a/src/runtime/rune.go
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * The authors of this software are Rob Pike and Ken Thompson.
- *              Copyright (c) 2002 by Lucent Technologies.
- *              Portions Copyright 2009 The Go Authors. All rights reserved.
- * Permission to use, copy, modify, and distribute this software for any
- * purpose without fee is hereby granted, provided that this entire notice
- * is included in all copies of any software which is or includes a copy
- * or modification of this software and in all copies of the supporting
- * documentation for such software.
- * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
- * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
- * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
- * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
- */
-
-/*
- * This code is copied, with slight editing due to type differences,
- * from a subset of ../lib9/utf/rune.c [which no longer exists]
- */
-
-package runtime
-
-const (
-	bit1 = 7
-	bitx = 6
-	bit2 = 5
-	bit3 = 4
-	bit4 = 3
-	bit5 = 2
-
-	t1 = ((1 << (bit1 + 1)) - 1) ^ 0xFF /* 0000 0000 */
-	tx = ((1 << (bitx + 1)) - 1) ^ 0xFF /* 1000 0000 */
-	t2 = ((1 << (bit2 + 1)) - 1) ^ 0xFF /* 1100 0000 */
-	t3 = ((1 << (bit3 + 1)) - 1) ^ 0xFF /* 1110 0000 */
-	t4 = ((1 << (bit4 + 1)) - 1) ^ 0xFF /* 1111 0000 */
-	t5 = ((1 << (bit5 + 1)) - 1) ^ 0xFF /* 1111 1000 */
-
-	rune1 = (1 << (bit1 + 0*bitx)) - 1 /* 0000 0000 0111 1111 */
-	rune2 = (1 << (bit2 + 1*bitx)) - 1 /* 0000 0111 1111 1111 */
-	rune3 = (1 << (bit3 + 2*bitx)) - 1 /* 1111 1111 1111 1111 */
-	rune4 = (1 << (bit4 + 3*bitx)) - 1 /* 0001 1111 1111 1111 1111 1111 */
-
-	maskx = (1 << bitx) - 1 /* 0011 1111 */
-	testx = maskx ^ 0xFF    /* 1100 0000 */
-
-	runeerror = 0xFFFD
-	runeself  = 0x80
-
-	surrogateMin = 0xD800
-	surrogateMax = 0xDFFF
-
-	bad = runeerror
-
-	runemax = 0x10FFFF /* maximum rune value */
-)
-
-/*
- * Modified by Wei-Hwa Huang, Google Inc., on 2004-09-24
- * This is a slower but "safe" version of the old chartorune
- * that works on strings that are not necessarily null-terminated.
- *
- * If you know for sure that your string is null-terminated,
- * chartorune will be a bit faster.
- *
- * It is guaranteed not to attempt to access "length"
- * past the incoming pointer.  This is to avoid
- * possible access violations.  If the string appears to be
- * well-formed but incomplete (i.e., to get the whole Rune
- * we'd need to read past str+length) then we'll set the Rune
- * to Bad and return 0.
- *
- * Note that if we have decoding problems for other
- * reasons, we return 1 instead of 0.
- */
-func charntorune(s string) (rune, int) {
-	/* When we're not allowed to read anything */
-	if len(s) <= 0 {
-		return bad, 1
-	}
-
-	/*
-	 * one character sequence (7-bit value)
-	 *	00000-0007F => T1
-	 */
-	c := s[0]
-	if c < tx {
-		return rune(c), 1
-	}
-
-	// If we can't read more than one character we must stop
-	if len(s) <= 1 {
-		return bad, 1
-	}
-
-	/*
-	 * two character sequence (11-bit value)
-	 *	0080-07FF => t2 tx
-	 */
-	c1 := s[1] ^ tx
-	if (c1 & testx) != 0 {
-		return bad, 1
-	}
-	if c < t3 {
-		if c < t2 {
-			return bad, 1
-		}
-		l := ((rune(c) << bitx) | rune(c1)) & rune2
-		if l <= rune1 {
-			return bad, 1
-		}
-		return l, 2
-	}
-
-	// If we can't read more than two characters we must stop
-	if len(s) <= 2 {
-		return bad, 1
-	}
-
-	/*
-	 * three character sequence (16-bit value)
-	 *	0800-FFFF => t3 tx tx
-	 */
-	c2 := s[2] ^ tx
-	if (c2 & testx) != 0 {
-		return bad, 1
-	}
-	if c < t4 {
-		l := ((((rune(c) << bitx) | rune(c1)) << bitx) | rune(c2)) & rune3
-		if l <= rune2 {
-			return bad, 1
-		}
-		if surrogateMin <= l && l <= surrogateMax {
-			return bad, 1
-		}
-		return l, 3
-	}
-
-	if len(s) <= 3 {
-		return bad, 1
-	}
-
-	/*
-	 * four character sequence (21-bit value)
-	 *	10000-1FFFFF => t4 tx tx tx
-	 */
-	c3 := s[3] ^ tx
-	if (c3 & testx) != 0 {
-		return bad, 1
-	}
-	if c < t5 {
-		l := ((((((rune(c) << bitx) | rune(c1)) << bitx) | rune(c2)) << bitx) | rune(c3)) & rune4
-		if l <= rune3 || l > runemax {
-			return bad, 1
-		}
-		return l, 4
-	}
-
-	// Support for 5-byte or longer UTF-8 would go here, but
-	// since we don't have that, we'll just return bad.
-	return bad, 1
-}
-
-// runetochar converts r to bytes and writes the result to str.
-// returns the number of bytes generated.
-func runetochar(str []byte, r rune) int {
-	/* runes are signed, so convert to unsigned for range check. */
-	c := uint32(r)
-	/*
-	 * one character sequence
-	 *	00000-0007F => 00-7F
-	 */
-	if c <= rune1 {
-		str[0] = byte(c)
-		return 1
-	}
-	/*
-	 * two character sequence
-	 *	0080-07FF => t2 tx
-	 */
-	if c <= rune2 {
-		str[0] = byte(t2 | (c >> (1 * bitx)))
-		str[1] = byte(tx | (c & maskx))
-		return 2
-	}
-
-	/*
-	 * If the rune is out of range or a surrogate half, convert it to the error rune.
-	 * Do this test here because the error rune encodes to three bytes.
-	 * Doing it earlier would duplicate work, since an out of range
-	 * rune wouldn't have fit in one or two bytes.
-	 */
-	if c > runemax {
-		c = runeerror
-	}
-	if surrogateMin <= c && c <= surrogateMax {
-		c = runeerror
-	}
-
-	/*
-	 * three character sequence
-	 *	0800-FFFF => t3 tx tx
-	 */
-	if c <= rune3 {
-		str[0] = byte(t3 | (c >> (2 * bitx)))
-		str[1] = byte(tx | ((c >> (1 * bitx)) & maskx))
-		str[2] = byte(tx | (c & maskx))
-		return 3
-	}
-
-	/*
-	 * four character sequence (21-bit value)
-	 *     10000-1FFFFF => t4 tx tx tx
-	 */
-	str[0] = byte(t4 | (c >> (3 * bitx)))
-	str[1] = byte(tx | ((c >> (2 * bitx)) & maskx))
-	str[2] = byte(tx | ((c >> (1 * bitx)) & maskx))
-	str[3] = byte(tx | (c & maskx))
-	return 4
-}
diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go
index aabe52d..94ba879 100644
--- a/src/runtime/runtime-gdb_test.go
+++ b/src/runtime/runtime-gdb_test.go
@@ -15,6 +15,7 @@ import (
 	"regexp"
 	"runtime"
 	"strconv"
+	"strings"
 	"testing"
 )
 
@@ -23,6 +24,9 @@ func checkGdbEnvironment(t *testing.T) {
 	if runtime.GOOS == "darwin" {
 		t.Skip("gdb does not work on darwin")
 	}
+	if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64" {
+		t.Skip("skipping gdb tests on linux/ppc64; see golang.org/issue/17366")
+	}
 	if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
 		t.Skip("gdb test can fail with GOROOT_FINAL pending")
 	}
@@ -65,18 +69,23 @@ func checkGdbPython(t *testing.T) {
 const helloSource = `
 package main
 import "fmt"
+var gslice []string
 func main() {
 	mapvar := make(map[string]string,5)
 	mapvar["abc"] = "def"
 	mapvar["ghi"] = "jkl"
 	strvar := "abc"
 	ptrvar := &strvar
-	fmt.Println("hi") // line 10
+	slicevar := make([]string, 0, 16)
+	slicevar = append(slicevar, mapvar["abc"])
+	fmt.Println("hi") // line 12
 	_ = ptrvar
+	gslice = slicevar
 }
 `
 
 func TestGdbPython(t *testing.T) {
+	t.Parallel()
 	checkGdbEnvironment(t)
 	checkGdbVersion(t)
 	checkGdbPython(t)
@@ -93,7 +102,7 @@ func TestGdbPython(t *testing.T) {
 		t.Fatalf("failed to create file: %v", err)
 	}
 
-	cmd := exec.Command("go", "build", "-o", "a.exe")
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe")
 	cmd.Dir = dir
 	out, err := testEnv(cmd).CombinedOutput()
 	if err != nil {
@@ -105,31 +114,27 @@ func TestGdbPython(t *testing.T) {
 		"-ex", "set startup-with-shell off",
 		"-ex", "info auto-load python-scripts",
 		"-ex", "set python print-stack full",
-		"-ex", "br main.go:10",
+		"-ex", "br fmt.Println",
 		"-ex", "run",
 		"-ex", "echo BEGIN info goroutines\n",
 		"-ex", "info goroutines",
 		"-ex", "echo END\n",
+		"-ex", "up", // up from fmt.Println to main
 		"-ex", "echo BEGIN print mapvar\n",
 		"-ex", "print mapvar",
 		"-ex", "echo END\n",
 		"-ex", "echo BEGIN print strvar\n",
 		"-ex", "print strvar",
-		"-ex", "echo END\n"}
-
-	// without framepointer, gdb cannot backtrace our non-standard
-	// stack frames on RISC architectures.
-	canBackTrace := false
-	switch runtime.GOARCH {
-	case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64", "mips64", "mips64le", "s390x":
-		canBackTrace = true
-		args = append(args,
-			"-ex", "echo BEGIN goroutine 2 bt\n",
-			"-ex", "goroutine 2 bt",
-			"-ex", "echo END\n")
+		"-ex", "echo END\n",
+		"-ex", "echo BEGIN info locals\n",
+		"-ex", "info locals",
+		"-ex", "echo END\n",
+		"-ex", "down", // back to fmt.Println (goroutine 2 below only works at bottom of stack.  TODO: fix that)
+		"-ex", "echo BEGIN goroutine 2 bt\n",
+		"-ex", "goroutine 2 bt",
+		"-ex", "echo END\n",
+		filepath.Join(dir, "a.exe"),
 	}
-
-	args = append(args, filepath.Join(dir, "a.exe"))
 	got, _ := exec.Command("gdb", args...).CombinedOutput()
 
 	firstLine := bytes.SplitN(got, []byte("\n"), 2)[0]
@@ -137,7 +142,7 @@ func TestGdbPython(t *testing.T) {
 		// This can happen when using all.bash with
 		// GOROOT_FINAL set, because the tests are run before
 		// the final installation of the files.
-		cmd := exec.Command("go", "env", "GOROOT")
+		cmd := exec.Command(testenv.GoToolPath(t), "env", "GOROOT")
 		cmd.Env = []string{}
 		out, err := cmd.CombinedOutput()
 		if err != nil && bytes.Contains(out, []byte("cannot find GOROOT")) {
@@ -172,11 +177,18 @@ func TestGdbPython(t *testing.T) {
 		t.Fatalf("print strvar failed: %s", bl)
 	}
 
+	// Issue 16338: ssa decompose phase can split a structure into
+	// a collection of scalar vars holding the fields. In such cases
+	// the DWARF variable location expression should be of the
+	// form "var.field" and not just "field".
+	infoLocalsRe := regexp.MustCompile(`^slicevar.len = `)
+	if bl := blocks["info locals"]; !infoLocalsRe.MatchString(bl) {
+		t.Fatalf("info locals failed: %s", bl)
+	}
+
 	btGoroutineRe := regexp.MustCompile(`^#0\s+runtime.+at`)
-	if bl := blocks["goroutine 2 bt"]; canBackTrace && !btGoroutineRe.MatchString(bl) {
+	if bl := blocks["goroutine 2 bt"]; !btGoroutineRe.MatchString(bl) {
 		t.Fatalf("goroutine 2 bt failed: %s", bl)
-	} else if !canBackTrace {
-		t.Logf("gdb cannot backtrace for GOARCH=%s, skipped goroutine backtrace test", runtime.GOARCH)
 	}
 }
 
@@ -208,6 +220,7 @@ func main() {
 // TestGdbBacktrace tests that gdb can unwind the stack correctly
 // using only the DWARF debug info.
 func TestGdbBacktrace(t *testing.T) {
+	t.Parallel()
 	checkGdbEnvironment(t)
 	checkGdbVersion(t)
 
@@ -227,7 +240,7 @@ func TestGdbBacktrace(t *testing.T) {
 	if err != nil {
 		t.Fatalf("failed to create file: %v", err)
 	}
-	cmd := exec.Command("go", "build", "-o", "a.exe")
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe")
 	cmd.Dir = dir
 	out, err := testEnv(cmd).CombinedOutput()
 	if err != nil {
@@ -263,3 +276,72 @@ func TestGdbBacktrace(t *testing.T) {
 		}
 	}
 }
+
+const autotmpTypeSource = `
+package main
+
+type astruct struct {
+	a, b int
+}
+
+func main() {
+	var iface interface{} = map[string]astruct{}
+	var iface2 interface{} = []astruct{}
+	println(iface, iface2)
+}
+`
+
+// TestGdbAutotmpTypes ensures that types of autotmp variables appear in .debug_info
+// See bug #17830.
+func TestGdbAutotmpTypes(t *testing.T) {
+	t.Parallel()
+	checkGdbEnvironment(t)
+	checkGdbVersion(t)
+
+	dir, err := ioutil.TempDir("", "go-build")
+	if err != nil {
+		t.Fatalf("failed to create temp directory: %v", err)
+	}
+	defer os.RemoveAll(dir)
+
+	// Build the source code.
+	src := filepath.Join(dir, "main.go")
+	err = ioutil.WriteFile(src, []byte(autotmpTypeSource), 0644)
+	if err != nil {
+		t.Fatalf("failed to create file: %v", err)
+	}
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", "a.exe")
+	cmd.Dir = dir
+	out, err := testEnv(cmd).CombinedOutput()
+	if err != nil {
+		t.Fatalf("building source %v\n%s", err, out)
+	}
+
+	// Execute gdb commands.
+	args := []string{"-nx", "-batch",
+		"-ex", "set startup-with-shell off",
+		"-ex", "break main.main",
+		"-ex", "run",
+		"-ex", "step",
+		"-ex", "info types astruct",
+		filepath.Join(dir, "a.exe"),
+	}
+	got, _ := exec.Command("gdb", args...).CombinedOutput()
+
+	sgot := string(got)
+
+	// Check that the backtrace matches the source code.
+	types := []string{
+		"struct []main.astruct;",
+		"struct bucket<string,main.astruct>;",
+		"struct hash<string,main.astruct>;",
+		"struct main.astruct;",
+		"typedef struct hash<string,main.astruct> * map[string]main.astruct;",
+	}
+	for _, name := range types {
+		if !strings.Contains(sgot, name) {
+			t.Errorf("could not find %s in 'info typrs astruct' output", name)
+			t.Fatalf("gdb output:\n%v", sgot)
+		}
+	}
+}
diff --git a/src/runtime/runtime-lldb_test.go b/src/runtime/runtime-lldb_test.go
index 4c379b9..98bc906 100644
--- a/src/runtime/runtime-lldb_test.go
+++ b/src/runtime/runtime-lldb_test.go
@@ -158,7 +158,7 @@ func TestLldbPython(t *testing.T) {
 		t.Fatalf("failed to create file: %v", err)
 	}
 
-	cmd := exec.Command("go", "build", "-gcflags", "-N -l", "-o", "a.exe")
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags", "-N -l", "-o", "a.exe")
 	cmd.Dir = dir
 	out, err := cmd.CombinedOutput()
 	if err != nil {
@@ -198,7 +198,7 @@ func TestDwarfAranges(t *testing.T) {
 		t.Fatalf("failed to create file: %v", err)
 	}
 
-	cmd := exec.Command("go", "build", "-o", "a.exe")
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "a.exe")
 	cmd.Dir = dir
 	out, err := cmd.CombinedOutput()
 	if err != nil {
diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go
index d9c26cc..d8fe2f4 100644
--- a/src/runtime/runtime.go
+++ b/src/runtime/runtime.go
@@ -52,5 +52,8 @@ var argslice []string
 //go:linkname syscall_runtime_envs syscall.runtime_envs
 func syscall_runtime_envs() []string { return append([]string{}, envs...) }
 
+//go:linkname syscall_Getpagesize syscall.Getpagesize
+func syscall_Getpagesize() int { return int(physPageSize) }
+
 //go:linkname os_runtime_args os.runtime_args
 func os_runtime_args() []string { return append([]string{}, argslice...) }
diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go
index 302f58d..40c0e85 100644
--- a/src/runtime/runtime1.go
+++ b/src/runtime/runtime1.go
@@ -68,7 +68,6 @@ func goargs() {
 	if GOOS == "windows" {
 		return
 	}
-
 	argslice = make([]string, argc)
 	for i := int32(0); i < argc; i++ {
 		argslice[i] = gostringnocopy(argv_index(argv, i))
@@ -322,6 +321,7 @@ var debug struct {
 	gcshrinkstackoff  int32
 	gcstackbarrieroff int32
 	gcstackbarrierall int32
+	gcrescanstacks    int32
 	gcstoptheworld    int32
 	gctrace           int32
 	invalidptr        int32
@@ -341,6 +341,7 @@ var dbgvars = []dbgVar{
 	{"gcshrinkstackoff", &debug.gcshrinkstackoff},
 	{"gcstackbarrieroff", &debug.gcstackbarrieroff},
 	{"gcstackbarrierall", &debug.gcstackbarrierall},
+	{"gcrescanstacks", &debug.gcrescanstacks},
 	{"gcstoptheworld", &debug.gcstoptheworld},
 	{"gctrace", &debug.gctrace},
 	{"invalidptr", &debug.invalidptr},
@@ -374,11 +375,15 @@ func parsedebugvars() {
 		// is int, not int32, and should only be updated
 		// if specified in GODEBUG.
 		if key == "memprofilerate" {
-			MemProfileRate = atoi(value)
+			if n, ok := atoi(value); ok {
+				MemProfileRate = n
+			}
 		} else {
 			for _, v := range dbgvars {
 				if v.name == key {
-					*v.value = int32(atoi(value))
+					if n, ok := atoi32(value); ok {
+						*v.value = n
+					}
 				}
 			}
 		}
@@ -387,6 +392,13 @@ func parsedebugvars() {
 	setTraceback(gogetenv("GOTRACEBACK"))
 	traceback_env = traceback_cache
 
+	if debug.gcrescanstacks == 0 {
+		// Without rescanning, there's no need for stack
+		// barriers.
+		debug.gcstackbarrieroff = 1
+		debug.gcstackbarrierall = 0
+	}
+
 	if debug.gcstackbarrierall > 0 {
 		firstStackBarrierOffset = 0
 	}
@@ -414,7 +426,10 @@ func setTraceback(level string) {
 	case "crash":
 		t = 2<<tracebackShift | tracebackAll | tracebackCrash
 	default:
-		t = uint32(atoi(level))<<tracebackShift | tracebackAll
+		t = tracebackAll
+		if n, ok := atoi(level); ok && n == int(uint32(n)) {
+			t |= uint32(n) << tracebackShift
+		}
 	}
 	// when C owns the process, simply exit'ing the process on fatal errors
 	// and panics is surprising. Be louder and abort instead.
@@ -478,11 +493,12 @@ func gomcache() *mcache {
 
 //go:linkname reflect_typelinks reflect.typelinks
 func reflect_typelinks() ([]unsafe.Pointer, [][]int32) {
-	sections := []unsafe.Pointer{unsafe.Pointer(firstmoduledata.types)}
-	ret := [][]int32{firstmoduledata.typelinks}
-	for datap := firstmoduledata.next; datap != nil; datap = datap.next {
-		sections = append(sections, unsafe.Pointer(datap.types))
-		ret = append(ret, datap.typelinks)
+	modules := activeModules()
+	sections := []unsafe.Pointer{unsafe.Pointer(modules[0].types)}
+	ret := [][]int32{modules[0].typelinks}
+	for _, md := range modules[1:] {
+		sections = append(sections, unsafe.Pointer(md.types))
+		ret = append(ret, md.typelinks)
 	}
 	return sections, ret
 }
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index 6119e75..696ea81 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -205,6 +205,14 @@ func (gp *guintptr) cas(old, new guintptr) bool {
 	return atomic.Casuintptr((*uintptr)(unsafe.Pointer(gp)), uintptr(old), uintptr(new))
 }
 
+// setGNoWB performs *gp = new without a write barrier.
+// For times when it's impractical to use a guintptr.
+//go:nosplit
+//go:nowritebarrier
+func setGNoWB(gp **g, new *g) {
+	(*guintptr)(unsafe.Pointer(gp)).set(new)
+}
+
 type puintptr uintptr
 
 //go:nosplit
@@ -221,8 +229,25 @@ func (mp muintptr) ptr() *m { return (*m)(unsafe.Pointer(mp)) }
 //go:nosplit
 func (mp *muintptr) set(m *m) { *mp = muintptr(unsafe.Pointer(m)) }
 
+// setMNoWB performs *mp = new without a write barrier.
+// For times when it's impractical to use an muintptr.
+//go:nosplit
+//go:nowritebarrier
+func setMNoWB(mp **m, new *m) {
+	(*muintptr)(unsafe.Pointer(mp)).set(new)
+}
+
 type gobuf struct {
 	// The offsets of sp, pc, and g are known to (hard-coded in) libmach.
+	//
+	// ctxt is unusual with respect to GC: it may be a
+	// heap-allocated funcval so write require a write barrier,
+	// but gobuf needs to be cleared from assembly. We take
+	// advantage of the fact that the only path that uses a
+	// non-nil ctxt is morestack. As a result, gogo is the only
+	// place where it may not already be nil, so gogo uses an
+	// explicit write barrier. Everywhere else that resets the
+	// gobuf asserts that ctxt is already nil.
 	sp   uintptr
 	pc   uintptr
 	g    guintptr
@@ -256,6 +281,7 @@ type sudog struct {
 	// The following fields are never accessed concurrently.
 	// waitlink is only accessed by g.
 
+	acquiretime int64
 	releasetime int64
 	ticket      uint32
 	waitlink    *sudog // g.waiting list
@@ -498,7 +524,7 @@ type p struct {
 
 	runSafePointFn uint32 // if 1, run sched.safePointFn at next safe point
 
-	pad [64]byte
+	pad [sys.CacheLineSize]byte
 }
 
 const (
@@ -575,11 +601,6 @@ const (
 	_LockInternal = 2
 )
 
-type sigtabtt struct {
-	flags int32
-	name  *int8
-}
-
 const (
 	_SigNotify   = 1 << iota // let signal.Notify have signal, even if from kernel
 	_SigKill                 // if signal.Notify doesn't take it, exit quietly
diff --git a/src/runtime/runtime_mmap_test.go b/src/runtime/runtime_mmap_test.go
index cf240c1..2eca6b9 100644
--- a/src/runtime/runtime_mmap_test.go
+++ b/src/runtime/runtime_mmap_test.go
@@ -8,15 +8,15 @@ package runtime_test
 
 import (
 	"runtime"
-	"runtime/internal/sys"
 	"testing"
+	"unsafe"
 )
 
 // Test that the error value returned by mmap is positive, as that is
 // what the code in mem_bsd.go, mem_darwin.go, and mem_linux.go expects.
 // See the uses of ENOMEM in sysMap in those files.
 func TestMmapErrorSign(t *testing.T) {
-	p := runtime.Mmap(nil, ^uintptr(0)&^(sys.PhysPageSize-1), 0, runtime.MAP_ANON|runtime.MAP_PRIVATE, -1, 0)
+	p := runtime.Mmap(nil, ^uintptr(0)&^(runtime.GetPhysPageSize()-1), 0, runtime.MAP_ANON|runtime.MAP_PRIVATE, -1, 0)
 
 	// The runtime.mmap function is nosplit, but t.Errorf is not.
 	// Reset the pointer so that we don't get an "invalid stack
@@ -28,3 +28,27 @@ func TestMmapErrorSign(t *testing.T) {
 		t.Errorf("mmap = %v, want %v", v, runtime.ENOMEM)
 	}
 }
+
+func TestPhysPageSize(t *testing.T) {
+	// Mmap fails if the address is not page aligned, so we can
+	// use this to test if the page size is the true page size.
+	ps := runtime.GetPhysPageSize()
+
+	// Get a region of memory to play with. This should be page-aligned.
+	b := uintptr(runtime.Mmap(nil, 2*ps, 0, runtime.MAP_ANON|runtime.MAP_PRIVATE, -1, 0))
+	if b < 4096 {
+		t.Fatalf("Mmap: %v", b)
+	}
+
+	// Mmap should fail at a half page into the buffer.
+	err := uintptr(runtime.Mmap(unsafe.Pointer(uintptr(b)+ps/2), ps, 0, runtime.MAP_ANON|runtime.MAP_PRIVATE|runtime.MAP_FIXED, -1, 0))
+	if err >= 4096 {
+		t.Errorf("Mmap should have failed with half-page alignment %d, but succeeded: %v", ps/2, err)
+	}
+
+	// Mmap should succeed at a full page into the buffer.
+	err = uintptr(runtime.Mmap(unsafe.Pointer(uintptr(b)+ps), ps, 0, runtime.MAP_ANON|runtime.MAP_PRIVATE|runtime.MAP_FIXED, -1, 0))
+	if err < 4096 {
+		t.Errorf("Mmap at full-page alignment %d failed: %v", ps, err)
+	}
+}
diff --git a/src/runtime/select.go b/src/runtime/select.go
index 433048f..03e9e4a 100644
--- a/src/runtime/select.go
+++ b/src/runtime/select.go
@@ -270,7 +270,7 @@ func selectgoImpl(sel *hselect) (uintptr, uint16) {
 	pollslice := slice{unsafe.Pointer(sel.pollorder), int(sel.ncase), int(sel.ncase)}
 	pollorder := *(*[]uint16)(unsafe.Pointer(&pollslice))
 	for i := 1; i < int(sel.ncase); i++ {
-		j := int(fastrand1()) % (i + 1)
+		j := int(fastrand()) % (i + 1)
 		pollorder[i] = pollorder[j]
 		pollorder[j] = uint16(i)
 	}
@@ -518,7 +518,7 @@ bufrecv:
 	if cas.elem != nil {
 		typedmemmove(c.elemtype, cas.elem, qp)
 	}
-	memclr(qp, uintptr(c.elemsize))
+	typedmemclr(c.elemtype, qp)
 	c.recvx++
 	if c.recvx == c.dataqsiz {
 		c.recvx = 0
@@ -564,7 +564,7 @@ rclose:
 		*cas.receivedp = false
 	}
 	if cas.elem != nil {
-		memclr(cas.elem, uintptr(c.elemsize))
+		typedmemclr(c.elemtype, cas.elem)
 	}
 	if raceenabled {
 		raceacquire(unsafe.Pointer(c))
diff --git a/src/runtime/sema.go b/src/runtime/sema.go
index 45fbbca..576a1fb 100644
--- a/src/runtime/sema.go
+++ b/src/runtime/sema.go
@@ -44,12 +44,12 @@ var semtable [semTabSize]struct {
 
 //go:linkname sync_runtime_Semacquire sync.runtime_Semacquire
 func sync_runtime_Semacquire(addr *uint32) {
-	semacquire(addr, true)
+	semacquire(addr, semaBlockProfile)
 }
 
 //go:linkname net_runtime_Semacquire net.runtime_Semacquire
 func net_runtime_Semacquire(addr *uint32) {
-	semacquire(addr, true)
+	semacquire(addr, semaBlockProfile)
 }
 
 //go:linkname sync_runtime_Semrelease sync.runtime_Semrelease
@@ -57,6 +57,11 @@ func sync_runtime_Semrelease(addr *uint32) {
 	semrelease(addr)
 }
 
+//go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex
+func sync_runtime_SemacquireMutex(addr *uint32) {
+	semacquire(addr, semaBlockProfile|semaMutexProfile)
+}
+
 //go:linkname net_runtime_Semrelease net.runtime_Semrelease
 func net_runtime_Semrelease(addr *uint32) {
 	semrelease(addr)
@@ -69,8 +74,15 @@ func readyWithTime(s *sudog, traceskip int) {
 	goready(s.g, traceskip)
 }
 
+type semaProfileFlags int
+
+const (
+	semaBlockProfile semaProfileFlags = 1 << iota
+	semaMutexProfile
+)
+
 // Called from runtime.
-func semacquire(addr *uint32, profile bool) {
+func semacquire(addr *uint32, profile semaProfileFlags) {
 	gp := getg()
 	if gp != gp.m.curg {
 		throw("semacquire not on the G stack")
@@ -91,10 +103,17 @@ func semacquire(addr *uint32, profile bool) {
 	root := semroot(addr)
 	t0 := int64(0)
 	s.releasetime = 0
-	if profile && blockprofilerate > 0 {
+	s.acquiretime = 0
+	if profile&semaBlockProfile != 0 && blockprofilerate > 0 {
 		t0 = cputicks()
 		s.releasetime = -1
 	}
+	if profile&semaMutexProfile != 0 && mutexprofilerate > 0 {
+		if t0 == 0 {
+			t0 = cputicks()
+		}
+		s.acquiretime = t0
+	}
 	for {
 		lock(&root.lock)
 		// Add ourselves to nwait to disable "easy case" in semrelease.
@@ -146,8 +165,19 @@ func semrelease(addr *uint32) {
 			break
 		}
 	}
-	unlock(&root.lock)
 	if s != nil {
+		if s.acquiretime != 0 {
+			t0 := cputicks()
+			for x := root.head; x != nil; x = x.next {
+				if x.elem == unsafe.Pointer(addr) {
+					x.acquiretime = t0
+				}
+			}
+			mutexevent(t0-s.acquiretime, 3)
+		}
+	}
+	unlock(&root.lock)
+	if s != nil { // May be slow, so unlock first
 		readyWithTime(s, 5)
 	}
 }
diff --git a/src/runtime/sigaction_linux.go b/src/runtime/sigaction_linux.go
new file mode 100644
index 0000000..0b2afb0
--- /dev/null
+++ b/src/runtime/sigaction_linux.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64
+
+package runtime
+
+// rt_sigaction calls the rt_sigaction system call. It is implemented in assembly.
+//go:noescape
+func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
diff --git a/src/runtime/signal1_unix.go b/src/runtime/signal1_unix.go
deleted file mode 100644
index 101d16d..0000000
--- a/src/runtime/signal1_unix.go
+++ /dev/null
@@ -1,350 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
-
-package runtime
-
-import (
-	"runtime/internal/sys"
-	"unsafe"
-)
-
-const (
-	_SIG_DFL uintptr = 0
-	_SIG_IGN uintptr = 1
-)
-
-// Stores the signal handlers registered before Go installed its own.
-// These signal handlers will be invoked in cases where Go doesn't want to
-// handle a particular signal (e.g., signal occurred on a non-Go thread).
-// See sigfwdgo() for more information on when the signals are forwarded.
-//
-// Signal forwarding is currently available only on Darwin and Linux.
-var fwdSig [_NSIG]uintptr
-
-// sigmask represents a general signal mask compatible with the GOOS
-// specific sigset types: the signal numbered x is represented by bit x-1
-// to match the representation expected by sigprocmask.
-type sigmask [(_NSIG + 31) / 32]uint32
-
-// channels for synchronizing signal mask updates with the signal mask
-// thread
-var (
-	disableSigChan  chan uint32
-	enableSigChan   chan uint32
-	maskUpdatedChan chan struct{}
-)
-
-func init() {
-	// _NSIG is the number of signals on this operating system.
-	// sigtable should describe what to do for all the possible signals.
-	if len(sigtable) != _NSIG {
-		print("runtime: len(sigtable)=", len(sigtable), " _NSIG=", _NSIG, "\n")
-		throw("bad sigtable len")
-	}
-}
-
-var signalsOK bool
-
-// Initialize signals.
-// Called by libpreinit so runtime may not be initialized.
-//go:nosplit
-//go:nowritebarrierrec
-func initsig(preinit bool) {
-	if !preinit {
-		// It's now OK for signal handlers to run.
-		signalsOK = true
-	}
-
-	// For c-archive/c-shared this is called by libpreinit with
-	// preinit == true.
-	if (isarchive || islibrary) && !preinit {
-		return
-	}
-
-	for i := int32(0); i < _NSIG; i++ {
-		t := &sigtable[i]
-		if t.flags == 0 || t.flags&_SigDefault != 0 {
-			continue
-		}
-		fwdSig[i] = getsig(i)
-
-		if !sigInstallGoHandler(i) {
-			// Even if we are not installing a signal handler,
-			// set SA_ONSTACK if necessary.
-			if fwdSig[i] != _SIG_DFL && fwdSig[i] != _SIG_IGN {
-				setsigstack(i)
-			}
-			continue
-		}
-
-		t.flags |= _SigHandling
-		setsig(i, funcPC(sighandler), true)
-	}
-}
-
-//go:nosplit
-//go:nowritebarrierrec
-func sigInstallGoHandler(sig int32) bool {
-	// For some signals, we respect an inherited SIG_IGN handler
-	// rather than insist on installing our own default handler.
-	// Even these signals can be fetched using the os/signal package.
-	switch sig {
-	case _SIGHUP, _SIGINT:
-		if fwdSig[sig] == _SIG_IGN {
-			return false
-		}
-	}
-
-	t := &sigtable[sig]
-	if t.flags&_SigSetStack != 0 {
-		return false
-	}
-
-	// When built using c-archive or c-shared, only install signal
-	// handlers for synchronous signals.
-	if (isarchive || islibrary) && t.flags&_SigPanic == 0 {
-		return false
-	}
-
-	return true
-}
-
-func sigenable(sig uint32) {
-	if sig >= uint32(len(sigtable)) {
-		return
-	}
-
-	t := &sigtable[sig]
-	if t.flags&_SigNotify != 0 {
-		ensureSigM()
-		enableSigChan <- sig
-		<-maskUpdatedChan
-		if t.flags&_SigHandling == 0 {
-			t.flags |= _SigHandling
-			fwdSig[sig] = getsig(int32(sig))
-			setsig(int32(sig), funcPC(sighandler), true)
-		}
-	}
-}
-
-func sigdisable(sig uint32) {
-	if sig >= uint32(len(sigtable)) {
-		return
-	}
-
-	t := &sigtable[sig]
-	if t.flags&_SigNotify != 0 {
-		ensureSigM()
-		disableSigChan <- sig
-		<-maskUpdatedChan
-
-		// If initsig does not install a signal handler for a
-		// signal, then to go back to the state before Notify
-		// we should remove the one we installed.
-		if !sigInstallGoHandler(int32(sig)) {
-			t.flags &^= _SigHandling
-			setsig(int32(sig), fwdSig[sig], true)
-		}
-	}
-}
-
-func sigignore(sig uint32) {
-	if sig >= uint32(len(sigtable)) {
-		return
-	}
-
-	t := &sigtable[sig]
-	if t.flags&_SigNotify != 0 {
-		t.flags &^= _SigHandling
-		setsig(int32(sig), _SIG_IGN, true)
-	}
-}
-
-func resetcpuprofiler(hz int32) {
-	var it itimerval
-	if hz == 0 {
-		setitimer(_ITIMER_PROF, &it, nil)
-	} else {
-		it.it_interval.tv_sec = 0
-		it.it_interval.set_usec(1000000 / hz)
-		it.it_value = it.it_interval
-		setitimer(_ITIMER_PROF, &it, nil)
-	}
-	_g_ := getg()
-	_g_.m.profilehz = hz
-}
-
-func sigpipe() {
-	if sigsend(_SIGPIPE) {
-		return
-	}
-	dieFromSignal(_SIGPIPE)
-}
-
-// dieFromSignal kills the program with a signal.
-// This provides the expected exit status for the shell.
-// This is only called with fatal signals expected to kill the process.
-//go:nosplit
-//go:nowritebarrierrec
-func dieFromSignal(sig int32) {
-	setsig(sig, _SIG_DFL, false)
-	updatesigmask(sigmask{})
-	raise(sig)
-
-	// That should have killed us. On some systems, though, raise
-	// sends the signal to the whole process rather than to just
-	// the current thread, which means that the signal may not yet
-	// have been delivered. Give other threads a chance to run and
-	// pick up the signal.
-	osyield()
-	osyield()
-	osyield()
-
-	// If we are still somehow running, just exit with the wrong status.
-	exit(2)
-}
-
-// raisebadsignal is called when a signal is received on a non-Go
-// thread, and the Go program does not want to handle it (that is, the
-// program has not called os/signal.Notify for the signal).
-func raisebadsignal(sig int32, c *sigctxt) {
-	if sig == _SIGPROF {
-		// Ignore profiling signals that arrive on non-Go threads.
-		return
-	}
-
-	var handler uintptr
-	if sig >= _NSIG {
-		handler = _SIG_DFL
-	} else {
-		handler = fwdSig[sig]
-	}
-
-	// Reset the signal handler and raise the signal.
-	// We are currently running inside a signal handler, so the
-	// signal is blocked. We need to unblock it before raising the
-	// signal, or the signal we raise will be ignored until we return
-	// from the signal handler. We know that the signal was unblocked
-	// before entering the handler, or else we would not have received
-	// it. That means that we don't have to worry about blocking it
-	// again.
-	unblocksig(sig)
-	setsig(sig, handler, false)
-
-	// If we're linked into a non-Go program we want to try to
-	// avoid modifying the original context in which the signal
-	// was raised. If the handler is the default, we know it
-	// is non-recoverable, so we don't have to worry about
-	// re-installing sighandler. At this point we can just
-	// return and the signal will be re-raised and caught by
-	// the default handler with the correct context.
-	if (isarchive || islibrary) && handler == _SIG_DFL && c.sigcode() != _SI_USER {
-		return
-	}
-
-	raise(sig)
-
-	// If the signal didn't cause the program to exit, restore the
-	// Go signal handler and carry on.
-	//
-	// We may receive another instance of the signal before we
-	// restore the Go handler, but that is not so bad: we know
-	// that the Go program has been ignoring the signal.
-	setsig(sig, funcPC(sighandler), true)
-}
-
-func crash() {
-	if GOOS == "darwin" {
-		// OS X core dumps are linear dumps of the mapped memory,
-		// from the first virtual byte to the last, with zeros in the gaps.
-		// Because of the way we arrange the address space on 64-bit systems,
-		// this means the OS X core file will be >128 GB and even on a zippy
-		// workstation can take OS X well over an hour to write (uninterruptible).
-		// Save users from making that mistake.
-		if sys.PtrSize == 8 {
-			return
-		}
-	}
-
-	dieFromSignal(_SIGABRT)
-}
-
-// ensureSigM starts one global, sleeping thread to make sure at least one thread
-// is available to catch signals enabled for os/signal.
-func ensureSigM() {
-	if maskUpdatedChan != nil {
-		return
-	}
-	maskUpdatedChan = make(chan struct{})
-	disableSigChan = make(chan uint32)
-	enableSigChan = make(chan uint32)
-	go func() {
-		// Signal masks are per-thread, so make sure this goroutine stays on one
-		// thread.
-		LockOSThread()
-		defer UnlockOSThread()
-		// The sigBlocked mask contains the signals not active for os/signal,
-		// initially all signals except the essential. When signal.Notify()/Stop is called,
-		// sigenable/sigdisable in turn notify this thread to update its signal
-		// mask accordingly.
-		var sigBlocked sigmask
-		for i := range sigBlocked {
-			sigBlocked[i] = ^uint32(0)
-		}
-		for i := range sigtable {
-			if sigtable[i].flags&_SigUnblock != 0 {
-				sigBlocked[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
-			}
-		}
-		updatesigmask(sigBlocked)
-		for {
-			select {
-			case sig := <-enableSigChan:
-				if b := sig - 1; sig > 0 {
-					sigBlocked[b/32] &^= (1 << (b & 31))
-				}
-			case sig := <-disableSigChan:
-				if b := sig - 1; sig > 0 {
-					sigBlocked[b/32] |= (1 << (b & 31))
-				}
-			}
-			updatesigmask(sigBlocked)
-			maskUpdatedChan <- struct{}{}
-		}
-	}()
-}
-
-// This is called when we receive a signal when there is no signal stack.
-// This can only happen if non-Go code calls sigaltstack to disable the
-// signal stack. This is called via cgocallback to establish a stack.
-func noSignalStack(sig uint32) {
-	println("signal", sig, "received on thread with no signal stack")
-	throw("non-Go code disabled sigaltstack")
-}
-
-// This is called if we receive a signal when there is a signal stack
-// but we are not on it. This can only happen if non-Go code called
-// sigaction without setting the SS_ONSTACK flag.
-func sigNotOnStack(sig uint32) {
-	println("signal", sig, "received but handler not on signal stack")
-	throw("non-Go code set up signal handler without SA_ONSTACK flag")
-}
-
-// This runs on a foreign stack, without an m or a g. No stack split.
-//go:nosplit
-//go:norace
-//go:nowritebarrierrec
-func badsignal(sig uintptr, c *sigctxt) {
-	cgocallback(unsafe.Pointer(funcPC(badsignalgo)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig)+unsafe.Sizeof(c), 0)
-}
-
-func badsignalgo(sig uintptr, c *sigctxt) {
-	if !sigsend(uint32(sig)) {
-		// A foreign thread received the signal sig, and the
-		// Go code does not want to handle it.
-		raisebadsignal(int32(sig), c)
-	}
-}
diff --git a/src/runtime/signal2_unix.go b/src/runtime/signal2_unix.go
deleted file mode 100644
index b137169..0000000
--- a/src/runtime/signal2_unix.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build darwin dragonfly freebsd linux netbsd openbsd
-
-package runtime
-
-import "unsafe"
-
-//go:noescape
-func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
-
-// Determines if the signal should be handled by Go and if not, forwards the
-// signal to the handler that was installed before Go's. Returns whether the
-// signal was forwarded.
-// This is called by the signal handler, and the world may be stopped.
-//go:nosplit
-//go:nowritebarrierrec
-func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
-	if sig >= uint32(len(sigtable)) {
-		return false
-	}
-	fwdFn := fwdSig[sig]
-
-	if !signalsOK {
-		// The only way we can get here is if we are in a
-		// library or archive, we installed a signal handler
-		// at program startup, but the Go runtime has not yet
-		// been initialized.
-		if fwdFn == _SIG_DFL {
-			dieFromSignal(int32(sig))
-		} else {
-			sigfwd(fwdFn, sig, info, ctx)
-		}
-		return true
-	}
-
-	flags := sigtable[sig].flags
-
-	// If there is no handler to forward to, no need to forward.
-	if fwdFn == _SIG_DFL {
-		return false
-	}
-
-	// If we aren't handling the signal, forward it.
-	if flags&_SigHandling == 0 {
-		sigfwd(fwdFn, sig, info, ctx)
-		return true
-	}
-
-	// Only forward synchronous signals.
-	c := &sigctxt{info, ctx}
-	if c.sigcode() == _SI_USER || flags&_SigPanic == 0 {
-		return false
-	}
-	// Determine if the signal occurred inside Go code. We test that:
-	//   (1) we were in a goroutine (i.e., m.curg != nil), and
-	//   (2) we weren't in CGO (i.e., m.curg.syscallsp == 0).
-	g := getg()
-	if g != nil && g.m != nil && g.m.curg != nil && g.m.curg.syscallsp == 0 {
-		return false
-	}
-	// Signal not handled by Go, forward it.
-	if fwdFn != _SIG_IGN {
-		sigfwd(fwdFn, sig, info, ctx)
-	}
-	return true
-}
diff --git a/src/runtime/signal_386.go b/src/runtime/signal_386.go
index f27cf9d..8807552 100644
--- a/src/runtime/signal_386.go
+++ b/src/runtime/signal_386.go
@@ -27,152 +27,57 @@ func dumpregs(c *sigctxt) {
 	print("gs     ", hex(c.gs()), "\n")
 }
 
-var crashing int32
-
-// May run during STW, so write barriers are not allowed.
-//
+//go:nosplit
 //go:nowritebarrierrec
-func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
-	_g_ := getg()
-	c := &sigctxt{info, ctxt}
-
-	if sig == _SIGPROF {
-		sigprof(uintptr(c.eip()), uintptr(c.esp()), 0, gp, _g_.m)
-		return
-	}
-
-	flags := int32(_SigThrow)
-	if sig < uint32(len(sigtable)) {
-		flags = sigtable[sig].flags
-	}
-	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
-		// Make it look like a call to the signal func.
-		// Have to pass arguments out of band since
-		// augmenting the stack frame would break
-		// the unwinding code.
-		gp.sig = sig
-		gp.sigcode0 = uintptr(c.sigcode())
-		gp.sigcode1 = uintptr(c.sigaddr())
-		gp.sigpc = uintptr(c.eip())
-
-		if GOOS == "darwin" {
-			// Work around Leopard bug that doesn't set FPE_INTDIV.
-			// Look at instruction to see if it is a divide.
-			// Not necessary in Snow Leopard (si_code will be != 0).
-			if sig == _SIGFPE && gp.sigcode0 == 0 {
-				pc := (*[4]byte)(unsafe.Pointer(gp.sigpc))
-				i := 0
-				if pc[i] == 0x66 { // 16-bit instruction prefix
-					i++
-				}
-				if pc[i] == 0xF6 || pc[i] == 0xF7 {
-					gp.sigcode0 = _FPE_INTDIV
-				}
+func (c *sigctxt) sigpc() uintptr { return uintptr(c.eip()) }
+
+func (c *sigctxt) sigsp() uintptr { return uintptr(c.esp()) }
+func (c *sigctxt) siglr() uintptr { return 0 }
+func (c *sigctxt) fault() uintptr { return uintptr(c.sigaddr()) }
+
+// preparePanic sets up the stack to look like a call to sigpanic.
+func (c *sigctxt) preparePanic(sig uint32, gp *g) {
+	if GOOS == "darwin" {
+		// Work around Leopard bug that doesn't set FPE_INTDIV.
+		// Look at instruction to see if it is a divide.
+		// Not necessary in Snow Leopard (si_code will be != 0).
+		if sig == _SIGFPE && gp.sigcode0 == 0 {
+			pc := (*[4]byte)(unsafe.Pointer(gp.sigpc))
+			i := 0
+			if pc[i] == 0x66 { // 16-bit instruction prefix
+				i++
 			}
-		}
-
-		pc := uintptr(c.eip())
-		sp := uintptr(c.esp())
-
-		// If we don't recognize the PC as code
-		// but we do recognize the top pointer on the stack as code,
-		// then assume this was a call to non-code and treat like
-		// pc == 0, to make unwinding show the context.
-		if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
-			pc = 0
-		}
-
-		// Only push runtime.sigpanic if pc != 0.
-		// If pc == 0, probably panicked because of a
-		// call to a nil func. Not pushing that onto sp will
-		// make the trace look like a call to runtime.sigpanic instead.
-		// (Otherwise the trace will end at runtime.sigpanic and we
-		// won't get to see who faulted.)
-		if pc != 0 {
-			if sys.RegSize > sys.PtrSize {
-				sp -= sys.PtrSize
-				*(*uintptr)(unsafe.Pointer(sp)) = 0
+			if pc[i] == 0xF6 || pc[i] == 0xF7 {
+				gp.sigcode0 = _FPE_INTDIV
 			}
-			sp -= sys.PtrSize
-			*(*uintptr)(unsafe.Pointer(sp)) = pc
-			c.set_esp(uint32(sp))
-		}
-		c.set_eip(uint32(funcPC(sigpanic)))
-		return
-	}
-
-	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
-		if sigsend(sig) {
-			return
 		}
 	}
 
-	if c.sigcode() == _SI_USER && signal_ignored(sig) {
-		return
-	}
-
-	if flags&_SigKill != 0 {
-		dieFromSignal(int32(sig))
-	}
-
-	if flags&_SigThrow == 0 {
-		return
-	}
-
-	_g_.m.throwing = 1
-	_g_.m.caughtsig.set(gp)
-
-	if crashing == 0 {
-		startpanic()
-	}
+	pc := uintptr(c.eip())
+	sp := uintptr(c.esp())
 
-	if sig < uint32(len(sigtable)) {
-		print(sigtable[sig].name, "\n")
-	} else {
-		print("Signal ", sig, "\n")
+	// If we don't recognize the PC as code
+	// but we do recognize the top pointer on the stack as code,
+	// then assume this was a call to non-code and treat like
+	// pc == 0, to make unwinding show the context.
+	if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
+		pc = 0
 	}
 
-	print("PC=", hex(c.eip()), " m=", _g_.m.id, "\n")
-	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
-		print("signal arrived during cgo execution\n")
-		gp = _g_.m.lockedg
-	}
-	print("\n")
-
-	level, _, docrash := gotraceback()
-	if level > 0 {
-		goroutineheader(gp)
-		tracebacktrap(uintptr(c.eip()), uintptr(c.esp()), 0, gp)
-		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
-			// tracebackothers on original m skipped this one; trace it now.
-			goroutineheader(_g_.m.curg)
-			traceback(^uintptr(0), ^uintptr(0), 0, gp)
-		} else if crashing == 0 {
-			tracebackothers(gp)
-			print("\n")
-		}
-		dumpregs(c)
-	}
-
-	if docrash {
-		crashing++
-		if crashing < sched.mcount {
-			// There are other m's that need to dump their stacks.
-			// Relay SIGQUIT to the next m by sending it to the current process.
-			// All m's that have already received SIGQUIT have signal masks blocking
-			// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
-			// When the last m receives the SIGQUIT, it will fall through to the call to
-			// crash below. Just in case the relaying gets botched, each m involved in
-			// the relay sleeps for 5 seconds and then does the crash/exit itself.
-			// In expected operation, the last m has received the SIGQUIT and run
-			// crash/exit and the process is gone, all long before any of the
-			// 5-second sleeps have finished.
-			print("\n-----\n\n")
-			raiseproc(_SIGQUIT)
-			usleep(5 * 1000 * 1000)
+	// Only push runtime.sigpanic if pc != 0.
+	// If pc == 0, probably panicked because of a
+	// call to a nil func. Not pushing that onto sp will
+	// make the trace look like a call to runtime.sigpanic instead.
+	// (Otherwise the trace will end at runtime.sigpanic and we
+	// won't get to see who faulted.)
+	if pc != 0 {
+		if sys.RegSize > sys.PtrSize {
+			sp -= sys.PtrSize
+			*(*uintptr)(unsafe.Pointer(sp)) = 0
 		}
-		crash()
+		sp -= sys.PtrSize
+		*(*uintptr)(unsafe.Pointer(sp)) = pc
+		c.set_esp(uint32(sp))
 	}
-
-	exit(2)
+	c.set_eip(uint32(funcPC(sigpanic)))
 }
diff --git a/src/runtime/signal_amd64x.go b/src/runtime/signal_amd64x.go
index 7b51fcc..c8a6513 100644
--- a/src/runtime/signal_amd64x.go
+++ b/src/runtime/signal_amd64x.go
@@ -36,175 +36,59 @@ func dumpregs(c *sigctxt) {
 	print("gs     ", hex(c.gs()), "\n")
 }
 
-var crashing int32
-
-// May run during STW, so write barriers are not allowed.
-//
+//go:nosplit
 //go:nowritebarrierrec
-func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
-	_g_ := getg()
-	c := &sigctxt{info, ctxt}
+func (c *sigctxt) sigpc() uintptr { return uintptr(c.rip()) }
 
-	if sig == _SIGPROF {
-		sigprof(uintptr(c.rip()), uintptr(c.rsp()), 0, gp, _g_.m)
-		return
-	}
+func (c *sigctxt) sigsp() uintptr { return uintptr(c.rsp()) }
+func (c *sigctxt) siglr() uintptr { return 0 }
+func (c *sigctxt) fault() uintptr { return uintptr(c.sigaddr()) }
 
+// preparePanic sets up the stack to look like a call to sigpanic.
+func (c *sigctxt) preparePanic(sig uint32, gp *g) {
 	if GOOS == "darwin" {
-		// x86-64 has 48-bit virtual addresses. The top 16 bits must echo bit 47.
-		// The hardware delivers a different kind of fault for a malformed address
-		// than it does for an attempt to access a valid but unmapped address.
-		// OS X 10.9.2 mishandles the malformed address case, making it look like
-		// a user-generated signal (like someone ran kill -SEGV ourpid).
-		// We pass user-generated signals to os/signal, or else ignore them.
-		// Doing that here - and returning to the faulting code - results in an
-		// infinite loop. It appears the best we can do is rewrite what the kernel
-		// delivers into something more like the truth. The address used below
-		// has very little chance of being the one that caused the fault, but it is
-		// malformed, it is clearly not a real pointer, and if it does get printed
-		// in real life, people will probably search for it and find this code.
-		// There are no Google hits for b01dfacedebac1e or 0xb01dfacedebac1e
-		// as I type this comment.
-		if sig == _SIGSEGV && c.sigcode() == _SI_USER {
-			c.set_sigcode(_SI_USER + 1)
-			c.set_sigaddr(0xb01dfacedebac1e)
-		}
-	}
-
-	flags := int32(_SigThrow)
-	if sig < uint32(len(sigtable)) {
-		flags = sigtable[sig].flags
-	}
-	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
-		// Make it look like a call to the signal func.
-		// Have to pass arguments out of band since
-		// augmenting the stack frame would break
-		// the unwinding code.
-		gp.sig = sig
-		gp.sigcode0 = uintptr(c.sigcode())
-		gp.sigcode1 = uintptr(c.sigaddr())
-		gp.sigpc = uintptr(c.rip())
-
-		if GOOS == "darwin" {
-			// Work around Leopard bug that doesn't set FPE_INTDIV.
-			// Look at instruction to see if it is a divide.
-			// Not necessary in Snow Leopard (si_code will be != 0).
-			if sig == _SIGFPE && gp.sigcode0 == 0 {
-				pc := (*[4]byte)(unsafe.Pointer(gp.sigpc))
-				i := 0
-				if pc[i]&0xF0 == 0x40 { // 64-bit REX prefix
-					i++
-				} else if pc[i] == 0x66 { // 16-bit instruction prefix
-					i++
-				}
-				if pc[i] == 0xF6 || pc[i] == 0xF7 {
-					gp.sigcode0 = _FPE_INTDIV
-				}
+		// Work around Leopard bug that doesn't set FPE_INTDIV.
+		// Look at instruction to see if it is a divide.
+		// Not necessary in Snow Leopard (si_code will be != 0).
+		if sig == _SIGFPE && gp.sigcode0 == 0 {
+			pc := (*[4]byte)(unsafe.Pointer(gp.sigpc))
+			i := 0
+			if pc[i]&0xF0 == 0x40 { // 64-bit REX prefix
+				i++
+			} else if pc[i] == 0x66 { // 16-bit instruction prefix
+				i++
 			}
-		}
-
-		pc := uintptr(c.rip())
-		sp := uintptr(c.rsp())
-
-		// If we don't recognize the PC as code
-		// but we do recognize the top pointer on the stack as code,
-		// then assume this was a call to non-code and treat like
-		// pc == 0, to make unwinding show the context.
-		if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
-			pc = 0
-		}
-
-		// Only push runtime.sigpanic if pc != 0.
-		// If pc == 0, probably panicked because of a
-		// call to a nil func. Not pushing that onto sp will
-		// make the trace look like a call to runtime.sigpanic instead.
-		// (Otherwise the trace will end at runtime.sigpanic and we
-		// won't get to see who faulted.)
-		if pc != 0 {
-			if sys.RegSize > sys.PtrSize {
-				sp -= sys.PtrSize
-				*(*uintptr)(unsafe.Pointer(sp)) = 0
+			if pc[i] == 0xF6 || pc[i] == 0xF7 {
+				gp.sigcode0 = _FPE_INTDIV
 			}
-			sp -= sys.PtrSize
-			*(*uintptr)(unsafe.Pointer(sp)) = pc
-			c.set_rsp(uint64(sp))
 		}
-		c.set_rip(uint64(funcPC(sigpanic)))
-		return
 	}
 
-	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
-		if sigsend(sig) {
-			return
-		}
-	}
+	pc := uintptr(c.rip())
+	sp := uintptr(c.rsp())
 
-	if c.sigcode() == _SI_USER && signal_ignored(sig) {
-		return
+	// If we don't recognize the PC as code
+	// but we do recognize the top pointer on the stack as code,
+	// then assume this was a call to non-code and treat like
+	// pc == 0, to make unwinding show the context.
+	if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
+		pc = 0
 	}
 
-	if flags&_SigKill != 0 {
-		dieFromSignal(int32(sig))
-	}
-
-	if flags&_SigThrow == 0 {
-		return
-	}
-
-	_g_.m.throwing = 1
-	_g_.m.caughtsig.set(gp)
-
-	if crashing == 0 {
-		startpanic()
-	}
-
-	if sig < uint32(len(sigtable)) {
-		print(sigtable[sig].name, "\n")
-	} else {
-		print("Signal ", sig, "\n")
-	}
-
-	print("PC=", hex(c.rip()), " m=", _g_.m.id, "\n")
-	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
-		print("signal arrived during cgo execution\n")
-		gp = _g_.m.lockedg
-	}
-	print("\n")
-
-	level, _, docrash := gotraceback()
-	if level > 0 {
-		goroutineheader(gp)
-		tracebacktrap(uintptr(c.rip()), uintptr(c.rsp()), 0, gp)
-		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
-			// tracebackothers on original m skipped this one; trace it now.
-			goroutineheader(_g_.m.curg)
-			traceback(^uintptr(0), ^uintptr(0), 0, gp)
-		} else if crashing == 0 {
-			tracebackothers(gp)
-			print("\n")
-		}
-		dumpregs(c)
-	}
-
-	if docrash {
-		crashing++
-		if crashing < sched.mcount {
-			// There are other m's that need to dump their stacks.
-			// Relay SIGQUIT to the next m by sending it to the current process.
-			// All m's that have already received SIGQUIT have signal masks blocking
-			// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
-			// When the last m receives the SIGQUIT, it will fall through to the call to
-			// crash below. Just in case the relaying gets botched, each m involved in
-			// the relay sleeps for 5 seconds and then does the crash/exit itself.
-			// In expected operation, the last m has received the SIGQUIT and run
-			// crash/exit and the process is gone, all long before any of the
-			// 5-second sleeps have finished.
-			print("\n-----\n\n")
-			raiseproc(_SIGQUIT)
-			usleep(5 * 1000 * 1000)
+	// Only push runtime.sigpanic if pc != 0.
+	// If pc == 0, probably panicked because of a
+	// call to a nil func. Not pushing that onto sp will
+	// make the trace look like a call to runtime.sigpanic instead.
+	// (Otherwise the trace will end at runtime.sigpanic and we
+	// won't get to see who faulted.)
+	if pc != 0 {
+		if sys.RegSize > sys.PtrSize {
+			sp -= sys.PtrSize
+			*(*uintptr)(unsafe.Pointer(sp)) = 0
 		}
-		crash()
+		sp -= sys.PtrSize
+		*(*uintptr)(unsafe.Pointer(sp)) = pc
+		c.set_rsp(uint64(sp))
 	}
-
-	exit(2)
+	c.set_rip(uint64(funcPC(sigpanic)))
 }
diff --git a/src/runtime/signal_arm.go b/src/runtime/signal_arm.go
index 3b8eaf6..9748544 100644
--- a/src/runtime/signal_arm.go
+++ b/src/runtime/signal_arm.go
@@ -32,139 +32,43 @@ func dumpregs(c *sigctxt) {
 	print("fault   ", hex(c.fault()), "\n")
 }
 
-var crashing int32
-
-// May run during STW, so write barriers are not allowed.
-//
+//go:nosplit
 //go:nowritebarrierrec
-func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
-	_g_ := getg()
-	c := &sigctxt{info, ctxt}
-
-	if sig == _SIGPROF {
-		sigprof(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.lr()), gp, _g_.m)
-		return
-	}
-
-	flags := int32(_SigThrow)
-	if sig < uint32(len(sigtable)) {
-		flags = sigtable[sig].flags
-	}
-	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
-		// Make it look like a call to the signal func.
-		// Have to pass arguments out of band since
-		// augmenting the stack frame would break
-		// the unwinding code.
-		gp.sig = sig
-		gp.sigcode0 = uintptr(c.sigcode())
-		gp.sigcode1 = uintptr(c.fault())
-		gp.sigpc = uintptr(c.pc())
-
-		// We arrange lr, and pc to pretend the panicking
-		// function calls sigpanic directly.
-		// Always save LR to stack so that panics in leaf
-		// functions are correctly handled. This smashes
-		// the stack frame but we're not going back there
-		// anyway.
-		sp := c.sp() - 4
-		c.set_sp(sp)
-		*(*uint32)(unsafe.Pointer(uintptr(sp))) = c.lr()
-
-		pc := gp.sigpc
-
-		// If we don't recognize the PC as code
-		// but we do recognize the link register as code,
-		// then assume this was a call to non-code and treat like
-		// pc == 0, to make unwinding show the context.
-		if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.lr())) != nil {
-			pc = 0
-		}
-
-		// Don't bother saving PC if it's zero, which is
-		// probably a call to a nil func: the old link register
-		// is more useful in the stack trace.
-		if pc != 0 {
-			c.set_lr(uint32(pc))
-		}
-
-		// In case we are panicking from external C code
-		c.set_r10(uint32(uintptr(unsafe.Pointer(gp))))
-		c.set_pc(uint32(funcPC(sigpanic)))
-		return
-	}
-
-	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
-		if sigsend(sig) {
-			return
-		}
-	}
-
-	if c.sigcode() == _SI_USER && signal_ignored(sig) {
-		return
-	}
-
-	if flags&_SigKill != 0 {
-		dieFromSignal(int32(sig))
-	}
-
-	if flags&_SigThrow == 0 {
-		return
-	}
-
-	_g_.m.throwing = 1
-	_g_.m.caughtsig.set(gp)
-
-	if crashing == 0 {
-		startpanic()
-	}
-
-	if sig < uint32(len(sigtable)) {
-		print(sigtable[sig].name, "\n")
-	} else {
-		print("Signal ", sig, "\n")
-	}
-
-	print("PC=", hex(c.pc()), " m=", _g_.m.id, "\n")
-	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
-		print("signal arrived during cgo execution\n")
-		gp = _g_.m.lockedg
-	}
-	print("\n")
-
-	level, _, docrash := gotraceback()
-	if level > 0 {
-		goroutineheader(gp)
-		tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.lr()), gp)
-		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
-			// tracebackothers on original m skipped this one; trace it now.
-			goroutineheader(_g_.m.curg)
-			traceback(^uintptr(0), ^uintptr(0), 0, gp)
-		} else if crashing == 0 {
-			tracebackothers(gp)
-			print("\n")
-		}
-		dumpregs(c)
+func (c *sigctxt) sigpc() uintptr { return uintptr(c.pc()) }
+
+func (c *sigctxt) sigsp() uintptr { return uintptr(c.sp()) }
+func (c *sigctxt) siglr() uintptr { return uintptr(c.lr()) }
+
+// preparePanic sets up the stack to look like a call to sigpanic.
+func (c *sigctxt) preparePanic(sig uint32, gp *g) {
+	// We arrange lr, and pc to pretend the panicking
+	// function calls sigpanic directly.
+	// Always save LR to stack so that panics in leaf
+	// functions are correctly handled. This smashes
+	// the stack frame but we're not going back there
+	// anyway.
+	sp := c.sp() - 4
+	c.set_sp(sp)
+	*(*uint32)(unsafe.Pointer(uintptr(sp))) = c.lr()
+
+	pc := gp.sigpc
+
+	// If we don't recognize the PC as code
+	// but we do recognize the link register as code,
+	// then assume this was a call to non-code and treat like
+	// pc == 0, to make unwinding show the context.
+	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.lr())) != nil {
+		pc = 0
 	}
 
-	if docrash {
-		crashing++
-		if crashing < sched.mcount {
-			// There are other m's that need to dump their stacks.
-			// Relay SIGQUIT to the next m by sending it to the current process.
-			// All m's that have already received SIGQUIT have signal masks blocking
-			// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
-			// When the last m receives the SIGQUIT, it will fall through to the call to
-			// crash below. Just in case the relaying gets botched, each m involved in
-			// the relay sleeps for 5 seconds and then does the crash/exit itself.
-			// In expected operation, the last m has received the SIGQUIT and run
-			// crash/exit and the process is gone, all long before any of the
-			// 5-second sleeps have finished.
-			print("\n-----\n\n")
-			raiseproc(_SIGQUIT)
-			usleep(5 * 1000 * 1000)
-		}
-		crash()
+	// Don't bother saving PC if it's zero, which is
+	// probably a call to a nil func: the old link register
+	// is more useful in the stack trace.
+	if pc != 0 {
+		c.set_lr(uint32(pc))
 	}
 
-	exit(2)
+	// In case we are panicking from external C code
+	c.set_r10(uint32(uintptr(unsafe.Pointer(gp))))
+	c.set_pc(uint32(funcPC(sigpanic)))
 }
diff --git a/src/runtime/signal_arm64.go b/src/runtime/signal_arm64.go
index 0e08623..4c6df42 100644
--- a/src/runtime/signal_arm64.go
+++ b/src/runtime/signal_arm64.go
@@ -48,139 +48,43 @@ func dumpregs(c *sigctxt) {
 	print("fault   ", hex(c.fault()), "\n")
 }
 
-var crashing int32
-
-// May run during STW, so write barriers are not allowed.
-//
+//go:nosplit
 //go:nowritebarrierrec
-func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
-	_g_ := getg()
-	c := &sigctxt{info, ctxt}
-
-	if sig == _SIGPROF {
-		sigprof(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.lr()), gp, _g_.m)
-		return
-	}
-
-	flags := int32(_SigThrow)
-	if sig < uint32(len(sigtable)) {
-		flags = sigtable[sig].flags
-	}
-	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
-		// Make it look like a call to the signal func.
-		// Have to pass arguments out of band since
-		// augmenting the stack frame would break
-		// the unwinding code.
-		gp.sig = sig
-		gp.sigcode0 = uintptr(c.sigcode())
-		gp.sigcode1 = uintptr(c.fault())
-		gp.sigpc = uintptr(c.pc())
-
-		// We arrange lr, and pc to pretend the panicking
-		// function calls sigpanic directly.
-		// Always save LR to stack so that panics in leaf
-		// functions are correctly handled. This smashes
-		// the stack frame but we're not going back there
-		// anyway.
-		sp := c.sp() - sys.SpAlign // needs only sizeof uint64, but must align the stack
-		c.set_sp(sp)
-		*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.lr()
-
-		pc := gp.sigpc
-
-		// If we don't recognize the PC as code
-		// but we do recognize the link register as code,
-		// then assume this was a call to non-code and treat like
-		// pc == 0, to make unwinding show the context.
-		if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.lr())) != nil {
-			pc = 0
-		}
-
-		// Don't bother saving PC if it's zero, which is
-		// probably a call to a nil func: the old link register
-		// is more useful in the stack trace.
-		if pc != 0 {
-			c.set_lr(uint64(pc))
-		}
-
-		// In case we are panicking from external C code
-		c.set_r28(uint64(uintptr(unsafe.Pointer(gp))))
-		c.set_pc(uint64(funcPC(sigpanic)))
-		return
-	}
-
-	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
-		if sigsend(sig) {
-			return
-		}
-	}
-
-	if c.sigcode() == _SI_USER && signal_ignored(sig) {
-		return
-	}
-
-	if flags&_SigKill != 0 {
-		dieFromSignal(int32(sig))
-	}
-
-	if flags&_SigThrow == 0 {
-		return
-	}
-
-	_g_.m.throwing = 1
-	_g_.m.caughtsig.set(gp)
-
-	if crashing == 0 {
-		startpanic()
-	}
-
-	if sig < uint32(len(sigtable)) {
-		print(sigtable[sig].name, "\n")
-	} else {
-		print("Signal ", sig, "\n")
-	}
-
-	print("PC=", hex(c.pc()), " m=", _g_.m.id, "\n")
-	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
-		print("signal arrived during cgo execution\n")
-		gp = _g_.m.lockedg
-	}
-	print("\n")
-
-	level, _, docrash := gotraceback()
-	if level > 0 {
-		goroutineheader(gp)
-		tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.lr()), gp)
-		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
-			// tracebackothers on original m skipped this one; trace it now.
-			goroutineheader(_g_.m.curg)
-			traceback(^uintptr(0), ^uintptr(0), 0, gp)
-		} else if crashing == 0 {
-			tracebackothers(gp)
-			print("\n")
-		}
-		dumpregs(c)
+func (c *sigctxt) sigpc() uintptr { return uintptr(c.pc()) }
+
+func (c *sigctxt) sigsp() uintptr { return uintptr(c.sp()) }
+func (c *sigctxt) siglr() uintptr { return uintptr(c.lr()) }
+
+// preparePanic sets up the stack to look like a call to sigpanic.
+func (c *sigctxt) preparePanic(sig uint32, gp *g) {
+	// We arrange lr, and pc to pretend the panicking
+	// function calls sigpanic directly.
+	// Always save LR to stack so that panics in leaf
+	// functions are correctly handled. This smashes
+	// the stack frame but we're not going back there
+	// anyway.
+	sp := c.sp() - sys.SpAlign // needs only sizeof uint64, but must align the stack
+	c.set_sp(sp)
+	*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.lr()
+
+	pc := gp.sigpc
+
+	// If we don't recognize the PC as code
+	// but we do recognize the link register as code,
+	// then assume this was a call to non-code and treat like
+	// pc == 0, to make unwinding show the context.
+	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.lr())) != nil {
+		pc = 0
 	}
 
-	if docrash {
-		crashing++
-		if crashing < sched.mcount {
-			// There are other m's that need to dump their stacks.
-			// Relay SIGQUIT to the next m by sending it to the current process.
-			// All m's that have already received SIGQUIT have signal masks blocking
-			// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
-			// When the last m receives the SIGQUIT, it will fall through to the call to
-			// crash below. Just in case the relaying gets botched, each m involved in
-			// the relay sleeps for 5 seconds and then does the crash/exit itself.
-			// In expected operation, the last m has received the SIGQUIT and run
-			// crash/exit and the process is gone, all long before any of the
-			// 5-second sleeps have finished.
-			print("\n-----\n\n")
-			raiseproc(_SIGQUIT)
-			usleep(5 * 1000 * 1000)
-		}
-		crash()
+	// Don't bother saving PC if it's zero, which is
+	// probably a call to a nil func: the old link register
+	// is more useful in the stack trace.
+	if pc != 0 {
+		c.set_lr(uint64(pc))
 	}
 
-	exit(2)
+	// In case we are panicking from external C code
+	c.set_r28(uint64(uintptr(unsafe.Pointer(gp))))
+	c.set_pc(uint64(funcPC(sigpanic)))
 }
diff --git a/src/runtime/signal_darwin.go b/src/runtime/signal_darwin.go
index fb06de5..0c5481a 100644
--- a/src/runtime/signal_darwin.go
+++ b/src/runtime/signal_darwin.go
@@ -4,8 +4,6 @@
 
 package runtime
 
-import "unsafe"
-
 type sigTabT struct {
 	flags int32
 	name  string
@@ -45,50 +43,3 @@ var sigtable = [...]sigTabT{
 	/* 30 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
 	/* 31 */ {_SigNotify, "SIGUSR2: user-defined signal 2"},
 }
-
-//go:noescape
-func sigreturn(ctx unsafe.Pointer, infostyle uint32)
-
-//go:nosplit
-//go:nowritebarrierrec
-func sigtrampgo(fn uintptr, infostyle, sig uint32, info *siginfo, ctx unsafe.Pointer) {
-	if sigfwdgo(sig, info, ctx) {
-		sigreturn(ctx, infostyle)
-		return
-	}
-	g := getg()
-	if g == nil {
-		badsignal(uintptr(sig), &sigctxt{info, ctx})
-		sigreturn(ctx, infostyle)
-		return
-	}
-
-	// If some non-Go code called sigaltstack, adjust.
-	sp := uintptr(unsafe.Pointer(&sig))
-	if sp < g.m.gsignal.stack.lo || sp >= g.m.gsignal.stack.hi {
-		var st stackt
-		sigaltstack(nil, &st)
-		if st.ss_flags&_SS_DISABLE != 0 {
-			setg(nil)
-			cgocallback(unsafe.Pointer(funcPC(noSignalStack)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig), 0)
-		}
-		stsp := uintptr(unsafe.Pointer(st.ss_sp))
-		if sp < stsp || sp >= stsp+st.ss_size {
-			setg(nil)
-			cgocallback(unsafe.Pointer(funcPC(sigNotOnStack)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig), 0)
-		}
-		g.m.gsignal.stack.lo = stsp
-		g.m.gsignal.stack.hi = stsp + st.ss_size
-		g.m.gsignal.stackguard0 = stsp + _StackGuard
-		g.m.gsignal.stackguard1 = stsp + _StackGuard
-		g.m.gsignal.stackAlloc = st.ss_size
-		g.m.gsignal.stktopsp = getcallersp(unsafe.Pointer(&sig))
-	}
-
-	setg(g.m.gsignal)
-	c := &sigctxt{info, ctx}
-	c.fixsigcode(sig)
-	sighandler(sig, info, ctx, g)
-	setg(g)
-	sigreturn(ctx, infostyle)
-}
diff --git a/src/runtime/signal_darwin_386.go b/src/runtime/signal_darwin_386.go
index 056f09a..c162959 100644
--- a/src/runtime/signal_darwin_386.go
+++ b/src/runtime/signal_darwin_386.go
@@ -11,16 +11,23 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
-func (c *sigctxt) regs() *regs32   { return &(*ucontext)(c.ctxt).uc_mcontext.ss }
-func (c *sigctxt) eax() uint32     { return c.regs().eax }
-func (c *sigctxt) ebx() uint32     { return c.regs().ebx }
-func (c *sigctxt) ecx() uint32     { return c.regs().ecx }
-func (c *sigctxt) edx() uint32     { return c.regs().edx }
-func (c *sigctxt) edi() uint32     { return c.regs().edi }
-func (c *sigctxt) esi() uint32     { return c.regs().esi }
-func (c *sigctxt) ebp() uint32     { return c.regs().ebp }
-func (c *sigctxt) esp() uint32     { return c.regs().esp }
-func (c *sigctxt) eip() uint32     { return c.regs().eip }
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) regs() *regs32 { return &(*ucontext)(c.ctxt).uc_mcontext.ss }
+
+func (c *sigctxt) eax() uint32 { return c.regs().eax }
+func (c *sigctxt) ebx() uint32 { return c.regs().ebx }
+func (c *sigctxt) ecx() uint32 { return c.regs().ecx }
+func (c *sigctxt) edx() uint32 { return c.regs().edx }
+func (c *sigctxt) edi() uint32 { return c.regs().edi }
+func (c *sigctxt) esi() uint32 { return c.regs().esi }
+func (c *sigctxt) ebp() uint32 { return c.regs().ebp }
+func (c *sigctxt) esp() uint32 { return c.regs().esp }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) eip() uint32 { return c.regs().eip }
+
 func (c *sigctxt) eflags() uint32  { return c.regs().eflags }
 func (c *sigctxt) cs() uint32      { return c.regs().cs }
 func (c *sigctxt) fs() uint32      { return c.regs().fs }
diff --git a/src/runtime/signal_darwin_amd64.go b/src/runtime/signal_darwin_amd64.go
index d219fa4..40de481 100644
--- a/src/runtime/signal_darwin_amd64.go
+++ b/src/runtime/signal_darwin_amd64.go
@@ -11,24 +11,31 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
-func (c *sigctxt) regs() *regs64   { return &(*ucontext)(c.ctxt).uc_mcontext.ss }
-func (c *sigctxt) rax() uint64     { return c.regs().rax }
-func (c *sigctxt) rbx() uint64     { return c.regs().rbx }
-func (c *sigctxt) rcx() uint64     { return c.regs().rcx }
-func (c *sigctxt) rdx() uint64     { return c.regs().rdx }
-func (c *sigctxt) rdi() uint64     { return c.regs().rdi }
-func (c *sigctxt) rsi() uint64     { return c.regs().rsi }
-func (c *sigctxt) rbp() uint64     { return c.regs().rbp }
-func (c *sigctxt) rsp() uint64     { return c.regs().rsp }
-func (c *sigctxt) r8() uint64      { return c.regs().r8 }
-func (c *sigctxt) r9() uint64      { return c.regs().r9 }
-func (c *sigctxt) r10() uint64     { return c.regs().r10 }
-func (c *sigctxt) r11() uint64     { return c.regs().r11 }
-func (c *sigctxt) r12() uint64     { return c.regs().r12 }
-func (c *sigctxt) r13() uint64     { return c.regs().r13 }
-func (c *sigctxt) r14() uint64     { return c.regs().r14 }
-func (c *sigctxt) r15() uint64     { return c.regs().r15 }
-func (c *sigctxt) rip() uint64     { return c.regs().rip }
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) regs() *regs64 { return &(*ucontext)(c.ctxt).uc_mcontext.ss }
+
+func (c *sigctxt) rax() uint64 { return c.regs().rax }
+func (c *sigctxt) rbx() uint64 { return c.regs().rbx }
+func (c *sigctxt) rcx() uint64 { return c.regs().rcx }
+func (c *sigctxt) rdx() uint64 { return c.regs().rdx }
+func (c *sigctxt) rdi() uint64 { return c.regs().rdi }
+func (c *sigctxt) rsi() uint64 { return c.regs().rsi }
+func (c *sigctxt) rbp() uint64 { return c.regs().rbp }
+func (c *sigctxt) rsp() uint64 { return c.regs().rsp }
+func (c *sigctxt) r8() uint64  { return c.regs().r8 }
+func (c *sigctxt) r9() uint64  { return c.regs().r9 }
+func (c *sigctxt) r10() uint64 { return c.regs().r10 }
+func (c *sigctxt) r11() uint64 { return c.regs().r11 }
+func (c *sigctxt) r12() uint64 { return c.regs().r12 }
+func (c *sigctxt) r13() uint64 { return c.regs().r13 }
+func (c *sigctxt) r14() uint64 { return c.regs().r14 }
+func (c *sigctxt) r15() uint64 { return c.regs().r15 }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) rip() uint64 { return c.regs().rip }
+
 func (c *sigctxt) rflags() uint64  { return c.regs().rflags }
 func (c *sigctxt) cs() uint64      { return c.regs().cs }
 func (c *sigctxt) fs() uint64      { return c.regs().fs }
@@ -60,5 +67,25 @@ func (c *sigctxt) fixsigcode(sig uint32) {
 			// SIGTRAP on something other than INT 3.
 			c.set_sigcode(_SI_USER)
 		}
+
+	case _SIGSEGV:
+		// x86-64 has 48-bit virtual addresses. The top 16 bits must echo bit 47.
+		// The hardware delivers a different kind of fault for a malformed address
+		// than it does for an attempt to access a valid but unmapped address.
+		// OS X 10.9.2 mishandles the malformed address case, making it look like
+		// a user-generated signal (like someone ran kill -SEGV ourpid).
+		// We pass user-generated signals to os/signal, or else ignore them.
+		// Doing that here - and returning to the faulting code - results in an
+		// infinite loop. It appears the best we can do is rewrite what the kernel
+		// delivers into something more like the truth. The address used below
+		// has very little chance of being the one that caused the fault, but it is
+		// malformed, it is clearly not a real pointer, and if it does get printed
+		// in real life, people will probably search for it and find this code.
+		// There are no Google hits for b01dfacedebac1e or 0xb01dfacedebac1e
+		// as I type this comment.
+		if c.sigcode() == _SI_USER {
+			c.set_sigcode(_SI_USER + 1)
+			c.set_sigaddr(0xb01dfacedebac1e)
+		}
 	}
 }
diff --git a/src/runtime/signal_darwin_arm.go b/src/runtime/signal_darwin_arm.go
index 82c7c93..c88b90c 100644
--- a/src/runtime/signal_darwin_arm.go
+++ b/src/runtime/signal_darwin_arm.go
@@ -11,23 +11,30 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
-func (c *sigctxt) regs() *regs32   { return &(*ucontext)(c.ctxt).uc_mcontext.ss }
-func (c *sigctxt) r0() uint32      { return c.regs().r[0] }
-func (c *sigctxt) r1() uint32      { return c.regs().r[1] }
-func (c *sigctxt) r2() uint32      { return c.regs().r[2] }
-func (c *sigctxt) r3() uint32      { return c.regs().r[3] }
-func (c *sigctxt) r4() uint32      { return c.regs().r[4] }
-func (c *sigctxt) r5() uint32      { return c.regs().r[5] }
-func (c *sigctxt) r6() uint32      { return c.regs().r[6] }
-func (c *sigctxt) r7() uint32      { return c.regs().r[7] }
-func (c *sigctxt) r8() uint32      { return c.regs().r[8] }
-func (c *sigctxt) r9() uint32      { return c.regs().r[9] }
-func (c *sigctxt) r10() uint32     { return c.regs().r[10] }
-func (c *sigctxt) fp() uint32      { return c.regs().r[11] }
-func (c *sigctxt) ip() uint32      { return c.regs().r[12] }
-func (c *sigctxt) sp() uint32      { return c.regs().sp }
-func (c *sigctxt) lr() uint32      { return c.regs().lr }
-func (c *sigctxt) pc() uint32      { return c.regs().pc }
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) regs() *regs32 { return &(*ucontext)(c.ctxt).uc_mcontext.ss }
+
+func (c *sigctxt) r0() uint32  { return c.regs().r[0] }
+func (c *sigctxt) r1() uint32  { return c.regs().r[1] }
+func (c *sigctxt) r2() uint32  { return c.regs().r[2] }
+func (c *sigctxt) r3() uint32  { return c.regs().r[3] }
+func (c *sigctxt) r4() uint32  { return c.regs().r[4] }
+func (c *sigctxt) r5() uint32  { return c.regs().r[5] }
+func (c *sigctxt) r6() uint32  { return c.regs().r[6] }
+func (c *sigctxt) r7() uint32  { return c.regs().r[7] }
+func (c *sigctxt) r8() uint32  { return c.regs().r[8] }
+func (c *sigctxt) r9() uint32  { return c.regs().r[9] }
+func (c *sigctxt) r10() uint32 { return c.regs().r[10] }
+func (c *sigctxt) fp() uint32  { return c.regs().r[11] }
+func (c *sigctxt) ip() uint32  { return c.regs().r[12] }
+func (c *sigctxt) sp() uint32  { return c.regs().sp }
+func (c *sigctxt) lr() uint32  { return c.regs().lr }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint32 { return c.regs().pc }
+
 func (c *sigctxt) cpsr() uint32    { return c.regs().cpsr }
 func (c *sigctxt) fault() uint32   { return c.info.si_addr }
 func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) }
diff --git a/src/runtime/signal_darwin_arm64.go b/src/runtime/signal_darwin_arm64.go
index 12fa520..b14b9f1 100644
--- a/src/runtime/signal_darwin_arm64.go
+++ b/src/runtime/signal_darwin_arm64.go
@@ -11,40 +11,47 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *regs64 { return &(*ucontext)(c.ctxt).uc_mcontext.ss }
-func (c *sigctxt) r0() uint64    { return c.regs().x[0] }
-func (c *sigctxt) r1() uint64    { return c.regs().x[1] }
-func (c *sigctxt) r2() uint64    { return c.regs().x[2] }
-func (c *sigctxt) r3() uint64    { return c.regs().x[3] }
-func (c *sigctxt) r4() uint64    { return c.regs().x[4] }
-func (c *sigctxt) r5() uint64    { return c.regs().x[5] }
-func (c *sigctxt) r6() uint64    { return c.regs().x[6] }
-func (c *sigctxt) r7() uint64    { return c.regs().x[7] }
-func (c *sigctxt) r8() uint64    { return c.regs().x[8] }
-func (c *sigctxt) r9() uint64    { return c.regs().x[9] }
-func (c *sigctxt) r10() uint64   { return c.regs().x[10] }
-func (c *sigctxt) r11() uint64   { return c.regs().x[11] }
-func (c *sigctxt) r12() uint64   { return c.regs().x[12] }
-func (c *sigctxt) r13() uint64   { return c.regs().x[13] }
-func (c *sigctxt) r14() uint64   { return c.regs().x[14] }
-func (c *sigctxt) r15() uint64   { return c.regs().x[15] }
-func (c *sigctxt) r16() uint64   { return c.regs().x[16] }
-func (c *sigctxt) r17() uint64   { return c.regs().x[17] }
-func (c *sigctxt) r18() uint64   { return c.regs().x[18] }
-func (c *sigctxt) r19() uint64   { return c.regs().x[19] }
-func (c *sigctxt) r20() uint64   { return c.regs().x[20] }
-func (c *sigctxt) r21() uint64   { return c.regs().x[21] }
-func (c *sigctxt) r22() uint64   { return c.regs().x[22] }
-func (c *sigctxt) r23() uint64   { return c.regs().x[23] }
-func (c *sigctxt) r24() uint64   { return c.regs().x[24] }
-func (c *sigctxt) r25() uint64   { return c.regs().x[25] }
-func (c *sigctxt) r26() uint64   { return c.regs().x[26] }
-func (c *sigctxt) r27() uint64   { return c.regs().x[27] }
-func (c *sigctxt) r28() uint64   { return c.regs().x[28] }
-func (c *sigctxt) r29() uint64   { return c.regs().fp }
-func (c *sigctxt) lr() uint64    { return c.regs().lr }
-func (c *sigctxt) sp() uint64    { return c.regs().sp }
-func (c *sigctxt) pc() uint64    { return c.regs().pc }
+
+func (c *sigctxt) r0() uint64  { return c.regs().x[0] }
+func (c *sigctxt) r1() uint64  { return c.regs().x[1] }
+func (c *sigctxt) r2() uint64  { return c.regs().x[2] }
+func (c *sigctxt) r3() uint64  { return c.regs().x[3] }
+func (c *sigctxt) r4() uint64  { return c.regs().x[4] }
+func (c *sigctxt) r5() uint64  { return c.regs().x[5] }
+func (c *sigctxt) r6() uint64  { return c.regs().x[6] }
+func (c *sigctxt) r7() uint64  { return c.regs().x[7] }
+func (c *sigctxt) r8() uint64  { return c.regs().x[8] }
+func (c *sigctxt) r9() uint64  { return c.regs().x[9] }
+func (c *sigctxt) r10() uint64 { return c.regs().x[10] }
+func (c *sigctxt) r11() uint64 { return c.regs().x[11] }
+func (c *sigctxt) r12() uint64 { return c.regs().x[12] }
+func (c *sigctxt) r13() uint64 { return c.regs().x[13] }
+func (c *sigctxt) r14() uint64 { return c.regs().x[14] }
+func (c *sigctxt) r15() uint64 { return c.regs().x[15] }
+func (c *sigctxt) r16() uint64 { return c.regs().x[16] }
+func (c *sigctxt) r17() uint64 { return c.regs().x[17] }
+func (c *sigctxt) r18() uint64 { return c.regs().x[18] }
+func (c *sigctxt) r19() uint64 { return c.regs().x[19] }
+func (c *sigctxt) r20() uint64 { return c.regs().x[20] }
+func (c *sigctxt) r21() uint64 { return c.regs().x[21] }
+func (c *sigctxt) r22() uint64 { return c.regs().x[22] }
+func (c *sigctxt) r23() uint64 { return c.regs().x[23] }
+func (c *sigctxt) r24() uint64 { return c.regs().x[24] }
+func (c *sigctxt) r25() uint64 { return c.regs().x[25] }
+func (c *sigctxt) r26() uint64 { return c.regs().x[26] }
+func (c *sigctxt) r27() uint64 { return c.regs().x[27] }
+func (c *sigctxt) r28() uint64 { return c.regs().x[28] }
+func (c *sigctxt) r29() uint64 { return c.regs().fp }
+func (c *sigctxt) lr() uint64  { return c.regs().lr }
+func (c *sigctxt) sp() uint64  { return c.regs().sp }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint64 { return c.regs().pc }
+
 func (c *sigctxt) fault() uint64 { return uint64(uintptr(unsafe.Pointer(c.info.si_addr))) }
 
 func (c *sigctxt) sigcode() uint64 { return uint64(c.info.si_code) }
diff --git a/src/runtime/signal_dragonfly_amd64.go b/src/runtime/signal_dragonfly_amd64.go
index b32df29..c473edd 100644
--- a/src/runtime/signal_dragonfly_amd64.go
+++ b/src/runtime/signal_dragonfly_amd64.go
@@ -11,26 +11,33 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *mcontext {
 	return (*mcontext)(unsafe.Pointer(&(*ucontext)(c.ctxt).uc_mcontext))
 }
-func (c *sigctxt) rax() uint64     { return c.regs().mc_rax }
-func (c *sigctxt) rbx() uint64     { return c.regs().mc_rbx }
-func (c *sigctxt) rcx() uint64     { return c.regs().mc_rcx }
-func (c *sigctxt) rdx() uint64     { return c.regs().mc_rdx }
-func (c *sigctxt) rdi() uint64     { return c.regs().mc_rdi }
-func (c *sigctxt) rsi() uint64     { return c.regs().mc_rsi }
-func (c *sigctxt) rbp() uint64     { return c.regs().mc_rbp }
-func (c *sigctxt) rsp() uint64     { return c.regs().mc_rsp }
-func (c *sigctxt) r8() uint64      { return c.regs().mc_r8 }
-func (c *sigctxt) r9() uint64      { return c.regs().mc_r9 }
-func (c *sigctxt) r10() uint64     { return c.regs().mc_r10 }
-func (c *sigctxt) r11() uint64     { return c.regs().mc_r11 }
-func (c *sigctxt) r12() uint64     { return c.regs().mc_r12 }
-func (c *sigctxt) r13() uint64     { return c.regs().mc_r13 }
-func (c *sigctxt) r14() uint64     { return c.regs().mc_r14 }
-func (c *sigctxt) r15() uint64     { return c.regs().mc_r15 }
-func (c *sigctxt) rip() uint64     { return c.regs().mc_rip }
+
+func (c *sigctxt) rax() uint64 { return c.regs().mc_rax }
+func (c *sigctxt) rbx() uint64 { return c.regs().mc_rbx }
+func (c *sigctxt) rcx() uint64 { return c.regs().mc_rcx }
+func (c *sigctxt) rdx() uint64 { return c.regs().mc_rdx }
+func (c *sigctxt) rdi() uint64 { return c.regs().mc_rdi }
+func (c *sigctxt) rsi() uint64 { return c.regs().mc_rsi }
+func (c *sigctxt) rbp() uint64 { return c.regs().mc_rbp }
+func (c *sigctxt) rsp() uint64 { return c.regs().mc_rsp }
+func (c *sigctxt) r8() uint64  { return c.regs().mc_r8 }
+func (c *sigctxt) r9() uint64  { return c.regs().mc_r9 }
+func (c *sigctxt) r10() uint64 { return c.regs().mc_r10 }
+func (c *sigctxt) r11() uint64 { return c.regs().mc_r11 }
+func (c *sigctxt) r12() uint64 { return c.regs().mc_r12 }
+func (c *sigctxt) r13() uint64 { return c.regs().mc_r13 }
+func (c *sigctxt) r14() uint64 { return c.regs().mc_r14 }
+func (c *sigctxt) r15() uint64 { return c.regs().mc_r15 }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) rip() uint64 { return c.regs().mc_rip }
+
 func (c *sigctxt) rflags() uint64  { return c.regs().mc_rflags }
 func (c *sigctxt) cs() uint64      { return c.regs().mc_cs }
 func (c *sigctxt) fs() uint64      { return c.regs().mc_ss }
diff --git a/src/runtime/signal_freebsd.go b/src/runtime/signal_freebsd.go
index c6c1269..7ce7217 100644
--- a/src/runtime/signal_freebsd.go
+++ b/src/runtime/signal_freebsd.go
@@ -4,8 +4,6 @@
 
 package runtime
 
-import "unsafe"
-
 type sigTabT struct {
 	flags int32
 	name  string
@@ -46,42 +44,3 @@ var sigtable = [...]sigTabT{
 	/* 31 */ {_SigNotify, "SIGUSR2: user-defined signal 2"},
 	/* 32 */ {_SigNotify, "SIGTHR: reserved"},
 }
-
-//go:nosplit
-//go:nowritebarrierrec
-func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
-	if sigfwdgo(sig, info, ctx) {
-		return
-	}
-	g := getg()
-	if g == nil {
-		badsignal(uintptr(sig), &sigctxt{info, ctx})
-		return
-	}
-
-	// If some non-Go code called sigaltstack, adjust.
-	sp := uintptr(unsafe.Pointer(&sig))
-	if sp < g.m.gsignal.stack.lo || sp >= g.m.gsignal.stack.hi {
-		var st stackt
-		sigaltstack(nil, &st)
-		if st.ss_flags&_SS_DISABLE != 0 {
-			setg(nil)
-			cgocallback(unsafe.Pointer(funcPC(noSignalStack)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig), 0)
-		}
-		stsp := uintptr(unsafe.Pointer(st.ss_sp))
-		if sp < stsp || sp >= stsp+st.ss_size {
-			setg(nil)
-			cgocallback(unsafe.Pointer(funcPC(sigNotOnStack)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig), 0)
-		}
-		g.m.gsignal.stack.lo = stsp
-		g.m.gsignal.stack.hi = stsp + st.ss_size
-		g.m.gsignal.stackguard0 = stsp + _StackGuard
-		g.m.gsignal.stackguard1 = stsp + _StackGuard
-		g.m.gsignal.stackAlloc = st.ss_size
-		g.m.gsignal.stktopsp = getcallersp(unsafe.Pointer(&sig))
-	}
-
-	setg(g.m.gsignal)
-	sighandler(sig, info, ctx, g)
-	setg(g)
-}
diff --git a/src/runtime/signal_freebsd_386.go b/src/runtime/signal_freebsd_386.go
index 092e6df..f7cc0df 100644
--- a/src/runtime/signal_freebsd_386.go
+++ b/src/runtime/signal_freebsd_386.go
@@ -11,16 +11,23 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *mcontext { return &(*ucontext)(c.ctxt).uc_mcontext }
-func (c *sigctxt) eax() uint32     { return c.regs().mc_eax }
-func (c *sigctxt) ebx() uint32     { return c.regs().mc_ebx }
-func (c *sigctxt) ecx() uint32     { return c.regs().mc_ecx }
-func (c *sigctxt) edx() uint32     { return c.regs().mc_edx }
-func (c *sigctxt) edi() uint32     { return c.regs().mc_edi }
-func (c *sigctxt) esi() uint32     { return c.regs().mc_esi }
-func (c *sigctxt) ebp() uint32     { return c.regs().mc_ebp }
-func (c *sigctxt) esp() uint32     { return c.regs().mc_esp }
-func (c *sigctxt) eip() uint32     { return c.regs().mc_eip }
+
+func (c *sigctxt) eax() uint32 { return c.regs().mc_eax }
+func (c *sigctxt) ebx() uint32 { return c.regs().mc_ebx }
+func (c *sigctxt) ecx() uint32 { return c.regs().mc_ecx }
+func (c *sigctxt) edx() uint32 { return c.regs().mc_edx }
+func (c *sigctxt) edi() uint32 { return c.regs().mc_edi }
+func (c *sigctxt) esi() uint32 { return c.regs().mc_esi }
+func (c *sigctxt) ebp() uint32 { return c.regs().mc_ebp }
+func (c *sigctxt) esp() uint32 { return c.regs().mc_esp }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) eip() uint32 { return c.regs().mc_eip }
+
 func (c *sigctxt) eflags() uint32  { return c.regs().mc_eflags }
 func (c *sigctxt) cs() uint32      { return c.regs().mc_cs }
 func (c *sigctxt) fs() uint32      { return c.regs().mc_fs }
diff --git a/src/runtime/signal_freebsd_amd64.go b/src/runtime/signal_freebsd_amd64.go
index a0b4a72..20b86e7 100644
--- a/src/runtime/signal_freebsd_amd64.go
+++ b/src/runtime/signal_freebsd_amd64.go
@@ -11,26 +11,33 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *mcontext {
 	return (*mcontext)(unsafe.Pointer(&(*ucontext)(c.ctxt).uc_mcontext))
 }
-func (c *sigctxt) rax() uint64     { return c.regs().mc_rax }
-func (c *sigctxt) rbx() uint64     { return c.regs().mc_rbx }
-func (c *sigctxt) rcx() uint64     { return c.regs().mc_rcx }
-func (c *sigctxt) rdx() uint64     { return c.regs().mc_rdx }
-func (c *sigctxt) rdi() uint64     { return c.regs().mc_rdi }
-func (c *sigctxt) rsi() uint64     { return c.regs().mc_rsi }
-func (c *sigctxt) rbp() uint64     { return c.regs().mc_rbp }
-func (c *sigctxt) rsp() uint64     { return c.regs().mc_rsp }
-func (c *sigctxt) r8() uint64      { return c.regs().mc_r8 }
-func (c *sigctxt) r9() uint64      { return c.regs().mc_r9 }
-func (c *sigctxt) r10() uint64     { return c.regs().mc_r10 }
-func (c *sigctxt) r11() uint64     { return c.regs().mc_r11 }
-func (c *sigctxt) r12() uint64     { return c.regs().mc_r12 }
-func (c *sigctxt) r13() uint64     { return c.regs().mc_r13 }
-func (c *sigctxt) r14() uint64     { return c.regs().mc_r14 }
-func (c *sigctxt) r15() uint64     { return c.regs().mc_r15 }
-func (c *sigctxt) rip() uint64     { return c.regs().mc_rip }
+
+func (c *sigctxt) rax() uint64 { return c.regs().mc_rax }
+func (c *sigctxt) rbx() uint64 { return c.regs().mc_rbx }
+func (c *sigctxt) rcx() uint64 { return c.regs().mc_rcx }
+func (c *sigctxt) rdx() uint64 { return c.regs().mc_rdx }
+func (c *sigctxt) rdi() uint64 { return c.regs().mc_rdi }
+func (c *sigctxt) rsi() uint64 { return c.regs().mc_rsi }
+func (c *sigctxt) rbp() uint64 { return c.regs().mc_rbp }
+func (c *sigctxt) rsp() uint64 { return c.regs().mc_rsp }
+func (c *sigctxt) r8() uint64  { return c.regs().mc_r8 }
+func (c *sigctxt) r9() uint64  { return c.regs().mc_r9 }
+func (c *sigctxt) r10() uint64 { return c.regs().mc_r10 }
+func (c *sigctxt) r11() uint64 { return c.regs().mc_r11 }
+func (c *sigctxt) r12() uint64 { return c.regs().mc_r12 }
+func (c *sigctxt) r13() uint64 { return c.regs().mc_r13 }
+func (c *sigctxt) r14() uint64 { return c.regs().mc_r14 }
+func (c *sigctxt) r15() uint64 { return c.regs().mc_r15 }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) rip() uint64 { return c.regs().mc_rip }
+
 func (c *sigctxt) rflags() uint64  { return c.regs().mc_rflags }
 func (c *sigctxt) cs() uint64      { return c.regs().mc_cs }
 func (c *sigctxt) fs() uint64      { return uint64(c.regs().mc_fs) }
diff --git a/src/runtime/signal_freebsd_arm.go b/src/runtime/signal_freebsd_arm.go
index 0357304..9601370 100644
--- a/src/runtime/signal_freebsd_arm.go
+++ b/src/runtime/signal_freebsd_arm.go
@@ -11,23 +11,30 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *mcontext { return &(*ucontext)(c.ctxt).uc_mcontext }
-func (c *sigctxt) r0() uint32      { return c.regs().__gregs[0] }
-func (c *sigctxt) r1() uint32      { return c.regs().__gregs[1] }
-func (c *sigctxt) r2() uint32      { return c.regs().__gregs[2] }
-func (c *sigctxt) r3() uint32      { return c.regs().__gregs[3] }
-func (c *sigctxt) r4() uint32      { return c.regs().__gregs[4] }
-func (c *sigctxt) r5() uint32      { return c.regs().__gregs[5] }
-func (c *sigctxt) r6() uint32      { return c.regs().__gregs[6] }
-func (c *sigctxt) r7() uint32      { return c.regs().__gregs[7] }
-func (c *sigctxt) r8() uint32      { return c.regs().__gregs[8] }
-func (c *sigctxt) r9() uint32      { return c.regs().__gregs[9] }
-func (c *sigctxt) r10() uint32     { return c.regs().__gregs[10] }
-func (c *sigctxt) fp() uint32      { return c.regs().__gregs[11] }
-func (c *sigctxt) ip() uint32      { return c.regs().__gregs[12] }
-func (c *sigctxt) sp() uint32      { return c.regs().__gregs[13] }
-func (c *sigctxt) lr() uint32      { return c.regs().__gregs[14] }
-func (c *sigctxt) pc() uint32      { return c.regs().__gregs[15] }
+
+func (c *sigctxt) r0() uint32  { return c.regs().__gregs[0] }
+func (c *sigctxt) r1() uint32  { return c.regs().__gregs[1] }
+func (c *sigctxt) r2() uint32  { return c.regs().__gregs[2] }
+func (c *sigctxt) r3() uint32  { return c.regs().__gregs[3] }
+func (c *sigctxt) r4() uint32  { return c.regs().__gregs[4] }
+func (c *sigctxt) r5() uint32  { return c.regs().__gregs[5] }
+func (c *sigctxt) r6() uint32  { return c.regs().__gregs[6] }
+func (c *sigctxt) r7() uint32  { return c.regs().__gregs[7] }
+func (c *sigctxt) r8() uint32  { return c.regs().__gregs[8] }
+func (c *sigctxt) r9() uint32  { return c.regs().__gregs[9] }
+func (c *sigctxt) r10() uint32 { return c.regs().__gregs[10] }
+func (c *sigctxt) fp() uint32  { return c.regs().__gregs[11] }
+func (c *sigctxt) ip() uint32  { return c.regs().__gregs[12] }
+func (c *sigctxt) sp() uint32  { return c.regs().__gregs[13] }
+func (c *sigctxt) lr() uint32  { return c.regs().__gregs[14] }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint32 { return c.regs().__gregs[15] }
+
 func (c *sigctxt) cpsr() uint32    { return c.regs().__gregs[16] }
 func (c *sigctxt) fault() uint32   { return uint32(c.info.si_addr) }
 func (c *sigctxt) trap() uint32    { return 0 }
diff --git a/src/runtime/signal_linux_386.go b/src/runtime/signal_linux_386.go
index 415f361..13d9df4 100644
--- a/src/runtime/signal_linux_386.go
+++ b/src/runtime/signal_linux_386.go
@@ -14,22 +14,29 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *sigcontext { return &(*ucontext)(c.ctxt).uc_mcontext }
-func (c *sigctxt) eax() uint32       { return c.regs().eax }
-func (c *sigctxt) ebx() uint32       { return c.regs().ebx }
-func (c *sigctxt) ecx() uint32       { return c.regs().ecx }
-func (c *sigctxt) edx() uint32       { return c.regs().edx }
-func (c *sigctxt) edi() uint32       { return c.regs().edi }
-func (c *sigctxt) esi() uint32       { return c.regs().esi }
-func (c *sigctxt) ebp() uint32       { return c.regs().ebp }
-func (c *sigctxt) esp() uint32       { return c.regs().esp }
-func (c *sigctxt) eip() uint32       { return c.regs().eip }
-func (c *sigctxt) eflags() uint32    { return c.regs().eflags }
-func (c *sigctxt) cs() uint32        { return uint32(c.regs().cs) }
-func (c *sigctxt) fs() uint32        { return uint32(c.regs().fs) }
-func (c *sigctxt) gs() uint32        { return uint32(c.regs().gs) }
-func (c *sigctxt) sigcode() uint32   { return uint32(c.info.si_code) }
-func (c *sigctxt) sigaddr() uint32   { return c.info.si_addr }
+
+func (c *sigctxt) eax() uint32 { return c.regs().eax }
+func (c *sigctxt) ebx() uint32 { return c.regs().ebx }
+func (c *sigctxt) ecx() uint32 { return c.regs().ecx }
+func (c *sigctxt) edx() uint32 { return c.regs().edx }
+func (c *sigctxt) edi() uint32 { return c.regs().edi }
+func (c *sigctxt) esi() uint32 { return c.regs().esi }
+func (c *sigctxt) ebp() uint32 { return c.regs().ebp }
+func (c *sigctxt) esp() uint32 { return c.regs().esp }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) eip() uint32 { return c.regs().eip }
+
+func (c *sigctxt) eflags() uint32  { return c.regs().eflags }
+func (c *sigctxt) cs() uint32      { return uint32(c.regs().cs) }
+func (c *sigctxt) fs() uint32      { return uint32(c.regs().fs) }
+func (c *sigctxt) gs() uint32      { return uint32(c.regs().gs) }
+func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) }
+func (c *sigctxt) sigaddr() uint32 { return c.info.si_addr }
 
 func (c *sigctxt) set_eip(x uint32)     { c.regs().eip = x }
 func (c *sigctxt) set_esp(x uint32)     { c.regs().esp = x }
diff --git a/src/runtime/signal_linux_amd64.go b/src/runtime/signal_linux_amd64.go
index 433747f..210e896 100644
--- a/src/runtime/signal_linux_amd64.go
+++ b/src/runtime/signal_linux_amd64.go
@@ -14,26 +14,33 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *sigcontext {
 	return (*sigcontext)(unsafe.Pointer(&(*ucontext)(c.ctxt).uc_mcontext))
 }
-func (c *sigctxt) rax() uint64     { return c.regs().rax }
-func (c *sigctxt) rbx() uint64     { return c.regs().rbx }
-func (c *sigctxt) rcx() uint64     { return c.regs().rcx }
-func (c *sigctxt) rdx() uint64     { return c.regs().rdx }
-func (c *sigctxt) rdi() uint64     { return c.regs().rdi }
-func (c *sigctxt) rsi() uint64     { return c.regs().rsi }
-func (c *sigctxt) rbp() uint64     { return c.regs().rbp }
-func (c *sigctxt) rsp() uint64     { return c.regs().rsp }
-func (c *sigctxt) r8() uint64      { return c.regs().r8 }
-func (c *sigctxt) r9() uint64      { return c.regs().r9 }
-func (c *sigctxt) r10() uint64     { return c.regs().r10 }
-func (c *sigctxt) r11() uint64     { return c.regs().r11 }
-func (c *sigctxt) r12() uint64     { return c.regs().r12 }
-func (c *sigctxt) r13() uint64     { return c.regs().r13 }
-func (c *sigctxt) r14() uint64     { return c.regs().r14 }
-func (c *sigctxt) r15() uint64     { return c.regs().r15 }
-func (c *sigctxt) rip() uint64     { return c.regs().rip }
+
+func (c *sigctxt) rax() uint64 { return c.regs().rax }
+func (c *sigctxt) rbx() uint64 { return c.regs().rbx }
+func (c *sigctxt) rcx() uint64 { return c.regs().rcx }
+func (c *sigctxt) rdx() uint64 { return c.regs().rdx }
+func (c *sigctxt) rdi() uint64 { return c.regs().rdi }
+func (c *sigctxt) rsi() uint64 { return c.regs().rsi }
+func (c *sigctxt) rbp() uint64 { return c.regs().rbp }
+func (c *sigctxt) rsp() uint64 { return c.regs().rsp }
+func (c *sigctxt) r8() uint64  { return c.regs().r8 }
+func (c *sigctxt) r9() uint64  { return c.regs().r9 }
+func (c *sigctxt) r10() uint64 { return c.regs().r10 }
+func (c *sigctxt) r11() uint64 { return c.regs().r11 }
+func (c *sigctxt) r12() uint64 { return c.regs().r12 }
+func (c *sigctxt) r13() uint64 { return c.regs().r13 }
+func (c *sigctxt) r14() uint64 { return c.regs().r14 }
+func (c *sigctxt) r15() uint64 { return c.regs().r15 }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) rip() uint64 { return c.regs().rip }
+
 func (c *sigctxt) rflags() uint64  { return c.regs().eflags }
 func (c *sigctxt) cs() uint64      { return uint64(c.regs().cs) }
 func (c *sigctxt) fs() uint64      { return uint64(c.regs().fs) }
diff --git a/src/runtime/signal_linux_arm.go b/src/runtime/signal_linux_arm.go
index cd6a0d7..06a57b8 100644
--- a/src/runtime/signal_linux_arm.go
+++ b/src/runtime/signal_linux_arm.go
@@ -14,28 +14,35 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *sigcontext { return &(*ucontext)(c.ctxt).uc_mcontext }
-func (c *sigctxt) r0() uint32        { return c.regs().r0 }
-func (c *sigctxt) r1() uint32        { return c.regs().r1 }
-func (c *sigctxt) r2() uint32        { return c.regs().r2 }
-func (c *sigctxt) r3() uint32        { return c.regs().r3 }
-func (c *sigctxt) r4() uint32        { return c.regs().r4 }
-func (c *sigctxt) r5() uint32        { return c.regs().r5 }
-func (c *sigctxt) r6() uint32        { return c.regs().r6 }
-func (c *sigctxt) r7() uint32        { return c.regs().r7 }
-func (c *sigctxt) r8() uint32        { return c.regs().r8 }
-func (c *sigctxt) r9() uint32        { return c.regs().r9 }
-func (c *sigctxt) r10() uint32       { return c.regs().r10 }
-func (c *sigctxt) fp() uint32        { return c.regs().fp }
-func (c *sigctxt) ip() uint32        { return c.regs().ip }
-func (c *sigctxt) sp() uint32        { return c.regs().sp }
-func (c *sigctxt) lr() uint32        { return c.regs().lr }
-func (c *sigctxt) pc() uint32        { return c.regs().pc }
-func (c *sigctxt) cpsr() uint32      { return c.regs().cpsr }
-func (c *sigctxt) fault() uint32     { return c.regs().fault_address }
-func (c *sigctxt) trap() uint32      { return c.regs().trap_no }
-func (c *sigctxt) error() uint32     { return c.regs().error_code }
-func (c *sigctxt) oldmask() uint32   { return c.regs().oldmask }
+
+func (c *sigctxt) r0() uint32  { return c.regs().r0 }
+func (c *sigctxt) r1() uint32  { return c.regs().r1 }
+func (c *sigctxt) r2() uint32  { return c.regs().r2 }
+func (c *sigctxt) r3() uint32  { return c.regs().r3 }
+func (c *sigctxt) r4() uint32  { return c.regs().r4 }
+func (c *sigctxt) r5() uint32  { return c.regs().r5 }
+func (c *sigctxt) r6() uint32  { return c.regs().r6 }
+func (c *sigctxt) r7() uint32  { return c.regs().r7 }
+func (c *sigctxt) r8() uint32  { return c.regs().r8 }
+func (c *sigctxt) r9() uint32  { return c.regs().r9 }
+func (c *sigctxt) r10() uint32 { return c.regs().r10 }
+func (c *sigctxt) fp() uint32  { return c.regs().fp }
+func (c *sigctxt) ip() uint32  { return c.regs().ip }
+func (c *sigctxt) sp() uint32  { return c.regs().sp }
+func (c *sigctxt) lr() uint32  { return c.regs().lr }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint32 { return c.regs().pc }
+
+func (c *sigctxt) cpsr() uint32    { return c.regs().cpsr }
+func (c *sigctxt) fault() uint32   { return c.regs().fault_address }
+func (c *sigctxt) trap() uint32    { return c.regs().trap_no }
+func (c *sigctxt) error() uint32   { return c.regs().error_code }
+func (c *sigctxt) oldmask() uint32 { return c.regs().oldmask }
 
 func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) }
 func (c *sigctxt) sigaddr() uint32 { return c.info.si_addr }
diff --git a/src/runtime/signal_linux_arm64.go b/src/runtime/signal_linux_arm64.go
index 4964e7b..f3d4d38 100644
--- a/src/runtime/signal_linux_arm64.go
+++ b/src/runtime/signal_linux_arm64.go
@@ -14,42 +14,49 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *sigcontext { return &(*ucontext)(c.ctxt).uc_mcontext }
-func (c *sigctxt) r0() uint64        { return c.regs().regs[0] }
-func (c *sigctxt) r1() uint64        { return c.regs().regs[1] }
-func (c *sigctxt) r2() uint64        { return c.regs().regs[2] }
-func (c *sigctxt) r3() uint64        { return c.regs().regs[3] }
-func (c *sigctxt) r4() uint64        { return c.regs().regs[4] }
-func (c *sigctxt) r5() uint64        { return c.regs().regs[5] }
-func (c *sigctxt) r6() uint64        { return c.regs().regs[6] }
-func (c *sigctxt) r7() uint64        { return c.regs().regs[7] }
-func (c *sigctxt) r8() uint64        { return c.regs().regs[8] }
-func (c *sigctxt) r9() uint64        { return c.regs().regs[9] }
-func (c *sigctxt) r10() uint64       { return c.regs().regs[10] }
-func (c *sigctxt) r11() uint64       { return c.regs().regs[11] }
-func (c *sigctxt) r12() uint64       { return c.regs().regs[12] }
-func (c *sigctxt) r13() uint64       { return c.regs().regs[13] }
-func (c *sigctxt) r14() uint64       { return c.regs().regs[14] }
-func (c *sigctxt) r15() uint64       { return c.regs().regs[15] }
-func (c *sigctxt) r16() uint64       { return c.regs().regs[16] }
-func (c *sigctxt) r17() uint64       { return c.regs().regs[17] }
-func (c *sigctxt) r18() uint64       { return c.regs().regs[18] }
-func (c *sigctxt) r19() uint64       { return c.regs().regs[19] }
-func (c *sigctxt) r20() uint64       { return c.regs().regs[20] }
-func (c *sigctxt) r21() uint64       { return c.regs().regs[21] }
-func (c *sigctxt) r22() uint64       { return c.regs().regs[22] }
-func (c *sigctxt) r23() uint64       { return c.regs().regs[23] }
-func (c *sigctxt) r24() uint64       { return c.regs().regs[24] }
-func (c *sigctxt) r25() uint64       { return c.regs().regs[25] }
-func (c *sigctxt) r26() uint64       { return c.regs().regs[26] }
-func (c *sigctxt) r27() uint64       { return c.regs().regs[27] }
-func (c *sigctxt) r28() uint64       { return c.regs().regs[28] }
-func (c *sigctxt) r29() uint64       { return c.regs().regs[29] }
-func (c *sigctxt) lr() uint64        { return c.regs().regs[30] }
-func (c *sigctxt) sp() uint64        { return c.regs().sp }
-func (c *sigctxt) pc() uint64        { return c.regs().pc }
-func (c *sigctxt) pstate() uint64    { return c.regs().pstate }
-func (c *sigctxt) fault() uint64     { return c.regs().fault_address }
+
+func (c *sigctxt) r0() uint64  { return c.regs().regs[0] }
+func (c *sigctxt) r1() uint64  { return c.regs().regs[1] }
+func (c *sigctxt) r2() uint64  { return c.regs().regs[2] }
+func (c *sigctxt) r3() uint64  { return c.regs().regs[3] }
+func (c *sigctxt) r4() uint64  { return c.regs().regs[4] }
+func (c *sigctxt) r5() uint64  { return c.regs().regs[5] }
+func (c *sigctxt) r6() uint64  { return c.regs().regs[6] }
+func (c *sigctxt) r7() uint64  { return c.regs().regs[7] }
+func (c *sigctxt) r8() uint64  { return c.regs().regs[8] }
+func (c *sigctxt) r9() uint64  { return c.regs().regs[9] }
+func (c *sigctxt) r10() uint64 { return c.regs().regs[10] }
+func (c *sigctxt) r11() uint64 { return c.regs().regs[11] }
+func (c *sigctxt) r12() uint64 { return c.regs().regs[12] }
+func (c *sigctxt) r13() uint64 { return c.regs().regs[13] }
+func (c *sigctxt) r14() uint64 { return c.regs().regs[14] }
+func (c *sigctxt) r15() uint64 { return c.regs().regs[15] }
+func (c *sigctxt) r16() uint64 { return c.regs().regs[16] }
+func (c *sigctxt) r17() uint64 { return c.regs().regs[17] }
+func (c *sigctxt) r18() uint64 { return c.regs().regs[18] }
+func (c *sigctxt) r19() uint64 { return c.regs().regs[19] }
+func (c *sigctxt) r20() uint64 { return c.regs().regs[20] }
+func (c *sigctxt) r21() uint64 { return c.regs().regs[21] }
+func (c *sigctxt) r22() uint64 { return c.regs().regs[22] }
+func (c *sigctxt) r23() uint64 { return c.regs().regs[23] }
+func (c *sigctxt) r24() uint64 { return c.regs().regs[24] }
+func (c *sigctxt) r25() uint64 { return c.regs().regs[25] }
+func (c *sigctxt) r26() uint64 { return c.regs().regs[26] }
+func (c *sigctxt) r27() uint64 { return c.regs().regs[27] }
+func (c *sigctxt) r28() uint64 { return c.regs().regs[28] }
+func (c *sigctxt) r29() uint64 { return c.regs().regs[29] }
+func (c *sigctxt) lr() uint64  { return c.regs().regs[30] }
+func (c *sigctxt) sp() uint64  { return c.regs().sp }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint64 { return c.regs().pc }
+
+func (c *sigctxt) pstate() uint64 { return c.regs().pstate }
+func (c *sigctxt) fault() uint64  { return c.regs().fault_address }
 
 func (c *sigctxt) sigcode() uint64 { return uint64(c.info.si_code) }
 func (c *sigctxt) sigaddr() uint64 { return c.info.si_addr }
diff --git a/src/runtime/signal_linux_mips64x.go b/src/runtime/signal_linux_mips64x.go
index 0f590e4..9e0cf42 100644
--- a/src/runtime/signal_linux_mips64x.go
+++ b/src/runtime/signal_linux_mips64x.go
@@ -17,44 +17,51 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *sigcontext { return &(*ucontext)(c.ctxt).uc_mcontext }
-func (c *sigctxt) r0() uint64        { return c.regs().sc_regs[0] }
-func (c *sigctxt) r1() uint64        { return c.regs().sc_regs[1] }
-func (c *sigctxt) r2() uint64        { return c.regs().sc_regs[2] }
-func (c *sigctxt) r3() uint64        { return c.regs().sc_regs[3] }
-func (c *sigctxt) r4() uint64        { return c.regs().sc_regs[4] }
-func (c *sigctxt) r5() uint64        { return c.regs().sc_regs[5] }
-func (c *sigctxt) r6() uint64        { return c.regs().sc_regs[6] }
-func (c *sigctxt) r7() uint64        { return c.regs().sc_regs[7] }
-func (c *sigctxt) r8() uint64        { return c.regs().sc_regs[8] }
-func (c *sigctxt) r9() uint64        { return c.regs().sc_regs[9] }
-func (c *sigctxt) r10() uint64       { return c.regs().sc_regs[10] }
-func (c *sigctxt) r11() uint64       { return c.regs().sc_regs[11] }
-func (c *sigctxt) r12() uint64       { return c.regs().sc_regs[12] }
-func (c *sigctxt) r13() uint64       { return c.regs().sc_regs[13] }
-func (c *sigctxt) r14() uint64       { return c.regs().sc_regs[14] }
-func (c *sigctxt) r15() uint64       { return c.regs().sc_regs[15] }
-func (c *sigctxt) r16() uint64       { return c.regs().sc_regs[16] }
-func (c *sigctxt) r17() uint64       { return c.regs().sc_regs[17] }
-func (c *sigctxt) r18() uint64       { return c.regs().sc_regs[18] }
-func (c *sigctxt) r19() uint64       { return c.regs().sc_regs[19] }
-func (c *sigctxt) r20() uint64       { return c.regs().sc_regs[20] }
-func (c *sigctxt) r21() uint64       { return c.regs().sc_regs[21] }
-func (c *sigctxt) r22() uint64       { return c.regs().sc_regs[22] }
-func (c *sigctxt) r23() uint64       { return c.regs().sc_regs[23] }
-func (c *sigctxt) r24() uint64       { return c.regs().sc_regs[24] }
-func (c *sigctxt) r25() uint64       { return c.regs().sc_regs[25] }
-func (c *sigctxt) r26() uint64       { return c.regs().sc_regs[26] }
-func (c *sigctxt) r27() uint64       { return c.regs().sc_regs[27] }
-func (c *sigctxt) r28() uint64       { return c.regs().sc_regs[28] }
-func (c *sigctxt) r29() uint64       { return c.regs().sc_regs[29] }
-func (c *sigctxt) r30() uint64       { return c.regs().sc_regs[30] }
-func (c *sigctxt) r31() uint64       { return c.regs().sc_regs[31] }
-func (c *sigctxt) sp() uint64        { return c.regs().sc_regs[29] }
-func (c *sigctxt) pc() uint64        { return c.regs().sc_pc }
-func (c *sigctxt) link() uint64      { return c.regs().sc_regs[31] }
-func (c *sigctxt) lo() uint64        { return c.regs().sc_mdlo }
-func (c *sigctxt) hi() uint64        { return c.regs().sc_mdhi }
+
+func (c *sigctxt) r0() uint64  { return c.regs().sc_regs[0] }
+func (c *sigctxt) r1() uint64  { return c.regs().sc_regs[1] }
+func (c *sigctxt) r2() uint64  { return c.regs().sc_regs[2] }
+func (c *sigctxt) r3() uint64  { return c.regs().sc_regs[3] }
+func (c *sigctxt) r4() uint64  { return c.regs().sc_regs[4] }
+func (c *sigctxt) r5() uint64  { return c.regs().sc_regs[5] }
+func (c *sigctxt) r6() uint64  { return c.regs().sc_regs[6] }
+func (c *sigctxt) r7() uint64  { return c.regs().sc_regs[7] }
+func (c *sigctxt) r8() uint64  { return c.regs().sc_regs[8] }
+func (c *sigctxt) r9() uint64  { return c.regs().sc_regs[9] }
+func (c *sigctxt) r10() uint64 { return c.regs().sc_regs[10] }
+func (c *sigctxt) r11() uint64 { return c.regs().sc_regs[11] }
+func (c *sigctxt) r12() uint64 { return c.regs().sc_regs[12] }
+func (c *sigctxt) r13() uint64 { return c.regs().sc_regs[13] }
+func (c *sigctxt) r14() uint64 { return c.regs().sc_regs[14] }
+func (c *sigctxt) r15() uint64 { return c.regs().sc_regs[15] }
+func (c *sigctxt) r16() uint64 { return c.regs().sc_regs[16] }
+func (c *sigctxt) r17() uint64 { return c.regs().sc_regs[17] }
+func (c *sigctxt) r18() uint64 { return c.regs().sc_regs[18] }
+func (c *sigctxt) r19() uint64 { return c.regs().sc_regs[19] }
+func (c *sigctxt) r20() uint64 { return c.regs().sc_regs[20] }
+func (c *sigctxt) r21() uint64 { return c.regs().sc_regs[21] }
+func (c *sigctxt) r22() uint64 { return c.regs().sc_regs[22] }
+func (c *sigctxt) r23() uint64 { return c.regs().sc_regs[23] }
+func (c *sigctxt) r24() uint64 { return c.regs().sc_regs[24] }
+func (c *sigctxt) r25() uint64 { return c.regs().sc_regs[25] }
+func (c *sigctxt) r26() uint64 { return c.regs().sc_regs[26] }
+func (c *sigctxt) r27() uint64 { return c.regs().sc_regs[27] }
+func (c *sigctxt) r28() uint64 { return c.regs().sc_regs[28] }
+func (c *sigctxt) r29() uint64 { return c.regs().sc_regs[29] }
+func (c *sigctxt) r30() uint64 { return c.regs().sc_regs[30] }
+func (c *sigctxt) r31() uint64 { return c.regs().sc_regs[31] }
+func (c *sigctxt) sp() uint64  { return c.regs().sc_regs[29] }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint64 { return c.regs().sc_pc }
+
+func (c *sigctxt) link() uint64 { return c.regs().sc_regs[31] }
+func (c *sigctxt) lo() uint64   { return c.regs().sc_mdlo }
+func (c *sigctxt) hi() uint64   { return c.regs().sc_mdhi }
 
 func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) }
 func (c *sigctxt) sigaddr() uint64 { return c.info.si_addr }
diff --git a/src/runtime/signal_linux_mipsx.go b/src/runtime/signal_linux_mipsx.go
new file mode 100644
index 0000000..c88ac4d
--- /dev/null
+++ b/src/runtime/signal_linux_mipsx.go
@@ -0,0 +1,65 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+// +build mips mipsle
+
+package runtime
+
+import "unsafe"
+
+type sigctxt struct {
+	info *siginfo
+	ctxt unsafe.Pointer
+}
+
+func (c *sigctxt) regs() *sigcontext { return &(*ucontext)(c.ctxt).uc_mcontext }
+func (c *sigctxt) r0() uint32        { return uint32(c.regs().sc_regs[0]) }
+func (c *sigctxt) r1() uint32        { return uint32(c.regs().sc_regs[1]) }
+func (c *sigctxt) r2() uint32        { return uint32(c.regs().sc_regs[2]) }
+func (c *sigctxt) r3() uint32        { return uint32(c.regs().sc_regs[3]) }
+func (c *sigctxt) r4() uint32        { return uint32(c.regs().sc_regs[4]) }
+func (c *sigctxt) r5() uint32        { return uint32(c.regs().sc_regs[5]) }
+func (c *sigctxt) r6() uint32        { return uint32(c.regs().sc_regs[6]) }
+func (c *sigctxt) r7() uint32        { return uint32(c.regs().sc_regs[7]) }
+func (c *sigctxt) r8() uint32        { return uint32(c.regs().sc_regs[8]) }
+func (c *sigctxt) r9() uint32        { return uint32(c.regs().sc_regs[9]) }
+func (c *sigctxt) r10() uint32       { return uint32(c.regs().sc_regs[10]) }
+func (c *sigctxt) r11() uint32       { return uint32(c.regs().sc_regs[11]) }
+func (c *sigctxt) r12() uint32       { return uint32(c.regs().sc_regs[12]) }
+func (c *sigctxt) r13() uint32       { return uint32(c.regs().sc_regs[13]) }
+func (c *sigctxt) r14() uint32       { return uint32(c.regs().sc_regs[14]) }
+func (c *sigctxt) r15() uint32       { return uint32(c.regs().sc_regs[15]) }
+func (c *sigctxt) r16() uint32       { return uint32(c.regs().sc_regs[16]) }
+func (c *sigctxt) r17() uint32       { return uint32(c.regs().sc_regs[17]) }
+func (c *sigctxt) r18() uint32       { return uint32(c.regs().sc_regs[18]) }
+func (c *sigctxt) r19() uint32       { return uint32(c.regs().sc_regs[19]) }
+func (c *sigctxt) r20() uint32       { return uint32(c.regs().sc_regs[20]) }
+func (c *sigctxt) r21() uint32       { return uint32(c.regs().sc_regs[21]) }
+func (c *sigctxt) r22() uint32       { return uint32(c.regs().sc_regs[22]) }
+func (c *sigctxt) r23() uint32       { return uint32(c.regs().sc_regs[23]) }
+func (c *sigctxt) r24() uint32       { return uint32(c.regs().sc_regs[24]) }
+func (c *sigctxt) r25() uint32       { return uint32(c.regs().sc_regs[25]) }
+func (c *sigctxt) r26() uint32       { return uint32(c.regs().sc_regs[26]) }
+func (c *sigctxt) r27() uint32       { return uint32(c.regs().sc_regs[27]) }
+func (c *sigctxt) r28() uint32       { return uint32(c.regs().sc_regs[28]) }
+func (c *sigctxt) r29() uint32       { return uint32(c.regs().sc_regs[29]) }
+func (c *sigctxt) r30() uint32       { return uint32(c.regs().sc_regs[30]) }
+func (c *sigctxt) r31() uint32       { return uint32(c.regs().sc_regs[31]) }
+func (c *sigctxt) sp() uint32        { return uint32(c.regs().sc_regs[29]) }
+func (c *sigctxt) pc() uint32        { return uint32(c.regs().sc_pc) }
+func (c *sigctxt) link() uint32      { return uint32(c.regs().sc_regs[31]) }
+func (c *sigctxt) lo() uint32        { return uint32(c.regs().sc_mdlo) }
+func (c *sigctxt) hi() uint32        { return uint32(c.regs().sc_mdhi) }
+
+func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) }
+func (c *sigctxt) sigaddr() uint32 { return c.info.si_addr }
+
+func (c *sigctxt) set_r30(x uint32)  { c.regs().sc_regs[30] = uint64(x) }
+func (c *sigctxt) set_pc(x uint32)   { c.regs().sc_pc = uint64(x) }
+func (c *sigctxt) set_sp(x uint32)   { c.regs().sc_regs[29] = uint64(x) }
+func (c *sigctxt) set_link(x uint32) { c.regs().sc_regs[31] = uint64(x) }
+
+func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) }
+func (c *sigctxt) set_sigaddr(x uint32) { c.info.si_addr = x }
diff --git a/src/runtime/signal_linux_ppc64x.go b/src/runtime/signal_linux_ppc64x.go
index 95835ca..b6831bc 100644
--- a/src/runtime/signal_linux_ppc64x.go
+++ b/src/runtime/signal_linux_ppc64x.go
@@ -17,46 +17,53 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *ptregs { return (*ucontext)(c.ctxt).uc_mcontext.regs }
-func (c *sigctxt) r0() uint64    { return c.regs().gpr[0] }
-func (c *sigctxt) r1() uint64    { return c.regs().gpr[1] }
-func (c *sigctxt) r2() uint64    { return c.regs().gpr[2] }
-func (c *sigctxt) r3() uint64    { return c.regs().gpr[3] }
-func (c *sigctxt) r4() uint64    { return c.regs().gpr[4] }
-func (c *sigctxt) r5() uint64    { return c.regs().gpr[5] }
-func (c *sigctxt) r6() uint64    { return c.regs().gpr[6] }
-func (c *sigctxt) r7() uint64    { return c.regs().gpr[7] }
-func (c *sigctxt) r8() uint64    { return c.regs().gpr[8] }
-func (c *sigctxt) r9() uint64    { return c.regs().gpr[9] }
-func (c *sigctxt) r10() uint64   { return c.regs().gpr[10] }
-func (c *sigctxt) r11() uint64   { return c.regs().gpr[11] }
-func (c *sigctxt) r12() uint64   { return c.regs().gpr[12] }
-func (c *sigctxt) r13() uint64   { return c.regs().gpr[13] }
-func (c *sigctxt) r14() uint64   { return c.regs().gpr[14] }
-func (c *sigctxt) r15() uint64   { return c.regs().gpr[15] }
-func (c *sigctxt) r16() uint64   { return c.regs().gpr[16] }
-func (c *sigctxt) r17() uint64   { return c.regs().gpr[17] }
-func (c *sigctxt) r18() uint64   { return c.regs().gpr[18] }
-func (c *sigctxt) r19() uint64   { return c.regs().gpr[19] }
-func (c *sigctxt) r20() uint64   { return c.regs().gpr[20] }
-func (c *sigctxt) r21() uint64   { return c.regs().gpr[21] }
-func (c *sigctxt) r22() uint64   { return c.regs().gpr[22] }
-func (c *sigctxt) r23() uint64   { return c.regs().gpr[23] }
-func (c *sigctxt) r24() uint64   { return c.regs().gpr[24] }
-func (c *sigctxt) r25() uint64   { return c.regs().gpr[25] }
-func (c *sigctxt) r26() uint64   { return c.regs().gpr[26] }
-func (c *sigctxt) r27() uint64   { return c.regs().gpr[27] }
-func (c *sigctxt) r28() uint64   { return c.regs().gpr[28] }
-func (c *sigctxt) r29() uint64   { return c.regs().gpr[29] }
-func (c *sigctxt) r30() uint64   { return c.regs().gpr[30] }
-func (c *sigctxt) r31() uint64   { return c.regs().gpr[31] }
-func (c *sigctxt) sp() uint64    { return c.regs().gpr[1] }
-func (c *sigctxt) pc() uint64    { return c.regs().nip }
-func (c *sigctxt) trap() uint64  { return c.regs().trap }
-func (c *sigctxt) ctr() uint64   { return c.regs().ctr }
-func (c *sigctxt) link() uint64  { return c.regs().link }
-func (c *sigctxt) xer() uint64   { return c.regs().xer }
-func (c *sigctxt) ccr() uint64   { return c.regs().ccr }
+
+func (c *sigctxt) r0() uint64  { return c.regs().gpr[0] }
+func (c *sigctxt) r1() uint64  { return c.regs().gpr[1] }
+func (c *sigctxt) r2() uint64  { return c.regs().gpr[2] }
+func (c *sigctxt) r3() uint64  { return c.regs().gpr[3] }
+func (c *sigctxt) r4() uint64  { return c.regs().gpr[4] }
+func (c *sigctxt) r5() uint64  { return c.regs().gpr[5] }
+func (c *sigctxt) r6() uint64  { return c.regs().gpr[6] }
+func (c *sigctxt) r7() uint64  { return c.regs().gpr[7] }
+func (c *sigctxt) r8() uint64  { return c.regs().gpr[8] }
+func (c *sigctxt) r9() uint64  { return c.regs().gpr[9] }
+func (c *sigctxt) r10() uint64 { return c.regs().gpr[10] }
+func (c *sigctxt) r11() uint64 { return c.regs().gpr[11] }
+func (c *sigctxt) r12() uint64 { return c.regs().gpr[12] }
+func (c *sigctxt) r13() uint64 { return c.regs().gpr[13] }
+func (c *sigctxt) r14() uint64 { return c.regs().gpr[14] }
+func (c *sigctxt) r15() uint64 { return c.regs().gpr[15] }
+func (c *sigctxt) r16() uint64 { return c.regs().gpr[16] }
+func (c *sigctxt) r17() uint64 { return c.regs().gpr[17] }
+func (c *sigctxt) r18() uint64 { return c.regs().gpr[18] }
+func (c *sigctxt) r19() uint64 { return c.regs().gpr[19] }
+func (c *sigctxt) r20() uint64 { return c.regs().gpr[20] }
+func (c *sigctxt) r21() uint64 { return c.regs().gpr[21] }
+func (c *sigctxt) r22() uint64 { return c.regs().gpr[22] }
+func (c *sigctxt) r23() uint64 { return c.regs().gpr[23] }
+func (c *sigctxt) r24() uint64 { return c.regs().gpr[24] }
+func (c *sigctxt) r25() uint64 { return c.regs().gpr[25] }
+func (c *sigctxt) r26() uint64 { return c.regs().gpr[26] }
+func (c *sigctxt) r27() uint64 { return c.regs().gpr[27] }
+func (c *sigctxt) r28() uint64 { return c.regs().gpr[28] }
+func (c *sigctxt) r29() uint64 { return c.regs().gpr[29] }
+func (c *sigctxt) r30() uint64 { return c.regs().gpr[30] }
+func (c *sigctxt) r31() uint64 { return c.regs().gpr[31] }
+func (c *sigctxt) sp() uint64  { return c.regs().gpr[1] }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint64 { return c.regs().nip }
+
+func (c *sigctxt) trap() uint64 { return c.regs().trap }
+func (c *sigctxt) ctr() uint64  { return c.regs().ctr }
+func (c *sigctxt) link() uint64 { return c.regs().link }
+func (c *sigctxt) xer() uint64  { return c.regs().xer }
+func (c *sigctxt) ccr() uint64  { return c.regs().ccr }
 
 func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) }
 func (c *sigctxt) sigaddr() uint64 { return c.info.si_addr }
diff --git a/src/runtime/signal_linux_s390x.go b/src/runtime/signal_linux_s390x.go
index 155d3a3..de71ee9 100644
--- a/src/runtime/signal_linux_s390x.go
+++ b/src/runtime/signal_linux_s390x.go
@@ -14,28 +14,35 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *sigcontext {
 	return (*sigcontext)(unsafe.Pointer(&(*ucontext)(c.ctxt).uc_mcontext))
 }
-func (c *sigctxt) r0() uint64      { return c.regs().gregs[0] }
-func (c *sigctxt) r1() uint64      { return c.regs().gregs[1] }
-func (c *sigctxt) r2() uint64      { return c.regs().gregs[2] }
-func (c *sigctxt) r3() uint64      { return c.regs().gregs[3] }
-func (c *sigctxt) r4() uint64      { return c.regs().gregs[4] }
-func (c *sigctxt) r5() uint64      { return c.regs().gregs[5] }
-func (c *sigctxt) r6() uint64      { return c.regs().gregs[6] }
-func (c *sigctxt) r7() uint64      { return c.regs().gregs[7] }
-func (c *sigctxt) r8() uint64      { return c.regs().gregs[8] }
-func (c *sigctxt) r9() uint64      { return c.regs().gregs[9] }
-func (c *sigctxt) r10() uint64     { return c.regs().gregs[10] }
-func (c *sigctxt) r11() uint64     { return c.regs().gregs[11] }
-func (c *sigctxt) r12() uint64     { return c.regs().gregs[12] }
-func (c *sigctxt) r13() uint64     { return c.regs().gregs[13] }
-func (c *sigctxt) r14() uint64     { return c.regs().gregs[14] }
-func (c *sigctxt) r15() uint64     { return c.regs().gregs[15] }
-func (c *sigctxt) link() uint64    { return c.regs().gregs[14] }
-func (c *sigctxt) sp() uint64      { return c.regs().gregs[15] }
-func (c *sigctxt) pc() uint64      { return c.regs().psw_addr }
+
+func (c *sigctxt) r0() uint64   { return c.regs().gregs[0] }
+func (c *sigctxt) r1() uint64   { return c.regs().gregs[1] }
+func (c *sigctxt) r2() uint64   { return c.regs().gregs[2] }
+func (c *sigctxt) r3() uint64   { return c.regs().gregs[3] }
+func (c *sigctxt) r4() uint64   { return c.regs().gregs[4] }
+func (c *sigctxt) r5() uint64   { return c.regs().gregs[5] }
+func (c *sigctxt) r6() uint64   { return c.regs().gregs[6] }
+func (c *sigctxt) r7() uint64   { return c.regs().gregs[7] }
+func (c *sigctxt) r8() uint64   { return c.regs().gregs[8] }
+func (c *sigctxt) r9() uint64   { return c.regs().gregs[9] }
+func (c *sigctxt) r10() uint64  { return c.regs().gregs[10] }
+func (c *sigctxt) r11() uint64  { return c.regs().gregs[11] }
+func (c *sigctxt) r12() uint64  { return c.regs().gregs[12] }
+func (c *sigctxt) r13() uint64  { return c.regs().gregs[13] }
+func (c *sigctxt) r14() uint64  { return c.regs().gregs[14] }
+func (c *sigctxt) r15() uint64  { return c.regs().gregs[15] }
+func (c *sigctxt) link() uint64 { return c.regs().gregs[14] }
+func (c *sigctxt) sp() uint64   { return c.regs().gregs[15] }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint64 { return c.regs().psw_addr }
+
 func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) }
 func (c *sigctxt) sigaddr() uint64 { return c.info.si_addr }
 
@@ -70,139 +77,45 @@ func dumpregs(c *sigctxt) {
 	print("link ", hex(c.link()), "\n")
 }
 
-var crashing int32
-
-// May run during STW, so write barriers are not allowed.
-//
+//go:nosplit
 //go:nowritebarrierrec
-func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
-	_g_ := getg()
-	c := &sigctxt{info, ctxt}
-
-	if sig == _SIGPROF {
-		sigprof(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp, _g_.m)
-		return
-	}
-	flags := int32(_SigThrow)
-	if sig < uint32(len(sigtable)) {
-		flags = sigtable[sig].flags
-	}
-	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
-		// Make it look like a call to the signal func.
-		// Have to pass arguments out of band since
-		// augmenting the stack frame would break
-		// the unwinding code.
-		gp.sig = sig
-		gp.sigcode0 = uintptr(c.sigcode())
-		gp.sigcode1 = uintptr(c.sigaddr())
-		gp.sigpc = uintptr(c.pc())
-
-		// We arrange link, and pc to pretend the panicking
-		// function calls sigpanic directly.
-		// Always save LINK to stack so that panics in leaf
-		// functions are correctly handled. This smashes
-		// the stack frame but we're not going back there
-		// anyway.
-		sp := c.sp() - sys.MinFrameSize
-		c.set_sp(sp)
-		*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link()
-
-		pc := uintptr(gp.sigpc)
-
-		// If we don't recognize the PC as code
-		// but we do recognize the link register as code,
-		// then assume this was a call to non-code and treat like
-		// pc == 0, to make unwinding show the context.
-		if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
-			pc = 0
-		}
-
-		// Don't bother saving PC if it's zero, which is
-		// probably a call to a nil func: the old link register
-		// is more useful in the stack trace.
-		if pc != 0 {
-			c.set_link(uint64(pc))
-		}
-
-		// In case we are panicking from external C code
-		c.set_r0(0)
-		c.set_r13(uint64(uintptr(unsafe.Pointer(gp))))
-		c.set_pc(uint64(funcPC(sigpanic)))
-		return
-	}
-
-	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
-		if sigsend(sig) {
-			return
-		}
-	}
-
-	if c.sigcode() == _SI_USER && signal_ignored(sig) {
-		return
-	}
-
-	if flags&_SigKill != 0 {
-		dieFromSignal(int32(sig))
-	}
-
-	if flags&_SigThrow == 0 {
-		return
-	}
-
-	_g_.m.throwing = 1
-	_g_.m.caughtsig.set(gp)
-
-	if crashing == 0 {
-		startpanic()
-	}
-
-	if sig < uint32(len(sigtable)) {
-		print(sigtable[sig].name, "\n")
-	} else {
-		print("Signal ", sig, "\n")
-	}
-
-	print("PC=", hex(c.pc()), " m=", _g_.m.id, "\n")
-	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
-		print("signal arrived during cgo execution\n")
-		gp = _g_.m.lockedg
-	}
-	print("\n")
-
-	level, _, docrash := gotraceback()
-	if level > 0 {
-		goroutineheader(gp)
-		tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp)
-		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
-			// tracebackothers on original m skipped this one; trace it now.
-			goroutineheader(_g_.m.curg)
-			traceback(^uintptr(0), ^uintptr(0), 0, gp)
-		} else if crashing == 0 {
-			tracebackothers(gp)
-			print("\n")
-		}
-		dumpregs(c)
+func (c *sigctxt) sigpc() uintptr { return uintptr(c.pc()) }
+
+func (c *sigctxt) sigsp() uintptr { return uintptr(c.sp()) }
+func (c *sigctxt) siglr() uintptr { return uintptr(c.link()) }
+func (c *sigctxt) fault() uintptr { return uintptr(c.sigaddr()) }
+
+// preparePanic sets up the stack to look like a call to sigpanic.
+func (c *sigctxt) preparePanic(sig uint32, gp *g) {
+	// We arrange link, and pc to pretend the panicking
+	// function calls sigpanic directly.
+	// Always save LINK to stack so that panics in leaf
+	// functions are correctly handled. This smashes
+	// the stack frame but we're not going back there
+	// anyway.
+	sp := c.sp() - sys.MinFrameSize
+	c.set_sp(sp)
+	*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link()
+
+	pc := uintptr(gp.sigpc)
+
+	// If we don't recognize the PC as code
+	// but we do recognize the link register as code,
+	// then assume this was a call to non-code and treat like
+	// pc == 0, to make unwinding show the context.
+	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
+		pc = 0
 	}
 
-	if docrash {
-		crashing++
-		if crashing < sched.mcount {
-			// There are other m's that need to dump their stacks.
-			// Relay SIGQUIT to the next m by sending it to the current process.
-			// All m's that have already received SIGQUIT have signal masks blocking
-			// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
-			// When the last m receives the SIGQUIT, it will fall through to the call to
-			// crash below. Just in case the relaying gets botched, each m involved in
-			// the relay sleeps for 5 seconds and then does the crash/exit itself.
-			// In expected operation, the last m has received the SIGQUIT and run
-			// crash/exit and the process is gone, all long before any of the
-			// 5-second sleeps have finished.
-			print("\n-----\n\n")
-			raiseproc(_SIGQUIT)
-			usleep(5 * 1000 * 1000)
-		}
-		crash()
+	// Don't bother saving PC if it's zero, which is
+	// probably a call to a nil func: the old link register
+	// is more useful in the stack trace.
+	if pc != 0 {
+		c.set_link(uint64(pc))
 	}
 
-	exit(2)
+	// In case we are panicking from external C code
+	c.set_r0(0)
+	c.set_r13(uint64(uintptr(unsafe.Pointer(gp))))
+	c.set_pc(uint64(funcPC(sigpanic)))
 }
diff --git a/src/runtime/signal_mips64x.go b/src/runtime/signal_mips64x.go
index 4dbeb42..973ec2d 100644
--- a/src/runtime/signal_mips64x.go
+++ b/src/runtime/signal_mips64x.go
@@ -51,138 +51,44 @@ func dumpregs(c *sigctxt) {
 	print("hi   ", hex(c.hi()), "\n")
 }
 
-var crashing int32
-
-// May run during STW, so write barriers are not allowed.
-//
+//go:nosplit
 //go:nowritebarrierrec
-func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
-	_g_ := getg()
-	c := &sigctxt{info, ctxt}
-
-	if sig == _SIGPROF {
-		sigprof(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp, _g_.m)
-		return
-	}
-	flags := int32(_SigThrow)
-	if sig < uint32(len(sigtable)) {
-		flags = sigtable[sig].flags
-	}
-	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
-		// Make it look like a call to the signal func.
-		// Have to pass arguments out of band since
-		// augmenting the stack frame would break
-		// the unwinding code.
-		gp.sig = sig
-		gp.sigcode0 = uintptr(c.sigcode())
-		gp.sigcode1 = uintptr(c.sigaddr())
-		gp.sigpc = uintptr(c.pc())
-
-		// We arrange link, and pc to pretend the panicking
-		// function calls sigpanic directly.
-		// Always save LINK to stack so that panics in leaf
-		// functions are correctly handled. This smashes
-		// the stack frame but we're not going back there
-		// anyway.
-		sp := c.sp() - sys.PtrSize
-		c.set_sp(sp)
-		*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link()
-
-		pc := gp.sigpc
-
-		// If we don't recognize the PC as code
-		// but we do recognize the link register as code,
-		// then assume this was a call to non-code and treat like
-		// pc == 0, to make unwinding show the context.
-		if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
-			pc = 0
-		}
-
-		// Don't bother saving PC if it's zero, which is
-		// probably a call to a nil func: the old link register
-		// is more useful in the stack trace.
-		if pc != 0 {
-			c.set_link(uint64(pc))
-		}
-
-		// In case we are panicking from external C code
-		c.set_r30(uint64(uintptr(unsafe.Pointer(gp))))
-		c.set_pc(uint64(funcPC(sigpanic)))
-		return
-	}
-
-	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
-		if sigsend(sig) {
-			return
-		}
-	}
-
-	if c.sigcode() == _SI_USER && signal_ignored(sig) {
-		return
-	}
-
-	if flags&_SigKill != 0 {
-		dieFromSignal(int32(sig))
-	}
-
-	if flags&_SigThrow == 0 {
-		return
-	}
-
-	_g_.m.throwing = 1
-	_g_.m.caughtsig.set(gp)
-
-	if crashing == 0 {
-		startpanic()
-	}
-
-	if sig < uint32(len(sigtable)) {
-		print(sigtable[sig].name, "\n")
-	} else {
-		print("Signal ", sig, "\n")
-	}
-
-	print("PC=", hex(c.pc()), " m=", _g_.m.id, "\n")
-	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
-		print("signal arrived during cgo execution\n")
-		gp = _g_.m.lockedg
-	}
-	print("\n")
-
-	level, _, docrash := gotraceback()
-	if level > 0 {
-		goroutineheader(gp)
-		tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp)
-		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
-			// tracebackothers on original m skipped this one; trace it now.
-			goroutineheader(_g_.m.curg)
-			traceback(^uintptr(0), ^uintptr(0), 0, gp)
-		} else if crashing == 0 {
-			tracebackothers(gp)
-			print("\n")
-		}
-		dumpregs(c)
+func (c *sigctxt) sigpc() uintptr { return uintptr(c.pc()) }
+
+func (c *sigctxt) sigsp() uintptr { return uintptr(c.sp()) }
+func (c *sigctxt) siglr() uintptr { return uintptr(c.link()) }
+func (c *sigctxt) fault() uintptr { return uintptr(c.sigaddr()) }
+
+// preparePanic sets up the stack to look like a call to sigpanic.
+func (c *sigctxt) preparePanic(sig uint32, gp *g) {
+	// We arrange link, and pc to pretend the panicking
+	// function calls sigpanic directly.
+	// Always save LINK to stack so that panics in leaf
+	// functions are correctly handled. This smashes
+	// the stack frame but we're not going back there
+	// anyway.
+	sp := c.sp() - sys.PtrSize
+	c.set_sp(sp)
+	*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link()
+
+	pc := gp.sigpc
+
+	// If we don't recognize the PC as code
+	// but we do recognize the link register as code,
+	// then assume this was a call to non-code and treat like
+	// pc == 0, to make unwinding show the context.
+	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
+		pc = 0
 	}
 
-	if docrash {
-		crashing++
-		if crashing < sched.mcount {
-			// There are other m's that need to dump their stacks.
-			// Relay SIGQUIT to the next m by sending it to the current process.
-			// All m's that have already received SIGQUIT have signal masks blocking
-			// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
-			// When the last m receives the SIGQUIT, it will fall through to the call to
-			// crash below. Just in case the relaying gets botched, each m involved in
-			// the relay sleeps for 5 seconds and then does the crash/exit itself.
-			// In expected operation, the last m has received the SIGQUIT and run
-			// crash/exit and the process is gone, all long before any of the
-			// 5-second sleeps have finished.
-			print("\n-----\n\n")
-			raiseproc(_SIGQUIT)
-			usleep(5 * 1000 * 1000)
-		}
-		crash()
+	// Don't bother saving PC if it's zero, which is
+	// probably a call to a nil func: the old link register
+	// is more useful in the stack trace.
+	if pc != 0 {
+		c.set_link(uint64(pc))
 	}
 
-	exit(2)
+	// In case we are panicking from external C code
+	c.set_r30(uint64(uintptr(unsafe.Pointer(gp))))
+	c.set_pc(uint64(funcPC(sigpanic)))
 }
diff --git a/src/runtime/signal_mipsx.go b/src/runtime/signal_mipsx.go
new file mode 100644
index 0000000..62df79c
--- /dev/null
+++ b/src/runtime/signal_mipsx.go
@@ -0,0 +1,91 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+// +build mips mipsle
+
+package runtime
+
+import (
+	"runtime/internal/sys"
+	"unsafe"
+)
+
+func dumpregs(c *sigctxt) {
+	print("r0   ", hex(c.r0()), "\t")
+	print("r1   ", hex(c.r1()), "\n")
+	print("r2   ", hex(c.r2()), "\t")
+	print("r3   ", hex(c.r3()), "\n")
+	print("r4   ", hex(c.r4()), "\t")
+	print("r5   ", hex(c.r5()), "\n")
+	print("r6   ", hex(c.r6()), "\t")
+	print("r7   ", hex(c.r7()), "\n")
+	print("r8   ", hex(c.r8()), "\t")
+	print("r9   ", hex(c.r9()), "\n")
+	print("r10  ", hex(c.r10()), "\t")
+	print("r11  ", hex(c.r11()), "\n")
+	print("r12  ", hex(c.r12()), "\t")
+	print("r13  ", hex(c.r13()), "\n")
+	print("r14  ", hex(c.r14()), "\t")
+	print("r15  ", hex(c.r15()), "\n")
+	print("r16  ", hex(c.r16()), "\t")
+	print("r17  ", hex(c.r17()), "\n")
+	print("r18  ", hex(c.r18()), "\t")
+	print("r19  ", hex(c.r19()), "\n")
+	print("r20  ", hex(c.r20()), "\t")
+	print("r21  ", hex(c.r21()), "\n")
+	print("r22  ", hex(c.r22()), "\t")
+	print("r23  ", hex(c.r23()), "\n")
+	print("r24  ", hex(c.r24()), "\t")
+	print("r25  ", hex(c.r25()), "\n")
+	print("r26  ", hex(c.r26()), "\t")
+	print("r27  ", hex(c.r27()), "\n")
+	print("r28  ", hex(c.r28()), "\t")
+	print("r29  ", hex(c.r29()), "\n")
+	print("r30  ", hex(c.r30()), "\t")
+	print("r31  ", hex(c.r31()), "\n")
+	print("pc   ", hex(c.pc()), "\t")
+	print("link ", hex(c.link()), "\n")
+	print("lo   ", hex(c.lo()), "\t")
+	print("hi   ", hex(c.hi()), "\n")
+}
+
+func (c *sigctxt) sigpc() uintptr { return uintptr(c.pc()) }
+func (c *sigctxt) sigsp() uintptr { return uintptr(c.sp()) }
+func (c *sigctxt) siglr() uintptr { return uintptr(c.link()) }
+func (c *sigctxt) fault() uintptr { return uintptr(c.sigaddr()) }
+
+// preparePanic sets up the stack to look like a call to sigpanic.
+func (c *sigctxt) preparePanic(sig uint32, gp *g) {
+	// We arrange link, and pc to pretend the panicking
+	// function calls sigpanic directly.
+	// Always save LINK to stack so that panics in leaf
+	// functions are correctly handled. This smashes
+	// the stack frame but we're not going back there
+	// anyway.
+	sp := c.sp() - sys.MinFrameSize
+	c.set_sp(sp)
+	*(*uint32)(unsafe.Pointer(uintptr(sp))) = c.link()
+
+	pc := gp.sigpc
+
+	// If we don't recognize the PC as code
+	// but we do recognize the link register as code,
+	// then assume this was a call to non-code and treat like
+	// pc == 0, to make unwinding show the context.
+	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
+		pc = 0
+	}
+
+	// Don't bother saving PC if it's zero, which is
+	// probably a call to a nil func: the old link register
+	// is more useful in the stack trace.
+	if pc != 0 {
+		c.set_link(uint32(pc))
+	}
+
+	// In case we are panicking from external C code
+	c.set_r30(uint32(uintptr(unsafe.Pointer(gp))))
+	c.set_pc(uint32(funcPC(sigpanic)))
+}
diff --git a/src/runtime/signal_nacl_386.go b/src/runtime/signal_nacl_386.go
index 1f48590..1a30a89 100644
--- a/src/runtime/signal_nacl_386.go
+++ b/src/runtime/signal_nacl_386.go
@@ -11,22 +11,29 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *excregs386 { return &(*exccontext)(c.ctxt).regs }
-func (c *sigctxt) eax() uint32       { return c.regs().eax }
-func (c *sigctxt) ebx() uint32       { return c.regs().ebx }
-func (c *sigctxt) ecx() uint32       { return c.regs().ecx }
-func (c *sigctxt) edx() uint32       { return c.regs().edx }
-func (c *sigctxt) edi() uint32       { return c.regs().edi }
-func (c *sigctxt) esi() uint32       { return c.regs().esi }
-func (c *sigctxt) ebp() uint32       { return c.regs().ebp }
-func (c *sigctxt) esp() uint32       { return c.regs().esp }
-func (c *sigctxt) eip() uint32       { return c.regs().eip }
-func (c *sigctxt) eflags() uint32    { return c.regs().eflags }
-func (c *sigctxt) cs() uint32        { return ^uint32(0) }
-func (c *sigctxt) fs() uint32        { return ^uint32(0) }
-func (c *sigctxt) gs() uint32        { return ^uint32(0) }
-func (c *sigctxt) sigcode() uint32   { return ^uint32(0) }
-func (c *sigctxt) sigaddr() uint32   { return 0 }
+
+func (c *sigctxt) eax() uint32 { return c.regs().eax }
+func (c *sigctxt) ebx() uint32 { return c.regs().ebx }
+func (c *sigctxt) ecx() uint32 { return c.regs().ecx }
+func (c *sigctxt) edx() uint32 { return c.regs().edx }
+func (c *sigctxt) edi() uint32 { return c.regs().edi }
+func (c *sigctxt) esi() uint32 { return c.regs().esi }
+func (c *sigctxt) ebp() uint32 { return c.regs().ebp }
+func (c *sigctxt) esp() uint32 { return c.regs().esp }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) eip() uint32 { return c.regs().eip }
+
+func (c *sigctxt) eflags() uint32  { return c.regs().eflags }
+func (c *sigctxt) cs() uint32      { return ^uint32(0) }
+func (c *sigctxt) fs() uint32      { return ^uint32(0) }
+func (c *sigctxt) gs() uint32      { return ^uint32(0) }
+func (c *sigctxt) sigcode() uint32 { return ^uint32(0) }
+func (c *sigctxt) sigaddr() uint32 { return 0 }
 
 func (c *sigctxt) set_eip(x uint32)     { c.regs().eip = x }
 func (c *sigctxt) set_esp(x uint32)     { c.regs().esp = x }
diff --git a/src/runtime/signal_nacl_amd64p32.go b/src/runtime/signal_nacl_amd64p32.go
index cb72dd0..6d656fe 100644
--- a/src/runtime/signal_nacl_amd64p32.go
+++ b/src/runtime/signal_nacl_amd64p32.go
@@ -11,26 +11,33 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *excregsamd64 {
 	return &(*exccontext)(c.ctxt).regs
 }
-func (c *sigctxt) rax() uint64     { return c.regs().rax }
-func (c *sigctxt) rbx() uint64     { return c.regs().rbx }
-func (c *sigctxt) rcx() uint64     { return c.regs().rcx }
-func (c *sigctxt) rdx() uint64     { return c.regs().rdx }
-func (c *sigctxt) rdi() uint64     { return c.regs().rdi }
-func (c *sigctxt) rsi() uint64     { return c.regs().rsi }
-func (c *sigctxt) rbp() uint64     { return c.regs().rbp }
-func (c *sigctxt) rsp() uint64     { return c.regs().rsp }
-func (c *sigctxt) r8() uint64      { return c.regs().r8 }
-func (c *sigctxt) r9() uint64      { return c.regs().r9 }
-func (c *sigctxt) r10() uint64     { return c.regs().r10 }
-func (c *sigctxt) r11() uint64     { return c.regs().r11 }
-func (c *sigctxt) r12() uint64     { return c.regs().r12 }
-func (c *sigctxt) r13() uint64     { return c.regs().r13 }
-func (c *sigctxt) r14() uint64     { return c.regs().r14 }
-func (c *sigctxt) r15() uint64     { return c.regs().r15 }
-func (c *sigctxt) rip() uint64     { return c.regs().rip }
+
+func (c *sigctxt) rax() uint64 { return c.regs().rax }
+func (c *sigctxt) rbx() uint64 { return c.regs().rbx }
+func (c *sigctxt) rcx() uint64 { return c.regs().rcx }
+func (c *sigctxt) rdx() uint64 { return c.regs().rdx }
+func (c *sigctxt) rdi() uint64 { return c.regs().rdi }
+func (c *sigctxt) rsi() uint64 { return c.regs().rsi }
+func (c *sigctxt) rbp() uint64 { return c.regs().rbp }
+func (c *sigctxt) rsp() uint64 { return c.regs().rsp }
+func (c *sigctxt) r8() uint64  { return c.regs().r8 }
+func (c *sigctxt) r9() uint64  { return c.regs().r9 }
+func (c *sigctxt) r10() uint64 { return c.regs().r10 }
+func (c *sigctxt) r11() uint64 { return c.regs().r11 }
+func (c *sigctxt) r12() uint64 { return c.regs().r12 }
+func (c *sigctxt) r13() uint64 { return c.regs().r13 }
+func (c *sigctxt) r14() uint64 { return c.regs().r14 }
+func (c *sigctxt) r15() uint64 { return c.regs().r15 }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) rip() uint64 { return c.regs().rip }
+
 func (c *sigctxt) rflags() uint64  { return uint64(c.regs().rflags) }
 func (c *sigctxt) cs() uint64      { return ^uint64(0) }
 func (c *sigctxt) fs() uint64      { return ^uint64(0) }
diff --git a/src/runtime/signal_nacl_arm.go b/src/runtime/signal_nacl_arm.go
index b99827c..959dbfb 100644
--- a/src/runtime/signal_nacl_arm.go
+++ b/src/runtime/signal_nacl_arm.go
@@ -11,24 +11,30 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *excregsarm { return &(*exccontext)(c.ctxt).regs }
 
-func (c *sigctxt) r0() uint32      { return c.regs().r0 }
-func (c *sigctxt) r1() uint32      { return c.regs().r1 }
-func (c *sigctxt) r2() uint32      { return c.regs().r2 }
-func (c *sigctxt) r3() uint32      { return c.regs().r3 }
-func (c *sigctxt) r4() uint32      { return c.regs().r4 }
-func (c *sigctxt) r5() uint32      { return c.regs().r5 }
-func (c *sigctxt) r6() uint32      { return c.regs().r6 }
-func (c *sigctxt) r7() uint32      { return c.regs().r7 }
-func (c *sigctxt) r8() uint32      { return c.regs().r8 }
-func (c *sigctxt) r9() uint32      { return c.regs().r9 }
-func (c *sigctxt) r10() uint32     { return c.regs().r10 }
-func (c *sigctxt) fp() uint32      { return c.regs().r11 }
-func (c *sigctxt) ip() uint32      { return c.regs().r12 }
-func (c *sigctxt) sp() uint32      { return c.regs().sp }
-func (c *sigctxt) lr() uint32      { return c.regs().lr }
-func (c *sigctxt) pc() uint32      { return c.regs().pc }
+func (c *sigctxt) r0() uint32  { return c.regs().r0 }
+func (c *sigctxt) r1() uint32  { return c.regs().r1 }
+func (c *sigctxt) r2() uint32  { return c.regs().r2 }
+func (c *sigctxt) r3() uint32  { return c.regs().r3 }
+func (c *sigctxt) r4() uint32  { return c.regs().r4 }
+func (c *sigctxt) r5() uint32  { return c.regs().r5 }
+func (c *sigctxt) r6() uint32  { return c.regs().r6 }
+func (c *sigctxt) r7() uint32  { return c.regs().r7 }
+func (c *sigctxt) r8() uint32  { return c.regs().r8 }
+func (c *sigctxt) r9() uint32  { return c.regs().r9 }
+func (c *sigctxt) r10() uint32 { return c.regs().r10 }
+func (c *sigctxt) fp() uint32  { return c.regs().r11 }
+func (c *sigctxt) ip() uint32  { return c.regs().r12 }
+func (c *sigctxt) sp() uint32  { return c.regs().sp }
+func (c *sigctxt) lr() uint32  { return c.regs().lr }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint32 { return c.regs().pc }
+
 func (c *sigctxt) cpsr() uint32    { return c.regs().cpsr }
 func (c *sigctxt) fault() uint32   { return ^uint32(0) }
 func (c *sigctxt) trap() uint32    { return ^uint32(0) }
diff --git a/src/runtime/signal_netbsd_386.go b/src/runtime/signal_netbsd_386.go
index af49d5d..845a575 100644
--- a/src/runtime/signal_netbsd_386.go
+++ b/src/runtime/signal_netbsd_386.go
@@ -11,21 +11,28 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *mcontextt { return &(*ucontextt)(c.ctxt).uc_mcontext }
-func (c *sigctxt) eax() uint32      { return c.regs().__gregs[_REG_EAX] }
-func (c *sigctxt) ebx() uint32      { return c.regs().__gregs[_REG_EBX] }
-func (c *sigctxt) ecx() uint32      { return c.regs().__gregs[_REG_ECX] }
-func (c *sigctxt) edx() uint32      { return c.regs().__gregs[_REG_EDX] }
-func (c *sigctxt) edi() uint32      { return c.regs().__gregs[_REG_EDI] }
-func (c *sigctxt) esi() uint32      { return c.regs().__gregs[_REG_ESI] }
-func (c *sigctxt) ebp() uint32      { return c.regs().__gregs[_REG_EBP] }
-func (c *sigctxt) esp() uint32      { return c.regs().__gregs[_REG_UESP] }
-func (c *sigctxt) eip() uint32      { return c.regs().__gregs[_REG_EIP] }
-func (c *sigctxt) eflags() uint32   { return c.regs().__gregs[_REG_EFL] }
-func (c *sigctxt) cs() uint32       { return c.regs().__gregs[_REG_CS] }
-func (c *sigctxt) fs() uint32       { return c.regs().__gregs[_REG_FS] }
-func (c *sigctxt) gs() uint32       { return c.regs().__gregs[_REG_GS] }
-func (c *sigctxt) sigcode() uint32  { return uint32(c.info._code) }
+
+func (c *sigctxt) eax() uint32 { return c.regs().__gregs[_REG_EAX] }
+func (c *sigctxt) ebx() uint32 { return c.regs().__gregs[_REG_EBX] }
+func (c *sigctxt) ecx() uint32 { return c.regs().__gregs[_REG_ECX] }
+func (c *sigctxt) edx() uint32 { return c.regs().__gregs[_REG_EDX] }
+func (c *sigctxt) edi() uint32 { return c.regs().__gregs[_REG_EDI] }
+func (c *sigctxt) esi() uint32 { return c.regs().__gregs[_REG_ESI] }
+func (c *sigctxt) ebp() uint32 { return c.regs().__gregs[_REG_EBP] }
+func (c *sigctxt) esp() uint32 { return c.regs().__gregs[_REG_UESP] }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) eip() uint32 { return c.regs().__gregs[_REG_EIP] }
+
+func (c *sigctxt) eflags() uint32  { return c.regs().__gregs[_REG_EFL] }
+func (c *sigctxt) cs() uint32      { return c.regs().__gregs[_REG_CS] }
+func (c *sigctxt) fs() uint32      { return c.regs().__gregs[_REG_FS] }
+func (c *sigctxt) gs() uint32      { return c.regs().__gregs[_REG_GS] }
+func (c *sigctxt) sigcode() uint32 { return uint32(c.info._code) }
 func (c *sigctxt) sigaddr() uint32 {
 	return *(*uint32)(unsafe.Pointer(&c.info._reason[0]))
 }
diff --git a/src/runtime/signal_netbsd_amd64.go b/src/runtime/signal_netbsd_amd64.go
index db230f8..67fe437 100644
--- a/src/runtime/signal_netbsd_amd64.go
+++ b/src/runtime/signal_netbsd_amd64.go
@@ -11,26 +11,33 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *mcontextt {
 	return (*mcontextt)(unsafe.Pointer(&(*ucontextt)(c.ctxt).uc_mcontext))
 }
-func (c *sigctxt) rax() uint64     { return c.regs().__gregs[_REG_RAX] }
-func (c *sigctxt) rbx() uint64     { return c.regs().__gregs[_REG_RBX] }
-func (c *sigctxt) rcx() uint64     { return c.regs().__gregs[_REG_RCX] }
-func (c *sigctxt) rdx() uint64     { return c.regs().__gregs[_REG_RDX] }
-func (c *sigctxt) rdi() uint64     { return c.regs().__gregs[_REG_RDI] }
-func (c *sigctxt) rsi() uint64     { return c.regs().__gregs[_REG_RSI] }
-func (c *sigctxt) rbp() uint64     { return c.regs().__gregs[_REG_RBP] }
-func (c *sigctxt) rsp() uint64     { return c.regs().__gregs[_REG_RSP] }
-func (c *sigctxt) r8() uint64      { return c.regs().__gregs[_REG_R8] }
-func (c *sigctxt) r9() uint64      { return c.regs().__gregs[_REG_R8] }
-func (c *sigctxt) r10() uint64     { return c.regs().__gregs[_REG_R10] }
-func (c *sigctxt) r11() uint64     { return c.regs().__gregs[_REG_R11] }
-func (c *sigctxt) r12() uint64     { return c.regs().__gregs[_REG_R12] }
-func (c *sigctxt) r13() uint64     { return c.regs().__gregs[_REG_R13] }
-func (c *sigctxt) r14() uint64     { return c.regs().__gregs[_REG_R14] }
-func (c *sigctxt) r15() uint64     { return c.regs().__gregs[_REG_R15] }
-func (c *sigctxt) rip() uint64     { return c.regs().__gregs[_REG_RIP] }
+
+func (c *sigctxt) rax() uint64 { return c.regs().__gregs[_REG_RAX] }
+func (c *sigctxt) rbx() uint64 { return c.regs().__gregs[_REG_RBX] }
+func (c *sigctxt) rcx() uint64 { return c.regs().__gregs[_REG_RCX] }
+func (c *sigctxt) rdx() uint64 { return c.regs().__gregs[_REG_RDX] }
+func (c *sigctxt) rdi() uint64 { return c.regs().__gregs[_REG_RDI] }
+func (c *sigctxt) rsi() uint64 { return c.regs().__gregs[_REG_RSI] }
+func (c *sigctxt) rbp() uint64 { return c.regs().__gregs[_REG_RBP] }
+func (c *sigctxt) rsp() uint64 { return c.regs().__gregs[_REG_RSP] }
+func (c *sigctxt) r8() uint64  { return c.regs().__gregs[_REG_R8] }
+func (c *sigctxt) r9() uint64  { return c.regs().__gregs[_REG_R8] }
+func (c *sigctxt) r10() uint64 { return c.regs().__gregs[_REG_R10] }
+func (c *sigctxt) r11() uint64 { return c.regs().__gregs[_REG_R11] }
+func (c *sigctxt) r12() uint64 { return c.regs().__gregs[_REG_R12] }
+func (c *sigctxt) r13() uint64 { return c.regs().__gregs[_REG_R13] }
+func (c *sigctxt) r14() uint64 { return c.regs().__gregs[_REG_R14] }
+func (c *sigctxt) r15() uint64 { return c.regs().__gregs[_REG_R15] }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) rip() uint64 { return c.regs().__gregs[_REG_RIP] }
+
 func (c *sigctxt) rflags() uint64  { return c.regs().__gregs[_REG_RFLAGS] }
 func (c *sigctxt) cs() uint64      { return c.regs().__gregs[_REG_CS] }
 func (c *sigctxt) fs() uint64      { return c.regs().__gregs[_REG_FS] }
diff --git a/src/runtime/signal_netbsd_arm.go b/src/runtime/signal_netbsd_arm.go
index 4e58d3b..64cfffa 100644
--- a/src/runtime/signal_netbsd_arm.go
+++ b/src/runtime/signal_netbsd_arm.go
@@ -11,28 +11,35 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *mcontextt { return &(*ucontextt)(c.ctxt).uc_mcontext }
-func (c *sigctxt) r0() uint32       { return c.regs().__gregs[_REG_R0] }
-func (c *sigctxt) r1() uint32       { return c.regs().__gregs[_REG_R1] }
-func (c *sigctxt) r2() uint32       { return c.regs().__gregs[_REG_R2] }
-func (c *sigctxt) r3() uint32       { return c.regs().__gregs[_REG_R3] }
-func (c *sigctxt) r4() uint32       { return c.regs().__gregs[_REG_R4] }
-func (c *sigctxt) r5() uint32       { return c.regs().__gregs[_REG_R5] }
-func (c *sigctxt) r6() uint32       { return c.regs().__gregs[_REG_R6] }
-func (c *sigctxt) r7() uint32       { return c.regs().__gregs[_REG_R7] }
-func (c *sigctxt) r8() uint32       { return c.regs().__gregs[_REG_R8] }
-func (c *sigctxt) r9() uint32       { return c.regs().__gregs[_REG_R9] }
-func (c *sigctxt) r10() uint32      { return c.regs().__gregs[_REG_R10] }
-func (c *sigctxt) fp() uint32       { return c.regs().__gregs[_REG_R11] }
-func (c *sigctxt) ip() uint32       { return c.regs().__gregs[_REG_R12] }
-func (c *sigctxt) sp() uint32       { return c.regs().__gregs[_REG_R13] }
-func (c *sigctxt) lr() uint32       { return c.regs().__gregs[_REG_R14] }
-func (c *sigctxt) pc() uint32       { return c.regs().__gregs[_REG_R15] }
-func (c *sigctxt) cpsr() uint32     { return c.regs().__gregs[_REG_CPSR] }
-func (c *sigctxt) fault() uint32    { return uint32(c.info._reason) }
-func (c *sigctxt) trap() uint32     { return 0 }
-func (c *sigctxt) error() uint32    { return 0 }
-func (c *sigctxt) oldmask() uint32  { return 0 }
+
+func (c *sigctxt) r0() uint32  { return c.regs().__gregs[_REG_R0] }
+func (c *sigctxt) r1() uint32  { return c.regs().__gregs[_REG_R1] }
+func (c *sigctxt) r2() uint32  { return c.regs().__gregs[_REG_R2] }
+func (c *sigctxt) r3() uint32  { return c.regs().__gregs[_REG_R3] }
+func (c *sigctxt) r4() uint32  { return c.regs().__gregs[_REG_R4] }
+func (c *sigctxt) r5() uint32  { return c.regs().__gregs[_REG_R5] }
+func (c *sigctxt) r6() uint32  { return c.regs().__gregs[_REG_R6] }
+func (c *sigctxt) r7() uint32  { return c.regs().__gregs[_REG_R7] }
+func (c *sigctxt) r8() uint32  { return c.regs().__gregs[_REG_R8] }
+func (c *sigctxt) r9() uint32  { return c.regs().__gregs[_REG_R9] }
+func (c *sigctxt) r10() uint32 { return c.regs().__gregs[_REG_R10] }
+func (c *sigctxt) fp() uint32  { return c.regs().__gregs[_REG_R11] }
+func (c *sigctxt) ip() uint32  { return c.regs().__gregs[_REG_R12] }
+func (c *sigctxt) sp() uint32  { return c.regs().__gregs[_REG_R13] }
+func (c *sigctxt) lr() uint32  { return c.regs().__gregs[_REG_R14] }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint32 { return c.regs().__gregs[_REG_R15] }
+
+func (c *sigctxt) cpsr() uint32    { return c.regs().__gregs[_REG_CPSR] }
+func (c *sigctxt) fault() uint32   { return uint32(c.info._reason) }
+func (c *sigctxt) trap() uint32    { return 0 }
+func (c *sigctxt) error() uint32   { return 0 }
+func (c *sigctxt) oldmask() uint32 { return 0 }
 
 func (c *sigctxt) sigcode() uint32 { return uint32(c.info._code) }
 func (c *sigctxt) sigaddr() uint32 { return uint32(c.info._reason) }
diff --git a/src/runtime/signal_openbsd.go b/src/runtime/signal_openbsd.go
index efe30da..30a3b8e 100644
--- a/src/runtime/signal_openbsd.go
+++ b/src/runtime/signal_openbsd.go
@@ -4,8 +4,6 @@
 
 package runtime
 
-import "unsafe"
-
 type sigTabT struct {
 	flags int32
 	name  string
@@ -46,42 +44,3 @@ var sigtable = [...]sigTabT{
 	/* 31 */ {_SigNotify, "SIGUSR2: user-defined signal 2"},
 	/* 32 */ {_SigNotify, "SIGTHR: reserved"},
 }
-
-//go:nosplit
-//go:nowritebarrierrec
-func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
-	if sigfwdgo(sig, info, ctx) {
-		return
-	}
-	g := getg()
-	if g == nil {
-		badsignal(uintptr(sig), &sigctxt{info, ctx})
-		return
-	}
-
-	// If some non-Go code called sigaltstack, adjust.
-	sp := uintptr(unsafe.Pointer(&sig))
-	if sp < g.m.gsignal.stack.lo || sp >= g.m.gsignal.stack.hi {
-		var st stackt
-		sigaltstack(nil, &st)
-		if st.ss_flags&_SS_DISABLE != 0 {
-			setg(nil)
-			cgocallback(unsafe.Pointer(funcPC(noSignalStack)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig), 0)
-		}
-		stsp := uintptr(unsafe.Pointer(st.ss_sp))
-		if sp < stsp || sp >= stsp+st.ss_size {
-			setg(nil)
-			cgocallback(unsafe.Pointer(funcPC(sigNotOnStack)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig), 0)
-		}
-		g.m.gsignal.stack.lo = stsp
-		g.m.gsignal.stack.hi = stsp + st.ss_size
-		g.m.gsignal.stackguard0 = stsp + _StackGuard
-		g.m.gsignal.stackguard1 = stsp + _StackGuard
-		g.m.gsignal.stackAlloc = st.ss_size
-		g.m.gsignal.stktopsp = getcallersp(unsafe.Pointer(&sig))
-	}
-
-	setg(g.m.gsignal)
-	sighandler(sig, info, ctx, g)
-	setg(g)
-}
diff --git a/src/runtime/signal_openbsd_386.go b/src/runtime/signal_openbsd_386.go
index dbbb2c8..2fc4b1d 100644
--- a/src/runtime/signal_openbsd_386.go
+++ b/src/runtime/signal_openbsd_386.go
@@ -11,19 +11,25 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *sigcontext {
 	return (*sigcontext)(c.ctxt)
 }
 
-func (c *sigctxt) eax() uint32     { return c.regs().sc_eax }
-func (c *sigctxt) ebx() uint32     { return c.regs().sc_ebx }
-func (c *sigctxt) ecx() uint32     { return c.regs().sc_ecx }
-func (c *sigctxt) edx() uint32     { return c.regs().sc_edx }
-func (c *sigctxt) edi() uint32     { return c.regs().sc_edi }
-func (c *sigctxt) esi() uint32     { return c.regs().sc_esi }
-func (c *sigctxt) ebp() uint32     { return c.regs().sc_ebp }
-func (c *sigctxt) esp() uint32     { return c.regs().sc_esp }
-func (c *sigctxt) eip() uint32     { return c.regs().sc_eip }
+func (c *sigctxt) eax() uint32 { return c.regs().sc_eax }
+func (c *sigctxt) ebx() uint32 { return c.regs().sc_ebx }
+func (c *sigctxt) ecx() uint32 { return c.regs().sc_ecx }
+func (c *sigctxt) edx() uint32 { return c.regs().sc_edx }
+func (c *sigctxt) edi() uint32 { return c.regs().sc_edi }
+func (c *sigctxt) esi() uint32 { return c.regs().sc_esi }
+func (c *sigctxt) ebp() uint32 { return c.regs().sc_ebp }
+func (c *sigctxt) esp() uint32 { return c.regs().sc_esp }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) eip() uint32 { return c.regs().sc_eip }
+
 func (c *sigctxt) eflags() uint32  { return c.regs().sc_eflags }
 func (c *sigctxt) cs() uint32      { return c.regs().sc_cs }
 func (c *sigctxt) fs() uint32      { return c.regs().sc_fs }
diff --git a/src/runtime/signal_openbsd_amd64.go b/src/runtime/signal_openbsd_amd64.go
index 5631ab4..091a88a 100644
--- a/src/runtime/signal_openbsd_amd64.go
+++ b/src/runtime/signal_openbsd_amd64.go
@@ -11,27 +11,33 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *sigcontext {
 	return (*sigcontext)(c.ctxt)
 }
 
-func (c *sigctxt) rax() uint64     { return c.regs().sc_rax }
-func (c *sigctxt) rbx() uint64     { return c.regs().sc_rbx }
-func (c *sigctxt) rcx() uint64     { return c.regs().sc_rcx }
-func (c *sigctxt) rdx() uint64     { return c.regs().sc_rdx }
-func (c *sigctxt) rdi() uint64     { return c.regs().sc_rdi }
-func (c *sigctxt) rsi() uint64     { return c.regs().sc_rsi }
-func (c *sigctxt) rbp() uint64     { return c.regs().sc_rbp }
-func (c *sigctxt) rsp() uint64     { return c.regs().sc_rsp }
-func (c *sigctxt) r8() uint64      { return c.regs().sc_r8 }
-func (c *sigctxt) r9() uint64      { return c.regs().sc_r9 }
-func (c *sigctxt) r10() uint64     { return c.regs().sc_r10 }
-func (c *sigctxt) r11() uint64     { return c.regs().sc_r11 }
-func (c *sigctxt) r12() uint64     { return c.regs().sc_r12 }
-func (c *sigctxt) r13() uint64     { return c.regs().sc_r13 }
-func (c *sigctxt) r14() uint64     { return c.regs().sc_r14 }
-func (c *sigctxt) r15() uint64     { return c.regs().sc_r15 }
-func (c *sigctxt) rip() uint64     { return c.regs().sc_rip }
+func (c *sigctxt) rax() uint64 { return c.regs().sc_rax }
+func (c *sigctxt) rbx() uint64 { return c.regs().sc_rbx }
+func (c *sigctxt) rcx() uint64 { return c.regs().sc_rcx }
+func (c *sigctxt) rdx() uint64 { return c.regs().sc_rdx }
+func (c *sigctxt) rdi() uint64 { return c.regs().sc_rdi }
+func (c *sigctxt) rsi() uint64 { return c.regs().sc_rsi }
+func (c *sigctxt) rbp() uint64 { return c.regs().sc_rbp }
+func (c *sigctxt) rsp() uint64 { return c.regs().sc_rsp }
+func (c *sigctxt) r8() uint64  { return c.regs().sc_r8 }
+func (c *sigctxt) r9() uint64  { return c.regs().sc_r9 }
+func (c *sigctxt) r10() uint64 { return c.regs().sc_r10 }
+func (c *sigctxt) r11() uint64 { return c.regs().sc_r11 }
+func (c *sigctxt) r12() uint64 { return c.regs().sc_r12 }
+func (c *sigctxt) r13() uint64 { return c.regs().sc_r13 }
+func (c *sigctxt) r14() uint64 { return c.regs().sc_r14 }
+func (c *sigctxt) r15() uint64 { return c.regs().sc_r15 }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) rip() uint64 { return c.regs().sc_rip }
+
 func (c *sigctxt) rflags() uint64  { return c.regs().sc_rflags }
 func (c *sigctxt) cs() uint64      { return c.regs().sc_cs }
 func (c *sigctxt) fs() uint64      { return c.regs().sc_fs }
diff --git a/src/runtime/signal_openbsd_arm.go b/src/runtime/signal_openbsd_arm.go
index 3158a4e..66aea93 100644
--- a/src/runtime/signal_openbsd_arm.go
+++ b/src/runtime/signal_openbsd_arm.go
@@ -11,26 +11,32 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *sigcontext {
 	return (*sigcontext)(c.ctxt)
 }
 
-func (c *sigctxt) r0() uint32      { return c.regs().sc_r0 }
-func (c *sigctxt) r1() uint32      { return c.regs().sc_r1 }
-func (c *sigctxt) r2() uint32      { return c.regs().sc_r2 }
-func (c *sigctxt) r3() uint32      { return c.regs().sc_r3 }
-func (c *sigctxt) r4() uint32      { return c.regs().sc_r4 }
-func (c *sigctxt) r5() uint32      { return c.regs().sc_r5 }
-func (c *sigctxt) r6() uint32      { return c.regs().sc_r6 }
-func (c *sigctxt) r7() uint32      { return c.regs().sc_r7 }
-func (c *sigctxt) r8() uint32      { return c.regs().sc_r8 }
-func (c *sigctxt) r9() uint32      { return c.regs().sc_r9 }
-func (c *sigctxt) r10() uint32     { return c.regs().sc_r10 }
-func (c *sigctxt) fp() uint32      { return c.regs().sc_r11 }
-func (c *sigctxt) ip() uint32      { return c.regs().sc_r12 }
-func (c *sigctxt) sp() uint32      { return c.regs().sc_usr_sp }
-func (c *sigctxt) lr() uint32      { return c.regs().sc_usr_lr }
-func (c *sigctxt) pc() uint32      { return c.regs().sc_pc }
+func (c *sigctxt) r0() uint32  { return c.regs().sc_r0 }
+func (c *sigctxt) r1() uint32  { return c.regs().sc_r1 }
+func (c *sigctxt) r2() uint32  { return c.regs().sc_r2 }
+func (c *sigctxt) r3() uint32  { return c.regs().sc_r3 }
+func (c *sigctxt) r4() uint32  { return c.regs().sc_r4 }
+func (c *sigctxt) r5() uint32  { return c.regs().sc_r5 }
+func (c *sigctxt) r6() uint32  { return c.regs().sc_r6 }
+func (c *sigctxt) r7() uint32  { return c.regs().sc_r7 }
+func (c *sigctxt) r8() uint32  { return c.regs().sc_r8 }
+func (c *sigctxt) r9() uint32  { return c.regs().sc_r9 }
+func (c *sigctxt) r10() uint32 { return c.regs().sc_r10 }
+func (c *sigctxt) fp() uint32  { return c.regs().sc_r11 }
+func (c *sigctxt) ip() uint32  { return c.regs().sc_r12 }
+func (c *sigctxt) sp() uint32  { return c.regs().sc_usr_sp }
+func (c *sigctxt) lr() uint32  { return c.regs().sc_usr_lr }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) pc() uint32 { return c.regs().sc_pc }
+
 func (c *sigctxt) cpsr() uint32    { return c.regs().sc_spsr }
 func (c *sigctxt) fault() uint32   { return c.sigaddr() }
 func (c *sigctxt) trap() uint32    { return 0 }
diff --git a/src/runtime/signal_ppc64x.go b/src/runtime/signal_ppc64x.go
index 01a4af7..f09f890 100644
--- a/src/runtime/signal_ppc64x.go
+++ b/src/runtime/signal_ppc64x.go
@@ -53,140 +53,45 @@ func dumpregs(c *sigctxt) {
 	print("trap ", hex(c.trap()), "\n")
 }
 
-var crashing int32
-
-// May run during STW, so write barriers are not allowed.
-//
+//go:nosplit
 //go:nowritebarrierrec
-func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
-	_g_ := getg()
-	c := &sigctxt{info, ctxt}
-
-	if sig == _SIGPROF {
-		sigprof(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp, _g_.m)
-		return
-	}
-	flags := int32(_SigThrow)
-	if sig < uint32(len(sigtable)) {
-		flags = sigtable[sig].flags
-	}
-	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
-		// Make it look like a call to the signal func.
-		// Have to pass arguments out of band since
-		// augmenting the stack frame would break
-		// the unwinding code.
-		gp.sig = sig
-		gp.sigcode0 = uintptr(c.sigcode())
-		gp.sigcode1 = uintptr(c.fault())
-		gp.sigpc = uintptr(c.pc())
-
-		// We arrange link, and pc to pretend the panicking
-		// function calls sigpanic directly.
-		// Always save LINK to stack so that panics in leaf
-		// functions are correctly handled. This smashes
-		// the stack frame but we're not going back there
-		// anyway.
-		sp := c.sp() - sys.MinFrameSize
-		c.set_sp(sp)
-		*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link()
-
-		pc := gp.sigpc
-
-		// If we don't recognize the PC as code
-		// but we do recognize the link register as code,
-		// then assume this was a call to non-code and treat like
-		// pc == 0, to make unwinding show the context.
-		if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
-			pc = 0
-		}
-
-		// Don't bother saving PC if it's zero, which is
-		// probably a call to a nil func: the old link register
-		// is more useful in the stack trace.
-		if pc != 0 {
-			c.set_link(uint64(pc))
-		}
-
-		// In case we are panicking from external C code
-		c.set_r0(0)
-		c.set_r30(uint64(uintptr(unsafe.Pointer(gp))))
-		c.set_r12(uint64(funcPC(sigpanic)))
-		c.set_pc(uint64(funcPC(sigpanic)))
-		return
-	}
-
-	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
-		if sigsend(sig) {
-			return
-		}
-	}
-
-	if c.sigcode() == _SI_USER && signal_ignored(sig) {
-		return
-	}
-
-	if flags&_SigKill != 0 {
-		dieFromSignal(int32(sig))
-	}
-
-	if flags&_SigThrow == 0 {
-		return
-	}
-
-	_g_.m.throwing = 1
-	_g_.m.caughtsig.set(gp)
-
-	if crashing == 0 {
-		startpanic()
-	}
-
-	if sig < uint32(len(sigtable)) {
-		print(sigtable[sig].name, "\n")
-	} else {
-		print("Signal ", sig, "\n")
-	}
-
-	print("PC=", hex(c.pc()), " m=", _g_.m.id, "\n")
-	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
-		print("signal arrived during cgo execution\n")
-		gp = _g_.m.lockedg
-	}
-	print("\n")
-
-	level, _, docrash := gotraceback()
-	if level > 0 {
-		goroutineheader(gp)
-		tracebacktrap(uintptr(c.pc()), uintptr(c.sp()), uintptr(c.link()), gp)
-		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
-			// tracebackothers on original m skipped this one; trace it now.
-			goroutineheader(_g_.m.curg)
-			traceback(^uintptr(0), ^uintptr(0), 0, gp)
-		} else if crashing == 0 {
-			tracebackothers(gp)
-			print("\n")
-		}
-		dumpregs(c)
+func (c *sigctxt) sigpc() uintptr { return uintptr(c.pc()) }
+
+func (c *sigctxt) sigsp() uintptr { return uintptr(c.sp()) }
+func (c *sigctxt) siglr() uintptr { return uintptr(c.link()) }
+
+// preparePanic sets up the stack to look like a call to sigpanic.
+func (c *sigctxt) preparePanic(sig uint32, gp *g) {
+	// We arrange link, and pc to pretend the panicking
+	// function calls sigpanic directly.
+	// Always save LINK to stack so that panics in leaf
+	// functions are correctly handled. This smashes
+	// the stack frame but we're not going back there
+	// anyway.
+	sp := c.sp() - sys.MinFrameSize
+	c.set_sp(sp)
+	*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.link()
+
+	pc := gp.sigpc
+
+	// If we don't recognize the PC as code
+	// but we do recognize the link register as code,
+	// then assume this was a call to non-code and treat like
+	// pc == 0, to make unwinding show the context.
+	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
+		pc = 0
 	}
 
-	if docrash {
-		crashing++
-		if crashing < sched.mcount {
-			// There are other m's that need to dump their stacks.
-			// Relay SIGQUIT to the next m by sending it to the current process.
-			// All m's that have already received SIGQUIT have signal masks blocking
-			// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
-			// When the last m receives the SIGQUIT, it will fall through to the call to
-			// crash below. Just in case the relaying gets botched, each m involved in
-			// the relay sleeps for 5 seconds and then does the crash/exit itself.
-			// In expected operation, the last m has received the SIGQUIT and run
-			// crash/exit and the process is gone, all long before any of the
-			// 5-second sleeps have finished.
-			print("\n-----\n\n")
-			raiseproc(_SIGQUIT)
-			usleep(5 * 1000 * 1000)
-		}
-		crash()
+	// Don't bother saving PC if it's zero, which is
+	// probably a call to a nil func: the old link register
+	// is more useful in the stack trace.
+	if pc != 0 {
+		c.set_link(uint64(pc))
 	}
 
-	exit(2)
+	// In case we are panicking from external C code
+	c.set_r0(0)
+	c.set_r30(uint64(uintptr(unsafe.Pointer(gp))))
+	c.set_r12(uint64(funcPC(sigpanic)))
+	c.set_pc(uint64(funcPC(sigpanic)))
 }
diff --git a/src/runtime/signal_sighandler.go b/src/runtime/signal_sighandler.go
new file mode 100644
index 0000000..5af12d7
--- /dev/null
+++ b/src/runtime/signal_sighandler.go
@@ -0,0 +1,133 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+
+package runtime
+
+import (
+	"unsafe"
+)
+
+// crashing is the number of m's we have waited for when implementing
+// GOTRACEBACK=crash when a signal is received.
+var crashing int32
+
+// sighandler is invoked when a signal occurs. The global g will be
+// set to a gsignal goroutine and we will be running on the alternate
+// signal stack. The parameter g will be the value of the global g
+// when the signal occurred. The sig, info, and ctxt parameters are
+// from the system signal handler: they are the parameters passed when
+// the SA is passed to the sigaction system call.
+//
+// The garbage collector may have stopped the world, so write barriers
+// are not allowed.
+//
+//go:nowritebarrierrec
+func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
+	_g_ := getg()
+	c := &sigctxt{info, ctxt}
+
+	if sig == _SIGPROF {
+		sigprof(c.sigpc(), c.sigsp(), c.siglr(), gp, _g_.m)
+		return
+	}
+
+	flags := int32(_SigThrow)
+	if sig < uint32(len(sigtable)) {
+		flags = sigtable[sig].flags
+	}
+	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
+		// The signal is going to cause a panic.
+		// Arrange the stack so that it looks like the point
+		// where the signal occurred made a call to the
+		// function sigpanic. Then set the PC to sigpanic.
+
+		// Have to pass arguments out of band since
+		// augmenting the stack frame would break
+		// the unwinding code.
+		gp.sig = sig
+		gp.sigcode0 = uintptr(c.sigcode())
+		gp.sigcode1 = uintptr(c.fault())
+		gp.sigpc = c.sigpc()
+
+		c.preparePanic(sig, gp)
+		return
+	}
+
+	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
+		if sigsend(sig) {
+			return
+		}
+	}
+
+	if c.sigcode() == _SI_USER && signal_ignored(sig) {
+		return
+	}
+
+	if flags&_SigKill != 0 {
+		dieFromSignal(sig)
+	}
+
+	if flags&_SigThrow == 0 {
+		return
+	}
+
+	_g_.m.throwing = 1
+	_g_.m.caughtsig.set(gp)
+
+	if crashing == 0 {
+		startpanic()
+	}
+
+	if sig < uint32(len(sigtable)) {
+		print(sigtable[sig].name, "\n")
+	} else {
+		print("Signal ", sig, "\n")
+	}
+
+	print("PC=", hex(c.sigpc()), " m=", _g_.m.id, " sigcode=", c.sigcode(), "\n")
+	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
+		print("signal arrived during cgo execution\n")
+		gp = _g_.m.lockedg
+	}
+	print("\n")
+
+	level, _, docrash := gotraceback()
+	if level > 0 {
+		goroutineheader(gp)
+		tracebacktrap(c.sigpc(), c.sigsp(), c.siglr(), gp)
+		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
+			// tracebackothers on original m skipped this one; trace it now.
+			goroutineheader(_g_.m.curg)
+			traceback(^uintptr(0), ^uintptr(0), 0, gp)
+		} else if crashing == 0 {
+			tracebackothers(gp)
+			print("\n")
+		}
+		dumpregs(c)
+	}
+
+	if docrash {
+		crashing++
+		if crashing < sched.mcount {
+			// There are other m's that need to dump their stacks.
+			// Relay SIGQUIT to the next m by sending it to the current process.
+			// All m's that have already received SIGQUIT have signal masks blocking
+			// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
+			// When the last m receives the SIGQUIT, it will fall through to the call to
+			// crash below. Just in case the relaying gets botched, each m involved in
+			// the relay sleeps for 5 seconds and then does the crash/exit itself.
+			// In expected operation, the last m has received the SIGQUIT and run
+			// crash/exit and the process is gone, all long before any of the
+			// 5-second sleeps have finished.
+			print("\n-----\n\n")
+			raiseproc(_SIGQUIT)
+			usleep(5 * 1000 * 1000)
+		}
+		crash()
+	}
+
+	exit(2)
+}
diff --git a/src/runtime/signal_sigtramp.go b/src/runtime/signal_sigtramp.go
deleted file mode 100644
index dbbbcd0..0000000
--- a/src/runtime/signal_sigtramp.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build dragonfly linux netbsd
-
-package runtime
-
-import "unsafe"
-
-// Continuation of the (assembly) sigtramp() logic.
-// This may be called with the world stopped.
-//go:nosplit
-//go:nowritebarrierrec
-func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
-	if sigfwdgo(sig, info, ctx) {
-		return
-	}
-	g := getg()
-	if g == nil {
-		if sig == _SIGPROF {
-			// Ignore profiling signals that arrive on
-			// non-Go threads. On some systems they will
-			// be handled directly by the signal handler,
-			// by calling sigprofNonGo, in which case we won't
-			// get here anyhow.
-			return
-		}
-		badsignal(uintptr(sig), &sigctxt{info, ctx})
-		return
-	}
-
-	// If some non-Go code called sigaltstack, adjust.
-	sp := uintptr(unsafe.Pointer(&sig))
-	if sp < g.m.gsignal.stack.lo || sp >= g.m.gsignal.stack.hi {
-		var st sigaltstackt
-		sigaltstack(nil, &st)
-		if st.ss_flags&_SS_DISABLE != 0 {
-			setg(nil)
-			cgocallback(unsafe.Pointer(funcPC(noSignalStack)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig), 0)
-		}
-		stsp := uintptr(unsafe.Pointer(st.ss_sp))
-		if sp < stsp || sp >= stsp+st.ss_size {
-			setg(nil)
-			cgocallback(unsafe.Pointer(funcPC(sigNotOnStack)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig), 0)
-		}
-		g.m.gsignal.stack.lo = stsp
-		g.m.gsignal.stack.hi = stsp + st.ss_size
-		g.m.gsignal.stackguard0 = stsp + _StackGuard
-		g.m.gsignal.stackguard1 = stsp + _StackGuard
-		g.m.gsignal.stackAlloc = st.ss_size
-		g.m.gsignal.stktopsp = getcallersp(unsafe.Pointer(&sig))
-	}
-
-	setg(g.m.gsignal)
-	sighandler(sig, info, ctx, g)
-	setg(g)
-}
diff --git a/src/runtime/signal_solaris.go b/src/runtime/signal_solaris.go
index a86f7bf..c931c22 100644
--- a/src/runtime/signal_solaris.go
+++ b/src/runtime/signal_solaris.go
@@ -33,7 +33,7 @@ var sigtable = [...]sigTabT{
 	/* 20 */ {_SigNotify, "SIGWINCH: window size change"},
 	/* 21 */ {_SigNotify, "SIGURG: urgent socket condition"},
 	/* 22 */ {_SigNotify, "SIGPOLL: pollable event occurred"},
-	/* 23 */ {_SigNotify + _SigDefault, "SIGSTOP: stop (cannot be caught or ignored)"},
+	/* 23 */ {0, "SIGSTOP: stop (cannot be caught or ignored)"},
 	/* 24 */ {_SigNotify + _SigDefault, "SIGTSTP: user stop requested from tty"},
 	/* 25 */ {_SigNotify + _SigDefault, "SIGCONT: stopped process has been continued"},
 	/* 26 */ {_SigNotify + _SigDefault, "SIGTTIN: background tty read attempted"},
diff --git a/src/runtime/signal_solaris_amd64.go b/src/runtime/signal_solaris_amd64.go
index a577c8c..b1da313 100644
--- a/src/runtime/signal_solaris_amd64.go
+++ b/src/runtime/signal_solaris_amd64.go
@@ -11,26 +11,33 @@ type sigctxt struct {
 	ctxt unsafe.Pointer
 }
 
+//go:nosplit
+//go:nowritebarrierrec
 func (c *sigctxt) regs() *mcontext {
 	return (*mcontext)(unsafe.Pointer(&(*ucontext)(c.ctxt).uc_mcontext))
 }
-func (c *sigctxt) rax() uint64     { return uint64(c.regs().gregs[_REG_RAX]) }
-func (c *sigctxt) rbx() uint64     { return uint64(c.regs().gregs[_REG_RBX]) }
-func (c *sigctxt) rcx() uint64     { return uint64(c.regs().gregs[_REG_RCX]) }
-func (c *sigctxt) rdx() uint64     { return uint64(c.regs().gregs[_REG_RDX]) }
-func (c *sigctxt) rdi() uint64     { return uint64(c.regs().gregs[_REG_RDI]) }
-func (c *sigctxt) rsi() uint64     { return uint64(c.regs().gregs[_REG_RSI]) }
-func (c *sigctxt) rbp() uint64     { return uint64(c.regs().gregs[_REG_RBP]) }
-func (c *sigctxt) rsp() uint64     { return uint64(c.regs().gregs[_REG_RSP]) }
-func (c *sigctxt) r8() uint64      { return uint64(c.regs().gregs[_REG_R8]) }
-func (c *sigctxt) r9() uint64      { return uint64(c.regs().gregs[_REG_R9]) }
-func (c *sigctxt) r10() uint64     { return uint64(c.regs().gregs[_REG_R10]) }
-func (c *sigctxt) r11() uint64     { return uint64(c.regs().gregs[_REG_R11]) }
-func (c *sigctxt) r12() uint64     { return uint64(c.regs().gregs[_REG_R12]) }
-func (c *sigctxt) r13() uint64     { return uint64(c.regs().gregs[_REG_R13]) }
-func (c *sigctxt) r14() uint64     { return uint64(c.regs().gregs[_REG_R14]) }
-func (c *sigctxt) r15() uint64     { return uint64(c.regs().gregs[_REG_R15]) }
-func (c *sigctxt) rip() uint64     { return uint64(c.regs().gregs[_REG_RIP]) }
+
+func (c *sigctxt) rax() uint64 { return uint64(c.regs().gregs[_REG_RAX]) }
+func (c *sigctxt) rbx() uint64 { return uint64(c.regs().gregs[_REG_RBX]) }
+func (c *sigctxt) rcx() uint64 { return uint64(c.regs().gregs[_REG_RCX]) }
+func (c *sigctxt) rdx() uint64 { return uint64(c.regs().gregs[_REG_RDX]) }
+func (c *sigctxt) rdi() uint64 { return uint64(c.regs().gregs[_REG_RDI]) }
+func (c *sigctxt) rsi() uint64 { return uint64(c.regs().gregs[_REG_RSI]) }
+func (c *sigctxt) rbp() uint64 { return uint64(c.regs().gregs[_REG_RBP]) }
+func (c *sigctxt) rsp() uint64 { return uint64(c.regs().gregs[_REG_RSP]) }
+func (c *sigctxt) r8() uint64  { return uint64(c.regs().gregs[_REG_R8]) }
+func (c *sigctxt) r9() uint64  { return uint64(c.regs().gregs[_REG_R9]) }
+func (c *sigctxt) r10() uint64 { return uint64(c.regs().gregs[_REG_R10]) }
+func (c *sigctxt) r11() uint64 { return uint64(c.regs().gregs[_REG_R11]) }
+func (c *sigctxt) r12() uint64 { return uint64(c.regs().gregs[_REG_R12]) }
+func (c *sigctxt) r13() uint64 { return uint64(c.regs().gregs[_REG_R13]) }
+func (c *sigctxt) r14() uint64 { return uint64(c.regs().gregs[_REG_R14]) }
+func (c *sigctxt) r15() uint64 { return uint64(c.regs().gregs[_REG_R15]) }
+
+//go:nosplit
+//go:nowritebarrierrec
+func (c *sigctxt) rip() uint64 { return uint64(c.regs().gregs[_REG_RIP]) }
+
 func (c *sigctxt) rflags() uint64  { return uint64(c.regs().gregs[_REG_RFLAGS]) }
 func (c *sigctxt) cs() uint64      { return uint64(c.regs().gregs[_REG_CS]) }
 func (c *sigctxt) fs() uint64      { return uint64(c.regs().gregs[_REG_FS]) }
diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go
index f59c9b9..78381e5 100644
--- a/src/runtime/signal_unix.go
+++ b/src/runtime/signal_unix.go
@@ -6,7 +6,10 @@
 
 package runtime
 
-import _ "unsafe" // for go:linkname
+import (
+	"runtime/internal/sys"
+	"unsafe"
+)
 
 //go:linkname os_sigpipe os.sigpipe
 func os_sigpipe() {
@@ -19,3 +22,638 @@ func signame(sig uint32) string {
 	}
 	return sigtable[sig].name
 }
+
+const (
+	_SIG_DFL uintptr = 0
+	_SIG_IGN uintptr = 1
+)
+
+// Stores the signal handlers registered before Go installed its own.
+// These signal handlers will be invoked in cases where Go doesn't want to
+// handle a particular signal (e.g., signal occurred on a non-Go thread).
+// See sigfwdgo() for more information on when the signals are forwarded.
+//
+// Signal forwarding is currently available only on Darwin and Linux.
+var fwdSig [_NSIG]uintptr
+
+// channels for synchronizing signal mask updates with the signal mask
+// thread
+var (
+	disableSigChan  chan uint32
+	enableSigChan   chan uint32
+	maskUpdatedChan chan struct{}
+)
+
+func init() {
+	// _NSIG is the number of signals on this operating system.
+	// sigtable should describe what to do for all the possible signals.
+	if len(sigtable) != _NSIG {
+		print("runtime: len(sigtable)=", len(sigtable), " _NSIG=", _NSIG, "\n")
+		throw("bad sigtable len")
+	}
+}
+
+var signalsOK bool
+
+// Initialize signals.
+// Called by libpreinit so runtime may not be initialized.
+//go:nosplit
+//go:nowritebarrierrec
+func initsig(preinit bool) {
+	if !preinit {
+		// It's now OK for signal handlers to run.
+		signalsOK = true
+	}
+
+	// For c-archive/c-shared this is called by libpreinit with
+	// preinit == true.
+	if (isarchive || islibrary) && !preinit {
+		return
+	}
+
+	for i := uint32(0); i < _NSIG; i++ {
+		t := &sigtable[i]
+		if t.flags == 0 || t.flags&_SigDefault != 0 {
+			continue
+		}
+		fwdSig[i] = getsig(i)
+
+		if !sigInstallGoHandler(i) {
+			// Even if we are not installing a signal handler,
+			// set SA_ONSTACK if necessary.
+			if fwdSig[i] != _SIG_DFL && fwdSig[i] != _SIG_IGN {
+				setsigstack(i)
+			}
+			continue
+		}
+
+		t.flags |= _SigHandling
+		setsig(i, funcPC(sighandler))
+	}
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func sigInstallGoHandler(sig uint32) bool {
+	// For some signals, we respect an inherited SIG_IGN handler
+	// rather than insist on installing our own default handler.
+	// Even these signals can be fetched using the os/signal package.
+	switch sig {
+	case _SIGHUP, _SIGINT:
+		if fwdSig[sig] == _SIG_IGN {
+			return false
+		}
+	}
+
+	t := &sigtable[sig]
+	if t.flags&_SigSetStack != 0 {
+		return false
+	}
+
+	// When built using c-archive or c-shared, only install signal
+	// handlers for synchronous signals and SIGPIPE.
+	if (isarchive || islibrary) && t.flags&_SigPanic == 0 && sig != _SIGPIPE {
+		return false
+	}
+
+	return true
+}
+
+func sigenable(sig uint32) {
+	if sig >= uint32(len(sigtable)) {
+		return
+	}
+
+	t := &sigtable[sig]
+	if t.flags&_SigNotify != 0 {
+		ensureSigM()
+		enableSigChan <- sig
+		<-maskUpdatedChan
+		if t.flags&_SigHandling == 0 {
+			t.flags |= _SigHandling
+			fwdSig[sig] = getsig(sig)
+			setsig(sig, funcPC(sighandler))
+		}
+	}
+}
+
+func sigdisable(sig uint32) {
+	if sig >= uint32(len(sigtable)) {
+		return
+	}
+
+	t := &sigtable[sig]
+	if t.flags&_SigNotify != 0 {
+		ensureSigM()
+		disableSigChan <- sig
+		<-maskUpdatedChan
+
+		// If initsig does not install a signal handler for a
+		// signal, then to go back to the state before Notify
+		// we should remove the one we installed.
+		if !sigInstallGoHandler(sig) {
+			t.flags &^= _SigHandling
+			setsig(sig, fwdSig[sig])
+		}
+	}
+}
+
+func sigignore(sig uint32) {
+	if sig >= uint32(len(sigtable)) {
+		return
+	}
+
+	t := &sigtable[sig]
+	if t.flags&_SigNotify != 0 {
+		t.flags &^= _SigHandling
+		setsig(sig, _SIG_IGN)
+	}
+}
+
+func resetcpuprofiler(hz int32) {
+	var it itimerval
+	if hz == 0 {
+		setitimer(_ITIMER_PROF, &it, nil)
+	} else {
+		it.it_interval.tv_sec = 0
+		it.it_interval.set_usec(1000000 / hz)
+		it.it_value = it.it_interval
+		setitimer(_ITIMER_PROF, &it, nil)
+	}
+	_g_ := getg()
+	_g_.m.profilehz = hz
+}
+
+func sigpipe() {
+	if sigsend(_SIGPIPE) {
+		return
+	}
+	dieFromSignal(_SIGPIPE)
+}
+
+// sigtrampgo is called from the signal handler function, sigtramp,
+// written in assembly code.
+// This is called by the signal handler, and the world may be stopped.
+//go:nosplit
+//go:nowritebarrierrec
+func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
+	if sigfwdgo(sig, info, ctx) {
+		return
+	}
+	g := getg()
+	if g == nil {
+		c := &sigctxt{info, ctx}
+		if sig == _SIGPROF {
+			sigprofNonGoPC(c.sigpc())
+			return
+		}
+		badsignal(uintptr(sig), c)
+		return
+	}
+
+	// If some non-Go code called sigaltstack, adjust.
+	sp := uintptr(unsafe.Pointer(&sig))
+	if sp < g.m.gsignal.stack.lo || sp >= g.m.gsignal.stack.hi {
+		var st stackt
+		sigaltstack(nil, &st)
+		if st.ss_flags&_SS_DISABLE != 0 {
+			setg(nil)
+			needm(0)
+			noSignalStack(sig)
+			dropm()
+		}
+		stsp := uintptr(unsafe.Pointer(st.ss_sp))
+		if sp < stsp || sp >= stsp+st.ss_size {
+			setg(nil)
+			needm(0)
+			sigNotOnStack(sig)
+			dropm()
+		}
+		setGsignalStack(&st)
+		g.m.gsignal.stktopsp = getcallersp(unsafe.Pointer(&sig))
+	}
+
+	setg(g.m.gsignal)
+	c := &sigctxt{info, ctx}
+	c.fixsigcode(sig)
+	sighandler(sig, info, ctx, g)
+	setg(g)
+}
+
+// sigpanic turns a synchronous signal into a run-time panic.
+// If the signal handler sees a synchronous panic, it arranges the
+// stack to look like the function where the signal occurred called
+// sigpanic, sets the signal's PC value to sigpanic, and returns from
+// the signal handler. The effect is that the program will act as
+// though the function that got the signal simply called sigpanic
+// instead.
+func sigpanic() {
+	g := getg()
+	if !canpanic(g) {
+		throw("unexpected signal during runtime execution")
+	}
+
+	switch g.sig {
+	case _SIGBUS:
+		if g.sigcode0 == _BUS_ADRERR && g.sigcode1 < 0x1000 {
+			panicmem()
+		}
+		// Support runtime/debug.SetPanicOnFault.
+		if g.paniconfault {
+			panicmem()
+		}
+		print("unexpected fault address ", hex(g.sigcode1), "\n")
+		throw("fault")
+	case _SIGSEGV:
+		if (g.sigcode0 == 0 || g.sigcode0 == _SEGV_MAPERR || g.sigcode0 == _SEGV_ACCERR) && g.sigcode1 < 0x1000 {
+			panicmem()
+		}
+		// Support runtime/debug.SetPanicOnFault.
+		if g.paniconfault {
+			panicmem()
+		}
+		print("unexpected fault address ", hex(g.sigcode1), "\n")
+		throw("fault")
+	case _SIGFPE:
+		switch g.sigcode0 {
+		case _FPE_INTDIV:
+			panicdivide()
+		case _FPE_INTOVF:
+			panicoverflow()
+		}
+		panicfloat()
+	}
+
+	if g.sig >= uint32(len(sigtable)) {
+		// can't happen: we looked up g.sig in sigtable to decide to call sigpanic
+		throw("unexpected signal value")
+	}
+	panic(errorString(sigtable[g.sig].name))
+}
+
+// dieFromSignal kills the program with a signal.
+// This provides the expected exit status for the shell.
+// This is only called with fatal signals expected to kill the process.
+//go:nosplit
+//go:nowritebarrierrec
+func dieFromSignal(sig uint32) {
+	setsig(sig, _SIG_DFL)
+	unblocksig(sig)
+	raise(sig)
+
+	// That should have killed us. On some systems, though, raise
+	// sends the signal to the whole process rather than to just
+	// the current thread, which means that the signal may not yet
+	// have been delivered. Give other threads a chance to run and
+	// pick up the signal.
+	osyield()
+	osyield()
+	osyield()
+
+	// If we are still somehow running, just exit with the wrong status.
+	exit(2)
+}
+
+// raisebadsignal is called when a signal is received on a non-Go
+// thread, and the Go program does not want to handle it (that is, the
+// program has not called os/signal.Notify for the signal).
+func raisebadsignal(sig uint32, c *sigctxt) {
+	if sig == _SIGPROF {
+		// Ignore profiling signals that arrive on non-Go threads.
+		return
+	}
+
+	var handler uintptr
+	if sig >= _NSIG {
+		handler = _SIG_DFL
+	} else {
+		handler = fwdSig[sig]
+	}
+
+	// Reset the signal handler and raise the signal.
+	// We are currently running inside a signal handler, so the
+	// signal is blocked. We need to unblock it before raising the
+	// signal, or the signal we raise will be ignored until we return
+	// from the signal handler. We know that the signal was unblocked
+	// before entering the handler, or else we would not have received
+	// it. That means that we don't have to worry about blocking it
+	// again.
+	unblocksig(sig)
+	setsig(sig, handler)
+
+	// If we're linked into a non-Go program we want to try to
+	// avoid modifying the original context in which the signal
+	// was raised. If the handler is the default, we know it
+	// is non-recoverable, so we don't have to worry about
+	// re-installing sighandler. At this point we can just
+	// return and the signal will be re-raised and caught by
+	// the default handler with the correct context.
+	if (isarchive || islibrary) && handler == _SIG_DFL && c.sigcode() != _SI_USER {
+		return
+	}
+
+	raise(sig)
+
+	// Give the signal a chance to be delivered.
+	// In almost all real cases the program is about to crash,
+	// so sleeping here is not a waste of time.
+	usleep(1000)
+
+	// If the signal didn't cause the program to exit, restore the
+	// Go signal handler and carry on.
+	//
+	// We may receive another instance of the signal before we
+	// restore the Go handler, but that is not so bad: we know
+	// that the Go program has been ignoring the signal.
+	setsig(sig, funcPC(sighandler))
+}
+
+func crash() {
+	if GOOS == "darwin" {
+		// OS X core dumps are linear dumps of the mapped memory,
+		// from the first virtual byte to the last, with zeros in the gaps.
+		// Because of the way we arrange the address space on 64-bit systems,
+		// this means the OS X core file will be >128 GB and even on a zippy
+		// workstation can take OS X well over an hour to write (uninterruptible).
+		// Save users from making that mistake.
+		if sys.PtrSize == 8 {
+			return
+		}
+	}
+
+	dieFromSignal(_SIGABRT)
+}
+
+// ensureSigM starts one global, sleeping thread to make sure at least one thread
+// is available to catch signals enabled for os/signal.
+func ensureSigM() {
+	if maskUpdatedChan != nil {
+		return
+	}
+	maskUpdatedChan = make(chan struct{})
+	disableSigChan = make(chan uint32)
+	enableSigChan = make(chan uint32)
+	go func() {
+		// Signal masks are per-thread, so make sure this goroutine stays on one
+		// thread.
+		LockOSThread()
+		defer UnlockOSThread()
+		// The sigBlocked mask contains the signals not active for os/signal,
+		// initially all signals except the essential. When signal.Notify()/Stop is called,
+		// sigenable/sigdisable in turn notify this thread to update its signal
+		// mask accordingly.
+		sigBlocked := sigset_all
+		for i := range sigtable {
+			if sigtable[i].flags&_SigUnblock != 0 {
+				sigdelset(&sigBlocked, i)
+			}
+		}
+		sigprocmask(_SIG_SETMASK, &sigBlocked, nil)
+		for {
+			select {
+			case sig := <-enableSigChan:
+				if sig > 0 {
+					sigdelset(&sigBlocked, int(sig))
+				}
+			case sig := <-disableSigChan:
+				if sig > 0 {
+					sigaddset(&sigBlocked, int(sig))
+				}
+			}
+			sigprocmask(_SIG_SETMASK, &sigBlocked, nil)
+			maskUpdatedChan <- struct{}{}
+		}
+	}()
+}
+
+// This is called when we receive a signal when there is no signal stack.
+// This can only happen if non-Go code calls sigaltstack to disable the
+// signal stack.
+func noSignalStack(sig uint32) {
+	println("signal", sig, "received on thread with no signal stack")
+	throw("non-Go code disabled sigaltstack")
+}
+
+// This is called if we receive a signal when there is a signal stack
+// but we are not on it. This can only happen if non-Go code called
+// sigaction without setting the SS_ONSTACK flag.
+func sigNotOnStack(sig uint32) {
+	println("signal", sig, "received but handler not on signal stack")
+	throw("non-Go code set up signal handler without SA_ONSTACK flag")
+}
+
+// This runs on a foreign stack, without an m or a g. No stack split.
+//go:nosplit
+//go:norace
+//go:nowritebarrierrec
+func badsignal(sig uintptr, c *sigctxt) {
+	needm(0)
+	if !sigsend(uint32(sig)) {
+		// A foreign thread received the signal sig, and the
+		// Go code does not want to handle it.
+		raisebadsignal(uint32(sig), c)
+	}
+	dropm()
+}
+
+//go:noescape
+func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
+
+// Determines if the signal should be handled by Go and if not, forwards the
+// signal to the handler that was installed before Go's. Returns whether the
+// signal was forwarded.
+// This is called by the signal handler, and the world may be stopped.
+//go:nosplit
+//go:nowritebarrierrec
+func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
+	if sig >= uint32(len(sigtable)) {
+		return false
+	}
+	fwdFn := fwdSig[sig]
+
+	if !signalsOK {
+		// The only way we can get here is if we are in a
+		// library or archive, we installed a signal handler
+		// at program startup, but the Go runtime has not yet
+		// been initialized.
+		if fwdFn == _SIG_DFL {
+			dieFromSignal(sig)
+		} else {
+			sigfwd(fwdFn, sig, info, ctx)
+		}
+		return true
+	}
+
+	flags := sigtable[sig].flags
+
+	// If there is no handler to forward to, no need to forward.
+	if fwdFn == _SIG_DFL {
+		return false
+	}
+
+	// If we aren't handling the signal, forward it.
+	if flags&_SigHandling == 0 {
+		sigfwd(fwdFn, sig, info, ctx)
+		return true
+	}
+
+	c := &sigctxt{info, ctx}
+	// Only forward signals from the kernel.
+	// On Linux and Darwin there is no way to distinguish a SIGPIPE raised by a write
+	// to a closed socket or pipe from a SIGPIPE raised by kill or pthread_kill
+	// so we'll treat every SIGPIPE as kernel-generated.
+	userSig := c.sigcode() == _SI_USER &&
+		(sig != _SIGPIPE || GOOS != "linux" && GOOS != "android" && GOOS != "darwin")
+	// Only forward synchronous signals and SIGPIPE.
+	if userSig || flags&_SigPanic == 0 && sig != _SIGPIPE {
+		return false
+	}
+	// Determine if the signal occurred inside Go code. We test that:
+	//   (1) we were in a goroutine (i.e., m.curg != nil), and
+	//   (2) we weren't in CGO (i.e., m.curg.syscallsp == 0).
+	g := getg()
+	if g != nil && g.m != nil && g.m.curg != nil && g.m.curg.syscallsp == 0 {
+		return false
+	}
+	// Signal not handled by Go, forward it.
+	if fwdFn != _SIG_IGN {
+		sigfwd(fwdFn, sig, info, ctx)
+	}
+	return true
+}
+
+// msigsave saves the current thread's signal mask into mp.sigmask.
+// This is used to preserve the non-Go signal mask when a non-Go
+// thread calls a Go function.
+// This is nosplit and nowritebarrierrec because it is called by needm
+// which may be called on a non-Go thread with no g available.
+//go:nosplit
+//go:nowritebarrierrec
+func msigsave(mp *m) {
+	sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
+}
+
+// msigrestore sets the current thread's signal mask to sigmask.
+// This is used to restore the non-Go signal mask when a non-Go thread
+// calls a Go function.
+// This is nosplit and nowritebarrierrec because it is called by dropm
+// after g has been cleared.
+//go:nosplit
+//go:nowritebarrierrec
+func msigrestore(sigmask sigset) {
+	sigprocmask(_SIG_SETMASK, &sigmask, nil)
+}
+
+// sigblock blocks all signals in the current thread's signal mask.
+// This is used to block signals while setting up and tearing down g
+// when a non-Go thread calls a Go function.
+// The OS-specific code is expected to define sigset_all.
+// This is nosplit and nowritebarrierrec because it is called by needm
+// which may be called on a non-Go thread with no g available.
+//go:nosplit
+//go:nowritebarrierrec
+func sigblock() {
+	sigprocmask(_SIG_SETMASK, &sigset_all, nil)
+}
+
+// unblocksig removes sig from the current thread's signal mask.
+// This is nosplit and nowritebarrierrec because it is called from
+// dieFromSignal, which can be called by sigfwdgo while running in the
+// signal handler, on the signal stack, with no g available.
+//go:nosplit
+//go:nowritebarrierrec
+func unblocksig(sig uint32) {
+	var set sigset
+	sigaddset(&set, int(sig))
+	sigprocmask(_SIG_UNBLOCK, &set, nil)
+}
+
+// minitSignals is called when initializing a new m to set the
+// thread's alternate signal stack and signal mask.
+func minitSignals() {
+	minitSignalStack()
+	minitSignalMask()
+}
+
+// minitSignalStack is called when initializing a new m to set the
+// alternate signal stack. If the alternate signal stack is not set
+// for the thread (the normal case) then set the alternate signal
+// stack to the gsignal stack. If the alternate signal stack is set
+// for the thread (the case when a non-Go thread sets the alternate
+// signal stack and then calls a Go function) then set the gsignal
+// stack to the alternate signal stack. Record which choice was made
+// in newSigstack, so that it can be undone in unminit.
+func minitSignalStack() {
+	_g_ := getg()
+	var st stackt
+	sigaltstack(nil, &st)
+	if st.ss_flags&_SS_DISABLE != 0 {
+		signalstack(&_g_.m.gsignal.stack)
+		_g_.m.newSigstack = true
+	} else {
+		setGsignalStack(&st)
+		_g_.m.newSigstack = false
+	}
+}
+
+// minitSignalMask is called when initializing a new m to set the
+// thread's signal mask. When this is called all signals have been
+// blocked for the thread.  This starts with m.sigmask, which was set
+// either from initSigmask for a newly created thread or by calling
+// msigsave if this is a non-Go thread calling a Go function. It
+// removes all essential signals from the mask, thus causing those
+// signals to not be blocked. Then it sets the thread's signal mask.
+// After this is called the thread can receive signals.
+func minitSignalMask() {
+	nmask := getg().m.sigmask
+	for i := range sigtable {
+		if sigtable[i].flags&_SigUnblock != 0 {
+			sigdelset(&nmask, i)
+		}
+	}
+	sigprocmask(_SIG_SETMASK, &nmask, nil)
+}
+
+// unminitSignals is called from dropm, via unminit, to undo the
+// effect of calling minit on a non-Go thread.
+//go:nosplit
+func unminitSignals() {
+	if getg().m.newSigstack {
+		st := stackt{ss_flags: _SS_DISABLE}
+		sigaltstack(&st, nil)
+	}
+}
+
+// setGsignalStack sets the gsignal stack of the current m to an
+// alternate signal stack returned from the sigaltstack system call.
+// This is used when handling a signal if non-Go code has set the
+// alternate signal stack.
+//go:nosplit
+//go:nowritebarrierrec
+func setGsignalStack(st *stackt) {
+	g := getg()
+	stsp := uintptr(unsafe.Pointer(st.ss_sp))
+	g.m.gsignal.stack.lo = stsp
+	g.m.gsignal.stack.hi = stsp + st.ss_size
+	g.m.gsignal.stackguard0 = stsp + _StackGuard
+	g.m.gsignal.stackguard1 = stsp + _StackGuard
+	g.m.gsignal.stackAlloc = st.ss_size
+}
+
+// signalstack sets the current thread's alternate signal stack to s.
+//go:nosplit
+func signalstack(s *stack) {
+	st := stackt{ss_size: s.hi - s.lo}
+	setSignalstackSP(&st, s.lo)
+	sigaltstack(&st, nil)
+}
+
+// setsigsegv is used on darwin/arm{,64} to fake a segmentation fault.
+//go:nosplit
+func setsigsegv(pc uintptr) {
+	g := getg()
+	g.sig = _SIGSEGV
+	g.sigpc = pc
+	g.sigcode0 = _SEGV_MAPERR
+	g.sigcode1 = 0 // TODO: emulate si_addr
+}
diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go
index 298dcc9..73bd5b5 100644
--- a/src/runtime/signal_windows.go
+++ b/src/runtime/signal_windows.go
@@ -205,7 +205,7 @@ func sigignore(sig uint32) {
 
 func badsignal2()
 
-func raisebadsignal(sig int32) {
+func raisebadsignal(sig uint32) {
 	badsignal2()
 }
 
diff --git a/src/runtime/sigpanic_unix.go b/src/runtime/sigpanic_unix.go
deleted file mode 100644
index 4cd8615..0000000
--- a/src/runtime/sigpanic_unix.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
-
-package runtime
-
-func sigpanic() {
-	g := getg()
-	if !canpanic(g) {
-		throw("unexpected signal during runtime execution")
-	}
-
-	switch g.sig {
-	case _SIGBUS:
-		if g.sigcode0 == _BUS_ADRERR && g.sigcode1 < 0x1000 || g.paniconfault {
-			panicmem()
-		}
-		print("unexpected fault address ", hex(g.sigcode1), "\n")
-		throw("fault")
-	case _SIGSEGV:
-		if (g.sigcode0 == 0 || g.sigcode0 == _SEGV_MAPERR || g.sigcode0 == _SEGV_ACCERR) && g.sigcode1 < 0x1000 || g.paniconfault {
-			panicmem()
-		}
-		print("unexpected fault address ", hex(g.sigcode1), "\n")
-		throw("fault")
-	case _SIGFPE:
-		switch g.sigcode0 {
-		case _FPE_INTDIV:
-			panicdivide()
-		case _FPE_INTOVF:
-			panicoverflow()
-		}
-		panicfloat()
-	}
-
-	if g.sig >= uint32(len(sigtable)) {
-		// can't happen: we looked up g.sig in sigtable to decide to call sigpanic
-		throw("unexpected signal value")
-	}
-	panic(errorString(sigtable[g.sig].name))
-}
-
-// setsigsegv is used on darwin/arm{,64} to fake a segmentation fault.
-//go:nosplit
-func setsigsegv(pc uintptr) {
-	g := getg()
-	g.sig = _SIGSEGV
-	g.sigpc = pc
-	g.sigcode0 = _SEGV_MAPERR
-	g.sigcode1 = 0 // TODO: emulate si_addr
-}
diff --git a/src/runtime/sigtab_linux_generic.go b/src/runtime/sigtab_linux_generic.go
index ea36bf3..874148e 100644
--- a/src/runtime/sigtab_linux_generic.go
+++ b/src/runtime/sigtab_linux_generic.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build !mips
+// +build !mipsle
 // +build !mips64
 // +build !mips64le
 // +build linux
diff --git a/src/runtime/sigtab_linux_mips64x.go b/src/runtime/sigtab_linux_mips64x.go
deleted file mode 100644
index 201fe3d..0000000
--- a/src/runtime/sigtab_linux_mips64x.go
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build mips64 mips64le
-// +build linux
-
-package runtime
-
-type sigTabT struct {
-	flags int32
-	name  string
-}
-
-var sigtable = [...]sigTabT{
-	/* 0 */ {0, "SIGNONE: no trap"},
-	/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
-	/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
-	/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
-	/* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
-	/* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
-	/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
-	/* 7 */ {_SigThrow, "SIGEMT"},
-	/* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
-	/* 9 */ {0, "SIGKILL: kill"},
-	/* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
-	/* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
-	/* 12 */ {_SigThrow, "SIGSYS: bad system call"},
-	/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
-	/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
-	/* 15 */ {_SigNotify + _SigKill, "SIGTERM: termination"},
-	/* 16 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
-	/* 17 */ {_SigNotify, "SIGUSR2: user-defined signal 2"},
-	/* 18 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
-	/* 19 */ {_SigNotify, "SIGPWR: power failure restart"},
-	/* 20 */ {_SigNotify, "SIGWINCH: window size change"},
-	/* 21 */ {_SigNotify, "SIGURG: urgent condition on socket"},
-	/* 22 */ {_SigNotify, "SIGIO: i/o now possible"},
-	/* 23 */ {0, "SIGSTOP: stop, unblockable"},
-	/* 24 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
-	/* 25 */ {_SigNotify + _SigDefault, "SIGCONT: continue"},
-	/* 26 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
-	/* 27 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
-	/* 28 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
-	/* 29 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
-	/* 30 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
-	/* 31 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
-	/* 32 */ {_SigSetStack + _SigUnblock, "signal 32"}, /* SIGCANCEL; see issue 6997 */
-	/* 33 */ {_SigSetStack + _SigUnblock, "signal 33"}, /* SIGSETXID; see issues 3871, 9400, 12498 */
-	/* 34 */ {_SigNotify, "signal 34"},
-	/* 35 */ {_SigNotify, "signal 35"},
-	/* 36 */ {_SigNotify, "signal 36"},
-	/* 37 */ {_SigNotify, "signal 37"},
-	/* 38 */ {_SigNotify, "signal 38"},
-	/* 39 */ {_SigNotify, "signal 39"},
-	/* 40 */ {_SigNotify, "signal 40"},
-	/* 41 */ {_SigNotify, "signal 41"},
-	/* 42 */ {_SigNotify, "signal 42"},
-	/* 43 */ {_SigNotify, "signal 43"},
-	/* 44 */ {_SigNotify, "signal 44"},
-	/* 45 */ {_SigNotify, "signal 45"},
-	/* 46 */ {_SigNotify, "signal 46"},
-	/* 47 */ {_SigNotify, "signal 47"},
-	/* 48 */ {_SigNotify, "signal 48"},
-	/* 49 */ {_SigNotify, "signal 49"},
-	/* 50 */ {_SigNotify, "signal 50"},
-	/* 51 */ {_SigNotify, "signal 51"},
-	/* 52 */ {_SigNotify, "signal 52"},
-	/* 53 */ {_SigNotify, "signal 53"},
-	/* 54 */ {_SigNotify, "signal 54"},
-	/* 55 */ {_SigNotify, "signal 55"},
-	/* 56 */ {_SigNotify, "signal 56"},
-	/* 57 */ {_SigNotify, "signal 57"},
-	/* 58 */ {_SigNotify, "signal 58"},
-	/* 59 */ {_SigNotify, "signal 59"},
-	/* 60 */ {_SigNotify, "signal 60"},
-	/* 61 */ {_SigNotify, "signal 61"},
-	/* 62 */ {_SigNotify, "signal 62"},
-	/* 63 */ {_SigNotify, "signal 63"},
-	/* 64 */ {_SigNotify, "signal 64"},
-}
diff --git a/src/runtime/sigtab_linux_mipsx.go b/src/runtime/sigtab_linux_mipsx.go
new file mode 100644
index 0000000..8d9fb06
--- /dev/null
+++ b/src/runtime/sigtab_linux_mipsx.go
@@ -0,0 +1,145 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle mips64 mips64le
+// +build linux
+
+package runtime
+
+type sigTabT struct {
+	flags int32
+	name  string
+}
+
+var sigtable = [...]sigTabT{
+	/*  0 */ {0, "SIGNONE: no trap"},
+	/*  1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
+	/*  2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
+	/*  3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
+	/*  4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
+	/*  5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
+	/*  6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
+	/*  7 */ {_SigThrow, "SIGEMT"},
+	/*  8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
+	/*  9 */ {0, "SIGKILL: kill"},
+	/*  10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+	/*  11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
+	/*  12 */ {_SigThrow, "SIGSYS: bad system call"},
+	/*  13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
+	/*  14 */ {_SigNotify, "SIGALRM: alarm clock"},
+	/*  15 */ {_SigNotify + _SigKill, "SIGTERM: termination"},
+	/*  16 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
+	/*  17 */ {_SigNotify, "SIGUSR2: user-defined signal 2"},
+	/*  18 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
+	/*  19 */ {_SigNotify, "SIGPWR: power failure restart"},
+	/*  20 */ {_SigNotify, "SIGWINCH: window size change"},
+	/*  21 */ {_SigNotify, "SIGURG: urgent condition on socket"},
+	/*  22 */ {_SigNotify, "SIGIO: i/o now possible"},
+	/*  23 */ {0, "SIGSTOP: stop, unblockable"},
+	/*  24 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
+	/*  25 */ {_SigNotify + _SigDefault, "SIGCONT: continue"},
+	/*  26 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
+	/*  27 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
+	/*  28 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
+	/*  29 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
+	/*  30 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
+	/*  31 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
+	/*  32 */ {_SigSetStack + _SigUnblock, "signal 32"}, /* SIGCANCEL; see issue 6997 */
+	/*  33 */ {_SigSetStack + _SigUnblock, "signal 33"}, /* SIGSETXID; see issues 3871, 9400, 12498 */
+	/*  34 */ {_SigNotify, "signal 34"},
+	/*  35 */ {_SigNotify, "signal 35"},
+	/*  36 */ {_SigNotify, "signal 36"},
+	/*  37 */ {_SigNotify, "signal 37"},
+	/*  38 */ {_SigNotify, "signal 38"},
+	/*  39 */ {_SigNotify, "signal 39"},
+	/*  40 */ {_SigNotify, "signal 40"},
+	/*  41 */ {_SigNotify, "signal 41"},
+	/*  42 */ {_SigNotify, "signal 42"},
+	/*  43 */ {_SigNotify, "signal 43"},
+	/*  44 */ {_SigNotify, "signal 44"},
+	/*  45 */ {_SigNotify, "signal 45"},
+	/*  46 */ {_SigNotify, "signal 46"},
+	/*  47 */ {_SigNotify, "signal 47"},
+	/*  48 */ {_SigNotify, "signal 48"},
+	/*  49 */ {_SigNotify, "signal 49"},
+	/*  50 */ {_SigNotify, "signal 50"},
+	/*  51 */ {_SigNotify, "signal 51"},
+	/*  52 */ {_SigNotify, "signal 52"},
+	/*  53 */ {_SigNotify, "signal 53"},
+	/*  54 */ {_SigNotify, "signal 54"},
+	/*  55 */ {_SigNotify, "signal 55"},
+	/*  56 */ {_SigNotify, "signal 56"},
+	/*  57 */ {_SigNotify, "signal 57"},
+	/*  58 */ {_SigNotify, "signal 58"},
+	/*  59 */ {_SigNotify, "signal 59"},
+	/*  60 */ {_SigNotify, "signal 60"},
+	/*  61 */ {_SigNotify, "signal 61"},
+	/*  62 */ {_SigNotify, "signal 62"},
+	/*  63 */ {_SigNotify, "signal 63"},
+	/*  64 */ {_SigNotify, "signal 64"},
+	/*  65 */ {_SigNotify, "signal 65"},
+	/*  66 */ {_SigNotify, "signal 66"},
+	/*  67 */ {_SigNotify, "signal 67"},
+	/*  68 */ {_SigNotify, "signal 68"},
+	/*  69 */ {_SigNotify, "signal 69"},
+	/*  70 */ {_SigNotify, "signal 70"},
+	/*  71 */ {_SigNotify, "signal 71"},
+	/*  72 */ {_SigNotify, "signal 72"},
+	/*  73 */ {_SigNotify, "signal 73"},
+	/*  74 */ {_SigNotify, "signal 74"},
+	/*  75 */ {_SigNotify, "signal 75"},
+	/*  76 */ {_SigNotify, "signal 76"},
+	/*  77 */ {_SigNotify, "signal 77"},
+	/*  78 */ {_SigNotify, "signal 78"},
+	/*  79 */ {_SigNotify, "signal 79"},
+	/*  80 */ {_SigNotify, "signal 80"},
+	/*  81 */ {_SigNotify, "signal 81"},
+	/*  82 */ {_SigNotify, "signal 82"},
+	/*  83 */ {_SigNotify, "signal 83"},
+	/*  84 */ {_SigNotify, "signal 84"},
+	/*  85 */ {_SigNotify, "signal 85"},
+	/*  86 */ {_SigNotify, "signal 86"},
+	/*  87 */ {_SigNotify, "signal 87"},
+	/*  88 */ {_SigNotify, "signal 88"},
+	/*  89 */ {_SigNotify, "signal 89"},
+	/*  90 */ {_SigNotify, "signal 90"},
+	/*  91 */ {_SigNotify, "signal 91"},
+	/*  92 */ {_SigNotify, "signal 92"},
+	/*  93 */ {_SigNotify, "signal 93"},
+	/*  94 */ {_SigNotify, "signal 94"},
+	/*  95 */ {_SigNotify, "signal 95"},
+	/*  96 */ {_SigNotify, "signal 96"},
+	/*  97 */ {_SigNotify, "signal 97"},
+	/*  98 */ {_SigNotify, "signal 98"},
+	/*  99 */ {_SigNotify, "signal 99"},
+	/* 100 */ {_SigNotify, "signal 100"},
+	/* 101 */ {_SigNotify, "signal 101"},
+	/* 102 */ {_SigNotify, "signal 102"},
+	/* 103 */ {_SigNotify, "signal 103"},
+	/* 104 */ {_SigNotify, "signal 104"},
+	/* 105 */ {_SigNotify, "signal 105"},
+	/* 106 */ {_SigNotify, "signal 106"},
+	/* 107 */ {_SigNotify, "signal 107"},
+	/* 108 */ {_SigNotify, "signal 108"},
+	/* 109 */ {_SigNotify, "signal 109"},
+	/* 110 */ {_SigNotify, "signal 110"},
+	/* 111 */ {_SigNotify, "signal 111"},
+	/* 112 */ {_SigNotify, "signal 112"},
+	/* 113 */ {_SigNotify, "signal 113"},
+	/* 114 */ {_SigNotify, "signal 114"},
+	/* 115 */ {_SigNotify, "signal 115"},
+	/* 116 */ {_SigNotify, "signal 116"},
+	/* 117 */ {_SigNotify, "signal 117"},
+	/* 118 */ {_SigNotify, "signal 118"},
+	/* 119 */ {_SigNotify, "signal 119"},
+	/* 120 */ {_SigNotify, "signal 120"},
+	/* 121 */ {_SigNotify, "signal 121"},
+	/* 122 */ {_SigNotify, "signal 122"},
+	/* 123 */ {_SigNotify, "signal 123"},
+	/* 124 */ {_SigNotify, "signal 124"},
+	/* 125 */ {_SigNotify, "signal 125"},
+	/* 126 */ {_SigNotify, "signal 126"},
+	/* 127 */ {_SigNotify, "signal 127"},
+	/* 128 */ {_SigNotify, "signal 128"},
+}
diff --git a/src/runtime/sizeclasses.go b/src/runtime/sizeclasses.go
new file mode 100644
index 0000000..ec30d15
--- /dev/null
+++ b/src/runtime/sizeclasses.go
@@ -0,0 +1,27 @@
+// AUTO-GENERATED by mksizeclasses.go; DO NOT EDIT
+//go:generate go run mksizeclasses.go
+
+package runtime
+
+const (
+	_MaxSmallSize   = 32768
+	smallSizeDiv    = 8
+	smallSizeMax    = 1024
+	largeSizeDiv    = 128
+	_NumSizeClasses = 67
+	_PageShift      = 13
+)
+
+var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768}
+var class_to_allocnpages = [_NumSizeClasses]uint8{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 2, 3, 1, 3, 2, 3, 4, 5, 6, 1, 7, 6, 5, 4, 3, 5, 7, 2, 9, 7, 5, 8, 3, 10, 7, 4}
+
+type divMagic struct {
+	shift    uint8
+	shift2   uint8
+	mul      uint16
+	baseMask uint16
+}
+
+var class_to_divmagic = [_NumSizeClasses]divMagic{{0, 0, 0, 0}, {3, 0, 1, 65528}, {4, 0, 1, 65520}, {5, 0, 1, 65504}, {4, 9, 171, 0}, {6, 0, 1, 65472}, {4, 10, 205, 0}, {5, 9, 171, 0}, {4, 11, 293, 0}, {7, 0, 1, 65408}, {4, 9, 57, 0}, {5, 10, 205, 0}, {4, 12, 373, 0}, {6, 7, 43, 0}, {4, 13, 631, 0}, {5, 11, 293, 0}, {4, 13, 547, 0}, {8, 0, 1, 65280}, {5, 9, 57, 0}, {6, 9, 103, 0}, {5, 12, 373, 0}, {7, 7, 43, 0}, {5, 10, 79, 0}, {6, 10, 147, 0}, {5, 11, 137, 0}, {9, 0, 1, 65024}, {6, 9, 5 [...]
+var size_to_class8 = [smallSizeMax/smallSizeDiv + 1]uint8{0, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, [...]
+var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv + 1]uint8{31, 32, 33, 34, 35, 36, 36, 37, 37, 38, 38, 39, 39, 39, 40, 40, 40, 41, 42, 42, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, 47, 47, 48, 48, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57,  [...]
diff --git a/src/runtime/slice.go b/src/runtime/slice.go
index e15e6c4..0f49df1 100644
--- a/src/runtime/slice.go
+++ b/src/runtime/slice.go
@@ -36,21 +36,18 @@ func maxSliceCap(elemsize uintptr) uintptr {
 	return _MaxMem / elemsize
 }
 
-// TODO: take uintptrs instead of int64s?
-func makeslice(et *_type, len64, cap64 int64) slice {
+func makeslice(et *_type, len, cap int) slice {
 	// NOTE: The len > maxElements check here is not strictly necessary,
 	// but it produces a 'len out of range' error instead of a 'cap out of range' error
 	// when someone does make([]T, bignumber). 'cap out of range' is true too,
 	// but since the cap is only being supplied implicitly, saying len is clearer.
 	// See issue 4085.
 	maxElements := maxSliceCap(et.size)
-	len := int(len64)
-	if len64 < 0 || int64(len) != len64 || uintptr(len) > maxElements {
+	if len < 0 || uintptr(len) > maxElements {
 		panic(errorString("makeslice: len out of range"))
 	}
 
-	cap := int(cap64)
-	if cap < len || int64(cap) != cap64 || uintptr(cap) > maxElements {
+	if cap < len || uintptr(cap) > maxElements {
 		panic(errorString("makeslice: cap out of range"))
 	}
 
@@ -58,6 +55,20 @@ func makeslice(et *_type, len64, cap64 int64) slice {
 	return slice{p, len, cap}
 }
 
+func makeslice64(et *_type, len64, cap64 int64) slice {
+	len := int(len64)
+	if int64(len) != len64 {
+		panic(errorString("makeslice: len out of range"))
+	}
+
+	cap := int(cap64)
+	if int64(cap) != cap64 {
+		panic(errorString("makeslice: cap out of range"))
+	}
+
+	return makeslice(et, len, cap)
+}
+
 // growslice handles slice growth during append.
 // It is passed the slice element type, the old slice, and the desired new minimum capacity,
 // and it returns a new slice with at least that capacity, with the old data
@@ -100,19 +111,22 @@ func growslice(et *_type, old slice, cap int) slice {
 		}
 	}
 
-	var lenmem, capmem uintptr
+	var lenmem, newlenmem, capmem uintptr
 	const ptrSize = unsafe.Sizeof((*byte)(nil))
 	switch et.size {
 	case 1:
 		lenmem = uintptr(old.len)
+		newlenmem = uintptr(cap)
 		capmem = roundupsize(uintptr(newcap))
 		newcap = int(capmem)
 	case ptrSize:
 		lenmem = uintptr(old.len) * ptrSize
+		newlenmem = uintptr(cap) * ptrSize
 		capmem = roundupsize(uintptr(newcap) * ptrSize)
 		newcap = int(capmem / ptrSize)
 	default:
 		lenmem = uintptr(old.len) * et.size
+		newlenmem = uintptr(cap) * et.size
 		capmem = roundupsize(uintptr(newcap) * et.size)
 		newcap = int(capmem / et.size)
 	}
@@ -125,7 +139,9 @@ func growslice(et *_type, old slice, cap int) slice {
 	if et.kind&kindNoPointers != 0 {
 		p = mallocgc(capmem, nil, false)
 		memmove(p, old.array, lenmem)
-		memclr(add(p, lenmem), capmem-lenmem)
+		// The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length).
+		// Only clear the part that will not be overwritten.
+		memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
 	} else {
 		// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
 		p = mallocgc(capmem, et, true)
diff --git a/src/runtime/softfloat_arm.go b/src/runtime/softfloat_arm.go
index 5f609c8..3cbb4b3 100644
--- a/src/runtime/softfloat_arm.go
+++ b/src/runtime/softfloat_arm.go
@@ -446,6 +446,23 @@ execute:
 		}
 		return 1
 
+	case 0xeeb10b40: // D[regd] = neg D[regm]
+		m.freglo[regd] = m.freglo[regm]
+		m.freghi[regd] = m.freghi[regm] ^ 1<<31
+
+		if fptrace > 0 {
+			print("*** D[", regd, "] = neg D[", regm, "] ", hex(m.freghi[regd]), "-", hex(m.freglo[regd]), "\n")
+		}
+		return 1
+
+	case 0xeeb10a40: // F[regd] = neg F[regm]
+		m.freglo[regd] = m.freglo[regm] ^ 1<<31
+
+		if fptrace > 0 {
+			print("*** F[", regd, "] = neg F[", regm, "] ", hex(m.freglo[regd]), "\n")
+		}
+		return 1
+
 	case 0xeeb40bc0: // D[regd] :: D[regm] (CMPD)
 		cmp, nan := fcmp64(fgetd(regd), fgetd(regm))
 		m.fflag = fstatus(nan, cmp)
@@ -464,6 +481,24 @@ execute:
 		}
 		return 1
 
+	case 0xeeb50bc0: // D[regd] :: 0 (CMPD)
+		cmp, nan := fcmp64(fgetd(regd), 0)
+		m.fflag = fstatus(nan, cmp)
+
+		if fptrace > 0 {
+			print("*** cmp D[", regd, "]::0 ", hex(m.fflag), "\n")
+		}
+		return 1
+
+	case 0xeeb50ac0: // F[regd] :: 0 (CMPF)
+		cmp, nan := fcmp64(f32to64(m.freglo[regd]), 0)
+		m.fflag = fstatus(nan, cmp)
+
+		if fptrace > 0 {
+			print("*** cmp F[", regd, "]::0 ", hex(m.fflag), "\n")
+		}
+		return 1
+
 	case 0xeeb70ac0: // D[regd] = F[regm] (MOVFD)
 		fputd(regd, f32to64(m.freglo[regm]))
 
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index 8398a10..ea9a69a 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -90,7 +90,7 @@ const (
 
 	// The stack guard is a pointer this many bytes above the
 	// bottom of the stack.
-	_StackGuard = 720*sys.StackGuardMultiplier + _StackSystem
+	_StackGuard = 880*sys.StackGuardMultiplier + _StackSystem
 
 	// After a stack split check the SP is allowed to be this
 	// many bytes below the stack guard. This saves an instruction
@@ -335,6 +335,7 @@ func stackalloc(n uint32) (stack, []stkbar) {
 	// Compute the size of stack barrier array.
 	maxstkbar := gcMaxStackBarriers(int(n))
 	nstkbar := unsafe.Sizeof(stkbar{}) * uintptr(maxstkbar)
+	var stkbarSlice slice
 
 	if debug.efence != 0 || stackFromSystem != 0 {
 		v := sysAlloc(round(uintptr(n), _PageSize), &memstats.stacks_sys)
@@ -342,7 +343,9 @@ func stackalloc(n uint32) (stack, []stkbar) {
 			throw("out of memory (stackalloc)")
 		}
 		top := uintptr(n) - nstkbar
-		stkbarSlice := slice{add(v, top), 0, maxstkbar}
+		if maxstkbar != 0 {
+			stkbarSlice = slice{add(v, top), 0, maxstkbar}
+		}
 		return stack{uintptr(v), uintptr(v) + top}, *(*[]stkbar)(unsafe.Pointer(&stkbarSlice))
 	}
 
@@ -410,7 +413,9 @@ func stackalloc(n uint32) (stack, []stkbar) {
 		print("  allocated ", v, "\n")
 	}
 	top := uintptr(n) - nstkbar
-	stkbarSlice := slice{add(v, top), 0, maxstkbar}
+	if maxstkbar != 0 {
+		stkbarSlice = slice{add(v, top), 0, maxstkbar}
+	}
 	return stack{uintptr(v), uintptr(v) + top}, *(*[]stkbar)(unsafe.Pointer(&stkbarSlice))
 }
 
@@ -431,7 +436,7 @@ func stackfree(stk stack, n uintptr) {
 	}
 	if stackDebug >= 1 {
 		println("stackfree", v, n)
-		memclr(v, n) // for testing, clobber stack data
+		memclrNoHeapPointers(v, n) // for testing, clobber stack data
 	}
 	if debug.efence != 0 || stackFromSystem != 0 {
 		if debug.efence != 0 || stackFaultOnFree != 0 {
@@ -598,11 +603,11 @@ func adjustpointers(scanp unsafe.Pointer, cbv *bitvector, adjinfo *adjustinfo, f
 				// Live analysis wrong?
 				getg().m.traceback = 2
 				print("runtime: bad pointer in frame ", funcname(f), " at ", pp, ": ", hex(p), "\n")
-				throw("invalid stack pointer")
+				throw("invalid pointer found on stack")
 			}
 			if minp <= p && p < maxp {
 				if stackDebug >= 3 {
-					print("adjust ptr ", p, " ", funcname(f), "\n")
+					print("adjust ptr ", hex(p), " ", funcname(f), "\n")
 				}
 				if useCAS {
 					ppu := (*unsafe.Pointer)(unsafe.Pointer(pp))
@@ -925,7 +930,10 @@ func round2(x int32) int32 {
 //
 // g->atomicstatus will be Grunning or Gscanrunning upon entry.
 // If the GC is trying to stop this g then it will set preemptscan to true.
-func newstack() {
+//
+// ctxt is the value of the context register on morestack. newstack
+// will write it to g.sched.ctxt.
+func newstack(ctxt unsafe.Pointer) {
 	thisg := getg()
 	// TODO: double check all gp. shouldn't be getg().
 	if thisg.m.morebuf.g.ptr().stackguard0 == stackFork {
@@ -937,8 +945,13 @@ func newstack() {
 		traceback(morebuf.pc, morebuf.sp, morebuf.lr, morebuf.g.ptr())
 		throw("runtime: wrong goroutine in newstack")
 	}
+
+	gp := thisg.m.curg
+	// Write ctxt to gp.sched. We do this here instead of in
+	// morestack so it has the necessary write barrier.
+	gp.sched.ctxt = ctxt
+
 	if thisg.m.curg.throwsplit {
-		gp := thisg.m.curg
 		// Update syscallsp, syscallpc in case traceback uses them.
 		morebuf := thisg.m.morebuf
 		gp.syscallsp = morebuf.sp
@@ -951,13 +964,11 @@ func newstack() {
 		throw("runtime: stack split at bad time")
 	}
 
-	gp := thisg.m.curg
 	morebuf := thisg.m.morebuf
 	thisg.m.morebuf.pc = 0
 	thisg.m.morebuf.lr = 0
 	thisg.m.morebuf.sp = 0
 	thisg.m.morebuf.g = 0
-	rewindmorestack(&gp.sched)
 
 	// NOTE: stackguard0 may change underfoot, if another thread
 	// is about to try to preempt gp. Read it just once and use that same
@@ -1004,14 +1015,6 @@ func newstack() {
 		throw("runtime: split stack overflow")
 	}
 
-	if gp.sched.ctxt != nil {
-		// morestack wrote sched.ctxt on its way in here,
-		// without a write barrier. Run the write barrier now.
-		// It is not possible to be preempted between then
-		// and now, so it's okay.
-		writebarrierptr_nostore((*uintptr)(unsafe.Pointer(&gp.sched.ctxt)), uintptr(gp.sched.ctxt))
-	}
-
 	if preempt {
 		if gp == thisg.m.g0 {
 			throw("runtime: preempt g0")
@@ -1119,6 +1122,11 @@ func shrinkstack(gp *g) {
 	if debug.gcshrinkstackoff > 0 {
 		return
 	}
+	if gp.startpc == gcBgMarkWorkerPC {
+		// We're not allowed to shrink the gcBgMarkWorker
+		// stack (see gcBgMarkWorker for explanation).
+		return
+	}
 
 	oldsize := gp.stackAlloc
 	newsize := oldsize / 2
diff --git a/src/runtime/string.go b/src/runtime/string.go
index ef28ba9..822adaa 100644
--- a/src/runtime/string.go
+++ b/src/runtime/string.go
@@ -4,10 +4,7 @@
 
 package runtime
 
-import (
-	"runtime/internal/atomic"
-	"unsafe"
-)
+import "unsafe"
 
 // The constant is known to the compiler.
 // There is no fundamental theory behind this number.
@@ -47,10 +44,9 @@ func concatstrings(buf *tmpBuf, a []string) string {
 		return a[idx]
 	}
 	s, b := rawstringtmp(buf, l)
-	l = 0
 	for _, x := range a {
-		copy(b[l:], x)
-		l += len(x)
+		copy(b, x)
+		b = b[len(x):]
 	}
 	return s
 }
@@ -113,17 +109,20 @@ func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) {
 	return
 }
 
+// slicebytetostringtmp returns a "string" referring to the actual []byte bytes.
+//
+// Callers need to ensure that the returned string will not be used after
+// the calling goroutine modifies the original slice or synchronizes with
+// another goroutine.
+//
+// The function is only called when instrumenting
+// and otherwise intrinsified by the compiler.
+//
+// Some internal compiler optimizations use this function.
+// - Used for m[string(k)] lookup where m is a string-keyed map and k is a []byte.
+// - Used for "<"+string(b)+">" concatenation where b is []byte.
+// - Used for string(b)=="foo" comparison where b is []byte.
 func slicebytetostringtmp(b []byte) string {
-	// Return a "string" referring to the actual []byte bytes.
-	// This is only for use by internal compiler optimizations
-	// that know that the string form will be discarded before
-	// the calling goroutine could possibly modify the original
-	// slice or synchronize with another goroutine.
-	// First such case is a m[string(k)] lookup where
-	// m is a string-keyed map and k is a []byte.
-	// Second such case is "<"+string(b)+">" concatenation where b is []byte.
-	// Third such case is string(b)=="foo" comparison where b is []byte.
-
 	if raceenabled && len(b) > 0 {
 		racereadrangepc(unsafe.Pointer(&b[0]),
 			uintptr(len(b)),
@@ -148,28 +147,14 @@ func stringtoslicebyte(buf *tmpBuf, s string) []byte {
 	return b
 }
 
-func stringtoslicebytetmp(s string) []byte {
-	// Return a slice referring to the actual string bytes.
-	// This is only for use by internal compiler optimizations
-	// that know that the slice won't be mutated.
-	// The only such case today is:
-	// for i, c := range []byte(str)
-
-	str := stringStructOf(&s)
-	ret := slice{array: str.str, len: str.len, cap: str.len}
-	return *(*[]byte)(unsafe.Pointer(&ret))
-}
-
 func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune {
 	// two passes.
 	// unlike slicerunetostring, no race because strings are immutable.
 	n := 0
-	t := s
-	for len(s) > 0 {
-		_, k := charntorune(s)
-		s = s[k:]
+	for range s {
 		n++
 	}
+
 	var a []rune
 	if buf != nil && n <= len(buf) {
 		*buf = [tmpStringBufSize]rune{}
@@ -177,10 +162,9 @@ func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune {
 	} else {
 		a = rawruneslice(n)
 	}
+
 	n = 0
-	for len(t) > 0 {
-		r, k := charntorune(t)
-		t = t[k:]
+	for _, r := range s {
 		a[n] = r
 		n++
 	}
@@ -200,7 +184,7 @@ func slicerunetostring(buf *tmpBuf, a []rune) string {
 	var dum [4]byte
 	size1 := 0
 	for _, r := range a {
-		size1 += runetochar(dum[:], r)
+		size1 += encoderune(dum[:], r)
 	}
 	s, b := rawstringtmp(buf, size1+3)
 	size2 := 0
@@ -209,7 +193,7 @@ func slicerunetostring(buf *tmpBuf, a []rune) string {
 		if size2 >= size1 {
 			break
 		}
-		size2 += runetochar(b[size2:], r)
+		size2 += encoderune(b[size2:], r)
 	}
 	return s[:size2]
 }
@@ -239,48 +223,12 @@ func intstring(buf *[4]byte, v int64) string {
 		s, b = rawstring(4)
 	}
 	if int64(rune(v)) != v {
-		v = runeerror
+		v = runeError
 	}
-	n := runetochar(b, rune(v))
+	n := encoderune(b, rune(v))
 	return s[:n]
 }
 
-// stringiter returns the index of the next
-// rune after the rune that starts at s[k].
-func stringiter(s string, k int) int {
-	if k >= len(s) {
-		// 0 is end of iteration
-		return 0
-	}
-
-	c := s[k]
-	if c < runeself {
-		return k + 1
-	}
-
-	// multi-char rune
-	_, n := charntorune(s[k:])
-	return k + n
-}
-
-// stringiter2 returns the rune that starts at s[k]
-// and the index where the next rune starts.
-func stringiter2(s string, k int) (int, rune) {
-	if k >= len(s) {
-		// 0 is end of iteration
-		return 0, 0
-	}
-
-	c := s[k]
-	if c < runeself {
-		return k + 1, rune(c)
-	}
-
-	// multi-char rune
-	r, n := charntorune(s[k:])
-	return k + n, r
-}
-
 // rawstring allocates storage for a new string. The returned
 // string and byte slice both refer to the same storage.
 // The storage is not zeroed. Callers should use
@@ -293,12 +241,7 @@ func rawstring(size int) (s string, b []byte) {
 
 	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, size}
 
-	for {
-		ms := maxstring
-		if uintptr(size) <= ms || atomic.Casuintptr((*uintptr)(unsafe.Pointer(&maxstring)), ms, uintptr(size)) {
-			return
-		}
-	}
+	return
 }
 
 // rawbyteslice allocates a new byte slice. The byte slice is not zeroed.
@@ -306,7 +249,7 @@ func rawbyteslice(size int) (b []byte) {
 	cap := roundupsize(uintptr(size))
 	p := mallocgc(cap, nil, false)
 	if cap != uintptr(size) {
-		memclr(add(p, uintptr(size)), cap-uintptr(size))
+		memclrNoHeapPointers(add(p, uintptr(size)), cap-uintptr(size))
 	}
 
 	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(cap)}
@@ -321,7 +264,7 @@ func rawruneslice(size int) (b []rune) {
 	mem := roundupsize(uintptr(size) * 4)
 	p := mallocgc(mem, nil, false)
 	if mem != uintptr(size)*4 {
-		memclr(add(p, uintptr(size)*4), mem-uintptr(size)*4)
+		memclrNoHeapPointers(add(p, uintptr(size)*4), mem-uintptr(size)*4)
 	}
 
 	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(mem / 4)}
@@ -377,13 +320,66 @@ func hasprefix(s, t string) bool {
 	return len(s) >= len(t) && s[:len(t)] == t
 }
 
-func atoi(s string) int {
-	n := 0
-	for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
-		n = n*10 + int(s[0]) - '0'
+const (
+	maxUint = ^uint(0)
+	maxInt  = int(maxUint >> 1)
+)
+
+// atoi parses an int from a string s.
+// The bool result reports whether s is a number
+// representable by a value of type int.
+func atoi(s string) (int, bool) {
+	if s == "" {
+		return 0, false
+	}
+
+	neg := false
+	if s[0] == '-' {
+		neg = true
 		s = s[1:]
 	}
-	return n
+
+	un := uint(0)
+	for i := 0; i < len(s); i++ {
+		c := s[i]
+		if c < '0' || c > '9' {
+			return 0, false
+		}
+		if un > maxUint/10 {
+			// overflow
+			return 0, false
+		}
+		un *= 10
+		un1 := un + uint(c) - '0'
+		if un1 < un {
+			// overflow
+			return 0, false
+		}
+		un = un1
+	}
+
+	if !neg && un > uint(maxInt) {
+		return 0, false
+	}
+	if neg && un > uint(maxInt)+1 {
+		return 0, false
+	}
+
+	n := int(un)
+	if neg {
+		n = -n
+	}
+
+	return n, true
+}
+
+// atoi32 is like atoi but for integers
+// that fit into an int32.
+func atoi32(s string) (int32, bool) {
+	if n, ok := atoi(s); n == int(int32(n)) {
+		return int32(n), ok
+	}
+	return 0, false
 }
 
 //go:nosplit
@@ -411,18 +407,10 @@ func findnullw(s *uint16) int {
 	return l
 }
 
-var maxstring uintptr = 256 // a hint for print
-
 //go:nosplit
 func gostringnocopy(str *byte) string {
 	ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)}
 	s := *(*string)(unsafe.Pointer(&ss))
-	for {
-		ms := maxstring
-		if uintptr(len(s)) <= ms || atomic.Casuintptr(&maxstring, ms, uintptr(len(s))) {
-			break
-		}
-	}
 	return s
 }
 
@@ -431,7 +419,7 @@ func gostringw(strw *uint16) string {
 	str := (*[_MaxMem/2/2 - 1]uint16)(unsafe.Pointer(strw))
 	n1 := 0
 	for i := 0; str[i] != 0; i++ {
-		n1 += runetochar(buf[:], rune(str[i]))
+		n1 += encoderune(buf[:], rune(str[i]))
 	}
 	s, b := rawstring(n1 + 4)
 	n2 := 0
@@ -440,7 +428,7 @@ func gostringw(strw *uint16) string {
 		if n2 >= n1 {
 			break
 		}
-		n2 += runetochar(b[n2:], rune(str[i]))
+		n2 += encoderune(b[n2:], rune(str[i]))
 	}
 	b[n2] = 0 // for luck
 	return s[:n2]
diff --git a/src/runtime/string_test.go b/src/runtime/string_test.go
index 0f1d82a..fcfc522 100644
--- a/src/runtime/string_test.go
+++ b/src/runtime/string_test.go
@@ -82,28 +82,50 @@ func BenchmarkCompareStringBig(b *testing.B) {
 	b.SetBytes(int64(len(s1)))
 }
 
-func BenchmarkRuneIterate(b *testing.B) {
-	bytes := make([]byte, 100)
-	for i := range bytes {
-		bytes[i] = byte('A')
-	}
-	s := string(bytes)
+func BenchmarkConcatStringAndBytes(b *testing.B) {
+	s1 := []byte("Gophers!")
 	for i := 0; i < b.N; i++ {
-		for range s {
-		}
+		_ = "Hello " + string(s1)
 	}
 }
 
-func BenchmarkRuneIterate2(b *testing.B) {
-	bytes := make([]byte, 100)
-	for i := range bytes {
-		bytes[i] = byte('A')
-	}
-	s := string(bytes)
-	for i := 0; i < b.N; i++ {
-		for range s {
+var stringdata = []struct{ name, data string }{
+	{"ASCII", "01234567890"},
+	{"Japanese", "日本語日本語日本語"},
+	{"MixedLength", "$Ѐࠀက퀀𐀀\U00040000\U0010FFFF"},
+}
+
+func BenchmarkRuneIterate(b *testing.B) {
+	b.Run("range", func(b *testing.B) {
+		for _, sd := range stringdata {
+			b.Run(sd.name, func(b *testing.B) {
+				for i := 0; i < b.N; i++ {
+					for range sd.data {
+					}
+				}
+			})
 		}
-	}
+	})
+	b.Run("range1", func(b *testing.B) {
+		for _, sd := range stringdata {
+			b.Run(sd.name, func(b *testing.B) {
+				for i := 0; i < b.N; i++ {
+					for _ = range sd.data {
+					}
+				}
+			})
+		}
+	})
+	b.Run("range2", func(b *testing.B) {
+		for _, sd := range stringdata {
+			b.Run(sd.name, func(b *testing.B) {
+				for i := 0; i < b.N; i++ {
+					for _, _ = range sd.data {
+					}
+				}
+			})
+		}
+	})
 }
 
 func BenchmarkArrayEqual(b *testing.B) {
@@ -148,19 +170,6 @@ func TestLargeStringConcat(t *testing.T) {
 	}
 }
 
-func TestGostringnocopy(t *testing.T) {
-	max := *runtime.Maxstring
-	b := make([]byte, max+10)
-	for i := uintptr(0); i < max+9; i++ {
-		b[i] = 'a'
-	}
-	_ = runtime.Gostringnocopy(&b[0])
-	newmax := *runtime.Maxstring
-	if newmax != max+9 {
-		t.Errorf("want %d, got %d", max+9, newmax)
-	}
-}
-
 func TestCompareTempString(t *testing.T) {
 	s := strings.Repeat("x", sizeNoStack)
 	b := []byte(s)
@@ -270,3 +279,97 @@ func TestString2Slice(t *testing.T) {
 		t.Errorf("extra runes not zeroed")
 	}
 }
+
+const intSize = 32 << (^uint(0) >> 63)
+
+type atoi64Test struct {
+	in  string
+	out int64
+	ok  bool
+}
+
+var atoi64tests = []atoi64Test{
+	{"", 0, false},
+	{"0", 0, true},
+	{"-0", 0, true},
+	{"1", 1, true},
+	{"-1", -1, true},
+	{"12345", 12345, true},
+	{"-12345", -12345, true},
+	{"012345", 12345, true},
+	{"-012345", -12345, true},
+	{"12345x", 0, false},
+	{"-12345x", 0, false},
+	{"98765432100", 98765432100, true},
+	{"-98765432100", -98765432100, true},
+	{"20496382327982653440", 0, false},
+	{"-20496382327982653440", 0, false},
+	{"9223372036854775807", 1<<63 - 1, true},
+	{"-9223372036854775807", -(1<<63 - 1), true},
+	{"9223372036854775808", 0, false},
+	{"-9223372036854775808", -1 << 63, true},
+	{"9223372036854775809", 0, false},
+	{"-9223372036854775809", 0, false},
+}
+
+func TestAtoi(t *testing.T) {
+	switch intSize {
+	case 32:
+		for i := range atoi32tests {
+			test := &atoi32tests[i]
+			out, ok := runtime.Atoi(test.in)
+			if test.out != int32(out) || test.ok != ok {
+				t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)",
+					test.in, out, ok, test.out, test.ok)
+			}
+		}
+	case 64:
+		for i := range atoi64tests {
+			test := &atoi64tests[i]
+			out, ok := runtime.Atoi(test.in)
+			if test.out != int64(out) || test.ok != ok {
+				t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)",
+					test.in, out, ok, test.out, test.ok)
+			}
+		}
+	}
+}
+
+type atoi32Test struct {
+	in  string
+	out int32
+	ok  bool
+}
+
+var atoi32tests = []atoi32Test{
+	{"", 0, false},
+	{"0", 0, true},
+	{"-0", 0, true},
+	{"1", 1, true},
+	{"-1", -1, true},
+	{"12345", 12345, true},
+	{"-12345", -12345, true},
+	{"012345", 12345, true},
+	{"-012345", -12345, true},
+	{"12345x", 0, false},
+	{"-12345x", 0, false},
+	{"987654321", 987654321, true},
+	{"-987654321", -987654321, true},
+	{"2147483647", 1<<31 - 1, true},
+	{"-2147483647", -(1<<31 - 1), true},
+	{"2147483648", 0, false},
+	{"-2147483648", -1 << 31, true},
+	{"2147483649", 0, false},
+	{"-2147483649", 0, false},
+}
+
+func TestAtoi32(t *testing.T) {
+	for i := range atoi32tests {
+		test := &atoi32tests[i]
+		out, ok := runtime.Atoi32(test.in)
+		if test.out != out || test.ok != ok {
+			t.Errorf("atoi32(%q) = (%v, %v) want (%v, %v)",
+				test.in, out, ok, test.out, test.ok)
+		}
+	}
+}
diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go
index a594c1b..107f260 100644
--- a/src/runtime/stubs.go
+++ b/src/runtime/stubs.go
@@ -4,7 +4,10 @@
 
 package runtime
 
-import "unsafe"
+import (
+	"runtime/internal/sys"
+	"unsafe"
+)
 
 // Should be a built-in for unsafe.Pointer?
 //go:nosplit
@@ -57,14 +60,24 @@ func badsystemstack() {
 	throw("systemstack called from unexpected goroutine")
 }
 
-// memclr clears n bytes starting at ptr.
+// memclrNoHeapPointers clears n bytes starting at ptr.
+//
+// Usually you should use typedmemclr. memclrNoHeapPointers should be
+// used only when the caller knows that *ptr contains no heap pointers
+// because either:
+//
+// 1. *ptr is initialized memory and its type is pointer-free.
+//
+// 2. *ptr is uninitialized memory (e.g., memory that's being reused
+//    for a new allocation) and hence contains only "junk".
+//
 // in memclr_*.s
 //go:noescape
-func memclr(ptr unsafe.Pointer, n uintptr)
+func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
 
-//go:linkname reflect_memclr reflect.memclr
-func reflect_memclr(ptr unsafe.Pointer, n uintptr) {
-	memclr(ptr, n)
+//go:linkname reflect_memclrNoHeapPointers reflect.memclrNoHeapPointers
+func reflect_memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) {
+	memclrNoHeapPointers(ptr, n)
 }
 
 // memmove copies n bytes from "from" to "to".
@@ -81,7 +94,10 @@ func reflect_memmove(to, from unsafe.Pointer, n uintptr) {
 var hashLoad = loadFactor
 
 // in asm_*.s
-func fastrand1() uint32
+func fastrand() uint32
+
+//go:linkname sync_fastrand sync.fastrand
+func sync_fastrand() uint32 { return fastrand() }
 
 // in asm_*.s
 //go:noescape
@@ -90,7 +106,7 @@ func memequal(a, b unsafe.Pointer, size uintptr) bool
 // noescape hides a pointer from escape analysis.  noescape is
 // the identity function but escape analysis doesn't think the
 // output depends on the input.  noescape is inlined and currently
-// compiles down to a single xor instruction.
+// compiles down to zero instructions.
 // USE CAREFULLY!
 //go:nosplit
 func noescape(p unsafe.Pointer) unsafe.Pointer {
@@ -196,8 +212,10 @@ func setcallerpc(argp unsafe.Pointer, pc uintptr)
 //go:noescape
 func getcallerpc(argp unsafe.Pointer) uintptr
 
-//go:noescape
-func getcallersp(argp unsafe.Pointer) uintptr
+//go:nosplit
+func getcallersp(argp unsafe.Pointer) uintptr {
+	return uintptr(argp) - sys.MinFrameSize
+}
 
 //go:noescape
 func asmcgocall(fn, arg unsafe.Pointer) int32
@@ -206,6 +224,7 @@ func asmcgocall(fn, arg unsafe.Pointer) int32
 const _NoArgs = ^uintptr(0)
 
 func morestack()
+func morestack_noctxt()
 func rt0_go()
 
 // stackBarrier records that the stack has been unwound past a certain
@@ -227,32 +246,32 @@ func time_now() (sec int64, nsec int32)
 
 // in asm_*.s
 // not called directly; definitions here supply type information for traceback.
-func call32(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call64(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call128(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call256(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call512(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call1024(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call2048(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call4096(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call8192(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call16384(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call32768(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call65536(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call131072(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call262144(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call524288(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call1048576(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call2097152(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call4194304(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call8388608(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call16777216(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call33554432(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call67108864(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call134217728(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call268435456(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call536870912(fn, arg unsafe.Pointer, n, retoffset uint32)
-func call1073741824(fn, arg unsafe.Pointer, n, retoffset uint32)
+func call32(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call64(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call128(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call256(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call512(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call1024(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call2048(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call4096(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call8192(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call16384(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call32768(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call65536(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call131072(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call262144(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call524288(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call1048576(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call2097152(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call4194304(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call8388608(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call16777216(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call33554432(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call67108864(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call134217728(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call268435456(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call536870912(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
+func call1073741824(typ, fn, arg unsafe.Pointer, n, retoffset uint32)
 
 func systemstack_switch()
 
@@ -273,3 +292,6 @@ func round(n, a uintptr) uintptr {
 
 // checkASM returns whether assembly runtime checks have passed.
 func checkASM() bool
+
+func memequal_varlen(a, b unsafe.Pointer) bool
+func eqstring(s1, s2 string) bool
diff --git a/src/runtime/stubs32.go b/src/runtime/stubs32.go
index cd442e9..149560f 100644
--- a/src/runtime/stubs32.go
+++ b/src/runtime/stubs32.go
@@ -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 386 arm amd64p32
+// +build 386 arm amd64p32 mips mipsle
 
 package runtime
 
diff --git a/src/runtime/stubs_asm.go b/src/runtime/stubs_asm.go
new file mode 100644
index 0000000..fd2eed9
--- /dev/null
+++ b/src/runtime/stubs_asm.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !mips64,!mips64le
+
+// Declarations for routines that are implemented in noasm.go.
+
+package runtime
+
+func cmpstring(s1, s2 string) int
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 4f6fae2..8a5b0df 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -5,6 +5,7 @@
 package runtime
 
 import (
+	"runtime/internal/atomic"
 	"runtime/internal/sys"
 	"unsafe"
 )
@@ -195,8 +196,14 @@ type moduledata struct {
 	end, gcdata, gcbss    uintptr
 	types, etypes         uintptr
 
-	typelinks []int32 // offsets from types
-	itablinks []*itab
+	textsectmap []textsect
+	typelinks   []int32 // offsets from types
+	itablinks   []*itab
+
+	ptab []ptabEntry
+
+	pluginpath string
+	pkghashes  []modulehash
 
 	modulename   string
 	modulehashes []modulehash
@@ -208,24 +215,92 @@ type moduledata struct {
 	next *moduledata
 }
 
+// A modulehash is used to compare the ABI of a new module or a
+// package in a new module with the loaded program.
+//
 // For each shared library a module links against, the linker creates an entry in the
 // moduledata.modulehashes slice containing the name of the module, the abi hash seen
 // at link time and a pointer to the runtime abi hash. These are checked in
 // moduledataverify1 below.
+//
+// For each loaded plugin, the the pkghashes slice has a modulehash of the
+// newly loaded package that can be used to check the plugin's version of
+// a package against any previously loaded version of the package.
+// This is done in plugin.lastmoduleinit.
 type modulehash struct {
 	modulename   string
 	linktimehash string
 	runtimehash  *string
 }
 
+// pinnedTypemaps are the map[typeOff]*_type from the moduledata objects.
+//
+// These typemap objects are allocated at run time on the heap, but the
+// only direct reference to them is in the moduledata, created by the
+// linker and marked SNOPTRDATA so it is ignored by the GC.
+//
+// To make sure the map isn't collected, we keep a second reference here.
+var pinnedTypemaps []map[typeOff]*_type
+
 var firstmoduledata moduledata  // linker symbol
 var lastmoduledatap *moduledata // linker symbol
+var modulesSlice unsafe.Pointer // see activeModules
+
+// activeModules returns a slice of active modules.
+//
+// A module is active once its gcdatamask and gcbssmask have been
+// assembled and it is usable by the GC.
+func activeModules() []*moduledata {
+	p := (*[]*moduledata)(atomic.Loadp(unsafe.Pointer(&modulesSlice)))
+	if p == nil {
+		return nil
+	}
+	return *p
+}
+
+// modulesinit creates the active modules slice out of all loaded modules.
+//
+// When a module is first loaded by the dynamic linker, an .init_array
+// function (written by cmd/link) is invoked to call addmoduledata,
+// appending to the module to the linked list that starts with
+// firstmoduledata.
+//
+// There are two times this can happen in the lifecycle of a Go
+// program. First, if compiled with -linkshared, a number of modules
+// built with -buildmode=shared can be loaded at program initialization.
+// Second, a Go program can load a module while running that was built
+// with -buildmode=plugin.
+//
+// After loading, this function is called which initializes the
+// moduledata so it is usable by the GC and creates a new activeModules
+// list.
+//
+// Only one goroutine may call modulesinit at a time.
+func modulesinit() {
+	modules := new([]*moduledata)
+	for md := &firstmoduledata; md != nil; md = md.next {
+		*modules = append(*modules, md)
+		if md.gcdatamask == (bitvector{}) {
+			md.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(md.gcdata)), md.edata-md.data)
+			md.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(md.gcbss)), md.ebss-md.bss)
+		}
+	}
+	atomicstorep(unsafe.Pointer(&modulesSlice), unsafe.Pointer(modules))
+}
 
 type functab struct {
 	entry   uintptr
 	funcoff uintptr
 }
 
+// Mapping information for secondary text sections
+
+type textsect struct {
+	vaddr    uintptr // prelinked section vaddr
+	length   uintptr // section length
+	baseaddr uintptr // relocated section address
+}
+
 const minfunc = 16                 // minimum function size
 const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table
 
@@ -367,13 +442,31 @@ func findfunc(pc uintptr) *_func {
 
 	ffb := (*findfuncbucket)(add(unsafe.Pointer(datap.findfunctab), b*unsafe.Sizeof(findfuncbucket{})))
 	idx := ffb.idx + uint32(ffb.subbuckets[i])
-	if pc < datap.ftab[idx].entry {
-		throw("findfunc: bad findfunctab entry")
+
+	// If the idx is beyond the end of the ftab, set it to the end of the table and search backward.
+	// This situation can occur if multiple text sections are generated to handle large text sections
+	// and the linker has inserted jump tables between them.
+
+	if idx >= uint32(len(datap.ftab)) {
+		idx = uint32(len(datap.ftab) - 1)
 	}
+	if pc < datap.ftab[idx].entry {
+
+		// With multiple text sections, the idx might reference a function address that
+		// is higher than the pc being searched, so search backward until the matching address is found.
 
-	// linear search to find func with pc >= entry.
-	for datap.ftab[idx+1].entry <= pc {
-		idx++
+		for datap.ftab[idx].entry > pc && idx > 0 {
+			idx--
+		}
+		if idx == 0 {
+			throw("findfunc: bad findfunctab entry idx")
+		}
+	} else {
+
+		// linear search to find func with pc >= entry.
+		for datap.ftab[idx+1].entry <= pc {
+			idx++
+		}
 	}
 	return (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[idx].funcoff]))
 }
@@ -437,7 +530,7 @@ func pcvalue(f *_func, off int32, targetpc uintptr, cache *pcvalueCache, strict
 			// a recursive stack's cycle is slightly
 			// larger than the cache.
 			if cache != nil {
-				ci := fastrand1() % uint32(len(cache.entries))
+				ci := fastrand() % uint32(len(cache.entries))
 				cache.entries[ci] = pcvalueCacheEnt{
 					targetpc: targetpc,
 					off:      off,
@@ -581,5 +674,5 @@ func stackmapdata(stkmap *stackmap, n int32) bitvector {
 	if n < 0 || n >= stkmap.n {
 		throw("stackmapdata: index out of range")
 	}
-	return bitvector{stkmap.nbit, (*byte)(add(unsafe.Pointer(&stkmap.bytedata), uintptr(n*((stkmap.nbit+31)/32*4))))}
+	return bitvector{stkmap.nbit, (*byte)(add(unsafe.Pointer(&stkmap.bytedata), uintptr(n*((stkmap.nbit+7)/8))))}
 }
diff --git a/src/runtime/sys_arm.go b/src/runtime/sys_arm.go
index d2e6914..730b9c9 100644
--- a/src/runtime/sys_arm.go
+++ b/src/runtime/sys_arm.go
@@ -17,22 +17,5 @@ func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
 	buf.ctxt = ctxt
 }
 
-// Called to rewind context saved during morestack back to beginning of function.
-// To help us, the linker emits a jmp back to the beginning right after the
-// call to morestack. We just have to decode and apply that jump.
-func rewindmorestack(buf *gobuf) {
-	var inst uint32
-	if buf.pc&3 == 0 && buf.pc != 0 {
-		inst = *(*uint32)(unsafe.Pointer(buf.pc))
-		if inst>>24 == 0x9a || inst>>24 == 0xea {
-			buf.pc += uintptr(int32(inst<<8)>>6) + 8
-			return
-		}
-	}
-
-	print("runtime: pc=", hex(buf.pc), " ", hex(inst), "\n")
-	throw("runtime: misuse of rewindmorestack")
-}
-
 // for testing
 func usplit(x uint32) (q, r uint32)
diff --git a/src/runtime/sys_arm64.go b/src/runtime/sys_arm64.go
index dee23ef..230241d 100644
--- a/src/runtime/sys_arm64.go
+++ b/src/runtime/sys_arm64.go
@@ -16,21 +16,3 @@ func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
 	buf.pc = uintptr(fn)
 	buf.ctxt = ctxt
 }
-
-// Called to rewind context saved during morestack back to beginning of function.
-// To help us, the linker emits a jmp back to the beginning right after the
-// call to morestack. We just have to decode and apply that jump.
-func rewindmorestack(buf *gobuf) {
-	var inst uint32
-	if buf.pc&3 == 0 && buf.pc != 0 {
-		inst = *(*uint32)(unsafe.Pointer(buf.pc))
-		// section C3.2.6 Unconditional branch (immediate)
-		if inst>>26 == 0x05 {
-			buf.pc += uintptr(int32(inst<<6) >> 4)
-			return
-		}
-	}
-
-	print("runtime: pc=", hex(buf.pc), " ", hex(inst), "\n")
-	throw("runtime: misuse of rewindmorestack")
-}
diff --git a/src/runtime/sys_darwin_386.s b/src/runtime/sys_darwin_386.s
index b5e65e6..200961f 100644
--- a/src/runtime/sys_darwin_386.s
+++ b/src/runtime/sys_darwin_386.s
@@ -200,7 +200,7 @@ systime:
 	MOVL	AX, 4(SP)
 	MOVL	$0, 8(SP)	// time zone pointer
 	MOVL	$0, 12(SP)	// required as of Sierra; Issue 16570
-	MOVL	$116, AX
+	MOVL	$116, AX // SYS_GETTIMEOFDAY
 	INT	$0x80
 	CMPL	AX, $0
 	JNE	inreg
@@ -254,52 +254,37 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-16
 	MOVL	info+8(FP), CX
 	MOVL	ctx+12(FP), DX
 	MOVL	SP, SI
-	SUBL	$32, SP		// align stack; handler might be C code
-	ANDL	$~15, SP
+	SUBL	$32, SP
+	ANDL	$~15, SP	// align stack: handler might be a C function
 	MOVL	BX, 0(SP)
 	MOVL	CX, 4(SP)
 	MOVL	DX, 8(SP)
-	MOVL	SI, 12(SP)
+	MOVL	SI, 12(SP)	// save SI: handler might be a Go function
 	CALL	AX
 	MOVL	12(SP), AX
 	MOVL	AX, SP
 	RET
 
-TEXT runtime·sigreturn(SB),NOSPLIT,$12-8
-	MOVL	ctx+0(FP), CX
-	MOVL	infostyle+4(FP), BX
-	MOVL	$0, 0(SP)	// "caller PC" - ignored
-	MOVL	CX, 4(SP)
-	MOVL	BX, 8(SP)
-	MOVL	$184, AX	// sigreturn(ucontext, infostyle)
-	INT	$0x80
-	MOVL	$0xf1, 0xf1  // crash
-	RET
-
 // Sigtramp's job is to call the actual signal handler.
 // It is called with the following arguments on the stack:
 //	0(SP)	"return address" - ignored
 //	4(SP)	actual handler
-//	8(SP)	signal number
-//	12(SP)	siginfo style
+//	8(SP)	siginfo style
+//	12(SP)	signal number
 //	16(SP)	siginfo
 //	20(SP)	context
 TEXT runtime·sigtramp(SB),NOSPLIT,$20
-	MOVL	fn+0(FP), BX
+	MOVL	sig+8(FP), BX
 	MOVL	BX, 0(SP)
-	MOVL	style+4(FP), BX
+	MOVL	info+12(FP), BX
 	MOVL	BX, 4(SP)
-	MOVL	sig+8(FP), BX
+	MOVL	ctx+16(FP), BX
 	MOVL	BX, 8(SP)
-	MOVL	info+12(FP), BX
-	MOVL	BX, 12(SP)
-	MOVL	context+16(FP), BX
-	MOVL	BX, 16(SP)
 	CALL	runtime·sigtrampgo(SB)
 
 	// call sigreturn
-	MOVL	context+16(FP), CX
-	MOVL	style+4(FP), BX
+	MOVL	ctx+16(FP), CX
+	MOVL	infostyle+4(FP), BX
 	MOVL	$0, 0(SP)	// "caller PC" - ignored
 	MOVL	CX, 4(SP)
 	MOVL	BX, 8(SP)
diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s
index ea2cc06..96fa5b9 100644
--- a/src/runtime/sys_darwin_amd64.s
+++ b/src/runtime/sys_darwin_amd64.s
@@ -158,7 +158,7 @@ systime:
 	MOVQ	SP, DI
 	MOVQ	$0, SI
 	MOVQ	$0, DX  // required as of Sierra; Issue 16570
-	MOVL	$(0x2000000+116), AX
+	MOVL	$(0x2000000+116), AX // gettimeofday
 	SYSCALL
 	CMPQ	AX, $0
 	JNE	inreg
@@ -197,7 +197,7 @@ TEXT time·now(SB),NOSPLIT,$0-12
 	RET
 
 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
-	MOVL	sig+0(FP), DI
+	MOVL	how+0(FP), DI
 	MOVQ	new+8(FP), SI
 	MOVQ	old+16(FP), DX
 	MOVL	$(0x2000000+329), AX  // pthread_sigmask (on OS X, sigprocmask==entire process)
@@ -219,33 +219,30 @@ TEXT runtime·sigaction(SB),NOSPLIT,$0-24
 	RET
 
 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
-	MOVQ fn+0(FP),    AX
-	MOVL sig+8(FP),   DI
-	MOVQ info+16(FP), SI
-	MOVQ ctx+24(FP),  DX
-	MOVQ SP, BP
-	SUBQ $64, SP
-	ANDQ $~15, SP     // alignment for x86_64 ABI
-	CALL AX
-	MOVQ BP, SP
+	MOVQ	fn+0(FP),    AX
+	MOVL	sig+8(FP),   DI
+	MOVQ	info+16(FP), SI
+	MOVQ	ctx+24(FP),  DX
+	PUSHQ	BP
+	MOVQ	SP, BP
+	ANDQ	$~15, SP     // alignment for x86_64 ABI
+	CALL	AX
+	MOVQ	BP, SP
+	POPQ	BP
 	RET
 
-TEXT runtime·sigreturn(SB),NOSPLIT,$0-12
-	MOVQ ctx+0(FP),        DI
-	MOVL infostyle+8(FP),  SI
-	MOVL $(0x2000000+184), AX
-	SYSCALL
-	INT $3 // not reached
-
 TEXT runtime·sigtramp(SB),NOSPLIT,$32
-	MOVQ DI,  0(SP) // fn
-	MOVL SI,  8(SP) // infostyle
-	MOVL DX, 12(SP) // sig
-	MOVQ CX, 16(SP) // info
-	MOVQ R8, 24(SP) // ctx
+	MOVL SI, 24(SP) // save infostyle for sigreturn below
+	MOVL DX, 0(SP)  // sig
+	MOVQ CX, 8(SP)  // info
+	MOVQ R8, 16(SP) // ctx
 	MOVQ $runtime·sigtrampgo(SB), AX
 	CALL AX
-	INT $3 // not reached (see issue 16453)
+	MOVQ 16(SP), DI // ctx
+	MOVL 24(SP), SI // infostyle
+	MOVL $(0x2000000+184), AX
+	SYSCALL
+	INT $3 // not reached
 
 TEXT runtime·mmap(SB),NOSPLIT,$0
 	MOVQ	addr+0(FP), DI		// arg 1 addr
diff --git a/src/runtime/sys_darwin_arm.s b/src/runtime/sys_darwin_arm.s
index 52f6a94..2c03c91 100644
--- a/src/runtime/sys_darwin_arm.s
+++ b/src/runtime/sys_darwin_arm.s
@@ -106,7 +106,7 @@ TEXT runtime·raiseproc(SB),NOSPLIT,$24
 	MOVW	$SYS_getpid, R12
 	SWI	$0x80
 	// arg 1 pid already in R0 from getpid
-	MOVW	unnamed+0(FP), R1	// arg 2 - signal
+	MOVW	sig+0(FP), R1	// arg 2 - signal
 	MOVW	$1, R2	// arg 3 - posix
 	MOVW	$SYS_kill, R12
 	SWI $0x80
@@ -286,7 +286,7 @@ ret:
 	B	runtime·exit(SB)
 
 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
-	MOVW	sig+0(FP), R0
+	MOVW	how+0(FP), R0
 	MOVW	new+4(FP), R1
 	MOVW	old+8(FP), R2
 	MOVW	$SYS_pthread_sigmask, R12
diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s
index 8e6b5b1..c02d000 100644
--- a/src/runtime/sys_darwin_arm64.s
+++ b/src/runtime/sys_darwin_arm64.s
@@ -271,7 +271,7 @@ ret:
 	B	runtime·exit(SB)
 
 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
-	MOVW	sig+0(FP), R0
+	MOVW	how+0(FP), R0
 	MOVD	new+8(FP), R1
 	MOVD	old+16(FP), R2
 	MOVW	$SYS_pthread_sigmask, R16
diff --git a/src/runtime/sys_dragonfly_amd64.s b/src/runtime/sys_dragonfly_amd64.s
index be964cb..88c7f9d 100644
--- a/src/runtime/sys_dragonfly_amd64.s
+++ b/src/runtime/sys_dragonfly_amd64.s
@@ -51,18 +51,6 @@ TEXT runtime·lwp_start(SB),NOSPLIT,$0
 	MOVQ	R13, g_m(DI)
 	MOVQ	DI, g(CX)
 
-	// On DragonFly, a new thread inherits the signal stack of the
-	// creating thread. That confuses minit, so we remove that
-	// signal stack here before calling the regular mstart. It's
-	// a bit baroque to remove a signal stack here only to add one
-	// in minit, but it's a simple change that keeps DragonFly
-	// working like other OS's. At this point all signals are
-	// blocked, so there is no race.
-	SUBQ	$8, SP
-	MOVQ	$0, 0(SP)
-	CALL	runtime·signalstack(SB)
-	ADDQ	$8, SP
-
 	CALL	runtime·stackcheck(SB)
 	CALL	runtime·mstart(SB)
 
@@ -162,7 +150,7 @@ TEXT runtime·setitimer(SB), NOSPLIT, $-8
 
 // func now() (sec int64, nsec int32)
 TEXT time·now(SB), NOSPLIT, $32
-	MOVL	$232, AX
+	MOVL	$232, AX // clock_gettime
 	MOVQ	$0, DI  	// CLOCK_REALTIME
 	LEAQ	8(SP), SI
 	SYSCALL
@@ -200,11 +188,16 @@ TEXT runtime·sigaction(SB),NOSPLIT,$-8
 	RET
 
 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
-	MOVL	sig+8(FP), DI
+	MOVQ	fn+0(FP),    AX
+	MOVL	sig+8(FP),   DI
 	MOVQ	info+16(FP), SI
-	MOVQ	ctx+24(FP), DX
-	MOVQ	fn+0(FP), AX
+	MOVQ	ctx+24(FP),  DX
+	PUSHQ	BP
+	MOVQ	SP, BP
+	ANDQ	$~15, SP     // alignment for x86_64 ABI
 	CALL	AX
+	MOVQ	BP, SP
+	POPQ	BP
 	RET
 
 TEXT runtime·sigtramp(SB),NOSPLIT,$24
@@ -249,8 +242,8 @@ TEXT runtime·madvise(SB),NOSPLIT,$0
 	RET
 	
 TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
-	MOVQ	new+8(SP), DI
-	MOVQ	old+16(SP), SI
+	MOVQ	new+0(FP), DI
+	MOVQ	old+8(FP), SI
 	MOVQ	$53, AX
 	SYSCALL
 	JCC	2(PC)
@@ -333,11 +326,11 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
 
 // int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
 TEXT runtime·kevent(SB),NOSPLIT,$0
-	MOVL	fd+0(FP), DI
-	MOVQ	ev1+8(FP), SI
-	MOVL	nev1+16(FP), DX
-	MOVQ	ev2+24(FP), R10
-	MOVL	nev2+32(FP), R8
+	MOVL	kq+0(FP), DI
+	MOVQ	ch+8(FP), SI
+	MOVL	nch+16(FP), DX
+	MOVQ	ev+24(FP), R10
+	MOVL	nev+32(FP), R8
 	MOVQ	ts+40(FP), R9
 	MOVL	$363, AX
 	SYSCALL
diff --git a/src/runtime/sys_freebsd_386.s b/src/runtime/sys_freebsd_386.s
index b37abce..8b6ee1f 100644
--- a/src/runtime/sys_freebsd_386.s
+++ b/src/runtime/sys_freebsd_386.s
@@ -161,7 +161,7 @@ TEXT runtime·setitimer(SB), NOSPLIT, $-4
 
 // func now() (sec int64, nsec int32)
 TEXT time·now(SB), NOSPLIT, $32
-	MOVL	$232, AX
+	MOVL	$232, AX // clock_gettime
 	LEAL	12(SP), BX
 	MOVL	$0, 4(SP)	// CLOCK_REALTIME
 	MOVL	BX, 8(SP)
@@ -208,14 +208,20 @@ TEXT runtime·sigaction(SB),NOSPLIT,$-4
 	RET
 
 TEXT runtime·sigfwd(SB),NOSPLIT,$12-16
-	MOVL	sig+4(FP), AX
-	MOVL	AX, 0(SP)
-	MOVL	info+8(FP), AX
-	MOVL	AX, 4(SP)
-	MOVL	ctx+12(FP), AX
-	MOVL	AX, 8(SP)
 	MOVL	fn+0(FP), AX
+	MOVL	sig+4(FP), BX
+	MOVL	info+8(FP), CX
+	MOVL	ctx+12(FP), DX
+	MOVL	SP, SI
+	SUBL	$32, SP
+	ANDL	$~15, SP	// align stack: handler might be a C function
+	MOVL	BX, 0(SP)
+	MOVL	CX, 4(SP)
+	MOVL	DX, 8(SP)
+	MOVL	SI, 12(SP)	// save SI: handler might be a Go function
 	CALL	AX
+	MOVL	12(SP), AX
+	MOVL	AX, SP
 	RET
 
 TEXT runtime·sigtramp(SB),NOSPLIT,$12
diff --git a/src/runtime/sys_freebsd_amd64.s b/src/runtime/sys_freebsd_amd64.s
index 277e7f8..19007dc 100644
--- a/src/runtime/sys_freebsd_amd64.s
+++ b/src/runtime/sys_freebsd_amd64.s
@@ -144,7 +144,7 @@ TEXT runtime·setitimer(SB), NOSPLIT, $-8
 
 // func now() (sec int64, nsec int32)
 TEXT time·now(SB), NOSPLIT, $32
-	MOVL	$232, AX
+	MOVL	$232, AX // clock_gettime
 	MOVQ	$0, DI		// CLOCK_REALTIME
 	LEAQ	8(SP), SI
 	SYSCALL
@@ -184,11 +184,16 @@ TEXT runtime·sigaction(SB),NOSPLIT,$-8
 	RET
 
 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
-	MOVL	sig+8(FP), DI
+	MOVQ	fn+0(FP),    AX
+	MOVL	sig+8(FP),   DI
 	MOVQ	info+16(FP), SI
-	MOVQ	ctx+24(FP), DX
-	MOVQ	fn+0(FP), AX
+	MOVQ	ctx+24(FP),  DX
+	PUSHQ	BP
+	MOVQ	SP, BP
+	ANDQ	$~15, SP     // alignment for x86_64 ABI
 	CALL	AX
+	MOVQ	BP, SP
+	POPQ	BP
 	RET
 
 TEXT runtime·sigtramp(SB),NOSPLIT,$24
@@ -229,8 +234,8 @@ TEXT runtime·madvise(SB),NOSPLIT,$0
 	RET
 	
 TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
-	MOVQ	new+8(SP), DI
-	MOVQ	old+16(SP), SI
+	MOVQ	new+0(FP), DI
+	MOVQ	old+8(FP), SI
 	MOVQ	$53, AX
 	SYSCALL
 	JCC	2(PC)
@@ -311,11 +316,11 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
 
 // int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
 TEXT runtime·kevent(SB),NOSPLIT,$0
-	MOVL	fd+0(FP), DI
-	MOVQ	ev1+8(FP), SI
-	MOVL	nev1+16(FP), DX
-	MOVQ	ev2+24(FP), R10
-	MOVL	nev2+32(FP), R8
+	MOVL	kq+0(FP), DI
+	MOVQ	ch+8(FP), SI
+	MOVL	nch+16(FP), DX
+	MOVQ	ev+24(FP), R10
+	MOVL	nev+32(FP), R8
 	MOVQ	ts+40(FP), R9
 	MOVL	$363, AX
 	SYSCALL
diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s
index 4fe07e0..1d798c7 100644
--- a/src/runtime/sys_linux_386.s
+++ b/src/runtime/sys_linux_386.s
@@ -191,7 +191,7 @@ TEXT runtime·nanotime(SB), NOSPLIT, $32
 
 TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
 	MOVL	$175, AX		// syscall entry
-	MOVL	sig+0(FP), BX
+	MOVL	how+0(FP), BX
 	MOVL	new+4(FP), CX
 	MOVL	old+8(FP), DX
 	MOVL	size+12(FP), SI
@@ -212,14 +212,20 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0
 	RET
 
 TEXT runtime·sigfwd(SB),NOSPLIT,$12-16
-	MOVL	sig+4(FP), AX
-	MOVL	AX, 0(SP)
-	MOVL	info+8(FP), AX
-	MOVL	AX, 4(SP)
-	MOVL	ctx+12(FP), AX
-	MOVL	AX, 8(SP)
 	MOVL	fn+0(FP), AX
+	MOVL	sig+4(FP), BX
+	MOVL	info+8(FP), CX
+	MOVL	ctx+12(FP), DX
+	MOVL	SP, SI
+	SUBL	$32, SP
+	ANDL	$-15, SP	// align stack: handler might be a C function
+	MOVL	BX, 0(SP)
+	MOVL	CX, 4(SP)
+	MOVL	DX, 8(SP)
+	MOVL	SI, 12(SP)	// save SI: handler might be a Go function
 	CALL	AX
+	MOVL	12(SP), AX
+	MOVL	AX, SP
 	RET
 
 TEXT runtime·sigtramp(SB),NOSPLIT,$12
@@ -227,7 +233,7 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$12
 	MOVL	BX, 0(SP)
 	MOVL	info+4(FP), BX
 	MOVL	BX, 4(SP)
-	MOVL	context+8(FP), BX
+	MOVL	ctx+8(FP), BX
 	MOVL	BX, 8(SP)
 	CALL	runtime·sigtrampgo(SB)
 	RET
@@ -297,15 +303,15 @@ TEXT runtime·futex(SB),NOSPLIT,$0
 TEXT runtime·clone(SB),NOSPLIT,$0
 	MOVL	$120, AX	// clone
 	MOVL	flags+0(FP), BX
-	MOVL	stack+4(FP), CX
+	MOVL	stk+4(FP), CX
 	MOVL	$0, DX	// parent tid ptr
 	MOVL	$0, DI	// child tid ptr
 
 	// Copy mp, gp, fn off parent stack for use by child.
 	SUBL	$16, CX
-	MOVL	mm+8(FP), SI
+	MOVL	mp+8(FP), SI
 	MOVL	SI, 0(CX)
-	MOVL	gg+12(FP), SI
+	MOVL	gp+12(FP), SI
 	MOVL	SI, 4(CX)
 	MOVL	fn+16(FP), SI
 	MOVL	SI, 8(CX)
@@ -379,8 +385,8 @@ nog:
 
 TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
 	MOVL	$186, AX	// sigaltstack
-	MOVL	new+4(SP), BX
-	MOVL	old+8(SP), CX
+	MOVL	new+0(FP), BX
+	MOVL	old+4(FP), CX
 	INVOKE_SYSCALL
 	CMPL	AX, $0xfffff001
 	JLS	2(PC)
diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s
index 8a8f3cc..832b98b 100644
--- a/src/runtime/sys_linux_amd64.s
+++ b/src/runtime/sys_linux_amd64.s
@@ -197,7 +197,7 @@ fallback:
 	RET
 
 TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0-28
-	MOVL	sig+0(FP), DI
+	MOVL	how+0(FP), DI
 	MOVQ	new+8(FP), SI
 	MOVQ	old+16(FP), DX
 	MOVL	size+24(FP), R10
@@ -208,7 +208,7 @@ TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0-28
 	MOVL	$0xf1, 0xf1  // crash
 	RET
 
-TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36
+TEXT runtime·sysSigaction(SB),NOSPLIT,$0-36
 	MOVQ	sig+0(FP), DI
 	MOVQ	new+8(FP), SI
 	MOVQ	old+16(FP), DX
@@ -218,12 +218,30 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36
 	MOVL	AX, ret+32(FP)
 	RET
 
+// Call the function stored in _cgo_sigaction using the GCC calling convention.
+TEXT runtime·callCgoSigaction(SB),NOSPLIT,$16
+	MOVQ	sig+0(FP), DI
+	MOVQ	new+8(FP), SI
+	MOVQ	old+16(FP), DX
+	MOVQ	_cgo_sigaction(SB), AX
+	MOVQ	SP, BX	// callee-saved
+	ANDQ	$~15, SP	// alignment as per amd64 psABI
+	CALL	AX
+	MOVQ	BX, SP
+	MOVL	AX, ret+24(FP)
+	RET
+
 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
-	MOVL	sig+8(FP), DI
+	MOVQ	fn+0(FP),    AX
+	MOVL	sig+8(FP),   DI
 	MOVQ	info+16(FP), SI
-	MOVQ	ctx+24(FP), DX
-	MOVQ	fn+0(FP), AX
+	MOVQ	ctx+24(FP),  DX
+	PUSHQ	BP
+	MOVQ	SP, BP
+	ANDQ	$~15, SP     // alignment for x86_64 ABI
 	CALL	AX
+	MOVQ	BP, SP
+	POPQ	BP
 	RET
 
 TEXT runtime·sigtramp(SB),NOSPLIT,$24
@@ -388,10 +406,10 @@ TEXT runtime·futex(SB),NOSPLIT,$0
 	MOVL	AX, ret+40(FP)
 	RET
 
-// int32 clone(int32 flags, void *stack, M *mp, G *gp, void (*fn)(void));
+// int32 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void));
 TEXT runtime·clone(SB),NOSPLIT,$0
 	MOVL	flags+0(FP), DI
-	MOVQ	stack+8(FP), SI
+	MOVQ	stk+8(FP), SI
 	MOVQ	$0, DX
 	MOVQ	$0, R10
 
@@ -445,8 +463,8 @@ nog:
 	JMP	-3(PC)	// keep exiting
 
 TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
-	MOVQ	new+8(SP), DI
-	MOVQ	old+16(SP), SI
+	MOVQ	new+0(FP), DI
+	MOVQ	old+8(FP), SI
 	MOVQ	$131, AX
 	SYSCALL
 	CMPQ	AX, $0xfffffffffffff001
@@ -548,7 +566,7 @@ TEXT runtime·access(SB),NOSPLIT,$0
 TEXT runtime·connect(SB),NOSPLIT,$0-28
 	MOVL	fd+0(FP), DI
 	MOVQ	addr+8(FP), SI
-	MOVL	addrlen+16(FP), DX
+	MOVL	len+16(FP), DX
 	MOVL	$42, AX  // syscall entry
 	SYSCALL
 	MOVL	AX, ret+24(FP)
@@ -557,8 +575,8 @@ TEXT runtime·connect(SB),NOSPLIT,$0-28
 // int socket(int domain, int type, int protocol)
 TEXT runtime·socket(SB),NOSPLIT,$0-20
 	MOVL	domain+0(FP), DI
-	MOVL	type+4(FP), SI
-	MOVL	protocol+8(FP), DX
+	MOVL	typ+4(FP), SI
+	MOVL	prot+8(FP), DX
 	MOVL	$41, AX  // syscall entry
 	SYSCALL
 	MOVL	AX, ret+16(FP)
diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s
index 5e5fcf0..666b879 100644
--- a/src/runtime/sys_linux_arm.s
+++ b/src/runtime/sys_linux_arm.s
@@ -235,13 +235,12 @@ TEXT runtime·nanotime(SB),NOSPLIT,$32
 // int32 futex(int32 *uaddr, int32 op, int32 val,
 //	struct timespec *timeout, int32 *uaddr2, int32 val2);
 TEXT runtime·futex(SB),NOSPLIT,$0
-	// TODO: Rewrite to use FP references. Vet complains.
-	MOVW	4(R13), R0
-	MOVW	8(R13), R1
-	MOVW	12(R13), R2
-	MOVW	16(R13), R3
-	MOVW	20(R13), R4
-	MOVW	24(R13), R5
+	MOVW    addr+0(FP), R0
+	MOVW    op+4(FP), R1
+	MOVW    val+8(FP), R2
+	MOVW    ts+12(FP), R3
+	MOVW    addr2+16(FP), R4
+	MOVW    val3+20(FP), R5
 	MOVW	$SYS_futex, R7
 	SWI	$0
 	MOVW	R0, ret+24(FP)
@@ -259,9 +258,9 @@ TEXT runtime·clone(SB),NOSPLIT,$0
 	// Copy mp, gp, fn off parent stack for use by child.
 	// TODO(kaib): figure out which registers are clobbered by clone and avoid stack copying
 	MOVW	$-16(R1), R1
-	MOVW	mm+8(FP), R6
+	MOVW	mp+8(FP), R6
 	MOVW	R6, 0(R1)
-	MOVW	gg+12(FP), R6
+	MOVW	gp+12(FP), R6
 	MOVW	R6, 4(R1)
 	MOVW	fn+16(FP), R6
 	MOVW	R6, 8(R1)
@@ -366,7 +365,7 @@ TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
 	B	(R11)
 
 TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
-	MOVW	sig+0(FP), R0
+	MOVW	how+0(FP), R0
 	MOVW	new+4(FP), R1
 	MOVW	old+8(FP), R2
 	MOVW	size+12(FP), R3
@@ -491,7 +490,7 @@ TEXT runtime·access(SB),NOSPLIT,$0
 TEXT runtime·connect(SB),NOSPLIT,$0
 	MOVW	fd+0(FP), R0
 	MOVW	addr+4(FP), R1
-	MOVW	addrlen+8(FP), R2
+	MOVW	len+8(FP), R2
 	MOVW	$SYS_connect, R7
 	SWI	$0
 	MOVW	R0, ret+12(FP)
@@ -499,8 +498,8 @@ TEXT runtime·connect(SB),NOSPLIT,$0
 
 TEXT runtime·socket(SB),NOSPLIT,$0
 	MOVW	domain+0(FP), R0
-	MOVW	type+4(FP), R1
-	MOVW	protocol+8(FP), R2
+	MOVW	typ+4(FP), R1
+	MOVW	prot+8(FP), R2
 	MOVW	$SYS_socket, R7
 	SWI	$0
 	MOVW	R0, ret+12(FP)
diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s
index 1bee847..1b91b44 100644
--- a/src/runtime/sys_linux_arm64.s
+++ b/src/runtime/sys_linux_arm64.s
@@ -184,14 +184,12 @@ TEXT runtime·mincore(SB),NOSPLIT,$-8-28
 
 // func now() (sec int64, nsec int32)
 TEXT time·now(SB),NOSPLIT,$24-12
-	MOVD	RSP, R0
-	MOVD	$0, R1
-	MOVD	$SYS_gettimeofday, R8
+	MOVW	$0, R0 // CLOCK_REALTIME
+	MOVD	RSP, R1
+	MOVD	$SYS_clock_gettime, R8
 	SVC
 	MOVD	0(RSP), R3	// sec
-	MOVD	8(RSP), R5	// usec
-	MOVD	$1000, R4
-	MUL	R4, R5
+	MOVD	8(RSP), R5	// nsec
 	MOVD	R3, sec+0(FP)
 	MOVW	R5, nsec+8(FP)
 	RET
@@ -212,7 +210,7 @@ TEXT runtime·nanotime(SB),NOSPLIT,$24-8
 	RET
 
 TEXT runtime·rtsigprocmask(SB),NOSPLIT,$-8-28
-	MOVW	sig+0(FP), R0
+	MOVW	how+0(FP), R0
 	MOVD	new+8(FP), R1
 	MOVD	old+16(FP), R2
 	MOVW	size+24(FP), R3
@@ -319,8 +317,8 @@ TEXT runtime·clone(SB),NOSPLIT,$-8
 	MOVD	stk+8(FP), R1
 
 	// Copy mp, gp, fn off parent stack for use by child.
-	MOVD	mm+16(FP), R10
-	MOVD	gg+24(FP), R11
+	MOVD	mp+16(FP), R10
+	MOVD	gp+24(FP), R11
 	MOVD	fn+32(FP), R12
 
 	MOVD	R10, -8(R1)
diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s
index d4a81ca..5a75bb8 100644
--- a/src/runtime/sys_linux_mips64x.s
+++ b/src/runtime/sys_linux_mips64x.s
@@ -174,15 +174,12 @@ TEXT runtime·mincore(SB),NOSPLIT,$-8-28
 
 // func now() (sec int64, nsec int32)
 TEXT time·now(SB),NOSPLIT,$16
-	MOVV	$0(R29), R4
-	MOVV	$0, R5
-	MOVV	$SYS_gettimeofday, R2
+	MOVW	$0, R4 // CLOCK_REALTIME
+	MOVV	$0(R29), R5
+	MOVV	$SYS_clock_gettime, R2
 	SYSCALL
 	MOVV	0(R29), R3	// sec
-	MOVV	8(R29), R5	// usec
-	MOVV	$1000, R4
-	MULVU	R4, R5
-	MOVV	LO, R5
+	MOVV	8(R29), R5	// nsec
 	MOVV	R3, sec+0(FP)
 	MOVW	R5, nsec+8(FP)
 	RET
@@ -204,7 +201,7 @@ TEXT runtime·nanotime(SB),NOSPLIT,$16
 	RET
 
 TEXT runtime·rtsigprocmask(SB),NOSPLIT,$-8-28
-	MOVW	sig+0(FP), R4
+	MOVW	how+0(FP), R4
 	MOVV	new+8(FP), R5
 	MOVV	old+16(FP), R6
 	MOVW	size+24(FP), R7
@@ -238,9 +235,6 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64
 	SRLV	$32, R31, RSB
 	SLLV	$32, RSB
 
-	// initialize essential registers (just in case)
-	JAL	runtime·reginit(SB)
-
 	// this might be called in external code context,
 	// where g is not set.
 	MOVB	runtime·iscgo(SB), R1
@@ -309,8 +303,8 @@ TEXT runtime·clone(SB),NOSPLIT,$-8
 
 	// Copy mp, gp, fn off parent stack for use by child.
 	// Careful: Linux system call clobbers ???.
-	MOVV	mm+16(FP), R16
-	MOVV	gg+24(FP), R17
+	MOVV	mp+16(FP), R16
+	MOVV	gp+24(FP), R17
 	MOVV	fn+32(FP), R18
 
 	MOVV	R16, -8(R5)
@@ -328,8 +322,6 @@ TEXT runtime·clone(SB),NOSPLIT,$-8
 	RET
 
 	// In child, on new stack.
-	// initialize essential registers
-	JAL	runtime·reginit(SB)
 	MOVV	-32(R29), R16
 	MOVV	$1234, R1
 	BEQ	R16, R1, 2(PC)
diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s
new file mode 100644
index 0000000..6f089f5
--- /dev/null
+++ b/src/runtime/sys_linux_mipsx.s
@@ -0,0 +1,467 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+// +build mips mipsle
+
+//
+// System calls and other sys.stuff for mips, Linux
+//
+
+#include "go_asm.h"
+#include "go_tls.h"
+#include "textflag.h"
+
+#define SYS_exit		        4001
+#define SYS_read		        4003
+#define SYS_write		        4004
+#define SYS_open		        4005
+#define SYS_close		        4006
+#define SYS_getpid		        4020
+#define SYS_kill		        4037
+#define SYS_fcntl		        4055
+#define SYS_gettimeofday	    4078
+#define SYS_mmap		        4090
+#define SYS_munmap		        4091
+#define SYS_setitimer		    4104
+#define SYS_clone		        4120
+#define SYS_newselect		    4142
+#define SYS_sched_yield		    4162
+#define SYS_rt_sigreturn	    4193
+#define SYS_rt_sigaction	    4194
+#define SYS_rt_sigprocmask		4195
+#define SYS_sigaltstack		    4206
+#define SYS_getrlimit		    4076
+#define SYS_madvise		        4218
+#define SYS_mincore		        4217
+#define SYS_gettid		        4222
+#define SYS_tkill		        4236
+#define SYS_futex		        4238
+#define SYS_sched_getaffinity	4240
+#define SYS_exit_group		    4246
+#define SYS_epoll_create	    4248
+#define SYS_epoll_ctl		    4249
+#define SYS_epoll_wait		    4250
+#define SYS_clock_gettime	    4263
+#define SYS_epoll_create1	    4326
+
+TEXT runtime·exit(SB),NOSPLIT,$0-4
+	MOVW	code+0(FP), R4
+	MOVW	$SYS_exit_group, R2
+	SYSCALL
+	UNDEF
+	RET
+
+TEXT runtime·exit1(SB),NOSPLIT,$0-4
+	MOVW	code+0(FP), R4
+	MOVW	$SYS_exit, R2
+	SYSCALL
+	UNDEF
+	RET
+
+TEXT runtime·open(SB),NOSPLIT,$0-16
+	MOVW	name+0(FP), R4
+	MOVW	mode+4(FP), R5
+	MOVW	perm+8(FP), R6
+	MOVW	$SYS_open, R2
+	SYSCALL
+	BEQ	R7, 2(PC)
+	MOVW	$-1, R2
+	MOVW	R2, ret+12(FP)
+	RET
+
+TEXT runtime·closefd(SB),NOSPLIT,$0-8
+	MOVW	fd+0(FP), R4
+	MOVW	$SYS_close, R2
+	SYSCALL
+	BEQ	R7, 2(PC)
+	MOVW	$-1, R2
+	MOVW	R2, ret+4(FP)
+	RET
+
+TEXT runtime·write(SB),NOSPLIT,$0-16
+	MOVW	fd+0(FP), R4
+	MOVW	p+4(FP), R5
+	MOVW	n+8(FP), R6
+	MOVW	$SYS_write, R2
+	SYSCALL
+	BEQ	R7, 2(PC)
+	MOVW	$-1, R2
+	MOVW	R2, ret+12(FP)
+	RET
+
+TEXT runtime·read(SB),NOSPLIT,$0-16
+	MOVW	fd+0(FP), R4
+	MOVW	p+4(FP), R5
+	MOVW	n+8(FP), R6
+	MOVW	$SYS_read, R2
+	SYSCALL
+	BEQ	R7, 2(PC)
+	MOVW	$-1, R2
+	MOVW	R2, ret+12(FP)
+	RET
+
+TEXT runtime·getrlimit(SB),NOSPLIT,$0-12
+	MOVW	kind+0(FP), R4
+	MOVW	limit+4(FP), R5
+	MOVW	$SYS_getrlimit, R2
+	SYSCALL
+	MOVW	R2, ret+8(FP)
+	RET
+
+TEXT runtime·usleep(SB),NOSPLIT,$28-4
+	MOVW	usec+0(FP), R3
+	MOVW	R3, R5
+	MOVW	$1000000, R4
+	DIVU	R4, R3
+	MOVW	LO, R3
+	MOVW	R3, 24(R29)
+	MULU	R3, R4
+	MOVW	LO, R4
+	SUBU	R4, R5
+	MOVW	R5, 28(R29)
+
+	// select(0, 0, 0, 0, &tv)
+	MOVW	$0, R4
+	MOVW	$0, R5
+	MOVW	$0, R6
+	MOVW	$0, R7
+	ADDU	$24, R29, R8
+	MOVW	R8, 16(R29)
+	MOVW	$SYS_newselect, R2
+	SYSCALL
+	RET
+
+TEXT runtime·gettid(SB),NOSPLIT,$0-4
+	MOVW	$SYS_gettid, R2
+	SYSCALL
+	MOVW	R2, ret+0(FP)
+	RET
+
+TEXT runtime·raise(SB),NOSPLIT,$0-4
+	MOVW	$SYS_gettid, R2
+	SYSCALL
+	MOVW	R2, R4	// arg 1 tid
+	MOVW	sig+0(FP), R5	// arg 2
+	MOVW	$SYS_tkill, R2
+	SYSCALL
+	RET
+
+TEXT runtime·raiseproc(SB),NOSPLIT,$0
+	MOVW	$SYS_getpid, R2
+	SYSCALL
+	MOVW	R2, R4	// arg 1 pid
+	MOVW	sig+0(FP), R5	// arg 2
+	MOVW	$SYS_kill, R2
+	SYSCALL
+	RET
+
+TEXT runtime·setitimer(SB),NOSPLIT,$0-12
+	MOVW	mode+0(FP), R4
+	MOVW	new+4(FP), R5
+	MOVW	old+8(FP), R6
+	MOVW	$SYS_setitimer, R2
+	SYSCALL
+	RET
+
+TEXT runtime·mincore(SB),NOSPLIT,$0-16
+	MOVW	addr+0(FP), R4
+	MOVW	n+4(FP), R5
+	MOVW	dst+8(FP), R6
+	MOVW	$SYS_mincore, R2
+	SYSCALL
+	SUBU	R2, R0, R2	// caller expects negative errno
+	MOVW	R2, ret+12(FP)
+	RET
+
+// func now() (sec int64, nsec int32)
+TEXT time·now(SB),NOSPLIT,$8-12
+	MOVW	$0, R4	// CLOCK_REALTIME
+	MOVW	$4(R29), R5
+	MOVW	$SYS_clock_gettime, R2
+	SYSCALL
+	MOVW	4(R29), R3	// sec
+	MOVW	8(R29), R5	// nsec
+#ifdef GOARCH_mips
+	MOVW	R3, sec_lo+4(FP)
+	MOVW	R0, sec_hi+0(FP)
+#else
+	MOVW	R3, sec_lo+0(FP)
+	MOVW	R0, sec_hi+4(FP)
+#endif
+	MOVW	R5, nsec+8(FP)
+	RET
+
+TEXT runtime·nanotime(SB),NOSPLIT,$8-8
+	MOVW	$1, R4	// CLOCK_MONOTONIC
+	MOVW	$4(R29), R5
+	MOVW	$SYS_clock_gettime, R2
+	SYSCALL
+	MOVW	4(R29), R3	// sec
+	MOVW	8(R29), R5	// nsec
+	// sec is in R3, nsec in R5
+	// return nsec in R3
+	MOVW	$1000000000, R4
+	MULU	R4, R3
+	MOVW	LO, R3
+	ADDU	R5, R3
+	SGTU	R5, R3, R4
+#ifdef GOARCH_mips
+	MOVW	R3, ret_lo+4(FP)
+#else
+	MOVW	R3, ret_lo+0(FP)
+#endif
+	MOVW	HI, R3
+	ADDU	R4, R3
+#ifdef GOARCH_mips
+	MOVW	R3, ret_hi+0(FP)
+#else
+	MOVW	R3, ret_hi+4(FP)
+#endif
+	RET
+
+TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0-16
+	MOVW	how+0(FP), R4
+	MOVW	new+4(FP), R5
+	MOVW	old+8(FP), R6
+	MOVW	size+12(FP), R7
+	MOVW	$SYS_rt_sigprocmask, R2
+	SYSCALL
+	BEQ	R7, 2(PC)
+	UNDEF	// crash
+	RET
+
+TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-20
+	MOVW	sig+0(FP), R4
+	MOVW	new+4(FP), R5
+	MOVW	old+8(FP), R6
+	MOVW	size+12(FP), R7
+	MOVW	$SYS_rt_sigaction, R2
+	SYSCALL
+	MOVW	R2, ret+16(FP)
+	RET
+
+TEXT runtime·sigfwd(SB),NOSPLIT,$0-16
+	MOVW	sig+4(FP), R4
+	MOVW	info+8(FP), R5
+	MOVW	ctx+12(FP), R6
+	MOVW	fn+0(FP), R25
+	MOVW	R29, R22
+	SUBU	$16, R29
+	AND	$0x7, R29	// shadow space for 4 args aligned to 8 bytes as per O32 ABI
+	JAL	(R25)
+	MOVW	R22, R29
+	RET
+
+TEXT runtime·sigtramp(SB),NOSPLIT,$12
+	// this might be called in external code context,
+	// where g is not set.
+	MOVB	runtime·iscgo(SB), R1
+	BEQ	R1, 2(PC)
+	JAL	runtime·load_g(SB)
+
+	MOVW	R4, 4(R29)
+	MOVW	R5, 8(R29)
+	MOVW	R6, 12(R29)
+	MOVW	$runtime·sigtrampgo(SB), R1
+	JAL	(R1)
+	RET
+
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+	JMP	runtime·sigtramp(SB)
+
+TEXT runtime·mmap(SB),NOSPLIT,$20-28
+	MOVW	addr+0(FP), R4
+	MOVW	n+4(FP), R5
+	MOVW	prot+8(FP), R6
+	MOVW	flags+12(FP), R7
+	MOVW	fd+16(FP), R8
+	MOVW	off+20(FP), R9
+	MOVW	R8, 16(R29)
+	MOVW	R9, 20(R29)
+
+	MOVW	$SYS_mmap, R2
+	SYSCALL
+	MOVW	R2, ret+24(FP)
+	RET
+
+TEXT runtime·munmap(SB),NOSPLIT,$0-8
+	MOVW	addr+0(FP), R4
+	MOVW	n+4(FP), R5
+	MOVW	$SYS_munmap, R2
+	SYSCALL
+	BEQ	R7, 2(PC)
+	UNDEF	// crash
+	RET
+
+TEXT runtime·madvise(SB),NOSPLIT,$0-12
+	MOVW	addr+0(FP), R4
+	MOVW	n+4(FP), R5
+	MOVW	flags+8(FP), R6
+	MOVW	$SYS_madvise, R2
+	SYSCALL
+	// ignore failure - maybe pages are locked
+	RET
+
+// int32 futex(int32 *uaddr, int32 op, int32 val, struct timespec *timeout, int32 *uaddr2, int32 val2);
+TEXT runtime·futex(SB),NOSPLIT,$20-28
+	MOVW	addr+0(FP), R4
+	MOVW	op+4(FP), R5
+	MOVW	val+8(FP), R6
+	MOVW	ts+12(FP), R7
+
+	MOVW	addr2+16(FP), R8
+	MOVW	val3+20(FP), R9
+
+	MOVW	R8, 16(R29)
+	MOVW	R9, 20(R29)
+
+	MOVW	$SYS_futex, R2
+	SYSCALL
+	MOVW	R2, ret+24(FP)
+	RET
+
+
+// int32 clone(int32 flags, void *stk, M *mm, G *gg, void (*fn)(void));
+TEXT runtime·clone(SB),NOSPLIT,$-4-24
+	MOVW	flags+0(FP), R4
+	MOVW	stk+4(FP), R5
+	MOVW	R0, R6	// ptid
+	MOVW	R0, R7	// tls
+
+	// O32 syscall handler unconditionally copies arguments 5-8 from stack,
+	// even for syscalls with less than 8 arguments. Reserve 32 bytes of new
+	// stack so that any syscall invoked immediately in the new thread won't fail.
+	ADD	$-32, R5
+
+	// Copy mm, gg, fn off parent stack for use by child.
+	MOVW	mm+8(FP), R16
+	MOVW	gg+12(FP), R17
+	MOVW	fn+16(FP), R18
+
+	MOVW	$1234, R1
+
+	MOVW	R16, 0(R5)
+	MOVW	R17, 4(R5)
+	MOVW	R18, 8(R5)
+
+	MOVW	R1, 12(R5)
+
+	MOVW	$SYS_clone, R2
+	SYSCALL
+
+	// In parent, return.
+	BEQ	R2, 5(PC)
+	SUBU	R2, R0, R3
+	CMOVN	R7, R3, R2
+	MOVW	R2, ret+20(FP)
+	RET
+
+	// In child, on new stack.
+	// Check that SP is as we expect
+	MOVW	12(R29), R16
+	MOVW	$1234, R1
+	BEQ	R16, R1, 2(PC)
+	MOVW	(R0), R0
+
+	// Initialize m->procid to Linux tid
+	MOVW	$SYS_gettid, R2
+	SYSCALL
+
+	MOVW	0(R29), R16	// m
+	MOVW	4(R29), R17	// g
+	MOVW	8(R29), R18	// fn
+
+	BEQ	R16, nog
+	BEQ	R17, nog
+
+	MOVW	R2, m_procid(R16)
+
+	// In child, set up new stack
+	MOVW	R16, g_m(R17)
+	MOVW	R17, g
+
+// TODO(mips32): doesn't have runtime·stackcheck(SB)
+
+nog:
+	// Call fn
+	ADDU	$32, R29
+	JAL	(R18)
+
+	// It shouldn't return.	 If it does, exit that thread.
+	ADDU	$-32, R29
+	MOVW	$0xf4, R4
+	MOVW	$SYS_exit, R2
+	SYSCALL
+	UNDEF
+
+TEXT runtime·sigaltstack(SB),NOSPLIT,$0
+	MOVW	new+0(FP), R4
+	MOVW	old+4(FP), R5
+	MOVW	$SYS_sigaltstack, R2
+	SYSCALL
+	BEQ	R7, 2(PC)
+	UNDEF	// crash
+	RET
+
+TEXT runtime·osyield(SB),NOSPLIT,$0
+	MOVW	$SYS_sched_yield, R2
+	SYSCALL
+	RET
+
+TEXT runtime·sched_getaffinity(SB),NOSPLIT,$0-16
+	MOVW	pid+0(FP), R4
+	MOVW	len+4(FP), R5
+	MOVW	buf+8(FP), R6
+	MOVW	$SYS_sched_getaffinity, R2
+	SYSCALL
+	MOVW	R2, ret+12(FP)
+	RET
+
+// int32 runtime·epollcreate(int32 size);
+TEXT runtime·epollcreate(SB),NOSPLIT,$0-8
+	MOVW	size+0(FP), R4
+	MOVW	$SYS_epoll_create, R2
+	SYSCALL
+	MOVW	R2, ret+4(FP)
+	RET
+
+// int32 runtime·epollcreate1(int32 flags);
+TEXT runtime·epollcreate1(SB),NOSPLIT,$0-8
+	MOVW	flags+0(FP), R4
+	MOVW	$SYS_epoll_create1, R2
+	SYSCALL
+	MOVW	R2, ret+4(FP)
+	RET
+
+// func epollctl(epfd, op, fd int32, ev *epollEvent) int
+TEXT runtime·epollctl(SB),NOSPLIT,$0-20
+	MOVW	epfd+0(FP), R4
+	MOVW	op+4(FP), R5
+	MOVW	fd+8(FP), R6
+	MOVW	ev+12(FP), R7
+	MOVW	$SYS_epoll_ctl, R2
+	SYSCALL
+	MOVW	R2, ret+16(FP)
+	RET
+
+// int32 runtime·epollwait(int32 epfd, EpollEvent *ev, int32 nev, int32 timeout);
+TEXT runtime·epollwait(SB),NOSPLIT,$0-20
+	MOVW	epfd+0(FP), R4
+	MOVW	ev+4(FP), R5
+	MOVW	nev+8(FP), R6
+	MOVW	timeout+12(FP), R7
+	MOVW	$SYS_epoll_wait, R2
+	SYSCALL
+	MOVW	R2, ret+16(FP)
+	RET
+
+// void runtime·closeonexec(int32 fd);
+TEXT runtime·closeonexec(SB),NOSPLIT,$0-4
+	MOVW	fd+0(FP), R4	// fd
+	MOVW	$2, R5	// F_SETFD
+	MOVW	$1, R6	// FD_CLOEXEC
+	MOVW	$SYS_fcntl, R2
+	SYSCALL
+	RET
diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s
index 56b842a..a40fe3b 100644
--- a/src/runtime/sys_linux_ppc64x.s
+++ b/src/runtime/sys_linux_ppc64x.s
@@ -159,13 +159,11 @@ TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28
 
 // func now() (sec int64, nsec int32)
 TEXT time·now(SB),NOSPLIT,$16
-	MOVD	$0(R1), R3
-	MOVD	$0, R4
-	SYSCALL	$SYS_gettimeofday
+	MOVD	$0, R3 // CLOCK_REALTIME
+	MOVD	$0(R1), R4
+	SYSCALL	$SYS_clock_gettime
 	MOVD	0(R1), R3	// sec
-	MOVD	8(R1), R5	// usec
-	MOVD	$1000, R4
-	MULLD	R4, R5
+	MOVD	8(R1), R5	// nsec
 	MOVD	R3, sec+0(FP)
 	MOVW	R5, nsec+8(FP)
 	RET
@@ -185,7 +183,7 @@ TEXT runtime·nanotime(SB),NOSPLIT,$16
 	RET
 
 TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28
-	MOVW	sig+0(FP), R3
+	MOVW	how+0(FP), R3
 	MOVD	new+8(FP), R4
 	MOVD	old+16(FP), R5
 	MOVW	size+24(FP), R6
@@ -306,8 +304,8 @@ TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0
 
 	// Copy mp, gp, fn off parent stack for use by child.
 	// Careful: Linux system call clobbers ???.
-	MOVD	mm+16(FP), R7
-	MOVD	gg+24(FP), R8
+	MOVD	mp+16(FP), R7
+	MOVD	gp+24(FP), R8
 	MOVD	fn+32(FP), R12
 
 	MOVD	R7, -8(R4)
diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s
index f43792b..47f34d9 100644
--- a/src/runtime/sys_linux_s390x.s
+++ b/src/runtime/sys_linux_s390x.s
@@ -171,35 +171,31 @@ TEXT runtime·mincore(SB),NOSPLIT|NOFRAME,$0-28
 
 // func now() (sec int64, nsec int32)
 TEXT time·now(SB),NOSPLIT,$16
-	MOVD	$0(R15), R2
-	MOVD	$0, R3
-	MOVW	$SYS_gettimeofday, R1
-	SYSCALL
-	MOVD	0(R15), R2	// sec
-	MOVD	8(R15), R4	// usec
-	MOVD	$1000, R3
-	MULLD	R3, R4
+	MOVW	$0, R2 // CLOCK_REALTIME
+	MOVD	$tp-16(SP), R3
+	MOVW	$SYS_clock_gettime, R1
+	SYSCALL
+	LMG	tp-16(SP), R2, R3
+	// sec is in R2, nsec in R3
 	MOVD	R2, sec+0(FP)
-	MOVW	R4, nsec+8(FP)
+	MOVW	R3, nsec+8(FP)
 	RET
 
 TEXT runtime·nanotime(SB),NOSPLIT,$16
 	MOVW	$1, R2 // CLOCK_MONOTONIC
-	MOVD	$0(R15), R3
+	MOVD	$tp-16(SP), R3
 	MOVW	$SYS_clock_gettime, R1
 	SYSCALL
-	MOVD	0(R15), R2	// sec
-	MOVD	8(R15), R4	// nsec
-	// sec is in R2, nsec in R4
+	LMG	tp-16(SP), R2, R3
+	// sec is in R2, nsec in R3
 	// return nsec in R2
-	MOVD	$1000000000, R3
-	MULLD	R3, R2
-	ADD	R4, R2
+	MULLD	$1000000000, R2
+	ADD	R3, R2
 	MOVD	R2, ret+0(FP)
 	RET
 
 TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28
-	MOVW	sig+0(FP), R2
+	MOVW	how+0(FP), R2
 	MOVD	new+8(FP), R3
 	MOVD	old+16(FP), R4
 	MOVW	size+24(FP), R5
@@ -315,8 +311,8 @@ TEXT runtime·clone(SB),NOSPLIT|NOFRAME,$0
 
 	// Copy mp, gp, fn off parent stack for use by child.
 	// Careful: Linux system call clobbers ???.
-	MOVD	mm+16(FP), R7
-	MOVD	gg+24(FP), R8
+	MOVD	mp+16(FP), R7
+	MOVD	gp+24(FP), R8
 	MOVD	fn+32(FP), R9
 
 	MOVD	R7, -8(R2)
diff --git a/src/runtime/sys_mips64x.go b/src/runtime/sys_mips64x.go
index 9e7d805..cb429c3 100644
--- a/src/runtime/sys_mips64x.go
+++ b/src/runtime/sys_mips64x.go
@@ -18,26 +18,3 @@ func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
 	buf.pc = uintptr(fn)
 	buf.ctxt = ctxt
 }
-
-// Called to rewind context saved during morestack back to beginning of function.
-// To help us, the linker emits a jmp back to the beginning right after the
-// call to morestack. We just have to decode and apply that jump.
-func rewindmorestack(buf *gobuf) {
-	var inst uint32
-	if buf.pc&3 == 0 && buf.pc != 0 {
-		inst = *(*uint32)(unsafe.Pointer(buf.pc))
-		if inst>>26 == 2 { // JMP addr
-			//print("runtime: rewind pc=", hex(buf.pc), " to pc=", hex(buf.pc &^ uintptr(1<<28-1) | uintptr((inst&^0xfc000000)<<2)), "\n");
-			buf.pc &^= 1<<28 - 1
-			buf.pc |= uintptr((inst &^ 0xfc000000) << 2)
-			return
-		}
-		if inst>>16 == 0x1000 { // BEQ	R0, R0, offset
-			//print("runtime: rewind pc=", hex(buf.pc), " to pc=", hex(buf.pc + uintptr(int32(int16(inst&0xffff))<<2 + 4)), "\n");
-			buf.pc += uintptr(int32(int16(inst&0xffff))<<2 + 4)
-			return
-		}
-	}
-	print("runtime: pc=", hex(buf.pc), " ", hex(inst), "\n")
-	throw("runtime: misuse of rewindmorestack")
-}
diff --git a/src/runtime/sys_mipsx.go b/src/runtime/sys_mipsx.go
new file mode 100644
index 0000000..2819218
--- /dev/null
+++ b/src/runtime/sys_mipsx.go
@@ -0,0 +1,20 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+package runtime
+
+import "unsafe"
+
+// adjust Gobuf as if it executed a call to fn with context ctxt
+// and then did an immediate Gosave.
+func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
+	if buf.lr != 0 {
+		throw("invalid use of gostartcall")
+	}
+	buf.lr = buf.pc
+	buf.pc = uintptr(fn)
+	buf.ctxt = ctxt
+}
diff --git a/src/runtime/sys_nacl_386.s b/src/runtime/sys_nacl_386.s
index e69a0b7..05de20c 100644
--- a/src/runtime/sys_nacl_386.s
+++ b/src/runtime/sys_nacl_386.s
@@ -368,9 +368,9 @@ ret:
 
 // func getRandomData([]byte)
 TEXT runtime·getRandomData(SB),NOSPLIT,$8-12
-	MOVL buf+0(FP), AX
+	MOVL arg_base+0(FP), AX
 	MOVL AX, 0(SP)
-	MOVL len+4(FP), AX
+	MOVL arg_len+4(FP), AX
 	MOVL AX, 4(SP)
 	NACL_SYSCALL(SYS_get_random_bytes)
 	RET
diff --git a/src/runtime/sys_nacl_amd64p32.s b/src/runtime/sys_nacl_amd64p32.s
index 0b29c9f..c2a24e8 100644
--- a/src/runtime/sys_nacl_amd64p32.s
+++ b/src/runtime/sys_nacl_amd64p32.s
@@ -414,8 +414,8 @@ MOVL $1, DI; NACL_SYSCALL(SYS_exit)
 
 // func getRandomData([]byte)
 TEXT runtime·getRandomData(SB),NOSPLIT,$0-12
-	MOVL buf+0(FP), DI
-	MOVL len+4(FP), SI
+	MOVL arg_base+0(FP), DI
+	MOVL arg_len+4(FP), SI
 	NACL_SYSCALL(SYS_get_random_bytes)
 	RET
 
diff --git a/src/runtime/sys_nacl_arm.s b/src/runtime/sys_nacl_arm.s
index 474d9fe..6cbc23f 100644
--- a/src/runtime/sys_nacl_arm.s
+++ b/src/runtime/sys_nacl_arm.s
@@ -303,8 +303,8 @@ TEXT runtime·nacl_sysinfo(SB),NOSPLIT,$16
 
 // func getRandomData([]byte)
 TEXT runtime·getRandomData(SB),NOSPLIT,$0-12
-	MOVW buf+0(FP), R0
-	MOVW len+4(FP), R1
+	MOVW arg_base+0(FP), R0
+	MOVW arg_len+4(FP), R1
 	NACL_SYSCALL(SYS_get_random_bytes)
 	RET
 
diff --git a/src/runtime/sys_netbsd_386.s b/src/runtime/sys_netbsd_386.s
index 0322c36..50d35e5 100644
--- a/src/runtime/sys_netbsd_386.s
+++ b/src/runtime/sys_netbsd_386.s
@@ -216,14 +216,20 @@ TEXT runtime·sigaction(SB),NOSPLIT,$24
 	RET
 
 TEXT runtime·sigfwd(SB),NOSPLIT,$12-16
-	MOVL	sig+4(FP), AX
-	MOVL	AX, 0(SP)
-	MOVL	info+8(FP), AX
-	MOVL	AX, 4(SP)
-	MOVL	ctx+12(FP), AX
-	MOVL	AX, 8(SP)
 	MOVL	fn+0(FP), AX
+	MOVL	sig+4(FP), BX
+	MOVL	info+8(FP), CX
+	MOVL	ctx+12(FP), DX
+	MOVL	SP, SI
+	SUBL	$32, SP
+	ANDL	$-15, SP	// align stack: handler might be a C function
+	MOVL	BX, 0(SP)
+	MOVL	CX, 4(SP)
+	MOVL	DX, 8(SP)
+	MOVL	SI, 12(SP)	// save SI: handler might be a Go function
 	CALL	AX
+	MOVL	12(SP), AX
+	MOVL	AX, SP
 	RET
 
 TEXT runtime·sigtramp(SB),NOSPLIT,$12
@@ -285,8 +291,8 @@ TEXT runtime·lwp_tramp(SB),NOSPLIT,$0
 
 TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
 	MOVL	$281, AX		// sys___sigaltstack14
-	MOVL	new+4(SP), BX
-	MOVL	old+8(SP), CX
+	MOVL	new+0(FP), BX
+	MOVL	old+4(FP), CX
 	INT	$0x80
 	CMPL	AX, $0xfffff001
 	JLS	2(PC)
diff --git a/src/runtime/sys_netbsd_amd64.s b/src/runtime/sys_netbsd_amd64.s
index d6b5d35..2c50adb 100644
--- a/src/runtime/sys_netbsd_amd64.s
+++ b/src/runtime/sys_netbsd_amd64.s
@@ -207,7 +207,7 @@ TEXT runtime·getcontext(SB),NOSPLIT,$-8
 	RET
 
 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
-	MOVL	mode+0(FP), DI		// arg 1 - how
+	MOVL	how+0(FP), DI		// arg 1 - how
 	MOVQ	new+8(FP), SI		// arg 2 - set
 	MOVQ	old+16(FP), DX		// arg 3 - oset
 	MOVL	$293, AX		// sys_sigprocmask
@@ -238,11 +238,16 @@ TEXT runtime·sigaction(SB),NOSPLIT,$-8
 	RET
 
 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
-	MOVL	sig+8(FP), DI
+	MOVQ	fn+0(FP),    AX
+	MOVL	sig+8(FP),   DI
 	MOVQ	info+16(FP), SI
-	MOVQ	ctx+24(FP), DX
-	MOVQ	fn+0(FP), AX
+	MOVQ	ctx+24(FP),  DX
+	PUSHQ	BP
+	MOVQ	SP, BP
+	ANDQ	$~15, SP     // alignment for x86_64 ABI
 	CALL	AX
+	MOVQ	BP, SP
+	POPQ	BP
 	RET
 
 TEXT runtime·sigtramp(SB),NOSPLIT,$32
@@ -290,8 +295,8 @@ TEXT runtime·madvise(SB),NOSPLIT,$0
 	RET
 
 TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
-	MOVQ	new+8(SP), DI		// arg 1 - nss
-	MOVQ	old+16(SP), SI		// arg 2 - oss
+	MOVQ	new+0(FP), DI		// arg 1 - nss
+	MOVQ	old+8(FP), SI		// arg 2 - oss
 	MOVQ	$281, AX		// sys___sigaltstack14
 	SYSCALL
 	JCC	2(PC)
@@ -337,11 +342,11 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
 
 // int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout)
 TEXT runtime·kevent(SB),NOSPLIT,$0
-	MOVL	fd+0(FP), DI
-	MOVQ	ev1+8(FP), SI
-	MOVL	nev1+16(FP), DX
-	MOVQ	ev2+24(FP), R10
-	MOVL	nev2+32(FP), R8
+	MOVL	kq+0(FP), DI
+	MOVQ	ch+8(FP), SI
+	MOVL	nch+16(FP), DX
+	MOVQ	ev+24(FP), R10
+	MOVL	nev+32(FP), R8
 	MOVQ	ts+40(FP), R9
 	MOVL	$435, AX
 	SYSCALL
diff --git a/src/runtime/sys_netbsd_arm.s b/src/runtime/sys_netbsd_arm.s
index 3d3b65f..a8914c1 100644
--- a/src/runtime/sys_netbsd_arm.s
+++ b/src/runtime/sys_netbsd_arm.s
@@ -181,7 +181,7 @@ TEXT runtime·getcontext(SB),NOSPLIT,$-4
 	RET
 
 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
-	MOVW mode+0(FP), R0	// arg 1 - how
+	MOVW how+0(FP), R0	// arg 1 - how
 	MOVW new+4(FP), R1	// arg 2 - set
 	MOVW old+8(FP), R2	// arg 3 - oset
 	SWI $0xa00125	// sys_sigprocmask
diff --git a/src/runtime/sys_openbsd_386.s b/src/runtime/sys_openbsd_386.s
index 2bb818f..e969395 100644
--- a/src/runtime/sys_openbsd_386.s
+++ b/src/runtime/sys_openbsd_386.s
@@ -187,7 +187,7 @@ TEXT runtime·sigaction(SB),NOSPLIT,$-4
 	MOVL	$0xf1, 0xf1		// crash
 	RET
 
-TEXT runtime·sigprocmask(SB),NOSPLIT,$-4
+TEXT runtime·obsdsigprocmask(SB),NOSPLIT,$-4
 	MOVL	$48, AX			// sys_sigprocmask
 	INT	$0x80
 	JAE	2(PC)
@@ -196,14 +196,20 @@ TEXT runtime·sigprocmask(SB),NOSPLIT,$-4
 	RET
 
 TEXT runtime·sigfwd(SB),NOSPLIT,$12-16
-	MOVL	sig+4(FP), AX
-	MOVL	AX, 0(SP)
-	MOVL	info+8(FP), AX
-	MOVL	AX, 4(SP)
-	MOVL	ctx+12(FP), AX
-	MOVL	AX, 8(SP)
 	MOVL	fn+0(FP), AX
+	MOVL	sig+4(FP), BX
+	MOVL	info+8(FP), CX
+	MOVL	ctx+12(FP), DX
+	MOVL	SP, SI
+	SUBL	$32, SP
+	ANDL	$~15, SP	// align stack: handler might be a C function
+	MOVL	BX, 0(SP)
+	MOVL	CX, 4(SP)
+	MOVL	DX, 8(SP)
+	MOVL	SI, 12(SP)	// save SI: handler might be a Go function
 	CALL	AX
+	MOVL	12(SP), AX
+	MOVL	AX, SP
 	RET
 
 TEXT runtime·sigtramp(SB),NOSPLIT,$12
@@ -294,8 +300,8 @@ TEXT runtime·tfork(SB),NOSPLIT,$12
 
 TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
 	MOVL	$288, AX		// sys_sigaltstack
-	MOVL	new+4(SP), BX
-	MOVL	old+8(SP), CX
+	MOVL	new+0(FP), BX
+	MOVL	old+4(FP), CX
 	INT	$0x80
 	CMPL	AX, $0xfffff001
 	JLS	2(PC)
diff --git a/src/runtime/sys_openbsd_amd64.s b/src/runtime/sys_openbsd_amd64.s
index c9fb832..01d6bd8 100644
--- a/src/runtime/sys_openbsd_amd64.s
+++ b/src/runtime/sys_openbsd_amd64.s
@@ -218,8 +218,8 @@ TEXT runtime·sigaction(SB),NOSPLIT,$-8
 	MOVL	$0xf1, 0xf1		// crash
 	RET
 
-TEXT runtime·sigprocmask(SB),NOSPLIT,$0
-	MOVL	mode+0(FP), DI		// arg 1 - how
+TEXT runtime·obsdsigprocmask(SB),NOSPLIT,$0
+	MOVL	how+0(FP), DI		// arg 1 - how
 	MOVL	new+4(FP), SI		// arg 2 - set
 	MOVL	$48, AX			// sys_sigprocmask
 	SYSCALL
@@ -229,11 +229,16 @@ TEXT runtime·sigprocmask(SB),NOSPLIT,$0
 	RET
 
 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
-	MOVL	sig+8(FP), DI
+	MOVQ	fn+0(FP),    AX
+	MOVL	sig+8(FP),   DI
 	MOVQ	info+16(FP), SI
-	MOVQ	ctx+24(FP), DX
-	MOVQ	fn+0(FP), AX
+	MOVQ	ctx+24(FP),  DX
+	PUSHQ	BP
+	MOVQ	SP, BP
+	ANDQ	$~15, SP     // alignment for x86_64 ABI
 	CALL	AX
+	MOVQ	BP, SP
+	POPQ	BP
 	RET
 
 TEXT runtime·sigtramp(SB),NOSPLIT,$24
@@ -278,8 +283,8 @@ TEXT runtime·madvise(SB),NOSPLIT,$0
 	RET
 
 TEXT runtime·sigaltstack(SB),NOSPLIT,$-8
-	MOVQ	new+8(SP), DI		// arg 1 - nss
-	MOVQ	old+16(SP), SI		// arg 2 - oss
+	MOVQ	new+0(FP), DI		// arg 1 - nss
+	MOVQ	old+8(FP), SI		// arg 2 - oss
 	MOVQ	$288, AX		// sys_sigaltstack
 	SYSCALL
 	JCC	2(PC)
@@ -327,11 +332,11 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
 
 // int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
 TEXT runtime·kevent(SB),NOSPLIT,$0
-	MOVL	fd+0(FP), DI
-	MOVQ	ev1+8(FP), SI
-	MOVL	nev1+16(FP), DX
-	MOVQ	ev2+24(FP), R10
-	MOVL	nev2+32(FP), R8
+	MOVL	kq+0(FP), DI
+	MOVQ	ch+8(FP), SI
+	MOVL	nch+16(FP), DX
+	MOVQ	ev+24(FP), R10
+	MOVL	nev+32(FP), R8
 	MOVQ	ts+40(FP), R9
 	MOVL	$72, AX
 	SYSCALL
diff --git a/src/runtime/sys_openbsd_arm.s b/src/runtime/sys_openbsd_arm.s
index 29e8971..e0f775d 100644
--- a/src/runtime/sys_openbsd_arm.s
+++ b/src/runtime/sys_openbsd_arm.s
@@ -15,7 +15,7 @@
 
 // Exit the entire program (like C exit)
 TEXT runtime·exit(SB),NOSPLIT,$-4
-	MOVW	status+0(FP), R0	// arg 1 - status
+	MOVW	code+0(FP), R0	// arg 1 - status
 	MOVW	$1, R12			// sys_exit
 	SWI	$0
 	MOVW.CS	$0, R8			// crash on syscall failure
@@ -31,9 +31,9 @@ TEXT runtime·exit1(SB),NOSPLIT,$-4
 	RET
 
 TEXT runtime·open(SB),NOSPLIT,$-4
-	MOVW	path+0(FP), R0		// arg 1 - path
-	MOVW	flags+4(FP), R1		// arg 2 - flags
-	MOVW	mode+8(FP), R2		// arg 3 - mode
+	MOVW	name+0(FP), R0		// arg 1 - path
+	MOVW	mode+4(FP), R1		// arg 2 - mode
+	MOVW	perm+8(FP), R2		// arg 3 - perm
 	MOVW	$5, R12			// sys_open
 	SWI	$0
 	MOVW.CS	$-1, R0
@@ -41,7 +41,7 @@ TEXT runtime·open(SB),NOSPLIT,$-4
 	RET
 
 TEXT runtime·closefd(SB),NOSPLIT,$-4
-	MOVW	path+0(FP), R0		// arg 1 - path
+	MOVW	fd+0(FP), R0		// arg 1 - fd
 	MOVW	$6, R12			// sys_close
 	SWI	$0
 	MOVW.CS	$-1, R0
@@ -50,8 +50,8 @@ TEXT runtime·closefd(SB),NOSPLIT,$-4
 
 TEXT runtime·read(SB),NOSPLIT,$-4
 	MOVW	fd+0(FP), R0		// arg 1 - fd
-	MOVW	buf+4(FP), R1		// arg 2 - buf
-	MOVW	nbyte+8(FP), R2		// arg 3 - nbyte
+	MOVW	p+4(FP), R1		// arg 2 - buf
+	MOVW	n+8(FP), R2		// arg 3 - nbyte
 	MOVW	$3, R12			// sys_read
 	SWI	$0
 	MOVW.CS	$-1, R0
@@ -60,8 +60,8 @@ TEXT runtime·read(SB),NOSPLIT,$-4
 
 TEXT runtime·write(SB),NOSPLIT,$-4
 	MOVW	fd+0(FP), R0		// arg 1 - fd
-	MOVW	buf+4(FP), R1		// arg 2 - buf
-	MOVW	nbyte+8(FP), R2		// arg 3 - nbyte
+	MOVW	p+4(FP), R1		// arg 2 - buf
+	MOVW	n+8(FP), R2		// arg 3 - nbyte
 	MOVW	$4, R12			// sys_write
 	SWI	$0
 	MOVW.CS	$-1, R0
@@ -104,14 +104,14 @@ TEXT runtime·raiseproc(SB),NOSPLIT,$12
 
 TEXT runtime·mmap(SB),NOSPLIT,$16
 	MOVW	addr+0(FP), R0		// arg 1 - addr
-	MOVW	len+4(FP), R1		// arg 2 - len
+	MOVW	n+4(FP), R1		// arg 2 - len
 	MOVW	prot+8(FP), R2		// arg 3 - prot
 	MOVW	flags+12(FP), R3	// arg 4 - flags
 	MOVW	fd+16(FP), R4		// arg 5 - fd (on stack)
 	MOVW	R4, 4(R13)
 	MOVW	$0, R5			// arg 6 - pad (on stack)
 	MOVW	R5, 8(R13)
-	MOVW	offset+20(FP), R6	// arg 7 - offset (on stack)
+	MOVW	off+20(FP), R6		// arg 7 - offset (on stack)
 	MOVW	R6, 12(R13)		// lower 32 bits (from Go runtime)
 	MOVW	$0, R7
 	MOVW	R7, 16(R13)		// high 32 bits
@@ -124,7 +124,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$16
 
 TEXT runtime·munmap(SB),NOSPLIT,$0
 	MOVW	addr+0(FP), R0		// arg 1 - addr
-	MOVW	len+4(FP), R1		// arg 2 - len
+	MOVW	n+4(FP), R1		// arg 2 - len
 	MOVW	$73, R12		// sys_munmap
 	SWI	$0
 	MOVW.CS	$0, R8			// crash on syscall failure
@@ -133,8 +133,8 @@ TEXT runtime·munmap(SB),NOSPLIT,$0
 
 TEXT runtime·madvise(SB),NOSPLIT,$0
 	MOVW	addr+0(FP), R0		// arg 1 - addr
-	MOVW	len+4(FP), R1		// arg 2 - len
-	MOVW	behav+8(FP), R2		// arg 2 - behav
+	MOVW	n+4(FP), R1		// arg 2 - len
+	MOVW	flags+8(FP), R2		// arg 2 - flags
 	MOVW	$75, R12		// sys_madvise
 	SWI	$0
 	MOVW.CS	$0, R8			// crash on syscall failure
@@ -142,9 +142,9 @@ TEXT runtime·madvise(SB),NOSPLIT,$0
 	RET
 
 TEXT runtime·setitimer(SB),NOSPLIT,$0
-	MOVW	which+0(FP), R0		// arg 1 - which
-	MOVW	value+4(FP), R1		// arg 2 - value
-	MOVW	ovalue+8(FP), R2	// arg 3 - ovalue
+	MOVW	mode+0(FP), R0		// arg 1 - mode
+	MOVW	new+4(FP), R1		// arg 2 - new value
+	MOVW	old+8(FP), R2		// arg 3 - old value
 	MOVW	$69, R12		// sys_setitimer
 	SWI	$0
 	RET
@@ -189,18 +189,18 @@ TEXT runtime·nanotime(SB),NOSPLIT,$32
 	RET
 
 TEXT runtime·sigaction(SB),NOSPLIT,$0
-	MOVW	signum+0(FP), R0	// arg 1 - signum
-	MOVW	nsa+4(FP), R1		// arg 2 - nsa
-	MOVW	osa+8(FP), R2		// arg 3 - osa
+	MOVW	sig+0(FP), R0		// arg 1 - signum
+	MOVW	new+4(FP), R1		// arg 2 - new sigaction
+	MOVW	old+8(FP), R2		// arg 3 - old sigaction
 	MOVW	$46, R12		// sys_sigaction
 	SWI	$0
 	MOVW.CS	$3, R8			// crash on syscall failure
 	MOVW.CS	R8, (R8)
 	RET
 
-TEXT runtime·sigprocmask(SB),NOSPLIT,$0
-	MOVW	how+0(FP), R0		// arg 1 - how
-	MOVW	mask+4(FP), R1		// arg 2 - mask
+TEXT runtime·obsdsigprocmask(SB),NOSPLIT,$0
+	MOVW	how+0(FP), R0		// arg 1 - mode
+	MOVW	new+4(FP), R1		// arg 2 - new
 	MOVW	$48, R12		// sys_sigprocmask
 	SWI	$0
 	MOVW.CS	$3, R8			// crash on syscall failure
@@ -274,8 +274,8 @@ TEXT runtime·tfork(SB),NOSPLIT,$0
 	RET
 
 TEXT runtime·sigaltstack(SB),NOSPLIT,$0
-	MOVW	nss+0(FP), R0		// arg 1 - nss
-	MOVW	oss+4(FP), R1		// arg 2 - oss
+	MOVW	new+0(FP), R0		// arg 1 - new sigaltstack
+	MOVW	old+4(FP), R1		// arg 2 - old sigaltstack
 	MOVW	$288, R12		// sys_sigaltstack
 	SWI	$0
 	MOVW.CS	$0, R8			// crash on syscall failure
@@ -290,7 +290,7 @@ TEXT runtime·osyield(SB),NOSPLIT,$0
 TEXT runtime·thrsleep(SB),NOSPLIT,$4
 	MOVW	ident+0(FP), R0		// arg 1 - ident
 	MOVW	clock_id+4(FP), R1	// arg 2 - clock_id
-	MOVW	tp+8(FP), R2		// arg 3 - tp
+	MOVW	tsp+8(FP), R2		// arg 3 - tsp
 	MOVW	lock+12(FP), R3		// arg 4 - lock
 	MOVW	abort+16(FP), R4	// arg 5 - abort (on stack)
 	MOVW	R4, 4(R13)
@@ -310,13 +310,13 @@ TEXT runtime·thrwakeup(SB),NOSPLIT,$0
 	RET
 
 TEXT runtime·sysctl(SB),NOSPLIT,$8
-	MOVW	name+0(FP), R0		// arg 1 - name
-	MOVW	namelen+4(FP), R1	// arg 2 - namelen
-	MOVW	oldp+8(FP), R2		// arg 3 - oldp
-	MOVW	oldlenp+12(FP), R3	// arg 4 - oldlenp
-	MOVW	newp+16(FP), R4		// arg 5 - newp (on stack)
+	MOVW	mib+0(FP), R0		// arg 1 - mib
+	MOVW	miblen+4(FP), R1	// arg 2 - miblen
+	MOVW	out+8(FP), R2		// arg 3 - out
+	MOVW	size+12(FP), R3		// arg 4 - size
+	MOVW	dst+16(FP), R4		// arg 5 - dest (on stack)
 	MOVW	R4, 4(R13)
-	MOVW	newlen+20(FP), R5	// arg 6 - newlen (on stack)
+	MOVW	ndst+20(FP), R5		// arg 6 - newlen (on stack)
 	MOVW	R5, 8(R13)
 	ADD	$4, R13
 	MOVW	$202, R12		// sys___sysctl
@@ -337,13 +337,13 @@ TEXT runtime·kqueue(SB),NOSPLIT,$0
 
 // int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);
 TEXT runtime·kevent(SB),NOSPLIT,$8
-	MOVW	fd+0(FP), R0		// arg 1 - fd
-	MOVW	changelist+4(FP), R1	// arg 2 - changelist
-	MOVW	nchanges+8(FP), R2	// arg 3 - nchanges
-	MOVW	eventlist+12(FP), R3	// arg 4 - eventlist
-	MOVW	nevents+16(FP), R4	// arg 5 - nevents (on stack)
+	MOVW	kq+0(FP), R0		// arg 1 - kq
+	MOVW	ch+4(FP), R1		// arg 2 - changelist
+	MOVW	nch+8(FP), R2		// arg 3 - nchanges
+	MOVW	ev+12(FP), R3		// arg 4 - eventlist
+	MOVW	nev+16(FP), R4		// arg 5 - nevents (on stack)
 	MOVW	R4, 4(R13)
-	MOVW	timeout+20(FP), R5	// arg 6 - timeout (on stack)
+	MOVW	ts+20(FP), R5		// arg 6 - timeout (on stack)
 	MOVW	R5, 8(R13)
 	ADD	$4, R13
 	MOVW	$72, R12		// sys_kevent
@@ -353,15 +353,13 @@ TEXT runtime·kevent(SB),NOSPLIT,$8
 	MOVW	R0, ret+24(FP)
 	RET
 
-// int32 runtime·closeonexec(int32 fd);
+// func closeonexec(fd int32)
 TEXT runtime·closeonexec(SB),NOSPLIT,$0
 	MOVW	fd+0(FP), R0		// arg 1 - fd
 	MOVW	$2, R1			// arg 2 - cmd (F_SETFD)
 	MOVW	$1, R2			// arg 3 - arg (FD_CLOEXEC)
 	MOVW	$92, R12		// sys_fcntl
 	SWI	$0
-	RSB.CS	$0, R0
-	MOVW	R0, ret+4(FP)
 	RET
 
 TEXT ·publicationBarrier(SB),NOSPLIT,$-4-0
diff --git a/src/runtime/sys_plan9_386.s b/src/runtime/sys_plan9_386.s
index 1af3cb1..41aa2fd 100644
--- a/src/runtime/sys_plan9_386.s
+++ b/src/runtime/sys_plan9_386.s
@@ -178,8 +178,8 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0
 	RET
 
 	// save args
-	MOVL	ureg+4(SP), CX
-	MOVL	note+8(SP), DX
+	MOVL	ureg+0(FP), CX
+	MOVL	note+4(FP), DX
 
 	// change stack
 	MOVL	g_m(BX), BX
diff --git a/src/runtime/sys_plan9_amd64.s b/src/runtime/sys_plan9_amd64.s
index 1492ef2..149505f 100644
--- a/src/runtime/sys_plan9_amd64.s
+++ b/src/runtime/sys_plan9_amd64.s
@@ -65,7 +65,7 @@ TEXT runtime·exits(SB),NOSPLIT,$0
 TEXT runtime·brk_(SB),NOSPLIT,$0
 	MOVQ	$24, BP
 	SYSCALL
-	MOVQ	AX, ret+8(FP)
+	MOVL	AX, ret+8(FP)
 	RET
 
 TEXT runtime·sleep(SB),NOSPLIT,$0
@@ -179,8 +179,8 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0
 	RET
 
 	// save args
-	MOVQ	ureg+8(SP), CX
-	MOVQ	note+16(SP), DX
+	MOVQ	ureg+0(FP), CX
+	MOVQ	note+8(FP), DX
 
 	// change stack
 	MOVQ	g_m(BX), BX
diff --git a/src/runtime/sys_plan9_arm.s b/src/runtime/sys_plan9_arm.s
index 6dee611..d54f56f 100644
--- a/src/runtime/sys_plan9_arm.s
+++ b/src/runtime/sys_plan9_arm.s
@@ -131,7 +131,7 @@ TEXT runtime·plan9_tsemacquire(SB),NOSPLIT,$0-12
 TEXT runtime·nsec(SB),NOSPLIT,$-4-12
 	MOVW	$SYS_NSEC, R0
 	SWI	0
-	MOVW	unnamed+0(FP), R1
+	MOVW	arg+0(FP), R1
 	MOVW	0(R1), R0
 	MOVW	R0, ret_lo+4(FP)
 	MOVW	4(R1), R0
@@ -230,7 +230,7 @@ TEXT runtime·tstart_plan9(SB),NOSPLIT,$0-4
 	MOVW	R0, 0(R0)		// not reached
 	RET
 
-//func sigtramp(ureg, msg unsafe.Pointer)
+//func sigtramp(ureg, note unsafe.Pointer)
 TEXT runtime·sigtramp(SB),NOSPLIT,$0-8
 	// check that g and m exist
 	CMP	$0, g
@@ -242,7 +242,7 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-8
 
 	// save args
 	MOVW	ureg+0(FP), R1
-	MOVW	msg+4(FP), R2
+	MOVW	note+4(FP), R2
 
 	// change stack
 	MOVW	m_gsignal(R0), R3
diff --git a/src/runtime/sys_ppc64x.go b/src/runtime/sys_ppc64x.go
index 2ea1f81..796f27c 100644
--- a/src/runtime/sys_ppc64x.go
+++ b/src/runtime/sys_ppc64x.go
@@ -19,21 +19,4 @@ func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
 	buf.ctxt = ctxt
 }
 
-// Called to rewind context saved during morestack back to beginning of function.
-// To help us, the linker emits a jmp back to the beginning right after the
-// call to morestack. We just have to decode and apply that jump.
-func rewindmorestack(buf *gobuf) {
-	var inst uint32
-	if buf.pc&3 == 0 && buf.pc != 0 {
-		inst = *(*uint32)(unsafe.Pointer(buf.pc))
-		if inst>>26 == 18 && inst&3 == 0 {
-			//print("runtime: rewind pc=", hex(buf.pc), " to pc=", hex(uintptr(buf.pc + int32(inst<<6)>>6)), "\n");
-			buf.pc += uintptr(int32(inst<<6) >> 6)
-			return
-		}
-	}
-	print("runtime: pc=", hex(buf.pc), " ", hex(inst), "\n")
-	throw("runtime: misuse of rewindmorestack")
-}
-
 func prepGoExitFrame(sp uintptr)
diff --git a/src/runtime/sys_s390x.go b/src/runtime/sys_s390x.go
index 2aa81e7..e710840 100644
--- a/src/runtime/sys_s390x.go
+++ b/src/runtime/sys_s390x.go
@@ -16,30 +16,3 @@ func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
 	buf.pc = uintptr(fn)
 	buf.ctxt = ctxt
 }
-
-// Called to rewind context saved during morestack back to beginning of function.
-// To help us, the linker emits a jmp back to the beginning right after the
-// call to morestack. We just have to decode and apply that jump.
-func rewindmorestack(buf *gobuf) {
-	var inst uint64
-	if buf.pc&1 == 0 && buf.pc != 0 {
-		inst = *(*uint64)(unsafe.Pointer(buf.pc))
-		switch inst >> 48 {
-		case 0xa7f4: // BRC (branch relative on condition) instruction.
-			inst >>= 32
-			inst &= 0xFFFF
-			offset := int64(int16(inst))
-			offset <<= 1
-			buf.pc += uintptr(offset)
-			return
-		case 0xc0f4: // BRCL (branch relative on condition long) instruction.
-			inst >>= 16
-			inst = inst & 0xFFFFFFFF
-			inst = (inst << 1) & 0xFFFFFFFF
-			buf.pc += uintptr(int32(inst))
-			return
-		}
-	}
-	print("runtime: pc=", hex(buf.pc), " ", hex(inst), "\n")
-	throw("runtime: misuse of rewindmorestack")
-}
diff --git a/src/runtime/sys_solaris_amd64.s b/src/runtime/sys_solaris_amd64.s
index 07a7ace..c542db3 100644
--- a/src/runtime/sys_solaris_amd64.s
+++ b/src/runtime/sys_solaris_amd64.s
@@ -289,6 +289,19 @@ exit:
 	ADDQ    $184, SP
 	RET
 
+TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
+	MOVQ	fn+0(FP),    AX
+	MOVL	sig+8(FP),   DI
+	MOVQ	info+16(FP), SI
+	MOVQ	ctx+24(FP),  DX
+	PUSHQ	BP
+	MOVQ	SP, BP
+	ANDQ	$~15, SP     // alignment for x86_64 ABI
+	CALL	AX
+	MOVQ	BP, SP
+	POPQ	BP
+	RET
+
 // Called from runtime·usleep (Go). Can be called on Go stack, on OS stack,
 // can also be called in cgo callback path without a g->m.
 TEXT runtime·usleep1(SB),NOSPLIT,$0
diff --git a/src/runtime/sys_windows_386.s b/src/runtime/sys_windows_386.s
index 95130b7..bd5de33 100644
--- a/src/runtime/sys_windows_386.s
+++ b/src/runtime/sys_windows_386.s
@@ -192,7 +192,7 @@ TEXT runtime·externalthreadhandler(SB),NOSPLIT,$0
 	SUBL	$m__size, SP		// space for M
 	MOVL	SP, 0(SP)
 	MOVL	$m__size, 4(SP)
-	CALL	runtime·memclr(SB)	// smashes AX,BX,CX
+	CALL	runtime·memclrNoHeapPointers(SB)	// smashes AX,BX,CX
 
 	LEAL	m_tls(SP), CX
 	MOVL	CX, 0x14(FS)
@@ -203,7 +203,7 @@ TEXT runtime·externalthreadhandler(SB),NOSPLIT,$0
 
 	MOVL	SP, 0(SP)
 	MOVL	$g__size, 4(SP)
-	CALL	runtime·memclr(SB)	// smashes AX,BX,CX
+	CALL	runtime·memclrNoHeapPointers(SB)	// smashes AX,BX,CX
 	LEAL	g__size(SP), BX
 	MOVL	BX, g_m(SP)
 
@@ -309,7 +309,7 @@ TEXT runtime·callbackasm1+0(SB),NOSPLIT,$0
 
 // void tstart(M *newm);
 TEXT runtime·tstart(SB),NOSPLIT,$0
-	MOVL	newm+4(SP), CX		// m
+	MOVL	newm+0(FP), CX		// m
 	MOVL	m_g0(CX), DX		// g
 
 	// Layout new m scheduler stack on os stack.
@@ -337,7 +337,7 @@ TEXT runtime·tstart(SB),NOSPLIT,$0
 
 // uint32 tstart_stdcall(M *newm);
 TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0
-	MOVL	newm+4(SP), BX
+	MOVL	newm+0(FP), BX
 
 	PUSHL	BX
 	CALL	runtime·tstart(SB)
diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s
index 9c19737..c61b79d 100644
--- a/src/runtime/sys_windows_amd64.s
+++ b/src/runtime/sys_windows_amd64.s
@@ -45,6 +45,14 @@ loadregs:
 	MOVQ	8(SI), DX
 	MOVQ	16(SI), R8
 	MOVQ	24(SI), R9
+	// Floating point arguments are passed in the XMM
+	// registers. Set them here in case any of the arguments
+	// are floating point values. For details see
+	//	https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx
+	MOVQ	CX, X0
+	MOVQ	DX, X1
+	MOVQ	R8, X2
+	MOVQ	R9, X3
 
 	// Call stdcall function.
 	CALL	AX
@@ -228,7 +236,7 @@ TEXT runtime·externalthreadhandler(SB),NOSPLIT|NOFRAME,$0
 	SUBQ	$m__size, SP		// space for M
 	MOVQ	SP, 0(SP)
 	MOVQ	$m__size, 8(SP)
-	CALL	runtime·memclr(SB)	// smashes AX,BX,CX, maybe BP
+	CALL	runtime·memclrNoHeapPointers(SB)	// smashes AX,BX,CX, maybe BP
 
 	LEAQ	m_tls(SP), CX
 	MOVQ	CX, 0x28(GS)
@@ -239,7 +247,7 @@ TEXT runtime·externalthreadhandler(SB),NOSPLIT|NOFRAME,$0
 
 	MOVQ	SP, 0(SP)
 	MOVQ	$g__size, 8(SP)
-	CALL	runtime·memclr(SB)	// smashes AX,BX,CX, maybe BP
+	CALL	runtime·memclrNoHeapPointers(SB)	// smashes AX,BX,CX, maybe BP
 	LEAQ	g__size(SP), BX
 	MOVQ	BX, g_m(SP)
 
diff --git a/src/runtime/sys_x86.go b/src/runtime/sys_x86.go
index f6e45cc..7e4e273 100644
--- a/src/runtime/sys_x86.go
+++ b/src/runtime/sys_x86.go
@@ -25,33 +25,3 @@ func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) {
 	buf.pc = uintptr(fn)
 	buf.ctxt = ctxt
 }
-
-// Called to rewind context saved during morestack back to beginning of function.
-// To help us, the linker emits a jmp back to the beginning right after the
-// call to morestack. We just have to decode and apply that jump.
-func rewindmorestack(buf *gobuf) {
-	pc := (*[8]byte)(unsafe.Pointer(buf.pc))
-	if pc[0] == 0xe9 { // jmp 4-byte offset
-		buf.pc = buf.pc + 5 + uintptr(int64(*(*int32)(unsafe.Pointer(&pc[1]))))
-		return
-	}
-	if pc[0] == 0xeb { // jmp 1-byte offset
-		buf.pc = buf.pc + 2 + uintptr(int64(*(*int8)(unsafe.Pointer(&pc[1]))))
-		return
-	}
-	if pc[0] == 0xcc {
-		// This is a breakpoint inserted by gdb. We could use
-		// runtime·findfunc to find the function. But if we
-		// do that, then we will continue execution at the
-		// function entry point, and we will not hit the gdb
-		// breakpoint. So for this case we don't change
-		// buf.pc, so that when we return we will execute
-		// the jump instruction and carry on. This means that
-		// stack unwinding may not work entirely correctly
-		// (https://golang.org/issue/5723) but the user is
-		// running under gdb anyhow.
-		return
-	}
-	print("runtime: pc=", pc, " ", hex(pc[0]), " ", hex(pc[1]), " ", hex(pc[2]), " ", hex(pc[3]), " ", hex(pc[4]), "\n")
-	throw("runtime: misuse of rewindmorestack")
-}
diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go
index 4a10749..11e67df 100644
--- a/src/runtime/syscall_windows_test.go
+++ b/src/runtime/syscall_windows_test.go
@@ -10,6 +10,7 @@ import (
 	"internal/syscall/windows/sysdll"
 	"internal/testenv"
 	"io/ioutil"
+	"math"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -622,6 +623,61 @@ uintptr_t cfunc(callback f, uintptr_t n) {
 	}
 }
 
+func TestFloatArgs(t *testing.T) {
+	if _, err := exec.LookPath("gcc"); err != nil {
+		t.Skip("skipping test: gcc is missing")
+	}
+	if runtime.GOARCH != "amd64" {
+		t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
+	}
+
+	const src = `
+#include <stdint.h>
+#include <windows.h>
+
+uintptr_t cfunc(uintptr_t a, double b, float c, double d) {
+	if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
+		return 1;
+	}
+	return 0;
+}
+`
+	tmpdir, err := ioutil.TempDir("", "TestFloatArgs")
+	if err != nil {
+		t.Fatal("TempDir failed: ", err)
+	}
+	defer os.RemoveAll(tmpdir)
+
+	srcname := "mydll.c"
+	err = ioutil.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	outname := "mydll.dll"
+	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
+	cmd.Dir = tmpdir
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("failed to build dll: %v - %v", err, string(out))
+	}
+	dllpath := filepath.Join(tmpdir, outname)
+
+	dll := syscall.MustLoadDLL(dllpath)
+	defer dll.Release()
+
+	proc := dll.MustFindProc("cfunc")
+
+	r, _, err := proc.Call(
+		1,
+		uintptr(math.Float64bits(2.2)),
+		uintptr(math.Float32bits(3.3)),
+		uintptr(math.Float64bits(4.4e44)),
+	)
+	if r != 1 {
+		t.Errorf("got %d want 1 (err=%v)", r, err)
+	}
+}
+
 func TestTimeBeginPeriod(t *testing.T) {
 	const TIMERR_NOERROR = 0
 	if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR {
@@ -972,3 +1028,41 @@ func BenchmarkOsYield(b *testing.B) {
 		runtime.OsYield()
 	}
 }
+
+func BenchmarkRunningGoProgram(b *testing.B) {
+	tmpdir, err := ioutil.TempDir("", "BenchmarkRunningGoProgram")
+	if err != nil {
+		b.Fatal(err)
+	}
+	defer os.RemoveAll(tmpdir)
+
+	src := filepath.Join(tmpdir, "main.go")
+	err = ioutil.WriteFile(src, []byte(benchmarkRunnigGoProgram), 0666)
+	if err != nil {
+		b.Fatal(err)
+	}
+
+	exe := filepath.Join(tmpdir, "main.exe")
+	cmd := exec.Command("go", "build", "-o", exe, src)
+	cmd.Dir = tmpdir
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		b.Fatalf("building main.exe failed: %v\n%s", err, out)
+	}
+
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		cmd := exec.Command(exe)
+		out, err := cmd.CombinedOutput()
+		if err != nil {
+			b.Fatalf("runing main.exe failed: %v\n%s", err, out)
+		}
+	}
+}
+
+const benchmarkRunnigGoProgram = `
+package main
+
+func main() {
+}
+`
diff --git a/src/runtime/testdata/testprog/deadlock.go b/src/runtime/testdata/testprog/deadlock.go
index c938fcf..ca2be57 100644
--- a/src/runtime/testdata/testprog/deadlock.go
+++ b/src/runtime/testdata/testprog/deadlock.go
@@ -32,6 +32,7 @@ func init() {
 	register("PanicTraceback", PanicTraceback)
 	register("GoschedInPanic", GoschedInPanic)
 	register("SyscallInPanic", SyscallInPanic)
+	register("PanicLoop", PanicLoop)
 }
 
 func SimpleDeadlock() {
@@ -214,3 +215,13 @@ func pt2() {
 	}()
 	panic("hello")
 }
+
+type panicError struct{}
+
+func (*panicError) Error() string {
+	panic("double error")
+}
+
+func PanicLoop() {
+	panic(&panicError{})
+}
diff --git a/src/runtime/testdata/testprog/gc.go b/src/runtime/testdata/testprog/gc.go
index a0c1f82..744b610 100644
--- a/src/runtime/testdata/testprog/gc.go
+++ b/src/runtime/testdata/testprog/gc.go
@@ -98,11 +98,25 @@ func GCFairness2() {
 	// If the scheduling rules change, this may not be enough time
 	// to let all goroutines run, but for now we cycle through
 	// them rapidly.
+	//
+	// OpenBSD's scheduler makes every usleep() take at least
+	// 20ms, so we need a long time to ensure all goroutines have
+	// run. If they haven't run after 30ms, give it another 1000ms
+	// and check again.
 	time.Sleep(30 * time.Millisecond)
+	var fail bool
 	for i := range count {
 		if atomic.LoadInt64(&count[i]) == 0 {
-			fmt.Printf("goroutine %d did not run\n", i)
-			return
+			fail = true
+		}
+	}
+	if fail {
+		time.Sleep(1 * time.Second)
+		for i := range count {
+			if atomic.LoadInt64(&count[i]) == 0 {
+				fmt.Printf("goroutine %d did not run\n", i)
+				return
+			}
 		}
 	}
 	fmt.Println("OK")
diff --git a/src/runtime/testdata/testprog/map.go b/src/runtime/testdata/testprog/map.go
new file mode 100644
index 0000000..5524289
--- /dev/null
+++ b/src/runtime/testdata/testprog/map.go
@@ -0,0 +1,77 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "runtime"
+
+func init() {
+	register("concurrentMapWrites", concurrentMapWrites)
+	register("concurrentMapReadWrite", concurrentMapReadWrite)
+	register("concurrentMapIterateWrite", concurrentMapIterateWrite)
+}
+
+func concurrentMapWrites() {
+	m := map[int]int{}
+	c := make(chan struct{})
+	go func() {
+		for i := 0; i < 10000; i++ {
+			m[5] = 0
+			runtime.Gosched()
+		}
+		c <- struct{}{}
+	}()
+	go func() {
+		for i := 0; i < 10000; i++ {
+			m[6] = 0
+			runtime.Gosched()
+		}
+		c <- struct{}{}
+	}()
+	<-c
+	<-c
+}
+
+func concurrentMapReadWrite() {
+	m := map[int]int{}
+	c := make(chan struct{})
+	go func() {
+		for i := 0; i < 10000; i++ {
+			m[5] = 0
+			runtime.Gosched()
+		}
+		c <- struct{}{}
+	}()
+	go func() {
+		for i := 0; i < 10000; i++ {
+			_ = m[6]
+			runtime.Gosched()
+		}
+		c <- struct{}{}
+	}()
+	<-c
+	<-c
+}
+
+func concurrentMapIterateWrite() {
+	m := map[int]int{}
+	c := make(chan struct{})
+	go func() {
+		for i := 0; i < 10000; i++ {
+			m[5] = 0
+			runtime.Gosched()
+		}
+		c <- struct{}{}
+	}()
+	go func() {
+		for i := 0; i < 10000; i++ {
+			for range m {
+			}
+			runtime.Gosched()
+		}
+		c <- struct{}{}
+	}()
+	<-c
+	<-c
+}
diff --git a/src/runtime/testdata/testprogcgo/pprof.go b/src/runtime/testdata/testprogcgo/pprof.go
index cb30ec5..4460b93 100644
--- a/src/runtime/testdata/testprogcgo/pprof.go
+++ b/src/runtime/testdata/testprogcgo/pprof.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The Go Authors.  All rights reserved.
+// Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
diff --git a/src/runtime/testdata/testprogcgo/raceprof.go b/src/runtime/testdata/testprogcgo/raceprof.go
new file mode 100644
index 0000000..fe624c5
--- /dev/null
+++ b/src/runtime/testdata/testprogcgo/raceprof.go
@@ -0,0 +1,78 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,amd64
+
+package main
+
+// Test that we can collect a lot of colliding profiling signals from
+// an external C thread. This used to fail when built with the race
+// detector, because a call of the predeclared function copy was
+// turned into a call to runtime.slicecopy, which is not marked nosplit.
+
+/*
+#include <signal.h>
+#include <stdint.h>
+#include <pthread.h>
+#include <sched.h>
+
+struct cgoTracebackArg {
+	uintptr_t  context;
+	uintptr_t  sigContext;
+	uintptr_t* buf;
+	uintptr_t  max;
+};
+
+static int raceprofCount;
+
+// We want a bunch of different profile stacks that collide in the
+// hash table maintained in runtime/cpuprof.go. This code knows the
+// size of the hash table (1 << 10) and knows that the hash function
+// is simply multiplicative.
+void raceprofTraceback(void* parg) {
+	struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg);
+	raceprofCount++;
+	arg->buf[0] = raceprofCount * (1 << 10);
+	arg->buf[1] = 0;
+}
+
+static void* raceprofThread(void* p) {
+	int i;
+
+	for (i = 0; i < 100; i++) {
+		pthread_kill(pthread_self(), SIGPROF);
+		sched_yield();
+	}
+	return 0;
+}
+
+void runRaceprofThread() {
+	pthread_t tid;
+	pthread_create(&tid, 0, raceprofThread, 0);
+	pthread_join(tid, 0);
+}
+*/
+import "C"
+
+import (
+	"bytes"
+	"fmt"
+	"runtime"
+	"runtime/pprof"
+	"unsafe"
+)
+
+func init() {
+	register("CgoRaceprof", CgoRaceprof)
+}
+
+func CgoRaceprof() {
+	runtime.SetCgoTraceback(0, unsafe.Pointer(C.raceprofTraceback), nil, nil)
+
+	var buf bytes.Buffer
+	pprof.StartCPUProfile(&buf)
+
+	C.runRaceprofThread()
+	fmt.Println("OK")
+}
diff --git a/src/runtime/testdata/testprogcgo/racesig.go b/src/runtime/testdata/testprogcgo/racesig.go
new file mode 100644
index 0000000..d0c1c3c
--- /dev/null
+++ b/src/runtime/testdata/testprogcgo/racesig.go
@@ -0,0 +1,102 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux,amd64
+
+package main
+
+// Test that an external C thread that is calling malloc can be hit
+// with SIGCHLD signals. This used to fail when built with the race
+// detector, because in that case the signal handler would indirectly
+// call the C malloc function.
+
+/*
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <sched.h>
+#include <unistd.h>
+
+#define ALLOCERS 100
+#define SIGNALERS 10
+
+static void* signalThread(void* p) {
+	pthread_t* pt = (pthread_t*)(p);
+	int i, j;
+
+	for (i = 0; i < 100; i++) {
+		for (j = 0; j < ALLOCERS; j++) {
+			if (pthread_kill(pt[j], SIGCHLD) < 0) {
+				return NULL;
+			}
+		}
+		usleep(1);
+	}
+	return NULL;
+}
+
+#define CALLS 100
+
+static void* mallocThread(void* p) {
+	int i;
+	void *a[CALLS];
+
+	for (i = 0; i < ALLOCERS; i++) {
+		sched_yield();
+	}
+	for (i = 0; i < CALLS; i++) {
+		a[i] = malloc(i);
+	}
+	for (i = 0; i < CALLS; i++) {
+		free(a[i]);
+	}
+	return NULL;
+}
+
+void runRaceSignalThread() {
+	int i;
+	pthread_t m[ALLOCERS];
+	pthread_t s[SIGNALERS];
+
+	for (i = 0; i < ALLOCERS; i++) {
+		pthread_create(&m[i], NULL, mallocThread, NULL);
+	}
+	for (i = 0; i < SIGNALERS; i++) {
+		pthread_create(&s[i], NULL, signalThread, &m[0]);
+	}
+	for (i = 0; i < SIGNALERS; i++) {
+		pthread_join(s[i], NULL);
+	}
+	for (i = 0; i < ALLOCERS; i++) {
+		pthread_join(m[i], NULL);
+	}
+}
+*/
+import "C"
+
+import (
+	"fmt"
+	"os"
+	"time"
+)
+
+func init() {
+	register("CgoRaceSignal", CgoRaceSignal)
+}
+
+func CgoRaceSignal() {
+	// The failure symptom is that the program hangs because of a
+	// deadlock in malloc, so set an alarm.
+	go func() {
+		time.Sleep(5 * time.Second)
+		fmt.Println("Hung for 5 seconds")
+		os.Exit(1)
+	}()
+
+	C.runRaceSignalThread()
+	fmt.Println("OK")
+}
diff --git a/src/runtime/testdata/testprogcgo/threadpprof.go b/src/runtime/testdata/testprogcgo/threadpprof.go
index fdeee69..44afb91 100644
--- a/src/runtime/testdata/testprogcgo/threadpprof.go
+++ b/src/runtime/testdata/testprogcgo/threadpprof.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The Go Authors.  All rights reserved.
+// Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
@@ -39,17 +39,6 @@ struct cgoTracebackArg {
 	uintptr_t  max;
 };
 
-static void *pprofThread(void* p) {
-	time_t start;
-
-	(void)p;
-	start = time(NULL);
-	while (__sync_add_and_fetch(&cpuHogThreadCount, 0) < 2 && time(NULL) - start < 2) {
-		cpuHogThread();
-	}
-}
-
-
 // pprofCgoThreadTraceback is passed to runtime.SetCgoTraceback.
 // For testing purposes it pretends that all CPU hits in C code are in cpuHog.
 void pprofCgoThreadTraceback(void* parg) {
@@ -64,6 +53,18 @@ void pprofCgoThreadTraceback(void* parg) {
 int getCPUHogThreadCount() {
 	return __sync_add_and_fetch(&cpuHogThreadCount, 0);
 }
+
+static void* cpuHogDriver(void* arg __attribute__ ((unused))) {
+	while (1) {
+		cpuHogThread();
+	}
+	return 0;
+}
+
+void runCPUHogThread() {
+	pthread_t tid;
+	pthread_create(&tid, 0, cpuHogDriver, 0);
+}
 */
 import "C"
 
@@ -79,11 +80,19 @@ import (
 
 func init() {
 	register("CgoPprofThread", CgoPprofThread)
+	register("CgoPprofThreadNoTraceback", CgoPprofThreadNoTraceback)
 }
 
 func CgoPprofThread() {
 	runtime.SetCgoTraceback(0, unsafe.Pointer(C.pprofCgoThreadTraceback), nil, nil)
+	pprofThread()
+}
+
+func CgoPprofThreadNoTraceback() {
+	pprofThread()
+}
 
+func pprofThread() {
 	f, err := ioutil.TempFile("", "prof")
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err)
@@ -95,6 +104,8 @@ func CgoPprofThread() {
 		os.Exit(2)
 	}
 
+	C.runCPUHogThread()
+
 	t0 := time.Now()
 	for C.getCPUHogThreadCount() < 2 && time.Since(t0) < time.Second {
 		time.Sleep(100 * time.Millisecond)
diff --git a/src/runtime/testdata/testprogcgo/threadprof.go b/src/runtime/testdata/testprogcgo/threadprof.go
index a77479d..2d4c103 100644
--- a/src/runtime/testdata/testprogcgo/threadprof.go
+++ b/src/runtime/testdata/testprogcgo/threadprof.go
@@ -2,7 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// We only build this file with the tag "threadprof", since it starts
+// a thread running a busy loop at constructor time.
+
 // +build !plan9,!windows
+// +build threadprof
 
 package main
 
@@ -21,6 +25,7 @@ static void *thread1(void *p) {
 	spinlock = 0;
 	return NULL;
 }
+
 __attribute__((constructor)) void issue9456() {
 	pthread_t tid;
 	pthread_create(&tid, 0, thread1, NULL);
@@ -84,8 +89,8 @@ func CgoExternalThreadSignal() {
 
 	out, err := exec.Command(os.Args[0], "CgoExternalThreadSignal", "crash").CombinedOutput()
 	if err == nil {
-		fmt.Println("C signal did not crash as expected\n")
-		fmt.Printf("%s\n", out)
+		fmt.Println("C signal did not crash as expected")
+		fmt.Printf("\n%s\n", out)
 		os.Exit(1)
 	}
 
diff --git a/src/runtime/time.go b/src/runtime/time.go
index 8df185d..604ccde 100644
--- a/src/runtime/time.go
+++ b/src/runtime/time.go
@@ -88,12 +88,12 @@ func addtimer(t *timer) {
 	unlock(&timers.lock)
 }
 
-// Add a timer to the heap and start or kick the timer proc.
-// If the new timer is earlier than any of the others.
+// Add a timer to the heap and start or kick timerproc if the new timer is
+// earlier than any of the others.
 // Timers are locked.
 func addtimerLocked(t *timer) {
 	// when must never be negative; otherwise timerproc will overflow
-	// during its delta calculation and never expire other runtime·timers.
+	// during its delta calculation and never expire other runtime timers.
 	if t.when < 0 {
 		t.when = 1<<63 - 1
 	}
@@ -150,7 +150,7 @@ func deltimer(t *timer) bool {
 
 // Timerproc runs the time-driven events.
 // It sleeps until the next event in the timers heap.
-// If addtimer inserts a new earlier event, addtimer1 wakes timerproc early.
+// If addtimer inserts a new earlier event, it wakes timerproc early.
 func timerproc() {
 	timers.gp = getg()
 	for {
diff --git a/src/runtime/tls_mipsx.s b/src/runtime/tls_mipsx.s
new file mode 100644
index 0000000..95fbc32
--- /dev/null
+++ b/src/runtime/tls_mipsx.s
@@ -0,0 +1,21 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "go_asm.h"
+#include "go_tls.h"
+#include "funcdata.h"
+#include "textflag.h"
+
+// If !iscgo, this is a no-op.
+TEXT runtime·save_g(SB),NOSPLIT,$-4-0
+	MOVB	runtime·iscgo(SB), R23
+	BEQ	R23, nocgo
+	UNDEF
+nocgo:
+	RET
+
+TEXT runtime·load_g(SB),NOSPLIT,$-4-0
+	RET
diff --git a/src/runtime/trace.go b/src/runtime/trace.go
index 092f941..a8f4ab6 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -28,8 +28,8 @@ const (
 	traceEvProcStop       = 6  // stop of P [timestamp]
 	traceEvGCStart        = 7  // GC start [timestamp, seq, stack id]
 	traceEvGCDone         = 8  // GC done [timestamp]
-	traceEvGCScanStart    = 9  // GC scan start [timestamp]
-	traceEvGCScanDone     = 10 // GC scan done [timestamp]
+	traceEvGCScanStart    = 9  // GC mark termination start [timestamp]
+	traceEvGCScanDone     = 10 // GC mark termination done [timestamp]
 	traceEvGCSweepStart   = 11 // GC sweep start [timestamp, stack id]
 	traceEvGCSweepDone    = 12 // GC sweep done [timestamp]
 	traceEvGoCreate       = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id]
@@ -60,7 +60,9 @@ const (
 	traceEvGoStartLocal   = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id]
 	traceEvGoUnblockLocal = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack]
 	traceEvGoSysExitLocal = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp]
-	traceEvCount          = 41
+	traceEvGoStartLabel   = 41 // goroutine starts running with label [timestamp, goroutine id, seq, label string id]
+	traceEvGoBlockGC      = 42 // goroutine blocks on GC assist [timestamp, stack]
+	traceEvCount          = 43
 )
 
 const (
@@ -112,15 +114,20 @@ var trace struct {
 	empty         traceBufPtr // stack of empty buffers
 	fullHead      traceBufPtr // queue of full buffers
 	fullTail      traceBufPtr
-	reader        *g              // goroutine that called ReadTrace, or nil
+	reader        guintptr        // goroutine that called ReadTrace, or nil
 	stackTab      traceStackTable // maps stack traces to unique ids
 
 	// Dictionary for traceEvString.
-	// Currently this is used only for func/file:line info after tracing session,
-	// so we assume single-threaded access.
+	//
+	// Currently this is used only at trace setup and for
+	// func/file:line info after tracing session, so we assume
+	// single-threaded access.
 	strings   map[string]uint64
 	stringSeq uint64
 
+	// markWorkerLabels maps gcMarkWorkerMode to string ID.
+	markWorkerLabels [len(gcMarkWorkerModeStrings)]uint64
+
 	bufLock mutex       // protects buf
 	buf     traceBufPtr // global trace buffer, used when running without a p
 }
@@ -134,6 +141,8 @@ type traceBufHeader struct {
 }
 
 // traceBuf is per-P tracing buffer.
+//
+//go:notinheap
 type traceBuf struct {
 	traceBufHeader
 	arr [64<<10 - unsafe.Sizeof(traceBufHeader{})]byte // underlying buffer for traceBufHeader.buf
@@ -144,6 +153,8 @@ type traceBuf struct {
 // allocated from the GC'd heap, so this is safe, and are often
 // manipulated in contexts where write barriers are not allowed, so
 // this is necessary.
+//
+// TODO: Since traceBuf is now go:notinheap, this isn't necessary.
 type traceBufPtr uintptr
 
 func (tp traceBufPtr) ptr() *traceBuf   { return (*traceBuf)(unsafe.Pointer(tp)) }
@@ -184,10 +195,21 @@ func StartTrace() error {
 	// trace.enabled is set afterwards once we have emitted all preliminary events.
 	_g_ := getg()
 	_g_.m.startingtrace = true
+
+	// Obtain current stack ID to use in all traceEvGoCreate events below.
+	mp := acquirem()
+	stkBuf := make([]uintptr, traceStackSize)
+	stackID := traceStackID(mp, stkBuf, 2)
+	releasem(mp)
+
 	for _, gp := range allgs {
 		status := readgstatus(gp)
 		if status != _Gdead {
-			traceGoCreate(gp, gp.startpc) // also resets gp.traceseq/tracelastp
+			gp.traceseq = 0
+			gp.tracelastp = getg().m.p
+			// +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum.
+			id := trace.stackTab.put([]uintptr{gp.startpc + sys.PCQuantum})
+			traceEvent(traceEvGoCreate, -1, uint64(gp.goid), uint64(id), stackID)
 		}
 		if status == _Gwaiting {
 			// traceEvGoWaiting is implied to have seq=1.
@@ -217,6 +239,18 @@ func StartTrace() error {
 	_g_.m.startingtrace = false
 	trace.enabled = true
 
+	// Register runtime goroutine labels.
+	_, pid, bufp := traceAcquireBuffer()
+	buf := (*bufp).ptr()
+	if buf == nil {
+		buf = traceFlush(0).ptr()
+		(*bufp).set(buf)
+	}
+	for i, label := range gcMarkWorkerModeStrings[:] {
+		trace.markWorkerLabels[i], buf = traceString(buf, label)
+	}
+	traceReleaseBuffer(pid)
+
 	unlock(&trace.bufLock)
 
 	startTheWorld()
@@ -251,10 +285,12 @@ func StopTrace() {
 			p.tracebuf = 0
 		}
 	}
-	if trace.buf != 0 && trace.buf.ptr().pos != 0 {
+	if trace.buf != 0 {
 		buf := trace.buf
 		trace.buf = 0
-		traceFullQueue(buf)
+		if buf.ptr().pos != 0 {
+			traceFullQueue(buf)
+		}
 	}
 
 	for {
@@ -275,7 +311,7 @@ func StopTrace() {
 
 	// The world is started but we've set trace.shutdown, so new tracing can't start.
 	// Wait for the trace reader to flush pending buffers and stop.
-	semacquire(&trace.shutdownSema, false)
+	semacquire(&trace.shutdownSema, 0)
 	if raceenabled {
 		raceacquire(unsafe.Pointer(&trace.shutdownSema))
 	}
@@ -296,7 +332,7 @@ func StopTrace() {
 	if trace.fullHead != 0 || trace.fullTail != 0 {
 		throw("trace: non-empty full trace buffer")
 	}
-	if trace.reading != 0 || trace.reader != nil {
+	if trace.reading != 0 || trace.reader != 0 {
 		throw("trace: reading after shutdown")
 	}
 	for trace.empty != 0 {
@@ -324,7 +360,7 @@ func ReadTrace() []byte {
 	lock(&trace.lock)
 	trace.lockOwner = getg()
 
-	if trace.reader != nil {
+	if trace.reader != 0 {
 		// More than one goroutine reads trace. This is bad.
 		// But we rather do not crash the program because of tracing,
 		// because tracing can be enabled at runtime on prod servers.
@@ -344,11 +380,11 @@ func ReadTrace() []byte {
 		trace.headerWritten = true
 		trace.lockOwner = nil
 		unlock(&trace.lock)
-		return []byte("go 1.7 trace\x00\x00\x00\x00")
+		return []byte("go 1.8 trace\x00\x00\x00\x00")
 	}
 	// Wait for new data.
 	if trace.fullHead == 0 && !trace.shutdown {
-		trace.reader = getg()
+		trace.reader.set(getg())
 		goparkunlock(&trace.lock, "trace reader (blocked)", traceEvGoBlock, 2)
 		lock(&trace.lock)
 	}
@@ -402,16 +438,16 @@ func ReadTrace() []byte {
 
 // traceReader returns the trace reader that should be woken up, if any.
 func traceReader() *g {
-	if trace.reader == nil || (trace.fullHead == 0 && !trace.shutdown) {
+	if trace.reader == 0 || (trace.fullHead == 0 && !trace.shutdown) {
 		return nil
 	}
 	lock(&trace.lock)
-	if trace.reader == nil || (trace.fullHead == 0 && !trace.shutdown) {
+	if trace.reader == 0 || (trace.fullHead == 0 && !trace.shutdown) {
 		unlock(&trace.lock)
 		return nil
 	}
-	gp := trace.reader
-	trace.reader = nil
+	gp := trace.reader.ptr()
+	trace.reader.set(nil)
 	unlock(&trace.lock)
 	return gp
 }
@@ -513,28 +549,7 @@ func traceEvent(ev byte, skip int, args ...uint64) {
 	if skip == 0 {
 		buf.varint(0)
 	} else if skip > 0 {
-		_g_ := getg()
-		gp := mp.curg
-		var nstk int
-		if gp == _g_ {
-			nstk = callers(skip, buf.stk[:])
-		} else if gp != nil {
-			gp = mp.curg
-			// This may happen when tracing a system call,
-			// so we must lock the stack.
-			if gcTryLockStackBarriers(gp) {
-				nstk = gcallers(gp, skip, buf.stk[:])
-				gcUnlockStackBarriers(gp)
-			}
-		}
-		if nstk > 0 {
-			nstk-- // skip runtime.goexit
-		}
-		if nstk > 0 && gp.goid == 1 {
-			nstk-- // skip runtime.main
-		}
-		id := trace.stackTab.put(buf.stk[:nstk])
-		buf.varint(uint64(id))
+		buf.varint(traceStackID(mp, buf.stk[:], skip))
 	}
 	evSize := buf.pos - startPos
 	if evSize > maxSize {
@@ -547,6 +562,31 @@ func traceEvent(ev byte, skip int, args ...uint64) {
 	traceReleaseBuffer(pid)
 }
 
+func traceStackID(mp *m, buf []uintptr, skip int) uint64 {
+	_g_ := getg()
+	gp := mp.curg
+	var nstk int
+	if gp == _g_ {
+		nstk = callers(skip+1, buf[:])
+	} else if gp != nil {
+		gp = mp.curg
+		// This may happen when tracing a system call,
+		// so we must lock the stack.
+		if gcTryLockStackBarriers(gp) {
+			nstk = gcallers(gp, skip, buf[:])
+			gcUnlockStackBarriers(gp)
+		}
+	}
+	if nstk > 0 {
+		nstk-- // skip runtime.goexit
+	}
+	if nstk > 0 && gp.goid == 1 {
+		nstk-- // skip runtime.main
+	}
+	id := trace.stackTab.put(buf[:nstk])
+	return uint64(id)
+}
+
 // traceAcquireBuffer returns trace buffer to use and, if necessary, locks it.
 func traceAcquireBuffer() (mp *m, pid int32, bufp *traceBufPtr) {
 	mp = acquirem()
@@ -811,11 +851,14 @@ type traceAlloc struct {
 // traceAllocBlock is allocated from non-GC'd memory, so it must not
 // contain heap pointers. Writes to pointers to traceAllocBlocks do
 // not need write barriers.
+//
+//go:notinheap
 type traceAllocBlock struct {
 	next traceAllocBlockPtr
 	data [64<<10 - sys.PtrSize]byte
 }
 
+// TODO: Since traceAllocBlock is now go:notinheap, this isn't necessary.
 type traceAllocBlockPtr uintptr
 
 func (p traceAllocBlockPtr) ptr() *traceAllocBlock   { return (*traceAllocBlock)(unsafe.Pointer(p)) }
@@ -908,7 +951,9 @@ func traceGoStart() {
 	_g_ := getg().m.curg
 	_p_ := _g_.m.p
 	_g_.traceseq++
-	if _g_.tracelastp == _p_ {
+	if _g_ == _p_.ptr().gcBgMarkWorker.ptr() {
+		traceEvent(traceEvGoStartLabel, -1, uint64(_g_.goid), _g_.traceseq, trace.markWorkerLabels[_p_.ptr().gcMarkWorkerMode])
+	} else if _g_.tracelastp == _p_ {
 		traceEvent(traceEvGoStartLocal, -1, uint64(_g_.goid))
 	} else {
 		_g_.tracelastp = _p_
@@ -989,5 +1034,10 @@ func traceHeapAlloc() {
 }
 
 func traceNextGC() {
-	traceEvent(traceEvNextGC, -1, memstats.next_gc)
+	if memstats.next_gc == ^uint64(0) {
+		// Heap-based triggering is disabled.
+		traceEvent(traceEvNextGC, -1, 0)
+	} else {
+		traceEvent(traceEvNextGC, -1, memstats.next_gc)
+	}
 }
diff --git a/src/runtime/trace/trace_stack_test.go b/src/runtime/trace/trace_stack_test.go
index 52a71bf..c37b33d 100644
--- a/src/runtime/trace/trace_stack_test.go
+++ b/src/runtime/trace/trace_stack_test.go
@@ -85,7 +85,8 @@ func TestTraceSymbolize(t *testing.T) {
 	go func() {
 		c, err := ln.Accept()
 		if err != nil {
-			t.Fatalf("failed to accept: %v", err)
+			t.Errorf("failed to accept: %v", err)
+			return
 		}
 		c.Close()
 	}()
@@ -102,10 +103,10 @@ func TestTraceSymbolize(t *testing.T) {
 		pipeReadDone <- true
 	}()
 
-	time.Sleep(time.Millisecond)
+	time.Sleep(100 * time.Millisecond)
 	runtime.GC()
 	runtime.Gosched()
-	time.Sleep(time.Millisecond) // the last chance for the goroutines above to block
+	time.Sleep(100 * time.Millisecond) // the last chance for the goroutines above to block
 	done1 <- true
 	<-done2
 	select {
@@ -139,14 +140,14 @@ func TestTraceSymbolize(t *testing.T) {
 	want := []eventDesc{
 		{trace.EvGCStart, []frame{
 			{"runtime.GC", 0},
-			{"runtime/trace_test.TestTraceSymbolize", 106},
+			{"runtime/trace_test.TestTraceSymbolize", 107},
 			{"testing.tRunner", 0},
 		}},
 		{trace.EvGoStart, []frame{
 			{"runtime/trace_test.TestTraceSymbolize.func1", 37},
 		}},
 		{trace.EvGoSched, []frame{
-			{"runtime/trace_test.TestTraceSymbolize", 107},
+			{"runtime/trace_test.TestTraceSymbolize", 108},
 			{"testing.tRunner", 0},
 		}},
 		{trace.EvGoCreate, []frame{
@@ -171,7 +172,7 @@ func TestTraceSymbolize(t *testing.T) {
 		}},
 		{trace.EvGoUnblock, []frame{
 			{"runtime.chansend1", 0},
-			{"runtime/trace_test.TestTraceSymbolize", 109},
+			{"runtime/trace_test.TestTraceSymbolize", 110},
 			{"testing.tRunner", 0},
 		}},
 		{trace.EvGoBlockSend, []frame{
@@ -180,7 +181,7 @@ func TestTraceSymbolize(t *testing.T) {
 		}},
 		{trace.EvGoUnblock, []frame{
 			{"runtime.chanrecv1", 0},
-			{"runtime/trace_test.TestTraceSymbolize", 110},
+			{"runtime/trace_test.TestTraceSymbolize", 111},
 			{"testing.tRunner", 0},
 		}},
 		{trace.EvGoBlockSelect, []frame{
@@ -189,7 +190,7 @@ func TestTraceSymbolize(t *testing.T) {
 		}},
 		{trace.EvGoUnblock, []frame{
 			{"runtime.selectgo", 0},
-			{"runtime/trace_test.TestTraceSymbolize", 111},
+			{"runtime/trace_test.TestTraceSymbolize", 112},
 			{"testing.tRunner", 0},
 		}},
 		{trace.EvGoBlockSync, []frame{
@@ -198,7 +199,7 @@ func TestTraceSymbolize(t *testing.T) {
 		}},
 		{trace.EvGoUnblock, []frame{
 			{"sync.(*Mutex).Unlock", 0},
-			{"runtime/trace_test.TestTraceSymbolize", 115},
+			{"runtime/trace_test.TestTraceSymbolize", 116},
 			{"testing.tRunner", 0},
 		}},
 		{trace.EvGoBlockSync, []frame{
@@ -208,7 +209,7 @@ func TestTraceSymbolize(t *testing.T) {
 		{trace.EvGoUnblock, []frame{
 			{"sync.(*WaitGroup).Add", 0},
 			{"sync.(*WaitGroup).Done", 0},
-			{"runtime/trace_test.TestTraceSymbolize", 116},
+			{"runtime/trace_test.TestTraceSymbolize", 117},
 			{"testing.tRunner", 0},
 		}},
 		{trace.EvGoBlockCond, []frame{
@@ -217,12 +218,12 @@ func TestTraceSymbolize(t *testing.T) {
 		}},
 		{trace.EvGoUnblock, []frame{
 			{"sync.(*Cond).Signal", 0},
-			{"runtime/trace_test.TestTraceSymbolize", 117},
+			{"runtime/trace_test.TestTraceSymbolize", 118},
 			{"testing.tRunner", 0},
 		}},
 		{trace.EvGoSleep, []frame{
 			{"time.Sleep", 0},
-			{"runtime/trace_test.TestTraceSymbolize", 108},
+			{"runtime/trace_test.TestTraceSymbolize", 109},
 			{"testing.tRunner", 0},
 		}},
 	}
@@ -240,7 +241,7 @@ func TestTraceSymbolize(t *testing.T) {
 				{"syscall.Read", 0},
 				{"os.(*File).read", 0},
 				{"os.(*File).Read", 0},
-				{"runtime/trace_test.TestTraceSymbolize.func11", 101},
+				{"runtime/trace_test.TestTraceSymbolize.func11", 102},
 			}},
 		}...)
 	}
diff --git a/src/runtime/trace/trace_test.go b/src/runtime/trace/trace_test.go
index 5fad3fb..c5f64fc 100644
--- a/src/runtime/trace/trace_test.go
+++ b/src/runtime/trace/trace_test.go
@@ -6,8 +6,10 @@ package trace_test
 
 import (
 	"bytes"
+	"flag"
 	"internal/trace"
 	"io"
+	"io/ioutil"
 	"net"
 	"os"
 	"runtime"
@@ -17,6 +19,10 @@ import (
 	"time"
 )
 
+var (
+	saveTraces = flag.Bool("savetraces", false, "save traces collected by tests")
+)
+
 func TestTraceStartStop(t *testing.T) {
 	buf := new(bytes.Buffer)
 	if err := Start(buf); err != nil {
@@ -31,6 +37,7 @@ func TestTraceStartStop(t *testing.T) {
 	if size != buf.Len() {
 		t.Fatalf("trace writes after stop: %v -> %v", size, buf.Len())
 	}
+	saveTrace(t, buf, "TestTraceStartStop")
 }
 
 func TestTraceDoubleStart(t *testing.T) {
@@ -52,6 +59,7 @@ func TestTrace(t *testing.T) {
 		t.Fatalf("failed to start tracing: %v", err)
 	}
 	Stop()
+	saveTrace(t, buf, "TestTrace")
 	_, err := trace.Parse(buf, "")
 	if err == trace.ErrTimeOrder {
 		t.Skipf("skipping trace: %v", err)
@@ -233,6 +241,7 @@ func TestTraceStress(t *testing.T) {
 	runtime.GOMAXPROCS(procs)
 
 	Stop()
+	saveTrace(t, buf, "TestTraceStress")
 	trace := buf.Bytes()
 	parseTrace(t, buf)
 	testBrokenTimestamps(t, trace)
@@ -260,7 +269,8 @@ func TestTraceStressStartStop(t *testing.T) {
 
 		rp, wp, err := os.Pipe()
 		if err != nil {
-			t.Fatalf("failed to create pipe: %v", err)
+			t.Errorf("failed to create pipe: %v", err)
+			return
 		}
 		defer func() {
 			rp.Close()
@@ -336,7 +346,8 @@ func TestTraceStressStartStop(t *testing.T) {
 		// A bit of network.
 		ln, err := net.Listen("tcp", "127.0.0.1:0")
 		if err != nil {
-			t.Fatalf("listen failed: %v", err)
+			t.Errorf("listen failed: %v", err)
+			return
 		}
 		defer ln.Close()
 		go func() {
@@ -351,7 +362,8 @@ func TestTraceStressStartStop(t *testing.T) {
 		}()
 		c, err := net.Dial("tcp", ln.Addr().String())
 		if err != nil {
-			t.Fatalf("dial failed: %v", err)
+			t.Errorf("dial failed: %v", err)
+			return
 		}
 		var tmp [1]byte
 		c.Read(tmp[:])
@@ -376,6 +388,7 @@ func TestTraceStressStartStop(t *testing.T) {
 		}
 		time.Sleep(time.Millisecond)
 		Stop()
+		saveTrace(t, buf, "TestTraceStressStartStop")
 		trace := buf.Bytes()
 		parseTrace(t, buf)
 		testBrokenTimestamps(t, trace)
@@ -436,6 +449,7 @@ func TestTraceFutileWakeup(t *testing.T) {
 	done.Wait()
 
 	Stop()
+	saveTrace(t, buf, "TestTraceFutileWakeup")
 	events, _ := parseTrace(t, buf)
 	// Check that (1) trace does not contain EvFutileWakeup events and
 	// (2) there are no consecutive EvGoBlock/EvGCStart/EvGoBlock events
@@ -464,3 +478,12 @@ func TestTraceFutileWakeup(t *testing.T) {
 		}
 	}
 }
+
+func saveTrace(t *testing.T, buf *bytes.Buffer, name string) {
+	if !*saveTraces {
+		return
+	}
+	if err := ioutil.WriteFile(name+".trace", buf.Bytes(), 0600); err != nil {
+		t.Errorf("failed to write trace file: %s", err)
+	}
+}
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 80a5440..0049e82 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -107,7 +107,7 @@ func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v uns
 			}
 			frame.fn = f
 			frame.argp = uintptr(deferArgs(d))
-			frame.arglen, frame.argmap = getArgInfo(&frame, f, true)
+			frame.arglen, frame.argmap = getArgInfo(&frame, f, true, fn)
 		}
 		frame.continpc = frame.pc
 		if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
@@ -339,7 +339,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
 		// metadata recorded by f's caller.
 		if callback != nil || printing {
 			frame.argp = frame.fp + sys.MinFrameSize
-			frame.arglen, frame.argmap = getArgInfo(&frame, f, callback != nil)
+			frame.arglen, frame.argmap = getArgInfo(&frame, f, callback != nil, nil)
 		}
 
 		// Determine frame's 'continuation PC', where it can continue.
@@ -380,7 +380,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
 			}
 		}
 		if printing {
-			if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp) {
+			if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0) {
 				// Print during crash.
 				//	main(0x1, 0x2, 0x3)
 				//		/home/rsc/go/src/runtime/x.go:23 +0xf
@@ -546,19 +546,48 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
 	return n
 }
 
-func getArgInfo(frame *stkframe, f *_func, needArgMap bool) (arglen uintptr, argmap *bitvector) {
+// reflectMethodValue is a partial duplicate of reflect.methodValue.
+type reflectMethodValue struct {
+	fn    uintptr
+	stack *bitvector // args bitmap
+}
+
+// getArgInfo returns the argument frame information for a call to f
+// with call frame frame.
+//
+// This is used for both actual calls with active stack frames and for
+// deferred calls that are not yet executing. If this is an actual
+// call, ctxt must be nil (getArgInfo will retrieve what it needs from
+// the active stack frame). If this is a deferred call, ctxt must be
+// the function object that was deferred.
+func getArgInfo(frame *stkframe, f *_func, needArgMap bool, ctxt *funcval) (arglen uintptr, argmap *bitvector) {
 	arglen = uintptr(f.args)
 	if needArgMap && f.args == _ArgsSizeUnknown {
 		// Extract argument bitmaps for reflect stubs from the calls they made to reflect.
 		switch funcname(f) {
 		case "reflect.makeFuncStub", "reflect.methodValueCall":
-			arg0 := frame.sp + sys.MinFrameSize
-			fn := *(**[2]uintptr)(unsafe.Pointer(arg0))
-			if fn[0] != f.entry {
+			// These take a *reflect.methodValue as their
+			// context register.
+			var mv *reflectMethodValue
+			if ctxt != nil {
+				// This is not an actual call, but a
+				// deferred call. The function value
+				// is itself the *reflect.methodValue.
+				mv = (*reflectMethodValue)(unsafe.Pointer(ctxt))
+			} else {
+				// This is a real call that took the
+				// *reflect.methodValue as its context
+				// register and immediately saved it
+				// to 0(SP). Get the methodValue from
+				// 0(SP).
+				arg0 := frame.sp + sys.MinFrameSize
+				mv = *(**reflectMethodValue)(unsafe.Pointer(arg0))
+			}
+			if mv.fn != f.entry {
 				print("runtime: confused by ", funcname(f), "\n")
 				throw("reflect mismatch")
 			}
-			bv := (*bitvector)(unsafe.Pointer(fn[1]))
+			bv := mv.stack
 			arglen = uintptr(bv.n * sys.PtrSize)
 			argmap = bv
 		}
@@ -603,7 +632,7 @@ func printcreatedby(gp *g) {
 	// Show what created goroutine, except main goroutine (goid 1).
 	pc := gp.gopc
 	f := findfunc(pc)
-	if f != nil && showframe(f, gp) && gp.goid != 1 {
+	if f != nil && showframe(f, gp, false) && gp.goid != 1 {
 		print("created by ", funcname(f), "\n")
 		tracepc := pc // back up to CALL instruction for funcline.
 		if pc > f.entry {
@@ -683,7 +712,7 @@ func gcallers(gp *g, skip int, pcbuf []uintptr) int {
 	return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
 }
 
-func showframe(f *_func, gp *g) bool {
+func showframe(f *_func, gp *g, firstFrame bool) bool {
 	g := getg()
 	if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
 		return true
@@ -691,10 +720,12 @@ func showframe(f *_func, gp *g) bool {
 	level, _, _ := gotraceback()
 	name := funcname(f)
 
-	// Special case: always show runtime.gopanic frame, so that we can
-	// see where a panic started in the middle of a stack trace.
+	// Special case: always show runtime.gopanic frame
+	// in the middle of a stack trace, so that we can
+	// see the boundary between ordinary code and
+	// panic-induced deferred code.
 	// See golang.org/issue/5832.
-	if name == "runtime.gopanic" {
+	if name == "runtime.gopanic" && !firstFrame {
 		return true
 	}
 
@@ -1077,6 +1108,9 @@ func callCgoSymbolizer(arg *cgoSymbolizerArg) {
 		// or when on the system stack.
 		call = asmcgocall
 	}
+	if msanenabled {
+		msanwrite(unsafe.Pointer(arg), unsafe.Sizeof(cgoSymbolizerArg{}))
+	}
 	call(cgoSymbolizer, noescape(unsafe.Pointer(arg)))
 }
 
@@ -1096,5 +1130,8 @@ func cgoContextPCs(ctxt uintptr, buf []uintptr) {
 		buf:     (*uintptr)(noescape(unsafe.Pointer(&buf[0]))),
 		max:     uintptr(len(buf)),
 	}
+	if msanenabled {
+		msanwrite(unsafe.Pointer(&arg), unsafe.Sizeof(arg))
+	}
 	call(cgoTraceback, noescape(unsafe.Pointer(&arg)))
 }
diff --git a/src/runtime/type.go b/src/runtime/type.go
index 5ef11a4..3ecc54c 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -199,11 +199,11 @@ func (t *_type) nameOff(off nameOff) name {
 	return resolveNameOff(unsafe.Pointer(t), off)
 }
 
-func (t *_type) typeOff(off typeOff) *_type {
+func resolveTypeOff(ptrInModule unsafe.Pointer, off typeOff) *_type {
 	if off == 0 {
 		return nil
 	}
-	base := uintptr(unsafe.Pointer(t))
+	base := uintptr(ptrInModule)
 	var md *moduledata
 	for next := &firstmoduledata; next != nil; next = next.next {
 		if base >= next.types && base < next.etypes {
@@ -235,6 +235,10 @@ func (t *_type) typeOff(off typeOff) *_type {
 	return (*_type)(unsafe.Pointer(res))
 }
 
+func (t *_type) typeOff(off typeOff) *_type {
+	return resolveTypeOff(unsafe.Pointer(t), off)
+}
+
 func (t *_type) textOff(off textOff) unsafe.Pointer {
 	base := uintptr(unsafe.Pointer(t))
 	var md *moduledata
@@ -257,7 +261,30 @@ func (t *_type) textOff(off textOff) unsafe.Pointer {
 		}
 		return res
 	}
-	res := md.text + uintptr(off)
+	res := uintptr(0)
+
+	// The text, or instruction stream is generated as one large buffer.  The off (offset) for a method is
+	// its offset within this buffer.  If the total text size gets too large, there can be issues on platforms like ppc64 if
+	// the target of calls are too far for the call instruction.  To resolve the large text issue, the text is split
+	// into multiple text sections to allow the linker to generate long calls when necessary.  When this happens, the vaddr
+	// for each text section is set to its offset within the text.  Each method's offset is compared against the section
+	// vaddrs and sizes to determine the containing section.  Then the section relative offset is added to the section's
+	// relocated baseaddr to compute the method addess.
+
+	if len(md.textsectmap) > 1 {
+		for i := range md.textsectmap {
+			sectaddr := md.textsectmap[i].vaddr
+			sectlen := md.textsectmap[i].length
+			if uintptr(off) >= sectaddr && uintptr(off) <= sectaddr+sectlen {
+				res = md.textsectmap[i].baseaddr + uintptr(off) - uintptr(md.textsectmap[i].vaddr)
+				break
+			}
+		}
+	} else {
+		// single text section
+		res = md.text + uintptr(off)
+	}
+
 	if res > md.etext {
 		println("runtime: textOff", hex(off), "out of range", hex(md.text), "-", hex(md.etext))
 		throw("runtime: text offset out of range")
@@ -446,14 +473,11 @@ func typelinksinit() {
 	if firstmoduledata.next == nil {
 		return
 	}
-	typehash := make(map[uint32][]*_type)
+	typehash := make(map[uint32][]*_type, len(firstmoduledata.typelinks))
 
-	modules := []*moduledata{}
-	for md := &firstmoduledata; md != nil; md = md.next {
-		modules = append(modules, md)
-	}
-	prev, modules := modules[len(modules)-1], modules[:len(modules)-1]
-	for len(modules) > 0 {
+	modules := activeModules()
+	prev := modules[0]
+	for _, md := range modules[1:] {
 		// Collect types from the previous module into typehash.
 	collect:
 		for _, tl := range prev.typelinks {
@@ -473,23 +497,26 @@ func typelinksinit() {
 			typehash[t.hash] = append(tlist, t)
 		}
 
-		// If any of this module's typelinks match a type from a
-		// prior module, prefer that prior type by adding the offset
-		// to this module's typemap.
-		md := modules[len(modules)-1]
-		md.typemap = make(map[typeOff]*_type, len(md.typelinks))
-		for _, tl := range md.typelinks {
-			t := (*_type)(unsafe.Pointer(md.types + uintptr(tl)))
-			for _, candidate := range typehash[t.hash] {
-				if typesEqual(t, candidate) {
-					t = candidate
-					break
+		if md.typemap == nil {
+			// If any of this module's typelinks match a type from a
+			// prior module, prefer that prior type by adding the offset
+			// to this module's typemap.
+			tm := make(map[typeOff]*_type, len(md.typelinks))
+			pinnedTypemaps = append(pinnedTypemaps, tm)
+			md.typemap = tm
+			for _, tl := range md.typelinks {
+				t := (*_type)(unsafe.Pointer(md.types + uintptr(tl)))
+				for _, candidate := range typehash[t.hash] {
+					if typesEqual(t, candidate) {
+						t = candidate
+						break
+					}
 				}
+				md.typemap[typeOff(tl)] = t
 			}
-			md.typemap[typeOff(tl)] = t
 		}
 
-		prev, modules = md, modules[:len(modules)-1]
+		prev = md
 	}
 }
 
@@ -573,15 +600,19 @@ func typesEqual(t, v *_type) bool {
 		for i := range it.mhdr {
 			tm := &it.mhdr[i]
 			vm := &iv.mhdr[i]
-			tname := it.typ.nameOff(tm.name)
-			vname := iv.typ.nameOff(vm.name)
+			// Note the mhdr array can be relocated from
+			// another module. See #17724.
+			tname := resolveNameOff(unsafe.Pointer(tm), tm.name)
+			vname := resolveNameOff(unsafe.Pointer(vm), vm.name)
 			if tname.name() != vname.name() {
 				return false
 			}
 			if tname.pkgPath() != vname.pkgPath() {
 				return false
 			}
-			if !typesEqual(it.typ.typeOff(tm.ityp), iv.typ.typeOff(vm.ityp)) {
+			tityp := resolveTypeOff(unsafe.Pointer(tm), tm.ityp)
+			vityp := resolveTypeOff(unsafe.Pointer(vm), vm.ityp)
+			if !typesEqual(tityp, vityp) {
 				return false
 			}
 		}
diff --git a/src/runtime/unaligned2.go b/src/runtime/unaligned2.go
index fed3cca..28b6119 100644
--- a/src/runtime/unaligned2.go
+++ b/src/runtime/unaligned2.go
@@ -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 arm mips64 mips64le
+// +build arm mips mipsle mips64 mips64le
 
 package runtime
 
diff --git a/src/runtime/utf8.go b/src/runtime/utf8.go
new file mode 100644
index 0000000..24ef179
--- /dev/null
+++ b/src/runtime/utf8.go
@@ -0,0 +1,123 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+// Numbers fundamental to the encoding.
+const (
+	runeError = '\uFFFD'     // the "error" Rune or "Unicode replacement character"
+	runeSelf  = 0x80         // characters below Runeself are represented as themselves in a single byte.
+	maxRune   = '\U0010FFFF' // Maximum valid Unicode code point.
+)
+
+// Code points in the surrogate range are not valid for UTF-8.
+const (
+	surrogateMin = 0xD800
+	surrogateMax = 0xDFFF
+)
+
+const (
+	t1 = 0x00 // 0000 0000
+	tx = 0x80 // 1000 0000
+	t2 = 0xC0 // 1100 0000
+	t3 = 0xE0 // 1110 0000
+	t4 = 0xF0 // 1111 0000
+	t5 = 0xF8 // 1111 1000
+
+	maskx = 0x3F // 0011 1111
+	mask2 = 0x1F // 0001 1111
+	mask3 = 0x0F // 0000 1111
+	mask4 = 0x07 // 0000 0111
+
+	rune1Max = 1<<7 - 1
+	rune2Max = 1<<11 - 1
+	rune3Max = 1<<16 - 1
+
+	// The default lowest and highest continuation byte.
+	locb = 0x80 // 1000 0000
+	hicb = 0xBF // 1011 1111
+)
+
+// decoderune returns the non-ASCII rune at the start of
+// s[k:] and the index after the rune in s.
+//
+// decoderune assumes that caller has checked that
+// the to be decoded rune is a non-ASCII rune.
+//
+// If the string appears to be incomplete or decoding problems
+// are encountered (runeerror, k + 1) is returned to ensure
+// progress when decoderune is used to iterate over a string.
+func decoderune(s string, k int) (r rune, pos int) {
+	pos = k
+
+	if k >= len(s) {
+		return runeError, k + 1
+	}
+
+	s = s[k:]
+
+	switch {
+	case t2 <= s[0] && s[0] < t3:
+		// 0080-07FF two byte sequence
+		if len(s) > 1 && (locb <= s[1] && s[1] <= hicb) {
+			r = rune(s[0]&mask2)<<6 | rune(s[1]&maskx)
+			pos += 2
+			if rune1Max < r {
+				return
+			}
+		}
+	case t3 <= s[0] && s[0] < t4:
+		// 0800-FFFF three byte sequence
+		if len(s) > 2 && (locb <= s[1] && s[1] <= hicb) && (locb <= s[2] && s[2] <= hicb) {
+			r = rune(s[0]&mask3)<<12 | rune(s[1]&maskx)<<6 | rune(s[2]&maskx)
+			pos += 3
+			if rune2Max < r && !(surrogateMin <= r && r <= surrogateMax) {
+				return
+			}
+		}
+	case t4 <= s[0] && s[0] < t5:
+		// 10000-1FFFFF four byte sequence
+		if len(s) > 3 && (locb <= s[1] && s[1] <= hicb) && (locb <= s[2] && s[2] <= hicb) && (locb <= s[3] && s[3] <= hicb) {
+			r = rune(s[0]&mask4)<<18 | rune(s[1]&maskx)<<12 | rune(s[2]&maskx)<<6 | rune(s[3]&maskx)
+			pos += 4
+			if rune3Max < r && r <= maxRune {
+				return
+			}
+		}
+	}
+
+	return runeError, k + 1
+}
+
+// encoderune writes into p (which must be large enough) the UTF-8 encoding of the rune.
+// It returns the number of bytes written.
+func encoderune(p []byte, r rune) int {
+	// Negative values are erroneous. Making it unsigned addresses the problem.
+	switch i := uint32(r); {
+	case i <= rune1Max:
+		p[0] = byte(r)
+		return 1
+	case i <= rune2Max:
+		_ = p[1] // eliminate bounds checks
+		p[0] = t2 | byte(r>>6)
+		p[1] = tx | byte(r)&maskx
+		return 2
+	case i > maxRune, surrogateMin <= i && i <= surrogateMax:
+		r = runeError
+		fallthrough
+	case i <= rune3Max:
+		_ = p[2] // eliminate bounds checks
+		p[0] = t3 | byte(r>>12)
+		p[1] = tx | byte(r>>6)&maskx
+		p[2] = tx | byte(r)&maskx
+		return 3
+	default:
+		_ = p[3] // eliminate bounds checks
+		p[0] = t4 | byte(r>>18)
+		p[1] = tx | byte(r>>12)&maskx
+		p[2] = tx | byte(r>>6)&maskx
+		p[3] = tx | byte(r)&maskx
+		return 4
+	}
+}
diff --git a/src/runtime/vdso_none.go b/src/runtime/vdso_none.go
index efae23f..fc21240 100644
--- a/src/runtime/vdso_none.go
+++ b/src/runtime/vdso_none.go
@@ -3,6 +3,7 @@
 // license that can be found in the LICENSE file.
 
 // +build !linux
+// +build !darwin
 
 package runtime
 
diff --git a/src/runtime/vlop_386.s b/src/runtime/vlop_386.s
index 92232d5..3387c51 100644
--- a/src/runtime/vlop_386.s
+++ b/src/runtime/vlop_386.s
@@ -1,5 +1,5 @@
 // Inferno's libkern/vlop-386.s
-// http://code.google.com/p/inferno-os/source/browse/libkern/vlop-386.s
+// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/vlop-386.s
 //
 //         Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
 //         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
@@ -29,28 +29,28 @@
  * C runtime for 64-bit divide.
  */
 
-// runtime·_mul64x32(r *uint64, a uint64, b uint32) uint32
-// sets *r = low 64 bits of 96-bit product a*b; returns high 32 bits.
+// runtime·_mul64x32(lo64 *uint64, a uint64, b uint32) (hi32 uint32)
+// sets *lo64 = low 64 bits of 96-bit product a*b; returns high 32 bits.
 TEXT runtime·_mul64by32(SB), NOSPLIT, $0
-	MOVL	r+0(FP), CX
-	MOVL	a+4(FP), AX
+	MOVL	lo64+0(FP), CX
+	MOVL	a_lo+4(FP), AX
 	MULL	b+12(FP)
 	MOVL	AX, 0(CX)
 	MOVL	DX, BX
-	MOVL	a+8(FP), AX
+	MOVL	a_hi+8(FP), AX
 	MULL	b+12(FP)
 	ADDL	AX, BX
 	ADCL	$0, DX
 	MOVL	BX, 4(CX)
 	MOVL	DX, AX
-	MOVL	AX, ret+16(FP)
+	MOVL	AX, hi32+16(FP)
 	RET
 
 TEXT runtime·_div64by32(SB), NOSPLIT, $0
 	MOVL	r+12(FP), CX
-	MOVL	a+0(FP), AX
-	MOVL	a+4(FP), DX
+	MOVL	a_lo+0(FP), AX
+	MOVL	a_hi+4(FP), DX
 	DIVL	b+8(FP)
 	MOVL	DX, 0(CX)
-	MOVL	AX, ret+16(FP)
+	MOVL	AX, q+16(FP)
 	RET
diff --git a/src/runtime/vlop_arm.s b/src/runtime/vlop_arm.s
index 338d9d5..d4c411c 100644
--- a/src/runtime/vlop_arm.s
+++ b/src/runtime/vlop_arm.s
@@ -1,5 +1,5 @@
 // Inferno's libkern/vlop-arm.s
-// http://code.google.com/p/inferno-os/source/browse/libkern/vlop-arm.s
+// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/vlop-arm.s
 //
 //         Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
 //         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
@@ -107,6 +107,7 @@ TEXT runtime·_sfloatpanic(SB),NOSPLIT,$-4
 	B	runtime·sigpanic(SB)
 
 // func udiv(n, d uint32) (q, r uint32)
+// compiler knowns the register usage of this function
 // Reference: 
 // Sloss, Andrew et. al; ARM System Developer's Guide: Designing and Optimizing System Software
 // Morgan Kaufmann; 1 edition (April 8, 2004), ISBN 978-1558608740
@@ -117,7 +118,7 @@ TEXT runtime·_sfloatpanic(SB),NOSPLIT,$-4
 #define Ra	R11
 
 // Be careful: Ra == R11 will be used by the linker for synthesized instructions.
-TEXT udiv<>(SB),NOSPLIT,$-4
+TEXT udiv(SB),NOSPLIT,$-4
 	CLZ 	Rq, Rs // find normalizing shift
 	MOVW.S	Rq<<Rs, Ra
 	MOVW	$fast_udiv_tab<>-64(SB), RM
@@ -202,8 +203,9 @@ DATA fast_udiv_tab<>+0x38(SB)/4, $0x85868788
 DATA fast_udiv_tab<>+0x3c(SB)/4, $0x81828384
 GLOBL fast_udiv_tab<>(SB), RODATA, $64
 
-// The linker will pass numerator in RTMP, and it also
-// expects the result in RTMP
+// The linker will pass numerator in R8
+#define Rn R8
+// The linker expects the result in RTMP
 #define RTMP R11
 
 TEXT _divu(SB), NOSPLIT, $16-0
@@ -224,10 +226,10 @@ TEXT _divu(SB), NOSPLIT, $16-0
 	MOVW	Rs, 12(R13)
 	MOVW	RM, 16(R13)
 
-	MOVW	RTMP, Rr		/* numerator */
+	MOVW	Rn, Rr			/* numerator */
 	MOVW	g_m(g), Rq
 	MOVW	m_divmod(Rq), Rq	/* denominator */
-	BL  	udiv<>(SB)
+	BL  	udiv(SB)
 	MOVW	Rq, RTMP
 	MOVW	4(R13), Rq
 	MOVW	8(R13), Rr
@@ -242,10 +244,10 @@ TEXT _modu(SB), NOSPLIT, $16-0
 	MOVW	Rs, 12(R13)
 	MOVW	RM, 16(R13)
 
-	MOVW	RTMP, Rr		/* numerator */
+	MOVW	Rn, Rr			/* numerator */
 	MOVW	g_m(g), Rq
 	MOVW	m_divmod(Rq), Rq	/* denominator */
-	BL  	udiv<>(SB)
+	BL  	udiv(SB)
 	MOVW	Rr, RTMP
 	MOVW	4(R13), Rq
 	MOVW	8(R13), Rr
@@ -259,7 +261,7 @@ TEXT _div(SB),NOSPLIT,$16-0
 	MOVW	Rr, 8(R13)
 	MOVW	Rs, 12(R13)
 	MOVW	RM, 16(R13)
-	MOVW	RTMP, Rr		/* numerator */
+	MOVW	Rn, Rr			/* numerator */
 	MOVW	g_m(g), Rq
 	MOVW	m_divmod(Rq), Rq	/* denominator */
 	CMP 	$0, Rr
@@ -269,16 +271,16 @@ TEXT _div(SB),NOSPLIT,$16-0
 	BGE 	d2
 	RSB 	$0, Rq, Rq
 d0:
-	BL  	udiv<>(SB)  		/* none/both neg */
+	BL  	udiv(SB)  		/* none/both neg */
 	MOVW	Rq, RTMP
-	B		out1
+	B	out1
 d1:
 	CMP 	$0, Rq
 	BGE 	d0
 	RSB 	$0, Rq, Rq
 d2:
-	BL  	udiv<>(SB)  		/* one neg */
-	RSB		$0, Rq, RTMP
+	BL  	udiv(SB)  		/* one neg */
+	RSB	$0, Rq, RTMP
 out1:
 	MOVW	4(R13), Rq
 	MOVW	8(R13), Rr
@@ -292,7 +294,7 @@ TEXT _mod(SB),NOSPLIT,$16-0
 	MOVW	Rr, 8(R13)
 	MOVW	Rs, 12(R13)
 	MOVW	RM, 16(R13)
-	MOVW	RTMP, Rr		/* numerator */
+	MOVW	Rn, Rr			/* numerator */
 	MOVW	g_m(g), Rq
 	MOVW	m_divmod(Rq), Rq	/* denominator */
 	CMP 	$0, Rq
@@ -300,11 +302,11 @@ TEXT _mod(SB),NOSPLIT,$16-0
 	CMP 	$0, Rr
 	BGE 	m1
 	RSB 	$0, Rr, Rr
-	BL  	udiv<>(SB)  		/* neg numerator */
+	BL  	udiv(SB)  		/* neg numerator */
 	RSB 	$0, Rr, RTMP
 	B   	out
 m1:
-	BL  	udiv<>(SB)  		/* pos numerator */
+	BL  	udiv(SB)  		/* pos numerator */
 	MOVW	Rr, RTMP
 out:
 	MOVW	4(R13), Rq
diff --git a/src/runtime/vlrt.go b/src/runtime/vlrt.go
index cd37828..d63da9c 100644
--- a/src/runtime/vlrt.go
+++ b/src/runtime/vlrt.go
@@ -1,5 +1,5 @@
 // Inferno's libkern/vlrt-arm.c
-// http://code.google.com/p/inferno-os/source/browse/libkern/vlrt-arm.c
+// https://bitbucket.org/inferno-os/inferno-os/src/default/libkern/vlrt-arm.c
 //
 //         Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
 //         Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
@@ -23,7 +23,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-// +build arm 386
+// +build arm 386 mips mipsle
 
 package runtime
 
@@ -198,6 +198,11 @@ func dodiv(n, d uint64) (q, r uint64) {
 		return slowdodiv(n, d)
 	}
 
+	if GOARCH == "mips" || GOARCH == "mipsle" {
+		// No _div64by32 on mips and using only _mul64by32 doesn't bring much benefit
+		return slowdodiv(n, d)
+	}
+
 	if d > n {
 		return 0, n
 	}
@@ -255,3 +260,17 @@ func slowdodiv(n, d uint64) (q, r uint64) {
 	}
 	return q, n
 }
+
+// Floating point control word values for GOARCH=386 GO386=387.
+// Bits 0-5 are bits to disable floating-point exceptions.
+// Bits 8-9 are the precision control:
+//   0 = single precision a.k.a. float32
+//   2 = double precision a.k.a. float64
+// Bits 10-11 are the rounding mode:
+//   0 = round to nearest (even on a tie)
+//   3 = round toward zero
+var (
+	controlWord64      uint16 = 0x3f + 2<<8 + 0<<10
+	controlWord32             = 0x3f + 0<<8 + 0<<10
+	controlWord64trunc        = 0x3f + 2<<8 + 3<<10
+)
diff --git a/src/runtime/write_err_android.go b/src/runtime/write_err_android.go
index 4411a14..748dec6 100644
--- a/src/runtime/write_err_android.go
+++ b/src/runtime/write_err_android.go
@@ -75,7 +75,9 @@ func writeErr(b []byte) {
 		if v == '\n' || writePos == len(dst)-1 {
 			dst[writePos] = 0
 			write(writeFD, unsafe.Pointer(&writeBuf[0]), int32(hlen+writePos))
-			memclrBytes(dst)
+			for i := range dst {
+				dst[i] = 0
+			}
 			writePos = 0
 		}
 	}
diff --git a/src/sort/example_search_test.go b/src/sort/example_search_test.go
new file mode 100644
index 0000000..6928f0f
--- /dev/null
+++ b/src/sort/example_search_test.go
@@ -0,0 +1,42 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sort_test
+
+import (
+	"fmt"
+	"sort"
+)
+
+// This example demonstrates searching a list sorted in ascending order.
+func ExampleSearch() {
+	a := []int{1, 3, 6, 10, 15, 21, 28, 36, 45, 55}
+	x := 6
+
+	i := sort.Search(len(a), func(i int) bool { return a[i] >= x })
+	if i < len(a) && a[i] == x {
+		fmt.Printf("found %d at index %d in %v\n", x, i, a)
+	} else {
+		fmt.Printf("%d not found in %v\n", x, a)
+	}
+	// Output:
+	// found 6 at index 2 in [1 3 6 10 15 21 28 36 45 55]
+}
+
+// This example demonstrates searching a list sorted in descending order.
+// The approach is the same as searching a list in ascending order,
+// but with the condition inverted.
+func ExampleSearch_descendingOrder() {
+	a := []int{55, 45, 36, 28, 21, 15, 10, 6, 3, 1}
+	x := 6
+
+	i := sort.Search(len(a), func(i int) bool { return a[i] <= x })
+	if i < len(a) && a[i] == x {
+		fmt.Printf("found %d at index %d in %v\n", x, i, a)
+	} else {
+		fmt.Printf("%d not found in %v\n", x, a)
+	}
+	// Output:
+	// found 6 at index 7 in [55 45 36 28 21 15 10 6 3 1]
+}
diff --git a/src/sort/genzfunc.go b/src/sort/genzfunc.go
new file mode 100644
index 0000000..6d2b471
--- /dev/null
+++ b/src/sort/genzfunc.go
@@ -0,0 +1,122 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// This program is run via "go generate" (via a directive in sort.go)
+// to generate zfuncversion.go.
+//
+// It copies sort.go to zfuncversion.go, only retaining funcs which
+// take a "data Interface" parameter, and renaming each to have a
+// "_func" suffix and taking a "data lessSwap" instead. It then rewrites
+// each internal function call to the appropriate _func variants.
+
+package main
+
+import (
+	"bytes"
+	"go/ast"
+	"go/format"
+	"go/parser"
+	"go/token"
+	"io/ioutil"
+	"log"
+	"regexp"
+)
+
+var fset = token.NewFileSet()
+
+func main() {
+	af, err := parser.ParseFile(fset, "sort.go", nil, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+	af.Doc = nil
+	af.Imports = nil
+	af.Comments = nil
+
+	var newDecl []ast.Decl
+	for _, d := range af.Decls {
+		fd, ok := d.(*ast.FuncDecl)
+		if !ok {
+			continue
+		}
+		if fd.Recv != nil || fd.Name.IsExported() {
+			continue
+		}
+		typ := fd.Type
+		if len(typ.Params.List) < 1 {
+			continue
+		}
+		arg0 := typ.Params.List[0]
+		arg0Name := arg0.Names[0].Name
+		arg0Type := arg0.Type.(*ast.Ident)
+		if arg0Name != "data" || arg0Type.Name != "Interface" {
+			continue
+		}
+		arg0Type.Name = "lessSwap"
+
+		newDecl = append(newDecl, fd)
+	}
+	af.Decls = newDecl
+	ast.Walk(visitFunc(rewriteCalls), af)
+
+	var out bytes.Buffer
+	if err := format.Node(&out, fset, af); err != nil {
+		log.Fatalf("format.Node: %v", err)
+	}
+
+	// Get rid of blank lines after removal of comments.
+	src := regexp.MustCompile(`\n{2,}`).ReplaceAll(out.Bytes(), []byte("\n"))
+
+	// Add comments to each func, for the lost reader.
+	// This is so much easier than adding comments via the AST
+	// and trying to get position info correct.
+	src = regexp.MustCompile(`(?m)^func (\w+)`).ReplaceAll(src, []byte("\n// Auto-generated variant of sort.go:$1\nfunc ${1}_func"))
+
+	// Final gofmt.
+	src, err = format.Source(src)
+	if err != nil {
+		log.Fatalf("format.Source: %v on\n%s", err, src)
+	}
+
+	out.Reset()
+	out.WriteString(`// DO NOT EDIT; AUTO-GENERATED from sort.go using genzfunc.go
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+`)
+	out.Write(src)
+
+	const target = "zfuncversion.go"
+	if err := ioutil.WriteFile(target, out.Bytes(), 0644); err != nil {
+		log.Fatal(err)
+	}
+}
+
+type visitFunc func(ast.Node) ast.Visitor
+
+func (f visitFunc) Visit(n ast.Node) ast.Visitor { return f(n) }
+
+func rewriteCalls(n ast.Node) ast.Visitor {
+	ce, ok := n.(*ast.CallExpr)
+	if ok {
+		rewriteCall(ce)
+	}
+	return visitFunc(rewriteCalls)
+}
+
+func rewriteCall(ce *ast.CallExpr) {
+	ident, ok := ce.Fun.(*ast.Ident)
+	if !ok {
+		// e.g. skip SelectorExpr (data.Less(..) calls)
+		return
+	}
+	if len(ce.Args) < 1 {
+		return
+	}
+	ident.Name += "_func"
+}
diff --git a/src/sort/sort.go b/src/sort/sort.go
index d07a0c2..72d24ef 100644
--- a/src/sort/sort.go
+++ b/src/sort/sort.go
@@ -2,10 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:generate go run genzfunc.go
+
 // Package sort provides primitives for sorting slices and user-defined
 // collections.
 package sort
 
+import "reflect"
+
 // A type, typically a collection, that satisfies sort.Interface can be
 // sorted by the routines in this package. The methods require that the
 // elements of the collection be enumerated by an integer index.
@@ -212,14 +216,63 @@ func quickSort(data Interface, a, b, maxDepth int) {
 // It makes one call to data.Len to determine n, and O(n*log(n)) calls to
 // data.Less and data.Swap. The sort is not guaranteed to be stable.
 func Sort(data Interface) {
-	// Switch to heapsort if depth of 2*ceil(lg(n+1)) is reached.
 	n := data.Len()
-	maxDepth := 0
+	quickSort(data, 0, n, maxDepth(n))
+}
+
+// maxDepth returns a threshold at which quicksort should switch
+// to heapsort. It returns 2*ceil(lg(n+1)).
+func maxDepth(n int) int {
+	var depth int
 	for i := n; i > 0; i >>= 1 {
-		maxDepth++
+		depth++
+	}
+	return depth * 2
+}
+
+// lessSwap is a pair of Less and Swap function for use with the
+// auto-generated func-optimized variant of sort.go in
+// zfuncversion.go.
+type lessSwap struct {
+	Less func(i, j int) bool
+	Swap func(i, j int)
+}
+
+// Slice sorts the provided slice given the provided less function.
+//
+// The sort is not guaranteed to be stable. For a stable sort, use
+// SliceStable.
+//
+// The function panics if the provided interface is not a slice.
+func Slice(slice interface{}, less func(i, j int) bool) {
+	rv := reflect.ValueOf(slice)
+	swap := reflect.Swapper(slice)
+	length := rv.Len()
+	quickSort_func(lessSwap{less, swap}, 0, length, maxDepth(length))
+}
+
+// SliceStable sorts the provided slice given the provided less
+// function while keeping the original order of equal elements.
+//
+// The function panics if the provided interface is not a slice.
+func SliceStable(slice interface{}, less func(i, j int) bool) {
+	rv := reflect.ValueOf(slice)
+	swap := reflect.Swapper(slice)
+	stable_func(lessSwap{less, swap}, rv.Len())
+}
+
+// SliceIsSorted tests whether a slice is sorted.
+//
+// The function panics if the provided interface is not a slice.
+func SliceIsSorted(slice interface{}, less func(i, j int) bool) bool {
+	rv := reflect.ValueOf(slice)
+	n := rv.Len()
+	for i := n - 1; i > 0; i-- {
+		if less(i, i-1) {
+			return false
+		}
 	}
-	maxDepth *= 2
-	quickSort(data, 0, n, maxDepth)
+	return true
 }
 
 type reverse struct {
@@ -337,7 +390,10 @@ func StringsAreSorted(a []string) bool { return IsSorted(StringSlice(a)) }
 // It makes one call to data.Len to determine n, O(n*log(n)) calls to
 // data.Less and O(n*log(n)*log(n)) calls to data.Swap.
 func Stable(data Interface) {
-	n := data.Len()
+	stable(data, data.Len())
+}
+
+func stable(data Interface, n int) {
 	blockSize := 20 // must be > 0
 	a, b := 0, blockSize
 	for b <= n {
diff --git a/src/sort/sort_test.go b/src/sort/sort_test.go
index 60fac2d..45713a2 100644
--- a/src/sort/sort_test.go
+++ b/src/sort/sort_test.go
@@ -6,10 +6,12 @@ package sort_test
 
 import (
 	"fmt"
+	"internal/testenv"
 	"math"
 	"math/rand"
 	. "sort"
 	"strconv"
+	stringspkg "strings"
 	"testing"
 )
 
@@ -74,6 +76,17 @@ func TestStrings(t *testing.T) {
 	}
 }
 
+func TestSlice(t *testing.T) {
+	data := strings
+	Slice(data[:], func(i, j int) bool {
+		return data[i] < data[j]
+	})
+	if !SliceIsSorted(data[:], func(i, j int) bool { return data[i] < data[j] }) {
+		t.Errorf("sorted %v", strings)
+		t.Errorf("   got %v", data)
+	}
+}
+
 func TestSortLarge_Random(t *testing.T) {
 	n := 1000000
 	if testing.Short() {
@@ -148,24 +161,46 @@ func TestNonDeterministicComparison(t *testing.T) {
 
 func BenchmarkSortString1K(b *testing.B) {
 	b.StopTimer()
+	unsorted := make([]string, 1<<10)
+	for i := range unsorted {
+		unsorted[i] = strconv.Itoa(i ^ 0x2cc)
+	}
+	data := make([]string, len(unsorted))
+
 	for i := 0; i < b.N; i++ {
-		data := make([]string, 1<<10)
-		for i := 0; i < len(data); i++ {
-			data[i] = strconv.Itoa(i ^ 0x2cc)
-		}
+		copy(data, unsorted)
 		b.StartTimer()
 		Strings(data)
 		b.StopTimer()
 	}
 }
 
+func BenchmarkSortString1K_Slice(b *testing.B) {
+	b.StopTimer()
+	unsorted := make([]string, 1<<10)
+	for i := range unsorted {
+		unsorted[i] = strconv.Itoa(i ^ 0x2cc)
+	}
+	data := make([]string, len(unsorted))
+
+	for i := 0; i < b.N; i++ {
+		copy(data, unsorted)
+		b.StartTimer()
+		Slice(data, func(i, j int) bool { return data[i] < data[j] })
+		b.StopTimer()
+	}
+}
+
 func BenchmarkStableString1K(b *testing.B) {
 	b.StopTimer()
+	unsorted := make([]string, 1<<10)
+	for i := 0; i < len(data); i++ {
+		unsorted[i] = strconv.Itoa(i ^ 0x2cc)
+	}
+	data := make([]string, len(unsorted))
+
 	for i := 0; i < b.N; i++ {
-		data := make([]string, 1<<10)
-		for i := 0; i < len(data); i++ {
-			data[i] = strconv.Itoa(i ^ 0x2cc)
-		}
+		copy(data, unsorted)
 		b.StartTimer()
 		Stable(StringSlice(data))
 		b.StopTimer()
@@ -187,17 +222,34 @@ func BenchmarkSortInt1K(b *testing.B) {
 
 func BenchmarkStableInt1K(b *testing.B) {
 	b.StopTimer()
+	unsorted := make([]int, 1<<10)
+	for i := range unsorted {
+		unsorted[i] = i ^ 0x2cc
+	}
+	data := make([]int, len(unsorted))
 	for i := 0; i < b.N; i++ {
-		data := make([]int, 1<<10)
-		for i := 0; i < len(data); i++ {
-			data[i] = i ^ 0x2cc
-		}
+		copy(data, unsorted)
 		b.StartTimer()
 		Stable(IntSlice(data))
 		b.StopTimer()
 	}
 }
 
+func BenchmarkStableInt1K_Slice(b *testing.B) {
+	b.StopTimer()
+	unsorted := make([]int, 1<<10)
+	for i := range unsorted {
+		unsorted[i] = i ^ 0x2cc
+	}
+	data := make([]int, len(unsorted))
+	for i := 0; i < b.N; i++ {
+		copy(data, unsorted)
+		b.StartTimer()
+		SliceStable(data, func(i, j int) bool { return data[i] < data[j] })
+		b.StopTimer()
+	}
+}
+
 func BenchmarkSortInt64K(b *testing.B) {
 	b.StopTimer()
 	for i := 0; i < b.N; i++ {
@@ -211,6 +263,19 @@ func BenchmarkSortInt64K(b *testing.B) {
 	}
 }
 
+func BenchmarkSortInt64K_Slice(b *testing.B) {
+	b.StopTimer()
+	for i := 0; i < b.N; i++ {
+		data := make([]int, 1<<16)
+		for i := 0; i < len(data); i++ {
+			data[i] = i ^ 0xcccc
+		}
+		b.StartTimer()
+		Slice(data, func(i, j int) bool { return data[i] < data[j] })
+		b.StopTimer()
+	}
+}
+
 func BenchmarkStableInt64K(b *testing.B) {
 	b.StopTimer()
 	for i := 0; i < b.N; i++ {
@@ -555,6 +620,9 @@ func TestCountStableOps(t *testing.T) { countOps(t, Stable, "Stable") }
 func TestCountSortOps(t *testing.T)   { countOps(t, Sort, "Sort  ") }
 
 func bench(b *testing.B, size int, algo func(Interface), name string) {
+	if stringspkg.HasSuffix(testenv.Builder(), "-race") && size > 1e4 {
+		b.Skip("skipping slow benchmark on race builder")
+	}
 	b.StopTimer()
 	data := make(intPairs, size)
 	x := ^uint32(0)
diff --git a/src/sort/zfuncversion.go b/src/sort/zfuncversion.go
new file mode 100644
index 0000000..7abb18a
--- /dev/null
+++ b/src/sort/zfuncversion.go
@@ -0,0 +1,265 @@
+// DO NOT EDIT; AUTO-GENERATED from sort.go using genzfunc.go
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sort
+
+// Auto-generated variant of sort.go:insertionSort
+func insertionSort_func(data lessSwap, a, b int) {
+	for i := a + 1; i < b; i++ {
+		for j := i; j > a && data.Less(j, j-1); j-- {
+			data.Swap(j, j-1)
+		}
+	}
+}
+
+// Auto-generated variant of sort.go:siftDown
+func siftDown_func(data lessSwap, lo, hi, first int) {
+	root := lo
+	for {
+		child := 2*root + 1
+		if child >= hi {
+			break
+		}
+		if child+1 < hi && data.Less(first+child, first+child+1) {
+			child++
+		}
+		if !data.Less(first+root, first+child) {
+			return
+		}
+		data.Swap(first+root, first+child)
+		root = child
+	}
+}
+
+// Auto-generated variant of sort.go:heapSort
+func heapSort_func(data lessSwap, a, b int) {
+	first := a
+	lo := 0
+	hi := b - a
+	for i := (hi - 1) / 2; i >= 0; i-- {
+		siftDown_func(data, i, hi, first)
+	}
+	for i := hi - 1; i >= 0; i-- {
+		data.Swap(first, first+i)
+		siftDown_func(data, lo, i, first)
+	}
+}
+
+// Auto-generated variant of sort.go:medianOfThree
+func medianOfThree_func(data lessSwap, m1, m0, m2 int) {
+	if data.Less(m1, m0) {
+		data.Swap(m1, m0)
+	}
+	if data.Less(m2, m1) {
+		data.Swap(m2, m1)
+		if data.Less(m1, m0) {
+			data.Swap(m1, m0)
+		}
+	}
+}
+
+// Auto-generated variant of sort.go:swapRange
+func swapRange_func(data lessSwap, a, b, n int) {
+	for i := 0; i < n; i++ {
+		data.Swap(a+i, b+i)
+	}
+}
+
+// Auto-generated variant of sort.go:doPivot
+func doPivot_func(data lessSwap, lo, hi int) (midlo, midhi int) {
+	m := lo + (hi-lo)/2
+	if hi-lo > 40 {
+		s := (hi - lo) / 8
+		medianOfThree_func(data, lo, lo+s, lo+2*s)
+		medianOfThree_func(data, m, m-s, m+s)
+		medianOfThree_func(data, hi-1, hi-1-s, hi-1-2*s)
+	}
+	medianOfThree_func(data, lo, m, hi-1)
+	pivot := lo
+	a, c := lo+1, hi-1
+	for ; a < c && data.Less(a, pivot); a++ {
+	}
+	b := a
+	for {
+		for ; b < c && !data.Less(pivot, b); b++ {
+		}
+		for ; b < c && data.Less(pivot, c-1); c-- {
+		}
+		if b >= c {
+			break
+		}
+		data.Swap(b, c-1)
+		b++
+		c--
+	}
+	protect := hi-c < 5
+	if !protect && hi-c < (hi-lo)/4 {
+		dups := 0
+		if !data.Less(pivot, hi-1) {
+			data.Swap(c, hi-1)
+			c++
+			dups++
+		}
+		if !data.Less(b-1, pivot) {
+			b--
+			dups++
+		}
+		if !data.Less(m, pivot) {
+			data.Swap(m, b-1)
+			b--
+			dups++
+		}
+		protect = dups > 1
+	}
+	if protect {
+		for {
+			for ; a < b && !data.Less(b-1, pivot); b-- {
+			}
+			for ; a < b && data.Less(a, pivot); a++ {
+			}
+			if a >= b {
+				break
+			}
+			data.Swap(a, b-1)
+			a++
+			b--
+		}
+	}
+	data.Swap(pivot, b-1)
+	return b - 1, c
+}
+
+// Auto-generated variant of sort.go:quickSort
+func quickSort_func(data lessSwap, a, b, maxDepth int) {
+	for b-a > 12 {
+		if maxDepth == 0 {
+			heapSort_func(data, a, b)
+			return
+		}
+		maxDepth--
+		mlo, mhi := doPivot_func(data, a, b)
+		if mlo-a < b-mhi {
+			quickSort_func(data, a, mlo, maxDepth)
+			a = mhi
+		} else {
+			quickSort_func(data, mhi, b, maxDepth)
+			b = mlo
+		}
+	}
+	if b-a > 1 {
+		for i := a + 6; i < b; i++ {
+			if data.Less(i, i-6) {
+				data.Swap(i, i-6)
+			}
+		}
+		insertionSort_func(data, a, b)
+	}
+}
+
+// Auto-generated variant of sort.go:stable
+func stable_func(data lessSwap, n int) {
+	blockSize := 20
+	a, b := 0, blockSize
+	for b <= n {
+		insertionSort_func(data, a, b)
+		a = b
+		b += blockSize
+	}
+	insertionSort_func(data, a, n)
+	for blockSize < n {
+		a, b = 0, 2*blockSize
+		for b <= n {
+			symMerge_func(data, a, a+blockSize, b)
+			a = b
+			b += 2 * blockSize
+		}
+		if m := a + blockSize; m < n {
+			symMerge_func(data, a, m, n)
+		}
+		blockSize *= 2
+	}
+}
+
+// Auto-generated variant of sort.go:symMerge
+func symMerge_func(data lessSwap, a, m, b int) {
+	if m-a == 1 {
+		i := m
+		j := b
+		for i < j {
+			h := i + (j-i)/2
+			if data.Less(h, a) {
+				i = h + 1
+			} else {
+				j = h
+			}
+		}
+		for k := a; k < i-1; k++ {
+			data.Swap(k, k+1)
+		}
+		return
+	}
+	if b-m == 1 {
+		i := a
+		j := m
+		for i < j {
+			h := i + (j-i)/2
+			if !data.Less(m, h) {
+				i = h + 1
+			} else {
+				j = h
+			}
+		}
+		for k := m; k > i; k-- {
+			data.Swap(k, k-1)
+		}
+		return
+	}
+	mid := a + (b-a)/2
+	n := mid + m
+	var start, r int
+	if m > mid {
+		start = n - b
+		r = mid
+	} else {
+		start = a
+		r = m
+	}
+	p := n - 1
+	for start < r {
+		c := start + (r-start)/2
+		if !data.Less(p-c, c) {
+			start = c + 1
+		} else {
+			r = c
+		}
+	}
+	end := n - start
+	if start < m && m < end {
+		rotate_func(data, start, m, end)
+	}
+	if a < start && start < mid {
+		symMerge_func(data, a, start, mid)
+	}
+	if mid < end && end < b {
+		symMerge_func(data, mid, end, b)
+	}
+}
+
+// Auto-generated variant of sort.go:rotate
+func rotate_func(data lessSwap, a, m, b int) {
+	i := m - a
+	j := b - m
+	for i != j {
+		if i > j {
+			swapRange_func(data, m-i, m, j)
+			i -= j
+		} else {
+			swapRange_func(data, m-i, m+j-i, i)
+			j -= i
+		}
+	}
+	swapRange_func(data, m-i, m, i)
+}
diff --git a/src/strconv/atoi.go b/src/strconv/atoi.go
index a236de4..66df149 100644
--- a/src/strconv/atoi.go
+++ b/src/strconv/atoi.go
@@ -199,6 +199,10 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) {
 
 // Atoi returns the result of ParseInt(s, 10, 0) converted to type int.
 func Atoi(s string) (int, error) {
+	const fnAtoi = "Atoi"
 	i64, err := ParseInt(s, 10, 0)
+	if nerr, ok := err.(*NumError); ok {
+		nerr.Func = fnAtoi
+	}
 	return int(i64), err
 }
diff --git a/src/strconv/decimal.go b/src/strconv/decimal.go
index 5252d6e..957acd9 100644
--- a/src/strconv/decimal.go
+++ b/src/strconv/decimal.go
@@ -131,11 +131,13 @@ func rightShift(a *decimal, k uint) {
 	}
 	a.dp -= r - 1
 
+	var mask uint = (1 << k) - 1
+
 	// Pick up a digit, put down a digit.
 	for ; r < a.nd; r++ {
 		c := uint(a.d[r])
 		dig := n >> k
-		n -= dig << k
+		n &= mask
 		a.d[w] = byte(dig + '0')
 		w++
 		n = n*10 + c - '0'
@@ -144,7 +146,7 @@ func rightShift(a *decimal, k uint) {
 	// Put down extra digits.
 	for n > 0 {
 		dig := n >> k
-		n -= dig << k
+		n &= mask
 		if w < len(a.d) {
 			a.d[w] = byte(dig + '0')
 			w++
diff --git a/src/strconv/ftoa_test.go b/src/strconv/ftoa_test.go
index 1d25242..976bd2c 100644
--- a/src/strconv/ftoa_test.go
+++ b/src/strconv/ftoa_test.go
@@ -208,6 +208,9 @@ var ftoaBenches = []struct {
 	{"64Fixed2", 123.456, 'e', 3, 64},
 	{"64Fixed3", 1.23456e+78, 'e', 3, 64},
 	{"64Fixed4", 1.23456e-78, 'e', 3, 64},
+
+	// Trigger slow path (see issue #15672).
+	{"Slowpath64", 622666234635.3213e-320, 'e', -1, 64},
 }
 
 func BenchmarkFormatFloat(b *testing.B) {
diff --git a/src/strconv/quote.go b/src/strconv/quote.go
index becfe1d..76c5c2a 100644
--- a/src/strconv/quote.go
+++ b/src/strconv/quote.go
@@ -362,6 +362,16 @@ func Unquote(s string) (string, error) {
 		if contains(s, '`') {
 			return "", ErrSyntax
 		}
+		if contains(s, '\r') {
+			// -1 because we know there is at least one \r to remove.
+			buf := make([]byte, 0, len(s)-1)
+			for i := 0; i < len(s); i++ {
+				if s[i] != '\r' {
+					buf = append(buf, s[i])
+				}
+			}
+			return string(buf), nil
+		}
 		return s, nil
 	}
 	if quote != '"' && quote != '\'' {
diff --git a/src/strconv/quote_test.go b/src/strconv/quote_test.go
index 10735e3..a4b5804 100644
--- a/src/strconv/quote_test.go
+++ b/src/strconv/quote_test.go
@@ -274,6 +274,7 @@ var unquotetests = []unQuoteTest{
 	{"`\n`", "\n"},
 	{"`	`", `	`},
 	{"` `", ` `},
+	{"`a\rb`", "ab"},
 }
 
 var misquoted = []string{
@@ -306,7 +307,7 @@ var misquoted = []string{
 
 func TestUnquote(t *testing.T) {
 	for _, tt := range unquotetests {
-		if out, err := Unquote(tt.in); err != nil && out != tt.out {
+		if out, err := Unquote(tt.in); err != nil || out != tt.out {
 			t.Errorf("Unquote(%#q) = %q, %v want %q, nil", tt.in, out, err, tt.out)
 		}
 	}
diff --git a/src/strconv/strconv_test.go b/src/strconv/strconv_test.go
index 9a007dd..0c14236 100644
--- a/src/strconv/strconv_test.go
+++ b/src/strconv/strconv_test.go
@@ -55,3 +55,34 @@ func TestCountMallocs(t *testing.T) {
 		}
 	}
 }
+
+func TestErrorPrefixes(t *testing.T) {
+	_, errInt := Atoi("INVALID")
+	_, errBool := ParseBool("INVALID")
+	_, errFloat := ParseFloat("INVALID", 64)
+	_, errInt64 := ParseInt("INVALID", 10, 64)
+	_, errUint64 := ParseUint("INVALID", 10, 64)
+
+	vectors := []struct {
+		err  error  // Input error
+		want string // Function name wanted
+	}{
+		{errInt, "Atoi"},
+		{errBool, "ParseBool"},
+		{errFloat, "ParseFloat"},
+		{errInt64, "ParseInt"},
+		{errUint64, "ParseUint"},
+	}
+
+	for _, v := range vectors {
+		nerr, ok := v.err.(*NumError)
+		if !ok {
+			t.Errorf("test %s, error was not a *NumError", v.want)
+			continue
+		}
+		if got := nerr.Func; got != v.want {
+			t.Errorf("mismatching Func: got %s, want %s", got, v.want)
+		}
+	}
+
+}
diff --git a/src/strings/strings.go b/src/strings/strings.go
index 919e8c8..60a281a 100644
--- a/src/strings/strings.go
+++ b/src/strings/strings.go
@@ -77,48 +77,18 @@ func hashStrRev(sep string) (uint32, uint32) {
 func Count(s, sep string) int {
 	n := 0
 	// special cases
-	switch {
-	case len(sep) == 0:
+	if len(sep) == 0 {
 		return utf8.RuneCountInString(s) + 1
-	case len(sep) == 1:
-		// special case worth making fast
-		c := sep[0]
-		for i := 0; i < len(s); i++ {
-			if s[i] == c {
-				n++
-			}
-		}
-		return n
-	case len(sep) > len(s):
-		return 0
-	case len(sep) == len(s):
-		if sep == s {
-			return 1
-		}
-		return 0
 	}
-	// Rabin-Karp search
-	hashsep, pow := hashStr(sep)
-	h := uint32(0)
-	for i := 0; i < len(sep); i++ {
-		h = h*primeRK + uint32(s[i])
-	}
-	lastmatch := 0
-	if h == hashsep && s[:len(sep)] == sep {
-		n++
-		lastmatch = len(sep)
-	}
-	for i := len(sep); i < len(s); {
-		h *= primeRK
-		h += uint32(s[i])
-		h -= pow * uint32(s[i-len(sep)])
-		i++
-		if h == hashsep && lastmatch <= i-len(sep) && s[i-len(sep):i] == sep {
-			n++
-			lastmatch = i
+	offset := 0
+	for {
+		i := Index(s[offset:], sep)
+		if i == -1 {
+			return n
 		}
+		n++
+		offset += i + len(sep)
 	}
-	return n
 }
 
 // Contains reports whether substr is within s.
@@ -175,24 +145,40 @@ func LastIndex(s, sep string) int {
 
 // IndexRune returns the index of the first instance of the Unicode code point
 // r, or -1 if rune is not present in s.
+// If r is utf8.RuneError, it returns the first instance of any
+// invalid UTF-8 byte sequence.
 func IndexRune(s string, r rune) int {
 	switch {
-	case r < utf8.RuneSelf:
+	case 0 <= r && r < utf8.RuneSelf:
 		return IndexByte(s, byte(r))
-	default:
-		for i, c := range s {
-			if c == r {
+	case r == utf8.RuneError:
+		for i, r := range s {
+			if r == utf8.RuneError {
 				return i
 			}
 		}
+		return -1
+	case !utf8.ValidRune(r):
+		return -1
+	default:
+		return Index(s, string(r))
 	}
-	return -1
 }
 
 // IndexAny returns the index of the first instance of any Unicode code point
 // from chars in s, or -1 if no Unicode code point from chars is present in s.
 func IndexAny(s, chars string) int {
 	if len(chars) > 0 {
+		if len(s) > 8 {
+			if as, isASCII := makeASCIISet(chars); isASCII {
+				for i := 0; i < len(s); i++ {
+					if as.contains(s[i]) {
+						return i
+					}
+				}
+				return -1
+			}
+		}
 		for i, c := range s {
 			for _, m := range chars {
 				if c == m {
@@ -209,11 +195,21 @@ func IndexAny(s, chars string) int {
 // present in s.
 func LastIndexAny(s, chars string) int {
 	if len(chars) > 0 {
+		if len(s) > 8 {
+			if as, isASCII := makeASCIISet(chars); isASCII {
+				for i := len(s) - 1; i >= 0; i-- {
+					if as.contains(s[i]) {
+						return i
+					}
+				}
+				return -1
+			}
+		}
 		for i := len(s); i > 0; {
-			rune, size := utf8.DecodeLastRuneInString(s[0:i])
+			r, size := utf8.DecodeLastRuneInString(s[:i])
 			i -= size
-			for _, m := range chars {
-				if rune == m {
+			for _, c := range chars {
+				if r == c {
 					return i
 				}
 			}
@@ -342,11 +338,19 @@ func FieldsFunc(s string, f func(rune) bool) []string {
 // Join concatenates the elements of a to create a single string. The separator string
 // sep is placed between elements in the resulting string.
 func Join(a []string, sep string) string {
-	if len(a) == 0 {
+	switch len(a) {
+	case 0:
 		return ""
-	}
-	if len(a) == 1 {
+	case 1:
 		return a[0]
+	case 2:
+		// Special case for common small values.
+		// Remove if golang.org/issue/6714 is fixed
+		return a[0] + sep + a[1]
+	case 3:
+		// Special case for common small values.
+		// Remove if golang.org/issue/6714 is fixed
+		return a[0] + sep + a[1] + sep + a[2]
 	}
 	n := len(sep) * (len(a) - 1)
 	for i := 0; i < len(a); i++ {
@@ -416,7 +420,20 @@ func Map(mapping func(rune) rune, s string) string {
 }
 
 // Repeat returns a new string consisting of count copies of the string s.
+//
+// It panics if count is negative or if
+// the result of (len(s) * count) overflows.
 func Repeat(s string, count int) string {
+	// Since we cannot return an error on overflow,
+	// we should panic if the repeat will generate
+	// an overflow.
+	// See Issue golang.org/issue/16237
+	if count < 0 {
+		panic("strings: negative Repeat count")
+	} else if count > 0 && len(s)*count/count != len(s) {
+		panic("strings: Repeat count causes overflow")
+	}
+
 	b := make([]byte, len(s)*count)
 	bp := copy(b, s)
 	for bp < len(b) {
@@ -437,20 +454,20 @@ func ToTitle(s string) string { return Map(unicode.ToTitle, s) }
 
 // ToUpperSpecial returns a copy of the string s with all Unicode letters mapped to their
 // upper case, giving priority to the special casing rules.
-func ToUpperSpecial(_case unicode.SpecialCase, s string) string {
-	return Map(func(r rune) rune { return _case.ToUpper(r) }, s)
+func ToUpperSpecial(c unicode.SpecialCase, s string) string {
+	return Map(func(r rune) rune { return c.ToUpper(r) }, s)
 }
 
 // ToLowerSpecial returns a copy of the string s with all Unicode letters mapped to their
 // lower case, giving priority to the special casing rules.
-func ToLowerSpecial(_case unicode.SpecialCase, s string) string {
-	return Map(func(r rune) rune { return _case.ToLower(r) }, s)
+func ToLowerSpecial(c unicode.SpecialCase, s string) string {
+	return Map(func(r rune) rune { return c.ToLower(r) }, s)
 }
 
 // ToTitleSpecial returns a copy of the string s with all Unicode letters mapped to their
 // title case, giving priority to the special casing rules.
-func ToTitleSpecial(_case unicode.SpecialCase, s string) string {
-	return Map(func(r rune) rune { return _case.ToTitle(r) }, s)
+func ToTitleSpecial(c unicode.SpecialCase, s string) string {
+	return Map(func(r rune) rune { return c.ToTitle(r) }, s)
 }
 
 // isSeparator reports whether the rune could mark a word boundary.
@@ -573,7 +590,43 @@ func lastIndexFunc(s string, f func(rune) bool, truth bool) int {
 	return -1
 }
 
+// asciiSet is a 32-byte value, where each bit represents the presence of a
+// given ASCII character in the set. The 128-bits of the lower 16 bytes,
+// starting with the least-significant bit of the lowest word to the
+// most-significant bit of the highest word, map to the full range of all
+// 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed,
+// ensuring that any non-ASCII character will be reported as not in the set.
+type asciiSet [8]uint32
+
+// makeASCIISet creates a set of ASCII characters and reports whether all
+// characters in chars are ASCII.
+func makeASCIISet(chars string) (as asciiSet, ok bool) {
+	for i := 0; i < len(chars); i++ {
+		c := chars[i]
+		if c >= utf8.RuneSelf {
+			return as, false
+		}
+		as[c>>5] |= 1 << uint(c&31)
+	}
+	return as, true
+}
+
+// contains reports whether c is inside the set.
+func (as *asciiSet) contains(c byte) bool {
+	return (as[c>>5] & (1 << uint(c&31))) != 0
+}
+
 func makeCutsetFunc(cutset string) func(rune) bool {
+	if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
+		return func(r rune) bool {
+			return r == rune(cutset[0])
+		}
+	}
+	if as, isASCII := makeASCIISet(cutset); isASCII {
+		return func(r rune) bool {
+			return r < utf8.RuneSelf && as.contains(byte(r))
+		}
+	}
 	return func(r rune) bool { return IndexRune(cutset, r) >= 0 }
 }
 
diff --git a/src/strings/strings_amd64.go b/src/strings/strings_amd64.go
index 55bf2d2..23a98d5 100644
--- a/src/strings/strings_amd64.go
+++ b/src/strings/strings_amd64.go
@@ -4,10 +4,22 @@
 
 package strings
 
+//go:noescape
+
 // indexShortStr returns the index of the first instance of c in s, or -1 if c is not present in s.
 // indexShortStr requires 2 <= len(c) <= shortStringLen
 func indexShortStr(s, c string) int // ../runtime/asm_$GOARCH.s
-const shortStringLen = 31
+func supportAVX2() bool             // ../runtime/asm_$GOARCH.s
+
+var shortStringLen int
+
+func init() {
+	if supportAVX2() {
+		shortStringLen = 63
+	} else {
+		shortStringLen = 31
+	}
+}
 
 // Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
 func Index(s, sep string) int {
@@ -17,8 +29,6 @@ func Index(s, sep string) int {
 		return 0
 	case n == 1:
 		return IndexByte(s, sep[0])
-	case n <= shortStringLen:
-		return indexShortStr(s, sep)
 	case n == len(s):
 		if sep == s {
 			return 0
@@ -26,6 +36,42 @@ func Index(s, sep string) int {
 		return -1
 	case n > len(s):
 		return -1
+	case n <= shortStringLen:
+		// Use brute force when s and sep both are small
+		if len(s) <= 64 {
+			return indexShortStr(s, sep)
+		}
+		c := sep[0]
+		i := 0
+		t := s[:len(s)-n+1]
+		fails := 0
+		for i < len(t) {
+			if t[i] != c {
+				// IndexByte skips 16/32 bytes per iteration,
+				// so it's faster than indexShortStr.
+				o := IndexByte(t[i:], c)
+				if o < 0 {
+					return -1
+				}
+				i += o
+			}
+			if s[i:i+n] == sep {
+				return i
+			}
+			fails++
+			i++
+			// Switch to indexShortStr when IndexByte produces too many false positives.
+			// Too many means more that 1 error per 8 characters.
+			// Allow some errors in the beginning.
+			if fails > (i+16)/8 {
+				r := indexShortStr(s[i:], sep)
+				if r >= 0 {
+					return r + i
+				}
+				return -1
+			}
+		}
+		return -1
 	}
 	// Rabin-Karp search
 	hashsep, pow := hashStr(sep)
diff --git a/src/strings/strings_generic.go b/src/strings/strings_generic.go
index d356f50..6e80559 100644
--- a/src/strings/strings_generic.go
+++ b/src/strings/strings_generic.go
@@ -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 !amd64
+// +build !amd64,!s390x
 
 package strings
 
diff --git a/src/strings/strings_s390x.go b/src/strings/strings_s390x.go
new file mode 100644
index 0000000..316a1b8
--- /dev/null
+++ b/src/strings/strings_s390x.go
@@ -0,0 +1,98 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package strings
+
+//go:noescape
+
+// indexShortStr returns the index of the first instance of sep in s,
+// or -1 if sep is not present in s.
+// indexShortStr requires 2 <= len(sep) <= shortStringLen
+func indexShortStr(s, sep string) int // ../runtime/asm_$GOARCH.s
+
+// supportsVX reports whether the vector facility is available.
+// indexShortStr must not be called if the vector facility is not
+// available.
+func supportsVX() bool // ../runtime/asm_s390x.s
+
+var shortStringLen = -1
+
+func init() {
+	if supportsVX() {
+		shortStringLen = 64
+	}
+}
+
+// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
+func Index(s, sep string) int {
+	n := len(sep)
+	switch {
+	case n == 0:
+		return 0
+	case n == 1:
+		return IndexByte(s, sep[0])
+	case n == len(s):
+		if sep == s {
+			return 0
+		}
+		return -1
+	case n > len(s):
+		return -1
+	case n <= shortStringLen:
+		// Use brute force when s and sep both are small
+		if len(s) <= 64 {
+			return indexShortStr(s, sep)
+		}
+		c := sep[0]
+		i := 0
+		t := s[:len(s)-n+1]
+		fails := 0
+		for i < len(t) {
+			if t[i] != c {
+				// IndexByte skips 16/32 bytes per iteration,
+				// so it's faster than indexShortStr.
+				o := IndexByte(t[i:], c)
+				if o < 0 {
+					return -1
+				}
+				i += o
+			}
+			if s[i:i+n] == sep {
+				return i
+			}
+			fails++
+			i++
+			// Switch to indexShortStr when IndexByte produces too many false positives.
+			// Too many means more that 1 error per 8 characters.
+			// Allow some errors in the beginning.
+			if fails > (i+16)/8 {
+				r := indexShortStr(s[i:], sep)
+				if r >= 0 {
+					return r + i
+				}
+				return -1
+			}
+		}
+		return -1
+	}
+	// Rabin-Karp search
+	hashsep, pow := hashStr(sep)
+	var h uint32
+	for i := 0; i < n; i++ {
+		h = h*primeRK + uint32(s[i])
+	}
+	if h == hashsep && s[:n] == sep {
+		return 0
+	}
+	for i := n; i < len(s); {
+		h *= primeRK
+		h += uint32(s[i])
+		h -= pow * uint32(s[i-n])
+		i++
+		if h == hashsep && s[i-n:i] == sep {
+			return i - n
+		}
+	}
+	return -1
+}
diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go
index fcef761..4397949 100644
--- a/src/strings/strings_test.go
+++ b/src/strings/strings_test.go
@@ -6,6 +6,7 @@ package strings_test
 
 import (
 	"bytes"
+	"fmt"
 	"io"
 	"math/rand"
 	"reflect"
@@ -86,32 +87,44 @@ var indexTests = []IndexTest{
 	{"32145678", "01234567", -1},
 	{"01234567", "01234567", 0},
 	{"x01234567", "01234567", 1},
+	{"x0123456x01234567", "01234567", 9},
 	{"xx01234567"[:9], "01234567", -1},
 	{"", "0123456789", -1},
 	{"3214567844", "0123456789", -1},
 	{"0123456789", "0123456789", 0},
 	{"x0123456789", "0123456789", 1},
+	{"x012345678x0123456789", "0123456789", 11},
 	{"xyz0123456789"[:12], "0123456789", -1},
 	{"x01234567x89", "0123456789", -1},
 	{"", "0123456789012345", -1},
 	{"3214567889012345", "0123456789012345", -1},
 	{"0123456789012345", "0123456789012345", 0},
 	{"x0123456789012345", "0123456789012345", 1},
+	{"x012345678901234x0123456789012345", "0123456789012345", 17},
 	{"", "01234567890123456789", -1},
 	{"32145678890123456789", "01234567890123456789", -1},
 	{"01234567890123456789", "01234567890123456789", 0},
 	{"x01234567890123456789", "01234567890123456789", 1},
+	{"x0123456789012345678x01234567890123456789", "01234567890123456789", 21},
 	{"xyz01234567890123456789"[:22], "01234567890123456789", -1},
 	{"", "0123456789012345678901234567890", -1},
 	{"321456788901234567890123456789012345678911", "0123456789012345678901234567890", -1},
 	{"0123456789012345678901234567890", "0123456789012345678901234567890", 0},
 	{"x0123456789012345678901234567890", "0123456789012345678901234567890", 1},
+	{"x012345678901234567890123456789x0123456789012345678901234567890", "0123456789012345678901234567890", 32},
 	{"xyz0123456789012345678901234567890"[:33], "0123456789012345678901234567890", -1},
 	{"", "01234567890123456789012345678901", -1},
 	{"32145678890123456789012345678901234567890211", "01234567890123456789012345678901", -1},
 	{"01234567890123456789012345678901", "01234567890123456789012345678901", 0},
 	{"x01234567890123456789012345678901", "01234567890123456789012345678901", 1},
+	{"x0123456789012345678901234567890x01234567890123456789012345678901", "01234567890123456789012345678901", 33},
 	{"xyz01234567890123456789012345678901"[:34], "01234567890123456789012345678901", -1},
+	{"xxxxxx012345678901234567890123456789012345678901234567890123456789012", "012345678901234567890123456789012345678901234567890123456789012", 6},
+	{"", "0123456789012345678901234567890123456789", -1},
+	{"xx012345678901234567890123456789012345678901234567890123456789012", "0123456789012345678901234567890123456789", 2},
+	{"xx012345678901234567890123456789012345678901234567890123456789012"[:41], "0123456789012345678901234567890123456789", -1},
+	{"xx012345678901234567890123456789012345678901234567890123456789012", "0123456789012345678901234567890123456xxx", -1},
+	{"xx0123456789012345678901234567890123456789012345678901234567890120123456789012345678901234567890123456xxx", "0123456789012345678901234567890123456xxx", 65},
 }
 
 var lastIndexTests = []IndexTest{
@@ -139,10 +152,15 @@ var indexAnyTests = []IndexTest{
 	{"aaa", "a", 0},
 	{"abc", "xyz", -1},
 	{"abc", "xcz", 2},
-	{"a☺b☻c☹d", "uvw☻xyz", 2 + len("☺")},
+	{"ab☺c", "x☺yz", 2},
+	{"a☺b☻c☹d", "cx", len("a☺b☻")},
+	{"a☺b☻c☹d", "uvw☻xyz", len("a☺b")},
 	{"aRegExp*", ".(|)*+?^$[]", 7},
 	{dots + dots + dots, " ", -1},
+	{"012abcba210", "\xffb", 4},
+	{"012\x80bcb\x80210", "\xffb", 3},
 }
+
 var lastIndexAnyTests = []IndexTest{
 	{"", "", -1},
 	{"", "a", -1},
@@ -152,9 +170,13 @@ var lastIndexAnyTests = []IndexTest{
 	{"aaa", "a", 2},
 	{"abc", "xyz", -1},
 	{"abc", "ab", 1},
-	{"a☺b☻c☹d", "uvw☻xyz", 2 + len("☺")},
+	{"ab☺c", "x☺yz", 2},
+	{"a☺b☻c☹d", "cx", len("a☺b☻")},
+	{"a☺b☻c☹d", "uvw☻xyz", len("a☺b")},
 	{"a.RegExp*", ".(|)*+?^$[]", 8},
 	{dots + dots + dots, " ", -1},
+	{"012abcba210", "\xffb", 6},
+	{"012\x80bcb\x80210", "\xffb", 7},
 }
 
 // Execute f on each test case.  funcName should be the name of f; it's used
@@ -227,23 +249,54 @@ func TestIndexRandom(t *testing.T) {
 	}
 }
 
-var indexRuneTests = []struct {
-	s    string
-	rune rune
-	out  int
-}{
-	{"a A x", 'A', 2},
-	{"some_text=some_value", '=', 9},
-	{"☺a", 'a', 3},
-	{"a☻☺b", '☺', 4},
-}
-
 func TestIndexRune(t *testing.T) {
-	for _, test := range indexRuneTests {
-		if actual := IndexRune(test.s, test.rune); actual != test.out {
-			t.Errorf("IndexRune(%q,%d)= %v; want %v", test.s, test.rune, actual, test.out)
+	tests := []struct {
+		in   string
+		rune rune
+		want int
+	}{
+		{"", 'a', -1},
+		{"", '☺', -1},
+		{"foo", '☹', -1},
+		{"foo", 'o', 1},
+		{"foo☺bar", '☺', 3},
+		{"foo☺☻☹bar", '☹', 9},
+		{"a A x", 'A', 2},
+		{"some_text=some_value", '=', 9},
+		{"☺a", 'a', 3},
+		{"a☻☺b", '☺', 4},
+
+		// RuneError should match any invalid UTF-8 byte sequence.
+		{"�", '�', 0},
+		{"\xff", '�', 0},
+		{"☻x�", '�', len("☻x")},
+		{"☻x\xe2\x98", '�', len("☻x")},
+		{"☻x\xe2\x98�", '�', len("☻x")},
+		{"☻x\xe2\x98x", '�', len("☻x")},
+
+		// Invalid rune values should never match.
+		{"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", -1, -1},
+		{"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", 0xD800, -1}, // Surrogate pair
+		{"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", utf8.MaxRune + 1, -1},
+	}
+	for _, tt := range tests {
+		if got := IndexRune(tt.in, tt.rune); got != tt.want {
+			t.Errorf("IndexRune(%q, %d) = %v; want %v", tt.in, tt.rune, got, tt.want)
 		}
 	}
+
+	haystack := "test世界"
+	allocs := testing.AllocsPerRun(1000, func() {
+		if i := IndexRune(haystack, 's'); i != 2 {
+			t.Fatalf("'s' at %d; want 2", i)
+		}
+		if i := IndexRune(haystack, '世'); i != 4 {
+			t.Fatalf("'世' at %d; want 4", i)
+		}
+	})
+	if allocs != 0 && testing.CoverMode() == "" {
+		t.Errorf("expected no allocations, got %f", allocs)
+	}
 }
 
 const benchmarkString = "some_text=some☺value"
@@ -257,6 +310,17 @@ func BenchmarkIndexRune(b *testing.B) {
 	}
 }
 
+var benchmarkLongString = Repeat(" ", 100) + benchmarkString
+
+func BenchmarkIndexRuneLongString(b *testing.B) {
+	if got := IndexRune(benchmarkLongString, '☺'); got != 114 {
+		b.Fatalf("wrong index: expected 114, got=%d", got)
+	}
+	for i := 0; i < b.N; i++ {
+		IndexRune(benchmarkLongString, '☺')
+	}
+}
+
 func BenchmarkIndexRuneFastPath(b *testing.B) {
 	if got := IndexRune(benchmarkString, 'v'); got != 17 {
 		b.Fatalf("wrong index: expected 17, got=%d", got)
@@ -613,6 +677,9 @@ var trimTests = []struct {
 	{"Trim", "* listitem", " *", "listitem"},
 	{"Trim", `"quote"`, `"`, "quote"},
 	{"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"},
+	{"Trim", "\x80test\xff", "\xff", "test"},
+	{"Trim", " Ġ ", " ", "Ġ"},
+	{"Trim", " Ġİ0", "0 ", "Ġİ"},
 	//empty string tests
 	{"Trim", "abba", "", "abba"},
 	{"Trim", "", "123", ""},
@@ -855,6 +922,54 @@ func TestRepeat(t *testing.T) {
 	}
 }
 
+func repeat(s string, count int) (err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			switch v := r.(type) {
+			case error:
+				err = v
+			default:
+				err = fmt.Errorf("%s", v)
+			}
+		}
+	}()
+
+	Repeat(s, count)
+
+	return
+}
+
+// See Issue golang.org/issue/16237
+func TestRepeatCatchesOverflow(t *testing.T) {
+	tests := [...]struct {
+		s      string
+		count  int
+		errStr string
+	}{
+		0: {"--", -2147483647, "negative"},
+		1: {"", int(^uint(0) >> 1), ""},
+		2: {"-", 10, ""},
+		3: {"gopher", 0, ""},
+		4: {"-", -1, "negative"},
+		5: {"--", -102, "negative"},
+		6: {string(make([]byte, 255)), int((^uint(0))/255 + 1), "overflow"},
+	}
+
+	for i, tt := range tests {
+		err := repeat(tt.s, tt.count)
+		if tt.errStr == "" {
+			if err != nil {
+				t.Errorf("#%d panicked %v", i, err)
+			}
+			continue
+		}
+
+		if err == nil || !Contains(err.Error(), tt.errStr) {
+			t.Errorf("#%d expected %q got %q", i, tt.errStr, err)
+		}
+	}
+}
+
 func runesEqual(a, b []rune) bool {
 	if len(a) != len(b) {
 		return false
@@ -1290,6 +1405,9 @@ func benchmarkCountHard(b *testing.B, sep string) {
 func BenchmarkIndexHard1(b *testing.B) { benchmarkIndexHard(b, "<>") }
 func BenchmarkIndexHard2(b *testing.B) { benchmarkIndexHard(b, "</pre>") }
 func BenchmarkIndexHard3(b *testing.B) { benchmarkIndexHard(b, "<b>hello world</b>") }
+func BenchmarkIndexHard4(b *testing.B) {
+	benchmarkIndexHard(b, "<pre><b>hello</b><strong>world</strong></pre>")
+}
 
 func BenchmarkLastIndexHard1(b *testing.B) { benchmarkLastIndexHard(b, "<>") }
 func BenchmarkLastIndexHard2(b *testing.B) { benchmarkLastIndexHard(b, "</pre>") }
@@ -1381,3 +1499,31 @@ func BenchmarkRepeat(b *testing.B) {
 		Repeat("-", 80)
 	}
 }
+
+func BenchmarkIndexAnyASCII(b *testing.B) {
+	x := Repeat("#", 4096) // Never matches set
+	cs := "0123456789abcdef"
+	for k := 1; k <= 4096; k <<= 4 {
+		for j := 1; j <= 16; j <<= 1 {
+			b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
+				for i := 0; i < b.N; i++ {
+					IndexAny(x[:k], cs[:j])
+				}
+			})
+		}
+	}
+}
+
+func BenchmarkTrimASCII(b *testing.B) {
+	cs := "0123456789abcdef"
+	for k := 1; k <= 4096; k <<= 4 {
+		for j := 1; j <= 16; j <<= 1 {
+			b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
+				x := Repeat(cs[:j], k) // Always matches set
+				for i := 0; i < b.N; i++ {
+					Trim(x[:k], cs[:j])
+				}
+			})
+		}
+	}
+}
diff --git a/src/sync/atomic/asm_amd64.s b/src/sync/atomic/asm_amd64.s
index 690907c..eddc6c5 100644
--- a/src/sync/atomic/asm_amd64.s
+++ b/src/sync/atomic/asm_amd64.s
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// Note: some of these functions are semantically inlined
+// by the compiler (in src/cmd/compile/internal/gc/ssa.go).
+
 // +build !race
 
 #include "textflag.h"
diff --git a/src/sync/atomic/asm_amd64p32.s b/src/sync/atomic/asm_amd64p32.s
index 8164b3e..5c64dc0 100644
--- a/src/sync/atomic/asm_amd64p32.s
+++ b/src/sync/atomic/asm_amd64p32.s
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// Note: some of these functions are semantically inlined
+// by the compiler (in src/cmd/compile/internal/gc/ssa.go).
+
 #include "textflag.h"
 
 TEXT ·SwapInt32(SB),NOSPLIT,$0-12
@@ -50,9 +53,6 @@ TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0-25
 
 TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-25
 	MOVL	addr+0(FP), BX
-	TESTL	$7, BX
-	JZ	2(PC)
-	MOVL	0, BX // crash with nil ptr deref
 	MOVQ	old+8(FP), AX
 	MOVQ	new+16(FP), CX
 	LOCK
@@ -81,9 +81,6 @@ TEXT ·AddInt64(SB),NOSPLIT,$0-24
 
 TEXT ·AddUint64(SB),NOSPLIT,$0-24
 	MOVL	addr+0(FP), BX
-	TESTL	$7, BX
-	JZ	2(PC)
-	MOVL	0, BX // crash with nil ptr deref
 	MOVQ	delta+8(FP), AX
 	MOVQ	AX, CX
 	LOCK
@@ -106,9 +103,6 @@ TEXT ·LoadInt64(SB),NOSPLIT,$0-16
 
 TEXT ·LoadUint64(SB),NOSPLIT,$0-16
 	MOVL	addr+0(FP), AX
-	TESTL	$7, AX
-	JZ	2(PC)
-	MOVL	0, AX // crash with nil ptr deref
 	MOVQ	0(AX), AX
 	MOVQ	AX, val+8(FP)
 	RET
@@ -136,9 +130,6 @@ TEXT ·StoreInt64(SB),NOSPLIT,$0-16
 
 TEXT ·StoreUint64(SB),NOSPLIT,$0-16
 	MOVL	addr+0(FP), BX
-	TESTL	$7, BX
-	JZ	2(PC)
-	MOVL	0, BX // crash with nil ptr deref
 	MOVQ	val+8(FP), AX
 	XCHGQ	AX, 0(BX)
 	RET
diff --git a/src/sync/atomic/asm_arm.s b/src/sync/atomic/asm_arm.s
index d35ea2a..77b0b24 100644
--- a/src/sync/atomic/asm_arm.s
+++ b/src/sync/atomic/asm_arm.s
@@ -35,11 +35,11 @@ casloop:
 	BNE	casloop
 	MOVW	$1, R0
 	DMB_ISH_7
-	MOVBU	R0, ret+12(FP)
+	MOVBU	R0, swapped+12(FP)
 	RET
 casfail:
 	MOVW	$0, R0
-	MOVBU	R0, ret+12(FP)
+	MOVBU	R0, swapped+12(FP)
 	RET
 
 TEXT ·armCompareAndSwapUint64(SB),NOSPLIT,$0-21
@@ -49,10 +49,10 @@ TEXT ·armCompareAndSwapUint64(SB),NOSPLIT,$0-21
 	AND.S	$7, R1, R2
 	BEQ 	2(PC)
 	MOVW	R2, (R2)
-	MOVW	oldlo+4(FP), R2
-	MOVW	oldhi+8(FP), R3
-	MOVW	newlo+12(FP), R4
-	MOVW	newhi+16(FP), R5
+	MOVW	old_lo+4(FP), R2
+	MOVW	old_hi+8(FP), R3
+	MOVW	new_lo+12(FP), R4
+	MOVW	new_hi+16(FP), R5
 cas64loop:
 	// LDREXD and STREXD were introduced in ARMv6k.
 	LDREXD	(R1), R6  // loads R6 and R7
@@ -66,11 +66,11 @@ cas64loop:
 	BNE	cas64loop
 	MOVW	$1, R0
 	DMB_ISH_7
-	MOVBU	R0, ret+20(FP)
+	MOVBU	R0, swapped+20(FP)
 	RET
 cas64fail:
 	MOVW	$0, R0
-	MOVBU	R0, ret+20(FP)
+	MOVBU	R0, swapped+20(FP)
 	RET
 
 TEXT ·armAddUint32(SB),NOSPLIT,$0-12
@@ -85,7 +85,7 @@ addloop:
 	CMP	$0, R0
 	BNE	addloop
 	DMB_ISH_7
-	MOVW	R3, ret+8(FP)
+	MOVW	R3, new+8(FP)
 	RET
 
 TEXT ·armAddUint64(SB),NOSPLIT,$0-20
@@ -95,8 +95,8 @@ TEXT ·armAddUint64(SB),NOSPLIT,$0-20
 	AND.S	$7, R1, R2
 	BEQ 	2(PC)
 	MOVW	R2, (R2)
-	MOVW	deltalo+4(FP), R2
-	MOVW	deltahi+8(FP), R3
+	MOVW	delta_lo+4(FP), R2
+	MOVW	delta_hi+8(FP), R3
 add64loop:
 	// LDREXD and STREXD were introduced in ARMv6k.
 	LDREXD	(R1), R4	// loads R4 and R5
@@ -107,8 +107,8 @@ add64loop:
 	CMP	$0, R0
 	BNE	add64loop
 	DMB_ISH_7
-	MOVW	R4, retlo+12(FP)
-	MOVW	R5, rethi+16(FP)
+	MOVW	R4, new_lo+12(FP)
+	MOVW	R5, new_hi+16(FP)
 	RET
 
 TEXT ·armSwapUint32(SB),NOSPLIT,$0-12
@@ -132,8 +132,8 @@ TEXT ·armSwapUint64(SB),NOSPLIT,$0-20
 	AND.S	$7, R1, R2
 	BEQ 	2(PC)
 	MOVW	R2, (R2)
-	MOVW	newlo+4(FP), R2
-	MOVW	newhi+8(FP), R3
+	MOVW	new_lo+4(FP), R2
+	MOVW	new_hi+8(FP), R3
 swap64loop:
 	// LDREXD and STREXD were introduced in ARMv6k.
 	LDREXD	(R1), R4	// loads R4 and R5
@@ -142,8 +142,8 @@ swap64loop:
 	CMP	$0, R0
 	BNE	swap64loop
 	DMB_ISH_7
-	MOVW	R4, oldlo+12(FP)
-	MOVW	R5, oldhi+16(FP)
+	MOVW	R4, old_lo+12(FP)
+	MOVW	R5, old_hi+16(FP)
 	RET
 
 TEXT ·armLoadUint64(SB),NOSPLIT,$0-12
@@ -160,8 +160,8 @@ load64loop:
 	CMP	$0, R0
 	BNE	load64loop
 	DMB_ISH_7
-	MOVW	R2, vallo+4(FP)
-	MOVW	R3, valhi+8(FP)
+	MOVW	R2, val_lo+4(FP)
+	MOVW	R3, val_hi+8(FP)
 	RET
 
 TEXT ·armStoreUint64(SB),NOSPLIT,$0-12
@@ -171,8 +171,8 @@ TEXT ·armStoreUint64(SB),NOSPLIT,$0-12
 	AND.S	$7, R1, R2
 	BEQ 	2(PC)
 	MOVW	R2, (R2)
-	MOVW	vallo+4(FP), R2
-	MOVW	valhi+8(FP), R3
+	MOVW	val_lo+4(FP), R2
+	MOVW	val_hi+8(FP), R3
 store64loop:
 	LDREXD	(R1), R4	// loads R4 and R5
 	DMB_ISHST_7
diff --git a/src/sync/atomic/asm_mips64x.s b/src/sync/atomic/asm_mips64x.s
index b3c4627..b7d4168 100644
--- a/src/sync/atomic/asm_mips64x.s
+++ b/src/sync/atomic/asm_mips64x.s
@@ -104,7 +104,7 @@ TEXT ·AddUint32(SB),NOSPLIT,$0-20
 	MOVV	R4, R1
 	SC(2, 4)	// *R2 = R4
 	BEQ	R4, -4(PC)
-	MOVW	R1, ret+16(FP)
+	MOVW	R1, new+16(FP)
 	SYNC
 	RET
 
@@ -123,7 +123,7 @@ TEXT ·AddUint64(SB),NOSPLIT,$0-24
 	MOVV	R4, R1
 	SCV(2, 4)	// *R2 = R4
 	BEQ	R4, -4(PC)
-	MOVV	R1, ret+16(FP)
+	MOVV	R1, new+16(FP)
 	SYNC
 	RET
 
diff --git a/src/sync/atomic/asm_mipsx.s b/src/sync/atomic/asm_mipsx.s
new file mode 100644
index 0000000..cf3318f
--- /dev/null
+++ b/src/sync/atomic/asm_mipsx.s
@@ -0,0 +1,85 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build mips mipsle
+
+#include "textflag.h"
+
+TEXT ·SwapInt32(SB),NOSPLIT,$0-12
+	JMP	runtime∕internal∕atomic·Xchg(SB)
+
+TEXT ·SwapUint32(SB),NOSPLIT,$0-12
+	JMP	runtime∕internal∕atomic·Xchg(SB)
+
+TEXT ·SwapInt64(SB),NOSPLIT,$0-24
+	JMP	runtime∕internal∕atomic·Xchg64(SB)
+
+TEXT ·SwapUint64(SB),NOSPLIT,$0-24
+	JMP	runtime∕internal∕atomic·Xchg64(SB)
+
+TEXT ·SwapUintptr(SB),NOSPLIT,$0-20
+	JMP	runtime∕internal∕atomic·Xchg(SB)
+
+TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0-13
+	JMP	runtime∕internal∕atomic·Cas(SB)
+
+TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0-13
+	JMP	runtime∕internal∕atomic·Cas(SB)
+
+TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0-13
+	JMP	runtime∕internal∕atomic·Cas(SB)
+
+TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0-21
+	JMP	runtime∕internal∕atomic·Cas64(SB)
+
+TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-21
+	JMP	runtime∕internal∕atomic·Cas64(SB)
+
+TEXT ·AddInt32(SB),NOSPLIT,$0-12
+	JMP	runtime∕internal∕atomic·Xadd(SB)
+
+TEXT ·AddUint32(SB),NOSPLIT,$0-12
+	JMP	runtime∕internal∕atomic·Xadd(SB)
+
+TEXT ·AddUintptr(SB),NOSPLIT,$0-12
+	JMP	runtime∕internal∕atomic·Xadd(SB)
+
+TEXT ·AddInt64(SB),NOSPLIT,$0-20
+	JMP	runtime∕internal∕atomic·Xadd64(SB)
+
+TEXT ·AddUint64(SB),NOSPLIT,$0-20
+	JMP	runtime∕internal∕atomic·Xadd64(SB)
+
+TEXT ·LoadInt32(SB),NOSPLIT,$0-8
+	JMP	runtime∕internal∕atomic·Load(SB)
+
+TEXT ·LoadUint32(SB),NOSPLIT,$0-8
+	JMP	runtime∕internal∕atomic·Load(SB)
+
+TEXT ·LoadInt64(SB),NOSPLIT,$0-12
+	JMP	runtime∕internal∕atomic·Load64(SB)
+
+TEXT ·LoadUint64(SB),NOSPLIT,$0-12
+	JMP	runtime∕internal∕atomic·Load64(SB)
+
+TEXT ·LoadUintptr(SB),NOSPLIT,$0-8
+	JMP	runtime∕internal∕atomic·Load(SB)
+
+TEXT ·LoadPointer(SB),NOSPLIT,$0-8
+	JMP	runtime∕internal∕atomic·Load(SB)
+
+TEXT ·StoreInt32(SB),NOSPLIT,$0-8
+	JMP	runtime∕internal∕atomic·Store(SB)
+
+TEXT ·StoreUint32(SB),NOSPLIT,$0-8
+	JMP	runtime∕internal∕atomic·Store(SB)
+
+TEXT ·StoreInt64(SB),NOSPLIT,$0-12
+	JMP	runtime∕internal∕atomic·Store64(SB)
+
+TEXT ·StoreUint64(SB),NOSPLIT,$0-12
+	JMP	runtime∕internal∕atomic·Store64(SB)
+
+TEXT ·StoreUintptr(SB),NOSPLIT,$0-8
+	JMP	runtime∕internal∕atomic·Store(SB)
diff --git a/src/sync/atomic/asm_ppc64x.s b/src/sync/atomic/asm_ppc64x.s
index 2474e96..44e2669 100644
--- a/src/sync/atomic/asm_ppc64x.s
+++ b/src/sync/atomic/asm_ppc64x.s
@@ -92,7 +92,7 @@ TEXT ·AddUint32(SB),NOSPLIT,$0-20
 	STWCCC	R5, (R3)
 	BNE	-3(PC)
 	ISYNC
-	MOVW	R5, ret+16(FP)
+	MOVW	R5, new+16(FP)
 	RET
 
 TEXT ·AddUintptr(SB),NOSPLIT,$0-24
@@ -110,7 +110,7 @@ TEXT ·AddUint64(SB),NOSPLIT,$0-24
 	STDCCC	R5, (R3)
 	BNE	-3(PC)
 	ISYNC
-	MOVD	R5, ret+16(FP)
+	MOVD	R5, new+16(FP)
 	RET
 
 TEXT ·LoadInt32(SB),NOSPLIT,$0-12
diff --git a/src/sync/atomic/asm_s390x.s b/src/sync/atomic/asm_s390x.s
index b5389be..cf61013 100644
--- a/src/sync/atomic/asm_s390x.s
+++ b/src/sync/atomic/asm_s390x.s
@@ -37,15 +37,15 @@ TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0-17
 	BR	·CompareAndSwapUint32(SB)
 
 TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0-17
-	MOVD	ptr+0(FP), R3
+	MOVD	addr+0(FP), R3
 	MOVWZ	old+8(FP), R4
 	MOVWZ	new+12(FP), R5
 	CS	R4, R5, 0(R3) // if R4==(R3) then (R3)=R5 else R4=(R3)
 	BNE	cas_fail
-	MOVB	$1, ret+16(FP)
+	MOVB	$1, swapped+16(FP)
 	RET
 cas_fail:
-	MOVB	$0, ret+16(FP)
+	MOVB	$0, swapped+16(FP)
 	RET
 
 TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0-25
@@ -55,29 +55,29 @@ TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0-25
 	BR	·CompareAndSwapUint64(SB)
 
 TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-25
-	MOVD	ptr+0(FP), R3
+	MOVD	addr+0(FP), R3
 	MOVD	old+8(FP), R4
 	MOVD	new+16(FP), R5
 	CSG	R4, R5, 0(R3) // if R4==(R3) then (R3)=R5 else R4=(R3)
 	BNE	cas64_fail
-	MOVB	$1, ret+24(FP)
+	MOVB	$1, swapped+24(FP)
 	RET
 cas64_fail:
-	MOVB	$0, ret+24(FP)
+	MOVB	$0, swapped+24(FP)
 	RET
 
 TEXT ·AddInt32(SB),NOSPLIT,$0-20
 	BR	·AddUint32(SB)
 
 TEXT ·AddUint32(SB),NOSPLIT,$0-20
-	MOVD	ptr+0(FP), R4
+	MOVD	addr+0(FP), R4
 	MOVWZ	delta+8(FP), R5
 	MOVWZ	(R4), R3
 repeat:
 	ADD	R3, R5, R6
 	CS	R3, R6, (R4) // if R3==(R4) then (R4)=R6 else R3=(R4)
 	BNE	repeat
-	MOVW	R6, ret+16(FP)
+	MOVW	R6, new+16(FP)
 	RET
 
 TEXT ·AddUintptr(SB),NOSPLIT,$0-24
@@ -87,14 +87,14 @@ TEXT ·AddInt64(SB),NOSPLIT,$0-24
 	BR	·AddUint64(SB)
 
 TEXT ·AddUint64(SB),NOSPLIT,$0-24
-	MOVD	ptr+0(FP), R4
+	MOVD	addr+0(FP), R4
 	MOVD	delta+8(FP), R5
 	MOVD	(R4), R3
 repeat:
 	ADD	R3, R5, R6
 	CSG	R3, R6, (R4) // if R3==(R4) then (R4)=R6 else R3=(R4)
 	BNE	repeat
-	MOVD	R6, ret+16(FP)
+	MOVD	R6, new+16(FP)
 	RET
 
 TEXT ·LoadInt32(SB),NOSPLIT,$0-12
@@ -125,7 +125,7 @@ TEXT ·StoreInt32(SB),NOSPLIT,$0-12
 	BR	·StoreUint32(SB)
 
 TEXT ·StoreUint32(SB),NOSPLIT,$0-12
-	MOVD	ptr+0(FP), R3
+	MOVD	addr+0(FP), R3
 	MOVW	val+8(FP), R4
 	MOVW	R4, 0(R3)
 	RET
diff --git a/src/sync/atomic/atomic_test.go b/src/sync/atomic/atomic_test.go
index deb3ccb..6d0831c 100644
--- a/src/sync/atomic/atomic_test.go
+++ b/src/sync/atomic/atomic_test.go
@@ -1226,10 +1226,12 @@ func TestStoreLoadSeqCst32(t *testing.T) {
 				}
 				his := LoadInt32(&ack[he][i%3])
 				if (my != i && my != i-1) || (his != i && his != i-1) {
-					t.Fatalf("invalid values: %d/%d (%d)", my, his, i)
+					t.Errorf("invalid values: %d/%d (%d)", my, his, i)
+					break
 				}
 				if my != i && his != i {
-					t.Fatalf("store/load are not sequentially consistent: %d/%d (%d)", my, his, i)
+					t.Errorf("store/load are not sequentially consistent: %d/%d (%d)", my, his, i)
+					break
 				}
 				StoreInt32(&ack[me][(i-1)%3], -1)
 			}
@@ -1269,10 +1271,12 @@ func TestStoreLoadSeqCst64(t *testing.T) {
 				}
 				his := LoadInt64(&ack[he][i%3])
 				if (my != i && my != i-1) || (his != i && his != i-1) {
-					t.Fatalf("invalid values: %d/%d (%d)", my, his, i)
+					t.Errorf("invalid values: %d/%d (%d)", my, his, i)
+					break
 				}
 				if my != i && his != i {
-					t.Fatalf("store/load are not sequentially consistent: %d/%d (%d)", my, his, i)
+					t.Errorf("store/load are not sequentially consistent: %d/%d (%d)", my, his, i)
+					break
 				}
 				StoreInt64(&ack[me][(i-1)%3], -1)
 			}
@@ -1317,7 +1321,8 @@ func TestStoreLoadRelAcq32(t *testing.T) {
 					d1 := X.data1
 					d2 := X.data2
 					if d1 != i || d2 != float32(i) {
-						t.Fatalf("incorrect data: %d/%g (%d)", d1, d2, i)
+						t.Errorf("incorrect data: %d/%g (%d)", d1, d2, i)
+						break
 					}
 				}
 			}
@@ -1365,7 +1370,8 @@ func TestStoreLoadRelAcq64(t *testing.T) {
 					d1 := X.data1
 					d2 := X.data2
 					if d1 != i || d2 != float64(i) {
-						t.Fatalf("incorrect data: %d/%g (%d)", d1, d2, i)
+						t.Errorf("incorrect data: %d/%g (%d)", d1, d2, i)
+						break
 					}
 				}
 			}
@@ -1389,8 +1395,15 @@ func TestUnaligned64(t *testing.T) {
 	// Unaligned 64-bit atomics on 32-bit systems are
 	// a continual source of pain. Test that on 32-bit systems they crash
 	// instead of failing silently.
-	if unsafe.Sizeof(int(0)) != 4 {
-		t.Skip("test only runs on 32-bit systems")
+
+	switch runtime.GOARCH {
+	default:
+		if unsafe.Sizeof(int(0)) != 4 {
+			t.Skip("test only runs on 32-bit systems")
+		}
+	case "amd64p32":
+		// amd64p32 can handle unaligned atomics.
+		t.Skipf("test not needed on %v", runtime.GOARCH)
 	}
 
 	x := make([]uint32, 4)
diff --git a/src/sync/cond_test.go b/src/sync/cond_test.go
index 7b07295..9019f8f 100644
--- a/src/sync/cond_test.go
+++ b/src/sync/cond_test.go
@@ -137,7 +137,7 @@ func TestRace(t *testing.T) {
 		x = 1
 		c.Wait()
 		if x != 2 {
-			t.Fatal("want 2")
+			t.Error("want 2")
 		}
 		x = 3
 		c.Signal()
@@ -165,7 +165,7 @@ func TestRace(t *testing.T) {
 			if x == 2 {
 				c.Wait()
 				if x != 3 {
-					t.Fatal("want 3")
+					t.Error("want 3")
 				}
 				break
 			}
diff --git a/src/sync/example_pool_test.go b/src/sync/example_pool_test.go
new file mode 100644
index 0000000..8288d41
--- /dev/null
+++ b/src/sync/example_pool_test.go
@@ -0,0 +1,45 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sync_test
+
+import (
+	"bytes"
+	"io"
+	"os"
+	"sync"
+	"time"
+)
+
+var bufPool = sync.Pool{
+	New: func() interface{} {
+		// The Pool's New function should generally only return pointer
+		// types, since a pointer can be put into the return interface
+		// value without an allocation:
+		return new(bytes.Buffer)
+	},
+}
+
+// timeNow is a fake version of time.Now for tests.
+func timeNow() time.Time {
+	return time.Unix(1136214245, 0)
+}
+
+func Log(w io.Writer, key, val string) {
+	b := bufPool.Get().(*bytes.Buffer)
+	b.Reset()
+	// Replace this with time.Now() in a real logger.
+	b.WriteString(timeNow().UTC().Format(time.RFC3339))
+	b.WriteByte(' ')
+	b.WriteString(key)
+	b.WriteByte('=')
+	b.WriteString(val)
+	w.Write(b.Bytes())
+	bufPool.Put(b)
+}
+
+func ExamplePool() {
+	Log(os.Stdout, "path", "/search?q=flowers")
+	// Output: 2006-01-02T15:04:05Z path=/search?q=flowers
+}
diff --git a/src/sync/mutex.go b/src/sync/mutex.go
index 9089279..8c9366f 100644
--- a/src/sync/mutex.go
+++ b/src/sync/mutex.go
@@ -16,6 +16,8 @@ import (
 	"unsafe"
 )
 
+func throw(string) // provided by runtime
+
 // A Mutex is a mutual exclusion lock.
 // Mutexes can be created as part of other structures;
 // the zero value for a Mutex is an unlocked mutex.
@@ -74,7 +76,7 @@ func (m *Mutex) Lock() {
 			// The goroutine has been woken from sleep,
 			// so we need to reset the flag in either case.
 			if new&mutexWoken == 0 {
-				panic("sync: inconsistent mutex state")
+				throw("sync: inconsistent mutex state")
 			}
 			new &^= mutexWoken
 		}
@@ -82,7 +84,7 @@ func (m *Mutex) Lock() {
 			if old&mutexLocked == 0 {
 				break
 			}
-			runtime_Semacquire(&m.sema)
+			runtime_SemacquireMutex(&m.sema)
 			awoke = true
 			iter = 0
 		}
@@ -108,7 +110,7 @@ func (m *Mutex) Unlock() {
 	// Fast path: drop lock bit.
 	new := atomic.AddInt32(&m.state, -mutexLocked)
 	if (new+mutexLocked)&mutexLocked == 0 {
-		panic("sync: unlock of unlocked mutex")
+		throw("sync: unlock of unlocked mutex")
 	}
 
 	old := new
diff --git a/src/sync/mutex_test.go b/src/sync/mutex_test.go
index 91a4855..88dbccf 100644
--- a/src/sync/mutex_test.go
+++ b/src/sync/mutex_test.go
@@ -7,7 +7,12 @@
 package sync_test
 
 import (
+	"fmt"
+	"internal/testenv"
+	"os"
+	"os/exec"
 	"runtime"
+	"strings"
 	. "sync"
 	"testing"
 )
@@ -61,6 +66,10 @@ func HammerMutex(m *Mutex, loops int, cdone chan bool) {
 }
 
 func TestMutex(t *testing.T) {
+	if n := runtime.SetMutexProfileFraction(1); n != 0 {
+		t.Logf("got mutexrate %d expected 0", n)
+	}
+	defer runtime.SetMutexProfileFraction(0)
 	m := new(Mutex)
 	c := make(chan bool)
 	for i := 0; i < 10; i++ {
@@ -71,17 +80,98 @@ func TestMutex(t *testing.T) {
 	}
 }
 
-func TestMutexPanic(t *testing.T) {
-	defer func() {
-		if recover() == nil {
-			t.Fatalf("unlock of unlocked mutex did not panic")
+var misuseTests = []struct {
+	name string
+	f    func()
+}{
+	{
+		"Mutex.Unlock",
+		func() {
+			var mu Mutex
+			mu.Unlock()
+		},
+	},
+	{
+		"Mutex.Unlock2",
+		func() {
+			var mu Mutex
+			mu.Lock()
+			mu.Unlock()
+			mu.Unlock()
+		},
+	},
+	{
+		"RWMutex.Unlock",
+		func() {
+			var mu RWMutex
+			mu.Unlock()
+		},
+	},
+	{
+		"RWMutex.Unlock2",
+		func() {
+			var mu RWMutex
+			mu.RLock()
+			mu.Unlock()
+		},
+	},
+	{
+		"RWMutex.Unlock3",
+		func() {
+			var mu RWMutex
+			mu.Lock()
+			mu.Unlock()
+			mu.Unlock()
+		},
+	},
+	{
+		"RWMutex.RUnlock",
+		func() {
+			var mu RWMutex
+			mu.RUnlock()
+		},
+	},
+	{
+		"RWMutex.RUnlock2",
+		func() {
+			var mu RWMutex
+			mu.Lock()
+			mu.RUnlock()
+		},
+	},
+	{
+		"RWMutex.RUnlock3",
+		func() {
+			var mu RWMutex
+			mu.RLock()
+			mu.RUnlock()
+			mu.RUnlock()
+		},
+	},
+}
+
+func init() {
+	if len(os.Args) == 3 && os.Args[1] == "TESTMISUSE" {
+		for _, test := range misuseTests {
+			if test.name == os.Args[2] {
+				test.f()
+				fmt.Printf("test completed\n")
+				os.Exit(0)
+			}
 		}
-	}()
+		fmt.Printf("unknown test\n")
+		os.Exit(0)
+	}
+}
 
-	var mu Mutex
-	mu.Lock()
-	mu.Unlock()
-	mu.Unlock()
+func TestMutexMisuse(t *testing.T) {
+	testenv.MustHaveExec(t)
+	for _, test := range misuseTests {
+		out, err := exec.Command(os.Args[0], "TESTMISUSE", test.name).CombinedOutput()
+		if err == nil || !strings.Contains(string(out), "unlocked") {
+			t.Errorf("%s: did not find failure with message about unlocked lock: %s\n%s\n", test.name, err, out)
+		}
+	}
 }
 
 func BenchmarkMutexUncontended(b *testing.B) {
diff --git a/src/sync/pool.go b/src/sync/pool.go
index bf29d88..0acdbde 100644
--- a/src/sync/pool.go
+++ b/src/sync/pool.go
@@ -61,29 +61,49 @@ type poolLocal struct {
 	pad     [128]byte     // Prevents false sharing.
 }
 
+// from runtime
+func fastrand() uint32
+
+var poolRaceHash [128]uint64
+
+// poolRaceAddr returns an address to use as the synchronization point
+// for race detector logic. We don't use the actual pointer stored in x
+// directly, for fear of conflicting with other synchronization on that address.
+// Instead, we hash the pointer to get an index into poolRaceHash.
+// See discussion on golang.org/cl/31589.
+func poolRaceAddr(x interface{}) unsafe.Pointer {
+	ptr := uintptr((*[2]unsafe.Pointer)(unsafe.Pointer(&x))[1])
+	h := uint32((uint64(uint32(ptr)) * 0x85ebca6b) >> 16)
+	return unsafe.Pointer(&poolRaceHash[h%uint32(len(poolRaceHash))])
+}
+
 // Put adds x to the pool.
 func (p *Pool) Put(x interface{}) {
-	if race.Enabled {
-		// Under race detector the Pool degenerates into no-op.
-		// It's conforming, simple and does not introduce excessive
-		// happens-before edges between unrelated goroutines.
-		return
-	}
 	if x == nil {
 		return
 	}
+	if race.Enabled {
+		if fastrand()%4 == 0 {
+			// Randomly drop x on floor.
+			return
+		}
+		race.ReleaseMerge(poolRaceAddr(x))
+		race.Disable()
+	}
 	l := p.pin()
 	if l.private == nil {
 		l.private = x
 		x = nil
 	}
 	runtime_procUnpin()
-	if x == nil {
-		return
+	if x != nil {
+		l.Lock()
+		l.shared = append(l.shared, x)
+		l.Unlock()
+	}
+	if race.Enabled {
+		race.Enable()
 	}
-	l.Lock()
-	l.shared = append(l.shared, x)
-	l.Unlock()
 }
 
 // Get selects an arbitrary item from the Pool, removes it from the
@@ -96,29 +116,34 @@ func (p *Pool) Put(x interface{}) {
 // the result of calling p.New.
 func (p *Pool) Get() interface{} {
 	if race.Enabled {
-		if p.New != nil {
-			return p.New()
-		}
-		return nil
+		race.Disable()
 	}
 	l := p.pin()
 	x := l.private
 	l.private = nil
 	runtime_procUnpin()
-	if x != nil {
-		return x
+	if x == nil {
+		l.Lock()
+		last := len(l.shared) - 1
+		if last >= 0 {
+			x = l.shared[last]
+			l.shared = l.shared[:last]
+		}
+		l.Unlock()
+		if x == nil {
+			x = p.getSlow()
+		}
 	}
-	l.Lock()
-	last := len(l.shared) - 1
-	if last >= 0 {
-		x = l.shared[last]
-		l.shared = l.shared[:last]
+	if race.Enabled {
+		race.Enable()
+		if x != nil {
+			race.Acquire(poolRaceAddr(x))
+		}
 	}
-	l.Unlock()
-	if x != nil {
-		return x
+	if x == nil && p.New != nil {
+		x = p.New()
 	}
-	return p.getSlow()
+	return x
 }
 
 func (p *Pool) getSlow() (x interface{}) {
@@ -140,10 +165,6 @@ func (p *Pool) getSlow() (x interface{}) {
 		}
 		l.Unlock()
 	}
-
-	if x == nil && p.New != nil {
-		x = p.New()
-	}
 	return x
 }
 
diff --git a/src/sync/pool_test.go b/src/sync/pool_test.go
index fa1a27b..5a38cbf 100644
--- a/src/sync/pool_test.go
+++ b/src/sync/pool_test.go
@@ -127,7 +127,8 @@ func TestPoolStress(t *testing.T) {
 				p.Put(v)
 				v = p.Get()
 				if v != nil && v.(int) != 0 {
-					t.Fatalf("expect 0, got %v", v)
+					t.Errorf("expect 0, got %v", v)
+					break
 				}
 			}
 			done <- true
diff --git a/src/sync/runtime.go b/src/sync/runtime.go
index 96c56c8..4d22ce6 100644
--- a/src/sync/runtime.go
+++ b/src/sync/runtime.go
@@ -13,6 +13,9 @@ import "unsafe"
 // library and should not be used directly.
 func runtime_Semacquire(s *uint32)
 
+// SemacquireMutex is like Semacquire, but for profiling contended Mutexes.
+func runtime_SemacquireMutex(*uint32)
+
 // Semrelease atomically increments *s and notifies a waiting goroutine
 // if one is blocked in Semacquire.
 // It is intended as a simple wakeup primitive for use by the synchronization
diff --git a/src/sync/rwmutex.go b/src/sync/rwmutex.go
index 6734360..71064ee 100644
--- a/src/sync/rwmutex.go
+++ b/src/sync/rwmutex.go
@@ -61,7 +61,7 @@ func (rw *RWMutex) RUnlock() {
 	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
 		if r+1 == 0 || r+1 == -rwmutexMaxReaders {
 			race.Enable()
-			panic("sync: RUnlock of unlocked RWMutex")
+			throw("sync: RUnlock of unlocked RWMutex")
 		}
 		// A writer is pending.
 		if atomic.AddInt32(&rw.readerWait, -1) == 0 {
@@ -115,7 +115,7 @@ func (rw *RWMutex) Unlock() {
 	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
 	if r >= rwmutexMaxReaders {
 		race.Enable()
-		panic("sync: Unlock of unlocked RWMutex")
+		throw("sync: Unlock of unlocked RWMutex")
 	}
 	// Unblock blocked readers, if any.
 	for i := 0; i < int(r); i++ {
diff --git a/src/sync/rwmutex_test.go b/src/sync/rwmutex_test.go
index f625bc3..0436f97 100644
--- a/src/sync/rwmutex_test.go
+++ b/src/sync/rwmutex_test.go
@@ -155,48 +155,6 @@ func TestRLocker(t *testing.T) {
 	}
 }
 
-func TestUnlockPanic(t *testing.T) {
-	defer func() {
-		if recover() == nil {
-			t.Fatalf("unlock of unlocked RWMutex did not panic")
-		}
-	}()
-	var mu RWMutex
-	mu.Unlock()
-}
-
-func TestUnlockPanic2(t *testing.T) {
-	defer func() {
-		if recover() == nil {
-			t.Fatalf("unlock of unlocked RWMutex did not panic")
-		}
-	}()
-	var mu RWMutex
-	mu.RLock()
-	mu.Unlock()
-}
-
-func TestRUnlockPanic(t *testing.T) {
-	defer func() {
-		if recover() == nil {
-			t.Fatalf("read unlock of unlocked RWMutex did not panic")
-		}
-	}()
-	var mu RWMutex
-	mu.RUnlock()
-}
-
-func TestRUnlockPanic2(t *testing.T) {
-	defer func() {
-		if recover() == nil {
-			t.Fatalf("read unlock of unlocked RWMutex did not panic")
-		}
-	}()
-	var mu RWMutex
-	mu.Lock()
-	mu.RUnlock()
-}
-
 func BenchmarkRWMutexUncontended(b *testing.B) {
 	type PaddedRWMutex struct {
 		RWMutex
diff --git a/src/syscall/asm9_unix1_amd64.s b/src/syscall/asm9_unix1_amd64.s
new file mode 100644
index 0000000..29af78c
--- /dev/null
+++ b/src/syscall/asm9_unix1_amd64.s
@@ -0,0 +1,45 @@
+// +build netbsd openbsd
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+#include "funcdata.h"
+
+//
+// Syscall9 support for AMD64, NetBSD and OpenBSD
+//
+
+// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64);
+TEXT	·Syscall9(SB),NOSPLIT,$0-104
+	CALL	runtime·entersyscall(SB)
+	MOVQ	num+0(FP), AX	// syscall entry
+	MOVQ	a1+8(FP), DI
+	MOVQ	a2+16(FP), SI
+	MOVQ	a3+24(FP), DX
+	MOVQ	a4+32(FP), R10
+	MOVQ	a5+40(FP), R8
+	MOVQ	a6+48(FP), R9
+	MOVQ	a7+56(FP), R11
+	MOVQ	a8+64(FP), R12
+	MOVQ	a9+72(FP), R13
+	SUBQ    $32, SP
+	MOVQ	R11, 8(SP)	// arg 7
+	MOVQ	R12, 16(SP)	// arg 8
+	MOVQ	R13, 24(SP)	// arg 9
+	SYSCALL
+	JCC	ok9
+	ADDQ    $32, SP
+	MOVQ	$-1, 88(SP)	// r1
+	MOVQ	$0, 96(SP)	// r2
+	MOVQ	AX, 104(SP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok9:
+	ADDQ    $32, SP
+	MOVQ	AX, 88(SP)	// r1
+	MOVQ	DX, 96(SP)	// r2
+	MOVQ	$0, 104(SP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
diff --git a/src/syscall/asm9_unix2_amd64.s b/src/syscall/asm9_unix2_amd64.s
new file mode 100644
index 0000000..11a6c1f
--- /dev/null
+++ b/src/syscall/asm9_unix2_amd64.s
@@ -0,0 +1,46 @@
+// +build dragonfly freebsd
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+#include "funcdata.h"
+
+//
+// Syscall9 support for AMD64, DragonFly and FreeBSD
+//
+
+// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64);
+TEXT	·Syscall9(SB),NOSPLIT,$0-104
+	CALL	runtime·entersyscall(SB)
+	MOVQ	num+0(FP), AX	// syscall entry
+	MOVQ	a1+8(FP), DI
+	MOVQ	a2+16(FP), SI
+	MOVQ	a3+24(FP), DX
+	MOVQ	a4+32(FP), R10
+	MOVQ	a5+40(FP), R8
+	MOVQ	a6+48(FP), R9
+
+	// shift around the last three arguments so they're at the
+	// top of the stack when the syscall is called.
+	MOVQ	a7+56(FP), R11 // arg 7
+	MOVQ	R11, 8(SP)
+	MOVQ	a8+64(FP), R11 // arg 8
+	MOVQ	R11, 16(SP)
+	MOVQ	a9+72(FP), R11 // arg 9
+	MOVQ	R11, 24(SP)
+
+	SYSCALL
+	JCC	ok9
+	MOVQ	$-1, r1+80(FP)	// r1
+	MOVQ	$0, r2+88(FP)	// r2
+	MOVQ	AX, err+96(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok9:
+	MOVQ	AX, r1+80(FP)	// r1
+	MOVQ	DX, r2+88(FP)	// r2
+	MOVQ	$0, err+96(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
diff --git a/src/syscall/asm_darwin_arm.s b/src/syscall/asm_darwin_arm.s
index 1a2aad0..4eae005 100644
--- a/src/syscall/asm_darwin_arm.s
+++ b/src/syscall/asm_darwin_arm.s
@@ -11,124 +11,123 @@
 // func Syscall(syscall uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
 TEXT	·Syscall(SB),NOSPLIT,$0-28
 	BL		runtime·entersyscall(SB)
-	MOVW	syscall+4(SP), R12
-	MOVW	a1+8(SP), R0
-	MOVW	a2+12(SP), R1
-	MOVW	a3+16(SP), R2
+	MOVW	trap+0(FP), R12
+	MOVW	a1+4(FP), R0
+	MOVW	a2+8(FP), R1
+	MOVW	a3+12(FP), R2
 	SWI		$0x80
 	BCC		ok
 	MOVW	$-1, R1
-	MOVW	R1, r1+20(SP)	// r1
+	MOVW	R1, r1+16(FP)	// r1
 	MOVW	$0, R2
-	MOVW	R2, r2+24(SP)	// r2
-	MOVW	R0, errno+28(SP)	// errno
+	MOVW	R2, r2+20(FP)	// r2
+	MOVW	R0, err+24(FP)	// err
 	BL		runtime·exitsyscall(SB)
 	RET
 ok:
-	MOVW	R0, r1+20(SP) // r1
-	MOVW	R1, r2+24(SP)	// r2
+	MOVW	R0, r1+16(FP) // r1
+	MOVW	R1, r2+20(FP)	// r2
 	MOVW	$0, R0
-	MOVW	R0, errno+28(SP)	// errno
+	MOVW	R0, err+24(FP)	// err
 	BL		runtime·exitsyscall(SB)
 	RET
 
 // func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
 TEXT ·RawSyscall(SB),NOSPLIT,$0-28
-	MOVW	syscall+4(SP), R12	// syscall entry
-	MOVW	a1+8(SP), R0
-	MOVW	a2+12(SP), R1
-	MOVW	a3+16(SP), R2
+	MOVW	trap+0(FP), R12	// syscall entry
+	MOVW	a1+4(FP), R0
+	MOVW	a2+8(FP), R1
+	MOVW	a3+12(FP), R2
 	SWI		$0x80
 	BCC		ok1
 	MOVW	$-1, R1
-	MOVW	R1, r1+20(SP)	// r1
+	MOVW	R1, r1+16(FP)	// r1
 	MOVW	$0, R2
-	MOVW	R2, r2+24(SP)	// r2
-	MOVW	R0, errno+28(SP)	// errno
+	MOVW	R2, r2+20(FP)	// r2
+	MOVW	R0, err+24(FP)	// err
 	RET
 ok1:
-	MOVW	R0, r1+20(SP) // r1
-	MOVW	R1, r2+24(SP)	// r2
+	MOVW	R0, r1+16(FP) // r1
+	MOVW	R1, r2+20(FP)	// r2
 	MOVW	$0, R0
-	MOVW	R0, errno+28(SP)	// errno
+	MOVW	R0, err+24(FP)	// err
 	RET
 
 // func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
 TEXT	·Syscall6(SB),NOSPLIT,$0-40
 	BL		runtime·entersyscall(SB)
-	MOVW	syscall+4(SP), R12	// syscall entry
-	MOVW	a1+8(SP), R0
-	MOVW	a2+12(SP), R1
-	MOVW	a3+16(SP), R2
-	MOVW	a4+20(SP), R3
-	MOVW	a5+24(SP), R4
-	MOVW	a6+28(SP), R5
+	MOVW	trap+0(FP), R12	// syscall entry
+	MOVW	a1+4(FP), R0
+	MOVW	a2+8(FP), R1
+	MOVW	a3+12(FP), R2
+	MOVW	a4+16(FP), R3
+	MOVW	a5+20(FP), R4
+	MOVW	a6+24(FP), R5
 	SWI		$0x80
 	BCC		ok6
 	MOVW	$-1, R1
-	MOVW	R1, r1+32(SP)	// r1
+	MOVW	R1, r1+28(FP)	// r1
 	MOVW	$0, R2
-	MOVW	R2, r2+36(SP)	// r2
-	MOVW	R0, errno+40(SP)	// errno
+	MOVW	R2, r2+32(FP)	// r2
+	MOVW	R0, err+36(FP)	// err
 	BL		runtime·exitsyscall(SB)
 	RET
 ok6:
-	MOVW	R0, r1+32(SP) // r1
-	MOVW	R1, r2+36(SP)	// r2
+	MOVW	R0, r1+28(FP) // r1
+	MOVW	R1, r2+32(FP)	// r2
 	MOVW	$0, R0
-	MOVW	R0, errno+40(SP)	// errno
+	MOVW	R0, err+36(FP)	// err
 	BL		runtime·exitsyscall(SB)
 	RET
 
 // func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
 TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
-	MOVW	trap+4(SP), R12	// syscall entry
-	MOVW	a1+8(SP), R0
-	MOVW	a2+12(SP), R1
-	MOVW	a3+16(SP), R2
-	MOVW	a4+20(SP), R3
-	MOVW	a5+24(SP), R4
-	MOVW	a6+28(SP), R5
+	MOVW	trap+0(FP), R12	// syscall entry
+	MOVW	a1+4(FP), R0
+	MOVW	a2+8(FP), R1
+	MOVW	a3+12(FP), R2
+	MOVW	a4+16(FP), R3
+	MOVW	a5+20(FP), R4
+	MOVW	a6+24(FP), R5
 	SWI		$0x80
 	BCC		ok2
 	MOVW	$-1, R1
-	MOVW	R1, r1+32(SP)	// r1
+	MOVW	R1, r1+28(FP)	// r1
 	MOVW	$0, R2
-	MOVW	R2, r2+36(SP)	// r2
-	MOVW	R0, errno+40(SP)	// errno
+	MOVW	R2, r2+32(FP)	// r2
+	MOVW	R0, err+36(FP)	// err
 	RET
 ok2:
-	MOVW	R0, r1+32(SP) // r1
-	MOVW	R1, r2+36(SP)	// r2
+	MOVW	R0, r1+28(FP) // r1
+	MOVW	R1, r2+32(FP)	// r2
 	MOVW	$0, R0
-	MOVW	R0, errno+40(SP)	// errno
+	MOVW	R0, err+36(FP)	// err
 	RET
 
 // Actually Syscall7.
 TEXT	·Syscall9(SB),NOSPLIT,$0-52
 	BL runtime·entersyscall(SB)
-	MOVW	syscall+4(SP), R12	// syscall entry
-	MOVW	a1+8(SP), R0
-	MOVW	a2+12(SP), R1
-	MOVW	a3+16(SP), R2
-	MOVW	a4+20(SP), R3
-	MOVW	a5+24(SP), R4
-	MOVW	a6+28(SP), R5
-	MOVW	a7+32(SP), R6
+	MOVW	num+0(FP), R12	// syscall entry
+	MOVW	a1+4(FP), R0
+	MOVW	a2+8(FP), R1
+	MOVW	a3+12(FP), R2
+	MOVW	a4+16(FP), R3
+	MOVW	a5+20(FP), R4
+	MOVW	a6+24(FP), R5
+	MOVW	a7+28(FP), R6
 	SWI		$0x80
 	BCC		ok9
 	MOVW	$-1, R1
-	MOVW	R1, r1+44(SP)	// r1
+	MOVW	R1, r1+40(FP)	// r1
 	MOVW	$0, R2
-	MOVW	R2, r2+48(SP)	// r2
-	MOVW	R0, errno+52(SP)	// errno
+	MOVW	R2, r2+44(FP)	// r2
+	MOVW	R0, err+48(FP)	// err
 	BL		runtime·exitsyscall(SB)
 	RET
 ok9:
-	MOVW	R0, r1+44(SP) // r1
-	MOVW	R1, r2+48(SP)	// r2
+	MOVW	R0, r1+40(FP) // r1
+	MOVW	R1, r2+44(FP)	// r2
 	MOVW	$0, R0
-	MOVW	R0, errno+52(SP)	// errno
+	MOVW	R0, err+48(FP)	// err
 	BL	runtime·exitsyscall(SB)
 	RET
-
diff --git a/src/syscall/asm_darwin_arm64.s b/src/syscall/asm_darwin_arm64.s
index e18ff6a..95b6dc0 100644
--- a/src/syscall/asm_darwin_arm64.s
+++ b/src/syscall/asm_darwin_arm64.s
@@ -8,10 +8,10 @@
 // System call support for ARM64, Darwin
 //
 
-// func Syscall(syscall uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
+// func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
 TEXT	·Syscall(SB),NOSPLIT,$0-56
 	BL	runtime·entersyscall(SB)
-	MOVD	syscall+0(FP), R16
+	MOVD	trap+0(FP), R16
 	MOVD	a1+8(FP), R0
 	MOVD	a2+16(FP), R1
 	MOVD	a3+24(FP), R2
@@ -20,19 +20,19 @@ TEXT	·Syscall(SB),NOSPLIT,$0-56
 	MOVD	$-1, R1
 	MOVD	R1, r1+32(FP)	// r1
 	MOVD	ZR, r2+40(FP)	// r2
-	MOVD	R0, errno+48(FP)	// errno
+	MOVD	R0, err+48(FP)	// err
 	BL	runtime·exitsyscall(SB)
 	RET
 ok:
 	MOVD	R0, r1+32(FP) // r1
 	MOVD	R1, r2+40(FP)	// r2
-	MOVD	ZR, errno+48(FP)	// errno
+	MOVD	ZR, err+48(FP)	// err
 	BL	runtime·exitsyscall(SB)
 	RET
 
 // func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
 TEXT ·RawSyscall(SB),NOSPLIT,$0-56
-	MOVD	syscall+0(FP), R16	// syscall entry
+	MOVD	trap+0(FP), R16	// syscall entry
 	MOVD	a1+8(FP), R0
 	MOVD	a2+16(FP), R1
 	MOVD	a3+24(FP), R2
@@ -41,18 +41,18 @@ TEXT ·RawSyscall(SB),NOSPLIT,$0-56
 	MOVD	$-1, R1
 	MOVD	R1, r1+32(FP)	// r1
 	MOVD	ZR, r2+40(FP)	// r2
-	MOVD	R0, errno+48(FP)	// errno
+	MOVD	R0, err+48(FP)	// err
 	RET
 ok:
 	MOVD	R0, r1+32(FP) // r1
 	MOVD	R1, r2+40(FP)	// r2
-	MOVD	ZR, errno+48(FP)	// errno
+	MOVD	ZR, err+48(FP)	// err
 	RET
 
 // func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
 TEXT	·Syscall6(SB),NOSPLIT,$0-80
 	BL	runtime·entersyscall(SB)
-	MOVD	syscall+0(FP), R16	// syscall entry
+	MOVD	trap+0(FP), R16	// syscall entry
 	MOVD	a1+8(FP), R0
 	MOVD	a2+16(FP), R1
 	MOVD	a3+24(FP), R2
@@ -64,13 +64,13 @@ TEXT	·Syscall6(SB),NOSPLIT,$0-80
 	MOVD	$-1, R1
 	MOVD	R1, r1+56(FP)	// r1
 	MOVD	ZR, r2+64(FP)	// r2
-	MOVD	R0, errno+72(FP)	// errno
+	MOVD	R0, err+72(FP)	// err
 	BL	runtime·exitsyscall(SB)
 	RET
 ok:
 	MOVD	R0, r1+56(FP) // r1
 	MOVD	R1, r2+64(FP)	// r2
-	MOVD	ZR, errno+72(FP)	// errno
+	MOVD	ZR, err+72(FP)	// err
 	BL	runtime·exitsyscall(SB)
 	RET
 
@@ -88,19 +88,19 @@ TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
 	MOVD	$-1, R1
 	MOVD	R1, r1+56(FP)	// r1
 	MOVD	ZR, r2+64(FP)	// r2
-	MOVD	R0, errno+72(FP)	// errno
+	MOVD	R0, err+72(FP)	// err
 	RET
 ok:
 	MOVD	R0, r1+56(FP) // r1
 	MOVD	R1, r2+64(FP)	// r2
 	MOVD	ZR, R0
-	MOVD	R0, errno+72(FP)	// errno
+	MOVD	R0, err+72(FP)	// err
 	RET
 
 // Actually Syscall7
 TEXT	·Syscall9(SB),NOSPLIT,$0-104
 	BL	runtime·entersyscall(SB)
-	MOVD	syscall+0(FP), R16	// syscall entry
+	MOVD	num+0(FP), R16	// syscall entry
 	MOVD	a1+8(FP), R0
 	MOVD	a2+16(FP), R1
 	MOVD	a3+24(FP), R2
@@ -115,13 +115,13 @@ TEXT	·Syscall9(SB),NOSPLIT,$0-104
 	MOVD	$-1, R1
 	MOVD	R1, r1+80(FP)	// r1
 	MOVD	ZR, r2+88(FP)	// r2
-	MOVD	R0, errno+96(FP)	// errno
+	MOVD	R0, err+96(FP)	// err
 	BL	runtime·exitsyscall(SB)
 	RET
 ok:
 	MOVD	R0, r1+80(FP) // r1
 	MOVD	R1, r2+88(FP)	// r2
-	MOVD	ZR, errno+96(FP)	// errno
+	MOVD	ZR, err+96(FP)	// err
 	BL	runtime·exitsyscall(SB)
 	RET
 
diff --git a/src/syscall/asm_dragonfly_amd64.s b/src/syscall/asm_dragonfly_amd64.s
deleted file mode 100644
index 004d360..0000000
--- a/src/syscall/asm_dragonfly_amd64.s
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "textflag.h"
-#include "funcdata.h"
-
-//
-// System call support for AMD64, DragonFly
-//
-
-// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);
-// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64);
-// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64)
-// Trap # in AX, args in DI SI DX, return in AX DX
-
-TEXT	·Syscall(SB),NOSPLIT,$0-64
-	CALL	runtime·entersyscall(SB)
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	$0, R10
-	MOVQ	$0, R8
-	MOVQ	$0, R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok
-	MOVQ	$-1, 40(SP)	// r1
-	MOVQ	$0, 48(SP)	// r2
-	MOVQ	AX, 56(SP)  // errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok:
-	MOVQ	AX, 40(SP)	// r1
-	MOVQ	DX, 48(SP)	// r2
-	MOVQ	$0, 56(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall6(SB),NOSPLIT,$0-88
-	CALL	runtime·entersyscall(SB)
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP), R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok6
-	MOVQ	$-1, 64(SP)	// r1
-	MOVQ	$0, 72(SP)	// r2
-	MOVQ	AX, 80(SP)  // errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok6:
-	MOVQ	AX, 64(SP)	// r1
-	MOVQ	DX, 72(SP)	// r2
-	MOVQ	$0, 80(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall9(SB),NOSPLIT,$0-112
-	CALL	runtime·entersyscall(SB)
-	MOVQ	8(SP), AX
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP),	R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-
-	// shift around the last three arguments so they're at the
-	// top of the stack when the syscall is called.
-	MOVQ	64(SP), R11 // arg 7
-	MOVQ	R11, 8(SP)
-	MOVQ	72(SP), R11 // arg 8
-	MOVQ	R11, 16(SP)
-	MOVQ	80(SP), R11 // arg 9
-	MOVQ	R11, 24(SP)
-
-	SYSCALL
-	JCC	ok9
-	MOVQ	$-1, 88(SP)	// r1
-	MOVQ	$0, 96(SP)	// r2
-	MOVQ	AX, 104(SP)  // errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok9:
-	MOVQ	AX, 88(SP)	// r1
-	MOVQ	DX, 96(SP)	// r2
-	MOVQ	$0, 104(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-64
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	$0, R10
-	MOVQ	$0, R8
-	MOVQ	$0, R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok1
-	MOVQ	$-1, 40(SP)	// r1
-	MOVQ	$0, 48(SP)	// r2
-	MOVQ	AX, 56(SP)  // errno
-	RET
-ok1:
-	MOVQ	AX, 40(SP)	// r1
-	MOVQ	DX, 48(SP)	// r2
-	MOVQ	$0, 56(SP)	// errno
-	RET
-
-TEXT	·RawSyscall6(SB),NOSPLIT,$0-88
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP), R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok2
-	MOVQ	$-1, 64(SP)	// r1
-	MOVQ	$0, 72(SP)	// r2
-	MOVQ	AX, 80(SP)  // errno
-	RET
-ok2:
-	MOVQ	AX, 64(SP)	// r1
-	MOVQ	DX, 72(SP)	// r2
-	MOVQ	$0, 80(SP)	// errno
-	RET
diff --git a/src/syscall/asm_freebsd_386.s b/src/syscall/asm_freebsd_386.s
deleted file mode 100644
index 1400d5f..0000000
--- a/src/syscall/asm_freebsd_386.s
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
-// so that go vet can check that they are correct.
-
-#include "textflag.h"
-#include "funcdata.h"
-
-//
-// System call support for 386, FreeBSD
-//
-
-// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
-// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
-// Trap # in AX, args on stack above caller pc.
-
-TEXT	·Syscall(SB),NOSPLIT,$0-28
-	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok
-	MOVL	$-1, 20(SP)	// r1
-	MOVL	$-1, 24(SP)	// r2
-	MOVL	AX, 28(SP)		// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok:
-	MOVL	AX, 20(SP)	// r1
-	MOVL	DX, 24(SP)	// r2
-	MOVL	$0, 28(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall6(SB),NOSPLIT,$0-40
-	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok6
-	MOVL	$-1, 32(SP)	// r1
-	MOVL	$-1, 36(SP)	// r2
-	MOVL	AX, 40(SP)		// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok6:
-	MOVL	AX, 32(SP)	// r1
-	MOVL	DX, 36(SP)	// r2
-	MOVL	$0, 40(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall9(SB),NOSPLIT,$0-52
-	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok9
-	MOVL	$-1, 44(SP)	// r1
-	MOVL	$-1, 48(SP)	// r2
-	MOVL	AX, 52(SP)		// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok9:
-	MOVL	AX, 44(SP)	// r1
-	MOVL	DX, 48(SP)	// r2
-	MOVL	$0, 52(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok1
-	MOVL	$-1, 20(SP)	// r1
-	MOVL	$-1, 24(SP)	// r2
-	MOVL	AX, 28(SP)		// errno
-	RET
-ok1:
-	MOVL	AX, 20(SP)	// r1
-	MOVL	DX, 24(SP)	// r2
-	MOVL	$0, 28(SP)	// errno
-	RET
-
-TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok2
-	MOVL	$-1, 32(SP)	// r1
-	MOVL	$-1, 36(SP)	// r2
-	MOVL	AX, 40(SP)		// errno
-	RET
-ok2:
-	MOVL	AX, 32(SP)	// r1
-	MOVL	DX, 36(SP)	// r2
-	MOVL	$0, 40(SP)	// errno
-	RET
diff --git a/src/syscall/asm_freebsd_amd64.s b/src/syscall/asm_freebsd_amd64.s
deleted file mode 100644
index c6988c9..0000000
--- a/src/syscall/asm_freebsd_amd64.s
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
-// so that go vet can check that they are correct.
-
-#include "textflag.h"
-#include "funcdata.h"
-
-//
-// System call support for AMD64, FreeBSD
-//
-
-// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);
-// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64);
-// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64)
-// Trap # in AX, args in DI SI DX, return in AX DX
-
-TEXT	·Syscall(SB),NOSPLIT,$0-56
-	CALL	runtime·entersyscall(SB)
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	$0, R10
-	MOVQ	$0, R8
-	MOVQ	$0, R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok
-	MOVQ	$-1, 40(SP)	// r1
-	MOVQ	$0, 48(SP)	// r2
-	MOVQ	AX, 56(SP)  // errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok:
-	MOVQ	AX, 40(SP)	// r1
-	MOVQ	DX, 48(SP)	// r2
-	MOVQ	$0, 56(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall6(SB),NOSPLIT,$0-80
-	CALL	runtime·entersyscall(SB)
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP), R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok6
-	MOVQ	$-1, 64(SP)	// r1
-	MOVQ	$0, 72(SP)	// r2
-	MOVQ	AX, 80(SP)  // errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok6:
-	MOVQ	AX, 64(SP)	// r1
-	MOVQ	DX, 72(SP)	// r2
-	MOVQ	$0, 80(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall9(SB),NOSPLIT,$0-104
-	CALL	runtime·entersyscall(SB)
-	MOVQ	8(SP), AX
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP),	R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-
-	// shift around the last three arguments so they're at the
-	// top of the stack when the syscall is called.
-	MOVQ	64(SP), R11 // arg 7
-	MOVQ	R11, 8(SP)
-	MOVQ	72(SP), R11 // arg 8
-	MOVQ	R11, 16(SP)
-	MOVQ	80(SP), R11 // arg 9
-	MOVQ	R11, 24(SP)
-
-	SYSCALL
-	JCC	ok9
-	MOVQ	$-1, 88(SP)	// r1
-	MOVQ	$0, 96(SP)	// r2
-	MOVQ	AX, 104(SP)  // errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok9:
-	MOVQ	AX, 88(SP)	// r1
-	MOVQ	DX, 96(SP)	// r2
-	MOVQ	$0, 104(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-56
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	$0, R10
-	MOVQ	$0, R8
-	MOVQ	$0, R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok1
-	MOVQ	$-1, 40(SP)	// r1
-	MOVQ	$0, 48(SP)	// r2
-	MOVQ	AX, 56(SP)  // errno
-	RET
-ok1:
-	MOVQ	AX, 40(SP)	// r1
-	MOVQ	DX, 48(SP)	// r2
-	MOVQ	$0, 56(SP)	// errno
-	RET
-
-TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP), R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok2
-	MOVQ	$-1, 64(SP)	// r1
-	MOVQ	$0, 72(SP)	// r2
-	MOVQ	AX, 80(SP)  // errno
-	RET
-ok2:
-	MOVQ	AX, 64(SP)	// r1
-	MOVQ	DX, 72(SP)	// r2
-	MOVQ	$0, 80(SP)	// errno
-	RET
diff --git a/src/syscall/asm_linux_mipsx.s b/src/syscall/asm_linux_mipsx.s
new file mode 100644
index 0000000..957f2a8
--- /dev/null
+++ b/src/syscall/asm_linux_mipsx.s
@@ -0,0 +1,142 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+// +build mips mipsle
+
+#include "textflag.h"
+#include "funcdata.h"
+
+//
+// System calls for mips, Linux
+//
+
+// func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr);
+
+TEXT	·Syscall(SB),NOSPLIT,$0-28
+	JAL	runtime·entersyscall(SB)
+	MOVW	a1+4(FP), R4
+	MOVW	a2+8(FP), R5
+	MOVW	a3+12(FP), R6
+	MOVW	R0, R7
+	MOVW	trap+0(FP), R2	// syscall entry
+	SYSCALL
+	BEQ	R7, ok
+	MOVW	$-1, R1
+	MOVW	R1, r1+16(FP)	// r1
+	MOVW	R0, r2+20(FP)	// r2
+	MOVW	R2, err+24(FP)	// errno
+	JAL	runtime·exitsyscall(SB)
+	RET
+ok:
+	MOVW	R2, r1+16(FP)	// r1
+	MOVW	R3, r2+20(FP)	// r2
+	MOVW	R0, err+24(FP)	// errno
+	JAL	runtime·exitsyscall(SB)
+	RET
+
+
+// func Syscall6(trap trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
+// 5th and 6th arg go at sp+16, sp+20.
+// Note that frame size of 20 means that 24 bytes gets reserved on stack.
+TEXT ·Syscall6(SB),NOSPLIT,$20-40
+	NO_LOCAL_POINTERS
+	JAL	runtime·entersyscall(SB)
+	MOVW	a1+4(FP), R4
+	MOVW	a2+8(FP), R5
+	MOVW	a3+12(FP), R6
+	MOVW	a4+16(FP), R7
+	MOVW	a5+20(FP), R8
+	MOVW	a6+24(FP), R9
+	MOVW	R8, 16(R29)
+	MOVW	R9, 20(R29)
+	MOVW	trap+0(FP), R2	// syscall entry
+	SYSCALL
+	BEQ	R7, ok6
+	MOVW	$-1, R1
+	MOVW	R1, r1+28(FP)	// r1
+	MOVW	R0, r2+32(FP)	// r2
+	MOVW	R2, err+36(FP)	// errno
+	JAL	runtime·exitsyscall(SB)
+	RET
+ok6:
+	MOVW	R2, r1+28(FP)	// r1
+	MOVW	R3, r2+32(FP)	// r2
+	MOVW	R0, err+36(FP)	// errno
+	JAL	runtime·exitsyscall(SB)
+	RET
+
+// func Syscall9(trap trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr);
+// Actually Syscall8 but the rest of the code expects it to be named Syscall9.
+TEXT ·Syscall9(SB),NOSPLIT,$28-52
+	NO_LOCAL_POINTERS
+	JAL	runtime·entersyscall(SB)
+	MOVW	a1+4(FP), R4
+	MOVW	a2+8(FP), R5
+	MOVW	a3+12(FP), R6
+	MOVW	a4+16(FP), R7
+	MOVW	a5+20(FP), R8
+	MOVW	a6+24(FP), R9
+	MOVW	a7+28(FP), R10
+	MOVW	a8+32(FP), R11
+	MOVW	R8, 16(R29)
+	MOVW	R9, 20(R29)
+	MOVW	R10, 24(R29)
+	MOVW	R11, 28(R29)
+	MOVW	trap+0(FP), R2	// syscall entry
+	SYSCALL
+	BEQ	R7, ok9
+	MOVW	$-1, R1
+	MOVW	R1, r1+28(FP)	// r1
+	MOVW	R0, r2+32(FP)	// r2
+	MOVW	R2, err+36(FP)	// errno
+	JAL	runtime·exitsyscall(SB)
+	RET
+ok9:
+	MOVW	R2, r1+28(FP)	// r1
+	MOVW	R3, r2+32(FP)	// r2
+	MOVW	R0, err+36(FP)	// errno
+	JAL	runtime·exitsyscall(SB)
+	RET
+
+TEXT ·RawSyscall(SB),NOSPLIT,$24-28
+	MOVW	a1+4(FP), R4
+	MOVW	a2+8(FP), R5
+	MOVW	a3+12(FP), R6
+	MOVW	trap+0(FP), R2	// syscall entry
+	SYSCALL
+	BEQ	R7, ok1
+	MOVW	$-1, R1
+	MOVW	R1, r1+16(FP)	// r1
+	MOVW	R0, r2+20(FP)	// r2
+	MOVW	R2, err+24(FP)	// errno
+	RET
+ok1:
+	MOVW	R2, r1+16(FP)	// r1
+	MOVW	R3, r2+20(FP)	// r2
+	MOVW	R0, err+24(FP)	// errno
+	RET
+
+TEXT ·RawSyscall6(SB),NOSPLIT,$20-40
+	MOVW	a1+4(FP), R4
+	MOVW	a2+8(FP), R5
+	MOVW	a3+12(FP), R6
+	MOVW	a4+16(FP), R7
+	MOVW	a5+20(FP), R8
+	MOVW	a6+24(FP), R9
+	MOVW	R8, 16(R29)
+	MOVW	R9, 20(R29)
+	MOVW	trap+0(FP), R2	// syscall entry
+	SYSCALL
+	BEQ	R7, ok2
+	MOVW	$-1, R1
+	MOVW	R1, r1+28(FP)	// r1
+	MOVW	R0, r2+32(FP)	// r2
+	MOVW	R2, err+36(FP)	// errno
+	RET
+ok2:
+	MOVW	R2, r1+28(FP)	// r1
+	MOVW	R3, r2+32(FP)	// r2
+	MOVW	R0, err+36(FP)	// errno
+	RET
diff --git a/src/syscall/asm_netbsd_386.s b/src/syscall/asm_netbsd_386.s
deleted file mode 100644
index a8c4849..0000000
--- a/src/syscall/asm_netbsd_386.s
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
-// so that go vet can check that they are correct.
-
-#include "textflag.h"
-#include "funcdata.h"
-
-//
-// System call support for 386, NetBSD
-//
-
-// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
-// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
-// Trap # in AX, args on stack above caller pc.
-
-TEXT	·Syscall(SB),NOSPLIT,$0-28
-	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok
-	MOVL	$-1, 20(SP)	// r1
-	MOVL	$-1, 24(SP)	// r2
-	MOVL	AX, 28(SP)		// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok:
-	MOVL	AX, 20(SP)	// r1
-	MOVL	DX, 24(SP)	// r2
-	MOVL	$0, 28(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall6(SB),NOSPLIT,$0-40
-	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok6
-	MOVL	$-1, 32(SP)	// r1
-	MOVL	$-1, 36(SP)	// r2
-	MOVL	AX, 40(SP)		// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok6:
-	MOVL	AX, 32(SP)	// r1
-	MOVL	DX, 36(SP)	// r2
-	MOVL	$0, 40(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall9(SB),NOSPLIT,$0-52
-	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok9
-	MOVL	$-1, 44(SP)	// r1
-	MOVL	$-1, 48(SP)	// r2
-	MOVL	AX, 52(SP)		// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok9:
-	MOVL	AX, 44(SP)	// r1
-	MOVL	DX, 48(SP)	// r2
-	MOVL	$0, 52(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok1
-	MOVL	$-1, 20(SP)	// r1
-	MOVL	$-1, 24(SP)	// r2
-	MOVL	AX, 28(SP)		// errno
-	RET
-ok1:
-	MOVL	AX, 20(SP)	// r1
-	MOVL	DX, 24(SP)	// r2
-	MOVL	$0, 28(SP)	// errno
-	RET
-
-TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok2
-	MOVL	$-1, 32(SP)	// r1
-	MOVL	$-1, 36(SP)	// r2
-	MOVL	AX, 40(SP)		// errno
-	RET
-ok2:
-	MOVL	AX, 32(SP)	// r1
-	MOVL	DX, 36(SP)	// r2
-	MOVL	$0, 40(SP)	// errno
-	RET
diff --git a/src/syscall/asm_netbsd_amd64.s b/src/syscall/asm_netbsd_amd64.s
deleted file mode 100644
index b300148..0000000
--- a/src/syscall/asm_netbsd_amd64.s
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
-// so that go vet can check that they are correct.
-
-#include "textflag.h"
-#include "funcdata.h"
-
-//
-// System call support for AMD64, NetBSD
-//
-
-// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);
-// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64);
-// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64);
-// Trap # in AX, args in DI SI DX, return in AX DX
-
-TEXT	·Syscall(SB),NOSPLIT,$0-56
-	CALL	runtime·entersyscall(SB)
-	MOVQ	8(SP), AX	// syscall entry
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	$0, R10
-	MOVQ	$0, R8
-	MOVQ	$0, R9
-	SYSCALL
-	JCC	ok
-	MOVQ	$-1, 40(SP)	// r1
-	MOVQ	$0, 48(SP)	// r2
-	MOVQ	AX, 56(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok:
-	MOVQ	AX, 40(SP)	// r1
-	MOVQ	DX, 48(SP)	// r2
-	MOVQ	$0, 56(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall6(SB),NOSPLIT,$0-80
-	CALL	runtime·entersyscall(SB)
-	MOVQ	8(SP), AX	// syscall entry
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP), R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-	SYSCALL
-	JCC	ok6
-	MOVQ	$-1, 64(SP)	// r1
-	MOVQ	$0, 72(SP)	// r2
-	MOVQ	AX, 80(SP)  	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok6:
-	MOVQ	AX, 64(SP)	// r1
-	MOVQ	DX, 72(SP)	// r2
-	MOVQ	$0, 80(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall9(SB),NOSPLIT,$0-104
-	CALL	runtime·entersyscall(SB)
-	MOVQ	8(SP), AX	// syscall entry
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP), R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-	MOVQ	64(SP), R11
-	MOVQ	72(SP), R12
-	MOVQ	80(SP), R13
-	SUBQ    $32, SP
-	MOVQ	R11, 8(SP)	// arg 7
-	MOVQ	R12, 16(SP)	// arg 8
-	MOVQ	R13, 24(SP)	// arg 9
-	SYSCALL
-	JCC	ok9
-	ADDQ    $32, SP
-	MOVQ	$-1, 88(SP)	// r1
-	MOVQ	$0, 96(SP)	// r2
-	MOVQ	AX, 104(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok9:
-	ADDQ    $32, SP
-	MOVQ	AX, 88(SP)	// r1
-	MOVQ	DX, 96(SP)	// r2
-	MOVQ	$0, 104(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·RawSyscall(SB),NOSPLIT,$0-56
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	$0, R10
-	MOVQ	$0, R8
-	MOVQ	$0, R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok1
-	MOVQ	$-1, 40(SP)	// r1
-	MOVQ	$0, 48(SP)	// r2
-	MOVQ	AX, 56(SP)	// errno
-	RET
-ok1:
-	MOVQ	AX, 40(SP)	// r1
-	MOVQ	DX, 48(SP)	// r2
-	MOVQ	$0, 56(SP)	// errno
-	RET
-
-TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP), R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok2
-	MOVQ	$-1, 64(SP)	// r1
-	MOVQ	$0, 72(SP)	// r2
-	MOVQ	AX, 80(SP)	// errno
-	RET
-ok2:
-	MOVQ	AX, 64(SP)	// r1
-	MOVQ	DX, 72(SP)	// r2
-	MOVQ	$0, 80(SP)	// errno
-	RET
diff --git a/src/syscall/asm_openbsd_386.s b/src/syscall/asm_openbsd_386.s
deleted file mode 100644
index 6458bdf..0000000
--- a/src/syscall/asm_openbsd_386.s
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
-// so that go vet can check that they are correct.
-
-#include "textflag.h"
-#include "funcdata.h"
-
-//
-// System call support for 386, OpenBSD
-//
-
-// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
-// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
-// Trap # in AX, args on stack above caller pc.
-
-TEXT	·Syscall(SB),NOSPLIT,$0-28
-	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok
-	MOVL	$-1, 20(SP)	// r1
-	MOVL	$-1, 24(SP)	// r2
-	MOVL	AX, 28(SP)		// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok:
-	MOVL	AX, 20(SP)	// r1
-	MOVL	DX, 24(SP)	// r2
-	MOVL	$0, 28(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall6(SB),NOSPLIT,$0-40
-	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok6
-	MOVL	$-1, 32(SP)	// r1
-	MOVL	$-1, 36(SP)	// r2
-	MOVL	AX, 40(SP)		// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok6:
-	MOVL	AX, 32(SP)	// r1
-	MOVL	DX, 36(SP)	// r2
-	MOVL	$0, 40(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall9(SB),NOSPLIT,$0-52
-	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok9
-	MOVL	$-1, 44(SP)	// r1
-	MOVL	$-1, 48(SP)	// r2
-	MOVL	AX, 52(SP)		// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok9:
-	MOVL	AX, 44(SP)	// r1
-	MOVL	DX, 48(SP)	// r2
-	MOVL	$0, 52(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT ·RawSyscall(SB),NOSPLIT,$0-28
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok1
-	MOVL	$-1, 20(SP)	// r1
-	MOVL	$-1, 24(SP)	// r2
-	MOVL	AX, 28(SP)		// errno
-	RET
-ok1:
-	MOVL	AX, 20(SP)	// r1
-	MOVL	DX, 24(SP)	// r2
-	MOVL	$0, 28(SP)	// errno
-	RET
-
-TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
-	MOVL	4(SP), AX	// syscall entry
-	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
-	CLD
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	MOVSL
-	INT	$0x80
-	JAE	ok2
-	MOVL	$-1, 32(SP)	// r1
-	MOVL	$-1, 36(SP)	// r2
-	MOVL	AX, 40(SP)		// errno
-	RET
-ok2:
-	MOVL	AX, 32(SP)	// r1
-	MOVL	DX, 36(SP)	// r2
-	MOVL	$0, 40(SP)	// errno
-	RET
diff --git a/src/syscall/asm_openbsd_amd64.s b/src/syscall/asm_openbsd_amd64.s
deleted file mode 100644
index 1e981fc..0000000
--- a/src/syscall/asm_openbsd_amd64.s
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
-// so that go vet can check that they are correct.
-
-#include "textflag.h"
-#include "funcdata.h"
-
-//
-// System call support for AMD64, OpenBSD
-//
-
-// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);
-// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64);
-// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64);
-// Trap # in AX, args in DI SI DX, return in AX DX
-
-TEXT	·Syscall(SB),NOSPLIT,$0-56
-	CALL	runtime·entersyscall(SB)
-	MOVQ	8(SP), AX	// syscall entry
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	$0, R10
-	MOVQ	$0, R8
-	MOVQ	$0, R9
-	SYSCALL
-	JCC	ok
-	MOVQ	$-1, 40(SP)	// r1
-	MOVQ	$0, 48(SP)	// r2
-	MOVQ	AX, 56(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok:
-	MOVQ	AX, 40(SP)	// r1
-	MOVQ	DX, 48(SP)	// r2
-	MOVQ	$0, 56(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall6(SB),NOSPLIT,$0-80
-	CALL	runtime·entersyscall(SB)
-	MOVQ	8(SP), AX	// syscall entry
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP), R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-	SYSCALL
-	JCC	ok6
-	MOVQ	$-1, 64(SP)	// r1
-	MOVQ	$0, 72(SP)	// r2
-	MOVQ	AX, 80(SP)  	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok6:
-	MOVQ	AX, 64(SP)	// r1
-	MOVQ	DX, 72(SP)	// r2
-	MOVQ	$0, 80(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·Syscall9(SB),NOSPLIT,$0-104
-	CALL	runtime·entersyscall(SB)
-	MOVQ	8(SP), AX	// syscall entry
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP), R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-	MOVQ	64(SP), R11
-	MOVQ	72(SP), R12
-	MOVQ	80(SP), R13
-	SUBQ    $32, SP
-	MOVQ	R11, 8(SP)	// arg 7
-	MOVQ	R12, 16(SP)	// arg 8
-	MOVQ	R13, 24(SP)	// arg 9
-	SYSCALL
-	JCC	ok9
-	ADDQ    $32, SP
-	MOVQ	$-1, 88(SP)	// r1
-	MOVQ	$0, 96(SP)	// r2
-	MOVQ	AX, 104(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-ok9:
-	ADDQ    $32, SP
-	MOVQ	AX, 88(SP)	// r1
-	MOVQ	DX, 96(SP)	// r2
-	MOVQ	$0, 104(SP)	// errno
-	CALL	runtime·exitsyscall(SB)
-	RET
-
-TEXT	·RawSyscall(SB),NOSPLIT,$0-56
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	$0, R10
-	MOVQ	$0, R8
-	MOVQ	$0, R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok1
-	MOVQ	$-1, 40(SP)	// r1
-	MOVQ	$0, 48(SP)	// r2
-	MOVQ	AX, 56(SP)	// errno
-	RET
-ok1:
-	MOVQ	AX, 40(SP)	// r1
-	MOVQ	DX, 48(SP)	// r2
-	MOVQ	$0, 56(SP)	// errno
-	RET
-
-TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
-	MOVQ	16(SP), DI
-	MOVQ	24(SP), SI
-	MOVQ	32(SP), DX
-	MOVQ	40(SP), R10
-	MOVQ	48(SP), R8
-	MOVQ	56(SP), R9
-	MOVQ	8(SP), AX	// syscall entry
-	SYSCALL
-	JCC	ok2
-	MOVQ	$-1, 64(SP)	// r1
-	MOVQ	$0, 72(SP)	// r2
-	MOVQ	AX, 80(SP)	// errno
-	RET
-ok2:
-	MOVQ	AX, 64(SP)	// r1
-	MOVQ	DX, 72(SP)	// r2
-	MOVQ	$0, 80(SP)	// errno
-	RET
diff --git a/src/syscall/asm_openbsd_arm.s b/src/syscall/asm_openbsd_arm.s
index 4f034a0..9279ed9 100644
--- a/src/syscall/asm_openbsd_arm.s
+++ b/src/syscall/asm_openbsd_arm.s
@@ -17,7 +17,7 @@
 
 TEXT	·Syscall(SB),NOSPLIT,$0-28
 	BL runtime·entersyscall(SB)
-	MOVW syscall+0(FP), R12		// syscall number
+	MOVW trap+0(FP), R12		// syscall number
 	MOVW a1+4(FP), R0		// arg 1
 	MOVW a2+8(FP), R1		// arg 2
 	MOVW a3+12(FP), R2		// arg 3
@@ -39,7 +39,7 @@ error:
 
 TEXT	·Syscall6(SB),NOSPLIT,$0-40
 	BL runtime·entersyscall(SB)
-	MOVW syscall+0(FP), R12		// syscall number
+	MOVW trap+0(FP), R12		// syscall number
 	MOVW a1+4(FP), R0		// arg 1
 	MOVW a2+8(FP), R1		// arg 2
 	MOVW a3+12(FP), R2		// arg 3
@@ -65,7 +65,7 @@ error6:
 
 TEXT	·Syscall9(SB),NOSPLIT,$0-52
 	BL runtime·entersyscall(SB)
-	MOVW syscall+0(FP), R12		// syscall number
+	MOVW num+0(FP), R12		// syscall number
 	MOVW a1+4(FP), R0		// arg 1
 	MOVW a2+8(FP), R1		// arg 2
 	MOVW a3+12(FP), R2		// arg 3
@@ -90,7 +90,7 @@ error9:
 	RET
 
 TEXT	·RawSyscall(SB),NOSPLIT,$0-28
-	MOVW syscall+0(FP), R12		// syscall number
+	MOVW trap+0(FP), R12		// syscall number
 	MOVW a1+4(FP), R0		// arg 1
 	MOVW a2+8(FP), R1		// arg 2
 	MOVW a3+12(FP), R2		// arg 3
@@ -109,7 +109,7 @@ errorr:
 	RET
 
 TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
-	MOVW syscall+0(FP), R12		// syscall number
+	MOVW trap+0(FP), R12		// syscall number
 	MOVW a1+4(FP), R0		// arg 1
 	MOVW a2+8(FP), R1		// arg 2
 	MOVW a3+12(FP), R2		// arg 3
diff --git a/src/syscall/asm_plan9_386.s b/src/syscall/asm_plan9_386.s
index fc13640..047ae59 100644
--- a/src/syscall/asm_plan9_386.s
+++ b/src/syscall/asm_plan9_386.s
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
-// so that go vet can check that they are correct.
-
 #include "textflag.h"
 #include "funcdata.h"
 
@@ -20,17 +17,17 @@
 // Trap # in AX, args on stack above caller pc.
 TEXT	·Syscall(SB),NOSPLIT,$0-32
 	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
+	MOVL	trap+0(FP), AX	// syscall entry
 	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
+	LEAL	a1+4(FP), SI
+	LEAL	trap+0(FP), DI
 	CLD
 	MOVSL
 	MOVSL
 	MOVSL
 	INT	$64
-	MOVL	AX, r1+20(SP)
-	MOVL	$0, r2+24(SP)
+	MOVL	AX, r1+16(FP)
+	MOVL	$0, r2+20(FP)
 	CMPL	AX, $-1
 	JNE	ok3
 
@@ -44,7 +41,7 @@ ok3:
 	LEAL	runtime·emptystring(SB), SI	
 	
 copyresult3:
-	LEAL	err+28(SP), DI
+	LEAL	err+24(FP), DI
 
 	CLD
 	MOVSL
@@ -55,10 +52,10 @@ copyresult3:
 
 TEXT	·Syscall6(SB),NOSPLIT,$0-44
 	CALL	runtime·entersyscall(SB)
-	MOVL	4(SP), AX	// syscall entry
+	MOVL	trap+0(FP), AX	// syscall entry
 	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
+	LEAL	a1+4(FP), SI
+	LEAL	trap+0(FP), DI
 	CLD
 	MOVSL
 	MOVSL
@@ -67,8 +64,8 @@ TEXT	·Syscall6(SB),NOSPLIT,$0-44
 	MOVSL
 	MOVSL
 	INT	$64
-	MOVL	AX, r1+32(SP)
-	MOVL	$0, r2+36(SP)
+	MOVL	AX, r1+28(FP)
+	MOVL	$0, r2+32(FP)
 	CMPL	AX, $-1
 	JNE	ok4
 	
@@ -82,7 +79,7 @@ ok4:
 	LEAL	runtime·emptystring(SB), SI
 	
 copyresult4:
-	LEAL	err+40(SP), DI
+	LEAL	err+36(FP), DI
 
 	CLD
 	MOVSL
@@ -92,25 +89,25 @@ copyresult4:
 	RET
 
 TEXT ·RawSyscall(SB),NOSPLIT,$0-28
-	MOVL	4(SP), AX	// syscall entry
+	MOVL	trap+0(FP), AX	// syscall entry
 	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
+	LEAL	a1+4(FP), SI
+	LEAL	trap+0(FP), DI
 	CLD
 	MOVSL
 	MOVSL
 	MOVSL
 	INT	$64
-	MOVL	AX, r1+20(SP)
-	MOVL	AX, r2+24(SP)
-	MOVL	AX, err+28(SP)
+	MOVL	AX, r1+16(FP)
+	MOVL	AX, r2+20(FP)
+	MOVL	AX, err+24(FP)
 	RET
 
 TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
-	MOVL	4(SP), AX	// syscall entry
+	MOVL	trap+0(FP), AX	// syscall entry
 	// slide args down on top of system call number
-	LEAL		8(SP), SI
-	LEAL		4(SP), DI
+	LEAL	a1+4(FP), SI
+	LEAL	trap+0(FP), DI
 	CLD
 	MOVSL
 	MOVSL
@@ -119,25 +116,25 @@ TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
 	MOVSL
 	MOVSL
 	INT	$64
-	MOVL	AX, r1+32(SP)
-	MOVL	AX, r2+36(SP)
-	MOVL	AX, err+40(SP)		
+	MOVL	AX, r1+28(FP)
+	MOVL	AX, r2+32(FP)
+	MOVL	AX, err+36(FP)
 	RET
 
 #define SYS_SEEK 39	/* from zsysnum_plan9_386.go */
 
 //func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
 TEXT ·seek(SB),NOSPLIT,$0-36
-	LEAL	newoffset+24(SP), AX
-	MOVL	AX, placeholder+4(SP)
+	LEAL	newoffset+20(FP), AX
+	MOVL	AX, placeholder+0(FP)
 	
 	MOVL	$SYS_SEEK, AX	// syscall entry
 	INT	$64
 	
 	CMPL	AX, $-1
 	JNE	ok6
-	MOVL	AX, 24(SP)	// newoffset low
-	MOVL	AX, 28(SP)	// newoffset high
+	MOVL	AX, newoffset_lo+20(FP)
+	MOVL	AX, newoffset_hi+24(FP)
 	
 	SUBL	$8, SP
 	CALL	syscall·errstr(SB)
@@ -149,7 +146,7 @@ ok6:
 	LEAL	runtime·emptystring(SB), SI
 	
 copyresult6:
-	LEAL	err+32(SP), DI
+	LEAL	err+28(FP), DI
 
 	CLD
 	MOVSL
diff --git a/src/syscall/asm_plan9_amd64.s b/src/syscall/asm_plan9_amd64.s
index 92419b7..8405023 100644
--- a/src/syscall/asm_plan9_amd64.s
+++ b/src/syscall/asm_plan9_amd64.s
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
-// so that go vet can check that they are correct.
-
 #include "textflag.h"
 #include "funcdata.h"
 
@@ -19,17 +16,17 @@
 
 TEXT	·Syscall(SB),NOSPLIT,$0-64
 	CALL	runtime·entersyscall(SB)
-	MOVQ	8(SP), BP	// syscall entry
+	MOVQ	trap+0(FP), BP	// syscall entry
 	// slide args down on top of system call number
-	LEAQ	16(SP), SI
-	LEAQ	8(SP), DI
+	LEAQ	a1+8(FP), SI
+	LEAQ	trap+0(FP), DI
 	CLD
 	MOVSQ
 	MOVSQ
 	MOVSQ
 	SYSCALL
-	MOVQ	AX, r1+40(SP)
-	MOVQ	$0, r2+48(SP)
+	MOVQ	AX, r1+32(FP)
+	MOVQ	$0, r2+40(FP)
 	CMPL	AX, $-1
 	JNE	ok3
 
@@ -43,7 +40,7 @@ ok3:
 	LEAQ	runtime·emptystring(SB), SI	
 	
 copyresult3:
-	LEAQ	err+56(SP), DI
+	LEAQ	err+48(FP), DI
 
 	CLD
 	MOVSQ
@@ -54,10 +51,10 @@ copyresult3:
 
 TEXT	·Syscall6(SB),NOSPLIT,$0-88
 	CALL	runtime·entersyscall(SB)
-	MOVQ	8(SP), BP	// syscall entry
+	MOVQ	trap+0(FP), BP	// syscall entry
 	// slide args down on top of system call number
-	LEAQ		16(SP), SI
-	LEAQ		8(SP), DI
+	LEAQ	a1+8(FP), SI
+	LEAQ	trap+0(FP), DI
 	CLD
 	MOVSQ
 	MOVSQ
@@ -66,8 +63,8 @@ TEXT	·Syscall6(SB),NOSPLIT,$0-88
 	MOVSQ
 	MOVSQ
 	SYSCALL
-	MOVQ	AX, r1+64(SP)
-	MOVQ	$0, r2+72(SP)
+	MOVQ	AX, r1+56(FP)
+	MOVQ	$0, r2+64(FP)
 	CMPL	AX, $-1
 	JNE	ok4
 	
@@ -81,7 +78,7 @@ ok4:
 	LEAQ	runtime·emptystring(SB), SI
 	
 copyresult4:
-	LEAQ	err+80(SP), DI
+	LEAQ	err+72(FP), DI
 
 	CLD
 	MOVSQ
@@ -91,25 +88,25 @@ copyresult4:
 	RET
 
 TEXT ·RawSyscall(SB),NOSPLIT,$0-56
-	MOVQ	8(SP), BP	// syscall entry
+	MOVQ	trap+0(FP), BP	// syscall entry
 	// slide args down on top of system call number
-	LEAQ		16(SP), SI
-	LEAQ		8(SP), DI
+	LEAQ	a1+8(FP), SI
+	LEAQ	trap+0(FP), DI
 	CLD
 	MOVSQ
 	MOVSQ
 	MOVSQ
 	SYSCALL
-	MOVQ	AX, r1+40(SP)
-	MOVQ	AX, r2+48(SP)
-	MOVQ	AX, err+56(SP)
+	MOVQ	AX, r1+32(FP)
+	MOVQ	AX, r2+40(FP)
+	MOVQ	AX, err+48(FP)
 	RET
 
 TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
-	MOVQ	8(SP), BP	// syscall entry
+	MOVQ	trap+0(FP), BP	// syscall entry
 	// slide args down on top of system call number
-	LEAQ		16(SP), SI
-	LEAQ		8(SP), DI
+	LEAQ	a1+8(FP), SI
+	LEAQ	trap+0(FP), DI
 	CLD
 	MOVSQ
 	MOVSQ
@@ -118,24 +115,24 @@ TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
 	MOVSQ
 	MOVSQ
 	SYSCALL
-	MOVQ	AX, r1+64(SP)
-	MOVQ	AX, r2+72(SP)
-	MOVQ	AX, err+80(SP)		
+	MOVQ	AX, r1+56(FP)
+	MOVQ	AX, r2+64(FP)
+	MOVQ	AX, err+72(FP)
 	RET
 
 #define SYS_SEEK 39	/* from zsysnum_plan9_amd64.go */
 
 //func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
 TEXT ·seek(SB),NOSPLIT,$0-56
-	LEAQ	newoffset+40(SP), AX
-	MOVQ	AX, placeholder+8(SP)
+	LEAQ	newoffset+32(FP), AX
+	MOVQ	AX, placeholder+0(FP)
 	
 	MOVQ	$SYS_SEEK, BP	// syscall entry
 	SYSCALL
 	
 	CMPL	AX, $-1
 	JNE	ok6
-	MOVQ	$-1, newoffset+40(SP)
+	MOVQ	$-1, newoffset+32(FP)
 	
 	SUBQ	$16, SP
 	CALL	syscall·errstr(SB)
@@ -147,7 +144,7 @@ ok6:
 	LEAQ	runtime·emptystring(SB), SI
 	
 copyresult6:
-	LEAQ	err+48(SP), DI
+	LEAQ	err+40(FP), DI
 
 	CLD
 	MOVSQ
diff --git a/src/syscall/asm_unix_386.s b/src/syscall/asm_unix_386.s
new file mode 100644
index 0000000..263355c
--- /dev/null
+++ b/src/syscall/asm_unix_386.s
@@ -0,0 +1,142 @@
+// +build netbsd freebsd openbsd
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+#include "funcdata.h"
+
+//
+// System call support for some 386 unixes
+//
+
+// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
+// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
+// Trap # in AX, args on stack above caller pc.
+
+TEXT	·Syscall(SB),NOSPLIT,$0-28
+	CALL	runtime·entersyscall(SB)
+	MOVL	trap+0(FP), AX	// syscall entry
+	// slide args down on top of system call number
+	LEAL		8(SP), SI
+	LEAL		4(SP), DI
+	CLD
+	MOVSL
+	MOVSL
+	MOVSL
+	INT	$0x80
+	JAE	ok
+	MOVL	$-1, r1+16(FP)	// r1
+	MOVL	$-1, r2+20(FP)	// r2
+	MOVL	AX, err+24(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok:
+	MOVL	AX, r1+16(FP)	// r1
+	MOVL	DX, r2+20(FP)	// r2
+	MOVL	$0, err+24(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT	·Syscall6(SB),NOSPLIT,$0-40
+	CALL	runtime·entersyscall(SB)
+	MOVL	trap+0(FP), AX	// syscall entry
+	// slide args down on top of system call number
+	LEAL		8(SP), SI
+	LEAL		4(SP), DI
+	CLD
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	INT	$0x80
+	JAE	ok6
+	MOVL	$-1, r1+28(FP)	// r1
+	MOVL	$-1, r2+32(FP)	// r2
+	MOVL	AX, err+36(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok6:
+	MOVL	AX, r1+28(FP)	// r1
+	MOVL	DX, r2+32(FP)	// r2
+	MOVL	$0, err+36(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT	·Syscall9(SB),NOSPLIT,$0-52
+	CALL	runtime·entersyscall(SB)
+	MOVL	num+0(FP), AX	// syscall entry
+	// slide args down on top of system call number
+	LEAL		8(SP), SI
+	LEAL		4(SP), DI
+	CLD
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	INT	$0x80
+	JAE	ok9
+	MOVL	$-1, r1+40(FP)	// r1
+	MOVL	$-1, r2+44(FP)	// r2
+	MOVL	AX, err+48(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok9:
+	MOVL	AX, r1+40(FP)	// r1
+	MOVL	DX, r2+44(FP)	// r2
+	MOVL	$0, err+48(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT ·RawSyscall(SB),NOSPLIT,$0-28
+	MOVL	trap+0(FP), AX	// syscall entry
+	// slide args down on top of system call number
+	LEAL		8(SP), SI
+	LEAL		4(SP), DI
+	CLD
+	MOVSL
+	MOVSL
+	MOVSL
+	INT	$0x80
+	JAE	ok1
+	MOVL	$-1, r1+16(FP)	// r1
+	MOVL	$-1, r2+20(FP)	// r2
+	MOVL	AX, err+24(FP)	// errno
+	RET
+ok1:
+	MOVL	AX, r1+16(FP)	// r1
+	MOVL	DX, r2+20(FP)	// r2
+	MOVL	$0, err+24(FP)	// errno
+	RET
+
+TEXT	·RawSyscall6(SB),NOSPLIT,$0-40
+	MOVL	trap+0(FP), AX	// syscall entry
+	// slide args down on top of system call number
+	LEAL		8(SP), SI
+	LEAL		4(SP), DI
+	CLD
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	MOVSL
+	INT	$0x80
+	JAE	ok2
+	MOVL	$-1, r1+28(FP)	// r1
+	MOVL	$-1, r2+32(FP)	// r2
+	MOVL	AX, err+36(FP)	// errno
+	RET
+ok2:
+	MOVL	AX, r1+28(FP)	// r1
+	MOVL	DX, r2+32(FP)	// r2
+	MOVL	$0, err+36(FP)	// errno
+	RET
diff --git a/src/syscall/asm_unix_amd64.s b/src/syscall/asm_unix_amd64.s
new file mode 100644
index 0000000..025408f
--- /dev/null
+++ b/src/syscall/asm_unix_amd64.s
@@ -0,0 +1,102 @@
+// +build netbsd freebsd openbsd dragonfly
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+#include "funcdata.h"
+
+//
+// System call support for AMD64 unixes
+//
+
+// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64)
+// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64)
+// Trap # in AX, args in DI SI DX, return in AX DX
+
+TEXT	·Syscall(SB),NOSPLIT,$0-56
+	CALL	runtime·entersyscall(SB)
+	MOVQ	trap+0(FP), AX	// syscall entry
+	MOVQ	a1+8(FP), DI
+	MOVQ	a2+16(FP), SI
+	MOVQ	a3+24(FP), DX
+	MOVQ	$0, R10
+	MOVQ	$0, R8
+	MOVQ	$0, R9
+	SYSCALL
+	JCC	ok
+	MOVQ	$-1, r1+32(FP)	// r1
+	MOVQ	$0, r2+40(FP)	// r2
+	MOVQ	AX, err+48(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok:
+	MOVQ	AX, r1+32(FP)	// r1
+	MOVQ	DX, r2+40(FP)	// r2
+	MOVQ	$0, err+48(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT	·Syscall6(SB),NOSPLIT,$0-80
+	CALL	runtime·entersyscall(SB)
+	MOVQ	trap+0(FP), AX	// syscall entry
+	MOVQ	a1+8(FP), DI
+	MOVQ	a2+16(FP), SI
+	MOVQ	a3+24(FP), DX
+	MOVQ	a4+32(FP), R10
+	MOVQ	a5+40(FP), R8
+	MOVQ	a6+48(FP), R9
+	SYSCALL
+	JCC	ok6
+	MOVQ	$-1, r1+56(FP)	// r1
+	MOVQ	$0, r2+64(FP)	// r2
+	MOVQ	AX, err+72(FP)  // errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+ok6:
+	MOVQ	AX, r1+56(FP)	// r1
+	MOVQ	DX, r2+64(FP)	// r2
+	MOVQ	$0, err+72(FP)	// errno
+	CALL	runtime·exitsyscall(SB)
+	RET
+
+TEXT	·RawSyscall(SB),NOSPLIT,$0-56
+	MOVQ	a1+8(FP), DI
+	MOVQ	a2+16(FP), SI
+	MOVQ	a3+24(FP), DX
+	MOVQ	$0, R10
+	MOVQ	$0, R8
+	MOVQ	$0, R9
+	MOVQ	trap+0(FP), AX	// syscall entry
+	SYSCALL
+	JCC	ok1
+	MOVQ	$-1, r1+32(FP)	// r1
+	MOVQ	$0, r2+40(FP)	// r2
+	MOVQ	AX, err+48(FP)	// errno
+	RET
+ok1:
+	MOVQ	AX, r1+32(FP)	// r1
+	MOVQ	DX, r2+40(FP)	// r2
+	MOVQ	$0, err+48(FP)	// errno
+	RET
+
+TEXT	·RawSyscall6(SB),NOSPLIT,$0-80
+	MOVQ	a1+8(FP), DI
+	MOVQ	a2+16(FP), SI
+	MOVQ	a3+24(FP), DX
+	MOVQ	a4+32(FP), R10
+	MOVQ	a5+40(FP), R8
+	MOVQ	a6+48(FP), R9
+	MOVQ	trap+0(FP), AX	// syscall entry
+	SYSCALL
+	JCC	ok2
+	MOVQ	$-1, r1+56(FP)	// r1
+	MOVQ	$0, r2+64(FP)	// r2
+	MOVQ	AX, err+72(FP)	// errno
+	RET
+ok2:
+	MOVQ	AX, r1+56(FP)	// r1
+	MOVQ	DX, r2+64(FP)	// r2
+	MOVQ	$0, err+72(FP)	// errno
+	RET
diff --git a/src/syscall/const_plan9.go b/src/syscall/const_plan9.go
index ba26f12..063d5df 100644
--- a/src/syscall/const_plan9.go
+++ b/src/syscall/const_plan9.go
@@ -12,6 +12,17 @@ const (
 	O_EXCL    = 0x1000
 )
 
+// Bind flags
+const (
+	MORDER  = 0x0003 // mask for bits defining order of mounting
+	MREPL   = 0x0000 // mount replaces object
+	MBEFORE = 0x0001 // mount goes before others in union directory
+	MAFTER  = 0x0002 // mount goes after others in union directory
+	MCREATE = 0x0004 // permit creation in mounted directory
+	MCACHE  = 0x0010 // cache some data
+	MMASK   = 0x0017 // all bits on
+)
+
 // Rfork flags
 const (
 	RFNAMEG  = 1 << 0
diff --git a/src/syscall/dir_plan9.go b/src/syscall/dir_plan9.go
index 15b2674..4ed052d 100644
--- a/src/syscall/dir_plan9.go
+++ b/src/syscall/dir_plan9.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Plan 9 directory marshalling. See intro(5).
+// Plan 9 directory marshaling. See intro(5).
 
 package syscall
 
diff --git a/src/syscall/dirent.go b/src/syscall/dirent.go
new file mode 100644
index 0000000..4db2d43
--- /dev/null
+++ b/src/syscall/dirent.go
@@ -0,0 +1,102 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+
+package syscall
+
+import "unsafe"
+
+// readInt returns the size-bytes unsigned integer in native byte order at offset off.
+func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
+	if len(b) < int(off+size) {
+		return 0, false
+	}
+	if isBigEndian {
+		return readIntBE(b[off:], size), true
+	}
+	return readIntLE(b[off:], size), true
+}
+
+func readIntBE(b []byte, size uintptr) uint64 {
+	switch size {
+	case 1:
+		return uint64(b[0])
+	case 2:
+		_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
+		return uint64(b[1]) | uint64(b[0])<<8
+	case 4:
+		_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
+		return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24
+	case 8:
+		_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
+		return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
+			uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
+	default:
+		panic("syscall: readInt with unsupported size")
+	}
+}
+
+func readIntLE(b []byte, size uintptr) uint64 {
+	switch size {
+	case 1:
+		return uint64(b[0])
+	case 2:
+		_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
+		return uint64(b[0]) | uint64(b[1])<<8
+	case 4:
+		_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
+		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
+	case 8:
+		_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
+		return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
+			uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
+	default:
+		panic("syscall: readInt with unsupported size")
+	}
+}
+
+// ParseDirent parses up to max directory entries in buf,
+// appending the names to names. It returns the number of
+// bytes consumed from buf, the number of entries added
+// to names, and the new names slice.
+func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
+	origlen := len(buf)
+	count = 0
+	for max != 0 && len(buf) > 0 {
+		reclen, ok := direntReclen(buf)
+		if !ok || reclen > uint64(len(buf)) {
+			return origlen, count, names
+		}
+		rec := buf[:reclen]
+		buf = buf[reclen:]
+		ino, ok := direntIno(rec)
+		if !ok {
+			break
+		}
+		if ino == 0 { // File absent in directory.
+			continue
+		}
+		const namoff = uint64(unsafe.Offsetof(Dirent{}.Name))
+		namlen, ok := direntNamlen(rec)
+		if !ok || namoff+namlen > uint64(len(rec)) {
+			break
+		}
+		name := rec[namoff : namoff+namlen]
+		for i, c := range name {
+			if c == 0 {
+				name = name[:i]
+				break
+			}
+		}
+		// Check for useless names before allocating a string.
+		if string(name) == "." || string(name) == ".." {
+			continue
+		}
+		max--
+		count++
+		names = append(names, string(name))
+	}
+	return origlen - len(buf), count, names
+}
diff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go
index e563848..864473b 100644
--- a/src/syscall/dll_windows.go
+++ b/src/syscall/dll_windows.go
@@ -176,7 +176,6 @@ func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
 	default:
 		panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
 	}
-	return
 }
 
 // A LazyDLL implements access to a single DLL.
diff --git a/src/syscall/endian_big.go b/src/syscall/endian_big.go
new file mode 100644
index 0000000..3c26005
--- /dev/null
+++ b/src/syscall/endian_big.go
@@ -0,0 +1,9 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// +build ppc64 s390x mips mips64
+
+package syscall
+
+const isBigEndian = true
diff --git a/src/syscall/endian_little.go b/src/syscall/endian_little.go
new file mode 100644
index 0000000..bd6f06e
--- /dev/null
+++ b/src/syscall/endian_little.go
@@ -0,0 +1,9 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// +build 386 amd64 amd64p32 arm arm64 ppc64le mips64le mipsle
+
+package syscall
+
+const isBigEndian = false
diff --git a/src/syscall/env_windows.go b/src/syscall/env_windows.go
index 3f75167..1606b42 100644
--- a/src/syscall/env_windows.go
+++ b/src/syscall/env_windows.go
@@ -60,7 +60,7 @@ func Clearenv() {
 		// http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
 		for j := 1; j < len(s); j++ {
 			if s[j] == '=' {
-				Setenv(s[0:j], "")
+				Unsetenv(s[0:j])
 				break
 			}
 		}
diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go
index 39764f7..979b6a2 100644
--- a/src/syscall/exec_linux.go
+++ b/src/syscall/exec_linux.go
@@ -214,16 +214,16 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
 		// and disabled setgroups, because otherwise unprivileged user namespace
 		// will fail with any non-empty SysProcAttr.Credential.
 		if !(sys.GidMappings != nil && !sys.GidMappingsEnableSetgroups && ngroups == 0) {
-			_, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0)
+			_, _, err1 = RawSyscall(_SYS_setgroups, ngroups, groups, 0)
 			if err1 != 0 {
 				goto childerror
 			}
 		}
-		_, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0)
+		_, _, err1 = RawSyscall(sys_SETGID, uintptr(cred.Gid), 0, 0)
 		if err1 != 0 {
 			goto childerror
 		}
-		_, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0)
+		_, _, err1 = RawSyscall(sys_SETUID, uintptr(cred.Uid), 0, 0)
 		if err1 != 0 {
 			goto childerror
 		}
diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go
index aaffa06..7a4b571 100644
--- a/src/syscall/exec_linux_test.go
+++ b/src/syscall/exec_linux_test.go
@@ -162,6 +162,12 @@ func TestUnshare(t *testing.T) {
 		t.Fatal(err)
 	}
 
+	orig, err := ioutil.ReadFile(path)
+	if err != nil {
+		t.Fatal(err)
+	}
+	origLines := strings.Split(strings.TrimSpace(string(orig)), "\n")
+
 	cmd := exec.Command("cat", path)
 	cmd.SysProcAttr = &syscall.SysProcAttr{
 		Unshareflags: syscall.CLONE_NEWNET,
@@ -178,8 +184,8 @@ func TestUnshare(t *testing.T) {
 	}
 
 	lines := strings.Split(sout, "\n")
-	if len(lines) != 3 {
-		t.Fatalf("Expected 3 lines of output, got %d", len(lines))
+	if len(lines) >= len(origLines) {
+		t.Fatalf("Got %d lines of output, want <%d", len(lines), len(origLines))
 	}
 }
 
diff --git a/src/syscall/exec_plan9.go b/src/syscall/exec_plan9.go
index 6551bcb..47ccbdc 100644
--- a/src/syscall/exec_plan9.go
+++ b/src/syscall/exec_plan9.go
@@ -298,11 +298,6 @@ childerror1:
 	for {
 		RawSyscall(SYS_EXITS, 0, 0, 0)
 	}
-
-	// Calling panic is not actually safe,
-	// but the for loop above won't break
-	// and this shuts up the compiler.
-	panic("unreached")
 }
 
 // close the numbered file descriptor, unless it is fd1, fd2, or a member of fds.
diff --git a/src/syscall/exec_unix.go b/src/syscall/exec_unix.go
index 9fd8cf4..af59c5d 100644
--- a/src/syscall/exec_unix.go
+++ b/src/syscall/exec_unix.go
@@ -241,7 +241,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
 	return pid, 0, err
 }
 
-// Ordinary exec.
+// Exec invokes the execve(2) system call.
 func Exec(argv0 string, argv []string, envv []string) (err error) {
 	argv0p, err := BytePtrFromString(argv0)
 	if err != nil {
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
index 5a01843..cafce1e 100644
--- a/src/syscall/exec_windows.go
+++ b/src/syscall/exec_windows.go
@@ -209,8 +209,6 @@ func joinExeDirAndFName(dir, p string) (name string, err error) {
 			return FullPath(d + "\\" + p)
 		}
 	}
-	// we shouldn't be here
-	return "", EINVAL
 }
 
 type ProcAttr struct {
diff --git a/src/syscall/flock_linux_32bit.go b/src/syscall/flock_linux_32bit.go
index 500a973..e154899 100644
--- a/src/syscall/flock_linux_32bit.go
+++ b/src/syscall/flock_linux_32bit.go
@@ -1,4 +1,4 @@
-// +build linux,386 linux,arm
+// +build linux,386 linux,arm linux,mips linux,mipsle
 
 // Copyright 2014 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
diff --git a/src/syscall/mkall.sh b/src/syscall/mkall.sh
index 6a9aacb..987ac23 100755
--- a/src/syscall/mkall.sh
+++ b/src/syscall/mkall.sh
@@ -275,7 +275,7 @@ solaris_amd64)
 	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
 	;;
 windows_*)
-	echo 'run "go generate syscall_windows.go" instead' 1>&2
+	echo 'run "go generate" instead' 1>&2
 	exit 1
 	;;
 *)
diff --git a/src/syscall/mksyscall_windows.go b/src/syscall/mksyscall_windows.go
index 1e0d940..37e4a07 100644
--- a/src/syscall/mksyscall_windows.go
+++ b/src/syscall/mksyscall_windows.go
@@ -281,7 +281,7 @@ func (r *Rets) SetReturnValuesCode() string {
 func (r *Rets) useLongHandleErrorCode(retvar string) string {
 	const code = `if %s {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = %sEINVAL
 		}
@@ -708,6 +708,10 @@ func (src *Source) IsStdRepo() (bool, error) {
 		abspath = strings.ToLower(abspath)
 		goroot = strings.ToLower(goroot)
 	}
+	sep := string(os.PathSeparator)
+	if !strings.HasSuffix(goroot, sep) {
+		goroot += sep
+	}
 	return strings.HasPrefix(abspath, goroot), nil
 }
 
@@ -825,6 +829,31 @@ import (
 
 var _ unsafe.Pointer
 
+// Do the interface allocations only once for common
+// Errno values.
+const (
+	errnoERROR_IO_PENDING = 997
+)
+
+var (
+	errERROR_IO_PENDING error = {{syscalldot}}Errno(errnoERROR_IO_PENDING)
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e {{syscalldot}}Errno) error {
+	switch e {
+	case 0:
+		return nil
+	case errnoERROR_IO_PENDING:
+		return errERROR_IO_PENDING
+	}
+	// TODO: add more here, after collecting data on the common
+	// error values see on Windows. (perhaps when running
+	// all.bat?)
+	return e
+}
+
 var (
 {{template "dlls" .}}
 {{template "funcnames" .}})
diff --git a/src/syscall/mksysnum_linux.pl b/src/syscall/mksysnum_linux.pl
index b6fbcb5..4db8149 100755
--- a/src/syscall/mksysnum_linux.pl
+++ b/src/syscall/mksysnum_linux.pl
@@ -16,21 +16,28 @@ package syscall
 const(
 EOF
 
+my $offset = 0;
+
 sub fmt {
 	my ($name, $num) = @_;
 	if($num > 999){
-		# ignore depricated syscalls that are no longer implemented
+		# ignore deprecated syscalls that are no longer implemented
 		# https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/unistd.h?id=refs/heads/master#n716
 		return;
 	}
 	$name =~ y/a-z/A-Z/;
+	$num = $num + $offset;
 	print "	SYS_$name = $num;\n";
 }
 
 my $prev;
 open(GCC, "gcc -E -dD $ARGV[0] |") || die "can't run gcc";
 while(<GCC>){
-	if(/^#define __NR_syscalls\s+/) {
+	if(/^#define __NR_Linux\s+([0-9]+)/){
+		# mips/mips64: extract offset
+		$offset = $1;
+	}
+	elsif(/^#define __NR_syscalls\s+/) {
 		# ignore redefinitions of __NR_syscalls
 	}
 	elsif(/^#define __NR_(\w+)\s+([0-9]+)/){
@@ -44,6 +51,9 @@ while(<GCC>){
 	elsif(/^#define __NR_(\w+)\s+\(\w+\+\s*([0-9]+)\)/){
 		fmt($1, $prev+$2)
 	}
+	elsif(/^#define __NR_(\w+)\s+\(__NR_Linux \+ ([0-9]+)/){
+		fmt($1, $2);
+	}
 }
 
 print <<EOF;
diff --git a/src/syscall/net_nacl.go b/src/syscall/net_nacl.go
index 1a0122c..9dc5d0c 100644
--- a/src/syscall/net_nacl.go
+++ b/src/syscall/net_nacl.go
@@ -6,6 +6,8 @@
 // The simulation is not particularly tied to NaCl,
 // but other systems have real networks.
 
+// All int64 times are UnixNanos.
+
 package syscall
 
 import (
@@ -50,6 +52,22 @@ func (t *timer) stop() {
 	stopTimer(&t.r)
 }
 
+func (t *timer) reset(q *queue, deadline int64) {
+	if t.r.f != nil {
+		t.stop()
+	}
+	if deadline == 0 {
+		return
+	}
+	if t.r.f == nil {
+		t.q = q
+		t.r.f = timerExpired
+		t.r.arg = t
+	}
+	t.r.when = deadline
+	startTimer(&t.r)
+}
+
 func timerExpired(i interface{}, seq uintptr) {
 	t := i.(*timer)
 	go func() {
@@ -233,9 +251,11 @@ type queue struct {
 	sync.Mutex
 	canRead  sync.Cond
 	canWrite sync.Cond
-	r        int // total read index
-	w        int // total write index
-	m        int // index mask
+	rtimer   *timer // non-nil if in read
+	wtimer   *timer // non-nil if in write
+	r        int    // total read index
+	w        int    // total write index
+	m        int    // index mask
 	closed   bool
 }
 
@@ -259,9 +279,11 @@ func (q *queue) waitRead(n int, deadline int64) (int, error) {
 	}
 	var t timer
 	t.start(q, deadline)
+	q.rtimer = &t
 	for q.w-q.r == 0 && !q.closed && !t.expired {
 		q.canRead.Wait()
 	}
+	q.rtimer = nil
 	t.stop()
 	m := q.w - q.r
 	if m == 0 && t.expired {
@@ -281,9 +303,11 @@ func (q *queue) waitWrite(n int, deadline int64) (int, error) {
 	}
 	var t timer
 	t.start(q, deadline)
+	q.wtimer = &t
 	for q.w-q.r > q.m && !q.closed && !t.expired {
 		q.canWrite.Wait()
 	}
+	q.wtimer = nil
 	t.stop()
 	m := q.m + 1 - (q.w - q.r)
 	if m == 0 && t.expired {
@@ -871,6 +895,13 @@ func SetReadDeadline(fd int, t int64) error {
 		return err
 	}
 	atomic.StoreInt64(&f.rddeadline, t)
+	if bq := f.rd; bq != nil {
+		bq.Lock()
+		if timer := bq.rtimer; timer != nil {
+			timer.reset(&bq.queue, t)
+		}
+		bq.Unlock()
+	}
 	return nil
 }
 
@@ -884,6 +915,13 @@ func SetWriteDeadline(fd int, t int64) error {
 		return err
 	}
 	atomic.StoreInt64(&f.wrdeadline, t)
+	if bq := f.wr; bq != nil {
+		bq.Lock()
+		if timer := bq.wtimer; timer != nil {
+			timer.reset(&bq.queue, t)
+		}
+		bq.Unlock()
+	}
 	return nil
 }
 
diff --git a/src/syscall/netlink_linux.go b/src/syscall/netlink_linux.go
index 26b3040..1cda8c7 100644
--- a/src/syscall/netlink_linux.go
+++ b/src/syscall/netlink_linux.go
@@ -129,10 +129,11 @@ func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) {
 
 func netlinkMessageHeaderAndData(b []byte) (*NlMsghdr, []byte, int, error) {
 	h := (*NlMsghdr)(unsafe.Pointer(&b[0]))
-	if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(b) {
+	l := nlmAlignOf(int(h.Len))
+	if int(h.Len) < NLMSG_HDRLEN || l > len(b) {
 		return nil, nil, 0, EINVAL
 	}
-	return h, b[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil
+	return h, b[NLMSG_HDRLEN:], l, nil
 }
 
 // NetlinkRouteAttr represents a netlink route attribute.
diff --git a/src/syscall/setuidgid_32_linux.go b/src/syscall/setuidgid_32_linux.go
new file mode 100644
index 0000000..182f5d2
--- /dev/null
+++ b/src/syscall/setuidgid_32_linux.go
@@ -0,0 +1,13 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+// +build 386 arm
+
+package syscall
+
+const (
+	sys_SETGID = SYS_SETGID32
+	sys_SETUID = SYS_SETUID32
+)
diff --git a/src/syscall/setuidgid_linux.go b/src/syscall/setuidgid_linux.go
new file mode 100644
index 0000000..bf40d2d
--- /dev/null
+++ b/src/syscall/setuidgid_linux.go
@@ -0,0 +1,13 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+// +build !386,!arm
+
+package syscall
+
+const (
+	sys_SETGID = SYS_SETGID
+	sys_SETUID = SYS_SETUID
+)
diff --git a/src/syscall/sockcmsg_linux.go b/src/syscall/sockcmsg_linux.go
index 5a56b25..4cb9075 100644
--- a/src/syscall/sockcmsg_linux.go
+++ b/src/syscall/sockcmsg_linux.go
@@ -31,6 +31,9 @@ func ParseUnixCredentials(m *SocketControlMessage) (*Ucred, error) {
 	if m.Header.Type != SCM_CREDENTIALS {
 		return nil, EINVAL
 	}
+	if uintptr(len(m.Data)) < unsafe.Sizeof(Ucred{}) {
+		return nil, EINVAL
+	}
 	ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
 	return &ucred, nil
 }
diff --git a/src/syscall/sockcmsg_unix.go b/src/syscall/sockcmsg_unix.go
index bc4caf5..5712bf1 100644
--- a/src/syscall/sockcmsg_unix.go
+++ b/src/syscall/sockcmsg_unix.go
@@ -13,9 +13,10 @@ import "unsafe"
 // Round the length of a raw sockaddr up to align it properly.
 func cmsgAlignOf(salen int) int {
 	salign := sizeofPtr
-	// NOTE: It seems like 64-bit Darwin and DragonFly BSD kernels
-	// still require 32-bit aligned access to network subsystem.
-	if darwin64Bit || dragonfly64Bit {
+	// NOTE: It seems like 64-bit Darwin, DragonFly BSD and
+	// Solaris kernels still require 32-bit aligned access to
+	// network subsystem.
+	if darwin64Bit || dragonfly64Bit || solaris64Bit {
 		salign = 4
 	}
 	return (salen + salign - 1) & ^(salign - 1)
diff --git a/src/syscall/syscall.go b/src/syscall/syscall.go
index bb102c6..2fbe624 100644
--- a/src/syscall/syscall.go
+++ b/src/syscall/syscall.go
@@ -28,6 +28,8 @@ package syscall
 
 import "unsafe"
 
+//go:generate go run mksyscall_windows.go -systemdll -output zsyscall_windows.go syscall_windows.go security_windows.go
+
 // StringByteSlice converts a string to a NUL-terminated []byte,
 // If s contains a NUL byte this function panics instead of
 // returning an error.
@@ -93,6 +95,10 @@ func (tv *Timeval) Nano() int64 {
 	return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
 }
 
+// Getpagesize is provided by the runtime.
+
+func Getpagesize() int
+
 // use is a no-op, but the compiler cannot see that it is.
 // Calling use(p) ensures that p is kept live until that point.
 // This was needed until Go 1.6 to call syscall.Syscall correctly.
diff --git a/src/syscall/syscall_darwin.go b/src/syscall/syscall_darwin.go
index 380be70..689bc14 100644
--- a/src/syscall/syscall_darwin.go
+++ b/src/syscall/syscall_darwin.go
@@ -75,32 +75,16 @@ func nametomib(name string) (mib []_C_int, err error) {
 	return buf[0 : n/siz], nil
 }
 
-// ParseDirent parses up to max directory entries in buf,
-// appending the names to names. It returns the number
-// bytes consumed from buf, the number of entries added
-// to names, and the new names slice.
-func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
-	origlen := len(buf)
-	for max != 0 && len(buf) > 0 {
-		dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
-		if dirent.Reclen == 0 {
-			buf = nil
-			break
-		}
-		buf = buf[dirent.Reclen:]
-		if dirent.Ino == 0 { // File absent in directory.
-			continue
-		}
-		bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
-		var name = string(bytes[0:dirent.Namlen])
-		if name == "." || name == ".." { // Useless names
-			continue
-		}
-		max--
-		count++
-		names = append(names, name)
-	}
-	return origlen - len(buf), count, names
+func direntIno(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
 }
 
 //sys   ptrace(request int, pid int, addr uintptr, data uintptr) (err error)
diff --git a/src/syscall/syscall_darwin_386.go b/src/syscall/syscall_darwin_386.go
index f75de00..05d02fc 100644
--- a/src/syscall/syscall_darwin_386.go
+++ b/src/syscall/syscall_darwin_386.go
@@ -6,23 +6,12 @@ package syscall
 
 import "unsafe"
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int32(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: int32(sec), Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int32(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: int32(sec), Usec: int32(usec)}
 }
 
 //sysnb	gettimeofday(tp *Timeval) (sec int32, usec int32, err error)
diff --git a/src/syscall/syscall_darwin_amd64.go b/src/syscall/syscall_darwin_amd64.go
index 7908311..b15bd68 100644
--- a/src/syscall/syscall_darwin_amd64.go
+++ b/src/syscall/syscall_darwin_amd64.go
@@ -6,23 +6,12 @@ package syscall
 
 import "unsafe"
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: int32(usec)}
 }
 
 //sysnb	gettimeofday(tp *Timeval) (sec int64, usec int32, err error)
diff --git a/src/syscall/syscall_darwin_arm.go b/src/syscall/syscall_darwin_arm.go
index fe43103..73bf83f 100644
--- a/src/syscall/syscall_darwin_arm.go
+++ b/src/syscall/syscall_darwin_arm.go
@@ -6,23 +6,12 @@ package syscall
 
 import "unsafe"
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int32(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: int32(sec), Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int32(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: int32(sec), Usec: int32(usec)}
 }
 
 //sysnb	gettimeofday(tp *Timeval) (sec int32, usec int32, err error)
diff --git a/src/syscall/syscall_darwin_arm64.go b/src/syscall/syscall_darwin_arm64.go
index d396e25..6c8f996 100644
--- a/src/syscall/syscall_darwin_arm64.go
+++ b/src/syscall/syscall_darwin_arm64.go
@@ -6,23 +6,12 @@ package syscall
 
 import "unsafe"
 
-func Getpagesize() int { return 16384 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: int64(sec), Nsec: int64(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: int64(sec), Usec: int32(usec)}
 }
 
 //sysnb	gettimeofday(tp *Timeval) (sec int64, usec int32, err error)
diff --git a/src/syscall/syscall_darwin_test.go b/src/syscall/syscall_darwin_test.go
deleted file mode 100644
index cea5636..0000000
--- a/src/syscall/syscall_darwin_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build darwin
-// +build amd64 386 arm arm64
-
-package syscall_test
-
-import (
-	"syscall"
-	"testing"
-)
-
-func TestDarwinGettimeofday(t *testing.T) {
-	tv := &syscall.Timeval{}
-	if err := syscall.Gettimeofday(tv); err != nil {
-		t.Fatal(err)
-	}
-	if tv.Sec == 0 && tv.Usec == 0 {
-		t.Fatal("Sec and Usec both zero")
-	}
-}
diff --git a/src/syscall/syscall_dragonfly.go b/src/syscall/syscall_dragonfly.go
index 4080b6b..980687c 100644
--- a/src/syscall/syscall_dragonfly.go
+++ b/src/syscall/syscall_dragonfly.go
@@ -56,29 +56,20 @@ func nametomib(name string) (mib []_C_int, err error) {
 	return buf[0 : n/siz], nil
 }
 
-// ParseDirent parses up to max directory entries in buf,
-// appending the names to names. It returns the number
-// bytes consumed from buf, the number of entries added
-// to names, and the new names slice.
-func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
-	origlen := len(buf)
-	for max != 0 && len(buf) > 0 {
-		dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
-		reclen := int(16+dirent.Namlen+1+7) & ^7
-		buf = buf[reclen:]
-		if dirent.Fileno == 0 { // File absent in directory.
-			continue
-		}
-		bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
-		var name = string(bytes[0:dirent.Namlen])
-		if name == "." || name == ".." { // Useless names
-			continue
-		}
-		max--
-		count++
-		names = append(names, name)
+func direntIno(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Fileno), unsafe.Sizeof(Dirent{}.Fileno))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+	namlen, ok := direntNamlen(buf)
+	if !ok {
+		return 0, false
 	}
-	return origlen - len(buf), count, names
+	return (16 + namlen + 1 + 7) & ^uint64(7), true
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
 }
 
 //sysnb pipe() (r int, w int, err error)
diff --git a/src/syscall/syscall_dragonfly_amd64.go b/src/syscall/syscall_dragonfly_amd64.go
index 70c2ffb..bb6130d 100644
--- a/src/syscall/syscall_dragonfly_amd64.go
+++ b/src/syscall/syscall_dragonfly_amd64.go
@@ -6,23 +6,12 @@ package syscall
 
 import "unsafe"
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = nsec % 1e9 / 1e3
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: usec}
 }
 
 func SetKevent(k *Kevent_t, fd, mode, flags int) {
diff --git a/src/syscall/syscall_freebsd.go b/src/syscall/syscall_freebsd.go
index 950dc64..2a304cd 100644
--- a/src/syscall/syscall_freebsd.go
+++ b/src/syscall/syscall_freebsd.go
@@ -54,32 +54,16 @@ func nametomib(name string) (mib []_C_int, err error) {
 	return buf[0 : n/siz], nil
 }
 
-// ParseDirent parses up to max directory entries in buf,
-// appending the names to names. It returns the number
-// bytes consumed from buf, the number of entries added
-// to names, and the new names slice.
-func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
-	origlen := len(buf)
-	for max != 0 && len(buf) > 0 {
-		dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
-		if dirent.Reclen == 0 {
-			buf = nil
-			break
-		}
-		buf = buf[dirent.Reclen:]
-		if dirent.Fileno == 0 { // File absent in directory.
-			continue
-		}
-		bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
-		var name = string(bytes[0:dirent.Namlen])
-		if name == "." || name == ".." { // Useless names
-			continue
-		}
-		max--
-		count++
-		names = append(names, name)
-	}
-	return origlen - len(buf), count, names
+func direntIno(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Fileno), unsafe.Sizeof(Dirent{}.Fileno))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
 }
 
 //sysnb pipe() (r int, w int, err error)
diff --git a/src/syscall/syscall_freebsd_386.go b/src/syscall/syscall_freebsd_386.go
index ebd3d4c..60359e3 100644
--- a/src/syscall/syscall_freebsd_386.go
+++ b/src/syscall/syscall_freebsd_386.go
@@ -6,23 +6,12 @@ package syscall
 
 import "unsafe"
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int32(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: int32(sec), Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int32(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: int32(sec), Usec: int32(usec)}
 }
 
 func SetKevent(k *Kevent_t, fd, mode, flags int) {
diff --git a/src/syscall/syscall_freebsd_amd64.go b/src/syscall/syscall_freebsd_amd64.go
index 70c2ffb..bb6130d 100644
--- a/src/syscall/syscall_freebsd_amd64.go
+++ b/src/syscall/syscall_freebsd_amd64.go
@@ -6,23 +6,12 @@ package syscall
 
 import "unsafe"
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = nsec % 1e9 / 1e3
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: usec}
 }
 
 func SetKevent(k *Kevent_t, fd, mode, flags int) {
diff --git a/src/syscall/syscall_freebsd_arm.go b/src/syscall/syscall_freebsd_arm.go
index ab72871..351f88f 100644
--- a/src/syscall/syscall_freebsd_arm.go
+++ b/src/syscall/syscall_freebsd_arm.go
@@ -6,23 +6,12 @@ package syscall
 
 import "unsafe"
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return ts.Sec*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return tv.Sec*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = nsec / 1e9
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: int32(usec)}
 }
 
 func SetKevent(k *Kevent_t, fd, mode, flags int) {
diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go
index 73a16f8..a8801ad 100644
--- a/src/syscall/syscall_linux.go
+++ b/src/syscall/syscall_linux.go
@@ -757,38 +757,24 @@ func Reboot(cmd int) (err error) {
 	return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, "")
 }
 
-func clen(n []byte) int {
-	for i := 0; i < len(n); i++ {
-		if n[i] == 0 {
-			return i
-		}
-	}
-	return len(n)
-}
-
 func ReadDirent(fd int, buf []byte) (n int, err error) {
 	return Getdents(fd, buf)
 }
 
-func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
-	origlen := len(buf)
-	count = 0
-	for max != 0 && len(buf) > 0 {
-		dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
-		buf = buf[dirent.Reclen:]
-		if dirent.Ino == 0 { // File absent in directory.
-			continue
-		}
-		bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
-		var name = string(bytes[0:clen(bytes[:])])
-		if name == "." || name == ".." { // Useless names
-			continue
-		}
-		max--
-		count++
-		names = append(names, name)
+func direntIno(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+	reclen, ok := direntReclen(buf)
+	if !ok {
+		return 0, false
 	}
-	return origlen - len(buf), count, names
+	return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
 }
 
 //sys	mount(source string, target string, fstype string, flags uintptr, data *byte) (err error)
@@ -862,7 +848,7 @@ func Getpgrp() (pid int) {
 //sys	Nanosleep(time *Timespec, leftover *Timespec) (err error)
 //sys	Pause() (err error)
 //sys	PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
-//sysnb prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) = SYS_PRLIMIT64
+//sysnb prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64
 //sys	read(fd int, p []byte) (n int, err error)
 //sys	Removexattr(path string, attr string) (err error)
 //sys	Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
diff --git a/src/syscall/syscall_linux_386.go b/src/syscall/syscall_linux_386.go
index d9e0ed5..00cf262 100644
--- a/src/syscall/syscall_linux_386.go
+++ b/src/syscall/syscall_linux_386.go
@@ -10,27 +10,17 @@ package syscall
 import "unsafe"
 
 const (
-	_SYS_dup      = SYS_DUP2
-	_SYS_getdents = SYS_GETDENTS64
+	_SYS_dup       = SYS_DUP2
+	_SYS_getdents  = SYS_GETDENTS64
+	_SYS_setgroups = SYS_SETGROUPS32
 )
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int32(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: int32(sec), Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Sec = int32(nsec / 1e9)
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: int32(sec), Usec: int32(usec)}
 }
 
 //sysnb	pipe(p *[2]_C_int) (err error)
diff --git a/src/syscall/syscall_linux_amd64.go b/src/syscall/syscall_linux_amd64.go
index d1bda29..0184d7d 100644
--- a/src/syscall/syscall_linux_amd64.go
+++ b/src/syscall/syscall_linux_amd64.go
@@ -5,8 +5,9 @@
 package syscall
 
 const (
-	_SYS_dup      = SYS_DUP2
-	_SYS_getdents = SYS_GETDENTS64
+	_SYS_dup       = SYS_DUP2
+	_SYS_getdents  = SYS_GETDENTS64
+	_SYS_setgroups = SYS_SETGROUPS
 )
 
 //sys	Dup2(oldfd int, newfd int) (err error)
@@ -72,8 +73,6 @@ func Gettimeofday(tv *Timeval) (err error) {
 	return nil
 }
 
-func Getpagesize() int { return 4096 }
-
 func Time(t *Time_t) (tt Time_t, err error) {
 	var tv Timeval
 	errno := gettimeofday(&tv)
@@ -86,21 +85,12 @@ func Time(t *Time_t) (tt Time_t, err error) {
 	return Time_t(tv.Sec), nil
 }
 
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Sec = nsec / 1e9
-	tv.Usec = nsec % 1e9 / 1e3
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: usec}
 }
 
 //sysnb	pipe(p *[2]_C_int) (err error)
diff --git a/src/syscall/syscall_linux_arm.go b/src/syscall/syscall_linux_arm.go
index 7c78254..2ed31f0 100644
--- a/src/syscall/syscall_linux_arm.go
+++ b/src/syscall/syscall_linux_arm.go
@@ -7,25 +7,17 @@ package syscall
 import "unsafe"
 
 const (
-	_SYS_dup      = SYS_DUP2
-	_SYS_getdents = SYS_GETDENTS64
+	_SYS_dup       = SYS_DUP2
+	_SYS_getdents  = SYS_GETDENTS64
+	_SYS_setgroups = SYS_SETGROUPS32
 )
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int32(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: int32(sec), Nsec: int32(nsec)}
 }
 
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Sec = int32(nsec / 1e9)
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: int32(sec), Usec: int32(usec)}
 }
 
 func Pipe(p []int) (err error) {
diff --git a/src/syscall/syscall_linux_arm64.go b/src/syscall/syscall_linux_arm64.go
index 5f1478c..4462139 100644
--- a/src/syscall/syscall_linux_arm64.go
+++ b/src/syscall/syscall_linux_arm64.go
@@ -5,8 +5,9 @@
 package syscall
 
 const (
-	_SYS_dup      = SYS_DUP3
-	_SYS_getdents = SYS_GETDENTS64
+	_SYS_dup       = SYS_DUP3
+	_SYS_getdents  = SYS_GETDENTS64
+	_SYS_setgroups = SYS_SETGROUPS
 )
 
 //sys	Fchown(fd int, uid int, gid int) (err error)
@@ -68,26 +69,15 @@ func Lstat(path string, stat *Stat_t) (err error) {
 //sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
 //sys	mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error)
 
-func Getpagesize() int { return 65536 }
-
 //sysnb	Gettimeofday(tv *Timeval) (err error)
 //sysnb	Time(t *Time_t) (tt Time_t, err error)
 
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Sec = nsec / 1e9
-	tv.Usec = nsec % 1e9 / 1e3
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: usec}
 }
 
 func Pipe(p []int) (err error) {
diff --git a/src/syscall/syscall_linux_mips64x.go b/src/syscall/syscall_linux_mips64x.go
index a14485a..9fd7982 100644
--- a/src/syscall/syscall_linux_mips64x.go
+++ b/src/syscall/syscall_linux_mips64x.go
@@ -15,7 +15,8 @@ const (
 	// to support older kernels, we have to use getdents for mips64.
 	// Also note that struct dirent is different for these two.
 	// Lookup linux_dirent{,64} in kernel source code for details.
-	_SYS_getdents = SYS_GETDENTS
+	_SYS_getdents  = SYS_GETDENTS
+	_SYS_setgroups = SYS_SETGROUPS
 )
 
 //sys	Dup2(oldfd int, newfd int) (err error)
@@ -65,8 +66,6 @@ const (
 //sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
 //sys	mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error)
 
-func Getpagesize() int { return 65536 }
-
 //sysnb	Gettimeofday(tv *Timeval) (err error)
 
 func Time(t *Time_t) (tt Time_t, err error) {
@@ -81,21 +80,12 @@ func Time(t *Time_t) (tt Time_t, err error) {
 	return Time_t(tv.Sec), nil
 }
 
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Sec = nsec / 1e9
-	tv.Usec = nsec % 1e9 / 1e3
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: usec}
 }
 
 func Pipe(p []int) (err error) {
diff --git a/src/syscall/syscall_linux_mipsx.go b/src/syscall/syscall_linux_mipsx.go
new file mode 100644
index 0000000..48e79ea
--- /dev/null
+++ b/src/syscall/syscall_linux_mipsx.go
@@ -0,0 +1,222 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+// +build mips mipsle
+
+package syscall
+
+import "unsafe"
+
+const (
+	_SYS_dup       = SYS_DUP2
+	_SYS_getdents  = SYS_GETDENTS64
+	_SYS_setgroups = SYS_SETGROUPS
+)
+
+func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
+
+//sys	Dup2(oldfd int, newfd int) (err error)
+//sys	Fchown(fd int, uid int, gid int) (err error)
+//sys	Ftruncate(fd int, length int64) (err error) = SYS_FTRUNCATE64
+//sysnb	Getegid() (egid int)
+//sysnb	Geteuid() (euid int)
+//sysnb	Getgid() (gid int)
+//sysnb	Getuid() (uid int)
+//sys	Lchown(path string, uid int, gid int) (err error)
+//sys	Listen(s int, n int) (err error)
+//sys	Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
+//sys	Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
+//sys	Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT
+//sys	sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
+//sys	Setfsgid(gid int) (err error)
+//sys	Setfsuid(uid int) (err error)
+//sysnb	Setregid(rgid int, egid int) (err error)
+//sysnb	Setresgid(rgid int, egid int, sgid int) (err error)
+//sysnb	Setresuid(ruid int, euid int, suid int) (err error)
+
+//sysnb	Setreuid(ruid int, euid int) (err error)
+//sys	Shutdown(fd int, how int) (err error)
+//sys	Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
+
+//sys	SyncFileRange(fd int, off int64, n int64, flags int) (err error)
+//sys	Truncate(path string, length int64) (err error) = SYS_TRUNCATE64
+//sys	accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error)
+//sys	accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error)
+//sys	bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error)
+//sys	connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error)
+//sysnb	getgroups(n int, list *_Gid_t) (nn int, err error)
+//sysnb	setgroups(n int, list *_Gid_t) (err error)
+//sys	getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error)
+//sys	setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error)
+//sysnb	socket(domain int, typ int, proto int) (fd int, err error)
+//sysnb	socketpair(domain int, typ int, proto int, fd *[2]int32) (err error)
+//sysnb	getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error)
+//sysnb	getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error)
+//sys	recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
+//sys	sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error)
+//sys	recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
+//sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
+
+//sysnb	InotifyInit() (fd int, err error)
+//sys	Ioperm(from int, num int, on int) (err error)
+//sys	Iopl(level int) (err error)
+
+//sysnb	Gettimeofday(tv *Timeval) (err error)
+//sysnb	Time(t *Time_t) (tt Time_t, err error)
+
+//sys	Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64
+//sys	Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
+//sys	Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
+
+func Fstatfs(fd int, buf *Statfs_t) (err error) {
+	_, _, e := Syscall(SYS_FSTATFS64, uintptr(fd), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf)))
+	use(unsafe.Pointer(buf))
+	if e != 0 {
+		err = errnoErr(e)
+	}
+	return
+}
+
+func Statfs(path string, buf *Statfs_t) (err error) {
+	p, err := BytePtrFromString(path)
+	if err != nil {
+		return err
+	}
+	_, _, e := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(p)), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf)))
+	use(unsafe.Pointer(p))
+	if e != 0 {
+		err = errnoErr(e)
+	}
+	return
+}
+
+func Seek(fd int, offset int64, whence int) (off int64, err error) {
+	_, _, e := Syscall6(SYS__LLSEEK, uintptr(fd), uintptr(offset>>32), uintptr(offset), uintptr(unsafe.Pointer(&off)), uintptr(whence), 0)
+	if e != 0 {
+		err = errnoErr(e)
+	}
+	return
+}
+
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: int32(sec), Nsec: int32(nsec)}
+}
+
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: int32(sec), Usec: int32(usec)}
+}
+
+//sysnb pipe2(p *[2]_C_int, flags int) (err error)
+
+func Pipe2(p []int, flags int) (err error) {
+	if len(p) != 2 {
+		return EINVAL
+	}
+	var pp [2]_C_int
+	err = pipe2(&pp, flags)
+	p[0] = int(pp[0])
+	p[1] = int(pp[1])
+	return
+}
+
+func Pipe(p []int) (err error) {
+	if len(p) != 2 {
+		return EINVAL
+	}
+	var pp [2]_C_int
+	err = pipe2(&pp, 0)
+	p[0] = int(pp[0])
+	p[1] = int(pp[1])
+	return
+}
+
+//sys	mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error)
+
+func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) {
+	page := uintptr(offset / 4096)
+	if offset != int64(page)*4096 {
+		return 0, EINVAL
+	}
+	return mmap2(addr, length, prot, flags, fd, page)
+}
+
+const rlimInf32 = ^uint32(0)
+const rlimInf64 = ^uint64(0)
+
+type rlimit32 struct {
+	Cur uint32
+	Max uint32
+}
+
+//sysnb getrlimit(resource int, rlim *rlimit32) (err error) = SYS_GETRLIMIT
+
+func Getrlimit(resource int, rlim *Rlimit) (err error) {
+	err = prlimit(0, resource, nil, rlim)
+	if err != ENOSYS {
+		return err
+	}
+
+	rl := rlimit32{}
+	err = getrlimit(resource, &rl)
+	if err != nil {
+		return
+	}
+
+	if rl.Cur == rlimInf32 {
+		rlim.Cur = rlimInf64
+	} else {
+		rlim.Cur = uint64(rl.Cur)
+	}
+
+	if rl.Max == rlimInf32 {
+		rlim.Max = rlimInf64
+	} else {
+		rlim.Max = uint64(rl.Max)
+	}
+	return
+}
+
+//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT
+
+func Setrlimit(resource int, rlim *Rlimit) (err error) {
+	err = prlimit(0, resource, rlim, nil)
+	if err != ENOSYS {
+		return err
+	}
+
+	rl := rlimit32{}
+	if rlim.Cur == rlimInf64 {
+		rl.Cur = rlimInf32
+	} else if rlim.Cur < uint64(rlimInf32) {
+		rl.Cur = uint32(rlim.Cur)
+	} else {
+		return EINVAL
+	}
+	if rlim.Max == rlimInf64 {
+		rl.Max = rlimInf32
+	} else if rlim.Max < uint64(rlimInf32) {
+		rl.Max = uint32(rlim.Max)
+	} else {
+		return EINVAL
+	}
+
+	return setrlimit(resource, &rl)
+}
+
+func (r *PtraceRegs) PC() uint64 { return uint64(r.Regs[64]) }
+
+func (r *PtraceRegs) SetPC(pc uint64) { r.Regs[64] = uint32(pc) }
+
+func (iov *Iovec) SetLen(length int) {
+	iov.Len = uint32(length)
+}
+
+func (msghdr *Msghdr) SetControllen(length int) {
+	msghdr.Controllen = uint32(length)
+}
+
+func (cmsg *Cmsghdr) SetLen(length int) {
+	cmsg.Len = uint32(length)
+}
diff --git a/src/syscall/syscall_linux_ppc64x.go b/src/syscall/syscall_linux_ppc64x.go
index 9f1c07e..307abc9 100644
--- a/src/syscall/syscall_linux_ppc64x.go
+++ b/src/syscall/syscall_linux_ppc64x.go
@@ -8,8 +8,9 @@
 package syscall
 
 const (
-	_SYS_dup      = SYS_DUP2
-	_SYS_getdents = SYS_GETDENTS64
+	_SYS_dup       = SYS_DUP2
+	_SYS_getdents  = SYS_GETDENTS64
+	_SYS_setgroups = SYS_SETGROUPS
 )
 
 //sys	Dup2(oldfd int, newfd int) (err error)
@@ -64,26 +65,15 @@ const (
 //sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
 //sys	mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error)
 
-func Getpagesize() int { return 65536 }
-
 //sysnb	Gettimeofday(tv *Timeval) (err error)
 //sysnb	Time(t *Time_t) (tt Time_t, err error)
 
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Sec = nsec / 1e9
-	tv.Usec = nsec % 1e9 / 1e3
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: usec}
 }
 
 func Pipe(p []int) (err error) {
diff --git a/src/syscall/syscall_linux_s390x.go b/src/syscall/syscall_linux_s390x.go
index d74277a..148790e 100644
--- a/src/syscall/syscall_linux_s390x.go
+++ b/src/syscall/syscall_linux_s390x.go
@@ -7,8 +7,9 @@ package syscall
 import "unsafe"
 
 const (
-	_SYS_dup      = SYS_DUP2
-	_SYS_getdents = SYS_GETDENTS64
+	_SYS_dup       = SYS_DUP2
+	_SYS_getdents  = SYS_GETDENTS64
+	_SYS_setgroups = SYS_SETGROUPS
 )
 
 //sys	Dup2(oldfd int, newfd int) (err error)
@@ -44,8 +45,6 @@ const (
 //sysnb	getgroups(n int, list *_Gid_t) (nn int, err error)
 //sysnb	setgroups(n int, list *_Gid_t) (err error)
 
-func Getpagesize() int { return 4096 }
-
 //sysnb	Gettimeofday(tv *Timeval) (err error)
 
 func Time(t *Time_t) (tt Time_t, err error) {
@@ -60,21 +59,12 @@ func Time(t *Time_t) (tt Time_t, err error) {
 	return Time_t(tv.Sec), nil
 }
 
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Sec = nsec / 1e9
-	tv.Usec = nsec % 1e9 / 1e3
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: usec}
 }
 
 func Pipe(p []int) (err error) {
diff --git a/src/syscall/syscall_linux_test.go b/src/syscall/syscall_linux_test.go
index 4cabf6c..2c4d953 100644
--- a/src/syscall/syscall_linux_test.go
+++ b/src/syscall/syscall_linux_test.go
@@ -138,3 +138,31 @@ func deathSignalChild() {
 	fmt.Println("not ok")
 	os.Exit(1)
 }
+
+func TestParseNetlinkMessage(t *testing.T) {
+	for i, b := range [][]byte{
+		{103, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 5, 8, 0, 3,
+			0, 8, 0, 6, 0, 0, 0, 0, 1, 63, 0, 10, 0, 69, 16, 0, 59, 39, 82, 64, 0, 64, 6, 21, 89, 127, 0, 0,
+			1, 127, 0, 0, 1, 230, 228, 31, 144, 32, 186, 155, 211, 185, 151, 209, 179, 128, 24, 1, 86,
+			53, 119, 0, 0, 1, 1, 8, 10, 0, 17, 234, 12, 0, 17, 189, 126, 107, 106, 108, 107, 106, 13, 10,
+		},
+		{106, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 3, 8, 0, 3,
+			0, 8, 0, 6, 0, 0, 0, 0, 1, 66, 0, 10, 0, 69, 0, 0, 62, 230, 255, 64, 0, 64, 6, 85, 184, 127, 0, 0,
+			1, 127, 0, 0, 1, 237, 206, 31, 144, 73, 197, 128, 65, 250, 60, 192, 97, 128, 24, 1, 86, 253, 21, 0,
+			0, 1, 1, 8, 10, 0, 51, 106, 89, 0, 51, 102, 198, 108, 104, 106, 108, 107, 104, 108, 107, 104, 10,
+		},
+		{102, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 1, 8, 0, 3, 0,
+			8, 0, 6, 0, 0, 0, 0, 1, 62, 0, 10, 0, 69, 0, 0, 58, 231, 2, 64, 0, 64, 6, 85, 185, 127, 0, 0, 1, 127,
+			0, 0, 1, 237, 206, 31, 144, 73, 197, 128, 86, 250, 60, 192, 97, 128, 24, 1, 86, 104, 64, 0, 0, 1, 1, 8,
+			10, 0, 52, 198, 200, 0, 51, 135, 232, 101, 115, 97, 103, 103, 10,
+		},
+	} {
+		m, err := syscall.ParseNetlinkMessage(b)
+		if err != syscall.EINVAL {
+			t.Errorf("#%d: got %v; want EINVAL", i, err)
+		}
+		if m != nil {
+			t.Errorf("#%d: got %v; want nil", i, m)
+		}
+	}
+}
diff --git a/src/syscall/syscall_nacl.go b/src/syscall/syscall_nacl.go
index ba6eafe..3247505 100644
--- a/src/syscall/syscall_nacl.go
+++ b/src/syscall/syscall_nacl.go
@@ -26,34 +26,20 @@ type Dirent struct {
 	Name   [256]byte
 }
 
-func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
-	origlen := len(buf)
-	count = 0
-	for max != 0 && len(buf) > 0 {
-		dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
-		buf = buf[dirent.Reclen:]
-		if dirent.Ino == 0 { // File absent in directory.
-			continue
-		}
-		bytes := (*[512 + PathMax]byte)(unsafe.Pointer(&dirent.Name[0]))
-		var name = string(bytes[0:clen(bytes[:])])
-		if name == "." || name == ".." { // Useless names
-			continue
-		}
-		max--
-		count++
-		names = append(names, name)
-	}
-	return origlen - len(buf), count, names
+func direntIno(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
 }
 
-func clen(n []byte) int {
-	for i := 0; i < len(n); i++ {
-		if n[i] == 0 {
-			return i
-		}
+func direntReclen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+	reclen, ok := direntReclen(buf)
+	if !ok {
+		return 0, false
 	}
-	return len(n)
+	return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
 }
 
 const PathMax = 256
@@ -292,9 +278,9 @@ func Getegid() int                      { return 1 }
 func Geteuid() int                      { return 1 }
 func Getgid() int                       { return 1 }
 func Getgroups() ([]int, error)         { return []int{1}, nil }
-func Getpagesize() int                  { return 65536 }
 func Getppid() int                      { return 2 }
 func Getpid() int                       { return 3 }
+func Gettimeofday(tv *Timeval) error    { return ENOSYS }
 func Getuid() int                       { return 1 }
 func Kill(pid int, signum Signal) error { return ENOSYS }
 func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
@@ -310,3 +296,5 @@ func RouteRIB(facility, param int) ([]byte, error)                { return nil,
 func ParseRoutingMessage(b []byte) ([]RoutingMessage, error)      { return nil, ENOSYS }
 func ParseRoutingSockaddr(msg RoutingMessage) ([]Sockaddr, error) { return nil, ENOSYS }
 func SysctlUint32(name string) (value uint32, err error)          { return 0, ENOSYS }
+
+type Iovec struct{} // dummy
diff --git a/src/syscall/syscall_nacl_386.go b/src/syscall/syscall_nacl_386.go
index 0d685a6..39112eb 100644
--- a/src/syscall/syscall_nacl_386.go
+++ b/src/syscall/syscall_nacl_386.go
@@ -14,19 +14,10 @@ type Timeval struct {
 	Usec int32
 }
 
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int64(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: int32(usec)}
 }
diff --git a/src/syscall/syscall_nacl_amd64p32.go b/src/syscall/syscall_nacl_amd64p32.go
index 0d685a6..39112eb 100644
--- a/src/syscall/syscall_nacl_amd64p32.go
+++ b/src/syscall/syscall_nacl_amd64p32.go
@@ -14,19 +14,10 @@ type Timeval struct {
 	Usec int32
 }
 
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int64(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: int32(usec)}
 }
diff --git a/src/syscall/syscall_nacl_arm.go b/src/syscall/syscall_nacl_arm.go
index 5d72503..dec97b5 100644
--- a/src/syscall/syscall_nacl_arm.go
+++ b/src/syscall/syscall_nacl_arm.go
@@ -14,19 +14,10 @@ type Timeval struct {
 	Usec int32
 }
 
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int64(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: int32(usec)}
 }
diff --git a/src/syscall/syscall_netbsd.go b/src/syscall/syscall_netbsd.go
index 7fd6e2b..f2e1694 100644
--- a/src/syscall/syscall_netbsd.go
+++ b/src/syscall/syscall_netbsd.go
@@ -26,7 +26,7 @@ type SockaddrDatalink struct {
 	raw    RawSockaddrDatalink
 }
 
-func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
+func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
 
 func sysctlNodes(mib []_C_int) (nodes []Sysctlnode, err error) {
 	var olen uintptr
@@ -90,32 +90,16 @@ func nametomib(name string) (mib []_C_int, err error) {
 	return mib, nil
 }
 
-// ParseDirent parses up to max directory entries in buf,
-// appending the names to names. It returns the number
-// bytes consumed from buf, the number of entries added
-// to names, and the new names slice.
-func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
-	origlen := len(buf)
-	for max != 0 && len(buf) > 0 {
-		dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
-		if dirent.Reclen == 0 {
-			buf = nil
-			break
-		}
-		buf = buf[dirent.Reclen:]
-		if dirent.Fileno == 0 { // File absent in directory.
-			continue
-		}
-		bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
-		var name = string(bytes[0:dirent.Namlen])
-		if name == "." || name == ".." { // Useless names
-			continue
-		}
-		max--
-		count++
-		names = append(names, name)
-	}
-	return origlen - len(buf), count, names
+func direntIno(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Fileno), unsafe.Sizeof(Dirent{}.Fileno))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
 }
 
 //sysnb pipe() (fd1 int, fd2 int, err error)
diff --git a/src/syscall/syscall_netbsd_386.go b/src/syscall/syscall_netbsd_386.go
index 2dbff07..3059b9a 100644
--- a/src/syscall/syscall_netbsd_386.go
+++ b/src/syscall/syscall_netbsd_386.go
@@ -4,23 +4,12 @@
 
 package syscall
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int64(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: int32(usec)}
 }
 
 func SetKevent(k *Kevent_t, fd, mode, flags int) {
diff --git a/src/syscall/syscall_netbsd_amd64.go b/src/syscall/syscall_netbsd_amd64.go
index 5784db9..b4c5d0d 100644
--- a/src/syscall/syscall_netbsd_amd64.go
+++ b/src/syscall/syscall_netbsd_amd64.go
@@ -4,23 +4,12 @@
 
 package syscall
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int64(nsec / 1e9)
-	ts.Nsec = int64(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: int32(usec)}
 }
 
 func SetKevent(k *Kevent_t, fd, mode, flags int) {
diff --git a/src/syscall/syscall_netbsd_arm.go b/src/syscall/syscall_netbsd_arm.go
index 659698a..dcafd1f 100644
--- a/src/syscall/syscall_netbsd_arm.go
+++ b/src/syscall/syscall_netbsd_arm.go
@@ -4,23 +4,12 @@
 
 package syscall
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int64(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: int32(usec)}
 }
 
 func SetKevent(k *Kevent_t, fd, mode, flags int) {
diff --git a/src/syscall/syscall_openbsd.go b/src/syscall/syscall_openbsd.go
index e196e59..bd25fbf 100644
--- a/src/syscall/syscall_openbsd.go
+++ b/src/syscall/syscall_openbsd.go
@@ -26,7 +26,7 @@ type SockaddrDatalink struct {
 	raw    RawSockaddrDatalink
 }
 
-func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
+func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
 
 func nametomib(name string) (mib []_C_int, err error) {
 
@@ -50,32 +50,16 @@ func nametomib(name string) (mib []_C_int, err error) {
 	return nil, EINVAL
 }
 
-// ParseDirent parses up to max directory entries in buf,
-// appending the names to names. It returns the number
-// bytes consumed from buf, the number of entries added
-// to names, and the new names slice.
-func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
-	origlen := len(buf)
-	for max != 0 && len(buf) > 0 {
-		dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
-		if dirent.Reclen == 0 {
-			buf = nil
-			break
-		}
-		buf = buf[dirent.Reclen:]
-		if dirent.Fileno == 0 { // File absent in directory.
-			continue
-		}
-		bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
-		var name = string(bytes[0:dirent.Namlen])
-		if name == "." || name == ".." { // Useless names
-			continue
-		}
-		max--
-		count++
-		names = append(names, name)
-	}
-	return origlen - len(buf), count, names
+func direntIno(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Fileno), unsafe.Sizeof(Dirent{}.Fileno))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
 }
 
 //sysnb pipe(p *[2]_C_int) (err error)
diff --git a/src/syscall/syscall_openbsd_386.go b/src/syscall/syscall_openbsd_386.go
index ad5ae14..ca07ae0 100644
--- a/src/syscall/syscall_openbsd_386.go
+++ b/src/syscall/syscall_openbsd_386.go
@@ -4,23 +4,12 @@
 
 package syscall
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int64(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: int32(usec)}
 }
 
 func SetKevent(k *Kevent_t, fd, mode, flags int) {
diff --git a/src/syscall/syscall_openbsd_amd64.go b/src/syscall/syscall_openbsd_amd64.go
index 6181344..47fc7e7 100644
--- a/src/syscall/syscall_openbsd_amd64.go
+++ b/src/syscall/syscall_openbsd_amd64.go
@@ -4,23 +4,12 @@
 
 package syscall
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = nsec % 1e9 / 1e3
-	tv.Sec = nsec / 1e9
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: usec}
 }
 
 func SetKevent(k *Kevent_t, fd, mode, flags int) {
diff --git a/src/syscall/syscall_openbsd_arm.go b/src/syscall/syscall_openbsd_arm.go
index ad5ae14..ca07ae0 100644
--- a/src/syscall/syscall_openbsd_arm.go
+++ b/src/syscall/syscall_openbsd_arm.go
@@ -4,23 +4,12 @@
 
 package syscall
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = int64(nsec / 1e9)
-	ts.Nsec = int32(nsec % 1e9)
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: int32(nsec)}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = int32(nsec % 1e9 / 1e3)
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: int32(usec)}
 }
 
 func SetKevent(k *Kevent_t, fd, mode, flags int) {
diff --git a/src/syscall/syscall_plan9.go b/src/syscall/syscall_plan9.go
index b511867..0691889 100644
--- a/src/syscall/syscall_plan9.go
+++ b/src/syscall/syscall_plan9.go
@@ -305,8 +305,6 @@ func Gettimeofday(tv *Timeval) error {
 	return nil
 }
 
-func Getpagesize() int { return 0x1000 }
-
 func Getegid() (egid int) { return -1 }
 func Geteuid() (euid int) { return -1 }
 func Getgid() (gid int)   { return -1 }
diff --git a/src/syscall/syscall_solaris.go b/src/syscall/syscall_solaris.go
index b307a80..636de92 100644
--- a/src/syscall/syscall_solaris.go
+++ b/src/syscall/syscall_solaris.go
@@ -38,32 +38,20 @@ func clen(n []byte) int {
 	return len(n)
 }
 
-// ParseDirent parses up to max directory entries in buf,
-// appending the names to names. It returns the number
-// bytes consumed from buf, the number of entries added
-// to names, and the new names slice.
-func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
-	origlen := len(buf)
-	for max != 0 && len(buf) > 0 {
-		dirent := (*Dirent)(unsafe.Pointer(&buf[0]))
-		if dirent.Reclen == 0 {
-			buf = nil
-			break
-		}
-		buf = buf[dirent.Reclen:]
-		if dirent.Ino == 0 { // File absent in directory.
-			continue
-		}
-		bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
-		var name = string(bytes[0:clen(bytes[:])])
-		if name == "." || name == ".." { // Useless names
-			continue
-		}
-		max--
-		count++
-		names = append(names, name)
+func direntIno(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+	return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+	reclen, ok := direntReclen(buf)
+	if !ok {
+		return 0, false
 	}
-	return origlen - len(buf), count, names
+	return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
 }
 
 func pipe() (r uintptr, w uintptr, err uintptr)
@@ -291,7 +279,7 @@ func UtimesNano(path string, ts []Timespec) (err error) {
 		tv[i].Sec = ts[i].Sec
 		tv[i].Usec = ts[i].Nsec / 1000
 	}
-	return Utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
+	return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
 }
 
 //sys	fcntl(fd int, cmd int, arg int) (val int, err error)
@@ -382,6 +370,7 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
 			iov.SetLen(1)
 		}
 		msg.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
+		msg.Accrightslen = int32(len(oob))
 	}
 	msg.Iov = &iov
 	msg.Iovlen = 1
@@ -401,7 +390,7 @@ func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
 	return
 }
 
-//sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.sendmsg
+//sys	sendmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_sendmsg
 
 func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
 	var ptr unsafe.Pointer
@@ -428,6 +417,7 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error)
 			iov.SetLen(1)
 		}
 		msg.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
+		msg.Accrightslen = int32(len(oob))
 	}
 	msg.Iov = &iov
 	msg.Iovlen = 1
@@ -470,7 +460,7 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error)
 //sys	Kill(pid int, signum Signal) (err error)
 //sys	Lchown(path string, uid int, gid int) (err error)
 //sys	Link(path string, link string) (err error)
-//sys	Listen(s int, backlog int) (err error) = libsocket.listen
+//sys	Listen(s int, backlog int) (err error) = libsocket.__xnet_listen
 //sys	Lstat(path string, stat *Stat_t) (err error)
 //sys	Mkdir(path string, mode uint32) (err error)
 //sys	Mknod(path string, mode uint32, dev int) (err error)
@@ -504,21 +494,36 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error)
 //sys	Ftruncate(fd int, length int64) (err error)
 //sys	Umask(newmask int) (oldmask int)
 //sys	Unlink(path string) (err error)
-//sys	Utimes(path string, times *[2]Timeval) (err error)
-//sys	bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) = libsocket.bind
-//sys	connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) = libsocket.connect
+//sys	utimes(path string, times *[2]Timeval) (err error)
+//sys	bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) = libsocket.__xnet_bind
+//sys	connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) = libsocket.__xnet_connect
 //sys	mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
 //sys	munmap(addr uintptr, length uintptr) (err error)
-//sys	sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) = libsocket.sendto
-//sys	socket(domain int, typ int, proto int) (fd int, err error) = libsocket.socket
-//sysnb	socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) = libsocket.socketpair
+//sys	sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) = libsocket.__xnet_sendto
+//sys	socket(domain int, typ int, proto int) (fd int, err error) = libsocket.__xnet_socket
+//sysnb	socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) = libsocket.__xnet_socketpair
 //sys	write(fd int, p []byte) (n int, err error)
-//sys	getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) = libsocket.getsockopt
+//sys	getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) = libsocket.__xnet_getsockopt
 //sysnb	getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) = libsocket.getpeername
 //sys	getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) = libsocket.getsockname
 //sys	setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) = libsocket.setsockopt
 //sys	recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) = libsocket.recvfrom
-//sys	recvmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.recvmsg
+//sys	recvmsg(s int, msg *Msghdr, flags int) (n int, err error) = libsocket.__xnet_recvmsg
+//sys	getexecname() (path unsafe.Pointer, err error) = libc.getexecname
+
+func Getexecname() (path string, err error) {
+	ptr, err := getexecname()
+	if err != nil {
+		return "", err
+	}
+	bytes := (*[1 << 29]byte)(ptr)[:]
+	for i, b := range bytes {
+		if b == 0 {
+			return string(bytes[:i]), nil
+		}
+	}
+	panic("unreachable")
+}
 
 func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
 	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_read)), 3, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf), 0, 0, 0)
@@ -537,3 +542,10 @@ func writelen(fd int, buf *byte, nbuf int) (n int, err error) {
 	}
 	return
 }
+
+func Utimes(path string, tv []Timeval) error {
+	if len(tv) != 2 {
+		return EINVAL
+	}
+	return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
+}
diff --git a/src/syscall/syscall_solaris_amd64.go b/src/syscall/syscall_solaris_amd64.go
index 67b8af1..87ad4bf 100644
--- a/src/syscall/syscall_solaris_amd64.go
+++ b/src/syscall/syscall_solaris_amd64.go
@@ -4,23 +4,12 @@
 
 package syscall
 
-func Getpagesize() int { return 4096 }
-
-func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
-
-func NsecToTimespec(nsec int64) (ts Timespec) {
-	ts.Sec = nsec / 1e9
-	ts.Nsec = nsec % 1e9
-	return
+func setTimespec(sec, nsec int64) Timespec {
+	return Timespec{Sec: sec, Nsec: nsec}
 }
 
-func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
-
-func NsecToTimeval(nsec int64) (tv Timeval) {
-	nsec += 999 // round up to microsecond
-	tv.Usec = nsec % 1e9 / 1e3
-	tv.Sec = int64(nsec / 1e9)
-	return
+func setTimeval(sec, usec int64) Timeval {
+	return Timeval{Sec: sec, Usec: usec}
 }
 
 func (iov *Iovec) SetLen(length int) {
diff --git a/src/syscall/syscall_test.go b/src/syscall/syscall_test.go
index 0a0b8b7..c3fffda 100644
--- a/src/syscall/syscall_test.go
+++ b/src/syscall/syscall_test.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"internal/testenv"
 	"os"
+	"runtime"
 	"syscall"
 	"testing"
 )
@@ -59,3 +60,16 @@ func TestExecErrPermutedFds(t *testing.T) {
 		t.Fatalf("StartProcess of invalid program returned err = nil")
 	}
 }
+
+func TestGettimeofday(t *testing.T) {
+	if runtime.GOOS == "nacl" {
+		t.Skip("not implemented on nacl")
+	}
+	tv := &syscall.Timeval{}
+	if err := syscall.Gettimeofday(tv); err != nil {
+		t.Fatal(err)
+	}
+	if tv.Sec == 0 && tv.Usec == 0 {
+		t.Fatal("Sec and Usec both zero")
+	}
+}
diff --git a/src/syscall/syscall_unix.go b/src/syscall/syscall_unix.go
index 4dae9d9..442f558 100644
--- a/src/syscall/syscall_unix.go
+++ b/src/syscall/syscall_unix.go
@@ -23,6 +23,7 @@ const (
 	darwin64Bit    = runtime.GOOS == "darwin" && sizeofPtr == 8
 	dragonfly64Bit = runtime.GOOS == "dragonfly" && sizeofPtr == 8
 	netbsd32Bit    = runtime.GOOS == "netbsd" && sizeofPtr == 4
+	solaris64Bit   = runtime.GOOS == "solaris" && sizeofPtr == 8
 )
 
 func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
diff --git a/src/syscall/syscall_unix_test.go b/src/syscall/syscall_unix_test.go
index 80544f3..2f25d18 100644
--- a/src/syscall/syscall_unix_test.go
+++ b/src/syscall/syscall_unix_test.go
@@ -125,15 +125,6 @@ func TestFcntlFlock(t *testing.T) {
 // "-test.run=^TestPassFD$" and an environment variable used to signal
 // that the test should become the child process instead.
 func TestPassFD(t *testing.T) {
-	switch runtime.GOOS {
-	case "dragonfly":
-		// TODO(jsing): Figure out why sendmsg is returning EINVAL.
-		t.Skip("skipping test on dragonfly")
-	case "solaris":
-		// TODO(aram): Figure out why ReadMsgUnix is returning empty message.
-		t.Skip("skipping test on solaris, see issue 7402")
-	}
-
 	testenv.MustHaveExec(t)
 
 	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go
index 703bb53..f4f8f3a 100644
--- a/src/syscall/syscall_windows.go
+++ b/src/syscall/syscall_windows.go
@@ -14,8 +14,6 @@ import (
 	"unsafe"
 )
 
-//go:generate go run mksyscall_windows.go -systemdll -output zsyscall_windows.go syscall_windows.go security_windows.go
-
 type Handle uintptr
 
 const InvalidHandle = ^Handle(0)
@@ -76,8 +74,6 @@ func UTF16PtrFromString(s string) (*uint16, error) {
 	return &a[0], nil
 }
 
-func Getpagesize() int { return 4096 }
-
 // Errno is the Windows error number.
 type Errno uintptr
 
@@ -1028,11 +1024,31 @@ func Readlink(path string, buf []byte) (n int, err error) {
 	case IO_REPARSE_TAG_SYMLINK:
 		data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
 		p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
-		s = UTF16ToString(p[data.PrintNameOffset/2 : (data.PrintNameLength-data.PrintNameOffset)/2])
+		s = UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
+		if data.Flags&_SYMLINK_FLAG_RELATIVE == 0 {
+			if len(s) >= 4 && s[:4] == `\??\` {
+				s = s[4:]
+				switch {
+				case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar
+					// do nothing
+				case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar
+					s = `\\` + s[4:]
+				default:
+					// unexpected; do nothing
+				}
+			} else {
+				// unexpected; do nothing
+			}
+		}
 	case _IO_REPARSE_TAG_MOUNT_POINT:
 		data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
 		p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
-		s = UTF16ToString(p[data.PrintNameOffset/2 : (data.PrintNameLength-data.PrintNameOffset)/2])
+		s = UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
+		if len(s) >= 4 && s[:4] == `\??\` { // \??\C:\foo\bar
+			s = s[4:]
+		} else {
+			// unexpected; do nothing
+		}
 	default:
 		// the path is not a symlink or junction but another type of reparse
 		// point
diff --git a/src/syscall/timestruct.go b/src/syscall/timestruct.go
new file mode 100644
index 0000000..49c3383
--- /dev/null
+++ b/src/syscall/timestruct.go
@@ -0,0 +1,40 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+
+package syscall
+
+// TimespecToNsec converts a Timespec value into a number of
+// nanoseconds since the Unix epoch.
+func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
+
+// NsecToTimespec takes a number of nanoseconds since the Unix epoch
+// and returns the corresponding Timespec value.
+func NsecToTimespec(nsec int64) Timespec {
+	sec := nsec / 1e9
+	nsec = nsec % 1e9
+	if nsec < 0 {
+		nsec += 1e9
+		sec--
+	}
+	return setTimespec(sec, nsec)
+}
+
+// TimevalToNsec converts a Timeval value into a number of nanoseconds
+// since the Unix epoch.
+func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }
+
+// NsecToTimeval takes a number of nanoseconds since the Unix epoch
+// and returns the corresponding Timeval value.
+func NsecToTimeval(nsec int64) Timeval {
+	nsec += 999 // round up to microsecond
+	usec := nsec % 1e9 / 1e3
+	sec := nsec / 1e9
+	if usec < 0 {
+		usec += 1e6
+		sec--
+	}
+	return setTimeval(sec, usec)
+}
diff --git a/src/syscall/types_linux.go b/src/syscall/types_linux.go
index 2a16650..125f69d 100644
--- a/src/syscall/types_linux.go
+++ b/src/syscall/types_linux.go
@@ -112,7 +112,7 @@ typedef struct {} ptracePer;
 // The real epoll_event is a union, and godefs doesn't handle it well.
 struct my_epoll_event {
 	uint32_t events;
-#ifdef __ARM_EABI__
+#if defined(__ARM_EABI__) || (defined(__mips__) && _MIPS_SIM == _ABIO32)
 	// padding is not specified in linux/eventpoll.h but added to conform to the
 	// alignment requirements of EABI
 	int32_t padFd;
diff --git a/src/syscall/zerrors_linux_mips.go b/src/syscall/zerrors_linux_mips.go
new file mode 100644
index 0000000..580d66f
--- /dev/null
+++ b/src/syscall/zerrors_linux_mips.go
@@ -0,0 +1,1834 @@
+// mkerrors.sh
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs -- _const.go
+
+package syscall
+
+const (
+	AF_ALG                           = 0x26
+	AF_APPLETALK                     = 0x5
+	AF_ASH                           = 0x12
+	AF_ATMPVC                        = 0x8
+	AF_ATMSVC                        = 0x14
+	AF_AX25                          = 0x3
+	AF_BLUETOOTH                     = 0x1f
+	AF_BRIDGE                        = 0x7
+	AF_CAIF                          = 0x25
+	AF_CAN                           = 0x1d
+	AF_DECnet                        = 0xc
+	AF_ECONET                        = 0x13
+	AF_FILE                          = 0x1
+	AF_IEEE802154                    = 0x24
+	AF_INET                          = 0x2
+	AF_INET6                         = 0xa
+	AF_IPX                           = 0x4
+	AF_IRDA                          = 0x17
+	AF_ISDN                          = 0x22
+	AF_IUCV                          = 0x20
+	AF_KEY                           = 0xf
+	AF_LLC                           = 0x1a
+	AF_LOCAL                         = 0x1
+	AF_MAX                           = 0x29
+	AF_NETBEUI                       = 0xd
+	AF_NETLINK                       = 0x10
+	AF_NETROM                        = 0x6
+	AF_NFC                           = 0x27
+	AF_PACKET                        = 0x11
+	AF_PHONET                        = 0x23
+	AF_PPPOX                         = 0x18
+	AF_RDS                           = 0x15
+	AF_ROSE                          = 0xb
+	AF_ROUTE                         = 0x10
+	AF_RXRPC                         = 0x21
+	AF_SECURITY                      = 0xe
+	AF_SNA                           = 0x16
+	AF_TIPC                          = 0x1e
+	AF_UNIX                          = 0x1
+	AF_UNSPEC                        = 0x0
+	AF_VSOCK                         = 0x28
+	AF_WANPIPE                       = 0x19
+	AF_X25                           = 0x9
+	ARPHRD_6LOWPAN                   = 0x339
+	ARPHRD_ADAPT                     = 0x108
+	ARPHRD_APPLETLK                  = 0x8
+	ARPHRD_ARCNET                    = 0x7
+	ARPHRD_ASH                       = 0x30d
+	ARPHRD_ATM                       = 0x13
+	ARPHRD_AX25                      = 0x3
+	ARPHRD_BIF                       = 0x307
+	ARPHRD_CAIF                      = 0x336
+	ARPHRD_CAN                       = 0x118
+	ARPHRD_CHAOS                     = 0x5
+	ARPHRD_CISCO                     = 0x201
+	ARPHRD_CSLIP                     = 0x101
+	ARPHRD_CSLIP6                    = 0x103
+	ARPHRD_DDCMP                     = 0x205
+	ARPHRD_DLCI                      = 0xf
+	ARPHRD_ECONET                    = 0x30e
+	ARPHRD_EETHER                    = 0x2
+	ARPHRD_ETHER                     = 0x1
+	ARPHRD_EUI64                     = 0x1b
+	ARPHRD_FCAL                      = 0x311
+	ARPHRD_FCFABRIC                  = 0x313
+	ARPHRD_FCPL                      = 0x312
+	ARPHRD_FCPP                      = 0x310
+	ARPHRD_FDDI                      = 0x306
+	ARPHRD_FRAD                      = 0x302
+	ARPHRD_HDLC                      = 0x201
+	ARPHRD_HIPPI                     = 0x30c
+	ARPHRD_HWX25                     = 0x110
+	ARPHRD_IEEE1394                  = 0x18
+	ARPHRD_IEEE802                   = 0x6
+	ARPHRD_IEEE80211                 = 0x321
+	ARPHRD_IEEE80211_PRISM           = 0x322
+	ARPHRD_IEEE80211_RADIOTAP        = 0x323
+	ARPHRD_IEEE802154                = 0x324
+	ARPHRD_IEEE802154_MONITOR        = 0x325
+	ARPHRD_IEEE802_TR                = 0x320
+	ARPHRD_INFINIBAND                = 0x20
+	ARPHRD_IP6GRE                    = 0x337
+	ARPHRD_IPDDP                     = 0x309
+	ARPHRD_IPGRE                     = 0x30a
+	ARPHRD_IRDA                      = 0x30f
+	ARPHRD_LAPB                      = 0x204
+	ARPHRD_LOCALTLK                  = 0x305
+	ARPHRD_LOOPBACK                  = 0x304
+	ARPHRD_METRICOM                  = 0x17
+	ARPHRD_NETLINK                   = 0x338
+	ARPHRD_NETROM                    = 0x0
+	ARPHRD_NONE                      = 0xfffe
+	ARPHRD_PHONET                    = 0x334
+	ARPHRD_PHONET_PIPE               = 0x335
+	ARPHRD_PIMREG                    = 0x30b
+	ARPHRD_PPP                       = 0x200
+	ARPHRD_PRONET                    = 0x4
+	ARPHRD_RAWHDLC                   = 0x206
+	ARPHRD_ROSE                      = 0x10e
+	ARPHRD_RSRVD                     = 0x104
+	ARPHRD_SIT                       = 0x308
+	ARPHRD_SKIP                      = 0x303
+	ARPHRD_SLIP                      = 0x100
+	ARPHRD_SLIP6                     = 0x102
+	ARPHRD_TUNNEL                    = 0x300
+	ARPHRD_TUNNEL6                   = 0x301
+	ARPHRD_VOID                      = 0xffff
+	ARPHRD_X25                       = 0x10f
+	B0                               = 0x0
+	B1000000                         = 0x1008
+	B110                             = 0x3
+	B115200                          = 0x1002
+	B1152000                         = 0x1009
+	B1200                            = 0x9
+	B134                             = 0x4
+	B150                             = 0x5
+	B1500000                         = 0x100a
+	B1800                            = 0xa
+	B19200                           = 0xe
+	B200                             = 0x6
+	B2000000                         = 0x100b
+	B230400                          = 0x1003
+	B2400                            = 0xb
+	B2500000                         = 0x100c
+	B300                             = 0x7
+	B3000000                         = 0x100d
+	B3500000                         = 0x100e
+	B38400                           = 0xf
+	B4000000                         = 0x100f
+	B460800                          = 0x1004
+	B4800                            = 0xc
+	B50                              = 0x1
+	B500000                          = 0x1005
+	B57600                           = 0x1001
+	B576000                          = 0x1006
+	B600                             = 0x8
+	B75                              = 0x2
+	B921600                          = 0x1007
+	B9600                            = 0xd
+	BPF_A                            = 0x10
+	BPF_ABS                          = 0x20
+	BPF_ADD                          = 0x0
+	BPF_ALU                          = 0x4
+	BPF_AND                          = 0x50
+	BPF_B                            = 0x10
+	BPF_DIV                          = 0x30
+	BPF_H                            = 0x8
+	BPF_IMM                          = 0x0
+	BPF_IND                          = 0x40
+	BPF_JA                           = 0x0
+	BPF_JEQ                          = 0x10
+	BPF_JGE                          = 0x30
+	BPF_JGT                          = 0x20
+	BPF_JMP                          = 0x5
+	BPF_JSET                         = 0x40
+	BPF_K                            = 0x0
+	BPF_LD                           = 0x0
+	BPF_LDX                          = 0x1
+	BPF_LEN                          = 0x80
+	BPF_LSH                          = 0x60
+	BPF_MAJOR_VERSION                = 0x1
+	BPF_MAXINSNS                     = 0x1000
+	BPF_MEM                          = 0x60
+	BPF_MEMWORDS                     = 0x10
+	BPF_MINOR_VERSION                = 0x1
+	BPF_MISC                         = 0x7
+	BPF_MOD                          = 0x90
+	BPF_MSH                          = 0xa0
+	BPF_MUL                          = 0x20
+	BPF_NEG                          = 0x80
+	BPF_OR                           = 0x40
+	BPF_RET                          = 0x6
+	BPF_RSH                          = 0x70
+	BPF_ST                           = 0x2
+	BPF_STX                          = 0x3
+	BPF_SUB                          = 0x10
+	BPF_TAX                          = 0x0
+	BPF_TXA                          = 0x80
+	BPF_W                            = 0x0
+	BPF_X                            = 0x8
+	BPF_XOR                          = 0xa0
+	BRKINT                           = 0x2
+	CFLUSH                           = 0xf
+	CLOCAL                           = 0x800
+	CLONE_CHILD_CLEARTID             = 0x200000
+	CLONE_CHILD_SETTID               = 0x1000000
+	CLONE_DETACHED                   = 0x400000
+	CLONE_FILES                      = 0x400
+	CLONE_FS                         = 0x200
+	CLONE_IO                         = 0x80000000
+	CLONE_NEWIPC                     = 0x8000000
+	CLONE_NEWNET                     = 0x40000000
+	CLONE_NEWNS                      = 0x20000
+	CLONE_NEWPID                     = 0x20000000
+	CLONE_NEWUSER                    = 0x10000000
+	CLONE_NEWUTS                     = 0x4000000
+	CLONE_PARENT                     = 0x8000
+	CLONE_PARENT_SETTID              = 0x100000
+	CLONE_PTRACE                     = 0x2000
+	CLONE_SETTLS                     = 0x80000
+	CLONE_SIGHAND                    = 0x800
+	CLONE_SYSVSEM                    = 0x40000
+	CLONE_THREAD                     = 0x10000
+	CLONE_UNTRACED                   = 0x800000
+	CLONE_VFORK                      = 0x4000
+	CLONE_VM                         = 0x100
+	CREAD                            = 0x80
+	CS5                              = 0x0
+	CS6                              = 0x10
+	CS7                              = 0x20
+	CS8                              = 0x30
+	CSIGNAL                          = 0xff
+	CSIZE                            = 0x30
+	CSTART                           = 0x11
+	CSTATUS                          = 0x0
+	CSTOP                            = 0x13
+	CSTOPB                           = 0x40
+	CSUSP                            = 0x1a
+	DT_BLK                           = 0x6
+	DT_CHR                           = 0x2
+	DT_DIR                           = 0x4
+	DT_FIFO                          = 0x1
+	DT_LNK                           = 0xa
+	DT_REG                           = 0x8
+	DT_SOCK                          = 0xc
+	DT_UNKNOWN                       = 0x0
+	DT_WHT                           = 0xe
+	ECHO                             = 0x8
+	ECHOCTL                          = 0x200
+	ECHOE                            = 0x10
+	ECHOK                            = 0x20
+	ECHOKE                           = 0x800
+	ECHONL                           = 0x40
+	ECHOPRT                          = 0x400
+	ENCODING_DEFAULT                 = 0x0
+	ENCODING_FM_MARK                 = 0x3
+	ENCODING_FM_SPACE                = 0x4
+	ENCODING_MANCHESTER              = 0x5
+	ENCODING_NRZ                     = 0x1
+	ENCODING_NRZI                    = 0x2
+	EPOLLERR                         = 0x8
+	EPOLLET                          = 0x80000000
+	EPOLLHUP                         = 0x10
+	EPOLLIN                          = 0x1
+	EPOLLMSG                         = 0x400
+	EPOLLONESHOT                     = 0x40000000
+	EPOLLOUT                         = 0x4
+	EPOLLPRI                         = 0x2
+	EPOLLRDBAND                      = 0x80
+	EPOLLRDHUP                       = 0x2000
+	EPOLLRDNORM                      = 0x40
+	EPOLLWAKEUP                      = 0x20000000
+	EPOLLWRBAND                      = 0x200
+	EPOLLWRNORM                      = 0x100
+	EPOLL_CLOEXEC                    = 0x80000
+	EPOLL_CTL_ADD                    = 0x1
+	EPOLL_CTL_DEL                    = 0x2
+	EPOLL_CTL_MOD                    = 0x3
+	ETH_P_1588                       = 0x88f7
+	ETH_P_8021AD                     = 0x88a8
+	ETH_P_8021AH                     = 0x88e7
+	ETH_P_8021Q                      = 0x8100
+	ETH_P_80221                      = 0x8917
+	ETH_P_802_2                      = 0x4
+	ETH_P_802_3                      = 0x1
+	ETH_P_802_3_MIN                  = 0x600
+	ETH_P_802_EX1                    = 0x88b5
+	ETH_P_AARP                       = 0x80f3
+	ETH_P_AF_IUCV                    = 0xfbfb
+	ETH_P_ALL                        = 0x3
+	ETH_P_AOE                        = 0x88a2
+	ETH_P_ARCNET                     = 0x1a
+	ETH_P_ARP                        = 0x806
+	ETH_P_ATALK                      = 0x809b
+	ETH_P_ATMFATE                    = 0x8884
+	ETH_P_ATMMPOA                    = 0x884c
+	ETH_P_AX25                       = 0x2
+	ETH_P_BATMAN                     = 0x4305
+	ETH_P_BPQ                        = 0x8ff
+	ETH_P_CAIF                       = 0xf7
+	ETH_P_CAN                        = 0xc
+	ETH_P_CANFD                      = 0xd
+	ETH_P_CONTROL                    = 0x16
+	ETH_P_CUST                       = 0x6006
+	ETH_P_DDCMP                      = 0x6
+	ETH_P_DEC                        = 0x6000
+	ETH_P_DIAG                       = 0x6005
+	ETH_P_DNA_DL                     = 0x6001
+	ETH_P_DNA_RC                     = 0x6002
+	ETH_P_DNA_RT                     = 0x6003
+	ETH_P_DSA                        = 0x1b
+	ETH_P_ECONET                     = 0x18
+	ETH_P_EDSA                       = 0xdada
+	ETH_P_FCOE                       = 0x8906
+	ETH_P_FIP                        = 0x8914
+	ETH_P_HDLC                       = 0x19
+	ETH_P_IEEE802154                 = 0xf6
+	ETH_P_IEEEPUP                    = 0xa00
+	ETH_P_IEEEPUPAT                  = 0xa01
+	ETH_P_IP                         = 0x800
+	ETH_P_IPV6                       = 0x86dd
+	ETH_P_IPX                        = 0x8137
+	ETH_P_IRDA                       = 0x17
+	ETH_P_LAT                        = 0x6004
+	ETH_P_LINK_CTL                   = 0x886c
+	ETH_P_LOCALTALK                  = 0x9
+	ETH_P_LOOP                       = 0x60
+	ETH_P_LOOPBACK                   = 0x9000
+	ETH_P_MOBITEX                    = 0x15
+	ETH_P_MPLS_MC                    = 0x8848
+	ETH_P_MPLS_UC                    = 0x8847
+	ETH_P_MVRP                       = 0x88f5
+	ETH_P_PAE                        = 0x888e
+	ETH_P_PAUSE                      = 0x8808
+	ETH_P_PHONET                     = 0xf5
+	ETH_P_PPPTALK                    = 0x10
+	ETH_P_PPP_DISC                   = 0x8863
+	ETH_P_PPP_MP                     = 0x8
+	ETH_P_PPP_SES                    = 0x8864
+	ETH_P_PRP                        = 0x88fb
+	ETH_P_PUP                        = 0x200
+	ETH_P_PUPAT                      = 0x201
+	ETH_P_QINQ1                      = 0x9100
+	ETH_P_QINQ2                      = 0x9200
+	ETH_P_QINQ3                      = 0x9300
+	ETH_P_RARP                       = 0x8035
+	ETH_P_SCA                        = 0x6007
+	ETH_P_SLOW                       = 0x8809
+	ETH_P_SNAP                       = 0x5
+	ETH_P_TDLS                       = 0x890d
+	ETH_P_TEB                        = 0x6558
+	ETH_P_TIPC                       = 0x88ca
+	ETH_P_TRAILER                    = 0x1c
+	ETH_P_TR_802_2                   = 0x11
+	ETH_P_WAN_PPP                    = 0x7
+	ETH_P_WCCP                       = 0x883e
+	ETH_P_X25                        = 0x805
+	EXTA                             = 0xe
+	EXTB                             = 0xf
+	EXTPROC                          = 0x10000
+	FD_CLOEXEC                       = 0x1
+	FD_SETSIZE                       = 0x400
+	FLUSHO                           = 0x2000
+	F_DUPFD                          = 0x0
+	F_DUPFD_CLOEXEC                  = 0x406
+	F_EXLCK                          = 0x4
+	F_GETFD                          = 0x1
+	F_GETFL                          = 0x3
+	F_GETLEASE                       = 0x401
+	F_GETLK                          = 0x21
+	F_GETLK64                        = 0x21
+	F_GETOWN                         = 0x17
+	F_GETOWN_EX                      = 0x10
+	F_GETPIPE_SZ                     = 0x408
+	F_GETSIG                         = 0xb
+	F_LOCK                           = 0x1
+	F_NOTIFY                         = 0x402
+	F_OK                             = 0x0
+	F_RDLCK                          = 0x0
+	F_SETFD                          = 0x2
+	F_SETFL                          = 0x4
+	F_SETLEASE                       = 0x400
+	F_SETLK                          = 0x22
+	F_SETLK64                        = 0x22
+	F_SETLKW                         = 0x23
+	F_SETLKW64                       = 0x23
+	F_SETOWN                         = 0x18
+	F_SETOWN_EX                      = 0xf
+	F_SETPIPE_SZ                     = 0x407
+	F_SETSIG                         = 0xa
+	F_SHLCK                          = 0x8
+	F_TEST                           = 0x3
+	F_TLOCK                          = 0x2
+	F_ULOCK                          = 0x0
+	F_UNLCK                          = 0x2
+	F_WRLCK                          = 0x1
+	HUPCL                            = 0x400
+	ICANON                           = 0x2
+	ICMPV6_FILTER                    = 0x1
+	ICRNL                            = 0x100
+	IEXTEN                           = 0x100
+	IFA_F_DADFAILED                  = 0x8
+	IFA_F_DEPRECATED                 = 0x20
+	IFA_F_HOMEADDRESS                = 0x10
+	IFA_F_MANAGETEMPADDR             = 0x100
+	IFA_F_NODAD                      = 0x2
+	IFA_F_NOPREFIXROUTE              = 0x200
+	IFA_F_OPTIMISTIC                 = 0x4
+	IFA_F_PERMANENT                  = 0x80
+	IFA_F_SECONDARY                  = 0x1
+	IFA_F_TEMPORARY                  = 0x1
+	IFA_F_TENTATIVE                  = 0x40
+	IFA_MAX                          = 0x8
+	IFF_ALLMULTI                     = 0x200
+	IFF_ATTACH_QUEUE                 = 0x200
+	IFF_AUTOMEDIA                    = 0x4000
+	IFF_BROADCAST                    = 0x2
+	IFF_DEBUG                        = 0x4
+	IFF_DETACH_QUEUE                 = 0x400
+	IFF_DORMANT                      = 0x20000
+	IFF_DYNAMIC                      = 0x8000
+	IFF_ECHO                         = 0x40000
+	IFF_LOOPBACK                     = 0x8
+	IFF_LOWER_UP                     = 0x10000
+	IFF_MASTER                       = 0x400
+	IFF_MULTICAST                    = 0x1000
+	IFF_MULTI_QUEUE                  = 0x100
+	IFF_NOARP                        = 0x80
+	IFF_NOFILTER                     = 0x1000
+	IFF_NOTRAILERS                   = 0x20
+	IFF_NO_PI                        = 0x1000
+	IFF_ONE_QUEUE                    = 0x2000
+	IFF_PERSIST                      = 0x800
+	IFF_POINTOPOINT                  = 0x10
+	IFF_PORTSEL                      = 0x2000
+	IFF_PROMISC                      = 0x100
+	IFF_RUNNING                      = 0x40
+	IFF_SLAVE                        = 0x800
+	IFF_TAP                          = 0x2
+	IFF_TUN                          = 0x1
+	IFF_TUN_EXCL                     = 0x8000
+	IFF_UP                           = 0x1
+	IFF_VNET_HDR                     = 0x4000
+	IFF_VOLATILE                     = 0x70c5a
+	IFNAMSIZ                         = 0x10
+	IGNBRK                           = 0x1
+	IGNCR                            = 0x80
+	IGNPAR                           = 0x4
+	IMAXBEL                          = 0x2000
+	INLCR                            = 0x40
+	INPCK                            = 0x10
+	IN_ACCESS                        = 0x1
+	IN_ALL_EVENTS                    = 0xfff
+	IN_ATTRIB                        = 0x4
+	IN_CLASSA_HOST                   = 0xffffff
+	IN_CLASSA_MAX                    = 0x80
+	IN_CLASSA_NET                    = 0xff000000
+	IN_CLASSA_NSHIFT                 = 0x18
+	IN_CLASSB_HOST                   = 0xffff
+	IN_CLASSB_MAX                    = 0x10000
+	IN_CLASSB_NET                    = 0xffff0000
+	IN_CLASSB_NSHIFT                 = 0x10
+	IN_CLASSC_HOST                   = 0xff
+	IN_CLASSC_NET                    = 0xffffff00
+	IN_CLASSC_NSHIFT                 = 0x8
+	IN_CLOEXEC                       = 0x80000
+	IN_CLOSE                         = 0x18
+	IN_CLOSE_NOWRITE                 = 0x10
+	IN_CLOSE_WRITE                   = 0x8
+	IN_CREATE                        = 0x100
+	IN_DELETE                        = 0x200
+	IN_DELETE_SELF                   = 0x400
+	IN_DONT_FOLLOW                   = 0x2000000
+	IN_EXCL_UNLINK                   = 0x4000000
+	IN_IGNORED                       = 0x8000
+	IN_ISDIR                         = 0x40000000
+	IN_LOOPBACKNET                   = 0x7f
+	IN_MASK_ADD                      = 0x20000000
+	IN_MODIFY                        = 0x2
+	IN_MOVE                          = 0xc0
+	IN_MOVED_FROM                    = 0x40
+	IN_MOVED_TO                      = 0x80
+	IN_MOVE_SELF                     = 0x800
+	IN_NONBLOCK                      = 0x80
+	IN_ONESHOT                       = 0x80000000
+	IN_ONLYDIR                       = 0x1000000
+	IN_OPEN                          = 0x20
+	IN_Q_OVERFLOW                    = 0x4000
+	IN_UNMOUNT                       = 0x2000
+	IPPROTO_AH                       = 0x33
+	IPPROTO_BEETPH                   = 0x5e
+	IPPROTO_COMP                     = 0x6c
+	IPPROTO_DCCP                     = 0x21
+	IPPROTO_DSTOPTS                  = 0x3c
+	IPPROTO_EGP                      = 0x8
+	IPPROTO_ENCAP                    = 0x62
+	IPPROTO_ESP                      = 0x32
+	IPPROTO_FRAGMENT                 = 0x2c
+	IPPROTO_GRE                      = 0x2f
+	IPPROTO_HOPOPTS                  = 0x0
+	IPPROTO_ICMP                     = 0x1
+	IPPROTO_ICMPV6                   = 0x3a
+	IPPROTO_IDP                      = 0x16
+	IPPROTO_IGMP                     = 0x2
+	IPPROTO_IP                       = 0x0
+	IPPROTO_IPIP                     = 0x4
+	IPPROTO_IPV6                     = 0x29
+	IPPROTO_MH                       = 0x87
+	IPPROTO_MTP                      = 0x5c
+	IPPROTO_NONE                     = 0x3b
+	IPPROTO_PIM                      = 0x67
+	IPPROTO_PUP                      = 0xc
+	IPPROTO_RAW                      = 0xff
+	IPPROTO_ROUTING                  = 0x2b
+	IPPROTO_RSVP                     = 0x2e
+	IPPROTO_SCTP                     = 0x84
+	IPPROTO_TCP                      = 0x6
+	IPPROTO_TP                       = 0x1d
+	IPPROTO_UDP                      = 0x11
+	IPPROTO_UDPLITE                  = 0x88
+	IPV6_2292DSTOPTS                 = 0x4
+	IPV6_2292HOPLIMIT                = 0x8
+	IPV6_2292HOPOPTS                 = 0x3
+	IPV6_2292PKTINFO                 = 0x2
+	IPV6_2292PKTOPTIONS              = 0x6
+	IPV6_2292RTHDR                   = 0x5
+	IPV6_ADDRFORM                    = 0x1
+	IPV6_ADD_MEMBERSHIP              = 0x14
+	IPV6_AUTHHDR                     = 0xa
+	IPV6_CHECKSUM                    = 0x7
+	IPV6_DROP_MEMBERSHIP             = 0x15
+	IPV6_DSTOPTS                     = 0x3b
+	IPV6_HOPLIMIT                    = 0x34
+	IPV6_HOPOPTS                     = 0x36
+	IPV6_IPSEC_POLICY                = 0x22
+	IPV6_JOIN_ANYCAST                = 0x1b
+	IPV6_JOIN_GROUP                  = 0x14
+	IPV6_LEAVE_ANYCAST               = 0x1c
+	IPV6_LEAVE_GROUP                 = 0x15
+	IPV6_MTU                         = 0x18
+	IPV6_MTU_DISCOVER                = 0x17
+	IPV6_MULTICAST_HOPS              = 0x12
+	IPV6_MULTICAST_IF                = 0x11
+	IPV6_MULTICAST_LOOP              = 0x13
+	IPV6_NEXTHOP                     = 0x9
+	IPV6_PKTINFO                     = 0x32
+	IPV6_PMTUDISC_DO                 = 0x2
+	IPV6_PMTUDISC_DONT               = 0x0
+	IPV6_PMTUDISC_PROBE              = 0x3
+	IPV6_PMTUDISC_WANT               = 0x1
+	IPV6_RECVDSTOPTS                 = 0x3a
+	IPV6_RECVERR                     = 0x19
+	IPV6_RECVHOPLIMIT                = 0x33
+	IPV6_RECVHOPOPTS                 = 0x35
+	IPV6_RECVPKTINFO                 = 0x31
+	IPV6_RECVRTHDR                   = 0x38
+	IPV6_RECVTCLASS                  = 0x42
+	IPV6_ROUTER_ALERT                = 0x16
+	IPV6_RTHDR                       = 0x39
+	IPV6_RTHDRDSTOPTS                = 0x37
+	IPV6_RTHDR_LOOSE                 = 0x0
+	IPV6_RTHDR_STRICT                = 0x1
+	IPV6_RTHDR_TYPE_0                = 0x0
+	IPV6_RXDSTOPTS                   = 0x3b
+	IPV6_RXHOPOPTS                   = 0x36
+	IPV6_TCLASS                      = 0x43
+	IPV6_UNICAST_HOPS                = 0x10
+	IPV6_V6ONLY                      = 0x1a
+	IPV6_XFRM_POLICY                 = 0x23
+	IP_ADD_MEMBERSHIP                = 0x23
+	IP_ADD_SOURCE_MEMBERSHIP         = 0x27
+	IP_BLOCK_SOURCE                  = 0x26
+	IP_DEFAULT_MULTICAST_LOOP        = 0x1
+	IP_DEFAULT_MULTICAST_TTL         = 0x1
+	IP_DF                            = 0x4000
+	IP_DROP_MEMBERSHIP               = 0x24
+	IP_DROP_SOURCE_MEMBERSHIP        = 0x28
+	IP_FREEBIND                      = 0xf
+	IP_HDRINCL                       = 0x3
+	IP_IPSEC_POLICY                  = 0x10
+	IP_MAXPACKET                     = 0xffff
+	IP_MAX_MEMBERSHIPS               = 0x14
+	IP_MF                            = 0x2000
+	IP_MINTTL                        = 0x15
+	IP_MSFILTER                      = 0x29
+	IP_MSS                           = 0x240
+	IP_MTU                           = 0xe
+	IP_MTU_DISCOVER                  = 0xa
+	IP_MULTICAST_ALL                 = 0x31
+	IP_MULTICAST_IF                  = 0x20
+	IP_MULTICAST_LOOP                = 0x22
+	IP_MULTICAST_TTL                 = 0x21
+	IP_OFFMASK                       = 0x1fff
+	IP_OPTIONS                       = 0x4
+	IP_ORIGDSTADDR                   = 0x14
+	IP_PASSSEC                       = 0x12
+	IP_PKTINFO                       = 0x8
+	IP_PKTOPTIONS                    = 0x9
+	IP_PMTUDISC                      = 0xa
+	IP_PMTUDISC_DO                   = 0x2
+	IP_PMTUDISC_DONT                 = 0x0
+	IP_PMTUDISC_PROBE                = 0x3
+	IP_PMTUDISC_WANT                 = 0x1
+	IP_RECVERR                       = 0xb
+	IP_RECVOPTS                      = 0x6
+	IP_RECVORIGDSTADDR               = 0x14
+	IP_RECVRETOPTS                   = 0x7
+	IP_RECVTOS                       = 0xd
+	IP_RECVTTL                       = 0xc
+	IP_RETOPTS                       = 0x7
+	IP_RF                            = 0x8000
+	IP_ROUTER_ALERT                  = 0x5
+	IP_TOS                           = 0x1
+	IP_TRANSPARENT                   = 0x13
+	IP_TTL                           = 0x2
+	IP_UNBLOCK_SOURCE                = 0x25
+	IP_UNICAST_IF                    = 0x32
+	IP_XFRM_POLICY                   = 0x11
+	ISIG                             = 0x1
+	ISTRIP                           = 0x20
+	IUTF8                            = 0x4000
+	IXANY                            = 0x800
+	IXOFF                            = 0x1000
+	IXON                             = 0x400
+	LINUX_REBOOT_CMD_CAD_OFF         = 0x0
+	LINUX_REBOOT_CMD_CAD_ON          = 0x89abcdef
+	LINUX_REBOOT_CMD_HALT            = 0xcdef0123
+	LINUX_REBOOT_CMD_KEXEC           = 0x45584543
+	LINUX_REBOOT_CMD_POWER_OFF       = 0x4321fedc
+	LINUX_REBOOT_CMD_RESTART         = 0x1234567
+	LINUX_REBOOT_CMD_RESTART2        = 0xa1b2c3d4
+	LINUX_REBOOT_CMD_SW_SUSPEND      = 0xd000fce2
+	LINUX_REBOOT_MAGIC1              = 0xfee1dead
+	LINUX_REBOOT_MAGIC2              = 0x28121969
+	LOCK_EX                          = 0x2
+	LOCK_NB                          = 0x4
+	LOCK_SH                          = 0x1
+	LOCK_UN                          = 0x8
+	MADV_DODUMP                      = 0x11
+	MADV_DOFORK                      = 0xb
+	MADV_DONTDUMP                    = 0x10
+	MADV_DONTFORK                    = 0xa
+	MADV_DONTNEED                    = 0x4
+	MADV_HUGEPAGE                    = 0xe
+	MADV_HWPOISON                    = 0x64
+	MADV_MERGEABLE                   = 0xc
+	MADV_NOHUGEPAGE                  = 0xf
+	MADV_NORMAL                      = 0x0
+	MADV_RANDOM                      = 0x1
+	MADV_REMOVE                      = 0x9
+	MADV_SEQUENTIAL                  = 0x2
+	MADV_UNMERGEABLE                 = 0xd
+	MADV_WILLNEED                    = 0x3
+	MAP_ANON                         = 0x800
+	MAP_ANONYMOUS                    = 0x800
+	MAP_DENYWRITE                    = 0x2000
+	MAP_EXECUTABLE                   = 0x4000
+	MAP_FILE                         = 0x0
+	MAP_FIXED                        = 0x10
+	MAP_GROWSDOWN                    = 0x1000
+	MAP_HUGETLB                      = 0x80000
+	MAP_HUGE_MASK                    = 0x3f
+	MAP_HUGE_SHIFT                   = 0x1a
+	MAP_LOCKED                       = 0x8000
+	MAP_NONBLOCK                     = 0x20000
+	MAP_NORESERVE                    = 0x400
+	MAP_POPULATE                     = 0x10000
+	MAP_PRIVATE                      = 0x2
+	MAP_RENAME                       = 0x800
+	MAP_SHARED                       = 0x1
+	MAP_STACK                        = 0x40000
+	MAP_TYPE                         = 0xf
+	MCL_CURRENT                      = 0x1
+	MCL_FUTURE                       = 0x2
+	MNT_DETACH                       = 0x2
+	MNT_EXPIRE                       = 0x4
+	MNT_FORCE                        = 0x1
+	MSG_CMSG_CLOEXEC                 = 0x40000000
+	MSG_CONFIRM                      = 0x800
+	MSG_CTRUNC                       = 0x8
+	MSG_DONTROUTE                    = 0x4
+	MSG_DONTWAIT                     = 0x40
+	MSG_EOR                          = 0x80
+	MSG_ERRQUEUE                     = 0x2000
+	MSG_FASTOPEN                     = 0x20000000
+	MSG_FIN                          = 0x200
+	MSG_MORE                         = 0x8000
+	MSG_NOSIGNAL                     = 0x4000
+	MSG_OOB                          = 0x1
+	MSG_PEEK                         = 0x2
+	MSG_PROXY                        = 0x10
+	MSG_RST                          = 0x1000
+	MSG_SYN                          = 0x400
+	MSG_TRUNC                        = 0x20
+	MSG_TRYHARD                      = 0x4
+	MSG_WAITALL                      = 0x100
+	MSG_WAITFORONE                   = 0x10000
+	MS_ACTIVE                        = 0x40000000
+	MS_ASYNC                         = 0x1
+	MS_BIND                          = 0x1000
+	MS_DIRSYNC                       = 0x80
+	MS_INVALIDATE                    = 0x2
+	MS_I_VERSION                     = 0x800000
+	MS_KERNMOUNT                     = 0x400000
+	MS_MANDLOCK                      = 0x40
+	MS_MGC_MSK                       = 0xffff0000
+	MS_MGC_VAL                       = 0xc0ed0000
+	MS_MOVE                          = 0x2000
+	MS_NOATIME                       = 0x400
+	MS_NODEV                         = 0x4
+	MS_NODIRATIME                    = 0x800
+	MS_NOEXEC                        = 0x8
+	MS_NOSUID                        = 0x2
+	MS_NOUSER                        = -0x80000000
+	MS_POSIXACL                      = 0x10000
+	MS_PRIVATE                       = 0x40000
+	MS_RDONLY                        = 0x1
+	MS_REC                           = 0x4000
+	MS_RELATIME                      = 0x200000
+	MS_REMOUNT                       = 0x20
+	MS_RMT_MASK                      = 0x800051
+	MS_SHARED                        = 0x100000
+	MS_SILENT                        = 0x8000
+	MS_SLAVE                         = 0x80000
+	MS_STRICTATIME                   = 0x1000000
+	MS_SYNC                          = 0x4
+	MS_SYNCHRONOUS                   = 0x10
+	MS_UNBINDABLE                    = 0x20000
+	NAME_MAX                         = 0xff
+	NETLINK_ADD_MEMBERSHIP           = 0x1
+	NETLINK_AUDIT                    = 0x9
+	NETLINK_BROADCAST_ERROR          = 0x4
+	NETLINK_CONNECTOR                = 0xb
+	NETLINK_CRYPTO                   = 0x15
+	NETLINK_DNRTMSG                  = 0xe
+	NETLINK_DROP_MEMBERSHIP          = 0x2
+	NETLINK_ECRYPTFS                 = 0x13
+	NETLINK_FIB_LOOKUP               = 0xa
+	NETLINK_FIREWALL                 = 0x3
+	NETLINK_GENERIC                  = 0x10
+	NETLINK_INET_DIAG                = 0x4
+	NETLINK_IP6_FW                   = 0xd
+	NETLINK_ISCSI                    = 0x8
+	NETLINK_KOBJECT_UEVENT           = 0xf
+	NETLINK_NETFILTER                = 0xc
+	NETLINK_NFLOG                    = 0x5
+	NETLINK_NO_ENOBUFS               = 0x5
+	NETLINK_PKTINFO                  = 0x3
+	NETLINK_RDMA                     = 0x14
+	NETLINK_ROUTE                    = 0x0
+	NETLINK_RX_RING                  = 0x6
+	NETLINK_SCSITRANSPORT            = 0x12
+	NETLINK_SELINUX                  = 0x7
+	NETLINK_SOCK_DIAG                = 0x4
+	NETLINK_TX_RING                  = 0x7
+	NETLINK_UNUSED                   = 0x1
+	NETLINK_USERSOCK                 = 0x2
+	NETLINK_XFRM                     = 0x6
+	NLA_ALIGNTO                      = 0x4
+	NLA_F_NESTED                     = 0x8000
+	NLA_F_NET_BYTEORDER              = 0x4000
+	NLA_HDRLEN                       = 0x4
+	NLMSG_ALIGNTO                    = 0x4
+	NLMSG_DONE                       = 0x3
+	NLMSG_ERROR                      = 0x2
+	NLMSG_HDRLEN                     = 0x10
+	NLMSG_MIN_TYPE                   = 0x10
+	NLMSG_NOOP                       = 0x1
+	NLMSG_OVERRUN                    = 0x4
+	NLM_F_ACK                        = 0x4
+	NLM_F_APPEND                     = 0x800
+	NLM_F_ATOMIC                     = 0x400
+	NLM_F_CREATE                     = 0x400
+	NLM_F_DUMP                       = 0x300
+	NLM_F_DUMP_INTR                  = 0x10
+	NLM_F_ECHO                       = 0x8
+	NLM_F_EXCL                       = 0x200
+	NLM_F_MATCH                      = 0x200
+	NLM_F_MULTI                      = 0x2
+	NLM_F_REPLACE                    = 0x100
+	NLM_F_REQUEST                    = 0x1
+	NLM_F_ROOT                       = 0x100
+	NOFLSH                           = 0x80
+	OCRNL                            = 0x8
+	OFDEL                            = 0x80
+	OFILL                            = 0x40
+	ONLCR                            = 0x4
+	ONLRET                           = 0x20
+	ONOCR                            = 0x10
+	OPOST                            = 0x1
+	O_ACCMODE                        = 0x3
+	O_APPEND                         = 0x8
+	O_ASYNC                          = 0x1000
+	O_CLOEXEC                        = 0x80000
+	O_CREAT                          = 0x100
+	O_DIRECT                         = 0x8000
+	O_DIRECTORY                      = 0x10000
+	O_DSYNC                          = 0x10
+	O_EXCL                           = 0x400
+	O_FSYNC                          = 0x4010
+	O_LARGEFILE                      = 0x2000
+	O_NDELAY                         = 0x80
+	O_NOATIME                        = 0x40000
+	O_NOCTTY                         = 0x800
+	O_NOFOLLOW                       = 0x20000
+	O_NONBLOCK                       = 0x80
+	O_PATH                           = 0x200000
+	O_RDONLY                         = 0x0
+	O_RDWR                           = 0x2
+	O_RSYNC                          = 0x4010
+	O_SYNC                           = 0x4010
+	O_TMPFILE                        = 0x410000
+	O_TRUNC                          = 0x200
+	O_WRONLY                         = 0x1
+	PACKET_ADD_MEMBERSHIP            = 0x1
+	PACKET_AUXDATA                   = 0x8
+	PACKET_BROADCAST                 = 0x1
+	PACKET_COPY_THRESH               = 0x7
+	PACKET_DROP_MEMBERSHIP           = 0x2
+	PACKET_FANOUT                    = 0x12
+	PACKET_FANOUT_CPU                = 0x2
+	PACKET_FANOUT_FLAG_DEFRAG        = 0x8000
+	PACKET_FANOUT_FLAG_ROLLOVER      = 0x1000
+	PACKET_FANOUT_HASH               = 0x0
+	PACKET_FANOUT_LB                 = 0x1
+	PACKET_FANOUT_QM                 = 0x5
+	PACKET_FANOUT_RND                = 0x4
+	PACKET_FANOUT_ROLLOVER           = 0x3
+	PACKET_FASTROUTE                 = 0x6
+	PACKET_HDRLEN                    = 0xb
+	PACKET_HOST                      = 0x0
+	PACKET_KERNEL                    = 0x7
+	PACKET_LOOPBACK                  = 0x5
+	PACKET_LOSS                      = 0xe
+	PACKET_MR_ALLMULTI               = 0x2
+	PACKET_MR_MULTICAST              = 0x0
+	PACKET_MR_PROMISC                = 0x1
+	PACKET_MR_UNICAST                = 0x3
+	PACKET_MULTICAST                 = 0x2
+	PACKET_ORIGDEV                   = 0x9
+	PACKET_OTHERHOST                 = 0x3
+	PACKET_OUTGOING                  = 0x4
+	PACKET_QDISC_BYPASS              = 0x14
+	PACKET_RECV_OUTPUT               = 0x3
+	PACKET_RESERVE                   = 0xc
+	PACKET_RX_RING                   = 0x5
+	PACKET_STATISTICS                = 0x6
+	PACKET_TIMESTAMP                 = 0x11
+	PACKET_TX_HAS_OFF                = 0x13
+	PACKET_TX_RING                   = 0xd
+	PACKET_TX_TIMESTAMP              = 0x10
+	PACKET_USER                      = 0x6
+	PACKET_VERSION                   = 0xa
+	PACKET_VNET_HDR                  = 0xf
+	PARENB                           = 0x100
+	PARITY_CRC16_PR0                 = 0x2
+	PARITY_CRC16_PR0_CCITT           = 0x4
+	PARITY_CRC16_PR1                 = 0x3
+	PARITY_CRC16_PR1_CCITT           = 0x5
+	PARITY_CRC32_PR0_CCITT           = 0x6
+	PARITY_CRC32_PR1_CCITT           = 0x7
+	PARITY_DEFAULT                   = 0x0
+	PARITY_NONE                      = 0x1
+	PARMRK                           = 0x8
+	PARODD                           = 0x200
+	PENDIN                           = 0x4000
+	PRIO_PGRP                        = 0x1
+	PRIO_PROCESS                     = 0x0
+	PRIO_USER                        = 0x2
+	PROT_EXEC                        = 0x4
+	PROT_GROWSDOWN                   = 0x1000000
+	PROT_GROWSUP                     = 0x2000000
+	PROT_NONE                        = 0x0
+	PROT_READ                        = 0x1
+	PROT_WRITE                       = 0x2
+	PR_CAPBSET_DROP                  = 0x18
+	PR_CAPBSET_READ                  = 0x17
+	PR_ENDIAN_BIG                    = 0x0
+	PR_ENDIAN_LITTLE                 = 0x1
+	PR_ENDIAN_PPC_LITTLE             = 0x2
+	PR_FPEMU_NOPRINT                 = 0x1
+	PR_FPEMU_SIGFPE                  = 0x2
+	PR_FP_EXC_ASYNC                  = 0x2
+	PR_FP_EXC_DISABLED               = 0x0
+	PR_FP_EXC_DIV                    = 0x10000
+	PR_FP_EXC_INV                    = 0x100000
+	PR_FP_EXC_NONRECOV               = 0x1
+	PR_FP_EXC_OVF                    = 0x20000
+	PR_FP_EXC_PRECISE                = 0x3
+	PR_FP_EXC_RES                    = 0x80000
+	PR_FP_EXC_SW_ENABLE              = 0x80
+	PR_FP_EXC_UND                    = 0x40000
+	PR_GET_CHILD_SUBREAPER           = 0x25
+	PR_GET_DUMPABLE                  = 0x3
+	PR_GET_ENDIAN                    = 0x13
+	PR_GET_FPEMU                     = 0x9
+	PR_GET_FPEXC                     = 0xb
+	PR_GET_KEEPCAPS                  = 0x7
+	PR_GET_NAME                      = 0x10
+	PR_GET_NO_NEW_PRIVS              = 0x27
+	PR_GET_PDEATHSIG                 = 0x2
+	PR_GET_SECCOMP                   = 0x15
+	PR_GET_SECUREBITS                = 0x1b
+	PR_GET_THP_DISABLE               = 0x2a
+	PR_GET_TID_ADDRESS               = 0x28
+	PR_GET_TIMERSLACK                = 0x1e
+	PR_GET_TIMING                    = 0xd
+	PR_GET_TSC                       = 0x19
+	PR_GET_UNALIGN                   = 0x5
+	PR_MCE_KILL                      = 0x21
+	PR_MCE_KILL_CLEAR                = 0x0
+	PR_MCE_KILL_DEFAULT              = 0x2
+	PR_MCE_KILL_EARLY                = 0x1
+	PR_MCE_KILL_GET                  = 0x22
+	PR_MCE_KILL_LATE                 = 0x0
+	PR_MCE_KILL_SET                  = 0x1
+	PR_SET_CHILD_SUBREAPER           = 0x24
+	PR_SET_DUMPABLE                  = 0x4
+	PR_SET_ENDIAN                    = 0x14
+	PR_SET_FPEMU                     = 0xa
+	PR_SET_FPEXC                     = 0xc
+	PR_SET_KEEPCAPS                  = 0x8
+	PR_SET_MM                        = 0x23
+	PR_SET_MM_ARG_END                = 0x9
+	PR_SET_MM_ARG_START              = 0x8
+	PR_SET_MM_AUXV                   = 0xc
+	PR_SET_MM_BRK                    = 0x7
+	PR_SET_MM_END_CODE               = 0x2
+	PR_SET_MM_END_DATA               = 0x4
+	PR_SET_MM_ENV_END                = 0xb
+	PR_SET_MM_ENV_START              = 0xa
+	PR_SET_MM_EXE_FILE               = 0xd
+	PR_SET_MM_START_BRK              = 0x6
+	PR_SET_MM_START_CODE             = 0x1
+	PR_SET_MM_START_DATA             = 0x3
+	PR_SET_MM_START_STACK            = 0x5
+	PR_SET_NAME                      = 0xf
+	PR_SET_NO_NEW_PRIVS              = 0x26
+	PR_SET_PDEATHSIG                 = 0x1
+	PR_SET_PTRACER                   = 0x59616d61
+	PR_SET_PTRACER_ANY               = 0xffffffff
+	PR_SET_SECCOMP                   = 0x16
+	PR_SET_SECUREBITS                = 0x1c
+	PR_SET_THP_DISABLE               = 0x29
+	PR_SET_TIMERSLACK                = 0x1d
+	PR_SET_TIMING                    = 0xe
+	PR_SET_TSC                       = 0x1a
+	PR_SET_UNALIGN                   = 0x6
+	PR_TASK_PERF_EVENTS_DISABLE      = 0x1f
+	PR_TASK_PERF_EVENTS_ENABLE       = 0x20
+	PR_TIMING_STATISTICAL            = 0x0
+	PR_TIMING_TIMESTAMP              = 0x1
+	PR_TSC_ENABLE                    = 0x1
+	PR_TSC_SIGSEGV                   = 0x2
+	PR_UNALIGN_NOPRINT               = 0x1
+	PR_UNALIGN_SIGBUS                = 0x2
+	PTRACE_ATTACH                    = 0x10
+	PTRACE_CONT                      = 0x7
+	PTRACE_DETACH                    = 0x11
+	PTRACE_EVENT_CLONE               = 0x3
+	PTRACE_EVENT_EXEC                = 0x4
+	PTRACE_EVENT_EXIT                = 0x6
+	PTRACE_EVENT_FORK                = 0x1
+	PTRACE_EVENT_SECCOMP             = 0x7
+	PTRACE_EVENT_STOP                = 0x80
+	PTRACE_EVENT_VFORK               = 0x2
+	PTRACE_EVENT_VFORK_DONE          = 0x5
+	PTRACE_GETEVENTMSG               = 0x4201
+	PTRACE_GETFPREGS                 = 0xe
+	PTRACE_GETREGS                   = 0xc
+	PTRACE_GETREGSET                 = 0x4204
+	PTRACE_GETSIGINFO                = 0x4202
+	PTRACE_GETSIGMASK                = 0x420a
+	PTRACE_GET_THREAD_AREA           = 0x19
+	PTRACE_GET_THREAD_AREA_3264      = 0xc4
+	PTRACE_GET_WATCH_REGS            = 0xd0
+	PTRACE_INTERRUPT                 = 0x4207
+	PTRACE_KILL                      = 0x8
+	PTRACE_LISTEN                    = 0x4208
+	PTRACE_OLDSETOPTIONS             = 0x15
+	PTRACE_O_EXITKILL                = 0x100000
+	PTRACE_O_MASK                    = 0x1000ff
+	PTRACE_O_TRACECLONE              = 0x8
+	PTRACE_O_TRACEEXEC               = 0x10
+	PTRACE_O_TRACEEXIT               = 0x40
+	PTRACE_O_TRACEFORK               = 0x2
+	PTRACE_O_TRACESECCOMP            = 0x80
+	PTRACE_O_TRACESYSGOOD            = 0x1
+	PTRACE_O_TRACEVFORK              = 0x4
+	PTRACE_O_TRACEVFORKDONE          = 0x20
+	PTRACE_PEEKDATA                  = 0x2
+	PTRACE_PEEKDATA_3264             = 0xc1
+	PTRACE_PEEKSIGINFO               = 0x4209
+	PTRACE_PEEKSIGINFO_SHARED        = 0x1
+	PTRACE_PEEKTEXT                  = 0x1
+	PTRACE_PEEKTEXT_3264             = 0xc0
+	PTRACE_PEEKUSR                   = 0x3
+	PTRACE_POKEDATA                  = 0x5
+	PTRACE_POKEDATA_3264             = 0xc3
+	PTRACE_POKETEXT                  = 0x4
+	PTRACE_POKETEXT_3264             = 0xc2
+	PTRACE_POKEUSR                   = 0x6
+	PTRACE_SEIZE                     = 0x4206
+	PTRACE_SETFPREGS                 = 0xf
+	PTRACE_SETOPTIONS                = 0x4200
+	PTRACE_SETREGS                   = 0xd
+	PTRACE_SETREGSET                 = 0x4205
+	PTRACE_SETSIGINFO                = 0x4203
+	PTRACE_SETSIGMASK                = 0x420b
+	PTRACE_SET_THREAD_AREA           = 0x1a
+	PTRACE_SET_WATCH_REGS            = 0xd1
+	PTRACE_SINGLESTEP                = 0x9
+	PTRACE_SYSCALL                   = 0x18
+	PTRACE_TRACEME                   = 0x0
+	RLIMIT_AS                        = 0x6
+	RLIMIT_CORE                      = 0x4
+	RLIMIT_CPU                       = 0x0
+	RLIMIT_DATA                      = 0x2
+	RLIMIT_FSIZE                     = 0x1
+	RLIMIT_NOFILE                    = 0x5
+	RLIMIT_STACK                     = 0x3
+	RLIM_INFINITY                    = -0x1
+	RTAX_ADVMSS                      = 0x8
+	RTAX_CWND                        = 0x7
+	RTAX_FEATURES                    = 0xc
+	RTAX_FEATURE_ALLFRAG             = 0x8
+	RTAX_FEATURE_ECN                 = 0x1
+	RTAX_FEATURE_SACK                = 0x2
+	RTAX_FEATURE_TIMESTAMP           = 0x4
+	RTAX_HOPLIMIT                    = 0xa
+	RTAX_INITCWND                    = 0xb
+	RTAX_INITRWND                    = 0xe
+	RTAX_LOCK                        = 0x1
+	RTAX_MAX                         = 0xf
+	RTAX_MTU                         = 0x2
+	RTAX_QUICKACK                    = 0xf
+	RTAX_REORDERING                  = 0x9
+	RTAX_RTO_MIN                     = 0xd
+	RTAX_RTT                         = 0x4
+	RTAX_RTTVAR                      = 0x5
+	RTAX_SSTHRESH                    = 0x6
+	RTAX_UNSPEC                      = 0x0
+	RTAX_WINDOW                      = 0x3
+	RTA_ALIGNTO                      = 0x4
+	RTA_MAX                          = 0x11
+	RTCF_DIRECTSRC                   = 0x4000000
+	RTCF_DOREDIRECT                  = 0x1000000
+	RTCF_LOG                         = 0x2000000
+	RTCF_MASQ                        = 0x400000
+	RTCF_NAT                         = 0x800000
+	RTCF_VALVE                       = 0x200000
+	RTF_ADDRCLASSMASK                = 0xf8000000
+	RTF_ADDRCONF                     = 0x40000
+	RTF_ALLONLINK                    = 0x20000
+	RTF_BROADCAST                    = 0x10000000
+	RTF_CACHE                        = 0x1000000
+	RTF_DEFAULT                      = 0x10000
+	RTF_DYNAMIC                      = 0x10
+	RTF_FLOW                         = 0x2000000
+	RTF_GATEWAY                      = 0x2
+	RTF_HOST                         = 0x4
+	RTF_INTERFACE                    = 0x40000000
+	RTF_IRTT                         = 0x100
+	RTF_LINKRT                       = 0x100000
+	RTF_LOCAL                        = 0x80000000
+	RTF_MODIFIED                     = 0x20
+	RTF_MSS                          = 0x40
+	RTF_MTU                          = 0x40
+	RTF_MULTICAST                    = 0x20000000
+	RTF_NAT                          = 0x8000000
+	RTF_NOFORWARD                    = 0x1000
+	RTF_NONEXTHOP                    = 0x200000
+	RTF_NOPMTUDISC                   = 0x4000
+	RTF_POLICY                       = 0x4000000
+	RTF_REINSTATE                    = 0x8
+	RTF_REJECT                       = 0x200
+	RTF_STATIC                       = 0x400
+	RTF_THROW                        = 0x2000
+	RTF_UP                           = 0x1
+	RTF_WINDOW                       = 0x80
+	RTF_XRESOLVE                     = 0x800
+	RTM_BASE                         = 0x10
+	RTM_DELACTION                    = 0x31
+	RTM_DELADDR                      = 0x15
+	RTM_DELADDRLABEL                 = 0x49
+	RTM_DELLINK                      = 0x11
+	RTM_DELMDB                       = 0x55
+	RTM_DELNEIGH                     = 0x1d
+	RTM_DELQDISC                     = 0x25
+	RTM_DELROUTE                     = 0x19
+	RTM_DELRULE                      = 0x21
+	RTM_DELTCLASS                    = 0x29
+	RTM_DELTFILTER                   = 0x2d
+	RTM_F_CLONED                     = 0x200
+	RTM_F_EQUALIZE                   = 0x400
+	RTM_F_NOTIFY                     = 0x100
+	RTM_F_PREFIX                     = 0x800
+	RTM_GETACTION                    = 0x32
+	RTM_GETADDR                      = 0x16
+	RTM_GETADDRLABEL                 = 0x4a
+	RTM_GETANYCAST                   = 0x3e
+	RTM_GETDCB                       = 0x4e
+	RTM_GETLINK                      = 0x12
+	RTM_GETMDB                       = 0x56
+	RTM_GETMULTICAST                 = 0x3a
+	RTM_GETNEIGH                     = 0x1e
+	RTM_GETNEIGHTBL                  = 0x42
+	RTM_GETNETCONF                   = 0x52
+	RTM_GETQDISC                     = 0x26
+	RTM_GETROUTE                     = 0x1a
+	RTM_GETRULE                      = 0x22
+	RTM_GETTCLASS                    = 0x2a
+	RTM_GETTFILTER                   = 0x2e
+	RTM_MAX                          = 0x57
+	RTM_NEWACTION                    = 0x30
+	RTM_NEWADDR                      = 0x14
+	RTM_NEWADDRLABEL                 = 0x48
+	RTM_NEWLINK                      = 0x10
+	RTM_NEWMDB                       = 0x54
+	RTM_NEWNDUSEROPT                 = 0x44
+	RTM_NEWNEIGH                     = 0x1c
+	RTM_NEWNEIGHTBL                  = 0x40
+	RTM_NEWNETCONF                   = 0x50
+	RTM_NEWPREFIX                    = 0x34
+	RTM_NEWQDISC                     = 0x24
+	RTM_NEWROUTE                     = 0x18
+	RTM_NEWRULE                      = 0x20
+	RTM_NEWTCLASS                    = 0x28
+	RTM_NEWTFILTER                   = 0x2c
+	RTM_NR_FAMILIES                  = 0x12
+	RTM_NR_MSGTYPES                  = 0x48
+	RTM_SETDCB                       = 0x4f
+	RTM_SETLINK                      = 0x13
+	RTM_SETNEIGHTBL                  = 0x43
+	RTNH_ALIGNTO                     = 0x4
+	RTNH_F_DEAD                      = 0x1
+	RTNH_F_ONLINK                    = 0x4
+	RTNH_F_PERVASIVE                 = 0x2
+	RTN_MAX                          = 0xb
+	RTPROT_BIRD                      = 0xc
+	RTPROT_BOOT                      = 0x3
+	RTPROT_DHCP                      = 0x10
+	RTPROT_DNROUTED                  = 0xd
+	RTPROT_GATED                     = 0x8
+	RTPROT_KERNEL                    = 0x2
+	RTPROT_MROUTED                   = 0x11
+	RTPROT_MRT                       = 0xa
+	RTPROT_NTK                       = 0xf
+	RTPROT_RA                        = 0x9
+	RTPROT_REDIRECT                  = 0x1
+	RTPROT_STATIC                    = 0x4
+	RTPROT_UNSPEC                    = 0x0
+	RTPROT_XORP                      = 0xe
+	RTPROT_ZEBRA                     = 0xb
+	RT_CLASS_DEFAULT                 = 0xfd
+	RT_CLASS_LOCAL                   = 0xff
+	RT_CLASS_MAIN                    = 0xfe
+	RT_CLASS_MAX                     = 0xff
+	RT_CLASS_UNSPEC                  = 0x0
+	RUSAGE_CHILDREN                  = -0x1
+	RUSAGE_SELF                      = 0x0
+	RUSAGE_THREAD                    = 0x1
+	SCM_CREDENTIALS                  = 0x2
+	SCM_RIGHTS                       = 0x1
+	SCM_TIMESTAMP                    = 0x1d
+	SCM_TIMESTAMPING                 = 0x25
+	SCM_TIMESTAMPNS                  = 0x23
+	SCM_WIFI_STATUS                  = 0x29
+	SHUT_RD                          = 0x0
+	SHUT_RDWR                        = 0x2
+	SHUT_WR                          = 0x1
+	SIOCADDDLCI                      = 0x8980
+	SIOCADDMULTI                     = 0x8931
+	SIOCADDRT                        = 0x890b
+	SIOCATMARK                       = 0x40047307
+	SIOCDARP                         = 0x8953
+	SIOCDELDLCI                      = 0x8981
+	SIOCDELMULTI                     = 0x8932
+	SIOCDELRT                        = 0x890c
+	SIOCDEVPRIVATE                   = 0x89f0
+	SIOCDIFADDR                      = 0x8936
+	SIOCDRARP                        = 0x8960
+	SIOCGARP                         = 0x8954
+	SIOCGIFADDR                      = 0x8915
+	SIOCGIFBR                        = 0x8940
+	SIOCGIFBRDADDR                   = 0x8919
+	SIOCGIFCONF                      = 0x8912
+	SIOCGIFCOUNT                     = 0x8938
+	SIOCGIFDSTADDR                   = 0x8917
+	SIOCGIFENCAP                     = 0x8925
+	SIOCGIFFLAGS                     = 0x8913
+	SIOCGIFHWADDR                    = 0x8927
+	SIOCGIFINDEX                     = 0x8933
+	SIOCGIFMAP                       = 0x8970
+	SIOCGIFMEM                       = 0x891f
+	SIOCGIFMETRIC                    = 0x891d
+	SIOCGIFMTU                       = 0x8921
+	SIOCGIFNAME                      = 0x8910
+	SIOCGIFNETMASK                   = 0x891b
+	SIOCGIFPFLAGS                    = 0x8935
+	SIOCGIFSLAVE                     = 0x8929
+	SIOCGIFTXQLEN                    = 0x8942
+	SIOCGPGRP                        = 0x40047309
+	SIOCGRARP                        = 0x8961
+	SIOCGSTAMP                       = 0x8906
+	SIOCGSTAMPNS                     = 0x8907
+	SIOCPROTOPRIVATE                 = 0x89e0
+	SIOCRTMSG                        = 0x890d
+	SIOCSARP                         = 0x8955
+	SIOCSIFADDR                      = 0x8916
+	SIOCSIFBR                        = 0x8941
+	SIOCSIFBRDADDR                   = 0x891a
+	SIOCSIFDSTADDR                   = 0x8918
+	SIOCSIFENCAP                     = 0x8926
+	SIOCSIFFLAGS                     = 0x8914
+	SIOCSIFHWADDR                    = 0x8924
+	SIOCSIFHWBROADCAST               = 0x8937
+	SIOCSIFLINK                      = 0x8911
+	SIOCSIFMAP                       = 0x8971
+	SIOCSIFMEM                       = 0x8920
+	SIOCSIFMETRIC                    = 0x891e
+	SIOCSIFMTU                       = 0x8922
+	SIOCSIFNAME                      = 0x8923
+	SIOCSIFNETMASK                   = 0x891c
+	SIOCSIFPFLAGS                    = 0x8934
+	SIOCSIFSLAVE                     = 0x8930
+	SIOCSIFTXQLEN                    = 0x8943
+	SIOCSPGRP                        = 0x80047308
+	SIOCSRARP                        = 0x8962
+	SOCK_CLOEXEC                     = 0x80000
+	SOCK_DCCP                        = 0x6
+	SOCK_DGRAM                       = 0x1
+	SOCK_NONBLOCK                    = 0x80
+	SOCK_PACKET                      = 0xa
+	SOCK_RAW                         = 0x3
+	SOCK_RDM                         = 0x4
+	SOCK_SEQPACKET                   = 0x5
+	SOCK_STREAM                      = 0x2
+	SOL_AAL                          = 0x109
+	SOL_ATM                          = 0x108
+	SOL_DECNET                       = 0x105
+	SOL_ICMPV6                       = 0x3a
+	SOL_IP                           = 0x0
+	SOL_IPV6                         = 0x29
+	SOL_IRDA                         = 0x10a
+	SOL_PACKET                       = 0x107
+	SOL_RAW                          = 0xff
+	SOL_SOCKET                       = 0xffff
+	SOL_TCP                          = 0x6
+	SOL_X25                          = 0x106
+	SOMAXCONN                        = 0x80
+	SO_ACCEPTCONN                    = 0x1009
+	SO_ATTACH_FILTER                 = 0x1a
+	SO_BINDTODEVICE                  = 0x19
+	SO_BPF_EXTENSIONS                = 0x30
+	SO_BROADCAST                     = 0x20
+	SO_BSDCOMPAT                     = 0xe
+	SO_BUSY_POLL                     = 0x2e
+	SO_DEBUG                         = 0x1
+	SO_DETACH_FILTER                 = 0x1b
+	SO_DOMAIN                        = 0x1029
+	SO_DONTROUTE                     = 0x10
+	SO_ERROR                         = 0x1007
+	SO_GET_FILTER                    = 0x1a
+	SO_KEEPALIVE                     = 0x8
+	SO_LINGER                        = 0x80
+	SO_LOCK_FILTER                   = 0x2c
+	SO_MARK                          = 0x24
+	SO_MAX_PACING_RATE               = 0x2f
+	SO_NOFCS                         = 0x2b
+	SO_NO_CHECK                      = 0xb
+	SO_OOBINLINE                     = 0x100
+	SO_PASSCRED                      = 0x11
+	SO_PASSSEC                       = 0x22
+	SO_PEEK_OFF                      = 0x2a
+	SO_PEERCRED                      = 0x12
+	SO_PEERNAME                      = 0x1c
+	SO_PEERSEC                       = 0x1e
+	SO_PRIORITY                      = 0xc
+	SO_PROTOCOL                      = 0x1028
+	SO_RCVBUF                        = 0x1002
+	SO_RCVBUFFORCE                   = 0x21
+	SO_RCVLOWAT                      = 0x1004
+	SO_RCVTIMEO                      = 0x1006
+	SO_REUSEADDR                     = 0x4
+	SO_REUSEPORT                     = 0x200
+	SO_RXQ_OVFL                      = 0x28
+	SO_SECURITY_AUTHENTICATION       = 0x16
+	SO_SECURITY_ENCRYPTION_NETWORK   = 0x18
+	SO_SECURITY_ENCRYPTION_TRANSPORT = 0x17
+	SO_SELECT_ERR_QUEUE              = 0x2d
+	SO_SNDBUF                        = 0x1001
+	SO_SNDBUFFORCE                   = 0x1f
+	SO_SNDLOWAT                      = 0x1003
+	SO_SNDTIMEO                      = 0x1005
+	SO_STYLE                         = 0x1008
+	SO_TIMESTAMP                     = 0x1d
+	SO_TIMESTAMPING                  = 0x25
+	SO_TIMESTAMPNS                   = 0x23
+	SO_TYPE                          = 0x1008
+	SO_WIFI_STATUS                   = 0x29
+	S_BLKSIZE                        = 0x200
+	S_IEXEC                          = 0x40
+	S_IFBLK                          = 0x6000
+	S_IFCHR                          = 0x2000
+	S_IFDIR                          = 0x4000
+	S_IFIFO                          = 0x1000
+	S_IFLNK                          = 0xa000
+	S_IFMT                           = 0xf000
+	S_IFREG                          = 0x8000
+	S_IFSOCK                         = 0xc000
+	S_IREAD                          = 0x100
+	S_IRGRP                          = 0x20
+	S_IROTH                          = 0x4
+	S_IRUSR                          = 0x100
+	S_IRWXG                          = 0x38
+	S_IRWXO                          = 0x7
+	S_IRWXU                          = 0x1c0
+	S_ISGID                          = 0x400
+	S_ISUID                          = 0x800
+	S_ISVTX                          = 0x200
+	S_IWGRP                          = 0x10
+	S_IWOTH                          = 0x2
+	S_IWRITE                         = 0x80
+	S_IWUSR                          = 0x80
+	S_IXGRP                          = 0x8
+	S_IXOTH                          = 0x1
+	S_IXUSR                          = 0x40
+	TCFLSH                           = 0x5407
+	TCIFLUSH                         = 0x0
+	TCIOFLUSH                        = 0x2
+	TCOFLUSH                         = 0x1
+	TCP_CONGESTION                   = 0xd
+	TCP_COOKIE_IN_ALWAYS             = 0x1
+	TCP_COOKIE_MAX                   = 0x10
+	TCP_COOKIE_MIN                   = 0x8
+	TCP_COOKIE_OUT_NEVER             = 0x2
+	TCP_COOKIE_PAIR_SIZE             = 0x20
+	TCP_COOKIE_TRANSACTIONS          = 0xf
+	TCP_CORK                         = 0x3
+	TCP_DEFER_ACCEPT                 = 0x9
+	TCP_FASTOPEN                     = 0x17
+	TCP_INFO                         = 0xb
+	TCP_KEEPCNT                      = 0x6
+	TCP_KEEPIDLE                     = 0x4
+	TCP_KEEPINTVL                    = 0x5
+	TCP_LINGER2                      = 0x8
+	TCP_MAXSEG                       = 0x2
+	TCP_MAXWIN                       = 0xffff
+	TCP_MAX_WINSHIFT                 = 0xe
+	TCP_MD5SIG                       = 0xe
+	TCP_MD5SIG_MAXKEYLEN             = 0x50
+	TCP_MSS                          = 0x200
+	TCP_MSS_DEFAULT                  = 0x218
+	TCP_MSS_DESIRED                  = 0x4c4
+	TCP_NODELAY                      = 0x1
+	TCP_QUEUE_SEQ                    = 0x15
+	TCP_QUICKACK                     = 0xc
+	TCP_REPAIR                       = 0x13
+	TCP_REPAIR_OPTIONS               = 0x16
+	TCP_REPAIR_QUEUE                 = 0x14
+	TCP_SYNCNT                       = 0x7
+	TCP_S_DATA_IN                    = 0x4
+	TCP_S_DATA_OUT                   = 0x8
+	TCP_THIN_DUPACK                  = 0x11
+	TCP_THIN_LINEAR_TIMEOUTS         = 0x10
+	TCP_TIMESTAMP                    = 0x18
+	TCP_USER_TIMEOUT                 = 0x12
+	TCP_WINDOW_CLAMP                 = 0xa
+	TCSAFLUSH                        = 0x5410
+	TIOCCBRK                         = 0x5428
+	TIOCCONS                         = 0x80047478
+	TIOCEXCL                         = 0x740d
+	TIOCGDEV                         = 0x40045432
+	TIOCGETD                         = 0x7400
+	TIOCGETP                         = 0x7408
+	TIOCGEXCL                        = 0x40045440
+	TIOCGICOUNT                      = 0x5492
+	TIOCGLCKTRMIOS                   = 0x548b
+	TIOCGLTC                         = 0x7474
+	TIOCGPGRP                        = 0x40047477
+	TIOCGPKT                         = 0x40045438
+	TIOCGPTLCK                       = 0x40045439
+	TIOCGPTN                         = 0x40045430
+	TIOCGSERIAL                      = 0x5484
+	TIOCGSID                         = 0x7416
+	TIOCGSOFTCAR                     = 0x5481
+	TIOCGWINSZ                       = 0x40087468
+	TIOCINQ                          = 0x467f
+	TIOCLINUX                        = 0x5483
+	TIOCMBIC                         = 0x741c
+	TIOCMBIS                         = 0x741b
+	TIOCMGET                         = 0x741d
+	TIOCMIWAIT                       = 0x5491
+	TIOCMSET                         = 0x741a
+	TIOCM_CAR                        = 0x100
+	TIOCM_CD                         = 0x100
+	TIOCM_CTS                        = 0x40
+	TIOCM_DSR                        = 0x400
+	TIOCM_DTR                        = 0x2
+	TIOCM_LE                         = 0x1
+	TIOCM_RI                         = 0x200
+	TIOCM_RNG                        = 0x200
+	TIOCM_RTS                        = 0x4
+	TIOCM_SR                         = 0x20
+	TIOCM_ST                         = 0x10
+	TIOCNOTTY                        = 0x5471
+	TIOCNXCL                         = 0x740e
+	TIOCOUTQ                         = 0x7472
+	TIOCPKT                          = 0x5470
+	TIOCPKT_DATA                     = 0x0
+	TIOCPKT_DOSTOP                   = 0x20
+	TIOCPKT_FLUSHREAD                = 0x1
+	TIOCPKT_FLUSHWRITE               = 0x2
+	TIOCPKT_IOCTL                    = 0x40
+	TIOCPKT_NOSTOP                   = 0x10
+	TIOCPKT_START                    = 0x8
+	TIOCPKT_STOP                     = 0x4
+	TIOCSBRK                         = 0x5427
+	TIOCSCTTY                        = 0x5480
+	TIOCSERCONFIG                    = 0x5488
+	TIOCSERGETLSR                    = 0x548e
+	TIOCSERGETMULTI                  = 0x548f
+	TIOCSERGSTRUCT                   = 0x548d
+	TIOCSERGWILD                     = 0x5489
+	TIOCSERSETMULTI                  = 0x5490
+	TIOCSERSWILD                     = 0x548a
+	TIOCSER_TEMT                     = 0x1
+	TIOCSETD                         = 0x7401
+	TIOCSETN                         = 0x740a
+	TIOCSETP                         = 0x7409
+	TIOCSIG                          = 0x80045436
+	TIOCSLCKTRMIOS                   = 0x548c
+	TIOCSLTC                         = 0x7475
+	TIOCSPGRP                        = 0x80047476
+	TIOCSPTLCK                       = 0x80045431
+	TIOCSSERIAL                      = 0x5485
+	TIOCSSOFTCAR                     = 0x5482
+	TIOCSTI                          = 0x5472
+	TIOCSWINSZ                       = 0x80087467
+	TIOCVHANGUP                      = 0x5437
+	TOSTOP                           = 0x8000
+	TUNATTACHFILTER                  = 0x800854d5
+	TUNDETACHFILTER                  = 0x800854d6
+	TUNGETFEATURES                   = 0x400454cf
+	TUNGETFILTER                     = 0x400854db
+	TUNGETIFF                        = 0x400454d2
+	TUNGETSNDBUF                     = 0x400454d3
+	TUNGETVNETHDRSZ                  = 0x400454d7
+	TUNSETDEBUG                      = 0x800454c9
+	TUNSETGROUP                      = 0x800454ce
+	TUNSETIFF                        = 0x800454ca
+	TUNSETIFINDEX                    = 0x800454da
+	TUNSETLINK                       = 0x800454cd
+	TUNSETNOCSUM                     = 0x800454c8
+	TUNSETOFFLOAD                    = 0x800454d0
+	TUNSETOWNER                      = 0x800454cc
+	TUNSETPERSIST                    = 0x800454cb
+	TUNSETQUEUE                      = 0x800454d9
+	TUNSETSNDBUF                     = 0x800454d4
+	TUNSETTXFILTER                   = 0x800454d1
+	TUNSETVNETHDRSZ                  = 0x800454d8
+	VDISCARD                         = 0xd
+	VEOF                             = 0x10
+	VEOL                             = 0x11
+	VEOL2                            = 0x6
+	VERASE                           = 0x2
+	VINTR                            = 0x0
+	VKILL                            = 0x3
+	VLNEXT                           = 0xf
+	VMIN                             = 0x4
+	VQUIT                            = 0x1
+	VREPRINT                         = 0xc
+	VSTART                           = 0x8
+	VSTOP                            = 0x9
+	VSUSP                            = 0xa
+	VSWTC                            = 0x7
+	VSWTCH                           = 0x7
+	VT0                              = 0x0
+	VT1                              = 0x4000
+	VTDLY                            = 0x4000
+	VTIME                            = 0x5
+	VWERASE                          = 0xe
+	WALL                             = 0x40000000
+	WCLONE                           = 0x80000000
+	WCONTINUED                       = 0x8
+	WEXITED                          = 0x4
+	WNOHANG                          = 0x1
+	WNOTHREAD                        = 0x20000000
+	WNOWAIT                          = 0x1000000
+	WORDSIZE                         = 0x20
+	WSTOPPED                         = 0x2
+	WUNTRACED                        = 0x2
+)
+
+// Errors
+const (
+	E2BIG           = Errno(0x7)
+	EACCES          = Errno(0xd)
+	EADDRINUSE      = Errno(0x7d)
+	EADDRNOTAVAIL   = Errno(0x7e)
+	EADV            = Errno(0x44)
+	EAFNOSUPPORT    = Errno(0x7c)
+	EAGAIN          = Errno(0xb)
+	EALREADY        = Errno(0x95)
+	EBADE           = Errno(0x32)
+	EBADF           = Errno(0x9)
+	EBADFD          = Errno(0x51)
+	EBADMSG         = Errno(0x4d)
+	EBADR           = Errno(0x33)
+	EBADRQC         = Errno(0x36)
+	EBADSLT         = Errno(0x37)
+	EBFONT          = Errno(0x3b)
+	EBUSY           = Errno(0x10)
+	ECANCELED       = Errno(0x9e)
+	ECHILD          = Errno(0xa)
+	ECHRNG          = Errno(0x25)
+	ECOMM           = Errno(0x46)
+	ECONNABORTED    = Errno(0x82)
+	ECONNREFUSED    = Errno(0x92)
+	ECONNRESET      = Errno(0x83)
+	EDEADLK         = Errno(0x2d)
+	EDEADLOCK       = Errno(0x38)
+	EDESTADDRREQ    = Errno(0x60)
+	EDOM            = Errno(0x21)
+	EDOTDOT         = Errno(0x49)
+	EDQUOT          = Errno(0x46d)
+	EEXIST          = Errno(0x11)
+	EFAULT          = Errno(0xe)
+	EFBIG           = Errno(0x1b)
+	EHOSTDOWN       = Errno(0x93)
+	EHOSTUNREACH    = Errno(0x94)
+	EHWPOISON       = Errno(0xa8)
+	EIDRM           = Errno(0x24)
+	EILSEQ          = Errno(0x58)
+	EINIT           = Errno(0x8d)
+	EINPROGRESS     = Errno(0x96)
+	EINTR           = Errno(0x4)
+	EINVAL          = Errno(0x16)
+	EIO             = Errno(0x5)
+	EISCONN         = Errno(0x85)
+	EISDIR          = Errno(0x15)
+	EISNAM          = Errno(0x8b)
+	EKEYEXPIRED     = Errno(0xa2)
+	EKEYREJECTED    = Errno(0xa4)
+	EKEYREVOKED     = Errno(0xa3)
+	EL2HLT          = Errno(0x2c)
+	EL2NSYNC        = Errno(0x26)
+	EL3HLT          = Errno(0x27)
+	EL3RST          = Errno(0x28)
+	ELIBACC         = Errno(0x53)
+	ELIBBAD         = Errno(0x54)
+	ELIBEXEC        = Errno(0x57)
+	ELIBMAX         = Errno(0x56)
+	ELIBSCN         = Errno(0x55)
+	ELNRNG          = Errno(0x29)
+	ELOOP           = Errno(0x5a)
+	EMEDIUMTYPE     = Errno(0xa0)
+	EMFILE          = Errno(0x18)
+	EMLINK          = Errno(0x1f)
+	EMSGSIZE        = Errno(0x61)
+	EMULTIHOP       = Errno(0x4a)
+	ENAMETOOLONG    = Errno(0x4e)
+	ENAVAIL         = Errno(0x8a)
+	ENETDOWN        = Errno(0x7f)
+	ENETRESET       = Errno(0x81)
+	ENETUNREACH     = Errno(0x80)
+	ENFILE          = Errno(0x17)
+	ENOANO          = Errno(0x35)
+	ENOBUFS         = Errno(0x84)
+	ENOCSI          = Errno(0x2b)
+	ENODATA         = Errno(0x3d)
+	ENODEV          = Errno(0x13)
+	ENOENT          = Errno(0x2)
+	ENOEXEC         = Errno(0x8)
+	ENOKEY          = Errno(0xa1)
+	ENOLCK          = Errno(0x2e)
+	ENOLINK         = Errno(0x43)
+	ENOMEDIUM       = Errno(0x9f)
+	ENOMEM          = Errno(0xc)
+	ENOMSG          = Errno(0x23)
+	ENONET          = Errno(0x40)
+	ENOPKG          = Errno(0x41)
+	ENOPROTOOPT     = Errno(0x63)
+	ENOSPC          = Errno(0x1c)
+	ENOSR           = Errno(0x3f)
+	ENOSTR          = Errno(0x3c)
+	ENOSYS          = Errno(0x59)
+	ENOTBLK         = Errno(0xf)
+	ENOTCONN        = Errno(0x86)
+	ENOTDIR         = Errno(0x14)
+	ENOTEMPTY       = Errno(0x5d)
+	ENOTNAM         = Errno(0x89)
+	ENOTRECOVERABLE = Errno(0xa6)
+	ENOTSOCK        = Errno(0x5f)
+	ENOTSUP         = Errno(0x7a)
+	ENOTTY          = Errno(0x19)
+	ENOTUNIQ        = Errno(0x50)
+	ENXIO           = Errno(0x6)
+	EOPNOTSUPP      = Errno(0x7a)
+	EOVERFLOW       = Errno(0x4f)
+	EOWNERDEAD      = Errno(0xa5)
+	EPERM           = Errno(0x1)
+	EPFNOSUPPORT    = Errno(0x7b)
+	EPIPE           = Errno(0x20)
+	EPROTO          = Errno(0x47)
+	EPROTONOSUPPORT = Errno(0x78)
+	EPROTOTYPE      = Errno(0x62)
+	ERANGE          = Errno(0x22)
+	EREMCHG         = Errno(0x52)
+	EREMDEV         = Errno(0x8e)
+	EREMOTE         = Errno(0x42)
+	EREMOTEIO       = Errno(0x8c)
+	ERESTART        = Errno(0x5b)
+	ERFKILL         = Errno(0xa7)
+	EROFS           = Errno(0x1e)
+	ESHUTDOWN       = Errno(0x8f)
+	ESOCKTNOSUPPORT = Errno(0x79)
+	ESPIPE          = Errno(0x1d)
+	ESRCH           = Errno(0x3)
+	ESRMNT          = Errno(0x45)
+	ESTALE          = Errno(0x97)
+	ESTRPIPE        = Errno(0x5c)
+	ETIME           = Errno(0x3e)
+	ETIMEDOUT       = Errno(0x91)
+	ETOOMANYREFS    = Errno(0x90)
+	ETXTBSY         = Errno(0x1a)
+	EUCLEAN         = Errno(0x87)
+	EUNATCH         = Errno(0x2a)
+	EUSERS          = Errno(0x5e)
+	EWOULDBLOCK     = Errno(0xb)
+	EXDEV           = Errno(0x12)
+	EXFULL          = Errno(0x34)
+)
+
+// Signals
+const (
+	SIGABRT   = Signal(0x6)
+	SIGALRM   = Signal(0xe)
+	SIGBUS    = Signal(0xa)
+	SIGCHLD   = Signal(0x12)
+	SIGCLD    = Signal(0x12)
+	SIGCONT   = Signal(0x19)
+	SIGEMT    = Signal(0x7)
+	SIGFPE    = Signal(0x8)
+	SIGHUP    = Signal(0x1)
+	SIGILL    = Signal(0x4)
+	SIGINT    = Signal(0x2)
+	SIGIO     = Signal(0x16)
+	SIGIOT    = Signal(0x6)
+	SIGKILL   = Signal(0x9)
+	SIGPIPE   = Signal(0xd)
+	SIGPOLL   = Signal(0x16)
+	SIGPROF   = Signal(0x1d)
+	SIGPWR    = Signal(0x13)
+	SIGQUIT   = Signal(0x3)
+	SIGSEGV   = Signal(0xb)
+	SIGSTOP   = Signal(0x17)
+	SIGSYS    = Signal(0xc)
+	SIGTERM   = Signal(0xf)
+	SIGTRAP   = Signal(0x5)
+	SIGTSTP   = Signal(0x18)
+	SIGTTIN   = Signal(0x1a)
+	SIGTTOU   = Signal(0x1b)
+	SIGURG    = Signal(0x15)
+	SIGUSR1   = Signal(0x10)
+	SIGUSR2   = Signal(0x11)
+	SIGVTALRM = Signal(0x1c)
+	SIGWINCH  = Signal(0x14)
+	SIGXCPU   = Signal(0x1e)
+	SIGXFSZ   = Signal(0x1f)
+)
+
+// Error table
+var errors = [...]string{
+	1:    "operation not permitted",
+	2:    "no such file or directory",
+	3:    "no such process",
+	4:    "interrupted system call",
+	5:    "input/output error",
+	6:    "no such device or address",
+	7:    "argument list too long",
+	8:    "exec format error",
+	9:    "bad file descriptor",
+	10:   "no child processes",
+	11:   "resource temporarily unavailable",
+	12:   "cannot allocate memory",
+	13:   "permission denied",
+	14:   "bad address",
+	15:   "block device required",
+	16:   "device or resource busy",
+	17:   "file exists",
+	18:   "invalid cross-device link",
+	19:   "no such device",
+	20:   "not a directory",
+	21:   "is a directory",
+	22:   "invalid argument",
+	23:   "too many open files in system",
+	24:   "too many open files",
+	25:   "inappropriate ioctl for device",
+	26:   "text file busy",
+	27:   "file too large",
+	28:   "no space left on device",
+	29:   "illegal seek",
+	30:   "read-only file system",
+	31:   "too many links",
+	32:   "broken pipe",
+	33:   "numerical argument out of domain",
+	34:   "numerical result out of range",
+	35:   "no message of desired type",
+	36:   "identifier removed",
+	37:   "channel number out of range",
+	38:   "level 2 not synchronized",
+	39:   "level 3 halted",
+	40:   "level 3 reset",
+	41:   "link number out of range",
+	42:   "protocol driver not attached",
+	43:   "no CSI structure available",
+	44:   "level 2 halted",
+	45:   "resource deadlock avoided",
+	46:   "no locks available",
+	50:   "invalid exchange",
+	51:   "invalid request descriptor",
+	52:   "exchange full",
+	53:   "no anode",
+	54:   "invalid request code",
+	55:   "invalid slot",
+	56:   "file locking deadlock error",
+	59:   "bad font file format",
+	60:   "device not a stream",
+	61:   "no data available",
+	62:   "timer expired",
+	63:   "out of streams resources",
+	64:   "machine is not on the network",
+	65:   "package not installed",
+	66:   "object is remote",
+	67:   "link has been severed",
+	68:   "advertise error",
+	69:   "srmount error",
+	70:   "communication error on send",
+	71:   "protocol error",
+	73:   "RFS specific error",
+	74:   "multihop attempted",
+	77:   "bad message",
+	78:   "file name too long",
+	79:   "value too large for defined data type",
+	80:   "name not unique on network",
+	81:   "file descriptor in bad state",
+	82:   "remote address changed",
+	83:   "can not access a needed shared library",
+	84:   "accessing a corrupted shared library",
+	85:   ".lib section in a.out corrupted",
+	86:   "attempting to link in too many shared libraries",
+	87:   "cannot exec a shared library directly",
+	88:   "invalid or incomplete multibyte or wide character",
+	89:   "function not implemented",
+	90:   "too many levels of symbolic links",
+	91:   "interrupted system call should be restarted",
+	92:   "streams pipe error",
+	93:   "directory not empty",
+	94:   "too many users",
+	95:   "socket operation on non-socket",
+	96:   "destination address required",
+	97:   "message too long",
+	98:   "protocol wrong type for socket",
+	99:   "protocol not available",
+	120:  "protocol not supported",
+	121:  "socket type not supported",
+	122:  "operation not supported",
+	123:  "protocol family not supported",
+	124:  "address family not supported by protocol",
+	125:  "address already in use",
+	126:  "cannot assign requested address",
+	127:  "network is down",
+	128:  "network is unreachable",
+	129:  "network dropped connection on reset",
+	130:  "software caused connection abort",
+	131:  "connection reset by peer",
+	132:  "no buffer space available",
+	133:  "transport endpoint is already connected",
+	134:  "transport endpoint is not connected",
+	135:  "structure needs cleaning",
+	137:  "not a XENIX named type file",
+	138:  "no XENIX semaphores available",
+	139:  "is a named type file",
+	140:  "remote I/O error",
+	141:  "unknown error 141",
+	142:  "unknown error 142",
+	143:  "cannot send after transport endpoint shutdown",
+	144:  "too many references: cannot splice",
+	145:  "connection timed out",
+	146:  "connection refused",
+	147:  "host is down",
+	148:  "no route to host",
+	149:  "operation already in progress",
+	150:  "operation now in progress",
+	151:  "stale file handle",
+	158:  "operation canceled",
+	159:  "no medium found",
+	160:  "wrong medium type",
+	161:  "required key not available",
+	162:  "key has expired",
+	163:  "key has been revoked",
+	164:  "key was rejected by service",
+	165:  "owner died",
+	166:  "state not recoverable",
+	167:  "operation not possible due to RF-kill",
+	168:  "memory page has hardware error",
+	1133: "disk quota exceeded",
+}
+
+// Signal table
+var signals = [...]string{
+	1:  "hangup",
+	2:  "interrupt",
+	3:  "quit",
+	4:  "illegal instruction",
+	5:  "trace/breakpoint trap",
+	6:  "aborted",
+	7:  "EMT trap",
+	8:  "floating point exception",
+	9:  "killed",
+	10: "bus error",
+	11: "segmentation fault",
+	12: "bad system call",
+	13: "broken pipe",
+	14: "alarm clock",
+	15: "terminated",
+	16: "user defined signal 1",
+	17: "user defined signal 2",
+	18: "child exited",
+	19: "power failure",
+	20: "window changed",
+	21: "urgent I/O condition",
+	22: "I/O possible",
+	23: "stopped (signal)",
+	24: "stopped",
+	25: "continued",
+	26: "stopped (tty input)",
+	27: "stopped (tty output)",
+	28: "virtual timer expired",
+	29: "profiling timer expired",
+	30: "CPU time limit exceeded",
+	31: "file size limit exceeded",
+}
diff --git a/src/syscall/zerrors_linux_mipsle.go b/src/syscall/zerrors_linux_mipsle.go
new file mode 100644
index 0000000..580d66f
--- /dev/null
+++ b/src/syscall/zerrors_linux_mipsle.go
@@ -0,0 +1,1834 @@
+// mkerrors.sh
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs -- _const.go
+
+package syscall
+
+const (
+	AF_ALG                           = 0x26
+	AF_APPLETALK                     = 0x5
+	AF_ASH                           = 0x12
+	AF_ATMPVC                        = 0x8
+	AF_ATMSVC                        = 0x14
+	AF_AX25                          = 0x3
+	AF_BLUETOOTH                     = 0x1f
+	AF_BRIDGE                        = 0x7
+	AF_CAIF                          = 0x25
+	AF_CAN                           = 0x1d
+	AF_DECnet                        = 0xc
+	AF_ECONET                        = 0x13
+	AF_FILE                          = 0x1
+	AF_IEEE802154                    = 0x24
+	AF_INET                          = 0x2
+	AF_INET6                         = 0xa
+	AF_IPX                           = 0x4
+	AF_IRDA                          = 0x17
+	AF_ISDN                          = 0x22
+	AF_IUCV                          = 0x20
+	AF_KEY                           = 0xf
+	AF_LLC                           = 0x1a
+	AF_LOCAL                         = 0x1
+	AF_MAX                           = 0x29
+	AF_NETBEUI                       = 0xd
+	AF_NETLINK                       = 0x10
+	AF_NETROM                        = 0x6
+	AF_NFC                           = 0x27
+	AF_PACKET                        = 0x11
+	AF_PHONET                        = 0x23
+	AF_PPPOX                         = 0x18
+	AF_RDS                           = 0x15
+	AF_ROSE                          = 0xb
+	AF_ROUTE                         = 0x10
+	AF_RXRPC                         = 0x21
+	AF_SECURITY                      = 0xe
+	AF_SNA                           = 0x16
+	AF_TIPC                          = 0x1e
+	AF_UNIX                          = 0x1
+	AF_UNSPEC                        = 0x0
+	AF_VSOCK                         = 0x28
+	AF_WANPIPE                       = 0x19
+	AF_X25                           = 0x9
+	ARPHRD_6LOWPAN                   = 0x339
+	ARPHRD_ADAPT                     = 0x108
+	ARPHRD_APPLETLK                  = 0x8
+	ARPHRD_ARCNET                    = 0x7
+	ARPHRD_ASH                       = 0x30d
+	ARPHRD_ATM                       = 0x13
+	ARPHRD_AX25                      = 0x3
+	ARPHRD_BIF                       = 0x307
+	ARPHRD_CAIF                      = 0x336
+	ARPHRD_CAN                       = 0x118
+	ARPHRD_CHAOS                     = 0x5
+	ARPHRD_CISCO                     = 0x201
+	ARPHRD_CSLIP                     = 0x101
+	ARPHRD_CSLIP6                    = 0x103
+	ARPHRD_DDCMP                     = 0x205
+	ARPHRD_DLCI                      = 0xf
+	ARPHRD_ECONET                    = 0x30e
+	ARPHRD_EETHER                    = 0x2
+	ARPHRD_ETHER                     = 0x1
+	ARPHRD_EUI64                     = 0x1b
+	ARPHRD_FCAL                      = 0x311
+	ARPHRD_FCFABRIC                  = 0x313
+	ARPHRD_FCPL                      = 0x312
+	ARPHRD_FCPP                      = 0x310
+	ARPHRD_FDDI                      = 0x306
+	ARPHRD_FRAD                      = 0x302
+	ARPHRD_HDLC                      = 0x201
+	ARPHRD_HIPPI                     = 0x30c
+	ARPHRD_HWX25                     = 0x110
+	ARPHRD_IEEE1394                  = 0x18
+	ARPHRD_IEEE802                   = 0x6
+	ARPHRD_IEEE80211                 = 0x321
+	ARPHRD_IEEE80211_PRISM           = 0x322
+	ARPHRD_IEEE80211_RADIOTAP        = 0x323
+	ARPHRD_IEEE802154                = 0x324
+	ARPHRD_IEEE802154_MONITOR        = 0x325
+	ARPHRD_IEEE802_TR                = 0x320
+	ARPHRD_INFINIBAND                = 0x20
+	ARPHRD_IP6GRE                    = 0x337
+	ARPHRD_IPDDP                     = 0x309
+	ARPHRD_IPGRE                     = 0x30a
+	ARPHRD_IRDA                      = 0x30f
+	ARPHRD_LAPB                      = 0x204
+	ARPHRD_LOCALTLK                  = 0x305
+	ARPHRD_LOOPBACK                  = 0x304
+	ARPHRD_METRICOM                  = 0x17
+	ARPHRD_NETLINK                   = 0x338
+	ARPHRD_NETROM                    = 0x0
+	ARPHRD_NONE                      = 0xfffe
+	ARPHRD_PHONET                    = 0x334
+	ARPHRD_PHONET_PIPE               = 0x335
+	ARPHRD_PIMREG                    = 0x30b
+	ARPHRD_PPP                       = 0x200
+	ARPHRD_PRONET                    = 0x4
+	ARPHRD_RAWHDLC                   = 0x206
+	ARPHRD_ROSE                      = 0x10e
+	ARPHRD_RSRVD                     = 0x104
+	ARPHRD_SIT                       = 0x308
+	ARPHRD_SKIP                      = 0x303
+	ARPHRD_SLIP                      = 0x100
+	ARPHRD_SLIP6                     = 0x102
+	ARPHRD_TUNNEL                    = 0x300
+	ARPHRD_TUNNEL6                   = 0x301
+	ARPHRD_VOID                      = 0xffff
+	ARPHRD_X25                       = 0x10f
+	B0                               = 0x0
+	B1000000                         = 0x1008
+	B110                             = 0x3
+	B115200                          = 0x1002
+	B1152000                         = 0x1009
+	B1200                            = 0x9
+	B134                             = 0x4
+	B150                             = 0x5
+	B1500000                         = 0x100a
+	B1800                            = 0xa
+	B19200                           = 0xe
+	B200                             = 0x6
+	B2000000                         = 0x100b
+	B230400                          = 0x1003
+	B2400                            = 0xb
+	B2500000                         = 0x100c
+	B300                             = 0x7
+	B3000000                         = 0x100d
+	B3500000                         = 0x100e
+	B38400                           = 0xf
+	B4000000                         = 0x100f
+	B460800                          = 0x1004
+	B4800                            = 0xc
+	B50                              = 0x1
+	B500000                          = 0x1005
+	B57600                           = 0x1001
+	B576000                          = 0x1006
+	B600                             = 0x8
+	B75                              = 0x2
+	B921600                          = 0x1007
+	B9600                            = 0xd
+	BPF_A                            = 0x10
+	BPF_ABS                          = 0x20
+	BPF_ADD                          = 0x0
+	BPF_ALU                          = 0x4
+	BPF_AND                          = 0x50
+	BPF_B                            = 0x10
+	BPF_DIV                          = 0x30
+	BPF_H                            = 0x8
+	BPF_IMM                          = 0x0
+	BPF_IND                          = 0x40
+	BPF_JA                           = 0x0
+	BPF_JEQ                          = 0x10
+	BPF_JGE                          = 0x30
+	BPF_JGT                          = 0x20
+	BPF_JMP                          = 0x5
+	BPF_JSET                         = 0x40
+	BPF_K                            = 0x0
+	BPF_LD                           = 0x0
+	BPF_LDX                          = 0x1
+	BPF_LEN                          = 0x80
+	BPF_LSH                          = 0x60
+	BPF_MAJOR_VERSION                = 0x1
+	BPF_MAXINSNS                     = 0x1000
+	BPF_MEM                          = 0x60
+	BPF_MEMWORDS                     = 0x10
+	BPF_MINOR_VERSION                = 0x1
+	BPF_MISC                         = 0x7
+	BPF_MOD                          = 0x90
+	BPF_MSH                          = 0xa0
+	BPF_MUL                          = 0x20
+	BPF_NEG                          = 0x80
+	BPF_OR                           = 0x40
+	BPF_RET                          = 0x6
+	BPF_RSH                          = 0x70
+	BPF_ST                           = 0x2
+	BPF_STX                          = 0x3
+	BPF_SUB                          = 0x10
+	BPF_TAX                          = 0x0
+	BPF_TXA                          = 0x80
+	BPF_W                            = 0x0
+	BPF_X                            = 0x8
+	BPF_XOR                          = 0xa0
+	BRKINT                           = 0x2
+	CFLUSH                           = 0xf
+	CLOCAL                           = 0x800
+	CLONE_CHILD_CLEARTID             = 0x200000
+	CLONE_CHILD_SETTID               = 0x1000000
+	CLONE_DETACHED                   = 0x400000
+	CLONE_FILES                      = 0x400
+	CLONE_FS                         = 0x200
+	CLONE_IO                         = 0x80000000
+	CLONE_NEWIPC                     = 0x8000000
+	CLONE_NEWNET                     = 0x40000000
+	CLONE_NEWNS                      = 0x20000
+	CLONE_NEWPID                     = 0x20000000
+	CLONE_NEWUSER                    = 0x10000000
+	CLONE_NEWUTS                     = 0x4000000
+	CLONE_PARENT                     = 0x8000
+	CLONE_PARENT_SETTID              = 0x100000
+	CLONE_PTRACE                     = 0x2000
+	CLONE_SETTLS                     = 0x80000
+	CLONE_SIGHAND                    = 0x800
+	CLONE_SYSVSEM                    = 0x40000
+	CLONE_THREAD                     = 0x10000
+	CLONE_UNTRACED                   = 0x800000
+	CLONE_VFORK                      = 0x4000
+	CLONE_VM                         = 0x100
+	CREAD                            = 0x80
+	CS5                              = 0x0
+	CS6                              = 0x10
+	CS7                              = 0x20
+	CS8                              = 0x30
+	CSIGNAL                          = 0xff
+	CSIZE                            = 0x30
+	CSTART                           = 0x11
+	CSTATUS                          = 0x0
+	CSTOP                            = 0x13
+	CSTOPB                           = 0x40
+	CSUSP                            = 0x1a
+	DT_BLK                           = 0x6
+	DT_CHR                           = 0x2
+	DT_DIR                           = 0x4
+	DT_FIFO                          = 0x1
+	DT_LNK                           = 0xa
+	DT_REG                           = 0x8
+	DT_SOCK                          = 0xc
+	DT_UNKNOWN                       = 0x0
+	DT_WHT                           = 0xe
+	ECHO                             = 0x8
+	ECHOCTL                          = 0x200
+	ECHOE                            = 0x10
+	ECHOK                            = 0x20
+	ECHOKE                           = 0x800
+	ECHONL                           = 0x40
+	ECHOPRT                          = 0x400
+	ENCODING_DEFAULT                 = 0x0
+	ENCODING_FM_MARK                 = 0x3
+	ENCODING_FM_SPACE                = 0x4
+	ENCODING_MANCHESTER              = 0x5
+	ENCODING_NRZ                     = 0x1
+	ENCODING_NRZI                    = 0x2
+	EPOLLERR                         = 0x8
+	EPOLLET                          = 0x80000000
+	EPOLLHUP                         = 0x10
+	EPOLLIN                          = 0x1
+	EPOLLMSG                         = 0x400
+	EPOLLONESHOT                     = 0x40000000
+	EPOLLOUT                         = 0x4
+	EPOLLPRI                         = 0x2
+	EPOLLRDBAND                      = 0x80
+	EPOLLRDHUP                       = 0x2000
+	EPOLLRDNORM                      = 0x40
+	EPOLLWAKEUP                      = 0x20000000
+	EPOLLWRBAND                      = 0x200
+	EPOLLWRNORM                      = 0x100
+	EPOLL_CLOEXEC                    = 0x80000
+	EPOLL_CTL_ADD                    = 0x1
+	EPOLL_CTL_DEL                    = 0x2
+	EPOLL_CTL_MOD                    = 0x3
+	ETH_P_1588                       = 0x88f7
+	ETH_P_8021AD                     = 0x88a8
+	ETH_P_8021AH                     = 0x88e7
+	ETH_P_8021Q                      = 0x8100
+	ETH_P_80221                      = 0x8917
+	ETH_P_802_2                      = 0x4
+	ETH_P_802_3                      = 0x1
+	ETH_P_802_3_MIN                  = 0x600
+	ETH_P_802_EX1                    = 0x88b5
+	ETH_P_AARP                       = 0x80f3
+	ETH_P_AF_IUCV                    = 0xfbfb
+	ETH_P_ALL                        = 0x3
+	ETH_P_AOE                        = 0x88a2
+	ETH_P_ARCNET                     = 0x1a
+	ETH_P_ARP                        = 0x806
+	ETH_P_ATALK                      = 0x809b
+	ETH_P_ATMFATE                    = 0x8884
+	ETH_P_ATMMPOA                    = 0x884c
+	ETH_P_AX25                       = 0x2
+	ETH_P_BATMAN                     = 0x4305
+	ETH_P_BPQ                        = 0x8ff
+	ETH_P_CAIF                       = 0xf7
+	ETH_P_CAN                        = 0xc
+	ETH_P_CANFD                      = 0xd
+	ETH_P_CONTROL                    = 0x16
+	ETH_P_CUST                       = 0x6006
+	ETH_P_DDCMP                      = 0x6
+	ETH_P_DEC                        = 0x6000
+	ETH_P_DIAG                       = 0x6005
+	ETH_P_DNA_DL                     = 0x6001
+	ETH_P_DNA_RC                     = 0x6002
+	ETH_P_DNA_RT                     = 0x6003
+	ETH_P_DSA                        = 0x1b
+	ETH_P_ECONET                     = 0x18
+	ETH_P_EDSA                       = 0xdada
+	ETH_P_FCOE                       = 0x8906
+	ETH_P_FIP                        = 0x8914
+	ETH_P_HDLC                       = 0x19
+	ETH_P_IEEE802154                 = 0xf6
+	ETH_P_IEEEPUP                    = 0xa00
+	ETH_P_IEEEPUPAT                  = 0xa01
+	ETH_P_IP                         = 0x800
+	ETH_P_IPV6                       = 0x86dd
+	ETH_P_IPX                        = 0x8137
+	ETH_P_IRDA                       = 0x17
+	ETH_P_LAT                        = 0x6004
+	ETH_P_LINK_CTL                   = 0x886c
+	ETH_P_LOCALTALK                  = 0x9
+	ETH_P_LOOP                       = 0x60
+	ETH_P_LOOPBACK                   = 0x9000
+	ETH_P_MOBITEX                    = 0x15
+	ETH_P_MPLS_MC                    = 0x8848
+	ETH_P_MPLS_UC                    = 0x8847
+	ETH_P_MVRP                       = 0x88f5
+	ETH_P_PAE                        = 0x888e
+	ETH_P_PAUSE                      = 0x8808
+	ETH_P_PHONET                     = 0xf5
+	ETH_P_PPPTALK                    = 0x10
+	ETH_P_PPP_DISC                   = 0x8863
+	ETH_P_PPP_MP                     = 0x8
+	ETH_P_PPP_SES                    = 0x8864
+	ETH_P_PRP                        = 0x88fb
+	ETH_P_PUP                        = 0x200
+	ETH_P_PUPAT                      = 0x201
+	ETH_P_QINQ1                      = 0x9100
+	ETH_P_QINQ2                      = 0x9200
+	ETH_P_QINQ3                      = 0x9300
+	ETH_P_RARP                       = 0x8035
+	ETH_P_SCA                        = 0x6007
+	ETH_P_SLOW                       = 0x8809
+	ETH_P_SNAP                       = 0x5
+	ETH_P_TDLS                       = 0x890d
+	ETH_P_TEB                        = 0x6558
+	ETH_P_TIPC                       = 0x88ca
+	ETH_P_TRAILER                    = 0x1c
+	ETH_P_TR_802_2                   = 0x11
+	ETH_P_WAN_PPP                    = 0x7
+	ETH_P_WCCP                       = 0x883e
+	ETH_P_X25                        = 0x805
+	EXTA                             = 0xe
+	EXTB                             = 0xf
+	EXTPROC                          = 0x10000
+	FD_CLOEXEC                       = 0x1
+	FD_SETSIZE                       = 0x400
+	FLUSHO                           = 0x2000
+	F_DUPFD                          = 0x0
+	F_DUPFD_CLOEXEC                  = 0x406
+	F_EXLCK                          = 0x4
+	F_GETFD                          = 0x1
+	F_GETFL                          = 0x3
+	F_GETLEASE                       = 0x401
+	F_GETLK                          = 0x21
+	F_GETLK64                        = 0x21
+	F_GETOWN                         = 0x17
+	F_GETOWN_EX                      = 0x10
+	F_GETPIPE_SZ                     = 0x408
+	F_GETSIG                         = 0xb
+	F_LOCK                           = 0x1
+	F_NOTIFY                         = 0x402
+	F_OK                             = 0x0
+	F_RDLCK                          = 0x0
+	F_SETFD                          = 0x2
+	F_SETFL                          = 0x4
+	F_SETLEASE                       = 0x400
+	F_SETLK                          = 0x22
+	F_SETLK64                        = 0x22
+	F_SETLKW                         = 0x23
+	F_SETLKW64                       = 0x23
+	F_SETOWN                         = 0x18
+	F_SETOWN_EX                      = 0xf
+	F_SETPIPE_SZ                     = 0x407
+	F_SETSIG                         = 0xa
+	F_SHLCK                          = 0x8
+	F_TEST                           = 0x3
+	F_TLOCK                          = 0x2
+	F_ULOCK                          = 0x0
+	F_UNLCK                          = 0x2
+	F_WRLCK                          = 0x1
+	HUPCL                            = 0x400
+	ICANON                           = 0x2
+	ICMPV6_FILTER                    = 0x1
+	ICRNL                            = 0x100
+	IEXTEN                           = 0x100
+	IFA_F_DADFAILED                  = 0x8
+	IFA_F_DEPRECATED                 = 0x20
+	IFA_F_HOMEADDRESS                = 0x10
+	IFA_F_MANAGETEMPADDR             = 0x100
+	IFA_F_NODAD                      = 0x2
+	IFA_F_NOPREFIXROUTE              = 0x200
+	IFA_F_OPTIMISTIC                 = 0x4
+	IFA_F_PERMANENT                  = 0x80
+	IFA_F_SECONDARY                  = 0x1
+	IFA_F_TEMPORARY                  = 0x1
+	IFA_F_TENTATIVE                  = 0x40
+	IFA_MAX                          = 0x8
+	IFF_ALLMULTI                     = 0x200
+	IFF_ATTACH_QUEUE                 = 0x200
+	IFF_AUTOMEDIA                    = 0x4000
+	IFF_BROADCAST                    = 0x2
+	IFF_DEBUG                        = 0x4
+	IFF_DETACH_QUEUE                 = 0x400
+	IFF_DORMANT                      = 0x20000
+	IFF_DYNAMIC                      = 0x8000
+	IFF_ECHO                         = 0x40000
+	IFF_LOOPBACK                     = 0x8
+	IFF_LOWER_UP                     = 0x10000
+	IFF_MASTER                       = 0x400
+	IFF_MULTICAST                    = 0x1000
+	IFF_MULTI_QUEUE                  = 0x100
+	IFF_NOARP                        = 0x80
+	IFF_NOFILTER                     = 0x1000
+	IFF_NOTRAILERS                   = 0x20
+	IFF_NO_PI                        = 0x1000
+	IFF_ONE_QUEUE                    = 0x2000
+	IFF_PERSIST                      = 0x800
+	IFF_POINTOPOINT                  = 0x10
+	IFF_PORTSEL                      = 0x2000
+	IFF_PROMISC                      = 0x100
+	IFF_RUNNING                      = 0x40
+	IFF_SLAVE                        = 0x800
+	IFF_TAP                          = 0x2
+	IFF_TUN                          = 0x1
+	IFF_TUN_EXCL                     = 0x8000
+	IFF_UP                           = 0x1
+	IFF_VNET_HDR                     = 0x4000
+	IFF_VOLATILE                     = 0x70c5a
+	IFNAMSIZ                         = 0x10
+	IGNBRK                           = 0x1
+	IGNCR                            = 0x80
+	IGNPAR                           = 0x4
+	IMAXBEL                          = 0x2000
+	INLCR                            = 0x40
+	INPCK                            = 0x10
+	IN_ACCESS                        = 0x1
+	IN_ALL_EVENTS                    = 0xfff
+	IN_ATTRIB                        = 0x4
+	IN_CLASSA_HOST                   = 0xffffff
+	IN_CLASSA_MAX                    = 0x80
+	IN_CLASSA_NET                    = 0xff000000
+	IN_CLASSA_NSHIFT                 = 0x18
+	IN_CLASSB_HOST                   = 0xffff
+	IN_CLASSB_MAX                    = 0x10000
+	IN_CLASSB_NET                    = 0xffff0000
+	IN_CLASSB_NSHIFT                 = 0x10
+	IN_CLASSC_HOST                   = 0xff
+	IN_CLASSC_NET                    = 0xffffff00
+	IN_CLASSC_NSHIFT                 = 0x8
+	IN_CLOEXEC                       = 0x80000
+	IN_CLOSE                         = 0x18
+	IN_CLOSE_NOWRITE                 = 0x10
+	IN_CLOSE_WRITE                   = 0x8
+	IN_CREATE                        = 0x100
+	IN_DELETE                        = 0x200
+	IN_DELETE_SELF                   = 0x400
+	IN_DONT_FOLLOW                   = 0x2000000
+	IN_EXCL_UNLINK                   = 0x4000000
+	IN_IGNORED                       = 0x8000
+	IN_ISDIR                         = 0x40000000
+	IN_LOOPBACKNET                   = 0x7f
+	IN_MASK_ADD                      = 0x20000000
+	IN_MODIFY                        = 0x2
+	IN_MOVE                          = 0xc0
+	IN_MOVED_FROM                    = 0x40
+	IN_MOVED_TO                      = 0x80
+	IN_MOVE_SELF                     = 0x800
+	IN_NONBLOCK                      = 0x80
+	IN_ONESHOT                       = 0x80000000
+	IN_ONLYDIR                       = 0x1000000
+	IN_OPEN                          = 0x20
+	IN_Q_OVERFLOW                    = 0x4000
+	IN_UNMOUNT                       = 0x2000
+	IPPROTO_AH                       = 0x33
+	IPPROTO_BEETPH                   = 0x5e
+	IPPROTO_COMP                     = 0x6c
+	IPPROTO_DCCP                     = 0x21
+	IPPROTO_DSTOPTS                  = 0x3c
+	IPPROTO_EGP                      = 0x8
+	IPPROTO_ENCAP                    = 0x62
+	IPPROTO_ESP                      = 0x32
+	IPPROTO_FRAGMENT                 = 0x2c
+	IPPROTO_GRE                      = 0x2f
+	IPPROTO_HOPOPTS                  = 0x0
+	IPPROTO_ICMP                     = 0x1
+	IPPROTO_ICMPV6                   = 0x3a
+	IPPROTO_IDP                      = 0x16
+	IPPROTO_IGMP                     = 0x2
+	IPPROTO_IP                       = 0x0
+	IPPROTO_IPIP                     = 0x4
+	IPPROTO_IPV6                     = 0x29
+	IPPROTO_MH                       = 0x87
+	IPPROTO_MTP                      = 0x5c
+	IPPROTO_NONE                     = 0x3b
+	IPPROTO_PIM                      = 0x67
+	IPPROTO_PUP                      = 0xc
+	IPPROTO_RAW                      = 0xff
+	IPPROTO_ROUTING                  = 0x2b
+	IPPROTO_RSVP                     = 0x2e
+	IPPROTO_SCTP                     = 0x84
+	IPPROTO_TCP                      = 0x6
+	IPPROTO_TP                       = 0x1d
+	IPPROTO_UDP                      = 0x11
+	IPPROTO_UDPLITE                  = 0x88
+	IPV6_2292DSTOPTS                 = 0x4
+	IPV6_2292HOPLIMIT                = 0x8
+	IPV6_2292HOPOPTS                 = 0x3
+	IPV6_2292PKTINFO                 = 0x2
+	IPV6_2292PKTOPTIONS              = 0x6
+	IPV6_2292RTHDR                   = 0x5
+	IPV6_ADDRFORM                    = 0x1
+	IPV6_ADD_MEMBERSHIP              = 0x14
+	IPV6_AUTHHDR                     = 0xa
+	IPV6_CHECKSUM                    = 0x7
+	IPV6_DROP_MEMBERSHIP             = 0x15
+	IPV6_DSTOPTS                     = 0x3b
+	IPV6_HOPLIMIT                    = 0x34
+	IPV6_HOPOPTS                     = 0x36
+	IPV6_IPSEC_POLICY                = 0x22
+	IPV6_JOIN_ANYCAST                = 0x1b
+	IPV6_JOIN_GROUP                  = 0x14
+	IPV6_LEAVE_ANYCAST               = 0x1c
+	IPV6_LEAVE_GROUP                 = 0x15
+	IPV6_MTU                         = 0x18
+	IPV6_MTU_DISCOVER                = 0x17
+	IPV6_MULTICAST_HOPS              = 0x12
+	IPV6_MULTICAST_IF                = 0x11
+	IPV6_MULTICAST_LOOP              = 0x13
+	IPV6_NEXTHOP                     = 0x9
+	IPV6_PKTINFO                     = 0x32
+	IPV6_PMTUDISC_DO                 = 0x2
+	IPV6_PMTUDISC_DONT               = 0x0
+	IPV6_PMTUDISC_PROBE              = 0x3
+	IPV6_PMTUDISC_WANT               = 0x1
+	IPV6_RECVDSTOPTS                 = 0x3a
+	IPV6_RECVERR                     = 0x19
+	IPV6_RECVHOPLIMIT                = 0x33
+	IPV6_RECVHOPOPTS                 = 0x35
+	IPV6_RECVPKTINFO                 = 0x31
+	IPV6_RECVRTHDR                   = 0x38
+	IPV6_RECVTCLASS                  = 0x42
+	IPV6_ROUTER_ALERT                = 0x16
+	IPV6_RTHDR                       = 0x39
+	IPV6_RTHDRDSTOPTS                = 0x37
+	IPV6_RTHDR_LOOSE                 = 0x0
+	IPV6_RTHDR_STRICT                = 0x1
+	IPV6_RTHDR_TYPE_0                = 0x0
+	IPV6_RXDSTOPTS                   = 0x3b
+	IPV6_RXHOPOPTS                   = 0x36
+	IPV6_TCLASS                      = 0x43
+	IPV6_UNICAST_HOPS                = 0x10
+	IPV6_V6ONLY                      = 0x1a
+	IPV6_XFRM_POLICY                 = 0x23
+	IP_ADD_MEMBERSHIP                = 0x23
+	IP_ADD_SOURCE_MEMBERSHIP         = 0x27
+	IP_BLOCK_SOURCE                  = 0x26
+	IP_DEFAULT_MULTICAST_LOOP        = 0x1
+	IP_DEFAULT_MULTICAST_TTL         = 0x1
+	IP_DF                            = 0x4000
+	IP_DROP_MEMBERSHIP               = 0x24
+	IP_DROP_SOURCE_MEMBERSHIP        = 0x28
+	IP_FREEBIND                      = 0xf
+	IP_HDRINCL                       = 0x3
+	IP_IPSEC_POLICY                  = 0x10
+	IP_MAXPACKET                     = 0xffff
+	IP_MAX_MEMBERSHIPS               = 0x14
+	IP_MF                            = 0x2000
+	IP_MINTTL                        = 0x15
+	IP_MSFILTER                      = 0x29
+	IP_MSS                           = 0x240
+	IP_MTU                           = 0xe
+	IP_MTU_DISCOVER                  = 0xa
+	IP_MULTICAST_ALL                 = 0x31
+	IP_MULTICAST_IF                  = 0x20
+	IP_MULTICAST_LOOP                = 0x22
+	IP_MULTICAST_TTL                 = 0x21
+	IP_OFFMASK                       = 0x1fff
+	IP_OPTIONS                       = 0x4
+	IP_ORIGDSTADDR                   = 0x14
+	IP_PASSSEC                       = 0x12
+	IP_PKTINFO                       = 0x8
+	IP_PKTOPTIONS                    = 0x9
+	IP_PMTUDISC                      = 0xa
+	IP_PMTUDISC_DO                   = 0x2
+	IP_PMTUDISC_DONT                 = 0x0
+	IP_PMTUDISC_PROBE                = 0x3
+	IP_PMTUDISC_WANT                 = 0x1
+	IP_RECVERR                       = 0xb
+	IP_RECVOPTS                      = 0x6
+	IP_RECVORIGDSTADDR               = 0x14
+	IP_RECVRETOPTS                   = 0x7
+	IP_RECVTOS                       = 0xd
+	IP_RECVTTL                       = 0xc
+	IP_RETOPTS                       = 0x7
+	IP_RF                            = 0x8000
+	IP_ROUTER_ALERT                  = 0x5
+	IP_TOS                           = 0x1
+	IP_TRANSPARENT                   = 0x13
+	IP_TTL                           = 0x2
+	IP_UNBLOCK_SOURCE                = 0x25
+	IP_UNICAST_IF                    = 0x32
+	IP_XFRM_POLICY                   = 0x11
+	ISIG                             = 0x1
+	ISTRIP                           = 0x20
+	IUTF8                            = 0x4000
+	IXANY                            = 0x800
+	IXOFF                            = 0x1000
+	IXON                             = 0x400
+	LINUX_REBOOT_CMD_CAD_OFF         = 0x0
+	LINUX_REBOOT_CMD_CAD_ON          = 0x89abcdef
+	LINUX_REBOOT_CMD_HALT            = 0xcdef0123
+	LINUX_REBOOT_CMD_KEXEC           = 0x45584543
+	LINUX_REBOOT_CMD_POWER_OFF       = 0x4321fedc
+	LINUX_REBOOT_CMD_RESTART         = 0x1234567
+	LINUX_REBOOT_CMD_RESTART2        = 0xa1b2c3d4
+	LINUX_REBOOT_CMD_SW_SUSPEND      = 0xd000fce2
+	LINUX_REBOOT_MAGIC1              = 0xfee1dead
+	LINUX_REBOOT_MAGIC2              = 0x28121969
+	LOCK_EX                          = 0x2
+	LOCK_NB                          = 0x4
+	LOCK_SH                          = 0x1
+	LOCK_UN                          = 0x8
+	MADV_DODUMP                      = 0x11
+	MADV_DOFORK                      = 0xb
+	MADV_DONTDUMP                    = 0x10
+	MADV_DONTFORK                    = 0xa
+	MADV_DONTNEED                    = 0x4
+	MADV_HUGEPAGE                    = 0xe
+	MADV_HWPOISON                    = 0x64
+	MADV_MERGEABLE                   = 0xc
+	MADV_NOHUGEPAGE                  = 0xf
+	MADV_NORMAL                      = 0x0
+	MADV_RANDOM                      = 0x1
+	MADV_REMOVE                      = 0x9
+	MADV_SEQUENTIAL                  = 0x2
+	MADV_UNMERGEABLE                 = 0xd
+	MADV_WILLNEED                    = 0x3
+	MAP_ANON                         = 0x800
+	MAP_ANONYMOUS                    = 0x800
+	MAP_DENYWRITE                    = 0x2000
+	MAP_EXECUTABLE                   = 0x4000
+	MAP_FILE                         = 0x0
+	MAP_FIXED                        = 0x10
+	MAP_GROWSDOWN                    = 0x1000
+	MAP_HUGETLB                      = 0x80000
+	MAP_HUGE_MASK                    = 0x3f
+	MAP_HUGE_SHIFT                   = 0x1a
+	MAP_LOCKED                       = 0x8000
+	MAP_NONBLOCK                     = 0x20000
+	MAP_NORESERVE                    = 0x400
+	MAP_POPULATE                     = 0x10000
+	MAP_PRIVATE                      = 0x2
+	MAP_RENAME                       = 0x800
+	MAP_SHARED                       = 0x1
+	MAP_STACK                        = 0x40000
+	MAP_TYPE                         = 0xf
+	MCL_CURRENT                      = 0x1
+	MCL_FUTURE                       = 0x2
+	MNT_DETACH                       = 0x2
+	MNT_EXPIRE                       = 0x4
+	MNT_FORCE                        = 0x1
+	MSG_CMSG_CLOEXEC                 = 0x40000000
+	MSG_CONFIRM                      = 0x800
+	MSG_CTRUNC                       = 0x8
+	MSG_DONTROUTE                    = 0x4
+	MSG_DONTWAIT                     = 0x40
+	MSG_EOR                          = 0x80
+	MSG_ERRQUEUE                     = 0x2000
+	MSG_FASTOPEN                     = 0x20000000
+	MSG_FIN                          = 0x200
+	MSG_MORE                         = 0x8000
+	MSG_NOSIGNAL                     = 0x4000
+	MSG_OOB                          = 0x1
+	MSG_PEEK                         = 0x2
+	MSG_PROXY                        = 0x10
+	MSG_RST                          = 0x1000
+	MSG_SYN                          = 0x400
+	MSG_TRUNC                        = 0x20
+	MSG_TRYHARD                      = 0x4
+	MSG_WAITALL                      = 0x100
+	MSG_WAITFORONE                   = 0x10000
+	MS_ACTIVE                        = 0x40000000
+	MS_ASYNC                         = 0x1
+	MS_BIND                          = 0x1000
+	MS_DIRSYNC                       = 0x80
+	MS_INVALIDATE                    = 0x2
+	MS_I_VERSION                     = 0x800000
+	MS_KERNMOUNT                     = 0x400000
+	MS_MANDLOCK                      = 0x40
+	MS_MGC_MSK                       = 0xffff0000
+	MS_MGC_VAL                       = 0xc0ed0000
+	MS_MOVE                          = 0x2000
+	MS_NOATIME                       = 0x400
+	MS_NODEV                         = 0x4
+	MS_NODIRATIME                    = 0x800
+	MS_NOEXEC                        = 0x8
+	MS_NOSUID                        = 0x2
+	MS_NOUSER                        = -0x80000000
+	MS_POSIXACL                      = 0x10000
+	MS_PRIVATE                       = 0x40000
+	MS_RDONLY                        = 0x1
+	MS_REC                           = 0x4000
+	MS_RELATIME                      = 0x200000
+	MS_REMOUNT                       = 0x20
+	MS_RMT_MASK                      = 0x800051
+	MS_SHARED                        = 0x100000
+	MS_SILENT                        = 0x8000
+	MS_SLAVE                         = 0x80000
+	MS_STRICTATIME                   = 0x1000000
+	MS_SYNC                          = 0x4
+	MS_SYNCHRONOUS                   = 0x10
+	MS_UNBINDABLE                    = 0x20000
+	NAME_MAX                         = 0xff
+	NETLINK_ADD_MEMBERSHIP           = 0x1
+	NETLINK_AUDIT                    = 0x9
+	NETLINK_BROADCAST_ERROR          = 0x4
+	NETLINK_CONNECTOR                = 0xb
+	NETLINK_CRYPTO                   = 0x15
+	NETLINK_DNRTMSG                  = 0xe
+	NETLINK_DROP_MEMBERSHIP          = 0x2
+	NETLINK_ECRYPTFS                 = 0x13
+	NETLINK_FIB_LOOKUP               = 0xa
+	NETLINK_FIREWALL                 = 0x3
+	NETLINK_GENERIC                  = 0x10
+	NETLINK_INET_DIAG                = 0x4
+	NETLINK_IP6_FW                   = 0xd
+	NETLINK_ISCSI                    = 0x8
+	NETLINK_KOBJECT_UEVENT           = 0xf
+	NETLINK_NETFILTER                = 0xc
+	NETLINK_NFLOG                    = 0x5
+	NETLINK_NO_ENOBUFS               = 0x5
+	NETLINK_PKTINFO                  = 0x3
+	NETLINK_RDMA                     = 0x14
+	NETLINK_ROUTE                    = 0x0
+	NETLINK_RX_RING                  = 0x6
+	NETLINK_SCSITRANSPORT            = 0x12
+	NETLINK_SELINUX                  = 0x7
+	NETLINK_SOCK_DIAG                = 0x4
+	NETLINK_TX_RING                  = 0x7
+	NETLINK_UNUSED                   = 0x1
+	NETLINK_USERSOCK                 = 0x2
+	NETLINK_XFRM                     = 0x6
+	NLA_ALIGNTO                      = 0x4
+	NLA_F_NESTED                     = 0x8000
+	NLA_F_NET_BYTEORDER              = 0x4000
+	NLA_HDRLEN                       = 0x4
+	NLMSG_ALIGNTO                    = 0x4
+	NLMSG_DONE                       = 0x3
+	NLMSG_ERROR                      = 0x2
+	NLMSG_HDRLEN                     = 0x10
+	NLMSG_MIN_TYPE                   = 0x10
+	NLMSG_NOOP                       = 0x1
+	NLMSG_OVERRUN                    = 0x4
+	NLM_F_ACK                        = 0x4
+	NLM_F_APPEND                     = 0x800
+	NLM_F_ATOMIC                     = 0x400
+	NLM_F_CREATE                     = 0x400
+	NLM_F_DUMP                       = 0x300
+	NLM_F_DUMP_INTR                  = 0x10
+	NLM_F_ECHO                       = 0x8
+	NLM_F_EXCL                       = 0x200
+	NLM_F_MATCH                      = 0x200
+	NLM_F_MULTI                      = 0x2
+	NLM_F_REPLACE                    = 0x100
+	NLM_F_REQUEST                    = 0x1
+	NLM_F_ROOT                       = 0x100
+	NOFLSH                           = 0x80
+	OCRNL                            = 0x8
+	OFDEL                            = 0x80
+	OFILL                            = 0x40
+	ONLCR                            = 0x4
+	ONLRET                           = 0x20
+	ONOCR                            = 0x10
+	OPOST                            = 0x1
+	O_ACCMODE                        = 0x3
+	O_APPEND                         = 0x8
+	O_ASYNC                          = 0x1000
+	O_CLOEXEC                        = 0x80000
+	O_CREAT                          = 0x100
+	O_DIRECT                         = 0x8000
+	O_DIRECTORY                      = 0x10000
+	O_DSYNC                          = 0x10
+	O_EXCL                           = 0x400
+	O_FSYNC                          = 0x4010
+	O_LARGEFILE                      = 0x2000
+	O_NDELAY                         = 0x80
+	O_NOATIME                        = 0x40000
+	O_NOCTTY                         = 0x800
+	O_NOFOLLOW                       = 0x20000
+	O_NONBLOCK                       = 0x80
+	O_PATH                           = 0x200000
+	O_RDONLY                         = 0x0
+	O_RDWR                           = 0x2
+	O_RSYNC                          = 0x4010
+	O_SYNC                           = 0x4010
+	O_TMPFILE                        = 0x410000
+	O_TRUNC                          = 0x200
+	O_WRONLY                         = 0x1
+	PACKET_ADD_MEMBERSHIP            = 0x1
+	PACKET_AUXDATA                   = 0x8
+	PACKET_BROADCAST                 = 0x1
+	PACKET_COPY_THRESH               = 0x7
+	PACKET_DROP_MEMBERSHIP           = 0x2
+	PACKET_FANOUT                    = 0x12
+	PACKET_FANOUT_CPU                = 0x2
+	PACKET_FANOUT_FLAG_DEFRAG        = 0x8000
+	PACKET_FANOUT_FLAG_ROLLOVER      = 0x1000
+	PACKET_FANOUT_HASH               = 0x0
+	PACKET_FANOUT_LB                 = 0x1
+	PACKET_FANOUT_QM                 = 0x5
+	PACKET_FANOUT_RND                = 0x4
+	PACKET_FANOUT_ROLLOVER           = 0x3
+	PACKET_FASTROUTE                 = 0x6
+	PACKET_HDRLEN                    = 0xb
+	PACKET_HOST                      = 0x0
+	PACKET_KERNEL                    = 0x7
+	PACKET_LOOPBACK                  = 0x5
+	PACKET_LOSS                      = 0xe
+	PACKET_MR_ALLMULTI               = 0x2
+	PACKET_MR_MULTICAST              = 0x0
+	PACKET_MR_PROMISC                = 0x1
+	PACKET_MR_UNICAST                = 0x3
+	PACKET_MULTICAST                 = 0x2
+	PACKET_ORIGDEV                   = 0x9
+	PACKET_OTHERHOST                 = 0x3
+	PACKET_OUTGOING                  = 0x4
+	PACKET_QDISC_BYPASS              = 0x14
+	PACKET_RECV_OUTPUT               = 0x3
+	PACKET_RESERVE                   = 0xc
+	PACKET_RX_RING                   = 0x5
+	PACKET_STATISTICS                = 0x6
+	PACKET_TIMESTAMP                 = 0x11
+	PACKET_TX_HAS_OFF                = 0x13
+	PACKET_TX_RING                   = 0xd
+	PACKET_TX_TIMESTAMP              = 0x10
+	PACKET_USER                      = 0x6
+	PACKET_VERSION                   = 0xa
+	PACKET_VNET_HDR                  = 0xf
+	PARENB                           = 0x100
+	PARITY_CRC16_PR0                 = 0x2
+	PARITY_CRC16_PR0_CCITT           = 0x4
+	PARITY_CRC16_PR1                 = 0x3
+	PARITY_CRC16_PR1_CCITT           = 0x5
+	PARITY_CRC32_PR0_CCITT           = 0x6
+	PARITY_CRC32_PR1_CCITT           = 0x7
+	PARITY_DEFAULT                   = 0x0
+	PARITY_NONE                      = 0x1
+	PARMRK                           = 0x8
+	PARODD                           = 0x200
+	PENDIN                           = 0x4000
+	PRIO_PGRP                        = 0x1
+	PRIO_PROCESS                     = 0x0
+	PRIO_USER                        = 0x2
+	PROT_EXEC                        = 0x4
+	PROT_GROWSDOWN                   = 0x1000000
+	PROT_GROWSUP                     = 0x2000000
+	PROT_NONE                        = 0x0
+	PROT_READ                        = 0x1
+	PROT_WRITE                       = 0x2
+	PR_CAPBSET_DROP                  = 0x18
+	PR_CAPBSET_READ                  = 0x17
+	PR_ENDIAN_BIG                    = 0x0
+	PR_ENDIAN_LITTLE                 = 0x1
+	PR_ENDIAN_PPC_LITTLE             = 0x2
+	PR_FPEMU_NOPRINT                 = 0x1
+	PR_FPEMU_SIGFPE                  = 0x2
+	PR_FP_EXC_ASYNC                  = 0x2
+	PR_FP_EXC_DISABLED               = 0x0
+	PR_FP_EXC_DIV                    = 0x10000
+	PR_FP_EXC_INV                    = 0x100000
+	PR_FP_EXC_NONRECOV               = 0x1
+	PR_FP_EXC_OVF                    = 0x20000
+	PR_FP_EXC_PRECISE                = 0x3
+	PR_FP_EXC_RES                    = 0x80000
+	PR_FP_EXC_SW_ENABLE              = 0x80
+	PR_FP_EXC_UND                    = 0x40000
+	PR_GET_CHILD_SUBREAPER           = 0x25
+	PR_GET_DUMPABLE                  = 0x3
+	PR_GET_ENDIAN                    = 0x13
+	PR_GET_FPEMU                     = 0x9
+	PR_GET_FPEXC                     = 0xb
+	PR_GET_KEEPCAPS                  = 0x7
+	PR_GET_NAME                      = 0x10
+	PR_GET_NO_NEW_PRIVS              = 0x27
+	PR_GET_PDEATHSIG                 = 0x2
+	PR_GET_SECCOMP                   = 0x15
+	PR_GET_SECUREBITS                = 0x1b
+	PR_GET_THP_DISABLE               = 0x2a
+	PR_GET_TID_ADDRESS               = 0x28
+	PR_GET_TIMERSLACK                = 0x1e
+	PR_GET_TIMING                    = 0xd
+	PR_GET_TSC                       = 0x19
+	PR_GET_UNALIGN                   = 0x5
+	PR_MCE_KILL                      = 0x21
+	PR_MCE_KILL_CLEAR                = 0x0
+	PR_MCE_KILL_DEFAULT              = 0x2
+	PR_MCE_KILL_EARLY                = 0x1
+	PR_MCE_KILL_GET                  = 0x22
+	PR_MCE_KILL_LATE                 = 0x0
+	PR_MCE_KILL_SET                  = 0x1
+	PR_SET_CHILD_SUBREAPER           = 0x24
+	PR_SET_DUMPABLE                  = 0x4
+	PR_SET_ENDIAN                    = 0x14
+	PR_SET_FPEMU                     = 0xa
+	PR_SET_FPEXC                     = 0xc
+	PR_SET_KEEPCAPS                  = 0x8
+	PR_SET_MM                        = 0x23
+	PR_SET_MM_ARG_END                = 0x9
+	PR_SET_MM_ARG_START              = 0x8
+	PR_SET_MM_AUXV                   = 0xc
+	PR_SET_MM_BRK                    = 0x7
+	PR_SET_MM_END_CODE               = 0x2
+	PR_SET_MM_END_DATA               = 0x4
+	PR_SET_MM_ENV_END                = 0xb
+	PR_SET_MM_ENV_START              = 0xa
+	PR_SET_MM_EXE_FILE               = 0xd
+	PR_SET_MM_START_BRK              = 0x6
+	PR_SET_MM_START_CODE             = 0x1
+	PR_SET_MM_START_DATA             = 0x3
+	PR_SET_MM_START_STACK            = 0x5
+	PR_SET_NAME                      = 0xf
+	PR_SET_NO_NEW_PRIVS              = 0x26
+	PR_SET_PDEATHSIG                 = 0x1
+	PR_SET_PTRACER                   = 0x59616d61
+	PR_SET_PTRACER_ANY               = 0xffffffff
+	PR_SET_SECCOMP                   = 0x16
+	PR_SET_SECUREBITS                = 0x1c
+	PR_SET_THP_DISABLE               = 0x29
+	PR_SET_TIMERSLACK                = 0x1d
+	PR_SET_TIMING                    = 0xe
+	PR_SET_TSC                       = 0x1a
+	PR_SET_UNALIGN                   = 0x6
+	PR_TASK_PERF_EVENTS_DISABLE      = 0x1f
+	PR_TASK_PERF_EVENTS_ENABLE       = 0x20
+	PR_TIMING_STATISTICAL            = 0x0
+	PR_TIMING_TIMESTAMP              = 0x1
+	PR_TSC_ENABLE                    = 0x1
+	PR_TSC_SIGSEGV                   = 0x2
+	PR_UNALIGN_NOPRINT               = 0x1
+	PR_UNALIGN_SIGBUS                = 0x2
+	PTRACE_ATTACH                    = 0x10
+	PTRACE_CONT                      = 0x7
+	PTRACE_DETACH                    = 0x11
+	PTRACE_EVENT_CLONE               = 0x3
+	PTRACE_EVENT_EXEC                = 0x4
+	PTRACE_EVENT_EXIT                = 0x6
+	PTRACE_EVENT_FORK                = 0x1
+	PTRACE_EVENT_SECCOMP             = 0x7
+	PTRACE_EVENT_STOP                = 0x80
+	PTRACE_EVENT_VFORK               = 0x2
+	PTRACE_EVENT_VFORK_DONE          = 0x5
+	PTRACE_GETEVENTMSG               = 0x4201
+	PTRACE_GETFPREGS                 = 0xe
+	PTRACE_GETREGS                   = 0xc
+	PTRACE_GETREGSET                 = 0x4204
+	PTRACE_GETSIGINFO                = 0x4202
+	PTRACE_GETSIGMASK                = 0x420a
+	PTRACE_GET_THREAD_AREA           = 0x19
+	PTRACE_GET_THREAD_AREA_3264      = 0xc4
+	PTRACE_GET_WATCH_REGS            = 0xd0
+	PTRACE_INTERRUPT                 = 0x4207
+	PTRACE_KILL                      = 0x8
+	PTRACE_LISTEN                    = 0x4208
+	PTRACE_OLDSETOPTIONS             = 0x15
+	PTRACE_O_EXITKILL                = 0x100000
+	PTRACE_O_MASK                    = 0x1000ff
+	PTRACE_O_TRACECLONE              = 0x8
+	PTRACE_O_TRACEEXEC               = 0x10
+	PTRACE_O_TRACEEXIT               = 0x40
+	PTRACE_O_TRACEFORK               = 0x2
+	PTRACE_O_TRACESECCOMP            = 0x80
+	PTRACE_O_TRACESYSGOOD            = 0x1
+	PTRACE_O_TRACEVFORK              = 0x4
+	PTRACE_O_TRACEVFORKDONE          = 0x20
+	PTRACE_PEEKDATA                  = 0x2
+	PTRACE_PEEKDATA_3264             = 0xc1
+	PTRACE_PEEKSIGINFO               = 0x4209
+	PTRACE_PEEKSIGINFO_SHARED        = 0x1
+	PTRACE_PEEKTEXT                  = 0x1
+	PTRACE_PEEKTEXT_3264             = 0xc0
+	PTRACE_PEEKUSR                   = 0x3
+	PTRACE_POKEDATA                  = 0x5
+	PTRACE_POKEDATA_3264             = 0xc3
+	PTRACE_POKETEXT                  = 0x4
+	PTRACE_POKETEXT_3264             = 0xc2
+	PTRACE_POKEUSR                   = 0x6
+	PTRACE_SEIZE                     = 0x4206
+	PTRACE_SETFPREGS                 = 0xf
+	PTRACE_SETOPTIONS                = 0x4200
+	PTRACE_SETREGS                   = 0xd
+	PTRACE_SETREGSET                 = 0x4205
+	PTRACE_SETSIGINFO                = 0x4203
+	PTRACE_SETSIGMASK                = 0x420b
+	PTRACE_SET_THREAD_AREA           = 0x1a
+	PTRACE_SET_WATCH_REGS            = 0xd1
+	PTRACE_SINGLESTEP                = 0x9
+	PTRACE_SYSCALL                   = 0x18
+	PTRACE_TRACEME                   = 0x0
+	RLIMIT_AS                        = 0x6
+	RLIMIT_CORE                      = 0x4
+	RLIMIT_CPU                       = 0x0
+	RLIMIT_DATA                      = 0x2
+	RLIMIT_FSIZE                     = 0x1
+	RLIMIT_NOFILE                    = 0x5
+	RLIMIT_STACK                     = 0x3
+	RLIM_INFINITY                    = -0x1
+	RTAX_ADVMSS                      = 0x8
+	RTAX_CWND                        = 0x7
+	RTAX_FEATURES                    = 0xc
+	RTAX_FEATURE_ALLFRAG             = 0x8
+	RTAX_FEATURE_ECN                 = 0x1
+	RTAX_FEATURE_SACK                = 0x2
+	RTAX_FEATURE_TIMESTAMP           = 0x4
+	RTAX_HOPLIMIT                    = 0xa
+	RTAX_INITCWND                    = 0xb
+	RTAX_INITRWND                    = 0xe
+	RTAX_LOCK                        = 0x1
+	RTAX_MAX                         = 0xf
+	RTAX_MTU                         = 0x2
+	RTAX_QUICKACK                    = 0xf
+	RTAX_REORDERING                  = 0x9
+	RTAX_RTO_MIN                     = 0xd
+	RTAX_RTT                         = 0x4
+	RTAX_RTTVAR                      = 0x5
+	RTAX_SSTHRESH                    = 0x6
+	RTAX_UNSPEC                      = 0x0
+	RTAX_WINDOW                      = 0x3
+	RTA_ALIGNTO                      = 0x4
+	RTA_MAX                          = 0x11
+	RTCF_DIRECTSRC                   = 0x4000000
+	RTCF_DOREDIRECT                  = 0x1000000
+	RTCF_LOG                         = 0x2000000
+	RTCF_MASQ                        = 0x400000
+	RTCF_NAT                         = 0x800000
+	RTCF_VALVE                       = 0x200000
+	RTF_ADDRCLASSMASK                = 0xf8000000
+	RTF_ADDRCONF                     = 0x40000
+	RTF_ALLONLINK                    = 0x20000
+	RTF_BROADCAST                    = 0x10000000
+	RTF_CACHE                        = 0x1000000
+	RTF_DEFAULT                      = 0x10000
+	RTF_DYNAMIC                      = 0x10
+	RTF_FLOW                         = 0x2000000
+	RTF_GATEWAY                      = 0x2
+	RTF_HOST                         = 0x4
+	RTF_INTERFACE                    = 0x40000000
+	RTF_IRTT                         = 0x100
+	RTF_LINKRT                       = 0x100000
+	RTF_LOCAL                        = 0x80000000
+	RTF_MODIFIED                     = 0x20
+	RTF_MSS                          = 0x40
+	RTF_MTU                          = 0x40
+	RTF_MULTICAST                    = 0x20000000
+	RTF_NAT                          = 0x8000000
+	RTF_NOFORWARD                    = 0x1000
+	RTF_NONEXTHOP                    = 0x200000
+	RTF_NOPMTUDISC                   = 0x4000
+	RTF_POLICY                       = 0x4000000
+	RTF_REINSTATE                    = 0x8
+	RTF_REJECT                       = 0x200
+	RTF_STATIC                       = 0x400
+	RTF_THROW                        = 0x2000
+	RTF_UP                           = 0x1
+	RTF_WINDOW                       = 0x80
+	RTF_XRESOLVE                     = 0x800
+	RTM_BASE                         = 0x10
+	RTM_DELACTION                    = 0x31
+	RTM_DELADDR                      = 0x15
+	RTM_DELADDRLABEL                 = 0x49
+	RTM_DELLINK                      = 0x11
+	RTM_DELMDB                       = 0x55
+	RTM_DELNEIGH                     = 0x1d
+	RTM_DELQDISC                     = 0x25
+	RTM_DELROUTE                     = 0x19
+	RTM_DELRULE                      = 0x21
+	RTM_DELTCLASS                    = 0x29
+	RTM_DELTFILTER                   = 0x2d
+	RTM_F_CLONED                     = 0x200
+	RTM_F_EQUALIZE                   = 0x400
+	RTM_F_NOTIFY                     = 0x100
+	RTM_F_PREFIX                     = 0x800
+	RTM_GETACTION                    = 0x32
+	RTM_GETADDR                      = 0x16
+	RTM_GETADDRLABEL                 = 0x4a
+	RTM_GETANYCAST                   = 0x3e
+	RTM_GETDCB                       = 0x4e
+	RTM_GETLINK                      = 0x12
+	RTM_GETMDB                       = 0x56
+	RTM_GETMULTICAST                 = 0x3a
+	RTM_GETNEIGH                     = 0x1e
+	RTM_GETNEIGHTBL                  = 0x42
+	RTM_GETNETCONF                   = 0x52
+	RTM_GETQDISC                     = 0x26
+	RTM_GETROUTE                     = 0x1a
+	RTM_GETRULE                      = 0x22
+	RTM_GETTCLASS                    = 0x2a
+	RTM_GETTFILTER                   = 0x2e
+	RTM_MAX                          = 0x57
+	RTM_NEWACTION                    = 0x30
+	RTM_NEWADDR                      = 0x14
+	RTM_NEWADDRLABEL                 = 0x48
+	RTM_NEWLINK                      = 0x10
+	RTM_NEWMDB                       = 0x54
+	RTM_NEWNDUSEROPT                 = 0x44
+	RTM_NEWNEIGH                     = 0x1c
+	RTM_NEWNEIGHTBL                  = 0x40
+	RTM_NEWNETCONF                   = 0x50
+	RTM_NEWPREFIX                    = 0x34
+	RTM_NEWQDISC                     = 0x24
+	RTM_NEWROUTE                     = 0x18
+	RTM_NEWRULE                      = 0x20
+	RTM_NEWTCLASS                    = 0x28
+	RTM_NEWTFILTER                   = 0x2c
+	RTM_NR_FAMILIES                  = 0x12
+	RTM_NR_MSGTYPES                  = 0x48
+	RTM_SETDCB                       = 0x4f
+	RTM_SETLINK                      = 0x13
+	RTM_SETNEIGHTBL                  = 0x43
+	RTNH_ALIGNTO                     = 0x4
+	RTNH_F_DEAD                      = 0x1
+	RTNH_F_ONLINK                    = 0x4
+	RTNH_F_PERVASIVE                 = 0x2
+	RTN_MAX                          = 0xb
+	RTPROT_BIRD                      = 0xc
+	RTPROT_BOOT                      = 0x3
+	RTPROT_DHCP                      = 0x10
+	RTPROT_DNROUTED                  = 0xd
+	RTPROT_GATED                     = 0x8
+	RTPROT_KERNEL                    = 0x2
+	RTPROT_MROUTED                   = 0x11
+	RTPROT_MRT                       = 0xa
+	RTPROT_NTK                       = 0xf
+	RTPROT_RA                        = 0x9
+	RTPROT_REDIRECT                  = 0x1
+	RTPROT_STATIC                    = 0x4
+	RTPROT_UNSPEC                    = 0x0
+	RTPROT_XORP                      = 0xe
+	RTPROT_ZEBRA                     = 0xb
+	RT_CLASS_DEFAULT                 = 0xfd
+	RT_CLASS_LOCAL                   = 0xff
+	RT_CLASS_MAIN                    = 0xfe
+	RT_CLASS_MAX                     = 0xff
+	RT_CLASS_UNSPEC                  = 0x0
+	RUSAGE_CHILDREN                  = -0x1
+	RUSAGE_SELF                      = 0x0
+	RUSAGE_THREAD                    = 0x1
+	SCM_CREDENTIALS                  = 0x2
+	SCM_RIGHTS                       = 0x1
+	SCM_TIMESTAMP                    = 0x1d
+	SCM_TIMESTAMPING                 = 0x25
+	SCM_TIMESTAMPNS                  = 0x23
+	SCM_WIFI_STATUS                  = 0x29
+	SHUT_RD                          = 0x0
+	SHUT_RDWR                        = 0x2
+	SHUT_WR                          = 0x1
+	SIOCADDDLCI                      = 0x8980
+	SIOCADDMULTI                     = 0x8931
+	SIOCADDRT                        = 0x890b
+	SIOCATMARK                       = 0x40047307
+	SIOCDARP                         = 0x8953
+	SIOCDELDLCI                      = 0x8981
+	SIOCDELMULTI                     = 0x8932
+	SIOCDELRT                        = 0x890c
+	SIOCDEVPRIVATE                   = 0x89f0
+	SIOCDIFADDR                      = 0x8936
+	SIOCDRARP                        = 0x8960
+	SIOCGARP                         = 0x8954
+	SIOCGIFADDR                      = 0x8915
+	SIOCGIFBR                        = 0x8940
+	SIOCGIFBRDADDR                   = 0x8919
+	SIOCGIFCONF                      = 0x8912
+	SIOCGIFCOUNT                     = 0x8938
+	SIOCGIFDSTADDR                   = 0x8917
+	SIOCGIFENCAP                     = 0x8925
+	SIOCGIFFLAGS                     = 0x8913
+	SIOCGIFHWADDR                    = 0x8927
+	SIOCGIFINDEX                     = 0x8933
+	SIOCGIFMAP                       = 0x8970
+	SIOCGIFMEM                       = 0x891f
+	SIOCGIFMETRIC                    = 0x891d
+	SIOCGIFMTU                       = 0x8921
+	SIOCGIFNAME                      = 0x8910
+	SIOCGIFNETMASK                   = 0x891b
+	SIOCGIFPFLAGS                    = 0x8935
+	SIOCGIFSLAVE                     = 0x8929
+	SIOCGIFTXQLEN                    = 0x8942
+	SIOCGPGRP                        = 0x40047309
+	SIOCGRARP                        = 0x8961
+	SIOCGSTAMP                       = 0x8906
+	SIOCGSTAMPNS                     = 0x8907
+	SIOCPROTOPRIVATE                 = 0x89e0
+	SIOCRTMSG                        = 0x890d
+	SIOCSARP                         = 0x8955
+	SIOCSIFADDR                      = 0x8916
+	SIOCSIFBR                        = 0x8941
+	SIOCSIFBRDADDR                   = 0x891a
+	SIOCSIFDSTADDR                   = 0x8918
+	SIOCSIFENCAP                     = 0x8926
+	SIOCSIFFLAGS                     = 0x8914
+	SIOCSIFHWADDR                    = 0x8924
+	SIOCSIFHWBROADCAST               = 0x8937
+	SIOCSIFLINK                      = 0x8911
+	SIOCSIFMAP                       = 0x8971
+	SIOCSIFMEM                       = 0x8920
+	SIOCSIFMETRIC                    = 0x891e
+	SIOCSIFMTU                       = 0x8922
+	SIOCSIFNAME                      = 0x8923
+	SIOCSIFNETMASK                   = 0x891c
+	SIOCSIFPFLAGS                    = 0x8934
+	SIOCSIFSLAVE                     = 0x8930
+	SIOCSIFTXQLEN                    = 0x8943
+	SIOCSPGRP                        = 0x80047308
+	SIOCSRARP                        = 0x8962
+	SOCK_CLOEXEC                     = 0x80000
+	SOCK_DCCP                        = 0x6
+	SOCK_DGRAM                       = 0x1
+	SOCK_NONBLOCK                    = 0x80
+	SOCK_PACKET                      = 0xa
+	SOCK_RAW                         = 0x3
+	SOCK_RDM                         = 0x4
+	SOCK_SEQPACKET                   = 0x5
+	SOCK_STREAM                      = 0x2
+	SOL_AAL                          = 0x109
+	SOL_ATM                          = 0x108
+	SOL_DECNET                       = 0x105
+	SOL_ICMPV6                       = 0x3a
+	SOL_IP                           = 0x0
+	SOL_IPV6                         = 0x29
+	SOL_IRDA                         = 0x10a
+	SOL_PACKET                       = 0x107
+	SOL_RAW                          = 0xff
+	SOL_SOCKET                       = 0xffff
+	SOL_TCP                          = 0x6
+	SOL_X25                          = 0x106
+	SOMAXCONN                        = 0x80
+	SO_ACCEPTCONN                    = 0x1009
+	SO_ATTACH_FILTER                 = 0x1a
+	SO_BINDTODEVICE                  = 0x19
+	SO_BPF_EXTENSIONS                = 0x30
+	SO_BROADCAST                     = 0x20
+	SO_BSDCOMPAT                     = 0xe
+	SO_BUSY_POLL                     = 0x2e
+	SO_DEBUG                         = 0x1
+	SO_DETACH_FILTER                 = 0x1b
+	SO_DOMAIN                        = 0x1029
+	SO_DONTROUTE                     = 0x10
+	SO_ERROR                         = 0x1007
+	SO_GET_FILTER                    = 0x1a
+	SO_KEEPALIVE                     = 0x8
+	SO_LINGER                        = 0x80
+	SO_LOCK_FILTER                   = 0x2c
+	SO_MARK                          = 0x24
+	SO_MAX_PACING_RATE               = 0x2f
+	SO_NOFCS                         = 0x2b
+	SO_NO_CHECK                      = 0xb
+	SO_OOBINLINE                     = 0x100
+	SO_PASSCRED                      = 0x11
+	SO_PASSSEC                       = 0x22
+	SO_PEEK_OFF                      = 0x2a
+	SO_PEERCRED                      = 0x12
+	SO_PEERNAME                      = 0x1c
+	SO_PEERSEC                       = 0x1e
+	SO_PRIORITY                      = 0xc
+	SO_PROTOCOL                      = 0x1028
+	SO_RCVBUF                        = 0x1002
+	SO_RCVBUFFORCE                   = 0x21
+	SO_RCVLOWAT                      = 0x1004
+	SO_RCVTIMEO                      = 0x1006
+	SO_REUSEADDR                     = 0x4
+	SO_REUSEPORT                     = 0x200
+	SO_RXQ_OVFL                      = 0x28
+	SO_SECURITY_AUTHENTICATION       = 0x16
+	SO_SECURITY_ENCRYPTION_NETWORK   = 0x18
+	SO_SECURITY_ENCRYPTION_TRANSPORT = 0x17
+	SO_SELECT_ERR_QUEUE              = 0x2d
+	SO_SNDBUF                        = 0x1001
+	SO_SNDBUFFORCE                   = 0x1f
+	SO_SNDLOWAT                      = 0x1003
+	SO_SNDTIMEO                      = 0x1005
+	SO_STYLE                         = 0x1008
+	SO_TIMESTAMP                     = 0x1d
+	SO_TIMESTAMPING                  = 0x25
+	SO_TIMESTAMPNS                   = 0x23
+	SO_TYPE                          = 0x1008
+	SO_WIFI_STATUS                   = 0x29
+	S_BLKSIZE                        = 0x200
+	S_IEXEC                          = 0x40
+	S_IFBLK                          = 0x6000
+	S_IFCHR                          = 0x2000
+	S_IFDIR                          = 0x4000
+	S_IFIFO                          = 0x1000
+	S_IFLNK                          = 0xa000
+	S_IFMT                           = 0xf000
+	S_IFREG                          = 0x8000
+	S_IFSOCK                         = 0xc000
+	S_IREAD                          = 0x100
+	S_IRGRP                          = 0x20
+	S_IROTH                          = 0x4
+	S_IRUSR                          = 0x100
+	S_IRWXG                          = 0x38
+	S_IRWXO                          = 0x7
+	S_IRWXU                          = 0x1c0
+	S_ISGID                          = 0x400
+	S_ISUID                          = 0x800
+	S_ISVTX                          = 0x200
+	S_IWGRP                          = 0x10
+	S_IWOTH                          = 0x2
+	S_IWRITE                         = 0x80
+	S_IWUSR                          = 0x80
+	S_IXGRP                          = 0x8
+	S_IXOTH                          = 0x1
+	S_IXUSR                          = 0x40
+	TCFLSH                           = 0x5407
+	TCIFLUSH                         = 0x0
+	TCIOFLUSH                        = 0x2
+	TCOFLUSH                         = 0x1
+	TCP_CONGESTION                   = 0xd
+	TCP_COOKIE_IN_ALWAYS             = 0x1
+	TCP_COOKIE_MAX                   = 0x10
+	TCP_COOKIE_MIN                   = 0x8
+	TCP_COOKIE_OUT_NEVER             = 0x2
+	TCP_COOKIE_PAIR_SIZE             = 0x20
+	TCP_COOKIE_TRANSACTIONS          = 0xf
+	TCP_CORK                         = 0x3
+	TCP_DEFER_ACCEPT                 = 0x9
+	TCP_FASTOPEN                     = 0x17
+	TCP_INFO                         = 0xb
+	TCP_KEEPCNT                      = 0x6
+	TCP_KEEPIDLE                     = 0x4
+	TCP_KEEPINTVL                    = 0x5
+	TCP_LINGER2                      = 0x8
+	TCP_MAXSEG                       = 0x2
+	TCP_MAXWIN                       = 0xffff
+	TCP_MAX_WINSHIFT                 = 0xe
+	TCP_MD5SIG                       = 0xe
+	TCP_MD5SIG_MAXKEYLEN             = 0x50
+	TCP_MSS                          = 0x200
+	TCP_MSS_DEFAULT                  = 0x218
+	TCP_MSS_DESIRED                  = 0x4c4
+	TCP_NODELAY                      = 0x1
+	TCP_QUEUE_SEQ                    = 0x15
+	TCP_QUICKACK                     = 0xc
+	TCP_REPAIR                       = 0x13
+	TCP_REPAIR_OPTIONS               = 0x16
+	TCP_REPAIR_QUEUE                 = 0x14
+	TCP_SYNCNT                       = 0x7
+	TCP_S_DATA_IN                    = 0x4
+	TCP_S_DATA_OUT                   = 0x8
+	TCP_THIN_DUPACK                  = 0x11
+	TCP_THIN_LINEAR_TIMEOUTS         = 0x10
+	TCP_TIMESTAMP                    = 0x18
+	TCP_USER_TIMEOUT                 = 0x12
+	TCP_WINDOW_CLAMP                 = 0xa
+	TCSAFLUSH                        = 0x5410
+	TIOCCBRK                         = 0x5428
+	TIOCCONS                         = 0x80047478
+	TIOCEXCL                         = 0x740d
+	TIOCGDEV                         = 0x40045432
+	TIOCGETD                         = 0x7400
+	TIOCGETP                         = 0x7408
+	TIOCGEXCL                        = 0x40045440
+	TIOCGICOUNT                      = 0x5492
+	TIOCGLCKTRMIOS                   = 0x548b
+	TIOCGLTC                         = 0x7474
+	TIOCGPGRP                        = 0x40047477
+	TIOCGPKT                         = 0x40045438
+	TIOCGPTLCK                       = 0x40045439
+	TIOCGPTN                         = 0x40045430
+	TIOCGSERIAL                      = 0x5484
+	TIOCGSID                         = 0x7416
+	TIOCGSOFTCAR                     = 0x5481
+	TIOCGWINSZ                       = 0x40087468
+	TIOCINQ                          = 0x467f
+	TIOCLINUX                        = 0x5483
+	TIOCMBIC                         = 0x741c
+	TIOCMBIS                         = 0x741b
+	TIOCMGET                         = 0x741d
+	TIOCMIWAIT                       = 0x5491
+	TIOCMSET                         = 0x741a
+	TIOCM_CAR                        = 0x100
+	TIOCM_CD                         = 0x100
+	TIOCM_CTS                        = 0x40
+	TIOCM_DSR                        = 0x400
+	TIOCM_DTR                        = 0x2
+	TIOCM_LE                         = 0x1
+	TIOCM_RI                         = 0x200
+	TIOCM_RNG                        = 0x200
+	TIOCM_RTS                        = 0x4
+	TIOCM_SR                         = 0x20
+	TIOCM_ST                         = 0x10
+	TIOCNOTTY                        = 0x5471
+	TIOCNXCL                         = 0x740e
+	TIOCOUTQ                         = 0x7472
+	TIOCPKT                          = 0x5470
+	TIOCPKT_DATA                     = 0x0
+	TIOCPKT_DOSTOP                   = 0x20
+	TIOCPKT_FLUSHREAD                = 0x1
+	TIOCPKT_FLUSHWRITE               = 0x2
+	TIOCPKT_IOCTL                    = 0x40
+	TIOCPKT_NOSTOP                   = 0x10
+	TIOCPKT_START                    = 0x8
+	TIOCPKT_STOP                     = 0x4
+	TIOCSBRK                         = 0x5427
+	TIOCSCTTY                        = 0x5480
+	TIOCSERCONFIG                    = 0x5488
+	TIOCSERGETLSR                    = 0x548e
+	TIOCSERGETMULTI                  = 0x548f
+	TIOCSERGSTRUCT                   = 0x548d
+	TIOCSERGWILD                     = 0x5489
+	TIOCSERSETMULTI                  = 0x5490
+	TIOCSERSWILD                     = 0x548a
+	TIOCSER_TEMT                     = 0x1
+	TIOCSETD                         = 0x7401
+	TIOCSETN                         = 0x740a
+	TIOCSETP                         = 0x7409
+	TIOCSIG                          = 0x80045436
+	TIOCSLCKTRMIOS                   = 0x548c
+	TIOCSLTC                         = 0x7475
+	TIOCSPGRP                        = 0x80047476
+	TIOCSPTLCK                       = 0x80045431
+	TIOCSSERIAL                      = 0x5485
+	TIOCSSOFTCAR                     = 0x5482
+	TIOCSTI                          = 0x5472
+	TIOCSWINSZ                       = 0x80087467
+	TIOCVHANGUP                      = 0x5437
+	TOSTOP                           = 0x8000
+	TUNATTACHFILTER                  = 0x800854d5
+	TUNDETACHFILTER                  = 0x800854d6
+	TUNGETFEATURES                   = 0x400454cf
+	TUNGETFILTER                     = 0x400854db
+	TUNGETIFF                        = 0x400454d2
+	TUNGETSNDBUF                     = 0x400454d3
+	TUNGETVNETHDRSZ                  = 0x400454d7
+	TUNSETDEBUG                      = 0x800454c9
+	TUNSETGROUP                      = 0x800454ce
+	TUNSETIFF                        = 0x800454ca
+	TUNSETIFINDEX                    = 0x800454da
+	TUNSETLINK                       = 0x800454cd
+	TUNSETNOCSUM                     = 0x800454c8
+	TUNSETOFFLOAD                    = 0x800454d0
+	TUNSETOWNER                      = 0x800454cc
+	TUNSETPERSIST                    = 0x800454cb
+	TUNSETQUEUE                      = 0x800454d9
+	TUNSETSNDBUF                     = 0x800454d4
+	TUNSETTXFILTER                   = 0x800454d1
+	TUNSETVNETHDRSZ                  = 0x800454d8
+	VDISCARD                         = 0xd
+	VEOF                             = 0x10
+	VEOL                             = 0x11
+	VEOL2                            = 0x6
+	VERASE                           = 0x2
+	VINTR                            = 0x0
+	VKILL                            = 0x3
+	VLNEXT                           = 0xf
+	VMIN                             = 0x4
+	VQUIT                            = 0x1
+	VREPRINT                         = 0xc
+	VSTART                           = 0x8
+	VSTOP                            = 0x9
+	VSUSP                            = 0xa
+	VSWTC                            = 0x7
+	VSWTCH                           = 0x7
+	VT0                              = 0x0
+	VT1                              = 0x4000
+	VTDLY                            = 0x4000
+	VTIME                            = 0x5
+	VWERASE                          = 0xe
+	WALL                             = 0x40000000
+	WCLONE                           = 0x80000000
+	WCONTINUED                       = 0x8
+	WEXITED                          = 0x4
+	WNOHANG                          = 0x1
+	WNOTHREAD                        = 0x20000000
+	WNOWAIT                          = 0x1000000
+	WORDSIZE                         = 0x20
+	WSTOPPED                         = 0x2
+	WUNTRACED                        = 0x2
+)
+
+// Errors
+const (
+	E2BIG           = Errno(0x7)
+	EACCES          = Errno(0xd)
+	EADDRINUSE      = Errno(0x7d)
+	EADDRNOTAVAIL   = Errno(0x7e)
+	EADV            = Errno(0x44)
+	EAFNOSUPPORT    = Errno(0x7c)
+	EAGAIN          = Errno(0xb)
+	EALREADY        = Errno(0x95)
+	EBADE           = Errno(0x32)
+	EBADF           = Errno(0x9)
+	EBADFD          = Errno(0x51)
+	EBADMSG         = Errno(0x4d)
+	EBADR           = Errno(0x33)
+	EBADRQC         = Errno(0x36)
+	EBADSLT         = Errno(0x37)
+	EBFONT          = Errno(0x3b)
+	EBUSY           = Errno(0x10)
+	ECANCELED       = Errno(0x9e)
+	ECHILD          = Errno(0xa)
+	ECHRNG          = Errno(0x25)
+	ECOMM           = Errno(0x46)
+	ECONNABORTED    = Errno(0x82)
+	ECONNREFUSED    = Errno(0x92)
+	ECONNRESET      = Errno(0x83)
+	EDEADLK         = Errno(0x2d)
+	EDEADLOCK       = Errno(0x38)
+	EDESTADDRREQ    = Errno(0x60)
+	EDOM            = Errno(0x21)
+	EDOTDOT         = Errno(0x49)
+	EDQUOT          = Errno(0x46d)
+	EEXIST          = Errno(0x11)
+	EFAULT          = Errno(0xe)
+	EFBIG           = Errno(0x1b)
+	EHOSTDOWN       = Errno(0x93)
+	EHOSTUNREACH    = Errno(0x94)
+	EHWPOISON       = Errno(0xa8)
+	EIDRM           = Errno(0x24)
+	EILSEQ          = Errno(0x58)
+	EINIT           = Errno(0x8d)
+	EINPROGRESS     = Errno(0x96)
+	EINTR           = Errno(0x4)
+	EINVAL          = Errno(0x16)
+	EIO             = Errno(0x5)
+	EISCONN         = Errno(0x85)
+	EISDIR          = Errno(0x15)
+	EISNAM          = Errno(0x8b)
+	EKEYEXPIRED     = Errno(0xa2)
+	EKEYREJECTED    = Errno(0xa4)
+	EKEYREVOKED     = Errno(0xa3)
+	EL2HLT          = Errno(0x2c)
+	EL2NSYNC        = Errno(0x26)
+	EL3HLT          = Errno(0x27)
+	EL3RST          = Errno(0x28)
+	ELIBACC         = Errno(0x53)
+	ELIBBAD         = Errno(0x54)
+	ELIBEXEC        = Errno(0x57)
+	ELIBMAX         = Errno(0x56)
+	ELIBSCN         = Errno(0x55)
+	ELNRNG          = Errno(0x29)
+	ELOOP           = Errno(0x5a)
+	EMEDIUMTYPE     = Errno(0xa0)
+	EMFILE          = Errno(0x18)
+	EMLINK          = Errno(0x1f)
+	EMSGSIZE        = Errno(0x61)
+	EMULTIHOP       = Errno(0x4a)
+	ENAMETOOLONG    = Errno(0x4e)
+	ENAVAIL         = Errno(0x8a)
+	ENETDOWN        = Errno(0x7f)
+	ENETRESET       = Errno(0x81)
+	ENETUNREACH     = Errno(0x80)
+	ENFILE          = Errno(0x17)
+	ENOANO          = Errno(0x35)
+	ENOBUFS         = Errno(0x84)
+	ENOCSI          = Errno(0x2b)
+	ENODATA         = Errno(0x3d)
+	ENODEV          = Errno(0x13)
+	ENOENT          = Errno(0x2)
+	ENOEXEC         = Errno(0x8)
+	ENOKEY          = Errno(0xa1)
+	ENOLCK          = Errno(0x2e)
+	ENOLINK         = Errno(0x43)
+	ENOMEDIUM       = Errno(0x9f)
+	ENOMEM          = Errno(0xc)
+	ENOMSG          = Errno(0x23)
+	ENONET          = Errno(0x40)
+	ENOPKG          = Errno(0x41)
+	ENOPROTOOPT     = Errno(0x63)
+	ENOSPC          = Errno(0x1c)
+	ENOSR           = Errno(0x3f)
+	ENOSTR          = Errno(0x3c)
+	ENOSYS          = Errno(0x59)
+	ENOTBLK         = Errno(0xf)
+	ENOTCONN        = Errno(0x86)
+	ENOTDIR         = Errno(0x14)
+	ENOTEMPTY       = Errno(0x5d)
+	ENOTNAM         = Errno(0x89)
+	ENOTRECOVERABLE = Errno(0xa6)
+	ENOTSOCK        = Errno(0x5f)
+	ENOTSUP         = Errno(0x7a)
+	ENOTTY          = Errno(0x19)
+	ENOTUNIQ        = Errno(0x50)
+	ENXIO           = Errno(0x6)
+	EOPNOTSUPP      = Errno(0x7a)
+	EOVERFLOW       = Errno(0x4f)
+	EOWNERDEAD      = Errno(0xa5)
+	EPERM           = Errno(0x1)
+	EPFNOSUPPORT    = Errno(0x7b)
+	EPIPE           = Errno(0x20)
+	EPROTO          = Errno(0x47)
+	EPROTONOSUPPORT = Errno(0x78)
+	EPROTOTYPE      = Errno(0x62)
+	ERANGE          = Errno(0x22)
+	EREMCHG         = Errno(0x52)
+	EREMDEV         = Errno(0x8e)
+	EREMOTE         = Errno(0x42)
+	EREMOTEIO       = Errno(0x8c)
+	ERESTART        = Errno(0x5b)
+	ERFKILL         = Errno(0xa7)
+	EROFS           = Errno(0x1e)
+	ESHUTDOWN       = Errno(0x8f)
+	ESOCKTNOSUPPORT = Errno(0x79)
+	ESPIPE          = Errno(0x1d)
+	ESRCH           = Errno(0x3)
+	ESRMNT          = Errno(0x45)
+	ESTALE          = Errno(0x97)
+	ESTRPIPE        = Errno(0x5c)
+	ETIME           = Errno(0x3e)
+	ETIMEDOUT       = Errno(0x91)
+	ETOOMANYREFS    = Errno(0x90)
+	ETXTBSY         = Errno(0x1a)
+	EUCLEAN         = Errno(0x87)
+	EUNATCH         = Errno(0x2a)
+	EUSERS          = Errno(0x5e)
+	EWOULDBLOCK     = Errno(0xb)
+	EXDEV           = Errno(0x12)
+	EXFULL          = Errno(0x34)
+)
+
+// Signals
+const (
+	SIGABRT   = Signal(0x6)
+	SIGALRM   = Signal(0xe)
+	SIGBUS    = Signal(0xa)
+	SIGCHLD   = Signal(0x12)
+	SIGCLD    = Signal(0x12)
+	SIGCONT   = Signal(0x19)
+	SIGEMT    = Signal(0x7)
+	SIGFPE    = Signal(0x8)
+	SIGHUP    = Signal(0x1)
+	SIGILL    = Signal(0x4)
+	SIGINT    = Signal(0x2)
+	SIGIO     = Signal(0x16)
+	SIGIOT    = Signal(0x6)
+	SIGKILL   = Signal(0x9)
+	SIGPIPE   = Signal(0xd)
+	SIGPOLL   = Signal(0x16)
+	SIGPROF   = Signal(0x1d)
+	SIGPWR    = Signal(0x13)
+	SIGQUIT   = Signal(0x3)
+	SIGSEGV   = Signal(0xb)
+	SIGSTOP   = Signal(0x17)
+	SIGSYS    = Signal(0xc)
+	SIGTERM   = Signal(0xf)
+	SIGTRAP   = Signal(0x5)
+	SIGTSTP   = Signal(0x18)
+	SIGTTIN   = Signal(0x1a)
+	SIGTTOU   = Signal(0x1b)
+	SIGURG    = Signal(0x15)
+	SIGUSR1   = Signal(0x10)
+	SIGUSR2   = Signal(0x11)
+	SIGVTALRM = Signal(0x1c)
+	SIGWINCH  = Signal(0x14)
+	SIGXCPU   = Signal(0x1e)
+	SIGXFSZ   = Signal(0x1f)
+)
+
+// Error table
+var errors = [...]string{
+	1:    "operation not permitted",
+	2:    "no such file or directory",
+	3:    "no such process",
+	4:    "interrupted system call",
+	5:    "input/output error",
+	6:    "no such device or address",
+	7:    "argument list too long",
+	8:    "exec format error",
+	9:    "bad file descriptor",
+	10:   "no child processes",
+	11:   "resource temporarily unavailable",
+	12:   "cannot allocate memory",
+	13:   "permission denied",
+	14:   "bad address",
+	15:   "block device required",
+	16:   "device or resource busy",
+	17:   "file exists",
+	18:   "invalid cross-device link",
+	19:   "no such device",
+	20:   "not a directory",
+	21:   "is a directory",
+	22:   "invalid argument",
+	23:   "too many open files in system",
+	24:   "too many open files",
+	25:   "inappropriate ioctl for device",
+	26:   "text file busy",
+	27:   "file too large",
+	28:   "no space left on device",
+	29:   "illegal seek",
+	30:   "read-only file system",
+	31:   "too many links",
+	32:   "broken pipe",
+	33:   "numerical argument out of domain",
+	34:   "numerical result out of range",
+	35:   "no message of desired type",
+	36:   "identifier removed",
+	37:   "channel number out of range",
+	38:   "level 2 not synchronized",
+	39:   "level 3 halted",
+	40:   "level 3 reset",
+	41:   "link number out of range",
+	42:   "protocol driver not attached",
+	43:   "no CSI structure available",
+	44:   "level 2 halted",
+	45:   "resource deadlock avoided",
+	46:   "no locks available",
+	50:   "invalid exchange",
+	51:   "invalid request descriptor",
+	52:   "exchange full",
+	53:   "no anode",
+	54:   "invalid request code",
+	55:   "invalid slot",
+	56:   "file locking deadlock error",
+	59:   "bad font file format",
+	60:   "device not a stream",
+	61:   "no data available",
+	62:   "timer expired",
+	63:   "out of streams resources",
+	64:   "machine is not on the network",
+	65:   "package not installed",
+	66:   "object is remote",
+	67:   "link has been severed",
+	68:   "advertise error",
+	69:   "srmount error",
+	70:   "communication error on send",
+	71:   "protocol error",
+	73:   "RFS specific error",
+	74:   "multihop attempted",
+	77:   "bad message",
+	78:   "file name too long",
+	79:   "value too large for defined data type",
+	80:   "name not unique on network",
+	81:   "file descriptor in bad state",
+	82:   "remote address changed",
+	83:   "can not access a needed shared library",
+	84:   "accessing a corrupted shared library",
+	85:   ".lib section in a.out corrupted",
+	86:   "attempting to link in too many shared libraries",
+	87:   "cannot exec a shared library directly",
+	88:   "invalid or incomplete multibyte or wide character",
+	89:   "function not implemented",
+	90:   "too many levels of symbolic links",
+	91:   "interrupted system call should be restarted",
+	92:   "streams pipe error",
+	93:   "directory not empty",
+	94:   "too many users",
+	95:   "socket operation on non-socket",
+	96:   "destination address required",
+	97:   "message too long",
+	98:   "protocol wrong type for socket",
+	99:   "protocol not available",
+	120:  "protocol not supported",
+	121:  "socket type not supported",
+	122:  "operation not supported",
+	123:  "protocol family not supported",
+	124:  "address family not supported by protocol",
+	125:  "address already in use",
+	126:  "cannot assign requested address",
+	127:  "network is down",
+	128:  "network is unreachable",
+	129:  "network dropped connection on reset",
+	130:  "software caused connection abort",
+	131:  "connection reset by peer",
+	132:  "no buffer space available",
+	133:  "transport endpoint is already connected",
+	134:  "transport endpoint is not connected",
+	135:  "structure needs cleaning",
+	137:  "not a XENIX named type file",
+	138:  "no XENIX semaphores available",
+	139:  "is a named type file",
+	140:  "remote I/O error",
+	141:  "unknown error 141",
+	142:  "unknown error 142",
+	143:  "cannot send after transport endpoint shutdown",
+	144:  "too many references: cannot splice",
+	145:  "connection timed out",
+	146:  "connection refused",
+	147:  "host is down",
+	148:  "no route to host",
+	149:  "operation already in progress",
+	150:  "operation now in progress",
+	151:  "stale file handle",
+	158:  "operation canceled",
+	159:  "no medium found",
+	160:  "wrong medium type",
+	161:  "required key not available",
+	162:  "key has expired",
+	163:  "key has been revoked",
+	164:  "key was rejected by service",
+	165:  "owner died",
+	166:  "state not recoverable",
+	167:  "operation not possible due to RF-kill",
+	168:  "memory page has hardware error",
+	1133: "disk quota exceeded",
+}
+
+// Signal table
+var signals = [...]string{
+	1:  "hangup",
+	2:  "interrupt",
+	3:  "quit",
+	4:  "illegal instruction",
+	5:  "trace/breakpoint trap",
+	6:  "aborted",
+	7:  "EMT trap",
+	8:  "floating point exception",
+	9:  "killed",
+	10: "bus error",
+	11: "segmentation fault",
+	12: "bad system call",
+	13: "broken pipe",
+	14: "alarm clock",
+	15: "terminated",
+	16: "user defined signal 1",
+	17: "user defined signal 2",
+	18: "child exited",
+	19: "power failure",
+	20: "window changed",
+	21: "urgent I/O condition",
+	22: "I/O possible",
+	23: "stopped (signal)",
+	24: "stopped",
+	25: "continued",
+	26: "stopped (tty input)",
+	27: "stopped (tty output)",
+	28: "virtual timer expired",
+	29: "profiling timer expired",
+	30: "CPU time limit exceeded",
+	31: "file size limit exceeded",
+}
diff --git a/src/syscall/zsyscall_linux_386.go b/src/syscall/zsyscall_linux_386.go
index 82834fd..195228d 100644
--- a/src/syscall/zsyscall_linux_386.go
+++ b/src/syscall/zsyscall_linux_386.go
@@ -763,8 +763,8 @@ func PivotRoot(newroot string, putold string) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
-	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
diff --git a/src/syscall/zsyscall_linux_amd64.go b/src/syscall/zsyscall_linux_amd64.go
index e7f754e..f413a19 100644
--- a/src/syscall/zsyscall_linux_amd64.go
+++ b/src/syscall/zsyscall_linux_amd64.go
@@ -763,8 +763,8 @@ func PivotRoot(newroot string, putold string) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
-	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
diff --git a/src/syscall/zsyscall_linux_arm.go b/src/syscall/zsyscall_linux_arm.go
index aed89cd..7773436 100644
--- a/src/syscall/zsyscall_linux_arm.go
+++ b/src/syscall/zsyscall_linux_arm.go
@@ -763,8 +763,8 @@ func PivotRoot(newroot string, putold string) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
-	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
diff --git a/src/syscall/zsyscall_linux_arm64.go b/src/syscall/zsyscall_linux_arm64.go
index 69df407..8e02f3c 100644
--- a/src/syscall/zsyscall_linux_arm64.go
+++ b/src/syscall/zsyscall_linux_arm64.go
@@ -763,8 +763,8 @@ func PivotRoot(newroot string, putold string) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
-	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
diff --git a/src/syscall/zsyscall_linux_mips.go b/src/syscall/zsyscall_linux_mips.go
new file mode 100644
index 0000000..0fa1452
--- /dev/null
+++ b/src/syscall/zsyscall_linux_mips.go
@@ -0,0 +1,1759 @@
+// mksyscall.pl -b32 -arm syscall_linux.go syscall_linux_mipsx.go
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package syscall
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(oldpath)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(newpath)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_LINKAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), uintptr(flags), 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0)
+	use(unsafe.Pointer(_p0))
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var _p1 unsafe.Pointer
+	if len(buf) > 0 {
+		_p1 = unsafe.Pointer(&buf[0])
+	} else {
+		_p1 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_READLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func symlinkat(oldpath string, newdirfd int, newpath string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(oldpath)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(newpath)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_SYMLINKAT, uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)))
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func unlinkat(dirfd int, path string, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags))
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimes(path string, times *[2]Timeval) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimensat(dirfd int, path string, times *[2]Timespec) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)))
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func futimesat(dirfd int, path *byte, times *[2]Timeval) (err error) {
+	_, _, e1 := Syscall(SYS_FUTIMESAT, uintptr(dirfd), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(times)))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getcwd(buf []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_GETCWD, uintptr(_p0), uintptr(len(buf)), 0)
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) {
+	r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0)
+	wpid = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
+	_, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(arg)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(_p0)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mount(source string, target string, fstype string, flags uintptr, data *byte) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(source)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(target)
+	if err != nil {
+		return
+	}
+	var _p2 *byte
+	_p2, err = BytePtrFromString(fstype)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(_p2)), uintptr(flags), uintptr(unsafe.Pointer(data)), 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	use(unsafe.Pointer(_p2))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Acct(path string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(_p0)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Adjtimex(buf *Timex) (state int, err error) {
+	r0, _, e1 := Syscall(SYS_ADJTIMEX, uintptr(unsafe.Pointer(buf)), 0, 0)
+	state = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chdir(path string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chroot(path string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+	_, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(oldfd int) (fd int, err error) {
+	r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup3(oldfd int, newfd int, flags int) (err error) {
+	_, _, e1 := Syscall(SYS_DUP3, uintptr(oldfd), uintptr(newfd), uintptr(flags))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func EpollCreate(size int) (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func EpollCreate1(flag int) (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE1, uintptr(flag), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
+	_, _, e1 := RawSyscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(events) > 0 {
+		_p0 = unsafe.Pointer(&events[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Exit(code int) {
+	Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fallocate(fd int, mode uint32, off int64, len int64) (err error) {
+	_, _, e1 := Syscall6(SYS_FALLOCATE, uintptr(fd), uintptr(mode), uintptr(off>>32), uintptr(off), uintptr(len>>32), uintptr(len))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchdir(fd int) (err error) {
+	_, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchmod(fd int, mode uint32) (err error) {
+	_, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid), uintptr(flags), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntl(fd int, cmd int, arg int) (val int, err error) {
+	r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
+	val = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fdatasync(fd int) (err error) {
+	_, _, e1 := Syscall(SYS_FDATASYNC, uintptr(fd), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Flock(fd int, how int) (err error) {
+	_, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fsync(fd int) (err error) {
+	_, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getdents(fd int, buf []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(_SYS_getdents, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgid(pid int) (pgid int, err error) {
+	r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
+	pgid = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpid() (pid int) {
+	r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
+	pid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getppid() (ppid int) {
+	r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
+	ppid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpriority(which int, who int) (prio int, err error) {
+	r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+	prio = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrusage(who int, rusage *Rusage) (err error) {
+	_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Gettid() (tid int) {
+	r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
+	tid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(attr)
+	if err != nil {
+		return
+	}
+	var _p2 unsafe.Pointer
+	if len(dest) > 0 {
+		_p2 = unsafe.Pointer(&dest[0])
+	} else {
+		_p2 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	sz = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(pathname)
+	if err != nil {
+		return
+	}
+	r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mask))
+	use(unsafe.Pointer(_p0))
+	watchdesc = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func InotifyInit1(flags int) (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func InotifyRmWatch(fd int, watchdesc uint32) (success int, err error) {
+	r0, _, e1 := RawSyscall(SYS_INOTIFY_RM_WATCH, uintptr(fd), uintptr(watchdesc), 0)
+	success = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kill(pid int, sig Signal) (err error) {
+	_, _, e1 := RawSyscall(SYS_KILL, uintptr(pid), uintptr(sig), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Klogctl(typ int, buf []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_SYSLOG, uintptr(typ), uintptr(_p0), uintptr(len(buf)))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listxattr(path string, dest []byte) (sz int, err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var _p1 unsafe.Pointer
+	if len(dest) > 0 {
+		_p1 = unsafe.Pointer(&dest[0])
+	} else {
+		_p1 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
+	use(unsafe.Pointer(_p0))
+	sz = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkdirat(dirfd int, path string, mode uint32) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode))
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
+	_, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pause() (err error) {
+	_, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func PivotRoot(newroot string, putold string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(newroot)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(putold)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, p []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Removexattr(path string, attr string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(attr)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(oldpath)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(newpath)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setdomainname(p []byte) (err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_SETDOMAINNAME, uintptr(_p0), uintptr(len(p)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sethostname(p []byte) (err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_SETHOSTNAME, uintptr(_p0), uintptr(len(p)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpgid(pid int, pgid int) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setsid() (pid int, err error) {
+	r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+	pid = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Settimeofday(tv *Timeval) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpriority(which int, who int, prio int) (err error) {
+	_, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setxattr(path string, attr string, data []byte, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(attr)
+	if err != nil {
+		return
+	}
+	var _p2 unsafe.Pointer
+	if len(data) > 0 {
+		_p2 = unsafe.Pointer(&data[0])
+	} else {
+		_p2 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall6(SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sync() {
+	Syscall(SYS_SYNC, 0, 0, 0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sysinfo(info *Sysinfo_t) (err error) {
+	_, _, e1 := RawSyscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
+	r0, r1, e1 := Syscall6(SYS_TEE, uintptr(rfd), uintptr(wfd), uintptr(len), uintptr(flags), 0, 0)
+	n = int64(int64(r0)<<32 | int64(r1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Tgkill(tgid int, tid int, sig Signal) (err error) {
+	_, _, e1 := RawSyscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Times(tms *Tms) (ticks uintptr, err error) {
+	r0, _, e1 := RawSyscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0)
+	ticks = uintptr(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Umask(mask int) (oldmask int) {
+	r0, _, _ := RawSyscall(SYS_UMASK, uintptr(mask), 0, 0)
+	oldmask = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Uname(buf *Utsname) (err error) {
+	_, _, e1 := RawSyscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unmount(target string, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(target)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unshare(flags int) (err error) {
+	_, _, e1 := Syscall(SYS_UNSHARE, uintptr(flags), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ustat(dev int, ubuf *Ustat_t) (err error) {
+	_, _, e1 := Syscall(SYS_USTAT, uintptr(dev), uintptr(unsafe.Pointer(ubuf)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Utime(path string, buf *Utimbuf) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func write(fd int, p []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func exitThread(code int) (err error) {
+	_, _, e1 := Syscall(SYS_EXIT, uintptr(code), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func readlen(fd int, p *byte, np int) (n int, err error) {
+	r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func writelen(fd int, p *byte, np int) (n int, err error) {
+	r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (err error) {
+	_, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Madvise(b []byte, advice int) (err error) {
+	var _p0 unsafe.Pointer
+	if len(b) > 0 {
+		_p0 = unsafe.Pointer(&b[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_MADVISE, uintptr(_p0), uintptr(len(b)), uintptr(advice))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mprotect(b []byte, prot int) (err error) {
+	var _p0 unsafe.Pointer
+	if len(b) > 0 {
+		_p0 = unsafe.Pointer(&b[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_MPROTECT, uintptr(_p0), uintptr(len(b)), uintptr(prot))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mlock(b []byte) (err error) {
+	var _p0 unsafe.Pointer
+	if len(b) > 0 {
+		_p0 = unsafe.Pointer(&b[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_MLOCK, uintptr(_p0), uintptr(len(b)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Munlock(b []byte) (err error) {
+	var _p0 unsafe.Pointer
+	if len(b) > 0 {
+		_p0 = unsafe.Pointer(&b[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_MUNLOCK, uintptr(_p0), uintptr(len(b)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mlockall(flags int) (err error) {
+	_, _, e1 := Syscall(SYS_MLOCKALL, uintptr(flags), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Munlockall() (err error) {
+	_, _, e1 := Syscall(SYS_MUNLOCKALL, 0, 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup2(oldfd int, newfd int) (err error) {
+	_, _, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchown(fd int, uid int, gid int) (err error) {
+	_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ftruncate(fd int, length int64) (err error) {
+	_, _, e1 := Syscall6(SYS_FTRUNCATE64, uintptr(fd), 0, uintptr(length>>32), uintptr(length), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getegid() (egid int) {
+	r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
+	egid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Geteuid() (euid int) {
+	r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
+	euid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getgid() (gid int) {
+	r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
+	gid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getuid() (uid int) {
+	r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
+	uid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lchown(path string, uid int, gid int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listen(s int, n int) (err error) {
+	_, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(n), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_PREAD64, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset>>32), uintptr(offset))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_PWRITE64, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset>>32), uintptr(offset))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) {
+	r0, _, e1 := Syscall6(SYS__NEWSELECT, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+	r0, _, e1 := Syscall6(SYS_SENDFILE64, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0)
+	written = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setfsgid(gid int) (err error) {
+	_, _, e1 := Syscall(SYS_SETFSGID, uintptr(gid), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setfsuid(uid int) (err error) {
+	_, _, e1 := Syscall(SYS_SETFSUID, uintptr(uid), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setregid(rgid int, egid int) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setresgid(rgid int, egid int, sgid int) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setresuid(ruid int, euid int, suid int) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setreuid(ruid int, euid int) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Shutdown(fd int, how int) (err error) {
+	_, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(fd), uintptr(how), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) {
+	r0, r1, e1 := Syscall6(SYS_SPLICE, uintptr(rfd), uintptr(unsafe.Pointer(roff)), uintptr(wfd), uintptr(unsafe.Pointer(woff)), uintptr(len), uintptr(flags))
+	n = int64(int64(r0)<<32 | int64(r1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func SyncFileRange(fd int, off int64, n int64, flags int) (err error) {
+	_, _, e1 := Syscall9(SYS_SYNC_FILE_RANGE, uintptr(fd), 0, uintptr(off>>32), uintptr(off), uintptr(n>>32), uintptr(n), uintptr(flags), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Truncate(path string, length int64) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_TRUNCATE64, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length>>32), uintptr(length), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
+	r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) {
+	r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
+	_, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
+	_, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getgroups(n int, list *_Gid_t) (nn int, err error) {
+	r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+	nn = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setgroups(n int, list *_Gid_t) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) {
+	_, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) {
+	_, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socket(domain int, typ int, proto int) (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
+	_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+	_, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+	_, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
+	r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
+	r0, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func InotifyInit() (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ioperm(from int, num int, on int) (err error) {
+	_, _, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Iopl(level int) (err error) {
+	_, _, e1 := Syscall(SYS_IOPL, uintptr(level), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Gettimeofday(tv *Timeval) (err error) {
+	_, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Time(t *Time_t) (tt Time_t, err error) {
+	r0, _, e1 := RawSyscall(SYS_TIME, uintptr(unsafe.Pointer(t)), 0, 0)
+	tt = Time_t(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lstat(path string, stat *Stat_t) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, stat *Stat_t) (err error) {
+	_, _, e1 := Syscall(SYS_FSTAT64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Stat(path string, stat *Stat_t) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) {
+	r0, _, e1 := Syscall6(SYS_MMAP2, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(pageOffset))
+	xaddr = uintptr(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getrlimit(resource int, rlim *rlimit32) (err error) {
+	_, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setrlimit(resource int, rlim *rlimit32) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
diff --git a/src/syscall/zsyscall_linux_mips64.go b/src/syscall/zsyscall_linux_mips64.go
index d1d5f24..b9708e3 100644
--- a/src/syscall/zsyscall_linux_mips64.go
+++ b/src/syscall/zsyscall_linux_mips64.go
@@ -761,8 +761,8 @@ func PivotRoot(newroot string, putold string) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
-	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
diff --git a/src/syscall/zsyscall_linux_mips64le.go b/src/syscall/zsyscall_linux_mips64le.go
index d1d5f24..b9708e3 100644
--- a/src/syscall/zsyscall_linux_mips64le.go
+++ b/src/syscall/zsyscall_linux_mips64le.go
@@ -761,8 +761,8 @@ func PivotRoot(newroot string, putold string) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
-	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
diff --git a/src/syscall/zsyscall_linux_mipsle.go b/src/syscall/zsyscall_linux_mipsle.go
new file mode 100644
index 0000000..527e3ef
--- /dev/null
+++ b/src/syscall/zsyscall_linux_mipsle.go
@@ -0,0 +1,1759 @@
+// mksyscall.pl -l32 -arm syscall_linux.go syscall_linux_mipsx.go
+// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+
+package syscall
+
+import "unsafe"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(oldpath)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(newpath)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_LINKAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), uintptr(flags), 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0)
+	use(unsafe.Pointer(_p0))
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func readlinkat(dirfd int, path string, buf []byte) (n int, err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var _p1 unsafe.Pointer
+	if len(buf) > 0 {
+		_p1 = unsafe.Pointer(&buf[0])
+	} else {
+		_p1 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_READLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func symlinkat(oldpath string, newdirfd int, newpath string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(oldpath)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(newpath)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_SYMLINKAT, uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)))
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func unlinkat(dirfd int, path string, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags))
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimes(path string, times *[2]Timeval) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func utimensat(dirfd int, path string, times *[2]Timespec) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_UTIMENSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(times)))
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func futimesat(dirfd int, path *byte, times *[2]Timeval) (err error) {
+	_, _, e1 := Syscall(SYS_FUTIMESAT, uintptr(dirfd), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(times)))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getcwd(buf []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_GETCWD, uintptr(_p0), uintptr(len(buf)), 0)
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) {
+	r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0)
+	wpid = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
+	_, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(arg)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_REBOOT, uintptr(magic1), uintptr(magic2), uintptr(cmd), uintptr(unsafe.Pointer(_p0)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mount(source string, target string, fstype string, flags uintptr, data *byte) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(source)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(target)
+	if err != nil {
+		return
+	}
+	var _p2 *byte
+	_p2, err = BytePtrFromString(fstype)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_MOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(unsafe.Pointer(_p2)), uintptr(flags), uintptr(unsafe.Pointer(data)), 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	use(unsafe.Pointer(_p2))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Acct(path string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(_p0)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Adjtimex(buf *Timex) (state int, err error) {
+	r0, _, e1 := Syscall(SYS_ADJTIMEX, uintptr(unsafe.Pointer(buf)), 0, 0)
+	state = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chdir(path string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Chroot(path string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Close(fd int) (err error) {
+	_, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup(oldfd int) (fd int, err error) {
+	r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup3(oldfd int, newfd int, flags int) (err error) {
+	_, _, e1 := Syscall(SYS_DUP3, uintptr(oldfd), uintptr(newfd), uintptr(flags))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func EpollCreate(size int) (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func EpollCreate1(flag int) (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE1, uintptr(flag), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
+	_, _, e1 := RawSyscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(events) > 0 {
+		_p0 = unsafe.Pointer(&events[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Exit(code int) {
+	Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fallocate(fd int, mode uint32, off int64, len int64) (err error) {
+	_, _, e1 := Syscall6(SYS_FALLOCATE, uintptr(fd), uintptr(mode), uintptr(off), uintptr(off>>32), uintptr(len), uintptr(len>>32))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchdir(fd int) (err error) {
+	_, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchmod(fd int, mode uint32) (err error) {
+	_, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid), uintptr(flags), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func fcntl(fd int, cmd int, arg int) (val int, err error) {
+	r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg))
+	val = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fdatasync(fd int) (err error) {
+	_, _, e1 := Syscall(SYS_FDATASYNC, uintptr(fd), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Flock(fd int, how int) (err error) {
+	_, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fsync(fd int) (err error) {
+	_, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getdents(fd int, buf []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(_SYS_getdents, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpgid(pid int) (pgid int, err error) {
+	r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0)
+	pgid = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpid() (pid int) {
+	r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
+	pid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getppid() (ppid int) {
+	r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0)
+	ppid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getpriority(which int, who int) (prio int, err error) {
+	r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0)
+	prio = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getrusage(who int, rusage *Rusage) (err error) {
+	_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Gettid() (tid int) {
+	r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
+	tid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(attr)
+	if err != nil {
+		return
+	}
+	var _p2 unsafe.Pointer
+	if len(dest) > 0 {
+		_p2 = unsafe.Pointer(&dest[0])
+	} else {
+		_p2 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	sz = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(pathname)
+	if err != nil {
+		return
+	}
+	r0, _, e1 := Syscall(SYS_INOTIFY_ADD_WATCH, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(mask))
+	use(unsafe.Pointer(_p0))
+	watchdesc = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func InotifyInit1(flags int) (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func InotifyRmWatch(fd int, watchdesc uint32) (success int, err error) {
+	r0, _, e1 := RawSyscall(SYS_INOTIFY_RM_WATCH, uintptr(fd), uintptr(watchdesc), 0)
+	success = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Kill(pid int, sig Signal) (err error) {
+	_, _, e1 := RawSyscall(SYS_KILL, uintptr(pid), uintptr(sig), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Klogctl(typ int, buf []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_SYSLOG, uintptr(typ), uintptr(_p0), uintptr(len(buf)))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listxattr(path string, dest []byte) (sz int, err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var _p1 unsafe.Pointer
+	if len(dest) > 0 {
+		_p1 = unsafe.Pointer(&dest[0])
+	} else {
+		_p1 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
+	use(unsafe.Pointer(_p0))
+	sz = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mkdirat(dirfd int, path string, mode uint32) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode))
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Nanosleep(time *Timespec, leftover *Timespec) (err error) {
+	_, _, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pause() (err error) {
+	_, _, e1 := Syscall(SYS_PAUSE, 0, 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func PivotRoot(newroot string, putold string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(newroot)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(putold)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func read(fd int, p []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Removexattr(path string, attr string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(attr)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(oldpath)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(newpath)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(_p0)), uintptr(newdirfd), uintptr(unsafe.Pointer(_p1)), 0, 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setdomainname(p []byte) (err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_SETDOMAINNAME, uintptr(_p0), uintptr(len(p)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sethostname(p []byte) (err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_SETHOSTNAME, uintptr(_p0), uintptr(len(p)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpgid(pid int, pgid int) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setsid() (pid int, err error) {
+	r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0)
+	pid = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Settimeofday(tv *Timeval) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setpriority(which int, who int, prio int) (err error) {
+	_, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setxattr(path string, attr string, data []byte, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var _p1 *byte
+	_p1, err = BytePtrFromString(attr)
+	if err != nil {
+		return
+	}
+	var _p2 unsafe.Pointer
+	if len(data) > 0 {
+		_p2 = unsafe.Pointer(&data[0])
+	} else {
+		_p2 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall6(SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
+	use(unsafe.Pointer(_p0))
+	use(unsafe.Pointer(_p1))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sync() {
+	Syscall(SYS_SYNC, 0, 0, 0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Sysinfo(info *Sysinfo_t) (err error) {
+	_, _, e1 := RawSyscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
+	r0, r1, e1 := Syscall6(SYS_TEE, uintptr(rfd), uintptr(wfd), uintptr(len), uintptr(flags), 0, 0)
+	n = int64(int64(r1)<<32 | int64(r0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Tgkill(tgid int, tid int, sig Signal) (err error) {
+	_, _, e1 := RawSyscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Times(tms *Tms) (ticks uintptr, err error) {
+	r0, _, e1 := RawSyscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0)
+	ticks = uintptr(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Umask(mask int) (oldmask int) {
+	r0, _, _ := RawSyscall(SYS_UMASK, uintptr(mask), 0, 0)
+	oldmask = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Uname(buf *Utsname) (err error) {
+	_, _, e1 := RawSyscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unmount(target string, flags int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(target)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_UMOUNT2, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Unshare(flags int) (err error) {
+	_, _, e1 := Syscall(SYS_UNSHARE, uintptr(flags), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ustat(dev int, ubuf *Ustat_t) (err error) {
+	_, _, e1 := Syscall(SYS_USTAT, uintptr(dev), uintptr(unsafe.Pointer(ubuf)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Utime(path string, buf *Utimbuf) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(buf)), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func write(fd int, p []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func exitThread(code int) (err error) {
+	_, _, e1 := Syscall(SYS_EXIT, uintptr(code), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func readlen(fd int, p *byte, np int) (n int, err error) {
+	r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func writelen(fd int, p *byte, np int) (n int, err error) {
+	r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func munmap(addr uintptr, length uintptr) (err error) {
+	_, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Madvise(b []byte, advice int) (err error) {
+	var _p0 unsafe.Pointer
+	if len(b) > 0 {
+		_p0 = unsafe.Pointer(&b[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_MADVISE, uintptr(_p0), uintptr(len(b)), uintptr(advice))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mprotect(b []byte, prot int) (err error) {
+	var _p0 unsafe.Pointer
+	if len(b) > 0 {
+		_p0 = unsafe.Pointer(&b[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_MPROTECT, uintptr(_p0), uintptr(len(b)), uintptr(prot))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mlock(b []byte) (err error) {
+	var _p0 unsafe.Pointer
+	if len(b) > 0 {
+		_p0 = unsafe.Pointer(&b[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_MLOCK, uintptr(_p0), uintptr(len(b)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Munlock(b []byte) (err error) {
+	var _p0 unsafe.Pointer
+	if len(b) > 0 {
+		_p0 = unsafe.Pointer(&b[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall(SYS_MUNLOCK, uintptr(_p0), uintptr(len(b)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Mlockall(flags int) (err error) {
+	_, _, e1 := Syscall(SYS_MLOCKALL, uintptr(flags), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Munlockall() (err error) {
+	_, _, e1 := Syscall(SYS_MUNLOCKALL, 0, 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Dup2(oldfd int, newfd int) (err error) {
+	_, _, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fchown(fd int, uid int, gid int) (err error) {
+	_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ftruncate(fd int, length int64) (err error) {
+	_, _, e1 := Syscall6(SYS_FTRUNCATE64, uintptr(fd), 0, uintptr(length), uintptr(length>>32), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getegid() (egid int) {
+	r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0)
+	egid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Geteuid() (euid int) {
+	r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0)
+	euid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getgid() (gid int) {
+	r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0)
+	gid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Getuid() (uid int) {
+	r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0)
+	uid = int(r0)
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lchown(path string, uid int, gid int) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid))
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Listen(s int, n int) (err error) {
+	_, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(n), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pread(fd int, p []byte, offset int64) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_PREAD64, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_PWRITE64, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) {
+	r0, _, e1 := Syscall6(SYS__NEWSELECT, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+	r0, _, e1 := Syscall6(SYS_SENDFILE64, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0)
+	written = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setfsgid(gid int) (err error) {
+	_, _, e1 := Syscall(SYS_SETFSGID, uintptr(gid), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setfsuid(uid int) (err error) {
+	_, _, e1 := Syscall(SYS_SETFSUID, uintptr(uid), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setregid(rgid int, egid int) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setresgid(rgid int, egid int, sgid int) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setresuid(ruid int, euid int, suid int) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Setreuid(ruid int, euid int) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Shutdown(fd int, how int) (err error) {
+	_, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(fd), uintptr(how), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) {
+	r0, r1, e1 := Syscall6(SYS_SPLICE, uintptr(rfd), uintptr(unsafe.Pointer(roff)), uintptr(wfd), uintptr(unsafe.Pointer(woff)), uintptr(len), uintptr(flags))
+	n = int64(int64(r1)<<32 | int64(r0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func SyncFileRange(fd int, off int64, n int64, flags int) (err error) {
+	_, _, e1 := Syscall9(SYS_SYNC_FILE_RANGE, uintptr(fd), 0, uintptr(off), uintptr(off>>32), uintptr(n), uintptr(n>>32), uintptr(flags), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Truncate(path string, length int64) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall6(SYS_TRUNCATE64, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
+	r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (fd int, err error) {
+	r0, _, e1 := Syscall6(SYS_ACCEPT4, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags), 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
+	_, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
+	_, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getgroups(n int, list *_Gid_t) (nn int, err error) {
+	r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+	nn = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setgroups(n int, list *_Gid_t) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) {
+	_, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) {
+	_, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socket(domain int, typ int, proto int) (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto))
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
+	_, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+	_, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) {
+	_, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(p) > 0 {
+		_p0 = unsafe.Pointer(&p[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	_, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
+	r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
+	r0, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags))
+	n = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func InotifyInit() (fd int, err error) {
+	r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0)
+	fd = int(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Ioperm(from int, num int, on int) (err error) {
+	_, _, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Iopl(level int) (err error) {
+	_, _, e1 := Syscall(SYS_IOPL, uintptr(level), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Gettimeofday(tv *Timeval) (err error) {
+	_, _, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Time(t *Time_t) (tt Time_t, err error) {
+	r0, _, e1 := RawSyscall(SYS_TIME, uintptr(unsafe.Pointer(t)), 0, 0)
+	tt = Time_t(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Lstat(path string, stat *Stat_t) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Fstat(fd int, stat *Stat_t) (err error) {
+	_, _, e1 := Syscall(SYS_FSTAT64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func Stat(path string, stat *Stat_t) (err error) {
+	var _p0 *byte
+	_p0, err = BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	_, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0)
+	use(unsafe.Pointer(_p0))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func pipe2(p *[2]_C_int, flags int) (err error) {
+	_, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) {
+	r0, _, e1 := Syscall6(SYS_MMAP2, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(pageOffset))
+	xaddr = uintptr(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getrlimit(resource int, rlim *rlimit32) (err error) {
+	_, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setrlimit(resource int, rlim *rlimit32) (err error) {
+	_, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
diff --git a/src/syscall/zsyscall_linux_ppc64.go b/src/syscall/zsyscall_linux_ppc64.go
index 352525f..b5315cd 100644
--- a/src/syscall/zsyscall_linux_ppc64.go
+++ b/src/syscall/zsyscall_linux_ppc64.go
@@ -763,8 +763,8 @@ func PivotRoot(newroot string, putold string) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
-	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
diff --git a/src/syscall/zsyscall_linux_ppc64le.go b/src/syscall/zsyscall_linux_ppc64le.go
index ac49c5c..501d680 100644
--- a/src/syscall/zsyscall_linux_ppc64le.go
+++ b/src/syscall/zsyscall_linux_ppc64le.go
@@ -763,8 +763,8 @@ func PivotRoot(newroot string, putold string) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
-	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
diff --git a/src/syscall/zsyscall_linux_s390x.go b/src/syscall/zsyscall_linux_s390x.go
index fd15366..b33ce68 100644
--- a/src/syscall/zsyscall_linux_s390x.go
+++ b/src/syscall/zsyscall_linux_s390x.go
@@ -761,8 +761,8 @@ func PivotRoot(newroot string, putold string) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
-	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
+func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
+	_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
diff --git a/src/syscall/zsyscall_solaris_amd64.go b/src/syscall/zsyscall_solaris_amd64.go
index ebdeb92..8cc3740 100644
--- a/src/syscall/zsyscall_solaris_amd64.go
+++ b/src/syscall/zsyscall_solaris_amd64.go
@@ -12,7 +12,7 @@ import "unsafe"
 //go:cgo_import_dynamic libc_setgroups setgroups "libc.so"
 //go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
 //go:cgo_import_dynamic libc_accept accept "libsocket.so"
-//go:cgo_import_dynamic libc_sendmsg sendmsg "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_sendmsg __xnet_sendmsg "libsocket.so"
 //go:cgo_import_dynamic libc_Access access "libc.so"
 //go:cgo_import_dynamic libc_Adjtime adjtime "libc.so"
 //go:cgo_import_dynamic libc_Chdir chdir "libc.so"
@@ -40,7 +40,7 @@ import "unsafe"
 //go:cgo_import_dynamic libc_Kill kill "libc.so"
 //go:cgo_import_dynamic libc_Lchown lchown "libc.so"
 //go:cgo_import_dynamic libc_Link link "libc.so"
-//go:cgo_import_dynamic libc_listen listen "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_listen __xnet_listen "libsocket.so"
 //go:cgo_import_dynamic libc_Lstat lstat "libc.so"
 //go:cgo_import_dynamic libc_Mkdir mkdir "libc.so"
 //go:cgo_import_dynamic libc_Mknod mknod "libc.so"
@@ -75,27 +75,28 @@ import "unsafe"
 //go:cgo_import_dynamic libc_Umask umask "libc.so"
 //go:cgo_import_dynamic libc_Unlink unlink "libc.so"
 //go:cgo_import_dynamic libc_Utimes utimes "libc.so"
-//go:cgo_import_dynamic libc_bind bind "libsocket.so"
-//go:cgo_import_dynamic libc_connect connect "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_bind __xnet_bind "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_connect __xnet_connect "libsocket.so"
 //go:cgo_import_dynamic libc_mmap mmap "libc.so"
 //go:cgo_import_dynamic libc_munmap munmap "libc.so"
-//go:cgo_import_dynamic libc_sendto sendto "libsocket.so"
-//go:cgo_import_dynamic libc_socket socket "libsocket.so"
-//go:cgo_import_dynamic libc_socketpair socketpair "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_sendto __xnet_sendto "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_socket __xnet_socket "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_socketpair __xnet_socketpair "libsocket.so"
 //go:cgo_import_dynamic libc_write write "libc.so"
-//go:cgo_import_dynamic libc_getsockopt getsockopt "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_getsockopt __xnet_getsockopt "libsocket.so"
 //go:cgo_import_dynamic libc_getpeername getpeername "libsocket.so"
 //go:cgo_import_dynamic libc_getsockname getsockname "libsocket.so"
 //go:cgo_import_dynamic libc_setsockopt setsockopt "libsocket.so"
 //go:cgo_import_dynamic libc_recvfrom recvfrom "libsocket.so"
-//go:cgo_import_dynamic libc_recvmsg recvmsg "libsocket.so"
+//go:cgo_import_dynamic libc___xnet_recvmsg __xnet_recvmsg "libsocket.so"
+//go:cgo_import_dynamic libc_getexecname getexecname "libc.so"
 
 //go:linkname libc_Getcwd libc_Getcwd
 //go:linkname libc_getgroups libc_getgroups
 //go:linkname libc_setgroups libc_setgroups
 //go:linkname libc_fcntl libc_fcntl
 //go:linkname libc_accept libc_accept
-//go:linkname libc_sendmsg libc_sendmsg
+//go:linkname libc___xnet_sendmsg libc___xnet_sendmsg
 //go:linkname libc_Access libc_Access
 //go:linkname libc_Adjtime libc_Adjtime
 //go:linkname libc_Chdir libc_Chdir
@@ -123,7 +124,7 @@ import "unsafe"
 //go:linkname libc_Kill libc_Kill
 //go:linkname libc_Lchown libc_Lchown
 //go:linkname libc_Link libc_Link
-//go:linkname libc_listen libc_listen
+//go:linkname libc___xnet_listen libc___xnet_listen
 //go:linkname libc_Lstat libc_Lstat
 //go:linkname libc_Mkdir libc_Mkdir
 //go:linkname libc_Mknod libc_Mknod
@@ -158,20 +159,21 @@ import "unsafe"
 //go:linkname libc_Umask libc_Umask
 //go:linkname libc_Unlink libc_Unlink
 //go:linkname libc_Utimes libc_Utimes
-//go:linkname libc_bind libc_bind
-//go:linkname libc_connect libc_connect
+//go:linkname libc___xnet_bind libc___xnet_bind
+//go:linkname libc___xnet_connect libc___xnet_connect
 //go:linkname libc_mmap libc_mmap
 //go:linkname libc_munmap libc_munmap
-//go:linkname libc_sendto libc_sendto
-//go:linkname libc_socket libc_socket
-//go:linkname libc_socketpair libc_socketpair
+//go:linkname libc___xnet_sendto libc___xnet_sendto
+//go:linkname libc___xnet_socket libc___xnet_socket
+//go:linkname libc___xnet_socketpair libc___xnet_socketpair
 //go:linkname libc_write libc_write
-//go:linkname libc_getsockopt libc_getsockopt
+//go:linkname libc___xnet_getsockopt libc___xnet_getsockopt
 //go:linkname libc_getpeername libc_getpeername
 //go:linkname libc_getsockname libc_getsockname
 //go:linkname libc_setsockopt libc_setsockopt
 //go:linkname libc_recvfrom libc_recvfrom
-//go:linkname libc_recvmsg libc_recvmsg
+//go:linkname libc___xnet_recvmsg libc___xnet_recvmsg
+//go:linkname libc_getexecname libc_getexecname
 
 type libcFunc uintptr
 
@@ -181,7 +183,7 @@ var (
 	libc_setgroups,
 	libc_fcntl,
 	libc_accept,
-	libc_sendmsg,
+	libc___xnet_sendmsg,
 	libc_Access,
 	libc_Adjtime,
 	libc_Chdir,
@@ -209,7 +211,7 @@ var (
 	libc_Kill,
 	libc_Lchown,
 	libc_Link,
-	libc_listen,
+	libc___xnet_listen,
 	libc_Lstat,
 	libc_Mkdir,
 	libc_Mknod,
@@ -244,20 +246,21 @@ var (
 	libc_Umask,
 	libc_Unlink,
 	libc_Utimes,
-	libc_bind,
-	libc_connect,
+	libc___xnet_bind,
+	libc___xnet_connect,
 	libc_mmap,
 	libc_munmap,
-	libc_sendto,
-	libc_socket,
-	libc_socketpair,
+	libc___xnet_sendto,
+	libc___xnet_socket,
+	libc___xnet_socketpair,
 	libc_write,
-	libc_getsockopt,
+	libc___xnet_getsockopt,
 	libc_getpeername,
 	libc_getsockname,
 	libc_setsockopt,
 	libc_recvfrom,
-	libc_recvmsg libcFunc
+	libc___xnet_recvmsg,
+	libc_getexecname libcFunc
 )
 
 func Getcwd(buf []byte) (n int, err error) {
@@ -309,7 +312,7 @@ func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
 }
 
 func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
-	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_sendmsg)), 3, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags), 0, 0, 0)
+	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc___xnet_sendmsg)), 3, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags), 0, 0, 0)
 	n = int(r0)
 	if e1 != 0 {
 		err = errnoErr(e1)
@@ -575,7 +578,7 @@ func Link(path string, link string) (err error) {
 }
 
 func Listen(s int, backlog int) (err error) {
-	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_listen)), 2, uintptr(s), uintptr(backlog), 0, 0, 0, 0)
+	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc___xnet_listen)), 2, uintptr(s), uintptr(backlog), 0, 0, 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
@@ -953,7 +956,7 @@ func Unlink(path string) (err error) {
 	return
 }
 
-func Utimes(path string, times *[2]Timeval) (err error) {
+func utimes(path string, times *[2]Timeval) (err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(path)
 	if err != nil {
@@ -968,7 +971,7 @@ func Utimes(path string, times *[2]Timeval) (err error) {
 }
 
 func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
-	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_bind)), 3, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0)
+	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc___xnet_bind)), 3, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
@@ -976,7 +979,7 @@ func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
 }
 
 func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) {
-	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_connect)), 3, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0)
+	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc___xnet_connect)), 3, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
@@ -1005,7 +1008,7 @@ func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (
 	if len(buf) > 0 {
 		_p0 = &buf[0]
 	}
-	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_sendto)), 6, uintptr(s), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen))
+	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc___xnet_sendto)), 6, uintptr(s), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen))
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
@@ -1013,7 +1016,7 @@ func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (
 }
 
 func socket(domain int, typ int, proto int) (fd int, err error) {
-	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_socket)), 3, uintptr(domain), uintptr(typ), uintptr(proto), 0, 0, 0)
+	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc___xnet_socket)), 3, uintptr(domain), uintptr(typ), uintptr(proto), 0, 0, 0)
 	fd = int(r0)
 	if e1 != 0 {
 		err = errnoErr(e1)
@@ -1022,7 +1025,7 @@ func socket(domain int, typ int, proto int) (fd int, err error) {
 }
 
 func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) {
-	_, _, e1 := rawSysvicall6(uintptr(unsafe.Pointer(&libc_socketpair)), 4, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
+	_, _, e1 := rawSysvicall6(uintptr(unsafe.Pointer(&libc___xnet_socketpair)), 4, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
@@ -1043,7 +1046,7 @@ func write(fd int, p []byte) (n int, err error) {
 }
 
 func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) {
-	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_getsockopt)), 5, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
+	_, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc___xnet_getsockopt)), 5, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
@@ -1088,10 +1091,19 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl
 }
 
 func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) {
-	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_recvmsg)), 3, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags), 0, 0, 0)
+	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc___xnet_recvmsg)), 3, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags), 0, 0, 0)
 	n = int(r0)
 	if e1 != 0 {
 		err = errnoErr(e1)
 	}
 	return
 }
+
+func getexecname() (path unsafe.Pointer, err error) {
+	r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_getexecname)), 0, 0, 0, 0, 0, 0, 0)
+	path = unsafe.Pointer(r0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
diff --git a/src/syscall/zsyscall_windows.go b/src/syscall/zsyscall_windows.go
index bb3e892..2283c79 100644
--- a/src/syscall/zsyscall_windows.go
+++ b/src/syscall/zsyscall_windows.go
@@ -9,6 +9,31 @@ import (
 
 var _ unsafe.Pointer
 
+// Do the interface allocations only once for common
+// Errno values.
+const (
+	errnoERROR_IO_PENDING = 997
+)
+
+var (
+	errERROR_IO_PENDING error = Errno(errnoERROR_IO_PENDING)
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e Errno) error {
+	switch e {
+	case 0:
+		return nil
+	case errnoERROR_IO_PENDING:
+		return errERROR_IO_PENDING
+	}
+	// TODO: add more here, after collecting data on the common
+	// error values see on Windows. (perhaps when running
+	// all.bat?)
+	return e
+}
+
 var (
 	modkernel32 = NewLazyDLL(sysdll.Add("kernel32.dll"))
 	modadvapi32 = NewLazyDLL(sysdll.Add("advapi32.dll"))
@@ -188,7 +213,7 @@ func _LoadLibrary(libname *uint16) (handle Handle, err error) {
 	handle = Handle(r0)
 	if handle == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -200,7 +225,7 @@ func FreeLibrary(handle Handle) (err error) {
 	r1, _, e1 := Syscall(procFreeLibrary.Addr(), 1, uintptr(handle), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -222,7 +247,7 @@ func _GetProcAddress(module Handle, procname *byte) (proc uintptr, err error) {
 	proc = uintptr(r0)
 	if proc == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -235,7 +260,7 @@ func GetVersion() (ver uint32, err error) {
 	ver = uint32(r0)
 	if ver == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -252,7 +277,7 @@ func formatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, bu
 	n = uint32(r0)
 	if n == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -270,7 +295,7 @@ func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes
 	handle = Handle(r0)
 	if handle == InvalidHandle {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -286,7 +311,7 @@ func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (
 	r1, _, e1 := Syscall6(procReadFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -302,7 +327,7 @@ func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped)
 	r1, _, e1 := Syscall6(procWriteFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -315,7 +340,7 @@ func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence
 	newlowoffset = uint32(r0)
 	if newlowoffset == 0xffffffff {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -327,7 +352,7 @@ func CloseHandle(handle Handle) (err error) {
 	r1, _, e1 := Syscall(procCloseHandle.Addr(), 1, uintptr(handle), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -340,7 +365,7 @@ func GetStdHandle(stdhandle int) (handle Handle, err error) {
 	handle = Handle(r0)
 	if handle == InvalidHandle {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -353,7 +378,7 @@ func findFirstFile1(name *uint16, data *win32finddata1) (handle Handle, err erro
 	handle = Handle(r0)
 	if handle == InvalidHandle {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -365,7 +390,7 @@ func findNextFile1(handle Handle, data *win32finddata1) (err error) {
 	r1, _, e1 := Syscall(procFindNextFileW.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -377,7 +402,7 @@ func FindClose(handle Handle) (err error) {
 	r1, _, e1 := Syscall(procFindClose.Addr(), 1, uintptr(handle), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -389,7 +414,7 @@ func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (e
 	r1, _, e1 := Syscall(procGetFileInformationByHandle.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -402,7 +427,7 @@ func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, err error) {
 	n = uint32(r0)
 	if n == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -414,7 +439,7 @@ func SetCurrentDirectory(path *uint16) (err error) {
 	r1, _, e1 := Syscall(procSetCurrentDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -426,7 +451,7 @@ func CreateDirectory(path *uint16, sa *SecurityAttributes) (err error) {
 	r1, _, e1 := Syscall(procCreateDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -438,7 +463,7 @@ func RemoveDirectory(path *uint16) (err error) {
 	r1, _, e1 := Syscall(procRemoveDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -450,7 +475,7 @@ func DeleteFile(path *uint16) (err error) {
 	r1, _, e1 := Syscall(procDeleteFileW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -462,7 +487,7 @@ func MoveFile(from *uint16, to *uint16) (err error) {
 	r1, _, e1 := Syscall(procMoveFileW.Addr(), 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -474,7 +499,7 @@ func GetComputerName(buf *uint16, n *uint32) (err error) {
 	r1, _, e1 := Syscall(procGetComputerNameW.Addr(), 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -486,7 +511,7 @@ func SetEndOfFile(handle Handle) (err error) {
 	r1, _, e1 := Syscall(procSetEndOfFile.Addr(), 1, uintptr(handle), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -504,7 +529,7 @@ func GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, err error) {
 	rc = uint32(r0)
 	if rc == 0xffffffff {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -517,7 +542,7 @@ func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, thre
 	handle = Handle(r0)
 	if handle == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -529,7 +554,7 @@ func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overla
 	r1, _, e1 := Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -541,7 +566,7 @@ func PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlap
 	r1, _, e1 := Syscall6(procPostQueuedCompletionStatus.Addr(), 4, uintptr(cphandle), uintptr(qty), uintptr(key), uintptr(unsafe.Pointer(overlapped)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -553,7 +578,7 @@ func CancelIo(s Handle) (err error) {
 	r1, _, e1 := Syscall(procCancelIo.Addr(), 1, uintptr(s), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -565,7 +590,7 @@ func CancelIoEx(s Handle, o *Overlapped) (err error) {
 	r1, _, e1 := Syscall(procCancelIoEx.Addr(), 2, uintptr(s), uintptr(unsafe.Pointer(o)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -583,7 +608,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA
 	r1, _, e1 := Syscall12(procCreateProcessW.Addr(), 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -602,7 +627,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err
 	handle = Handle(r0)
 	if handle == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -614,7 +639,7 @@ func TerminateProcess(handle Handle, exitcode uint32) (err error) {
 	r1, _, e1 := Syscall(procTerminateProcess.Addr(), 2, uintptr(handle), uintptr(exitcode), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -626,7 +651,7 @@ func GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) {
 	r1, _, e1 := Syscall(procGetExitCodeProcess.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -638,7 +663,7 @@ func GetStartupInfo(startupInfo *StartupInfo) (err error) {
 	r1, _, e1 := Syscall(procGetStartupInfoW.Addr(), 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -651,7 +676,7 @@ func GetCurrentProcess() (pseudoHandle Handle, err error) {
 	pseudoHandle = Handle(r0)
 	if pseudoHandle == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -663,7 +688,7 @@ func GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime,
 	r1, _, e1 := Syscall6(procGetProcessTimes.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(creationTime)), uintptr(unsafe.Pointer(exitTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -681,7 +706,7 @@ func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetP
 	r1, _, e1 := Syscall9(procDuplicateHandle.Addr(), 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -694,7 +719,7 @@ func WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32,
 	event = uint32(r0)
 	if event == 0xffffffff {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -707,7 +732,7 @@ func GetTempPath(buflen uint32, buf *uint16) (n uint32, err error) {
 	n = uint32(r0)
 	if n == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -719,7 +744,7 @@ func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes,
 	r1, _, e1 := Syscall6(procCreatePipe.Addr(), 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -732,7 +757,7 @@ func GetFileType(filehandle Handle) (n uint32, err error) {
 	n = uint32(r0)
 	if n == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -744,7 +769,7 @@ func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16
 	r1, _, e1 := Syscall6(procCryptAcquireContextW.Addr(), 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -756,7 +781,7 @@ func CryptReleaseContext(provhandle Handle, flags uint32) (err error) {
 	r1, _, e1 := Syscall(procCryptReleaseContext.Addr(), 2, uintptr(provhandle), uintptr(flags), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -768,7 +793,7 @@ func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (err error) {
 	r1, _, e1 := Syscall(procCryptGenRandom.Addr(), 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf)))
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -781,7 +806,7 @@ func GetEnvironmentStrings() (envs *uint16, err error) {
 	envs = (*uint16)(unsafe.Pointer(r0))
 	if envs == nil {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -793,7 +818,7 @@ func FreeEnvironmentStrings(envs *uint16) (err error) {
 	r1, _, e1 := Syscall(procFreeEnvironmentStringsW.Addr(), 1, uintptr(unsafe.Pointer(envs)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -806,7 +831,7 @@ func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32
 	n = uint32(r0)
 	if n == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -818,7 +843,7 @@ func SetEnvironmentVariable(name *uint16, value *uint16) (err error) {
 	r1, _, e1 := Syscall(procSetEnvironmentVariableW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -830,7 +855,7 @@ func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetim
 	r1, _, e1 := Syscall6(procSetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -843,7 +868,7 @@ func GetFileAttributes(name *uint16) (attrs uint32, err error) {
 	attrs = uint32(r0)
 	if attrs == INVALID_FILE_ATTRIBUTES {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -855,7 +880,7 @@ func SetFileAttributes(name *uint16, attrs uint32) (err error) {
 	r1, _, e1 := Syscall(procSetFileAttributesW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(attrs), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -867,7 +892,7 @@ func GetFileAttributesEx(name *uint16, level uint32, info *byte) (err error) {
 	r1, _, e1 := Syscall(procGetFileAttributesExW.Addr(), 3, uintptr(unsafe.Pointer(name)), uintptr(level), uintptr(unsafe.Pointer(info)))
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -886,7 +911,7 @@ func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err
 	argv = (*[8192]*[8192]uint16)(unsafe.Pointer(r0))
 	if argv == nil {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -899,7 +924,7 @@ func LocalFree(hmem Handle) (handle Handle, err error) {
 	handle = Handle(r0)
 	if handle != 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -911,7 +936,7 @@ func SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error)
 	r1, _, e1 := Syscall(procSetHandleInformation.Addr(), 3, uintptr(handle), uintptr(mask), uintptr(flags))
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -923,7 +948,7 @@ func FlushFileBuffers(handle Handle) (err error) {
 	r1, _, e1 := Syscall(procFlushFileBuffers.Addr(), 1, uintptr(handle), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -936,7 +961,7 @@ func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (
 	n = uint32(r0)
 	if n == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -949,7 +974,7 @@ func GetLongPathName(path *uint16, buf *uint16, buflen uint32) (n uint32, err er
 	n = uint32(r0)
 	if n == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -962,7 +987,7 @@ func GetShortPathName(longpath *uint16, shortpath *uint16, buflen uint32) (n uin
 	n = uint32(r0)
 	if n == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -975,7 +1000,7 @@ func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxS
 	handle = Handle(r0)
 	if handle == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -988,7 +1013,7 @@ func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow ui
 	addr = uintptr(r0)
 	if addr == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1000,7 +1025,7 @@ func UnmapViewOfFile(addr uintptr) (err error) {
 	r1, _, e1 := Syscall(procUnmapViewOfFile.Addr(), 1, uintptr(addr), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1012,7 +1037,7 @@ func FlushViewOfFile(addr uintptr, length uintptr) (err error) {
 	r1, _, e1 := Syscall(procFlushViewOfFile.Addr(), 2, uintptr(addr), uintptr(length), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1024,7 +1049,7 @@ func VirtualLock(addr uintptr, length uintptr) (err error) {
 	r1, _, e1 := Syscall(procVirtualLock.Addr(), 2, uintptr(addr), uintptr(length), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1036,7 +1061,7 @@ func VirtualUnlock(addr uintptr, length uintptr) (err error) {
 	r1, _, e1 := Syscall(procVirtualUnlock.Addr(), 2, uintptr(addr), uintptr(length), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1048,7 +1073,7 @@ func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint
 	r1, _, e1 := Syscall9(procTransmitFile.Addr(), 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1066,7 +1091,7 @@ func ReadDirectoryChanges(handle Handle, buf *byte, buflen uint32, watchSubTree
 	r1, _, e1 := Syscall9(procReadDirectoryChangesW.Addr(), 8, uintptr(handle), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(_p0), uintptr(mask), uintptr(unsafe.Pointer(retlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1079,7 +1104,7 @@ func CertOpenSystemStore(hprov Handle, name *uint16) (store Handle, err error) {
 	store = Handle(r0)
 	if store == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1092,7 +1117,7 @@ func CertOpenStore(storeProvider uintptr, msgAndCertEncodingType uint32, cryptPr
 	handle = Handle(r0)
 	if handle == InvalidHandle {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1105,7 +1130,7 @@ func CertEnumCertificatesInStore(store Handle, prevContext *CertContext) (contex
 	context = (*CertContext)(unsafe.Pointer(r0))
 	if context == nil {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1117,7 +1142,7 @@ func CertAddCertificateContextToStore(store Handle, certContext *CertContext, ad
 	r1, _, e1 := Syscall6(procCertAddCertificateContextToStore.Addr(), 4, uintptr(store), uintptr(unsafe.Pointer(certContext)), uintptr(addDisposition), uintptr(unsafe.Pointer(storeContext)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1129,7 +1154,7 @@ func CertCloseStore(store Handle, flags uint32) (err error) {
 	r1, _, e1 := Syscall(procCertCloseStore.Addr(), 2, uintptr(store), uintptr(flags), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1141,7 +1166,7 @@ func CertGetCertificateChain(engine Handle, leaf *CertContext, time *Filetime, a
 	r1, _, e1 := Syscall9(procCertGetCertificateChain.Addr(), 8, uintptr(engine), uintptr(unsafe.Pointer(leaf)), uintptr(unsafe.Pointer(time)), uintptr(additionalStore), uintptr(unsafe.Pointer(para)), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(chainCtx)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1159,7 +1184,7 @@ func CertCreateCertificateContext(certEncodingType uint32, certEncoded *byte, en
 	context = (*CertContext)(unsafe.Pointer(r0))
 	if context == nil {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1171,7 +1196,7 @@ func CertFreeCertificateContext(ctx *CertContext) (err error) {
 	r1, _, e1 := Syscall(procCertFreeCertificateContext.Addr(), 1, uintptr(unsafe.Pointer(ctx)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1183,7 +1208,7 @@ func CertVerifyCertificateChainPolicy(policyOID uintptr, chain *CertChainContext
 	r1, _, e1 := Syscall6(procCertVerifyCertificateChainPolicy.Addr(), 4, uintptr(policyOID), uintptr(unsafe.Pointer(chain)), uintptr(unsafe.Pointer(para)), uintptr(unsafe.Pointer(status)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1241,7 +1266,7 @@ func GetConsoleMode(console Handle, mode *uint32) (err error) {
 	r1, _, e1 := Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1253,7 +1278,7 @@ func WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32,
 	r1, _, e1 := Syscall6(procWriteConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(towrite), uintptr(unsafe.Pointer(written)), uintptr(unsafe.Pointer(reserved)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1265,7 +1290,7 @@ func ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, input
 	r1, _, e1 := Syscall6(procReadConsoleW.Addr(), 5, uintptr(console), uintptr(unsafe.Pointer(buf)), uintptr(toread), uintptr(unsafe.Pointer(read)), uintptr(unsafe.Pointer(inputControl)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1278,7 +1303,7 @@ func CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, er
 	handle = Handle(r0)
 	if handle == InvalidHandle {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1290,7 +1315,7 @@ func Process32First(snapshot Handle, procEntry *ProcessEntry32) (err error) {
 	r1, _, e1 := Syscall(procProcess32FirstW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(procEntry)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1302,7 +1327,7 @@ func Process32Next(snapshot Handle, procEntry *ProcessEntry32) (err error) {
 	r1, _, e1 := Syscall(procProcess32NextW.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(procEntry)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1314,7 +1339,7 @@ func DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBuff
 	r1, _, e1 := Syscall9(procDeviceIoControl.Addr(), 8, uintptr(handle), uintptr(ioControlCode), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferSize), uintptr(unsafe.Pointer(outBuffer)), uintptr(outBufferSize), uintptr(unsafe.Pointer(bytesReturned)), uintptr(unsafe.Pointer(overlapped)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1326,7 +1351,7 @@ func CreateSymbolicLink(symlinkfilename *uint16, targetfilename *uint16, flags u
 	r1, _, e1 := Syscall(procCreateSymbolicLinkW.Addr(), 3, uintptr(unsafe.Pointer(symlinkfilename)), uintptr(unsafe.Pointer(targetfilename)), uintptr(flags))
 	if r1&0xff == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1338,7 +1363,7 @@ func CreateHardLink(filename *uint16, existingfilename *uint16, reserved uintptr
 	r1, _, e1 := Syscall(procCreateHardLinkW.Addr(), 3, uintptr(unsafe.Pointer(filename)), uintptr(unsafe.Pointer(existingfilename)), uintptr(reserved))
 	if r1&0xff == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1358,7 +1383,7 @@ func WSACleanup() (err error) {
 	r1, _, e1 := Syscall(procWSACleanup.Addr(), 0, 0, 0, 0)
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1370,7 +1395,7 @@ func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbo
 	r1, _, e1 := Syscall9(procWSAIoctl.Addr(), 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine))
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1383,7 +1408,7 @@ func socket(af int32, typ int32, protocol int32) (handle Handle, err error) {
 	handle = Handle(r0)
 	if handle == InvalidHandle {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1395,7 +1420,7 @@ func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32
 	r1, _, e1 := Syscall6(procsetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0)
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1407,7 +1432,7 @@ func Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int3
 	r1, _, e1 := Syscall6(procgetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(unsafe.Pointer(optlen)), 0)
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1419,7 +1444,7 @@ func bind(s Handle, name unsafe.Pointer, namelen int32) (err error) {
 	r1, _, e1 := Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1431,7 +1456,7 @@ func connect(s Handle, name unsafe.Pointer, namelen int32) (err error) {
 	r1, _, e1 := Syscall(procconnect.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1443,7 +1468,7 @@ func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
 	r1, _, e1 := Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1455,7 +1480,7 @@ func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (err error) {
 	r1, _, e1 := Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1467,7 +1492,7 @@ func listen(s Handle, backlog int32) (err error) {
 	r1, _, e1 := Syscall(proclisten.Addr(), 2, uintptr(s), uintptr(backlog), 0)
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1479,7 +1504,7 @@ func shutdown(s Handle, how int32) (err error) {
 	r1, _, e1 := Syscall(procshutdown.Addr(), 2, uintptr(s), uintptr(how), 0)
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1491,7 +1516,7 @@ func Closesocket(s Handle) (err error) {
 	r1, _, e1 := Syscall(procclosesocket.Addr(), 1, uintptr(s), 0, 0)
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1503,7 +1528,7 @@ func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32
 	r1, _, e1 := Syscall9(procAcceptEx.Addr(), 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1520,7 +1545,7 @@ func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32
 	r1, _, e1 := Syscall9(procWSARecv.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1532,7 +1557,7 @@ func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32,
 	r1, _, e1 := Syscall9(procWSASend.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1544,7 +1569,7 @@ func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *ui
 	r1, _, e1 := Syscall9(procWSARecvFrom.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1556,7 +1581,7 @@ func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32
 	r1, _, e1 := Syscall9(procWSASendTo.Addr(), 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
 	if r1 == socket_error {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1578,7 +1603,7 @@ func _GetHostByName(name *byte) (h *Hostent, err error) {
 	h = (*Hostent)(unsafe.Pointer(r0))
 	if h == nil {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1605,7 +1630,7 @@ func _GetServByName(name *byte, proto *byte) (s *Servent, err error) {
 	s = (*Servent)(unsafe.Pointer(r0))
 	if s == nil {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1633,7 +1658,7 @@ func _GetProtoByName(name *byte) (p *Protoent, err error) {
 	p = (*Protoent)(unsafe.Pointer(r0))
 	if p == nil {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1702,7 +1727,7 @@ func SetFileCompletionNotificationModes(handle Handle, flags uint8) (err error)
 	r1, _, e1 := Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(handle), uintptr(flags), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1715,7 +1740,7 @@ func WSAEnumProtocols(protocols *int32, protocolBuffer *WSAProtocolInfo, bufferL
 	n = int32(r0)
 	if n == -1 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1727,7 +1752,7 @@ func TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint
 	r1, _, e1 := Syscall6(procTranslateNameW.Addr(), 5, uintptr(unsafe.Pointer(accName)), uintptr(accNameFormat), uintptr(desiredNameFormat), uintptr(unsafe.Pointer(translatedName)), uintptr(unsafe.Pointer(nSize)), 0)
 	if r1&0xff == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1739,7 +1764,7 @@ func GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err er
 	r1, _, e1 := Syscall(procGetUserNameExW.Addr(), 3, uintptr(nameFormat), uintptr(unsafe.Pointer(nameBuffre)), uintptr(unsafe.Pointer(nSize)))
 	if r1&0xff == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1775,7 +1800,7 @@ func LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint3
 	r1, _, e1 := Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1787,7 +1812,7 @@ func LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen
 	r1, _, e1 := Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidLen)), uintptr(unsafe.Pointer(refdDomainName)), uintptr(unsafe.Pointer(refdDomainNameLen)), uintptr(unsafe.Pointer(use)), 0, 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1799,7 +1824,7 @@ func ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) {
 	r1, _, e1 := Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(stringSid)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1811,7 +1836,7 @@ func ConvertStringSidToSid(stringSid *uint16, sid **SID) (err error) {
 	r1, _, e1 := Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(stringSid)), uintptr(unsafe.Pointer(sid)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1829,7 +1854,7 @@ func CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) {
 	r1, _, e1 := Syscall(procCopySid.Addr(), 3, uintptr(destSidLen), uintptr(unsafe.Pointer(destSid)), uintptr(unsafe.Pointer(srcSid)))
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1841,7 +1866,7 @@ func OpenProcessToken(h Handle, access uint32, token *Token) (err error) {
 	r1, _, e1 := Syscall(procOpenProcessToken.Addr(), 3, uintptr(h), uintptr(access), uintptr(unsafe.Pointer(token)))
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1853,7 +1878,7 @@ func GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32,
 	r1, _, e1 := Syscall6(procGetTokenInformation.Addr(), 5, uintptr(t), uintptr(infoClass), uintptr(unsafe.Pointer(info)), uintptr(infoLen), uintptr(unsafe.Pointer(returnedLen)), 0)
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
@@ -1865,7 +1890,7 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
 	r1, _, e1 := Syscall(procGetUserProfileDirectoryW.Addr(), 3, uintptr(t), uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen)))
 	if r1 == 0 {
 		if e1 != 0 {
-			err = error(e1)
+			err = errnoErr(e1)
 		} else {
 			err = EINVAL
 		}
diff --git a/src/syscall/zsysnum_linux_mips.go b/src/syscall/zsysnum_linux_mips.go
new file mode 100644
index 0000000..f99ca56
--- /dev/null
+++ b/src/syscall/zsysnum_linux_mips.go
@@ -0,0 +1,357 @@
+// mksysnum_linux.pl /usr/include/mips-linux-gnu/asm/unistd.h
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+	SYS_SYSCALL                = 4000
+	SYS_EXIT                   = 4001
+	SYS_FORK                   = 4002
+	SYS_READ                   = 4003
+	SYS_WRITE                  = 4004
+	SYS_OPEN                   = 4005
+	SYS_CLOSE                  = 4006
+	SYS_WAITPID                = 4007
+	SYS_CREAT                  = 4008
+	SYS_LINK                   = 4009
+	SYS_UNLINK                 = 4010
+	SYS_EXECVE                 = 4011
+	SYS_CHDIR                  = 4012
+	SYS_TIME                   = 4013
+	SYS_MKNOD                  = 4014
+	SYS_CHMOD                  = 4015
+	SYS_LCHOWN                 = 4016
+	SYS_BREAK                  = 4017
+	SYS_UNUSED18               = 4018
+	SYS_LSEEK                  = 4019
+	SYS_GETPID                 = 4020
+	SYS_MOUNT                  = 4021
+	SYS_UMOUNT                 = 4022
+	SYS_SETUID                 = 4023
+	SYS_GETUID                 = 4024
+	SYS_STIME                  = 4025
+	SYS_PTRACE                 = 4026
+	SYS_ALARM                  = 4027
+	SYS_UNUSED28               = 4028
+	SYS_PAUSE                  = 4029
+	SYS_UTIME                  = 4030
+	SYS_STTY                   = 4031
+	SYS_GTTY                   = 4032
+	SYS_ACCESS                 = 4033
+	SYS_NICE                   = 4034
+	SYS_FTIME                  = 4035
+	SYS_SYNC                   = 4036
+	SYS_KILL                   = 4037
+	SYS_RENAME                 = 4038
+	SYS_MKDIR                  = 4039
+	SYS_RMDIR                  = 4040
+	SYS_DUP                    = 4041
+	SYS_PIPE                   = 4042
+	SYS_TIMES                  = 4043
+	SYS_PROF                   = 4044
+	SYS_BRK                    = 4045
+	SYS_SETGID                 = 4046
+	SYS_GETGID                 = 4047
+	SYS_SIGNAL                 = 4048
+	SYS_GETEUID                = 4049
+	SYS_GETEGID                = 4050
+	SYS_ACCT                   = 4051
+	SYS_UMOUNT2                = 4052
+	SYS_LOCK                   = 4053
+	SYS_IOCTL                  = 4054
+	SYS_FCNTL                  = 4055
+	SYS_MPX                    = 4056
+	SYS_SETPGID                = 4057
+	SYS_ULIMIT                 = 4058
+	SYS_UNUSED59               = 4059
+	SYS_UMASK                  = 4060
+	SYS_CHROOT                 = 4061
+	SYS_USTAT                  = 4062
+	SYS_DUP2                   = 4063
+	SYS_GETPPID                = 4064
+	SYS_GETPGRP                = 4065
+	SYS_SETSID                 = 4066
+	SYS_SIGACTION              = 4067
+	SYS_SGETMASK               = 4068
+	SYS_SSETMASK               = 4069
+	SYS_SETREUID               = 4070
+	SYS_SETREGID               = 4071
+	SYS_SIGSUSPEND             = 4072
+	SYS_SIGPENDING             = 4073
+	SYS_SETHOSTNAME            = 4074
+	SYS_SETRLIMIT              = 4075
+	SYS_GETRLIMIT              = 4076
+	SYS_GETRUSAGE              = 4077
+	SYS_GETTIMEOFDAY           = 4078
+	SYS_SETTIMEOFDAY           = 4079
+	SYS_GETGROUPS              = 4080
+	SYS_SETGROUPS              = 4081
+	SYS_RESERVED82             = 4082
+	SYS_SYMLINK                = 4083
+	SYS_UNUSED84               = 4084
+	SYS_READLINK               = 4085
+	SYS_USELIB                 = 4086
+	SYS_SWAPON                 = 4087
+	SYS_REBOOT                 = 4088
+	SYS_READDIR                = 4089
+	SYS_MMAP                   = 4090
+	SYS_MUNMAP                 = 4091
+	SYS_TRUNCATE               = 4092
+	SYS_FTRUNCATE              = 4093
+	SYS_FCHMOD                 = 4094
+	SYS_FCHOWN                 = 4095
+	SYS_GETPRIORITY            = 4096
+	SYS_SETPRIORITY            = 4097
+	SYS_PROFIL                 = 4098
+	SYS_STATFS                 = 4099
+	SYS_FSTATFS                = 4100
+	SYS_IOPERM                 = 4101
+	SYS_SOCKETCALL             = 4102
+	SYS_SYSLOG                 = 4103
+	SYS_SETITIMER              = 4104
+	SYS_GETITIMER              = 4105
+	SYS_STAT                   = 4106
+	SYS_LSTAT                  = 4107
+	SYS_FSTAT                  = 4108
+	SYS_UNUSED109              = 4109
+	SYS_IOPL                   = 4110
+	SYS_VHANGUP                = 4111
+	SYS_IDLE                   = 4112
+	SYS_VM86                   = 4113
+	SYS_WAIT4                  = 4114
+	SYS_SWAPOFF                = 4115
+	SYS_SYSINFO                = 4116
+	SYS_IPC                    = 4117
+	SYS_FSYNC                  = 4118
+	SYS_SIGRETURN              = 4119
+	SYS_CLONE                  = 4120
+	SYS_SETDOMAINNAME          = 4121
+	SYS_UNAME                  = 4122
+	SYS_MODIFY_LDT             = 4123
+	SYS_ADJTIMEX               = 4124
+	SYS_MPROTECT               = 4125
+	SYS_SIGPROCMASK            = 4126
+	SYS_CREATE_MODULE          = 4127
+	SYS_INIT_MODULE            = 4128
+	SYS_DELETE_MODULE          = 4129
+	SYS_GET_KERNEL_SYMS        = 4130
+	SYS_QUOTACTL               = 4131
+	SYS_GETPGID                = 4132
+	SYS_FCHDIR                 = 4133
+	SYS_BDFLUSH                = 4134
+	SYS_SYSFS                  = 4135
+	SYS_PERSONALITY            = 4136
+	SYS_AFS_SYSCALL            = 4137
+	SYS_SETFSUID               = 4138
+	SYS_SETFSGID               = 4139
+	SYS__LLSEEK                = 4140
+	SYS_GETDENTS               = 4141
+	SYS__NEWSELECT             = 4142
+	SYS_FLOCK                  = 4143
+	SYS_MSYNC                  = 4144
+	SYS_READV                  = 4145
+	SYS_WRITEV                 = 4146
+	SYS_CACHEFLUSH             = 4147
+	SYS_CACHECTL               = 4148
+	SYS_SYSMIPS                = 4149
+	SYS_UNUSED150              = 4150
+	SYS_GETSID                 = 4151
+	SYS_FDATASYNC              = 4152
+	SYS__SYSCTL                = 4153
+	SYS_MLOCK                  = 4154
+	SYS_MUNLOCK                = 4155
+	SYS_MLOCKALL               = 4156
+	SYS_MUNLOCKALL             = 4157
+	SYS_SCHED_SETPARAM         = 4158
+	SYS_SCHED_GETPARAM         = 4159
+	SYS_SCHED_SETSCHEDULER     = 4160
+	SYS_SCHED_GETSCHEDULER     = 4161
+	SYS_SCHED_YIELD            = 4162
+	SYS_SCHED_GET_PRIORITY_MAX = 4163
+	SYS_SCHED_GET_PRIORITY_MIN = 4164
+	SYS_SCHED_RR_GET_INTERVAL  = 4165
+	SYS_NANOSLEEP              = 4166
+	SYS_MREMAP                 = 4167
+	SYS_ACCEPT                 = 4168
+	SYS_BIND                   = 4169
+	SYS_CONNECT                = 4170
+	SYS_GETPEERNAME            = 4171
+	SYS_GETSOCKNAME            = 4172
+	SYS_GETSOCKOPT             = 4173
+	SYS_LISTEN                 = 4174
+	SYS_RECV                   = 4175
+	SYS_RECVFROM               = 4176
+	SYS_RECVMSG                = 4177
+	SYS_SEND                   = 4178
+	SYS_SENDMSG                = 4179
+	SYS_SENDTO                 = 4180
+	SYS_SETSOCKOPT             = 4181
+	SYS_SHUTDOWN               = 4182
+	SYS_SOCKET                 = 4183
+	SYS_SOCKETPAIR             = 4184
+	SYS_SETRESUID              = 4185
+	SYS_GETRESUID              = 4186
+	SYS_QUERY_MODULE           = 4187
+	SYS_POLL                   = 4188
+	SYS_NFSSERVCTL             = 4189
+	SYS_SETRESGID              = 4190
+	SYS_GETRESGID              = 4191
+	SYS_PRCTL                  = 4192
+	SYS_RT_SIGRETURN           = 4193
+	SYS_RT_SIGACTION           = 4194
+	SYS_RT_SIGPROCMASK         = 4195
+	SYS_RT_SIGPENDING          = 4196
+	SYS_RT_SIGTIMEDWAIT        = 4197
+	SYS_RT_SIGQUEUEINFO        = 4198
+	SYS_RT_SIGSUSPEND          = 4199
+	SYS_PREAD64                = 4200
+	SYS_PWRITE64               = 4201
+	SYS_CHOWN                  = 4202
+	SYS_GETCWD                 = 4203
+	SYS_CAPGET                 = 4204
+	SYS_CAPSET                 = 4205
+	SYS_SIGALTSTACK            = 4206
+	SYS_SENDFILE               = 4207
+	SYS_GETPMSG                = 4208
+	SYS_PUTPMSG                = 4209
+	SYS_MMAP2                  = 4210
+	SYS_TRUNCATE64             = 4211
+	SYS_FTRUNCATE64            = 4212
+	SYS_STAT64                 = 4213
+	SYS_LSTAT64                = 4214
+	SYS_FSTAT64                = 4215
+	SYS_PIVOT_ROOT             = 4216
+	SYS_MINCORE                = 4217
+	SYS_MADVISE                = 4218
+	SYS_GETDENTS64             = 4219
+	SYS_FCNTL64                = 4220
+	SYS_RESERVED221            = 4221
+	SYS_GETTID                 = 4222
+	SYS_READAHEAD              = 4223
+	SYS_SETXATTR               = 4224
+	SYS_LSETXATTR              = 4225
+	SYS_FSETXATTR              = 4226
+	SYS_GETXATTR               = 4227
+	SYS_LGETXATTR              = 4228
+	SYS_FGETXATTR              = 4229
+	SYS_LISTXATTR              = 4230
+	SYS_LLISTXATTR             = 4231
+	SYS_FLISTXATTR             = 4232
+	SYS_REMOVEXATTR            = 4233
+	SYS_LREMOVEXATTR           = 4234
+	SYS_FREMOVEXATTR           = 4235
+	SYS_TKILL                  = 4236
+	SYS_SENDFILE64             = 4237
+	SYS_FUTEX                  = 4238
+	SYS_SCHED_SETAFFINITY      = 4239
+	SYS_SCHED_GETAFFINITY      = 4240
+	SYS_IO_SETUP               = 4241
+	SYS_IO_DESTROY             = 4242
+	SYS_IO_GETEVENTS           = 4243
+	SYS_IO_SUBMIT              = 4244
+	SYS_IO_CANCEL              = 4245
+	SYS_EXIT_GROUP             = 4246
+	SYS_LOOKUP_DCOOKIE         = 4247
+	SYS_EPOLL_CREATE           = 4248
+	SYS_EPOLL_CTL              = 4249
+	SYS_EPOLL_WAIT             = 4250
+	SYS_REMAP_FILE_PAGES       = 4251
+	SYS_SET_TID_ADDRESS        = 4252
+	SYS_RESTART_SYSCALL        = 4253
+	SYS_FADVISE64              = 4254
+	SYS_STATFS64               = 4255
+	SYS_FSTATFS64              = 4256
+	SYS_TIMER_CREATE           = 4257
+	SYS_TIMER_SETTIME          = 4258
+	SYS_TIMER_GETTIME          = 4259
+	SYS_TIMER_GETOVERRUN       = 4260
+	SYS_TIMER_DELETE           = 4261
+	SYS_CLOCK_SETTIME          = 4262
+	SYS_CLOCK_GETTIME          = 4263
+	SYS_CLOCK_GETRES           = 4264
+	SYS_CLOCK_NANOSLEEP        = 4265
+	SYS_TGKILL                 = 4266
+	SYS_UTIMES                 = 4267
+	SYS_MBIND                  = 4268
+	SYS_GET_MEMPOLICY          = 4269
+	SYS_SET_MEMPOLICY          = 4270
+	SYS_MQ_OPEN                = 4271
+	SYS_MQ_UNLINK              = 4272
+	SYS_MQ_TIMEDSEND           = 4273
+	SYS_MQ_TIMEDRECEIVE        = 4274
+	SYS_MQ_NOTIFY              = 4275
+	SYS_MQ_GETSETATTR          = 4276
+	SYS_VSERVER                = 4277
+	SYS_WAITID                 = 4278
+	SYS_ADD_KEY                = 4280
+	SYS_REQUEST_KEY            = 4281
+	SYS_KEYCTL                 = 4282
+	SYS_SET_THREAD_AREA        = 4283
+	SYS_INOTIFY_INIT           = 4284
+	SYS_INOTIFY_ADD_WATCH      = 4285
+	SYS_INOTIFY_RM_WATCH       = 4286
+	SYS_MIGRATE_PAGES          = 4287
+	SYS_OPENAT                 = 4288
+	SYS_MKDIRAT                = 4289
+	SYS_MKNODAT                = 4290
+	SYS_FCHOWNAT               = 4291
+	SYS_FUTIMESAT              = 4292
+	SYS_FSTATAT64              = 4293
+	SYS_UNLINKAT               = 4294
+	SYS_RENAMEAT               = 4295
+	SYS_LINKAT                 = 4296
+	SYS_SYMLINKAT              = 4297
+	SYS_READLINKAT             = 4298
+	SYS_FCHMODAT               = 4299
+	SYS_FACCESSAT              = 4300
+	SYS_PSELECT6               = 4301
+	SYS_PPOLL                  = 4302
+	SYS_UNSHARE                = 4303
+	SYS_SPLICE                 = 4304
+	SYS_SYNC_FILE_RANGE        = 4305
+	SYS_TEE                    = 4306
+	SYS_VMSPLICE               = 4307
+	SYS_MOVE_PAGES             = 4308
+	SYS_SET_ROBUST_LIST        = 4309
+	SYS_GET_ROBUST_LIST        = 4310
+	SYS_KEXEC_LOAD             = 4311
+	SYS_GETCPU                 = 4312
+	SYS_EPOLL_PWAIT            = 4313
+	SYS_IOPRIO_SET             = 4314
+	SYS_IOPRIO_GET             = 4315
+	SYS_UTIMENSAT              = 4316
+	SYS_SIGNALFD               = 4317
+	SYS_TIMERFD                = 4318
+	SYS_EVENTFD                = 4319
+	SYS_FALLOCATE              = 4320
+	SYS_TIMERFD_CREATE         = 4321
+	SYS_TIMERFD_GETTIME        = 4322
+	SYS_TIMERFD_SETTIME        = 4323
+	SYS_SIGNALFD4              = 4324
+	SYS_EVENTFD2               = 4325
+	SYS_EPOLL_CREATE1          = 4326
+	SYS_DUP3                   = 4327
+	SYS_PIPE2                  = 4328
+	SYS_INOTIFY_INIT1          = 4329
+	SYS_PREADV                 = 4330
+	SYS_PWRITEV                = 4331
+	SYS_RT_TGSIGQUEUEINFO      = 4332
+	SYS_PERF_EVENT_OPEN        = 4333
+	SYS_ACCEPT4                = 4334
+	SYS_RECVMMSG               = 4335
+	SYS_FANOTIFY_INIT          = 4336
+	SYS_FANOTIFY_MARK          = 4337
+	SYS_PRLIMIT64              = 4338
+	SYS_NAME_TO_HANDLE_AT      = 4339
+	SYS_OPEN_BY_HANDLE_AT      = 4340
+	SYS_CLOCK_ADJTIME          = 4341
+	SYS_SYNCFS                 = 4342
+	SYS_SENDMMSG               = 4343
+	SYS_SETNS                  = 4344
+	SYS_PROCESS_VM_READV       = 4345
+	SYS_PROCESS_VM_WRITEV      = 4346
+	SYS_LINUX_SYSCALLS         = 4346
+	SYS_O32_LINUX_SYSCALLS     = 4346
+	SYS_64_LINUX_SYSCALLS      = 4305
+	SYS_N32_LINUX_SYSCALLS     = 4310
+)
diff --git a/src/syscall/zsysnum_linux_mipsle.go b/src/syscall/zsysnum_linux_mipsle.go
new file mode 100644
index 0000000..f99ca56
--- /dev/null
+++ b/src/syscall/zsysnum_linux_mipsle.go
@@ -0,0 +1,357 @@
+// mksysnum_linux.pl /usr/include/mips-linux-gnu/asm/unistd.h
+// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
+
+package syscall
+
+const (
+	SYS_SYSCALL                = 4000
+	SYS_EXIT                   = 4001
+	SYS_FORK                   = 4002
+	SYS_READ                   = 4003
+	SYS_WRITE                  = 4004
+	SYS_OPEN                   = 4005
+	SYS_CLOSE                  = 4006
+	SYS_WAITPID                = 4007
+	SYS_CREAT                  = 4008
+	SYS_LINK                   = 4009
+	SYS_UNLINK                 = 4010
+	SYS_EXECVE                 = 4011
+	SYS_CHDIR                  = 4012
+	SYS_TIME                   = 4013
+	SYS_MKNOD                  = 4014
+	SYS_CHMOD                  = 4015
+	SYS_LCHOWN                 = 4016
+	SYS_BREAK                  = 4017
+	SYS_UNUSED18               = 4018
+	SYS_LSEEK                  = 4019
+	SYS_GETPID                 = 4020
+	SYS_MOUNT                  = 4021
+	SYS_UMOUNT                 = 4022
+	SYS_SETUID                 = 4023
+	SYS_GETUID                 = 4024
+	SYS_STIME                  = 4025
+	SYS_PTRACE                 = 4026
+	SYS_ALARM                  = 4027
+	SYS_UNUSED28               = 4028
+	SYS_PAUSE                  = 4029
+	SYS_UTIME                  = 4030
+	SYS_STTY                   = 4031
+	SYS_GTTY                   = 4032
+	SYS_ACCESS                 = 4033
+	SYS_NICE                   = 4034
+	SYS_FTIME                  = 4035
+	SYS_SYNC                   = 4036
+	SYS_KILL                   = 4037
+	SYS_RENAME                 = 4038
+	SYS_MKDIR                  = 4039
+	SYS_RMDIR                  = 4040
+	SYS_DUP                    = 4041
+	SYS_PIPE                   = 4042
+	SYS_TIMES                  = 4043
+	SYS_PROF                   = 4044
+	SYS_BRK                    = 4045
+	SYS_SETGID                 = 4046
+	SYS_GETGID                 = 4047
+	SYS_SIGNAL                 = 4048
+	SYS_GETEUID                = 4049
+	SYS_GETEGID                = 4050
+	SYS_ACCT                   = 4051
+	SYS_UMOUNT2                = 4052
+	SYS_LOCK                   = 4053
+	SYS_IOCTL                  = 4054
+	SYS_FCNTL                  = 4055
+	SYS_MPX                    = 4056
+	SYS_SETPGID                = 4057
+	SYS_ULIMIT                 = 4058
+	SYS_UNUSED59               = 4059
+	SYS_UMASK                  = 4060
+	SYS_CHROOT                 = 4061
+	SYS_USTAT                  = 4062
+	SYS_DUP2                   = 4063
+	SYS_GETPPID                = 4064
+	SYS_GETPGRP                = 4065
+	SYS_SETSID                 = 4066
+	SYS_SIGACTION              = 4067
+	SYS_SGETMASK               = 4068
+	SYS_SSETMASK               = 4069
+	SYS_SETREUID               = 4070
+	SYS_SETREGID               = 4071
+	SYS_SIGSUSPEND             = 4072
+	SYS_SIGPENDING             = 4073
+	SYS_SETHOSTNAME            = 4074
+	SYS_SETRLIMIT              = 4075
+	SYS_GETRLIMIT              = 4076
+	SYS_GETRUSAGE              = 4077
+	SYS_GETTIMEOFDAY           = 4078
+	SYS_SETTIMEOFDAY           = 4079
+	SYS_GETGROUPS              = 4080
+	SYS_SETGROUPS              = 4081
+	SYS_RESERVED82             = 4082
+	SYS_SYMLINK                = 4083
+	SYS_UNUSED84               = 4084
+	SYS_READLINK               = 4085
+	SYS_USELIB                 = 4086
+	SYS_SWAPON                 = 4087
+	SYS_REBOOT                 = 4088
+	SYS_READDIR                = 4089
+	SYS_MMAP                   = 4090
+	SYS_MUNMAP                 = 4091
+	SYS_TRUNCATE               = 4092
+	SYS_FTRUNCATE              = 4093
+	SYS_FCHMOD                 = 4094
+	SYS_FCHOWN                 = 4095
+	SYS_GETPRIORITY            = 4096
+	SYS_SETPRIORITY            = 4097
+	SYS_PROFIL                 = 4098
+	SYS_STATFS                 = 4099
+	SYS_FSTATFS                = 4100
+	SYS_IOPERM                 = 4101
+	SYS_SOCKETCALL             = 4102
+	SYS_SYSLOG                 = 4103
+	SYS_SETITIMER              = 4104
+	SYS_GETITIMER              = 4105
+	SYS_STAT                   = 4106
+	SYS_LSTAT                  = 4107
+	SYS_FSTAT                  = 4108
+	SYS_UNUSED109              = 4109
+	SYS_IOPL                   = 4110
+	SYS_VHANGUP                = 4111
+	SYS_IDLE                   = 4112
+	SYS_VM86                   = 4113
+	SYS_WAIT4                  = 4114
+	SYS_SWAPOFF                = 4115
+	SYS_SYSINFO                = 4116
+	SYS_IPC                    = 4117
+	SYS_FSYNC                  = 4118
+	SYS_SIGRETURN              = 4119
+	SYS_CLONE                  = 4120
+	SYS_SETDOMAINNAME          = 4121
+	SYS_UNAME                  = 4122
+	SYS_MODIFY_LDT             = 4123
+	SYS_ADJTIMEX               = 4124
+	SYS_MPROTECT               = 4125
+	SYS_SIGPROCMASK            = 4126
+	SYS_CREATE_MODULE          = 4127
+	SYS_INIT_MODULE            = 4128
+	SYS_DELETE_MODULE          = 4129
+	SYS_GET_KERNEL_SYMS        = 4130
+	SYS_QUOTACTL               = 4131
+	SYS_GETPGID                = 4132
+	SYS_FCHDIR                 = 4133
+	SYS_BDFLUSH                = 4134
+	SYS_SYSFS                  = 4135
+	SYS_PERSONALITY            = 4136
+	SYS_AFS_SYSCALL            = 4137
+	SYS_SETFSUID               = 4138
+	SYS_SETFSGID               = 4139
+	SYS__LLSEEK                = 4140
+	SYS_GETDENTS               = 4141
+	SYS__NEWSELECT             = 4142
+	SYS_FLOCK                  = 4143
+	SYS_MSYNC                  = 4144
+	SYS_READV                  = 4145
+	SYS_WRITEV                 = 4146
+	SYS_CACHEFLUSH             = 4147
+	SYS_CACHECTL               = 4148
+	SYS_SYSMIPS                = 4149
+	SYS_UNUSED150              = 4150
+	SYS_GETSID                 = 4151
+	SYS_FDATASYNC              = 4152
+	SYS__SYSCTL                = 4153
+	SYS_MLOCK                  = 4154
+	SYS_MUNLOCK                = 4155
+	SYS_MLOCKALL               = 4156
+	SYS_MUNLOCKALL             = 4157
+	SYS_SCHED_SETPARAM         = 4158
+	SYS_SCHED_GETPARAM         = 4159
+	SYS_SCHED_SETSCHEDULER     = 4160
+	SYS_SCHED_GETSCHEDULER     = 4161
+	SYS_SCHED_YIELD            = 4162
+	SYS_SCHED_GET_PRIORITY_MAX = 4163
+	SYS_SCHED_GET_PRIORITY_MIN = 4164
+	SYS_SCHED_RR_GET_INTERVAL  = 4165
+	SYS_NANOSLEEP              = 4166
+	SYS_MREMAP                 = 4167
+	SYS_ACCEPT                 = 4168
+	SYS_BIND                   = 4169
+	SYS_CONNECT                = 4170
+	SYS_GETPEERNAME            = 4171
+	SYS_GETSOCKNAME            = 4172
+	SYS_GETSOCKOPT             = 4173
+	SYS_LISTEN                 = 4174
+	SYS_RECV                   = 4175
+	SYS_RECVFROM               = 4176
+	SYS_RECVMSG                = 4177
+	SYS_SEND                   = 4178
+	SYS_SENDMSG                = 4179
+	SYS_SENDTO                 = 4180
+	SYS_SETSOCKOPT             = 4181
+	SYS_SHUTDOWN               = 4182
+	SYS_SOCKET                 = 4183
+	SYS_SOCKETPAIR             = 4184
+	SYS_SETRESUID              = 4185
+	SYS_GETRESUID              = 4186
+	SYS_QUERY_MODULE           = 4187
+	SYS_POLL                   = 4188
+	SYS_NFSSERVCTL             = 4189
+	SYS_SETRESGID              = 4190
+	SYS_GETRESGID              = 4191
+	SYS_PRCTL                  = 4192
+	SYS_RT_SIGRETURN           = 4193
+	SYS_RT_SIGACTION           = 4194
+	SYS_RT_SIGPROCMASK         = 4195
+	SYS_RT_SIGPENDING          = 4196
+	SYS_RT_SIGTIMEDWAIT        = 4197
+	SYS_RT_SIGQUEUEINFO        = 4198
+	SYS_RT_SIGSUSPEND          = 4199
+	SYS_PREAD64                = 4200
+	SYS_PWRITE64               = 4201
+	SYS_CHOWN                  = 4202
+	SYS_GETCWD                 = 4203
+	SYS_CAPGET                 = 4204
+	SYS_CAPSET                 = 4205
+	SYS_SIGALTSTACK            = 4206
+	SYS_SENDFILE               = 4207
+	SYS_GETPMSG                = 4208
+	SYS_PUTPMSG                = 4209
+	SYS_MMAP2                  = 4210
+	SYS_TRUNCATE64             = 4211
+	SYS_FTRUNCATE64            = 4212
+	SYS_STAT64                 = 4213
+	SYS_LSTAT64                = 4214
+	SYS_FSTAT64                = 4215
+	SYS_PIVOT_ROOT             = 4216
+	SYS_MINCORE                = 4217
+	SYS_MADVISE                = 4218
+	SYS_GETDENTS64             = 4219
+	SYS_FCNTL64                = 4220
+	SYS_RESERVED221            = 4221
+	SYS_GETTID                 = 4222
+	SYS_READAHEAD              = 4223
+	SYS_SETXATTR               = 4224
+	SYS_LSETXATTR              = 4225
+	SYS_FSETXATTR              = 4226
+	SYS_GETXATTR               = 4227
+	SYS_LGETXATTR              = 4228
+	SYS_FGETXATTR              = 4229
+	SYS_LISTXATTR              = 4230
+	SYS_LLISTXATTR             = 4231
+	SYS_FLISTXATTR             = 4232
+	SYS_REMOVEXATTR            = 4233
+	SYS_LREMOVEXATTR           = 4234
+	SYS_FREMOVEXATTR           = 4235
+	SYS_TKILL                  = 4236
+	SYS_SENDFILE64             = 4237
+	SYS_FUTEX                  = 4238
+	SYS_SCHED_SETAFFINITY      = 4239
+	SYS_SCHED_GETAFFINITY      = 4240
+	SYS_IO_SETUP               = 4241
+	SYS_IO_DESTROY             = 4242
+	SYS_IO_GETEVENTS           = 4243
+	SYS_IO_SUBMIT              = 4244
+	SYS_IO_CANCEL              = 4245
+	SYS_EXIT_GROUP             = 4246
+	SYS_LOOKUP_DCOOKIE         = 4247
+	SYS_EPOLL_CREATE           = 4248
+	SYS_EPOLL_CTL              = 4249
+	SYS_EPOLL_WAIT             = 4250
+	SYS_REMAP_FILE_PAGES       = 4251
+	SYS_SET_TID_ADDRESS        = 4252
+	SYS_RESTART_SYSCALL        = 4253
+	SYS_FADVISE64              = 4254
+	SYS_STATFS64               = 4255
+	SYS_FSTATFS64              = 4256
+	SYS_TIMER_CREATE           = 4257
+	SYS_TIMER_SETTIME          = 4258
+	SYS_TIMER_GETTIME          = 4259
+	SYS_TIMER_GETOVERRUN       = 4260
+	SYS_TIMER_DELETE           = 4261
+	SYS_CLOCK_SETTIME          = 4262
+	SYS_CLOCK_GETTIME          = 4263
+	SYS_CLOCK_GETRES           = 4264
+	SYS_CLOCK_NANOSLEEP        = 4265
+	SYS_TGKILL                 = 4266
+	SYS_UTIMES                 = 4267
+	SYS_MBIND                  = 4268
+	SYS_GET_MEMPOLICY          = 4269
+	SYS_SET_MEMPOLICY          = 4270
+	SYS_MQ_OPEN                = 4271
+	SYS_MQ_UNLINK              = 4272
+	SYS_MQ_TIMEDSEND           = 4273
+	SYS_MQ_TIMEDRECEIVE        = 4274
+	SYS_MQ_NOTIFY              = 4275
+	SYS_MQ_GETSETATTR          = 4276
+	SYS_VSERVER                = 4277
+	SYS_WAITID                 = 4278
+	SYS_ADD_KEY                = 4280
+	SYS_REQUEST_KEY            = 4281
+	SYS_KEYCTL                 = 4282
+	SYS_SET_THREAD_AREA        = 4283
+	SYS_INOTIFY_INIT           = 4284
+	SYS_INOTIFY_ADD_WATCH      = 4285
+	SYS_INOTIFY_RM_WATCH       = 4286
+	SYS_MIGRATE_PAGES          = 4287
+	SYS_OPENAT                 = 4288
+	SYS_MKDIRAT                = 4289
+	SYS_MKNODAT                = 4290
+	SYS_FCHOWNAT               = 4291
+	SYS_FUTIMESAT              = 4292
+	SYS_FSTATAT64              = 4293
+	SYS_UNLINKAT               = 4294
+	SYS_RENAMEAT               = 4295
+	SYS_LINKAT                 = 4296
+	SYS_SYMLINKAT              = 4297
+	SYS_READLINKAT             = 4298
+	SYS_FCHMODAT               = 4299
+	SYS_FACCESSAT              = 4300
+	SYS_PSELECT6               = 4301
+	SYS_PPOLL                  = 4302
+	SYS_UNSHARE                = 4303
+	SYS_SPLICE                 = 4304
+	SYS_SYNC_FILE_RANGE        = 4305
+	SYS_TEE                    = 4306
+	SYS_VMSPLICE               = 4307
+	SYS_MOVE_PAGES             = 4308
+	SYS_SET_ROBUST_LIST        = 4309
+	SYS_GET_ROBUST_LIST        = 4310
+	SYS_KEXEC_LOAD             = 4311
+	SYS_GETCPU                 = 4312
+	SYS_EPOLL_PWAIT            = 4313
+	SYS_IOPRIO_SET             = 4314
+	SYS_IOPRIO_GET             = 4315
+	SYS_UTIMENSAT              = 4316
+	SYS_SIGNALFD               = 4317
+	SYS_TIMERFD                = 4318
+	SYS_EVENTFD                = 4319
+	SYS_FALLOCATE              = 4320
+	SYS_TIMERFD_CREATE         = 4321
+	SYS_TIMERFD_GETTIME        = 4322
+	SYS_TIMERFD_SETTIME        = 4323
+	SYS_SIGNALFD4              = 4324
+	SYS_EVENTFD2               = 4325
+	SYS_EPOLL_CREATE1          = 4326
+	SYS_DUP3                   = 4327
+	SYS_PIPE2                  = 4328
+	SYS_INOTIFY_INIT1          = 4329
+	SYS_PREADV                 = 4330
+	SYS_PWRITEV                = 4331
+	SYS_RT_TGSIGQUEUEINFO      = 4332
+	SYS_PERF_EVENT_OPEN        = 4333
+	SYS_ACCEPT4                = 4334
+	SYS_RECVMMSG               = 4335
+	SYS_FANOTIFY_INIT          = 4336
+	SYS_FANOTIFY_MARK          = 4337
+	SYS_PRLIMIT64              = 4338
+	SYS_NAME_TO_HANDLE_AT      = 4339
+	SYS_OPEN_BY_HANDLE_AT      = 4340
+	SYS_CLOCK_ADJTIME          = 4341
+	SYS_SYNCFS                 = 4342
+	SYS_SENDMMSG               = 4343
+	SYS_SETNS                  = 4344
+	SYS_PROCESS_VM_READV       = 4345
+	SYS_PROCESS_VM_WRITEV      = 4346
+	SYS_LINUX_SYSCALLS         = 4346
+	SYS_O32_LINUX_SYSCALLS     = 4346
+	SYS_64_LINUX_SYSCALLS      = 4305
+	SYS_N32_LINUX_SYSCALLS     = 4310
+)
diff --git a/src/syscall/ztypes_linux_386.go b/src/syscall/ztypes_linux_386.go
index dd198cb..a73c917 100644
--- a/src/syscall/ztypes_linux_386.go
+++ b/src/syscall/ztypes_linux_386.go
@@ -238,10 +238,9 @@ type Msghdr struct {
 }
 
 type Cmsghdr struct {
-	Len          uint32
-	Level        int32
-	Type         int32
-	X__cmsg_data [0]uint8
+	Len   uint32
+	Level int32
+	Type  int32
 }
 
 type Inet4Pktinfo struct {
diff --git a/src/syscall/ztypes_linux_amd64.go b/src/syscall/ztypes_linux_amd64.go
index a39489e..4cbd5d8 100644
--- a/src/syscall/ztypes_linux_amd64.go
+++ b/src/syscall/ztypes_linux_amd64.go
@@ -242,10 +242,9 @@ type Msghdr struct {
 }
 
 type Cmsghdr struct {
-	Len          uint64
-	Level        int32
-	Type         int32
-	X__cmsg_data [0]uint8
+	Len   uint64
+	Level int32
+	Type  int32
 }
 
 type Inet4Pktinfo struct {
diff --git a/src/syscall/ztypes_linux_arm.go b/src/syscall/ztypes_linux_arm.go
index f446e41..16aa014 100644
--- a/src/syscall/ztypes_linux_arm.go
+++ b/src/syscall/ztypes_linux_arm.go
@@ -242,10 +242,9 @@ type Msghdr struct {
 }
 
 type Cmsghdr struct {
-	Len          uint32
-	Level        int32
-	Type         int32
-	X__cmsg_data [0]uint8
+	Len   uint32
+	Level int32
+	Type  int32
 }
 
 type Inet4Pktinfo struct {
diff --git a/src/syscall/ztypes_linux_arm64.go b/src/syscall/ztypes_linux_arm64.go
index dcb1178..e5d669c 100644
--- a/src/syscall/ztypes_linux_arm64.go
+++ b/src/syscall/ztypes_linux_arm64.go
@@ -243,10 +243,9 @@ type Msghdr struct {
 }
 
 type Cmsghdr struct {
-	Len          uint64
-	Level        int32
-	Type         int32
-	X__cmsg_data [0]uint8
+	Len   uint64
+	Level int32
+	Type  int32
 }
 
 type Inet4Pktinfo struct {
diff --git a/src/syscall/ztypes_linux_mips.go b/src/syscall/ztypes_linux_mips.go
new file mode 100644
index 0000000..7a8d34d
--- /dev/null
+++ b/src/syscall/ztypes_linux_mips.go
@@ -0,0 +1,592 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_linux.go
+
+package syscall
+
+const (
+	sizeofPtr      = 0x4
+	sizeofShort    = 0x2
+	sizeofInt      = 0x4
+	sizeofLong     = 0x4
+	sizeofLongLong = 0x8
+	PathMax        = 0x1000
+)
+
+type (
+	_C_short     int16
+	_C_int       int32
+	_C_long      int32
+	_C_long_long int64
+)
+
+type Timespec struct {
+	Sec  int32
+	Nsec int32
+}
+
+type Timeval struct {
+	Sec  int32
+	Usec int32
+}
+
+type Timex struct {
+	Modes     uint32
+	Offset    int32
+	Freq      int32
+	Maxerror  int32
+	Esterror  int32
+	Status    int32
+	Constant  int32
+	Precision int32
+	Tolerance int32
+	Time      Timeval
+	Tick      int32
+	Ppsfreq   int32
+	Jitter    int32
+	Shift     int32
+	Stabil    int32
+	Jitcnt    int32
+	Calcnt    int32
+	Errcnt    int32
+	Stbcnt    int32
+	Tai       int32
+	Pad_cgo_0 [44]byte
+}
+
+type Time_t int32
+
+type Tms struct {
+	Utime  int32
+	Stime  int32
+	Cutime int32
+	Cstime int32
+}
+
+type Utimbuf struct {
+	Actime  int32
+	Modtime int32
+}
+
+type Rusage struct {
+	Utime    Timeval
+	Stime    Timeval
+	Maxrss   int32
+	Ixrss    int32
+	Idrss    int32
+	Isrss    int32
+	Minflt   int32
+	Majflt   int32
+	Nswap    int32
+	Inblock  int32
+	Oublock  int32
+	Msgsnd   int32
+	Msgrcv   int32
+	Nsignals int32
+	Nvcsw    int32
+	Nivcsw   int32
+}
+
+type Rlimit struct {
+	Cur uint64
+	Max uint64
+}
+
+type _Gid_t uint32
+
+type Stat_t struct {
+	Dev     uint32
+	Pad1    [3]int32
+	Ino     uint64
+	Mode    uint32
+	Nlink   uint32
+	Uid     uint32
+	Gid     uint32
+	Rdev    uint32
+	Pad2    [3]int32
+	Size    int64
+	Atim    Timespec
+	Mtim    Timespec
+	Ctim    Timespec
+	Blksize int32
+	Pad4    int32
+	Blocks  int64
+	Pad5    [14]int32
+}
+
+type Statfs_t struct {
+	Type      int32
+	Bsize     int32
+	Frsize    int32
+	Pad_cgo_0 [4]byte
+	Blocks    uint64
+	Bfree     uint64
+	Files     uint64
+	Ffree     uint64
+	Bavail    uint64
+	Fsid      Fsid
+	Namelen   int32
+	Flags     int32
+	Spare     [5]int32
+	Pad_cgo_1 [4]byte
+}
+
+type Dirent struct {
+	Ino       uint64
+	Off       int64
+	Reclen    uint16
+	Type      uint8
+	Name      [256]int8
+	Pad_cgo_0 [5]byte
+}
+
+type Fsid struct {
+	X__val [2]int32
+}
+
+type Flock_t struct {
+	Type      int16
+	Whence    int16
+	Pad_cgo_0 [4]byte
+	Start     int64
+	Len       int64
+	Pid       int32
+	Pad_cgo_1 [4]byte
+}
+
+type RawSockaddrInet4 struct {
+	Family uint16
+	Port   uint16
+	Addr   [4]byte /* in_addr */
+	Zero   [8]uint8
+}
+
+type RawSockaddrInet6 struct {
+	Family   uint16
+	Port     uint16
+	Flowinfo uint32
+	Addr     [16]byte /* in6_addr */
+	Scope_id uint32
+}
+
+type RawSockaddrUnix struct {
+	Family uint16
+	Path   [108]int8
+}
+
+type RawSockaddrLinklayer struct {
+	Family   uint16
+	Protocol uint16
+	Ifindex  int32
+	Hatype   uint16
+	Pkttype  uint8
+	Halen    uint8
+	Addr     [8]uint8
+}
+
+type RawSockaddrNetlink struct {
+	Family uint16
+	Pad    uint16
+	Pid    uint32
+	Groups uint32
+}
+
+type RawSockaddr struct {
+	Family uint16
+	Data   [14]int8
+}
+
+type RawSockaddrAny struct {
+	Addr RawSockaddr
+	Pad  [96]int8
+}
+
+type _Socklen uint32
+
+type Linger struct {
+	Onoff  int32
+	Linger int32
+}
+
+type Iovec struct {
+	Base *byte
+	Len  uint32
+}
+
+type IPMreq struct {
+	Multiaddr [4]byte /* in_addr */
+	Interface [4]byte /* in_addr */
+}
+
+type IPMreqn struct {
+	Multiaddr [4]byte /* in_addr */
+	Address   [4]byte /* in_addr */
+	Ifindex   int32
+}
+
+type IPv6Mreq struct {
+	Multiaddr [16]byte /* in6_addr */
+	Interface uint32
+}
+
+type Msghdr struct {
+	Name       *byte
+	Namelen    uint32
+	Iov        *Iovec
+	Iovlen     uint32
+	Control    *byte
+	Controllen uint32
+	Flags      int32
+}
+
+type Cmsghdr struct {
+	Len   uint32
+	Level int32
+	Type  int32
+}
+
+type Inet4Pktinfo struct {
+	Ifindex  int32
+	Spec_dst [4]byte /* in_addr */
+	Addr     [4]byte /* in_addr */
+}
+
+type Inet6Pktinfo struct {
+	Addr    [16]byte /* in6_addr */
+	Ifindex uint32
+}
+
+type IPv6MTUInfo struct {
+	Addr RawSockaddrInet6
+	Mtu  uint32
+}
+
+type ICMPv6Filter struct {
+	Data [8]uint32
+}
+
+type Ucred struct {
+	Pid int32
+	Uid uint32
+	Gid uint32
+}
+
+type TCPInfo struct {
+	State          uint8
+	Ca_state       uint8
+	Retransmits    uint8
+	Probes         uint8
+	Backoff        uint8
+	Options        uint8
+	Pad_cgo_0      [2]byte
+	Rto            uint32
+	Ato            uint32
+	Snd_mss        uint32
+	Rcv_mss        uint32
+	Unacked        uint32
+	Sacked         uint32
+	Lost           uint32
+	Retrans        uint32
+	Fackets        uint32
+	Last_data_sent uint32
+	Last_ack_sent  uint32
+	Last_data_recv uint32
+	Last_ack_recv  uint32
+	Pmtu           uint32
+	Rcv_ssthresh   uint32
+	Rtt            uint32
+	Rttvar         uint32
+	Snd_ssthresh   uint32
+	Snd_cwnd       uint32
+	Advmss         uint32
+	Reordering     uint32
+	Rcv_rtt        uint32
+	Rcv_space      uint32
+	Total_retrans  uint32
+}
+
+const (
+	SizeofSockaddrInet4     = 0x10
+	SizeofSockaddrInet6     = 0x1c
+	SizeofSockaddrAny       = 0x70
+	SizeofSockaddrUnix      = 0x6e
+	SizeofSockaddrLinklayer = 0x14
+	SizeofSockaddrNetlink   = 0xc
+	SizeofLinger            = 0x8
+	SizeofIPMreq            = 0x8
+	SizeofIPMreqn           = 0xc
+	SizeofIPv6Mreq          = 0x14
+	SizeofMsghdr            = 0x1c
+	SizeofCmsghdr           = 0xc
+	SizeofInet4Pktinfo      = 0xc
+	SizeofInet6Pktinfo      = 0x14
+	SizeofIPv6MTUInfo       = 0x20
+	SizeofICMPv6Filter      = 0x20
+	SizeofUcred             = 0xc
+	SizeofTCPInfo           = 0x68
+)
+
+const (
+	IFA_UNSPEC          = 0x0
+	IFA_ADDRESS         = 0x1
+	IFA_LOCAL           = 0x2
+	IFA_LABEL           = 0x3
+	IFA_BROADCAST       = 0x4
+	IFA_ANYCAST         = 0x5
+	IFA_CACHEINFO       = 0x6
+	IFA_MULTICAST       = 0x7
+	IFLA_UNSPEC         = 0x0
+	IFLA_ADDRESS        = 0x1
+	IFLA_BROADCAST      = 0x2
+	IFLA_IFNAME         = 0x3
+	IFLA_MTU            = 0x4
+	IFLA_LINK           = 0x5
+	IFLA_QDISC          = 0x6
+	IFLA_STATS          = 0x7
+	IFLA_COST           = 0x8
+	IFLA_PRIORITY       = 0x9
+	IFLA_MASTER         = 0xa
+	IFLA_WIRELESS       = 0xb
+	IFLA_PROTINFO       = 0xc
+	IFLA_TXQLEN         = 0xd
+	IFLA_MAP            = 0xe
+	IFLA_WEIGHT         = 0xf
+	IFLA_OPERSTATE      = 0x10
+	IFLA_LINKMODE       = 0x11
+	IFLA_LINKINFO       = 0x12
+	IFLA_NET_NS_PID     = 0x13
+	IFLA_IFALIAS        = 0x14
+	IFLA_MAX            = 0x27
+	RT_SCOPE_UNIVERSE   = 0x0
+	RT_SCOPE_SITE       = 0xc8
+	RT_SCOPE_LINK       = 0xfd
+	RT_SCOPE_HOST       = 0xfe
+	RT_SCOPE_NOWHERE    = 0xff
+	RT_TABLE_UNSPEC     = 0x0
+	RT_TABLE_COMPAT     = 0xfc
+	RT_TABLE_DEFAULT    = 0xfd
+	RT_TABLE_MAIN       = 0xfe
+	RT_TABLE_LOCAL      = 0xff
+	RT_TABLE_MAX        = 0xffffffff
+	RTA_UNSPEC          = 0x0
+	RTA_DST             = 0x1
+	RTA_SRC             = 0x2
+	RTA_IIF             = 0x3
+	RTA_OIF             = 0x4
+	RTA_GATEWAY         = 0x5
+	RTA_PRIORITY        = 0x6
+	RTA_PREFSRC         = 0x7
+	RTA_METRICS         = 0x8
+	RTA_MULTIPATH       = 0x9
+	RTA_FLOW            = 0xb
+	RTA_CACHEINFO       = 0xc
+	RTA_TABLE           = 0xf
+	RTN_UNSPEC          = 0x0
+	RTN_UNICAST         = 0x1
+	RTN_LOCAL           = 0x2
+	RTN_BROADCAST       = 0x3
+	RTN_ANYCAST         = 0x4
+	RTN_MULTICAST       = 0x5
+	RTN_BLACKHOLE       = 0x6
+	RTN_UNREACHABLE     = 0x7
+	RTN_PROHIBIT        = 0x8
+	RTN_THROW           = 0x9
+	RTN_NAT             = 0xa
+	RTN_XRESOLVE        = 0xb
+	RTNLGRP_NONE        = 0x0
+	RTNLGRP_LINK        = 0x1
+	RTNLGRP_NOTIFY      = 0x2
+	RTNLGRP_NEIGH       = 0x3
+	RTNLGRP_TC          = 0x4
+	RTNLGRP_IPV4_IFADDR = 0x5
+	RTNLGRP_IPV4_MROUTE = 0x6
+	RTNLGRP_IPV4_ROUTE  = 0x7
+	RTNLGRP_IPV4_RULE   = 0x8
+	RTNLGRP_IPV6_IFADDR = 0x9
+	RTNLGRP_IPV6_MROUTE = 0xa
+	RTNLGRP_IPV6_ROUTE  = 0xb
+	RTNLGRP_IPV6_IFINFO = 0xc
+	RTNLGRP_IPV6_PREFIX = 0x12
+	RTNLGRP_IPV6_RULE   = 0x13
+	RTNLGRP_ND_USEROPT  = 0x14
+	SizeofNlMsghdr      = 0x10
+	SizeofNlMsgerr      = 0x14
+	SizeofRtGenmsg      = 0x1
+	SizeofNlAttr        = 0x4
+	SizeofRtAttr        = 0x4
+	SizeofIfInfomsg     = 0x10
+	SizeofIfAddrmsg     = 0x8
+	SizeofRtMsg         = 0xc
+	SizeofRtNexthop     = 0x8
+)
+
+type NlMsghdr struct {
+	Len   uint32
+	Type  uint16
+	Flags uint16
+	Seq   uint32
+	Pid   uint32
+}
+
+type NlMsgerr struct {
+	Error int32
+	Msg   NlMsghdr
+}
+
+type RtGenmsg struct {
+	Family uint8
+}
+
+type NlAttr struct {
+	Len  uint16
+	Type uint16
+}
+
+type RtAttr struct {
+	Len  uint16
+	Type uint16
+}
+
+type IfInfomsg struct {
+	Family     uint8
+	X__ifi_pad uint8
+	Type       uint16
+	Index      int32
+	Flags      uint32
+	Change     uint32
+}
+
+type IfAddrmsg struct {
+	Family    uint8
+	Prefixlen uint8
+	Flags     uint8
+	Scope     uint8
+	Index     uint32
+}
+
+type RtMsg struct {
+	Family   uint8
+	Dst_len  uint8
+	Src_len  uint8
+	Tos      uint8
+	Table    uint8
+	Protocol uint8
+	Scope    uint8
+	Type     uint8
+	Flags    uint32
+}
+
+type RtNexthop struct {
+	Len     uint16
+	Flags   uint8
+	Hops    uint8
+	Ifindex int32
+}
+
+const (
+	SizeofSockFilter = 0x8
+	SizeofSockFprog  = 0x8
+)
+
+type SockFilter struct {
+	Code uint16
+	Jt   uint8
+	Jf   uint8
+	K    uint32
+}
+
+type SockFprog struct {
+	Len       uint16
+	Pad_cgo_0 [2]byte
+	Filter    *SockFilter
+}
+
+type InotifyEvent struct {
+	Wd     int32
+	Mask   uint32
+	Cookie uint32
+	Len    uint32
+}
+
+const SizeofInotifyEvent = 0x10
+
+type PtraceRegs struct {
+	Regs        [109]uint32
+	U_tsize     uint32
+	U_dsize     uint32
+	U_ssize     uint32
+	Start_code  uint32
+	Start_data  uint32
+	Start_stack uint32
+	Signal      int32
+	U_ar0       *byte
+	Magic       uint32
+	U_comm      [32]int8
+}
+
+type FdSet struct {
+	Bits [32]int32
+}
+
+type Sysinfo_t struct {
+	Uptime    int32
+	Loads     [3]uint32
+	Totalram  uint32
+	Freeram   uint32
+	Sharedram uint32
+	Bufferram uint32
+	Totalswap uint32
+	Freeswap  uint32
+	Procs     uint16
+	Pad       uint16
+	Totalhigh uint32
+	Freehigh  uint32
+	Unit      uint32
+	X_f       [8]int8
+}
+
+type Utsname struct {
+	Sysname    [65]int8
+	Nodename   [65]int8
+	Release    [65]int8
+	Version    [65]int8
+	Machine    [65]int8
+	Domainname [65]int8
+}
+
+type Ustat_t struct {
+	Tfree  int32
+	Tinode uint32
+	Fname  [6]int8
+	Fpack  [6]int8
+}
+
+type EpollEvent struct {
+	Events uint32
+	PadFd  int32
+	Fd     int32
+	Pad    int32
+}
+
+const (
+	_AT_FDCWD            = -0x64
+	_AT_REMOVEDIR        = 0x200
+	_AT_SYMLINK_NOFOLLOW = 0x100
+)
+
+type Termios struct {
+	Iflag     uint32
+	Oflag     uint32
+	Cflag     uint32
+	Lflag     uint32
+	Line      uint8
+	Cc        [32]uint8
+	Pad_cgo_0 [3]byte
+}
+
+const (
+	IUCLC  = 0x200
+	OLCUC  = 0x2
+	TCGETS = 0x540d
+	TCSETS = 0x540e
+	XCASE  = 0x4
+)
diff --git a/src/syscall/ztypes_linux_mips64.go b/src/syscall/ztypes_linux_mips64.go
index 9093086..925afb9 100644
--- a/src/syscall/ztypes_linux_mips64.go
+++ b/src/syscall/ztypes_linux_mips64.go
@@ -244,10 +244,9 @@ type Msghdr struct {
 }
 
 type Cmsghdr struct {
-	Len          uint64
-	Level        int32
-	Type         int32
-	X__cmsg_data [0]uint8
+	Len   uint64
+	Level int32
+	Type  int32
 }
 
 type Inet4Pktinfo struct {
diff --git a/src/syscall/ztypes_linux_mips64le.go b/src/syscall/ztypes_linux_mips64le.go
index 9093086..925afb9 100644
--- a/src/syscall/ztypes_linux_mips64le.go
+++ b/src/syscall/ztypes_linux_mips64le.go
@@ -244,10 +244,9 @@ type Msghdr struct {
 }
 
 type Cmsghdr struct {
-	Len          uint64
-	Level        int32
-	Type         int32
-	X__cmsg_data [0]uint8
+	Len   uint64
+	Level int32
+	Type  int32
 }
 
 type Inet4Pktinfo struct {
diff --git a/src/syscall/ztypes_linux_mipsle.go b/src/syscall/ztypes_linux_mipsle.go
new file mode 100644
index 0000000..7a8d34d
--- /dev/null
+++ b/src/syscall/ztypes_linux_mipsle.go
@@ -0,0 +1,592 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs types_linux.go
+
+package syscall
+
+const (
+	sizeofPtr      = 0x4
+	sizeofShort    = 0x2
+	sizeofInt      = 0x4
+	sizeofLong     = 0x4
+	sizeofLongLong = 0x8
+	PathMax        = 0x1000
+)
+
+type (
+	_C_short     int16
+	_C_int       int32
+	_C_long      int32
+	_C_long_long int64
+)
+
+type Timespec struct {
+	Sec  int32
+	Nsec int32
+}
+
+type Timeval struct {
+	Sec  int32
+	Usec int32
+}
+
+type Timex struct {
+	Modes     uint32
+	Offset    int32
+	Freq      int32
+	Maxerror  int32
+	Esterror  int32
+	Status    int32
+	Constant  int32
+	Precision int32
+	Tolerance int32
+	Time      Timeval
+	Tick      int32
+	Ppsfreq   int32
+	Jitter    int32
+	Shift     int32
+	Stabil    int32
+	Jitcnt    int32
+	Calcnt    int32
+	Errcnt    int32
+	Stbcnt    int32
+	Tai       int32
+	Pad_cgo_0 [44]byte
+}
+
+type Time_t int32
+
+type Tms struct {
+	Utime  int32
+	Stime  int32
+	Cutime int32
+	Cstime int32
+}
+
+type Utimbuf struct {
+	Actime  int32
+	Modtime int32
+}
+
+type Rusage struct {
+	Utime    Timeval
+	Stime    Timeval
+	Maxrss   int32
+	Ixrss    int32
+	Idrss    int32
+	Isrss    int32
+	Minflt   int32
+	Majflt   int32
+	Nswap    int32
+	Inblock  int32
+	Oublock  int32
+	Msgsnd   int32
+	Msgrcv   int32
+	Nsignals int32
+	Nvcsw    int32
+	Nivcsw   int32
+}
+
+type Rlimit struct {
+	Cur uint64
+	Max uint64
+}
+
+type _Gid_t uint32
+
+type Stat_t struct {
+	Dev     uint32
+	Pad1    [3]int32
+	Ino     uint64
+	Mode    uint32
+	Nlink   uint32
+	Uid     uint32
+	Gid     uint32
+	Rdev    uint32
+	Pad2    [3]int32
+	Size    int64
+	Atim    Timespec
+	Mtim    Timespec
+	Ctim    Timespec
+	Blksize int32
+	Pad4    int32
+	Blocks  int64
+	Pad5    [14]int32
+}
+
+type Statfs_t struct {
+	Type      int32
+	Bsize     int32
+	Frsize    int32
+	Pad_cgo_0 [4]byte
+	Blocks    uint64
+	Bfree     uint64
+	Files     uint64
+	Ffree     uint64
+	Bavail    uint64
+	Fsid      Fsid
+	Namelen   int32
+	Flags     int32
+	Spare     [5]int32
+	Pad_cgo_1 [4]byte
+}
+
+type Dirent struct {
+	Ino       uint64
+	Off       int64
+	Reclen    uint16
+	Type      uint8
+	Name      [256]int8
+	Pad_cgo_0 [5]byte
+}
+
+type Fsid struct {
+	X__val [2]int32
+}
+
+type Flock_t struct {
+	Type      int16
+	Whence    int16
+	Pad_cgo_0 [4]byte
+	Start     int64
+	Len       int64
+	Pid       int32
+	Pad_cgo_1 [4]byte
+}
+
+type RawSockaddrInet4 struct {
+	Family uint16
+	Port   uint16
+	Addr   [4]byte /* in_addr */
+	Zero   [8]uint8
+}
+
+type RawSockaddrInet6 struct {
+	Family   uint16
+	Port     uint16
+	Flowinfo uint32
+	Addr     [16]byte /* in6_addr */
+	Scope_id uint32
+}
+
+type RawSockaddrUnix struct {
+	Family uint16
+	Path   [108]int8
+}
+
+type RawSockaddrLinklayer struct {
+	Family   uint16
+	Protocol uint16
+	Ifindex  int32
+	Hatype   uint16
+	Pkttype  uint8
+	Halen    uint8
+	Addr     [8]uint8
+}
+
+type RawSockaddrNetlink struct {
+	Family uint16
+	Pad    uint16
+	Pid    uint32
+	Groups uint32
+}
+
+type RawSockaddr struct {
+	Family uint16
+	Data   [14]int8
+}
+
+type RawSockaddrAny struct {
+	Addr RawSockaddr
+	Pad  [96]int8
+}
+
+type _Socklen uint32
+
+type Linger struct {
+	Onoff  int32
+	Linger int32
+}
+
+type Iovec struct {
+	Base *byte
+	Len  uint32
+}
+
+type IPMreq struct {
+	Multiaddr [4]byte /* in_addr */
+	Interface [4]byte /* in_addr */
+}
+
+type IPMreqn struct {
+	Multiaddr [4]byte /* in_addr */
+	Address   [4]byte /* in_addr */
+	Ifindex   int32
+}
+
+type IPv6Mreq struct {
+	Multiaddr [16]byte /* in6_addr */
+	Interface uint32
+}
+
+type Msghdr struct {
+	Name       *byte
+	Namelen    uint32
+	Iov        *Iovec
+	Iovlen     uint32
+	Control    *byte
+	Controllen uint32
+	Flags      int32
+}
+
+type Cmsghdr struct {
+	Len   uint32
+	Level int32
+	Type  int32
+}
+
+type Inet4Pktinfo struct {
+	Ifindex  int32
+	Spec_dst [4]byte /* in_addr */
+	Addr     [4]byte /* in_addr */
+}
+
+type Inet6Pktinfo struct {
+	Addr    [16]byte /* in6_addr */
+	Ifindex uint32
+}
+
+type IPv6MTUInfo struct {
+	Addr RawSockaddrInet6
+	Mtu  uint32
+}
+
+type ICMPv6Filter struct {
+	Data [8]uint32
+}
+
+type Ucred struct {
+	Pid int32
+	Uid uint32
+	Gid uint32
+}
+
+type TCPInfo struct {
+	State          uint8
+	Ca_state       uint8
+	Retransmits    uint8
+	Probes         uint8
+	Backoff        uint8
+	Options        uint8
+	Pad_cgo_0      [2]byte
+	Rto            uint32
+	Ato            uint32
+	Snd_mss        uint32
+	Rcv_mss        uint32
+	Unacked        uint32
+	Sacked         uint32
+	Lost           uint32
+	Retrans        uint32
+	Fackets        uint32
+	Last_data_sent uint32
+	Last_ack_sent  uint32
+	Last_data_recv uint32
+	Last_ack_recv  uint32
+	Pmtu           uint32
+	Rcv_ssthresh   uint32
+	Rtt            uint32
+	Rttvar         uint32
+	Snd_ssthresh   uint32
+	Snd_cwnd       uint32
+	Advmss         uint32
+	Reordering     uint32
+	Rcv_rtt        uint32
+	Rcv_space      uint32
+	Total_retrans  uint32
+}
+
+const (
+	SizeofSockaddrInet4     = 0x10
+	SizeofSockaddrInet6     = 0x1c
+	SizeofSockaddrAny       = 0x70
+	SizeofSockaddrUnix      = 0x6e
+	SizeofSockaddrLinklayer = 0x14
+	SizeofSockaddrNetlink   = 0xc
+	SizeofLinger            = 0x8
+	SizeofIPMreq            = 0x8
+	SizeofIPMreqn           = 0xc
+	SizeofIPv6Mreq          = 0x14
+	SizeofMsghdr            = 0x1c
+	SizeofCmsghdr           = 0xc
+	SizeofInet4Pktinfo      = 0xc
+	SizeofInet6Pktinfo      = 0x14
+	SizeofIPv6MTUInfo       = 0x20
+	SizeofICMPv6Filter      = 0x20
+	SizeofUcred             = 0xc
+	SizeofTCPInfo           = 0x68
+)
+
+const (
+	IFA_UNSPEC          = 0x0
+	IFA_ADDRESS         = 0x1
+	IFA_LOCAL           = 0x2
+	IFA_LABEL           = 0x3
+	IFA_BROADCAST       = 0x4
+	IFA_ANYCAST         = 0x5
+	IFA_CACHEINFO       = 0x6
+	IFA_MULTICAST       = 0x7
+	IFLA_UNSPEC         = 0x0
+	IFLA_ADDRESS        = 0x1
+	IFLA_BROADCAST      = 0x2
+	IFLA_IFNAME         = 0x3
+	IFLA_MTU            = 0x4
+	IFLA_LINK           = 0x5
+	IFLA_QDISC          = 0x6
+	IFLA_STATS          = 0x7
+	IFLA_COST           = 0x8
+	IFLA_PRIORITY       = 0x9
+	IFLA_MASTER         = 0xa
+	IFLA_WIRELESS       = 0xb
+	IFLA_PROTINFO       = 0xc
+	IFLA_TXQLEN         = 0xd
+	IFLA_MAP            = 0xe
+	IFLA_WEIGHT         = 0xf
+	IFLA_OPERSTATE      = 0x10
+	IFLA_LINKMODE       = 0x11
+	IFLA_LINKINFO       = 0x12
+	IFLA_NET_NS_PID     = 0x13
+	IFLA_IFALIAS        = 0x14
+	IFLA_MAX            = 0x27
+	RT_SCOPE_UNIVERSE   = 0x0
+	RT_SCOPE_SITE       = 0xc8
+	RT_SCOPE_LINK       = 0xfd
+	RT_SCOPE_HOST       = 0xfe
+	RT_SCOPE_NOWHERE    = 0xff
+	RT_TABLE_UNSPEC     = 0x0
+	RT_TABLE_COMPAT     = 0xfc
+	RT_TABLE_DEFAULT    = 0xfd
+	RT_TABLE_MAIN       = 0xfe
+	RT_TABLE_LOCAL      = 0xff
+	RT_TABLE_MAX        = 0xffffffff
+	RTA_UNSPEC          = 0x0
+	RTA_DST             = 0x1
+	RTA_SRC             = 0x2
+	RTA_IIF             = 0x3
+	RTA_OIF             = 0x4
+	RTA_GATEWAY         = 0x5
+	RTA_PRIORITY        = 0x6
+	RTA_PREFSRC         = 0x7
+	RTA_METRICS         = 0x8
+	RTA_MULTIPATH       = 0x9
+	RTA_FLOW            = 0xb
+	RTA_CACHEINFO       = 0xc
+	RTA_TABLE           = 0xf
+	RTN_UNSPEC          = 0x0
+	RTN_UNICAST         = 0x1
+	RTN_LOCAL           = 0x2
+	RTN_BROADCAST       = 0x3
+	RTN_ANYCAST         = 0x4
+	RTN_MULTICAST       = 0x5
+	RTN_BLACKHOLE       = 0x6
+	RTN_UNREACHABLE     = 0x7
+	RTN_PROHIBIT        = 0x8
+	RTN_THROW           = 0x9
+	RTN_NAT             = 0xa
+	RTN_XRESOLVE        = 0xb
+	RTNLGRP_NONE        = 0x0
+	RTNLGRP_LINK        = 0x1
+	RTNLGRP_NOTIFY      = 0x2
+	RTNLGRP_NEIGH       = 0x3
+	RTNLGRP_TC          = 0x4
+	RTNLGRP_IPV4_IFADDR = 0x5
+	RTNLGRP_IPV4_MROUTE = 0x6
+	RTNLGRP_IPV4_ROUTE  = 0x7
+	RTNLGRP_IPV4_RULE   = 0x8
+	RTNLGRP_IPV6_IFADDR = 0x9
+	RTNLGRP_IPV6_MROUTE = 0xa
+	RTNLGRP_IPV6_ROUTE  = 0xb
+	RTNLGRP_IPV6_IFINFO = 0xc
+	RTNLGRP_IPV6_PREFIX = 0x12
+	RTNLGRP_IPV6_RULE   = 0x13
+	RTNLGRP_ND_USEROPT  = 0x14
+	SizeofNlMsghdr      = 0x10
+	SizeofNlMsgerr      = 0x14
+	SizeofRtGenmsg      = 0x1
+	SizeofNlAttr        = 0x4
+	SizeofRtAttr        = 0x4
+	SizeofIfInfomsg     = 0x10
+	SizeofIfAddrmsg     = 0x8
+	SizeofRtMsg         = 0xc
+	SizeofRtNexthop     = 0x8
+)
+
+type NlMsghdr struct {
+	Len   uint32
+	Type  uint16
+	Flags uint16
+	Seq   uint32
+	Pid   uint32
+}
+
+type NlMsgerr struct {
+	Error int32
+	Msg   NlMsghdr
+}
+
+type RtGenmsg struct {
+	Family uint8
+}
+
+type NlAttr struct {
+	Len  uint16
+	Type uint16
+}
+
+type RtAttr struct {
+	Len  uint16
+	Type uint16
+}
+
+type IfInfomsg struct {
+	Family     uint8
+	X__ifi_pad uint8
+	Type       uint16
+	Index      int32
+	Flags      uint32
+	Change     uint32
+}
+
+type IfAddrmsg struct {
+	Family    uint8
+	Prefixlen uint8
+	Flags     uint8
+	Scope     uint8
+	Index     uint32
+}
+
+type RtMsg struct {
+	Family   uint8
+	Dst_len  uint8
+	Src_len  uint8
+	Tos      uint8
+	Table    uint8
+	Protocol uint8
+	Scope    uint8
+	Type     uint8
+	Flags    uint32
+}
+
+type RtNexthop struct {
+	Len     uint16
+	Flags   uint8
+	Hops    uint8
+	Ifindex int32
+}
+
+const (
+	SizeofSockFilter = 0x8
+	SizeofSockFprog  = 0x8
+)
+
+type SockFilter struct {
+	Code uint16
+	Jt   uint8
+	Jf   uint8
+	K    uint32
+}
+
+type SockFprog struct {
+	Len       uint16
+	Pad_cgo_0 [2]byte
+	Filter    *SockFilter
+}
+
+type InotifyEvent struct {
+	Wd     int32
+	Mask   uint32
+	Cookie uint32
+	Len    uint32
+}
+
+const SizeofInotifyEvent = 0x10
+
+type PtraceRegs struct {
+	Regs        [109]uint32
+	U_tsize     uint32
+	U_dsize     uint32
+	U_ssize     uint32
+	Start_code  uint32
+	Start_data  uint32
+	Start_stack uint32
+	Signal      int32
+	U_ar0       *byte
+	Magic       uint32
+	U_comm      [32]int8
+}
+
+type FdSet struct {
+	Bits [32]int32
+}
+
+type Sysinfo_t struct {
+	Uptime    int32
+	Loads     [3]uint32
+	Totalram  uint32
+	Freeram   uint32
+	Sharedram uint32
+	Bufferram uint32
+	Totalswap uint32
+	Freeswap  uint32
+	Procs     uint16
+	Pad       uint16
+	Totalhigh uint32
+	Freehigh  uint32
+	Unit      uint32
+	X_f       [8]int8
+}
+
+type Utsname struct {
+	Sysname    [65]int8
+	Nodename   [65]int8
+	Release    [65]int8
+	Version    [65]int8
+	Machine    [65]int8
+	Domainname [65]int8
+}
+
+type Ustat_t struct {
+	Tfree  int32
+	Tinode uint32
+	Fname  [6]int8
+	Fpack  [6]int8
+}
+
+type EpollEvent struct {
+	Events uint32
+	PadFd  int32
+	Fd     int32
+	Pad    int32
+}
+
+const (
+	_AT_FDCWD            = -0x64
+	_AT_REMOVEDIR        = 0x200
+	_AT_SYMLINK_NOFOLLOW = 0x100
+)
+
+type Termios struct {
+	Iflag     uint32
+	Oflag     uint32
+	Cflag     uint32
+	Lflag     uint32
+	Line      uint8
+	Cc        [32]uint8
+	Pad_cgo_0 [3]byte
+}
+
+const (
+	IUCLC  = 0x200
+	OLCUC  = 0x2
+	TCGETS = 0x540d
+	TCSETS = 0x540e
+	XCASE  = 0x4
+)
diff --git a/src/syscall/ztypes_linux_ppc64.go b/src/syscall/ztypes_linux_ppc64.go
index 915ca95..de817f5 100644
--- a/src/syscall/ztypes_linux_ppc64.go
+++ b/src/syscall/ztypes_linux_ppc64.go
@@ -244,10 +244,9 @@ type Msghdr struct {
 }
 
 type Cmsghdr struct {
-	Len          uint64
-	Level        int32
-	Type         int32
-	X__cmsg_data [0]uint8
+	Len   uint64
+	Level int32
+	Type  int32
 }
 
 type Inet4Pktinfo struct {
diff --git a/src/syscall/ztypes_linux_ppc64le.go b/src/syscall/ztypes_linux_ppc64le.go
index a118055..e75d8e3 100644
--- a/src/syscall/ztypes_linux_ppc64le.go
+++ b/src/syscall/ztypes_linux_ppc64le.go
@@ -244,10 +244,9 @@ type Msghdr struct {
 }
 
 type Cmsghdr struct {
-	Len          uint64
-	Level        int32
-	Type         int32
-	X__cmsg_data [0]uint8
+	Len   uint64
+	Level int32
+	Type  int32
 }
 
 type Inet4Pktinfo struct {
diff --git a/src/syscall/ztypes_windows.go b/src/syscall/ztypes_windows.go
index 191c6e6..1fb6f5c 100644
--- a/src/syscall/ztypes_windows.go
+++ b/src/syscall/ztypes_windows.go
@@ -18,6 +18,7 @@ const (
 	ERROR_INSUFFICIENT_BUFFER Errno = 122
 	ERROR_MOD_NOT_FOUND       Errno = 126
 	ERROR_PROC_NOT_FOUND      Errno = 127
+	ERROR_DIR_NOT_EMPTY       Errno = 145
 	ERROR_ALREADY_EXISTS      Errno = 183
 	ERROR_ENVVAR_NOT_FOUND    Errno = 203
 	ERROR_MORE_DATA           Errno = 234
@@ -1115,4 +1116,5 @@ const (
 	_IO_REPARSE_TAG_MOUNT_POINT      = 0xA0000003
 	IO_REPARSE_TAG_SYMLINK           = 0xA000000C
 	SYMBOLIC_LINK_FLAG_DIRECTORY     = 0x1
+	_SYMLINK_FLAG_RELATIVE           = 1
 )
diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go
index 5d58b85..b1c6d2e 100644
--- a/src/testing/benchmark.go
+++ b/src/testing/benchmark.go
@@ -5,8 +5,10 @@
 package testing
 
 import (
+	"context"
 	"flag"
 	"fmt"
+	"internal/race"
 	"os"
 	"runtime"
 	"sync"
@@ -14,8 +16,8 @@ import (
 	"time"
 )
 
-var matchBenchmarks = flag.String("test.bench", "", "regular expression per path component to select benchmarks to run")
-var benchTime = flag.Duration("test.benchtime", 1*time.Second, "approximate run time for each benchmark")
+var matchBenchmarks = flag.String("test.bench", "", "run only benchmarks matching `regexp`")
+var benchTime = flag.Duration("test.benchtime", 1*time.Second, "run each benchmark for duration `d`")
 var benchmarkMemory = flag.Bool("test.benchmem", false, "print memory allocations for benchmarks")
 
 // Global lock to ensure only one benchmark runs at a time.
@@ -56,7 +58,6 @@ type B struct {
 	missingBytes     bool // one of the subbenchmarks does not have bytes set.
 	timerOn          bool
 	showAllocResult  bool
-	hasSub           bool
 	result           BenchmarkResult
 	parallelism      int // RunParallel creates parallelism*GOMAXPROCS goroutines
 	// The initial states of memStats.Mallocs and memStats.TotalAlloc.
@@ -127,11 +128,15 @@ func (b *B) nsPerOp() int64 {
 
 // runN runs a single benchmark for the specified number of iterations.
 func (b *B) runN(n int) {
+	b.ctx, b.cancel = context.WithCancel(b.parentContext())
+	defer b.cancel()
+
 	benchmarkLock.Lock()
 	defer benchmarkLock.Unlock()
 	// Try to get a comparable environment for each run
 	// by clearing garbage from previous runs.
 	runtime.GC()
+	b.raceErrors = -race.Errors()
 	b.N = n
 	b.parallelism = 1
 	b.ResetTimer()
@@ -140,6 +145,10 @@ func (b *B) runN(n int) {
 	b.StopTimer()
 	b.previousN = n
 	b.previousDuration = b.duration
+	b.raceErrors += race.Errors()
+	if b.raceErrors > 0 {
+		b.Errorf("race detected during execution of benchmark")
+	}
 }
 
 func min(x, y int) int {
@@ -263,10 +272,9 @@ func (b *B) launch() {
 	for n := 1; !b.failed && b.duration < d && n < 1e9; {
 		last := n
 		// Predict required iterations.
-		if b.nsPerOp() == 0 {
-			n = 1e9
-		} else {
-			n = int(d.Nanoseconds() / b.nsPerOp())
+		n = int(d.Nanoseconds())
+		if nsop := b.nsPerOp(); nsop != 0 {
+			n /= int(nsop)
 		}
 		// Run more iterations than we think we'll need (1.2x).
 		// Don't grow too fast in case we had timing errors previously.
@@ -359,10 +367,10 @@ type benchContext struct {
 // An internal function but exported because it is cross-package; part of the implementation
 // of the "go test" command.
 func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
-	runBenchmarksInternal(matchString, benchmarks)
+	runBenchmarks(matchString, benchmarks)
 }
 
-func runBenchmarksInternal(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) bool {
+func runBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) bool {
 	// If no flag was specified, don't run benchmarks.
 	if len(*matchBenchmarks) == 0 {
 		return true
diff --git a/src/testing/example.go b/src/testing/example.go
index fd8343f..e5bce7a 100644
--- a/src/testing/example.go
+++ b/src/testing/example.go
@@ -21,7 +21,14 @@ type InternalExample struct {
 	Unordered bool
 }
 
+// An internal function but exported because it is cross-package; part of the implementation
+// of the "go test" command.
 func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
+	_, ok = runExamples(matchString, examples)
+	return ok
+}
+
+func runExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ran, ok bool) {
 	ok = true
 
 	var eg InternalExample
@@ -35,12 +42,13 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int
 		if !matched {
 			continue
 		}
+		ran = true
 		if !runExample(eg) {
 			ok = false
 		}
 	}
 
-	return
+	return ran, ok
 }
 
 func sortLines(output string) string {
diff --git a/src/testing/internal/testdeps/deps.go b/src/testing/internal/testdeps/deps.go
new file mode 100644
index 0000000..b08300b
--- /dev/null
+++ b/src/testing/internal/testdeps/deps.go
@@ -0,0 +1,51 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package testdeps provides access to dependencies needed by test execution.
+//
+// This package is imported by the generated main package, which passes
+// TestDeps into testing.Main. This allows tests to use packages at run time
+// without making those packages direct dependencies of package testing.
+// Direct dependencies of package testing are harder to write tests for.
+package testdeps
+
+import (
+	"io"
+	"regexp"
+	"runtime/pprof"
+)
+
+// TestDeps is an implementation of the testing.testDeps interface,
+// suitable for passing to testing.MainStart.
+type TestDeps struct{}
+
+var matchPat string
+var matchRe *regexp.Regexp
+
+func (TestDeps) MatchString(pat, str string) (result bool, err error) {
+	if matchRe == nil || matchPat != pat {
+		matchPat = pat
+		matchRe, err = regexp.Compile(matchPat)
+		if err != nil {
+			return
+		}
+	}
+	return matchRe.MatchString(str), nil
+}
+
+func (TestDeps) StartCPUProfile(w io.Writer) error {
+	return pprof.StartCPUProfile(w)
+}
+
+func (TestDeps) StopCPUProfile() {
+	pprof.StopCPUProfile()
+}
+
+func (TestDeps) WriteHeapProfile(w io.Writer) error {
+	return pprof.WriteHeapProfile(w)
+}
+
+func (TestDeps) WriteProfileTo(name string, w io.Writer, debug int) error {
+	return pprof.Lookup(name).WriteTo(w, debug)
+}
diff --git a/src/testing/quick/quick.go b/src/testing/quick/quick.go
index 798d41a..95860fd 100644
--- a/src/testing/quick/quick.go
+++ b/src/testing/quick/quick.go
@@ -3,6 +3,8 @@
 // license that can be found in the LICENSE file.
 
 // Package quick implements utility functions to help with black box testing.
+//
+// The testing/quick package is frozen and is not accepting new features.
 package quick
 
 import (
diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go
index 2a24aaa..563e865 100644
--- a/src/testing/sub_test.go
+++ b/src/testing/sub_test.go
@@ -6,6 +6,7 @@ package testing
 
 import (
 	"bytes"
+	"context"
 	"regexp"
 	"strings"
 	"sync/atomic"
@@ -277,28 +278,33 @@ func TestTRun(t *T) {
 		ok:     true,
 		maxPar: 4,
 		f: func(t *T) {
-			t.Parallel()
-			for i := 0; i < 12; i++ {
-				t.Run("a", func(t *T) {
-					t.Parallel()
-					time.Sleep(time.Nanosecond)
-					for i := 0; i < 12; i++ {
-						t.Run("b", func(t *T) {
-							time.Sleep(time.Nanosecond)
-							for i := 0; i < 12; i++ {
-								t.Run("c", func(t *T) {
-									t.Parallel()
-									time.Sleep(time.Nanosecond)
-									t.Run("d1", func(t *T) {})
-									t.Run("d2", func(t *T) {})
-									t.Run("d3", func(t *T) {})
-									t.Run("d4", func(t *T) {})
-								})
-							}
-						})
-					}
-				})
-			}
+			// t.Parallel doesn't work in the pseudo-T we start with:
+			// it leaks a goroutine.
+			// Call t.Run to get a real one.
+			t.Run("X", func(t *T) {
+				t.Parallel()
+				for i := 0; i < 12; i++ {
+					t.Run("a", func(t *T) {
+						t.Parallel()
+						time.Sleep(time.Nanosecond)
+						for i := 0; i < 12; i++ {
+							t.Run("b", func(t *T) {
+								time.Sleep(time.Nanosecond)
+								for i := 0; i < 12; i++ {
+									t.Run("c", func(t *T) {
+										t.Parallel()
+										time.Sleep(time.Nanosecond)
+										t.Run("d1", func(t *T) {})
+										t.Run("d2", func(t *T) {})
+										t.Run("d3", func(t *T) {})
+										t.Run("d4", func(t *T) {})
+									})
+								}
+							})
+						}
+					})
+				}
+			})
 		},
 	}, {
 		desc:   "skip output",
@@ -341,6 +347,7 @@ func TestTRun(t *T) {
 			},
 			context: ctx,
 		}
+		root.ctx, root.cancel = context.WithCancel(context.Background())
 		ok := root.Run(tc.desc, tc.f)
 		ctx.release()
 
diff --git a/src/testing/testing.go b/src/testing/testing.go
index e1dbe00..f08c5c6 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -137,13 +137,17 @@
 // of the top-level test and the sequence of names passed to Run, separated by
 // slashes, with an optional trailing sequence number for disambiguation.
 //
-// The argument to the -run and -bench command-line flags is a slash-separated
-// list of regular expressions that match each name element in turn.
-// For example:
+// The argument to the -run and -bench command-line flags is an unanchored regular
+// expression that matches the test's name. For tests with multiple slash-separated
+// elements, such as subtests, the argument is itself slash-separated, with
+// expressions matching each name element in turn. Because it is unanchored, an
+// empty expression matches any string.
+// For example, using "matching" to mean "whose name contains":
 //
-//     go test -run Foo     # Run top-level tests matching "Foo".
-//     go test -run Foo/A=  # Run subtests of Foo matching "A=".
-//     go test -run /A=1    # Run all subtests of a top-level test matching "A=1".
+//     go test -run ''      # Run all tests.
+//     go test -run Foo     # Run top-level tests matching "Foo", such as "TestFooBar".
+//     go test -run Foo/A=  # For top-level tests matching "Foo", run subtests matching "A=".
+//     go test -run /A=1    # For all top-level tests, run subtests matching "A=1".
 //
 // Subtests can also be used to control parallelism. A parent test will only
 // complete once all of its subtests complete. In this example, all tests are
@@ -192,7 +196,7 @@
 // A simple implementation of TestMain is:
 //
 //	func TestMain(m *testing.M) {
-//		flag.Parse()
+//		// call flag.Parse() here if TestMain uses flags
 //		os.Exit(m.Run())
 //	}
 //
@@ -200,13 +204,15 @@ package testing
 
 import (
 	"bytes"
+	"context"
+	"errors"
 	"flag"
 	"fmt"
+	"internal/race"
 	"io"
 	"os"
 	"runtime"
 	"runtime/debug"
-	"runtime/pprof"
 	"runtime/trace"
 	"strconv"
 	"strings"
@@ -226,22 +232,24 @@ var (
 	// "go test", the binary always runs in the source directory for the package;
 	// this flag lets "go test" tell the binary to write the files in the directory where
 	// the "go test" command is run.
-	outputDir = flag.String("test.outputdir", "", "directory in which to write profiles")
+	outputDir = flag.String("test.outputdir", "", "write profiles to `dir`")
 
 	// Report as tests are run; default is silent for success.
-	chatty           = flag.Bool("test.v", false, "verbose: print additional output")
-	count            = flag.Uint("test.count", 1, "run tests and benchmarks `n` times")
-	coverProfile     = flag.String("test.coverprofile", "", "write a coverage profile to the named file after execution")
-	match            = flag.String("test.run", "", "regular expression to select tests and examples to run")
-	memProfile       = flag.String("test.memprofile", "", "write a memory profile to the named file after execution")
-	memProfileRate   = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate")
-	cpuProfile       = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution")
-	blockProfile     = flag.String("test.blockprofile", "", "write a goroutine blocking profile to the named file after execution")
-	blockProfileRate = flag.Int("test.blockprofilerate", 1, "if >= 0, calls runtime.SetBlockProfileRate()")
-	traceFile        = flag.String("test.trace", "", "write an execution trace to the named file after execution")
-	timeout          = flag.Duration("test.timeout", 0, "if positive, sets an aggregate time limit for all tests")
-	cpuListStr       = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test")
-	parallel         = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "maximum test parallelism")
+	chatty               = flag.Bool("test.v", false, "verbose: print additional output")
+	count                = flag.Uint("test.count", 1, "run tests and benchmarks `n` times")
+	coverProfile         = flag.String("test.coverprofile", "", "write a coverage profile to `file`")
+	match                = flag.String("test.run", "", "run only tests and examples matching `regexp`")
+	memProfile           = flag.String("test.memprofile", "", "write a memory profile to `file`")
+	memProfileRate       = flag.Int("test.memprofilerate", 0, "set memory profiling `rate` (see runtime.MemProfileRate)")
+	cpuProfile           = flag.String("test.cpuprofile", "", "write a cpu profile to `file`")
+	blockProfile         = flag.String("test.blockprofile", "", "write a goroutine blocking profile to `file`")
+	blockProfileRate     = flag.Int("test.blockprofilerate", 1, "set blocking profile `rate` (see runtime.SetBlockProfileRate)")
+	mutexProfile         = flag.String("test.mutexprofile", "", "write a mutex contention profile to the named file after execution")
+	mutexProfileFraction = flag.Int("test.mutexprofilefraction", 1, "if >= 0, calls runtime.SetMutexProfileFraction()")
+	traceFile            = flag.String("test.trace", "", "write an execution trace to `file`")
+	timeout              = flag.Duration("test.timeout", 0, "fail test binary execution after duration `d` (0 means unlimited)")
+	cpuListStr           = flag.String("test.cpu", "", "comma-separated `list` of cpu counts to run each test with")
+	parallel             = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "run at most `n` tests in parallel")
 
 	haveExamples bool // are there examples?
 
@@ -251,14 +259,19 @@ var (
 // common holds the elements common between T and B and
 // captures common methods such as Errorf.
 type common struct {
-	mu       sync.RWMutex // guards output, failed, and done.
-	output   []byte       // Output generated by test or benchmark.
-	w        io.Writer    // For flushToParent.
-	chatty   bool         // A copy of the chatty flag.
-	failed   bool         // Test or benchmark has failed.
-	skipped  bool         // Test of benchmark has been skipped.
-	finished bool         // Test function has completed.
-	done     bool         // Test is finished and all subtests have completed.
+	mu         sync.RWMutex // guards output, failed, and done.
+	output     []byte       // Output generated by test or benchmark.
+	w          io.Writer    // For flushToParent.
+	ctx        context.Context
+	cancel     context.CancelFunc
+	chatty     bool // A copy of the chatty flag.
+	ran        bool // Test or benchmark (or one of its subtests) was executed.
+	failed     bool // Test or benchmark has failed.
+	skipped    bool // Test of benchmark has been skipped.
+	finished   bool // Test function has completed.
+	done       bool // Test is finished and all subtests have completed.
+	hasSub     bool
+	raceErrors int // number of races detected during test
 
 	parent   *common
 	level    int       // Nesting depth of test or benchmark.
@@ -270,11 +283,25 @@ type common struct {
 	sub      []*T      // Queue of subtests to be run in parallel.
 }
 
+func (c *common) parentContext() context.Context {
+	if c == nil || c.parent == nil || c.parent.ctx == nil {
+		return context.Background()
+	}
+	return c.parent.ctx
+}
+
 // Short reports whether the -test.short flag is set.
 func Short() bool {
 	return *short
 }
 
+// CoverMode reports what the test coverage mode is set to. The
+// values are "set", "count", or "atomic". The return value will be
+// empty if test coverage is not enabled.
+func CoverMode() string {
+	return cover.Mode
+}
+
 // Verbose reports whether the -test.v flag is set.
 func Verbose() bool {
 	return *chatty
@@ -359,6 +386,7 @@ func fmtDuration(d time.Duration) string {
 
 // TB is the interface common to T and B.
 type TB interface {
+	Context() context.Context
 	Error(args ...interface{})
 	Errorf(format string, args ...interface{})
 	Fail()
@@ -368,6 +396,7 @@ type TB interface {
 	Fatalf(format string, args ...interface{})
 	Log(args ...interface{})
 	Logf(format string, args ...interface{})
+	Name() string
 	Skip(args ...interface{})
 	SkipNow()
 	Skipf(format string, args ...interface{})
@@ -400,6 +429,29 @@ type T struct {
 
 func (c *common) private() {}
 
+// Name returns the name of the running test or benchmark.
+func (c *common) Name() string {
+	return c.name
+}
+
+// Context returns the context for the current test or benchmark.
+// The context is cancelled when the test or benchmark finishes.
+// A goroutine started during a test or benchmark can wait for the
+// context's Done channel to become readable as a signal that the
+// test or benchmark is over, so that the goroutine can exit.
+func (c *common) Context() context.Context {
+	return c.ctx
+}
+
+func (c *common) setRan() {
+	if c.parent != nil {
+		c.parent.setRan()
+	}
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	c.ran = true
+}
+
 // Fail marks the function as having failed but continues execution.
 func (c *common) Fail() {
 	if c.parent != nil {
@@ -466,10 +518,11 @@ func (c *common) log(s string) {
 // printed to avoid having performance depend on the value of the -test.v flag.
 func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) }
 
-// Logf formats its arguments according to the format, analogous to Printf,
-// and records the text in the error log. For tests, the text will be printed only if
-// the test fails or the -test.v flag is set. For benchmarks, the text is always
-// printed to avoid having performance depend on the value of the -test.v flag.
+// Logf formats its arguments according to the format, analogous to Printf, and
+// records the text in the error log. A final newline is added if not provided. For
+// tests, the text will be printed only if the test fails or the -test.v flag is
+// set. For benchmarks, the text is always printed to avoid having performance
+// depend on the value of the -test.v flag.
 func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) }
 
 // Error is equivalent to Log followed by Fail.
@@ -509,6 +562,8 @@ func (c *common) Skipf(format string, args ...interface{}) {
 }
 
 // SkipNow marks the test as having been skipped and stops its execution.
+// If a test fails (see Error, Errorf, Fail) and is then skipped,
+// it is still considered to have failed.
 // Execution will continue at the next test or benchmark. See also FailNow.
 // SkipNow must be called from the goroutine running the test, not from
 // other goroutines created during the test. Calling SkipNow does not stop
@@ -547,11 +602,13 @@ func (t *T) Parallel() {
 
 	// Add to the list of tests to be released by the parent.
 	t.parent.sub = append(t.parent.sub, t)
+	t.raceErrors += race.Errors()
 
 	t.signal <- true   // Release calling test.
 	<-t.parent.barrier // Wait for the parent test to complete.
 	t.context.waitParallel()
 	t.start = time.Now()
+	t.raceErrors += -race.Errors()
 }
 
 // An internal type but exported because it is cross-package; part of the implementation
@@ -562,11 +619,19 @@ type InternalTest struct {
 }
 
 func tRunner(t *T, fn func(t *T)) {
+	t.ctx, t.cancel = context.WithCancel(t.parentContext())
+	defer t.cancel()
+
 	// When this goroutine is done, either because fn(t)
 	// returned normally or because a test failure triggered
 	// a call to runtime.Goexit, record the duration and send
 	// a signal saying that the test is done.
 	defer func() {
+		t.raceErrors += race.Errors()
+		if t.raceErrors > 0 {
+			t.Errorf("race detected during execution of test")
+		}
+
 		t.duration += time.Now().Sub(t.start)
 		// If the test panicked, print any test output before dying.
 		err := recover()
@@ -603,10 +668,14 @@ func tRunner(t *T, fn func(t *T)) {
 		// Do not lock t.done to allow race detector to detect race in case
 		// the user does not appropriately synchronizes a goroutine.
 		t.done = true
+		if t.parent != nil && !t.hasSub {
+			t.setRan()
+		}
 		t.signal <- true
 	}()
 
 	t.start = time.Now()
+	t.raceErrors = -race.Errors()
 	fn(t)
 	t.finished = true
 }
@@ -614,6 +683,7 @@ func tRunner(t *T, fn func(t *T)) {
 // Run runs f as a subtest of t called name. It reports whether f succeeded.
 // Run will block until all its parallel subtests have completed.
 func (t *T) Run(name string, f func(t *T)) bool {
+	t.hasSub = true
 	testName, ok := t.context.match.fullName(&t.common, name)
 	if !ok {
 		return true
@@ -702,29 +772,57 @@ func (c *testContext) release() {
 	c.startParallel <- true // Pick a waiting test to be run.
 }
 
-// An internal function but exported because it is cross-package; part of the implementation
-// of the "go test" command.
+// No one should be using func Main anymore.
+// See the doc comment on func Main and use MainStart instead.
+var errMain = errors.New("testing: unexpected use of func Main")
+
+type matchStringOnly func(pat, str string) (bool, error)
+
+func (f matchStringOnly) MatchString(pat, str string) (bool, error)   { return f(pat, str) }
+func (f matchStringOnly) StartCPUProfile(w io.Writer) error           { return errMain }
+func (f matchStringOnly) StopCPUProfile()                             {}
+func (f matchStringOnly) WriteHeapProfile(w io.Writer) error          { return errMain }
+func (f matchStringOnly) WriteProfileTo(string, io.Writer, int) error { return errMain }
+
+// Main is an internal function, part of the implementation of the "go test" command.
+// It was exported because it is cross-package and predates "internal" packages.
+// It is no longer used by "go test" but preserved, as much as possible, for other
+// systems that simulate "go test" using Main, but Main sometimes cannot be updated as
+// new functionality is added to the testing package.
+// Systems simulating "go test" should be updated to use MainStart.
 func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
-	os.Exit(MainStart(matchString, tests, benchmarks, examples).Run())
+	os.Exit(MainStart(matchStringOnly(matchString), tests, benchmarks, examples).Run())
 }
 
 // M is a type passed to a TestMain function to run the actual tests.
 type M struct {
-	matchString func(pat, str string) (bool, error)
-	tests       []InternalTest
-	benchmarks  []InternalBenchmark
-	examples    []InternalExample
+	deps       testDeps
+	tests      []InternalTest
+	benchmarks []InternalBenchmark
+	examples   []InternalExample
+}
+
+// testDeps is an internal interface of functionality that is
+// passed into this package by a test's generated main package.
+// The canonical implementation of this interface is
+// testing/internal/testdeps's TestDeps.
+type testDeps interface {
+	MatchString(pat, str string) (bool, error)
+	StartCPUProfile(io.Writer) error
+	StopCPUProfile()
+	WriteHeapProfile(io.Writer) error
+	WriteProfileTo(string, io.Writer, int) error
 }
 
 // MainStart is meant for use by tests generated by 'go test'.
 // It is not meant to be called directly and is not subject to the Go 1 compatibility document.
 // It may change signature from release to release.
-func MainStart(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
+func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
 	return &M{
-		matchString: matchString,
-		tests:       tests,
-		benchmarks:  benchmarks,
-		examples:    examples,
+		deps:       deps,
+		tests:      tests,
+		benchmarks: benchmarks,
+		examples:   examples,
 	}
 }
 
@@ -737,19 +835,22 @@ func (m *M) Run() int {
 
 	parseCpuList()
 
-	before()
+	m.before()
 	startAlarm()
 	haveExamples = len(m.examples) > 0
-	testOk := RunTests(m.matchString, m.tests)
-	exampleOk := RunExamples(m.matchString, m.examples)
-	stopAlarm()
-	if !testOk || !exampleOk || !runBenchmarksInternal(m.matchString, m.benchmarks) {
+	testRan, testOk := runTests(m.deps.MatchString, m.tests)
+	exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
+	if !testRan && !exampleRan && *matchBenchmarks == "" {
+		fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
+	}
+	if !testOk || !exampleOk || !runBenchmarks(m.deps.MatchString, m.benchmarks) || race.Errors() > 0 {
 		fmt.Println("FAIL")
-		after()
+		m.after()
 		return 1
 	}
+
 	fmt.Println("PASS")
-	after()
+	m.after()
 	return 0
 }
 
@@ -770,12 +871,18 @@ func (t *T) report() {
 	}
 }
 
+// An internal function but exported because it is cross-package; part of the implementation
+// of the "go test" command.
 func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) {
-	ok = true
-	if len(tests) == 0 && !haveExamples {
+	ran, ok := runTests(matchString, tests)
+	if !ran && !haveExamples {
 		fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
-		return
 	}
+	return ok
+}
+
+func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ran, ok bool) {
+	ok = true
 	for _, procs := range cpuList {
 		runtime.GOMAXPROCS(procs)
 		ctx := newTestContext(*parallel, newMatcher(matchString, *match, "-test.run"))
@@ -798,12 +905,13 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
 			go func() { <-t.signal }()
 		})
 		ok = ok && !t.Failed()
+		ran = ran || t.ran
 	}
-	return
+	return ran, ok
 }
 
 // before runs before all testing.
-func before() {
+func (m *M) before() {
 	if *memProfileRate > 0 {
 		runtime.MemProfileRate = *memProfileRate
 	}
@@ -813,7 +921,7 @@ func before() {
 			fmt.Fprintf(os.Stderr, "testing: %s", err)
 			return
 		}
-		if err := pprof.StartCPUProfile(f); err != nil {
+		if err := m.deps.StartCPUProfile(f); err != nil {
 			fmt.Fprintf(os.Stderr, "testing: can't start cpu profile: %s", err)
 			f.Close()
 			return
@@ -836,6 +944,9 @@ func before() {
 	if *blockProfile != "" && *blockProfileRate >= 0 {
 		runtime.SetBlockProfileRate(*blockProfileRate)
 	}
+	if *mutexProfile != "" && *mutexProfileFraction >= 0 {
+		runtime.SetMutexProfileFraction(*mutexProfileFraction)
+	}
 	if *coverProfile != "" && cover.Mode == "" {
 		fmt.Fprintf(os.Stderr, "testing: cannot use -test.coverprofile because test binary was not built with coverage enabled\n")
 		os.Exit(2)
@@ -843,9 +954,9 @@ func before() {
 }
 
 // after runs after all testing.
-func after() {
+func (m *M) after() {
 	if *cpuProfile != "" {
-		pprof.StopCPUProfile() // flushes profile to disk
+		m.deps.StopCPUProfile() // flushes profile to disk
 	}
 	if *traceFile != "" {
 		trace.Stop() // flushes trace to disk
@@ -857,7 +968,7 @@ func after() {
 			os.Exit(2)
 		}
 		runtime.GC() // materialize all statistics
-		if err = pprof.WriteHeapProfile(f); err != nil {
+		if err = m.deps.WriteHeapProfile(f); err != nil {
 			fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *memProfile, err)
 			os.Exit(2)
 		}
@@ -869,7 +980,19 @@ func after() {
 			fmt.Fprintf(os.Stderr, "testing: %s\n", err)
 			os.Exit(2)
 		}
-		if err = pprof.Lookup("block").WriteTo(f, 0); err != nil {
+		if err = m.deps.WriteProfileTo("block", f, 0); err != nil {
+			fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *blockProfile, err)
+			os.Exit(2)
+		}
+		f.Close()
+	}
+	if *mutexProfile != "" && *mutexProfileFraction >= 0 {
+		f, err := os.Create(toOutputDir(*mutexProfile))
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+			os.Exit(2)
+		}
+		if err = m.deps.WriteProfileTo("mutex", f, 0); err != nil {
 			fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *blockProfile, err)
 			os.Exit(2)
 		}
diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go
index 45e4468..9954f9a 100644
--- a/src/testing/testing_test.go
+++ b/src/testing/testing_test.go
@@ -5,14 +5,42 @@
 package testing_test
 
 import (
+	"fmt"
 	"os"
+	"runtime"
 	"testing"
+	"time"
 )
 
-// This is exactly what a test would do without a TestMain.
-// It's here only so that there is at least one package in the
-// standard library with a TestMain, so that code is executed.
-
 func TestMain(m *testing.M) {
-	os.Exit(m.Run())
+	g0 := runtime.NumGoroutine()
+
+	code := m.Run()
+	if code != 0 {
+		os.Exit(code)
+	}
+
+	// Check that there are no goroutines left behind.
+	t0 := time.Now()
+	stacks := make([]byte, 1<<20)
+	for {
+		g1 := runtime.NumGoroutine()
+		if g1 == g0 {
+			return
+		}
+		stacks = stacks[:runtime.Stack(stacks, true)]
+		time.Sleep(50 * time.Millisecond)
+		if time.Since(t0) > 2*time.Second {
+			fmt.Fprintf(os.Stderr, "Unexpected leftover goroutines detected: %v -> %v\n%s\n", g0, g1, stacks)
+			os.Exit(1)
+		}
+	}
+}
+
+func TestContextCancel(t *testing.T) {
+	ctx := t.Context()
+	// Tests we don't leak this goroutine:
+	go func() {
+		<-ctx.Done()
+	}()
 }
diff --git a/src/text/tabwriter/tabwriter.go b/src/text/tabwriter/tabwriter.go
index 796e1e8..752c9b8 100644
--- a/src/text/tabwriter/tabwriter.go
+++ b/src/text/tabwriter/tabwriter.go
@@ -8,6 +8,7 @@
 // The package is using the Elastic Tabstops algorithm described at
 // http://nickgravgaard.com/elastictabstops/index.html.
 //
+// The text/tabwriter package is frozen and is not accepting new features.
 package tabwriter
 
 import (
diff --git a/src/text/template/exec.go b/src/text/template/exec.go
index 8e5ad93..ea964dc 100644
--- a/src/text/template/exec.go
+++ b/src/text/template/exec.go
@@ -171,20 +171,26 @@ func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{})
 // execution stops, but partial results may already have been written to
 // the output writer.
 // A template may be executed safely in parallel.
+//
+// If data is a reflect.Value, the template applies to the concrete
+// value that the reflect.Value holds, as in fmt.Print.
 func (t *Template) Execute(wr io.Writer, data interface{}) error {
 	return t.execute(wr, data)
 }
 
 func (t *Template) execute(wr io.Writer, data interface{}) (err error) {
 	defer errRecover(&err)
-	value := reflect.ValueOf(data)
+	value, ok := data.(reflect.Value)
+	if !ok {
+		value = reflect.ValueOf(data)
+	}
 	state := &state{
 		tmpl: t,
 		wr:   wr,
 		vars: []variable{{"$", value}},
 	}
 	if t.Tree == nil || t.Root == nil {
-		state.errorf("%q is an incomplete or empty template%s", t.Name(), t.DefinedTemplates())
+		state.errorf("%q is an incomplete or empty template", t.Name())
 	}
 	state.walk(value, t.Root)
 	return
@@ -535,6 +541,9 @@ func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd
 // value of the pipeline, if any.
 func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value {
 	if !receiver.IsValid() {
+		if s.tmpl.option.missingKey == mapError { // Treat invalid value as missing map key.
+			s.errorf("nil data; no entry for key %q", fieldName)
+		}
 		return zero
 	}
 	typ := receiver.Type()
@@ -596,8 +605,9 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
 }
 
 var (
-	errorType       = reflect.TypeOf((*error)(nil)).Elem()
-	fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
+	errorType        = reflect.TypeOf((*error)(nil)).Elem()
+	fmtStringerType  = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
+	reflectValueType = reflect.TypeOf((*reflect.Value)(nil)).Elem()
 )
 
 // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
@@ -661,7 +671,11 @@ func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, a
 		s.at(node)
 		s.errorf("error calling %s: %s", name, result[1].Interface().(error))
 	}
-	return result[0]
+	v := result[0]
+	if v.Type() == reflectValueType {
+		v = v.Interface().(reflect.Value)
+	}
+	return v
 }
 
 // canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
@@ -669,6 +683,8 @@ func canBeNil(typ reflect.Type) bool {
 	switch typ.Kind() {
 	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
 		return true
+	case reflect.Struct:
+		return typ == reflectValueType
 	}
 	return false
 }
@@ -682,6 +698,9 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
 		}
 		s.errorf("invalid value; expected %s", typ)
 	}
+	if typ == reflectValueType && value.Type() != typ {
+		return reflect.ValueOf(value)
+	}
 	if typ != nil && !value.Type().AssignableTo(typ) {
 		if value.Kind() == reflect.Interface && !value.IsNil() {
 			value = value.Elem()
@@ -743,6 +762,10 @@ func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) refle
 		if typ.NumMethod() == 0 {
 			return s.evalEmptyInterface(dot, n)
 		}
+	case reflect.Struct:
+		if typ == reflectValueType {
+			return reflect.ValueOf(s.evalEmptyInterface(dot, n))
+		}
 	case reflect.String:
 		return s.evalString(typ, n)
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -854,6 +877,20 @@ func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
 	return v, false
 }
 
+// indirectInterface returns the concrete value in an interface value,
+// or else the zero reflect.Value.
+// That is, if v represents the interface value x, the result is the same as reflect.ValueOf(x):
+// the fact that x was an interface value is forgotten.
+func indirectInterface(v reflect.Value) reflect.Value {
+	if v.Kind() != reflect.Interface {
+		return v
+	}
+	if v.IsNil() {
+		return reflect.Value{}
+	}
+	return v.Elem()
+}
+
 // printValue writes the textual representation of the value to the output of
 // the template.
 func (s *state) printValue(n parse.Node, v reflect.Value) {
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
index 3ef065e..5892b27 100644
--- a/src/text/template/exec_test.go
+++ b/src/text/template/exec_test.go
@@ -932,7 +932,7 @@ func TestMessageForExecuteEmpty(t *testing.T) {
 		t.Fatal("expected second error")
 	}
 	got = err.Error()
-	want = `template: empty: "empty" is an incomplete or empty template; defined templates are: "secondary"`
+	want = `template: empty: "empty" is an incomplete or empty template`
 	if got != want {
 		t.Errorf("expected error %s got %s", want, got)
 	}
@@ -1142,6 +1142,12 @@ func TestMissingMapKey(t *testing.T) {
 	if err == nil {
 		t.Errorf("expected error; got none")
 	}
+	// same Option, but now a nil interface: ask for an error
+	err = tmpl.Execute(&b, nil)
+	t.Log(err)
+	if err == nil {
+		t.Errorf("expected error for nil-interface; got none")
+	}
 }
 
 // Test that the error message for multiline unterminated string
@@ -1152,7 +1158,7 @@ func TestUnterminatedStringError(t *testing.T) {
 		t.Fatal("expected error")
 	}
 	str := err.Error()
-	if !strings.Contains(str, "X:3: unexpected unterminated raw quoted strin") {
+	if !strings.Contains(str, "X:3: unexpected unterminated raw quoted string") {
 		t.Fatalf("unexpected error: %s", str)
 	}
 }
@@ -1310,3 +1316,99 @@ func TestMaxExecDepth(t *testing.T) {
 		t.Errorf("got error %q; want %q", got, want)
 	}
 }
+
+func TestAddrOfIndex(t *testing.T) {
+	// golang.org/issue/14916.
+	// Before index worked on reflect.Values, the .String could not be
+	// found on the (incorrectly unaddressable) V value,
+	// in contrast to range, which worked fine.
+	// Also testing that passing a reflect.Value to tmpl.Execute works.
+	texts := []string{
+		`{{range .}}{{.String}}{{end}}`,
+		`{{with index . 0}}{{.String}}{{end}}`,
+	}
+	for _, text := range texts {
+		tmpl := Must(New("tmpl").Parse(text))
+		var buf bytes.Buffer
+		err := tmpl.Execute(&buf, reflect.ValueOf([]V{{1}}))
+		if err != nil {
+			t.Fatalf("%s: Execute: %v", text, err)
+		}
+		if buf.String() != "<1>" {
+			t.Fatalf("%s: template output = %q, want %q", text, &buf, "<1>")
+		}
+	}
+}
+
+func TestInterfaceValues(t *testing.T) {
+	// golang.org/issue/17714.
+	// Before index worked on reflect.Values, interface values
+	// were always implicitly promoted to the underlying value,
+	// except that nil interfaces were promoted to the zero reflect.Value.
+	// Eliminating a round trip to interface{} and back to reflect.Value
+	// eliminated this promotion, breaking these cases.
+	tests := []struct {
+		text string
+		out  string
+	}{
+		{`{{index .Nil 1}}`, "ERROR: index of untyped nil"},
+		{`{{index .Slice 2}}`, "2"},
+		{`{{index .Slice .Two}}`, "2"},
+		{`{{call .Nil 1}}`, "ERROR: call of nil"},
+		{`{{call .PlusOne 1}}`, "2"},
+		{`{{call .PlusOne .One}}`, "2"},
+		{`{{and (index .Slice 0) true}}`, "0"},
+		{`{{and .Zero true}}`, "0"},
+		{`{{and (index .Slice 1) false}}`, "false"},
+		{`{{and .One false}}`, "false"},
+		{`{{or (index .Slice 0) false}}`, "false"},
+		{`{{or .Zero false}}`, "false"},
+		{`{{or (index .Slice 1) true}}`, "1"},
+		{`{{or .One true}}`, "1"},
+		{`{{not (index .Slice 0)}}`, "true"},
+		{`{{not .Zero}}`, "true"},
+		{`{{not (index .Slice 1)}}`, "false"},
+		{`{{not .One}}`, "false"},
+		{`{{eq (index .Slice 0) .Zero}}`, "true"},
+		{`{{eq (index .Slice 1) .One}}`, "true"},
+		{`{{ne (index .Slice 0) .Zero}}`, "false"},
+		{`{{ne (index .Slice 1) .One}}`, "false"},
+		{`{{ge (index .Slice 0) .One}}`, "false"},
+		{`{{ge (index .Slice 1) .Zero}}`, "true"},
+		{`{{gt (index .Slice 0) .One}}`, "false"},
+		{`{{gt (index .Slice 1) .Zero}}`, "true"},
+		{`{{le (index .Slice 0) .One}}`, "true"},
+		{`{{le (index .Slice 1) .Zero}}`, "false"},
+		{`{{lt (index .Slice 0) .One}}`, "true"},
+		{`{{lt (index .Slice 1) .Zero}}`, "false"},
+	}
+
+	for _, tt := range tests {
+		tmpl := Must(New("tmpl").Parse(tt.text))
+		var buf bytes.Buffer
+		err := tmpl.Execute(&buf, map[string]interface{}{
+			"PlusOne": func(n int) int {
+				return n + 1
+			},
+			"Slice": []int{0, 1, 2, 3},
+			"One":   1,
+			"Two":   2,
+			"Nil":   nil,
+			"Zero":  0,
+		})
+		if strings.HasPrefix(tt.out, "ERROR:") {
+			e := strings.TrimSpace(strings.TrimPrefix(tt.out, "ERROR:"))
+			if err == nil || !strings.Contains(err.Error(), e) {
+				t.Errorf("%s: Execute: %v, want error %q", tt.text, err, e)
+			}
+			continue
+		}
+		if err != nil {
+			t.Errorf("%s: Execute: %v", tt.text, err)
+			continue
+		}
+		if buf.String() != tt.out {
+			t.Errorf("%s: template output = %q, want %q", tt.text, &buf, tt.out)
+		}
+	}
+}
diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go
index cd0b82b..3047b27 100644
--- a/src/text/template/funcs.go
+++ b/src/text/template/funcs.go
@@ -21,6 +21,12 @@ import (
 // which the second has type error. In that case, if the second (error)
 // return value evaluates to non-nil during execution, execution terminates and
 // Execute returns that error.
+//
+// When template execution invokes a function with an argument list, that list
+// must be assignable to the function's parameter types. Functions meant to
+// apply to arguments of arbitrary type can use parameters of type interface{} or
+// of type reflect.Value. Similarly, functions meant to return a result of arbitrary
+// type can return interface{} or reflect.Value.
 type FuncMap map[string]interface{}
 
 var builtins = FuncMap{
@@ -144,16 +150,16 @@ func prepareArg(value reflect.Value, argType reflect.Type) (reflect.Value, error
 // index returns the result of indexing its first argument by the following
 // arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
 // indexed item must be a map, slice, or array.
-func index(item interface{}, indices ...interface{}) (interface{}, error) {
-	v := reflect.ValueOf(item)
+func index(item reflect.Value, indices ...reflect.Value) (reflect.Value, error) {
+	v := indirectInterface(item)
 	if !v.IsValid() {
-		return nil, fmt.Errorf("index of untyped nil")
+		return reflect.Value{}, fmt.Errorf("index of untyped nil")
 	}
 	for _, i := range indices {
-		index := reflect.ValueOf(i)
+		index := indirectInterface(i)
 		var isNil bool
 		if v, isNil = indirect(v); isNil {
-			return nil, fmt.Errorf("index of nil pointer")
+			return reflect.Value{}, fmt.Errorf("index of nil pointer")
 		}
 		switch v.Kind() {
 		case reflect.Array, reflect.Slice, reflect.String:
@@ -164,18 +170,18 @@ func index(item interface{}, indices ...interface{}) (interface{}, error) {
 			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 				x = int64(index.Uint())
 			case reflect.Invalid:
-				return nil, fmt.Errorf("cannot index slice/array with nil")
+				return reflect.Value{}, fmt.Errorf("cannot index slice/array with nil")
 			default:
-				return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
+				return reflect.Value{}, fmt.Errorf("cannot index slice/array with type %s", index.Type())
 			}
 			if x < 0 || x >= int64(v.Len()) {
-				return nil, fmt.Errorf("index out of range: %d", x)
+				return reflect.Value{}, fmt.Errorf("index out of range: %d", x)
 			}
 			v = v.Index(int(x))
 		case reflect.Map:
 			index, err := prepareArg(index, v.Type().Key())
 			if err != nil {
-				return nil, err
+				return reflect.Value{}, err
 			}
 			if x := v.MapIndex(index); x.IsValid() {
 				v = x
@@ -186,10 +192,10 @@ func index(item interface{}, indices ...interface{}) (interface{}, error) {
 			// the loop holds invariant: v.IsValid()
 			panic("unreachable")
 		default:
-			return nil, fmt.Errorf("can't index item of type %s", v.Type())
+			return reflect.Value{}, fmt.Errorf("can't index item of type %s", v.Type())
 		}
 	}
-	return v.Interface(), nil
+	return v, nil
 }
 
 // Length
@@ -215,33 +221,33 @@ func length(item interface{}) (int, error) {
 
 // call returns the result of evaluating the first argument as a function.
 // The function must return 1 result, or 2 results, the second of which is an error.
-func call(fn interface{}, args ...interface{}) (interface{}, error) {
-	v := reflect.ValueOf(fn)
+func call(fn reflect.Value, args ...reflect.Value) (reflect.Value, error) {
+	v := indirectInterface(fn)
 	if !v.IsValid() {
-		return nil, fmt.Errorf("call of nil")
+		return reflect.Value{}, fmt.Errorf("call of nil")
 	}
 	typ := v.Type()
 	if typ.Kind() != reflect.Func {
-		return nil, fmt.Errorf("non-function of type %s", typ)
+		return reflect.Value{}, fmt.Errorf("non-function of type %s", typ)
 	}
 	if !goodFunc(typ) {
-		return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
+		return reflect.Value{}, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
 	}
 	numIn := typ.NumIn()
 	var dddType reflect.Type
 	if typ.IsVariadic() {
 		if len(args) < numIn-1 {
-			return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
+			return reflect.Value{}, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
 		}
 		dddType = typ.In(numIn - 1).Elem()
 	} else {
 		if len(args) != numIn {
-			return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
+			return reflect.Value{}, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
 		}
 	}
 	argv := make([]reflect.Value, len(args))
 	for i, arg := range args {
-		value := reflect.ValueOf(arg)
+		value := indirectInterface(arg)
 		// Compute the expected type. Clumsy because of variadics.
 		var argType reflect.Type
 		if !typ.IsVariadic() || i < numIn-1 {
@@ -252,26 +258,26 @@ func call(fn interface{}, args ...interface{}) (interface{}, error) {
 
 		var err error
 		if argv[i], err = prepareArg(value, argType); err != nil {
-			return nil, fmt.Errorf("arg %d: %s", i, err)
+			return reflect.Value{}, fmt.Errorf("arg %d: %s", i, err)
 		}
 	}
 	result := v.Call(argv)
 	if len(result) == 2 && !result[1].IsNil() {
-		return result[0].Interface(), result[1].Interface().(error)
+		return result[0], result[1].Interface().(error)
 	}
-	return result[0].Interface(), nil
+	return result[0], nil
 }
 
 // Boolean logic.
 
-func truth(a interface{}) bool {
-	t, _ := IsTrue(a)
+func truth(arg reflect.Value) bool {
+	t, _ := isTrue(indirectInterface(arg))
 	return t
 }
 
 // and computes the Boolean AND of its arguments, returning
 // the first false argument it encounters, or the last argument.
-func and(arg0 interface{}, args ...interface{}) interface{} {
+func and(arg0 reflect.Value, args ...reflect.Value) reflect.Value {
 	if !truth(arg0) {
 		return arg0
 	}
@@ -286,7 +292,7 @@ func and(arg0 interface{}, args ...interface{}) interface{} {
 
 // or computes the Boolean OR of its arguments, returning
 // the first true argument it encounters, or the last argument.
-func or(arg0 interface{}, args ...interface{}) interface{} {
+func or(arg0 reflect.Value, args ...reflect.Value) reflect.Value {
 	if truth(arg0) {
 		return arg0
 	}
@@ -300,7 +306,7 @@ func or(arg0 interface{}, args ...interface{}) interface{} {
 }
 
 // not returns the Boolean negation of its argument.
-func not(arg interface{}) bool {
+func not(arg reflect.Value) bool {
 	return !truth(arg)
 }
 
@@ -345,8 +351,8 @@ func basicKind(v reflect.Value) (kind, error) {
 }
 
 // eq evaluates the comparison a == b || a == c || ...
-func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
-	v1 := reflect.ValueOf(arg1)
+func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) {
+	v1 := indirectInterface(arg1)
 	k1, err := basicKind(v1)
 	if err != nil {
 		return false, err
@@ -355,7 +361,7 @@ func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
 		return false, errNoComparison
 	}
 	for _, arg := range arg2 {
-		v2 := reflect.ValueOf(arg)
+		v2 := indirectInterface(arg)
 		k2, err := basicKind(v2)
 		if err != nil {
 			return false, err
@@ -397,20 +403,20 @@ func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
 }
 
 // ne evaluates the comparison a != b.
-func ne(arg1, arg2 interface{}) (bool, error) {
+func ne(arg1, arg2 reflect.Value) (bool, error) {
 	// != is the inverse of ==.
 	equal, err := eq(arg1, arg2)
 	return !equal, err
 }
 
 // lt evaluates the comparison a < b.
-func lt(arg1, arg2 interface{}) (bool, error) {
-	v1 := reflect.ValueOf(arg1)
+func lt(arg1, arg2 reflect.Value) (bool, error) {
+	v1 := indirectInterface(arg1)
 	k1, err := basicKind(v1)
 	if err != nil {
 		return false, err
 	}
-	v2 := reflect.ValueOf(arg2)
+	v2 := indirectInterface(arg2)
 	k2, err := basicKind(v2)
 	if err != nil {
 		return false, err
@@ -446,7 +452,7 @@ func lt(arg1, arg2 interface{}) (bool, error) {
 }
 
 // le evaluates the comparison <= b.
-func le(arg1, arg2 interface{}) (bool, error) {
+func le(arg1, arg2 reflect.Value) (bool, error) {
 	// <= is < or ==.
 	lessThan, err := lt(arg1, arg2)
 	if lessThan || err != nil {
@@ -456,7 +462,7 @@ func le(arg1, arg2 interface{}) (bool, error) {
 }
 
 // gt evaluates the comparison a > b.
-func gt(arg1, arg2 interface{}) (bool, error) {
+func gt(arg1, arg2 reflect.Value) (bool, error) {
 	// > is the inverse of <=.
 	lessOrEqual, err := le(arg1, arg2)
 	if err != nil {
@@ -466,7 +472,7 @@ func gt(arg1, arg2 interface{}) (bool, error) {
 }
 
 // ge evaluates the comparison a >= b.
-func ge(arg1, arg2 interface{}) (bool, error) {
+func ge(arg1, arg2 reflect.Value) (bool, error) {
 	// >= is the inverse of <.
 	lessThan, err := lt(arg1, arg2)
 	if err != nil {
diff --git a/src/text/template/multi_test.go b/src/text/template/multi_test.go
index c8723cb..8142f00 100644
--- a/src/text/template/multi_test.go
+++ b/src/text/template/multi_test.go
@@ -349,3 +349,39 @@ func TestParse(t *testing.T) {
 		t.Fatalf("parsing test: %s", err)
 	}
 }
+
+func TestEmptyTemplate(t *testing.T) {
+	cases := []struct {
+		defn []string
+		in   string
+		want string
+	}{
+		{[]string{""}, "once", ""},
+		{[]string{"", ""}, "twice", ""},
+		{[]string{"{{.}}", "{{.}}"}, "twice", "twice"},
+		{[]string{"{{/* a comment */}}", "{{/* a comment */}}"}, "comment", ""},
+		{[]string{"{{.}}", ""}, "twice", ""},
+	}
+
+	for _, c := range cases {
+		root := New("root")
+
+		var (
+			m   *Template
+			err error
+		)
+		for _, d := range c.defn {
+			m, err = root.New(c.in).Parse(d)
+			if err != nil {
+				t.Fatal(err)
+			}
+		}
+		buf := &bytes.Buffer{}
+		if err := m.Execute(buf, c.in); err != nil {
+			t.Fatal(err)
+		}
+		if buf.String() != c.want {
+			t.Errorf("expected string %q: got %q", c.want, buf.String())
+		}
+	}
+}
diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go
index 079c0ea..6fbf36d 100644
--- a/src/text/template/parse/lex.go
+++ b/src/text/template/parse/lex.go
@@ -13,9 +13,10 @@ import (
 
 // item represents a token or text string returned from the scanner.
 type item struct {
-	typ itemType // The type of this item.
-	pos Pos      // The starting position, in bytes, of this item in the input string.
-	val string   // The value of this item.
+	typ  itemType // The type of this item.
+	pos  Pos      // The starting position, in bytes, of this item in the input string.
+	val  string   // The value of this item.
+	line int      // The line number at the start of this item.
 }
 
 func (i item) String() string {
@@ -116,6 +117,7 @@ type lexer struct {
 	lastPos    Pos       // position of most recent item returned by nextItem
 	items      chan item // channel of scanned items
 	parenDepth int       // nesting depth of ( ) exprs
+	line       int       // 1+number of newlines seen
 }
 
 // next returns the next rune in the input.
@@ -127,6 +129,9 @@ func (l *lexer) next() rune {
 	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
 	l.width = Pos(w)
 	l.pos += l.width
+	if r == '\n' {
+		l.line++
+	}
 	return r
 }
 
@@ -140,11 +145,20 @@ func (l *lexer) peek() rune {
 // backup steps back one rune. Can only be called once per call of next.
 func (l *lexer) backup() {
 	l.pos -= l.width
+	// Correct newline count.
+	if l.width == 1 && l.input[l.pos] == '\n' {
+		l.line--
+	}
 }
 
 // emit passes an item back to the client.
 func (l *lexer) emit(t itemType) {
-	l.items <- item{t, l.start, l.input[l.start:l.pos]}
+	l.items <- item{t, l.start, l.input[l.start:l.pos], l.line}
+	// Some items contain text internally. If so, count their newlines.
+	switch t {
+	case itemText, itemRawString, itemLeftDelim, itemRightDelim:
+		l.line += strings.Count(l.input[l.start:l.pos], "\n")
+	}
 	l.start = l.pos
 }
 
@@ -169,17 +183,10 @@ func (l *lexer) acceptRun(valid string) {
 	l.backup()
 }
 
-// lineNumber reports which line we're on, based on the position of
-// the previous item returned by nextItem. Doing it this way
-// means we don't have to worry about peek double counting.
-func (l *lexer) lineNumber() int {
-	return 1 + strings.Count(l.input[:l.lastPos], "\n")
-}
-
 // errorf returns an error token and terminates the scan by passing
 // back a nil pointer that will be the next state, terminating l.nextItem.
 func (l *lexer) errorf(format string, args ...interface{}) stateFn {
-	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
+	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...), l.line}
 	return nil
 }
 
@@ -212,6 +219,7 @@ func lex(name, input, left, right string) *lexer {
 		leftDelim:  left,
 		rightDelim: right,
 		items:      make(chan item),
+		line:       1,
 	}
 	go l.run()
 	return l
@@ -236,24 +244,23 @@ const (
 
 // lexText scans until an opening action delimiter, "{{".
 func lexText(l *lexer) stateFn {
-	for {
-		delim, trimSpace := l.atLeftDelim()
-		if delim {
-			trimLength := Pos(0)
-			if trimSpace {
-				trimLength = rightTrimLength(l.input[l.start:l.pos])
-			}
-			l.pos -= trimLength
-			if l.pos > l.start {
-				l.emit(itemText)
-			}
-			l.pos += trimLength
-			l.ignore()
-			return lexLeftDelim
+	l.width = 0
+	if x := strings.Index(l.input[l.pos:], l.leftDelim); x >= 0 {
+		ldn := Pos(len(l.leftDelim))
+		l.pos += Pos(x)
+		trimLength := Pos(0)
+		if strings.HasPrefix(l.input[l.pos+ldn:], leftTrimMarker) {
+			trimLength = rightTrimLength(l.input[l.start:l.pos])
 		}
-		if l.next() == eof {
-			break
+		l.pos -= trimLength
+		if l.pos > l.start {
+			l.emit(itemText)
 		}
+		l.pos += trimLength
+		l.ignore()
+		return lexLeftDelim
+	} else {
+		l.pos = Pos(len(l.input))
 	}
 	// Correctly reached EOF.
 	if l.pos > l.start {
@@ -263,16 +270,6 @@ func lexText(l *lexer) stateFn {
 	return nil
 }
 
-// atLeftDelim reports whether the lexer is at a left delimiter, possibly followed by a trim marker.
-func (l *lexer) atLeftDelim() (delim, trimSpaces bool) {
-	if !strings.HasPrefix(l.input[l.pos:], l.leftDelim) {
-		return false, false
-	}
-	// The left delim might have the marker afterwards.
-	trimSpaces = strings.HasPrefix(l.input[l.pos+Pos(len(l.leftDelim)):], leftTrimMarker)
-	return true, trimSpaces
-}
-
 // rightTrimLength returns the length of the spaces at the end of the string.
 func rightTrimLength(s string) Pos {
 	return Pos(len(s) - len(strings.TrimRight(s, spaceChars)))
@@ -613,10 +610,14 @@ Loop:
 
 // lexRawQuote scans a raw quoted string.
 func lexRawQuote(l *lexer) stateFn {
+	startLine := l.line
 Loop:
 	for {
 		switch l.next() {
 		case eof:
+			// Restore line number to location of opening quote.
+			// We will error out so it's ok just to overwrite the field.
+			l.line = startLine
 			return l.errorf("unterminated raw quoted string")
 		case '`':
 			break Loop
diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go
index e35ebf1..d655d78 100644
--- a/src/text/template/parse/lex_test.go
+++ b/src/text/template/parse/lex_test.go
@@ -58,39 +58,46 @@ type lexTest struct {
 	items []item
 }
 
+func mkItem(typ itemType, text string) item {
+	return item{
+		typ: typ,
+		val: text,
+	}
+}
+
 var (
-	tDot        = item{itemDot, 0, "."}
-	tBlock      = item{itemBlock, 0, "block"}
-	tEOF        = item{itemEOF, 0, ""}
-	tFor        = item{itemIdentifier, 0, "for"}
-	tLeft       = item{itemLeftDelim, 0, "{{"}
-	tLpar       = item{itemLeftParen, 0, "("}
-	tPipe       = item{itemPipe, 0, "|"}
-	tQuote      = item{itemString, 0, `"abc \n\t\" "`}
-	tRange      = item{itemRange, 0, "range"}
-	tRight      = item{itemRightDelim, 0, "}}"}
-	tRpar       = item{itemRightParen, 0, ")"}
-	tSpace      = item{itemSpace, 0, " "}
+	tDot        = mkItem(itemDot, ".")
+	tBlock      = mkItem(itemBlock, "block")
+	tEOF        = mkItem(itemEOF, "")
+	tFor        = mkItem(itemIdentifier, "for")
+	tLeft       = mkItem(itemLeftDelim, "{{")
+	tLpar       = mkItem(itemLeftParen, "(")
+	tPipe       = mkItem(itemPipe, "|")
+	tQuote      = mkItem(itemString, `"abc \n\t\" "`)
+	tRange      = mkItem(itemRange, "range")
+	tRight      = mkItem(itemRightDelim, "}}")
+	tRpar       = mkItem(itemRightParen, ")")
+	tSpace      = mkItem(itemSpace, " ")
 	raw         = "`" + `abc\n\t\" ` + "`"
 	rawNL       = "`now is{{\n}}the time`" // Contains newline inside raw quote.
-	tRawQuote   = item{itemRawString, 0, raw}
-	tRawQuoteNL = item{itemRawString, 0, rawNL}
+	tRawQuote   = mkItem(itemRawString, raw)
+	tRawQuoteNL = mkItem(itemRawString, rawNL)
 )
 
 var lexTests = []lexTest{
 	{"empty", "", []item{tEOF}},
-	{"spaces", " \t\n", []item{{itemText, 0, " \t\n"}, tEOF}},
-	{"text", `now is the time`, []item{{itemText, 0, "now is the time"}, tEOF}},
+	{"spaces", " \t\n", []item{mkItem(itemText, " \t\n"), tEOF}},
+	{"text", `now is the time`, []item{mkItem(itemText, "now is the time"), tEOF}},
 	{"text with comment", "hello-{{/* this is a comment */}}-world", []item{
-		{itemText, 0, "hello-"},
-		{itemText, 0, "-world"},
+		mkItem(itemText, "hello-"),
+		mkItem(itemText, "-world"),
 		tEOF,
 	}},
 	{"punctuation", "{{,@% }}", []item{
 		tLeft,
-		{itemChar, 0, ","},
-		{itemChar, 0, "@"},
-		{itemChar, 0, "%"},
+		mkItem(itemChar, ","),
+		mkItem(itemChar, "@"),
+		mkItem(itemChar, "%"),
 		tSpace,
 		tRight,
 		tEOF,
@@ -99,7 +106,7 @@ var lexTests = []lexTest{
 		tLeft,
 		tLpar,
 		tLpar,
-		{itemNumber, 0, "3"},
+		mkItem(itemNumber, "3"),
 		tRpar,
 		tRpar,
 		tRight,
@@ -108,54 +115,54 @@ var lexTests = []lexTest{
 	{"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
 	{"for", `{{for}}`, []item{tLeft, tFor, tRight, tEOF}},
 	{"block", `{{block "foo" .}}`, []item{
-		tLeft, tBlock, tSpace, {itemString, 0, `"foo"`}, tSpace, tDot, tRight, tEOF,
+		tLeft, tBlock, tSpace, mkItem(itemString, `"foo"`), tSpace, tDot, tRight, tEOF,
 	}},
 	{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
 	{"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}},
 	{"raw quote with newline", "{{" + rawNL + "}}", []item{tLeft, tRawQuoteNL, tRight, tEOF}},
 	{"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
 		tLeft,
-		{itemNumber, 0, "1"},
+		mkItem(itemNumber, "1"),
 		tSpace,
-		{itemNumber, 0, "02"},
+		mkItem(itemNumber, "02"),
 		tSpace,
-		{itemNumber, 0, "0x14"},
+		mkItem(itemNumber, "0x14"),
 		tSpace,
-		{itemNumber, 0, "-7.2i"},
+		mkItem(itemNumber, "-7.2i"),
 		tSpace,
-		{itemNumber, 0, "1e3"},
+		mkItem(itemNumber, "1e3"),
 		tSpace,
-		{itemNumber, 0, "+1.2e-4"},
+		mkItem(itemNumber, "+1.2e-4"),
 		tSpace,
-		{itemNumber, 0, "4.2i"},
+		mkItem(itemNumber, "4.2i"),
 		tSpace,
-		{itemComplex, 0, "1+2i"},
+		mkItem(itemComplex, "1+2i"),
 		tRight,
 		tEOF,
 	}},
 	{"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{
 		tLeft,
-		{itemCharConstant, 0, `'a'`},
+		mkItem(itemCharConstant, `'a'`),
 		tSpace,
-		{itemCharConstant, 0, `'\n'`},
+		mkItem(itemCharConstant, `'\n'`),
 		tSpace,
-		{itemCharConstant, 0, `'\''`},
+		mkItem(itemCharConstant, `'\''`),
 		tSpace,
-		{itemCharConstant, 0, `'\\'`},
+		mkItem(itemCharConstant, `'\\'`),
 		tSpace,
-		{itemCharConstant, 0, `'\u00FF'`},
+		mkItem(itemCharConstant, `'\u00FF'`),
 		tSpace,
-		{itemCharConstant, 0, `'\xFF'`},
+		mkItem(itemCharConstant, `'\xFF'`),
 		tSpace,
-		{itemCharConstant, 0, `'本'`},
+		mkItem(itemCharConstant, `'本'`),
 		tRight,
 		tEOF,
 	}},
 	{"bools", "{{true false}}", []item{
 		tLeft,
-		{itemBool, 0, "true"},
+		mkItem(itemBool, "true"),
 		tSpace,
-		{itemBool, 0, "false"},
+		mkItem(itemBool, "false"),
 		tRight,
 		tEOF,
 	}},
@@ -167,178 +174,178 @@ var lexTests = []lexTest{
 	}},
 	{"nil", "{{nil}}", []item{
 		tLeft,
-		{itemNil, 0, "nil"},
+		mkItem(itemNil, "nil"),
 		tRight,
 		tEOF,
 	}},
 	{"dots", "{{.x . .2 .x.y.z}}", []item{
 		tLeft,
-		{itemField, 0, ".x"},
+		mkItem(itemField, ".x"),
 		tSpace,
 		tDot,
 		tSpace,
-		{itemNumber, 0, ".2"},
+		mkItem(itemNumber, ".2"),
 		tSpace,
-		{itemField, 0, ".x"},
-		{itemField, 0, ".y"},
-		{itemField, 0, ".z"},
+		mkItem(itemField, ".x"),
+		mkItem(itemField, ".y"),
+		mkItem(itemField, ".z"),
 		tRight,
 		tEOF,
 	}},
 	{"keywords", "{{range if else end with}}", []item{
 		tLeft,
-		{itemRange, 0, "range"},
+		mkItem(itemRange, "range"),
 		tSpace,
-		{itemIf, 0, "if"},
+		mkItem(itemIf, "if"),
 		tSpace,
-		{itemElse, 0, "else"},
+		mkItem(itemElse, "else"),
 		tSpace,
-		{itemEnd, 0, "end"},
+		mkItem(itemEnd, "end"),
 		tSpace,
-		{itemWith, 0, "with"},
+		mkItem(itemWith, "with"),
 		tRight,
 		tEOF,
 	}},
 	{"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{
 		tLeft,
-		{itemVariable, 0, "$c"},
+		mkItem(itemVariable, "$c"),
 		tSpace,
-		{itemColonEquals, 0, ":="},
+		mkItem(itemColonEquals, ":="),
 		tSpace,
-		{itemIdentifier, 0, "printf"},
+		mkItem(itemIdentifier, "printf"),
 		tSpace,
-		{itemVariable, 0, "$"},
+		mkItem(itemVariable, "$"),
 		tSpace,
-		{itemVariable, 0, "$hello"},
+		mkItem(itemVariable, "$hello"),
 		tSpace,
-		{itemVariable, 0, "$23"},
+		mkItem(itemVariable, "$23"),
 		tSpace,
-		{itemVariable, 0, "$"},
+		mkItem(itemVariable, "$"),
 		tSpace,
-		{itemVariable, 0, "$var"},
-		{itemField, 0, ".Field"},
+		mkItem(itemVariable, "$var"),
+		mkItem(itemField, ".Field"),
 		tSpace,
-		{itemField, 0, ".Method"},
+		mkItem(itemField, ".Method"),
 		tRight,
 		tEOF,
 	}},
 	{"variable invocation", "{{$x 23}}", []item{
 		tLeft,
-		{itemVariable, 0, "$x"},
+		mkItem(itemVariable, "$x"),
 		tSpace,
-		{itemNumber, 0, "23"},
+		mkItem(itemNumber, "23"),
 		tRight,
 		tEOF,
 	}},
 	{"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{
-		{itemText, 0, "intro "},
+		mkItem(itemText, "intro "),
 		tLeft,
-		{itemIdentifier, 0, "echo"},
+		mkItem(itemIdentifier, "echo"),
 		tSpace,
-		{itemIdentifier, 0, "hi"},
+		mkItem(itemIdentifier, "hi"),
 		tSpace,
-		{itemNumber, 0, "1.2"},
+		mkItem(itemNumber, "1.2"),
 		tSpace,
 		tPipe,
-		{itemIdentifier, 0, "noargs"},
+		mkItem(itemIdentifier, "noargs"),
 		tPipe,
-		{itemIdentifier, 0, "args"},
+		mkItem(itemIdentifier, "args"),
 		tSpace,
-		{itemNumber, 0, "1"},
+		mkItem(itemNumber, "1"),
 		tSpace,
-		{itemString, 0, `"hi"`},
+		mkItem(itemString, `"hi"`),
 		tRight,
-		{itemText, 0, " outro"},
+		mkItem(itemText, " outro"),
 		tEOF,
 	}},
 	{"declaration", "{{$v := 3}}", []item{
 		tLeft,
-		{itemVariable, 0, "$v"},
+		mkItem(itemVariable, "$v"),
 		tSpace,
-		{itemColonEquals, 0, ":="},
+		mkItem(itemColonEquals, ":="),
 		tSpace,
-		{itemNumber, 0, "3"},
+		mkItem(itemNumber, "3"),
 		tRight,
 		tEOF,
 	}},
 	{"2 declarations", "{{$v , $w := 3}}", []item{
 		tLeft,
-		{itemVariable, 0, "$v"},
+		mkItem(itemVariable, "$v"),
 		tSpace,
-		{itemChar, 0, ","},
+		mkItem(itemChar, ","),
 		tSpace,
-		{itemVariable, 0, "$w"},
+		mkItem(itemVariable, "$w"),
 		tSpace,
-		{itemColonEquals, 0, ":="},
+		mkItem(itemColonEquals, ":="),
 		tSpace,
-		{itemNumber, 0, "3"},
+		mkItem(itemNumber, "3"),
 		tRight,
 		tEOF,
 	}},
 	{"field of parenthesized expression", "{{(.X).Y}}", []item{
 		tLeft,
 		tLpar,
-		{itemField, 0, ".X"},
+		mkItem(itemField, ".X"),
 		tRpar,
-		{itemField, 0, ".Y"},
+		mkItem(itemField, ".Y"),
 		tRight,
 		tEOF,
 	}},
 	{"trimming spaces before and after", "hello- {{- 3 -}} -world", []item{
-		{itemText, 0, "hello-"},
+		mkItem(itemText, "hello-"),
 		tLeft,
-		{itemNumber, 0, "3"},
+		mkItem(itemNumber, "3"),
 		tRight,
-		{itemText, 0, "-world"},
+		mkItem(itemText, "-world"),
 		tEOF,
 	}},
 	{"trimming spaces before and after comment", "hello- {{- /* hello */ -}} -world", []item{
-		{itemText, 0, "hello-"},
-		{itemText, 0, "-world"},
+		mkItem(itemText, "hello-"),
+		mkItem(itemText, "-world"),
 		tEOF,
 	}},
 	// errors
 	{"badchar", "#{{\x01}}", []item{
-		{itemText, 0, "#"},
+		mkItem(itemText, "#"),
 		tLeft,
-		{itemError, 0, "unrecognized character in action: U+0001"},
+		mkItem(itemError, "unrecognized character in action: U+0001"),
 	}},
 	{"unclosed action", "{{\n}}", []item{
 		tLeft,
-		{itemError, 0, "unclosed action"},
+		mkItem(itemError, "unclosed action"),
 	}},
 	{"EOF in action", "{{range", []item{
 		tLeft,
 		tRange,
-		{itemError, 0, "unclosed action"},
+		mkItem(itemError, "unclosed action"),
 	}},
 	{"unclosed quote", "{{\"\n\"}}", []item{
 		tLeft,
-		{itemError, 0, "unterminated quoted string"},
+		mkItem(itemError, "unterminated quoted string"),
 	}},
 	{"unclosed raw quote", "{{`xx}}", []item{
 		tLeft,
-		{itemError, 0, "unterminated raw quoted string"},
+		mkItem(itemError, "unterminated raw quoted string"),
 	}},
 	{"unclosed char constant", "{{'\n}}", []item{
 		tLeft,
-		{itemError, 0, "unterminated character constant"},
+		mkItem(itemError, "unterminated character constant"),
 	}},
 	{"bad number", "{{3k}}", []item{
 		tLeft,
-		{itemError, 0, `bad number syntax: "3k"`},
+		mkItem(itemError, `bad number syntax: "3k"`),
 	}},
 	{"unclosed paren", "{{(3}}", []item{
 		tLeft,
 		tLpar,
-		{itemNumber, 0, "3"},
-		{itemError, 0, `unclosed left paren`},
+		mkItem(itemNumber, "3"),
+		mkItem(itemError, `unclosed left paren`),
 	}},
 	{"extra right paren", "{{3)}}", []item{
 		tLeft,
-		{itemNumber, 0, "3"},
+		mkItem(itemNumber, "3"),
 		tRpar,
-		{itemError, 0, `unexpected right paren U+0029 ')'`},
+		mkItem(itemError, `unexpected right paren U+0029 ')'`),
 	}},
 
 	// Fixed bugs
@@ -355,17 +362,17 @@ var lexTests = []lexTest{
 		tEOF,
 	}},
 	{"text with bad comment", "hello-{{/*/}}-world", []item{
-		{itemText, 0, "hello-"},
-		{itemError, 0, `unclosed comment`},
+		mkItem(itemText, "hello-"),
+		mkItem(itemError, `unclosed comment`),
 	}},
 	{"text with comment close separated from delim", "hello-{{/* */ }}-world", []item{
-		{itemText, 0, "hello-"},
-		{itemError, 0, `comment ends before closing delimiter`},
+		mkItem(itemText, "hello-"),
+		mkItem(itemError, `comment ends before closing delimiter`),
 	}},
 	// This one is an error that we can't catch because it breaks templates with
 	// minimized JavaScript. Should have fixed it before Go 1.1.
 	{"unmatched right delimiter", "hello-{.}}-world", []item{
-		{itemText, 0, "hello-{.}}-world"},
+		mkItem(itemText, "hello-{.}}-world"),
 		tEOF,
 	}},
 }
@@ -414,13 +421,13 @@ func TestLex(t *testing.T) {
 var lexDelimTests = []lexTest{
 	{"punctuation", "$$,@%{{}}@@", []item{
 		tLeftDelim,
-		{itemChar, 0, ","},
-		{itemChar, 0, "@"},
-		{itemChar, 0, "%"},
-		{itemChar, 0, "{"},
-		{itemChar, 0, "{"},
-		{itemChar, 0, "}"},
-		{itemChar, 0, "}"},
+		mkItem(itemChar, ","),
+		mkItem(itemChar, "@"),
+		mkItem(itemChar, "%"),
+		mkItem(itemChar, "{"),
+		mkItem(itemChar, "{"),
+		mkItem(itemChar, "}"),
+		mkItem(itemChar, "}"),
 		tRightDelim,
 		tEOF,
 	}},
@@ -431,8 +438,8 @@ var lexDelimTests = []lexTest{
 }
 
 var (
-	tLeftDelim  = item{itemLeftDelim, 0, "$$"}
-	tRightDelim = item{itemRightDelim, 0, "@@"}
+	tLeftDelim  = mkItem(itemLeftDelim, "$$")
+	tRightDelim = mkItem(itemRightDelim, "@@")
 )
 
 func TestDelims(t *testing.T) {
@@ -447,21 +454,21 @@ func TestDelims(t *testing.T) {
 var lexPosTests = []lexTest{
 	{"empty", "", []item{tEOF}},
 	{"punctuation", "{{,@%#}}", []item{
-		{itemLeftDelim, 0, "{{"},
-		{itemChar, 2, ","},
-		{itemChar, 3, "@"},
-		{itemChar, 4, "%"},
-		{itemChar, 5, "#"},
-		{itemRightDelim, 6, "}}"},
-		{itemEOF, 8, ""},
+		{itemLeftDelim, 0, "{{", 1},
+		{itemChar, 2, ",", 1},
+		{itemChar, 3, "@", 1},
+		{itemChar, 4, "%", 1},
+		{itemChar, 5, "#", 1},
+		{itemRightDelim, 6, "}}", 1},
+		{itemEOF, 8, "", 1},
 	}},
 	{"sample", "0123{{hello}}xyz", []item{
-		{itemText, 0, "0123"},
-		{itemLeftDelim, 4, "{{"},
-		{itemIdentifier, 6, "hello"},
-		{itemRightDelim, 11, "}}"},
-		{itemText, 13, "xyz"},
-		{itemEOF, 16, ""},
+		{itemText, 0, "0123", 1},
+		{itemLeftDelim, 4, "{{", 1},
+		{itemIdentifier, 6, "hello", 1},
+		{itemRightDelim, 11, "}}", 1},
+		{itemText, 13, "xyz", 1},
+		{itemEOF, 16, "", 1},
 	}},
 }
 
diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go
index 86705e5..6060c6d 100644
--- a/src/text/template/parse/parse.go
+++ b/src/text/template/parse/parse.go
@@ -157,7 +157,7 @@ func (t *Tree) ErrorContext(n Node) (location, context string) {
 // errorf formats the error and terminates processing.
 func (t *Tree) errorf(format string, args ...interface{}) {
 	t.Root = nil
-	format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
+	format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.token[0].line, format)
 	panic(fmt.Errorf(format, args...))
 }
 
@@ -277,7 +277,7 @@ func IsEmptyTree(n Node) bool {
 // parse is the top-level parser for a template, essentially the same
 // as itemList except it also parses {{define}} actions.
 // It runs to EOF.
-func (t *Tree) parse() (next Node) {
+func (t *Tree) parse() {
 	t.Root = t.newList(t.peek().pos)
 	for t.peek().typ != itemEOF {
 		if t.peek().typ == itemLeftDelim {
@@ -299,7 +299,6 @@ func (t *Tree) parse() (next Node) {
 			t.Root.append(n)
 		}
 	}
-	return nil
 }
 
 // parseDefinition parses a {{define}} ...  {{end}} template definition and
@@ -377,15 +376,17 @@ func (t *Tree) action() (n Node) {
 		return t.withControl()
 	}
 	t.backup()
+	token := t.peek()
 	// Do not pop variables; they persist until "end".
-	return t.newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))
+	return t.newAction(token.pos, token.line, t.pipeline("command"))
 }
 
 // Pipeline:
 //	declarations? command ('|' command)*
 func (t *Tree) pipeline(context string) (pipe *PipeNode) {
 	var decl []*VariableNode
-	pos := t.peekNonSpace().pos
+	token := t.peekNonSpace()
+	pos := token.pos
 	// Are there declarations?
 	for {
 		if v := t.peekNonSpace(); v.typ == itemVariable {
@@ -414,7 +415,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
 		}
 		break
 	}
-	pipe = t.newPipeline(pos, t.lex.lineNumber(), decl)
+	pipe = t.newPipeline(pos, token.line, decl)
 	for {
 		switch token := t.nextNonSpace(); token.typ {
 		case itemRightDelim, itemRightParen:
@@ -451,7 +452,6 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
 
 func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
 	defer t.popVars(len(t.vars))
-	line = t.lex.lineNumber()
 	pipe = t.pipeline(context)
 	var next Node
 	list, next = t.itemList()
@@ -480,7 +480,7 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int
 			t.errorf("expected end; found %s", next)
 		}
 	}
-	return pipe.Position(), line, pipe, list, elseList
+	return pipe.Position(), pipe.Line, pipe, list, elseList
 }
 
 // If:
@@ -522,9 +522,10 @@ func (t *Tree) elseControl() Node {
 	peek := t.peekNonSpace()
 	if peek.typ == itemIf {
 		// We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ".
-		return t.newElse(peek.pos, t.lex.lineNumber())
+		return t.newElse(peek.pos, peek.line)
 	}
-	return t.newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
+	token := t.expect(itemRightDelim, "else")
+	return t.newElse(token.pos, token.line)
 }
 
 // Block:
@@ -551,7 +552,7 @@ func (t *Tree) blockControl() Node {
 	block.add()
 	block.stopParse()
 
-	return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
+	return t.newTemplate(token.pos, token.line, name, pipe)
 }
 
 // Template:
@@ -568,7 +569,7 @@ func (t *Tree) templateControl() Node {
 		// Do not pop variables; they persist until "end".
 		pipe = t.pipeline(context)
 	}
-	return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
+	return t.newTemplate(token.pos, token.line, name, pipe)
 }
 
 func (t *Tree) parseTemplateName(token item, context string) (name string) {
diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go
index 9d856bc..81f14ac 100644
--- a/src/text/template/parse/parse_test.go
+++ b/src/text/template/parse/parse_test.go
@@ -484,3 +484,37 @@ func TestBlock(t *testing.T) {
 		t.Errorf("inner template = %q, want %q", g, w)
 	}
 }
+
+func TestLineNum(t *testing.T) {
+	const count = 100
+	text := strings.Repeat("{{printf 1234}}\n", count)
+	tree, err := New("bench").Parse(text, "", "", make(map[string]*Tree), builtins)
+	if err != nil {
+		t.Fatal(err)
+	}
+	// Check the line numbers. Each line is an action containing a template, followed by text.
+	// That's two nodes per line.
+	nodes := tree.Root.Nodes
+	for i := 0; i < len(nodes); i += 2 {
+		line := 1 + i/2
+		// Action first.
+		action := nodes[i].(*ActionNode)
+		if action.Line != line {
+			t.Fatalf("line %d: action is line %d", line, action.Line)
+		}
+		pipe := action.Pipe
+		if pipe.Line != line {
+			t.Fatalf("line %d: pipe is line %d", line, pipe.Line)
+		}
+	}
+}
+
+func BenchmarkParseLarge(b *testing.B) {
+	text := strings.Repeat("{{1234}}\n", 10000)
+	for i := 0; i < b.N; i++ {
+		_, err := New("bench").Parse(text, "", "", make(map[string]*Tree), builtins)
+		if err != nil {
+			b.Fatal(err)
+		}
+	}
+}
diff --git a/src/text/template/template.go b/src/text/template/template.go
index 7a7f42a..b6fceb1 100644
--- a/src/text/template/template.go
+++ b/src/text/template/template.go
@@ -181,9 +181,16 @@ func (t *Template) Lookup(name string) *Template {
 	return t.tmpl[name]
 }
 
-// Parse defines the template by parsing the text. Nested template definitions will be
-// associated with the top-level template t. Parse may be called multiple times
-// to parse definitions of templates to associate with t.
+// Parse parses text as a template body for t.
+// Named template definitions ({{define ...}} or {{block ...}} statements) in text
+// define additional templates associated with t and are removed from the
+// definition of t itself.
+//
+// Templates can be redefined in successive calls to Parse.
+// A template definition with a body containing only white space and comments
+// is considered empty and will not replace an existing template's body.
+// This allows using Parse to add new named template definitions without
+// overwriting the main template body.
 func (t *Template) Parse(text string) (*Template, error) {
 	t.init()
 	t.muFuncs.RLock()
@@ -208,7 +215,7 @@ func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) {
 	if new.common != t.common {
 		panic("internal error: associate not common")
 	}
-	if t.tmpl[new.name] != nil && parse.IsEmptyTree(tree.Root) {
+	if t.tmpl[new.name] != nil && parse.IsEmptyTree(tree.Root) && t.Tree != nil {
 		// If a template by that name exists,
 		// don't replace it with an empty template.
 		return false, nil
diff --git a/src/time/example_test.go b/src/time/example_test.go
index 4170d51..7dc2bb5 100644
--- a/src/time/example_test.go
+++ b/src/time/example_test.go
@@ -251,20 +251,18 @@ func ExampleTime_Truncate() {
 		2 * time.Second,
 		time.Minute,
 		10 * time.Minute,
-		time.Hour,
 	}
 
 	for _, d := range trunc {
-		fmt.Printf("t.Truncate(%6s) = %s\n", d, t.Truncate(d).Format("15:04:05.999999999"))
+		fmt.Printf("t.Truncate(%5s) = %s\n", d, t.Truncate(d).Format("15:04:05.999999999"))
 	}
 
 	// Output:
-	// t.Truncate(   1ns) = 12:15:30.918273645
-	// t.Truncate(   1µs) = 12:15:30.918273
-	// t.Truncate(   1ms) = 12:15:30.918
-	// t.Truncate(    1s) = 12:15:30
-	// t.Truncate(    2s) = 12:15:30
-	// t.Truncate(  1m0s) = 12:15:00
-	// t.Truncate( 10m0s) = 12:10:00
-	// t.Truncate(1h0m0s) = 12:00:00
+	// t.Truncate(  1ns) = 12:15:30.918273645
+	// t.Truncate(  1µs) = 12:15:30.918273
+	// t.Truncate(  1ms) = 12:15:30.918
+	// t.Truncate(   1s) = 12:15:30
+	// t.Truncate(   2s) = 12:15:30
+	// t.Truncate( 1m0s) = 12:15:00
+	// t.Truncate(10m0s) = 12:10:00
 }
diff --git a/src/time/export_android_test.go b/src/time/export_android_test.go
new file mode 100644
index 0000000..fa6a058
--- /dev/null
+++ b/src/time/export_android_test.go
@@ -0,0 +1,12 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package time
+
+func ForceAndroidTzdataForTest(tzdata bool) {
+	tzdataPaths = origTzdataPaths
+	if tzdata {
+		tzdataPaths = tzdataPaths[:1]
+	}
+}
diff --git a/src/time/format.go b/src/time/format.go
index c2ae793..3fbfa73 100644
--- a/src/time/format.go
+++ b/src/time/format.go
@@ -42,6 +42,13 @@ import "errors"
 //	Z07:00 Z or ±hh:mm
 //	Z07    Z or ±hh
 //
+// The recognized day of week formats are "Mon" and "Monday".
+// The recognized month formats are "Jan" and "January".
+//
+// Text in the format string that is not recognized as part of the reference
+// time is echoed verbatim during Format and expected to appear verbatim
+// in the input to Parse.
+//
 // The executable example for time.Format demonstrates the working
 // of the layout string in detail and is a good reference.
 //
@@ -844,6 +851,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
 			sec, value, err = getnum(value, std == stdZeroSecond)
 			if sec < 0 || 60 <= sec {
 				rangeErrString = "second"
+				break
 			}
 			// Special case: do we have a fractional second but no
 			// fractional second in the format?
@@ -1004,7 +1012,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
 	}
 
 	// Validate the day of the month.
-	if day > daysIn(Month(month), year) {
+	if day < 1 || day > daysIn(Month(month), year) {
 		return Time{}, &ParseError{alayout, avalue, "", value, ": day out of range"}
 	}
 
@@ -1020,12 +1028,12 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
 		// If that zone was in effect at the given time, use it.
 		name, offset, _, _, _ := local.lookup(t.sec + internalToUnix)
 		if offset == zoneOffset && (zoneName == "" || name == zoneName) {
-			t.loc = local
+			t.setLoc(local)
 			return t, nil
 		}
 
 		// Otherwise create fake zone to record offset.
-		t.loc = FixedZone(zoneName, zoneOffset)
+		t.setLoc(FixedZone(zoneName, zoneOffset))
 		return t, nil
 	}
 
@@ -1036,7 +1044,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
 		offset, _, ok := local.lookupName(zoneName, t.sec+internalToUnix)
 		if ok {
 			t.sec -= int64(offset)
-			t.loc = local
+			t.setLoc(local)
 			return t, nil
 		}
 
@@ -1045,7 +1053,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
 			offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT.
 			offset *= 3600
 		}
-		t.loc = FixedZone(zoneName, offset)
+		t.setLoc(FixedZone(zoneName, offset))
 		return t, nil
 	}
 
@@ -1173,6 +1181,37 @@ func leadingInt(s string) (x int64, rem string, err error) {
 	return x, s[i:], nil
 }
 
+// leadingFraction consumes the leading [0-9]* from s.
+// It is used only for fractions, so does not return an error on overflow,
+// it just stops accumulating precision.
+func leadingFraction(s string) (x int64, scale float64, rem string) {
+	i := 0
+	scale = 1
+	overflow := false
+	for ; i < len(s); i++ {
+		c := s[i]
+		if c < '0' || c > '9' {
+			break
+		}
+		if overflow {
+			continue
+		}
+		if x > (1<<63-1)/10 {
+			// It's possible for overflow to give a positive number, so take care.
+			overflow = true
+			continue
+		}
+		y := x*10 + int64(c) - '0'
+		if y < 0 {
+			overflow = true
+			continue
+		}
+		x = y
+		scale *= 10
+	}
+	return x, scale, s[i:]
+}
+
 var unitMap = map[string]int64{
 	"ns": int64(Nanosecond),
 	"us": int64(Microsecond),
@@ -1235,13 +1274,7 @@ func ParseDuration(s string) (Duration, error) {
 		if s != "" && s[0] == '.' {
 			s = s[1:]
 			pl := len(s)
-			f, s, err = leadingInt(s)
-			if err != nil {
-				return 0, errors.New("time: invalid duration " + orig)
-			}
-			for n := pl - len(s); n > 0; n-- {
-				scale *= 10
-			}
+			f, scale, s = leadingFraction(s)
 			post = pl != len(s)
 		}
 		if !pre && !post {
diff --git a/src/time/format_test.go b/src/time/format_test.go
index 8c47dbc..aa4434a 100644
--- a/src/time/format_test.go
+++ b/src/time/format_test.go
@@ -224,6 +224,7 @@ var dayOutOfRangeTests = []struct {
 	{"Thu Nov 31 21:00:57 2010", false},
 	{"Thu Dec 31 21:00:57 2010", true},
 	{"Thu Dec 32 21:00:57 2010", false},
+	{"Thu Dec 00 21:00:57 2010", false},
 }
 
 func TestParseDayOutOfRange(t *testing.T) {
@@ -440,6 +441,8 @@ var parseErrorTests = []ParseErrorTest{
 	{RFC3339, "2006-01-02T15:04_abc", `parsing time "2006-01-02T15:04_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as ":"`},
 	{RFC3339, "2006-01-02T15:04:05_abc", `parsing time "2006-01-02T15:04:05_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as "Z07:00"`},
 	{RFC3339, "2006-01-02T15:04:05Z_abc", `parsing time "2006-01-02T15:04:05Z_abc": extra text: _abc`},
+	// invalid second followed by optional fractional seconds
+	{RFC3339, "2010-02-04T21:00:67.012345678-08:00", "second out of range"},
 }
 
 func TestParseErrors(t *testing.T) {
diff --git a/src/time/sleep.go b/src/time/sleep.go
index 73114f5..4b01404 100644
--- a/src/time/sleep.go
+++ b/src/time/sleep.go
@@ -12,7 +12,7 @@ func Sleep(d Duration)
 func runtimeNano() int64
 
 // Interface to timers implemented in package runtime.
-// Must be in sync with ../runtime/runtime.h:/^struct.Timer$
+// Must be in sync with ../runtime/time.go:/^type timer
 type runtimeTimer struct {
 	i      int
 	when   int64
@@ -55,13 +55,22 @@ type Timer struct {
 // Stop does not close the channel, to prevent a read from the channel succeeding
 // incorrectly.
 //
-// To prevent the timer firing after a call to Stop,
-// check the return value and drain the channel. For example:
+// To prevent a timer created with NewTimer from firing after a call to Stop,
+// check the return value and drain the channel.
+// For example, assuming the program has not received from t.C already:
+//
 // 	if !t.Stop() {
 // 		<-t.C
 // 	}
+//
 // This cannot be done concurrent to other receives from the Timer's
 // channel.
+//
+// For a timer created with AfterFunc(d, f), if t.Stop returns false, then the timer
+// has already expired and the function f has been started in its own goroutine;
+// Stop does not wait for f to complete before returning.
+// If the caller needs to know whether f is completed, it must coordinate
+// with f explicitly.
 func (t *Timer) Stop() bool {
 	if t.r.f == nil {
 		panic("time: Stop called on uninitialized Timer")
@@ -89,18 +98,25 @@ func NewTimer(d Duration) *Timer {
 // It returns true if the timer had been active, false if the timer had
 // expired or been stopped.
 //
-// To reuse an active timer, always call its Stop method first and—if it had
-// expired—drain the value from its channel. For example:
+// Resetting a timer must take care not to race with the send into t.C
+// that happens when the current timer expires.
+// If a program has already received a value from t.C, the timer is known
+// to have expired, and t.Reset can be used directly.
+// If a program has not yet received a value from t.C, however,
+// the timer must be stopped and—if Stop reports that the timer expired
+// before being stopped—the channel explicitly drained:
+//
 // 	if !t.Stop() {
 // 		<-t.C
 // 	}
 // 	t.Reset(d)
+//
 // This should not be done concurrent to other receives from the Timer's
 // channel.
 //
 // Note that it is not possible to use Reset's return value correctly, as there
 // is a race condition between draining the channel and the new timer expiring.
-// Reset should always be used in concert with Stop, as described above.
+// Reset should always be invoked on stopped or expired channels, as described above.
 // The return value exists to preserve compatibility with existing programs.
 func (t *Timer) Reset(d Duration) bool {
 	if t.r.f == nil {
diff --git a/src/time/time.go b/src/time/time.go
index c31de35..10b3246 100644
--- a/src/time/time.go
+++ b/src/time/time.go
@@ -4,7 +4,8 @@
 
 // Package time provides functionality for measuring and displaying time.
 //
-// The calendrical calculations always assume a Gregorian calendar.
+// The calendrical calculations always assume a Gregorian calendar, with
+// no leap seconds.
 package time
 
 import "errors"
@@ -49,11 +50,18 @@ type Time struct {
 	// loc specifies the Location that should be used to
 	// determine the minute, hour, month, day, and year
 	// that correspond to this Time.
-	// Only the zero Time has a nil Location.
-	// In that case it is interpreted to mean UTC.
+	// The nil location means UTC.
+	// All UTC times are represented with loc==nil, never loc==&utcLoc.
 	loc *Location
 }
 
+func (t *Time) setLoc(loc *Location) {
+	if loc == &utcLoc {
+		loc = nil
+	}
+	t.loc = loc
+}
+
 // After reports whether the time instant t is after u.
 func (t Time) After(u Time) bool {
 	return t.sec > u.sec || t.sec == u.sec && t.nsec > u.nsec
@@ -67,8 +75,7 @@ func (t Time) Before(u Time) bool {
 // Equal reports whether t and u represent the same time instant.
 // Two times can be equal even if they are in different locations.
 // For example, 6:00 +0200 CEST and 4:00 UTC are Equal.
-// This comparison is different from using t == u, which also compares
-// the locations.
+// Do not use == with Time values.
 func (t Time) Equal(u Time) bool {
 	return t.sec == u.sec && t.nsec == u.nsec
 }
@@ -107,7 +114,14 @@ var months = [...]string{
 }
 
 // String returns the English name of the month ("January", "February", ...).
-func (m Month) String() string { return months[m-1] }
+func (m Month) String() string {
+	if January <= m && m <= December {
+		return months[m-1]
+	}
+	buf := make([]byte, 20)
+	n := fmtInt(buf, uint64(m))
+	return "%!Month(" + string(buf[n:]) + ")"
+}
 
 // A Weekday specifies a day of the week (Sunday = 0, ...).
 type Weekday int
@@ -585,21 +599,21 @@ func (d Duration) Nanoseconds() int64 { return int64(d) }
 func (d Duration) Seconds() float64 {
 	sec := d / Second
 	nsec := d % Second
-	return float64(sec) + float64(nsec)*1e-9
+	return float64(sec) + float64(nsec)/1e9
 }
 
 // Minutes returns the duration as a floating point number of minutes.
 func (d Duration) Minutes() float64 {
 	min := d / Minute
 	nsec := d % Minute
-	return float64(min) + float64(nsec)*(1e-9/60)
+	return float64(min) + float64(nsec)/(60*1e9)
 }
 
 // Hours returns the duration as a floating point number of hours.
 func (d Duration) Hours() float64 {
 	hour := d / Hour
 	nsec := d % Hour
-	return float64(hour) + float64(nsec)*(1e-9/60/60)
+	return float64(hour) + float64(nsec)/(60*60*1e9)
 }
 
 // Add returns the time t+d.
@@ -640,6 +654,12 @@ func Since(t Time) Duration {
 	return Now().Sub(t)
 }
 
+// Until returns the duration until t.
+// It is shorthand for t.Sub(time.Now()).
+func Until(t Time) Duration {
+	return t.Sub(Now())
+}
+
 // AddDate returns the time corresponding to adding the
 // given number of years, months, and days to t.
 // For example, AddDate(-1, 2, 3) applied to January 1, 2011
@@ -651,7 +671,7 @@ func Since(t Time) Duration {
 func (t Time) AddDate(years int, months int, days int) Time {
 	year, month, day := t.Date()
 	hour, min, sec := t.Clock()
-	return Date(year+years, month+Month(months), day+days, hour, min, sec, int(t.nsec), t.loc)
+	return Date(year+years, month+Month(months), day+days, hour, min, sec, int(t.nsec), t.Location())
 }
 
 const (
@@ -781,13 +801,13 @@ func Now() Time {
 
 // UTC returns t with the location set to UTC.
 func (t Time) UTC() Time {
-	t.loc = UTC
+	t.setLoc(&utcLoc)
 	return t
 }
 
 // Local returns t with the location set to local time.
 func (t Time) Local() Time {
-	t.loc = Local
+	t.setLoc(Local)
 	return t
 }
 
@@ -798,7 +818,7 @@ func (t Time) In(loc *Location) Time {
 	if loc == nil {
 		panic("time: missing Location in call to Time.In")
 	}
-	t.loc = loc
+	t.setLoc(loc)
 	return t
 }
 
@@ -826,8 +846,9 @@ func (t Time) Unix() int64 {
 
 // UnixNano returns t as a Unix time, the number of nanoseconds elapsed
 // since January 1, 1970 UTC. The result is undefined if the Unix time
-// in nanoseconds cannot be represented by an int64. Note that this
-// means the result of calling UnixNano on the zero Time is undefined.
+// in nanoseconds cannot be represented by an int64 (a date before the year
+// 1678 or after 2262). Note that this means the result of calling UnixNano
+// on the zero Time is undefined.
 func (t Time) UnixNano() int64 {
 	return (t.sec+internalToUnix)*1e9 + int64(t.nsec)
 }
@@ -838,7 +859,7 @@ const timeBinaryVersion byte = 1
 func (t Time) MarshalBinary() ([]byte, error) {
 	var offsetMin int16 // minutes east of UTC. -1 is UTC.
 
-	if t.Location() == &utcLoc {
+	if t.Location() == UTC {
 		offsetMin = -1
 	} else {
 		_, offset := t.Zone()
@@ -899,11 +920,11 @@ func (t *Time) UnmarshalBinary(data []byte) error {
 	offset := int(int16(buf[1])|int16(buf[0])<<8) * 60
 
 	if offset == -1*60 {
-		t.loc = &utcLoc
+		t.setLoc(&utcLoc)
 	} else if _, localoff, _, _, _ := Local.lookup(t.sec + internalToUnix); offset == localoff {
-		t.loc = Local
+		t.setLoc(Local)
 	} else {
-		t.loc = FixedZone("", offset)
+		t.setLoc(FixedZone("", offset))
 	}
 
 	return nil
@@ -942,6 +963,10 @@ func (t Time) MarshalJSON() ([]byte, error) {
 // UnmarshalJSON implements the json.Unmarshaler interface.
 // The time is expected to be a quoted string in RFC 3339 format.
 func (t *Time) UnmarshalJSON(data []byte) error {
+	// Ignore null, like in the main JSON package.
+	if string(data) == "null" {
+		return nil
+	}
 	// Fractional seconds are handled implicitly by Parse.
 	var err error
 	*t, err = Parse(`"`+RFC3339+`"`, string(data))
@@ -1092,11 +1117,18 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T
 		unix -= int64(offset)
 	}
 
-	return Time{unix + unixToInternal, int32(nsec), loc}
+	t := Time{unix + unixToInternal, int32(nsec), nil}
+	t.setLoc(loc)
+	return t
 }
 
 // Truncate returns the result of rounding t down to a multiple of d (since the zero time).
 // If d <= 0, Truncate returns t unchanged.
+//
+// Truncate operates on the time as an absolute duration since the
+// zero time; it does not operate on the presentation form of the
+// time. Thus, Truncate(Hour) may return a time with a non-zero
+// minute, depending on the time's Location.
 func (t Time) Truncate(d Duration) Time {
 	if d <= 0 {
 		return t
@@ -1108,6 +1140,11 @@ func (t Time) Truncate(d Duration) Time {
 // Round returns the result of rounding t to the nearest multiple of d (since the zero time).
 // The rounding behavior for halfway values is to round up.
 // If d <= 0, Round returns t unchanged.
+//
+// Round operates on the time as an absolute duration since the
+// zero time; it does not operate on the presentation form of the
+// time. Thus, Round(Hour) may return a time with a non-zero
+// minute, depending on the time's Location.
 func (t Time) Round(d Duration) Time {
 	if d <= 0 {
 		return t
diff --git a/src/time/time_test.go b/src/time/time_test.go
index b7ebb37..2922560 100644
--- a/src/time/time_test.go
+++ b/src/time/time_test.go
@@ -840,6 +840,10 @@ var parseDurationTests = []struct {
 	{"9223372036s854ms775us807ns", true, (1<<63 - 1) * Nanosecond},
 	// large negative value
 	{"-9223372036854775807ns", true, -1<<63 + 1*Nanosecond},
+	// huge string; issue 15011.
+	{"0.100000000000000000000h", true, 6 * Minute},
+	// This value tests the first overflow check in leadingFraction.
+	{"0.830103483285477580700h", true, 49*Minute + 48*Second + 372539827*Nanosecond},
 
 	// errors
 	{"", false, 0},
@@ -891,7 +895,7 @@ func TestLocationRace(t *testing.T) {
 	go func() {
 		c <- Now().String()
 	}()
-	Now().String()
+	_ = Now().String()
 	<-c
 	Sleep(100 * Millisecond)
 
@@ -939,8 +943,11 @@ func TestLoadFixed(t *testing.T) {
 	// but Go and most other systems use "east is positive".
 	// So GMT+1 corresponds to -3600 in the Go zone, not +3600.
 	name, offset := Now().In(loc).Zone()
-	if name != "GMT+1" || offset != -1*60*60 {
-		t.Errorf("Now().In(loc).Zone() = %q, %d, want %q, %d", name, offset, "GMT+1", -1*60*60)
+	// The zone abbreviation is "-01" since tzdata-2016g, and "GMT+1"
+	// on earlier versions; we accept both. (Issue #17276).
+	if !(name == "GMT+1" || name == "-01") || offset != -1*60*60 {
+		t.Errorf("Now().In(loc).Zone() = %q, %d, want %q or %q, %d",
+			name, offset, "GMT+1", "-01", -1*60*60)
 	}
 }
 
@@ -996,6 +1003,21 @@ func TestDurationNanoseconds(t *testing.T) {
 	}
 }
 
+var secDurationTests = []struct {
+	d    Duration
+	want float64
+}{
+	{Duration(300000000), 0.3},
+}
+
+func TestDurationSeconds(t *testing.T) {
+	for _, tt := range secDurationTests {
+		if got := tt.d.Seconds(); got != tt.want {
+			t.Errorf("d.Seconds() = %g; want: %g", got, tt.want)
+		}
+	}
+}
+
 var minDurationTests = []struct {
 	d    Duration
 	want float64
@@ -1004,6 +1026,7 @@ var minDurationTests = []struct {
 	{Duration(-1), -1 / 60e9},
 	{Duration(1), 1 / 60e9},
 	{Duration(60000000000), 1},
+	{Duration(3000), 5e-8},
 }
 
 func TestDurationMinutes(t *testing.T) {
@@ -1022,6 +1045,7 @@ var hourDurationTests = []struct {
 	{Duration(-1), -1 / 3600e9},
 	{Duration(1), 1 / 3600e9},
 	{Duration(3600000000000), 1},
+	{Duration(36), 1e-11},
 }
 
 func TestDurationHours(t *testing.T) {
@@ -1032,6 +1056,100 @@ func TestDurationHours(t *testing.T) {
 	}
 }
 
+var defaultLocTests = []struct {
+	name string
+	f    func(t1, t2 Time) bool
+}{
+	{"After", func(t1, t2 Time) bool { return t1.After(t2) == t2.After(t1) }},
+	{"Before", func(t1, t2 Time) bool { return t1.Before(t2) == t2.Before(t1) }},
+	{"Equal", func(t1, t2 Time) bool { return t1.Equal(t2) == t2.Equal(t1) }},
+
+	{"IsZero", func(t1, t2 Time) bool { return t1.IsZero() == t2.IsZero() }},
+	{"Date", func(t1, t2 Time) bool {
+		a1, b1, c1 := t1.Date()
+		a2, b2, c2 := t2.Date()
+		return a1 == a2 && b1 == b2 && c1 == c2
+	}},
+	{"Year", func(t1, t2 Time) bool { return t1.Year() == t2.Year() }},
+	{"Month", func(t1, t2 Time) bool { return t1.Month() == t2.Month() }},
+	{"Day", func(t1, t2 Time) bool { return t1.Day() == t2.Day() }},
+	{"Weekday", func(t1, t2 Time) bool { return t1.Weekday() == t2.Weekday() }},
+	{"ISOWeek", func(t1, t2 Time) bool {
+		a1, b1 := t1.ISOWeek()
+		a2, b2 := t2.ISOWeek()
+		return a1 == a2 && b1 == b2
+	}},
+	{"Clock", func(t1, t2 Time) bool {
+		a1, b1, c1 := t1.Clock()
+		a2, b2, c2 := t2.Clock()
+		return a1 == a2 && b1 == b2 && c1 == c2
+	}},
+	{"Hour", func(t1, t2 Time) bool { return t1.Hour() == t2.Hour() }},
+	{"Minute", func(t1, t2 Time) bool { return t1.Minute() == t2.Minute() }},
+	{"Second", func(t1, t2 Time) bool { return t1.Second() == t2.Second() }},
+	{"Nanosecond", func(t1, t2 Time) bool { return t1.Hour() == t2.Hour() }},
+	{"YearDay", func(t1, t2 Time) bool { return t1.YearDay() == t2.YearDay() }},
+
+	// Using Equal since Add don't modify loc using "==" will cause a fail
+	{"Add", func(t1, t2 Time) bool { return t1.Add(Hour).Equal(t2.Add(Hour)) }},
+	{"Sub", func(t1, t2 Time) bool { return t1.Sub(t2) == t2.Sub(t1) }},
+
+	//Original caus for this test case bug 15852
+	{"AddDate", func(t1, t2 Time) bool { return t1.AddDate(1991, 9, 3) == t2.AddDate(1991, 9, 3) }},
+
+	{"UTC", func(t1, t2 Time) bool { return t1.UTC() == t2.UTC() }},
+	{"Local", func(t1, t2 Time) bool { return t1.Local() == t2.Local() }},
+	{"In", func(t1, t2 Time) bool { return t1.In(UTC) == t2.In(UTC) }},
+
+	{"Local", func(t1, t2 Time) bool { return t1.Local() == t2.Local() }},
+	{"Zone", func(t1, t2 Time) bool {
+		a1, b1 := t1.Zone()
+		a2, b2 := t2.Zone()
+		return a1 == a2 && b1 == b2
+	}},
+
+	{"Unix", func(t1, t2 Time) bool { return t1.Unix() == t2.Unix() }},
+	{"UnixNano", func(t1, t2 Time) bool { return t1.UnixNano() == t2.UnixNano() }},
+
+	{"MarshalBinary", func(t1, t2 Time) bool {
+		a1, b1 := t1.MarshalBinary()
+		a2, b2 := t2.MarshalBinary()
+		return bytes.Equal(a1, a2) && b1 == b2
+	}},
+	{"GobEncode", func(t1, t2 Time) bool {
+		a1, b1 := t1.GobEncode()
+		a2, b2 := t2.GobEncode()
+		return bytes.Equal(a1, a2) && b1 == b2
+	}},
+	{"MarshalJSON", func(t1, t2 Time) bool {
+		a1, b1 := t1.MarshalJSON()
+		a2, b2 := t2.MarshalJSON()
+		return bytes.Equal(a1, a2) && b1 == b2
+	}},
+	{"MarshalText", func(t1, t2 Time) bool {
+		a1, b1 := t1.MarshalText()
+		a2, b2 := t2.MarshalText()
+		return bytes.Equal(a1, a2) && b1 == b2
+	}},
+
+	{"Truncate", func(t1, t2 Time) bool { return t1.Truncate(Hour).Equal(t2.Truncate(Hour)) }},
+	{"Round", func(t1, t2 Time) bool { return t1.Round(Hour).Equal(t2.Round(Hour)) }},
+
+	{"== Time{}", func(t1, t2 Time) bool { return (t1 == Time{}) == (t2 == Time{}) }},
+}
+
+func TestDefaultLoc(t *testing.T) {
+	//This test verifyes that all Time's methods behaves identical if loc is set
+	//as nil or UTC
+	for _, tt := range defaultLocTests {
+		t1 := Time{}
+		t2 := Time{}.UTC()
+		if !tt.f(t1, t2) {
+			t.Errorf("Time{} and Time{}.UTC() behave differently for %s", tt.name)
+		}
+	}
+}
+
 func BenchmarkNow(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		t = Now()
@@ -1114,3 +1232,25 @@ func BenchmarkDay(b *testing.B) {
 		_ = t.Day()
 	}
 }
+
+func TestMarshalBinaryZeroTime(t *testing.T) {
+	t0 := Time{}
+	enc, err := t0.MarshalBinary()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t1 := Now() // not zero
+	if err := t1.UnmarshalBinary(enc); err != nil {
+		t.Fatal(err)
+	}
+	if t1 != t0 {
+		t.Errorf("t0=%#v\nt1=%#v\nwant identical structures", t0, t1)
+	}
+}
+
+// Issue 17720: Zero value of time.Month fails to print
+func TestZeroMonthString(t *testing.T) {
+	if got, want := Month(0).String(), "%!Month(0)"; got != want {
+		t.Errorf("zero month = %q; want %q", got, want)
+	}
+}
diff --git a/src/time/zoneinfo.go b/src/time/zoneinfo.go
index c567439..fb0aa39 100644
--- a/src/time/zoneinfo.go
+++ b/src/time/zoneinfo.go
@@ -9,6 +9,8 @@ import (
 	"syscall"
 )
 
+//go:generate env ZONEINFO=$GOROOT/lib/time/zoneinfo.zip go run genzabbrs.go -output zoneinfo_abbrs_windows.go
+
 // A Location maps time instants to the zone in use at that time.
 // Typically, the Location represents the collection of time offsets
 // in use in a geographical area, such as CEST and CET for central Europe.
diff --git a/src/time/zoneinfo_abbrs_windows.go b/src/time/zoneinfo_abbrs_windows.go
index 344a891..9425db8 100644
--- a/src/time/zoneinfo_abbrs_windows.go
+++ b/src/time/zoneinfo_abbrs_windows.go
@@ -13,92 +13,106 @@ type abbr struct {
 }
 
 var abbrs = map[string]abbr{
-	"Egypt Standard Time":             {"EET", "EET"},    // Africa/Cairo
-	"Morocco Standard Time":           {"WET", "WEST"},   // Africa/Casablanca
-	"South Africa Standard Time":      {"SAST", "SAST"},  // Africa/Johannesburg
-	"W. Central Africa Standard Time": {"WAT", "WAT"},    // Africa/Lagos
-	"E. Africa Standard Time":         {"EAT", "EAT"},    // Africa/Nairobi
-	"Libya Standard Time":             {"EET", "EET"},    // Africa/Tripoli
-	"Namibia Standard Time":           {"WAT", "WAST"},   // Africa/Windhoek
-	"Alaskan Standard Time":           {"AKST", "AKDT"},  // America/Anchorage
-	"Paraguay Standard Time":          {"PYT", "PYST"},   // America/Asuncion
-	"Bahia Standard Time":             {"BRT", "BRT"},    // America/Bahia
-	"SA Pacific Standard Time":        {"COT", "COT"},    // America/Bogota
-	"Argentina Standard Time":         {"ART", "ART"},    // America/Buenos_Aires
-	"Eastern Standard Time (Mexico)":  {"EST", "EST"},    // America/Cancun
-	"Venezuela Standard Time":         {"VET", "VET"},    // America/Caracas
-	"SA Eastern Standard Time":        {"GFT", "GFT"},    // America/Cayenne
-	"Central Standard Time":           {"CST", "CDT"},    // America/Chicago
-	"Mountain Standard Time (Mexico)": {"MST", "MDT"},    // America/Chihuahua
-	"Central Brazilian Standard Time": {"AMT", "AMST"},   // America/Cuiaba
-	"Mountain Standard Time":          {"MST", "MDT"},    // America/Denver
-	"Greenland Standard Time":         {"WGT", "WGST"},   // America/Godthab
-	"Central America Standard Time":   {"CST", "CST"},    // America/Guatemala
-	"Atlantic Standard Time":          {"AST", "ADT"},    // America/Halifax
-	"US Eastern Standard Time":        {"EST", "EDT"},    // America/Indianapolis
-	"SA Western Standard Time":        {"BOT", "BOT"},    // America/La_Paz
-	"Pacific Standard Time":           {"PST", "PDT"},    // America/Los_Angeles
-	"Central Standard Time (Mexico)":  {"CST", "CDT"},    // America/Mexico_City
-	"Montevideo Standard Time":        {"UYT", "UYT"},    // America/Montevideo
-	"Eastern Standard Time":           {"EST", "EDT"},    // America/New_York
-	"US Mountain Standard Time":       {"MST", "MST"},    // America/Phoenix
-	"Canada Central Standard Time":    {"CST", "CST"},    // America/Regina
-	"Pacific SA Standard Time":        {"CLT", "CLST"},   // America/Santiago
-	"E. South America Standard Time":  {"BRT", "BRST"},   // America/Sao_Paulo
-	"Newfoundland Standard Time":      {"NST", "NDT"},    // America/St_Johns
-	"Central Asia Standard Time":      {"+06", "+06"},    // Asia/Almaty
-	"Jordan Standard Time":            {"EET", "EEST"},   // Asia/Amman
-	"Arabic Standard Time":            {"AST", "AST"},    // Asia/Baghdad
-	"Azerbaijan Standard Time":        {"AZT", "AZT"},    // Asia/Baku
-	"SE Asia Standard Time":           {"ICT", "ICT"},    // Asia/Bangkok
-	"Altai Standard Time":             {"+06", "+07"},    // Asia/Barnaul
-	"Middle East Standard Time":       {"EET", "EEST"},   // Asia/Beirut
-	"India Standard Time":             {"IST", "IST"},    // Asia/Calcutta
-	"Transbaikal Standard Time":       {"IRKT", "YAKT"},  // Asia/Chita
-	"Sri Lanka Standard Time":         {"IST", "IST"},    // Asia/Colombo
-	"Syria Standard Time":             {"EET", "EEST"},   // Asia/Damascus
-	"Bangladesh Standard Time":        {"BDT", "BDT"},    // Asia/Dhaka
-	"Arabian Standard Time":           {"GST", "GST"},    // Asia/Dubai
-	"North Asia East Standard Time":   {"IRKT", "IRKT"},  // Asia/Irkutsk
-	"Israel Standard Time":            {"IST", "IDT"},    // Asia/Jerusalem
-	"Afghanistan Standard Time":       {"AFT", "AFT"},    // Asia/Kabul
-	"Russia Time Zone 11":             {"PETT", "PETT"},  // Asia/Kamchatka
-	"Pakistan Standard Time":          {"PKT", "PKT"},    // Asia/Karachi
-	"Nepal Standard Time":             {"NPT", "NPT"},    // Asia/Katmandu
-	"North Asia Standard Time":        {"KRAT", "KRAT"},  // Asia/Krasnoyarsk
-	"Magadan Standard Time":           {"MAGT", "MAGT"},  // Asia/Magadan
-	"N. Central Asia Standard Time":   {"NOVT", "NOVT"},  // Asia/Novosibirsk
-	"North Korea Standard Time":       {"KST", "KST"},    // Asia/Pyongyang
-	"Myanmar Standard Time":           {"MMT", "MMT"},    // Asia/Rangoon
-	"Arab Standard Time":              {"AST", "AST"},    // Asia/Riyadh
-	"Sakhalin Standard Time":          {"SAKT", "SAKT"},  // Asia/Sakhalin
-	"Korea Standard Time":             {"KST", "KST"},    // Asia/Seoul
-	"China Standard Time":             {"CST", "CST"},    // Asia/Shanghai
-	"Singapore Standard Time":         {"SGT", "SGT"},    // Asia/Singapore
-	"Russia Time Zone 10":             {"SRET", "SRET"},  // Asia/Srednekolymsk
-	"Taipei Standard Time":            {"CST", "CST"},    // Asia/Taipei
-	"West Asia Standard Time":         {"UZT", "UZT"},    // Asia/Tashkent
-	"Georgian Standard Time":          {"GET", "GET"},    // Asia/Tbilisi
-	"Iran Standard Time":              {"IRST", "IRDT"},  // Asia/Tehran
-	"Tokyo Standard Time":             {"JST", "JST"},    // Asia/Tokyo
-	"Ulaanbaatar Standard Time":       {"ULAT", "ULAST"}, // Asia/Ulaanbaatar
-	"Vladivostok Standard Time":       {"VLAT", "VLAT"},  // Asia/Vladivostok
-	"Yakutsk Standard Time":           {"YAKT", "YAKT"},  // Asia/Yakutsk
-	"Ekaterinburg Standard Time":      {"YEKT", "YEKT"},  // Asia/Yekaterinburg
-	"Caucasus Standard Time":          {"AMT", "AMT"},    // Asia/Yerevan
-	"Azores Standard Time":            {"AZOT", "AZOST"}, // Atlantic/Azores
-	"Cape Verde Standard Time":        {"CVT", "CVT"},    // Atlantic/Cape_Verde
-	"Greenwich Standard Time":         {"GMT", "GMT"},    // Atlantic/Reykjavik
-	"Cen. Australia Standard Time":    {"ACST", "ACDT"},  // Australia/Adelaide
-	"E. Australia Standard Time":      {"AEST", "AEST"},  // Australia/Brisbane
-	"AUS Central Standard Time":       {"ACST", "ACST"},  // Australia/Darwin
-	"Tasmania Standard Time":          {"AEST", "AEDT"},  // Australia/Hobart
-	"W. Australia Standard Time":      {"AWST", "AWST"},  // Australia/Perth
-	"AUS Eastern Standard Time":       {"AEST", "AEDT"},  // Australia/Sydney
+	"Egypt Standard Time":             {"EET", "EET"},     // Africa/Cairo
+	"Morocco Standard Time":           {"WET", "WEST"},    // Africa/Casablanca
+	"South Africa Standard Time":      {"SAST", "SAST"},   // Africa/Johannesburg
+	"W. Central Africa Standard Time": {"WAT", "WAT"},     // Africa/Lagos
+	"E. Africa Standard Time":         {"EAT", "EAT"},     // Africa/Nairobi
+	"Libya Standard Time":             {"EET", "EET"},     // Africa/Tripoli
+	"Namibia Standard Time":           {"WAT", "WAST"},    // Africa/Windhoek
+	"Aleutian Standard Time":          {"HST", "HDT"},     // America/Adak
+	"Alaskan Standard Time":           {"AKST", "AKDT"},   // America/Anchorage
+	"Tocantins Standard Time":         {"BRT", "BRT"},     // America/Araguaina
+	"Paraguay Standard Time":          {"PYT", "PYST"},    // America/Asuncion
+	"Bahia Standard Time":             {"BRT", "BRT"},     // America/Bahia
+	"SA Pacific Standard Time":        {"COT", "COT"},     // America/Bogota
+	"Argentina Standard Time":         {"ART", "ART"},     // America/Buenos_Aires
+	"Eastern Standard Time (Mexico)":  {"EST", "EST"},     // America/Cancun
+	"Venezuela Standard Time":         {"VET", "VET"},     // America/Caracas
+	"SA Eastern Standard Time":        {"GFT", "GFT"},     // America/Cayenne
+	"Central Standard Time":           {"CST", "CDT"},     // America/Chicago
+	"Mountain Standard Time (Mexico)": {"MST", "MDT"},     // America/Chihuahua
+	"Central Brazilian Standard Time": {"AMT", "AMST"},    // America/Cuiaba
+	"Mountain Standard Time":          {"MST", "MDT"},     // America/Denver
+	"Greenland Standard Time":         {"WGT", "WGST"},    // America/Godthab
+	"Turks And Caicos Standard Time":  {"AST", "AST"},     // America/Grand_Turk
+	"Central America Standard Time":   {"CST", "CST"},     // America/Guatemala
+	"Atlantic Standard Time":          {"AST", "ADT"},     // America/Halifax
+	"Cuba Standard Time":              {"CST", "CDT"},     // America/Havana
+	"US Eastern Standard Time":        {"EST", "EDT"},     // America/Indianapolis
+	"SA Western Standard Time":        {"BOT", "BOT"},     // America/La_Paz
+	"Pacific Standard Time":           {"PST", "PDT"},     // America/Los_Angeles
+	"Central Standard Time (Mexico)":  {"CST", "CDT"},     // America/Mexico_City
+	"Saint Pierre Standard Time":      {"PMST", "PMDT"},   // America/Miquelon
+	"Montevideo Standard Time":        {"UYT", "UYT"},     // America/Montevideo
+	"Eastern Standard Time":           {"EST", "EDT"},     // America/New_York
+	"US Mountain Standard Time":       {"MST", "MST"},     // America/Phoenix
+	"Haiti Standard Time":             {"EST", "EST"},     // America/Port-au-Prince
+	"Canada Central Standard Time":    {"CST", "CST"},     // America/Regina
+	"Pacific SA Standard Time":        {"CLT", "CLST"},    // America/Santiago
+	"E. South America Standard Time":  {"BRT", "BRST"},    // America/Sao_Paulo
+	"Newfoundland Standard Time":      {"NST", "NDT"},     // America/St_Johns
+	"Pacific Standard Time (Mexico)":  {"PST", "PDT"},     // America/Tijuana
+	"Central Asia Standard Time":      {"+06", "+06"},     // Asia/Almaty
+	"Jordan Standard Time":            {"EET", "EEST"},    // Asia/Amman
+	"Arabic Standard Time":            {"AST", "AST"},     // Asia/Baghdad
+	"Azerbaijan Standard Time":        {"AZT", "AZT"},     // Asia/Baku
+	"SE Asia Standard Time":           {"ICT", "ICT"},     // Asia/Bangkok
+	"Altai Standard Time":             {"+06", "+07"},     // Asia/Barnaul
+	"Middle East Standard Time":       {"EET", "EEST"},    // Asia/Beirut
+	"India Standard Time":             {"IST", "IST"},     // Asia/Calcutta
+	"Transbaikal Standard Time":       {"IRKT", "YAKT"},   // Asia/Chita
+	"Sri Lanka Standard Time":         {"IST", "IST"},     // Asia/Colombo
+	"Syria Standard Time":             {"EET", "EEST"},    // Asia/Damascus
+	"Bangladesh Standard Time":        {"BDT", "BDT"},     // Asia/Dhaka
+	"Arabian Standard Time":           {"GST", "GST"},     // Asia/Dubai
+	"West Bank Standard Time":         {"EET", "EEST"},    // Asia/Hebron
+	"W. Mongolia Standard Time":       {"HOVT", "HOVST"},  // Asia/Hovd
+	"North Asia East Standard Time":   {"IRKT", "IRKT"},   // Asia/Irkutsk
+	"Israel Standard Time":            {"IST", "IDT"},     // Asia/Jerusalem
+	"Afghanistan Standard Time":       {"AFT", "AFT"},     // Asia/Kabul
+	"Russia Time Zone 11":             {"PETT", "PETT"},   // Asia/Kamchatka
+	"Pakistan Standard Time":          {"PKT", "PKT"},     // Asia/Karachi
+	"Nepal Standard Time":             {"NPT", "NPT"},     // Asia/Katmandu
+	"North Asia Standard Time":        {"KRAT", "KRAT"},   // Asia/Krasnoyarsk
+	"Magadan Standard Time":           {"MAGT", "MAGT"},   // Asia/Magadan
+	"N. Central Asia Standard Time":   {"+06", "+07"},     // Asia/Novosibirsk
+	"North Korea Standard Time":       {"KST", "KST"},     // Asia/Pyongyang
+	"Myanmar Standard Time":           {"MMT", "MMT"},     // Asia/Rangoon
+	"Arab Standard Time":              {"AST", "AST"},     // Asia/Riyadh
+	"Sakhalin Standard Time":          {"SAKT", "SAKT"},   // Asia/Sakhalin
+	"Korea Standard Time":             {"KST", "KST"},     // Asia/Seoul
+	"China Standard Time":             {"CST", "CST"},     // Asia/Shanghai
+	"Singapore Standard Time":         {"SGT", "SGT"},     // Asia/Singapore
+	"Russia Time Zone 10":             {"SRET", "SRET"},   // Asia/Srednekolymsk
+	"Taipei Standard Time":            {"CST", "CST"},     // Asia/Taipei
+	"West Asia Standard Time":         {"UZT", "UZT"},     // Asia/Tashkent
+	"Georgian Standard Time":          {"GET", "GET"},     // Asia/Tbilisi
+	"Iran Standard Time":              {"IRST", "IRDT"},   // Asia/Tehran
+	"Tokyo Standard Time":             {"JST", "JST"},     // Asia/Tokyo
+	"Tomsk Standard Time":             {"+06", "+07"},     // Asia/Tomsk
+	"Ulaanbaatar Standard Time":       {"ULAT", "ULAST"},  // Asia/Ulaanbaatar
+	"Vladivostok Standard Time":       {"VLAT", "VLAT"},   // Asia/Vladivostok
+	"Yakutsk Standard Time":           {"YAKT", "YAKT"},   // Asia/Yakutsk
+	"Ekaterinburg Standard Time":      {"YEKT", "YEKT"},   // Asia/Yekaterinburg
+	"Caucasus Standard Time":          {"AMT", "AMT"},     // Asia/Yerevan
+	"Azores Standard Time":            {"AZOT", "AZOST"},  // Atlantic/Azores
+	"Cape Verde Standard Time":        {"CVT", "CVT"},     // Atlantic/Cape_Verde
+	"Greenwich Standard Time":         {"GMT", "GMT"},     // Atlantic/Reykjavik
+	"Cen. Australia Standard Time":    {"ACST", "ACDT"},   // Australia/Adelaide
+	"E. Australia Standard Time":      {"AEST", "AEST"},   // Australia/Brisbane
+	"AUS Central Standard Time":       {"ACST", "ACST"},   // Australia/Darwin
+	"Aus Central W. Standard Time":    {"ACWST", "ACWST"}, // Australia/Eucla
+	"Tasmania Standard Time":          {"AEST", "AEDT"},   // Australia/Hobart
+	"Lord Howe Standard Time":         {"LHST", "LHDT"},   // Australia/Lord_Howe
+	"W. Australia Standard Time":      {"AWST", "AWST"},   // Australia/Perth
+	"AUS Eastern Standard Time":       {"AEST", "AEDT"},   // Australia/Sydney
 	"UTC":                            {"GMT", "GMT"},       // Etc/GMT
 	"UTC-11":                         {"GMT+11", "GMT+11"}, // Etc/GMT+11
 	"Dateline Standard Time":         {"GMT+12", "GMT+12"}, // Etc/GMT+12
 	"UTC-02":                         {"GMT+2", "GMT+2"},   // Etc/GMT+2
+	"UTC-08":                         {"GMT+8", "GMT+8"},   // Etc/GMT+8
+	"UTC-09":                         {"GMT+9", "GMT+9"},   // Etc/GMT+9
 	"UTC+12":                         {"GMT-12", "GMT-12"}, // Etc/GMT-12
 	"Astrakhan Standard Time":        {"+03", "+04"},       // Europe/Astrakhan
 	"W. Europe Standard Time":        {"CET", "CEST"},      // Europe/Berlin
@@ -117,10 +131,15 @@ var abbrs = map[string]abbr{
 	"Mauritius Standard Time":        {"MUT", "MUT"},       // Indian/Mauritius
 	"Samoa Standard Time":            {"WSST", "WSDT"},     // Pacific/Apia
 	"New Zealand Standard Time":      {"NZST", "NZDT"},     // Pacific/Auckland
+	"Bougainville Standard Time":     {"BST", "BST"},       // Pacific/Bougainville
+	"Chatham Islands Standard Time":  {"CHAST", "CHADT"},   // Pacific/Chatham
+	"Easter Island Standard Time":    {"EAST", "EASST"},    // Pacific/Easter
 	"Fiji Standard Time":             {"FJT", "FJST"},      // Pacific/Fiji
 	"Central Pacific Standard Time":  {"SBT", "SBT"},       // Pacific/Guadalcanal
 	"Hawaiian Standard Time":         {"HST", "HST"},       // Pacific/Honolulu
 	"Line Islands Standard Time":     {"LINT", "LINT"},     // Pacific/Kiritimati
+	"Marquesas Standard Time":        {"MART", "MART"},     // Pacific/Marquesas
+	"Norfolk Standard Time":          {"NFT", "NFT"},       // Pacific/Norfolk
 	"West Pacific Standard Time":     {"PGT", "PGT"},       // Pacific/Port_Moresby
 	"Tonga Standard Time":            {"TOT", "TOT"},       // Pacific/Tongatapu
 }
diff --git a/src/time/zoneinfo_android.go b/src/time/zoneinfo_android.go
new file mode 100644
index 0000000..695a8ad
--- /dev/null
+++ b/src/time/zoneinfo_android.go
@@ -0,0 +1,119 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Parse the "tzdata" packed timezone file used on Android.
+// The format is lifted from ZoneInfoDB.java and ZoneInfo.java in
+// java/libcore/util in the AOSP.
+
+package time
+
+import (
+	"errors"
+	"runtime"
+)
+
+var tzdataPaths = []string{
+	"/system/usr/share/zoneinfo/tzdata",
+	"/data/misc/zoneinfo/current/tzdata",
+	runtime.GOROOT() + "/lib/time/zoneinfo.zip",
+}
+
+var origTzdataPaths = tzdataPaths
+
+func forceZipFileForTesting(zipOnly bool) {
+	tzdataPaths = make([]string, len(origTzdataPaths))
+	copy(tzdataPaths, origTzdataPaths)
+	if zipOnly {
+		for i := 0; i < len(tzdataPaths)-1; i++ {
+			tzdataPaths[i] = "/XXXNOEXIST"
+		}
+	}
+}
+
+func initTestingZone() {
+	z, err := loadLocation("America/Los_Angeles")
+	if err != nil {
+		panic("cannot load America/Los_Angeles for testing: " + err.Error())
+	}
+	z.name = "Local"
+	localLoc = *z
+}
+
+func initLocal() {
+	// TODO(elias.naur): getprop persist.sys.timezone
+	localLoc = *UTC
+}
+
+func loadLocation(name string) (*Location, error) {
+	var firstErr error
+	for _, path := range tzdataPaths {
+		var z *Location
+		var err error
+		if len(path) > 4 && path[len(path)-4:] == ".zip" {
+			z, err = loadZoneZip(path, name)
+		} else {
+			z, err = loadTzdataFile(path, name)
+		}
+		if err == nil {
+			z.name = name
+			return z, nil
+		} else if firstErr == nil && !isNotExist(err) {
+			firstErr = err
+		}
+	}
+	if firstErr != nil {
+		return nil, firstErr
+	}
+	return nil, errors.New("unknown time zone " + name)
+}
+
+func loadTzdataFile(file, name string) (*Location, error) {
+	const (
+		headersize = 12 + 3*4
+		namesize   = 40
+		entrysize  = namesize + 3*4
+	)
+	if len(name) > namesize {
+		return nil, errors.New(name + " is longer than the maximum zone name length (40 bytes)")
+	}
+	fd, err := open(file)
+	if err != nil {
+		return nil, err
+	}
+	defer closefd(fd)
+
+	buf := make([]byte, headersize)
+	if err := preadn(fd, buf, 0); err != nil {
+		return nil, errors.New("corrupt tzdata file " + file)
+	}
+	d := data{buf, false}
+	if magic := d.read(6); string(magic) != "tzdata" {
+		return nil, errors.New("corrupt tzdata file " + file)
+	}
+	d = data{buf[12:], false}
+	indexOff, _ := d.big4()
+	dataOff, _ := d.big4()
+	indexSize := dataOff - indexOff
+	entrycount := indexSize / entrysize
+	buf = make([]byte, indexSize)
+	if err := preadn(fd, buf, int(indexOff)); err != nil {
+		return nil, errors.New("corrupt tzdata file " + file)
+	}
+	for i := 0; i < int(entrycount); i++ {
+		entry := buf[i*entrysize : (i+1)*entrysize]
+		// len(name) <= namesize is checked at function entry
+		if string(entry[:len(name)]) != name {
+			continue
+		}
+		d := data{entry[namesize:], false}
+		off, _ := d.big4()
+		size, _ := d.big4()
+		buf := make([]byte, size)
+		if err := preadn(fd, buf, int(off+dataOff)); err != nil {
+			return nil, errors.New("corrupt tzdata file " + file)
+		}
+		return loadZoneData(buf)
+	}
+	return nil, errors.New("cannot find " + name + " in tzdata file " + file)
+}
diff --git a/src/time/zoneinfo_android_test.go b/src/time/zoneinfo_android_test.go
new file mode 100644
index 0000000..ba065d1
--- /dev/null
+++ b/src/time/zoneinfo_android_test.go
@@ -0,0 +1,18 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package time_test
+
+import (
+	"testing"
+	. "time"
+)
+
+func TestAndroidTzdata(t *testing.T) {
+	ForceAndroidTzdataForTest(true)
+	defer ForceAndroidTzdataForTest(false)
+	if _, err := LoadLocation("America/Los_Angeles"); err != nil {
+		t.Error(err)
+	}
+}
diff --git a/src/time/zoneinfo_unix.go b/src/time/zoneinfo_unix.go
index ed9502d..bbf263a 100644
--- a/src/time/zoneinfo_unix.go
+++ b/src/time/zoneinfo_unix.go
@@ -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,386 darwin,amd64 dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin,386 darwin,amd64 dragonfly freebsd linux,!android nacl netbsd openbsd solaris
 
 // Parse "zoneinfo" time zone file.
 // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
diff --git a/src/time/zoneinfo_windows.go b/src/time/zoneinfo_windows.go
index a6546f5..a6e227b 100644
--- a/src/time/zoneinfo_windows.go
+++ b/src/time/zoneinfo_windows.go
@@ -11,8 +11,6 @@ import (
 	"syscall"
 )
 
-//go:generate go run genzabbrs.go -output zoneinfo_abbrs_windows.go
-
 // TODO(rsc): Fall back to copy of zoneinfo files.
 
 // BUG(brainman,rsc): On Windows, the operating system does not provide complete
diff --git a/src/unicode/letter.go b/src/unicode/letter.go
index 8aec920..b43cc66 100644
--- a/src/unicode/letter.go
+++ b/src/unicode/letter.go
@@ -320,6 +320,7 @@ type foldPair struct {
 // the Unicode-defined simple case folding. Among the code points
 // equivalent to rune (including rune itself), SimpleFold returns the
 // smallest rune > r if one exists, or else the smallest rune >= 0.
+// If r is not a valid Unicode code point, SimpleFold(r) returns r.
 //
 // For example:
 //	SimpleFold('A') = 'a'
@@ -331,7 +332,13 @@ type foldPair struct {
 //
 //	SimpleFold('1') = '1'
 //
+//	SimpleFold(-2) = -2
+//
 func SimpleFold(r rune) rune {
+	if r < 0 || r > MaxRune {
+		return r
+	}
+
 	if int(r) < len(asciiFold) {
 		return rune(asciiFold[r])
 	}
diff --git a/src/unicode/letter_test.go b/src/unicode/letter_test.go
index 0eb9ee9..3fe72ff 100644
--- a/src/unicode/letter_test.go
+++ b/src/unicode/letter_test.go
@@ -432,6 +432,10 @@ func TestSimpleFold(t *testing.T) {
 			r = out
 		}
 	}
+
+	if r := SimpleFold(-42); r != -42 {
+		t.Errorf("SimpleFold(-42) = %v, want -42", r)
+	}
 }
 
 // Running 'go test -calibrate' runs the calibration to find a plausible
diff --git a/src/unicode/utf8/utf8.go b/src/unicode/utf8/utf8.go
index 9d35be6..6ccd464 100644
--- a/src/unicode/utf8/utf8.go
+++ b/src/unicode/utf8/utf8.go
@@ -347,6 +347,7 @@ func EncodeRune(p []byte, r rune) int {
 		p[0] = byte(r)
 		return 1
 	case i <= rune2Max:
+		_ = p[1] // eliminate bounds checks
 		p[0] = t2 | byte(r>>6)
 		p[1] = tx | byte(r)&maskx
 		return 2
@@ -354,11 +355,13 @@ func EncodeRune(p []byte, r rune) int {
 		r = RuneError
 		fallthrough
 	case i <= rune3Max:
+		_ = p[2] // eliminate bounds checks
 		p[0] = t3 | byte(r>>12)
 		p[1] = tx | byte(r>>6)&maskx
 		p[2] = tx | byte(r)&maskx
 		return 3
 	default:
+		_ = p[3] // eliminate bounds checks
 		p[0] = t4 | byte(r>>18)
 		p[1] = tx | byte(r>>12)&maskx
 		p[2] = tx | byte(r>>6)&maskx
@@ -513,12 +516,10 @@ func ValidString(s string) bool {
 // Code points that are out of range or a surrogate half are illegal.
 func ValidRune(r rune) bool {
 	switch {
-	case r < 0:
-		return false
-	case surrogateMin <= r && r <= surrogateMax:
-		return false
-	case r > MaxRune:
-		return false
+	case 0 <= r && r < surrogateMin:
+		return true
+	case surrogateMax < r && r <= MaxRune:
+		return true
 	}
-	return true
+	return false
 }
diff --git a/src/unicode/utf8/utf8_test.go b/src/unicode/utf8/utf8_test.go
index 51571b6..dc9c425 100644
--- a/src/unicode/utf8/utf8_test.go
+++ b/src/unicode/utf8/utf8_test.go
@@ -54,14 +54,18 @@ var utf8map = []Utf8Map{
 	{0x00ff, "\xc3\xbf"},
 	{0x0100, "\xc4\x80"},
 	{0x07ff, "\xdf\xbf"},
+	{0x0400, "\xd0\x80"},
 	{0x0800, "\xe0\xa0\x80"},
 	{0x0801, "\xe0\xa0\x81"},
+	{0x1000, "\xe1\x80\x80"},
+	{0xd000, "\xed\x80\x80"},
 	{0xd7ff, "\xed\x9f\xbf"}, // last code point before surrogate half.
 	{0xe000, "\xee\x80\x80"}, // first code point after surrogate half.
 	{0xfffe, "\xef\xbf\xbe"},
 	{0xffff, "\xef\xbf\xbf"},
 	{0x10000, "\xf0\x90\x80\x80"},
 	{0x10001, "\xf0\x90\x80\x81"},
+	{0x40000, "\xf1\x80\x80\x80"},
 	{0x10fffe, "\xf4\x8f\xbf\xbe"},
 	{0x10ffff, "\xf4\x8f\xbf\xbf"},
 	{0xFFFD, "\xef\xbf\xbd"},
@@ -228,6 +232,93 @@ func TestIntConversion(t *testing.T) {
 	}
 }
 
+var invalidSequenceTests = []string{
+	"\xed\xa0\x80\x80", // surrogate min
+	"\xed\xbf\xbf\x80", // surrogate max
+
+	// xx
+	"\x91\x80\x80\x80",
+
+	// s1
+	"\xC2\x7F\x80\x80",
+	"\xC2\xC0\x80\x80",
+	"\xDF\x7F\x80\x80",
+	"\xDF\xC0\x80\x80",
+
+	// s2
+	"\xE0\x9F\xBF\x80",
+	"\xE0\xA0\x7F\x80",
+	"\xE0\xBF\xC0\x80",
+	"\xE0\xC0\x80\x80",
+
+	// s3
+	"\xE1\x7F\xBF\x80",
+	"\xE1\x80\x7F\x80",
+	"\xE1\xBF\xC0\x80",
+	"\xE1\xC0\x80\x80",
+
+	//s4
+	"\xED\x7F\xBF\x80",
+	"\xED\x80\x7F\x80",
+	"\xED\x9F\xC0\x80",
+	"\xED\xA0\x80\x80",
+
+	// s5
+	"\xF0\x8F\xBF\xBF",
+	"\xF0\x90\x7F\xBF",
+	"\xF0\x90\x80\x7F",
+	"\xF0\xBF\xBF\xC0",
+	"\xF0\xBF\xC0\x80",
+	"\xF0\xC0\x80\x80",
+
+	// s6
+	"\xF1\x7F\xBF\xBF",
+	"\xF1\x80\x7F\xBF",
+	"\xF1\x80\x80\x7F",
+	"\xF1\xBF\xBF\xC0",
+	"\xF1\xBF\xC0\x80",
+	"\xF1\xC0\x80\x80",
+
+	// s7
+	"\xF4\x7F\xBF\xBF",
+	"\xF4\x80\x7F\xBF",
+	"\xF4\x80\x80\x7F",
+	"\xF4\x8F\xBF\xC0",
+	"\xF4\x8F\xC0\x80",
+	"\xF4\x90\x80\x80",
+}
+
+func runtimeDecodeRune(s string) rune {
+	for _, r := range s {
+		return r
+	}
+	return -1
+}
+
+func TestDecodeInvalidSequence(t *testing.T) {
+	for _, s := range invalidSequenceTests {
+		r1, _ := DecodeRune([]byte(s))
+		if want := RuneError; r1 != want {
+			t.Errorf("DecodeRune(%#x) = %#04x, want %#04x", s, r1, want)
+			return
+		}
+		r2, _ := DecodeRuneInString(s)
+		if want := RuneError; r2 != want {
+			t.Errorf("DecodeRuneInString(%q) = %#04x, want %#04x", s, r2, want)
+			return
+		}
+		if r1 != r2 {
+			t.Errorf("DecodeRune(%#x) = %#04x mismatch with DecodeRuneInString(%q) = %#04x", s, r1, s, r2)
+			return
+		}
+		r3 := runtimeDecodeRune(s)
+		if r2 != r3 {
+			t.Errorf("DecodeRuneInString(%q) = %#04x mismatch with runtime.decoderune(%q) = %#04x", s, r2, s, r3)
+			return
+		}
+	}
+}
+
 func testSequence(t *testing.T, s string) {
 	type info struct {
 		index int
diff --git a/src/unsafe/unsafe.go b/src/unsafe/unsafe.go
index 8f43e72..859ca4f 100644
--- a/src/unsafe/unsafe.go
+++ b/src/unsafe/unsafe.go
@@ -76,8 +76,10 @@ type ArbitraryType int
 //	// equivalent to e := unsafe.Pointer(&x[i])
 //	e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))
 //
-// It is valid both to add and to subtract offsets from a pointer in this way,
-// but the result must continue to point into the original allocated object.
+// It is valid both to add and to subtract offsets from a pointer in this way.
+// It is also valid to use &^ to round pointers, usually for alignment.
+// In all cases, the result must continue to point into the original allocated object.
+//
 // Unlike in C, it is not valid to advance a pointer just beyond the end of
 // its original allocation:
 //
@@ -153,7 +155,7 @@ type ArbitraryType int
 //	var s string
 //	hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1
 //	hdr.Data = uintptr(unsafe.Pointer(p))              // case 6 (this case)
-//	hdr.Len = uintptr(n)
+//	hdr.Len = n
 //
 // In this usage hdr.Data is really an alternate way to refer to the underlying
 // pointer in the slice header, not a uintptr variable itself.
@@ -166,7 +168,7 @@ type ArbitraryType int
 //	// INVALID: a directly-declared header will not hold Data as a reference.
 //	var hdr reflect.StringHeader
 //	hdr.Data = uintptr(unsafe.Pointer(p))
-//	hdr.Len = uintptr(n)
+//	hdr.Len = n
 //	s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost
 //
 type Pointer *ArbitraryType
diff --git a/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305.go b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305.go
new file mode 100644
index 0000000..eb6739a
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305.go
@@ -0,0 +1,83 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD as specified in RFC 7539.
+package chacha20poly1305
+
+import (
+	"crypto/cipher"
+	"errors"
+)
+
+const (
+	// KeySize is the size of the key used by this AEAD, in bytes.
+	KeySize = 32
+	// NonceSize is the size of the nonce used with this AEAD, in bytes.
+	NonceSize = 12
+)
+
+type chacha20poly1305 struct {
+	key [32]byte
+}
+
+// New returns a ChaCha20-Poly1305 AEAD that uses the given, 256-bit key.
+func New(key []byte) (cipher.AEAD, error) {
+	if len(key) != KeySize {
+		return nil, errors.New("chacha20poly1305: bad key length")
+	}
+	ret := new(chacha20poly1305)
+	copy(ret.key[:], key)
+	return ret, nil
+}
+
+func (c *chacha20poly1305) NonceSize() int {
+	return NonceSize
+}
+
+func (c *chacha20poly1305) Overhead() int {
+	return 16
+}
+
+func (c *chacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
+	if len(nonce) != NonceSize {
+		panic("chacha20poly1305: bad nonce length passed to Seal")
+	}
+
+	if uint64(len(plaintext)) > (1<<38)-64 {
+		panic("chacha20poly1305: plaintext too large")
+	}
+
+	return c.seal(dst, nonce, plaintext, additionalData)
+}
+
+var errOpen = errors.New("chacha20poly1305: message authentication failed")
+
+func (c *chacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+	if len(nonce) != NonceSize {
+		panic("chacha20poly1305: bad nonce length passed to Open")
+	}
+	if len(ciphertext) < 16 {
+		return nil, errOpen
+	}
+	if uint64(len(ciphertext)) > (1<<38)-48 {
+		panic("chacha20poly1305: ciphertext too large")
+	}
+
+	return c.open(dst, nonce, ciphertext, additionalData)
+}
+
+// sliceForAppend takes a slice and a requested number of bytes. It returns a
+// slice with the contents of the given slice followed by that many bytes and a
+// second slice that aliases into it and contains only the extra bytes. If the
+// original slice has sufficient capacity then no allocation is performed.
+func sliceForAppend(in []byte, n int) (head, tail []byte) {
+	if total := len(in) + n; cap(in) >= total {
+		head = in[:total]
+	} else {
+		head = make([]byte, total)
+		copy(head, in)
+	}
+	tail = head[len(in):]
+	return
+}
diff --git a/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go
new file mode 100644
index 0000000..f0d3485
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go
@@ -0,0 +1,80 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,go1.7
+
+package chacha20poly1305
+
+import "encoding/binary"
+
+//go:noescape
+func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
+
+//go:noescape
+func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
+
+//go:noescape
+func haveSSSE3() bool
+
+var canUseASM bool
+
+func init() {
+	canUseASM = haveSSSE3()
+}
+
+// setupState writes a ChaCha20 input matrix to state. See
+// https://tools.ietf.org/html/rfc7539#section-2.3.
+func setupState(state *[16]uint32, key *[32]byte, nonce []byte) {
+	state[0] = 0x61707865
+	state[1] = 0x3320646e
+	state[2] = 0x79622d32
+	state[3] = 0x6b206574
+
+	state[4] = binary.LittleEndian.Uint32(key[:4])
+	state[5] = binary.LittleEndian.Uint32(key[4:8])
+	state[6] = binary.LittleEndian.Uint32(key[8:12])
+	state[7] = binary.LittleEndian.Uint32(key[12:16])
+	state[8] = binary.LittleEndian.Uint32(key[16:20])
+	state[9] = binary.LittleEndian.Uint32(key[20:24])
+	state[10] = binary.LittleEndian.Uint32(key[24:28])
+	state[11] = binary.LittleEndian.Uint32(key[28:32])
+
+	state[12] = 0
+	state[13] = binary.LittleEndian.Uint32(nonce[:4])
+	state[14] = binary.LittleEndian.Uint32(nonce[4:8])
+	state[15] = binary.LittleEndian.Uint32(nonce[8:12])
+}
+
+func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
+	if !canUseASM {
+		return c.sealGeneric(dst, nonce, plaintext, additionalData)
+	}
+
+	var state [16]uint32
+	setupState(&state, &c.key, nonce)
+
+	ret, out := sliceForAppend(dst, len(plaintext)+16)
+	chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
+	return ret
+}
+
+func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+	if !canUseASM {
+		return c.openGeneric(dst, nonce, ciphertext, additionalData)
+	}
+
+	var state [16]uint32
+	setupState(&state, &c.key, nonce)
+
+	ciphertext = ciphertext[:len(ciphertext)-16]
+	ret, out := sliceForAppend(dst, len(ciphertext))
+	if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
+		for i := range out {
+			out[i] = 0
+		}
+		return nil, errOpen
+	}
+
+	return ret, nil
+}
diff --git a/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s
new file mode 100644
index 0000000..ac95844
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s
@@ -0,0 +1,2707 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was originally from https://golang.org/cl/24717 by Vlad Krasnov of CloudFlare.
+
+// +build go1.7
+
+#include "textflag.h"
+// General register allocation
+#define oup DI
+#define inp SI
+#define inl BX
+#define adp CX // free to reuse, after we hash the additional data
+#define keyp R8 // free to reuse, when we copy the key to stack
+#define itr2 R9 // general iterator
+#define itr1 CX // general iterator
+#define acc0 R10
+#define acc1 R11
+#define acc2 R12
+#define t0 R13
+#define t1 R14
+#define t2 R15
+#define t3 R8
+// Register and stack allocation for the SSE code
+#define rStore (0*16)(BP)
+#define sStore (1*16)(BP)
+#define state1Store (2*16)(BP)
+#define state2Store (3*16)(BP)
+#define tmpStore (4*16)(BP)
+#define ctr0Store (5*16)(BP)
+#define ctr1Store (6*16)(BP)
+#define ctr2Store (7*16)(BP)
+#define ctr3Store (8*16)(BP)
+#define A0 X0
+#define A1 X1
+#define A2 X2
+#define B0 X3
+#define B1 X4
+#define B2 X5
+#define C0 X6
+#define C1 X7
+#define C2 X8
+#define D0 X9
+#define D1 X10
+#define D2 X11
+#define T0 X12
+#define T1 X13
+#define T2 X14
+#define T3 X15
+#define A3 T0
+#define B3 T1
+#define C3 T2
+#define D3 T3
+// Register and stack allocation for the AVX2 code
+#define rsStoreAVX2 (0*32)(BP)
+#define state1StoreAVX2 (1*32)(BP)
+#define state2StoreAVX2 (2*32)(BP)
+#define ctr0StoreAVX2 (3*32)(BP)
+#define ctr1StoreAVX2 (4*32)(BP)
+#define ctr2StoreAVX2 (5*32)(BP)
+#define ctr3StoreAVX2 (6*32)(BP)
+#define tmpStoreAVX2 (7*32)(BP) // 256 bytes on stack
+#define AA0 Y0
+#define AA1 Y5
+#define AA2 Y6
+#define AA3 Y7
+#define BB0 Y14
+#define BB1 Y9
+#define BB2 Y10
+#define BB3 Y11
+#define CC0 Y12
+#define CC1 Y13
+#define CC2 Y8
+#define CC3 Y15
+#define DD0 Y4
+#define DD1 Y1
+#define DD2 Y2
+#define DD3 Y3
+#define TT0 DD3
+#define TT1 AA3
+#define TT2 BB3
+#define TT3 CC3
+// ChaCha20 constants
+DATA ·chacha20Constants<>+0x00(SB)/4, $0x61707865
+DATA ·chacha20Constants<>+0x04(SB)/4, $0x3320646e
+DATA ·chacha20Constants<>+0x08(SB)/4, $0x79622d32
+DATA ·chacha20Constants<>+0x0c(SB)/4, $0x6b206574
+DATA ·chacha20Constants<>+0x10(SB)/4, $0x61707865
+DATA ·chacha20Constants<>+0x14(SB)/4, $0x3320646e
+DATA ·chacha20Constants<>+0x18(SB)/4, $0x79622d32
+DATA ·chacha20Constants<>+0x1c(SB)/4, $0x6b206574
+// <<< 16 with PSHUFB
+DATA ·rol16<>+0x00(SB)/8, $0x0504070601000302
+DATA ·rol16<>+0x08(SB)/8, $0x0D0C0F0E09080B0A
+DATA ·rol16<>+0x10(SB)/8, $0x0504070601000302
+DATA ·rol16<>+0x18(SB)/8, $0x0D0C0F0E09080B0A
+// <<< 8 with PSHUFB
+DATA ·rol8<>+0x00(SB)/8, $0x0605040702010003
+DATA ·rol8<>+0x08(SB)/8, $0x0E0D0C0F0A09080B
+DATA ·rol8<>+0x10(SB)/8, $0x0605040702010003
+DATA ·rol8<>+0x18(SB)/8, $0x0E0D0C0F0A09080B
+
+DATA ·avx2InitMask<>+0x00(SB)/8, $0x0
+DATA ·avx2InitMask<>+0x08(SB)/8, $0x0
+DATA ·avx2InitMask<>+0x10(SB)/8, $0x1
+DATA ·avx2InitMask<>+0x18(SB)/8, $0x0
+
+DATA ·avx2IncMask<>+0x00(SB)/8, $0x2
+DATA ·avx2IncMask<>+0x08(SB)/8, $0x0
+DATA ·avx2IncMask<>+0x10(SB)/8, $0x2
+DATA ·avx2IncMask<>+0x18(SB)/8, $0x0
+// Poly1305 key clamp
+DATA ·polyClampMask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
+DATA ·polyClampMask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
+DATA ·polyClampMask<>+0x10(SB)/8, $0xFFFFFFFFFFFFFFFF
+DATA ·polyClampMask<>+0x18(SB)/8, $0xFFFFFFFFFFFFFFFF
+
+DATA ·sseIncMask<>+0x00(SB)/8, $0x1
+DATA ·sseIncMask<>+0x08(SB)/8, $0x0
+// To load/store the last < 16 bytes in a buffer
+DATA ·andMask<>+0x00(SB)/8, $0x00000000000000ff
+DATA ·andMask<>+0x08(SB)/8, $0x0000000000000000
+DATA ·andMask<>+0x10(SB)/8, $0x000000000000ffff
+DATA ·andMask<>+0x18(SB)/8, $0x0000000000000000
+DATA ·andMask<>+0x20(SB)/8, $0x0000000000ffffff
+DATA ·andMask<>+0x28(SB)/8, $0x0000000000000000
+DATA ·andMask<>+0x30(SB)/8, $0x00000000ffffffff
+DATA ·andMask<>+0x38(SB)/8, $0x0000000000000000
+DATA ·andMask<>+0x40(SB)/8, $0x000000ffffffffff
+DATA ·andMask<>+0x48(SB)/8, $0x0000000000000000
+DATA ·andMask<>+0x50(SB)/8, $0x0000ffffffffffff
+DATA ·andMask<>+0x58(SB)/8, $0x0000000000000000
+DATA ·andMask<>+0x60(SB)/8, $0x00ffffffffffffff
+DATA ·andMask<>+0x68(SB)/8, $0x0000000000000000
+DATA ·andMask<>+0x70(SB)/8, $0xffffffffffffffff
+DATA ·andMask<>+0x78(SB)/8, $0x0000000000000000
+DATA ·andMask<>+0x80(SB)/8, $0xffffffffffffffff
+DATA ·andMask<>+0x88(SB)/8, $0x00000000000000ff
+DATA ·andMask<>+0x90(SB)/8, $0xffffffffffffffff
+DATA ·andMask<>+0x98(SB)/8, $0x000000000000ffff
+DATA ·andMask<>+0xa0(SB)/8, $0xffffffffffffffff
+DATA ·andMask<>+0xa8(SB)/8, $0x0000000000ffffff
+DATA ·andMask<>+0xb0(SB)/8, $0xffffffffffffffff
+DATA ·andMask<>+0xb8(SB)/8, $0x00000000ffffffff
+DATA ·andMask<>+0xc0(SB)/8, $0xffffffffffffffff
+DATA ·andMask<>+0xc8(SB)/8, $0x000000ffffffffff
+DATA ·andMask<>+0xd0(SB)/8, $0xffffffffffffffff
+DATA ·andMask<>+0xd8(SB)/8, $0x0000ffffffffffff
+DATA ·andMask<>+0xe0(SB)/8, $0xffffffffffffffff
+DATA ·andMask<>+0xe8(SB)/8, $0x00ffffffffffffff
+
+GLOBL ·chacha20Constants<>(SB), (NOPTR+RODATA), $32
+GLOBL ·rol16<>(SB), (NOPTR+RODATA), $32
+GLOBL ·rol8<>(SB), (NOPTR+RODATA), $32
+GLOBL ·sseIncMask<>(SB), (NOPTR+RODATA), $16
+GLOBL ·avx2IncMask<>(SB), (NOPTR+RODATA), $32
+GLOBL ·avx2InitMask<>(SB), (NOPTR+RODATA), $32
+GLOBL ·polyClampMask<>(SB), (NOPTR+RODATA), $32
+GLOBL ·andMask<>(SB), (NOPTR+RODATA), $240
+// No PALIGNR in Go ASM yet (but VPALIGNR is present).
+#define shiftB0Left BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x04 // PALIGNR $4, X3, X3
+#define shiftB1Left BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xe4; BYTE $0x04 // PALIGNR $4, X4, X4
+#define shiftB2Left BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xed; BYTE $0x04 // PALIGNR $4, X5, X5
+#define shiftB3Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xed; BYTE $0x04 // PALIGNR $4, X13, X13
+#define shiftC0Left BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xf6; BYTE $0x08 // PALIGNR $8, X6, X6
+#define shiftC1Left BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xff; BYTE $0x08 // PALIGNR $8, X7, X7
+#define shiftC2Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xc0; BYTE $0x08 // PALIGNR $8, X8, X8
+#define shiftC3Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xf6; BYTE $0x08 // PALIGNR $8, X14, X14
+#define shiftD0Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xc9; BYTE $0x0c // PALIGNR $12, X9, X9
+#define shiftD1Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xd2; BYTE $0x0c // PALIGNR $12, X10, X10
+#define shiftD2Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x0c // PALIGNR $12, X11, X11
+#define shiftD3Left BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xff; BYTE $0x0c // PALIGNR $12, X15, X15
+#define shiftB0Right BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x0c // PALIGNR $12, X3, X3
+#define shiftB1Right BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xe4; BYTE $0x0c // PALIGNR $12, X4, X4
+#define shiftB2Right BYTE $0x66; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xed; BYTE $0x0c // PALIGNR $12, X5, X5
+#define shiftB3Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xed; BYTE $0x0c // PALIGNR $12, X13, X13
+#define shiftC0Right shiftC0Left
+#define shiftC1Right shiftC1Left
+#define shiftC2Right shiftC2Left
+#define shiftC3Right shiftC3Left
+#define shiftD0Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xc9; BYTE $0x04 // PALIGNR $4, X9, X9
+#define shiftD1Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xd2; BYTE $0x04 // PALIGNR $4, X10, X10
+#define shiftD2Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x04 // PALIGNR $4, X11, X11
+#define shiftD3Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xff; BYTE $0x04 // PALIGNR $4, X15, X15
+// Some macros
+#define chachaQR(A, B, C, D, T) \
+	PADDD B, A; PXOR A, D; PSHUFB ·rol16<>(SB), D                            \
+	PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $12, T; PSRLL $20, B; PXOR T, B \
+	PADDD B, A; PXOR A, D; PSHUFB ·rol8<>(SB), D                             \
+	PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $7, T; PSRLL $25, B; PXOR T, B
+
+#define chachaQR_AVX2(A, B, C, D, T) \
+	VPADDD B, A, A; VPXOR A, D, D; VPSHUFB ·rol16<>(SB), D, D                         \
+	VPADDD D, C, C; VPXOR C, B, B; VPSLLD $12, B, T; VPSRLD $20, B, B; VPXOR T, B, B \
+	VPADDD B, A, A; VPXOR A, D, D; VPSHUFB ·rol8<>(SB), D, D                          \
+	VPADDD D, C, C; VPXOR C, B, B; VPSLLD $7, B, T; VPSRLD $25, B, B; VPXOR T, B, B
+
+#define polyAdd(S) ADDQ S, acc0; ADCQ 8+S, acc1; ADCQ $1, acc2
+#define polyMulStage1 MOVQ (0*8)(BP), AX; MOVQ AX, t2; MULQ acc0; MOVQ AX, t0; MOVQ DX, t1; MOVQ (0*8)(BP), AX; MULQ acc1; IMULQ acc2, t2; ADDQ AX, t1; ADCQ DX, t2
+#define polyMulStage2 MOVQ (1*8)(BP), AX; MOVQ AX, t3; MULQ acc0; ADDQ AX, t1; ADCQ $0, DX; MOVQ DX, acc0; MOVQ (1*8)(BP), AX; MULQ acc1; ADDQ AX, t2; ADCQ $0, DX
+#define polyMulStage3 IMULQ acc2, t3; ADDQ acc0, t2; ADCQ DX, t3
+#define polyMulReduceStage MOVQ t0, acc0; MOVQ t1, acc1; MOVQ t2, acc2; ANDQ $3, acc2; MOVQ t2, t0; ANDQ $-4, t0; MOVQ t3, t1; SHRQ $2, t2:t3; SHRQ $2, t3; ADDQ t0, acc0; ADCQ t1, acc1; ADCQ $0, acc2; ADDQ t2, acc0; ADCQ t3, acc1; ADCQ $0, acc2
+
+#define polyMulStage1_AVX2 MOVQ (0*8)(BP), DX; MOVQ DX, t2; MULXQ acc0, t0, t1; IMULQ acc2, t2; MULXQ acc1, AX, DX; ADDQ AX, t1; ADCQ DX, t2
+#define polyMulStage2_AVX2 MOVQ (1*8)(BP), DX; MULXQ acc0, acc0, AX; ADDQ acc0, t1; MULXQ acc1, acc1, t3; ADCQ acc1, t2; ADCQ $0, t3
+#define polyMulStage3_AVX2 IMULQ acc2, DX; ADDQ AX, t2; ADCQ DX, t3
+
+#define polyMul polyMulStage1; polyMulStage2; polyMulStage3; polyMulReduceStage
+#define polyMulAVX2 polyMulStage1_AVX2; polyMulStage2_AVX2; polyMulStage3_AVX2; polyMulReduceStage
+// ----------------------------------------------------------------------------
+TEXT polyHashADInternal(SB), NOSPLIT, $0
+	// adp points to beginning of additional data
+	// itr2 holds ad length
+	XORQ acc0, acc0
+	XORQ acc1, acc1
+	XORQ acc2, acc2
+	CMPQ itr2, $13
+	JNE  hashADLoop
+
+openFastTLSAD:
+	// Special treatment for the TLS case of 13 bytes
+	MOVQ (adp), acc0
+	MOVQ 5(adp), acc1
+	SHRQ $24, acc1
+	MOVQ $1, acc2
+	polyMul
+	RET
+
+hashADLoop:
+	// Hash in 16 byte chunks
+	CMPQ itr2, $16
+	JB   hashADTail
+	polyAdd(0(adp))
+	LEAQ (1*16)(adp), adp
+	SUBQ $16, itr2
+	polyMul
+	JMP  hashADLoop
+
+hashADTail:
+	CMPQ itr2, $0
+	JE   hashADDone
+
+	// Hash last < 16 byte tail
+	XORQ t0, t0
+	XORQ t1, t1
+	XORQ t2, t2
+	ADDQ itr2, adp
+
+hashADTailLoop:
+	SHLQ $8, t1:t0
+	SHLQ $8, t0
+	MOVB -1(adp), t2
+	XORQ t2, t0
+	DECQ adp
+	DECQ itr2
+	JNE  hashADTailLoop
+
+hashADTailFinish:
+	ADDQ t0, acc0; ADCQ t1, acc1; ADCQ $1, acc2
+	polyMul
+
+	// Finished AD
+hashADDone:
+	RET
+
+// ----------------------------------------------------------------------------
+// func chacha20Poly1305Open(dst, key, src, ad []byte) bool
+TEXT ·chacha20Poly1305Open(SB), 0, $288-97
+	// For aligned stack access
+	MOVQ SP, BP
+	ADDQ $32, BP
+	ANDQ $-32, BP
+	MOVQ dst+0(FP), oup
+	MOVQ key+24(FP), keyp
+	MOVQ src+48(FP), inp
+	MOVQ src_len+56(FP), inl
+	MOVQ ad+72(FP), adp
+
+	// Check for AVX2 support
+	CMPB runtime·support_avx2(SB), $1
+	JE   chacha20Poly1305Open_AVX2
+
+	// Special optimization, for very short buffers
+	CMPQ inl, $128
+	JBE  openSSE128 // About 16% faster
+
+	// For long buffers, prepare the poly key first
+	MOVOU ·chacha20Constants<>(SB), A0
+	MOVOU (1*16)(keyp), B0
+	MOVOU (2*16)(keyp), C0
+	MOVOU (3*16)(keyp), D0
+	MOVO  D0, T1
+
+	// Store state on stack for future use
+	MOVO B0, state1Store
+	MOVO C0, state2Store
+	MOVO D0, ctr3Store
+	MOVQ $10, itr2
+
+openSSEPreparePolyKey:
+	chachaQR(A0, B0, C0, D0, T0)
+	shiftB0Left;  shiftC0Left; shiftD0Left
+	chachaQR(A0, B0, C0, D0, T0)
+	shiftB0Right; shiftC0Right; shiftD0Right
+	DECQ          itr2
+	JNE           openSSEPreparePolyKey
+
+	// A0|B0 hold the Poly1305 32-byte key, C0,D0 can be discarded
+	PADDL ·chacha20Constants<>(SB), A0; PADDL state1Store, B0
+
+	// Clamp and store the key
+	PAND ·polyClampMask<>(SB), A0
+	MOVO A0, rStore; MOVO B0, sStore
+
+	// Hash AAD
+	MOVQ ad_len+80(FP), itr2
+	CALL polyHashADInternal(SB)
+
+openSSEMainLoop:
+	CMPQ inl, $256
+	JB   openSSEMainLoopDone
+
+	// Load state, increment counter blocks
+	MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0
+	MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1
+	MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2
+	MOVO A2, A3; MOVO B2, B3; MOVO C2, C3; MOVO D2, D3; PADDL ·sseIncMask<>(SB), D3
+
+	// Store counters
+	MOVO D0, ctr0Store; MOVO D1, ctr1Store; MOVO D2, ctr2Store; MOVO D3, ctr3Store
+
+	// There are 10 ChaCha20 iterations of 2QR each, so for 6 iterations we hash 2 blocks, and for the remaining 4 only 1 block - for a total of 16
+	MOVQ $4, itr1
+	MOVQ inp, itr2
+
+openSSEInternalLoop:
+	MOVO          C3, tmpStore
+	chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3)
+	MOVO          tmpStore, C3
+	MOVO          C1, tmpStore
+	chachaQR(A3, B3, C3, D3, C1)
+	MOVO          tmpStore, C1
+	polyAdd(0(itr2))
+	shiftB0Left;  shiftB1Left; shiftB2Left; shiftB3Left
+	shiftC0Left;  shiftC1Left; shiftC2Left; shiftC3Left
+	shiftD0Left;  shiftD1Left; shiftD2Left; shiftD3Left
+	polyMulStage1
+	polyMulStage2
+	LEAQ          (2*8)(itr2), itr2
+	MOVO          C3, tmpStore
+	chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3)
+	MOVO          tmpStore, C3
+	MOVO          C1, tmpStore
+	polyMulStage3
+	chachaQR(A3, B3, C3, D3, C1)
+	MOVO          tmpStore, C1
+	polyMulReduceStage
+	shiftB0Right; shiftB1Right; shiftB2Right; shiftB3Right
+	shiftC0Right; shiftC1Right; shiftC2Right; shiftC3Right
+	shiftD0Right; shiftD1Right; shiftD2Right; shiftD3Right
+	DECQ          itr1
+	JGE           openSSEInternalLoop
+
+	polyAdd(0(itr2))
+	polyMul
+	LEAQ (2*8)(itr2), itr2
+
+	CMPQ itr1, $-6
+	JG   openSSEInternalLoop
+
+	// Add in the state
+	PADDD ·chacha20Constants<>(SB), A0; PADDD ·chacha20Constants<>(SB), A1; PADDD ·chacha20Constants<>(SB), A2; PADDD ·chacha20Constants<>(SB), A3
+	PADDD state1Store, B0; PADDD state1Store, B1; PADDD state1Store, B2; PADDD state1Store, B3
+	PADDD state2Store, C0; PADDD state2Store, C1; PADDD state2Store, C2; PADDD state2Store, C3
+	PADDD ctr0Store, D0; PADDD ctr1Store, D1; PADDD ctr2Store, D2; PADDD ctr3Store, D3
+
+	// Load - xor - store
+	MOVO  D3, tmpStore
+	MOVOU (0*16)(inp), D3; PXOR D3, A0; MOVOU A0, (0*16)(oup)
+	MOVOU (1*16)(inp), D3; PXOR D3, B0; MOVOU B0, (1*16)(oup)
+	MOVOU (2*16)(inp), D3; PXOR D3, C0; MOVOU C0, (2*16)(oup)
+	MOVOU (3*16)(inp), D3; PXOR D3, D0; MOVOU D0, (3*16)(oup)
+	MOVOU (4*16)(inp), D0; PXOR D0, A1; MOVOU A1, (4*16)(oup)
+	MOVOU (5*16)(inp), D0; PXOR D0, B1; MOVOU B1, (5*16)(oup)
+	MOVOU (6*16)(inp), D0; PXOR D0, C1; MOVOU C1, (6*16)(oup)
+	MOVOU (7*16)(inp), D0; PXOR D0, D1; MOVOU D1, (7*16)(oup)
+	MOVOU (8*16)(inp), D0; PXOR D0, A2; MOVOU A2, (8*16)(oup)
+	MOVOU (9*16)(inp), D0; PXOR D0, B2; MOVOU B2, (9*16)(oup)
+	MOVOU (10*16)(inp), D0; PXOR D0, C2; MOVOU C2, (10*16)(oup)
+	MOVOU (11*16)(inp), D0; PXOR D0, D2; MOVOU D2, (11*16)(oup)
+	MOVOU (12*16)(inp), D0; PXOR D0, A3; MOVOU A3, (12*16)(oup)
+	MOVOU (13*16)(inp), D0; PXOR D0, B3; MOVOU B3, (13*16)(oup)
+	MOVOU (14*16)(inp), D0; PXOR D0, C3; MOVOU C3, (14*16)(oup)
+	MOVOU (15*16)(inp), D0; PXOR tmpStore, D0; MOVOU D0, (15*16)(oup)
+	LEAQ  256(inp), inp
+	LEAQ  256(oup), oup
+	SUBQ  $256, inl
+	JMP   openSSEMainLoop
+
+openSSEMainLoopDone:
+	// Handle the various tail sizes efficiently
+	TESTQ inl, inl
+	JE    openSSEFinalize
+	CMPQ  inl, $64
+	JBE   openSSETail64
+	CMPQ  inl, $128
+	JBE   openSSETail128
+	CMPQ  inl, $192
+	JBE   openSSETail192
+	JMP   openSSETail256
+
+openSSEFinalize:
+	// Hash in the PT, AAD lengths
+	ADDQ ad_len+80(FP), acc0; ADCQ src_len+56(FP), acc1; ADCQ $1, acc2
+	polyMul
+
+	// Final reduce
+	MOVQ    acc0, t0
+	MOVQ    acc1, t1
+	MOVQ    acc2, t2
+	SUBQ    $-5, acc0
+	SBBQ    $-1, acc1
+	SBBQ    $3, acc2
+	CMOVQCS t0, acc0
+	CMOVQCS t1, acc1
+	CMOVQCS t2, acc2
+
+	// Add in the "s" part of the key
+	ADDQ 0+sStore, acc0
+	ADCQ 8+sStore, acc1
+
+	// Finally, constant time compare to the tag at the end of the message
+	XORQ    AX, AX
+	MOVQ    $1, DX
+	XORQ    (0*8)(inp), acc0
+	XORQ    (1*8)(inp), acc1
+	ORQ     acc1, acc0
+	CMOVQEQ DX, AX
+
+	// Return true iff tags are equal
+	MOVB AX, ret+96(FP)
+	RET
+
+// ----------------------------------------------------------------------------
+// Special optimization for buffers smaller than 129 bytes
+openSSE128:
+	// For up to 128 bytes of ciphertext and 64 bytes for the poly key, we require to process three blocks
+	MOVOU ·chacha20Constants<>(SB), A0; MOVOU (1*16)(keyp), B0; MOVOU (2*16)(keyp), C0; MOVOU (3*16)(keyp), D0
+	MOVO  A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1
+	MOVO  A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2
+	MOVO  B0, T1; MOVO C0, T2; MOVO D1, T3
+	MOVQ  $10, itr2
+
+openSSE128InnerCipherLoop:
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0)
+	shiftB0Left;  shiftB1Left; shiftB2Left
+	shiftC0Left;  shiftC1Left; shiftC2Left
+	shiftD0Left;  shiftD1Left; shiftD2Left
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0)
+	shiftB0Right; shiftB1Right; shiftB2Right
+	shiftC0Right; shiftC1Right; shiftC2Right
+	shiftD0Right; shiftD1Right; shiftD2Right
+	DECQ          itr2
+	JNE           openSSE128InnerCipherLoop
+
+	// A0|B0 hold the Poly1305 32-byte key, C0,D0 can be discarded
+	PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1; PADDL ·chacha20Constants<>(SB), A2
+	PADDL T1, B0; PADDL T1, B1; PADDL T1, B2
+	PADDL T2, C1; PADDL T2, C2
+	PADDL T3, D1; PADDL ·sseIncMask<>(SB), T3; PADDL T3, D2
+
+	// Clamp and store the key
+	PAND  ·polyClampMask<>(SB), A0
+	MOVOU A0, rStore; MOVOU B0, sStore
+
+	// Hash
+	MOVQ ad_len+80(FP), itr2
+	CALL polyHashADInternal(SB)
+
+openSSE128Open:
+	CMPQ inl, $16
+	JB   openSSETail16
+	SUBQ $16, inl
+
+	// Load for hashing
+	polyAdd(0(inp))
+
+	// Load for decryption
+	MOVOU (inp), T0; PXOR T0, A1; MOVOU A1, (oup)
+	LEAQ  (1*16)(inp), inp
+	LEAQ  (1*16)(oup), oup
+	polyMul
+
+	// Shift the stream "left"
+	MOVO B1, A1
+	MOVO C1, B1
+	MOVO D1, C1
+	MOVO A2, D1
+	MOVO B2, A2
+	MOVO C2, B2
+	MOVO D2, C2
+	JMP  openSSE128Open
+
+openSSETail16:
+	TESTQ inl, inl
+	JE    openSSEFinalize
+
+	// We can safely load the CT from the end, because it is padded with the MAC
+	MOVQ   inl, itr2
+	SHLQ   $4, itr2
+	LEAQ   ·andMask<>(SB), t0
+	MOVOU  (inp), T0
+	ADDQ   inl, inp
+	PAND   -16(t0)(itr2*1), T0
+	MOVO   T0, 0+tmpStore
+	MOVQ   T0, t0
+	MOVQ   8+tmpStore, t1
+	PXOR   A1, T0
+
+	// We can only store one byte at a time, since plaintext can be shorter than 16 bytes
+openSSETail16Store:
+	MOVQ T0, t3
+	MOVB t3, (oup)
+	PSRLDQ $1, T0
+	INCQ   oup
+	DECQ   inl
+	JNE    openSSETail16Store
+	ADDQ   t0, acc0; ADCQ t1, acc1; ADCQ $1, acc2
+	polyMul
+	JMP    openSSEFinalize
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 64 bytes of ciphertext
+openSSETail64:
+	// Need to decrypt up to 64 bytes - prepare single block
+	MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0; MOVO D0, ctr0Store
+	XORQ itr2, itr2
+	MOVQ inl, itr1
+	CMPQ itr1, $16
+	JB   openSSETail64LoopB
+
+openSSETail64LoopA:
+	// Perform ChaCha rounds, while hashing the remaining input
+	polyAdd(0(inp)(itr2*1))
+	polyMul
+	SUBQ $16, itr1
+
+openSSETail64LoopB:
+	ADDQ          $16, itr2
+	chachaQR(A0, B0, C0, D0, T0)
+	shiftB0Left;  shiftC0Left; shiftD0Left
+	chachaQR(A0, B0, C0, D0, T0)
+	shiftB0Right; shiftC0Right; shiftD0Right
+
+	CMPQ itr1, $16
+	JAE  openSSETail64LoopA
+
+	CMPQ itr2, $160
+	JNE  openSSETail64LoopB
+
+	PADDL ·chacha20Constants<>(SB), A0; PADDL state1Store, B0; PADDL state2Store, C0; PADDL ctr0Store, D0
+
+openSSETail64DecLoop:
+	CMPQ  inl, $16
+	JB    openSSETail64DecLoopDone
+	SUBQ  $16, inl
+	MOVOU (inp), T0
+	PXOR  T0, A0
+	MOVOU A0, (oup)
+	LEAQ  16(inp), inp
+	LEAQ  16(oup), oup
+	MOVO  B0, A0
+	MOVO  C0, B0
+	MOVO  D0, C0
+	JMP   openSSETail64DecLoop
+
+openSSETail64DecLoopDone:
+	MOVO A0, A1
+	JMP  openSSETail16
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 128 bytes of ciphertext
+openSSETail128:
+	// Need to decrypt up to 128 bytes - prepare two blocks
+	MOVO ·chacha20Constants<>(SB), A1; MOVO state1Store, B1; MOVO state2Store, C1; MOVO ctr3Store, D1; PADDL ·sseIncMask<>(SB), D1; MOVO D1, ctr0Store
+	MOVO A1, A0; MOVO B1, B0; MOVO C1, C0; MOVO D1, D0; PADDL ·sseIncMask<>(SB), D0; MOVO D0, ctr1Store
+	XORQ itr2, itr2
+	MOVQ inl, itr1
+	ANDQ $-16, itr1
+
+openSSETail128LoopA:
+	// Perform ChaCha rounds, while hashing the remaining input
+	polyAdd(0(inp)(itr2*1))
+	polyMul
+
+openSSETail128LoopB:
+	ADDQ          $16, itr2
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0)
+	shiftB0Left;  shiftC0Left; shiftD0Left
+	shiftB1Left;  shiftC1Left; shiftD1Left
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0)
+	shiftB0Right; shiftC0Right; shiftD0Right
+	shiftB1Right; shiftC1Right; shiftD1Right
+
+	CMPQ itr2, itr1
+	JB   openSSETail128LoopA
+
+	CMPQ itr2, $160
+	JNE  openSSETail128LoopB
+
+	PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1
+	PADDL state1Store, B0; PADDL state1Store, B1
+	PADDL state2Store, C0; PADDL state2Store, C1
+	PADDL ctr1Store, D0; PADDL ctr0Store, D1
+
+	MOVOU (0*16)(inp), T0; MOVOU (1*16)(inp), T1; MOVOU (2*16)(inp), T2; MOVOU (3*16)(inp), T3
+	PXOR  T0, A1; PXOR T1, B1; PXOR T2, C1; PXOR T3, D1
+	MOVOU A1, (0*16)(oup); MOVOU B1, (1*16)(oup); MOVOU C1, (2*16)(oup); MOVOU D1, (3*16)(oup)
+
+	SUBQ $64, inl
+	LEAQ 64(inp), inp
+	LEAQ 64(oup), oup
+	JMP  openSSETail64DecLoop
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 192 bytes of ciphertext
+openSSETail192:
+	// Need to decrypt up to 192 bytes - prepare three blocks
+	MOVO ·chacha20Constants<>(SB), A2; MOVO state1Store, B2; MOVO state2Store, C2; MOVO ctr3Store, D2; PADDL ·sseIncMask<>(SB), D2; MOVO D2, ctr0Store
+	MOVO A2, A1; MOVO B2, B1; MOVO C2, C1; MOVO D2, D1; PADDL ·sseIncMask<>(SB), D1; MOVO D1, ctr1Store
+	MOVO A1, A0; MOVO B1, B0; MOVO C1, C0; MOVO D1, D0; PADDL ·sseIncMask<>(SB), D0; MOVO D0, ctr2Store
+
+	MOVQ    inl, itr1
+	MOVQ    $160, itr2
+	CMPQ    itr1, $160
+	CMOVQGT itr2, itr1
+	ANDQ    $-16, itr1
+	XORQ    itr2, itr2
+
+openSSLTail192LoopA:
+	// Perform ChaCha rounds, while hashing the remaining input
+	polyAdd(0(inp)(itr2*1))
+	polyMul
+
+openSSLTail192LoopB:
+	ADDQ         $16, itr2
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0)
+	shiftB0Left; shiftC0Left; shiftD0Left
+	shiftB1Left; shiftC1Left; shiftD1Left
+	shiftB2Left; shiftC2Left; shiftD2Left
+
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0)
+	shiftB0Right; shiftC0Right; shiftD0Right
+	shiftB1Right; shiftC1Right; shiftD1Right
+	shiftB2Right; shiftC2Right; shiftD2Right
+
+	CMPQ itr2, itr1
+	JB   openSSLTail192LoopA
+
+	CMPQ itr2, $160
+	JNE  openSSLTail192LoopB
+
+	CMPQ inl, $176
+	JB   openSSLTail192Store
+
+	polyAdd(160(inp))
+	polyMul
+
+	CMPQ inl, $192
+	JB   openSSLTail192Store
+
+	polyAdd(176(inp))
+	polyMul
+
+openSSLTail192Store:
+	PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1; PADDL ·chacha20Constants<>(SB), A2
+	PADDL state1Store, B0; PADDL state1Store, B1; PADDL state1Store, B2
+	PADDL state2Store, C0; PADDL state2Store, C1; PADDL state2Store, C2
+	PADDL ctr2Store, D0; PADDL ctr1Store, D1; PADDL ctr0Store, D2
+
+	MOVOU (0*16)(inp), T0; MOVOU (1*16)(inp), T1; MOVOU (2*16)(inp), T2; MOVOU (3*16)(inp), T3
+	PXOR  T0, A2; PXOR T1, B2; PXOR T2, C2; PXOR T3, D2
+	MOVOU A2, (0*16)(oup); MOVOU B2, (1*16)(oup); MOVOU C2, (2*16)(oup); MOVOU D2, (3*16)(oup)
+
+	MOVOU (4*16)(inp), T0; MOVOU (5*16)(inp), T1; MOVOU (6*16)(inp), T2; MOVOU (7*16)(inp), T3
+	PXOR  T0, A1; PXOR T1, B1; PXOR T2, C1; PXOR T3, D1
+	MOVOU A1, (4*16)(oup); MOVOU B1, (5*16)(oup); MOVOU C1, (6*16)(oup); MOVOU D1, (7*16)(oup)
+
+	SUBQ $128, inl
+	LEAQ 128(inp), inp
+	LEAQ 128(oup), oup
+	JMP  openSSETail64DecLoop
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 256 bytes of ciphertext
+openSSETail256:
+	// Need to decrypt up to 256 bytes - prepare four blocks
+	MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0
+	MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1
+	MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2
+	MOVO A2, A3; MOVO B2, B3; MOVO C2, C3; MOVO D2, D3; PADDL ·sseIncMask<>(SB), D3
+
+	// Store counters
+	MOVO D0, ctr0Store; MOVO D1, ctr1Store; MOVO D2, ctr2Store; MOVO D3, ctr3Store
+	XORQ itr2, itr2
+
+openSSETail256Loop:
+	// This loop inteleaves 8 ChaCha quarter rounds with 1 poly multiplication
+	polyAdd(0(inp)(itr2*1))
+	MOVO          C3, tmpStore
+	chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3)
+	MOVO          tmpStore, C3
+	MOVO          C1, tmpStore
+	chachaQR(A3, B3, C3, D3, C1)
+	MOVO          tmpStore, C1
+	shiftB0Left;  shiftB1Left; shiftB2Left; shiftB3Left
+	shiftC0Left;  shiftC1Left; shiftC2Left; shiftC3Left
+	shiftD0Left;  shiftD1Left; shiftD2Left; shiftD3Left
+	polyMulStage1
+	polyMulStage2
+	MOVO          C3, tmpStore
+	chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3)
+	MOVO          tmpStore, C3
+	MOVO          C1, tmpStore
+	chachaQR(A3, B3, C3, D3, C1)
+	MOVO          tmpStore, C1
+	polyMulStage3
+	polyMulReduceStage
+	shiftB0Right; shiftB1Right; shiftB2Right; shiftB3Right
+	shiftC0Right; shiftC1Right; shiftC2Right; shiftC3Right
+	shiftD0Right; shiftD1Right; shiftD2Right; shiftD3Right
+	ADDQ          $2*8, itr2
+	CMPQ          itr2, $160
+	JB            openSSETail256Loop
+	MOVQ          inl, itr1
+	ANDQ          $-16, itr1
+
+openSSETail256HashLoop:
+	polyAdd(0(inp)(itr2*1))
+	polyMul
+	ADDQ $2*8, itr2
+	CMPQ itr2, itr1
+	JB   openSSETail256HashLoop
+
+	// Add in the state
+	PADDD ·chacha20Constants<>(SB), A0; PADDD ·chacha20Constants<>(SB), A1; PADDD ·chacha20Constants<>(SB), A2; PADDD ·chacha20Constants<>(SB), A3
+	PADDD state1Store, B0; PADDD state1Store, B1; PADDD state1Store, B2; PADDD state1Store, B3
+	PADDD state2Store, C0; PADDD state2Store, C1; PADDD state2Store, C2; PADDD state2Store, C3
+	PADDD ctr0Store, D0; PADDD ctr1Store, D1; PADDD ctr2Store, D2; PADDD ctr3Store, D3
+	MOVO  D3, tmpStore
+
+	// Load - xor - store
+	MOVOU (0*16)(inp), D3; PXOR D3, A0
+	MOVOU (1*16)(inp), D3; PXOR D3, B0
+	MOVOU (2*16)(inp), D3; PXOR D3, C0
+	MOVOU (3*16)(inp), D3; PXOR D3, D0
+	MOVOU A0, (0*16)(oup)
+	MOVOU B0, (1*16)(oup)
+	MOVOU C0, (2*16)(oup)
+	MOVOU D0, (3*16)(oup)
+	MOVOU (4*16)(inp), A0; MOVOU (5*16)(inp), B0; MOVOU (6*16)(inp), C0; MOVOU (7*16)(inp), D0
+	PXOR  A0, A1; PXOR B0, B1; PXOR C0, C1; PXOR D0, D1
+	MOVOU A1, (4*16)(oup); MOVOU B1, (5*16)(oup); MOVOU C1, (6*16)(oup); MOVOU D1, (7*16)(oup)
+	MOVOU (8*16)(inp), A0; MOVOU (9*16)(inp), B0; MOVOU (10*16)(inp), C0; MOVOU (11*16)(inp), D0
+	PXOR  A0, A2; PXOR B0, B2; PXOR C0, C2; PXOR D0, D2
+	MOVOU A2, (8*16)(oup); MOVOU B2, (9*16)(oup); MOVOU C2, (10*16)(oup); MOVOU D2, (11*16)(oup)
+	LEAQ  192(inp), inp
+	LEAQ  192(oup), oup
+	SUBQ  $192, inl
+	MOVO  A3, A0
+	MOVO  B3, B0
+	MOVO  C3, C0
+	MOVO  tmpStore, D0
+
+	JMP openSSETail64DecLoop
+
+// ----------------------------------------------------------------------------
+// ------------------------- AVX2 Code ----------------------------------------
+chacha20Poly1305Open_AVX2:
+	VZEROUPPER
+	VMOVDQU ·chacha20Constants<>(SB), AA0
+	BYTE    $0xc4; BYTE $0x42; BYTE $0x7d; BYTE $0x5a; BYTE $0x70; BYTE $0x10 // broadcasti128 16(r8), ymm14
+	BYTE    $0xc4; BYTE $0x42; BYTE $0x7d; BYTE $0x5a; BYTE $0x60; BYTE $0x20 // broadcasti128 32(r8), ymm12
+	BYTE    $0xc4; BYTE $0xc2; BYTE $0x7d; BYTE $0x5a; BYTE $0x60; BYTE $0x30 // broadcasti128 48(r8), ymm4
+	VPADDD  ·avx2InitMask<>(SB), DD0, DD0
+
+	// Special optimization, for very short buffers
+	CMPQ inl, $192
+	JBE  openAVX2192
+	CMPQ inl, $320
+	JBE  openAVX2320
+
+	// For the general key prepare the key first - as a byproduct we have 64 bytes of cipher stream
+	VMOVDQA BB0, state1StoreAVX2
+	VMOVDQA CC0, state2StoreAVX2
+	VMOVDQA DD0, ctr3StoreAVX2
+	MOVQ    $10, itr2
+
+openAVX2PreparePolyKey:
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0)
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $12, DD0, DD0, DD0
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0)
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $4, DD0, DD0, DD0
+	DECQ     itr2
+	JNE      openAVX2PreparePolyKey
+
+	VPADDD ·chacha20Constants<>(SB), AA0, AA0
+	VPADDD state1StoreAVX2, BB0, BB0
+	VPADDD state2StoreAVX2, CC0, CC0
+	VPADDD ctr3StoreAVX2, DD0, DD0
+
+	VPERM2I128 $0x02, AA0, BB0, TT0
+
+	// Clamp and store poly key
+	VPAND   ·polyClampMask<>(SB), TT0, TT0
+	VMOVDQA TT0, rsStoreAVX2
+
+	// Stream for the first 64 bytes
+	VPERM2I128 $0x13, AA0, BB0, AA0
+	VPERM2I128 $0x13, CC0, DD0, BB0
+
+	// Hash AD + first 64 bytes
+	MOVQ ad_len+80(FP), itr2
+	CALL polyHashADInternal(SB)
+	XORQ itr1, itr1
+
+openAVX2InitialHash64:
+	polyAdd(0(inp)(itr1*1))
+	polyMulAVX2
+	ADDQ $16, itr1
+	CMPQ itr1, $64
+	JNE  openAVX2InitialHash64
+
+	// Decrypt the first 64 bytes
+	VPXOR   (0*32)(inp), AA0, AA0
+	VPXOR   (1*32)(inp), BB0, BB0
+	VMOVDQU AA0, (0*32)(oup)
+	VMOVDQU BB0, (1*32)(oup)
+	LEAQ    (2*32)(inp), inp
+	LEAQ    (2*32)(oup), oup
+	SUBQ    $64, inl
+
+openAVX2MainLoop:
+	CMPQ inl, $512
+	JB   openAVX2MainLoopDone
+
+	// Load state, increment counter blocks, store the incremented counters
+	VMOVDQU ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3
+	VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3
+	VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3
+	VMOVDQA ctr3StoreAVX2, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2; VPADDD ·avx2IncMask<>(SB), DD2, DD3
+	VMOVDQA DD0, ctr0StoreAVX2; VMOVDQA DD1, ctr1StoreAVX2; VMOVDQA DD2, ctr2StoreAVX2; VMOVDQA DD3, ctr3StoreAVX2
+	XORQ    itr1, itr1
+
+openAVX2InternalLoop:
+	// Lets just say this spaghetti loop interleaves 2 quarter rounds with 3 poly multiplications
+	// Effectively per 512 bytes of stream we hash 480 bytes of ciphertext
+	polyAdd(0*8(inp)(itr1*1))
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	polyMulStage1_AVX2
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3
+	polyMulStage2_AVX2
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	polyMulStage3_AVX2
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	polyMulReduceStage
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3
+	polyAdd(2*8(inp)(itr1*1))
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	polyMulStage1_AVX2
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	polyMulStage2_AVX2
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $4, BB3, BB3, BB3
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3
+	VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2; VPALIGNR $12, DD3, DD3, DD3
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	polyMulStage3_AVX2
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3
+	polyMulReduceStage
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	polyAdd(4*8(inp)(itr1*1))
+	LEAQ     (6*8)(itr1), itr1
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	polyMulStage1_AVX2
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	polyMulStage2_AVX2
+	VPSHUFB  ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	polyMulStage3_AVX2
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	polyMulReduceStage
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $12, BB3, BB3, BB3
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3
+	VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2; VPALIGNR $4, DD3, DD3, DD3
+	CMPQ     itr1, $480
+	JNE      openAVX2InternalLoop
+
+	VPADDD  ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2; VPADDD ·chacha20Constants<>(SB), AA3, AA3
+	VPADDD  state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2; VPADDD state1StoreAVX2, BB3, BB3
+	VPADDD  state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2; VPADDD state2StoreAVX2, CC3, CC3
+	VPADDD  ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2; VPADDD ctr3StoreAVX2, DD3, DD3
+	VMOVDQA CC3, tmpStoreAVX2
+
+	// We only hashed 480 of the 512 bytes available - hash the remaining 32 here
+	polyAdd(480(inp))
+	polyMulAVX2
+	VPERM2I128 $0x02, AA0, BB0, CC3; VPERM2I128 $0x13, AA0, BB0, BB0; VPERM2I128 $0x02, CC0, DD0, AA0; VPERM2I128 $0x13, CC0, DD0, CC0
+	VPXOR      (0*32)(inp), CC3, CC3; VPXOR (1*32)(inp), AA0, AA0; VPXOR (2*32)(inp), BB0, BB0; VPXOR (3*32)(inp), CC0, CC0
+	VMOVDQU    CC3, (0*32)(oup); VMOVDQU AA0, (1*32)(oup); VMOVDQU BB0, (2*32)(oup); VMOVDQU CC0, (3*32)(oup)
+	VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0
+	VPXOR      (4*32)(inp), AA0, AA0; VPXOR (5*32)(inp), BB0, BB0; VPXOR (6*32)(inp), CC0, CC0; VPXOR (7*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (4*32)(oup); VMOVDQU BB0, (5*32)(oup); VMOVDQU CC0, (6*32)(oup); VMOVDQU DD0, (7*32)(oup)
+
+	// and here
+	polyAdd(496(inp))
+	polyMulAVX2
+	VPERM2I128 $0x02, AA2, BB2, AA0; VPERM2I128 $0x02, CC2, DD2, BB0; VPERM2I128 $0x13, AA2, BB2, CC0; VPERM2I128 $0x13, CC2, DD2, DD0
+	VPXOR      (8*32)(inp), AA0, AA0; VPXOR (9*32)(inp), BB0, BB0; VPXOR (10*32)(inp), CC0, CC0; VPXOR (11*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (8*32)(oup); VMOVDQU BB0, (9*32)(oup); VMOVDQU CC0, (10*32)(oup); VMOVDQU DD0, (11*32)(oup)
+	VPERM2I128 $0x02, AA3, BB3, AA0; VPERM2I128 $0x02, tmpStoreAVX2, DD3, BB0; VPERM2I128 $0x13, AA3, BB3, CC0; VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0
+	VPXOR      (12*32)(inp), AA0, AA0; VPXOR (13*32)(inp), BB0, BB0; VPXOR (14*32)(inp), CC0, CC0; VPXOR (15*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (12*32)(oup); VMOVDQU BB0, (13*32)(oup); VMOVDQU CC0, (14*32)(oup); VMOVDQU DD0, (15*32)(oup)
+	LEAQ       (32*16)(inp), inp
+	LEAQ       (32*16)(oup), oup
+	SUBQ       $(32*16), inl
+	JMP        openAVX2MainLoop
+
+openAVX2MainLoopDone:
+	// Handle the various tail sizes efficiently
+	TESTQ inl, inl
+	JE    openSSEFinalize
+	CMPQ  inl, $128
+	JBE   openAVX2Tail128
+	CMPQ  inl, $256
+	JBE   openAVX2Tail256
+	CMPQ  inl, $384
+	JBE   openAVX2Tail384
+	JMP   openAVX2Tail512
+
+// ----------------------------------------------------------------------------
+// Special optimization for buffers smaller than 193 bytes
+openAVX2192:
+	// For up to 192 bytes of ciphertext and 64 bytes for the poly key, we process four blocks
+	VMOVDQA AA0, AA1
+	VMOVDQA BB0, BB1
+	VMOVDQA CC0, CC1
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD1
+	VMOVDQA AA0, AA2
+	VMOVDQA BB0, BB2
+	VMOVDQA CC0, CC2
+	VMOVDQA DD0, DD2
+	VMOVDQA DD1, TT3
+	MOVQ    $10, itr2
+
+openAVX2192InnerCipherLoop:
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0)
+	VPALIGNR   $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1
+	VPALIGNR   $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1
+	VPALIGNR   $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0)
+	VPALIGNR   $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1
+	VPALIGNR   $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1
+	VPALIGNR   $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1
+	DECQ       itr2
+	JNE        openAVX2192InnerCipherLoop
+	VPADDD     AA2, AA0, AA0; VPADDD AA2, AA1, AA1
+	VPADDD     BB2, BB0, BB0; VPADDD BB2, BB1, BB1
+	VPADDD     CC2, CC0, CC0; VPADDD CC2, CC1, CC1
+	VPADDD     DD2, DD0, DD0; VPADDD TT3, DD1, DD1
+	VPERM2I128 $0x02, AA0, BB0, TT0
+
+	// Clamp and store poly key
+	VPAND   ·polyClampMask<>(SB), TT0, TT0
+	VMOVDQA TT0, rsStoreAVX2
+
+	// Stream for up to 192 bytes
+	VPERM2I128 $0x13, AA0, BB0, AA0
+	VPERM2I128 $0x13, CC0, DD0, BB0
+	VPERM2I128 $0x02, AA1, BB1, CC0
+	VPERM2I128 $0x02, CC1, DD1, DD0
+	VPERM2I128 $0x13, AA1, BB1, AA1
+	VPERM2I128 $0x13, CC1, DD1, BB1
+
+openAVX2ShortOpen:
+	// Hash
+	MOVQ ad_len+80(FP), itr2
+	CALL polyHashADInternal(SB)
+
+openAVX2ShortOpenLoop:
+	CMPQ inl, $32
+	JB   openAVX2ShortTail32
+	SUBQ $32, inl
+
+	// Load for hashing
+	polyAdd(0*8(inp))
+	polyMulAVX2
+	polyAdd(2*8(inp))
+	polyMulAVX2
+
+	// Load for decryption
+	VPXOR   (inp), AA0, AA0
+	VMOVDQU AA0, (oup)
+	LEAQ    (1*32)(inp), inp
+	LEAQ    (1*32)(oup), oup
+
+	// Shift stream left
+	VMOVDQA BB0, AA0
+	VMOVDQA CC0, BB0
+	VMOVDQA DD0, CC0
+	VMOVDQA AA1, DD0
+	VMOVDQA BB1, AA1
+	VMOVDQA CC1, BB1
+	VMOVDQA DD1, CC1
+	VMOVDQA AA2, DD1
+	VMOVDQA BB2, AA2
+	JMP     openAVX2ShortOpenLoop
+
+openAVX2ShortTail32:
+	CMPQ    inl, $16
+	VMOVDQA A0, A1
+	JB      openAVX2ShortDone
+
+	SUBQ $16, inl
+
+	// Load for hashing
+	polyAdd(0*8(inp))
+	polyMulAVX2
+
+	// Load for decryption
+	VPXOR      (inp), A0, T0
+	VMOVDQU    T0, (oup)
+	LEAQ       (1*16)(inp), inp
+	LEAQ       (1*16)(oup), oup
+	VPERM2I128 $0x11, AA0, AA0, AA0
+	VMOVDQA    A0, A1
+
+openAVX2ShortDone:
+	VZEROUPPER
+	JMP openSSETail16
+
+// ----------------------------------------------------------------------------
+// Special optimization for buffers smaller than 321 bytes
+openAVX2320:
+	// For up to 320 bytes of ciphertext and 64 bytes for the poly key, we process six blocks
+	VMOVDQA AA0, AA1; VMOVDQA BB0, BB1; VMOVDQA CC0, CC1; VPADDD ·avx2IncMask<>(SB), DD0, DD1
+	VMOVDQA AA0, AA2; VMOVDQA BB0, BB2; VMOVDQA CC0, CC2; VPADDD ·avx2IncMask<>(SB), DD1, DD2
+	VMOVDQA BB0, TT1; VMOVDQA CC0, TT2; VMOVDQA DD0, TT3
+	MOVQ    $10, itr2
+
+openAVX2320InnerCipherLoop:
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0)
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2
+	VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0)
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2
+	VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2
+	DECQ     itr2
+	JNE      openAVX2320InnerCipherLoop
+
+	VMOVDQA ·chacha20Constants<>(SB), TT0
+	VPADDD  TT0, AA0, AA0; VPADDD TT0, AA1, AA1; VPADDD TT0, AA2, AA2
+	VPADDD  TT1, BB0, BB0; VPADDD TT1, BB1, BB1; VPADDD TT1, BB2, BB2
+	VPADDD  TT2, CC0, CC0; VPADDD TT2, CC1, CC1; VPADDD TT2, CC2, CC2
+	VMOVDQA ·avx2IncMask<>(SB), TT0
+	VPADDD  TT3, DD0, DD0; VPADDD TT0, TT3, TT3
+	VPADDD  TT3, DD1, DD1; VPADDD TT0, TT3, TT3
+	VPADDD  TT3, DD2, DD2
+
+	// Clamp and store poly key
+	VPERM2I128 $0x02, AA0, BB0, TT0
+	VPAND      ·polyClampMask<>(SB), TT0, TT0
+	VMOVDQA    TT0, rsStoreAVX2
+
+	// Stream for up to 320 bytes
+	VPERM2I128 $0x13, AA0, BB0, AA0
+	VPERM2I128 $0x13, CC0, DD0, BB0
+	VPERM2I128 $0x02, AA1, BB1, CC0
+	VPERM2I128 $0x02, CC1, DD1, DD0
+	VPERM2I128 $0x13, AA1, BB1, AA1
+	VPERM2I128 $0x13, CC1, DD1, BB1
+	VPERM2I128 $0x02, AA2, BB2, CC1
+	VPERM2I128 $0x02, CC2, DD2, DD1
+	VPERM2I128 $0x13, AA2, BB2, AA2
+	VPERM2I128 $0x13, CC2, DD2, BB2
+	JMP        openAVX2ShortOpen
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 128 bytes of ciphertext
+openAVX2Tail128:
+	// Need to decrypt up to 128 bytes - prepare two blocks
+	VMOVDQA ·chacha20Constants<>(SB), AA1
+	VMOVDQA state1StoreAVX2, BB1
+	VMOVDQA state2StoreAVX2, CC1
+	VMOVDQA ctr3StoreAVX2, DD1
+	VPADDD  ·avx2IncMask<>(SB), DD1, DD1
+	VMOVDQA DD1, DD0
+
+	XORQ  itr2, itr2
+	MOVQ  inl, itr1
+	ANDQ  $-16, itr1
+	TESTQ itr1, itr1
+	JE    openAVX2Tail128LoopB
+
+openAVX2Tail128LoopA:
+	// Perform ChaCha rounds, while hashing the remaining input
+	polyAdd(0(inp)(itr2*1))
+	polyMulAVX2
+
+openAVX2Tail128LoopB:
+	ADDQ     $16, itr2
+	chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0)
+	VPALIGNR $4, BB1, BB1, BB1
+	VPALIGNR $8, CC1, CC1, CC1
+	VPALIGNR $12, DD1, DD1, DD1
+	chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0)
+	VPALIGNR $12, BB1, BB1, BB1
+	VPALIGNR $8, CC1, CC1, CC1
+	VPALIGNR $4, DD1, DD1, DD1
+	CMPQ     itr2, itr1
+	JB       openAVX2Tail128LoopA
+	CMPQ     itr2, $160
+	JNE      openAVX2Tail128LoopB
+
+	VPADDD     ·chacha20Constants<>(SB), AA1, AA1
+	VPADDD     state1StoreAVX2, BB1, BB1
+	VPADDD     state2StoreAVX2, CC1, CC1
+	VPADDD     DD0, DD1, DD1
+	VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0
+
+openAVX2TailLoop:
+	CMPQ inl, $32
+	JB   openAVX2Tail
+	SUBQ $32, inl
+
+	// Load for decryption
+	VPXOR   (inp), AA0, AA0
+	VMOVDQU AA0, (oup)
+	LEAQ    (1*32)(inp), inp
+	LEAQ    (1*32)(oup), oup
+	VMOVDQA BB0, AA0
+	VMOVDQA CC0, BB0
+	VMOVDQA DD0, CC0
+	JMP     openAVX2TailLoop
+
+openAVX2Tail:
+	CMPQ    inl, $16
+	VMOVDQA A0, A1
+	JB      openAVX2TailDone
+	SUBQ    $16, inl
+
+	// Load for decryption
+	VPXOR      (inp), A0, T0
+	VMOVDQU    T0, (oup)
+	LEAQ       (1*16)(inp), inp
+	LEAQ       (1*16)(oup), oup
+	VPERM2I128 $0x11, AA0, AA0, AA0
+	VMOVDQA    A0, A1
+
+openAVX2TailDone:
+	VZEROUPPER
+	JMP openSSETail16
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 256 bytes of ciphertext
+openAVX2Tail256:
+	// Need to decrypt up to 256 bytes - prepare four blocks
+	VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1
+	VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1
+	VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1
+	VMOVDQA ctr3StoreAVX2, DD0
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD0
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD1
+	VMOVDQA DD0, TT1
+	VMOVDQA DD1, TT2
+
+	// Compute the number of iterations that will hash data
+	MOVQ    inl, tmpStoreAVX2
+	MOVQ    inl, itr1
+	SUBQ    $128, itr1
+	SHRQ    $4, itr1
+	MOVQ    $10, itr2
+	CMPQ    itr1, $10
+	CMOVQGT itr2, itr1
+	MOVQ    inp, inl
+	XORQ    itr2, itr2
+
+openAVX2Tail256LoopA:
+	polyAdd(0(inl))
+	polyMulAVX2
+	LEAQ 16(inl), inl
+
+	// Perform ChaCha rounds, while hashing the remaining input
+openAVX2Tail256LoopB:
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0)
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1
+	VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1
+	INCQ     itr2
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0)
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1
+	VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1
+	CMPQ     itr2, itr1
+	JB       openAVX2Tail256LoopA
+
+	CMPQ itr2, $10
+	JNE  openAVX2Tail256LoopB
+
+	MOVQ inl, itr2
+	SUBQ inp, inl
+	MOVQ inl, itr1
+	MOVQ tmpStoreAVX2, inl
+
+	// Hash the remainder of data (if any)
+openAVX2Tail256Hash:
+	ADDQ $16, itr1
+	CMPQ itr1, inl
+	JGT  openAVX2Tail256HashEnd
+	polyAdd (0(itr2))
+	polyMulAVX2
+	LEAQ 16(itr2), itr2
+	JMP  openAVX2Tail256Hash
+
+// Store 128 bytes safely, then go to store loop
+openAVX2Tail256HashEnd:
+	VPADDD     ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1
+	VPADDD     state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1
+	VPADDD     state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1
+	VPADDD     TT1, DD0, DD0; VPADDD TT2, DD1, DD1
+	VPERM2I128 $0x02, AA0, BB0, AA2; VPERM2I128 $0x02, CC0, DD0, BB2; VPERM2I128 $0x13, AA0, BB0, CC2; VPERM2I128 $0x13, CC0, DD0, DD2
+	VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0
+
+	VPXOR   (0*32)(inp), AA2, AA2; VPXOR (1*32)(inp), BB2, BB2; VPXOR (2*32)(inp), CC2, CC2; VPXOR (3*32)(inp), DD2, DD2
+	VMOVDQU AA2, (0*32)(oup); VMOVDQU BB2, (1*32)(oup); VMOVDQU CC2, (2*32)(oup); VMOVDQU DD2, (3*32)(oup)
+	LEAQ    (4*32)(inp), inp
+	LEAQ    (4*32)(oup), oup
+	SUBQ    $4*32, inl
+
+	JMP openAVX2TailLoop
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 384 bytes of ciphertext
+openAVX2Tail384:
+	// Need to decrypt up to 384 bytes - prepare six blocks
+	VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2
+	VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2
+	VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2
+	VMOVDQA ctr3StoreAVX2, DD0
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD0
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD1
+	VPADDD  ·avx2IncMask<>(SB), DD1, DD2
+	VMOVDQA DD0, ctr0StoreAVX2
+	VMOVDQA DD1, ctr1StoreAVX2
+	VMOVDQA DD2, ctr2StoreAVX2
+
+	// Compute the number of iterations that will hash two blocks of data
+	MOVQ    inl, tmpStoreAVX2
+	MOVQ    inl, itr1
+	SUBQ    $256, itr1
+	SHRQ    $4, itr1
+	ADDQ    $6, itr1
+	MOVQ    $10, itr2
+	CMPQ    itr1, $10
+	CMOVQGT itr2, itr1
+	MOVQ    inp, inl
+	XORQ    itr2, itr2
+
+	// Perform ChaCha rounds, while hashing the remaining input
+openAVX2Tail384LoopB:
+	polyAdd(0(inl))
+	polyMulAVX2
+	LEAQ 16(inl), inl
+
+openAVX2Tail384LoopA:
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0)
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2
+	VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2
+	polyAdd(0(inl))
+	polyMulAVX2
+	LEAQ     16(inl), inl
+	INCQ     itr2
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0)
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2
+	VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2
+
+	CMPQ itr2, itr1
+	JB   openAVX2Tail384LoopB
+
+	CMPQ itr2, $10
+	JNE  openAVX2Tail384LoopA
+
+	MOVQ inl, itr2
+	SUBQ inp, inl
+	MOVQ inl, itr1
+	MOVQ tmpStoreAVX2, inl
+
+openAVX2Tail384Hash:
+	ADDQ $16, itr1
+	CMPQ itr1, inl
+	JGT  openAVX2Tail384HashEnd
+	polyAdd(0(itr2))
+	polyMulAVX2
+	LEAQ 16(itr2), itr2
+	JMP  openAVX2Tail384Hash
+
+// Store 256 bytes safely, then go to store loop
+openAVX2Tail384HashEnd:
+	VPADDD     ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2
+	VPADDD     state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2
+	VPADDD     state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2
+	VPADDD     ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2
+	VPERM2I128 $0x02, AA0, BB0, TT0; VPERM2I128 $0x02, CC0, DD0, TT1; VPERM2I128 $0x13, AA0, BB0, TT2; VPERM2I128 $0x13, CC0, DD0, TT3
+	VPXOR      (0*32)(inp), TT0, TT0; VPXOR (1*32)(inp), TT1, TT1; VPXOR (2*32)(inp), TT2, TT2; VPXOR (3*32)(inp), TT3, TT3
+	VMOVDQU    TT0, (0*32)(oup); VMOVDQU TT1, (1*32)(oup); VMOVDQU TT2, (2*32)(oup); VMOVDQU TT3, (3*32)(oup)
+	VPERM2I128 $0x02, AA1, BB1, TT0; VPERM2I128 $0x02, CC1, DD1, TT1; VPERM2I128 $0x13, AA1, BB1, TT2; VPERM2I128 $0x13, CC1, DD1, TT3
+	VPXOR      (4*32)(inp), TT0, TT0; VPXOR (5*32)(inp), TT1, TT1; VPXOR (6*32)(inp), TT2, TT2; VPXOR (7*32)(inp), TT3, TT3
+	VMOVDQU    TT0, (4*32)(oup); VMOVDQU TT1, (5*32)(oup); VMOVDQU TT2, (6*32)(oup); VMOVDQU TT3, (7*32)(oup)
+	VPERM2I128 $0x02, AA2, BB2, AA0; VPERM2I128 $0x02, CC2, DD2, BB0; VPERM2I128 $0x13, AA2, BB2, CC0; VPERM2I128 $0x13, CC2, DD2, DD0
+	LEAQ       (8*32)(inp), inp
+	LEAQ       (8*32)(oup), oup
+	SUBQ       $8*32, inl
+	JMP        openAVX2TailLoop
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 512 bytes of ciphertext
+openAVX2Tail512:
+	VMOVDQU ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3
+	VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3
+	VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3
+	VMOVDQA ctr3StoreAVX2, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2; VPADDD ·avx2IncMask<>(SB), DD2, DD3
+	VMOVDQA DD0, ctr0StoreAVX2; VMOVDQA DD1, ctr1StoreAVX2; VMOVDQA DD2, ctr2StoreAVX2; VMOVDQA DD3, ctr3StoreAVX2
+	XORQ    itr1, itr1
+	MOVQ    inp, itr2
+
+openAVX2Tail512LoopB:
+	polyAdd(0(itr2))
+	polyMulAVX2
+	LEAQ (2*8)(itr2), itr2
+
+openAVX2Tail512LoopA:
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	polyAdd(0*8(itr2))
+	polyMulAVX2
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $4, BB3, BB3, BB3
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3
+	VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2; VPALIGNR $12, DD3, DD3, DD3
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	polyAdd(2*8(itr2))
+	polyMulAVX2
+	LEAQ     (4*8)(itr2), itr2
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $12, BB3, BB3, BB3
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3
+	VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2; VPALIGNR $4, DD3, DD3, DD3
+	INCQ     itr1
+	CMPQ     itr1, $4
+	JLT      openAVX2Tail512LoopB
+
+	CMPQ itr1, $10
+	JNE  openAVX2Tail512LoopA
+
+	MOVQ inl, itr1
+	SUBQ $384, itr1
+	ANDQ $-16, itr1
+
+openAVX2Tail512HashLoop:
+	TESTQ itr1, itr1
+	JE    openAVX2Tail512HashEnd
+	polyAdd(0(itr2))
+	polyMulAVX2
+	LEAQ  16(itr2), itr2
+	SUBQ  $16, itr1
+	JMP   openAVX2Tail512HashLoop
+
+openAVX2Tail512HashEnd:
+	VPADDD     ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2; VPADDD ·chacha20Constants<>(SB), AA3, AA3
+	VPADDD     state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2; VPADDD state1StoreAVX2, BB3, BB3
+	VPADDD     state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2; VPADDD state2StoreAVX2, CC3, CC3
+	VPADDD     ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2; VPADDD ctr3StoreAVX2, DD3, DD3
+	VMOVDQA    CC3, tmpStoreAVX2
+	VPERM2I128 $0x02, AA0, BB0, CC3; VPERM2I128 $0x13, AA0, BB0, BB0; VPERM2I128 $0x02, CC0, DD0, AA0; VPERM2I128 $0x13, CC0, DD0, CC0
+	VPXOR      (0*32)(inp), CC3, CC3; VPXOR (1*32)(inp), AA0, AA0; VPXOR (2*32)(inp), BB0, BB0; VPXOR (3*32)(inp), CC0, CC0
+	VMOVDQU    CC3, (0*32)(oup); VMOVDQU AA0, (1*32)(oup); VMOVDQU BB0, (2*32)(oup); VMOVDQU CC0, (3*32)(oup)
+	VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0
+	VPXOR      (4*32)(inp), AA0, AA0; VPXOR (5*32)(inp), BB0, BB0; VPXOR (6*32)(inp), CC0, CC0; VPXOR (7*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (4*32)(oup); VMOVDQU BB0, (5*32)(oup); VMOVDQU CC0, (6*32)(oup); VMOVDQU DD0, (7*32)(oup)
+	VPERM2I128 $0x02, AA2, BB2, AA0; VPERM2I128 $0x02, CC2, DD2, BB0; VPERM2I128 $0x13, AA2, BB2, CC0; VPERM2I128 $0x13, CC2, DD2, DD0
+	VPXOR      (8*32)(inp), AA0, AA0; VPXOR (9*32)(inp), BB0, BB0; VPXOR (10*32)(inp), CC0, CC0; VPXOR (11*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (8*32)(oup); VMOVDQU BB0, (9*32)(oup); VMOVDQU CC0, (10*32)(oup); VMOVDQU DD0, (11*32)(oup)
+	VPERM2I128 $0x02, AA3, BB3, AA0; VPERM2I128 $0x02, tmpStoreAVX2, DD3, BB0; VPERM2I128 $0x13, AA3, BB3, CC0; VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0
+
+	LEAQ (12*32)(inp), inp
+	LEAQ (12*32)(oup), oup
+	SUBQ $12*32, inl
+
+	JMP openAVX2TailLoop
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+// func chacha20Poly1305Seal(dst, key, src, ad []byte)
+TEXT ·chacha20Poly1305Seal(SB), 0, $288-96
+	// For aligned stack access
+	MOVQ SP, BP
+	ADDQ $32, BP
+	ANDQ $-32, BP
+	MOVQ dst+0(FP), oup
+	MOVQ key+24(FP), keyp
+	MOVQ src+48(FP), inp
+	MOVQ src_len+56(FP), inl
+	MOVQ ad+72(FP), adp
+
+	// Check for AVX2 support
+	CMPB runtime·support_avx2(SB), $1
+	JE   chacha20Poly1305Seal_AVX2
+
+	// Special optimization, for very short buffers
+	CMPQ inl, $128
+	JBE  sealSSE128 // About 15% faster
+
+	// In the seal case - prepare the poly key + 3 blocks of stream in the first iteration
+	MOVOU ·chacha20Constants<>(SB), A0
+	MOVOU (1*16)(keyp), B0
+	MOVOU (2*16)(keyp), C0
+	MOVOU (3*16)(keyp), D0
+
+	// Store state on stack for future use
+	MOVO B0, state1Store
+	MOVO C0, state2Store
+
+	// Load state, increment counter blocks
+	MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1
+	MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2
+	MOVO A2, A3; MOVO B2, B3; MOVO C2, C3; MOVO D2, D3; PADDL ·sseIncMask<>(SB), D3
+
+	// Store counters
+	MOVO D0, ctr0Store; MOVO D1, ctr1Store; MOVO D2, ctr2Store; MOVO D3, ctr3Store
+	MOVQ $10, itr2
+
+sealSSEIntroLoop:
+	MOVO         C3, tmpStore
+	chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3)
+	MOVO         tmpStore, C3
+	MOVO         C1, tmpStore
+	chachaQR(A3, B3, C3, D3, C1)
+	MOVO         tmpStore, C1
+	shiftB0Left; shiftB1Left; shiftB2Left; shiftB3Left
+	shiftC0Left; shiftC1Left; shiftC2Left; shiftC3Left
+	shiftD0Left; shiftD1Left; shiftD2Left; shiftD3Left
+
+	MOVO          C3, tmpStore
+	chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3)
+	MOVO          tmpStore, C3
+	MOVO          C1, tmpStore
+	chachaQR(A3, B3, C3, D3, C1)
+	MOVO          tmpStore, C1
+	shiftB0Right; shiftB1Right; shiftB2Right; shiftB3Right
+	shiftC0Right; shiftC1Right; shiftC2Right; shiftC3Right
+	shiftD0Right; shiftD1Right; shiftD2Right; shiftD3Right
+	DECQ          itr2
+	JNE           sealSSEIntroLoop
+
+	// Add in the state
+	PADDD ·chacha20Constants<>(SB), A0; PADDD ·chacha20Constants<>(SB), A1; PADDD ·chacha20Constants<>(SB), A2; PADDD ·chacha20Constants<>(SB), A3
+	PADDD state1Store, B0; PADDD state1Store, B1; PADDD state1Store, B2; PADDD state1Store, B3
+	PADDD state2Store, C1; PADDD state2Store, C2; PADDD state2Store, C3
+	PADDD ctr1Store, D1; PADDD ctr2Store, D2; PADDD ctr3Store, D3
+
+	// Clamp and store the key
+	PAND ·polyClampMask<>(SB), A0
+	MOVO A0, rStore
+	MOVO B0, sStore
+
+	// Hash AAD
+	MOVQ ad_len+80(FP), itr2
+	CALL polyHashADInternal(SB)
+
+	MOVOU (0*16)(inp), A0; MOVOU (1*16)(inp), B0; MOVOU (2*16)(inp), C0; MOVOU (3*16)(inp), D0
+	PXOR  A0, A1; PXOR B0, B1; PXOR C0, C1; PXOR D0, D1
+	MOVOU A1, (0*16)(oup); MOVOU B1, (1*16)(oup); MOVOU C1, (2*16)(oup); MOVOU D1, (3*16)(oup)
+	MOVOU (4*16)(inp), A0; MOVOU (5*16)(inp), B0; MOVOU (6*16)(inp), C0; MOVOU (7*16)(inp), D0
+	PXOR  A0, A2; PXOR B0, B2; PXOR C0, C2; PXOR D0, D2
+	MOVOU A2, (4*16)(oup); MOVOU B2, (5*16)(oup); MOVOU C2, (6*16)(oup); MOVOU D2, (7*16)(oup)
+
+	MOVQ $128, itr1
+	SUBQ $128, inl
+	LEAQ 128(inp), inp
+
+	MOVO A3, A1; MOVO B3, B1; MOVO C3, C1; MOVO D3, D1
+
+	CMPQ inl, $64
+	JBE  sealSSE128SealHash
+
+	MOVOU (0*16)(inp), A0; MOVOU (1*16)(inp), B0; MOVOU (2*16)(inp), C0; MOVOU (3*16)(inp), D0
+	PXOR  A0, A3; PXOR B0, B3; PXOR C0, C3; PXOR D0, D3
+	MOVOU A3, (8*16)(oup); MOVOU B3, (9*16)(oup); MOVOU C3, (10*16)(oup); MOVOU D3, (11*16)(oup)
+
+	ADDQ $64, itr1
+	SUBQ $64, inl
+	LEAQ 64(inp), inp
+
+	MOVQ $2, itr1
+	MOVQ $8, itr2
+
+	CMPQ inl, $64
+	JBE  sealSSETail64
+	CMPQ inl, $128
+	JBE  sealSSETail128
+	CMPQ inl, $192
+	JBE  sealSSETail192
+
+sealSSEMainLoop:
+	// Load state, increment counter blocks
+	MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0
+	MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1
+	MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2
+	MOVO A2, A3; MOVO B2, B3; MOVO C2, C3; MOVO D2, D3; PADDL ·sseIncMask<>(SB), D3
+
+	// Store counters
+	MOVO D0, ctr0Store; MOVO D1, ctr1Store; MOVO D2, ctr2Store; MOVO D3, ctr3Store
+
+sealSSEInnerLoop:
+	MOVO          C3, tmpStore
+	chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3)
+	MOVO          tmpStore, C3
+	MOVO          C1, tmpStore
+	chachaQR(A3, B3, C3, D3, C1)
+	MOVO          tmpStore, C1
+	polyAdd(0(oup))
+	shiftB0Left;  shiftB1Left; shiftB2Left; shiftB3Left
+	shiftC0Left;  shiftC1Left; shiftC2Left; shiftC3Left
+	shiftD0Left;  shiftD1Left; shiftD2Left; shiftD3Left
+	polyMulStage1
+	polyMulStage2
+	LEAQ          (2*8)(oup), oup
+	MOVO          C3, tmpStore
+	chachaQR(A0, B0, C0, D0, C3); chachaQR(A1, B1, C1, D1, C3); chachaQR(A2, B2, C2, D2, C3)
+	MOVO          tmpStore, C3
+	MOVO          C1, tmpStore
+	polyMulStage3
+	chachaQR(A3, B3, C3, D3, C1)
+	MOVO          tmpStore, C1
+	polyMulReduceStage
+	shiftB0Right; shiftB1Right; shiftB2Right; shiftB3Right
+	shiftC0Right; shiftC1Right; shiftC2Right; shiftC3Right
+	shiftD0Right; shiftD1Right; shiftD2Right; shiftD3Right
+	DECQ          itr2
+	JGE           sealSSEInnerLoop
+	polyAdd(0(oup))
+	polyMul
+	LEAQ          (2*8)(oup), oup
+	DECQ          itr1
+	JG            sealSSEInnerLoop
+
+	// Add in the state
+	PADDD ·chacha20Constants<>(SB), A0; PADDD ·chacha20Constants<>(SB), A1; PADDD ·chacha20Constants<>(SB), A2; PADDD ·chacha20Constants<>(SB), A3
+	PADDD state1Store, B0; PADDD state1Store, B1; PADDD state1Store, B2; PADDD state1Store, B3
+	PADDD state2Store, C0; PADDD state2Store, C1; PADDD state2Store, C2; PADDD state2Store, C3
+	PADDD ctr0Store, D0; PADDD ctr1Store, D1; PADDD ctr2Store, D2; PADDD ctr3Store, D3
+	MOVO  D3, tmpStore
+
+	// Load - xor - store
+	MOVOU (0*16)(inp), D3; PXOR D3, A0
+	MOVOU (1*16)(inp), D3; PXOR D3, B0
+	MOVOU (2*16)(inp), D3; PXOR D3, C0
+	MOVOU (3*16)(inp), D3; PXOR D3, D0
+	MOVOU A0, (0*16)(oup)
+	MOVOU B0, (1*16)(oup)
+	MOVOU C0, (2*16)(oup)
+	MOVOU D0, (3*16)(oup)
+	MOVO  tmpStore, D3
+
+	MOVOU (4*16)(inp), A0; MOVOU (5*16)(inp), B0; MOVOU (6*16)(inp), C0; MOVOU (7*16)(inp), D0
+	PXOR  A0, A1; PXOR B0, B1; PXOR C0, C1; PXOR D0, D1
+	MOVOU A1, (4*16)(oup); MOVOU B1, (5*16)(oup); MOVOU C1, (6*16)(oup); MOVOU D1, (7*16)(oup)
+	MOVOU (8*16)(inp), A0; MOVOU (9*16)(inp), B0; MOVOU (10*16)(inp), C0; MOVOU (11*16)(inp), D0
+	PXOR  A0, A2; PXOR B0, B2; PXOR C0, C2; PXOR D0, D2
+	MOVOU A2, (8*16)(oup); MOVOU B2, (9*16)(oup); MOVOU C2, (10*16)(oup); MOVOU D2, (11*16)(oup)
+	ADDQ  $192, inp
+	MOVQ  $192, itr1
+	SUBQ  $192, inl
+	MOVO  A3, A1
+	MOVO  B3, B1
+	MOVO  C3, C1
+	MOVO  D3, D1
+	CMPQ  inl, $64
+	JBE   sealSSE128SealHash
+	MOVOU (0*16)(inp), A0; MOVOU (1*16)(inp), B0; MOVOU (2*16)(inp), C0; MOVOU (3*16)(inp), D0
+	PXOR  A0, A3; PXOR B0, B3; PXOR C0, C3; PXOR D0, D3
+	MOVOU A3, (12*16)(oup); MOVOU B3, (13*16)(oup); MOVOU C3, (14*16)(oup); MOVOU D3, (15*16)(oup)
+	LEAQ  64(inp), inp
+	SUBQ  $64, inl
+	MOVQ  $6, itr1
+	MOVQ  $4, itr2
+	CMPQ  inl, $192
+	JG    sealSSEMainLoop
+
+	MOVQ  inl, itr1
+	TESTQ inl, inl
+	JE    sealSSE128SealHash
+	MOVQ  $6, itr1
+	CMPQ  inl, $64
+	JBE   sealSSETail64
+	CMPQ  inl, $128
+	JBE   sealSSETail128
+	JMP   sealSSETail192
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 64 bytes of plaintext
+sealSSETail64:
+	// Need to encrypt up to 64 bytes - prepare single block, hash 192 or 256 bytes
+	MOVO  ·chacha20Constants<>(SB), A1
+	MOVO  state1Store, B1
+	MOVO  state2Store, C1
+	MOVO  ctr3Store, D1
+	PADDL ·sseIncMask<>(SB), D1
+	MOVO  D1, ctr0Store
+
+sealSSETail64LoopA:
+	// Perform ChaCha rounds, while hashing the prevsiosly encrpyted ciphertext
+	polyAdd(0(oup))
+	polyMul
+	LEAQ 16(oup), oup
+
+sealSSETail64LoopB:
+	chachaQR(A1, B1, C1, D1, T1)
+	shiftB1Left;  shiftC1Left; shiftD1Left
+	chachaQR(A1, B1, C1, D1, T1)
+	shiftB1Right; shiftC1Right; shiftD1Right
+	polyAdd(0(oup))
+	polyMul
+	LEAQ          16(oup), oup
+
+	DECQ itr1
+	JG   sealSSETail64LoopA
+
+	DECQ  itr2
+	JGE   sealSSETail64LoopB
+	PADDL ·chacha20Constants<>(SB), A1
+	PADDL state1Store, B1
+	PADDL state2Store, C1
+	PADDL ctr0Store, D1
+
+	JMP sealSSE128Seal
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 128 bytes of plaintext
+sealSSETail128:
+	// Need to encrypt up to 128 bytes - prepare two blocks, hash 192 or 256 bytes
+	MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0; MOVO D0, ctr0Store
+	MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1; MOVO D1, ctr1Store
+
+sealSSETail128LoopA:
+	// Perform ChaCha rounds, while hashing the prevsiosly encrpyted ciphertext
+	polyAdd(0(oup))
+	polyMul
+	LEAQ 16(oup), oup
+
+sealSSETail128LoopB:
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0)
+	shiftB0Left;  shiftC0Left; shiftD0Left
+	shiftB1Left;  shiftC1Left; shiftD1Left
+	polyAdd(0(oup))
+	polyMul
+	LEAQ          16(oup), oup
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0)
+	shiftB0Right; shiftC0Right; shiftD0Right
+	shiftB1Right; shiftC1Right; shiftD1Right
+
+	DECQ itr1
+	JG   sealSSETail128LoopA
+
+	DECQ itr2
+	JGE  sealSSETail128LoopB
+
+	PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1
+	PADDL state1Store, B0; PADDL state1Store, B1
+	PADDL state2Store, C0; PADDL state2Store, C1
+	PADDL ctr0Store, D0; PADDL ctr1Store, D1
+
+	MOVOU (0*16)(inp), T0; MOVOU (1*16)(inp), T1; MOVOU (2*16)(inp), T2; MOVOU (3*16)(inp), T3
+	PXOR  T0, A0; PXOR T1, B0; PXOR T2, C0; PXOR T3, D0
+	MOVOU A0, (0*16)(oup); MOVOU B0, (1*16)(oup); MOVOU C0, (2*16)(oup); MOVOU D0, (3*16)(oup)
+
+	MOVQ $64, itr1
+	LEAQ 64(inp), inp
+	SUBQ $64, inl
+
+	JMP sealSSE128SealHash
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 192 bytes of plaintext
+sealSSETail192:
+	// Need to encrypt up to 192 bytes - prepare three blocks, hash 192 or 256 bytes
+	MOVO ·chacha20Constants<>(SB), A0; MOVO state1Store, B0; MOVO state2Store, C0; MOVO ctr3Store, D0; PADDL ·sseIncMask<>(SB), D0; MOVO D0, ctr0Store
+	MOVO A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1; MOVO D1, ctr1Store
+	MOVO A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2; MOVO D2, ctr2Store
+
+sealSSETail192LoopA:
+	// Perform ChaCha rounds, while hashing the prevsiosly encrpyted ciphertext
+	polyAdd(0(oup))
+	polyMul
+	LEAQ 16(oup), oup
+
+sealSSETail192LoopB:
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0)
+	shiftB0Left; shiftC0Left; shiftD0Left
+	shiftB1Left; shiftC1Left; shiftD1Left
+	shiftB2Left; shiftC2Left; shiftD2Left
+
+	polyAdd(0(oup))
+	polyMul
+	LEAQ 16(oup), oup
+
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0)
+	shiftB0Right; shiftC0Right; shiftD0Right
+	shiftB1Right; shiftC1Right; shiftD1Right
+	shiftB2Right; shiftC2Right; shiftD2Right
+
+	DECQ itr1
+	JG   sealSSETail192LoopA
+
+	DECQ itr2
+	JGE  sealSSETail192LoopB
+
+	PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1; PADDL ·chacha20Constants<>(SB), A2
+	PADDL state1Store, B0; PADDL state1Store, B1; PADDL state1Store, B2
+	PADDL state2Store, C0; PADDL state2Store, C1; PADDL state2Store, C2
+	PADDL ctr0Store, D0; PADDL ctr1Store, D1; PADDL ctr2Store, D2
+
+	MOVOU (0*16)(inp), T0; MOVOU (1*16)(inp), T1; MOVOU (2*16)(inp), T2; MOVOU (3*16)(inp), T3
+	PXOR  T0, A0; PXOR T1, B0; PXOR T2, C0; PXOR T3, D0
+	MOVOU A0, (0*16)(oup); MOVOU B0, (1*16)(oup); MOVOU C0, (2*16)(oup); MOVOU D0, (3*16)(oup)
+	MOVOU (4*16)(inp), T0; MOVOU (5*16)(inp), T1; MOVOU (6*16)(inp), T2; MOVOU (7*16)(inp), T3
+	PXOR  T0, A1; PXOR T1, B1; PXOR T2, C1; PXOR T3, D1
+	MOVOU A1, (4*16)(oup); MOVOU B1, (5*16)(oup); MOVOU C1, (6*16)(oup); MOVOU D1, (7*16)(oup)
+
+	MOVO A2, A1
+	MOVO B2, B1
+	MOVO C2, C1
+	MOVO D2, D1
+	MOVQ $128, itr1
+	LEAQ 128(inp), inp
+	SUBQ $128, inl
+
+	JMP sealSSE128SealHash
+
+// ----------------------------------------------------------------------------
+// Special seal optimization for buffers smaller than 129 bytes
+sealSSE128:
+	// For up to 128 bytes of ciphertext and 64 bytes for the poly key, we require to process three blocks
+	MOVOU ·chacha20Constants<>(SB), A0; MOVOU (1*16)(keyp), B0; MOVOU (2*16)(keyp), C0; MOVOU (3*16)(keyp), D0
+	MOVO  A0, A1; MOVO B0, B1; MOVO C0, C1; MOVO D0, D1; PADDL ·sseIncMask<>(SB), D1
+	MOVO  A1, A2; MOVO B1, B2; MOVO C1, C2; MOVO D1, D2; PADDL ·sseIncMask<>(SB), D2
+	MOVO  B0, T1; MOVO C0, T2; MOVO D1, T3
+	MOVQ  $10, itr2
+
+sealSSE128InnerCipherLoop:
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0)
+	shiftB0Left;  shiftB1Left; shiftB2Left
+	shiftC0Left;  shiftC1Left; shiftC2Left
+	shiftD0Left;  shiftD1Left; shiftD2Left
+	chachaQR(A0, B0, C0, D0, T0); chachaQR(A1, B1, C1, D1, T0); chachaQR(A2, B2, C2, D2, T0)
+	shiftB0Right; shiftB1Right; shiftB2Right
+	shiftC0Right; shiftC1Right; shiftC2Right
+	shiftD0Right; shiftD1Right; shiftD2Right
+	DECQ          itr2
+	JNE           sealSSE128InnerCipherLoop
+
+	// A0|B0 hold the Poly1305 32-byte key, C0,D0 can be discarded
+	PADDL ·chacha20Constants<>(SB), A0; PADDL ·chacha20Constants<>(SB), A1; PADDL ·chacha20Constants<>(SB), A2
+	PADDL T1, B0; PADDL T1, B1; PADDL T1, B2
+	PADDL T2, C1; PADDL T2, C2
+	PADDL T3, D1; PADDL ·sseIncMask<>(SB), T3; PADDL T3, D2
+	PAND  ·polyClampMask<>(SB), A0
+	MOVOU A0, rStore
+	MOVOU B0, sStore
+
+	// Hash
+	MOVQ ad_len+80(FP), itr2
+	CALL polyHashADInternal(SB)
+	XORQ itr1, itr1
+
+sealSSE128SealHash:
+	// itr1 holds the number of bytes encrypted but not yet hashed
+	CMPQ itr1, $16
+	JB   sealSSE128Seal
+	polyAdd(0(oup))
+	polyMul
+
+	SUBQ $16, itr1
+	ADDQ $16, oup
+
+	JMP sealSSE128SealHash
+
+sealSSE128Seal:
+	CMPQ inl, $16
+	JB   sealSSETail
+	SUBQ $16, inl
+
+	// Load for decryption
+	MOVOU (inp), T0
+	PXOR  T0, A1
+	MOVOU A1, (oup)
+	LEAQ  (1*16)(inp), inp
+	LEAQ  (1*16)(oup), oup
+
+	// Extract for hashing
+	MOVQ   A1, t0
+	PSRLDQ $8, A1
+	MOVQ A1, t1
+	ADDQ   t0, acc0; ADCQ t1, acc1; ADCQ $1, acc2
+	polyMul
+
+	// Shift the stream "left"
+	MOVO B1, A1
+	MOVO C1, B1
+	MOVO D1, C1
+	MOVO A2, D1
+	MOVO B2, A2
+	MOVO C2, B2
+	MOVO D2, C2
+	JMP  sealSSE128Seal
+
+sealSSETail:
+	TESTQ inl, inl
+	JE    sealSSEFinalize
+
+	// We can only load the PT one byte at a time to avoid read after end of buffer
+	MOVQ inl, itr2
+	SHLQ $4, itr2
+	LEAQ ·andMask<>(SB), t0
+	MOVQ inl, itr1
+	LEAQ -1(inp)(inl*1), inp
+	XORQ t2, t2
+	XORQ t3, t3
+	XORQ AX, AX
+
+sealSSETailLoadLoop:
+	SHLQ $8, t2, t3
+	SHLQ $8, t2
+	MOVB (inp), AX
+	XORQ AX, t2
+	LEAQ   -1(inp), inp
+	DECQ   itr1
+	JNE    sealSSETailLoadLoop
+	MOVQ t2, 0+tmpStore
+	MOVQ t3, 8+tmpStore
+	PXOR 0+tmpStore, A1
+	MOVOU  A1, (oup)
+	MOVOU  -16(t0)(itr2*1), T0
+	PAND   T0, A1
+	MOVQ   A1, t0
+	PSRLDQ $8, A1
+	MOVQ   A1, t1
+	ADDQ   t0, acc0; ADCQ t1, acc1; ADCQ $1, acc2
+	polyMul
+
+	ADDQ inl, oup
+
+sealSSEFinalize:
+	// Hash in the buffer lengths
+	ADDQ ad_len+80(FP), acc0
+	ADCQ src_len+56(FP), acc1
+	ADCQ $1, acc2
+	polyMul
+
+	// Final reduce
+	MOVQ    acc0, t0
+	MOVQ    acc1, t1
+	MOVQ    acc2, t2
+	SUBQ    $-5, acc0
+	SBBQ    $-1, acc1
+	SBBQ    $3, acc2
+	CMOVQCS t0, acc0
+	CMOVQCS t1, acc1
+	CMOVQCS t2, acc2
+
+	// Add in the "s" part of the key
+	ADDQ 0+sStore, acc0
+	ADCQ 8+sStore, acc1
+
+	// Finally store the tag at the end of the message
+	MOVQ acc0, (0*8)(oup)
+	MOVQ acc1, (1*8)(oup)
+	RET
+
+// ----------------------------------------------------------------------------
+// ------------------------- AVX2 Code ----------------------------------------
+chacha20Poly1305Seal_AVX2:
+	VZEROUPPER
+	VMOVDQU ·chacha20Constants<>(SB), AA0
+	BYTE    $0xc4; BYTE $0x42; BYTE $0x7d; BYTE $0x5a; BYTE $0x70; BYTE $0x10 // broadcasti128 16(r8), ymm14
+	BYTE    $0xc4; BYTE $0x42; BYTE $0x7d; BYTE $0x5a; BYTE $0x60; BYTE $0x20 // broadcasti128 32(r8), ymm12
+	BYTE    $0xc4; BYTE $0xc2; BYTE $0x7d; BYTE $0x5a; BYTE $0x60; BYTE $0x30 // broadcasti128 48(r8), ymm4
+	VPADDD  ·avx2InitMask<>(SB), DD0, DD0
+
+	// Special optimizations, for very short buffers
+	CMPQ inl, $192
+	JBE  seal192AVX2 // 33% faster
+	CMPQ inl, $320
+	JBE  seal320AVX2 // 17% faster
+
+	// For the general key prepare the key first - as a byproduct we have 64 bytes of cipher stream
+	VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3
+	VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3; VMOVDQA BB0, state1StoreAVX2
+	VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3; VMOVDQA CC0, state2StoreAVX2
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD1; VMOVDQA DD0, ctr0StoreAVX2
+	VPADDD  ·avx2IncMask<>(SB), DD1, DD2; VMOVDQA DD1, ctr1StoreAVX2
+	VPADDD  ·avx2IncMask<>(SB), DD2, DD3; VMOVDQA DD2, ctr2StoreAVX2
+	VMOVDQA DD3, ctr3StoreAVX2
+	MOVQ    $10, itr2
+
+sealAVX2IntroLoop:
+	VMOVDQA CC3, tmpStoreAVX2
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, CC3); chachaQR_AVX2(AA1, BB1, CC1, DD1, CC3); chachaQR_AVX2(AA2, BB2, CC2, DD2, CC3)
+	VMOVDQA tmpStoreAVX2, CC3
+	VMOVDQA CC1, tmpStoreAVX2
+	chachaQR_AVX2(AA3, BB3, CC3, DD3, CC1)
+	VMOVDQA tmpStoreAVX2, CC1
+
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $12, DD0, DD0, DD0
+	VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $12, DD1, DD1, DD1
+	VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $12, DD2, DD2, DD2
+	VPALIGNR $4, BB3, BB3, BB3; VPALIGNR $8, CC3, CC3, CC3; VPALIGNR $12, DD3, DD3, DD3
+
+	VMOVDQA CC3, tmpStoreAVX2
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, CC3); chachaQR_AVX2(AA1, BB1, CC1, DD1, CC3); chachaQR_AVX2(AA2, BB2, CC2, DD2, CC3)
+	VMOVDQA tmpStoreAVX2, CC3
+	VMOVDQA CC1, tmpStoreAVX2
+	chachaQR_AVX2(AA3, BB3, CC3, DD3, CC1)
+	VMOVDQA tmpStoreAVX2, CC1
+
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $4, DD0, DD0, DD0
+	VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $4, DD1, DD1, DD1
+	VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $4, DD2, DD2, DD2
+	VPALIGNR $12, BB3, BB3, BB3; VPALIGNR $8, CC3, CC3, CC3; VPALIGNR $4, DD3, DD3, DD3
+	DECQ     itr2
+	JNE      sealAVX2IntroLoop
+
+	VPADDD ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2; VPADDD ·chacha20Constants<>(SB), AA3, AA3
+	VPADDD state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2; VPADDD state1StoreAVX2, BB3, BB3
+	VPADDD state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2; VPADDD state2StoreAVX2, CC3, CC3
+	VPADDD ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2; VPADDD ctr3StoreAVX2, DD3, DD3
+
+	VPERM2I128 $0x13, CC0, DD0, CC0 // Stream bytes 96 - 127
+	VPERM2I128 $0x02, AA0, BB0, DD0 // The Poly1305 key
+	VPERM2I128 $0x13, AA0, BB0, AA0 // Stream bytes 64 - 95
+
+	// Clamp and store poly key
+	VPAND   ·polyClampMask<>(SB), DD0, DD0
+	VMOVDQA DD0, rsStoreAVX2
+
+	// Hash AD
+	MOVQ ad_len+80(FP), itr2
+	CALL polyHashADInternal(SB)
+
+	// Can store at least 320 bytes
+	VPXOR   (0*32)(inp), AA0, AA0
+	VPXOR   (1*32)(inp), CC0, CC0
+	VMOVDQU AA0, (0*32)(oup)
+	VMOVDQU CC0, (1*32)(oup)
+
+	VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0
+	VPXOR      (2*32)(inp), AA0, AA0; VPXOR (3*32)(inp), BB0, BB0; VPXOR (4*32)(inp), CC0, CC0; VPXOR (5*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (2*32)(oup); VMOVDQU BB0, (3*32)(oup); VMOVDQU CC0, (4*32)(oup); VMOVDQU DD0, (5*32)(oup)
+	VPERM2I128 $0x02, AA2, BB2, AA0; VPERM2I128 $0x02, CC2, DD2, BB0; VPERM2I128 $0x13, AA2, BB2, CC0; VPERM2I128 $0x13, CC2, DD2, DD0
+	VPXOR      (6*32)(inp), AA0, AA0; VPXOR (7*32)(inp), BB0, BB0; VPXOR (8*32)(inp), CC0, CC0; VPXOR (9*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (6*32)(oup); VMOVDQU BB0, (7*32)(oup); VMOVDQU CC0, (8*32)(oup); VMOVDQU DD0, (9*32)(oup)
+
+	MOVQ $320, itr1
+	SUBQ $320, inl
+	LEAQ 320(inp), inp
+
+	VPERM2I128 $0x02, AA3, BB3, AA0; VPERM2I128 $0x02, CC3, DD3, BB0; VPERM2I128 $0x13, AA3, BB3, CC0; VPERM2I128 $0x13, CC3, DD3, DD0
+	CMPQ       inl, $128
+	JBE        sealAVX2SealHash
+
+	VPXOR   (0*32)(inp), AA0, AA0; VPXOR (1*32)(inp), BB0, BB0; VPXOR (2*32)(inp), CC0, CC0; VPXOR (3*32)(inp), DD0, DD0
+	VMOVDQU AA0, (10*32)(oup); VMOVDQU BB0, (11*32)(oup); VMOVDQU CC0, (12*32)(oup); VMOVDQU DD0, (13*32)(oup)
+	SUBQ    $128, inl
+	LEAQ    128(inp), inp
+
+	MOVQ $8, itr1
+	MOVQ $2, itr2
+
+	CMPQ inl, $128
+	JBE  sealAVX2Tail128
+	CMPQ inl, $256
+	JBE  sealAVX2Tail256
+	CMPQ inl, $384
+	JBE  sealAVX2Tail384
+	CMPQ inl, $512
+	JBE  sealAVX2Tail512
+
+	// We have 448 bytes to hash, but main loop hashes 512 bytes at a time - perform some rounds, before the main loop
+	VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3
+	VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3
+	VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3
+	VMOVDQA ctr3StoreAVX2, DD0
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2; VPADDD ·avx2IncMask<>(SB), DD2, DD3
+	VMOVDQA DD0, ctr0StoreAVX2; VMOVDQA DD1, ctr1StoreAVX2; VMOVDQA DD2, ctr2StoreAVX2; VMOVDQA DD3, ctr3StoreAVX2
+
+	VMOVDQA CC3, tmpStoreAVX2
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, CC3); chachaQR_AVX2(AA1, BB1, CC1, DD1, CC3); chachaQR_AVX2(AA2, BB2, CC2, DD2, CC3)
+	VMOVDQA tmpStoreAVX2, CC3
+	VMOVDQA CC1, tmpStoreAVX2
+	chachaQR_AVX2(AA3, BB3, CC3, DD3, CC1)
+	VMOVDQA tmpStoreAVX2, CC1
+
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $12, DD0, DD0, DD0
+	VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $12, DD1, DD1, DD1
+	VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $12, DD2, DD2, DD2
+	VPALIGNR $4, BB3, BB3, BB3; VPALIGNR $8, CC3, CC3, CC3; VPALIGNR $12, DD3, DD3, DD3
+
+	VMOVDQA CC3, tmpStoreAVX2
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, CC3); chachaQR_AVX2(AA1, BB1, CC1, DD1, CC3); chachaQR_AVX2(AA2, BB2, CC2, DD2, CC3)
+	VMOVDQA tmpStoreAVX2, CC3
+	VMOVDQA CC1, tmpStoreAVX2
+	chachaQR_AVX2(AA3, BB3, CC3, DD3, CC1)
+	VMOVDQA tmpStoreAVX2, CC1
+
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $4, DD0, DD0, DD0
+	VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $4, DD1, DD1, DD1
+	VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $4, DD2, DD2, DD2
+	VPALIGNR $12, BB3, BB3, BB3; VPALIGNR $8, CC3, CC3, CC3; VPALIGNR $4, DD3, DD3, DD3
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+
+	SUBQ $16, oup                  // Adjust the pointer
+	MOVQ $9, itr1
+	JMP  sealAVX2InternalLoopStart
+
+sealAVX2MainLoop:
+	// Load state, increment counter blocks, store the incremented counters
+	VMOVDQU ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3
+	VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3
+	VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3
+	VMOVDQA ctr3StoreAVX2, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2; VPADDD ·avx2IncMask<>(SB), DD2, DD3
+	VMOVDQA DD0, ctr0StoreAVX2; VMOVDQA DD1, ctr1StoreAVX2; VMOVDQA DD2, ctr2StoreAVX2; VMOVDQA DD3, ctr3StoreAVX2
+	MOVQ    $10, itr1
+
+sealAVX2InternalLoop:
+	polyAdd(0*8(oup))
+	VPADDD  BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	polyMulStage1_AVX2
+	VPXOR   AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3
+	polyMulStage2_AVX2
+	VPADDD  DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR   CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	polyMulStage3_AVX2
+	VMOVDQA CC3, tmpStoreAVX2
+	VPSLLD  $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD  $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD  $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD  $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA tmpStoreAVX2, CC3
+	polyMulReduceStage
+
+sealAVX2InternalLoopStart:
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3
+	polyAdd(2*8(oup))
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	polyMulStage1_AVX2
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	polyMulStage2_AVX2
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $4, BB3, BB3, BB3
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3
+	VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2; VPALIGNR $12, DD3, DD3, DD3
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	polyMulStage3_AVX2
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3
+	polyMulReduceStage
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	polyAdd(4*8(oup))
+	LEAQ     (6*8)(oup), oup
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	polyMulStage1_AVX2
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	polyMulStage2_AVX2
+	VPSHUFB  ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	polyMulStage3_AVX2
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	polyMulReduceStage
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $12, BB3, BB3, BB3
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3
+	VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2; VPALIGNR $4, DD3, DD3, DD3
+	DECQ     itr1
+	JNE      sealAVX2InternalLoop
+
+	VPADDD  ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2; VPADDD ·chacha20Constants<>(SB), AA3, AA3
+	VPADDD  state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2; VPADDD state1StoreAVX2, BB3, BB3
+	VPADDD  state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2; VPADDD state2StoreAVX2, CC3, CC3
+	VPADDD  ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2; VPADDD ctr3StoreAVX2, DD3, DD3
+	VMOVDQA CC3, tmpStoreAVX2
+
+	// We only hashed 480 of the 512 bytes available - hash the remaining 32 here
+	polyAdd(0*8(oup))
+	polyMulAVX2
+	LEAQ       (4*8)(oup), oup
+	VPERM2I128 $0x02, AA0, BB0, CC3; VPERM2I128 $0x13, AA0, BB0, BB0; VPERM2I128 $0x02, CC0, DD0, AA0; VPERM2I128 $0x13, CC0, DD0, CC0
+	VPXOR      (0*32)(inp), CC3, CC3; VPXOR (1*32)(inp), AA0, AA0; VPXOR (2*32)(inp), BB0, BB0; VPXOR (3*32)(inp), CC0, CC0
+	VMOVDQU    CC3, (0*32)(oup); VMOVDQU AA0, (1*32)(oup); VMOVDQU BB0, (2*32)(oup); VMOVDQU CC0, (3*32)(oup)
+	VPERM2I128 $0x02, AA1, BB1, AA0; VPERM2I128 $0x02, CC1, DD1, BB0; VPERM2I128 $0x13, AA1, BB1, CC0; VPERM2I128 $0x13, CC1, DD1, DD0
+	VPXOR      (4*32)(inp), AA0, AA0; VPXOR (5*32)(inp), BB0, BB0; VPXOR (6*32)(inp), CC0, CC0; VPXOR (7*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (4*32)(oup); VMOVDQU BB0, (5*32)(oup); VMOVDQU CC0, (6*32)(oup); VMOVDQU DD0, (7*32)(oup)
+
+	// and here
+	polyAdd(-2*8(oup))
+	polyMulAVX2
+	VPERM2I128 $0x02, AA2, BB2, AA0; VPERM2I128 $0x02, CC2, DD2, BB0; VPERM2I128 $0x13, AA2, BB2, CC0; VPERM2I128 $0x13, CC2, DD2, DD0
+	VPXOR      (8*32)(inp), AA0, AA0; VPXOR (9*32)(inp), BB0, BB0; VPXOR (10*32)(inp), CC0, CC0; VPXOR (11*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (8*32)(oup); VMOVDQU BB0, (9*32)(oup); VMOVDQU CC0, (10*32)(oup); VMOVDQU DD0, (11*32)(oup)
+	VPERM2I128 $0x02, AA3, BB3, AA0; VPERM2I128 $0x02, tmpStoreAVX2, DD3, BB0; VPERM2I128 $0x13, AA3, BB3, CC0; VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0
+	VPXOR      (12*32)(inp), AA0, AA0; VPXOR (13*32)(inp), BB0, BB0; VPXOR (14*32)(inp), CC0, CC0; VPXOR (15*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (12*32)(oup); VMOVDQU BB0, (13*32)(oup); VMOVDQU CC0, (14*32)(oup); VMOVDQU DD0, (15*32)(oup)
+	LEAQ       (32*16)(inp), inp
+	SUBQ       $(32*16), inl
+	CMPQ       inl, $512
+	JG         sealAVX2MainLoop
+
+	// Tail can only hash 480 bytes
+	polyAdd(0*8(oup))
+	polyMulAVX2
+	polyAdd(2*8(oup))
+	polyMulAVX2
+	LEAQ 32(oup), oup
+
+	MOVQ $10, itr1
+	MOVQ $0, itr2
+	CMPQ inl, $128
+	JBE  sealAVX2Tail128
+	CMPQ inl, $256
+	JBE  sealAVX2Tail256
+	CMPQ inl, $384
+	JBE  sealAVX2Tail384
+	JMP  sealAVX2Tail512
+
+// ----------------------------------------------------------------------------
+// Special optimization for buffers smaller than 193 bytes
+seal192AVX2:
+	// For up to 192 bytes of ciphertext and 64 bytes for the poly key, we process four blocks
+	VMOVDQA AA0, AA1
+	VMOVDQA BB0, BB1
+	VMOVDQA CC0, CC1
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD1
+	VMOVDQA AA0, AA2
+	VMOVDQA BB0, BB2
+	VMOVDQA CC0, CC2
+	VMOVDQA DD0, DD2
+	VMOVDQA DD1, TT3
+	MOVQ    $10, itr2
+
+sealAVX2192InnerCipherLoop:
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0)
+	VPALIGNR   $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1
+	VPALIGNR   $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1
+	VPALIGNR   $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0)
+	VPALIGNR   $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1
+	VPALIGNR   $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1
+	VPALIGNR   $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1
+	DECQ       itr2
+	JNE        sealAVX2192InnerCipherLoop
+	VPADDD     AA2, AA0, AA0; VPADDD AA2, AA1, AA1
+	VPADDD     BB2, BB0, BB0; VPADDD BB2, BB1, BB1
+	VPADDD     CC2, CC0, CC0; VPADDD CC2, CC1, CC1
+	VPADDD     DD2, DD0, DD0; VPADDD TT3, DD1, DD1
+	VPERM2I128 $0x02, AA0, BB0, TT0
+
+	// Clamp and store poly key
+	VPAND   ·polyClampMask<>(SB), TT0, TT0
+	VMOVDQA TT0, rsStoreAVX2
+
+	// Stream for up to 192 bytes
+	VPERM2I128 $0x13, AA0, BB0, AA0
+	VPERM2I128 $0x13, CC0, DD0, BB0
+	VPERM2I128 $0x02, AA1, BB1, CC0
+	VPERM2I128 $0x02, CC1, DD1, DD0
+	VPERM2I128 $0x13, AA1, BB1, AA1
+	VPERM2I128 $0x13, CC1, DD1, BB1
+
+sealAVX2ShortSeal:
+	// Hash aad
+	MOVQ ad_len+80(FP), itr2
+	CALL polyHashADInternal(SB)
+	XORQ itr1, itr1
+
+sealAVX2SealHash:
+	// itr1 holds the number of bytes encrypted but not yet hashed
+	CMPQ itr1, $16
+	JB   sealAVX2ShortSealLoop
+	polyAdd(0(oup))
+	polyMul
+	SUBQ $16, itr1
+	ADDQ $16, oup
+	JMP  sealAVX2SealHash
+
+sealAVX2ShortSealLoop:
+	CMPQ inl, $32
+	JB   sealAVX2ShortTail32
+	SUBQ $32, inl
+
+	// Load for encryption
+	VPXOR   (inp), AA0, AA0
+	VMOVDQU AA0, (oup)
+	LEAQ    (1*32)(inp), inp
+
+	// Now can hash
+	polyAdd(0*8(oup))
+	polyMulAVX2
+	polyAdd(2*8(oup))
+	polyMulAVX2
+	LEAQ (1*32)(oup), oup
+
+	// Shift stream left
+	VMOVDQA BB0, AA0
+	VMOVDQA CC0, BB0
+	VMOVDQA DD0, CC0
+	VMOVDQA AA1, DD0
+	VMOVDQA BB1, AA1
+	VMOVDQA CC1, BB1
+	VMOVDQA DD1, CC1
+	VMOVDQA AA2, DD1
+	VMOVDQA BB2, AA2
+	JMP     sealAVX2ShortSealLoop
+
+sealAVX2ShortTail32:
+	CMPQ    inl, $16
+	VMOVDQA A0, A1
+	JB      sealAVX2ShortDone
+
+	SUBQ $16, inl
+
+	// Load for encryption
+	VPXOR   (inp), A0, T0
+	VMOVDQU T0, (oup)
+	LEAQ    (1*16)(inp), inp
+
+	// Hash
+	polyAdd(0*8(oup))
+	polyMulAVX2
+	LEAQ       (1*16)(oup), oup
+	VPERM2I128 $0x11, AA0, AA0, AA0
+	VMOVDQA    A0, A1
+
+sealAVX2ShortDone:
+	VZEROUPPER
+	JMP sealSSETail
+
+// ----------------------------------------------------------------------------
+// Special optimization for buffers smaller than 321 bytes
+seal320AVX2:
+	// For up to 320 bytes of ciphertext and 64 bytes for the poly key, we process six blocks
+	VMOVDQA AA0, AA1; VMOVDQA BB0, BB1; VMOVDQA CC0, CC1; VPADDD ·avx2IncMask<>(SB), DD0, DD1
+	VMOVDQA AA0, AA2; VMOVDQA BB0, BB2; VMOVDQA CC0, CC2; VPADDD ·avx2IncMask<>(SB), DD1, DD2
+	VMOVDQA BB0, TT1; VMOVDQA CC0, TT2; VMOVDQA DD0, TT3
+	MOVQ    $10, itr2
+
+sealAVX2320InnerCipherLoop:
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0)
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2
+	VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0)
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2
+	VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2
+	DECQ     itr2
+	JNE      sealAVX2320InnerCipherLoop
+
+	VMOVDQA ·chacha20Constants<>(SB), TT0
+	VPADDD  TT0, AA0, AA0; VPADDD TT0, AA1, AA1; VPADDD TT0, AA2, AA2
+	VPADDD  TT1, BB0, BB0; VPADDD TT1, BB1, BB1; VPADDD TT1, BB2, BB2
+	VPADDD  TT2, CC0, CC0; VPADDD TT2, CC1, CC1; VPADDD TT2, CC2, CC2
+	VMOVDQA ·avx2IncMask<>(SB), TT0
+	VPADDD  TT3, DD0, DD0; VPADDD TT0, TT3, TT3
+	VPADDD  TT3, DD1, DD1; VPADDD TT0, TT3, TT3
+	VPADDD  TT3, DD2, DD2
+
+	// Clamp and store poly key
+	VPERM2I128 $0x02, AA0, BB0, TT0
+	VPAND      ·polyClampMask<>(SB), TT0, TT0
+	VMOVDQA    TT0, rsStoreAVX2
+
+	// Stream for up to 320 bytes
+	VPERM2I128 $0x13, AA0, BB0, AA0
+	VPERM2I128 $0x13, CC0, DD0, BB0
+	VPERM2I128 $0x02, AA1, BB1, CC0
+	VPERM2I128 $0x02, CC1, DD1, DD0
+	VPERM2I128 $0x13, AA1, BB1, AA1
+	VPERM2I128 $0x13, CC1, DD1, BB1
+	VPERM2I128 $0x02, AA2, BB2, CC1
+	VPERM2I128 $0x02, CC2, DD2, DD1
+	VPERM2I128 $0x13, AA2, BB2, AA2
+	VPERM2I128 $0x13, CC2, DD2, BB2
+	JMP        sealAVX2ShortSeal
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 128 bytes of ciphertext
+sealAVX2Tail128:
+	// Need to decrypt up to 128 bytes - prepare two blocks
+	// If we got here after the main loop - there are 512 encrypted bytes waiting to be hashed
+	// If we got here before the main loop - there are 448 encrpyred bytes waiting to be hashed
+	VMOVDQA ·chacha20Constants<>(SB), AA0
+	VMOVDQA state1StoreAVX2, BB0
+	VMOVDQA state2StoreAVX2, CC0
+	VMOVDQA ctr3StoreAVX2, DD0
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD0
+	VMOVDQA DD0, DD1
+
+sealAVX2Tail128LoopA:
+	polyAdd(0(oup))
+	polyMul
+	LEAQ 16(oup), oup
+
+sealAVX2Tail128LoopB:
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0)
+	polyAdd(0(oup))
+	polyMul
+	VPALIGNR $4, BB0, BB0, BB0
+	VPALIGNR $8, CC0, CC0, CC0
+	VPALIGNR $12, DD0, DD0, DD0
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0)
+	polyAdd(16(oup))
+	polyMul
+	LEAQ     32(oup), oup
+	VPALIGNR $12, BB0, BB0, BB0
+	VPALIGNR $8, CC0, CC0, CC0
+	VPALIGNR $4, DD0, DD0, DD0
+	DECQ     itr1
+	JG       sealAVX2Tail128LoopA
+	DECQ     itr2
+	JGE      sealAVX2Tail128LoopB
+
+	VPADDD ·chacha20Constants<>(SB), AA0, AA1
+	VPADDD state1StoreAVX2, BB0, BB1
+	VPADDD state2StoreAVX2, CC0, CC1
+	VPADDD DD1, DD0, DD1
+
+	VPERM2I128 $0x02, AA1, BB1, AA0
+	VPERM2I128 $0x02, CC1, DD1, BB0
+	VPERM2I128 $0x13, AA1, BB1, CC0
+	VPERM2I128 $0x13, CC1, DD1, DD0
+	JMP        sealAVX2ShortSealLoop
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 256 bytes of ciphertext
+sealAVX2Tail256:
+	// Need to decrypt up to 256 bytes - prepare two blocks
+	// If we got here after the main loop - there are 512 encrypted bytes waiting to be hashed
+	// If we got here before the main loop - there are 448 encrpyred bytes waiting to be hashed
+	VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA ·chacha20Constants<>(SB), AA1
+	VMOVDQA state1StoreAVX2, BB0; VMOVDQA state1StoreAVX2, BB1
+	VMOVDQA state2StoreAVX2, CC0; VMOVDQA state2StoreAVX2, CC1
+	VMOVDQA ctr3StoreAVX2, DD0
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD0
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD1
+	VMOVDQA DD0, TT1
+	VMOVDQA DD1, TT2
+
+sealAVX2Tail256LoopA:
+	polyAdd(0(oup))
+	polyMul
+	LEAQ 16(oup), oup
+
+sealAVX2Tail256LoopB:
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0)
+	polyAdd(0(oup))
+	polyMul
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1
+	VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0)
+	polyAdd(16(oup))
+	polyMul
+	LEAQ     32(oup), oup
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1
+	VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1
+	DECQ     itr1
+	JG       sealAVX2Tail256LoopA
+	DECQ     itr2
+	JGE      sealAVX2Tail256LoopB
+
+	VPADDD     ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1
+	VPADDD     state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1
+	VPADDD     state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1
+	VPADDD     TT1, DD0, DD0; VPADDD TT2, DD1, DD1
+	VPERM2I128 $0x02, AA0, BB0, TT0
+	VPERM2I128 $0x02, CC0, DD0, TT1
+	VPERM2I128 $0x13, AA0, BB0, TT2
+	VPERM2I128 $0x13, CC0, DD0, TT3
+	VPXOR      (0*32)(inp), TT0, TT0; VPXOR (1*32)(inp), TT1, TT1; VPXOR (2*32)(inp), TT2, TT2; VPXOR (3*32)(inp), TT3, TT3
+	VMOVDQU    TT0, (0*32)(oup); VMOVDQU TT1, (1*32)(oup); VMOVDQU TT2, (2*32)(oup); VMOVDQU TT3, (3*32)(oup)
+	MOVQ       $128, itr1
+	LEAQ       128(inp), inp
+	SUBQ       $128, inl
+	VPERM2I128 $0x02, AA1, BB1, AA0
+	VPERM2I128 $0x02, CC1, DD1, BB0
+	VPERM2I128 $0x13, AA1, BB1, CC0
+	VPERM2I128 $0x13, CC1, DD1, DD0
+
+	JMP sealAVX2SealHash
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 384 bytes of ciphertext
+sealAVX2Tail384:
+	// Need to decrypt up to 384 bytes - prepare two blocks
+	// If we got here after the main loop - there are 512 encrypted bytes waiting to be hashed
+	// If we got here before the main loop - there are 448 encrpyred bytes waiting to be hashed
+	VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2
+	VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2
+	VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2
+	VMOVDQA ctr3StoreAVX2, DD0
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2
+	VMOVDQA DD0, TT1; VMOVDQA DD1, TT2; VMOVDQA DD2, TT3
+
+sealAVX2Tail384LoopA:
+	polyAdd(0(oup))
+	polyMul
+	LEAQ 16(oup), oup
+
+sealAVX2Tail384LoopB:
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0)
+	polyAdd(0(oup))
+	polyMul
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2
+	VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2
+	chachaQR_AVX2(AA0, BB0, CC0, DD0, TT0); chachaQR_AVX2(AA1, BB1, CC1, DD1, TT0); chachaQR_AVX2(AA2, BB2, CC2, DD2, TT0)
+	polyAdd(16(oup))
+	polyMul
+	LEAQ     32(oup), oup
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2
+	VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2
+	DECQ     itr1
+	JG       sealAVX2Tail384LoopA
+	DECQ     itr2
+	JGE      sealAVX2Tail384LoopB
+
+	VPADDD     ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2
+	VPADDD     state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2
+	VPADDD     state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2
+	VPADDD     TT1, DD0, DD0; VPADDD TT2, DD1, DD1; VPADDD TT3, DD2, DD2
+	VPERM2I128 $0x02, AA0, BB0, TT0
+	VPERM2I128 $0x02, CC0, DD0, TT1
+	VPERM2I128 $0x13, AA0, BB0, TT2
+	VPERM2I128 $0x13, CC0, DD0, TT3
+	VPXOR      (0*32)(inp), TT0, TT0; VPXOR (1*32)(inp), TT1, TT1; VPXOR (2*32)(inp), TT2, TT2; VPXOR (3*32)(inp), TT3, TT3
+	VMOVDQU    TT0, (0*32)(oup); VMOVDQU TT1, (1*32)(oup); VMOVDQU TT2, (2*32)(oup); VMOVDQU TT3, (3*32)(oup)
+	VPERM2I128 $0x02, AA1, BB1, TT0
+	VPERM2I128 $0x02, CC1, DD1, TT1
+	VPERM2I128 $0x13, AA1, BB1, TT2
+	VPERM2I128 $0x13, CC1, DD1, TT3
+	VPXOR      (4*32)(inp), TT0, TT0; VPXOR (5*32)(inp), TT1, TT1; VPXOR (6*32)(inp), TT2, TT2; VPXOR (7*32)(inp), TT3, TT3
+	VMOVDQU    TT0, (4*32)(oup); VMOVDQU TT1, (5*32)(oup); VMOVDQU TT2, (6*32)(oup); VMOVDQU TT3, (7*32)(oup)
+	MOVQ       $256, itr1
+	LEAQ       256(inp), inp
+	SUBQ       $256, inl
+	VPERM2I128 $0x02, AA2, BB2, AA0
+	VPERM2I128 $0x02, CC2, DD2, BB0
+	VPERM2I128 $0x13, AA2, BB2, CC0
+	VPERM2I128 $0x13, CC2, DD2, DD0
+
+	JMP sealAVX2SealHash
+
+// ----------------------------------------------------------------------------
+// Special optimization for the last 512 bytes of ciphertext
+sealAVX2Tail512:
+	// Need to decrypt up to 512 bytes - prepare two blocks
+	// If we got here after the main loop - there are 512 encrypted bytes waiting to be hashed
+	// If we got here before the main loop - there are 448 encrpyred bytes waiting to be hashed
+	VMOVDQA ·chacha20Constants<>(SB), AA0; VMOVDQA AA0, AA1; VMOVDQA AA0, AA2; VMOVDQA AA0, AA3
+	VMOVDQA state1StoreAVX2, BB0; VMOVDQA BB0, BB1; VMOVDQA BB0, BB2; VMOVDQA BB0, BB3
+	VMOVDQA state2StoreAVX2, CC0; VMOVDQA CC0, CC1; VMOVDQA CC0, CC2; VMOVDQA CC0, CC3
+	VMOVDQA ctr3StoreAVX2, DD0
+	VPADDD  ·avx2IncMask<>(SB), DD0, DD0; VPADDD ·avx2IncMask<>(SB), DD0, DD1; VPADDD ·avx2IncMask<>(SB), DD1, DD2; VPADDD ·avx2IncMask<>(SB), DD2, DD3
+	VMOVDQA DD0, ctr0StoreAVX2; VMOVDQA DD1, ctr1StoreAVX2; VMOVDQA DD2, ctr2StoreAVX2; VMOVDQA DD3, ctr3StoreAVX2
+
+sealAVX2Tail512LoopA:
+	polyAdd(0(oup))
+	polyMul
+	LEAQ 16(oup), oup
+
+sealAVX2Tail512LoopB:
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	polyAdd(0*8(oup))
+	polyMulAVX2
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	VPALIGNR $4, BB0, BB0, BB0; VPALIGNR $4, BB1, BB1, BB1; VPALIGNR $4, BB2, BB2, BB2; VPALIGNR $4, BB3, BB3, BB3
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3
+	VPALIGNR $12, DD0, DD0, DD0; VPALIGNR $12, DD1, DD1, DD1; VPALIGNR $12, DD2, DD2, DD2; VPALIGNR $12, DD3, DD3, DD3
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol16<>(SB), DD0, DD0; VPSHUFB ·rol16<>(SB), DD1, DD1; VPSHUFB ·rol16<>(SB), DD2, DD2; VPSHUFB ·rol16<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	polyAdd(2*8(oup))
+	polyMulAVX2
+	LEAQ     (4*8)(oup), oup
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $12, BB0, CC3; VPSRLD $20, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $12, BB1, CC3; VPSRLD $20, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $12, BB2, CC3; VPSRLD $20, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $12, BB3, CC3; VPSRLD $20, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	VPADDD   BB0, AA0, AA0; VPADDD BB1, AA1, AA1; VPADDD BB2, AA2, AA2; VPADDD BB3, AA3, AA3
+	VPXOR    AA0, DD0, DD0; VPXOR AA1, DD1, DD1; VPXOR AA2, DD2, DD2; VPXOR AA3, DD3, DD3
+	VPSHUFB  ·rol8<>(SB), DD0, DD0; VPSHUFB ·rol8<>(SB), DD1, DD1; VPSHUFB ·rol8<>(SB), DD2, DD2; VPSHUFB ·rol8<>(SB), DD3, DD3
+	VPADDD   DD0, CC0, CC0; VPADDD DD1, CC1, CC1; VPADDD DD2, CC2, CC2; VPADDD DD3, CC3, CC3
+	VPXOR    CC0, BB0, BB0; VPXOR CC1, BB1, BB1; VPXOR CC2, BB2, BB2; VPXOR CC3, BB3, BB3
+	VMOVDQA  CC3, tmpStoreAVX2
+	VPSLLD   $7, BB0, CC3; VPSRLD $25, BB0, BB0; VPXOR CC3, BB0, BB0
+	VPSLLD   $7, BB1, CC3; VPSRLD $25, BB1, BB1; VPXOR CC3, BB1, BB1
+	VPSLLD   $7, BB2, CC3; VPSRLD $25, BB2, BB2; VPXOR CC3, BB2, BB2
+	VPSLLD   $7, BB3, CC3; VPSRLD $25, BB3, BB3; VPXOR CC3, BB3, BB3
+	VMOVDQA  tmpStoreAVX2, CC3
+	VPALIGNR $12, BB0, BB0, BB0; VPALIGNR $12, BB1, BB1, BB1; VPALIGNR $12, BB2, BB2, BB2; VPALIGNR $12, BB3, BB3, BB3
+	VPALIGNR $8, CC0, CC0, CC0; VPALIGNR $8, CC1, CC1, CC1; VPALIGNR $8, CC2, CC2, CC2; VPALIGNR $8, CC3, CC3, CC3
+	VPALIGNR $4, DD0, DD0, DD0; VPALIGNR $4, DD1, DD1, DD1; VPALIGNR $4, DD2, DD2, DD2; VPALIGNR $4, DD3, DD3, DD3
+
+	DECQ itr1
+	JG   sealAVX2Tail512LoopA
+	DECQ itr2
+	JGE  sealAVX2Tail512LoopB
+
+	VPADDD     ·chacha20Constants<>(SB), AA0, AA0; VPADDD ·chacha20Constants<>(SB), AA1, AA1; VPADDD ·chacha20Constants<>(SB), AA2, AA2; VPADDD ·chacha20Constants<>(SB), AA3, AA3
+	VPADDD     state1StoreAVX2, BB0, BB0; VPADDD state1StoreAVX2, BB1, BB1; VPADDD state1StoreAVX2, BB2, BB2; VPADDD state1StoreAVX2, BB3, BB3
+	VPADDD     state2StoreAVX2, CC0, CC0; VPADDD state2StoreAVX2, CC1, CC1; VPADDD state2StoreAVX2, CC2, CC2; VPADDD state2StoreAVX2, CC3, CC3
+	VPADDD     ctr0StoreAVX2, DD0, DD0; VPADDD ctr1StoreAVX2, DD1, DD1; VPADDD ctr2StoreAVX2, DD2, DD2; VPADDD ctr3StoreAVX2, DD3, DD3
+	VMOVDQA    CC3, tmpStoreAVX2
+	VPERM2I128 $0x02, AA0, BB0, CC3
+	VPXOR      (0*32)(inp), CC3, CC3
+	VMOVDQU    CC3, (0*32)(oup)
+	VPERM2I128 $0x02, CC0, DD0, CC3
+	VPXOR      (1*32)(inp), CC3, CC3
+	VMOVDQU    CC3, (1*32)(oup)
+	VPERM2I128 $0x13, AA0, BB0, CC3
+	VPXOR      (2*32)(inp), CC3, CC3
+	VMOVDQU    CC3, (2*32)(oup)
+	VPERM2I128 $0x13, CC0, DD0, CC3
+	VPXOR      (3*32)(inp), CC3, CC3
+	VMOVDQU    CC3, (3*32)(oup)
+
+	VPERM2I128 $0x02, AA1, BB1, AA0
+	VPERM2I128 $0x02, CC1, DD1, BB0
+	VPERM2I128 $0x13, AA1, BB1, CC0
+	VPERM2I128 $0x13, CC1, DD1, DD0
+	VPXOR      (4*32)(inp), AA0, AA0; VPXOR (5*32)(inp), BB0, BB0; VPXOR (6*32)(inp), CC0, CC0; VPXOR (7*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (4*32)(oup); VMOVDQU BB0, (5*32)(oup); VMOVDQU CC0, (6*32)(oup); VMOVDQU DD0, (7*32)(oup)
+
+	VPERM2I128 $0x02, AA2, BB2, AA0
+	VPERM2I128 $0x02, CC2, DD2, BB0
+	VPERM2I128 $0x13, AA2, BB2, CC0
+	VPERM2I128 $0x13, CC2, DD2, DD0
+	VPXOR      (8*32)(inp), AA0, AA0; VPXOR (9*32)(inp), BB0, BB0; VPXOR (10*32)(inp), CC0, CC0; VPXOR (11*32)(inp), DD0, DD0
+	VMOVDQU    AA0, (8*32)(oup); VMOVDQU BB0, (9*32)(oup); VMOVDQU CC0, (10*32)(oup); VMOVDQU DD0, (11*32)(oup)
+
+	MOVQ       $384, itr1
+	LEAQ       384(inp), inp
+	SUBQ       $384, inl
+	VPERM2I128 $0x02, AA3, BB3, AA0
+	VPERM2I128 $0x02, tmpStoreAVX2, DD3, BB0
+	VPERM2I128 $0x13, AA3, BB3, CC0
+	VPERM2I128 $0x13, tmpStoreAVX2, DD3, DD0
+
+	JMP sealAVX2SealHash
+
+// func haveSSSE3() bool
+TEXT ·haveSSSE3(SB), NOSPLIT, $0
+	XORQ AX, AX
+	INCL AX
+	CPUID
+	SHRQ $9, CX
+	ANDQ $1, CX
+	MOVB CX, ret+0(FP)
+	RET
+
diff --git a/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go
new file mode 100644
index 0000000..b9a55ea
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go
@@ -0,0 +1,70 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package chacha20poly1305
+
+import (
+	"encoding/binary"
+
+	"golang_org/x/crypto/chacha20poly1305/internal/chacha20"
+	"golang_org/x/crypto/poly1305"
+)
+
+func roundTo16(n int) int {
+	return 16 * ((n + 15) / 16)
+}
+
+func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte {
+	var counter [16]byte
+	copy(counter[4:], nonce)
+
+	var polyKey [32]byte
+	chacha20.XORKeyStream(polyKey[:], polyKey[:], &counter, &c.key)
+
+	ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
+	counter[0] = 1
+	chacha20.XORKeyStream(out, plaintext, &counter, &c.key)
+
+	polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(plaintext))+8+8)
+	copy(polyInput, additionalData)
+	copy(polyInput[roundTo16(len(additionalData)):], out[:len(plaintext)])
+	binary.LittleEndian.PutUint64(polyInput[len(polyInput)-16:], uint64(len(additionalData)))
+	binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(plaintext)))
+
+	var tag [poly1305.TagSize]byte
+	poly1305.Sum(&tag, polyInput, &polyKey)
+	copy(out[len(plaintext):], tag[:])
+
+	return ret
+}
+
+func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+	var tag [poly1305.TagSize]byte
+	copy(tag[:], ciphertext[len(ciphertext)-16:])
+	ciphertext = ciphertext[:len(ciphertext)-16]
+
+	var counter [16]byte
+	copy(counter[4:], nonce)
+
+	var polyKey [32]byte
+	chacha20.XORKeyStream(polyKey[:], polyKey[:], &counter, &c.key)
+
+	polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(ciphertext))+8+8)
+	copy(polyInput, additionalData)
+	copy(polyInput[roundTo16(len(additionalData)):], ciphertext)
+	binary.LittleEndian.PutUint64(polyInput[len(polyInput)-16:], uint64(len(additionalData)))
+	binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(ciphertext)))
+
+	ret, out := sliceForAppend(dst, len(ciphertext))
+	if !poly1305.Verify(&tag, polyInput, &polyKey) {
+		for i := range out {
+			out[i] = 0
+		}
+		return nil, errOpen
+	}
+
+	counter[0] = 1
+	chacha20.XORKeyStream(out, ciphertext, &counter, &c.key)
+	return ret, nil
+}
diff --git a/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go
new file mode 100644
index 0000000..1d4dcd3
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go
@@ -0,0 +1,15 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64 !go1.7
+
+package chacha20poly1305
+
+func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
+	return c.sealGeneric(dst, nonce, plaintext, additionalData)
+}
+
+func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+	return c.openGeneric(dst, nonce, ciphertext, additionalData)
+}
diff --git a/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_test.go b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_test.go
new file mode 100644
index 0000000..78f981a
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_test.go
@@ -0,0 +1,182 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package chacha20poly1305
+
+import (
+	"bytes"
+	cr "crypto/rand"
+	"encoding/hex"
+	mr "math/rand"
+	"testing"
+)
+
+func TestVectors(t *testing.T) {
+	for i, test := range chacha20Poly1305Tests {
+		key, _ := hex.DecodeString(test.key)
+		nonce, _ := hex.DecodeString(test.nonce)
+		ad, _ := hex.DecodeString(test.aad)
+		plaintext, _ := hex.DecodeString(test.plaintext)
+
+		aead, err := New(key)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		ct := aead.Seal(nil, nonce, plaintext, ad)
+		if ctHex := hex.EncodeToString(ct); ctHex != test.out {
+			t.Errorf("#%d: got %s, want %s", i, ctHex, test.out)
+			continue
+		}
+
+		plaintext2, err := aead.Open(nil, nonce, ct, ad)
+		if err != nil {
+			t.Errorf("#%d: Open failed", i)
+			continue
+		}
+
+		if !bytes.Equal(plaintext, plaintext2) {
+			t.Errorf("#%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
+			continue
+		}
+
+		if len(ad) > 0 {
+			alterAdIdx := mr.Intn(len(ad))
+			ad[alterAdIdx] ^= 0x80
+			if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
+				t.Errorf("#%d: Open was successful after altering additional data", i)
+			}
+			ad[alterAdIdx] ^= 0x80
+		}
+
+		alterNonceIdx := mr.Intn(aead.NonceSize())
+		nonce[alterNonceIdx] ^= 0x80
+		if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
+			t.Errorf("#%d: Open was successful after altering nonce", i)
+		}
+		nonce[alterNonceIdx] ^= 0x80
+
+		alterCtIdx := mr.Intn(len(ct))
+		ct[alterCtIdx] ^= 0x80
+		if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
+			t.Errorf("#%d: Open was successful after altering ciphertext", i)
+		}
+		ct[alterCtIdx] ^= 0x80
+	}
+}
+
+func TestRandom(t *testing.T) {
+	// Some random tests to verify Open(Seal) == Plaintext
+	for i := 0; i < 256; i++ {
+		var nonce [12]byte
+		var key [32]byte
+
+		al := mr.Intn(128)
+		pl := mr.Intn(16384)
+		ad := make([]byte, al)
+		plaintext := make([]byte, pl)
+		cr.Read(key[:])
+		cr.Read(nonce[:])
+		cr.Read(ad)
+		cr.Read(plaintext)
+
+		aead, err := New(key[:])
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		ct := aead.Seal(nil, nonce[:], plaintext, ad)
+
+		plaintext2, err := aead.Open(nil, nonce[:], ct, ad)
+		if err != nil {
+			t.Errorf("Random #%d: Open failed", i)
+			continue
+		}
+
+		if !bytes.Equal(plaintext, plaintext2) {
+			t.Errorf("Random #%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
+			continue
+		}
+
+		if len(ad) > 0 {
+			alterAdIdx := mr.Intn(len(ad))
+			ad[alterAdIdx] ^= 0x80
+			if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
+				t.Errorf("Random #%d: Open was successful after altering additional data", i)
+			}
+			ad[alterAdIdx] ^= 0x80
+		}
+
+		alterNonceIdx := mr.Intn(aead.NonceSize())
+		nonce[alterNonceIdx] ^= 0x80
+		if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
+			t.Errorf("Random #%d: Open was successful after altering nonce", i)
+		}
+		nonce[alterNonceIdx] ^= 0x80
+
+		alterCtIdx := mr.Intn(len(ct))
+		ct[alterCtIdx] ^= 0x80
+		if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
+			t.Errorf("Random #%d: Open was successful after altering ciphertext", i)
+		}
+		ct[alterCtIdx] ^= 0x80
+	}
+}
+
+func benchamarkChaCha20Poly1305Seal(b *testing.B, buf []byte) {
+	b.SetBytes(int64(len(buf)))
+
+	var key [32]byte
+	var nonce [12]byte
+	var ad [13]byte
+	var out []byte
+
+	aead, _ := New(key[:])
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		out = aead.Seal(out[:0], nonce[:], buf[:], ad[:])
+	}
+}
+
+func benchamarkChaCha20Poly1305Open(b *testing.B, buf []byte) {
+	b.SetBytes(int64(len(buf)))
+
+	var key [32]byte
+	var nonce [12]byte
+	var ad [13]byte
+	var ct []byte
+	var out []byte
+
+	aead, _ := New(key[:])
+	ct = aead.Seal(ct[:0], nonce[:], buf[:], ad[:])
+
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		out, _ = aead.Open(out[:0], nonce[:], ct[:], ad[:])
+	}
+}
+
+func BenchmarkChacha20Poly1305Open_64(b *testing.B) {
+	benchamarkChaCha20Poly1305Open(b, make([]byte, 64))
+}
+
+func BenchmarkChacha20Poly1305Seal_64(b *testing.B) {
+	benchamarkChaCha20Poly1305Seal(b, make([]byte, 64))
+}
+
+func BenchmarkChacha20Poly1305Open_1350(b *testing.B) {
+	benchamarkChaCha20Poly1305Open(b, make([]byte, 1350))
+}
+
+func BenchmarkChacha20Poly1305Seal_1350(b *testing.B) {
+	benchamarkChaCha20Poly1305Seal(b, make([]byte, 1350))
+}
+
+func BenchmarkChacha20Poly1305Open_8K(b *testing.B) {
+	benchamarkChaCha20Poly1305Open(b, make([]byte, 8*1024))
+}
+
+func BenchmarkChacha20Poly1305Seal_8K(b *testing.B) {
+	benchamarkChaCha20Poly1305Seal(b, make([]byte, 8*1024))
+}
diff --git a/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_test_vectors.go b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_test_vectors.go
new file mode 100644
index 0000000..49f0da6
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/chacha20poly1305/chacha20poly1305_test_vectors.go
@@ -0,0 +1,332 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package chacha20poly1305
+
+var chacha20Poly1305Tests = []struct {
+	plaintext, aad, key, nonce, out string
+}{
+	{
+		"4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e",
+		"50515253c0c1c2c3c4c5c6c7",
+		"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+		"070000004041424344454647",
+		"d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd0600691",
+	},
+	{
+		"1400000cebccee3bf561b292340fec60",
+		"00000000000000001603030010",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"2b487a2941bc07f3cc76d1a531662588ee7c2598e59778c24d5b27559a80d163",
+	},
+	{
+		"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 [...]
+		"00000000000000000000000000",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"3f487a25aa70e9c8391763370569c9e83b7650dd1921c8b78869f241f25d2096c910b180930c5b8747fd90959fe8ca2dcadb4fa50fa1439f916b2301e1cc0810d6725775d3ab86721700f96e22709b0a7a8bef32627dd929b2dd3ba15772b669062bb558bc92e6c241a1d60d9f0035e80c335f854815fe1138ab8af653eab3e122135feeec7dfaba1cc24af82a2b7acccdd824899a7e03cc29c25be8a4f56a66673845b93bae1556f09dafc89a0d22af207718e2a6bb022e9d917597295992ea3b750cc0e7a7c3d33b23c5a8aeab45f5bb542f6c9e6c1747ae5a344aff483ba38577ad534b33b3abc7d284776ea33ed488c2a2475 [...]
+	},
+	{
+		"0967de57eefe1aaa999b9b746d88a1a248000d8734e0e938c6aa87",
+		"e4f0a3a4f90a8250f8806aa319053e8d73c62f150e2f239563037e9cc92823ad18c65111d0d462c954cc6c6ed2aafb45702a5a7e597d13bd8091594ab97cf7d1",
+		"f2db28620582e05f00f31c808475ca3df1c20e340bf14828352499466d79295f",
+		"4349e2131d44dc711148dfe3",
+		"bd06cc144fdc0d8b735fa4452eabbf78fd4ad2966ea41a84f68da40ca2da439777bc2ba6c4ec2de0d003eb",
+	},
+	{
+		"c4c920fb52a56fe66eaa8aa3fa187c543e3db8e5c8094c4313dc4ed35dfc5821c5791d171e8cfe8d37883031a0ad",
+		"85deea3dc4",
+		"05ff881d1e151bab4ca3db7d44880222733fe62686f71ce1e4610f2ea19599a7",
+		"b34710f65aed442e4a40866b",
+		"b154452fb7e85d175dd0b0db08591565c5587a725cf22386922f5d27a01015aba778975510b38754b2182e24352f019b7ad493e1ed255906715644aec6e0",
+	},
+	{
+		"c4b337df5e83823900c6c202e93541cf5bc8c677a9aad8b8d87a4d7221e294e595cbc4f34e462d4e0def50f62491c57f598cf60236cfba0f4908816aea154f80e013732e59a07c668fcc5cb35d2232b7ae29b9e4f874f3417c74ab6689fae6690d5a9766fa13cd8adf293d3d4b70f4f999adde9121d1d29d467d04cf77ea398444d0ea3fe4b7c9c3e106002c76f4260fa204a0c3d5",
+		"72611bef65eb664f24ea94f4d5d3d88c9c9c6da29c9a1991c02833c4c9f6993b57b5",
+		"dd0f2d4bb1c9e5ca5aa5f38d69bc8402f7dbb7229857b4a41b3044d481b7655e",
+		"2bbca0910cc47ca0b8517391",
+		"83aa28d6d98901e2981d21d3758ae4db8cce07fe08d82ca6f036a68daa88a7dda56eeb38040c942bdda0fd2d369eec44bd070e2c9314992f68dc16989a6ac0c3912c378cf3254f4bae74a66b075e828df6f855c0d8a827ffed3c03582c12a9112eeb7be43dfe8bd78beb2d1e56678b99a0372531727cb7f2b98d2f917ec10de93fe86267100c20356e80528c5066688c8b7acba76e591449952343f663993d5b642e59eb0f",
+	},
+	{
+		"a9775b8e42b63335439cf1c79fe8a3560b3baebfdfc9ef239d70da02cea0947817f00659a63a8ee9d67fb1756854cc738f7a326e432191e1916be35f0b78d72268de7c0e180af7ee8aa864f2fc30658baa97f9edb88ace49f5b2a8002a8023925e9fa076a997643340c8253cf88ac8a221c190d94c5e224110cb423a4b65cca9046c1fad0483e1444c0680449148e7b20a778c56d5ae97e679d920c43eed6d42598cf05d10d1a15cd722a0686a871b74fea7cad45562bacf3bda937ac701bc218dac7e9d7d20f955429abdac21d821207febf4d54daea4898837035038bf71c66cef63e90f5d3e51f7fcfe18d41f38540a2c2958d [...]
+		"74ba3372d308910b5c9c3885f41252d57556",
+		"9cf77bd06a4ed8fb59349791b98ba40b6019611942f5768e8be2ee88477149e3",
+		"b928935c4c966c60fd6583c0",
+		"ec7fd64fd75b254961a2b7fc942470d8620f439258b871d0d00f58028b5e0bee5e139e8108ac439391465d6658f559b1df57aa21cf826ede1a28bc11af885e13eebfc009870928fae8abfdd943a60c54fca93f0502dc23d29c2fd5340f9bc0e6ef2a18b66ef627af95f796d5bbca50de22c8ec802da9397089b25c6ba5262468e3977b45dc112e51896c70731b0a52d7efec7c93b41995823436bf4b0c477ae79684407c9831b487928b2b8303caca752b3edf1f0598e15831155462706f94ef3fa3a9e5f937f37085afa9b4bbf939d275796a61b78f70597acfd25cd87f967021cd99328fc371b5eb5739869520657b30e4a5b0d [...]
+	},
+	{
+		"b3d3128bce6bbf66fd78f1a18352bae56bfcdae18b65c379ee0aeb37ee54fba1270d2df578ec5b75654d16e89fd1cd0acda7ec580dafd2fbbabd32a8112d49383a762db2638928c8d63eb0750f7e7fdd256b35321b072dd5c45f7dd58cc60dc63d3b79a0c4a1689adf180fef968eccbcfa01ee15091ceacd7b67a3082db0ce6aeb470aafe87249c88b58b721e783dde184ccf68de8e05b6347fe6b74ae3adf9a81e9496a5c9332e7ebe908d26ce6b3f0b2a97e9a89d9fdd0d7694585a3241f240d698e69fcc050e7a959ba153f6d06f117848ba05d887134f1b6b994dad9b9e74247513e08a125b1fadfc7394dcd2a6451b504ae3 [...]
+		"7e8da4f3018f673f8e43bd7a1dee05f8031ec49129c361abbc2a434e9eaf791c3c1d0f3dad767d3bba3ab6d728bbcf2bd994bd03571eae1348f161e6a1da03ddf7121ba4",
+		"7ee32dd501dce849cd492f6e23324c1a4567bfceff9f11d1352bcb8615f1b093",
+		"8998e043d2961afa51ea262a",
+		"ba85e72af18cb5ba85a4a0d6c28b4ac1e5509a3a2fdb0e3255cbc559df5e6a661fc560c756a0264dd99b72c61c51a4b7ad56ca4c8ccb7e8edfc48ff3cceac5d1e8ac5fc87096adc4d0e9a27492857b17604c3a694cfe0e70b22df106c8f3c61f840bcd634964cdb571840e125e381e7dd3a0d97972e965f16f775fa4ce555124318290bf508beb7bd77e633042deb0e863631478fc3dc9122862b3c31264471bcce54e0b74040c8bafd481cf798f332e8940f1134d3027d6f28e771d15e154fc89c6c25fe18a5d312807cc2e623bb1bbb4f0b6ec71d009407eb54bb0759f03682f65d0da8812f84d8e97483f6a8d76a8417efcd95 [...]
+	},
+	{
+		"68d5ba501e87994ef6bc8042d7c5a99693a835a4796ad044f0e536a0790a7ee1e03832fec0cb4cb688cdf85f92a1f526492acac2949a0684803c24f947a3da27db0c259bd87251603f49bfd1eab4f733dec2f5725cfcf6dc381ad57fbdb0a699bccc34943e86f47dcfb34eba6746ed4508e3b764dfad4117c8169785c63d1e8309531747d90cc4a8bf13622759506c613324c512d10629991dc01fe3fe3d6607907e4f698a1312492674707fc4dde0f701a609d2ac336cc9f38badf1c813f9599148c21b5bd4658249d5010db2e205b3880e863441f2fe357dab2645be1f9e5067616bc335d0457ea6468c5828910cb09f92e5e18 [...]
+		"63b90dd89066ad7b61cc39497899a8f14399eace1810f5fe3b76d2501f5d8f83169c5ba602082164d45aad4df3553e36ef29050739fa067470d8c58f3554124bf06df1f27612564a6c04976059d69648ff9b50389556ad052e729563c6a7",
+		"7d5c4314a542aff57a454b274a7999dfdc5f878a159c29be27dabdfcf7c06975",
+		"aeb6159fa88bb1ffd51d036d",
+		"7597f7f44191e815a409754db7fea688e0105c987fa065e621823ea6dea617aed613092ad566c487cfa1a93f556615d2a575fb30ac34b11e19cd908d74545906f929dc9e59f6f1e1e6eaaabe182748ef87057ef7820ffcf254c40237d3ea9ff004472db783ed54b5a294a46cf90519bf89367b04fc01ce544c5bcdd3197eb1237923ce2c0c99921ca959c53b54176d292e97f6d9696ded6054711721aebda543e3e077c90e6f216cdc275b86d45603521c5aab24f08fd06833b0743c388382f941e19e0283ac7c4ef22383e1b9b08572882769c1382bab9ad127e7f3e09b5330b82d3e0c7d6f0df46edc93265999eef8e7afa0cb1 [...]
+	},
+	{
+		"89c1ee38b6697d0190c87a2aa756892ee09fca095df1e31aeedbda5750f604d9b8f2116e5b8f70ec57ea16fe419f2d213ef72b9be90eb5d7e98f2e398632123e2524ac80b31c6c0a07820848223569602d94fc16a3b1ed8c411bc6c74ed80573fcb1f3afce60b9d5e2c21d04f78665241b613abe12274a5343101a91e91f04e5d1f7959f574e743a10913e0817a32c320467f0178e3b6ad14b856234a4661a755eaf14b5fd88ef0e192e1631d14263d6a954ed388f5709dadc6c0f81d229f630d80be6d593d5e3ad03f9ded53c41abe595981d24ef27ffcc930e4d653743960f4e7ce4e251c88f55c16d2afdaed5e3446d00685c2 [...]
+		"7219bd21a834d917f93a9b45647ec77102578bc2f2a132dfde6489b9095b4f7b740c9c1c4075333ab0ce7f14",
+		"a7f849b054982cc8a4c8e5e53e181feee79e0233e58882839892134ad582da7c",
+		"4c46854e9e101090b1436f90",
+		"ab2e189baf60886bed88eb751bf3560a8bd3cdb6ee621d8c18b5fb3aa418f350048ecf359a7d542daf7090ec8688c3b0fe85914aa49d83be4ae3396f7bdc48051afae6a97fca7b42c0bf612a42d3c79ef6aadceb57f5cfe8d67f89d49add0ea1ffd423da058297239e72a85fa6cd1d82e243a503b1b0e12d7510a9ee98d7921dae2754d7581e52acb8ab9e7f9df3c73410789115cef6ce7c937a5441ad4edf2b7a8c0c6d152d5a5909c4ce839d59594a6163364038c4c71a1507389717f61e2bda1ea66a83ef477762e7834ebcfaa8f2ee61ced1605ba1380108236e1763bf40af5259da07dd3e3d0fb2801868c2e7c839e318678 [...]
+	},
+	{
+		"2dcfbb59975f217c445f95634d7c0250afe7d8316a70c47dba99ff94167ab74349729ce1d2bd5d161df27a6a6e7cba1e63924fcd03134abdad4952c3c409060d7ca2ee4e5f4c647c3edee7ad5aa1cbbd341a8a372ed4f4db1e469ee250a4efcc46de1aa52a7e22685d0915b7aae075defbff1529d40a04f250a2d4a046c36c8ca18631cb055334625c4919072a8ee5258efb4e6205525455f428f63aeb62c68de9f758ee4b8c50a7d669ae00f89425868f73e894c53ce9b964dff34f42b9dc2bb03519fbc169a397d25197cae5bc50742f3808f474f2add8d1a0281359043e0a395705fbc0a89293fa2a5ddfe6ae5416e65c0a5b4 [...]
+		"33791b0d653fb72c2d88519b02bde85a7c51f99cfb4456dfa6f84a61e10b4a14846521",
+		"a0a7b73ca2fc9282a28acc036bd74d7f5cb2a146577a5c29dbc3963fe7ebfd87",
+		"eaa4d916d261676d632455be",
+		"c9a631de470fd04dcbf8ea9f4d8ac37c3988878b6381707ac2c91d3720edbb31576ba90731f433a5e13582aca2b3c76ae75ca8881a463ecfa789910d3a776a9ad4800521c6baa120b2f1afd10f32ef8da63f5b69f5e5fd88ee84bf66b0666b15d05c4050f5358a050b9d5cf1503719f56cd48ceba78f29efe2ae8092e37f5134df526831532f86ccb9339637e2c9e9b9036f83cc058fda23e826a188456e7fd3f4ee20f4e4a3221883fe3232b49db607b90a8956133ab95051c9ec33a908ea7e81a1bfa7bd06c09f0143d07bb23a3feeac7f0d7720269c93e2df19d03605828c8713b84d183c9a50954c12fe3b047511ad15ef03a [...]
+	},
+	{
+		"c335b055b752e083554b5aa2cbb6556cfcace658d5c11b6b000256fd89e9b24c1e62a2d5b582580acdb2ad9869020465aeeabe83acd9eeacdc44aa652d5cb24bbe542073d6787ea32b2b3c942d40f9db2bb75ed7914c836d902dd2be89840948d82abbaea23952cd648e6191ce5b6cf912cad0a3165410a781e3650b676e5340980eee3b484008acce6a3e9dc5aa96d775677b8bbb8b323c6e9747d6069a169ea904d9f145e29d134cdbb0118647e8fbae638669efb9a55d50ed33568749f5304ece2193b0bfa6fc9a570d209ef61b4c59a2b5485b5aa6ab47d902cf23f7ff71c5210476e0aa727a01809b9f76b6ebcf58a018b3f [...]
+		"f5ff810a41d4b34751e9942970d4c9f26b33f24689a4b1e4449b243490afc485af468ff01a42376b2bcb949b9f5e8d0b917f511a",
+		"a74271c184a82cb074c14b131fd91eb05870cb7c73c9e511ec8140bfe2f34089",
+		"2403fe689e239c2ed261b381",
+		"af9be893d5fd23aab42e6a2e59a8e7cb13d4f543db02af87cb0802bc1af7c717cd0093cc8244994cf21189146922b69927ffd5745e57118bea07a6afe7c21d952c13ab636b3c2e461dc9ffb3ae701175360156338be94b1fa7115799831019455cfaf5114010fe45f8fb9c77ec50fe06f2c5a32423edccb3b2210ee1200a78e1a3130c567542377827586ca8cf0c14c19fa1449a2cce9c039bb441b04e9c0a3f9a743b31c828032174fcdb7c894349aa68f5adf97dfe9294d24e6b5fed95eb994397883f58487bf5c57b0aea5268be7cee9efeab370f89805ebe5373ab2e93658fc078955ccf68b554dd5605005751ee8531c35ca [...]
+	},
+	{
+		"4aba5a776ace38b6e2578f0007e770d264e39c49f588ca3547ad2888365e3a811994f8836330394587c8458eb0b6611499fd5d8e8527c3cdd4ec550b4a8f8c632384e786b420cb3be911c999c72aad60270aefad31b27a069ecf11e95e9d4c81213308d554d3103de4d9d6ab04830c2b8dfbd8bead52c44c21d5357f72810193b5096809dc7846c1521c6c569f78812c735aea21acaf6dce84a24df7234e8ad857f3e1346b27f5bd436113e2da950e4deff96e9ba8db692c7db723a105ae795da15b910c8286cac6e7dda8c172b70f61b07dfd58596684d61da8772356f180f74c1103ce97cd947eab3d401df44f7fa4cc7cfc25e [...]
+		"921a401db90935c60edda8624a0590d5c46eff3522e35de2872f6f9394e24126fd8143b68a797c995624fba0298b75eef974",
+		"6a4d35ae03bf277f587da4541dcddf92bbd906dff45d5ff23c0f60ae53b062be",
+		"231b5780fedfb06d724450b3",
+		"ba40968282d98849b19d867f8b564ea5a81d657516099362926bca4cb6e9ae02719d10c8061f53008c727a0eeea5e1e36c9e55c117e9434e213316c96840231a1e356b254a9981d4a6ca3c66cfc61018bcaade1a4486506559e6aa3a86bac980d391d835fd5ded98d10f1394d84bf1bbf2cd3397890d704154802f7864ecc753db782fd3d19213ae65ace4770e1bacf32d61c6730aa5adcab4d7e2e437888c11c29abba4890a17a00f67a53b660becd94092df0598df5ac57326f6860593a519e28bd4a39f6481e1a4748881fd5f0456a3cd9f28d1d1e78dc64030cbd8fdb2c5abdab3f13d6ccccd187e71e989f8c486929efcdbf [...]
+	},
+	{
+		"6c0056937faf1023032df1e2bfacbbc58bb022eba25ffa020d4eb26f0caf0678af5d0b2f0c1b520f4843f107f0adcc7b5dee66ff4d61025bafb4cabb64d133132e3e423a599549a1d83aa8c8e774444462aa44b00b460bbafad5755ea6a872d4e6b40e3f4957e0229288ea79fc2ebe5fd9020fe4481a9f42ef14a196bd136aa3c779e311d0c333624c1ddc484c9aa7259cb609e4d0a826c0bdc7567adac01da23900b30ac4e66c100348584fe200747eb67e6287268947e3509d5d2b5d7bcd977b80a13f660d4f6956a8b938a82db75eab19e5d2a22cb5f3c9131e278eebbe096b5f49d16c983ac240f3fbe821b247cccb2c9e6e5 [...]
+		"0d55dcd08b54e58916f622f81761ef6a2e19b167ac47d3",
+		"e42e1d6d44138f3d2bf12c951f454686f18d590fd30057405b5a3dc2b317fa97",
+		"1e46a7486c5a03fd6758d938",
+		"fd3c1fac10cc82e49235fd57f5aea0ee7a7bd6d539b138d4b3fb623aee591615c1a61228ef9673113a3a90a3687a12d4c6367d5f7bc67d422fdc4106455084d79c2c42c5e86368dd164bcbce7925bfffe7d96c13a2f49aac8e9d1ada3554e3fdc21aab00455a0f33b0c1fdea91b3588e7ad301bfccf9940027332fbdf966463491f7a33c093e0a13831ea9d2183294f89f414cf7b5876af04fa68d594430194429df74fa5915394427259e832bc545c13400aef6cf16620d48280798a6e49773c9316d79fa1dc758e54cde2e2cdb856092d83f4e9b698385cb976fd6cc2538abe055273a5b34a784182ea5e7d3ac9019a05de5e5a [...]
+	},
+	{
+		"04892b94c65685f2eba438322b29bf8439938590d3e0eb10a29e279d356cb439f6dfcdbc3552af21f7e753221012a649a52bda780bc589ae63b04b981dffd113df9fcf14f17e35e865880a769bb1bf40dc99b9e85e4296c1f2e1590fe02b22bfcaf2d4bb7009a4d692ae4c2d5f0b6d3ca526240368bac55b9b1e6a7b498d3b137f0fcfef1873c5aa2111d7811d45bdc26be1c5d49b8a2f36a999b1f226ec06a5fbd59514485abe696c96ea89dba74b4688101a239b495944e30b3609f73caff3114407599ec5c30a5bad933655de7dddef97018ae15acec46504cd5d417c5052c057ac5f1c6f69781cfdae71db2b4fcac35054a4a [...]
+		"67b5eccb1790babc2dab5e0d1ff3871c3024177d45a2ae",
+		"259603e1c3af3fd0ce3257eb627b02e0c0a48ea2f175de3d8c36570a445e5369",
+		"e14de73c4b17581a7e0d0649",
+		"33522e67ef932da5fa8abe628b51f3abd5049951dbc982ea95b7769652d4830c588fa45e3fcff094c8602b9008d7b2f9bf6c1c4a8cfb515401c7c44a7ec42ccb967722a710199e121a41160b1ec581507e9bd2e2e506b10c4b5a8d6977435aa08e27504957cd49e756e1574c4ccbbdde937de35128b7ee3455d2e665c596c2e97c253c94e405f85eb5de84874c099b4a97eb8f492d28f2e4bc64b228dd5984e76ca08376d7f1355ba8e0fa60fca96635075417d8b436278e0fb91e3bfc7d61ca8c7407086933c061b2d318f46f352099e1d317d6c44098539d1d2c1b7894db668e7a82ff991864fae236570cc420a4229883f1e22 [...]
+	},
+	{
+		"4ab8068988d4bbe0bf1e5bc2fe1c668cbe58019c958dd2ec97164aea7f3f41c9f747527f1c0e5fdb2cbb9d2ad704b6955cb731f14403dddb1a28c5996707635e4eb5dd6ac33d46eff8e319cfe7cf6443869534ca9812a5b23a6b4ca172afffc064dc2b28197117115431e03c00447f87d9b45172c6f724006270a1d41fa094847cbfac9630c3a785f488c1f5cc407ca6f4cd18bac43cba26ad5bfaccfb8f50784efc0e7fc0b504b43dc5a90a0525b0faf3c8b4b7046fdeb1cad87ec667ce3eb6cb4c358b01393f3ffee949030ef9fd01c1b2b9c5219777eb6ff5b1d7c3ef8d8e3bc2193dfb597cf942c5fc50befa527fac0b44cda [...]
+		"0d471079ad3c3432b6de852ec71692d12d9df4f984554d458a9dd1f28a2697976da8111ae4454c9a23d1c8eae75bbc14f8b00e7c065bc290f8938282b91a1a26c22b40a6708c40945d087e45633a595beb67d8f1c29a81",
+		"f3dac58738ce057d3140d68d2b3e651c00ff9dbb2ca0f913be50219dd36f23c6",
+		"bb2d033de71d570ddf824e85",
+		"238c4e6be84bfb151557327095c88f6dc2889bce2d6f0329e0c42a5cd7554ab16c8b5a4db26eab30f519c24766b1085e11d40823053ca77adfe2af387b4dcde12bc38502229510606ff086265f45b1087375dc4a022eb0b641101c74ad566ab6f230133b7aa61861aa8202b67beddc30dda506691a42032357010d45adc7ee633b536a2fefb3b2143837bb46db04f66a6e2bc628d6041b3d306ff78e96205ab66847036efa1fb6e6a387cf8d5a105738be7163df9da0db48e3d8fd6a786f0f887968e180ad6888e110fb3d7919c42a7f8c92491d795c813f30ea645fafcddf877f5035f133f864fd0ba1415b3d698f2349ebe03d9 [...]
+	},
+	{
+		"4d81b652fee892d575bd13dad913d976cf0517c819d5183a72eba995b1f27efe743451721ce34791a15a6b7a6e44f13d4a080563dd1d9d4f0946e5ba3863b9ac970a1fb4ed66458ec1b1092ff5fa6c3f0271a2df8e3f2e97851352be760b6a0e1589c202f00791b1b89ae0ae944ced96bd90754bcfa3e355b735132d407d3b5507fd57f705e8a8bd82886b16d459ac91e921dcb8c5bf0d7cf420a9349ee589a5e2e19ce7c944a54ccc1062a0690f3152300d0bf5cd1871c1391bf6d7007f7ce26018ca2a5c6f76287fd8c8e9e7f93b1806460dd35f7f95989a8b6f9a0aeb7c6b0346955fb50b8735e34f1ecb4859e34ea0f022ff6 [...]
+		"2538d98b64b6aa9258f9141840a5abef66d6037a10356366a3a294719c10d6c148b04cac66f63ebff052d730f8821f5e5822d869573bcffbdd636c7973433abbf38767597da5186df8ef9df071bc4ecade2633366102313e659db8d8e0f293d379fa2df79f456497",
+		"a5049b0aa153e282457555bf6f82b60fc81aa6fd1c2ea3db031478ffb74b5b5d",
+		"350287a6bed5709dfba3d35c",
+		"849670914f5fe318eb01e8849e536374ec11e813acdbbe6a5e82a506f6aef4f916a3a7fb2e41db3adf990175e21f2386d1805af9bbc32a6ac156b13b1a9505958f68599019c4b7297314229c467114754277b10e9f49a4d12837ef24184629c8902ebe2a23f740dc826b01f8963d47100bf617b314835e436104eb207fa9a1079b8feba06d9369b9aa8222d38d87096b73678bc5db9a1add59394530e678b6ec93a80efc6e8320f2909e3e891306d69b016ade0d30cde64c2c903b401f9d01a29b5cb8619dc68ad6c21900b365a6b657f7d9ca4c145fe598a94eeea741e20a9329996b17aba5d7115c93623f2f5d6927068d0f190 [...]
+	},
+	{
+		"67f0494a728fbfc84e2f4a043e121ee40f3b12b31616c78e157ed970db28674318b08d8b3f4c538d7b9d91b9b0b09ebfebb07201c6398fdbb8684c9390b3d6a8636333a3b086302b24c2e5d47283935d33065efa3fedd5f755218be5d4618d38c5c1db75470ba06bcd853f3f08d39c3cd9fa3618e70b103c2d9b2101fcaf39c1701436b720d723ed5c622d6535c9a10ec4d727abe237e80fd20911ceb84a90285fc6e07f9d036cfa65995f9b6300a927d7d0d2b907bac9d9c4daa87c2438a583fe85029c886f96ed08f5886bf53292cc0265850a1f4ee3e3288b604dc305d0c28ad35e1242f4ff4ae988b6deba48aabcad2fc6cd7 [...]
+		"74dfdc364097c39ef91c01b707a522e28edb1c11529d5050ff820234e6c0295aa00591e09d547e9671804d7825705ab44b76c59d1315ed1297ef477db070d85076693013bdafa92e2ff6a654660008b176cd4e8ae23b9c792be3f7db54cf2bca385bddf50a8624397cca8ee3cb96944164e3cb461e68",
+		"b3b5ccd7ef49a27d2c6d13c0ae77a37abec2e27e0b2d3530cdbb7f36792a7d2c",
+		"c0494bb7249f864f69beab46",
+		"ed8d6e964bcde1df68e7f362243073941fd68ac77929c8e480c89f519f748b3dc337b1af6231632c975167a8425b174b42c2c60dfc0ec85a0a212bf5c9aada818a83f9664c8712d96de1036b5e5d8c8298786b753638de3a8da958549f16eb9c723355cdf7b999aac464ec39df7d6c1607e81b88b63043d1c847dab618f1b19336911b4b0145c2a694e61db71e021282006d48e37f10f3b6314dd012a07618228532c28ca84a936e0eff83723d117b2f2db857d14af5bbd5948a0e53018b31e57cc2a81f36aa013a844990753ccb347fe98fab294cbd252a8b8f7246276275d2780511fd3cb7baa2fd1548184f968c422230f7ad7 [...]
+	},
+	{
+		"04cf92a64cbe135f7fc1d7223b95e41d13f04b482018039f4e7ccacba8aa15ac79a752c5666524e527fb076290ec80a3dccbebfce3ee9b316a65fd130f12bf88b9124d1f7772049e6d0c01fef881a1d44c8dd02f7b6b60e6d15df9e06fb86929cab64842284de09659e19451623525aec2f5dd3e603e24319b1d120bd57b34a0317ce25ac9c2f022a4847306b998b57c8d92baeed0de1f6cfb3177d0acab70de275238f1152813b9ac87bf651f74e1ad079b9bd779ba4374ecba459865b5768d08ae7e1dd691d6821895e8380ac9e5116580e8de3a2c5326e698bf4c4d35d955e45772bae8483d01de2539e8ee1ef9539ee132d80 [...]
+		"001084c8a5175c0ad43108f9215c35886c82321c800740c6118a3fcd45776a4588ee0e2d056a37d99b40d2bd9c0845088a77240b7e65d18fef105d3488c77910b5873dbbf275948db5",
+		"d614d2e671066e387f7eee07bca9f9313d86e6a4d1371360900d670e11ecf1e7",
+		"674a516f8f59f9067e0485e3",
+		"1ee376e9e3c89b2147bcf75480ff0dec1d0e8cd45ba812f34c84124871d484b4ca87bfc8cf99f85ad452c482933801426e2737a97468809fa36caebebe8eed07a626b3bc3614ef1ceb54f9221ecb16f413f0bd9ed4b3010c40632f05223484af7bf5948c2fb8a3d2ce04c53e3f2682494f3969a0f8eb738cf93c0141799c9e6b68924433f0326991e19626bb19e6fbb5dd46baf39f92e830f9b1ff465a007f031891fb1f1799cc122d3ae7a55624356b5297bd5d948d9ff2e414cd8adf00a53524df43f398938d33c93b2c06bcde2679566c0a7b0177b4a873f35874739d550712d5cfe3d25c19292ba97c01d84224738bb25546e [...]
+	},
+	{
+		"ce72c93caa49bb9850774149a87fcf8e23a0c53701554468645554553d54190bc6e247712b02097b794bc421ca94afed34742435ca689d2ebef183fb469c060c7f4d7daa508726c9d2eaeb9c7e9a89b30faee8d9168607d4778acfbd27d5caa623475073ce763ca061273cdfc2c692d1747baa8a01b15f783b2e36620400082747599a16cfd6b630fef310c0b9a2912d1d3bb71eec16972745cd8a49cd927014eb0a2abbe0e1ebded4fb9e8d9e2fbabb6a71da5688717ecd3e08160b9a861f86904a41702b2c4fff28ed8cc61d468187b75bde3fcc5c0c0a642215fea83584387fc5a9aaf2f8a91ae535e0027b618a32bd687289c [...]
+		"08b3fbd73d157e79ea9f61665d19867dcb8c1598c1d37b793606936d8aecd992a0d46addeae857d488b83be5d1c1639d4d78350e4cb08782b61bef4107c9d3a79d3d85",
+		"a56f38b4bbe83b3d9e562cdf5ef5b30593f08a1a166676c4fb0d5861654e640b",
+		"8726dc5cae9497936658f603",
+		"88420357d1ad70e7c7bfd55b3cfd4bf06cd4e9b4ed5cba681045199a06985956d35fe86b28b9a4599964930d05d230a23c55a6a152f67082a453fc31f68489df05c553f9ae5cdb3f611445db384d79af865e52440a876fc4153d896b7a2318dbc2a4495ecdbb2e9dc68022326d35289e82aa55197aedc266dd91ba3018c7b474ba22b4e773773f3e9890ea84bc16a6b235e4bb69e785c40c1adc15b0e0ef03aa147b0d14e62341e27398b84a53f72c9199cc1c94cbcad2bd31aa69c96b06d01775b8c0f80278a43f526664bdd430164863c9c9140ad87798a5b8f38dfe90d37f54d1137709d5311136b728e6c799da244294daa4c [...]
+	},
+	{
+		"bf7884fab52251e202afd7b5b46aa53f85bca5fb80a009d8016e276579e401385d853312a884f4aa33cc5fe7360426bbc0ccb7416cc0196e2e40d3a825d5e0825a1394029789acca550bb28b10d847d0a4fe1111be2b7fec6b5294902775128288a784203031ea853c9c104c75571d19552e2a1359a900c5fc9455230968a5920f2ab23f5b9cc49739d4e4ae2c01c7812ff295899b954e9729a3bb330b60c51a8a7759e5131d7d4cf261fa1e62c29f91b4341a4fc968e7f30ca2261702eb328d628b7275a9efc29b50bcb9b27e5844328d5e8256c76949d30b6fea0d5a1c9abca80d3251fcf4ec4db0a5ff2ffd43618aa2e3e1694 [...]
+		"eaae1c53919e2029c137a80f91704d0871be2c1870d0333d8bcf7f94",
+		"4c434cddb399e52457113cc7e16f046c3f8301f5b6c296979f8a091a9ea557ea",
+		"b633c1a0e1ddf4727b149b3d",
+		"f1de487001a580cee6edadb1ef6b700c861a70c6ef16274447b8c61bb10d2d1efbf104d5f7d7172c6a5cf9c06d886165a2919ee9418e2e8f803d47832dae5ef232ee300d1f973a6298c22d777a1b16264353cc731a7a683cfe31e0abc704460788c555c0c24f281b81d7761235a955c736f17f213a896b40a034609ca8456ec3cf5906d01121b7580ce19d89347b6a59c81add318df487b2442a7a8b5e30df78467abbf46bcd5ee5b994a39ca5bd8846caba6f02f4f1335b73d4e20be0b6ad85966f86d1bb857713ebf947ae936782f1f4929498bbd66bdd5ad6fa252364a5a6b46180e93b54cc321b3cf63cf23d55392475c6b8c [...]
+	},
+	{
+		"c89c3cadc094bffd5ba06c600dabe30ea19ad037316fc13b895fe0e14ac8841264c1bf25557e22b01f8e102c3af43adb8e0a12bf79d3fa0232dae37ca3688e07294e2c7ecc4e2eebdd3f17173351f2c15b0480d4d77bd70955ba86f82214004b622cc92f7bf81a5837326f6a83612bdf65abb33c268a457c45cb7467e074b342a17c711c748c74abbee31541444020a9ecd4e5125e2a8ea3f6030bd677be18183a8a34af16a85ad48b7015cfb036789c0a5daf68883d0c7e401754b8d56cd00ff605be0cad19e03989f608392c81d636de859e66c2aae403c138bb96a58ba69b9064a83e7d8877067e7f40aa0016e0df9b7f455d2 [...]
+		"82abb4ff5457b21f359754f151e456e2c0a185c8363d15918bcee0d6d49f12564ac655",
+		"b63b577e719494032062e3f63682098dcdcfe26cedea2a40893c847a331e4ce9",
+		"9f1d08be539f1244f0f69ad2",
+		"88dcdb0309f8c4a96ad5560f8210eda1f5afb31b85b7a8b15525777748967d4ed77c063f65d64ef19b31044f2adc690f5e457faa1abe2e127b38c626eaa94053c9ae1b6b4d0db1f02c8404b50f58210cc9fcc6fa4ecc615631da631031cd6253b4a13a3e88295ffdc775fd4bdf29655d9780dbe02b0a82aad4c4088e90b51f170909c0f98ff93ca3926067ec94be05841603db4f913b7025a9ee34b8d8bc629ed827a2a9857e0814d36b83cba21e670f8f94ceb4be5757e0b8782895b5d8605868e4f584b5bb6a5f3a94edd9b23fc2b6fa06914aec970c260fc370aa245ca68888c90c43eecb68474c9e45c53a7da055f5bfe39b5 [...]
+	},
+	{
+		"68ca38fccd585eb14f953358220027046c14ef965478d3d8f206f63fef4fe3571a1b53e774b298c347cc1b69cc426d39575ccfabd5a284c7a87a0698cae9efe40543cb79f5643c3c3057a5fc991235f06f059c44a7200b509a12e864fbd748001a14790f78b54ba80cf0a4a603da9672df32b68652c1d6edd3be51cf969acfb0ae49c026fe0bce0bfc72b1ff4c47712b7a27b2cce888b9bc470b8bdda55a8d53a34d79a25947ad55b95e5406a5c5311fece3ecd46ca590b3b01b9055761da8196b21bbc468681922c66d286c32598b1e3d77f2a91d835ccd9eec231409cb2e74ede9385552517718be9f84f0f9100e368701dfa48 [...]
+		"ea196b6a64be4b0799b1c2f72281300c3a2577db44e5d3606b8b02fa8fc46c7aded7e442",
+		"7c86a2c06b7520d60023d18fe73d09c514ed07a91d50d8fd5bff00e7550faed1",
+		"952f492fe00b26028c560fc6",
+		"b3f3294815ce461c8843172efe93f73a8254e58a0e71953e35c15aa89a7bd9dfee967853dcbfba73d3b87fa60449cbcabf13b1206d0cb27d2c3fedcfa695b6d41efda37bb6db35449bd470a23787619ee48f981d3f0b1c8e121725b2289b6d67858a4f9ab41683bdaec8a913ca2cc292a9640efe50fb85a1d1f7b286f45d4448f85b3242f45ab44e3281d759db24dfabbae4259f127d6546ecb914d7e93e2c19230c67fba8a6cba6069023ff7ea3d8a170289c2b4391bb97a7b899228d032b36186dfbb29ae8f0e6c06d753f4c6b21982d49ee682bef50a5c2c8434510c5fa2b9c0349592f33f8d7ad6f7243d42b292aee6d210c6 [...]
+	},
+	{
+		"9100c5b2d7c5d5a854bce55e82f94b89a268da7b66357a661dcf75cba10a1b320ae0e4e1a5b989f9766e57f867a3810a0b5b857191ffd7aece4c796f5694a2617486421940cc12b63a6aaea20d2fac188b318a1c3061cafeae436e04d710654b96a864d674768caee03a50ed6afc06f52d90115df1db5c9f1ecaa4f5da094070b1a447251ad3d4fb0e24e87821ee6d4e7e7eac7059080f77d2b36cacbdac1c6e5063946a376865458c4ebdad3c2afcbba8a82b01b03a7882eee42eab904a19e0aead4ae515b02aa2fee74f3a114bf5b9f320baa35b3225491653f4a69e0d864cbbd031d0805b727e42c2b9530dae0c01cfc6a42af [...]
+		"3c77f30bbb698b1571aeb54653fcae2c23dc16be58603f0c361eedd813ec0c4f63005a1e69e533da93e820e6e4ce1308aa29c60289060ebf24fc9738e8a4874ca4e26a0dc79ee75b8607416bd554737f",
+		"0223c0a6052bb3cdc99a284fa169ba76be2df53b677642a606090a9267a60769",
+		"7d3981073f90c6648c5e9c74",
+		"61ec5230306b70113f67b340575b77ef76d521ff75b754d551e4177591a02351ad382b2a4067f2b3af7e8e15431c7133e98be9d8293d17ef40161dbad9a4f1a4f30cdd557bb9a8b03b5f1b277c850e23ecfa0fc2ab1102e4b1d5e836a606883c3d43527fc3aa26955964b144a9a56cafa7b174d72a0635b80e7b4f871ead3838a955a14c4b8c5c3c66fd86a5e4ff10dfaa92105378bbc5f76ad29727e5bc4779ba3e6dc19bf45020f6ce4dfb3400df05cac51577d58eec21b22839b8f055226b204e641783bb3305b4461172f1c1d48eec56fe6f82aae564ac6688d7b0994747d9b23a24418e69f8a4fc548f854f86baacbdec78b [...]
+	},
+	{
+		"0fcff2c29cbb5cc40bfd2ec573ecf368275ade6a00e5730b77dab17e437b46524b3814e7f470acff6ddac4e0c6b748ed112657120bca1d83a4ce01e74a473995804d7c74bd28732a02370ac8ef52b600790d1284d82f077cfe096448509dddd0eb5944a882b7d384efdd4dde3003dea910f12de82035651e3ec9668e66435f519da3fa1f5bcda34aaaf028daf3068304f7b1ec18e65136241a9db281e011d27db5cc9c1099405a4430821e2488a228805314983966ce5d806b0f014c21d4c9d6a066e63aa6407ed6c29cfa4a3e22ca913762ca9d31271d9c371fe858f3b22e931814cdbe544b9416e88f6026b12bb8e88d8285bea [...]
+		"0c5fb7075f5e15a6733737b614bf46871e29417e4b140bae6e10081623f5c52f557c36b4da4b5a4e82920497514b1e6f745fedbf73f86ee10976f82c6cbd5bc13a917514ddd062",
+		"e70954c812cac03e367e99f7b82a6dcc073d2f679f965d524872756ee58654cc",
+		"5f6267f6b3b21423267310e6",
+		"c53868c0fdc14e891ae1bc257fbb13be210a5d9cdbd9d18fe1b474f9a1929dbba3f25222d8fe8c1be3eef22352100064b922fd9642ad128a202b6382ae0a67c8affb0c5bfa1a80e55c1084cc372485243df872d677a80a3ef1ca3589908bca621f6f50133eb762cb9c05775d13db7dd3eb65ffd3eef96e8dd42928facc68390f6bbc50b17e1ef5ea6310d8756dd177be2cceb63a97bcceaa046794915589ca022d90756b02c22e8634c0ed44192abc3b8b1e2814c855ab27aaae3bdd801a73e6209fdd559ceb59a94fd98a66d12a31a643ca2f4b07ed910bc390f77ab89395d5cd1d783d8940dad4447f0452991b209cfcd998b0c [...]
+	},
+	{
+		"95a17355dfa9d378a18ba20e58aa4b8711ea1d6e3c65e0b2d3c6382892c7d02768437d47ed50bf8edc619c340be7bb1cd1d88b0d3d6bbf1031f738c4be09eb264c686d39b92cc7958e63c9994a84b61b5c412999ace8a9dee0e2a29eeb8dc537f63271af5f3844ed9c0d86e6913c02ed7d2b862a132f08f311aa92fc3757342d89a5dce8dd20d5792d5c60be9862ab168d3140a061489472f2266f297da357064833ef2554c49f8120ff40b961ebcfee1d0f8e7e5722f049485f72c502c9cc4afdbb70517f0fd2a00e12596ffe285d1b37eb998e0e89d756e9491ceb13e83610a3a66122b533c2c3461b3244438f5f7a7af808888 [...]
+		"5e24f34a8d53b17bd0c2aee5369e3276dbd7e7c2ea0990f1300fbbb00831b76655aab1e2fd625ecd",
+		"c1d796f1e651a1ee825855d80206baff6818cc8c247ee6ce62b7531e6e9ac32f",
+		"240cb25aaae4d085bbb747a5",
+		"319e968ad291ea5d4a057c38f7afa4ddb9c9565962fa1a7b231e397a268ad8e0c5030a2df09dc4f99402ddf2e0d06e753bf55e1b318b3e5ff0108de2328d3b8d53e23e08bf7d84d59fededd60d47bbb52736b0491f82c616eb5f779c496abd6499555035e4513c8613e7204e6bff8d06dfecd9ce38c6b83efd8d0e41f84f7cfc9ae07113237987a4b2eaa87f7e0a310155e282e57858244e9071712fa026cb781e5a4bfe6fa1bc480e534096394459a3d1354e2d9a54aac6926a60b388410fd0b53f7a3a9116292f37406369c22ea674418c4deeead171e00f74f5cabae5d24a0686a4bcd8ba99aea613a23edd0a019a319daa377 [...]
+	},
+	{
+		"2158abc2472e1b9c061da2c01d0ad9e996fd687cccca331fe8a2baacd12c06f284b1b5cbdfd067e5ed09a60a137ff4a97c5c26482659680ffb22bbcd4ec1bfd272749e52440537320fdd3c225c30ccd98cf221b34b89c247ab7d14f93ed3ccb0486a028c6f3abe7e17fba1742b6d4db85f6e6baaf82df1a3aa059de8d9699821d39bad42d56cc1ec67626092cfad4a2e1cb5d814e2cab78ccf5474a8bd0dc990a877d37de394694af6cadcc57727f393dccba7bf955f4b65b3c00d71cdd701754ed4f231685b7b5e2557239d7e16305be2d81a773765dcea25ea5bf2c15d670f3159409ab5bbf8da121c779132a8ec1480068cb76 [...]
+		"088fc7ba068f80efd8d4d62813c93c1eba77e9ff400c7781314abc901873ce200295da09245bf8fd2fce254397616151d94b511957c89a881256182ac9e64acb7b25d4a080cc9daf9ac2f231235483fc9fd415f69caf7eaf0597",
+		"78d5f86b071bbf8a185e5e2d54faddd2a9e26983b1e7a74be0f0b979b9f4af31",
+		"d9ce7d249af9496e99c93b36",
+		"ad542824b49fc520f0b7ff8ce2bff8b3d47baacb4a1c95ed56a306483aac551fffba48e8a8f5e4cc536e9266182f6811d070fb9282f5c542cefb4993ccc7044b42cfd6fc71793dc8dd2de23c630f9ceaeddba45efed9d7fca25fcb07d193c000822478b19c2ee9fb31760cfe01475ba8a003db469d1130318a79345a29d054a9f9412dca1edf6d8f1498af5bb6fdbbd3d5f9a244ff176f62742c53779291ef6294df6540d841f4ee8c7c58fc8497ba74d9cf7947add5373427d81ae928305b93dd26cfc65e63b0ed0812ce759511bfbb10aca98f2abdbc9055c4e5ab82637f6a965bb74f592bdf11118b8eb79d50331e76cb4d10c [...]
+	},
+	{
+		"9cd1c25b5bdab9b9080db3e5e05dc749e0783087c310777d89307138613bdffe0ca259677c13208420d4690031314a11a97a986d8b0fea143f5b4da0972c9ea3cef80b4b0b2bcf2bff392c306a764113f0d9807be86a9027c6ddc85d096600d85e0b236937f295362bc1679537a8a9278229a36a9433925a105ab719c0b7f11fc31488fa071d3032de97c81540713dc29ae02c2e13be8823183f3cd9f72ef8ba4280b4499ee47c7c7c4492bcb5cf7e4fafaa7ec26906e58146215a3d4f52f792d3abdb718f57ed0b9b7fc7504e45a0fdf01ebf5924a4da6ac635a715879ea75a4983cbd9dab9e47638acc687f16684e184443aa9e [...]
+		"c22add33457539a957d32dd07ec9110f8cdd2f00ab6ac256b4bc7732f63dd3b867b0ecac262555",
+		"e71f9a3dd457b4064df1d9055889f105af175a2d10dd7b8729da0d0116c2d9fd",
+		"7df9824e774c5f86d83cb5d8",
+		"689683c9e7aa9c48b9fda0cfffea0458ea0c3dedccd21efeb06126f1194780917c9f4f2f44b1daceec3f6b1f75506f4169bdacf12c1f65958784851056fe0b4b42a22aeb043ab35ca73747346ac58c550324c4b849a404c94b8860967b6fc58aff25dad0556f1952c045b91f56ec8eebf6f552c18b2a0641c037e6c6538b289601e1fd5a7bbe7b6e0b224124fec341bf77615183abafb52b3e30082a0abfc2cf224324338c132426011d9f800b382e6b834896ea48a8247f149d92ded7e69c7800096076cd2a729a1fe41c70dafb1f855ffa2ffc27b93e2f5f6827ade7118af60730033675d84de9cde6c260d3d615a945dfe0ed2 [...]
+	},
+	{
+		"3ab6cbeebc18df951d371e0f3cce2697fb367476bd9d50ca9e668c77636eeb9d24b68be0ce6a75eca194fbde6221755d57e9d3148623de24896a9becd98789fd3d14de0c7e53f81fe7f3fd491472a66b5b797fe19c5d0525c7a111a0289a9e65ae7c712ccf694cb75c490070bca7db17205af9bdb7fee27f9ff41fc78ebd2d3d399e690908b5c064ffc0d5bb67b0d2880bcb45c2ca2741691b6131aa1e5ee758fc50610406216905e13ec049ee92d1f95e16bc283dfd91595ec2037d20ead51d3a362140578a4538c80581b79852b0f6686c1ea66aafffc872024592ec1aaf2650d167a75bace024b261db4ab48b401cf85ec2620 [...]
+		"50772c5a0e156ba13a9d86edc0e600021d56f7d31e7e452a74ad53a6775339c7ca6521d87a8c79b42900a1e9e6a1ec03f7e3d615611c3fd5c9927c40e5b508af1a298794b60148df01e9c9e78ab5ea8198c097fadcd6cfa6694be64e00eefe1a1885aece86f6ad87df766e692b58ebc41982bef5",
+		"93a2561a9904a1787a10e2a668cd6a814f2877a7b512698e94796805875c8d1a",
+		"588d9bc1d98210d9700ef488",
+		"165d8c9eabcd5e93e6eff7be122c8c242e1a7f284790c93324f924efabcec4a4ce48262011b7360c2833143d645ff295453853c92f0c48c6dfc2af7ec58d9bec0d13239c7e5593cdb39d49376c6341263df80c0ed2ed79fe9899d0c07de93f6ea95a5dfd307e49bdb5672b158a4df623ee86d54cd1a0fa9a60ce39d1f5f4b6b0ce9daf2a61a907cff3bdd3f29156ac439638e0910d728843ae17ea7368814ad7734732e7c023d4954e1cd5fd19fc9b76e9bb84b61dd4371478917757b14b366b4bfab4eab0d9de746088ad43d8742e2b9e58faff15c2eff084df5f4316111d5dd7d23cc0b1ee1000253f26cd260aa636f03f64a83 [...]
+	},
+	{
+		"3497e8d61062e6f2084ebf72d00e9a47b550591edeee9746f31ea28039a1646d384c4348af293ab778f92a4807c48fbd14e8dbf3d67339c991dc4aca7dae38b5fb7bfeaaa538611d328b653950f4f664dcd257b345917cd66dc6a1ea75d99f70549d1af9d67b1608077b41576f38bb4c0a13ff4fa47b251142c6fbb79f9a27f43841ed0ebc0416c37f571aef8fd63b99e93ae88db50e9ef7d499ae7433d5686b165579d3598f96d9e7b1c876870310703df8fdf2069beadb34984f676eb7d3840c4c5766dcee3fc39f0739260a499647429339482e232362bc72c92a299cae36e9069cc5f4db8893e2c1b9ec0b4f334de26c95109 [...]
+		"823d0cd34e7450550da9716c1f456ce0cbc79431483a6214939266581b0e899e4c95719a09c1ef166a618289a6ee6971b6fea3fe380512cb977823b387ac51d341c26d4a835c61eebde37764d2e1d588df7886177e98e3151106c898b3196bf4dbd83f5f",
+		"a4639c22fc7f370d8500a53819102df5e86c541c0ca10e8f6564e50b90c28f34",
+		"34a04df283c45655a52bdd84",
+		"cd8d1b2e5f65ddb3c0da8f12096134da22ad4d541444964077610aafc1f77f8da5ffc75bee807541cb6eb0526e78d57fd88fa9d9608914cf391ae7ccb8eedb0aa711889f9b6192601163b271c90df5d69fef487b6c05a24fc667469cf16cbd5afd58fc830119fc9f61b26dd50a96ed84c96825a615a3aee84ea4c950152323b20884346b25c9e2a6be3a93505ba059fbb114c224bed8f05f54eab76b2c9c23a0fd942eef9696ff67484b542c8347f1b1fd7df7242872b3528c9e45030447b2bc85eaf191963291e4223b75778335e5f1256618ff87bbd68b5a9e5cbd2ca1dc8aff4625c834edf8fb0d879b1f75ba9b85895a6bb4d [...]
+	},
+	{
+		"5622aa8d2f308dd468a7e4959ccc01f0e80d91f79df65b8201eb44911f6abc758c6703bb97908fff377395d33f96c328a4541f414b7ac34c6607dd85729afbfe01feba988e4997c6bd2c99fcc35d2467b143a8fcbe6b49247226a9e4c0a4e3c1a29d5931e6f1f7a31d90a0e0edc4479f08ef9bc65ae4eacd0b93b1cb38948dda31e60b18d702bbf5935bd580201d1f280cbbee679fd834aa6be576a37a037eabe989c3c18c7fb61fda8b9ffaa8bf22b57a101c19e850c454353af7af3d755b26ff1ee78b9d9daa78294972d108958682a5a29c8ef260e2289ad9d7d74f32fd4e51e5d9ee828366abccd97dd56e035713a6f3a1985 [...]
+		"9f522375925222a04f5c95ee14b6386412025903ecad0bc3ab78afe1145136b3a3592835ab4ad6faa66be9",
+		"d1ba82b3ced3e9817642aaacedf482e79bedd0560ef2754215ee792514bbf8e6",
+		"bb21211f342379370f2642d3",
+		"1a6683805d3f478ca1c1512b9846468378f83be27393db63956e151ec408368b47334afe610249182f54c4d0a01b704db2aa90a9755b8feb67ef9301f0715d7d6bdfa5cc4497cef1142a43eeb42f7c413e8f489af30d742a706d05a40a0c4a5991f9e2cc5d9fbca6ad3767682e20c146ac35aef38dfb2a77388b738fa022158d5c802e5f0761096bb45b50815ebf09172759521b5c5d459703ebe9ff669ee4d14a86e5d0650b597f4a082ba0aef366a924ea378b91c3262d99f48189eea19c76c0f644079f8415c11033cf24d30d6c149ab13ca5c29deafdc816e457257361c1af4b915da312d2e6c7fc712faa27be3e67c893f90 [...]
+	},
+	{
+		"99444e82c6c4c47070b164f298ffdf6955ee5bcb3070b9aa95ce658db4db084d2056cfe61a93568b44ba7ddcba5d450f4ba0da7b119425a6628b3416663c638692326cacc5c237097db5e537122b465dcb21d8dcb5fe831789b72deff3907685c2e23187a56990221e755930a09f8d6cc065487563cb8cec82b9dc754952fa0b342c92d99522fbb39854e338f470a4b4d5ed2a39b8b6253b7001b0b953abc588d757616c7a5d1f12b1024aa572ef5a47dc8480943aa6cfaaa78064fb2b29830280e46efa418d0cf38f57980146f2482276c9b6b16f865b1606bf1131e894336979a163ba2e70adbdc746be0d38062fafcfe5603e6 [...]
+		"0ce980442336d0f427db869a6799baa6785b5e030567c588e2a7d2680e96c11b7f415fa27730969e0b1c3973b5f3192d4e773153def6dcc09dae29ac44eac7c42c2666a356fd4262197bd5cf6eeefcbd662d104423ec05c19a2e6ddf1834a3445a09e8b1062a1320a5e8ef13d6ebd03c19e1813ccd86fd68b46a",
+		"1ac8a509db7bf4acb80d8d394a5abf47c273b2093f50f35049e749f3e16cb0fb",
+		"47cc9eea11f9f3f9aafa23bd",
+		"088888333340b3a057b05491fb2402301c8654948aa6d5ee1ec75eb045858c22056fef0873d6675f897126052923a47a30675b266ffb6181cbd29ce2da3720e36a227e4c6e53328d789913c0d9cd149a6e49293996b1be7d6c513b24d876445a950e723ade3efc36907c840b9b8cfdb1503811b4044d931a0009b381fd60a5bf1e73d16348cb57eea672709875fb9d56908dbc729d5d7d322a17a41d0f62c9af9a013ab1e19fb7b6c6e7fa0c0b18bec5e3d3e92546c77e3753193389e5fcdb6a6a1896cba461343e71ef7a156b136b27ae6f45be9368301cfade203e9b53824d70f07de9abfea1968b8ff8489b9804422ba05ac3c [...]
+	},
+	{
+		"0410d1f8bc890649c250a3819766f4496f339a6384e34acdd72b3a87266edd2a7eae223a372883f978277a108d6e59fca1f35f25d7a9f3aed42d35fa9b12241ac04754f76fd8f0e8ff6af88cd851887a45e89f1c9192ca66bfff605b128575d2ccc9ca3ba1ba23a0251b2cfd6db577b29d17ce2ea998946997f5c4a97a397c46024681a400a54425c071232d269adfc3b1adf15b4586c4dd7b8886f5c1023bc348bc674961ac6e221d914f432c2f06dddcf738227dfcfff88485ed45882809d0e57019461c88683919b87c45e78223c37a5be5f758e4f0dc6add22f2062bc2eb9bdc31b8649af17d526ec339f0e6fc6a41e26299c [...]
+		"113b261414b4b7dfa028668ac8b0cde5734120124991c54f4dd16a87d181efe2bc15f6d0caaeaf6ad615f59ec5c2833904a34b4d34109c82e10609b387f995430e8c13d83ac34310d838af9efa32d7fed6224c0a33",
+		"cd762390b93369f1e207eb15deeaeb0036f5331e82480d180f84a76c3e44550b",
+		"e88c14ef96c7768f5dba9de9",
+		"8d6aaa27892a76fb05a2e96cef9a9b4b7ae0670a12cff95f7b076372456889fbd3b9b4fb5fd98b3bd85b247f15009be2f4e7a0329dd118b6872199b314e159618ede0381dd97db28743461ace1a694c0383d8458150a501d6c45f4b50d5b1bd47e61a51f9ed4929bf2e564f201ed0e6825170027d93e482c1ce268459d2f81cab41f0e7ff281430c16b34a29b5c76630dba72ab9e751bae41122b26121d91f2af271a23e818263f46e05fdd52f319d58330bcabf66637a368c0a8aeeb20cad1916d966e5e0b0de74cc67ebe57e3d1fe01e9743d42a931cb4b98bb762ea43ab937d1e5c42eb08fd56e70e911bdcc1ca4ca0604a329 [...]
+	},
+	{
+		"9c73ac05648e0c50a3ea3a8eea70841e8e06669c1e7520c5e25e093769c4b005375c0a9cea16ec8e00261ceb96a00924a66fc0c4e4e089c63e93fea857aead8e0ab82af4ce1682cf3c9fbad23fc3f7e632b7aa169834ddd6c7db7e1e892cac93e4d787b2ed0a812aa93bfce8fef3ce30ab794743ad241974ff989288c43e1ba815a25a03acdc2d5517293e161d0c46c8858d0b32b124a6b0bc3838807753288cf6838fa25fbcf876e6368c0342d3cbc860d6fa12faa1c2b7d9fb37504e60dd44e36ce74229dfb80f1545125718dd1f78b31a8aadbb4d6494489ce596fcc2dbdf2ec22157a1d966b61e780d36552daf084739b6028 [...]
+		"bf96bbc17abcd1f56a9f22ad164d25ca72f8c996f1a7a66d6effe140336da4f20460b47e1c8573872496343be35a055552ceec437692b0e4919224c4ffc8b603286a8245eff5cc148b004f6e5a54c4ac22b0f09842a07cd332a09732694d3591b8b7d6a7ada2bb38a30aa7fd5e6baa811b9a195d3a96306d",
+		"aa2f714d3a184a9883f4199e8e33fbc9c92b36fff2d59f07a9d0d335d7476e81",
+		"36c79f9f14d431cc8c077439",
+		"873d0617c986dc9d83e9cdfc50b1f916626a9d9e1c595dc7ccd99d1e993d25d89b04a893c89e205952eef8f1733054bbb55fa5e1b07135787d4fcfae226737b50cafa2c11276e8708451be9b4d7f662e98ef6b705c5c4fc64588728eab1dfee22a0a92bae61828a7394977b0ae8a3b6d0126a23583fec025becf0a72a28891391ac1495732a7a4a1d43a63ed8eb37b280b6d886096fbc4f77aadbc5e441e996334d0e10cd7f3dbba9bb7efb147297986509a07735385c681e0543186dc166291edc3b4664f5c8ffb0965c85bc30ff5e7769a69609c69ebb68f35d104bafe3dbd3e2a40e13865f19bca3612e48592aa930eaee2944 [...]
+	},
+	{
+		"ceb1f819497c0d631a9c9616655f419b5e3470fd3b19cd0e4fa556bd26cd9df57e960ec7121b2a2cb7c0421c1f84b77eb8277bf341490190ee574d1424eb09a281176a933394bfea5502077486bef23ee66e3127b732b7a58a04b9aeefc35170dabb030d4fc3f8a4c5ff194bbd0b89a379baca30ec81d576868f25755276e62c31e93a80ac322571313ebcee494592c3ff5cf3ecdec962645887d9aafdbfd62ea910af5542d4c7731283625bc9f41ec85012b42edb1792339e6cdd9c2bb3cad4c4792a064df17a5f74dcbb3dd0d90620ebba4fc6d1e1f9704dd60c798ad64d4e5077549d68cefdddaab81a7a91209b7ddbea43acc [...]
+		"e45eef9561f3acb3672b4f38570256e8cc4d877e2998e72b022e33de8fc20f7320fe0882f2b53559e084923786e8205336a7d15f3fb88a41e7bd20767f2feaa02df2221fa7577988db0bbf61f3dfb429868688c53e130725d0279c505686f083",
+		"475a44cde0cc931edf9a44b0c1e0001766f09ade023dfe6b59a6af800e549b55",
+		"7812a320691ca8442767a51a",
+		"eaa577bd67fe79ce4586f43355c94528e306c1678946e4f7a907d2a8ee7f4281270502522119a8b09b6f05d864921cb515fddf6a1000fc2f67b52d0627998591e2acf5b6faf71c278e5754b2703662ce670dd049da8d6e280c2b84d6a9b29ce28980563c40e03381a49c54608b72faec9b272ef05cfa41957d9eaf3e944b22610c725d8efea90aaac6e782848d368ffc08784d7fe37ea1effbbbb34952def29fc511fb10a1282bb0b6334328e4d00529a44de3259b522553a07d524dc75f431cc9670127c15670c0df419826617cfb5ebdd8788d5f528a9eb1e61324eac5c1746f339aae2e2e2fae598642a389da671482128acf2 [...]
+	},
+	{
+		"228eabb5ad8b4ff13b10d13b27372bc2152dff149859ba47d9c89b741d4a5340d8fff5858a4576c55547007d7e2b3f94583ea8f0976237712bd2e5481c3988f5387e7ac2c3f18718388795b7b2d44b0a13f3faaa55311b800301c9203a511572cf8f349280bbabb9424070f415bbfe28aef8d20329ee842cef4d4c299e619b6ef1cf00718aab2accec9ac00155be2903b6fb07dfe98b0bd8d8580176b99ce4aa6be51cf59046c17ce1817d363fa63af5a241d48bcce064a438651af102ff9c6de4b86374fe24f1dfa66e16e51550dbb791af425d8fa601c70c1bb90e1a557bfe0dde730b0364eba9d2018ee751699ee219e13fa88 [...]
+		"ade72c2ea29cf829ffe99c2d63840b2eef9b51a9919c02128347d2e88e9f063b86326928cf6252ce4beefbae7206dc61a22d0b33c90d464d551835e3b73c1e3d6e88663deab80c35a607e4180ec079b0ee84e3b7922904e7423acaf976e837",
+		"43348cf32211d7daa300de8a4218543c8e3c7373ad10950765c39760f80b733c",
+		"e4709d225a552e90fb357413",
+		"562050bfb40451f27b1181c389508550a0f46b53d14ca73143da9dae3d3d2b466e9618db39e3219675d2b6eadded7dd9c741d7c9bf3c5619a521189607acbcf6b3964d469d966fa134444aa06d80749c873f0f976e0c5efc5be8d00a2729f03eda6a7b8630575df8b3a19388ff88daf0d00bb3e7c35a525ded90a4511ce815fe6c8904406cf72d7bfa14ca533566f7b54268835285c5402e22a63f98b5d90c86dae0a76d65eacc1ba85b3f5a1499d5f3432dd5455fab9e8bfbd266e99283c2bddf9b556410956b2f061603d1fc91194766f90da841699ba7da3d53ed5abdd8e98034f8fe734446d92b458a731aa4c578552ec1ac5 [...]
+	},
+	{
+		"2f6210063cb3071b3d49339185c2cef8357b08ca826d8d1acd852540c16540f1c850f70404fe1f414853d3cd15a1c64a1cce149e3ca1b80926de4ae8438ad90bdad010decf2f201782f3e49794aae1b079f54eb59607bebde508a528927e346d4e444b1d736b34f65e198df2c36fa23c64f1f1fbf8b0b8ddb85d054bdb39b8297d0347f16f7be7cd9474c058e36294485386434b36fb28ee582e393367f15ce5f5a3d6641fbd31b331f10b1554a05da726a0f35c9b1b4af3498426b17582966a266cce452900f85af1046f45a4ccedca6ce02607fb70fa45f420f66aa38cd4c9f8a30e21a3067b940aebdaaeb7c77824a79e2ba20 [...]
+		"fd5008477b0855f6f2486fd4f74b9fb4f6e19726c6996bc66893183bd76054d5b05c1c2b64722256ba912ab2dcca66d2abfdf972966438fff7513acfb18ea461eac08c4e32aea4ed3fcf9f1c9905ee4402e7b6984bef974340d212f160b6524b76de99a98d3e96cc0d35e8a63ad7ea3cbea1d40a906c4dd03e5fc19e1513e9",
+		"390a5e75c9ff4ad38fb6205ff47f209294337c1f25ff54a3c01eee8e1e220257",
+		"8bf183347ec1ca4bceff3374",
+		"19fa2641519e21293094e9d767ee1237f9e0715dc57172794867c3bbe2cb647f9b28a8d3f85c0ff557b91bad66f5ea16e0107757b0277fdd3ca05bf47c19bcb92a958a57e8c142a51af29bddb20af84377b6db65f77494e0dc4d2634a776b3a5d777319873bc0dacbbd4b9ebccfae849fa7e9769cdf54660ecca0d5cf4fa5190713726d54d02b3a3f21857125b8a808c0ca2f99d11dc430ed5113ee49ff8f00bcc08f0370dd510e8100e1285659a7b2c7457a6049f2af7786c4db1471ce5bd164e11c7a2165e83e03a135ae2b3429f82f677de044a067e99e0bda2d65a7270d629c00e1d528212d3aeb2896e58ee5145a93ed06a9 [...]
+	},
+	{
+		"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe [...]
+		"0942e506c433afcda3847f2dad",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b [...]
+	},
+	{
+		"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe [...]
+		"d3d934f75ea0f210a8f6059401",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b [...]
+	},
+	{
+		"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe [...]
+		"d3d934f75ea0f210a8f6059401beb4bc4478fa4969e623d01ada696a7e4c7e5125b34884533a94fb319990325744ee9bbce9e525cf08f5e9e25e5360aad2b2d085fa54d835e8d466826498d9a8877565705a8a3f62802944de7ca5894e5759d351adac869580ec17e485f18c0c66f17cc07cbb",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b [...]
+	},
+	{
+		"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe [...]
+		"bc",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b [...]
+	},
+	{
+		"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe [...]
+		"7cbb22fce466da610b63af62bc83b4692f3affaf271693ac071fb86d11342d",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b [...]
+	},
+	{
+		"67c6697351ff4aec29cdbaabf2fbe3467cc254f81be8e78d765a2e63339fc99a66320db73158a35a255d051758e95ed4abb2cdc69bb454110e827441213ddc8770e93ea141e1fc673e017e97eadc6b968f385c2aecb03bfb32af3c54ec18db5c021afe43fbfaaa3afb29d1e6053c7c9475d8be6189f95cbba8990f95b1ebf1b305eff700e9a13ae5ca0bcbd0484764bd1f231ea81c7b64c514735ac55e4b79633b706424119e09dcaad4acf21b10af3b33cde3504847155cbb6f2219ba9b7df50be11a1c7f23f829f8a41b13b5ca4ee8983238e0794d3d34bc5f4e77facb6c05ac86212baa1a55a2be70b5733b045cd33694b3afe [...]
+		"",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"588e1356fb8fa32410dad99cf7922aae47b4042502c92f3afe33dc22c1c2e90caf22bc37a254f8dd62a09582c70194f9616982639415178e9fe95740c0f1d497a69b69d4924a7a15290187f9c8acf09cf5b3b3188ecde2d2807207f5bb6a6d3504314b1b47684cf8ba8807eb9a3c497c79ebe1e4c1eca2aa90328563e201425227fca8ee05dcc05fd6c98128626c1e71d2fb3a21860567093db1012dfabe13055c48219d2a301c8a5a49033a811d8d9413bafbb2eefc177226fe578e93c2ef1f309416dc98843bfac387debb1b610b1d2366178ce7212a7312057a3d058357a629f18c78e129e60979a2310455a76207be5611e8b [...]
+	},
+	{
+		"0fb826ddb2eb5e708de203d0438be12cf708d635ebdbae56278be09077009586b9bc646ba7c2db35a5de05e86ae71461efea96dac64430edcf117d461113cccacf303576f310ab98efb180599894ba877e50614494923163a3afa9b4c2757f91a6b40799c5b331b464b10dfc45c783c317e408ab76390e19e8b7ceaa2c4d3bd201436bc6f69c7a5a4d8756924ed95665bd5e1034971e4d80d51b2a",
+		"026866d46aa940309fdcabf92a324fbc",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"30f05cf8189bb7b8b4f560e746e228c4cc7e86e8f2fa66e1afe212d1855db51070acd5eb34ce80b2e223957df50fde4c2531d97fc9e573725e7a5e47f0dfc4da1942620320bb2deaf8b17937bae4218d04db8e76f6efe84a117292159507c9f8a09fb2c17921d7762510dbf1dac7b62b1bd7572e3e2cf008d01c445c7fa78833235034281ae180e051451c6a64f22ca9708634bd0d604e4cfcd971b13742efa5b6363e662a875daccb2b00",
+	},
+	{
+		"c7d4f8790e4c47d4daecbddf5939973521ddbf3b832e564afc66f03b5583c41c58bd956609dc3ae3c8f7c2213059575236168dba44e3044049f47c9e7840bbd0fd5036062d70e9f567ac1797056ee93c8476f6c959fa09a3ee854166c6fc36c34d6cca7adcb36f435f86db65f4c4a1793b974294914b377fd179e697751c5ac289243c65d8aca93732849c27483da083d4e218652d4fe5fec8cb953ee7f00070143dd6ece97f241b03c0424bfee2cfd2c4e738f2361df0ffe8863dcf763d408a7a167763959b7f985bc1e359a4b22c6899645ad0814bcf69d10c38474978d1c48e482723e3a6bb3f689f980c51c474eb28cfbba91 [...]
+		"56",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"f89c825ca43cae1ce3fbdee85c505edd1aabefe69a0f9efd740f027aa7dee48a91ad24e69ad061648f0a52b4afb19d7ffccdc21f4b4247dfd89f5f9f998cb3c02b226173fedb6f8770aceef9271e7236fefd19fb3b87d08a5c587ac7918e80aa4b477f22602189811e270d686bc4949137a41d11d95ec96ee9d26c6126f6e923ab37638b34d1538d2e46d6df6216da4f193a3cecb731e632e109ced643056a1673059355d2d1314df35ded8364efed7de490201090a6f2d1751748585f64d26041637ba3723cbc4b60e226f10a19699d223075bc1f27d82e7f560c0db630ea670b3f8a70a8950894af4d1c7b3f674a3fa00d19ee4 [...]
+	},
+	{
+		"135a28170fe89066da7bcff3a9ccc1b27dfe942a6f47b23835ef746aaea63dc10066d90f4e697528e5451b8e11dd408fdbd4b94a1c6c82515bf7bc099df9cb9d5fa4acad0d22d5f267f18078cec107a995c1f3b12d7603886dbf910ab85ca7180053c50e759b00dc8c81555a425c03d71df6894a6c8cd2d94b64e303c08a1bc1dee1cf537ccf300850856292e1656aff5bf349c87f1ca1ca8085cd400fe901edcad04146a0714ef0f6b083d715edd670e020385f3cda29bc5ff6fc6edffe5ca9ce9def6e0e3d5f04ede2db02cfb2",
+		"73afd2ab0e0e8537cae42dc6530dc4afb6934ca6",
+		"a5117e70953568bf750862df9e6f92af81677c3a188e847917a4a915bda7792e",
+		"129039b5572e8a7a8131f76a",
+		"2c125232a59879aee36cacc4aca5085a4688c4f776667a8fbd86862b5cfb1d57c976688fdd652eafa2b88b1b8e358aa2110ff6ef13cdc1ceca9c9f087c35c38d89d6fbd8de89538070f17916ecb19ca3ef4a1c834f0bdaa1df62aaabef2e117106787056c909e61ecd208357dd5c363f11c5d6cf24992cc873cf69f59360a820fcf290bd90b2cab24c47286acb4e1033962b6d41e562a206a94796a8ab1c6b8bade804ff9bdf5ba6062d2c1f8fe0f4dfc05720bd9a612b92c26789f9f6a7ce43f5e8e3aee99a9cd7d6c11eaa611983c36935b0dda57d898a60a0ab7c4b54",
+	},
+}
diff --git a/src/vendor/golang_org/x/crypto/chacha20poly1305/internal/chacha20/chacha_generic.go b/src/vendor/golang_org/x/crypto/chacha20poly1305/internal/chacha20/chacha_generic.go
new file mode 100644
index 0000000..f9e8a3a
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/chacha20poly1305/internal/chacha20/chacha_generic.go
@@ -0,0 +1,199 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package ChaCha20 implements the core ChaCha20 function as specified in https://tools.ietf.org/html/rfc7539#section-2.3.
+package chacha20
+
+import "encoding/binary"
+
+const rounds = 20
+
+// core applies the ChaCha20 core function to 16-byte input in, 32-byte key k,
+// and 16-byte constant c, and puts the result into 64-byte array out.
+func core(out *[64]byte, in *[16]byte, k *[32]byte) {
+	j0 := uint32(0x61707865)
+	j1 := uint32(0x3320646e)
+	j2 := uint32(0x79622d32)
+	j3 := uint32(0x6b206574)
+	j4 := binary.LittleEndian.Uint32(k[0:4])
+	j5 := binary.LittleEndian.Uint32(k[4:8])
+	j6 := binary.LittleEndian.Uint32(k[8:12])
+	j7 := binary.LittleEndian.Uint32(k[12:16])
+	j8 := binary.LittleEndian.Uint32(k[16:20])
+	j9 := binary.LittleEndian.Uint32(k[20:24])
+	j10 := binary.LittleEndian.Uint32(k[24:28])
+	j11 := binary.LittleEndian.Uint32(k[28:32])
+	j12 := binary.LittleEndian.Uint32(in[0:4])
+	j13 := binary.LittleEndian.Uint32(in[4:8])
+	j14 := binary.LittleEndian.Uint32(in[8:12])
+	j15 := binary.LittleEndian.Uint32(in[12:16])
+
+	x0, x1, x2, x3, x4, x5, x6, x7 := j0, j1, j2, j3, j4, j5, j6, j7
+	x8, x9, x10, x11, x12, x13, x14, x15 := j8, j9, j10, j11, j12, j13, j14, j15
+
+	for i := 0; i < rounds; i += 2 {
+		x0 += x4
+		x12 ^= x0
+		x12 = (x12 << 16) | (x12 >> (16))
+		x8 += x12
+		x4 ^= x8
+		x4 = (x4 << 12) | (x4 >> (20))
+		x0 += x4
+		x12 ^= x0
+		x12 = (x12 << 8) | (x12 >> (24))
+		x8 += x12
+		x4 ^= x8
+		x4 = (x4 << 7) | (x4 >> (25))
+		x1 += x5
+		x13 ^= x1
+		x13 = (x13 << 16) | (x13 >> 16)
+		x9 += x13
+		x5 ^= x9
+		x5 = (x5 << 12) | (x5 >> 20)
+		x1 += x5
+		x13 ^= x1
+		x13 = (x13 << 8) | (x13 >> 24)
+		x9 += x13
+		x5 ^= x9
+		x5 = (x5 << 7) | (x5 >> 25)
+		x2 += x6
+		x14 ^= x2
+		x14 = (x14 << 16) | (x14 >> 16)
+		x10 += x14
+		x6 ^= x10
+		x6 = (x6 << 12) | (x6 >> 20)
+		x2 += x6
+		x14 ^= x2
+		x14 = (x14 << 8) | (x14 >> 24)
+		x10 += x14
+		x6 ^= x10
+		x6 = (x6 << 7) | (x6 >> 25)
+		x3 += x7
+		x15 ^= x3
+		x15 = (x15 << 16) | (x15 >> 16)
+		x11 += x15
+		x7 ^= x11
+		x7 = (x7 << 12) | (x7 >> 20)
+		x3 += x7
+		x15 ^= x3
+		x15 = (x15 << 8) | (x15 >> 24)
+		x11 += x15
+		x7 ^= x11
+		x7 = (x7 << 7) | (x7 >> 25)
+		x0 += x5
+		x15 ^= x0
+		x15 = (x15 << 16) | (x15 >> 16)
+		x10 += x15
+		x5 ^= x10
+		x5 = (x5 << 12) | (x5 >> 20)
+		x0 += x5
+		x15 ^= x0
+		x15 = (x15 << 8) | (x15 >> 24)
+		x10 += x15
+		x5 ^= x10
+		x5 = (x5 << 7) | (x5 >> 25)
+		x1 += x6
+		x12 ^= x1
+		x12 = (x12 << 16) | (x12 >> 16)
+		x11 += x12
+		x6 ^= x11
+		x6 = (x6 << 12) | (x6 >> 20)
+		x1 += x6
+		x12 ^= x1
+		x12 = (x12 << 8) | (x12 >> 24)
+		x11 += x12
+		x6 ^= x11
+		x6 = (x6 << 7) | (x6 >> 25)
+		x2 += x7
+		x13 ^= x2
+		x13 = (x13 << 16) | (x13 >> 16)
+		x8 += x13
+		x7 ^= x8
+		x7 = (x7 << 12) | (x7 >> 20)
+		x2 += x7
+		x13 ^= x2
+		x13 = (x13 << 8) | (x13 >> 24)
+		x8 += x13
+		x7 ^= x8
+		x7 = (x7 << 7) | (x7 >> 25)
+		x3 += x4
+		x14 ^= x3
+		x14 = (x14 << 16) | (x14 >> 16)
+		x9 += x14
+		x4 ^= x9
+		x4 = (x4 << 12) | (x4 >> 20)
+		x3 += x4
+		x14 ^= x3
+		x14 = (x14 << 8) | (x14 >> 24)
+		x9 += x14
+		x4 ^= x9
+		x4 = (x4 << 7) | (x4 >> 25)
+	}
+
+	x0 += j0
+	x1 += j1
+	x2 += j2
+	x3 += j3
+	x4 += j4
+	x5 += j5
+	x6 += j6
+	x7 += j7
+	x8 += j8
+	x9 += j9
+	x10 += j10
+	x11 += j11
+	x12 += j12
+	x13 += j13
+	x14 += j14
+	x15 += j15
+
+	binary.LittleEndian.PutUint32(out[0:4], x0)
+	binary.LittleEndian.PutUint32(out[4:8], x1)
+	binary.LittleEndian.PutUint32(out[8:12], x2)
+	binary.LittleEndian.PutUint32(out[12:16], x3)
+	binary.LittleEndian.PutUint32(out[16:20], x4)
+	binary.LittleEndian.PutUint32(out[20:24], x5)
+	binary.LittleEndian.PutUint32(out[24:28], x6)
+	binary.LittleEndian.PutUint32(out[28:32], x7)
+	binary.LittleEndian.PutUint32(out[32:36], x8)
+	binary.LittleEndian.PutUint32(out[36:40], x9)
+	binary.LittleEndian.PutUint32(out[40:44], x10)
+	binary.LittleEndian.PutUint32(out[44:48], x11)
+	binary.LittleEndian.PutUint32(out[48:52], x12)
+	binary.LittleEndian.PutUint32(out[52:56], x13)
+	binary.LittleEndian.PutUint32(out[56:60], x14)
+	binary.LittleEndian.PutUint32(out[60:64], x15)
+}
+
+// XORKeyStream crypts bytes from in to out using the given key and counters.
+// In and out may be the same slice but otherwise should not overlap. Counter
+// contains the raw ChaCha20 counter bytes (i.e. block counter followed by
+// nonce).
+func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
+	var block [64]byte
+	var counterCopy [16]byte
+	copy(counterCopy[:], counter[:])
+
+	for len(in) >= 64 {
+		core(&block, &counterCopy, key)
+		for i, x := range block {
+			out[i] = in[i] ^ x
+		}
+		u := uint32(1)
+		for i := 0; i < 4; i++ {
+			u += uint32(counterCopy[i])
+			counterCopy[i] = byte(u)
+			u >>= 8
+		}
+		in = in[64:]
+		out = out[64:]
+	}
+
+	if len(in) > 0 {
+		core(&block, &counterCopy, key)
+		for i, v := range in {
+			out[i] = v ^ block[i]
+		}
+	}
+}
diff --git a/src/vendor/golang_org/x/crypto/chacha20poly1305/internal/chacha20/chacha_test.go b/src/vendor/golang_org/x/crypto/chacha20poly1305/internal/chacha20/chacha_test.go
new file mode 100644
index 0000000..ca9663f
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/chacha20poly1305/internal/chacha20/chacha_test.go
@@ -0,0 +1,29 @@
+package chacha20
+
+import (
+	"encoding/hex"
+	"testing"
+)
+
+func TestCore(t *testing.T) {
+	// This is just a smoke test that checks the example from
+	// https://tools.ietf.org/html/rfc7539#section-2.3.2. The
+	// chacha20poly1305 package contains much more extensive tests of this
+	// code.
+	var key [32]byte
+	for i := range key {
+		key[i] = byte(i)
+	}
+
+	var input [16]byte
+	input[0] = 1
+	input[7] = 9
+	input[11] = 0x4a
+
+	var out [64]byte
+	XORKeyStream(out[:], out[:], &input, &key)
+	const expected = "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e"
+	if result := hex.EncodeToString(out[:]); result != expected {
+		t.Errorf("wanted %x but got %x", expected, result)
+	}
+}
diff --git a/src/vendor/golang_org/x/crypto/curve25519/const_amd64.s b/src/vendor/golang_org/x/crypto/curve25519/const_amd64.s
new file mode 100644
index 0000000..797f9b0
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/curve25519/const_amd64.s
@@ -0,0 +1,20 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+DATA ·REDMASK51(SB)/8, $0x0007FFFFFFFFFFFF
+GLOBL ·REDMASK51(SB), 8, $8
+
+DATA ·_121666_213(SB)/8, $996687872
+GLOBL ·_121666_213(SB), 8, $8
+
+DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA
+GLOBL ·_2P0(SB), 8, $8
+
+DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE
+GLOBL ·_2P1234(SB), 8, $8
diff --git a/src/vendor/golang_org/x/crypto/curve25519/cswap_amd64.s b/src/vendor/golang_org/x/crypto/curve25519/cswap_amd64.s
new file mode 100644
index 0000000..45484d1
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/curve25519/cswap_amd64.s
@@ -0,0 +1,88 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func cswap(inout *[5]uint64, v uint64)
+TEXT ·cswap(SB),7,$0
+	MOVQ inout+0(FP),DI
+	MOVQ v+8(FP),SI
+
+	CMPQ SI,$1
+	MOVQ 0(DI),SI
+	MOVQ 80(DI),DX
+	MOVQ 8(DI),CX
+	MOVQ 88(DI),R8
+	MOVQ SI,R9
+	CMOVQEQ DX,SI
+	CMOVQEQ R9,DX
+	MOVQ CX,R9
+	CMOVQEQ R8,CX
+	CMOVQEQ R9,R8
+	MOVQ SI,0(DI)
+	MOVQ DX,80(DI)
+	MOVQ CX,8(DI)
+	MOVQ R8,88(DI)
+	MOVQ 16(DI),SI
+	MOVQ 96(DI),DX
+	MOVQ 24(DI),CX
+	MOVQ 104(DI),R8
+	MOVQ SI,R9
+	CMOVQEQ DX,SI
+	CMOVQEQ R9,DX
+	MOVQ CX,R9
+	CMOVQEQ R8,CX
+	CMOVQEQ R9,R8
+	MOVQ SI,16(DI)
+	MOVQ DX,96(DI)
+	MOVQ CX,24(DI)
+	MOVQ R8,104(DI)
+	MOVQ 32(DI),SI
+	MOVQ 112(DI),DX
+	MOVQ 40(DI),CX
+	MOVQ 120(DI),R8
+	MOVQ SI,R9
+	CMOVQEQ DX,SI
+	CMOVQEQ R9,DX
+	MOVQ CX,R9
+	CMOVQEQ R8,CX
+	CMOVQEQ R9,R8
+	MOVQ SI,32(DI)
+	MOVQ DX,112(DI)
+	MOVQ CX,40(DI)
+	MOVQ R8,120(DI)
+	MOVQ 48(DI),SI
+	MOVQ 128(DI),DX
+	MOVQ 56(DI),CX
+	MOVQ 136(DI),R8
+	MOVQ SI,R9
+	CMOVQEQ DX,SI
+	CMOVQEQ R9,DX
+	MOVQ CX,R9
+	CMOVQEQ R8,CX
+	CMOVQEQ R9,R8
+	MOVQ SI,48(DI)
+	MOVQ DX,128(DI)
+	MOVQ CX,56(DI)
+	MOVQ R8,136(DI)
+	MOVQ 64(DI),SI
+	MOVQ 144(DI),DX
+	MOVQ 72(DI),CX
+	MOVQ 152(DI),R8
+	MOVQ SI,R9
+	CMOVQEQ DX,SI
+	CMOVQEQ R9,DX
+	MOVQ CX,R9
+	CMOVQEQ R8,CX
+	CMOVQEQ R9,R8
+	MOVQ SI,64(DI)
+	MOVQ DX,144(DI)
+	MOVQ CX,72(DI)
+	MOVQ R8,152(DI)
+	MOVQ DI,AX
+	MOVQ SI,DX
+	RET
diff --git a/src/vendor/golang_org/x/crypto/curve25519/curve25519.go b/src/vendor/golang_org/x/crypto/curve25519/curve25519.go
new file mode 100644
index 0000000..6918c47
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/curve25519/curve25519.go
@@ -0,0 +1,841 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// We have a implementation in amd64 assembly so this code is only run on
+// non-amd64 platforms. The amd64 assembly does not support gccgo.
+// +build !amd64 gccgo appengine
+
+package curve25519
+
+// This code is a port of the public domain, "ref10" implementation of
+// curve25519 from SUPERCOP 20130419 by D. J. Bernstein.
+
+// fieldElement represents an element of the field GF(2^255 - 19). An element
+// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
+// context.
+type fieldElement [10]int32
+
+func feZero(fe *fieldElement) {
+	for i := range fe {
+		fe[i] = 0
+	}
+}
+
+func feOne(fe *fieldElement) {
+	feZero(fe)
+	fe[0] = 1
+}
+
+func feAdd(dst, a, b *fieldElement) {
+	for i := range dst {
+		dst[i] = a[i] + b[i]
+	}
+}
+
+func feSub(dst, a, b *fieldElement) {
+	for i := range dst {
+		dst[i] = a[i] - b[i]
+	}
+}
+
+func feCopy(dst, src *fieldElement) {
+	for i := range dst {
+		dst[i] = src[i]
+	}
+}
+
+// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0.
+//
+// Preconditions: b in {0,1}.
+func feCSwap(f, g *fieldElement, b int32) {
+	var x fieldElement
+	b = -b
+	for i := range x {
+		x[i] = b & (f[i] ^ g[i])
+	}
+
+	for i := range f {
+		f[i] ^= x[i]
+	}
+	for i := range g {
+		g[i] ^= x[i]
+	}
+}
+
+// load3 reads a 24-bit, little-endian value from in.
+func load3(in []byte) int64 {
+	var r int64
+	r = int64(in[0])
+	r |= int64(in[1]) << 8
+	r |= int64(in[2]) << 16
+	return r
+}
+
+// load4 reads a 32-bit, little-endian value from in.
+func load4(in []byte) int64 {
+	var r int64
+	r = int64(in[0])
+	r |= int64(in[1]) << 8
+	r |= int64(in[2]) << 16
+	r |= int64(in[3]) << 24
+	return r
+}
+
+func feFromBytes(dst *fieldElement, src *[32]byte) {
+	h0 := load4(src[:])
+	h1 := load3(src[4:]) << 6
+	h2 := load3(src[7:]) << 5
+	h3 := load3(src[10:]) << 3
+	h4 := load3(src[13:]) << 2
+	h5 := load4(src[16:])
+	h6 := load3(src[20:]) << 7
+	h7 := load3(src[23:]) << 5
+	h8 := load3(src[26:]) << 4
+	h9 := load3(src[29:]) << 2
+
+	var carry [10]int64
+	carry[9] = (h9 + 1<<24) >> 25
+	h0 += carry[9] * 19
+	h9 -= carry[9] << 25
+	carry[1] = (h1 + 1<<24) >> 25
+	h2 += carry[1]
+	h1 -= carry[1] << 25
+	carry[3] = (h3 + 1<<24) >> 25
+	h4 += carry[3]
+	h3 -= carry[3] << 25
+	carry[5] = (h5 + 1<<24) >> 25
+	h6 += carry[5]
+	h5 -= carry[5] << 25
+	carry[7] = (h7 + 1<<24) >> 25
+	h8 += carry[7]
+	h7 -= carry[7] << 25
+
+	carry[0] = (h0 + 1<<25) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+	carry[2] = (h2 + 1<<25) >> 26
+	h3 += carry[2]
+	h2 -= carry[2] << 26
+	carry[4] = (h4 + 1<<25) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+	carry[6] = (h6 + 1<<25) >> 26
+	h7 += carry[6]
+	h6 -= carry[6] << 26
+	carry[8] = (h8 + 1<<25) >> 26
+	h9 += carry[8]
+	h8 -= carry[8] << 26
+
+	dst[0] = int32(h0)
+	dst[1] = int32(h1)
+	dst[2] = int32(h2)
+	dst[3] = int32(h3)
+	dst[4] = int32(h4)
+	dst[5] = int32(h5)
+	dst[6] = int32(h6)
+	dst[7] = int32(h7)
+	dst[8] = int32(h8)
+	dst[9] = int32(h9)
+}
+
+// feToBytes marshals h to s.
+// Preconditions:
+//   |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Write p=2^255-19; q=floor(h/p).
+// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+//
+// Proof:
+//   Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+//   Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
+//
+//   Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+//   Then 0<y<1.
+//
+//   Write r=h-pq.
+//   Have 0<=r<=p-1=2^255-20.
+//   Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
+//
+//   Write x=r+19(2^-255)r+y.
+//   Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
+//
+//   Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
+//   so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
+func feToBytes(s *[32]byte, h *fieldElement) {
+	var carry [10]int32
+
+	q := (19*h[9] + (1 << 24)) >> 25
+	q = (h[0] + q) >> 26
+	q = (h[1] + q) >> 25
+	q = (h[2] + q) >> 26
+	q = (h[3] + q) >> 25
+	q = (h[4] + q) >> 26
+	q = (h[5] + q) >> 25
+	q = (h[6] + q) >> 26
+	q = (h[7] + q) >> 25
+	q = (h[8] + q) >> 26
+	q = (h[9] + q) >> 25
+
+	// Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
+	h[0] += 19 * q
+	// Goal: Output h-2^255 q, which is between 0 and 2^255-20.
+
+	carry[0] = h[0] >> 26
+	h[1] += carry[0]
+	h[0] -= carry[0] << 26
+	carry[1] = h[1] >> 25
+	h[2] += carry[1]
+	h[1] -= carry[1] << 25
+	carry[2] = h[2] >> 26
+	h[3] += carry[2]
+	h[2] -= carry[2] << 26
+	carry[3] = h[3] >> 25
+	h[4] += carry[3]
+	h[3] -= carry[3] << 25
+	carry[4] = h[4] >> 26
+	h[5] += carry[4]
+	h[4] -= carry[4] << 26
+	carry[5] = h[5] >> 25
+	h[6] += carry[5]
+	h[5] -= carry[5] << 25
+	carry[6] = h[6] >> 26
+	h[7] += carry[6]
+	h[6] -= carry[6] << 26
+	carry[7] = h[7] >> 25
+	h[8] += carry[7]
+	h[7] -= carry[7] << 25
+	carry[8] = h[8] >> 26
+	h[9] += carry[8]
+	h[8] -= carry[8] << 26
+	carry[9] = h[9] >> 25
+	h[9] -= carry[9] << 25
+	// h10 = carry9
+
+	// Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+	// Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
+	// evidently 2^255 h10-2^255 q = 0.
+	// Goal: Output h[0]+...+2^230 h[9].
+
+	s[0] = byte(h[0] >> 0)
+	s[1] = byte(h[0] >> 8)
+	s[2] = byte(h[0] >> 16)
+	s[3] = byte((h[0] >> 24) | (h[1] << 2))
+	s[4] = byte(h[1] >> 6)
+	s[5] = byte(h[1] >> 14)
+	s[6] = byte((h[1] >> 22) | (h[2] << 3))
+	s[7] = byte(h[2] >> 5)
+	s[8] = byte(h[2] >> 13)
+	s[9] = byte((h[2] >> 21) | (h[3] << 5))
+	s[10] = byte(h[3] >> 3)
+	s[11] = byte(h[3] >> 11)
+	s[12] = byte((h[3] >> 19) | (h[4] << 6))
+	s[13] = byte(h[4] >> 2)
+	s[14] = byte(h[4] >> 10)
+	s[15] = byte(h[4] >> 18)
+	s[16] = byte(h[5] >> 0)
+	s[17] = byte(h[5] >> 8)
+	s[18] = byte(h[5] >> 16)
+	s[19] = byte((h[5] >> 24) | (h[6] << 1))
+	s[20] = byte(h[6] >> 7)
+	s[21] = byte(h[6] >> 15)
+	s[22] = byte((h[6] >> 23) | (h[7] << 3))
+	s[23] = byte(h[7] >> 5)
+	s[24] = byte(h[7] >> 13)
+	s[25] = byte((h[7] >> 21) | (h[8] << 4))
+	s[26] = byte(h[8] >> 4)
+	s[27] = byte(h[8] >> 12)
+	s[28] = byte((h[8] >> 20) | (h[9] << 6))
+	s[29] = byte(h[9] >> 2)
+	s[30] = byte(h[9] >> 10)
+	s[31] = byte(h[9] >> 18)
+}
+
+// feMul calculates h = f * g
+// Can overlap h with f or g.
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//    |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Notes on implementation strategy:
+//
+// Using schoolbook multiplication.
+// Karatsuba would save a little in some cost models.
+//
+// Most multiplications by 2 and 19 are 32-bit precomputations;
+// cheaper than 64-bit postcomputations.
+//
+// There is one remaining multiplication by 19 in the carry chain;
+// one *19 precomputation can be merged into this,
+// but the resulting data flow is considerably less clean.
+//
+// There are 12 carries below.
+// 10 of them are 2-way parallelizable and vectorizable.
+// Can get away with 11 carries, but then data flow is much deeper.
+//
+// With tighter constraints on inputs can squeeze carries into int32.
+func feMul(h, f, g *fieldElement) {
+	f0 := f[0]
+	f1 := f[1]
+	f2 := f[2]
+	f3 := f[3]
+	f4 := f[4]
+	f5 := f[5]
+	f6 := f[6]
+	f7 := f[7]
+	f8 := f[8]
+	f9 := f[9]
+	g0 := g[0]
+	g1 := g[1]
+	g2 := g[2]
+	g3 := g[3]
+	g4 := g[4]
+	g5 := g[5]
+	g6 := g[6]
+	g7 := g[7]
+	g8 := g[8]
+	g9 := g[9]
+	g1_19 := 19 * g1 // 1.4*2^29
+	g2_19 := 19 * g2 // 1.4*2^30; still ok
+	g3_19 := 19 * g3
+	g4_19 := 19 * g4
+	g5_19 := 19 * g5
+	g6_19 := 19 * g6
+	g7_19 := 19 * g7
+	g8_19 := 19 * g8
+	g9_19 := 19 * g9
+	f1_2 := 2 * f1
+	f3_2 := 2 * f3
+	f5_2 := 2 * f5
+	f7_2 := 2 * f7
+	f9_2 := 2 * f9
+	f0g0 := int64(f0) * int64(g0)
+	f0g1 := int64(f0) * int64(g1)
+	f0g2 := int64(f0) * int64(g2)
+	f0g3 := int64(f0) * int64(g3)
+	f0g4 := int64(f0) * int64(g4)
+	f0g5 := int64(f0) * int64(g5)
+	f0g6 := int64(f0) * int64(g6)
+	f0g7 := int64(f0) * int64(g7)
+	f0g8 := int64(f0) * int64(g8)
+	f0g9 := int64(f0) * int64(g9)
+	f1g0 := int64(f1) * int64(g0)
+	f1g1_2 := int64(f1_2) * int64(g1)
+	f1g2 := int64(f1) * int64(g2)
+	f1g3_2 := int64(f1_2) * int64(g3)
+	f1g4 := int64(f1) * int64(g4)
+	f1g5_2 := int64(f1_2) * int64(g5)
+	f1g6 := int64(f1) * int64(g6)
+	f1g7_2 := int64(f1_2) * int64(g7)
+	f1g8 := int64(f1) * int64(g8)
+	f1g9_38 := int64(f1_2) * int64(g9_19)
+	f2g0 := int64(f2) * int64(g0)
+	f2g1 := int64(f2) * int64(g1)
+	f2g2 := int64(f2) * int64(g2)
+	f2g3 := int64(f2) * int64(g3)
+	f2g4 := int64(f2) * int64(g4)
+	f2g5 := int64(f2) * int64(g5)
+	f2g6 := int64(f2) * int64(g6)
+	f2g7 := int64(f2) * int64(g7)
+	f2g8_19 := int64(f2) * int64(g8_19)
+	f2g9_19 := int64(f2) * int64(g9_19)
+	f3g0 := int64(f3) * int64(g0)
+	f3g1_2 := int64(f3_2) * int64(g1)
+	f3g2 := int64(f3) * int64(g2)
+	f3g3_2 := int64(f3_2) * int64(g3)
+	f3g4 := int64(f3) * int64(g4)
+	f3g5_2 := int64(f3_2) * int64(g5)
+	f3g6 := int64(f3) * int64(g6)
+	f3g7_38 := int64(f3_2) * int64(g7_19)
+	f3g8_19 := int64(f3) * int64(g8_19)
+	f3g9_38 := int64(f3_2) * int64(g9_19)
+	f4g0 := int64(f4) * int64(g0)
+	f4g1 := int64(f4) * int64(g1)
+	f4g2 := int64(f4) * int64(g2)
+	f4g3 := int64(f4) * int64(g3)
+	f4g4 := int64(f4) * int64(g4)
+	f4g5 := int64(f4) * int64(g5)
+	f4g6_19 := int64(f4) * int64(g6_19)
+	f4g7_19 := int64(f4) * int64(g7_19)
+	f4g8_19 := int64(f4) * int64(g8_19)
+	f4g9_19 := int64(f4) * int64(g9_19)
+	f5g0 := int64(f5) * int64(g0)
+	f5g1_2 := int64(f5_2) * int64(g1)
+	f5g2 := int64(f5) * int64(g2)
+	f5g3_2 := int64(f5_2) * int64(g3)
+	f5g4 := int64(f5) * int64(g4)
+	f5g5_38 := int64(f5_2) * int64(g5_19)
+	f5g6_19 := int64(f5) * int64(g6_19)
+	f5g7_38 := int64(f5_2) * int64(g7_19)
+	f5g8_19 := int64(f5) * int64(g8_19)
+	f5g9_38 := int64(f5_2) * int64(g9_19)
+	f6g0 := int64(f6) * int64(g0)
+	f6g1 := int64(f6) * int64(g1)
+	f6g2 := int64(f6) * int64(g2)
+	f6g3 := int64(f6) * int64(g3)
+	f6g4_19 := int64(f6) * int64(g4_19)
+	f6g5_19 := int64(f6) * int64(g5_19)
+	f6g6_19 := int64(f6) * int64(g6_19)
+	f6g7_19 := int64(f6) * int64(g7_19)
+	f6g8_19 := int64(f6) * int64(g8_19)
+	f6g9_19 := int64(f6) * int64(g9_19)
+	f7g0 := int64(f7) * int64(g0)
+	f7g1_2 := int64(f7_2) * int64(g1)
+	f7g2 := int64(f7) * int64(g2)
+	f7g3_38 := int64(f7_2) * int64(g3_19)
+	f7g4_19 := int64(f7) * int64(g4_19)
+	f7g5_38 := int64(f7_2) * int64(g5_19)
+	f7g6_19 := int64(f7) * int64(g6_19)
+	f7g7_38 := int64(f7_2) * int64(g7_19)
+	f7g8_19 := int64(f7) * int64(g8_19)
+	f7g9_38 := int64(f7_2) * int64(g9_19)
+	f8g0 := int64(f8) * int64(g0)
+	f8g1 := int64(f8) * int64(g1)
+	f8g2_19 := int64(f8) * int64(g2_19)
+	f8g3_19 := int64(f8) * int64(g3_19)
+	f8g4_19 := int64(f8) * int64(g4_19)
+	f8g5_19 := int64(f8) * int64(g5_19)
+	f8g6_19 := int64(f8) * int64(g6_19)
+	f8g7_19 := int64(f8) * int64(g7_19)
+	f8g8_19 := int64(f8) * int64(g8_19)
+	f8g9_19 := int64(f8) * int64(g9_19)
+	f9g0 := int64(f9) * int64(g0)
+	f9g1_38 := int64(f9_2) * int64(g1_19)
+	f9g2_19 := int64(f9) * int64(g2_19)
+	f9g3_38 := int64(f9_2) * int64(g3_19)
+	f9g4_19 := int64(f9) * int64(g4_19)
+	f9g5_38 := int64(f9_2) * int64(g5_19)
+	f9g6_19 := int64(f9) * int64(g6_19)
+	f9g7_38 := int64(f9_2) * int64(g7_19)
+	f9g8_19 := int64(f9) * int64(g8_19)
+	f9g9_38 := int64(f9_2) * int64(g9_19)
+	h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38
+	h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19
+	h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38
+	h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19
+	h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38
+	h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19
+	h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38
+	h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19
+	h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38
+	h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0
+	var carry [10]int64
+
+	// |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
+	//   i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
+	// |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
+	//   i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
+
+	carry[0] = (h0 + (1 << 25)) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+	carry[4] = (h4 + (1 << 25)) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+	// |h0| <= 2^25
+	// |h4| <= 2^25
+	// |h1| <= 1.51*2^58
+	// |h5| <= 1.51*2^58
+
+	carry[1] = (h1 + (1 << 24)) >> 25
+	h2 += carry[1]
+	h1 -= carry[1] << 25
+	carry[5] = (h5 + (1 << 24)) >> 25
+	h6 += carry[5]
+	h5 -= carry[5] << 25
+	// |h1| <= 2^24; from now on fits into int32
+	// |h5| <= 2^24; from now on fits into int32
+	// |h2| <= 1.21*2^59
+	// |h6| <= 1.21*2^59
+
+	carry[2] = (h2 + (1 << 25)) >> 26
+	h3 += carry[2]
+	h2 -= carry[2] << 26
+	carry[6] = (h6 + (1 << 25)) >> 26
+	h7 += carry[6]
+	h6 -= carry[6] << 26
+	// |h2| <= 2^25; from now on fits into int32 unchanged
+	// |h6| <= 2^25; from now on fits into int32 unchanged
+	// |h3| <= 1.51*2^58
+	// |h7| <= 1.51*2^58
+
+	carry[3] = (h3 + (1 << 24)) >> 25
+	h4 += carry[3]
+	h3 -= carry[3] << 25
+	carry[7] = (h7 + (1 << 24)) >> 25
+	h8 += carry[7]
+	h7 -= carry[7] << 25
+	// |h3| <= 2^24; from now on fits into int32 unchanged
+	// |h7| <= 2^24; from now on fits into int32 unchanged
+	// |h4| <= 1.52*2^33
+	// |h8| <= 1.52*2^33
+
+	carry[4] = (h4 + (1 << 25)) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+	carry[8] = (h8 + (1 << 25)) >> 26
+	h9 += carry[8]
+	h8 -= carry[8] << 26
+	// |h4| <= 2^25; from now on fits into int32 unchanged
+	// |h8| <= 2^25; from now on fits into int32 unchanged
+	// |h5| <= 1.01*2^24
+	// |h9| <= 1.51*2^58
+
+	carry[9] = (h9 + (1 << 24)) >> 25
+	h0 += carry[9] * 19
+	h9 -= carry[9] << 25
+	// |h9| <= 2^24; from now on fits into int32 unchanged
+	// |h0| <= 1.8*2^37
+
+	carry[0] = (h0 + (1 << 25)) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+	// |h0| <= 2^25; from now on fits into int32 unchanged
+	// |h1| <= 1.01*2^24
+
+	h[0] = int32(h0)
+	h[1] = int32(h1)
+	h[2] = int32(h2)
+	h[3] = int32(h3)
+	h[4] = int32(h4)
+	h[5] = int32(h5)
+	h[6] = int32(h6)
+	h[7] = int32(h7)
+	h[8] = int32(h8)
+	h[9] = int32(h9)
+}
+
+// feSquare calculates h = f*f. Can overlap h with f.
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func feSquare(h, f *fieldElement) {
+	f0 := f[0]
+	f1 := f[1]
+	f2 := f[2]
+	f3 := f[3]
+	f4 := f[4]
+	f5 := f[5]
+	f6 := f[6]
+	f7 := f[7]
+	f8 := f[8]
+	f9 := f[9]
+	f0_2 := 2 * f0
+	f1_2 := 2 * f1
+	f2_2 := 2 * f2
+	f3_2 := 2 * f3
+	f4_2 := 2 * f4
+	f5_2 := 2 * f5
+	f6_2 := 2 * f6
+	f7_2 := 2 * f7
+	f5_38 := 38 * f5 // 1.31*2^30
+	f6_19 := 19 * f6 // 1.31*2^30
+	f7_38 := 38 * f7 // 1.31*2^30
+	f8_19 := 19 * f8 // 1.31*2^30
+	f9_38 := 38 * f9 // 1.31*2^30
+	f0f0 := int64(f0) * int64(f0)
+	f0f1_2 := int64(f0_2) * int64(f1)
+	f0f2_2 := int64(f0_2) * int64(f2)
+	f0f3_2 := int64(f0_2) * int64(f3)
+	f0f4_2 := int64(f0_2) * int64(f4)
+	f0f5_2 := int64(f0_2) * int64(f5)
+	f0f6_2 := int64(f0_2) * int64(f6)
+	f0f7_2 := int64(f0_2) * int64(f7)
+	f0f8_2 := int64(f0_2) * int64(f8)
+	f0f9_2 := int64(f0_2) * int64(f9)
+	f1f1_2 := int64(f1_2) * int64(f1)
+	f1f2_2 := int64(f1_2) * int64(f2)
+	f1f3_4 := int64(f1_2) * int64(f3_2)
+	f1f4_2 := int64(f1_2) * int64(f4)
+	f1f5_4 := int64(f1_2) * int64(f5_2)
+	f1f6_2 := int64(f1_2) * int64(f6)
+	f1f7_4 := int64(f1_2) * int64(f7_2)
+	f1f8_2 := int64(f1_2) * int64(f8)
+	f1f9_76 := int64(f1_2) * int64(f9_38)
+	f2f2 := int64(f2) * int64(f2)
+	f2f3_2 := int64(f2_2) * int64(f3)
+	f2f4_2 := int64(f2_2) * int64(f4)
+	f2f5_2 := int64(f2_2) * int64(f5)
+	f2f6_2 := int64(f2_2) * int64(f6)
+	f2f7_2 := int64(f2_2) * int64(f7)
+	f2f8_38 := int64(f2_2) * int64(f8_19)
+	f2f9_38 := int64(f2) * int64(f9_38)
+	f3f3_2 := int64(f3_2) * int64(f3)
+	f3f4_2 := int64(f3_2) * int64(f4)
+	f3f5_4 := int64(f3_2) * int64(f5_2)
+	f3f6_2 := int64(f3_2) * int64(f6)
+	f3f7_76 := int64(f3_2) * int64(f7_38)
+	f3f8_38 := int64(f3_2) * int64(f8_19)
+	f3f9_76 := int64(f3_2) * int64(f9_38)
+	f4f4 := int64(f4) * int64(f4)
+	f4f5_2 := int64(f4_2) * int64(f5)
+	f4f6_38 := int64(f4_2) * int64(f6_19)
+	f4f7_38 := int64(f4) * int64(f7_38)
+	f4f8_38 := int64(f4_2) * int64(f8_19)
+	f4f9_38 := int64(f4) * int64(f9_38)
+	f5f5_38 := int64(f5) * int64(f5_38)
+	f5f6_38 := int64(f5_2) * int64(f6_19)
+	f5f7_76 := int64(f5_2) * int64(f7_38)
+	f5f8_38 := int64(f5_2) * int64(f8_19)
+	f5f9_76 := int64(f5_2) * int64(f9_38)
+	f6f6_19 := int64(f6) * int64(f6_19)
+	f6f7_38 := int64(f6) * int64(f7_38)
+	f6f8_38 := int64(f6_2) * int64(f8_19)
+	f6f9_38 := int64(f6) * int64(f9_38)
+	f7f7_38 := int64(f7) * int64(f7_38)
+	f7f8_38 := int64(f7_2) * int64(f8_19)
+	f7f9_76 := int64(f7_2) * int64(f9_38)
+	f8f8_19 := int64(f8) * int64(f8_19)
+	f8f9_38 := int64(f8) * int64(f9_38)
+	f9f9_38 := int64(f9) * int64(f9_38)
+	h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38
+	h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38
+	h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19
+	h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38
+	h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38
+	h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38
+	h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19
+	h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38
+	h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38
+	h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2
+	var carry [10]int64
+
+	carry[0] = (h0 + (1 << 25)) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+	carry[4] = (h4 + (1 << 25)) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+
+	carry[1] = (h1 + (1 << 24)) >> 25
+	h2 += carry[1]
+	h1 -= carry[1] << 25
+	carry[5] = (h5 + (1 << 24)) >> 25
+	h6 += carry[5]
+	h5 -= carry[5] << 25
+
+	carry[2] = (h2 + (1 << 25)) >> 26
+	h3 += carry[2]
+	h2 -= carry[2] << 26
+	carry[6] = (h6 + (1 << 25)) >> 26
+	h7 += carry[6]
+	h6 -= carry[6] << 26
+
+	carry[3] = (h3 + (1 << 24)) >> 25
+	h4 += carry[3]
+	h3 -= carry[3] << 25
+	carry[7] = (h7 + (1 << 24)) >> 25
+	h8 += carry[7]
+	h7 -= carry[7] << 25
+
+	carry[4] = (h4 + (1 << 25)) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+	carry[8] = (h8 + (1 << 25)) >> 26
+	h9 += carry[8]
+	h8 -= carry[8] << 26
+
+	carry[9] = (h9 + (1 << 24)) >> 25
+	h0 += carry[9] * 19
+	h9 -= carry[9] << 25
+
+	carry[0] = (h0 + (1 << 25)) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+
+	h[0] = int32(h0)
+	h[1] = int32(h1)
+	h[2] = int32(h2)
+	h[3] = int32(h3)
+	h[4] = int32(h4)
+	h[5] = int32(h5)
+	h[6] = int32(h6)
+	h[7] = int32(h7)
+	h[8] = int32(h8)
+	h[9] = int32(h9)
+}
+
+// feMul121666 calculates h = f * 121666. Can overlap h with f.
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func feMul121666(h, f *fieldElement) {
+	h0 := int64(f[0]) * 121666
+	h1 := int64(f[1]) * 121666
+	h2 := int64(f[2]) * 121666
+	h3 := int64(f[3]) * 121666
+	h4 := int64(f[4]) * 121666
+	h5 := int64(f[5]) * 121666
+	h6 := int64(f[6]) * 121666
+	h7 := int64(f[7]) * 121666
+	h8 := int64(f[8]) * 121666
+	h9 := int64(f[9]) * 121666
+	var carry [10]int64
+
+	carry[9] = (h9 + (1 << 24)) >> 25
+	h0 += carry[9] * 19
+	h9 -= carry[9] << 25
+	carry[1] = (h1 + (1 << 24)) >> 25
+	h2 += carry[1]
+	h1 -= carry[1] << 25
+	carry[3] = (h3 + (1 << 24)) >> 25
+	h4 += carry[3]
+	h3 -= carry[3] << 25
+	carry[5] = (h5 + (1 << 24)) >> 25
+	h6 += carry[5]
+	h5 -= carry[5] << 25
+	carry[7] = (h7 + (1 << 24)) >> 25
+	h8 += carry[7]
+	h7 -= carry[7] << 25
+
+	carry[0] = (h0 + (1 << 25)) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+	carry[2] = (h2 + (1 << 25)) >> 26
+	h3 += carry[2]
+	h2 -= carry[2] << 26
+	carry[4] = (h4 + (1 << 25)) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+	carry[6] = (h6 + (1 << 25)) >> 26
+	h7 += carry[6]
+	h6 -= carry[6] << 26
+	carry[8] = (h8 + (1 << 25)) >> 26
+	h9 += carry[8]
+	h8 -= carry[8] << 26
+
+	h[0] = int32(h0)
+	h[1] = int32(h1)
+	h[2] = int32(h2)
+	h[3] = int32(h3)
+	h[4] = int32(h4)
+	h[5] = int32(h5)
+	h[6] = int32(h6)
+	h[7] = int32(h7)
+	h[8] = int32(h8)
+	h[9] = int32(h9)
+}
+
+// feInvert sets out = z^-1.
+func feInvert(out, z *fieldElement) {
+	var t0, t1, t2, t3 fieldElement
+	var i int
+
+	feSquare(&t0, z)
+	for i = 1; i < 1; i++ {
+		feSquare(&t0, &t0)
+	}
+	feSquare(&t1, &t0)
+	for i = 1; i < 2; i++ {
+		feSquare(&t1, &t1)
+	}
+	feMul(&t1, z, &t1)
+	feMul(&t0, &t0, &t1)
+	feSquare(&t2, &t0)
+	for i = 1; i < 1; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t1, &t1, &t2)
+	feSquare(&t2, &t1)
+	for i = 1; i < 5; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t1, &t2, &t1)
+	feSquare(&t2, &t1)
+	for i = 1; i < 10; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t2, &t2, &t1)
+	feSquare(&t3, &t2)
+	for i = 1; i < 20; i++ {
+		feSquare(&t3, &t3)
+	}
+	feMul(&t2, &t3, &t2)
+	feSquare(&t2, &t2)
+	for i = 1; i < 10; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t1, &t2, &t1)
+	feSquare(&t2, &t1)
+	for i = 1; i < 50; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t2, &t2, &t1)
+	feSquare(&t3, &t2)
+	for i = 1; i < 100; i++ {
+		feSquare(&t3, &t3)
+	}
+	feMul(&t2, &t3, &t2)
+	feSquare(&t2, &t2)
+	for i = 1; i < 50; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t1, &t2, &t1)
+	feSquare(&t1, &t1)
+	for i = 1; i < 5; i++ {
+		feSquare(&t1, &t1)
+	}
+	feMul(out, &t1, &t0)
+}
+
+func scalarMult(out, in, base *[32]byte) {
+	var e [32]byte
+
+	copy(e[:], in[:])
+	e[0] &= 248
+	e[31] &= 127
+	e[31] |= 64
+
+	var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement
+	feFromBytes(&x1, base)
+	feOne(&x2)
+	feCopy(&x3, &x1)
+	feOne(&z3)
+
+	swap := int32(0)
+	for pos := 254; pos >= 0; pos-- {
+		b := e[pos/8] >> uint(pos&7)
+		b &= 1
+		swap ^= int32(b)
+		feCSwap(&x2, &x3, swap)
+		feCSwap(&z2, &z3, swap)
+		swap = int32(b)
+
+		feSub(&tmp0, &x3, &z3)
+		feSub(&tmp1, &x2, &z2)
+		feAdd(&x2, &x2, &z2)
+		feAdd(&z2, &x3, &z3)
+		feMul(&z3, &tmp0, &x2)
+		feMul(&z2, &z2, &tmp1)
+		feSquare(&tmp0, &tmp1)
+		feSquare(&tmp1, &x2)
+		feAdd(&x3, &z3, &z2)
+		feSub(&z2, &z3, &z2)
+		feMul(&x2, &tmp1, &tmp0)
+		feSub(&tmp1, &tmp1, &tmp0)
+		feSquare(&z2, &z2)
+		feMul121666(&z3, &tmp1)
+		feSquare(&x3, &x3)
+		feAdd(&tmp0, &tmp0, &z3)
+		feMul(&z3, &x1, &z2)
+		feMul(&z2, &tmp1, &tmp0)
+	}
+
+	feCSwap(&x2, &x3, swap)
+	feCSwap(&z2, &z3, swap)
+
+	feInvert(&z2, &z2)
+	feMul(&x2, &x2, &z2)
+	feToBytes(out, &x2)
+}
diff --git a/src/vendor/golang_org/x/crypto/curve25519/curve25519_test.go b/src/vendor/golang_org/x/crypto/curve25519/curve25519_test.go
new file mode 100644
index 0000000..14b0ee8
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/curve25519/curve25519_test.go
@@ -0,0 +1,29 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package curve25519
+
+import (
+	"fmt"
+	"testing"
+)
+
+const expectedHex = "89161fde887b2b53de549af483940106ecc114d6982daa98256de23bdf77661a"
+
+func TestBaseScalarMult(t *testing.T) {
+	var a, b [32]byte
+	in := &a
+	out := &b
+	a[0] = 1
+
+	for i := 0; i < 200; i++ {
+		ScalarBaseMult(out, in)
+		in, out = out, in
+	}
+
+	result := fmt.Sprintf("%x", in[:])
+	if result != expectedHex {
+		t.Errorf("incorrect result: got %s, want %s", result, expectedHex)
+	}
+}
diff --git a/src/vendor/golang_org/x/crypto/curve25519/doc.go b/src/vendor/golang_org/x/crypto/curve25519/doc.go
new file mode 100644
index 0000000..ebeea3c
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/curve25519/doc.go
@@ -0,0 +1,23 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package curve25519 provides an implementation of scalar multiplication on
+// the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html
+package curve25519 // import "golang.org/x/crypto/curve25519"
+
+// basePoint is the x coordinate of the generator of the curve.
+var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+// ScalarMult sets dst to the product in*base where dst and base are the x
+// coordinates of group points and all values are in little-endian form.
+func ScalarMult(dst, in, base *[32]byte) {
+	scalarMult(dst, in, base)
+}
+
+// ScalarBaseMult sets dst to the product in*base where dst and base are the x
+// coordinates of group points, base is the standard generator and all values
+// are in little-endian form.
+func ScalarBaseMult(dst, in *[32]byte) {
+	ScalarMult(dst, in, &basePoint)
+}
diff --git a/src/vendor/golang_org/x/crypto/curve25519/freeze_amd64.s b/src/vendor/golang_org/x/crypto/curve25519/freeze_amd64.s
new file mode 100644
index 0000000..932800b
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/curve25519/freeze_amd64.s
@@ -0,0 +1,71 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func freeze(inout *[5]uint64)
+TEXT ·freeze(SB),7,$0-8
+	MOVQ inout+0(FP), DI
+
+	MOVQ 0(DI),SI
+	MOVQ 8(DI),DX
+	MOVQ 16(DI),CX
+	MOVQ 24(DI),R8
+	MOVQ 32(DI),R9
+	MOVQ ·REDMASK51(SB),AX
+	MOVQ AX,R10
+	SUBQ $18,R10
+	MOVQ $3,R11
+REDUCELOOP:
+	MOVQ SI,R12
+	SHRQ $51,R12
+	ANDQ AX,SI
+	ADDQ R12,DX
+	MOVQ DX,R12
+	SHRQ $51,R12
+	ANDQ AX,DX
+	ADDQ R12,CX
+	MOVQ CX,R12
+	SHRQ $51,R12
+	ANDQ AX,CX
+	ADDQ R12,R8
+	MOVQ R8,R12
+	SHRQ $51,R12
+	ANDQ AX,R8
+	ADDQ R12,R9
+	MOVQ R9,R12
+	SHRQ $51,R12
+	ANDQ AX,R9
+	IMUL3Q $19,R12,R12
+	ADDQ R12,SI
+	SUBQ $1,R11
+	JA REDUCELOOP
+	MOVQ $1,R12
+	CMPQ R10,SI
+	CMOVQLT R11,R12
+	CMPQ AX,DX
+	CMOVQNE R11,R12
+	CMPQ AX,CX
+	CMOVQNE R11,R12
+	CMPQ AX,R8
+	CMOVQNE R11,R12
+	CMPQ AX,R9
+	CMOVQNE R11,R12
+	NEGQ R12
+	ANDQ R12,AX
+	ANDQ R12,R10
+	SUBQ R10,SI
+	SUBQ AX,DX
+	SUBQ AX,CX
+	SUBQ AX,R8
+	SUBQ AX,R9
+	MOVQ SI,0(DI)
+	MOVQ DX,8(DI)
+	MOVQ CX,16(DI)
+	MOVQ R8,24(DI)
+	MOVQ R9,32(DI)
+	RET
diff --git a/src/vendor/golang_org/x/crypto/curve25519/ladderstep_amd64.s b/src/vendor/golang_org/x/crypto/curve25519/ladderstep_amd64.s
new file mode 100644
index 0000000..ee7b36c
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/curve25519/ladderstep_amd64.s
@@ -0,0 +1,1375 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func ladderstep(inout *[5][5]uint64)
+TEXT ·ladderstep(SB),0,$296-8
+	MOVQ inout+0(FP),DI
+
+	MOVQ 40(DI),SI
+	MOVQ 48(DI),DX
+	MOVQ 56(DI),CX
+	MOVQ 64(DI),R8
+	MOVQ 72(DI),R9
+	MOVQ SI,AX
+	MOVQ DX,R10
+	MOVQ CX,R11
+	MOVQ R8,R12
+	MOVQ R9,R13
+	ADDQ ·_2P0(SB),AX
+	ADDQ ·_2P1234(SB),R10
+	ADDQ ·_2P1234(SB),R11
+	ADDQ ·_2P1234(SB),R12
+	ADDQ ·_2P1234(SB),R13
+	ADDQ 80(DI),SI
+	ADDQ 88(DI),DX
+	ADDQ 96(DI),CX
+	ADDQ 104(DI),R8
+	ADDQ 112(DI),R9
+	SUBQ 80(DI),AX
+	SUBQ 88(DI),R10
+	SUBQ 96(DI),R11
+	SUBQ 104(DI),R12
+	SUBQ 112(DI),R13
+	MOVQ SI,0(SP)
+	MOVQ DX,8(SP)
+	MOVQ CX,16(SP)
+	MOVQ R8,24(SP)
+	MOVQ R9,32(SP)
+	MOVQ AX,40(SP)
+	MOVQ R10,48(SP)
+	MOVQ R11,56(SP)
+	MOVQ R12,64(SP)
+	MOVQ R13,72(SP)
+	MOVQ 40(SP),AX
+	MULQ 40(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 40(SP),AX
+	SHLQ $1,AX
+	MULQ 48(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 40(SP),AX
+	SHLQ $1,AX
+	MULQ 56(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 40(SP),AX
+	SHLQ $1,AX
+	MULQ 64(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 40(SP),AX
+	SHLQ $1,AX
+	MULQ 72(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 48(SP),AX
+	MULQ 48(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 48(SP),AX
+	SHLQ $1,AX
+	MULQ 56(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 48(SP),AX
+	SHLQ $1,AX
+	MULQ 64(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 48(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 72(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 56(SP),AX
+	MULQ 56(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 56(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 64(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 56(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 72(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 64(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 64(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 64(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 72(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 72(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 72(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	ANDQ DX,SI
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ADDQ R10,CX
+	ANDQ DX,R8
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ADDQ R12,CX
+	ANDQ DX,R9
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ADDQ R14,CX
+	ANDQ DX,AX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,80(SP)
+	MOVQ R8,88(SP)
+	MOVQ R9,96(SP)
+	MOVQ AX,104(SP)
+	MOVQ R10,112(SP)
+	MOVQ 0(SP),AX
+	MULQ 0(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 0(SP),AX
+	SHLQ $1,AX
+	MULQ 8(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 0(SP),AX
+	SHLQ $1,AX
+	MULQ 16(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 0(SP),AX
+	SHLQ $1,AX
+	MULQ 24(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 0(SP),AX
+	SHLQ $1,AX
+	MULQ 32(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 8(SP),AX
+	MULQ 8(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 8(SP),AX
+	SHLQ $1,AX
+	MULQ 16(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 8(SP),AX
+	SHLQ $1,AX
+	MULQ 24(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 8(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 32(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 16(SP),AX
+	MULQ 16(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 16(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 24(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 16(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 32(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 24(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 24(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 24(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 32(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 32(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	ANDQ DX,SI
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ADDQ R10,CX
+	ANDQ DX,R8
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ADDQ R12,CX
+	ANDQ DX,R9
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ADDQ R14,CX
+	ANDQ DX,AX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,120(SP)
+	MOVQ R8,128(SP)
+	MOVQ R9,136(SP)
+	MOVQ AX,144(SP)
+	MOVQ R10,152(SP)
+	MOVQ SI,SI
+	MOVQ R8,DX
+	MOVQ R9,CX
+	MOVQ AX,R8
+	MOVQ R10,R9
+	ADDQ ·_2P0(SB),SI
+	ADDQ ·_2P1234(SB),DX
+	ADDQ ·_2P1234(SB),CX
+	ADDQ ·_2P1234(SB),R8
+	ADDQ ·_2P1234(SB),R9
+	SUBQ 80(SP),SI
+	SUBQ 88(SP),DX
+	SUBQ 96(SP),CX
+	SUBQ 104(SP),R8
+	SUBQ 112(SP),R9
+	MOVQ SI,160(SP)
+	MOVQ DX,168(SP)
+	MOVQ CX,176(SP)
+	MOVQ R8,184(SP)
+	MOVQ R9,192(SP)
+	MOVQ 120(DI),SI
+	MOVQ 128(DI),DX
+	MOVQ 136(DI),CX
+	MOVQ 144(DI),R8
+	MOVQ 152(DI),R9
+	MOVQ SI,AX
+	MOVQ DX,R10
+	MOVQ CX,R11
+	MOVQ R8,R12
+	MOVQ R9,R13
+	ADDQ ·_2P0(SB),AX
+	ADDQ ·_2P1234(SB),R10
+	ADDQ ·_2P1234(SB),R11
+	ADDQ ·_2P1234(SB),R12
+	ADDQ ·_2P1234(SB),R13
+	ADDQ 160(DI),SI
+	ADDQ 168(DI),DX
+	ADDQ 176(DI),CX
+	ADDQ 184(DI),R8
+	ADDQ 192(DI),R9
+	SUBQ 160(DI),AX
+	SUBQ 168(DI),R10
+	SUBQ 176(DI),R11
+	SUBQ 184(DI),R12
+	SUBQ 192(DI),R13
+	MOVQ SI,200(SP)
+	MOVQ DX,208(SP)
+	MOVQ CX,216(SP)
+	MOVQ R8,224(SP)
+	MOVQ R9,232(SP)
+	MOVQ AX,240(SP)
+	MOVQ R10,248(SP)
+	MOVQ R11,256(SP)
+	MOVQ R12,264(SP)
+	MOVQ R13,272(SP)
+	MOVQ 224(SP),SI
+	IMUL3Q $19,SI,AX
+	MOVQ AX,280(SP)
+	MULQ 56(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 232(SP),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,288(SP)
+	MULQ 48(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 200(SP),AX
+	MULQ 40(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 200(SP),AX
+	MULQ 48(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 200(SP),AX
+	MULQ 56(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 200(SP),AX
+	MULQ 64(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 200(SP),AX
+	MULQ 72(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 208(SP),AX
+	MULQ 40(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 208(SP),AX
+	MULQ 48(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 208(SP),AX
+	MULQ 56(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 208(SP),AX
+	MULQ 64(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 208(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 72(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 216(SP),AX
+	MULQ 40(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 216(SP),AX
+	MULQ 48(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 216(SP),AX
+	MULQ 56(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 216(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 64(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 216(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 72(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 224(SP),AX
+	MULQ 40(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 224(SP),AX
+	MULQ 48(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 280(SP),AX
+	MULQ 64(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 280(SP),AX
+	MULQ 72(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 232(SP),AX
+	MULQ 40(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 288(SP),AX
+	MULQ 56(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 288(SP),AX
+	MULQ 64(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 288(SP),AX
+	MULQ 72(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ANDQ DX,SI
+	ADDQ R10,CX
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ANDQ DX,R8
+	ADDQ R12,CX
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ANDQ DX,R9
+	ADDQ R14,CX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	ANDQ DX,AX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,40(SP)
+	MOVQ R8,48(SP)
+	MOVQ R9,56(SP)
+	MOVQ AX,64(SP)
+	MOVQ R10,72(SP)
+	MOVQ 264(SP),SI
+	IMUL3Q $19,SI,AX
+	MOVQ AX,200(SP)
+	MULQ 16(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 272(SP),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,208(SP)
+	MULQ 8(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 240(SP),AX
+	MULQ 0(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 240(SP),AX
+	MULQ 8(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 240(SP),AX
+	MULQ 16(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 240(SP),AX
+	MULQ 24(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 240(SP),AX
+	MULQ 32(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 248(SP),AX
+	MULQ 0(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 248(SP),AX
+	MULQ 8(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 248(SP),AX
+	MULQ 16(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 248(SP),AX
+	MULQ 24(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 248(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 256(SP),AX
+	MULQ 0(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 256(SP),AX
+	MULQ 8(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 256(SP),AX
+	MULQ 16(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 256(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 24(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 256(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 264(SP),AX
+	MULQ 0(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 264(SP),AX
+	MULQ 8(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 200(SP),AX
+	MULQ 24(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 200(SP),AX
+	MULQ 32(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 272(SP),AX
+	MULQ 0(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 208(SP),AX
+	MULQ 16(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 208(SP),AX
+	MULQ 24(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 208(SP),AX
+	MULQ 32(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ANDQ DX,SI
+	ADDQ R10,CX
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ANDQ DX,R8
+	ADDQ R12,CX
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ANDQ DX,R9
+	ADDQ R14,CX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	ANDQ DX,AX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,DX
+	MOVQ R8,CX
+	MOVQ R9,R11
+	MOVQ AX,R12
+	MOVQ R10,R13
+	ADDQ ·_2P0(SB),DX
+	ADDQ ·_2P1234(SB),CX
+	ADDQ ·_2P1234(SB),R11
+	ADDQ ·_2P1234(SB),R12
+	ADDQ ·_2P1234(SB),R13
+	ADDQ 40(SP),SI
+	ADDQ 48(SP),R8
+	ADDQ 56(SP),R9
+	ADDQ 64(SP),AX
+	ADDQ 72(SP),R10
+	SUBQ 40(SP),DX
+	SUBQ 48(SP),CX
+	SUBQ 56(SP),R11
+	SUBQ 64(SP),R12
+	SUBQ 72(SP),R13
+	MOVQ SI,120(DI)
+	MOVQ R8,128(DI)
+	MOVQ R9,136(DI)
+	MOVQ AX,144(DI)
+	MOVQ R10,152(DI)
+	MOVQ DX,160(DI)
+	MOVQ CX,168(DI)
+	MOVQ R11,176(DI)
+	MOVQ R12,184(DI)
+	MOVQ R13,192(DI)
+	MOVQ 120(DI),AX
+	MULQ 120(DI)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 120(DI),AX
+	SHLQ $1,AX
+	MULQ 128(DI)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 120(DI),AX
+	SHLQ $1,AX
+	MULQ 136(DI)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 120(DI),AX
+	SHLQ $1,AX
+	MULQ 144(DI)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 120(DI),AX
+	SHLQ $1,AX
+	MULQ 152(DI)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 128(DI),AX
+	MULQ 128(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 128(DI),AX
+	SHLQ $1,AX
+	MULQ 136(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 128(DI),AX
+	SHLQ $1,AX
+	MULQ 144(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 128(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 152(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 136(DI),AX
+	MULQ 136(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 136(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 144(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 136(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 152(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 144(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 144(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 144(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 152(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 152(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 152(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	ANDQ DX,SI
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ADDQ R10,CX
+	ANDQ DX,R8
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ADDQ R12,CX
+	ANDQ DX,R9
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ADDQ R14,CX
+	ANDQ DX,AX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,120(DI)
+	MOVQ R8,128(DI)
+	MOVQ R9,136(DI)
+	MOVQ AX,144(DI)
+	MOVQ R10,152(DI)
+	MOVQ 160(DI),AX
+	MULQ 160(DI)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 160(DI),AX
+	SHLQ $1,AX
+	MULQ 168(DI)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 160(DI),AX
+	SHLQ $1,AX
+	MULQ 176(DI)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 160(DI),AX
+	SHLQ $1,AX
+	MULQ 184(DI)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 160(DI),AX
+	SHLQ $1,AX
+	MULQ 192(DI)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 168(DI),AX
+	MULQ 168(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 168(DI),AX
+	SHLQ $1,AX
+	MULQ 176(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 168(DI),AX
+	SHLQ $1,AX
+	MULQ 184(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 168(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 192(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 176(DI),AX
+	MULQ 176(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 176(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 184(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 176(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 192(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 184(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 184(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 184(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 192(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 192(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 192(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	ANDQ DX,SI
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ADDQ R10,CX
+	ANDQ DX,R8
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ADDQ R12,CX
+	ANDQ DX,R9
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ADDQ R14,CX
+	ANDQ DX,AX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,160(DI)
+	MOVQ R8,168(DI)
+	MOVQ R9,176(DI)
+	MOVQ AX,184(DI)
+	MOVQ R10,192(DI)
+	MOVQ 184(DI),SI
+	IMUL3Q $19,SI,AX
+	MOVQ AX,0(SP)
+	MULQ 16(DI)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 192(DI),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,8(SP)
+	MULQ 8(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 160(DI),AX
+	MULQ 0(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 160(DI),AX
+	MULQ 8(DI)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 160(DI),AX
+	MULQ 16(DI)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 160(DI),AX
+	MULQ 24(DI)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 160(DI),AX
+	MULQ 32(DI)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 168(DI),AX
+	MULQ 0(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 168(DI),AX
+	MULQ 8(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 168(DI),AX
+	MULQ 16(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 168(DI),AX
+	MULQ 24(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 168(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 176(DI),AX
+	MULQ 0(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 176(DI),AX
+	MULQ 8(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 176(DI),AX
+	MULQ 16(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 176(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 24(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 176(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 184(DI),AX
+	MULQ 0(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 184(DI),AX
+	MULQ 8(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 0(SP),AX
+	MULQ 24(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 0(SP),AX
+	MULQ 32(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 192(DI),AX
+	MULQ 0(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 8(SP),AX
+	MULQ 16(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 8(SP),AX
+	MULQ 24(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 8(SP),AX
+	MULQ 32(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ANDQ DX,SI
+	ADDQ R10,CX
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ANDQ DX,R8
+	ADDQ R12,CX
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ANDQ DX,R9
+	ADDQ R14,CX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	ANDQ DX,AX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,160(DI)
+	MOVQ R8,168(DI)
+	MOVQ R9,176(DI)
+	MOVQ AX,184(DI)
+	MOVQ R10,192(DI)
+	MOVQ 144(SP),SI
+	IMUL3Q $19,SI,AX
+	MOVQ AX,0(SP)
+	MULQ 96(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 152(SP),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,8(SP)
+	MULQ 88(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 120(SP),AX
+	MULQ 80(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 120(SP),AX
+	MULQ 88(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 120(SP),AX
+	MULQ 96(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 120(SP),AX
+	MULQ 104(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 120(SP),AX
+	MULQ 112(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 128(SP),AX
+	MULQ 80(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 128(SP),AX
+	MULQ 88(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 128(SP),AX
+	MULQ 96(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 128(SP),AX
+	MULQ 104(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 128(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 112(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 136(SP),AX
+	MULQ 80(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 136(SP),AX
+	MULQ 88(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 136(SP),AX
+	MULQ 96(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 136(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 104(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 136(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 112(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 144(SP),AX
+	MULQ 80(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 144(SP),AX
+	MULQ 88(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 0(SP),AX
+	MULQ 104(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 0(SP),AX
+	MULQ 112(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 152(SP),AX
+	MULQ 80(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 8(SP),AX
+	MULQ 96(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 8(SP),AX
+	MULQ 104(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 8(SP),AX
+	MULQ 112(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ANDQ DX,SI
+	ADDQ R10,CX
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ANDQ DX,R8
+	ADDQ R12,CX
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ANDQ DX,R9
+	ADDQ R14,CX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	ANDQ DX,AX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,40(DI)
+	MOVQ R8,48(DI)
+	MOVQ R9,56(DI)
+	MOVQ AX,64(DI)
+	MOVQ R10,72(DI)
+	MOVQ 160(SP),AX
+	MULQ ·_121666_213(SB)
+	SHRQ $13,AX
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 168(SP),AX
+	MULQ ·_121666_213(SB)
+	SHRQ $13,AX
+	ADDQ AX,CX
+	MOVQ DX,R8
+	MOVQ 176(SP),AX
+	MULQ ·_121666_213(SB)
+	SHRQ $13,AX
+	ADDQ AX,R8
+	MOVQ DX,R9
+	MOVQ 184(SP),AX
+	MULQ ·_121666_213(SB)
+	SHRQ $13,AX
+	ADDQ AX,R9
+	MOVQ DX,R10
+	MOVQ 192(SP),AX
+	MULQ ·_121666_213(SB)
+	SHRQ $13,AX
+	ADDQ AX,R10
+	IMUL3Q $19,DX,DX
+	ADDQ DX,SI
+	ADDQ 80(SP),SI
+	ADDQ 88(SP),CX
+	ADDQ 96(SP),R8
+	ADDQ 104(SP),R9
+	ADDQ 112(SP),R10
+	MOVQ SI,80(DI)
+	MOVQ CX,88(DI)
+	MOVQ R8,96(DI)
+	MOVQ R9,104(DI)
+	MOVQ R10,112(DI)
+	MOVQ 104(DI),SI
+	IMUL3Q $19,SI,AX
+	MOVQ AX,0(SP)
+	MULQ 176(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 112(DI),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,8(SP)
+	MULQ 168(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 80(DI),AX
+	MULQ 160(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 80(DI),AX
+	MULQ 168(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 80(DI),AX
+	MULQ 176(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 80(DI),AX
+	MULQ 184(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 80(DI),AX
+	MULQ 192(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 88(DI),AX
+	MULQ 160(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 88(DI),AX
+	MULQ 168(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 88(DI),AX
+	MULQ 176(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 88(DI),AX
+	MULQ 184(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 88(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 192(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 96(DI),AX
+	MULQ 160(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 96(DI),AX
+	MULQ 168(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 96(DI),AX
+	MULQ 176(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 96(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 184(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 96(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 192(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 104(DI),AX
+	MULQ 160(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 104(DI),AX
+	MULQ 168(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 0(SP),AX
+	MULQ 184(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 0(SP),AX
+	MULQ 192(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 112(DI),AX
+	MULQ 160(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 8(SP),AX
+	MULQ 176(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 8(SP),AX
+	MULQ 184(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 8(SP),AX
+	MULQ 192(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ANDQ DX,SI
+	ADDQ R10,CX
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ANDQ DX,R8
+	ADDQ R12,CX
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ANDQ DX,R9
+	ADDQ R14,CX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	ANDQ DX,AX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,80(DI)
+	MOVQ R8,88(DI)
+	MOVQ R9,96(DI)
+	MOVQ AX,104(DI)
+	MOVQ R10,112(DI)
+	RET
diff --git a/src/vendor/golang_org/x/crypto/curve25519/mont25519_amd64.go b/src/vendor/golang_org/x/crypto/curve25519/mont25519_amd64.go
new file mode 100644
index 0000000..5822bd5
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/curve25519/mont25519_amd64.go
@@ -0,0 +1,240 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!gccgo,!appengine
+
+package curve25519
+
+// These functions are implemented in the .s files. The names of the functions
+// in the rest of the file are also taken from the SUPERCOP sources to help
+// people following along.
+
+//go:noescape
+
+func cswap(inout *[5]uint64, v uint64)
+
+//go:noescape
+
+func ladderstep(inout *[5][5]uint64)
+
+//go:noescape
+
+func freeze(inout *[5]uint64)
+
+//go:noescape
+
+func mul(dest, a, b *[5]uint64)
+
+//go:noescape
+
+func square(out, in *[5]uint64)
+
+// mladder uses a Montgomery ladder to calculate (xr/zr) *= s.
+func mladder(xr, zr *[5]uint64, s *[32]byte) {
+	var work [5][5]uint64
+
+	work[0] = *xr
+	setint(&work[1], 1)
+	setint(&work[2], 0)
+	work[3] = *xr
+	setint(&work[4], 1)
+
+	j := uint(6)
+	var prevbit byte
+
+	for i := 31; i >= 0; i-- {
+		for j < 8 {
+			bit := ((*s)[i] >> j) & 1
+			swap := bit ^ prevbit
+			prevbit = bit
+			cswap(&work[1], uint64(swap))
+			ladderstep(&work)
+			j--
+		}
+		j = 7
+	}
+
+	*xr = work[1]
+	*zr = work[2]
+}
+
+func scalarMult(out, in, base *[32]byte) {
+	var e [32]byte
+	copy(e[:], (*in)[:])
+	e[0] &= 248
+	e[31] &= 127
+	e[31] |= 64
+
+	var t, z [5]uint64
+	unpack(&t, base)
+	mladder(&t, &z, &e)
+	invert(&z, &z)
+	mul(&t, &t, &z)
+	pack(out, &t)
+}
+
+func setint(r *[5]uint64, v uint64) {
+	r[0] = v
+	r[1] = 0
+	r[2] = 0
+	r[3] = 0
+	r[4] = 0
+}
+
+// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian
+// order.
+func unpack(r *[5]uint64, x *[32]byte) {
+	r[0] = uint64(x[0]) |
+		uint64(x[1])<<8 |
+		uint64(x[2])<<16 |
+		uint64(x[3])<<24 |
+		uint64(x[4])<<32 |
+		uint64(x[5])<<40 |
+		uint64(x[6]&7)<<48
+
+	r[1] = uint64(x[6])>>3 |
+		uint64(x[7])<<5 |
+		uint64(x[8])<<13 |
+		uint64(x[9])<<21 |
+		uint64(x[10])<<29 |
+		uint64(x[11])<<37 |
+		uint64(x[12]&63)<<45
+
+	r[2] = uint64(x[12])>>6 |
+		uint64(x[13])<<2 |
+		uint64(x[14])<<10 |
+		uint64(x[15])<<18 |
+		uint64(x[16])<<26 |
+		uint64(x[17])<<34 |
+		uint64(x[18])<<42 |
+		uint64(x[19]&1)<<50
+
+	r[3] = uint64(x[19])>>1 |
+		uint64(x[20])<<7 |
+		uint64(x[21])<<15 |
+		uint64(x[22])<<23 |
+		uint64(x[23])<<31 |
+		uint64(x[24])<<39 |
+		uint64(x[25]&15)<<47
+
+	r[4] = uint64(x[25])>>4 |
+		uint64(x[26])<<4 |
+		uint64(x[27])<<12 |
+		uint64(x[28])<<20 |
+		uint64(x[29])<<28 |
+		uint64(x[30])<<36 |
+		uint64(x[31]&127)<<44
+}
+
+// pack sets out = x where out is the usual, little-endian form of the 5,
+// 51-bit limbs in x.
+func pack(out *[32]byte, x *[5]uint64) {
+	t := *x
+	freeze(&t)
+
+	out[0] = byte(t[0])
+	out[1] = byte(t[0] >> 8)
+	out[2] = byte(t[0] >> 16)
+	out[3] = byte(t[0] >> 24)
+	out[4] = byte(t[0] >> 32)
+	out[5] = byte(t[0] >> 40)
+	out[6] = byte(t[0] >> 48)
+
+	out[6] ^= byte(t[1]<<3) & 0xf8
+	out[7] = byte(t[1] >> 5)
+	out[8] = byte(t[1] >> 13)
+	out[9] = byte(t[1] >> 21)
+	out[10] = byte(t[1] >> 29)
+	out[11] = byte(t[1] >> 37)
+	out[12] = byte(t[1] >> 45)
+
+	out[12] ^= byte(t[2]<<6) & 0xc0
+	out[13] = byte(t[2] >> 2)
+	out[14] = byte(t[2] >> 10)
+	out[15] = byte(t[2] >> 18)
+	out[16] = byte(t[2] >> 26)
+	out[17] = byte(t[2] >> 34)
+	out[18] = byte(t[2] >> 42)
+	out[19] = byte(t[2] >> 50)
+
+	out[19] ^= byte(t[3]<<1) & 0xfe
+	out[20] = byte(t[3] >> 7)
+	out[21] = byte(t[3] >> 15)
+	out[22] = byte(t[3] >> 23)
+	out[23] = byte(t[3] >> 31)
+	out[24] = byte(t[3] >> 39)
+	out[25] = byte(t[3] >> 47)
+
+	out[25] ^= byte(t[4]<<4) & 0xf0
+	out[26] = byte(t[4] >> 4)
+	out[27] = byte(t[4] >> 12)
+	out[28] = byte(t[4] >> 20)
+	out[29] = byte(t[4] >> 28)
+	out[30] = byte(t[4] >> 36)
+	out[31] = byte(t[4] >> 44)
+}
+
+// invert calculates r = x^-1 mod p using Fermat's little theorem.
+func invert(r *[5]uint64, x *[5]uint64) {
+	var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64
+
+	square(&z2, x)        /* 2 */
+	square(&t, &z2)       /* 4 */
+	square(&t, &t)        /* 8 */
+	mul(&z9, &t, x)       /* 9 */
+	mul(&z11, &z9, &z2)   /* 11 */
+	square(&t, &z11)      /* 22 */
+	mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */
+
+	square(&t, &z2_5_0)      /* 2^6 - 2^1 */
+	for i := 1; i < 5; i++ { /* 2^20 - 2^10 */
+		square(&t, &t)
+	}
+	mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */
+
+	square(&t, &z2_10_0)      /* 2^11 - 2^1 */
+	for i := 1; i < 10; i++ { /* 2^20 - 2^10 */
+		square(&t, &t)
+	}
+	mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */
+
+	square(&t, &z2_20_0)      /* 2^21 - 2^1 */
+	for i := 1; i < 20; i++ { /* 2^40 - 2^20 */
+		square(&t, &t)
+	}
+	mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */
+
+	square(&t, &t)            /* 2^41 - 2^1 */
+	for i := 1; i < 10; i++ { /* 2^50 - 2^10 */
+		square(&t, &t)
+	}
+	mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */
+
+	square(&t, &z2_50_0)      /* 2^51 - 2^1 */
+	for i := 1; i < 50; i++ { /* 2^100 - 2^50 */
+		square(&t, &t)
+	}
+	mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */
+
+	square(&t, &z2_100_0)      /* 2^101 - 2^1 */
+	for i := 1; i < 100; i++ { /* 2^200 - 2^100 */
+		square(&t, &t)
+	}
+	mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */
+
+	square(&t, &t)            /* 2^201 - 2^1 */
+	for i := 1; i < 50; i++ { /* 2^250 - 2^50 */
+		square(&t, &t)
+	}
+	mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */
+
+	square(&t, &t) /* 2^251 - 2^1 */
+	square(&t, &t) /* 2^252 - 2^2 */
+	square(&t, &t) /* 2^253 - 2^3 */
+
+	square(&t, &t) /* 2^254 - 2^4 */
+
+	square(&t, &t)   /* 2^255 - 2^5 */
+	mul(r, &t, &z11) /* 2^255 - 21 */
+}
diff --git a/src/vendor/golang_org/x/crypto/curve25519/mul_amd64.s b/src/vendor/golang_org/x/crypto/curve25519/mul_amd64.s
new file mode 100644
index 0000000..33ce57d
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/curve25519/mul_amd64.s
@@ -0,0 +1,167 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func mul(dest, a, b *[5]uint64)
+TEXT ·mul(SB),0,$16-24
+	MOVQ dest+0(FP), DI
+	MOVQ a+8(FP), SI
+	MOVQ b+16(FP), DX
+
+	MOVQ DX,CX
+	MOVQ 24(SI),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,0(SP)
+	MULQ 16(CX)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 32(SI),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,8(SP)
+	MULQ 8(CX)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 0(SI),AX
+	MULQ 0(CX)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 0(SI),AX
+	MULQ 8(CX)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 0(SI),AX
+	MULQ 16(CX)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 0(SI),AX
+	MULQ 24(CX)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 0(SI),AX
+	MULQ 32(CX)
+	MOVQ AX,BX
+	MOVQ DX,BP
+	MOVQ 8(SI),AX
+	MULQ 0(CX)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 8(SI),AX
+	MULQ 8(CX)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 8(SI),AX
+	MULQ 16(CX)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 8(SI),AX
+	MULQ 24(CX)
+	ADDQ AX,BX
+	ADCQ DX,BP
+	MOVQ 8(SI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(CX)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 16(SI),AX
+	MULQ 0(CX)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 16(SI),AX
+	MULQ 8(CX)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 16(SI),AX
+	MULQ 16(CX)
+	ADDQ AX,BX
+	ADCQ DX,BP
+	MOVQ 16(SI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 24(CX)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 16(SI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(CX)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 24(SI),AX
+	MULQ 0(CX)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 24(SI),AX
+	MULQ 8(CX)
+	ADDQ AX,BX
+	ADCQ DX,BP
+	MOVQ 0(SP),AX
+	MULQ 24(CX)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 0(SP),AX
+	MULQ 32(CX)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 32(SI),AX
+	MULQ 0(CX)
+	ADDQ AX,BX
+	ADCQ DX,BP
+	MOVQ 8(SP),AX
+	MULQ 16(CX)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 8(SP),AX
+	MULQ 24(CX)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 8(SP),AX
+	MULQ 32(CX)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ ·REDMASK51(SB),SI
+	SHLQ $13,R9:R8
+	ANDQ SI,R8
+	SHLQ $13,R11:R10
+	ANDQ SI,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ SI,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ SI,R14
+	ADDQ R13,R14
+	SHLQ $13,BP:BX
+	ANDQ SI,BX
+	ADDQ R15,BX
+	IMUL3Q $19,BP,DX
+	ADDQ DX,R8
+	MOVQ R8,DX
+	SHRQ $51,DX
+	ADDQ R10,DX
+	MOVQ DX,CX
+	SHRQ $51,DX
+	ANDQ SI,R8
+	ADDQ R12,DX
+	MOVQ DX,R9
+	SHRQ $51,DX
+	ANDQ SI,CX
+	ADDQ R14,DX
+	MOVQ DX,AX
+	SHRQ $51,DX
+	ANDQ SI,R9
+	ADDQ BX,DX
+	MOVQ DX,R10
+	SHRQ $51,DX
+	ANDQ SI,AX
+	IMUL3Q $19,DX,DX
+	ADDQ DX,R8
+	ANDQ SI,R10
+	MOVQ R8,0(DI)
+	MOVQ CX,8(DI)
+	MOVQ R9,16(DI)
+	MOVQ AX,24(DI)
+	MOVQ R10,32(DI)
+	RET
diff --git a/src/vendor/golang_org/x/crypto/curve25519/square_amd64.s b/src/vendor/golang_org/x/crypto/curve25519/square_amd64.s
new file mode 100644
index 0000000..3a92804
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/curve25519/square_amd64.s
@@ -0,0 +1,130 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func square(out, in *[5]uint64)
+TEXT ·square(SB),7,$0-16
+	MOVQ out+0(FP), DI
+	MOVQ in+8(FP), SI
+
+	MOVQ 0(SI),AX
+	MULQ 0(SI)
+	MOVQ AX,CX
+	MOVQ DX,R8
+	MOVQ 0(SI),AX
+	SHLQ $1,AX
+	MULQ 8(SI)
+	MOVQ AX,R9
+	MOVQ DX,R10
+	MOVQ 0(SI),AX
+	SHLQ $1,AX
+	MULQ 16(SI)
+	MOVQ AX,R11
+	MOVQ DX,R12
+	MOVQ 0(SI),AX
+	SHLQ $1,AX
+	MULQ 24(SI)
+	MOVQ AX,R13
+	MOVQ DX,R14
+	MOVQ 0(SI),AX
+	SHLQ $1,AX
+	MULQ 32(SI)
+	MOVQ AX,R15
+	MOVQ DX,BX
+	MOVQ 8(SI),AX
+	MULQ 8(SI)
+	ADDQ AX,R11
+	ADCQ DX,R12
+	MOVQ 8(SI),AX
+	SHLQ $1,AX
+	MULQ 16(SI)
+	ADDQ AX,R13
+	ADCQ DX,R14
+	MOVQ 8(SI),AX
+	SHLQ $1,AX
+	MULQ 24(SI)
+	ADDQ AX,R15
+	ADCQ DX,BX
+	MOVQ 8(SI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 32(SI)
+	ADDQ AX,CX
+	ADCQ DX,R8
+	MOVQ 16(SI),AX
+	MULQ 16(SI)
+	ADDQ AX,R15
+	ADCQ DX,BX
+	MOVQ 16(SI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 24(SI)
+	ADDQ AX,CX
+	ADCQ DX,R8
+	MOVQ 16(SI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 32(SI)
+	ADDQ AX,R9
+	ADCQ DX,R10
+	MOVQ 24(SI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 24(SI)
+	ADDQ AX,R9
+	ADCQ DX,R10
+	MOVQ 24(SI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 32(SI)
+	ADDQ AX,R11
+	ADCQ DX,R12
+	MOVQ 32(SI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(SI)
+	ADDQ AX,R13
+	ADCQ DX,R14
+	MOVQ ·REDMASK51(SB),SI
+	SHLQ $13,R8:CX
+	ANDQ SI,CX
+	SHLQ $13,R10:R9
+	ANDQ SI,R9
+	ADDQ R8,R9
+	SHLQ $13,R12:R11
+	ANDQ SI,R11
+	ADDQ R10,R11
+	SHLQ $13,R14:R13
+	ANDQ SI,R13
+	ADDQ R12,R13
+	SHLQ $13,BX:R15
+	ANDQ SI,R15
+	ADDQ R14,R15
+	IMUL3Q $19,BX,DX
+	ADDQ DX,CX
+	MOVQ CX,DX
+	SHRQ $51,DX
+	ADDQ R9,DX
+	ANDQ SI,CX
+	MOVQ DX,R8
+	SHRQ $51,DX
+	ADDQ R11,DX
+	ANDQ SI,R8
+	MOVQ DX,R9
+	SHRQ $51,DX
+	ADDQ R13,DX
+	ANDQ SI,R9
+	MOVQ DX,AX
+	SHRQ $51,DX
+	ADDQ R15,DX
+	ANDQ SI,AX
+	MOVQ DX,R10
+	SHRQ $51,DX
+	IMUL3Q $19,DX,DX
+	ADDQ DX,CX
+	ANDQ SI,R10
+	MOVQ CX,0(DI)
+	MOVQ R8,8(DI)
+	MOVQ R9,16(DI)
+	MOVQ AX,24(DI)
+	MOVQ R10,32(DI)
+	RET
diff --git a/src/vendor/golang_org/x/crypto/poly1305/poly1305.go b/src/vendor/golang_org/x/crypto/poly1305/poly1305.go
new file mode 100644
index 0000000..4a5f826
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/poly1305/poly1305.go
@@ -0,0 +1,32 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package poly1305 implements Poly1305 one-time message authentication code as specified in http://cr.yp.to/mac/poly1305-20050329.pdf.
+
+Poly1305 is a fast, one-time authentication function. It is infeasible for an
+attacker to generate an authenticator for a message without the key. However, a
+key must only be used for a single message. Authenticating two different
+messages with the same key allows an attacker to forge authenticators for other
+messages with the same key.
+
+Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was
+used with a fixed key in order to generate one-time keys from an nonce.
+However, in this package AES isn't used and the one-time key is specified
+directly.
+*/
+package poly1305 // import "golang.org/x/crypto/poly1305"
+
+import "crypto/subtle"
+
+// TagSize is the size, in bytes, of a poly1305 authenticator.
+const TagSize = 16
+
+// Verify returns true if mac is a valid authenticator for m with the given
+// key.
+func Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
+	var tmp [16]byte
+	Sum(&tmp, m, key)
+	return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
+}
diff --git a/src/vendor/golang_org/x/crypto/poly1305/poly1305_test.go b/src/vendor/golang_org/x/crypto/poly1305/poly1305_test.go
new file mode 100644
index 0000000..91b8e2b
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/poly1305/poly1305_test.go
@@ -0,0 +1,92 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package poly1305
+
+import (
+	"bytes"
+	"testing"
+	"unsafe"
+)
+
+var testData = []struct {
+	in, k, correct []byte
+}{
+	{
+		[]byte("Hello world!"),
+		[]byte("this is 32-byte key for Poly1305"),
+		[]byte{0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0},
+	},
+	{
+		make([]byte, 32),
+		[]byte("this is 32-byte key for Poly1305"),
+		[]byte{0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07},
+	},
+	{
+		make([]byte, 2007),
+		[]byte("this is 32-byte key for Poly1305"),
+		[]byte{0xda, 0x84, 0xbc, 0xab, 0x02, 0x67, 0x6c, 0x38, 0xcd, 0xb0, 0x15, 0x60, 0x42, 0x74, 0xc2, 0xaa},
+	},
+	{
+		make([]byte, 2007),
+		make([]byte, 32),
+		make([]byte, 16),
+	},
+	{
+		// This test triggers an edge-case. See https://go-review.googlesource.com/#/c/30101/.
+		[]byte{0x81, 0xd8, 0xb2, 0xe4, 0x6a, 0x25, 0x21, 0x3b, 0x58, 0xfe, 0xe4, 0x21, 0x3a, 0x2a, 0x28, 0xe9, 0x21, 0xc1, 0x2a, 0x96, 0x32, 0x51, 0x6d, 0x3b, 0x73, 0x27, 0x27, 0x27, 0xbe, 0xcf, 0x21, 0x29},
+		[]byte{0x3b, 0x3a, 0x29, 0xe9, 0x3b, 0x21, 0x3a, 0x5c, 0x5c, 0x3b, 0x3b, 0x05, 0x3a, 0x3a, 0x8c, 0x0d},
+		[]byte{0x6d, 0xc1, 0x8b, 0x8c, 0x34, 0x4c, 0xd7, 0x99, 0x27, 0x11, 0x8b, 0xbe, 0x84, 0xb7, 0xf3, 0x14},
+	},
+}
+
+func testSum(t *testing.T, unaligned bool) {
+	var out [16]byte
+	var key [32]byte
+
+	for i, v := range testData {
+		in := v.in
+		if unaligned {
+			in = unalignBytes(in)
+		}
+		copy(key[:], v.k)
+		Sum(&out, in, &key)
+		if !bytes.Equal(out[:], v.correct) {
+			t.Errorf("%d: expected %x, got %x", i, v.correct, out[:])
+		}
+	}
+}
+
+func TestSum(t *testing.T)          { testSum(t, false) }
+func TestSumUnaligned(t *testing.T) { testSum(t, true) }
+
+func benchmark(b *testing.B, size int, unaligned bool) {
+	var out [16]byte
+	var key [32]byte
+	in := make([]byte, size)
+	if unaligned {
+		in = unalignBytes(in)
+	}
+	b.SetBytes(int64(len(in)))
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		Sum(&out, in, &key)
+	}
+}
+
+func Benchmark64(b *testing.B)          { benchmark(b, 64, false) }
+func Benchmark1K(b *testing.B)          { benchmark(b, 1024, false) }
+func Benchmark64Unaligned(b *testing.B) { benchmark(b, 64, true) }
+func Benchmark1KUnaligned(b *testing.B) { benchmark(b, 1024, true) }
+
+func unalignBytes(in []byte) []byte {
+	out := make([]byte, len(in)+1)
+	if uintptr(unsafe.Pointer(&out[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
+		out = out[1:]
+	} else {
+		out = out[:len(in)]
+	}
+	copy(out, in)
+	return out
+}
diff --git a/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.go b/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.go
new file mode 100644
index 0000000..4dd72fe
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.go
@@ -0,0 +1,22 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!gccgo,!appengine
+
+package poly1305
+
+// This function is implemented in sum_amd64.s
+//go:noescape
+func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
+
+// Sum generates an authenticator for m using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[16]byte, m []byte, key *[32]byte) {
+	var mPtr *byte
+	if len(m) > 0 {
+		mPtr = &m[0]
+	}
+	poly1305(out, mPtr, uint64(len(m)), key)
+}
diff --git a/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s b/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s
new file mode 100644
index 0000000..bc75c61
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/poly1305/sum_amd64.s
@@ -0,0 +1,125 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!gccgo,!appengine
+
+#include "textflag.h"
+
+#define POLY1305_ADD(msg, h0, h1, h2) \
+	ADDQ 0(msg), h0;  \
+	ADCQ 8(msg), h1;  \
+	ADCQ $1, h2;      \
+	LEAQ 16(msg), msg
+
+#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \
+	MOVQ  r0, AX;                  \
+	MULQ  h0;                      \
+	MOVQ  AX, t0;                  \
+	MOVQ  DX, t1;                  \
+	MOVQ  r0, AX;                  \
+	MULQ  h1;                      \
+	ADDQ  AX, t1;                  \
+	ADCQ  $0, DX;                  \
+	MOVQ  r0, t2;                  \
+	IMULQ h2, t2;                  \
+	ADDQ  DX, t2;                  \
+	                               \
+	MOVQ  r1, AX;                  \
+	MULQ  h0;                      \
+	ADDQ  AX, t1;                  \
+	ADCQ  $0, DX;                  \
+	MOVQ  DX, h0;                  \
+	MOVQ  r1, t3;                  \
+	IMULQ h2, t3;                  \
+	MOVQ  r1, AX;                  \
+	MULQ  h1;                      \
+	ADDQ  AX, t2;                  \
+	ADCQ  DX, t3;                  \
+	ADDQ  h0, t2;                  \
+	ADCQ  $0, t3;                  \
+	                               \
+	MOVQ  t0, h0;                  \
+	MOVQ  t1, h1;                  \
+	MOVQ  t2, h2;                  \
+	ANDQ  $3, h2;                  \
+	MOVQ  t2, t0;                  \
+	ANDQ  $0xFFFFFFFFFFFFFFFC, t0; \
+	ADDQ  t0, h0;                  \
+	ADCQ  t3, h1;                  \
+	ADCQ  $0, h2;                  \
+	SHRQ  $2, t3, t2;              \
+	SHRQ  $2, t3;                  \
+	ADDQ  t2, h0;                  \
+	ADCQ  t3, h1;                  \
+	ADCQ  $0, h2
+
+DATA poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
+DATA poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
+GLOBL poly1305Mask<>(SB), RODATA, $16
+
+// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key)
+TEXT ·poly1305(SB), $0-32
+	MOVQ out+0(FP), DI
+	MOVQ m+8(FP), SI
+	MOVQ mlen+16(FP), R15
+	MOVQ key+24(FP), AX
+
+	MOVQ 0(AX), R11
+	MOVQ 8(AX), R12
+	ANDQ poly1305Mask<>(SB), R11   // r0
+	ANDQ poly1305Mask<>+8(SB), R12 // r1
+	XORQ R8, R8                    // h0
+	XORQ R9, R9                    // h1
+	XORQ R10, R10                  // h2
+
+	CMPQ R15, $16
+	JB   bytes_between_0_and_15
+
+loop:
+	POLY1305_ADD(SI, R8, R9, R10)
+
+multiply:
+	POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14)
+	SUBQ $16, R15
+	CMPQ R15, $16
+	JAE  loop
+
+bytes_between_0_and_15:
+	TESTQ R15, R15
+	JZ    done
+	MOVQ  $1, BX
+	XORQ  CX, CX
+	XORQ  R13, R13
+	ADDQ  R15, SI
+
+flush_buffer:
+	SHLQ $8, BX, CX
+	SHLQ $8, BX
+	MOVB -1(SI), R13
+	XORQ R13, BX
+	DECQ SI
+	DECQ R15
+	JNZ  flush_buffer
+
+	ADDQ BX, R8
+	ADCQ CX, R9
+	ADCQ $0, R10
+	MOVQ $16, R15
+	JMP  multiply
+
+done:
+	MOVQ    R8, AX
+	MOVQ    R9, BX
+	SUBQ    $0xFFFFFFFFFFFFFFFB, AX
+	SBBQ    $0xFFFFFFFFFFFFFFFF, BX
+	SBBQ    $3, R10
+	CMOVQCS R8, AX
+	CMOVQCS R9, BX
+	MOVQ    key+24(FP), R8
+	ADDQ    16(R8), AX
+	ADCQ    24(R8), BX
+
+	MOVQ AX, 0(DI)
+	MOVQ BX, 8(DI)
+	RET
diff --git a/src/vendor/golang_org/x/crypto/poly1305/sum_arm.go b/src/vendor/golang_org/x/crypto/poly1305/sum_arm.go
new file mode 100644
index 0000000..5dc321c
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/poly1305/sum_arm.go
@@ -0,0 +1,22 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build arm,!gccgo,!appengine,!nacl
+
+package poly1305
+
+// This function is implemented in sum_arm.s
+//go:noescape
+func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]byte)
+
+// Sum generates an authenticator for m using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[16]byte, m []byte, key *[32]byte) {
+	var mPtr *byte
+	if len(m) > 0 {
+		mPtr = &m[0]
+	}
+	poly1305_auth_armv6(out, mPtr, uint32(len(m)), key)
+}
diff --git a/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s b/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s
new file mode 100644
index 0000000..93167b2
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/poly1305/sum_arm.s
@@ -0,0 +1,427 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build arm,!gccgo,!appengine,!nacl
+
+#include "textflag.h"
+
+// This code was translated into a form compatible with 5a from the public
+// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305.
+
+DATA poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
+DATA poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
+DATA poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
+DATA poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
+DATA poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
+GLOBL poly1305_init_constants_armv6<>(SB), 8, $20
+
+// Warning: the linker may use R11 to synthesize certain instructions. Please
+// take care and verify that no synthetic instructions use it.
+
+TEXT poly1305_init_ext_armv6<>(SB), NOSPLIT, $0
+	// Needs 16 bytes of stack and 64 bytes of space pointed to by R0.  (It
+	// might look like it's only 60 bytes of space but the final four bytes
+	// will be written by another function.) We need to skip over four
+	// bytes of stack because that's saving the value of 'g'.
+	ADD       $4, R13, R8
+	MOVM.IB   [R4-R7], (R8)
+	MOVM.IA.W (R1), [R2-R5]
+	MOVW      $poly1305_init_constants_armv6<>(SB), R7
+	MOVW      R2, R8
+	MOVW      R2>>26, R9
+	MOVW      R3>>20, g
+	MOVW      R4>>14, R11
+	MOVW      R5>>8, R12
+	ORR       R3<<6, R9, R9
+	ORR       R4<<12, g, g
+	ORR       R5<<18, R11, R11
+	MOVM.IA   (R7), [R2-R6]
+	AND       R8, R2, R2
+	AND       R9, R3, R3
+	AND       g, R4, R4
+	AND       R11, R5, R5
+	AND       R12, R6, R6
+	MOVM.IA.W [R2-R6], (R0)
+	EOR       R2, R2, R2
+	EOR       R3, R3, R3
+	EOR       R4, R4, R4
+	EOR       R5, R5, R5
+	EOR       R6, R6, R6
+	MOVM.IA.W [R2-R6], (R0)
+	MOVM.IA.W (R1), [R2-R5]
+	MOVM.IA   [R2-R6], (R0)
+	ADD       $20, R13, R0
+	MOVM.DA   (R0), [R4-R7]
+	RET
+
+#define MOVW_UNALIGNED(Rsrc, Rdst, Rtmp, offset) \
+	MOVBU (offset+0)(Rsrc), Rtmp; \
+	MOVBU Rtmp, (offset+0)(Rdst); \
+	MOVBU (offset+1)(Rsrc), Rtmp; \
+	MOVBU Rtmp, (offset+1)(Rdst); \
+	MOVBU (offset+2)(Rsrc), Rtmp; \
+	MOVBU Rtmp, (offset+2)(Rdst); \
+	MOVBU (offset+3)(Rsrc), Rtmp; \
+	MOVBU Rtmp, (offset+3)(Rdst)
+
+TEXT poly1305_blocks_armv6<>(SB), NOSPLIT, $0
+	// Needs 24 bytes of stack for saved registers and then 88 bytes of
+	// scratch space after that. We assume that 24 bytes at (R13) have
+	// already been used: four bytes for the link register saved in the
+	// prelude of poly1305_auth_armv6, four bytes for saving the value of g
+	// in that function and 16 bytes of scratch space used around
+	// poly1305_finish_ext_armv6_skip1.
+	ADD     $24, R13, R12
+	MOVM.IB [R4-R8, R14], (R12)
+	MOVW    R0, 88(R13)
+	MOVW    R1, 92(R13)
+	MOVW    R2, 96(R13)
+	MOVW    R1, R14
+	MOVW    R2, R12
+	MOVW    56(R0), R8
+	WORD    $0xe1180008                // TST R8, R8 not working see issue 5921
+	EOR     R6, R6, R6
+	MOVW.EQ $(1<<24), R6
+	MOVW    R6, 84(R13)
+	ADD     $116, R13, g
+	MOVM.IA (R0), [R0-R9]
+	MOVM.IA [R0-R4], (g)
+	CMP     $16, R12
+	BLO     poly1305_blocks_armv6_done
+
+poly1305_blocks_armv6_mainloop:
+	WORD    $0xe31e0003                            // TST R14, #3 not working see issue 5921
+	BEQ     poly1305_blocks_armv6_mainloop_aligned
+	ADD     $100, R13, g
+	MOVW_UNALIGNED(R14, g, R0, 0)
+	MOVW_UNALIGNED(R14, g, R0, 4)
+	MOVW_UNALIGNED(R14, g, R0, 8)
+	MOVW_UNALIGNED(R14, g, R0, 12)
+	MOVM.IA (g), [R0-R3]
+	ADD     $16, R14
+	B       poly1305_blocks_armv6_mainloop_loaded
+
+poly1305_blocks_armv6_mainloop_aligned:
+	MOVM.IA.W (R14), [R0-R3]
+
+poly1305_blocks_armv6_mainloop_loaded:
+	MOVW    R0>>26, g
+	MOVW    R1>>20, R11
+	MOVW    R2>>14, R12
+	MOVW    R14, 92(R13)
+	MOVW    R3>>8, R4
+	ORR     R1<<6, g, g
+	ORR     R2<<12, R11, R11
+	ORR     R3<<18, R12, R12
+	BIC     $0xfc000000, R0, R0
+	BIC     $0xfc000000, g, g
+	MOVW    84(R13), R3
+	BIC     $0xfc000000, R11, R11
+	BIC     $0xfc000000, R12, R12
+	ADD     R0, R5, R5
+	ADD     g, R6, R6
+	ORR     R3, R4, R4
+	ADD     R11, R7, R7
+	ADD     $116, R13, R14
+	ADD     R12, R8, R8
+	ADD     R4, R9, R9
+	MOVM.IA (R14), [R0-R4]
+	MULLU   R4, R5, (R11, g)
+	MULLU   R3, R5, (R14, R12)
+	MULALU  R3, R6, (R11, g)
+	MULALU  R2, R6, (R14, R12)
+	MULALU  R2, R7, (R11, g)
+	MULALU  R1, R7, (R14, R12)
+	ADD     R4<<2, R4, R4
+	ADD     R3<<2, R3, R3
+	MULALU  R1, R8, (R11, g)
+	MULALU  R0, R8, (R14, R12)
+	MULALU  R0, R9, (R11, g)
+	MULALU  R4, R9, (R14, R12)
+	MOVW    g, 76(R13)
+	MOVW    R11, 80(R13)
+	MOVW    R12, 68(R13)
+	MOVW    R14, 72(R13)
+	MULLU   R2, R5, (R11, g)
+	MULLU   R1, R5, (R14, R12)
+	MULALU  R1, R6, (R11, g)
+	MULALU  R0, R6, (R14, R12)
+	MULALU  R0, R7, (R11, g)
+	MULALU  R4, R7, (R14, R12)
+	ADD     R2<<2, R2, R2
+	ADD     R1<<2, R1, R1
+	MULALU  R4, R8, (R11, g)
+	MULALU  R3, R8, (R14, R12)
+	MULALU  R3, R9, (R11, g)
+	MULALU  R2, R9, (R14, R12)
+	MOVW    g, 60(R13)
+	MOVW    R11, 64(R13)
+	MOVW    R12, 52(R13)
+	MOVW    R14, 56(R13)
+	MULLU   R0, R5, (R11, g)
+	MULALU  R4, R6, (R11, g)
+	MULALU  R3, R7, (R11, g)
+	MULALU  R2, R8, (R11, g)
+	MULALU  R1, R9, (R11, g)
+	ADD     $52, R13, R0
+	MOVM.IA (R0), [R0-R7]
+	MOVW    g>>26, R12
+	MOVW    R4>>26, R14
+	ORR     R11<<6, R12, R12
+	ORR     R5<<6, R14, R14
+	BIC     $0xfc000000, g, g
+	BIC     $0xfc000000, R4, R4
+	ADD.S   R12, R0, R0
+	ADC     $0, R1, R1
+	ADD.S   R14, R6, R6
+	ADC     $0, R7, R7
+	MOVW    R0>>26, R12
+	MOVW    R6>>26, R14
+	ORR     R1<<6, R12, R12
+	ORR     R7<<6, R14, R14
+	BIC     $0xfc000000, R0, R0
+	BIC     $0xfc000000, R6, R6
+	ADD     R14<<2, R14, R14
+	ADD.S   R12, R2, R2
+	ADC     $0, R3, R3
+	ADD     R14, g, g
+	MOVW    R2>>26, R12
+	MOVW    g>>26, R14
+	ORR     R3<<6, R12, R12
+	BIC     $0xfc000000, g, R5
+	BIC     $0xfc000000, R2, R7
+	ADD     R12, R4, R4
+	ADD     R14, R0, R0
+	MOVW    R4>>26, R12
+	BIC     $0xfc000000, R4, R8
+	ADD     R12, R6, R9
+	MOVW    96(R13), R12
+	MOVW    92(R13), R14
+	MOVW    R0, R6
+	CMP     $32, R12
+	SUB     $16, R12, R12
+	MOVW    R12, 96(R13)
+	BHS     poly1305_blocks_armv6_mainloop
+
+poly1305_blocks_armv6_done:
+	MOVW    88(R13), R12
+	MOVW    R5, 20(R12)
+	MOVW    R6, 24(R12)
+	MOVW    R7, 28(R12)
+	MOVW    R8, 32(R12)
+	MOVW    R9, 36(R12)
+	ADD     $48, R13, R0
+	MOVM.DA (R0), [R4-R8, R14]
+	RET
+
+#define MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) \
+	MOVBU.P 1(Rsrc), Rtmp; \
+	MOVBU.P Rtmp, 1(Rdst); \
+	MOVBU.P 1(Rsrc), Rtmp; \
+	MOVBU.P Rtmp, 1(Rdst)
+
+#define MOVWP_UNALIGNED(Rsrc, Rdst, Rtmp) \
+	MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp); \
+	MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp)
+
+// func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]key)
+TEXT ·poly1305_auth_armv6(SB), $196-16
+	// The value 196, just above, is the sum of 64 (the size of the context
+	// structure) and 132 (the amount of stack needed).
+	//
+	// At this point, the stack pointer (R13) has been moved down. It
+	// points to the saved link register and there's 196 bytes of free
+	// space above it.
+	//
+	// The stack for this function looks like:
+	//
+	// +---------------------
+	// |
+	// | 64 bytes of context structure
+	// |
+	// +---------------------
+	// |
+	// | 112 bytes for poly1305_blocks_armv6
+	// |
+	// +---------------------
+	// | 16 bytes of final block, constructed at
+	// | poly1305_finish_ext_armv6_skip8
+	// +---------------------
+	// | four bytes of saved 'g'
+	// +---------------------
+	// | lr, saved by prelude    <- R13 points here
+	// +---------------------
+	MOVW g, 4(R13)
+
+	MOVW out+0(FP), R4
+	MOVW m+4(FP), R5
+	MOVW mlen+8(FP), R6
+	MOVW key+12(FP), R7
+
+	ADD  $136, R13, R0 // 136 = 4 + 4 + 16 + 112
+	MOVW R7, R1
+
+	// poly1305_init_ext_armv6 will write to the stack from R13+4, but
+	// that's ok because none of the other values have been written yet.
+	BL    poly1305_init_ext_armv6<>(SB)
+	BIC.S $15, R6, R2
+	BEQ   poly1305_auth_armv6_noblocks
+	ADD   $136, R13, R0
+	MOVW  R5, R1
+	ADD   R2, R5, R5
+	SUB   R2, R6, R6
+	BL    poly1305_blocks_armv6<>(SB)
+
+poly1305_auth_armv6_noblocks:
+	ADD  $136, R13, R0
+	MOVW R5, R1
+	MOVW R6, R2
+	MOVW R4, R3
+
+	MOVW  R0, R5
+	MOVW  R1, R6
+	MOVW  R2, R7
+	MOVW  R3, R8
+	AND.S R2, R2, R2
+	BEQ   poly1305_finish_ext_armv6_noremaining
+	EOR   R0, R0
+	ADD   $8, R13, R9                           // 8 = offset to 16 byte scratch space
+	MOVW  R0, (R9)
+	MOVW  R0, 4(R9)
+	MOVW  R0, 8(R9)
+	MOVW  R0, 12(R9)
+	WORD  $0xe3110003                           // TST R1, #3 not working see issue 5921
+	BEQ   poly1305_finish_ext_armv6_aligned
+	WORD  $0xe3120008                           // TST R2, #8 not working see issue 5921
+	BEQ   poly1305_finish_ext_armv6_skip8
+	MOVWP_UNALIGNED(R1, R9, g)
+	MOVWP_UNALIGNED(R1, R9, g)
+
+poly1305_finish_ext_armv6_skip8:
+	WORD $0xe3120004                     // TST $4, R2 not working see issue 5921
+	BEQ  poly1305_finish_ext_armv6_skip4
+	MOVWP_UNALIGNED(R1, R9, g)
+
+poly1305_finish_ext_armv6_skip4:
+	WORD $0xe3120002                     // TST $2, R2 not working see issue 5921
+	BEQ  poly1305_finish_ext_armv6_skip2
+	MOVHUP_UNALIGNED(R1, R9, g)
+	B    poly1305_finish_ext_armv6_skip2
+
+poly1305_finish_ext_armv6_aligned:
+	WORD      $0xe3120008                             // TST R2, #8 not working see issue 5921
+	BEQ       poly1305_finish_ext_armv6_skip8_aligned
+	MOVM.IA.W (R1), [g-R11]
+	MOVM.IA.W [g-R11], (R9)
+
+poly1305_finish_ext_armv6_skip8_aligned:
+	WORD   $0xe3120004                             // TST $4, R2 not working see issue 5921
+	BEQ    poly1305_finish_ext_armv6_skip4_aligned
+	MOVW.P 4(R1), g
+	MOVW.P g, 4(R9)
+
+poly1305_finish_ext_armv6_skip4_aligned:
+	WORD    $0xe3120002                     // TST $2, R2 not working see issue 5921
+	BEQ     poly1305_finish_ext_armv6_skip2
+	MOVHU.P 2(R1), g
+	MOVH.P  g, 2(R9)
+
+poly1305_finish_ext_armv6_skip2:
+	WORD    $0xe3120001                     // TST $1, R2 not working see issue 5921
+	BEQ     poly1305_finish_ext_armv6_skip1
+	MOVBU.P 1(R1), g
+	MOVBU.P g, 1(R9)
+
+poly1305_finish_ext_armv6_skip1:
+	MOVW  $1, R11
+	MOVBU R11, 0(R9)
+	MOVW  R11, 56(R5)
+	MOVW  R5, R0
+	ADD   $8, R13, R1
+	MOVW  $16, R2
+	BL    poly1305_blocks_armv6<>(SB)
+
+poly1305_finish_ext_armv6_noremaining:
+	MOVW      20(R5), R0
+	MOVW      24(R5), R1
+	MOVW      28(R5), R2
+	MOVW      32(R5), R3
+	MOVW      36(R5), R4
+	MOVW      R4>>26, R12
+	BIC       $0xfc000000, R4, R4
+	ADD       R12<<2, R12, R12
+	ADD       R12, R0, R0
+	MOVW      R0>>26, R12
+	BIC       $0xfc000000, R0, R0
+	ADD       R12, R1, R1
+	MOVW      R1>>26, R12
+	BIC       $0xfc000000, R1, R1
+	ADD       R12, R2, R2
+	MOVW      R2>>26, R12
+	BIC       $0xfc000000, R2, R2
+	ADD       R12, R3, R3
+	MOVW      R3>>26, R12
+	BIC       $0xfc000000, R3, R3
+	ADD       R12, R4, R4
+	ADD       $5, R0, R6
+	MOVW      R6>>26, R12
+	BIC       $0xfc000000, R6, R6
+	ADD       R12, R1, R7
+	MOVW      R7>>26, R12
+	BIC       $0xfc000000, R7, R7
+	ADD       R12, R2, g
+	MOVW      g>>26, R12
+	BIC       $0xfc000000, g, g
+	ADD       R12, R3, R11
+	MOVW      $-(1<<26), R12
+	ADD       R11>>26, R12, R12
+	BIC       $0xfc000000, R11, R11
+	ADD       R12, R4, R9
+	MOVW      R9>>31, R12
+	SUB       $1, R12
+	AND       R12, R6, R6
+	AND       R12, R7, R7
+	AND       R12, g, g
+	AND       R12, R11, R11
+	AND       R12, R9, R9
+	MVN       R12, R12
+	AND       R12, R0, R0
+	AND       R12, R1, R1
+	AND       R12, R2, R2
+	AND       R12, R3, R3
+	AND       R12, R4, R4
+	ORR       R6, R0, R0
+	ORR       R7, R1, R1
+	ORR       g, R2, R2
+	ORR       R11, R3, R3
+	ORR       R9, R4, R4
+	ORR       R1<<26, R0, R0
+	MOVW      R1>>6, R1
+	ORR       R2<<20, R1, R1
+	MOVW      R2>>12, R2
+	ORR       R3<<14, R2, R2
+	MOVW      R3>>18, R3
+	ORR       R4<<8, R3, R3
+	MOVW      40(R5), R6
+	MOVW      44(R5), R7
+	MOVW      48(R5), g
+	MOVW      52(R5), R11
+	ADD.S     R6, R0, R0
+	ADC.S     R7, R1, R1
+	ADC.S     g, R2, R2
+	ADC.S     R11, R3, R3
+	MOVM.IA   [R0-R3], (R8)
+	MOVW      R5, R12
+	EOR       R0, R0, R0
+	EOR       R1, R1, R1
+	EOR       R2, R2, R2
+	EOR       R3, R3, R3
+	EOR       R4, R4, R4
+	EOR       R5, R5, R5
+	EOR       R6, R6, R6
+	EOR       R7, R7, R7
+	MOVM.IA.W [R0-R7], (R12)
+	MOVM.IA   [R0-R7], (R12)
+	MOVW      4(R13), g
+	RET
diff --git a/src/vendor/golang_org/x/crypto/poly1305/sum_ref.go b/src/vendor/golang_org/x/crypto/poly1305/sum_ref.go
new file mode 100644
index 0000000..dbe50e7
--- /dev/null
+++ b/src/vendor/golang_org/x/crypto/poly1305/sum_ref.go
@@ -0,0 +1,1531 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !amd64,!arm gccgo appengine nacl
+
+package poly1305
+
+// Based on original, public domain implementation from NaCl by D. J.
+// Bernstein.
+
+import "math"
+
+const (
+	alpham80 = 0.00000000558793544769287109375
+	alpham48 = 24.0
+	alpham16 = 103079215104.0
+	alpha0   = 6755399441055744.0
+	alpha18  = 1770887431076116955136.0
+	alpha32  = 29014219670751100192948224.0
+	alpha50  = 7605903601369376408980219232256.0
+	alpha64  = 124615124604835863084731911901282304.0
+	alpha82  = 32667107224410092492483962313449748299776.0
+	alpha96  = 535217884764734955396857238543560676143529984.0
+	alpha112 = 35076039295941670036888435985190792471742381031424.0
+	alpha130 = 9194973245195333150150082162901855101712434733101613056.0
+	scale    = 0.0000000000000000000000000000000000000036734198463196484624023016788195177431833298649127735047148490821200539357960224151611328125
+	offset0  = 6755408030990331.0
+	offset1  = 29014256564239239022116864.0
+	offset2  = 124615283061160854719918951570079744.0
+	offset3  = 535219245894202480694386063513315216128475136.0
+)
+
+// Sum generates an authenticator for m using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[16]byte, m []byte, key *[32]byte) {
+	r := key
+	s := key[16:]
+	var (
+		y7        float64
+		y6        float64
+		y1        float64
+		y0        float64
+		y5        float64
+		y4        float64
+		x7        float64
+		x6        float64
+		x1        float64
+		x0        float64
+		y3        float64
+		y2        float64
+		x5        float64
+		r3lowx0   float64
+		x4        float64
+		r0lowx6   float64
+		x3        float64
+		r3highx0  float64
+		x2        float64
+		r0highx6  float64
+		r0lowx0   float64
+		sr1lowx6  float64
+		r0highx0  float64
+		sr1highx6 float64
+		sr3low    float64
+		r1lowx0   float64
+		sr2lowx6  float64
+		r1highx0  float64
+		sr2highx6 float64
+		r2lowx0   float64
+		sr3lowx6  float64
+		r2highx0  float64
+		sr3highx6 float64
+		r1highx4  float64
+		r1lowx4   float64
+		r0highx4  float64
+		r0lowx4   float64
+		sr3highx4 float64
+		sr3lowx4  float64
+		sr2highx4 float64
+		sr2lowx4  float64
+		r0lowx2   float64
+		r0highx2  float64
+		r1lowx2   float64
+		r1highx2  float64
+		r2lowx2   float64
+		r2highx2  float64
+		sr3lowx2  float64
+		sr3highx2 float64
+		z0        float64
+		z1        float64
+		z2        float64
+		z3        float64
+		m0        int64
+		m1        int64
+		m2        int64
+		m3        int64
+		m00       uint32
+		m01       uint32
+		m02       uint32
+		m03       uint32
+		m10       uint32
+		m11       uint32
+		m12       uint32
+		m13       uint32
+		m20       uint32
+		m21       uint32
+		m22       uint32
+		m23       uint32
+		m30       uint32
+		m31       uint32
+		m32       uint32
+		m33       uint64
+		lbelow2   int32
+		lbelow3   int32
+		lbelow4   int32
+		lbelow5   int32
+		lbelow6   int32
+		lbelow7   int32
+		lbelow8   int32
+		lbelow9   int32
+		lbelow10  int32
+		lbelow11  int32
+		lbelow12  int32
+		lbelow13  int32
+		lbelow14  int32
+		lbelow15  int32
+		s00       uint32
+		s01       uint32
+		s02       uint32
+		s03       uint32
+		s10       uint32
+		s11       uint32
+		s12       uint32
+		s13       uint32
+		s20       uint32
+		s21       uint32
+		s22       uint32
+		s23       uint32
+		s30       uint32
+		s31       uint32
+		s32       uint32
+		s33       uint32
+		bits32    uint64
+		f         uint64
+		f0        uint64
+		f1        uint64
+		f2        uint64
+		f3        uint64
+		f4        uint64
+		g         uint64
+		g0        uint64
+		g1        uint64
+		g2        uint64
+		g3        uint64
+		g4        uint64
+	)
+
+	var p int32
+
+	l := int32(len(m))
+
+	r00 := uint32(r[0])
+
+	r01 := uint32(r[1])
+
+	r02 := uint32(r[2])
+	r0 := int64(2151)
+
+	r03 := uint32(r[3])
+	r03 &= 15
+	r0 <<= 51
+
+	r10 := uint32(r[4])
+	r10 &= 252
+	r01 <<= 8
+	r0 += int64(r00)
+
+	r11 := uint32(r[5])
+	r02 <<= 16
+	r0 += int64(r01)
+
+	r12 := uint32(r[6])
+	r03 <<= 24
+	r0 += int64(r02)
+
+	r13 := uint32(r[7])
+	r13 &= 15
+	r1 := int64(2215)
+	r0 += int64(r03)
+
+	d0 := r0
+	r1 <<= 51
+	r2 := int64(2279)
+
+	r20 := uint32(r[8])
+	r20 &= 252
+	r11 <<= 8
+	r1 += int64(r10)
+
+	r21 := uint32(r[9])
+	r12 <<= 16
+	r1 += int64(r11)
+
+	r22 := uint32(r[10])
+	r13 <<= 24
+	r1 += int64(r12)
+
+	r23 := uint32(r[11])
+	r23 &= 15
+	r2 <<= 51
+	r1 += int64(r13)
+
+	d1 := r1
+	r21 <<= 8
+	r2 += int64(r20)
+
+	r30 := uint32(r[12])
+	r30 &= 252
+	r22 <<= 16
+	r2 += int64(r21)
+
+	r31 := uint32(r[13])
+	r23 <<= 24
+	r2 += int64(r22)
+
+	r32 := uint32(r[14])
+	r2 += int64(r23)
+	r3 := int64(2343)
+
+	d2 := r2
+	r3 <<= 51
+
+	r33 := uint32(r[15])
+	r33 &= 15
+	r31 <<= 8
+	r3 += int64(r30)
+
+	r32 <<= 16
+	r3 += int64(r31)
+
+	r33 <<= 24
+	r3 += int64(r32)
+
+	r3 += int64(r33)
+	h0 := alpha32 - alpha32
+
+	d3 := r3
+	h1 := alpha32 - alpha32
+
+	h2 := alpha32 - alpha32
+
+	h3 := alpha32 - alpha32
+
+	h4 := alpha32 - alpha32
+
+	r0low := math.Float64frombits(uint64(d0))
+	h5 := alpha32 - alpha32
+
+	r1low := math.Float64frombits(uint64(d1))
+	h6 := alpha32 - alpha32
+
+	r2low := math.Float64frombits(uint64(d2))
+	h7 := alpha32 - alpha32
+
+	r0low -= alpha0
+
+	r1low -= alpha32
+
+	r2low -= alpha64
+
+	r0high := r0low + alpha18
+
+	r3low := math.Float64frombits(uint64(d3))
+
+	r1high := r1low + alpha50
+	sr1low := scale * r1low
+
+	r2high := r2low + alpha82
+	sr2low := scale * r2low
+
+	r0high -= alpha18
+	r0high_stack := r0high
+
+	r3low -= alpha96
+
+	r1high -= alpha50
+	r1high_stack := r1high
+
+	sr1high := sr1low + alpham80
+
+	r0low -= r0high
+
+	r2high -= alpha82
+	sr3low = scale * r3low
+
+	sr2high := sr2low + alpham48
+
+	r1low -= r1high
+	r1low_stack := r1low
+
+	sr1high -= alpham80
+	sr1high_stack := sr1high
+
+	r2low -= r2high
+	r2low_stack := r2low
+
+	sr2high -= alpham48
+	sr2high_stack := sr2high
+
+	r3high := r3low + alpha112
+	r0low_stack := r0low
+
+	sr1low -= sr1high
+	sr1low_stack := sr1low
+
+	sr3high := sr3low + alpham16
+	r2high_stack := r2high
+
+	sr2low -= sr2high
+	sr2low_stack := sr2low
+
+	r3high -= alpha112
+	r3high_stack := r3high
+
+	sr3high -= alpham16
+	sr3high_stack := sr3high
+
+	r3low -= r3high
+	r3low_stack := r3low
+
+	sr3low -= sr3high
+	sr3low_stack := sr3low
+
+	if l < 16 {
+		goto addatmost15bytes
+	}
+
+	m00 = uint32(m[p+0])
+	m0 = 2151
+
+	m0 <<= 51
+	m1 = 2215
+	m01 = uint32(m[p+1])
+
+	m1 <<= 51
+	m2 = 2279
+	m02 = uint32(m[p+2])
+
+	m2 <<= 51
+	m3 = 2343
+	m03 = uint32(m[p+3])
+
+	m10 = uint32(m[p+4])
+	m01 <<= 8
+	m0 += int64(m00)
+
+	m11 = uint32(m[p+5])
+	m02 <<= 16
+	m0 += int64(m01)
+
+	m12 = uint32(m[p+6])
+	m03 <<= 24
+	m0 += int64(m02)
+
+	m13 = uint32(m[p+7])
+	m3 <<= 51
+	m0 += int64(m03)
+
+	m20 = uint32(m[p+8])
+	m11 <<= 8
+	m1 += int64(m10)
+
+	m21 = uint32(m[p+9])
+	m12 <<= 16
+	m1 += int64(m11)
+
+	m22 = uint32(m[p+10])
+	m13 <<= 24
+	m1 += int64(m12)
+
+	m23 = uint32(m[p+11])
+	m1 += int64(m13)
+
+	m30 = uint32(m[p+12])
+	m21 <<= 8
+	m2 += int64(m20)
+
+	m31 = uint32(m[p+13])
+	m22 <<= 16
+	m2 += int64(m21)
+
+	m32 = uint32(m[p+14])
+	m23 <<= 24
+	m2 += int64(m22)
+
+	m33 = uint64(m[p+15])
+	m2 += int64(m23)
+
+	d0 = m0
+	m31 <<= 8
+	m3 += int64(m30)
+
+	d1 = m1
+	m32 <<= 16
+	m3 += int64(m31)
+
+	d2 = m2
+	m33 += 256
+
+	m33 <<= 24
+	m3 += int64(m32)
+
+	m3 += int64(m33)
+	d3 = m3
+
+	p += 16
+	l -= 16
+
+	z0 = math.Float64frombits(uint64(d0))
+
+	z1 = math.Float64frombits(uint64(d1))
+
+	z2 = math.Float64frombits(uint64(d2))
+
+	z3 = math.Float64frombits(uint64(d3))
+
+	z0 -= alpha0
+
+	z1 -= alpha32
+
+	z2 -= alpha64
+
+	z3 -= alpha96
+
+	h0 += z0
+
+	h1 += z1
+
+	h3 += z2
+
+	h5 += z3
+
+	if l < 16 {
+		goto multiplyaddatmost15bytes
+	}
+
+multiplyaddatleast16bytes:
+
+	m2 = 2279
+	m20 = uint32(m[p+8])
+	y7 = h7 + alpha130
+
+	m2 <<= 51
+	m3 = 2343
+	m21 = uint32(m[p+9])
+	y6 = h6 + alpha130
+
+	m3 <<= 51
+	m0 = 2151
+	m22 = uint32(m[p+10])
+	y1 = h1 + alpha32
+
+	m0 <<= 51
+	m1 = 2215
+	m23 = uint32(m[p+11])
+	y0 = h0 + alpha32
+
+	m1 <<= 51
+	m30 = uint32(m[p+12])
+	y7 -= alpha130
+
+	m21 <<= 8
+	m2 += int64(m20)
+	m31 = uint32(m[p+13])
+	y6 -= alpha130
+
+	m22 <<= 16
+	m2 += int64(m21)
+	m32 = uint32(m[p+14])
+	y1 -= alpha32
+
+	m23 <<= 24
+	m2 += int64(m22)
+	m33 = uint64(m[p+15])
+	y0 -= alpha32
+
+	m2 += int64(m23)
+	m00 = uint32(m[p+0])
+	y5 = h5 + alpha96
+
+	m31 <<= 8
+	m3 += int64(m30)
+	m01 = uint32(m[p+1])
+	y4 = h4 + alpha96
+
+	m32 <<= 16
+	m02 = uint32(m[p+2])
+	x7 = h7 - y7
+	y7 *= scale
+
+	m33 += 256
+	m03 = uint32(m[p+3])
+	x6 = h6 - y6
+	y6 *= scale
+
+	m33 <<= 24
+	m3 += int64(m31)
+	m10 = uint32(m[p+4])
+	x1 = h1 - y1
+
+	m01 <<= 8
+	m3 += int64(m32)
+	m11 = uint32(m[p+5])
+	x0 = h0 - y0
+
+	m3 += int64(m33)
+	m0 += int64(m00)
+	m12 = uint32(m[p+6])
+	y5 -= alpha96
+
+	m02 <<= 16
+	m0 += int64(m01)
+	m13 = uint32(m[p+7])
+	y4 -= alpha96
+
+	m03 <<= 24
+	m0 += int64(m02)
+	d2 = m2
+	x1 += y7
+
+	m0 += int64(m03)
+	d3 = m3
+	x0 += y6
+
+	m11 <<= 8
+	m1 += int64(m10)
+	d0 = m0
+	x7 += y5
+
+	m12 <<= 16
+	m1 += int64(m11)
+	x6 += y4
+
+	m13 <<= 24
+	m1 += int64(m12)
+	y3 = h3 + alpha64
+
+	m1 += int64(m13)
+	d1 = m1
+	y2 = h2 + alpha64
+
+	x0 += x1
+
+	x6 += x7
+
+	y3 -= alpha64
+	r3low = r3low_stack
+
+	y2 -= alpha64
+	r0low = r0low_stack
+
+	x5 = h5 - y5
+	r3lowx0 = r3low * x0
+	r3high = r3high_stack
+
+	x4 = h4 - y4
+	r0lowx6 = r0low * x6
+	r0high = r0high_stack
+
+	x3 = h3 - y3
+	r3highx0 = r3high * x0
+	sr1low = sr1low_stack
+
+	x2 = h2 - y2
+	r0highx6 = r0high * x6
+	sr1high = sr1high_stack
+
+	x5 += y3
+	r0lowx0 = r0low * x0
+	r1low = r1low_stack
+
+	h6 = r3lowx0 + r0lowx6
+	sr1lowx6 = sr1low * x6
+	r1high = r1high_stack
+
+	x4 += y2
+	r0highx0 = r0high * x0
+	sr2low = sr2low_stack
+
+	h7 = r3highx0 + r0highx6
+	sr1highx6 = sr1high * x6
+	sr2high = sr2high_stack
+
+	x3 += y1
+	r1lowx0 = r1low * x0
+	r2low = r2low_stack
+
+	h0 = r0lowx0 + sr1lowx6
+	sr2lowx6 = sr2low * x6
+	r2high = r2high_stack
+
+	x2 += y0
+	r1highx0 = r1high * x0
+	sr3low = sr3low_stack
+
+	h1 = r0highx0 + sr1highx6
+	sr2highx6 = sr2high * x6
+	sr3high = sr3high_stack
+
+	x4 += x5
+	r2lowx0 = r2low * x0
+	z2 = math.Float64frombits(uint64(d2))
+
+	h2 = r1lowx0 + sr2lowx6
+	sr3lowx6 = sr3low * x6
+
+	x2 += x3
+	r2highx0 = r2high * x0
+	z3 = math.Float64frombits(uint64(d3))
+
+	h3 = r1highx0 + sr2highx6
+	sr3highx6 = sr3high * x6
+
+	r1highx4 = r1high * x4
+	z2 -= alpha64
+
+	h4 = r2lowx0 + sr3lowx6
+	r1lowx4 = r1low * x4
+
+	r0highx4 = r0high * x4
+	z3 -= alpha96
+
+	h5 = r2highx0 + sr3highx6
+	r0lowx4 = r0low * x4
+
+	h7 += r1highx4
+	sr3highx4 = sr3high * x4
+
+	h6 += r1lowx4
+	sr3lowx4 = sr3low * x4
+
+	h5 += r0highx4
+	sr2highx4 = sr2high * x4
+
+	h4 += r0lowx4
+	sr2lowx4 = sr2low * x4
+
+	h3 += sr3highx4
+	r0lowx2 = r0low * x2
+
+	h2 += sr3lowx4
+	r0highx2 = r0high * x2
+
+	h1 += sr2highx4
+	r1lowx2 = r1low * x2
+
+	h0 += sr2lowx4
+	r1highx2 = r1high * x2
+
+	h2 += r0lowx2
+	r2lowx2 = r2low * x2
+
+	h3 += r0highx2
+	r2highx2 = r2high * x2
+
+	h4 += r1lowx2
+	sr3lowx2 = sr3low * x2
+
+	h5 += r1highx2
+	sr3highx2 = sr3high * x2
+
+	p += 16
+	l -= 16
+	h6 += r2lowx2
+
+	h7 += r2highx2
+
+	z1 = math.Float64frombits(uint64(d1))
+	h0 += sr3lowx2
+
+	z0 = math.Float64frombits(uint64(d0))
+	h1 += sr3highx2
+
+	z1 -= alpha32
+
+	z0 -= alpha0
+
+	h5 += z3
+
+	h3 += z2
+
+	h1 += z1
+
+	h0 += z0
+
+	if l >= 16 {
+		goto multiplyaddatleast16bytes
+	}
+
+multiplyaddatmost15bytes:
+
+	y7 = h7 + alpha130
+
+	y6 = h6 + alpha130
+
+	y1 = h1 + alpha32
+
+	y0 = h0 + alpha32
+
+	y7 -= alpha130
+
+	y6 -= alpha130
+
+	y1 -= alpha32
+
+	y0 -= alpha32
+
+	y5 = h5 + alpha96
+
+	y4 = h4 + alpha96
+
+	x7 = h7 - y7
+	y7 *= scale
+
+	x6 = h6 - y6
+	y6 *= scale
+
+	x1 = h1 - y1
+
+	x0 = h0 - y0
+
+	y5 -= alpha96
+
+	y4 -= alpha96
+
+	x1 += y7
+
+	x0 += y6
+
+	x7 += y5
+
+	x6 += y4
+
+	y3 = h3 + alpha64
+
+	y2 = h2 + alpha64
+
+	x0 += x1
+
+	x6 += x7
+
+	y3 -= alpha64
+	r3low = r3low_stack
+
+	y2 -= alpha64
+	r0low = r0low_stack
+
+	x5 = h5 - y5
+	r3lowx0 = r3low * x0
+	r3high = r3high_stack
+
+	x4 = h4 - y4
+	r0lowx6 = r0low * x6
+	r0high = r0high_stack
+
+	x3 = h3 - y3
+	r3highx0 = r3high * x0
+	sr1low = sr1low_stack
+
+	x2 = h2 - y2
+	r0highx6 = r0high * x6
+	sr1high = sr1high_stack
+
+	x5 += y3
+	r0lowx0 = r0low * x0
+	r1low = r1low_stack
+
+	h6 = r3lowx0 + r0lowx6
+	sr1lowx6 = sr1low * x6
+	r1high = r1high_stack
+
+	x4 += y2
+	r0highx0 = r0high * x0
+	sr2low = sr2low_stack
+
+	h7 = r3highx0 + r0highx6
+	sr1highx6 = sr1high * x6
+	sr2high = sr2high_stack
+
+	x3 += y1
+	r1lowx0 = r1low * x0
+	r2low = r2low_stack
+
+	h0 = r0lowx0 + sr1lowx6
+	sr2lowx6 = sr2low * x6
+	r2high = r2high_stack
+
+	x2 += y0
+	r1highx0 = r1high * x0
+	sr3low = sr3low_stack
+
+	h1 = r0highx0 + sr1highx6
+	sr2highx6 = sr2high * x6
+	sr3high = sr3high_stack
+
+	x4 += x5
+	r2lowx0 = r2low * x0
+
+	h2 = r1lowx0 + sr2lowx6
+	sr3lowx6 = sr3low * x6
+
+	x2 += x3
+	r2highx0 = r2high * x0
+
+	h3 = r1highx0 + sr2highx6
+	sr3highx6 = sr3high * x6
+
+	r1highx4 = r1high * x4
+
+	h4 = r2lowx0 + sr3lowx6
+	r1lowx4 = r1low * x4
+
+	r0highx4 = r0high * x4
+
+	h5 = r2highx0 + sr3highx6
+	r0lowx4 = r0low * x4
+
+	h7 += r1highx4
+	sr3highx4 = sr3high * x4
+
+	h6 += r1lowx4
+	sr3lowx4 = sr3low * x4
+
+	h5 += r0highx4
+	sr2highx4 = sr2high * x4
+
+	h4 += r0lowx4
+	sr2lowx4 = sr2low * x4
+
+	h3 += sr3highx4
+	r0lowx2 = r0low * x2
+
+	h2 += sr3lowx4
+	r0highx2 = r0high * x2
+
+	h1 += sr2highx4
+	r1lowx2 = r1low * x2
+
+	h0 += sr2lowx4
+	r1highx2 = r1high * x2
+
+	h2 += r0lowx2
+	r2lowx2 = r2low * x2
+
+	h3 += r0highx2
+	r2highx2 = r2high * x2
+
+	h4 += r1lowx2
+	sr3lowx2 = sr3low * x2
+
+	h5 += r1highx2
+	sr3highx2 = sr3high * x2
+
+	h6 += r2lowx2
+
+	h7 += r2highx2
+
+	h0 += sr3lowx2
+
+	h1 += sr3highx2
+
+addatmost15bytes:
+
+	if l == 0 {
+		goto nomorebytes
+	}
+
+	lbelow2 = l - 2
+
+	lbelow3 = l - 3
+
+	lbelow2 >>= 31
+	lbelow4 = l - 4
+
+	m00 = uint32(m[p+0])
+	lbelow3 >>= 31
+	p += lbelow2
+
+	m01 = uint32(m[p+1])
+	lbelow4 >>= 31
+	p += lbelow3
+
+	m02 = uint32(m[p+2])
+	p += lbelow4
+	m0 = 2151
+
+	m03 = uint32(m[p+3])
+	m0 <<= 51
+	m1 = 2215
+
+	m0 += int64(m00)
+	m01 &^= uint32(lbelow2)
+
+	m02 &^= uint32(lbelow3)
+	m01 -= uint32(lbelow2)
+
+	m01 <<= 8
+	m03 &^= uint32(lbelow4)
+
+	m0 += int64(m01)
+	lbelow2 -= lbelow3
+
+	m02 += uint32(lbelow2)
+	lbelow3 -= lbelow4
+
+	m02 <<= 16
+	m03 += uint32(lbelow3)
+
+	m03 <<= 24
+	m0 += int64(m02)
+
+	m0 += int64(m03)
+	lbelow5 = l - 5
+
+	lbelow6 = l - 6
+	lbelow7 = l - 7
+
+	lbelow5 >>= 31
+	lbelow8 = l - 8
+
+	lbelow6 >>= 31
+	p += lbelow5
+
+	m10 = uint32(m[p+4])
+	lbelow7 >>= 31
+	p += lbelow6
+
+	m11 = uint32(m[p+5])
+	lbelow8 >>= 31
+	p += lbelow7
+
+	m12 = uint32(m[p+6])
+	m1 <<= 51
+	p += lbelow8
+
+	m13 = uint32(m[p+7])
+	m10 &^= uint32(lbelow5)
+	lbelow4 -= lbelow5
+
+	m10 += uint32(lbelow4)
+	lbelow5 -= lbelow6
+
+	m11 &^= uint32(lbelow6)
+	m11 += uint32(lbelow5)
+
+	m11 <<= 8
+	m1 += int64(m10)
+
+	m1 += int64(m11)
+	m12 &^= uint32(lbelow7)
+
+	lbelow6 -= lbelow7
+	m13 &^= uint32(lbelow8)
+
+	m12 += uint32(lbelow6)
+	lbelow7 -= lbelow8
+
+	m12 <<= 16
+	m13 += uint32(lbelow7)
+
+	m13 <<= 24
+	m1 += int64(m12)
+
+	m1 += int64(m13)
+	m2 = 2279
+
+	lbelow9 = l - 9
+	m3 = 2343
+
+	lbelow10 = l - 10
+	lbelow11 = l - 11
+
+	lbelow9 >>= 31
+	lbelow12 = l - 12
+
+	lbelow10 >>= 31
+	p += lbelow9
+
+	m20 = uint32(m[p+8])
+	lbelow11 >>= 31
+	p += lbelow10
+
+	m21 = uint32(m[p+9])
+	lbelow12 >>= 31
+	p += lbelow11
+
+	m22 = uint32(m[p+10])
+	m2 <<= 51
+	p += lbelow12
+
+	m23 = uint32(m[p+11])
+	m20 &^= uint32(lbelow9)
+	lbelow8 -= lbelow9
+
+	m20 += uint32(lbelow8)
+	lbelow9 -= lbelow10
+
+	m21 &^= uint32(lbelow10)
+	m21 += uint32(lbelow9)
+
+	m21 <<= 8
+	m2 += int64(m20)
+
+	m2 += int64(m21)
+	m22 &^= uint32(lbelow11)
+
+	lbelow10 -= lbelow11
+	m23 &^= uint32(lbelow12)
+
+	m22 += uint32(lbelow10)
+	lbelow11 -= lbelow12
+
+	m22 <<= 16
+	m23 += uint32(lbelow11)
+
+	m23 <<= 24
+	m2 += int64(m22)
+
+	m3 <<= 51
+	lbelow13 = l - 13
+
+	lbelow13 >>= 31
+	lbelow14 = l - 14
+
+	lbelow14 >>= 31
+	p += lbelow13
+	lbelow15 = l - 15
+
+	m30 = uint32(m[p+12])
+	lbelow15 >>= 31
+	p += lbelow14
+
+	m31 = uint32(m[p+13])
+	p += lbelow15
+	m2 += int64(m23)
+
+	m32 = uint32(m[p+14])
+	m30 &^= uint32(lbelow13)
+	lbelow12 -= lbelow13
+
+	m30 += uint32(lbelow12)
+	lbelow13 -= lbelow14
+
+	m3 += int64(m30)
+	m31 &^= uint32(lbelow14)
+
+	m31 += uint32(lbelow13)
+	m32 &^= uint32(lbelow15)
+
+	m31 <<= 8
+	lbelow14 -= lbelow15
+
+	m3 += int64(m31)
+	m32 += uint32(lbelow14)
+	d0 = m0
+
+	m32 <<= 16
+	m33 = uint64(lbelow15 + 1)
+	d1 = m1
+
+	m33 <<= 24
+	m3 += int64(m32)
+	d2 = m2
+
+	m3 += int64(m33)
+	d3 = m3
+
+	z3 = math.Float64frombits(uint64(d3))
+
+	z2 = math.Float64frombits(uint64(d2))
+
+	z1 = math.Float64frombits(uint64(d1))
+
+	z0 = math.Float64frombits(uint64(d0))
+
+	z3 -= alpha96
+
+	z2 -= alpha64
+
+	z1 -= alpha32
+
+	z0 -= alpha0
+
+	h5 += z3
+
+	h3 += z2
+
+	h1 += z1
+
+	h0 += z0
+
+	y7 = h7 + alpha130
+
+	y6 = h6 + alpha130
+
+	y1 = h1 + alpha32
+
+	y0 = h0 + alpha32
+
+	y7 -= alpha130
+
+	y6 -= alpha130
+
+	y1 -= alpha32
+
+	y0 -= alpha32
+
+	y5 = h5 + alpha96
+
+	y4 = h4 + alpha96
+
+	x7 = h7 - y7
+	y7 *= scale
+
+	x6 = h6 - y6
+	y6 *= scale
+
+	x1 = h1 - y1
+
+	x0 = h0 - y0
+
+	y5 -= alpha96
+
+	y4 -= alpha96
+
+	x1 += y7
+
+	x0 += y6
+
+	x7 += y5
+
+	x6 += y4
+
+	y3 = h3 + alpha64
+
+	y2 = h2 + alpha64
+
+	x0 += x1
+
+	x6 += x7
+
+	y3 -= alpha64
+	r3low = r3low_stack
+
+	y2 -= alpha64
+	r0low = r0low_stack
+
+	x5 = h5 - y5
+	r3lowx0 = r3low * x0
+	r3high = r3high_stack
+
+	x4 = h4 - y4
+	r0lowx6 = r0low * x6
+	r0high = r0high_stack
+
+	x3 = h3 - y3
+	r3highx0 = r3high * x0
+	sr1low = sr1low_stack
+
+	x2 = h2 - y2
+	r0highx6 = r0high * x6
+	sr1high = sr1high_stack
+
+	x5 += y3
+	r0lowx0 = r0low * x0
+	r1low = r1low_stack
+
+	h6 = r3lowx0 + r0lowx6
+	sr1lowx6 = sr1low * x6
+	r1high = r1high_stack
+
+	x4 += y2
+	r0highx0 = r0high * x0
+	sr2low = sr2low_stack
+
+	h7 = r3highx0 + r0highx6
+	sr1highx6 = sr1high * x6
+	sr2high = sr2high_stack
+
+	x3 += y1
+	r1lowx0 = r1low * x0
+	r2low = r2low_stack
+
+	h0 = r0lowx0 + sr1lowx6
+	sr2lowx6 = sr2low * x6
+	r2high = r2high_stack
+
+	x2 += y0
+	r1highx0 = r1high * x0
+	sr3low = sr3low_stack
+
+	h1 = r0highx0 + sr1highx6
+	sr2highx6 = sr2high * x6
+	sr3high = sr3high_stack
+
+	x4 += x5
+	r2lowx0 = r2low * x0
+
+	h2 = r1lowx0 + sr2lowx6
+	sr3lowx6 = sr3low * x6
+
+	x2 += x3
+	r2highx0 = r2high * x0
+
+	h3 = r1highx0 + sr2highx6
+	sr3highx6 = sr3high * x6
+
+	r1highx4 = r1high * x4
+
+	h4 = r2lowx0 + sr3lowx6
+	r1lowx4 = r1low * x4
+
+	r0highx4 = r0high * x4
+
+	h5 = r2highx0 + sr3highx6
+	r0lowx4 = r0low * x4
+
+	h7 += r1highx4
+	sr3highx4 = sr3high * x4
+
+	h6 += r1lowx4
+	sr3lowx4 = sr3low * x4
+
+	h5 += r0highx4
+	sr2highx4 = sr2high * x4
+
+	h4 += r0lowx4
+	sr2lowx4 = sr2low * x4
+
+	h3 += sr3highx4
+	r0lowx2 = r0low * x2
+
+	h2 += sr3lowx4
+	r0highx2 = r0high * x2
+
+	h1 += sr2highx4
+	r1lowx2 = r1low * x2
+
+	h0 += sr2lowx4
+	r1highx2 = r1high * x2
+
+	h2 += r0lowx2
+	r2lowx2 = r2low * x2
+
+	h3 += r0highx2
+	r2highx2 = r2high * x2
+
+	h4 += r1lowx2
+	sr3lowx2 = sr3low * x2
+
+	h5 += r1highx2
+	sr3highx2 = sr3high * x2
+
+	h6 += r2lowx2
+
+	h7 += r2highx2
+
+	h0 += sr3lowx2
+
+	h1 += sr3highx2
+
+nomorebytes:
+
+	y7 = h7 + alpha130
+
+	y0 = h0 + alpha32
+
+	y1 = h1 + alpha32
+
+	y2 = h2 + alpha64
+
+	y7 -= alpha130
+
+	y3 = h3 + alpha64
+
+	y4 = h4 + alpha96
+
+	y5 = h5 + alpha96
+
+	x7 = h7 - y7
+	y7 *= scale
+
+	y0 -= alpha32
+
+	y1 -= alpha32
+
+	y2 -= alpha64
+
+	h6 += x7
+
+	y3 -= alpha64
+
+	y4 -= alpha96
+
+	y5 -= alpha96
+
+	y6 = h6 + alpha130
+
+	x0 = h0 - y0
+
+	x1 = h1 - y1
+
+	x2 = h2 - y2
+
+	y6 -= alpha130
+
+	x0 += y7
+
+	x3 = h3 - y3
+
+	x4 = h4 - y4
+
+	x5 = h5 - y5
+
+	x6 = h6 - y6
+
+	y6 *= scale
+
+	x2 += y0
+
+	x3 += y1
+
+	x4 += y2
+
+	x0 += y6
+
+	x5 += y3
+
+	x6 += y4
+
+	x2 += x3
+
+	x0 += x1
+
+	x4 += x5
+
+	x6 += y5
+
+	x2 += offset1
+	d1 = int64(math.Float64bits(x2))
+
+	x0 += offset0
+	d0 = int64(math.Float64bits(x0))
+
+	x4 += offset2
+	d2 = int64(math.Float64bits(x4))
+
+	x6 += offset3
+	d3 = int64(math.Float64bits(x6))
+
+	f0 = uint64(d0)
+
+	f1 = uint64(d1)
+	bits32 = math.MaxUint64
+
+	f2 = uint64(d2)
+	bits32 >>= 32
+
+	f3 = uint64(d3)
+	f = f0 >> 32
+
+	f0 &= bits32
+	f &= 255
+
+	f1 += f
+	g0 = f0 + 5
+
+	g = g0 >> 32
+	g0 &= bits32
+
+	f = f1 >> 32
+	f1 &= bits32
+
+	f &= 255
+	g1 = f1 + g
+
+	g = g1 >> 32
+	f2 += f
+
+	f = f2 >> 32
+	g1 &= bits32
+
+	f2 &= bits32
+	f &= 255
+
+	f3 += f
+	g2 = f2 + g
+
+	g = g2 >> 32
+	g2 &= bits32
+
+	f4 = f3 >> 32
+	f3 &= bits32
+
+	f4 &= 255
+	g3 = f3 + g
+
+	g = g3 >> 32
+	g3 &= bits32
+
+	g4 = f4 + g
+
+	g4 = g4 - 4
+	s00 = uint32(s[0])
+
+	f = uint64(int64(g4) >> 63)
+	s01 = uint32(s[1])
+
+	f0 &= f
+	g0 &^= f
+	s02 = uint32(s[2])
+
+	f1 &= f
+	f0 |= g0
+	s03 = uint32(s[3])
+
+	g1 &^= f
+	f2 &= f
+	s10 = uint32(s[4])
+
+	f3 &= f
+	g2 &^= f
+	s11 = uint32(s[5])
+
+	g3 &^= f
+	f1 |= g1
+	s12 = uint32(s[6])
+
+	f2 |= g2
+	f3 |= g3
+	s13 = uint32(s[7])
+
+	s01 <<= 8
+	f0 += uint64(s00)
+	s20 = uint32(s[8])
+
+	s02 <<= 16
+	f0 += uint64(s01)
+	s21 = uint32(s[9])
+
+	s03 <<= 24
+	f0 += uint64(s02)
+	s22 = uint32(s[10])
+
+	s11 <<= 8
+	f1 += uint64(s10)
+	s23 = uint32(s[11])
+
+	s12 <<= 16
+	f1 += uint64(s11)
+	s30 = uint32(s[12])
+
+	s13 <<= 24
+	f1 += uint64(s12)
+	s31 = uint32(s[13])
+
+	f0 += uint64(s03)
+	f1 += uint64(s13)
+	s32 = uint32(s[14])
+
+	s21 <<= 8
+	f2 += uint64(s20)
+	s33 = uint32(s[15])
+
+	s22 <<= 16
+	f2 += uint64(s21)
+
+	s23 <<= 24
+	f2 += uint64(s22)
+
+	s31 <<= 8
+	f3 += uint64(s30)
+
+	s32 <<= 16
+	f3 += uint64(s31)
+
+	s33 <<= 24
+	f3 += uint64(s32)
+
+	f2 += uint64(s23)
+	f3 += uint64(s33)
+
+	out[0] = byte(f0)
+	f0 >>= 8
+	out[1] = byte(f0)
+	f0 >>= 8
+	out[2] = byte(f0)
+	f0 >>= 8
+	out[3] = byte(f0)
+	f0 >>= 8
+	f1 += f0
+
+	out[4] = byte(f1)
+	f1 >>= 8
+	out[5] = byte(f1)
+	f1 >>= 8
+	out[6] = byte(f1)
+	f1 >>= 8
+	out[7] = byte(f1)
+	f1 >>= 8
+	f2 += f1
+
+	out[8] = byte(f2)
+	f2 >>= 8
+	out[9] = byte(f2)
+	f2 >>= 8
+	out[10] = byte(f2)
+	f2 >>= 8
+	out[11] = byte(f2)
+	f2 >>= 8
+	f3 += f2
+
+	out[12] = byte(f3)
+	f3 >>= 8
+	out[13] = byte(f3)
+	f3 >>= 8
+	out[14] = byte(f3)
+	f3 >>= 8
+	out[15] = byte(f3)
+}
diff --git a/src/vendor/golang_org/x/net/idna/idna.go b/src/vendor/golang_org/x/net/idna/idna.go
new file mode 100644
index 0000000..3daa897
--- /dev/null
+++ b/src/vendor/golang_org/x/net/idna/idna.go
@@ -0,0 +1,68 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package idna implements IDNA2008 (Internationalized Domain Names for
+// Applications), defined in RFC 5890, RFC 5891, RFC 5892, RFC 5893 and
+// RFC 5894.
+package idna // import "golang.org/x/net/idna"
+
+import (
+	"strings"
+	"unicode/utf8"
+)
+
+// TODO(nigeltao): specify when errors occur. For example, is ToASCII(".") or
+// ToASCII("foo\x00") an error? See also http://www.unicode.org/faq/idn.html#11
+
+// acePrefix is the ASCII Compatible Encoding prefix.
+const acePrefix = "xn--"
+
+// ToASCII converts a domain or domain label to its ASCII form. For example,
+// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
+// ToASCII("golang") is "golang".
+func ToASCII(s string) (string, error) {
+	if ascii(s) {
+		return s, nil
+	}
+	labels := strings.Split(s, ".")
+	for i, label := range labels {
+		if !ascii(label) {
+			a, err := encode(acePrefix, label)
+			if err != nil {
+				return "", err
+			}
+			labels[i] = a
+		}
+	}
+	return strings.Join(labels, "."), nil
+}
+
+// ToUnicode converts a domain or domain label to its Unicode form. For example,
+// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
+// ToUnicode("golang") is "golang".
+func ToUnicode(s string) (string, error) {
+	if !strings.Contains(s, acePrefix) {
+		return s, nil
+	}
+	labels := strings.Split(s, ".")
+	for i, label := range labels {
+		if strings.HasPrefix(label, acePrefix) {
+			u, err := decode(label[len(acePrefix):])
+			if err != nil {
+				return "", err
+			}
+			labels[i] = u
+		}
+	}
+	return strings.Join(labels, "."), nil
+}
+
+func ascii(s string) bool {
+	for i := 0; i < len(s); i++ {
+		if s[i] >= utf8.RuneSelf {
+			return false
+		}
+	}
+	return true
+}
diff --git a/src/vendor/golang_org/x/net/idna/idna_test.go b/src/vendor/golang_org/x/net/idna/idna_test.go
new file mode 100644
index 0000000..b1bc6fa
--- /dev/null
+++ b/src/vendor/golang_org/x/net/idna/idna_test.go
@@ -0,0 +1,43 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package idna
+
+import (
+	"testing"
+)
+
+var idnaTestCases = [...]struct {
+	ascii, unicode string
+}{
+	// Labels.
+	{"books", "books"},
+	{"xn--bcher-kva", "bücher"},
+
+	// Domains.
+	{"foo--xn--bar.org", "foo--xn--bar.org"},
+	{"golang.org", "golang.org"},
+	{"example.xn--p1ai", "example.рф"},
+	{"xn--czrw28b.tw", "商業.tw"},
+	{"www.xn--mller-kva.de", "www.müller.de"},
+}
+
+func TestIDNA(t *testing.T) {
+	for _, tc := range idnaTestCases {
+		if a, err := ToASCII(tc.unicode); err != nil {
+			t.Errorf("ToASCII(%q): %v", tc.unicode, err)
+		} else if a != tc.ascii {
+			t.Errorf("ToASCII(%q): got %q, want %q", tc.unicode, a, tc.ascii)
+		}
+
+		if u, err := ToUnicode(tc.ascii); err != nil {
+			t.Errorf("ToUnicode(%q): %v", tc.ascii, err)
+		} else if u != tc.unicode {
+			t.Errorf("ToUnicode(%q): got %q, want %q", tc.ascii, u, tc.unicode)
+		}
+	}
+}
+
+// TODO(nigeltao): test errors, once we've specified when ToASCII and ToUnicode
+// return errors.
diff --git a/src/vendor/golang_org/x/net/idna/punycode.go b/src/vendor/golang_org/x/net/idna/punycode.go
new file mode 100644
index 0000000..92e733f
--- /dev/null
+++ b/src/vendor/golang_org/x/net/idna/punycode.go
@@ -0,0 +1,200 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package idna
+
+// This file implements the Punycode algorithm from RFC 3492.
+
+import (
+	"fmt"
+	"math"
+	"strings"
+	"unicode/utf8"
+)
+
+// These parameter values are specified in section 5.
+//
+// All computation is done with int32s, so that overflow behavior is identical
+// regardless of whether int is 32-bit or 64-bit.
+const (
+	base        int32 = 36
+	damp        int32 = 700
+	initialBias int32 = 72
+	initialN    int32 = 128
+	skew        int32 = 38
+	tmax        int32 = 26
+	tmin        int32 = 1
+)
+
+// decode decodes a string as specified in section 6.2.
+func decode(encoded string) (string, error) {
+	if encoded == "" {
+		return "", nil
+	}
+	pos := 1 + strings.LastIndex(encoded, "-")
+	if pos == 1 {
+		return "", fmt.Errorf("idna: invalid label %q", encoded)
+	}
+	if pos == len(encoded) {
+		return encoded[:len(encoded)-1], nil
+	}
+	output := make([]rune, 0, len(encoded))
+	if pos != 0 {
+		for _, r := range encoded[:pos-1] {
+			output = append(output, r)
+		}
+	}
+	i, n, bias := int32(0), initialN, initialBias
+	for pos < len(encoded) {
+		oldI, w := i, int32(1)
+		for k := base; ; k += base {
+			if pos == len(encoded) {
+				return "", fmt.Errorf("idna: invalid label %q", encoded)
+			}
+			digit, ok := decodeDigit(encoded[pos])
+			if !ok {
+				return "", fmt.Errorf("idna: invalid label %q", encoded)
+			}
+			pos++
+			i += digit * w
+			if i < 0 {
+				return "", fmt.Errorf("idna: invalid label %q", encoded)
+			}
+			t := k - bias
+			if t < tmin {
+				t = tmin
+			} else if t > tmax {
+				t = tmax
+			}
+			if digit < t {
+				break
+			}
+			w *= base - t
+			if w >= math.MaxInt32/base {
+				return "", fmt.Errorf("idna: invalid label %q", encoded)
+			}
+		}
+		x := int32(len(output) + 1)
+		bias = adapt(i-oldI, x, oldI == 0)
+		n += i / x
+		i %= x
+		if n > utf8.MaxRune || len(output) >= 1024 {
+			return "", fmt.Errorf("idna: invalid label %q", encoded)
+		}
+		output = append(output, 0)
+		copy(output[i+1:], output[i:])
+		output[i] = n
+		i++
+	}
+	return string(output), nil
+}
+
+// encode encodes a string as specified in section 6.3 and prepends prefix to
+// the result.
+//
+// The "while h < length(input)" line in the specification becomes "for
+// remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes.
+func encode(prefix, s string) (string, error) {
+	output := make([]byte, len(prefix), len(prefix)+1+2*len(s))
+	copy(output, prefix)
+	delta, n, bias := int32(0), initialN, initialBias
+	b, remaining := int32(0), int32(0)
+	for _, r := range s {
+		if r < 0x80 {
+			b++
+			output = append(output, byte(r))
+		} else {
+			remaining++
+		}
+	}
+	h := b
+	if b > 0 {
+		output = append(output, '-')
+	}
+	for remaining != 0 {
+		m := int32(0x7fffffff)
+		for _, r := range s {
+			if m > r && r >= n {
+				m = r
+			}
+		}
+		delta += (m - n) * (h + 1)
+		if delta < 0 {
+			return "", fmt.Errorf("idna: invalid label %q", s)
+		}
+		n = m
+		for _, r := range s {
+			if r < n {
+				delta++
+				if delta < 0 {
+					return "", fmt.Errorf("idna: invalid label %q", s)
+				}
+				continue
+			}
+			if r > n {
+				continue
+			}
+			q := delta
+			for k := base; ; k += base {
+				t := k - bias
+				if t < tmin {
+					t = tmin
+				} else if t > tmax {
+					t = tmax
+				}
+				if q < t {
+					break
+				}
+				output = append(output, encodeDigit(t+(q-t)%(base-t)))
+				q = (q - t) / (base - t)
+			}
+			output = append(output, encodeDigit(q))
+			bias = adapt(delta, h+1, h == b)
+			delta = 0
+			h++
+			remaining--
+		}
+		delta++
+		n++
+	}
+	return string(output), nil
+}
+
+func decodeDigit(x byte) (digit int32, ok bool) {
+	switch {
+	case '0' <= x && x <= '9':
+		return int32(x - ('0' - 26)), true
+	case 'A' <= x && x <= 'Z':
+		return int32(x - 'A'), true
+	case 'a' <= x && x <= 'z':
+		return int32(x - 'a'), true
+	}
+	return 0, false
+}
+
+func encodeDigit(digit int32) byte {
+	switch {
+	case 0 <= digit && digit < 26:
+		return byte(digit + 'a')
+	case 26 <= digit && digit < 36:
+		return byte(digit + ('0' - 26))
+	}
+	panic("idna: internal error in punycode encoding")
+}
+
+// adapt is the bias adaptation function specified in section 6.1.
+func adapt(delta, numPoints int32, firstTime bool) int32 {
+	if firstTime {
+		delta /= damp
+	} else {
+		delta /= 2
+	}
+	delta += delta / numPoints
+	k := int32(0)
+	for delta > ((base-tmin)*tmax)/2 {
+		delta /= base - tmin
+		k += base
+	}
+	return k + (base-tmin+1)*delta/(delta+skew)
+}
diff --git a/src/vendor/golang_org/x/net/idna/punycode_test.go b/src/vendor/golang_org/x/net/idna/punycode_test.go
new file mode 100644
index 0000000..bfec81d
--- /dev/null
+++ b/src/vendor/golang_org/x/net/idna/punycode_test.go
@@ -0,0 +1,198 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package idna
+
+import (
+	"strings"
+	"testing"
+)
+
+var punycodeTestCases = [...]struct {
+	s, encoded string
+}{
+	{"", ""},
+	{"-", "--"},
+	{"-a", "-a-"},
+	{"-a-", "-a--"},
+	{"a", "a-"},
+	{"a-", "a--"},
+	{"a-b", "a-b-"},
+	{"books", "books-"},
+	{"bücher", "bcher-kva"},
+	{"Hello世界", "Hello-ck1hg65u"},
+	{"ü", "tda"},
+	{"üý", "tdac"},
+
+	// The test cases below come from RFC 3492 section 7.1 with Errata 3026.
+	{
+		// (A) Arabic (Egyptian).
+		"\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644" +
+			"\u0645\u0648\u0634\u0639\u0631\u0628\u064A\u061F",
+		"egbpdaj6bu4bxfgehfvwxn",
+	},
+	{
+		// (B) Chinese (simplified).
+		"\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587",
+		"ihqwcrb4cv8a8dqg056pqjye",
+	},
+	{
+		// (C) Chinese (traditional).
+		"\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587",
+		"ihqwctvzc91f659drss3x8bo0yb",
+	},
+	{
+		// (D) Czech.
+		"\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074" +
+			"\u011B\u006E\u0065\u006D\u006C\u0075\u0076\u00ED\u010D" +
+			"\u0065\u0073\u006B\u0079",
+		"Proprostnemluvesky-uyb24dma41a",
+	},
+	{
+		// (E) Hebrew.
+		"\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8" +
+			"\u05DC\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2" +
+			"\u05D1\u05E8\u05D9\u05EA",
+		"4dbcagdahymbxekheh6e0a7fei0b",
+	},
+	{
+		// (F) Hindi (Devanagari).
+		"\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D" +
+			"\u0926\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939" +
+			"\u0940\u0902\u092C\u094B\u0932\u0938\u0915\u0924\u0947" +
+			"\u0939\u0948\u0902",
+		"i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd",
+	},
+	{
+		// (G) Japanese (kanji and hiragana).
+		"\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092" +
+			"\u8A71\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B",
+		"n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa",
+	},
+	{
+		// (H) Korean (Hangul syllables).
+		"\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774" +
+			"\uD55C\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74" +
+			"\uC5BC\uB9C8\uB098\uC88B\uC744\uAE4C",
+		"989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5j" +
+			"psd879ccm6fea98c",
+	},
+	{
+		// (I) Russian (Cyrillic).
+		"\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E" +
+			"\u043D\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440" +
+			"\u044F\u0442\u043F\u043E\u0440\u0443\u0441\u0441\u043A" +
+			"\u0438",
+		"b1abfaaepdrnnbgefbadotcwatmq2g4l",
+	},
+	{
+		// (J) Spanish.
+		"\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070" +
+			"\u0075\u0065\u0064\u0065\u006E\u0073\u0069\u006D\u0070" +
+			"\u006C\u0065\u006D\u0065\u006E\u0074\u0065\u0068\u0061" +
+			"\u0062\u006C\u0061\u0072\u0065\u006E\u0045\u0073\u0070" +
+			"\u0061\u00F1\u006F\u006C",
+		"PorqunopuedensimplementehablarenEspaol-fmd56a",
+	},
+	{
+		// (K) Vietnamese.
+		"\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B" +
+			"\u0068\u00F4\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068" +
+			"\u1EC9\u006E\u00F3\u0069\u0074\u0069\u1EBF\u006E\u0067" +
+			"\u0056\u0069\u1EC7\u0074",
+		"TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g",
+	},
+	{
+		// (L) 3<nen>B<gumi><kinpachi><sensei>.
+		"\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F",
+		"3B-ww4c5e180e575a65lsy2b",
+	},
+	{
+		// (M) <amuro><namie>-with-SUPER-MONKEYS.
+		"\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074" +
+			"\u0068\u002D\u0053\u0055\u0050\u0045\u0052\u002D\u004D" +
+			"\u004F\u004E\u004B\u0045\u0059\u0053",
+		"-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n",
+	},
+	{
+		// (N) Hello-Another-Way-<sorezore><no><basho>.
+		"\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F" +
+			"\u0074\u0068\u0065\u0072\u002D\u0057\u0061\u0079\u002D" +
+			"\u305D\u308C\u305E\u308C\u306E\u5834\u6240",
+		"Hello-Another-Way--fc4qua05auwb3674vfr0b",
+	},
+	{
+		// (O) <hitotsu><yane><no><shita>2.
+		"\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032",
+		"2-u9tlzr9756bt3uc0v",
+	},
+	{
+		// (P) Maji<de>Koi<suru>5<byou><mae>
+		"\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059" +
+			"\u308B\u0035\u79D2\u524D",
+		"MajiKoi5-783gue6qz075azm5e",
+	},
+	{
+		// (Q) <pafii>de<runba>
+		"\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0",
+		"de-jg4avhby1noc0d",
+	},
+	{
+		// (R) <sono><supiido><de>
+		"\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067",
+		"d9juau41awczczp",
+	},
+	{
+		// (S) -> $1.00 <-
+		"\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020" +
+			"\u003C\u002D",
+		"-> $1.00 <--",
+	},
+}
+
+func TestPunycode(t *testing.T) {
+	for _, tc := range punycodeTestCases {
+		if got, err := decode(tc.encoded); err != nil {
+			t.Errorf("decode(%q): %v", tc.encoded, err)
+		} else if got != tc.s {
+			t.Errorf("decode(%q): got %q, want %q", tc.encoded, got, tc.s)
+		}
+
+		if got, err := encode("", tc.s); err != nil {
+			t.Errorf(`encode("", %q): %v`, tc.s, err)
+		} else if got != tc.encoded {
+			t.Errorf(`encode("", %q): got %q, want %q`, tc.s, got, tc.encoded)
+		}
+	}
+}
+
+var punycodeErrorTestCases = [...]string{
+	"decode -",            // A sole '-' is invalid.
+	"decode foo\x00bar",   // '\x00' is not in [0-9A-Za-z].
+	"decode foo#bar",      // '#' is not in [0-9A-Za-z].
+	"decode foo\u00A3bar", // '\u00A3' is not in [0-9A-Za-z].
+	"decode 9",            // "9a" decodes to codepoint \u00A3; "9" is truncated.
+	"decode 99999a",       // "99999a" decodes to codepoint \U0048A3C1, which is > \U0010FFFF.
+	"decode 9999999999a",  // "9999999999a" overflows the int32 calculation.
+
+	"encode " + strings.Repeat("x", 65536) + "\uff00", // int32 overflow.
+}
+
+func TestPunycodeErrors(t *testing.T) {
+	for _, tc := range punycodeErrorTestCases {
+		var err error
+		switch {
+		case strings.HasPrefix(tc, "decode "):
+			_, err = decode(tc[7:])
+		case strings.HasPrefix(tc, "encode "):
+			_, err = encode("", tc[7:])
+		}
+		if err == nil {
+			if len(tc) > 256 {
+				tc = tc[:100] + "..." + tc[len(tc)-100:]
+			}
+			t.Errorf("no error for %s", tc)
+		}
+	}
+}
diff --git a/src/vendor/golang_org/x/net/lex/httplex/httplex.go b/src/vendor/golang_org/x/net/lex/httplex/httplex.go
index bd0ec24..b6493f0 100644
--- a/src/vendor/golang_org/x/net/lex/httplex/httplex.go
+++ b/src/vendor/golang_org/x/net/lex/httplex/httplex.go
@@ -10,8 +10,11 @@
 package httplex
 
 import (
+	"net"
 	"strings"
 	"unicode/utf8"
+
+	"golang_org/x/net/idna"
 )
 
 var isTokenTable = [127]bool{
@@ -310,3 +313,39 @@ func ValidHeaderFieldValue(v string) bool {
 	}
 	return true
 }
+
+func isASCII(s string) bool {
+	for i := 0; i < len(s); i++ {
+		if s[i] >= utf8.RuneSelf {
+			return false
+		}
+	}
+	return true
+}
+
+// PunycodeHostPort returns the IDNA Punycode version
+// of the provided "host" or "host:port" string.
+func PunycodeHostPort(v string) (string, error) {
+	if isASCII(v) {
+		return v, nil
+	}
+
+	host, port, err := net.SplitHostPort(v)
+	if err != nil {
+		// The input 'v' argument was just a "host" argument,
+		// without a port. This error should not be returned
+		// to the caller.
+		host = v
+		port = ""
+	}
+	host, err = idna.ToASCII(host)
+	if err != nil {
+		// Non-UTF-8? Not representable in Punycode, in any
+		// case.
+		return "", err
+	}
+	if port == "" {
+		return host, nil
+	}
+	return net.JoinHostPort(host, port), nil
+}
diff --git a/src/vendor/golang_org/x/net/lex/httplex/httplex_test.go b/src/vendor/golang_org/x/net/lex/httplex/httplex_test.go
index c4ace19..f47adc9 100644
--- a/src/vendor/golang_org/x/net/lex/httplex/httplex_test.go
+++ b/src/vendor/golang_org/x/net/lex/httplex/httplex_test.go
@@ -99,3 +99,21 @@ func TestHeaderValuesContainsToken(t *testing.T) {
 		}
 	}
 }
+
+func TestPunycodeHostPort(t *testing.T) {
+	tests := []struct {
+		in, want string
+	}{
+		{"www.google.com", "www.google.com"},
+		{"гофер.рф", "xn--c1ae0ajs.xn--p1ai"},
+		{"bücher.de", "xn--bcher-kva.de"},
+		{"bücher.de:8080", "xn--bcher-kva.de:8080"},
+		{"[1::6]:8080", "[1::6]:8080"},
+	}
+	for _, tt := range tests {
+		got, err := PunycodeHostPort(tt.in)
+		if tt.want != got || err != nil {
+			t.Errorf("PunycodeHostPort(%q) = %q, %v, want %q, nil", tt.in, got, err, tt.want)
+		}
+	}
+}
diff --git a/src/vendor/golang_org/x/net/lif/address.go b/src/vendor/golang_org/x/net/lif/address.go
new file mode 100644
index 0000000..f9b34ae
--- /dev/null
+++ b/src/vendor/golang_org/x/net/lif/address.go
@@ -0,0 +1,105 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build solaris
+
+package lif
+
+import (
+	"errors"
+	"unsafe"
+)
+
+// An Addr represents an address associated with packet routing.
+type Addr interface {
+	// Family returns an address family.
+	Family() int
+}
+
+// An Inet4Addr represents an internet address for IPv4.
+type Inet4Addr struct {
+	IP        [4]byte // IP address
+	PrefixLen int     // address prefix length
+}
+
+// Family implements the Family method of Addr interface.
+func (a *Inet4Addr) Family() int { return sysAF_INET }
+
+// An Inet6Addr represents an internet address for IPv6.
+type Inet6Addr struct {
+	IP        [16]byte // IP address
+	PrefixLen int      // address prefix length
+	ZoneID    int      // zone identifier
+}
+
+// Family implements the Family method of Addr interface.
+func (a *Inet6Addr) Family() int { return sysAF_INET6 }
+
+// Addrs returns a list of interface addresses.
+//
+// The provided af must be an address family and name must be a data
+// link name. The zero value of af or name means a wildcard.
+func Addrs(af int, name string) ([]Addr, error) {
+	eps, err := newEndpoints(af)
+	if len(eps) == 0 {
+		return nil, err
+	}
+	defer func() {
+		for _, ep := range eps {
+			ep.close()
+		}
+	}()
+	lls, err := links(eps, name)
+	if len(lls) == 0 {
+		return nil, err
+	}
+	var as []Addr
+	for _, ll := range lls {
+		var lifr lifreq
+		for i := 0; i < len(ll.Name); i++ {
+			lifr.Name[i] = int8(ll.Name[i])
+		}
+		for _, ep := range eps {
+			ioc := int64(sysSIOCGLIFADDR)
+			err := ioctl(ep.s, uintptr(ioc), unsafe.Pointer(&lifr))
+			if err != nil {
+				continue
+			}
+			sa := (*sockaddrStorage)(unsafe.Pointer(&lifr.Lifru[0]))
+			l := int(littleEndian.Uint32(lifr.Lifru1[:4]))
+			if l == 0 {
+				continue
+			}
+			switch sa.Family {
+			case sysAF_INET:
+				a := &Inet4Addr{PrefixLen: l}
+				copy(a.IP[:], lifr.Lifru[4:8])
+				as = append(as, a)
+			case sysAF_INET6:
+				a := &Inet6Addr{PrefixLen: l, ZoneID: int(littleEndian.Uint32(lifr.Lifru[24:28]))}
+				copy(a.IP[:], lifr.Lifru[8:24])
+				as = append(as, a)
+			}
+		}
+	}
+	return as, nil
+}
+
+func parseLinkAddr(b []byte) ([]byte, error) {
+	nlen, alen, slen := int(b[1]), int(b[2]), int(b[3])
+	l := 4 + nlen + alen + slen
+	if len(b) < l {
+		return nil, errors.New("invalid address")
+	}
+	b = b[4:]
+	var addr []byte
+	if nlen > 0 {
+		b = b[nlen:]
+	}
+	if alen > 0 {
+		addr = make([]byte, alen)
+		copy(addr, b[:alen])
+	}
+	return addr, nil
+}
diff --git a/src/vendor/golang_org/x/net/lif/address_test.go b/src/vendor/golang_org/x/net/lif/address_test.go
new file mode 100644
index 0000000..f62ed93
--- /dev/null
+++ b/src/vendor/golang_org/x/net/lif/address_test.go
@@ -0,0 +1,121 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build solaris
+
+package lif
+
+import (
+	"fmt"
+	"testing"
+)
+
+type addrFamily int
+
+func (af addrFamily) String() string {
+	switch af {
+	case sysAF_UNSPEC:
+		return "unspec"
+	case sysAF_INET:
+		return "inet4"
+	case sysAF_INET6:
+		return "inet6"
+	default:
+		return fmt.Sprintf("%d", af)
+	}
+}
+
+const hexDigit = "0123456789abcdef"
+
+type llAddr []byte
+
+func (a llAddr) String() string {
+	if len(a) == 0 {
+		return ""
+	}
+	buf := make([]byte, 0, len(a)*3-1)
+	for i, b := range a {
+		if i > 0 {
+			buf = append(buf, ':')
+		}
+		buf = append(buf, hexDigit[b>>4])
+		buf = append(buf, hexDigit[b&0xF])
+	}
+	return string(buf)
+}
+
+type ipAddr []byte
+
+func (a ipAddr) String() string {
+	if len(a) == 0 {
+		return "<nil>"
+	}
+	if len(a) == 4 {
+		return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3])
+	}
+	if len(a) == 16 {
+		return fmt.Sprintf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15])
+	}
+	s := make([]byte, len(a)*2)
+	for i, tn := range a {
+		s[i*2], s[i*2+1] = hexDigit[tn>>4], hexDigit[tn&0xf]
+	}
+	return string(s)
+}
+
+func (a *Inet4Addr) String() string {
+	return fmt.Sprintf("(%s %s %d)", addrFamily(a.Family()), ipAddr(a.IP[:]), a.PrefixLen)
+}
+
+func (a *Inet6Addr) String() string {
+	return fmt.Sprintf("(%s %s %d %d)", addrFamily(a.Family()), ipAddr(a.IP[:]), a.PrefixLen, a.ZoneID)
+}
+
+type addrPack struct {
+	af int
+	as []Addr
+}
+
+func addrPacks() ([]addrPack, error) {
+	var aps []addrPack
+	for _, af := range [...]int{sysAF_UNSPEC, sysAF_INET, sysAF_INET6} {
+		as, err := Addrs(af, "")
+		if err != nil {
+			return nil, err
+		}
+		aps = append(aps, addrPack{af: af, as: as})
+	}
+	return aps, nil
+}
+
+func TestAddrs(t *testing.T) {
+	aps, err := addrPacks()
+	if len(aps) == 0 && err != nil {
+		t.Fatal(err)
+	}
+	lps, err := linkPacks()
+	if len(lps) == 0 && err != nil {
+		t.Fatal(err)
+	}
+	for _, lp := range lps {
+		n := 0
+		for _, ll := range lp.lls {
+			as, err := Addrs(lp.af, ll.Name)
+			if err != nil {
+				t.Fatal(lp.af, ll.Name, err)
+			}
+			t.Logf("af=%s name=%s %v", addrFamily(lp.af), ll.Name, as)
+			n += len(as)
+		}
+		for _, ap := range aps {
+			if ap.af != lp.af {
+				continue
+			}
+			if n != len(ap.as) {
+				t.Errorf("af=%s got %d; want %d", addrFamily(lp.af), n, len(ap.as))
+				continue
+			}
+		}
+	}
+}
diff --git a/src/vendor/golang_org/x/net/lif/binary.go b/src/vendor/golang_org/x/net/lif/binary.go
new file mode 100644
index 0000000..aade9ea
--- /dev/null
+++ b/src/vendor/golang_org/x/net/lif/binary.go
@@ -0,0 +1,68 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build solaris
+
+package lif
+
+// This file contains duplicates of encoding/binary package.
+//
+// This package is supposed to be used by the net package of standard
+// library. Therefore the package set used in the package must be the
+// same as net package.
+
+var littleEndian binaryLittleEndian
+
+type binaryByteOrder interface {
+	Uint16([]byte) uint16
+	Uint32([]byte) uint32
+	Uint64([]byte) uint64
+	PutUint16([]byte, uint16)
+	PutUint32([]byte, uint32)
+	PutUint64([]byte, uint64)
+}
+
+type binaryLittleEndian struct{}
+
+func (binaryLittleEndian) Uint16(b []byte) uint16 {
+	_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
+	return uint16(b[0]) | uint16(b[1])<<8
+}
+
+func (binaryLittleEndian) PutUint16(b []byte, v uint16) {
+	_ = b[1] // early bounds check to guarantee safety of writes below
+	b[0] = byte(v)
+	b[1] = byte(v >> 8)
+}
+
+func (binaryLittleEndian) Uint32(b []byte) uint32 {
+	_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
+	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+}
+
+func (binaryLittleEndian) PutUint32(b []byte, v uint32) {
+	_ = b[3] // early bounds check to guarantee safety of writes below
+	b[0] = byte(v)
+	b[1] = byte(v >> 8)
+	b[2] = byte(v >> 16)
+	b[3] = byte(v >> 24)
+}
+
+func (binaryLittleEndian) Uint64(b []byte) uint64 {
+	_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
+	return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
+		uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
+}
+
+func (binaryLittleEndian) PutUint64(b []byte, v uint64) {
+	_ = b[7] // early bounds check to guarantee safety of writes below
+	b[0] = byte(v)
+	b[1] = byte(v >> 8)
+	b[2] = byte(v >> 16)
+	b[3] = byte(v >> 24)
+	b[4] = byte(v >> 32)
+	b[5] = byte(v >> 40)
+	b[6] = byte(v >> 48)
+	b[7] = byte(v >> 56)
+}
diff --git a/src/vendor/golang_org/x/net/lif/defs_solaris.go b/src/vendor/golang_org/x/net/lif/defs_solaris.go
new file mode 100644
index 0000000..8b84ba5
--- /dev/null
+++ b/src/vendor/golang_org/x/net/lif/defs_solaris.go
@@ -0,0 +1,90 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// +godefs map struct_in_addr [4]byte /* in_addr */
+// +godefs map struct_in6_addr [16]byte /* in6_addr */
+
+package lif
+
+/*
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+*/
+import "C"
+
+const (
+	sysAF_UNSPEC = C.AF_UNSPEC
+	sysAF_INET   = C.AF_INET
+	sysAF_INET6  = C.AF_INET6
+
+	sysSOCK_DGRAM = C.SOCK_DGRAM
+)
+
+type sockaddrStorage C.struct_sockaddr_storage
+
+const (
+	sysLIFC_NOXMIT          = C.LIFC_NOXMIT
+	sysLIFC_EXTERNAL_SOURCE = C.LIFC_EXTERNAL_SOURCE
+	sysLIFC_TEMPORARY       = C.LIFC_TEMPORARY
+	sysLIFC_ALLZONES        = C.LIFC_ALLZONES
+	sysLIFC_UNDER_IPMP      = C.LIFC_UNDER_IPMP
+	sysLIFC_ENABLED         = C.LIFC_ENABLED
+
+	sysSIOCGLIFADDR    = C.SIOCGLIFADDR
+	sysSIOCGLIFDSTADDR = C.SIOCGLIFDSTADDR
+	sysSIOCGLIFFLAGS   = C.SIOCGLIFFLAGS
+	sysSIOCGLIFMTU     = C.SIOCGLIFMTU
+	sysSIOCGLIFNETMASK = C.SIOCGLIFNETMASK
+	sysSIOCGLIFMETRIC  = C.SIOCGLIFMETRIC
+	sysSIOCGLIFNUM     = C.SIOCGLIFNUM
+	sysSIOCGLIFINDEX   = C.SIOCGLIFINDEX
+	sysSIOCGLIFSUBNET  = C.SIOCGLIFSUBNET
+	sysSIOCGLIFLNKINFO = C.SIOCGLIFLNKINFO
+	sysSIOCGLIFCONF    = C.SIOCGLIFCONF
+	sysSIOCGLIFHWADDR  = C.SIOCGLIFHWADDR
+)
+
+const (
+	sysIFF_UP          = C.IFF_UP
+	sysIFF_BROADCAST   = C.IFF_BROADCAST
+	sysIFF_DEBUG       = C.IFF_DEBUG
+	sysIFF_LOOPBACK    = C.IFF_LOOPBACK
+	sysIFF_POINTOPOINT = C.IFF_POINTOPOINT
+	sysIFF_NOTRAILERS  = C.IFF_NOTRAILERS
+	sysIFF_RUNNING     = C.IFF_RUNNING
+	sysIFF_NOARP       = C.IFF_NOARP
+	sysIFF_PROMISC     = C.IFF_PROMISC
+	sysIFF_ALLMULTI    = C.IFF_ALLMULTI
+	sysIFF_INTELLIGENT = C.IFF_INTELLIGENT
+	sysIFF_MULTICAST   = C.IFF_MULTICAST
+	sysIFF_MULTI_BCAST = C.IFF_MULTI_BCAST
+	sysIFF_UNNUMBERED  = C.IFF_UNNUMBERED
+	sysIFF_PRIVATE     = C.IFF_PRIVATE
+)
+
+const (
+	sizeofLifnum       = C.sizeof_struct_lifnum
+	sizeofLifreq       = C.sizeof_struct_lifreq
+	sizeofLifconf      = C.sizeof_struct_lifconf
+	sizeofLifIfinfoReq = C.sizeof_struct_lif_ifinfo_req
+)
+
+type sysLifnum C.struct_lifnum
+
+type lifreq C.struct_lifreq
+
+type lifconf C.struct_lifconf
+
+type lifIfinfoReq C.struct_lif_ifinfo_req
+
+const (
+	sysIFT_IPV4 = C.IFT_IPV4
+	sysIFT_IPV6 = C.IFT_IPV6
+	sysIFT_6TO4 = C.IFT_6TO4
+)
diff --git a/src/vendor/golang_org/x/net/lif/lif.go b/src/vendor/golang_org/x/net/lif/lif.go
new file mode 100644
index 0000000..6e81f81
--- /dev/null
+++ b/src/vendor/golang_org/x/net/lif/lif.go
@@ -0,0 +1,43 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build solaris
+
+// Package lif provides basic functions for the manipulation of
+// logical network interfaces and interface addresses on Solaris.
+//
+// The package supports Solaris 11 or above.
+package lif
+
+import "syscall"
+
+type endpoint struct {
+	af int
+	s  uintptr
+}
+
+func (ep *endpoint) close() error {
+	return syscall.Close(int(ep.s))
+}
+
+func newEndpoints(af int) ([]endpoint, error) {
+	var lastErr error
+	var eps []endpoint
+	afs := []int{sysAF_INET, sysAF_INET6}
+	if af != sysAF_UNSPEC {
+		afs = []int{af}
+	}
+	for _, af := range afs {
+		s, err := syscall.Socket(af, sysSOCK_DGRAM, 0)
+		if err != nil {
+			lastErr = err
+			continue
+		}
+		eps = append(eps, endpoint{af: af, s: uintptr(s)})
+	}
+	if len(eps) == 0 {
+		return nil, lastErr
+	}
+	return eps, nil
+}
diff --git a/src/vendor/golang_org/x/net/lif/link.go b/src/vendor/golang_org/x/net/lif/link.go
new file mode 100644
index 0000000..76fa6c6
--- /dev/null
+++ b/src/vendor/golang_org/x/net/lif/link.go
@@ -0,0 +1,122 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build solaris
+
+package lif
+
+import "unsafe"
+
+// A Link represents logical data link information.
+//
+// It also represents base information for logical network interface.
+// On Solaris, each logical network interface represents network layer
+// adjacency information and the interface has a only single network
+// address or address pair for tunneling. It's usual that multiple
+// logical network interfaces share the same logical data link.
+type Link struct {
+	Name  string // name, equivalent to IP interface name
+	Index int    // index, equivalent to IP interface index
+	Type  int    // type
+	Flags int    // flags
+	MTU   int    // maximum transmission unit, basically link MTU but may differ between IP address families
+	Addr  []byte // address
+}
+
+func (ll *Link) fetch(s uintptr) {
+	var lifr lifreq
+	for i := 0; i < len(ll.Name); i++ {
+		lifr.Name[i] = int8(ll.Name[i])
+	}
+	ioc := int64(sysSIOCGLIFINDEX)
+	if err := ioctl(s, uintptr(ioc), unsafe.Pointer(&lifr)); err == nil {
+		ll.Index = int(littleEndian.Uint32(lifr.Lifru[:4]))
+	}
+	ioc = int64(sysSIOCGLIFFLAGS)
+	if err := ioctl(s, uintptr(ioc), unsafe.Pointer(&lifr)); err == nil {
+		ll.Flags = int(littleEndian.Uint64(lifr.Lifru[:8]))
+	}
+	ioc = int64(sysSIOCGLIFMTU)
+	if err := ioctl(s, uintptr(ioc), unsafe.Pointer(&lifr)); err == nil {
+		ll.MTU = int(littleEndian.Uint32(lifr.Lifru[:4]))
+	}
+	switch ll.Type {
+	case sysIFT_IPV4, sysIFT_IPV6, sysIFT_6TO4:
+	default:
+		ioc = int64(sysSIOCGLIFHWADDR)
+		if err := ioctl(s, uintptr(ioc), unsafe.Pointer(&lifr)); err == nil {
+			ll.Addr, _ = parseLinkAddr(lifr.Lifru[4:])
+		}
+	}
+}
+
+// Links returns a list of logical data links.
+//
+// The provided af must be an address family and name must be a data
+// link name. The zero value of af or name means a wildcard.
+func Links(af int, name string) ([]Link, error) {
+	eps, err := newEndpoints(af)
+	if len(eps) == 0 {
+		return nil, err
+	}
+	defer func() {
+		for _, ep := range eps {
+			ep.close()
+		}
+	}()
+	return links(eps, name)
+}
+
+func links(eps []endpoint, name string) ([]Link, error) {
+	var lls []Link
+	lifn := sysLifnum{Flags: sysLIFC_NOXMIT | sysLIFC_TEMPORARY | sysLIFC_ALLZONES | sysLIFC_UNDER_IPMP}
+	lifc := lifconf{Flags: sysLIFC_NOXMIT | sysLIFC_TEMPORARY | sysLIFC_ALLZONES | sysLIFC_UNDER_IPMP}
+	for _, ep := range eps {
+		lifn.Family = uint16(ep.af)
+		ioc := int64(sysSIOCGLIFNUM)
+		if err := ioctl(ep.s, uintptr(ioc), unsafe.Pointer(&lifn)); err != nil {
+			continue
+		}
+		if lifn.Count == 0 {
+			continue
+		}
+		b := make([]byte, lifn.Count*sizeofLifreq)
+		lifc.Family = uint16(ep.af)
+		lifc.Len = lifn.Count * sizeofLifreq
+		littleEndian.PutUint64(lifc.Lifcu[:], uint64(uintptr(unsafe.Pointer(&b[0]))))
+		ioc = int64(sysSIOCGLIFCONF)
+		if err := ioctl(ep.s, uintptr(ioc), unsafe.Pointer(&lifc)); err != nil {
+			continue
+		}
+		nb := make([]byte, 32) // see LIFNAMSIZ in net/if.h
+		for i := 0; i < int(lifn.Count); i++ {
+			lifr := (*lifreq)(unsafe.Pointer(&b[i*sizeofLifreq]))
+			for i := 0; i < 32; i++ {
+				if lifr.Name[i] == 0 {
+					nb = nb[:i]
+					break
+				}
+				nb[i] = byte(lifr.Name[i])
+			}
+			llname := string(nb)
+			nb = nb[:32]
+			if isDupLink(lls, llname) || name != "" && name != llname {
+				continue
+			}
+			ll := Link{Name: llname, Type: int(lifr.Type)}
+			ll.fetch(ep.s)
+			lls = append(lls, ll)
+		}
+	}
+	return lls, nil
+}
+
+func isDupLink(lls []Link, name string) bool {
+	for _, ll := range lls {
+		if ll.Name == name {
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/vendor/golang_org/x/net/lif/link_test.go b/src/vendor/golang_org/x/net/lif/link_test.go
new file mode 100644
index 0000000..8fb2bf6
--- /dev/null
+++ b/src/vendor/golang_org/x/net/lif/link_test.go
@@ -0,0 +1,61 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build solaris
+
+package lif
+
+import (
+	"fmt"
+	"testing"
+)
+
+func (ll *Link) String() string {
+	return fmt.Sprintf("name=%s index=%d type=%d flags=%#x mtu=%d addr=%v", ll.Name, ll.Index, ll.Type, ll.Flags, ll.MTU, llAddr(ll.Addr))
+}
+
+type linkPack struct {
+	af  int
+	lls []Link
+}
+
+func linkPacks() ([]linkPack, error) {
+	var lps []linkPack
+	for _, af := range [...]int{sysAF_UNSPEC, sysAF_INET, sysAF_INET6} {
+		lls, err := Links(af, "")
+		if err != nil {
+			return nil, err
+		}
+		lps = append(lps, linkPack{af: af, lls: lls})
+	}
+	return lps, nil
+}
+
+func TestLinks(t *testing.T) {
+	lps, err := linkPacks()
+	if len(lps) == 0 && err != nil {
+		t.Fatal(err)
+	}
+	for _, lp := range lps {
+		n := 0
+		for _, sll := range lp.lls {
+			lls, err := Links(lp.af, sll.Name)
+			if err != nil {
+				t.Fatal(lp.af, sll.Name, err)
+			}
+			for _, ll := range lls {
+				if ll.Name != sll.Name || ll.Index != sll.Index {
+					t.Errorf("af=%s got %v; want %v", addrFamily(lp.af), &ll, &sll)
+					continue
+				}
+				t.Logf("af=%s name=%s %v", addrFamily(lp.af), sll.Name, &ll)
+				n++
+			}
+		}
+		if n != len(lp.lls) {
+			t.Errorf("af=%s got %d; want %d", addrFamily(lp.af), n, len(lp.lls))
+			continue
+		}
+	}
+}
diff --git a/src/vendor/golang_org/x/net/lif/sys_solaris_amd64.s b/src/vendor/golang_org/x/net/lif/sys_solaris_amd64.s
new file mode 100644
index 0000000..1ebca37
--- /dev/null
+++ b/src/vendor/golang_org/x/net/lif/sys_solaris_amd64.s
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+TEXT ·sysvicall6(SB),NOSPLIT,$0-88
+	JMP	syscall·sysvicall6(SB)
+
+TEXT ·keepAlive(SB),NOSPLIT,$0
+	RET
diff --git a/src/vendor/golang_org/x/net/lif/syscall.go b/src/vendor/golang_org/x/net/lif/syscall.go
new file mode 100644
index 0000000..5fe0736
--- /dev/null
+++ b/src/vendor/golang_org/x/net/lif/syscall.go
@@ -0,0 +1,33 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build solaris
+
+package lif
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+//go:cgo_import_dynamic libc_ioctl ioctl "libc.so"
+
+//go:linkname procIoctl libc_ioctl
+
+var procIoctl uintptr
+
+func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (uintptr, uintptr, syscall.Errno)
+
+// TODO: replace with runtime.KeepAlive when available
+//go:noescape
+func keepAlive(p unsafe.Pointer)
+
+func ioctl(s, ioc uintptr, arg unsafe.Pointer) error {
+	_, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procIoctl)), 3, s, ioc, uintptr(arg), 0, 0, 0)
+	keepAlive(arg)
+	if errno != 0 {
+		return error(errno)
+	}
+	return nil
+}
diff --git a/src/vendor/golang_org/x/net/lif/zsys_solaris_amd64.go b/src/vendor/golang_org/x/net/lif/zsys_solaris_amd64.go
new file mode 100644
index 0000000..94231c4
--- /dev/null
+++ b/src/vendor/golang_org/x/net/lif/zsys_solaris_amd64.go
@@ -0,0 +1,103 @@
+// Created by cgo -godefs - DO NOT EDIT
+// cgo -godefs defs_solaris.go
+
+package lif
+
+const (
+	sysAF_UNSPEC = 0x0
+	sysAF_INET   = 0x2
+	sysAF_INET6  = 0x1a
+
+	sysSOCK_DGRAM = 0x1
+)
+
+type sockaddrStorage struct {
+	Family     uint16
+	X_ss_pad1  [6]int8
+	X_ss_align float64
+	X_ss_pad2  [240]int8
+}
+
+const (
+	sysLIFC_NOXMIT          = 0x1
+	sysLIFC_EXTERNAL_SOURCE = 0x2
+	sysLIFC_TEMPORARY       = 0x4
+	sysLIFC_ALLZONES        = 0x8
+	sysLIFC_UNDER_IPMP      = 0x10
+	sysLIFC_ENABLED         = 0x20
+
+	sysSIOCGLIFADDR    = -0x3f87968f
+	sysSIOCGLIFDSTADDR = -0x3f87968d
+	sysSIOCGLIFFLAGS   = -0x3f87968b
+	sysSIOCGLIFMTU     = -0x3f879686
+	sysSIOCGLIFNETMASK = -0x3f879683
+	sysSIOCGLIFMETRIC  = -0x3f879681
+	sysSIOCGLIFNUM     = -0x3ff3967e
+	sysSIOCGLIFINDEX   = -0x3f87967b
+	sysSIOCGLIFSUBNET  = -0x3f879676
+	sysSIOCGLIFLNKINFO = -0x3f879674
+	sysSIOCGLIFCONF    = -0x3fef965b
+	sysSIOCGLIFHWADDR  = -0x3f879640
+)
+
+const (
+	sysIFF_UP          = 0x1
+	sysIFF_BROADCAST   = 0x2
+	sysIFF_DEBUG       = 0x4
+	sysIFF_LOOPBACK    = 0x8
+	sysIFF_POINTOPOINT = 0x10
+	sysIFF_NOTRAILERS  = 0x20
+	sysIFF_RUNNING     = 0x40
+	sysIFF_NOARP       = 0x80
+	sysIFF_PROMISC     = 0x100
+	sysIFF_ALLMULTI    = 0x200
+	sysIFF_INTELLIGENT = 0x400
+	sysIFF_MULTICAST   = 0x800
+	sysIFF_MULTI_BCAST = 0x1000
+	sysIFF_UNNUMBERED  = 0x2000
+	sysIFF_PRIVATE     = 0x8000
+)
+
+const (
+	sizeofLifnum       = 0xc
+	sizeofLifreq       = 0x178
+	sizeofLifconf      = 0x18
+	sizeofLifIfinfoReq = 0x10
+)
+
+type sysLifnum struct {
+	Family    uint16
+	Pad_cgo_0 [2]byte
+	Flags     int32
+	Count     int32
+}
+
+type lifreq struct {
+	Name   [32]int8
+	Lifru1 [4]byte
+	Type   uint32
+	Lifru  [336]byte
+}
+
+type lifconf struct {
+	Family    uint16
+	Pad_cgo_0 [2]byte
+	Flags     int32
+	Len       int32
+	Pad_cgo_1 [4]byte
+	Lifcu     [8]byte
+}
+
+type lifIfinfoReq struct {
+	Maxhops      uint8
+	Pad_cgo_0    [3]byte
+	Reachtime    uint32
+	Reachretrans uint32
+	Maxmtu       uint32
+}
+
+const (
+	sysIFT_IPV4 = 0xc8
+	sysIFT_IPV6 = 0xc9
+	sysIFT_6TO4 = 0xca
+)
diff --git a/src/vendor/golang_org/x/net/route/address.go b/src/vendor/golang_org/x/net/route/address.go
index 206a837..a56909c 100644
--- a/src/vendor/golang_org/x/net/route/address.go
+++ b/src/vendor/golang_org/x/net/route/address.go
@@ -234,7 +234,11 @@ func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) (
 					return nil, err
 				}
 				as[i] = a
-				b = b[roundup(int(b[0])):]
+				l := roundup(int(b[0]))
+				if len(b) < l {
+					return nil, errMessageTooShort
+				}
+				b = b[l:]
 			case sysAF_INET, sysAF_INET6:
 				af = int(b[1])
 				a, err := parseInetAddr(af, b)
@@ -242,7 +246,11 @@ func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) (
 					return nil, err
 				}
 				as[i] = a
-				b = b[roundup(int(b[0])):]
+				l := roundup(int(b[0]))
+				if len(b) < l {
+					return nil, errMessageTooShort
+				}
+				b = b[l:]
 			default:
 				l, a, err := fn(af, b)
 				if err != nil {
@@ -262,7 +270,11 @@ func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) (
 				return nil, err
 			}
 			as[i] = a
-			b = b[roundup(int(b[0])):]
+			l := roundup(int(b[0]))
+			if len(b) < l {
+				return nil, errMessageTooShort
+			}
+			b = b[l:]
 		}
 	}
 	return as[:], nil
diff --git a/src/vendor/golang_org/x/net/route/interface_freebsd.go b/src/vendor/golang_org/x/net/route/interface_freebsd.go
index c830539..9f6f50c 100644
--- a/src/vendor/golang_org/x/net/route/interface_freebsd.go
+++ b/src/vendor/golang_org/x/net/route/interface_freebsd.go
@@ -13,12 +13,12 @@ func (w *wireFormat) parseInterfaceMessage(typ RIBType, b []byte) (Message, erro
 		extOff = int(nativeEndian.Uint16(b[18:20]))
 		bodyOff = int(nativeEndian.Uint16(b[16:18]))
 	} else {
-		if len(b) < w.bodyOff {
-			return nil, errMessageTooShort
-		}
 		extOff = w.extOff
 		bodyOff = w.bodyOff
 	}
+	if len(b) < extOff || len(b) < bodyOff {
+		return nil, errInvalidMessage
+	}
 	l := int(nativeEndian.Uint16(b[:2]))
 	if len(b) < l {
 		return nil, errInvalidMessage
@@ -53,11 +53,11 @@ func (w *wireFormat) parseInterfaceAddrMessage(typ RIBType, b []byte) (Message,
 		}
 		bodyOff = int(nativeEndian.Uint16(b[16:18]))
 	} else {
-		if len(b) < w.bodyOff {
-			return nil, errMessageTooShort
-		}
 		bodyOff = w.bodyOff
 	}
+	if len(b) < bodyOff {
+		return nil, errInvalidMessage
+	}
 	l := int(nativeEndian.Uint16(b[:2]))
 	if len(b) < l {
 		return nil, errInvalidMessage
diff --git a/src/vendor/golang_org/x/net/route/interface_openbsd.go b/src/vendor/golang_org/x/net/route/interface_openbsd.go
index 24451d8..e4a143c 100644
--- a/src/vendor/golang_org/x/net/route/interface_openbsd.go
+++ b/src/vendor/golang_org/x/net/route/interface_openbsd.go
@@ -24,7 +24,11 @@ func (*wireFormat) parseInterfaceMessage(_ RIBType, b []byte) (Message, error) {
 		Addrs:   make([]Addr, sysRTAX_MAX),
 		raw:     b[:l],
 	}
-	a, err := parseLinkAddr(b[int(nativeEndian.Uint16(b[4:6])):])
+	ll := int(nativeEndian.Uint16(b[4:6]))
+	if len(b) < ll {
+		return nil, errInvalidMessage
+	}
+	a, err := parseLinkAddr(b[ll:])
 	if err != nil {
 		return nil, err
 	}
@@ -42,6 +46,9 @@ func (*wireFormat) parseInterfaceAddrMessage(_ RIBType, b []byte) (Message, erro
 		return nil, errInvalidMessage
 	}
 	bodyOff := int(nativeEndian.Uint16(b[4:6]))
+	if len(b) < bodyOff {
+		return nil, errInvalidMessage
+	}
 	m := &InterfaceAddrMessage{
 		Version: int(b[2]),
 		Type:    int(b[3]),
diff --git a/src/vendor/golang_org/x/net/route/message.go b/src/vendor/golang_org/x/net/route/message.go
index 27cbf6b..d7ae0eb 100644
--- a/src/vendor/golang_org/x/net/route/message.go
+++ b/src/vendor/golang_org/x/net/route/message.go
@@ -42,6 +42,12 @@ func ParseRIB(typ RIBType, b []byte) ([]Message, error) {
 	for len(b) > 4 {
 		nmsgs++
 		l := int(nativeEndian.Uint16(b[:2]))
+		if l == 0 {
+			return nil, errInvalidMessage
+		}
+		if len(b) < l {
+			return nil, errMessageTooShort
+		}
 		if b[2] != sysRTM_VERSION {
 			b = b[l:]
 			continue
diff --git a/src/vendor/golang_org/x/net/route/message_test.go b/src/vendor/golang_org/x/net/route/message_test.go
index a1263d8..c0c7c57 100644
--- a/src/vendor/golang_org/x/net/route/message_test.go
+++ b/src/vendor/golang_org/x/net/route/message_test.go
@@ -93,3 +93,26 @@ func TestMonitorAndParseRIB(t *testing.T) {
 		time.Sleep(200 * time.Millisecond)
 	}
 }
+
+func TestParseRIBWithFuzz(t *testing.T) {
+	for _, fuzz := range []string{
+		"0\x00\x05\x050000000000000000" +
+			"00000000000000000000" +
+			"00000000000000000000" +
+			"00000000000000000000" +
+			"0000000000000\x02000000" +
+			"00000000",
+		"\x02\x00\x05\f0000000000000000" +
+			"0\x0200000000000000",
+		"\x02\x00\x05\x100000000000000\x1200" +
+			"0\x00\xff\x00",
+		"\x02\x00\x05\f0000000000000000" +
+			"0\x12000\x00\x02\x0000",
+		"\x00\x00\x00\x01\x00",
+		"00000",
+	} {
+		for typ := RIBType(0); typ < 256; typ++ {
+			ParseRIB(typ, []byte(fuzz))
+		}
+	}
+}
diff --git a/src/vendor/golang_org/x/net/route/route_openbsd.go b/src/vendor/golang_org/x/net/route/route_openbsd.go
index b07862f..76eae40 100644
--- a/src/vendor/golang_org/x/net/route/route_openbsd.go
+++ b/src/vendor/golang_org/x/net/route/route_openbsd.go
@@ -19,7 +19,11 @@ func (*wireFormat) parseRouteMessage(_ RIBType, b []byte) (Message, error) {
 		Index:   int(nativeEndian.Uint16(b[6:8])),
 		raw:     b[:l],
 	}
-	as, err := parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[int(nativeEndian.Uint16(b[4:6])):])
+	ll := int(nativeEndian.Uint16(b[4:6]))
+	if len(b) < ll {
+		return nil, errInvalidMessage
+	}
+	as, err := parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[ll:])
 	if err != nil {
 		return nil, err
 	}
diff --git a/src/vendor/golang_org/x/net/route/route_test.go b/src/vendor/golang_org/x/net/route/route_test.go
index 99f57b7..63fd8c5 100644
--- a/src/vendor/golang_org/x/net/route/route_test.go
+++ b/src/vendor/golang_org/x/net/route/route_test.go
@@ -235,7 +235,7 @@ func (a *LinkAddr) String() string {
 	return fmt.Sprintf("(%v %d %s %s)", addrFamily(a.Family()), a.Index, name, lla)
 }
 
-func (a Inet4Addr) String() string {
+func (a *Inet4Addr) String() string {
 	return fmt.Sprintf("(%v %v)", addrFamily(a.Family()), ipAddr(a.IP[:]))
 }
 
@@ -325,6 +325,7 @@ func fetchAndParseRIB(af int, typ RIBType) ([]Message, error) {
 	return ms, nil
 }
 
+// propVirtual is a proprietary virtual network interface.
 type propVirtual struct {
 	name         string
 	addr, mask   string
@@ -332,18 +333,18 @@ type propVirtual struct {
 	teardownCmds []*exec.Cmd
 }
 
-func (ti *propVirtual) setup() error {
-	for _, cmd := range ti.setupCmds {
+func (pv *propVirtual) setup() error {
+	for _, cmd := range pv.setupCmds {
 		if err := cmd.Run(); err != nil {
-			ti.teardown()
+			pv.teardown()
 			return err
 		}
 	}
 	return nil
 }
 
-func (ti *propVirtual) teardown() error {
-	for _, cmd := range ti.teardownCmds {
+func (pv *propVirtual) teardown() error {
+	for _, cmd := range pv.teardownCmds {
 		if err := cmd.Run(); err != nil {
 			return err
 		}
@@ -351,35 +352,35 @@ func (ti *propVirtual) teardown() error {
 	return nil
 }
 
-func (ti *propVirtual) configure(suffix int) error {
+func (pv *propVirtual) configure(suffix int) error {
 	if runtime.GOOS == "openbsd" {
-		ti.name = fmt.Sprintf("vether%d", suffix)
+		pv.name = fmt.Sprintf("vether%d", suffix)
 	} else {
-		ti.name = fmt.Sprintf("vlan%d", suffix)
+		pv.name = fmt.Sprintf("vlan%d", suffix)
 	}
 	xname, err := exec.LookPath("ifconfig")
 	if err != nil {
 		return err
 	}
-	ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+	pv.setupCmds = append(pv.setupCmds, &exec.Cmd{
 		Path: xname,
-		Args: []string{"ifconfig", ti.name, "create"},
+		Args: []string{"ifconfig", pv.name, "create"},
 	})
 	if runtime.GOOS == "netbsd" {
 		// NetBSD requires an underlying dot1Q-capable network
 		// interface.
-		ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+		pv.setupCmds = append(pv.setupCmds, &exec.Cmd{
 			Path: xname,
-			Args: []string{"ifconfig", ti.name, "vlan", fmt.Sprintf("%d", suffix&0xfff), "vlanif", "wm0"},
+			Args: []string{"ifconfig", pv.name, "vlan", fmt.Sprintf("%d", suffix&0xfff), "vlanif", "wm0"},
 		})
 	}
-	ti.setupCmds = append(ti.setupCmds, &exec.Cmd{
+	pv.setupCmds = append(pv.setupCmds, &exec.Cmd{
 		Path: xname,
-		Args: []string{"ifconfig", ti.name, "inet", ti.addr, "netmask", ti.mask},
+		Args: []string{"ifconfig", pv.name, "inet", pv.addr, "netmask", pv.mask},
 	})
-	ti.teardownCmds = append(ti.teardownCmds, &exec.Cmd{
+	pv.teardownCmds = append(pv.teardownCmds, &exec.Cmd{
 		Path: xname,
-		Args: []string{"ifconfig", ti.name, "destroy"},
+		Args: []string{"ifconfig", pv.name, "destroy"},
 	})
 	return nil
 }
diff --git a/src/vendor/golang_org/x/text/transform/transform.go b/src/vendor/golang_org/x/text/transform/transform.go
new file mode 100644
index 0000000..fe47b9b
--- /dev/null
+++ b/src/vendor/golang_org/x/text/transform/transform.go
@@ -0,0 +1,705 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package transform provides reader and writer wrappers that transform the
+// bytes passing through as well as various transformations. Example
+// transformations provided by other packages include normalization and
+// conversion between character sets.
+package transform // import "golang.org/x/text/transform"
+
+import (
+	"bytes"
+	"errors"
+	"io"
+	"unicode/utf8"
+)
+
+var (
+	// ErrShortDst means that the destination buffer was too short to
+	// receive all of the transformed bytes.
+	ErrShortDst = errors.New("transform: short destination buffer")
+
+	// ErrShortSrc means that the source buffer has insufficient data to
+	// complete the transformation.
+	ErrShortSrc = errors.New("transform: short source buffer")
+
+	// ErrEndOfSpan means that the input and output (the transformed input)
+	// are not identical.
+	ErrEndOfSpan = errors.New("transform: input and output are not identical")
+
+	// errInconsistentByteCount means that Transform returned success (nil
+	// error) but also returned nSrc inconsistent with the src argument.
+	errInconsistentByteCount = errors.New("transform: inconsistent byte count returned")
+
+	// errShortInternal means that an internal buffer is not large enough
+	// to make progress and the Transform operation must be aborted.
+	errShortInternal = errors.New("transform: short internal buffer")
+)
+
+// Transformer transforms bytes.
+type Transformer interface {
+	// Transform writes to dst the transformed bytes read from src, and
+	// returns the number of dst bytes written and src bytes read. The
+	// atEOF argument tells whether src represents the last bytes of the
+	// input.
+	//
+	// Callers should always process the nDst bytes produced and account
+	// for the nSrc bytes consumed before considering the error err.
+	//
+	// A nil error means that all of the transformed bytes (whether freshly
+	// transformed from src or left over from previous Transform calls)
+	// were written to dst. A nil error can be returned regardless of
+	// whether atEOF is true. If err is nil then nSrc must equal len(src);
+	// the converse is not necessarily true.
+	//
+	// ErrShortDst means that dst was too short to receive all of the
+	// transformed bytes. ErrShortSrc means that src had insufficient data
+	// to complete the transformation. If both conditions apply, then
+	// either error may be returned. Other than the error conditions listed
+	// here, implementations are free to report other errors that arise.
+	Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error)
+
+	// Reset resets the state and allows a Transformer to be reused.
+	Reset()
+}
+
+// SpanningTransformer extends the Transformer interface with a Span method
+// that determines how much of the input already conforms to the Transformer.
+type SpanningTransformer interface {
+	Transformer
+
+	// Span returns a position in src such that transforming src[:n] results in
+	// identical output src[:n] for these bytes. It does not necessarily return
+	// the largest such n. The atEOF argument tells whether src represents the
+	// last bytes of the input.
+	//
+	// Callers should always account for the n bytes consumed before
+	// considering the error err.
+	//
+	// A nil error means that all input bytes are known to be identical to the
+	// output produced by the Transformer. A nil error can be be returned
+	// regardless of whether atEOF is true. If err is nil, then then n must
+	// equal len(src); the converse is not necessarily true.
+	//
+	// ErrEndOfSpan means that the Transformer output may differ from the
+	// input after n bytes. Note that n may be len(src), meaning that the output
+	// would contain additional bytes after otherwise identical output.
+	// ErrShortSrc means that src had insufficient data to determine whether the
+	// remaining bytes would change. Other than the error conditions listed
+	// here, implementations are free to report other errors that arise.
+	//
+	// Calling Span can modify the Transformer state as a side effect. In
+	// effect, it does the transformation just as calling Transform would, only
+	// without copying to a destination buffer and only up to a point it can
+	// determine the input and output bytes are the same. This is obviously more
+	// limited than calling Transform, but can be more efficient in terms of
+	// copying and allocating buffers. Calls to Span and Transform may be
+	// interleaved.
+	Span(src []byte, atEOF bool) (n int, err error)
+}
+
+// NopResetter can be embedded by implementations of Transformer to add a nop
+// Reset method.
+type NopResetter struct{}
+
+// Reset implements the Reset method of the Transformer interface.
+func (NopResetter) Reset() {}
+
+// Reader wraps another io.Reader by transforming the bytes read.
+type Reader struct {
+	r   io.Reader
+	t   Transformer
+	err error
+
+	// dst[dst0:dst1] contains bytes that have been transformed by t but
+	// not yet copied out via Read.
+	dst        []byte
+	dst0, dst1 int
+
+	// src[src0:src1] contains bytes that have been read from r but not
+	// yet transformed through t.
+	src        []byte
+	src0, src1 int
+
+	// transformComplete is whether the transformation is complete,
+	// regardless of whether or not it was successful.
+	transformComplete bool
+}
+
+const defaultBufSize = 4096
+
+// NewReader returns a new Reader that wraps r by transforming the bytes read
+// via t. It calls Reset on t.
+func NewReader(r io.Reader, t Transformer) *Reader {
+	t.Reset()
+	return &Reader{
+		r:   r,
+		t:   t,
+		dst: make([]byte, defaultBufSize),
+		src: make([]byte, defaultBufSize),
+	}
+}
+
+// Read implements the io.Reader interface.
+func (r *Reader) Read(p []byte) (int, error) {
+	n, err := 0, error(nil)
+	for {
+		// Copy out any transformed bytes and return the final error if we are done.
+		if r.dst0 != r.dst1 {
+			n = copy(p, r.dst[r.dst0:r.dst1])
+			r.dst0 += n
+			if r.dst0 == r.dst1 && r.transformComplete {
+				return n, r.err
+			}
+			return n, nil
+		} else if r.transformComplete {
+			return 0, r.err
+		}
+
+		// Try to transform some source bytes, or to flush the transformer if we
+		// are out of source bytes. We do this even if r.r.Read returned an error.
+		// As the io.Reader documentation says, "process the n > 0 bytes returned
+		// before considering the error".
+		if r.src0 != r.src1 || r.err != nil {
+			r.dst0 = 0
+			r.dst1, n, err = r.t.Transform(r.dst, r.src[r.src0:r.src1], r.err == io.EOF)
+			r.src0 += n
+
+			switch {
+			case err == nil:
+				if r.src0 != r.src1 {
+					r.err = errInconsistentByteCount
+				}
+				// The Transform call was successful; we are complete if we
+				// cannot read more bytes into src.
+				r.transformComplete = r.err != nil
+				continue
+			case err == ErrShortDst && (r.dst1 != 0 || n != 0):
+				// Make room in dst by copying out, and try again.
+				continue
+			case err == ErrShortSrc && r.src1-r.src0 != len(r.src) && r.err == nil:
+				// Read more bytes into src via the code below, and try again.
+			default:
+				r.transformComplete = true
+				// The reader error (r.err) takes precedence over the
+				// transformer error (err) unless r.err is nil or io.EOF.
+				if r.err == nil || r.err == io.EOF {
+					r.err = err
+				}
+				continue
+			}
+		}
+
+		// Move any untransformed source bytes to the start of the buffer
+		// and read more bytes.
+		if r.src0 != 0 {
+			r.src0, r.src1 = 0, copy(r.src, r.src[r.src0:r.src1])
+		}
+		n, r.err = r.r.Read(r.src[r.src1:])
+		r.src1 += n
+	}
+}
+
+// TODO: implement ReadByte (and ReadRune??).
+
+// Writer wraps another io.Writer by transforming the bytes read.
+// The user needs to call Close to flush unwritten bytes that may
+// be buffered.
+type Writer struct {
+	w   io.Writer
+	t   Transformer
+	dst []byte
+
+	// src[:n] contains bytes that have not yet passed through t.
+	src []byte
+	n   int
+}
+
+// NewWriter returns a new Writer that wraps w by transforming the bytes written
+// via t. It calls Reset on t.
+func NewWriter(w io.Writer, t Transformer) *Writer {
+	t.Reset()
+	return &Writer{
+		w:   w,
+		t:   t,
+		dst: make([]byte, defaultBufSize),
+		src: make([]byte, defaultBufSize),
+	}
+}
+
+// Write implements the io.Writer interface. If there are not enough
+// bytes available to complete a Transform, the bytes will be buffered
+// for the next write. Call Close to convert the remaining bytes.
+func (w *Writer) Write(data []byte) (n int, err error) {
+	src := data
+	if w.n > 0 {
+		// Append bytes from data to the last remainder.
+		// TODO: limit the amount copied on first try.
+		n = copy(w.src[w.n:], data)
+		w.n += n
+		src = w.src[:w.n]
+	}
+	for {
+		nDst, nSrc, err := w.t.Transform(w.dst, src, false)
+		if _, werr := w.w.Write(w.dst[:nDst]); werr != nil {
+			return n, werr
+		}
+		src = src[nSrc:]
+		if w.n == 0 {
+			n += nSrc
+		} else if len(src) <= n {
+			// Enough bytes from w.src have been consumed. We make src point
+			// to data instead to reduce the copying.
+			w.n = 0
+			n -= len(src)
+			src = data[n:]
+			if n < len(data) && (err == nil || err == ErrShortSrc) {
+				continue
+			}
+		}
+		switch err {
+		case ErrShortDst:
+			// This error is okay as long as we are making progress.
+			if nDst > 0 || nSrc > 0 {
+				continue
+			}
+		case ErrShortSrc:
+			if len(src) < len(w.src) {
+				m := copy(w.src, src)
+				// If w.n > 0, bytes from data were already copied to w.src and n
+				// was already set to the number of bytes consumed.
+				if w.n == 0 {
+					n += m
+				}
+				w.n = m
+				err = nil
+			} else if nDst > 0 || nSrc > 0 {
+				// Not enough buffer to store the remainder. Keep processing as
+				// long as there is progress. Without this case, transforms that
+				// require a lookahead larger than the buffer may result in an
+				// error. This is not something one may expect to be common in
+				// practice, but it may occur when buffers are set to small
+				// sizes during testing.
+				continue
+			}
+		case nil:
+			if w.n > 0 {
+				err = errInconsistentByteCount
+			}
+		}
+		return n, err
+	}
+}
+
+// Close implements the io.Closer interface.
+func (w *Writer) Close() error {
+	src := w.src[:w.n]
+	for {
+		nDst, nSrc, err := w.t.Transform(w.dst, src, true)
+		if _, werr := w.w.Write(w.dst[:nDst]); werr != nil {
+			return werr
+		}
+		if err != ErrShortDst {
+			return err
+		}
+		src = src[nSrc:]
+	}
+}
+
+type nop struct{ NopResetter }
+
+func (nop) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+	n := copy(dst, src)
+	if n < len(src) {
+		err = ErrShortDst
+	}
+	return n, n, err
+}
+
+func (nop) Span(src []byte, atEOF bool) (n int, err error) {
+	return len(src), nil
+}
+
+type discard struct{ NopResetter }
+
+func (discard) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+	return 0, len(src), nil
+}
+
+var (
+	// Discard is a Transformer for which all Transform calls succeed
+	// by consuming all bytes and writing nothing.
+	Discard Transformer = discard{}
+
+	// Nop is a SpanningTransformer that copies src to dst.
+	Nop SpanningTransformer = nop{}
+)
+
+// chain is a sequence of links. A chain with N Transformers has N+1 links and
+// N+1 buffers. Of those N+1 buffers, the first and last are the src and dst
+// buffers given to chain.Transform and the middle N-1 buffers are intermediate
+// buffers owned by the chain. The i'th link transforms bytes from the i'th
+// buffer chain.link[i].b at read offset chain.link[i].p to the i+1'th buffer
+// chain.link[i+1].b at write offset chain.link[i+1].n, for i in [0, N).
+type chain struct {
+	link []link
+	err  error
+	// errStart is the index at which the error occurred plus 1. Processing
+	// errStart at this level at the next call to Transform. As long as
+	// errStart > 0, chain will not consume any more source bytes.
+	errStart int
+}
+
+func (c *chain) fatalError(errIndex int, err error) {
+	if i := errIndex + 1; i > c.errStart {
+		c.errStart = i
+		c.err = err
+	}
+}
+
+type link struct {
+	t Transformer
+	// b[p:n] holds the bytes to be transformed by t.
+	b []byte
+	p int
+	n int
+}
+
+func (l *link) src() []byte {
+	return l.b[l.p:l.n]
+}
+
+func (l *link) dst() []byte {
+	return l.b[l.n:]
+}
+
+// Chain returns a Transformer that applies t in sequence.
+func Chain(t ...Transformer) Transformer {
+	if len(t) == 0 {
+		return nop{}
+	}
+	c := &chain{link: make([]link, len(t)+1)}
+	for i, tt := range t {
+		c.link[i].t = tt
+	}
+	// Allocate intermediate buffers.
+	b := make([][defaultBufSize]byte, len(t)-1)
+	for i := range b {
+		c.link[i+1].b = b[i][:]
+	}
+	return c
+}
+
+// Reset resets the state of Chain. It calls Reset on all the Transformers.
+func (c *chain) Reset() {
+	for i, l := range c.link {
+		if l.t != nil {
+			l.t.Reset()
+		}
+		c.link[i].p, c.link[i].n = 0, 0
+	}
+}
+
+// TODO: make chain use Span (is going to be fun to implement!)
+
+// Transform applies the transformers of c in sequence.
+func (c *chain) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+	// Set up src and dst in the chain.
+	srcL := &c.link[0]
+	dstL := &c.link[len(c.link)-1]
+	srcL.b, srcL.p, srcL.n = src, 0, len(src)
+	dstL.b, dstL.n = dst, 0
+	var lastFull, needProgress bool // for detecting progress
+
+	// i is the index of the next Transformer to apply, for i in [low, high].
+	// low is the lowest index for which c.link[low] may still produce bytes.
+	// high is the highest index for which c.link[high] has a Transformer.
+	// The error returned by Transform determines whether to increase or
+	// decrease i. We try to completely fill a buffer before converting it.
+	for low, i, high := c.errStart, c.errStart, len(c.link)-2; low <= i && i <= high; {
+		in, out := &c.link[i], &c.link[i+1]
+		nDst, nSrc, err0 := in.t.Transform(out.dst(), in.src(), atEOF && low == i)
+		out.n += nDst
+		in.p += nSrc
+		if i > 0 && in.p == in.n {
+			in.p, in.n = 0, 0
+		}
+		needProgress, lastFull = lastFull, false
+		switch err0 {
+		case ErrShortDst:
+			// Process the destination buffer next. Return if we are already
+			// at the high index.
+			if i == high {
+				return dstL.n, srcL.p, ErrShortDst
+			}
+			if out.n != 0 {
+				i++
+				// If the Transformer at the next index is not able to process any
+				// source bytes there is nothing that can be done to make progress
+				// and the bytes will remain unprocessed. lastFull is used to
+				// detect this and break out of the loop with a fatal error.
+				lastFull = true
+				continue
+			}
+			// The destination buffer was too small, but is completely empty.
+			// Return a fatal error as this transformation can never complete.
+			c.fatalError(i, errShortInternal)
+		case ErrShortSrc:
+			if i == 0 {
+				// Save ErrShortSrc in err. All other errors take precedence.
+				err = ErrShortSrc
+				break
+			}
+			// Source bytes were depleted before filling up the destination buffer.
+			// Verify we made some progress, move the remaining bytes to the errStart
+			// and try to get more source bytes.
+			if needProgress && nSrc == 0 || in.n-in.p == len(in.b) {
+				// There were not enough source bytes to proceed while the source
+				// buffer cannot hold any more bytes. Return a fatal error as this
+				// transformation can never complete.
+				c.fatalError(i, errShortInternal)
+				break
+			}
+			// in.b is an internal buffer and we can make progress.
+			in.p, in.n = 0, copy(in.b, in.src())
+			fallthrough
+		case nil:
+			// if i == low, we have depleted the bytes at index i or any lower levels.
+			// In that case we increase low and i. In all other cases we decrease i to
+			// fetch more bytes before proceeding to the next index.
+			if i > low {
+				i--
+				continue
+			}
+		default:
+			c.fatalError(i, err0)
+		}
+		// Exhausted level low or fatal error: increase low and continue
+		// to process the bytes accepted so far.
+		i++
+		low = i
+	}
+
+	// If c.errStart > 0, this means we found a fatal error.  We will clear
+	// all upstream buffers. At this point, no more progress can be made
+	// downstream, as Transform would have bailed while handling ErrShortDst.
+	if c.errStart > 0 {
+		for i := 1; i < c.errStart; i++ {
+			c.link[i].p, c.link[i].n = 0, 0
+		}
+		err, c.errStart, c.err = c.err, 0, nil
+	}
+	return dstL.n, srcL.p, err
+}
+
+// Deprecated: use runes.Remove instead.
+func RemoveFunc(f func(r rune) bool) Transformer {
+	return removeF(f)
+}
+
+type removeF func(r rune) bool
+
+func (removeF) Reset() {}
+
+// Transform implements the Transformer interface.
+func (t removeF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+	for r, sz := rune(0), 0; len(src) > 0; src = src[sz:] {
+
+		if r = rune(src[0]); r < utf8.RuneSelf {
+			sz = 1
+		} else {
+			r, sz = utf8.DecodeRune(src)
+
+			if sz == 1 {
+				// Invalid rune.
+				if !atEOF && !utf8.FullRune(src) {
+					err = ErrShortSrc
+					break
+				}
+				// We replace illegal bytes with RuneError. Not doing so might
+				// otherwise turn a sequence of invalid UTF-8 into valid UTF-8.
+				// The resulting byte sequence may subsequently contain runes
+				// for which t(r) is true that were passed unnoticed.
+				if !t(r) {
+					if nDst+3 > len(dst) {
+						err = ErrShortDst
+						break
+					}
+					nDst += copy(dst[nDst:], "\uFFFD")
+				}
+				nSrc++
+				continue
+			}
+		}
+
+		if !t(r) {
+			if nDst+sz > len(dst) {
+				err = ErrShortDst
+				break
+			}
+			nDst += copy(dst[nDst:], src[:sz])
+		}
+		nSrc += sz
+	}
+	return
+}
+
+// grow returns a new []byte that is longer than b, and copies the first n bytes
+// of b to the start of the new slice.
+func grow(b []byte, n int) []byte {
+	m := len(b)
+	if m <= 32 {
+		m = 64
+	} else if m <= 256 {
+		m *= 2
+	} else {
+		m += m >> 1
+	}
+	buf := make([]byte, m)
+	copy(buf, b[:n])
+	return buf
+}
+
+const initialBufSize = 128
+
+// String returns a string with the result of converting s[:n] using t, where
+// n <= len(s). If err == nil, n will be len(s). It calls Reset on t.
+func String(t Transformer, s string) (result string, n int, err error) {
+	t.Reset()
+	if s == "" {
+		// Fast path for the common case for empty input. Results in about a
+		// 86% reduction of running time for BenchmarkStringLowerEmpty.
+		if _, _, err := t.Transform(nil, nil, true); err == nil {
+			return "", 0, nil
+		}
+	}
+
+	// Allocate only once. Note that both dst and src escape when passed to
+	// Transform.
+	buf := [2 * initialBufSize]byte{}
+	dst := buf[:initialBufSize:initialBufSize]
+	src := buf[initialBufSize : 2*initialBufSize]
+
+	// The input string s is transformed in multiple chunks (starting with a
+	// chunk size of initialBufSize). nDst and nSrc are per-chunk (or
+	// per-Transform-call) indexes, pDst and pSrc are overall indexes.
+	nDst, nSrc := 0, 0
+	pDst, pSrc := 0, 0
+
+	// pPrefix is the length of a common prefix: the first pPrefix bytes of the
+	// result will equal the first pPrefix bytes of s. It is not guaranteed to
+	// be the largest such value, but if pPrefix, len(result) and len(s) are
+	// all equal after the final transform (i.e. calling Transform with atEOF
+	// being true returned nil error) then we don't need to allocate a new
+	// result string.
+	pPrefix := 0
+	for {
+		// Invariant: pDst == pPrefix && pSrc == pPrefix.
+
+		n := copy(src, s[pSrc:])
+		nDst, nSrc, err = t.Transform(dst, src[:n], pSrc+n == len(s))
+		pDst += nDst
+		pSrc += nSrc
+
+		// TODO:  let transformers implement an optional Spanner interface, akin
+		// to norm's QuickSpan. This would even allow us to avoid any allocation.
+		if !bytes.Equal(dst[:nDst], src[:nSrc]) {
+			break
+		}
+		pPrefix = pSrc
+		if err == ErrShortDst {
+			// A buffer can only be short if a transformer modifies its input.
+			break
+		} else if err == ErrShortSrc {
+			if nSrc == 0 {
+				// No progress was made.
+				break
+			}
+			// Equal so far and !atEOF, so continue checking.
+		} else if err != nil || pPrefix == len(s) {
+			return string(s[:pPrefix]), pPrefix, err
+		}
+	}
+	// Post-condition: pDst == pPrefix + nDst && pSrc == pPrefix + nSrc.
+
+	// We have transformed the first pSrc bytes of the input s to become pDst
+	// transformed bytes. Those transformed bytes are discontiguous: the first
+	// pPrefix of them equal s[:pPrefix] and the last nDst of them equal
+	// dst[:nDst]. We copy them around, into a new dst buffer if necessary, so
+	// that they become one contiguous slice: dst[:pDst].
+	if pPrefix != 0 {
+		newDst := dst
+		if pDst > len(newDst) {
+			newDst = make([]byte, len(s)+nDst-nSrc)
+		}
+		copy(newDst[pPrefix:pDst], dst[:nDst])
+		copy(newDst[:pPrefix], s[:pPrefix])
+		dst = newDst
+	}
+
+	// Prevent duplicate Transform calls with atEOF being true at the end of
+	// the input. Also return if we have an unrecoverable error.
+	if (err == nil && pSrc == len(s)) ||
+		(err != nil && err != ErrShortDst && err != ErrShortSrc) {
+		return string(dst[:pDst]), pSrc, err
+	}
+
+	// Transform the remaining input, growing dst and src buffers as necessary.
+	for {
+		n := copy(src, s[pSrc:])
+		nDst, nSrc, err := t.Transform(dst[pDst:], src[:n], pSrc+n == len(s))
+		pDst += nDst
+		pSrc += nSrc
+
+		// If we got ErrShortDst or ErrShortSrc, do not grow as long as we can
+		// make progress. This may avoid excessive allocations.
+		if err == ErrShortDst {
+			if nDst == 0 {
+				dst = grow(dst, pDst)
+			}
+		} else if err == ErrShortSrc {
+			if nSrc == 0 {
+				src = grow(src, 0)
+			}
+		} else if err != nil || pSrc == len(s) {
+			return string(dst[:pDst]), pSrc, err
+		}
+	}
+}
+
+// Bytes returns a new byte slice with the result of converting b[:n] using t,
+// where n <= len(b). If err == nil, n will be len(b). It calls Reset on t.
+func Bytes(t Transformer, b []byte) (result []byte, n int, err error) {
+	return doAppend(t, 0, make([]byte, len(b)), b)
+}
+
+// Append appends the result of converting src[:n] using t to dst, where
+// n <= len(src), If err == nil, n will be len(src). It calls Reset on t.
+func Append(t Transformer, dst, src []byte) (result []byte, n int, err error) {
+	if len(dst) == cap(dst) {
+		n := len(src) + len(dst) // It is okay for this to be 0.
+		b := make([]byte, n)
+		dst = b[:copy(b, dst)]
+	}
+	return doAppend(t, len(dst), dst[:cap(dst)], src)
+}
+
+func doAppend(t Transformer, pDst int, dst, src []byte) (result []byte, n int, err error) {
+	t.Reset()
+	pSrc := 0
+	for {
+		nDst, nSrc, err := t.Transform(dst[pDst:], src[pSrc:], true)
+		pDst += nDst
+		pSrc += nSrc
+		if err != ErrShortDst {
+			return dst[:pDst], pSrc, err
+		}
+
+		// Grow the destination buffer, but do not grow as long as we can make
+		// progress. This may avoid excessive allocations.
+		if nDst == 0 {
+			dst = grow(dst, pDst)
+		}
+	}
+}
diff --git a/src/vendor/golang_org/x/text/unicode/norm/composition.go b/src/vendor/golang_org/x/text/unicode/norm/composition.go
new file mode 100644
index 0000000..d17b278
--- /dev/null
+++ b/src/vendor/golang_org/x/text/unicode/norm/composition.go
@@ -0,0 +1,514 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import "unicode/utf8"
+
+const (
+	maxNonStarters = 30
+	// The maximum number of characters needed for a buffer is
+	// maxNonStarters + 1 for the starter + 1 for the GCJ
+	maxBufferSize    = maxNonStarters + 2
+	maxNFCExpansion  = 3  // NFC(0x1D160)
+	maxNFKCExpansion = 18 // NFKC(0xFDFA)
+
+	maxByteBufferSize = utf8.UTFMax * maxBufferSize // 128
+)
+
+// ssState is used for reporting the segment state after inserting a rune.
+// It is returned by streamSafe.next.
+type ssState int
+
+const (
+	// Indicates a rune was successfully added to the segment.
+	ssSuccess ssState = iota
+	// Indicates a rune starts a new segment and should not be added.
+	ssStarter
+	// Indicates a rune caused a segment overflow and a CGJ should be inserted.
+	ssOverflow
+)
+
+// streamSafe implements the policy of when a CGJ should be inserted.
+type streamSafe uint8
+
+// mkStreamSafe is a shorthand for declaring a streamSafe var and calling
+// first on it.
+func mkStreamSafe(p Properties) streamSafe {
+	return streamSafe(p.nTrailingNonStarters())
+}
+
+// first inserts the first rune of a segment.
+func (ss *streamSafe) first(p Properties) {
+	if *ss != 0 {
+		panic("!= 0")
+	}
+	*ss = streamSafe(p.nTrailingNonStarters())
+}
+
+// insert returns a ssState value to indicate whether a rune represented by p
+// can be inserted.
+func (ss *streamSafe) next(p Properties) ssState {
+	if *ss > maxNonStarters {
+		panic("streamSafe was not reset")
+	}
+	n := p.nLeadingNonStarters()
+	if *ss += streamSafe(n); *ss > maxNonStarters {
+		*ss = 0
+		return ssOverflow
+	}
+	// The Stream-Safe Text Processing prescribes that the counting can stop
+	// as soon as a starter is encountered. However, there are some starters,
+	// like Jamo V and T, that can combine with other runes, leaving their
+	// successive non-starters appended to the previous, possibly causing an
+	// overflow. We will therefore consider any rune with a non-zero nLead to
+	// be a non-starter. Note that it always hold that if nLead > 0 then
+	// nLead == nTrail.
+	if n == 0 {
+		*ss = 0
+		return ssStarter
+	}
+	return ssSuccess
+}
+
+// backwards is used for checking for overflow and segment starts
+// when traversing a string backwards. Users do not need to call first
+// for the first rune. The state of the streamSafe retains the count of
+// the non-starters loaded.
+func (ss *streamSafe) backwards(p Properties) ssState {
+	if *ss > maxNonStarters {
+		panic("streamSafe was not reset")
+	}
+	c := *ss + streamSafe(p.nTrailingNonStarters())
+	if c > maxNonStarters {
+		return ssOverflow
+	}
+	*ss = c
+	if p.nLeadingNonStarters() == 0 {
+		return ssStarter
+	}
+	return ssSuccess
+}
+
+func (ss streamSafe) isMax() bool {
+	return ss == maxNonStarters
+}
+
+// GraphemeJoiner is inserted after maxNonStarters non-starter runes.
+const GraphemeJoiner = "\u034F"
+
+// reorderBuffer is used to normalize a single segment.  Characters inserted with
+// insert are decomposed and reordered based on CCC. The compose method can
+// be used to recombine characters.  Note that the byte buffer does not hold
+// the UTF-8 characters in order.  Only the rune array is maintained in sorted
+// order. flush writes the resulting segment to a byte array.
+type reorderBuffer struct {
+	rune  [maxBufferSize]Properties // Per character info.
+	byte  [maxByteBufferSize]byte   // UTF-8 buffer. Referenced by runeInfo.pos.
+	nbyte uint8                     // Number or bytes.
+	ss    streamSafe                // For limiting length of non-starter sequence.
+	nrune int                       // Number of runeInfos.
+	f     formInfo
+
+	src      input
+	nsrc     int
+	tmpBytes input
+
+	out    []byte
+	flushF func(*reorderBuffer) bool
+}
+
+func (rb *reorderBuffer) init(f Form, src []byte) {
+	rb.f = *formTable[f]
+	rb.src.setBytes(src)
+	rb.nsrc = len(src)
+	rb.ss = 0
+}
+
+func (rb *reorderBuffer) initString(f Form, src string) {
+	rb.f = *formTable[f]
+	rb.src.setString(src)
+	rb.nsrc = len(src)
+	rb.ss = 0
+}
+
+func (rb *reorderBuffer) setFlusher(out []byte, f func(*reorderBuffer) bool) {
+	rb.out = out
+	rb.flushF = f
+}
+
+// reset discards all characters from the buffer.
+func (rb *reorderBuffer) reset() {
+	rb.nrune = 0
+	rb.nbyte = 0
+	rb.ss = 0
+}
+
+func (rb *reorderBuffer) doFlush() bool {
+	if rb.f.composing {
+		rb.compose()
+	}
+	res := rb.flushF(rb)
+	rb.reset()
+	return res
+}
+
+// appendFlush appends the normalized segment to rb.out.
+func appendFlush(rb *reorderBuffer) bool {
+	for i := 0; i < rb.nrune; i++ {
+		start := rb.rune[i].pos
+		end := start + rb.rune[i].size
+		rb.out = append(rb.out, rb.byte[start:end]...)
+	}
+	return true
+}
+
+// flush appends the normalized segment to out and resets rb.
+func (rb *reorderBuffer) flush(out []byte) []byte {
+	for i := 0; i < rb.nrune; i++ {
+		start := rb.rune[i].pos
+		end := start + rb.rune[i].size
+		out = append(out, rb.byte[start:end]...)
+	}
+	rb.reset()
+	return out
+}
+
+// flushCopy copies the normalized segment to buf and resets rb.
+// It returns the number of bytes written to buf.
+func (rb *reorderBuffer) flushCopy(buf []byte) int {
+	p := 0
+	for i := 0; i < rb.nrune; i++ {
+		runep := rb.rune[i]
+		p += copy(buf[p:], rb.byte[runep.pos:runep.pos+runep.size])
+	}
+	rb.reset()
+	return p
+}
+
+// insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class.
+// It returns false if the buffer is not large enough to hold the rune.
+// It is used internally by insert and insertString only.
+func (rb *reorderBuffer) insertOrdered(info Properties) {
+	n := rb.nrune
+	b := rb.rune[:]
+	cc := info.ccc
+	if cc > 0 {
+		// Find insertion position + move elements to make room.
+		for ; n > 0; n-- {
+			if b[n-1].ccc <= cc {
+				break
+			}
+			b[n] = b[n-1]
+		}
+	}
+	rb.nrune += 1
+	pos := uint8(rb.nbyte)
+	rb.nbyte += utf8.UTFMax
+	info.pos = pos
+	b[n] = info
+}
+
+// insertErr is an error code returned by insert. Using this type instead
+// of error improves performance up to 20% for many of the benchmarks.
+type insertErr int
+
+const (
+	iSuccess insertErr = -iota
+	iShortDst
+	iShortSrc
+)
+
+// insertFlush inserts the given rune in the buffer ordered by CCC.
+// If a decomposition with multiple segments are encountered, they leading
+// ones are flushed.
+// It returns a non-zero error code if the rune was not inserted.
+func (rb *reorderBuffer) insertFlush(src input, i int, info Properties) insertErr {
+	if rune := src.hangul(i); rune != 0 {
+		rb.decomposeHangul(rune)
+		return iSuccess
+	}
+	if info.hasDecomposition() {
+		return rb.insertDecomposed(info.Decomposition())
+	}
+	rb.insertSingle(src, i, info)
+	return iSuccess
+}
+
+// insertUnsafe inserts the given rune in the buffer ordered by CCC.
+// It is assumed there is sufficient space to hold the runes. It is the
+// responsibility of the caller to ensure this. This can be done by checking
+// the state returned by the streamSafe type.
+func (rb *reorderBuffer) insertUnsafe(src input, i int, info Properties) {
+	if rune := src.hangul(i); rune != 0 {
+		rb.decomposeHangul(rune)
+	}
+	if info.hasDecomposition() {
+		// TODO: inline.
+		rb.insertDecomposed(info.Decomposition())
+	} else {
+		rb.insertSingle(src, i, info)
+	}
+}
+
+// insertDecomposed inserts an entry in to the reorderBuffer for each rune
+// in dcomp. dcomp must be a sequence of decomposed UTF-8-encoded runes.
+// It flushes the buffer on each new segment start.
+func (rb *reorderBuffer) insertDecomposed(dcomp []byte) insertErr {
+	rb.tmpBytes.setBytes(dcomp)
+	for i := 0; i < len(dcomp); {
+		info := rb.f.info(rb.tmpBytes, i)
+		if info.BoundaryBefore() && rb.nrune > 0 && !rb.doFlush() {
+			return iShortDst
+		}
+		i += copy(rb.byte[rb.nbyte:], dcomp[i:i+int(info.size)])
+		rb.insertOrdered(info)
+	}
+	return iSuccess
+}
+
+// insertSingle inserts an entry in the reorderBuffer for the rune at
+// position i. info is the runeInfo for the rune at position i.
+func (rb *reorderBuffer) insertSingle(src input, i int, info Properties) {
+	src.copySlice(rb.byte[rb.nbyte:], i, i+int(info.size))
+	rb.insertOrdered(info)
+}
+
+// insertCGJ inserts a Combining Grapheme Joiner (0x034f) into rb.
+func (rb *reorderBuffer) insertCGJ() {
+	rb.insertSingle(input{str: GraphemeJoiner}, 0, Properties{size: uint8(len(GraphemeJoiner))})
+}
+
+// appendRune inserts a rune at the end of the buffer. It is used for Hangul.
+func (rb *reorderBuffer) appendRune(r rune) {
+	bn := rb.nbyte
+	sz := utf8.EncodeRune(rb.byte[bn:], rune(r))
+	rb.nbyte += utf8.UTFMax
+	rb.rune[rb.nrune] = Properties{pos: bn, size: uint8(sz)}
+	rb.nrune++
+}
+
+// assignRune sets a rune at position pos. It is used for Hangul and recomposition.
+func (rb *reorderBuffer) assignRune(pos int, r rune) {
+	bn := rb.rune[pos].pos
+	sz := utf8.EncodeRune(rb.byte[bn:], rune(r))
+	rb.rune[pos] = Properties{pos: bn, size: uint8(sz)}
+}
+
+// runeAt returns the rune at position n. It is used for Hangul and recomposition.
+func (rb *reorderBuffer) runeAt(n int) rune {
+	inf := rb.rune[n]
+	r, _ := utf8.DecodeRune(rb.byte[inf.pos : inf.pos+inf.size])
+	return r
+}
+
+// bytesAt returns the UTF-8 encoding of the rune at position n.
+// It is used for Hangul and recomposition.
+func (rb *reorderBuffer) bytesAt(n int) []byte {
+	inf := rb.rune[n]
+	return rb.byte[inf.pos : int(inf.pos)+int(inf.size)]
+}
+
+// For Hangul we combine algorithmically, instead of using tables.
+const (
+	hangulBase  = 0xAC00 // UTF-8(hangulBase) -> EA B0 80
+	hangulBase0 = 0xEA
+	hangulBase1 = 0xB0
+	hangulBase2 = 0x80
+
+	hangulEnd  = hangulBase + jamoLVTCount // UTF-8(0xD7A4) -> ED 9E A4
+	hangulEnd0 = 0xED
+	hangulEnd1 = 0x9E
+	hangulEnd2 = 0xA4
+
+	jamoLBase  = 0x1100 // UTF-8(jamoLBase) -> E1 84 00
+	jamoLBase0 = 0xE1
+	jamoLBase1 = 0x84
+	jamoLEnd   = 0x1113
+	jamoVBase  = 0x1161
+	jamoVEnd   = 0x1176
+	jamoTBase  = 0x11A7
+	jamoTEnd   = 0x11C3
+
+	jamoTCount   = 28
+	jamoVCount   = 21
+	jamoVTCount  = 21 * 28
+	jamoLVTCount = 19 * 21 * 28
+)
+
+const hangulUTF8Size = 3
+
+func isHangul(b []byte) bool {
+	if len(b) < hangulUTF8Size {
+		return false
+	}
+	b0 := b[0]
+	if b0 < hangulBase0 {
+		return false
+	}
+	b1 := b[1]
+	switch {
+	case b0 == hangulBase0:
+		return b1 >= hangulBase1
+	case b0 < hangulEnd0:
+		return true
+	case b0 > hangulEnd0:
+		return false
+	case b1 < hangulEnd1:
+		return true
+	}
+	return b1 == hangulEnd1 && b[2] < hangulEnd2
+}
+
+func isHangulString(b string) bool {
+	if len(b) < hangulUTF8Size {
+		return false
+	}
+	b0 := b[0]
+	if b0 < hangulBase0 {
+		return false
+	}
+	b1 := b[1]
+	switch {
+	case b0 == hangulBase0:
+		return b1 >= hangulBase1
+	case b0 < hangulEnd0:
+		return true
+	case b0 > hangulEnd0:
+		return false
+	case b1 < hangulEnd1:
+		return true
+	}
+	return b1 == hangulEnd1 && b[2] < hangulEnd2
+}
+
+// Caller must ensure len(b) >= 2.
+func isJamoVT(b []byte) bool {
+	// True if (rune & 0xff00) == jamoLBase
+	return b[0] == jamoLBase0 && (b[1]&0xFC) == jamoLBase1
+}
+
+func isHangulWithoutJamoT(b []byte) bool {
+	c, _ := utf8.DecodeRune(b)
+	c -= hangulBase
+	return c < jamoLVTCount && c%jamoTCount == 0
+}
+
+// decomposeHangul writes the decomposed Hangul to buf and returns the number
+// of bytes written.  len(buf) should be at least 9.
+func decomposeHangul(buf []byte, r rune) int {
+	const JamoUTF8Len = 3
+	r -= hangulBase
+	x := r % jamoTCount
+	r /= jamoTCount
+	utf8.EncodeRune(buf, jamoLBase+r/jamoVCount)
+	utf8.EncodeRune(buf[JamoUTF8Len:], jamoVBase+r%jamoVCount)
+	if x != 0 {
+		utf8.EncodeRune(buf[2*JamoUTF8Len:], jamoTBase+x)
+		return 3 * JamoUTF8Len
+	}
+	return 2 * JamoUTF8Len
+}
+
+// decomposeHangul algorithmically decomposes a Hangul rune into
+// its Jamo components.
+// See http://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul.
+func (rb *reorderBuffer) decomposeHangul(r rune) {
+	r -= hangulBase
+	x := r % jamoTCount
+	r /= jamoTCount
+	rb.appendRune(jamoLBase + r/jamoVCount)
+	rb.appendRune(jamoVBase + r%jamoVCount)
+	if x != 0 {
+		rb.appendRune(jamoTBase + x)
+	}
+}
+
+// combineHangul algorithmically combines Jamo character components into Hangul.
+// See http://unicode.org/reports/tr15/#Hangul for details on combining Hangul.
+func (rb *reorderBuffer) combineHangul(s, i, k int) {
+	b := rb.rune[:]
+	bn := rb.nrune
+	for ; i < bn; i++ {
+		cccB := b[k-1].ccc
+		cccC := b[i].ccc
+		if cccB == 0 {
+			s = k - 1
+		}
+		if s != k-1 && cccB >= cccC {
+			// b[i] is blocked by greater-equal cccX below it
+			b[k] = b[i]
+			k++
+		} else {
+			l := rb.runeAt(s) // also used to compare to hangulBase
+			v := rb.runeAt(i) // also used to compare to jamoT
+			switch {
+			case jamoLBase <= l && l < jamoLEnd &&
+				jamoVBase <= v && v < jamoVEnd:
+				// 11xx plus 116x to LV
+				rb.assignRune(s, hangulBase+
+					(l-jamoLBase)*jamoVTCount+(v-jamoVBase)*jamoTCount)
+			case hangulBase <= l && l < hangulEnd &&
+				jamoTBase < v && v < jamoTEnd &&
+				((l-hangulBase)%jamoTCount) == 0:
+				// ACxx plus 11Ax to LVT
+				rb.assignRune(s, l+v-jamoTBase)
+			default:
+				b[k] = b[i]
+				k++
+			}
+		}
+	}
+	rb.nrune = k
+}
+
+// compose recombines the runes in the buffer.
+// It should only be used to recompose a single segment, as it will not
+// handle alternations between Hangul and non-Hangul characters correctly.
+func (rb *reorderBuffer) compose() {
+	// UAX #15, section X5 , including Corrigendum #5
+	// "In any character sequence beginning with starter S, a character C is
+	//  blocked from S if and only if there is some character B between S
+	//  and C, and either B is a starter or it has the same or higher
+	//  combining class as C."
+	bn := rb.nrune
+	if bn == 0 {
+		return
+	}
+	k := 1
+	b := rb.rune[:]
+	for s, i := 0, 1; i < bn; i++ {
+		if isJamoVT(rb.bytesAt(i)) {
+			// Redo from start in Hangul mode. Necessary to support
+			// U+320E..U+321E in NFKC mode.
+			rb.combineHangul(s, i, k)
+			return
+		}
+		ii := b[i]
+		// We can only use combineForward as a filter if we later
+		// get the info for the combined character. This is more
+		// expensive than using the filter. Using combinesBackward()
+		// is safe.
+		if ii.combinesBackward() {
+			cccB := b[k-1].ccc
+			cccC := ii.ccc
+			blocked := false // b[i] blocked by starter or greater or equal CCC?
+			if cccB == 0 {
+				s = k - 1
+			} else {
+				blocked = s != k-1 && cccB >= cccC
+			}
+			if !blocked {
+				combined := combine(rb.runeAt(s), rb.runeAt(i))
+				if combined != 0 {
+					rb.assignRune(s, combined)
+					continue
+				}
+			}
+		}
+		b[k] = b[i]
+		k++
+	}
+	rb.nrune = k
+}
diff --git a/src/vendor/golang_org/x/text/unicode/norm/forminfo.go b/src/vendor/golang_org/x/text/unicode/norm/forminfo.go
new file mode 100644
index 0000000..15a67c6
--- /dev/null
+++ b/src/vendor/golang_org/x/text/unicode/norm/forminfo.go
@@ -0,0 +1,256 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+// This file contains Form-specific logic and wrappers for data in tables.go.
+
+// Rune info is stored in a separate trie per composing form. A composing form
+// and its corresponding decomposing form share the same trie.  Each trie maps
+// a rune to a uint16. The values take two forms.  For v >= 0x8000:
+//   bits
+//   15:    1 (inverse of NFD_QD bit of qcInfo)
+//   13..7: qcInfo (see below). isYesD is always true (no decompostion).
+//    6..0: ccc (compressed CCC value).
+// For v < 0x8000, the respective rune has a decomposition and v is an index
+// into a byte array of UTF-8 decomposition sequences and additional info and
+// has the form:
+//    <header> <decomp_byte>* [<tccc> [<lccc>]]
+// The header contains the number of bytes in the decomposition (excluding this
+// length byte). The two most significant bits of this length byte correspond
+// to bit 5 and 4 of qcInfo (see below).  The byte sequence itself starts at v+1.
+// The byte sequence is followed by a trailing and leading CCC if the values
+// for these are not zero.  The value of v determines which ccc are appended
+// to the sequences.  For v < firstCCC, there are none, for v >= firstCCC,
+// the sequence is followed by a trailing ccc, and for v >= firstLeadingCC
+// there is an additional leading ccc. The value of tccc itself is the
+// trailing CCC shifted left 2 bits. The two least-significant bits of tccc
+// are the number of trailing non-starters.
+
+const (
+	qcInfoMask      = 0x3F // to clear all but the relevant bits in a qcInfo
+	headerLenMask   = 0x3F // extract the length value from the header byte
+	headerFlagsMask = 0xC0 // extract the qcInfo bits from the header byte
+)
+
+// Properties provides access to normalization properties of a rune.
+type Properties struct {
+	pos   uint8  // start position in reorderBuffer; used in composition.go
+	size  uint8  // length of UTF-8 encoding of this rune
+	ccc   uint8  // leading canonical combining class (ccc if not decomposition)
+	tccc  uint8  // trailing canonical combining class (ccc if not decomposition)
+	nLead uint8  // number of leading non-starters.
+	flags qcInfo // quick check flags
+	index uint16
+}
+
+// functions dispatchable per form
+type lookupFunc func(b input, i int) Properties
+
+// formInfo holds Form-specific functions and tables.
+type formInfo struct {
+	form                     Form
+	composing, compatibility bool // form type
+	info                     lookupFunc
+	nextMain                 iterFunc
+}
+
+var formTable []*formInfo
+
+func init() {
+	formTable = make([]*formInfo, 4)
+
+	for i := range formTable {
+		f := &formInfo{}
+		formTable[i] = f
+		f.form = Form(i)
+		if Form(i) == NFKD || Form(i) == NFKC {
+			f.compatibility = true
+			f.info = lookupInfoNFKC
+		} else {
+			f.info = lookupInfoNFC
+		}
+		f.nextMain = nextDecomposed
+		if Form(i) == NFC || Form(i) == NFKC {
+			f.nextMain = nextComposed
+			f.composing = true
+		}
+	}
+}
+
+// We do not distinguish between boundaries for NFC, NFD, etc. to avoid
+// unexpected behavior for the user.  For example, in NFD, there is a boundary
+// after 'a'.  However, 'a' might combine with modifiers, so from the application's
+// perspective it is not a good boundary. We will therefore always use the
+// boundaries for the combining variants.
+
+// BoundaryBefore returns true if this rune starts a new segment and
+// cannot combine with any rune on the left.
+func (p Properties) BoundaryBefore() bool {
+	if p.ccc == 0 && !p.combinesBackward() {
+		return true
+	}
+	// We assume that the CCC of the first character in a decomposition
+	// is always non-zero if different from info.ccc and that we can return
+	// false at this point. This is verified by maketables.
+	return false
+}
+
+// BoundaryAfter returns true if runes cannot combine with or otherwise
+// interact with this or previous runes.
+func (p Properties) BoundaryAfter() bool {
+	// TODO: loosen these conditions.
+	return p.isInert()
+}
+
+// We pack quick check data in 4 bits:
+//   5:    Combines forward  (0 == false, 1 == true)
+//   4..3: NFC_QC Yes(00), No (10), or Maybe (11)
+//   2:    NFD_QC Yes (0) or No (1). No also means there is a decomposition.
+//   1..0: Number of trailing non-starters.
+//
+// When all 4 bits are zero, the character is inert, meaning it is never
+// influenced by normalization.
+type qcInfo uint8
+
+func (p Properties) isYesC() bool { return p.flags&0x10 == 0 }
+func (p Properties) isYesD() bool { return p.flags&0x4 == 0 }
+
+func (p Properties) combinesForward() bool  { return p.flags&0x20 != 0 }
+func (p Properties) combinesBackward() bool { return p.flags&0x8 != 0 } // == isMaybe
+func (p Properties) hasDecomposition() bool { return p.flags&0x4 != 0 } // == isNoD
+
+func (p Properties) isInert() bool {
+	return p.flags&qcInfoMask == 0 && p.ccc == 0
+}
+
+func (p Properties) multiSegment() bool {
+	return p.index >= firstMulti && p.index < endMulti
+}
+
+func (p Properties) nLeadingNonStarters() uint8 {
+	return p.nLead
+}
+
+func (p Properties) nTrailingNonStarters() uint8 {
+	return uint8(p.flags & 0x03)
+}
+
+// Decomposition returns the decomposition for the underlying rune
+// or nil if there is none.
+func (p Properties) Decomposition() []byte {
+	// TODO: create the decomposition for Hangul?
+	if p.index == 0 {
+		return nil
+	}
+	i := p.index
+	n := decomps[i] & headerLenMask
+	i++
+	return decomps[i : i+uint16(n)]
+}
+
+// Size returns the length of UTF-8 encoding of the rune.
+func (p Properties) Size() int {
+	return int(p.size)
+}
+
+// CCC returns the canonical combining class of the underlying rune.
+func (p Properties) CCC() uint8 {
+	if p.index >= firstCCCZeroExcept {
+		return 0
+	}
+	return ccc[p.ccc]
+}
+
+// LeadCCC returns the CCC of the first rune in the decomposition.
+// If there is no decomposition, LeadCCC equals CCC.
+func (p Properties) LeadCCC() uint8 {
+	return ccc[p.ccc]
+}
+
+// TrailCCC returns the CCC of the last rune in the decomposition.
+// If there is no decomposition, TrailCCC equals CCC.
+func (p Properties) TrailCCC() uint8 {
+	return ccc[p.tccc]
+}
+
+// Recomposition
+// We use 32-bit keys instead of 64-bit for the two codepoint keys.
+// This clips off the bits of three entries, but we know this will not
+// result in a collision. In the unlikely event that changes to
+// UnicodeData.txt introduce collisions, the compiler will catch it.
+// Note that the recomposition map for NFC and NFKC are identical.
+
+// combine returns the combined rune or 0 if it doesn't exist.
+func combine(a, b rune) rune {
+	key := uint32(uint16(a))<<16 + uint32(uint16(b))
+	return recompMap[key]
+}
+
+func lookupInfoNFC(b input, i int) Properties {
+	v, sz := b.charinfoNFC(i)
+	return compInfo(v, sz)
+}
+
+func lookupInfoNFKC(b input, i int) Properties {
+	v, sz := b.charinfoNFKC(i)
+	return compInfo(v, sz)
+}
+
+// Properties returns properties for the first rune in s.
+func (f Form) Properties(s []byte) Properties {
+	if f == NFC || f == NFD {
+		return compInfo(nfcData.lookup(s))
+	}
+	return compInfo(nfkcData.lookup(s))
+}
+
+// PropertiesString returns properties for the first rune in s.
+func (f Form) PropertiesString(s string) Properties {
+	if f == NFC || f == NFD {
+		return compInfo(nfcData.lookupString(s))
+	}
+	return compInfo(nfkcData.lookupString(s))
+}
+
+// compInfo converts the information contained in v and sz
+// to a Properties.  See the comment at the top of the file
+// for more information on the format.
+func compInfo(v uint16, sz int) Properties {
+	if v == 0 {
+		return Properties{size: uint8(sz)}
+	} else if v >= 0x8000 {
+		p := Properties{
+			size:  uint8(sz),
+			ccc:   uint8(v),
+			tccc:  uint8(v),
+			flags: qcInfo(v >> 8),
+		}
+		if p.ccc > 0 || p.combinesBackward() {
+			p.nLead = uint8(p.flags & 0x3)
+		}
+		return p
+	}
+	// has decomposition
+	h := decomps[v]
+	f := (qcInfo(h&headerFlagsMask) >> 2) | 0x4
+	p := Properties{size: uint8(sz), flags: f, index: v}
+	if v >= firstCCC {
+		v += uint16(h&headerLenMask) + 1
+		c := decomps[v]
+		p.tccc = c >> 2
+		p.flags |= qcInfo(c & 0x3)
+		if v >= firstLeadingCCC {
+			p.nLead = c & 0x3
+			if v >= firstStarterWithNLead {
+				// We were tricked. Remove the decomposition.
+				p.flags &= 0x03
+				p.index = 0
+				return p
+			}
+			p.ccc = decomps[v+1]
+		}
+	}
+	return p
+}
diff --git a/src/vendor/golang_org/x/text/unicode/norm/input.go b/src/vendor/golang_org/x/text/unicode/norm/input.go
new file mode 100644
index 0000000..045d4cc
--- /dev/null
+++ b/src/vendor/golang_org/x/text/unicode/norm/input.go
@@ -0,0 +1,105 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import "unicode/utf8"
+
+type input struct {
+	str   string
+	bytes []byte
+}
+
+func inputBytes(str []byte) input {
+	return input{bytes: str}
+}
+
+func inputString(str string) input {
+	return input{str: str}
+}
+
+func (in *input) setBytes(str []byte) {
+	in.str = ""
+	in.bytes = str
+}
+
+func (in *input) setString(str string) {
+	in.str = str
+	in.bytes = nil
+}
+
+func (in *input) _byte(p int) byte {
+	if in.bytes == nil {
+		return in.str[p]
+	}
+	return in.bytes[p]
+}
+
+func (in *input) skipASCII(p, max int) int {
+	if in.bytes == nil {
+		for ; p < max && in.str[p] < utf8.RuneSelf; p++ {
+		}
+	} else {
+		for ; p < max && in.bytes[p] < utf8.RuneSelf; p++ {
+		}
+	}
+	return p
+}
+
+func (in *input) skipContinuationBytes(p int) int {
+	if in.bytes == nil {
+		for ; p < len(in.str) && !utf8.RuneStart(in.str[p]); p++ {
+		}
+	} else {
+		for ; p < len(in.bytes) && !utf8.RuneStart(in.bytes[p]); p++ {
+		}
+	}
+	return p
+}
+
+func (in *input) appendSlice(buf []byte, b, e int) []byte {
+	if in.bytes != nil {
+		return append(buf, in.bytes[b:e]...)
+	}
+	for i := b; i < e; i++ {
+		buf = append(buf, in.str[i])
+	}
+	return buf
+}
+
+func (in *input) copySlice(buf []byte, b, e int) int {
+	if in.bytes == nil {
+		return copy(buf, in.str[b:e])
+	}
+	return copy(buf, in.bytes[b:e])
+}
+
+func (in *input) charinfoNFC(p int) (uint16, int) {
+	if in.bytes == nil {
+		return nfcData.lookupString(in.str[p:])
+	}
+	return nfcData.lookup(in.bytes[p:])
+}
+
+func (in *input) charinfoNFKC(p int) (uint16, int) {
+	if in.bytes == nil {
+		return nfkcData.lookupString(in.str[p:])
+	}
+	return nfkcData.lookup(in.bytes[p:])
+}
+
+func (in *input) hangul(p int) (r rune) {
+	if in.bytes == nil {
+		if !isHangulString(in.str[p:]) {
+			return 0
+		}
+		r, _ = utf8.DecodeRuneInString(in.str[p:])
+	} else {
+		if !isHangul(in.bytes[p:]) {
+			return 0
+		}
+		r, _ = utf8.DecodeRune(in.bytes[p:])
+	}
+	return r
+}
diff --git a/src/vendor/golang_org/x/text/unicode/norm/iter.go b/src/vendor/golang_org/x/text/unicode/norm/iter.go
new file mode 100644
index 0000000..0a42a72
--- /dev/null
+++ b/src/vendor/golang_org/x/text/unicode/norm/iter.go
@@ -0,0 +1,450 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import (
+	"fmt"
+	"unicode/utf8"
+)
+
+// MaxSegmentSize is the maximum size of a byte buffer needed to consider any
+// sequence of starter and non-starter runes for the purpose of normalization.
+const MaxSegmentSize = maxByteBufferSize
+
+// An Iter iterates over a string or byte slice, while normalizing it
+// to a given Form.
+type Iter struct {
+	rb     reorderBuffer
+	buf    [maxByteBufferSize]byte
+	info   Properties // first character saved from previous iteration
+	next   iterFunc   // implementation of next depends on form
+	asciiF iterFunc
+
+	p        int    // current position in input source
+	multiSeg []byte // remainder of multi-segment decomposition
+}
+
+type iterFunc func(*Iter) []byte
+
+// Init initializes i to iterate over src after normalizing it to Form f.
+func (i *Iter) Init(f Form, src []byte) {
+	i.p = 0
+	if len(src) == 0 {
+		i.setDone()
+		i.rb.nsrc = 0
+		return
+	}
+	i.multiSeg = nil
+	i.rb.init(f, src)
+	i.next = i.rb.f.nextMain
+	i.asciiF = nextASCIIBytes
+	i.info = i.rb.f.info(i.rb.src, i.p)
+}
+
+// InitString initializes i to iterate over src after normalizing it to Form f.
+func (i *Iter) InitString(f Form, src string) {
+	i.p = 0
+	if len(src) == 0 {
+		i.setDone()
+		i.rb.nsrc = 0
+		return
+	}
+	i.multiSeg = nil
+	i.rb.initString(f, src)
+	i.next = i.rb.f.nextMain
+	i.asciiF = nextASCIIString
+	i.info = i.rb.f.info(i.rb.src, i.p)
+}
+
+// Seek sets the segment to be returned by the next call to Next to start
+// at position p.  It is the responsibility of the caller to set p to the
+// start of a UTF8 rune.
+func (i *Iter) Seek(offset int64, whence int) (int64, error) {
+	var abs int64
+	switch whence {
+	case 0:
+		abs = offset
+	case 1:
+		abs = int64(i.p) + offset
+	case 2:
+		abs = int64(i.rb.nsrc) + offset
+	default:
+		return 0, fmt.Errorf("norm: invalid whence")
+	}
+	if abs < 0 {
+		return 0, fmt.Errorf("norm: negative position")
+	}
+	if int(abs) >= i.rb.nsrc {
+		i.setDone()
+		return int64(i.p), nil
+	}
+	i.p = int(abs)
+	i.multiSeg = nil
+	i.next = i.rb.f.nextMain
+	i.info = i.rb.f.info(i.rb.src, i.p)
+	return abs, nil
+}
+
+// returnSlice returns a slice of the underlying input type as a byte slice.
+// If the underlying is of type []byte, it will simply return a slice.
+// If the underlying is of type string, it will copy the slice to the buffer
+// and return that.
+func (i *Iter) returnSlice(a, b int) []byte {
+	if i.rb.src.bytes == nil {
+		return i.buf[:copy(i.buf[:], i.rb.src.str[a:b])]
+	}
+	return i.rb.src.bytes[a:b]
+}
+
+// Pos returns the byte position at which the next call to Next will commence processing.
+func (i *Iter) Pos() int {
+	return i.p
+}
+
+func (i *Iter) setDone() {
+	i.next = nextDone
+	i.p = i.rb.nsrc
+}
+
+// Done returns true if there is no more input to process.
+func (i *Iter) Done() bool {
+	return i.p >= i.rb.nsrc
+}
+
+// Next returns f(i.input[i.Pos():n]), where n is a boundary of i.input.
+// For any input a and b for which f(a) == f(b), subsequent calls
+// to Next will return the same segments.
+// Modifying runes are grouped together with the preceding starter, if such a starter exists.
+// Although not guaranteed, n will typically be the smallest possible n.
+func (i *Iter) Next() []byte {
+	return i.next(i)
+}
+
+func nextASCIIBytes(i *Iter) []byte {
+	p := i.p + 1
+	if p >= i.rb.nsrc {
+		i.setDone()
+		return i.rb.src.bytes[i.p:p]
+	}
+	if i.rb.src.bytes[p] < utf8.RuneSelf {
+		p0 := i.p
+		i.p = p
+		return i.rb.src.bytes[p0:p]
+	}
+	i.info = i.rb.f.info(i.rb.src, i.p)
+	i.next = i.rb.f.nextMain
+	return i.next(i)
+}
+
+func nextASCIIString(i *Iter) []byte {
+	p := i.p + 1
+	if p >= i.rb.nsrc {
+		i.buf[0] = i.rb.src.str[i.p]
+		i.setDone()
+		return i.buf[:1]
+	}
+	if i.rb.src.str[p] < utf8.RuneSelf {
+		i.buf[0] = i.rb.src.str[i.p]
+		i.p = p
+		return i.buf[:1]
+	}
+	i.info = i.rb.f.info(i.rb.src, i.p)
+	i.next = i.rb.f.nextMain
+	return i.next(i)
+}
+
+func nextHangul(i *Iter) []byte {
+	p := i.p
+	next := p + hangulUTF8Size
+	if next >= i.rb.nsrc {
+		i.setDone()
+	} else if i.rb.src.hangul(next) == 0 {
+		i.info = i.rb.f.info(i.rb.src, i.p)
+		i.next = i.rb.f.nextMain
+		return i.next(i)
+	}
+	i.p = next
+	return i.buf[:decomposeHangul(i.buf[:], i.rb.src.hangul(p))]
+}
+
+func nextDone(i *Iter) []byte {
+	return nil
+}
+
+// nextMulti is used for iterating over multi-segment decompositions
+// for decomposing normal forms.
+func nextMulti(i *Iter) []byte {
+	j := 0
+	d := i.multiSeg
+	// skip first rune
+	for j = 1; j < len(d) && !utf8.RuneStart(d[j]); j++ {
+	}
+	for j < len(d) {
+		info := i.rb.f.info(input{bytes: d}, j)
+		if info.BoundaryBefore() {
+			i.multiSeg = d[j:]
+			return d[:j]
+		}
+		j += int(info.size)
+	}
+	// treat last segment as normal decomposition
+	i.next = i.rb.f.nextMain
+	return i.next(i)
+}
+
+// nextMultiNorm is used for iterating over multi-segment decompositions
+// for composing normal forms.
+func nextMultiNorm(i *Iter) []byte {
+	j := 0
+	d := i.multiSeg
+	for j < len(d) {
+		info := i.rb.f.info(input{bytes: d}, j)
+		if info.BoundaryBefore() {
+			i.rb.compose()
+			seg := i.buf[:i.rb.flushCopy(i.buf[:])]
+			i.rb.ss.first(info)
+			i.rb.insertUnsafe(input{bytes: d}, j, info)
+			i.multiSeg = d[j+int(info.size):]
+			return seg
+		}
+		i.rb.ss.next(info)
+		i.rb.insertUnsafe(input{bytes: d}, j, info)
+		j += int(info.size)
+	}
+	i.multiSeg = nil
+	i.next = nextComposed
+	return doNormComposed(i)
+}
+
+// nextDecomposed is the implementation of Next for forms NFD and NFKD.
+func nextDecomposed(i *Iter) (next []byte) {
+	outp := 0
+	inCopyStart, outCopyStart := i.p, 0
+	ss := mkStreamSafe(i.info)
+	for {
+		if sz := int(i.info.size); sz <= 1 {
+			p := i.p
+			i.p++ // ASCII or illegal byte.  Either way, advance by 1.
+			if i.p >= i.rb.nsrc {
+				i.setDone()
+				return i.returnSlice(p, i.p)
+			} else if i.rb.src._byte(i.p) < utf8.RuneSelf {
+				i.next = i.asciiF
+				return i.returnSlice(p, i.p)
+			}
+			outp++
+		} else if d := i.info.Decomposition(); d != nil {
+			// Note: If leading CCC != 0, then len(d) == 2 and last is also non-zero.
+			// Case 1: there is a leftover to copy.  In this case the decomposition
+			// must begin with a modifier and should always be appended.
+			// Case 2: no leftover. Simply return d if followed by a ccc == 0 value.
+			p := outp + len(d)
+			if outp > 0 {
+				i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p)
+				if p > len(i.buf) {
+					return i.buf[:outp]
+				}
+			} else if i.info.multiSegment() {
+				// outp must be 0 as multi-segment decompositions always
+				// start a new segment.
+				if i.multiSeg == nil {
+					i.multiSeg = d
+					i.next = nextMulti
+					return nextMulti(i)
+				}
+				// We are in the last segment.  Treat as normal decomposition.
+				d = i.multiSeg
+				i.multiSeg = nil
+				p = len(d)
+			}
+			prevCC := i.info.tccc
+			if i.p += sz; i.p >= i.rb.nsrc {
+				i.setDone()
+				i.info = Properties{} // Force BoundaryBefore to succeed.
+			} else {
+				i.info = i.rb.f.info(i.rb.src, i.p)
+			}
+			switch ss.next(i.info) {
+			case ssOverflow:
+				i.next = nextCGJDecompose
+				fallthrough
+			case ssStarter:
+				if outp > 0 {
+					copy(i.buf[outp:], d)
+					return i.buf[:p]
+				}
+				return d
+			}
+			copy(i.buf[outp:], d)
+			outp = p
+			inCopyStart, outCopyStart = i.p, outp
+			if i.info.ccc < prevCC {
+				goto doNorm
+			}
+			continue
+		} else if r := i.rb.src.hangul(i.p); r != 0 {
+			outp = decomposeHangul(i.buf[:], r)
+			i.p += hangulUTF8Size
+			inCopyStart, outCopyStart = i.p, outp
+			if i.p >= i.rb.nsrc {
+				i.setDone()
+				break
+			} else if i.rb.src.hangul(i.p) != 0 {
+				i.next = nextHangul
+				return i.buf[:outp]
+			}
+		} else {
+			p := outp + sz
+			if p > len(i.buf) {
+				break
+			}
+			outp = p
+			i.p += sz
+		}
+		if i.p >= i.rb.nsrc {
+			i.setDone()
+			break
+		}
+		prevCC := i.info.tccc
+		i.info = i.rb.f.info(i.rb.src, i.p)
+		if v := ss.next(i.info); v == ssStarter {
+			break
+		} else if v == ssOverflow {
+			i.next = nextCGJDecompose
+			break
+		}
+		if i.info.ccc < prevCC {
+			goto doNorm
+		}
+	}
+	if outCopyStart == 0 {
+		return i.returnSlice(inCopyStart, i.p)
+	} else if inCopyStart < i.p {
+		i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p)
+	}
+	return i.buf[:outp]
+doNorm:
+	// Insert what we have decomposed so far in the reorderBuffer.
+	// As we will only reorder, there will always be enough room.
+	i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p)
+	i.rb.insertDecomposed(i.buf[0:outp])
+	return doNormDecomposed(i)
+}
+
+func doNormDecomposed(i *Iter) []byte {
+	for {
+		if s := i.rb.ss.next(i.info); s == ssOverflow {
+			i.next = nextCGJDecompose
+			break
+		}
+		i.rb.insertUnsafe(i.rb.src, i.p, i.info)
+		if i.p += int(i.info.size); i.p >= i.rb.nsrc {
+			i.setDone()
+			break
+		}
+		i.info = i.rb.f.info(i.rb.src, i.p)
+		if i.info.ccc == 0 {
+			break
+		}
+	}
+	// new segment or too many combining characters: exit normalization
+	return i.buf[:i.rb.flushCopy(i.buf[:])]
+}
+
+func nextCGJDecompose(i *Iter) []byte {
+	i.rb.ss = 0
+	i.rb.insertCGJ()
+	i.next = nextDecomposed
+	buf := doNormDecomposed(i)
+	return buf
+}
+
+// nextComposed is the implementation of Next for forms NFC and NFKC.
+func nextComposed(i *Iter) []byte {
+	outp, startp := 0, i.p
+	var prevCC uint8
+	ss := mkStreamSafe(i.info)
+	for {
+		if !i.info.isYesC() {
+			goto doNorm
+		}
+		prevCC = i.info.tccc
+		sz := int(i.info.size)
+		if sz == 0 {
+			sz = 1 // illegal rune: copy byte-by-byte
+		}
+		p := outp + sz
+		if p > len(i.buf) {
+			break
+		}
+		outp = p
+		i.p += sz
+		if i.p >= i.rb.nsrc {
+			i.setDone()
+			break
+		} else if i.rb.src._byte(i.p) < utf8.RuneSelf {
+			i.next = i.asciiF
+			break
+		}
+		i.info = i.rb.f.info(i.rb.src, i.p)
+		if v := ss.next(i.info); v == ssStarter {
+			break
+		} else if v == ssOverflow {
+			i.next = nextCGJCompose
+			break
+		}
+		if i.info.ccc < prevCC {
+			goto doNorm
+		}
+	}
+	return i.returnSlice(startp, i.p)
+doNorm:
+	i.p = startp
+	i.info = i.rb.f.info(i.rb.src, i.p)
+	if i.info.multiSegment() {
+		d := i.info.Decomposition()
+		info := i.rb.f.info(input{bytes: d}, 0)
+		i.rb.insertUnsafe(input{bytes: d}, 0, info)
+		i.multiSeg = d[int(info.size):]
+		i.next = nextMultiNorm
+		return nextMultiNorm(i)
+	}
+	i.rb.ss.first(i.info)
+	i.rb.insertUnsafe(i.rb.src, i.p, i.info)
+	return doNormComposed(i)
+}
+
+func doNormComposed(i *Iter) []byte {
+	// First rune should already be inserted.
+	for {
+		if i.p += int(i.info.size); i.p >= i.rb.nsrc {
+			i.setDone()
+			break
+		}
+		i.info = i.rb.f.info(i.rb.src, i.p)
+		if s := i.rb.ss.next(i.info); s == ssStarter {
+			break
+		} else if s == ssOverflow {
+			i.next = nextCGJCompose
+			break
+		}
+		i.rb.insertUnsafe(i.rb.src, i.p, i.info)
+	}
+	i.rb.compose()
+	seg := i.buf[:i.rb.flushCopy(i.buf[:])]
+	return seg
+}
+
+func nextCGJCompose(i *Iter) []byte {
+	i.rb.ss = 0 // instead of first
+	i.rb.insertCGJ()
+	i.next = nextComposed
+	// Note that we treat any rune with nLeadingNonStarters > 0 as a non-starter,
+	// even if they are not. This is particularly dubious for U+FF9E and UFF9A.
+	// If we ever change that, insert a check here.
+	i.rb.ss.first(i.info)
+	i.rb.insertUnsafe(i.rb.src, i.p, i.info)
+	return doNormComposed(i)
+}
diff --git a/src/vendor/golang_org/x/text/unicode/norm/normalize.go b/src/vendor/golang_org/x/text/unicode/norm/normalize.go
new file mode 100644
index 0000000..15c962e
--- /dev/null
+++ b/src/vendor/golang_org/x/text/unicode/norm/normalize.go
@@ -0,0 +1,608 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:generate go run maketables.go triegen.go
+//go:generate go run maketables.go triegen.go -test
+
+// Package norm contains types and functions for normalizing Unicode strings.
+package norm // import "golang.org/x/text/unicode/norm"
+
+import (
+	"unicode/utf8"
+
+	"golang_org/x/text/transform"
+)
+
+// A Form denotes a canonical representation of Unicode code points.
+// The Unicode-defined normalization and equivalence forms are:
+//
+//   NFC   Unicode Normalization Form C
+//   NFD   Unicode Normalization Form D
+//   NFKC  Unicode Normalization Form KC
+//   NFKD  Unicode Normalization Form KD
+//
+// For a Form f, this documentation uses the notation f(x) to mean
+// the bytes or string x converted to the given form.
+// A position n in x is called a boundary if conversion to the form can
+// proceed independently on both sides:
+//   f(x) == append(f(x[0:n]), f(x[n:])...)
+//
+// References: http://unicode.org/reports/tr15/ and
+// http://unicode.org/notes/tn5/.
+type Form int
+
+const (
+	NFC Form = iota
+	NFD
+	NFKC
+	NFKD
+)
+
+// Bytes returns f(b). May return b if f(b) = b.
+func (f Form) Bytes(b []byte) []byte {
+	src := inputBytes(b)
+	ft := formTable[f]
+	n, ok := ft.quickSpan(src, 0, len(b), true)
+	if ok {
+		return b
+	}
+	out := make([]byte, n, len(b))
+	copy(out, b[0:n])
+	rb := reorderBuffer{f: *ft, src: src, nsrc: len(b), out: out, flushF: appendFlush}
+	return doAppendInner(&rb, n)
+}
+
+// String returns f(s).
+func (f Form) String(s string) string {
+	src := inputString(s)
+	ft := formTable[f]
+	n, ok := ft.quickSpan(src, 0, len(s), true)
+	if ok {
+		return s
+	}
+	out := make([]byte, n, len(s))
+	copy(out, s[0:n])
+	rb := reorderBuffer{f: *ft, src: src, nsrc: len(s), out: out, flushF: appendFlush}
+	return string(doAppendInner(&rb, n))
+}
+
+// IsNormal returns true if b == f(b).
+func (f Form) IsNormal(b []byte) bool {
+	src := inputBytes(b)
+	ft := formTable[f]
+	bp, ok := ft.quickSpan(src, 0, len(b), true)
+	if ok {
+		return true
+	}
+	rb := reorderBuffer{f: *ft, src: src, nsrc: len(b)}
+	rb.setFlusher(nil, cmpNormalBytes)
+	for bp < len(b) {
+		rb.out = b[bp:]
+		if bp = decomposeSegment(&rb, bp, true); bp < 0 {
+			return false
+		}
+		bp, _ = rb.f.quickSpan(rb.src, bp, len(b), true)
+	}
+	return true
+}
+
+func cmpNormalBytes(rb *reorderBuffer) bool {
+	b := rb.out
+	for i := 0; i < rb.nrune; i++ {
+		info := rb.rune[i]
+		if int(info.size) > len(b) {
+			return false
+		}
+		p := info.pos
+		pe := p + info.size
+		for ; p < pe; p++ {
+			if b[0] != rb.byte[p] {
+				return false
+			}
+			b = b[1:]
+		}
+	}
+	return true
+}
+
+// IsNormalString returns true if s == f(s).
+func (f Form) IsNormalString(s string) bool {
+	src := inputString(s)
+	ft := formTable[f]
+	bp, ok := ft.quickSpan(src, 0, len(s), true)
+	if ok {
+		return true
+	}
+	rb := reorderBuffer{f: *ft, src: src, nsrc: len(s)}
+	rb.setFlusher(nil, func(rb *reorderBuffer) bool {
+		for i := 0; i < rb.nrune; i++ {
+			info := rb.rune[i]
+			if bp+int(info.size) > len(s) {
+				return false
+			}
+			p := info.pos
+			pe := p + info.size
+			for ; p < pe; p++ {
+				if s[bp] != rb.byte[p] {
+					return false
+				}
+				bp++
+			}
+		}
+		return true
+	})
+	for bp < len(s) {
+		if bp = decomposeSegment(&rb, bp, true); bp < 0 {
+			return false
+		}
+		bp, _ = rb.f.quickSpan(rb.src, bp, len(s), true)
+	}
+	return true
+}
+
+// patchTail fixes a case where a rune may be incorrectly normalized
+// if it is followed by illegal continuation bytes. It returns the
+// patched buffer and whether the decomposition is still in progress.
+func patchTail(rb *reorderBuffer) bool {
+	info, p := lastRuneStart(&rb.f, rb.out)
+	if p == -1 || info.size == 0 {
+		return true
+	}
+	end := p + int(info.size)
+	extra := len(rb.out) - end
+	if extra > 0 {
+		// Potentially allocating memory. However, this only
+		// happens with ill-formed UTF-8.
+		x := make([]byte, 0)
+		x = append(x, rb.out[len(rb.out)-extra:]...)
+		rb.out = rb.out[:end]
+		decomposeToLastBoundary(rb)
+		rb.doFlush()
+		rb.out = append(rb.out, x...)
+		return false
+	}
+	buf := rb.out[p:]
+	rb.out = rb.out[:p]
+	decomposeToLastBoundary(rb)
+	if s := rb.ss.next(info); s == ssStarter {
+		rb.doFlush()
+		rb.ss.first(info)
+	} else if s == ssOverflow {
+		rb.doFlush()
+		rb.insertCGJ()
+		rb.ss = 0
+	}
+	rb.insertUnsafe(inputBytes(buf), 0, info)
+	return true
+}
+
+func appendQuick(rb *reorderBuffer, i int) int {
+	if rb.nsrc == i {
+		return i
+	}
+	end, _ := rb.f.quickSpan(rb.src, i, rb.nsrc, true)
+	rb.out = rb.src.appendSlice(rb.out, i, end)
+	return end
+}
+
+// Append returns f(append(out, b...)).
+// The buffer out must be nil, empty, or equal to f(out).
+func (f Form) Append(out []byte, src ...byte) []byte {
+	return f.doAppend(out, inputBytes(src), len(src))
+}
+
+func (f Form) doAppend(out []byte, src input, n int) []byte {
+	if n == 0 {
+		return out
+	}
+	ft := formTable[f]
+	// Attempt to do a quickSpan first so we can avoid initializing the reorderBuffer.
+	if len(out) == 0 {
+		p, _ := ft.quickSpan(src, 0, n, true)
+		out = src.appendSlice(out, 0, p)
+		if p == n {
+			return out
+		}
+		rb := reorderBuffer{f: *ft, src: src, nsrc: n, out: out, flushF: appendFlush}
+		return doAppendInner(&rb, p)
+	}
+	rb := reorderBuffer{f: *ft, src: src, nsrc: n}
+	return doAppend(&rb, out, 0)
+}
+
+func doAppend(rb *reorderBuffer, out []byte, p int) []byte {
+	rb.setFlusher(out, appendFlush)
+	src, n := rb.src, rb.nsrc
+	doMerge := len(out) > 0
+	if q := src.skipContinuationBytes(p); q > p {
+		// Move leading non-starters to destination.
+		rb.out = src.appendSlice(rb.out, p, q)
+		p = q
+		doMerge = patchTail(rb)
+	}
+	fd := &rb.f
+	if doMerge {
+		var info Properties
+		if p < n {
+			info = fd.info(src, p)
+			if !info.BoundaryBefore() || info.nLeadingNonStarters() > 0 {
+				if p == 0 {
+					decomposeToLastBoundary(rb)
+				}
+				p = decomposeSegment(rb, p, true)
+			}
+		}
+		if info.size == 0 {
+			rb.doFlush()
+			// Append incomplete UTF-8 encoding.
+			return src.appendSlice(rb.out, p, n)
+		}
+		if rb.nrune > 0 {
+			return doAppendInner(rb, p)
+		}
+	}
+	p = appendQuick(rb, p)
+	return doAppendInner(rb, p)
+}
+
+func doAppendInner(rb *reorderBuffer, p int) []byte {
+	for n := rb.nsrc; p < n; {
+		p = decomposeSegment(rb, p, true)
+		p = appendQuick(rb, p)
+	}
+	return rb.out
+}
+
+// AppendString returns f(append(out, []byte(s))).
+// The buffer out must be nil, empty, or equal to f(out).
+func (f Form) AppendString(out []byte, src string) []byte {
+	return f.doAppend(out, inputString(src), len(src))
+}
+
+// QuickSpan returns a boundary n such that b[0:n] == f(b[0:n]).
+// It is not guaranteed to return the largest such n.
+func (f Form) QuickSpan(b []byte) int {
+	n, _ := formTable[f].quickSpan(inputBytes(b), 0, len(b), true)
+	return n
+}
+
+// Span implements transform.SpanningTransformer. It returns a boundary n such
+// that b[0:n] == f(b[0:n]). It is not guaranteed to return the largest such n.
+func (f Form) Span(b []byte, atEOF bool) (n int, err error) {
+	n, ok := formTable[f].quickSpan(inputBytes(b), 0, len(b), atEOF)
+	if n < len(b) {
+		if !ok {
+			err = transform.ErrEndOfSpan
+		} else {
+			err = transform.ErrShortSrc
+		}
+	}
+	return n, err
+}
+
+// SpanString returns a boundary n such that s[0:n] == f(s[0:n]).
+// It is not guaranteed to return the largest such n.
+func (f Form) SpanString(s string, atEOF bool) (n int, err error) {
+	n, ok := formTable[f].quickSpan(inputString(s), 0, len(s), atEOF)
+	if n < len(s) {
+		if !ok {
+			err = transform.ErrEndOfSpan
+		} else {
+			err = transform.ErrShortSrc
+		}
+	}
+	return n, err
+}
+
+// quickSpan returns a boundary n such that src[0:n] == f(src[0:n]) and
+// whether any non-normalized parts were found. If atEOF is false, n will
+// not point past the last segment if this segment might be become
+// non-normalized by appending other runes.
+func (f *formInfo) quickSpan(src input, i, end int, atEOF bool) (n int, ok bool) {
+	var lastCC uint8
+	ss := streamSafe(0)
+	lastSegStart := i
+	for n = end; i < n; {
+		if j := src.skipASCII(i, n); i != j {
+			i = j
+			lastSegStart = i - 1
+			lastCC = 0
+			ss = 0
+			continue
+		}
+		info := f.info(src, i)
+		if info.size == 0 {
+			if atEOF {
+				// include incomplete runes
+				return n, true
+			}
+			return lastSegStart, true
+		}
+		// This block needs to be before the next, because it is possible to
+		// have an overflow for runes that are starters (e.g. with U+FF9E).
+		switch ss.next(info) {
+		case ssStarter:
+			ss.first(info)
+			lastSegStart = i
+		case ssOverflow:
+			return lastSegStart, false
+		case ssSuccess:
+			if lastCC > info.ccc {
+				return lastSegStart, false
+			}
+		}
+		if f.composing {
+			if !info.isYesC() {
+				break
+			}
+		} else {
+			if !info.isYesD() {
+				break
+			}
+		}
+		lastCC = info.ccc
+		i += int(info.size)
+	}
+	if i == n {
+		if !atEOF {
+			n = lastSegStart
+		}
+		return n, true
+	}
+	return lastSegStart, false
+}
+
+// QuickSpanString returns a boundary n such that s[0:n] == f(s[0:n]).
+// It is not guaranteed to return the largest such n.
+func (f Form) QuickSpanString(s string) int {
+	n, _ := formTable[f].quickSpan(inputString(s), 0, len(s), true)
+	return n
+}
+
+// FirstBoundary returns the position i of the first boundary in b
+// or -1 if b contains no boundary.
+func (f Form) FirstBoundary(b []byte) int {
+	return f.firstBoundary(inputBytes(b), len(b))
+}
+
+func (f Form) firstBoundary(src input, nsrc int) int {
+	i := src.skipContinuationBytes(0)
+	if i >= nsrc {
+		return -1
+	}
+	fd := formTable[f]
+	ss := streamSafe(0)
+	// We should call ss.first here, but we can't as the first rune is
+	// skipped already. This means FirstBoundary can't really determine
+	// CGJ insertion points correctly. Luckily it doesn't have to.
+	for {
+		info := fd.info(src, i)
+		if info.size == 0 {
+			return -1
+		}
+		if s := ss.next(info); s != ssSuccess {
+			return i
+		}
+		i += int(info.size)
+		if i >= nsrc {
+			if !info.BoundaryAfter() && !ss.isMax() {
+				return -1
+			}
+			return nsrc
+		}
+	}
+}
+
+// FirstBoundaryInString returns the position i of the first boundary in s
+// or -1 if s contains no boundary.
+func (f Form) FirstBoundaryInString(s string) int {
+	return f.firstBoundary(inputString(s), len(s))
+}
+
+// NextBoundary reports the index of the boundary between the first and next
+// segment in b or -1 if atEOF is false and there are not enough bytes to
+// determine this boundary.
+func (f Form) NextBoundary(b []byte, atEOF bool) int {
+	return f.nextBoundary(inputBytes(b), len(b), atEOF)
+}
+
+// NextBoundaryInString reports the index of the boundary between the first and
+// next segment in b or -1 if atEOF is false and there are not enough bytes to
+// determine this boundary.
+func (f Form) NextBoundaryInString(s string, atEOF bool) int {
+	return f.nextBoundary(inputString(s), len(s), atEOF)
+}
+
+func (f Form) nextBoundary(src input, nsrc int, atEOF bool) int {
+	if nsrc == 0 {
+		if atEOF {
+			return 0
+		}
+		return -1
+	}
+	fd := formTable[f]
+	info := fd.info(src, 0)
+	if info.size == 0 {
+		if atEOF {
+			return 1
+		}
+		return -1
+	}
+	ss := streamSafe(0)
+	ss.first(info)
+
+	for i := int(info.size); i < nsrc; i += int(info.size) {
+		info = fd.info(src, i)
+		if info.size == 0 {
+			if atEOF {
+				return i
+			}
+			return -1
+		}
+		if s := ss.next(info); s != ssSuccess {
+			return i
+		}
+	}
+	if !atEOF && !info.BoundaryAfter() && !ss.isMax() {
+		return -1
+	}
+	return nsrc
+}
+
+// LastBoundary returns the position i of the last boundary in b
+// or -1 if b contains no boundary.
+func (f Form) LastBoundary(b []byte) int {
+	return lastBoundary(formTable[f], b)
+}
+
+func lastBoundary(fd *formInfo, b []byte) int {
+	i := len(b)
+	info, p := lastRuneStart(fd, b)
+	if p == -1 {
+		return -1
+	}
+	if info.size == 0 { // ends with incomplete rune
+		if p == 0 { // starts with incomplete rune
+			return -1
+		}
+		i = p
+		info, p = lastRuneStart(fd, b[:i])
+		if p == -1 { // incomplete UTF-8 encoding or non-starter bytes without a starter
+			return i
+		}
+	}
+	if p+int(info.size) != i { // trailing non-starter bytes: illegal UTF-8
+		return i
+	}
+	if info.BoundaryAfter() {
+		return i
+	}
+	ss := streamSafe(0)
+	v := ss.backwards(info)
+	for i = p; i >= 0 && v != ssStarter; i = p {
+		info, p = lastRuneStart(fd, b[:i])
+		if v = ss.backwards(info); v == ssOverflow {
+			break
+		}
+		if p+int(info.size) != i {
+			if p == -1 { // no boundary found
+				return -1
+			}
+			return i // boundary after an illegal UTF-8 encoding
+		}
+	}
+	return i
+}
+
+// decomposeSegment scans the first segment in src into rb. It inserts 0x034f
+// (Grapheme Joiner) when it encounters a sequence of more than 30 non-starters
+// and returns the number of bytes consumed from src or iShortDst or iShortSrc.
+func decomposeSegment(rb *reorderBuffer, sp int, atEOF bool) int {
+	// Force one character to be consumed.
+	info := rb.f.info(rb.src, sp)
+	if info.size == 0 {
+		return 0
+	}
+	if rb.nrune > 0 {
+		if s := rb.ss.next(info); s == ssStarter {
+			goto end
+		} else if s == ssOverflow {
+			rb.insertCGJ()
+			goto end
+		}
+	} else {
+		rb.ss.first(info)
+	}
+	if err := rb.insertFlush(rb.src, sp, info); err != iSuccess {
+		return int(err)
+	}
+	for {
+		sp += int(info.size)
+		if sp >= rb.nsrc {
+			if !atEOF && !info.BoundaryAfter() {
+				return int(iShortSrc)
+			}
+			break
+		}
+		info = rb.f.info(rb.src, sp)
+		if info.size == 0 {
+			if !atEOF {
+				return int(iShortSrc)
+			}
+			break
+		}
+		if s := rb.ss.next(info); s == ssStarter {
+			break
+		} else if s == ssOverflow {
+			rb.insertCGJ()
+			break
+		}
+		if err := rb.insertFlush(rb.src, sp, info); err != iSuccess {
+			return int(err)
+		}
+	}
+end:
+	if !rb.doFlush() {
+		return int(iShortDst)
+	}
+	return sp
+}
+
+// lastRuneStart returns the runeInfo and position of the last
+// rune in buf or the zero runeInfo and -1 if no rune was found.
+func lastRuneStart(fd *formInfo, buf []byte) (Properties, int) {
+	p := len(buf) - 1
+	for ; p >= 0 && !utf8.RuneStart(buf[p]); p-- {
+	}
+	if p < 0 {
+		return Properties{}, -1
+	}
+	return fd.info(inputBytes(buf), p), p
+}
+
+// decomposeToLastBoundary finds an open segment at the end of the buffer
+// and scans it into rb. Returns the buffer minus the last segment.
+func decomposeToLastBoundary(rb *reorderBuffer) {
+	fd := &rb.f
+	info, i := lastRuneStart(fd, rb.out)
+	if int(info.size) != len(rb.out)-i {
+		// illegal trailing continuation bytes
+		return
+	}
+	if info.BoundaryAfter() {
+		return
+	}
+	var add [maxNonStarters + 1]Properties // stores runeInfo in reverse order
+	padd := 0
+	ss := streamSafe(0)
+	p := len(rb.out)
+	for {
+		add[padd] = info
+		v := ss.backwards(info)
+		if v == ssOverflow {
+			// Note that if we have an overflow, it the string we are appending to
+			// is not correctly normalized. In this case the behavior is undefined.
+			break
+		}
+		padd++
+		p -= int(info.size)
+		if v == ssStarter || p < 0 {
+			break
+		}
+		info, i = lastRuneStart(fd, rb.out[:p])
+		if int(info.size) != p-i {
+			break
+		}
+	}
+	rb.ss = ss
+	// Copy bytes for insertion as we may need to overwrite rb.out.
+	var buf [maxBufferSize * utf8.UTFMax]byte
+	cp := buf[:copy(buf[:], rb.out[p:])]
+	rb.out = rb.out[:p]
+	for padd--; padd >= 0; padd-- {
+		info = add[padd]
+		rb.insertUnsafe(inputBytes(cp), 0, info)
+		cp = cp[info.size:]
+	}
+}
diff --git a/src/vendor/golang_org/x/text/unicode/norm/readwriter.go b/src/vendor/golang_org/x/text/unicode/norm/readwriter.go
new file mode 100644
index 0000000..d926ee9
--- /dev/null
+++ b/src/vendor/golang_org/x/text/unicode/norm/readwriter.go
@@ -0,0 +1,125 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import "io"
+
+type normWriter struct {
+	rb  reorderBuffer
+	w   io.Writer
+	buf []byte
+}
+
+// Write implements the standard write interface.  If the last characters are
+// not at a normalization boundary, the bytes will be buffered for the next
+// write. The remaining bytes will be written on close.
+func (w *normWriter) Write(data []byte) (n int, err error) {
+	// Process data in pieces to keep w.buf size bounded.
+	const chunk = 4000
+
+	for len(data) > 0 {
+		// Normalize into w.buf.
+		m := len(data)
+		if m > chunk {
+			m = chunk
+		}
+		w.rb.src = inputBytes(data[:m])
+		w.rb.nsrc = m
+		w.buf = doAppend(&w.rb, w.buf, 0)
+		data = data[m:]
+		n += m
+
+		// Write out complete prefix, save remainder.
+		// Note that lastBoundary looks back at most 31 runes.
+		i := lastBoundary(&w.rb.f, w.buf)
+		if i == -1 {
+			i = 0
+		}
+		if i > 0 {
+			if _, err = w.w.Write(w.buf[:i]); err != nil {
+				break
+			}
+			bn := copy(w.buf, w.buf[i:])
+			w.buf = w.buf[:bn]
+		}
+	}
+	return n, err
+}
+
+// Close forces data that remains in the buffer to be written.
+func (w *normWriter) Close() error {
+	if len(w.buf) > 0 {
+		_, err := w.w.Write(w.buf)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Writer returns a new writer that implements Write(b)
+// by writing f(b) to w.  The returned writer may use an
+// an internal buffer to maintain state across Write calls.
+// Calling its Close method writes any buffered data to w.
+func (f Form) Writer(w io.Writer) io.WriteCloser {
+	wr := &normWriter{rb: reorderBuffer{}, w: w}
+	wr.rb.init(f, nil)
+	return wr
+}
+
+type normReader struct {
+	rb           reorderBuffer
+	r            io.Reader
+	inbuf        []byte
+	outbuf       []byte
+	bufStart     int
+	lastBoundary int
+	err          error
+}
+
+// Read implements the standard read interface.
+func (r *normReader) Read(p []byte) (int, error) {
+	for {
+		if r.lastBoundary-r.bufStart > 0 {
+			n := copy(p, r.outbuf[r.bufStart:r.lastBoundary])
+			r.bufStart += n
+			if r.lastBoundary-r.bufStart > 0 {
+				return n, nil
+			}
+			return n, r.err
+		}
+		if r.err != nil {
+			return 0, r.err
+		}
+		outn := copy(r.outbuf, r.outbuf[r.lastBoundary:])
+		r.outbuf = r.outbuf[0:outn]
+		r.bufStart = 0
+
+		n, err := r.r.Read(r.inbuf)
+		r.rb.src = inputBytes(r.inbuf[0:n])
+		r.rb.nsrc, r.err = n, err
+		if n > 0 {
+			r.outbuf = doAppend(&r.rb, r.outbuf, 0)
+		}
+		if err == io.EOF {
+			r.lastBoundary = len(r.outbuf)
+		} else {
+			r.lastBoundary = lastBoundary(&r.rb.f, r.outbuf)
+			if r.lastBoundary == -1 {
+				r.lastBoundary = 0
+			}
+		}
+	}
+}
+
+// Reader returns a new reader that implements Read
+// by reading data from r and returning f(data).
+func (f Form) Reader(r io.Reader) io.Reader {
+	const chunk = 4000
+	buf := make([]byte, chunk)
+	rr := &normReader{rb: reorderBuffer{}, r: r, inbuf: buf}
+	rr.rb.init(f, buf)
+	return rr
+}
diff --git a/src/vendor/golang_org/x/text/unicode/norm/tables.go b/src/vendor/golang_org/x/text/unicode/norm/tables.go
new file mode 100644
index 0000000..a56697b
--- /dev/null
+++ b/src/vendor/golang_org/x/text/unicode/norm/tables.go
@@ -0,0 +1,7627 @@
+// This file was generated by go generate; DO NOT EDIT
+
+package norm
+
+const (
+	// Version is the Unicode edition from which the tables are derived.
+	Version = "9.0.0"
+
+	// MaxTransformChunkSize indicates the maximum number of bytes that Transform
+	// may need to write atomically for any Form. Making a destination buffer at
+	// least this size ensures that Transform can always make progress and that
+	// the user does not need to grow the buffer on an ErrShortDst.
+	MaxTransformChunkSize = 35 + maxNonStarters*4
+)
+
+var ccc = [55]uint8{
+	0, 1, 7, 8, 9, 10, 11, 12,
+	13, 14, 15, 16, 17, 18, 19, 20,
+	21, 22, 23, 24, 25, 26, 27, 28,
+	29, 30, 31, 32, 33, 34, 35, 36,
+	84, 91, 103, 107, 118, 122, 129, 130,
+	132, 202, 214, 216, 218, 220, 222, 224,
+	226, 228, 230, 232, 233, 234, 240,
+}
+
+const (
+	firstMulti            = 0x186D
+	firstCCC              = 0x2C9E
+	endMulti              = 0x2F60
+	firstLeadingCCC       = 0x4A44
+	firstCCCZeroExcept    = 0x4A5A
+	firstStarterWithNLead = 0x4A81
+	lastDecomp            = 0x4A83
+	maxDecomp             = 0x8000
+)
+
+// decomps: 19075 bytes
+var decomps = [...]byte{
+	// Bytes 0 - 3f
+	0x00, 0x41, 0x20, 0x41, 0x21, 0x41, 0x22, 0x41,
+	0x23, 0x41, 0x24, 0x41, 0x25, 0x41, 0x26, 0x41,
+	0x27, 0x41, 0x28, 0x41, 0x29, 0x41, 0x2A, 0x41,
+	0x2B, 0x41, 0x2C, 0x41, 0x2D, 0x41, 0x2E, 0x41,
+	0x2F, 0x41, 0x30, 0x41, 0x31, 0x41, 0x32, 0x41,
+	0x33, 0x41, 0x34, 0x41, 0x35, 0x41, 0x36, 0x41,
+	0x37, 0x41, 0x38, 0x41, 0x39, 0x41, 0x3A, 0x41,
+	0x3B, 0x41, 0x3C, 0x41, 0x3D, 0x41, 0x3E, 0x41,
+	// Bytes 40 - 7f
+	0x3F, 0x41, 0x40, 0x41, 0x41, 0x41, 0x42, 0x41,
+	0x43, 0x41, 0x44, 0x41, 0x45, 0x41, 0x46, 0x41,
+	0x47, 0x41, 0x48, 0x41, 0x49, 0x41, 0x4A, 0x41,
+	0x4B, 0x41, 0x4C, 0x41, 0x4D, 0x41, 0x4E, 0x41,
+	0x4F, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41,
+	0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41,
+	0x57, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5A, 0x41,
+	0x5B, 0x41, 0x5C, 0x41, 0x5D, 0x41, 0x5E, 0x41,
+	// Bytes 80 - bf
+	0x5F, 0x41, 0x60, 0x41, 0x61, 0x41, 0x62, 0x41,
+	0x63, 0x41, 0x64, 0x41, 0x65, 0x41, 0x66, 0x41,
+	0x67, 0x41, 0x68, 0x41, 0x69, 0x41, 0x6A, 0x41,
+	0x6B, 0x41, 0x6C, 0x41, 0x6D, 0x41, 0x6E, 0x41,
+	0x6F, 0x41, 0x70, 0x41, 0x71, 0x41, 0x72, 0x41,
+	0x73, 0x41, 0x74, 0x41, 0x75, 0x41, 0x76, 0x41,
+	0x77, 0x41, 0x78, 0x41, 0x79, 0x41, 0x7A, 0x41,
+	0x7B, 0x41, 0x7C, 0x41, 0x7D, 0x41, 0x7E, 0x42,
+	// Bytes c0 - ff
+	0xC2, 0xA2, 0x42, 0xC2, 0xA3, 0x42, 0xC2, 0xA5,
+	0x42, 0xC2, 0xA6, 0x42, 0xC2, 0xAC, 0x42, 0xC2,
+	0xB7, 0x42, 0xC3, 0x86, 0x42, 0xC3, 0xB0, 0x42,
+	0xC4, 0xA6, 0x42, 0xC4, 0xA7, 0x42, 0xC4, 0xB1,
+	0x42, 0xC5, 0x8B, 0x42, 0xC5, 0x93, 0x42, 0xC6,
+	0x8E, 0x42, 0xC6, 0x90, 0x42, 0xC6, 0xAB, 0x42,
+	0xC8, 0xA2, 0x42, 0xC8, 0xB7, 0x42, 0xC9, 0x90,
+	0x42, 0xC9, 0x91, 0x42, 0xC9, 0x92, 0x42, 0xC9,
+	// Bytes 100 - 13f
+	0x94, 0x42, 0xC9, 0x95, 0x42, 0xC9, 0x99, 0x42,
+	0xC9, 0x9B, 0x42, 0xC9, 0x9C, 0x42, 0xC9, 0x9F,
+	0x42, 0xC9, 0xA1, 0x42, 0xC9, 0xA3, 0x42, 0xC9,
+	0xA5, 0x42, 0xC9, 0xA6, 0x42, 0xC9, 0xA8, 0x42,
+	0xC9, 0xA9, 0x42, 0xC9, 0xAA, 0x42, 0xC9, 0xAB,
+	0x42, 0xC9, 0xAD, 0x42, 0xC9, 0xAF, 0x42, 0xC9,
+	0xB0, 0x42, 0xC9, 0xB1, 0x42, 0xC9, 0xB2, 0x42,
+	0xC9, 0xB3, 0x42, 0xC9, 0xB4, 0x42, 0xC9, 0xB5,
+	// Bytes 140 - 17f
+	0x42, 0xC9, 0xB8, 0x42, 0xC9, 0xB9, 0x42, 0xC9,
+	0xBB, 0x42, 0xCA, 0x81, 0x42, 0xCA, 0x82, 0x42,
+	0xCA, 0x83, 0x42, 0xCA, 0x89, 0x42, 0xCA, 0x8A,
+	0x42, 0xCA, 0x8B, 0x42, 0xCA, 0x8C, 0x42, 0xCA,
+	0x90, 0x42, 0xCA, 0x91, 0x42, 0xCA, 0x92, 0x42,
+	0xCA, 0x95, 0x42, 0xCA, 0x9D, 0x42, 0xCA, 0x9F,
+	0x42, 0xCA, 0xB9, 0x42, 0xCE, 0x91, 0x42, 0xCE,
+	0x92, 0x42, 0xCE, 0x93, 0x42, 0xCE, 0x94, 0x42,
+	// Bytes 180 - 1bf
+	0xCE, 0x95, 0x42, 0xCE, 0x96, 0x42, 0xCE, 0x97,
+	0x42, 0xCE, 0x98, 0x42, 0xCE, 0x99, 0x42, 0xCE,
+	0x9A, 0x42, 0xCE, 0x9B, 0x42, 0xCE, 0x9C, 0x42,
+	0xCE, 0x9D, 0x42, 0xCE, 0x9E, 0x42, 0xCE, 0x9F,
+	0x42, 0xCE, 0xA0, 0x42, 0xCE, 0xA1, 0x42, 0xCE,
+	0xA3, 0x42, 0xCE, 0xA4, 0x42, 0xCE, 0xA5, 0x42,
+	0xCE, 0xA6, 0x42, 0xCE, 0xA7, 0x42, 0xCE, 0xA8,
+	0x42, 0xCE, 0xA9, 0x42, 0xCE, 0xB1, 0x42, 0xCE,
+	// Bytes 1c0 - 1ff
+	0xB2, 0x42, 0xCE, 0xB3, 0x42, 0xCE, 0xB4, 0x42,
+	0xCE, 0xB5, 0x42, 0xCE, 0xB6, 0x42, 0xCE, 0xB7,
+	0x42, 0xCE, 0xB8, 0x42, 0xCE, 0xB9, 0x42, 0xCE,
+	0xBA, 0x42, 0xCE, 0xBB, 0x42, 0xCE, 0xBC, 0x42,
+	0xCE, 0xBD, 0x42, 0xCE, 0xBE, 0x42, 0xCE, 0xBF,
+	0x42, 0xCF, 0x80, 0x42, 0xCF, 0x81, 0x42, 0xCF,
+	0x82, 0x42, 0xCF, 0x83, 0x42, 0xCF, 0x84, 0x42,
+	0xCF, 0x85, 0x42, 0xCF, 0x86, 0x42, 0xCF, 0x87,
+	// Bytes 200 - 23f
+	0x42, 0xCF, 0x88, 0x42, 0xCF, 0x89, 0x42, 0xCF,
+	0x9C, 0x42, 0xCF, 0x9D, 0x42, 0xD0, 0xBD, 0x42,
+	0xD1, 0x8A, 0x42, 0xD1, 0x8C, 0x42, 0xD7, 0x90,
+	0x42, 0xD7, 0x91, 0x42, 0xD7, 0x92, 0x42, 0xD7,
+	0x93, 0x42, 0xD7, 0x94, 0x42, 0xD7, 0x9B, 0x42,
+	0xD7, 0x9C, 0x42, 0xD7, 0x9D, 0x42, 0xD7, 0xA2,
+	0x42, 0xD7, 0xA8, 0x42, 0xD7, 0xAA, 0x42, 0xD8,
+	0xA1, 0x42, 0xD8, 0xA7, 0x42, 0xD8, 0xA8, 0x42,
+	// Bytes 240 - 27f
+	0xD8, 0xA9, 0x42, 0xD8, 0xAA, 0x42, 0xD8, 0xAB,
+	0x42, 0xD8, 0xAC, 0x42, 0xD8, 0xAD, 0x42, 0xD8,
+	0xAE, 0x42, 0xD8, 0xAF, 0x42, 0xD8, 0xB0, 0x42,
+	0xD8, 0xB1, 0x42, 0xD8, 0xB2, 0x42, 0xD8, 0xB3,
+	0x42, 0xD8, 0xB4, 0x42, 0xD8, 0xB5, 0x42, 0xD8,
+	0xB6, 0x42, 0xD8, 0xB7, 0x42, 0xD8, 0xB8, 0x42,
+	0xD8, 0xB9, 0x42, 0xD8, 0xBA, 0x42, 0xD9, 0x81,
+	0x42, 0xD9, 0x82, 0x42, 0xD9, 0x83, 0x42, 0xD9,
+	// Bytes 280 - 2bf
+	0x84, 0x42, 0xD9, 0x85, 0x42, 0xD9, 0x86, 0x42,
+	0xD9, 0x87, 0x42, 0xD9, 0x88, 0x42, 0xD9, 0x89,
+	0x42, 0xD9, 0x8A, 0x42, 0xD9, 0xAE, 0x42, 0xD9,
+	0xAF, 0x42, 0xD9, 0xB1, 0x42, 0xD9, 0xB9, 0x42,
+	0xD9, 0xBA, 0x42, 0xD9, 0xBB, 0x42, 0xD9, 0xBE,
+	0x42, 0xD9, 0xBF, 0x42, 0xDA, 0x80, 0x42, 0xDA,
+	0x83, 0x42, 0xDA, 0x84, 0x42, 0xDA, 0x86, 0x42,
+	0xDA, 0x87, 0x42, 0xDA, 0x88, 0x42, 0xDA, 0x8C,
+	// Bytes 2c0 - 2ff
+	0x42, 0xDA, 0x8D, 0x42, 0xDA, 0x8E, 0x42, 0xDA,
+	0x91, 0x42, 0xDA, 0x98, 0x42, 0xDA, 0xA1, 0x42,
+	0xDA, 0xA4, 0x42, 0xDA, 0xA6, 0x42, 0xDA, 0xA9,
+	0x42, 0xDA, 0xAD, 0x42, 0xDA, 0xAF, 0x42, 0xDA,
+	0xB1, 0x42, 0xDA, 0xB3, 0x42, 0xDA, 0xBA, 0x42,
+	0xDA, 0xBB, 0x42, 0xDA, 0xBE, 0x42, 0xDB, 0x81,
+	0x42, 0xDB, 0x85, 0x42, 0xDB, 0x86, 0x42, 0xDB,
+	0x87, 0x42, 0xDB, 0x88, 0x42, 0xDB, 0x89, 0x42,
+	// Bytes 300 - 33f
+	0xDB, 0x8B, 0x42, 0xDB, 0x8C, 0x42, 0xDB, 0x90,
+	0x42, 0xDB, 0x92, 0x43, 0xE0, 0xBC, 0x8B, 0x43,
+	0xE1, 0x83, 0x9C, 0x43, 0xE1, 0x84, 0x80, 0x43,
+	0xE1, 0x84, 0x81, 0x43, 0xE1, 0x84, 0x82, 0x43,
+	0xE1, 0x84, 0x83, 0x43, 0xE1, 0x84, 0x84, 0x43,
+	0xE1, 0x84, 0x85, 0x43, 0xE1, 0x84, 0x86, 0x43,
+	0xE1, 0x84, 0x87, 0x43, 0xE1, 0x84, 0x88, 0x43,
+	0xE1, 0x84, 0x89, 0x43, 0xE1, 0x84, 0x8A, 0x43,
+	// Bytes 340 - 37f
+	0xE1, 0x84, 0x8B, 0x43, 0xE1, 0x84, 0x8C, 0x43,
+	0xE1, 0x84, 0x8D, 0x43, 0xE1, 0x84, 0x8E, 0x43,
+	0xE1, 0x84, 0x8F, 0x43, 0xE1, 0x84, 0x90, 0x43,
+	0xE1, 0x84, 0x91, 0x43, 0xE1, 0x84, 0x92, 0x43,
+	0xE1, 0x84, 0x94, 0x43, 0xE1, 0x84, 0x95, 0x43,
+	0xE1, 0x84, 0x9A, 0x43, 0xE1, 0x84, 0x9C, 0x43,
+	0xE1, 0x84, 0x9D, 0x43, 0xE1, 0x84, 0x9E, 0x43,
+	0xE1, 0x84, 0xA0, 0x43, 0xE1, 0x84, 0xA1, 0x43,
+	// Bytes 380 - 3bf
+	0xE1, 0x84, 0xA2, 0x43, 0xE1, 0x84, 0xA3, 0x43,
+	0xE1, 0x84, 0xA7, 0x43, 0xE1, 0x84, 0xA9, 0x43,
+	0xE1, 0x84, 0xAB, 0x43, 0xE1, 0x84, 0xAC, 0x43,
+	0xE1, 0x84, 0xAD, 0x43, 0xE1, 0x84, 0xAE, 0x43,
+	0xE1, 0x84, 0xAF, 0x43, 0xE1, 0x84, 0xB2, 0x43,
+	0xE1, 0x84, 0xB6, 0x43, 0xE1, 0x85, 0x80, 0x43,
+	0xE1, 0x85, 0x87, 0x43, 0xE1, 0x85, 0x8C, 0x43,
+	0xE1, 0x85, 0x97, 0x43, 0xE1, 0x85, 0x98, 0x43,
+	// Bytes 3c0 - 3ff
+	0xE1, 0x85, 0x99, 0x43, 0xE1, 0x85, 0xA0, 0x43,
+	0xE1, 0x86, 0x84, 0x43, 0xE1, 0x86, 0x85, 0x43,
+	0xE1, 0x86, 0x88, 0x43, 0xE1, 0x86, 0x91, 0x43,
+	0xE1, 0x86, 0x92, 0x43, 0xE1, 0x86, 0x94, 0x43,
+	0xE1, 0x86, 0x9E, 0x43, 0xE1, 0x86, 0xA1, 0x43,
+	0xE1, 0x87, 0x87, 0x43, 0xE1, 0x87, 0x88, 0x43,
+	0xE1, 0x87, 0x8C, 0x43, 0xE1, 0x87, 0x8E, 0x43,
+	0xE1, 0x87, 0x93, 0x43, 0xE1, 0x87, 0x97, 0x43,
+	// Bytes 400 - 43f
+	0xE1, 0x87, 0x99, 0x43, 0xE1, 0x87, 0x9D, 0x43,
+	0xE1, 0x87, 0x9F, 0x43, 0xE1, 0x87, 0xB1, 0x43,
+	0xE1, 0x87, 0xB2, 0x43, 0xE1, 0xB4, 0x82, 0x43,
+	0xE1, 0xB4, 0x96, 0x43, 0xE1, 0xB4, 0x97, 0x43,
+	0xE1, 0xB4, 0x9C, 0x43, 0xE1, 0xB4, 0x9D, 0x43,
+	0xE1, 0xB4, 0xA5, 0x43, 0xE1, 0xB5, 0xBB, 0x43,
+	0xE1, 0xB6, 0x85, 0x43, 0xE2, 0x80, 0x82, 0x43,
+	0xE2, 0x80, 0x83, 0x43, 0xE2, 0x80, 0x90, 0x43,
+	// Bytes 440 - 47f
+	0xE2, 0x80, 0x93, 0x43, 0xE2, 0x80, 0x94, 0x43,
+	0xE2, 0x82, 0xA9, 0x43, 0xE2, 0x86, 0x90, 0x43,
+	0xE2, 0x86, 0x91, 0x43, 0xE2, 0x86, 0x92, 0x43,
+	0xE2, 0x86, 0x93, 0x43, 0xE2, 0x88, 0x82, 0x43,
+	0xE2, 0x88, 0x87, 0x43, 0xE2, 0x88, 0x91, 0x43,
+	0xE2, 0x88, 0x92, 0x43, 0xE2, 0x94, 0x82, 0x43,
+	0xE2, 0x96, 0xA0, 0x43, 0xE2, 0x97, 0x8B, 0x43,
+	0xE2, 0xA6, 0x85, 0x43, 0xE2, 0xA6, 0x86, 0x43,
+	// Bytes 480 - 4bf
+	0xE2, 0xB5, 0xA1, 0x43, 0xE3, 0x80, 0x81, 0x43,
+	0xE3, 0x80, 0x82, 0x43, 0xE3, 0x80, 0x88, 0x43,
+	0xE3, 0x80, 0x89, 0x43, 0xE3, 0x80, 0x8A, 0x43,
+	0xE3, 0x80, 0x8B, 0x43, 0xE3, 0x80, 0x8C, 0x43,
+	0xE3, 0x80, 0x8D, 0x43, 0xE3, 0x80, 0x8E, 0x43,
+	0xE3, 0x80, 0x8F, 0x43, 0xE3, 0x80, 0x90, 0x43,
+	0xE3, 0x80, 0x91, 0x43, 0xE3, 0x80, 0x92, 0x43,
+	0xE3, 0x80, 0x94, 0x43, 0xE3, 0x80, 0x95, 0x43,
+	// Bytes 4c0 - 4ff
+	0xE3, 0x80, 0x96, 0x43, 0xE3, 0x80, 0x97, 0x43,
+	0xE3, 0x82, 0xA1, 0x43, 0xE3, 0x82, 0xA2, 0x43,
+	0xE3, 0x82, 0xA3, 0x43, 0xE3, 0x82, 0xA4, 0x43,
+	0xE3, 0x82, 0xA5, 0x43, 0xE3, 0x82, 0xA6, 0x43,
+	0xE3, 0x82, 0xA7, 0x43, 0xE3, 0x82, 0xA8, 0x43,
+	0xE3, 0x82, 0xA9, 0x43, 0xE3, 0x82, 0xAA, 0x43,
+	0xE3, 0x82, 0xAB, 0x43, 0xE3, 0x82, 0xAD, 0x43,
+	0xE3, 0x82, 0xAF, 0x43, 0xE3, 0x82, 0xB1, 0x43,
+	// Bytes 500 - 53f
+	0xE3, 0x82, 0xB3, 0x43, 0xE3, 0x82, 0xB5, 0x43,
+	0xE3, 0x82, 0xB7, 0x43, 0xE3, 0x82, 0xB9, 0x43,
+	0xE3, 0x82, 0xBB, 0x43, 0xE3, 0x82, 0xBD, 0x43,
+	0xE3, 0x82, 0xBF, 0x43, 0xE3, 0x83, 0x81, 0x43,
+	0xE3, 0x83, 0x83, 0x43, 0xE3, 0x83, 0x84, 0x43,
+	0xE3, 0x83, 0x86, 0x43, 0xE3, 0x83, 0x88, 0x43,
+	0xE3, 0x83, 0x8A, 0x43, 0xE3, 0x83, 0x8B, 0x43,
+	0xE3, 0x83, 0x8C, 0x43, 0xE3, 0x83, 0x8D, 0x43,
+	// Bytes 540 - 57f
+	0xE3, 0x83, 0x8E, 0x43, 0xE3, 0x83, 0x8F, 0x43,
+	0xE3, 0x83, 0x92, 0x43, 0xE3, 0x83, 0x95, 0x43,
+	0xE3, 0x83, 0x98, 0x43, 0xE3, 0x83, 0x9B, 0x43,
+	0xE3, 0x83, 0x9E, 0x43, 0xE3, 0x83, 0x9F, 0x43,
+	0xE3, 0x83, 0xA0, 0x43, 0xE3, 0x83, 0xA1, 0x43,
+	0xE3, 0x83, 0xA2, 0x43, 0xE3, 0x83, 0xA3, 0x43,
+	0xE3, 0x83, 0xA4, 0x43, 0xE3, 0x83, 0xA5, 0x43,
+	0xE3, 0x83, 0xA6, 0x43, 0xE3, 0x83, 0xA7, 0x43,
+	// Bytes 580 - 5bf
+	0xE3, 0x83, 0xA8, 0x43, 0xE3, 0x83, 0xA9, 0x43,
+	0xE3, 0x83, 0xAA, 0x43, 0xE3, 0x83, 0xAB, 0x43,
+	0xE3, 0x83, 0xAC, 0x43, 0xE3, 0x83, 0xAD, 0x43,
+	0xE3, 0x83, 0xAF, 0x43, 0xE3, 0x83, 0xB0, 0x43,
+	0xE3, 0x83, 0xB1, 0x43, 0xE3, 0x83, 0xB2, 0x43,
+	0xE3, 0x83, 0xB3, 0x43, 0xE3, 0x83, 0xBB, 0x43,
+	0xE3, 0x83, 0xBC, 0x43, 0xE3, 0x92, 0x9E, 0x43,
+	0xE3, 0x92, 0xB9, 0x43, 0xE3, 0x92, 0xBB, 0x43,
+	// Bytes 5c0 - 5ff
+	0xE3, 0x93, 0x9F, 0x43, 0xE3, 0x94, 0x95, 0x43,
+	0xE3, 0x9B, 0xAE, 0x43, 0xE3, 0x9B, 0xBC, 0x43,
+	0xE3, 0x9E, 0x81, 0x43, 0xE3, 0xA0, 0xAF, 0x43,
+	0xE3, 0xA1, 0xA2, 0x43, 0xE3, 0xA1, 0xBC, 0x43,
+	0xE3, 0xA3, 0x87, 0x43, 0xE3, 0xA3, 0xA3, 0x43,
+	0xE3, 0xA4, 0x9C, 0x43, 0xE3, 0xA4, 0xBA, 0x43,
+	0xE3, 0xA8, 0xAE, 0x43, 0xE3, 0xA9, 0xAC, 0x43,
+	0xE3, 0xAB, 0xA4, 0x43, 0xE3, 0xAC, 0x88, 0x43,
+	// Bytes 600 - 63f
+	0xE3, 0xAC, 0x99, 0x43, 0xE3, 0xAD, 0x89, 0x43,
+	0xE3, 0xAE, 0x9D, 0x43, 0xE3, 0xB0, 0x98, 0x43,
+	0xE3, 0xB1, 0x8E, 0x43, 0xE3, 0xB4, 0xB3, 0x43,
+	0xE3, 0xB6, 0x96, 0x43, 0xE3, 0xBA, 0xAC, 0x43,
+	0xE3, 0xBA, 0xB8, 0x43, 0xE3, 0xBC, 0x9B, 0x43,
+	0xE3, 0xBF, 0xBC, 0x43, 0xE4, 0x80, 0x88, 0x43,
+	0xE4, 0x80, 0x98, 0x43, 0xE4, 0x80, 0xB9, 0x43,
+	0xE4, 0x81, 0x86, 0x43, 0xE4, 0x82, 0x96, 0x43,
+	// Bytes 640 - 67f
+	0xE4, 0x83, 0xA3, 0x43, 0xE4, 0x84, 0xAF, 0x43,
+	0xE4, 0x88, 0x82, 0x43, 0xE4, 0x88, 0xA7, 0x43,
+	0xE4, 0x8A, 0xA0, 0x43, 0xE4, 0x8C, 0x81, 0x43,
+	0xE4, 0x8C, 0xB4, 0x43, 0xE4, 0x8D, 0x99, 0x43,
+	0xE4, 0x8F, 0x95, 0x43, 0xE4, 0x8F, 0x99, 0x43,
+	0xE4, 0x90, 0x8B, 0x43, 0xE4, 0x91, 0xAB, 0x43,
+	0xE4, 0x94, 0xAB, 0x43, 0xE4, 0x95, 0x9D, 0x43,
+	0xE4, 0x95, 0xA1, 0x43, 0xE4, 0x95, 0xAB, 0x43,
+	// Bytes 680 - 6bf
+	0xE4, 0x97, 0x97, 0x43, 0xE4, 0x97, 0xB9, 0x43,
+	0xE4, 0x98, 0xB5, 0x43, 0xE4, 0x9A, 0xBE, 0x43,
+	0xE4, 0x9B, 0x87, 0x43, 0xE4, 0xA6, 0x95, 0x43,
+	0xE4, 0xA7, 0xA6, 0x43, 0xE4, 0xA9, 0xAE, 0x43,
+	0xE4, 0xA9, 0xB6, 0x43, 0xE4, 0xAA, 0xB2, 0x43,
+	0xE4, 0xAC, 0xB3, 0x43, 0xE4, 0xAF, 0x8E, 0x43,
+	0xE4, 0xB3, 0x8E, 0x43, 0xE4, 0xB3, 0xAD, 0x43,
+	0xE4, 0xB3, 0xB8, 0x43, 0xE4, 0xB5, 0x96, 0x43,
+	// Bytes 6c0 - 6ff
+	0xE4, 0xB8, 0x80, 0x43, 0xE4, 0xB8, 0x81, 0x43,
+	0xE4, 0xB8, 0x83, 0x43, 0xE4, 0xB8, 0x89, 0x43,
+	0xE4, 0xB8, 0x8A, 0x43, 0xE4, 0xB8, 0x8B, 0x43,
+	0xE4, 0xB8, 0x8D, 0x43, 0xE4, 0xB8, 0x99, 0x43,
+	0xE4, 0xB8, 0xA6, 0x43, 0xE4, 0xB8, 0xA8, 0x43,
+	0xE4, 0xB8, 0xAD, 0x43, 0xE4, 0xB8, 0xB2, 0x43,
+	0xE4, 0xB8, 0xB6, 0x43, 0xE4, 0xB8, 0xB8, 0x43,
+	0xE4, 0xB8, 0xB9, 0x43, 0xE4, 0xB8, 0xBD, 0x43,
+	// Bytes 700 - 73f
+	0xE4, 0xB8, 0xBF, 0x43, 0xE4, 0xB9, 0x81, 0x43,
+	0xE4, 0xB9, 0x99, 0x43, 0xE4, 0xB9, 0x9D, 0x43,
+	0xE4, 0xBA, 0x82, 0x43, 0xE4, 0xBA, 0x85, 0x43,
+	0xE4, 0xBA, 0x86, 0x43, 0xE4, 0xBA, 0x8C, 0x43,
+	0xE4, 0xBA, 0x94, 0x43, 0xE4, 0xBA, 0xA0, 0x43,
+	0xE4, 0xBA, 0xA4, 0x43, 0xE4, 0xBA, 0xAE, 0x43,
+	0xE4, 0xBA, 0xBA, 0x43, 0xE4, 0xBB, 0x80, 0x43,
+	0xE4, 0xBB, 0x8C, 0x43, 0xE4, 0xBB, 0xA4, 0x43,
+	// Bytes 740 - 77f
+	0xE4, 0xBC, 0x81, 0x43, 0xE4, 0xBC, 0x91, 0x43,
+	0xE4, 0xBD, 0xA0, 0x43, 0xE4, 0xBE, 0x80, 0x43,
+	0xE4, 0xBE, 0x86, 0x43, 0xE4, 0xBE, 0x8B, 0x43,
+	0xE4, 0xBE, 0xAE, 0x43, 0xE4, 0xBE, 0xBB, 0x43,
+	0xE4, 0xBE, 0xBF, 0x43, 0xE5, 0x80, 0x82, 0x43,
+	0xE5, 0x80, 0xAB, 0x43, 0xE5, 0x81, 0xBA, 0x43,
+	0xE5, 0x82, 0x99, 0x43, 0xE5, 0x83, 0x8F, 0x43,
+	0xE5, 0x83, 0x9A, 0x43, 0xE5, 0x83, 0xA7, 0x43,
+	// Bytes 780 - 7bf
+	0xE5, 0x84, 0xAA, 0x43, 0xE5, 0x84, 0xBF, 0x43,
+	0xE5, 0x85, 0x80, 0x43, 0xE5, 0x85, 0x85, 0x43,
+	0xE5, 0x85, 0x8D, 0x43, 0xE5, 0x85, 0x94, 0x43,
+	0xE5, 0x85, 0xA4, 0x43, 0xE5, 0x85, 0xA5, 0x43,
+	0xE5, 0x85, 0xA7, 0x43, 0xE5, 0x85, 0xA8, 0x43,
+	0xE5, 0x85, 0xA9, 0x43, 0xE5, 0x85, 0xAB, 0x43,
+	0xE5, 0x85, 0xAD, 0x43, 0xE5, 0x85, 0xB7, 0x43,
+	0xE5, 0x86, 0x80, 0x43, 0xE5, 0x86, 0x82, 0x43,
+	// Bytes 7c0 - 7ff
+	0xE5, 0x86, 0x8D, 0x43, 0xE5, 0x86, 0x92, 0x43,
+	0xE5, 0x86, 0x95, 0x43, 0xE5, 0x86, 0x96, 0x43,
+	0xE5, 0x86, 0x97, 0x43, 0xE5, 0x86, 0x99, 0x43,
+	0xE5, 0x86, 0xA4, 0x43, 0xE5, 0x86, 0xAB, 0x43,
+	0xE5, 0x86, 0xAC, 0x43, 0xE5, 0x86, 0xB5, 0x43,
+	0xE5, 0x86, 0xB7, 0x43, 0xE5, 0x87, 0x89, 0x43,
+	0xE5, 0x87, 0x8C, 0x43, 0xE5, 0x87, 0x9C, 0x43,
+	0xE5, 0x87, 0x9E, 0x43, 0xE5, 0x87, 0xA0, 0x43,
+	// Bytes 800 - 83f
+	0xE5, 0x87, 0xB5, 0x43, 0xE5, 0x88, 0x80, 0x43,
+	0xE5, 0x88, 0x83, 0x43, 0xE5, 0x88, 0x87, 0x43,
+	0xE5, 0x88, 0x97, 0x43, 0xE5, 0x88, 0x9D, 0x43,
+	0xE5, 0x88, 0xA9, 0x43, 0xE5, 0x88, 0xBA, 0x43,
+	0xE5, 0x88, 0xBB, 0x43, 0xE5, 0x89, 0x86, 0x43,
+	0xE5, 0x89, 0x8D, 0x43, 0xE5, 0x89, 0xB2, 0x43,
+	0xE5, 0x89, 0xB7, 0x43, 0xE5, 0x8A, 0x89, 0x43,
+	0xE5, 0x8A, 0x9B, 0x43, 0xE5, 0x8A, 0xA3, 0x43,
+	// Bytes 840 - 87f
+	0xE5, 0x8A, 0xB3, 0x43, 0xE5, 0x8A, 0xB4, 0x43,
+	0xE5, 0x8B, 0x87, 0x43, 0xE5, 0x8B, 0x89, 0x43,
+	0xE5, 0x8B, 0x92, 0x43, 0xE5, 0x8B, 0x9E, 0x43,
+	0xE5, 0x8B, 0xA4, 0x43, 0xE5, 0x8B, 0xB5, 0x43,
+	0xE5, 0x8B, 0xB9, 0x43, 0xE5, 0x8B, 0xBA, 0x43,
+	0xE5, 0x8C, 0x85, 0x43, 0xE5, 0x8C, 0x86, 0x43,
+	0xE5, 0x8C, 0x95, 0x43, 0xE5, 0x8C, 0x97, 0x43,
+	0xE5, 0x8C, 0x9A, 0x43, 0xE5, 0x8C, 0xB8, 0x43,
+	// Bytes 880 - 8bf
+	0xE5, 0x8C, 0xBB, 0x43, 0xE5, 0x8C, 0xBF, 0x43,
+	0xE5, 0x8D, 0x81, 0x43, 0xE5, 0x8D, 0x84, 0x43,
+	0xE5, 0x8D, 0x85, 0x43, 0xE5, 0x8D, 0x89, 0x43,
+	0xE5, 0x8D, 0x91, 0x43, 0xE5, 0x8D, 0x94, 0x43,
+	0xE5, 0x8D, 0x9A, 0x43, 0xE5, 0x8D, 0x9C, 0x43,
+	0xE5, 0x8D, 0xA9, 0x43, 0xE5, 0x8D, 0xB0, 0x43,
+	0xE5, 0x8D, 0xB3, 0x43, 0xE5, 0x8D, 0xB5, 0x43,
+	0xE5, 0x8D, 0xBD, 0x43, 0xE5, 0x8D, 0xBF, 0x43,
+	// Bytes 8c0 - 8ff
+	0xE5, 0x8E, 0x82, 0x43, 0xE5, 0x8E, 0xB6, 0x43,
+	0xE5, 0x8F, 0x83, 0x43, 0xE5, 0x8F, 0x88, 0x43,
+	0xE5, 0x8F, 0x8A, 0x43, 0xE5, 0x8F, 0x8C, 0x43,
+	0xE5, 0x8F, 0x9F, 0x43, 0xE5, 0x8F, 0xA3, 0x43,
+	0xE5, 0x8F, 0xA5, 0x43, 0xE5, 0x8F, 0xAB, 0x43,
+	0xE5, 0x8F, 0xAF, 0x43, 0xE5, 0x8F, 0xB1, 0x43,
+	0xE5, 0x8F, 0xB3, 0x43, 0xE5, 0x90, 0x86, 0x43,
+	0xE5, 0x90, 0x88, 0x43, 0xE5, 0x90, 0x8D, 0x43,
+	// Bytes 900 - 93f
+	0xE5, 0x90, 0x8F, 0x43, 0xE5, 0x90, 0x9D, 0x43,
+	0xE5, 0x90, 0xB8, 0x43, 0xE5, 0x90, 0xB9, 0x43,
+	0xE5, 0x91, 0x82, 0x43, 0xE5, 0x91, 0x88, 0x43,
+	0xE5, 0x91, 0xA8, 0x43, 0xE5, 0x92, 0x9E, 0x43,
+	0xE5, 0x92, 0xA2, 0x43, 0xE5, 0x92, 0xBD, 0x43,
+	0xE5, 0x93, 0xB6, 0x43, 0xE5, 0x94, 0x90, 0x43,
+	0xE5, 0x95, 0x8F, 0x43, 0xE5, 0x95, 0x93, 0x43,
+	0xE5, 0x95, 0x95, 0x43, 0xE5, 0x95, 0xA3, 0x43,
+	// Bytes 940 - 97f
+	0xE5, 0x96, 0x84, 0x43, 0xE5, 0x96, 0x87, 0x43,
+	0xE5, 0x96, 0x99, 0x43, 0xE5, 0x96, 0x9D, 0x43,
+	0xE5, 0x96, 0xAB, 0x43, 0xE5, 0x96, 0xB3, 0x43,
+	0xE5, 0x96, 0xB6, 0x43, 0xE5, 0x97, 0x80, 0x43,
+	0xE5, 0x97, 0x82, 0x43, 0xE5, 0x97, 0xA2, 0x43,
+	0xE5, 0x98, 0x86, 0x43, 0xE5, 0x99, 0x91, 0x43,
+	0xE5, 0x99, 0xA8, 0x43, 0xE5, 0x99, 0xB4, 0x43,
+	0xE5, 0x9B, 0x97, 0x43, 0xE5, 0x9B, 0x9B, 0x43,
+	// Bytes 980 - 9bf
+	0xE5, 0x9B, 0xB9, 0x43, 0xE5, 0x9C, 0x96, 0x43,
+	0xE5, 0x9C, 0x97, 0x43, 0xE5, 0x9C, 0x9F, 0x43,
+	0xE5, 0x9C, 0xB0, 0x43, 0xE5, 0x9E, 0x8B, 0x43,
+	0xE5, 0x9F, 0x8E, 0x43, 0xE5, 0x9F, 0xB4, 0x43,
+	0xE5, 0xA0, 0x8D, 0x43, 0xE5, 0xA0, 0xB1, 0x43,
+	0xE5, 0xA0, 0xB2, 0x43, 0xE5, 0xA1, 0x80, 0x43,
+	0xE5, 0xA1, 0x9A, 0x43, 0xE5, 0xA1, 0x9E, 0x43,
+	0xE5, 0xA2, 0xA8, 0x43, 0xE5, 0xA2, 0xAC, 0x43,
+	// Bytes 9c0 - 9ff
+	0xE5, 0xA2, 0xB3, 0x43, 0xE5, 0xA3, 0x98, 0x43,
+	0xE5, 0xA3, 0x9F, 0x43, 0xE5, 0xA3, 0xAB, 0x43,
+	0xE5, 0xA3, 0xAE, 0x43, 0xE5, 0xA3, 0xB0, 0x43,
+	0xE5, 0xA3, 0xB2, 0x43, 0xE5, 0xA3, 0xB7, 0x43,
+	0xE5, 0xA4, 0x82, 0x43, 0xE5, 0xA4, 0x86, 0x43,
+	0xE5, 0xA4, 0x8A, 0x43, 0xE5, 0xA4, 0x95, 0x43,
+	0xE5, 0xA4, 0x9A, 0x43, 0xE5, 0xA4, 0x9C, 0x43,
+	0xE5, 0xA4, 0xA2, 0x43, 0xE5, 0xA4, 0xA7, 0x43,
+	// Bytes a00 - a3f
+	0xE5, 0xA4, 0xA9, 0x43, 0xE5, 0xA5, 0x84, 0x43,
+	0xE5, 0xA5, 0x88, 0x43, 0xE5, 0xA5, 0x91, 0x43,
+	0xE5, 0xA5, 0x94, 0x43, 0xE5, 0xA5, 0xA2, 0x43,
+	0xE5, 0xA5, 0xB3, 0x43, 0xE5, 0xA7, 0x98, 0x43,
+	0xE5, 0xA7, 0xAC, 0x43, 0xE5, 0xA8, 0x9B, 0x43,
+	0xE5, 0xA8, 0xA7, 0x43, 0xE5, 0xA9, 0xA2, 0x43,
+	0xE5, 0xA9, 0xA6, 0x43, 0xE5, 0xAA, 0xB5, 0x43,
+	0xE5, 0xAC, 0x88, 0x43, 0xE5, 0xAC, 0xA8, 0x43,
+	// Bytes a40 - a7f
+	0xE5, 0xAC, 0xBE, 0x43, 0xE5, 0xAD, 0x90, 0x43,
+	0xE5, 0xAD, 0x97, 0x43, 0xE5, 0xAD, 0xA6, 0x43,
+	0xE5, 0xAE, 0x80, 0x43, 0xE5, 0xAE, 0x85, 0x43,
+	0xE5, 0xAE, 0x97, 0x43, 0xE5, 0xAF, 0x83, 0x43,
+	0xE5, 0xAF, 0x98, 0x43, 0xE5, 0xAF, 0xA7, 0x43,
+	0xE5, 0xAF, 0xAE, 0x43, 0xE5, 0xAF, 0xB3, 0x43,
+	0xE5, 0xAF, 0xB8, 0x43, 0xE5, 0xAF, 0xBF, 0x43,
+	0xE5, 0xB0, 0x86, 0x43, 0xE5, 0xB0, 0x8F, 0x43,
+	// Bytes a80 - abf
+	0xE5, 0xB0, 0xA2, 0x43, 0xE5, 0xB0, 0xB8, 0x43,
+	0xE5, 0xB0, 0xBF, 0x43, 0xE5, 0xB1, 0xA0, 0x43,
+	0xE5, 0xB1, 0xA2, 0x43, 0xE5, 0xB1, 0xA4, 0x43,
+	0xE5, 0xB1, 0xA5, 0x43, 0xE5, 0xB1, 0xAE, 0x43,
+	0xE5, 0xB1, 0xB1, 0x43, 0xE5, 0xB2, 0x8D, 0x43,
+	0xE5, 0xB3, 0x80, 0x43, 0xE5, 0xB4, 0x99, 0x43,
+	0xE5, 0xB5, 0x83, 0x43, 0xE5, 0xB5, 0x90, 0x43,
+	0xE5, 0xB5, 0xAB, 0x43, 0xE5, 0xB5, 0xAE, 0x43,
+	// Bytes ac0 - aff
+	0xE5, 0xB5, 0xBC, 0x43, 0xE5, 0xB6, 0xB2, 0x43,
+	0xE5, 0xB6, 0xBA, 0x43, 0xE5, 0xB7, 0x9B, 0x43,
+	0xE5, 0xB7, 0xA1, 0x43, 0xE5, 0xB7, 0xA2, 0x43,
+	0xE5, 0xB7, 0xA5, 0x43, 0xE5, 0xB7, 0xA6, 0x43,
+	0xE5, 0xB7, 0xB1, 0x43, 0xE5, 0xB7, 0xBD, 0x43,
+	0xE5, 0xB7, 0xBE, 0x43, 0xE5, 0xB8, 0xA8, 0x43,
+	0xE5, 0xB8, 0xBD, 0x43, 0xE5, 0xB9, 0xA9, 0x43,
+	0xE5, 0xB9, 0xB2, 0x43, 0xE5, 0xB9, 0xB4, 0x43,
+	// Bytes b00 - b3f
+	0xE5, 0xB9, 0xBA, 0x43, 0xE5, 0xB9, 0xBC, 0x43,
+	0xE5, 0xB9, 0xBF, 0x43, 0xE5, 0xBA, 0xA6, 0x43,
+	0xE5, 0xBA, 0xB0, 0x43, 0xE5, 0xBA, 0xB3, 0x43,
+	0xE5, 0xBA, 0xB6, 0x43, 0xE5, 0xBB, 0x89, 0x43,
+	0xE5, 0xBB, 0x8A, 0x43, 0xE5, 0xBB, 0x92, 0x43,
+	0xE5, 0xBB, 0x93, 0x43, 0xE5, 0xBB, 0x99, 0x43,
+	0xE5, 0xBB, 0xAC, 0x43, 0xE5, 0xBB, 0xB4, 0x43,
+	0xE5, 0xBB, 0xBE, 0x43, 0xE5, 0xBC, 0x84, 0x43,
+	// Bytes b40 - b7f
+	0xE5, 0xBC, 0x8B, 0x43, 0xE5, 0xBC, 0x93, 0x43,
+	0xE5, 0xBC, 0xA2, 0x43, 0xE5, 0xBD, 0x90, 0x43,
+	0xE5, 0xBD, 0x93, 0x43, 0xE5, 0xBD, 0xA1, 0x43,
+	0xE5, 0xBD, 0xA2, 0x43, 0xE5, 0xBD, 0xA9, 0x43,
+	0xE5, 0xBD, 0xAB, 0x43, 0xE5, 0xBD, 0xB3, 0x43,
+	0xE5, 0xBE, 0x8B, 0x43, 0xE5, 0xBE, 0x8C, 0x43,
+	0xE5, 0xBE, 0x97, 0x43, 0xE5, 0xBE, 0x9A, 0x43,
+	0xE5, 0xBE, 0xA9, 0x43, 0xE5, 0xBE, 0xAD, 0x43,
+	// Bytes b80 - bbf
+	0xE5, 0xBF, 0x83, 0x43, 0xE5, 0xBF, 0x8D, 0x43,
+	0xE5, 0xBF, 0x97, 0x43, 0xE5, 0xBF, 0xB5, 0x43,
+	0xE5, 0xBF, 0xB9, 0x43, 0xE6, 0x80, 0x92, 0x43,
+	0xE6, 0x80, 0x9C, 0x43, 0xE6, 0x81, 0xB5, 0x43,
+	0xE6, 0x82, 0x81, 0x43, 0xE6, 0x82, 0x94, 0x43,
+	0xE6, 0x83, 0x87, 0x43, 0xE6, 0x83, 0x98, 0x43,
+	0xE6, 0x83, 0xA1, 0x43, 0xE6, 0x84, 0x88, 0x43,
+	0xE6, 0x85, 0x84, 0x43, 0xE6, 0x85, 0x88, 0x43,
+	// Bytes bc0 - bff
+	0xE6, 0x85, 0x8C, 0x43, 0xE6, 0x85, 0x8E, 0x43,
+	0xE6, 0x85, 0xA0, 0x43, 0xE6, 0x85, 0xA8, 0x43,
+	0xE6, 0x85, 0xBA, 0x43, 0xE6, 0x86, 0x8E, 0x43,
+	0xE6, 0x86, 0x90, 0x43, 0xE6, 0x86, 0xA4, 0x43,
+	0xE6, 0x86, 0xAF, 0x43, 0xE6, 0x86, 0xB2, 0x43,
+	0xE6, 0x87, 0x9E, 0x43, 0xE6, 0x87, 0xB2, 0x43,
+	0xE6, 0x87, 0xB6, 0x43, 0xE6, 0x88, 0x80, 0x43,
+	0xE6, 0x88, 0x88, 0x43, 0xE6, 0x88, 0x90, 0x43,
+	// Bytes c00 - c3f
+	0xE6, 0x88, 0x9B, 0x43, 0xE6, 0x88, 0xAE, 0x43,
+	0xE6, 0x88, 0xB4, 0x43, 0xE6, 0x88, 0xB6, 0x43,
+	0xE6, 0x89, 0x8B, 0x43, 0xE6, 0x89, 0x93, 0x43,
+	0xE6, 0x89, 0x9D, 0x43, 0xE6, 0x8A, 0x95, 0x43,
+	0xE6, 0x8A, 0xB1, 0x43, 0xE6, 0x8B, 0x89, 0x43,
+	0xE6, 0x8B, 0x8F, 0x43, 0xE6, 0x8B, 0x93, 0x43,
+	0xE6, 0x8B, 0x94, 0x43, 0xE6, 0x8B, 0xBC, 0x43,
+	0xE6, 0x8B, 0xBE, 0x43, 0xE6, 0x8C, 0x87, 0x43,
+	// Bytes c40 - c7f
+	0xE6, 0x8C, 0xBD, 0x43, 0xE6, 0x8D, 0x90, 0x43,
+	0xE6, 0x8D, 0x95, 0x43, 0xE6, 0x8D, 0xA8, 0x43,
+	0xE6, 0x8D, 0xBB, 0x43, 0xE6, 0x8E, 0x83, 0x43,
+	0xE6, 0x8E, 0xA0, 0x43, 0xE6, 0x8E, 0xA9, 0x43,
+	0xE6, 0x8F, 0x84, 0x43, 0xE6, 0x8F, 0x85, 0x43,
+	0xE6, 0x8F, 0xA4, 0x43, 0xE6, 0x90, 0x9C, 0x43,
+	0xE6, 0x90, 0xA2, 0x43, 0xE6, 0x91, 0x92, 0x43,
+	0xE6, 0x91, 0xA9, 0x43, 0xE6, 0x91, 0xB7, 0x43,
+	// Bytes c80 - cbf
+	0xE6, 0x91, 0xBE, 0x43, 0xE6, 0x92, 0x9A, 0x43,
+	0xE6, 0x92, 0x9D, 0x43, 0xE6, 0x93, 0x84, 0x43,
+	0xE6, 0x94, 0xAF, 0x43, 0xE6, 0x94, 0xB4, 0x43,
+	0xE6, 0x95, 0x8F, 0x43, 0xE6, 0x95, 0x96, 0x43,
+	0xE6, 0x95, 0xAC, 0x43, 0xE6, 0x95, 0xB8, 0x43,
+	0xE6, 0x96, 0x87, 0x43, 0xE6, 0x96, 0x97, 0x43,
+	0xE6, 0x96, 0x99, 0x43, 0xE6, 0x96, 0xA4, 0x43,
+	0xE6, 0x96, 0xB0, 0x43, 0xE6, 0x96, 0xB9, 0x43,
+	// Bytes cc0 - cff
+	0xE6, 0x97, 0x85, 0x43, 0xE6, 0x97, 0xA0, 0x43,
+	0xE6, 0x97, 0xA2, 0x43, 0xE6, 0x97, 0xA3, 0x43,
+	0xE6, 0x97, 0xA5, 0x43, 0xE6, 0x98, 0x93, 0x43,
+	0xE6, 0x98, 0xA0, 0x43, 0xE6, 0x99, 0x89, 0x43,
+	0xE6, 0x99, 0xB4, 0x43, 0xE6, 0x9A, 0x88, 0x43,
+	0xE6, 0x9A, 0x91, 0x43, 0xE6, 0x9A, 0x9C, 0x43,
+	0xE6, 0x9A, 0xB4, 0x43, 0xE6, 0x9B, 0x86, 0x43,
+	0xE6, 0x9B, 0xB0, 0x43, 0xE6, 0x9B, 0xB4, 0x43,
+	// Bytes d00 - d3f
+	0xE6, 0x9B, 0xB8, 0x43, 0xE6, 0x9C, 0x80, 0x43,
+	0xE6, 0x9C, 0x88, 0x43, 0xE6, 0x9C, 0x89, 0x43,
+	0xE6, 0x9C, 0x97, 0x43, 0xE6, 0x9C, 0x9B, 0x43,
+	0xE6, 0x9C, 0xA1, 0x43, 0xE6, 0x9C, 0xA8, 0x43,
+	0xE6, 0x9D, 0x8E, 0x43, 0xE6, 0x9D, 0x93, 0x43,
+	0xE6, 0x9D, 0x96, 0x43, 0xE6, 0x9D, 0x9E, 0x43,
+	0xE6, 0x9D, 0xBB, 0x43, 0xE6, 0x9E, 0x85, 0x43,
+	0xE6, 0x9E, 0x97, 0x43, 0xE6, 0x9F, 0xB3, 0x43,
+	// Bytes d40 - d7f
+	0xE6, 0x9F, 0xBA, 0x43, 0xE6, 0xA0, 0x97, 0x43,
+	0xE6, 0xA0, 0x9F, 0x43, 0xE6, 0xA0, 0xAA, 0x43,
+	0xE6, 0xA1, 0x92, 0x43, 0xE6, 0xA2, 0x81, 0x43,
+	0xE6, 0xA2, 0x85, 0x43, 0xE6, 0xA2, 0x8E, 0x43,
+	0xE6, 0xA2, 0xA8, 0x43, 0xE6, 0xA4, 0x94, 0x43,
+	0xE6, 0xA5, 0x82, 0x43, 0xE6, 0xA6, 0xA3, 0x43,
+	0xE6, 0xA7, 0xAA, 0x43, 0xE6, 0xA8, 0x82, 0x43,
+	0xE6, 0xA8, 0x93, 0x43, 0xE6, 0xAA, 0xA8, 0x43,
+	// Bytes d80 - dbf
+	0xE6, 0xAB, 0x93, 0x43, 0xE6, 0xAB, 0x9B, 0x43,
+	0xE6, 0xAC, 0x84, 0x43, 0xE6, 0xAC, 0xA0, 0x43,
+	0xE6, 0xAC, 0xA1, 0x43, 0xE6, 0xAD, 0x94, 0x43,
+	0xE6, 0xAD, 0xA2, 0x43, 0xE6, 0xAD, 0xA3, 0x43,
+	0xE6, 0xAD, 0xB2, 0x43, 0xE6, 0xAD, 0xB7, 0x43,
+	0xE6, 0xAD, 0xB9, 0x43, 0xE6, 0xAE, 0x9F, 0x43,
+	0xE6, 0xAE, 0xAE, 0x43, 0xE6, 0xAE, 0xB3, 0x43,
+	0xE6, 0xAE, 0xBA, 0x43, 0xE6, 0xAE, 0xBB, 0x43,
+	// Bytes dc0 - dff
+	0xE6, 0xAF, 0x8B, 0x43, 0xE6, 0xAF, 0x8D, 0x43,
+	0xE6, 0xAF, 0x94, 0x43, 0xE6, 0xAF, 0x9B, 0x43,
+	0xE6, 0xB0, 0x8F, 0x43, 0xE6, 0xB0, 0x94, 0x43,
+	0xE6, 0xB0, 0xB4, 0x43, 0xE6, 0xB1, 0x8E, 0x43,
+	0xE6, 0xB1, 0xA7, 0x43, 0xE6, 0xB2, 0x88, 0x43,
+	0xE6, 0xB2, 0xBF, 0x43, 0xE6, 0xB3, 0x8C, 0x43,
+	0xE6, 0xB3, 0x8D, 0x43, 0xE6, 0xB3, 0xA5, 0x43,
+	0xE6, 0xB3, 0xA8, 0x43, 0xE6, 0xB4, 0x96, 0x43,
+	// Bytes e00 - e3f
+	0xE6, 0xB4, 0x9B, 0x43, 0xE6, 0xB4, 0x9E, 0x43,
+	0xE6, 0xB4, 0xB4, 0x43, 0xE6, 0xB4, 0xBE, 0x43,
+	0xE6, 0xB5, 0x81, 0x43, 0xE6, 0xB5, 0xA9, 0x43,
+	0xE6, 0xB5, 0xAA, 0x43, 0xE6, 0xB5, 0xB7, 0x43,
+	0xE6, 0xB5, 0xB8, 0x43, 0xE6, 0xB6, 0x85, 0x43,
+	0xE6, 0xB7, 0x8B, 0x43, 0xE6, 0xB7, 0x9A, 0x43,
+	0xE6, 0xB7, 0xAA, 0x43, 0xE6, 0xB7, 0xB9, 0x43,
+	0xE6, 0xB8, 0x9A, 0x43, 0xE6, 0xB8, 0xAF, 0x43,
+	// Bytes e40 - e7f
+	0xE6, 0xB9, 0xAE, 0x43, 0xE6, 0xBA, 0x80, 0x43,
+	0xE6, 0xBA, 0x9C, 0x43, 0xE6, 0xBA, 0xBA, 0x43,
+	0xE6, 0xBB, 0x87, 0x43, 0xE6, 0xBB, 0x8B, 0x43,
+	0xE6, 0xBB, 0x91, 0x43, 0xE6, 0xBB, 0x9B, 0x43,
+	0xE6, 0xBC, 0x8F, 0x43, 0xE6, 0xBC, 0x94, 0x43,
+	0xE6, 0xBC, 0xA2, 0x43, 0xE6, 0xBC, 0xA3, 0x43,
+	0xE6, 0xBD, 0xAE, 0x43, 0xE6, 0xBF, 0x86, 0x43,
+	0xE6, 0xBF, 0xAB, 0x43, 0xE6, 0xBF, 0xBE, 0x43,
+	// Bytes e80 - ebf
+	0xE7, 0x80, 0x9B, 0x43, 0xE7, 0x80, 0x9E, 0x43,
+	0xE7, 0x80, 0xB9, 0x43, 0xE7, 0x81, 0x8A, 0x43,
+	0xE7, 0x81, 0xAB, 0x43, 0xE7, 0x81, 0xB0, 0x43,
+	0xE7, 0x81, 0xB7, 0x43, 0xE7, 0x81, 0xBD, 0x43,
+	0xE7, 0x82, 0x99, 0x43, 0xE7, 0x82, 0xAD, 0x43,
+	0xE7, 0x83, 0x88, 0x43, 0xE7, 0x83, 0x99, 0x43,
+	0xE7, 0x84, 0xA1, 0x43, 0xE7, 0x85, 0x85, 0x43,
+	0xE7, 0x85, 0x89, 0x43, 0xE7, 0x85, 0xAE, 0x43,
+	// Bytes ec0 - eff
+	0xE7, 0x86, 0x9C, 0x43, 0xE7, 0x87, 0x8E, 0x43,
+	0xE7, 0x87, 0x90, 0x43, 0xE7, 0x88, 0x90, 0x43,
+	0xE7, 0x88, 0x9B, 0x43, 0xE7, 0x88, 0xA8, 0x43,
+	0xE7, 0x88, 0xAA, 0x43, 0xE7, 0x88, 0xAB, 0x43,
+	0xE7, 0x88, 0xB5, 0x43, 0xE7, 0x88, 0xB6, 0x43,
+	0xE7, 0x88, 0xBB, 0x43, 0xE7, 0x88, 0xBF, 0x43,
+	0xE7, 0x89, 0x87, 0x43, 0xE7, 0x89, 0x90, 0x43,
+	0xE7, 0x89, 0x99, 0x43, 0xE7, 0x89, 0x9B, 0x43,
+	// Bytes f00 - f3f
+	0xE7, 0x89, 0xA2, 0x43, 0xE7, 0x89, 0xB9, 0x43,
+	0xE7, 0x8A, 0x80, 0x43, 0xE7, 0x8A, 0x95, 0x43,
+	0xE7, 0x8A, 0xAC, 0x43, 0xE7, 0x8A, 0xAF, 0x43,
+	0xE7, 0x8B, 0x80, 0x43, 0xE7, 0x8B, 0xBC, 0x43,
+	0xE7, 0x8C, 0xAA, 0x43, 0xE7, 0x8D, 0xB5, 0x43,
+	0xE7, 0x8D, 0xBA, 0x43, 0xE7, 0x8E, 0x84, 0x43,
+	0xE7, 0x8E, 0x87, 0x43, 0xE7, 0x8E, 0x89, 0x43,
+	0xE7, 0x8E, 0x8B, 0x43, 0xE7, 0x8E, 0xA5, 0x43,
+	// Bytes f40 - f7f
+	0xE7, 0x8E, 0xB2, 0x43, 0xE7, 0x8F, 0x9E, 0x43,
+	0xE7, 0x90, 0x86, 0x43, 0xE7, 0x90, 0x89, 0x43,
+	0xE7, 0x90, 0xA2, 0x43, 0xE7, 0x91, 0x87, 0x43,
+	0xE7, 0x91, 0x9C, 0x43, 0xE7, 0x91, 0xA9, 0x43,
+	0xE7, 0x91, 0xB1, 0x43, 0xE7, 0x92, 0x85, 0x43,
+	0xE7, 0x92, 0x89, 0x43, 0xE7, 0x92, 0x98, 0x43,
+	0xE7, 0x93, 0x8A, 0x43, 0xE7, 0x93, 0x9C, 0x43,
+	0xE7, 0x93, 0xA6, 0x43, 0xE7, 0x94, 0x86, 0x43,
+	// Bytes f80 - fbf
+	0xE7, 0x94, 0x98, 0x43, 0xE7, 0x94, 0x9F, 0x43,
+	0xE7, 0x94, 0xA4, 0x43, 0xE7, 0x94, 0xA8, 0x43,
+	0xE7, 0x94, 0xB0, 0x43, 0xE7, 0x94, 0xB2, 0x43,
+	0xE7, 0x94, 0xB3, 0x43, 0xE7, 0x94, 0xB7, 0x43,
+	0xE7, 0x94, 0xBB, 0x43, 0xE7, 0x94, 0xBE, 0x43,
+	0xE7, 0x95, 0x99, 0x43, 0xE7, 0x95, 0xA5, 0x43,
+	0xE7, 0x95, 0xB0, 0x43, 0xE7, 0x96, 0x8B, 0x43,
+	0xE7, 0x96, 0x92, 0x43, 0xE7, 0x97, 0xA2, 0x43,
+	// Bytes fc0 - fff
+	0xE7, 0x98, 0x90, 0x43, 0xE7, 0x98, 0x9D, 0x43,
+	0xE7, 0x98, 0x9F, 0x43, 0xE7, 0x99, 0x82, 0x43,
+	0xE7, 0x99, 0xA9, 0x43, 0xE7, 0x99, 0xB6, 0x43,
+	0xE7, 0x99, 0xBD, 0x43, 0xE7, 0x9A, 0xAE, 0x43,
+	0xE7, 0x9A, 0xBF, 0x43, 0xE7, 0x9B, 0x8A, 0x43,
+	0xE7, 0x9B, 0x9B, 0x43, 0xE7, 0x9B, 0xA3, 0x43,
+	0xE7, 0x9B, 0xA7, 0x43, 0xE7, 0x9B, 0xAE, 0x43,
+	0xE7, 0x9B, 0xB4, 0x43, 0xE7, 0x9C, 0x81, 0x43,
+	// Bytes 1000 - 103f
+	0xE7, 0x9C, 0x9E, 0x43, 0xE7, 0x9C, 0x9F, 0x43,
+	0xE7, 0x9D, 0x80, 0x43, 0xE7, 0x9D, 0x8A, 0x43,
+	0xE7, 0x9E, 0x8B, 0x43, 0xE7, 0x9E, 0xA7, 0x43,
+	0xE7, 0x9F, 0x9B, 0x43, 0xE7, 0x9F, 0xA2, 0x43,
+	0xE7, 0x9F, 0xB3, 0x43, 0xE7, 0xA1, 0x8E, 0x43,
+	0xE7, 0xA1, 0xAB, 0x43, 0xE7, 0xA2, 0x8C, 0x43,
+	0xE7, 0xA2, 0x91, 0x43, 0xE7, 0xA3, 0x8A, 0x43,
+	0xE7, 0xA3, 0x8C, 0x43, 0xE7, 0xA3, 0xBB, 0x43,
+	// Bytes 1040 - 107f
+	0xE7, 0xA4, 0xAA, 0x43, 0xE7, 0xA4, 0xBA, 0x43,
+	0xE7, 0xA4, 0xBC, 0x43, 0xE7, 0xA4, 0xBE, 0x43,
+	0xE7, 0xA5, 0x88, 0x43, 0xE7, 0xA5, 0x89, 0x43,
+	0xE7, 0xA5, 0x90, 0x43, 0xE7, 0xA5, 0x96, 0x43,
+	0xE7, 0xA5, 0x9D, 0x43, 0xE7, 0xA5, 0x9E, 0x43,
+	0xE7, 0xA5, 0xA5, 0x43, 0xE7, 0xA5, 0xBF, 0x43,
+	0xE7, 0xA6, 0x81, 0x43, 0xE7, 0xA6, 0x8D, 0x43,
+	0xE7, 0xA6, 0x8E, 0x43, 0xE7, 0xA6, 0x8F, 0x43,
+	// Bytes 1080 - 10bf
+	0xE7, 0xA6, 0xAE, 0x43, 0xE7, 0xA6, 0xB8, 0x43,
+	0xE7, 0xA6, 0xBE, 0x43, 0xE7, 0xA7, 0x8A, 0x43,
+	0xE7, 0xA7, 0x98, 0x43, 0xE7, 0xA7, 0xAB, 0x43,
+	0xE7, 0xA8, 0x9C, 0x43, 0xE7, 0xA9, 0x80, 0x43,
+	0xE7, 0xA9, 0x8A, 0x43, 0xE7, 0xA9, 0x8F, 0x43,
+	0xE7, 0xA9, 0xB4, 0x43, 0xE7, 0xA9, 0xBA, 0x43,
+	0xE7, 0xAA, 0x81, 0x43, 0xE7, 0xAA, 0xB1, 0x43,
+	0xE7, 0xAB, 0x8B, 0x43, 0xE7, 0xAB, 0xAE, 0x43,
+	// Bytes 10c0 - 10ff
+	0xE7, 0xAB, 0xB9, 0x43, 0xE7, 0xAC, 0xA0, 0x43,
+	0xE7, 0xAE, 0x8F, 0x43, 0xE7, 0xAF, 0x80, 0x43,
+	0xE7, 0xAF, 0x86, 0x43, 0xE7, 0xAF, 0x89, 0x43,
+	0xE7, 0xB0, 0xBE, 0x43, 0xE7, 0xB1, 0xA0, 0x43,
+	0xE7, 0xB1, 0xB3, 0x43, 0xE7, 0xB1, 0xBB, 0x43,
+	0xE7, 0xB2, 0x92, 0x43, 0xE7, 0xB2, 0xBE, 0x43,
+	0xE7, 0xB3, 0x92, 0x43, 0xE7, 0xB3, 0x96, 0x43,
+	0xE7, 0xB3, 0xA3, 0x43, 0xE7, 0xB3, 0xA7, 0x43,
+	// Bytes 1100 - 113f
+	0xE7, 0xB3, 0xA8, 0x43, 0xE7, 0xB3, 0xB8, 0x43,
+	0xE7, 0xB4, 0x80, 0x43, 0xE7, 0xB4, 0x90, 0x43,
+	0xE7, 0xB4, 0xA2, 0x43, 0xE7, 0xB4, 0xAF, 0x43,
+	0xE7, 0xB5, 0x82, 0x43, 0xE7, 0xB5, 0x9B, 0x43,
+	0xE7, 0xB5, 0xA3, 0x43, 0xE7, 0xB6, 0xA0, 0x43,
+	0xE7, 0xB6, 0xBE, 0x43, 0xE7, 0xB7, 0x87, 0x43,
+	0xE7, 0xB7, 0xB4, 0x43, 0xE7, 0xB8, 0x82, 0x43,
+	0xE7, 0xB8, 0x89, 0x43, 0xE7, 0xB8, 0xB7, 0x43,
+	// Bytes 1140 - 117f
+	0xE7, 0xB9, 0x81, 0x43, 0xE7, 0xB9, 0x85, 0x43,
+	0xE7, 0xBC, 0xB6, 0x43, 0xE7, 0xBC, 0xBE, 0x43,
+	0xE7, 0xBD, 0x91, 0x43, 0xE7, 0xBD, 0xB2, 0x43,
+	0xE7, 0xBD, 0xB9, 0x43, 0xE7, 0xBD, 0xBA, 0x43,
+	0xE7, 0xBE, 0x85, 0x43, 0xE7, 0xBE, 0x8A, 0x43,
+	0xE7, 0xBE, 0x95, 0x43, 0xE7, 0xBE, 0x9A, 0x43,
+	0xE7, 0xBE, 0xBD, 0x43, 0xE7, 0xBF, 0xBA, 0x43,
+	0xE8, 0x80, 0x81, 0x43, 0xE8, 0x80, 0x85, 0x43,
+	// Bytes 1180 - 11bf
+	0xE8, 0x80, 0x8C, 0x43, 0xE8, 0x80, 0x92, 0x43,
+	0xE8, 0x80, 0xB3, 0x43, 0xE8, 0x81, 0x86, 0x43,
+	0xE8, 0x81, 0xA0, 0x43, 0xE8, 0x81, 0xAF, 0x43,
+	0xE8, 0x81, 0xB0, 0x43, 0xE8, 0x81, 0xBE, 0x43,
+	0xE8, 0x81, 0xBF, 0x43, 0xE8, 0x82, 0x89, 0x43,
+	0xE8, 0x82, 0x8B, 0x43, 0xE8, 0x82, 0xAD, 0x43,
+	0xE8, 0x82, 0xB2, 0x43, 0xE8, 0x84, 0x83, 0x43,
+	0xE8, 0x84, 0xBE, 0x43, 0xE8, 0x87, 0x98, 0x43,
+	// Bytes 11c0 - 11ff
+	0xE8, 0x87, 0xA3, 0x43, 0xE8, 0x87, 0xA8, 0x43,
+	0xE8, 0x87, 0xAA, 0x43, 0xE8, 0x87, 0xAD, 0x43,
+	0xE8, 0x87, 0xB3, 0x43, 0xE8, 0x87, 0xBC, 0x43,
+	0xE8, 0x88, 0x81, 0x43, 0xE8, 0x88, 0x84, 0x43,
+	0xE8, 0x88, 0x8C, 0x43, 0xE8, 0x88, 0x98, 0x43,
+	0xE8, 0x88, 0x9B, 0x43, 0xE8, 0x88, 0x9F, 0x43,
+	0xE8, 0x89, 0xAE, 0x43, 0xE8, 0x89, 0xAF, 0x43,
+	0xE8, 0x89, 0xB2, 0x43, 0xE8, 0x89, 0xB8, 0x43,
+	// Bytes 1200 - 123f
+	0xE8, 0x89, 0xB9, 0x43, 0xE8, 0x8A, 0x8B, 0x43,
+	0xE8, 0x8A, 0x91, 0x43, 0xE8, 0x8A, 0x9D, 0x43,
+	0xE8, 0x8A, 0xB1, 0x43, 0xE8, 0x8A, 0xB3, 0x43,
+	0xE8, 0x8A, 0xBD, 0x43, 0xE8, 0x8B, 0xA5, 0x43,
+	0xE8, 0x8B, 0xA6, 0x43, 0xE8, 0x8C, 0x9D, 0x43,
+	0xE8, 0x8C, 0xA3, 0x43, 0xE8, 0x8C, 0xB6, 0x43,
+	0xE8, 0x8D, 0x92, 0x43, 0xE8, 0x8D, 0x93, 0x43,
+	0xE8, 0x8D, 0xA3, 0x43, 0xE8, 0x8E, 0xAD, 0x43,
+	// Bytes 1240 - 127f
+	0xE8, 0x8E, 0xBD, 0x43, 0xE8, 0x8F, 0x89, 0x43,
+	0xE8, 0x8F, 0x8A, 0x43, 0xE8, 0x8F, 0x8C, 0x43,
+	0xE8, 0x8F, 0x9C, 0x43, 0xE8, 0x8F, 0xA7, 0x43,
+	0xE8, 0x8F, 0xAF, 0x43, 0xE8, 0x8F, 0xB1, 0x43,
+	0xE8, 0x90, 0xBD, 0x43, 0xE8, 0x91, 0x89, 0x43,
+	0xE8, 0x91, 0x97, 0x43, 0xE8, 0x93, 0xAE, 0x43,
+	0xE8, 0x93, 0xB1, 0x43, 0xE8, 0x93, 0xB3, 0x43,
+	0xE8, 0x93, 0xBC, 0x43, 0xE8, 0x94, 0x96, 0x43,
+	// Bytes 1280 - 12bf
+	0xE8, 0x95, 0xA4, 0x43, 0xE8, 0x97, 0x8D, 0x43,
+	0xE8, 0x97, 0xBA, 0x43, 0xE8, 0x98, 0x86, 0x43,
+	0xE8, 0x98, 0x92, 0x43, 0xE8, 0x98, 0xAD, 0x43,
+	0xE8, 0x98, 0xBF, 0x43, 0xE8, 0x99, 0x8D, 0x43,
+	0xE8, 0x99, 0x90, 0x43, 0xE8, 0x99, 0x9C, 0x43,
+	0xE8, 0x99, 0xA7, 0x43, 0xE8, 0x99, 0xA9, 0x43,
+	0xE8, 0x99, 0xAB, 0x43, 0xE8, 0x9A, 0x88, 0x43,
+	0xE8, 0x9A, 0xA9, 0x43, 0xE8, 0x9B, 0xA2, 0x43,
+	// Bytes 12c0 - 12ff
+	0xE8, 0x9C, 0x8E, 0x43, 0xE8, 0x9C, 0xA8, 0x43,
+	0xE8, 0x9D, 0xAB, 0x43, 0xE8, 0x9D, 0xB9, 0x43,
+	0xE8, 0x9E, 0x86, 0x43, 0xE8, 0x9E, 0xBA, 0x43,
+	0xE8, 0x9F, 0xA1, 0x43, 0xE8, 0xA0, 0x81, 0x43,
+	0xE8, 0xA0, 0x9F, 0x43, 0xE8, 0xA1, 0x80, 0x43,
+	0xE8, 0xA1, 0x8C, 0x43, 0xE8, 0xA1, 0xA0, 0x43,
+	0xE8, 0xA1, 0xA3, 0x43, 0xE8, 0xA3, 0x82, 0x43,
+	0xE8, 0xA3, 0x8F, 0x43, 0xE8, 0xA3, 0x97, 0x43,
+	// Bytes 1300 - 133f
+	0xE8, 0xA3, 0x9E, 0x43, 0xE8, 0xA3, 0xA1, 0x43,
+	0xE8, 0xA3, 0xB8, 0x43, 0xE8, 0xA3, 0xBA, 0x43,
+	0xE8, 0xA4, 0x90, 0x43, 0xE8, 0xA5, 0x81, 0x43,
+	0xE8, 0xA5, 0xA4, 0x43, 0xE8, 0xA5, 0xBE, 0x43,
+	0xE8, 0xA6, 0x86, 0x43, 0xE8, 0xA6, 0x8B, 0x43,
+	0xE8, 0xA6, 0x96, 0x43, 0xE8, 0xA7, 0x92, 0x43,
+	0xE8, 0xA7, 0xA3, 0x43, 0xE8, 0xA8, 0x80, 0x43,
+	0xE8, 0xAA, 0xA0, 0x43, 0xE8, 0xAA, 0xAA, 0x43,
+	// Bytes 1340 - 137f
+	0xE8, 0xAA, 0xBF, 0x43, 0xE8, 0xAB, 0x8B, 0x43,
+	0xE8, 0xAB, 0x92, 0x43, 0xE8, 0xAB, 0x96, 0x43,
+	0xE8, 0xAB, 0xAD, 0x43, 0xE8, 0xAB, 0xB8, 0x43,
+	0xE8, 0xAB, 0xBE, 0x43, 0xE8, 0xAC, 0x81, 0x43,
+	0xE8, 0xAC, 0xB9, 0x43, 0xE8, 0xAD, 0x98, 0x43,
+	0xE8, 0xAE, 0x80, 0x43, 0xE8, 0xAE, 0x8A, 0x43,
+	0xE8, 0xB0, 0xB7, 0x43, 0xE8, 0xB1, 0x86, 0x43,
+	0xE8, 0xB1, 0x88, 0x43, 0xE8, 0xB1, 0x95, 0x43,
+	// Bytes 1380 - 13bf
+	0xE8, 0xB1, 0xB8, 0x43, 0xE8, 0xB2, 0x9D, 0x43,
+	0xE8, 0xB2, 0xA1, 0x43, 0xE8, 0xB2, 0xA9, 0x43,
+	0xE8, 0xB2, 0xAB, 0x43, 0xE8, 0xB3, 0x81, 0x43,
+	0xE8, 0xB3, 0x82, 0x43, 0xE8, 0xB3, 0x87, 0x43,
+	0xE8, 0xB3, 0x88, 0x43, 0xE8, 0xB3, 0x93, 0x43,
+	0xE8, 0xB4, 0x88, 0x43, 0xE8, 0xB4, 0x9B, 0x43,
+	0xE8, 0xB5, 0xA4, 0x43, 0xE8, 0xB5, 0xB0, 0x43,
+	0xE8, 0xB5, 0xB7, 0x43, 0xE8, 0xB6, 0xB3, 0x43,
+	// Bytes 13c0 - 13ff
+	0xE8, 0xB6, 0xBC, 0x43, 0xE8, 0xB7, 0x8B, 0x43,
+	0xE8, 0xB7, 0xAF, 0x43, 0xE8, 0xB7, 0xB0, 0x43,
+	0xE8, 0xBA, 0xAB, 0x43, 0xE8, 0xBB, 0x8A, 0x43,
+	0xE8, 0xBB, 0x94, 0x43, 0xE8, 0xBC, 0xA6, 0x43,
+	0xE8, 0xBC, 0xAA, 0x43, 0xE8, 0xBC, 0xB8, 0x43,
+	0xE8, 0xBC, 0xBB, 0x43, 0xE8, 0xBD, 0xA2, 0x43,
+	0xE8, 0xBE, 0x9B, 0x43, 0xE8, 0xBE, 0x9E, 0x43,
+	0xE8, 0xBE, 0xB0, 0x43, 0xE8, 0xBE, 0xB5, 0x43,
+	// Bytes 1400 - 143f
+	0xE8, 0xBE, 0xB6, 0x43, 0xE9, 0x80, 0xA3, 0x43,
+	0xE9, 0x80, 0xB8, 0x43, 0xE9, 0x81, 0x8A, 0x43,
+	0xE9, 0x81, 0xA9, 0x43, 0xE9, 0x81, 0xB2, 0x43,
+	0xE9, 0x81, 0xBC, 0x43, 0xE9, 0x82, 0x8F, 0x43,
+	0xE9, 0x82, 0x91, 0x43, 0xE9, 0x82, 0x94, 0x43,
+	0xE9, 0x83, 0x8E, 0x43, 0xE9, 0x83, 0x9E, 0x43,
+	0xE9, 0x83, 0xB1, 0x43, 0xE9, 0x83, 0xBD, 0x43,
+	0xE9, 0x84, 0x91, 0x43, 0xE9, 0x84, 0x9B, 0x43,
+	// Bytes 1440 - 147f
+	0xE9, 0x85, 0x89, 0x43, 0xE9, 0x85, 0x8D, 0x43,
+	0xE9, 0x85, 0xAA, 0x43, 0xE9, 0x86, 0x99, 0x43,
+	0xE9, 0x86, 0xB4, 0x43, 0xE9, 0x87, 0x86, 0x43,
+	0xE9, 0x87, 0x8C, 0x43, 0xE9, 0x87, 0x8F, 0x43,
+	0xE9, 0x87, 0x91, 0x43, 0xE9, 0x88, 0xB4, 0x43,
+	0xE9, 0x88, 0xB8, 0x43, 0xE9, 0x89, 0xB6, 0x43,
+	0xE9, 0x89, 0xBC, 0x43, 0xE9, 0x8B, 0x97, 0x43,
+	0xE9, 0x8B, 0x98, 0x43, 0xE9, 0x8C, 0x84, 0x43,
+	// Bytes 1480 - 14bf
+	0xE9, 0x8D, 0x8A, 0x43, 0xE9, 0x8F, 0xB9, 0x43,
+	0xE9, 0x90, 0x95, 0x43, 0xE9, 0x95, 0xB7, 0x43,
+	0xE9, 0x96, 0x80, 0x43, 0xE9, 0x96, 0x8B, 0x43,
+	0xE9, 0x96, 0xAD, 0x43, 0xE9, 0x96, 0xB7, 0x43,
+	0xE9, 0x98, 0x9C, 0x43, 0xE9, 0x98, 0xAE, 0x43,
+	0xE9, 0x99, 0x8B, 0x43, 0xE9, 0x99, 0x8D, 0x43,
+	0xE9, 0x99, 0xB5, 0x43, 0xE9, 0x99, 0xB8, 0x43,
+	0xE9, 0x99, 0xBC, 0x43, 0xE9, 0x9A, 0x86, 0x43,
+	// Bytes 14c0 - 14ff
+	0xE9, 0x9A, 0xA3, 0x43, 0xE9, 0x9A, 0xB6, 0x43,
+	0xE9, 0x9A, 0xB7, 0x43, 0xE9, 0x9A, 0xB8, 0x43,
+	0xE9, 0x9A, 0xB9, 0x43, 0xE9, 0x9B, 0x83, 0x43,
+	0xE9, 0x9B, 0xA2, 0x43, 0xE9, 0x9B, 0xA3, 0x43,
+	0xE9, 0x9B, 0xA8, 0x43, 0xE9, 0x9B, 0xB6, 0x43,
+	0xE9, 0x9B, 0xB7, 0x43, 0xE9, 0x9C, 0xA3, 0x43,
+	0xE9, 0x9C, 0xB2, 0x43, 0xE9, 0x9D, 0x88, 0x43,
+	0xE9, 0x9D, 0x91, 0x43, 0xE9, 0x9D, 0x96, 0x43,
+	// Bytes 1500 - 153f
+	0xE9, 0x9D, 0x9E, 0x43, 0xE9, 0x9D, 0xA2, 0x43,
+	0xE9, 0x9D, 0xA9, 0x43, 0xE9, 0x9F, 0x8B, 0x43,
+	0xE9, 0x9F, 0x9B, 0x43, 0xE9, 0x9F, 0xA0, 0x43,
+	0xE9, 0x9F, 0xAD, 0x43, 0xE9, 0x9F, 0xB3, 0x43,
+	0xE9, 0x9F, 0xBF, 0x43, 0xE9, 0xA0, 0x81, 0x43,
+	0xE9, 0xA0, 0x85, 0x43, 0xE9, 0xA0, 0x8B, 0x43,
+	0xE9, 0xA0, 0x98, 0x43, 0xE9, 0xA0, 0xA9, 0x43,
+	0xE9, 0xA0, 0xBB, 0x43, 0xE9, 0xA1, 0x9E, 0x43,
+	// Bytes 1540 - 157f
+	0xE9, 0xA2, 0xA8, 0x43, 0xE9, 0xA3, 0x9B, 0x43,
+	0xE9, 0xA3, 0x9F, 0x43, 0xE9, 0xA3, 0xA2, 0x43,
+	0xE9, 0xA3, 0xAF, 0x43, 0xE9, 0xA3, 0xBC, 0x43,
+	0xE9, 0xA4, 0xA8, 0x43, 0xE9, 0xA4, 0xA9, 0x43,
+	0xE9, 0xA6, 0x96, 0x43, 0xE9, 0xA6, 0x99, 0x43,
+	0xE9, 0xA6, 0xA7, 0x43, 0xE9, 0xA6, 0xAC, 0x43,
+	0xE9, 0xA7, 0x82, 0x43, 0xE9, 0xA7, 0xB1, 0x43,
+	0xE9, 0xA7, 0xBE, 0x43, 0xE9, 0xA9, 0xAA, 0x43,
+	// Bytes 1580 - 15bf
+	0xE9, 0xAA, 0xA8, 0x43, 0xE9, 0xAB, 0x98, 0x43,
+	0xE9, 0xAB, 0x9F, 0x43, 0xE9, 0xAC, 0x92, 0x43,
+	0xE9, 0xAC, 0xA5, 0x43, 0xE9, 0xAC, 0xAF, 0x43,
+	0xE9, 0xAC, 0xB2, 0x43, 0xE9, 0xAC, 0xBC, 0x43,
+	0xE9, 0xAD, 0x9A, 0x43, 0xE9, 0xAD, 0xAF, 0x43,
+	0xE9, 0xB1, 0x80, 0x43, 0xE9, 0xB1, 0x97, 0x43,
+	0xE9, 0xB3, 0xA5, 0x43, 0xE9, 0xB3, 0xBD, 0x43,
+	0xE9, 0xB5, 0xA7, 0x43, 0xE9, 0xB6, 0xB4, 0x43,
+	// Bytes 15c0 - 15ff
+	0xE9, 0xB7, 0xBA, 0x43, 0xE9, 0xB8, 0x9E, 0x43,
+	0xE9, 0xB9, 0xB5, 0x43, 0xE9, 0xB9, 0xBF, 0x43,
+	0xE9, 0xBA, 0x97, 0x43, 0xE9, 0xBA, 0x9F, 0x43,
+	0xE9, 0xBA, 0xA5, 0x43, 0xE9, 0xBA, 0xBB, 0x43,
+	0xE9, 0xBB, 0x83, 0x43, 0xE9, 0xBB, 0x8D, 0x43,
+	0xE9, 0xBB, 0x8E, 0x43, 0xE9, 0xBB, 0x91, 0x43,
+	0xE9, 0xBB, 0xB9, 0x43, 0xE9, 0xBB, 0xBD, 0x43,
+	0xE9, 0xBB, 0xBE, 0x43, 0xE9, 0xBC, 0x85, 0x43,
+	// Bytes 1600 - 163f
+	0xE9, 0xBC, 0x8E, 0x43, 0xE9, 0xBC, 0x8F, 0x43,
+	0xE9, 0xBC, 0x93, 0x43, 0xE9, 0xBC, 0x96, 0x43,
+	0xE9, 0xBC, 0xA0, 0x43, 0xE9, 0xBC, 0xBB, 0x43,
+	0xE9, 0xBD, 0x83, 0x43, 0xE9, 0xBD, 0x8A, 0x43,
+	0xE9, 0xBD, 0x92, 0x43, 0xE9, 0xBE, 0x8D, 0x43,
+	0xE9, 0xBE, 0x8E, 0x43, 0xE9, 0xBE, 0x9C, 0x43,
+	0xE9, 0xBE, 0x9F, 0x43, 0xE9, 0xBE, 0xA0, 0x43,
+	0xEA, 0x9C, 0xA7, 0x43, 0xEA, 0x9D, 0xAF, 0x43,
+	// Bytes 1640 - 167f
+	0xEA, 0xAC, 0xB7, 0x43, 0xEA, 0xAD, 0x92, 0x44,
+	0xF0, 0xA0, 0x84, 0xA2, 0x44, 0xF0, 0xA0, 0x94,
+	0x9C, 0x44, 0xF0, 0xA0, 0x94, 0xA5, 0x44, 0xF0,
+	0xA0, 0x95, 0x8B, 0x44, 0xF0, 0xA0, 0x98, 0xBA,
+	0x44, 0xF0, 0xA0, 0xA0, 0x84, 0x44, 0xF0, 0xA0,
+	0xA3, 0x9E, 0x44, 0xF0, 0xA0, 0xA8, 0xAC, 0x44,
+	0xF0, 0xA0, 0xAD, 0xA3, 0x44, 0xF0, 0xA1, 0x93,
+	0xA4, 0x44, 0xF0, 0xA1, 0x9A, 0xA8, 0x44, 0xF0,
+	// Bytes 1680 - 16bf
+	0xA1, 0x9B, 0xAA, 0x44, 0xF0, 0xA1, 0xA7, 0x88,
+	0x44, 0xF0, 0xA1, 0xAC, 0x98, 0x44, 0xF0, 0xA1,
+	0xB4, 0x8B, 0x44, 0xF0, 0xA1, 0xB7, 0xA4, 0x44,
+	0xF0, 0xA1, 0xB7, 0xA6, 0x44, 0xF0, 0xA2, 0x86,
+	0x83, 0x44, 0xF0, 0xA2, 0x86, 0x9F, 0x44, 0xF0,
+	0xA2, 0x8C, 0xB1, 0x44, 0xF0, 0xA2, 0x9B, 0x94,
+	0x44, 0xF0, 0xA2, 0xA1, 0x84, 0x44, 0xF0, 0xA2,
+	0xA1, 0x8A, 0x44, 0xF0, 0xA2, 0xAC, 0x8C, 0x44,
+	// Bytes 16c0 - 16ff
+	0xF0, 0xA2, 0xAF, 0xB1, 0x44, 0xF0, 0xA3, 0x80,
+	0x8A, 0x44, 0xF0, 0xA3, 0x8A, 0xB8, 0x44, 0xF0,
+	0xA3, 0x8D, 0x9F, 0x44, 0xF0, 0xA3, 0x8E, 0x93,
+	0x44, 0xF0, 0xA3, 0x8E, 0x9C, 0x44, 0xF0, 0xA3,
+	0x8F, 0x83, 0x44, 0xF0, 0xA3, 0x8F, 0x95, 0x44,
+	0xF0, 0xA3, 0x91, 0xAD, 0x44, 0xF0, 0xA3, 0x9A,
+	0xA3, 0x44, 0xF0, 0xA3, 0xA2, 0xA7, 0x44, 0xF0,
+	0xA3, 0xAA, 0x8D, 0x44, 0xF0, 0xA3, 0xAB, 0xBA,
+	// Bytes 1700 - 173f
+	0x44, 0xF0, 0xA3, 0xB2, 0xBC, 0x44, 0xF0, 0xA3,
+	0xB4, 0x9E, 0x44, 0xF0, 0xA3, 0xBB, 0x91, 0x44,
+	0xF0, 0xA3, 0xBD, 0x9E, 0x44, 0xF0, 0xA3, 0xBE,
+	0x8E, 0x44, 0xF0, 0xA4, 0x89, 0xA3, 0x44, 0xF0,
+	0xA4, 0x8B, 0xAE, 0x44, 0xF0, 0xA4, 0x8E, 0xAB,
+	0x44, 0xF0, 0xA4, 0x98, 0x88, 0x44, 0xF0, 0xA4,
+	0x9C, 0xB5, 0x44, 0xF0, 0xA4, 0xA0, 0x94, 0x44,
+	0xF0, 0xA4, 0xB0, 0xB6, 0x44, 0xF0, 0xA4, 0xB2,
+	// Bytes 1740 - 177f
+	0x92, 0x44, 0xF0, 0xA4, 0xBE, 0xA1, 0x44, 0xF0,
+	0xA4, 0xBE, 0xB8, 0x44, 0xF0, 0xA5, 0x81, 0x84,
+	0x44, 0xF0, 0xA5, 0x83, 0xB2, 0x44, 0xF0, 0xA5,
+	0x83, 0xB3, 0x44, 0xF0, 0xA5, 0x84, 0x99, 0x44,
+	0xF0, 0xA5, 0x84, 0xB3, 0x44, 0xF0, 0xA5, 0x89,
+	0x89, 0x44, 0xF0, 0xA5, 0x90, 0x9D, 0x44, 0xF0,
+	0xA5, 0x98, 0xA6, 0x44, 0xF0, 0xA5, 0x9A, 0x9A,
+	0x44, 0xF0, 0xA5, 0x9B, 0x85, 0x44, 0xF0, 0xA5,
+	// Bytes 1780 - 17bf
+	0xA5, 0xBC, 0x44, 0xF0, 0xA5, 0xAA, 0xA7, 0x44,
+	0xF0, 0xA5, 0xAE, 0xAB, 0x44, 0xF0, 0xA5, 0xB2,
+	0x80, 0x44, 0xF0, 0xA5, 0xB3, 0x90, 0x44, 0xF0,
+	0xA5, 0xBE, 0x86, 0x44, 0xF0, 0xA6, 0x87, 0x9A,
+	0x44, 0xF0, 0xA6, 0x88, 0xA8, 0x44, 0xF0, 0xA6,
+	0x89, 0x87, 0x44, 0xF0, 0xA6, 0x8B, 0x99, 0x44,
+	0xF0, 0xA6, 0x8C, 0xBE, 0x44, 0xF0, 0xA6, 0x93,
+	0x9A, 0x44, 0xF0, 0xA6, 0x94, 0xA3, 0x44, 0xF0,
+	// Bytes 17c0 - 17ff
+	0xA6, 0x96, 0xA8, 0x44, 0xF0, 0xA6, 0x9E, 0xA7,
+	0x44, 0xF0, 0xA6, 0x9E, 0xB5, 0x44, 0xF0, 0xA6,
+	0xAC, 0xBC, 0x44, 0xF0, 0xA6, 0xB0, 0xB6, 0x44,
+	0xF0, 0xA6, 0xB3, 0x95, 0x44, 0xF0, 0xA6, 0xB5,
+	0xAB, 0x44, 0xF0, 0xA6, 0xBC, 0xAC, 0x44, 0xF0,
+	0xA6, 0xBE, 0xB1, 0x44, 0xF0, 0xA7, 0x83, 0x92,
+	0x44, 0xF0, 0xA7, 0x8F, 0x8A, 0x44, 0xF0, 0xA7,
+	0x99, 0xA7, 0x44, 0xF0, 0xA7, 0xA2, 0xAE, 0x44,
+	// Bytes 1800 - 183f
+	0xF0, 0xA7, 0xA5, 0xA6, 0x44, 0xF0, 0xA7, 0xB2,
+	0xA8, 0x44, 0xF0, 0xA7, 0xBB, 0x93, 0x44, 0xF0,
+	0xA7, 0xBC, 0xAF, 0x44, 0xF0, 0xA8, 0x97, 0x92,
+	0x44, 0xF0, 0xA8, 0x97, 0xAD, 0x44, 0xF0, 0xA8,
+	0x9C, 0xAE, 0x44, 0xF0, 0xA8, 0xAF, 0xBA, 0x44,
+	0xF0, 0xA8, 0xB5, 0xB7, 0x44, 0xF0, 0xA9, 0x85,
+	0x85, 0x44, 0xF0, 0xA9, 0x87, 0x9F, 0x44, 0xF0,
+	0xA9, 0x88, 0x9A, 0x44, 0xF0, 0xA9, 0x90, 0x8A,
+	// Bytes 1840 - 187f
+	0x44, 0xF0, 0xA9, 0x92, 0x96, 0x44, 0xF0, 0xA9,
+	0x96, 0xB6, 0x44, 0xF0, 0xA9, 0xAC, 0xB0, 0x44,
+	0xF0, 0xAA, 0x83, 0x8E, 0x44, 0xF0, 0xAA, 0x84,
+	0x85, 0x44, 0xF0, 0xAA, 0x88, 0x8E, 0x44, 0xF0,
+	0xAA, 0x8A, 0x91, 0x44, 0xF0, 0xAA, 0x8E, 0x92,
+	0x44, 0xF0, 0xAA, 0x98, 0x80, 0x42, 0x21, 0x21,
+	0x42, 0x21, 0x3F, 0x42, 0x2E, 0x2E, 0x42, 0x30,
+	0x2C, 0x42, 0x30, 0x2E, 0x42, 0x31, 0x2C, 0x42,
+	// Bytes 1880 - 18bf
+	0x31, 0x2E, 0x42, 0x31, 0x30, 0x42, 0x31, 0x31,
+	0x42, 0x31, 0x32, 0x42, 0x31, 0x33, 0x42, 0x31,
+	0x34, 0x42, 0x31, 0x35, 0x42, 0x31, 0x36, 0x42,
+	0x31, 0x37, 0x42, 0x31, 0x38, 0x42, 0x31, 0x39,
+	0x42, 0x32, 0x2C, 0x42, 0x32, 0x2E, 0x42, 0x32,
+	0x30, 0x42, 0x32, 0x31, 0x42, 0x32, 0x32, 0x42,
+	0x32, 0x33, 0x42, 0x32, 0x34, 0x42, 0x32, 0x35,
+	0x42, 0x32, 0x36, 0x42, 0x32, 0x37, 0x42, 0x32,
+	// Bytes 18c0 - 18ff
+	0x38, 0x42, 0x32, 0x39, 0x42, 0x33, 0x2C, 0x42,
+	0x33, 0x2E, 0x42, 0x33, 0x30, 0x42, 0x33, 0x31,
+	0x42, 0x33, 0x32, 0x42, 0x33, 0x33, 0x42, 0x33,
+	0x34, 0x42, 0x33, 0x35, 0x42, 0x33, 0x36, 0x42,
+	0x33, 0x37, 0x42, 0x33, 0x38, 0x42, 0x33, 0x39,
+	0x42, 0x34, 0x2C, 0x42, 0x34, 0x2E, 0x42, 0x34,
+	0x30, 0x42, 0x34, 0x31, 0x42, 0x34, 0x32, 0x42,
+	0x34, 0x33, 0x42, 0x34, 0x34, 0x42, 0x34, 0x35,
+	// Bytes 1900 - 193f
+	0x42, 0x34, 0x36, 0x42, 0x34, 0x37, 0x42, 0x34,
+	0x38, 0x42, 0x34, 0x39, 0x42, 0x35, 0x2C, 0x42,
+	0x35, 0x2E, 0x42, 0x35, 0x30, 0x42, 0x36, 0x2C,
+	0x42, 0x36, 0x2E, 0x42, 0x37, 0x2C, 0x42, 0x37,
+	0x2E, 0x42, 0x38, 0x2C, 0x42, 0x38, 0x2E, 0x42,
+	0x39, 0x2C, 0x42, 0x39, 0x2E, 0x42, 0x3D, 0x3D,
+	0x42, 0x3F, 0x21, 0x42, 0x3F, 0x3F, 0x42, 0x41,
+	0x55, 0x42, 0x42, 0x71, 0x42, 0x43, 0x44, 0x42,
+	// Bytes 1940 - 197f
+	0x44, 0x4A, 0x42, 0x44, 0x5A, 0x42, 0x44, 0x7A,
+	0x42, 0x47, 0x42, 0x42, 0x47, 0x79, 0x42, 0x48,
+	0x50, 0x42, 0x48, 0x56, 0x42, 0x48, 0x67, 0x42,
+	0x48, 0x7A, 0x42, 0x49, 0x49, 0x42, 0x49, 0x4A,
+	0x42, 0x49, 0x55, 0x42, 0x49, 0x56, 0x42, 0x49,
+	0x58, 0x42, 0x4B, 0x42, 0x42, 0x4B, 0x4B, 0x42,
+	0x4B, 0x4D, 0x42, 0x4C, 0x4A, 0x42, 0x4C, 0x6A,
+	0x42, 0x4D, 0x42, 0x42, 0x4D, 0x43, 0x42, 0x4D,
+	// Bytes 1980 - 19bf
+	0x44, 0x42, 0x4D, 0x56, 0x42, 0x4D, 0x57, 0x42,
+	0x4E, 0x4A, 0x42, 0x4E, 0x6A, 0x42, 0x4E, 0x6F,
+	0x42, 0x50, 0x48, 0x42, 0x50, 0x52, 0x42, 0x50,
+	0x61, 0x42, 0x52, 0x73, 0x42, 0x53, 0x44, 0x42,
+	0x53, 0x4D, 0x42, 0x53, 0x53, 0x42, 0x53, 0x76,
+	0x42, 0x54, 0x4D, 0x42, 0x56, 0x49, 0x42, 0x57,
+	0x43, 0x42, 0x57, 0x5A, 0x42, 0x57, 0x62, 0x42,
+	0x58, 0x49, 0x42, 0x63, 0x63, 0x42, 0x63, 0x64,
+	// Bytes 19c0 - 19ff
+	0x42, 0x63, 0x6D, 0x42, 0x64, 0x42, 0x42, 0x64,
+	0x61, 0x42, 0x64, 0x6C, 0x42, 0x64, 0x6D, 0x42,
+	0x64, 0x7A, 0x42, 0x65, 0x56, 0x42, 0x66, 0x66,
+	0x42, 0x66, 0x69, 0x42, 0x66, 0x6C, 0x42, 0x66,
+	0x6D, 0x42, 0x68, 0x61, 0x42, 0x69, 0x69, 0x42,
+	0x69, 0x6A, 0x42, 0x69, 0x6E, 0x42, 0x69, 0x76,
+	0x42, 0x69, 0x78, 0x42, 0x6B, 0x41, 0x42, 0x6B,
+	0x56, 0x42, 0x6B, 0x57, 0x42, 0x6B, 0x67, 0x42,
+	// Bytes 1a00 - 1a3f
+	0x6B, 0x6C, 0x42, 0x6B, 0x6D, 0x42, 0x6B, 0x74,
+	0x42, 0x6C, 0x6A, 0x42, 0x6C, 0x6D, 0x42, 0x6C,
+	0x6E, 0x42, 0x6C, 0x78, 0x42, 0x6D, 0x32, 0x42,
+	0x6D, 0x33, 0x42, 0x6D, 0x41, 0x42, 0x6D, 0x56,
+	0x42, 0x6D, 0x57, 0x42, 0x6D, 0x62, 0x42, 0x6D,
+	0x67, 0x42, 0x6D, 0x6C, 0x42, 0x6D, 0x6D, 0x42,
+	0x6D, 0x73, 0x42, 0x6E, 0x41, 0x42, 0x6E, 0x46,
+	0x42, 0x6E, 0x56, 0x42, 0x6E, 0x57, 0x42, 0x6E,
+	// Bytes 1a40 - 1a7f
+	0x6A, 0x42, 0x6E, 0x6D, 0x42, 0x6E, 0x73, 0x42,
+	0x6F, 0x56, 0x42, 0x70, 0x41, 0x42, 0x70, 0x46,
+	0x42, 0x70, 0x56, 0x42, 0x70, 0x57, 0x42, 0x70,
+	0x63, 0x42, 0x70, 0x73, 0x42, 0x73, 0x72, 0x42,
+	0x73, 0x74, 0x42, 0x76, 0x69, 0x42, 0x78, 0x69,
+	0x43, 0x28, 0x31, 0x29, 0x43, 0x28, 0x32, 0x29,
+	0x43, 0x28, 0x33, 0x29, 0x43, 0x28, 0x34, 0x29,
+	0x43, 0x28, 0x35, 0x29, 0x43, 0x28, 0x36, 0x29,
+	// Bytes 1a80 - 1abf
+	0x43, 0x28, 0x37, 0x29, 0x43, 0x28, 0x38, 0x29,
+	0x43, 0x28, 0x39, 0x29, 0x43, 0x28, 0x41, 0x29,
+	0x43, 0x28, 0x42, 0x29, 0x43, 0x28, 0x43, 0x29,
+	0x43, 0x28, 0x44, 0x29, 0x43, 0x28, 0x45, 0x29,
+	0x43, 0x28, 0x46, 0x29, 0x43, 0x28, 0x47, 0x29,
+	0x43, 0x28, 0x48, 0x29, 0x43, 0x28, 0x49, 0x29,
+	0x43, 0x28, 0x4A, 0x29, 0x43, 0x28, 0x4B, 0x29,
+	0x43, 0x28, 0x4C, 0x29, 0x43, 0x28, 0x4D, 0x29,
+	// Bytes 1ac0 - 1aff
+	0x43, 0x28, 0x4E, 0x29, 0x43, 0x28, 0x4F, 0x29,
+	0x43, 0x28, 0x50, 0x29, 0x43, 0x28, 0x51, 0x29,
+	0x43, 0x28, 0x52, 0x29, 0x43, 0x28, 0x53, 0x29,
+	0x43, 0x28, 0x54, 0x29, 0x43, 0x28, 0x55, 0x29,
+	0x43, 0x28, 0x56, 0x29, 0x43, 0x28, 0x57, 0x29,
+	0x43, 0x28, 0x58, 0x29, 0x43, 0x28, 0x59, 0x29,
+	0x43, 0x28, 0x5A, 0x29, 0x43, 0x28, 0x61, 0x29,
+	0x43, 0x28, 0x62, 0x29, 0x43, 0x28, 0x63, 0x29,
+	// Bytes 1b00 - 1b3f
+	0x43, 0x28, 0x64, 0x29, 0x43, 0x28, 0x65, 0x29,
+	0x43, 0x28, 0x66, 0x29, 0x43, 0x28, 0x67, 0x29,
+	0x43, 0x28, 0x68, 0x29, 0x43, 0x28, 0x69, 0x29,
+	0x43, 0x28, 0x6A, 0x29, 0x43, 0x28, 0x6B, 0x29,
+	0x43, 0x28, 0x6C, 0x29, 0x43, 0x28, 0x6D, 0x29,
+	0x43, 0x28, 0x6E, 0x29, 0x43, 0x28, 0x6F, 0x29,
+	0x43, 0x28, 0x70, 0x29, 0x43, 0x28, 0x71, 0x29,
+	0x43, 0x28, 0x72, 0x29, 0x43, 0x28, 0x73, 0x29,
+	// Bytes 1b40 - 1b7f
+	0x43, 0x28, 0x74, 0x29, 0x43, 0x28, 0x75, 0x29,
+	0x43, 0x28, 0x76, 0x29, 0x43, 0x28, 0x77, 0x29,
+	0x43, 0x28, 0x78, 0x29, 0x43, 0x28, 0x79, 0x29,
+	0x43, 0x28, 0x7A, 0x29, 0x43, 0x2E, 0x2E, 0x2E,
+	0x43, 0x31, 0x30, 0x2E, 0x43, 0x31, 0x31, 0x2E,
+	0x43, 0x31, 0x32, 0x2E, 0x43, 0x31, 0x33, 0x2E,
+	0x43, 0x31, 0x34, 0x2E, 0x43, 0x31, 0x35, 0x2E,
+	0x43, 0x31, 0x36, 0x2E, 0x43, 0x31, 0x37, 0x2E,
+	// Bytes 1b80 - 1bbf
+	0x43, 0x31, 0x38, 0x2E, 0x43, 0x31, 0x39, 0x2E,
+	0x43, 0x32, 0x30, 0x2E, 0x43, 0x3A, 0x3A, 0x3D,
+	0x43, 0x3D, 0x3D, 0x3D, 0x43, 0x43, 0x6F, 0x2E,
+	0x43, 0x46, 0x41, 0x58, 0x43, 0x47, 0x48, 0x7A,
+	0x43, 0x47, 0x50, 0x61, 0x43, 0x49, 0x49, 0x49,
+	0x43, 0x4C, 0x54, 0x44, 0x43, 0x4C, 0xC2, 0xB7,
+	0x43, 0x4D, 0x48, 0x7A, 0x43, 0x4D, 0x50, 0x61,
+	0x43, 0x4D, 0xCE, 0xA9, 0x43, 0x50, 0x50, 0x4D,
+	// Bytes 1bc0 - 1bff
+	0x43, 0x50, 0x50, 0x56, 0x43, 0x50, 0x54, 0x45,
+	0x43, 0x54, 0x45, 0x4C, 0x43, 0x54, 0x48, 0x7A,
+	0x43, 0x56, 0x49, 0x49, 0x43, 0x58, 0x49, 0x49,
+	0x43, 0x61, 0x2F, 0x63, 0x43, 0x61, 0x2F, 0x73,
+	0x43, 0x61, 0xCA, 0xBE, 0x43, 0x62, 0x61, 0x72,
+	0x43, 0x63, 0x2F, 0x6F, 0x43, 0x63, 0x2F, 0x75,
+	0x43, 0x63, 0x61, 0x6C, 0x43, 0x63, 0x6D, 0x32,
+	0x43, 0x63, 0x6D, 0x33, 0x43, 0x64, 0x6D, 0x32,
+	// Bytes 1c00 - 1c3f
+	0x43, 0x64, 0x6D, 0x33, 0x43, 0x65, 0x72, 0x67,
+	0x43, 0x66, 0x66, 0x69, 0x43, 0x66, 0x66, 0x6C,
+	0x43, 0x67, 0x61, 0x6C, 0x43, 0x68, 0x50, 0x61,
+	0x43, 0x69, 0x69, 0x69, 0x43, 0x6B, 0x48, 0x7A,
+	0x43, 0x6B, 0x50, 0x61, 0x43, 0x6B, 0x6D, 0x32,
+	0x43, 0x6B, 0x6D, 0x33, 0x43, 0x6B, 0xCE, 0xA9,
+	0x43, 0x6C, 0x6F, 0x67, 0x43, 0x6C, 0xC2, 0xB7,
+	0x43, 0x6D, 0x69, 0x6C, 0x43, 0x6D, 0x6D, 0x32,
+	// Bytes 1c40 - 1c7f
+	0x43, 0x6D, 0x6D, 0x33, 0x43, 0x6D, 0x6F, 0x6C,
+	0x43, 0x72, 0x61, 0x64, 0x43, 0x76, 0x69, 0x69,
+	0x43, 0x78, 0x69, 0x69, 0x43, 0xC2, 0xB0, 0x43,
+	0x43, 0xC2, 0xB0, 0x46, 0x43, 0xCA, 0xBC, 0x6E,
+	0x43, 0xCE, 0xBC, 0x41, 0x43, 0xCE, 0xBC, 0x46,
+	0x43, 0xCE, 0xBC, 0x56, 0x43, 0xCE, 0xBC, 0x57,
+	0x43, 0xCE, 0xBC, 0x67, 0x43, 0xCE, 0xBC, 0x6C,
+	0x43, 0xCE, 0xBC, 0x6D, 0x43, 0xCE, 0xBC, 0x73,
+	// Bytes 1c80 - 1cbf
+	0x44, 0x28, 0x31, 0x30, 0x29, 0x44, 0x28, 0x31,
+	0x31, 0x29, 0x44, 0x28, 0x31, 0x32, 0x29, 0x44,
+	0x28, 0x31, 0x33, 0x29, 0x44, 0x28, 0x31, 0x34,
+	0x29, 0x44, 0x28, 0x31, 0x35, 0x29, 0x44, 0x28,
+	0x31, 0x36, 0x29, 0x44, 0x28, 0x31, 0x37, 0x29,
+	0x44, 0x28, 0x31, 0x38, 0x29, 0x44, 0x28, 0x31,
+	0x39, 0x29, 0x44, 0x28, 0x32, 0x30, 0x29, 0x44,
+	0x30, 0xE7, 0x82, 0xB9, 0x44, 0x31, 0xE2, 0x81,
+	// Bytes 1cc0 - 1cff
+	0x84, 0x44, 0x31, 0xE6, 0x97, 0xA5, 0x44, 0x31,
+	0xE6, 0x9C, 0x88, 0x44, 0x31, 0xE7, 0x82, 0xB9,
+	0x44, 0x32, 0xE6, 0x97, 0xA5, 0x44, 0x32, 0xE6,
+	0x9C, 0x88, 0x44, 0x32, 0xE7, 0x82, 0xB9, 0x44,
+	0x33, 0xE6, 0x97, 0xA5, 0x44, 0x33, 0xE6, 0x9C,
+	0x88, 0x44, 0x33, 0xE7, 0x82, 0xB9, 0x44, 0x34,
+	0xE6, 0x97, 0xA5, 0x44, 0x34, 0xE6, 0x9C, 0x88,
+	0x44, 0x34, 0xE7, 0x82, 0xB9, 0x44, 0x35, 0xE6,
+	// Bytes 1d00 - 1d3f
+	0x97, 0xA5, 0x44, 0x35, 0xE6, 0x9C, 0x88, 0x44,
+	0x35, 0xE7, 0x82, 0xB9, 0x44, 0x36, 0xE6, 0x97,
+	0xA5, 0x44, 0x36, 0xE6, 0x9C, 0x88, 0x44, 0x36,
+	0xE7, 0x82, 0xB9, 0x44, 0x37, 0xE6, 0x97, 0xA5,
+	0x44, 0x37, 0xE6, 0x9C, 0x88, 0x44, 0x37, 0xE7,
+	0x82, 0xB9, 0x44, 0x38, 0xE6, 0x97, 0xA5, 0x44,
+	0x38, 0xE6, 0x9C, 0x88, 0x44, 0x38, 0xE7, 0x82,
+	0xB9, 0x44, 0x39, 0xE6, 0x97, 0xA5, 0x44, 0x39,
+	// Bytes 1d40 - 1d7f
+	0xE6, 0x9C, 0x88, 0x44, 0x39, 0xE7, 0x82, 0xB9,
+	0x44, 0x56, 0x49, 0x49, 0x49, 0x44, 0x61, 0x2E,
+	0x6D, 0x2E, 0x44, 0x6B, 0x63, 0x61, 0x6C, 0x44,
+	0x70, 0x2E, 0x6D, 0x2E, 0x44, 0x76, 0x69, 0x69,
+	0x69, 0x44, 0xD5, 0xA5, 0xD6, 0x82, 0x44, 0xD5,
+	0xB4, 0xD5, 0xA5, 0x44, 0xD5, 0xB4, 0xD5, 0xAB,
+	0x44, 0xD5, 0xB4, 0xD5, 0xAD, 0x44, 0xD5, 0xB4,
+	0xD5, 0xB6, 0x44, 0xD5, 0xBE, 0xD5, 0xB6, 0x44,
+	// Bytes 1d80 - 1dbf
+	0xD7, 0x90, 0xD7, 0x9C, 0x44, 0xD8, 0xA7, 0xD9,
+	0xB4, 0x44, 0xD8, 0xA8, 0xD8, 0xAC, 0x44, 0xD8,
+	0xA8, 0xD8, 0xAD, 0x44, 0xD8, 0xA8, 0xD8, 0xAE,
+	0x44, 0xD8, 0xA8, 0xD8, 0xB1, 0x44, 0xD8, 0xA8,
+	0xD8, 0xB2, 0x44, 0xD8, 0xA8, 0xD9, 0x85, 0x44,
+	0xD8, 0xA8, 0xD9, 0x86, 0x44, 0xD8, 0xA8, 0xD9,
+	0x87, 0x44, 0xD8, 0xA8, 0xD9, 0x89, 0x44, 0xD8,
+	0xA8, 0xD9, 0x8A, 0x44, 0xD8, 0xAA, 0xD8, 0xAC,
+	// Bytes 1dc0 - 1dff
+	0x44, 0xD8, 0xAA, 0xD8, 0xAD, 0x44, 0xD8, 0xAA,
+	0xD8, 0xAE, 0x44, 0xD8, 0xAA, 0xD8, 0xB1, 0x44,
+	0xD8, 0xAA, 0xD8, 0xB2, 0x44, 0xD8, 0xAA, 0xD9,
+	0x85, 0x44, 0xD8, 0xAA, 0xD9, 0x86, 0x44, 0xD8,
+	0xAA, 0xD9, 0x87, 0x44, 0xD8, 0xAA, 0xD9, 0x89,
+	0x44, 0xD8, 0xAA, 0xD9, 0x8A, 0x44, 0xD8, 0xAB,
+	0xD8, 0xAC, 0x44, 0xD8, 0xAB, 0xD8, 0xB1, 0x44,
+	0xD8, 0xAB, 0xD8, 0xB2, 0x44, 0xD8, 0xAB, 0xD9,
+	// Bytes 1e00 - 1e3f
+	0x85, 0x44, 0xD8, 0xAB, 0xD9, 0x86, 0x44, 0xD8,
+	0xAB, 0xD9, 0x87, 0x44, 0xD8, 0xAB, 0xD9, 0x89,
+	0x44, 0xD8, 0xAB, 0xD9, 0x8A, 0x44, 0xD8, 0xAC,
+	0xD8, 0xAD, 0x44, 0xD8, 0xAC, 0xD9, 0x85, 0x44,
+	0xD8, 0xAC, 0xD9, 0x89, 0x44, 0xD8, 0xAC, 0xD9,
+	0x8A, 0x44, 0xD8, 0xAD, 0xD8, 0xAC, 0x44, 0xD8,
+	0xAD, 0xD9, 0x85, 0x44, 0xD8, 0xAD, 0xD9, 0x89,
+	0x44, 0xD8, 0xAD, 0xD9, 0x8A, 0x44, 0xD8, 0xAE,
+	// Bytes 1e40 - 1e7f
+	0xD8, 0xAC, 0x44, 0xD8, 0xAE, 0xD8, 0xAD, 0x44,
+	0xD8, 0xAE, 0xD9, 0x85, 0x44, 0xD8, 0xAE, 0xD9,
+	0x89, 0x44, 0xD8, 0xAE, 0xD9, 0x8A, 0x44, 0xD8,
+	0xB3, 0xD8, 0xAC, 0x44, 0xD8, 0xB3, 0xD8, 0xAD,
+	0x44, 0xD8, 0xB3, 0xD8, 0xAE, 0x44, 0xD8, 0xB3,
+	0xD8, 0xB1, 0x44, 0xD8, 0xB3, 0xD9, 0x85, 0x44,
+	0xD8, 0xB3, 0xD9, 0x87, 0x44, 0xD8, 0xB3, 0xD9,
+	0x89, 0x44, 0xD8, 0xB3, 0xD9, 0x8A, 0x44, 0xD8,
+	// Bytes 1e80 - 1ebf
+	0xB4, 0xD8, 0xAC, 0x44, 0xD8, 0xB4, 0xD8, 0xAD,
+	0x44, 0xD8, 0xB4, 0xD8, 0xAE, 0x44, 0xD8, 0xB4,
+	0xD8, 0xB1, 0x44, 0xD8, 0xB4, 0xD9, 0x85, 0x44,
+	0xD8, 0xB4, 0xD9, 0x87, 0x44, 0xD8, 0xB4, 0xD9,
+	0x89, 0x44, 0xD8, 0xB4, 0xD9, 0x8A, 0x44, 0xD8,
+	0xB5, 0xD8, 0xAD, 0x44, 0xD8, 0xB5, 0xD8, 0xAE,
+	0x44, 0xD8, 0xB5, 0xD8, 0xB1, 0x44, 0xD8, 0xB5,
+	0xD9, 0x85, 0x44, 0xD8, 0xB5, 0xD9, 0x89, 0x44,
+	// Bytes 1ec0 - 1eff
+	0xD8, 0xB5, 0xD9, 0x8A, 0x44, 0xD8, 0xB6, 0xD8,
+	0xAC, 0x44, 0xD8, 0xB6, 0xD8, 0xAD, 0x44, 0xD8,
+	0xB6, 0xD8, 0xAE, 0x44, 0xD8, 0xB6, 0xD8, 0xB1,
+	0x44, 0xD8, 0xB6, 0xD9, 0x85, 0x44, 0xD8, 0xB6,
+	0xD9, 0x89, 0x44, 0xD8, 0xB6, 0xD9, 0x8A, 0x44,
+	0xD8, 0xB7, 0xD8, 0xAD, 0x44, 0xD8, 0xB7, 0xD9,
+	0x85, 0x44, 0xD8, 0xB7, 0xD9, 0x89, 0x44, 0xD8,
+	0xB7, 0xD9, 0x8A, 0x44, 0xD8, 0xB8, 0xD9, 0x85,
+	// Bytes 1f00 - 1f3f
+	0x44, 0xD8, 0xB9, 0xD8, 0xAC, 0x44, 0xD8, 0xB9,
+	0xD9, 0x85, 0x44, 0xD8, 0xB9, 0xD9, 0x89, 0x44,
+	0xD8, 0xB9, 0xD9, 0x8A, 0x44, 0xD8, 0xBA, 0xD8,
+	0xAC, 0x44, 0xD8, 0xBA, 0xD9, 0x85, 0x44, 0xD8,
+	0xBA, 0xD9, 0x89, 0x44, 0xD8, 0xBA, 0xD9, 0x8A,
+	0x44, 0xD9, 0x81, 0xD8, 0xAC, 0x44, 0xD9, 0x81,
+	0xD8, 0xAD, 0x44, 0xD9, 0x81, 0xD8, 0xAE, 0x44,
+	0xD9, 0x81, 0xD9, 0x85, 0x44, 0xD9, 0x81, 0xD9,
+	// Bytes 1f40 - 1f7f
+	0x89, 0x44, 0xD9, 0x81, 0xD9, 0x8A, 0x44, 0xD9,
+	0x82, 0xD8, 0xAD, 0x44, 0xD9, 0x82, 0xD9, 0x85,
+	0x44, 0xD9, 0x82, 0xD9, 0x89, 0x44, 0xD9, 0x82,
+	0xD9, 0x8A, 0x44, 0xD9, 0x83, 0xD8, 0xA7, 0x44,
+	0xD9, 0x83, 0xD8, 0xAC, 0x44, 0xD9, 0x83, 0xD8,
+	0xAD, 0x44, 0xD9, 0x83, 0xD8, 0xAE, 0x44, 0xD9,
+	0x83, 0xD9, 0x84, 0x44, 0xD9, 0x83, 0xD9, 0x85,
+	0x44, 0xD9, 0x83, 0xD9, 0x89, 0x44, 0xD9, 0x83,
+	// Bytes 1f80 - 1fbf
+	0xD9, 0x8A, 0x44, 0xD9, 0x84, 0xD8, 0xA7, 0x44,
+	0xD9, 0x84, 0xD8, 0xAC, 0x44, 0xD9, 0x84, 0xD8,
+	0xAD, 0x44, 0xD9, 0x84, 0xD8, 0xAE, 0x44, 0xD9,
+	0x84, 0xD9, 0x85, 0x44, 0xD9, 0x84, 0xD9, 0x87,
+	0x44, 0xD9, 0x84, 0xD9, 0x89, 0x44, 0xD9, 0x84,
+	0xD9, 0x8A, 0x44, 0xD9, 0x85, 0xD8, 0xA7, 0x44,
+	0xD9, 0x85, 0xD8, 0xAC, 0x44, 0xD9, 0x85, 0xD8,
+	0xAD, 0x44, 0xD9, 0x85, 0xD8, 0xAE, 0x44, 0xD9,
+	// Bytes 1fc0 - 1fff
+	0x85, 0xD9, 0x85, 0x44, 0xD9, 0x85, 0xD9, 0x89,
+	0x44, 0xD9, 0x85, 0xD9, 0x8A, 0x44, 0xD9, 0x86,
+	0xD8, 0xAC, 0x44, 0xD9, 0x86, 0xD8, 0xAD, 0x44,
+	0xD9, 0x86, 0xD8, 0xAE, 0x44, 0xD9, 0x86, 0xD8,
+	0xB1, 0x44, 0xD9, 0x86, 0xD8, 0xB2, 0x44, 0xD9,
+	0x86, 0xD9, 0x85, 0x44, 0xD9, 0x86, 0xD9, 0x86,
+	0x44, 0xD9, 0x86, 0xD9, 0x87, 0x44, 0xD9, 0x86,
+	0xD9, 0x89, 0x44, 0xD9, 0x86, 0xD9, 0x8A, 0x44,
+	// Bytes 2000 - 203f
+	0xD9, 0x87, 0xD8, 0xAC, 0x44, 0xD9, 0x87, 0xD9,
+	0x85, 0x44, 0xD9, 0x87, 0xD9, 0x89, 0x44, 0xD9,
+	0x87, 0xD9, 0x8A, 0x44, 0xD9, 0x88, 0xD9, 0xB4,
+	0x44, 0xD9, 0x8A, 0xD8, 0xAC, 0x44, 0xD9, 0x8A,
+	0xD8, 0xAD, 0x44, 0xD9, 0x8A, 0xD8, 0xAE, 0x44,
+	0xD9, 0x8A, 0xD8, 0xB1, 0x44, 0xD9, 0x8A, 0xD8,
+	0xB2, 0x44, 0xD9, 0x8A, 0xD9, 0x85, 0x44, 0xD9,
+	0x8A, 0xD9, 0x86, 0x44, 0xD9, 0x8A, 0xD9, 0x87,
+	// Bytes 2040 - 207f
+	0x44, 0xD9, 0x8A, 0xD9, 0x89, 0x44, 0xD9, 0x8A,
+	0xD9, 0x8A, 0x44, 0xD9, 0x8A, 0xD9, 0xB4, 0x44,
+	0xDB, 0x87, 0xD9, 0xB4, 0x45, 0x28, 0xE1, 0x84,
+	0x80, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x82, 0x29,
+	0x45, 0x28, 0xE1, 0x84, 0x83, 0x29, 0x45, 0x28,
+	0xE1, 0x84, 0x85, 0x29, 0x45, 0x28, 0xE1, 0x84,
+	0x86, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x87, 0x29,
+	0x45, 0x28, 0xE1, 0x84, 0x89, 0x29, 0x45, 0x28,
+	// Bytes 2080 - 20bf
+	0xE1, 0x84, 0x8B, 0x29, 0x45, 0x28, 0xE1, 0x84,
+	0x8C, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x8E, 0x29,
+	0x45, 0x28, 0xE1, 0x84, 0x8F, 0x29, 0x45, 0x28,
+	0xE1, 0x84, 0x90, 0x29, 0x45, 0x28, 0xE1, 0x84,
+	0x91, 0x29, 0x45, 0x28, 0xE1, 0x84, 0x92, 0x29,
+	0x45, 0x28, 0xE4, 0xB8, 0x80, 0x29, 0x45, 0x28,
+	0xE4, 0xB8, 0x83, 0x29, 0x45, 0x28, 0xE4, 0xB8,
+	0x89, 0x29, 0x45, 0x28, 0xE4, 0xB9, 0x9D, 0x29,
+	// Bytes 20c0 - 20ff
+	0x45, 0x28, 0xE4, 0xBA, 0x8C, 0x29, 0x45, 0x28,
+	0xE4, 0xBA, 0x94, 0x29, 0x45, 0x28, 0xE4, 0xBB,
+	0xA3, 0x29, 0x45, 0x28, 0xE4, 0xBC, 0x81, 0x29,
+	0x45, 0x28, 0xE4, 0xBC, 0x91, 0x29, 0x45, 0x28,
+	0xE5, 0x85, 0xAB, 0x29, 0x45, 0x28, 0xE5, 0x85,
+	0xAD, 0x29, 0x45, 0x28, 0xE5, 0x8A, 0xB4, 0x29,
+	0x45, 0x28, 0xE5, 0x8D, 0x81, 0x29, 0x45, 0x28,
+	0xE5, 0x8D, 0x94, 0x29, 0x45, 0x28, 0xE5, 0x90,
+	// Bytes 2100 - 213f
+	0x8D, 0x29, 0x45, 0x28, 0xE5, 0x91, 0xBC, 0x29,
+	0x45, 0x28, 0xE5, 0x9B, 0x9B, 0x29, 0x45, 0x28,
+	0xE5, 0x9C, 0x9F, 0x29, 0x45, 0x28, 0xE5, 0xAD,
+	0xA6, 0x29, 0x45, 0x28, 0xE6, 0x97, 0xA5, 0x29,
+	0x45, 0x28, 0xE6, 0x9C, 0x88, 0x29, 0x45, 0x28,
+	0xE6, 0x9C, 0x89, 0x29, 0x45, 0x28, 0xE6, 0x9C,
+	0xA8, 0x29, 0x45, 0x28, 0xE6, 0xA0, 0xAA, 0x29,
+	0x45, 0x28, 0xE6, 0xB0, 0xB4, 0x29, 0x45, 0x28,
+	// Bytes 2140 - 217f
+	0xE7, 0x81, 0xAB, 0x29, 0x45, 0x28, 0xE7, 0x89,
+	0xB9, 0x29, 0x45, 0x28, 0xE7, 0x9B, 0xA3, 0x29,
+	0x45, 0x28, 0xE7, 0xA4, 0xBE, 0x29, 0x45, 0x28,
+	0xE7, 0xA5, 0x9D, 0x29, 0x45, 0x28, 0xE7, 0xA5,
+	0xAD, 0x29, 0x45, 0x28, 0xE8, 0x87, 0xAA, 0x29,
+	0x45, 0x28, 0xE8, 0x87, 0xB3, 0x29, 0x45, 0x28,
+	0xE8, 0xB2, 0xA1, 0x29, 0x45, 0x28, 0xE8, 0xB3,
+	0x87, 0x29, 0x45, 0x28, 0xE9, 0x87, 0x91, 0x29,
+	// Bytes 2180 - 21bf
+	0x45, 0x30, 0xE2, 0x81, 0x84, 0x33, 0x45, 0x31,
+	0x30, 0xE6, 0x97, 0xA5, 0x45, 0x31, 0x30, 0xE6,
+	0x9C, 0x88, 0x45, 0x31, 0x30, 0xE7, 0x82, 0xB9,
+	0x45, 0x31, 0x31, 0xE6, 0x97, 0xA5, 0x45, 0x31,
+	0x31, 0xE6, 0x9C, 0x88, 0x45, 0x31, 0x31, 0xE7,
+	0x82, 0xB9, 0x45, 0x31, 0x32, 0xE6, 0x97, 0xA5,
+	0x45, 0x31, 0x32, 0xE6, 0x9C, 0x88, 0x45, 0x31,
+	0x32, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0x33, 0xE6,
+	// Bytes 21c0 - 21ff
+	0x97, 0xA5, 0x45, 0x31, 0x33, 0xE7, 0x82, 0xB9,
+	0x45, 0x31, 0x34, 0xE6, 0x97, 0xA5, 0x45, 0x31,
+	0x34, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0x35, 0xE6,
+	0x97, 0xA5, 0x45, 0x31, 0x35, 0xE7, 0x82, 0xB9,
+	0x45, 0x31, 0x36, 0xE6, 0x97, 0xA5, 0x45, 0x31,
+	0x36, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0x37, 0xE6,
+	0x97, 0xA5, 0x45, 0x31, 0x37, 0xE7, 0x82, 0xB9,
+	0x45, 0x31, 0x38, 0xE6, 0x97, 0xA5, 0x45, 0x31,
+	// Bytes 2200 - 223f
+	0x38, 0xE7, 0x82, 0xB9, 0x45, 0x31, 0x39, 0xE6,
+	0x97, 0xA5, 0x45, 0x31, 0x39, 0xE7, 0x82, 0xB9,
+	0x45, 0x31, 0xE2, 0x81, 0x84, 0x32, 0x45, 0x31,
+	0xE2, 0x81, 0x84, 0x33, 0x45, 0x31, 0xE2, 0x81,
+	0x84, 0x34, 0x45, 0x31, 0xE2, 0x81, 0x84, 0x35,
+	0x45, 0x31, 0xE2, 0x81, 0x84, 0x36, 0x45, 0x31,
+	0xE2, 0x81, 0x84, 0x37, 0x45, 0x31, 0xE2, 0x81,
+	0x84, 0x38, 0x45, 0x31, 0xE2, 0x81, 0x84, 0x39,
+	// Bytes 2240 - 227f
+	0x45, 0x32, 0x30, 0xE6, 0x97, 0xA5, 0x45, 0x32,
+	0x30, 0xE7, 0x82, 0xB9, 0x45, 0x32, 0x31, 0xE6,
+	0x97, 0xA5, 0x45, 0x32, 0x31, 0xE7, 0x82, 0xB9,
+	0x45, 0x32, 0x32, 0xE6, 0x97, 0xA5, 0x45, 0x32,
+	0x32, 0xE7, 0x82, 0xB9, 0x45, 0x32, 0x33, 0xE6,
+	0x97, 0xA5, 0x45, 0x32, 0x33, 0xE7, 0x82, 0xB9,
+	0x45, 0x32, 0x34, 0xE6, 0x97, 0xA5, 0x45, 0x32,
+	0x34, 0xE7, 0x82, 0xB9, 0x45, 0x32, 0x35, 0xE6,
+	// Bytes 2280 - 22bf
+	0x97, 0xA5, 0x45, 0x32, 0x36, 0xE6, 0x97, 0xA5,
+	0x45, 0x32, 0x37, 0xE6, 0x97, 0xA5, 0x45, 0x32,
+	0x38, 0xE6, 0x97, 0xA5, 0x45, 0x32, 0x39, 0xE6,
+	0x97, 0xA5, 0x45, 0x32, 0xE2, 0x81, 0x84, 0x33,
+	0x45, 0x32, 0xE2, 0x81, 0x84, 0x35, 0x45, 0x33,
+	0x30, 0xE6, 0x97, 0xA5, 0x45, 0x33, 0x31, 0xE6,
+	0x97, 0xA5, 0x45, 0x33, 0xE2, 0x81, 0x84, 0x34,
+	0x45, 0x33, 0xE2, 0x81, 0x84, 0x35, 0x45, 0x33,
+	// Bytes 22c0 - 22ff
+	0xE2, 0x81, 0x84, 0x38, 0x45, 0x34, 0xE2, 0x81,
+	0x84, 0x35, 0x45, 0x35, 0xE2, 0x81, 0x84, 0x36,
+	0x45, 0x35, 0xE2, 0x81, 0x84, 0x38, 0x45, 0x37,
+	0xE2, 0x81, 0x84, 0x38, 0x45, 0x41, 0xE2, 0x88,
+	0x95, 0x6D, 0x45, 0x56, 0xE2, 0x88, 0x95, 0x6D,
+	0x45, 0x6D, 0xE2, 0x88, 0x95, 0x73, 0x46, 0x31,
+	0xE2, 0x81, 0x84, 0x31, 0x30, 0x46, 0x43, 0xE2,
+	0x88, 0x95, 0x6B, 0x67, 0x46, 0x6D, 0xE2, 0x88,
+	// Bytes 2300 - 233f
+	0x95, 0x73, 0x32, 0x46, 0xD8, 0xA8, 0xD8, 0xAD,
+	0xD9, 0x8A, 0x46, 0xD8, 0xA8, 0xD8, 0xAE, 0xD9,
+	0x8A, 0x46, 0xD8, 0xAA, 0xD8, 0xAC, 0xD9, 0x85,
+	0x46, 0xD8, 0xAA, 0xD8, 0xAC, 0xD9, 0x89, 0x46,
+	0xD8, 0xAA, 0xD8, 0xAC, 0xD9, 0x8A, 0x46, 0xD8,
+	0xAA, 0xD8, 0xAD, 0xD8, 0xAC, 0x46, 0xD8, 0xAA,
+	0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD8, 0xAA, 0xD8,
+	0xAE, 0xD9, 0x85, 0x46, 0xD8, 0xAA, 0xD8, 0xAE,
+	// Bytes 2340 - 237f
+	0xD9, 0x89, 0x46, 0xD8, 0xAA, 0xD8, 0xAE, 0xD9,
+	0x8A, 0x46, 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAC,
+	0x46, 0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAD, 0x46,
+	0xD8, 0xAA, 0xD9, 0x85, 0xD8, 0xAE, 0x46, 0xD8,
+	0xAA, 0xD9, 0x85, 0xD9, 0x89, 0x46, 0xD8, 0xAA,
+	0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD8, 0xAC, 0xD8,
+	0xAD, 0xD9, 0x89, 0x46, 0xD8, 0xAC, 0xD8, 0xAD,
+	0xD9, 0x8A, 0x46, 0xD8, 0xAC, 0xD9, 0x85, 0xD8,
+	// Bytes 2380 - 23bf
+	0xAD, 0x46, 0xD8, 0xAC, 0xD9, 0x85, 0xD9, 0x89,
+	0x46, 0xD8, 0xAC, 0xD9, 0x85, 0xD9, 0x8A, 0x46,
+	0xD8, 0xAD, 0xD8, 0xAC, 0xD9, 0x8A, 0x46, 0xD8,
+	0xAD, 0xD9, 0x85, 0xD9, 0x89, 0x46, 0xD8, 0xAD,
+	0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD8, 0xB3, 0xD8,
+	0xAC, 0xD8, 0xAD, 0x46, 0xD8, 0xB3, 0xD8, 0xAC,
+	0xD9, 0x89, 0x46, 0xD8, 0xB3, 0xD8, 0xAD, 0xD8,
+	0xAC, 0x46, 0xD8, 0xB3, 0xD8, 0xAE, 0xD9, 0x89,
+	// Bytes 23c0 - 23ff
+	0x46, 0xD8, 0xB3, 0xD8, 0xAE, 0xD9, 0x8A, 0x46,
+	0xD8, 0xB3, 0xD9, 0x85, 0xD8, 0xAC, 0x46, 0xD8,
+	0xB3, 0xD9, 0x85, 0xD8, 0xAD, 0x46, 0xD8, 0xB3,
+	0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD8, 0xB4, 0xD8,
+	0xAC, 0xD9, 0x8A, 0x46, 0xD8, 0xB4, 0xD8, 0xAD,
+	0xD9, 0x85, 0x46, 0xD8, 0xB4, 0xD8, 0xAD, 0xD9,
+	0x8A, 0x46, 0xD8, 0xB4, 0xD9, 0x85, 0xD8, 0xAE,
+	0x46, 0xD8, 0xB4, 0xD9, 0x85, 0xD9, 0x85, 0x46,
+	// Bytes 2400 - 243f
+	0xD8, 0xB5, 0xD8, 0xAD, 0xD8, 0xAD, 0x46, 0xD8,
+	0xB5, 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD8, 0xB5,
+	0xD9, 0x84, 0xD9, 0x89, 0x46, 0xD8, 0xB5, 0xD9,
+	0x84, 0xDB, 0x92, 0x46, 0xD8, 0xB5, 0xD9, 0x85,
+	0xD9, 0x85, 0x46, 0xD8, 0xB6, 0xD8, 0xAD, 0xD9,
+	0x89, 0x46, 0xD8, 0xB6, 0xD8, 0xAD, 0xD9, 0x8A,
+	0x46, 0xD8, 0xB6, 0xD8, 0xAE, 0xD9, 0x85, 0x46,
+	0xD8, 0xB7, 0xD9, 0x85, 0xD8, 0xAD, 0x46, 0xD8,
+	// Bytes 2440 - 247f
+	0xB7, 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD8, 0xB7,
+	0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD8, 0xB9, 0xD8,
+	0xAC, 0xD9, 0x85, 0x46, 0xD8, 0xB9, 0xD9, 0x85,
+	0xD9, 0x85, 0x46, 0xD8, 0xB9, 0xD9, 0x85, 0xD9,
+	0x89, 0x46, 0xD8, 0xB9, 0xD9, 0x85, 0xD9, 0x8A,
+	0x46, 0xD8, 0xBA, 0xD9, 0x85, 0xD9, 0x85, 0x46,
+	0xD8, 0xBA, 0xD9, 0x85, 0xD9, 0x89, 0x46, 0xD8,
+	0xBA, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x81,
+	// Bytes 2480 - 24bf
+	0xD8, 0xAE, 0xD9, 0x85, 0x46, 0xD9, 0x81, 0xD9,
+	0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x82, 0xD9, 0x84,
+	0xDB, 0x92, 0x46, 0xD9, 0x82, 0xD9, 0x85, 0xD8,
+	0xAD, 0x46, 0xD9, 0x82, 0xD9, 0x85, 0xD9, 0x85,
+	0x46, 0xD9, 0x82, 0xD9, 0x85, 0xD9, 0x8A, 0x46,
+	0xD9, 0x83, 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD9,
+	0x83, 0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x84,
+	0xD8, 0xAC, 0xD8, 0xAC, 0x46, 0xD9, 0x84, 0xD8,
+	// Bytes 24c0 - 24ff
+	0xAC, 0xD9, 0x85, 0x46, 0xD9, 0x84, 0xD8, 0xAC,
+	0xD9, 0x8A, 0x46, 0xD9, 0x84, 0xD8, 0xAD, 0xD9,
+	0x85, 0x46, 0xD9, 0x84, 0xD8, 0xAD, 0xD9, 0x89,
+	0x46, 0xD9, 0x84, 0xD8, 0xAD, 0xD9, 0x8A, 0x46,
+	0xD9, 0x84, 0xD8, 0xAE, 0xD9, 0x85, 0x46, 0xD9,
+	0x84, 0xD9, 0x85, 0xD8, 0xAD, 0x46, 0xD9, 0x84,
+	0xD9, 0x85, 0xD9, 0x8A, 0x46, 0xD9, 0x85, 0xD8,
+	0xAC, 0xD8, 0xAD, 0x46, 0xD9, 0x85, 0xD8, 0xAC,
+	// Bytes 2500 - 253f
+	0xD8, 0xAE, 0x46, 0xD9, 0x85, 0xD8, 0xAC, 0xD9,
+	0x85, 0x46, 0xD9, 0x85, 0xD8, 0xAC, 0xD9, 0x8A,
+	0x46, 0xD9, 0x85, 0xD8, 0xAD, 0xD8, 0xAC, 0x46,
+	0xD9, 0x85, 0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD9,
+	0x85, 0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD9, 0x85,
+	0xD8, 0xAE, 0xD8, 0xAC, 0x46, 0xD9, 0x85, 0xD8,
+	0xAE, 0xD9, 0x85, 0x46, 0xD9, 0x85, 0xD8, 0xAE,
+	0xD9, 0x8A, 0x46, 0xD9, 0x85, 0xD9, 0x85, 0xD9,
+	// Bytes 2540 - 257f
+	0x8A, 0x46, 0xD9, 0x86, 0xD8, 0xAC, 0xD8, 0xAD,
+	0x46, 0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x85, 0x46,
+	0xD9, 0x86, 0xD8, 0xAC, 0xD9, 0x89, 0x46, 0xD9,
+	0x86, 0xD8, 0xAC, 0xD9, 0x8A, 0x46, 0xD9, 0x86,
+	0xD8, 0xAD, 0xD9, 0x85, 0x46, 0xD9, 0x86, 0xD8,
+	0xAD, 0xD9, 0x89, 0x46, 0xD9, 0x86, 0xD8, 0xAD,
+	0xD9, 0x8A, 0x46, 0xD9, 0x86, 0xD9, 0x85, 0xD9,
+	0x89, 0x46, 0xD9, 0x86, 0xD9, 0x85, 0xD9, 0x8A,
+	// Bytes 2580 - 25bf
+	0x46, 0xD9, 0x87, 0xD9, 0x85, 0xD8, 0xAC, 0x46,
+	0xD9, 0x87, 0xD9, 0x85, 0xD9, 0x85, 0x46, 0xD9,
+	0x8A, 0xD8, 0xAC, 0xD9, 0x8A, 0x46, 0xD9, 0x8A,
+	0xD8, 0xAD, 0xD9, 0x8A, 0x46, 0xD9, 0x8A, 0xD9,
+	0x85, 0xD9, 0x85, 0x46, 0xD9, 0x8A, 0xD9, 0x85,
+	0xD9, 0x8A, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8,
+	0xA7, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xAC,
+	0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xAD, 0x46,
+	// Bytes 25c0 - 25ff
+	0xD9, 0x8A, 0xD9, 0x94, 0xD8, 0xAE, 0x46, 0xD9,
+	0x8A, 0xD9, 0x94, 0xD8, 0xB1, 0x46, 0xD9, 0x8A,
+	0xD9, 0x94, 0xD8, 0xB2, 0x46, 0xD9, 0x8A, 0xD9,
+	0x94, 0xD9, 0x85, 0x46, 0xD9, 0x8A, 0xD9, 0x94,
+	0xD9, 0x86, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD9,
+	0x87, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x88,
+	0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x89, 0x46,
+	0xD9, 0x8A, 0xD9, 0x94, 0xD9, 0x8A, 0x46, 0xD9,
+	// Bytes 2600 - 263f
+	0x8A, 0xD9, 0x94, 0xDB, 0x86, 0x46, 0xD9, 0x8A,
+	0xD9, 0x94, 0xDB, 0x87, 0x46, 0xD9, 0x8A, 0xD9,
+	0x94, 0xDB, 0x88, 0x46, 0xD9, 0x8A, 0xD9, 0x94,
+	0xDB, 0x90, 0x46, 0xD9, 0x8A, 0xD9, 0x94, 0xDB,
+	0x95, 0x46, 0xE0, 0xB9, 0x8D, 0xE0, 0xB8, 0xB2,
+	0x46, 0xE0, 0xBA, 0xAB, 0xE0, 0xBA, 0x99, 0x46,
+	0xE0, 0xBA, 0xAB, 0xE0, 0xBA, 0xA1, 0x46, 0xE0,
+	0xBB, 0x8D, 0xE0, 0xBA, 0xB2, 0x46, 0xE0, 0xBD,
+	// Bytes 2640 - 267f
+	0x80, 0xE0, 0xBE, 0xB5, 0x46, 0xE0, 0xBD, 0x82,
+	0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBD, 0x8C, 0xE0,
+	0xBE, 0xB7, 0x46, 0xE0, 0xBD, 0x91, 0xE0, 0xBE,
+	0xB7, 0x46, 0xE0, 0xBD, 0x96, 0xE0, 0xBE, 0xB7,
+	0x46, 0xE0, 0xBD, 0x9B, 0xE0, 0xBE, 0xB7, 0x46,
+	0xE0, 0xBE, 0x90, 0xE0, 0xBE, 0xB5, 0x46, 0xE0,
+	0xBE, 0x92, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBE,
+	0x9C, 0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBE, 0xA1,
+	// Bytes 2680 - 26bf
+	0xE0, 0xBE, 0xB7, 0x46, 0xE0, 0xBE, 0xA6, 0xE0,
+	0xBE, 0xB7, 0x46, 0xE0, 0xBE, 0xAB, 0xE0, 0xBE,
+	0xB7, 0x46, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2,
+	0x46, 0xE2, 0x80, 0xB5, 0xE2, 0x80, 0xB5, 0x46,
+	0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB, 0x46, 0xE2,
+	0x88, 0xAE, 0xE2, 0x88, 0xAE, 0x46, 0xE3, 0x81,
+	0xBB, 0xE3, 0x81, 0x8B, 0x46, 0xE3, 0x82, 0x88,
+	0xE3, 0x82, 0x8A, 0x46, 0xE3, 0x82, 0xAD, 0xE3,
+	// Bytes 26c0 - 26ff
+	0x83, 0xAD, 0x46, 0xE3, 0x82, 0xB3, 0xE3, 0x82,
+	0xB3, 0x46, 0xE3, 0x82, 0xB3, 0xE3, 0x83, 0x88,
+	0x46, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xB3, 0x46,
+	0xE3, 0x83, 0x8A, 0xE3, 0x83, 0x8E, 0x46, 0xE3,
+	0x83, 0x9B, 0xE3, 0x83, 0xB3, 0x46, 0xE3, 0x83,
+	0x9F, 0xE3, 0x83, 0xAA, 0x46, 0xE3, 0x83, 0xAA,
+	0xE3, 0x83, 0xA9, 0x46, 0xE3, 0x83, 0xAC, 0xE3,
+	0x83, 0xA0, 0x46, 0xE5, 0xA4, 0xA7, 0xE6, 0xAD,
+	// Bytes 2700 - 273f
+	0xA3, 0x46, 0xE5, 0xB9, 0xB3, 0xE6, 0x88, 0x90,
+	0x46, 0xE6, 0x98, 0x8E, 0xE6, 0xB2, 0xBB, 0x46,
+	0xE6, 0x98, 0xAD, 0xE5, 0x92, 0x8C, 0x47, 0x72,
+	0x61, 0x64, 0xE2, 0x88, 0x95, 0x73, 0x47, 0xE3,
+	0x80, 0x94, 0x53, 0xE3, 0x80, 0x95, 0x48, 0x28,
+	0xE1, 0x84, 0x80, 0xE1, 0x85, 0xA1, 0x29, 0x48,
+	0x28, 0xE1, 0x84, 0x82, 0xE1, 0x85, 0xA1, 0x29,
+	0x48, 0x28, 0xE1, 0x84, 0x83, 0xE1, 0x85, 0xA1,
+	// Bytes 2740 - 277f
+	0x29, 0x48, 0x28, 0xE1, 0x84, 0x85, 0xE1, 0x85,
+	0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x86, 0xE1,
+	0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x87,
+	0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84,
+	0x89, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1,
+	0x84, 0x8B, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28,
+	0xE1, 0x84, 0x8C, 0xE1, 0x85, 0xA1, 0x29, 0x48,
+	0x28, 0xE1, 0x84, 0x8C, 0xE1, 0x85, 0xAE, 0x29,
+	// Bytes 2780 - 27bf
+	0x48, 0x28, 0xE1, 0x84, 0x8E, 0xE1, 0x85, 0xA1,
+	0x29, 0x48, 0x28, 0xE1, 0x84, 0x8F, 0xE1, 0x85,
+	0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x90, 0xE1,
+	0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84, 0x91,
+	0xE1, 0x85, 0xA1, 0x29, 0x48, 0x28, 0xE1, 0x84,
+	0x92, 0xE1, 0x85, 0xA1, 0x29, 0x48, 0x72, 0x61,
+	0x64, 0xE2, 0x88, 0x95, 0x73, 0x32, 0x48, 0xD8,
+	0xA7, 0xD9, 0x83, 0xD8, 0xA8, 0xD8, 0xB1, 0x48,
+	// Bytes 27c0 - 27ff
+	0xD8, 0xA7, 0xD9, 0x84, 0xD9, 0x84, 0xD9, 0x87,
+	0x48, 0xD8, 0xB1, 0xD8, 0xB3, 0xD9, 0x88, 0xD9,
+	0x84, 0x48, 0xD8, 0xB1, 0xDB, 0x8C, 0xD8, 0xA7,
+	0xD9, 0x84, 0x48, 0xD8, 0xB5, 0xD9, 0x84, 0xD8,
+	0xB9, 0xD9, 0x85, 0x48, 0xD8, 0xB9, 0xD9, 0x84,
+	0xD9, 0x8A, 0xD9, 0x87, 0x48, 0xD9, 0x85, 0xD8,
+	0xAD, 0xD9, 0x85, 0xD8, 0xAF, 0x48, 0xD9, 0x88,
+	0xD8, 0xB3, 0xD9, 0x84, 0xD9, 0x85, 0x49, 0xE2,
+	// Bytes 2800 - 283f
+	0x80, 0xB2, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2,
+	0x49, 0xE2, 0x80, 0xB5, 0xE2, 0x80, 0xB5, 0xE2,
+	0x80, 0xB5, 0x49, 0xE2, 0x88, 0xAB, 0xE2, 0x88,
+	0xAB, 0xE2, 0x88, 0xAB, 0x49, 0xE2, 0x88, 0xAE,
+	0xE2, 0x88, 0xAE, 0xE2, 0x88, 0xAE, 0x49, 0xE3,
+	0x80, 0x94, 0xE4, 0xB8, 0x89, 0xE3, 0x80, 0x95,
+	0x49, 0xE3, 0x80, 0x94, 0xE4, 0xBA, 0x8C, 0xE3,
+	0x80, 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE5, 0x8B,
+	// Bytes 2840 - 287f
+	0x9D, 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80, 0x94,
+	0xE5, 0xAE, 0x89, 0xE3, 0x80, 0x95, 0x49, 0xE3,
+	0x80, 0x94, 0xE6, 0x89, 0x93, 0xE3, 0x80, 0x95,
+	0x49, 0xE3, 0x80, 0x94, 0xE6, 0x95, 0x97, 0xE3,
+	0x80, 0x95, 0x49, 0xE3, 0x80, 0x94, 0xE6, 0x9C,
+	0xAC, 0xE3, 0x80, 0x95, 0x49, 0xE3, 0x80, 0x94,
+	0xE7, 0x82, 0xB9, 0xE3, 0x80, 0x95, 0x49, 0xE3,
+	0x80, 0x94, 0xE7, 0x9B, 0x97, 0xE3, 0x80, 0x95,
+	// Bytes 2880 - 28bf
+	0x49, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xBC, 0xE3,
+	0x83, 0xAB, 0x49, 0xE3, 0x82, 0xA4, 0xE3, 0x83,
+	0xB3, 0xE3, 0x83, 0x81, 0x49, 0xE3, 0x82, 0xA6,
+	0xE3, 0x82, 0xA9, 0xE3, 0x83, 0xB3, 0x49, 0xE3,
+	0x82, 0xAA, 0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xB9,
+	0x49, 0xE3, 0x82, 0xAA, 0xE3, 0x83, 0xBC, 0xE3,
+	0x83, 0xA0, 0x49, 0xE3, 0x82, 0xAB, 0xE3, 0x82,
+	0xA4, 0xE3, 0x83, 0xAA, 0x49, 0xE3, 0x82, 0xB1,
+	// Bytes 28c0 - 28ff
+	0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xB9, 0x49, 0xE3,
+	0x82, 0xB3, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x8A,
+	0x49, 0xE3, 0x82, 0xBB, 0xE3, 0x83, 0xB3, 0xE3,
+	0x83, 0x81, 0x49, 0xE3, 0x82, 0xBB, 0xE3, 0x83,
+	0xB3, 0xE3, 0x83, 0x88, 0x49, 0xE3, 0x83, 0x86,
+	0xE3, 0x82, 0x99, 0xE3, 0x82, 0xB7, 0x49, 0xE3,
+	0x83, 0x88, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAB,
+	0x49, 0xE3, 0x83, 0x8E, 0xE3, 0x83, 0x83, 0xE3,
+	// Bytes 2900 - 293f
+	0x83, 0x88, 0x49, 0xE3, 0x83, 0x8F, 0xE3, 0x82,
+	0xA4, 0xE3, 0x83, 0x84, 0x49, 0xE3, 0x83, 0x92,
+	0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAB, 0x49, 0xE3,
+	0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xB3,
+	0x49, 0xE3, 0x83, 0x95, 0xE3, 0x83, 0xA9, 0xE3,
+	0x83, 0xB3, 0x49, 0xE3, 0x83, 0x98, 0xE3, 0x82,
+	0x9A, 0xE3, 0x82, 0xBD, 0x49, 0xE3, 0x83, 0x98,
+	0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x84, 0x49, 0xE3,
+	// Bytes 2940 - 297f
+	0x83, 0x9B, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0xAB,
+	0x49, 0xE3, 0x83, 0x9B, 0xE3, 0x83, 0xBC, 0xE3,
+	0x83, 0xB3, 0x49, 0xE3, 0x83, 0x9E, 0xE3, 0x82,
+	0xA4, 0xE3, 0x83, 0xAB, 0x49, 0xE3, 0x83, 0x9E,
+	0xE3, 0x83, 0x83, 0xE3, 0x83, 0x8F, 0x49, 0xE3,
+	0x83, 0x9E, 0xE3, 0x83, 0xAB, 0xE3, 0x82, 0xAF,
+	0x49, 0xE3, 0x83, 0xA4, 0xE3, 0x83, 0xBC, 0xE3,
+	0x83, 0xAB, 0x49, 0xE3, 0x83, 0xA6, 0xE3, 0x82,
+	// Bytes 2980 - 29bf
+	0xA2, 0xE3, 0x83, 0xB3, 0x49, 0xE3, 0x83, 0xAF,
+	0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88, 0x4C, 0xE2,
+	0x80, 0xB2, 0xE2, 0x80, 0xB2, 0xE2, 0x80, 0xB2,
+	0xE2, 0x80, 0xB2, 0x4C, 0xE2, 0x88, 0xAB, 0xE2,
+	0x88, 0xAB, 0xE2, 0x88, 0xAB, 0xE2, 0x88, 0xAB,
+	0x4C, 0xE3, 0x82, 0xA2, 0xE3, 0x83, 0xAB, 0xE3,
+	0x83, 0x95, 0xE3, 0x82, 0xA1, 0x4C, 0xE3, 0x82,
+	0xA8, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xAB, 0xE3,
+	// Bytes 29c0 - 29ff
+	0x83, 0xBC, 0x4C, 0xE3, 0x82, 0xAB, 0xE3, 0x82,
+	0x99, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xB3, 0x4C,
+	0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+	0xB3, 0xE3, 0x83, 0x9E, 0x4C, 0xE3, 0x82, 0xAB,
+	0xE3, 0x83, 0xA9, 0xE3, 0x83, 0x83, 0xE3, 0x83,
+	0x88, 0x4C, 0xE3, 0x82, 0xAB, 0xE3, 0x83, 0xAD,
+	0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xBC, 0x4C, 0xE3,
+	0x82, 0xAD, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0x8B,
+	// Bytes 2a00 - 2a3f
+	0xE3, 0x83, 0xBC, 0x4C, 0xE3, 0x82, 0xAD, 0xE3,
+	0x83, 0xA5, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xBC,
+	0x4C, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3,
+	0x83, 0xA9, 0xE3, 0x83, 0xA0, 0x4C, 0xE3, 0x82,
+	0xAF, 0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xBC, 0xE3,
+	0x83, 0x8D, 0x4C, 0xE3, 0x82, 0xB5, 0xE3, 0x82,
+	0xA4, 0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAB, 0x4C,
+	0xE3, 0x82, 0xBF, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+	// Bytes 2a40 - 2a7f
+	0xBC, 0xE3, 0x82, 0xB9, 0x4C, 0xE3, 0x83, 0x8F,
+	0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+	0x84, 0x4C, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A,
+	0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAB, 0x4C, 0xE3,
+	0x83, 0x95, 0xE3, 0x82, 0xA3, 0xE3, 0x83, 0xBC,
+	0xE3, 0x83, 0x88, 0x4C, 0xE3, 0x83, 0x98, 0xE3,
+	0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0xBF,
+	0x4C, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3,
+	// Bytes 2a80 - 2abf
+	0x83, 0x8B, 0xE3, 0x83, 0x92, 0x4C, 0xE3, 0x83,
+	0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xB3, 0xE3,
+	0x82, 0xB9, 0x4C, 0xE3, 0x83, 0x9B, 0xE3, 0x82,
+	0x99, 0xE3, 0x83, 0xAB, 0xE3, 0x83, 0x88, 0x4C,
+	0xE3, 0x83, 0x9E, 0xE3, 0x82, 0xA4, 0xE3, 0x82,
+	0xAF, 0xE3, 0x83, 0xAD, 0x4C, 0xE3, 0x83, 0x9F,
+	0xE3, 0x82, 0xAF, 0xE3, 0x83, 0xAD, 0xE3, 0x83,
+	0xB3, 0x4C, 0xE3, 0x83, 0xA1, 0xE3, 0x83, 0xBC,
+	// Bytes 2ac0 - 2aff
+	0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0x4C, 0xE3,
+	0x83, 0xAA, 0xE3, 0x83, 0x83, 0xE3, 0x83, 0x88,
+	0xE3, 0x83, 0xAB, 0x4C, 0xE3, 0x83, 0xAB, 0xE3,
+	0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC,
+	0x4C, 0xE6, 0xA0, 0xAA, 0xE5, 0xBC, 0x8F, 0xE4,
+	0xBC, 0x9A, 0xE7, 0xA4, 0xBE, 0x4E, 0x28, 0xE1,
+	0x84, 0x8B, 0xE1, 0x85, 0xA9, 0xE1, 0x84, 0x92,
+	0xE1, 0x85, 0xAE, 0x29, 0x4F, 0xD8, 0xAC, 0xD9,
+	// Bytes 2b00 - 2b3f
+	0x84, 0x20, 0xD8, 0xAC, 0xD9, 0x84, 0xD8, 0xA7,
+	0xD9, 0x84, 0xD9, 0x87, 0x4F, 0xE3, 0x82, 0xA2,
+	0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x9A, 0xE3, 0x83,
+	0xBC, 0xE3, 0x83, 0x88, 0x4F, 0xE3, 0x82, 0xA2,
+	0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x98, 0xE3, 0x82,
+	0x9A, 0xE3, 0x82, 0xA2, 0x4F, 0xE3, 0x82, 0xAD,
+	0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xAF, 0xE3, 0x83,
+	0x83, 0xE3, 0x83, 0x88, 0x4F, 0xE3, 0x82, 0xB5,
+	// Bytes 2b40 - 2b7f
+	0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x81, 0xE3, 0x83,
+	0xBC, 0xE3, 0x83, 0xA0, 0x4F, 0xE3, 0x83, 0x8F,
+	0xE3, 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+	0xAC, 0xE3, 0x83, 0xAB, 0x4F, 0xE3, 0x83, 0x98,
+	0xE3, 0x82, 0xAF, 0xE3, 0x82, 0xBF, 0xE3, 0x83,
+	0xBC, 0xE3, 0x83, 0xAB, 0x4F, 0xE3, 0x83, 0x9B,
+	0xE3, 0x82, 0x9A, 0xE3, 0x82, 0xA4, 0xE3, 0x83,
+	0xB3, 0xE3, 0x83, 0x88, 0x4F, 0xE3, 0x83, 0x9E,
+	// Bytes 2b80 - 2bbf
+	0xE3, 0x83, 0xB3, 0xE3, 0x82, 0xB7, 0xE3, 0x83,
+	0xA7, 0xE3, 0x83, 0xB3, 0x4F, 0xE3, 0x83, 0xA1,
+	0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+	0x88, 0xE3, 0x83, 0xB3, 0x4F, 0xE3, 0x83, 0xAB,
+	0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x95, 0xE3, 0x82,
+	0x99, 0xE3, 0x83, 0xAB, 0x51, 0x28, 0xE1, 0x84,
+	0x8B, 0xE1, 0x85, 0xA9, 0xE1, 0x84, 0x8C, 0xE1,
+	0x85, 0xA5, 0xE1, 0x86, 0xAB, 0x29, 0x52, 0xE3,
+	// Bytes 2bc0 - 2bff
+	0x82, 0xAD, 0xE3, 0x82, 0x99, 0xE3, 0x83, 0xAB,
+	0xE3, 0x82, 0xBF, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+	0xBC, 0x52, 0xE3, 0x82, 0xAD, 0xE3, 0x83, 0xAD,
+	0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+	0xA9, 0xE3, 0x83, 0xA0, 0x52, 0xE3, 0x82, 0xAD,
+	0xE3, 0x83, 0xAD, 0xE3, 0x83, 0xA1, 0xE3, 0x83,
+	0xBC, 0xE3, 0x83, 0x88, 0xE3, 0x83, 0xAB, 0x52,
+	0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0xE3, 0x83,
+	// Bytes 2c00 - 2c3f
+	0xA9, 0xE3, 0x83, 0xA0, 0xE3, 0x83, 0x88, 0xE3,
+	0x83, 0xB3, 0x52, 0xE3, 0x82, 0xAF, 0xE3, 0x83,
+	0xAB, 0xE3, 0x82, 0xBB, 0xE3, 0x82, 0x99, 0xE3,
+	0x82, 0xA4, 0xE3, 0x83, 0xAD, 0x52, 0xE3, 0x83,
+	0x8F, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC, 0xE3,
+	0x82, 0xBB, 0xE3, 0x83, 0xB3, 0xE3, 0x83, 0x88,
+	0x52, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A, 0xE3,
+	0x82, 0xA2, 0xE3, 0x82, 0xB9, 0xE3, 0x83, 0x88,
+	// Bytes 2c40 - 2c7f
+	0xE3, 0x83, 0xAB, 0x52, 0xE3, 0x83, 0x95, 0xE3,
+	0x82, 0x99, 0xE3, 0x83, 0x83, 0xE3, 0x82, 0xB7,
+	0xE3, 0x82, 0xA7, 0xE3, 0x83, 0xAB, 0x52, 0xE3,
+	0x83, 0x9F, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0x8F,
+	0xE3, 0x82, 0x99, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+	0xAB, 0x52, 0xE3, 0x83, 0xAC, 0xE3, 0x83, 0xB3,
+	0xE3, 0x83, 0x88, 0xE3, 0x82, 0xB1, 0xE3, 0x82,
+	0x99, 0xE3, 0x83, 0xB3, 0x61, 0xD8, 0xB5, 0xD9,
+	// Bytes 2c80 - 2cbf
+	0x84, 0xD9, 0x89, 0x20, 0xD8, 0xA7, 0xD9, 0x84,
+	0xD9, 0x84, 0xD9, 0x87, 0x20, 0xD8, 0xB9, 0xD9,
+	0x84, 0xD9, 0x8A, 0xD9, 0x87, 0x20, 0xD9, 0x88,
+	0xD8, 0xB3, 0xD9, 0x84, 0xD9, 0x85, 0x06, 0xE0,
+	0xA7, 0x87, 0xE0, 0xA6, 0xBE, 0x01, 0x06, 0xE0,
+	0xA7, 0x87, 0xE0, 0xA7, 0x97, 0x01, 0x06, 0xE0,
+	0xAD, 0x87, 0xE0, 0xAC, 0xBE, 0x01, 0x06, 0xE0,
+	0xAD, 0x87, 0xE0, 0xAD, 0x96, 0x01, 0x06, 0xE0,
+	// Bytes 2cc0 - 2cff
+	0xAD, 0x87, 0xE0, 0xAD, 0x97, 0x01, 0x06, 0xE0,
+	0xAE, 0x92, 0xE0, 0xAF, 0x97, 0x01, 0x06, 0xE0,
+	0xAF, 0x86, 0xE0, 0xAE, 0xBE, 0x01, 0x06, 0xE0,
+	0xAF, 0x86, 0xE0, 0xAF, 0x97, 0x01, 0x06, 0xE0,
+	0xAF, 0x87, 0xE0, 0xAE, 0xBE, 0x01, 0x06, 0xE0,
+	0xB2, 0xBF, 0xE0, 0xB3, 0x95, 0x01, 0x06, 0xE0,
+	0xB3, 0x86, 0xE0, 0xB3, 0x95, 0x01, 0x06, 0xE0,
+	0xB3, 0x86, 0xE0, 0xB3, 0x96, 0x01, 0x06, 0xE0,
+	// Bytes 2d00 - 2d3f
+	0xB5, 0x86, 0xE0, 0xB4, 0xBE, 0x01, 0x06, 0xE0,
+	0xB5, 0x86, 0xE0, 0xB5, 0x97, 0x01, 0x06, 0xE0,
+	0xB5, 0x87, 0xE0, 0xB4, 0xBE, 0x01, 0x06, 0xE0,
+	0xB7, 0x99, 0xE0, 0xB7, 0x9F, 0x01, 0x06, 0xE1,
+	0x80, 0xA5, 0xE1, 0x80, 0xAE, 0x01, 0x06, 0xE1,
+	0xAC, 0x85, 0xE1, 0xAC, 0xB5, 0x01, 0x06, 0xE1,
+	0xAC, 0x87, 0xE1, 0xAC, 0xB5, 0x01, 0x06, 0xE1,
+	0xAC, 0x89, 0xE1, 0xAC, 0xB5, 0x01, 0x06, 0xE1,
+	// Bytes 2d40 - 2d7f
+	0xAC, 0x8B, 0xE1, 0xAC, 0xB5, 0x01, 0x06, 0xE1,
+	0xAC, 0x8D, 0xE1, 0xAC, 0xB5, 0x01, 0x06, 0xE1,
+	0xAC, 0x91, 0xE1, 0xAC, 0xB5, 0x01, 0x06, 0xE1,
+	0xAC, 0xBA, 0xE1, 0xAC, 0xB5, 0x01, 0x06, 0xE1,
+	0xAC, 0xBC, 0xE1, 0xAC, 0xB5, 0x01, 0x06, 0xE1,
+	0xAC, 0xBE, 0xE1, 0xAC, 0xB5, 0x01, 0x06, 0xE1,
+	0xAC, 0xBF, 0xE1, 0xAC, 0xB5, 0x01, 0x06, 0xE1,
+	0xAD, 0x82, 0xE1, 0xAC, 0xB5, 0x01, 0x08, 0xF0,
+	// Bytes 2d80 - 2dbf
+	0x91, 0x84, 0xB1, 0xF0, 0x91, 0x84, 0xA7, 0x01,
+	0x08, 0xF0, 0x91, 0x84, 0xB2, 0xF0, 0x91, 0x84,
+	0xA7, 0x01, 0x08, 0xF0, 0x91, 0x8D, 0x87, 0xF0,
+	0x91, 0x8C, 0xBE, 0x01, 0x08, 0xF0, 0x91, 0x8D,
+	0x87, 0xF0, 0x91, 0x8D, 0x97, 0x01, 0x08, 0xF0,
+	0x91, 0x92, 0xB9, 0xF0, 0x91, 0x92, 0xB0, 0x01,
+	0x08, 0xF0, 0x91, 0x92, 0xB9, 0xF0, 0x91, 0x92,
+	0xBA, 0x01, 0x08, 0xF0, 0x91, 0x92, 0xB9, 0xF0,
+	// Bytes 2dc0 - 2dff
+	0x91, 0x92, 0xBD, 0x01, 0x08, 0xF0, 0x91, 0x96,
+	0xB8, 0xF0, 0x91, 0x96, 0xAF, 0x01, 0x08, 0xF0,
+	0x91, 0x96, 0xB9, 0xF0, 0x91, 0x96, 0xAF, 0x01,
+	0x09, 0xE0, 0xB3, 0x86, 0xE0, 0xB3, 0x82, 0xE0,
+	0xB3, 0x95, 0x02, 0x09, 0xE0, 0xB7, 0x99, 0xE0,
+	0xB7, 0x8F, 0xE0, 0xB7, 0x8A, 0x12, 0x44, 0x44,
+	0x5A, 0xCC, 0x8C, 0xC9, 0x44, 0x44, 0x7A, 0xCC,
+	0x8C, 0xC9, 0x44, 0x64, 0x7A, 0xCC, 0x8C, 0xC9,
+	// Bytes 2e00 - 2e3f
+	0x46, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x93, 0xC9,
+	0x46, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x94, 0xC9,
+	0x46, 0xD9, 0x84, 0xD8, 0xA7, 0xD9, 0x95, 0xB5,
+	0x46, 0xE1, 0x84, 0x80, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x82, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x83, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x85, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x86, 0xE1, 0x85, 0xA1, 0x01,
+	// Bytes 2e40 - 2e7f
+	0x46, 0xE1, 0x84, 0x87, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x89, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x8B, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x8B, 0xE1, 0x85, 0xAE, 0x01,
+	0x46, 0xE1, 0x84, 0x8C, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x8E, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x8F, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x90, 0xE1, 0x85, 0xA1, 0x01,
+	// Bytes 2e80 - 2ebf
+	0x46, 0xE1, 0x84, 0x91, 0xE1, 0x85, 0xA1, 0x01,
+	0x46, 0xE1, 0x84, 0x92, 0xE1, 0x85, 0xA1, 0x01,
+	0x49, 0xE3, 0x83, 0xA1, 0xE3, 0x82, 0xAB, 0xE3,
+	0x82, 0x99, 0x0D, 0x4C, 0xE1, 0x84, 0x8C, 0xE1,
+	0x85, 0xAE, 0xE1, 0x84, 0x8B, 0xE1, 0x85, 0xB4,
+	0x01, 0x4C, 0xE3, 0x82, 0xAD, 0xE3, 0x82, 0x99,
+	0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0x0D, 0x4C,
+	0xE3, 0x82, 0xB3, 0xE3, 0x83, 0xBC, 0xE3, 0x83,
+	// Bytes 2ec0 - 2eff
+	0x9B, 0xE3, 0x82, 0x9A, 0x0D, 0x4C, 0xE3, 0x83,
+	0xA4, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x88, 0xE3,
+	0x82, 0x99, 0x0D, 0x4F, 0xE1, 0x84, 0x8E, 0xE1,
+	0x85, 0xA1, 0xE1, 0x86, 0xB7, 0xE1, 0x84, 0x80,
+	0xE1, 0x85, 0xA9, 0x01, 0x4F, 0xE3, 0x82, 0xA4,
+	0xE3, 0x83, 0x8B, 0xE3, 0x83, 0xB3, 0xE3, 0x82,
+	0xAF, 0xE3, 0x82, 0x99, 0x0D, 0x4F, 0xE3, 0x82,
+	0xB7, 0xE3, 0x83, 0xAA, 0xE3, 0x83, 0xB3, 0xE3,
+	// Bytes 2f00 - 2f3f
+	0x82, 0xAF, 0xE3, 0x82, 0x99, 0x0D, 0x4F, 0xE3,
+	0x83, 0x98, 0xE3, 0x82, 0x9A, 0xE3, 0x83, 0xBC,
+	0xE3, 0x82, 0xB7, 0xE3, 0x82, 0x99, 0x0D, 0x4F,
+	0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x9A, 0xE3, 0x83,
+	0xB3, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99, 0x0D,
+	0x52, 0xE3, 0x82, 0xA8, 0xE3, 0x82, 0xB9, 0xE3,
+	0x82, 0xAF, 0xE3, 0x83, 0xBC, 0xE3, 0x83, 0x88,
+	0xE3, 0x82, 0x99, 0x0D, 0x52, 0xE3, 0x83, 0x95,
+	// Bytes 2f40 - 2f7f
+	0xE3, 0x82, 0xA1, 0xE3, 0x83, 0xA9, 0xE3, 0x83,
+	0x83, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99, 0x0D,
+	0x86, 0xE0, 0xB3, 0x86, 0xE0, 0xB3, 0x82, 0x01,
+	0x86, 0xE0, 0xB7, 0x99, 0xE0, 0xB7, 0x8F, 0x01,
+	0x03, 0x3C, 0xCC, 0xB8, 0x05, 0x03, 0x3D, 0xCC,
+	0xB8, 0x05, 0x03, 0x3E, 0xCC, 0xB8, 0x05, 0x03,
+	0x41, 0xCC, 0x80, 0xC9, 0x03, 0x41, 0xCC, 0x81,
+	0xC9, 0x03, 0x41, 0xCC, 0x83, 0xC9, 0x03, 0x41,
+	// Bytes 2f80 - 2fbf
+	0xCC, 0x84, 0xC9, 0x03, 0x41, 0xCC, 0x89, 0xC9,
+	0x03, 0x41, 0xCC, 0x8C, 0xC9, 0x03, 0x41, 0xCC,
+	0x8F, 0xC9, 0x03, 0x41, 0xCC, 0x91, 0xC9, 0x03,
+	0x41, 0xCC, 0xA5, 0xB5, 0x03, 0x41, 0xCC, 0xA8,
+	0xA5, 0x03, 0x42, 0xCC, 0x87, 0xC9, 0x03, 0x42,
+	0xCC, 0xA3, 0xB5, 0x03, 0x42, 0xCC, 0xB1, 0xB5,
+	0x03, 0x43, 0xCC, 0x81, 0xC9, 0x03, 0x43, 0xCC,
+	0x82, 0xC9, 0x03, 0x43, 0xCC, 0x87, 0xC9, 0x03,
+	// Bytes 2fc0 - 2fff
+	0x43, 0xCC, 0x8C, 0xC9, 0x03, 0x44, 0xCC, 0x87,
+	0xC9, 0x03, 0x44, 0xCC, 0x8C, 0xC9, 0x03, 0x44,
+	0xCC, 0xA3, 0xB5, 0x03, 0x44, 0xCC, 0xA7, 0xA5,
+	0x03, 0x44, 0xCC, 0xAD, 0xB5, 0x03, 0x44, 0xCC,
+	0xB1, 0xB5, 0x03, 0x45, 0xCC, 0x80, 0xC9, 0x03,
+	0x45, 0xCC, 0x81, 0xC9, 0x03, 0x45, 0xCC, 0x83,
+	0xC9, 0x03, 0x45, 0xCC, 0x86, 0xC9, 0x03, 0x45,
+	0xCC, 0x87, 0xC9, 0x03, 0x45, 0xCC, 0x88, 0xC9,
+	// Bytes 3000 - 303f
+	0x03, 0x45, 0xCC, 0x89, 0xC9, 0x03, 0x45, 0xCC,
+	0x8C, 0xC9, 0x03, 0x45, 0xCC, 0x8F, 0xC9, 0x03,
+	0x45, 0xCC, 0x91, 0xC9, 0x03, 0x45, 0xCC, 0xA8,
+	0xA5, 0x03, 0x45, 0xCC, 0xAD, 0xB5, 0x03, 0x45,
+	0xCC, 0xB0, 0xB5, 0x03, 0x46, 0xCC, 0x87, 0xC9,
+	0x03, 0x47, 0xCC, 0x81, 0xC9, 0x03, 0x47, 0xCC,
+	0x82, 0xC9, 0x03, 0x47, 0xCC, 0x84, 0xC9, 0x03,
+	0x47, 0xCC, 0x86, 0xC9, 0x03, 0x47, 0xCC, 0x87,
+	// Bytes 3040 - 307f
+	0xC9, 0x03, 0x47, 0xCC, 0x8C, 0xC9, 0x03, 0x47,
+	0xCC, 0xA7, 0xA5, 0x03, 0x48, 0xCC, 0x82, 0xC9,
+	0x03, 0x48, 0xCC, 0x87, 0xC9, 0x03, 0x48, 0xCC,
+	0x88, 0xC9, 0x03, 0x48, 0xCC, 0x8C, 0xC9, 0x03,
+	0x48, 0xCC, 0xA3, 0xB5, 0x03, 0x48, 0xCC, 0xA7,
+	0xA5, 0x03, 0x48, 0xCC, 0xAE, 0xB5, 0x03, 0x49,
+	0xCC, 0x80, 0xC9, 0x03, 0x49, 0xCC, 0x81, 0xC9,
+	0x03, 0x49, 0xCC, 0x82, 0xC9, 0x03, 0x49, 0xCC,
+	// Bytes 3080 - 30bf
+	0x83, 0xC9, 0x03, 0x49, 0xCC, 0x84, 0xC9, 0x03,
+	0x49, 0xCC, 0x86, 0xC9, 0x03, 0x49, 0xCC, 0x87,
+	0xC9, 0x03, 0x49, 0xCC, 0x89, 0xC9, 0x03, 0x49,
+	0xCC, 0x8C, 0xC9, 0x03, 0x49, 0xCC, 0x8F, 0xC9,
+	0x03, 0x49, 0xCC, 0x91, 0xC9, 0x03, 0x49, 0xCC,
+	0xA3, 0xB5, 0x03, 0x49, 0xCC, 0xA8, 0xA5, 0x03,
+	0x49, 0xCC, 0xB0, 0xB5, 0x03, 0x4A, 0xCC, 0x82,
+	0xC9, 0x03, 0x4B, 0xCC, 0x81, 0xC9, 0x03, 0x4B,
+	// Bytes 30c0 - 30ff
+	0xCC, 0x8C, 0xC9, 0x03, 0x4B, 0xCC, 0xA3, 0xB5,
+	0x03, 0x4B, 0xCC, 0xA7, 0xA5, 0x03, 0x4B, 0xCC,
+	0xB1, 0xB5, 0x03, 0x4C, 0xCC, 0x81, 0xC9, 0x03,
+	0x4C, 0xCC, 0x8C, 0xC9, 0x03, 0x4C, 0xCC, 0xA7,
+	0xA5, 0x03, 0x4C, 0xCC, 0xAD, 0xB5, 0x03, 0x4C,
+	0xCC, 0xB1, 0xB5, 0x03, 0x4D, 0xCC, 0x81, 0xC9,
+	0x03, 0x4D, 0xCC, 0x87, 0xC9, 0x03, 0x4D, 0xCC,
+	0xA3, 0xB5, 0x03, 0x4E, 0xCC, 0x80, 0xC9, 0x03,
+	// Bytes 3100 - 313f
+	0x4E, 0xCC, 0x81, 0xC9, 0x03, 0x4E, 0xCC, 0x83,
+	0xC9, 0x03, 0x4E, 0xCC, 0x87, 0xC9, 0x03, 0x4E,
+	0xCC, 0x8C, 0xC9, 0x03, 0x4E, 0xCC, 0xA3, 0xB5,
+	0x03, 0x4E, 0xCC, 0xA7, 0xA5, 0x03, 0x4E, 0xCC,
+	0xAD, 0xB5, 0x03, 0x4E, 0xCC, 0xB1, 0xB5, 0x03,
+	0x4F, 0xCC, 0x80, 0xC9, 0x03, 0x4F, 0xCC, 0x81,
+	0xC9, 0x03, 0x4F, 0xCC, 0x86, 0xC9, 0x03, 0x4F,
+	0xCC, 0x89, 0xC9, 0x03, 0x4F, 0xCC, 0x8B, 0xC9,
+	// Bytes 3140 - 317f
+	0x03, 0x4F, 0xCC, 0x8C, 0xC9, 0x03, 0x4F, 0xCC,
+	0x8F, 0xC9, 0x03, 0x4F, 0xCC, 0x91, 0xC9, 0x03,
+	0x50, 0xCC, 0x81, 0xC9, 0x03, 0x50, 0xCC, 0x87,
+	0xC9, 0x03, 0x52, 0xCC, 0x81, 0xC9, 0x03, 0x52,
+	0xCC, 0x87, 0xC9, 0x03, 0x52, 0xCC, 0x8C, 0xC9,
+	0x03, 0x52, 0xCC, 0x8F, 0xC9, 0x03, 0x52, 0xCC,
+	0x91, 0xC9, 0x03, 0x52, 0xCC, 0xA7, 0xA5, 0x03,
+	0x52, 0xCC, 0xB1, 0xB5, 0x03, 0x53, 0xCC, 0x82,
+	// Bytes 3180 - 31bf
+	0xC9, 0x03, 0x53, 0xCC, 0x87, 0xC9, 0x03, 0x53,
+	0xCC, 0xA6, 0xB5, 0x03, 0x53, 0xCC, 0xA7, 0xA5,
+	0x03, 0x54, 0xCC, 0x87, 0xC9, 0x03, 0x54, 0xCC,
+	0x8C, 0xC9, 0x03, 0x54, 0xCC, 0xA3, 0xB5, 0x03,
+	0x54, 0xCC, 0xA6, 0xB5, 0x03, 0x54, 0xCC, 0xA7,
+	0xA5, 0x03, 0x54, 0xCC, 0xAD, 0xB5, 0x03, 0x54,
+	0xCC, 0xB1, 0xB5, 0x03, 0x55, 0xCC, 0x80, 0xC9,
+	0x03, 0x55, 0xCC, 0x81, 0xC9, 0x03, 0x55, 0xCC,
+	// Bytes 31c0 - 31ff
+	0x82, 0xC9, 0x03, 0x55, 0xCC, 0x86, 0xC9, 0x03,
+	0x55, 0xCC, 0x89, 0xC9, 0x03, 0x55, 0xCC, 0x8A,
+	0xC9, 0x03, 0x55, 0xCC, 0x8B, 0xC9, 0x03, 0x55,
+	0xCC, 0x8C, 0xC9, 0x03, 0x55, 0xCC, 0x8F, 0xC9,
+	0x03, 0x55, 0xCC, 0x91, 0xC9, 0x03, 0x55, 0xCC,
+	0xA3, 0xB5, 0x03, 0x55, 0xCC, 0xA4, 0xB5, 0x03,
+	0x55, 0xCC, 0xA8, 0xA5, 0x03, 0x55, 0xCC, 0xAD,
+	0xB5, 0x03, 0x55, 0xCC, 0xB0, 0xB5, 0x03, 0x56,
+	// Bytes 3200 - 323f
+	0xCC, 0x83, 0xC9, 0x03, 0x56, 0xCC, 0xA3, 0xB5,
+	0x03, 0x57, 0xCC, 0x80, 0xC9, 0x03, 0x57, 0xCC,
+	0x81, 0xC9, 0x03, 0x57, 0xCC, 0x82, 0xC9, 0x03,
+	0x57, 0xCC, 0x87, 0xC9, 0x03, 0x57, 0xCC, 0x88,
+	0xC9, 0x03, 0x57, 0xCC, 0xA3, 0xB5, 0x03, 0x58,
+	0xCC, 0x87, 0xC9, 0x03, 0x58, 0xCC, 0x88, 0xC9,
+	0x03, 0x59, 0xCC, 0x80, 0xC9, 0x03, 0x59, 0xCC,
+	0x81, 0xC9, 0x03, 0x59, 0xCC, 0x82, 0xC9, 0x03,
+	// Bytes 3240 - 327f
+	0x59, 0xCC, 0x83, 0xC9, 0x03, 0x59, 0xCC, 0x84,
+	0xC9, 0x03, 0x59, 0xCC, 0x87, 0xC9, 0x03, 0x59,
+	0xCC, 0x88, 0xC9, 0x03, 0x59, 0xCC, 0x89, 0xC9,
+	0x03, 0x59, 0xCC, 0xA3, 0xB5, 0x03, 0x5A, 0xCC,
+	0x81, 0xC9, 0x03, 0x5A, 0xCC, 0x82, 0xC9, 0x03,
+	0x5A, 0xCC, 0x87, 0xC9, 0x03, 0x5A, 0xCC, 0x8C,
+	0xC9, 0x03, 0x5A, 0xCC, 0xA3, 0xB5, 0x03, 0x5A,
+	0xCC, 0xB1, 0xB5, 0x03, 0x61, 0xCC, 0x80, 0xC9,
+	// Bytes 3280 - 32bf
+	0x03, 0x61, 0xCC, 0x81, 0xC9, 0x03, 0x61, 0xCC,
+	0x83, 0xC9, 0x03, 0x61, 0xCC, 0x84, 0xC9, 0x03,
+	0x61, 0xCC, 0x89, 0xC9, 0x03, 0x61, 0xCC, 0x8C,
+	0xC9, 0x03, 0x61, 0xCC, 0x8F, 0xC9, 0x03, 0x61,
+	0xCC, 0x91, 0xC9, 0x03, 0x61, 0xCC, 0xA5, 0xB5,
+	0x03, 0x61, 0xCC, 0xA8, 0xA5, 0x03, 0x62, 0xCC,
+	0x87, 0xC9, 0x03, 0x62, 0xCC, 0xA3, 0xB5, 0x03,
+	0x62, 0xCC, 0xB1, 0xB5, 0x03, 0x63, 0xCC, 0x81,
+	// Bytes 32c0 - 32ff
+	0xC9, 0x03, 0x63, 0xCC, 0x82, 0xC9, 0x03, 0x63,
+	0xCC, 0x87, 0xC9, 0x03, 0x63, 0xCC, 0x8C, 0xC9,
+	0x03, 0x64, 0xCC, 0x87, 0xC9, 0x03, 0x64, 0xCC,
+	0x8C, 0xC9, 0x03, 0x64, 0xCC, 0xA3, 0xB5, 0x03,
+	0x64, 0xCC, 0xA7, 0xA5, 0x03, 0x64, 0xCC, 0xAD,
+	0xB5, 0x03, 0x64, 0xCC, 0xB1, 0xB5, 0x03, 0x65,
+	0xCC, 0x80, 0xC9, 0x03, 0x65, 0xCC, 0x81, 0xC9,
+	0x03, 0x65, 0xCC, 0x83, 0xC9, 0x03, 0x65, 0xCC,
+	// Bytes 3300 - 333f
+	0x86, 0xC9, 0x03, 0x65, 0xCC, 0x87, 0xC9, 0x03,
+	0x65, 0xCC, 0x88, 0xC9, 0x03, 0x65, 0xCC, 0x89,
+	0xC9, 0x03, 0x65, 0xCC, 0x8C, 0xC9, 0x03, 0x65,
+	0xCC, 0x8F, 0xC9, 0x03, 0x65, 0xCC, 0x91, 0xC9,
+	0x03, 0x65, 0xCC, 0xA8, 0xA5, 0x03, 0x65, 0xCC,
+	0xAD, 0xB5, 0x03, 0x65, 0xCC, 0xB0, 0xB5, 0x03,
+	0x66, 0xCC, 0x87, 0xC9, 0x03, 0x67, 0xCC, 0x81,
+	0xC9, 0x03, 0x67, 0xCC, 0x82, 0xC9, 0x03, 0x67,
+	// Bytes 3340 - 337f
+	0xCC, 0x84, 0xC9, 0x03, 0x67, 0xCC, 0x86, 0xC9,
+	0x03, 0x67, 0xCC, 0x87, 0xC9, 0x03, 0x67, 0xCC,
+	0x8C, 0xC9, 0x03, 0x67, 0xCC, 0xA7, 0xA5, 0x03,
+	0x68, 0xCC, 0x82, 0xC9, 0x03, 0x68, 0xCC, 0x87,
+	0xC9, 0x03, 0x68, 0xCC, 0x88, 0xC9, 0x03, 0x68,
+	0xCC, 0x8C, 0xC9, 0x03, 0x68, 0xCC, 0xA3, 0xB5,
+	0x03, 0x68, 0xCC, 0xA7, 0xA5, 0x03, 0x68, 0xCC,
+	0xAE, 0xB5, 0x03, 0x68, 0xCC, 0xB1, 0xB5, 0x03,
+	// Bytes 3380 - 33bf
+	0x69, 0xCC, 0x80, 0xC9, 0x03, 0x69, 0xCC, 0x81,
+	0xC9, 0x03, 0x69, 0xCC, 0x82, 0xC9, 0x03, 0x69,
+	0xCC, 0x83, 0xC9, 0x03, 0x69, 0xCC, 0x84, 0xC9,
+	0x03, 0x69, 0xCC, 0x86, 0xC9, 0x03, 0x69, 0xCC,
+	0x89, 0xC9, 0x03, 0x69, 0xCC, 0x8C, 0xC9, 0x03,
+	0x69, 0xCC, 0x8F, 0xC9, 0x03, 0x69, 0xCC, 0x91,
+	0xC9, 0x03, 0x69, 0xCC, 0xA3, 0xB5, 0x03, 0x69,
+	0xCC, 0xA8, 0xA5, 0x03, 0x69, 0xCC, 0xB0, 0xB5,
+	// Bytes 33c0 - 33ff
+	0x03, 0x6A, 0xCC, 0x82, 0xC9, 0x03, 0x6A, 0xCC,
+	0x8C, 0xC9, 0x03, 0x6B, 0xCC, 0x81, 0xC9, 0x03,
+	0x6B, 0xCC, 0x8C, 0xC9, 0x03, 0x6B, 0xCC, 0xA3,
+	0xB5, 0x03, 0x6B, 0xCC, 0xA7, 0xA5, 0x03, 0x6B,
+	0xCC, 0xB1, 0xB5, 0x03, 0x6C, 0xCC, 0x81, 0xC9,
+	0x03, 0x6C, 0xCC, 0x8C, 0xC9, 0x03, 0x6C, 0xCC,
+	0xA7, 0xA5, 0x03, 0x6C, 0xCC, 0xAD, 0xB5, 0x03,
+	0x6C, 0xCC, 0xB1, 0xB5, 0x03, 0x6D, 0xCC, 0x81,
+	// Bytes 3400 - 343f
+	0xC9, 0x03, 0x6D, 0xCC, 0x87, 0xC9, 0x03, 0x6D,
+	0xCC, 0xA3, 0xB5, 0x03, 0x6E, 0xCC, 0x80, 0xC9,
+	0x03, 0x6E, 0xCC, 0x81, 0xC9, 0x03, 0x6E, 0xCC,
+	0x83, 0xC9, 0x03, 0x6E, 0xCC, 0x87, 0xC9, 0x03,
+	0x6E, 0xCC, 0x8C, 0xC9, 0x03, 0x6E, 0xCC, 0xA3,
+	0xB5, 0x03, 0x6E, 0xCC, 0xA7, 0xA5, 0x03, 0x6E,
+	0xCC, 0xAD, 0xB5, 0x03, 0x6E, 0xCC, 0xB1, 0xB5,
+	0x03, 0x6F, 0xCC, 0x80, 0xC9, 0x03, 0x6F, 0xCC,
+	// Bytes 3440 - 347f
+	0x81, 0xC9, 0x03, 0x6F, 0xCC, 0x86, 0xC9, 0x03,
+	0x6F, 0xCC, 0x89, 0xC9, 0x03, 0x6F, 0xCC, 0x8B,
+	0xC9, 0x03, 0x6F, 0xCC, 0x8C, 0xC9, 0x03, 0x6F,
+	0xCC, 0x8F, 0xC9, 0x03, 0x6F, 0xCC, 0x91, 0xC9,
+	0x03, 0x70, 0xCC, 0x81, 0xC9, 0x03, 0x70, 0xCC,
+	0x87, 0xC9, 0x03, 0x72, 0xCC, 0x81, 0xC9, 0x03,
+	0x72, 0xCC, 0x87, 0xC9, 0x03, 0x72, 0xCC, 0x8C,
+	0xC9, 0x03, 0x72, 0xCC, 0x8F, 0xC9, 0x03, 0x72,
+	// Bytes 3480 - 34bf
+	0xCC, 0x91, 0xC9, 0x03, 0x72, 0xCC, 0xA7, 0xA5,
+	0x03, 0x72, 0xCC, 0xB1, 0xB5, 0x03, 0x73, 0xCC,
+	0x82, 0xC9, 0x03, 0x73, 0xCC, 0x87, 0xC9, 0x03,
+	0x73, 0xCC, 0xA6, 0xB5, 0x03, 0x73, 0xCC, 0xA7,
+	0xA5, 0x03, 0x74, 0xCC, 0x87, 0xC9, 0x03, 0x74,
+	0xCC, 0x88, 0xC9, 0x03, 0x74, 0xCC, 0x8C, 0xC9,
+	0x03, 0x74, 0xCC, 0xA3, 0xB5, 0x03, 0x74, 0xCC,
+	0xA6, 0xB5, 0x03, 0x74, 0xCC, 0xA7, 0xA5, 0x03,
+	// Bytes 34c0 - 34ff
+	0x74, 0xCC, 0xAD, 0xB5, 0x03, 0x74, 0xCC, 0xB1,
+	0xB5, 0x03, 0x75, 0xCC, 0x80, 0xC9, 0x03, 0x75,
+	0xCC, 0x81, 0xC9, 0x03, 0x75, 0xCC, 0x82, 0xC9,
+	0x03, 0x75, 0xCC, 0x86, 0xC9, 0x03, 0x75, 0xCC,
+	0x89, 0xC9, 0x03, 0x75, 0xCC, 0x8A, 0xC9, 0x03,
+	0x75, 0xCC, 0x8B, 0xC9, 0x03, 0x75, 0xCC, 0x8C,
+	0xC9, 0x03, 0x75, 0xCC, 0x8F, 0xC9, 0x03, 0x75,
+	0xCC, 0x91, 0xC9, 0x03, 0x75, 0xCC, 0xA3, 0xB5,
+	// Bytes 3500 - 353f
+	0x03, 0x75, 0xCC, 0xA4, 0xB5, 0x03, 0x75, 0xCC,
+	0xA8, 0xA5, 0x03, 0x75, 0xCC, 0xAD, 0xB5, 0x03,
+	0x75, 0xCC, 0xB0, 0xB5, 0x03, 0x76, 0xCC, 0x83,
+	0xC9, 0x03, 0x76, 0xCC, 0xA3, 0xB5, 0x03, 0x77,
+	0xCC, 0x80, 0xC9, 0x03, 0x77, 0xCC, 0x81, 0xC9,
+	0x03, 0x77, 0xCC, 0x82, 0xC9, 0x03, 0x77, 0xCC,
+	0x87, 0xC9, 0x03, 0x77, 0xCC, 0x88, 0xC9, 0x03,
+	0x77, 0xCC, 0x8A, 0xC9, 0x03, 0x77, 0xCC, 0xA3,
+	// Bytes 3540 - 357f
+	0xB5, 0x03, 0x78, 0xCC, 0x87, 0xC9, 0x03, 0x78,
+	0xCC, 0x88, 0xC9, 0x03, 0x79, 0xCC, 0x80, 0xC9,
+	0x03, 0x79, 0xCC, 0x81, 0xC9, 0x03, 0x79, 0xCC,
+	0x82, 0xC9, 0x03, 0x79, 0xCC, 0x83, 0xC9, 0x03,
+	0x79, 0xCC, 0x84, 0xC9, 0x03, 0x79, 0xCC, 0x87,
+	0xC9, 0x03, 0x79, 0xCC, 0x88, 0xC9, 0x03, 0x79,
+	0xCC, 0x89, 0xC9, 0x03, 0x79, 0xCC, 0x8A, 0xC9,
+	0x03, 0x79, 0xCC, 0xA3, 0xB5, 0x03, 0x7A, 0xCC,
+	// Bytes 3580 - 35bf
+	0x81, 0xC9, 0x03, 0x7A, 0xCC, 0x82, 0xC9, 0x03,
+	0x7A, 0xCC, 0x87, 0xC9, 0x03, 0x7A, 0xCC, 0x8C,
+	0xC9, 0x03, 0x7A, 0xCC, 0xA3, 0xB5, 0x03, 0x7A,
+	0xCC, 0xB1, 0xB5, 0x04, 0xC2, 0xA8, 0xCC, 0x80,
+	0xCA, 0x04, 0xC2, 0xA8, 0xCC, 0x81, 0xCA, 0x04,
+	0xC2, 0xA8, 0xCD, 0x82, 0xCA, 0x04, 0xC3, 0x86,
+	0xCC, 0x81, 0xC9, 0x04, 0xC3, 0x86, 0xCC, 0x84,
+	0xC9, 0x04, 0xC3, 0x98, 0xCC, 0x81, 0xC9, 0x04,
+	// Bytes 35c0 - 35ff
+	0xC3, 0xA6, 0xCC, 0x81, 0xC9, 0x04, 0xC3, 0xA6,
+	0xCC, 0x84, 0xC9, 0x04, 0xC3, 0xB8, 0xCC, 0x81,
+	0xC9, 0x04, 0xC5, 0xBF, 0xCC, 0x87, 0xC9, 0x04,
+	0xC6, 0xB7, 0xCC, 0x8C, 0xC9, 0x04, 0xCA, 0x92,
+	0xCC, 0x8C, 0xC9, 0x04, 0xCE, 0x91, 0xCC, 0x80,
+	0xC9, 0x04, 0xCE, 0x91, 0xCC, 0x81, 0xC9, 0x04,
+	0xCE, 0x91, 0xCC, 0x84, 0xC9, 0x04, 0xCE, 0x91,
+	0xCC, 0x86, 0xC9, 0x04, 0xCE, 0x91, 0xCD, 0x85,
+	// Bytes 3600 - 363f
+	0xD9, 0x04, 0xCE, 0x95, 0xCC, 0x80, 0xC9, 0x04,
+	0xCE, 0x95, 0xCC, 0x81, 0xC9, 0x04, 0xCE, 0x97,
+	0xCC, 0x80, 0xC9, 0x04, 0xCE, 0x97, 0xCC, 0x81,
+	0xC9, 0x04, 0xCE, 0x97, 0xCD, 0x85, 0xD9, 0x04,
+	0xCE, 0x99, 0xCC, 0x80, 0xC9, 0x04, 0xCE, 0x99,
+	0xCC, 0x81, 0xC9, 0x04, 0xCE, 0x99, 0xCC, 0x84,
+	0xC9, 0x04, 0xCE, 0x99, 0xCC, 0x86, 0xC9, 0x04,
+	0xCE, 0x99, 0xCC, 0x88, 0xC9, 0x04, 0xCE, 0x9F,
+	// Bytes 3640 - 367f
+	0xCC, 0x80, 0xC9, 0x04, 0xCE, 0x9F, 0xCC, 0x81,
+	0xC9, 0x04, 0xCE, 0xA1, 0xCC, 0x94, 0xC9, 0x04,
+	0xCE, 0xA5, 0xCC, 0x80, 0xC9, 0x04, 0xCE, 0xA5,
+	0xCC, 0x81, 0xC9, 0x04, 0xCE, 0xA5, 0xCC, 0x84,
+	0xC9, 0x04, 0xCE, 0xA5, 0xCC, 0x86, 0xC9, 0x04,
+	0xCE, 0xA5, 0xCC, 0x88, 0xC9, 0x04, 0xCE, 0xA9,
+	0xCC, 0x80, 0xC9, 0x04, 0xCE, 0xA9, 0xCC, 0x81,
+	0xC9, 0x04, 0xCE, 0xA9, 0xCD, 0x85, 0xD9, 0x04,
+	// Bytes 3680 - 36bf
+	0xCE, 0xB1, 0xCC, 0x84, 0xC9, 0x04, 0xCE, 0xB1,
+	0xCC, 0x86, 0xC9, 0x04, 0xCE, 0xB1, 0xCD, 0x85,
+	0xD9, 0x04, 0xCE, 0xB5, 0xCC, 0x80, 0xC9, 0x04,
+	0xCE, 0xB5, 0xCC, 0x81, 0xC9, 0x04, 0xCE, 0xB7,
+	0xCD, 0x85, 0xD9, 0x04, 0xCE, 0xB9, 0xCC, 0x80,
+	0xC9, 0x04, 0xCE, 0xB9, 0xCC, 0x81, 0xC9, 0x04,
+	0xCE, 0xB9, 0xCC, 0x84, 0xC9, 0x04, 0xCE, 0xB9,
+	0xCC, 0x86, 0xC9, 0x04, 0xCE, 0xB9, 0xCD, 0x82,
+	// Bytes 36c0 - 36ff
+	0xC9, 0x04, 0xCE, 0xBF, 0xCC, 0x80, 0xC9, 0x04,
+	0xCE, 0xBF, 0xCC, 0x81, 0xC9, 0x04, 0xCF, 0x81,
+	0xCC, 0x93, 0xC9, 0x04, 0xCF, 0x81, 0xCC, 0x94,
+	0xC9, 0x04, 0xCF, 0x85, 0xCC, 0x80, 0xC9, 0x04,
+	0xCF, 0x85, 0xCC, 0x81, 0xC9, 0x04, 0xCF, 0x85,
+	0xCC, 0x84, 0xC9, 0x04, 0xCF, 0x85, 0xCC, 0x86,
+	0xC9, 0x04, 0xCF, 0x85, 0xCD, 0x82, 0xC9, 0x04,
+	0xCF, 0x89, 0xCD, 0x85, 0xD9, 0x04, 0xCF, 0x92,
+	// Bytes 3700 - 373f
+	0xCC, 0x81, 0xC9, 0x04, 0xCF, 0x92, 0xCC, 0x88,
+	0xC9, 0x04, 0xD0, 0x86, 0xCC, 0x88, 0xC9, 0x04,
+	0xD0, 0x90, 0xCC, 0x86, 0xC9, 0x04, 0xD0, 0x90,
+	0xCC, 0x88, 0xC9, 0x04, 0xD0, 0x93, 0xCC, 0x81,
+	0xC9, 0x04, 0xD0, 0x95, 0xCC, 0x80, 0xC9, 0x04,
+	0xD0, 0x95, 0xCC, 0x86, 0xC9, 0x04, 0xD0, 0x95,
+	0xCC, 0x88, 0xC9, 0x04, 0xD0, 0x96, 0xCC, 0x86,
+	0xC9, 0x04, 0xD0, 0x96, 0xCC, 0x88, 0xC9, 0x04,
+	// Bytes 3740 - 377f
+	0xD0, 0x97, 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0x98,
+	0xCC, 0x80, 0xC9, 0x04, 0xD0, 0x98, 0xCC, 0x84,
+	0xC9, 0x04, 0xD0, 0x98, 0xCC, 0x86, 0xC9, 0x04,
+	0xD0, 0x98, 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0x9A,
+	0xCC, 0x81, 0xC9, 0x04, 0xD0, 0x9E, 0xCC, 0x88,
+	0xC9, 0x04, 0xD0, 0xA3, 0xCC, 0x84, 0xC9, 0x04,
+	0xD0, 0xA3, 0xCC, 0x86, 0xC9, 0x04, 0xD0, 0xA3,
+	0xCC, 0x88, 0xC9, 0x04, 0xD0, 0xA3, 0xCC, 0x8B,
+	// Bytes 3780 - 37bf
+	0xC9, 0x04, 0xD0, 0xA7, 0xCC, 0x88, 0xC9, 0x04,
+	0xD0, 0xAB, 0xCC, 0x88, 0xC9, 0x04, 0xD0, 0xAD,
+	0xCC, 0x88, 0xC9, 0x04, 0xD0, 0xB0, 0xCC, 0x86,
+	0xC9, 0x04, 0xD0, 0xB0, 0xCC, 0x88, 0xC9, 0x04,
+	0xD0, 0xB3, 0xCC, 0x81, 0xC9, 0x04, 0xD0, 0xB5,
+	0xCC, 0x80, 0xC9, 0x04, 0xD0, 0xB5, 0xCC, 0x86,
+	0xC9, 0x04, 0xD0, 0xB5, 0xCC, 0x88, 0xC9, 0x04,
+	0xD0, 0xB6, 0xCC, 0x86, 0xC9, 0x04, 0xD0, 0xB6,
+	// Bytes 37c0 - 37ff
+	0xCC, 0x88, 0xC9, 0x04, 0xD0, 0xB7, 0xCC, 0x88,
+	0xC9, 0x04, 0xD0, 0xB8, 0xCC, 0x80, 0xC9, 0x04,
+	0xD0, 0xB8, 0xCC, 0x84, 0xC9, 0x04, 0xD0, 0xB8,
+	0xCC, 0x86, 0xC9, 0x04, 0xD0, 0xB8, 0xCC, 0x88,
+	0xC9, 0x04, 0xD0, 0xBA, 0xCC, 0x81, 0xC9, 0x04,
+	0xD0, 0xBE, 0xCC, 0x88, 0xC9, 0x04, 0xD1, 0x83,
+	0xCC, 0x84, 0xC9, 0x04, 0xD1, 0x83, 0xCC, 0x86,
+	0xC9, 0x04, 0xD1, 0x83, 0xCC, 0x88, 0xC9, 0x04,
+	// Bytes 3800 - 383f
+	0xD1, 0x83, 0xCC, 0x8B, 0xC9, 0x04, 0xD1, 0x87,
+	0xCC, 0x88, 0xC9, 0x04, 0xD1, 0x8B, 0xCC, 0x88,
+	0xC9, 0x04, 0xD1, 0x8D, 0xCC, 0x88, 0xC9, 0x04,
+	0xD1, 0x96, 0xCC, 0x88, 0xC9, 0x04, 0xD1, 0xB4,
+	0xCC, 0x8F, 0xC9, 0x04, 0xD1, 0xB5, 0xCC, 0x8F,
+	0xC9, 0x04, 0xD3, 0x98, 0xCC, 0x88, 0xC9, 0x04,
+	0xD3, 0x99, 0xCC, 0x88, 0xC9, 0x04, 0xD3, 0xA8,
+	0xCC, 0x88, 0xC9, 0x04, 0xD3, 0xA9, 0xCC, 0x88,
+	// Bytes 3840 - 387f
+	0xC9, 0x04, 0xD8, 0xA7, 0xD9, 0x93, 0xC9, 0x04,
+	0xD8, 0xA7, 0xD9, 0x94, 0xC9, 0x04, 0xD8, 0xA7,
+	0xD9, 0x95, 0xB5, 0x04, 0xD9, 0x88, 0xD9, 0x94,
+	0xC9, 0x04, 0xD9, 0x8A, 0xD9, 0x94, 0xC9, 0x04,
+	0xDB, 0x81, 0xD9, 0x94, 0xC9, 0x04, 0xDB, 0x92,
+	0xD9, 0x94, 0xC9, 0x04, 0xDB, 0x95, 0xD9, 0x94,
+	0xC9, 0x05, 0x41, 0xCC, 0x82, 0xCC, 0x80, 0xCA,
+	0x05, 0x41, 0xCC, 0x82, 0xCC, 0x81, 0xCA, 0x05,
+	// Bytes 3880 - 38bf
+	0x41, 0xCC, 0x82, 0xCC, 0x83, 0xCA, 0x05, 0x41,
+	0xCC, 0x82, 0xCC, 0x89, 0xCA, 0x05, 0x41, 0xCC,
+	0x86, 0xCC, 0x80, 0xCA, 0x05, 0x41, 0xCC, 0x86,
+	0xCC, 0x81, 0xCA, 0x05, 0x41, 0xCC, 0x86, 0xCC,
+	0x83, 0xCA, 0x05, 0x41, 0xCC, 0x86, 0xCC, 0x89,
+	0xCA, 0x05, 0x41, 0xCC, 0x87, 0xCC, 0x84, 0xCA,
+	0x05, 0x41, 0xCC, 0x88, 0xCC, 0x84, 0xCA, 0x05,
+	0x41, 0xCC, 0x8A, 0xCC, 0x81, 0xCA, 0x05, 0x41,
+	// Bytes 38c0 - 38ff
+	0xCC, 0xA3, 0xCC, 0x82, 0xCA, 0x05, 0x41, 0xCC,
+	0xA3, 0xCC, 0x86, 0xCA, 0x05, 0x43, 0xCC, 0xA7,
+	0xCC, 0x81, 0xCA, 0x05, 0x45, 0xCC, 0x82, 0xCC,
+	0x80, 0xCA, 0x05, 0x45, 0xCC, 0x82, 0xCC, 0x81,
+	0xCA, 0x05, 0x45, 0xCC, 0x82, 0xCC, 0x83, 0xCA,
+	0x05, 0x45, 0xCC, 0x82, 0xCC, 0x89, 0xCA, 0x05,
+	0x45, 0xCC, 0x84, 0xCC, 0x80, 0xCA, 0x05, 0x45,
+	0xCC, 0x84, 0xCC, 0x81, 0xCA, 0x05, 0x45, 0xCC,
+	// Bytes 3900 - 393f
+	0xA3, 0xCC, 0x82, 0xCA, 0x05, 0x45, 0xCC, 0xA7,
+	0xCC, 0x86, 0xCA, 0x05, 0x49, 0xCC, 0x88, 0xCC,
+	0x81, 0xCA, 0x05, 0x4C, 0xCC, 0xA3, 0xCC, 0x84,
+	0xCA, 0x05, 0x4F, 0xCC, 0x82, 0xCC, 0x80, 0xCA,
+	0x05, 0x4F, 0xCC, 0x82, 0xCC, 0x81, 0xCA, 0x05,
+	0x4F, 0xCC, 0x82, 0xCC, 0x83, 0xCA, 0x05, 0x4F,
+	0xCC, 0x82, 0xCC, 0x89, 0xCA, 0x05, 0x4F, 0xCC,
+	0x83, 0xCC, 0x81, 0xCA, 0x05, 0x4F, 0xCC, 0x83,
+	// Bytes 3940 - 397f
+	0xCC, 0x84, 0xCA, 0x05, 0x4F, 0xCC, 0x83, 0xCC,
+	0x88, 0xCA, 0x05, 0x4F, 0xCC, 0x84, 0xCC, 0x80,
+	0xCA, 0x05, 0x4F, 0xCC, 0x84, 0xCC, 0x81, 0xCA,
+	0x05, 0x4F, 0xCC, 0x87, 0xCC, 0x84, 0xCA, 0x05,
+	0x4F, 0xCC, 0x88, 0xCC, 0x84, 0xCA, 0x05, 0x4F,
+	0xCC, 0x9B, 0xCC, 0x80, 0xCA, 0x05, 0x4F, 0xCC,
+	0x9B, 0xCC, 0x81, 0xCA, 0x05, 0x4F, 0xCC, 0x9B,
+	0xCC, 0x83, 0xCA, 0x05, 0x4F, 0xCC, 0x9B, 0xCC,
+	// Bytes 3980 - 39bf
+	0x89, 0xCA, 0x05, 0x4F, 0xCC, 0x9B, 0xCC, 0xA3,
+	0xB6, 0x05, 0x4F, 0xCC, 0xA3, 0xCC, 0x82, 0xCA,
+	0x05, 0x4F, 0xCC, 0xA8, 0xCC, 0x84, 0xCA, 0x05,
+	0x52, 0xCC, 0xA3, 0xCC, 0x84, 0xCA, 0x05, 0x53,
+	0xCC, 0x81, 0xCC, 0x87, 0xCA, 0x05, 0x53, 0xCC,
+	0x8C, 0xCC, 0x87, 0xCA, 0x05, 0x53, 0xCC, 0xA3,
+	0xCC, 0x87, 0xCA, 0x05, 0x55, 0xCC, 0x83, 0xCC,
+	0x81, 0xCA, 0x05, 0x55, 0xCC, 0x84, 0xCC, 0x88,
+	// Bytes 39c0 - 39ff
+	0xCA, 0x05, 0x55, 0xCC, 0x88, 0xCC, 0x80, 0xCA,
+	0x05, 0x55, 0xCC, 0x88, 0xCC, 0x81, 0xCA, 0x05,
+	0x55, 0xCC, 0x88, 0xCC, 0x84, 0xCA, 0x05, 0x55,
+	0xCC, 0x88, 0xCC, 0x8C, 0xCA, 0x05, 0x55, 0xCC,
+	0x9B, 0xCC, 0x80, 0xCA, 0x05, 0x55, 0xCC, 0x9B,
+	0xCC, 0x81, 0xCA, 0x05, 0x55, 0xCC, 0x9B, 0xCC,
+	0x83, 0xCA, 0x05, 0x55, 0xCC, 0x9B, 0xCC, 0x89,
+	0xCA, 0x05, 0x55, 0xCC, 0x9B, 0xCC, 0xA3, 0xB6,
+	// Bytes 3a00 - 3a3f
+	0x05, 0x61, 0xCC, 0x82, 0xCC, 0x80, 0xCA, 0x05,
+	0x61, 0xCC, 0x82, 0xCC, 0x81, 0xCA, 0x05, 0x61,
+	0xCC, 0x82, 0xCC, 0x83, 0xCA, 0x05, 0x61, 0xCC,
+	0x82, 0xCC, 0x89, 0xCA, 0x05, 0x61, 0xCC, 0x86,
+	0xCC, 0x80, 0xCA, 0x05, 0x61, 0xCC, 0x86, 0xCC,
+	0x81, 0xCA, 0x05, 0x61, 0xCC, 0x86, 0xCC, 0x83,
+	0xCA, 0x05, 0x61, 0xCC, 0x86, 0xCC, 0x89, 0xCA,
+	0x05, 0x61, 0xCC, 0x87, 0xCC, 0x84, 0xCA, 0x05,
+	// Bytes 3a40 - 3a7f
+	0x61, 0xCC, 0x88, 0xCC, 0x84, 0xCA, 0x05, 0x61,
+	0xCC, 0x8A, 0xCC, 0x81, 0xCA, 0x05, 0x61, 0xCC,
+	0xA3, 0xCC, 0x82, 0xCA, 0x05, 0x61, 0xCC, 0xA3,
+	0xCC, 0x86, 0xCA, 0x05, 0x63, 0xCC, 0xA7, 0xCC,
+	0x81, 0xCA, 0x05, 0x65, 0xCC, 0x82, 0xCC, 0x80,
+	0xCA, 0x05, 0x65, 0xCC, 0x82, 0xCC, 0x81, 0xCA,
+	0x05, 0x65, 0xCC, 0x82, 0xCC, 0x83, 0xCA, 0x05,
+	0x65, 0xCC, 0x82, 0xCC, 0x89, 0xCA, 0x05, 0x65,
+	// Bytes 3a80 - 3abf
+	0xCC, 0x84, 0xCC, 0x80, 0xCA, 0x05, 0x65, 0xCC,
+	0x84, 0xCC, 0x81, 0xCA, 0x05, 0x65, 0xCC, 0xA3,
+	0xCC, 0x82, 0xCA, 0x05, 0x65, 0xCC, 0xA7, 0xCC,
+	0x86, 0xCA, 0x05, 0x69, 0xCC, 0x88, 0xCC, 0x81,
+	0xCA, 0x05, 0x6C, 0xCC, 0xA3, 0xCC, 0x84, 0xCA,
+	0x05, 0x6F, 0xCC, 0x82, 0xCC, 0x80, 0xCA, 0x05,
+	0x6F, 0xCC, 0x82, 0xCC, 0x81, 0xCA, 0x05, 0x6F,
+	0xCC, 0x82, 0xCC, 0x83, 0xCA, 0x05, 0x6F, 0xCC,
+	// Bytes 3ac0 - 3aff
+	0x82, 0xCC, 0x89, 0xCA, 0x05, 0x6F, 0xCC, 0x83,
+	0xCC, 0x81, 0xCA, 0x05, 0x6F, 0xCC, 0x83, 0xCC,
+	0x84, 0xCA, 0x05, 0x6F, 0xCC, 0x83, 0xCC, 0x88,
+	0xCA, 0x05, 0x6F, 0xCC, 0x84, 0xCC, 0x80, 0xCA,
+	0x05, 0x6F, 0xCC, 0x84, 0xCC, 0x81, 0xCA, 0x05,
+	0x6F, 0xCC, 0x87, 0xCC, 0x84, 0xCA, 0x05, 0x6F,
+	0xCC, 0x88, 0xCC, 0x84, 0xCA, 0x05, 0x6F, 0xCC,
+	0x9B, 0xCC, 0x80, 0xCA, 0x05, 0x6F, 0xCC, 0x9B,
+	// Bytes 3b00 - 3b3f
+	0xCC, 0x81, 0xCA, 0x05, 0x6F, 0xCC, 0x9B, 0xCC,
+	0x83, 0xCA, 0x05, 0x6F, 0xCC, 0x9B, 0xCC, 0x89,
+	0xCA, 0x05, 0x6F, 0xCC, 0x9B, 0xCC, 0xA3, 0xB6,
+	0x05, 0x6F, 0xCC, 0xA3, 0xCC, 0x82, 0xCA, 0x05,
+	0x6F, 0xCC, 0xA8, 0xCC, 0x84, 0xCA, 0x05, 0x72,
+	0xCC, 0xA3, 0xCC, 0x84, 0xCA, 0x05, 0x73, 0xCC,
+	0x81, 0xCC, 0x87, 0xCA, 0x05, 0x73, 0xCC, 0x8C,
+	0xCC, 0x87, 0xCA, 0x05, 0x73, 0xCC, 0xA3, 0xCC,
+	// Bytes 3b40 - 3b7f
+	0x87, 0xCA, 0x05, 0x75, 0xCC, 0x83, 0xCC, 0x81,
+	0xCA, 0x05, 0x75, 0xCC, 0x84, 0xCC, 0x88, 0xCA,
+	0x05, 0x75, 0xCC, 0x88, 0xCC, 0x80, 0xCA, 0x05,
+	0x75, 0xCC, 0x88, 0xCC, 0x81, 0xCA, 0x05, 0x75,
+	0xCC, 0x88, 0xCC, 0x84, 0xCA, 0x05, 0x75, 0xCC,
+	0x88, 0xCC, 0x8C, 0xCA, 0x05, 0x75, 0xCC, 0x9B,
+	0xCC, 0x80, 0xCA, 0x05, 0x75, 0xCC, 0x9B, 0xCC,
+	0x81, 0xCA, 0x05, 0x75, 0xCC, 0x9B, 0xCC, 0x83,
+	// Bytes 3b80 - 3bbf
+	0xCA, 0x05, 0x75, 0xCC, 0x9B, 0xCC, 0x89, 0xCA,
+	0x05, 0x75, 0xCC, 0x9B, 0xCC, 0xA3, 0xB6, 0x05,
+	0xE1, 0xBE, 0xBF, 0xCC, 0x80, 0xCA, 0x05, 0xE1,
+	0xBE, 0xBF, 0xCC, 0x81, 0xCA, 0x05, 0xE1, 0xBE,
+	0xBF, 0xCD, 0x82, 0xCA, 0x05, 0xE1, 0xBF, 0xBE,
+	0xCC, 0x80, 0xCA, 0x05, 0xE1, 0xBF, 0xBE, 0xCC,
+	0x81, 0xCA, 0x05, 0xE1, 0xBF, 0xBE, 0xCD, 0x82,
+	0xCA, 0x05, 0xE2, 0x86, 0x90, 0xCC, 0xB8, 0x05,
+	// Bytes 3bc0 - 3bff
+	0x05, 0xE2, 0x86, 0x92, 0xCC, 0xB8, 0x05, 0x05,
+	0xE2, 0x86, 0x94, 0xCC, 0xB8, 0x05, 0x05, 0xE2,
+	0x87, 0x90, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x87,
+	0x92, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x87, 0x94,
+	0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x88, 0x83, 0xCC,
+	0xB8, 0x05, 0x05, 0xE2, 0x88, 0x88, 0xCC, 0xB8,
+	0x05, 0x05, 0xE2, 0x88, 0x8B, 0xCC, 0xB8, 0x05,
+	0x05, 0xE2, 0x88, 0xA3, 0xCC, 0xB8, 0x05, 0x05,
+	// Bytes 3c00 - 3c3f
+	0xE2, 0x88, 0xA5, 0xCC, 0xB8, 0x05, 0x05, 0xE2,
+	0x88, 0xBC, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89,
+	0x83, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89, 0x85,
+	0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89, 0x88, 0xCC,
+	0xB8, 0x05, 0x05, 0xE2, 0x89, 0x8D, 0xCC, 0xB8,
+	0x05, 0x05, 0xE2, 0x89, 0xA1, 0xCC, 0xB8, 0x05,
+	0x05, 0xE2, 0x89, 0xA4, 0xCC, 0xB8, 0x05, 0x05,
+	0xE2, 0x89, 0xA5, 0xCC, 0xB8, 0x05, 0x05, 0xE2,
+	// Bytes 3c40 - 3c7f
+	0x89, 0xB2, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89,
+	0xB3, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89, 0xB6,
+	0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x89, 0xB7, 0xCC,
+	0xB8, 0x05, 0x05, 0xE2, 0x89, 0xBA, 0xCC, 0xB8,
+	0x05, 0x05, 0xE2, 0x89, 0xBB, 0xCC, 0xB8, 0x05,
+	0x05, 0xE2, 0x89, 0xBC, 0xCC, 0xB8, 0x05, 0x05,
+	0xE2, 0x89, 0xBD, 0xCC, 0xB8, 0x05, 0x05, 0xE2,
+	0x8A, 0x82, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x8A,
+	// Bytes 3c80 - 3cbf
+	0x83, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x8A, 0x86,
+	0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x8A, 0x87, 0xCC,
+	0xB8, 0x05, 0x05, 0xE2, 0x8A, 0x91, 0xCC, 0xB8,
+	0x05, 0x05, 0xE2, 0x8A, 0x92, 0xCC, 0xB8, 0x05,
+	0x05, 0xE2, 0x8A, 0xA2, 0xCC, 0xB8, 0x05, 0x05,
+	0xE2, 0x8A, 0xA8, 0xCC, 0xB8, 0x05, 0x05, 0xE2,
+	0x8A, 0xA9, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x8A,
+	0xAB, 0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x8A, 0xB2,
+	// Bytes 3cc0 - 3cff
+	0xCC, 0xB8, 0x05, 0x05, 0xE2, 0x8A, 0xB3, 0xCC,
+	0xB8, 0x05, 0x05, 0xE2, 0x8A, 0xB4, 0xCC, 0xB8,
+	0x05, 0x05, 0xE2, 0x8A, 0xB5, 0xCC, 0xB8, 0x05,
+	0x06, 0xCE, 0x91, 0xCC, 0x93, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0x91, 0xCC, 0x94, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0x95, 0xCC, 0x93, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0x95, 0xCC, 0x93, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0x95, 0xCC, 0x94, 0xCC, 0x80, 0xCA,
+	// Bytes 3d00 - 3d3f
+	0x06, 0xCE, 0x95, 0xCC, 0x94, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0x97, 0xCC, 0x93, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0x97, 0xCC, 0x94, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0x99, 0xCC, 0x93, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0x99, 0xCC, 0x93, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0x99, 0xCC, 0x93, 0xCD, 0x82, 0xCA,
+	0x06, 0xCE, 0x99, 0xCC, 0x94, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0x99, 0xCC, 0x94, 0xCC, 0x81, 0xCA,
+	// Bytes 3d40 - 3d7f
+	0x06, 0xCE, 0x99, 0xCC, 0x94, 0xCD, 0x82, 0xCA,
+	0x06, 0xCE, 0x9F, 0xCC, 0x93, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0x9F, 0xCC, 0x93, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0x9F, 0xCC, 0x94, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0x9F, 0xCC, 0x94, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0xA5, 0xCC, 0x94, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0xA5, 0xCC, 0x94, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0xA5, 0xCC, 0x94, 0xCD, 0x82, 0xCA,
+	// Bytes 3d80 - 3dbf
+	0x06, 0xCE, 0xA9, 0xCC, 0x93, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xA9, 0xCC, 0x94, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xB1, 0xCC, 0x80, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xB1, 0xCC, 0x81, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xB1, 0xCC, 0x93, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xB1, 0xCC, 0x94, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xB1, 0xCD, 0x82, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xB5, 0xCC, 0x93, 0xCC, 0x80, 0xCA,
+	// Bytes 3dc0 - 3dff
+	0x06, 0xCE, 0xB5, 0xCC, 0x93, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0xB5, 0xCC, 0x94, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0xB5, 0xCC, 0x94, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0xB7, 0xCC, 0x80, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xB7, 0xCC, 0x81, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xB7, 0xCC, 0x93, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xB7, 0xCC, 0x94, 0xCD, 0x85, 0xDA,
+	0x06, 0xCE, 0xB7, 0xCD, 0x82, 0xCD, 0x85, 0xDA,
+	// Bytes 3e00 - 3e3f
+	0x06, 0xCE, 0xB9, 0xCC, 0x88, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0xB9, 0xCC, 0x88, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0xB9, 0xCC, 0x88, 0xCD, 0x82, 0xCA,
+	0x06, 0xCE, 0xB9, 0xCC, 0x93, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0xB9, 0xCC, 0x93, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0xB9, 0xCC, 0x93, 0xCD, 0x82, 0xCA,
+	0x06, 0xCE, 0xB9, 0xCC, 0x94, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0xB9, 0xCC, 0x94, 0xCC, 0x81, 0xCA,
+	// Bytes 3e40 - 3e7f
+	0x06, 0xCE, 0xB9, 0xCC, 0x94, 0xCD, 0x82, 0xCA,
+	0x06, 0xCE, 0xBF, 0xCC, 0x93, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0xBF, 0xCC, 0x93, 0xCC, 0x81, 0xCA,
+	0x06, 0xCE, 0xBF, 0xCC, 0x94, 0xCC, 0x80, 0xCA,
+	0x06, 0xCE, 0xBF, 0xCC, 0x94, 0xCC, 0x81, 0xCA,
+	0x06, 0xCF, 0x85, 0xCC, 0x88, 0xCC, 0x80, 0xCA,
+	0x06, 0xCF, 0x85, 0xCC, 0x88, 0xCC, 0x81, 0xCA,
+	0x06, 0xCF, 0x85, 0xCC, 0x88, 0xCD, 0x82, 0xCA,
+	// Bytes 3e80 - 3ebf
+	0x06, 0xCF, 0x85, 0xCC, 0x93, 0xCC, 0x80, 0xCA,
+	0x06, 0xCF, 0x85, 0xCC, 0x93, 0xCC, 0x81, 0xCA,
+	0x06, 0xCF, 0x85, 0xCC, 0x93, 0xCD, 0x82, 0xCA,
+	0x06, 0xCF, 0x85, 0xCC, 0x94, 0xCC, 0x80, 0xCA,
+	0x06, 0xCF, 0x85, 0xCC, 0x94, 0xCC, 0x81, 0xCA,
+	0x06, 0xCF, 0x85, 0xCC, 0x94, 0xCD, 0x82, 0xCA,
+	0x06, 0xCF, 0x89, 0xCC, 0x80, 0xCD, 0x85, 0xDA,
+	0x06, 0xCF, 0x89, 0xCC, 0x81, 0xCD, 0x85, 0xDA,
+	// Bytes 3ec0 - 3eff
+	0x06, 0xCF, 0x89, 0xCC, 0x93, 0xCD, 0x85, 0xDA,
+	0x06, 0xCF, 0x89, 0xCC, 0x94, 0xCD, 0x85, 0xDA,
+	0x06, 0xCF, 0x89, 0xCD, 0x82, 0xCD, 0x85, 0xDA,
+	0x06, 0xE0, 0xA4, 0xA8, 0xE0, 0xA4, 0xBC, 0x09,
+	0x06, 0xE0, 0xA4, 0xB0, 0xE0, 0xA4, 0xBC, 0x09,
+	0x06, 0xE0, 0xA4, 0xB3, 0xE0, 0xA4, 0xBC, 0x09,
+	0x06, 0xE0, 0xB1, 0x86, 0xE0, 0xB1, 0x96, 0x85,
+	0x06, 0xE0, 0xB7, 0x99, 0xE0, 0xB7, 0x8A, 0x11,
+	// Bytes 3f00 - 3f3f
+	0x06, 0xE3, 0x81, 0x86, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0x8B, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0x8D, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0x8F, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0x91, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0x93, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0x95, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0x97, 0xE3, 0x82, 0x99, 0x0D,
+	// Bytes 3f40 - 3f7f
+	0x06, 0xE3, 0x81, 0x99, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0x9B, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0x9D, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0x9F, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0xA1, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0xA4, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0xA6, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0xA8, 0xE3, 0x82, 0x99, 0x0D,
+	// Bytes 3f80 - 3fbf
+	0x06, 0xE3, 0x81, 0xAF, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0xAF, 0xE3, 0x82, 0x9A, 0x0D,
+	0x06, 0xE3, 0x81, 0xB2, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0xB2, 0xE3, 0x82, 0x9A, 0x0D,
+	0x06, 0xE3, 0x81, 0xB5, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0xB5, 0xE3, 0x82, 0x9A, 0x0D,
+	0x06, 0xE3, 0x81, 0xB8, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0xB8, 0xE3, 0x82, 0x9A, 0x0D,
+	// Bytes 3fc0 - 3fff
+	0x06, 0xE3, 0x81, 0xBB, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x81, 0xBB, 0xE3, 0x82, 0x9A, 0x0D,
+	0x06, 0xE3, 0x82, 0x9D, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xA6, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xAB, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xAD, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xAF, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xB1, 0xE3, 0x82, 0x99, 0x0D,
+	// Bytes 4000 - 403f
+	0x06, 0xE3, 0x82, 0xB3, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xB5, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xB7, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xB9, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xBB, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xBD, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x82, 0xBF, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0x81, 0xE3, 0x82, 0x99, 0x0D,
+	// Bytes 4040 - 407f
+	0x06, 0xE3, 0x83, 0x84, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0x86, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0x88, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0x8F, 0xE3, 0x82, 0x9A, 0x0D,
+	0x06, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0x92, 0xE3, 0x82, 0x9A, 0x0D,
+	0x06, 0xE3, 0x83, 0x95, 0xE3, 0x82, 0x99, 0x0D,
+	// Bytes 4080 - 40bf
+	0x06, 0xE3, 0x83, 0x95, 0xE3, 0x82, 0x9A, 0x0D,
+	0x06, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0x98, 0xE3, 0x82, 0x9A, 0x0D,
+	0x06, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0x9B, 0xE3, 0x82, 0x9A, 0x0D,
+	0x06, 0xE3, 0x83, 0xAF, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0xB0, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0xB1, 0xE3, 0x82, 0x99, 0x0D,
+	// Bytes 40c0 - 40ff
+	0x06, 0xE3, 0x83, 0xB2, 0xE3, 0x82, 0x99, 0x0D,
+	0x06, 0xE3, 0x83, 0xBD, 0xE3, 0x82, 0x99, 0x0D,
+	0x08, 0xCE, 0x91, 0xCC, 0x93, 0xCC, 0x80, 0xCD,
+	0x85, 0xDB, 0x08, 0xCE, 0x91, 0xCC, 0x93, 0xCC,
+	0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0x91, 0xCC,
+	0x93, 0xCD, 0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCE,
+	0x91, 0xCC, 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xDB,
+	0x08, 0xCE, 0x91, 0xCC, 0x94, 0xCC, 0x81, 0xCD,
+	// Bytes 4100 - 413f
+	0x85, 0xDB, 0x08, 0xCE, 0x91, 0xCC, 0x94, 0xCD,
+	0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0x97, 0xCC,
+	0x93, 0xCC, 0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCE,
+	0x97, 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xDB,
+	0x08, 0xCE, 0x97, 0xCC, 0x93, 0xCD, 0x82, 0xCD,
+	0x85, 0xDB, 0x08, 0xCE, 0x97, 0xCC, 0x94, 0xCC,
+	0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0x97, 0xCC,
+	0x94, 0xCC, 0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCE,
+	// Bytes 4140 - 417f
+	0x97, 0xCC, 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xDB,
+	0x08, 0xCE, 0xA9, 0xCC, 0x93, 0xCC, 0x80, 0xCD,
+	0x85, 0xDB, 0x08, 0xCE, 0xA9, 0xCC, 0x93, 0xCC,
+	0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0xA9, 0xCC,
+	0x93, 0xCD, 0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCE,
+	0xA9, 0xCC, 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xDB,
+	0x08, 0xCE, 0xA9, 0xCC, 0x94, 0xCC, 0x81, 0xCD,
+	0x85, 0xDB, 0x08, 0xCE, 0xA9, 0xCC, 0x94, 0xCD,
+	// Bytes 4180 - 41bf
+	0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0xB1, 0xCC,
+	0x93, 0xCC, 0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCE,
+	0xB1, 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xDB,
+	0x08, 0xCE, 0xB1, 0xCC, 0x93, 0xCD, 0x82, 0xCD,
+	0x85, 0xDB, 0x08, 0xCE, 0xB1, 0xCC, 0x94, 0xCC,
+	0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0xB1, 0xCC,
+	0x94, 0xCC, 0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCE,
+	0xB1, 0xCC, 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xDB,
+	// Bytes 41c0 - 41ff
+	0x08, 0xCE, 0xB7, 0xCC, 0x93, 0xCC, 0x80, 0xCD,
+	0x85, 0xDB, 0x08, 0xCE, 0xB7, 0xCC, 0x93, 0xCC,
+	0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCE, 0xB7, 0xCC,
+	0x93, 0xCD, 0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCE,
+	0xB7, 0xCC, 0x94, 0xCC, 0x80, 0xCD, 0x85, 0xDB,
+	0x08, 0xCE, 0xB7, 0xCC, 0x94, 0xCC, 0x81, 0xCD,
+	0x85, 0xDB, 0x08, 0xCE, 0xB7, 0xCC, 0x94, 0xCD,
+	0x82, 0xCD, 0x85, 0xDB, 0x08, 0xCF, 0x89, 0xCC,
+	// Bytes 4200 - 423f
+	0x93, 0xCC, 0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCF,
+	0x89, 0xCC, 0x93, 0xCC, 0x81, 0xCD, 0x85, 0xDB,
+	0x08, 0xCF, 0x89, 0xCC, 0x93, 0xCD, 0x82, 0xCD,
+	0x85, 0xDB, 0x08, 0xCF, 0x89, 0xCC, 0x94, 0xCC,
+	0x80, 0xCD, 0x85, 0xDB, 0x08, 0xCF, 0x89, 0xCC,
+	0x94, 0xCC, 0x81, 0xCD, 0x85, 0xDB, 0x08, 0xCF,
+	0x89, 0xCC, 0x94, 0xCD, 0x82, 0xCD, 0x85, 0xDB,
+	0x08, 0xF0, 0x91, 0x82, 0x99, 0xF0, 0x91, 0x82,
+	// Bytes 4240 - 427f
+	0xBA, 0x09, 0x08, 0xF0, 0x91, 0x82, 0x9B, 0xF0,
+	0x91, 0x82, 0xBA, 0x09, 0x08, 0xF0, 0x91, 0x82,
+	0xA5, 0xF0, 0x91, 0x82, 0xBA, 0x09, 0x42, 0xC2,
+	0xB4, 0x01, 0x43, 0x20, 0xCC, 0x81, 0xC9, 0x43,
+	0x20, 0xCC, 0x83, 0xC9, 0x43, 0x20, 0xCC, 0x84,
+	0xC9, 0x43, 0x20, 0xCC, 0x85, 0xC9, 0x43, 0x20,
+	0xCC, 0x86, 0xC9, 0x43, 0x20, 0xCC, 0x87, 0xC9,
+	0x43, 0x20, 0xCC, 0x88, 0xC9, 0x43, 0x20, 0xCC,
+	// Bytes 4280 - 42bf
+	0x8A, 0xC9, 0x43, 0x20, 0xCC, 0x8B, 0xC9, 0x43,
+	0x20, 0xCC, 0x93, 0xC9, 0x43, 0x20, 0xCC, 0x94,
+	0xC9, 0x43, 0x20, 0xCC, 0xA7, 0xA5, 0x43, 0x20,
+	0xCC, 0xA8, 0xA5, 0x43, 0x20, 0xCC, 0xB3, 0xB5,
+	0x43, 0x20, 0xCD, 0x82, 0xC9, 0x43, 0x20, 0xCD,
+	0x85, 0xD9, 0x43, 0x20, 0xD9, 0x8B, 0x59, 0x43,
+	0x20, 0xD9, 0x8C, 0x5D, 0x43, 0x20, 0xD9, 0x8D,
+	0x61, 0x43, 0x20, 0xD9, 0x8E, 0x65, 0x43, 0x20,
+	// Bytes 42c0 - 42ff
+	0xD9, 0x8F, 0x69, 0x43, 0x20, 0xD9, 0x90, 0x6D,
+	0x43, 0x20, 0xD9, 0x91, 0x71, 0x43, 0x20, 0xD9,
+	0x92, 0x75, 0x43, 0x41, 0xCC, 0x8A, 0xC9, 0x43,
+	0x73, 0xCC, 0x87, 0xC9, 0x43, 0xE1, 0x85, 0xA1,
+	0x01, 0x43, 0xE1, 0x85, 0xA2, 0x01, 0x43, 0xE1,
+	0x85, 0xA3, 0x01, 0x43, 0xE1, 0x85, 0xA4, 0x01,
+	0x43, 0xE1, 0x85, 0xA5, 0x01, 0x43, 0xE1, 0x85,
+	0xA6, 0x01, 0x43, 0xE1, 0x85, 0xA7, 0x01, 0x43,
+	// Bytes 4300 - 433f
+	0xE1, 0x85, 0xA8, 0x01, 0x43, 0xE1, 0x85, 0xA9,
+	0x01, 0x43, 0xE1, 0x85, 0xAA, 0x01, 0x43, 0xE1,
+	0x85, 0xAB, 0x01, 0x43, 0xE1, 0x85, 0xAC, 0x01,
+	0x43, 0xE1, 0x85, 0xAD, 0x01, 0x43, 0xE1, 0x85,
+	0xAE, 0x01, 0x43, 0xE1, 0x85, 0xAF, 0x01, 0x43,
+	0xE1, 0x85, 0xB0, 0x01, 0x43, 0xE1, 0x85, 0xB1,
+	0x01, 0x43, 0xE1, 0x85, 0xB2, 0x01, 0x43, 0xE1,
+	0x85, 0xB3, 0x01, 0x43, 0xE1, 0x85, 0xB4, 0x01,
+	// Bytes 4340 - 437f
+	0x43, 0xE1, 0x85, 0xB5, 0x01, 0x43, 0xE1, 0x86,
+	0xAA, 0x01, 0x43, 0xE1, 0x86, 0xAC, 0x01, 0x43,
+	0xE1, 0x86, 0xAD, 0x01, 0x43, 0xE1, 0x86, 0xB0,
+	0x01, 0x43, 0xE1, 0x86, 0xB1, 0x01, 0x43, 0xE1,
+	0x86, 0xB2, 0x01, 0x43, 0xE1, 0x86, 0xB3, 0x01,
+	0x43, 0xE1, 0x86, 0xB4, 0x01, 0x43, 0xE1, 0x86,
+	0xB5, 0x01, 0x44, 0x20, 0xE3, 0x82, 0x99, 0x0D,
+	0x44, 0x20, 0xE3, 0x82, 0x9A, 0x0D, 0x44, 0xC2,
+	// Bytes 4380 - 43bf
+	0xA8, 0xCC, 0x81, 0xCA, 0x44, 0xCE, 0x91, 0xCC,
+	0x81, 0xC9, 0x44, 0xCE, 0x95, 0xCC, 0x81, 0xC9,
+	0x44, 0xCE, 0x97, 0xCC, 0x81, 0xC9, 0x44, 0xCE,
+	0x99, 0xCC, 0x81, 0xC9, 0x44, 0xCE, 0x9F, 0xCC,
+	0x81, 0xC9, 0x44, 0xCE, 0xA5, 0xCC, 0x81, 0xC9,
+	0x44, 0xCE, 0xA5, 0xCC, 0x88, 0xC9, 0x44, 0xCE,
+	0xA9, 0xCC, 0x81, 0xC9, 0x44, 0xCE, 0xB1, 0xCC,
+	0x81, 0xC9, 0x44, 0xCE, 0xB5, 0xCC, 0x81, 0xC9,
+	// Bytes 43c0 - 43ff
+	0x44, 0xCE, 0xB7, 0xCC, 0x81, 0xC9, 0x44, 0xCE,
+	0xB9, 0xCC, 0x81, 0xC9, 0x44, 0xCE, 0xBF, 0xCC,
+	0x81, 0xC9, 0x44, 0xCF, 0x85, 0xCC, 0x81, 0xC9,
+	0x44, 0xCF, 0x89, 0xCC, 0x81, 0xC9, 0x44, 0xD7,
+	0x90, 0xD6, 0xB7, 0x31, 0x44, 0xD7, 0x90, 0xD6,
+	0xB8, 0x35, 0x44, 0xD7, 0x90, 0xD6, 0xBC, 0x41,
+	0x44, 0xD7, 0x91, 0xD6, 0xBC, 0x41, 0x44, 0xD7,
+	0x91, 0xD6, 0xBF, 0x49, 0x44, 0xD7, 0x92, 0xD6,
+	// Bytes 4400 - 443f
+	0xBC, 0x41, 0x44, 0xD7, 0x93, 0xD6, 0xBC, 0x41,
+	0x44, 0xD7, 0x94, 0xD6, 0xBC, 0x41, 0x44, 0xD7,
+	0x95, 0xD6, 0xB9, 0x39, 0x44, 0xD7, 0x95, 0xD6,
+	0xBC, 0x41, 0x44, 0xD7, 0x96, 0xD6, 0xBC, 0x41,
+	0x44, 0xD7, 0x98, 0xD6, 0xBC, 0x41, 0x44, 0xD7,
+	0x99, 0xD6, 0xB4, 0x25, 0x44, 0xD7, 0x99, 0xD6,
+	0xBC, 0x41, 0x44, 0xD7, 0x9A, 0xD6, 0xBC, 0x41,
+	0x44, 0xD7, 0x9B, 0xD6, 0xBC, 0x41, 0x44, 0xD7,
+	// Bytes 4440 - 447f
+	0x9B, 0xD6, 0xBF, 0x49, 0x44, 0xD7, 0x9C, 0xD6,
+	0xBC, 0x41, 0x44, 0xD7, 0x9E, 0xD6, 0xBC, 0x41,
+	0x44, 0xD7, 0xA0, 0xD6, 0xBC, 0x41, 0x44, 0xD7,
+	0xA1, 0xD6, 0xBC, 0x41, 0x44, 0xD7, 0xA3, 0xD6,
+	0xBC, 0x41, 0x44, 0xD7, 0xA4, 0xD6, 0xBC, 0x41,
+	0x44, 0xD7, 0xA4, 0xD6, 0xBF, 0x49, 0x44, 0xD7,
+	0xA6, 0xD6, 0xBC, 0x41, 0x44, 0xD7, 0xA7, 0xD6,
+	0xBC, 0x41, 0x44, 0xD7, 0xA8, 0xD6, 0xBC, 0x41,
+	// Bytes 4480 - 44bf
+	0x44, 0xD7, 0xA9, 0xD6, 0xBC, 0x41, 0x44, 0xD7,
+	0xA9, 0xD7, 0x81, 0x4D, 0x44, 0xD7, 0xA9, 0xD7,
+	0x82, 0x51, 0x44, 0xD7, 0xAA, 0xD6, 0xBC, 0x41,
+	0x44, 0xD7, 0xB2, 0xD6, 0xB7, 0x31, 0x44, 0xD8,
+	0xA7, 0xD9, 0x8B, 0x59, 0x44, 0xD8, 0xA7, 0xD9,
+	0x93, 0xC9, 0x44, 0xD8, 0xA7, 0xD9, 0x94, 0xC9,
+	0x44, 0xD8, 0xA7, 0xD9, 0x95, 0xB5, 0x44, 0xD8,
+	0xB0, 0xD9, 0xB0, 0x79, 0x44, 0xD8, 0xB1, 0xD9,
+	// Bytes 44c0 - 44ff
+	0xB0, 0x79, 0x44, 0xD9, 0x80, 0xD9, 0x8B, 0x59,
+	0x44, 0xD9, 0x80, 0xD9, 0x8E, 0x65, 0x44, 0xD9,
+	0x80, 0xD9, 0x8F, 0x69, 0x44, 0xD9, 0x80, 0xD9,
+	0x90, 0x6D, 0x44, 0xD9, 0x80, 0xD9, 0x91, 0x71,
+	0x44, 0xD9, 0x80, 0xD9, 0x92, 0x75, 0x44, 0xD9,
+	0x87, 0xD9, 0xB0, 0x79, 0x44, 0xD9, 0x88, 0xD9,
+	0x94, 0xC9, 0x44, 0xD9, 0x89, 0xD9, 0xB0, 0x79,
+	0x44, 0xD9, 0x8A, 0xD9, 0x94, 0xC9, 0x44, 0xDB,
+	// Bytes 4500 - 453f
+	0x92, 0xD9, 0x94, 0xC9, 0x44, 0xDB, 0x95, 0xD9,
+	0x94, 0xC9, 0x45, 0x20, 0xCC, 0x88, 0xCC, 0x80,
+	0xCA, 0x45, 0x20, 0xCC, 0x88, 0xCC, 0x81, 0xCA,
+	0x45, 0x20, 0xCC, 0x88, 0xCD, 0x82, 0xCA, 0x45,
+	0x20, 0xCC, 0x93, 0xCC, 0x80, 0xCA, 0x45, 0x20,
+	0xCC, 0x93, 0xCC, 0x81, 0xCA, 0x45, 0x20, 0xCC,
+	0x93, 0xCD, 0x82, 0xCA, 0x45, 0x20, 0xCC, 0x94,
+	0xCC, 0x80, 0xCA, 0x45, 0x20, 0xCC, 0x94, 0xCC,
+	// Bytes 4540 - 457f
+	0x81, 0xCA, 0x45, 0x20, 0xCC, 0x94, 0xCD, 0x82,
+	0xCA, 0x45, 0x20, 0xD9, 0x8C, 0xD9, 0x91, 0x72,
+	0x45, 0x20, 0xD9, 0x8D, 0xD9, 0x91, 0x72, 0x45,
+	0x20, 0xD9, 0x8E, 0xD9, 0x91, 0x72, 0x45, 0x20,
+	0xD9, 0x8F, 0xD9, 0x91, 0x72, 0x45, 0x20, 0xD9,
+	0x90, 0xD9, 0x91, 0x72, 0x45, 0x20, 0xD9, 0x91,
+	0xD9, 0xB0, 0x7A, 0x45, 0xE2, 0xAB, 0x9D, 0xCC,
+	0xB8, 0x05, 0x46, 0xCE, 0xB9, 0xCC, 0x88, 0xCC,
+	// Bytes 4580 - 45bf
+	0x81, 0xCA, 0x46, 0xCF, 0x85, 0xCC, 0x88, 0xCC,
+	0x81, 0xCA, 0x46, 0xD7, 0xA9, 0xD6, 0xBC, 0xD7,
+	0x81, 0x4E, 0x46, 0xD7, 0xA9, 0xD6, 0xBC, 0xD7,
+	0x82, 0x52, 0x46, 0xD9, 0x80, 0xD9, 0x8E, 0xD9,
+	0x91, 0x72, 0x46, 0xD9, 0x80, 0xD9, 0x8F, 0xD9,
+	0x91, 0x72, 0x46, 0xD9, 0x80, 0xD9, 0x90, 0xD9,
+	0x91, 0x72, 0x46, 0xE0, 0xA4, 0x95, 0xE0, 0xA4,
+	0xBC, 0x09, 0x46, 0xE0, 0xA4, 0x96, 0xE0, 0xA4,
+	// Bytes 45c0 - 45ff
+	0xBC, 0x09, 0x46, 0xE0, 0xA4, 0x97, 0xE0, 0xA4,
+	0xBC, 0x09, 0x46, 0xE0, 0xA4, 0x9C, 0xE0, 0xA4,
+	0xBC, 0x09, 0x46, 0xE0, 0xA4, 0xA1, 0xE0, 0xA4,
+	0xBC, 0x09, 0x46, 0xE0, 0xA4, 0xA2, 0xE0, 0xA4,
+	0xBC, 0x09, 0x46, 0xE0, 0xA4, 0xAB, 0xE0, 0xA4,
+	0xBC, 0x09, 0x46, 0xE0, 0xA4, 0xAF, 0xE0, 0xA4,
+	0xBC, 0x09, 0x46, 0xE0, 0xA6, 0xA1, 0xE0, 0xA6,
+	0xBC, 0x09, 0x46, 0xE0, 0xA6, 0xA2, 0xE0, 0xA6,
+	// Bytes 4600 - 463f
+	0xBC, 0x09, 0x46, 0xE0, 0xA6, 0xAF, 0xE0, 0xA6,
+	0xBC, 0x09, 0x46, 0xE0, 0xA8, 0x96, 0xE0, 0xA8,
+	0xBC, 0x09, 0x46, 0xE0, 0xA8, 0x97, 0xE0, 0xA8,
+	0xBC, 0x09, 0x46, 0xE0, 0xA8, 0x9C, 0xE0, 0xA8,
+	0xBC, 0x09, 0x46, 0xE0, 0xA8, 0xAB, 0xE0, 0xA8,
+	0xBC, 0x09, 0x46, 0xE0, 0xA8, 0xB2, 0xE0, 0xA8,
+	0xBC, 0x09, 0x46, 0xE0, 0xA8, 0xB8, 0xE0, 0xA8,
+	0xBC, 0x09, 0x46, 0xE0, 0xAC, 0xA1, 0xE0, 0xAC,
+	// Bytes 4640 - 467f
+	0xBC, 0x09, 0x46, 0xE0, 0xAC, 0xA2, 0xE0, 0xAC,
+	0xBC, 0x09, 0x46, 0xE0, 0xBE, 0xB2, 0xE0, 0xBE,
+	0x80, 0x9D, 0x46, 0xE0, 0xBE, 0xB3, 0xE0, 0xBE,
+	0x80, 0x9D, 0x46, 0xE3, 0x83, 0x86, 0xE3, 0x82,
+	0x99, 0x0D, 0x48, 0xF0, 0x9D, 0x85, 0x97, 0xF0,
+	0x9D, 0x85, 0xA5, 0xAD, 0x48, 0xF0, 0x9D, 0x85,
+	0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xAD, 0x48, 0xF0,
+	0x9D, 0x86, 0xB9, 0xF0, 0x9D, 0x85, 0xA5, 0xAD,
+	// Bytes 4680 - 46bf
+	0x48, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85,
+	0xA5, 0xAD, 0x49, 0xE0, 0xBE, 0xB2, 0xE0, 0xBD,
+	0xB1, 0xE0, 0xBE, 0x80, 0x9E, 0x49, 0xE0, 0xBE,
+	0xB3, 0xE0, 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0x9E,
+	0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85,
+	0xA5, 0xF0, 0x9D, 0x85, 0xAE, 0xAE, 0x4C, 0xF0,
+	0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0,
+	0x9D, 0x85, 0xAF, 0xAE, 0x4C, 0xF0, 0x9D, 0x85,
+	// Bytes 46c0 - 46ff
+	0x98, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85,
+	0xB0, 0xAE, 0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0,
+	0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xB1, 0xAE,
+	0x4C, 0xF0, 0x9D, 0x85, 0x98, 0xF0, 0x9D, 0x85,
+	0xA5, 0xF0, 0x9D, 0x85, 0xB2, 0xAE, 0x4C, 0xF0,
+	0x9D, 0x86, 0xB9, 0xF0, 0x9D, 0x85, 0xA5, 0xF0,
+	0x9D, 0x85, 0xAE, 0xAE, 0x4C, 0xF0, 0x9D, 0x86,
+	0xB9, 0xF0, 0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85,
+	// Bytes 4700 - 473f
+	0xAF, 0xAE, 0x4C, 0xF0, 0x9D, 0x86, 0xBA, 0xF0,
+	0x9D, 0x85, 0xA5, 0xF0, 0x9D, 0x85, 0xAE, 0xAE,
+	0x4C, 0xF0, 0x9D, 0x86, 0xBA, 0xF0, 0x9D, 0x85,
+	0xA5, 0xF0, 0x9D, 0x85, 0xAF, 0xAE, 0x83, 0x41,
+	0xCC, 0x82, 0xC9, 0x83, 0x41, 0xCC, 0x86, 0xC9,
+	0x83, 0x41, 0xCC, 0x87, 0xC9, 0x83, 0x41, 0xCC,
+	0x88, 0xC9, 0x83, 0x41, 0xCC, 0x8A, 0xC9, 0x83,
+	0x41, 0xCC, 0xA3, 0xB5, 0x83, 0x43, 0xCC, 0xA7,
+	// Bytes 4740 - 477f
+	0xA5, 0x83, 0x45, 0xCC, 0x82, 0xC9, 0x83, 0x45,
+	0xCC, 0x84, 0xC9, 0x83, 0x45, 0xCC, 0xA3, 0xB5,
+	0x83, 0x45, 0xCC, 0xA7, 0xA5, 0x83, 0x49, 0xCC,
+	0x88, 0xC9, 0x83, 0x4C, 0xCC, 0xA3, 0xB5, 0x83,
+	0x4F, 0xCC, 0x82, 0xC9, 0x83, 0x4F, 0xCC, 0x83,
+	0xC9, 0x83, 0x4F, 0xCC, 0x84, 0xC9, 0x83, 0x4F,
+	0xCC, 0x87, 0xC9, 0x83, 0x4F, 0xCC, 0x88, 0xC9,
+	0x83, 0x4F, 0xCC, 0x9B, 0xAD, 0x83, 0x4F, 0xCC,
+	// Bytes 4780 - 47bf
+	0xA3, 0xB5, 0x83, 0x4F, 0xCC, 0xA8, 0xA5, 0x83,
+	0x52, 0xCC, 0xA3, 0xB5, 0x83, 0x53, 0xCC, 0x81,
+	0xC9, 0x83, 0x53, 0xCC, 0x8C, 0xC9, 0x83, 0x53,
+	0xCC, 0xA3, 0xB5, 0x83, 0x55, 0xCC, 0x83, 0xC9,
+	0x83, 0x55, 0xCC, 0x84, 0xC9, 0x83, 0x55, 0xCC,
+	0x88, 0xC9, 0x83, 0x55, 0xCC, 0x9B, 0xAD, 0x83,
+	0x61, 0xCC, 0x82, 0xC9, 0x83, 0x61, 0xCC, 0x86,
+	0xC9, 0x83, 0x61, 0xCC, 0x87, 0xC9, 0x83, 0x61,
+	// Bytes 47c0 - 47ff
+	0xCC, 0x88, 0xC9, 0x83, 0x61, 0xCC, 0x8A, 0xC9,
+	0x83, 0x61, 0xCC, 0xA3, 0xB5, 0x83, 0x63, 0xCC,
+	0xA7, 0xA5, 0x83, 0x65, 0xCC, 0x82, 0xC9, 0x83,
+	0x65, 0xCC, 0x84, 0xC9, 0x83, 0x65, 0xCC, 0xA3,
+	0xB5, 0x83, 0x65, 0xCC, 0xA7, 0xA5, 0x83, 0x69,
+	0xCC, 0x88, 0xC9, 0x83, 0x6C, 0xCC, 0xA3, 0xB5,
+	0x83, 0x6F, 0xCC, 0x82, 0xC9, 0x83, 0x6F, 0xCC,
+	0x83, 0xC9, 0x83, 0x6F, 0xCC, 0x84, 0xC9, 0x83,
+	// Bytes 4800 - 483f
+	0x6F, 0xCC, 0x87, 0xC9, 0x83, 0x6F, 0xCC, 0x88,
+	0xC9, 0x83, 0x6F, 0xCC, 0x9B, 0xAD, 0x83, 0x6F,
+	0xCC, 0xA3, 0xB5, 0x83, 0x6F, 0xCC, 0xA8, 0xA5,
+	0x83, 0x72, 0xCC, 0xA3, 0xB5, 0x83, 0x73, 0xCC,
+	0x81, 0xC9, 0x83, 0x73, 0xCC, 0x8C, 0xC9, 0x83,
+	0x73, 0xCC, 0xA3, 0xB5, 0x83, 0x75, 0xCC, 0x83,
+	0xC9, 0x83, 0x75, 0xCC, 0x84, 0xC9, 0x83, 0x75,
+	0xCC, 0x88, 0xC9, 0x83, 0x75, 0xCC, 0x9B, 0xAD,
+	// Bytes 4840 - 487f
+	0x84, 0xCE, 0x91, 0xCC, 0x93, 0xC9, 0x84, 0xCE,
+	0x91, 0xCC, 0x94, 0xC9, 0x84, 0xCE, 0x95, 0xCC,
+	0x93, 0xC9, 0x84, 0xCE, 0x95, 0xCC, 0x94, 0xC9,
+	0x84, 0xCE, 0x97, 0xCC, 0x93, 0xC9, 0x84, 0xCE,
+	0x97, 0xCC, 0x94, 0xC9, 0x84, 0xCE, 0x99, 0xCC,
+	0x93, 0xC9, 0x84, 0xCE, 0x99, 0xCC, 0x94, 0xC9,
+	0x84, 0xCE, 0x9F, 0xCC, 0x93, 0xC9, 0x84, 0xCE,
+	0x9F, 0xCC, 0x94, 0xC9, 0x84, 0xCE, 0xA5, 0xCC,
+	// Bytes 4880 - 48bf
+	0x94, 0xC9, 0x84, 0xCE, 0xA9, 0xCC, 0x93, 0xC9,
+	0x84, 0xCE, 0xA9, 0xCC, 0x94, 0xC9, 0x84, 0xCE,
+	0xB1, 0xCC, 0x80, 0xC9, 0x84, 0xCE, 0xB1, 0xCC,
+	0x81, 0xC9, 0x84, 0xCE, 0xB1, 0xCC, 0x93, 0xC9,
+	0x84, 0xCE, 0xB1, 0xCC, 0x94, 0xC9, 0x84, 0xCE,
+	0xB1, 0xCD, 0x82, 0xC9, 0x84, 0xCE, 0xB5, 0xCC,
+	0x93, 0xC9, 0x84, 0xCE, 0xB5, 0xCC, 0x94, 0xC9,
+	0x84, 0xCE, 0xB7, 0xCC, 0x80, 0xC9, 0x84, 0xCE,
+	// Bytes 48c0 - 48ff
+	0xB7, 0xCC, 0x81, 0xC9, 0x84, 0xCE, 0xB7, 0xCC,
+	0x93, 0xC9, 0x84, 0xCE, 0xB7, 0xCC, 0x94, 0xC9,
+	0x84, 0xCE, 0xB7, 0xCD, 0x82, 0xC9, 0x84, 0xCE,
+	0xB9, 0xCC, 0x88, 0xC9, 0x84, 0xCE, 0xB9, 0xCC,
+	0x93, 0xC9, 0x84, 0xCE, 0xB9, 0xCC, 0x94, 0xC9,
+	0x84, 0xCE, 0xBF, 0xCC, 0x93, 0xC9, 0x84, 0xCE,
+	0xBF, 0xCC, 0x94, 0xC9, 0x84, 0xCF, 0x85, 0xCC,
+	0x88, 0xC9, 0x84, 0xCF, 0x85, 0xCC, 0x93, 0xC9,
+	// Bytes 4900 - 493f
+	0x84, 0xCF, 0x85, 0xCC, 0x94, 0xC9, 0x84, 0xCF,
+	0x89, 0xCC, 0x80, 0xC9, 0x84, 0xCF, 0x89, 0xCC,
+	0x81, 0xC9, 0x84, 0xCF, 0x89, 0xCC, 0x93, 0xC9,
+	0x84, 0xCF, 0x89, 0xCC, 0x94, 0xC9, 0x84, 0xCF,
+	0x89, 0xCD, 0x82, 0xC9, 0x86, 0xCE, 0x91, 0xCC,
+	0x93, 0xCC, 0x80, 0xCA, 0x86, 0xCE, 0x91, 0xCC,
+	0x93, 0xCC, 0x81, 0xCA, 0x86, 0xCE, 0x91, 0xCC,
+	0x93, 0xCD, 0x82, 0xCA, 0x86, 0xCE, 0x91, 0xCC,
+	// Bytes 4940 - 497f
+	0x94, 0xCC, 0x80, 0xCA, 0x86, 0xCE, 0x91, 0xCC,
+	0x94, 0xCC, 0x81, 0xCA, 0x86, 0xCE, 0x91, 0xCC,
+	0x94, 0xCD, 0x82, 0xCA, 0x86, 0xCE, 0x97, 0xCC,
+	0x93, 0xCC, 0x80, 0xCA, 0x86, 0xCE, 0x97, 0xCC,
+	0x93, 0xCC, 0x81, 0xCA, 0x86, 0xCE, 0x97, 0xCC,
+	0x93, 0xCD, 0x82, 0xCA, 0x86, 0xCE, 0x97, 0xCC,
+	0x94, 0xCC, 0x80, 0xCA, 0x86, 0xCE, 0x97, 0xCC,
+	0x94, 0xCC, 0x81, 0xCA, 0x86, 0xCE, 0x97, 0xCC,
+	// Bytes 4980 - 49bf
+	0x94, 0xCD, 0x82, 0xCA, 0x86, 0xCE, 0xA9, 0xCC,
+	0x93, 0xCC, 0x80, 0xCA, 0x86, 0xCE, 0xA9, 0xCC,
+	0x93, 0xCC, 0x81, 0xCA, 0x86, 0xCE, 0xA9, 0xCC,
+	0x93, 0xCD, 0x82, 0xCA, 0x86, 0xCE, 0xA9, 0xCC,
+	0x94, 0xCC, 0x80, 0xCA, 0x86, 0xCE, 0xA9, 0xCC,
+	0x94, 0xCC, 0x81, 0xCA, 0x86, 0xCE, 0xA9, 0xCC,
+	0x94, 0xCD, 0x82, 0xCA, 0x86, 0xCE, 0xB1, 0xCC,
+	0x93, 0xCC, 0x80, 0xCA, 0x86, 0xCE, 0xB1, 0xCC,
+	// Bytes 49c0 - 49ff
+	0x93, 0xCC, 0x81, 0xCA, 0x86, 0xCE, 0xB1, 0xCC,
+	0x93, 0xCD, 0x82, 0xCA, 0x86, 0xCE, 0xB1, 0xCC,
+	0x94, 0xCC, 0x80, 0xCA, 0x86, 0xCE, 0xB1, 0xCC,
+	0x94, 0xCC, 0x81, 0xCA, 0x86, 0xCE, 0xB1, 0xCC,
+	0x94, 0xCD, 0x82, 0xCA, 0x86, 0xCE, 0xB7, 0xCC,
+	0x93, 0xCC, 0x80, 0xCA, 0x86, 0xCE, 0xB7, 0xCC,
+	0x93, 0xCC, 0x81, 0xCA, 0x86, 0xCE, 0xB7, 0xCC,
+	0x93, 0xCD, 0x82, 0xCA, 0x86, 0xCE, 0xB7, 0xCC,
+	// Bytes 4a00 - 4a3f
+	0x94, 0xCC, 0x80, 0xCA, 0x86, 0xCE, 0xB7, 0xCC,
+	0x94, 0xCC, 0x81, 0xCA, 0x86, 0xCE, 0xB7, 0xCC,
+	0x94, 0xCD, 0x82, 0xCA, 0x86, 0xCF, 0x89, 0xCC,
+	0x93, 0xCC, 0x80, 0xCA, 0x86, 0xCF, 0x89, 0xCC,
+	0x93, 0xCC, 0x81, 0xCA, 0x86, 0xCF, 0x89, 0xCC,
+	0x93, 0xCD, 0x82, 0xCA, 0x86, 0xCF, 0x89, 0xCC,
+	0x94, 0xCC, 0x80, 0xCA, 0x86, 0xCF, 0x89, 0xCC,
+	0x94, 0xCC, 0x81, 0xCA, 0x86, 0xCF, 0x89, 0xCC,
+	// Bytes 4a40 - 4a7f
+	0x94, 0xCD, 0x82, 0xCA, 0x42, 0xCC, 0x80, 0xC9,
+	0x32, 0x42, 0xCC, 0x81, 0xC9, 0x32, 0x42, 0xCC,
+	0x93, 0xC9, 0x32, 0x44, 0xCC, 0x88, 0xCC, 0x81,
+	0xCA, 0x32, 0x43, 0xE3, 0x82, 0x99, 0x0D, 0x03,
+	0x43, 0xE3, 0x82, 0x9A, 0x0D, 0x03, 0x46, 0xE0,
+	0xBD, 0xB1, 0xE0, 0xBD, 0xB2, 0x9E, 0x26, 0x46,
+	0xE0, 0xBD, 0xB1, 0xE0, 0xBD, 0xB4, 0xA2, 0x26,
+	0x46, 0xE0, 0xBD, 0xB1, 0xE0, 0xBE, 0x80, 0x9E,
+	// Bytes 4a80 - 4abf
+	0x26, 0x00, 0x01,
+}
+
+// lookup returns the trie value for the first UTF-8 encoding in s and
+// the width in bytes of this encoding. The size will be 0 if s does not
+// hold enough bytes to complete the encoding. len(s) must be greater than 0.
+func (t *nfcTrie) lookup(s []byte) (v uint16, sz int) {
+	c0 := s[0]
+	switch {
+	case c0 < 0x80: // is ASCII
+		return nfcValues[c0], 1
+	case c0 < 0xC2:
+		return 0, 1 // Illegal UTF-8: not a starter, not ASCII.
+	case c0 < 0xE0: // 2-byte UTF-8
+		if len(s) < 2 {
+			return 0, 0
+		}
+		i := nfcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c1), 2
+	case c0 < 0xF0: // 3-byte UTF-8
+		if len(s) < 3 {
+			return 0, 0
+		}
+		i := nfcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = nfcIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c2), 3
+	case c0 < 0xF8: // 4-byte UTF-8
+		if len(s) < 4 {
+			return 0, 0
+		}
+		i := nfcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = nfcIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		o = uint32(i)<<6 + uint32(c2)
+		i = nfcIndex[o]
+		c3 := s[3]
+		if c3 < 0x80 || 0xC0 <= c3 {
+			return 0, 3 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c3), 4
+	}
+	// Illegal rune
+	return 0, 1
+}
+
+// lookupUnsafe returns the trie value for the first UTF-8 encoding in s.
+// s must start with a full and valid UTF-8 encoded rune.
+func (t *nfcTrie) lookupUnsafe(s []byte) uint16 {
+	c0 := s[0]
+	if c0 < 0x80 { // is ASCII
+		return nfcValues[c0]
+	}
+	i := nfcIndex[c0]
+	if c0 < 0xE0 { // 2-byte UTF-8
+		return t.lookupValue(uint32(i), s[1])
+	}
+	i = nfcIndex[uint32(i)<<6+uint32(s[1])]
+	if c0 < 0xF0 { // 3-byte UTF-8
+		return t.lookupValue(uint32(i), s[2])
+	}
+	i = nfcIndex[uint32(i)<<6+uint32(s[2])]
+	if c0 < 0xF8 { // 4-byte UTF-8
+		return t.lookupValue(uint32(i), s[3])
+	}
+	return 0
+}
+
+// lookupString returns the trie value for the first UTF-8 encoding in s and
+// the width in bytes of this encoding. The size will be 0 if s does not
+// hold enough bytes to complete the encoding. len(s) must be greater than 0.
+func (t *nfcTrie) lookupString(s string) (v uint16, sz int) {
+	c0 := s[0]
+	switch {
+	case c0 < 0x80: // is ASCII
+		return nfcValues[c0], 1
+	case c0 < 0xC2:
+		return 0, 1 // Illegal UTF-8: not a starter, not ASCII.
+	case c0 < 0xE0: // 2-byte UTF-8
+		if len(s) < 2 {
+			return 0, 0
+		}
+		i := nfcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c1), 2
+	case c0 < 0xF0: // 3-byte UTF-8
+		if len(s) < 3 {
+			return 0, 0
+		}
+		i := nfcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = nfcIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c2), 3
+	case c0 < 0xF8: // 4-byte UTF-8
+		if len(s) < 4 {
+			return 0, 0
+		}
+		i := nfcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = nfcIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		o = uint32(i)<<6 + uint32(c2)
+		i = nfcIndex[o]
+		c3 := s[3]
+		if c3 < 0x80 || 0xC0 <= c3 {
+			return 0, 3 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c3), 4
+	}
+	// Illegal rune
+	return 0, 1
+}
+
+// lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s.
+// s must start with a full and valid UTF-8 encoded rune.
+func (t *nfcTrie) lookupStringUnsafe(s string) uint16 {
+	c0 := s[0]
+	if c0 < 0x80 { // is ASCII
+		return nfcValues[c0]
+	}
+	i := nfcIndex[c0]
+	if c0 < 0xE0 { // 2-byte UTF-8
+		return t.lookupValue(uint32(i), s[1])
+	}
+	i = nfcIndex[uint32(i)<<6+uint32(s[1])]
+	if c0 < 0xF0 { // 3-byte UTF-8
+		return t.lookupValue(uint32(i), s[2])
+	}
+	i = nfcIndex[uint32(i)<<6+uint32(s[2])]
+	if c0 < 0xF8 { // 4-byte UTF-8
+		return t.lookupValue(uint32(i), s[3])
+	}
+	return 0
+}
+
+// nfcTrie. Total size: 10332 bytes (10.09 KiB). Checksum: ad355b768fddb1b6.
+type nfcTrie struct{}
+
+func newNfcTrie(i int) *nfcTrie {
+	return &nfcTrie{}
+}
+
+// lookupValue determines the type of block n and looks up the value for b.
+func (t *nfcTrie) lookupValue(n uint32, b byte) uint16 {
+	switch {
+	case n < 44:
+		return uint16(nfcValues[n<<6+uint32(b)])
+	default:
+		n -= 44
+		return uint16(nfcSparse.lookup(n, b))
+	}
+}
+
+// nfcValues: 46 blocks, 2944 entries, 5888 bytes
+// The third block is the zero block.
+var nfcValues = [2944]uint16{
+	// Block 0x0, offset 0x0
+	0x3c: 0xa000, 0x3d: 0xa000, 0x3e: 0xa000,
+	// Block 0x1, offset 0x40
+	0x41: 0xa000, 0x42: 0xa000, 0x43: 0xa000, 0x44: 0xa000, 0x45: 0xa000,
+	0x46: 0xa000, 0x47: 0xa000, 0x48: 0xa000, 0x49: 0xa000, 0x4a: 0xa000, 0x4b: 0xa000,
+	0x4c: 0xa000, 0x4d: 0xa000, 0x4e: 0xa000, 0x4f: 0xa000, 0x50: 0xa000,
+	0x52: 0xa000, 0x53: 0xa000, 0x54: 0xa000, 0x55: 0xa000, 0x56: 0xa000, 0x57: 0xa000,
+	0x58: 0xa000, 0x59: 0xa000, 0x5a: 0xa000,
+	0x61: 0xa000, 0x62: 0xa000, 0x63: 0xa000,
+	0x64: 0xa000, 0x65: 0xa000, 0x66: 0xa000, 0x67: 0xa000, 0x68: 0xa000, 0x69: 0xa000,
+	0x6a: 0xa000, 0x6b: 0xa000, 0x6c: 0xa000, 0x6d: 0xa000, 0x6e: 0xa000, 0x6f: 0xa000,
+	0x70: 0xa000, 0x72: 0xa000, 0x73: 0xa000, 0x74: 0xa000, 0x75: 0xa000,
+	0x76: 0xa000, 0x77: 0xa000, 0x78: 0xa000, 0x79: 0xa000, 0x7a: 0xa000,
+	// Block 0x2, offset 0x80
+	// Block 0x3, offset 0xc0
+	0xc0: 0x2f6f, 0xc1: 0x2f74, 0xc2: 0x471e, 0xc3: 0x2f79, 0xc4: 0x472d, 0xc5: 0x4732,
+	0xc6: 0xa000, 0xc7: 0x473c, 0xc8: 0x2fe2, 0xc9: 0x2fe7, 0xca: 0x4741, 0xcb: 0x2ffb,
+	0xcc: 0x306e, 0xcd: 0x3073, 0xce: 0x3078, 0xcf: 0x4755, 0xd1: 0x3104,
+	0xd2: 0x3127, 0xd3: 0x312c, 0xd4: 0x475f, 0xd5: 0x4764, 0xd6: 0x4773,
+	0xd8: 0xa000, 0xd9: 0x31b3, 0xda: 0x31b8, 0xdb: 0x31bd, 0xdc: 0x47a5, 0xdd: 0x3235,
+	0xe0: 0x327b, 0xe1: 0x3280, 0xe2: 0x47af, 0xe3: 0x3285,
+	0xe4: 0x47be, 0xe5: 0x47c3, 0xe6: 0xa000, 0xe7: 0x47cd, 0xe8: 0x32ee, 0xe9: 0x32f3,
+	0xea: 0x47d2, 0xeb: 0x3307, 0xec: 0x337f, 0xed: 0x3384, 0xee: 0x3389, 0xef: 0x47e6,
+	0xf1: 0x3415, 0xf2: 0x3438, 0xf3: 0x343d, 0xf4: 0x47f0, 0xf5: 0x47f5,
+	0xf6: 0x4804, 0xf8: 0xa000, 0xf9: 0x34c9, 0xfa: 0x34ce, 0xfb: 0x34d3,
+	0xfc: 0x4836, 0xfd: 0x3550, 0xff: 0x3569,
+	// Block 0x4, offset 0x100
+	0x100: 0x2f7e, 0x101: 0x328a, 0x102: 0x4723, 0x103: 0x47b4, 0x104: 0x2f9c, 0x105: 0x32a8,
+	0x106: 0x2fb0, 0x107: 0x32bc, 0x108: 0x2fb5, 0x109: 0x32c1, 0x10a: 0x2fba, 0x10b: 0x32c6,
+	0x10c: 0x2fbf, 0x10d: 0x32cb, 0x10e: 0x2fc9, 0x10f: 0x32d5,
+	0x112: 0x4746, 0x113: 0x47d7, 0x114: 0x2ff1, 0x115: 0x32fd, 0x116: 0x2ff6, 0x117: 0x3302,
+	0x118: 0x3014, 0x119: 0x3320, 0x11a: 0x3005, 0x11b: 0x3311, 0x11c: 0x302d, 0x11d: 0x3339,
+	0x11e: 0x3037, 0x11f: 0x3343, 0x120: 0x303c, 0x121: 0x3348, 0x122: 0x3046, 0x123: 0x3352,
+	0x124: 0x304b, 0x125: 0x3357, 0x128: 0x307d, 0x129: 0x338e,
+	0x12a: 0x3082, 0x12b: 0x3393, 0x12c: 0x3087, 0x12d: 0x3398, 0x12e: 0x30aa, 0x12f: 0x33b6,
+	0x130: 0x308c, 0x134: 0x30b4, 0x135: 0x33c0,
+	0x136: 0x30c8, 0x137: 0x33d9, 0x139: 0x30d2, 0x13a: 0x33e3, 0x13b: 0x30dc,
+	0x13c: 0x33ed, 0x13d: 0x30d7, 0x13e: 0x33e8,
+	// Block 0x5, offset 0x140
+	0x143: 0x30ff, 0x144: 0x3410, 0x145: 0x3118,
+	0x146: 0x3429, 0x147: 0x310e, 0x148: 0x341f,
+	0x14c: 0x4769, 0x14d: 0x47fa, 0x14e: 0x3131, 0x14f: 0x3442, 0x150: 0x313b, 0x151: 0x344c,
+	0x154: 0x3159, 0x155: 0x346a, 0x156: 0x3172, 0x157: 0x3483,
+	0x158: 0x3163, 0x159: 0x3474, 0x15a: 0x478c, 0x15b: 0x481d, 0x15c: 0x317c, 0x15d: 0x348d,
+	0x15e: 0x318b, 0x15f: 0x349c, 0x160: 0x4791, 0x161: 0x4822, 0x162: 0x31a4, 0x163: 0x34ba,
+	0x164: 0x3195, 0x165: 0x34ab, 0x168: 0x479b, 0x169: 0x482c,
+	0x16a: 0x47a0, 0x16b: 0x4831, 0x16c: 0x31c2, 0x16d: 0x34d8, 0x16e: 0x31cc, 0x16f: 0x34e2,
+	0x170: 0x31d1, 0x171: 0x34e7, 0x172: 0x31ef, 0x173: 0x3505, 0x174: 0x3212, 0x175: 0x3528,
+	0x176: 0x323a, 0x177: 0x3555, 0x178: 0x324e, 0x179: 0x325d, 0x17a: 0x357d, 0x17b: 0x3267,
+	0x17c: 0x3587, 0x17d: 0x326c, 0x17e: 0x358c, 0x17f: 0xa000,
+	// Block 0x6, offset 0x180
+	0x184: 0x8100, 0x185: 0x8100,
+	0x186: 0x8100,
+	0x18d: 0x2f88, 0x18e: 0x3294, 0x18f: 0x3096, 0x190: 0x33a2, 0x191: 0x3140,
+	0x192: 0x3451, 0x193: 0x31d6, 0x194: 0x34ec, 0x195: 0x39cf, 0x196: 0x3b5e, 0x197: 0x39c8,
+	0x198: 0x3b57, 0x199: 0x39d6, 0x19a: 0x3b65, 0x19b: 0x39c1, 0x19c: 0x3b50,
+	0x19e: 0x38b0, 0x19f: 0x3a3f, 0x1a0: 0x38a9, 0x1a1: 0x3a38, 0x1a2: 0x35b3, 0x1a3: 0x35c5,
+	0x1a6: 0x3041, 0x1a7: 0x334d, 0x1a8: 0x30be, 0x1a9: 0x33cf,
+	0x1aa: 0x4782, 0x1ab: 0x4813, 0x1ac: 0x3990, 0x1ad: 0x3b1f, 0x1ae: 0x35d7, 0x1af: 0x35dd,
+	0x1b0: 0x33c5, 0x1b4: 0x3028, 0x1b5: 0x3334,
+	0x1b8: 0x30fa, 0x1b9: 0x340b, 0x1ba: 0x38b7, 0x1bb: 0x3a46,
+	0x1bc: 0x35ad, 0x1bd: 0x35bf, 0x1be: 0x35b9, 0x1bf: 0x35cb,
+	// Block 0x7, offset 0x1c0
+	0x1c0: 0x2f8d, 0x1c1: 0x3299, 0x1c2: 0x2f92, 0x1c3: 0x329e, 0x1c4: 0x300a, 0x1c5: 0x3316,
+	0x1c6: 0x300f, 0x1c7: 0x331b, 0x1c8: 0x309b, 0x1c9: 0x33a7, 0x1ca: 0x30a0, 0x1cb: 0x33ac,
+	0x1cc: 0x3145, 0x1cd: 0x3456, 0x1ce: 0x314a, 0x1cf: 0x345b, 0x1d0: 0x3168, 0x1d1: 0x3479,
+	0x1d2: 0x316d, 0x1d3: 0x347e, 0x1d4: 0x31db, 0x1d5: 0x34f1, 0x1d6: 0x31e0, 0x1d7: 0x34f6,
+	0x1d8: 0x3186, 0x1d9: 0x3497, 0x1da: 0x319f, 0x1db: 0x34b5,
+	0x1de: 0x305a, 0x1df: 0x3366,
+	0x1e6: 0x4728, 0x1e7: 0x47b9, 0x1e8: 0x4750, 0x1e9: 0x47e1,
+	0x1ea: 0x395f, 0x1eb: 0x3aee, 0x1ec: 0x393c, 0x1ed: 0x3acb, 0x1ee: 0x476e, 0x1ef: 0x47ff,
+	0x1f0: 0x3958, 0x1f1: 0x3ae7, 0x1f2: 0x3244, 0x1f3: 0x355f,
+	// Block 0x8, offset 0x200
+	0x200: 0x9932, 0x201: 0x9932, 0x202: 0x9932, 0x203: 0x9932, 0x204: 0x9932, 0x205: 0x8132,
+	0x206: 0x9932, 0x207: 0x9932, 0x208: 0x9932, 0x209: 0x9932, 0x20a: 0x9932, 0x20b: 0x9932,
+	0x20c: 0x9932, 0x20d: 0x8132, 0x20e: 0x8132, 0x20f: 0x9932, 0x210: 0x8132, 0x211: 0x9932,
+	0x212: 0x8132, 0x213: 0x9932, 0x214: 0x9932, 0x215: 0x8133, 0x216: 0x812d, 0x217: 0x812d,
+	0x218: 0x812d, 0x219: 0x812d, 0x21a: 0x8133, 0x21b: 0x992b, 0x21c: 0x812d, 0x21d: 0x812d,
+	0x21e: 0x812d, 0x21f: 0x812d, 0x220: 0x812d, 0x221: 0x8129, 0x222: 0x8129, 0x223: 0x992d,
+	0x224: 0x992d, 0x225: 0x992d, 0x226: 0x992d, 0x227: 0x9929, 0x228: 0x9929, 0x229: 0x812d,
+	0x22a: 0x812d, 0x22b: 0x812d, 0x22c: 0x812d, 0x22d: 0x992d, 0x22e: 0x992d, 0x22f: 0x812d,
+	0x230: 0x992d, 0x231: 0x992d, 0x232: 0x812d, 0x233: 0x812d, 0x234: 0x8101, 0x235: 0x8101,
+	0x236: 0x8101, 0x237: 0x8101, 0x238: 0x9901, 0x239: 0x812d, 0x23a: 0x812d, 0x23b: 0x812d,
+	0x23c: 0x812d, 0x23d: 0x8132, 0x23e: 0x8132, 0x23f: 0x8132,
+	// Block 0x9, offset 0x240
+	0x240: 0x4a44, 0x241: 0x4a49, 0x242: 0x9932, 0x243: 0x4a4e, 0x244: 0x4a53, 0x245: 0x9936,
+	0x246: 0x8132, 0x247: 0x812d, 0x248: 0x812d, 0x249: 0x812d, 0x24a: 0x8132, 0x24b: 0x8132,
+	0x24c: 0x8132, 0x24d: 0x812d, 0x24e: 0x812d, 0x250: 0x8132, 0x251: 0x8132,
+	0x252: 0x8132, 0x253: 0x812d, 0x254: 0x812d, 0x255: 0x812d, 0x256: 0x812d, 0x257: 0x8132,
+	0x258: 0x8133, 0x259: 0x812d, 0x25a: 0x812d, 0x25b: 0x8132, 0x25c: 0x8134, 0x25d: 0x8135,
+	0x25e: 0x8135, 0x25f: 0x8134, 0x260: 0x8135, 0x261: 0x8135, 0x262: 0x8134, 0x263: 0x8132,
+	0x264: 0x8132, 0x265: 0x8132, 0x266: 0x8132, 0x267: 0x8132, 0x268: 0x8132, 0x269: 0x8132,
+	0x26a: 0x8132, 0x26b: 0x8132, 0x26c: 0x8132, 0x26d: 0x8132, 0x26e: 0x8132, 0x26f: 0x8132,
+	0x274: 0x0170,
+	0x27a: 0x8100,
+	0x27e: 0x0037,
+	// Block 0xa, offset 0x280
+	0x284: 0x8100, 0x285: 0x35a1,
+	0x286: 0x35e9, 0x287: 0x00ce, 0x288: 0x3607, 0x289: 0x3613, 0x28a: 0x3625,
+	0x28c: 0x3643, 0x28e: 0x3655, 0x28f: 0x3673, 0x290: 0x3e08, 0x291: 0xa000,
+	0x295: 0xa000, 0x297: 0xa000,
+	0x299: 0xa000,
+	0x29f: 0xa000, 0x2a1: 0xa000,
+	0x2a5: 0xa000, 0x2a9: 0xa000,
+	0x2aa: 0x3637, 0x2ab: 0x3667, 0x2ac: 0x4894, 0x2ad: 0x3697, 0x2ae: 0x48be, 0x2af: 0x36a9,
+	0x2b0: 0x3e70, 0x2b1: 0xa000, 0x2b5: 0xa000,
+	0x2b7: 0xa000, 0x2b9: 0xa000,
+	0x2bf: 0xa000,
+	// Block 0xb, offset 0x2c0
+	0x2c0: 0x3721, 0x2c1: 0x372d, 0x2c3: 0x371b,
+	0x2c6: 0xa000, 0x2c7: 0x3709,
+	0x2cc: 0x375d, 0x2cd: 0x3745, 0x2ce: 0x376f, 0x2d0: 0xa000,
+	0x2d3: 0xa000, 0x2d5: 0xa000, 0x2d6: 0xa000, 0x2d7: 0xa000,
+	0x2d8: 0xa000, 0x2d9: 0x3751, 0x2da: 0xa000,
+	0x2de: 0xa000, 0x2e3: 0xa000,
+	0x2e7: 0xa000,
+	0x2eb: 0xa000, 0x2ed: 0xa000,
+	0x2f0: 0xa000, 0x2f3: 0xa000, 0x2f5: 0xa000,
+	0x2f6: 0xa000, 0x2f7: 0xa000, 0x2f8: 0xa000, 0x2f9: 0x37d5, 0x2fa: 0xa000,
+	0x2fe: 0xa000,
+	// Block 0xc, offset 0x300
+	0x301: 0x3733, 0x302: 0x37b7,
+	0x310: 0x370f, 0x311: 0x3793,
+	0x312: 0x3715, 0x313: 0x3799, 0x316: 0x3727, 0x317: 0x37ab,
+	0x318: 0xa000, 0x319: 0xa000, 0x31a: 0x3829, 0x31b: 0x382f, 0x31c: 0x3739, 0x31d: 0x37bd,
+	0x31e: 0x373f, 0x31f: 0x37c3, 0x322: 0x374b, 0x323: 0x37cf,
+	0x324: 0x3757, 0x325: 0x37db, 0x326: 0x3763, 0x327: 0x37e7, 0x328: 0xa000, 0x329: 0xa000,
+	0x32a: 0x3835, 0x32b: 0x383b, 0x32c: 0x378d, 0x32d: 0x3811, 0x32e: 0x3769, 0x32f: 0x37ed,
+	0x330: 0x3775, 0x331: 0x37f9, 0x332: 0x377b, 0x333: 0x37ff, 0x334: 0x3781, 0x335: 0x3805,
+	0x338: 0x3787, 0x339: 0x380b,
+	// Block 0xd, offset 0x340
+	0x351: 0x812d,
+	0x352: 0x8132, 0x353: 0x8132, 0x354: 0x8132, 0x355: 0x8132, 0x356: 0x812d, 0x357: 0x8132,
+	0x358: 0x8132, 0x359: 0x8132, 0x35a: 0x812e, 0x35b: 0x812d, 0x35c: 0x8132, 0x35d: 0x8132,
+	0x35e: 0x8132, 0x35f: 0x8132, 0x360: 0x8132, 0x361: 0x8132, 0x362: 0x812d, 0x363: 0x812d,
+	0x364: 0x812d, 0x365: 0x812d, 0x366: 0x812d, 0x367: 0x812d, 0x368: 0x8132, 0x369: 0x8132,
+	0x36a: 0x812d, 0x36b: 0x8132, 0x36c: 0x8132, 0x36d: 0x812e, 0x36e: 0x8131, 0x36f: 0x8132,
+	0x370: 0x8105, 0x371: 0x8106, 0x372: 0x8107, 0x373: 0x8108, 0x374: 0x8109, 0x375: 0x810a,
+	0x376: 0x810b, 0x377: 0x810c, 0x378: 0x810d, 0x379: 0x810e, 0x37a: 0x810e, 0x37b: 0x810f,
+	0x37c: 0x8110, 0x37d: 0x8111, 0x37f: 0x8112,
+	// Block 0xe, offset 0x380
+	0x388: 0xa000, 0x38a: 0xa000, 0x38b: 0x8116,
+	0x38c: 0x8117, 0x38d: 0x8118, 0x38e: 0x8119, 0x38f: 0x811a, 0x390: 0x811b, 0x391: 0x811c,
+	0x392: 0x811d, 0x393: 0x9932, 0x394: 0x9932, 0x395: 0x992d, 0x396: 0x812d, 0x397: 0x8132,
+	0x398: 0x8132, 0x399: 0x8132, 0x39a: 0x8132, 0x39b: 0x8132, 0x39c: 0x812d, 0x39d: 0x8132,
+	0x39e: 0x8132, 0x39f: 0x812d,
+	0x3b0: 0x811e,
+	// Block 0xf, offset 0x3c0
+	0x3c5: 0xa000,
+	0x3c6: 0x2d26, 0x3c7: 0xa000, 0x3c8: 0x2d2e, 0x3c9: 0xa000, 0x3ca: 0x2d36, 0x3cb: 0xa000,
+	0x3cc: 0x2d3e, 0x3cd: 0xa000, 0x3ce: 0x2d46, 0x3d1: 0xa000,
+	0x3d2: 0x2d4e,
+	0x3f4: 0x8102, 0x3f5: 0x9900,
+	0x3fa: 0xa000, 0x3fb: 0x2d56,
+	0x3fc: 0xa000, 0x3fd: 0x2d5e, 0x3fe: 0xa000, 0x3ff: 0xa000,
+	// Block 0x10, offset 0x400
+	0x400: 0x2f97, 0x401: 0x32a3, 0x402: 0x2fa1, 0x403: 0x32ad, 0x404: 0x2fa6, 0x405: 0x32b2,
+	0x406: 0x2fab, 0x407: 0x32b7, 0x408: 0x38cc, 0x409: 0x3a5b, 0x40a: 0x2fc4, 0x40b: 0x32d0,
+	0x40c: 0x2fce, 0x40d: 0x32da, 0x40e: 0x2fdd, 0x40f: 0x32e9, 0x410: 0x2fd3, 0x411: 0x32df,
+	0x412: 0x2fd8, 0x413: 0x32e4, 0x414: 0x38ef, 0x415: 0x3a7e, 0x416: 0x38f6, 0x417: 0x3a85,
+	0x418: 0x3019, 0x419: 0x3325, 0x41a: 0x301e, 0x41b: 0x332a, 0x41c: 0x3904, 0x41d: 0x3a93,
+	0x41e: 0x3023, 0x41f: 0x332f, 0x420: 0x3032, 0x421: 0x333e, 0x422: 0x3050, 0x423: 0x335c,
+	0x424: 0x305f, 0x425: 0x336b, 0x426: 0x3055, 0x427: 0x3361, 0x428: 0x3064, 0x429: 0x3370,
+	0x42a: 0x3069, 0x42b: 0x3375, 0x42c: 0x30af, 0x42d: 0x33bb, 0x42e: 0x390b, 0x42f: 0x3a9a,
+	0x430: 0x30b9, 0x431: 0x33ca, 0x432: 0x30c3, 0x433: 0x33d4, 0x434: 0x30cd, 0x435: 0x33de,
+	0x436: 0x475a, 0x437: 0x47eb, 0x438: 0x3912, 0x439: 0x3aa1, 0x43a: 0x30e6, 0x43b: 0x33f7,
+	0x43c: 0x30e1, 0x43d: 0x33f2, 0x43e: 0x30eb, 0x43f: 0x33fc,
+	// Block 0x11, offset 0x440
+	0x440: 0x30f0, 0x441: 0x3401, 0x442: 0x30f5, 0x443: 0x3406, 0x444: 0x3109, 0x445: 0x341a,
+	0x446: 0x3113, 0x447: 0x3424, 0x448: 0x3122, 0x449: 0x3433, 0x44a: 0x311d, 0x44b: 0x342e,
+	0x44c: 0x3935, 0x44d: 0x3ac4, 0x44e: 0x3943, 0x44f: 0x3ad2, 0x450: 0x394a, 0x451: 0x3ad9,
+	0x452: 0x3951, 0x453: 0x3ae0, 0x454: 0x314f, 0x455: 0x3460, 0x456: 0x3154, 0x457: 0x3465,
+	0x458: 0x315e, 0x459: 0x346f, 0x45a: 0x4787, 0x45b: 0x4818, 0x45c: 0x3997, 0x45d: 0x3b26,
+	0x45e: 0x3177, 0x45f: 0x3488, 0x460: 0x3181, 0x461: 0x3492, 0x462: 0x4796, 0x463: 0x4827,
+	0x464: 0x399e, 0x465: 0x3b2d, 0x466: 0x39a5, 0x467: 0x3b34, 0x468: 0x39ac, 0x469: 0x3b3b,
+	0x46a: 0x3190, 0x46b: 0x34a1, 0x46c: 0x319a, 0x46d: 0x34b0, 0x46e: 0x31ae, 0x46f: 0x34c4,
+	0x470: 0x31a9, 0x471: 0x34bf, 0x472: 0x31ea, 0x473: 0x3500, 0x474: 0x31f9, 0x475: 0x350f,
+	0x476: 0x31f4, 0x477: 0x350a, 0x478: 0x39b3, 0x479: 0x3b42, 0x47a: 0x39ba, 0x47b: 0x3b49,
+	0x47c: 0x31fe, 0x47d: 0x3514, 0x47e: 0x3203, 0x47f: 0x3519,
+	// Block 0x12, offset 0x480
+	0x480: 0x3208, 0x481: 0x351e, 0x482: 0x320d, 0x483: 0x3523, 0x484: 0x321c, 0x485: 0x3532,
+	0x486: 0x3217, 0x487: 0x352d, 0x488: 0x3221, 0x489: 0x353c, 0x48a: 0x3226, 0x48b: 0x3541,
+	0x48c: 0x322b, 0x48d: 0x3546, 0x48e: 0x3249, 0x48f: 0x3564, 0x490: 0x3262, 0x491: 0x3582,
+	0x492: 0x3271, 0x493: 0x3591, 0x494: 0x3276, 0x495: 0x3596, 0x496: 0x337a, 0x497: 0x34a6,
+	0x498: 0x3537, 0x499: 0x3573, 0x49b: 0x35d1,
+	0x4a0: 0x4737, 0x4a1: 0x47c8, 0x4a2: 0x2f83, 0x4a3: 0x328f,
+	0x4a4: 0x3878, 0x4a5: 0x3a07, 0x4a6: 0x3871, 0x4a7: 0x3a00, 0x4a8: 0x3886, 0x4a9: 0x3a15,
+	0x4aa: 0x387f, 0x4ab: 0x3a0e, 0x4ac: 0x38be, 0x4ad: 0x3a4d, 0x4ae: 0x3894, 0x4af: 0x3a23,
+	0x4b0: 0x388d, 0x4b1: 0x3a1c, 0x4b2: 0x38a2, 0x4b3: 0x3a31, 0x4b4: 0x389b, 0x4b5: 0x3a2a,
+	0x4b6: 0x38c5, 0x4b7: 0x3a54, 0x4b8: 0x474b, 0x4b9: 0x47dc, 0x4ba: 0x3000, 0x4bb: 0x330c,
+	0x4bc: 0x2fec, 0x4bd: 0x32f8, 0x4be: 0x38da, 0x4bf: 0x3a69,
+	// Block 0x13, offset 0x4c0
+	0x4c0: 0x38d3, 0x4c1: 0x3a62, 0x4c2: 0x38e8, 0x4c3: 0x3a77, 0x4c4: 0x38e1, 0x4c5: 0x3a70,
+	0x4c6: 0x38fd, 0x4c7: 0x3a8c, 0x4c8: 0x3091, 0x4c9: 0x339d, 0x4ca: 0x30a5, 0x4cb: 0x33b1,
+	0x4cc: 0x477d, 0x4cd: 0x480e, 0x4ce: 0x3136, 0x4cf: 0x3447, 0x4d0: 0x3920, 0x4d1: 0x3aaf,
+	0x4d2: 0x3919, 0x4d3: 0x3aa8, 0x4d4: 0x392e, 0x4d5: 0x3abd, 0x4d6: 0x3927, 0x4d7: 0x3ab6,
+	0x4d8: 0x3989, 0x4d9: 0x3b18, 0x4da: 0x396d, 0x4db: 0x3afc, 0x4dc: 0x3966, 0x4dd: 0x3af5,
+	0x4de: 0x397b, 0x4df: 0x3b0a, 0x4e0: 0x3974, 0x4e1: 0x3b03, 0x4e2: 0x3982, 0x4e3: 0x3b11,
+	0x4e4: 0x31e5, 0x4e5: 0x34fb, 0x4e6: 0x31c7, 0x4e7: 0x34dd, 0x4e8: 0x39e4, 0x4e9: 0x3b73,
+	0x4ea: 0x39dd, 0x4eb: 0x3b6c, 0x4ec: 0x39f2, 0x4ed: 0x3b81, 0x4ee: 0x39eb, 0x4ef: 0x3b7a,
+	0x4f0: 0x39f9, 0x4f1: 0x3b88, 0x4f2: 0x3230, 0x4f3: 0x354b, 0x4f4: 0x3258, 0x4f5: 0x3578,
+	0x4f6: 0x3253, 0x4f7: 0x356e, 0x4f8: 0x323f, 0x4f9: 0x355a,
+	// Block 0x14, offset 0x500
+	0x500: 0x489a, 0x501: 0x48a0, 0x502: 0x49b4, 0x503: 0x49cc, 0x504: 0x49bc, 0x505: 0x49d4,
+	0x506: 0x49c4, 0x507: 0x49dc, 0x508: 0x4840, 0x509: 0x4846, 0x50a: 0x4924, 0x50b: 0x493c,
+	0x50c: 0x492c, 0x50d: 0x4944, 0x50e: 0x4934, 0x50f: 0x494c, 0x510: 0x48ac, 0x511: 0x48b2,
+	0x512: 0x3db8, 0x513: 0x3dc8, 0x514: 0x3dc0, 0x515: 0x3dd0,
+	0x518: 0x484c, 0x519: 0x4852, 0x51a: 0x3ce8, 0x51b: 0x3cf8, 0x51c: 0x3cf0, 0x51d: 0x3d00,
+	0x520: 0x48c4, 0x521: 0x48ca, 0x522: 0x49e4, 0x523: 0x49fc,
+	0x524: 0x49ec, 0x525: 0x4a04, 0x526: 0x49f4, 0x527: 0x4a0c, 0x528: 0x4858, 0x529: 0x485e,
+	0x52a: 0x4954, 0x52b: 0x496c, 0x52c: 0x495c, 0x52d: 0x4974, 0x52e: 0x4964, 0x52f: 0x497c,
+	0x530: 0x48dc, 0x531: 0x48e2, 0x532: 0x3e18, 0x533: 0x3e30, 0x534: 0x3e20, 0x535: 0x3e38,
+	0x536: 0x3e28, 0x537: 0x3e40, 0x538: 0x4864, 0x539: 0x486a, 0x53a: 0x3d18, 0x53b: 0x3d30,
+	0x53c: 0x3d20, 0x53d: 0x3d38, 0x53e: 0x3d28, 0x53f: 0x3d40,
+	// Block 0x15, offset 0x540
+	0x540: 0x48e8, 0x541: 0x48ee, 0x542: 0x3e48, 0x543: 0x3e58, 0x544: 0x3e50, 0x545: 0x3e60,
+	0x548: 0x4870, 0x549: 0x4876, 0x54a: 0x3d48, 0x54b: 0x3d58,
+	0x54c: 0x3d50, 0x54d: 0x3d60, 0x550: 0x48fa, 0x551: 0x4900,
+	0x552: 0x3e80, 0x553: 0x3e98, 0x554: 0x3e88, 0x555: 0x3ea0, 0x556: 0x3e90, 0x557: 0x3ea8,
+	0x559: 0x487c, 0x55b: 0x3d68, 0x55d: 0x3d70,
+	0x55f: 0x3d78, 0x560: 0x4912, 0x561: 0x4918, 0x562: 0x4a14, 0x563: 0x4a2c,
+	0x564: 0x4a1c, 0x565: 0x4a34, 0x566: 0x4a24, 0x567: 0x4a3c, 0x568: 0x4882, 0x569: 0x4888,
+	0x56a: 0x4984, 0x56b: 0x499c, 0x56c: 0x498c, 0x56d: 0x49a4, 0x56e: 0x4994, 0x56f: 0x49ac,
+	0x570: 0x488e, 0x571: 0x43b4, 0x572: 0x3691, 0x573: 0x43ba, 0x574: 0x48b8, 0x575: 0x43c0,
+	0x576: 0x36a3, 0x577: 0x43c6, 0x578: 0x36c1, 0x579: 0x43cc, 0x57a: 0x36d9, 0x57b: 0x43d2,
+	0x57c: 0x4906, 0x57d: 0x43d8,
+	// Block 0x16, offset 0x580
+	0x580: 0x3da0, 0x581: 0x3da8, 0x582: 0x4184, 0x583: 0x41a2, 0x584: 0x418e, 0x585: 0x41ac,
+	0x586: 0x4198, 0x587: 0x41b6, 0x588: 0x3cd8, 0x589: 0x3ce0, 0x58a: 0x40d0, 0x58b: 0x40ee,
+	0x58c: 0x40da, 0x58d: 0x40f8, 0x58e: 0x40e4, 0x58f: 0x4102, 0x590: 0x3de8, 0x591: 0x3df0,
+	0x592: 0x41c0, 0x593: 0x41de, 0x594: 0x41ca, 0x595: 0x41e8, 0x596: 0x41d4, 0x597: 0x41f2,
+	0x598: 0x3d08, 0x599: 0x3d10, 0x59a: 0x410c, 0x59b: 0x412a, 0x59c: 0x4116, 0x59d: 0x4134,
+	0x59e: 0x4120, 0x59f: 0x413e, 0x5a0: 0x3ec0, 0x5a1: 0x3ec8, 0x5a2: 0x41fc, 0x5a3: 0x421a,
+	0x5a4: 0x4206, 0x5a5: 0x4224, 0x5a6: 0x4210, 0x5a7: 0x422e, 0x5a8: 0x3d80, 0x5a9: 0x3d88,
+	0x5aa: 0x4148, 0x5ab: 0x4166, 0x5ac: 0x4152, 0x5ad: 0x4170, 0x5ae: 0x415c, 0x5af: 0x417a,
+	0x5b0: 0x3685, 0x5b1: 0x367f, 0x5b2: 0x3d90, 0x5b3: 0x368b, 0x5b4: 0x3d98,
+	0x5b6: 0x48a6, 0x5b7: 0x3db0, 0x5b8: 0x35f5, 0x5b9: 0x35ef, 0x5ba: 0x35e3, 0x5bb: 0x4384,
+	0x5bc: 0x35fb, 0x5bd: 0x8100, 0x5be: 0x01d3, 0x5bf: 0xa100,
+	// Block 0x17, offset 0x5c0
+	0x5c0: 0x8100, 0x5c1: 0x35a7, 0x5c2: 0x3dd8, 0x5c3: 0x369d, 0x5c4: 0x3de0,
+	0x5c6: 0x48d0, 0x5c7: 0x3df8, 0x5c8: 0x3601, 0x5c9: 0x438a, 0x5ca: 0x360d, 0x5cb: 0x4390,
+	0x5cc: 0x3619, 0x5cd: 0x3b8f, 0x5ce: 0x3b96, 0x5cf: 0x3b9d, 0x5d0: 0x36b5, 0x5d1: 0x36af,
+	0x5d2: 0x3e00, 0x5d3: 0x457a, 0x5d6: 0x36bb, 0x5d7: 0x3e10,
+	0x5d8: 0x3631, 0x5d9: 0x362b, 0x5da: 0x361f, 0x5db: 0x4396, 0x5dd: 0x3ba4,
+	0x5de: 0x3bab, 0x5df: 0x3bb2, 0x5e0: 0x36eb, 0x5e1: 0x36e5, 0x5e2: 0x3e68, 0x5e3: 0x4582,
+	0x5e4: 0x36cd, 0x5e5: 0x36d3, 0x5e6: 0x36f1, 0x5e7: 0x3e78, 0x5e8: 0x3661, 0x5e9: 0x365b,
+	0x5ea: 0x364f, 0x5eb: 0x43a2, 0x5ec: 0x3649, 0x5ed: 0x359b, 0x5ee: 0x437e, 0x5ef: 0x0081,
+	0x5f2: 0x3eb0, 0x5f3: 0x36f7, 0x5f4: 0x3eb8,
+	0x5f6: 0x491e, 0x5f7: 0x3ed0, 0x5f8: 0x363d, 0x5f9: 0x439c, 0x5fa: 0x366d, 0x5fb: 0x43ae,
+	0x5fc: 0x3679, 0x5fd: 0x4256, 0x5fe: 0xa100,
+	// Block 0x18, offset 0x600
+	0x601: 0x3c06, 0x603: 0xa000, 0x604: 0x3c0d, 0x605: 0xa000,
+	0x607: 0x3c14, 0x608: 0xa000, 0x609: 0x3c1b,
+	0x60d: 0xa000,
+	0x620: 0x2f65, 0x621: 0xa000, 0x622: 0x3c29,
+	0x624: 0xa000, 0x625: 0xa000,
+	0x62d: 0x3c22, 0x62e: 0x2f60, 0x62f: 0x2f6a,
+	0x630: 0x3c30, 0x631: 0x3c37, 0x632: 0xa000, 0x633: 0xa000, 0x634: 0x3c3e, 0x635: 0x3c45,
+	0x636: 0xa000, 0x637: 0xa000, 0x638: 0x3c4c, 0x639: 0x3c53, 0x63a: 0xa000, 0x63b: 0xa000,
+	0x63c: 0xa000, 0x63d: 0xa000,
+	// Block 0x19, offset 0x640
+	0x640: 0x3c5a, 0x641: 0x3c61, 0x642: 0xa000, 0x643: 0xa000, 0x644: 0x3c76, 0x645: 0x3c7d,
+	0x646: 0xa000, 0x647: 0xa000, 0x648: 0x3c84, 0x649: 0x3c8b,
+	0x651: 0xa000,
+	0x652: 0xa000,
+	0x662: 0xa000,
+	0x668: 0xa000, 0x669: 0xa000,
+	0x66b: 0xa000, 0x66c: 0x3ca0, 0x66d: 0x3ca7, 0x66e: 0x3cae, 0x66f: 0x3cb5,
+	0x672: 0xa000, 0x673: 0xa000, 0x674: 0xa000, 0x675: 0xa000,
+	// Block 0x1a, offset 0x680
+	0x686: 0xa000, 0x68b: 0xa000,
+	0x68c: 0x3f08, 0x68d: 0xa000, 0x68e: 0x3f10, 0x68f: 0xa000, 0x690: 0x3f18, 0x691: 0xa000,
+	0x692: 0x3f20, 0x693: 0xa000, 0x694: 0x3f28, 0x695: 0xa000, 0x696: 0x3f30, 0x697: 0xa000,
+	0x698: 0x3f38, 0x699: 0xa000, 0x69a: 0x3f40, 0x69b: 0xa000, 0x69c: 0x3f48, 0x69d: 0xa000,
+	0x69e: 0x3f50, 0x69f: 0xa000, 0x6a0: 0x3f58, 0x6a1: 0xa000, 0x6a2: 0x3f60,
+	0x6a4: 0xa000, 0x6a5: 0x3f68, 0x6a6: 0xa000, 0x6a7: 0x3f70, 0x6a8: 0xa000, 0x6a9: 0x3f78,
+	0x6af: 0xa000,
+	0x6b0: 0x3f80, 0x6b1: 0x3f88, 0x6b2: 0xa000, 0x6b3: 0x3f90, 0x6b4: 0x3f98, 0x6b5: 0xa000,
+	0x6b6: 0x3fa0, 0x6b7: 0x3fa8, 0x6b8: 0xa000, 0x6b9: 0x3fb0, 0x6ba: 0x3fb8, 0x6bb: 0xa000,
+	0x6bc: 0x3fc0, 0x6bd: 0x3fc8,
+	// Block 0x1b, offset 0x6c0
+	0x6d4: 0x3f00,
+	0x6d9: 0x9903, 0x6da: 0x9903, 0x6db: 0x8100, 0x6dc: 0x8100, 0x6dd: 0xa000,
+	0x6de: 0x3fd0,
+	0x6e6: 0xa000,
+	0x6eb: 0xa000, 0x6ec: 0x3fe0, 0x6ed: 0xa000, 0x6ee: 0x3fe8, 0x6ef: 0xa000,
+	0x6f0: 0x3ff0, 0x6f1: 0xa000, 0x6f2: 0x3ff8, 0x6f3: 0xa000, 0x6f4: 0x4000, 0x6f5: 0xa000,
+	0x6f6: 0x4008, 0x6f7: 0xa000, 0x6f8: 0x4010, 0x6f9: 0xa000, 0x6fa: 0x4018, 0x6fb: 0xa000,
+	0x6fc: 0x4020, 0x6fd: 0xa000, 0x6fe: 0x4028, 0x6ff: 0xa000,
+	// Block 0x1c, offset 0x700
+	0x700: 0x4030, 0x701: 0xa000, 0x702: 0x4038, 0x704: 0xa000, 0x705: 0x4040,
+	0x706: 0xa000, 0x707: 0x4048, 0x708: 0xa000, 0x709: 0x4050,
+	0x70f: 0xa000, 0x710: 0x4058, 0x711: 0x4060,
+	0x712: 0xa000, 0x713: 0x4068, 0x714: 0x4070, 0x715: 0xa000, 0x716: 0x4078, 0x717: 0x4080,
+	0x718: 0xa000, 0x719: 0x4088, 0x71a: 0x4090, 0x71b: 0xa000, 0x71c: 0x4098, 0x71d: 0x40a0,
+	0x72f: 0xa000,
+	0x730: 0xa000, 0x731: 0xa000, 0x732: 0xa000, 0x734: 0x3fd8,
+	0x737: 0x40a8, 0x738: 0x40b0, 0x739: 0x40b8, 0x73a: 0x40c0,
+	0x73d: 0xa000, 0x73e: 0x40c8,
+	// Block 0x1d, offset 0x740
+	0x740: 0x1377, 0x741: 0x0cfb, 0x742: 0x13d3, 0x743: 0x139f, 0x744: 0x0e57, 0x745: 0x06eb,
+	0x746: 0x08df, 0x747: 0x162b, 0x748: 0x162b, 0x749: 0x0a0b, 0x74a: 0x145f, 0x74b: 0x0943,
+	0x74c: 0x0a07, 0x74d: 0x0bef, 0x74e: 0x0fcf, 0x74f: 0x115f, 0x750: 0x1297, 0x751: 0x12d3,
+	0x752: 0x1307, 0x753: 0x141b, 0x754: 0x0d73, 0x755: 0x0dff, 0x756: 0x0eab, 0x757: 0x0f43,
+	0x758: 0x125f, 0x759: 0x1447, 0x75a: 0x1573, 0x75b: 0x070f, 0x75c: 0x08b3, 0x75d: 0x0d87,
+	0x75e: 0x0ecf, 0x75f: 0x1293, 0x760: 0x15c3, 0x761: 0x0ab3, 0x762: 0x0e77, 0x763: 0x1283,
+	0x764: 0x1317, 0x765: 0x0c23, 0x766: 0x11bb, 0x767: 0x12df, 0x768: 0x0b1f, 0x769: 0x0d0f,
+	0x76a: 0x0e17, 0x76b: 0x0f1b, 0x76c: 0x1427, 0x76d: 0x074f, 0x76e: 0x07e7, 0x76f: 0x0853,
+	0x770: 0x0c8b, 0x771: 0x0d7f, 0x772: 0x0ecb, 0x773: 0x0fef, 0x774: 0x1177, 0x775: 0x128b,
+	0x776: 0x12a3, 0x777: 0x13c7, 0x778: 0x14ef, 0x779: 0x15a3, 0x77a: 0x15bf, 0x77b: 0x102b,
+	0x77c: 0x106b, 0x77d: 0x1123, 0x77e: 0x1243, 0x77f: 0x147b,
+	// Block 0x1e, offset 0x780
+	0x780: 0x15cb, 0x781: 0x134b, 0x782: 0x09c7, 0x783: 0x0b3b, 0x784: 0x10db, 0x785: 0x119b,
+	0x786: 0x0eff, 0x787: 0x1033, 0x788: 0x1397, 0x789: 0x14e7, 0x78a: 0x09c3, 0x78b: 0x0a8f,
+	0x78c: 0x0d77, 0x78d: 0x0e2b, 0x78e: 0x0e5f, 0x78f: 0x1113, 0x790: 0x113b, 0x791: 0x14a7,
+	0x792: 0x084f, 0x793: 0x11a7, 0x794: 0x07f3, 0x795: 0x07ef, 0x796: 0x1097, 0x797: 0x1127,
+	0x798: 0x125b, 0x799: 0x14af, 0x79a: 0x1367, 0x79b: 0x0c27, 0x79c: 0x0d73, 0x79d: 0x1357,
+	0x79e: 0x06f7, 0x79f: 0x0a63, 0x7a0: 0x0b93, 0x7a1: 0x0f2f, 0x7a2: 0x0faf, 0x7a3: 0x0873,
+	0x7a4: 0x103b, 0x7a5: 0x075f, 0x7a6: 0x0b77, 0x7a7: 0x06d7, 0x7a8: 0x0deb, 0x7a9: 0x0ca3,
+	0x7aa: 0x110f, 0x7ab: 0x08c7, 0x7ac: 0x09b3, 0x7ad: 0x0ffb, 0x7ae: 0x1263, 0x7af: 0x133b,
+	0x7b0: 0x0db7, 0x7b1: 0x13f7, 0x7b2: 0x0de3, 0x7b3: 0x0c37, 0x7b4: 0x121b, 0x7b5: 0x0c57,
+	0x7b6: 0x0fab, 0x7b7: 0x072b, 0x7b8: 0x07a7, 0x7b9: 0x07eb, 0x7ba: 0x0d53, 0x7bb: 0x10fb,
+	0x7bc: 0x11f3, 0x7bd: 0x1347, 0x7be: 0x145b, 0x7bf: 0x085b,
+	// Block 0x1f, offset 0x7c0
+	0x7c0: 0x090f, 0x7c1: 0x0a17, 0x7c2: 0x0b2f, 0x7c3: 0x0cbf, 0x7c4: 0x0e7b, 0x7c5: 0x103f,
+	0x7c6: 0x1497, 0x7c7: 0x157b, 0x7c8: 0x15cf, 0x7c9: 0x15e7, 0x7ca: 0x0837, 0x7cb: 0x0cf3,
+	0x7cc: 0x0da3, 0x7cd: 0x13eb, 0x7ce: 0x0afb, 0x7cf: 0x0bd7, 0x7d0: 0x0bf3, 0x7d1: 0x0c83,
+	0x7d2: 0x0e6b, 0x7d3: 0x0eb7, 0x7d4: 0x0f67, 0x7d5: 0x108b, 0x7d6: 0x112f, 0x7d7: 0x1193,
+	0x7d8: 0x13db, 0x7d9: 0x126b, 0x7da: 0x1403, 0x7db: 0x147f, 0x7dc: 0x080f, 0x7dd: 0x083b,
+	0x7de: 0x0923, 0x7df: 0x0ea7, 0x7e0: 0x12f3, 0x7e1: 0x133b, 0x7e2: 0x0b1b, 0x7e3: 0x0b8b,
+	0x7e4: 0x0c4f, 0x7e5: 0x0daf, 0x7e6: 0x10d7, 0x7e7: 0x0f23, 0x7e8: 0x073b, 0x7e9: 0x097f,
+	0x7ea: 0x0a63, 0x7eb: 0x0ac7, 0x7ec: 0x0b97, 0x7ed: 0x0f3f, 0x7ee: 0x0f5b, 0x7ef: 0x116b,
+	0x7f0: 0x118b, 0x7f1: 0x1463, 0x7f2: 0x14e3, 0x7f3: 0x14f3, 0x7f4: 0x152f, 0x7f5: 0x0753,
+	0x7f6: 0x107f, 0x7f7: 0x144f, 0x7f8: 0x14cb, 0x7f9: 0x0baf, 0x7fa: 0x0717, 0x7fb: 0x0777,
+	0x7fc: 0x0a67, 0x7fd: 0x0a87, 0x7fe: 0x0caf, 0x7ff: 0x0d73,
+	// Block 0x20, offset 0x800
+	0x800: 0x0ec3, 0x801: 0x0fcb, 0x802: 0x1277, 0x803: 0x1417, 0x804: 0x1623, 0x805: 0x0ce3,
+	0x806: 0x14a3, 0x807: 0x0833, 0x808: 0x0d2f, 0x809: 0x0d3b, 0x80a: 0x0e0f, 0x80b: 0x0e47,
+	0x80c: 0x0f4b, 0x80d: 0x0fa7, 0x80e: 0x1027, 0x80f: 0x110b, 0x810: 0x153b, 0x811: 0x07af,
+	0x812: 0x0c03, 0x813: 0x14b3, 0x814: 0x0767, 0x815: 0x0aab, 0x816: 0x0e2f, 0x817: 0x13df,
+	0x818: 0x0b67, 0x819: 0x0bb7, 0x81a: 0x0d43, 0x81b: 0x0f2f, 0x81c: 0x14bb, 0x81d: 0x0817,
+	0x81e: 0x08ff, 0x81f: 0x0a97, 0x820: 0x0cd3, 0x821: 0x0d1f, 0x822: 0x0d5f, 0x823: 0x0df3,
+	0x824: 0x0f47, 0x825: 0x0fbb, 0x826: 0x1157, 0x827: 0x12f7, 0x828: 0x1303, 0x829: 0x1457,
+	0x82a: 0x14d7, 0x82b: 0x0883, 0x82c: 0x0e4b, 0x82d: 0x0903, 0x82e: 0x0ec7, 0x82f: 0x0f6b,
+	0x830: 0x1287, 0x831: 0x14bf, 0x832: 0x15ab, 0x833: 0x15d3, 0x834: 0x0d37, 0x835: 0x0e27,
+	0x836: 0x11c3, 0x837: 0x10b7, 0x838: 0x10c3, 0x839: 0x10e7, 0x83a: 0x0f17, 0x83b: 0x0e9f,
+	0x83c: 0x1363, 0x83d: 0x0733, 0x83e: 0x122b, 0x83f: 0x081b,
+	// Block 0x21, offset 0x840
+	0x840: 0x080b, 0x841: 0x0b0b, 0x842: 0x0c2b, 0x843: 0x10f3, 0x844: 0x0a53, 0x845: 0x0e03,
+	0x846: 0x0cef, 0x847: 0x13e7, 0x848: 0x12e7, 0x849: 0x14ab, 0x84a: 0x1323, 0x84b: 0x0b27,
+	0x84c: 0x0787, 0x84d: 0x095b, 0x850: 0x09af,
+	0x852: 0x0cdf, 0x855: 0x07f7, 0x856: 0x0f1f, 0x857: 0x0fe3,
+	0x858: 0x1047, 0x859: 0x1063, 0x85a: 0x1067, 0x85b: 0x107b, 0x85c: 0x14fb, 0x85d: 0x10eb,
+	0x85e: 0x116f, 0x860: 0x128f, 0x862: 0x1353,
+	0x865: 0x1407, 0x866: 0x1433,
+	0x86a: 0x154f, 0x86b: 0x1553, 0x86c: 0x1557, 0x86d: 0x15bb, 0x86e: 0x142b, 0x86f: 0x14c7,
+	0x870: 0x0757, 0x871: 0x077b, 0x872: 0x078f, 0x873: 0x084b, 0x874: 0x0857, 0x875: 0x0897,
+	0x876: 0x094b, 0x877: 0x0967, 0x878: 0x096f, 0x879: 0x09ab, 0x87a: 0x09b7, 0x87b: 0x0a93,
+	0x87c: 0x0a9b, 0x87d: 0x0ba3, 0x87e: 0x0bcb, 0x87f: 0x0bd3,
+	// Block 0x22, offset 0x880
+	0x880: 0x0beb, 0x881: 0x0c97, 0x882: 0x0cc7, 0x883: 0x0ce7, 0x884: 0x0d57, 0x885: 0x0e1b,
+	0x886: 0x0e37, 0x887: 0x0e67, 0x888: 0x0ebb, 0x889: 0x0edb, 0x88a: 0x0f4f, 0x88b: 0x102f,
+	0x88c: 0x104b, 0x88d: 0x1053, 0x88e: 0x104f, 0x88f: 0x1057, 0x890: 0x105b, 0x891: 0x105f,
+	0x892: 0x1073, 0x893: 0x1077, 0x894: 0x109b, 0x895: 0x10af, 0x896: 0x10cb, 0x897: 0x112f,
+	0x898: 0x1137, 0x899: 0x113f, 0x89a: 0x1153, 0x89b: 0x117b, 0x89c: 0x11cb, 0x89d: 0x11ff,
+	0x89e: 0x11ff, 0x89f: 0x1267, 0x8a0: 0x130f, 0x8a1: 0x1327, 0x8a2: 0x135b, 0x8a3: 0x135f,
+	0x8a4: 0x13a3, 0x8a5: 0x13a7, 0x8a6: 0x13ff, 0x8a7: 0x1407, 0x8a8: 0x14db, 0x8a9: 0x151f,
+	0x8aa: 0x1537, 0x8ab: 0x0b9b, 0x8ac: 0x171e, 0x8ad: 0x11e3,
+	0x8b0: 0x06df, 0x8b1: 0x07e3, 0x8b2: 0x07a3, 0x8b3: 0x074b, 0x8b4: 0x078b, 0x8b5: 0x07b7,
+	0x8b6: 0x0847, 0x8b7: 0x0863, 0x8b8: 0x094b, 0x8b9: 0x0937, 0x8ba: 0x0947, 0x8bb: 0x0963,
+	0x8bc: 0x09af, 0x8bd: 0x09bf, 0x8be: 0x0a03, 0x8bf: 0x0a0f,
+	// Block 0x23, offset 0x8c0
+	0x8c0: 0x0a2b, 0x8c1: 0x0a3b, 0x8c2: 0x0b23, 0x8c3: 0x0b2b, 0x8c4: 0x0b5b, 0x8c5: 0x0b7b,
+	0x8c6: 0x0bab, 0x8c7: 0x0bc3, 0x8c8: 0x0bb3, 0x8c9: 0x0bd3, 0x8ca: 0x0bc7, 0x8cb: 0x0beb,
+	0x8cc: 0x0c07, 0x8cd: 0x0c5f, 0x8ce: 0x0c6b, 0x8cf: 0x0c73, 0x8d0: 0x0c9b, 0x8d1: 0x0cdf,
+	0x8d2: 0x0d0f, 0x8d3: 0x0d13, 0x8d4: 0x0d27, 0x8d5: 0x0da7, 0x8d6: 0x0db7, 0x8d7: 0x0e0f,
+	0x8d8: 0x0e5b, 0x8d9: 0x0e53, 0x8da: 0x0e67, 0x8db: 0x0e83, 0x8dc: 0x0ebb, 0x8dd: 0x1013,
+	0x8de: 0x0edf, 0x8df: 0x0f13, 0x8e0: 0x0f1f, 0x8e1: 0x0f5f, 0x8e2: 0x0f7b, 0x8e3: 0x0f9f,
+	0x8e4: 0x0fc3, 0x8e5: 0x0fc7, 0x8e6: 0x0fe3, 0x8e7: 0x0fe7, 0x8e8: 0x0ff7, 0x8e9: 0x100b,
+	0x8ea: 0x1007, 0x8eb: 0x1037, 0x8ec: 0x10b3, 0x8ed: 0x10cb, 0x8ee: 0x10e3, 0x8ef: 0x111b,
+	0x8f0: 0x112f, 0x8f1: 0x114b, 0x8f2: 0x117b, 0x8f3: 0x122f, 0x8f4: 0x1257, 0x8f5: 0x12cb,
+	0x8f6: 0x1313, 0x8f7: 0x131f, 0x8f8: 0x1327, 0x8f9: 0x133f, 0x8fa: 0x1353, 0x8fb: 0x1343,
+	0x8fc: 0x135b, 0x8fd: 0x1357, 0x8fe: 0x134f, 0x8ff: 0x135f,
+	// Block 0x24, offset 0x900
+	0x900: 0x136b, 0x901: 0x13a7, 0x902: 0x13e3, 0x903: 0x1413, 0x904: 0x144b, 0x905: 0x146b,
+	0x906: 0x14b7, 0x907: 0x14db, 0x908: 0x14fb, 0x909: 0x150f, 0x90a: 0x151f, 0x90b: 0x152b,
+	0x90c: 0x1537, 0x90d: 0x158b, 0x90e: 0x162b, 0x90f: 0x16b5, 0x910: 0x16b0, 0x911: 0x16e2,
+	0x912: 0x0607, 0x913: 0x062f, 0x914: 0x0633, 0x915: 0x1764, 0x916: 0x1791, 0x917: 0x1809,
+	0x918: 0x1617, 0x919: 0x1627,
+	// Block 0x25, offset 0x940
+	0x940: 0x06fb, 0x941: 0x06f3, 0x942: 0x0703, 0x943: 0x1647, 0x944: 0x0747, 0x945: 0x0757,
+	0x946: 0x075b, 0x947: 0x0763, 0x948: 0x076b, 0x949: 0x076f, 0x94a: 0x077b, 0x94b: 0x0773,
+	0x94c: 0x05b3, 0x94d: 0x165b, 0x94e: 0x078f, 0x94f: 0x0793, 0x950: 0x0797, 0x951: 0x07b3,
+	0x952: 0x164c, 0x953: 0x05b7, 0x954: 0x079f, 0x955: 0x07bf, 0x956: 0x1656, 0x957: 0x07cf,
+	0x958: 0x07d7, 0x959: 0x0737, 0x95a: 0x07df, 0x95b: 0x07e3, 0x95c: 0x1831, 0x95d: 0x07ff,
+	0x95e: 0x0807, 0x95f: 0x05bf, 0x960: 0x081f, 0x961: 0x0823, 0x962: 0x082b, 0x963: 0x082f,
+	0x964: 0x05c3, 0x965: 0x0847, 0x966: 0x084b, 0x967: 0x0857, 0x968: 0x0863, 0x969: 0x0867,
+	0x96a: 0x086b, 0x96b: 0x0873, 0x96c: 0x0893, 0x96d: 0x0897, 0x96e: 0x089f, 0x96f: 0x08af,
+	0x970: 0x08b7, 0x971: 0x08bb, 0x972: 0x08bb, 0x973: 0x08bb, 0x974: 0x166a, 0x975: 0x0e93,
+	0x976: 0x08cf, 0x977: 0x08d7, 0x978: 0x166f, 0x979: 0x08e3, 0x97a: 0x08eb, 0x97b: 0x08f3,
+	0x97c: 0x091b, 0x97d: 0x0907, 0x97e: 0x0913, 0x97f: 0x0917,
+	// Block 0x26, offset 0x980
+	0x980: 0x091f, 0x981: 0x0927, 0x982: 0x092b, 0x983: 0x0933, 0x984: 0x093b, 0x985: 0x093f,
+	0x986: 0x093f, 0x987: 0x0947, 0x988: 0x094f, 0x989: 0x0953, 0x98a: 0x095f, 0x98b: 0x0983,
+	0x98c: 0x0967, 0x98d: 0x0987, 0x98e: 0x096b, 0x98f: 0x0973, 0x990: 0x080b, 0x991: 0x09cf,
+	0x992: 0x0997, 0x993: 0x099b, 0x994: 0x099f, 0x995: 0x0993, 0x996: 0x09a7, 0x997: 0x09a3,
+	0x998: 0x09bb, 0x999: 0x1674, 0x99a: 0x09d7, 0x99b: 0x09db, 0x99c: 0x09e3, 0x99d: 0x09ef,
+	0x99e: 0x09f7, 0x99f: 0x0a13, 0x9a0: 0x1679, 0x9a1: 0x167e, 0x9a2: 0x0a1f, 0x9a3: 0x0a23,
+	0x9a4: 0x0a27, 0x9a5: 0x0a1b, 0x9a6: 0x0a2f, 0x9a7: 0x05c7, 0x9a8: 0x05cb, 0x9a9: 0x0a37,
+	0x9aa: 0x0a3f, 0x9ab: 0x0a3f, 0x9ac: 0x1683, 0x9ad: 0x0a5b, 0x9ae: 0x0a5f, 0x9af: 0x0a63,
+	0x9b0: 0x0a6b, 0x9b1: 0x1688, 0x9b2: 0x0a73, 0x9b3: 0x0a77, 0x9b4: 0x0b4f, 0x9b5: 0x0a7f,
+	0x9b6: 0x05cf, 0x9b7: 0x0a8b, 0x9b8: 0x0a9b, 0x9b9: 0x0aa7, 0x9ba: 0x0aa3, 0x9bb: 0x1692,
+	0x9bc: 0x0aaf, 0x9bd: 0x1697, 0x9be: 0x0abb, 0x9bf: 0x0ab7,
+	// Block 0x27, offset 0x9c0
+	0x9c0: 0x0abf, 0x9c1: 0x0acf, 0x9c2: 0x0ad3, 0x9c3: 0x05d3, 0x9c4: 0x0ae3, 0x9c5: 0x0aeb,
+	0x9c6: 0x0aef, 0x9c7: 0x0af3, 0x9c8: 0x05d7, 0x9c9: 0x169c, 0x9ca: 0x05db, 0x9cb: 0x0b0f,
+	0x9cc: 0x0b13, 0x9cd: 0x0b17, 0x9ce: 0x0b1f, 0x9cf: 0x1863, 0x9d0: 0x0b37, 0x9d1: 0x16a6,
+	0x9d2: 0x16a6, 0x9d3: 0x11d7, 0x9d4: 0x0b47, 0x9d5: 0x0b47, 0x9d6: 0x05df, 0x9d7: 0x16c9,
+	0x9d8: 0x179b, 0x9d9: 0x0b57, 0x9da: 0x0b5f, 0x9db: 0x05e3, 0x9dc: 0x0b73, 0x9dd: 0x0b83,
+	0x9de: 0x0b87, 0x9df: 0x0b8f, 0x9e0: 0x0b9f, 0x9e1: 0x05eb, 0x9e2: 0x05e7, 0x9e3: 0x0ba3,
+	0x9e4: 0x16ab, 0x9e5: 0x0ba7, 0x9e6: 0x0bbb, 0x9e7: 0x0bbf, 0x9e8: 0x0bc3, 0x9e9: 0x0bbf,
+	0x9ea: 0x0bcf, 0x9eb: 0x0bd3, 0x9ec: 0x0be3, 0x9ed: 0x0bdb, 0x9ee: 0x0bdf, 0x9ef: 0x0be7,
+	0x9f0: 0x0beb, 0x9f1: 0x0bef, 0x9f2: 0x0bfb, 0x9f3: 0x0bff, 0x9f4: 0x0c17, 0x9f5: 0x0c1f,
+	0x9f6: 0x0c2f, 0x9f7: 0x0c43, 0x9f8: 0x16ba, 0x9f9: 0x0c3f, 0x9fa: 0x0c33, 0x9fb: 0x0c4b,
+	0x9fc: 0x0c53, 0x9fd: 0x0c67, 0x9fe: 0x16bf, 0x9ff: 0x0c6f,
+	// Block 0x28, offset 0xa00
+	0xa00: 0x0c63, 0xa01: 0x0c5b, 0xa02: 0x05ef, 0xa03: 0x0c77, 0xa04: 0x0c7f, 0xa05: 0x0c87,
+	0xa06: 0x0c7b, 0xa07: 0x05f3, 0xa08: 0x0c97, 0xa09: 0x0c9f, 0xa0a: 0x16c4, 0xa0b: 0x0ccb,
+	0xa0c: 0x0cff, 0xa0d: 0x0cdb, 0xa0e: 0x05ff, 0xa0f: 0x0ce7, 0xa10: 0x05fb, 0xa11: 0x05f7,
+	0xa12: 0x07c3, 0xa13: 0x07c7, 0xa14: 0x0d03, 0xa15: 0x0ceb, 0xa16: 0x11ab, 0xa17: 0x0663,
+	0xa18: 0x0d0f, 0xa19: 0x0d13, 0xa1a: 0x0d17, 0xa1b: 0x0d2b, 0xa1c: 0x0d23, 0xa1d: 0x16dd,
+	0xa1e: 0x0603, 0xa1f: 0x0d3f, 0xa20: 0x0d33, 0xa21: 0x0d4f, 0xa22: 0x0d57, 0xa23: 0x16e7,
+	0xa24: 0x0d5b, 0xa25: 0x0d47, 0xa26: 0x0d63, 0xa27: 0x0607, 0xa28: 0x0d67, 0xa29: 0x0d6b,
+	0xa2a: 0x0d6f, 0xa2b: 0x0d7b, 0xa2c: 0x16ec, 0xa2d: 0x0d83, 0xa2e: 0x060b, 0xa2f: 0x0d8f,
+	0xa30: 0x16f1, 0xa31: 0x0d93, 0xa32: 0x060f, 0xa33: 0x0d9f, 0xa34: 0x0dab, 0xa35: 0x0db7,
+	0xa36: 0x0dbb, 0xa37: 0x16f6, 0xa38: 0x168d, 0xa39: 0x16fb, 0xa3a: 0x0ddb, 0xa3b: 0x1700,
+	0xa3c: 0x0de7, 0xa3d: 0x0def, 0xa3e: 0x0ddf, 0xa3f: 0x0dfb,
+	// Block 0x29, offset 0xa40
+	0xa40: 0x0e0b, 0xa41: 0x0e1b, 0xa42: 0x0e0f, 0xa43: 0x0e13, 0xa44: 0x0e1f, 0xa45: 0x0e23,
+	0xa46: 0x1705, 0xa47: 0x0e07, 0xa48: 0x0e3b, 0xa49: 0x0e3f, 0xa4a: 0x0613, 0xa4b: 0x0e53,
+	0xa4c: 0x0e4f, 0xa4d: 0x170a, 0xa4e: 0x0e33, 0xa4f: 0x0e6f, 0xa50: 0x170f, 0xa51: 0x1714,
+	0xa52: 0x0e73, 0xa53: 0x0e87, 0xa54: 0x0e83, 0xa55: 0x0e7f, 0xa56: 0x0617, 0xa57: 0x0e8b,
+	0xa58: 0x0e9b, 0xa59: 0x0e97, 0xa5a: 0x0ea3, 0xa5b: 0x1651, 0xa5c: 0x0eb3, 0xa5d: 0x1719,
+	0xa5e: 0x0ebf, 0xa5f: 0x1723, 0xa60: 0x0ed3, 0xa61: 0x0edf, 0xa62: 0x0ef3, 0xa63: 0x1728,
+	0xa64: 0x0f07, 0xa65: 0x0f0b, 0xa66: 0x172d, 0xa67: 0x1732, 0xa68: 0x0f27, 0xa69: 0x0f37,
+	0xa6a: 0x061b, 0xa6b: 0x0f3b, 0xa6c: 0x061f, 0xa6d: 0x061f, 0xa6e: 0x0f53, 0xa6f: 0x0f57,
+	0xa70: 0x0f5f, 0xa71: 0x0f63, 0xa72: 0x0f6f, 0xa73: 0x0623, 0xa74: 0x0f87, 0xa75: 0x1737,
+	0xa76: 0x0fa3, 0xa77: 0x173c, 0xa78: 0x0faf, 0xa79: 0x16a1, 0xa7a: 0x0fbf, 0xa7b: 0x1741,
+	0xa7c: 0x1746, 0xa7d: 0x174b, 0xa7e: 0x0627, 0xa7f: 0x062b,
+	// Block 0x2a, offset 0xa80
+	0xa80: 0x0ff7, 0xa81: 0x1755, 0xa82: 0x1750, 0xa83: 0x175a, 0xa84: 0x175f, 0xa85: 0x0fff,
+	0xa86: 0x1003, 0xa87: 0x1003, 0xa88: 0x100b, 0xa89: 0x0633, 0xa8a: 0x100f, 0xa8b: 0x0637,
+	0xa8c: 0x063b, 0xa8d: 0x1769, 0xa8e: 0x1023, 0xa8f: 0x102b, 0xa90: 0x1037, 0xa91: 0x063f,
+	0xa92: 0x176e, 0xa93: 0x105b, 0xa94: 0x1773, 0xa95: 0x1778, 0xa96: 0x107b, 0xa97: 0x1093,
+	0xa98: 0x0643, 0xa99: 0x109b, 0xa9a: 0x109f, 0xa9b: 0x10a3, 0xa9c: 0x177d, 0xa9d: 0x1782,
+	0xa9e: 0x1782, 0xa9f: 0x10bb, 0xaa0: 0x0647, 0xaa1: 0x1787, 0xaa2: 0x10cf, 0xaa3: 0x10d3,
+	0xaa4: 0x064b, 0xaa5: 0x178c, 0xaa6: 0x10ef, 0xaa7: 0x064f, 0xaa8: 0x10ff, 0xaa9: 0x10f7,
+	0xaaa: 0x1107, 0xaab: 0x1796, 0xaac: 0x111f, 0xaad: 0x0653, 0xaae: 0x112b, 0xaaf: 0x1133,
+	0xab0: 0x1143, 0xab1: 0x0657, 0xab2: 0x17a0, 0xab3: 0x17a5, 0xab4: 0x065b, 0xab5: 0x17aa,
+	0xab6: 0x115b, 0xab7: 0x17af, 0xab8: 0x1167, 0xab9: 0x1173, 0xaba: 0x117b, 0xabb: 0x17b4,
+	0xabc: 0x17b9, 0xabd: 0x118f, 0xabe: 0x17be, 0xabf: 0x1197,
+	// Block 0x2b, offset 0xac0
+	0xac0: 0x16ce, 0xac1: 0x065f, 0xac2: 0x11af, 0xac3: 0x11b3, 0xac4: 0x0667, 0xac5: 0x11b7,
+	0xac6: 0x0a33, 0xac7: 0x17c3, 0xac8: 0x17c8, 0xac9: 0x16d3, 0xaca: 0x16d8, 0xacb: 0x11d7,
+	0xacc: 0x11db, 0xacd: 0x13f3, 0xace: 0x066b, 0xacf: 0x1207, 0xad0: 0x1203, 0xad1: 0x120b,
+	0xad2: 0x083f, 0xad3: 0x120f, 0xad4: 0x1213, 0xad5: 0x1217, 0xad6: 0x121f, 0xad7: 0x17cd,
+	0xad8: 0x121b, 0xad9: 0x1223, 0xada: 0x1237, 0xadb: 0x123b, 0xadc: 0x1227, 0xadd: 0x123f,
+	0xade: 0x1253, 0xadf: 0x1267, 0xae0: 0x1233, 0xae1: 0x1247, 0xae2: 0x124b, 0xae3: 0x124f,
+	0xae4: 0x17d2, 0xae5: 0x17dc, 0xae6: 0x17d7, 0xae7: 0x066f, 0xae8: 0x126f, 0xae9: 0x1273,
+	0xaea: 0x127b, 0xaeb: 0x17f0, 0xaec: 0x127f, 0xaed: 0x17e1, 0xaee: 0x0673, 0xaef: 0x0677,
+	0xaf0: 0x17e6, 0xaf1: 0x17eb, 0xaf2: 0x067b, 0xaf3: 0x129f, 0xaf4: 0x12a3, 0xaf5: 0x12a7,
+	0xaf6: 0x12ab, 0xaf7: 0x12b7, 0xaf8: 0x12b3, 0xaf9: 0x12bf, 0xafa: 0x12bb, 0xafb: 0x12cb,
+	0xafc: 0x12c3, 0xafd: 0x12c7, 0xafe: 0x12cf, 0xaff: 0x067f,
+	// Block 0x2c, offset 0xb00
+	0xb00: 0x12d7, 0xb01: 0x12db, 0xb02: 0x0683, 0xb03: 0x12eb, 0xb04: 0x12ef, 0xb05: 0x17f5,
+	0xb06: 0x12fb, 0xb07: 0x12ff, 0xb08: 0x0687, 0xb09: 0x130b, 0xb0a: 0x05bb, 0xb0b: 0x17fa,
+	0xb0c: 0x17ff, 0xb0d: 0x068b, 0xb0e: 0x068f, 0xb0f: 0x1337, 0xb10: 0x134f, 0xb11: 0x136b,
+	0xb12: 0x137b, 0xb13: 0x1804, 0xb14: 0x138f, 0xb15: 0x1393, 0xb16: 0x13ab, 0xb17: 0x13b7,
+	0xb18: 0x180e, 0xb19: 0x1660, 0xb1a: 0x13c3, 0xb1b: 0x13bf, 0xb1c: 0x13cb, 0xb1d: 0x1665,
+	0xb1e: 0x13d7, 0xb1f: 0x13e3, 0xb20: 0x1813, 0xb21: 0x1818, 0xb22: 0x1423, 0xb23: 0x142f,
+	0xb24: 0x1437, 0xb25: 0x181d, 0xb26: 0x143b, 0xb27: 0x1467, 0xb28: 0x1473, 0xb29: 0x1477,
+	0xb2a: 0x146f, 0xb2b: 0x1483, 0xb2c: 0x1487, 0xb2d: 0x1822, 0xb2e: 0x1493, 0xb2f: 0x0693,
+	0xb30: 0x149b, 0xb31: 0x1827, 0xb32: 0x0697, 0xb33: 0x14d3, 0xb34: 0x0ac3, 0xb35: 0x14eb,
+	0xb36: 0x182c, 0xb37: 0x1836, 0xb38: 0x069b, 0xb39: 0x069f, 0xb3a: 0x1513, 0xb3b: 0x183b,
+	0xb3c: 0x06a3, 0xb3d: 0x1840, 0xb3e: 0x152b, 0xb3f: 0x152b,
+	// Block 0x2d, offset 0xb40
+	0xb40: 0x1533, 0xb41: 0x1845, 0xb42: 0x154b, 0xb43: 0x06a7, 0xb44: 0x155b, 0xb45: 0x1567,
+	0xb46: 0x156f, 0xb47: 0x1577, 0xb48: 0x06ab, 0xb49: 0x184a, 0xb4a: 0x158b, 0xb4b: 0x15a7,
+	0xb4c: 0x15b3, 0xb4d: 0x06af, 0xb4e: 0x06b3, 0xb4f: 0x15b7, 0xb50: 0x184f, 0xb51: 0x06b7,
+	0xb52: 0x1854, 0xb53: 0x1859, 0xb54: 0x185e, 0xb55: 0x15db, 0xb56: 0x06bb, 0xb57: 0x15ef,
+	0xb58: 0x15f7, 0xb59: 0x15fb, 0xb5a: 0x1603, 0xb5b: 0x160b, 0xb5c: 0x1613, 0xb5d: 0x1868,
+}
+
+// nfcIndex: 22 blocks, 1408 entries, 1408 bytes
+// Block 0 is the zero block.
+var nfcIndex = [1408]uint8{
+	// Block 0x0, offset 0x0
+	// Block 0x1, offset 0x40
+	// Block 0x2, offset 0x80
+	// Block 0x3, offset 0xc0
+	0xc2: 0x2c, 0xc3: 0x01, 0xc4: 0x02, 0xc5: 0x03, 0xc6: 0x2d, 0xc7: 0x04,
+	0xc8: 0x05, 0xca: 0x2e, 0xcb: 0x2f, 0xcc: 0x06, 0xcd: 0x07, 0xce: 0x08, 0xcf: 0x30,
+	0xd0: 0x09, 0xd1: 0x31, 0xd2: 0x32, 0xd3: 0x0a, 0xd6: 0x0b, 0xd7: 0x33,
+	0xd8: 0x34, 0xd9: 0x0c, 0xdb: 0x35, 0xdc: 0x36, 0xdd: 0x37, 0xdf: 0x38,
+	0xe0: 0x02, 0xe1: 0x03, 0xe2: 0x04, 0xe3: 0x05,
+	0xea: 0x06, 0xeb: 0x07, 0xec: 0x08, 0xed: 0x09, 0xef: 0x0a,
+	0xf0: 0x13,
+	// Block 0x4, offset 0x100
+	0x120: 0x39, 0x121: 0x3a, 0x123: 0x3b, 0x124: 0x3c, 0x125: 0x3d, 0x126: 0x3e, 0x127: 0x3f,
+	0x128: 0x40, 0x129: 0x41, 0x12a: 0x42, 0x12b: 0x43, 0x12c: 0x3e, 0x12d: 0x44, 0x12e: 0x45, 0x12f: 0x46,
+	0x131: 0x47, 0x132: 0x48, 0x133: 0x49, 0x134: 0x4a, 0x135: 0x4b, 0x137: 0x4c,
+	0x138: 0x4d, 0x139: 0x4e, 0x13a: 0x4f, 0x13b: 0x50, 0x13c: 0x51, 0x13d: 0x52, 0x13e: 0x53, 0x13f: 0x54,
+	// Block 0x5, offset 0x140
+	0x140: 0x55, 0x142: 0x56, 0x144: 0x57, 0x145: 0x58, 0x146: 0x59, 0x147: 0x5a,
+	0x14d: 0x5b,
+	0x15c: 0x5c, 0x15f: 0x5d,
+	0x162: 0x5e, 0x164: 0x5f,
+	0x168: 0x60, 0x169: 0x61, 0x16a: 0x62, 0x16c: 0x0d, 0x16d: 0x63, 0x16e: 0x64, 0x16f: 0x65,
+	0x170: 0x66, 0x173: 0x67, 0x177: 0x68,
+	0x178: 0x0e, 0x179: 0x0f, 0x17a: 0x10, 0x17b: 0x11, 0x17c: 0x12, 0x17d: 0x13, 0x17e: 0x14, 0x17f: 0x15,
+	// Block 0x6, offset 0x180
+	0x180: 0x69, 0x183: 0x6a, 0x184: 0x6b, 0x186: 0x6c, 0x187: 0x6d,
+	0x188: 0x6e, 0x189: 0x16, 0x18a: 0x17, 0x18b: 0x6f, 0x18c: 0x70,
+	0x1ab: 0x71,
+	0x1b3: 0x72, 0x1b5: 0x73, 0x1b7: 0x74,
+	// Block 0x7, offset 0x1c0
+	0x1c0: 0x75, 0x1c1: 0x18, 0x1c2: 0x19, 0x1c3: 0x1a, 0x1c4: 0x76, 0x1c5: 0x77,
+	0x1c9: 0x78, 0x1cc: 0x79, 0x1cd: 0x7a,
+	// Block 0x8, offset 0x200
+	0x219: 0x7b, 0x21a: 0x7c, 0x21b: 0x7d,
+	0x220: 0x7e, 0x223: 0x7f, 0x224: 0x80, 0x225: 0x81, 0x226: 0x82, 0x227: 0x83,
+	0x22a: 0x84, 0x22b: 0x85, 0x22f: 0x86,
+	0x230: 0x87, 0x231: 0x88, 0x232: 0x89, 0x233: 0x8a, 0x234: 0x8b, 0x235: 0x8c, 0x236: 0x8d, 0x237: 0x87,
+	0x238: 0x88, 0x239: 0x89, 0x23a: 0x8a, 0x23b: 0x8b, 0x23c: 0x8c, 0x23d: 0x8d, 0x23e: 0x87, 0x23f: 0x88,
+	// Block 0x9, offset 0x240
+	0x240: 0x89, 0x241: 0x8a, 0x242: 0x8b, 0x243: 0x8c, 0x244: 0x8d, 0x245: 0x87, 0x246: 0x88, 0x247: 0x89,
+	0x248: 0x8a, 0x249: 0x8b, 0x24a: 0x8c, 0x24b: 0x8d, 0x24c: 0x87, 0x24d: 0x88, 0x24e: 0x89, 0x24f: 0x8a,
+	0x250: 0x8b, 0x251: 0x8c, 0x252: 0x8d, 0x253: 0x87, 0x254: 0x88, 0x255: 0x89, 0x256: 0x8a, 0x257: 0x8b,
+	0x258: 0x8c, 0x259: 0x8d, 0x25a: 0x87, 0x25b: 0x88, 0x25c: 0x89, 0x25d: 0x8a, 0x25e: 0x8b, 0x25f: 0x8c,
+	0x260: 0x8d, 0x261: 0x87, 0x262: 0x88, 0x263: 0x89, 0x264: 0x8a, 0x265: 0x8b, 0x266: 0x8c, 0x267: 0x8d,
+	0x268: 0x87, 0x269: 0x88, 0x26a: 0x89, 0x26b: 0x8a, 0x26c: 0x8b, 0x26d: 0x8c, 0x26e: 0x8d, 0x26f: 0x87,
+	0x270: 0x88, 0x271: 0x89, 0x272: 0x8a, 0x273: 0x8b, 0x274: 0x8c, 0x275: 0x8d, 0x276: 0x87, 0x277: 0x88,
+	0x278: 0x89, 0x279: 0x8a, 0x27a: 0x8b, 0x27b: 0x8c, 0x27c: 0x8d, 0x27d: 0x87, 0x27e: 0x88, 0x27f: 0x89,
+	// Block 0xa, offset 0x280
+	0x280: 0x8a, 0x281: 0x8b, 0x282: 0x8c, 0x283: 0x8d, 0x284: 0x87, 0x285: 0x88, 0x286: 0x89, 0x287: 0x8a,
+	0x288: 0x8b, 0x289: 0x8c, 0x28a: 0x8d, 0x28b: 0x87, 0x28c: 0x88, 0x28d: 0x89, 0x28e: 0x8a, 0x28f: 0x8b,
+	0x290: 0x8c, 0x291: 0x8d, 0x292: 0x87, 0x293: 0x88, 0x294: 0x89, 0x295: 0x8a, 0x296: 0x8b, 0x297: 0x8c,
+	0x298: 0x8d, 0x299: 0x87, 0x29a: 0x88, 0x29b: 0x89, 0x29c: 0x8a, 0x29d: 0x8b, 0x29e: 0x8c, 0x29f: 0x8d,
+	0x2a0: 0x87, 0x2a1: 0x88, 0x2a2: 0x89, 0x2a3: 0x8a, 0x2a4: 0x8b, 0x2a5: 0x8c, 0x2a6: 0x8d, 0x2a7: 0x87,
+	0x2a8: 0x88, 0x2a9: 0x89, 0x2aa: 0x8a, 0x2ab: 0x8b, 0x2ac: 0x8c, 0x2ad: 0x8d, 0x2ae: 0x87, 0x2af: 0x88,
+	0x2b0: 0x89, 0x2b1: 0x8a, 0x2b2: 0x8b, 0x2b3: 0x8c, 0x2b4: 0x8d, 0x2b5: 0x87, 0x2b6: 0x88, 0x2b7: 0x89,
+	0x2b8: 0x8a, 0x2b9: 0x8b, 0x2ba: 0x8c, 0x2bb: 0x8d, 0x2bc: 0x87, 0x2bd: 0x88, 0x2be: 0x89, 0x2bf: 0x8a,
+	// Block 0xb, offset 0x2c0
+	0x2c0: 0x8b, 0x2c1: 0x8c, 0x2c2: 0x8d, 0x2c3: 0x87, 0x2c4: 0x88, 0x2c5: 0x89, 0x2c6: 0x8a, 0x2c7: 0x8b,
+	0x2c8: 0x8c, 0x2c9: 0x8d, 0x2ca: 0x87, 0x2cb: 0x88, 0x2cc: 0x89, 0x2cd: 0x8a, 0x2ce: 0x8b, 0x2cf: 0x8c,
+	0x2d0: 0x8d, 0x2d1: 0x87, 0x2d2: 0x88, 0x2d3: 0x89, 0x2d4: 0x8a, 0x2d5: 0x8b, 0x2d6: 0x8c, 0x2d7: 0x8d,
+	0x2d8: 0x87, 0x2d9: 0x88, 0x2da: 0x89, 0x2db: 0x8a, 0x2dc: 0x8b, 0x2dd: 0x8c, 0x2de: 0x8e,
+	// Block 0xc, offset 0x300
+	0x324: 0x1b, 0x325: 0x1c, 0x326: 0x1d, 0x327: 0x1e,
+	0x328: 0x1f, 0x329: 0x20, 0x32a: 0x21, 0x32b: 0x22, 0x32c: 0x8f, 0x32d: 0x90, 0x32e: 0x91,
+	0x331: 0x92, 0x332: 0x93, 0x333: 0x94, 0x334: 0x95,
+	0x338: 0x96, 0x339: 0x97, 0x33a: 0x98, 0x33b: 0x99, 0x33e: 0x9a, 0x33f: 0x9b,
+	// Block 0xd, offset 0x340
+	0x347: 0x9c,
+	0x34b: 0x9d, 0x34d: 0x9e,
+	0x368: 0x9f, 0x36b: 0xa0,
+	// Block 0xe, offset 0x380
+	0x381: 0xa1, 0x382: 0xa2, 0x384: 0xa3, 0x385: 0x82, 0x387: 0xa4,
+	0x388: 0xa5, 0x38b: 0xa6, 0x38c: 0x3e, 0x38d: 0xa7,
+	0x391: 0xa8, 0x392: 0xa9, 0x393: 0xaa, 0x396: 0xab, 0x397: 0xac,
+	0x398: 0x73, 0x39a: 0xad, 0x39c: 0xae,
+	0x3b0: 0x73,
+	// Block 0xf, offset 0x3c0
+	0x3eb: 0xaf, 0x3ec: 0xb0,
+	// Block 0x10, offset 0x400
+	0x432: 0xb1,
+	// Block 0x11, offset 0x440
+	0x445: 0xb2, 0x446: 0xb3, 0x447: 0xb4,
+	0x449: 0xb5,
+	// Block 0x12, offset 0x480
+	0x480: 0xb6,
+	0x4a3: 0xb7, 0x4a5: 0xb8,
+	// Block 0x13, offset 0x4c0
+	0x4c8: 0xb9,
+	// Block 0x14, offset 0x500
+	0x520: 0x23, 0x521: 0x24, 0x522: 0x25, 0x523: 0x26, 0x524: 0x27, 0x525: 0x28, 0x526: 0x29, 0x527: 0x2a,
+	0x528: 0x2b,
+	// Block 0x15, offset 0x540
+	0x550: 0x0b, 0x551: 0x0c, 0x556: 0x0d,
+	0x55b: 0x0e, 0x55d: 0x0f, 0x55e: 0x10, 0x55f: 0x11,
+	0x56f: 0x12,
+}
+
+// nfcSparseOffset: 142 entries, 284 bytes
+var nfcSparseOffset = []uint16{0x0, 0x5, 0x9, 0xb, 0xd, 0x18, 0x28, 0x2a, 0x2f, 0x3a, 0x49, 0x56, 0x5e, 0x62, 0x67, 0x69, 0x7a, 0x82, 0x89, 0x8c, 0x93, 0x97, 0x9b, 0x9d, 0x9f, 0xa8, 0xac, 0xb3, 0xb8, 0xbb, 0xc5, 0xc7, 0xce, 0xd6, 0xd9, 0xdb, 0xdd, 0xdf, 0xe4, 0xf5, 0x101, 0x103, 0x109, 0x10b, 0x10d, 0x10f, 0x111, 0x113, 0x115, 0x118, 0x11b, 0x11d, 0x120, 0x123, 0x127, 0x12c, 0x135, 0x137, 0x13a, 0x13c, 0x147, 0x157, 0x15b, 0x169, 0x16c, 0x172, 0x178, 0x183, 0x187, 0x189, 0x18b, 0x18d, 0x [...]
+
+// nfcSparseValues: 688 entries, 2752 bytes
+var nfcSparseValues = [688]valueRange{
+	// Block 0x0, offset 0x0
+	{value: 0x0000, lo: 0x04},
+	{value: 0xa100, lo: 0xa8, hi: 0xa8},
+	{value: 0x8100, lo: 0xaf, hi: 0xaf},
+	{value: 0x8100, lo: 0xb4, hi: 0xb4},
+	{value: 0x8100, lo: 0xb8, hi: 0xb8},
+	// Block 0x1, offset 0x5
+	{value: 0x0091, lo: 0x03},
+	{value: 0x4778, lo: 0xa0, hi: 0xa1},
+	{value: 0x47aa, lo: 0xaf, hi: 0xb0},
+	{value: 0xa000, lo: 0xb7, hi: 0xb7},
+	// Block 0x2, offset 0x9
+	{value: 0x0000, lo: 0x01},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	// Block 0x3, offset 0xb
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8100, lo: 0x98, hi: 0x9d},
+	// Block 0x4, offset 0xd
+	{value: 0x0006, lo: 0x0a},
+	{value: 0xa000, lo: 0x81, hi: 0x81},
+	{value: 0xa000, lo: 0x85, hi: 0x85},
+	{value: 0xa000, lo: 0x89, hi: 0x89},
+	{value: 0x48d6, lo: 0x8a, hi: 0x8a},
+	{value: 0x48f4, lo: 0x8b, hi: 0x8b},
+	{value: 0x36c7, lo: 0x8c, hi: 0x8c},
+	{value: 0x36df, lo: 0x8d, hi: 0x8d},
+	{value: 0x490c, lo: 0x8e, hi: 0x8e},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	{value: 0x36fd, lo: 0x93, hi: 0x94},
+	// Block 0x5, offset 0x18
+	{value: 0x0000, lo: 0x0f},
+	{value: 0xa000, lo: 0x83, hi: 0x83},
+	{value: 0xa000, lo: 0x87, hi: 0x87},
+	{value: 0xa000, lo: 0x8b, hi: 0x8b},
+	{value: 0xa000, lo: 0x8d, hi: 0x8d},
+	{value: 0x37a5, lo: 0x90, hi: 0x90},
+	{value: 0x37b1, lo: 0x91, hi: 0x91},
+	{value: 0x379f, lo: 0x93, hi: 0x93},
+	{value: 0xa000, lo: 0x96, hi: 0x96},
+	{value: 0x3817, lo: 0x97, hi: 0x97},
+	{value: 0x37e1, lo: 0x9c, hi: 0x9c},
+	{value: 0x37c9, lo: 0x9d, hi: 0x9d},
+	{value: 0x37f3, lo: 0x9e, hi: 0x9e},
+	{value: 0xa000, lo: 0xb4, hi: 0xb5},
+	{value: 0x381d, lo: 0xb6, hi: 0xb6},
+	{value: 0x3823, lo: 0xb7, hi: 0xb7},
+	// Block 0x6, offset 0x28
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0x83, hi: 0x87},
+	// Block 0x7, offset 0x2a
+	{value: 0x0001, lo: 0x04},
+	{value: 0x8113, lo: 0x81, hi: 0x82},
+	{value: 0x8132, lo: 0x84, hi: 0x84},
+	{value: 0x812d, lo: 0x85, hi: 0x85},
+	{value: 0x810d, lo: 0x87, hi: 0x87},
+	// Block 0x8, offset 0x2f
+	{value: 0x0000, lo: 0x0a},
+	{value: 0x8132, lo: 0x90, hi: 0x97},
+	{value: 0x8119, lo: 0x98, hi: 0x98},
+	{value: 0x811a, lo: 0x99, hi: 0x99},
+	{value: 0x811b, lo: 0x9a, hi: 0x9a},
+	{value: 0x3841, lo: 0xa2, hi: 0xa2},
+	{value: 0x3847, lo: 0xa3, hi: 0xa3},
+	{value: 0x3853, lo: 0xa4, hi: 0xa4},
+	{value: 0x384d, lo: 0xa5, hi: 0xa5},
+	{value: 0x3859, lo: 0xa6, hi: 0xa6},
+	{value: 0xa000, lo: 0xa7, hi: 0xa7},
+	// Block 0x9, offset 0x3a
+	{value: 0x0000, lo: 0x0e},
+	{value: 0x386b, lo: 0x80, hi: 0x80},
+	{value: 0xa000, lo: 0x81, hi: 0x81},
+	{value: 0x385f, lo: 0x82, hi: 0x82},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	{value: 0x3865, lo: 0x93, hi: 0x93},
+	{value: 0xa000, lo: 0x95, hi: 0x95},
+	{value: 0x8132, lo: 0x96, hi: 0x9c},
+	{value: 0x8132, lo: 0x9f, hi: 0xa2},
+	{value: 0x812d, lo: 0xa3, hi: 0xa3},
+	{value: 0x8132, lo: 0xa4, hi: 0xa4},
+	{value: 0x8132, lo: 0xa7, hi: 0xa8},
+	{value: 0x812d, lo: 0xaa, hi: 0xaa},
+	{value: 0x8132, lo: 0xab, hi: 0xac},
+	{value: 0x812d, lo: 0xad, hi: 0xad},
+	// Block 0xa, offset 0x49
+	{value: 0x0000, lo: 0x0c},
+	{value: 0x811f, lo: 0x91, hi: 0x91},
+	{value: 0x8132, lo: 0xb0, hi: 0xb0},
+	{value: 0x812d, lo: 0xb1, hi: 0xb1},
+	{value: 0x8132, lo: 0xb2, hi: 0xb3},
+	{value: 0x812d, lo: 0xb4, hi: 0xb4},
+	{value: 0x8132, lo: 0xb5, hi: 0xb6},
+	{value: 0x812d, lo: 0xb7, hi: 0xb9},
+	{value: 0x8132, lo: 0xba, hi: 0xba},
+	{value: 0x812d, lo: 0xbb, hi: 0xbc},
+	{value: 0x8132, lo: 0xbd, hi: 0xbd},
+	{value: 0x812d, lo: 0xbe, hi: 0xbe},
+	{value: 0x8132, lo: 0xbf, hi: 0xbf},
+	// Block 0xb, offset 0x56
+	{value: 0x0005, lo: 0x07},
+	{value: 0x8132, lo: 0x80, hi: 0x80},
+	{value: 0x8132, lo: 0x81, hi: 0x81},
+	{value: 0x812d, lo: 0x82, hi: 0x83},
+	{value: 0x812d, lo: 0x84, hi: 0x85},
+	{value: 0x812d, lo: 0x86, hi: 0x87},
+	{value: 0x812d, lo: 0x88, hi: 0x89},
+	{value: 0x8132, lo: 0x8a, hi: 0x8a},
+	// Block 0xc, offset 0x5e
+	{value: 0x0000, lo: 0x03},
+	{value: 0x8132, lo: 0xab, hi: 0xb1},
+	{value: 0x812d, lo: 0xb2, hi: 0xb2},
+	{value: 0x8132, lo: 0xb3, hi: 0xb3},
+	// Block 0xd, offset 0x62
+	{value: 0x0000, lo: 0x04},
+	{value: 0x8132, lo: 0x96, hi: 0x99},
+	{value: 0x8132, lo: 0x9b, hi: 0xa3},
+	{value: 0x8132, lo: 0xa5, hi: 0xa7},
+	{value: 0x8132, lo: 0xa9, hi: 0xad},
+	// Block 0xe, offset 0x67
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0x99, hi: 0x9b},
+	// Block 0xf, offset 0x69
+	{value: 0x0000, lo: 0x10},
+	{value: 0x8132, lo: 0x94, hi: 0xa1},
+	{value: 0x812d, lo: 0xa3, hi: 0xa3},
+	{value: 0x8132, lo: 0xa4, hi: 0xa5},
+	{value: 0x812d, lo: 0xa6, hi: 0xa6},
+	{value: 0x8132, lo: 0xa7, hi: 0xa8},
+	{value: 0x812d, lo: 0xa9, hi: 0xa9},
+	{value: 0x8132, lo: 0xaa, hi: 0xac},
+	{value: 0x812d, lo: 0xad, hi: 0xaf},
+	{value: 0x8116, lo: 0xb0, hi: 0xb0},
+	{value: 0x8117, lo: 0xb1, hi: 0xb1},
+	{value: 0x8118, lo: 0xb2, hi: 0xb2},
+	{value: 0x8132, lo: 0xb3, hi: 0xb5},
+	{value: 0x812d, lo: 0xb6, hi: 0xb6},
+	{value: 0x8132, lo: 0xb7, hi: 0xb8},
+	{value: 0x812d, lo: 0xb9, hi: 0xba},
+	{value: 0x8132, lo: 0xbb, hi: 0xbf},
+	// Block 0x10, offset 0x7a
+	{value: 0x0000, lo: 0x07},
+	{value: 0xa000, lo: 0xa8, hi: 0xa8},
+	{value: 0x3ed8, lo: 0xa9, hi: 0xa9},
+	{value: 0xa000, lo: 0xb0, hi: 0xb0},
+	{value: 0x3ee0, lo: 0xb1, hi: 0xb1},
+	{value: 0xa000, lo: 0xb3, hi: 0xb3},
+	{value: 0x3ee8, lo: 0xb4, hi: 0xb4},
+	{value: 0x9902, lo: 0xbc, hi: 0xbc},
+	// Block 0x11, offset 0x82
+	{value: 0x0008, lo: 0x06},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x8132, lo: 0x91, hi: 0x91},
+	{value: 0x812d, lo: 0x92, hi: 0x92},
+	{value: 0x8132, lo: 0x93, hi: 0x93},
+	{value: 0x8132, lo: 0x94, hi: 0x94},
+	{value: 0x45b2, lo: 0x98, hi: 0x9f},
+	// Block 0x12, offset 0x89
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8102, lo: 0xbc, hi: 0xbc},
+	{value: 0x9900, lo: 0xbe, hi: 0xbe},
+	// Block 0x13, offset 0x8c
+	{value: 0x0008, lo: 0x06},
+	{value: 0xa000, lo: 0x87, hi: 0x87},
+	{value: 0x2c9e, lo: 0x8b, hi: 0x8c},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x97, hi: 0x97},
+	{value: 0x45f2, lo: 0x9c, hi: 0x9d},
+	{value: 0x4602, lo: 0x9f, hi: 0x9f},
+	// Block 0x14, offset 0x93
+	{value: 0x0000, lo: 0x03},
+	{value: 0x462a, lo: 0xb3, hi: 0xb3},
+	{value: 0x4632, lo: 0xb6, hi: 0xb6},
+	{value: 0x8102, lo: 0xbc, hi: 0xbc},
+	// Block 0x15, offset 0x97
+	{value: 0x0008, lo: 0x03},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x460a, lo: 0x99, hi: 0x9b},
+	{value: 0x4622, lo: 0x9e, hi: 0x9e},
+	// Block 0x16, offset 0x9b
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8102, lo: 0xbc, hi: 0xbc},
+	// Block 0x17, offset 0x9d
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	// Block 0x18, offset 0x9f
+	{value: 0x0000, lo: 0x08},
+	{value: 0xa000, lo: 0x87, hi: 0x87},
+	{value: 0x2cb6, lo: 0x88, hi: 0x88},
+	{value: 0x2cae, lo: 0x8b, hi: 0x8b},
+	{value: 0x2cbe, lo: 0x8c, hi: 0x8c},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x96, hi: 0x97},
+	{value: 0x463a, lo: 0x9c, hi: 0x9c},
+	{value: 0x4642, lo: 0x9d, hi: 0x9d},
+	// Block 0x19, offset 0xa8
+	{value: 0x0000, lo: 0x03},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	{value: 0x2cc6, lo: 0x94, hi: 0x94},
+	{value: 0x9900, lo: 0xbe, hi: 0xbe},
+	// Block 0x1a, offset 0xac
+	{value: 0x0000, lo: 0x06},
+	{value: 0xa000, lo: 0x86, hi: 0x87},
+	{value: 0x2cce, lo: 0x8a, hi: 0x8a},
+	{value: 0x2cde, lo: 0x8b, hi: 0x8b},
+	{value: 0x2cd6, lo: 0x8c, hi: 0x8c},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x97, hi: 0x97},
+	// Block 0x1b, offset 0xb3
+	{value: 0x1801, lo: 0x04},
+	{value: 0xa000, lo: 0x86, hi: 0x86},
+	{value: 0x3ef0, lo: 0x88, hi: 0x88},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x8120, lo: 0x95, hi: 0x96},
+	// Block 0x1c, offset 0xb8
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8102, lo: 0xbc, hi: 0xbc},
+	{value: 0xa000, lo: 0xbf, hi: 0xbf},
+	// Block 0x1d, offset 0xbb
+	{value: 0x0000, lo: 0x09},
+	{value: 0x2ce6, lo: 0x80, hi: 0x80},
+	{value: 0x9900, lo: 0x82, hi: 0x82},
+	{value: 0xa000, lo: 0x86, hi: 0x86},
+	{value: 0x2cee, lo: 0x87, hi: 0x87},
+	{value: 0x2cf6, lo: 0x88, hi: 0x88},
+	{value: 0x2f50, lo: 0x8a, hi: 0x8a},
+	{value: 0x2dd8, lo: 0x8b, hi: 0x8b},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x95, hi: 0x96},
+	// Block 0x1e, offset 0xc5
+	{value: 0x0000, lo: 0x01},
+	{value: 0x9900, lo: 0xbe, hi: 0xbe},
+	// Block 0x1f, offset 0xc7
+	{value: 0x0000, lo: 0x06},
+	{value: 0xa000, lo: 0x86, hi: 0x87},
+	{value: 0x2cfe, lo: 0x8a, hi: 0x8a},
+	{value: 0x2d0e, lo: 0x8b, hi: 0x8b},
+	{value: 0x2d06, lo: 0x8c, hi: 0x8c},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x97, hi: 0x97},
+	// Block 0x20, offset 0xce
+	{value: 0x6bea, lo: 0x07},
+	{value: 0x9904, lo: 0x8a, hi: 0x8a},
+	{value: 0x9900, lo: 0x8f, hi: 0x8f},
+	{value: 0xa000, lo: 0x99, hi: 0x99},
+	{value: 0x3ef8, lo: 0x9a, hi: 0x9a},
+	{value: 0x2f58, lo: 0x9c, hi: 0x9c},
+	{value: 0x2de3, lo: 0x9d, hi: 0x9d},
+	{value: 0x2d16, lo: 0x9e, hi: 0x9f},
+	// Block 0x21, offset 0xd6
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8122, lo: 0xb8, hi: 0xb9},
+	{value: 0x8104, lo: 0xba, hi: 0xba},
+	// Block 0x22, offset 0xd9
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8123, lo: 0x88, hi: 0x8b},
+	// Block 0x23, offset 0xdb
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8124, lo: 0xb8, hi: 0xb9},
+	// Block 0x24, offset 0xdd
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8125, lo: 0x88, hi: 0x8b},
+	// Block 0x25, offset 0xdf
+	{value: 0x0000, lo: 0x04},
+	{value: 0x812d, lo: 0x98, hi: 0x99},
+	{value: 0x812d, lo: 0xb5, hi: 0xb5},
+	{value: 0x812d, lo: 0xb7, hi: 0xb7},
+	{value: 0x812b, lo: 0xb9, hi: 0xb9},
+	// Block 0x26, offset 0xe4
+	{value: 0x0000, lo: 0x10},
+	{value: 0x2644, lo: 0x83, hi: 0x83},
+	{value: 0x264b, lo: 0x8d, hi: 0x8d},
+	{value: 0x2652, lo: 0x92, hi: 0x92},
+	{value: 0x2659, lo: 0x97, hi: 0x97},
+	{value: 0x2660, lo: 0x9c, hi: 0x9c},
+	{value: 0x263d, lo: 0xa9, hi: 0xa9},
+	{value: 0x8126, lo: 0xb1, hi: 0xb1},
+	{value: 0x8127, lo: 0xb2, hi: 0xb2},
+	{value: 0x4a66, lo: 0xb3, hi: 0xb3},
+	{value: 0x8128, lo: 0xb4, hi: 0xb4},
+	{value: 0x4a6f, lo: 0xb5, hi: 0xb5},
+	{value: 0x464a, lo: 0xb6, hi: 0xb6},
+	{value: 0x8200, lo: 0xb7, hi: 0xb7},
+	{value: 0x4652, lo: 0xb8, hi: 0xb8},
+	{value: 0x8200, lo: 0xb9, hi: 0xb9},
+	{value: 0x8127, lo: 0xba, hi: 0xbd},
+	// Block 0x27, offset 0xf5
+	{value: 0x0000, lo: 0x0b},
+	{value: 0x8127, lo: 0x80, hi: 0x80},
+	{value: 0x4a78, lo: 0x81, hi: 0x81},
+	{value: 0x8132, lo: 0x82, hi: 0x83},
+	{value: 0x8104, lo: 0x84, hi: 0x84},
+	{value: 0x8132, lo: 0x86, hi: 0x87},
+	{value: 0x266e, lo: 0x93, hi: 0x93},
+	{value: 0x2675, lo: 0x9d, hi: 0x9d},
+	{value: 0x267c, lo: 0xa2, hi: 0xa2},
+	{value: 0x2683, lo: 0xa7, hi: 0xa7},
+	{value: 0x268a, lo: 0xac, hi: 0xac},
+	{value: 0x2667, lo: 0xb9, hi: 0xb9},
+	// Block 0x28, offset 0x101
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0x86, hi: 0x86},
+	// Block 0x29, offset 0x103
+	{value: 0x0000, lo: 0x05},
+	{value: 0xa000, lo: 0xa5, hi: 0xa5},
+	{value: 0x2d1e, lo: 0xa6, hi: 0xa6},
+	{value: 0x9900, lo: 0xae, hi: 0xae},
+	{value: 0x8102, lo: 0xb7, hi: 0xb7},
+	{value: 0x8104, lo: 0xb9, hi: 0xba},
+	// Block 0x2a, offset 0x109
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0x8d, hi: 0x8d},
+	// Block 0x2b, offset 0x10b
+	{value: 0x0000, lo: 0x01},
+	{value: 0xa000, lo: 0x80, hi: 0x92},
+	// Block 0x2c, offset 0x10d
+	{value: 0x0000, lo: 0x01},
+	{value: 0xb900, lo: 0xa1, hi: 0xb5},
+	// Block 0x2d, offset 0x10f
+	{value: 0x0000, lo: 0x01},
+	{value: 0x9900, lo: 0xa8, hi: 0xbf},
+	// Block 0x2e, offset 0x111
+	{value: 0x0000, lo: 0x01},
+	{value: 0x9900, lo: 0x80, hi: 0x82},
+	// Block 0x2f, offset 0x113
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0x9d, hi: 0x9f},
+	// Block 0x30, offset 0x115
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x94, hi: 0x94},
+	{value: 0x8104, lo: 0xb4, hi: 0xb4},
+	// Block 0x31, offset 0x118
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x92, hi: 0x92},
+	{value: 0x8132, lo: 0x9d, hi: 0x9d},
+	// Block 0x32, offset 0x11b
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8131, lo: 0xa9, hi: 0xa9},
+	// Block 0x33, offset 0x11d
+	{value: 0x0004, lo: 0x02},
+	{value: 0x812e, lo: 0xb9, hi: 0xba},
+	{value: 0x812d, lo: 0xbb, hi: 0xbb},
+	// Block 0x34, offset 0x120
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8132, lo: 0x97, hi: 0x97},
+	{value: 0x812d, lo: 0x98, hi: 0x98},
+	// Block 0x35, offset 0x123
+	{value: 0x0000, lo: 0x03},
+	{value: 0x8104, lo: 0xa0, hi: 0xa0},
+	{value: 0x8132, lo: 0xb5, hi: 0xbc},
+	{value: 0x812d, lo: 0xbf, hi: 0xbf},
+	// Block 0x36, offset 0x127
+	{value: 0x0000, lo: 0x04},
+	{value: 0x8132, lo: 0xb0, hi: 0xb4},
+	{value: 0x812d, lo: 0xb5, hi: 0xba},
+	{value: 0x8132, lo: 0xbb, hi: 0xbc},
+	{value: 0x812d, lo: 0xbd, hi: 0xbd},
+	// Block 0x37, offset 0x12c
+	{value: 0x0000, lo: 0x08},
+	{value: 0x2d66, lo: 0x80, hi: 0x80},
+	{value: 0x2d6e, lo: 0x81, hi: 0x81},
+	{value: 0xa000, lo: 0x82, hi: 0x82},
+	{value: 0x2d76, lo: 0x83, hi: 0x83},
+	{value: 0x8104, lo: 0x84, hi: 0x84},
+	{value: 0x8132, lo: 0xab, hi: 0xab},
+	{value: 0x812d, lo: 0xac, hi: 0xac},
+	{value: 0x8132, lo: 0xad, hi: 0xb3},
+	// Block 0x38, offset 0x135
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0xaa, hi: 0xab},
+	// Block 0x39, offset 0x137
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8102, lo: 0xa6, hi: 0xa6},
+	{value: 0x8104, lo: 0xb2, hi: 0xb3},
+	// Block 0x3a, offset 0x13a
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8102, lo: 0xb7, hi: 0xb7},
+	// Block 0x3b, offset 0x13c
+	{value: 0x0000, lo: 0x0a},
+	{value: 0x8132, lo: 0x90, hi: 0x92},
+	{value: 0x8101, lo: 0x94, hi: 0x94},
+	{value: 0x812d, lo: 0x95, hi: 0x99},
+	{value: 0x8132, lo: 0x9a, hi: 0x9b},
+	{value: 0x812d, lo: 0x9c, hi: 0x9f},
+	{value: 0x8132, lo: 0xa0, hi: 0xa0},
+	{value: 0x8101, lo: 0xa2, hi: 0xa8},
+	{value: 0x812d, lo: 0xad, hi: 0xad},
+	{value: 0x8132, lo: 0xb4, hi: 0xb4},
+	{value: 0x8132, lo: 0xb8, hi: 0xb9},
+	// Block 0x3c, offset 0x147
+	{value: 0x0000, lo: 0x0f},
+	{value: 0x8132, lo: 0x80, hi: 0x81},
+	{value: 0x812d, lo: 0x82, hi: 0x82},
+	{value: 0x8132, lo: 0x83, hi: 0x89},
+	{value: 0x812d, lo: 0x8a, hi: 0x8a},
+	{value: 0x8132, lo: 0x8b, hi: 0x8c},
+	{value: 0x8135, lo: 0x8d, hi: 0x8d},
+	{value: 0x812a, lo: 0x8e, hi: 0x8e},
+	{value: 0x812d, lo: 0x8f, hi: 0x8f},
+	{value: 0x8129, lo: 0x90, hi: 0x90},
+	{value: 0x8132, lo: 0x91, hi: 0xb5},
+	{value: 0x8132, lo: 0xbb, hi: 0xbb},
+	{value: 0x8134, lo: 0xbc, hi: 0xbc},
+	{value: 0x812d, lo: 0xbd, hi: 0xbd},
+	{value: 0x8132, lo: 0xbe, hi: 0xbe},
+	{value: 0x812d, lo: 0xbf, hi: 0xbf},
+	// Block 0x3d, offset 0x157
+	{value: 0x0004, lo: 0x03},
+	{value: 0x0433, lo: 0x80, hi: 0x81},
+	{value: 0x8100, lo: 0x97, hi: 0x97},
+	{value: 0x8100, lo: 0xbe, hi: 0xbe},
+	// Block 0x3e, offset 0x15b
+	{value: 0x0000, lo: 0x0d},
+	{value: 0x8132, lo: 0x90, hi: 0x91},
+	{value: 0x8101, lo: 0x92, hi: 0x93},
+	{value: 0x8132, lo: 0x94, hi: 0x97},
+	{value: 0x8101, lo: 0x98, hi: 0x9a},
+	{value: 0x8132, lo: 0x9b, hi: 0x9c},
+	{value: 0x8132, lo: 0xa1, hi: 0xa1},
+	{value: 0x8101, lo: 0xa5, hi: 0xa6},
+	{value: 0x8132, lo: 0xa7, hi: 0xa7},
+	{value: 0x812d, lo: 0xa8, hi: 0xa8},
+	{value: 0x8132, lo: 0xa9, hi: 0xa9},
+	{value: 0x8101, lo: 0xaa, hi: 0xab},
+	{value: 0x812d, lo: 0xac, hi: 0xaf},
+	{value: 0x8132, lo: 0xb0, hi: 0xb0},
+	// Block 0x3f, offset 0x169
+	{value: 0x427b, lo: 0x02},
+	{value: 0x01b8, lo: 0xa6, hi: 0xa6},
+	{value: 0x0057, lo: 0xaa, hi: 0xab},
+	// Block 0x40, offset 0x16c
+	{value: 0x0007, lo: 0x05},
+	{value: 0xa000, lo: 0x90, hi: 0x90},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	{value: 0xa000, lo: 0x94, hi: 0x94},
+	{value: 0x3bb9, lo: 0x9a, hi: 0x9b},
+	{value: 0x3bc7, lo: 0xae, hi: 0xae},
+	// Block 0x41, offset 0x172
+	{value: 0x000e, lo: 0x05},
+	{value: 0x3bce, lo: 0x8d, hi: 0x8e},
+	{value: 0x3bd5, lo: 0x8f, hi: 0x8f},
+	{value: 0xa000, lo: 0x90, hi: 0x90},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	{value: 0xa000, lo: 0x94, hi: 0x94},
+	// Block 0x42, offset 0x178
+	{value: 0x6408, lo: 0x0a},
+	{value: 0xa000, lo: 0x83, hi: 0x83},
+	{value: 0x3be3, lo: 0x84, hi: 0x84},
+	{value: 0xa000, lo: 0x88, hi: 0x88},
+	{value: 0x3bea, lo: 0x89, hi: 0x89},
+	{value: 0xa000, lo: 0x8b, hi: 0x8b},
+	{value: 0x3bf1, lo: 0x8c, hi: 0x8c},
+	{value: 0xa000, lo: 0xa3, hi: 0xa3},
+	{value: 0x3bf8, lo: 0xa4, hi: 0xa5},
+	{value: 0x3bff, lo: 0xa6, hi: 0xa6},
+	{value: 0xa000, lo: 0xbc, hi: 0xbc},
+	// Block 0x43, offset 0x183
+	{value: 0x0007, lo: 0x03},
+	{value: 0x3c68, lo: 0xa0, hi: 0xa1},
+	{value: 0x3c92, lo: 0xa2, hi: 0xa3},
+	{value: 0x3cbc, lo: 0xaa, hi: 0xad},
+	// Block 0x44, offset 0x187
+	{value: 0x0004, lo: 0x01},
+	{value: 0x048b, lo: 0xa9, hi: 0xaa},
+	// Block 0x45, offset 0x189
+	{value: 0x0000, lo: 0x01},
+	{value: 0x4573, lo: 0x9c, hi: 0x9c},
+	// Block 0x46, offset 0x18b
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0xaf, hi: 0xb1},
+	// Block 0x47, offset 0x18d
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0xbf, hi: 0xbf},
+	// Block 0x48, offset 0x18f
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0xa0, hi: 0xbf},
+	// Block 0x49, offset 0x191
+	{value: 0x0000, lo: 0x05},
+	{value: 0x812c, lo: 0xaa, hi: 0xaa},
+	{value: 0x8131, lo: 0xab, hi: 0xab},
+	{value: 0x8133, lo: 0xac, hi: 0xac},
+	{value: 0x812e, lo: 0xad, hi: 0xad},
+	{value: 0x812f, lo: 0xae, hi: 0xaf},
+	// Block 0x4a, offset 0x197
+	{value: 0x0000, lo: 0x03},
+	{value: 0x4a81, lo: 0xb3, hi: 0xb3},
+	{value: 0x4a81, lo: 0xb5, hi: 0xb6},
+	{value: 0x4a81, lo: 0xba, hi: 0xbf},
+	// Block 0x4b, offset 0x19b
+	{value: 0x0000, lo: 0x01},
+	{value: 0x4a81, lo: 0x8f, hi: 0xa3},
+	// Block 0x4c, offset 0x19d
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8100, lo: 0xae, hi: 0xbe},
+	// Block 0x4d, offset 0x19f
+	{value: 0x0000, lo: 0x07},
+	{value: 0x8100, lo: 0x84, hi: 0x84},
+	{value: 0x8100, lo: 0x87, hi: 0x87},
+	{value: 0x8100, lo: 0x90, hi: 0x90},
+	{value: 0x8100, lo: 0x9e, hi: 0x9e},
+	{value: 0x8100, lo: 0xa1, hi: 0xa1},
+	{value: 0x8100, lo: 0xb2, hi: 0xb2},
+	{value: 0x8100, lo: 0xbb, hi: 0xbb},
+	// Block 0x4e, offset 0x1a7
+	{value: 0x0000, lo: 0x03},
+	{value: 0x8100, lo: 0x80, hi: 0x80},
+	{value: 0x8100, lo: 0x8b, hi: 0x8b},
+	{value: 0x8100, lo: 0x8e, hi: 0x8e},
+	// Block 0x4f, offset 0x1ab
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8132, lo: 0xaf, hi: 0xaf},
+	{value: 0x8132, lo: 0xb4, hi: 0xbd},
+	// Block 0x50, offset 0x1ae
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0x9e, hi: 0x9f},
+	// Block 0x51, offset 0x1b0
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0xb0, hi: 0xb1},
+	// Block 0x52, offset 0x1b2
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0x86, hi: 0x86},
+	// Block 0x53, offset 0x1b4
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x84, hi: 0x84},
+	{value: 0x8132, lo: 0xa0, hi: 0xb1},
+	// Block 0x54, offset 0x1b7
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0xab, hi: 0xad},
+	// Block 0x55, offset 0x1b9
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0x93, hi: 0x93},
+	// Block 0x56, offset 0x1bb
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8102, lo: 0xb3, hi: 0xb3},
+	// Block 0x57, offset 0x1bd
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0x80, hi: 0x80},
+	// Block 0x58, offset 0x1bf
+	{value: 0x0000, lo: 0x05},
+	{value: 0x8132, lo: 0xb0, hi: 0xb0},
+	{value: 0x8132, lo: 0xb2, hi: 0xb3},
+	{value: 0x812d, lo: 0xb4, hi: 0xb4},
+	{value: 0x8132, lo: 0xb7, hi: 0xb8},
+	{value: 0x8132, lo: 0xbe, hi: 0xbf},
+	// Block 0x59, offset 0x1c5
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8132, lo: 0x81, hi: 0x81},
+	{value: 0x8104, lo: 0xb6, hi: 0xb6},
+	// Block 0x5a, offset 0x1c8
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0xad, hi: 0xad},
+	// Block 0x5b, offset 0x1ca
+	{value: 0x0000, lo: 0x06},
+	{value: 0xe500, lo: 0x80, hi: 0x80},
+	{value: 0xc600, lo: 0x81, hi: 0x9b},
+	{value: 0xe500, lo: 0x9c, hi: 0x9c},
+	{value: 0xc600, lo: 0x9d, hi: 0xb7},
+	{value: 0xe500, lo: 0xb8, hi: 0xb8},
+	{value: 0xc600, lo: 0xb9, hi: 0xbf},
+	// Block 0x5c, offset 0x1d1
+	{value: 0x0000, lo: 0x05},
+	{value: 0xc600, lo: 0x80, hi: 0x93},
+	{value: 0xe500, lo: 0x94, hi: 0x94},
+	{value: 0xc600, lo: 0x95, hi: 0xaf},
+	{value: 0xe500, lo: 0xb0, hi: 0xb0},
+	{value: 0xc600, lo: 0xb1, hi: 0xbf},
+	// Block 0x5d, offset 0x1d7
+	{value: 0x0000, lo: 0x05},
+	{value: 0xc600, lo: 0x80, hi: 0x8b},
+	{value: 0xe500, lo: 0x8c, hi: 0x8c},
+	{value: 0xc600, lo: 0x8d, hi: 0xa7},
+	{value: 0xe500, lo: 0xa8, hi: 0xa8},
+	{value: 0xc600, lo: 0xa9, hi: 0xbf},
+	// Block 0x5e, offset 0x1dd
+	{value: 0x0000, lo: 0x07},
+	{value: 0xc600, lo: 0x80, hi: 0x83},
+	{value: 0xe500, lo: 0x84, hi: 0x84},
+	{value: 0xc600, lo: 0x85, hi: 0x9f},
+	{value: 0xe500, lo: 0xa0, hi: 0xa0},
+	{value: 0xc600, lo: 0xa1, hi: 0xbb},
+	{value: 0xe500, lo: 0xbc, hi: 0xbc},
+	{value: 0xc600, lo: 0xbd, hi: 0xbf},
+	// Block 0x5f, offset 0x1e5
+	{value: 0x0000, lo: 0x05},
+	{value: 0xc600, lo: 0x80, hi: 0x97},
+	{value: 0xe500, lo: 0x98, hi: 0x98},
+	{value: 0xc600, lo: 0x99, hi: 0xb3},
+	{value: 0xe500, lo: 0xb4, hi: 0xb4},
+	{value: 0xc600, lo: 0xb5, hi: 0xbf},
+	// Block 0x60, offset 0x1eb
+	{value: 0x0000, lo: 0x05},
+	{value: 0xc600, lo: 0x80, hi: 0x8f},
+	{value: 0xe500, lo: 0x90, hi: 0x90},
+	{value: 0xc600, lo: 0x91, hi: 0xab},
+	{value: 0xe500, lo: 0xac, hi: 0xac},
+	{value: 0xc600, lo: 0xad, hi: 0xbf},
+	// Block 0x61, offset 0x1f1
+	{value: 0x0000, lo: 0x05},
+	{value: 0xc600, lo: 0x80, hi: 0x87},
+	{value: 0xe500, lo: 0x88, hi: 0x88},
+	{value: 0xc600, lo: 0x89, hi: 0xa3},
+	{value: 0xe500, lo: 0xa4, hi: 0xa4},
+	{value: 0xc600, lo: 0xa5, hi: 0xbf},
+	// Block 0x62, offset 0x1f7
+	{value: 0x0000, lo: 0x03},
+	{value: 0xc600, lo: 0x80, hi: 0x87},
+	{value: 0xe500, lo: 0x88, hi: 0x88},
+	{value: 0xc600, lo: 0x89, hi: 0xa3},
+	// Block 0x63, offset 0x1fb
+	{value: 0x0006, lo: 0x0d},
+	{value: 0x4426, lo: 0x9d, hi: 0x9d},
+	{value: 0x8115, lo: 0x9e, hi: 0x9e},
+	{value: 0x4498, lo: 0x9f, hi: 0x9f},
+	{value: 0x4486, lo: 0xaa, hi: 0xab},
+	{value: 0x458a, lo: 0xac, hi: 0xac},
+	{value: 0x4592, lo: 0xad, hi: 0xad},
+	{value: 0x43de, lo: 0xae, hi: 0xb1},
+	{value: 0x43fc, lo: 0xb2, hi: 0xb4},
+	{value: 0x4414, lo: 0xb5, hi: 0xb6},
+	{value: 0x4420, lo: 0xb8, hi: 0xb8},
+	{value: 0x442c, lo: 0xb9, hi: 0xbb},
+	{value: 0x4444, lo: 0xbc, hi: 0xbc},
+	{value: 0x444a, lo: 0xbe, hi: 0xbe},
+	// Block 0x64, offset 0x209
+	{value: 0x0006, lo: 0x08},
+	{value: 0x4450, lo: 0x80, hi: 0x81},
+	{value: 0x445c, lo: 0x83, hi: 0x84},
+	{value: 0x446e, lo: 0x86, hi: 0x89},
+	{value: 0x4492, lo: 0x8a, hi: 0x8a},
+	{value: 0x440e, lo: 0x8b, hi: 0x8b},
+	{value: 0x43f6, lo: 0x8c, hi: 0x8c},
+	{value: 0x443e, lo: 0x8d, hi: 0x8d},
+	{value: 0x4468, lo: 0x8e, hi: 0x8e},
+	// Block 0x65, offset 0x212
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8100, lo: 0xa4, hi: 0xa5},
+	{value: 0x8100, lo: 0xb0, hi: 0xb1},
+	// Block 0x66, offset 0x215
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8100, lo: 0x9b, hi: 0x9d},
+	{value: 0x8200, lo: 0x9e, hi: 0xa3},
+	// Block 0x67, offset 0x218
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8100, lo: 0x90, hi: 0x90},
+	// Block 0x68, offset 0x21a
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8100, lo: 0x99, hi: 0x99},
+	{value: 0x8200, lo: 0xb2, hi: 0xb4},
+	// Block 0x69, offset 0x21d
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8100, lo: 0xbc, hi: 0xbd},
+	// Block 0x6a, offset 0x21f
+	{value: 0x0000, lo: 0x03},
+	{value: 0x8132, lo: 0xa0, hi: 0xa6},
+	{value: 0x812d, lo: 0xa7, hi: 0xad},
+	{value: 0x8132, lo: 0xae, hi: 0xaf},
+	// Block 0x6b, offset 0x223
+	{value: 0x0000, lo: 0x04},
+	{value: 0x8100, lo: 0x89, hi: 0x8c},
+	{value: 0x8100, lo: 0xb0, hi: 0xb2},
+	{value: 0x8100, lo: 0xb4, hi: 0xb4},
+	{value: 0x8100, lo: 0xb6, hi: 0xbf},
+	// Block 0x6c, offset 0x228
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8100, lo: 0x81, hi: 0x8c},
+	// Block 0x6d, offset 0x22a
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8100, lo: 0xb5, hi: 0xba},
+	// Block 0x6e, offset 0x22c
+	{value: 0x0000, lo: 0x04},
+	{value: 0x4a81, lo: 0x9e, hi: 0x9f},
+	{value: 0x4a81, lo: 0xa3, hi: 0xa3},
+	{value: 0x4a81, lo: 0xa5, hi: 0xa6},
+	{value: 0x4a81, lo: 0xaa, hi: 0xaf},
+	// Block 0x6f, offset 0x231
+	{value: 0x0000, lo: 0x05},
+	{value: 0x4a81, lo: 0x82, hi: 0x87},
+	{value: 0x4a81, lo: 0x8a, hi: 0x8f},
+	{value: 0x4a81, lo: 0x92, hi: 0x97},
+	{value: 0x4a81, lo: 0x9a, hi: 0x9c},
+	{value: 0x8100, lo: 0xa3, hi: 0xa3},
+	// Block 0x70, offset 0x237
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0xbd, hi: 0xbd},
+	// Block 0x71, offset 0x239
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0xa0, hi: 0xa0},
+	// Block 0x72, offset 0x23b
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0xb6, hi: 0xba},
+	// Block 0x73, offset 0x23d
+	{value: 0x002c, lo: 0x05},
+	{value: 0x812d, lo: 0x8d, hi: 0x8d},
+	{value: 0x8132, lo: 0x8f, hi: 0x8f},
+	{value: 0x8132, lo: 0xb8, hi: 0xb8},
+	{value: 0x8101, lo: 0xb9, hi: 0xba},
+	{value: 0x8104, lo: 0xbf, hi: 0xbf},
+	// Block 0x74, offset 0x243
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8132, lo: 0xa5, hi: 0xa5},
+	{value: 0x812d, lo: 0xa6, hi: 0xa6},
+	// Block 0x75, offset 0x246
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x86, hi: 0x86},
+	{value: 0x8104, lo: 0xbf, hi: 0xbf},
+	// Block 0x76, offset 0x249
+	{value: 0x17fe, lo: 0x07},
+	{value: 0xa000, lo: 0x99, hi: 0x99},
+	{value: 0x4238, lo: 0x9a, hi: 0x9a},
+	{value: 0xa000, lo: 0x9b, hi: 0x9b},
+	{value: 0x4242, lo: 0x9c, hi: 0x9c},
+	{value: 0xa000, lo: 0xa5, hi: 0xa5},
+	{value: 0x424c, lo: 0xab, hi: 0xab},
+	{value: 0x8104, lo: 0xb9, hi: 0xba},
+	// Block 0x77, offset 0x251
+	{value: 0x0000, lo: 0x06},
+	{value: 0x8132, lo: 0x80, hi: 0x82},
+	{value: 0x9900, lo: 0xa7, hi: 0xa7},
+	{value: 0x2d7e, lo: 0xae, hi: 0xae},
+	{value: 0x2d88, lo: 0xaf, hi: 0xaf},
+	{value: 0xa000, lo: 0xb1, hi: 0xb2},
+	{value: 0x8104, lo: 0xb3, hi: 0xb4},
+	// Block 0x78, offset 0x258
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x80, hi: 0x80},
+	{value: 0x8102, lo: 0x8a, hi: 0x8a},
+	// Block 0x79, offset 0x25b
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0xb5, hi: 0xb5},
+	{value: 0x8102, lo: 0xb6, hi: 0xb6},
+	// Block 0x7a, offset 0x25e
+	{value: 0x0002, lo: 0x01},
+	{value: 0x8102, lo: 0xa9, hi: 0xaa},
+	// Block 0x7b, offset 0x260
+	{value: 0x0000, lo: 0x07},
+	{value: 0xa000, lo: 0x87, hi: 0x87},
+	{value: 0x2d92, lo: 0x8b, hi: 0x8b},
+	{value: 0x2d9c, lo: 0x8c, hi: 0x8c},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x97, hi: 0x97},
+	{value: 0x8132, lo: 0xa6, hi: 0xac},
+	{value: 0x8132, lo: 0xb0, hi: 0xb4},
+	// Block 0x7c, offset 0x268
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x82, hi: 0x82},
+	{value: 0x8102, lo: 0x86, hi: 0x86},
+	// Block 0x7d, offset 0x26b
+	{value: 0x6b5a, lo: 0x06},
+	{value: 0x9900, lo: 0xb0, hi: 0xb0},
+	{value: 0xa000, lo: 0xb9, hi: 0xb9},
+	{value: 0x9900, lo: 0xba, hi: 0xba},
+	{value: 0x2db0, lo: 0xbb, hi: 0xbb},
+	{value: 0x2da6, lo: 0xbc, hi: 0xbd},
+	{value: 0x2dba, lo: 0xbe, hi: 0xbe},
+	// Block 0x7e, offset 0x272
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x82, hi: 0x82},
+	{value: 0x8102, lo: 0x83, hi: 0x83},
+	// Block 0x7f, offset 0x275
+	{value: 0x0000, lo: 0x05},
+	{value: 0x9900, lo: 0xaf, hi: 0xaf},
+	{value: 0xa000, lo: 0xb8, hi: 0xb9},
+	{value: 0x2dc4, lo: 0xba, hi: 0xba},
+	{value: 0x2dce, lo: 0xbb, hi: 0xbb},
+	{value: 0x8104, lo: 0xbf, hi: 0xbf},
+	// Block 0x80, offset 0x27b
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8102, lo: 0x80, hi: 0x80},
+	// Block 0x81, offset 0x27d
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0xb6, hi: 0xb6},
+	{value: 0x8102, lo: 0xb7, hi: 0xb7},
+	// Block 0x82, offset 0x280
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0xab, hi: 0xab},
+	// Block 0x83, offset 0x282
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8101, lo: 0xb0, hi: 0xb4},
+	// Block 0x84, offset 0x284
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0xb0, hi: 0xb6},
+	// Block 0x85, offset 0x286
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8101, lo: 0x9e, hi: 0x9e},
+	// Block 0x86, offset 0x288
+	{value: 0x0000, lo: 0x0c},
+	{value: 0x4662, lo: 0x9e, hi: 0x9e},
+	{value: 0x466c, lo: 0x9f, hi: 0x9f},
+	{value: 0x46a0, lo: 0xa0, hi: 0xa0},
+	{value: 0x46ae, lo: 0xa1, hi: 0xa1},
+	{value: 0x46bc, lo: 0xa2, hi: 0xa2},
+	{value: 0x46ca, lo: 0xa3, hi: 0xa3},
+	{value: 0x46d8, lo: 0xa4, hi: 0xa4},
+	{value: 0x812b, lo: 0xa5, hi: 0xa6},
+	{value: 0x8101, lo: 0xa7, hi: 0xa9},
+	{value: 0x8130, lo: 0xad, hi: 0xad},
+	{value: 0x812b, lo: 0xae, hi: 0xb2},
+	{value: 0x812d, lo: 0xbb, hi: 0xbf},
+	// Block 0x87, offset 0x295
+	{value: 0x0000, lo: 0x09},
+	{value: 0x812d, lo: 0x80, hi: 0x82},
+	{value: 0x8132, lo: 0x85, hi: 0x89},
+	{value: 0x812d, lo: 0x8a, hi: 0x8b},
+	{value: 0x8132, lo: 0xaa, hi: 0xad},
+	{value: 0x4676, lo: 0xbb, hi: 0xbb},
+	{value: 0x4680, lo: 0xbc, hi: 0xbc},
+	{value: 0x46e6, lo: 0xbd, hi: 0xbd},
+	{value: 0x4702, lo: 0xbe, hi: 0xbe},
+	{value: 0x46f4, lo: 0xbf, hi: 0xbf},
+	// Block 0x88, offset 0x29f
+	{value: 0x0000, lo: 0x01},
+	{value: 0x4710, lo: 0x80, hi: 0x80},
+	// Block 0x89, offset 0x2a1
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0x82, hi: 0x84},
+	// Block 0x8a, offset 0x2a3
+	{value: 0x0000, lo: 0x05},
+	{value: 0x8132, lo: 0x80, hi: 0x86},
+	{value: 0x8132, lo: 0x88, hi: 0x98},
+	{value: 0x8132, lo: 0x9b, hi: 0xa1},
+	{value: 0x8132, lo: 0xa3, hi: 0xa4},
+	{value: 0x8132, lo: 0xa6, hi: 0xaa},
+	// Block 0x8b, offset 0x2a9
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0x90, hi: 0x96},
+	// Block 0x8c, offset 0x2ab
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8132, lo: 0x84, hi: 0x89},
+	{value: 0x8102, lo: 0x8a, hi: 0x8a},
+	// Block 0x8d, offset 0x2ae
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8100, lo: 0x93, hi: 0x93},
+}
+
+// lookup returns the trie value for the first UTF-8 encoding in s and
+// the width in bytes of this encoding. The size will be 0 if s does not
+// hold enough bytes to complete the encoding. len(s) must be greater than 0.
+func (t *nfkcTrie) lookup(s []byte) (v uint16, sz int) {
+	c0 := s[0]
+	switch {
+	case c0 < 0x80: // is ASCII
+		return nfkcValues[c0], 1
+	case c0 < 0xC2:
+		return 0, 1 // Illegal UTF-8: not a starter, not ASCII.
+	case c0 < 0xE0: // 2-byte UTF-8
+		if len(s) < 2 {
+			return 0, 0
+		}
+		i := nfkcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c1), 2
+	case c0 < 0xF0: // 3-byte UTF-8
+		if len(s) < 3 {
+			return 0, 0
+		}
+		i := nfkcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = nfkcIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c2), 3
+	case c0 < 0xF8: // 4-byte UTF-8
+		if len(s) < 4 {
+			return 0, 0
+		}
+		i := nfkcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = nfkcIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		o = uint32(i)<<6 + uint32(c2)
+		i = nfkcIndex[o]
+		c3 := s[3]
+		if c3 < 0x80 || 0xC0 <= c3 {
+			return 0, 3 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c3), 4
+	}
+	// Illegal rune
+	return 0, 1
+}
+
+// lookupUnsafe returns the trie value for the first UTF-8 encoding in s.
+// s must start with a full and valid UTF-8 encoded rune.
+func (t *nfkcTrie) lookupUnsafe(s []byte) uint16 {
+	c0 := s[0]
+	if c0 < 0x80 { // is ASCII
+		return nfkcValues[c0]
+	}
+	i := nfkcIndex[c0]
+	if c0 < 0xE0 { // 2-byte UTF-8
+		return t.lookupValue(uint32(i), s[1])
+	}
+	i = nfkcIndex[uint32(i)<<6+uint32(s[1])]
+	if c0 < 0xF0 { // 3-byte UTF-8
+		return t.lookupValue(uint32(i), s[2])
+	}
+	i = nfkcIndex[uint32(i)<<6+uint32(s[2])]
+	if c0 < 0xF8 { // 4-byte UTF-8
+		return t.lookupValue(uint32(i), s[3])
+	}
+	return 0
+}
+
+// lookupString returns the trie value for the first UTF-8 encoding in s and
+// the width in bytes of this encoding. The size will be 0 if s does not
+// hold enough bytes to complete the encoding. len(s) must be greater than 0.
+func (t *nfkcTrie) lookupString(s string) (v uint16, sz int) {
+	c0 := s[0]
+	switch {
+	case c0 < 0x80: // is ASCII
+		return nfkcValues[c0], 1
+	case c0 < 0xC2:
+		return 0, 1 // Illegal UTF-8: not a starter, not ASCII.
+	case c0 < 0xE0: // 2-byte UTF-8
+		if len(s) < 2 {
+			return 0, 0
+		}
+		i := nfkcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c1), 2
+	case c0 < 0xF0: // 3-byte UTF-8
+		if len(s) < 3 {
+			return 0, 0
+		}
+		i := nfkcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = nfkcIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c2), 3
+	case c0 < 0xF8: // 4-byte UTF-8
+		if len(s) < 4 {
+			return 0, 0
+		}
+		i := nfkcIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = nfkcIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		o = uint32(i)<<6 + uint32(c2)
+		i = nfkcIndex[o]
+		c3 := s[3]
+		if c3 < 0x80 || 0xC0 <= c3 {
+			return 0, 3 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c3), 4
+	}
+	// Illegal rune
+	return 0, 1
+}
+
+// lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s.
+// s must start with a full and valid UTF-8 encoded rune.
+func (t *nfkcTrie) lookupStringUnsafe(s string) uint16 {
+	c0 := s[0]
+	if c0 < 0x80 { // is ASCII
+		return nfkcValues[c0]
+	}
+	i := nfkcIndex[c0]
+	if c0 < 0xE0 { // 2-byte UTF-8
+		return t.lookupValue(uint32(i), s[1])
+	}
+	i = nfkcIndex[uint32(i)<<6+uint32(s[1])]
+	if c0 < 0xF0 { // 3-byte UTF-8
+		return t.lookupValue(uint32(i), s[2])
+	}
+	i = nfkcIndex[uint32(i)<<6+uint32(s[2])]
+	if c0 < 0xF8 { // 4-byte UTF-8
+		return t.lookupValue(uint32(i), s[3])
+	}
+	return 0
+}
+
+// nfkcTrie. Total size: 16994 bytes (16.60 KiB). Checksum: 146925fc21092b17.
+type nfkcTrie struct{}
+
+func newNfkcTrie(i int) *nfkcTrie {
+	return &nfkcTrie{}
+}
+
+// lookupValue determines the type of block n and looks up the value for b.
+func (t *nfkcTrie) lookupValue(n uint32, b byte) uint16 {
+	switch {
+	case n < 90:
+		return uint16(nfkcValues[n<<6+uint32(b)])
+	default:
+		n -= 90
+		return uint16(nfkcSparse.lookup(n, b))
+	}
+}
+
+// nfkcValues: 92 blocks, 5888 entries, 11776 bytes
+// The third block is the zero block.
+var nfkcValues = [5888]uint16{
+	// Block 0x0, offset 0x0
+	0x3c: 0xa000, 0x3d: 0xa000, 0x3e: 0xa000,
+	// Block 0x1, offset 0x40
+	0x41: 0xa000, 0x42: 0xa000, 0x43: 0xa000, 0x44: 0xa000, 0x45: 0xa000,
+	0x46: 0xa000, 0x47: 0xa000, 0x48: 0xa000, 0x49: 0xa000, 0x4a: 0xa000, 0x4b: 0xa000,
+	0x4c: 0xa000, 0x4d: 0xa000, 0x4e: 0xa000, 0x4f: 0xa000, 0x50: 0xa000,
+	0x52: 0xa000, 0x53: 0xa000, 0x54: 0xa000, 0x55: 0xa000, 0x56: 0xa000, 0x57: 0xa000,
+	0x58: 0xa000, 0x59: 0xa000, 0x5a: 0xa000,
+	0x61: 0xa000, 0x62: 0xa000, 0x63: 0xa000,
+	0x64: 0xa000, 0x65: 0xa000, 0x66: 0xa000, 0x67: 0xa000, 0x68: 0xa000, 0x69: 0xa000,
+	0x6a: 0xa000, 0x6b: 0xa000, 0x6c: 0xa000, 0x6d: 0xa000, 0x6e: 0xa000, 0x6f: 0xa000,
+	0x70: 0xa000, 0x72: 0xa000, 0x73: 0xa000, 0x74: 0xa000, 0x75: 0xa000,
+	0x76: 0xa000, 0x77: 0xa000, 0x78: 0xa000, 0x79: 0xa000, 0x7a: 0xa000,
+	// Block 0x2, offset 0x80
+	// Block 0x3, offset 0xc0
+	0xc0: 0x2f6f, 0xc1: 0x2f74, 0xc2: 0x471e, 0xc3: 0x2f79, 0xc4: 0x472d, 0xc5: 0x4732,
+	0xc6: 0xa000, 0xc7: 0x473c, 0xc8: 0x2fe2, 0xc9: 0x2fe7, 0xca: 0x4741, 0xcb: 0x2ffb,
+	0xcc: 0x306e, 0xcd: 0x3073, 0xce: 0x3078, 0xcf: 0x4755, 0xd1: 0x3104,
+	0xd2: 0x3127, 0xd3: 0x312c, 0xd4: 0x475f, 0xd5: 0x4764, 0xd6: 0x4773,
+	0xd8: 0xa000, 0xd9: 0x31b3, 0xda: 0x31b8, 0xdb: 0x31bd, 0xdc: 0x47a5, 0xdd: 0x3235,
+	0xe0: 0x327b, 0xe1: 0x3280, 0xe2: 0x47af, 0xe3: 0x3285,
+	0xe4: 0x47be, 0xe5: 0x47c3, 0xe6: 0xa000, 0xe7: 0x47cd, 0xe8: 0x32ee, 0xe9: 0x32f3,
+	0xea: 0x47d2, 0xeb: 0x3307, 0xec: 0x337f, 0xed: 0x3384, 0xee: 0x3389, 0xef: 0x47e6,
+	0xf1: 0x3415, 0xf2: 0x3438, 0xf3: 0x343d, 0xf4: 0x47f0, 0xf5: 0x47f5,
+	0xf6: 0x4804, 0xf8: 0xa000, 0xf9: 0x34c9, 0xfa: 0x34ce, 0xfb: 0x34d3,
+	0xfc: 0x4836, 0xfd: 0x3550, 0xff: 0x3569,
+	// Block 0x4, offset 0x100
+	0x100: 0x2f7e, 0x101: 0x328a, 0x102: 0x4723, 0x103: 0x47b4, 0x104: 0x2f9c, 0x105: 0x32a8,
+	0x106: 0x2fb0, 0x107: 0x32bc, 0x108: 0x2fb5, 0x109: 0x32c1, 0x10a: 0x2fba, 0x10b: 0x32c6,
+	0x10c: 0x2fbf, 0x10d: 0x32cb, 0x10e: 0x2fc9, 0x10f: 0x32d5,
+	0x112: 0x4746, 0x113: 0x47d7, 0x114: 0x2ff1, 0x115: 0x32fd, 0x116: 0x2ff6, 0x117: 0x3302,
+	0x118: 0x3014, 0x119: 0x3320, 0x11a: 0x3005, 0x11b: 0x3311, 0x11c: 0x302d, 0x11d: 0x3339,
+	0x11e: 0x3037, 0x11f: 0x3343, 0x120: 0x303c, 0x121: 0x3348, 0x122: 0x3046, 0x123: 0x3352,
+	0x124: 0x304b, 0x125: 0x3357, 0x128: 0x307d, 0x129: 0x338e,
+	0x12a: 0x3082, 0x12b: 0x3393, 0x12c: 0x3087, 0x12d: 0x3398, 0x12e: 0x30aa, 0x12f: 0x33b6,
+	0x130: 0x308c, 0x132: 0x195d, 0x133: 0x19e7, 0x134: 0x30b4, 0x135: 0x33c0,
+	0x136: 0x30c8, 0x137: 0x33d9, 0x139: 0x30d2, 0x13a: 0x33e3, 0x13b: 0x30dc,
+	0x13c: 0x33ed, 0x13d: 0x30d7, 0x13e: 0x33e8, 0x13f: 0x1bac,
+	// Block 0x5, offset 0x140
+	0x140: 0x1c34, 0x143: 0x30ff, 0x144: 0x3410, 0x145: 0x3118,
+	0x146: 0x3429, 0x147: 0x310e, 0x148: 0x341f, 0x149: 0x1c5c,
+	0x14c: 0x4769, 0x14d: 0x47fa, 0x14e: 0x3131, 0x14f: 0x3442, 0x150: 0x313b, 0x151: 0x344c,
+	0x154: 0x3159, 0x155: 0x346a, 0x156: 0x3172, 0x157: 0x3483,
+	0x158: 0x3163, 0x159: 0x3474, 0x15a: 0x478c, 0x15b: 0x481d, 0x15c: 0x317c, 0x15d: 0x348d,
+	0x15e: 0x318b, 0x15f: 0x349c, 0x160: 0x4791, 0x161: 0x4822, 0x162: 0x31a4, 0x163: 0x34ba,
+	0x164: 0x3195, 0x165: 0x34ab, 0x168: 0x479b, 0x169: 0x482c,
+	0x16a: 0x47a0, 0x16b: 0x4831, 0x16c: 0x31c2, 0x16d: 0x34d8, 0x16e: 0x31cc, 0x16f: 0x34e2,
+	0x170: 0x31d1, 0x171: 0x34e7, 0x172: 0x31ef, 0x173: 0x3505, 0x174: 0x3212, 0x175: 0x3528,
+	0x176: 0x323a, 0x177: 0x3555, 0x178: 0x324e, 0x179: 0x325d, 0x17a: 0x357d, 0x17b: 0x3267,
+	0x17c: 0x3587, 0x17d: 0x326c, 0x17e: 0x358c, 0x17f: 0x00a7,
+	// Block 0x6, offset 0x180
+	0x184: 0x2dee, 0x185: 0x2df4,
+	0x186: 0x2dfa, 0x187: 0x1972, 0x188: 0x1975, 0x189: 0x1a08, 0x18a: 0x1987, 0x18b: 0x198a,
+	0x18c: 0x1a3e, 0x18d: 0x2f88, 0x18e: 0x3294, 0x18f: 0x3096, 0x190: 0x33a2, 0x191: 0x3140,
+	0x192: 0x3451, 0x193: 0x31d6, 0x194: 0x34ec, 0x195: 0x39cf, 0x196: 0x3b5e, 0x197: 0x39c8,
+	0x198: 0x3b57, 0x199: 0x39d6, 0x19a: 0x3b65, 0x19b: 0x39c1, 0x19c: 0x3b50,
+	0x19e: 0x38b0, 0x19f: 0x3a3f, 0x1a0: 0x38a9, 0x1a1: 0x3a38, 0x1a2: 0x35b3, 0x1a3: 0x35c5,
+	0x1a6: 0x3041, 0x1a7: 0x334d, 0x1a8: 0x30be, 0x1a9: 0x33cf,
+	0x1aa: 0x4782, 0x1ab: 0x4813, 0x1ac: 0x3990, 0x1ad: 0x3b1f, 0x1ae: 0x35d7, 0x1af: 0x35dd,
+	0x1b0: 0x33c5, 0x1b1: 0x1942, 0x1b2: 0x1945, 0x1b3: 0x19cf, 0x1b4: 0x3028, 0x1b5: 0x3334,
+	0x1b8: 0x30fa, 0x1b9: 0x340b, 0x1ba: 0x38b7, 0x1bb: 0x3a46,
+	0x1bc: 0x35ad, 0x1bd: 0x35bf, 0x1be: 0x35b9, 0x1bf: 0x35cb,
+	// Block 0x7, offset 0x1c0
+	0x1c0: 0x2f8d, 0x1c1: 0x3299, 0x1c2: 0x2f92, 0x1c3: 0x329e, 0x1c4: 0x300a, 0x1c5: 0x3316,
+	0x1c6: 0x300f, 0x1c7: 0x331b, 0x1c8: 0x309b, 0x1c9: 0x33a7, 0x1ca: 0x30a0, 0x1cb: 0x33ac,
+	0x1cc: 0x3145, 0x1cd: 0x3456, 0x1ce: 0x314a, 0x1cf: 0x345b, 0x1d0: 0x3168, 0x1d1: 0x3479,
+	0x1d2: 0x316d, 0x1d3: 0x347e, 0x1d4: 0x31db, 0x1d5: 0x34f1, 0x1d6: 0x31e0, 0x1d7: 0x34f6,
+	0x1d8: 0x3186, 0x1d9: 0x3497, 0x1da: 0x319f, 0x1db: 0x34b5,
+	0x1de: 0x305a, 0x1df: 0x3366,
+	0x1e6: 0x4728, 0x1e7: 0x47b9, 0x1e8: 0x4750, 0x1e9: 0x47e1,
+	0x1ea: 0x395f, 0x1eb: 0x3aee, 0x1ec: 0x393c, 0x1ed: 0x3acb, 0x1ee: 0x476e, 0x1ef: 0x47ff,
+	0x1f0: 0x3958, 0x1f1: 0x3ae7, 0x1f2: 0x3244, 0x1f3: 0x355f,
+	// Block 0x8, offset 0x200
+	0x200: 0x9932, 0x201: 0x9932, 0x202: 0x9932, 0x203: 0x9932, 0x204: 0x9932, 0x205: 0x8132,
+	0x206: 0x9932, 0x207: 0x9932, 0x208: 0x9932, 0x209: 0x9932, 0x20a: 0x9932, 0x20b: 0x9932,
+	0x20c: 0x9932, 0x20d: 0x8132, 0x20e: 0x8132, 0x20f: 0x9932, 0x210: 0x8132, 0x211: 0x9932,
+	0x212: 0x8132, 0x213: 0x9932, 0x214: 0x9932, 0x215: 0x8133, 0x216: 0x812d, 0x217: 0x812d,
+	0x218: 0x812d, 0x219: 0x812d, 0x21a: 0x8133, 0x21b: 0x992b, 0x21c: 0x812d, 0x21d: 0x812d,
+	0x21e: 0x812d, 0x21f: 0x812d, 0x220: 0x812d, 0x221: 0x8129, 0x222: 0x8129, 0x223: 0x992d,
+	0x224: 0x992d, 0x225: 0x992d, 0x226: 0x992d, 0x227: 0x9929, 0x228: 0x9929, 0x229: 0x812d,
+	0x22a: 0x812d, 0x22b: 0x812d, 0x22c: 0x812d, 0x22d: 0x992d, 0x22e: 0x992d, 0x22f: 0x812d,
+	0x230: 0x992d, 0x231: 0x992d, 0x232: 0x812d, 0x233: 0x812d, 0x234: 0x8101, 0x235: 0x8101,
+	0x236: 0x8101, 0x237: 0x8101, 0x238: 0x9901, 0x239: 0x812d, 0x23a: 0x812d, 0x23b: 0x812d,
+	0x23c: 0x812d, 0x23d: 0x8132, 0x23e: 0x8132, 0x23f: 0x8132,
+	// Block 0x9, offset 0x240
+	0x240: 0x4a44, 0x241: 0x4a49, 0x242: 0x9932, 0x243: 0x4a4e, 0x244: 0x4a53, 0x245: 0x9936,
+	0x246: 0x8132, 0x247: 0x812d, 0x248: 0x812d, 0x249: 0x812d, 0x24a: 0x8132, 0x24b: 0x8132,
+	0x24c: 0x8132, 0x24d: 0x812d, 0x24e: 0x812d, 0x250: 0x8132, 0x251: 0x8132,
+	0x252: 0x8132, 0x253: 0x812d, 0x254: 0x812d, 0x255: 0x812d, 0x256: 0x812d, 0x257: 0x8132,
+	0x258: 0x8133, 0x259: 0x812d, 0x25a: 0x812d, 0x25b: 0x8132, 0x25c: 0x8134, 0x25d: 0x8135,
+	0x25e: 0x8135, 0x25f: 0x8134, 0x260: 0x8135, 0x261: 0x8135, 0x262: 0x8134, 0x263: 0x8132,
+	0x264: 0x8132, 0x265: 0x8132, 0x266: 0x8132, 0x267: 0x8132, 0x268: 0x8132, 0x269: 0x8132,
+	0x26a: 0x8132, 0x26b: 0x8132, 0x26c: 0x8132, 0x26d: 0x8132, 0x26e: 0x8132, 0x26f: 0x8132,
+	0x274: 0x0170,
+	0x27a: 0x42a5,
+	0x27e: 0x0037,
+	// Block 0xa, offset 0x280
+	0x284: 0x425a, 0x285: 0x4511,
+	0x286: 0x35e9, 0x287: 0x00ce, 0x288: 0x3607, 0x289: 0x3613, 0x28a: 0x3625,
+	0x28c: 0x3643, 0x28e: 0x3655, 0x28f: 0x3673, 0x290: 0x3e08, 0x291: 0xa000,
+	0x295: 0xa000, 0x297: 0xa000,
+	0x299: 0xa000,
+	0x29f: 0xa000, 0x2a1: 0xa000,
+	0x2a5: 0xa000, 0x2a9: 0xa000,
+	0x2aa: 0x3637, 0x2ab: 0x3667, 0x2ac: 0x4894, 0x2ad: 0x3697, 0x2ae: 0x48be, 0x2af: 0x36a9,
+	0x2b0: 0x3e70, 0x2b1: 0xa000, 0x2b5: 0xa000,
+	0x2b7: 0xa000, 0x2b9: 0xa000,
+	0x2bf: 0xa000,
+	// Block 0xb, offset 0x2c0
+	0x2c1: 0xa000, 0x2c5: 0xa000,
+	0x2c9: 0xa000, 0x2ca: 0x48d6, 0x2cb: 0x48f4,
+	0x2cc: 0x36c7, 0x2cd: 0x36df, 0x2ce: 0x490c, 0x2d0: 0x01be, 0x2d1: 0x01d0,
+	0x2d2: 0x01ac, 0x2d3: 0x43a2, 0x2d4: 0x43a8, 0x2d5: 0x01fa, 0x2d6: 0x01e8,
+	0x2f0: 0x01d6, 0x2f1: 0x01eb, 0x2f2: 0x01ee, 0x2f4: 0x0188, 0x2f5: 0x01c7,
+	0x2f9: 0x01a6,
+	// Block 0xc, offset 0x300
+	0x300: 0x3721, 0x301: 0x372d, 0x303: 0x371b,
+	0x306: 0xa000, 0x307: 0x3709,
+	0x30c: 0x375d, 0x30d: 0x3745, 0x30e: 0x376f, 0x310: 0xa000,
+	0x313: 0xa000, 0x315: 0xa000, 0x316: 0xa000, 0x317: 0xa000,
+	0x318: 0xa000, 0x319: 0x3751, 0x31a: 0xa000,
+	0x31e: 0xa000, 0x323: 0xa000,
+	0x327: 0xa000,
+	0x32b: 0xa000, 0x32d: 0xa000,
+	0x330: 0xa000, 0x333: 0xa000, 0x335: 0xa000,
+	0x336: 0xa000, 0x337: 0xa000, 0x338: 0xa000, 0x339: 0x37d5, 0x33a: 0xa000,
+	0x33e: 0xa000,
+	// Block 0xd, offset 0x340
+	0x341: 0x3733, 0x342: 0x37b7,
+	0x350: 0x370f, 0x351: 0x3793,
+	0x352: 0x3715, 0x353: 0x3799, 0x356: 0x3727, 0x357: 0x37ab,
+	0x358: 0xa000, 0x359: 0xa000, 0x35a: 0x3829, 0x35b: 0x382f, 0x35c: 0x3739, 0x35d: 0x37bd,
+	0x35e: 0x373f, 0x35f: 0x37c3, 0x362: 0x374b, 0x363: 0x37cf,
+	0x364: 0x3757, 0x365: 0x37db, 0x366: 0x3763, 0x367: 0x37e7, 0x368: 0xa000, 0x369: 0xa000,
+	0x36a: 0x3835, 0x36b: 0x383b, 0x36c: 0x378d, 0x36d: 0x3811, 0x36e: 0x3769, 0x36f: 0x37ed,
+	0x370: 0x3775, 0x371: 0x37f9, 0x372: 0x377b, 0x373: 0x37ff, 0x374: 0x3781, 0x375: 0x3805,
+	0x378: 0x3787, 0x379: 0x380b,
+	// Block 0xe, offset 0x380
+	0x387: 0x1d61,
+	0x391: 0x812d,
+	0x392: 0x8132, 0x393: 0x8132, 0x394: 0x8132, 0x395: 0x8132, 0x396: 0x812d, 0x397: 0x8132,
+	0x398: 0x8132, 0x399: 0x8132, 0x39a: 0x812e, 0x39b: 0x812d, 0x39c: 0x8132, 0x39d: 0x8132,
+	0x39e: 0x8132, 0x39f: 0x8132, 0x3a0: 0x8132, 0x3a1: 0x8132, 0x3a2: 0x812d, 0x3a3: 0x812d,
+	0x3a4: 0x812d, 0x3a5: 0x812d, 0x3a6: 0x812d, 0x3a7: 0x812d, 0x3a8: 0x8132, 0x3a9: 0x8132,
+	0x3aa: 0x812d, 0x3ab: 0x8132, 0x3ac: 0x8132, 0x3ad: 0x812e, 0x3ae: 0x8131, 0x3af: 0x8132,
+	0x3b0: 0x8105, 0x3b1: 0x8106, 0x3b2: 0x8107, 0x3b3: 0x8108, 0x3b4: 0x8109, 0x3b5: 0x810a,
+	0x3b6: 0x810b, 0x3b7: 0x810c, 0x3b8: 0x810d, 0x3b9: 0x810e, 0x3ba: 0x810e, 0x3bb: 0x810f,
+	0x3bc: 0x8110, 0x3bd: 0x8111, 0x3bf: 0x8112,
+	// Block 0xf, offset 0x3c0
+	0x3c8: 0xa000, 0x3ca: 0xa000, 0x3cb: 0x8116,
+	0x3cc: 0x8117, 0x3cd: 0x8118, 0x3ce: 0x8119, 0x3cf: 0x811a, 0x3d0: 0x811b, 0x3d1: 0x811c,
+	0x3d2: 0x811d, 0x3d3: 0x9932, 0x3d4: 0x9932, 0x3d5: 0x992d, 0x3d6: 0x812d, 0x3d7: 0x8132,
+	0x3d8: 0x8132, 0x3d9: 0x8132, 0x3da: 0x8132, 0x3db: 0x8132, 0x3dc: 0x812d, 0x3dd: 0x8132,
+	0x3de: 0x8132, 0x3df: 0x812d,
+	0x3f0: 0x811e, 0x3f5: 0x1d84,
+	0x3f6: 0x2013, 0x3f7: 0x204f, 0x3f8: 0x204a,
+	// Block 0x10, offset 0x400
+	0x405: 0xa000,
+	0x406: 0x2d26, 0x407: 0xa000, 0x408: 0x2d2e, 0x409: 0xa000, 0x40a: 0x2d36, 0x40b: 0xa000,
+	0x40c: 0x2d3e, 0x40d: 0xa000, 0x40e: 0x2d46, 0x411: 0xa000,
+	0x412: 0x2d4e,
+	0x434: 0x8102, 0x435: 0x9900,
+	0x43a: 0xa000, 0x43b: 0x2d56,
+	0x43c: 0xa000, 0x43d: 0x2d5e, 0x43e: 0xa000, 0x43f: 0xa000,
+	// Block 0x11, offset 0x440
+	0x440: 0x0069, 0x441: 0x006b, 0x442: 0x006f, 0x443: 0x0083, 0x444: 0x00f5, 0x445: 0x00f8,
+	0x446: 0x0413, 0x447: 0x0085, 0x448: 0x0089, 0x449: 0x008b, 0x44a: 0x0104, 0x44b: 0x0107,
+	0x44c: 0x010a, 0x44d: 0x008f, 0x44f: 0x0097, 0x450: 0x009b, 0x451: 0x00e0,
+	0x452: 0x009f, 0x453: 0x00fe, 0x454: 0x0417, 0x455: 0x041b, 0x456: 0x00a1, 0x457: 0x00a9,
+	0x458: 0x00ab, 0x459: 0x0423, 0x45a: 0x012b, 0x45b: 0x00ad, 0x45c: 0x0427, 0x45d: 0x01be,
+	0x45e: 0x01c1, 0x45f: 0x01c4, 0x460: 0x01fa, 0x461: 0x01fd, 0x462: 0x0093, 0x463: 0x00a5,
+	0x464: 0x00ab, 0x465: 0x00ad, 0x466: 0x01be, 0x467: 0x01c1, 0x468: 0x01eb, 0x469: 0x01fa,
+	0x46a: 0x01fd,
+	0x478: 0x020c,
+	// Block 0x12, offset 0x480
+	0x49b: 0x00fb, 0x49c: 0x0087, 0x49d: 0x0101,
+	0x49e: 0x00d4, 0x49f: 0x010a, 0x4a0: 0x008d, 0x4a1: 0x010d, 0x4a2: 0x0110, 0x4a3: 0x0116,
+	0x4a4: 0x011c, 0x4a5: 0x011f, 0x4a6: 0x0122, 0x4a7: 0x042b, 0x4a8: 0x016a, 0x4a9: 0x0128,
+	0x4aa: 0x042f, 0x4ab: 0x016d, 0x4ac: 0x0131, 0x4ad: 0x012e, 0x4ae: 0x0134, 0x4af: 0x0137,
+	0x4b0: 0x013a, 0x4b1: 0x013d, 0x4b2: 0x0140, 0x4b3: 0x014c, 0x4b4: 0x014f, 0x4b5: 0x00ec,
+	0x4b6: 0x0152, 0x4b7: 0x0155, 0x4b8: 0x041f, 0x4b9: 0x0158, 0x4ba: 0x015b, 0x4bb: 0x00b5,
+	0x4bc: 0x015e, 0x4bd: 0x0161, 0x4be: 0x0164, 0x4bf: 0x01d0,
+	// Block 0x13, offset 0x4c0
+	0x4c0: 0x2f97, 0x4c1: 0x32a3, 0x4c2: 0x2fa1, 0x4c3: 0x32ad, 0x4c4: 0x2fa6, 0x4c5: 0x32b2,
+	0x4c6: 0x2fab, 0x4c7: 0x32b7, 0x4c8: 0x38cc, 0x4c9: 0x3a5b, 0x4ca: 0x2fc4, 0x4cb: 0x32d0,
+	0x4cc: 0x2fce, 0x4cd: 0x32da, 0x4ce: 0x2fdd, 0x4cf: 0x32e9, 0x4d0: 0x2fd3, 0x4d1: 0x32df,
+	0x4d2: 0x2fd8, 0x4d3: 0x32e4, 0x4d4: 0x38ef, 0x4d5: 0x3a7e, 0x4d6: 0x38f6, 0x4d7: 0x3a85,
+	0x4d8: 0x3019, 0x4d9: 0x3325, 0x4da: 0x301e, 0x4db: 0x332a, 0x4dc: 0x3904, 0x4dd: 0x3a93,
+	0x4de: 0x3023, 0x4df: 0x332f, 0x4e0: 0x3032, 0x4e1: 0x333e, 0x4e2: 0x3050, 0x4e3: 0x335c,
+	0x4e4: 0x305f, 0x4e5: 0x336b, 0x4e6: 0x3055, 0x4e7: 0x3361, 0x4e8: 0x3064, 0x4e9: 0x3370,
+	0x4ea: 0x3069, 0x4eb: 0x3375, 0x4ec: 0x30af, 0x4ed: 0x33bb, 0x4ee: 0x390b, 0x4ef: 0x3a9a,
+	0x4f0: 0x30b9, 0x4f1: 0x33ca, 0x4f2: 0x30c3, 0x4f3: 0x33d4, 0x4f4: 0x30cd, 0x4f5: 0x33de,
+	0x4f6: 0x475a, 0x4f7: 0x47eb, 0x4f8: 0x3912, 0x4f9: 0x3aa1, 0x4fa: 0x30e6, 0x4fb: 0x33f7,
+	0x4fc: 0x30e1, 0x4fd: 0x33f2, 0x4fe: 0x30eb, 0x4ff: 0x33fc,
+	// Block 0x14, offset 0x500
+	0x500: 0x30f0, 0x501: 0x3401, 0x502: 0x30f5, 0x503: 0x3406, 0x504: 0x3109, 0x505: 0x341a,
+	0x506: 0x3113, 0x507: 0x3424, 0x508: 0x3122, 0x509: 0x3433, 0x50a: 0x311d, 0x50b: 0x342e,
+	0x50c: 0x3935, 0x50d: 0x3ac4, 0x50e: 0x3943, 0x50f: 0x3ad2, 0x510: 0x394a, 0x511: 0x3ad9,
+	0x512: 0x3951, 0x513: 0x3ae0, 0x514: 0x314f, 0x515: 0x3460, 0x516: 0x3154, 0x517: 0x3465,
+	0x518: 0x315e, 0x519: 0x346f, 0x51a: 0x4787, 0x51b: 0x4818, 0x51c: 0x3997, 0x51d: 0x3b26,
+	0x51e: 0x3177, 0x51f: 0x3488, 0x520: 0x3181, 0x521: 0x3492, 0x522: 0x4796, 0x523: 0x4827,
+	0x524: 0x399e, 0x525: 0x3b2d, 0x526: 0x39a5, 0x527: 0x3b34, 0x528: 0x39ac, 0x529: 0x3b3b,
+	0x52a: 0x3190, 0x52b: 0x34a1, 0x52c: 0x319a, 0x52d: 0x34b0, 0x52e: 0x31ae, 0x52f: 0x34c4,
+	0x530: 0x31a9, 0x531: 0x34bf, 0x532: 0x31ea, 0x533: 0x3500, 0x534: 0x31f9, 0x535: 0x350f,
+	0x536: 0x31f4, 0x537: 0x350a, 0x538: 0x39b3, 0x539: 0x3b42, 0x53a: 0x39ba, 0x53b: 0x3b49,
+	0x53c: 0x31fe, 0x53d: 0x3514, 0x53e: 0x3203, 0x53f: 0x3519,
+	// Block 0x15, offset 0x540
+	0x540: 0x3208, 0x541: 0x351e, 0x542: 0x320d, 0x543: 0x3523, 0x544: 0x321c, 0x545: 0x3532,
+	0x546: 0x3217, 0x547: 0x352d, 0x548: 0x3221, 0x549: 0x353c, 0x54a: 0x3226, 0x54b: 0x3541,
+	0x54c: 0x322b, 0x54d: 0x3546, 0x54e: 0x3249, 0x54f: 0x3564, 0x550: 0x3262, 0x551: 0x3582,
+	0x552: 0x3271, 0x553: 0x3591, 0x554: 0x3276, 0x555: 0x3596, 0x556: 0x337a, 0x557: 0x34a6,
+	0x558: 0x3537, 0x559: 0x3573, 0x55a: 0x1be0, 0x55b: 0x42d7,
+	0x560: 0x4737, 0x561: 0x47c8, 0x562: 0x2f83, 0x563: 0x328f,
+	0x564: 0x3878, 0x565: 0x3a07, 0x566: 0x3871, 0x567: 0x3a00, 0x568: 0x3886, 0x569: 0x3a15,
+	0x56a: 0x387f, 0x56b: 0x3a0e, 0x56c: 0x38be, 0x56d: 0x3a4d, 0x56e: 0x3894, 0x56f: 0x3a23,
+	0x570: 0x388d, 0x571: 0x3a1c, 0x572: 0x38a2, 0x573: 0x3a31, 0x574: 0x389b, 0x575: 0x3a2a,
+	0x576: 0x38c5, 0x577: 0x3a54, 0x578: 0x474b, 0x579: 0x47dc, 0x57a: 0x3000, 0x57b: 0x330c,
+	0x57c: 0x2fec, 0x57d: 0x32f8, 0x57e: 0x38da, 0x57f: 0x3a69,
+	// Block 0x16, offset 0x580
+	0x580: 0x38d3, 0x581: 0x3a62, 0x582: 0x38e8, 0x583: 0x3a77, 0x584: 0x38e1, 0x585: 0x3a70,
+	0x586: 0x38fd, 0x587: 0x3a8c, 0x588: 0x3091, 0x589: 0x339d, 0x58a: 0x30a5, 0x58b: 0x33b1,
+	0x58c: 0x477d, 0x58d: 0x480e, 0x58e: 0x3136, 0x58f: 0x3447, 0x590: 0x3920, 0x591: 0x3aaf,
+	0x592: 0x3919, 0x593: 0x3aa8, 0x594: 0x392e, 0x595: 0x3abd, 0x596: 0x3927, 0x597: 0x3ab6,
+	0x598: 0x3989, 0x599: 0x3b18, 0x59a: 0x396d, 0x59b: 0x3afc, 0x59c: 0x3966, 0x59d: 0x3af5,
+	0x59e: 0x397b, 0x59f: 0x3b0a, 0x5a0: 0x3974, 0x5a1: 0x3b03, 0x5a2: 0x3982, 0x5a3: 0x3b11,
+	0x5a4: 0x31e5, 0x5a5: 0x34fb, 0x5a6: 0x31c7, 0x5a7: 0x34dd, 0x5a8: 0x39e4, 0x5a9: 0x3b73,
+	0x5aa: 0x39dd, 0x5ab: 0x3b6c, 0x5ac: 0x39f2, 0x5ad: 0x3b81, 0x5ae: 0x39eb, 0x5af: 0x3b7a,
+	0x5b0: 0x39f9, 0x5b1: 0x3b88, 0x5b2: 0x3230, 0x5b3: 0x354b, 0x5b4: 0x3258, 0x5b5: 0x3578,
+	0x5b6: 0x3253, 0x5b7: 0x356e, 0x5b8: 0x323f, 0x5b9: 0x355a,
+	// Block 0x17, offset 0x5c0
+	0x5c0: 0x489a, 0x5c1: 0x48a0, 0x5c2: 0x49b4, 0x5c3: 0x49cc, 0x5c4: 0x49bc, 0x5c5: 0x49d4,
+	0x5c6: 0x49c4, 0x5c7: 0x49dc, 0x5c8: 0x4840, 0x5c9: 0x4846, 0x5ca: 0x4924, 0x5cb: 0x493c,
+	0x5cc: 0x492c, 0x5cd: 0x4944, 0x5ce: 0x4934, 0x5cf: 0x494c, 0x5d0: 0x48ac, 0x5d1: 0x48b2,
+	0x5d2: 0x3db8, 0x5d3: 0x3dc8, 0x5d4: 0x3dc0, 0x5d5: 0x3dd0,
+	0x5d8: 0x484c, 0x5d9: 0x4852, 0x5da: 0x3ce8, 0x5db: 0x3cf8, 0x5dc: 0x3cf0, 0x5dd: 0x3d00,
+	0x5e0: 0x48c4, 0x5e1: 0x48ca, 0x5e2: 0x49e4, 0x5e3: 0x49fc,
+	0x5e4: 0x49ec, 0x5e5: 0x4a04, 0x5e6: 0x49f4, 0x5e7: 0x4a0c, 0x5e8: 0x4858, 0x5e9: 0x485e,
+	0x5ea: 0x4954, 0x5eb: 0x496c, 0x5ec: 0x495c, 0x5ed: 0x4974, 0x5ee: 0x4964, 0x5ef: 0x497c,
+	0x5f0: 0x48dc, 0x5f1: 0x48e2, 0x5f2: 0x3e18, 0x5f3: 0x3e30, 0x5f4: 0x3e20, 0x5f5: 0x3e38,
+	0x5f6: 0x3e28, 0x5f7: 0x3e40, 0x5f8: 0x4864, 0x5f9: 0x486a, 0x5fa: 0x3d18, 0x5fb: 0x3d30,
+	0x5fc: 0x3d20, 0x5fd: 0x3d38, 0x5fe: 0x3d28, 0x5ff: 0x3d40,
+	// Block 0x18, offset 0x600
+	0x600: 0x48e8, 0x601: 0x48ee, 0x602: 0x3e48, 0x603: 0x3e58, 0x604: 0x3e50, 0x605: 0x3e60,
+	0x608: 0x4870, 0x609: 0x4876, 0x60a: 0x3d48, 0x60b: 0x3d58,
+	0x60c: 0x3d50, 0x60d: 0x3d60, 0x610: 0x48fa, 0x611: 0x4900,
+	0x612: 0x3e80, 0x613: 0x3e98, 0x614: 0x3e88, 0x615: 0x3ea0, 0x616: 0x3e90, 0x617: 0x3ea8,
+	0x619: 0x487c, 0x61b: 0x3d68, 0x61d: 0x3d70,
+	0x61f: 0x3d78, 0x620: 0x4912, 0x621: 0x4918, 0x622: 0x4a14, 0x623: 0x4a2c,
+	0x624: 0x4a1c, 0x625: 0x4a34, 0x626: 0x4a24, 0x627: 0x4a3c, 0x628: 0x4882, 0x629: 0x4888,
+	0x62a: 0x4984, 0x62b: 0x499c, 0x62c: 0x498c, 0x62d: 0x49a4, 0x62e: 0x4994, 0x62f: 0x49ac,
+	0x630: 0x488e, 0x631: 0x43b4, 0x632: 0x3691, 0x633: 0x43ba, 0x634: 0x48b8, 0x635: 0x43c0,
+	0x636: 0x36a3, 0x637: 0x43c6, 0x638: 0x36c1, 0x639: 0x43cc, 0x63a: 0x36d9, 0x63b: 0x43d2,
+	0x63c: 0x4906, 0x63d: 0x43d8,
+	// Block 0x19, offset 0x640
+	0x640: 0x3da0, 0x641: 0x3da8, 0x642: 0x4184, 0x643: 0x41a2, 0x644: 0x418e, 0x645: 0x41ac,
+	0x646: 0x4198, 0x647: 0x41b6, 0x648: 0x3cd8, 0x649: 0x3ce0, 0x64a: 0x40d0, 0x64b: 0x40ee,
+	0x64c: 0x40da, 0x64d: 0x40f8, 0x64e: 0x40e4, 0x64f: 0x4102, 0x650: 0x3de8, 0x651: 0x3df0,
+	0x652: 0x41c0, 0x653: 0x41de, 0x654: 0x41ca, 0x655: 0x41e8, 0x656: 0x41d4, 0x657: 0x41f2,
+	0x658: 0x3d08, 0x659: 0x3d10, 0x65a: 0x410c, 0x65b: 0x412a, 0x65c: 0x4116, 0x65d: 0x4134,
+	0x65e: 0x4120, 0x65f: 0x413e, 0x660: 0x3ec0, 0x661: 0x3ec8, 0x662: 0x41fc, 0x663: 0x421a,
+	0x664: 0x4206, 0x665: 0x4224, 0x666: 0x4210, 0x667: 0x422e, 0x668: 0x3d80, 0x669: 0x3d88,
+	0x66a: 0x4148, 0x66b: 0x4166, 0x66c: 0x4152, 0x66d: 0x4170, 0x66e: 0x415c, 0x66f: 0x417a,
+	0x670: 0x3685, 0x671: 0x367f, 0x672: 0x3d90, 0x673: 0x368b, 0x674: 0x3d98,
+	0x676: 0x48a6, 0x677: 0x3db0, 0x678: 0x35f5, 0x679: 0x35ef, 0x67a: 0x35e3, 0x67b: 0x4384,
+	0x67c: 0x35fb, 0x67d: 0x4287, 0x67e: 0x01d3, 0x67f: 0x4287,
+	// Block 0x1a, offset 0x680
+	0x680: 0x42a0, 0x681: 0x4518, 0x682: 0x3dd8, 0x683: 0x369d, 0x684: 0x3de0,
+	0x686: 0x48d0, 0x687: 0x3df8, 0x688: 0x3601, 0x689: 0x438a, 0x68a: 0x360d, 0x68b: 0x4390,
+	0x68c: 0x3619, 0x68d: 0x451f, 0x68e: 0x4526, 0x68f: 0x452d, 0x690: 0x36b5, 0x691: 0x36af,
+	0x692: 0x3e00, 0x693: 0x457a, 0x696: 0x36bb, 0x697: 0x3e10,
+	0x698: 0x3631, 0x699: 0x362b, 0x69a: 0x361f, 0x69b: 0x4396, 0x69d: 0x4534,
+	0x69e: 0x453b, 0x69f: 0x4542, 0x6a0: 0x36eb, 0x6a1: 0x36e5, 0x6a2: 0x3e68, 0x6a3: 0x4582,
+	0x6a4: 0x36cd, 0x6a5: 0x36d3, 0x6a6: 0x36f1, 0x6a7: 0x3e78, 0x6a8: 0x3661, 0x6a9: 0x365b,
+	0x6aa: 0x364f, 0x6ab: 0x43a2, 0x6ac: 0x3649, 0x6ad: 0x450a, 0x6ae: 0x4511, 0x6af: 0x0081,
+	0x6b2: 0x3eb0, 0x6b3: 0x36f7, 0x6b4: 0x3eb8,
+	0x6b6: 0x491e, 0x6b7: 0x3ed0, 0x6b8: 0x363d, 0x6b9: 0x439c, 0x6ba: 0x366d, 0x6bb: 0x43ae,
+	0x6bc: 0x3679, 0x6bd: 0x425a, 0x6be: 0x428c,
+	// Block 0x1b, offset 0x6c0
+	0x6c0: 0x1bd8, 0x6c1: 0x1bdc, 0x6c2: 0x0047, 0x6c3: 0x1c54, 0x6c5: 0x1be8,
+	0x6c6: 0x1bec, 0x6c7: 0x00e9, 0x6c9: 0x1c58, 0x6ca: 0x008f, 0x6cb: 0x0051,
+	0x6cc: 0x0051, 0x6cd: 0x0051, 0x6ce: 0x0091, 0x6cf: 0x00da, 0x6d0: 0x0053, 0x6d1: 0x0053,
+	0x6d2: 0x0059, 0x6d3: 0x0099, 0x6d5: 0x005d, 0x6d6: 0x198d,
+	0x6d9: 0x0061, 0x6da: 0x0063, 0x6db: 0x0065, 0x6dc: 0x0065, 0x6dd: 0x0065,
+	0x6e0: 0x199f, 0x6e1: 0x1bc8, 0x6e2: 0x19a8,
+	0x6e4: 0x0075, 0x6e6: 0x01b8, 0x6e8: 0x0075,
+	0x6ea: 0x0057, 0x6eb: 0x42d2, 0x6ec: 0x0045, 0x6ed: 0x0047, 0x6ef: 0x008b,
+	0x6f0: 0x004b, 0x6f1: 0x004d, 0x6f3: 0x005b, 0x6f4: 0x009f, 0x6f5: 0x0215,
+	0x6f6: 0x0218, 0x6f7: 0x021b, 0x6f8: 0x021e, 0x6f9: 0x0093, 0x6fb: 0x1b98,
+	0x6fc: 0x01e8, 0x6fd: 0x01c1, 0x6fe: 0x0179, 0x6ff: 0x01a0,
+	// Block 0x1c, offset 0x700
+	0x700: 0x0463, 0x705: 0x0049,
+	0x706: 0x0089, 0x707: 0x008b, 0x708: 0x0093, 0x709: 0x0095,
+	0x710: 0x222e, 0x711: 0x223a,
+	0x712: 0x22ee, 0x713: 0x2216, 0x714: 0x229a, 0x715: 0x2222, 0x716: 0x22a0, 0x717: 0x22b8,
+	0x718: 0x22c4, 0x719: 0x2228, 0x71a: 0x22ca, 0x71b: 0x2234, 0x71c: 0x22be, 0x71d: 0x22d0,
+	0x71e: 0x22d6, 0x71f: 0x1cbc, 0x720: 0x0053, 0x721: 0x195a, 0x722: 0x1ba4, 0x723: 0x1963,
+	0x724: 0x006d, 0x725: 0x19ab, 0x726: 0x1bd0, 0x727: 0x1d48, 0x728: 0x1966, 0x729: 0x0071,
+	0x72a: 0x19b7, 0x72b: 0x1bd4, 0x72c: 0x0059, 0x72d: 0x0047, 0x72e: 0x0049, 0x72f: 0x005b,
+	0x730: 0x0093, 0x731: 0x19e4, 0x732: 0x1c18, 0x733: 0x19ed, 0x734: 0x00ad, 0x735: 0x1a62,
+	0x736: 0x1c4c, 0x737: 0x1d5c, 0x738: 0x19f0, 0x739: 0x00b1, 0x73a: 0x1a65, 0x73b: 0x1c50,
+	0x73c: 0x0099, 0x73d: 0x0087, 0x73e: 0x0089, 0x73f: 0x009b,
+	// Block 0x1d, offset 0x740
+	0x741: 0x3c06, 0x743: 0xa000, 0x744: 0x3c0d, 0x745: 0xa000,
+	0x747: 0x3c14, 0x748: 0xa000, 0x749: 0x3c1b,
+	0x74d: 0xa000,
+	0x760: 0x2f65, 0x761: 0xa000, 0x762: 0x3c29,
+	0x764: 0xa000, 0x765: 0xa000,
+	0x76d: 0x3c22, 0x76e: 0x2f60, 0x76f: 0x2f6a,
+	0x770: 0x3c30, 0x771: 0x3c37, 0x772: 0xa000, 0x773: 0xa000, 0x774: 0x3c3e, 0x775: 0x3c45,
+	0x776: 0xa000, 0x777: 0xa000, 0x778: 0x3c4c, 0x779: 0x3c53, 0x77a: 0xa000, 0x77b: 0xa000,
+	0x77c: 0xa000, 0x77d: 0xa000,
+	// Block 0x1e, offset 0x780
+	0x780: 0x3c5a, 0x781: 0x3c61, 0x782: 0xa000, 0x783: 0xa000, 0x784: 0x3c76, 0x785: 0x3c7d,
+	0x786: 0xa000, 0x787: 0xa000, 0x788: 0x3c84, 0x789: 0x3c8b,
+	0x791: 0xa000,
+	0x792: 0xa000,
+	0x7a2: 0xa000,
+	0x7a8: 0xa000, 0x7a9: 0xa000,
+	0x7ab: 0xa000, 0x7ac: 0x3ca0, 0x7ad: 0x3ca7, 0x7ae: 0x3cae, 0x7af: 0x3cb5,
+	0x7b2: 0xa000, 0x7b3: 0xa000, 0x7b4: 0xa000, 0x7b5: 0xa000,
+	// Block 0x1f, offset 0x7c0
+	0x7e0: 0x0023, 0x7e1: 0x0025, 0x7e2: 0x0027, 0x7e3: 0x0029,
+	0x7e4: 0x002b, 0x7e5: 0x002d, 0x7e6: 0x002f, 0x7e7: 0x0031, 0x7e8: 0x0033, 0x7e9: 0x1882,
+	0x7ea: 0x1885, 0x7eb: 0x1888, 0x7ec: 0x188b, 0x7ed: 0x188e, 0x7ee: 0x1891, 0x7ef: 0x1894,
+	0x7f0: 0x1897, 0x7f1: 0x189a, 0x7f2: 0x189d, 0x7f3: 0x18a6, 0x7f4: 0x1a68, 0x7f5: 0x1a6c,
+	0x7f6: 0x1a70, 0x7f7: 0x1a74, 0x7f8: 0x1a78, 0x7f9: 0x1a7c, 0x7fa: 0x1a80, 0x7fb: 0x1a84,
+	0x7fc: 0x1a88, 0x7fd: 0x1c80, 0x7fe: 0x1c85, 0x7ff: 0x1c8a,
+	// Block 0x20, offset 0x800
+	0x800: 0x1c8f, 0x801: 0x1c94, 0x802: 0x1c99, 0x803: 0x1c9e, 0x804: 0x1ca3, 0x805: 0x1ca8,
+	0x806: 0x1cad, 0x807: 0x1cb2, 0x808: 0x187f, 0x809: 0x18a3, 0x80a: 0x18c7, 0x80b: 0x18eb,
+	0x80c: 0x190f, 0x80d: 0x1918, 0x80e: 0x191e, 0x80f: 0x1924, 0x810: 0x192a, 0x811: 0x1b60,
+	0x812: 0x1b64, 0x813: 0x1b68, 0x814: 0x1b6c, 0x815: 0x1b70, 0x816: 0x1b74, 0x817: 0x1b78,
+	0x818: 0x1b7c, 0x819: 0x1b80, 0x81a: 0x1b84, 0x81b: 0x1b88, 0x81c: 0x1af4, 0x81d: 0x1af8,
+	0x81e: 0x1afc, 0x81f: 0x1b00, 0x820: 0x1b04, 0x821: 0x1b08, 0x822: 0x1b0c, 0x823: 0x1b10,
+	0x824: 0x1b14, 0x825: 0x1b18, 0x826: 0x1b1c, 0x827: 0x1b20, 0x828: 0x1b24, 0x829: 0x1b28,
+	0x82a: 0x1b2c, 0x82b: 0x1b30, 0x82c: 0x1b34, 0x82d: 0x1b38, 0x82e: 0x1b3c, 0x82f: 0x1b40,
+	0x830: 0x1b44, 0x831: 0x1b48, 0x832: 0x1b4c, 0x833: 0x1b50, 0x834: 0x1b54, 0x835: 0x1b58,
+	0x836: 0x0043, 0x837: 0x0045, 0x838: 0x0047, 0x839: 0x0049, 0x83a: 0x004b, 0x83b: 0x004d,
+	0x83c: 0x004f, 0x83d: 0x0051, 0x83e: 0x0053, 0x83f: 0x0055,
+	// Block 0x21, offset 0x840
+	0x840: 0x06bf, 0x841: 0x06e3, 0x842: 0x06ef, 0x843: 0x06ff, 0x844: 0x0707, 0x845: 0x0713,
+	0x846: 0x071b, 0x847: 0x0723, 0x848: 0x072f, 0x849: 0x0783, 0x84a: 0x079b, 0x84b: 0x07ab,
+	0x84c: 0x07bb, 0x84d: 0x07cb, 0x84e: 0x07db, 0x84f: 0x07fb, 0x850: 0x07ff, 0x851: 0x0803,
+	0x852: 0x0837, 0x853: 0x085f, 0x854: 0x086f, 0x855: 0x0877, 0x856: 0x087b, 0x857: 0x0887,
+	0x858: 0x08a3, 0x859: 0x08a7, 0x85a: 0x08bf, 0x85b: 0x08c3, 0x85c: 0x08cb, 0x85d: 0x08db,
+	0x85e: 0x0977, 0x85f: 0x098b, 0x860: 0x09cb, 0x861: 0x09df, 0x862: 0x09e7, 0x863: 0x09eb,
+	0x864: 0x09fb, 0x865: 0x0a17, 0x866: 0x0a43, 0x867: 0x0a4f, 0x868: 0x0a6f, 0x869: 0x0a7b,
+	0x86a: 0x0a7f, 0x86b: 0x0a83, 0x86c: 0x0a9b, 0x86d: 0x0a9f, 0x86e: 0x0acb, 0x86f: 0x0ad7,
+	0x870: 0x0adf, 0x871: 0x0ae7, 0x872: 0x0af7, 0x873: 0x0aff, 0x874: 0x0b07, 0x875: 0x0b33,
+	0x876: 0x0b37, 0x877: 0x0b3f, 0x878: 0x0b43, 0x879: 0x0b4b, 0x87a: 0x0b53, 0x87b: 0x0b63,
+	0x87c: 0x0b7f, 0x87d: 0x0bf7, 0x87e: 0x0c0b, 0x87f: 0x0c0f,
+	// Block 0x22, offset 0x880
+	0x880: 0x0c8f, 0x881: 0x0c93, 0x882: 0x0ca7, 0x883: 0x0cab, 0x884: 0x0cb3, 0x885: 0x0cbb,
+	0x886: 0x0cc3, 0x887: 0x0ccf, 0x888: 0x0cf7, 0x889: 0x0d07, 0x88a: 0x0d1b, 0x88b: 0x0d8b,
+	0x88c: 0x0d97, 0x88d: 0x0da7, 0x88e: 0x0db3, 0x88f: 0x0dbf, 0x890: 0x0dc7, 0x891: 0x0dcb,
+	0x892: 0x0dcf, 0x893: 0x0dd3, 0x894: 0x0dd7, 0x895: 0x0e8f, 0x896: 0x0ed7, 0x897: 0x0ee3,
+	0x898: 0x0ee7, 0x899: 0x0eeb, 0x89a: 0x0eef, 0x89b: 0x0ef7, 0x89c: 0x0efb, 0x89d: 0x0f0f,
+	0x89e: 0x0f2b, 0x89f: 0x0f33, 0x8a0: 0x0f73, 0x8a1: 0x0f77, 0x8a2: 0x0f7f, 0x8a3: 0x0f83,
+	0x8a4: 0x0f8b, 0x8a5: 0x0f8f, 0x8a6: 0x0fb3, 0x8a7: 0x0fb7, 0x8a8: 0x0fd3, 0x8a9: 0x0fd7,
+	0x8aa: 0x0fdb, 0x8ab: 0x0fdf, 0x8ac: 0x0ff3, 0x8ad: 0x1017, 0x8ae: 0x101b, 0x8af: 0x101f,
+	0x8b0: 0x1043, 0x8b1: 0x1083, 0x8b2: 0x1087, 0x8b3: 0x10a7, 0x8b4: 0x10b7, 0x8b5: 0x10bf,
+	0x8b6: 0x10df, 0x8b7: 0x1103, 0x8b8: 0x1147, 0x8b9: 0x114f, 0x8ba: 0x1163, 0x8bb: 0x116f,
+	0x8bc: 0x1177, 0x8bd: 0x117f, 0x8be: 0x1183, 0x8bf: 0x1187,
+	// Block 0x23, offset 0x8c0
+	0x8c0: 0x119f, 0x8c1: 0x11a3, 0x8c2: 0x11bf, 0x8c3: 0x11c7, 0x8c4: 0x11cf, 0x8c5: 0x11d3,
+	0x8c6: 0x11df, 0x8c7: 0x11e7, 0x8c8: 0x11eb, 0x8c9: 0x11ef, 0x8ca: 0x11f7, 0x8cb: 0x11fb,
+	0x8cc: 0x129b, 0x8cd: 0x12af, 0x8ce: 0x12e3, 0x8cf: 0x12e7, 0x8d0: 0x12ef, 0x8d1: 0x131b,
+	0x8d2: 0x1323, 0x8d3: 0x132b, 0x8d4: 0x1333, 0x8d5: 0x136f, 0x8d6: 0x1373, 0x8d7: 0x137b,
+	0x8d8: 0x137f, 0x8d9: 0x1383, 0x8da: 0x13af, 0x8db: 0x13b3, 0x8dc: 0x13bb, 0x8dd: 0x13cf,
+	0x8de: 0x13d3, 0x8df: 0x13ef, 0x8e0: 0x13f7, 0x8e1: 0x13fb, 0x8e2: 0x141f, 0x8e3: 0x143f,
+	0x8e4: 0x1453, 0x8e5: 0x1457, 0x8e6: 0x145f, 0x8e7: 0x148b, 0x8e8: 0x148f, 0x8e9: 0x149f,
+	0x8ea: 0x14c3, 0x8eb: 0x14cf, 0x8ec: 0x14df, 0x8ed: 0x14f7, 0x8ee: 0x14ff, 0x8ef: 0x1503,
+	0x8f0: 0x1507, 0x8f1: 0x150b, 0x8f2: 0x1517, 0x8f3: 0x151b, 0x8f4: 0x1523, 0x8f5: 0x153f,
+	0x8f6: 0x1543, 0x8f7: 0x1547, 0x8f8: 0x155f, 0x8f9: 0x1563, 0x8fa: 0x156b, 0x8fb: 0x157f,
+	0x8fc: 0x1583, 0x8fd: 0x1587, 0x8fe: 0x158f, 0x8ff: 0x1593,
+	// Block 0x24, offset 0x900
+	0x906: 0xa000, 0x90b: 0xa000,
+	0x90c: 0x3f08, 0x90d: 0xa000, 0x90e: 0x3f10, 0x90f: 0xa000, 0x910: 0x3f18, 0x911: 0xa000,
+	0x912: 0x3f20, 0x913: 0xa000, 0x914: 0x3f28, 0x915: 0xa000, 0x916: 0x3f30, 0x917: 0xa000,
+	0x918: 0x3f38, 0x919: 0xa000, 0x91a: 0x3f40, 0x91b: 0xa000, 0x91c: 0x3f48, 0x91d: 0xa000,
+	0x91e: 0x3f50, 0x91f: 0xa000, 0x920: 0x3f58, 0x921: 0xa000, 0x922: 0x3f60,
+	0x924: 0xa000, 0x925: 0x3f68, 0x926: 0xa000, 0x927: 0x3f70, 0x928: 0xa000, 0x929: 0x3f78,
+	0x92f: 0xa000,
+	0x930: 0x3f80, 0x931: 0x3f88, 0x932: 0xa000, 0x933: 0x3f90, 0x934: 0x3f98, 0x935: 0xa000,
+	0x936: 0x3fa0, 0x937: 0x3fa8, 0x938: 0xa000, 0x939: 0x3fb0, 0x93a: 0x3fb8, 0x93b: 0xa000,
+	0x93c: 0x3fc0, 0x93d: 0x3fc8,
+	// Block 0x25, offset 0x940
+	0x954: 0x3f00,
+	0x959: 0x9903, 0x95a: 0x9903, 0x95b: 0x4372, 0x95c: 0x4378, 0x95d: 0xa000,
+	0x95e: 0x3fd0, 0x95f: 0x26b4,
+	0x966: 0xa000,
+	0x96b: 0xa000, 0x96c: 0x3fe0, 0x96d: 0xa000, 0x96e: 0x3fe8, 0x96f: 0xa000,
+	0x970: 0x3ff0, 0x971: 0xa000, 0x972: 0x3ff8, 0x973: 0xa000, 0x974: 0x4000, 0x975: 0xa000,
+	0x976: 0x4008, 0x977: 0xa000, 0x978: 0x4010, 0x979: 0xa000, 0x97a: 0x4018, 0x97b: 0xa000,
+	0x97c: 0x4020, 0x97d: 0xa000, 0x97e: 0x4028, 0x97f: 0xa000,
+	// Block 0x26, offset 0x980
+	0x980: 0x4030, 0x981: 0xa000, 0x982: 0x4038, 0x984: 0xa000, 0x985: 0x4040,
+	0x986: 0xa000, 0x987: 0x4048, 0x988: 0xa000, 0x989: 0x4050,
+	0x98f: 0xa000, 0x990: 0x4058, 0x991: 0x4060,
+	0x992: 0xa000, 0x993: 0x4068, 0x994: 0x4070, 0x995: 0xa000, 0x996: 0x4078, 0x997: 0x4080,
+	0x998: 0xa000, 0x999: 0x4088, 0x99a: 0x4090, 0x99b: 0xa000, 0x99c: 0x4098, 0x99d: 0x40a0,
+	0x9af: 0xa000,
+	0x9b0: 0xa000, 0x9b1: 0xa000, 0x9b2: 0xa000, 0x9b4: 0x3fd8,
+	0x9b7: 0x40a8, 0x9b8: 0x40b0, 0x9b9: 0x40b8, 0x9ba: 0x40c0,
+	0x9bd: 0xa000, 0x9be: 0x40c8, 0x9bf: 0x26c9,
+	// Block 0x27, offset 0x9c0
+	0x9c0: 0x0367, 0x9c1: 0x032b, 0x9c2: 0x032f, 0x9c3: 0x0333, 0x9c4: 0x037b, 0x9c5: 0x0337,
+	0x9c6: 0x033b, 0x9c7: 0x033f, 0x9c8: 0x0343, 0x9c9: 0x0347, 0x9ca: 0x034b, 0x9cb: 0x034f,
+	0x9cc: 0x0353, 0x9cd: 0x0357, 0x9ce: 0x035b, 0x9cf: 0x42dc, 0x9d0: 0x42e1, 0x9d1: 0x42e6,
+	0x9d2: 0x42eb, 0x9d3: 0x42f0, 0x9d4: 0x42f5, 0x9d5: 0x42fa, 0x9d6: 0x42ff, 0x9d7: 0x4304,
+	0x9d8: 0x4309, 0x9d9: 0x430e, 0x9da: 0x4313, 0x9db: 0x4318, 0x9dc: 0x431d, 0x9dd: 0x4322,
+	0x9de: 0x4327, 0x9df: 0x432c, 0x9e0: 0x4331, 0x9e1: 0x4336, 0x9e2: 0x433b, 0x9e3: 0x4340,
+	0x9e4: 0x03c3, 0x9e5: 0x035f, 0x9e6: 0x0363, 0x9e7: 0x03e7, 0x9e8: 0x03eb, 0x9e9: 0x03ef,
+	0x9ea: 0x03f3, 0x9eb: 0x03f7, 0x9ec: 0x03fb, 0x9ed: 0x03ff, 0x9ee: 0x036b, 0x9ef: 0x0403,
+	0x9f0: 0x0407, 0x9f1: 0x036f, 0x9f2: 0x0373, 0x9f3: 0x0377, 0x9f4: 0x037f, 0x9f5: 0x0383,
+	0x9f6: 0x0387, 0x9f7: 0x038b, 0x9f8: 0x038f, 0x9f9: 0x0393, 0x9fa: 0x0397, 0x9fb: 0x039b,
+	0x9fc: 0x039f, 0x9fd: 0x03a3, 0x9fe: 0x03a7, 0x9ff: 0x03ab,
+	// Block 0x28, offset 0xa00
+	0xa00: 0x03af, 0xa01: 0x03b3, 0xa02: 0x040b, 0xa03: 0x040f, 0xa04: 0x03b7, 0xa05: 0x03bb,
+	0xa06: 0x03bf, 0xa07: 0x03c7, 0xa08: 0x03cb, 0xa09: 0x03cf, 0xa0a: 0x03d3, 0xa0b: 0x03d7,
+	0xa0c: 0x03db, 0xa0d: 0x03df, 0xa0e: 0x03e3,
+	0xa12: 0x06bf, 0xa13: 0x071b, 0xa14: 0x06cb, 0xa15: 0x097b, 0xa16: 0x06cf, 0xa17: 0x06e7,
+	0xa18: 0x06d3, 0xa19: 0x0f93, 0xa1a: 0x0707, 0xa1b: 0x06db, 0xa1c: 0x06c3, 0xa1d: 0x09ff,
+	0xa1e: 0x098f, 0xa1f: 0x072f,
+	// Block 0x29, offset 0xa40
+	0xa40: 0x2054, 0xa41: 0x205a, 0xa42: 0x2060, 0xa43: 0x2066, 0xa44: 0x206c, 0xa45: 0x2072,
+	0xa46: 0x2078, 0xa47: 0x207e, 0xa48: 0x2084, 0xa49: 0x208a, 0xa4a: 0x2090, 0xa4b: 0x2096,
+	0xa4c: 0x209c, 0xa4d: 0x20a2, 0xa4e: 0x2726, 0xa4f: 0x272f, 0xa50: 0x2738, 0xa51: 0x2741,
+	0xa52: 0x274a, 0xa53: 0x2753, 0xa54: 0x275c, 0xa55: 0x2765, 0xa56: 0x276e, 0xa57: 0x2780,
+	0xa58: 0x2789, 0xa59: 0x2792, 0xa5a: 0x279b, 0xa5b: 0x27a4, 0xa5c: 0x2777, 0xa5d: 0x2bac,
+	0xa5e: 0x2aed, 0xa60: 0x20a8, 0xa61: 0x20c0, 0xa62: 0x20b4, 0xa63: 0x2108,
+	0xa64: 0x20c6, 0xa65: 0x20e4, 0xa66: 0x20ae, 0xa67: 0x20de, 0xa68: 0x20ba, 0xa69: 0x20f0,
+	0xa6a: 0x2120, 0xa6b: 0x213e, 0xa6c: 0x2138, 0xa6d: 0x212c, 0xa6e: 0x217a, 0xa6f: 0x210e,
+	0xa70: 0x211a, 0xa71: 0x2132, 0xa72: 0x2126, 0xa73: 0x2150, 0xa74: 0x20fc, 0xa75: 0x2144,
+	0xa76: 0x216e, 0xa77: 0x2156, 0xa78: 0x20ea, 0xa79: 0x20cc, 0xa7a: 0x2102, 0xa7b: 0x2114,
+	0xa7c: 0x214a, 0xa7d: 0x20d2, 0xa7e: 0x2174, 0xa7f: 0x20f6,
+	// Block 0x2a, offset 0xa80
+	0xa80: 0x215c, 0xa81: 0x20d8, 0xa82: 0x2162, 0xa83: 0x2168, 0xa84: 0x092f, 0xa85: 0x0b03,
+	0xa86: 0x0ca7, 0xa87: 0x10c7,
+	0xa90: 0x1bc4, 0xa91: 0x18a9,
+	0xa92: 0x18ac, 0xa93: 0x18af, 0xa94: 0x18b2, 0xa95: 0x18b5, 0xa96: 0x18b8, 0xa97: 0x18bb,
+	0xa98: 0x18be, 0xa99: 0x18c1, 0xa9a: 0x18ca, 0xa9b: 0x18cd, 0xa9c: 0x18d0, 0xa9d: 0x18d3,
+	0xa9e: 0x18d6, 0xa9f: 0x18d9, 0xaa0: 0x0313, 0xaa1: 0x031b, 0xaa2: 0x031f, 0xaa3: 0x0327,
+	0xaa4: 0x032b, 0xaa5: 0x032f, 0xaa6: 0x0337, 0xaa7: 0x033f, 0xaa8: 0x0343, 0xaa9: 0x034b,
+	0xaaa: 0x034f, 0xaab: 0x0353, 0xaac: 0x0357, 0xaad: 0x035b, 0xaae: 0x2e18, 0xaaf: 0x2e20,
+	0xab0: 0x2e28, 0xab1: 0x2e30, 0xab2: 0x2e38, 0xab3: 0x2e40, 0xab4: 0x2e48, 0xab5: 0x2e50,
+	0xab6: 0x2e60, 0xab7: 0x2e68, 0xab8: 0x2e70, 0xab9: 0x2e78, 0xaba: 0x2e80, 0xabb: 0x2e88,
+	0xabc: 0x2ed3, 0xabd: 0x2e9b, 0xabe: 0x2e58,
+	// Block 0x2b, offset 0xac0
+	0xac0: 0x06bf, 0xac1: 0x071b, 0xac2: 0x06cb, 0xac3: 0x097b, 0xac4: 0x071f, 0xac5: 0x07af,
+	0xac6: 0x06c7, 0xac7: 0x07ab, 0xac8: 0x070b, 0xac9: 0x0887, 0xaca: 0x0d07, 0xacb: 0x0e8f,
+	0xacc: 0x0dd7, 0xacd: 0x0d1b, 0xace: 0x145f, 0xacf: 0x098b, 0xad0: 0x0ccf, 0xad1: 0x0d4b,
+	0xad2: 0x0d0b, 0xad3: 0x104b, 0xad4: 0x08fb, 0xad5: 0x0f03, 0xad6: 0x1387, 0xad7: 0x105f,
+	0xad8: 0x0843, 0xad9: 0x108f, 0xada: 0x0f9b, 0xadb: 0x0a17, 0xadc: 0x140f, 0xadd: 0x077f,
+	0xade: 0x08ab, 0xadf: 0x0df7, 0xae0: 0x1527, 0xae1: 0x0743, 0xae2: 0x07d3, 0xae3: 0x0d9b,
+	0xae4: 0x06cf, 0xae5: 0x06e7, 0xae6: 0x06d3, 0xae7: 0x0adb, 0xae8: 0x08ef, 0xae9: 0x087f,
+	0xaea: 0x0a57, 0xaeb: 0x0a4b, 0xaec: 0x0feb, 0xaed: 0x073f, 0xaee: 0x139b, 0xaef: 0x089b,
+	0xaf0: 0x09f3, 0xaf1: 0x18dc, 0xaf2: 0x18df, 0xaf3: 0x18e2, 0xaf4: 0x18e5, 0xaf5: 0x18ee,
+	0xaf6: 0x18f1, 0xaf7: 0x18f4, 0xaf8: 0x18f7, 0xaf9: 0x18fa, 0xafa: 0x18fd, 0xafb: 0x1900,
+	0xafc: 0x1903, 0xafd: 0x1906, 0xafe: 0x1909, 0xaff: 0x1912,
+	// Block 0x2c, offset 0xb00
+	0xb00: 0x1cc6, 0xb01: 0x1cd5, 0xb02: 0x1ce4, 0xb03: 0x1cf3, 0xb04: 0x1d02, 0xb05: 0x1d11,
+	0xb06: 0x1d20, 0xb07: 0x1d2f, 0xb08: 0x1d3e, 0xb09: 0x218c, 0xb0a: 0x219e, 0xb0b: 0x21b0,
+	0xb0c: 0x1954, 0xb0d: 0x1c04, 0xb0e: 0x19d2, 0xb0f: 0x1ba8, 0xb10: 0x04cb, 0xb11: 0x04d3,
+	0xb12: 0x04db, 0xb13: 0x04e3, 0xb14: 0x04eb, 0xb15: 0x04ef, 0xb16: 0x04f3, 0xb17: 0x04f7,
+	0xb18: 0x04fb, 0xb19: 0x04ff, 0xb1a: 0x0503, 0xb1b: 0x0507, 0xb1c: 0x050b, 0xb1d: 0x050f,
+	0xb1e: 0x0513, 0xb1f: 0x0517, 0xb20: 0x051b, 0xb21: 0x0523, 0xb22: 0x0527, 0xb23: 0x052b,
+	0xb24: 0x052f, 0xb25: 0x0533, 0xb26: 0x0537, 0xb27: 0x053b, 0xb28: 0x053f, 0xb29: 0x0543,
+	0xb2a: 0x0547, 0xb2b: 0x054b, 0xb2c: 0x054f, 0xb2d: 0x0553, 0xb2e: 0x0557, 0xb2f: 0x055b,
+	0xb30: 0x055f, 0xb31: 0x0563, 0xb32: 0x0567, 0xb33: 0x056f, 0xb34: 0x0577, 0xb35: 0x057f,
+	0xb36: 0x0583, 0xb37: 0x0587, 0xb38: 0x058b, 0xb39: 0x058f, 0xb3a: 0x0593, 0xb3b: 0x0597,
+	0xb3c: 0x059b, 0xb3d: 0x059f, 0xb3e: 0x05a3,
+	// Block 0x2d, offset 0xb40
+	0xb40: 0x2b0c, 0xb41: 0x29a8, 0xb42: 0x2b1c, 0xb43: 0x2880, 0xb44: 0x2ee4, 0xb45: 0x288a,
+	0xb46: 0x2894, 0xb47: 0x2f28, 0xb48: 0x29b5, 0xb49: 0x289e, 0xb4a: 0x28a8, 0xb4b: 0x28b2,
+	0xb4c: 0x29dc, 0xb4d: 0x29e9, 0xb4e: 0x29c2, 0xb4f: 0x29cf, 0xb50: 0x2ea9, 0xb51: 0x29f6,
+	0xb52: 0x2a03, 0xb53: 0x2bbe, 0xb54: 0x26bb, 0xb55: 0x2bd1, 0xb56: 0x2be4, 0xb57: 0x2b2c,
+	0xb58: 0x2a10, 0xb59: 0x2bf7, 0xb5a: 0x2c0a, 0xb5b: 0x2a1d, 0xb5c: 0x28bc, 0xb5d: 0x28c6,
+	0xb5e: 0x2eb7, 0xb5f: 0x2a2a, 0xb60: 0x2b3c, 0xb61: 0x2ef5, 0xb62: 0x28d0, 0xb63: 0x28da,
+	0xb64: 0x2a37, 0xb65: 0x28e4, 0xb66: 0x28ee, 0xb67: 0x26d0, 0xb68: 0x26d7, 0xb69: 0x28f8,
+	0xb6a: 0x2902, 0xb6b: 0x2c1d, 0xb6c: 0x2a44, 0xb6d: 0x2b4c, 0xb6e: 0x2c30, 0xb6f: 0x2a51,
+	0xb70: 0x2916, 0xb71: 0x290c, 0xb72: 0x2f3c, 0xb73: 0x2a5e, 0xb74: 0x2c43, 0xb75: 0x2920,
+	0xb76: 0x2b5c, 0xb77: 0x292a, 0xb78: 0x2a78, 0xb79: 0x2934, 0xb7a: 0x2a85, 0xb7b: 0x2f06,
+	0xb7c: 0x2a6b, 0xb7d: 0x2b6c, 0xb7e: 0x2a92, 0xb7f: 0x26de,
+	// Block 0x2e, offset 0xb80
+	0xb80: 0x2f17, 0xb81: 0x293e, 0xb82: 0x2948, 0xb83: 0x2a9f, 0xb84: 0x2952, 0xb85: 0x295c,
+	0xb86: 0x2966, 0xb87: 0x2b7c, 0xb88: 0x2aac, 0xb89: 0x26e5, 0xb8a: 0x2c56, 0xb8b: 0x2e90,
+	0xb8c: 0x2b8c, 0xb8d: 0x2ab9, 0xb8e: 0x2ec5, 0xb8f: 0x2970, 0xb90: 0x297a, 0xb91: 0x2ac6,
+	0xb92: 0x26ec, 0xb93: 0x2ad3, 0xb94: 0x2b9c, 0xb95: 0x26f3, 0xb96: 0x2c69, 0xb97: 0x2984,
+	0xb98: 0x1cb7, 0xb99: 0x1ccb, 0xb9a: 0x1cda, 0xb9b: 0x1ce9, 0xb9c: 0x1cf8, 0xb9d: 0x1d07,
+	0xb9e: 0x1d16, 0xb9f: 0x1d25, 0xba0: 0x1d34, 0xba1: 0x1d43, 0xba2: 0x2192, 0xba3: 0x21a4,
+	0xba4: 0x21b6, 0xba5: 0x21c2, 0xba6: 0x21ce, 0xba7: 0x21da, 0xba8: 0x21e6, 0xba9: 0x21f2,
+	0xbaa: 0x21fe, 0xbab: 0x220a, 0xbac: 0x2246, 0xbad: 0x2252, 0xbae: 0x225e, 0xbaf: 0x226a,
+	0xbb0: 0x2276, 0xbb1: 0x1c14, 0xbb2: 0x19c6, 0xbb3: 0x1936, 0xbb4: 0x1be4, 0xbb5: 0x1a47,
+	0xbb6: 0x1a56, 0xbb7: 0x19cc, 0xbb8: 0x1bfc, 0xbb9: 0x1c00, 0xbba: 0x1960, 0xbbb: 0x2701,
+	0xbbc: 0x270f, 0xbbd: 0x26fa, 0xbbe: 0x2708, 0xbbf: 0x2ae0,
+	// Block 0x2f, offset 0xbc0
+	0xbc0: 0x1a4a, 0xbc1: 0x1a32, 0xbc2: 0x1c60, 0xbc3: 0x1a1a, 0xbc4: 0x19f3, 0xbc5: 0x1969,
+	0xbc6: 0x1978, 0xbc7: 0x1948, 0xbc8: 0x1bf0, 0xbc9: 0x1d52, 0xbca: 0x1a4d, 0xbcb: 0x1a35,
+	0xbcc: 0x1c64, 0xbcd: 0x1c70, 0xbce: 0x1a26, 0xbcf: 0x19fc, 0xbd0: 0x1957, 0xbd1: 0x1c1c,
+	0xbd2: 0x1bb0, 0xbd3: 0x1b9c, 0xbd4: 0x1bcc, 0xbd5: 0x1c74, 0xbd6: 0x1a29, 0xbd7: 0x19c9,
+	0xbd8: 0x19ff, 0xbd9: 0x19de, 0xbda: 0x1a41, 0xbdb: 0x1c78, 0xbdc: 0x1a2c, 0xbdd: 0x19c0,
+	0xbde: 0x1a02, 0xbdf: 0x1c3c, 0xbe0: 0x1bf4, 0xbe1: 0x1a14, 0xbe2: 0x1c24, 0xbe3: 0x1c40,
+	0xbe4: 0x1bf8, 0xbe5: 0x1a17, 0xbe6: 0x1c28, 0xbe7: 0x22e8, 0xbe8: 0x22fc, 0xbe9: 0x1996,
+	0xbea: 0x1c20, 0xbeb: 0x1bb4, 0xbec: 0x1ba0, 0xbed: 0x1c48, 0xbee: 0x2716, 0xbef: 0x27ad,
+	0xbf0: 0x1a59, 0xbf1: 0x1a44, 0xbf2: 0x1c7c, 0xbf3: 0x1a2f, 0xbf4: 0x1a50, 0xbf5: 0x1a38,
+	0xbf6: 0x1c68, 0xbf7: 0x1a1d, 0xbf8: 0x19f6, 0xbf9: 0x1981, 0xbfa: 0x1a53, 0xbfb: 0x1a3b,
+	0xbfc: 0x1c6c, 0xbfd: 0x1a20, 0xbfe: 0x19f9, 0xbff: 0x1984,
+	// Block 0x30, offset 0xc00
+	0xc00: 0x1c2c, 0xc01: 0x1bb8, 0xc02: 0x1d4d, 0xc03: 0x1939, 0xc04: 0x19ba, 0xc05: 0x19bd,
+	0xc06: 0x22f5, 0xc07: 0x1b94, 0xc08: 0x19c3, 0xc09: 0x194b, 0xc0a: 0x19e1, 0xc0b: 0x194e,
+	0xc0c: 0x19ea, 0xc0d: 0x196c, 0xc0e: 0x196f, 0xc0f: 0x1a05, 0xc10: 0x1a0b, 0xc11: 0x1a0e,
+	0xc12: 0x1c30, 0xc13: 0x1a11, 0xc14: 0x1a23, 0xc15: 0x1c38, 0xc16: 0x1c44, 0xc17: 0x1990,
+	0xc18: 0x1d57, 0xc19: 0x1bbc, 0xc1a: 0x1993, 0xc1b: 0x1a5c, 0xc1c: 0x19a5, 0xc1d: 0x19b4,
+	0xc1e: 0x22e2, 0xc1f: 0x22dc, 0xc20: 0x1cc1, 0xc21: 0x1cd0, 0xc22: 0x1cdf, 0xc23: 0x1cee,
+	0xc24: 0x1cfd, 0xc25: 0x1d0c, 0xc26: 0x1d1b, 0xc27: 0x1d2a, 0xc28: 0x1d39, 0xc29: 0x2186,
+	0xc2a: 0x2198, 0xc2b: 0x21aa, 0xc2c: 0x21bc, 0xc2d: 0x21c8, 0xc2e: 0x21d4, 0xc2f: 0x21e0,
+	0xc30: 0x21ec, 0xc31: 0x21f8, 0xc32: 0x2204, 0xc33: 0x2240, 0xc34: 0x224c, 0xc35: 0x2258,
+	0xc36: 0x2264, 0xc37: 0x2270, 0xc38: 0x227c, 0xc39: 0x2282, 0xc3a: 0x2288, 0xc3b: 0x228e,
+	0xc3c: 0x2294, 0xc3d: 0x22a6, 0xc3e: 0x22ac, 0xc3f: 0x1c10,
+	// Block 0x31, offset 0xc40
+	0xc40: 0x1377, 0xc41: 0x0cfb, 0xc42: 0x13d3, 0xc43: 0x139f, 0xc44: 0x0e57, 0xc45: 0x06eb,
+	0xc46: 0x08df, 0xc47: 0x162b, 0xc48: 0x162b, 0xc49: 0x0a0b, 0xc4a: 0x145f, 0xc4b: 0x0943,
+	0xc4c: 0x0a07, 0xc4d: 0x0bef, 0xc4e: 0x0fcf, 0xc4f: 0x115f, 0xc50: 0x1297, 0xc51: 0x12d3,
+	0xc52: 0x1307, 0xc53: 0x141b, 0xc54: 0x0d73, 0xc55: 0x0dff, 0xc56: 0x0eab, 0xc57: 0x0f43,
+	0xc58: 0x125f, 0xc59: 0x1447, 0xc5a: 0x1573, 0xc5b: 0x070f, 0xc5c: 0x08b3, 0xc5d: 0x0d87,
+	0xc5e: 0x0ecf, 0xc5f: 0x1293, 0xc60: 0x15c3, 0xc61: 0x0ab3, 0xc62: 0x0e77, 0xc63: 0x1283,
+	0xc64: 0x1317, 0xc65: 0x0c23, 0xc66: 0x11bb, 0xc67: 0x12df, 0xc68: 0x0b1f, 0xc69: 0x0d0f,
+	0xc6a: 0x0e17, 0xc6b: 0x0f1b, 0xc6c: 0x1427, 0xc6d: 0x074f, 0xc6e: 0x07e7, 0xc6f: 0x0853,
+	0xc70: 0x0c8b, 0xc71: 0x0d7f, 0xc72: 0x0ecb, 0xc73: 0x0fef, 0xc74: 0x1177, 0xc75: 0x128b,
+	0xc76: 0x12a3, 0xc77: 0x13c7, 0xc78: 0x14ef, 0xc79: 0x15a3, 0xc7a: 0x15bf, 0xc7b: 0x102b,
+	0xc7c: 0x106b, 0xc7d: 0x1123, 0xc7e: 0x1243, 0xc7f: 0x147b,
+	// Block 0x32, offset 0xc80
+	0xc80: 0x15cb, 0xc81: 0x134b, 0xc82: 0x09c7, 0xc83: 0x0b3b, 0xc84: 0x10db, 0xc85: 0x119b,
+	0xc86: 0x0eff, 0xc87: 0x1033, 0xc88: 0x1397, 0xc89: 0x14e7, 0xc8a: 0x09c3, 0xc8b: 0x0a8f,
+	0xc8c: 0x0d77, 0xc8d: 0x0e2b, 0xc8e: 0x0e5f, 0xc8f: 0x1113, 0xc90: 0x113b, 0xc91: 0x14a7,
+	0xc92: 0x084f, 0xc93: 0x11a7, 0xc94: 0x07f3, 0xc95: 0x07ef, 0xc96: 0x1097, 0xc97: 0x1127,
+	0xc98: 0x125b, 0xc99: 0x14af, 0xc9a: 0x1367, 0xc9b: 0x0c27, 0xc9c: 0x0d73, 0xc9d: 0x1357,
+	0xc9e: 0x06f7, 0xc9f: 0x0a63, 0xca0: 0x0b93, 0xca1: 0x0f2f, 0xca2: 0x0faf, 0xca3: 0x0873,
+	0xca4: 0x103b, 0xca5: 0x075f, 0xca6: 0x0b77, 0xca7: 0x06d7, 0xca8: 0x0deb, 0xca9: 0x0ca3,
+	0xcaa: 0x110f, 0xcab: 0x08c7, 0xcac: 0x09b3, 0xcad: 0x0ffb, 0xcae: 0x1263, 0xcaf: 0x133b,
+	0xcb0: 0x0db7, 0xcb1: 0x13f7, 0xcb2: 0x0de3, 0xcb3: 0x0c37, 0xcb4: 0x121b, 0xcb5: 0x0c57,
+	0xcb6: 0x0fab, 0xcb7: 0x072b, 0xcb8: 0x07a7, 0xcb9: 0x07eb, 0xcba: 0x0d53, 0xcbb: 0x10fb,
+	0xcbc: 0x11f3, 0xcbd: 0x1347, 0xcbe: 0x145b, 0xcbf: 0x085b,
+	// Block 0x33, offset 0xcc0
+	0xcc0: 0x090f, 0xcc1: 0x0a17, 0xcc2: 0x0b2f, 0xcc3: 0x0cbf, 0xcc4: 0x0e7b, 0xcc5: 0x103f,
+	0xcc6: 0x1497, 0xcc7: 0x157b, 0xcc8: 0x15cf, 0xcc9: 0x15e7, 0xcca: 0x0837, 0xccb: 0x0cf3,
+	0xccc: 0x0da3, 0xccd: 0x13eb, 0xcce: 0x0afb, 0xccf: 0x0bd7, 0xcd0: 0x0bf3, 0xcd1: 0x0c83,
+	0xcd2: 0x0e6b, 0xcd3: 0x0eb7, 0xcd4: 0x0f67, 0xcd5: 0x108b, 0xcd6: 0x112f, 0xcd7: 0x1193,
+	0xcd8: 0x13db, 0xcd9: 0x126b, 0xcda: 0x1403, 0xcdb: 0x147f, 0xcdc: 0x080f, 0xcdd: 0x083b,
+	0xcde: 0x0923, 0xcdf: 0x0ea7, 0xce0: 0x12f3, 0xce1: 0x133b, 0xce2: 0x0b1b, 0xce3: 0x0b8b,
+	0xce4: 0x0c4f, 0xce5: 0x0daf, 0xce6: 0x10d7, 0xce7: 0x0f23, 0xce8: 0x073b, 0xce9: 0x097f,
+	0xcea: 0x0a63, 0xceb: 0x0ac7, 0xcec: 0x0b97, 0xced: 0x0f3f, 0xcee: 0x0f5b, 0xcef: 0x116b,
+	0xcf0: 0x118b, 0xcf1: 0x1463, 0xcf2: 0x14e3, 0xcf3: 0x14f3, 0xcf4: 0x152f, 0xcf5: 0x0753,
+	0xcf6: 0x107f, 0xcf7: 0x144f, 0xcf8: 0x14cb, 0xcf9: 0x0baf, 0xcfa: 0x0717, 0xcfb: 0x0777,
+	0xcfc: 0x0a67, 0xcfd: 0x0a87, 0xcfe: 0x0caf, 0xcff: 0x0d73,
+	// Block 0x34, offset 0xd00
+	0xd00: 0x0ec3, 0xd01: 0x0fcb, 0xd02: 0x1277, 0xd03: 0x1417, 0xd04: 0x1623, 0xd05: 0x0ce3,
+	0xd06: 0x14a3, 0xd07: 0x0833, 0xd08: 0x0d2f, 0xd09: 0x0d3b, 0xd0a: 0x0e0f, 0xd0b: 0x0e47,
+	0xd0c: 0x0f4b, 0xd0d: 0x0fa7, 0xd0e: 0x1027, 0xd0f: 0x110b, 0xd10: 0x153b, 0xd11: 0x07af,
+	0xd12: 0x0c03, 0xd13: 0x14b3, 0xd14: 0x0767, 0xd15: 0x0aab, 0xd16: 0x0e2f, 0xd17: 0x13df,
+	0xd18: 0x0b67, 0xd19: 0x0bb7, 0xd1a: 0x0d43, 0xd1b: 0x0f2f, 0xd1c: 0x14bb, 0xd1d: 0x0817,
+	0xd1e: 0x08ff, 0xd1f: 0x0a97, 0xd20: 0x0cd3, 0xd21: 0x0d1f, 0xd22: 0x0d5f, 0xd23: 0x0df3,
+	0xd24: 0x0f47, 0xd25: 0x0fbb, 0xd26: 0x1157, 0xd27: 0x12f7, 0xd28: 0x1303, 0xd29: 0x1457,
+	0xd2a: 0x14d7, 0xd2b: 0x0883, 0xd2c: 0x0e4b, 0xd2d: 0x0903, 0xd2e: 0x0ec7, 0xd2f: 0x0f6b,
+	0xd30: 0x1287, 0xd31: 0x14bf, 0xd32: 0x15ab, 0xd33: 0x15d3, 0xd34: 0x0d37, 0xd35: 0x0e27,
+	0xd36: 0x11c3, 0xd37: 0x10b7, 0xd38: 0x10c3, 0xd39: 0x10e7, 0xd3a: 0x0f17, 0xd3b: 0x0e9f,
+	0xd3c: 0x1363, 0xd3d: 0x0733, 0xd3e: 0x122b, 0xd3f: 0x081b,
+	// Block 0x35, offset 0xd40
+	0xd40: 0x080b, 0xd41: 0x0b0b, 0xd42: 0x0c2b, 0xd43: 0x10f3, 0xd44: 0x0a53, 0xd45: 0x0e03,
+	0xd46: 0x0cef, 0xd47: 0x13e7, 0xd48: 0x12e7, 0xd49: 0x14ab, 0xd4a: 0x1323, 0xd4b: 0x0b27,
+	0xd4c: 0x0787, 0xd4d: 0x095b, 0xd50: 0x09af,
+	0xd52: 0x0cdf, 0xd55: 0x07f7, 0xd56: 0x0f1f, 0xd57: 0x0fe3,
+	0xd58: 0x1047, 0xd59: 0x1063, 0xd5a: 0x1067, 0xd5b: 0x107b, 0xd5c: 0x14fb, 0xd5d: 0x10eb,
+	0xd5e: 0x116f, 0xd60: 0x128f, 0xd62: 0x1353,
+	0xd65: 0x1407, 0xd66: 0x1433,
+	0xd6a: 0x154f, 0xd6b: 0x1553, 0xd6c: 0x1557, 0xd6d: 0x15bb, 0xd6e: 0x142b, 0xd6f: 0x14c7,
+	0xd70: 0x0757, 0xd71: 0x077b, 0xd72: 0x078f, 0xd73: 0x084b, 0xd74: 0x0857, 0xd75: 0x0897,
+	0xd76: 0x094b, 0xd77: 0x0967, 0xd78: 0x096f, 0xd79: 0x09ab, 0xd7a: 0x09b7, 0xd7b: 0x0a93,
+	0xd7c: 0x0a9b, 0xd7d: 0x0ba3, 0xd7e: 0x0bcb, 0xd7f: 0x0bd3,
+	// Block 0x36, offset 0xd80
+	0xd80: 0x0beb, 0xd81: 0x0c97, 0xd82: 0x0cc7, 0xd83: 0x0ce7, 0xd84: 0x0d57, 0xd85: 0x0e1b,
+	0xd86: 0x0e37, 0xd87: 0x0e67, 0xd88: 0x0ebb, 0xd89: 0x0edb, 0xd8a: 0x0f4f, 0xd8b: 0x102f,
+	0xd8c: 0x104b, 0xd8d: 0x1053, 0xd8e: 0x104f, 0xd8f: 0x1057, 0xd90: 0x105b, 0xd91: 0x105f,
+	0xd92: 0x1073, 0xd93: 0x1077, 0xd94: 0x109b, 0xd95: 0x10af, 0xd96: 0x10cb, 0xd97: 0x112f,
+	0xd98: 0x1137, 0xd99: 0x113f, 0xd9a: 0x1153, 0xd9b: 0x117b, 0xd9c: 0x11cb, 0xd9d: 0x11ff,
+	0xd9e: 0x11ff, 0xd9f: 0x1267, 0xda0: 0x130f, 0xda1: 0x1327, 0xda2: 0x135b, 0xda3: 0x135f,
+	0xda4: 0x13a3, 0xda5: 0x13a7, 0xda6: 0x13ff, 0xda7: 0x1407, 0xda8: 0x14db, 0xda9: 0x151f,
+	0xdaa: 0x1537, 0xdab: 0x0b9b, 0xdac: 0x171e, 0xdad: 0x11e3,
+	0xdb0: 0x06df, 0xdb1: 0x07e3, 0xdb2: 0x07a3, 0xdb3: 0x074b, 0xdb4: 0x078b, 0xdb5: 0x07b7,
+	0xdb6: 0x0847, 0xdb7: 0x0863, 0xdb8: 0x094b, 0xdb9: 0x0937, 0xdba: 0x0947, 0xdbb: 0x0963,
+	0xdbc: 0x09af, 0xdbd: 0x09bf, 0xdbe: 0x0a03, 0xdbf: 0x0a0f,
+	// Block 0x37, offset 0xdc0
+	0xdc0: 0x0a2b, 0xdc1: 0x0a3b, 0xdc2: 0x0b23, 0xdc3: 0x0b2b, 0xdc4: 0x0b5b, 0xdc5: 0x0b7b,
+	0xdc6: 0x0bab, 0xdc7: 0x0bc3, 0xdc8: 0x0bb3, 0xdc9: 0x0bd3, 0xdca: 0x0bc7, 0xdcb: 0x0beb,
+	0xdcc: 0x0c07, 0xdcd: 0x0c5f, 0xdce: 0x0c6b, 0xdcf: 0x0c73, 0xdd0: 0x0c9b, 0xdd1: 0x0cdf,
+	0xdd2: 0x0d0f, 0xdd3: 0x0d13, 0xdd4: 0x0d27, 0xdd5: 0x0da7, 0xdd6: 0x0db7, 0xdd7: 0x0e0f,
+	0xdd8: 0x0e5b, 0xdd9: 0x0e53, 0xdda: 0x0e67, 0xddb: 0x0e83, 0xddc: 0x0ebb, 0xddd: 0x1013,
+	0xdde: 0x0edf, 0xddf: 0x0f13, 0xde0: 0x0f1f, 0xde1: 0x0f5f, 0xde2: 0x0f7b, 0xde3: 0x0f9f,
+	0xde4: 0x0fc3, 0xde5: 0x0fc7, 0xde6: 0x0fe3, 0xde7: 0x0fe7, 0xde8: 0x0ff7, 0xde9: 0x100b,
+	0xdea: 0x1007, 0xdeb: 0x1037, 0xdec: 0x10b3, 0xded: 0x10cb, 0xdee: 0x10e3, 0xdef: 0x111b,
+	0xdf0: 0x112f, 0xdf1: 0x114b, 0xdf2: 0x117b, 0xdf3: 0x122f, 0xdf4: 0x1257, 0xdf5: 0x12cb,
+	0xdf6: 0x1313, 0xdf7: 0x131f, 0xdf8: 0x1327, 0xdf9: 0x133f, 0xdfa: 0x1353, 0xdfb: 0x1343,
+	0xdfc: 0x135b, 0xdfd: 0x1357, 0xdfe: 0x134f, 0xdff: 0x135f,
+	// Block 0x38, offset 0xe00
+	0xe00: 0x136b, 0xe01: 0x13a7, 0xe02: 0x13e3, 0xe03: 0x1413, 0xe04: 0x144b, 0xe05: 0x146b,
+	0xe06: 0x14b7, 0xe07: 0x14db, 0xe08: 0x14fb, 0xe09: 0x150f, 0xe0a: 0x151f, 0xe0b: 0x152b,
+	0xe0c: 0x1537, 0xe0d: 0x158b, 0xe0e: 0x162b, 0xe0f: 0x16b5, 0xe10: 0x16b0, 0xe11: 0x16e2,
+	0xe12: 0x0607, 0xe13: 0x062f, 0xe14: 0x0633, 0xe15: 0x1764, 0xe16: 0x1791, 0xe17: 0x1809,
+	0xe18: 0x1617, 0xe19: 0x1627,
+	// Block 0x39, offset 0xe40
+	0xe40: 0x19d5, 0xe41: 0x19d8, 0xe42: 0x19db, 0xe43: 0x1c08, 0xe44: 0x1c0c, 0xe45: 0x1a5f,
+	0xe46: 0x1a5f,
+	0xe53: 0x1d75, 0xe54: 0x1d66, 0xe55: 0x1d6b, 0xe56: 0x1d7a, 0xe57: 0x1d70,
+	0xe5d: 0x4426,
+	0xe5e: 0x8115, 0xe5f: 0x4498, 0xe60: 0x022d, 0xe61: 0x0215, 0xe62: 0x021e, 0xe63: 0x0221,
+	0xe64: 0x0224, 0xe65: 0x0227, 0xe66: 0x022a, 0xe67: 0x0230, 0xe68: 0x0233, 0xe69: 0x0017,
+	0xe6a: 0x4486, 0xe6b: 0x448c, 0xe6c: 0x458a, 0xe6d: 0x4592, 0xe6e: 0x43de, 0xe6f: 0x43e4,
+	0xe70: 0x43ea, 0xe71: 0x43f0, 0xe72: 0x43fc, 0xe73: 0x4402, 0xe74: 0x4408, 0xe75: 0x4414,
+	0xe76: 0x441a, 0xe78: 0x4420, 0xe79: 0x442c, 0xe7a: 0x4432, 0xe7b: 0x4438,
+	0xe7c: 0x4444, 0xe7e: 0x444a,
+	// Block 0x3a, offset 0xe80
+	0xe80: 0x4450, 0xe81: 0x4456, 0xe83: 0x445c, 0xe84: 0x4462,
+	0xe86: 0x446e, 0xe87: 0x4474, 0xe88: 0x447a, 0xe89: 0x4480, 0xe8a: 0x4492, 0xe8b: 0x440e,
+	0xe8c: 0x43f6, 0xe8d: 0x443e, 0xe8e: 0x4468, 0xe8f: 0x1d7f, 0xe90: 0x0299, 0xe91: 0x0299,
+	0xe92: 0x02a2, 0xe93: 0x02a2, 0xe94: 0x02a2, 0xe95: 0x02a2, 0xe96: 0x02a5, 0xe97: 0x02a5,
+	0xe98: 0x02a5, 0xe99: 0x02a5, 0xe9a: 0x02ab, 0xe9b: 0x02ab, 0xe9c: 0x02ab, 0xe9d: 0x02ab,
+	0xe9e: 0x029f, 0xe9f: 0x029f, 0xea0: 0x029f, 0xea1: 0x029f, 0xea2: 0x02a8, 0xea3: 0x02a8,
+	0xea4: 0x02a8, 0xea5: 0x02a8, 0xea6: 0x029c, 0xea7: 0x029c, 0xea8: 0x029c, 0xea9: 0x029c,
+	0xeaa: 0x02cf, 0xeab: 0x02cf, 0xeac: 0x02cf, 0xead: 0x02cf, 0xeae: 0x02d2, 0xeaf: 0x02d2,
+	0xeb0: 0x02d2, 0xeb1: 0x02d2, 0xeb2: 0x02b1, 0xeb3: 0x02b1, 0xeb4: 0x02b1, 0xeb5: 0x02b1,
+	0xeb6: 0x02ae, 0xeb7: 0x02ae, 0xeb8: 0x02ae, 0xeb9: 0x02ae, 0xeba: 0x02b4, 0xebb: 0x02b4,
+	0xebc: 0x02b4, 0xebd: 0x02b4, 0xebe: 0x02b7, 0xebf: 0x02b7,
+	// Block 0x3b, offset 0xec0
+	0xec0: 0x02b7, 0xec1: 0x02b7, 0xec2: 0x02c0, 0xec3: 0x02c0, 0xec4: 0x02bd, 0xec5: 0x02bd,
+	0xec6: 0x02c3, 0xec7: 0x02c3, 0xec8: 0x02ba, 0xec9: 0x02ba, 0xeca: 0x02c9, 0xecb: 0x02c9,
+	0xecc: 0x02c6, 0xecd: 0x02c6, 0xece: 0x02d5, 0xecf: 0x02d5, 0xed0: 0x02d5, 0xed1: 0x02d5,
+	0xed2: 0x02db, 0xed3: 0x02db, 0xed4: 0x02db, 0xed5: 0x02db, 0xed6: 0x02e1, 0xed7: 0x02e1,
+	0xed8: 0x02e1, 0xed9: 0x02e1, 0xeda: 0x02de, 0xedb: 0x02de, 0xedc: 0x02de, 0xedd: 0x02de,
+	0xede: 0x02e4, 0xedf: 0x02e4, 0xee0: 0x02e7, 0xee1: 0x02e7, 0xee2: 0x02e7, 0xee3: 0x02e7,
+	0xee4: 0x4504, 0xee5: 0x4504, 0xee6: 0x02ed, 0xee7: 0x02ed, 0xee8: 0x02ed, 0xee9: 0x02ed,
+	0xeea: 0x02ea, 0xeeb: 0x02ea, 0xeec: 0x02ea, 0xeed: 0x02ea, 0xeee: 0x0308, 0xeef: 0x0308,
+	0xef0: 0x44fe, 0xef1: 0x44fe,
+	// Block 0x3c, offset 0xf00
+	0xf13: 0x02d8, 0xf14: 0x02d8, 0xf15: 0x02d8, 0xf16: 0x02d8, 0xf17: 0x02f6,
+	0xf18: 0x02f6, 0xf19: 0x02f3, 0xf1a: 0x02f3, 0xf1b: 0x02f9, 0xf1c: 0x02f9, 0xf1d: 0x204f,
+	0xf1e: 0x02ff, 0xf1f: 0x02ff, 0xf20: 0x02f0, 0xf21: 0x02f0, 0xf22: 0x02fc, 0xf23: 0x02fc,
+	0xf24: 0x0305, 0xf25: 0x0305, 0xf26: 0x0305, 0xf27: 0x0305, 0xf28: 0x028d, 0xf29: 0x028d,
+	0xf2a: 0x25aa, 0xf2b: 0x25aa, 0xf2c: 0x261a, 0xf2d: 0x261a, 0xf2e: 0x25e9, 0xf2f: 0x25e9,
+	0xf30: 0x2605, 0xf31: 0x2605, 0xf32: 0x25fe, 0xf33: 0x25fe, 0xf34: 0x260c, 0xf35: 0x260c,
+	0xf36: 0x2613, 0xf37: 0x2613, 0xf38: 0x2613, 0xf39: 0x25f0, 0xf3a: 0x25f0, 0xf3b: 0x25f0,
+	0xf3c: 0x0302, 0xf3d: 0x0302, 0xf3e: 0x0302, 0xf3f: 0x0302,
+	// Block 0x3d, offset 0xf40
+	0xf40: 0x25b1, 0xf41: 0x25b8, 0xf42: 0x25d4, 0xf43: 0x25f0, 0xf44: 0x25f7, 0xf45: 0x1d89,
+	0xf46: 0x1d8e, 0xf47: 0x1d93, 0xf48: 0x1da2, 0xf49: 0x1db1, 0xf4a: 0x1db6, 0xf4b: 0x1dbb,
+	0xf4c: 0x1dc0, 0xf4d: 0x1dc5, 0xf4e: 0x1dd4, 0xf4f: 0x1de3, 0xf50: 0x1de8, 0xf51: 0x1ded,
+	0xf52: 0x1dfc, 0xf53: 0x1e0b, 0xf54: 0x1e10, 0xf55: 0x1e15, 0xf56: 0x1e1a, 0xf57: 0x1e29,
+	0xf58: 0x1e2e, 0xf59: 0x1e3d, 0xf5a: 0x1e42, 0xf5b: 0x1e47, 0xf5c: 0x1e56, 0xf5d: 0x1e5b,
+	0xf5e: 0x1e60, 0xf5f: 0x1e6a, 0xf60: 0x1ea6, 0xf61: 0x1eb5, 0xf62: 0x1ec4, 0xf63: 0x1ec9,
+	0xf64: 0x1ece, 0xf65: 0x1ed8, 0xf66: 0x1ee7, 0xf67: 0x1eec, 0xf68: 0x1efb, 0xf69: 0x1f00,
+	0xf6a: 0x1f05, 0xf6b: 0x1f14, 0xf6c: 0x1f19, 0xf6d: 0x1f28, 0xf6e: 0x1f2d, 0xf6f: 0x1f32,
+	0xf70: 0x1f37, 0xf71: 0x1f3c, 0xf72: 0x1f41, 0xf73: 0x1f46, 0xf74: 0x1f4b, 0xf75: 0x1f50,
+	0xf76: 0x1f55, 0xf77: 0x1f5a, 0xf78: 0x1f5f, 0xf79: 0x1f64, 0xf7a: 0x1f69, 0xf7b: 0x1f6e,
+	0xf7c: 0x1f73, 0xf7d: 0x1f78, 0xf7e: 0x1f7d, 0xf7f: 0x1f87,
+	// Block 0x3e, offset 0xf80
+	0xf80: 0x1f8c, 0xf81: 0x1f91, 0xf82: 0x1f96, 0xf83: 0x1fa0, 0xf84: 0x1fa5, 0xf85: 0x1faf,
+	0xf86: 0x1fb4, 0xf87: 0x1fb9, 0xf88: 0x1fbe, 0xf89: 0x1fc3, 0xf8a: 0x1fc8, 0xf8b: 0x1fcd,
+	0xf8c: 0x1fd2, 0xf8d: 0x1fd7, 0xf8e: 0x1fe6, 0xf8f: 0x1ff5, 0xf90: 0x1ffa, 0xf91: 0x1fff,
+	0xf92: 0x2004, 0xf93: 0x2009, 0xf94: 0x200e, 0xf95: 0x2018, 0xf96: 0x201d, 0xf97: 0x2022,
+	0xf98: 0x2031, 0xf99: 0x2040, 0xf9a: 0x2045, 0xf9b: 0x44b6, 0xf9c: 0x44bc, 0xf9d: 0x44f2,
+	0xf9e: 0x4549, 0xf9f: 0x4550, 0xfa0: 0x4557, 0xfa1: 0x455e, 0xfa2: 0x4565, 0xfa3: 0x456c,
+	0xfa4: 0x25c6, 0xfa5: 0x25cd, 0xfa6: 0x25d4, 0xfa7: 0x25db, 0xfa8: 0x25f0, 0xfa9: 0x25f7,
+	0xfaa: 0x1d98, 0xfab: 0x1d9d, 0xfac: 0x1da2, 0xfad: 0x1da7, 0xfae: 0x1db1, 0xfaf: 0x1db6,
+	0xfb0: 0x1dca, 0xfb1: 0x1dcf, 0xfb2: 0x1dd4, 0xfb3: 0x1dd9, 0xfb4: 0x1de3, 0xfb5: 0x1de8,
+	0xfb6: 0x1df2, 0xfb7: 0x1df7, 0xfb8: 0x1dfc, 0xfb9: 0x1e01, 0xfba: 0x1e0b, 0xfbb: 0x1e10,
+	0xfbc: 0x1f3c, 0xfbd: 0x1f41, 0xfbe: 0x1f50, 0xfbf: 0x1f55,
+	// Block 0x3f, offset 0xfc0
+	0xfc0: 0x1f5a, 0xfc1: 0x1f6e, 0xfc2: 0x1f73, 0xfc3: 0x1f78, 0xfc4: 0x1f7d, 0xfc5: 0x1f96,
+	0xfc6: 0x1fa0, 0xfc7: 0x1fa5, 0xfc8: 0x1faa, 0xfc9: 0x1fbe, 0xfca: 0x1fdc, 0xfcb: 0x1fe1,
+	0xfcc: 0x1fe6, 0xfcd: 0x1feb, 0xfce: 0x1ff5, 0xfcf: 0x1ffa, 0xfd0: 0x44f2, 0xfd1: 0x2027,
+	0xfd2: 0x202c, 0xfd3: 0x2031, 0xfd4: 0x2036, 0xfd5: 0x2040, 0xfd6: 0x2045, 0xfd7: 0x25b1,
+	0xfd8: 0x25b8, 0xfd9: 0x25bf, 0xfda: 0x25d4, 0xfdb: 0x25e2, 0xfdc: 0x1d89, 0xfdd: 0x1d8e,
+	0xfde: 0x1d93, 0xfdf: 0x1da2, 0xfe0: 0x1dac, 0xfe1: 0x1dbb, 0xfe2: 0x1dc0, 0xfe3: 0x1dc5,
+	0xfe4: 0x1dd4, 0xfe5: 0x1dde, 0xfe6: 0x1dfc, 0xfe7: 0x1e15, 0xfe8: 0x1e1a, 0xfe9: 0x1e29,
+	0xfea: 0x1e2e, 0xfeb: 0x1e3d, 0xfec: 0x1e47, 0xfed: 0x1e56, 0xfee: 0x1e5b, 0xfef: 0x1e60,
+	0xff0: 0x1e6a, 0xff1: 0x1ea6, 0xff2: 0x1eab, 0xff3: 0x1eb5, 0xff4: 0x1ec4, 0xff5: 0x1ec9,
+	0xff6: 0x1ece, 0xff7: 0x1ed8, 0xff8: 0x1ee7, 0xff9: 0x1efb, 0xffa: 0x1f00, 0xffb: 0x1f05,
+	0xffc: 0x1f14, 0xffd: 0x1f19, 0xffe: 0x1f28, 0xfff: 0x1f2d,
+	// Block 0x40, offset 0x1000
+	0x1000: 0x1f32, 0x1001: 0x1f37, 0x1002: 0x1f46, 0x1003: 0x1f4b, 0x1004: 0x1f5f, 0x1005: 0x1f64,
+	0x1006: 0x1f69, 0x1007: 0x1f6e, 0x1008: 0x1f73, 0x1009: 0x1f87, 0x100a: 0x1f8c, 0x100b: 0x1f91,
+	0x100c: 0x1f96, 0x100d: 0x1f9b, 0x100e: 0x1faf, 0x100f: 0x1fb4, 0x1010: 0x1fb9, 0x1011: 0x1fbe,
+	0x1012: 0x1fcd, 0x1013: 0x1fd2, 0x1014: 0x1fd7, 0x1015: 0x1fe6, 0x1016: 0x1ff0, 0x1017: 0x1fff,
+	0x1018: 0x2004, 0x1019: 0x44e6, 0x101a: 0x2018, 0x101b: 0x201d, 0x101c: 0x2022, 0x101d: 0x2031,
+	0x101e: 0x203b, 0x101f: 0x25d4, 0x1020: 0x25e2, 0x1021: 0x1da2, 0x1022: 0x1dac, 0x1023: 0x1dd4,
+	0x1024: 0x1dde, 0x1025: 0x1dfc, 0x1026: 0x1e06, 0x1027: 0x1e6a, 0x1028: 0x1e6f, 0x1029: 0x1e92,
+	0x102a: 0x1e97, 0x102b: 0x1f6e, 0x102c: 0x1f73, 0x102d: 0x1f96, 0x102e: 0x1fe6, 0x102f: 0x1ff0,
+	0x1030: 0x2031, 0x1031: 0x203b, 0x1032: 0x459a, 0x1033: 0x45a2, 0x1034: 0x45aa, 0x1035: 0x1ef1,
+	0x1036: 0x1ef6, 0x1037: 0x1f0a, 0x1038: 0x1f0f, 0x1039: 0x1f1e, 0x103a: 0x1f23, 0x103b: 0x1e74,
+	0x103c: 0x1e79, 0x103d: 0x1e9c, 0x103e: 0x1ea1, 0x103f: 0x1e33,
+	// Block 0x41, offset 0x1040
+	0x1040: 0x1e38, 0x1041: 0x1e1f, 0x1042: 0x1e24, 0x1043: 0x1e4c, 0x1044: 0x1e51, 0x1045: 0x1eba,
+	0x1046: 0x1ebf, 0x1047: 0x1edd, 0x1048: 0x1ee2, 0x1049: 0x1e7e, 0x104a: 0x1e83, 0x104b: 0x1e88,
+	0x104c: 0x1e92, 0x104d: 0x1e8d, 0x104e: 0x1e65, 0x104f: 0x1eb0, 0x1050: 0x1ed3, 0x1051: 0x1ef1,
+	0x1052: 0x1ef6, 0x1053: 0x1f0a, 0x1054: 0x1f0f, 0x1055: 0x1f1e, 0x1056: 0x1f23, 0x1057: 0x1e74,
+	0x1058: 0x1e79, 0x1059: 0x1e9c, 0x105a: 0x1ea1, 0x105b: 0x1e33, 0x105c: 0x1e38, 0x105d: 0x1e1f,
+	0x105e: 0x1e24, 0x105f: 0x1e4c, 0x1060: 0x1e51, 0x1061: 0x1eba, 0x1062: 0x1ebf, 0x1063: 0x1edd,
+	0x1064: 0x1ee2, 0x1065: 0x1e7e, 0x1066: 0x1e83, 0x1067: 0x1e88, 0x1068: 0x1e92, 0x1069: 0x1e8d,
+	0x106a: 0x1e65, 0x106b: 0x1eb0, 0x106c: 0x1ed3, 0x106d: 0x1e7e, 0x106e: 0x1e83, 0x106f: 0x1e88,
+	0x1070: 0x1e92, 0x1071: 0x1e6f, 0x1072: 0x1e97, 0x1073: 0x1eec, 0x1074: 0x1e56, 0x1075: 0x1e5b,
+	0x1076: 0x1e60, 0x1077: 0x1e7e, 0x1078: 0x1e83, 0x1079: 0x1e88, 0x107a: 0x1eec, 0x107b: 0x1efb,
+	0x107c: 0x449e, 0x107d: 0x449e,
+	// Block 0x42, offset 0x1080
+	0x1090: 0x2311, 0x1091: 0x2326,
+	0x1092: 0x2326, 0x1093: 0x232d, 0x1094: 0x2334, 0x1095: 0x2349, 0x1096: 0x2350, 0x1097: 0x2357,
+	0x1098: 0x237a, 0x1099: 0x237a, 0x109a: 0x239d, 0x109b: 0x2396, 0x109c: 0x23b2, 0x109d: 0x23a4,
+	0x109e: 0x23ab, 0x109f: 0x23ce, 0x10a0: 0x23ce, 0x10a1: 0x23c7, 0x10a2: 0x23d5, 0x10a3: 0x23d5,
+	0x10a4: 0x23ff, 0x10a5: 0x23ff, 0x10a6: 0x241b, 0x10a7: 0x23e3, 0x10a8: 0x23e3, 0x10a9: 0x23dc,
+	0x10aa: 0x23f1, 0x10ab: 0x23f1, 0x10ac: 0x23f8, 0x10ad: 0x23f8, 0x10ae: 0x2422, 0x10af: 0x2430,
+	0x10b0: 0x2430, 0x10b1: 0x2437, 0x10b2: 0x2437, 0x10b3: 0x243e, 0x10b4: 0x2445, 0x10b5: 0x244c,
+	0x10b6: 0x2453, 0x10b7: 0x2453, 0x10b8: 0x245a, 0x10b9: 0x2468, 0x10ba: 0x2476, 0x10bb: 0x246f,
+	0x10bc: 0x247d, 0x10bd: 0x247d, 0x10be: 0x2492, 0x10bf: 0x2499,
+	// Block 0x43, offset 0x10c0
+	0x10c0: 0x24ca, 0x10c1: 0x24d8, 0x10c2: 0x24d1, 0x10c3: 0x24b5, 0x10c4: 0x24b5, 0x10c5: 0x24df,
+	0x10c6: 0x24df, 0x10c7: 0x24e6, 0x10c8: 0x24e6, 0x10c9: 0x2510, 0x10ca: 0x2517, 0x10cb: 0x251e,
+	0x10cc: 0x24f4, 0x10cd: 0x2502, 0x10ce: 0x2525, 0x10cf: 0x252c,
+	0x10d2: 0x24fb, 0x10d3: 0x2580, 0x10d4: 0x2587, 0x10d5: 0x255d, 0x10d6: 0x2564, 0x10d7: 0x2548,
+	0x10d8: 0x2548, 0x10d9: 0x254f, 0x10da: 0x2579, 0x10db: 0x2572, 0x10dc: 0x259c, 0x10dd: 0x259c,
+	0x10de: 0x230a, 0x10df: 0x231f, 0x10e0: 0x2318, 0x10e1: 0x2342, 0x10e2: 0x233b, 0x10e3: 0x2365,
+	0x10e4: 0x235e, 0x10e5: 0x2388, 0x10e6: 0x236c, 0x10e7: 0x2381, 0x10e8: 0x23b9, 0x10e9: 0x2406,
+	0x10ea: 0x23ea, 0x10eb: 0x2429, 0x10ec: 0x24c3, 0x10ed: 0x24ed, 0x10ee: 0x2595, 0x10ef: 0x258e,
+	0x10f0: 0x25a3, 0x10f1: 0x253a, 0x10f2: 0x24a0, 0x10f3: 0x256b, 0x10f4: 0x2492, 0x10f5: 0x24ca,
+	0x10f6: 0x2461, 0x10f7: 0x24ae, 0x10f8: 0x2541, 0x10f9: 0x2533, 0x10fa: 0x24bc, 0x10fb: 0x24a7,
+	0x10fc: 0x24bc, 0x10fd: 0x2541, 0x10fe: 0x2373, 0x10ff: 0x238f,
+	// Block 0x44, offset 0x1100
+	0x1100: 0x2509, 0x1101: 0x2484, 0x1102: 0x2303, 0x1103: 0x24a7, 0x1104: 0x244c, 0x1105: 0x241b,
+	0x1106: 0x23c0, 0x1107: 0x2556,
+	0x1130: 0x2414, 0x1131: 0x248b, 0x1132: 0x27bf, 0x1133: 0x27b6, 0x1134: 0x27ec, 0x1135: 0x27da,
+	0x1136: 0x27c8, 0x1137: 0x27e3, 0x1138: 0x27f5, 0x1139: 0x240d, 0x113a: 0x2c7c, 0x113b: 0x2afc,
+	0x113c: 0x27d1,
+	// Block 0x45, offset 0x1140
+	0x1150: 0x0019, 0x1151: 0x0483,
+	0x1152: 0x0487, 0x1153: 0x0035, 0x1154: 0x0037, 0x1155: 0x0003, 0x1156: 0x003f, 0x1157: 0x04bf,
+	0x1158: 0x04c3, 0x1159: 0x1b5c,
+	0x1160: 0x8132, 0x1161: 0x8132, 0x1162: 0x8132, 0x1163: 0x8132,
+	0x1164: 0x8132, 0x1165: 0x8132, 0x1166: 0x8132, 0x1167: 0x812d, 0x1168: 0x812d, 0x1169: 0x812d,
+	0x116a: 0x812d, 0x116b: 0x812d, 0x116c: 0x812d, 0x116d: 0x812d, 0x116e: 0x8132, 0x116f: 0x8132,
+	0x1170: 0x1873, 0x1171: 0x0443, 0x1172: 0x043f, 0x1173: 0x007f, 0x1174: 0x007f, 0x1175: 0x0011,
+	0x1176: 0x0013, 0x1177: 0x00b7, 0x1178: 0x00bb, 0x1179: 0x04b7, 0x117a: 0x04bb, 0x117b: 0x04ab,
+	0x117c: 0x04af, 0x117d: 0x0493, 0x117e: 0x0497, 0x117f: 0x048b,
+	// Block 0x46, offset 0x1180
+	0x1180: 0x048f, 0x1181: 0x049b, 0x1182: 0x049f, 0x1183: 0x04a3, 0x1184: 0x04a7,
+	0x1187: 0x0077, 0x1188: 0x007b, 0x1189: 0x4269, 0x118a: 0x4269, 0x118b: 0x4269,
+	0x118c: 0x4269, 0x118d: 0x007f, 0x118e: 0x007f, 0x118f: 0x007f, 0x1190: 0x0019, 0x1191: 0x0483,
+	0x1192: 0x001d, 0x1194: 0x0037, 0x1195: 0x0035, 0x1196: 0x003f, 0x1197: 0x0003,
+	0x1198: 0x0443, 0x1199: 0x0011, 0x119a: 0x0013, 0x119b: 0x00b7, 0x119c: 0x00bb, 0x119d: 0x04b7,
+	0x119e: 0x04bb, 0x119f: 0x0007, 0x11a0: 0x000d, 0x11a1: 0x0015, 0x11a2: 0x0017, 0x11a3: 0x001b,
+	0x11a4: 0x0039, 0x11a5: 0x003d, 0x11a6: 0x003b, 0x11a8: 0x0079, 0x11a9: 0x0009,
+	0x11aa: 0x000b, 0x11ab: 0x0041,
+	0x11b0: 0x42aa, 0x11b1: 0x44c2, 0x11b2: 0x42af, 0x11b4: 0x42b4,
+	0x11b6: 0x42b9, 0x11b7: 0x44c8, 0x11b8: 0x42be, 0x11b9: 0x44ce, 0x11ba: 0x42c3, 0x11bb: 0x44d4,
+	0x11bc: 0x42c8, 0x11bd: 0x44da, 0x11be: 0x42cd, 0x11bf: 0x44e0,
+	// Block 0x47, offset 0x11c0
+	0x11c0: 0x0236, 0x11c1: 0x44a4, 0x11c2: 0x44a4, 0x11c3: 0x44aa, 0x11c4: 0x44aa, 0x11c5: 0x44ec,
+	0x11c6: 0x44ec, 0x11c7: 0x44b0, 0x11c8: 0x44b0, 0x11c9: 0x44f8, 0x11ca: 0x44f8, 0x11cb: 0x44f8,
+	0x11cc: 0x44f8, 0x11cd: 0x0239, 0x11ce: 0x0239, 0x11cf: 0x023c, 0x11d0: 0x023c, 0x11d1: 0x023c,
+	0x11d2: 0x023c, 0x11d3: 0x023f, 0x11d4: 0x023f, 0x11d5: 0x0242, 0x11d6: 0x0242, 0x11d7: 0x0242,
+	0x11d8: 0x0242, 0x11d9: 0x0245, 0x11da: 0x0245, 0x11db: 0x0245, 0x11dc: 0x0245, 0x11dd: 0x0248,
+	0x11de: 0x0248, 0x11df: 0x0248, 0x11e0: 0x0248, 0x11e1: 0x024b, 0x11e2: 0x024b, 0x11e3: 0x024b,
+	0x11e4: 0x024b, 0x11e5: 0x024e, 0x11e6: 0x024e, 0x11e7: 0x024e, 0x11e8: 0x024e, 0x11e9: 0x0251,
+	0x11ea: 0x0251, 0x11eb: 0x0254, 0x11ec: 0x0254, 0x11ed: 0x0257, 0x11ee: 0x0257, 0x11ef: 0x025a,
+	0x11f0: 0x025a, 0x11f1: 0x025d, 0x11f2: 0x025d, 0x11f3: 0x025d, 0x11f4: 0x025d, 0x11f5: 0x0260,
+	0x11f6: 0x0260, 0x11f7: 0x0260, 0x11f8: 0x0260, 0x11f9: 0x0263, 0x11fa: 0x0263, 0x11fb: 0x0263,
+	0x11fc: 0x0263, 0x11fd: 0x0266, 0x11fe: 0x0266, 0x11ff: 0x0266,
+	// Block 0x48, offset 0x1200
+	0x1200: 0x0266, 0x1201: 0x0269, 0x1202: 0x0269, 0x1203: 0x0269, 0x1204: 0x0269, 0x1205: 0x026c,
+	0x1206: 0x026c, 0x1207: 0x026c, 0x1208: 0x026c, 0x1209: 0x026f, 0x120a: 0x026f, 0x120b: 0x026f,
+	0x120c: 0x026f, 0x120d: 0x0272, 0x120e: 0x0272, 0x120f: 0x0272, 0x1210: 0x0272, 0x1211: 0x0275,
+	0x1212: 0x0275, 0x1213: 0x0275, 0x1214: 0x0275, 0x1215: 0x0278, 0x1216: 0x0278, 0x1217: 0x0278,
+	0x1218: 0x0278, 0x1219: 0x027b, 0x121a: 0x027b, 0x121b: 0x027b, 0x121c: 0x027b, 0x121d: 0x027e,
+	0x121e: 0x027e, 0x121f: 0x027e, 0x1220: 0x027e, 0x1221: 0x0281, 0x1222: 0x0281, 0x1223: 0x0281,
+	0x1224: 0x0281, 0x1225: 0x0284, 0x1226: 0x0284, 0x1227: 0x0284, 0x1228: 0x0284, 0x1229: 0x0287,
+	0x122a: 0x0287, 0x122b: 0x0287, 0x122c: 0x0287, 0x122d: 0x028a, 0x122e: 0x028a, 0x122f: 0x028d,
+	0x1230: 0x028d, 0x1231: 0x0290, 0x1232: 0x0290, 0x1233: 0x0290, 0x1234: 0x0290, 0x1235: 0x2e00,
+	0x1236: 0x2e00, 0x1237: 0x2e08, 0x1238: 0x2e08, 0x1239: 0x2e10, 0x123a: 0x2e10, 0x123b: 0x1f82,
+	0x123c: 0x1f82,
+	// Block 0x49, offset 0x1240
+	0x1240: 0x0081, 0x1241: 0x0083, 0x1242: 0x0085, 0x1243: 0x0087, 0x1244: 0x0089, 0x1245: 0x008b,
+	0x1246: 0x008d, 0x1247: 0x008f, 0x1248: 0x0091, 0x1249: 0x0093, 0x124a: 0x0095, 0x124b: 0x0097,
+	0x124c: 0x0099, 0x124d: 0x009b, 0x124e: 0x009d, 0x124f: 0x009f, 0x1250: 0x00a1, 0x1251: 0x00a3,
+	0x1252: 0x00a5, 0x1253: 0x00a7, 0x1254: 0x00a9, 0x1255: 0x00ab, 0x1256: 0x00ad, 0x1257: 0x00af,
+	0x1258: 0x00b1, 0x1259: 0x00b3, 0x125a: 0x00b5, 0x125b: 0x00b7, 0x125c: 0x00b9, 0x125d: 0x00bb,
+	0x125e: 0x00bd, 0x125f: 0x0477, 0x1260: 0x047b, 0x1261: 0x0487, 0x1262: 0x049b, 0x1263: 0x049f,
+	0x1264: 0x0483, 0x1265: 0x05ab, 0x1266: 0x05a3, 0x1267: 0x04c7, 0x1268: 0x04cf, 0x1269: 0x04d7,
+	0x126a: 0x04df, 0x126b: 0x04e7, 0x126c: 0x056b, 0x126d: 0x0573, 0x126e: 0x057b, 0x126f: 0x051f,
+	0x1270: 0x05af, 0x1271: 0x04cb, 0x1272: 0x04d3, 0x1273: 0x04db, 0x1274: 0x04e3, 0x1275: 0x04eb,
+	0x1276: 0x04ef, 0x1277: 0x04f3, 0x1278: 0x04f7, 0x1279: 0x04fb, 0x127a: 0x04ff, 0x127b: 0x0503,
+	0x127c: 0x0507, 0x127d: 0x050b, 0x127e: 0x050f, 0x127f: 0x0513,
+	// Block 0x4a, offset 0x1280
+	0x1280: 0x0517, 0x1281: 0x051b, 0x1282: 0x0523, 0x1283: 0x0527, 0x1284: 0x052b, 0x1285: 0x052f,
+	0x1286: 0x0533, 0x1287: 0x0537, 0x1288: 0x053b, 0x1289: 0x053f, 0x128a: 0x0543, 0x128b: 0x0547,
+	0x128c: 0x054b, 0x128d: 0x054f, 0x128e: 0x0553, 0x128f: 0x0557, 0x1290: 0x055b, 0x1291: 0x055f,
+	0x1292: 0x0563, 0x1293: 0x0567, 0x1294: 0x056f, 0x1295: 0x0577, 0x1296: 0x057f, 0x1297: 0x0583,
+	0x1298: 0x0587, 0x1299: 0x058b, 0x129a: 0x058f, 0x129b: 0x0593, 0x129c: 0x0597, 0x129d: 0x05a7,
+	0x129e: 0x4a5a, 0x129f: 0x4a60, 0x12a0: 0x03c3, 0x12a1: 0x0313, 0x12a2: 0x0317, 0x12a3: 0x4345,
+	0x12a4: 0x031b, 0x12a5: 0x434a, 0x12a6: 0x434f, 0x12a7: 0x031f, 0x12a8: 0x0323, 0x12a9: 0x0327,
+	0x12aa: 0x4354, 0x12ab: 0x4359, 0x12ac: 0x435e, 0x12ad: 0x4363, 0x12ae: 0x4368, 0x12af: 0x436d,
+	0x12b0: 0x0367, 0x12b1: 0x032b, 0x12b2: 0x032f, 0x12b3: 0x0333, 0x12b4: 0x037b, 0x12b5: 0x0337,
+	0x12b6: 0x033b, 0x12b7: 0x033f, 0x12b8: 0x0343, 0x12b9: 0x0347, 0x12ba: 0x034b, 0x12bb: 0x034f,
+	0x12bc: 0x0353, 0x12bd: 0x0357, 0x12be: 0x035b,
+	// Block 0x4b, offset 0x12c0
+	0x12c2: 0x42dc, 0x12c3: 0x42e1, 0x12c4: 0x42e6, 0x12c5: 0x42eb,
+	0x12c6: 0x42f0, 0x12c7: 0x42f5, 0x12ca: 0x42fa, 0x12cb: 0x42ff,
+	0x12cc: 0x4304, 0x12cd: 0x4309, 0x12ce: 0x430e, 0x12cf: 0x4313,
+	0x12d2: 0x4318, 0x12d3: 0x431d, 0x12d4: 0x4322, 0x12d5: 0x4327, 0x12d6: 0x432c, 0x12d7: 0x4331,
+	0x12da: 0x4336, 0x12db: 0x433b, 0x12dc: 0x4340,
+	0x12e0: 0x00bf, 0x12e1: 0x00c2, 0x12e2: 0x00cb, 0x12e3: 0x4264,
+	0x12e4: 0x00c8, 0x12e5: 0x00c5, 0x12e6: 0x0447, 0x12e8: 0x046b, 0x12e9: 0x044b,
+	0x12ea: 0x044f, 0x12eb: 0x0453, 0x12ec: 0x0457, 0x12ed: 0x046f, 0x12ee: 0x0473,
+	// Block 0x4c, offset 0x1300
+	0x1300: 0x0063, 0x1301: 0x0065, 0x1302: 0x0067, 0x1303: 0x0069, 0x1304: 0x006b, 0x1305: 0x006d,
+	0x1306: 0x006f, 0x1307: 0x0071, 0x1308: 0x0073, 0x1309: 0x0075, 0x130a: 0x0083, 0x130b: 0x0085,
+	0x130c: 0x0087, 0x130d: 0x0089, 0x130e: 0x008b, 0x130f: 0x008d, 0x1310: 0x008f, 0x1311: 0x0091,
+	0x1312: 0x0093, 0x1313: 0x0095, 0x1314: 0x0097, 0x1315: 0x0099, 0x1316: 0x009b, 0x1317: 0x009d,
+	0x1318: 0x009f, 0x1319: 0x00a1, 0x131a: 0x00a3, 0x131b: 0x00a5, 0x131c: 0x00a7, 0x131d: 0x00a9,
+	0x131e: 0x00ab, 0x131f: 0x00ad, 0x1320: 0x00af, 0x1321: 0x00b1, 0x1322: 0x00b3, 0x1323: 0x00b5,
+	0x1324: 0x00dd, 0x1325: 0x00f2, 0x1328: 0x0173, 0x1329: 0x0176,
+	0x132a: 0x0179, 0x132b: 0x017c, 0x132c: 0x017f, 0x132d: 0x0182, 0x132e: 0x0185, 0x132f: 0x0188,
+	0x1330: 0x018b, 0x1331: 0x018e, 0x1332: 0x0191, 0x1333: 0x0194, 0x1334: 0x0197, 0x1335: 0x019a,
+	0x1336: 0x019d, 0x1337: 0x01a0, 0x1338: 0x01a3, 0x1339: 0x0188, 0x133a: 0x01a6, 0x133b: 0x01a9,
+	0x133c: 0x01ac, 0x133d: 0x01af, 0x133e: 0x01b2, 0x133f: 0x01b5,
+	// Block 0x4d, offset 0x1340
+	0x1340: 0x01fd, 0x1341: 0x0200, 0x1342: 0x0203, 0x1343: 0x045b, 0x1344: 0x01c7, 0x1345: 0x01d0,
+	0x1346: 0x01d6, 0x1347: 0x01fa, 0x1348: 0x01eb, 0x1349: 0x01e8, 0x134a: 0x0206, 0x134b: 0x0209,
+	0x134e: 0x0021, 0x134f: 0x0023, 0x1350: 0x0025, 0x1351: 0x0027,
+	0x1352: 0x0029, 0x1353: 0x002b, 0x1354: 0x002d, 0x1355: 0x002f, 0x1356: 0x0031, 0x1357: 0x0033,
+	0x1358: 0x0021, 0x1359: 0x0023, 0x135a: 0x0025, 0x135b: 0x0027, 0x135c: 0x0029, 0x135d: 0x002b,
+	0x135e: 0x002d, 0x135f: 0x002f, 0x1360: 0x0031, 0x1361: 0x0033, 0x1362: 0x0021, 0x1363: 0x0023,
+	0x1364: 0x0025, 0x1365: 0x0027, 0x1366: 0x0029, 0x1367: 0x002b, 0x1368: 0x002d, 0x1369: 0x002f,
+	0x136a: 0x0031, 0x136b: 0x0033, 0x136c: 0x0021, 0x136d: 0x0023, 0x136e: 0x0025, 0x136f: 0x0027,
+	0x1370: 0x0029, 0x1371: 0x002b, 0x1372: 0x002d, 0x1373: 0x002f, 0x1374: 0x0031, 0x1375: 0x0033,
+	0x1376: 0x0021, 0x1377: 0x0023, 0x1378: 0x0025, 0x1379: 0x0027, 0x137a: 0x0029, 0x137b: 0x002b,
+	0x137c: 0x002d, 0x137d: 0x002f, 0x137e: 0x0031, 0x137f: 0x0033,
+	// Block 0x4e, offset 0x1380
+	0x1380: 0x0239, 0x1381: 0x023c, 0x1382: 0x0248, 0x1383: 0x0251, 0x1385: 0x028a,
+	0x1386: 0x025a, 0x1387: 0x024b, 0x1388: 0x0269, 0x1389: 0x0290, 0x138a: 0x027b, 0x138b: 0x027e,
+	0x138c: 0x0281, 0x138d: 0x0284, 0x138e: 0x025d, 0x138f: 0x026f, 0x1390: 0x0275, 0x1391: 0x0263,
+	0x1392: 0x0278, 0x1393: 0x0257, 0x1394: 0x0260, 0x1395: 0x0242, 0x1396: 0x0245, 0x1397: 0x024e,
+	0x1398: 0x0254, 0x1399: 0x0266, 0x139a: 0x026c, 0x139b: 0x0272, 0x139c: 0x0293, 0x139d: 0x02e4,
+	0x139e: 0x02cc, 0x139f: 0x0296, 0x13a1: 0x023c, 0x13a2: 0x0248,
+	0x13a4: 0x0287, 0x13a7: 0x024b, 0x13a9: 0x0290,
+	0x13aa: 0x027b, 0x13ab: 0x027e, 0x13ac: 0x0281, 0x13ad: 0x0284, 0x13ae: 0x025d, 0x13af: 0x026f,
+	0x13b0: 0x0275, 0x13b1: 0x0263, 0x13b2: 0x0278, 0x13b4: 0x0260, 0x13b5: 0x0242,
+	0x13b6: 0x0245, 0x13b7: 0x024e, 0x13b9: 0x0266, 0x13bb: 0x0272,
+	// Block 0x4f, offset 0x13c0
+	0x13c2: 0x0248,
+	0x13c7: 0x024b, 0x13c9: 0x0290, 0x13cb: 0x027e,
+	0x13cd: 0x0284, 0x13ce: 0x025d, 0x13cf: 0x026f, 0x13d1: 0x0263,
+	0x13d2: 0x0278, 0x13d4: 0x0260, 0x13d7: 0x024e,
+	0x13d9: 0x0266, 0x13db: 0x0272, 0x13dd: 0x02e4,
+	0x13df: 0x0296, 0x13e1: 0x023c, 0x13e2: 0x0248,
+	0x13e4: 0x0287, 0x13e7: 0x024b, 0x13e8: 0x0269, 0x13e9: 0x0290,
+	0x13ea: 0x027b, 0x13ec: 0x0281, 0x13ed: 0x0284, 0x13ee: 0x025d, 0x13ef: 0x026f,
+	0x13f0: 0x0275, 0x13f1: 0x0263, 0x13f2: 0x0278, 0x13f4: 0x0260, 0x13f5: 0x0242,
+	0x13f6: 0x0245, 0x13f7: 0x024e, 0x13f9: 0x0266, 0x13fa: 0x026c, 0x13fb: 0x0272,
+	0x13fc: 0x0293, 0x13fe: 0x02cc,
+	// Block 0x50, offset 0x1400
+	0x1400: 0x0239, 0x1401: 0x023c, 0x1402: 0x0248, 0x1403: 0x0251, 0x1404: 0x0287, 0x1405: 0x028a,
+	0x1406: 0x025a, 0x1407: 0x024b, 0x1408: 0x0269, 0x1409: 0x0290, 0x140b: 0x027e,
+	0x140c: 0x0281, 0x140d: 0x0284, 0x140e: 0x025d, 0x140f: 0x026f, 0x1410: 0x0275, 0x1411: 0x0263,
+	0x1412: 0x0278, 0x1413: 0x0257, 0x1414: 0x0260, 0x1415: 0x0242, 0x1416: 0x0245, 0x1417: 0x024e,
+	0x1418: 0x0254, 0x1419: 0x0266, 0x141a: 0x026c, 0x141b: 0x0272,
+	0x1421: 0x023c, 0x1422: 0x0248, 0x1423: 0x0251,
+	0x1425: 0x028a, 0x1426: 0x025a, 0x1427: 0x024b, 0x1428: 0x0269, 0x1429: 0x0290,
+	0x142b: 0x027e, 0x142c: 0x0281, 0x142d: 0x0284, 0x142e: 0x025d, 0x142f: 0x026f,
+	0x1430: 0x0275, 0x1431: 0x0263, 0x1432: 0x0278, 0x1433: 0x0257, 0x1434: 0x0260, 0x1435: 0x0242,
+	0x1436: 0x0245, 0x1437: 0x024e, 0x1438: 0x0254, 0x1439: 0x0266, 0x143a: 0x026c, 0x143b: 0x0272,
+	// Block 0x51, offset 0x1440
+	0x1440: 0x1879, 0x1441: 0x1876, 0x1442: 0x187c, 0x1443: 0x18a0, 0x1444: 0x18c4, 0x1445: 0x18e8,
+	0x1446: 0x190c, 0x1447: 0x1915, 0x1448: 0x191b, 0x1449: 0x1921, 0x144a: 0x1927,
+	0x1450: 0x1a8c, 0x1451: 0x1a90,
+	0x1452: 0x1a94, 0x1453: 0x1a98, 0x1454: 0x1a9c, 0x1455: 0x1aa0, 0x1456: 0x1aa4, 0x1457: 0x1aa8,
+	0x1458: 0x1aac, 0x1459: 0x1ab0, 0x145a: 0x1ab4, 0x145b: 0x1ab8, 0x145c: 0x1abc, 0x145d: 0x1ac0,
+	0x145e: 0x1ac4, 0x145f: 0x1ac8, 0x1460: 0x1acc, 0x1461: 0x1ad0, 0x1462: 0x1ad4, 0x1463: 0x1ad8,
+	0x1464: 0x1adc, 0x1465: 0x1ae0, 0x1466: 0x1ae4, 0x1467: 0x1ae8, 0x1468: 0x1aec, 0x1469: 0x1af0,
+	0x146a: 0x271e, 0x146b: 0x0047, 0x146c: 0x0065, 0x146d: 0x193c, 0x146e: 0x19b1,
+	0x1470: 0x0043, 0x1471: 0x0045, 0x1472: 0x0047, 0x1473: 0x0049, 0x1474: 0x004b, 0x1475: 0x004d,
+	0x1476: 0x004f, 0x1477: 0x0051, 0x1478: 0x0053, 0x1479: 0x0055, 0x147a: 0x0057, 0x147b: 0x0059,
+	0x147c: 0x005b, 0x147d: 0x005d, 0x147e: 0x005f, 0x147f: 0x0061,
+	// Block 0x52, offset 0x1480
+	0x1480: 0x26ad, 0x1481: 0x26c2, 0x1482: 0x0503,
+	0x1490: 0x0c0f, 0x1491: 0x0a47,
+	0x1492: 0x08d3, 0x1493: 0x465a, 0x1494: 0x071b, 0x1495: 0x09ef, 0x1496: 0x132f, 0x1497: 0x09ff,
+	0x1498: 0x0727, 0x1499: 0x0cd7, 0x149a: 0x0eaf, 0x149b: 0x0caf, 0x149c: 0x0827, 0x149d: 0x0b6b,
+	0x149e: 0x07bf, 0x149f: 0x0cb7, 0x14a0: 0x0813, 0x14a1: 0x1117, 0x14a2: 0x0f83, 0x14a3: 0x138b,
+	0x14a4: 0x09d3, 0x14a5: 0x090b, 0x14a6: 0x0e63, 0x14a7: 0x0c1b, 0x14a8: 0x0c47, 0x14a9: 0x06bf,
+	0x14aa: 0x06cb, 0x14ab: 0x140b, 0x14ac: 0x0adb, 0x14ad: 0x06e7, 0x14ae: 0x08ef, 0x14af: 0x0c3b,
+	0x14b0: 0x13b3, 0x14b1: 0x0c13, 0x14b2: 0x106f, 0x14b3: 0x10ab, 0x14b4: 0x08f7, 0x14b5: 0x0e43,
+	0x14b6: 0x0d0b, 0x14b7: 0x0d07, 0x14b8: 0x0f97, 0x14b9: 0x082b, 0x14ba: 0x0957, 0x14bb: 0x1443,
+	// Block 0x53, offset 0x14c0
+	0x14c0: 0x06fb, 0x14c1: 0x06f3, 0x14c2: 0x0703, 0x14c3: 0x1647, 0x14c4: 0x0747, 0x14c5: 0x0757,
+	0x14c6: 0x075b, 0x14c7: 0x0763, 0x14c8: 0x076b, 0x14c9: 0x076f, 0x14ca: 0x077b, 0x14cb: 0x0773,
+	0x14cc: 0x05b3, 0x14cd: 0x165b, 0x14ce: 0x078f, 0x14cf: 0x0793, 0x14d0: 0x0797, 0x14d1: 0x07b3,
+	0x14d2: 0x164c, 0x14d3: 0x05b7, 0x14d4: 0x079f, 0x14d5: 0x07bf, 0x14d6: 0x1656, 0x14d7: 0x07cf,
+	0x14d8: 0x07d7, 0x14d9: 0x0737, 0x14da: 0x07df, 0x14db: 0x07e3, 0x14dc: 0x1831, 0x14dd: 0x07ff,
+	0x14de: 0x0807, 0x14df: 0x05bf, 0x14e0: 0x081f, 0x14e1: 0x0823, 0x14e2: 0x082b, 0x14e3: 0x082f,
+	0x14e4: 0x05c3, 0x14e5: 0x0847, 0x14e6: 0x084b, 0x14e7: 0x0857, 0x14e8: 0x0863, 0x14e9: 0x0867,
+	0x14ea: 0x086b, 0x14eb: 0x0873, 0x14ec: 0x0893, 0x14ed: 0x0897, 0x14ee: 0x089f, 0x14ef: 0x08af,
+	0x14f0: 0x08b7, 0x14f1: 0x08bb, 0x14f2: 0x08bb, 0x14f3: 0x08bb, 0x14f4: 0x166a, 0x14f5: 0x0e93,
+	0x14f6: 0x08cf, 0x14f7: 0x08d7, 0x14f8: 0x166f, 0x14f9: 0x08e3, 0x14fa: 0x08eb, 0x14fb: 0x08f3,
+	0x14fc: 0x091b, 0x14fd: 0x0907, 0x14fe: 0x0913, 0x14ff: 0x0917,
+	// Block 0x54, offset 0x1500
+	0x1500: 0x091f, 0x1501: 0x0927, 0x1502: 0x092b, 0x1503: 0x0933, 0x1504: 0x093b, 0x1505: 0x093f,
+	0x1506: 0x093f, 0x1507: 0x0947, 0x1508: 0x094f, 0x1509: 0x0953, 0x150a: 0x095f, 0x150b: 0x0983,
+	0x150c: 0x0967, 0x150d: 0x0987, 0x150e: 0x096b, 0x150f: 0x0973, 0x1510: 0x080b, 0x1511: 0x09cf,
+	0x1512: 0x0997, 0x1513: 0x099b, 0x1514: 0x099f, 0x1515: 0x0993, 0x1516: 0x09a7, 0x1517: 0x09a3,
+	0x1518: 0x09bb, 0x1519: 0x1674, 0x151a: 0x09d7, 0x151b: 0x09db, 0x151c: 0x09e3, 0x151d: 0x09ef,
+	0x151e: 0x09f7, 0x151f: 0x0a13, 0x1520: 0x1679, 0x1521: 0x167e, 0x1522: 0x0a1f, 0x1523: 0x0a23,
+	0x1524: 0x0a27, 0x1525: 0x0a1b, 0x1526: 0x0a2f, 0x1527: 0x05c7, 0x1528: 0x05cb, 0x1529: 0x0a37,
+	0x152a: 0x0a3f, 0x152b: 0x0a3f, 0x152c: 0x1683, 0x152d: 0x0a5b, 0x152e: 0x0a5f, 0x152f: 0x0a63,
+	0x1530: 0x0a6b, 0x1531: 0x1688, 0x1532: 0x0a73, 0x1533: 0x0a77, 0x1534: 0x0b4f, 0x1535: 0x0a7f,
+	0x1536: 0x05cf, 0x1537: 0x0a8b, 0x1538: 0x0a9b, 0x1539: 0x0aa7, 0x153a: 0x0aa3, 0x153b: 0x1692,
+	0x153c: 0x0aaf, 0x153d: 0x1697, 0x153e: 0x0abb, 0x153f: 0x0ab7,
+	// Block 0x55, offset 0x1540
+	0x1540: 0x0abf, 0x1541: 0x0acf, 0x1542: 0x0ad3, 0x1543: 0x05d3, 0x1544: 0x0ae3, 0x1545: 0x0aeb,
+	0x1546: 0x0aef, 0x1547: 0x0af3, 0x1548: 0x05d7, 0x1549: 0x169c, 0x154a: 0x05db, 0x154b: 0x0b0f,
+	0x154c: 0x0b13, 0x154d: 0x0b17, 0x154e: 0x0b1f, 0x154f: 0x1863, 0x1550: 0x0b37, 0x1551: 0x16a6,
+	0x1552: 0x16a6, 0x1553: 0x11d7, 0x1554: 0x0b47, 0x1555: 0x0b47, 0x1556: 0x05df, 0x1557: 0x16c9,
+	0x1558: 0x179b, 0x1559: 0x0b57, 0x155a: 0x0b5f, 0x155b: 0x05e3, 0x155c: 0x0b73, 0x155d: 0x0b83,
+	0x155e: 0x0b87, 0x155f: 0x0b8f, 0x1560: 0x0b9f, 0x1561: 0x05eb, 0x1562: 0x05e7, 0x1563: 0x0ba3,
+	0x1564: 0x16ab, 0x1565: 0x0ba7, 0x1566: 0x0bbb, 0x1567: 0x0bbf, 0x1568: 0x0bc3, 0x1569: 0x0bbf,
+	0x156a: 0x0bcf, 0x156b: 0x0bd3, 0x156c: 0x0be3, 0x156d: 0x0bdb, 0x156e: 0x0bdf, 0x156f: 0x0be7,
+	0x1570: 0x0beb, 0x1571: 0x0bef, 0x1572: 0x0bfb, 0x1573: 0x0bff, 0x1574: 0x0c17, 0x1575: 0x0c1f,
+	0x1576: 0x0c2f, 0x1577: 0x0c43, 0x1578: 0x16ba, 0x1579: 0x0c3f, 0x157a: 0x0c33, 0x157b: 0x0c4b,
+	0x157c: 0x0c53, 0x157d: 0x0c67, 0x157e: 0x16bf, 0x157f: 0x0c6f,
+	// Block 0x56, offset 0x1580
+	0x1580: 0x0c63, 0x1581: 0x0c5b, 0x1582: 0x05ef, 0x1583: 0x0c77, 0x1584: 0x0c7f, 0x1585: 0x0c87,
+	0x1586: 0x0c7b, 0x1587: 0x05f3, 0x1588: 0x0c97, 0x1589: 0x0c9f, 0x158a: 0x16c4, 0x158b: 0x0ccb,
+	0x158c: 0x0cff, 0x158d: 0x0cdb, 0x158e: 0x05ff, 0x158f: 0x0ce7, 0x1590: 0x05fb, 0x1591: 0x05f7,
+	0x1592: 0x07c3, 0x1593: 0x07c7, 0x1594: 0x0d03, 0x1595: 0x0ceb, 0x1596: 0x11ab, 0x1597: 0x0663,
+	0x1598: 0x0d0f, 0x1599: 0x0d13, 0x159a: 0x0d17, 0x159b: 0x0d2b, 0x159c: 0x0d23, 0x159d: 0x16dd,
+	0x159e: 0x0603, 0x159f: 0x0d3f, 0x15a0: 0x0d33, 0x15a1: 0x0d4f, 0x15a2: 0x0d57, 0x15a3: 0x16e7,
+	0x15a4: 0x0d5b, 0x15a5: 0x0d47, 0x15a6: 0x0d63, 0x15a7: 0x0607, 0x15a8: 0x0d67, 0x15a9: 0x0d6b,
+	0x15aa: 0x0d6f, 0x15ab: 0x0d7b, 0x15ac: 0x16ec, 0x15ad: 0x0d83, 0x15ae: 0x060b, 0x15af: 0x0d8f,
+	0x15b0: 0x16f1, 0x15b1: 0x0d93, 0x15b2: 0x060f, 0x15b3: 0x0d9f, 0x15b4: 0x0dab, 0x15b5: 0x0db7,
+	0x15b6: 0x0dbb, 0x15b7: 0x16f6, 0x15b8: 0x168d, 0x15b9: 0x16fb, 0x15ba: 0x0ddb, 0x15bb: 0x1700,
+	0x15bc: 0x0de7, 0x15bd: 0x0def, 0x15be: 0x0ddf, 0x15bf: 0x0dfb,
+	// Block 0x57, offset 0x15c0
+	0x15c0: 0x0e0b, 0x15c1: 0x0e1b, 0x15c2: 0x0e0f, 0x15c3: 0x0e13, 0x15c4: 0x0e1f, 0x15c5: 0x0e23,
+	0x15c6: 0x1705, 0x15c7: 0x0e07, 0x15c8: 0x0e3b, 0x15c9: 0x0e3f, 0x15ca: 0x0613, 0x15cb: 0x0e53,
+	0x15cc: 0x0e4f, 0x15cd: 0x170a, 0x15ce: 0x0e33, 0x15cf: 0x0e6f, 0x15d0: 0x170f, 0x15d1: 0x1714,
+	0x15d2: 0x0e73, 0x15d3: 0x0e87, 0x15d4: 0x0e83, 0x15d5: 0x0e7f, 0x15d6: 0x0617, 0x15d7: 0x0e8b,
+	0x15d8: 0x0e9b, 0x15d9: 0x0e97, 0x15da: 0x0ea3, 0x15db: 0x1651, 0x15dc: 0x0eb3, 0x15dd: 0x1719,
+	0x15de: 0x0ebf, 0x15df: 0x1723, 0x15e0: 0x0ed3, 0x15e1: 0x0edf, 0x15e2: 0x0ef3, 0x15e3: 0x1728,
+	0x15e4: 0x0f07, 0x15e5: 0x0f0b, 0x15e6: 0x172d, 0x15e7: 0x1732, 0x15e8: 0x0f27, 0x15e9: 0x0f37,
+	0x15ea: 0x061b, 0x15eb: 0x0f3b, 0x15ec: 0x061f, 0x15ed: 0x061f, 0x15ee: 0x0f53, 0x15ef: 0x0f57,
+	0x15f0: 0x0f5f, 0x15f1: 0x0f63, 0x15f2: 0x0f6f, 0x15f3: 0x0623, 0x15f4: 0x0f87, 0x15f5: 0x1737,
+	0x15f6: 0x0fa3, 0x15f7: 0x173c, 0x15f8: 0x0faf, 0x15f9: 0x16a1, 0x15fa: 0x0fbf, 0x15fb: 0x1741,
+	0x15fc: 0x1746, 0x15fd: 0x174b, 0x15fe: 0x0627, 0x15ff: 0x062b,
+	// Block 0x58, offset 0x1600
+	0x1600: 0x0ff7, 0x1601: 0x1755, 0x1602: 0x1750, 0x1603: 0x175a, 0x1604: 0x175f, 0x1605: 0x0fff,
+	0x1606: 0x1003, 0x1607: 0x1003, 0x1608: 0x100b, 0x1609: 0x0633, 0x160a: 0x100f, 0x160b: 0x0637,
+	0x160c: 0x063b, 0x160d: 0x1769, 0x160e: 0x1023, 0x160f: 0x102b, 0x1610: 0x1037, 0x1611: 0x063f,
+	0x1612: 0x176e, 0x1613: 0x105b, 0x1614: 0x1773, 0x1615: 0x1778, 0x1616: 0x107b, 0x1617: 0x1093,
+	0x1618: 0x0643, 0x1619: 0x109b, 0x161a: 0x109f, 0x161b: 0x10a3, 0x161c: 0x177d, 0x161d: 0x1782,
+	0x161e: 0x1782, 0x161f: 0x10bb, 0x1620: 0x0647, 0x1621: 0x1787, 0x1622: 0x10cf, 0x1623: 0x10d3,
+	0x1624: 0x064b, 0x1625: 0x178c, 0x1626: 0x10ef, 0x1627: 0x064f, 0x1628: 0x10ff, 0x1629: 0x10f7,
+	0x162a: 0x1107, 0x162b: 0x1796, 0x162c: 0x111f, 0x162d: 0x0653, 0x162e: 0x112b, 0x162f: 0x1133,
+	0x1630: 0x1143, 0x1631: 0x0657, 0x1632: 0x17a0, 0x1633: 0x17a5, 0x1634: 0x065b, 0x1635: 0x17aa,
+	0x1636: 0x115b, 0x1637: 0x17af, 0x1638: 0x1167, 0x1639: 0x1173, 0x163a: 0x117b, 0x163b: 0x17b4,
+	0x163c: 0x17b9, 0x163d: 0x118f, 0x163e: 0x17be, 0x163f: 0x1197,
+	// Block 0x59, offset 0x1640
+	0x1640: 0x16ce, 0x1641: 0x065f, 0x1642: 0x11af, 0x1643: 0x11b3, 0x1644: 0x0667, 0x1645: 0x11b7,
+	0x1646: 0x0a33, 0x1647: 0x17c3, 0x1648: 0x17c8, 0x1649: 0x16d3, 0x164a: 0x16d8, 0x164b: 0x11d7,
+	0x164c: 0x11db, 0x164d: 0x13f3, 0x164e: 0x066b, 0x164f: 0x1207, 0x1650: 0x1203, 0x1651: 0x120b,
+	0x1652: 0x083f, 0x1653: 0x120f, 0x1654: 0x1213, 0x1655: 0x1217, 0x1656: 0x121f, 0x1657: 0x17cd,
+	0x1658: 0x121b, 0x1659: 0x1223, 0x165a: 0x1237, 0x165b: 0x123b, 0x165c: 0x1227, 0x165d: 0x123f,
+	0x165e: 0x1253, 0x165f: 0x1267, 0x1660: 0x1233, 0x1661: 0x1247, 0x1662: 0x124b, 0x1663: 0x124f,
+	0x1664: 0x17d2, 0x1665: 0x17dc, 0x1666: 0x17d7, 0x1667: 0x066f, 0x1668: 0x126f, 0x1669: 0x1273,
+	0x166a: 0x127b, 0x166b: 0x17f0, 0x166c: 0x127f, 0x166d: 0x17e1, 0x166e: 0x0673, 0x166f: 0x0677,
+	0x1670: 0x17e6, 0x1671: 0x17eb, 0x1672: 0x067b, 0x1673: 0x129f, 0x1674: 0x12a3, 0x1675: 0x12a7,
+	0x1676: 0x12ab, 0x1677: 0x12b7, 0x1678: 0x12b3, 0x1679: 0x12bf, 0x167a: 0x12bb, 0x167b: 0x12cb,
+	0x167c: 0x12c3, 0x167d: 0x12c7, 0x167e: 0x12cf, 0x167f: 0x067f,
+	// Block 0x5a, offset 0x1680
+	0x1680: 0x12d7, 0x1681: 0x12db, 0x1682: 0x0683, 0x1683: 0x12eb, 0x1684: 0x12ef, 0x1685: 0x17f5,
+	0x1686: 0x12fb, 0x1687: 0x12ff, 0x1688: 0x0687, 0x1689: 0x130b, 0x168a: 0x05bb, 0x168b: 0x17fa,
+	0x168c: 0x17ff, 0x168d: 0x068b, 0x168e: 0x068f, 0x168f: 0x1337, 0x1690: 0x134f, 0x1691: 0x136b,
+	0x1692: 0x137b, 0x1693: 0x1804, 0x1694: 0x138f, 0x1695: 0x1393, 0x1696: 0x13ab, 0x1697: 0x13b7,
+	0x1698: 0x180e, 0x1699: 0x1660, 0x169a: 0x13c3, 0x169b: 0x13bf, 0x169c: 0x13cb, 0x169d: 0x1665,
+	0x169e: 0x13d7, 0x169f: 0x13e3, 0x16a0: 0x1813, 0x16a1: 0x1818, 0x16a2: 0x1423, 0x16a3: 0x142f,
+	0x16a4: 0x1437, 0x16a5: 0x181d, 0x16a6: 0x143b, 0x16a7: 0x1467, 0x16a8: 0x1473, 0x16a9: 0x1477,
+	0x16aa: 0x146f, 0x16ab: 0x1483, 0x16ac: 0x1487, 0x16ad: 0x1822, 0x16ae: 0x1493, 0x16af: 0x0693,
+	0x16b0: 0x149b, 0x16b1: 0x1827, 0x16b2: 0x0697, 0x16b3: 0x14d3, 0x16b4: 0x0ac3, 0x16b5: 0x14eb,
+	0x16b6: 0x182c, 0x16b7: 0x1836, 0x16b8: 0x069b, 0x16b9: 0x069f, 0x16ba: 0x1513, 0x16bb: 0x183b,
+	0x16bc: 0x06a3, 0x16bd: 0x1840, 0x16be: 0x152b, 0x16bf: 0x152b,
+	// Block 0x5b, offset 0x16c0
+	0x16c0: 0x1533, 0x16c1: 0x1845, 0x16c2: 0x154b, 0x16c3: 0x06a7, 0x16c4: 0x155b, 0x16c5: 0x1567,
+	0x16c6: 0x156f, 0x16c7: 0x1577, 0x16c8: 0x06ab, 0x16c9: 0x184a, 0x16ca: 0x158b, 0x16cb: 0x15a7,
+	0x16cc: 0x15b3, 0x16cd: 0x06af, 0x16ce: 0x06b3, 0x16cf: 0x15b7, 0x16d0: 0x184f, 0x16d1: 0x06b7,
+	0x16d2: 0x1854, 0x16d3: 0x1859, 0x16d4: 0x185e, 0x16d5: 0x15db, 0x16d6: 0x06bb, 0x16d7: 0x15ef,
+	0x16d8: 0x15f7, 0x16d9: 0x15fb, 0x16da: 0x1603, 0x16db: 0x160b, 0x16dc: 0x1613, 0x16dd: 0x1868,
+}
+
+// nfkcIndex: 22 blocks, 1408 entries, 1408 bytes
+// Block 0 is the zero block.
+var nfkcIndex = [1408]uint8{
+	// Block 0x0, offset 0x0
+	// Block 0x1, offset 0x40
+	// Block 0x2, offset 0x80
+	// Block 0x3, offset 0xc0
+	0xc2: 0x5a, 0xc3: 0x01, 0xc4: 0x02, 0xc5: 0x03, 0xc6: 0x5b, 0xc7: 0x04,
+	0xc8: 0x05, 0xca: 0x5c, 0xcb: 0x5d, 0xcc: 0x06, 0xcd: 0x07, 0xce: 0x08, 0xcf: 0x09,
+	0xd0: 0x0a, 0xd1: 0x5e, 0xd2: 0x5f, 0xd3: 0x0b, 0xd6: 0x0c, 0xd7: 0x60,
+	0xd8: 0x61, 0xd9: 0x0d, 0xdb: 0x62, 0xdc: 0x63, 0xdd: 0x64, 0xdf: 0x65,
+	0xe0: 0x02, 0xe1: 0x03, 0xe2: 0x04, 0xe3: 0x05,
+	0xea: 0x06, 0xeb: 0x07, 0xec: 0x08, 0xed: 0x09, 0xef: 0x0a,
+	0xf0: 0x13,
+	// Block 0x4, offset 0x100
+	0x120: 0x66, 0x121: 0x67, 0x123: 0x68, 0x124: 0x69, 0x125: 0x6a, 0x126: 0x6b, 0x127: 0x6c,
+	0x128: 0x6d, 0x129: 0x6e, 0x12a: 0x6f, 0x12b: 0x70, 0x12c: 0x6b, 0x12d: 0x71, 0x12e: 0x72, 0x12f: 0x73,
+	0x131: 0x74, 0x132: 0x75, 0x133: 0x76, 0x134: 0x77, 0x135: 0x78, 0x137: 0x79,
+	0x138: 0x7a, 0x139: 0x7b, 0x13a: 0x7c, 0x13b: 0x7d, 0x13c: 0x7e, 0x13d: 0x7f, 0x13e: 0x80, 0x13f: 0x81,
+	// Block 0x5, offset 0x140
+	0x140: 0x82, 0x142: 0x83, 0x143: 0x84, 0x144: 0x85, 0x145: 0x86, 0x146: 0x87, 0x147: 0x88,
+	0x14d: 0x89,
+	0x15c: 0x8a, 0x15f: 0x8b,
+	0x162: 0x8c, 0x164: 0x8d,
+	0x168: 0x8e, 0x169: 0x8f, 0x16a: 0x90, 0x16c: 0x0e, 0x16d: 0x91, 0x16e: 0x92, 0x16f: 0x93,
+	0x170: 0x94, 0x173: 0x95, 0x174: 0x96, 0x175: 0x0f, 0x176: 0x10, 0x177: 0x97,
+	0x178: 0x11, 0x179: 0x12, 0x17a: 0x13, 0x17b: 0x14, 0x17c: 0x15, 0x17d: 0x16, 0x17e: 0x17, 0x17f: 0x18,
+	// Block 0x6, offset 0x180
+	0x180: 0x98, 0x181: 0x99, 0x182: 0x9a, 0x183: 0x9b, 0x184: 0x19, 0x185: 0x1a, 0x186: 0x9c, 0x187: 0x9d,
+	0x188: 0x9e, 0x189: 0x1b, 0x18a: 0x1c, 0x18b: 0x9f, 0x18c: 0xa0,
+	0x191: 0x1d, 0x192: 0x1e, 0x193: 0xa1,
+	0x1a8: 0xa2, 0x1a9: 0xa3, 0x1ab: 0xa4,
+	0x1b1: 0xa5, 0x1b3: 0xa6, 0x1b5: 0xa7, 0x1b7: 0xa8,
+	0x1ba: 0xa9, 0x1bb: 0xaa, 0x1bc: 0x1f, 0x1bd: 0x20, 0x1be: 0x21, 0x1bf: 0xab,
+	// Block 0x7, offset 0x1c0
+	0x1c0: 0xac, 0x1c1: 0x22, 0x1c2: 0x23, 0x1c3: 0x24, 0x1c4: 0xad, 0x1c5: 0x25, 0x1c6: 0x26,
+	0x1c8: 0x27, 0x1c9: 0x28, 0x1ca: 0x29, 0x1cb: 0x2a, 0x1cc: 0x2b, 0x1cd: 0x2c, 0x1ce: 0x2d, 0x1cf: 0x2e,
+	// Block 0x8, offset 0x200
+	0x219: 0xae, 0x21a: 0xaf, 0x21b: 0xb0, 0x21d: 0xb1, 0x21f: 0xb2,
+	0x220: 0xb3, 0x223: 0xb4, 0x224: 0xb5, 0x225: 0xb6, 0x226: 0xb7, 0x227: 0xb8,
+	0x22a: 0xb9, 0x22b: 0xba, 0x22d: 0xbb, 0x22f: 0xbc,
+	0x230: 0xbd, 0x231: 0xbe, 0x232: 0xbf, 0x233: 0xc0, 0x234: 0xc1, 0x235: 0xc2, 0x236: 0xc3, 0x237: 0xbd,
+	0x238: 0xbe, 0x239: 0xbf, 0x23a: 0xc0, 0x23b: 0xc1, 0x23c: 0xc2, 0x23d: 0xc3, 0x23e: 0xbd, 0x23f: 0xbe,
+	// Block 0x9, offset 0x240
+	0x240: 0xbf, 0x241: 0xc0, 0x242: 0xc1, 0x243: 0xc2, 0x244: 0xc3, 0x245: 0xbd, 0x246: 0xbe, 0x247: 0xbf,
+	0x248: 0xc0, 0x249: 0xc1, 0x24a: 0xc2, 0x24b: 0xc3, 0x24c: 0xbd, 0x24d: 0xbe, 0x24e: 0xbf, 0x24f: 0xc0,
+	0x250: 0xc1, 0x251: 0xc2, 0x252: 0xc3, 0x253: 0xbd, 0x254: 0xbe, 0x255: 0xbf, 0x256: 0xc0, 0x257: 0xc1,
+	0x258: 0xc2, 0x259: 0xc3, 0x25a: 0xbd, 0x25b: 0xbe, 0x25c: 0xbf, 0x25d: 0xc0, 0x25e: 0xc1, 0x25f: 0xc2,
+	0x260: 0xc3, 0x261: 0xbd, 0x262: 0xbe, 0x263: 0xbf, 0x264: 0xc0, 0x265: 0xc1, 0x266: 0xc2, 0x267: 0xc3,
+	0x268: 0xbd, 0x269: 0xbe, 0x26a: 0xbf, 0x26b: 0xc0, 0x26c: 0xc1, 0x26d: 0xc2, 0x26e: 0xc3, 0x26f: 0xbd,
+	0x270: 0xbe, 0x271: 0xbf, 0x272: 0xc0, 0x273: 0xc1, 0x274: 0xc2, 0x275: 0xc3, 0x276: 0xbd, 0x277: 0xbe,
+	0x278: 0xbf, 0x279: 0xc0, 0x27a: 0xc1, 0x27b: 0xc2, 0x27c: 0xc3, 0x27d: 0xbd, 0x27e: 0xbe, 0x27f: 0xbf,
+	// Block 0xa, offset 0x280
+	0x280: 0xc0, 0x281: 0xc1, 0x282: 0xc2, 0x283: 0xc3, 0x284: 0xbd, 0x285: 0xbe, 0x286: 0xbf, 0x287: 0xc0,
+	0x288: 0xc1, 0x289: 0xc2, 0x28a: 0xc3, 0x28b: 0xbd, 0x28c: 0xbe, 0x28d: 0xbf, 0x28e: 0xc0, 0x28f: 0xc1,
+	0x290: 0xc2, 0x291: 0xc3, 0x292: 0xbd, 0x293: 0xbe, 0x294: 0xbf, 0x295: 0xc0, 0x296: 0xc1, 0x297: 0xc2,
+	0x298: 0xc3, 0x299: 0xbd, 0x29a: 0xbe, 0x29b: 0xbf, 0x29c: 0xc0, 0x29d: 0xc1, 0x29e: 0xc2, 0x29f: 0xc3,
+	0x2a0: 0xbd, 0x2a1: 0xbe, 0x2a2: 0xbf, 0x2a3: 0xc0, 0x2a4: 0xc1, 0x2a5: 0xc2, 0x2a6: 0xc3, 0x2a7: 0xbd,
+	0x2a8: 0xbe, 0x2a9: 0xbf, 0x2aa: 0xc0, 0x2ab: 0xc1, 0x2ac: 0xc2, 0x2ad: 0xc3, 0x2ae: 0xbd, 0x2af: 0xbe,
+	0x2b0: 0xbf, 0x2b1: 0xc0, 0x2b2: 0xc1, 0x2b3: 0xc2, 0x2b4: 0xc3, 0x2b5: 0xbd, 0x2b6: 0xbe, 0x2b7: 0xbf,
+	0x2b8: 0xc0, 0x2b9: 0xc1, 0x2ba: 0xc2, 0x2bb: 0xc3, 0x2bc: 0xbd, 0x2bd: 0xbe, 0x2be: 0xbf, 0x2bf: 0xc0,
+	// Block 0xb, offset 0x2c0
+	0x2c0: 0xc1, 0x2c1: 0xc2, 0x2c2: 0xc3, 0x2c3: 0xbd, 0x2c4: 0xbe, 0x2c5: 0xbf, 0x2c6: 0xc0, 0x2c7: 0xc1,
+	0x2c8: 0xc2, 0x2c9: 0xc3, 0x2ca: 0xbd, 0x2cb: 0xbe, 0x2cc: 0xbf, 0x2cd: 0xc0, 0x2ce: 0xc1, 0x2cf: 0xc2,
+	0x2d0: 0xc3, 0x2d1: 0xbd, 0x2d2: 0xbe, 0x2d3: 0xbf, 0x2d4: 0xc0, 0x2d5: 0xc1, 0x2d6: 0xc2, 0x2d7: 0xc3,
+	0x2d8: 0xbd, 0x2d9: 0xbe, 0x2da: 0xbf, 0x2db: 0xc0, 0x2dc: 0xc1, 0x2dd: 0xc2, 0x2de: 0xc4,
+	// Block 0xc, offset 0x300
+	0x324: 0x2f, 0x325: 0x30, 0x326: 0x31, 0x327: 0x32,
+	0x328: 0x33, 0x329: 0x34, 0x32a: 0x35, 0x32b: 0x36, 0x32c: 0x37, 0x32d: 0x38, 0x32e: 0x39, 0x32f: 0x3a,
+	0x330: 0x3b, 0x331: 0x3c, 0x332: 0x3d, 0x333: 0x3e, 0x334: 0x3f, 0x335: 0x40, 0x336: 0x41, 0x337: 0x42,
+	0x338: 0x43, 0x339: 0x44, 0x33a: 0x45, 0x33b: 0x46, 0x33c: 0xc5, 0x33d: 0x47, 0x33e: 0x48, 0x33f: 0x49,
+	// Block 0xd, offset 0x340
+	0x347: 0xc6,
+	0x34b: 0xc7, 0x34d: 0xc8,
+	0x368: 0xc9, 0x36b: 0xca,
+	// Block 0xe, offset 0x380
+	0x381: 0xcb, 0x382: 0xcc, 0x384: 0xcd, 0x385: 0xb7, 0x387: 0xce,
+	0x388: 0xcf, 0x38b: 0xd0, 0x38c: 0x6b, 0x38d: 0xd1,
+	0x391: 0xd2, 0x392: 0xd3, 0x393: 0xd4, 0x396: 0xd5, 0x397: 0xd6,
+	0x398: 0xd7, 0x39a: 0xd8, 0x39c: 0xd9,
+	0x3b0: 0xd7,
+	// Block 0xf, offset 0x3c0
+	0x3eb: 0xda, 0x3ec: 0xdb,
+	// Block 0x10, offset 0x400
+	0x432: 0xdc,
+	// Block 0x11, offset 0x440
+	0x445: 0xdd, 0x446: 0xde, 0x447: 0xdf,
+	0x449: 0xe0,
+	0x450: 0xe1, 0x451: 0xe2, 0x452: 0xe3, 0x453: 0xe4, 0x454: 0xe5, 0x455: 0xe6, 0x456: 0xe7, 0x457: 0xe8,
+	0x458: 0xe9, 0x459: 0xea, 0x45a: 0x4a, 0x45b: 0xeb, 0x45c: 0xec, 0x45d: 0xed, 0x45e: 0xee, 0x45f: 0x4b,
+	// Block 0x12, offset 0x480
+	0x480: 0xef,
+	0x4a3: 0xf0, 0x4a5: 0xf1,
+	0x4b8: 0x4c, 0x4b9: 0x4d, 0x4ba: 0x4e,
+	// Block 0x13, offset 0x4c0
+	0x4c4: 0x4f, 0x4c5: 0xf2, 0x4c6: 0xf3,
+	0x4c8: 0x50, 0x4c9: 0xf4,
+	// Block 0x14, offset 0x500
+	0x520: 0x51, 0x521: 0x52, 0x522: 0x53, 0x523: 0x54, 0x524: 0x55, 0x525: 0x56, 0x526: 0x57, 0x527: 0x58,
+	0x528: 0x59,
+	// Block 0x15, offset 0x540
+	0x550: 0x0b, 0x551: 0x0c, 0x556: 0x0d,
+	0x55b: 0x0e, 0x55d: 0x0f, 0x55e: 0x10, 0x55f: 0x11,
+	0x56f: 0x12,
+}
+
+// nfkcSparseOffset: 155 entries, 310 bytes
+var nfkcSparseOffset = []uint16{0x0, 0xe, 0x12, 0x1b, 0x25, 0x35, 0x37, 0x3c, 0x47, 0x56, 0x63, 0x6b, 0x6f, 0x74, 0x76, 0x87, 0x8f, 0x96, 0x99, 0xa0, 0xa4, 0xa8, 0xaa, 0xac, 0xb5, 0xb9, 0xc0, 0xc5, 0xc8, 0xd2, 0xd4, 0xdb, 0xe3, 0xe7, 0xe9, 0xec, 0xf0, 0xf6, 0x107, 0x113, 0x115, 0x11b, 0x11d, 0x11f, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12c, 0x12f, 0x131, 0x134, 0x137, 0x13b, 0x140, 0x149, 0x14b, 0x14e, 0x150, 0x15b, 0x166, 0x176, 0x184, 0x192, 0x1a2, 0x1b0, 0x1b7, 0x1bd, 0x1cc, 0x1d0, 0x1 [...]
+
+// nfkcSparseValues: 875 entries, 3500 bytes
+var nfkcSparseValues = [875]valueRange{
+	// Block 0x0, offset 0x0
+	{value: 0x0002, lo: 0x0d},
+	{value: 0x0001, lo: 0xa0, hi: 0xa0},
+	{value: 0x4278, lo: 0xa8, hi: 0xa8},
+	{value: 0x0083, lo: 0xaa, hi: 0xaa},
+	{value: 0x4264, lo: 0xaf, hi: 0xaf},
+	{value: 0x0025, lo: 0xb2, hi: 0xb3},
+	{value: 0x425a, lo: 0xb4, hi: 0xb4},
+	{value: 0x01dc, lo: 0xb5, hi: 0xb5},
+	{value: 0x4291, lo: 0xb8, hi: 0xb8},
+	{value: 0x0023, lo: 0xb9, hi: 0xb9},
+	{value: 0x009f, lo: 0xba, hi: 0xba},
+	{value: 0x221c, lo: 0xbc, hi: 0xbc},
+	{value: 0x2210, lo: 0xbd, hi: 0xbd},
+	{value: 0x22b2, lo: 0xbe, hi: 0xbe},
+	// Block 0x1, offset 0xe
+	{value: 0x0091, lo: 0x03},
+	{value: 0x4778, lo: 0xa0, hi: 0xa1},
+	{value: 0x47aa, lo: 0xaf, hi: 0xb0},
+	{value: 0xa000, lo: 0xb7, hi: 0xb7},
+	// Block 0x2, offset 0x12
+	{value: 0x0003, lo: 0x08},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	{value: 0x0091, lo: 0xb0, hi: 0xb0},
+	{value: 0x0119, lo: 0xb1, hi: 0xb1},
+	{value: 0x0095, lo: 0xb2, hi: 0xb2},
+	{value: 0x00a5, lo: 0xb3, hi: 0xb3},
+	{value: 0x0143, lo: 0xb4, hi: 0xb6},
+	{value: 0x00af, lo: 0xb7, hi: 0xb7},
+	{value: 0x00b3, lo: 0xb8, hi: 0xb8},
+	// Block 0x3, offset 0x1b
+	{value: 0x000a, lo: 0x09},
+	{value: 0x426e, lo: 0x98, hi: 0x98},
+	{value: 0x4273, lo: 0x99, hi: 0x9a},
+	{value: 0x4296, lo: 0x9b, hi: 0x9b},
+	{value: 0x425f, lo: 0x9c, hi: 0x9c},
+	{value: 0x4282, lo: 0x9d, hi: 0x9d},
+	{value: 0x0113, lo: 0xa0, hi: 0xa0},
+	{value: 0x0099, lo: 0xa1, hi: 0xa1},
+	{value: 0x00a7, lo: 0xa2, hi: 0xa3},
+	{value: 0x0167, lo: 0xa4, hi: 0xa4},
+	// Block 0x4, offset 0x25
+	{value: 0x0000, lo: 0x0f},
+	{value: 0xa000, lo: 0x83, hi: 0x83},
+	{value: 0xa000, lo: 0x87, hi: 0x87},
+	{value: 0xa000, lo: 0x8b, hi: 0x8b},
+	{value: 0xa000, lo: 0x8d, hi: 0x8d},
+	{value: 0x37a5, lo: 0x90, hi: 0x90},
+	{value: 0x37b1, lo: 0x91, hi: 0x91},
+	{value: 0x379f, lo: 0x93, hi: 0x93},
+	{value: 0xa000, lo: 0x96, hi: 0x96},
+	{value: 0x3817, lo: 0x97, hi: 0x97},
+	{value: 0x37e1, lo: 0x9c, hi: 0x9c},
+	{value: 0x37c9, lo: 0x9d, hi: 0x9d},
+	{value: 0x37f3, lo: 0x9e, hi: 0x9e},
+	{value: 0xa000, lo: 0xb4, hi: 0xb5},
+	{value: 0x381d, lo: 0xb6, hi: 0xb6},
+	{value: 0x3823, lo: 0xb7, hi: 0xb7},
+	// Block 0x5, offset 0x35
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0x83, hi: 0x87},
+	// Block 0x6, offset 0x37
+	{value: 0x0001, lo: 0x04},
+	{value: 0x8113, lo: 0x81, hi: 0x82},
+	{value: 0x8132, lo: 0x84, hi: 0x84},
+	{value: 0x812d, lo: 0x85, hi: 0x85},
+	{value: 0x810d, lo: 0x87, hi: 0x87},
+	// Block 0x7, offset 0x3c
+	{value: 0x0000, lo: 0x0a},
+	{value: 0x8132, lo: 0x90, hi: 0x97},
+	{value: 0x8119, lo: 0x98, hi: 0x98},
+	{value: 0x811a, lo: 0x99, hi: 0x99},
+	{value: 0x811b, lo: 0x9a, hi: 0x9a},
+	{value: 0x3841, lo: 0xa2, hi: 0xa2},
+	{value: 0x3847, lo: 0xa3, hi: 0xa3},
+	{value: 0x3853, lo: 0xa4, hi: 0xa4},
+	{value: 0x384d, lo: 0xa5, hi: 0xa5},
+	{value: 0x3859, lo: 0xa6, hi: 0xa6},
+	{value: 0xa000, lo: 0xa7, hi: 0xa7},
+	// Block 0x8, offset 0x47
+	{value: 0x0000, lo: 0x0e},
+	{value: 0x386b, lo: 0x80, hi: 0x80},
+	{value: 0xa000, lo: 0x81, hi: 0x81},
+	{value: 0x385f, lo: 0x82, hi: 0x82},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	{value: 0x3865, lo: 0x93, hi: 0x93},
+	{value: 0xa000, lo: 0x95, hi: 0x95},
+	{value: 0x8132, lo: 0x96, hi: 0x9c},
+	{value: 0x8132, lo: 0x9f, hi: 0xa2},
+	{value: 0x812d, lo: 0xa3, hi: 0xa3},
+	{value: 0x8132, lo: 0xa4, hi: 0xa4},
+	{value: 0x8132, lo: 0xa7, hi: 0xa8},
+	{value: 0x812d, lo: 0xaa, hi: 0xaa},
+	{value: 0x8132, lo: 0xab, hi: 0xac},
+	{value: 0x812d, lo: 0xad, hi: 0xad},
+	// Block 0x9, offset 0x56
+	{value: 0x0000, lo: 0x0c},
+	{value: 0x811f, lo: 0x91, hi: 0x91},
+	{value: 0x8132, lo: 0xb0, hi: 0xb0},
+	{value: 0x812d, lo: 0xb1, hi: 0xb1},
+	{value: 0x8132, lo: 0xb2, hi: 0xb3},
+	{value: 0x812d, lo: 0xb4, hi: 0xb4},
+	{value: 0x8132, lo: 0xb5, hi: 0xb6},
+	{value: 0x812d, lo: 0xb7, hi: 0xb9},
+	{value: 0x8132, lo: 0xba, hi: 0xba},
+	{value: 0x812d, lo: 0xbb, hi: 0xbc},
+	{value: 0x8132, lo: 0xbd, hi: 0xbd},
+	{value: 0x812d, lo: 0xbe, hi: 0xbe},
+	{value: 0x8132, lo: 0xbf, hi: 0xbf},
+	// Block 0xa, offset 0x63
+	{value: 0x0005, lo: 0x07},
+	{value: 0x8132, lo: 0x80, hi: 0x80},
+	{value: 0x8132, lo: 0x81, hi: 0x81},
+	{value: 0x812d, lo: 0x82, hi: 0x83},
+	{value: 0x812d, lo: 0x84, hi: 0x85},
+	{value: 0x812d, lo: 0x86, hi: 0x87},
+	{value: 0x812d, lo: 0x88, hi: 0x89},
+	{value: 0x8132, lo: 0x8a, hi: 0x8a},
+	// Block 0xb, offset 0x6b
+	{value: 0x0000, lo: 0x03},
+	{value: 0x8132, lo: 0xab, hi: 0xb1},
+	{value: 0x812d, lo: 0xb2, hi: 0xb2},
+	{value: 0x8132, lo: 0xb3, hi: 0xb3},
+	// Block 0xc, offset 0x6f
+	{value: 0x0000, lo: 0x04},
+	{value: 0x8132, lo: 0x96, hi: 0x99},
+	{value: 0x8132, lo: 0x9b, hi: 0xa3},
+	{value: 0x8132, lo: 0xa5, hi: 0xa7},
+	{value: 0x8132, lo: 0xa9, hi: 0xad},
+	// Block 0xd, offset 0x74
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0x99, hi: 0x9b},
+	// Block 0xe, offset 0x76
+	{value: 0x0000, lo: 0x10},
+	{value: 0x8132, lo: 0x94, hi: 0xa1},
+	{value: 0x812d, lo: 0xa3, hi: 0xa3},
+	{value: 0x8132, lo: 0xa4, hi: 0xa5},
+	{value: 0x812d, lo: 0xa6, hi: 0xa6},
+	{value: 0x8132, lo: 0xa7, hi: 0xa8},
+	{value: 0x812d, lo: 0xa9, hi: 0xa9},
+	{value: 0x8132, lo: 0xaa, hi: 0xac},
+	{value: 0x812d, lo: 0xad, hi: 0xaf},
+	{value: 0x8116, lo: 0xb0, hi: 0xb0},
+	{value: 0x8117, lo: 0xb1, hi: 0xb1},
+	{value: 0x8118, lo: 0xb2, hi: 0xb2},
+	{value: 0x8132, lo: 0xb3, hi: 0xb5},
+	{value: 0x812d, lo: 0xb6, hi: 0xb6},
+	{value: 0x8132, lo: 0xb7, hi: 0xb8},
+	{value: 0x812d, lo: 0xb9, hi: 0xba},
+	{value: 0x8132, lo: 0xbb, hi: 0xbf},
+	// Block 0xf, offset 0x87
+	{value: 0x0000, lo: 0x07},
+	{value: 0xa000, lo: 0xa8, hi: 0xa8},
+	{value: 0x3ed8, lo: 0xa9, hi: 0xa9},
+	{value: 0xa000, lo: 0xb0, hi: 0xb0},
+	{value: 0x3ee0, lo: 0xb1, hi: 0xb1},
+	{value: 0xa000, lo: 0xb3, hi: 0xb3},
+	{value: 0x3ee8, lo: 0xb4, hi: 0xb4},
+	{value: 0x9902, lo: 0xbc, hi: 0xbc},
+	// Block 0x10, offset 0x8f
+	{value: 0x0008, lo: 0x06},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x8132, lo: 0x91, hi: 0x91},
+	{value: 0x812d, lo: 0x92, hi: 0x92},
+	{value: 0x8132, lo: 0x93, hi: 0x93},
+	{value: 0x8132, lo: 0x94, hi: 0x94},
+	{value: 0x45b2, lo: 0x98, hi: 0x9f},
+	// Block 0x11, offset 0x96
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8102, lo: 0xbc, hi: 0xbc},
+	{value: 0x9900, lo: 0xbe, hi: 0xbe},
+	// Block 0x12, offset 0x99
+	{value: 0x0008, lo: 0x06},
+	{value: 0xa000, lo: 0x87, hi: 0x87},
+	{value: 0x2c9e, lo: 0x8b, hi: 0x8c},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x97, hi: 0x97},
+	{value: 0x45f2, lo: 0x9c, hi: 0x9d},
+	{value: 0x4602, lo: 0x9f, hi: 0x9f},
+	// Block 0x13, offset 0xa0
+	{value: 0x0000, lo: 0x03},
+	{value: 0x462a, lo: 0xb3, hi: 0xb3},
+	{value: 0x4632, lo: 0xb6, hi: 0xb6},
+	{value: 0x8102, lo: 0xbc, hi: 0xbc},
+	// Block 0x14, offset 0xa4
+	{value: 0x0008, lo: 0x03},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x460a, lo: 0x99, hi: 0x9b},
+	{value: 0x4622, lo: 0x9e, hi: 0x9e},
+	// Block 0x15, offset 0xa8
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8102, lo: 0xbc, hi: 0xbc},
+	// Block 0x16, offset 0xaa
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	// Block 0x17, offset 0xac
+	{value: 0x0000, lo: 0x08},
+	{value: 0xa000, lo: 0x87, hi: 0x87},
+	{value: 0x2cb6, lo: 0x88, hi: 0x88},
+	{value: 0x2cae, lo: 0x8b, hi: 0x8b},
+	{value: 0x2cbe, lo: 0x8c, hi: 0x8c},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x96, hi: 0x97},
+	{value: 0x463a, lo: 0x9c, hi: 0x9c},
+	{value: 0x4642, lo: 0x9d, hi: 0x9d},
+	// Block 0x18, offset 0xb5
+	{value: 0x0000, lo: 0x03},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	{value: 0x2cc6, lo: 0x94, hi: 0x94},
+	{value: 0x9900, lo: 0xbe, hi: 0xbe},
+	// Block 0x19, offset 0xb9
+	{value: 0x0000, lo: 0x06},
+	{value: 0xa000, lo: 0x86, hi: 0x87},
+	{value: 0x2cce, lo: 0x8a, hi: 0x8a},
+	{value: 0x2cde, lo: 0x8b, hi: 0x8b},
+	{value: 0x2cd6, lo: 0x8c, hi: 0x8c},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x97, hi: 0x97},
+	// Block 0x1a, offset 0xc0
+	{value: 0x1801, lo: 0x04},
+	{value: 0xa000, lo: 0x86, hi: 0x86},
+	{value: 0x3ef0, lo: 0x88, hi: 0x88},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x8120, lo: 0x95, hi: 0x96},
+	// Block 0x1b, offset 0xc5
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8102, lo: 0xbc, hi: 0xbc},
+	{value: 0xa000, lo: 0xbf, hi: 0xbf},
+	// Block 0x1c, offset 0xc8
+	{value: 0x0000, lo: 0x09},
+	{value: 0x2ce6, lo: 0x80, hi: 0x80},
+	{value: 0x9900, lo: 0x82, hi: 0x82},
+	{value: 0xa000, lo: 0x86, hi: 0x86},
+	{value: 0x2cee, lo: 0x87, hi: 0x87},
+	{value: 0x2cf6, lo: 0x88, hi: 0x88},
+	{value: 0x2f50, lo: 0x8a, hi: 0x8a},
+	{value: 0x2dd8, lo: 0x8b, hi: 0x8b},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x95, hi: 0x96},
+	// Block 0x1d, offset 0xd2
+	{value: 0x0000, lo: 0x01},
+	{value: 0x9900, lo: 0xbe, hi: 0xbe},
+	// Block 0x1e, offset 0xd4
+	{value: 0x0000, lo: 0x06},
+	{value: 0xa000, lo: 0x86, hi: 0x87},
+	{value: 0x2cfe, lo: 0x8a, hi: 0x8a},
+	{value: 0x2d0e, lo: 0x8b, hi: 0x8b},
+	{value: 0x2d06, lo: 0x8c, hi: 0x8c},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x97, hi: 0x97},
+	// Block 0x1f, offset 0xdb
+	{value: 0x6bea, lo: 0x07},
+	{value: 0x9904, lo: 0x8a, hi: 0x8a},
+	{value: 0x9900, lo: 0x8f, hi: 0x8f},
+	{value: 0xa000, lo: 0x99, hi: 0x99},
+	{value: 0x3ef8, lo: 0x9a, hi: 0x9a},
+	{value: 0x2f58, lo: 0x9c, hi: 0x9c},
+	{value: 0x2de3, lo: 0x9d, hi: 0x9d},
+	{value: 0x2d16, lo: 0x9e, hi: 0x9f},
+	// Block 0x20, offset 0xe3
+	{value: 0x0000, lo: 0x03},
+	{value: 0x2621, lo: 0xb3, hi: 0xb3},
+	{value: 0x8122, lo: 0xb8, hi: 0xb9},
+	{value: 0x8104, lo: 0xba, hi: 0xba},
+	// Block 0x21, offset 0xe7
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8123, lo: 0x88, hi: 0x8b},
+	// Block 0x22, offset 0xe9
+	{value: 0x0000, lo: 0x02},
+	{value: 0x2636, lo: 0xb3, hi: 0xb3},
+	{value: 0x8124, lo: 0xb8, hi: 0xb9},
+	// Block 0x23, offset 0xec
+	{value: 0x0000, lo: 0x03},
+	{value: 0x8125, lo: 0x88, hi: 0x8b},
+	{value: 0x2628, lo: 0x9c, hi: 0x9c},
+	{value: 0x262f, lo: 0x9d, hi: 0x9d},
+	// Block 0x24, offset 0xf0
+	{value: 0x0000, lo: 0x05},
+	{value: 0x030b, lo: 0x8c, hi: 0x8c},
+	{value: 0x812d, lo: 0x98, hi: 0x99},
+	{value: 0x812d, lo: 0xb5, hi: 0xb5},
+	{value: 0x812d, lo: 0xb7, hi: 0xb7},
+	{value: 0x812b, lo: 0xb9, hi: 0xb9},
+	// Block 0x25, offset 0xf6
+	{value: 0x0000, lo: 0x10},
+	{value: 0x2644, lo: 0x83, hi: 0x83},
+	{value: 0x264b, lo: 0x8d, hi: 0x8d},
+	{value: 0x2652, lo: 0x92, hi: 0x92},
+	{value: 0x2659, lo: 0x97, hi: 0x97},
+	{value: 0x2660, lo: 0x9c, hi: 0x9c},
+	{value: 0x263d, lo: 0xa9, hi: 0xa9},
+	{value: 0x8126, lo: 0xb1, hi: 0xb1},
+	{value: 0x8127, lo: 0xb2, hi: 0xb2},
+	{value: 0x4a66, lo: 0xb3, hi: 0xb3},
+	{value: 0x8128, lo: 0xb4, hi: 0xb4},
+	{value: 0x4a6f, lo: 0xb5, hi: 0xb5},
+	{value: 0x464a, lo: 0xb6, hi: 0xb6},
+	{value: 0x468a, lo: 0xb7, hi: 0xb7},
+	{value: 0x4652, lo: 0xb8, hi: 0xb8},
+	{value: 0x4695, lo: 0xb9, hi: 0xb9},
+	{value: 0x8127, lo: 0xba, hi: 0xbd},
+	// Block 0x26, offset 0x107
+	{value: 0x0000, lo: 0x0b},
+	{value: 0x8127, lo: 0x80, hi: 0x80},
+	{value: 0x4a78, lo: 0x81, hi: 0x81},
+	{value: 0x8132, lo: 0x82, hi: 0x83},
+	{value: 0x8104, lo: 0x84, hi: 0x84},
+	{value: 0x8132, lo: 0x86, hi: 0x87},
+	{value: 0x266e, lo: 0x93, hi: 0x93},
+	{value: 0x2675, lo: 0x9d, hi: 0x9d},
+	{value: 0x267c, lo: 0xa2, hi: 0xa2},
+	{value: 0x2683, lo: 0xa7, hi: 0xa7},
+	{value: 0x268a, lo: 0xac, hi: 0xac},
+	{value: 0x2667, lo: 0xb9, hi: 0xb9},
+	// Block 0x27, offset 0x113
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0x86, hi: 0x86},
+	// Block 0x28, offset 0x115
+	{value: 0x0000, lo: 0x05},
+	{value: 0xa000, lo: 0xa5, hi: 0xa5},
+	{value: 0x2d1e, lo: 0xa6, hi: 0xa6},
+	{value: 0x9900, lo: 0xae, hi: 0xae},
+	{value: 0x8102, lo: 0xb7, hi: 0xb7},
+	{value: 0x8104, lo: 0xb9, hi: 0xba},
+	// Block 0x29, offset 0x11b
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0x8d, hi: 0x8d},
+	// Block 0x2a, offset 0x11d
+	{value: 0x0000, lo: 0x01},
+	{value: 0x030f, lo: 0xbc, hi: 0xbc},
+	// Block 0x2b, offset 0x11f
+	{value: 0x0000, lo: 0x01},
+	{value: 0xa000, lo: 0x80, hi: 0x92},
+	// Block 0x2c, offset 0x121
+	{value: 0x0000, lo: 0x01},
+	{value: 0xb900, lo: 0xa1, hi: 0xb5},
+	// Block 0x2d, offset 0x123
+	{value: 0x0000, lo: 0x01},
+	{value: 0x9900, lo: 0xa8, hi: 0xbf},
+	// Block 0x2e, offset 0x125
+	{value: 0x0000, lo: 0x01},
+	{value: 0x9900, lo: 0x80, hi: 0x82},
+	// Block 0x2f, offset 0x127
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0x9d, hi: 0x9f},
+	// Block 0x30, offset 0x129
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x94, hi: 0x94},
+	{value: 0x8104, lo: 0xb4, hi: 0xb4},
+	// Block 0x31, offset 0x12c
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x92, hi: 0x92},
+	{value: 0x8132, lo: 0x9d, hi: 0x9d},
+	// Block 0x32, offset 0x12f
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8131, lo: 0xa9, hi: 0xa9},
+	// Block 0x33, offset 0x131
+	{value: 0x0004, lo: 0x02},
+	{value: 0x812e, lo: 0xb9, hi: 0xba},
+	{value: 0x812d, lo: 0xbb, hi: 0xbb},
+	// Block 0x34, offset 0x134
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8132, lo: 0x97, hi: 0x97},
+	{value: 0x812d, lo: 0x98, hi: 0x98},
+	// Block 0x35, offset 0x137
+	{value: 0x0000, lo: 0x03},
+	{value: 0x8104, lo: 0xa0, hi: 0xa0},
+	{value: 0x8132, lo: 0xb5, hi: 0xbc},
+	{value: 0x812d, lo: 0xbf, hi: 0xbf},
+	// Block 0x36, offset 0x13b
+	{value: 0x0000, lo: 0x04},
+	{value: 0x8132, lo: 0xb0, hi: 0xb4},
+	{value: 0x812d, lo: 0xb5, hi: 0xba},
+	{value: 0x8132, lo: 0xbb, hi: 0xbc},
+	{value: 0x812d, lo: 0xbd, hi: 0xbd},
+	// Block 0x37, offset 0x140
+	{value: 0x0000, lo: 0x08},
+	{value: 0x2d66, lo: 0x80, hi: 0x80},
+	{value: 0x2d6e, lo: 0x81, hi: 0x81},
+	{value: 0xa000, lo: 0x82, hi: 0x82},
+	{value: 0x2d76, lo: 0x83, hi: 0x83},
+	{value: 0x8104, lo: 0x84, hi: 0x84},
+	{value: 0x8132, lo: 0xab, hi: 0xab},
+	{value: 0x812d, lo: 0xac, hi: 0xac},
+	{value: 0x8132, lo: 0xad, hi: 0xb3},
+	// Block 0x38, offset 0x149
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0xaa, hi: 0xab},
+	// Block 0x39, offset 0x14b
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8102, lo: 0xa6, hi: 0xa6},
+	{value: 0x8104, lo: 0xb2, hi: 0xb3},
+	// Block 0x3a, offset 0x14e
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8102, lo: 0xb7, hi: 0xb7},
+	// Block 0x3b, offset 0x150
+	{value: 0x0000, lo: 0x0a},
+	{value: 0x8132, lo: 0x90, hi: 0x92},
+	{value: 0x8101, lo: 0x94, hi: 0x94},
+	{value: 0x812d, lo: 0x95, hi: 0x99},
+	{value: 0x8132, lo: 0x9a, hi: 0x9b},
+	{value: 0x812d, lo: 0x9c, hi: 0x9f},
+	{value: 0x8132, lo: 0xa0, hi: 0xa0},
+	{value: 0x8101, lo: 0xa2, hi: 0xa8},
+	{value: 0x812d, lo: 0xad, hi: 0xad},
+	{value: 0x8132, lo: 0xb4, hi: 0xb4},
+	{value: 0x8132, lo: 0xb8, hi: 0xb9},
+	// Block 0x3c, offset 0x15b
+	{value: 0x0002, lo: 0x0a},
+	{value: 0x0043, lo: 0xac, hi: 0xac},
+	{value: 0x00d1, lo: 0xad, hi: 0xad},
+	{value: 0x0045, lo: 0xae, hi: 0xae},
+	{value: 0x0049, lo: 0xb0, hi: 0xb1},
+	{value: 0x00e6, lo: 0xb2, hi: 0xb2},
+	{value: 0x004f, lo: 0xb3, hi: 0xba},
+	{value: 0x005f, lo: 0xbc, hi: 0xbc},
+	{value: 0x00ef, lo: 0xbd, hi: 0xbd},
+	{value: 0x0061, lo: 0xbe, hi: 0xbe},
+	{value: 0x0065, lo: 0xbf, hi: 0xbf},
+	// Block 0x3d, offset 0x166
+	{value: 0x0000, lo: 0x0f},
+	{value: 0x8132, lo: 0x80, hi: 0x81},
+	{value: 0x812d, lo: 0x82, hi: 0x82},
+	{value: 0x8132, lo: 0x83, hi: 0x89},
+	{value: 0x812d, lo: 0x8a, hi: 0x8a},
+	{value: 0x8132, lo: 0x8b, hi: 0x8c},
+	{value: 0x8135, lo: 0x8d, hi: 0x8d},
+	{value: 0x812a, lo: 0x8e, hi: 0x8e},
+	{value: 0x812d, lo: 0x8f, hi: 0x8f},
+	{value: 0x8129, lo: 0x90, hi: 0x90},
+	{value: 0x8132, lo: 0x91, hi: 0xb5},
+	{value: 0x8132, lo: 0xbb, hi: 0xbb},
+	{value: 0x8134, lo: 0xbc, hi: 0xbc},
+	{value: 0x812d, lo: 0xbd, hi: 0xbd},
+	{value: 0x8132, lo: 0xbe, hi: 0xbe},
+	{value: 0x812d, lo: 0xbf, hi: 0xbf},
+	// Block 0x3e, offset 0x176
+	{value: 0x0000, lo: 0x0d},
+	{value: 0x0001, lo: 0x80, hi: 0x8a},
+	{value: 0x043b, lo: 0x91, hi: 0x91},
+	{value: 0x429b, lo: 0x97, hi: 0x97},
+	{value: 0x001d, lo: 0xa4, hi: 0xa4},
+	{value: 0x1873, lo: 0xa5, hi: 0xa5},
+	{value: 0x1b5c, lo: 0xa6, hi: 0xa6},
+	{value: 0x0001, lo: 0xaf, hi: 0xaf},
+	{value: 0x2691, lo: 0xb3, hi: 0xb3},
+	{value: 0x27fe, lo: 0xb4, hi: 0xb4},
+	{value: 0x2698, lo: 0xb6, hi: 0xb6},
+	{value: 0x2808, lo: 0xb7, hi: 0xb7},
+	{value: 0x186d, lo: 0xbc, hi: 0xbc},
+	{value: 0x4269, lo: 0xbe, hi: 0xbe},
+	// Block 0x3f, offset 0x184
+	{value: 0x0002, lo: 0x0d},
+	{value: 0x1933, lo: 0x87, hi: 0x87},
+	{value: 0x1930, lo: 0x88, hi: 0x88},
+	{value: 0x1870, lo: 0x89, hi: 0x89},
+	{value: 0x298e, lo: 0x97, hi: 0x97},
+	{value: 0x0001, lo: 0x9f, hi: 0x9f},
+	{value: 0x0021, lo: 0xb0, hi: 0xb0},
+	{value: 0x0093, lo: 0xb1, hi: 0xb1},
+	{value: 0x0029, lo: 0xb4, hi: 0xb9},
+	{value: 0x0017, lo: 0xba, hi: 0xba},
+	{value: 0x0467, lo: 0xbb, hi: 0xbb},
+	{value: 0x003b, lo: 0xbc, hi: 0xbc},
+	{value: 0x0011, lo: 0xbd, hi: 0xbe},
+	{value: 0x009d, lo: 0xbf, hi: 0xbf},
+	// Block 0x40, offset 0x192
+	{value: 0x0002, lo: 0x0f},
+	{value: 0x0021, lo: 0x80, hi: 0x89},
+	{value: 0x0017, lo: 0x8a, hi: 0x8a},
+	{value: 0x0467, lo: 0x8b, hi: 0x8b},
+	{value: 0x003b, lo: 0x8c, hi: 0x8c},
+	{value: 0x0011, lo: 0x8d, hi: 0x8e},
+	{value: 0x0083, lo: 0x90, hi: 0x90},
+	{value: 0x008b, lo: 0x91, hi: 0x91},
+	{value: 0x009f, lo: 0x92, hi: 0x92},
+	{value: 0x00b1, lo: 0x93, hi: 0x93},
+	{value: 0x0104, lo: 0x94, hi: 0x94},
+	{value: 0x0091, lo: 0x95, hi: 0x95},
+	{value: 0x0097, lo: 0x96, hi: 0x99},
+	{value: 0x00a1, lo: 0x9a, hi: 0x9a},
+	{value: 0x00a7, lo: 0x9b, hi: 0x9c},
+	{value: 0x1999, lo: 0xa8, hi: 0xa8},
+	// Block 0x41, offset 0x1a2
+	{value: 0x0000, lo: 0x0d},
+	{value: 0x8132, lo: 0x90, hi: 0x91},
+	{value: 0x8101, lo: 0x92, hi: 0x93},
+	{value: 0x8132, lo: 0x94, hi: 0x97},
+	{value: 0x8101, lo: 0x98, hi: 0x9a},
+	{value: 0x8132, lo: 0x9b, hi: 0x9c},
+	{value: 0x8132, lo: 0xa1, hi: 0xa1},
+	{value: 0x8101, lo: 0xa5, hi: 0xa6},
+	{value: 0x8132, lo: 0xa7, hi: 0xa7},
+	{value: 0x812d, lo: 0xa8, hi: 0xa8},
+	{value: 0x8132, lo: 0xa9, hi: 0xa9},
+	{value: 0x8101, lo: 0xaa, hi: 0xab},
+	{value: 0x812d, lo: 0xac, hi: 0xaf},
+	{value: 0x8132, lo: 0xb0, hi: 0xb0},
+	// Block 0x42, offset 0x1b0
+	{value: 0x0007, lo: 0x06},
+	{value: 0x2180, lo: 0x89, hi: 0x89},
+	{value: 0xa000, lo: 0x90, hi: 0x90},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	{value: 0xa000, lo: 0x94, hi: 0x94},
+	{value: 0x3bb9, lo: 0x9a, hi: 0x9b},
+	{value: 0x3bc7, lo: 0xae, hi: 0xae},
+	// Block 0x43, offset 0x1b7
+	{value: 0x000e, lo: 0x05},
+	{value: 0x3bce, lo: 0x8d, hi: 0x8e},
+	{value: 0x3bd5, lo: 0x8f, hi: 0x8f},
+	{value: 0xa000, lo: 0x90, hi: 0x90},
+	{value: 0xa000, lo: 0x92, hi: 0x92},
+	{value: 0xa000, lo: 0x94, hi: 0x94},
+	// Block 0x44, offset 0x1bd
+	{value: 0x0173, lo: 0x0e},
+	{value: 0xa000, lo: 0x83, hi: 0x83},
+	{value: 0x3be3, lo: 0x84, hi: 0x84},
+	{value: 0xa000, lo: 0x88, hi: 0x88},
+	{value: 0x3bea, lo: 0x89, hi: 0x89},
+	{value: 0xa000, lo: 0x8b, hi: 0x8b},
+	{value: 0x3bf1, lo: 0x8c, hi: 0x8c},
+	{value: 0xa000, lo: 0xa3, hi: 0xa3},
+	{value: 0x3bf8, lo: 0xa4, hi: 0xa4},
+	{value: 0xa000, lo: 0xa5, hi: 0xa5},
+	{value: 0x3bff, lo: 0xa6, hi: 0xa6},
+	{value: 0x269f, lo: 0xac, hi: 0xad},
+	{value: 0x26a6, lo: 0xaf, hi: 0xaf},
+	{value: 0x281c, lo: 0xb0, hi: 0xb0},
+	{value: 0xa000, lo: 0xbc, hi: 0xbc},
+	// Block 0x45, offset 0x1cc
+	{value: 0x0007, lo: 0x03},
+	{value: 0x3c68, lo: 0xa0, hi: 0xa1},
+	{value: 0x3c92, lo: 0xa2, hi: 0xa3},
+	{value: 0x3cbc, lo: 0xaa, hi: 0xad},
+	// Block 0x46, offset 0x1d0
+	{value: 0x0004, lo: 0x01},
+	{value: 0x048b, lo: 0xa9, hi: 0xaa},
+	// Block 0x47, offset 0x1d2
+	{value: 0x0002, lo: 0x03},
+	{value: 0x0057, lo: 0x80, hi: 0x8f},
+	{value: 0x0083, lo: 0x90, hi: 0xa9},
+	{value: 0x0021, lo: 0xaa, hi: 0xaa},
+	// Block 0x48, offset 0x1d6
+	{value: 0x0000, lo: 0x01},
+	{value: 0x299b, lo: 0x8c, hi: 0x8c},
+	// Block 0x49, offset 0x1d8
+	{value: 0x0263, lo: 0x02},
+	{value: 0x1b8c, lo: 0xb4, hi: 0xb4},
+	{value: 0x192d, lo: 0xb5, hi: 0xb6},
+	// Block 0x4a, offset 0x1db
+	{value: 0x0000, lo: 0x01},
+	{value: 0x4573, lo: 0x9c, hi: 0x9c},
+	// Block 0x4b, offset 0x1dd
+	{value: 0x0000, lo: 0x02},
+	{value: 0x0095, lo: 0xbc, hi: 0xbc},
+	{value: 0x006d, lo: 0xbd, hi: 0xbd},
+	// Block 0x4c, offset 0x1e0
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0xaf, hi: 0xb1},
+	// Block 0x4d, offset 0x1e2
+	{value: 0x0000, lo: 0x02},
+	{value: 0x047f, lo: 0xaf, hi: 0xaf},
+	{value: 0x8104, lo: 0xbf, hi: 0xbf},
+	// Block 0x4e, offset 0x1e5
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0xa0, hi: 0xbf},
+	// Block 0x4f, offset 0x1e7
+	{value: 0x0000, lo: 0x01},
+	{value: 0x0dc3, lo: 0x9f, hi: 0x9f},
+	// Block 0x50, offset 0x1e9
+	{value: 0x0000, lo: 0x01},
+	{value: 0x162f, lo: 0xb3, hi: 0xb3},
+	// Block 0x51, offset 0x1eb
+	{value: 0x0004, lo: 0x0b},
+	{value: 0x1597, lo: 0x80, hi: 0x82},
+	{value: 0x15af, lo: 0x83, hi: 0x83},
+	{value: 0x15c7, lo: 0x84, hi: 0x85},
+	{value: 0x15d7, lo: 0x86, hi: 0x89},
+	{value: 0x15eb, lo: 0x8a, hi: 0x8c},
+	{value: 0x15ff, lo: 0x8d, hi: 0x8d},
+	{value: 0x1607, lo: 0x8e, hi: 0x8e},
+	{value: 0x160f, lo: 0x8f, hi: 0x90},
+	{value: 0x161b, lo: 0x91, hi: 0x93},
+	{value: 0x162b, lo: 0x94, hi: 0x94},
+	{value: 0x1633, lo: 0x95, hi: 0x95},
+	// Block 0x52, offset 0x1f7
+	{value: 0x0004, lo: 0x09},
+	{value: 0x0001, lo: 0x80, hi: 0x80},
+	{value: 0x812c, lo: 0xaa, hi: 0xaa},
+	{value: 0x8131, lo: 0xab, hi: 0xab},
+	{value: 0x8133, lo: 0xac, hi: 0xac},
+	{value: 0x812e, lo: 0xad, hi: 0xad},
+	{value: 0x812f, lo: 0xae, hi: 0xae},
+	{value: 0x812f, lo: 0xaf, hi: 0xaf},
+	{value: 0x04b3, lo: 0xb6, hi: 0xb6},
+	{value: 0x0887, lo: 0xb8, hi: 0xba},
+	// Block 0x53, offset 0x201
+	{value: 0x0005, lo: 0x09},
+	{value: 0x0313, lo: 0xb1, hi: 0xb1},
+	{value: 0x0317, lo: 0xb2, hi: 0xb2},
+	{value: 0x4345, lo: 0xb3, hi: 0xb3},
+	{value: 0x031b, lo: 0xb4, hi: 0xb4},
+	{value: 0x434a, lo: 0xb5, hi: 0xb6},
+	{value: 0x031f, lo: 0xb7, hi: 0xb7},
+	{value: 0x0323, lo: 0xb8, hi: 0xb8},
+	{value: 0x0327, lo: 0xb9, hi: 0xb9},
+	{value: 0x4354, lo: 0xba, hi: 0xbf},
+	// Block 0x54, offset 0x20b
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8132, lo: 0xaf, hi: 0xaf},
+	{value: 0x8132, lo: 0xb4, hi: 0xbd},
+	// Block 0x55, offset 0x20e
+	{value: 0x0000, lo: 0x03},
+	{value: 0x020f, lo: 0x9c, hi: 0x9c},
+	{value: 0x0212, lo: 0x9d, hi: 0x9d},
+	{value: 0x8132, lo: 0x9e, hi: 0x9f},
+	// Block 0x56, offset 0x212
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0xb0, hi: 0xb1},
+	// Block 0x57, offset 0x214
+	{value: 0x0000, lo: 0x01},
+	{value: 0x163b, lo: 0xb0, hi: 0xb0},
+	// Block 0x58, offset 0x216
+	{value: 0x000c, lo: 0x01},
+	{value: 0x00d7, lo: 0xb8, hi: 0xb9},
+	// Block 0x59, offset 0x218
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0x86, hi: 0x86},
+	// Block 0x5a, offset 0x21a
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x84, hi: 0x84},
+	{value: 0x8132, lo: 0xa0, hi: 0xb1},
+	// Block 0x5b, offset 0x21d
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0xab, hi: 0xad},
+	// Block 0x5c, offset 0x21f
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0x93, hi: 0x93},
+	// Block 0x5d, offset 0x221
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8102, lo: 0xb3, hi: 0xb3},
+	// Block 0x5e, offset 0x223
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0x80, hi: 0x80},
+	// Block 0x5f, offset 0x225
+	{value: 0x0000, lo: 0x05},
+	{value: 0x8132, lo: 0xb0, hi: 0xb0},
+	{value: 0x8132, lo: 0xb2, hi: 0xb3},
+	{value: 0x812d, lo: 0xb4, hi: 0xb4},
+	{value: 0x8132, lo: 0xb7, hi: 0xb8},
+	{value: 0x8132, lo: 0xbe, hi: 0xbf},
+	// Block 0x60, offset 0x22b
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8132, lo: 0x81, hi: 0x81},
+	{value: 0x8104, lo: 0xb6, hi: 0xb6},
+	// Block 0x61, offset 0x22e
+	{value: 0x0008, lo: 0x03},
+	{value: 0x1637, lo: 0x9c, hi: 0x9d},
+	{value: 0x0125, lo: 0x9e, hi: 0x9e},
+	{value: 0x1643, lo: 0x9f, hi: 0x9f},
+	// Block 0x62, offset 0x232
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0xad, hi: 0xad},
+	// Block 0x63, offset 0x234
+	{value: 0x0000, lo: 0x06},
+	{value: 0xe500, lo: 0x80, hi: 0x80},
+	{value: 0xc600, lo: 0x81, hi: 0x9b},
+	{value: 0xe500, lo: 0x9c, hi: 0x9c},
+	{value: 0xc600, lo: 0x9d, hi: 0xb7},
+	{value: 0xe500, lo: 0xb8, hi: 0xb8},
+	{value: 0xc600, lo: 0xb9, hi: 0xbf},
+	// Block 0x64, offset 0x23b
+	{value: 0x0000, lo: 0x05},
+	{value: 0xc600, lo: 0x80, hi: 0x93},
+	{value: 0xe500, lo: 0x94, hi: 0x94},
+	{value: 0xc600, lo: 0x95, hi: 0xaf},
+	{value: 0xe500, lo: 0xb0, hi: 0xb0},
+	{value: 0xc600, lo: 0xb1, hi: 0xbf},
+	// Block 0x65, offset 0x241
+	{value: 0x0000, lo: 0x05},
+	{value: 0xc600, lo: 0x80, hi: 0x8b},
+	{value: 0xe500, lo: 0x8c, hi: 0x8c},
+	{value: 0xc600, lo: 0x8d, hi: 0xa7},
+	{value: 0xe500, lo: 0xa8, hi: 0xa8},
+	{value: 0xc600, lo: 0xa9, hi: 0xbf},
+	// Block 0x66, offset 0x247
+	{value: 0x0000, lo: 0x07},
+	{value: 0xc600, lo: 0x80, hi: 0x83},
+	{value: 0xe500, lo: 0x84, hi: 0x84},
+	{value: 0xc600, lo: 0x85, hi: 0x9f},
+	{value: 0xe500, lo: 0xa0, hi: 0xa0},
+	{value: 0xc600, lo: 0xa1, hi: 0xbb},
+	{value: 0xe500, lo: 0xbc, hi: 0xbc},
+	{value: 0xc600, lo: 0xbd, hi: 0xbf},
+	// Block 0x67, offset 0x24f
+	{value: 0x0000, lo: 0x05},
+	{value: 0xc600, lo: 0x80, hi: 0x97},
+	{value: 0xe500, lo: 0x98, hi: 0x98},
+	{value: 0xc600, lo: 0x99, hi: 0xb3},
+	{value: 0xe500, lo: 0xb4, hi: 0xb4},
+	{value: 0xc600, lo: 0xb5, hi: 0xbf},
+	// Block 0x68, offset 0x255
+	{value: 0x0000, lo: 0x05},
+	{value: 0xc600, lo: 0x80, hi: 0x8f},
+	{value: 0xe500, lo: 0x90, hi: 0x90},
+	{value: 0xc600, lo: 0x91, hi: 0xab},
+	{value: 0xe500, lo: 0xac, hi: 0xac},
+	{value: 0xc600, lo: 0xad, hi: 0xbf},
+	// Block 0x69, offset 0x25b
+	{value: 0x0000, lo: 0x05},
+	{value: 0xc600, lo: 0x80, hi: 0x87},
+	{value: 0xe500, lo: 0x88, hi: 0x88},
+	{value: 0xc600, lo: 0x89, hi: 0xa3},
+	{value: 0xe500, lo: 0xa4, hi: 0xa4},
+	{value: 0xc600, lo: 0xa5, hi: 0xbf},
+	// Block 0x6a, offset 0x261
+	{value: 0x0000, lo: 0x03},
+	{value: 0xc600, lo: 0x80, hi: 0x87},
+	{value: 0xe500, lo: 0x88, hi: 0x88},
+	{value: 0xc600, lo: 0x89, hi: 0xa3},
+	// Block 0x6b, offset 0x265
+	{value: 0x0002, lo: 0x01},
+	{value: 0x0003, lo: 0x81, hi: 0xbf},
+	// Block 0x6c, offset 0x267
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0xbd, hi: 0xbd},
+	// Block 0x6d, offset 0x269
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0xa0, hi: 0xa0},
+	// Block 0x6e, offset 0x26b
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0xb6, hi: 0xba},
+	// Block 0x6f, offset 0x26d
+	{value: 0x002c, lo: 0x05},
+	{value: 0x812d, lo: 0x8d, hi: 0x8d},
+	{value: 0x8132, lo: 0x8f, hi: 0x8f},
+	{value: 0x8132, lo: 0xb8, hi: 0xb8},
+	{value: 0x8101, lo: 0xb9, hi: 0xba},
+	{value: 0x8104, lo: 0xbf, hi: 0xbf},
+	// Block 0x70, offset 0x273
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8132, lo: 0xa5, hi: 0xa5},
+	{value: 0x812d, lo: 0xa6, hi: 0xa6},
+	// Block 0x71, offset 0x276
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x86, hi: 0x86},
+	{value: 0x8104, lo: 0xbf, hi: 0xbf},
+	// Block 0x72, offset 0x279
+	{value: 0x17fe, lo: 0x07},
+	{value: 0xa000, lo: 0x99, hi: 0x99},
+	{value: 0x4238, lo: 0x9a, hi: 0x9a},
+	{value: 0xa000, lo: 0x9b, hi: 0x9b},
+	{value: 0x4242, lo: 0x9c, hi: 0x9c},
+	{value: 0xa000, lo: 0xa5, hi: 0xa5},
+	{value: 0x424c, lo: 0xab, hi: 0xab},
+	{value: 0x8104, lo: 0xb9, hi: 0xba},
+	// Block 0x73, offset 0x281
+	{value: 0x0000, lo: 0x06},
+	{value: 0x8132, lo: 0x80, hi: 0x82},
+	{value: 0x9900, lo: 0xa7, hi: 0xa7},
+	{value: 0x2d7e, lo: 0xae, hi: 0xae},
+	{value: 0x2d88, lo: 0xaf, hi: 0xaf},
+	{value: 0xa000, lo: 0xb1, hi: 0xb2},
+	{value: 0x8104, lo: 0xb3, hi: 0xb4},
+	// Block 0x74, offset 0x288
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x80, hi: 0x80},
+	{value: 0x8102, lo: 0x8a, hi: 0x8a},
+	// Block 0x75, offset 0x28b
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0xb5, hi: 0xb5},
+	{value: 0x8102, lo: 0xb6, hi: 0xb6},
+	// Block 0x76, offset 0x28e
+	{value: 0x0002, lo: 0x01},
+	{value: 0x8102, lo: 0xa9, hi: 0xaa},
+	// Block 0x77, offset 0x290
+	{value: 0x0000, lo: 0x07},
+	{value: 0xa000, lo: 0x87, hi: 0x87},
+	{value: 0x2d92, lo: 0x8b, hi: 0x8b},
+	{value: 0x2d9c, lo: 0x8c, hi: 0x8c},
+	{value: 0x8104, lo: 0x8d, hi: 0x8d},
+	{value: 0x9900, lo: 0x97, hi: 0x97},
+	{value: 0x8132, lo: 0xa6, hi: 0xac},
+	{value: 0x8132, lo: 0xb0, hi: 0xb4},
+	// Block 0x78, offset 0x298
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x82, hi: 0x82},
+	{value: 0x8102, lo: 0x86, hi: 0x86},
+	// Block 0x79, offset 0x29b
+	{value: 0x6b5a, lo: 0x06},
+	{value: 0x9900, lo: 0xb0, hi: 0xb0},
+	{value: 0xa000, lo: 0xb9, hi: 0xb9},
+	{value: 0x9900, lo: 0xba, hi: 0xba},
+	{value: 0x2db0, lo: 0xbb, hi: 0xbb},
+	{value: 0x2da6, lo: 0xbc, hi: 0xbd},
+	{value: 0x2dba, lo: 0xbe, hi: 0xbe},
+	// Block 0x7a, offset 0x2a2
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0x82, hi: 0x82},
+	{value: 0x8102, lo: 0x83, hi: 0x83},
+	// Block 0x7b, offset 0x2a5
+	{value: 0x0000, lo: 0x05},
+	{value: 0x9900, lo: 0xaf, hi: 0xaf},
+	{value: 0xa000, lo: 0xb8, hi: 0xb9},
+	{value: 0x2dc4, lo: 0xba, hi: 0xba},
+	{value: 0x2dce, lo: 0xbb, hi: 0xbb},
+	{value: 0x8104, lo: 0xbf, hi: 0xbf},
+	// Block 0x7c, offset 0x2ab
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8102, lo: 0x80, hi: 0x80},
+	// Block 0x7d, offset 0x2ad
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0xbf, hi: 0xbf},
+	// Block 0x7e, offset 0x2af
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8104, lo: 0xb6, hi: 0xb6},
+	{value: 0x8102, lo: 0xb7, hi: 0xb7},
+	// Block 0x7f, offset 0x2b2
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8104, lo: 0xab, hi: 0xab},
+	// Block 0x80, offset 0x2b4
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8101, lo: 0xb0, hi: 0xb4},
+	// Block 0x81, offset 0x2b6
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0xb0, hi: 0xb6},
+	// Block 0x82, offset 0x2b8
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8101, lo: 0x9e, hi: 0x9e},
+	// Block 0x83, offset 0x2ba
+	{value: 0x0000, lo: 0x0c},
+	{value: 0x4662, lo: 0x9e, hi: 0x9e},
+	{value: 0x466c, lo: 0x9f, hi: 0x9f},
+	{value: 0x46a0, lo: 0xa0, hi: 0xa0},
+	{value: 0x46ae, lo: 0xa1, hi: 0xa1},
+	{value: 0x46bc, lo: 0xa2, hi: 0xa2},
+	{value: 0x46ca, lo: 0xa3, hi: 0xa3},
+	{value: 0x46d8, lo: 0xa4, hi: 0xa4},
+	{value: 0x812b, lo: 0xa5, hi: 0xa6},
+	{value: 0x8101, lo: 0xa7, hi: 0xa9},
+	{value: 0x8130, lo: 0xad, hi: 0xad},
+	{value: 0x812b, lo: 0xae, hi: 0xb2},
+	{value: 0x812d, lo: 0xbb, hi: 0xbf},
+	// Block 0x84, offset 0x2c7
+	{value: 0x0000, lo: 0x09},
+	{value: 0x812d, lo: 0x80, hi: 0x82},
+	{value: 0x8132, lo: 0x85, hi: 0x89},
+	{value: 0x812d, lo: 0x8a, hi: 0x8b},
+	{value: 0x8132, lo: 0xaa, hi: 0xad},
+	{value: 0x4676, lo: 0xbb, hi: 0xbb},
+	{value: 0x4680, lo: 0xbc, hi: 0xbc},
+	{value: 0x46e6, lo: 0xbd, hi: 0xbd},
+	{value: 0x4702, lo: 0xbe, hi: 0xbe},
+	{value: 0x46f4, lo: 0xbf, hi: 0xbf},
+	// Block 0x85, offset 0x2d1
+	{value: 0x0000, lo: 0x01},
+	{value: 0x4710, lo: 0x80, hi: 0x80},
+	// Block 0x86, offset 0x2d3
+	{value: 0x0000, lo: 0x01},
+	{value: 0x8132, lo: 0x82, hi: 0x84},
+	// Block 0x87, offset 0x2d5
+	{value: 0x0002, lo: 0x03},
+	{value: 0x0043, lo: 0x80, hi: 0x99},
+	{value: 0x0083, lo: 0x9a, hi: 0xb3},
+	{value: 0x0043, lo: 0xb4, hi: 0xbf},
+	// Block 0x88, offset 0x2d9
+	{value: 0x0002, lo: 0x04},
+	{value: 0x005b, lo: 0x80, hi: 0x8d},
+	{value: 0x0083, lo: 0x8e, hi: 0x94},
+	{value: 0x0093, lo: 0x96, hi: 0xa7},
+	{value: 0x0043, lo: 0xa8, hi: 0xbf},
+	// Block 0x89, offset 0x2de
+	{value: 0x0002, lo: 0x0b},
+	{value: 0x0073, lo: 0x80, hi: 0x81},
+	{value: 0x0083, lo: 0x82, hi: 0x9b},
+	{value: 0x0043, lo: 0x9c, hi: 0x9c},
+	{value: 0x0047, lo: 0x9e, hi: 0x9f},
+	{value: 0x004f, lo: 0xa2, hi: 0xa2},
+	{value: 0x0055, lo: 0xa5, hi: 0xa6},
+	{value: 0x005d, lo: 0xa9, hi: 0xac},
+	{value: 0x0067, lo: 0xae, hi: 0xb5},
+	{value: 0x0083, lo: 0xb6, hi: 0xb9},
+	{value: 0x008d, lo: 0xbb, hi: 0xbb},
+	{value: 0x0091, lo: 0xbd, hi: 0xbf},
+	// Block 0x8a, offset 0x2ea
+	{value: 0x0002, lo: 0x04},
+	{value: 0x0097, lo: 0x80, hi: 0x83},
+	{value: 0x00a1, lo: 0x85, hi: 0x8f},
+	{value: 0x0043, lo: 0x90, hi: 0xa9},
+	{value: 0x0083, lo: 0xaa, hi: 0xbf},
+	// Block 0x8b, offset 0x2ef
+	{value: 0x0002, lo: 0x08},
+	{value: 0x00af, lo: 0x80, hi: 0x83},
+	{value: 0x0043, lo: 0x84, hi: 0x85},
+	{value: 0x0049, lo: 0x87, hi: 0x8a},
+	{value: 0x0055, lo: 0x8d, hi: 0x94},
+	{value: 0x0067, lo: 0x96, hi: 0x9c},
+	{value: 0x0083, lo: 0x9e, hi: 0xb7},
+	{value: 0x0043, lo: 0xb8, hi: 0xb9},
+	{value: 0x0049, lo: 0xbb, hi: 0xbe},
+	// Block 0x8c, offset 0x2f8
+	{value: 0x0002, lo: 0x05},
+	{value: 0x0053, lo: 0x80, hi: 0x84},
+	{value: 0x005f, lo: 0x86, hi: 0x86},
+	{value: 0x0067, lo: 0x8a, hi: 0x90},
+	{value: 0x0083, lo: 0x92, hi: 0xab},
+	{value: 0x0043, lo: 0xac, hi: 0xbf},
+	// Block 0x8d, offset 0x2fe
+	{value: 0x0002, lo: 0x04},
+	{value: 0x006b, lo: 0x80, hi: 0x85},
+	{value: 0x0083, lo: 0x86, hi: 0x9f},
+	{value: 0x0043, lo: 0xa0, hi: 0xb9},
+	{value: 0x0083, lo: 0xba, hi: 0xbf},
+	// Block 0x8e, offset 0x303
+	{value: 0x0002, lo: 0x03},
+	{value: 0x008f, lo: 0x80, hi: 0x93},
+	{value: 0x0043, lo: 0x94, hi: 0xad},
+	{value: 0x0083, lo: 0xae, hi: 0xbf},
+	// Block 0x8f, offset 0x307
+	{value: 0x0002, lo: 0x04},
+	{value: 0x00a7, lo: 0x80, hi: 0x87},
+	{value: 0x0043, lo: 0x88, hi: 0xa1},
+	{value: 0x0083, lo: 0xa2, hi: 0xbb},
+	{value: 0x0043, lo: 0xbc, hi: 0xbf},
+	// Block 0x90, offset 0x30c
+	{value: 0x0002, lo: 0x03},
+	{value: 0x004b, lo: 0x80, hi: 0x95},
+	{value: 0x0083, lo: 0x96, hi: 0xaf},
+	{value: 0x0043, lo: 0xb0, hi: 0xbf},
+	// Block 0x91, offset 0x310
+	{value: 0x0003, lo: 0x0f},
+	{value: 0x01b8, lo: 0x80, hi: 0x80},
+	{value: 0x045f, lo: 0x81, hi: 0x81},
+	{value: 0x01bb, lo: 0x82, hi: 0x9a},
+	{value: 0x045b, lo: 0x9b, hi: 0x9b},
+	{value: 0x01c7, lo: 0x9c, hi: 0x9c},
+	{value: 0x01d0, lo: 0x9d, hi: 0x9d},
+	{value: 0x01d6, lo: 0x9e, hi: 0x9e},
+	{value: 0x01fa, lo: 0x9f, hi: 0x9f},
+	{value: 0x01eb, lo: 0xa0, hi: 0xa0},
+	{value: 0x01e8, lo: 0xa1, hi: 0xa1},
+	{value: 0x0173, lo: 0xa2, hi: 0xb2},
+	{value: 0x0188, lo: 0xb3, hi: 0xb3},
+	{value: 0x01a6, lo: 0xb4, hi: 0xba},
+	{value: 0x045f, lo: 0xbb, hi: 0xbb},
+	{value: 0x01bb, lo: 0xbc, hi: 0xbf},
+	// Block 0x92, offset 0x320
+	{value: 0x0003, lo: 0x0d},
+	{value: 0x01c7, lo: 0x80, hi: 0x94},
+	{value: 0x045b, lo: 0x95, hi: 0x95},
+	{value: 0x01c7, lo: 0x96, hi: 0x96},
+	{value: 0x01d0, lo: 0x97, hi: 0x97},
+	{value: 0x01d6, lo: 0x98, hi: 0x98},
+	{value: 0x01fa, lo: 0x99, hi: 0x99},
+	{value: 0x01eb, lo: 0x9a, hi: 0x9a},
+	{value: 0x01e8, lo: 0x9b, hi: 0x9b},
+	{value: 0x0173, lo: 0x9c, hi: 0xac},
+	{value: 0x0188, lo: 0xad, hi: 0xad},
+	{value: 0x01a6, lo: 0xae, hi: 0xb4},
+	{value: 0x045f, lo: 0xb5, hi: 0xb5},
+	{value: 0x01bb, lo: 0xb6, hi: 0xbf},
+	// Block 0x93, offset 0x32e
+	{value: 0x0003, lo: 0x0d},
+	{value: 0x01d9, lo: 0x80, hi: 0x8e},
+	{value: 0x045b, lo: 0x8f, hi: 0x8f},
+	{value: 0x01c7, lo: 0x90, hi: 0x90},
+	{value: 0x01d0, lo: 0x91, hi: 0x91},
+	{value: 0x01d6, lo: 0x92, hi: 0x92},
+	{value: 0x01fa, lo: 0x93, hi: 0x93},
+	{value: 0x01eb, lo: 0x94, hi: 0x94},
+	{value: 0x01e8, lo: 0x95, hi: 0x95},
+	{value: 0x0173, lo: 0x96, hi: 0xa6},
+	{value: 0x0188, lo: 0xa7, hi: 0xa7},
+	{value: 0x01a6, lo: 0xa8, hi: 0xae},
+	{value: 0x045f, lo: 0xaf, hi: 0xaf},
+	{value: 0x01bb, lo: 0xb0, hi: 0xbf},
+	// Block 0x94, offset 0x33c
+	{value: 0x0003, lo: 0x0d},
+	{value: 0x01eb, lo: 0x80, hi: 0x88},
+	{value: 0x045b, lo: 0x89, hi: 0x89},
+	{value: 0x01c7, lo: 0x8a, hi: 0x8a},
+	{value: 0x01d0, lo: 0x8b, hi: 0x8b},
+	{value: 0x01d6, lo: 0x8c, hi: 0x8c},
+	{value: 0x01fa, lo: 0x8d, hi: 0x8d},
+	{value: 0x01eb, lo: 0x8e, hi: 0x8e},
+	{value: 0x01e8, lo: 0x8f, hi: 0x8f},
+	{value: 0x0173, lo: 0x90, hi: 0xa0},
+	{value: 0x0188, lo: 0xa1, hi: 0xa1},
+	{value: 0x01a6, lo: 0xa2, hi: 0xa8},
+	{value: 0x045f, lo: 0xa9, hi: 0xa9},
+	{value: 0x01bb, lo: 0xaa, hi: 0xbf},
+	// Block 0x95, offset 0x34a
+	{value: 0x0000, lo: 0x05},
+	{value: 0x8132, lo: 0x80, hi: 0x86},
+	{value: 0x8132, lo: 0x88, hi: 0x98},
+	{value: 0x8132, lo: 0x9b, hi: 0xa1},
+	{value: 0x8132, lo: 0xa3, hi: 0xa4},
+	{value: 0x8132, lo: 0xa6, hi: 0xaa},
+	// Block 0x96, offset 0x350
+	{value: 0x0000, lo: 0x01},
+	{value: 0x812d, lo: 0x90, hi: 0x96},
+	// Block 0x97, offset 0x352
+	{value: 0x0000, lo: 0x02},
+	{value: 0x8132, lo: 0x84, hi: 0x89},
+	{value: 0x8102, lo: 0x8a, hi: 0x8a},
+	// Block 0x98, offset 0x355
+	{value: 0x0002, lo: 0x09},
+	{value: 0x0063, lo: 0x80, hi: 0x89},
+	{value: 0x1951, lo: 0x8a, hi: 0x8a},
+	{value: 0x1981, lo: 0x8b, hi: 0x8b},
+	{value: 0x199c, lo: 0x8c, hi: 0x8c},
+	{value: 0x19a2, lo: 0x8d, hi: 0x8d},
+	{value: 0x1bc0, lo: 0x8e, hi: 0x8e},
+	{value: 0x19ae, lo: 0x8f, hi: 0x8f},
+	{value: 0x197b, lo: 0xaa, hi: 0xaa},
+	{value: 0x197e, lo: 0xab, hi: 0xab},
+	// Block 0x99, offset 0x35f
+	{value: 0x0000, lo: 0x01},
+	{value: 0x193f, lo: 0x90, hi: 0x90},
+	// Block 0x9a, offset 0x361
+	{value: 0x0028, lo: 0x09},
+	{value: 0x2862, lo: 0x80, hi: 0x80},
+	{value: 0x2826, lo: 0x81, hi: 0x81},
+	{value: 0x2830, lo: 0x82, hi: 0x82},
+	{value: 0x2844, lo: 0x83, hi: 0x84},
+	{value: 0x284e, lo: 0x85, hi: 0x86},
+	{value: 0x283a, lo: 0x87, hi: 0x87},
+	{value: 0x2858, lo: 0x88, hi: 0x88},
+	{value: 0x0b6f, lo: 0x90, hi: 0x90},
+	{value: 0x08e7, lo: 0x91, hi: 0x91},
+}
+
+// recompMap: 7520 bytes (entries only)
+var recompMap = map[uint32]rune{
+	0x00410300: 0x00C0,
+	0x00410301: 0x00C1,
+	0x00410302: 0x00C2,
+	0x00410303: 0x00C3,
+	0x00410308: 0x00C4,
+	0x0041030A: 0x00C5,
+	0x00430327: 0x00C7,
+	0x00450300: 0x00C8,
+	0x00450301: 0x00C9,
+	0x00450302: 0x00CA,
+	0x00450308: 0x00CB,
+	0x00490300: 0x00CC,
+	0x00490301: 0x00CD,
+	0x00490302: 0x00CE,
+	0x00490308: 0x00CF,
+	0x004E0303: 0x00D1,
+	0x004F0300: 0x00D2,
+	0x004F0301: 0x00D3,
+	0x004F0302: 0x00D4,
+	0x004F0303: 0x00D5,
+	0x004F0308: 0x00D6,
+	0x00550300: 0x00D9,
+	0x00550301: 0x00DA,
+	0x00550302: 0x00DB,
+	0x00550308: 0x00DC,
+	0x00590301: 0x00DD,
+	0x00610300: 0x00E0,
+	0x00610301: 0x00E1,
+	0x00610302: 0x00E2,
+	0x00610303: 0x00E3,
+	0x00610308: 0x00E4,
+	0x0061030A: 0x00E5,
+	0x00630327: 0x00E7,
+	0x00650300: 0x00E8,
+	0x00650301: 0x00E9,
+	0x00650302: 0x00EA,
+	0x00650308: 0x00EB,
+	0x00690300: 0x00EC,
+	0x00690301: 0x00ED,
+	0x00690302: 0x00EE,
+	0x00690308: 0x00EF,
+	0x006E0303: 0x00F1,
+	0x006F0300: 0x00F2,
+	0x006F0301: 0x00F3,
+	0x006F0302: 0x00F4,
+	0x006F0303: 0x00F5,
+	0x006F0308: 0x00F6,
+	0x00750300: 0x00F9,
+	0x00750301: 0x00FA,
+	0x00750302: 0x00FB,
+	0x00750308: 0x00FC,
+	0x00790301: 0x00FD,
+	0x00790308: 0x00FF,
+	0x00410304: 0x0100,
+	0x00610304: 0x0101,
+	0x00410306: 0x0102,
+	0x00610306: 0x0103,
+	0x00410328: 0x0104,
+	0x00610328: 0x0105,
+	0x00430301: 0x0106,
+	0x00630301: 0x0107,
+	0x00430302: 0x0108,
+	0x00630302: 0x0109,
+	0x00430307: 0x010A,
+	0x00630307: 0x010B,
+	0x0043030C: 0x010C,
+	0x0063030C: 0x010D,
+	0x0044030C: 0x010E,
+	0x0064030C: 0x010F,
+	0x00450304: 0x0112,
+	0x00650304: 0x0113,
+	0x00450306: 0x0114,
+	0x00650306: 0x0115,
+	0x00450307: 0x0116,
+	0x00650307: 0x0117,
+	0x00450328: 0x0118,
+	0x00650328: 0x0119,
+	0x0045030C: 0x011A,
+	0x0065030C: 0x011B,
+	0x00470302: 0x011C,
+	0x00670302: 0x011D,
+	0x00470306: 0x011E,
+	0x00670306: 0x011F,
+	0x00470307: 0x0120,
+	0x00670307: 0x0121,
+	0x00470327: 0x0122,
+	0x00670327: 0x0123,
+	0x00480302: 0x0124,
+	0x00680302: 0x0125,
+	0x00490303: 0x0128,
+	0x00690303: 0x0129,
+	0x00490304: 0x012A,
+	0x00690304: 0x012B,
+	0x00490306: 0x012C,
+	0x00690306: 0x012D,
+	0x00490328: 0x012E,
+	0x00690328: 0x012F,
+	0x00490307: 0x0130,
+	0x004A0302: 0x0134,
+	0x006A0302: 0x0135,
+	0x004B0327: 0x0136,
+	0x006B0327: 0x0137,
+	0x004C0301: 0x0139,
+	0x006C0301: 0x013A,
+	0x004C0327: 0x013B,
+	0x006C0327: 0x013C,
+	0x004C030C: 0x013D,
+	0x006C030C: 0x013E,
+	0x004E0301: 0x0143,
+	0x006E0301: 0x0144,
+	0x004E0327: 0x0145,
+	0x006E0327: 0x0146,
+	0x004E030C: 0x0147,
+	0x006E030C: 0x0148,
+	0x004F0304: 0x014C,
+	0x006F0304: 0x014D,
+	0x004F0306: 0x014E,
+	0x006F0306: 0x014F,
+	0x004F030B: 0x0150,
+	0x006F030B: 0x0151,
+	0x00520301: 0x0154,
+	0x00720301: 0x0155,
+	0x00520327: 0x0156,
+	0x00720327: 0x0157,
+	0x0052030C: 0x0158,
+	0x0072030C: 0x0159,
+	0x00530301: 0x015A,
+	0x00730301: 0x015B,
+	0x00530302: 0x015C,
+	0x00730302: 0x015D,
+	0x00530327: 0x015E,
+	0x00730327: 0x015F,
+	0x0053030C: 0x0160,
+	0x0073030C: 0x0161,
+	0x00540327: 0x0162,
+	0x00740327: 0x0163,
+	0x0054030C: 0x0164,
+	0x0074030C: 0x0165,
+	0x00550303: 0x0168,
+	0x00750303: 0x0169,
+	0x00550304: 0x016A,
+	0x00750304: 0x016B,
+	0x00550306: 0x016C,
+	0x00750306: 0x016D,
+	0x0055030A: 0x016E,
+	0x0075030A: 0x016F,
+	0x0055030B: 0x0170,
+	0x0075030B: 0x0171,
+	0x00550328: 0x0172,
+	0x00750328: 0x0173,
+	0x00570302: 0x0174,
+	0x00770302: 0x0175,
+	0x00590302: 0x0176,
+	0x00790302: 0x0177,
+	0x00590308: 0x0178,
+	0x005A0301: 0x0179,
+	0x007A0301: 0x017A,
+	0x005A0307: 0x017B,
+	0x007A0307: 0x017C,
+	0x005A030C: 0x017D,
+	0x007A030C: 0x017E,
+	0x004F031B: 0x01A0,
+	0x006F031B: 0x01A1,
+	0x0055031B: 0x01AF,
+	0x0075031B: 0x01B0,
+	0x0041030C: 0x01CD,
+	0x0061030C: 0x01CE,
+	0x0049030C: 0x01CF,
+	0x0069030C: 0x01D0,
+	0x004F030C: 0x01D1,
+	0x006F030C: 0x01D2,
+	0x0055030C: 0x01D3,
+	0x0075030C: 0x01D4,
+	0x00DC0304: 0x01D5,
+	0x00FC0304: 0x01D6,
+	0x00DC0301: 0x01D7,
+	0x00FC0301: 0x01D8,
+	0x00DC030C: 0x01D9,
+	0x00FC030C: 0x01DA,
+	0x00DC0300: 0x01DB,
+	0x00FC0300: 0x01DC,
+	0x00C40304: 0x01DE,
+	0x00E40304: 0x01DF,
+	0x02260304: 0x01E0,
+	0x02270304: 0x01E1,
+	0x00C60304: 0x01E2,
+	0x00E60304: 0x01E3,
+	0x0047030C: 0x01E6,
+	0x0067030C: 0x01E7,
+	0x004B030C: 0x01E8,
+	0x006B030C: 0x01E9,
+	0x004F0328: 0x01EA,
+	0x006F0328: 0x01EB,
+	0x01EA0304: 0x01EC,
+	0x01EB0304: 0x01ED,
+	0x01B7030C: 0x01EE,
+	0x0292030C: 0x01EF,
+	0x006A030C: 0x01F0,
+	0x00470301: 0x01F4,
+	0x00670301: 0x01F5,
+	0x004E0300: 0x01F8,
+	0x006E0300: 0x01F9,
+	0x00C50301: 0x01FA,
+	0x00E50301: 0x01FB,
+	0x00C60301: 0x01FC,
+	0x00E60301: 0x01FD,
+	0x00D80301: 0x01FE,
+	0x00F80301: 0x01FF,
+	0x0041030F: 0x0200,
+	0x0061030F: 0x0201,
+	0x00410311: 0x0202,
+	0x00610311: 0x0203,
+	0x0045030F: 0x0204,
+	0x0065030F: 0x0205,
+	0x00450311: 0x0206,
+	0x00650311: 0x0207,
+	0x0049030F: 0x0208,
+	0x0069030F: 0x0209,
+	0x00490311: 0x020A,
+	0x00690311: 0x020B,
+	0x004F030F: 0x020C,
+	0x006F030F: 0x020D,
+	0x004F0311: 0x020E,
+	0x006F0311: 0x020F,
+	0x0052030F: 0x0210,
+	0x0072030F: 0x0211,
+	0x00520311: 0x0212,
+	0x00720311: 0x0213,
+	0x0055030F: 0x0214,
+	0x0075030F: 0x0215,
+	0x00550311: 0x0216,
+	0x00750311: 0x0217,
+	0x00530326: 0x0218,
+	0x00730326: 0x0219,
+	0x00540326: 0x021A,
+	0x00740326: 0x021B,
+	0x0048030C: 0x021E,
+	0x0068030C: 0x021F,
+	0x00410307: 0x0226,
+	0x00610307: 0x0227,
+	0x00450327: 0x0228,
+	0x00650327: 0x0229,
+	0x00D60304: 0x022A,
+	0x00F60304: 0x022B,
+	0x00D50304: 0x022C,
+	0x00F50304: 0x022D,
+	0x004F0307: 0x022E,
+	0x006F0307: 0x022F,
+	0x022E0304: 0x0230,
+	0x022F0304: 0x0231,
+	0x00590304: 0x0232,
+	0x00790304: 0x0233,
+	0x00A80301: 0x0385,
+	0x03910301: 0x0386,
+	0x03950301: 0x0388,
+	0x03970301: 0x0389,
+	0x03990301: 0x038A,
+	0x039F0301: 0x038C,
+	0x03A50301: 0x038E,
+	0x03A90301: 0x038F,
+	0x03CA0301: 0x0390,
+	0x03990308: 0x03AA,
+	0x03A50308: 0x03AB,
+	0x03B10301: 0x03AC,
+	0x03B50301: 0x03AD,
+	0x03B70301: 0x03AE,
+	0x03B90301: 0x03AF,
+	0x03CB0301: 0x03B0,
+	0x03B90308: 0x03CA,
+	0x03C50308: 0x03CB,
+	0x03BF0301: 0x03CC,
+	0x03C50301: 0x03CD,
+	0x03C90301: 0x03CE,
+	0x03D20301: 0x03D3,
+	0x03D20308: 0x03D4,
+	0x04150300: 0x0400,
+	0x04150308: 0x0401,
+	0x04130301: 0x0403,
+	0x04060308: 0x0407,
+	0x041A0301: 0x040C,
+	0x04180300: 0x040D,
+	0x04230306: 0x040E,
+	0x04180306: 0x0419,
+	0x04380306: 0x0439,
+	0x04350300: 0x0450,
+	0x04350308: 0x0451,
+	0x04330301: 0x0453,
+	0x04560308: 0x0457,
+	0x043A0301: 0x045C,
+	0x04380300: 0x045D,
+	0x04430306: 0x045E,
+	0x0474030F: 0x0476,
+	0x0475030F: 0x0477,
+	0x04160306: 0x04C1,
+	0x04360306: 0x04C2,
+	0x04100306: 0x04D0,
+	0x04300306: 0x04D1,
+	0x04100308: 0x04D2,
+	0x04300308: 0x04D3,
+	0x04150306: 0x04D6,
+	0x04350306: 0x04D7,
+	0x04D80308: 0x04DA,
+	0x04D90308: 0x04DB,
+	0x04160308: 0x04DC,
+	0x04360308: 0x04DD,
+	0x04170308: 0x04DE,
+	0x04370308: 0x04DF,
+	0x04180304: 0x04E2,
+	0x04380304: 0x04E3,
+	0x04180308: 0x04E4,
+	0x04380308: 0x04E5,
+	0x041E0308: 0x04E6,
+	0x043E0308: 0x04E7,
+	0x04E80308: 0x04EA,
+	0x04E90308: 0x04EB,
+	0x042D0308: 0x04EC,
+	0x044D0308: 0x04ED,
+	0x04230304: 0x04EE,
+	0x04430304: 0x04EF,
+	0x04230308: 0x04F0,
+	0x04430308: 0x04F1,
+	0x0423030B: 0x04F2,
+	0x0443030B: 0x04F3,
+	0x04270308: 0x04F4,
+	0x04470308: 0x04F5,
+	0x042B0308: 0x04F8,
+	0x044B0308: 0x04F9,
+	0x06270653: 0x0622,
+	0x06270654: 0x0623,
+	0x06480654: 0x0624,
+	0x06270655: 0x0625,
+	0x064A0654: 0x0626,
+	0x06D50654: 0x06C0,
+	0x06C10654: 0x06C2,
+	0x06D20654: 0x06D3,
+	0x0928093C: 0x0929,
+	0x0930093C: 0x0931,
+	0x0933093C: 0x0934,
+	0x09C709BE: 0x09CB,
+	0x09C709D7: 0x09CC,
+	0x0B470B56: 0x0B48,
+	0x0B470B3E: 0x0B4B,
+	0x0B470B57: 0x0B4C,
+	0x0B920BD7: 0x0B94,
+	0x0BC60BBE: 0x0BCA,
+	0x0BC70BBE: 0x0BCB,
+	0x0BC60BD7: 0x0BCC,
+	0x0C460C56: 0x0C48,
+	0x0CBF0CD5: 0x0CC0,
+	0x0CC60CD5: 0x0CC7,
+	0x0CC60CD6: 0x0CC8,
+	0x0CC60CC2: 0x0CCA,
+	0x0CCA0CD5: 0x0CCB,
+	0x0D460D3E: 0x0D4A,
+	0x0D470D3E: 0x0D4B,
+	0x0D460D57: 0x0D4C,
+	0x0DD90DCA: 0x0DDA,
+	0x0DD90DCF: 0x0DDC,
+	0x0DDC0DCA: 0x0DDD,
+	0x0DD90DDF: 0x0DDE,
+	0x1025102E: 0x1026,
+	0x1B051B35: 0x1B06,
+	0x1B071B35: 0x1B08,
+	0x1B091B35: 0x1B0A,
+	0x1B0B1B35: 0x1B0C,
+	0x1B0D1B35: 0x1B0E,
+	0x1B111B35: 0x1B12,
+	0x1B3A1B35: 0x1B3B,
+	0x1B3C1B35: 0x1B3D,
+	0x1B3E1B35: 0x1B40,
+	0x1B3F1B35: 0x1B41,
+	0x1B421B35: 0x1B43,
+	0x00410325: 0x1E00,
+	0x00610325: 0x1E01,
+	0x00420307: 0x1E02,
+	0x00620307: 0x1E03,
+	0x00420323: 0x1E04,
+	0x00620323: 0x1E05,
+	0x00420331: 0x1E06,
+	0x00620331: 0x1E07,
+	0x00C70301: 0x1E08,
+	0x00E70301: 0x1E09,
+	0x00440307: 0x1E0A,
+	0x00640307: 0x1E0B,
+	0x00440323: 0x1E0C,
+	0x00640323: 0x1E0D,
+	0x00440331: 0x1E0E,
+	0x00640331: 0x1E0F,
+	0x00440327: 0x1E10,
+	0x00640327: 0x1E11,
+	0x0044032D: 0x1E12,
+	0x0064032D: 0x1E13,
+	0x01120300: 0x1E14,
+	0x01130300: 0x1E15,
+	0x01120301: 0x1E16,
+	0x01130301: 0x1E17,
+	0x0045032D: 0x1E18,
+	0x0065032D: 0x1E19,
+	0x00450330: 0x1E1A,
+	0x00650330: 0x1E1B,
+	0x02280306: 0x1E1C,
+	0x02290306: 0x1E1D,
+	0x00460307: 0x1E1E,
+	0x00660307: 0x1E1F,
+	0x00470304: 0x1E20,
+	0x00670304: 0x1E21,
+	0x00480307: 0x1E22,
+	0x00680307: 0x1E23,
+	0x00480323: 0x1E24,
+	0x00680323: 0x1E25,
+	0x00480308: 0x1E26,
+	0x00680308: 0x1E27,
+	0x00480327: 0x1E28,
+	0x00680327: 0x1E29,
+	0x0048032E: 0x1E2A,
+	0x0068032E: 0x1E2B,
+	0x00490330: 0x1E2C,
+	0x00690330: 0x1E2D,
+	0x00CF0301: 0x1E2E,
+	0x00EF0301: 0x1E2F,
+	0x004B0301: 0x1E30,
+	0x006B0301: 0x1E31,
+	0x004B0323: 0x1E32,
+	0x006B0323: 0x1E33,
+	0x004B0331: 0x1E34,
+	0x006B0331: 0x1E35,
+	0x004C0323: 0x1E36,
+	0x006C0323: 0x1E37,
+	0x1E360304: 0x1E38,
+	0x1E370304: 0x1E39,
+	0x004C0331: 0x1E3A,
+	0x006C0331: 0x1E3B,
+	0x004C032D: 0x1E3C,
+	0x006C032D: 0x1E3D,
+	0x004D0301: 0x1E3E,
+	0x006D0301: 0x1E3F,
+	0x004D0307: 0x1E40,
+	0x006D0307: 0x1E41,
+	0x004D0323: 0x1E42,
+	0x006D0323: 0x1E43,
+	0x004E0307: 0x1E44,
+	0x006E0307: 0x1E45,
+	0x004E0323: 0x1E46,
+	0x006E0323: 0x1E47,
+	0x004E0331: 0x1E48,
+	0x006E0331: 0x1E49,
+	0x004E032D: 0x1E4A,
+	0x006E032D: 0x1E4B,
+	0x00D50301: 0x1E4C,
+	0x00F50301: 0x1E4D,
+	0x00D50308: 0x1E4E,
+	0x00F50308: 0x1E4F,
+	0x014C0300: 0x1E50,
+	0x014D0300: 0x1E51,
+	0x014C0301: 0x1E52,
+	0x014D0301: 0x1E53,
+	0x00500301: 0x1E54,
+	0x00700301: 0x1E55,
+	0x00500307: 0x1E56,
+	0x00700307: 0x1E57,
+	0x00520307: 0x1E58,
+	0x00720307: 0x1E59,
+	0x00520323: 0x1E5A,
+	0x00720323: 0x1E5B,
+	0x1E5A0304: 0x1E5C,
+	0x1E5B0304: 0x1E5D,
+	0x00520331: 0x1E5E,
+	0x00720331: 0x1E5F,
+	0x00530307: 0x1E60,
+	0x00730307: 0x1E61,
+	0x00530323: 0x1E62,
+	0x00730323: 0x1E63,
+	0x015A0307: 0x1E64,
+	0x015B0307: 0x1E65,
+	0x01600307: 0x1E66,
+	0x01610307: 0x1E67,
+	0x1E620307: 0x1E68,
+	0x1E630307: 0x1E69,
+	0x00540307: 0x1E6A,
+	0x00740307: 0x1E6B,
+	0x00540323: 0x1E6C,
+	0x00740323: 0x1E6D,
+	0x00540331: 0x1E6E,
+	0x00740331: 0x1E6F,
+	0x0054032D: 0x1E70,
+	0x0074032D: 0x1E71,
+	0x00550324: 0x1E72,
+	0x00750324: 0x1E73,
+	0x00550330: 0x1E74,
+	0x00750330: 0x1E75,
+	0x0055032D: 0x1E76,
+	0x0075032D: 0x1E77,
+	0x01680301: 0x1E78,
+	0x01690301: 0x1E79,
+	0x016A0308: 0x1E7A,
+	0x016B0308: 0x1E7B,
+	0x00560303: 0x1E7C,
+	0x00760303: 0x1E7D,
+	0x00560323: 0x1E7E,
+	0x00760323: 0x1E7F,
+	0x00570300: 0x1E80,
+	0x00770300: 0x1E81,
+	0x00570301: 0x1E82,
+	0x00770301: 0x1E83,
+	0x00570308: 0x1E84,
+	0x00770308: 0x1E85,
+	0x00570307: 0x1E86,
+	0x00770307: 0x1E87,
+	0x00570323: 0x1E88,
+	0x00770323: 0x1E89,
+	0x00580307: 0x1E8A,
+	0x00780307: 0x1E8B,
+	0x00580308: 0x1E8C,
+	0x00780308: 0x1E8D,
+	0x00590307: 0x1E8E,
+	0x00790307: 0x1E8F,
+	0x005A0302: 0x1E90,
+	0x007A0302: 0x1E91,
+	0x005A0323: 0x1E92,
+	0x007A0323: 0x1E93,
+	0x005A0331: 0x1E94,
+	0x007A0331: 0x1E95,
+	0x00680331: 0x1E96,
+	0x00740308: 0x1E97,
+	0x0077030A: 0x1E98,
+	0x0079030A: 0x1E99,
+	0x017F0307: 0x1E9B,
+	0x00410323: 0x1EA0,
+	0x00610323: 0x1EA1,
+	0x00410309: 0x1EA2,
+	0x00610309: 0x1EA3,
+	0x00C20301: 0x1EA4,
+	0x00E20301: 0x1EA5,
+	0x00C20300: 0x1EA6,
+	0x00E20300: 0x1EA7,
+	0x00C20309: 0x1EA8,
+	0x00E20309: 0x1EA9,
+	0x00C20303: 0x1EAA,
+	0x00E20303: 0x1EAB,
+	0x1EA00302: 0x1EAC,
+	0x1EA10302: 0x1EAD,
+	0x01020301: 0x1EAE,
+	0x01030301: 0x1EAF,
+	0x01020300: 0x1EB0,
+	0x01030300: 0x1EB1,
+	0x01020309: 0x1EB2,
+	0x01030309: 0x1EB3,
+	0x01020303: 0x1EB4,
+	0x01030303: 0x1EB5,
+	0x1EA00306: 0x1EB6,
+	0x1EA10306: 0x1EB7,
+	0x00450323: 0x1EB8,
+	0x00650323: 0x1EB9,
+	0x00450309: 0x1EBA,
+	0x00650309: 0x1EBB,
+	0x00450303: 0x1EBC,
+	0x00650303: 0x1EBD,
+	0x00CA0301: 0x1EBE,
+	0x00EA0301: 0x1EBF,
+	0x00CA0300: 0x1EC0,
+	0x00EA0300: 0x1EC1,
+	0x00CA0309: 0x1EC2,
+	0x00EA0309: 0x1EC3,
+	0x00CA0303: 0x1EC4,
+	0x00EA0303: 0x1EC5,
+	0x1EB80302: 0x1EC6,
+	0x1EB90302: 0x1EC7,
+	0x00490309: 0x1EC8,
+	0x00690309: 0x1EC9,
+	0x00490323: 0x1ECA,
+	0x00690323: 0x1ECB,
+	0x004F0323: 0x1ECC,
+	0x006F0323: 0x1ECD,
+	0x004F0309: 0x1ECE,
+	0x006F0309: 0x1ECF,
+	0x00D40301: 0x1ED0,
+	0x00F40301: 0x1ED1,
+	0x00D40300: 0x1ED2,
+	0x00F40300: 0x1ED3,
+	0x00D40309: 0x1ED4,
+	0x00F40309: 0x1ED5,
+	0x00D40303: 0x1ED6,
+	0x00F40303: 0x1ED7,
+	0x1ECC0302: 0x1ED8,
+	0x1ECD0302: 0x1ED9,
+	0x01A00301: 0x1EDA,
+	0x01A10301: 0x1EDB,
+	0x01A00300: 0x1EDC,
+	0x01A10300: 0x1EDD,
+	0x01A00309: 0x1EDE,
+	0x01A10309: 0x1EDF,
+	0x01A00303: 0x1EE0,
+	0x01A10303: 0x1EE1,
+	0x01A00323: 0x1EE2,
+	0x01A10323: 0x1EE3,
+	0x00550323: 0x1EE4,
+	0x00750323: 0x1EE5,
+	0x00550309: 0x1EE6,
+	0x00750309: 0x1EE7,
+	0x01AF0301: 0x1EE8,
+	0x01B00301: 0x1EE9,
+	0x01AF0300: 0x1EEA,
+	0x01B00300: 0x1EEB,
+	0x01AF0309: 0x1EEC,
+	0x01B00309: 0x1EED,
+	0x01AF0303: 0x1EEE,
+	0x01B00303: 0x1EEF,
+	0x01AF0323: 0x1EF0,
+	0x01B00323: 0x1EF1,
+	0x00590300: 0x1EF2,
+	0x00790300: 0x1EF3,
+	0x00590323: 0x1EF4,
+	0x00790323: 0x1EF5,
+	0x00590309: 0x1EF6,
+	0x00790309: 0x1EF7,
+	0x00590303: 0x1EF8,
+	0x00790303: 0x1EF9,
+	0x03B10313: 0x1F00,
+	0x03B10314: 0x1F01,
+	0x1F000300: 0x1F02,
+	0x1F010300: 0x1F03,
+	0x1F000301: 0x1F04,
+	0x1F010301: 0x1F05,
+	0x1F000342: 0x1F06,
+	0x1F010342: 0x1F07,
+	0x03910313: 0x1F08,
+	0x03910314: 0x1F09,
+	0x1F080300: 0x1F0A,
+	0x1F090300: 0x1F0B,
+	0x1F080301: 0x1F0C,
+	0x1F090301: 0x1F0D,
+	0x1F080342: 0x1F0E,
+	0x1F090342: 0x1F0F,
+	0x03B50313: 0x1F10,
+	0x03B50314: 0x1F11,
+	0x1F100300: 0x1F12,
+	0x1F110300: 0x1F13,
+	0x1F100301: 0x1F14,
+	0x1F110301: 0x1F15,
+	0x03950313: 0x1F18,
+	0x03950314: 0x1F19,
+	0x1F180300: 0x1F1A,
+	0x1F190300: 0x1F1B,
+	0x1F180301: 0x1F1C,
+	0x1F190301: 0x1F1D,
+	0x03B70313: 0x1F20,
+	0x03B70314: 0x1F21,
+	0x1F200300: 0x1F22,
+	0x1F210300: 0x1F23,
+	0x1F200301: 0x1F24,
+	0x1F210301: 0x1F25,
+	0x1F200342: 0x1F26,
+	0x1F210342: 0x1F27,
+	0x03970313: 0x1F28,
+	0x03970314: 0x1F29,
+	0x1F280300: 0x1F2A,
+	0x1F290300: 0x1F2B,
+	0x1F280301: 0x1F2C,
+	0x1F290301: 0x1F2D,
+	0x1F280342: 0x1F2E,
+	0x1F290342: 0x1F2F,
+	0x03B90313: 0x1F30,
+	0x03B90314: 0x1F31,
+	0x1F300300: 0x1F32,
+	0x1F310300: 0x1F33,
+	0x1F300301: 0x1F34,
+	0x1F310301: 0x1F35,
+	0x1F300342: 0x1F36,
+	0x1F310342: 0x1F37,
+	0x03990313: 0x1F38,
+	0x03990314: 0x1F39,
+	0x1F380300: 0x1F3A,
+	0x1F390300: 0x1F3B,
+	0x1F380301: 0x1F3C,
+	0x1F390301: 0x1F3D,
+	0x1F380342: 0x1F3E,
+	0x1F390342: 0x1F3F,
+	0x03BF0313: 0x1F40,
+	0x03BF0314: 0x1F41,
+	0x1F400300: 0x1F42,
+	0x1F410300: 0x1F43,
+	0x1F400301: 0x1F44,
+	0x1F410301: 0x1F45,
+	0x039F0313: 0x1F48,
+	0x039F0314: 0x1F49,
+	0x1F480300: 0x1F4A,
+	0x1F490300: 0x1F4B,
+	0x1F480301: 0x1F4C,
+	0x1F490301: 0x1F4D,
+	0x03C50313: 0x1F50,
+	0x03C50314: 0x1F51,
+	0x1F500300: 0x1F52,
+	0x1F510300: 0x1F53,
+	0x1F500301: 0x1F54,
+	0x1F510301: 0x1F55,
+	0x1F500342: 0x1F56,
+	0x1F510342: 0x1F57,
+	0x03A50314: 0x1F59,
+	0x1F590300: 0x1F5B,
+	0x1F590301: 0x1F5D,
+	0x1F590342: 0x1F5F,
+	0x03C90313: 0x1F60,
+	0x03C90314: 0x1F61,
+	0x1F600300: 0x1F62,
+	0x1F610300: 0x1F63,
+	0x1F600301: 0x1F64,
+	0x1F610301: 0x1F65,
+	0x1F600342: 0x1F66,
+	0x1F610342: 0x1F67,
+	0x03A90313: 0x1F68,
+	0x03A90314: 0x1F69,
+	0x1F680300: 0x1F6A,
+	0x1F690300: 0x1F6B,
+	0x1F680301: 0x1F6C,
+	0x1F690301: 0x1F6D,
+	0x1F680342: 0x1F6E,
+	0x1F690342: 0x1F6F,
+	0x03B10300: 0x1F70,
+	0x03B50300: 0x1F72,
+	0x03B70300: 0x1F74,
+	0x03B90300: 0x1F76,
+	0x03BF0300: 0x1F78,
+	0x03C50300: 0x1F7A,
+	0x03C90300: 0x1F7C,
+	0x1F000345: 0x1F80,
+	0x1F010345: 0x1F81,
+	0x1F020345: 0x1F82,
+	0x1F030345: 0x1F83,
+	0x1F040345: 0x1F84,
+	0x1F050345: 0x1F85,
+	0x1F060345: 0x1F86,
+	0x1F070345: 0x1F87,
+	0x1F080345: 0x1F88,
+	0x1F090345: 0x1F89,
+	0x1F0A0345: 0x1F8A,
+	0x1F0B0345: 0x1F8B,
+	0x1F0C0345: 0x1F8C,
+	0x1F0D0345: 0x1F8D,
+	0x1F0E0345: 0x1F8E,
+	0x1F0F0345: 0x1F8F,
+	0x1F200345: 0x1F90,
+	0x1F210345: 0x1F91,
+	0x1F220345: 0x1F92,
+	0x1F230345: 0x1F93,
+	0x1F240345: 0x1F94,
+	0x1F250345: 0x1F95,
+	0x1F260345: 0x1F96,
+	0x1F270345: 0x1F97,
+	0x1F280345: 0x1F98,
+	0x1F290345: 0x1F99,
+	0x1F2A0345: 0x1F9A,
+	0x1F2B0345: 0x1F9B,
+	0x1F2C0345: 0x1F9C,
+	0x1F2D0345: 0x1F9D,
+	0x1F2E0345: 0x1F9E,
+	0x1F2F0345: 0x1F9F,
+	0x1F600345: 0x1FA0,
+	0x1F610345: 0x1FA1,
+	0x1F620345: 0x1FA2,
+	0x1F630345: 0x1FA3,
+	0x1F640345: 0x1FA4,
+	0x1F650345: 0x1FA5,
+	0x1F660345: 0x1FA6,
+	0x1F670345: 0x1FA7,
+	0x1F680345: 0x1FA8,
+	0x1F690345: 0x1FA9,
+	0x1F6A0345: 0x1FAA,
+	0x1F6B0345: 0x1FAB,
+	0x1F6C0345: 0x1FAC,
+	0x1F6D0345: 0x1FAD,
+	0x1F6E0345: 0x1FAE,
+	0x1F6F0345: 0x1FAF,
+	0x03B10306: 0x1FB0,
+	0x03B10304: 0x1FB1,
+	0x1F700345: 0x1FB2,
+	0x03B10345: 0x1FB3,
+	0x03AC0345: 0x1FB4,
+	0x03B10342: 0x1FB6,
+	0x1FB60345: 0x1FB7,
+	0x03910306: 0x1FB8,
+	0x03910304: 0x1FB9,
+	0x03910300: 0x1FBA,
+	0x03910345: 0x1FBC,
+	0x00A80342: 0x1FC1,
+	0x1F740345: 0x1FC2,
+	0x03B70345: 0x1FC3,
+	0x03AE0345: 0x1FC4,
+	0x03B70342: 0x1FC6,
+	0x1FC60345: 0x1FC7,
+	0x03950300: 0x1FC8,
+	0x03970300: 0x1FCA,
+	0x03970345: 0x1FCC,
+	0x1FBF0300: 0x1FCD,
+	0x1FBF0301: 0x1FCE,
+	0x1FBF0342: 0x1FCF,
+	0x03B90306: 0x1FD0,
+	0x03B90304: 0x1FD1,
+	0x03CA0300: 0x1FD2,
+	0x03B90342: 0x1FD6,
+	0x03CA0342: 0x1FD7,
+	0x03990306: 0x1FD8,
+	0x03990304: 0x1FD9,
+	0x03990300: 0x1FDA,
+	0x1FFE0300: 0x1FDD,
+	0x1FFE0301: 0x1FDE,
+	0x1FFE0342: 0x1FDF,
+	0x03C50306: 0x1FE0,
+	0x03C50304: 0x1FE1,
+	0x03CB0300: 0x1FE2,
+	0x03C10313: 0x1FE4,
+	0x03C10314: 0x1FE5,
+	0x03C50342: 0x1FE6,
+	0x03CB0342: 0x1FE7,
+	0x03A50306: 0x1FE8,
+	0x03A50304: 0x1FE9,
+	0x03A50300: 0x1FEA,
+	0x03A10314: 0x1FEC,
+	0x00A80300: 0x1FED,
+	0x1F7C0345: 0x1FF2,
+	0x03C90345: 0x1FF3,
+	0x03CE0345: 0x1FF4,
+	0x03C90342: 0x1FF6,
+	0x1FF60345: 0x1FF7,
+	0x039F0300: 0x1FF8,
+	0x03A90300: 0x1FFA,
+	0x03A90345: 0x1FFC,
+	0x21900338: 0x219A,
+	0x21920338: 0x219B,
+	0x21940338: 0x21AE,
+	0x21D00338: 0x21CD,
+	0x21D40338: 0x21CE,
+	0x21D20338: 0x21CF,
+	0x22030338: 0x2204,
+	0x22080338: 0x2209,
+	0x220B0338: 0x220C,
+	0x22230338: 0x2224,
+	0x22250338: 0x2226,
+	0x223C0338: 0x2241,
+	0x22430338: 0x2244,
+	0x22450338: 0x2247,
+	0x22480338: 0x2249,
+	0x003D0338: 0x2260,
+	0x22610338: 0x2262,
+	0x224D0338: 0x226D,
+	0x003C0338: 0x226E,
+	0x003E0338: 0x226F,
+	0x22640338: 0x2270,
+	0x22650338: 0x2271,
+	0x22720338: 0x2274,
+	0x22730338: 0x2275,
+	0x22760338: 0x2278,
+	0x22770338: 0x2279,
+	0x227A0338: 0x2280,
+	0x227B0338: 0x2281,
+	0x22820338: 0x2284,
+	0x22830338: 0x2285,
+	0x22860338: 0x2288,
+	0x22870338: 0x2289,
+	0x22A20338: 0x22AC,
+	0x22A80338: 0x22AD,
+	0x22A90338: 0x22AE,
+	0x22AB0338: 0x22AF,
+	0x227C0338: 0x22E0,
+	0x227D0338: 0x22E1,
+	0x22910338: 0x22E2,
+	0x22920338: 0x22E3,
+	0x22B20338: 0x22EA,
+	0x22B30338: 0x22EB,
+	0x22B40338: 0x22EC,
+	0x22B50338: 0x22ED,
+	0x304B3099: 0x304C,
+	0x304D3099: 0x304E,
+	0x304F3099: 0x3050,
+	0x30513099: 0x3052,
+	0x30533099: 0x3054,
+	0x30553099: 0x3056,
+	0x30573099: 0x3058,
+	0x30593099: 0x305A,
+	0x305B3099: 0x305C,
+	0x305D3099: 0x305E,
+	0x305F3099: 0x3060,
+	0x30613099: 0x3062,
+	0x30643099: 0x3065,
+	0x30663099: 0x3067,
+	0x30683099: 0x3069,
+	0x306F3099: 0x3070,
+	0x306F309A: 0x3071,
+	0x30723099: 0x3073,
+	0x3072309A: 0x3074,
+	0x30753099: 0x3076,
+	0x3075309A: 0x3077,
+	0x30783099: 0x3079,
+	0x3078309A: 0x307A,
+	0x307B3099: 0x307C,
+	0x307B309A: 0x307D,
+	0x30463099: 0x3094,
+	0x309D3099: 0x309E,
+	0x30AB3099: 0x30AC,
+	0x30AD3099: 0x30AE,
+	0x30AF3099: 0x30B0,
+	0x30B13099: 0x30B2,
+	0x30B33099: 0x30B4,
+	0x30B53099: 0x30B6,
+	0x30B73099: 0x30B8,
+	0x30B93099: 0x30BA,
+	0x30BB3099: 0x30BC,
+	0x30BD3099: 0x30BE,
+	0x30BF3099: 0x30C0,
+	0x30C13099: 0x30C2,
+	0x30C43099: 0x30C5,
+	0x30C63099: 0x30C7,
+	0x30C83099: 0x30C9,
+	0x30CF3099: 0x30D0,
+	0x30CF309A: 0x30D1,
+	0x30D23099: 0x30D3,
+	0x30D2309A: 0x30D4,
+	0x30D53099: 0x30D6,
+	0x30D5309A: 0x30D7,
+	0x30D83099: 0x30D9,
+	0x30D8309A: 0x30DA,
+	0x30DB3099: 0x30DC,
+	0x30DB309A: 0x30DD,
+	0x30A63099: 0x30F4,
+	0x30EF3099: 0x30F7,
+	0x30F03099: 0x30F8,
+	0x30F13099: 0x30F9,
+	0x30F23099: 0x30FA,
+	0x30FD3099: 0x30FE,
+	0x109910BA: 0x1109A,
+	0x109B10BA: 0x1109C,
+	0x10A510BA: 0x110AB,
+	0x11311127: 0x1112E,
+	0x11321127: 0x1112F,
+	0x1347133E: 0x1134B,
+	0x13471357: 0x1134C,
+	0x14B914BA: 0x114BB,
+	0x14B914B0: 0x114BC,
+	0x14B914BD: 0x114BE,
+	0x15B815AF: 0x115BA,
+	0x15B915AF: 0x115BB,
+}
+
+// Total size of tables: 53KB (53976 bytes)
diff --git a/src/vendor/golang_org/x/text/unicode/norm/transform.go b/src/vendor/golang_org/x/text/unicode/norm/transform.go
new file mode 100644
index 0000000..b341789
--- /dev/null
+++ b/src/vendor/golang_org/x/text/unicode/norm/transform.go
@@ -0,0 +1,88 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+import (
+	"unicode/utf8"
+
+	"golang_org/x/text/transform"
+)
+
+// Reset implements the Reset method of the transform.Transformer interface.
+func (Form) Reset() {}
+
+// Transform implements the Transform method of the transform.Transformer
+// interface. It may need to write segments of up to MaxSegmentSize at once.
+// Users should either catch ErrShortDst and allow dst to grow or have dst be at
+// least of size MaxTransformChunkSize to be guaranteed of progress.
+func (f Form) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+	n := 0
+	// Cap the maximum number of src bytes to check.
+	b := src
+	eof := atEOF
+	if ns := len(dst); ns < len(b) {
+		err = transform.ErrShortDst
+		eof = false
+		b = b[:ns]
+	}
+	i, ok := formTable[f].quickSpan(inputBytes(b), n, len(b), eof)
+	n += copy(dst[n:], b[n:i])
+	if !ok {
+		nDst, nSrc, err = f.transform(dst[n:], src[n:], atEOF)
+		return nDst + n, nSrc + n, err
+	}
+	if n < len(src) && !atEOF {
+		err = transform.ErrShortSrc
+	}
+	return n, n, err
+}
+
+func flushTransform(rb *reorderBuffer) bool {
+	// Write out (must fully fit in dst, or else it is a ErrShortDst).
+	if len(rb.out) < rb.nrune*utf8.UTFMax {
+		return false
+	}
+	rb.out = rb.out[rb.flushCopy(rb.out):]
+	return true
+}
+
+var errs = []error{nil, transform.ErrShortDst, transform.ErrShortSrc}
+
+// transform implements the transform.Transformer interface. It is only called
+// when quickSpan does not pass for a given string.
+func (f Form) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+	// TODO: get rid of reorderBuffer. See CL 23460044.
+	rb := reorderBuffer{}
+	rb.init(f, src)
+	for {
+		// Load segment into reorder buffer.
+		rb.setFlusher(dst[nDst:], flushTransform)
+		end := decomposeSegment(&rb, nSrc, atEOF)
+		if end < 0 {
+			return nDst, nSrc, errs[-end]
+		}
+		nDst = len(dst) - len(rb.out)
+		nSrc = end
+
+		// Next quickSpan.
+		end = rb.nsrc
+		eof := atEOF
+		if n := nSrc + len(dst) - nDst; n < end {
+			err = transform.ErrShortDst
+			end = n
+			eof = false
+		}
+		end, ok := rb.f.quickSpan(rb.src, nSrc, end, eof)
+		n := copy(dst[nDst:], rb.src.bytes[nSrc:end])
+		nSrc += n
+		nDst += n
+		if ok {
+			if n < rb.nsrc && !atEOF {
+				err = transform.ErrShortSrc
+			}
+			return nDst, nSrc, err
+		}
+	}
+}
diff --git a/src/vendor/golang_org/x/text/unicode/norm/trie.go b/src/vendor/golang_org/x/text/unicode/norm/trie.go
new file mode 100644
index 0000000..423386b
--- /dev/null
+++ b/src/vendor/golang_org/x/text/unicode/norm/trie.go
@@ -0,0 +1,54 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package norm
+
+type valueRange struct {
+	value  uint16 // header: value:stride
+	lo, hi byte   // header: lo:n
+}
+
+type sparseBlocks struct {
+	values []valueRange
+	offset []uint16
+}
+
+var nfcSparse = sparseBlocks{
+	values: nfcSparseValues[:],
+	offset: nfcSparseOffset[:],
+}
+
+var nfkcSparse = sparseBlocks{
+	values: nfkcSparseValues[:],
+	offset: nfkcSparseOffset[:],
+}
+
+var (
+	nfcData  = newNfcTrie(0)
+	nfkcData = newNfkcTrie(0)
+)
+
+// lookupValue determines the type of block n and looks up the value for b.
+// For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
+// is a list of ranges with an accompanying value. Given a matching range r,
+// the value for b is by r.value + (b - r.lo) * stride.
+func (t *sparseBlocks) lookup(n uint32, b byte) uint16 {
+	offset := t.offset[n]
+	header := t.values[offset]
+	lo := offset + 1
+	hi := lo + uint16(header.lo)
+	for lo < hi {
+		m := lo + (hi-lo)/2
+		r := t.values[m]
+		if r.lo <= b && b <= r.hi {
+			return r.value + uint16(b-r.lo)*header.value
+		}
+		if b < r.lo {
+			hi = m
+		} else {
+			lo = m + 1
+		}
+	}
+	return 0
+}
diff --git a/src/vendor/golang_org/x/text/unicode/norm/triegen.go b/src/vendor/golang_org/x/text/unicode/norm/triegen.go
new file mode 100644
index 0000000..45d7119
--- /dev/null
+++ b/src/vendor/golang_org/x/text/unicode/norm/triegen.go
@@ -0,0 +1,117 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Trie table generator.
+// Used by make*tables tools to generate a go file with trie data structures
+// for mapping UTF-8 to a 16-bit value. All but the last byte in a UTF-8 byte
+// sequence are used to lookup offsets in the index table to be used for the
+// next byte. The last byte is used to index into a table with 16-bit values.
+
+package main
+
+import (
+	"fmt"
+	"io"
+)
+
+const maxSparseEntries = 16
+
+type normCompacter struct {
+	sparseBlocks [][]uint64
+	sparseOffset []uint16
+	sparseCount  int
+	name         string
+}
+
+func mostFrequentStride(a []uint64) int {
+	counts := make(map[int]int)
+	var v int
+	for _, x := range a {
+		if stride := int(x) - v; v != 0 && stride >= 0 {
+			counts[stride]++
+		}
+		v = int(x)
+	}
+	var maxs, maxc int
+	for stride, cnt := range counts {
+		if cnt > maxc || (cnt == maxc && stride < maxs) {
+			maxs, maxc = stride, cnt
+		}
+	}
+	return maxs
+}
+
+func countSparseEntries(a []uint64) int {
+	stride := mostFrequentStride(a)
+	var v, count int
+	for _, tv := range a {
+		if int(tv)-v != stride {
+			if tv != 0 {
+				count++
+			}
+		}
+		v = int(tv)
+	}
+	return count
+}
+
+func (c *normCompacter) Size(v []uint64) (sz int, ok bool) {
+	if n := countSparseEntries(v); n <= maxSparseEntries {
+		return (n+1)*4 + 2, true
+	}
+	return 0, false
+}
+
+func (c *normCompacter) Store(v []uint64) uint32 {
+	h := uint32(len(c.sparseOffset))
+	c.sparseBlocks = append(c.sparseBlocks, v)
+	c.sparseOffset = append(c.sparseOffset, uint16(c.sparseCount))
+	c.sparseCount += countSparseEntries(v) + 1
+	return h
+}
+
+func (c *normCompacter) Handler() string {
+	return c.name + "Sparse.lookup"
+}
+
+func (c *normCompacter) Print(w io.Writer) (retErr error) {
+	p := func(f string, x ...interface{}) {
+		if _, err := fmt.Fprintf(w, f, x...); retErr == nil && err != nil {
+			retErr = err
+		}
+	}
+
+	ls := len(c.sparseBlocks)
+	p("// %sSparseOffset: %d entries, %d bytes\n", c.name, ls, ls*2)
+	p("var %sSparseOffset = %#v\n\n", c.name, c.sparseOffset)
+
+	ns := c.sparseCount
+	p("// %sSparseValues: %d entries, %d bytes\n", c.name, ns, ns*4)
+	p("var %sSparseValues = [%d]valueRange {", c.name, ns)
+	for i, b := range c.sparseBlocks {
+		p("\n// Block %#x, offset %#x", i, c.sparseOffset[i])
+		var v int
+		stride := mostFrequentStride(b)
+		n := countSparseEntries(b)
+		p("\n{value:%#04x,lo:%#02x},", stride, uint8(n))
+		for i, nv := range b {
+			if int(nv)-v != stride {
+				if v != 0 {
+					p(",hi:%#02x},", 0x80+i-1)
+				}
+				if nv != 0 {
+					p("\n{value:%#04x,lo:%#02x", nv, 0x80+i)
+				}
+			}
+			v = int(nv)
+		}
+		if v != 0 {
+			p(",hi:%#02x},", 0x80+len(b)-1)
+		}
+	}
+	p("\n}\n\n")
+	return
+}
diff --git a/src/vendor/golang_org/x/text/width/kind_string.go b/src/vendor/golang_org/x/text/width/kind_string.go
new file mode 100644
index 0000000..ab4fee5
--- /dev/null
+++ b/src/vendor/golang_org/x/text/width/kind_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=Kind"; DO NOT EDIT
+
+package width
+
+import "fmt"
+
+const _Kind_name = "NeutralEastAsianAmbiguousEastAsianWideEastAsianNarrowEastAsianFullwidthEastAsianHalfwidth"
+
+var _Kind_index = [...]uint8{0, 7, 25, 38, 53, 71, 89}
+
+func (i Kind) String() string {
+	if i < 0 || i >= Kind(len(_Kind_index)-1) {
+		return fmt.Sprintf("Kind(%d)", i)
+	}
+	return _Kind_name[_Kind_index[i]:_Kind_index[i+1]]
+}
diff --git a/src/vendor/golang_org/x/text/width/tables.go b/src/vendor/golang_org/x/text/width/tables.go
new file mode 100644
index 0000000..242da0f
--- /dev/null
+++ b/src/vendor/golang_org/x/text/width/tables.go
@@ -0,0 +1,1284 @@
+// This file was generated by go generate; DO NOT EDIT
+
+package width
+
+// UnicodeVersion is the Unicode version from which the tables in this package are derived.
+const UnicodeVersion = "9.0.0"
+
+// lookup returns the trie value for the first UTF-8 encoding in s and
+// the width in bytes of this encoding. The size will be 0 if s does not
+// hold enough bytes to complete the encoding. len(s) must be greater than 0.
+func (t *widthTrie) lookup(s []byte) (v uint16, sz int) {
+	c0 := s[0]
+	switch {
+	case c0 < 0x80: // is ASCII
+		return widthValues[c0], 1
+	case c0 < 0xC2:
+		return 0, 1 // Illegal UTF-8: not a starter, not ASCII.
+	case c0 < 0xE0: // 2-byte UTF-8
+		if len(s) < 2 {
+			return 0, 0
+		}
+		i := widthIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c1), 2
+	case c0 < 0xF0: // 3-byte UTF-8
+		if len(s) < 3 {
+			return 0, 0
+		}
+		i := widthIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = widthIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c2), 3
+	case c0 < 0xF8: // 4-byte UTF-8
+		if len(s) < 4 {
+			return 0, 0
+		}
+		i := widthIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = widthIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		o = uint32(i)<<6 + uint32(c2)
+		i = widthIndex[o]
+		c3 := s[3]
+		if c3 < 0x80 || 0xC0 <= c3 {
+			return 0, 3 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c3), 4
+	}
+	// Illegal rune
+	return 0, 1
+}
+
+// lookupUnsafe returns the trie value for the first UTF-8 encoding in s.
+// s must start with a full and valid UTF-8 encoded rune.
+func (t *widthTrie) lookupUnsafe(s []byte) uint16 {
+	c0 := s[0]
+	if c0 < 0x80 { // is ASCII
+		return widthValues[c0]
+	}
+	i := widthIndex[c0]
+	if c0 < 0xE0 { // 2-byte UTF-8
+		return t.lookupValue(uint32(i), s[1])
+	}
+	i = widthIndex[uint32(i)<<6+uint32(s[1])]
+	if c0 < 0xF0 { // 3-byte UTF-8
+		return t.lookupValue(uint32(i), s[2])
+	}
+	i = widthIndex[uint32(i)<<6+uint32(s[2])]
+	if c0 < 0xF8 { // 4-byte UTF-8
+		return t.lookupValue(uint32(i), s[3])
+	}
+	return 0
+}
+
+// lookupString returns the trie value for the first UTF-8 encoding in s and
+// the width in bytes of this encoding. The size will be 0 if s does not
+// hold enough bytes to complete the encoding. len(s) must be greater than 0.
+func (t *widthTrie) lookupString(s string) (v uint16, sz int) {
+	c0 := s[0]
+	switch {
+	case c0 < 0x80: // is ASCII
+		return widthValues[c0], 1
+	case c0 < 0xC2:
+		return 0, 1 // Illegal UTF-8: not a starter, not ASCII.
+	case c0 < 0xE0: // 2-byte UTF-8
+		if len(s) < 2 {
+			return 0, 0
+		}
+		i := widthIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c1), 2
+	case c0 < 0xF0: // 3-byte UTF-8
+		if len(s) < 3 {
+			return 0, 0
+		}
+		i := widthIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = widthIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c2), 3
+	case c0 < 0xF8: // 4-byte UTF-8
+		if len(s) < 4 {
+			return 0, 0
+		}
+		i := widthIndex[c0]
+		c1 := s[1]
+		if c1 < 0x80 || 0xC0 <= c1 {
+			return 0, 1 // Illegal UTF-8: not a continuation byte.
+		}
+		o := uint32(i)<<6 + uint32(c1)
+		i = widthIndex[o]
+		c2 := s[2]
+		if c2 < 0x80 || 0xC0 <= c2 {
+			return 0, 2 // Illegal UTF-8: not a continuation byte.
+		}
+		o = uint32(i)<<6 + uint32(c2)
+		i = widthIndex[o]
+		c3 := s[3]
+		if c3 < 0x80 || 0xC0 <= c3 {
+			return 0, 3 // Illegal UTF-8: not a continuation byte.
+		}
+		return t.lookupValue(uint32(i), c3), 4
+	}
+	// Illegal rune
+	return 0, 1
+}
+
+// lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s.
+// s must start with a full and valid UTF-8 encoded rune.
+func (t *widthTrie) lookupStringUnsafe(s string) uint16 {
+	c0 := s[0]
+	if c0 < 0x80 { // is ASCII
+		return widthValues[c0]
+	}
+	i := widthIndex[c0]
+	if c0 < 0xE0 { // 2-byte UTF-8
+		return t.lookupValue(uint32(i), s[1])
+	}
+	i = widthIndex[uint32(i)<<6+uint32(s[1])]
+	if c0 < 0xF0 { // 3-byte UTF-8
+		return t.lookupValue(uint32(i), s[2])
+	}
+	i = widthIndex[uint32(i)<<6+uint32(s[2])]
+	if c0 < 0xF8 { // 4-byte UTF-8
+		return t.lookupValue(uint32(i), s[3])
+	}
+	return 0
+}
+
+// widthTrie. Total size: 14080 bytes (13.75 KiB). Checksum: 3b8aeb3dc03667a3.
+type widthTrie struct{}
+
+func newWidthTrie(i int) *widthTrie {
+	return &widthTrie{}
+}
+
+// lookupValue determines the type of block n and looks up the value for b.
+func (t *widthTrie) lookupValue(n uint32, b byte) uint16 {
+	switch {
+	default:
+		return uint16(widthValues[n<<6+uint32(b)])
+	}
+}
+
+// widthValues: 99 blocks, 6336 entries, 12672 bytes
+// The third block is the zero block.
+var widthValues = [6336]uint16{
+	// Block 0x0, offset 0x0
+	0x20: 0x6001, 0x21: 0x6002, 0x22: 0x6002, 0x23: 0x6002,
+	0x24: 0x6002, 0x25: 0x6002, 0x26: 0x6002, 0x27: 0x6002, 0x28: 0x6002, 0x29: 0x6002,
+	0x2a: 0x6002, 0x2b: 0x6002, 0x2c: 0x6002, 0x2d: 0x6002, 0x2e: 0x6002, 0x2f: 0x6002,
+	0x30: 0x6002, 0x31: 0x6002, 0x32: 0x6002, 0x33: 0x6002, 0x34: 0x6002, 0x35: 0x6002,
+	0x36: 0x6002, 0x37: 0x6002, 0x38: 0x6002, 0x39: 0x6002, 0x3a: 0x6002, 0x3b: 0x6002,
+	0x3c: 0x6002, 0x3d: 0x6002, 0x3e: 0x6002, 0x3f: 0x6002,
+	// Block 0x1, offset 0x40
+	0x40: 0x6003, 0x41: 0x6003, 0x42: 0x6003, 0x43: 0x6003, 0x44: 0x6003, 0x45: 0x6003,
+	0x46: 0x6003, 0x47: 0x6003, 0x48: 0x6003, 0x49: 0x6003, 0x4a: 0x6003, 0x4b: 0x6003,
+	0x4c: 0x6003, 0x4d: 0x6003, 0x4e: 0x6003, 0x4f: 0x6003, 0x50: 0x6003, 0x51: 0x6003,
+	0x52: 0x6003, 0x53: 0x6003, 0x54: 0x6003, 0x55: 0x6003, 0x56: 0x6003, 0x57: 0x6003,
+	0x58: 0x6003, 0x59: 0x6003, 0x5a: 0x6003, 0x5b: 0x6003, 0x5c: 0x6003, 0x5d: 0x6003,
+	0x5e: 0x6003, 0x5f: 0x6003, 0x60: 0x6004, 0x61: 0x6004, 0x62: 0x6004, 0x63: 0x6004,
+	0x64: 0x6004, 0x65: 0x6004, 0x66: 0x6004, 0x67: 0x6004, 0x68: 0x6004, 0x69: 0x6004,
+	0x6a: 0x6004, 0x6b: 0x6004, 0x6c: 0x6004, 0x6d: 0x6004, 0x6e: 0x6004, 0x6f: 0x6004,
+	0x70: 0x6004, 0x71: 0x6004, 0x72: 0x6004, 0x73: 0x6004, 0x74: 0x6004, 0x75: 0x6004,
+	0x76: 0x6004, 0x77: 0x6004, 0x78: 0x6004, 0x79: 0x6004, 0x7a: 0x6004, 0x7b: 0x6004,
+	0x7c: 0x6004, 0x7d: 0x6004, 0x7e: 0x6004,
+	// Block 0x2, offset 0x80
+	// Block 0x3, offset 0xc0
+	0xe1: 0x2000, 0xe2: 0x6005, 0xe3: 0x6005,
+	0xe4: 0x2000, 0xe5: 0x6006, 0xe6: 0x6005, 0xe7: 0x2000, 0xe8: 0x2000,
+	0xea: 0x2000, 0xec: 0x6007, 0xed: 0x2000, 0xee: 0x2000, 0xef: 0x6008,
+	0xf0: 0x2000, 0xf1: 0x2000, 0xf2: 0x2000, 0xf3: 0x2000, 0xf4: 0x2000,
+	0xf6: 0x2000, 0xf7: 0x2000, 0xf8: 0x2000, 0xf9: 0x2000, 0xfa: 0x2000,
+	0xfc: 0x2000, 0xfd: 0x2000, 0xfe: 0x2000, 0xff: 0x2000,
+	// Block 0x4, offset 0x100
+	0x106: 0x2000,
+	0x110: 0x2000,
+	0x117: 0x2000,
+	0x118: 0x2000,
+	0x11e: 0x2000, 0x11f: 0x2000, 0x120: 0x2000, 0x121: 0x2000,
+	0x126: 0x2000, 0x128: 0x2000, 0x129: 0x2000,
+	0x12a: 0x2000, 0x12c: 0x2000, 0x12d: 0x2000,
+	0x130: 0x2000, 0x132: 0x2000, 0x133: 0x2000,
+	0x137: 0x2000, 0x138: 0x2000, 0x139: 0x2000, 0x13a: 0x2000,
+	0x13c: 0x2000, 0x13e: 0x2000,
+	// Block 0x5, offset 0x140
+	0x141: 0x2000,
+	0x151: 0x2000,
+	0x153: 0x2000,
+	0x15b: 0x2000,
+	0x166: 0x2000, 0x167: 0x2000,
+	0x16b: 0x2000,
+	0x171: 0x2000, 0x172: 0x2000, 0x173: 0x2000,
+	0x178: 0x2000,
+	0x17f: 0x2000,
+	// Block 0x6, offset 0x180
+	0x180: 0x2000, 0x181: 0x2000, 0x182: 0x2000, 0x184: 0x2000,
+	0x188: 0x2000, 0x189: 0x2000, 0x18a: 0x2000, 0x18b: 0x2000,
+	0x18d: 0x2000,
+	0x192: 0x2000, 0x193: 0x2000,
+	0x1a6: 0x2000, 0x1a7: 0x2000,
+	0x1ab: 0x2000,
+	// Block 0x7, offset 0x1c0
+	0x1ce: 0x2000, 0x1d0: 0x2000,
+	0x1d2: 0x2000, 0x1d4: 0x2000, 0x1d6: 0x2000,
+	0x1d8: 0x2000, 0x1da: 0x2000, 0x1dc: 0x2000,
+	// Block 0x8, offset 0x200
+	0x211: 0x2000,
+	0x221: 0x2000,
+	// Block 0x9, offset 0x240
+	0x244: 0x2000,
+	0x247: 0x2000, 0x249: 0x2000, 0x24a: 0x2000, 0x24b: 0x2000,
+	0x24d: 0x2000, 0x250: 0x2000,
+	0x258: 0x2000, 0x259: 0x2000, 0x25a: 0x2000, 0x25b: 0x2000, 0x25d: 0x2000,
+	0x25f: 0x2000,
+	// Block 0xa, offset 0x280
+	0x280: 0x2000, 0x281: 0x2000, 0x282: 0x2000, 0x283: 0x2000, 0x284: 0x2000, 0x285: 0x2000,
+	0x286: 0x2000, 0x287: 0x2000, 0x288: 0x2000, 0x289: 0x2000, 0x28a: 0x2000, 0x28b: 0x2000,
+	0x28c: 0x2000, 0x28d: 0x2000, 0x28e: 0x2000, 0x28f: 0x2000, 0x290: 0x2000, 0x291: 0x2000,
+	0x292: 0x2000, 0x293: 0x2000, 0x294: 0x2000, 0x295: 0x2000, 0x296: 0x2000, 0x297: 0x2000,
+	0x298: 0x2000, 0x299: 0x2000, 0x29a: 0x2000, 0x29b: 0x2000, 0x29c: 0x2000, 0x29d: 0x2000,
+	0x29e: 0x2000, 0x29f: 0x2000, 0x2a0: 0x2000, 0x2a1: 0x2000, 0x2a2: 0x2000, 0x2a3: 0x2000,
+	0x2a4: 0x2000, 0x2a5: 0x2000, 0x2a6: 0x2000, 0x2a7: 0x2000, 0x2a8: 0x2000, 0x2a9: 0x2000,
+	0x2aa: 0x2000, 0x2ab: 0x2000, 0x2ac: 0x2000, 0x2ad: 0x2000, 0x2ae: 0x2000, 0x2af: 0x2000,
+	0x2b0: 0x2000, 0x2b1: 0x2000, 0x2b2: 0x2000, 0x2b3: 0x2000, 0x2b4: 0x2000, 0x2b5: 0x2000,
+	0x2b6: 0x2000, 0x2b7: 0x2000, 0x2b8: 0x2000, 0x2b9: 0x2000, 0x2ba: 0x2000, 0x2bb: 0x2000,
+	0x2bc: 0x2000, 0x2bd: 0x2000, 0x2be: 0x2000, 0x2bf: 0x2000,
+	// Block 0xb, offset 0x2c0
+	0x2c0: 0x2000, 0x2c1: 0x2000, 0x2c2: 0x2000, 0x2c3: 0x2000, 0x2c4: 0x2000, 0x2c5: 0x2000,
+	0x2c6: 0x2000, 0x2c7: 0x2000, 0x2c8: 0x2000, 0x2c9: 0x2000, 0x2ca: 0x2000, 0x2cb: 0x2000,
+	0x2cc: 0x2000, 0x2cd: 0x2000, 0x2ce: 0x2000, 0x2cf: 0x2000, 0x2d0: 0x2000, 0x2d1: 0x2000,
+	0x2d2: 0x2000, 0x2d3: 0x2000, 0x2d4: 0x2000, 0x2d5: 0x2000, 0x2d6: 0x2000, 0x2d7: 0x2000,
+	0x2d8: 0x2000, 0x2d9: 0x2000, 0x2da: 0x2000, 0x2db: 0x2000, 0x2dc: 0x2000, 0x2dd: 0x2000,
+	0x2de: 0x2000, 0x2df: 0x2000, 0x2e0: 0x2000, 0x2e1: 0x2000, 0x2e2: 0x2000, 0x2e3: 0x2000,
+	0x2e4: 0x2000, 0x2e5: 0x2000, 0x2e6: 0x2000, 0x2e7: 0x2000, 0x2e8: 0x2000, 0x2e9: 0x2000,
+	0x2ea: 0x2000, 0x2eb: 0x2000, 0x2ec: 0x2000, 0x2ed: 0x2000, 0x2ee: 0x2000, 0x2ef: 0x2000,
+	// Block 0xc, offset 0x300
+	0x311: 0x2000,
+	0x312: 0x2000, 0x313: 0x2000, 0x314: 0x2000, 0x315: 0x2000, 0x316: 0x2000, 0x317: 0x2000,
+	0x318: 0x2000, 0x319: 0x2000, 0x31a: 0x2000, 0x31b: 0x2000, 0x31c: 0x2000, 0x31d: 0x2000,
+	0x31e: 0x2000, 0x31f: 0x2000, 0x320: 0x2000, 0x321: 0x2000, 0x323: 0x2000,
+	0x324: 0x2000, 0x325: 0x2000, 0x326: 0x2000, 0x327: 0x2000, 0x328: 0x2000, 0x329: 0x2000,
+	0x331: 0x2000, 0x332: 0x2000, 0x333: 0x2000, 0x334: 0x2000, 0x335: 0x2000,
+	0x336: 0x2000, 0x337: 0x2000, 0x338: 0x2000, 0x339: 0x2000, 0x33a: 0x2000, 0x33b: 0x2000,
+	0x33c: 0x2000, 0x33d: 0x2000, 0x33e: 0x2000, 0x33f: 0x2000,
+	// Block 0xd, offset 0x340
+	0x340: 0x2000, 0x341: 0x2000, 0x343: 0x2000, 0x344: 0x2000, 0x345: 0x2000,
+	0x346: 0x2000, 0x347: 0x2000, 0x348: 0x2000, 0x349: 0x2000,
+	// Block 0xe, offset 0x380
+	0x381: 0x2000,
+	0x390: 0x2000, 0x391: 0x2000,
+	0x392: 0x2000, 0x393: 0x2000, 0x394: 0x2000, 0x395: 0x2000, 0x396: 0x2000, 0x397: 0x2000,
+	0x398: 0x2000, 0x399: 0x2000, 0x39a: 0x2000, 0x39b: 0x2000, 0x39c: 0x2000, 0x39d: 0x2000,
+	0x39e: 0x2000, 0x39f: 0x2000, 0x3a0: 0x2000, 0x3a1: 0x2000, 0x3a2: 0x2000, 0x3a3: 0x2000,
+	0x3a4: 0x2000, 0x3a5: 0x2000, 0x3a6: 0x2000, 0x3a7: 0x2000, 0x3a8: 0x2000, 0x3a9: 0x2000,
+	0x3aa: 0x2000, 0x3ab: 0x2000, 0x3ac: 0x2000, 0x3ad: 0x2000, 0x3ae: 0x2000, 0x3af: 0x2000,
+	0x3b0: 0x2000, 0x3b1: 0x2000, 0x3b2: 0x2000, 0x3b3: 0x2000, 0x3b4: 0x2000, 0x3b5: 0x2000,
+	0x3b6: 0x2000, 0x3b7: 0x2000, 0x3b8: 0x2000, 0x3b9: 0x2000, 0x3ba: 0x2000, 0x3bb: 0x2000,
+	0x3bc: 0x2000, 0x3bd: 0x2000, 0x3be: 0x2000, 0x3bf: 0x2000,
+	// Block 0xf, offset 0x3c0
+	0x3c0: 0x2000, 0x3c1: 0x2000, 0x3c2: 0x2000, 0x3c3: 0x2000, 0x3c4: 0x2000, 0x3c5: 0x2000,
+	0x3c6: 0x2000, 0x3c7: 0x2000, 0x3c8: 0x2000, 0x3c9: 0x2000, 0x3ca: 0x2000, 0x3cb: 0x2000,
+	0x3cc: 0x2000, 0x3cd: 0x2000, 0x3ce: 0x2000, 0x3cf: 0x2000, 0x3d1: 0x2000,
+	// Block 0x10, offset 0x400
+	0x400: 0x4000, 0x401: 0x4000, 0x402: 0x4000, 0x403: 0x4000, 0x404: 0x4000, 0x405: 0x4000,
+	0x406: 0x4000, 0x407: 0x4000, 0x408: 0x4000, 0x409: 0x4000, 0x40a: 0x4000, 0x40b: 0x4000,
+	0x40c: 0x4000, 0x40d: 0x4000, 0x40e: 0x4000, 0x40f: 0x4000, 0x410: 0x4000, 0x411: 0x4000,
+	0x412: 0x4000, 0x413: 0x4000, 0x414: 0x4000, 0x415: 0x4000, 0x416: 0x4000, 0x417: 0x4000,
+	0x418: 0x4000, 0x419: 0x4000, 0x41a: 0x4000, 0x41b: 0x4000, 0x41c: 0x4000, 0x41d: 0x4000,
+	0x41e: 0x4000, 0x41f: 0x4000, 0x420: 0x4000, 0x421: 0x4000, 0x422: 0x4000, 0x423: 0x4000,
+	0x424: 0x4000, 0x425: 0x4000, 0x426: 0x4000, 0x427: 0x4000, 0x428: 0x4000, 0x429: 0x4000,
+	0x42a: 0x4000, 0x42b: 0x4000, 0x42c: 0x4000, 0x42d: 0x4000, 0x42e: 0x4000, 0x42f: 0x4000,
+	0x430: 0x4000, 0x431: 0x4000, 0x432: 0x4000, 0x433: 0x4000, 0x434: 0x4000, 0x435: 0x4000,
+	0x436: 0x4000, 0x437: 0x4000, 0x438: 0x4000, 0x439: 0x4000, 0x43a: 0x4000, 0x43b: 0x4000,
+	0x43c: 0x4000, 0x43d: 0x4000, 0x43e: 0x4000, 0x43f: 0x4000,
+	// Block 0x11, offset 0x440
+	0x440: 0x4000, 0x441: 0x4000, 0x442: 0x4000, 0x443: 0x4000, 0x444: 0x4000, 0x445: 0x4000,
+	0x446: 0x4000, 0x447: 0x4000, 0x448: 0x4000, 0x449: 0x4000, 0x44a: 0x4000, 0x44b: 0x4000,
+	0x44c: 0x4000, 0x44d: 0x4000, 0x44e: 0x4000, 0x44f: 0x4000, 0x450: 0x4000, 0x451: 0x4000,
+	0x452: 0x4000, 0x453: 0x4000, 0x454: 0x4000, 0x455: 0x4000, 0x456: 0x4000, 0x457: 0x4000,
+	0x458: 0x4000, 0x459: 0x4000, 0x45a: 0x4000, 0x45b: 0x4000, 0x45c: 0x4000, 0x45d: 0x4000,
+	0x45e: 0x4000, 0x45f: 0x4000,
+	// Block 0x12, offset 0x480
+	0x490: 0x2000,
+	0x493: 0x2000, 0x494: 0x2000, 0x495: 0x2000, 0x496: 0x2000,
+	0x498: 0x2000, 0x499: 0x2000, 0x49c: 0x2000, 0x49d: 0x2000,
+	0x4a0: 0x2000, 0x4a1: 0x2000, 0x4a2: 0x2000,
+	0x4a4: 0x2000, 0x4a5: 0x2000, 0x4a6: 0x2000, 0x4a7: 0x2000,
+	0x4b0: 0x2000, 0x4b2: 0x2000, 0x4b3: 0x2000, 0x4b5: 0x2000,
+	0x4bb: 0x2000,
+	0x4be: 0x2000,
+	// Block 0x13, offset 0x4c0
+	0x4f4: 0x2000,
+	0x4ff: 0x2000,
+	// Block 0x14, offset 0x500
+	0x501: 0x2000, 0x502: 0x2000, 0x503: 0x2000, 0x504: 0x2000,
+	0x529: 0xa009,
+	0x52c: 0x2000,
+	// Block 0x15, offset 0x540
+	0x543: 0x2000, 0x545: 0x2000,
+	0x549: 0x2000,
+	0x553: 0x2000, 0x556: 0x2000,
+	0x561: 0x2000, 0x562: 0x2000,
+	0x566: 0x2000,
+	0x56b: 0x2000,
+	// Block 0x16, offset 0x580
+	0x593: 0x2000, 0x594: 0x2000,
+	0x59b: 0x2000, 0x59c: 0x2000, 0x59d: 0x2000,
+	0x59e: 0x2000, 0x5a0: 0x2000, 0x5a1: 0x2000, 0x5a2: 0x2000, 0x5a3: 0x2000,
+	0x5a4: 0x2000, 0x5a5: 0x2000, 0x5a6: 0x2000, 0x5a7: 0x2000, 0x5a8: 0x2000, 0x5a9: 0x2000,
+	0x5aa: 0x2000, 0x5ab: 0x2000,
+	0x5b0: 0x2000, 0x5b1: 0x2000, 0x5b2: 0x2000, 0x5b3: 0x2000, 0x5b4: 0x2000, 0x5b5: 0x2000,
+	0x5b6: 0x2000, 0x5b7: 0x2000, 0x5b8: 0x2000, 0x5b9: 0x2000,
+	// Block 0x17, offset 0x5c0
+	0x5c9: 0x2000,
+	0x5d0: 0x200a, 0x5d1: 0x200b,
+	0x5d2: 0x200a, 0x5d3: 0x200c, 0x5d4: 0x2000, 0x5d5: 0x2000, 0x5d6: 0x2000, 0x5d7: 0x2000,
+	0x5d8: 0x2000, 0x5d9: 0x2000,
+	0x5f8: 0x2000, 0x5f9: 0x2000,
+	// Block 0x18, offset 0x600
+	0x612: 0x2000, 0x614: 0x2000,
+	0x627: 0x2000,
+	// Block 0x19, offset 0x640
+	0x640: 0x2000, 0x642: 0x2000, 0x643: 0x2000,
+	0x647: 0x2000, 0x648: 0x2000, 0x64b: 0x2000,
+	0x64f: 0x2000, 0x651: 0x2000,
+	0x655: 0x2000,
+	0x65a: 0x2000, 0x65d: 0x2000,
+	0x65e: 0x2000, 0x65f: 0x2000, 0x660: 0x2000, 0x663: 0x2000,
+	0x665: 0x2000, 0x667: 0x2000, 0x668: 0x2000, 0x669: 0x2000,
+	0x66a: 0x2000, 0x66b: 0x2000, 0x66c: 0x2000, 0x66e: 0x2000,
+	0x674: 0x2000, 0x675: 0x2000,
+	0x676: 0x2000, 0x677: 0x2000,
+	0x67c: 0x2000, 0x67d: 0x2000,
+	// Block 0x1a, offset 0x680
+	0x688: 0x2000,
+	0x68c: 0x2000,
+	0x692: 0x2000,
+	0x6a0: 0x2000, 0x6a1: 0x2000,
+	0x6a4: 0x2000, 0x6a5: 0x2000, 0x6a6: 0x2000, 0x6a7: 0x2000,
+	0x6aa: 0x2000, 0x6ab: 0x2000, 0x6ae: 0x2000, 0x6af: 0x2000,
+	// Block 0x1b, offset 0x6c0
+	0x6c2: 0x2000, 0x6c3: 0x2000,
+	0x6c6: 0x2000, 0x6c7: 0x2000,
+	0x6d5: 0x2000,
+	0x6d9: 0x2000,
+	0x6e5: 0x2000,
+	0x6ff: 0x2000,
+	// Block 0x1c, offset 0x700
+	0x712: 0x2000,
+	0x71a: 0x4000, 0x71b: 0x4000,
+	0x729: 0x4000,
+	0x72a: 0x4000,
+	// Block 0x1d, offset 0x740
+	0x769: 0x4000,
+	0x76a: 0x4000, 0x76b: 0x4000, 0x76c: 0x4000,
+	0x770: 0x4000, 0x773: 0x4000,
+	// Block 0x1e, offset 0x780
+	0x7a0: 0x2000, 0x7a1: 0x2000, 0x7a2: 0x2000, 0x7a3: 0x2000,
+	0x7a4: 0x2000, 0x7a5: 0x2000, 0x7a6: 0x2000, 0x7a7: 0x2000, 0x7a8: 0x2000, 0x7a9: 0x2000,
+	0x7aa: 0x2000, 0x7ab: 0x2000, 0x7ac: 0x2000, 0x7ad: 0x2000, 0x7ae: 0x2000, 0x7af: 0x2000,
+	0x7b0: 0x2000, 0x7b1: 0x2000, 0x7b2: 0x2000, 0x7b3: 0x2000, 0x7b4: 0x2000, 0x7b5: 0x2000,
+	0x7b6: 0x2000, 0x7b7: 0x2000, 0x7b8: 0x2000, 0x7b9: 0x2000, 0x7ba: 0x2000, 0x7bb: 0x2000,
+	0x7bc: 0x2000, 0x7bd: 0x2000, 0x7be: 0x2000, 0x7bf: 0x2000,
+	// Block 0x1f, offset 0x7c0
+	0x7c0: 0x2000, 0x7c1: 0x2000, 0x7c2: 0x2000, 0x7c3: 0x2000, 0x7c4: 0x2000, 0x7c5: 0x2000,
+	0x7c6: 0x2000, 0x7c7: 0x2000, 0x7c8: 0x2000, 0x7c9: 0x2000, 0x7ca: 0x2000, 0x7cb: 0x2000,
+	0x7cc: 0x2000, 0x7cd: 0x2000, 0x7ce: 0x2000, 0x7cf: 0x2000, 0x7d0: 0x2000, 0x7d1: 0x2000,
+	0x7d2: 0x2000, 0x7d3: 0x2000, 0x7d4: 0x2000, 0x7d5: 0x2000, 0x7d6: 0x2000, 0x7d7: 0x2000,
+	0x7d8: 0x2000, 0x7d9: 0x2000, 0x7da: 0x2000, 0x7db: 0x2000, 0x7dc: 0x2000, 0x7dd: 0x2000,
+	0x7de: 0x2000, 0x7df: 0x2000, 0x7e0: 0x2000, 0x7e1: 0x2000, 0x7e2: 0x2000, 0x7e3: 0x2000,
+	0x7e4: 0x2000, 0x7e5: 0x2000, 0x7e6: 0x2000, 0x7e7: 0x2000, 0x7e8: 0x2000, 0x7e9: 0x2000,
+	0x7eb: 0x2000, 0x7ec: 0x2000, 0x7ed: 0x2000, 0x7ee: 0x2000, 0x7ef: 0x2000,
+	0x7f0: 0x2000, 0x7f1: 0x2000, 0x7f2: 0x2000, 0x7f3: 0x2000, 0x7f4: 0x2000, 0x7f5: 0x2000,
+	0x7f6: 0x2000, 0x7f7: 0x2000, 0x7f8: 0x2000, 0x7f9: 0x2000, 0x7fa: 0x2000, 0x7fb: 0x2000,
+	0x7fc: 0x2000, 0x7fd: 0x2000, 0x7fe: 0x2000, 0x7ff: 0x2000,
+	// Block 0x20, offset 0x800
+	0x800: 0x2000, 0x801: 0x2000, 0x802: 0x200d, 0x803: 0x2000, 0x804: 0x2000, 0x805: 0x2000,
+	0x806: 0x2000, 0x807: 0x2000, 0x808: 0x2000, 0x809: 0x2000, 0x80a: 0x2000, 0x80b: 0x2000,
+	0x80c: 0x2000, 0x80d: 0x2000, 0x80e: 0x2000, 0x80f: 0x2000, 0x810: 0x2000, 0x811: 0x2000,
+	0x812: 0x2000, 0x813: 0x2000, 0x814: 0x2000, 0x815: 0x2000, 0x816: 0x2000, 0x817: 0x2000,
+	0x818: 0x2000, 0x819: 0x2000, 0x81a: 0x2000, 0x81b: 0x2000, 0x81c: 0x2000, 0x81d: 0x2000,
+	0x81e: 0x2000, 0x81f: 0x2000, 0x820: 0x2000, 0x821: 0x2000, 0x822: 0x2000, 0x823: 0x2000,
+	0x824: 0x2000, 0x825: 0x2000, 0x826: 0x2000, 0x827: 0x2000, 0x828: 0x2000, 0x829: 0x2000,
+	0x82a: 0x2000, 0x82b: 0x2000, 0x82c: 0x2000, 0x82d: 0x2000, 0x82e: 0x2000, 0x82f: 0x2000,
+	0x830: 0x2000, 0x831: 0x2000, 0x832: 0x2000, 0x833: 0x2000, 0x834: 0x2000, 0x835: 0x2000,
+	0x836: 0x2000, 0x837: 0x2000, 0x838: 0x2000, 0x839: 0x2000, 0x83a: 0x2000, 0x83b: 0x2000,
+	0x83c: 0x2000, 0x83d: 0x2000, 0x83e: 0x2000, 0x83f: 0x2000,
+	// Block 0x21, offset 0x840
+	0x840: 0x2000, 0x841: 0x2000, 0x842: 0x2000, 0x843: 0x2000, 0x844: 0x2000, 0x845: 0x2000,
+	0x846: 0x2000, 0x847: 0x2000, 0x848: 0x2000, 0x849: 0x2000, 0x84a: 0x2000, 0x84b: 0x2000,
+	0x850: 0x2000, 0x851: 0x2000,
+	0x852: 0x2000, 0x853: 0x2000, 0x854: 0x2000, 0x855: 0x2000, 0x856: 0x2000, 0x857: 0x2000,
+	0x858: 0x2000, 0x859: 0x2000, 0x85a: 0x2000, 0x85b: 0x2000, 0x85c: 0x2000, 0x85d: 0x2000,
+	0x85e: 0x2000, 0x85f: 0x2000, 0x860: 0x2000, 0x861: 0x2000, 0x862: 0x2000, 0x863: 0x2000,
+	0x864: 0x2000, 0x865: 0x2000, 0x866: 0x2000, 0x867: 0x2000, 0x868: 0x2000, 0x869: 0x2000,
+	0x86a: 0x2000, 0x86b: 0x2000, 0x86c: 0x2000, 0x86d: 0x2000, 0x86e: 0x2000, 0x86f: 0x2000,
+	0x870: 0x2000, 0x871: 0x2000, 0x872: 0x2000, 0x873: 0x2000,
+	// Block 0x22, offset 0x880
+	0x880: 0x2000, 0x881: 0x2000, 0x882: 0x2000, 0x883: 0x2000, 0x884: 0x2000, 0x885: 0x2000,
+	0x886: 0x2000, 0x887: 0x2000, 0x888: 0x2000, 0x889: 0x2000, 0x88a: 0x2000, 0x88b: 0x2000,
+	0x88c: 0x2000, 0x88d: 0x2000, 0x88e: 0x2000, 0x88f: 0x2000,
+	0x892: 0x2000, 0x893: 0x2000, 0x894: 0x2000, 0x895: 0x2000,
+	0x8a0: 0x200e, 0x8a1: 0x2000, 0x8a3: 0x2000,
+	0x8a4: 0x2000, 0x8a5: 0x2000, 0x8a6: 0x2000, 0x8a7: 0x2000, 0x8a8: 0x2000, 0x8a9: 0x2000,
+	0x8b2: 0x2000, 0x8b3: 0x2000,
+	0x8b6: 0x2000, 0x8b7: 0x2000,
+	0x8bc: 0x2000, 0x8bd: 0x2000,
+	// Block 0x23, offset 0x8c0
+	0x8c0: 0x2000, 0x8c1: 0x2000,
+	0x8c6: 0x2000, 0x8c7: 0x2000, 0x8c8: 0x2000, 0x8cb: 0x200f,
+	0x8ce: 0x2000, 0x8cf: 0x2000, 0x8d0: 0x2000, 0x8d1: 0x2000,
+	0x8e2: 0x2000, 0x8e3: 0x2000,
+	0x8e4: 0x2000, 0x8e5: 0x2000,
+	0x8ef: 0x2000,
+	0x8fd: 0x4000, 0x8fe: 0x4000,
+	// Block 0x24, offset 0x900
+	0x905: 0x2000,
+	0x906: 0x2000, 0x909: 0x2000,
+	0x90e: 0x2000, 0x90f: 0x2000,
+	0x914: 0x4000, 0x915: 0x4000,
+	0x91c: 0x2000,
+	0x91e: 0x2000,
+	// Block 0x25, offset 0x940
+	0x940: 0x2000, 0x942: 0x2000,
+	0x948: 0x4000, 0x949: 0x4000, 0x94a: 0x4000, 0x94b: 0x4000,
+	0x94c: 0x4000, 0x94d: 0x4000, 0x94e: 0x4000, 0x94f: 0x4000, 0x950: 0x4000, 0x951: 0x4000,
+	0x952: 0x4000, 0x953: 0x4000,
+	0x960: 0x2000, 0x961: 0x2000, 0x963: 0x2000,
+	0x964: 0x2000, 0x965: 0x2000, 0x967: 0x2000, 0x968: 0x2000, 0x969: 0x2000,
+	0x96a: 0x2000, 0x96c: 0x2000, 0x96d: 0x2000, 0x96f: 0x2000,
+	0x97f: 0x4000,
+	// Block 0x26, offset 0x980
+	0x993: 0x4000,
+	0x99e: 0x2000, 0x99f: 0x2000, 0x9a1: 0x4000,
+	0x9aa: 0x4000, 0x9ab: 0x4000,
+	0x9bd: 0x4000, 0x9be: 0x4000, 0x9bf: 0x2000,
+	// Block 0x27, offset 0x9c0
+	0x9c4: 0x4000, 0x9c5: 0x4000,
+	0x9c6: 0x2000, 0x9c7: 0x2000, 0x9c8: 0x2000, 0x9c9: 0x2000, 0x9ca: 0x2000, 0x9cb: 0x2000,
+	0x9cc: 0x2000, 0x9cd: 0x2000, 0x9ce: 0x4000, 0x9cf: 0x2000, 0x9d0: 0x2000, 0x9d1: 0x2000,
+	0x9d2: 0x2000, 0x9d3: 0x2000, 0x9d4: 0x4000, 0x9d5: 0x2000, 0x9d6: 0x2000, 0x9d7: 0x2000,
+	0x9d8: 0x2000, 0x9d9: 0x2000, 0x9da: 0x2000, 0x9db: 0x2000, 0x9dc: 0x2000, 0x9dd: 0x2000,
+	0x9de: 0x2000, 0x9df: 0x2000, 0x9e0: 0x2000, 0x9e1: 0x2000, 0x9e3: 0x2000,
+	0x9e8: 0x2000, 0x9e9: 0x2000,
+	0x9ea: 0x4000, 0x9eb: 0x2000, 0x9ec: 0x2000, 0x9ed: 0x2000, 0x9ee: 0x2000, 0x9ef: 0x2000,
+	0x9f0: 0x2000, 0x9f1: 0x2000, 0x9f2: 0x4000, 0x9f3: 0x4000, 0x9f4: 0x2000, 0x9f5: 0x4000,
+	0x9f6: 0x2000, 0x9f7: 0x2000, 0x9f8: 0x2000, 0x9f9: 0x2000, 0x9fa: 0x4000, 0x9fb: 0x2000,
+	0x9fc: 0x2000, 0x9fd: 0x4000, 0x9fe: 0x2000, 0x9ff: 0x2000,
+	// Block 0x28, offset 0xa00
+	0xa05: 0x4000,
+	0xa0a: 0x4000, 0xa0b: 0x4000,
+	0xa28: 0x4000,
+	0xa3d: 0x2000,
+	// Block 0x29, offset 0xa40
+	0xa4c: 0x4000, 0xa4e: 0x4000,
+	0xa53: 0x4000, 0xa54: 0x4000, 0xa55: 0x4000, 0xa57: 0x4000,
+	0xa76: 0x2000, 0xa77: 0x2000, 0xa78: 0x2000, 0xa79: 0x2000, 0xa7a: 0x2000, 0xa7b: 0x2000,
+	0xa7c: 0x2000, 0xa7d: 0x2000, 0xa7e: 0x2000, 0xa7f: 0x2000,
+	// Block 0x2a, offset 0xa80
+	0xa95: 0x4000, 0xa96: 0x4000, 0xa97: 0x4000,
+	0xab0: 0x4000,
+	0xabf: 0x4000,
+	// Block 0x2b, offset 0xac0
+	0xae6: 0x6000, 0xae7: 0x6000, 0xae8: 0x6000, 0xae9: 0x6000,
+	0xaea: 0x6000, 0xaeb: 0x6000, 0xaec: 0x6000, 0xaed: 0x6000,
+	// Block 0x2c, offset 0xb00
+	0xb05: 0x6010,
+	0xb06: 0x6011,
+	// Block 0x2d, offset 0xb40
+	0xb5b: 0x4000, 0xb5c: 0x4000,
+	// Block 0x2e, offset 0xb80
+	0xb90: 0x4000,
+	0xb95: 0x4000, 0xb96: 0x2000, 0xb97: 0x2000,
+	0xb98: 0x2000, 0xb99: 0x2000,
+	// Block 0x2f, offset 0xbc0
+	0xbc0: 0x4000, 0xbc1: 0x4000, 0xbc2: 0x4000, 0xbc3: 0x4000, 0xbc4: 0x4000, 0xbc5: 0x4000,
+	0xbc6: 0x4000, 0xbc7: 0x4000, 0xbc8: 0x4000, 0xbc9: 0x4000, 0xbca: 0x4000, 0xbcb: 0x4000,
+	0xbcc: 0x4000, 0xbcd: 0x4000, 0xbce: 0x4000, 0xbcf: 0x4000, 0xbd0: 0x4000, 0xbd1: 0x4000,
+	0xbd2: 0x4000, 0xbd3: 0x4000, 0xbd4: 0x4000, 0xbd5: 0x4000, 0xbd6: 0x4000, 0xbd7: 0x4000,
+	0xbd8: 0x4000, 0xbd9: 0x4000, 0xbdb: 0x4000, 0xbdc: 0x4000, 0xbdd: 0x4000,
+	0xbde: 0x4000, 0xbdf: 0x4000, 0xbe0: 0x4000, 0xbe1: 0x4000, 0xbe2: 0x4000, 0xbe3: 0x4000,
+	0xbe4: 0x4000, 0xbe5: 0x4000, 0xbe6: 0x4000, 0xbe7: 0x4000, 0xbe8: 0x4000, 0xbe9: 0x4000,
+	0xbea: 0x4000, 0xbeb: 0x4000, 0xbec: 0x4000, 0xbed: 0x4000, 0xbee: 0x4000, 0xbef: 0x4000,
+	0xbf0: 0x4000, 0xbf1: 0x4000, 0xbf2: 0x4000, 0xbf3: 0x4000, 0xbf4: 0x4000, 0xbf5: 0x4000,
+	0xbf6: 0x4000, 0xbf7: 0x4000, 0xbf8: 0x4000, 0xbf9: 0x4000, 0xbfa: 0x4000, 0xbfb: 0x4000,
+	0xbfc: 0x4000, 0xbfd: 0x4000, 0xbfe: 0x4000, 0xbff: 0x4000,
+	// Block 0x30, offset 0xc00
+	0xc00: 0x4000, 0xc01: 0x4000, 0xc02: 0x4000, 0xc03: 0x4000, 0xc04: 0x4000, 0xc05: 0x4000,
+	0xc06: 0x4000, 0xc07: 0x4000, 0xc08: 0x4000, 0xc09: 0x4000, 0xc0a: 0x4000, 0xc0b: 0x4000,
+	0xc0c: 0x4000, 0xc0d: 0x4000, 0xc0e: 0x4000, 0xc0f: 0x4000, 0xc10: 0x4000, 0xc11: 0x4000,
+	0xc12: 0x4000, 0xc13: 0x4000, 0xc14: 0x4000, 0xc15: 0x4000, 0xc16: 0x4000, 0xc17: 0x4000,
+	0xc18: 0x4000, 0xc19: 0x4000, 0xc1a: 0x4000, 0xc1b: 0x4000, 0xc1c: 0x4000, 0xc1d: 0x4000,
+	0xc1e: 0x4000, 0xc1f: 0x4000, 0xc20: 0x4000, 0xc21: 0x4000, 0xc22: 0x4000, 0xc23: 0x4000,
+	0xc24: 0x4000, 0xc25: 0x4000, 0xc26: 0x4000, 0xc27: 0x4000, 0xc28: 0x4000, 0xc29: 0x4000,
+	0xc2a: 0x4000, 0xc2b: 0x4000, 0xc2c: 0x4000, 0xc2d: 0x4000, 0xc2e: 0x4000, 0xc2f: 0x4000,
+	0xc30: 0x4000, 0xc31: 0x4000, 0xc32: 0x4000, 0xc33: 0x4000,
+	// Block 0x31, offset 0xc40
+	0xc40: 0x4000, 0xc41: 0x4000, 0xc42: 0x4000, 0xc43: 0x4000, 0xc44: 0x4000, 0xc45: 0x4000,
+	0xc46: 0x4000, 0xc47: 0x4000, 0xc48: 0x4000, 0xc49: 0x4000, 0xc4a: 0x4000, 0xc4b: 0x4000,
+	0xc4c: 0x4000, 0xc4d: 0x4000, 0xc4e: 0x4000, 0xc4f: 0x4000, 0xc50: 0x4000, 0xc51: 0x4000,
+	0xc52: 0x4000, 0xc53: 0x4000, 0xc54: 0x4000, 0xc55: 0x4000,
+	0xc70: 0x4000, 0xc71: 0x4000, 0xc72: 0x4000, 0xc73: 0x4000, 0xc74: 0x4000, 0xc75: 0x4000,
+	0xc76: 0x4000, 0xc77: 0x4000, 0xc78: 0x4000, 0xc79: 0x4000, 0xc7a: 0x4000, 0xc7b: 0x4000,
+	// Block 0x32, offset 0xc80
+	0xc80: 0x9012, 0xc81: 0x4013, 0xc82: 0x4014, 0xc83: 0x4000, 0xc84: 0x4000, 0xc85: 0x4000,
+	0xc86: 0x4000, 0xc87: 0x4000, 0xc88: 0x4000, 0xc89: 0x4000, 0xc8a: 0x4000, 0xc8b: 0x4000,
+	0xc8c: 0x4015, 0xc8d: 0x4015, 0xc8e: 0x4000, 0xc8f: 0x4000, 0xc90: 0x4000, 0xc91: 0x4000,
+	0xc92: 0x4000, 0xc93: 0x4000, 0xc94: 0x4000, 0xc95: 0x4000, 0xc96: 0x4000, 0xc97: 0x4000,
+	0xc98: 0x4000, 0xc99: 0x4000, 0xc9a: 0x4000, 0xc9b: 0x4000, 0xc9c: 0x4000, 0xc9d: 0x4000,
+	0xc9e: 0x4000, 0xc9f: 0x4000, 0xca0: 0x4000, 0xca1: 0x4000, 0xca2: 0x4000, 0xca3: 0x4000,
+	0xca4: 0x4000, 0xca5: 0x4000, 0xca6: 0x4000, 0xca7: 0x4000, 0xca8: 0x4000, 0xca9: 0x4000,
+	0xcaa: 0x4000, 0xcab: 0x4000, 0xcac: 0x4000, 0xcad: 0x4000, 0xcae: 0x4000, 0xcaf: 0x4000,
+	0xcb0: 0x4000, 0xcb1: 0x4000, 0xcb2: 0x4000, 0xcb3: 0x4000, 0xcb4: 0x4000, 0xcb5: 0x4000,
+	0xcb6: 0x4000, 0xcb7: 0x4000, 0xcb8: 0x4000, 0xcb9: 0x4000, 0xcba: 0x4000, 0xcbb: 0x4000,
+	0xcbc: 0x4000, 0xcbd: 0x4000, 0xcbe: 0x4000,
+	// Block 0x33, offset 0xcc0
+	0xcc1: 0x4000, 0xcc2: 0x4000, 0xcc3: 0x4000, 0xcc4: 0x4000, 0xcc5: 0x4000,
+	0xcc6: 0x4000, 0xcc7: 0x4000, 0xcc8: 0x4000, 0xcc9: 0x4000, 0xcca: 0x4000, 0xccb: 0x4000,
+	0xccc: 0x4000, 0xccd: 0x4000, 0xcce: 0x4000, 0xccf: 0x4000, 0xcd0: 0x4000, 0xcd1: 0x4000,
+	0xcd2: 0x4000, 0xcd3: 0x4000, 0xcd4: 0x4000, 0xcd5: 0x4000, 0xcd6: 0x4000, 0xcd7: 0x4000,
+	0xcd8: 0x4000, 0xcd9: 0x4000, 0xcda: 0x4000, 0xcdb: 0x4000, 0xcdc: 0x4000, 0xcdd: 0x4000,
+	0xcde: 0x4000, 0xcdf: 0x4000, 0xce0: 0x4000, 0xce1: 0x4000, 0xce2: 0x4000, 0xce3: 0x4000,
+	0xce4: 0x4000, 0xce5: 0x4000, 0xce6: 0x4000, 0xce7: 0x4000, 0xce8: 0x4000, 0xce9: 0x4000,
+	0xcea: 0x4000, 0xceb: 0x4000, 0xcec: 0x4000, 0xced: 0x4000, 0xcee: 0x4000, 0xcef: 0x4000,
+	0xcf0: 0x4000, 0xcf1: 0x4000, 0xcf2: 0x4000, 0xcf3: 0x4000, 0xcf4: 0x4000, 0xcf5: 0x4000,
+	0xcf6: 0x4000, 0xcf7: 0x4000, 0xcf8: 0x4000, 0xcf9: 0x4000, 0xcfa: 0x4000, 0xcfb: 0x4000,
+	0xcfc: 0x4000, 0xcfd: 0x4000, 0xcfe: 0x4000, 0xcff: 0x4000,
+	// Block 0x34, offset 0xd00
+	0xd00: 0x4000, 0xd01: 0x4000, 0xd02: 0x4000, 0xd03: 0x4000, 0xd04: 0x4000, 0xd05: 0x4000,
+	0xd06: 0x4000, 0xd07: 0x4000, 0xd08: 0x4000, 0xd09: 0x4000, 0xd0a: 0x4000, 0xd0b: 0x4000,
+	0xd0c: 0x4000, 0xd0d: 0x4000, 0xd0e: 0x4000, 0xd0f: 0x4000, 0xd10: 0x4000, 0xd11: 0x4000,
+	0xd12: 0x4000, 0xd13: 0x4000, 0xd14: 0x4000, 0xd15: 0x4000, 0xd16: 0x4000,
+	0xd19: 0x4016, 0xd1a: 0x4017, 0xd1b: 0x4000, 0xd1c: 0x4000, 0xd1d: 0x4000,
+	0xd1e: 0x4000, 0xd1f: 0x4000, 0xd20: 0x4000, 0xd21: 0x4018, 0xd22: 0x4019, 0xd23: 0x401a,
+	0xd24: 0x401b, 0xd25: 0x401c, 0xd26: 0x401d, 0xd27: 0x401e, 0xd28: 0x401f, 0xd29: 0x4020,
+	0xd2a: 0x4021, 0xd2b: 0x4022, 0xd2c: 0x4000, 0xd2d: 0x4010, 0xd2e: 0x4000, 0xd2f: 0x4023,
+	0xd30: 0x4000, 0xd31: 0x4024, 0xd32: 0x4000, 0xd33: 0x4025, 0xd34: 0x4000, 0xd35: 0x4026,
+	0xd36: 0x4000, 0xd37: 0x401a, 0xd38: 0x4000, 0xd39: 0x4027, 0xd3a: 0x4000, 0xd3b: 0x4028,
+	0xd3c: 0x4000, 0xd3d: 0x4020, 0xd3e: 0x4000, 0xd3f: 0x4029,
+	// Block 0x35, offset 0xd40
+	0xd40: 0x4000, 0xd41: 0x402a, 0xd42: 0x4000, 0xd43: 0x402b, 0xd44: 0x402c, 0xd45: 0x4000,
+	0xd46: 0x4017, 0xd47: 0x4000, 0xd48: 0x402d, 0xd49: 0x4000, 0xd4a: 0x402e, 0xd4b: 0x402f,
+	0xd4c: 0x4030, 0xd4d: 0x4017, 0xd4e: 0x4016, 0xd4f: 0x4017, 0xd50: 0x4000, 0xd51: 0x4000,
+	0xd52: 0x4031, 0xd53: 0x4000, 0xd54: 0x4000, 0xd55: 0x4031, 0xd56: 0x4000, 0xd57: 0x4000,
+	0xd58: 0x4032, 0xd59: 0x4000, 0xd5a: 0x4000, 0xd5b: 0x4032, 0xd5c: 0x4000, 0xd5d: 0x4000,
+	0xd5e: 0x4033, 0xd5f: 0x402e, 0xd60: 0x4034, 0xd61: 0x4035, 0xd62: 0x4034, 0xd63: 0x4036,
+	0xd64: 0x4037, 0xd65: 0x4024, 0xd66: 0x4035, 0xd67: 0x4025, 0xd68: 0x4038, 0xd69: 0x4038,
+	0xd6a: 0x4039, 0xd6b: 0x4039, 0xd6c: 0x403a, 0xd6d: 0x403a, 0xd6e: 0x4000, 0xd6f: 0x4035,
+	0xd70: 0x4000, 0xd71: 0x4000, 0xd72: 0x403b, 0xd73: 0x403c, 0xd74: 0x4000, 0xd75: 0x4000,
+	0xd76: 0x4000, 0xd77: 0x4000, 0xd78: 0x4000, 0xd79: 0x4000, 0xd7a: 0x4000, 0xd7b: 0x403d,
+	0xd7c: 0x401c, 0xd7d: 0x4000, 0xd7e: 0x4000, 0xd7f: 0x4000,
+	// Block 0x36, offset 0xd80
+	0xd85: 0x4000,
+	0xd86: 0x4000, 0xd87: 0x4000, 0xd88: 0x4000, 0xd89: 0x4000, 0xd8a: 0x4000, 0xd8b: 0x4000,
+	0xd8c: 0x4000, 0xd8d: 0x4000, 0xd8e: 0x4000, 0xd8f: 0x4000, 0xd90: 0x4000, 0xd91: 0x4000,
+	0xd92: 0x4000, 0xd93: 0x4000, 0xd94: 0x4000, 0xd95: 0x4000, 0xd96: 0x4000, 0xd97: 0x4000,
+	0xd98: 0x4000, 0xd99: 0x4000, 0xd9a: 0x4000, 0xd9b: 0x4000, 0xd9c: 0x4000, 0xd9d: 0x4000,
+	0xd9e: 0x4000, 0xd9f: 0x4000, 0xda0: 0x4000, 0xda1: 0x4000, 0xda2: 0x4000, 0xda3: 0x4000,
+	0xda4: 0x4000, 0xda5: 0x4000, 0xda6: 0x4000, 0xda7: 0x4000, 0xda8: 0x4000, 0xda9: 0x4000,
+	0xdaa: 0x4000, 0xdab: 0x4000, 0xdac: 0x4000, 0xdad: 0x4000,
+	0xdb1: 0x403e, 0xdb2: 0x403e, 0xdb3: 0x403e, 0xdb4: 0x403e, 0xdb5: 0x403e,
+	0xdb6: 0x403e, 0xdb7: 0x403e, 0xdb8: 0x403e, 0xdb9: 0x403e, 0xdba: 0x403e, 0xdbb: 0x403e,
+	0xdbc: 0x403e, 0xdbd: 0x403e, 0xdbe: 0x403e, 0xdbf: 0x403e,
+	// Block 0x37, offset 0xdc0
+	0xdc0: 0x4037, 0xdc1: 0x4037, 0xdc2: 0x4037, 0xdc3: 0x4037, 0xdc4: 0x4037, 0xdc5: 0x4037,
+	0xdc6: 0x4037, 0xdc7: 0x4037, 0xdc8: 0x4037, 0xdc9: 0x4037, 0xdca: 0x4037, 0xdcb: 0x4037,
+	0xdcc: 0x4037, 0xdcd: 0x4037, 0xdce: 0x4037, 0xdcf: 0x400e, 0xdd0: 0x403f, 0xdd1: 0x4040,
+	0xdd2: 0x4041, 0xdd3: 0x4040, 0xdd4: 0x403f, 0xdd5: 0x4042, 0xdd6: 0x4043, 0xdd7: 0x4044,
+	0xdd8: 0x4040, 0xdd9: 0x4041, 0xdda: 0x4040, 0xddb: 0x4045, 0xddc: 0x4009, 0xddd: 0x4045,
+	0xdde: 0x4046, 0xddf: 0x4045, 0xde0: 0x4047, 0xde1: 0x400b, 0xde2: 0x400a, 0xde3: 0x400c,
+	0xde4: 0x4048, 0xde5: 0x4000, 0xde6: 0x4000, 0xde7: 0x4000, 0xde8: 0x4000, 0xde9: 0x4000,
+	0xdea: 0x4000, 0xdeb: 0x4000, 0xdec: 0x4000, 0xded: 0x4000, 0xdee: 0x4000, 0xdef: 0x4000,
+	0xdf0: 0x4000, 0xdf1: 0x4000, 0xdf2: 0x4000, 0xdf3: 0x4000, 0xdf4: 0x4000, 0xdf5: 0x4000,
+	0xdf6: 0x4000, 0xdf7: 0x4000, 0xdf8: 0x4000, 0xdf9: 0x4000, 0xdfa: 0x4000, 0xdfb: 0x4000,
+	0xdfc: 0x4000, 0xdfd: 0x4000, 0xdfe: 0x4000, 0xdff: 0x4000,
+	// Block 0x38, offset 0xe00
+	0xe00: 0x4000, 0xe01: 0x4000, 0xe02: 0x4000, 0xe03: 0x4000, 0xe04: 0x4000, 0xe05: 0x4000,
+	0xe06: 0x4000, 0xe07: 0x4000, 0xe08: 0x4000, 0xe09: 0x4000, 0xe0a: 0x4000, 0xe0b: 0x4000,
+	0xe0c: 0x4000, 0xe0d: 0x4000, 0xe0e: 0x4000, 0xe10: 0x4000, 0xe11: 0x4000,
+	0xe12: 0x4000, 0xe13: 0x4000, 0xe14: 0x4000, 0xe15: 0x4000, 0xe16: 0x4000, 0xe17: 0x4000,
+	0xe18: 0x4000, 0xe19: 0x4000, 0xe1a: 0x4000, 0xe1b: 0x4000, 0xe1c: 0x4000, 0xe1d: 0x4000,
+	0xe1e: 0x4000, 0xe1f: 0x4000, 0xe20: 0x4000, 0xe21: 0x4000, 0xe22: 0x4000, 0xe23: 0x4000,
+	0xe24: 0x4000, 0xe25: 0x4000, 0xe26: 0x4000, 0xe27: 0x4000, 0xe28: 0x4000, 0xe29: 0x4000,
+	0xe2a: 0x4000, 0xe2b: 0x4000, 0xe2c: 0x4000, 0xe2d: 0x4000, 0xe2e: 0x4000, 0xe2f: 0x4000,
+	0xe30: 0x4000, 0xe31: 0x4000, 0xe32: 0x4000, 0xe33: 0x4000, 0xe34: 0x4000, 0xe35: 0x4000,
+	0xe36: 0x4000, 0xe37: 0x4000, 0xe38: 0x4000, 0xe39: 0x4000, 0xe3a: 0x4000,
+	// Block 0x39, offset 0xe40
+	0xe40: 0x4000, 0xe41: 0x4000, 0xe42: 0x4000, 0xe43: 0x4000, 0xe44: 0x4000, 0xe45: 0x4000,
+	0xe46: 0x4000, 0xe47: 0x4000, 0xe48: 0x4000, 0xe49: 0x4000, 0xe4a: 0x4000, 0xe4b: 0x4000,
+	0xe4c: 0x4000, 0xe4d: 0x4000, 0xe4e: 0x4000, 0xe4f: 0x4000, 0xe50: 0x4000, 0xe51: 0x4000,
+	0xe52: 0x4000, 0xe53: 0x4000, 0xe54: 0x4000, 0xe55: 0x4000, 0xe56: 0x4000, 0xe57: 0x4000,
+	0xe58: 0x4000, 0xe59: 0x4000, 0xe5a: 0x4000, 0xe5b: 0x4000, 0xe5c: 0x4000, 0xe5d: 0x4000,
+	0xe5e: 0x4000, 0xe5f: 0x4000, 0xe60: 0x4000, 0xe61: 0x4000, 0xe62: 0x4000, 0xe63: 0x4000,
+	0xe70: 0x4000, 0xe71: 0x4000, 0xe72: 0x4000, 0xe73: 0x4000, 0xe74: 0x4000, 0xe75: 0x4000,
+	0xe76: 0x4000, 0xe77: 0x4000, 0xe78: 0x4000, 0xe79: 0x4000, 0xe7a: 0x4000, 0xe7b: 0x4000,
+	0xe7c: 0x4000, 0xe7d: 0x4000, 0xe7e: 0x4000, 0xe7f: 0x4000,
+	// Block 0x3a, offset 0xe80
+	0xe80: 0x4000, 0xe81: 0x4000, 0xe82: 0x4000, 0xe83: 0x4000, 0xe84: 0x4000, 0xe85: 0x4000,
+	0xe86: 0x4000, 0xe87: 0x4000, 0xe88: 0x4000, 0xe89: 0x4000, 0xe8a: 0x4000, 0xe8b: 0x4000,
+	0xe8c: 0x4000, 0xe8d: 0x4000, 0xe8e: 0x4000, 0xe8f: 0x4000, 0xe90: 0x4000, 0xe91: 0x4000,
+	0xe92: 0x4000, 0xe93: 0x4000, 0xe94: 0x4000, 0xe95: 0x4000, 0xe96: 0x4000, 0xe97: 0x4000,
+	0xe98: 0x4000, 0xe99: 0x4000, 0xe9a: 0x4000, 0xe9b: 0x4000, 0xe9c: 0x4000, 0xe9d: 0x4000,
+	0xe9e: 0x4000, 0xea0: 0x4000, 0xea1: 0x4000, 0xea2: 0x4000, 0xea3: 0x4000,
+	0xea4: 0x4000, 0xea5: 0x4000, 0xea6: 0x4000, 0xea7: 0x4000, 0xea8: 0x4000, 0xea9: 0x4000,
+	0xeaa: 0x4000, 0xeab: 0x4000, 0xeac: 0x4000, 0xead: 0x4000, 0xeae: 0x4000, 0xeaf: 0x4000,
+	0xeb0: 0x4000, 0xeb1: 0x4000, 0xeb2: 0x4000, 0xeb3: 0x4000, 0xeb4: 0x4000, 0xeb5: 0x4000,
+	0xeb6: 0x4000, 0xeb7: 0x4000, 0xeb8: 0x4000, 0xeb9: 0x4000, 0xeba: 0x4000, 0xebb: 0x4000,
+	0xebc: 0x4000, 0xebd: 0x4000, 0xebe: 0x4000, 0xebf: 0x4000,
+	// Block 0x3b, offset 0xec0
+	0xec0: 0x4000, 0xec1: 0x4000, 0xec2: 0x4000, 0xec3: 0x4000, 0xec4: 0x4000, 0xec5: 0x4000,
+	0xec6: 0x4000, 0xec7: 0x4000, 0xec8: 0x2000, 0xec9: 0x2000, 0xeca: 0x2000, 0xecb: 0x2000,
+	0xecc: 0x2000, 0xecd: 0x2000, 0xece: 0x2000, 0xecf: 0x2000, 0xed0: 0x4000, 0xed1: 0x4000,
+	0xed2: 0x4000, 0xed3: 0x4000, 0xed4: 0x4000, 0xed5: 0x4000, 0xed6: 0x4000, 0xed7: 0x4000,
+	0xed8: 0x4000, 0xed9: 0x4000, 0xeda: 0x4000, 0xedb: 0x4000, 0xedc: 0x4000, 0xedd: 0x4000,
+	0xede: 0x4000, 0xedf: 0x4000, 0xee0: 0x4000, 0xee1: 0x4000, 0xee2: 0x4000, 0xee3: 0x4000,
+	0xee4: 0x4000, 0xee5: 0x4000, 0xee6: 0x4000, 0xee7: 0x4000, 0xee8: 0x4000, 0xee9: 0x4000,
+	0xeea: 0x4000, 0xeeb: 0x4000, 0xeec: 0x4000, 0xeed: 0x4000, 0xeee: 0x4000, 0xeef: 0x4000,
+	0xef0: 0x4000, 0xef1: 0x4000, 0xef2: 0x4000, 0xef3: 0x4000, 0xef4: 0x4000, 0xef5: 0x4000,
+	0xef6: 0x4000, 0xef7: 0x4000, 0xef8: 0x4000, 0xef9: 0x4000, 0xefa: 0x4000, 0xefb: 0x4000,
+	0xefc: 0x4000, 0xefd: 0x4000, 0xefe: 0x4000, 0xeff: 0x4000,
+	// Block 0x3c, offset 0xf00
+	0xf00: 0x4000, 0xf01: 0x4000, 0xf02: 0x4000, 0xf03: 0x4000, 0xf04: 0x4000, 0xf05: 0x4000,
+	0xf06: 0x4000, 0xf07: 0x4000, 0xf08: 0x4000, 0xf09: 0x4000, 0xf0a: 0x4000, 0xf0b: 0x4000,
+	0xf0c: 0x4000, 0xf0d: 0x4000, 0xf0e: 0x4000, 0xf0f: 0x4000, 0xf10: 0x4000, 0xf11: 0x4000,
+	0xf12: 0x4000, 0xf13: 0x4000, 0xf14: 0x4000, 0xf15: 0x4000, 0xf16: 0x4000, 0xf17: 0x4000,
+	0xf18: 0x4000, 0xf19: 0x4000, 0xf1a: 0x4000, 0xf1b: 0x4000, 0xf1c: 0x4000, 0xf1d: 0x4000,
+	0xf1e: 0x4000, 0xf1f: 0x4000, 0xf20: 0x4000, 0xf21: 0x4000, 0xf22: 0x4000, 0xf23: 0x4000,
+	0xf24: 0x4000, 0xf25: 0x4000, 0xf26: 0x4000, 0xf27: 0x4000, 0xf28: 0x4000, 0xf29: 0x4000,
+	0xf2a: 0x4000, 0xf2b: 0x4000, 0xf2c: 0x4000, 0xf2d: 0x4000, 0xf2e: 0x4000, 0xf2f: 0x4000,
+	0xf30: 0x4000, 0xf31: 0x4000, 0xf32: 0x4000, 0xf33: 0x4000, 0xf34: 0x4000, 0xf35: 0x4000,
+	0xf36: 0x4000, 0xf37: 0x4000, 0xf38: 0x4000, 0xf39: 0x4000, 0xf3a: 0x4000, 0xf3b: 0x4000,
+	0xf3c: 0x4000, 0xf3d: 0x4000, 0xf3e: 0x4000,
+	// Block 0x3d, offset 0xf40
+	0xf40: 0x4000, 0xf41: 0x4000, 0xf42: 0x4000, 0xf43: 0x4000, 0xf44: 0x4000, 0xf45: 0x4000,
+	0xf46: 0x4000, 0xf47: 0x4000, 0xf48: 0x4000, 0xf49: 0x4000, 0xf4a: 0x4000, 0xf4b: 0x4000,
+	0xf4c: 0x4000, 0xf50: 0x4000, 0xf51: 0x4000,
+	0xf52: 0x4000, 0xf53: 0x4000, 0xf54: 0x4000, 0xf55: 0x4000, 0xf56: 0x4000, 0xf57: 0x4000,
+	0xf58: 0x4000, 0xf59: 0x4000, 0xf5a: 0x4000, 0xf5b: 0x4000, 0xf5c: 0x4000, 0xf5d: 0x4000,
+	0xf5e: 0x4000, 0xf5f: 0x4000, 0xf60: 0x4000, 0xf61: 0x4000, 0xf62: 0x4000, 0xf63: 0x4000,
+	0xf64: 0x4000, 0xf65: 0x4000, 0xf66: 0x4000, 0xf67: 0x4000, 0xf68: 0x4000, 0xf69: 0x4000,
+	0xf6a: 0x4000, 0xf6b: 0x4000, 0xf6c: 0x4000, 0xf6d: 0x4000, 0xf6e: 0x4000, 0xf6f: 0x4000,
+	0xf70: 0x4000, 0xf71: 0x4000, 0xf72: 0x4000, 0xf73: 0x4000, 0xf74: 0x4000, 0xf75: 0x4000,
+	0xf76: 0x4000, 0xf77: 0x4000, 0xf78: 0x4000, 0xf79: 0x4000, 0xf7a: 0x4000, 0xf7b: 0x4000,
+	0xf7c: 0x4000, 0xf7d: 0x4000, 0xf7e: 0x4000, 0xf7f: 0x4000,
+	// Block 0x3e, offset 0xf80
+	0xf80: 0x4000, 0xf81: 0x4000, 0xf82: 0x4000, 0xf83: 0x4000, 0xf84: 0x4000, 0xf85: 0x4000,
+	0xf86: 0x4000,
+	// Block 0x3f, offset 0xfc0
+	0xfe0: 0x4000, 0xfe1: 0x4000, 0xfe2: 0x4000, 0xfe3: 0x4000,
+	0xfe4: 0x4000, 0xfe5: 0x4000, 0xfe6: 0x4000, 0xfe7: 0x4000, 0xfe8: 0x4000, 0xfe9: 0x4000,
+	0xfea: 0x4000, 0xfeb: 0x4000, 0xfec: 0x4000, 0xfed: 0x4000, 0xfee: 0x4000, 0xfef: 0x4000,
+	0xff0: 0x4000, 0xff1: 0x4000, 0xff2: 0x4000, 0xff3: 0x4000, 0xff4: 0x4000, 0xff5: 0x4000,
+	0xff6: 0x4000, 0xff7: 0x4000, 0xff8: 0x4000, 0xff9: 0x4000, 0xffa: 0x4000, 0xffb: 0x4000,
+	0xffc: 0x4000,
+	// Block 0x40, offset 0x1000
+	0x1000: 0x4000, 0x1001: 0x4000, 0x1002: 0x4000, 0x1003: 0x4000, 0x1004: 0x4000, 0x1005: 0x4000,
+	0x1006: 0x4000, 0x1007: 0x4000, 0x1008: 0x4000, 0x1009: 0x4000, 0x100a: 0x4000, 0x100b: 0x4000,
+	0x100c: 0x4000, 0x100d: 0x4000, 0x100e: 0x4000, 0x100f: 0x4000, 0x1010: 0x4000, 0x1011: 0x4000,
+	0x1012: 0x4000, 0x1013: 0x4000, 0x1014: 0x4000, 0x1015: 0x4000, 0x1016: 0x4000, 0x1017: 0x4000,
+	0x1018: 0x4000, 0x1019: 0x4000, 0x101a: 0x4000, 0x101b: 0x4000, 0x101c: 0x4000, 0x101d: 0x4000,
+	0x101e: 0x4000, 0x101f: 0x4000, 0x1020: 0x4000, 0x1021: 0x4000, 0x1022: 0x4000, 0x1023: 0x4000,
+	// Block 0x41, offset 0x1040
+	0x1040: 0x2000, 0x1041: 0x2000, 0x1042: 0x2000, 0x1043: 0x2000, 0x1044: 0x2000, 0x1045: 0x2000,
+	0x1046: 0x2000, 0x1047: 0x2000, 0x1048: 0x2000, 0x1049: 0x2000, 0x104a: 0x2000, 0x104b: 0x2000,
+	0x104c: 0x2000, 0x104d: 0x2000, 0x104e: 0x2000, 0x104f: 0x2000, 0x1050: 0x4000, 0x1051: 0x4000,
+	0x1052: 0x4000, 0x1053: 0x4000, 0x1054: 0x4000, 0x1055: 0x4000, 0x1056: 0x4000, 0x1057: 0x4000,
+	0x1058: 0x4000, 0x1059: 0x4000,
+	0x1070: 0x4000, 0x1071: 0x4000, 0x1072: 0x4000, 0x1073: 0x4000, 0x1074: 0x4000, 0x1075: 0x4000,
+	0x1076: 0x4000, 0x1077: 0x4000, 0x1078: 0x4000, 0x1079: 0x4000, 0x107a: 0x4000, 0x107b: 0x4000,
+	0x107c: 0x4000, 0x107d: 0x4000, 0x107e: 0x4000, 0x107f: 0x4000,
+	// Block 0x42, offset 0x1080
+	0x1080: 0x4000, 0x1081: 0x4000, 0x1082: 0x4000, 0x1083: 0x4000, 0x1084: 0x4000, 0x1085: 0x4000,
+	0x1086: 0x4000, 0x1087: 0x4000, 0x1088: 0x4000, 0x1089: 0x4000, 0x108a: 0x4000, 0x108b: 0x4000,
+	0x108c: 0x4000, 0x108d: 0x4000, 0x108e: 0x4000, 0x108f: 0x4000, 0x1090: 0x4000, 0x1091: 0x4000,
+	0x1092: 0x4000, 0x1094: 0x4000, 0x1095: 0x4000, 0x1096: 0x4000, 0x1097: 0x4000,
+	0x1098: 0x4000, 0x1099: 0x4000, 0x109a: 0x4000, 0x109b: 0x4000, 0x109c: 0x4000, 0x109d: 0x4000,
+	0x109e: 0x4000, 0x109f: 0x4000, 0x10a0: 0x4000, 0x10a1: 0x4000, 0x10a2: 0x4000, 0x10a3: 0x4000,
+	0x10a4: 0x4000, 0x10a5: 0x4000, 0x10a6: 0x4000, 0x10a8: 0x4000, 0x10a9: 0x4000,
+	0x10aa: 0x4000, 0x10ab: 0x4000,
+	// Block 0x43, offset 0x10c0
+	0x10c1: 0x9012, 0x10c2: 0x9012, 0x10c3: 0x9012, 0x10c4: 0x9012, 0x10c5: 0x9012,
+	0x10c6: 0x9012, 0x10c7: 0x9012, 0x10c8: 0x9012, 0x10c9: 0x9012, 0x10ca: 0x9012, 0x10cb: 0x9012,
+	0x10cc: 0x9012, 0x10cd: 0x9012, 0x10ce: 0x9012, 0x10cf: 0x9012, 0x10d0: 0x9012, 0x10d1: 0x9012,
+	0x10d2: 0x9012, 0x10d3: 0x9012, 0x10d4: 0x9012, 0x10d5: 0x9012, 0x10d6: 0x9012, 0x10d7: 0x9012,
+	0x10d8: 0x9012, 0x10d9: 0x9012, 0x10da: 0x9012, 0x10db: 0x9012, 0x10dc: 0x9012, 0x10dd: 0x9012,
+	0x10de: 0x9012, 0x10df: 0x9012, 0x10e0: 0x9049, 0x10e1: 0x9049, 0x10e2: 0x9049, 0x10e3: 0x9049,
+	0x10e4: 0x9049, 0x10e5: 0x9049, 0x10e6: 0x9049, 0x10e7: 0x9049, 0x10e8: 0x9049, 0x10e9: 0x9049,
+	0x10ea: 0x9049, 0x10eb: 0x9049, 0x10ec: 0x9049, 0x10ed: 0x9049, 0x10ee: 0x9049, 0x10ef: 0x9049,
+	0x10f0: 0x9049, 0x10f1: 0x9049, 0x10f2: 0x9049, 0x10f3: 0x9049, 0x10f4: 0x9049, 0x10f5: 0x9049,
+	0x10f6: 0x9049, 0x10f7: 0x9049, 0x10f8: 0x9049, 0x10f9: 0x9049, 0x10fa: 0x9049, 0x10fb: 0x9049,
+	0x10fc: 0x9049, 0x10fd: 0x9049, 0x10fe: 0x9049, 0x10ff: 0x9049,
+	// Block 0x44, offset 0x1100
+	0x1100: 0x9049, 0x1101: 0x9049, 0x1102: 0x9049, 0x1103: 0x9049, 0x1104: 0x9049, 0x1105: 0x9049,
+	0x1106: 0x9049, 0x1107: 0x9049, 0x1108: 0x9049, 0x1109: 0x9049, 0x110a: 0x9049, 0x110b: 0x9049,
+	0x110c: 0x9049, 0x110d: 0x9049, 0x110e: 0x9049, 0x110f: 0x9049, 0x1110: 0x9049, 0x1111: 0x9049,
+	0x1112: 0x9049, 0x1113: 0x9049, 0x1114: 0x9049, 0x1115: 0x9049, 0x1116: 0x9049, 0x1117: 0x9049,
+	0x1118: 0x9049, 0x1119: 0x9049, 0x111a: 0x9049, 0x111b: 0x9049, 0x111c: 0x9049, 0x111d: 0x9049,
+	0x111e: 0x9049, 0x111f: 0x904a, 0x1120: 0x904b, 0x1121: 0xb04c, 0x1122: 0xb04d, 0x1123: 0xb04d,
+	0x1124: 0xb04e, 0x1125: 0xb04f, 0x1126: 0xb050, 0x1127: 0xb051, 0x1128: 0xb052, 0x1129: 0xb053,
+	0x112a: 0xb054, 0x112b: 0xb055, 0x112c: 0xb056, 0x112d: 0xb057, 0x112e: 0xb058, 0x112f: 0xb059,
+	0x1130: 0xb05a, 0x1131: 0xb05b, 0x1132: 0xb05c, 0x1133: 0xb05d, 0x1134: 0xb05e, 0x1135: 0xb05f,
+	0x1136: 0xb060, 0x1137: 0xb061, 0x1138: 0xb062, 0x1139: 0xb063, 0x113a: 0xb064, 0x113b: 0xb065,
+	0x113c: 0xb052, 0x113d: 0xb066, 0x113e: 0xb067, 0x113f: 0xb055,
+	// Block 0x45, offset 0x1140
+	0x1140: 0xb068, 0x1141: 0xb069, 0x1142: 0xb06a, 0x1143: 0xb06b, 0x1144: 0xb05a, 0x1145: 0xb056,
+	0x1146: 0xb06c, 0x1147: 0xb06d, 0x1148: 0xb06b, 0x1149: 0xb06e, 0x114a: 0xb06b, 0x114b: 0xb06f,
+	0x114c: 0xb06f, 0x114d: 0xb070, 0x114e: 0xb070, 0x114f: 0xb071, 0x1150: 0xb056, 0x1151: 0xb072,
+	0x1152: 0xb073, 0x1153: 0xb072, 0x1154: 0xb074, 0x1155: 0xb073, 0x1156: 0xb075, 0x1157: 0xb075,
+	0x1158: 0xb076, 0x1159: 0xb076, 0x115a: 0xb077, 0x115b: 0xb077, 0x115c: 0xb073, 0x115d: 0xb078,
+	0x115e: 0xb079, 0x115f: 0xb067, 0x1160: 0xb07a, 0x1161: 0xb07b, 0x1162: 0xb07b, 0x1163: 0xb07b,
+	0x1164: 0xb07b, 0x1165: 0xb07b, 0x1166: 0xb07b, 0x1167: 0xb07b, 0x1168: 0xb07b, 0x1169: 0xb07b,
+	0x116a: 0xb07b, 0x116b: 0xb07b, 0x116c: 0xb07b, 0x116d: 0xb07b, 0x116e: 0xb07b, 0x116f: 0xb07b,
+	0x1170: 0xb07c, 0x1171: 0xb07c, 0x1172: 0xb07c, 0x1173: 0xb07c, 0x1174: 0xb07c, 0x1175: 0xb07c,
+	0x1176: 0xb07c, 0x1177: 0xb07c, 0x1178: 0xb07c, 0x1179: 0xb07c, 0x117a: 0xb07c, 0x117b: 0xb07c,
+	0x117c: 0xb07c, 0x117d: 0xb07c, 0x117e: 0xb07c,
+	// Block 0x46, offset 0x1180
+	0x1182: 0xb07d, 0x1183: 0xb07e, 0x1184: 0xb07f, 0x1185: 0xb080,
+	0x1186: 0xb07f, 0x1187: 0xb07e, 0x118a: 0xb081, 0x118b: 0xb082,
+	0x118c: 0xb083, 0x118d: 0xb07f, 0x118e: 0xb080, 0x118f: 0xb07f,
+	0x1192: 0xb084, 0x1193: 0xb085, 0x1194: 0xb084, 0x1195: 0xb086, 0x1196: 0xb084, 0x1197: 0xb087,
+	0x119a: 0xb088, 0x119b: 0xb089, 0x119c: 0xb08a,
+	0x11a0: 0x908b, 0x11a1: 0x908b, 0x11a2: 0x908c, 0x11a3: 0x908d,
+	0x11a4: 0x908b, 0x11a5: 0x908e, 0x11a6: 0x908f, 0x11a8: 0xb090, 0x11a9: 0xb091,
+	0x11aa: 0xb092, 0x11ab: 0xb091, 0x11ac: 0xb093, 0x11ad: 0xb094, 0x11ae: 0xb095,
+	0x11bd: 0x2000,
+	// Block 0x47, offset 0x11c0
+	0x11e0: 0x4000,
+	// Block 0x48, offset 0x1200
+	0x1200: 0x4000, 0x1201: 0x4000, 0x1202: 0x4000, 0x1203: 0x4000, 0x1204: 0x4000, 0x1205: 0x4000,
+	0x1206: 0x4000, 0x1207: 0x4000, 0x1208: 0x4000, 0x1209: 0x4000, 0x120a: 0x4000, 0x120b: 0x4000,
+	0x120c: 0x4000, 0x120d: 0x4000, 0x120e: 0x4000, 0x120f: 0x4000, 0x1210: 0x4000, 0x1211: 0x4000,
+	0x1212: 0x4000, 0x1213: 0x4000, 0x1214: 0x4000, 0x1215: 0x4000, 0x1216: 0x4000, 0x1217: 0x4000,
+	0x1218: 0x4000, 0x1219: 0x4000, 0x121a: 0x4000, 0x121b: 0x4000, 0x121c: 0x4000, 0x121d: 0x4000,
+	0x121e: 0x4000, 0x121f: 0x4000, 0x1220: 0x4000, 0x1221: 0x4000, 0x1222: 0x4000, 0x1223: 0x4000,
+	0x1224: 0x4000, 0x1225: 0x4000, 0x1226: 0x4000, 0x1227: 0x4000, 0x1228: 0x4000, 0x1229: 0x4000,
+	0x122a: 0x4000, 0x122b: 0x4000, 0x122c: 0x4000,
+	// Block 0x49, offset 0x1240
+	0x1240: 0x4000, 0x1241: 0x4000, 0x1242: 0x4000, 0x1243: 0x4000, 0x1244: 0x4000, 0x1245: 0x4000,
+	0x1246: 0x4000, 0x1247: 0x4000, 0x1248: 0x4000, 0x1249: 0x4000, 0x124a: 0x4000, 0x124b: 0x4000,
+	0x124c: 0x4000, 0x124d: 0x4000, 0x124e: 0x4000, 0x124f: 0x4000, 0x1250: 0x4000, 0x1251: 0x4000,
+	0x1252: 0x4000, 0x1253: 0x4000, 0x1254: 0x4000, 0x1255: 0x4000, 0x1256: 0x4000, 0x1257: 0x4000,
+	0x1258: 0x4000, 0x1259: 0x4000, 0x125a: 0x4000, 0x125b: 0x4000, 0x125c: 0x4000, 0x125d: 0x4000,
+	0x125e: 0x4000, 0x125f: 0x4000, 0x1260: 0x4000, 0x1261: 0x4000, 0x1262: 0x4000, 0x1263: 0x4000,
+	0x1264: 0x4000, 0x1265: 0x4000, 0x1266: 0x4000, 0x1267: 0x4000, 0x1268: 0x4000, 0x1269: 0x4000,
+	0x126a: 0x4000, 0x126b: 0x4000, 0x126c: 0x4000, 0x126d: 0x4000, 0x126e: 0x4000, 0x126f: 0x4000,
+	0x1270: 0x4000, 0x1271: 0x4000, 0x1272: 0x4000,
+	// Block 0x4a, offset 0x1280
+	0x1280: 0x4000, 0x1281: 0x4000,
+	// Block 0x4b, offset 0x12c0
+	0x12c4: 0x4000,
+	// Block 0x4c, offset 0x1300
+	0x130f: 0x4000,
+	// Block 0x4d, offset 0x1340
+	0x1340: 0x2000, 0x1341: 0x2000, 0x1342: 0x2000, 0x1343: 0x2000, 0x1344: 0x2000, 0x1345: 0x2000,
+	0x1346: 0x2000, 0x1347: 0x2000, 0x1348: 0x2000, 0x1349: 0x2000, 0x134a: 0x2000,
+	0x1350: 0x2000, 0x1351: 0x2000,
+	0x1352: 0x2000, 0x1353: 0x2000, 0x1354: 0x2000, 0x1355: 0x2000, 0x1356: 0x2000, 0x1357: 0x2000,
+	0x1358: 0x2000, 0x1359: 0x2000, 0x135a: 0x2000, 0x135b: 0x2000, 0x135c: 0x2000, 0x135d: 0x2000,
+	0x135e: 0x2000, 0x135f: 0x2000, 0x1360: 0x2000, 0x1361: 0x2000, 0x1362: 0x2000, 0x1363: 0x2000,
+	0x1364: 0x2000, 0x1365: 0x2000, 0x1366: 0x2000, 0x1367: 0x2000, 0x1368: 0x2000, 0x1369: 0x2000,
+	0x136a: 0x2000, 0x136b: 0x2000, 0x136c: 0x2000, 0x136d: 0x2000,
+	0x1370: 0x2000, 0x1371: 0x2000, 0x1372: 0x2000, 0x1373: 0x2000, 0x1374: 0x2000, 0x1375: 0x2000,
+	0x1376: 0x2000, 0x1377: 0x2000, 0x1378: 0x2000, 0x1379: 0x2000, 0x137a: 0x2000, 0x137b: 0x2000,
+	0x137c: 0x2000, 0x137d: 0x2000, 0x137e: 0x2000, 0x137f: 0x2000,
+	// Block 0x4e, offset 0x1380
+	0x1380: 0x2000, 0x1381: 0x2000, 0x1382: 0x2000, 0x1383: 0x2000, 0x1384: 0x2000, 0x1385: 0x2000,
+	0x1386: 0x2000, 0x1387: 0x2000, 0x1388: 0x2000, 0x1389: 0x2000, 0x138a: 0x2000, 0x138b: 0x2000,
+	0x138c: 0x2000, 0x138d: 0x2000, 0x138e: 0x2000, 0x138f: 0x2000, 0x1390: 0x2000, 0x1391: 0x2000,
+	0x1392: 0x2000, 0x1393: 0x2000, 0x1394: 0x2000, 0x1395: 0x2000, 0x1396: 0x2000, 0x1397: 0x2000,
+	0x1398: 0x2000, 0x1399: 0x2000, 0x139a: 0x2000, 0x139b: 0x2000, 0x139c: 0x2000, 0x139d: 0x2000,
+	0x139e: 0x2000, 0x139f: 0x2000, 0x13a0: 0x2000, 0x13a1: 0x2000, 0x13a2: 0x2000, 0x13a3: 0x2000,
+	0x13a4: 0x2000, 0x13a5: 0x2000, 0x13a6: 0x2000, 0x13a7: 0x2000, 0x13a8: 0x2000, 0x13a9: 0x2000,
+	0x13b0: 0x2000, 0x13b1: 0x2000, 0x13b2: 0x2000, 0x13b3: 0x2000, 0x13b4: 0x2000, 0x13b5: 0x2000,
+	0x13b6: 0x2000, 0x13b7: 0x2000, 0x13b8: 0x2000, 0x13b9: 0x2000, 0x13ba: 0x2000, 0x13bb: 0x2000,
+	0x13bc: 0x2000, 0x13bd: 0x2000, 0x13be: 0x2000, 0x13bf: 0x2000,
+	// Block 0x4f, offset 0x13c0
+	0x13c0: 0x2000, 0x13c1: 0x2000, 0x13c2: 0x2000, 0x13c3: 0x2000, 0x13c4: 0x2000, 0x13c5: 0x2000,
+	0x13c6: 0x2000, 0x13c7: 0x2000, 0x13c8: 0x2000, 0x13c9: 0x2000, 0x13ca: 0x2000, 0x13cb: 0x2000,
+	0x13cc: 0x2000, 0x13cd: 0x2000, 0x13ce: 0x4000, 0x13cf: 0x2000, 0x13d0: 0x2000, 0x13d1: 0x4000,
+	0x13d2: 0x4000, 0x13d3: 0x4000, 0x13d4: 0x4000, 0x13d5: 0x4000, 0x13d6: 0x4000, 0x13d7: 0x4000,
+	0x13d8: 0x4000, 0x13d9: 0x4000, 0x13da: 0x4000, 0x13db: 0x2000, 0x13dc: 0x2000, 0x13dd: 0x2000,
+	0x13de: 0x2000, 0x13df: 0x2000, 0x13e0: 0x2000, 0x13e1: 0x2000, 0x13e2: 0x2000, 0x13e3: 0x2000,
+	0x13e4: 0x2000, 0x13e5: 0x2000, 0x13e6: 0x2000, 0x13e7: 0x2000, 0x13e8: 0x2000, 0x13e9: 0x2000,
+	0x13ea: 0x2000, 0x13eb: 0x2000, 0x13ec: 0x2000,
+	// Block 0x50, offset 0x1400
+	0x1400: 0x4000, 0x1401: 0x4000, 0x1402: 0x4000,
+	0x1410: 0x4000, 0x1411: 0x4000,
+	0x1412: 0x4000, 0x1413: 0x4000, 0x1414: 0x4000, 0x1415: 0x4000, 0x1416: 0x4000, 0x1417: 0x4000,
+	0x1418: 0x4000, 0x1419: 0x4000, 0x141a: 0x4000, 0x141b: 0x4000, 0x141c: 0x4000, 0x141d: 0x4000,
+	0x141e: 0x4000, 0x141f: 0x4000, 0x1420: 0x4000, 0x1421: 0x4000, 0x1422: 0x4000, 0x1423: 0x4000,
+	0x1424: 0x4000, 0x1425: 0x4000, 0x1426: 0x4000, 0x1427: 0x4000, 0x1428: 0x4000, 0x1429: 0x4000,
+	0x142a: 0x4000, 0x142b: 0x4000, 0x142c: 0x4000, 0x142d: 0x4000, 0x142e: 0x4000, 0x142f: 0x4000,
+	0x1430: 0x4000, 0x1431: 0x4000, 0x1432: 0x4000, 0x1433: 0x4000, 0x1434: 0x4000, 0x1435: 0x4000,
+	0x1436: 0x4000, 0x1437: 0x4000, 0x1438: 0x4000, 0x1439: 0x4000, 0x143a: 0x4000, 0x143b: 0x4000,
+	// Block 0x51, offset 0x1440
+	0x1440: 0x4000, 0x1441: 0x4000, 0x1442: 0x4000, 0x1443: 0x4000, 0x1444: 0x4000, 0x1445: 0x4000,
+	0x1446: 0x4000, 0x1447: 0x4000, 0x1448: 0x4000,
+	0x1450: 0x4000, 0x1451: 0x4000,
+	// Block 0x52, offset 0x1480
+	0x1480: 0x4000, 0x1481: 0x4000, 0x1482: 0x4000, 0x1483: 0x4000, 0x1484: 0x4000, 0x1485: 0x4000,
+	0x1486: 0x4000, 0x1487: 0x4000, 0x1488: 0x4000, 0x1489: 0x4000, 0x148a: 0x4000, 0x148b: 0x4000,
+	0x148c: 0x4000, 0x148d: 0x4000, 0x148e: 0x4000, 0x148f: 0x4000, 0x1490: 0x4000, 0x1491: 0x4000,
+	0x1492: 0x4000, 0x1493: 0x4000, 0x1494: 0x4000, 0x1495: 0x4000, 0x1496: 0x4000, 0x1497: 0x4000,
+	0x1498: 0x4000, 0x1499: 0x4000, 0x149a: 0x4000, 0x149b: 0x4000, 0x149c: 0x4000, 0x149d: 0x4000,
+	0x149e: 0x4000, 0x149f: 0x4000, 0x14a0: 0x4000,
+	0x14ad: 0x4000, 0x14ae: 0x4000, 0x14af: 0x4000,
+	0x14b0: 0x4000, 0x14b1: 0x4000, 0x14b2: 0x4000, 0x14b3: 0x4000, 0x14b4: 0x4000, 0x14b5: 0x4000,
+	0x14b7: 0x4000, 0x14b8: 0x4000, 0x14b9: 0x4000, 0x14ba: 0x4000, 0x14bb: 0x4000,
+	0x14bc: 0x4000, 0x14bd: 0x4000, 0x14be: 0x4000, 0x14bf: 0x4000,
+	// Block 0x53, offset 0x14c0
+	0x14c0: 0x4000, 0x14c1: 0x4000, 0x14c2: 0x4000, 0x14c3: 0x4000, 0x14c4: 0x4000, 0x14c5: 0x4000,
+	0x14c6: 0x4000, 0x14c7: 0x4000, 0x14c8: 0x4000, 0x14c9: 0x4000, 0x14ca: 0x4000, 0x14cb: 0x4000,
+	0x14cc: 0x4000, 0x14cd: 0x4000, 0x14ce: 0x4000, 0x14cf: 0x4000, 0x14d0: 0x4000, 0x14d1: 0x4000,
+	0x14d2: 0x4000, 0x14d3: 0x4000, 0x14d4: 0x4000, 0x14d5: 0x4000, 0x14d6: 0x4000, 0x14d7: 0x4000,
+	0x14d8: 0x4000, 0x14d9: 0x4000, 0x14da: 0x4000, 0x14db: 0x4000, 0x14dc: 0x4000, 0x14dd: 0x4000,
+	0x14de: 0x4000, 0x14df: 0x4000, 0x14e0: 0x4000, 0x14e1: 0x4000, 0x14e2: 0x4000, 0x14e3: 0x4000,
+	0x14e4: 0x4000, 0x14e5: 0x4000, 0x14e6: 0x4000, 0x14e7: 0x4000, 0x14e8: 0x4000, 0x14e9: 0x4000,
+	0x14ea: 0x4000, 0x14eb: 0x4000, 0x14ec: 0x4000, 0x14ed: 0x4000, 0x14ee: 0x4000, 0x14ef: 0x4000,
+	0x14f0: 0x4000, 0x14f1: 0x4000, 0x14f2: 0x4000, 0x14f3: 0x4000, 0x14f4: 0x4000, 0x14f5: 0x4000,
+	0x14f6: 0x4000, 0x14f7: 0x4000, 0x14f8: 0x4000, 0x14f9: 0x4000, 0x14fa: 0x4000, 0x14fb: 0x4000,
+	0x14fc: 0x4000, 0x14fe: 0x4000, 0x14ff: 0x4000,
+	// Block 0x54, offset 0x1500
+	0x1500: 0x4000, 0x1501: 0x4000, 0x1502: 0x4000, 0x1503: 0x4000, 0x1504: 0x4000, 0x1505: 0x4000,
+	0x1506: 0x4000, 0x1507: 0x4000, 0x1508: 0x4000, 0x1509: 0x4000, 0x150a: 0x4000, 0x150b: 0x4000,
+	0x150c: 0x4000, 0x150d: 0x4000, 0x150e: 0x4000, 0x150f: 0x4000, 0x1510: 0x4000, 0x1511: 0x4000,
+	0x1512: 0x4000, 0x1513: 0x4000,
+	0x1520: 0x4000, 0x1521: 0x4000, 0x1522: 0x4000, 0x1523: 0x4000,
+	0x1524: 0x4000, 0x1525: 0x4000, 0x1526: 0x4000, 0x1527: 0x4000, 0x1528: 0x4000, 0x1529: 0x4000,
+	0x152a: 0x4000, 0x152b: 0x4000, 0x152c: 0x4000, 0x152d: 0x4000, 0x152e: 0x4000, 0x152f: 0x4000,
+	0x1530: 0x4000, 0x1531: 0x4000, 0x1532: 0x4000, 0x1533: 0x4000, 0x1534: 0x4000, 0x1535: 0x4000,
+	0x1536: 0x4000, 0x1537: 0x4000, 0x1538: 0x4000, 0x1539: 0x4000, 0x153a: 0x4000, 0x153b: 0x4000,
+	0x153c: 0x4000, 0x153d: 0x4000, 0x153e: 0x4000, 0x153f: 0x4000,
+	// Block 0x55, offset 0x1540
+	0x1540: 0x4000, 0x1541: 0x4000, 0x1542: 0x4000, 0x1543: 0x4000, 0x1544: 0x4000, 0x1545: 0x4000,
+	0x1546: 0x4000, 0x1547: 0x4000, 0x1548: 0x4000, 0x1549: 0x4000, 0x154a: 0x4000,
+	0x154f: 0x4000, 0x1550: 0x4000, 0x1551: 0x4000,
+	0x1552: 0x4000, 0x1553: 0x4000,
+	0x1560: 0x4000, 0x1561: 0x4000, 0x1562: 0x4000, 0x1563: 0x4000,
+	0x1564: 0x4000, 0x1565: 0x4000, 0x1566: 0x4000, 0x1567: 0x4000, 0x1568: 0x4000, 0x1569: 0x4000,
+	0x156a: 0x4000, 0x156b: 0x4000, 0x156c: 0x4000, 0x156d: 0x4000, 0x156e: 0x4000, 0x156f: 0x4000,
+	0x1570: 0x4000, 0x1574: 0x4000,
+	0x1578: 0x4000, 0x1579: 0x4000, 0x157a: 0x4000, 0x157b: 0x4000,
+	0x157c: 0x4000, 0x157d: 0x4000, 0x157e: 0x4000, 0x157f: 0x4000,
+	// Block 0x56, offset 0x1580
+	0x1580: 0x4000, 0x1582: 0x4000, 0x1583: 0x4000, 0x1584: 0x4000, 0x1585: 0x4000,
+	0x1586: 0x4000, 0x1587: 0x4000, 0x1588: 0x4000, 0x1589: 0x4000, 0x158a: 0x4000, 0x158b: 0x4000,
+	0x158c: 0x4000, 0x158d: 0x4000, 0x158e: 0x4000, 0x158f: 0x4000, 0x1590: 0x4000, 0x1591: 0x4000,
+	0x1592: 0x4000, 0x1593: 0x4000, 0x1594: 0x4000, 0x1595: 0x4000, 0x1596: 0x4000, 0x1597: 0x4000,
+	0x1598: 0x4000, 0x1599: 0x4000, 0x159a: 0x4000, 0x159b: 0x4000, 0x159c: 0x4000, 0x159d: 0x4000,
+	0x159e: 0x4000, 0x159f: 0x4000, 0x15a0: 0x4000, 0x15a1: 0x4000, 0x15a2: 0x4000, 0x15a3: 0x4000,
+	0x15a4: 0x4000, 0x15a5: 0x4000, 0x15a6: 0x4000, 0x15a7: 0x4000, 0x15a8: 0x4000, 0x15a9: 0x4000,
+	0x15aa: 0x4000, 0x15ab: 0x4000, 0x15ac: 0x4000, 0x15ad: 0x4000, 0x15ae: 0x4000, 0x15af: 0x4000,
+	0x15b0: 0x4000, 0x15b1: 0x4000, 0x15b2: 0x4000, 0x15b3: 0x4000, 0x15b4: 0x4000, 0x15b5: 0x4000,
+	0x15b6: 0x4000, 0x15b7: 0x4000, 0x15b8: 0x4000, 0x15b9: 0x4000, 0x15ba: 0x4000, 0x15bb: 0x4000,
+	0x15bc: 0x4000, 0x15bd: 0x4000, 0x15be: 0x4000, 0x15bf: 0x4000,
+	// Block 0x57, offset 0x15c0
+	0x15c0: 0x4000, 0x15c1: 0x4000, 0x15c2: 0x4000, 0x15c3: 0x4000, 0x15c4: 0x4000, 0x15c5: 0x4000,
+	0x15c6: 0x4000, 0x15c7: 0x4000, 0x15c8: 0x4000, 0x15c9: 0x4000, 0x15ca: 0x4000, 0x15cb: 0x4000,
+	0x15cc: 0x4000, 0x15cd: 0x4000, 0x15ce: 0x4000, 0x15cf: 0x4000, 0x15d0: 0x4000, 0x15d1: 0x4000,
+	0x15d2: 0x4000, 0x15d3: 0x4000, 0x15d4: 0x4000, 0x15d5: 0x4000, 0x15d6: 0x4000, 0x15d7: 0x4000,
+	0x15d8: 0x4000, 0x15d9: 0x4000, 0x15da: 0x4000, 0x15db: 0x4000, 0x15dc: 0x4000, 0x15dd: 0x4000,
+	0x15de: 0x4000, 0x15df: 0x4000, 0x15e0: 0x4000, 0x15e1: 0x4000, 0x15e2: 0x4000, 0x15e3: 0x4000,
+	0x15e4: 0x4000, 0x15e5: 0x4000, 0x15e6: 0x4000, 0x15e7: 0x4000, 0x15e8: 0x4000, 0x15e9: 0x4000,
+	0x15ea: 0x4000, 0x15eb: 0x4000, 0x15ec: 0x4000, 0x15ed: 0x4000, 0x15ee: 0x4000, 0x15ef: 0x4000,
+	0x15f0: 0x4000, 0x15f1: 0x4000, 0x15f2: 0x4000, 0x15f3: 0x4000, 0x15f4: 0x4000, 0x15f5: 0x4000,
+	0x15f6: 0x4000, 0x15f7: 0x4000, 0x15f8: 0x4000, 0x15f9: 0x4000, 0x15fa: 0x4000, 0x15fb: 0x4000,
+	0x15fc: 0x4000, 0x15ff: 0x4000,
+	// Block 0x58, offset 0x1600
+	0x1600: 0x4000, 0x1601: 0x4000, 0x1602: 0x4000, 0x1603: 0x4000, 0x1604: 0x4000, 0x1605: 0x4000,
+	0x1606: 0x4000, 0x1607: 0x4000, 0x1608: 0x4000, 0x1609: 0x4000, 0x160a: 0x4000, 0x160b: 0x4000,
+	0x160c: 0x4000, 0x160d: 0x4000, 0x160e: 0x4000, 0x160f: 0x4000, 0x1610: 0x4000, 0x1611: 0x4000,
+	0x1612: 0x4000, 0x1613: 0x4000, 0x1614: 0x4000, 0x1615: 0x4000, 0x1616: 0x4000, 0x1617: 0x4000,
+	0x1618: 0x4000, 0x1619: 0x4000, 0x161a: 0x4000, 0x161b: 0x4000, 0x161c: 0x4000, 0x161d: 0x4000,
+	0x161e: 0x4000, 0x161f: 0x4000, 0x1620: 0x4000, 0x1621: 0x4000, 0x1622: 0x4000, 0x1623: 0x4000,
+	0x1624: 0x4000, 0x1625: 0x4000, 0x1626: 0x4000, 0x1627: 0x4000, 0x1628: 0x4000, 0x1629: 0x4000,
+	0x162a: 0x4000, 0x162b: 0x4000, 0x162c: 0x4000, 0x162d: 0x4000, 0x162e: 0x4000, 0x162f: 0x4000,
+	0x1630: 0x4000, 0x1631: 0x4000, 0x1632: 0x4000, 0x1633: 0x4000, 0x1634: 0x4000, 0x1635: 0x4000,
+	0x1636: 0x4000, 0x1637: 0x4000, 0x1638: 0x4000, 0x1639: 0x4000, 0x163a: 0x4000, 0x163b: 0x4000,
+	0x163c: 0x4000, 0x163d: 0x4000,
+	// Block 0x59, offset 0x1640
+	0x164b: 0x4000,
+	0x164c: 0x4000, 0x164d: 0x4000, 0x164e: 0x4000, 0x1650: 0x4000, 0x1651: 0x4000,
+	0x1652: 0x4000, 0x1653: 0x4000, 0x1654: 0x4000, 0x1655: 0x4000, 0x1656: 0x4000, 0x1657: 0x4000,
+	0x1658: 0x4000, 0x1659: 0x4000, 0x165a: 0x4000, 0x165b: 0x4000, 0x165c: 0x4000, 0x165d: 0x4000,
+	0x165e: 0x4000, 0x165f: 0x4000, 0x1660: 0x4000, 0x1661: 0x4000, 0x1662: 0x4000, 0x1663: 0x4000,
+	0x1664: 0x4000, 0x1665: 0x4000, 0x1666: 0x4000, 0x1667: 0x4000,
+	0x167a: 0x4000,
+	// Block 0x5a, offset 0x1680
+	0x1695: 0x4000, 0x1696: 0x4000,
+	0x16a4: 0x4000,
+	// Block 0x5b, offset 0x16c0
+	0x16fb: 0x4000,
+	0x16fc: 0x4000, 0x16fd: 0x4000, 0x16fe: 0x4000, 0x16ff: 0x4000,
+	// Block 0x5c, offset 0x1700
+	0x1700: 0x4000, 0x1701: 0x4000, 0x1702: 0x4000, 0x1703: 0x4000, 0x1704: 0x4000, 0x1705: 0x4000,
+	0x1706: 0x4000, 0x1707: 0x4000, 0x1708: 0x4000, 0x1709: 0x4000, 0x170a: 0x4000, 0x170b: 0x4000,
+	0x170c: 0x4000, 0x170d: 0x4000, 0x170e: 0x4000, 0x170f: 0x4000,
+	// Block 0x5d, offset 0x1740
+	0x1740: 0x4000, 0x1741: 0x4000, 0x1742: 0x4000, 0x1743: 0x4000, 0x1744: 0x4000, 0x1745: 0x4000,
+	0x174c: 0x4000, 0x1750: 0x4000, 0x1751: 0x4000,
+	0x1752: 0x4000,
+	0x176b: 0x4000, 0x176c: 0x4000,
+	0x1774: 0x4000, 0x1775: 0x4000,
+	0x1776: 0x4000,
+	// Block 0x5e, offset 0x1780
+	0x1790: 0x4000, 0x1791: 0x4000,
+	0x1792: 0x4000, 0x1793: 0x4000, 0x1794: 0x4000, 0x1795: 0x4000, 0x1796: 0x4000, 0x1797: 0x4000,
+	0x1798: 0x4000, 0x1799: 0x4000, 0x179a: 0x4000, 0x179b: 0x4000, 0x179c: 0x4000, 0x179d: 0x4000,
+	0x179e: 0x4000, 0x17a0: 0x4000, 0x17a1: 0x4000, 0x17a2: 0x4000, 0x17a3: 0x4000,
+	0x17a4: 0x4000, 0x17a5: 0x4000, 0x17a6: 0x4000, 0x17a7: 0x4000,
+	0x17b0: 0x4000, 0x17b3: 0x4000, 0x17b4: 0x4000, 0x17b5: 0x4000,
+	0x17b6: 0x4000, 0x17b7: 0x4000, 0x17b8: 0x4000, 0x17b9: 0x4000, 0x17ba: 0x4000, 0x17bb: 0x4000,
+	0x17bc: 0x4000, 0x17bd: 0x4000, 0x17be: 0x4000,
+	// Block 0x5f, offset 0x17c0
+	0x17c0: 0x4000, 0x17c1: 0x4000, 0x17c2: 0x4000, 0x17c3: 0x4000, 0x17c4: 0x4000, 0x17c5: 0x4000,
+	0x17c6: 0x4000, 0x17c7: 0x4000, 0x17c8: 0x4000, 0x17c9: 0x4000, 0x17ca: 0x4000, 0x17cb: 0x4000,
+	0x17d0: 0x4000, 0x17d1: 0x4000,
+	0x17d2: 0x4000, 0x17d3: 0x4000, 0x17d4: 0x4000, 0x17d5: 0x4000, 0x17d6: 0x4000, 0x17d7: 0x4000,
+	0x17d8: 0x4000, 0x17d9: 0x4000, 0x17da: 0x4000, 0x17db: 0x4000, 0x17dc: 0x4000, 0x17dd: 0x4000,
+	0x17de: 0x4000,
+	// Block 0x60, offset 0x1800
+	0x1800: 0x4000, 0x1801: 0x4000, 0x1802: 0x4000, 0x1803: 0x4000, 0x1804: 0x4000, 0x1805: 0x4000,
+	0x1806: 0x4000, 0x1807: 0x4000, 0x1808: 0x4000, 0x1809: 0x4000, 0x180a: 0x4000, 0x180b: 0x4000,
+	0x180c: 0x4000, 0x180d: 0x4000, 0x180e: 0x4000, 0x180f: 0x4000, 0x1810: 0x4000, 0x1811: 0x4000,
+	// Block 0x61, offset 0x1840
+	0x1840: 0x4000,
+	// Block 0x62, offset 0x1880
+	0x1880: 0x2000, 0x1881: 0x2000, 0x1882: 0x2000, 0x1883: 0x2000, 0x1884: 0x2000, 0x1885: 0x2000,
+	0x1886: 0x2000, 0x1887: 0x2000, 0x1888: 0x2000, 0x1889: 0x2000, 0x188a: 0x2000, 0x188b: 0x2000,
+	0x188c: 0x2000, 0x188d: 0x2000, 0x188e: 0x2000, 0x188f: 0x2000, 0x1890: 0x2000, 0x1891: 0x2000,
+	0x1892: 0x2000, 0x1893: 0x2000, 0x1894: 0x2000, 0x1895: 0x2000, 0x1896: 0x2000, 0x1897: 0x2000,
+	0x1898: 0x2000, 0x1899: 0x2000, 0x189a: 0x2000, 0x189b: 0x2000, 0x189c: 0x2000, 0x189d: 0x2000,
+	0x189e: 0x2000, 0x189f: 0x2000, 0x18a0: 0x2000, 0x18a1: 0x2000, 0x18a2: 0x2000, 0x18a3: 0x2000,
+	0x18a4: 0x2000, 0x18a5: 0x2000, 0x18a6: 0x2000, 0x18a7: 0x2000, 0x18a8: 0x2000, 0x18a9: 0x2000,
+	0x18aa: 0x2000, 0x18ab: 0x2000, 0x18ac: 0x2000, 0x18ad: 0x2000, 0x18ae: 0x2000, 0x18af: 0x2000,
+	0x18b0: 0x2000, 0x18b1: 0x2000, 0x18b2: 0x2000, 0x18b3: 0x2000, 0x18b4: 0x2000, 0x18b5: 0x2000,
+	0x18b6: 0x2000, 0x18b7: 0x2000, 0x18b8: 0x2000, 0x18b9: 0x2000, 0x18ba: 0x2000, 0x18bb: 0x2000,
+	0x18bc: 0x2000, 0x18bd: 0x2000,
+}
+
+// widthIndex: 22 blocks, 1408 entries, 1408 bytes
+// Block 0 is the zero block.
+var widthIndex = [1408]uint8{
+	// Block 0x0, offset 0x0
+	// Block 0x1, offset 0x40
+	// Block 0x2, offset 0x80
+	// Block 0x3, offset 0xc0
+	0xc2: 0x01, 0xc3: 0x02, 0xc4: 0x03, 0xc5: 0x04, 0xc7: 0x05,
+	0xc9: 0x06, 0xcb: 0x07, 0xcc: 0x08, 0xcd: 0x09, 0xce: 0x0a, 0xcf: 0x0b,
+	0xd0: 0x0c, 0xd1: 0x0d,
+	0xe1: 0x02, 0xe2: 0x03, 0xe3: 0x04, 0xe4: 0x05, 0xe5: 0x06, 0xe6: 0x06, 0xe7: 0x06,
+	0xe8: 0x06, 0xe9: 0x06, 0xea: 0x07, 0xeb: 0x06, 0xec: 0x06, 0xed: 0x08, 0xee: 0x09, 0xef: 0x0a,
+	0xf0: 0x0f, 0xf3: 0x12, 0xf4: 0x13,
+	// Block 0x4, offset 0x100
+	0x104: 0x0e, 0x105: 0x0f,
+	// Block 0x5, offset 0x140
+	0x140: 0x10, 0x141: 0x11, 0x142: 0x12, 0x144: 0x13, 0x145: 0x14, 0x146: 0x15, 0x147: 0x16,
+	0x148: 0x17, 0x149: 0x18, 0x14a: 0x19, 0x14c: 0x1a, 0x14f: 0x1b,
+	0x151: 0x1c, 0x152: 0x08, 0x153: 0x1d, 0x154: 0x1e, 0x155: 0x1f, 0x156: 0x20, 0x157: 0x21,
+	0x158: 0x22, 0x159: 0x23, 0x15a: 0x24, 0x15b: 0x25, 0x15c: 0x26, 0x15d: 0x27, 0x15e: 0x28, 0x15f: 0x29,
+	0x166: 0x2a,
+	0x16c: 0x2b, 0x16d: 0x2c,
+	0x17a: 0x2d, 0x17b: 0x2e, 0x17c: 0x0e, 0x17d: 0x0e, 0x17e: 0x0e, 0x17f: 0x2f,
+	// Block 0x6, offset 0x180
+	0x180: 0x30, 0x181: 0x31, 0x182: 0x32, 0x183: 0x33, 0x184: 0x34, 0x185: 0x35, 0x186: 0x36, 0x187: 0x37,
+	0x188: 0x38, 0x189: 0x39, 0x18a: 0x0e, 0x18b: 0x3a, 0x18c: 0x0e, 0x18d: 0x0e, 0x18e: 0x0e, 0x18f: 0x0e,
+	0x190: 0x0e, 0x191: 0x0e, 0x192: 0x0e, 0x193: 0x0e, 0x194: 0x0e, 0x195: 0x0e, 0x196: 0x0e, 0x197: 0x0e,
+	0x198: 0x0e, 0x199: 0x0e, 0x19a: 0x0e, 0x19b: 0x0e, 0x19c: 0x0e, 0x19d: 0x0e, 0x19e: 0x0e, 0x19f: 0x0e,
+	0x1a0: 0x0e, 0x1a1: 0x0e, 0x1a2: 0x0e, 0x1a3: 0x0e, 0x1a4: 0x0e, 0x1a5: 0x0e, 0x1a6: 0x0e, 0x1a7: 0x0e,
+	0x1a8: 0x0e, 0x1a9: 0x0e, 0x1aa: 0x0e, 0x1ab: 0x0e, 0x1ac: 0x0e, 0x1ad: 0x0e, 0x1ae: 0x0e, 0x1af: 0x0e,
+	0x1b0: 0x0e, 0x1b1: 0x0e, 0x1b2: 0x0e, 0x1b3: 0x0e, 0x1b4: 0x0e, 0x1b5: 0x0e, 0x1b6: 0x0e, 0x1b7: 0x0e,
+	0x1b8: 0x0e, 0x1b9: 0x0e, 0x1ba: 0x0e, 0x1bb: 0x0e, 0x1bc: 0x0e, 0x1bd: 0x0e, 0x1be: 0x0e, 0x1bf: 0x0e,
+	// Block 0x7, offset 0x1c0
+	0x1c0: 0x0e, 0x1c1: 0x0e, 0x1c2: 0x0e, 0x1c3: 0x0e, 0x1c4: 0x0e, 0x1c5: 0x0e, 0x1c6: 0x0e, 0x1c7: 0x0e,
+	0x1c8: 0x0e, 0x1c9: 0x0e, 0x1ca: 0x0e, 0x1cb: 0x0e, 0x1cc: 0x0e, 0x1cd: 0x0e, 0x1ce: 0x0e, 0x1cf: 0x0e,
+	0x1d0: 0x0e, 0x1d1: 0x0e, 0x1d2: 0x0e, 0x1d3: 0x0e, 0x1d4: 0x0e, 0x1d5: 0x0e, 0x1d6: 0x0e, 0x1d7: 0x0e,
+	0x1d8: 0x0e, 0x1d9: 0x0e, 0x1da: 0x0e, 0x1db: 0x0e, 0x1dc: 0x0e, 0x1dd: 0x0e, 0x1de: 0x0e, 0x1df: 0x0e,
+	0x1e0: 0x0e, 0x1e1: 0x0e, 0x1e2: 0x0e, 0x1e3: 0x0e, 0x1e4: 0x0e, 0x1e5: 0x0e, 0x1e6: 0x0e, 0x1e7: 0x0e,
+	0x1e8: 0x0e, 0x1e9: 0x0e, 0x1ea: 0x0e, 0x1eb: 0x0e, 0x1ec: 0x0e, 0x1ed: 0x0e, 0x1ee: 0x0e, 0x1ef: 0x0e,
+	0x1f0: 0x0e, 0x1f1: 0x0e, 0x1f2: 0x0e, 0x1f3: 0x0e, 0x1f4: 0x0e, 0x1f5: 0x0e, 0x1f6: 0x0e,
+	0x1f8: 0x0e, 0x1f9: 0x0e, 0x1fa: 0x0e, 0x1fb: 0x0e, 0x1fc: 0x0e, 0x1fd: 0x0e, 0x1fe: 0x0e, 0x1ff: 0x0e,
+	// Block 0x8, offset 0x200
+	0x200: 0x0e, 0x201: 0x0e, 0x202: 0x0e, 0x203: 0x0e, 0x204: 0x0e, 0x205: 0x0e, 0x206: 0x0e, 0x207: 0x0e,
+	0x208: 0x0e, 0x209: 0x0e, 0x20a: 0x0e, 0x20b: 0x0e, 0x20c: 0x0e, 0x20d: 0x0e, 0x20e: 0x0e, 0x20f: 0x0e,
+	0x210: 0x0e, 0x211: 0x0e, 0x212: 0x0e, 0x213: 0x0e, 0x214: 0x0e, 0x215: 0x0e, 0x216: 0x0e, 0x217: 0x0e,
+	0x218: 0x0e, 0x219: 0x0e, 0x21a: 0x0e, 0x21b: 0x0e, 0x21c: 0x0e, 0x21d: 0x0e, 0x21e: 0x0e, 0x21f: 0x0e,
+	0x220: 0x0e, 0x221: 0x0e, 0x222: 0x0e, 0x223: 0x0e, 0x224: 0x0e, 0x225: 0x0e, 0x226: 0x0e, 0x227: 0x0e,
+	0x228: 0x0e, 0x229: 0x0e, 0x22a: 0x0e, 0x22b: 0x0e, 0x22c: 0x0e, 0x22d: 0x0e, 0x22e: 0x0e, 0x22f: 0x0e,
+	0x230: 0x0e, 0x231: 0x0e, 0x232: 0x0e, 0x233: 0x0e, 0x234: 0x0e, 0x235: 0x0e, 0x236: 0x0e, 0x237: 0x0e,
+	0x238: 0x0e, 0x239: 0x0e, 0x23a: 0x0e, 0x23b: 0x0e, 0x23c: 0x0e, 0x23d: 0x0e, 0x23e: 0x0e, 0x23f: 0x0e,
+	// Block 0x9, offset 0x240
+	0x240: 0x0e, 0x241: 0x0e, 0x242: 0x0e, 0x243: 0x0e, 0x244: 0x0e, 0x245: 0x0e, 0x246: 0x0e, 0x247: 0x0e,
+	0x248: 0x0e, 0x249: 0x0e, 0x24a: 0x0e, 0x24b: 0x0e, 0x24c: 0x0e, 0x24d: 0x0e, 0x24e: 0x0e, 0x24f: 0x0e,
+	0x250: 0x0e, 0x251: 0x0e, 0x252: 0x3b, 0x253: 0x3c,
+	0x265: 0x3d,
+	0x270: 0x0e, 0x271: 0x0e, 0x272: 0x0e, 0x273: 0x0e, 0x274: 0x0e, 0x275: 0x0e, 0x276: 0x0e, 0x277: 0x0e,
+	0x278: 0x0e, 0x279: 0x0e, 0x27a: 0x0e, 0x27b: 0x0e, 0x27c: 0x0e, 0x27d: 0x0e, 0x27e: 0x0e, 0x27f: 0x0e,
+	// Block 0xa, offset 0x280
+	0x280: 0x0e, 0x281: 0x0e, 0x282: 0x0e, 0x283: 0x0e, 0x284: 0x0e, 0x285: 0x0e, 0x286: 0x0e, 0x287: 0x0e,
+	0x288: 0x0e, 0x289: 0x0e, 0x28a: 0x0e, 0x28b: 0x0e, 0x28c: 0x0e, 0x28d: 0x0e, 0x28e: 0x0e, 0x28f: 0x0e,
+	0x290: 0x0e, 0x291: 0x0e, 0x292: 0x0e, 0x293: 0x0e, 0x294: 0x0e, 0x295: 0x0e, 0x296: 0x0e, 0x297: 0x0e,
+	0x298: 0x0e, 0x299: 0x0e, 0x29a: 0x0e, 0x29b: 0x0e, 0x29c: 0x0e, 0x29d: 0x0e, 0x29e: 0x3e,
+	// Block 0xb, offset 0x2c0
+	0x2c0: 0x08, 0x2c1: 0x08, 0x2c2: 0x08, 0x2c3: 0x08, 0x2c4: 0x08, 0x2c5: 0x08, 0x2c6: 0x08, 0x2c7: 0x08,
+	0x2c8: 0x08, 0x2c9: 0x08, 0x2ca: 0x08, 0x2cb: 0x08, 0x2cc: 0x08, 0x2cd: 0x08, 0x2ce: 0x08, 0x2cf: 0x08,
+	0x2d0: 0x08, 0x2d1: 0x08, 0x2d2: 0x08, 0x2d3: 0x08, 0x2d4: 0x08, 0x2d5: 0x08, 0x2d6: 0x08, 0x2d7: 0x08,
+	0x2d8: 0x08, 0x2d9: 0x08, 0x2da: 0x08, 0x2db: 0x08, 0x2dc: 0x08, 0x2dd: 0x08, 0x2de: 0x08, 0x2df: 0x08,
+	0x2e0: 0x08, 0x2e1: 0x08, 0x2e2: 0x08, 0x2e3: 0x08, 0x2e4: 0x08, 0x2e5: 0x08, 0x2e6: 0x08, 0x2e7: 0x08,
+	0x2e8: 0x08, 0x2e9: 0x08, 0x2ea: 0x08, 0x2eb: 0x08, 0x2ec: 0x08, 0x2ed: 0x08, 0x2ee: 0x08, 0x2ef: 0x08,
+	0x2f0: 0x08, 0x2f1: 0x08, 0x2f2: 0x08, 0x2f3: 0x08, 0x2f4: 0x08, 0x2f5: 0x08, 0x2f6: 0x08, 0x2f7: 0x08,
+	0x2f8: 0x08, 0x2f9: 0x08, 0x2fa: 0x08, 0x2fb: 0x08, 0x2fc: 0x08, 0x2fd: 0x08, 0x2fe: 0x08, 0x2ff: 0x08,
+	// Block 0xc, offset 0x300
+	0x300: 0x08, 0x301: 0x08, 0x302: 0x08, 0x303: 0x08, 0x304: 0x08, 0x305: 0x08, 0x306: 0x08, 0x307: 0x08,
+	0x308: 0x08, 0x309: 0x08, 0x30a: 0x08, 0x30b: 0x08, 0x30c: 0x08, 0x30d: 0x08, 0x30e: 0x08, 0x30f: 0x08,
+	0x310: 0x08, 0x311: 0x08, 0x312: 0x08, 0x313: 0x08, 0x314: 0x08, 0x315: 0x08, 0x316: 0x08, 0x317: 0x08,
+	0x318: 0x08, 0x319: 0x08, 0x31a: 0x08, 0x31b: 0x08, 0x31c: 0x08, 0x31d: 0x08, 0x31e: 0x08, 0x31f: 0x08,
+	0x320: 0x08, 0x321: 0x08, 0x322: 0x08, 0x323: 0x08, 0x324: 0x0e, 0x325: 0x0e, 0x326: 0x0e, 0x327: 0x0e,
+	0x328: 0x0e, 0x329: 0x0e, 0x32a: 0x0e, 0x32b: 0x0e,
+	0x338: 0x3f, 0x339: 0x40, 0x33c: 0x41, 0x33d: 0x42, 0x33e: 0x43, 0x33f: 0x44,
+	// Block 0xd, offset 0x340
+	0x37f: 0x45,
+	// Block 0xe, offset 0x380
+	0x380: 0x0e, 0x381: 0x0e, 0x382: 0x0e, 0x383: 0x0e, 0x384: 0x0e, 0x385: 0x0e, 0x386: 0x0e, 0x387: 0x0e,
+	0x388: 0x0e, 0x389: 0x0e, 0x38a: 0x0e, 0x38b: 0x0e, 0x38c: 0x0e, 0x38d: 0x0e, 0x38e: 0x0e, 0x38f: 0x0e,
+	0x390: 0x0e, 0x391: 0x0e, 0x392: 0x0e, 0x393: 0x0e, 0x394: 0x0e, 0x395: 0x0e, 0x396: 0x0e, 0x397: 0x0e,
+	0x398: 0x0e, 0x399: 0x0e, 0x39a: 0x0e, 0x39b: 0x0e, 0x39c: 0x0e, 0x39d: 0x0e, 0x39e: 0x0e, 0x39f: 0x46,
+	0x3a0: 0x0e, 0x3a1: 0x0e, 0x3a2: 0x0e, 0x3a3: 0x0e, 0x3a4: 0x0e, 0x3a5: 0x0e, 0x3a6: 0x0e, 0x3a7: 0x0e,
+	0x3a8: 0x0e, 0x3a9: 0x0e, 0x3aa: 0x0e, 0x3ab: 0x47,
+	// Block 0xf, offset 0x3c0
+	0x3c0: 0x48,
+	// Block 0x10, offset 0x400
+	0x400: 0x49, 0x403: 0x4a, 0x404: 0x4b, 0x405: 0x4c, 0x406: 0x4d,
+	0x408: 0x4e, 0x409: 0x4f, 0x40c: 0x50, 0x40d: 0x51, 0x40e: 0x52, 0x40f: 0x53,
+	0x410: 0x3a, 0x411: 0x54, 0x412: 0x0e, 0x413: 0x55, 0x414: 0x56, 0x415: 0x57, 0x416: 0x58, 0x417: 0x59,
+	0x418: 0x0e, 0x419: 0x5a, 0x41a: 0x0e, 0x41b: 0x5b,
+	0x424: 0x5c, 0x425: 0x5d, 0x426: 0x5e, 0x427: 0x5f,
+	// Block 0x11, offset 0x440
+	0x456: 0x0b, 0x457: 0x06,
+	0x458: 0x0c, 0x45b: 0x0d, 0x45f: 0x0e,
+	0x460: 0x06, 0x461: 0x06, 0x462: 0x06, 0x463: 0x06, 0x464: 0x06, 0x465: 0x06, 0x466: 0x06, 0x467: 0x06,
+	0x468: 0x06, 0x469: 0x06, 0x46a: 0x06, 0x46b: 0x06, 0x46c: 0x06, 0x46d: 0x06, 0x46e: 0x06, 0x46f: 0x06,
+	0x470: 0x06, 0x471: 0x06, 0x472: 0x06, 0x473: 0x06, 0x474: 0x06, 0x475: 0x06, 0x476: 0x06, 0x477: 0x06,
+	0x478: 0x06, 0x479: 0x06, 0x47a: 0x06, 0x47b: 0x06, 0x47c: 0x06, 0x47d: 0x06, 0x47e: 0x06, 0x47f: 0x06,
+	// Block 0x12, offset 0x480
+	0x484: 0x08, 0x485: 0x08, 0x486: 0x08, 0x487: 0x09,
+	// Block 0x13, offset 0x4c0
+	0x4c0: 0x08, 0x4c1: 0x08, 0x4c2: 0x08, 0x4c3: 0x08, 0x4c4: 0x08, 0x4c5: 0x08, 0x4c6: 0x08, 0x4c7: 0x08,
+	0x4c8: 0x08, 0x4c9: 0x08, 0x4ca: 0x08, 0x4cb: 0x08, 0x4cc: 0x08, 0x4cd: 0x08, 0x4ce: 0x08, 0x4cf: 0x08,
+	0x4d0: 0x08, 0x4d1: 0x08, 0x4d2: 0x08, 0x4d3: 0x08, 0x4d4: 0x08, 0x4d5: 0x08, 0x4d6: 0x08, 0x4d7: 0x08,
+	0x4d8: 0x08, 0x4d9: 0x08, 0x4da: 0x08, 0x4db: 0x08, 0x4dc: 0x08, 0x4dd: 0x08, 0x4de: 0x08, 0x4df: 0x08,
+	0x4e0: 0x08, 0x4e1: 0x08, 0x4e2: 0x08, 0x4e3: 0x08, 0x4e4: 0x08, 0x4e5: 0x08, 0x4e6: 0x08, 0x4e7: 0x08,
+	0x4e8: 0x08, 0x4e9: 0x08, 0x4ea: 0x08, 0x4eb: 0x08, 0x4ec: 0x08, 0x4ed: 0x08, 0x4ee: 0x08, 0x4ef: 0x08,
+	0x4f0: 0x08, 0x4f1: 0x08, 0x4f2: 0x08, 0x4f3: 0x08, 0x4f4: 0x08, 0x4f5: 0x08, 0x4f6: 0x08, 0x4f7: 0x08,
+	0x4f8: 0x08, 0x4f9: 0x08, 0x4fa: 0x08, 0x4fb: 0x08, 0x4fc: 0x08, 0x4fd: 0x08, 0x4fe: 0x08, 0x4ff: 0x60,
+	// Block 0x14, offset 0x500
+	0x520: 0x10,
+	0x530: 0x09, 0x531: 0x09, 0x532: 0x09, 0x533: 0x09, 0x534: 0x09, 0x535: 0x09, 0x536: 0x09, 0x537: 0x09,
+	0x538: 0x09, 0x539: 0x09, 0x53a: 0x09, 0x53b: 0x09, 0x53c: 0x09, 0x53d: 0x09, 0x53e: 0x09, 0x53f: 0x11,
+	// Block 0x15, offset 0x540
+	0x540: 0x09, 0x541: 0x09, 0x542: 0x09, 0x543: 0x09, 0x544: 0x09, 0x545: 0x09, 0x546: 0x09, 0x547: 0x09,
+	0x548: 0x09, 0x549: 0x09, 0x54a: 0x09, 0x54b: 0x09, 0x54c: 0x09, 0x54d: 0x09, 0x54e: 0x09, 0x54f: 0x11,
+}
+
+// inverseData contains 4-byte entries of the following format:
+//   <length> <modified UTF-8-encoded rune> <0 padding>
+// The last byte of the UTF-8-encoded rune is xor-ed with the last byte of the
+// UTF-8 encoding of the original rune. Mappings often have the following
+// pattern:
+//   A -> A  (U+FF21 -> U+0041)
+//   B -> B  (U+FF22 -> U+0042)
+//   ...
+// By xor-ing the last byte the same entry can be shared by many mappings. This
+// reduces the total number of distinct entries by about two thirds.
+// The resulting entry for the aforementioned mappings is
+//   { 0x01, 0xE0, 0x00, 0x00 }
+// Using this entry to map U+FF21 (UTF-8 [EF BC A1]), we get
+//   E0 ^ A1 = 41.
+// Similarly, for U+FF22 (UTF-8 [EF BC A2]), we get
+//   E0 ^ A2 = 42.
+// Note that because of the xor-ing, the byte sequence stored in the entry is
+// not valid UTF-8.
+var inverseData = [150][4]byte{
+	{0x00, 0x00, 0x00, 0x00},
+	{0x03, 0xe3, 0x80, 0xa0},
+	{0x03, 0xef, 0xbc, 0xa0},
+	{0x03, 0xef, 0xbc, 0xe0},
+	{0x03, 0xef, 0xbd, 0xe0},
+	{0x03, 0xef, 0xbf, 0x02},
+	{0x03, 0xef, 0xbf, 0x00},
+	{0x03, 0xef, 0xbf, 0x0e},
+	{0x03, 0xef, 0xbf, 0x0c},
+	{0x03, 0xef, 0xbf, 0x0f},
+	{0x03, 0xef, 0xbf, 0x39},
+	{0x03, 0xef, 0xbf, 0x3b},
+	{0x03, 0xef, 0xbf, 0x3f},
+	{0x03, 0xef, 0xbf, 0x2a},
+	{0x03, 0xef, 0xbf, 0x0d},
+	{0x03, 0xef, 0xbf, 0x25},
+	{0x03, 0xef, 0xbd, 0x1a},
+	{0x03, 0xef, 0xbd, 0x26},
+	{0x01, 0xa0, 0x00, 0x00},
+	{0x03, 0xef, 0xbd, 0x25},
+	{0x03, 0xef, 0xbd, 0x23},
+	{0x03, 0xef, 0xbd, 0x2e},
+	{0x03, 0xef, 0xbe, 0x07},
+	{0x03, 0xef, 0xbe, 0x05},
+	{0x03, 0xef, 0xbd, 0x06},
+	{0x03, 0xef, 0xbd, 0x13},
+	{0x03, 0xef, 0xbd, 0x0b},
+	{0x03, 0xef, 0xbd, 0x16},
+	{0x03, 0xef, 0xbd, 0x0c},
+	{0x03, 0xef, 0xbd, 0x15},
+	{0x03, 0xef, 0xbd, 0x0d},
+	{0x03, 0xef, 0xbd, 0x1c},
+	{0x03, 0xef, 0xbd, 0x02},
+	{0x03, 0xef, 0xbd, 0x1f},
+	{0x03, 0xef, 0xbd, 0x1d},
+	{0x03, 0xef, 0xbd, 0x17},
+	{0x03, 0xef, 0xbd, 0x08},
+	{0x03, 0xef, 0xbd, 0x09},
+	{0x03, 0xef, 0xbd, 0x0e},
+	{0x03, 0xef, 0xbd, 0x04},
+	{0x03, 0xef, 0xbd, 0x05},
+	{0x03, 0xef, 0xbe, 0x3f},
+	{0x03, 0xef, 0xbe, 0x00},
+	{0x03, 0xef, 0xbd, 0x2c},
+	{0x03, 0xef, 0xbe, 0x06},
+	{0x03, 0xef, 0xbe, 0x0c},
+	{0x03, 0xef, 0xbe, 0x0f},
+	{0x03, 0xef, 0xbe, 0x0d},
+	{0x03, 0xef, 0xbe, 0x0b},
+	{0x03, 0xef, 0xbe, 0x19},
+	{0x03, 0xef, 0xbe, 0x15},
+	{0x03, 0xef, 0xbe, 0x11},
+	{0x03, 0xef, 0xbe, 0x31},
+	{0x03, 0xef, 0xbe, 0x33},
+	{0x03, 0xef, 0xbd, 0x0f},
+	{0x03, 0xef, 0xbe, 0x30},
+	{0x03, 0xef, 0xbe, 0x3e},
+	{0x03, 0xef, 0xbe, 0x32},
+	{0x03, 0xef, 0xbe, 0x36},
+	{0x03, 0xef, 0xbd, 0x14},
+	{0x03, 0xef, 0xbe, 0x2e},
+	{0x03, 0xef, 0xbd, 0x1e},
+	{0x03, 0xef, 0xbe, 0x10},
+	{0x03, 0xef, 0xbf, 0x13},
+	{0x03, 0xef, 0xbf, 0x15},
+	{0x03, 0xef, 0xbf, 0x17},
+	{0x03, 0xef, 0xbf, 0x1f},
+	{0x03, 0xef, 0xbf, 0x1d},
+	{0x03, 0xef, 0xbf, 0x1b},
+	{0x03, 0xef, 0xbf, 0x09},
+	{0x03, 0xef, 0xbf, 0x0b},
+	{0x03, 0xef, 0xbf, 0x37},
+	{0x03, 0xef, 0xbe, 0x04},
+	{0x01, 0xe0, 0x00, 0x00},
+	{0x03, 0xe2, 0xa6, 0x1a},
+	{0x03, 0xe2, 0xa6, 0x26},
+	{0x03, 0xe3, 0x80, 0x23},
+	{0x03, 0xe3, 0x80, 0x2e},
+	{0x03, 0xe3, 0x80, 0x25},
+	{0x03, 0xe3, 0x83, 0x1e},
+	{0x03, 0xe3, 0x83, 0x14},
+	{0x03, 0xe3, 0x82, 0x06},
+	{0x03, 0xe3, 0x82, 0x0b},
+	{0x03, 0xe3, 0x82, 0x0c},
+	{0x03, 0xe3, 0x82, 0x0d},
+	{0x03, 0xe3, 0x82, 0x02},
+	{0x03, 0xe3, 0x83, 0x0f},
+	{0x03, 0xe3, 0x83, 0x08},
+	{0x03, 0xe3, 0x83, 0x09},
+	{0x03, 0xe3, 0x83, 0x2c},
+	{0x03, 0xe3, 0x83, 0x0c},
+	{0x03, 0xe3, 0x82, 0x13},
+	{0x03, 0xe3, 0x82, 0x16},
+	{0x03, 0xe3, 0x82, 0x15},
+	{0x03, 0xe3, 0x82, 0x1c},
+	{0x03, 0xe3, 0x82, 0x1f},
+	{0x03, 0xe3, 0x82, 0x1d},
+	{0x03, 0xe3, 0x82, 0x1a},
+	{0x03, 0xe3, 0x82, 0x17},
+	{0x03, 0xe3, 0x82, 0x08},
+	{0x03, 0xe3, 0x82, 0x09},
+	{0x03, 0xe3, 0x82, 0x0e},
+	{0x03, 0xe3, 0x82, 0x04},
+	{0x03, 0xe3, 0x82, 0x05},
+	{0x03, 0xe3, 0x82, 0x3f},
+	{0x03, 0xe3, 0x83, 0x00},
+	{0x03, 0xe3, 0x83, 0x06},
+	{0x03, 0xe3, 0x83, 0x05},
+	{0x03, 0xe3, 0x83, 0x0d},
+	{0x03, 0xe3, 0x83, 0x0b},
+	{0x03, 0xe3, 0x83, 0x07},
+	{0x03, 0xe3, 0x83, 0x19},
+	{0x03, 0xe3, 0x83, 0x15},
+	{0x03, 0xe3, 0x83, 0x11},
+	{0x03, 0xe3, 0x83, 0x31},
+	{0x03, 0xe3, 0x83, 0x33},
+	{0x03, 0xe3, 0x83, 0x30},
+	{0x03, 0xe3, 0x83, 0x3e},
+	{0x03, 0xe3, 0x83, 0x32},
+	{0x03, 0xe3, 0x83, 0x36},
+	{0x03, 0xe3, 0x83, 0x2e},
+	{0x03, 0xe3, 0x82, 0x07},
+	{0x03, 0xe3, 0x85, 0x04},
+	{0x03, 0xe3, 0x84, 0x10},
+	{0x03, 0xe3, 0x85, 0x30},
+	{0x03, 0xe3, 0x85, 0x0d},
+	{0x03, 0xe3, 0x85, 0x13},
+	{0x03, 0xe3, 0x85, 0x15},
+	{0x03, 0xe3, 0x85, 0x17},
+	{0x03, 0xe3, 0x85, 0x1f},
+	{0x03, 0xe3, 0x85, 0x1d},
+	{0x03, 0xe3, 0x85, 0x1b},
+	{0x03, 0xe3, 0x85, 0x09},
+	{0x03, 0xe3, 0x85, 0x0f},
+	{0x03, 0xe3, 0x85, 0x0b},
+	{0x03, 0xe3, 0x85, 0x37},
+	{0x03, 0xe3, 0x85, 0x3b},
+	{0x03, 0xe3, 0x85, 0x39},
+	{0x03, 0xe3, 0x85, 0x3f},
+	{0x02, 0xc2, 0x02, 0x00},
+	{0x02, 0xc2, 0x0e, 0x00},
+	{0x02, 0xc2, 0x0c, 0x00},
+	{0x02, 0xc2, 0x00, 0x00},
+	{0x03, 0xe2, 0x82, 0x0f},
+	{0x03, 0xe2, 0x94, 0x2a},
+	{0x03, 0xe2, 0x86, 0x39},
+	{0x03, 0xe2, 0x86, 0x3b},
+	{0x03, 0xe2, 0x86, 0x3f},
+	{0x03, 0xe2, 0x96, 0x0d},
+	{0x03, 0xe2, 0x97, 0x25},
+}
+
+// Total table size 14680 bytes (14KiB)
diff --git a/src/vendor/golang_org/x/text/width/transform.go b/src/vendor/golang_org/x/text/width/transform.go
new file mode 100644
index 0000000..04f1a3f
--- /dev/null
+++ b/src/vendor/golang_org/x/text/width/transform.go
@@ -0,0 +1,239 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package width
+
+import (
+	"unicode/utf8"
+
+	"golang_org/x/text/transform"
+)
+
+type foldTransform struct {
+	transform.NopResetter
+}
+
+func (foldTransform) Span(src []byte, atEOF bool) (n int, err error) {
+	for n < len(src) {
+		if src[n] < utf8.RuneSelf {
+			// ASCII fast path.
+			for n++; n < len(src) && src[n] < utf8.RuneSelf; n++ {
+			}
+			continue
+		}
+		v, size := trie.lookup(src[n:])
+		if size == 0 { // incomplete UTF-8 encoding
+			if !atEOF {
+				err = transform.ErrShortSrc
+			} else {
+				n = len(src)
+			}
+			break
+		}
+		if elem(v)&tagNeedsFold != 0 {
+			err = transform.ErrEndOfSpan
+			break
+		}
+		n += size
+	}
+	return n, err
+}
+
+func (foldTransform) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+	for nSrc < len(src) {
+		if src[nSrc] < utf8.RuneSelf {
+			// ASCII fast path.
+			start, end := nSrc, len(src)
+			if d := len(dst) - nDst; d < end-start {
+				end = nSrc + d
+			}
+			for nSrc++; nSrc < end && src[nSrc] < utf8.RuneSelf; nSrc++ {
+			}
+			n := copy(dst[nDst:], src[start:nSrc])
+			if nDst += n; nDst == len(dst) {
+				nSrc = start + n
+				if nSrc == len(src) {
+					return nDst, nSrc, nil
+				}
+				if src[nSrc] < utf8.RuneSelf {
+					return nDst, nSrc, transform.ErrShortDst
+				}
+			}
+			continue
+		}
+		v, size := trie.lookup(src[nSrc:])
+		if size == 0 { // incomplete UTF-8 encoding
+			if !atEOF {
+				return nDst, nSrc, transform.ErrShortSrc
+			}
+			size = 1 // gobble 1 byte
+		}
+		if elem(v)&tagNeedsFold == 0 {
+			if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
+				return nDst, nSrc, transform.ErrShortDst
+			}
+			nDst += size
+		} else {
+			data := inverseData[byte(v)]
+			if len(dst)-nDst < int(data[0]) {
+				return nDst, nSrc, transform.ErrShortDst
+			}
+			i := 1
+			for end := int(data[0]); i < end; i++ {
+				dst[nDst] = data[i]
+				nDst++
+			}
+			dst[nDst] = data[i] ^ src[nSrc+size-1]
+			nDst++
+		}
+		nSrc += size
+	}
+	return nDst, nSrc, nil
+}
+
+type narrowTransform struct {
+	transform.NopResetter
+}
+
+func (narrowTransform) Span(src []byte, atEOF bool) (n int, err error) {
+	for n < len(src) {
+		if src[n] < utf8.RuneSelf {
+			// ASCII fast path.
+			for n++; n < len(src) && src[n] < utf8.RuneSelf; n++ {
+			}
+			continue
+		}
+		v, size := trie.lookup(src[n:])
+		if size == 0 { // incomplete UTF-8 encoding
+			if !atEOF {
+				err = transform.ErrShortSrc
+			} else {
+				n = len(src)
+			}
+			break
+		}
+		if k := elem(v).kind(); byte(v) == 0 || k != EastAsianFullwidth && k != EastAsianWide && k != EastAsianAmbiguous {
+		} else {
+			err = transform.ErrEndOfSpan
+			break
+		}
+		n += size
+	}
+	return n, err
+}
+
+func (narrowTransform) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+	for nSrc < len(src) {
+		if src[nSrc] < utf8.RuneSelf {
+			// ASCII fast path.
+			start, end := nSrc, len(src)
+			if d := len(dst) - nDst; d < end-start {
+				end = nSrc + d
+			}
+			for nSrc++; nSrc < end && src[nSrc] < utf8.RuneSelf; nSrc++ {
+			}
+			n := copy(dst[nDst:], src[start:nSrc])
+			if nDst += n; nDst == len(dst) {
+				nSrc = start + n
+				if nSrc == len(src) {
+					return nDst, nSrc, nil
+				}
+				if src[nSrc] < utf8.RuneSelf {
+					return nDst, nSrc, transform.ErrShortDst
+				}
+			}
+			continue
+		}
+		v, size := trie.lookup(src[nSrc:])
+		if size == 0 { // incomplete UTF-8 encoding
+			if !atEOF {
+				return nDst, nSrc, transform.ErrShortSrc
+			}
+			size = 1 // gobble 1 byte
+		}
+		if k := elem(v).kind(); byte(v) == 0 || k != EastAsianFullwidth && k != EastAsianWide && k != EastAsianAmbiguous {
+			if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
+				return nDst, nSrc, transform.ErrShortDst
+			}
+			nDst += size
+		} else {
+			data := inverseData[byte(v)]
+			if len(dst)-nDst < int(data[0]) {
+				return nDst, nSrc, transform.ErrShortDst
+			}
+			i := 1
+			for end := int(data[0]); i < end; i++ {
+				dst[nDst] = data[i]
+				nDst++
+			}
+			dst[nDst] = data[i] ^ src[nSrc+size-1]
+			nDst++
+		}
+		nSrc += size
+	}
+	return nDst, nSrc, nil
+}
+
+type wideTransform struct {
+	transform.NopResetter
+}
+
+func (wideTransform) Span(src []byte, atEOF bool) (n int, err error) {
+	for n < len(src) {
+		// TODO: Consider ASCII fast path. Special-casing ASCII handling can
+		// reduce the ns/op of BenchmarkWideASCII by about 30%. This is probably
+		// not enough to warrant the extra code and complexity.
+		v, size := trie.lookup(src[n:])
+		if size == 0 { // incomplete UTF-8 encoding
+			if !atEOF {
+				err = transform.ErrShortSrc
+			} else {
+				n = len(src)
+			}
+			break
+		}
+		if k := elem(v).kind(); byte(v) == 0 || k != EastAsianHalfwidth && k != EastAsianNarrow {
+		} else {
+			err = transform.ErrEndOfSpan
+			break
+		}
+		n += size
+	}
+	return n, err
+}
+
+func (wideTransform) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+	for nSrc < len(src) {
+		// TODO: Consider ASCII fast path. Special-casing ASCII handling can
+		// reduce the ns/op of BenchmarkWideASCII by about 30%. This is probably
+		// not enough to warrant the extra code and complexity.
+		v, size := trie.lookup(src[nSrc:])
+		if size == 0 { // incomplete UTF-8 encoding
+			if !atEOF {
+				return nDst, nSrc, transform.ErrShortSrc
+			}
+			size = 1 // gobble 1 byte
+		}
+		if k := elem(v).kind(); byte(v) == 0 || k != EastAsianHalfwidth && k != EastAsianNarrow {
+			if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
+				return nDst, nSrc, transform.ErrShortDst
+			}
+			nDst += size
+		} else {
+			data := inverseData[byte(v)]
+			if len(dst)-nDst < int(data[0]) {
+				return nDst, nSrc, transform.ErrShortDst
+			}
+			i := 1
+			for end := int(data[0]); i < end; i++ {
+				dst[nDst] = data[i]
+				nDst++
+			}
+			dst[nDst] = data[i] ^ src[nSrc+size-1]
+			nDst++
+		}
+		nSrc += size
+	}
+	return nDst, nSrc, nil
+}
diff --git a/src/vendor/golang_org/x/text/width/trieval.go b/src/vendor/golang_org/x/text/width/trieval.go
new file mode 100644
index 0000000..0ecffb4
--- /dev/null
+++ b/src/vendor/golang_org/x/text/width/trieval.go
@@ -0,0 +1,30 @@
+// This file was generated by go generate; DO NOT EDIT
+
+package width
+
+// elem is an entry of the width trie. The high byte is used to encode the type
+// of the rune. The low byte is used to store the index to a mapping entry in
+// the inverseData array.
+type elem uint16
+
+const (
+	tagNeutral elem = iota << typeShift
+	tagAmbiguous
+	tagWide
+	tagNarrow
+	tagFullwidth
+	tagHalfwidth
+)
+
+const (
+	numTypeBits = 3
+	typeShift   = 16 - numTypeBits
+
+	// tagNeedsFold is true for all fullwidth and halfwidth runes except for
+	// the Won sign U+20A9.
+	tagNeedsFold = 0x1000
+
+	// The Korean Won sign is halfwidth, but SHOULD NOT be mapped to a wide
+	// variant.
+	wonSign rune = 0x20A9
+)
diff --git a/src/vendor/golang_org/x/text/width/width.go b/src/vendor/golang_org/x/text/width/width.go
new file mode 100644
index 0000000..27f55b8
--- /dev/null
+++ b/src/vendor/golang_org/x/text/width/width.go
@@ -0,0 +1,206 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:generate stringer -type=Kind
+//go:generate go run gen.go gen_common.go gen_trieval.go
+
+// Package width provides functionality for handling different widths in text.
+//
+// Wide characters behave like ideographs; they tend to allow line breaks after
+// each character and remain upright in vertical text layout. Narrow characters
+// are kept together in words or runs that are rotated sideways in vertical text
+// layout.
+//
+// For more information, see http://unicode.org/reports/tr11/.
+package width // import "golang_org/x/text/width"
+
+import (
+	"unicode/utf8"
+
+	"golang_org/x/text/transform"
+)
+
+// TODO
+// 1) Reduce table size by compressing blocks.
+// 2) API proposition for computing display length
+//    (approximation, fixed pitch only).
+// 3) Implement display length.
+
+// Kind indicates the type of width property as defined in http://unicode.org/reports/tr11/.
+type Kind int
+
+const (
+	// Neutral characters do not occur in legacy East Asian character sets.
+	Neutral Kind = iota
+
+	// EastAsianAmbiguous characters that can be sometimes wide and sometimes
+	// narrow and require additional information not contained in the character
+	// code to further resolve their width.
+	EastAsianAmbiguous
+
+	// EastAsianWide characters are wide in its usual form. They occur only in
+	// the context of East Asian typography. These runes may have explicit
+	// halfwidth counterparts.
+	EastAsianWide
+
+	// EastAsianNarrow characters are narrow in its usual form. They often have
+	// fullwidth counterparts.
+	EastAsianNarrow
+
+	// Note: there exist Narrow runes that do not have fullwidth or wide
+	// counterparts, despite what the definition says (e.g. U+27E6).
+
+	// EastAsianFullwidth characters have a compatibility decompositions of type
+	// wide that map to a narrow counterpart.
+	EastAsianFullwidth
+
+	// EastAsianHalfwidth characters have a compatibility decomposition of type
+	// narrow that map to a wide or ambiguous counterpart, plus U+20A9 ₩ WON
+	// SIGN.
+	EastAsianHalfwidth
+
+	// Note: there exist runes that have a halfwidth counterparts but that are
+	// classified as Ambiguous, rather than wide (e.g. U+2190).
+)
+
+// TODO: the generated tries need to return size 1 for invalid runes for the
+// width to be computed correctly (each byte should render width 1)
+
+var trie = newWidthTrie(0)
+
+// Lookup reports the Properties of the first rune in b and the number of bytes
+// of its UTF-8 encoding.
+func Lookup(b []byte) (p Properties, size int) {
+	v, sz := trie.lookup(b)
+	return Properties{elem(v), b[sz-1]}, sz
+}
+
+// LookupString reports the Properties of the first rune in s and the number of
+// bytes of its UTF-8 encoding.
+func LookupString(s string) (p Properties, size int) {
+	v, sz := trie.lookupString(s)
+	return Properties{elem(v), s[sz-1]}, sz
+}
+
+// LookupRune reports the Properties of rune r.
+func LookupRune(r rune) Properties {
+	var buf [4]byte
+	n := utf8.EncodeRune(buf[:], r)
+	v, _ := trie.lookup(buf[:n])
+	last := byte(r)
+	if r >= utf8.RuneSelf {
+		last = 0x80 + byte(r&0x3f)
+	}
+	return Properties{elem(v), last}
+}
+
+// Properties provides access to width properties of a rune.
+type Properties struct {
+	elem elem
+	last byte
+}
+
+func (e elem) kind() Kind {
+	return Kind(e >> typeShift)
+}
+
+// Kind returns the Kind of a rune as defined in Unicode TR #11.
+// See http://unicode.org/reports/tr11/ for more details.
+func (p Properties) Kind() Kind {
+	return p.elem.kind()
+}
+
+// Folded returns the folded variant of a rune or 0 if the rune is canonical.
+func (p Properties) Folded() rune {
+	if p.elem&tagNeedsFold != 0 {
+		buf := inverseData[byte(p.elem)]
+		buf[buf[0]] ^= p.last
+		r, _ := utf8.DecodeRune(buf[1 : 1+buf[0]])
+		return r
+	}
+	return 0
+}
+
+// Narrow returns the narrow variant of a rune or 0 if the rune is already
+// narrow or doesn't have a narrow variant.
+func (p Properties) Narrow() rune {
+	if k := p.elem.kind(); byte(p.elem) != 0 && (k == EastAsianFullwidth || k == EastAsianWide || k == EastAsianAmbiguous) {
+		buf := inverseData[byte(p.elem)]
+		buf[buf[0]] ^= p.last
+		r, _ := utf8.DecodeRune(buf[1 : 1+buf[0]])
+		return r
+	}
+	return 0
+}
+
+// Wide returns the wide variant of a rune or 0 if the rune is already
+// wide or doesn't have a wide variant.
+func (p Properties) Wide() rune {
+	if k := p.elem.kind(); byte(p.elem) != 0 && (k == EastAsianHalfwidth || k == EastAsianNarrow) {
+		buf := inverseData[byte(p.elem)]
+		buf[buf[0]] ^= p.last
+		r, _ := utf8.DecodeRune(buf[1 : 1+buf[0]])
+		return r
+	}
+	return 0
+}
+
+// TODO for Properties:
+// - Add Fullwidth/Halfwidth or Inverted methods for computing variants
+// mapping.
+// - Add width information (including information on non-spacing runes).
+
+// Transformer implements the transform.Transformer interface.
+type Transformer struct {
+	t transform.SpanningTransformer
+}
+
+// Reset implements the transform.Transformer interface.
+func (t Transformer) Reset() { t.t.Reset() }
+
+// Transform implements the transform.Transformer interface.
+func (t Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
+	return t.t.Transform(dst, src, atEOF)
+}
+
+// Span implements the transform.SpanningTransformer interface.
+func (t Transformer) Span(src []byte, atEOF bool) (n int, err error) {
+	return t.t.Span(src, atEOF)
+}
+
+// Bytes returns a new byte slice with the result of applying t to b.
+func (t Transformer) Bytes(b []byte) []byte {
+	b, _, _ = transform.Bytes(t, b)
+	return b
+}
+
+// String returns a string with the result of applying t to s.
+func (t Transformer) String(s string) string {
+	s, _, _ = transform.String(t, s)
+	return s
+}
+
+var (
+	// Fold is a transform that maps all runes to their canonical width.
+	//
+	// Note that the NFKC and NFKD transforms in golang.org/x/text/unicode/norm
+	// provide a more generic folding mechanism.
+	Fold Transformer = Transformer{foldTransform{}}
+
+	// Widen is a transform that maps runes to their wide variant, if
+	// available.
+	Widen Transformer = Transformer{wideTransform{}}
+
+	// Narrow is a transform that maps runes to their narrow variant, if
+	// available.
+	Narrow Transformer = Transformer{narrowTransform{}}
+)
+
+// TODO: Consider the following options:
+// - Treat Ambiguous runes that have a halfwidth counterpart as wide, or some
+//   generalized variant of this.
+// - Consider a wide Won character to be the default width (or some generalized
+//   variant of this).
+// - Filter the set of characters that gets converted (the preferred approach is
+//   to allow applying filters to transforms).
diff --git a/test/bugs/bug395.go b/test/bugs/bug395.go
deleted file mode 100644
index 4fe81e0..0000000
--- a/test/bugs/bug395.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// skip
-
-// When issue 1909 is fixed, change from skip to compile.
-
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Issue 1909
-// Would OOM due to exponential recursion on Foo's expanded methodset in nodefmt
-
-package test
-
-type Foo interface {
-	Bar() interface {
-		Foo
-	}
-	Baz() interface {
-		Foo
-	}
-	Bug() interface {
-		Foo
-	}
-}
diff --git a/test/bugs/placeholder b/test/bugs/placeholder
deleted file mode 100644
index b816d34..0000000
--- a/test/bugs/placeholder
+++ /dev/null
@@ -1,2 +0,0 @@
-This file keeps Mercurial from deleting the directory
-when there are no known bugs.
diff --git a/test/checkbce.go b/test/checkbce.go
index fa0ea12..59bd41b 100644
--- a/test/checkbce.go
+++ b/test/checkbce.go
@@ -21,7 +21,9 @@ func f1(a [256]int, i int) {
 	if 4 <= i && i < len(a) {
 		useInt(a[i])
 		useInt(a[i-1]) // ERROR "Found IsInBounds$"
-		useInt(a[i-4]) // ERROR "Found IsInBounds$"
+		// TODO: 'if 4 <= i && i < len(a)' gets rewritten to 'if uint(i - 4) < 256 - 4',
+		// which the bounds checker cannot yet use to infer that the next line doesn't need a bounds check.
+		useInt(a[i-4])
 	}
 }
 
diff --git a/test/const.go b/test/const.go
index 6c29336..f8e0a75 100644
--- a/test/const.go
+++ b/test/const.go
@@ -123,9 +123,44 @@ func floats() {
 	assert(f == f1e3, "f == f1e3")
 }
 
+func interfaces() {
+	var (
+		nilN interface{}
+		nilI *int
+		five = 5
+
+		_ = nil == interface{}(nil)
+		_ = interface{}(nil) == nil
+	)
+	ii := func(i1 interface{}, i2 interface{}) bool { return i1 == i2 }
+	ni := func(n interface{}, i int) bool { return n == i }
+	in := func(i int, n interface{}) bool { return i == n }
+	pi := func(p *int, i interface{}) bool { return p == i }
+	ip := func(i interface{}, p *int) bool { return i == p }
+
+	assert((interface{}(nil) == interface{}(nil)) == ii(nilN, nilN),
+		"for interface{}==interface{} compiler == runtime")
+
+	assert(((*int)(nil) == interface{}(nil)) == pi(nilI, nilN),
+		"for *int==interface{} compiler == runtime")
+	assert((interface{}(nil) == (*int)(nil)) == ip(nilN, nilI),
+		"for interface{}==*int compiler == runtime")
+
+	assert((&five == interface{}(nil)) == pi(&five, nilN),
+		"for interface{}==*int compiler == runtime")
+	assert((interface{}(nil) == &five) == ip(nilN, &five),
+		"for interface{}==*int compiler == runtime")
+
+	assert((5 == interface{}(5)) == ni(five, five),
+		"for int==interface{} compiler == runtime")
+	assert((interface{}(5) == 5) == in(five, five),
+		"for interface{}==int comipiler == runtime")
+}
+
 func main() {
 	ints()
 	floats()
+	interfaces()
 
 	assert(ctrue == true, "ctrue == true")
 	assert(cfalse == false, "cfalse == false")
diff --git a/test/convert2.go b/test/convert2.go
new file mode 100644
index 0000000..c500638
--- /dev/null
+++ b/test/convert2.go
@@ -0,0 +1,315 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test various valid and invalid struct assignments and conversions.
+// Does not compile.
+
+package main
+
+type I interface {
+	m()
+}
+
+// conversions between structs
+
+func _() {
+	type S struct{}
+	type T struct{}
+	var s S
+	var t T
+	var u struct{}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u
+	s = S(s)
+	s = S(t)
+	s = S(u)
+	t = u
+	t = T(u)
+}
+
+func _() {
+	type S struct{ x int }
+	type T struct {
+		x int "foo"
+	}
+	var s S
+	var t T
+	var u struct {
+		x int "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = S(s)
+	s = S(t)
+	s = S(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = T(u)
+}
+
+func _() {
+	type E struct{ x int }
+	type S struct{ x E }
+	type T struct {
+		x E "foo"
+	}
+	var s S
+	var t T
+	var u struct {
+		x E "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = S(s)
+	s = S(t)
+	s = S(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = T(u)
+}
+
+func _() {
+	type S struct {
+		x struct {
+			x int "foo"
+		}
+	}
+	type T struct {
+		x struct {
+			x int "bar"
+		} "foo"
+	}
+	var s S
+	var t T
+	var u struct {
+		x struct {
+			x int "bar"
+		} "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = S(s)
+	s = S(t)
+	s = S(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = T(u)
+}
+
+func _() {
+	type E1 struct {
+		x int "foo"
+	}
+	type E2 struct {
+		x int "bar"
+	}
+	type S struct{ x E1 }
+	type T struct {
+		x E2 "foo"
+	}
+	var s S
+	var t T
+	var u struct {
+		x E2 "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = S(s)
+	s = S(t) // ERROR "cannot convert"
+	s = S(u) // ERROR "cannot convert"
+	t = u    // ERROR "cannot use .* in assignment"
+	t = T(u)
+}
+
+func _() {
+	type E struct{ x int }
+	type S struct {
+		f func(struct {
+			x int "foo"
+		})
+	}
+	type T struct {
+		f func(struct {
+			x int "bar"
+		})
+	}
+	var s S
+	var t T
+	var u struct{ f func(E) }
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = S(s)
+	s = S(t)
+	s = S(u) // ERROR "cannot convert"
+	t = u    // ERROR "cannot use .* in assignment"
+	t = T(u) // ERROR "cannot convert"
+}
+
+// conversions between pointers to structs
+
+func _() {
+	type S struct{}
+	type T struct{}
+	var s *S
+	var t *T
+	var u *struct{}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = (*T)(u)
+}
+
+func _() {
+	type S struct{ x int }
+	type T struct {
+		x int "foo"
+	}
+	var s *S
+	var t *T
+	var u *struct {
+		x int "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = (*T)(u)
+}
+
+func _() {
+	type E struct{ x int }
+	type S struct{ x E }
+	type T struct {
+		x E "foo"
+	}
+	var s *S
+	var t *T
+	var u *struct {
+		x E "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = (*T)(u)
+}
+
+func _() {
+	type S struct {
+		x struct {
+			x int "foo"
+		}
+	}
+	type T struct {
+		x struct {
+			x int "bar"
+		} "foo"
+	}
+	var s *S
+	var t *T
+	var u *struct {
+		x struct {
+			x int "bar"
+		} "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u)
+	t = u // ERROR "cannot use .* in assignment"
+	t = (*T)(u)
+}
+
+func _() {
+	type E1 struct {
+		x int "foo"
+	}
+	type E2 struct {
+		x int "bar"
+	}
+	type S struct{ x E1 }
+	type T struct {
+		x E2 "foo"
+	}
+	var s *S
+	var t *T
+	var u *struct {
+		x E2 "bar"
+	}
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t) // ERROR "cannot convert"
+	s = (*S)(u) // ERROR "cannot convert"
+	t = u       // ERROR "cannot use .* in assignment"
+	t = (*T)(u)
+}
+
+func _() {
+	type E struct{ x int }
+	type S struct {
+		f func(struct {
+			x int "foo"
+		})
+	}
+	type T struct {
+		f func(struct {
+			x int "bar"
+		})
+	}
+	var s *S
+	var t *T
+	var u *struct{ f func(E) }
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u) // ERROR "cannot convert"
+	t = u       // ERROR "cannot use .* in assignment"
+	t = (*T)(u) // ERROR "cannot convert"
+}
+
+func _() {
+	type E struct{ x int }
+	type S struct {
+		f func(*struct {
+			x int "foo"
+		})
+	}
+	type T struct {
+		f func(*struct {
+			x int "bar"
+		})
+	}
+	var s *S
+	var t *T
+	var u *struct{ f func(E) }
+	s = s
+	s = t // ERROR "cannot use .* in assignment"
+	s = u // ERROR "cannot use .* in assignment"
+	s = (*S)(s)
+	s = (*S)(t)
+	s = (*S)(u) // ERROR "cannot convert"
+	t = u       // ERROR "cannot use .* in assignment"
+	t = (*T)(u) // ERROR "cannot convert"
+}
diff --git a/test/ddd1.go b/test/ddd1.go
index 7ea04f3..cf6a3a5 100644
--- a/test/ddd1.go
+++ b/test/ddd1.go
@@ -27,9 +27,9 @@ func tuple() (int, int, int) { return 1, 2, 3 }
 
 var (
 	_ = sum(tuple())
-	_ = sum(tuple()...) // ERROR "multiple-value|[.][.][.]"
+	_ = sum(tuple()...) // ERROR "multiple-value"
 	_ = sum3(tuple())
-	_ = sum3(tuple()...) // ERROR "multiple-value|[.][.][.]" "not enough"
+	_ = sum3(tuple()...) // ERROR "multiple-value" "not enough"
 )
 
 type T []T
@@ -59,4 +59,3 @@ func bad(args ...int) {
 	_ = [...]byte("foo") // ERROR "[.][.][.]"
 	_ = [...][...]int{{1,2,3},{4,5,6}}	// ERROR "[.][.][.]"
 }
-
diff --git a/test/escape_because.go b/test/escape_because.go
index f0bbd0b..7d349b7 100644
--- a/test/escape_because.go
+++ b/test/escape_because.go
@@ -30,22 +30,22 @@ func (p *pair) EqualParts() bool { // ERROR "\(\*pair\).EqualParts p does not es
 	return p != nil && (p.x == p.y || *p.x == *p.y)
 }
 
-func f1(p *int) { // ERROR "from \[3\]\*int literal \(array literal element\) at escape_because.go:34$" "from a \(assigned\) at escape_because.go:34$" "from a \(interface-converted\) at escape_because.go:35$" "from sink \(assigned to top level variable\) at escape_because.go:19$" "leaking param: p$"
+func f1(p *int) { // ERROR "from \[3\]\*int literal \(array literal element\) at escape_because.go:34$" "from a \(assigned\) at escape_because.go:34$" "from a \(interface-converted\) at escape_because.go:35$" "from sink \(assigned to top level variable\) at escape_because.go:35$" "leaking param: p$"
 	a := [3]*int{p, nil, nil}
-	sink = a // ERROR "a escapes to heap$" "from sink \(assigned to top level variable\) at escape_because.go:19$"
+	sink = a // ERROR "a escapes to heap$" "from sink \(assigned to top level variable\) at escape_because.go:35$"
 
 }
 
-func f2(q *int) { // ERROR "from &u \(address-of\) at escape_because.go:43$" "from &u \(interface-converted\) at escape_because.go:43$" "from pair literal \(struct literal element\) at escape_because.go:41$" "from s \(assigned\) at escape_because.go:40$" "from sink \(assigned to top level variable\) at escape_because.go:19$" "from t \(assigned\) at escape_because.go:41$" "from u \(assigned\) at escape_because.go:42$" "leaking param: q$"
+func f2(q *int) { // ERROR "from &u \(address-of\) at escape_because.go:43$" "from &u \(interface-converted\) at escape_because.go:43$" "from pair literal \(struct literal element\) at escape_because.go:41$" "from s \(assigned\) at escape_because.go:40$" "from sink \(assigned to top level variable\) at escape_because.go:43$" "from t \(assigned\) at escape_because.go:41$" "from u \(assigned\) at escape_because.go:42$" "leaking param: q$"
 	s := q
 	t := pair{s, nil}
 	u := t    // ERROR "moved to heap: u$"
-	sink = &u // ERROR "&u escapes to heap$" "from &u \(interface-converted\) at escape_because.go:43$" "from sink \(assigned to top level variable\) at escape_because.go:19$"
+	sink = &u // ERROR "&u escapes to heap$" "from &u \(interface-converted\) at escape_because.go:43$" "from sink \(assigned to top level variable\) at escape_because.go:43$"
 }
 
-func f3(r *int) interface{} { // ERROR "from \[\]\*int literal \(slice-literal-element\) at escape_because.go:47$" "from c \(assigned\) at escape_because.go:47$" "from c \(interface-converted\) at escape_because.go:48$" "from ~r1 \(return\) at escape_because.go:46$" "leaking param: r to result ~r1 level=-1$"
-	c := []*int{r} // ERROR "\[\]\*int literal escapes to heap$" "from c \(assigned\) at escape_because.go:47$" "from c \(interface-converted\) at escape_because.go:48$" "from ~r1 \(return\) at escape_because.go:46$"
-	return c       // "return" // ERROR "c escapes to heap$" "from ~r1 \(return\) at escape_because.go:46$"
+func f3(r *int) interface{} { // ERROR "from \[\]\*int literal \(slice-literal-element\) at escape_because.go:47$" "from c \(assigned\) at escape_because.go:47$" "from c \(interface-converted\) at escape_because.go:48$" "from ~r1 \(return\) at escape_because.go:48$" "leaking param: r to result ~r1 level=-1$"
+	c := []*int{r} // ERROR "\[\]\*int literal escapes to heap$" "from c \(assigned\) at escape_because.go:47$" "from c \(interface-converted\) at escape_because.go:48$" "from ~r1 \(return\) at escape_because.go:48$"
+	return c       // "return" // ERROR "c escapes to heap$" "from ~r1 \(return\) at escape_because.go:48$"
 }
 
 func f4(a *int, s []*int) int { // ERROR "from \*s \(indirection\) at escape_because.go:51$" "from append\(s, a\) \(appended to slice\) at escape_because.go:52$" "from append\(s, a\) \(appendee slice\) at escape_because.go:52$" "leaking param content: s$" "leaking param: a$"
@@ -73,15 +73,15 @@ func f7(x map[int]*int, y int) *int { // ERROR "f7 x does not escape$"
 	return z
 }
 
-func f8(x int, y *int) *int { // ERROR "from ~r2 \(return\) at escape_because.go:76$" "from ~r2 \(returned from recursive function\) at escape_because.go:76$" "leaking param: y$" "moved to heap: x$"
+func f8(x int, y *int) *int { // ERROR "from ~r2 \(return\) at escape_because.go:78$" "from ~r2 \(returned from recursive function\) at escape_because.go:76$" "leaking param: y$" "moved to heap: x$"
 	if x <= 0 {
 		return y
 	}
 	x--
-	return f8(*y, &x) // ERROR "&x escapes to heap$" "from y \(arg to recursive call\) at escape_because.go:76$" "from ~r2 \(return\) at escape_because.go:76$" "from ~r2 \(returned from recursive function\) at escape_because.go:76$"
+	return f8(*y, &x) // ERROR "&x escapes to heap$" "from y \(arg to recursive call\) at escape_because.go:81$" "from ~r2 \(return\) at escape_because.go:78$" "from ~r2 \(returned from recursive function\) at escape_because.go:76$"
 }
 
-func f9(x int, y ...*int) *int { // ERROR "from y\[0\] \(dot of pointer\) at escape_because.go:86$" "from ~r2 \(return\) at escape_because.go:84$" "from ~r2 \(returned from recursive function\) at escape_because.go:84$" "leaking param content: y$" "leaking param: y to result ~r2 level=1$" "moved to heap: x$"
+func f9(x int, y ...*int) *int { // ERROR "from y\[0\] \(dot of pointer\) at escape_because.go:86$" "from ~r2 \(return\) at escape_because.go:86$" "from ~r2 \(returned from recursive function\) at escape_because.go:84$" "leaking param content: y$" "leaking param: y to result ~r2 level=1$" "moved to heap: x$"
 	if x <= 0 {
 		return y[0]
 	}
@@ -95,7 +95,27 @@ func f10(x map[*int]*int, y, z *int) *int { // ERROR "f10 x does not escape$" "f
 }
 
 func f11(x map[*int]*int, y, z *int) map[*int]*int { // ERROR "f11 x does not escape$" "from map\[\*int\]\*int literal \(map literal key\) at escape_because.go:98$" "from map\[\*int\]\*int literal \(map literal value\) at escape_because.go:98$" "leaking param: y$" "leaking param: z$"
-	return map[*int]*int{y: z} // ERROR "from ~r3 \(return\) at escape_because.go:97$" "map\[\*int\]\*int literal escapes to heap$"
+	return map[*int]*int{y: z} // ERROR "from ~r3 \(return\) at escape_because.go:98$" "map\[\*int\]\*int literal escapes to heap$"
+}
+
+func f12() {
+	b := []byte("test") // ERROR "\(\[\]byte\)\(.test.\) escapes to heap$" "from b \(assigned\) at escape_because.go:102$" "from b \(passed to call\[argument escapes\]\) at escape_because.go:103$"
+	escape(b)
+}
+
+func escape(b []byte) { // ERROR "from panic\(b\) \(panic\) at escape_because.go:107$" "leaking param: b$"
+	panic(b)
+}
+
+func f13() {
+	b := []byte("test") // ERROR "\(\[\]byte\)\(.test.\) escapes to heap$" "from .out0 \(passed-to-and-returned-from-call\) at escape_because.go:112$" "from b \(assigned\) at escape_because.go:111$" "from c \(assigned\) at escape_because.go:112$" "from c \(passed to call\[argument escapes\]\) at escape_because.go:113$"
+	c := transmit(b)
+	escape(c)
+}
+
+//go:noinline
+func transmit(b []byte) []byte { // ERROR "from ~r1 \(return\) at escape_because.go:118$" "leaking param: b to result ~r1 level=0$"
+	return b
 }
 
 // The list below is all of the why-escapes messages seen building the escape analysis tests.
@@ -142,9 +162,9 @@ key of map put
 map literal key
 map literal value
 parameter to indirect call
-passed to function[content escapes]
-passed to function[unknown]
-passed-to-and-returned-from-function
+passed to call[argument content escapes]
+passed to call[argument escapes]
+passed-to-and-returned-from-call
 pointer literal
 range
 range-deref
diff --git a/test/escape_iface.go b/test/escape_iface.go
index 50a5132..8a11d7e 100644
--- a/test/escape_iface.go
+++ b/test/escape_iface.go
@@ -226,22 +226,36 @@ func dotTypeEscape() *T2 { // #11931
 	}
 }
 
-func dotTypeEscape2() { // #13805
+func dotTypeEscape2() { // #13805, #15796
 	{
 		i := 0
+		j := 0
 		var v int
+		var ok bool
 		var x interface{} = i // ERROR "i does not escape"
+		var y interface{} = j // ERROR "j does not escape"
+
 		*(&v) = x.(int) // ERROR "&v does not escape"
+		*(&v), *(&ok) = y.(int) // ERROR "&v does not escape" "&ok does not escape"
 	}
 	{
 		i := 0
+		j := 0
+		var ok bool
 		var x interface{} = i // ERROR "i does not escape"
-		sink = x.(int)        // ERROR "x.\(int\) escapes to heap"
+		var y interface{} = j // ERROR "j does not escape"
 
+		sink = x.(int)        // ERROR "x.\(int\) escapes to heap"
+		sink, *(&ok) = y.(int)     // ERROR "&ok does not escape"
 	}
 	{
 		i := 0 // ERROR "moved to heap: i"
+		j := 0 // ERROR "moved to heap: j"
+		var ok bool
 		var x interface{} = &i // ERROR "&i escapes to heap"
+		var y interface{} = &j // ERROR "&j escapes to heap"
+
 		sink = x.(*int)        // ERROR "x.\(\*int\) escapes to heap"
+		sink, *(&ok) = y.(*int)     // ERROR "&ok does not escape"
 	}
 }
diff --git a/test/fixedbugs/bug255.go b/test/fixedbugs/bug255.go
index cc7d92f..247ca32 100644
--- a/test/fixedbugs/bug255.go
+++ b/test/fixedbugs/bug255.go
@@ -10,7 +10,7 @@ var a [10]int      // ok
 var b [1e1]int     // ok
 var c [1.5]int     // ERROR "truncated"
 var d ["abc"]int   // ERROR "invalid array bound|not numeric"
-var e [nil]int     // ERROR "invalid array bound|not numeric"
+var e [nil]int     // ERROR "use of untyped nil|invalid array bound|not numeric"
 var f [e]int       // ERROR "invalid array bound|not constant"
 var g [1 << 65]int // ERROR "array bound is too large|overflows"
 var h [len(a)]int  // ok
diff --git a/test/fixedbugs/bug332.go b/test/fixedbugs/bug332.go
index 91ae0b2..d43c2dd 100644
--- a/test/fixedbugs/bug332.go
+++ b/test/fixedbugs/bug332.go
@@ -13,5 +13,5 @@ func main() {}
 // issue 1474
 
 // important: no newline on end of next line.
-// 6g used to print <epoch> instead of bug332.go:111 
-func (t *T) F() {} // ERROR "bug332"
\ No newline at end of file
+// 6g used to print <epoch> instead of bug332.go:111
+func (t *T) F() {} // ERROR "undefined: T"
\ No newline at end of file
diff --git a/test/fixedbugs/bug376.go b/test/fixedbugs/bug376.go
index 7bef58b..cd70012 100644
--- a/test/fixedbugs/bug376.go
+++ b/test/fixedbugs/bug376.go
@@ -7,5 +7,4 @@
 // issue 1951
 package foo
 import "unsafe"
-var v = unsafe.Sizeof  // ERROR "must be called"
-
+var v = unsafe.Sizeof  // ERROR "not in function call|must be called"
diff --git a/test/fixedbugs/bug498.go b/test/fixedbugs/bug498.go
new file mode 100644
index 0000000..91b5c2f
--- /dev/null
+++ b/test/fixedbugs/bug498.go
@@ -0,0 +1,23 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Gccgo incorrectly rejected an assignment to multiple instances of
+// the same variable.
+
+package main
+
+var a int
+
+func F() {
+	a, a, a = 1, 2, 3
+}
+
+func main() {
+	F()
+	if a != 3 {
+		panic(a)
+	}
+}
diff --git a/test/fixedbugs/bug499.go b/test/fixedbugs/bug499.go
new file mode 100644
index 0000000..e4142e9
--- /dev/null
+++ b/test/fixedbugs/bug499.go
@@ -0,0 +1,15 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Gccgo got confused when a type was used both for a map bucket type
+// and for a map key type.
+
+package main
+
+func main() {
+	_ = make(map[byte]byte)
+	_ = make(map[[8]byte]chan struct{})
+}
diff --git a/test/fixedbugs/issue10607.go b/test/fixedbugs/issue10607.go
index bf527d0..52fb51a 100644
--- a/test/fixedbugs/issue10607.go
+++ b/test/fixedbugs/issue10607.go
@@ -1,4 +1,4 @@
-// +build linux,!ppc64,!ppc64le
+// +build linux,!mips,!mipsle,!ppc64
 // run
 
 // Copyright 2015 The Go Authors. All rights reserved.
diff --git a/test/fixedbugs/issue11370.go b/test/fixedbugs/issue11370.go
new file mode 100644
index 0000000..30f2904
--- /dev/null
+++ b/test/fixedbugs/issue11370.go
@@ -0,0 +1,13 @@
+// compile
+
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// issue 11370: cmd/compile: "0"[0] should not be a constant
+
+package p
+
+func main() {
+	println(-"abc"[1] >> 1)
+}
diff --git a/test/fixedbugs/issue11610.go b/test/fixedbugs/issue11610.go
index f32d480..5e77932 100644
--- a/test/fixedbugs/issue11610.go
+++ b/test/fixedbugs/issue11610.go
@@ -11,7 +11,7 @@ package a
 import""  // ERROR "import path is empty"
 var?      // ERROR "illegal character U\+003F '\?'"
 
-var x int // ERROR "unexpected var" "cannot declare name"
+var x int // ERROR "unexpected var"
 
 func main() {
 }
diff --git a/test/fixedbugs/issue13162.go b/test/fixedbugs/issue13162.go
new file mode 100644
index 0000000..f8b3150
--- /dev/null
+++ b/test/fixedbugs/issue13162.go
@@ -0,0 +1,82 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Ensure that range loops over a string have the requisite side-effects.
+
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func check(n int) {
+	var i int
+	var r rune
+
+	b := make([]byte, n)
+	for i = range b {
+		b[i] = byte(i + 1)
+	}
+	s := string(b)
+
+	// When n == 0, i is untouched by the range loop.
+	// Picking an initial value of -1 for i makes the
+	// "want" calculation below correct in all cases.
+	i = -1
+	for i = range s {
+		b[i] = s[i]
+	}
+	if want := n - 1; i != want {
+		fmt.Printf("index after range with side-effect = %d want %d\n", i, want)
+		os.Exit(1)
+	}
+
+	i = -1
+	r = '\x00'
+	for i, r = range s {
+		b[i] = byte(r)
+	}
+	if want := n - 1; i != want {
+		fmt.Printf("index after range with side-effect = %d want %d\n", i, want)
+		os.Exit(1)
+	}
+	if want := rune(n); r != want {
+		fmt.Printf("rune after range with side-effect = %q want %q\n", r, want)
+		os.Exit(1)
+	}
+
+	i = -1
+	// i is shadowed here, so its value should be unchanged.
+	for i := range s {
+		b[i] = s[i]
+	}
+	if want := -1; i != want {
+		fmt.Printf("index after range without side-effect = %d want %d\n", i, want)
+		os.Exit(1)
+	}
+
+	i = -1
+	r = -1
+	// i and r are shadowed here, so their values should be unchanged.
+	for i, r := range s {
+		b[i] = byte(r)
+	}
+	if want := -1; i != want {
+		fmt.Printf("index after range without side-effect = %d want %d\n", i, want)
+		os.Exit(1)
+	}
+	if want := rune(-1); r != want {
+		fmt.Printf("rune after range without side-effect = %q want %q\n", r, want)
+		os.Exit(1)
+	}
+}
+
+func main() {
+	check(0)
+	check(1)
+	check(15)
+}
diff --git a/test/fixedbugs/issue13171.go b/test/fixedbugs/issue13171.go
index 5d127a5..addb872 100644
--- a/test/fixedbugs/issue13171.go
+++ b/test/fixedbugs/issue13171.go
@@ -14,7 +14,7 @@ import "fmt"
 func f(x float64) float64 {
 	// y is allocated to X0
 	y := x + 5
-	// marshals z before y.  Marshalling z
+	// marshals z before y.  Marshaling z
 	// calls DUFFCOPY.
 	return g(z, y)
 }
diff --git a/test/fixedbugs/issue13262.go b/test/fixedbugs/issue13262.go
new file mode 100644
index 0000000..8837c00
--- /dev/null
+++ b/test/fixedbugs/issue13262.go
@@ -0,0 +1,21 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 13262: cmd/compile: bogus "fallthrough
+// statement out of place" error
+
+package p
+
+func f() int {
+	var a int
+	switch a {
+	case 0:
+		return func() int { return 1 }()
+		fallthrough
+	default:
+	}
+	return 0
+}
diff --git a/test/fixedbugs/issue13485.go b/test/fixedbugs/issue13485.go
new file mode 100644
index 0000000..a9beea1
--- /dev/null
+++ b/test/fixedbugs/issue13485.go
@@ -0,0 +1,18 @@
+// errorcheck
+
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+var (
+	_ [10]int
+	_ [10.0]int
+	_ [float64(10)]int                // ERROR "invalid array bound"
+	_ [10 + 0i]int
+	_ [complex(10, 0)]int
+	_ [complex128(complex(10, 0))]int // ERROR "invalid array bound"
+	_ ['a']int
+	_ [rune(65)]int
+)
diff --git a/test/fixedbugs/issue14136.go b/test/fixedbugs/issue14136.go
index 928a60b..f9efd05 100644
--- a/test/fixedbugs/issue14136.go
+++ b/test/fixedbugs/issue14136.go
@@ -14,6 +14,6 @@ package main
 type T struct{}
 
 func main() {
-	t := T{X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1} // ERROR "unknown T field"
+	t := T{X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1, X: 1} // ERROR "unknown field 'X' in struct literal of type T"
 	var s string = 1 // ERROR "cannot use 1"
 }
diff --git a/test/fixedbugs/issue15141.go b/test/fixedbugs/issue15141.go
new file mode 100644
index 0000000..752f530
--- /dev/null
+++ b/test/fixedbugs/issue15141.go
@@ -0,0 +1,33 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func main() {
+	a := f(1, 99)
+	b := g(0xFFFFFFe, 98)
+	c := h(0xFFFFFFe, 98)
+	println(a[1], b[1], c[1], a[0xFFFFFFe], b[0xFFFFFFe], c[0xFFFFFFe])
+}
+
+//go:noinline
+func f(i, y int) (a [0xFFFFFFF]byte) {
+	a[i] = byte(y)
+	return
+}
+
+//go:noinline
+func g(i, y int) [0xFFFFFFF]byte {
+	var a [0xFFFFFFF]byte
+	a[i] = byte(y)
+	return a
+}
+
+//go:noinline
+func h(i, y int) (a [0xFFFFFFF]byte) {
+	a[i] = byte(y)
+	return a
+}
diff --git a/test/fixedbugs/issue15277.go b/test/fixedbugs/issue15277.go
index 719c9a4..af165f7 100644
--- a/test/fixedbugs/issue15277.go
+++ b/test/fixedbugs/issue15277.go
@@ -15,6 +15,7 @@ func f(x *big, start int64) {
 	if delta := inuse() - start; delta < 9<<20 {
 		println("after alloc: expected delta at least 9MB, got: ", delta)
 	}
+	runtime.KeepAlive(x)
 	x = nil
 	if delta := inuse() - start; delta > 1<<20 {
 		println("after drop: expected delta below 1MB, got: ", delta)
@@ -23,6 +24,7 @@ func f(x *big, start int64) {
 	if delta := inuse() - start; delta < 9<<20 {
 		println("second alloc: expected delta at least 9MB, got: ", delta)
 	}
+	runtime.KeepAlive(x)
 }
 
 func main() {
diff --git a/test/fixedbugs/issue15303.go b/test/fixedbugs/issue15303.go
new file mode 100644
index 0000000..c8dfa30
--- /dev/null
+++ b/test/fixedbugs/issue15303.go
@@ -0,0 +1,24 @@
+// run
+
+// Copyright 2016 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Ensure that inlined struct/array comparisons have the right side-effects.
+
+package main
+
+import "os"
+
+func main() {
+	var x int
+	f := func() (r [4]int) {
+		x++
+		return
+	}
+	_ = f() == f()
+	if x != 2 {
+		println("f evaluated ", x, " times, want 2")
+		os.Exit(1)
+	}
+}
diff --git a/test/fixedbugs/issue15514.dir/a.go b/test/fixedbugs/issue15514.dir/a.go
new file mode 100644
index 0000000..663303b
--- /dev/null
+++ b/test/fixedbugs/issue15514.dir/a.go
@@ -0,0 +1,7 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+type A struct{ _ int32 }
diff --git a/test/fixedbugs/issue15514.dir/b.go b/test/fixedbugs/issue15514.dir/b.go
new file mode 100644
index 0000000..f0750d3
--- /dev/null
+++ b/test/fixedbugs/issue15514.dir/b.go
@@ -0,0 +1,7 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package b
+
+func B() (_ struct{ _ int32 }) { return }
diff --git a/test/fixedbugs/issue15514.dir/c.go b/test/fixedbugs/issue15514.dir/c.go
new file mode 100644
index 0000000..11624f9
--- /dev/null
+++ b/test/fixedbugs/issue15514.dir/c.go
@@ -0,0 +1,10 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package c
+
+import "./a"
+import "./b"
+
+var _ a.A = b.B() // ERROR "cannot use b\.B"
diff --git a/test/fixedbugs/issue15514.go b/test/fixedbugs/issue15514.go
new file mode 100644
index 0000000..626f7ad
--- /dev/null
+++ b/test/fixedbugs/issue15514.go
@@ -0,0 +1,7 @@
+// errorcheckdir
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ignored
diff --git a/test/fixedbugs/issue15528.go b/test/fixedbugs/issue15528.go
new file mode 100644
index 0000000..b1f9dfb
--- /dev/null
+++ b/test/fixedbugs/issue15528.go
@@ -0,0 +1,131 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"reflect"
+	"unsafe"
+)
+
+type RWS struct{}
+
+func (x *RWS) Read(p []byte) (n int, err error)                   { return }
+func (x *RWS) Write(p []byte) (n int, err error)                  { return }
+func (x *RWS) Seek(offset int64, whence int) (n int64, err error) { return }
+func (x *RWS) String() string                                     { return "rws" }
+
+func makeRWS() io.ReadWriteSeeker { return &RWS{} }
+func makeStringer() fmt.Stringer  { return &RWS{} }
+
+// Test correct construction of static empty interface values
+var efaces = [...]struct {
+	x interface{}
+	s string
+}{
+	{nil, "<nil> <nil>"},
+	{1, "int 1"},
+	{int(1), "int 1"},
+	{Int(int(2)), "main.Int Int=2"},
+	{int(Int(3)), "int 3"},
+	{[1]int{2}, "[1]int [2]"},
+	{io.Reader(io.ReadWriter(io.ReadWriteSeeker(nil))), "<nil> <nil>"},
+	{io.Reader(io.ReadWriter(io.ReadWriteSeeker(&RWS{}))), "*main.RWS rws"},
+	{makeRWS(), "*main.RWS rws"},
+	{map[string]string{"here": "there"}, "map[string]string map[here:there]"},
+	{chan bool(nil), "chan bool <nil>"},
+	{unsafe.Pointer(uintptr(0)), "unsafe.Pointer <nil>"},
+	{(*byte)(nil), "*uint8 <nil>"},
+	{io.Writer((*os.File)(nil)), "*os.File <nil>"},
+	{(interface{})(io.Writer((*os.File)(nil))), "*os.File <nil>"},
+	{fmt.Stringer(Strunger(((*Int)(nil)))), "*main.Int <nil>"},
+}
+
+type Int int
+
+func (i Int) String() string { return fmt.Sprintf("Int=%d", i) }
+func (i Int) Strung()        {}
+
+type Strunger interface {
+	fmt.Stringer
+	Strung()
+}
+
+// Test correct construction of static non-empty interface values
+var ifaces = [...]struct {
+	x fmt.Stringer
+	s string
+}{
+	{nil, "<nil> <nil> %!s(<nil>)"},
+	{Int(3), "main.Int 3 Int=3"},
+	{Int(int(Int(4))), "main.Int 4 Int=4"},
+	{Strunger(Int(5)), "main.Int 5 Int=5"},
+	{makeStringer(), "*main.RWS &main.RWS{} rws"},
+	{fmt.Stringer(nil), "<nil> <nil> %!s(<nil>)"},
+	{(*RWS)(nil), "*main.RWS (*main.RWS)(nil) rws"},
+}
+
+// Test correct handling of direct interface values
+var (
+	one  int         = 1
+	iptr interface{} = &one
+	clos int
+	f    interface{} = func() { clos++ }
+	deep interface{} = [1]struct{ a *[2]byte }{{a: &[2]byte{'z', 'w'}}}
+	ch   interface{} = make(chan bool, 1)
+)
+
+func main() {
+	var fail bool
+	for i, test := range efaces {
+		s := fmt.Sprintf("%[1]T %[1]v", test.x)
+		if s != test.s {
+			fmt.Printf("eface(%d)=%q want %q\n", i, s, test.s)
+			fail = true
+		}
+	}
+
+	for i, test := range ifaces {
+		s := fmt.Sprintf("%[1]T %#[1]v %[1]s", test.x)
+		if s != test.s {
+			fmt.Printf("iface(%d)=%q want %q\n", i, s, test.s)
+			fail = true
+		}
+	}
+
+	if got := *(iptr.(*int)); got != 1 {
+		fmt.Printf("bad int ptr %d\n", got)
+		fail = true
+	}
+
+	f.(func())()
+	f.(func())()
+	f.(func())()
+	if clos != 3 {
+		fmt.Printf("bad closure exec %d\n", clos)
+		fail = true
+	}
+
+	if !reflect.DeepEqual(*(deep.([1]struct{ a *[2]byte })[0].a), [2]byte{'z', 'w'}) {
+		fmt.Printf("bad deep directiface\n")
+		fail = true
+	}
+
+	cc := ch.(chan bool)
+	cc <- true
+	if got := <-cc; !got {
+		fmt.Printf("bad chan\n")
+		fail = true
+	}
+
+	if fail {
+		fmt.Println("BUG")
+		os.Exit(1)
+	}
+}
diff --git a/test/fixedbugs/issue15609.dir/call.go b/test/fixedbugs/issue15609.dir/call.go
new file mode 100644
index 0000000..41a489c
--- /dev/null
+++ b/test/fixedbugs/issue15609.dir/call.go
@@ -0,0 +1,7 @@
+// +build !amd64,!386
+
+package main
+
+func jump() {
+	target()
+}
diff --git a/test/fixedbugs/issue15609.dir/call_386.s b/test/fixedbugs/issue15609.dir/call_386.s
new file mode 100644
index 0000000..751084c
--- /dev/null
+++ b/test/fixedbugs/issue15609.dir/call_386.s
@@ -0,0 +1,8 @@
+#include "textflag.h"
+
+DATA ·pointer(SB)/4, $·target(SB)
+GLOBL ·pointer(SB),RODATA,$4
+
+TEXT ·jump(SB),NOSPLIT,$4
+        CALL *·pointer(SB)
+        RET
diff --git a/test/fixedbugs/issue15609.dir/call_amd64.s b/test/fixedbugs/issue15609.dir/call_amd64.s
new file mode 100644
index 0000000..09fbe5d
--- /dev/null
+++ b/test/fixedbugs/issue15609.dir/call_amd64.s
@@ -0,0 +1,8 @@
+#include "textflag.h"
+
+DATA ·pointer(SB)/8, $·target(SB)
+GLOBL ·pointer(SB),RODATA,$8
+
+TEXT ·jump(SB),NOSPLIT,$8
+        CALL *·pointer(SB)
+        RET
diff --git a/test/fixedbugs/issue15609.dir/call_decl.go b/test/fixedbugs/issue15609.dir/call_decl.go
new file mode 100644
index 0000000..d9c5a4e
--- /dev/null
+++ b/test/fixedbugs/issue15609.dir/call_decl.go
@@ -0,0 +1,5 @@
+// +build amd64 386
+
+package main
+
+func jump()
diff --git a/test/fixedbugs/issue15609.dir/main.go b/test/fixedbugs/issue15609.dir/main.go
new file mode 100644
index 0000000..4855e31
--- /dev/null
+++ b/test/fixedbugs/issue15609.dir/main.go
@@ -0,0 +1,14 @@
+package main
+
+var called bool
+
+func target() {
+	called = true
+}
+
+func main() {
+	jump()
+	if !called {
+		panic("target not called")
+	}
+}
diff --git a/test/fixedbugs/issue15722.go b/test/fixedbugs/issue15722.go
new file mode 100644
index 0000000..dec5458
--- /dev/null
+++ b/test/fixedbugs/issue15722.go
@@ -0,0 +1,21 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Checks to make sure that the compiler can catch a specific invalid
+// method type expression. NB: gccgo and gc have slightly different
+// error messages, hence the generic test for 'method' and not something
+// more specific.
+
+package issue15722
+
+type T int
+type P *T
+
+func (T) t() {}
+
+func _(p P) {
+	P.t(p) // ERROR "method"
+}
diff --git a/test/fixedbugs/issue15747.go b/test/fixedbugs/issue15747.go
index 34ec719..836d0ea 100644
--- a/test/fixedbugs/issue15747.go
+++ b/test/fixedbugs/issue15747.go
@@ -17,23 +17,24 @@ type T struct{ M string }
 
 var b bool
 
-func f1(q *Q, xx []byte) interface{} { // ERROR "live at entry to f1: q xx" "live at call to newobject: q xx" "live at call to writebarrierptr: q &xx"
+func f1(q *Q, xx []byte) interface{} { // ERROR "live at call to newobject: xx$" "live at call to writebarrierptr: &xx$" "live at entry to f1: xx$"
 	// xx was copied from the stack to the heap on the previous line:
 	// xx was live for the first two prints but then it switched to &xx
 	// being live. We should not see plain xx again.
 	if b {
-		global = &xx // ERROR "live at call to writebarrierptr: q &xx$"
+		global = &xx // ERROR "live at call to writebarrierptr: &xx$"
 	}
-	xx, _, err := f2(xx, 5) // ERROR "live at call to newobject: q( d)? &xx( odata.ptr)?" "live at call to writebarrierptr: q (e|err.data err.type)$"
+	xx, _, err := f2(xx, 5) // ERROR "live at call to f2: &xx$" "live at call to writebarrierptr: err.data err.type$"
 	if err != nil {
 		return err
 	}
 	return nil
 }
 
-func f2(d []byte, n int) (odata, res []byte, e interface{}) { // ERROR "live at entry to f2: d"
+//go:noinline
+func f2(d []byte, n int) (odata, res []byte, e interface{}) { // ERROR "live at entry to f2: d$"
 	if n > len(d) {
-		return d, nil, &T{M: "hello"} // ERROR "live at call to newobject: d"
+		return d, nil, &T{M: "hello"} // ERROR "live at call to newobject: d" "live at call to writebarrierptr: d"
 	}
 	res = d[:n]
 	odata = d[n:]
diff --git a/test/fixedbugs/issue15895.go b/test/fixedbugs/issue15895.go
new file mode 100644
index 0000000..3ef295c
--- /dev/null
+++ b/test/fixedbugs/issue15895.go
@@ -0,0 +1,27 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// func bad used to fail to compile.
+
+package p
+
+type A [1]int
+
+func bad(x A) {
+	switch x {
+	case A([1]int{1}):
+	case A([1]int{1}):
+	}
+}
+
+func good(x A) {
+	y := A([1]int{1})
+	z := A([1]int{1})
+	switch x {
+	case y:
+	case z:
+	}
+}
diff --git a/test/fixedbugs/issue16306.go b/test/fixedbugs/issue16306.go
new file mode 100644
index 0000000..d29a75a
--- /dev/null
+++ b/test/fixedbugs/issue16306.go
@@ -0,0 +1,15 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "unsafe"
+
+var x = unsafe.Pointer(uintptr(0))
+
+func main() {
+	_ = map[unsafe.Pointer]int{unsafe.Pointer(uintptr(0)): 0}
+}
diff --git a/test/fixedbugs/issue16317.dir/a.go b/test/fixedbugs/issue16317.dir/a.go
new file mode 100644
index 0000000..3a1b7e0
--- /dev/null
+++ b/test/fixedbugs/issue16317.dir/a.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+import "unsafe"
+
+func ConstUnsafePointer() unsafe.Pointer {
+	return unsafe.Pointer(uintptr(0))
+}
diff --git a/test/fixedbugs/issue16317.dir/b.go b/test/fixedbugs/issue16317.dir/b.go
new file mode 100644
index 0000000..b813918
--- /dev/null
+++ b/test/fixedbugs/issue16317.dir/b.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "./a"
+
+func main() {
+	_ = a.ConstUnsafePointer()
+}
diff --git a/test/fixedbugs/issue16317.go b/test/fixedbugs/issue16317.go
new file mode 100644
index 0000000..b3376bb
--- /dev/null
+++ b/test/fixedbugs/issue16317.go
@@ -0,0 +1,10 @@
+// compiledir
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 16317: cmd/compile: internal compiler error:
+//              unhandled OCONV INT -> TUNSAFEPTR
+
+package ignored
diff --git a/test/fixedbugs/issue16331.go b/test/fixedbugs/issue16331.go
new file mode 100644
index 0000000..665e7fc
--- /dev/null
+++ b/test/fixedbugs/issue16331.go
@@ -0,0 +1,48 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Perform tracebackdefers with a deferred reflection method.
+
+package main
+
+import "reflect"
+
+type T struct{}
+
+func (T) M() {
+}
+
+func F(args []reflect.Value) (results []reflect.Value) {
+	return nil
+}
+
+func main() {
+	done := make(chan bool)
+	go func() {
+		// Test reflect.makeFuncStub.
+		t := reflect.TypeOf((func())(nil))
+		f := reflect.MakeFunc(t, F).Interface().(func())
+		defer f()
+		growstack(10000)
+		done <- true
+	}()
+	<-done
+	go func() {
+		// Test reflect.methodValueCall.
+		f := reflect.ValueOf(T{}).Method(0).Interface().(func())
+		defer f()
+		growstack(10000)
+		done <- true
+	}()
+	<-done
+}
+
+func growstack(x int) {
+	if x == 0 {
+		return
+	}
+	growstack(x - 1)
+}
diff --git a/test/fixedbugs/issue16369.go b/test/fixedbugs/issue16369.go
new file mode 100644
index 0000000..bd03fbc
--- /dev/null
+++ b/test/fixedbugs/issue16369.go
@@ -0,0 +1,13 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type T interface {
+	M(interface {
+		T
+	}) // ERROR "cannot export unnamed recursive interface"
+}
diff --git a/test/fixedbugs/issue16428.go b/test/fixedbugs/issue16428.go
new file mode 100644
index 0000000..5696d18
--- /dev/null
+++ b/test/fixedbugs/issue16428.go
@@ -0,0 +1,12 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+var (
+	b = [...]byte("abc") // ERROR "outside of array literal"
+	s = len(b)
+)
diff --git a/test/fixedbugs/issue16439.go b/test/fixedbugs/issue16439.go
new file mode 100644
index 0000000..d321b60
--- /dev/null
+++ b/test/fixedbugs/issue16439.go
@@ -0,0 +1,18 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+var a []int = []int{1: 1}
+var b []int = []int{-1: 1} // ERROR "must be non-negative integer constant"
+
+var c []int = []int{2.0: 2}
+var d []int = []int{-2.0: 2} // ERROR "must be non-negative integer constant"
+
+var e []int = []int{3 + 0i: 3}
+var f []int = []int{3i: 3} // ERROR "truncated to real"
+
+var g []int = []int{"a": 4} // ERROR "must be non-negative integer constant"
diff --git a/test/fixedbugs/issue16616.dir/a.go b/test/fixedbugs/issue16616.dir/a.go
new file mode 100644
index 0000000..0ffdbbe
--- /dev/null
+++ b/test/fixedbugs/issue16616.dir/a.go
@@ -0,0 +1,7 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+type V struct{ i int }
diff --git a/test/fixedbugs/issue16616.dir/b.go b/test/fixedbugs/issue16616.dir/b.go
new file mode 100644
index 0000000..4f238b9
--- /dev/null
+++ b/test/fixedbugs/issue16616.dir/b.go
@@ -0,0 +1,14 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package b
+
+import "./a"
+
+var V struct{ i int }
+
+var U struct {
+	a.V
+	j int
+}
diff --git a/test/fixedbugs/issue16616.dir/issue16616.go b/test/fixedbugs/issue16616.dir/issue16616.go
new file mode 100644
index 0000000..0bfadb8
--- /dev/null
+++ b/test/fixedbugs/issue16616.dir/issue16616.go
@@ -0,0 +1,26 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"reflect"
+
+	_ "./a"
+	"./b"
+)
+
+var V struct{ i int }
+
+func main() {
+	if got := reflect.ValueOf(b.V).Type().Field(0).PkgPath; got != "b" {
+		panic(`PkgPath=` + got + ` for first field of b.V, want "b"`)
+	}
+	if got := reflect.ValueOf(V).Type().Field(0).PkgPath; got != "main" {
+		panic(`PkgPath=` + got + ` for first field of V, want "main"`)
+	}
+	if got := reflect.ValueOf(b.U).Type().Field(0).PkgPath; got != "b" {
+		panic(`PkgPath=` + got + ` for first field of b.U, want "b"`)
+	}
+}
diff --git a/test/fixedbugs/issue16616.go b/test/fixedbugs/issue16616.go
new file mode 100644
index 0000000..a7d6ac0
--- /dev/null
+++ b/test/fixedbugs/issue16616.go
@@ -0,0 +1,9 @@
+// compiledir
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Tests that unexported fields of unnamed types have different PkgPath values.
+
+package ignored
diff --git a/test/fixedbugs/issue16733.go b/test/fixedbugs/issue16733.go
new file mode 100644
index 0000000..850b042
--- /dev/null
+++ b/test/fixedbugs/issue16733.go
@@ -0,0 +1,16 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 16733: don't fold constant factors into a multiply
+// beyond the capacity of a MULQ instruction (32 bits).
+
+package p
+
+func f(n int64) int64 {
+	n *= 1000000
+	n *= 1000000
+	return n
+}
diff --git a/test/fixedbugs/issue16741.go b/test/fixedbugs/issue16741.go
new file mode 100644
index 0000000..9946062
--- /dev/null
+++ b/test/fixedbugs/issue16741.go
@@ -0,0 +1,17 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Make sure CSE of multi-output opcodes works correctly
+// with select0/1 operations.
+
+package main
+
+func div(d, r int64) int64 {
+	if m := d % r; m > 0 {
+		return d/r + 1
+	}
+	return d / r
+}
diff --git a/test/fixedbugs/issue16760.go b/test/fixedbugs/issue16760.go
new file mode 100644
index 0000000..d0e08b5
--- /dev/null
+++ b/test/fixedbugs/issue16760.go
@@ -0,0 +1,42 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Make sure we don't start marshaling (writing to the stack)
+// arguments until those arguments are evaluated and known
+// not to unconditinally panic. If they unconditionally panic,
+// we write some args but never do the call. That messes up
+// the logic which decides how big the argout section needs to be.
+
+package main
+
+type W interface {
+	Write([]byte)
+}
+
+type F func(W)
+
+func foo(f F) {
+	defer func() {
+		if r := recover(); r != nil {
+			usestack(1000)
+		}
+	}()
+	f(nil)
+}
+
+func main() {
+	foo(func(w W) {
+		var x []string
+		w.Write([]byte(x[5]))
+	})
+}
+
+func usestack(n int) {
+	if n == 0 {
+		return
+	}
+	usestack(n - 1)
+}
diff --git a/test/fixedbugs/issue16804.go b/test/fixedbugs/issue16804.go
new file mode 100644
index 0000000..46dd4a3
--- /dev/null
+++ b/test/fixedbugs/issue16804.go
@@ -0,0 +1,16 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 16804: internal error for math.Sqrt as statement
+//              rather than expression
+
+package main
+
+import "math"
+
+func sqrt() {
+	math.Sqrt(2.0)
+}
diff --git a/test/fixedbugs/issue16870.go b/test/fixedbugs/issue16870.go
new file mode 100644
index 0000000..2309997
--- /dev/null
+++ b/test/fixedbugs/issue16870.go
@@ -0,0 +1,140 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"log"
+	"reflect"
+)
+
+func test(got, want interface{}) {
+	if !reflect.DeepEqual(got, want) {
+		log.Fatalf("got %v, want %v", got, want)
+	}
+}
+
+func main() {
+	var i int
+	var ip *int
+	var ok interface{}
+
+	// Channel receives.
+	c := make(chan int, 1)
+	c2 := make(chan int)
+
+	c <- 42
+	i, ok = <-c
+	test(i, 42)
+	test(ok, true)
+
+	c <- 42
+	_, ok = <-c
+	test(ok, true)
+
+	c <- 42
+	select {
+	case i, ok = <-c:
+		test(i, 42)
+		test(ok, true)
+	}
+
+	c <- 42
+	select {
+	case _, ok = <-c:
+		test(ok, true)
+	}
+
+	c <- 42
+	select {
+	case i, ok = <-c:
+		test(i, 42)
+		test(ok, true)
+	default:
+		log.Fatal("bad select")
+	}
+
+	c <- 42
+	select {
+	case _, ok = <-c:
+		test(ok, true)
+	default:
+		log.Fatal("bad select")
+	}
+
+	c <- 42
+	select {
+	case i, ok = <-c:
+		test(i, 42)
+		test(ok, true)
+	case <-c2:
+		log.Fatal("bad select")
+	}
+
+	c <- 42
+	select {
+	case _, ok = <-c:
+		test(ok, true)
+	case <-c2:
+		log.Fatal("bad select")
+	}
+
+	close(c)
+	i, ok = <-c
+	test(i, 0)
+	test(ok, false)
+
+	_, ok = <-c
+	test(ok, false)
+
+	// Map indexing.
+	m := make(map[int]int)
+
+	i, ok = m[0]
+	test(i, 0)
+	test(ok, false)
+
+	_, ok = m[0]
+	test(ok, false)
+
+	m[0] = 42
+	i, ok = m[0]
+	test(i, 42)
+	test(ok, true)
+
+	_, ok = m[0]
+	test(ok, true)
+
+	// Type assertions.
+	var u interface{}
+
+	i, ok = u.(int)
+	test(i, 0)
+	test(ok, false)
+
+	ip, ok = u.(*int)
+	test(ip, (*int)(nil))
+	test(ok, false)
+
+	_, ok = u.(int)
+	test(ok, false)
+
+	u = 42
+	i, ok = u.(int)
+	test(i, 42)
+	test(ok, true)
+
+	_, ok = u.(int)
+	test(ok, true)
+
+	u = &i
+	ip, ok = u.(*int)
+	test(ip, &i)
+	test(ok, true)
+
+	_, ok = u.(*int)
+	test(ok, true)
+}
diff --git a/test/fixedbugs/issue16948.go b/test/fixedbugs/issue16948.go
new file mode 100644
index 0000000..c986024
--- /dev/null
+++ b/test/fixedbugs/issue16948.go
@@ -0,0 +1,34 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// issue 16948: make sure intrinsified atomic ops won't
+// confuse the scheduler.
+
+package main
+
+import "sync/atomic"
+
+func main() {
+	f()
+}
+
+var x int32
+
+type T [10]int
+var sink *T
+
+func f() (t T) {
+	atomic.AddInt32(&x, 1)
+	g(42, 42, 42, 42, 42, &t) // use int values that is invalid pointer to smash the stack slot of return value of runtime.newobject
+	return
+}
+
+//go:noinline
+func g(a, b, c, d, e int, p *T) {
+	var t [10000]int // a large stack frame to trigger stack growing
+	_ = t
+	sink = p // force p (in caller) heap allocated
+}
diff --git a/test/fixedbugs/issue16949.go b/test/fixedbugs/issue16949.go
new file mode 100644
index 0000000..9ee3387
--- /dev/null
+++ b/test/fixedbugs/issue16949.go
@@ -0,0 +1,30 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Ensure that typed non-integer len and cap make arguments are not accepted.
+
+package main
+
+var sink []byte
+
+func main() {
+	sink = make([]byte, 1.0)
+	sink = make([]byte, float32(1.0)) // ERROR "non-integer.*len"
+	sink = make([]byte, float64(1.0)) // ERROR "non-integer.*len"
+
+	sink = make([]byte, 0, 1.0)
+	sink = make([]byte, 0, float32(1.0)) // ERROR "non-integer.*cap"
+	sink = make([]byte, 0, float64(1.0)) // ERROR "non-integer.*cap"
+
+	sink = make([]byte, 1+0i)
+	sink = make([]byte, complex64(1+0i))  // ERROR "non-integer.*len"
+	sink = make([]byte, complex128(1+0i)) // ERROR "non-integer.*len"
+
+	sink = make([]byte, 0, 1+0i)
+	sink = make([]byte, 0, complex64(1+0i))  // ERROR "non-integer.*cap"
+	sink = make([]byte, 0, complex128(1+0i)) // ERROR "non-integer.*cap"
+
+}
diff --git a/test/fixedbugs/issue16985.go b/test/fixedbugs/issue16985.go
new file mode 100644
index 0000000..0cb0dae
--- /dev/null
+++ b/test/fixedbugs/issue16985.go
@@ -0,0 +1,37 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// issue 16985: intrinsified AMD64 atomic ops should clobber flags
+
+package main
+
+import "sync/atomic"
+
+var count uint32
+
+func main() {
+	buffer := []byte("T")
+	for i := 0; i < len(buffer); {
+		atomic.AddUint32(&count, 1)
+		_ = buffer[i]
+		i++
+		i++
+	}
+
+	for i := 0; i < len(buffer); {
+		atomic.CompareAndSwapUint32(&count, 0, 1)
+		_ = buffer[i]
+		i++
+		i++
+	}
+
+	for i := 0; i < len(buffer); {
+		atomic.SwapUint32(&count, 1)
+		_ = buffer[i]
+		i++
+		i++
+	}
+}
diff --git a/test/fixedbugs/issue17005.go b/test/fixedbugs/issue17005.go
new file mode 100644
index 0000000..e539519
--- /dev/null
+++ b/test/fixedbugs/issue17005.go
@@ -0,0 +1,46 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This tickles (a version of) the PPC64 back end to
+// emit a BVS instruction.
+
+package foo
+
+type Flag int
+
+const (
+	Identity  Flag = iota - 2 // H is the identity matrix; no rotation is needed.
+	Rescaling                 // H specifies rescaling.
+)
+
+type DrotmParams struct {
+	Flag
+}
+
+func Drotmg(d1, d2, x1, y1 float64) (p DrotmParams, rd1, rd2, rx1 float64) {
+
+	const (
+		gam    = 4.0
+		gamsq  = 16.0
+		rgamsq = 5e-8
+	)
+
+	if d1 < 0 {
+		p.Flag = Rescaling
+		return
+	}
+
+	for rd1 <= rgamsq || rd1 >= gamsq {
+		if rd1 <= rgamsq {
+			rd1 *= gam * gam
+			rx1 /= gam
+		} else {
+			rd1 /= gam * gam
+			rx1 *= gam
+		}
+	}
+	return
+}
diff --git a/test/fixedbugs/issue17038.go b/test/fixedbugs/issue17038.go
new file mode 100644
index 0000000..1b65ffc
--- /dev/null
+++ b/test/fixedbugs/issue17038.go
@@ -0,0 +1,9 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+const A = complex(0()) // ERROR "cannot call non-function"
diff --git a/test/fixedbugs/issue17039.go b/test/fixedbugs/issue17039.go
new file mode 100644
index 0000000..1298e2b
--- /dev/null
+++ b/test/fixedbugs/issue17039.go
@@ -0,0 +1,17 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+type S []S
+
+func main() {
+	var s S
+	s = append(s, s) // append a nil value to s
+	if s[0] != nil {
+		println("BUG: s[0] != nil")
+	}
+}
diff --git a/test/fixedbugs/issue17111.go b/test/fixedbugs/issue17111.go
new file mode 100644
index 0000000..05284a7
--- /dev/null
+++ b/test/fixedbugs/issue17111.go
@@ -0,0 +1,16 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+type I int
+
+var (
+	i int
+	x = I(i)
+
+	e interface{} = x
+)
diff --git a/test/fixedbugs/issue17194.go b/test/fixedbugs/issue17194.go
new file mode 100644
index 0000000..0594e1c
--- /dev/null
+++ b/test/fixedbugs/issue17194.go
@@ -0,0 +1,17 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package foo
+
+func f(x []interface{}) (err error) {
+	for _, d := range x {
+		_, ok := d.(*int)
+		if ok {
+			return
+		}
+	}
+	return
+}
diff --git a/test/fixedbugs/issue17270.go b/test/fixedbugs/issue17270.go
new file mode 100644
index 0000000..5c009b5
--- /dev/null
+++ b/test/fixedbugs/issue17270.go
@@ -0,0 +1,11 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import "unsafe"
+
+const _ = (unsafe.Sizeof)(0)
diff --git a/test/fixedbugs/issue17381.go b/test/fixedbugs/issue17381.go
new file mode 100644
index 0000000..be63633
--- /dev/null
+++ b/test/fixedbugs/issue17381.go
@@ -0,0 +1,54 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// issue 17381: make sure leave function with non-empty frame
+// saves link register, so that traceback will work.
+
+package main
+
+import (
+	"runtime"
+	"unsafe"
+)
+
+func main() {
+	defer func() {
+		if recover() == nil {
+			panic("did not panic")
+		}
+		pcs := make([]uintptr, 20)
+		n := runtime.Callers(1, pcs)
+		for _, pc := range pcs[:n] {
+			if runtime.FuncForPC(pc).Name() == "main.main" {
+				return
+			}
+		}
+		panic("cannot find main.main in backtrace")
+	}()
+
+	prep()
+	f() // should panic
+}
+
+func funcPC(f interface{}) uintptr {
+	var ptr uintptr
+	return **(**uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&f)) + unsafe.Sizeof(ptr)))
+}
+
+//go:noinline
+func f() {
+	var t [1]int // non-empty frame
+	*(*int)(nil) = t[0]
+}
+
+var p = funcPC(runtime.GC) + 8
+
+//go:noinline
+func prep() {
+	// put some garbage on stack
+	var x = [20]uintptr{p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p}
+	_ = x
+}
diff --git a/test/fixedbugs/issue17449.go b/test/fixedbugs/issue17449.go
new file mode 100644
index 0000000..2302917
--- /dev/null
+++ b/test/fixedbugs/issue17449.go
@@ -0,0 +1,34 @@
+// errorcheck -0 -race
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 17449: race instrumentation copies over previous instrumented nodes from parents block into child's Ninit block.
+// This code surfaces the duplication at compile time because of generated inline labels.
+
+package master
+
+type PriorityList struct {
+    elems []interface{}
+}
+
+func (x *PriorityList) Len() int { return len(x.elems) }
+
+func (l *PriorityList) remove(i int) interface{} {
+    elem := l.elems[i]
+    l.elems = append(l.elems[:i], l.elems[i+1:]...)
+    return elem
+}
+
+func (l *PriorityList) Next() interface{} {
+    return l.remove(l.Len() - 1)
+}
+
+var l *PriorityList
+
+func Foo() {
+    // It would fail here if instrumented code (including inline-label) was copied.
+    for elem := l.Next(); elem != nil; elem = l.Next() {
+    }
+}
diff --git a/test/fixedbugs/issue17551.go b/test/fixedbugs/issue17551.go
new file mode 100644
index 0000000..b8751ab
--- /dev/null
+++ b/test/fixedbugs/issue17551.go
@@ -0,0 +1,21 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 17551: inrange optimization failed to preserve type information.
+
+package main
+
+import "fmt"
+
+func main() {
+	_, x := X()
+	fmt.Printf("x = %v\n", x)
+}
+
+func X() (i int, ok bool) {
+	ii := int(1)
+	return ii, 0 <= ii && ii <= 0x7fffffff
+}
diff --git a/test/fixedbugs/issue17588.go b/test/fixedbugs/issue17588.go
new file mode 100644
index 0000000..1be57c6
--- /dev/null
+++ b/test/fixedbugs/issue17588.go
@@ -0,0 +1,20 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 17588: internal compiler error in typecheckclosure()
+// because in case of Func.Nname.Type == nil, Decldepth
+// is not initialized in typecheckfunc(). This test
+// produces that case.
+
+package p
+
+type F func(b T)  // ERROR "T is not a type"
+
+func T(fn F) {
+    func() {
+        fn(nil)  // If Decldepth is not initialized properly, typecheckclosure() Fatals here.
+    }()
+}
diff --git a/test/fixedbugs/issue17596.go b/test/fixedbugs/issue17596.go
new file mode 100644
index 0000000..7398292
--- /dev/null
+++ b/test/fixedbugs/issue17596.go
@@ -0,0 +1,19 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package foo
+
+type T interface {
+	foo()
+}
+
+func f() (T, int)
+
+func g(v interface{}) (interface{}, int) {
+	var x int
+	v, x = f()
+	return v, x
+}
diff --git a/test/fixedbugs/issue17631.go b/test/fixedbugs/issue17631.go
new file mode 100644
index 0000000..79b7e8a
--- /dev/null
+++ b/test/fixedbugs/issue17631.go
@@ -0,0 +1,22 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "time"
+
+func main() {
+	_ = struct {
+		about      string
+		before     map[string]uint
+		update     map[string]int
+		updateTime time.Time
+		expect     map[string]int
+	}{
+		about:   "this one",
+		updates: map[string]int{"gopher": 10}, // ERROR "unknown field 'updates' in struct literal of type"
+	}
+}
diff --git a/test/fixedbugs/issue17640.go b/test/fixedbugs/issue17640.go
new file mode 100644
index 0000000..a311521
--- /dev/null
+++ b/test/fixedbugs/issue17640.go
@@ -0,0 +1,28 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "fmt"
+
+var results string
+
+type TwoInts struct {
+	x, y int
+}
+
+func f(x int) int { results = results + fmt.Sprintf("_%d", x); return x }
+
+func main() {
+	_ = [19]int{1: f(1), 0: f(0), 2: f(2), 6, 7}
+	_ = [2]int{1: f(4), 0: f(3)}
+	_ = TwoInts{y: f(6), x: f(5)}
+	_ = map[int]int{f(f(9) + 1): f(8), 0: f(7), f(22): -1}
+	if results != "_1_0_2_4_3_6_5_9_10_8_7_22" {
+		fmt.Printf("unexpected: %s\n", results)
+		panic("fail")
+	}
+}
diff --git a/test/fixedbugs/issue17645.go b/test/fixedbugs/issue17645.go
new file mode 100644
index 0000000..ed92c54
--- /dev/null
+++ b/test/fixedbugs/issue17645.go
@@ -0,0 +1,17 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+type Foo struct {
+	X int
+}
+
+func main() {
+	var s []int
+	var _ string = append(s, Foo{""})  // ERROR "cannot use .. \(type string\) as type int in field value" "cannot use Foo literal \(type Foo\) as type int in append" "cannot use append\(s\, Foo literal\) \(type \[\]int\) as type string in assignment"
+}
+
diff --git a/test/fixedbugs/issue17710.go b/test/fixedbugs/issue17710.go
new file mode 100644
index 0000000..2843458
--- /dev/null
+++ b/test/fixedbugs/issue17710.go
@@ -0,0 +1,13 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "runtime"
+
+func f(x interface{}) {
+	runtime.KeepAlive(x)
+}
diff --git a/test/fixedbugs/issue17752.go b/test/fixedbugs/issue17752.go
new file mode 100644
index 0000000..83283ad
--- /dev/null
+++ b/test/fixedbugs/issue17752.go
@@ -0,0 +1,20 @@
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func f(m map[string]int) int {
+	return m["a"]
+}
+
+func g(m map[[8]string]int) int {
+	return m[[8]string{"a", "a", "a", "a", "a", "a", "a", "a"}]
+}
+
+func main() {
+	m := map[[8]string]int{}
+	g(m)
+}
diff --git a/test/fixedbugs/issue17918.go b/test/fixedbugs/issue17918.go
new file mode 100644
index 0000000..88ede6f
--- /dev/null
+++ b/test/fixedbugs/issue17918.go
@@ -0,0 +1,41 @@
+// compile
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 17918: slice out-of-bounds in ssa/cse
+
+package dead
+
+import (
+	"fmt"
+	"time"
+)
+
+var (
+	units = []struct {
+		divisor time.Duration
+		unit    rune
+	}{
+		{1000000, 's'},
+		{60, 'm'},
+		{60, 'h'},
+		{24, 'd'},
+		{7, 'w'},
+	}
+)
+
+func foobar(d time.Duration) string {
+	d /= time.Microsecond
+	unit := 'u'
+
+	for _, f := range units {
+		if d%f.divisor != 0 {
+			break
+		}
+		d /= f.divisor
+		unit = f.unit
+	}
+	return fmt.Sprintf("%d%c", d, unit)
+}
diff --git a/test/fixedbugs/issue18092.go b/test/fixedbugs/issue18092.go
new file mode 100644
index 0000000..94fd2dd
--- /dev/null
+++ b/test/fixedbugs/issue18092.go
@@ -0,0 +1,15 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func _() {
+	var ch chan bool
+	select {
+	default:
+	case <-ch { // don't crash here
+	}           // ERROR "expecting :"
+}
diff --git a/test/fixedbugs/issue4085b.go b/test/fixedbugs/issue4085b.go
index 583c417..b91bbd7 100644
--- a/test/fixedbugs/issue4085b.go
+++ b/test/fixedbugs/issue4085b.go
@@ -15,21 +15,25 @@ type T []int
 
 func main() {
 	n := -1
-	shouldPanic("len out of range", func() {_ = make(T, n)})
-	shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
+	shouldPanic("len out of range", func() { _ = make(T, n) })
+	shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
+	shouldPanic("len out of range", func() { _ = make(T, int64(n)) })
+	shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) })
 	var t *byte
 	if unsafe.Sizeof(t) == 8 {
-		n = 1<<20
+		n = 1 << 20
 		n <<= 20
-		shouldPanic("len out of range", func() {_ = make(T, n)})
-		shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
+		shouldPanic("len out of range", func() { _ = make(T, n) })
+		shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
 		n <<= 20
-		shouldPanic("len out of range", func() {_ = make(T, n)})
-		shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
+		shouldPanic("len out of range", func() { _ = make(T, n) })
+		shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
 	} else {
 		n = 1<<31 - 1
-		shouldPanic("len out of range", func() {_ = make(T, n)})
-		shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
+		shouldPanic("len out of range", func() { _ = make(T, n) })
+		shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
+		shouldPanic("len out of range", func() { _ = make(T, int64(n)) })
+		shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) })
 	}
 }
 
@@ -44,6 +48,6 @@ func shouldPanic(str string, f func()) {
 			panic("got panic " + s + ", want " + str)
 		}
 	}()
-	
+
 	f()
 }
diff --git a/test/fixedbugs/issue4215.go b/test/fixedbugs/issue4215.go
new file mode 100644
index 0000000..795d48d
--- /dev/null
+++ b/test/fixedbugs/issue4215.go
@@ -0,0 +1,53 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func foo() (int, int) {
+	return 2.3 // ERROR "not enough arguments to return\n\thave \(number\)\n\twant \(int, int\)"
+}
+
+func foo2() {
+	return int(2), 2 // ERROR "too many arguments to return\n\thave \(int, number\)\n\twant \(\)"
+}
+
+func foo3(v int) (a, b, c, d int) {
+	if v >= 0 {
+		return 1 // ERROR "not enough arguments to return\n\thave \(number\)\n\twant \(int, int, int, int\)"
+	}
+	return 2, 3 // ERROR "not enough arguments to return\n\thave \(number, number\)\n\twant \(int, int, int, int\)"
+}
+
+func foo4(name string) (string, int) {
+	switch name {
+	case "cow":
+		return "moo" // ERROR "not enough arguments to return\n\thave \(string\)\n\twant \(string, int\)"
+	case "dog":
+		return "dog", 10, true // ERROR "too many arguments to return\n\thave \(string, number, bool\)\n\twant \(string, int\)"
+	case "fish":
+		return "" // ERROR "not enough arguments to return\n\thave \(string\)\n\twant \(string, int\)"
+	default:
+		return "lizard", 10
+	}
+}
+
+type S int
+type T string
+type U float64
+
+func foo5() (S, T, U) {
+	if false {
+		return "" // ERROR "not enough arguments to return\n\thave \(string\)\n\twant \(S, T, U\)"
+	} else {
+		ptr := new(T)
+		return ptr // ERROR "not enough arguments to return\n\thave \(\*T\)\n\twant \(S, T, U\)"
+	}
+	return new(S), 12.34, 1 + 0i, 'r', true // ERROR "too many arguments to return\n\thave \(\*S, number, number, number, bool\)\n\twant \(S, T, U\)"
+}
+
+func foo6() (T, string) {
+	return "T", true, true // ERROR "too many arguments to return\n\thave \(string, bool, bool\)\n\twant \(T, string\)"
+}
diff --git a/test/fixedbugs/issue6750.go b/test/fixedbugs/issue6750.go
new file mode 100644
index 0000000..dbbb454
--- /dev/null
+++ b/test/fixedbugs/issue6750.go
@@ -0,0 +1,22 @@
+// errorcheck
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "fmt"
+
+func printmany(nums ...int) {
+	for i, n := range nums {
+		fmt.Printf("%d: %d\n", i, n)
+	}
+	fmt.Printf("\n")
+}
+
+func main() {
+	printmany(1, 2, 3)
+	printmany([]int{1, 2, 3}...)
+	printmany(1, "abc", []int{2, 3}...) // ERROR "too many arguments in call to printmany\n\thave \(number, string, \[\]int\.\.\.\)\n\twant \(...int\)"
+}
diff --git a/test/fixedbugs/issue8613.go b/test/fixedbugs/issue8613.go
index c0ad131..ffa75a4 100644
--- a/test/fixedbugs/issue8613.go
+++ b/test/fixedbugs/issue8613.go
@@ -1,4 +1,3 @@
-// +build amd64
 // run
 
 // Copyright 2016 The Go Authors. All rights reserved.
diff --git a/test/fixedbugs/issue9608.dir/issue9608.go b/test/fixedbugs/issue9608.dir/issue9608.go
index 56b52cc..ca82ded 100644
--- a/test/fixedbugs/issue9608.dir/issue9608.go
+++ b/test/fixedbugs/issue9608.dir/issue9608.go
@@ -67,6 +67,15 @@ func init() {
 	case true:
 		fail()
 	}
+
+	// Test dead code elimination in large ranges.
+	switch 5 {
+	case 3, 4, 5, 6, 7:
+	case 0, 1, 2:
+		fail()
+	default:
+		fail()
+	}
 }
 
 func main() {
diff --git a/test/float_lit2.go b/test/float_lit2.go
index bb86559..901698f 100644
--- a/test/float_lit2.go
+++ b/test/float_lit2.go
@@ -13,12 +13,12 @@ import (
 	"math"
 )
 
-// The largest exact float32 is f₁ = (1+(1-2²³))×2¹²⁷ = (1-2²⁴)×2¹²⁸ = 2¹²⁸ - 2¹⁰⁴.
+// The largest exact float32 is f₁ = (1+1-1/2²³)×2¹²⁷ = (2-2⁻²³)×2¹²⁷ = 2¹²⁸ - 2¹⁰⁴.
 // The next float32 would be f₂ = (1+1)×2¹²⁷ = 1×2¹²⁸, except that exponent is out of range.
 // Float32 conversion rounds to the nearest float32, rounding to even mantissa:
 // between f₁ and f₂, values closer to f₁ round to f₁ and values closer to f₂ are rejected as out of range.
 // f₁ is an odd mantissa, so the halfway point (f₁+f₂)/2 rounds to f₂ and is rejected.
-// The halfway point is (f₁+f₂)/2 = 2¹²⁸ - 2¹⁰⁵.
+// The halfway point is (f₁+f₂)/2 = 2¹²⁸ - 2¹⁰³.
 //
 // The same is true of float64, with different constants: s/24/53/ and s/128/1024/.
 
diff --git a/test/inline_variadic.go b/test/inline_variadic.go
new file mode 100644
index 0000000..6466c2b
--- /dev/null
+++ b/test/inline_variadic.go
@@ -0,0 +1,19 @@
+// errorcheck -0 -m -l=3
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test more aggressive inlining (-l=3 allows variadic functions)
+// See issue #18116.
+
+package foo
+
+func head(xs ...string) string { // ERROR "can inline head" "leaking param: xs to result"
+	return xs[0]
+}
+
+func f() string { // ERROR "can inline f"
+	x := head("hello", "world") // ERROR "inlining call to head" "\[\]string literal does not escape"
+	return x
+}
diff --git a/test/interface/assertinline.go b/test/interface/assertinline.go
index 227fe70..324316b 100644
--- a/test/interface/assertinline.go
+++ b/test/interface/assertinline.go
@@ -24,30 +24,51 @@ func assertfunc2(x interface{}) (func(), bool) {
 	return z, ok
 }
 
-// TODO(rsc): struct{*int} is stored directly in the interface
-// and should be possible to fetch back out of the interface,
-// but more of the general data movement code needs to
-// realize that before we can inline the assertion.
-
 func assertstruct(x interface{}) struct{ *int } {
-	return x.(struct{ *int }) // ERROR "type assertion not inlined"
+	return x.(struct{ *int }) // ERROR "type assertion inlined"
 }
 
 func assertstruct2(x interface{}) (struct{ *int }, bool) {
-	z, ok := x.(struct{ *int }) // ERROR "type assertion not inlined"
+	z, ok := x.(struct{ *int }) // ERROR "type assertion inlined"
 	return z, ok
 }
 
 func assertbig(x interface{}) complex128 {
-	return x.(complex128) // ERROR "type assertion not inlined"
+	return x.(complex128) // ERROR "type assertion inlined"
 }
 
 func assertbig2(x interface{}) (complex128, bool) {
-	z, ok := x.(complex128) // ERROR "type assertion not inlined"
+	z, ok := x.(complex128) // ERROR "type assertion inlined"
 	return z, ok
 }
 
 func assertbig2ok(x interface{}) (complex128, bool) {
-	_, ok := x.(complex128) // ERROR "type assertion [(]ok only[)] inlined"
+	_, ok := x.(complex128) // ERROR "type assertion inlined"
 	return 0, ok
 }
+
+func assertslice(x interface{}) []int {
+	return x.([]int) // ERROR "type assertion inlined"
+}
+
+func assertslice2(x interface{}) ([]int, bool) {
+	z, ok := x.([]int) // ERROR "type assertion inlined"
+	return z, ok
+}
+
+func assertslice2ok(x interface{}) ([]int, bool) {
+	_, ok := x.([]int) // ERROR "type assertion inlined"
+	return nil, ok
+}
+
+type I interface {
+	foo()
+}
+
+func assertInter(x interface{}) I {
+	return x.(I) // ERROR "type assertion not inlined"
+}
+func assertInter2(x interface{}) (I, bool) {
+	z, ok := x.(I) // ERROR "type assertion not inlined"
+	return z, ok
+}
diff --git a/test/intrinsic.dir/main.go b/test/intrinsic.dir/main.go
index 46e6cb3..e0c11d0 100644
--- a/test/intrinsic.dir/main.go
+++ b/test/intrinsic.dir/main.go
@@ -45,18 +45,6 @@ func test(i, x uint64) {
 			logf("Ctz32(0x%x) expected %d but got %d\n", x32, i, t32)
 		}
 	}
-	if i <= 16 {
-		x16 := uint16(x)
-		t16 := T.Ctz16(x16) // ERROR "intrinsic substitution for Ctz16"
-		if uint16(i) != t16 {
-			logf("Ctz16(0x%x) expected %d but got %d\n", x16, i, t16)
-		}
-		x16 = -x16
-		t16 = T.Ctz16(x16) // ERROR "intrinsic substitution for Ctz16"
-		if uint16(i) != t16 {
-			logf("Ctz16(0x%x) expected %d but got %d\n", x16, i, t16)
-		}
-	}
 }
 
 func main() {
@@ -88,9 +76,6 @@ func main() {
 	}
 
 	// Zero is a special case, be sure it is done right.
-	if T.Ctz16(0) != 16 { // ERROR "intrinsic substitution for Ctz16"
-		logf("ctz16(0) != 16")
-	}
 	if T.Ctz32(0) != 32 { // ERROR "intrinsic substitution for Ctz32"
 		logf("ctz32(0) != 32")
 	}
diff --git a/test/intrinsic.go b/test/intrinsic.go
index f774128..0b783d1 100644
--- a/test/intrinsic.go
+++ b/test/intrinsic.go
@@ -1,5 +1,5 @@
 // errorcheckandrundir -0 -d=ssa/intrinsics/debug
-// +build !ppc64,!ppc64le,amd64
+// +build amd64 arm64 arm s390x
 
 // Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
diff --git a/test/intrinsic_atomic.go b/test/intrinsic_atomic.go
new file mode 100644
index 0000000..dd765a0
--- /dev/null
+++ b/test/intrinsic_atomic.go
@@ -0,0 +1,20 @@
+// errorcheck -0 -d=ssa/intrinsics/debug
+// +build amd64 arm64
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "sync/atomic"
+
+var x uint32
+
+func atomics() {
+	_ = atomic.LoadUint32(&x)             // ERROR "intrinsic substitution for LoadUint32"
+	atomic.StoreUint32(&x, 1)             // ERROR "intrinsic substitution for StoreUint32"
+	atomic.AddUint32(&x, 1)               // ERROR "intrinsic substitution for AddUint32"
+	atomic.SwapUint32(&x, 1)              // ERROR "intrinsic substitution for SwapUint32"
+	atomic.CompareAndSwapUint32(&x, 1, 2) // ERROR "intrinsic substitution for CompareAndSwapUint32"
+}
diff --git a/test/live.go b/test/live.go
index da0606d..4fb231c 100644
--- a/test/live.go
+++ b/test/live.go
@@ -1,5 +1,6 @@
-// +build !amd64
-// errorcheck -0 -l -live -wb=0
+// errorcheckwithauto -0 -l -live -wb=0
+// +build !ppc64,!ppc64le
+// ppc64 needs a better tighten pass to make f18 pass
 
 // Copyright 2014 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
@@ -45,25 +46,26 @@ func f2(b bool) {
 	printpointer(&x) // ERROR "live at call to printpointer: x$"
 }
 
-func f3(b bool) {
-	// Because x and y are ambiguously live, they appear
-	// live throughout the function, to avoid being poisoned
-	// in GODEBUG=gcdead=1 mode.
+func f3(b1, b2 bool) {
+	// Here x and y are ambiguously live. In previous go versions they
+	// were marked as live throughout the function to avoid being
+	// poisoned in GODEBUG=gcdead=1 mode; this is now no longer the
+	// case.
 
-	printint(0) // ERROR "live at call to printint: x y$"
-	if b == false {
-		printint(0) // ERROR "live at call to printint: x y$"
+	printint(0)
+	if b1 == false {
+		printint(0)
 		return
 	}
 
-	if b {
+	if b2 {
 		var x *int
-		printpointer(&x) // ERROR "live at call to printpointer: x y$"
-		printpointer(&x) // ERROR "live at call to printpointer: x y$"
+		printpointer(&x) // ERROR "live at call to printpointer: x$"
+		printpointer(&x) // ERROR "live at call to printpointer: x$"
 	} else {
 		var y *int
-		printpointer(&y) // ERROR "live at call to printpointer: x y$"
-		printpointer(&y) // ERROR "live at call to printpointer: x y$"
+		printpointer(&y) // ERROR "live at call to printpointer: y$"
+		printpointer(&y) // ERROR "live at call to printpointer: y$"
 	}
 	printint(0) // ERROR "f3: x \(type \*int\) is ambiguously live$" "f3: y \(type \*int\) is ambiguously live$" "live at call to printint: x y$"
 }
@@ -83,13 +85,13 @@ func f4(b1, b2 bool) { // x not live here
 	x := new(int)
 	*x = 42
 	z = &x
-	printint(**z) // ERROR "live at call to printint: x z$"
+	printint(**z) // ERROR "live at call to printint: x$"
 	if b2 {
-		printint(1) // ERROR "live at call to printint: x$"
+		printint(1) // x not live here
 		return
 	}
 	for {
-		printint(**z) // ERROR "live at call to printint: x z$"
+		printint(**z) // ERROR "live at call to printint: x$"
 	}
 }
 
@@ -138,7 +140,9 @@ var i9 interface{}
 func f9() bool {
 	g8()
 	x := i9
-	return x != interface{}(99.0i) // ERROR "live at call to convT2E: x$"
+	y := interface{}(99.0i) // ERROR "live at call to convT2E: x.data x.type$"
+	i9 = y                  // make y escape so the line above has to call convT2E
+	return x != y
 }
 
 // liveness formerly confused by UNDEF followed by RET,
@@ -158,10 +162,10 @@ var b bool
 
 // this used to have a spurious "live at entry to f11a: ~r0"
 func f11a() *int {
-	select { // ERROR "live at call to newselect: autotmp_[0-9]+$" "live at call to selectgo: autotmp_[0-9]+$"
-	case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+$"
+	select { // ERROR "live at call to newselect: .autotmp_[0-9]+$" "live at call to selectgo: .autotmp_[0-9]+$"
+	case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+$"
 		return nil
-	case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+$"
+	case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+$"
 		return nil
 	}
 }
@@ -173,10 +177,10 @@ func f11b() *int {
 		// get to the bottom of the function.
 		// This used to have a spurious "live at call to printint: p".
 		printint(1) // nothing live here!
-		select {    // ERROR "live at call to newselect: autotmp_[0-9]+$" "live at call to selectgo: autotmp_[0-9]+$"
-		case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+$"
+		select {    // ERROR "live at call to newselect: .autotmp_[0-9]+$" "live at call to selectgo: .autotmp_[0-9]+$"
+		case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+$"
 			return nil
-		case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+$"
+		case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+$"
 			return nil
 		}
 	}
@@ -184,15 +188,18 @@ func f11b() *int {
 	return nil
 }
 
+var sink *int
+
 func f11c() *int {
 	p := new(int)
+	sink = p // prevent stack allocation, otherwise p is rematerializeable
 	if b {
 		// Unlike previous, the cases in this select fall through,
 		// so we can get to the println, so p is not dead.
 		printint(1) // ERROR "live at call to printint: p$"
-		select {    // ERROR "live at call to newselect: autotmp_[0-9]+ p$" "live at call to selectgo: autotmp_[0-9]+ p$"
-		case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+ p$"
-		case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+ p$"
+		select {    // ERROR "live at call to newselect: .autotmp_[0-9]+ p$" "live at call to selectgo: .autotmp_[0-9]+ p$"
+		case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+ p$"
+		case <-c: // ERROR "live at call to selectrecv: .autotmp_[0-9]+ p$"
 		}
 	}
 	println(*p)
@@ -215,8 +222,8 @@ func f12() *int {
 // needed for the call to h13).
 
 func f13() {
-	s := "hello"
-	s = h13(s, g13(s)) // ERROR "live at call to g13: s$"
+	s := g14()
+	s = h13(s, g13(s)) // ERROR "live at call to g13: s.ptr$"
 }
 
 func g13(string) string
@@ -250,10 +257,10 @@ var m map[string]int
 
 func f16() {
 	if b {
-		delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$"
+		delete(m, "hi") // ERROR "live at call to mapdelete: .autotmp_[0-9]+$"
 	}
-	delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$"
-	delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$"
+	delete(m, "hi") // ERROR "live at call to mapdelete: .autotmp_[0-9]+$"
+	delete(m, "hi") // ERROR "live at call to mapdelete: .autotmp_[0-9]+$"
 }
 
 var m2s map[string]*byte
@@ -261,33 +268,34 @@ var m2 map[[2]string]*byte
 var x2 [2]string
 var bp *byte
 
-func f17a() {
-	// value temporary only
+func f17a(p *byte) { // ERROR "live at entry to f17a: p$"
 	if b {
-		m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
+		m2[x2] = p // ERROR "live at call to mapassign: p$"
 	}
-	m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
-	m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
+	m2[x2] = p // ERROR "live at call to mapassign: p$"
+	m2[x2] = p // ERROR "live at call to mapassign: p$"
 }
 
-func f17b() {
-	// key temporary only
+func f17b(p *byte) { // ERROR "live at entry to f17b: p$"
+	// key temporary
 	if b {
-		m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
+		m2s["x"] = p // ERROR "live at call to mapassign: p .autotmp_[0-9]+$"
 	}
-	m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
-	m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
+	m2s["x"] = p // ERROR "live at call to mapassign: p .autotmp_[0-9]+$"
+	m2s["x"] = p // ERROR "live at call to mapassign: p .autotmp_[0-9]+$"
 }
 
 func f17c() {
 	// key and value temporaries
 	if b {
-		m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
+		m2s["x"] = f17d() // ERROR "live at call to f17d: .autotmp_[0-9]+$" "live at call to mapassign: .autotmp_[0-9]+ .autotmp_[0-9]+$"
 	}
-	m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
-	m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
+	m2s["x"] = f17d() // ERROR "live at call to f17d: .autotmp_[0-9]+$" "live at call to mapassign: .autotmp_[0-9]+ .autotmp_[0-9]+$"
+	m2s["x"] = f17d() // ERROR "live at call to f17d: .autotmp_[0-9]+$" "live at call to mapassign: .autotmp_[0-9]+ .autotmp_[0-9]+$"
 }
 
+func f17d() *byte
+
 func g18() [2]string
 
 func f18() {
@@ -295,10 +303,10 @@ func f18() {
 	// temporary introduced by orderexpr.
 	var z *byte
 	if b {
-		z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+		z = m2[g18()] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
 	}
-	z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-	z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+	z = m2[g18()] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
+	z = m2[g18()] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
 	printbytepointer(z)
 }
 
@@ -309,30 +317,30 @@ func f19() {
 	var z *byte
 
 	if b {
-		z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$"
+		z = <-ch // ERROR "live at call to chanrecv1: .autotmp_[0-9]+$"
 	}
-	z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$"
-	z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$"
+	z = <-ch // ERROR "live at call to chanrecv1: .autotmp_[0-9]+$"
+	z = <-ch // ERROR "live at call to chanrecv1: .autotmp_[0-9]+$"
 	printbytepointer(z)
 }
 
 func f20() {
 	// src temporary for channel send
 	if b {
-		ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$"
+		ch <- nil // ERROR "live at call to chansend1: .autotmp_[0-9]+$"
 	}
-	ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$"
-	ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$"
+	ch <- nil // ERROR "live at call to chansend1: .autotmp_[0-9]+$"
+	ch <- nil // ERROR "live at call to chansend1: .autotmp_[0-9]+$"
 }
 
 func f21() {
 	// key temporary for mapaccess using array literal key.
 	var z *byte
 	if b {
-		z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+		z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
 	}
-	z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-	z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+	z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
+	z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
 	printbytepointer(z)
 }
 
@@ -341,10 +349,10 @@ func f23() {
 	var z *byte
 	var ok bool
 	if b {
-		z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$"
+		z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: .autotmp_[0-9]+$"
 	}
-	z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$"
-	z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$"
+	z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: .autotmp_[0-9]+$"
+	z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: .autotmp_[0-9]+$"
 	printbytepointer(z)
 	print(ok)
 }
@@ -353,10 +361,10 @@ func f24() {
 	// key temporary for map access using array literal key.
 	// value temporary too.
 	if b {
-		m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
+		m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign: .autotmp_[0-9]+$"
 	}
-	m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
-	m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
+	m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign: .autotmp_[0-9]+$"
+	m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign: .autotmp_[0-9]+$"
 }
 
 // defer should not cause spurious ambiguously live variables
@@ -380,10 +388,10 @@ func g25()
 
 func f26(b bool) {
 	if b {
-		print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$"
+		print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: .autotmp_[0-9]+$"
 	}
-	print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$"
-	print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$"
+	print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: .autotmp_[0-9]+$"
+	print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: .autotmp_[0-9]+$"
 	printnl()
 }
 
@@ -395,10 +403,10 @@ func print26(...interface{})
 func f27(b bool) {
 	x := 0
 	if b {
-		call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$"
+		call27(func() { x++ }) // ERROR "live at call to call27: .autotmp_[0-9]+$"
 	}
-	call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$"
-	call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$"
+	call27(func() { x++ }) // ERROR "live at call to call27: .autotmp_[0-9]+$"
+	call27(func() { x++ }) // ERROR "live at call to call27: .autotmp_[0-9]+$"
 	printnl()
 }
 
@@ -407,11 +415,11 @@ func f27(b bool) {
 func f27defer(b bool) {
 	x := 0
 	if b {
-		defer call27(func() { x++ }) // ERROR "live at call to deferproc: autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+$"
+		defer call27(func() { x++ }) // ERROR "live at call to deferproc: .autotmp_[0-9]+$" "live at call to deferreturn: .autotmp_[0-9]+$"
 	}
-	defer call27(func() { x++ }) // ERROR "f27defer: autotmp_[0-9]+ \(type struct { F uintptr; x \*int }\) is ambiguously live$" "live at call to deferproc: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$"
-	printnl()                    // ERROR "live at call to printnl: autotmp_[0-9]+ autotmp_[0-9]+$"
-} // ERROR "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$"
+	defer call27(func() { x++ }) // ERROR "f27defer: .autotmp_[0-9]+ \(type struct { F uintptr; x \*int }\) is ambiguously live$" "live at call to deferproc: .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to deferreturn: .autotmp_[0-9]+ .autotmp_[0-9]+$"
+	printnl()                    // ERROR "live at call to printnl: .autotmp_[0-9]+ .autotmp_[0-9]+$"
+} // ERROR "live at call to deferreturn: .autotmp_[0-9]+ .autotmp_[0-9]+$"
 
 // and newproc (go) escapes to the heap
 
@@ -433,25 +441,25 @@ var s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 string
 
 func f28(b bool) {
 	if b {
-		printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+		printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: .autotmp_[0-9]+$" "live at call to printstring: .autotmp_[0-9]+$"
 	}
-	printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
-	printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+	printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: .autotmp_[0-9]+$" "live at call to printstring: .autotmp_[0-9]+$"
+	printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: .autotmp_[0-9]+$" "live at call to printstring: .autotmp_[0-9]+$"
 }
 
 // map iterator should die on end of range loop
 
 func f29(b bool) {
 	if b {
-		for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
-			printstring(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
+		for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$"
+			printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
 		}
 	}
-	for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
-		printstring(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
+	for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$"
+		printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
 	}
-	for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
-		printstring(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
+	for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$"
+		printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
 	}
 }
 
@@ -464,14 +472,14 @@ func f30(b bool) {
 	// the copy of ptrarr and the internal iterator pointer.
 	if b {
 		for _, p := range ptrarr {
-			printintpointer(p) // ERROR "live at call to printintpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
+			printintpointer(p) // ERROR "live at call to printintpointer: .autotmp_[0-9]+ .autotmp_[0-9]+$"
 		}
 	}
 	for _, p := range ptrarr {
-		printintpointer(p) // ERROR "live at call to printintpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
+		printintpointer(p) // ERROR "live at call to printintpointer: .autotmp_[0-9]+ .autotmp_[0-9]+$"
 	}
 	for _, p := range ptrarr {
-		printintpointer(p) // ERROR "live at call to printintpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
+		printintpointer(p) // ERROR "live at call to printintpointer: .autotmp_[0-9]+ .autotmp_[0-9]+$"
 	}
 }
 
@@ -479,13 +487,13 @@ func f30(b bool) {
 
 func f31(b1, b2, b3 bool) {
 	if b1 {
-		g31("a") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to g31: autotmp_[0-9]+$"
+		g31("a") // ERROR "live at call to convT2E: .autotmp_[0-9]+$" "live at call to g31: .autotmp_[0-9]+$"
 	}
 	if b2 {
-		h31("b") // ERROR "live at call to convT2E: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to h31: autotmp_[0-9]+$" "live at call to newobject: autotmp_[0-9]+$"
+		h31("b") // ERROR "live at call to convT2E: .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to h31: .autotmp_[0-9]+$" "live at call to newobject: .autotmp_[0-9]+$"
 	}
 	if b3 {
-		panic("asdf") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to gopanic: autotmp_[0-9]+$"
+		panic("asdf") // ERROR "live at call to convT2E: .autotmp_[0-9]+$" "live at call to gopanic: .autotmp_[0-9]+$"
 	}
 	print(b3)
 }
@@ -505,10 +513,10 @@ var t32 T32
 
 func f32(b bool) {
 	if b {
-		call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$"
+		call32(t32.Inc) // ERROR "live at call to call32: .autotmp_[0-9]+$"
 	}
-	call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$"
-	call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$"
+	call32(t32.Inc) // ERROR "live at call to call32: .autotmp_[0-9]+$"
+	call32(t32.Inc) // ERROR "live at call to call32: .autotmp_[0-9]+$"
 }
 
 //go:noescape
@@ -520,7 +528,7 @@ func call32(func())
 var m33 map[interface{}]int
 
 func f33() {
-	if m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+	if m33[nil] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
 		printnl()
 		return
 	} else {
@@ -530,7 +538,7 @@ func f33() {
 }
 
 func f34() {
-	if m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+	if m33[nil] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
 		printnl()
 		return
 	}
@@ -538,7 +546,7 @@ func f34() {
 }
 
 func f35() {
-	if m33[nil] == 0 && m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+	if m33[nil] == 0 && m33[nil] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
 		printnl()
 		return
 	}
@@ -546,7 +554,7 @@ func f35() {
 }
 
 func f36() {
-	if m33[nil] == 0 || m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+	if m33[nil] == 0 || m33[nil] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
 		printnl()
 		return
 	}
@@ -554,7 +562,7 @@ func f36() {
 }
 
 func f37() {
-	if (m33[nil] == 0 || m33[nil] == 0) && m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+	if (m33[nil] == 0 || m33[nil] == 0) && m33[nil] == 0 { // ERROR "live at call to mapaccess1: .autotmp_[0-9]+$"
 		printnl()
 		return
 	}
@@ -574,14 +582,14 @@ func f38(b bool) {
 	// we care that the println lines have no live variables
 	// and therefore no output.
 	if b {
-		select { // ERROR "live at call to newselect: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to selectgo: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$"
-		case <-fc38(): // ERROR "live at call to selectrecv: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$"
+		select { // ERROR "live at call to newselect: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to selectgo: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$"
+		case <-fc38(): // ERROR "live at call to selectrecv: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$"
 			printnl()
-		case fc38() <- *fi38(1): // ERROR "live at call to fc38: autotmp_[0-9]+$" "live at call to fi38: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to selectsend: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$"
+		case fc38() <- *fi38(1): // ERROR "live at call to fc38: .autotmp_[0-9]+$" "live at call to fi38: .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to selectsend: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$"
 			printnl()
-		case *fi38(2) = <-fc38(): // ERROR "live at call to fc38: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to fi38: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to selectrecv: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$"
+		case *fi38(2) = <-fc38(): // ERROR "live at call to fc38: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to fi38: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to selectrecv: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$"
 			printnl()
-		case *fi38(3), *fb38() = <-fc38(): // ERROR "live at call to fb38: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to fc38: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to fi38: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to selectrecv2: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$"
+		case *fi38(3), *fb38() = <-fc38(): // ERROR "live at call to fb38: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to fc38: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to fi38: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$" "live at call to selectrecv2: .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+ .autotmp_[0-9]+$"
 			printnl()
 		}
 		printnl()
@@ -593,13 +601,13 @@ func f38(b bool) {
 
 func f39() (x []int) {
 	x = []int{1}
-	printnl() // ERROR "live at call to printnl: x$"
+	printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+$"
 	return x
 }
 
 func f39a() (x []int) {
 	x = []int{1}
-	printnl() // ERROR "live at call to printnl: x$"
+	printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+$"
 	return
 }
 
@@ -638,8 +646,24 @@ func bad40() {
 
 func good40() {
 	ret := T40{}
-	ret.m = make(map[int]int) // ERROR "live at call to makemap: autotmp_[0-9]+ ret$"
+	ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
 	t := &ret
-	printnl() // ERROR "live at call to printnl: autotmp_[0-9]+ ret$"
+	printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
 	_ = t
 }
+
+func ddd1(x, y *int) { // ERROR "live at entry to ddd1: x y$"
+	ddd2(x, y) // ERROR "live at call to ddd2: .autotmp_[0-9]+$"
+	printnl()
+	// Note: no .?autotmp live at printnl.  See issue 16996.
+}
+func ddd2(a ...*int) { // ERROR "live at entry to ddd2: a$"
+	sink = a[0]
+}
+
+// issue 16016: autogenerated wrapper should have arguments live
+type T struct{}
+
+func (*T) Foo(ptr *int) {}
+
+type R struct{ *T } // ERRORAUTO "live at entry to \(\*R\)\.Foo: \.this ptr" "live at entry to R\.Foo: \.this ptr"
diff --git a/test/live2.go b/test/live2.go
index a5bbfa5..6138d36 100644
--- a/test/live2.go
+++ b/test/live2.go
@@ -1,4 +1,3 @@
-// +build !amd64
 // errorcheck -0 -live -wb=0
 
 // Copyright 2014 The Go Authors. All rights reserved.
@@ -21,20 +20,20 @@ type T40 struct {
 
 func newT40() *T40 {
 	ret := T40{}
-	ret.m = make(map[int]int) // ERROR "live at call to makemap: &ret"
+	ret.m = make(map[int]int) // ERROR "live at call to makemap: &ret$"
 	return &ret
 }
 
 func bad40() {
-	t := newT40() // ERROR "live at call to makemap: autotmp_.* ret"
-	printnl()     // ERROR "live at call to printnl: autotmp_.* ret"
+	t := newT40() // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
+	printnl()     // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
 	_ = t
 }
 
 func good40() {
 	ret := T40{}
-	ret.m = make(map[int]int) // ERROR "live at call to makemap: autotmp_.* ret"
+	ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
 	t := &ret
-	printnl() // ERROR "live at call to printnl: autotmp_.* ret"
+	printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
 	_ = t
 }
diff --git a/test/live_ssa.go b/test/live_ssa.go
deleted file mode 100644
index bd70924..0000000
--- a/test/live_ssa.go
+++ /dev/null
@@ -1,648 +0,0 @@
-// +build amd64
-// errorcheck -0 -l -live -wb=0
-
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// liveness tests with inlining disabled.
-// see also live2.go.
-
-package main
-
-func printnl()
-
-//go:noescape
-func printpointer(**int)
-
-//go:noescape
-func printintpointer(*int)
-
-//go:noescape
-func printstringpointer(*string)
-
-//go:noescape
-func printstring(string)
-
-//go:noescape
-func printbytepointer(*byte)
-
-func printint(int)
-
-func f1() {
-	var x *int
-	printpointer(&x) // ERROR "live at call to printpointer: x$"
-	printpointer(&x) // ERROR "live at call to printpointer: x$"
-}
-
-func f2(b bool) {
-	if b {
-		printint(0) // nothing live here
-		return
-	}
-	var x *int
-	printpointer(&x) // ERROR "live at call to printpointer: x$"
-	printpointer(&x) // ERROR "live at call to printpointer: x$"
-}
-
-func f3(b1, b2 bool) {
-	// Because x and y are ambiguously live, they appear
-	// live throughout the function, to avoid being poisoned
-	// in GODEBUG=gcdead=1 mode.
-
-	printint(0) // ERROR "live at call to printint: x y$"
-	if b1 == false {
-		printint(0) // ERROR "live at call to printint: x y$"
-		return
-	}
-
-	if b2 {
-		var x *int
-		printpointer(&x) // ERROR "live at call to printpointer: x y$"
-		printpointer(&x) // ERROR "live at call to printpointer: x y$"
-	} else {
-		var y *int
-		printpointer(&y) // ERROR "live at call to printpointer: x y$"
-		printpointer(&y) // ERROR "live at call to printpointer: x y$"
-	}
-	printint(0) // ERROR "f3: x \(type \*int\) is ambiguously live$" "f3: y \(type \*int\) is ambiguously live$" "live at call to printint: x y$"
-}
-
-// The old algorithm treated x as live on all code that
-// could flow to a return statement, so it included the
-// function entry and code above the declaration of x
-// but would not include an indirect use of x in an infinite loop.
-// Check that these cases are handled correctly.
-
-func f4(b1, b2 bool) { // x not live here
-	if b2 {
-		printint(0) // x not live here
-		return
-	}
-	var z **int
-	x := new(int)
-	*x = 42
-	z = &x
-	printint(**z) // ERROR "live at call to printint: x$"
-	if b2 {
-		printint(1) // x not live here
-		return
-	}
-	for {
-		printint(**z) // ERROR "live at call to printint: x$"
-	}
-}
-
-func f5(b1 bool) {
-	var z **int
-	if b1 {
-		x := new(int)
-		*x = 42
-		z = &x
-	} else {
-		y := new(int)
-		*y = 54
-		z = &y
-	}
-	printint(**z) // ERROR "f5: x \(type \*int\) is ambiguously live$" "f5: y \(type \*int\) is ambiguously live$" "live at call to printint: x y$"
-}
-
-// confusion about the _ result used to cause spurious "live at entry to f6: _".
-
-func f6() (_, y string) {
-	y = "hello"
-	return
-}
-
-// confusion about addressed results used to cause "live at entry to f7: x".
-
-func f7() (x string) {
-	_ = &x
-	x = "hello"
-	return
-}
-
-// ignoring block returns used to cause "live at entry to f8: x, y".
-
-func f8() (x, y string) {
-	return g8()
-}
-
-func g8() (string, string)
-
-// ignoring block assignments used to cause "live at entry to f9: x"
-// issue 7205
-
-var i9 interface{}
-
-func f9() bool {
-	g8()
-	x := i9
-	return x != interface{}(99.0i) // ERROR "live at call to convT2E: x.data x.type$"
-}
-
-// liveness formerly confused by UNDEF followed by RET,
-// leading to "live at entry to f10: ~r1" (unnamed result).
-
-func f10() string {
-	panic(1)
-}
-
-// liveness formerly confused by select, thinking runtime.selectgo
-// can return to next instruction; it always jumps elsewhere.
-// note that you have to use at least two cases in the select
-// to get a true select; smaller selects compile to optimized helper functions.
-
-var c chan *int
-var b bool
-
-// this used to have a spurious "live at entry to f11a: ~r0"
-func f11a() *int {
-	select { // ERROR "live at call to newselect: autotmp_[0-9]+$" "live at call to selectgo: autotmp_[0-9]+$"
-	case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+$"
-		return nil
-	case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+$"
-		return nil
-	}
-}
-
-func f11b() *int {
-	p := new(int)
-	if b {
-		// At this point p is dead: the code here cannot
-		// get to the bottom of the function.
-		// This used to have a spurious "live at call to printint: p".
-		printint(1) // nothing live here!
-		select {    // ERROR "live at call to newselect: autotmp_[0-9]+$" "live at call to selectgo: autotmp_[0-9]+$"
-		case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+$"
-			return nil
-		case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+$"
-			return nil
-		}
-	}
-	println(*p)
-	return nil
-}
-
-var sink *int
-
-func f11c() *int {
-	p := new(int)
-	sink = p // prevent stack allocation, otherwise p is rematerializeable
-	if b {
-		// Unlike previous, the cases in this select fall through,
-		// so we can get to the println, so p is not dead.
-		printint(1) // ERROR "live at call to printint: p$"
-		select {    // ERROR "live at call to newselect: autotmp_[0-9]+ p$" "live at call to selectgo: autotmp_[0-9]+ p$"
-		case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+ p$"
-		case <-c: // ERROR "live at call to selectrecv: autotmp_[0-9]+ p$"
-		}
-	}
-	println(*p)
-	return nil
-}
-
-// similarly, select{} does not fall through.
-// this used to have a spurious "live at entry to f12: ~r0".
-
-func f12() *int {
-	if b {
-		select {}
-	} else {
-		return nil
-	}
-}
-
-// incorrectly placed VARDEF annotations can cause missing liveness annotations.
-// this used to be missing the fact that s is live during the call to g13 (because it is
-// needed for the call to h13).
-
-func f13() {
-	s := g14()
-	s = h13(s, g13(s)) // ERROR "live at call to g13: s.ptr$"
-}
-
-func g13(string) string
-func h13(string, string) string
-
-// more incorrectly placed VARDEF.
-
-func f14() {
-	x := g14()
-	printstringpointer(&x) // ERROR "live at call to printstringpointer: x$"
-}
-
-func g14() string
-
-func f15() {
-	var x string
-	_ = &x
-	x = g15()      // ERROR "live at call to g15: x$"
-	printstring(x) // ERROR "live at call to printstring: x$"
-}
-
-func g15() string
-
-// Checking that various temporaries do not persist or cause
-// ambiguously live values that must be zeroed.
-// The exact temporary names are inconsequential but we are
-// trying to check that there is only one at any given site,
-// and also that none show up in "ambiguously live" messages.
-
-var m map[string]int
-
-func f16() {
-	if b {
-		delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$"
-	}
-	delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$"
-	delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$"
-}
-
-var m2s map[string]*byte
-var m2 map[[2]string]*byte
-var x2 [2]string
-var bp *byte
-
-func f17a() {
-	// value temporary only
-	if b {
-		m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
-	}
-	m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
-	m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
-}
-
-func f17b() {
-	// key temporary only
-	if b {
-		m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
-	}
-	m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
-	m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
-}
-
-func f17c() {
-	// key and value temporaries
-	if b {
-		m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
-	}
-	m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
-	m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
-}
-
-func g18() [2]string
-
-func f18() {
-	// key temporary for mapaccess.
-	// temporary introduced by orderexpr.
-	var z *byte
-	if b {
-		z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-	}
-	z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-	z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-	printbytepointer(z)
-}
-
-var ch chan *byte
-
-func f19() {
-	// dest temporary for channel receive.
-	var z *byte
-
-	if b {
-		z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$"
-	}
-	z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$"
-	z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$"
-	printbytepointer(z)
-}
-
-func f20() {
-	// src temporary for channel send
-	if b {
-		ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$"
-	}
-	ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$"
-	ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$"
-}
-
-func f21() {
-	// key temporary for mapaccess using array literal key.
-	var z *byte
-	if b {
-		z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-	}
-	z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-	z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-	printbytepointer(z)
-}
-
-func f23() {
-	// key temporary for two-result map access using array literal key.
-	var z *byte
-	var ok bool
-	if b {
-		z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$"
-	}
-	z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$"
-	z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$"
-	printbytepointer(z)
-	print(ok)
-}
-
-func f24() {
-	// key temporary for map access using array literal key.
-	// value temporary too.
-	if b {
-		m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
-	}
-	m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
-	m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
-}
-
-// defer should not cause spurious ambiguously live variables
-
-func f25(b bool) {
-	defer g25()
-	if b {
-		return
-	}
-	var x string
-	_ = &x
-	x = g15()      // ERROR "live at call to g15: x$"
-	printstring(x) // ERROR "live at call to printstring: x$"
-} // ERROR "live at call to deferreturn: x$"
-
-func g25()
-
-// non-escaping ... slices passed to function call should die on return,
-// so that the temporaries do not stack and do not cause ambiguously
-// live variables.
-
-func f26(b bool) {
-	if b {
-		print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$"
-	}
-	print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$"
-	print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$"
-	printnl()
-}
-
-//go:noescape
-func print26(...interface{})
-
-// non-escaping closures passed to function call should die on return
-
-func f27(b bool) {
-	x := 0
-	if b {
-		call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$"
-	}
-	call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$"
-	call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$"
-	printnl()
-}
-
-// but defer does escape to later execution in the function
-
-func f27defer(b bool) {
-	x := 0
-	if b {
-		defer call27(func() { x++ }) // ERROR "live at call to deferproc: autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+$"
-	}
-	defer call27(func() { x++ }) // ERROR "f27defer: autotmp_[0-9]+ \(type struct { F uintptr; x \*int }\) is ambiguously live$" "live at call to deferproc: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$"
-	printnl()                    // ERROR "live at call to printnl: autotmp_[0-9]+ autotmp_[0-9]+$"
-} // ERROR "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$"
-
-// and newproc (go) escapes to the heap
-
-func f27go(b bool) {
-	x := 0
-	if b {
-		go call27(func() { x++ }) // ERROR "live at call to newobject: &x$" "live at call to newproc: &x$"
-	}
-	go call27(func() { x++ }) // ERROR "live at call to newobject: &x$"
-	printnl()
-}
-
-//go:noescape
-func call27(func())
-
-// concatstring slice should die on return
-
-var s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 string
-
-func f28(b bool) {
-	if b {
-		printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
-	}
-	printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
-	printstring(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
-}
-
-// map iterator should die on end of range loop
-
-func f29(b bool) {
-	if b {
-		for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
-			printstring(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
-		}
-	}
-	for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
-		printstring(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
-	}
-	for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
-		printstring(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
-	}
-}
-
-// copy of array of pointers should die at end of range loop
-
-var ptrarr [10]*int
-
-func f30(b bool) {
-	// two live temps during print(p):
-	// the copy of ptrarr and the internal iterator pointer.
-	if b {
-		for _, p := range ptrarr {
-			printintpointer(p) // ERROR "live at call to printintpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
-		}
-	}
-	for _, p := range ptrarr {
-		printintpointer(p) // ERROR "live at call to printintpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
-	}
-	for _, p := range ptrarr {
-		printintpointer(p) // ERROR "live at call to printintpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
-	}
-}
-
-// conversion to interface should not leave temporary behind
-
-func f31(b1, b2, b3 bool) {
-	if b1 {
-		g31("a") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to g31: autotmp_[0-9]+$"
-	}
-	if b2 {
-		h31("b") // ERROR "live at call to convT2E: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to h31: autotmp_[0-9]+$" "live at call to newobject: autotmp_[0-9]+$"
-	}
-	if b3 {
-		panic("asdf") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to gopanic: autotmp_[0-9]+$"
-	}
-	print(b3)
-}
-
-func g31(interface{})
-func h31(...interface{})
-
-// non-escaping partial functions passed to function call should die on return
-
-type T32 int
-
-func (t *T32) Inc() { // ERROR "live at entry to \(\*T32\).Inc: t$"
-	*t++
-}
-
-var t32 T32
-
-func f32(b bool) {
-	if b {
-		call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$"
-	}
-	call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$"
-	call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$"
-}
-
-//go:noescape
-func call32(func())
-
-// temporaries introduced during if conditions and && || expressions
-// should die once the condition has been acted upon.
-
-var m33 map[interface{}]int
-
-func f33() {
-	if m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-		printnl()
-		return
-	} else {
-		printnl()
-	}
-	printnl()
-}
-
-func f34() {
-	if m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-		printnl()
-		return
-	}
-	printnl()
-}
-
-func f35() {
-	if m33[nil] == 0 && m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-		printnl()
-		return
-	}
-	printnl()
-}
-
-func f36() {
-	if m33[nil] == 0 || m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-		printnl()
-		return
-	}
-	printnl()
-}
-
-func f37() {
-	if (m33[nil] == 0 || m33[nil] == 0) && m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
-		printnl()
-		return
-	}
-	printnl()
-}
-
-// select temps should disappear in the case bodies
-
-var c38 chan string
-
-func fc38() chan string
-func fi38(int) *string
-func fb38() *bool
-
-func f38(b bool) {
-	// we don't care what temps are printed on the lines with output.
-	// we care that the println lines have no live variables
-	// and therefore no output.
-	if b {
-		select { // ERROR "live at call to newselect: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to selectgo: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$"
-		case <-fc38(): // ERROR "live at call to selectrecv: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$"
-			printnl()
-		case fc38() <- *fi38(1): // ERROR "live at call to fc38: autotmp_[0-9]+$" "live at call to fi38: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to selectsend: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$"
-			printnl()
-		case *fi38(2) = <-fc38(): // ERROR "live at call to fc38: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to fi38: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to selectrecv: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$"
-			printnl()
-		case *fi38(3), *fb38() = <-fc38(): // ERROR "live at call to fb38: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to fc38: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to fi38: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to selectrecv2: autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+ autotmp_[0-9]+$"
-			printnl()
-		}
-		printnl()
-	}
-	printnl()
-}
-
-// issue 8097: mishandling of x = x during return.
-
-func f39() (x []int) {
-	x = []int{1}
-	printnl() // ERROR "live at call to printnl: autotmp_[0-9]+$"
-	return x
-}
-
-func f39a() (x []int) {
-	x = []int{1}
-	printnl() // ERROR "live at call to printnl: autotmp_[0-9]+$"
-	return
-}
-
-func f39b() (x [10]*int) {
-	x = [10]*int{}
-	x[0] = new(int) // ERROR "live at call to newobject: x$"
-	printnl()       // ERROR "live at call to printnl: x$"
-	return x
-}
-
-func f39c() (x [10]*int) {
-	x = [10]*int{}
-	x[0] = new(int) // ERROR "live at call to newobject: x$"
-	printnl()       // ERROR "live at call to printnl: x$"
-	return
-}
-
-// issue 8142: lost 'addrtaken' bit on inlined variables.
-// no inlining in this test, so just checking that non-inlined works.
-
-type T40 struct {
-	m map[int]int
-}
-
-func newT40() *T40 {
-	ret := T40{}
-	ret.m = make(map[int]int) // ERROR "live at call to makemap: &ret$"
-	return &ret
-}
-
-func bad40() {
-	t := newT40()
-	_ = t
-	printnl()
-}
-
-func good40() {
-	ret := T40{}
-	ret.m = make(map[int]int) // ERROR "live at call to makemap: autotmp_[0-9]+ ret$"
-	t := &ret
-	printnl() // ERROR "live at call to printnl: autotmp_[0-9]+ ret$"
-	_ = t
-}
diff --git a/test/live_syscall.go b/test/live_syscall.go
index 8aaa691..f693e93 100644
--- a/test/live_syscall.go
+++ b/test/live_syscall.go
@@ -19,10 +19,10 @@ func f(uintptr) // ERROR "f assuming arg#1 is unsafe uintptr"
 
 func g() {
 	var t int
-	f(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to f: autotmp" "g &t does not escape"
+	f(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to f: .?autotmp" "g &t does not escape"
 }
 
 func h() {
 	var v int
-	syscall.Syscall(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to Syscall: autotmp" "h &v does not escape"
+	syscall.Syscall(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to Syscall: .?autotmp" "h &v does not escape"
 }
diff --git a/test/method2.go b/test/method2.go
index aaa850e..e55aee4 100644
--- a/test/method2.go
+++ b/test/method2.go
@@ -33,5 +33,5 @@ var _ = (*Val).val // ERROR "method"
 var v Val
 var pv = &v
 
-var _ = pv.val() // ERROR "method"
-var _ = pv.val   // ERROR "method"
+var _ = pv.val() // ERROR "pv.val undefined"
+var _ = pv.val   // ERROR "pv.val undefined"
diff --git a/test/nilptr3.go b/test/nilptr3.go
index 8922729..8fdae8c 100644
--- a/test/nilptr3.go
+++ b/test/nilptr3.go
@@ -1,8 +1,4 @@
 // errorcheck -0 -d=nil
-// Fails on ppc64x because of incomplete optimization.
-// See issues 9058.
-// Same reason for mips64x and s390x.
-// +build !ppc64,!ppc64le,!mips64,!mips64le,!amd64,!s390x
 
 // Copyright 2013 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
@@ -52,15 +48,15 @@ func f1() {
 	_ = *arrayp // ERROR "generated nil check"
 
 	// 0-byte indirect doesn't suffice.
-	// we don't registerize globals, so there are no removed repeated nil checks.
-	_ = *array0p // ERROR "generated nil check"
+	// we don't registerize globals, so there are no removed.* nil checks.
 	_ = *array0p // ERROR "generated nil check"
+	_ = *array0p // ERROR "removed nil check"
 
-	_ = *intp    // ERROR "generated nil check"
-	_ = *arrayp  // ERROR "generated nil check"
+	_ = *intp    // ERROR "removed nil check"
+	_ = *arrayp  // ERROR "removed nil check"
 	_ = *structp // ERROR "generated nil check"
 	_ = *emptyp  // ERROR "generated nil check"
-	_ = *arrayp  // ERROR "generated nil check"
+	_ = *arrayp  // ERROR "removed nil check"
 }
 
 func f2() {
@@ -78,12 +74,12 @@ func f2() {
 	_ = *intp       // ERROR "generated nil check"
 	_ = *arrayp     // ERROR "generated nil check"
 	_ = *array0p    // ERROR "generated nil check"
-	_ = *array0p    // ERROR "removed repeated nil check"
-	_ = *intp       // ERROR "removed repeated nil check"
-	_ = *arrayp     // ERROR "removed repeated nil check"
+	_ = *array0p    // ERROR "removed.* nil check"
+	_ = *intp       // ERROR "removed.* nil check"
+	_ = *arrayp     // ERROR "removed.* nil check"
 	_ = *structp    // ERROR "generated nil check"
 	_ = *emptyp     // ERROR "generated nil check"
-	_ = *arrayp     // ERROR "removed repeated nil check"
+	_ = *arrayp     // ERROR "removed.* nil check"
 	_ = *bigarrayp  // ERROR "generated nil check" ARM removed nil check before indirect!!
 	_ = *bigstructp // ERROR "generated nil check"
 	_ = *empty1p    // ERROR "generated nil check"
@@ -99,7 +95,7 @@ func f3(x *[10000]int) {
 	_ = x[9999] // ERROR "generated nil check"
 
 	for {
-		if x[9999] != 0 { // ERROR "generated nil check"
+		if x[9999] != 0 { // ERROR "removed nil check"
 			break
 		}
 	}
@@ -107,11 +103,11 @@ func f3(x *[10000]int) {
 	x = fx10k()
 	_ = x[9999] // ERROR "generated nil check"
 	if b {
-		_ = x[9999] // ERROR "removed repeated nil check"
+		_ = x[9999] // ERROR "removed.* nil check"
 	} else {
-		_ = x[9999] // ERROR "removed repeated nil check"
+		_ = x[9999] // ERROR "removed.* nil check"
 	}
-	_ = x[9999] // ERROR "generated nil check"
+	_ = x[9999] // ERROR "removed nil check"
 
 	x = fx10k()
 	if b {
@@ -126,7 +122,7 @@ func f3(x *[10000]int) {
 	// x wasn't going to change across the function call.
 	// But it's a little complex to do and in practice doesn't
 	// matter enough.
-	_ = x[9999] // ERROR "generated nil check"
+	_ = x[9999] // ERROR "removed nil check"
 }
 
 func f3a() {
@@ -135,7 +131,7 @@ func f3a() {
 	z := fx10k()
 	_ = &x[9] // ERROR "generated nil check"
 	y = z
-	_ = &x[9] // ERROR "removed repeated nil check"
+	_ = &x[9] // ERROR "removed.* nil check"
 	x = y
 	_ = &x[9] // ERROR "generated nil check"
 }
@@ -145,9 +141,9 @@ func f3b() {
 	y := fx10k()
 	_ = &x[9] // ERROR "generated nil check"
 	y = x
-	_ = &x[9] // ERROR "removed repeated nil check"
+	_ = &x[9] // ERROR "removed.* nil check"
 	x = y
-	_ = &x[9] // ERROR "removed repeated nil check"
+	_ = &x[9] // ERROR "removed.* nil check"
 }
 
 func fx10() *[10]int
@@ -157,41 +153,56 @@ func f4(x *[10]int) {
 	// and the offset is small enough that if x is nil, the address will still be
 	// in the first unmapped page of memory.
 
-	_ = x[9] // ERROR "removed nil check before indirect"
+	_ = x[9] // ERROR "generated nil check" // bug: would like to remove this check (but nilcheck and load are in different blocks)
 
 	for {
-		if x[9] != 0 { // ERROR "removed nil check before indirect"
+		if x[9] != 0 { // ERROR "removed nil check"
 			break
 		}
 	}
 
 	x = fx10()
-	_ = x[9] // ERROR "removed nil check before indirect"
+	_ = x[9] // ERROR "generated nil check" // bug would like to remove before indirect
 	if b {
-		_ = x[9] // ERROR "removed nil check before indirect"
+		_ = x[9] // ERROR "removed nil check"
 	} else {
-		_ = x[9] // ERROR "removed nil check before indirect"
+		_ = x[9] // ERROR "removed nil check"
 	}
-	_ = x[9] // ERROR "removed nil check before indirect"
+	_ = x[9] // ERROR "removed nil check"
 
 	x = fx10()
 	if b {
-		_ = x[9] // ERROR "removed nil check before indirect"
+		_ = x[9] // ERROR "generated nil check"  // bug would like to remove before indirect
 	} else {
 		_ = &x[9] // ERROR "generated nil check"
 	}
-	_ = x[9] // ERROR "removed nil check before indirect"
+	_ = x[9] // ERROR "generated nil check"  // bug would like to remove before indirect
 
 	fx10()
-	_ = x[9] // ERROR "removed nil check before indirect"
+	_ = x[9] // ERROR "removed nil check"
 
 	x = fx10()
 	y := fx10()
 	_ = &x[9] // ERROR "generated nil check"
 	y = x
-	_ = &x[9] // ERROR "removed repeated nil check"
+	_ = &x[9] // ERROR "removed[a-z ]* nil check"
 	x = y
-	_ = &x[9] // ERROR "removed repeated nil check"
+	_ = &x[9] // ERROR "removed[a-z ]* nil check"
+}
+
+func f5(p *float32, q *float64, r *float32, s *float64) float64 {
+	x := float64(*p) // ERROR "removed nil check"
+	y := *q          // ERROR "removed nil check"
+	*r = 7           // ERROR "removed nil check"
+	*s = 9           // ERROR "removed nil check"
+	return x + y
+}
+
+type T [29]byte
+
+func f6(p, q *T) {
+	x := *p // ERROR "removed nil check"
+	*q = x  // ERROR "removed nil check"
 }
 
 func m1(m map[int][80]byte) byte {
@@ -214,3 +225,32 @@ func p1() byte {
 	p := new([100]byte)
 	return p[5] // ERROR "removed nil check"
 }
+
+// make sure not to do nil check for access of PAUTOHEAP
+//go:noinline
+func (p *Struct) m() {}
+func c1() {
+	var x Struct
+	func() { x.m() }() // ERROR "removed nil check"
+}
+
+type SS struct {
+	x byte
+}
+
+type TT struct {
+	SS
+}
+
+func f(t *TT) *byte {
+	// See issue 17242.
+	s := &t.SS  // ERROR "removed nil check"
+	return &s.x // ERROR "generated nil check"
+}
+
+// make sure not to do nil check for newobject
+func f7() (*Struct, float64) {
+	t := new(Struct)
+	p := &t.Y    // ERROR "removed nil check"
+	return t, *p // ERROR "removed nil check"
+}
diff --git a/test/nilptr3_ssa.go b/test/nilptr3_ssa.go
deleted file mode 100644
index 0d690eb..0000000
--- a/test/nilptr3_ssa.go
+++ /dev/null
@@ -1,230 +0,0 @@
-// errorcheck -0 -d=nil
-// Fails on ppc64x because of incomplete optimization.
-// See issues 9058.
-// +build !ppc64,!ppc64le,amd64
-
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Test that nil checks are removed.
-// Optimization is enabled.
-
-package p
-
-type Struct struct {
-	X int
-	Y float64
-}
-
-type BigStruct struct {
-	X int
-	Y float64
-	A [1 << 20]int
-	Z string
-}
-
-type Empty struct {
-}
-
-type Empty1 struct {
-	Empty
-}
-
-var (
-	intp       *int
-	arrayp     *[10]int
-	array0p    *[0]int
-	bigarrayp  *[1 << 26]int
-	structp    *Struct
-	bigstructp *BigStruct
-	emptyp     *Empty
-	empty1p    *Empty1
-)
-
-func f1() {
-	_ = *intp // ERROR "generated nil check"
-
-	// This one should be removed but the block copy needs
-	// to be turned into its own pseudo-op in order to see
-	// the indirect.
-	_ = *arrayp // ERROR "generated nil check"
-
-	// 0-byte indirect doesn't suffice.
-	// we don't registerize globals, so there are no removed.* nil checks.
-	_ = *array0p // ERROR "generated nil check"
-	_ = *array0p // ERROR "removed nil check"
-
-	_ = *intp    // ERROR "removed nil check"
-	_ = *arrayp  // ERROR "removed nil check"
-	_ = *structp // ERROR "generated nil check"
-	_ = *emptyp  // ERROR "generated nil check"
-	_ = *arrayp  // ERROR "removed nil check"
-}
-
-func f2() {
-	var (
-		intp       *int
-		arrayp     *[10]int
-		array0p    *[0]int
-		bigarrayp  *[1 << 20]int
-		structp    *Struct
-		bigstructp *BigStruct
-		emptyp     *Empty
-		empty1p    *Empty1
-	)
-
-	_ = *intp       // ERROR "generated nil check"
-	_ = *arrayp     // ERROR "generated nil check"
-	_ = *array0p    // ERROR "generated nil check"
-	_ = *array0p    // ERROR "removed.* nil check"
-	_ = *intp       // ERROR "removed.* nil check"
-	_ = *arrayp     // ERROR "removed.* nil check"
-	_ = *structp    // ERROR "generated nil check"
-	_ = *emptyp     // ERROR "generated nil check"
-	_ = *arrayp     // ERROR "removed.* nil check"
-	_ = *bigarrayp  // ERROR "generated nil check" ARM removed nil check before indirect!!
-	_ = *bigstructp // ERROR "generated nil check"
-	_ = *empty1p    // ERROR "generated nil check"
-}
-
-func fx10k() *[10000]int
-
-var b bool
-
-func f3(x *[10000]int) {
-	// Using a huge type and huge offsets so the compiler
-	// does not expect the memory hardware to fault.
-	_ = x[9999] // ERROR "generated nil check"
-
-	for {
-		if x[9999] != 0 { // ERROR "removed nil check"
-			break
-		}
-	}
-
-	x = fx10k()
-	_ = x[9999] // ERROR "generated nil check"
-	if b {
-		_ = x[9999] // ERROR "removed.* nil check"
-	} else {
-		_ = x[9999] // ERROR "removed.* nil check"
-	}
-	_ = x[9999] // ERROR "removed nil check"
-
-	x = fx10k()
-	if b {
-		_ = x[9999] // ERROR "generated nil check"
-	} else {
-		_ = x[9999] // ERROR "generated nil check"
-	}
-	_ = x[9999] // ERROR "generated nil check"
-
-	fx10k()
-	// This one is a bit redundant, if we figured out that
-	// x wasn't going to change across the function call.
-	// But it's a little complex to do and in practice doesn't
-	// matter enough.
-	_ = x[9999] // ERROR "removed nil check"
-}
-
-func f3a() {
-	x := fx10k()
-	y := fx10k()
-	z := fx10k()
-	_ = &x[9] // ERROR "generated nil check"
-	y = z
-	_ = &x[9] // ERROR "removed.* nil check"
-	x = y
-	_ = &x[9] // ERROR "generated nil check"
-}
-
-func f3b() {
-	x := fx10k()
-	y := fx10k()
-	_ = &x[9] // ERROR "generated nil check"
-	y = x
-	_ = &x[9] // ERROR "removed.* nil check"
-	x = y
-	_ = &x[9] // ERROR "removed.* nil check"
-}
-
-func fx10() *[10]int
-
-func f4(x *[10]int) {
-	// Most of these have no checks because a real memory reference follows,
-	// and the offset is small enough that if x is nil, the address will still be
-	// in the first unmapped page of memory.
-
-	_ = x[9] // ERROR "removed nil check"
-
-	for {
-		if x[9] != 0 { // ERROR "removed nil check"
-			break
-		}
-	}
-
-	x = fx10()
-	_ = x[9] // ERROR "generated nil check" // bug would like to remove before indirect
-	if b {
-		_ = x[9] // ERROR "removed nil check"
-	} else {
-		_ = x[9] // ERROR "removed nil check"
-	}
-	_ = x[9] // ERROR "removed nil check"
-
-	x = fx10()
-	if b {
-		_ = x[9] // ERROR "generated nil check"  // bug would like to remove before indirect
-	} else {
-		_ = &x[9] // ERROR "generated nil check"
-	}
-	_ = x[9] // ERROR "generated nil check"  // bug would like to remove before indirect
-
-	fx10()
-	_ = x[9] // ERROR "removed nil check"
-
-	x = fx10()
-	y := fx10()
-	_ = &x[9] // ERROR "generated nil check"
-	y = x
-	_ = &x[9] // ERROR "removed[a-z ]* nil check"
-	x = y
-	_ = &x[9] // ERROR "removed[a-z ]* nil check"
-}
-
-func f5(p *float32, q *float64, r *float32, s *float64) float64 {
-	x := float64(*p) // ERROR "removed nil check"
-	y := *q          // ERROR "removed nil check"
-	*r = 7           // ERROR "removed nil check"
-	*s = 9           // ERROR "removed nil check"
-	return x + y
-}
-
-type T [29]byte
-
-func f6(p, q *T) {
-	x := *p // ERROR "removed nil check"
-	*q = x  // ERROR "removed nil check"
-}
-
-func m1(m map[int][80]byte) byte {
-	v := m[3] // ERROR "removed nil check"
-	return v[5]
-}
-func m2(m map[int][800]byte) byte {
-	v := m[3] // ERROR "removed nil check"
-	return v[5]
-}
-func m3(m map[int][80]byte) (byte, bool) {
-	v, ok := m[3] // ERROR "removed nil check"
-	return v[5], ok
-}
-func m4(m map[int][800]byte) (byte, bool) {
-	v, ok := m[3] // ERROR "removed nil check"
-	return v[5], ok
-}
-func p1() byte {
-	p := new([100]byte)
-	return p[5] // ERROR "removed nil check"
-}
diff --git a/test/nosplit.go b/test/nosplit.go
index a58a645..5f4e62f 100644
--- a/test/nosplit.go
+++ b/test/nosplit.go
@@ -305,13 +305,13 @@ TestCases:
 				// Instead of rewriting the test cases above, adjust
 				// the first stack frame to use up the extra bytes.
 				if i == 0 {
-					size += (720 - 128) - 128
+					size += (880 - 128) - 128
 					// Noopt builds have a larger stackguard.
 					// See ../src/cmd/dist/buildruntime.go:stackGuardMultiplier
 					// This increase is included in obj.StackGuard
 					for _, s := range strings.Split(os.Getenv("GO_GCFLAGS"), " ") {
 						if s == "-N" {
-							size += 720
+							size += 880
 						}
 					}
 				}
diff --git a/test/notinheap.go b/test/notinheap.go
new file mode 100644
index 0000000..c3fdfd6
--- /dev/null
+++ b/test/notinheap.go
@@ -0,0 +1,55 @@
+// errorcheck -+
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test type-checking errors for go:notinheap.
+
+package p
+
+//go:notinheap
+type nih struct{}
+
+// Types embedding notinheap types must be notinheap.
+
+type embed1 struct {
+	x nih
+} // ERROR "must be go:notinheap"
+
+type embed2 [1]nih // ERROR "must be go:notinheap"
+
+type embed3 struct {
+	x [1]nih
+} // ERROR "must be go:notinheap"
+
+type embed4 map[nih]int // ERROR "go:notinheap map key not allowed"
+
+type embed5 map[int]nih // ERROR "go:notinheap map value not allowed"
+
+type emebd6 chan nih // ERROR "chan of go:notinheap type not allowed"
+
+type okay1 *nih
+
+type okay2 []nih
+
+type okay3 func(x nih) nih
+
+type okay4 interface {
+	f(x nih) nih
+}
+
+// Type conversions don't let you sneak past notinheap.
+
+type t1 struct{ x int }
+
+//go:notinheap
+type t2 t1
+
+var sink interface{}
+
+func i() {
+	sink = new(t1)                     // no error
+	sink = (*t2)(new(t1))              // ERROR "cannot convert(.|\n)*t2 is go:notinheap"
+	sink = (*t2)(new(struct{ x int })) // ERROR "cannot convert(.|\n)*t2 is go:notinheap"
+}
diff --git a/test/notinheap2.go b/test/notinheap2.go
new file mode 100644
index 0000000..944f299
--- /dev/null
+++ b/test/notinheap2.go
@@ -0,0 +1,43 @@
+// errorcheck -+
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test walk errors for go:notinheap.
+
+package p
+
+//go:notinheap
+type nih struct {
+	next *nih
+}
+
+// Globals and stack variables are okay.
+
+var x nih
+
+func f() {
+	var y nih
+	x = y
+}
+
+// Heap allocation is not okay.
+
+var y *nih
+var z []nih
+
+func g() {
+	y = new(nih)       // ERROR "heap allocation disallowed"
+	z = make([]nih, 1) // ERROR "heap allocation disallowed"
+	z = append(z, x)   // ERROR "heap allocation disallowed"
+}
+
+// Writes don't produce write barriers.
+
+var p *nih
+
+//go:nowritebarrier
+func h() {
+	y.next = p.next
+}
diff --git a/test/nowritebarrier.go b/test/nowritebarrier.go
new file mode 100644
index 0000000..23dce75
--- /dev/null
+++ b/test/nowritebarrier.go
@@ -0,0 +1,78 @@
+// errorcheck -+
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test go:nowritebarrier and related directives.
+
+package p
+
+type t struct {
+	f *t
+}
+
+var x t
+var y *t
+
+//go:nowritebarrier
+func a1() {
+	x.f = y // ERROR "write barrier prohibited"
+	a2()    // no error
+}
+
+//go:noinline
+func a2() {
+	x.f = y
+}
+
+//go:nowritebarrierrec
+func b1() {
+	b2()
+}
+
+//go:noinline
+func b2() {
+	x.f = y // ERROR "write barrier prohibited by caller"
+}
+
+// Test recursive cycles through nowritebarrierrec and yeswritebarrierrec.
+
+//go:nowritebarrierrec
+func c1() {
+	c2()
+}
+
+//go:yeswritebarrierrec
+func c2() {
+	c3()
+}
+
+func c3() {
+	x.f = y
+	c4()
+}
+
+//go:nowritebarrierrec
+func c4() {
+	c2()
+}
+
+//go:nowritebarrierrec
+func d1() {
+	d2()
+}
+
+func d2() {
+	d3()
+}
+
+func d3() {
+	x.f = y // ERROR "write barrier prohibited by caller"
+	d4()
+}
+
+//go:yeswritebarrierrec
+func d4() {
+	d2()
+}
diff --git a/test/nul1.go b/test/nul1.go
index 20426b4..fbba198 100644
--- a/test/nul1.go
+++ b/test/nul1.go
@@ -36,7 +36,7 @@ var y = ` + "`in raw string \x00 foo`" + `  // ERROR "NUL"
 
 /* in other comment ` + "\x00" + ` */ // ERROR "NUL"
 
-/* in source code */ ` + "\x00" + `// ERROR "NUL" "illegal character"
+/* in source code */ ` + "\x00" + `// ERROR "NUL"
 
 var xx = "in string ` + "\xc2\xff" + `" // ERROR "UTF-8"
 
@@ -47,10 +47,9 @@ var yy = ` + "`in raw string \xff foo`" + `  // ERROR "UTF-8"
 /* in other comment ` + "\xe0\x00\x00" + ` */ // ERROR "UTF-8|NUL"
 
 /* in variable name */
-var z` + "\xc1\x81" + ` int // ERROR "UTF-8" "invalid identifier character"
+var z` + "\xc1\x81" + ` int // ERROR "UTF-8"
 
-/* in source code */ ` + "var \xc2A int" + `// ERROR "UTF-8" "invalid identifier character"
+/* in source code */ ` + "var \xc2A int" + `// ERROR "UTF-8"
 
 `)
 }
-
diff --git a/test/phiopt.go b/test/phiopt.go
index 21dd131..98a7b75 100644
--- a/test/phiopt.go
+++ b/test/phiopt.go
@@ -1,4 +1,4 @@
-// +build amd64
+// +build amd64 s390x
 // errorcheck -0 -d=ssa/phiopt/debug=3
 
 // Copyright 2016 The Go Authors. All rights reserved.
diff --git a/test/prove.go b/test/prove.go
index 8bcc9ae..9ced616 100644
--- a/test/prove.go
+++ b/test/prove.go
@@ -1,5 +1,5 @@
 // +build amd64
-// errorcheck -0 -d=ssa/prove/debug=3
+// errorcheck -0 -d=ssa/prove/debug=1
 
 // Copyright 2016 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
@@ -250,7 +250,7 @@ func f9(a, b bool) int {
 
 func f10(a string) int {
 	n := len(a)
-	if a[:n>>1] == "aaa" {
+	if a[:n>>1] == "aaaaaaaaaaaaaa" {
 		return 0
 	}
 	return 1
diff --git a/test/range.go b/test/range.go
index af89eda..bae7a1c 100644
--- a/test/range.go
+++ b/test/range.go
@@ -110,6 +110,30 @@ func testslice2() {
 	}
 }
 
+// test that range over []byte(string) only evaluates
+// the expression after "range" once.
+
+func makenumstring() string {
+	nmake++
+	return "\x01\x02\x03\x04\x05"
+}
+
+func testslice3() {
+	s := byte(0)
+	nmake = 0
+	for _, v := range []byte(makenumstring()) {
+		s += v
+	}
+	if nmake != 1 {
+		println("range called makenumstring", nmake, "times")
+		panic("fail")
+	}
+	if s != 15 {
+		println("wrong sum ranging over []byte(makenumstring)", s)
+		panic("fail")
+	}
+}
+
 // test that range over array only evaluates
 // the expression after "range" once.
 
@@ -392,6 +416,7 @@ func main() {
 	testslice()
 	testslice1()
 	testslice2()
+	testslice3()
 	teststring()
 	teststring1()
 	teststring2()
diff --git a/test/run.go b/test/run.go
index a1ab9d5..0dee6b5 100644
--- a/test/run.go
+++ b/test/run.go
@@ -52,7 +52,7 @@ var (
 
 	// dirs are the directories to look for *.go files in.
 	// TODO(bradfitz): just use all directories?
-	dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "bugs"}
+	dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs"}
 
 	// ratec controls the max number of tests running at a time.
 	ratec chan bool
@@ -242,8 +242,7 @@ type test struct {
 	donec       chan bool // closed when done
 	dt          time.Duration
 
-	src    string
-	action string // "compile", "build", etc.
+	src string
 
 	tempDir string
 	err     error
@@ -457,15 +456,15 @@ func (t *test) run() {
 		pkgPos = pos // some files are intentionally malformed
 	}
 	if ok, why := shouldTest(t.src[:pkgPos], goos, goarch); !ok {
-		t.action = "skip"
 		if *showSkips {
-			fmt.Printf("%-20s %-20s: %s\n", t.action, t.goFileName(), why)
+			fmt.Printf("%-20s %-20s: %s\n", "skip", t.goFileName(), why)
 		}
 		return
 	}
 
 	var args, flags []string
 	wantError := false
+	wantAuto := false
 	singlefilepkgs := false
 	f := strings.Fields(action)
 	if len(f) > 0 {
@@ -477,27 +476,25 @@ func (t *test) run() {
 	switch action {
 	case "rundircmpout":
 		action = "rundir"
-		t.action = "rundir"
 	case "cmpout":
 		action = "run" // the run case already looks for <dir>/<test>.out files
-		fallthrough
 	case "compile", "compiledir", "build", "run", "runoutput", "rundir":
-		t.action = action
+		// nothing to do
 	case "errorcheckandrundir":
 		wantError = false // should be no error if also will run
-		fallthrough
+	case "errorcheckwithauto":
+		action = "errorcheck"
+		wantAuto = true
+		wantError = true
 	case "errorcheck", "errorcheckdir", "errorcheckoutput":
-		t.action = action
 		wantError = true
 	case "skip":
 		if *runSkips {
 			break
 		}
-		t.action = "skip"
 		return
 	default:
 		t.err = skipError("skipped; unknown pattern: " + action)
-		t.action = "??"
 		return
 	}
 
@@ -531,7 +528,6 @@ func (t *test) run() {
 	}
 
 	useTmp := true
-	ssaMain := false
 	runcmd := func(args ...string) ([]byte, error) {
 		cmd := exec.Command(args[0], args[1:]...)
 		var buf bytes.Buffer
@@ -543,9 +539,6 @@ func (t *test) run() {
 		} else {
 			cmd.Env = os.Environ()
 		}
-		if ssaMain && os.Getenv("GOARCH") == "amd64" {
-			cmd.Env = append(cmd.Env, "GOSSAPKG=main")
-		}
 		err := cmd.Run()
 		if err != nil {
 			err = fmt.Errorf("%s\n%s", err, buf.Bytes())
@@ -578,7 +571,7 @@ func (t *test) run() {
 		if *updateErrors {
 			t.updateErrors(string(out), long)
 		}
-		t.err = t.errorCheck(string(out), long, t.gofile)
+		t.err = t.errorCheck(string(out), wantAuto, long, t.gofile)
 		return
 
 	case "compile":
@@ -626,7 +619,7 @@ func (t *test) run() {
 			for _, name := range gofiles {
 				fullshort = append(fullshort, filepath.Join(longdir, name), name)
 			}
-			t.err = t.errorCheck(string(out), fullshort...)
+			t.err = t.errorCheck(string(out), wantAuto, fullshort...)
 			if t.err != nil {
 				break
 			}
@@ -680,7 +673,6 @@ func (t *test) run() {
 
 	case "run":
 		useTmp = false
-		ssaMain = true
 		cmd := []string{"go", "run"}
 		if *linkshared {
 			cmd = append(cmd, "-linkshared")
@@ -716,7 +708,6 @@ func (t *test) run() {
 			t.err = fmt.Errorf("write tempfile:%s", err)
 			return
 		}
-		ssaMain = true
 		cmd = []string{"go", "run"}
 		if *linkshared {
 			cmd = append(cmd, "-linkshared")
@@ -764,7 +755,7 @@ func (t *test) run() {
 				return
 			}
 		}
-		t.err = t.errorCheck(string(out), tfile, "tmp__.go")
+		t.err = t.errorCheck(string(out), false, tfile, "tmp__.go")
 		return
 	}
 }
@@ -807,7 +798,7 @@ func (t *test) expectedOutput() string {
 	return string(b)
 }
 
-func splitOutput(out string) []string {
+func splitOutput(out string, wantAuto bool) []string {
 	// gc error messages continue onto additional lines with leading tabs.
 	// Split the output at the beginning of each line that doesn't begin with a tab.
 	// <autogenerated> lines are impossible to match so those are filtered out.
@@ -818,7 +809,7 @@ func splitOutput(out string) []string {
 		}
 		if strings.HasPrefix(line, "\t") {
 			res[len(res)-1] += "\n" + line
-		} else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "<autogenerated>") || strings.HasPrefix(line, "#") {
+		} else if strings.HasPrefix(line, "go tool") || strings.HasPrefix(line, "#") || !wantAuto && strings.HasPrefix(line, "<autogenerated>") {
 			continue
 		} else if strings.TrimSpace(line) != "" {
 			res = append(res, line)
@@ -827,14 +818,14 @@ func splitOutput(out string) []string {
 	return res
 }
 
-func (t *test) errorCheck(outStr string, fullshort ...string) (err error) {
+func (t *test) errorCheck(outStr string, wantAuto bool, fullshort ...string) (err error) {
 	defer func() {
 		if *verbose && err != nil {
 			log.Printf("%s gc output:\n%s", t, outStr)
 		}
 	}()
 	var errs []error
-	out := splitOutput(outStr)
+	out := splitOutput(outStr, wantAuto)
 
 	// Cut directory name.
 	for i := range out {
@@ -852,7 +843,11 @@ func (t *test) errorCheck(outStr string, fullshort ...string) (err error) {
 
 	for _, we := range want {
 		var errmsgs []string
-		errmsgs, out = partitionStrings(we.prefix, out)
+		if we.auto {
+			errmsgs, out = partitionStrings("<autogenerated>", out)
+		} else {
+			errmsgs, out = partitionStrings(we.prefix, out)
+		}
 		if len(errmsgs) == 0 {
 			errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr))
 			continue
@@ -860,7 +855,13 @@ func (t *test) errorCheck(outStr string, fullshort ...string) (err error) {
 		matched := false
 		n := len(out)
 		for _, errmsg := range errmsgs {
-			if we.re.MatchString(errmsg) {
+			// Assume errmsg says "file:line: foo".
+			// Cut leading "file:line: " to avoid accidental matching of file name instead of message.
+			text := errmsg
+			if i := strings.Index(text, " "); i >= 0 {
+				text = text[i+1:]
+			}
+			if we.re.MatchString(text) {
 				matched = true
 			} else {
 				out = append(out, errmsg)
@@ -912,7 +913,7 @@ func (t *test) updateErrors(out, file string) {
 	// Parse new errors.
 	errors := make(map[int]map[string]bool)
 	tmpRe := regexp.MustCompile(`autotmp_[0-9]+`)
-	for _, errStr := range splitOutput(out) {
+	for _, errStr := range splitOutput(out, false) {
 		colon1 := strings.Index(errStr, ":")
 		if colon1 < 0 || errStr[:colon1] != file {
 			continue
@@ -997,12 +998,14 @@ type wantedError struct {
 	reStr   string
 	re      *regexp.Regexp
 	lineNum int
+	auto    bool // match <autogenerated> line
 	file    string
 	prefix  string
 }
 
 var (
 	errRx       = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`)
+	errAutoRx   = regexp.MustCompile(`// (?:GC_)?ERRORAUTO (.*)`)
 	errQuotesRx = regexp.MustCompile(`"([^"]*)"`)
 	lineRx      = regexp.MustCompile(`LINE(([+-])([0-9]+))?`)
 )
@@ -1017,7 +1020,13 @@ func (t *test) wantedErrors(file, short string) (errs []wantedError) {
 			// double comment disables ERROR
 			continue
 		}
-		m := errRx.FindStringSubmatch(line)
+		var auto bool
+		m := errAutoRx.FindStringSubmatch(line)
+		if m != nil {
+			auto = true
+		} else {
+			m = errRx.FindStringSubmatch(line)
+		}
 		if m == nil {
 			continue
 		}
@@ -1052,6 +1061,7 @@ func (t *test) wantedErrors(file, short string) (errs []wantedError) {
 				reStr:   rx,
 				re:      re,
 				prefix:  prefix,
+				auto:    auto,
 				lineNum: lineNum,
 				file:    short,
 			})
diff --git a/test/sliceopt.go b/test/sliceopt.go
index a830ab7..eb24701 100644
--- a/test/sliceopt.go
+++ b/test/sliceopt.go
@@ -1,5 +1,4 @@
-// +build !amd64
-// errorcheck -0 -d=append,slice
+// errorcheck -0 -d=append,slice,ssa/prove/debug=1
 
 // Copyright 2015 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
@@ -10,51 +9,63 @@
 package main
 
 func a1(x []int, y int) []int {
-	x = append(x, y) // ERROR "append: len-only update"
+	x = append(x, y) // ERROR "append: len-only update \(in local slice\)$"
 	return x
 }
 
 func a2(x []int, y int) []int {
-	return append(x, y) // ERROR "append: full update"
+	return append(x, y)
 }
 
 func a3(x *[]int, y int) {
-	*x = append(*x, y) // ERROR "append: len-only update"
+	*x = append(*x, y) // ERROR "append: len-only update$"
+}
+
+// s1_if_false_then_anything
+func s1_if_false_then_anything(x **[]int, xs **string, i, j int) {
+	z := (**x)[0:i]
+	z = z[i : i+1]
+	println(z) // if we get here, then we have proven that i==i+1 (this cannot happen, but the program is still being analyzed...)
+
+	zs := (**xs)[0:i] // since i=i+1 is proven, i+1 is "in bounds", ha-ha
+	zs = zs[i : i+1]  // ERROR "Proved boolean IsSliceInBounds$"
+	println(zs)
 }
 
 func s1(x **[]int, xs **string, i, j int) {
 	var z []int
-	z = (**x)[2:]         // ERROR "slice: omit check for 2nd index"
-	z = (**x)[2:len(**x)] // not yet: "slice: reuse len" "slice: omit check for 2nd index"
-	z = (**x)[2:cap(**x)] // not yet: "slice: reuse cap" "slice: omit check for 2nd index"
-	z = (**x)[i:i]        // ERROR "slice: reuse 1st index" "slice: omit check for 1st index" "slice: result len == 0"
-	z = (**x)[1:i:i]      // ERROR "slice: reuse 2nd index" "slice: omit check for 2nd index" "slice: result cap == result len"
-	z = (**x)[i:j:0]      // ERROR "slice: omit check for 3rd index"
-	z = (**x)[i:0:j]      // ERROR "slice: omit check for 2nd index"
-	z = (**x)[0:i:j]      // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0"
-	z = (**x)[0:]         // ERROR "slice: omit slice operation"
-	z = (**x)[2:8]        // ERROR "slice: omit check for 1st index" "slice: result len == 6"
-	z = (**x)[2:2]        // ERROR "slice: omit check for 1st index" "slice: result len == 0"
-	z = (**x)[0:i]        // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0"
-	z = (**x)[2:i:8]      // ERROR "slice: result cap == 6"
-	z = (**x)[i:2:i]      // ERROR "slice: reuse 1st index" "slice: result cap == 0" "slice: skip base adjustment for cap == 0"
-
-	z = z[0:i]       // ERROR "slice: omit check for 1st index" "slice: result cap not computed" "slice: skip base adjustment for 1st index 0" "slice: len-only update"
-	z = z[0:i : i+1] // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0" "slice: len/cap-only update"
-	z = z[i : i+1]
+	z = (**x)[2:]
+	z = (**x)[2:len(**x)] // ERROR "Proved boolean IsSliceInBounds$"
+	z = (**x)[2:cap(**x)] // ERROR "Proved IsSliceInBounds$"
+	z = (**x)[i:i]        // -ERROR "Proved IsSliceInBounds"
+	z = (**x)[1:i:i]      // ERROR "Proved boolean IsSliceInBounds$"
+	z = (**x)[i:j:0]
+	z = (**x)[i:0:j] // ERROR "Disproved IsSliceInBounds$"
+	z = (**x)[0:i:j] // ERROR "Proved boolean IsSliceInBounds$"
+	z = (**x)[0:]    // ERROR "slice: omit slice operation$"
+	z = (**x)[2:8]   // ERROR "Proved slicemask not needed$"
+	println(z)
+	z = (**x)[2:2]
+	z = (**x)[0:i]
+	z = (**x)[2:i:8] // ERROR "Disproved IsSliceInBounds$" "Proved IsSliceInBounds$"
+	z = (**x)[i:2:i] // ERROR "Proved IsSliceInBounds$" "Proved boolean IsSliceInBounds$"
+
+	z = z[0:i] // ERROR "Proved boolean IsSliceInBounds"
+	z = z[0:i : i+1]
+	z = z[i : i+1] // ERROR "Proved boolean IsSliceInBounds$"
 
 	println(z)
 
 	var zs string
-	zs = (**xs)[2:]          // ERROR "slice: omit check for 2nd index"
-	zs = (**xs)[2:len(**xs)] // not yet: "slice: reuse len" "slice: omit check for 2nd index"
-	zs = (**xs)[i:i]         // ERROR "slice: reuse 1st index" "slice: omit check for 1st index" "slice: result len == 0" "slice: skip base adjustment for string len == 0"
-	zs = (**xs)[0:]          // ERROR "slice: omit slice operation"
-	zs = (**xs)[2:8]         // ERROR "slice: omit check for 1st index" "slice: result len == 6"
-	zs = (**xs)[2:2]         // ERROR "slice: omit check for 1st index" "slice: result len == 0" "slice: skip base adjustment for string len == 0"
-	zs = (**xs)[0:i]         // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0"
-
-	zs = zs[0:i] // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0" "slice: len-only update"
-	zs = zs[i : i+1]
+	zs = (**xs)[2:]
+	zs = (**xs)[2:len(**xs)] // ERROR "Proved IsSliceInBounds$" "Proved boolean IsSliceInBounds$"
+	zs = (**xs)[i:i]         // -ERROR "Proved boolean IsSliceInBounds"
+	zs = (**xs)[0:]          // ERROR "slice: omit slice operation$"
+	zs = (**xs)[2:8]
+	zs = (**xs)[2:2] // ERROR "Proved boolean IsSliceInBounds$"
+	zs = (**xs)[0:i] // ERROR "Proved boolean IsSliceInBounds$"
+
+	zs = zs[0:i]     // See s1_if_false_then_anything above to explain the counterfactual bounds check result below
+	zs = zs[i : i+1] // ERROR "Proved boolean IsSliceInBounds$"
 	println(zs)
 }
diff --git a/test/switch2.go b/test/switch2.go
index 11ff5c5..11b85d3 100644
--- a/test/switch2.go
+++ b/test/switch2.go
@@ -11,11 +11,11 @@ package main
 
 func f() {
 	switch {
-	case 0; // ERROR "expecting := or = or : or comma"
+	case 0; // ERROR "expecting := or = or : or comma|expecting :"
 	}
 
 	switch {
-	case 0; // ERROR "expecting := or = or : or comma"
+	case 0; // ERROR "expecting := or = or : or comma|expecting :"
 	default:
 	}
 
diff --git a/test/switch5.go b/test/switch5.go
index 7da2c66..5ca53ba 100644
--- a/test/switch5.go
+++ b/test/switch5.go
@@ -57,8 +57,8 @@ func f4(e interface{}) {
 	case int:
 	case int: // ERROR "duplicate case int in type switch"
 	case int64:
-	case error: // ERROR "duplicate case error in type switch"
 	case error:
+	case error: // ERROR "duplicate case error in type switch"
 	case fmt.Stringer:
 	case fmt.Stringer: // ERROR "duplicate case fmt.Stringer in type switch"
 	case struct {
@@ -79,3 +79,23 @@ func f5(a [1]int) {
 	case [1]int{0}: // OK -- see issue 15896
 	}
 }
+
+// Ensure duplicate const bool clauses are accepted.
+func f6() int {
+	switch {
+	case 0 == 0:
+		return 0
+	case 1 == 1: // Intentionally OK, even though a duplicate of the above const true
+		return 1
+	}
+	return 2
+}
+
+// Ensure duplicates in ranges are detected (issue #17517).
+func f7(a int) {
+	switch a {
+	case 0:
+	case 0, 1: // ERROR "duplicate case 0"
+	case 1, 2, 3, 4: // ERROR "duplicate case 1"
+	}
+}
diff --git a/test/switch6.go b/test/switch6.go
index bd62c62..32392d8 100644
--- a/test/switch6.go
+++ b/test/switch6.go
@@ -21,12 +21,12 @@ func f0(e error) {
 
 // Verify that the compiler rejects multiple default cases.
 func f1(e interface{}) {
-	switch e { // ERROR "multiple defaults in switch"
-	default:
+	switch e {
 	default:
+	default: // ERROR "multiple defaults in switch"
 	}
-	switch e.(type) { // ERROR "multiple defaults in switch"
-	default:
+	switch e.(type) {
 	default:
+	default: // ERROR "multiple defaults in switch"
 	}
 }
diff --git a/test/syntax/chan1.go b/test/syntax/chan1.go
index 2e9929b..a33a0d4 100644
--- a/test/syntax/chan1.go
+++ b/test/syntax/chan1.go
@@ -10,8 +10,8 @@ var c chan int
 var v int
 
 func main() {
-	if c <- v { // ERROR "used as value"
+	if c <- v { // ERROR "used as value|missing condition|invalid condition"
 	}
 }
 
-var _ = c <- v // ERROR "used as value"
+var _ = c <- v // ERROR "used as value|unexpected <-"
diff --git a/test/syntax/semi4.go b/test/syntax/semi4.go
index 6315f34..6f5592e 100644
--- a/test/syntax/semi4.go
+++ b/test/syntax/semi4.go
@@ -4,11 +4,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// TODO(mdempsky): Update error expectations for new parser.
+// The new parser emits an extra "missing { after for clause" error.
+// The old parser is supposed to emit this too, but it panics first
+// due to a nil pointer dereference.
+
 package main
 
 func main() {
 	for x		// GCCGO_ERROR "undefined"
 	{		// ERROR "missing .*{.* after for clause|missing operand"
-		z	// GCCGO_ERROR "undefined"
-
-
+		z	// ERROR "undefined|missing { after for clause"
diff --git a/test/uintptrescapes2.go b/test/uintptrescapes2.go
index 7ff676d..57c21ed 100644
--- a/test/uintptrescapes2.go
+++ b/test/uintptrescapes2.go
@@ -18,14 +18,14 @@ func F1(a uintptr) {} // ERROR "escaping uintptr"
 
 //go:uintptrescapes
 //go:noinline
-func F2(a ...uintptr) {} // ERROR "escaping ...uintptr" "live at entry" "a does not escape"
+func F2(a ...uintptr) {} // ERROR "escaping ...uintptr" "a does not escape"
 
 func G() {
-	var t int // ERROR "moved to heap"
-	F1(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to F1: autotmp" "&t escapes to heap"
+	var t int                       // ERROR "moved to heap"
+	F1(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to F1: .?autotmp" "&t escapes to heap"
 }
 
 func H() {
-	var v int // ERROR "moved to heap"
-	F2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to newobject: autotmp" "live at call to F2: autotmp" "escapes to heap"
+	var v int                                // ERROR "moved to heap"
+	F2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F2: .?autotmp" "escapes to heap"
 }
diff --git a/test/writebarrier.go b/test/writebarrier.go
index 88b4b29..6460a6f 100644
--- a/test/writebarrier.go
+++ b/test/writebarrier.go
@@ -164,8 +164,9 @@ type T17 struct {
 }
 
 func f17(x *T17) {
-	// See golang.org/issue/13901
-	x.f = f17                      // no barrier
+	// Originally from golang.org/issue/13901, but the hybrid
+	// barrier requires both to have barriers.
+	x.f = f17                      // ERROR "write barrier"
 	x.f = func(y *T17) { *y = *x } // ERROR "write barrier"
 }
 
@@ -207,7 +208,15 @@ func f21(x *int) {
 	// Global -> heap pointer updates must have write barriers.
 	x21 = x                   // ERROR "write barrier"
 	y21.x = x                 // ERROR "write barrier"
-	x21 = &z21                // no barrier
-	y21.x = &z21              // no barrier
+	x21 = &z21                // ERROR "write barrier"
+	y21.x = &z21              // ERROR "write barrier"
 	y21 = struct{ x *int }{x} // ERROR "write barrier"
 }
+
+func f22(x *int) (y *int) {
+	// pointer write on stack should have no write barrier.
+	// this is a case that the frontend failed to eliminate.
+	p := &y
+	*p = x // no barrier
+	return
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-golang/golang.git



More information about the pkg-golang-commits mailing list